From 1b7f288804c3246c17537ae4fc7b25f43da7c52b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Wed, 18 Mar 2026 12:11:30 +0100 Subject: [PATCH 01/33] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 588838093..e300d7b6d 100644 --- a/README.md +++ b/README.md @@ -374,4 +374,4 @@ For example, PlantBiophysics.jl, which implements ecophysiological models using The package is developed so anyone can easily implement plant/crop models, use it freely and as you want thanks to its MIT license. -If you develop such tools and it is not on the list yet, please make a PR or contact me so we can add it! 😃 Make sure to read the community guidelines before in case you're not familiar with such things. +If you develop such tools and it is not on the list yet, please make a PR or contact me so we can add it! 😃 Make sure to read the community guidelines before in case you're not familiar with such things. \ No newline at end of file From 75954d16bcaf5ffaae0d84fb95dd661f04383d5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Wed, 3 Jun 2026 17:25:38 +0200 Subject: [PATCH 02/33] Add domain simulations with hard-domain targets and microclimate support - Added `Domain`, `SimulationMapping`, and `DomainSimulation` for composing plant, soil, scene, and future environment domains. - Added cross-domain stream/value dependencies with `AllDomains(...)` and explicit `Route(...)` materialization. - Added hard-domain dependencies with `HardDomains(...)`, `dependency_targets(...)`, `model_target(...)`, and `run_target!(...)` so scene models can manually run plant/soil targets, including iterative workflows. - Added MTG-backed domain support with selectors, multi-plant domains, graph-domain output publication, dynamic topology registration, organ removal, and reparenting. - Added `Updates(:var; after=...)` for intentional same-scale variable updates by later models. - Added environment/microclimate backend protocol with `AbstractEnvironmentBackend`, `GlobalConstant`, `meteo_inputs_`, `meteo_outputs_`, sampling, scattering, and validation. - Extended multirate support with `Dates`-based timesteps, inferred/explicit bindings, temporal output policies, scoped streams, meteo aggregation, and requested output export. - Added MAESPA-style example in [examples/maespa_domain_example.jl](/Users/rvezy/Documents/dev/PlantSimEngine/examples/maespa_domain_example.jl) with two plant species, shared soil, scene-scale iterative energy balance, and hard-domain targets. - Added docs for domain simulation, model traits, hard-domain target design, and implementation plan/handoff notes. - Added focused tests for domains, environment backends, meteo traits, updates, and the MAESPA example. Verification already run: - `julia --project=test test/runtests.jl` passed: `1844 / 1844` - `julia --project=docs docs/make.jl` passed - `git diff --check` passed Files to remember are untracked too, especially: - `src/domains/domain_simulation.jl` - `src/time/runtime/environment_backends.jl` - `src/dependencies/update_dependencies.jl` - `examples/maespa_domain_example.jl` - new docs under `docs/src/dev/` - new tests under `test/test-domain-simulation.jl`, `test/test-environment-backends.jl`, `test/test-maespa-domain-example.jl`, `test/test-meteo-traits.jl`, `test/test-updates.jl` --- docs/make.jl | 6 + docs/src/dev/maespa_domain_handoff.md | 39 + .../src/dev/multi_domain_simulation_design.md | 494 ++++ docs/src/dev/multi_domain_simulation_plan.md | 250 ++ docs/src/domain_simulation.md | 360 +++ docs/src/model_traits.md | 40 +- docs/src/step_by_step/advanced_coupling.md | 4 +- examples/dummy.jl | 4 +- examples/maespa_domain_example.jl | 363 +++ src/PlantSimEngine.jl | 22 +- src/dependencies/dependencies.jl | 2 + src/dependencies/hard_dependencies.jl | 14 +- src/dependencies/is_graph_cyclic.jl | 6 +- src/dependencies/update_dependencies.jl | 189 ++ src/domains/domain_simulation.jl | 2177 +++++++++++++++++ src/mtg/ModelSpec.jl | 92 +- src/mtg/add_organ.jl | 178 +- src/mtg/initialisation.jl | 7 +- src/mtg/model_spec_inference.jl | 150 +- src/mtg/model_spec_validation.jl | 6 +- src/processes/model_initialisation.jl | 5 +- src/processes/models_inputs_outputs.jl | 34 + src/run.jl | 57 +- src/time/runtime/environment_backends.jl | 310 +++ src/time/runtime/meteo_sampling.jl | 115 + src/time/runtime/output_export.jl | 4 +- src/time/runtime/publishers.jl | 17 +- test/runtests.jl | 20 + test/test-domain-simulation.jl | 1420 +++++++++++ test/test-environment-backends.jl | 375 +++ test/test-maespa-domain-example.jl | 38 + test/test-meteo-traits.jl | 60 + test/test-multirate-output-export.jl | 130 + test/test-multirate-runtime.jl | 35 +- test/test-multirate-scaffolding.jl | 13 +- test/test-updates.jl | 86 + 36 files changed, 7039 insertions(+), 83 deletions(-) create mode 100644 docs/src/dev/maespa_domain_handoff.md create mode 100644 docs/src/dev/multi_domain_simulation_design.md create mode 100644 docs/src/dev/multi_domain_simulation_plan.md create mode 100644 docs/src/domain_simulation.md create mode 100644 examples/maespa_domain_example.jl create mode 100644 src/dependencies/update_dependencies.jl create mode 100644 src/domains/domain_simulation.jl create mode 100644 src/time/runtime/environment_backends.jl create mode 100644 test/test-domain-simulation.jl create mode 100644 test/test-environment-backends.jl create mode 100644 test/test-maespa-domain-example.jl create mode 100644 test/test-meteo-traits.jl create mode 100644 test/test-updates.jl diff --git a/docs/make.jl b/docs/make.jl index 7c3dd6a01..8719079f1 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -42,6 +42,7 @@ makedocs(; "Implementing a model : additional notes" => "./step_by_step/implement_a_model_additional.md", ], "Execution" => "model_execution.md", + "Domain simulations" => "domain_simulation.md", "Model traits" => "model_traits.md", "AI agent skill" => "agent_skill.md", "Working with data" => [ @@ -78,6 +79,11 @@ makedocs(; "Public API" => "./API/API_public.md", "Example models" => "./API/API_examples.md", "Internal API" => "./API/API_private.md",], + "Development designs" => [ + "Multi-domain simulation design" => "./dev/multi_domain_simulation_design.md", + "Multi-domain simulation plan" => "./dev/multi_domain_simulation_plan.md", + "MAESPA-style domain example handoff" => "./dev/maespa_domain_handoff.md", + ], "Developer guidelines" => "developers.md", "Roadmap" => "planned_features.md", ] diff --git a/docs/src/dev/maespa_domain_handoff.md b/docs/src/dev/maespa_domain_handoff.md new file mode 100644 index 000000000..c27e0cac0 --- /dev/null +++ b/docs/src/dev/maespa_domain_handoff.md @@ -0,0 +1,39 @@ +# MAESPA-Style Domain Example Handoff + +The example in `examples/maespa_domain_example.jl` is the target API for the +new hard-domain design. + +## API Expectations + +- `HardDomains(kind=:plant, scale=:Leaf, process=:leaf_eb)` selects models that + are hard dependencies of the declaring model. +- Hard-domain targets are excluded from normal domain scheduling. +- Parent models retrieve selected targets with + `dependency_targets(extra, :dependency_name)`. +- Parent models execute selected targets with `run_target!(target)`. +- `run_target!(target; publish=true)` publishes the hard-run model outputs + to domain streams and `DomainSimulation.outputs`. +- Trial target runs still mutate the target status. Irreversible accumulators + such as growth/carbon pools should be committed only after the accepted final + target run, not inside every trial iteration. +- `explain_domain_dependencies(sim)` reports hard-domain dependencies with + `mode=:hard_domain`. + +## Scheduler Expectations + +- `AllDomains` keeps stream-reading semantics for normally scheduled producers. +- `HardDomains` keeps hard-dependency semantics for manually run producers. +- Normal plant allocation models are not hard-called by scene models. +- Daily allocation models run through the normal dependency graph after scene + hard-runs have published leaf outputs. + +## Example Verification + +- Species A has two leaves. +- Species B has three leaves. +- `LeafEB`, `FvCB`, `Tuzet`, and `SoilWater` are driven through `SceneEB`, not + through normal scheduling. +- `AllocA` and `AllocB` run normally on a daily clock. +- Leaf temperature, assimilation, transpiration, canopy air state, and soil + water potential remain finite and change during the run. +- Allocation differs between species because their parameters differ. diff --git a/docs/src/dev/multi_domain_simulation_design.md b/docs/src/dev/multi_domain_simulation_design.md new file mode 100644 index 000000000..63fcb116d --- /dev/null +++ b/docs/src/dev/multi_domain_simulation_design.md @@ -0,0 +1,494 @@ +# Multi-Domain Simulation Design + +This page is the working design for extending PlantSimEngine from one plant or +one MTG mapping to reusable plant, soil, scene, and environment domains. + +The goal is incremental: keep existing `ModelMapping`, `ModelSpec`, +`MultiScaleModel`, hard dependencies, and multi-rate machinery, then add one +composition layer above them. + +## Goals + +- Reuse complete plant models, such as XPalm, as independent species or variety + domains. +- Assemble several plant domains with shared soil, scene, and microclimate + domains. +- Keep plant-local mappings readable and reusable. +- Make cross-domain coupling explicit. +- Support multi-rate execution from the start using `Dates.FixedPeriod` + timesteps. +- Preserve fast reference-based coupling inside existing mappings. +- Make compiled simulations inspectable by humans and agents. +- Allow exceptional same-scale duplicate writers through scenario-level + `Updates(...)` declarations. +- Let meteorology be either constant/table-driven or provided by an external + microclimate backend. + +## Non-Goals For The First Implementation + +- Do not infer cross-domain dependencies from matching variable names. +- Do not implement octree or voxel microclimate in PlantSimEngine. +- Do not solve arbitrary dynamic-topology MTG registration in the first slice; + organ addition, terminal-organ removal, recursive subtree removal, and + same-simulation reparenting are covered. +- Do not rewrite model kernels or force model authors to adopt a new `run!` + signature. + +## Domain + +A `Domain` wraps an existing mapping and gives it an identity: + +```julia +oil_palm = Domain(:oil_palm, kind=:plant, mapping=xpalm) +maize = Domain(:maize, kind=:plant, mapping=maize) +soil = Domain(:soil, kind=:soil, mapping=soil_mapping) +scene = Domain(:scene, kind=:scene, mapping=scene_mapping) +microclimate = Domain(:microclimate, kind=:environment, mapping=microclimate_mapping) +``` + +Model identity inside the compiled simulation is: + +```julia +(domain, scale, process) +``` + +not only: + +```julia +(scale, process) +``` + +This avoids renaming `:Leaf` into species-specific scale names. XPalm can keep +using `:Plant`, `:Leaf`, `:Internode`, and so on. + +For MTG runs, domains will also need selectors: + +```julia +Domain( + :oil_palm, + kind=:plant, + mapping=xpalm, + selector=node -> node[:species] == :oil_palm, +) +``` + +The selector decides which MTG nodes belong to the domain-local view. + +## SimulationMapping + +`SimulationMapping` assembles domains: + +```julia +simulation_mapping = SimulationMapping(oil_palm, maize, soil, scene) +``` + +It should preserve both domain-local and global scale views: + +```julia +status(sim, :oil_palm, :Leaf) # oil palm leaves +status(sim, :maize, :Leaf) # maize leaves +status(sim, :Leaf) # all leaves across plant domains +status(sim, :soil, :Soil) # shared soil statuses +``` + +When one domain selector matches several subtree roots, the domain-local status +view is flattened across those roots: + +```julia +forest = Domain(:forest, kind=:plant, mapping=leaf_mapping, selector=:Plant) +status(sim, :forest, :Leaf) # leaves from every selected Plant subtree +``` + +The initial `run!(mapping::SimulationMapping, meteo)` path supports +single-status domains. The `run!(mtg, mapping::SimulationMapping, meteo)` path +also supports MTG-backed domains when a domain selector resolves to one or more +subtree roots. That path reuses the existing `GraphSimulation` engine for each +selected root, advances domains one base timestep at a time, then publishes +aggregated graph outputs into the domain stream layer so single-status scene +domains can consume them during the same timestep. It is still acyclic by +domain order: domains whose `kind` is not `:scene` run first, then `:scene` +domains run. +Routes into graph domains are supported when their source domain has already +run in the current timestep. Runtime status counts are inspectable with +`explain_domain_statuses(sim)`. + +## Cross-Domain Routes + +Local variable inference remains inside one domain. Cross-domain exchange is +explicit: + +```julia +Route( + from = AllDomains(kind=:plant, scale=:Leaf, var=:Tleaf), + to = DomainRouteTarget(:scene, scale=:Scene, var=:leaf_temperatures), + policy = Integrate(), +) +``` + +Route cardinality should be explicit where needed: + +```julia +ManyToOneVector() +ManyToOneAggregate(sum) +OneToManyBroadcast() +SpatialSample() +SpatialScatterAdd() +``` + +This prevents ambiguous behavior when several plant domains publish the same +scale, process, or variable. + +In the single-status runner, routes are executable for target +`scale=:Default`: + +```julia +Route( + from = AllDomains( + kind=:plant, + process=:plant_transpiration, + var=:transpiration, + ), + to = DomainRouteTarget( + :scene, + var=:plant_transpirations, + process=:scene_evapotranspiration, + ), + cardinality = ManyToOneVector(), +) +``` + +The target variable must already exist in the target domain status. If the +target process is omitted, PlantSimEngine tries to infer a unique model at the +target domain/scale that consumes the routed variable; otherwise the route clock +is hourly/base-step. `ManyToOneVector()` materializes one value per producer. +`ManyToOneAggregate(f)` reduces producer values with `f`. Spatial and broadcast +cardinalities are defined as public route types but are reserved for the +MTG/spatial runner. + +In the current single-status runner, initialize routed vector targets with a +typed placeholder such as `[0.0]` rather than an empty vector. The route +overwrites the value before the target model runs, but the underlying +single-scale `ModelMapping` still treats an empty vector status value as +not initialized. + +The MTG runner also supports one graph-target route form: +`OneToManyBroadcast()` into an MTG-backed domain. The route writes the resolved +source value into every status at the target scale before that graph domain +runs for the current timestep. On the first timestep, the runner also seeds the +selected MTG nodes with the routed attribute before `GraphSimulation` +initialization so existing graph initialization checks still apply. This is +useful for same-timestep coupling from an earlier domain, such as a shared soil +signal broadcast to leaves. Scene-to-plant feedback remains out of scope for +this acyclic order because scene domains run after plant domains. + +## Domain-Aware Value Dependencies + +Scene and environment models often need to consume outputs from all plant +domains. Stream/value dependencies use `AllDomains(...)` and should be explicit: + +```julia +PlantSimEngine.dep(::SceneEvapotranspirationModel) = ( + plant_transpiration = AllDomains( + kind=:plant, + process=:plant_transpiration, + var=:transpiration, + policy=Integrate(), + ), + soil_evaporation = AllDomains( + kind=:soil, + process=:soil_evaporation, + var=:evaporation, + policy=Integrate(), + ), +) +``` + +The runtime resolves this to concrete `(domain, scale, process)` producers. +Scene models consume resolved values, not manually index a single +`extra.models[:Leaf]`. + +Unmatched `AllDomains(...)` selectors are reported with the consumer +`domain/scale/process`, the selector fields, available producer outputs, and +near matches when only `var` is wrong. This is part of the agent-facing API: +errors should contain enough concrete symbols for a user or AI system to repair +the mapping without reverse-engineering the compiled graph. + +In the first executable slice, scene models can read producer values with: + +```julia +plant_values = dependency_values(extra, :plant_transpiration, :transpiration) +soil_values = dependency_values(extra, :soil_evaporation, :evaporation) +``` + +When the selector declares `var`, model code can use the shorter form: + +```julia +plant_values = dependency_values(extra, :plant_transpiration) +``` + +For MTG-backed producer domains, each resolved `(domain, scale, process)` +producer can represent several node statuses. The default +`dependency_values(...)` result therefore contains one value per producer, with +graph-domain producers unwrapped as vectors of node values. Scene models that +want one flat list across all selected domains can request: + +```julia +leaf_values = dependency_values(extra, :leaf_fluxes; flatten=true) +``` + +When graph-domain producer values are reduced over a multi-rate window, the +runtime carries MTG node ids alongside the values and aggregates by node id. +This makes growth and pruning inside the window well-defined: a newly appeared +organ contributes from the timestep where it exists, and a removed organ keeps +its already-published contribution without requiring all value vectors in the +window to have the same length. + +If a selected producer is nested as a hard dependency inside a domain model, its +outputs are published when the owning parent model runs. This keeps the model +visible to scene-level `AllDomains(...)` dependencies without making the hard +dependency independently scheduled. + +## Hard Cross-Domain Dependencies + +Some coupled models need true call-stack control. Energy balance is the typical +case: a scene-scale solver may need to run leaf-scale energy, stomatal +conductance, and photosynthesis models repeatedly until leaf temperatures +converge. That is not an `AllDomains(...)` value dependency. + +Hard cross-domain dependencies use `HardDomains(...)`: + +```julia +PlantSimEngine.dep(::SceneEnergyBalanceModel) = ( + leaf_energy = HardDomains( + kind=:plant, + scale=:Leaf, + process=:leaf_energy_balance, + ), +) +``` + +Inside `run!`, the parent requests targets and runs them manually: + +```julia +function PlantSimEngine.run!(::SceneEnergyBalanceModel, models, status, meteo, constants=nothing, extra=nothing) + leaf_targets = dependency_targets(extra, :leaf_energy) + for iteration in 1:max_iterations + for target in leaf_targets + run_target!(target) + end + converged(status) && break + end + return nothing +end +``` + +Each target selects one model on one runtime status. In single-status +domains, a producer usually gives one target. In MTG-backed domains, a producer +at `scale=:Leaf` gives one target per matching leaf status. The call mutates the +status, just like a normal hard dependency call. By default it does not publish +to domain streams or outputs, which keeps trial iterations from becoming +canonical temporal outputs. The final accepted hard-dependency call can opt into +`publish=true`. + +Hard dependencies remain manual calls owned by the parent model. A hard +dependency child cannot have an independent `ModelSpec` timestep; if the child +needs its own clock, it should be coupled as a soft dependency instead. + +## Multi-Rate Semantics + +User-facing time configuration should continue to use `Dates`: + +```julia +ModelSpec(model) |> TimeStepModel(Dates.Hour(1)) +ModelSpec(model) |> TimeStepModel(Dates.Day(1)) +``` + +For the first implementation, use `Dates.FixedPeriod` only. `Dates.Month` and +`Dates.Year` are calendar-dependent and should error unless a calendar-aware +runtime is added. + +Cross-domain dependencies and routes carry a temporal policy: + +```julia +HoldLast() +Interpolate() +Integrate() +Aggregate() +``` + +The domain key must be part of temporal state keys from the beginning to avoid +collisions between species, soil, and environment streams. + +## Variable Updates + +Duplicate writers remain errors by default. Exceptional updates are declared by +the scenario author, not the model author: + +```julia +ModelSpec(LeafPruning()) |> + Updates(:leaf_biomass; after=:carbon_allocation) +``` + +Rules: + +- one canonical producer is allowed; +- additional same-scale writers must declare `Updates(...)`; +- `after` adds an ordering edge; +- if several update writers target the same variable, they must also be ordered + relative to each other; +- if both models run at the same `DateTime`, the updater runs after the + producer; +- if only the updater runs, it updates the latest available state; +- downstream consumers read the updated value. +- in multi-rate input binding inference, an ordered update chain is treated as + one effective source by selecting the unique terminal updater for the updated + variable. + +Only variables with duplicate writers need annotation. + +## Meteorology And Microclimate + +Models should declare meteorological fields explicitly: + +```julia +meteo_inputs_(::LeafEnergyBalanceModel) = ( + T = -Inf, + Rh = -Inf, + Wind = -Inf, + Ri_PAR_f = -Inf, + CO2 = -Inf, +) + +meteo_outputs_(::MicroclimateModel) = ( + T = -Inf, + Rh = -Inf, + Wind = -Inf, + Ri_PAR_f = -Inf, + CO2 = -Inf, +) +``` + +`inputs_` and `outputs_` remain object status variables. `meteo_inputs_` and +`meteo_outputs_` describe weather/environment variables; `meteo_inputs` and +`meteo_outputs` are the public key accessors. + +PlantSimEngine should define the microclimate backend interface, not the octree +or voxel implementation: + +```julia +abstract type AbstractEnvironmentBackend end + +get_nsteps(backend) +base_step_seconds(backend) +sample(backend, variable, support, time) +sample_environment(backend, support, time, variables) +scatter!(backend, variable, support, value, time) +update_index!(backend, entities) +``` + +`GlobalConstant(meteo)` is the simple backend and preserves current behavior: +all models see the same meteo object or meteo row at a given timestep. Plain +meteo passed to `run!(SimulationMapping, meteo)` is wrapped automatically. + +Backends receive an `EnvironmentSupport(domain, scale, process, status)` so +they can decide whether a sampled value is global, plant-local, organ-local, or +spatially resolved. The same protocol is used for single-status domains and +MTG-backed domains; in graph domains, `status` is the current node status, so +the backend can inspect node geometry, scale, domain, or any status variable. +Octree, voxel, layered-canopy, and CFD backends should live in specialized +packages. + +After each domain step, the domain runtime calls: + +```julia +update_index!(backend, entities) +``` + +where `entities` is a small collection of `(domain, kind, scale, statuses, +state)` rows. Spatial backends can use this hook to refresh their entity index +after geometry changes, growth, pruning, or status updates. `GlobalConstant` +ignores the hook. + +When a model declares `meteo_outputs_`, the domain runtime scatters those values +to the backend after the model runs: + +```julia +meteo_outputs_(::MicroclimateUpdateModel) = (T = -Inf,) +outputs_(::MicroclimateUpdateModel) = (T = -Inf,) +``` + +The same variable must currently be available on the model status, usually by +declaring it in `outputs_`. This keeps the existing `run!` signature unchanged +while giving backends a clear `scatter!` hook. In MTG-backed domains, the +scatter hook is called once per node status after the model runs. `GlobalConstant` +is immutable and errors if a model tries to scatter into it. + +## Growth And Dynamic Entities + +Dynamic organ creation must go through a central registration API. When a new +organ is added, the runtime must update: + +- domain-local status views; +- global scale views; +- cross-domain routes; +- outputs; +- temporal buffers; +- environment spatial indexes. + +This should not be scattered through model code. In MTG-backed domains, models +should keep using the existing: + +```julia +add_organ!(parent_node, graph_simulation, link, symbol, scale; kwargs...) +``` + +from their `run!` implementation. The domain runner passes the underlying +`GraphSimulation` as `extra`, so existing growth models can register new organs +without knowing they are part of a `SimulationMapping`. + +For the current domain runner: + +- `add_organ!` initializes the new node status and updates multiscale + `RefVector` wiring through the existing MTG initialization helpers. +- `remove_organ!` supports terminal MTG nodes by default and internal-node + subtree deletion with `recursive=true`. It removes node statuses, detaches + references from downstream `RefVector`s, clears node-scoped temporal cache and + stream entries, and deletes the MTG nodes. +- `reparent_organ!` moves an already simulated node under another already + simulated parent in the same `GraphSimulation`, validating that both nodes are + registered and that the move does not create a cycle. Statuses and + `RefVector`s continue to reference the same node objects, so no status + rewiring is needed for this case. +- `status(sim, domain, scale)` and `status(sim, scale)` read the live + `GraphSimulation` status vectors, so newly added organs are visible after + registration. If a declared graph scale currently has no active statuses, + these helpers return `Status[]` rather than throwing a dictionary lookup + error, and `explain_domain_statuses(sim)` reports the scale with + `nstatuses=0`. +- graph-domain output publication runs after each graph-domain timestep and + includes newly registered statuses while skipping removed terminal organs. +- `update_index!(backend, entities)` is called after each domain step so + external microclimate backends can refresh their spatial/entity index. + +The current tests cover a new graph-domain leaf publishing an hourly stream +that is consumed by a daily model with `Integrate()`, online +`OutputRequest(...)` exports from dynamically created leaves, several leaves +created in the same timestep, custom callable scopes returning `ScopeId`, +terminal leaf removal in direct and multirate graph-domain coupling, repeated +terminal create/remove cycles, recursive internal-node subtree deletion, and +same-simulation topology reparenting. + +## Agent-Friendly Requirements + +The compiled simulation must be explainable as structured data: + +```julia +explain_domains(sim) +explain_routes(sim) +explain_schedule(sim) +explain_writers(sim) +explain_domain_dependencies(sim) +``` + +Errors should include the conflicting domains/scales/processes and a suggested +fix. For example, duplicate writers should suggest `Updates(:var; after=...)`. diff --git a/docs/src/dev/multi_domain_simulation_plan.md b/docs/src/dev/multi_domain_simulation_plan.md new file mode 100644 index 000000000..a6f2e41f6 --- /dev/null +++ b/docs/src/dev/multi_domain_simulation_plan.md @@ -0,0 +1,250 @@ +# Multi-Domain Simulation Implementation Plan + +This page tracks the implementation plan for the multi-domain work. The design +reference is `multi_domain_simulation_design.md`. + +## Milestone 1: Executable Single-Status Domain Slice + +Done when: + +- `Domain`, `SimulationMapping`, `DomainSimulation`, `DomainModelKey`, and + `AllDomains` exist. +- Domains can wrap existing single-scale `ModelMapping`s. +- A `SimulationMapping` can run plant and soil domains hourly and a scene + domain daily with `Dates.Hour(1)` and `Dates.Day(1)`. +- Single-status domains can contain models with different `ModelSpec` + timesteps. Implemented. +- Scene models can consume resolved cross-domain producer streams with + `dependency_values`. +- `AllDomains(...; var=:x)` can declare the consumed output variable, allowing + `dependency_values(extra, :dependency_name)` and earlier validation of + producer matches. Implemented for the single-status domain runner. +- Scene models can consume a model nested as a hard dependency inside a domain; + the nested model output is published when its owning parent runs. + Implemented for the single-status domain runner. +- `explain_domains`, `explain_schedule`, and + `explain_domain_dependencies` return structured rows. +- A test example covers two plant domains, one soil domain, and one scene + evapotranspiration model. + +Likely files: + +- `src/domains/domain_simulation.jl` +- `src/PlantSimEngine.jl` +- `test/test-domain-simulation.jl` +- `test/runtests.jl` + +## Milestone 2: Agent-Friendly Inspection And Validation + +Current status: implemented for the current domain runner surface. +`explain_domain_models(...)` returns expanded rows, the initial runner +validates unsupported cases early, mixed rates inside single-status domains are +scheduled independently, and explicit `Route(...)` materialization is +implemented for single-status domains with `ManyToOneVector()` and +`ManyToOneAggregate(...)`. + +Done when: + +- `explain_domain_models(simulation_mapping)` returns expanded domain model + rows. Implemented. +- Errors include domain, scale, process, variable, and suggested fixes. + Implemented for unmatched `AllDomains(...)` dependencies and routes, route + targets, duplicate domains, and unsupported domain/route shapes. +- Duplicate domain names error early. Implemented. +- Mixed timesteps inside one initial domain are scheduled independently. + Implemented for single-status domains. +- `AllDomains(...)` selectors report unmatched dependencies with context. + Implemented. +- `Route(...)` materializes cross-domain producer streams into target domain + status variables. Implemented for single-status domains. +- `explain_domain_dependencies(simulation)` reports resolved producers, + temporal policy, and the declared dependency variable. Implemented. +- `explain_routes(simulation)` reports resolved route producers, target + variables, cardinality, temporal policy, and effective route clocks. + Implemented. + +Likely tests: + +- duplicate domain name; +- unmatched `AllDomains`; +- scene domain with multiple scene processes; +- domain with mixed model rates in the initial runner. +- scene dependency targeting a hard-dependency child inside a plant domain. +- vector and aggregate cross-domain routes. + +## Milestone 3: `Updates(...)` In `ModelSpec` + +Current status: implemented for same-scale dependency graphs, single-scale +runs, and MTG-backed domain runs. Downstream input binding inference treats an +unambiguous ordered update chain as one effective producer by selecting the +terminal updater. + +Done when: + +- `Updates(:var; after=:process)` exists as a scenario-level annotation. + Implemented. +- Duplicate writers remain errors unless additional writers declare updates. + Implemented for canonical same-scale writers. +- `after` creates an ordering edge. + Implemented for executable soft dependency nodes. +- Downstream consumers read the terminal updated value when the update order is + unambiguous. Implemented for MTG-backed domains. +- Errors suggest the exact `Updates(:var; after=...)` fix. + Implemented for duplicate writers and invalid update declarations. +- Only variables with duplicate writers need annotation. + Implemented. + +Likely files: + +- `src/mtg/ModelSpec.jl` +- dependency graph construction files +- model spec validation files +- tests for duplicate writers and ordered updates + +## Milestone 4: Meteo Traits + +Current status: trait surface and runtime field validation are implemented. +Environment backend coupling is implemented for the single-status domain +runner through the protocol described in Milestone 6. + +Done when: + +- `meteo_inputs(model)` and `meteo_outputs(model)` default to empty keys from + `NamedTuple()`. Implemented. +- Existing meteo validation can check required fields when traits are present. + Implemented for constant/table meteo field presence, including + `MeteoBindings(source=...)` remapping and public + `validate_meteo_inputs(mapping, meteo)` checks. +- `explain_model_specs` or a new explanation helper reports meteo needs. + Implemented in `explain_model_specs` and `explain_domain_models`. +- Trait declarations support simple NamedTuple defaults first. + Implemented. + +Likely files: + +- `src/processes/models_inputs_outputs.jl` +- `src/time/runtime/meteo_sampling.jl` +- documentation for model authors + +## Milestone 5: MTG-Backed Domains + +Current status: timestep-interleaved MTG-backed domain execution is implemented +for domain selectors that resolve to one or more subtree roots. The runner +advances non-scene domains, including graph domains backed by the existing +`GraphSimulation`, then advances single-status scene domains in the same base +timestep. Graph-domain outputs are aggregated per domain and published into +per-scale streams for `Route(...)` and `AllDomains(...)` consumers. + +Done when: + +- `Domain(..., selector=...)` partitions MTG nodes into domain-local views. + Implemented for one or more selected subtree roots per graph domain. +- Domain-local statuses and global scale statuses are both available. + Domain-local statuses are available through `status(sim, domain, scale)`. + Global cross-domain views are available through `status(sim, scale)` when no domain + has the same name, and runtime counts are reported by + `explain_domain_statuses(sim)`. +- Cross-domain routes can consume all statuses from selected domains. + Implemented for graph-domain sources routed into single-status + domains. Also implemented for `OneToManyBroadcast()` routes into graph + domains when the source domain runs earlier in the current timestep. +- Existing single-domain MTG behavior still works. + Reused by the domain runner through `GraphSimulation`. + +Likely files: + +- `src/mtg/initialisation.jl` +- `src/mtg/GraphSimulation.jl` +- new domain graph simulation code +- tests with two plant species sharing `:Leaf` scale names + +## Milestone 6: Environment Provider Interface + +Current status: protocol and constant backend are implemented for the domain +runner. Custom backends are supported by single-status domains and MTG-backed +domains. Spatial backends remain external-package work. + +Done when: + +- PlantSimEngine defines the backend protocol for sampling and scattering + meteo/environment variables. Implemented with `AbstractEnvironmentBackend`, + `EnvironmentSupport`, `sample`, `sample_environment`, `scatter!`, + `update_index!`, `get_nsteps`, and `base_step_seconds`. +- A constant meteo backend supports current behavior. Implemented with + `GlobalConstant`, and plain meteo passed to `run!(SimulationMapping, meteo)` + is wrapped automatically. +- External packages can implement spatial backends without PlantSimEngine + knowing whether they use voxels, octrees, layers, or another structure. + Implemented for the protocol surface; full spatial examples remain future. +- Environment bindings are visible to explanation helpers. Implemented with + `explain_environment(simulation)` plus `meteo_inputs_` in + `explain_domain_models`. +- Domain models declaring `meteo_outputs_` scatter same-named status values into + mutable backends after `run!`. Implemented for the single-status domain + runner and MTG-backed domain runner; `GlobalConstant` errors because it is + immutable. +- Domain steps call `update_index!(backend, entities)` after model execution so + mutable/spatial backends can refresh their entity indexes. Implemented for + single-status domains and MTG-backed domains; `GlobalConstant` is a no-op. + +Likely API: + +```julia +sample(backend, variable, support, time) +scatter!(backend, variable, support, value, time) +update_index!(backend, entities) +``` + +## Milestone 7: Growth Registration + +Current status: implemented for MTG-backed domains that use the existing +`add_organ!` API inside model `run!` methods, and for terminal or recursive +subtree removal with `remove_organ!`, and for same-simulation topology +reparenting with `reparent_organ!`. Domain status views and graph-domain stream +publication observe the live `GraphSimulation` status vectors, dynamic producer +streams are extended lazily, removed organs are detached from downstream +`RefVector`s and temporal caches, reparented organs keep their existing status +and reference identity, and environment backends receive `update_index!` calls +after each domain step. + +Done when: + +- new organs are added through one runtime API. Implemented by reusing + `add_organ!` from graph-domain models. +- domain-local and global status views are updated. Implemented because + `DomainSimulation` reads the live graph-domain status vectors. +- graph-domain output publication includes newly registered statuses. + Implemented in the timestep-interleaved domain runner. +- environment indexes can be updated by the backend. Implemented through + `update_index!(backend, entities)` after each domain step. +- organs can be removed through one runtime API. Implemented with + `remove_organ!`, which removes the node status, detaches downstream + `RefVector` references, removes node-scoped temporal cache and stream entries, + and deletes the MTG node. Terminal deletion is the default; internal-node + subtree deletion is available with `recursive=true`. +- already simulated organs can be reparented through one runtime API. + Implemented with `reparent_organ!` for moves inside the same active + `GraphSimulation`. +- output and temporal buffers are resized or lazily extended for all covered + dynamic multi-rate cases. Regular graph outputs use the existing + `save_results!` resizing path. Dynamic producer streams are covered for an + hourly producer added during the run and consumed by a daily model with + `Integrate()`. Online `OutputRequest(...)` exports are covered for a + dynamically created leaf using plant scope, several leaves created in the + same timestep, and custom callable scopes returning `ScopeId`. Terminal + removal is covered for direct `RefVector` coupling, multirate temporal + streams, repeated terminal create/remove cycles, and recursive internal-node + subtree deletion. Same-simulation reparenting is covered for topology + mutation while preserving status and reference identity. + +## Current First Example + +The first executable example should use: + +- two plant domains with different parameters and several hourly models each; +- one soil domain with several hourly models; +- one scene domain with a daily evapotranspiration model; +- explicit `AllDomains(...)` dependencies from the scene model to plant + transpiration and soil evaporation; +- `Dates.Hour(1)` for plant/soil domains and `Dates.Day(1)` for the scene + model. diff --git a/docs/src/domain_simulation.md b/docs/src/domain_simulation.md new file mode 100644 index 000000000..226d6bec0 --- /dev/null +++ b/docs/src/domain_simulation.md @@ -0,0 +1,360 @@ +# Domain simulations + +Domain simulations let you reuse complete plant, soil, scene, or environment +model mappings without renaming their internal scales and processes. + +This is useful when a scene contains several plant species. Each species can +keep its own `ModelMapping`, parameters, hard dependencies, multiscale mappings, +and rates. A `SimulationMapping` then assembles these domains and makes +cross-domain coupling explicit. + +The example on this page is deliberately simple. The numbers are arbitrary and +chosen only to show domain coupling and multi-rate execution. + +## A two-plant scene with shared soil + +First we define a few small models. The plant domains compute absorbed radiation +and transpiration hourly. The soil domain computes water content and evaporation +hourly. The scene domain runs daily and consumes all plant transpiration plus +soil evaporation through explicit `AllDomains(...)` value dependencies. These +dependencies read already-published producer streams; they do not manually run +the producer models. + +```@example domains +using PlantSimEngine +using PlantMeteo +using Dates + +PlantSimEngine.@process "doc_domain_absorbed_radiation" verbose=false +PlantSimEngine.@process "doc_domain_plant_transpiration" verbose=false +PlantSimEngine.@process "doc_domain_soil_water" verbose=false +PlantSimEngine.@process "doc_domain_soil_evaporation" verbose=false +PlantSimEngine.@process "doc_domain_scene_evapotranspiration" verbose=false + +struct DocDomainAbsorbedRadiationModel <: AbstractDoc_Domain_Absorbed_RadiationModel + coefficient::Float64 +end + +PlantSimEngine.inputs_(::DocDomainAbsorbedRadiationModel) = NamedTuple() +PlantSimEngine.outputs_(::DocDomainAbsorbedRadiationModel) = (absorbed_radiation=0.0,) +PlantSimEngine.meteo_inputs_(::DocDomainAbsorbedRadiationModel) = (Ri_PAR_f=0.0,) + +function PlantSimEngine.run!(model::DocDomainAbsorbedRadiationModel, models, status, meteo, constants=nothing, extra=nothing) + status.absorbed_radiation = model.coefficient * meteo.Ri_PAR_f + return nothing +end + +struct DocDomainPlantTranspirationModel <: AbstractDoc_Domain_Plant_TranspirationModel + coefficient::Float64 +end + +PlantSimEngine.inputs_(::DocDomainPlantTranspirationModel) = (absorbed_radiation=0.0,) +PlantSimEngine.outputs_(::DocDomainPlantTranspirationModel) = (transpiration=0.0,) + +function PlantSimEngine.run!(model::DocDomainPlantTranspirationModel, models, status, meteo, constants=nothing, extra=nothing) + status.transpiration = model.coefficient * status.absorbed_radiation + return nothing +end + +struct DocDomainSoilWaterModel <: AbstractDoc_Domain_Soil_WaterModel + baseline::Float64 +end + +PlantSimEngine.inputs_(::DocDomainSoilWaterModel) = NamedTuple() +PlantSimEngine.outputs_(::DocDomainSoilWaterModel) = (soil_water_content=0.0,) +PlantSimEngine.meteo_inputs_(::DocDomainSoilWaterModel) = (T=0.0,) + +function PlantSimEngine.run!(model::DocDomainSoilWaterModel, models, status, meteo, constants=nothing, extra=nothing) + status.soil_water_content = model.baseline - 0.001 * meteo.T + return nothing +end + +struct DocDomainSoilEvaporationModel <: AbstractDoc_Domain_Soil_EvaporationModel + coefficient::Float64 +end + +PlantSimEngine.inputs_(::DocDomainSoilEvaporationModel) = (soil_water_content=0.0,) +PlantSimEngine.outputs_(::DocDomainSoilEvaporationModel) = (evaporation=0.0,) +PlantSimEngine.meteo_inputs_(::DocDomainSoilEvaporationModel) = (T=0.0,) + +function PlantSimEngine.run!(model::DocDomainSoilEvaporationModel, models, status, meteo, constants=nothing, extra=nothing) + status.evaporation = model.coefficient * status.soil_water_content * meteo.T + return nothing +end + +struct DocDomainSceneEvapotranspirationModel <: AbstractDoc_Domain_Scene_EvapotranspirationModel end + +PlantSimEngine.inputs_(::DocDomainSceneEvapotranspirationModel) = NamedTuple() +PlantSimEngine.outputs_(::DocDomainSceneEvapotranspirationModel) = (evapotranspiration=0.0,) + +PlantSimEngine.dep(::DocDomainSceneEvapotranspirationModel) = ( + plant_transpiration=AllDomains(kind=:plant, process=:doc_domain_plant_transpiration, var=:transpiration, policy=Integrate()), + soil_evaporation=AllDomains(kind=:soil, process=:doc_domain_soil_evaporation, var=:evaporation, policy=Integrate()), +) + +function PlantSimEngine.run!(::DocDomainSceneEvapotranspirationModel, models, status, meteo, constants=nothing, extra=nothing) + plant_values = dependency_values(extra, :plant_transpiration) + soil_values = dependency_values(extra, :soil_evaporation) + status.evapotranspiration = + sum(filter(x -> !isnothing(x), plant_values)) + + sum(filter(x -> !isnothing(x), soil_values)) + return nothing +end +nothing +``` + +Now each domain can be built independently. The oil palm and maize mappings use +the same model types but different parameters. In a real application these +could be completely different plant models, with different processes and +different internal mappings. + +```@example domains +oil_palm_mapping = ModelMapping( + ModelSpec(DocDomainAbsorbedRadiationModel(0.5)) |> TimeStepModel(Hour(1)), + ModelSpec(DocDomainPlantTranspirationModel(0.01)) |> TimeStepModel(Hour(1)), + status=(absorbed_radiation=0.0, transpiration=0.0), +) + +maize_mapping = ModelMapping( + ModelSpec(DocDomainAbsorbedRadiationModel(0.3)) |> TimeStepModel(Hour(1)), + ModelSpec(DocDomainPlantTranspirationModel(0.02)) |> TimeStepModel(Hour(1)), + status=(absorbed_radiation=0.0, transpiration=0.0), +) + +soil_mapping = ModelMapping( + ModelSpec(DocDomainSoilWaterModel(0.35)) |> TimeStepModel(Hour(1)), + ModelSpec(DocDomainSoilEvaporationModel(0.2)) |> TimeStepModel(Hour(1)), + status=(soil_water_content=0.0, evaporation=0.0), +) + +scene_mapping = ModelMapping( + ModelSpec(DocDomainSceneEvapotranspirationModel()) |> TimeStepModel(Day(1)), + status=(evapotranspiration=0.0,), +) + +simulation_mapping = SimulationMapping( + Domain(:oil_palm, oil_palm_mapping; kind=:plant), + Domain(:maize, maize_mapping; kind=:plant), + Domain(:soil, soil_mapping; kind=:soil), + Domain(:scene, scene_mapping; kind=:scene), +) +nothing +``` + +The meteorology is hourly. The plant and soil models run hourly, while the +scene model runs every day and integrates the producer streams. + +```@example domains +hourly_meteo = Weather([ + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Hour(1)) + for _ in 1:25 +]) + +sim = run!(simulation_mapping, hourly_meteo; check=true) +round(status(sim, :scene).evapotranspiration; digits=2) +``` + +The final value is the daily integral of the two plant transpiration streams and +the soil evaporation stream. + +## Inspecting the compiled simulation + +Domain simulations expose small structured explanation helpers. These are meant +for users and for agents that need to repair a mapping from concrete symbols. + +```@example domains +sort([(row.domain, row.kind) for row in explain_domains(sim)]) +``` + +```@example domains +sort([(row.domain, row.process, row.dt_seconds) for row in explain_schedule(sim)]) +``` + +```@example domains +sort( + [(row.dependency, string(row.producer), row.policy) for row in explain_domain_dependencies(sim)]; + by=string, +) +``` + +The model-level explanation also includes the weather variables declared with +`meteo_inputs_` and any mutable environment variables declared with +`meteo_outputs_`: + +```@example domains +[(row.domain, row.process, collect(keys(row.meteo_inputs))) for row in explain_domain_models(sim) if !isempty(row.meteo_inputs)] +``` + +## Explicit routes + +`AllDomains(...)` value dependencies are the most direct way for a scene model +to consume several domain outputs. Routes are another option when you want to +materialize values into the target status before the target model runs. + +```julia +Route( + from=AllDomains(kind=:plant, process=:plant_transpiration, var=:transpiration), + to=DomainRouteTarget(:scene, var=:plant_transpirations, process=:scene_evapotranspiration), + cardinality=ManyToOneVector(), +) +``` + +Use `ManyToOneVector()` when the target model needs one value per matching +producer, and `ManyToOneAggregate(f)` when it needs a scalar reduction. For an +MTG-backed target domain, `OneToManyBroadcast()` can broadcast one source value +into every status at the target scale before that domain runs. + +When graph-domain values are aggregated across time, PlantSimEngine aligns them +by MTG node id. Growth and pruning inside the aggregation window therefore do +not require every timestep to publish vectors with the same length. + +## Hard-domain dependencies + +Use `HardDomains(...)` when a model must manually run selected producer +models, for example to control an iterative energy-balance solver: + +```julia +PlantSimEngine.dep(::SceneEnergyBalanceModel) = ( + leaf_energy=HardDomains(kind=:plant, scale=:Leaf, process=:leaf_energy_balance), +) + +function PlantSimEngine.run!(::SceneEnergyBalanceModel, models, status, meteo, constants=nothing, extra=nothing) + leaf_targets = dependency_targets(extra, :leaf_energy) + for iteration in 1:10 + for target in leaf_targets + run_target!(target) + end + converged(status) && break + end + return nothing +end +``` + +Each target represents one selected model on one status. For an MTG-backed +producer domain, this means one target per matching node status, such as one +target per leaf. `run_target!` mutates that status like a normal model call, +but it does not append to domain streams or outputs unless `publish=true` is +requested. Trial iterations should usually run with `publish=false`, then the +final accepted hard-dependency call can use `publish=true`. + +For same-status hard dependencies, the same target API can replace direct model +calls: + +```julia +function PlantSimEngine.run!(::PhyllochronModel, models, status, meteo, constants, extra) + run_target!(models, status, :phytomer_emission; meteo=meteo, constants=constants, extra=extra) + return nothing +end +``` + +## MTG-backed plant domains + +The same `Domain` wrapper can be used around a scale-keyed `ModelMapping`. +When running on an MTG, the `selector` decides which subtree roots belong to +the domain: + +```julia +oil_palm = Domain(:oil_palm, xpalm_mapping; kind=:plant, selector=node -> node[:species] == :oil_palm) +maize = Domain(:maize, maize_mapping; kind=:plant, selector=node -> node[:species] == :maize) +sim = run!(scene_mtg, SimulationMapping(oil_palm, maize, scene), meteo) +``` + +Status inspection keeps both the domain-local view and the global scale view: + +```julia +status(sim, :oil_palm, :Leaf) # only oil palm leaves +status(sim, :maize, :Leaf) # only maize leaves +status(sim, :Leaf) # all leaves across graph-backed domains +``` + +If a declared graph scale currently has no active statuses, for example after +pruning all leaves, these status queries return `Status[]`. The same empty scale +is reported by `explain_domain_statuses(sim)` with `nstatuses=0`. + +Selectors can also match several non-overlapping roots, for example +`selector=:Plant` to run one mapping over every plant subtree. Selectors that +match overlapping roots are rejected because they would register the same organ +twice. + +## Meteorology and microclimate + +Plain meteorology passed to `run!(SimulationMapping, meteo)` is wrapped as a +`GlobalConstant` environment backend. This preserves the existing behavior: +every object sees the same weather row at a given timestep. +If a model declares `meteo_inputs_`, running without meteorology now fails +during validation with the missing environment variables, instead of failing +later inside the model code. + +Spatial or mutable microclimate should live in a specialized backend, not in +PlantSimEngine itself. A backend subtypes `AbstractEnvironmentBackend` and +implements the sampling and update hooks: + +```julia +PlantSimEngine.sample(backend, variable, support, time) +PlantSimEngine.scatter!(backend, variable, support, value, time) +PlantSimEngine.update_index!(backend, entities) +``` + +Models declare which environment variables they read and write: + +```julia +PlantSimEngine.meteo_inputs_(::LeafEnergyBalanceModel) = ( + T=0.0, + Rh=0.0, + Wind=0.0, + Ri_PAR_f=0.0, + CO2=400.0, +) + +PlantSimEngine.meteo_outputs_(::MicroclimateUpdateModel) = (T=0.0,) +``` + +`EnvironmentSupport(domain, scale, process, status)` is passed to the backend, +so an octree, voxel, layered-canopy, or CFD backend can decide how to sample the +environment for the current organ or plant. In graph-backed domains, `status` +is the current node status. + +## Updating a variable + +Duplicate canonical writers are still errors by default. If a model is meant to +update a variable already produced at the same scale, declare that scenario rule +on the updating `ModelSpec`: + +```julia +ModelSpec(LeafPruningModel()) |> + Updates(:leaf_biomass; after=:carbon_allocation) +``` + +Only variables with several writers need an `Updates(...)` declaration. The +declaration adds an ordering edge and downstream consumers infer the terminal +updater as the effective source. + +## Growth and topology changes + +MTG-backed domain models receive the underlying `GraphSimulation` as `extra`, +so existing growth models can keep using the MTG registration APIs: + +```julia +add_organ!(parent_node, extra, "+", :Leaf, 2; check=true) +remove_organ!(leaf_node, extra) +remove_organ!(internode_node, extra; recursive=true) +reparent_organ!(leaf_node, new_parent_node, extra) +``` + +These helpers update statuses, multiscale `RefVector`s, temporal streams, and +domain outputs. `update_index!(backend, entities)` is called after each domain +step so spatial environment backends can refresh their entity index after +growth, pruning, or geometry changes. + +## Current boundaries + +Cross-domain dependencies are explicit. PlantSimEngine does not infer them from +matching variable names across domains. + +The domain layer defines the environment backend protocol, but it does not +implement an octree, voxel grid, or energy-balance solver. Those belong in +domain packages that provide models or environment backends. + +Scene domains run after plant and soil domains in the current acyclic runner. +Routes that feed an MTG-backed target domain therefore need their source domain +to run earlier in the same timestep. diff --git a/docs/src/model_traits.md b/docs/src/model_traits.md index 6c9633451..c8fd7bc12 100644 --- a/docs/src/model_traits.md +++ b/docs/src/model_traits.md @@ -74,7 +74,7 @@ Supported forms include: - range: `(Dates.Minute(30), Dates.Hour(2))`; - named tuple: `(; required=..., preferred=...)`. -`required` is enforced when runtime uses meteo-derived timestep. +`required` is enforced when runtime uses meteo-derived timestep. `preferred` is informational only. ### `meteo_hint(::Type{<:MyModel})` @@ -98,6 +98,42 @@ Where: - `bindings` is compatible with `MeteoBindings(...)`, - `window` is compatible with `MeteoWindow(...)`. +### `meteo_inputs_(::MyModel)` +### `meteo_outputs_(::MyModel)` + +Declare meteorology or microclimate variables separately from object status +variables. + +Default: + +```julia +PlantSimEngine.meteo_inputs_(::AbstractModel) = NamedTuple() +PlantSimEngine.meteo_outputs_(::AbstractModel) = NamedTuple() +``` + +Use `meteo_inputs_` for variables read from the weather or environment backend: + +```julia +PlantSimEngine.meteo_inputs_(::LeafEnergyBalanceModel) = ( + T=0.0, + Rh=0.0, + Wind=0.0, + Ri_PAR_f=0.0, + CO2=400.0, +) +``` + +Use `meteo_outputs_` when a model updates a mutable environment backend, for +example a microclimate model updating local air temperature: + +```julia +PlantSimEngine.outputs_(::MicroclimateUpdateModel) = (T=0.0,) +PlantSimEngine.meteo_outputs_(::MicroclimateUpdateModel) = (T=0.0,) +``` + +The current runtime scatters `meteo_outputs_` from status variables, so the +same variable should usually be declared in `outputs_` as well. + ### `TimeStepDependencyTrait(::Type{<:MyModel})` ### `ObjectDependencyTrait(::Type{<:MyModel})` @@ -127,6 +163,8 @@ For model-level traits, the documented set is now: - `output_policy`, - `timestep_hint`, - `meteo_hint`, +- `meteo_inputs_`, +- `meteo_outputs_`, - `TimeStepDependencyTrait`, - `ObjectDependencyTrait`. diff --git a/docs/src/step_by_step/advanced_coupling.md b/docs/src/step_by_step/advanced_coupling.md index 07b81c8bb..9fc612cff 100644 --- a/docs/src/step_by_step/advanced_coupling.md +++ b/docs/src/step_by_step/advanced_coupling.md @@ -33,7 +33,7 @@ When run, `Process2Model` calls another process's [`run!`](@ref) function explic ```julia function PlantSimEngine.run!(::Process2Model, models, status, meteo, constants, extra) # computing var3 using process1: - run!(models.process1, models, status, meteo, constants) + run_target!(models, status, :process1; meteo=meteo, constants=constants, extra=extra) # computing var4 and var5: status.var4 = status.var3 * 2.0 status.var5 = status.var4 + 1.0 * meteo.T + 2.0 * meteo.Wind + 3.0 * meteo.Rh @@ -61,4 +61,4 @@ PlantSimEngine.dep(::Process2Model) = (process1=Process1Model,) ## Examples in the wild -You can find a typical example in a companion package: [PlantBioPhysics.jl](https://github.com/VEZY/PlantBiophysics.jl). An energy balance model, the [Monteith model](https://github.com/VEZY/PlantBiophysics.jl/blob/master/src/processes/energy/Monteith.jl), needs to [iteratively run a photosynthesis model](https://github.com/VEZY/PlantBiophysics.jl/blob/c1a75f294109d52dc619f764ce51c6ca1ea897e8/src/processes/energy/Monteith.jl#L154) in its [`run!`](@ref) function. \ No newline at end of file +You can find a typical example in a companion package: [PlantBioPhysics.jl](https://github.com/VEZY/PlantBiophysics.jl). An energy balance model, the [Monteith model](https://github.com/VEZY/PlantBiophysics.jl/blob/master/src/processes/energy/Monteith.jl), needs to [iteratively run a photosynthesis model](https://github.com/VEZY/PlantBiophysics.jl/blob/c1a75f294109d52dc619f764ce51c6ca1ea897e8/src/processes/energy/Monteith.jl#L154) in its [`run!`](@ref) function. diff --git a/examples/dummy.jl b/examples/dummy.jl index f3a556f1d..82dcde9f1 100644 --- a/examples/dummy.jl +++ b/examples/dummy.jl @@ -37,7 +37,7 @@ PlantSimEngine.outputs_(::Process2Model) = (var4=-Inf, var5=-Inf) PlantSimEngine.dep(::Process2Model) = (process1=AbstractProcess1Model,) function PlantSimEngine.run!(::Process2Model, models, status, meteo, constants=nothing, extra=nothing) # computing var3 using process1: - PlantSimEngine.run!(models.process1, models, status, meteo, constants) + PlantSimEngine.run_target!(models, status, :process1; meteo=meteo, constants=constants, extra=extra) # computing var4 and var5: status.var4 = status.var3 * 2.0 status.var5 = status.var4 + 1.0 * meteo.T + 2.0 * meteo.Wind + 3.0 * meteo.Rh @@ -63,7 +63,7 @@ PlantSimEngine.outputs_(::Process3Model) = (var4=-Inf, var6=-Inf,) PlantSimEngine.dep(::Process3Model) = (process2=Process2Model,) function PlantSimEngine.run!(::Process3Model, models, status, meteo, constants=nothing, extra=nothing) # computing var3 using process1: - PlantSimEngine.run!(models.process2, models, status, meteo, constants, extra) + PlantSimEngine.run_target!(models, status, :process2; meteo=meteo, constants=constants, extra=extra) # re-computing var4: status.var4 = status.var4 * 2.0 status.var6 = status.var5 + status.var4 diff --git a/examples/maespa_domain_example.jl b/examples/maespa_domain_example.jl new file mode 100644 index 000000000..9833fd335 --- /dev/null +++ b/examples/maespa_domain_example.jl @@ -0,0 +1,363 @@ +using Dates +using PlantMeteo +using PlantSimEngine +using MultiScaleTreeGraph + +PlantSimEngine.@process "tuzet" verbose = false +PlantSimEngine.@process "fvcb" verbose = false +PlantSimEngine.@process "leaf_eb" verbose = false +PlantSimEngine.@process "soil_water" verbose = false +PlantSimEngine.@process "scene_eb" verbose = false +PlantSimEngine.@process "alloc_a" verbose = false +PlantSimEngine.@process "alloc_b" verbose = false + +sat_vp_kpa(T) = 0.6108 * exp(17.27 * T / (T + 237.3)) +vpd_kpa(meteo) = max(0.02, sat_vp_kpa(meteo.T) * (1.0 - meteo.Rh)) +rh_from_vpd(T, vpd) = clamp(1.0 - vpd / sat_vp_kpa(T), 0.05, 0.99) +duration_seconds(meteo) = Dates.value(Dates.Millisecond(meteo.duration)) / 1000.0 + +struct Tuzet <: AbstractTuzetModel + sf::Float64 + psi50::Float64 + g1::Float64 +end + +PlantSimEngine.inputs_(::Tuzet) = (psi_leaf=-0.5,) +PlantSimEngine.outputs_(::Tuzet) = (fpsi=1.0, g1_eff=4.0) + +function PlantSimEngine.run!(m::Tuzet, models, status, meteo, constants, extra=nothing) + status.fpsi = clamp((1.0 + exp(m.sf * m.psi50)) / (1.0 + exp(m.sf * (m.psi50 - status.psi_leaf))), 0.0, 1.0) + status.g1_eff = m.g1 * status.fpsi + return nothing +end + +struct FvCB <: AbstractFvcbModel + Vcmax::Float64 + Jmax::Float64 + Rd::Float64 + gamma_star::Float64 + Kc::Float64 + g0::Float64 +end + +PlantSimEngine.dep(::FvCB) = (tuzet=AbstractTuzetModel,) +PlantSimEngine.inputs_(::FvCB) = (apar=0.0, Cs=400.0, Tleaf=20.0, psi_leaf=-0.5) +PlantSimEngine.outputs_(::FvCB) = (An=0.0, gs=0.02, fpsi=1.0) + +function PlantSimEngine.run!(m::FvCB, models, status, meteo, constants, extra=nothing) + run_target!(models, status, :tuzet; meteo=meteo, constants=constants, extra=extra) + temp_factor = exp(0.06 * (status.Tleaf - 25.0)) + vcmax = m.Vcmax * temp_factor + j = m.Jmax * status.apar / (status.apar + 2.1 * m.Jmax + eps()) + wc = vcmax * max(status.Cs - m.gamma_star, 0.0) / (status.Cs + m.Kc) + wj = j * max(status.Cs - m.gamma_star, 0.0) / (4.0 * (status.Cs + 2.0 * m.gamma_star)) + status.An = max(0.0, min(wc, wj) - m.Rd) + status.gs = max(m.g0, m.g0 + status.g1_eff * status.An / max(status.Cs, 80.0)) + return nothing +end + +struct LeafEB <: AbstractLeaf_EbModel + absorptance::Float64 + emissive_loss::Float64 + maxiter::Int + tol::Float64 +end + +PlantSimEngine.dep(::LeafEB) = (fvcb=AbstractFvcbModel,) +PlantSimEngine.inputs_(::LeafEB) = ( + Tair=20.0, + VPD=1.0, + wind=1.0, + par=600.0, + psi_soil=-0.2, + Kplant=6.0, + leaf_area=0.015, +) +PlantSimEngine.outputs_(::LeafEB) = ( + apar=0.0, + Tleaf=20.0, + Cs=400.0, + Dl=1.0, + E=0.0, + H=0.0, + Rn=0.0, + An=0.0, + gs=0.02, + fpsi=1.0, + psi_leaf=-0.2, + leaf_carbon=0.0, +) + +function PlantSimEngine.run!(m::LeafEB, models, status, meteo, constants, extra=nothing) + Tleaf = isfinite(status.Tleaf) ? status.Tleaf : status.Tair + E = max(status.E, 0.0) + for _ in 1:m.maxiter + status.Tleaf = Tleaf + status.psi_leaf = status.psi_soil - E / max(status.Kplant, 1.0e-6) + status.apar = m.absorptance * status.par + status.Dl = max(0.02, status.VPD * sat_vp_kpa(Tleaf) / sat_vp_kpa(status.Tair)) + status.Cs = clamp(400.0 - 1.6 * status.An / max(status.gs, 0.02), 120.0, 420.0) + run_target!(models, status, :fvcb; meteo=meteo, constants=constants, extra=extra) + + gb = 0.12 + 0.08 * sqrt(max(status.wind, 0.05)) + status.Rn = m.absorptance * 0.48 * status.par - m.emissive_loss * (Tleaf - status.Tair) + E = max(0.0, 0.20 * status.gs * status.Dl) + lambdaE = 44.0 * E + status.H = 22.0 * gb * (Tleaf - status.Tair) + Tnew = status.Tair + (status.Rn - lambdaE) / max(22.0 * gb + 4.0, 1.0) + abs(Tnew - Tleaf) < m.tol && (Tleaf = Tnew; break) + Tleaf = 0.55 * Tleaf + 0.45 * Tnew + end + status.Tleaf = Tleaf + status.E = E + return nothing +end + +struct SoilWater <: AbstractSoil_WaterModel + theta_sat::Float64 + psi_e::Float64 + b::Float64 + depth1::Float64 + depth2::Float64 + use_second_layer::Bool +end + +PlantSimEngine.inputs_(::SoilWater) = (transpiration=0.0, infiltration=0.0) +PlantSimEngine.outputs_(::SoilWater) = (theta1=0.32, theta2=0.34, psi_soil=-0.1) + +function PlantSimEngine.run!(m::SoilWater, models, status, meteo, constants, extra=nothing) + withdrawal = max(status.transpiration, 0.0) + recharge = max(status.infiltration, 0.0) + status.theta1 = clamp(status.theta1 + (recharge - 0.7 * withdrawal) / max(m.depth1 * 1000.0, 1.0), 0.04, m.theta_sat) + if m.use_second_layer + status.theta2 = clamp(status.theta2 - 0.3 * withdrawal / max(m.depth2 * 1000.0, 1.0), 0.04, m.theta_sat) + end + rel = clamp(status.theta1 / m.theta_sat, 0.05, 1.0) + status.psi_soil = m.psi_e * rel^(-m.b) + return nothing +end + +struct SceneEB <: AbstractScene_EbModel + maxiter::Int + tol_T::Float64 + tol_VPD::Float64 +end + +PlantSimEngine.dep(::SceneEB) = ( + leaf_eb=HardDomains(kind=:plant, scale=:Leaf, process=:leaf_eb), + soil=HardDomains(kind=:soil, process=:soil_water), +) +PlantSimEngine.inputs_(::SceneEB) = NamedTuple() +PlantSimEngine.outputs_(::SceneEB) = ( + canopy_Tair=20.0, + canopy_VPD=1.0, + scene_transpiration=0.0, + scene_assimilation=0.0, + psi_soil=-0.1, + iterations=0, +) + +function PlantSimEngine.run!(m::SceneEB, models, status, meteo, constants, extra) + leaf_targets = dependency_targets(extra, :leaf_eb) + soil_target = only(dependency_targets(extra, :soil)) + Tair = meteo.T + VPD = vpd_kpa(meteo) + psi_soil = soil_target.status.psi_soil + final_meteo = meteo + + for iter in 1:m.maxiter + total_H = 0.0 + total_E = 0.0 + total_A = 0.0 + local_meteo = Atmosphere( + T=Tair, + Rh=rh_from_vpd(Tair, VPD), + Wind=meteo.Wind, + Ri_PAR_f=meteo.Ri_PAR_f, + duration=meteo.duration, + ) + for target in leaf_targets + target.status.Tair = Tair + target.status.VPD = VPD + target.status.wind = meteo.Wind + target.status.par = meteo.Ri_PAR_f + target.status.psi_soil = psi_soil + run_target!(target; meteo=local_meteo) + total_H += target.status.H * target.status.leaf_area + total_E += target.status.E * target.status.leaf_area + total_A += target.status.An * target.status.leaf_area + end + + Tnew = meteo.T + 0.30 * total_H / max(length(leaf_targets), 1) + VPDnew = max(0.02, vpd_kpa(meteo) + 0.04 * (Tnew - meteo.T) - 0.30 * total_E) + status.iterations = iter + final_meteo = local_meteo + if abs(Tnew - Tair) < m.tol_T && abs(VPDnew - VPD) < m.tol_VPD + Tair = Tnew + VPD = VPDnew + break + end + Tair = 0.50 * Tair + 0.50 * Tnew + VPD = 0.50 * VPD + 0.50 * VPDnew + end + + total_E = 0.0 + total_A = 0.0 + for target in leaf_targets + target.status.Tair = Tair + target.status.VPD = VPD + target.status.psi_soil = psi_soil + run_target!(target; meteo=final_meteo, publish=true) + target.status.leaf_carbon += target.status.An * target.status.leaf_area * duration_seconds(meteo) * 12.0e-6 + total_E += target.status.E * target.status.leaf_area + total_A += target.status.An * target.status.leaf_area + end + + transpiration_mm = total_E * duration_seconds(meteo) * 18.0e-6 + soil_target.status.transpiration = transpiration_mm + soil_target.status.infiltration = 0.0 + run_target!(soil_target; publish=true) + + status.canopy_Tair = Tair + status.canopy_VPD = VPD + status.scene_transpiration = transpiration_mm + status.scene_assimilation = total_A + status.psi_soil = soil_target.status.psi_soil + return nothing +end + +alloc_inputs() = (leaf_carbon=[0.0],) +alloc_outputs() = (daily_growth=0.0, leaf_pool=0.0, wood_pool=0.0) + +function allocate!(status, leaf_fraction, wood_fraction) + carbon = sum(status.leaf_carbon) + status.daily_growth = carbon + status.leaf_pool += leaf_fraction * carbon + status.wood_pool += wood_fraction * carbon + return nothing +end + +struct AllocA <: AbstractAlloc_AModel + leaf_fraction::Float64 + wood_fraction::Float64 +end + +struct AllocB <: AbstractAlloc_BModel + leaf_fraction::Float64 + wood_fraction::Float64 +end + +PlantSimEngine.inputs_(::AllocA) = alloc_inputs() +PlantSimEngine.outputs_(::AllocA) = alloc_outputs() +PlantSimEngine.inputs_(::AllocB) = alloc_inputs() +PlantSimEngine.outputs_(::AllocB) = alloc_outputs() + +PlantSimEngine.run!(m::AllocA, models, status, meteo, constants, extra=nothing) = + allocate!(status, m.leaf_fraction, m.wood_fraction) +PlantSimEngine.run!(m::AllocB, models, status, meteo, constants, extra=nothing) = + allocate!(status, m.leaf_fraction, m.wood_fraction) + +function build_maespa_scene() + scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + plant_a = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + plant_a[:species] = :A + axis_a = Node(plant_a, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) + for i in 1:2 + leaf = Node(axis_a, MultiScaleTreeGraph.NodeMTG("+", :Leaf, i, 3)) + leaf[:species] = :A + end + + plant_b = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 2, 1)) + plant_b[:species] = :B + axis_b = Node(plant_b, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) + for i in 1:3 + leaf = Node(axis_b, MultiScaleTreeGraph.NodeMTG("+", :Leaf, i, 3)) + leaf[:species] = :B + end + return scene +end + +has_species(node, species) = try + node[:species] == species +catch + false +end + +function maespa_mapping() + leaf_a = ( + ModelSpec(LeafEB(0.86, 4.5, 20, 0.02)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(FvCB(72.0, 135.0, 1.1, 42.0, 404.0, 0.015)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(Tuzet(3.2, -1.4, 4.8)) |> TimeStepModel(Dates.Hour(1)), + Status(Tair=20.0, VPD=1.0, wind=1.0, par=0.0, psi_soil=-0.1, Kplant=7.5, leaf_area=0.018, leaf_carbon=0.0), + ) + leaf_b = ( + ModelSpec(LeafEB(0.82, 4.0, 20, 0.02)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(FvCB(58.0, 110.0, 1.3, 42.0, 404.0, 0.012)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(Tuzet(3.8, -1.1, 3.5)) |> TimeStepModel(Dates.Hour(1)), + Status(Tair=20.0, VPD=1.0, wind=1.0, par=0.0, psi_soil=-0.1, Kplant=5.2, leaf_area=0.014, leaf_carbon=0.0), + ) + + plant_a = ModelMapping( + :Plant => ( + ModelSpec(AllocA(0.35, 0.55)) |> + MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]]) |> + TimeStepModel(ClockSpec(24.0, 0.0)), + Status(leaf_pool=0.0, wood_pool=0.0), + ), + :Leaf => leaf_a, + ) + plant_b = ModelMapping( + :Plant => ( + ModelSpec(AllocB(0.55, 0.35)) |> + MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]]) |> + TimeStepModel(ClockSpec(24.0, 0.0)), + Status(leaf_pool=0.0, wood_pool=0.0), + ), + :Leaf => leaf_b, + ) + soil = ModelMapping( + ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75, true)) |> TimeStepModel(Dates.Hour(1)), + status=(theta1=0.33, theta2=0.36, psi_soil=-0.10, transpiration=0.0, infiltration=0.0), + ) + scene = ModelMapping( + ModelSpec(SceneEB(25, 0.03, 0.005)) |> TimeStepModel(Dates.Hour(1)), + status=(canopy_Tair=20.0, canopy_VPD=1.0, scene_transpiration=0.0, scene_assimilation=0.0, psi_soil=-0.1, iterations=0), + ) + + return SimulationMapping( + Domain(:plant_A, plant_a; kind=:plant, selector=node -> MultiScaleTreeGraph.symbol(node) == :Plant && has_species(node, :A)), + Domain(:plant_B, plant_b; kind=:plant, selector=node -> MultiScaleTreeGraph.symbol(node) == :Plant && has_species(node, :B)), + Domain(:soil, soil; kind=:soil), + Domain(:scene, scene; kind=:scene), + ) +end + +function maespa_meteo(; nhours=24) + return Weather([ + Atmosphere( + T=22.0 + 5.0 * sinpi((hour - 7) / 12), + Rh=clamp(0.72 - 0.22 * sinpi((hour - 7) / 12), 0.35, 0.90), + Wind=1.2 + 0.3 * sinpi(hour / 12), + Ri_PAR_f=max(0.0, 900.0 * sinpi((hour - 6) / 12)), + duration=Dates.Hour(1), + ) + for hour in 1:nhours + ]) +end + +function run_maespa_example(; nhours=24, check=true) + mtg = build_maespa_scene() + mapping = maespa_mapping() + sim = run!(mtg, mapping, maespa_meteo(; nhours=nhours), check=check, executor=SequentialEx()) + return (mtg=mtg, mapping=mapping, simulation=sim) +end + +if abspath(PROGRAM_FILE) == @__FILE__ + result = run_maespa_example() + sim = result.simulation + println("leaf_count = ", length(status(sim, :Leaf))) + println("scene_transpiration = ", status(sim, :scene).scene_transpiration) + println("psi_soil = ", status(sim, :scene).psi_soil) + println("plant_A = ", only(status(sim, :plant_A, :Plant)).daily_growth) + println("plant_B = ", only(status(sim, :plant_B, :Plant)).daily_growth) +end diff --git a/src/PlantSimEngine.jl b/src/PlantSimEngine.jl index 1151b8f7a..0d5397170 100644 --- a/src/PlantSimEngine.jl +++ b/src/PlantSimEngine.jl @@ -68,6 +68,7 @@ include("dependencies/hard_dependencies.jl") include("dependencies/traversal.jl") include("dependencies/is_graph_cyclic.jl") include("dependencies/printing.jl") +include("dependencies/update_dependencies.jl") include("dependencies/dependencies.jl") include("dependencies/get_model_in_dependency_graph.jl") @@ -103,6 +104,10 @@ include("time/runtime/input_resolution.jl") include("time/runtime/publishers.jl") include("time/runtime/output_export.jl") include("time/runtime/meteo_sampling.jl") +include("time/runtime/environment_backends.jl") + +# Domain-aware simulation scaffolding: +include("domains/domain_simulation.jl") # Simulation: include("run.jl") @@ -125,17 +130,28 @@ export OutputCache, HoldLastCache, InterpolateCache, IntegrateCache, AggregateCa export TemporalState export OutputRequest, collect_outputs export effective_rate_summary -export ModelList, MultiScaleModel, ModelMapping, ModelSpec, TimeStepModel, InputBindings, MeteoBindings, MeteoWindow, OutputRouting, ScopeModel +export ModelList, MultiScaleModel, ModelMapping, ModelSpec, Updates, TimeStepModel, InputBindings, MeteoBindings, MeteoWindow, OutputRouting, ScopeModel export resolved_model_specs, explain_model_specs +export Domain, SimulationMapping, DomainSimulation, DomainModelKey, AllDomains, HardDomains +export Route, DomainRouteTarget, RouteCardinality +export ManyToOneVector, ManyToOneAggregate, OneToManyBroadcast, SpatialSample, SpatialScatterAdd +export dependency_values, dependency_targets, dependency_target, model_target, run_target!, ModelTarget, explain_domains, explain_domain_models, explain_domain_statuses, explain_schedule, explain_domain_dependencies, explain_routes export RMSE, NRMSE, EF, dr export Status, TimeStepTable, status export init_status! -export add_organ! +export add_organ!, remove_organ!, reparent_organ! export @process, process export to_initialize, is_initialized, init_variables, dep export inputs, outputs, variables, convert_outputs export timespec, output_policy, timestep_hint, meteo_hint -export input_bindings, meteo_bindings, meteo_window, output_routing, model_scope +export input_bindings, meteo_bindings, meteo_window, output_routing, model_scope, updates +export meteo_inputs, meteo_inputs_, meteo_outputs, meteo_outputs_ +export validate_meteo_inputs +export AbstractEnvironmentBackend, EnvironmentSupport, GlobalConstant +export environment_backend, environment_variables, base_step_seconds +export sample, sample_environment, scatter!, update_index! +export scatter_environment_outputs! +export explain_environment export run! export fit diff --git a/src/dependencies/dependencies.jl b/src/dependencies/dependencies.jl index d59031c17..b5862613a 100644 --- a/src/dependencies/dependencies.jl +++ b/src/dependencies/dependencies.jl @@ -76,6 +76,7 @@ dep(;models...) function dep(nsteps=1; verbose::Bool=true, vars...) hard_dep = hard_dependencies((; vars...), verbose=verbose) deps = soft_dependencies(hard_dep, nsteps) + apply_update_dependencies!(deps, _model_specs_for_dependency_updates((; vars...))) # Return the dependency graph return deps @@ -114,6 +115,7 @@ function dep(mapping::AbstractDict{Symbol,T}; verbose::Bool=true) where {T} # nodes that have no soft-dependencies, and we set them as root nodes of the soft-dependency graph. The other nodes are set as children # of the nodes that they depend on. dep_graph = soft_dependencies_multiscale(soft_dep_graphs_roots, reverse_multiscale_mapping, hard_dep_dict) + apply_update_dependencies!(dep_graph, _model_specs_for_dependency_updates(mapping)) # During the building of the soft-dependency graph, we identified the inputs and outputs of each dependency node, # and also defined **inputs** as MappedVar if they are multiscale, i.e. if they take their values from another scale. # What we are missing is that we need to also define **outputs** as multiscale if they are needed by another scale. diff --git a/src/dependencies/hard_dependencies.jl b/src/dependencies/hard_dependencies.jl index ba623980d..cb2f3704f 100644 --- a/src/dependencies/hard_dependencies.jl +++ b/src/dependencies/hard_dependencies.jl @@ -5,6 +5,8 @@ Compute the hard dependencies between models. """ +_is_domain_dependency_selector(x) = nameof(typeof(x)) in (:AllDomains, :HardDomains) + function _normalize_hard_dependency_scales(scales, process::Symbol, dependency_process::Symbol) if scales isa Symbol || scales isa AbstractString return [Symbol(scales)] @@ -41,6 +43,7 @@ function hard_dependencies(models; scale=nothing, verbose::Bool=true) length(level_1_dep) == 0 && continue # if there is no dependency we skip the iteration dep_graph[process].dependency = level_1_dep for (p, depend) in pairs(level_1_dep) # for each dependency of the model i. p=:leaf_rank; depend=pairs(level_1_dep)[p] + _is_domain_dependency_selector(depend) && continue # The dependency can be given as multiscale, e.g. `leaf_area=AbstractLeaf_AreaModel => [m.leaf_symbol],` # This means we should search this model in another scale. This is not done here, but after the call to this # function in the other method for `hard_dependencies` below. @@ -59,7 +62,8 @@ function hard_dependencies(models; scale=nothing, verbose::Bool=true) end if hasproperty(models, p) - if typeof(getfield(models, p)) <: depend + candidate_model = getfield(models, p) + if model_(candidate_model) isa depend parent_dep = dep_graph[process] push!(parent_dep.children, dep_graph[p]) for child in parent_dep.children @@ -68,7 +72,7 @@ function hard_dependencies(models; scale=nothing, verbose::Bool=true) else if verbose @info string( - "Model ", typeof(i).name.name, " from process ", process, + "Model ", typeof(model_(i)).name.name, " from process ", process, isnothing(scale) ? "" : " at scale $scale", " needs a model that is a subtype of ", depend, " in process ", p @@ -86,7 +90,7 @@ function hard_dependencies(models; scale=nothing, verbose::Bool=true) else if verbose @info string( - "Model ", typeof(i).name.name, " from process ", process, + "Model ", typeof(model_(i)).name.name, " from process ", process, isnothing(scale) ? "" : " at scale $scale", " needs a model that is a subtype of ", depend, " in process ", p, ", but the process is not parameterized in the ModelList." @@ -214,8 +218,8 @@ function hard_dependencies(mapping::AbstractDict{Symbol,T}; verbose::Bool=true) end dep_node_model = only(dep_node_model) - if !isa(dep_node_model.value, model_type) - error("Model `$(typeof(parent_node.value))` from scale $organ requires a model of type `$model_type` at scale $s as a hard dependency, but the model found for this process is of type $(typeof(dep_node_model.value)).") + if !(model_(dep_node_model.value) isa model_type) + error("Model `$(typeof(model_(parent_node.value)))` from scale $organ requires a model of type `$model_type` at scale $s as a hard dependency, but the model found for this process is of type $(typeof(model_(dep_node_model.value))).") end # We make a new node out of the previous one: diff --git a/src/dependencies/is_graph_cyclic.jl b/src/dependencies/is_graph_cyclic.jl index 918eb28b7..94fb0d928 100644 --- a/src/dependencies/is_graph_cyclic.jl +++ b/src/dependencies/is_graph_cyclic.jl @@ -12,15 +12,15 @@ Check if the dependency graph is cyclic. Return a boolean indicating if the graph is cyclic, and the stack of nodes as a vector. """ function is_graph_cyclic(dependency_graph::DependencyGraph; full_stack=false, warn=true) - visited = Dict{Pair{AbstractModel,Symbol},Bool}() - recursion_stack = Dict{Pair{AbstractModel,Symbol},Bool}() + visited = Dict{Pair{Any,Symbol},Bool}() + recursion_stack = Dict{Pair{Any,Symbol},Bool}() for node in values(dependency_graph.roots) visited[node.value=>node.scale] = false recursion_stack[node.value=>node.scale] = false end for (root, node) in dependency_graph.roots - cycle_vec = Vector{Pair{AbstractModel,Symbol}}() + cycle_vec = Vector{Pair{Any,Symbol}}() if is_graph_cyclic_(node, visited, recursion_stack, cycle_vec) if full_stack diff --git a/src/dependencies/update_dependencies.jl b/src/dependencies/update_dependencies.jl new file mode 100644 index 000000000..4d6f74cec --- /dev/null +++ b/src/dependencies/update_dependencies.jl @@ -0,0 +1,189 @@ +function _model_specs_for_dependency_updates(vars::NamedTuple) + return Dict( + :Default => Dict{Symbol,ModelSpec}( + process(model) => as_model_spec(model) for model in values(vars) + ) + ) +end + +_model_specs_for_dependency_updates(mapping::AbstractDict{Symbol,T}) where {T} = + Dict(scale => parse_model_specs(declarations) for (scale, declarations) in pairs(mapping)) + +function _update_variables_for_spec(spec::ModelSpec) + vars = Set{Symbol}() + for update in updates(spec) + union!(vars, update.variables) + end + return vars +end + +function _update_after_for_var(spec::ModelSpec, var::Symbol) + after = Symbol[] + for update in updates(spec) + var in update.variables || continue + append!(after, update.after) + end + return unique(after) +end + +function _canonical_output_vars(spec::ModelSpec) + vars = Symbol[] + routing = output_routing(spec) + for var in keys(outputs_(spec)) + mode = var in keys(routing) ? routing[var] : :canonical + mode == :stream_only && continue + push!(vars, var) + end + return vars +end + +function _validate_updates_for_scale(scale::Symbol, specs_at_scale::Dict{Symbol,ModelSpec}, ignored_processes::Set{Symbol}=Set{Symbol}()) + canonical_writers = Dict{Symbol,Vector{Symbol}}() + + for (process, spec) in specs_at_scale + model_outputs = Set(keys(outputs_(spec))) + for update in updates(spec) + isempty(update.after) && error( + "Updates declaration for process `$(process)` at scale `$(scale)` must specify `after=...`. ", + "Use for example `Updates(:var; after=:producer_process)`." + ) + for var in update.variables + var in model_outputs || error( + "Updates declaration for process `$(process)` at scale `$(scale)` mentions variable `$(var)`, ", + "but this model does not output `$(var)`." + ) + for after_process in update.after + haskey(specs_at_scale, after_process) || error( + "Updates declaration for variable `$(var)` in process `$(process)` at scale `$(scale)` ", + "references unknown process `$(after_process)`." + ) + var in keys(outputs_(specs_at_scale[after_process])) || error( + "Updates declaration `Updates(:$(var); after=:$(after_process))` in process `$(process)` ", + "at scale `$(scale)` requires `$(after_process)` to output `$(var)`." + ) + end + end + end + + process in ignored_processes && continue + for var in _canonical_output_vars(spec) + push!(get!(canonical_writers, var, Symbol[]), process) + end + end + + for (var, writers) in canonical_writers + length(writers) <= 1 && continue + updater_flags = Dict(process => (var in _update_variables_for_spec(specs_at_scale[process])) for process in writers) + primary_writers = [process for process in writers if !updater_flags[process]] + update_writers = [process for process in writers if updater_flags[process]] + + length(primary_writers) == 1 || error( + "Ambiguous canonical writers for variable `$(var)` at scale `$(scale)`: ", + join(writers, ", "), + ". Keep one primary writer and declare additional writers with `Updates(:$(var); after=:primary_process)`." + ) + + primary = only(primary_writers) + for updater in update_writers + after = _update_after_for_var(specs_at_scale[updater], var) + primary in after || error( + "Update writer `$(updater)` for variable `$(var)` at scale `$(scale)` must declare its primary producer in `after`. ", + "Use `Updates(:$(var); after=:$(primary))` or include `:$(primary)` in the `after` list." + ) + end + + for i in eachindex(update_writers), j in (i+1):length(update_writers) + left = update_writers[i] + right = update_writers[j] + left_after = _update_after_for_var(specs_at_scale[left], var) + right_after = _update_after_for_var(specs_at_scale[right], var) + (left in right_after || right in left_after) || error( + "Update writers `$(left)` and `$(right)` both update variable `$(var)` at scale `$(scale)` ", + "without an ordering relation. Declare one updater `after` the other." + ) + end + end + + return nothing +end + +function validate_update_dependencies( + model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}; + ignored_processes_by_scale::Dict{Symbol,Set{Symbol}}=Dict{Symbol,Set{Symbol}}() +) + for (scale, specs_at_scale) in model_specs + _validate_updates_for_scale(scale, specs_at_scale, get(ignored_processes_by_scale, scale, Set{Symbol}())) + end + return nothing +end + +function _soft_nodes_by_scale_process(graph::DependencyGraph) + nodes = Dict{Tuple{Symbol,Symbol},SoftDependencyNode}() + for node in traverse_dependency_graph(graph, false) + nodes[(node.scale, node.process)] = node + end + return nodes +end + +function _delete_root_for_node!(graph::DependencyGraph, node::SoftDependencyNode) + if haskey(graph.roots, node.process) + delete!(graph.roots, node.process) + end + key = node.scale => node.process + if haskey(graph.roots, key) + delete!(graph.roots, key) + end + return nothing +end + +function _add_update_edge!(graph::DependencyGraph, parent_node::SoftDependencyNode, child_node::SoftDependencyNode) + parent_node === child_node && error( + "Invalid Updates dependency: process `$(child_node.process)` cannot be ordered after itself." + ) + + child_node in parent_node.children || push!(parent_node.children, child_node) + if child_node.parent === nothing + child_node.parent = [parent_node] + elseif !(parent_node in child_node.parent) + push!(child_node.parent, parent_node) + end + _delete_root_for_node!(graph, child_node) + return nothing +end + +function apply_update_dependencies!(graph::DependencyGraph, model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}) + validate_hard_dependency_timestep_consistency(model_specs, graph) + ignored_processes_by_scale = _hard_dependency_children(graph) + validate_update_dependencies(model_specs; ignored_processes_by_scale=ignored_processes_by_scale) + has_updates = any(!isempty(updates(spec)) for specs_at_scale in values(model_specs) for spec in values(specs_at_scale)) + has_updates || return graph + + nodes = _soft_nodes_by_scale_process(graph) + + for (scale, specs_at_scale) in model_specs + ignored_processes = get(ignored_processes_by_scale, scale, Set{Symbol}()) + for (process, spec) in specs_at_scale + process in ignored_processes && continue + child_node = get(nodes, (scale, process), nothing) + isnothing(child_node) && continue + for update in updates(spec) + for after_process in update.after + parent_node = get(nodes, (scale, after_process), nothing) + isnothing(parent_node) && error( + "Updates declaration for process `$(process)` at scale `$(scale)` references `$(after_process)`, ", + "but that process is not an executable dependency node. It may be nested as a hard dependency." + ) + _add_update_edge!(graph, parent_node, child_node) + end + end + end + end + + iscyclic, cycle_vec = is_graph_cyclic(graph; warn=false) + iscyclic && error( + "Cyclic dependency detected after applying Updates declarations. Cycle: \n", + print_cycle(cycle_vec) + ) + + return graph +end diff --git a/src/domains/domain_simulation.jl b/src/domains/domain_simulation.jl new file mode 100644 index 000000000..05bd9488b --- /dev/null +++ b/src/domains/domain_simulation.jl @@ -0,0 +1,2177 @@ +""" + Domain(name, mapping; kind=:generic, selector=nothing) + Domain(name; kind, mapping, selector=nothing) + +Reusable model domain used by [`SimulationMapping`](@ref). + +This first implementation supports single-status domains backed by existing +`ModelMapping`s. The domain identity is kept alongside scale/process identity +so future multiscale, multi-plant, soil, scene, and environment domains can be +compiled into one simulation without renaming scales. +""" +struct Domain{M,S} + name::Symbol + kind::Symbol + mapping::M + selector::S +end + +function _normalize_domain_mapping(mapping) + mapping isa ModelMapping && return mapping + if mapping isa Tuple + status_index = findlast(x -> x isa Status, mapping) + if !isnothing(status_index) + status_index == length(mapping) || error( + "Raw tuple domain mappings may contain a positional Status only as the last element." + ) + return ModelMapping(mapping[begin:(end - 1)]...; status=last(mapping)) + end + return ModelMapping(mapping...) + end + return ModelMapping(mapping) +end + +function Domain(name::Union{Symbol,AbstractString}, mapping; kind::Union{Symbol,AbstractString}=:generic, selector=nothing) + normalized_mapping = copy(_normalize_domain_mapping(mapping)) + return Domain(Symbol(name), Symbol(kind), normalized_mapping, selector) +end + +function Domain(name::Union{Symbol,AbstractString}; kind::Union{Symbol,AbstractString}=:generic, mapping, selector=nothing) + return Domain(name, mapping; kind=kind, selector=selector) +end + +_domain_mapping(domain::Domain) = _normalize_domain_mapping(domain.mapping) + +""" + DomainModelKey(domain, scale, process) + +Stable key for one model process inside one domain and scale. +""" +struct DomainModelKey + domain::Symbol + scale::Symbol + process::Symbol +end + +Base.show(io::IO, key::DomainModelKey) = print(io, key.domain, "/", key.scale, "/", key.process) + +""" + AllDomains(; kind=nothing, domain=nothing, scale=nothing, process=nothing, var=nothing, policy=HoldLast()) + AllDomains(process; kwargs...) + +Value selector for scene-level models that intentionally consume output streams +from matching models in several domains. + +`policy` controls how producer streams are sampled when the consumer runs at a +different rate. `AllDomains` does not provide hard-dependency call-stack +control; use [`HardDomains`](@ref) when the parent model must manually run +the selected models. +""" +struct AllDomains{P<:SchedulePolicy} + kind::Union{Nothing,Symbol} + domain::Union{Nothing,Symbol} + scale::Union{Nothing,Symbol} + process::Union{Nothing,Symbol} + var::Union{Nothing,Symbol} + policy::P +end + +function AllDomains(; kind=nothing, domain=nothing, scale=nothing, process=nothing, var=nothing, policy::SchedulePolicy=HoldLast()) + return AllDomains( + isnothing(kind) ? nothing : Symbol(kind), + isnothing(domain) ? nothing : Symbol(domain), + isnothing(scale) ? nothing : Symbol(scale), + isnothing(process) ? nothing : Symbol(process), + isnothing(var) ? nothing : Symbol(var), + policy, + ) +end + +AllDomains(process::Union{Symbol,AbstractString}; kwargs...) = AllDomains(; process=Symbol(process), kwargs...) + +""" + HardDomains(; kind=nothing, domain=nothing, scale=nothing, process=nothing) + HardDomains(process; kwargs...) + +Selector for models that are cross-domain hard dependencies. A model declaring +`dep(model) = (; name=HardDomains(...))` +can retrieve executable targets with [`dependency_targets`](@ref) and manually +execute them with [`run_target!`](@ref). +""" +struct HardDomains + kind::Union{Nothing,Symbol} + domain::Union{Nothing,Symbol} + scale::Union{Nothing,Symbol} + process::Union{Nothing,Symbol} +end + +function HardDomains(; kind=nothing, domain=nothing, scale=nothing, process=nothing) + return HardDomains( + isnothing(kind) ? nothing : Symbol(kind), + isnothing(domain) ? nothing : Symbol(domain), + isnothing(scale) ? nothing : Symbol(scale), + isnothing(process) ? nothing : Symbol(process), + ) +end + +HardDomains(process::Union{Symbol,AbstractString}; kwargs...) = HardDomains(; process=Symbol(process), kwargs...) + +function _push_selector_term!(terms::Vector{String}, name::Symbol, value) + isnothing(value) || push!(terms, "$(name)=:$(value)") + return terms +end + +function _policy_display(policy::SchedulePolicy) + return string(nameof(typeof(policy)), "()") +end + +function Base.show(io::IO, selector::AllDomains) + terms = String[] + _push_selector_term!(terms, :kind, selector.kind) + _push_selector_term!(terms, :domain, selector.domain) + _push_selector_term!(terms, :scale, selector.scale) + _push_selector_term!(terms, :process, selector.process) + _push_selector_term!(terms, :var, selector.var) + selector.policy isa HoldLast || push!(terms, "policy=$(_policy_display(selector.policy))") + print(io, "AllDomains(", join(terms, ", "), ")") +end + +function Base.show(io::IO, selector::HardDomains) + terms = String[] + _push_selector_term!(terms, :kind, selector.kind) + _push_selector_term!(terms, :domain, selector.domain) + _push_selector_term!(terms, :scale, selector.scale) + _push_selector_term!(terms, :process, selector.process) + print(io, "HardDomains(", join(terms, ", "), ")") +end + +abstract type RouteCardinality end + +""" + ManyToOneVector() + +Route cardinality that materializes one value per resolved producer. +""" +struct ManyToOneVector <: RouteCardinality end + +""" + ManyToOneAggregate(reducer=sum) + +Route cardinality that reduces resolved producer values to one scalar. +""" +struct ManyToOneAggregate{F} <: RouteCardinality + reducer::F +end + +ManyToOneAggregate() = ManyToOneAggregate(sum) + +""" + OneToManyBroadcast() + SpatialSample() + SpatialScatterAdd() + +Reserved route cardinalities for the MTG/spatial domain runner. +""" +struct OneToManyBroadcast <: RouteCardinality end +struct SpatialSample <: RouteCardinality end +struct SpatialScatterAdd <: RouteCardinality end + +""" + DomainRouteTarget(domain; scale=:Default, var, process=nothing) + +Target of an explicit cross-domain route. +""" +struct DomainRouteTarget + domain::Symbol + scale::Symbol + var::Symbol + process::Union{Nothing,Symbol} +end + +function Base.show(io::IO, target::DomainRouteTarget) + terms = ["domain=:$(target.domain)"] + target.scale == :Default || push!(terms, "scale=:$(target.scale)") + push!(terms, "var=:$(target.var)") + isnothing(target.process) || push!(terms, "process=:$(target.process)") + print(io, "DomainRouteTarget(", join(terms, ", "), ")") +end + +function DomainRouteTarget( + domain::Union{Symbol,AbstractString}; + scale::Union{Symbol,AbstractString}=:Default, + var, + process=nothing, +) + return DomainRouteTarget( + Symbol(domain), + Symbol(scale), + Symbol(var), + isnothing(process) ? nothing : Symbol(process), + ) +end + +""" + Route(from, to; cardinality=ManyToOneVector(), policy=nothing) + Route(; from, to, cardinality=ManyToOneVector(), policy=nothing) + +Explicit cross-domain route materialized into a target domain status before +that domain runs. +""" +struct Route{F,T,C<:RouteCardinality,P<:SchedulePolicy} + from::F + to::T + cardinality::C + policy::P +end + +function Route(from::AllDomains, to::DomainRouteTarget; cardinality::RouteCardinality=ManyToOneVector(), policy=nothing) + route_policy = isnothing(policy) ? from.policy : _as_schedule_policy(policy; context="Route policy") + isnothing(from.var) && error( + "Route source `AllDomains(...)` must declare `var=:source_variable` so the runtime knows what to materialize." + ) + return Route(from, to, cardinality, route_policy) +end + +Route(; from, to, cardinality::RouteCardinality=ManyToOneVector(), policy=nothing) = + Route(from, to; cardinality=cardinality, policy=policy) + +""" + SimulationMapping(domains...) + +Top-level composition of reusable domains. This is the incremental entry point +for multi-plant/soil/scene simulations. +""" +struct SimulationMapping + domains::Vector{Domain} + routes::Vector{Route} +end + +function SimulationMapping(domains::Domain...; routes=()) + names = [domain.name for domain in domains] + duplicates = unique(filter(name -> count(==(name), names) > 1, names)) + isempty(duplicates) || error("Duplicate domain name(s) in SimulationMapping: $(join(duplicates, ", ")).") + normalized_routes = Route[] + for route in routes + route isa Route || error("SimulationMapping routes must be `Route` objects, got `$(typeof(route))`.") + push!(normalized_routes, route) + end + return SimulationMapping(collect(domains), normalized_routes) +end + +""" + DomainRunContext + +Runtime context passed as `extra` to models run by [`SimulationMapping`](@ref). +Scene models can call [`dependency_values`](@ref) to consume resolved +`AllDomains` value dependencies, or [`dependency_targets`](@ref) to manually +run resolved `HardDomains` dependencies. +""" +struct DomainRunContext + simulation + consumer::DomainModelKey + step::Int + clock::ClockSpec + constants +end + +struct ModelTarget + simulation + key + node + step::Int + model + models + status + meteo + constants + extra +end + +struct DomainNodeValues{I,T<:AbstractVector} + ids::I + values::T +end + +DomainNodeValues(values::AbstractVector) = DomainNodeValues(nothing, values) + +""" + model_target(model, models, status, meteo=nothing, constants=nothing, extra=nothing; kwargs...) + +Build one executable model target. This is the low-level representation used by +hard-domain dependencies and can also be used by ordinary same-status hard +dependencies. +""" +function model_target( + model, + models, + status, + meteo=nothing, + constants=nothing, + extra=nothing; + simulation=nothing, + key=nothing, + node=nothing, + step::Integer=1, +) + return ModelTarget(simulation, key, node, Int(step), model, models, status, meteo, constants, extra) +end + +function dependency_targets( + models, + status, + dependency_name::Symbol; + meteo=nothing, + constants=nothing, + extra=nothing, +) + hasproperty(models, dependency_name) || error( + "No model named `$(dependency_name)` is available in `models`. ", + "Declare the hard dependency with `dep(model)` and include a model for that process in the mapping." + ) + return ModelTarget[ + model_target(getproperty(models, dependency_name), models, status, meteo, constants, extra), + ] +end + +dependency_targets(models, status, dependency_name::AbstractString; kwargs...) = + dependency_targets(models, status, Symbol(dependency_name); kwargs...) + +dependency_target(args...; kwargs...) = only(dependency_targets(args...; kwargs...)) + +struct DomainGraphState{S<:AbstractVector} + simulations::S +end + +struct DomainGraphRuntime + state::DomainGraphState + meteo + constants + effective_multirate::Bool + timeline::TimelineContext + meteo_sampler + executor +end + +""" + DomainSimulation + +Result and mutable runtime state for a [`SimulationMapping`](@ref) run. +""" +mutable struct DomainSimulation + mapping::SimulationMapping + environment::AbstractEnvironmentBackend + model_specs::Dict{DomainModelKey,ModelSpec} + model_clocks::Dict{DomainModelKey,ClockSpec} + dependency_graphs::Dict{Symbol,DependencyGraph} + dependency_bindings::Dict{Tuple{DomainModelKey,Symbol},Vector{DomainModelKey}} + dependency_variables::Dict{Tuple{DomainModelKey,Symbol},Union{Nothing,Symbol}} + dependency_policies::Dict{Tuple{DomainModelKey,Symbol},SchedulePolicy} + hard_domain_dependency_bindings::Dict{Tuple{DomainModelKey,Symbol},Vector{DomainModelKey}} + route_bindings::Vector{Vector{DomainModelKey}} + route_clocks::Vector{ClockSpec} + streams::Dict{Tuple{DomainModelKey,Symbol},Vector{Pair{Int,Any}}} + outputs::Dict{Tuple{DomainModelKey,Symbol},Vector{Any}} + domain_states::Dict{Symbol,Any} + timeline::TimelineContext +end + +outputs(simulation::DomainSimulation) = simulation.outputs + +function status(state::DomainGraphState) + statuses = Dict{Symbol,Vector{Status}}() + for graph_simulation in state.simulations + for (scale, statuses_at_scale) in status(graph_simulation) + append!(get!(statuses, scale, Status[]), statuses_at_scale) + end + end + return statuses +end + +function _global_scale_statuses(simulation::DomainSimulation, scale::Symbol) + statuses = Status[] + for state in values(simulation.domain_states) + if state isa DomainGraphState + graph_statuses = status(state) + haskey(graph_statuses, scale) || continue + append!(statuses, graph_statuses[scale]) + elseif state isa ModelMapping{SingleScale} && scale == :Default + push!(statuses, status(state)) + end + end + return statuses +end + +function _domain_declares_scale(domain::Domain, scale::Symbol) + mapping = _domain_mapping(domain) + mapping isa ModelMapping{MultiScale} || return false + return any(entry -> first(entry) == scale, pairs(mapping)) +end + +function _simulation_declares_graph_scale(simulation::DomainSimulation, scale::Symbol) + return any(domain -> _domain_declares_scale(domain, scale), simulation.mapping.domains) +end + +function status(simulation::DomainSimulation, domain_name::Symbol) + state = get(simulation.domain_states, domain_name, nothing) + if isnothing(state) + statuses = _global_scale_statuses(simulation, domain_name) + isempty(statuses) && _simulation_declares_graph_scale(simulation, domain_name) && return statuses + isempty(statuses) && error( + "No domain named `$(domain_name)` and no global scale `$(domain_name)` exists in this DomainSimulation." + ) + return statuses + end + state isa ModelMapping{SingleScale} || error( + "Domain `$(domain_name)` is MTG-backed. Use `status(simulation, domain, scale)` to inspect statuses at one scale." + ) + return status(state) +end + +status(simulation::DomainSimulation, domain_name::AbstractString) = status(simulation, Symbol(domain_name)) + +function status(simulation::DomainSimulation, domain_name::Symbol, scale::Symbol) + state = get(simulation.domain_states, domain_name, nothing) + isnothing(state) && error("Domain `$(domain_name)` has no runtime state.") + state isa DomainGraphState || error( + "Domain `$(domain_name)` is single-status. Use `status(simulation, domain)` without a scale." + ) + graph_statuses = status(state) + haskey(graph_statuses, scale) && return graph_statuses[scale] + domain = _domain_for_name(simulation.mapping, domain_name) + _domain_declares_scale(domain, scale) && return Status[] + error("Domain `$(domain_name)` has no runtime statuses and no declared mapping at scale `$(scale)`.") +end + +status(simulation::DomainSimulation, domain_name::AbstractString, scale::Union{Symbol,AbstractString}) = + status(simulation, Symbol(domain_name), Symbol(scale)) + +function _domain_entries(domain::Domain) + mapping = _domain_mapping(domain) + return collect(pairs(mapping)) +end + +function _validate_initial_domain_mapping(domain::Domain) + mapping = _domain_mapping(domain) + mapping isa ModelMapping{SingleScale} || error( + "Domain `$(domain.name)` uses a multiscale ModelMapping. ", + "The initial SimulationMapping runner only supports single-status domains; ", + "the MTG/domain runner will handle nested multiscale plant domains." + ) + return nothing +end + +_is_single_status_domain(domain::Domain) = _domain_mapping(domain) isa ModelMapping{SingleScale} +_is_graph_domain(domain::Domain) = _domain_mapping(domain) isa ModelMapping{MultiScale} + +function _validate_staged_domain_mapping(domain::Domain) + mapping = _domain_mapping(domain) + mapping isa ModelMapping || error("Domain `$(domain.name)` does not wrap a valid ModelMapping.") + if mapping isa ModelMapping{SingleScale} && !isnothing(domain.selector) + error( + "Domain `$(domain.name)` has a selector but uses a single-status ModelMapping. ", + "Selectors are only supported for MTG-backed, scale-keyed domain mappings in the MTG-domain runner." + ) + end + return nothing +end + +function _collect_matching_nodes(root, predicate) + matches = Any[] + MultiScaleTreeGraph.traverse!(root) do node + predicate(node) && push!(matches, node) + end + return matches +end + +function _is_ancestor_node(ancestor, node) + current = parent(node) + while !isnothing(current) + current === ancestor && return true + current = parent(current) + end + return false +end + +function _validate_domain_graph_roots!(domain::Domain, roots) + for i in eachindex(roots), j in (i + 1):lastindex(roots) + left = roots[i] + right = roots[j] + if _is_ancestor_node(left, right) || _is_ancestor_node(right, left) + error( + "Selector for MTG-backed domain `$(domain.name)` matched overlapping MTG roots ", + "`$(node_id(left))` and `$(node_id(right))`. ", + "Select non-overlapping domain roots, for example plant roots rather than both plants and organs." + ) + end + end + return roots +end + +function _domain_graph_roots(object, domain::Domain) + selector = domain.selector + isnothing(selector) && return Any[object] + selector isa MultiScaleTreeGraph.Node && return Any[selector] + if selector isa Symbol + matches = _collect_matching_nodes(object, node -> symbol(node) == selector) + elseif selector isa Function + matches = _collect_matching_nodes(object, selector) + else + error( + "Unsupported selector for MTG-backed domain `$(domain.name)`: `$(typeof(selector))`. ", + "Use `selector=nothing`, a `MultiScaleTreeGraph.Node`, a scale `Symbol`, or a predicate function." + ) + end + isempty(matches) && error( + "Selector for MTG-backed domain `$(domain.name)` matched $(length(matches)) nodes. ", + "Use a selector that identifies at least one domain root for this MTG-domain runner." + ) + return _validate_domain_graph_roots!(domain, matches) +end + +function _domain_model_specs(domain::Domain) + specs = Dict{DomainModelKey,ModelSpec}() + for (scale, declarations) in _domain_entries(domain) + for (process, spec) in pairs(parse_model_specs(declarations)) + specs[DomainModelKey(domain.name, scale, process)] = spec + end + end + return specs +end + +function _domain_model_clocks(specs::Dict{DomainModelKey,ModelSpec}, timeline::TimelineContext) + clocks = Dict{DomainModelKey,ClockSpec}() + for (key, spec) in specs + clocks[key] = _model_clock(spec, model_(spec), timeline) + end + return clocks +end + +function _domain_dependency_graphs(mapping::SimulationMapping) + graphs = Dict{Symbol,DependencyGraph}() + for domain in mapping.domains + graph = dep(_domain_mapping(domain)) + if !isempty(graph.not_found) + error("Domain `$(domain.name)` has unresolved dependencies: $(graph.not_found).") + end + graphs[domain.name] = graph + end + return graphs +end + +function _domain_for_name(mapping::SimulationMapping, name::Symbol) + for domain in mapping.domains + domain.name == name && return domain + end + error("Unknown domain `$(name)`.") +end + +function _matches(selector::AllDomains, domain::Domain, key::DomainModelKey, spec::ModelSpec; check_var=true) + isnothing(selector.kind) || selector.kind == domain.kind || return false + isnothing(selector.domain) || selector.domain == domain.name || return false + isnothing(selector.scale) || selector.scale == key.scale || return false + isnothing(selector.process) || selector.process == key.process || return false + !check_var || isnothing(selector.var) || selector.var in keys(outputs_(spec)) || return false + return true +end + +function _matches(selector::HardDomains, domain::Domain, key::DomainModelKey, spec::ModelSpec; check_var=false) + isnothing(selector.kind) || selector.kind == domain.kind || return false + isnothing(selector.domain) || selector.domain == domain.name || return false + isnothing(selector.scale) || selector.scale == key.scale || return false + isnothing(selector.process) || selector.process == key.process || return false + return true +end + +function _format_symbol_keys(keys_iter) + syms = sort!(collect(Symbol.(keys_iter)); by=string) + isempty(syms) && return "none" + return join((":" * string(sym) for sym in syms), ", ") +end + +function _domain_candidate_rows( + mapping::SimulationMapping, + specs::Dict{DomainModelKey,ModelSpec}; + selector=nothing, + check_var=true, + max_rows=12, +) + rows = String[] + keys_by_domain = _keys_by_domain(specs) + for domain in mapping.domains + producer_keys = sort!( + copy(get(keys_by_domain, domain.name, DomainModelKey[])); + by=key -> (string(key.scale), string(key.process)), + ) + for producer_key in producer_keys + producer_spec = specs[producer_key] + if selector isa Union{AllDomains,HardDomains} + _matches(selector, domain, producer_key, producer_spec; check_var=check_var) || continue + end + push!( + rows, + "$(producer_key) outputs=($(_format_symbol_keys(keys(outputs_(producer_spec)))))", + ) + end + end + + length(rows) <= max_rows && return join(rows, "\n ") + shown = rows[1:max_rows] + push!(shown, "... and $(length(rows) - max_rows) more") + return join(shown, "\n ") +end + +function _selector_match_error( + mapping::SimulationMapping, + specs::Dict{DomainModelKey,ModelSpec}, + selector::Union{AllDomains,HardDomains}; + context::String, +) + candidates = _domain_candidate_rows(mapping, specs) + message = string( + context, + " did not match any model for selector `", + selector, + "`. Suggested fixes: check `kind`, `domain`, `scale`, and `process`; ", + "if `var` is set, use one of the producer outputs listed below.\n", + "Available producers:\n ", + isempty(candidates) ? "none" : candidates, + ) + if selector isa AllDomains && !isnothing(selector.var) + near_matches = _domain_candidate_rows(mapping, specs; selector=selector, check_var=false) + if !isempty(near_matches) + message = string( + message, + "\nModels matching all selector fields except `var=:", + selector.var, + "`:\n ", + near_matches, + ) + end + end + return message +end + +function _resolve_domain_dependencies(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) + bindings = Dict{Tuple{DomainModelKey,Symbol},Vector{DomainModelKey}}() + policies = Dict{Tuple{DomainModelKey,Symbol},SchedulePolicy}() + variables = Dict{Tuple{DomainModelKey,Symbol},Union{Nothing,Symbol}}() + keys_by_domain = _keys_by_domain(specs) + + for (consumer_key, spec) in specs + model_deps = dep(model_(spec)) + for (dep_name, selector) in pairs(model_deps) + selector isa AllDomains || continue + resolved = DomainModelKey[] + for domain in mapping.domains + for producer_key in get(keys_by_domain, domain.name, DomainModelKey[]) + producer_key == consumer_key && continue + producer_spec = specs[producer_key] + _matches(selector, domain, producer_key, producer_spec) && push!(resolved, producer_key) + end + end + isempty(resolved) && error( + _selector_match_error( + mapping, + specs, + selector; + context="Domain dependency `$(dep_name)` for consumer `$(consumer_key)`", + ) + ) + binding_key = (consumer_key, dep_name) + bindings[binding_key] = resolved + policies[binding_key] = selector.policy + variables[binding_key] = selector.var + end + end + + return bindings, policies, variables +end + +function _resolve_hard_domain_dependencies(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) + bindings = Dict{Tuple{DomainModelKey,Symbol},Vector{DomainModelKey}}() + keys_by_domain = _keys_by_domain(specs) + + for (consumer_key, spec) in specs + model_deps = dep(model_(spec)) + for (dep_name, selector) in pairs(model_deps) + selector isa HardDomains || continue + resolved = DomainModelKey[] + for domain in mapping.domains + for producer_key in get(keys_by_domain, domain.name, DomainModelKey[]) + producer_key == consumer_key && continue + producer_spec = specs[producer_key] + _matches(selector, domain, producer_key, producer_spec) && push!(resolved, producer_key) + end + end + isempty(resolved) && error( + _selector_match_error( + mapping, + specs, + selector; + context="Hard domain dependency `$(dep_name)` for consumer `$(consumer_key)`", + ) + ) + bindings[(consumer_key, dep_name)] = resolved + end + end + + return bindings +end + +function _keys_by_domain(specs::Dict{DomainModelKey,ModelSpec}) + keys_by_domain = Dict{Symbol,Vector{DomainModelKey}}() + for key in keys(specs) + push!(get!(keys_by_domain, key.domain, DomainModelKey[]), key) + end + return keys_by_domain +end + +function _resolve_selector_matches( + mapping::SimulationMapping, + specs::Dict{DomainModelKey,ModelSpec}, + selector::Union{AllDomains,HardDomains}; + context::String, +) + resolved = DomainModelKey[] + keys_by_domain = _keys_by_domain(specs) + for domain in mapping.domains + for producer_key in get(keys_by_domain, domain.name, DomainModelKey[]) + producer_spec = specs[producer_key] + _matches(selector, domain, producer_key, producer_spec) && push!(resolved, producer_key) + end + end + isempty(resolved) && error( + _selector_match_error(mapping, specs, selector; context=context) + ) + return resolved +end + +function _resolve_route_bindings(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) + bindings = Vector{DomainModelKey}[] + for (i, route) in enumerate(mapping.routes) + push!( + bindings, + _resolve_selector_matches( + mapping, + specs, + route.from; + context="Route $(i) from `$(route.from)`", + ), + ) + end + return bindings +end + +function _route_target_consumer_key(route::Route, specs::Dict{DomainModelKey,ModelSpec}) + target = route.to + if !isnothing(target.process) + key = DomainModelKey(target.domain, target.scale, target.process) + haskey(specs, key) || error( + "Route target process `$(key)` does not exist." + ) + target.var in keys(inputs_(specs[key])) || error( + "Route target process `$(key)` does not consume variable `$(target.var)`. ", + "Use a process that declares `$(target.var)` in `inputs_`, or omit `process=...` ", + "if the route should only materialize a domain status variable." + ) + return key + end + + consumers = DomainModelKey[] + for (key, spec) in specs + key.domain == target.domain || continue + key.scale == target.scale || continue + target.var in keys(inputs_(spec)) || continue + push!(consumers, key) + end + + isempty(consumers) && return nothing + length(consumers) == 1 || error( + "Route target `$(target.domain)/$(target.scale)/$(target.var)` is consumed by several models: ", + join(consumers, ", "), + ". Specify `process=...` in `DomainRouteTarget` so the route clock is unambiguous." + ) + return only(consumers) +end + +function _validate_route_targets(mapping::SimulationMapping, routes::Vector{Route}, specs::Dict{DomainModelKey,ModelSpec}; staged_graph_domains=false) + for (i, route) in enumerate(routes) + target = route.to + domain = _domain_for_name(mapping, target.domain) + target_mapping = _domain_mapping(domain) + if target_mapping isa ModelMapping{MultiScale} + staged_graph_domains || error( + "Route $(i) targets MTG-backed domain `$(target.domain)`, but this runner only supports single-status domains." + ) + route.cardinality isa OneToManyBroadcast || error( + "Route $(i) targets MTG-backed domain `$(target.domain)`. ", + "The MTG-domain runner only supports `OneToManyBroadcast()` routes into graph domains." + ) + target.scale == :Default && error( + "Route $(i) targets MTG-backed domain `$(target.domain)` but uses `scale=:Default`. ", + "Specify the target graph scale, for example `scale=:Leaf`." + ) + isnothing(_route_target_consumer_key(route, specs)) && error( + "Route $(i) targets graph variable `$(target.var)` in `$(target.domain)/$(target.scale)`, ", + "but no target process consumes that variable. Specify `process=...` or add the variable to one model's `inputs_`." + ) + continue + end + target.scale == :Default || error( + "Route $(i) target `$(target.domain)/$(target.scale)/$(target.var)` is not supported by the single-status domain runner. ", + "Use `scale=:Default` until MTG-backed domains are enabled." + ) + st = status(target_mapping) + target.var in propertynames(st) || error( + "Route $(i) target status `$(target.domain)/$(target.scale)` does not contain variable `$(target.var)`. ", + "Initialize it in the target domain status so the route can materialize its value." + ) + _route_target_consumer_key(route, specs) + end + return nothing +end + +function _validate_graph_route_order( + mapping::SimulationMapping, + routes::Vector{Route}, + route_bindings::Vector{Vector{DomainModelKey}}, +) + ordered_domains = _domain_run_order(mapping) + domain_positions = Dict(domain.name => i for (i, domain) in enumerate(ordered_domains)) + for (i, route) in enumerate(routes) + target_domain = _domain_for_name(mapping, route.to.domain) + _is_graph_domain(target_domain) || continue + target_position = domain_positions[target_domain.name] + for producer in route_bindings[i] + producer_position = domain_positions[producer.domain] + producer_position < target_position || error( + "Route $(i) targets MTG-backed domain `$(target_domain.name)` from producer `$(producer)`, ", + "but graph-target routes require source domains to run earlier in the same timestep. ", + "Move source domain `$(producer.domain)` before target domain `$(target_domain.name)` in the ", + "`SimulationMapping`, or route the value through a later single-status scene domain instead." + ) + end + end + return nothing +end + +function _route_clocks(routes::Vector{Route}, specs::Dict{DomainModelKey,ModelSpec}, model_clocks) + clocks = ClockSpec[] + for route in routes + consumer_key = _route_target_consumer_key(route, specs) + if isnothing(consumer_key) + push!(clocks, ClockSpec(1.0, 0.0)) + else + push!(clocks, model_clocks[consumer_key]) + end + end + return clocks +end + +function _build_domain_simulation(mapping::SimulationMapping, meteo; staged_graph_domains=false) + environment = environment_backend(meteo) + _validate_meteo_duration(environment) + timeline = _timeline_context(environment) + foreach(staged_graph_domains ? _validate_staged_domain_mapping : _validate_initial_domain_mapping, mapping.domains) + specs = Dict{DomainModelKey,ModelSpec}() + for domain in mapping.domains + merge!(specs, _domain_model_specs(domain)) + end + + model_clocks = _domain_model_clocks(specs, timeline) + graphs = _domain_dependency_graphs(mapping) + bindings, policies, variables = _resolve_domain_dependencies(mapping, specs) + hard_domain_bindings = _resolve_hard_domain_dependencies(mapping, specs) + route_bindings = _resolve_route_bindings(mapping, specs) + _validate_route_targets(mapping, mapping.routes, specs; staged_graph_domains=staged_graph_domains) + staged_graph_domains && _validate_graph_route_order(mapping, mapping.routes, route_bindings) + route_clocks = _route_clocks(mapping.routes, specs, model_clocks) + validate_meteo_inputs(_domain_model_specs_by_scale(specs), environment) + return DomainSimulation( + mapping, + environment, + specs, + model_clocks, + graphs, + bindings, + variables, + policies, + hard_domain_bindings, + route_bindings, + route_clocks, + Dict{Tuple{DomainModelKey,Symbol},Vector{Pair{Int,Any}}}(), + Dict{Tuple{DomainModelKey,Symbol},Vector{Any}}(), + Dict{Symbol,Any}(domain.name => _domain_mapping(domain) for domain in mapping.domains if _domain_mapping(domain) isa ModelMapping{SingleScale}), + timeline, + ) +end + +function _domain_model_specs_by_scale(specs::Dict{DomainModelKey,ModelSpec}) + by_scale = Dict{Symbol,Dict{Symbol,ModelSpec}}() + for (key, spec) in specs + scale_key = Symbol(string(key.domain), "/", string(key.scale)) + scale_specs = get!(by_scale, scale_key, Dict{Symbol,ModelSpec}()) + scale_specs[key.process] = spec + end + return by_scale +end + +function _domain_run_order(mapping::SimulationMapping) + # Scene-like aggregators should observe plant/soil/environment updates from + # the current base step. Keep this simple and explicit for the first runner. + indexed_domains = collect(enumerate(mapping.domains)) + return last.(sort(indexed_domains; by=item -> (last(item).kind == :scene ? 1 : 0, first(item)))) +end + +function _publish_domain_model_outputs!( + simulation::DomainSimulation, + domain::Domain, + node::AbstractDependencyNode, + status, + step::Int +) + key = DomainModelKey(domain.name, node.scale, node.process) + haskey(simulation.model_specs, key) || return nothing + spec = simulation.model_specs[key] + for out_var in keys(outputs_(spec)) + stream_key = (key, out_var) + value = status[out_var] + push!(get!(simulation.streams, stream_key, Pair{Int,Any}[]), step => value) + push!(get!(simulation.outputs, stream_key, Any[]), value) + end + return nothing +end + +function _publish_domain_hard_dependency_outputs!( + simulation::DomainSimulation, + domain::Domain, + node::HardDependencyNode, + status, + step::Int +) + _publish_domain_model_outputs!(simulation, domain, node, status, step) + for child in node.children + _publish_domain_hard_dependency_outputs!(simulation, domain, child, status, step) + end + return nothing +end + +function _publish_graph_domain_step_outputs!( + simulation::DomainSimulation, + domain::Domain, + graph_state::DomainGraphState, + step::Int; + effective_multirate::Bool=false, + phase::Symbol=:normal, +) + graph_statuses = status(graph_state) + for (key, spec) in simulation.model_specs + key.domain == domain.name || continue + _should_publish_domain_key(simulation, domain, key; phase=phase) || continue + if effective_multirate + clock = simulation.model_clocks[key] + _should_run_at_time(clock, float(step)) || continue + end + haskey(graph_statuses, key.scale) || continue + for out_var in keys(outputs_(spec)) + stream_key = (key, out_var) + ids = Int[] + values = Any[] + for st in graph_statuses[key.scale] + out_var in propertynames(st) || continue + push!(ids, node_id(st.node)) + push!(values, st[out_var]) + end + push!(get!(simulation.streams, stream_key, Pair{Int,Any}[]), step => DomainNodeValues(ids, values)) + push!(get!(simulation.outputs, stream_key, Any[]), values) + end + end + return nothing +end + +function _domain_environment_entities(simulation::DomainSimulation, domain::Domain) + state = get(simulation.domain_states, domain.name, nothing) + entities = NamedTuple[] + if state isa DomainGraphState + for (scale, statuses_at_scale) in status(state) + push!(entities, ( + domain=domain.name, + kind=domain.kind, + scale=scale, + statuses=statuses_at_scale, + state=state, + )) + end + elseif state isa ModelMapping{SingleScale} + push!(entities, ( + domain=domain.name, + kind=domain.kind, + scale=:Default, + statuses=Status[status(state)], + state=state, + )) + end + return entities +end + +function _update_domain_environment_index!(simulation::DomainSimulation, domain::Domain) + return update_index!(simulation.environment, _domain_environment_entities(simulation, domain)) +end + +function _resolve_stream_values(simulation::DomainSimulation, producer::DomainModelKey, var::Symbol, steps) + stream = get(simulation.streams, (producer, var), Pair{Int,Any}[]) + vals = Any[] + for (step, value) in stream + step in steps && push!(vals, value) + end + return vals +end + +function _domain_node_values(values::Vector{Any}) + return DomainNodeValues[value for value in values if value isa DomainNodeValues] +end + +function _domain_node_values_have_ids(values::Vector{DomainNodeValues}) + return all(value -> !isnothing(value.ids) && length(value.ids) == length(value.values), values) +end + +function _combine_domain_node_values_by_position(values::Vector{DomainNodeValues}, reducer) + lengths = unique(length(value.values) for value in values) + length(lengths) == 1 || error( + "Cannot aggregate graph-domain values with changing vector lengths because node ids are unavailable. ", + "Publish graph-domain outputs through PlantSimEngine's graph-domain runtime so values can be aligned by node id." + ) + n = only(lengths) + return DomainNodeValues(Any[reducer(Any[value.values[i] for value in values]) for i in 1:n]) +end + +function _combine_domain_node_values_by_id(values::Vector{DomainNodeValues}, reducer) + grouped = Dict{Int,Vector{Any}}() + order = Int[] + for node_values in values + for (id, value) in zip(node_values.ids, node_values.values) + bucket = get!(grouped, id) do + push!(order, id) + Any[] + end + push!(bucket, value) + end + end + return DomainNodeValues(order, Any[reducer(grouped[id]) for id in order]) +end + +function _combine_domain_node_values(values::Vector{Any}, reducer) + node_values = _domain_node_values(values) + _domain_node_values_have_ids(node_values) && return _combine_domain_node_values_by_id(node_values, reducer) + return _combine_domain_node_values_by_position(node_values, reducer) +end + +function _apply_dependency_policy(values::Vector{Any}, policy::SchedulePolicy) + isempty(values) && return nothing + if policy isa HoldLast || policy isa Interpolate + return last(values) + elseif policy isa Integrate + if any(value -> value isa DomainNodeValues, values) + return _combine_domain_node_values(values, sum) + end + return sum(values) + elseif policy isa Aggregate + if any(value -> value isa DomainNodeValues, values) + return _combine_domain_node_values(values, vals -> sum(vals) / length(vals)) + end + return sum(values) / length(values) + end + return last(values) +end + +_public_dependency_value(value) = value isa DomainNodeValues ? value.values : value + +function _push_public_dependency_value!(dest::Vector{Any}, value, flatten::Bool) + isnothing(value) && return dest + if flatten && value isa DomainNodeValues + append!(dest, value.values) + else + push!(dest, _public_dependency_value(value)) + end + return dest +end + +function _window_steps(step::Int, clock::ClockSpec) + start = _window_start_for_clock(clock, float(step)) + return ceil(Int, start):step +end + +""" + dependency_values(ctx, dependency_name, variable) + dependency_values(ctx, dependency_name) + +Return one value per resolved producer for a scene-level `AllDomains` +dependency, applying the selector's temporal policy over the consumer window. +When `AllDomains(...; var=:x)` declares a variable, the two-argument form uses +that variable. +""" +function dependency_values(ctx::DomainRunContext, dependency_name::Symbol, variable=nothing; flatten=false) + sim = ctx.simulation + binding_key = (ctx.consumer, dependency_name) + producers = get(sim.dependency_bindings, binding_key, nothing) + isnothing(producers) && error( + "No resolved dependency named `$(dependency_name)` for `$(ctx.consumer)`. ", + "Declare it with `dep(model) = (; $(dependency_name)=AllDomains(...))`." + ) + declared_var = sim.dependency_variables[binding_key] + resolved_var = isnothing(variable) ? declared_var : Symbol(variable) + isnothing(resolved_var) && error( + "No variable was provided for domain dependency `$(dependency_name)` in `$(ctx.consumer)`. ", + "Call `dependency_values(extra, :$(dependency_name), :variable)` or declare ", + "`AllDomains(...; var=:variable)`." + ) + if !isnothing(declared_var) && !isnothing(variable) && Symbol(variable) != declared_var + error( + "Domain dependency `$(dependency_name)` in `$(ctx.consumer)` was declared with `var=:$(declared_var)`, ", + "but `dependency_values` was called for variable `$(Symbol(variable))`." + ) + end + for producer in producers + producer_spec = sim.model_specs[producer] + resolved_var in keys(outputs_(producer_spec)) || error( + "Domain dependency `$(dependency_name)` resolved producer `$(producer)`, ", + "but that producer does not output variable `$(resolved_var)`." + ) + end + policy = sim.dependency_policies[binding_key] + steps = _window_steps(ctx.step, ctx.clock) + values = Any[] + for producer in producers + value = _apply_dependency_policy(_resolve_stream_values(sim, producer, resolved_var, steps), policy) + _push_public_dependency_value!(values, value, flatten) + end + return values +end + +dependency_values(ctx::DomainRunContext, dependency_name::AbstractString, variable=nothing; flatten=false) = + dependency_values(ctx, Symbol(dependency_name), variable; flatten=flatten) + +function _find_dependency_node(node::SoftDependencyNode, key::DomainModelKey) + node.scale == key.scale && node.process == key.process && return node + for child in node.children + found = _find_dependency_node(child, key) + isnothing(found) || return found + end + return nothing +end + +function _find_dependency_node(graph::DependencyGraph, key::DomainModelKey) + for (_, root) in graph.roots + found = _find_dependency_node(root, key) + isnothing(found) || return found + end + error("No dependency node found for domain model `$(key)`.") +end + +function _dependency_target_meteo(simulation::DomainSimulation, key::DomainModelKey, st, step::Int) + spec = simulation.model_specs[key] + support = EnvironmentSupport(key.domain, key.scale, key.process, st) + t = _time_from_step(step, simulation.timeline) + return sample_environment(simulation.environment, support, t, spec) +end + +function _single_status_dependency_targets(ctx::DomainRunContext, producer::DomainModelKey) + sim = ctx.simulation + domain = _domain_for_name(sim.mapping, producer.domain) + model_list = _modellist_from_model_mapping(_domain_mapping(domain)) + node = _find_dependency_node(sim.dependency_graphs[producer.domain], producer) + st = status(model_list) + producer_context = DomainRunContext(sim, producer, ctx.step, sim.model_clocks[producer], ctx.constants) + return ModelTarget[ + ModelTarget( + sim, + producer, + node, + ctx.step, + node.value, + model_list.models, + st, + _dependency_target_meteo(sim, producer, st, ctx.step), + ctx.constants, + producer_context, + ), + ] +end + +function _graph_dependency_targets(ctx::DomainRunContext, producer::DomainModelKey) + sim = ctx.simulation + graph_state = sim.domain_states[producer.domain] + targets = ModelTarget[] + for graph_simulation in graph_state.simulations + graph_statuses = status(graph_simulation) + haskey(graph_statuses, producer.scale) || continue + node = _find_dependency_node(dep(graph_simulation), producer) + models_at_scale = get_models(graph_simulation)[producer.scale] + for st in graph_statuses[producer.scale] + push!( + targets, + ModelTarget( + sim, + producer, + node, + ctx.step, + node.value, + models_at_scale, + st, + _dependency_target_meteo(sim, producer, st, ctx.step), + ctx.constants, + graph_simulation, + ), + ) + end + end + return targets +end + +function _dependency_targets_for_producer(ctx::DomainRunContext, producer::DomainModelKey) + state = ctx.simulation.domain_states[producer.domain] + state isa ModelMapping{SingleScale} && return _single_status_dependency_targets(ctx, producer) + state isa DomainGraphState && return _graph_dependency_targets(ctx, producer) + error("Unsupported runtime state for domain `$(producer.domain)`: `$(typeof(state))`.") +end + +""" + dependency_targets(ctx, dependency_name) + +Return executable targets for a resolved `HardDomains` dependency. The parent +model controls when, how often, and in which order targets are executed by +calling [`run_target!`](@ref). +""" +function dependency_targets(ctx::DomainRunContext, dependency_name::Symbol) + sim = ctx.simulation + binding_key = (ctx.consumer, dependency_name) + producers = get(sim.hard_domain_dependency_bindings, binding_key, nothing) + isnothing(producers) && error( + "No hard-domain dependency named `$(dependency_name)` for `$(ctx.consumer)`. ", + "Declare it with `dep(model) = (; $(dependency_name)=HardDomains(...))`." + ) + targets = ModelTarget[] + for producer in producers + append!(targets, _dependency_targets_for_producer(ctx, producer)) + end + return targets +end + +dependency_targets(ctx::DomainRunContext, dependency_name::AbstractString) = + dependency_targets(ctx, Symbol(dependency_name)) + +function _publish_graph_target!(target::ModelTarget) + spec = target.simulation.model_specs[target.key] + domain = _domain_for_name(target.simulation.mapping, target.key.domain) + t = _time_from_step(target.step, target.simulation.timeline) + _scatter_graph_domain_environment_outputs!(target.simulation, domain, target.node, spec, target.status, t) + for hard_child in target.node.hard_dependency + _scatter_graph_domain_hard_dependency_environment_outputs!(target.simulation, domain, hard_child, target.status, t) + end + for out_var in keys(outputs_(spec)) + out_var in propertynames(target.status) || continue + stream_key = (target.key, out_var) + value = target.status[out_var] + push!(get!(target.simulation.streams, stream_key, Pair{Int,Any}[]), target.step => value) + push!(get!(target.simulation.outputs, stream_key, Any[]), value) + end + for hard_child in target.node.hard_dependency + _publish_domain_hard_dependency_outputs!(target.simulation, domain, hard_child, target.status, target.step) + end + return nothing +end + +function _publish_target!(target::ModelTarget) + isnothing(target.simulation) && error( + "`publish=true` requires a target created by `dependency_targets(extra, name)` in a domain simulation." + ) + target.extra isa GraphSimulation && return _publish_graph_target!(target) + domain = _domain_for_name(target.simulation.mapping, target.key.domain) + spec = target.simulation.model_specs[target.key] + _scatter_domain_environment_outputs!(target.simulation, domain, target.node, spec, target.status, target.step) + for hard_child in target.node.hard_dependency + _scatter_domain_hard_dependency_environment_outputs!(target.simulation, domain, hard_child, target.status, target.step) + end + _publish_domain_model_outputs!(target.simulation, domain, target.node, target.status, target.step) + for hard_child in target.node.hard_dependency + _publish_domain_hard_dependency_outputs!(target.simulation, domain, hard_child, target.status, target.step) + end + return nothing +end + +""" + run_target!(target; meteo=target.meteo, constants=target.constants, extra=target.extra, publish=false) + +Run one executable model target. The call mutates the target's +status, just like a normal hard dependency call. It does not append to domain +streams or outputs unless `publish=true`. +""" +function run_target!( + target::ModelTarget; + meteo=target.meteo, + constants=target.constants, + extra=target.extra, + publish::Bool=false, +) + run!(target.model, target.models, target.status, meteo, constants, extra) + publish && _publish_target!(target) + return target.status +end + +function run_target!( + models, + status, + dependency_name::Symbol; + meteo=nothing, + constants=nothing, + extra=nothing, + publish::Bool=false, +) + target = dependency_target(models, status, dependency_name; meteo=meteo, constants=constants, extra=extra) + return run_target!(target; publish=publish) +end + +run_target!(models, status, dependency_name::AbstractString; kwargs...) = + run_target!(models, status, Symbol(dependency_name); kwargs...) + +function _route_due(simulation::DomainSimulation, route_index::Int, step::Int) + clock = simulation.route_clocks[route_index] + return _should_run_at_time(clock, float(step)) +end + +function _route_producer_values(simulation::DomainSimulation, route_index::Int, step::Int) + route = simulation.mapping.routes[route_index] + source_var = route.from.var + steps = _window_steps(step, simulation.route_clocks[route_index]) + return Any[ + _apply_dependency_policy(_resolve_stream_values(simulation, producer, source_var, steps), route.policy) + for producer in simulation.route_bindings[route_index] + ] +end + +function _route_value_items(values::Vector{Any}) + items = Any[] + for value in values + isnothing(value) && continue + if value isa DomainNodeValues + append!(items, value.values) + else + push!(items, value) + end + end + return items +end + +function _materialize_route_value(values::Vector{Any}, cardinality::ManyToOneVector) + return _route_value_items(values) +end + +function _materialize_route_value(values::Vector{Any}, cardinality::ManyToOneAggregate) + return cardinality.reducer(_route_value_items(values)) +end + +function _materialize_graph_broadcast_value(values::Vector{Any}, route_index::Int) + items = _route_value_items(values) + length(items) == 1 || error( + "Route $(route_index) uses `OneToManyBroadcast()` into an MTG-backed domain and resolved $(length(items)) values. ", + "Use a selector that resolves one source value, or aggregate upstream before broadcasting." + ) + return only(items) +end + +function _materialize_route_value(values::Vector{Any}, cardinality::RouteCardinality) + error( + "Route cardinality `$(typeof(cardinality))` is declared but not implemented in the single-status domain runner. ", + "Use `ManyToOneVector()` or `ManyToOneAggregate(...)`, or run this route in the future MTG/spatial domain runner." + ) +end + +function _set_route_target_value!(simulation::DomainSimulation, route::Route, value) + target = route.to + domain = _domain_for_name(simulation.mapping, target.domain) + target.scale == :Default || error( + "Route target `$(target.domain)/$(target.scale)/$(target.var)` is not supported by the single-status domain runner. ", + "Use `scale=:Default` until MTG-backed domains are enabled." + ) + st = status(simulation, domain.name) + target.var in propertynames(st) || error( + "Route target status `$(target.domain)/$(target.scale)` does not contain variable `$(target.var)`. ", + "Initialize it in the target domain status so the route can materialize its value." + ) + st[target.var] = value + return nothing +end + +function _materialize_routes_for_domain!(simulation::DomainSimulation, domain::Domain, step::Int) + for (i, route) in enumerate(simulation.mapping.routes) + route.to.domain == domain.name || continue + _route_due(simulation, i, step) || continue + values = _route_producer_values(simulation, i, step) + value = _materialize_route_value(values, route.cardinality) + _set_route_target_value!(simulation, route, value) + end + return nothing +end + +function _materialize_graph_routes_for_domain!( + simulation::DomainSimulation, + domain::Domain, + graph_simulation::GraphSimulation, + step::Int, +) + for (i, route) in enumerate(simulation.mapping.routes) + route.to.domain == domain.name || continue + route.cardinality isa OneToManyBroadcast || continue + _route_due(simulation, i, step) || continue + target = route.to + graph_statuses = status(graph_simulation) + haskey(graph_statuses, target.scale) || error( + "Route $(i) targets `$(target.domain)/$(target.scale)/$(target.var)`, ", + "but the selected graph domain has no statuses at scale `$(target.scale)`." + ) + values = _route_producer_values(simulation, i, step) + value = _materialize_graph_broadcast_value(values, i) + for st in graph_statuses[target.scale] + target.var in propertynames(st) || error( + "Route $(i) targets `$(target.domain)/$(target.scale)/$(target.var)`, ", + "but one target status does not contain variable `$(target.var)`." + ) + st[target.var] = value + end + end + return nothing +end + +function _materialize_graph_route_attributes_for_domain!( + simulation::DomainSimulation, + domain::Domain, + root, + step::Int, +) + for (i, route) in enumerate(simulation.mapping.routes) + route.to.domain == domain.name || continue + route.cardinality isa OneToManyBroadcast || continue + target = route.to + values = _route_producer_values(simulation, i, step) + value = _materialize_graph_broadcast_value(values, i) + matched = 0 + MultiScaleTreeGraph.traverse!(root) do node + symbol(node) == target.scale || return + node[target.var] = value + matched += 1 + end + matched > 0 || error( + "Route $(i) targets `$(target.domain)/$(target.scale)/$(target.var)`, ", + "but the selected graph domain has no nodes at scale `$(target.scale)`." + ) + end + return nothing +end + +function _domain_context_for(simulation::DomainSimulation, domain::Domain, node::SoftDependencyNode, step::Int, constants=nothing) + key = DomainModelKey(domain.name, node.scale, node.process) + return DomainRunContext(simulation, key, step, simulation.model_clocks[key], constants) +end + +function _domain_environment_for_model( + simulation::DomainSimulation, + domain::Domain, + node::SoftDependencyNode, + model_spec::ModelSpec, + status, + step::Int +) + support = EnvironmentSupport(domain.name, node.scale, node.process, status) + t = _time_from_step(step, simulation.timeline) + return sample_environment(simulation.environment, support, t, model_spec) +end + +function _scatter_domain_environment_outputs!( + simulation::DomainSimulation, + domain::Domain, + node::AbstractDependencyNode, + model_spec::ModelSpec, + status, + step::Int +) + isempty(keys(meteo_outputs_(model_spec))) && return nothing + support = EnvironmentSupport(domain.name, node.scale, node.process, status) + t = _time_from_step(step, simulation.timeline) + return scatter_environment_outputs!(simulation.environment, support, t, model_spec, status) +end + +function _scatter_domain_hard_dependency_environment_outputs!( + simulation::DomainSimulation, + domain::Domain, + node::HardDependencyNode, + status, + step::Int +) + key = DomainModelKey(domain.name, node.scale, node.process) + if haskey(simulation.model_specs, key) + spec = simulation.model_specs[key] + _scatter_domain_environment_outputs!(simulation, domain, node, spec, status, step) + end + for child in node.children + _scatter_domain_hard_dependency_environment_outputs!(simulation, domain, child, status, step) + end + return nothing +end + +function _graph_domain_environment_for_model( + simulation::DomainSimulation, + domain::Domain, + node::SoftDependencyNode, + status, + t, + model_clock::ClockSpec, + model_spec::ModelSpec, + meteo, + meteo_sampler, + multirate::Bool +) + if simulation.environment isa GlobalConstant + return multirate ? _sample_meteo_for_model(meteo_sampler, meteo, round(Int, t), model_clock, model_spec) : meteo + end + support = EnvironmentSupport(domain.name, node.scale, node.process, status) + return sample_environment(simulation.environment, support, t, model_spec) +end + +function _scatter_graph_domain_environment_outputs!( + simulation::DomainSimulation, + domain::Domain, + node::AbstractDependencyNode, + model_spec::ModelSpec, + status, + t +) + isempty(keys(meteo_outputs_(model_spec))) && return nothing + support = EnvironmentSupport(domain.name, node.scale, node.process, status) + return scatter_environment_outputs!(simulation.environment, support, t, model_spec, status) +end + +function _scatter_graph_domain_hard_dependency_environment_outputs!( + simulation::DomainSimulation, + domain::Domain, + node::HardDependencyNode, + status, + t +) + key = DomainModelKey(domain.name, node.scale, node.process) + if haskey(simulation.model_specs, key) + spec = simulation.model_specs[key] + _scatter_graph_domain_environment_outputs!(simulation, domain, node, spec, status, t) + end + for child in node.children + _scatter_graph_domain_hard_dependency_environment_outputs!(simulation, domain, child, status, t) + end + return nothing +end + +function _domain_node_due(simulation::DomainSimulation, domain::Domain, node::SoftDependencyNode, step::Int) + key = DomainModelKey(domain.name, node.scale, node.process) + clock = simulation.model_clocks[key] + return _should_run_at_time(clock, float(step)) +end + +function _hard_domain_dependency_keys(simulation::DomainSimulation) + keys = Set{DomainModelKey}() + for producers in values(simulation.hard_domain_dependency_bindings) + union!(keys, producers) + end + return keys +end + +function _is_hard_domain_dependency(simulation::DomainSimulation, key::DomainModelKey) + key in _hard_domain_dependency_keys(simulation) +end + +function _has_hard_domain_parent(simulation::DomainSimulation, domain::Domain, node::SoftDependencyNode) + AbstractTrees.isroot(node) && return false + hard_keys = _hard_domain_dependency_keys(simulation) + for parent in node.parent + parent_key = DomainModelKey(domain.name, parent.scale, parent.process) + parent_key in hard_keys && return true + end + return false +end + +function _should_visit_domain_node( + simulation::DomainSimulation, + domain::Domain, + node::SoftDependencyNode; + phase::Symbol, +) + key = DomainModelKey(domain.name, node.scale, node.process) + _is_hard_domain_dependency(simulation, key) && return false + has_hard_parent = _has_hard_domain_parent(simulation, domain, node) + phase == :normal && return !has_hard_parent + phase == :post_scene && return has_hard_parent + error("Unknown domain scheduling phase `$(phase)`.") +end + +function _has_hard_domain_parent(simulation::DomainSimulation, domain::Domain, key::DomainModelKey) + node = try + _find_dependency_node(simulation.dependency_graphs[domain.name], key) + catch + return false + end + return _has_hard_domain_parent(simulation, domain, node) +end + +function _has_domain_soft_node(simulation::DomainSimulation, domain::Domain, key::DomainModelKey) + try + _find_dependency_node(simulation.dependency_graphs[domain.name], key) + return true + catch + return false + end +end + +function _should_publish_domain_key( + simulation::DomainSimulation, + domain::Domain, + key::DomainModelKey; + phase::Symbol, +) + _has_domain_soft_node(simulation, domain, key) || return false + _is_hard_domain_dependency(simulation, key) && return false + has_hard_parent = _has_hard_domain_parent(simulation, domain, key) + phase == :normal && return !has_hard_parent + phase == :post_scene && return has_hard_parent + error("Unknown domain publishing phase `$(phase)`.") +end + +function _domain_has_post_scene_work(simulation::DomainSimulation, domain::Domain) + for key in keys(simulation.model_specs) + key.domain == domain.name || continue + _is_hard_domain_dependency(simulation, key) && continue + _has_hard_domain_parent(simulation, domain, key) && return true + end + return false +end + +function _domain_parents_ready( + simulation::DomainSimulation, + domain::Domain, + node::SoftDependencyNode, + step::Int, + ran::Set{DomainModelKey} +) + AbstractTrees.isroot(node) && return true + for parent in node.parent + _domain_node_due(simulation, domain, parent, step) || continue + parent_key = DomainModelKey(domain.name, parent.scale, parent.process) + _is_hard_domain_dependency(simulation, parent_key) && continue + parent_key in ran || return false + end + return true +end + +function _run_domain_node!( + simulation::DomainSimulation, + domain::Domain, + node::SoftDependencyNode, + model_list::ModelList, + constants, + step::Int, + ran::Set{DomainModelKey}; + phase::Symbol=:normal, +) + key = DomainModelKey(domain.name, node.scale, node.process) + if _domain_node_due(simulation, domain, node, step) && + _domain_parents_ready(simulation, domain, node, step, ran) && + _should_visit_domain_node(simulation, domain, node; phase=phase) && + !(key in ran) + ctx = _domain_context_for(simulation, domain, node, step, constants) + model_spec = simulation.model_specs[key] + meteo_for_model = _domain_environment_for_model( + simulation, + domain, + node, + model_spec, + status(model_list), + step, + ) + run!(node.value, model_list.models, status(model_list), meteo_for_model, constants, ctx) + _scatter_domain_environment_outputs!(simulation, domain, node, model_spec, status(model_list), step) + push!(ran, key) + _publish_domain_model_outputs!(simulation, domain, node, status(model_list), step) + for hard_child in node.hard_dependency + _scatter_domain_hard_dependency_environment_outputs!(simulation, domain, hard_child, status(model_list), step) + _publish_domain_hard_dependency_outputs!(simulation, domain, hard_child, status(model_list), step) + end + end + + for child in node.children + _run_domain_node!(simulation, domain, child, model_list, constants, step, ran; phase=phase) + end + return nothing +end + +function _run_domain_models!( + simulation::DomainSimulation, + domain::Domain, + constants, + step::Int; + phase::Symbol=:normal, +) + mapping = _domain_mapping(domain) + model_list = _modellist_from_model_mapping(mapping) + ran = Set{DomainModelKey}() + for (_, root) in simulation.dependency_graphs[domain.name].roots + _run_domain_node!(simulation, domain, root, model_list, constants, step, ran; phase=phase) + end + return ran +end + +""" + run!(mapping::SimulationMapping, meteo=nothing, constants=PlantMeteo.Constants(); check=true) + +Run the initial domain-aware simulation path. This runner is deliberately +limited to single-status domains, but it schedules each model with its own +effective timestep. It is intended to make the multi-domain API executable +while the full MTG path is implemented. +""" +function run!( + mapping::SimulationMapping, + meteo=nothing, + constants=PlantMeteo.Constants(); + check=true +) + simulation = _build_domain_simulation(mapping, meteo) + nsteps = get_nsteps(simulation.environment) + for i in 1:nsteps + for domain in _domain_run_order(mapping) + _materialize_routes_for_domain!(simulation, domain, i) + _run_domain_models!(simulation, domain, constants, i) + _update_domain_environment_index!(simulation, domain) + end + for domain in _domain_run_order(mapping) + domain.kind == :scene && continue + _domain_has_post_scene_work(simulation, domain) || continue + _materialize_routes_for_domain!(simulation, domain, i) + _run_domain_models!(simulation, domain, constants, i; phase=:post_scene) + _update_domain_environment_index!(simulation, domain) + end + end + return simulation +end + +function _raw_meteo_for_staged_graph_domains(environment::GlobalConstant) + return environment_meteo(environment) +end + +function _raw_meteo_for_staged_graph_domains(environment::AbstractEnvironmentBackend) + return environment +end + +function _run_single_status_domain_all_steps!( + simulation::DomainSimulation, + domain::Domain, + constants, + nsteps::Int +) + for step in 1:nsteps + _run_domain_models!(simulation, domain, constants, step) + end + return nothing +end + +function _prepare_graph_domain_runtime!( + simulation::DomainSimulation, + domain::Domain, + object, + meteo, + constants, + nsteps::Int; + check=true, + executor=SequentialEx(), + type_promotion=_type_promotion(_domain_mapping(domain)), +) + roots = _domain_graph_roots(object, domain) + graph_simulations = GraphSimulation[] + for root in roots + _materialize_graph_route_attributes_for_domain!(simulation, domain, root, 1) + push!( + graph_simulations, + GraphSimulation( + root, + _domain_mapping(domain); + nsteps=nsteps, + check=check, + outputs=nothing, + type_promotion=type_promotion, + ), + ) + end + graph_state = DomainGraphState(graph_simulations) + simulation.domain_states[domain.name] = graph_state + representative_simulation = first(graph_simulations) + effective_multirate = _effective_multirate(representative_simulation) + dep_graph = dep(representative_simulation) + timeline = _timeline_context(meteo) + meteo_sampler = effective_multirate ? _prepare_meteo_sampler(meteo) : nothing + runtime_clock_rows = _runtime_clock_rows(representative_simulation, timeline, dep_graph) + effective_executor = executor + validate_meteo_inputs(get_model_specs(representative_simulation), meteo) + _validate_meteo_derived_timestep_requirements!(runtime_clock_rows, timeline) + if effective_multirate + if executor != SequentialEx() + @warn string( + "Multi-rate MTG domain `$(domain.name)` currently executes sequentially. ", + "Provided `executor=$(executor)` is ignored in this mode. ", + "Use `executor=SequentialEx()` to silence this warning." + ) maxlog = 1 + effective_executor = SequentialEx() + end + _warn_if_no_model_runs_at_base_timestep(runtime_clock_rows, timeline) + for graph_simulation in graph_simulations + validate_canonical_publishers(graph_simulation) + configure_temporal_buffers!(graph_simulation, timeline) + end + end + return DomainGraphRuntime(graph_state, meteo, constants, effective_multirate, timeline, meteo_sampler, effective_executor) +end + +function _meteo_for_graph_step(meteo, step::Int, nsteps::Int) + return _meteo_row_at_step(meteo, step) +end + +function _meteo_for_graph_step(backend::AbstractEnvironmentBackend, step::Int, nsteps::Int) + return backend +end + +function _run_graph_domain_step!( + simulation::DomainSimulation, + domain::Domain, + runtime::DomainGraphRuntime, + step::Int, + nsteps::Int; + check=true, + phase::Symbol=:normal, +) + meteo_i = _meteo_for_graph_step(runtime.meteo, step, nsteps) + meteo_provider = (node, status, i, t, model_clock, model_spec, meteo, meteo_sampler, multirate) -> + _graph_domain_environment_for_model( + simulation, + domain, + node, + status, + t, + model_clock, + model_spec, + meteo, + meteo_sampler, + multirate, + ) + after_model_run = (node, model_spec, status, i, t) -> begin + _scatter_graph_domain_environment_outputs!(simulation, domain, node, model_spec, status, t) + for hard_child in node.hard_dependency + _scatter_graph_domain_hard_dependency_environment_outputs!(simulation, domain, hard_child, status, t) + end + nothing + end + skip_model_run = node -> !_should_visit_domain_node(simulation, domain, node; phase=phase) + for graph_simulation in runtime.state.simulations + _materialize_graph_routes_for_domain!(simulation, domain, graph_simulation, step) + roots = collect(dep(graph_simulation).roots) + models = get_models(graph_simulation) + for (_, dependency_node) in roots + run_node_multiscale!( + graph_simulation, + dependency_node, + step, + models, + meteo_i, + runtime.constants, + graph_simulation, + check, + runtime.executor, + runtime.effective_multirate, + runtime.timeline, + runtime.meteo_sampler; + meteo_provider=meteo_provider, + after_model_run=after_model_run, + skip_model_run=skip_model_run, + ) + end + if phase == :normal + runtime.effective_multirate && update_requested_outputs!(graph_simulation, _time_from_step(step, runtime.timeline)) + save_results!(graph_simulation, step) + end + end + _publish_graph_domain_step_outputs!( + simulation, + domain, + runtime.state, + step; + effective_multirate=runtime.effective_multirate, + phase=phase, + ) + _update_domain_environment_index!(simulation, domain) + return runtime.state +end + +function _finalize_graph_domain_runtime!(runtime::DomainGraphRuntime) + for graph_simulation in runtime.state.simulations + for (organ, index) in graph_simulation.outputs_index + resize!(outputs(graph_simulation)[organ], index - 1) + end + end + return runtime.state +end + +""" + run!(mtg, mapping::SimulationMapping, meteo=nothing, constants=PlantMeteo.Constants(); ...) + +Run a multi-domain simulation where MTG-backed domains are selected from `mtg` +and executed with the existing `GraphSimulation` engine. + +The runner advances all domains one base timestep at a time. Domains whose +`kind` is not `:scene` run first in mapping order, then `:scene` domains run so +they can consume plant, soil, and graph-domain streams from the same timestep. +Routes into graph domains are supported for `OneToManyBroadcast()` when the +source domain runs earlier in the timestep. +""" +function run!( + object::MultiScaleTreeGraph.Node, + mapping::SimulationMapping, + meteo=nothing, + constants=PlantMeteo.Constants(); + nsteps=nothing, + check=true, + executor=SequentialEx(), + type_promotion=nothing, +) + simulation = _build_domain_simulation(mapping, meteo; staged_graph_domains=true) + raw_meteo = _raw_meteo_for_staged_graph_domains(simulation.environment) + isnothing(nsteps) && (nsteps = get_nsteps(simulation.environment)) + run_order = _domain_run_order(mapping) + graph_runtimes = Dict{Symbol,DomainGraphRuntime}() + + for step in 1:nsteps + for scene_phase in (false, true) + for domain in run_order + (domain.kind == :scene) == scene_phase || continue + if _is_graph_domain(domain) + domain.kind == :scene && error( + "Scene domain `$(domain.name)` is MTG-backed. The MTG-domain runner currently supports ", + "single-status scene domains only." + ) + runtime = get(graph_runtimes, domain.name, nothing) + if isnothing(runtime) + domain_type_promotion = isnothing(type_promotion) ? _type_promotion(_domain_mapping(domain)) : type_promotion + runtime = _prepare_graph_domain_runtime!( + simulation, + domain, + object, + raw_meteo, + constants, + nsteps; + check=check, + executor=executor, + type_promotion=domain_type_promotion, + ) + graph_runtimes[domain.name] = runtime + end + _run_graph_domain_step!(simulation, domain, runtime, step, nsteps; check=check) + else + _materialize_routes_for_domain!(simulation, domain, step) + _run_domain_models!(simulation, domain, constants, step) + _update_domain_environment_index!(simulation, domain) + end + end + end + for domain in run_order + domain.kind == :scene && continue + _domain_has_post_scene_work(simulation, domain) || continue + if _is_graph_domain(domain) + runtime = graph_runtimes[domain.name] + _run_graph_domain_step!(simulation, domain, runtime, step, nsteps; check=check, phase=:post_scene) + else + _materialize_routes_for_domain!(simulation, domain, step) + _run_domain_models!(simulation, domain, constants, step; phase=:post_scene) + _update_domain_environment_index!(simulation, domain) + end + end + end + + for runtime in values(graph_runtimes) + _finalize_graph_domain_runtime!(runtime) + end + + return simulation +end + +""" + explain_domains(mapping_or_simulation) + +Return structured rows describing domains in a simulation mapping. +""" +function explain_domains(mapping::SimulationMapping) + return [ + (domain=domain.name, kind=domain.kind, mapping=typeof(domain.mapping), selector=domain.selector) + for domain in mapping.domains + ] +end + +explain_domains(simulation::DomainSimulation) = explain_domains(simulation.mapping) + +function _domain_model_rows(mapping::SimulationMapping) + rows = NamedTuple[] + for domain in mapping.domains + for (scale, declarations) in _domain_entries(domain) + for (process, spec) in pairs(parse_model_specs(declarations)) + key = DomainModelKey(domain.name, scale, process) + push!(rows, ( + key=key, + domain=domain.name, + kind=domain.kind, + scale=scale, + process=process, + model=typeof(model_(spec)), + timestep=timestep(spec), + inputs=inputs_(spec), + outputs=outputs_(spec), + meteo_inputs=meteo_inputs_(spec), + meteo_outputs=meteo_outputs_(spec), + updates=updates(spec), + )) + end + end + end + return rows +end + +""" + explain_domain_models(mapping_or_simulation) + +Return structured rows for every model process inside every domain. +""" +explain_domain_models(mapping::SimulationMapping) = _domain_model_rows(mapping) +explain_domain_models(simulation::DomainSimulation) = explain_domain_models(simulation.mapping) + +""" + explain_domain_statuses(simulation) + +Return structured rows describing runtime status counts by domain and scale. +For graph domains, one row is returned per scale. For single-status domains, +the scale is `:Default`. +""" +function explain_domain_statuses(simulation::DomainSimulation) + rows = NamedTuple[] + for domain in simulation.mapping.domains + state = get(simulation.domain_states, domain.name, nothing) + if state isa DomainGraphState + graph_statuses = status(state) + declared_scales = Symbol[first(entry) for entry in _domain_entries(domain)] + runtime_scales = collect(keys(graph_statuses)) + for scale in sort!(unique!(vcat(declared_scales, runtime_scales)); by=string) + statuses_at_scale = get(graph_statuses, scale, Status[]) + push!(rows, ( + domain=domain.name, + kind=domain.kind, + scale=scale, + nstatuses=length(statuses_at_scale), + state=typeof(state), + )) + end + elseif state isa ModelMapping{SingleScale} + push!(rows, ( + domain=domain.name, + kind=domain.kind, + scale=:Default, + nstatuses=1, + state=typeof(state), + )) + end + end + return rows +end + +""" + explain_schedule(simulation) + +Return structured rows describing effective per-domain schedules. +""" +function explain_schedule(simulation::DomainSimulation) + rows = NamedTuple[] + for domain in simulation.mapping.domains, (key, clock) in simulation.model_clocks + key.domain == domain.name || continue + push!(rows, ( + domain=domain.name, + kind=domain.kind, + scale=key.scale, + process=key.process, + dt_steps=clock.dt, + phase=clock.phase, + dt_seconds=clock.dt * simulation.timeline.base_step_seconds, + )) + end + return rows +end + +""" + explain_domain_dependencies(simulation) + +Return structured rows describing resolved cross-domain dependencies. +""" +function explain_domain_dependencies(simulation::DomainSimulation) + rows = NamedTuple[] + for ((consumer, name), producers) in simulation.dependency_bindings + policy = simulation.dependency_policies[(consumer, name)] + variable = simulation.dependency_variables[(consumer, name)] + for producer in producers + push!(rows, ( + mode=:value, + consumer=consumer, + dependency=name, + producer=producer, + variable=variable, + policy=typeof(policy), + )) + end + end + for ((consumer, name), producers) in simulation.hard_domain_dependency_bindings + for producer in producers + push!(rows, ( + mode=:hard_domain, + consumer=consumer, + dependency=name, + producer=producer, + variable=nothing, + policy=nothing, + )) + end + end + return rows +end + +""" + explain_routes(simulation) + +Return structured rows describing resolved explicit cross-domain routes. +""" +function explain_routes(simulation::DomainSimulation) + rows = NamedTuple[] + for (i, route) in enumerate(simulation.mapping.routes) + clock = simulation.route_clocks[i] + for producer in simulation.route_bindings[i] + push!(rows, ( + route=i, + from=route.from, + to=route.to, + producer=producer, + source_var=route.from.var, + target_var=route.to.var, + cardinality=typeof(route.cardinality), + policy=typeof(route.policy), + dt_steps=clock.dt, + phase=clock.phase, + dt_seconds=clock.dt * simulation.timeline.base_step_seconds, + )) + end + end + return rows +end diff --git a/src/mtg/ModelSpec.jl b/src/mtg/ModelSpec.jl index f461515ec..2953f88ce 100644 --- a/src/mtg/ModelSpec.jl +++ b/src/mtg/ModelSpec.jl @@ -1,5 +1,5 @@ """ - ModelSpec(model; multiscale=nothing, timestep=nothing, input_bindings=NamedTuple(), meteo_bindings=NamedTuple(), meteo_window=nothing, output_routing=NamedTuple(), scope=:global) + ModelSpec(model; multiscale=nothing, timestep=nothing, input_bindings=NamedTuple(), meteo_bindings=NamedTuple(), meteo_window=nothing, output_routing=NamedTuple(), scope=:global, updates=()) User-side model configuration wrapper for mapping/model list composition. @@ -7,7 +7,7 @@ User-side model configuration wrapper for mapping/model list composition. This allows modelers to publish reusable models while users decide how models are coupled in their simulation setup. """ -struct ModelSpec{M,MS,TS,IB,MB,MW,OR,SC} +struct ModelSpec{M,MS,TS,IB,MB,MW,OR,SC,UP} model::M multiscale::MS timestep::TS @@ -16,6 +16,33 @@ struct ModelSpec{M,MS,TS,IB,MB,MW,OR,SC} meteo_window::MW output_routing::OR scope::SC + updates::UP +end + +""" + Updates(vars...; after=nothing) + +Scenario-level declaration that a model updates variables which may also be +computed by another model at the same scale. + +`after` is intentionally mapping-level metadata: the model implementation stays +reusable, while the simulation setup can declare ordering constraints that only +exist in this coupling. +""" +struct Updates{V,A} + variables::V + after::A +end + +function Updates(vars::Vararg{Union{Symbol,AbstractString}}; after=nothing) + isempty(vars) && error("Updates(...) requires at least one variable.") + return Updates(Tuple(Symbol(v) for v in vars), _normalize_update_after(after)) +end + +function _normalize_update_after(after) + isnothing(after) && return () + after isa Union{Symbol,AbstractString} && return (Symbol(after),) + return Tuple(Symbol(v) for v in after) end function _normalize_multiscale_mapping(model::AbstractModel, mapped_variables) @@ -24,6 +51,31 @@ function _normalize_multiscale_mapping(model::AbstractModel, mapped_variables) return mapped_variables_(mapped) end +_normalize_updates(updates::Updates) = (updates,) + +function _normalize_updates(updates::Tuple) + all(update -> update isa Updates, updates) || error( + "Unsupported updates tuple. Use `Updates(:var; after=:process)` entries." + ) + return updates +end + +function _normalize_updates(updates::AbstractVector) + all(update -> update isa Updates, updates) || error( + "Unsupported updates vector. Use `Updates(:var; after=:process)` entries." + ) + return Tuple(updates) +end + +function _normalize_updates(updates) + updates == NamedTuple() && return () + updates == () && return () + error( + "Unsupported updates metadata `$(updates)` of type `$(typeof(updates))`. ", + "Use `Updates(:var; after=:process)` or a tuple/vector of `Updates`." + ) +end + function ModelSpec( model::AbstractModel; multiscale=nothing, @@ -32,7 +84,8 @@ function ModelSpec( meteo_bindings=NamedTuple(), meteo_window=nothing, output_routing=NamedTuple(), - scope=:global + scope=:global, + updates=() ) base_model = model base_multiscale = multiscale @@ -48,7 +101,8 @@ function ModelSpec( normalized_meteo_window = _normalize_meteo_window(meteo_window) normalized_output_routing = _normalize_output_routing(output_routing) normalized_scope = _normalize_scope_selector(scope) - return ModelSpec{typeof(base_model),typeof(normalized_multiscale),typeof(timestep),typeof(normalized_input_bindings),typeof(normalized_meteo_bindings),typeof(normalized_meteo_window),typeof(normalized_output_routing),typeof(normalized_scope)}( + normalized_updates = _normalize_updates(updates) + return ModelSpec{typeof(base_model),typeof(normalized_multiscale),typeof(timestep),typeof(normalized_input_bindings),typeof(normalized_meteo_bindings),typeof(normalized_meteo_window),typeof(normalized_output_routing),typeof(normalized_scope),typeof(normalized_updates)}( base_model, normalized_multiscale, timestep, @@ -56,7 +110,8 @@ function ModelSpec( normalized_meteo_bindings, normalized_meteo_window, normalized_output_routing, - normalized_scope + normalized_scope, + normalized_updates ) end @@ -69,9 +124,10 @@ function ModelSpec( meteo_bindings=spec.meteo_bindings, meteo_window=spec.meteo_window, output_routing=spec.output_routing, - scope=spec.scope + scope=spec.scope, + updates=spec.updates ) - ModelSpec(model; multiscale=multiscale, timestep=timestep, input_bindings=input_bindings, meteo_bindings=meteo_bindings, meteo_window=meteo_window, output_routing=output_routing, scope=scope) + ModelSpec(model; multiscale=multiscale, timestep=timestep, input_bindings=input_bindings, meteo_bindings=meteo_bindings, meteo_window=meteo_window, output_routing=output_routing, scope=scope, updates=updates) end as_model_spec(spec::ModelSpec) = spec @@ -148,6 +204,18 @@ function with_scope(model_or_spec, scope) return ModelSpec(spec; scope=_normalize_scope_selector(scope)) end +""" + with_updates(model_or_spec, updates) + +Return a `ModelSpec` with explicit variable-update metadata. +""" +function with_updates(model_or_spec, updates) + spec = as_model_spec(model_or_spec) + return ModelSpec(spec; updates=(spec.updates..., _normalize_updates(updates)...)) +end + +(updates::Updates)(model_or_spec) = with_updates(model_or_spec, updates) + function _normalize_input_binding(binding) if binding isa NamedTuple return haskey(binding, :policy) ? binding : (; binding..., policy=HoldLast()) @@ -408,3 +476,13 @@ get_status(m::ModelSpec) = nothing get_mapped_variables(m::ModelSpec) = mapped_variables_(m) process(m::ModelSpec) = process(model_(m)) timestep(m::ModelSpec) = m.timestep +inputs_(m::ModelSpec) = inputs_(model_(m)) +outputs_(m::ModelSpec) = outputs_(model_(m)) +dep(m::ModelSpec) = dep(model_(m)) +init_variables(m::ModelSpec; verbose::Bool=true) = init_variables(model_(m); verbose=verbose) +meteo_inputs_(m::ModelSpec) = meteo_inputs_(model_(m)) +meteo_outputs_(m::ModelSpec) = meteo_outputs_(model_(m)) + +function run!(m::ModelSpec, models, status, meteo, constants=nothing, extra=nothing) + return run!(model_(m), models, status, meteo, constants, extra) +end diff --git a/src/mtg/add_organ.jl b/src/mtg/add_organ.jl index 784127f89..5ac2c8450 100644 --- a/src/mtg/add_organ.jl +++ b/src/mtg/add_organ.jl @@ -4,7 +4,7 @@ Add an organ to the graph, automatically taking care of initialising the status of the organ (multiscale-)variables. This function should be called from a model that implements organ emergence, for example in function of thermal time. - + # Arguments * `node`: the node to which the organ is added (the parent organ of the new organ) @@ -20,7 +20,7 @@ This function should be called from a model that implements organ emergence, for * `attributes`: the attributes of the new node. If not provided, an empty dictionary is used. * `check`: a boolean indicating if variables initialisation should be checked. Passed to `init_node_status!`. -# Returns +# Returns * `status`: the status of the new node @@ -34,4 +34,176 @@ function add_organ!(node::MultiScaleTreeGraph.Node, sim_object, link, symbol, sc st = init_node_status!(new_node, sim_object.statuses, sim_object.status_templates, sim_object.reverse_multiscale_mapping, sim_object.var_need_init, check=check) return st -end \ No newline at end of file +end + +function _delete_ref_from_refvector!(rv::RefVector, ref) + filter!(stored_ref -> stored_ref !== ref, parent(rv)) + return rv +end + +function _remove_status_from_scale!(statuses::Dict, scale::Symbol, st::Status, nid::Int) + haskey(statuses, scale) || return nothing + deleteat!(statuses[scale], findall(candidate -> candidate === st || node_id(candidate.node) == nid, statuses[scale])) + return nothing +end + +function _remove_reverse_refs_for_status!(sim_object, node_scale::Symbol, st::Status) + haskey(sim_object.reverse_multiscale_mapping, node_scale) || return nothing + for (target_scale, vars) in sim_object.reverse_multiscale_mapping[node_scale] + haskey(sim_object.status_templates, target_scale) || continue + target_template = sim_object.status_templates[target_scale] + for (source_var, target_var_) in vars + source_var in propertynames(st) || continue + target_var = target_var_ isa PreviousTimeStep ? target_var_.variable : target_var_ + haskey(target_template, target_var) || continue + target_value = target_template[target_var] + target_value isa RefVector || continue + _delete_ref_from_refvector!(target_value, refvalue(st, source_var)) + end + end + return nothing +end + +function _remove_temporal_state_for_node!(sim_object, nid::Int) + temporal = temporal_state(sim_object) + for key in collect(keys(temporal.caches)) + key.node_id == nid && delete!(temporal.caches, key) + end + for key in collect(keys(temporal.streams)) + key.node_id == nid && delete!(temporal.streams, key) + end + return nothing +end + +function _status_node_registered(sim_object, node::MultiScaleTreeGraph.Node) + node_scale = symbol(node) + haskey(sim_object.statuses, node_scale) || return false + nid = node_id(node) + return any(st -> hasproperty(st, :node) && node_id(st.node) == nid && st.node === node, sim_object.statuses[node_scale]) +end + +function _is_descendant_node(candidate::MultiScaleTreeGraph.Node, ancestor::MultiScaleTreeGraph.Node) + current = parent(candidate) + while !isnothing(current) + current === ancestor && return true + current = parent(current) + end + return false +end + +function _children_without_node(parent_node::MultiScaleTreeGraph.Node, node::MultiScaleTreeGraph.Node) + children_without_node = empty(AbstractTrees.children(parent_node)) + for child in AbstractTrees.children(parent_node) + child === node || push!(children_without_node, child) + end + return children_without_node +end + +function _repair_reparent_child_links!( + node::MultiScaleTreeGraph.Node, + old_parent, + new_parent::MultiScaleTreeGraph.Node, +) + if !isnothing(old_parent) && old_parent !== new_parent + MultiScaleTreeGraph.rechildren!(old_parent, _children_without_node(old_parent, node)) + end + + new_children = _children_without_node(new_parent, node) + push!(new_children, node) + MultiScaleTreeGraph.rechildren!(new_parent, new_children) + return nothing +end + +""" + remove_organ!(node::MultiScaleTreeGraph.Node, sim_object; attribute_name=:plantsimengine_status, recursive=false) + +Remove a simulated organ from an active [`GraphSimulation`](@ref). + +The wrapper updates PlantSimEngine runtime state before delegating to +`MultiScaleTreeGraph.delete_node!`: it removes the node status from +`sim_object.statuses`, removes references from downstream `RefVector`s, clears +temporal caches/streams for the removed node, and then deletes the MTG node. + +Only leaf/terminal nodes are removed by default. Pass `recursive=true` to delete +an internal node and its whole subtree. Reparenting children is intentionally not +handled here because it requires caller-specific biological and topological +policy. +""" +function remove_organ!(node::MultiScaleTreeGraph.Node, sim_object; attribute_name=:plantsimengine_status, recursive=false) + children = collect(AbstractTrees.children(node)) + if !recursive + isempty(children) || error( + "remove_organ! currently supports only leaf/terminal MTG nodes. ", + "Pass `recursive=true` to delete node $(node_id(node)) and its descendants, ", + "or move descendants first." + ) + else + for child in children + remove_organ!(child, sim_object; attribute_name=attribute_name, recursive=true) + end + end + + haskey(node, attribute_name) || error( + "Cannot remove MTG node $(node_id(node)) ($(symbol(node))) from PlantSimEngine runtime: ", + "the node has no `$(attribute_name)` status." + ) + + st = node[attribute_name] + st isa Status || error( + "Cannot remove MTG node $(node_id(node)) ($(symbol(node))) from PlantSimEngine runtime: ", + "`$(attribute_name)` is not a Status." + ) + + nid = node_id(node) + node_scale = symbol(node) + _remove_reverse_refs_for_status!(sim_object, node_scale, st) + _remove_status_from_scale!(sim_object.statuses, node_scale, st, nid) + _remove_temporal_state_for_node!(sim_object, nid) + pop!(node, attribute_name) + return MultiScaleTreeGraph.delete_node!(node) +end + +""" + reparent_organ!(node::MultiScaleTreeGraph.Node, new_parent::MultiScaleTreeGraph.Node, sim_object; attribute_name=:plantsimengine_status) + +Move an already-simulated MTG node under another already-simulated parent in the +same active [`GraphSimulation`](@ref). + +The node status and downstream `RefVector`s keep pointing to the same node +object, so no status rewiring is needed when the subtree remains in the same +simulation. This wrapper validates that both nodes are registered in +PlantSimEngine runtime state and rejects moves that would create a cycle. +""" +function reparent_organ!( + node::MultiScaleTreeGraph.Node, + new_parent::MultiScaleTreeGraph.Node, + sim_object; + attribute_name=:plantsimengine_status, +) + node === new_parent && error("Cannot reparent MTG node $(node_id(node)) to itself.") + _is_descendant_node(new_parent, node) && error( + "Cannot reparent MTG node $(node_id(node)) under descendant node $(node_id(new_parent)); ", + "this would create a cycle." + ) + haskey(node, attribute_name) || error( + "Cannot reparent MTG node $(node_id(node)) ($(symbol(node))) in PlantSimEngine runtime: ", + "the node has no `$(attribute_name)` status." + ) + haskey(new_parent, attribute_name) || error( + "Cannot reparent MTG node $(node_id(node)) under node $(node_id(new_parent)) ($(symbol(new_parent))) ", + "in PlantSimEngine runtime: the new parent has no `$(attribute_name)` status." + ) + _status_node_registered(sim_object, node) || error( + "Cannot reparent MTG node $(node_id(node)) ($(symbol(node))): ", + "it is not registered in this GraphSimulation." + ) + _status_node_registered(sim_object, new_parent) || error( + "Cannot reparent MTG node $(node_id(node)) under node $(node_id(new_parent)) ($(symbol(new_parent))): ", + "the new parent is not registered in this GraphSimulation." + ) + + old_parent = parent(node) + MultiScaleTreeGraph.reparent!(node, new_parent) + _repair_reparent_child_links!(node, old_parent, new_parent) + return node +end diff --git a/src/mtg/initialisation.jl b/src/mtg/initialisation.jl index 98aa612c6..f3fba1bd8 100644 --- a/src/mtg/initialisation.jl +++ b/src/mtg/initialisation.jl @@ -328,14 +328,15 @@ function init_simulation(mtg, mapping; nsteps=1, outputs=nothing, type_promotion scale_reachability = _scale_reachability_from_mtg(mtg) _infer_timestep_hints!(model_specs) - ignored_same_rate_hard_children = _same_rate_hard_dependency_children(model_specs, soft_dep_graphs_roots) - active_processes_by_scale = _active_processes_for_inference(model_specs, ignored_same_rate_hard_children) + validate_hard_dependency_timestep_consistency(model_specs, soft_dep_graphs_roots) + ignored_hard_children = _hard_dependency_children(soft_dep_graphs_roots) + active_processes_by_scale = _active_processes_for_inference(model_specs, ignored_hard_children) infer_model_specs_configuration!( model_specs; scale_reachability=scale_reachability, active_processes_by_scale=active_processes_by_scale ) - validate_model_specs_configuration(model_specs) + validate_model_specs_configuration(model_specs; ignored_processes_by_scale=ignored_hard_children) # Get the status of each node by node type, pre-initialised considering multi-scale variables: statuses, status_templates, reverse_multiscale_mapping, vars_need_init = diff --git a/src/mtg/model_spec_inference.jl b/src/mtg/model_spec_inference.jl index 00dfd6ba3..ef2701fa6 100644 --- a/src/mtg/model_spec_inference.jl +++ b/src/mtg/model_spec_inference.jl @@ -243,11 +243,12 @@ function _same_timestep_signature(sig_a, sig_b) end function _hard_dep_same_rate_as_parent(model_specs, parent_scale::Symbol, parent_process::Symbol, child_scale::Symbol, child_process::Symbol) - parent_scale == child_scale || return false parent_specs = get(model_specs, parent_scale, nothing) + child_specs = get(model_specs, child_scale, nothing) isnothing(parent_specs) && return false + isnothing(child_specs) && return false parent_spec = get(parent_specs, parent_process, nothing) - child_spec = get(parent_specs, child_process, nothing) + child_spec = get(child_specs, child_process, nothing) isnothing(parent_spec) && return false isnothing(child_spec) && return false @@ -280,6 +281,41 @@ function _collect_same_rate_hard_dependency_children!( return nothing end +function _collect_hard_dependency_children!( + ignored_processes_by_scale::Dict{Symbol,Set{Symbol}}, + child::HardDependencyNode +) + push!(get!(ignored_processes_by_scale, child.scale, Set{Symbol}()), child.process) + for nested in child.children + _collect_hard_dependency_children!(ignored_processes_by_scale, nested) + end + return nothing +end + +function _validate_hard_dependency_timestep_consistency!( + model_specs, + parent_scale::Symbol, + parent_process::Symbol, + child::HardDependencyNode +) + _hard_dep_same_rate_as_parent(model_specs, parent_scale, parent_process, child.scale, child.process) || error( + "Hard dependency `$(child.process)` at scale `$(child.scale)` has a different timestep than parent process ", + "`$(parent_process)` at scale `$(parent_scale)`. Hard dependencies are called manually by their parent, ", + "so their `ModelSpec` timestep cannot be scheduled independently. Align the timesteps, or make the child a soft dependency." + ) + + for nested in child.children + _validate_hard_dependency_timestep_consistency!( + model_specs, + child.scale, + child.process, + nested + ) + end + + return nothing +end + function _soft_nodes_for_hard_dependency_analysis(dep_graph::DependencyGraph{Dict{Symbol,Any}}) nodes = SoftDependencyNode[] for (_, roots_at_scale) in pairs(dep_graph.roots) @@ -309,6 +345,33 @@ function _same_rate_hard_dependency_children(model_specs, dep_graph::DependencyG return ignored_processes_by_scale end +function _hard_dependency_children(dep_graph::DependencyGraph) + ignored_processes_by_scale = Dict{Symbol,Set{Symbol}}() + + for soft_node in _soft_nodes_for_hard_dependency_analysis(dep_graph) + for child in soft_node.hard_dependency + _collect_hard_dependency_children!(ignored_processes_by_scale, child) + end + end + + return ignored_processes_by_scale +end + +function validate_hard_dependency_timestep_consistency(model_specs, dep_graph::DependencyGraph) + for soft_node in _soft_nodes_for_hard_dependency_analysis(dep_graph) + for child in soft_node.hard_dependency + _validate_hard_dependency_timestep_consistency!( + model_specs, + soft_node.scale, + soft_node.process, + child + ) + end + end + + return nothing +end + function _active_processes_for_inference(model_specs, ignored_processes_by_scale::Dict{Symbol,Set{Symbol}}) active = Dict{Symbol,Set{Symbol}}() for (scale, specs_at_scale) in pairs(model_specs) @@ -368,6 +431,28 @@ function _default_policy_for_inferred_binding(model_specs, source_scale::Symbol, ) end +function _terminal_update_candidate(model_specs, candidates::Vector, var::Symbol) + length(candidates) > 1 || return nothing + + terminal_updates = NamedTuple[] + for candidate in candidates + candidate_spec = model_specs[candidate.scale][candidate.process] + var in _update_variables_for_spec(candidate_spec) || continue + has_later_update = any(candidates) do other + other.scale == candidate.scale || return false + other.process == candidate.process && return false + other.var == var || return false + other_spec = model_specs[other.scale][other.process] + var in _update_variables_for_spec(other_spec) || return false + return candidate.process in _update_after_for_var(other_spec, var) + end + has_later_update || push!(terminal_updates, candidate) + end + + length(terminal_updates) == 1 || return nothing + return only(terminal_updates) +end + function _mapped_source_scales_for_input(spec::ModelSpec, input_var::Symbol) mapped = mapped_variables_(spec) isempty(mapped) && return Set{Symbol}() @@ -457,7 +542,7 @@ function _infer_binding_from_multiscale_mapping( src_var = last(src) haskey(model_specs, src_scale) || return :skip - procs = Symbol[] + candidates = NamedTuple[] for (src_process, src_spec) in pairs(model_specs[src_scale]) if !isnothing(active_processes_by_scale) active = get(active_processes_by_scale, src_scale, Set{Symbol}()) @@ -465,11 +550,17 @@ function _infer_binding_from_multiscale_mapping( end src_var in keys(outputs_(model_(src_spec))) || continue _is_stream_only_output(src_spec, src_var) && continue - push!(procs, src_process) + push!(candidates, (scale=src_scale, process=src_process, var=src_var)) + end + + if length(candidates) == 1 + candidate = only(candidates) + else + candidate = _terminal_update_candidate(model_specs, candidates, src_var) + isnothing(candidate) && return :skip end - length(procs) == 1 || return :skip - src_process = only(procs) + src_process = candidate.process policy = _default_policy_for_inferred_binding(model_specs, src_scale, src_process, src_var) return (process=src_process, var=src_var, scale=src_scale, policy=policy) end @@ -496,6 +587,21 @@ function _infer_input_binding_for_var( policy = _default_policy_for_inferred_binding(model_specs, c.scale, c.process, c.var) return (process=c.process, var=c.var, scale=c.scale, policy=policy) elseif length(same_scale) > 1 + terminal_update = _terminal_update_candidate(model_specs, same_scale, input_var) + if !isnothing(terminal_update) + policy = _default_policy_for_inferred_binding( + model_specs, + terminal_update.scale, + terminal_update.process, + terminal_update.var, + ) + return ( + process=terminal_update.process, + var=terminal_update.var, + scale=terminal_update.scale, + policy=policy, + ) + end error( "Ambiguous inferred producer for input `$(input_var)` in process `$(process)` at scale `$(scale)`. ", "Multiple same-scale candidates were found: $(_format_candidate_list(same_scale)). ", @@ -696,7 +802,20 @@ function resolved_model_specs(mapping::AbstractDict; infer::Bool=true, validate: end infer && infer_model_specs_configuration!(model_specs) - validate && validate_model_specs_configuration(model_specs) + if validate + hard_dep_roots = try + first(hard_dependencies(mapping; verbose=false)) + catch + nothing + end + ignored_hard_children = if isnothing(hard_dep_roots) + Dict{Symbol,Set{Symbol}}() + else + validate_hard_dependency_timestep_consistency(model_specs, hard_dep_roots) + _hard_dependency_children(hard_dep_roots) + end + validate_model_specs_configuration(model_specs; ignored_processes_by_scale=ignored_hard_children) + end return model_specs end @@ -724,6 +843,9 @@ function _model_specs_rows(model_specs) input_bindings=input_bindings(spec), meteo_bindings=meteo_bindings(spec), meteo_window=meteo_window(spec), + meteo_inputs=meteo_inputs_(spec), + meteo_outputs=meteo_outputs_(spec), + updates=updates(spec), )) end end @@ -744,6 +866,9 @@ Summary fields: - `input_bindings` - `meteo_bindings` - `meteo_window` +- `meteo_inputs` +- `meteo_outputs` +- `updates` """ function explain_model_specs(target; io::IO=stdout, infer::Bool=true, validate::Bool=true) specs = target isa GraphSimulation ? resolved_model_specs(target) : resolved_model_specs(target; infer=infer, validate=validate) @@ -766,6 +891,9 @@ function explain_model_specs(target; io::IO=stdout, infer::Bool=true, validate:: input_bindings_desc = (row.input_bindings isa NamedTuple && isempty(keys(row.input_bindings))) ? "(none)" : _stringify_compact(row.input_bindings) meteo_bindings_desc = (row.meteo_bindings isa NamedTuple && isempty(keys(row.meteo_bindings))) ? "(none)" : _stringify_compact(row.meteo_bindings) meteo_window_desc = isnothing(row.meteo_window) ? "(default rolling)" : _stringify_compact(row.meteo_window) + meteo_inputs_desc = (row.meteo_inputs isa NamedTuple && isempty(keys(row.meteo_inputs))) ? "(none)" : _stringify_compact(row.meteo_inputs) + meteo_outputs_desc = (row.meteo_outputs isa NamedTuple && isempty(keys(row.meteo_outputs))) ? "(none)" : _stringify_compact(row.meteo_outputs) + updates_desc = isempty(row.updates) ? "(none)" : _stringify_compact(row.updates) println( io, " - ", @@ -781,7 +909,13 @@ function explain_model_specs(target; io::IO=stdout, infer::Bool=true, validate:: ", meteo_bindings=", meteo_bindings_desc, ", meteo_window=", - meteo_window_desc + meteo_window_desc, + ", meteo_inputs=", + meteo_inputs_desc, + ", meteo_outputs=", + meteo_outputs_desc, + ", updates=", + updates_desc ) end return rows diff --git a/src/mtg/model_spec_validation.jl b/src/mtg/model_spec_validation.jl index e45cbfd6f..6fd210ee6 100644 --- a/src/mtg/model_spec_validation.jl +++ b/src/mtg/model_spec_validation.jl @@ -419,7 +419,10 @@ end Validate mapping-level `ModelSpec` configuration before simulation runtime starts. This catches invalid timestep declarations, input bindings and output routing early. """ -function validate_model_specs_configuration(model_specs) +function validate_model_specs_configuration( + model_specs; + ignored_processes_by_scale::Dict{Symbol,Set{Symbol}}=Dict{Symbol,Set{Symbol}}() +) known_processes = Set{Symbol}() for specs_at_scale in values(model_specs) union!(known_processes, keys(specs_at_scale)) @@ -435,6 +438,7 @@ function validate_model_specs_configuration(model_specs) _validate_output_routing_for_spec(scale, process, spec) end end + validate_update_dependencies(model_specs; ignored_processes_by_scale=ignored_processes_by_scale) return nothing end diff --git a/src/processes/model_initialisation.jl b/src/processes/model_initialisation.jl index 7c999d903..7f642dbe6 100755 --- a/src/processes/model_initialisation.jl +++ b/src/processes/model_initialisation.jl @@ -90,10 +90,11 @@ function to_initialize(m::DependencyGraph) for (key, value) in dependencies for (key_in, val_in) in pairs(value.inputs) if key_in ∉ outputs_all + input_default = NamedTuple{(key_in,)}((val_in,)) if haskey(needed_variables_process, key) - needed_variables_process[key] = merge(needed_variables_process[key], NamedTuple{(key_in,)}(val_in)) + needed_variables_process[key] = merge(needed_variables_process[key], input_default) else - push!(needed_variables_process, key => NamedTuple{(key_in,)}(val_in)) + push!(needed_variables_process, key => input_default) end end end diff --git a/src/processes/models_inputs_outputs.jl b/src/processes/models_inputs_outputs.jl index 02bac7eb1..5f496d380 100644 --- a/src/processes/models_inputs_outputs.jl +++ b/src/processes/models_inputs_outputs.jl @@ -102,6 +102,14 @@ Default is `:global`. """ model_scope(spec::ModelSpec) = spec.scope +""" + updates(spec::ModelSpec) + +Scenario-level metadata for variables intentionally updated by this model after +another producer in the same mapping. +""" +updates(spec::ModelSpec) = spec.updates + """ meteo_bindings(spec::ModelSpec) @@ -122,6 +130,32 @@ Defaults to `nothing` (runtime falls back to `PlantMeteo.RollingWindow()` behavi """ meteo_window(spec::ModelSpec) = spec.meteo_window +""" + meteo_inputs(model::AbstractModel) + meteo_inputs_(model::AbstractModel) + +Meteorological/environment variables read directly by a model. + +This trait is separate from `inputs_` because meteorology may be constant, +table-backed, or produced by a microclimate domain. The default is empty. +""" +meteo_inputs(model::AbstractModel) = keys(meteo_inputs_(model)) +meteo_inputs(spec::ModelSpec) = keys(meteo_inputs_(spec)) +meteo_inputs_(model::AbstractModel) = NamedTuple() +meteo_inputs_(model::Missing) = NamedTuple() + +""" + meteo_outputs(model::AbstractModel) + meteo_outputs_(model::AbstractModel) + +Meteorological/environment variables produced by a model, for example local +microclimate variables computed over a voxel/octree domain. +""" +meteo_outputs(model::AbstractModel) = keys(meteo_outputs_(model)) +meteo_outputs(spec::ModelSpec) = keys(meteo_outputs_(spec)) +meteo_outputs_(model::AbstractModel) = NamedTuple() +meteo_outputs_(model::Missing) = NamedTuple() + """ inputs(mapping::ModelMapping) inputs(mapping::AbstractDict{Symbol,T}) diff --git a/src/run.jl b/src/run.jl index 99cbe6d15..ea1b3fb99 100644 --- a/src/run.jl +++ b/src/run.jl @@ -352,6 +352,7 @@ function _run_modellist_singleton( meteo_adjusted = adjust_weather_timesteps_to_given_length(get_status_vector_max_length(object.status), meteo) nsteps = get_nsteps(meteo_adjusted) + validate_meteo_inputs(object.models, meteo_adjusted) dep_graph = dep!(object, nsteps) @@ -661,6 +662,7 @@ function run!( runtime_clock_rows = _runtime_clock_rows(object, timeline, dep_graph) effective_executor = executor # st = status(object) + validate_meteo_inputs(get_model_specs(object), meteo) _validate_meteo_derived_timestep_requirements!(runtime_clock_rows, timeline) if effective_multirate if executor != SequentialEx() @@ -730,7 +732,10 @@ function run_node_multiscale!( executor, multirate, timeline::TimelineContext, - meteo_sampler + meteo_sampler; + meteo_provider=nothing, + after_model_run=nothing, + skip_model_run=nothing, ) where {T<:GraphSimulation} # T is the status of each node by organ type # run!(status(object), dependency_node, meteo, constants, extra) @@ -747,17 +752,27 @@ function run_node_multiscale!( model_clock = _model_clock(model_spec, node.value, timeline) t = _time_from_step(i, timeline) - for st in node_statuses # for each node status at the current scale (potentially in parallel over nodes) - should_run = !multirate || _should_run_at_time(model_clock, t) - !should_run && continue - if multirate - resolve_inputs_from_temporal_state!(object, node, st, t, model_spec, timeline) - end - meteo_for_model = multirate ? _sample_meteo_for_model(meteo_sampler, meteo, i, model_clock, model_spec) : meteo - # Actual call to the model: - run!(node.value, models_at_scale, st, meteo_for_model, constants, extra) - if multirate - update_temporal_state_outputs!(object, node, model_spec, st, t) + skip_node = !isnothing(skip_model_run) && skip_model_run(node) + if !skip_node + for st in node_statuses # for each node status at the current scale (potentially in parallel over nodes) + should_run = !multirate || _should_run_at_time(model_clock, t) + !should_run && continue + if multirate + resolve_inputs_from_temporal_state!(object, node, st, t, model_spec, timeline) + end + meteo_for_model = if isnothing(meteo_provider) + multirate ? _sample_meteo_for_model(meteo_sampler, meteo, i, model_clock, model_spec) : meteo + else + meteo_provider(node, st, i, t, model_clock, model_spec, meteo, meteo_sampler, multirate) + end + # Actual call to the model: + run!(node.value, models_at_scale, st, meteo_for_model, constants, extra) + if !isnothing(after_model_run) + after_model_run(node, model_spec, st, i, t) + end + if multirate + update_temporal_state_outputs!(object, node, model_spec, st, t) + end end end @@ -768,6 +783,22 @@ function run_node_multiscale!( #! check if we can run this safely in a @floop loop. I would say no, #! because we are running a parallel computation above already, modifying the node.simulation_id, #! which is not thread-safe yet. - run_node_multiscale!(object, child, i, models, meteo, constants, extra, check, executor, multirate, timeline, meteo_sampler) + run_node_multiscale!( + object, + child, + i, + models, + meteo, + constants, + extra, + check, + executor, + multirate, + timeline, + meteo_sampler; + meteo_provider=meteo_provider, + after_model_run=after_model_run, + skip_model_run=skip_model_run, + ) end end diff --git a/src/time/runtime/environment_backends.jl b/src/time/runtime/environment_backends.jl new file mode 100644 index 000000000..85440b46f --- /dev/null +++ b/src/time/runtime/environment_backends.jl @@ -0,0 +1,310 @@ +""" + AbstractEnvironmentBackend + +Backend protocol for meteorology and mutable microclimate providers. + +PlantSimEngine defines the protocol, not the spatial indexing strategy. External +packages can subtype this and implement `sample`, `scatter!`, `update_index!`, +`get_nsteps`, and `base_step_seconds`. +""" +abstract type AbstractEnvironmentBackend end + +""" + EnvironmentSupport(domain, scale, process, status) + +Minimal support descriptor passed to environment backends when a model samples +or scatters environmental variables. +""" +struct EnvironmentSupport{S} + domain::Symbol + scale::Symbol + process::Symbol + status::S +end + +""" + GlobalConstant(meteo) + +Environment backend that preserves the current PlantSimEngine behavior: every +model receives the same meteo object or meteo row at a given timestep. +""" +struct GlobalConstant{M} <: AbstractEnvironmentBackend + meteo::M +end + +environment_meteo(backend::GlobalConstant) = backend.meteo + +""" + environment_backend(meteo_or_backend) + +Return an environment backend. Plain meteorology is wrapped in +`GlobalConstant`; existing environment backends are returned unchanged. +""" +environment_backend(backend::AbstractEnvironmentBackend) = backend +environment_backend(meteo) = GlobalConstant(meteo) + +""" + base_step_seconds(backend) + +Return the duration of one simulation base step in seconds. +""" +function base_step_seconds(backend::AbstractEnvironmentBackend) + error( + "Environment backend `$(typeof(backend))` must implement ", + "`PlantSimEngine.base_step_seconds(backend)`." + ) +end + +function base_step_seconds(backend::GlobalConstant) + return _timeline_context(environment_meteo(backend)).base_step_seconds +end + +get_nsteps(backend::AbstractEnvironmentBackend) = error( + "Environment backend `$(typeof(backend))` must implement ", + "`PlantSimEngine.get_nsteps(backend)`." +) +get_nsteps(backend::GlobalConstant) = isnothing(environment_meteo(backend)) ? 1 : get_nsteps(environment_meteo(backend)) + +function _validate_meteo_duration(backend::AbstractEnvironmentBackend) + sec = base_step_seconds(backend) + sec isa Real && sec > 0 || error( + "Environment backend `$(typeof(backend))` returned invalid base step seconds `$(sec)`." + ) + return nothing +end + +_validate_meteo_duration(backend::GlobalConstant) = _validate_meteo_duration(environment_meteo(backend)) + +_timeline_context(backend::AbstractEnvironmentBackend) = TimelineContext(float(base_step_seconds(backend))) +_timeline_context(backend::GlobalConstant) = _timeline_context(environment_meteo(backend)) + +function _meteo_row_at_step(meteo, i::Int) + isnothing(meteo) && return nothing + get_nsteps(meteo) == 1 && return meteo + return first(Iterators.drop(Tables.rows(meteo), i - 1)) +end + +function _available_meteo_variables(meteo) + row = _first_meteo_row(meteo) + isnothing(row) && return nothing + return Set(Symbol.(propertynames(row))) +end + +""" + environment_variables(backend) + +Return a set of variable names that the backend can provide, or `nothing` when +the backend cannot enumerate them cheaply. +""" +environment_variables(::AbstractEnvironmentBackend) = nothing +function environment_variables(backend::GlobalConstant) + meteo = environment_meteo(backend) + isnothing(meteo) && return Set{Symbol}() + return _available_meteo_variables(meteo) +end + +function validate_meteo_inputs(model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}, backend::GlobalConstant) + return invoke( + validate_meteo_inputs, + Tuple{Dict{Symbol,Dict{Symbol,ModelSpec}},AbstractEnvironmentBackend}, + model_specs, + backend, + ) +end + +function validate_meteo_inputs(model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}, backend::AbstractEnvironmentBackend) + available = environment_variables(backend) + isnothing(available) && return nothing + + missing_rows = NamedTuple[] + for (scale, specs_at_scale) in model_specs + for (process, spec) in specs_at_scale + required = _raw_meteo_requirements_for_spec(spec) + missing = Symbol[var for var in required if !(var in available)] + isempty(missing) && continue + push!(missing_rows, (scale=scale, process=process, missing=Tuple(missing))) + end + end + + isempty(missing_rows) && return nothing + + details = join( + [ + string(row.scale, "/", row.process, " missing ", row.missing) + for row in missing_rows + ], + "; " + ) + error( + "Environment backend `$(typeof(backend))` is missing variables required by model `meteo_inputs_`: ", + details, + ". Add the variables to the backend, declare a `MeteoBindings(source=...)` remapping, ", + "or remove the unused meteo input from the model trait." + ) +end + +""" + sample(backend, variable, support, time) + +Sample one environmental variable for a model support at a runtime time. +""" +function sample(backend::AbstractEnvironmentBackend, variable::Symbol, support::EnvironmentSupport, time) + error( + "Environment backend `$(typeof(backend))` must implement ", + "`PlantSimEngine.sample(backend, variable, support, time)`." + ) +end + +function sample(backend::GlobalConstant, variable::Symbol, support::EnvironmentSupport, time) + meteo = _meteo_row_at_step(environment_meteo(backend), Int(round(time))) + isnothing(meteo) && return nothing + hasproperty(meteo, variable) || error( + "GlobalConstant meteo does not provide variable `$(variable)` for `$(support.domain)/$(support.scale)/$(support.process)`." + ) + return getproperty(meteo, variable) +end + +""" + scatter!(backend, variable, support, value, time) + +Scatter one model-computed environmental value back to a mutable backend. +""" +function scatter!(backend::AbstractEnvironmentBackend, variable::Symbol, support::EnvironmentSupport, value, time) + error( + "Environment backend `$(typeof(backend))` does not implement ", + "`PlantSimEngine.scatter!(backend, variable, support, value, time)`." + ) +end + +scatter!(backend::GlobalConstant, variable::Symbol, support::EnvironmentSupport, value, time) = error( + "GlobalConstant is immutable and cannot receive environment output `$(variable)` from ", + "`$(support.domain)/$(support.scale)/$(support.process)`." +) + +""" + update_index!(backend, entities) + +Update the backend spatial/entity index after topology or geometry changes. +""" +update_index!(::AbstractEnvironmentBackend, entities) = nothing +update_index!(::GlobalConstant, entities) = nothing + +""" + sample_environment(backend, support, time, variables) + +Sample a meteo-like row for a model. `GlobalConstant` returns the original meteo +row; other backends return a `NamedTuple` assembled from `sample` calls. +""" +function sample_environment(backend::AbstractEnvironmentBackend, support::EnvironmentSupport, time, variables) + pairs = Pair{Symbol,Any}[] + for variable in variables + push!(pairs, variable => sample(backend, variable, support, time)) + end + return (; pairs...) +end + +function sample_environment(backend::GlobalConstant, support::EnvironmentSupport, time, variables) + return _meteo_row_at_step(environment_meteo(backend), Int(round(time))) +end + +function _environment_sampling_rules(model_spec::ModelSpec) + bindings = meteo_bindings(model_spec) + bindings = bindings isa NamedTuple ? bindings : NamedTuple() + rules = Pair{Symbol,Symbol}[] + for target in keys(meteo_inputs_(model_spec)) + source = target + if haskey(bindings, target) + rule = bindings[target] + rule isa NamedTuple && haskey(rule, :source) && (source = Symbol(rule.source)) + end + push!(rules, target => source) + end + return rules +end + +function sample_environment( + backend::AbstractEnvironmentBackend, + support::EnvironmentSupport, + time, + model_spec::ModelSpec +) + pairs = Pair{Symbol,Any}[] + for (target, source) in _environment_sampling_rules(model_spec) + push!(pairs, target => sample(backend, source, support, time)) + end + return (; pairs...) +end + +function sample_environment( + backend::GlobalConstant, + support::EnvironmentSupport, + time, + model_spec::ModelSpec +) + row = _meteo_row_at_step(environment_meteo(backend), Int(round(time))) + bindings = meteo_bindings(model_spec) + has_bindings = bindings isa NamedTuple && !isempty(keys(bindings)) + !has_bindings && return row + + pairs = Pair{Symbol,Any}[] + for (target, source) in _environment_sampling_rules(model_spec) + isnothing(row) && error( + "GlobalConstant meteo is `nothing`, but `$(support.domain)/$(support.scale)/$(support.process)` ", + "requires meteo variable `$(target)`." + ) + hasproperty(row, source) || error( + "GlobalConstant meteo does not provide source variable `$(source)` for model-facing variable ", + "`$(target)` in `$(support.domain)/$(support.scale)/$(support.process)`." + ) + push!(pairs, target => getproperty(row, source)) + end + if !isnothing(row) && hasproperty(row, :duration) + push!(pairs, :duration => getproperty(row, :duration)) + end + return (; pairs...) +end + +function _environment_output_value(status, variable::Symbol, support::EnvironmentSupport) + hasproperty(status, variable) && return getproperty(status, variable) + error( + "Model `$(support.domain)/$(support.scale)/$(support.process)` declares environment output ", + "`$(variable)` in `meteo_outputs_`, but its status does not contain `$(variable)`. ", + "Expose the computed value as a same-named `outputs_` variable or initialize it in the status." + ) +end + +""" + scatter_environment_outputs!(backend, support, time, model_spec, status) + +Scatter values declared by `meteo_outputs_(model)` from model status into a +mutable environment backend. +""" +function scatter_environment_outputs!( + backend::AbstractEnvironmentBackend, + support::EnvironmentSupport, + time, + model_spec::ModelSpec, + status +) + for variable in keys(meteo_outputs_(model_spec)) + value = _environment_output_value(status, variable, support) + scatter!(backend, variable, support, value, time) + end + return nothing +end + +""" + explain_environment(simulation) + +Return a compact description of the environment backend used by a domain +simulation. +""" +function explain_environment(simulation) + backend = simulation.environment + return ( + backend=typeof(backend), + variables=environment_variables(backend), + nsteps=get_nsteps(backend), + base_step_seconds=base_step_seconds(backend), + ) +end diff --git a/src/time/runtime/meteo_sampling.jl b/src/time/runtime/meteo_sampling.jl index 8a80f62bf..83898e53b 100644 --- a/src/time/runtime/meteo_sampling.jl +++ b/src/time/runtime/meteo_sampling.jl @@ -76,6 +76,121 @@ function _normalize_meteo_binding_rule(target::Symbol, rule) ) end +function _raw_meteo_requirements_for_spec(model_spec) + required = Set{Symbol}(keys(meteo_inputs_(model_spec))) + isempty(required) && return required + + raw_required = Set{Symbol}() + bindings = meteo_bindings(model_spec) + bindings = bindings isa NamedTuple ? bindings : NamedTuple() + for var in required + if haskey(bindings, var) + rule = bindings[var] + if rule isa NamedTuple && haskey(rule, :source) + push!(raw_required, Symbol(rule.source)) + else + push!(raw_required, var) + end + else + push!(raw_required, var) + end + end + + return raw_required +end + +function _first_meteo_row(meteo) + isnothing(meteo) && return nothing + is_table = try + DataFormat(meteo) == TableAlike() + catch + false + end + if is_table + rows = Tables.rows(meteo) + state = iterate(rows) + isnothing(state) && return nothing + return state[1] + end + return meteo +end + +_meteo_has_field(row, var::Symbol) = hasproperty(row, var) +_meteo_has_field(row::NamedTuple, var::Symbol) = haskey(row, var) + +""" + validate_meteo_inputs(model_specs, meteo) + +Validate declared `meteo_inputs_` against the available meteorological fields. + +The check is intentionally field-based and independent from units/backends. When +`MeteoBindings` remap a declared model input from another source variable, the +source variable is checked on the raw meteo object. +""" +function validate_meteo_inputs(model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}, meteo) + row = _first_meteo_row(meteo) + isnothing(row) && return nothing + + missing_rows = NamedTuple[] + for (scale, specs_at_scale) in model_specs + for (process, spec) in specs_at_scale + required = _raw_meteo_requirements_for_spec(spec) + missing = Symbol[var for var in required if !_meteo_has_field(row, var)] + isempty(missing) && continue + push!(missing_rows, (scale=scale, process=process, missing=Tuple(missing))) + end + end + + isempty(missing_rows) && return nothing + + details = join( + [ + string(row.scale, "/", row.process, " missing ", row.missing) + for row in missing_rows + ], + "; " + ) + error( + "Meteorology is missing fields required by model `meteo_inputs_`: ", + details, + ". Add the fields to meteo, declare a `MeteoBindings(source=...)` remapping, ", + "or remove the unused meteo input from the model trait." + ) +end + +function validate_meteo_inputs(model_specs::AbstractDict{Symbol,<:AbstractDict}, meteo) + normalized_specs = Dict{Symbol,Dict{Symbol,ModelSpec}}() + for (scale, specs_at_scale) in pairs(model_specs) + normalized_specs[scale] = Dict{Symbol,ModelSpec}( + Symbol(process) => as_model_spec(spec) for (process, spec) in pairs(specs_at_scale) + ) + end + return validate_meteo_inputs(normalized_specs, meteo) +end + +function validate_meteo_inputs(models::NamedTuple, meteo) + specs = Dict( + :Default => Dict{Symbol,ModelSpec}( + process(model) => as_model_spec(model) for model in values(models) + ) + ) + return validate_meteo_inputs(specs, meteo) +end + +function validate_meteo_inputs(mapping::ModelMapping, meteo) + specs = Dict{Symbol,Dict{Symbol,ModelSpec}}( + scale => parse_model_specs(declarations) for (scale, declarations) in pairs(mapping) + ) + return validate_meteo_inputs(specs, meteo) +end + +function validate_meteo_inputs(mapping::AbstractDict, meteo) + specs = Dict{Symbol,Dict{Symbol,ModelSpec}}( + Symbol(scale) => parse_model_specs(declarations) for (scale, declarations) in pairs(mapping) + ) + return validate_meteo_inputs(specs, meteo) +end + function _meteo_transforms_for_model(model_spec) bindings = meteo_bindings(model_spec) isnothing(bindings) && return nothing diff --git a/src/time/runtime/output_export.jl b/src/time/runtime/output_export.jl index 1565a83db..55fd37d63 100644 --- a/src/time/runtime/output_export.jl +++ b/src/time/runtime/output_export.jl @@ -89,8 +89,8 @@ function _canonical_source_process(sim::GraphSimulation, scale::Symbol, var::Sym haskey(get_models(sim), scale) || error("Unknown scale `$(scale)` in output export request.") models_at_scale = get_models(sim)[scale] specs_at_scale = get_model_specs(sim)[scale] - ignored_same_rate_hard_children = _same_rate_hard_dependency_children(get_model_specs(sim), dep(sim)) - ignored_at_scale = get(ignored_same_rate_hard_children, scale, Set{Symbol}()) + ignored_hard_children = _hard_dependency_children(dep(sim)) + ignored_at_scale = get(ignored_hard_children, scale, Set{Symbol}()) publishers = Symbol[] for (process, model) in pairs(models_at_scale) diff --git a/src/time/runtime/publishers.jl b/src/time/runtime/publishers.jl index ff314ece2..cecf5b2ca 100644 --- a/src/time/runtime/publishers.jl +++ b/src/time/runtime/publishers.jl @@ -32,10 +32,10 @@ Ensure that each `(scale, variable)` has at most one canonical publisher. Throws when multiple producers publish the same canonical output. """ function validate_canonical_publishers(sim::GraphSimulation) - ignored_same_rate_hard_children = _same_rate_hard_dependency_children(get_model_specs(sim), dep(sim)) + ignored_hard_children = _hard_dependency_children(dep(sim)) for (scale, models_at_scale) in get_models(sim) specs_at_scale = get_model_specs(sim)[scale] - ignored_at_scale = get(ignored_same_rate_hard_children, scale, Set{Symbol}()) + ignored_at_scale = get(ignored_hard_children, scale, Set{Symbol}()) publishers = Dict{Symbol,Vector{Symbol}}() for (process, model) in pairs(models_at_scale) process in ignored_at_scale && continue @@ -52,10 +52,21 @@ function validate_canonical_publishers(sim::GraphSimulation) for (var, procs) in publishers if length(procs) > 1 + updater_flags = Dict( + process => (var in _update_variables_for_spec(get(specs_at_scale, process, as_model_spec(models_at_scale[process])))) + for process in procs + ) + primary_procs = [process for process in procs if !updater_flags[process]] + if length(primary_procs) == 1 + update_procs = [process for process in procs if updater_flags[process]] + primary = only(primary_procs) + all(process -> primary in _update_after_for_var(specs_at_scale[process], var), update_procs) && continue + end error( "Ambiguous canonical publishers for variable `$(var)` at scale `$(scale)`: ", join(procs, ", "), - ". Declare `OutputRouting(; $(var)=:stream_only)` for non-canonical producers." + ". Declare `OutputRouting(; $(var)=:stream_only)` for non-canonical producers, ", + "or `Updates(:$(var); after=:primary_process)` for intentional state updates." ) end end diff --git a/test/runtests.jl b/test/runtests.jl index 99cc88cdb..093e0c860 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -38,6 +38,26 @@ include("helper-functions.jl") include("test-multirate-output-export.jl") end + @testset "ModelSpec Updates" begin + include("test-updates.jl") + end + + @testset "Meteo traits" begin + include("test-meteo-traits.jl") + end + + @testset "Environment backends" begin + include("test-environment-backends.jl") + end + + @testset "Domain simulation" begin + include("test-domain-simulation.jl") + end + + @testset "MAESPA-style domain example" begin + include("test-maespa-domain-example.jl") + end + @testset "MultiScaleModel" begin include("test-MultiScaleModel.jl") end diff --git a/test/test-domain-simulation.jl b/test/test-domain-simulation.jl new file mode 100644 index 000000000..f04dff6d4 --- /dev/null +++ b/test/test-domain-simulation.jl @@ -0,0 +1,1420 @@ +using Dates + +PlantSimEngine.@process "domain_absorbed_radiation" verbose = false +PlantSimEngine.@process "domain_plant_transpiration" verbose = false +PlantSimEngine.@process "domain_soil_water" verbose = false +PlantSimEngine.@process "domain_soil_evaporation" verbose = false +PlantSimEngine.@process "domain_scene_evapotranspiration" verbose = false +PlantSimEngine.@process "domain_scene_plant_evapotranspiration" verbose = false +PlantSimEngine.@process "domain_hard_leaf_conductance" verbose = false +PlantSimEngine.@process "domain_hard_leaf_energy" verbose = false +PlantSimEngine.@process "domain_scene_conductance_sum" verbose = false +PlantSimEngine.@process "domain_hard_target_signal" verbose = false +PlantSimEngine.@process "domain_scene_hard_target_sum" verbose = false +PlantSimEngine.@process "domain_hard_target_leaf_counter" verbose = false +PlantSimEngine.@process "domain_scene_hard_target_leaf_sum" verbose = false +PlantSimEngine.@process "domain_scene_routed_vector" verbose = false +PlantSimEngine.@process "domain_scene_routed_aggregate" verbose = false +PlantSimEngine.@process "domain_mtg_leaf_flux" verbose = false +PlantSimEngine.@process "domain_scene_dependency_flux_sum" verbose = false +PlantSimEngine.@process "domain_mtg_leaf_soil_flux" verbose = false +PlantSimEngine.@process "domain_growth_leaf_emergence" verbose = false +PlantSimEngine.@process "domain_growth_leaf_flux" verbose = false +PlantSimEngine.@process "domain_growth_integrated_flux" verbose = false +PlantSimEngine.@process "domain_scene_growth_flux_sum" verbose = false +PlantSimEngine.@process "domain_update_allocation" verbose = false +PlantSimEngine.@process "domain_update_pruning" verbose = false +PlantSimEngine.@process "domain_update_observer" verbose = false +PlantSimEngine.@process "domain_removal_leaf_flux" verbose = false +PlantSimEngine.@process "domain_removal_pruning" verbose = false +PlantSimEngine.@process "domain_churn_leaf_flux" verbose = false +PlantSimEngine.@process "domain_churn_leaf_controller" verbose = false +PlantSimEngine.@process "domain_subtree_internode_flux" verbose = false +PlantSimEngine.@process "domain_subtree_leaf_flux" verbose = false +PlantSimEngine.@process "domain_subtree_pruning" verbose = false +PlantSimEngine.@process "domain_reparent_leaf_controller" verbose = false + +struct DomainAbsorbedRadiationModel <: AbstractDomain_Absorbed_RadiationModel + coefficient::Float64 +end + +PlantSimEngine.inputs_(::DomainAbsorbedRadiationModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainAbsorbedRadiationModel) = (absorbed_radiation=0.0,) +PlantSimEngine.meteo_inputs_(::DomainAbsorbedRadiationModel) = (Ri_PAR_f=0.0,) + +function PlantSimEngine.run!(model::DomainAbsorbedRadiationModel, models, status, meteo, constants=nothing, extra=nothing) + status.absorbed_radiation = model.coefficient * meteo.Ri_PAR_f + return nothing +end + +struct DomainPlantTranspirationModel <: AbstractDomain_Plant_TranspirationModel + coefficient::Float64 +end + +PlantSimEngine.inputs_(::DomainPlantTranspirationModel) = (absorbed_radiation=0.0,) +PlantSimEngine.outputs_(::DomainPlantTranspirationModel) = (transpiration=0.0,) + +function PlantSimEngine.run!(model::DomainPlantTranspirationModel, models, status, meteo, constants=nothing, extra=nothing) + status.transpiration = model.coefficient * status.absorbed_radiation + return nothing +end + +struct DomainSoilWaterModel <: AbstractDomain_Soil_WaterModel + baseline::Float64 +end + +PlantSimEngine.inputs_(::DomainSoilWaterModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainSoilWaterModel) = (soil_water_content=0.0,) +PlantSimEngine.meteo_inputs_(::DomainSoilWaterModel) = (T=0.0,) + +function PlantSimEngine.run!(model::DomainSoilWaterModel, models, status, meteo, constants=nothing, extra=nothing) + status.soil_water_content = model.baseline - 0.001 * meteo.T + return nothing +end + +struct DomainSoilEvaporationModel <: AbstractDomain_Soil_EvaporationModel + coefficient::Float64 +end + +PlantSimEngine.inputs_(::DomainSoilEvaporationModel) = (soil_water_content=0.0,) +PlantSimEngine.outputs_(::DomainSoilEvaporationModel) = (evaporation=0.0,) +PlantSimEngine.meteo_inputs_(::DomainSoilEvaporationModel) = (T=0.0,) + +function PlantSimEngine.run!(model::DomainSoilEvaporationModel, models, status, meteo, constants=nothing, extra=nothing) + status.evaporation = model.coefficient * status.soil_water_content * meteo.T + return nothing +end + +struct DomainSceneEvapotranspirationModel <: AbstractDomain_Scene_EvapotranspirationModel +end + +PlantSimEngine.inputs_(::DomainSceneEvapotranspirationModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainSceneEvapotranspirationModel) = (evapotranspiration=0.0,) + +PlantSimEngine.dep(::DomainSceneEvapotranspirationModel) = ( + plant_transpiration=AllDomains(kind=:plant, process=:domain_plant_transpiration, policy=Integrate()), + soil_evaporation=AllDomains(kind=:soil, process=:domain_soil_evaporation, policy=Integrate()), +) + +function PlantSimEngine.run!(::DomainSceneEvapotranspirationModel, models, status, meteo, constants=nothing, extra=nothing) + plant_values = dependency_values(extra, :plant_transpiration, :transpiration) + soil_values = dependency_values(extra, :soil_evaporation, :evaporation) + status.evapotranspiration = sum(filter(x -> !isnothing(x), plant_values)) + sum(filter(x -> !isnothing(x), soil_values)) + return nothing +end + +struct DomainScenePlantEvapotranspirationModel <: AbstractDomain_Scene_Plant_EvapotranspirationModel +end + +PlantSimEngine.inputs_(::DomainScenePlantEvapotranspirationModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainScenePlantEvapotranspirationModel) = (plant_evapotranspiration=0.0,) + +PlantSimEngine.dep(::DomainScenePlantEvapotranspirationModel) = ( + plant_transpiration=AllDomains(kind=:plant, process=:domain_plant_transpiration, policy=Integrate()), +) + +function PlantSimEngine.run!(::DomainScenePlantEvapotranspirationModel, models, status, meteo, constants=nothing, extra=nothing) + plant_values = dependency_values(extra, :plant_transpiration, :transpiration) + status.plant_evapotranspiration = sum(filter(x -> !isnothing(x), plant_values)) + return nothing +end + +struct DomainHardLeafConductanceModel <: AbstractDomain_Hard_Leaf_ConductanceModel +end + +PlantSimEngine.inputs_(::DomainHardLeafConductanceModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainHardLeafConductanceModel) = (conductance=0.0,) + +function PlantSimEngine.run!(::DomainHardLeafConductanceModel, models, status, meteo, constants=nothing, extra=nothing) + status.conductance = 2.0 + return nothing +end + +struct DomainHardLeafEnergyModel <: AbstractDomain_Hard_Leaf_EnergyModel +end + +PlantSimEngine.dep(::DomainHardLeafEnergyModel) = (domain_hard_leaf_conductance=AbstractDomain_Hard_Leaf_ConductanceModel,) +PlantSimEngine.inputs_(::DomainHardLeafEnergyModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainHardLeafEnergyModel) = (leaf_temperature=0.0,) + +function PlantSimEngine.run!(::DomainHardLeafEnergyModel, models, status, meteo, constants=nothing, extra=nothing) + run_target!(models, status, :domain_hard_leaf_conductance; meteo=meteo, constants=constants, extra=extra) + status.leaf_temperature = 20.0 + status.conductance + return nothing +end + +struct DomainSceneConductanceSumModel <: AbstractDomain_Scene_Conductance_SumModel +end + +PlantSimEngine.inputs_(::DomainSceneConductanceSumModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainSceneConductanceSumModel) = (conductance_sum=0.0,) + +PlantSimEngine.dep(::DomainSceneConductanceSumModel) = ( + conductance=AllDomains(kind=:plant, process=:domain_hard_leaf_conductance, var=:conductance, policy=Integrate()), +) + +function PlantSimEngine.run!(::DomainSceneConductanceSumModel, models, status, meteo, constants=nothing, extra=nothing) + conductance_values = dependency_values(extra, :conductance) + status.conductance_sum = sum(filter(x -> !isnothing(x), conductance_values)) + return nothing +end + +struct DomainHardTargetSignalModel{T} <: AbstractDomain_Hard_Target_SignalModel + coefficient::T +end + +PlantSimEngine.inputs_(::DomainHardTargetSignalModel) = (call_count=0,) +PlantSimEngine.outputs_(::DomainHardTargetSignalModel) = (call_count=0, signal=0.0,) + +function PlantSimEngine.run!(model::DomainHardTargetSignalModel, models, status, meteo, constants=nothing, extra=nothing) + status.call_count += 1 + status.signal = model.coefficient * status.call_count + return nothing +end + +struct DomainSceneHardTargetSumModel <: AbstractDomain_Scene_Hard_Target_SumModel end + +PlantSimEngine.inputs_(::DomainSceneHardTargetSumModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainSceneHardTargetSumModel) = (hard_target_total=0.0,) + +PlantSimEngine.dep(::DomainSceneHardTargetSumModel) = ( + plant_signal=HardDomains(kind=:plant, process=:domain_hard_target_signal), +) + +function PlantSimEngine.run!(::DomainSceneHardTargetSumModel, models, status, meteo, constants=nothing, extra=nothing) + targets = dependency_targets(extra, :plant_signal) + for target in targets + run_target!(target) + run_target!(target) + end + status.hard_target_total = sum(target.status.signal for target in targets) + return nothing +end + +struct DomainHardTargetLeafCounterModel{T} <: AbstractDomain_Hard_Target_Leaf_CounterModel + coefficient::T +end + +PlantSimEngine.inputs_(::DomainHardTargetLeafCounterModel) = (call_count=0,) +PlantSimEngine.outputs_(::DomainHardTargetLeafCounterModel) = (call_count=0, leaf_signal=0.0,) + +function PlantSimEngine.run!(model::DomainHardTargetLeafCounterModel, models, status, meteo, constants=nothing, extra=nothing) + status.call_count += 1 + status.leaf_signal = model.coefficient * status.call_count + return nothing +end + +struct DomainSceneHardTargetLeafSumModel <: AbstractDomain_Scene_Hard_Target_Leaf_SumModel end + +PlantSimEngine.inputs_(::DomainSceneHardTargetLeafSumModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainSceneHardTargetLeafSumModel) = (leaf_hard_target_total=0.0,) + +PlantSimEngine.dep(::DomainSceneHardTargetLeafSumModel) = ( + leaf_calls=HardDomains(kind=:plant, scale=:Leaf, process=:domain_hard_target_leaf_counter), +) + +function PlantSimEngine.run!(::DomainSceneHardTargetLeafSumModel, models, status, meteo, constants=nothing, extra=nothing) + targets = dependency_targets(extra, :leaf_calls) + for target in targets + run_target!(target; publish=true) + end + status.leaf_hard_target_total = sum(target.status.leaf_signal for target in targets) + return nothing +end + +struct DomainSceneRoutedVectorModel <: AbstractDomain_Scene_Routed_VectorModel end + +PlantSimEngine.inputs_(::DomainSceneRoutedVectorModel) = (plant_transpirations=Float64[],) +PlantSimEngine.outputs_(::DomainSceneRoutedVectorModel) = (routed_total=0.0,) + +function PlantSimEngine.run!(::DomainSceneRoutedVectorModel, models, status, meteo, constants=nothing, extra=nothing) + status.routed_total = sum(status.plant_transpirations) + return nothing +end + +struct DomainSceneRoutedAggregateModel <: AbstractDomain_Scene_Routed_AggregateModel end + +PlantSimEngine.inputs_(::DomainSceneRoutedAggregateModel) = (daily_plant_transpiration=0.0,) +PlantSimEngine.outputs_(::DomainSceneRoutedAggregateModel) = (daily_routed_total=0.0,) + +function PlantSimEngine.run!(::DomainSceneRoutedAggregateModel, models, status, meteo, constants=nothing, extra=nothing) + status.daily_routed_total = status.daily_plant_transpiration + return nothing +end + +struct DomainMTGLeafFluxModel{T} <: AbstractDomain_Mtg_Leaf_FluxModel + coefficient::T +end + +PlantSimEngine.inputs_(::DomainMTGLeafFluxModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainMTGLeafFluxModel) = (leaf_flux=0.0,) + +function PlantSimEngine.run!(model::DomainMTGLeafFluxModel, models, status, meteo, constants=nothing, extra=nothing) + status.leaf_flux = model.coefficient + return nothing +end + +struct DomainMTGLeafSoilFluxModel{T} <: AbstractDomain_Mtg_Leaf_Soil_FluxModel + coefficient::T +end + +PlantSimEngine.inputs_(::DomainMTGLeafSoilFluxModel) = (soil_signal=0.0,) +PlantSimEngine.outputs_(::DomainMTGLeafSoilFluxModel) = (leaf_flux=0.0,) + +function PlantSimEngine.run!(model::DomainMTGLeafSoilFluxModel, models, status, meteo, constants=nothing, extra=nothing) + status.leaf_flux = model.coefficient * status.soil_signal + return nothing +end + +struct DomainGrowthLeafEmergenceModel <: AbstractDomain_Growth_Leaf_EmergenceModel end + +PlantSimEngine.inputs_(::DomainGrowthLeafEmergenceModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainGrowthLeafEmergenceModel) = (grown_leaves=0.0,) + +function PlantSimEngine.run!(::DomainGrowthLeafEmergenceModel, models, status, meteo, constants=nothing, extra=nothing) + if length(PlantSimEngine.status(extra)[:Leaf]) == 0 + add_organ!(status.node, extra, "+", :Leaf, 2; check=true) + end + status.grown_leaves = length(PlantSimEngine.status(extra)[:Leaf]) + return nothing +end + +struct DomainGrowthLeafFluxModel{T} <: AbstractDomain_Growth_Leaf_FluxModel + coefficient::T +end + +PlantSimEngine.inputs_(::DomainGrowthLeafFluxModel) = (grown_leaves=0.0,) +PlantSimEngine.outputs_(::DomainGrowthLeafFluxModel) = (leaf_flux=0.0,) + +function PlantSimEngine.run!(model::DomainGrowthLeafFluxModel, models, status, meteo, constants=nothing, extra=nothing) + status.leaf_flux = model.coefficient * status.grown_leaves + return nothing +end + +struct DomainGrowthIntegratedFluxModel <: AbstractDomain_Growth_Integrated_FluxModel end + +PlantSimEngine.inputs_(::DomainGrowthIntegratedFluxModel) = (leaf_flux=-Inf,) +PlantSimEngine.outputs_(::DomainGrowthIntegratedFluxModel) = (integrated_leaf_flux=0.0,) + +function PlantSimEngine.run!(::DomainGrowthIntegratedFluxModel, models, status, meteo, constants=nothing, extra=nothing) + status.integrated_leaf_flux = sum(status.leaf_flux) + return nothing +end + +struct DomainSceneDependencyFluxSumModel <: AbstractDomain_Scene_Dependency_Flux_SumModel end + +PlantSimEngine.inputs_(::DomainSceneDependencyFluxSumModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainSceneDependencyFluxSumModel) = (dependency_total=0.0, grouped_dependency_total=0.0,) + +PlantSimEngine.dep(::DomainSceneDependencyFluxSumModel) = ( + leaf_fluxes=AllDomains(kind=:plant, scale=:Leaf, process=:domain_mtg_leaf_flux, var=:leaf_flux), +) + +function PlantSimEngine.run!(::DomainSceneDependencyFluxSumModel, models, status, meteo, constants=nothing, extra=nothing) + grouped_values = dependency_values(extra, :leaf_fluxes) + flattened_values = dependency_values(extra, :leaf_fluxes; flatten=true) + status.grouped_dependency_total = sum(sum, grouped_values) + status.dependency_total = sum(flattened_values) + return nothing +end + +struct DomainSceneGrowthFluxSumModel <: AbstractDomain_Scene_Growth_Flux_SumModel end + +PlantSimEngine.inputs_(::DomainSceneGrowthFluxSumModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainSceneGrowthFluxSumModel) = (growth_flux_total=0.0,) + +PlantSimEngine.dep(::DomainSceneGrowthFluxSumModel) = ( + leaf_fluxes=AllDomains(kind=:plant, scale=:Leaf, process=:domain_growth_leaf_flux, var=:leaf_flux), +) + +function PlantSimEngine.run!(::DomainSceneGrowthFluxSumModel, models, status, meteo, constants=nothing, extra=nothing) + status.growth_flux_total = sum(dependency_values(extra, :leaf_fluxes; flatten=true)) + return nothing +end + +struct DomainUpdateAllocationModel <: AbstractDomain_Update_AllocationModel end + +PlantSimEngine.inputs_(::DomainUpdateAllocationModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainUpdateAllocationModel) = (leaf_biomass=0.0,) + +function PlantSimEngine.run!(::DomainUpdateAllocationModel, models, status, meteo, constants=nothing, extra=nothing) + status.leaf_biomass = 10.0 + return nothing +end + +struct DomainUpdatePruningModel <: AbstractDomain_Update_PruningModel end + +PlantSimEngine.inputs_(::DomainUpdatePruningModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainUpdatePruningModel) = (leaf_biomass=0.0,) + +function PlantSimEngine.run!(::DomainUpdatePruningModel, models, status, meteo, constants=nothing, extra=nothing) + status.leaf_biomass = 0.0 + return nothing +end + +struct DomainUpdateObserverModel <: AbstractDomain_Update_ObserverModel end + +PlantSimEngine.inputs_(::DomainUpdateObserverModel) = (leaf_biomass=0.0,) +PlantSimEngine.outputs_(::DomainUpdateObserverModel) = (observed_biomass=0.0,) + +function PlantSimEngine.run!(::DomainUpdateObserverModel, models, status, meteo, constants=nothing, extra=nothing) + status.observed_biomass = status.leaf_biomass + return nothing +end + +struct DomainRemovalLeafFluxModel <: AbstractDomain_Removal_Leaf_FluxModel end + +PlantSimEngine.inputs_(::DomainRemovalLeafFluxModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainRemovalLeafFluxModel) = (leaf_flux=0.0,) + +function PlantSimEngine.run!(::DomainRemovalLeafFluxModel, models, status, meteo, constants=nothing, extra=nothing) + status.leaf_flux = 1.0 + return nothing +end + +struct DomainRemovalPruningModel <: AbstractDomain_Removal_PruningModel end + +PlantSimEngine.inputs_(::DomainRemovalPruningModel) = (leaf_flux=Float64[], removed_count=0, removed_node_id=0,) +PlantSimEngine.outputs_(::DomainRemovalPruningModel) = (remaining_leaf_flux=0.0, removed_count=0, removed_node_id=0,) + +function PlantSimEngine.run!(::DomainRemovalPruningModel, models, status, meteo, constants=nothing, extra=nothing) + if status.removed_count == 0 && length(status.leaf_flux) > 1 + leaf_status = first(PlantSimEngine.status(extra)[:Leaf]) + status.removed_node_id = node_id(leaf_status.node) + remove_organ!(leaf_status.node, extra) + status.removed_count = 1 + end + status.remaining_leaf_flux = sum(status.leaf_flux) + return nothing +end + +struct DomainChurnLeafFluxModel <: AbstractDomain_Churn_Leaf_FluxModel end + +PlantSimEngine.inputs_(::DomainChurnLeafFluxModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainChurnLeafFluxModel) = (leaf_flux=0.0,) + +function PlantSimEngine.run!(::DomainChurnLeafFluxModel, models, status, meteo, constants=nothing, extra=nothing) + status.leaf_flux = 1.0 + return nothing +end + +struct DomainChurnLeafControllerModel <: AbstractDomain_Churn_Leaf_ControllerModel end + +PlantSimEngine.inputs_(::DomainChurnLeafControllerModel) = ( + leaf_flux=Float64[], + created_count=0, + removed_count=0, + active_leaf_count=0, + last_removed_node_id=0, +) +PlantSimEngine.outputs_(::DomainChurnLeafControllerModel) = ( + created_count=0, + removed_count=0, + active_leaf_count=0, + last_removed_node_id=0, +) + +function PlantSimEngine.run!(::DomainChurnLeafControllerModel, models, status, meteo, constants=nothing, extra=nothing) + if isempty(status.leaf_flux) + add_organ!(status.node, extra, "+", :Leaf, 2; check=true) + status.created_count += 1 + else + leaf_status = first(PlantSimEngine.status(extra)[:Leaf]) + status.last_removed_node_id = node_id(leaf_status.node) + remove_organ!(leaf_status.node, extra) + status.removed_count += 1 + end + status.active_leaf_count = length(status.leaf_flux) + return nothing +end + +struct DomainSubtreeInternodeFluxModel <: AbstractDomain_Subtree_Internode_FluxModel end +struct DomainSubtreeLeafFluxModel <: AbstractDomain_Subtree_Leaf_FluxModel end + +PlantSimEngine.inputs_(::DomainSubtreeInternodeFluxModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainSubtreeInternodeFluxModel) = (internode_flux=0.0,) + +function PlantSimEngine.run!(::DomainSubtreeInternodeFluxModel, models, status, meteo, constants=nothing, extra=nothing) + status.internode_flux = 2.0 + return nothing +end + +PlantSimEngine.inputs_(::DomainSubtreeLeafFluxModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainSubtreeLeafFluxModel) = (leaf_flux=0.0,) + +function PlantSimEngine.run!(::DomainSubtreeLeafFluxModel, models, status, meteo, constants=nothing, extra=nothing) + status.leaf_flux = 1.0 + return nothing +end + +struct DomainSubtreePruningModel <: AbstractDomain_Subtree_PruningModel end + +PlantSimEngine.inputs_(::DomainSubtreePruningModel) = ( + internode_flux=Float64[], + leaf_flux=Float64[], + removed_count=0, + removed_internode_id=0, + removed_leaf_id=0, + remaining_internode_count=0, + remaining_leaf_count=0, +) +PlantSimEngine.outputs_(::DomainSubtreePruningModel) = ( + removed_count=0, + removed_internode_id=0, + removed_leaf_id=0, + remaining_internode_count=0, + remaining_leaf_count=0, +) + +function PlantSimEngine.run!(::DomainSubtreePruningModel, models, status, meteo, constants=nothing, extra=nothing) + graph_status = PlantSimEngine.status(extra) + if status.removed_count == 0 && !isempty(get(graph_status, :Internode, Status[])) + internode_status = first(graph_status[:Internode]) + leaf_status = first(graph_status[:Leaf]) + status.removed_internode_id = node_id(internode_status.node) + status.removed_leaf_id = node_id(leaf_status.node) + remove_organ!(internode_status.node, extra; recursive=true) + status.removed_count = 1 + end + status.remaining_internode_count = length(status.internode_flux) + status.remaining_leaf_count = length(status.leaf_flux) + return nothing +end + +struct DomainReparentLeafControllerModel <: AbstractDomain_Reparent_Leaf_ControllerModel end + +PlantSimEngine.inputs_(::DomainReparentLeafControllerModel) = ( + leaf_flux=Float64[], + reparented_count=0, + new_parent_id=0, + leaf_parent_id=0, + active_leaf_count=0, +) +PlantSimEngine.outputs_(::DomainReparentLeafControllerModel) = ( + reparented_count=0, + new_parent_id=0, + leaf_parent_id=0, + active_leaf_count=0, +) + +function PlantSimEngine.run!(::DomainReparentLeafControllerModel, models, status, meteo, constants=nothing, extra=nothing) + graph_status = PlantSimEngine.status(extra) + if status.reparented_count == 0 + leaf_status = only(graph_status[:Leaf]) + new_parent_status = graph_status[:Internode][2] + reparent_organ!(leaf_status.node, new_parent_status.node, extra) + status.reparented_count = 1 + status.new_parent_id = node_id(new_parent_status.node) + end + leaf_status = only(graph_status[:Leaf]) + status.leaf_parent_id = node_id(parent(leaf_status.node)) + status.active_leaf_count = length(status.leaf_flux) + return nothing +end + +@testset "Domain simulation: two plants, soil, and daily scene aggregation" begin + hourly_meteo = Weather([ + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) + for _ in 1:25 + ]) + + oil_palm_mapping = ModelMapping( + ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStepModel(Dates.Hour(1)), + status=(absorbed_radiation=0.0, transpiration=0.0), + ) + + maize_mapping = ModelMapping( + ModelSpec(DomainAbsorbedRadiationModel(0.3)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainPlantTranspirationModel(0.02)) |> TimeStepModel(Dates.Hour(1)), + status=(absorbed_radiation=0.0, transpiration=0.0), + ) + + soil_mapping = ModelMapping( + ModelSpec(DomainSoilWaterModel(0.35)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainSoilEvaporationModel(0.2)) |> TimeStepModel(Dates.Hour(1)), + status=(soil_water_content=0.0, evaporation=0.0), + ) + + scene_mapping = ModelMapping( + ModelSpec(DomainSceneEvapotranspirationModel()) |> TimeStepModel(Dates.Day(1)), + status=(evapotranspiration=0.0,), + ) + + simulation_mapping = SimulationMapping( + Domain(:oil_palm, oil_palm_mapping; kind=:plant), + Domain(:maize, maize_mapping; kind=:plant), + Domain(:soil, soil_mapping; kind=:soil), + Domain(:scene, scene_mapping; kind=:scene), + ) + + domain_rows = explain_domains(simulation_mapping) + @test length(domain_rows) == 4 + @test any(row -> row.domain == :oil_palm && row.kind == :plant, domain_rows) + + model_rows = explain_domain_models(simulation_mapping) + @test length(model_rows) == 7 + @test any(row -> row.domain == :oil_palm && row.process == :domain_absorbed_radiation && haskey(row.meteo_inputs, :Ri_PAR_f), model_rows) + + sim = run!(simulation_mapping, hourly_meteo, check=true) + @test status(sim, :oil_palm).transpiration ≈ 0.01 * 0.5 * 100.0 + @test outputs(sim) === sim.outputs + + schedule = explain_schedule(sim) + @test any(row -> row.domain == :scene && row.dt_seconds == 86_400.0, schedule) + @test any(row -> row.domain == :oil_palm && row.dt_seconds == 3_600.0, schedule) + + deps = explain_domain_dependencies(sim) + @test length(deps) == 3 + @test count(row -> row.dependency == :plant_transpiration, deps) == 2 + @test count(row -> row.dependency == :soil_evaporation, deps) == 1 + @test all(row -> isnothing(row.variable), deps) + + scene_key = DomainModelKey(:scene, :Default, :domain_scene_evapotranspiration) + scene_values = sim.outputs[(scene_key, :evapotranspiration)] + + # Dates.Day(1) currently aligns to step 1, then step 25 when the base step is hourly. + # The second scene value integrates producer values from steps 2:25. + hourly_plant_sum = 0.01 * 0.5 * 100.0 + 0.02 * 0.3 * 100.0 + hourly_soil = 0.2 * (0.35 - 0.001 * 20.0) * 20.0 + expected_daily_et = 24.0 * (hourly_plant_sum + hourly_soil) + + @test length(scene_values) == 2 + @test scene_values[2] ≈ expected_daily_et +end + +@testset "Domain simulation validation" begin + hourly_meteo = Weather([ + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) + for _ in 1:2 + ]) + + plant_mapping = ModelMapping( + ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStepModel(Dates.Hour(1)), + status=(absorbed_radiation=0.0, transpiration=0.0), + ) + + raw_domain = Domain( + :plant_kw; + kind=:plant, + mapping=( + ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStepModel(Dates.Hour(1)), + Status(absorbed_radiation=0.0, transpiration=0.0), + ), + ) + @test raw_domain.mapping isa ModelMapping + + @test_throws ErrorException SimulationMapping( + Domain(:plant, plant_mapping; kind=:plant), + Domain(:plant, plant_mapping; kind=:plant), + ) + + daily_meteo = Weather([ + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) + for _ in 1:25 + ]) + mixed_rate_mapping = ModelMapping( + ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStepModel(Dates.Day(1)), + status=(absorbed_radiation=0.0, transpiration=0.0), + ) + mixed_sim = run!( + SimulationMapping(Domain(:mixed_plant, mixed_rate_mapping; kind=:plant)), + daily_meteo, + check=true, + ) + absorbed_key = DomainModelKey(:mixed_plant, :Default, :domain_absorbed_radiation) + transpiration_key = DomainModelKey(:mixed_plant, :Default, :domain_plant_transpiration) + @test length(mixed_sim.outputs[(absorbed_key, :absorbed_radiation)]) == 25 + @test length(mixed_sim.outputs[(transpiration_key, :transpiration)]) == 2 + + unmatched_scene_mapping = ModelMapping( + ModelSpec(DomainSceneEvapotranspirationModel()) |> TimeStepModel(Dates.Day(1)), + status=(evapotranspiration=0.0,), + ) + unmatched_error = try + run!( + SimulationMapping(Domain(:scene, unmatched_scene_mapping; kind=:scene)), + hourly_meteo, + check=true, + ) + "" + catch err + sprint(showerror, err) + end + @test occursin("Domain dependency `plant_transpiration`", unmatched_error) + @test occursin("consumer `scene/Default/domain_scene_evapotranspiration`", unmatched_error) + @test occursin("AllDomains(kind=:plant, process=:domain_plant_transpiration, policy=Integrate())", unmatched_error) + @test occursin("Available producers:", unmatched_error) + + multi_process_scene_mapping = ModelMapping( + ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainScenePlantEvapotranspirationModel()) |> TimeStepModel(Dates.Hour(1)), + status=(absorbed_radiation=0.0, plant_evapotranspiration=0.0), + ) + multi_scene_sim = run!( + SimulationMapping( + Domain(:plant, plant_mapping; kind=:plant), + Domain(:scene, multi_process_scene_mapping; kind=:scene), + ), + hourly_meteo, + check=true, + ) + @test status(multi_scene_sim, :scene).plant_evapotranspiration > 0.0 + + hard_plant_mapping = ModelMapping( + ModelSpec(DomainHardLeafConductanceModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainHardLeafEnergyModel()) |> TimeStepModel(Dates.Hour(1)), + status=(conductance=0.0, leaf_temperature=0.0), + ) + conductance_scene_mapping = ModelMapping( + ModelSpec(DomainSceneConductanceSumModel()) |> TimeStepModel(Dates.Hour(1)), + status=(conductance_sum=0.0,), + ) + hard_sim = run!( + SimulationMapping( + Domain(:hard_plant, hard_plant_mapping; kind=:plant), + Domain(:scene, conductance_scene_mapping; kind=:scene), + ), + hourly_meteo, + check=true, + ) + conductance_key = DomainModelKey(:hard_plant, :Default, :domain_hard_leaf_conductance) + @test hard_sim.outputs[(conductance_key, :conductance)] == [2.0, 2.0] + @test status(hard_sim, :scene).conductance_sum == 2.0 + hard_deps = explain_domain_dependencies(hard_sim) + @test only(hard_deps).variable == :conductance + + hard_target_plant_mapping = ModelMapping( + ModelSpec(DomainHardTargetSignalModel(2.0)) |> TimeStepModel(Dates.Hour(1)), + status=(call_count=0, signal=0.0), + ) + hard_target_scene_mapping = ModelMapping( + ModelSpec(DomainSceneHardTargetSumModel()) |> TimeStepModel(Dates.Hour(1)), + status=(hard_target_total=0.0,), + ) + hard_target_sim = run!( + SimulationMapping( + Domain(:hard_target_plant, hard_target_plant_mapping; kind=:plant), + Domain(:scene, hard_target_scene_mapping; kind=:scene), + ), + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)), + check=true, + ) + @test status(hard_target_sim, :hard_target_plant).call_count == 2 + @test status(hard_target_sim, :hard_target_plant).signal ≈ 4.0 + @test status(hard_target_sim, :scene).hard_target_total ≈ 4.0 + @test only(explain_domain_dependencies(hard_target_sim)).mode == :hard_domain + + route_source = AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration) + bad_route_source = AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:missing_output) + route_selector_error = try + run!( + SimulationMapping( + Domain(:plant, plant_mapping; kind=:plant), + Domain(:scene, multi_process_scene_mapping; kind=:scene); + routes=(Route( + from=bad_route_source, + to=DomainRouteTarget(:scene, var=:plant_transpirations), + ),), + ), + hourly_meteo, + check=true, + ) + "" + catch err + sprint(showerror, err) + end + @test occursin("Route 1 from `AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:missing_output)`", route_selector_error) + @test occursin("Models matching all selector fields except `var=:missing_output`", route_selector_error) + @test occursin("plant/Default/domain_plant_transpiration outputs=(:transpiration)", route_selector_error) + + missing_target_scene_mapping = ModelMapping( + ModelSpec(DomainSceneRoutedAggregateModel()) |> TimeStepModel(Dates.Hour(1)), + status=(daily_plant_transpiration=0.0, daily_routed_total=0.0), + ) + @test_throws "does not contain variable `plant_transpirations`" run!( + SimulationMapping( + Domain(:plant, plant_mapping; kind=:plant), + Domain(:scene, missing_target_scene_mapping; kind=:scene); + routes=(Route( + from=route_source, + to=DomainRouteTarget(:scene, var=:plant_transpirations), + ),), + ), + hourly_meteo, + check=true, + ) + + wrong_process_scene_mapping = ModelMapping( + ModelSpec(DomainSceneRoutedAggregateModel()) |> TimeStepModel(Dates.Hour(1)), + status=(plant_transpirations=[0.0], daily_plant_transpiration=0.0, daily_routed_total=0.0), + ) + @test_throws "does not consume variable `plant_transpirations`" run!( + SimulationMapping( + Domain(:plant, plant_mapping; kind=:plant), + Domain(:scene, wrong_process_scene_mapping; kind=:scene); + routes=(Route( + from=route_source, + to=DomainRouteTarget(:scene, var=:plant_transpirations, process=:domain_scene_routed_aggregate), + ),), + ), + hourly_meteo, + check=true, + ) + + @test_throws "has a selector but uses a single-status ModelMapping" run!( + Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)), + SimulationMapping(Domain(:plant, plant_mapping; kind=:plant, selector=:Plant)), + hourly_meteo, + check=true, + ) +end + +@testset "Domain simulation routes" begin + hourly_meteo = Weather([ + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) + for _ in 1:25 + ]) + + oil_palm_mapping = ModelMapping( + ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStepModel(Dates.Hour(1)), + status=(absorbed_radiation=0.0, transpiration=0.0), + ) + + maize_mapping = ModelMapping( + ModelSpec(DomainAbsorbedRadiationModel(0.3)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainPlantTranspirationModel(0.02)) |> TimeStepModel(Dates.Hour(1)), + status=(absorbed_radiation=0.0, transpiration=0.0), + ) + + hourly_scene_mapping = ModelMapping( + ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStepModel(Dates.Hour(1)), + status=(plant_transpirations=[0.0], routed_total=0.0), + ) + + vector_route = Route( + from=AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration), + to=DomainRouteTarget(:scene, var=:plant_transpirations, process=:domain_scene_routed_vector), + cardinality=ManyToOneVector(), + ) + + vector_sim = run!( + SimulationMapping( + Domain(:oil_palm, oil_palm_mapping; kind=:plant), + Domain(:maize, maize_mapping; kind=:plant), + Domain(:scene, hourly_scene_mapping; kind=:scene); + routes=(vector_route,), + ), + hourly_meteo, + check=true, + ) + hourly_plant_sum = 0.01 * 0.5 * 100.0 + 0.02 * 0.3 * 100.0 + @test status(vector_sim, :scene).plant_transpirations ≈ [0.5, 0.6] + @test status(vector_sim, :scene).routed_total ≈ hourly_plant_sum + + route_rows = explain_routes(vector_sim) + @test length(route_rows) == 2 + @test all(row -> row.target_var == :plant_transpirations, route_rows) + @test all(row -> row.cardinality == ManyToOneVector, route_rows) + + daily_scene_mapping = ModelMapping( + ModelSpec(DomainSceneRoutedAggregateModel()) |> TimeStepModel(Dates.Day(1)), + status=(daily_plant_transpiration=0.0, daily_routed_total=0.0), + ) + + aggregate_route = Route( + from=AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration), + to=DomainRouteTarget(:scene, var=:daily_plant_transpiration, process=:domain_scene_routed_aggregate), + cardinality=ManyToOneAggregate(sum), + policy=Integrate(), + ) + + aggregate_sim = run!( + SimulationMapping( + Domain(:oil_palm, oil_palm_mapping; kind=:plant), + Domain(:maize, maize_mapping; kind=:plant), + Domain(:scene, daily_scene_mapping; kind=:scene); + routes=(aggregate_route,), + ), + hourly_meteo, + check=true, + ) + @test status(aggregate_sim, :scene).daily_plant_transpiration ≈ 24.0 * hourly_plant_sum + @test status(aggregate_sim, :scene).daily_routed_total ≈ 24.0 * hourly_plant_sum + + aggregate_rows = explain_routes(aggregate_sim) + @test length(aggregate_rows) == 2 + @test all(row -> row.dt_seconds == 86_400.0, aggregate_rows) + @test all(row -> row.cardinality <: ManyToOneAggregate, aggregate_rows) +end + +@testset "Domain graph dependency policy with changing topology" begin + first_step = PlantSimEngine.DomainNodeValues([11, 12], Any[1.0, 2.0]) + second_step = PlantSimEngine.DomainNodeValues([11, 13], Any[3.0, 4.0]) + + integrated = PlantSimEngine._apply_dependency_policy(Any[first_step, second_step], Integrate()) + @test integrated.ids == [11, 12, 13] + @test integrated.values ≈ [4.0, 2.0, 4.0] + + aggregated = PlantSimEngine._apply_dependency_policy(Any[first_step, second_step], Aggregate()) + @test aggregated.ids == [11, 12, 13] + @test aggregated.values ≈ [2.0, 2.0, 4.0] + + no_ids_first_step = PlantSimEngine.DomainNodeValues(Any[1.0, 2.0]) + no_ids_second_step = PlantSimEngine.DomainNodeValues(Any[3.0]) + @test_throws "changing vector lengths" PlantSimEngine._apply_dependency_policy( + Any[no_ids_first_step, no_ids_second_step], + Integrate(), + ) +end + +@testset "Staged MTG-backed domain simulation" begin + scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + oil_palm = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + oil_axis = Node(oil_palm, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) + Node(oil_axis, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 3)) + maize = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 2, 1)) + maize_axis = Node(maize, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) + Node(maize_axis, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 3)) + + meteo = Weather([ + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)), + Atmosphere(T=25.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)), + ]) + + oil_leaf_mapping = ModelMapping( + :Leaf => ( + ModelSpec(DomainMTGLeafFluxModel(0.5)) |> TimeStepModel(Dates.Hour(1)), + ), + ) + maize_leaf_mapping = ModelMapping( + :Leaf => ( + ModelSpec(DomainMTGLeafFluxModel(0.7)) |> TimeStepModel(Dates.Hour(1)), + ), + ) + scene_mapping = ModelMapping( + ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStepModel(Dates.Hour(1)), + status=(plant_transpirations=[0.0], routed_total=0.0), + ) + route = Route( + from=AllDomains(kind=:plant, scale=:Leaf, process=:domain_mtg_leaf_flux, var=:leaf_flux), + to=DomainRouteTarget(:scene, var=:plant_transpirations, process=:domain_scene_routed_vector), + cardinality=ManyToOneVector(), + ) + + sim = run!( + scene, + SimulationMapping( + Domain(:oil_palm, oil_leaf_mapping; kind=:plant, selector=oil_palm), + Domain(:maize, maize_leaf_mapping; kind=:plant, selector=maize), + Domain(:scene, scene_mapping; kind=:scene); + routes=(route,), + ), + meteo, + check=true, + ) + + @test length(status(sim, :oil_palm, :Leaf)) == 1 + @test length(status(sim, :maize, :Leaf)) == 1 + @test length(status(sim, :Leaf)) == 2 + @test length(status(sim, :Default)) == 1 + @test status(sim, :scene).plant_transpirations ≈ [0.5, 0.7] + @test status(sim, :scene).routed_total ≈ 1.2 + @test sim.outputs[(DomainModelKey(:scene, :Default, :domain_scene_routed_vector), :routed_total)] ≈ [1.2, 1.2] + @test sim.outputs[(DomainModelKey(:oil_palm, :Leaf, :domain_mtg_leaf_flux), :leaf_flux)] == [[0.5], [0.5]] + @test sim.outputs[(DomainModelKey(:maize, :Leaf, :domain_mtg_leaf_flux), :leaf_flux)] == [[0.7], [0.7]] + status_rows = explain_domain_statuses(sim) + @test sum(row -> row.scale == :Leaf, status_rows) == 2 + @test only(row.nstatuses for row in status_rows if row.domain == :scene && row.scale == :Default) == 1 + + forest_leaf_mapping = ModelMapping( + :Leaf => ( + ModelSpec(DomainMTGLeafFluxModel(0.4)) |> TimeStepModel(Dates.Hour(1)), + ), + ) + forest_sim = run!( + scene, + SimulationMapping( + Domain(:forest, forest_leaf_mapping; kind=:plant, selector=:Plant), + Domain(:scene, scene_mapping; kind=:scene); + routes=(route,), + ), + meteo, + check=true, + ) + @test length(status(forest_sim, :forest, :Leaf)) == 2 + @test length(status(forest_sim, :Leaf)) == 2 + @test status(forest_sim, :scene).plant_transpirations ≈ [0.4, 0.4] + @test status(forest_sim, :scene).routed_total ≈ 0.8 + @test forest_sim.outputs[(DomainModelKey(:forest, :Leaf, :domain_mtg_leaf_flux), :leaf_flux)] == [[0.4, 0.4], [0.4, 0.4]] + @test only(row.nstatuses for row in explain_domain_statuses(forest_sim) if row.domain == :forest && row.scale == :Leaf) == 2 + + @test_throws "matched overlapping MTG roots" run!( + scene, + SimulationMapping( + Domain( + :overlapping_forest, + forest_leaf_mapping; + kind=:plant, + selector=node -> symbol(node) in (:Plant, :Leaf), + ), + ), + meteo, + check=true, + ) + + dependency_scene_mapping = ModelMapping( + ModelSpec(DomainSceneDependencyFluxSumModel()) |> TimeStepModel(Dates.Hour(1)), + status=(dependency_total=0.0, grouped_dependency_total=0.0), + ) + dependency_sim = run!( + scene, + SimulationMapping( + Domain(:oil_palm, oil_leaf_mapping; kind=:plant, selector=oil_palm), + Domain(:maize, maize_leaf_mapping; kind=:plant, selector=maize), + Domain(:scene, dependency_scene_mapping; kind=:scene), + ), + meteo, + check=true, + ) + @test status(dependency_sim, :scene).dependency_total ≈ 1.2 + @test status(dependency_sim, :scene).grouped_dependency_total ≈ 1.2 + + soil_mapping = ModelMapping( + ModelSpec(DomainSoilWaterModel(0.35)) |> TimeStepModel(Dates.Hour(1)), + status=(soil_water_content=0.0,), + ) + soil_leaf_mapping = ModelMapping( + :Leaf => ( + ModelSpec(DomainMTGLeafSoilFluxModel(2.0)) |> TimeStepModel(Dates.Hour(1)), + ), + ) + soil_route = Route( + from=AllDomains(kind=:soil, process=:domain_soil_water, var=:soil_water_content), + to=DomainRouteTarget(:oil_palm, scale=:Leaf, var=:soil_signal, process=:domain_mtg_leaf_soil_flux), + cardinality=OneToManyBroadcast(), + ) + soil_to_graph_sim = run!( + scene, + SimulationMapping( + Domain(:soil, soil_mapping; kind=:soil), + Domain(:oil_palm, soil_leaf_mapping; kind=:plant, selector=oil_palm); + routes=(soil_route,), + ), + meteo, + check=true, + ) + expected_soil_signals = [0.35 - 0.001 * 20.0, 0.35 - 0.001 * 25.0] + @test only(status(soil_to_graph_sim, :oil_palm, :Leaf)).soil_signal ≈ expected_soil_signals[2] + @test soil_to_graph_sim.outputs[(DomainModelKey(:oil_palm, :Leaf, :domain_mtg_leaf_soil_flux), :leaf_flux)] == [[2.0 * expected_soil_signals[1]], [2.0 * expected_soil_signals[2]]] + + @test_throws "source domains to run earlier" run!( + scene, + SimulationMapping( + Domain(:oil_palm, soil_leaf_mapping; kind=:plant, selector=oil_palm), + Domain(:soil, soil_mapping; kind=:soil); + routes=(soil_route,), + ), + meteo, + check=true, + ) +end + +@testset "Hard-domain targets from MTG-backed domains" begin + scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) + Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 2, 2)) + + meteo = Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) + plant_mapping = ModelMapping( + :Leaf => ( + ModelSpec(DomainHardTargetLeafCounterModel(1.5)) |> TimeStepModel(Dates.Hour(1)), + Status(call_count=0, leaf_signal=0.0), + ), + ) + scene_mapping = ModelMapping( + ModelSpec(DomainSceneHardTargetLeafSumModel()) |> TimeStepModel(Dates.Hour(1)), + status=(leaf_hard_target_total=0.0,), + ) + + sim = run!( + scene, + SimulationMapping( + Domain(:hard_target_plant, plant_mapping; kind=:plant, selector=plant), + Domain(:scene, scene_mapping; kind=:scene), + ), + meteo, + check=true, + ) + + leaf_statuses = status(sim, :hard_target_plant, :Leaf) + @test length(leaf_statuses) == 2 + @test all(st -> st.call_count == 1, leaf_statuses) + @test all(st -> st.leaf_signal ≈ 1.5, leaf_statuses) + @test status(sim, :scene).leaf_hard_target_total ≈ 3.0 + @test sim.outputs[(DomainModelKey(:hard_target_plant, :Leaf, :domain_hard_target_leaf_counter), :leaf_signal)] == [1.5, 1.5] +end + +@testset "MTG-backed domain growth registration" begin + scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + + meteo = Weather([ + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) + for _ in 1:2 + ]) + + growth_mapping = ModelMapping( + :Plant => ( + ModelSpec(DomainGrowthLeafEmergenceModel()) |> TimeStepModel(Dates.Hour(1)), + ), + :Leaf => ( + ModelSpec(DomainGrowthLeafFluxModel(0.9)) |> + MultiScaleModel([:grown_leaves => (:Plant => :grown_leaves)]) |> + TimeStepModel(Dates.Hour(1)), + ), + ) + scene_mapping = ModelMapping( + ModelSpec(DomainSceneGrowthFluxSumModel()) |> TimeStepModel(Dates.Hour(1)), + status=(growth_flux_total=0.0,), + ) + + sim = run!( + scene, + SimulationMapping( + Domain(:growing_plant, growth_mapping; kind=:plant, selector=plant), + Domain(:scene, scene_mapping; kind=:scene), + ), + meteo, + check=true, + ) + + @test length(status(sim, :growing_plant, :Leaf)) == 1 + @test length(status(sim, :Leaf)) == 1 + @test only(status(sim, :growing_plant, :Leaf)).grown_leaves == 1.0 + @test only(status(sim, :growing_plant, :Leaf)).leaf_flux ≈ 0.9 + @test status(sim, :scene).growth_flux_total ≈ 0.9 + @test sim.outputs[(DomainModelKey(:growing_plant, :Leaf, :domain_growth_leaf_flux), :leaf_flux)] == [[0.9], [0.9]] + @test sim.outputs[(DomainModelKey(:scene, :Default, :domain_scene_growth_flux_sum), :growth_flux_total)] ≈ [0.9, 0.9] + @test only(row.nstatuses for row in explain_domain_statuses(sim) if row.domain == :growing_plant && row.scale == :Leaf) == 1 + + multirate_scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + multirate_plant = Node(multirate_scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + multirate_meteo = Weather([ + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) + for _ in 1:25 + ]) + multirate_mapping = ModelMapping( + :Plant => ( + ModelSpec(DomainGrowthLeafEmergenceModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainGrowthIntegratedFluxModel()) |> + MultiScaleModel([:leaf_flux => [:Leaf]]) |> + InputBindings(; leaf_flux=(process=:domain_growth_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=Integrate())) |> + TimeStepModel(Dates.Day(1)), + ), + :Leaf => ( + ModelSpec(DomainGrowthLeafFluxModel(0.9)) |> + MultiScaleModel([:grown_leaves => (:Plant => :grown_leaves)]) |> + TimeStepModel(Dates.Hour(1)), + ), + ) + multirate_sim = run!( + multirate_scene, + SimulationMapping(Domain(:growing_plant, multirate_mapping; kind=:plant, selector=multirate_plant)), + multirate_meteo, + check=true, + ) + + @test length(status(multirate_sim, :growing_plant, :Leaf)) == 1 + @test only(status(multirate_sim, :growing_plant, :Plant)).integrated_leaf_flux ≈ 24.0 * 0.9 + integrated_outputs = multirate_sim.outputs[(DomainModelKey(:growing_plant, :Plant, :domain_growth_integrated_flux), :integrated_leaf_flux)] + @test only.(integrated_outputs) ≈ [0.9, 24.0 * 0.9] + @test length(integrated_outputs) == 2 +end + +@testset "MTG-backed domain variable updates" begin + scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) + + meteo = Weather([ + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) + for _ in 1:2 + ]) + + update_mapping = ModelMapping( + :Leaf => ( + ModelSpec(DomainUpdateAllocationModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainUpdatePruningModel()) |> + Updates(:leaf_biomass; after=:domain_update_allocation) |> + TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainUpdateObserverModel()) |> TimeStepModel(Dates.Hour(1)), + ), + ) + + resolved_update_specs = resolved_model_specs(update_mapping) + inferred_update_binding = input_bindings(resolved_update_specs[:Leaf][:domain_update_observer]).leaf_biomass + @test inferred_update_binding.process == :domain_update_pruning + + sim = run!( + scene, + SimulationMapping(Domain(:updated_plant, update_mapping; kind=:plant, selector=plant)), + meteo, + check=true, + ) + + leaf_status = only(status(sim, :updated_plant, :Leaf)) + @test leaf_status.leaf_biomass == 0.0 + @test leaf_status.observed_biomass == 0.0 + @test sim.outputs[(DomainModelKey(:updated_plant, :Leaf, :domain_update_pruning), :leaf_biomass)] == [[0.0], [0.0]] + @test sim.outputs[(DomainModelKey(:updated_plant, :Leaf, :domain_update_observer), :observed_biomass)] == [[0.0], [0.0]] +end + +@testset "MTG-backed domain leaf removal registration" begin + scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) + Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 2, 2)) + + meteo = Weather([ + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) + for _ in 1:2 + ]) + + removal_mapping = ModelMapping( + :Plant => ( + ModelSpec(DomainRemovalPruningModel()) |> + MultiScaleModel([:leaf_flux => [:Leaf]]) |> + TimeStepModel(Dates.Hour(1)), + Status(removed_count=0, removed_node_id=0, remaining_leaf_flux=0.0), + ), + :Leaf => ( + ModelSpec(DomainRemovalLeafFluxModel()) |> TimeStepModel(Dates.Hour(1)), + ), + ) + + sim = run!( + scene, + SimulationMapping(Domain(:pruned_plant, removal_mapping; kind=:plant, selector=plant)), + meteo, + check=true, + ) + + plant_status = only(status(sim, :pruned_plant, :Plant)) + @test length(status(sim, :pruned_plant, :Leaf)) == 1 + @test length(status(sim, :Leaf)) == 1 + @test length(plant_status.leaf_flux) == 1 + @test plant_status.removed_count == 1 + @test plant_status.removed_node_id > 0 + @test plant_status.remaining_leaf_flux ≈ 1.0 + @test sim.outputs[(DomainModelKey(:pruned_plant, :Leaf, :domain_removal_leaf_flux), :leaf_flux)] == [[1.0], [1.0]] + @test sim.outputs[(DomainModelKey(:pruned_plant, :Plant, :domain_removal_pruning), :remaining_leaf_flux)] == [[1.0], [1.0]] + @test_throws ErrorException remove_organ!(plant, only(sim.domain_states[:pruned_plant].simulations)) + + multirate_scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + multirate_plant = Node(multirate_scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + Node(multirate_plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) + Node(multirate_plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 2, 2)) + multirate_removal_mapping = ModelMapping( + :Plant => ( + ModelSpec(DomainRemovalPruningModel()) |> + MultiScaleModel([:leaf_flux => [:Leaf]]) |> + InputBindings(; leaf_flux=(process=:domain_removal_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=Integrate())) |> + TimeStepModel(Dates.Day(1)), + Status(removed_count=0, removed_node_id=0, remaining_leaf_flux=0.0), + ), + :Leaf => ( + ModelSpec(DomainRemovalLeafFluxModel()) |> TimeStepModel(Dates.Hour(1)), + ), + ) + + multirate_sim = run!( + multirate_scene, + SimulationMapping(Domain(:pruned_plant, multirate_removal_mapping; kind=:plant, selector=multirate_plant)), + meteo, + check=true, + ) + + multirate_plant_status = only(status(multirate_sim, :pruned_plant, :Plant)) + multirate_graph = only(multirate_sim.domain_states[:pruned_plant].simulations) + @test length(status(multirate_sim, :pruned_plant, :Leaf)) == 1 + @test length(multirate_plant_status.leaf_flux) == 1 + @test multirate_plant_status.remaining_leaf_flux ≈ 1.0 + @test all(key -> key.node_id != multirate_plant_status.removed_node_id, keys(multirate_graph.temporal_state.streams)) + @test all(key -> key.node_id != multirate_plant_status.removed_node_id, keys(multirate_graph.temporal_state.caches)) +end + +@testset "MTG-backed domain repeated create/remove churn" begin + scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + meteo = Weather([ + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) + for _ in 1:4 + ]) + + churn_mapping = ModelMapping( + :Plant => ( + ModelSpec(DomainChurnLeafControllerModel()) |> + MultiScaleModel([:leaf_flux => [:Leaf]]) |> + InputBindings(; leaf_flux=(process=:domain_churn_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=HoldLast())) |> + TimeStepModel(Dates.Hour(1)), + Status(created_count=0, removed_count=0, active_leaf_count=0, last_removed_node_id=0), + ), + :Leaf => ( + ModelSpec(DomainChurnLeafFluxModel()) |> TimeStepModel(Dates.Hour(1)), + ), + ) + + sim = run!( + scene, + SimulationMapping(Domain(:churn_plant, churn_mapping; kind=:plant, selector=plant)), + meteo, + check=true, + ) + + plant_status = only(status(sim, :churn_plant, :Plant)) + graph_sim = only(sim.domain_states[:churn_plant].simulations) + @test plant_status.created_count == 2 + @test plant_status.removed_count == 2 + @test plant_status.active_leaf_count == 0 + @test length(plant_status.leaf_flux) == 0 + @test get(status(graph_sim), :Leaf, Status[]) == Status[] + @test get(status(sim.domain_states[:churn_plant]), :Leaf, Status[]) == Status[] + @test status(sim, :churn_plant, :Leaf) == Status[] + @test status(sim, :Leaf) == Status[] + @test only(row.nstatuses for row in explain_domain_statuses(sim) if row.domain == :churn_plant && row.scale == :Leaf) == 0 + @test all(key -> key.scale != :Leaf, keys(graph_sim.temporal_state.streams)) + @test all(key -> key.scale != :Leaf, keys(graph_sim.temporal_state.caches)) + @test sim.outputs[(DomainModelKey(:churn_plant, :Plant, :domain_churn_leaf_controller), :created_count)] == [[1], [1], [2], [2]] + @test sim.outputs[(DomainModelKey(:churn_plant, :Plant, :domain_churn_leaf_controller), :removed_count)] == [[0], [1], [1], [2]] + @test sim.outputs[(DomainModelKey(:churn_plant, :Plant, :domain_churn_leaf_controller), :active_leaf_count)] == [[1], [0], [1], [0]] +end + +@testset "MTG-backed domain recursive subtree removal" begin + scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + internode = Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) + leaf = Node(internode, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 3)) + meteo = Weather([ + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) + for _ in 1:2 + ]) + + subtree_mapping = ModelMapping( + :Plant => ( + ModelSpec(DomainSubtreePruningModel()) |> + MultiScaleModel([ + :internode_flux => [:Internode], + :leaf_flux => [:Leaf], + ]) |> + InputBindings(; + internode_flux=(process=:domain_subtree_internode_flux, scale=:Internode, var=:internode_flux, policy=HoldLast()), + leaf_flux=(process=:domain_subtree_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=HoldLast()), + ) |> + TimeStepModel(Dates.Hour(1)), + Status( + removed_count=0, + removed_internode_id=0, + removed_leaf_id=0, + remaining_internode_count=0, + remaining_leaf_count=0, + ), + ), + :Internode => ( + ModelSpec(DomainSubtreeInternodeFluxModel()) |> TimeStepModel(Dates.Hour(1)), + ), + :Leaf => ( + ModelSpec(DomainSubtreeLeafFluxModel()) |> TimeStepModel(Dates.Hour(1)), + ), + ) + + sim = run!( + scene, + SimulationMapping(Domain(:subtree_plant, subtree_mapping; kind=:plant, selector=plant)), + meteo, + check=true, + ) + + plant_status = only(status(sim, :subtree_plant, :Plant)) + graph_sim = only(sim.domain_states[:subtree_plant].simulations) + @test plant_status.removed_count == 1 + @test plant_status.removed_internode_id == node_id(internode) + @test plant_status.removed_leaf_id == node_id(leaf) + @test plant_status.remaining_internode_count == 0 + @test plant_status.remaining_leaf_count == 0 + @test length(plant_status.internode_flux) == 0 + @test length(plant_status.leaf_flux) == 0 + @test get(status(graph_sim), :Internode, Status[]) == Status[] + @test get(status(graph_sim), :Leaf, Status[]) == Status[] + @test status(sim, :subtree_plant, :Internode) == Status[] + @test status(sim, :subtree_plant, :Leaf) == Status[] + @test status(sim, :Internode) == Status[] + @test status(sim, :Leaf) == Status[] + @test only(row.nstatuses for row in explain_domain_statuses(sim) if row.domain == :subtree_plant && row.scale == :Internode) == 0 + @test only(row.nstatuses for row in explain_domain_statuses(sim) if row.domain == :subtree_plant && row.scale == :Leaf) == 0 + @test all(key -> key.node_id != plant_status.removed_internode_id, keys(graph_sim.temporal_state.streams)) + @test all(key -> key.node_id != plant_status.removed_leaf_id, keys(graph_sim.temporal_state.streams)) + @test all(key -> key.node_id != plant_status.removed_internode_id, keys(graph_sim.temporal_state.caches)) + @test all(key -> key.node_id != plant_status.removed_leaf_id, keys(graph_sim.temporal_state.caches)) + @test sim.outputs[(DomainModelKey(:subtree_plant, :Internode, :domain_subtree_internode_flux), :internode_flux)] == [[], []] + @test sim.outputs[(DomainModelKey(:subtree_plant, :Leaf, :domain_subtree_leaf_flux), :leaf_flux)] == [[], []] + @test sim.outputs[(DomainModelKey(:subtree_plant, :Plant, :domain_subtree_pruning), :remaining_internode_count)] == [[0], [0]] + @test sim.outputs[(DomainModelKey(:subtree_plant, :Plant, :domain_subtree_pruning), :remaining_leaf_count)] == [[0], [0]] +end + +@testset "MTG-backed domain topology reparenting" begin + scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + internode_1 = Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) + internode_2 = Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Internode, 2, 2)) + leaf = Node(internode_1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 3)) + meteo = Weather([ + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) + for _ in 1:2 + ]) + + reparent_mapping = ModelMapping( + :Plant => ( + ModelSpec(DomainReparentLeafControllerModel()) |> + MultiScaleModel([:leaf_flux => [:Leaf]]) |> + InputBindings(; leaf_flux=(process=:domain_subtree_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=HoldLast())) |> + TimeStepModel(Dates.Hour(1)), + Status(reparented_count=0, new_parent_id=0, leaf_parent_id=0, active_leaf_count=0), + ), + :Internode => ( + ModelSpec(DomainSubtreeInternodeFluxModel()) |> TimeStepModel(Dates.Hour(1)), + ), + :Leaf => ( + ModelSpec(DomainSubtreeLeafFluxModel()) |> TimeStepModel(Dates.Hour(1)), + ), + ) + + sim = run!( + scene, + SimulationMapping(Domain(:reparented_plant, reparent_mapping; kind=:plant, selector=plant)), + meteo, + check=true, + ) + + plant_status = only(status(sim, :reparented_plant, :Plant)) + graph_sim = only(sim.domain_states[:reparented_plant].simulations) + @test parent(leaf) === internode_2 + @test !any(node -> node === leaf, children(internode_1)) + @test count(node -> node === leaf, children(internode_2)) == 1 + @test plant_status.reparented_count == 1 + @test plant_status.new_parent_id == node_id(internode_2) + @test plant_status.leaf_parent_id == node_id(internode_2) + @test plant_status.active_leaf_count == 1 + @test length(plant_status.leaf_flux) == 1 + @test length(status(sim, :reparented_plant, :Internode)) == 2 + @test length(status(sim, :reparented_plant, :Leaf)) == 1 + @test all(key -> key.node_id != 0, keys(graph_sim.temporal_state.streams)) + @test sim.outputs[(DomainModelKey(:reparented_plant, :Leaf, :domain_subtree_leaf_flux), :leaf_flux)] == [[1.0], [1.0]] + @test sim.outputs[(DomainModelKey(:reparented_plant, :Plant, :domain_reparent_leaf_controller), :leaf_parent_id)] == [[node_id(internode_2)], [node_id(internode_2)]] + @test_throws ErrorException reparent_organ!(internode_2, leaf, graph_sim) +end diff --git a/test/test-environment-backends.jl b/test/test-environment-backends.jl new file mode 100644 index 000000000..2d04fa783 --- /dev/null +++ b/test/test-environment-backends.jl @@ -0,0 +1,375 @@ +using Dates + +PlantSimEngine.@process "environment_probe" verbose = false +PlantSimEngine.@process "environment_temperature_update" verbose = false +PlantSimEngine.@process "environment_bad_output" verbose = false +PlantSimEngine.@process "environment_graph_leaf_probe" verbose = false +PlantSimEngine.@process "environment_graph_temperature_update" verbose = false +PlantSimEngine.@process "environment_scene_hard_graph_update" verbose = false + +struct EnvironmentProbeModel <: AbstractEnvironment_ProbeModel end +struct EnvironmentTemperatureUpdateModel <: AbstractEnvironment_Temperature_UpdateModel end +struct EnvironmentBadOutputModel <: AbstractEnvironment_Bad_OutputModel end +struct EnvironmentGraphLeafProbeModel <: AbstractEnvironment_Graph_Leaf_ProbeModel end +struct EnvironmentGraphTemperatureUpdateModel <: AbstractEnvironment_Graph_Temperature_UpdateModel end +struct EnvironmentSceneHardGraphUpdateModel <: AbstractEnvironment_Scene_Hard_Graph_UpdateModel end + +PlantSimEngine.inputs_(::EnvironmentProbeModel) = NamedTuple() +PlantSimEngine.outputs_(::EnvironmentProbeModel) = (meteo_seen=0.0,) +PlantSimEngine.meteo_inputs_(::EnvironmentProbeModel) = (T=0.0, CO2=400.0) + +function PlantSimEngine.run!(::EnvironmentProbeModel, models, status, meteo, constants=nothing, extra=nothing) + status.meteo_seen = meteo.T + 0.001 * meteo.CO2 + return nothing +end + +PlantSimEngine.inputs_(::EnvironmentTemperatureUpdateModel) = NamedTuple() +PlantSimEngine.outputs_(::EnvironmentTemperatureUpdateModel) = (T=0.0,) +PlantSimEngine.meteo_inputs_(::EnvironmentTemperatureUpdateModel) = (T=0.0,) +PlantSimEngine.meteo_outputs_(::EnvironmentTemperatureUpdateModel) = (T=0.0,) + +function PlantSimEngine.run!(::EnvironmentTemperatureUpdateModel, models, status, meteo, constants=nothing, extra=nothing) + status.T = meteo.T + 1.0 + return nothing +end + +PlantSimEngine.inputs_(::EnvironmentBadOutputModel) = NamedTuple() +PlantSimEngine.outputs_(::EnvironmentBadOutputModel) = NamedTuple() +PlantSimEngine.meteo_inputs_(::EnvironmentBadOutputModel) = (T=0.0,) +PlantSimEngine.meteo_outputs_(::EnvironmentBadOutputModel) = (T=0.0,) + +function PlantSimEngine.run!(::EnvironmentBadOutputModel, models, status, meteo, constants=nothing, extra=nothing) + return nothing +end + +PlantSimEngine.inputs_(::EnvironmentGraphLeafProbeModel) = NamedTuple() +PlantSimEngine.outputs_(::EnvironmentGraphLeafProbeModel) = (meteo_seen=0.0,) +PlantSimEngine.meteo_inputs_(::EnvironmentGraphLeafProbeModel) = (T=0.0, CO2=400.0) + +function PlantSimEngine.run!(::EnvironmentGraphLeafProbeModel, models, status, meteo, constants=nothing, extra=nothing) + status.meteo_seen = meteo.T + 0.001 * meteo.CO2 + return nothing +end + +PlantSimEngine.inputs_(::EnvironmentGraphTemperatureUpdateModel) = NamedTuple() +PlantSimEngine.outputs_(::EnvironmentGraphTemperatureUpdateModel) = (T=0.0,) +PlantSimEngine.meteo_inputs_(::EnvironmentGraphTemperatureUpdateModel) = (T=0.0,) +PlantSimEngine.meteo_outputs_(::EnvironmentGraphTemperatureUpdateModel) = (T=0.0,) + +function PlantSimEngine.run!(::EnvironmentGraphTemperatureUpdateModel, models, status, meteo, constants=nothing, extra=nothing) + status.T = meteo.T + 2.0 + return nothing +end + +PlantSimEngine.dep(::EnvironmentSceneHardGraphUpdateModel) = ( + leaf_temperature=HardDomains(kind=:plant, scale=:Leaf, process=:environment_graph_temperature_update), +) +PlantSimEngine.inputs_(::EnvironmentSceneHardGraphUpdateModel) = NamedTuple() +PlantSimEngine.outputs_(::EnvironmentSceneHardGraphUpdateModel) = (hard_temperature_sum=0.0,) + +function PlantSimEngine.run!(::EnvironmentSceneHardGraphUpdateModel, models, status, meteo, constants=nothing, extra=nothing) + targets = dependency_targets(extra, :leaf_temperature) + for target in targets + run_target!(target; publish=true) + end + status.hard_temperature_sum = sum(target.status.T for target in targets) + return nothing +end + +struct ProbeEnvironmentBackend <: AbstractEnvironmentBackend + nsteps::Int + base_seconds::Float64 +end + +PlantSimEngine.get_nsteps(backend::ProbeEnvironmentBackend) = backend.nsteps +PlantSimEngine.base_step_seconds(backend::ProbeEnvironmentBackend) = backend.base_seconds +PlantSimEngine.environment_variables(::ProbeEnvironmentBackend) = Set([:T, :CO2, :Ca]) + +function PlantSimEngine.sample( + ::ProbeEnvironmentBackend, + variable::Symbol, + support::EnvironmentSupport, + time +) + variable == :CO2 && return 410.0 + variable == :Ca && return 420.0 + variable == :T || error("Unexpected variable `$(variable)`.") + offset = support.domain == :plant_b ? 100.0 : 0.0 + return offset + 10.0 + float(time) +end + +struct MissingCO2EnvironmentBackend <: AbstractEnvironmentBackend end + +PlantSimEngine.get_nsteps(::MissingCO2EnvironmentBackend) = 1 +PlantSimEngine.base_step_seconds(::MissingCO2EnvironmentBackend) = 3600.0 +PlantSimEngine.environment_variables(::MissingCO2EnvironmentBackend) = Set([:T]) +PlantSimEngine.sample(::MissingCO2EnvironmentBackend, variable::Symbol, support::EnvironmentSupport, time) = 20.0 + +mutable struct ScatteringEnvironmentBackend <: AbstractEnvironmentBackend + nsteps::Int + base_seconds::Float64 + writes::Vector{NamedTuple} + index_updates::Vector{Any} +end + +PlantSimEngine.get_nsteps(backend::ScatteringEnvironmentBackend) = backend.nsteps +PlantSimEngine.base_step_seconds(backend::ScatteringEnvironmentBackend) = backend.base_seconds +PlantSimEngine.environment_variables(::ScatteringEnvironmentBackend) = Set([:T]) +PlantSimEngine.sample(::ScatteringEnvironmentBackend, variable::Symbol, support::EnvironmentSupport, time) = 20.0 + float(time) + +function PlantSimEngine.scatter!( + backend::ScatteringEnvironmentBackend, + variable::Symbol, + support::EnvironmentSupport, + value, + time +) + push!(backend.writes, (domain=support.domain, process=support.process, variable=variable, value=value, time=time)) + return nothing +end + +function PlantSimEngine.update_index!(backend::ScatteringEnvironmentBackend, entities) + push!( + backend.index_updates, + [ + (domain=entity.domain, kind=entity.kind, scale=entity.scale, nstatuses=length(entity.statuses)) + for entity in entities + ], + ) + return nothing +end + +mutable struct GraphEnvironmentBackend <: AbstractEnvironmentBackend + nsteps::Int + base_seconds::Float64 + writes::Vector{NamedTuple} + index_updates::Vector{Any} +end + +PlantSimEngine.get_nsteps(backend::GraphEnvironmentBackend) = backend.nsteps +PlantSimEngine.base_step_seconds(backend::GraphEnvironmentBackend) = backend.base_seconds +PlantSimEngine.environment_variables(::GraphEnvironmentBackend) = Set([:T, :CO2]) + +function PlantSimEngine.sample( + ::GraphEnvironmentBackend, + variable::Symbol, + support::EnvironmentSupport, + time +) + variable == :CO2 && return 410.0 + variable == :T || error("Unexpected variable `$(variable)`.") + return 20.0 + float(time) + 0.1 * node_id(support.status.node) +end + +function PlantSimEngine.scatter!( + backend::GraphEnvironmentBackend, + variable::Symbol, + support::EnvironmentSupport, + value, + time +) + push!( + backend.writes, + ( + domain=support.domain, + scale=support.scale, + process=support.process, + node_id=node_id(support.status.node), + variable=variable, + value=value, + time=time, + ), + ) + return nothing +end + +function PlantSimEngine.update_index!(backend::GraphEnvironmentBackend, entities) + push!( + backend.index_updates, + [ + (domain=entity.domain, kind=entity.kind, scale=entity.scale, nstatuses=length(entity.statuses)) + for entity in entities + ], + ) + return nothing +end + +@testset "Environment backends" begin + support = EnvironmentSupport(:plant_a, :Default, :environment_probe, nothing) + global_backend = GlobalConstant(Atmosphere(T=20.0, Rh=0.65, Wind=1.0, CO2=410.0, duration=Dates.Hour(1))) + @test sample(global_backend, :T, support, 1.0) == 20.0 + @test PlantSimEngine.get_nsteps(global_backend) == 1 + @test base_step_seconds(global_backend) == 3600.0 + @test environment_variables(GlobalConstant(nothing)) == Set{Symbol}() + + mapping = ModelMapping( + ModelSpec(EnvironmentProbeModel()) |> TimeStepModel(Dates.Hour(1)), + status=(meteo_seen=0.0,), + ) + simulation_mapping = SimulationMapping( + Domain(:plant_a, mapping; kind=:plant), + Domain(:plant_b, mapping; kind=:plant), + ) + + sim = run!(simulation_mapping, ProbeEnvironmentBackend(3, 3600.0), check=true) + @test status(sim, :plant_a).meteo_seen ≈ 13.41 + @test status(sim, :plant_b).meteo_seen ≈ 113.41 + + environment = explain_environment(sim) + @test environment.backend == ProbeEnvironmentBackend + @test environment.nsteps == 3 + @test environment.base_step_seconds == 3600.0 + @test :CO2 in environment.variables + + bound_mapping = ModelMapping( + ModelSpec(EnvironmentProbeModel()) |> + TimeStepModel(Dates.Hour(1)) |> + MeteoBindings(; CO2=(source=:Ca, reducer=MeanReducer())), + status=(meteo_seen=0.0,), + ) + bound_sim = run!( + SimulationMapping(Domain(:plant_a, bound_mapping; kind=:plant)), + ProbeEnvironmentBackend(1, 3600.0), + check=true, + ) + @test status(bound_sim, :plant_a).meteo_seen ≈ 11.42 + + @test_throws "CO2" run!( + SimulationMapping(Domain(:plant_a, mapping; kind=:plant)), + MissingCO2EnvironmentBackend(), + check=true, + ) + @test_throws "CO2" run!( + SimulationMapping(Domain(:plant_a, mapping; kind=:plant)), + nothing, + check=true, + ) + + update_mapping = ModelMapping( + ModelSpec(EnvironmentTemperatureUpdateModel()) |> TimeStepModel(Dates.Hour(1)), + status=(T=0.0,), + ) + scattering_backend = ScatteringEnvironmentBackend(2, 3600.0, NamedTuple[], Any[]) + scatter_sim = run!( + SimulationMapping(Domain(:plant_a, update_mapping; kind=:plant)), + scattering_backend, + check=true, + ) + @test status(scatter_sim, :plant_a).T == 23.0 + @test scattering_backend.writes == [ + (domain=:plant_a, process=:environment_temperature_update, variable=:T, value=22.0, time=1.0), + (domain=:plant_a, process=:environment_temperature_update, variable=:T, value=23.0, time=2.0), + ] + @test scattering_backend.index_updates == [ + [(domain=:plant_a, kind=:plant, scale=:Default, nstatuses=1)], + [(domain=:plant_a, kind=:plant, scale=:Default, nstatuses=1)], + ] + + @test_throws "GlobalConstant is immutable" run!( + SimulationMapping(Domain(:plant_a, update_mapping; kind=:plant)), + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, duration=Dates.Hour(1)), + check=true, + ) + + bad_mapping = ModelMapping( + ModelSpec(EnvironmentBadOutputModel()) |> TimeStepModel(Dates.Hour(1)), + status=NamedTuple(), + ) + @test_throws "status does not contain" run!( + SimulationMapping(Domain(:plant_a, bad_mapping; kind=:plant)), + ScatteringEnvironmentBackend(1, 3600.0, NamedTuple[], Any[]), + check=true, + ) + + scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + leaf_1 = Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) + leaf_2 = Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 2, 2)) + leaf_ids = sort([node_id(leaf_1), node_id(leaf_2)]) + graph_mapping = ModelMapping( + :Leaf => ( + ModelSpec(EnvironmentGraphLeafProbeModel()) |> TimeStepModel(Dates.Hour(1)), + ), + ) + graph_backend = GraphEnvironmentBackend(2, 3600.0, NamedTuple[], Any[]) + graph_sim = run!( + scene, + SimulationMapping(Domain(:plant_a, graph_mapping; kind=:plant, selector=plant)), + graph_backend, + check=true, + ) + graph_values = graph_sim.outputs[(DomainModelKey(:plant_a, :Leaf, :environment_graph_leaf_probe), :meteo_seen)] + @test graph_values == [ + [20.0 + 1.0 + 0.1 * leaf_ids[1] + 0.410, 20.0 + 1.0 + 0.1 * leaf_ids[2] + 0.410], + [20.0 + 2.0 + 0.1 * leaf_ids[1] + 0.410, 20.0 + 2.0 + 0.1 * leaf_ids[2] + 0.410], + ] + @test graph_backend.index_updates == [ + [(domain=:plant_a, kind=:plant, scale=:Leaf, nstatuses=2)], + [(domain=:plant_a, kind=:plant, scale=:Leaf, nstatuses=2)], + ] + + scatter_scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + scatter_plant = Node(scatter_scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + scatter_leaf = Node(scatter_plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) + scatter_mapping = ModelMapping( + :Leaf => ( + ModelSpec(EnvironmentGraphTemperatureUpdateModel()) |> TimeStepModel(Dates.Hour(1)), + ), + ) + graph_scatter_backend = GraphEnvironmentBackend(1, 3600.0, NamedTuple[], Any[]) + graph_scatter_sim = run!( + scatter_scene, + SimulationMapping(Domain(:plant_a, scatter_mapping; kind=:plant, selector=scatter_plant)), + graph_scatter_backend, + check=true, + ) + @test only(status(graph_scatter_sim, :plant_a, :Leaf)).T ≈ 20.0 + 1.0 + 0.1 * node_id(scatter_leaf) + 2.0 + @test graph_scatter_backend.writes == [ + ( + domain=:plant_a, + scale=:Leaf, + process=:environment_graph_temperature_update, + node_id=node_id(scatter_leaf), + variable=:T, + value=20.0 + 1.0 + 0.1 * node_id(scatter_leaf) + 2.0, + time=1.0, + ), + ] + + hard_scatter_scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + hard_scatter_plant = Node(hard_scatter_scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + hard_scatter_leaf = Node(hard_scatter_plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) + hard_scatter_mapping = ModelMapping( + :Leaf => ( + ModelSpec(EnvironmentGraphTemperatureUpdateModel()) |> TimeStepModel(Dates.Hour(1)), + ), + ) + hard_scatter_scene_mapping = ModelMapping( + ModelSpec(EnvironmentSceneHardGraphUpdateModel()) |> TimeStepModel(Dates.Hour(1)), + status=(hard_temperature_sum=0.0,), + ) + hard_graph_scatter_backend = GraphEnvironmentBackend(1, 3600.0, NamedTuple[], Any[]) + hard_graph_scatter_sim = run!( + hard_scatter_scene, + SimulationMapping( + Domain(:plant_a, hard_scatter_mapping; kind=:plant, selector=hard_scatter_plant), + Domain(:scene, hard_scatter_scene_mapping; kind=:scene), + ), + hard_graph_scatter_backend, + check=true, + ) + expected_hard_T = 20.0 + 1.0 + 0.1 * node_id(hard_scatter_leaf) + 2.0 + @test only(status(hard_graph_scatter_sim, :plant_a, :Leaf)).T ≈ expected_hard_T + @test status(hard_graph_scatter_sim, :scene).hard_temperature_sum ≈ expected_hard_T + @test hard_graph_scatter_backend.writes == [ + ( + domain=:plant_a, + scale=:Leaf, + process=:environment_graph_temperature_update, + node_id=node_id(hard_scatter_leaf), + variable=:T, + value=expected_hard_T, + time=1.0, + ), + ] +end diff --git a/test/test-maespa-domain-example.jl b/test/test-maespa-domain-example.jl new file mode 100644 index 000000000..20b42f733 --- /dev/null +++ b/test/test-maespa-domain-example.jl @@ -0,0 +1,38 @@ +include("../examples/maespa_domain_example.jl") + +@testset "MAESPA-style domain example" begin + result = run_maespa_example(; nhours=24, check=true) + sim = result.simulation + + @test length(status(sim, :Leaf)) == 5 + @test length(status(sim, :plant_A, :Leaf)) == 2 + @test length(status(sim, :plant_B, :Leaf)) == 3 + + scene_status = status(sim, :scene) + @test isfinite(scene_status.canopy_Tair) + @test isfinite(scene_status.canopy_VPD) + @test isfinite(scene_status.scene_transpiration) + @test scene_status.scene_transpiration > 0.0 + @test scene_status.iterations > 0 + + leaf_statuses = status(sim, :Leaf) + @test all(st -> isfinite(st.Tleaf), leaf_statuses) + @test all(st -> isfinite(st.An), leaf_statuses) + @test all(st -> isfinite(st.E), leaf_statuses) + @test any(st -> abs(st.Tleaf - st.Tair) > 1.0e-6, leaf_statuses) + + plant_a_status = only(status(sim, :plant_A, :Plant)) + plant_b_status = only(status(sim, :plant_B, :Plant)) + @test plant_a_status.daily_growth > 0.0 + @test plant_b_status.daily_growth > 0.0 + @test plant_a_status.daily_growth != plant_b_status.daily_growth + @test plant_a_status.leaf_pool != plant_b_status.leaf_pool + + deps = explain_domain_dependencies(sim) + @test count(row -> row.mode == :hard_domain && row.dependency == :leaf_eb, deps) == 2 + @test count(row -> row.mode == :hard_domain && row.dependency == :soil, deps) == 1 + + @test length(sim.outputs[(DomainModelKey(:plant_A, :Leaf, :leaf_eb), :E)]) == 2 * 24 + @test length(sim.outputs[(DomainModelKey(:plant_B, :Leaf, :leaf_eb), :E)]) == 3 * 24 + @test length(sim.outputs[(DomainModelKey(:soil, :Default, :soil_water), :psi_soil)]) == 24 +end diff --git a/test/test-meteo-traits.jl b/test/test-meteo-traits.jl new file mode 100644 index 000000000..4111003e2 --- /dev/null +++ b/test/test-meteo-traits.jl @@ -0,0 +1,60 @@ +using Dates + +PlantSimEngine.@process "meteo_trait_consumer" verbose = false + +struct MeteoTraitConsumerModel <: AbstractMeteo_Trait_ConsumerModel end + +PlantSimEngine.inputs_(::MeteoTraitConsumerModel) = NamedTuple() +PlantSimEngine.outputs_(::MeteoTraitConsumerModel) = (meteo_seen=0.0,) +PlantSimEngine.meteo_inputs_(::MeteoTraitConsumerModel) = (T=0.0, CO2=400.0) + +function PlantSimEngine.run!(::MeteoTraitConsumerModel, models, status, meteo, constants=nothing, extra=nothing) + status.meteo_seen = meteo.T + return nothing +end + +@testset "Meteo traits" begin + specs = Dict(:Leaf => Dict(:meteo_trait_consumer => ModelSpec(MeteoTraitConsumerModel()))) + + @test_throws "CO2" PlantSimEngine.validate_meteo_inputs( + specs, + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, duration=Dates.Hour(1)) + ) + + @test PlantSimEngine.validate_meteo_inputs( + specs, + (T=20.0, CO2=410.0, duration=Dates.Hour(1)) + ) === nothing + + bound_spec = + ModelSpec(MeteoTraitConsumerModel()) |> + MeteoBindings(; CO2=(source=:Ca, reducer=MeanReducer())) + bound_specs = Dict(:Leaf => Dict(:meteo_trait_consumer => bound_spec)) + + @test_throws "Ca" PlantSimEngine.validate_meteo_inputs( + bound_specs, + (T=20.0, CO2=410.0, duration=Dates.Hour(1)) + ) + @test PlantSimEngine.validate_meteo_inputs( + bound_specs, + (T=20.0, Ca=410.0, duration=Dates.Hour(1)) + ) === nothing + + mapping = ModelMapping( + MeteoTraitConsumerModel(), + status=(meteo_seen=0.0,), + ) + @test_throws "CO2" PlantSimEngine.validate_meteo_inputs( + mapping, + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, duration=Dates.Hour(1)) + ) + @test PlantSimEngine.validate_meteo_inputs( + mapping, + (T=20.0, CO2=410.0, duration=Dates.Hour(1)) + ) === nothing + + @test PlantSimEngine.validate_meteo_inputs( + Dict("Leaf" => (MeteoTraitConsumerModel(),)), + (T=20.0, CO2=410.0, duration=Dates.Hour(1)) + ) === nothing +end diff --git a/test/test-multirate-output-export.jl b/test/test-multirate-output-export.jl index 30162d09d..9b1b2e3f4 100644 --- a/test/test-multirate-output-export.jl +++ b/test/test-multirate-output-export.jl @@ -44,6 +44,50 @@ function PlantSimEngine.run!(::MRDefaultSceneAggModel, models, status, meteo, co end PlantSimEngine.timespec(::Type{<:MRDefaultSceneAggModel}) = ClockSpec(4.0, 1.0) +PlantSimEngine.@process "mrdynamicgrow" verbose = false +struct MRDynamicGrowModel <: AbstractMrdynamicgrowModel end +PlantSimEngine.inputs_(::MRDynamicGrowModel) = NamedTuple() +PlantSimEngine.outputs_(::MRDynamicGrowModel) = (nleaves=0.0,) +function PlantSimEngine.run!(::MRDynamicGrowModel, models, status, meteo, constants=nothing, extra=nothing) + if length(PlantSimEngine.status(extra)[:Leaf]) == 0 + add_organ!(status.node, extra, "+", :Leaf, 2; check=true) + end + status.nleaves = length(PlantSimEngine.status(extra)[:Leaf]) + return nothing +end + +PlantSimEngine.@process "mrdynamicgrowmany" verbose = false +struct MRDynamicGrowManyModel <: AbstractMrdynamicgrowmanyModel end +PlantSimEngine.inputs_(::MRDynamicGrowManyModel) = NamedTuple() +PlantSimEngine.outputs_(::MRDynamicGrowManyModel) = (nleaves=0.0,) +function PlantSimEngine.run!(::MRDynamicGrowManyModel, models, status, meteo, constants=nothing, extra=nothing) + while length(PlantSimEngine.status(extra)[:Leaf]) < 2 + add_organ!(status.node, extra, "+", :Leaf, 2; check=true) + end + status.nleaves = length(PlantSimEngine.status(extra)[:Leaf]) + return nothing +end + +PlantSimEngine.@process "mrdynamicleafsource" verbose = false +struct MRDynamicLeafSourceModel <: AbstractMrdynamicleafsourceModel end +PlantSimEngine.inputs_(::MRDynamicLeafSourceModel) = (nleaves=0.0,) +PlantSimEngine.outputs_(::MRDynamicLeafSourceModel) = (X=-Inf,) +function PlantSimEngine.run!(::MRDynamicLeafSourceModel, models, status, meteo, constants=nothing, extra=nothing) + status.X = status.nleaves * meteo.T / 10.0 + return nothing +end + +function _mr_custom_plant_scope(node, scale, process) + current = node + while !isnothing(current) + if MultiScaleTreeGraph.symbol(current) == :Plant + return ScopeId(:custom_plant, MultiScaleTreeGraph.node_id(current)) + end + current = parent(current) + end + return ScopeId(:custom_plant, 0) +end + @testset "Multi-rate output export API" begin mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) plant = Node(mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) @@ -143,6 +187,92 @@ PlantSimEngine.timespec(::Type{<:MRDefaultSceneAggModel}) = ClockSpec(4.0, 1.0) ) @test haskey(out_status_mtg, :Leaf) @test out_requested_mtg[:x_mtg][:, :value] == [1.0, 2.0, 3.0, 4.0] + + # Dynamic statuses created after GraphSimulation initialization are visible + # to online OutputRequest exports. + dynamic_mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + dynamic_plant = Node(dynamic_mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + dynamic_meteo = Weather([ + Atmosphere(T=10.0, Wind=1.0, Rh=0.65), + Atmosphere(T=20.0, Wind=1.0, Rh=0.65), + Atmosphere(T=30.0, Wind=1.0, Rh=0.65), + Atmosphere(T=40.0, Wind=1.0, Rh=0.65), + ]) + dynamic_mapping = ModelMapping( + :Plant => ( + ModelSpec(MRDynamicGrowModel()) |> + TimeStepModel(1.0) |> + ScopeModel(:plant), + ), + :Leaf => ( + ModelSpec(MRDynamicLeafSourceModel()) |> + MultiScaleModel([:nleaves => (:Plant => :nleaves)]) |> + TimeStepModel(1.0) |> + ScopeModel(:plant), + ), + ) + dynamic_sim = PlantSimEngine.GraphSimulation( + dynamic_mtg, + dynamic_mapping, + nsteps=4, + check=true, + outputs=Dict(:Leaf => (:X,)), + ) + run!( + dynamic_sim, + dynamic_meteo, + executor=SequentialEx(), + tracked_outputs=[ + OutputRequest(:Leaf, :X; name=:dynamic_hold, process=:mrdynamicleafsource, policy=HoldLast()), + OutputRequest(:Leaf, :X; name=:dynamic_sum2, process=:mrdynamicleafsource, policy=Integrate(), clock=ClockSpec(2.0, 1.0)), + ], + ) + dynamic_exported = collect_outputs(dynamic_sim; sink=DataFrame) + @test length(status(dynamic_sim)[:Leaf]) == 1 + @test dynamic_exported[:dynamic_hold][:, :timestep] == [1, 2, 3, 4] + @test dynamic_exported[:dynamic_hold][:, :value] == [1.0, 2.0, 3.0, 4.0] + @test dynamic_exported[:dynamic_sum2][:, :timestep] == [1, 3] + @test dynamic_exported[:dynamic_sum2][:, :value] == [1.0, 5.0] + + # Several dynamic statuses and callable scopes are resolved from live status + # vectors when temporal outputs are exported. + many_mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + many_plant = Node(many_mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + many_mapping = ModelMapping( + :Plant => ( + ModelSpec(MRDynamicGrowManyModel()) |> + TimeStepModel(1.0) |> + ScopeModel(_mr_custom_plant_scope), + ), + :Leaf => ( + ModelSpec(MRDynamicLeafSourceModel()) |> + MultiScaleModel([:nleaves => (:Plant => :nleaves)]) |> + TimeStepModel(1.0) |> + ScopeModel(_mr_custom_plant_scope), + ), + ) + many_sim = PlantSimEngine.GraphSimulation( + many_mtg, + many_mapping, + nsteps=4, + check=true, + outputs=Dict(:Leaf => (:X,)), + ) + run!( + many_sim, + dynamic_meteo, + executor=SequentialEx(), + tracked_outputs=[ + OutputRequest(:Leaf, :X; name=:many_hold, process=:mrdynamicleafsource, policy=HoldLast()), + OutputRequest(:Leaf, :X; name=:many_sum2, process=:mrdynamicleafsource, policy=Integrate(), clock=ClockSpec(2.0, 1.0)), + ], + ) + many_exported = collect_outputs(many_sim; sink=DataFrame) + @test length(status(many_sim)[:Leaf]) == 2 + @test many_exported[:many_hold][:, :timestep] == [1, 1, 2, 2, 3, 3, 4, 4] + @test many_exported[:many_hold][:, :value] == [2.0, 2.0, 4.0, 4.0, 6.0, 6.0, 8.0, 8.0] + @test many_exported[:many_sum2][:, :timestep] == [1, 1, 3, 3] + @test many_exported[:many_sum2][:, :value] == [2.0, 2.0, 10.0, 10.0] end @testset "Multi-rate output export defaults on multi-scale mapping with timespec traits" begin diff --git a/test/test-multirate-runtime.jl b/test/test-multirate-runtime.jl index 2d88c7fd2..4768794a3 100644 --- a/test/test-multirate-runtime.jl +++ b/test/test-multirate-runtime.jl @@ -281,7 +281,7 @@ PlantSimEngine.dep(::MRHardParentModel) = (mrhardchild=AbstractMrhardchildModel, PlantSimEngine.inputs_(::MRHardParentModel) = NamedTuple() PlantSimEngine.outputs_(::MRHardParentModel) = (A=-Inf,) function PlantSimEngine.run!(::MRHardParentModel, models, status, meteo, constants=nothing, extra=nothing) - run!(models.mrhardchild, models, status, meteo, constants, extra) + run_target!(models, status, :mrhardchild; meteo=meteo, constants=constants, extra=extra) status.A = 5.0 end @@ -441,9 +441,14 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( ModelSpec(MRConflict2Model()) |> TimeStepModel(1.0), ), ) - sim_conflict = PlantSimEngine.GraphSimulation(mtg, mapping_conflict, nsteps=1, check=true, outputs=Dict(:Leaf => (:Z,))) - # Expectation 5: two canonical publishers of the same output are rejected. - @test_throws "Ambiguous canonical publishers" run!(sim_conflict, meteo, executor=SequentialEx()) + # Expectation 5: two canonical writers of the same output are rejected at graph construction. + @test_throws "Ambiguous canonical writers" PlantSimEngine.GraphSimulation( + mtg, + mapping_conflict, + nsteps=1, + check=true, + outputs=Dict(:Leaf => (:Z,)) + ) # Expectation 6: models run at different clocks; slower model holds last value between runs. source_counter = Ref(0) @@ -1250,7 +1255,7 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test input_bindings(spec_hard_same_rate).A.process == :mrhardparent @test status(sim_hard_same_rate)[:Leaf][1].B == 5.0 - # Expectation 24f: different-rate hard dependencies remain strict and require explicit disambiguation. + # Expectation 24f: hard dependency children cannot be scheduled independently from their parent. mapping_hard_different_rate = ModelMapping( :Leaf => ( ModelSpec(MRHardParentModel()) |> TimeStepModel(1.0), @@ -1258,7 +1263,7 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( ModelSpec(MRHardConsumerModel()) |> TimeStepModel(1.0), ), ) - @test_throws "Ambiguous inferred producer for input `A`" PlantSimEngine.GraphSimulation( + @test_throws "Hard dependency `mrhardchild`" PlantSimEngine.GraphSimulation( mtg, mapping_hard_different_rate, nsteps=1, @@ -1266,24 +1271,6 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( outputs=Dict(:Leaf => (:A, :B)) ) - mapping_hard_different_rate_explicit = ModelMapping( - :Leaf => ( - ModelSpec(MRHardParentModel()) |> TimeStepModel(1.0), - ModelSpec(MRHardChildModel()) |> TimeStepModel(2.0), - ModelSpec(MRHardConsumerModel()) |> - TimeStepModel(1.0) |> - InputBindings(; A=(process=:mrhardparent, var=:A)), - ), - ) - sim_hard_different_rate_explicit = PlantSimEngine.GraphSimulation( - mtg, - mapping_hard_different_rate_explicit, - nsteps=1, - check=true, - outputs=Dict(:Leaf => (:A, :B)) - ) - @test_throws "Ambiguous canonical publishers" run!(sim_hard_different_rate_explicit, meteo, executor=SequentialEx()) - # Expectation 25: missing producer remains allowed; model can rely on initialized/forced inputs. mapping_missing_input = ModelMapping( :Leaf => ( diff --git a/test/test-multirate-scaffolding.jl b/test/test-multirate-scaffolding.jl index 1312cd3cd..54f188e72 100644 --- a/test/test-multirate-scaffolding.jl +++ b/test/test-multirate-scaffolding.jl @@ -12,9 +12,14 @@ using Test @test output_policy(m) == NamedTuple() @test isnothing(timestep_hint(m)) @test isnothing(meteo_hint(m)) + @test meteo_inputs(m) == () + @test meteo_outputs(m) == () + @test meteo_inputs(ModelSpec(m)) == () + @test meteo_outputs(ModelSpec(m)) == () @test input_bindings(ModelSpec(m)) == NamedTuple() @test output_routing(ModelSpec(m)) == NamedTuple() @test model_scope(ModelSpec(m)) == :global + @test updates(ModelSpec(m)) == () mapping = Dict(:Leaf => (m,)) resolved_specs = resolved_model_specs(mapping) @@ -34,13 +39,19 @@ using Test TimeStepModel(24.0) |> InputBindings(; var1=(process=:process1, var=:var3)) |> OutputRouting(; var3=:stream_only) |> - ScopeModel(:plant) + ScopeModel(:plant) |> + Updates(:var3; after=:process1) |> + Updates(:var3; after=:process2) @test PlantSimEngine.model_(spec) === m @test PlantSimEngine.timestep(spec) == 24.0 @test input_bindings(spec).var1.process == :process1 @test input_bindings(spec).var1.policy isa HoldLast @test output_routing(spec).var3 == :stream_only @test model_scope(spec) == :plant + @test updates(spec)[1].variables == (:var3,) + @test updates(spec)[1].after == (:process1,) + @test updates(spec)[2].variables == (:var3,) + @test updates(spec)[2].after == (:process2,) mspec = ModelSpec(m) |> MultiScaleModel([:var1 => (:Leaf => :var1)]) @test length(PlantSimEngine.get_mapped_variables(mspec)) == 1 diff --git a/test/test-updates.jl b/test/test-updates.jl new file mode 100644 index 000000000..082864926 --- /dev/null +++ b/test/test-updates.jl @@ -0,0 +1,86 @@ +using Dates + +PlantSimEngine.@process "update_carbon_allocation" verbose = false +PlantSimEngine.@process "update_leaf_pruning" verbose = false +PlantSimEngine.@process "update_leaf_senescence" verbose = false +PlantSimEngine.@process "update_biomass_observer" verbose = false + +struct UpdateCarbonAllocationModel <: AbstractUpdate_Carbon_AllocationModel end +PlantSimEngine.inputs_(::UpdateCarbonAllocationModel) = NamedTuple() +PlantSimEngine.outputs_(::UpdateCarbonAllocationModel) = (leaf_biomass=0.0,) +function PlantSimEngine.run!(::UpdateCarbonAllocationModel, models, status, meteo, constants=nothing, extra=nothing) + status.leaf_biomass = 10.0 + return nothing +end + +struct UpdateLeafPruningModel <: AbstractUpdate_Leaf_PruningModel end +PlantSimEngine.inputs_(::UpdateLeafPruningModel) = NamedTuple() +PlantSimEngine.outputs_(::UpdateLeafPruningModel) = (leaf_biomass=0.0,) +function PlantSimEngine.run!(::UpdateLeafPruningModel, models, status, meteo, constants=nothing, extra=nothing) + status.leaf_biomass = 0.0 + return nothing +end + +struct UpdateLeafSenescenceModel <: AbstractUpdate_Leaf_SenescenceModel end +PlantSimEngine.inputs_(::UpdateLeafSenescenceModel) = NamedTuple() +PlantSimEngine.outputs_(::UpdateLeafSenescenceModel) = (leaf_biomass=0.0,) +function PlantSimEngine.run!(::UpdateLeafSenescenceModel, models, status, meteo, constants=nothing, extra=nothing) + status.leaf_biomass *= 0.5 + return nothing +end + +struct UpdateBiomassObserverModel <: AbstractUpdate_Biomass_ObserverModel end +PlantSimEngine.inputs_(::UpdateBiomassObserverModel) = (leaf_biomass=0.0,) +PlantSimEngine.outputs_(::UpdateBiomassObserverModel) = (observed_biomass=0.0,) +function PlantSimEngine.run!(::UpdateBiomassObserverModel, models, status, meteo, constants=nothing, extra=nothing) + status.observed_biomass = status.leaf_biomass + return nothing +end + +@testset "ModelSpec Updates" begin + meteo = Atmosphere(T=20.0, Rh=0.65, Wind=1.0, duration=Dates.Hour(1)) + + @test_throws "Ambiguous canonical writers" ModelMapping( + UpdateCarbonAllocationModel(), + UpdateLeafPruningModel(), + status=(leaf_biomass=1.0,), + ) + + mapping = ModelMapping( + UpdateCarbonAllocationModel(), + ModelSpec(UpdateLeafPruningModel()) |> + Updates(:leaf_biomass; after=:update_carbon_allocation), + UpdateBiomassObserverModel(), + status=(leaf_biomass=1.0, observed_biomass=-1.0), + ) + + outputs = run!(mapping, meteo; executor=SequentialEx()) + @test only(outputs[:leaf_biomass]) == 0.0 + @test only(outputs[:observed_biomass]) == 0.0 + + graph_nodes = PlantSimEngine.traverse_dependency_graph(dep(mapping), false) + pruning_node = only(filter(node -> node.process == :update_leaf_pruning, graph_nodes)) + @test any(parent -> parent.process == :update_carbon_allocation, pruning_node.parent) + + @test_throws "without an ordering relation" ModelMapping( + UpdateCarbonAllocationModel(), + ModelSpec(UpdateLeafPruningModel()) |> + Updates(:leaf_biomass; after=:update_carbon_allocation), + ModelSpec(UpdateLeafSenescenceModel()) |> + Updates(:leaf_biomass; after=:update_carbon_allocation), + status=(leaf_biomass=1.0,), + ) + + ordered_updates = ModelMapping( + UpdateCarbonAllocationModel(), + ModelSpec(UpdateLeafSenescenceModel()) |> + Updates(:leaf_biomass; after=:update_carbon_allocation), + ModelSpec(UpdateLeafPruningModel()) |> + Updates(:leaf_biomass; after=(:update_carbon_allocation, :update_leaf_senescence)), + UpdateBiomassObserverModel(), + status=(leaf_biomass=1.0, observed_biomass=-1.0), + ) + ordered_outputs = run!(ordered_updates, meteo; executor=SequentialEx()) + @test only(ordered_outputs[:leaf_biomass]) == 0.0 + @test only(ordered_outputs[:observed_biomass]) == 0.0 +end From 62b78782149f7037996d983bb5c67fe572473794 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Wed, 3 Jun 2026 17:53:05 +0200 Subject: [PATCH 03/33] Update docstrings and doc --- docs/src/dev/multi_domain_simulation_plan.md | 16 ++++++++++++++-- docs/src/multiscale/multiscale_considerations.md | 2 +- src/domains/domain_simulation.jl | 16 +++++++++------- src/mtg/add_organ.jl | 2 +- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/docs/src/dev/multi_domain_simulation_plan.md b/docs/src/dev/multi_domain_simulation_plan.md index a6f2e41f6..f4aebc1f7 100644 --- a/docs/src/dev/multi_domain_simulation_plan.md +++ b/docs/src/dev/multi_domain_simulation_plan.md @@ -237,9 +237,9 @@ Done when: subtree deletion. Same-simulation reparenting is covered for topology mutation while preserving status and reference identity. -## Current First Example +## Executable Examples -The first executable example should use: +The first executable domain example in `docs/src/domain_simulation.md` uses: - two plant domains with different parameters and several hourly models each; - one soil domain with several hourly models; @@ -248,3 +248,15 @@ The first executable example should use: transpiration and soil evaporation; - `Dates.Hour(1)` for plant/soil domains and `Dates.Day(1)` for the scene model. + +The MAESPA-style example in `examples/maespa_domain_example.jl` exercises the +hard-dependency path: + +- two MTG-backed plant domains with different models and parameters; +- one shared soil domain; +- one scene energy-balance model using `HardDomains(...)`; +- manual calls through `dependency_targets(...)` and `run_target!(...)`; +- trial iterations with `publish=false`, followed by final accepted calls with + `publish=true`; +- hourly scene/plant/soil energy-balance models and daily plant allocation + models. diff --git a/docs/src/multiscale/multiscale_considerations.md b/docs/src/multiscale/multiscale_considerations.md index 091cf32e6..226ff0c96 100644 --- a/docs/src/multiscale/multiscale_considerations.md +++ b/docs/src/multiscale/multiscale_considerations.md @@ -142,7 +142,7 @@ Converting to a dictionary of DataFrame objects can make such queries easier to !!! warning Currently, the `:node` entry only shallow copies nodes. The `:node` values at each scale for every timestep actually reflect the final state of the node, meaning attribute values may not correspond to the value at that timestep. You may need to output these values via a dedicated model to keep track of them properly. - Also note that there currently is no way of removing nodes. Nodes corresponding to organs considered to be pruned/dead/aborted are still present in the output data structure. + Nodes can be removed from an active graph simulation with [`remove_organ!`](@ref), but already-exported outputs are historical records. A pruned/dead/aborted organ may still appear in past output rows, while current statuses and future outputs no longer include it. Multi-scale simulations, especially for plants which have thousands of leaves, internodes, root branches, buds and fruits, may compute huge amounts of data. Just like in single-scale simulations, it is possible to keep only variables whose values you want to track for every timestep, and filter the rest out, using the `tracked_outputs` keyword argument for the [`run!`](@ref) function. diff --git a/src/domains/domain_simulation.jl b/src/domains/domain_simulation.jl index 05bd9488b..c57e66915 100644 --- a/src/domains/domain_simulation.jl +++ b/src/domains/domain_simulation.jl @@ -4,10 +4,11 @@ Reusable model domain used by [`SimulationMapping`](@ref). -This first implementation supports single-status domains backed by existing -`ModelMapping`s. The domain identity is kept alongside scale/process identity -so future multiscale, multi-plant, soil, scene, and environment domains can be -compiled into one simulation without renaming scales. +Domains can wrap either a single-status `ModelMapping` or a scale-keyed +multiscale `ModelMapping` backed by an MTG subtree selected with `selector`. +The domain identity is kept alongside scale/process identity so multi-plant, +soil, scene, and environment domains can be compiled into one simulation +without renaming scales. """ struct Domain{M,S} name::Symbol @@ -819,7 +820,7 @@ function _validate_route_targets(mapping::SimulationMapping, routes::Vector{Rout end target.scale == :Default || error( "Route $(i) target `$(target.domain)/$(target.scale)/$(target.var)` is not supported by the single-status domain runner. ", - "Use `scale=:Default` until MTG-backed domains are enabled." + "Use `scale=:Default` for single-status targets, or target an MTG-backed domain with a supported graph route cardinality." ) st = status(target_mapping) target.var in propertynames(st) || error( @@ -1382,7 +1383,8 @@ end function _materialize_route_value(values::Vector{Any}, cardinality::RouteCardinality) error( "Route cardinality `$(typeof(cardinality))` is declared but not implemented in the single-status domain runner. ", - "Use `ManyToOneVector()` or `ManyToOneAggregate(...)`, or run this route in the future MTG/spatial domain runner." + "Use `ManyToOneVector()` or `ManyToOneAggregate(...)` for single-status targets. ", + "`OneToManyBroadcast()` is supported for MTG-backed target domains." ) end @@ -1391,7 +1393,7 @@ function _set_route_target_value!(simulation::DomainSimulation, route::Route, va domain = _domain_for_name(simulation.mapping, target.domain) target.scale == :Default || error( "Route target `$(target.domain)/$(target.scale)/$(target.var)` is not supported by the single-status domain runner. ", - "Use `scale=:Default` until MTG-backed domains are enabled." + "Use `scale=:Default` for single-status targets, or target an MTG-backed domain with a supported graph route cardinality." ) st = status(simulation, domain.name) target.var in propertynames(st) || error( diff --git a/src/mtg/add_organ.jl b/src/mtg/add_organ.jl index 5ac2c8450..fca4b9fe6 100644 --- a/src/mtg/add_organ.jl +++ b/src/mtg/add_organ.jl @@ -133,7 +133,7 @@ function remove_organ!(node::MultiScaleTreeGraph.Node, sim_object; attribute_nam children = collect(AbstractTrees.children(node)) if !recursive isempty(children) || error( - "remove_organ! currently supports only leaf/terminal MTG nodes. ", + "remove_organ!(...; recursive=false) only supports leaf/terminal MTG nodes. ", "Pass `recursive=true` to delete node $(node_id(node)) and its descendants, ", "or move descendants first." ) From 2e274c384682f6ff8265f2889f62762c9553f945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Wed, 3 Jun 2026 19:55:20 +0200 Subject: [PATCH 04/33] Remove backward compatibility - remove ModelList compatibility - replace Symbol("") mapping sentinels - refactor repeated multirate input-resolution flow - split the large domain runner into scheduler/routes/environment/graph/publication pieces - replace assertions by more standard errors --- docs/make.jl | 1 + docs/src/dev/code_cleanup_audit.md | 92 +++++++++++++++++++ src/dependencies/soft_dependencies.jl | 4 +- src/mtg/GraphSimulation.jl | 4 +- src/mtg/initialisation.jl | 16 +++- src/mtg/mapping/compute_mapping.jl | 38 +++++--- src/mtg/mapping/getters.jl | 4 +- src/mtg/mapping/mapping.jl | 8 +- .../model_generation_from_status_vectors.jl | 12 ++- src/mtg/save_results.jl | 8 +- test/test-simulation.jl | 2 +- 11 files changed, 160 insertions(+), 29 deletions(-) create mode 100644 docs/src/dev/code_cleanup_audit.md diff --git a/docs/make.jl b/docs/make.jl index 8719079f1..6890c07ef 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -83,6 +83,7 @@ makedocs(; "Multi-domain simulation design" => "./dev/multi_domain_simulation_design.md", "Multi-domain simulation plan" => "./dev/multi_domain_simulation_plan.md", "MAESPA-style domain example handoff" => "./dev/maespa_domain_handoff.md", + "Code cleanup audit" => "./dev/code_cleanup_audit.md", ], "Developer guidelines" => "developers.md", "Roadmap" => "planned_features.md", diff --git a/docs/src/dev/code_cleanup_audit.md b/docs/src/dev/code_cleanup_audit.md new file mode 100644 index 000000000..b5607c26b --- /dev/null +++ b/docs/src/dev/code_cleanup_audit.md @@ -0,0 +1,92 @@ +# Code Cleanup Audit + +This page records cleanup candidates found during the multi-domain experimental +branch audit. It is intentionally biased toward code health and release-note +planning rather than immediate implementation. + +Priority meanings: + +- P0: architectural compatibility removal or high-impact breaking cleanup. +- P1: should be handled before stabilizing the new API. +- P2: useful cleanup with moderate risk or blast radius. +- P3: lower-risk cleanup or follow-up once nearby code is touched. + +## Functions With Mergeable Intent + +These functions are not always wrong as separate Julia methods. The cleanup +target is duplicated control flow, not legitimate multiple dispatch. + +| Priority | Functions | Evidence | Recommended cleanup | +| --- | --- | --- | --- | +| P1 | `_resolve_input_windowed`, `_resolve_input_interpolate`, `_resolve_input_holdlast` | `src/time/runtime/input_resolution.jl` around lines 201, 314, and 391 | Keep policy-specific dispatch, but extract the common source-status resolution flow. Policy methods should only decide how to sample a resolved source. | +| P1 | `_normalize_meteo_reducer`, `_resolve_window_reducer` | `src/time/runtime/meteo_sampling.jl` around line 45; `src/time/runtime/input_resolution.jl` around line 114 | Merge into one reducer normalizer with a context argument for error messages. | +| P1 | `validate_meteo_inputs(model_specs, meteo)` and `validate_meteo_inputs(model_specs, backend)` | `src/time/runtime/meteo_sampling.jl` around line 130; `src/time/runtime/environment_backends.jl` around line 115 | Keep dispatch entry points, but share missing-row collection and diagnostic formatting. | +| P2 | `_required_horizon_for_export_policy` and `_required_horizon_for_policy` | `src/time/runtime/output_export.jl` around line 171; `src/time/runtime/publishers.jl` around line 84 | Use a single `_required_horizon_for_policy(policy, consumer_dt, source_dt)` helper. Export code can pass `clock.dt`. | +| P2 | `_normalize_meteo_window` and `_runtime_meteo_window` | `src/mtg/ModelSpec.jl` around line 270; `src/time/runtime/meteo_sampling.jl` around line 13 | Make runtime normalization call the `ModelSpec` normalizer or move both to one shared helper. | +| P2 | Domain environment helpers for single-status and graph domains | `src/domains/domain_simulation.jl` around lines 1478, 1491, 1523, and 1542 | Share `EnvironmentSupport` construction and scatter flow. Keep thin entry points for single-status vs graph-domain differences. | +| P2 | `_should_visit_domain_node` and `_should_publish_domain_key` | `src/domains/domain_simulation.jl` around lines 1601 and 1633 | Extract the shared hard-domain phase rule into one helper. | +| P3 | `Status` and `StatusView` Base interface methods | `src/component_models/Status.jl` around line 70; `src/component_models/StatusView.jl` around line 99 | Do not merge storage-specific methods blindly. If touched, share small private helpers for tuple/key iteration. | +| P3 | Tutorial helper functions repeated in toy multiscale examples | `examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl`; `examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl` | Merge only if examples no longer need to stay standalone. | +| P3 | Test helper flows that run a graph simulation and compare outputs | `test/helper-functions.jl` around lines 49 and 374 | Share a private test runner/comparator if test maintenance becomes painful. | + +## Backward Compatibility To Remove + +This section is the release-note source list. Removing these items is breaking, +and some are still internal dependencies rather than shallow exported shims. + +| Priority | Compatibility surface | Evidence | Migration note | +| --- | --- | --- | --- | +| P0 | `ModelList` public API and legacy backing type | `src/PlantSimEngine.jl`; `src/component_models/ModelList.jl`; `src/mtg/mapping/mapping.jl` | `ModelList` has been removed. Use `ModelMapping(model...; status=..., type_promotion=...)` for single-scale simulations. | +| P0 | `run!(::ModelList, ...)` | `src/run.jl` around lines 139, 284, and 338 | `run!(::ModelList, ...)` has been removed. Wrap models in `ModelMapping` before running. | +| P1 | Batch `run!` for collections of `ModelList` or single-scale mappings | `src/run.jl` around lines 238 and 434; `test/test-simulation.jl` | Batch `run!([mapping1, mapping2], meteo)` and `run!(Dict(...), meteo)` are removed. Use an explicit loop or comprehension and call `run!` per mapping. | +| P1 | `run!(mtg, mapping::AbstractDict, ...)` | `src/run.jl` around line 609 | Passing a raw `Dict` to multiscale `run!` is removed. Construct `ModelMapping(dict)` first, or use `ModelMapping(:Scale => models, ...)`. | +| P1 | String scale names | `src/mtg/mapping/mapping.jl`; `src/mtg/MultiScaleModel.jl`; `src/mtg/model_spec_inference.jl`; `src/time/runtime/bindings.jl` | String scale names are removed. Use symbols everywhere, for example `:Leaf` instead of `"Leaf"`. | +| P2 | `ModelMapping(Float64 => Float32)` as old type-promotion shorthand | `src/mtg/mapping/mapping.jl` around line 381 | `ModelMapping(Float64 => Float32)` is removed. Use `Dict(Float64 => Float32)` as the `type_promotion` value. | +| P2 | Old output indexing helpers on multiscale output dictionaries | `src/mtg/GraphSimulation.jl` around line 144 | `outputs(out_dict, key)` and `outputs(out_dict, i)` are removed. Use `convert_outputs(out_dict, sink)` and index the converted table or dictionary explicitly. | + +Important: full `ModelList` removal is the largest cleanup. `ModelMapping{SingleScale}` +currently delegates to it internally, so complete removal requires a replacement +single-scale backing path in `ModelMapping`, `run!`, dependency/status helpers, +output preallocation, and the domain runtime. + +## Non-Idiomatic Julia Patterns + +| Priority | Pattern | Evidence | Recommended cleanup | +| --- | --- | --- | --- | +| Done | Source-side `@assert` used for user/data validation | Formerly in MTG initialization, mapping, output conversion, and save-result helpers | Converted to explicit `if` checks and `error` messages in this cleanup pass. Remaining `@assert` uses are limited to tests and documentation examples. | +| P2 | `ModelSpec(model::AbstractModel)` checks `model isa MultiScaleModel`, which is effectively dead | `src/mtg/ModelSpec.jl` around lines 79 and 93 | Add a dedicated `ModelSpec(model::MultiScaleModel; ...)` method or remove the dead branch. | +| P2 | `Symbol("")` sentinel for same-scale/no-op mappings | `src/mtg/MultiScaleModel.jl` around line 201; `src/mtg/mapping/mapping.jl` around line 635 | Replace with `nothing` or a typed singleton such as `SameScale()`. | +| P2 | Domain selector detection by type name | `src/dependencies/hard_dependencies.jl` around line 8 | Use dispatch or a trait instead of `nameof(typeof(x)) in (...)`. | +| P2 | Policy handling by large `isa` branch chains | `src/time/runtime/input_resolution.jl` around lines 207, 320, 397, and 544 | Dispatch on policy type and share source-resolution helpers. | +| P3 | Scope selectors accept strings and hard-code built-in scale names | `src/time/runtime/scopes.jl` around lines 31, 63, and 78 | Prefer explicit selector objects/traits, or validate symbols at construction. | +| P3 | Normalizer fallbacks return unknown values unchanged | `src/mtg/ModelSpec.jl` around lines 230, 238, and 302 | Fallback methods should throw `ArgumentError`; support shorthands with explicit methods. | +| P3 | Broad `Any` and anonymous named tuples in runtime storage | `src/mtg/mapping/mapping.jl`; `src/mtg/GraphSimulation.jl`; `src/time/multirate.jl`; `src/time/runtime/output_export.jl` | Introduce small structs or type aliases for mapping metadata, temporal streams, and export plans. | +| P4 | Awkward container signatures with broad `AbstractArray` / verbose `where` clauses | `src/dataframe.jl`; `src/checks/dimensions.jl` | Prefer simpler signatures such as `AbstractVector{<:ModelMapping}` unless N-dimensional arrays are intentional. | + +## Brittle Or Overloaded Code + +| Priority | Location | Risk | Recommended cleanup | +| --- | --- | --- | --- | +| P1 | `src/dependencies/soft_dependencies.jl` hard-dependency redirection | Nested hard-dependency redirection is duplicated, walks parents with a depth cap, and can match by process without enough scale context. | Extract an owner-resolution helper keyed by `(scale, process)` or stable node identity, and validate ownership once. | +| P1 | `src/domains/domain_simulation.jl` runner | Scheduling, routing, environment sampling/scattering, graph runtime lifecycle, output publication, and post-scene phases are all mixed in one large file. | Split into domain scheduler, route materializer, environment bridge, graph-domain runner, and output publisher modules. | +| P2 | `src/time/runtime/input_resolution.jl` fallback resolution | Same-node, ancestor, and candidate-scan fallback can silently change behavior when topology or scope changes. | Build a shared source-status resolver with explicit cardinality outcomes and scalar-ambiguity validation. | +| P2 | `src/mtg/initialisation.jl` `RefVector` population | Vector input order depends on MTG traversal order and can drift after growth/removal. | Make ordering explicit, for example by node id or user-provided policy, and normalize after topology mutations. | +| P2 | `src/mtg/mapping/compute_mapping.jl` and `src/mtg/mapping/mapping.jl` mapping sentinels/invariants | Magic sentinel values make mapping control flow fragile. | Replace sentinels with typed mapping variants. Explicit validation now replaces the former source-side assertions. | +| P2 | Domain run order | Domain order is mostly declaration order with `kind == :scene` last. Route constraints are validated by producer position only. | Compile domains/routes into a small DAG scheduler and detect cycles/phase constraints explicitly. | +| P2 | `src/mtg/add_organ.jl` topology mutation | Add/remove/reparent updates local status and refs, but scope-derived temporal keys and environment indexes need centralized invalidation. | Centralize topology mutations around a single reindex/invalidation step. | +| P2 | `examples/maespa_domain_example.jl` scene model | The example mixes solver math, hard-domain target orchestration, publication, soil feedback, and carbon updates. | Split solver math from domain side effects and add tests for selector mismatch, convergence failure, publication counts, and post-scene feedback. | +| P3 | `src/dependencies/is_graph_cyclic.jl` cycle keys | Cycle detection keys nodes by model value and scale, which can conflate reused model objects. | Key traversal by `(scale, process)` or stable dependency-node identity. | +| P3 | `src/time/runtime/bindings.jl` and input binding inference | Producer candidates can lose renamed source-variable identity. | Preserve source variable in dependency metadata and return `(scale, process, source_var)` candidates. | + +## Suggested Cleanup Order + +1. Remove shallow compatibility shims that do not affect internals: raw dict + multiscale `run!`, string scale names, old output indexing helpers, and old + type-promotion shorthand. +2. Remove dead constructor branches and other shallow non-idiomatic API checks. +3. Refactor low-risk duplicated helpers: horizon policy, reducer normalization, + meteo-window normalization, and domain phase predicates. +4. Refactor input-resolution source lookup behind tests. +5. Replace `ModelList` as the single-scale backing path. +6. Split the domain runner into scheduler, routes, environment, graph-domain, + and publication modules. diff --git a/src/dependencies/soft_dependencies.jl b/src/dependencies/soft_dependencies.jl index ecf36a8bd..377a50294 100644 --- a/src/dependencies/soft_dependencies.jl +++ b/src/dependencies/soft_dependencies.jl @@ -477,7 +477,9 @@ function search_inputs_in_multiscale_output(process, organ, inputs, soft_dep_gra var_organ = [var_organ] end - @assert all(var_o != organ for var_o in var_organ) "$var in process $process is set to be multiscale, but points to its own scale ($organ). This is not allowed." + if !all(var_o != organ for var_o in var_organ) + error("$var in process $process is set to be multiscale, but points to its own scale ($organ). This is not allowed.") + end for org in var_organ # e.g. org = :Leaf # The variable is a multiscale variable: haskey(soft_dep_graphs, org) || error("Scale $org not found in the mapping, but mapped to the $organ scale.") diff --git a/src/mtg/GraphSimulation.jl b/src/mtg/GraphSimulation.jl index 8b826680d..6a8ea15e7 100644 --- a/src/mtg/GraphSimulation.jl +++ b/src/mtg/GraphSimulation.jl @@ -152,6 +152,8 @@ end # ModelLists now return outputs as a TimeStepTable{Status}, conversion is straightforward function convert_outputs(out::TimeStepTable{T} where T, sink) - @assert Tables.istable(sink) "The sink argument must be compatible with the Tables.jl interface (`Tables.istable(sink)` must return `true`, *e.g.* `DataFrame`)" + if !Tables.istable(sink) + error("The sink argument must be compatible with the Tables.jl interface (`Tables.istable(sink)` must return `true`, *e.g.* `DataFrame`)") + end return sink(out) end diff --git a/src/mtg/initialisation.jl b/src/mtg/initialisation.jl index f3fba1bd8..069c166e0 100644 --- a/src/mtg/initialisation.jl +++ b/src/mtg/initialisation.jl @@ -136,10 +136,12 @@ function init_node_status!(node, statuses, mapped_vars, reverse_multiscale_mappi error("Failed to convert variable `$(var)` in MTG node $(node_id(node)) ($(symbol(node))) from type `$(typeof(node[var]))` to type `$(eltype(st_template[var]))`: $(e)") end end - @assert typeof(node_var) == eltype(st_template[var]) string( - "Initializing variable `$(var)` using MTG node $(node_id(node)) ($(symbol(node))): expected type $(eltype(st_template[var])), found $(typeof(node_var)). ", - "Please check the type of the variable in the MTG, and make it a $(eltype(st_template[var])) by updating the model, or by using `type_promotion`." - ) + if typeof(node_var) != eltype(st_template[var]) + error( + "Initializing variable `$(var)` using MTG node $(node_id(node)) ($(symbol(node))): expected type $(eltype(st_template[var])), found $(typeof(node_var)). ", + "Please check the type of the variable in the MTG, and make it a $(eltype(st_template[var])) by updating the model, or by using `type_promotion`." + ) + end st_template[var] = node_var # NB: the variable is not a reference to the value in the MTG, but a copy of it. # This is because we can't reference a value in a Dict. If we need a ref, the user can use a RefValue in the MTG directly, @@ -314,7 +316,11 @@ function init_simulation(mtg, mapping; nsteps=1, outputs=nothing, type_promotion # before we keep going (organ_with_vector, no_vectors_found) = (check_statuses_contain_no_remaining_vectors(mapping)) if !no_vectors_found - @assert false "Error : Mapping status at $organ_with_vector level contains a vector. If this was intentional, call the function generate_models_from_status_vectors on your mapping before calling run!. And bear in mind this is not meant for production. If this wasn't intentional, then it's likely an issue on the mapping definition, or an unusual model." + error( + "Error : Mapping status at $organ_with_vector level contains a vector. ", + "If this was intentional, call the function generate_models_from_status_vectors on your mapping before calling run!. ", + "And bear in mind this is not meant for production. If this wasn't intentional, then it's likely an issue on the mapping definition, or an unusual model." + ) end models = Dict(first(m) => parse_models(get_models(last(m))) for m in mapping) diff --git a/src/mtg/mapping/compute_mapping.jl b/src/mtg/mapping/compute_mapping.jl index a6ccabccf..c4345f0d7 100644 --- a/src/mtg/mapping/compute_mapping.jl +++ b/src/mtg/mapping/compute_mapping.jl @@ -89,10 +89,14 @@ function variables_outputs_from_other_scale(mapped_vars) # The variable is written to several organs, the default value must be a vector: if isa(var_default_value, AbstractVector) # Mapping into a vector of organs, the default value must be a vector: - @assert length(var_default_value) == 1 "The variable `$(mapped_variable(val))` is an output variable computed by scale `$organ` and written to organs at scale `$(join(mapped_organ(val), ", "))`, " * - "but the default value coming from `$organ` is not of length 1: $(var_default_value). " * - "Make sure the model that computes this variable at scale `$organ` has a vector of values of length 1 as " * - "default outputs for variable `$(mapped_variable(val))`." + if length(var_default_value) != 1 + error( + "The variable `$(mapped_variable(val))` is an output variable computed by scale `$organ` and written to organs at scale `$(join(mapped_organ(val), ", "))`, " * + "but the default value coming from `$organ` is not of length 1: $(var_default_value). " * + "Make sure the model that computes this variable at scale `$organ` has a vector of values of length 1 as " * + "default outputs for variable `$(mapped_variable(val))`." + ) + end var_default_value = var_default_value[1] else error( @@ -104,11 +108,15 @@ function variables_outputs_from_other_scale(mapped_vars) end else # The variables is mapped to a single organ, the default value must be a scalar: - @assert !isa(var_default_value, AbstractVector) "The variable `$(mapped_variable(val))` is an output variable computed by scale `$organ` and written to organ at scale `$o`, " * - "but the default value coming from `$organ` is a vector: $(var_default_value). " * - "Make sure the model that computes this variable at scale `$organ` has a scalar value as " * - "default outputs for variable `$(mapped_variable(val))` (*e.g.* $(var_default_value[1])), or update your mapping to map the organ as a vector: " * - """`$(mapped_variable(val)) => ["$o"]`.""" + if isa(var_default_value, AbstractVector) + error( + "The variable `$(mapped_variable(val))` is an output variable computed by scale `$organ` and written to organ at scale `$o`, " * + "but the default value coming from `$organ` is a vector: $(var_default_value). " * + "Make sure the model that computes this variable at scale `$organ` has a scalar value as " * + "default outputs for variable `$(mapped_variable(val))` (*e.g.* $(var_default_value[1])), or update your mapping to map the organ as a vector: " * + """`$(mapped_variable(val)) => ["$o"]`.""" + ) + end end # We make a MappedVar object to declare the variable as an input of this scale: # mapped_var = MappedVar( @@ -166,11 +174,15 @@ function transform_single_node_mapped_variables_as_self_node_output!(mapped_vars if isa(mapped_var, MappedVar{SingleNodeMapping}) source_organ = mapped_organ(mapped_var) source_organ == Symbol("") && continue # We skip the variables that are mapped to themselves (e.g. [PreviousTimeStep(:variable_name)], or just renaming a variable) - @assert source_organ != organ "Variable `$var` is mapped to its own scale in organ $organ. This is not allowed." + if source_organ == organ + error("Variable `$var` is mapped to its own scale in organ $organ. This is not allowed.") + end - @assert haskey(mapped_vars[:outputs], source_organ) "Scale $source_organ not found in the mapping, but mapped to the $organ scale." - @assert haskey(mapped_vars[:outputs][source_organ], source_variable(mapped_var)) "The variable `$(source_variable(mapped_var))` is mapped from scale `$source_organ` to " * - "scale `$organ`, but is not computed by any model at `$source_organ` scale." + haskey(mapped_vars[:outputs], source_organ) || error("Scale $source_organ not found in the mapping, but mapped to the $organ scale.") + haskey(mapped_vars[:outputs][source_organ], source_variable(mapped_var)) || error( + "The variable `$(source_variable(mapped_var))` is mapped from scale `$source_organ` to " * + "scale `$organ`, but is not computed by any model at `$source_organ` scale." + ) # If the source variable was already defined as a `MappedVar{SelfNodeMapping}` by another scale, we skip it: isa(mapped_vars[:outputs][source_organ][source_variable(mapped_var)], MappedVar{SelfNodeMapping}) && continue diff --git a/src/mtg/mapping/getters.jl b/src/mtg/mapping/getters.jl index c9512c7d2..1b9b4a2b0 100644 --- a/src/mtg/mapping/getters.jl +++ b/src/mtg/mapping/getters.jl @@ -102,7 +102,9 @@ See [`get_models`](@ref) for examples. """ function get_status(m) st = Status[i for i in m if isa(i, Status)] - @assert length(st) <= 1 "Only one status can be provided for each organ type." + if length(st) > 1 + error("Only one status can be provided for each organ type.") + end length(st) == 0 && return nothing return first(st) end diff --git a/src/mtg/mapping/mapping.jl b/src/mtg/mapping/mapping.jl index 51b34efd4..0ac36f9e9 100755 --- a/src/mtg/mapping/mapping.jl +++ b/src/mtg/mapping/mapping.jl @@ -96,12 +96,16 @@ mapped_organ(m::MappedVar{O,V1,V2,T}) where {O<:SelfNodeMapping,V1,V2,T} = nothi mapped_organ_type(m::MappedVar{O,V1,V2,T}) where {O<:AbstractNodeMapping,V1,V2,T} = O source_variable(m::MappedVar) = m.source_variable function source_variable(m::MappedVar{O,V1,V2,T}, organ) where {O<:SingleNodeMapping,V1,V2<:Symbol,T} - @assert organ == mapped_organ(m) "Organ $organ not found in the mapping of the variable $(mapped_variable(m))." + if organ != mapped_organ(m) + error("Organ $organ not found in the mapping of the variable $(mapped_variable(m)).") + end m.source_variable end function source_variable(m::MappedVar{O,V1,V2,T}, organ) where {O<:MultiNodeMapping,V1,V2<:Vector{Symbol},T} - @assert organ in mapped_organ(m) "Organ $organ not found in the mapping of the variable $(mapped_variable(m))." + if !(organ in mapped_organ(m)) + error("Organ $organ not found in the mapping of the variable $(mapped_variable(m)).") + end m.source_variable[findfirst(o -> o == organ, mapped_organ(m))] end diff --git a/src/mtg/mapping/model_generation_from_status_vectors.jl b/src/mtg/mapping/model_generation_from_status_vectors.jl index 76cb22901..694de3018 100644 --- a/src/mtg/mapping/model_generation_from_status_vectors.jl +++ b/src/mtg/mapping/model_generation_from_status_vectors.jl @@ -144,9 +144,13 @@ function generate_model_from_status_vector_variable(mapping, timestep_scale, sta for symbol in keys(status) value = getproperty(status, symbol) if isa(value, AbstractVector) - @assert length(value) > 0 "Error during generation of models from vector values provided at the $organ-level status : provided $symbol vector is empty" + if length(value) == 0 + error("Error during generation of models from vector values provided at the $organ-level status : provided $symbol vector is empty") + end # TODO : Might need to fiddle with timesteps here in the future in case of varying timestep models - @assert nsteps == length(value) "Error during generation of models from vector values provided at the $organ-level status : provided $symbol vector length doesn't match the expected # of timesteps" + if nsteps != length(value) + error("Error during generation of models from vector values provided at the $organ-level status : provided $symbol vector length doesn't match the expected # of timesteps") + end process_name = Symbol(lowercase(string(symbol) * bytes2hex(sha1(repr(value))))) model = GeneratedStatusVectorModel(process_name, symbol, value) @@ -172,7 +176,9 @@ function generate_model_from_status_vector_variable(mapping, timestep_scale, sta new_status = Status(NamedTuple{Tuple(new_status_names)}(Tuple(new_status_values))) generated_models_tuple = Tuple(generated_models) - @assert length(status) == length(new_status) + length(generated_models_tuple) "Error during generation of models from vector values provided at the $organ-level status" + if length(status) != length(new_status) + length(generated_models_tuple) + error("Error during generation of models from vector values provided at the $organ-level status") + end return new_status, generated_models_tuple end diff --git a/src/mtg/save_results.jl b/src/mtg/save_results.jl index 8230f534a..f403fda60 100644 --- a/src/mtg/save_results.jl +++ b/src/mtg/save_results.jl @@ -126,7 +126,9 @@ function pre_allocate_outputs(statuses, statuses_template, reverse_multiscale_ma else for i in keys(outs) # i = :Plant i isa Symbol || error("Output scale keys must be `Symbol`, got `$(typeof(i))` for key `$(repr(i))`.") - @assert isa(outs[i], Tuple{Vararg{Symbol}}) """Outputs for scale $i should be a tuple of symbols, *e.g.* `$i => (:a, :b)`, found `$i => $(outs[i])` instead.""" + if !isa(outs[i], Tuple{Vararg{Symbol}}) + error("""Outputs for scale $i should be a tuple of symbols, *e.g.* `$i => (:a, :b)`, found `$i => $(outs[i])` instead.""") + end outs_[i] = [outs[i]...] end end @@ -207,7 +209,9 @@ function pre_allocate_outputs(statuses, statuses_template, reverse_multiscale_ma end node_type = unique(node_types) - @assert length(node_type) == 1 "All plant graph nodes should have the same type, found $(unique(node_type))." + if length(node_type) != 1 + error("All plant graph nodes should have the same type, found $(unique(node_type)).") + end node_type = only(node_type) # I don't know if this function barrier is necessary diff --git a/test/test-simulation.jl b/test/test-simulation.jl index c1a2fb8cc..1086c0b2a 100644 --- a/test/test-simulation.jl +++ b/test/test-simulation.jl @@ -238,7 +238,7 @@ end; ) # var1 is taken from the MTG attributes but is a vector instead of a scalar, expecting an error: - VERSION >= v"1.8" && @test_throws AssertionError run!(mtg, mapping, meteo) + @test_throws ErrorException run!(mtg, mapping, meteo) leaf[:var1] = 15.0 From 236534ec297d46bfb0256d809e47843860c6ada2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Thu, 4 Jun 2026 13:00:48 +0200 Subject: [PATCH 05/33] Another large cleanup Shared Status / StatusView interface helpers. Extracted repeated test graph comparison runner. Extracted toy tutorial MTG helpers. Typed export plans, reverse mappings, temporal stream aliases. Preserved renamed producer source variables in binding inference. Added explicit domain DAG run ordering. Added topology reindexing for RefVectors after add/remove/reparent. Split MAESPA scene solver math from side effects and added validation tests. --- docs/src/dev/code_cleanup_audit.md | 77 +- .../ToyPlantHelpers.jl | 14 + .../ToyPlantSimulation2.jl | 16 +- .../ToyPlantSimulation3.jl | 16 +- src/PlantSimEngine.jl | 3 +- src/checks/dimensions.jl | 6 +- src/component_models/ModelList.jl | 11 +- src/component_models/Status.jl | 25 +- src/component_models/StatusView.jl | 16 +- src/dataframe.jl | 6 +- src/dependencies/dependency_graph.jl | 15 + src/dependencies/hard_dependencies.jl | 19 +- src/dependencies/is_graph_cyclic.jl | 34 +- src/dependencies/soft_dependencies.jl | 130 ++-- src/domains/domain_simulation.jl | 190 +++-- src/mtg/GraphSimulation.jl | 11 +- src/mtg/ModelSpec.jl | 51 +- src/mtg/MultiScaleModel.jl | 48 +- src/mtg/add_organ.jl | 23 +- src/mtg/initialisation.jl | 78 ++ src/mtg/mapping/compute_mapping.jl | 11 +- src/mtg/mapping/mapping.jl | 96 +-- .../model_generation_from_status_vectors.jl | 9 +- src/mtg/mapping/reverse_mapping.jl | 7 +- src/mtg/model_spec_inference.jl | 26 +- src/mtg/model_spec_validation.jl | 10 +- src/mtg/node_mapping_types.jl | 15 + src/run.jl | 193 +---- src/time/multirate.jl | 27 +- src/time/runtime/bindings.jl | 26 +- src/time/runtime/environment_backends.jl | 30 +- src/time/runtime/input_resolution.jl | 724 ++++++++++++------ src/time/runtime/meteo_sampling.jl | 99 ++- src/time/runtime/output_export.jl | 53 +- src/time/runtime/publishers.jl | 10 +- src/time/runtime/scopes.jl | 7 +- test/helper-functions.jl | 34 +- test/test-MultiScaleModel.jl | 6 +- test/test-domain-simulation.jl | 44 +- test/test-maespa-domain-example.jl | 30 + test/test-meteo-traits.jl | 2 +- test/test-mtg-dynamic.jl | 6 + test/test-mtg-multiscale-cyclic-dep.jl | 2 +- test/test-multirate-output-export.jl | 2 +- test/test-multirate-runtime.jl | 15 + test/test-multirate-scaffolding.jl | 11 + test/test-simulation.jl | 19 +- 47 files changed, 1325 insertions(+), 978 deletions(-) create mode 100644 examples/ToyMultiScalePlantTutorial/ToyPlantHelpers.jl create mode 100644 src/mtg/node_mapping_types.jl diff --git a/docs/src/dev/code_cleanup_audit.md b/docs/src/dev/code_cleanup_audit.md index b5607c26b..27d69677a 100644 --- a/docs/src/dev/code_cleanup_audit.md +++ b/docs/src/dev/code_cleanup_audit.md @@ -18,16 +18,16 @@ target is duplicated control flow, not legitimate multiple dispatch. | Priority | Functions | Evidence | Recommended cleanup | | --- | --- | --- | --- | -| P1 | `_resolve_input_windowed`, `_resolve_input_interpolate`, `_resolve_input_holdlast` | `src/time/runtime/input_resolution.jl` around lines 201, 314, and 391 | Keep policy-specific dispatch, but extract the common source-status resolution flow. Policy methods should only decide how to sample a resolved source. | -| P1 | `_normalize_meteo_reducer`, `_resolve_window_reducer` | `src/time/runtime/meteo_sampling.jl` around line 45; `src/time/runtime/input_resolution.jl` around line 114 | Merge into one reducer normalizer with a context argument for error messages. | -| P1 | `validate_meteo_inputs(model_specs, meteo)` and `validate_meteo_inputs(model_specs, backend)` | `src/time/runtime/meteo_sampling.jl` around line 130; `src/time/runtime/environment_backends.jl` around line 115 | Keep dispatch entry points, but share missing-row collection and diagnostic formatting. | -| P2 | `_required_horizon_for_export_policy` and `_required_horizon_for_policy` | `src/time/runtime/output_export.jl` around line 171; `src/time/runtime/publishers.jl` around line 84 | Use a single `_required_horizon_for_policy(policy, consumer_dt, source_dt)` helper. Export code can pass `clock.dt`. | -| P2 | `_normalize_meteo_window` and `_runtime_meteo_window` | `src/mtg/ModelSpec.jl` around line 270; `src/time/runtime/meteo_sampling.jl` around line 13 | Make runtime normalization call the `ModelSpec` normalizer or move both to one shared helper. | -| P2 | Domain environment helpers for single-status and graph domains | `src/domains/domain_simulation.jl` around lines 1478, 1491, 1523, and 1542 | Share `EnvironmentSupport` construction and scatter flow. Keep thin entry points for single-status vs graph-domain differences. | -| P2 | `_should_visit_domain_node` and `_should_publish_domain_key` | `src/domains/domain_simulation.jl` around lines 1601 and 1633 | Extract the shared hard-domain phase rule into one helper. | -| P3 | `Status` and `StatusView` Base interface methods | `src/component_models/Status.jl` around line 70; `src/component_models/StatusView.jl` around line 99 | Do not merge storage-specific methods blindly. If touched, share small private helpers for tuple/key iteration. | -| P3 | Tutorial helper functions repeated in toy multiscale examples | `examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl`; `examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl` | Merge only if examples no longer need to stay standalone. | -| P3 | Test helper flows that run a graph simulation and compare outputs | `test/helper-functions.jl` around lines 49 and 374 | Share a private test runner/comparator if test maintenance becomes painful. | +| Done | `_resolve_input_windowed`, `_resolve_input_interpolate`, `_resolve_input_holdlast` | `src/time/runtime/input_resolution.jl` | Policy-specific wrappers now delegate to `_resolve_input_from_policy!`, with shared vector/scalar source lookup and policy-specific sampling dispatch. | +| Done | `_normalize_meteo_reducer`, `_resolve_window_reducer` | `src/time/runtime/meteo_sampling.jl`; `src/time/runtime/input_resolution.jl` | Merged through `_normalize_time_reducer(...; context=...)`. | +| Done | `validate_meteo_inputs(model_specs, meteo)` and `validate_meteo_inputs(model_specs, backend)` | `src/time/runtime/meteo_sampling.jl`; `src/time/runtime/environment_backends.jl` | Shared missing-row collection and diagnostic formatting through `_collect_missing_meteo_rows` and `_error_missing_meteo_inputs`. | +| Done | `_required_horizon_for_export_policy` and `_required_horizon_for_policy` | `src/time/runtime/output_export.jl`; `src/time/runtime/publishers.jl` | Export code now delegates to `_required_horizon_for_policy(policy, clock.dt, source_dt)`. | +| Done | `_normalize_meteo_window` and `_runtime_meteo_window` | `src/mtg/ModelSpec.jl`; `src/time/runtime/meteo_sampling.jl` | Runtime meteo-window normalization now calls `_normalize_meteo_window`. | +| Done | Domain environment helpers for single-status and graph domains | `src/domains/domain_simulation.jl` | Shared `EnvironmentSupport` construction, environment sampling, and scatter flow while keeping thin single-status and graph-domain entry points. | +| Done | `_should_visit_domain_node` and `_should_publish_domain_key` | `src/domains/domain_simulation.jl` | Extracted `_phase_allows_hard_parent` for the shared hard-domain phase rule. | +| Done | `Status` and `StatusView` Base interface methods | `src/component_models/Status.jl`; `src/component_models/StatusView.jl` | Shared small private helpers for values, tuple conversion, iteration, and indexed iteration while keeping storage-specific access and mutation methods separate. | +| Done | Tutorial helper functions repeated in toy multiscale examples | `examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl`; `examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl` | Extracted shared MTG navigation helpers to `examples/ToyMultiScalePlantTutorial/ToyPlantHelpers.jl` and included it with `@__DIR__` so tutorial scripts remain standalone. | +| Done | Test helper flows that run a graph simulation and compare outputs | `test/helper-functions.jl` | Shared `run_graphsim_for_comparison` for generated multiscale comparison and filtered-output comparison paths. | ## Backward Compatibility To Remove @@ -37,12 +37,12 @@ and some are still internal dependencies rather than shallow exported shims. | Priority | Compatibility surface | Evidence | Migration note | | --- | --- | --- | --- | | P0 | `ModelList` public API and legacy backing type | `src/PlantSimEngine.jl`; `src/component_models/ModelList.jl`; `src/mtg/mapping/mapping.jl` | `ModelList` has been removed. Use `ModelMapping(model...; status=..., type_promotion=...)` for single-scale simulations. | -| P0 | `run!(::ModelList, ...)` | `src/run.jl` around lines 139, 284, and 338 | `run!(::ModelList, ...)` has been removed. Wrap models in `ModelMapping` before running. | -| P1 | Batch `run!` for collections of `ModelList` or single-scale mappings | `src/run.jl` around lines 238 and 434; `test/test-simulation.jl` | Batch `run!([mapping1, mapping2], meteo)` and `run!(Dict(...), meteo)` are removed. Use an explicit loop or comprehension and call `run!` per mapping. | -| P1 | `run!(mtg, mapping::AbstractDict, ...)` | `src/run.jl` around line 609 | Passing a raw `Dict` to multiscale `run!` is removed. Construct `ModelMapping(dict)` first, or use `ModelMapping(:Scale => models, ...)`. | -| P1 | String scale names | `src/mtg/mapping/mapping.jl`; `src/mtg/MultiScaleModel.jl`; `src/mtg/model_spec_inference.jl`; `src/time/runtime/bindings.jl` | String scale names are removed. Use symbols everywhere, for example `:Leaf` instead of `"Leaf"`. | -| P2 | `ModelMapping(Float64 => Float32)` as old type-promotion shorthand | `src/mtg/mapping/mapping.jl` around line 381 | `ModelMapping(Float64 => Float32)` is removed. Use `Dict(Float64 => Float32)` as the `type_promotion` value. | -| P2 | Old output indexing helpers on multiscale output dictionaries | `src/mtg/GraphSimulation.jl` around line 144 | `outputs(out_dict, key)` and `outputs(out_dict, i)` are removed. Use `convert_outputs(out_dict, sink)` and index the converted table or dictionary explicitly. | +| Done | `run!(::ModelList, ...)` | Formerly in `src/run.jl` direct `ModelList` methods | `run!(::ModelList, ...)` has been removed. Wrap models in `ModelMapping` before running. | +| Done | Batch `run!` for collections of `ModelList` or single-scale mappings | Formerly in `src/run.jl` collection methods | Batch `run!([mapping1, mapping2], meteo)` and `run!(Dict(...), meteo)` are removed. Use an explicit loop or comprehension and call `run!` per mapping. | +| Done | `run!(mtg, mapping::AbstractDict, ...)` | Formerly in `src/run.jl` | Passing a raw `Dict` to multiscale `run!` is removed. Construct `ModelMapping(dict)` first, or use `ModelMapping(:Scale => models, ...)`. | +| Done | String scale names | `src/mtg/mapping/mapping.jl`; `src/mtg/MultiScaleModel.jl`; `src/mtg/model_spec_inference.jl`; `src/time/runtime/bindings.jl` | String scale names are removed. Use symbols everywhere, for example `:Leaf` instead of `"Leaf"`. | +| Done | `ModelMapping(Float64 => Float32)` as old type-promotion shorthand | Formerly in `src/mtg/mapping/mapping.jl` | `ModelMapping(Float64 => Float32)` is removed. Use `Dict(Float64 => Float32)` as the `type_promotion` value. | +| Done | Old output indexing helpers on multiscale output dictionaries | Formerly in `src/mtg/GraphSimulation.jl` | `outputs(out_dict, key)` and `outputs(out_dict, i)` are removed. Use `convert_outputs(out_dict, sink)` and index the converted table or dictionary explicitly. | Important: full `ModelList` removal is the largest cleanup. `ModelMapping{SingleScale}` currently delegates to it internally, so complete removal requires a replacement @@ -54,39 +54,32 @@ output preallocation, and the domain runtime. | Priority | Pattern | Evidence | Recommended cleanup | | --- | --- | --- | --- | | Done | Source-side `@assert` used for user/data validation | Formerly in MTG initialization, mapping, output conversion, and save-result helpers | Converted to explicit `if` checks and `error` messages in this cleanup pass. Remaining `@assert` uses are limited to tests and documentation examples. | -| P2 | `ModelSpec(model::AbstractModel)` checks `model isa MultiScaleModel`, which is effectively dead | `src/mtg/ModelSpec.jl` around lines 79 and 93 | Add a dedicated `ModelSpec(model::MultiScaleModel; ...)` method or remove the dead branch. | -| P2 | `Symbol("")` sentinel for same-scale/no-op mappings | `src/mtg/MultiScaleModel.jl` around line 201; `src/mtg/mapping/mapping.jl` around line 635 | Replace with `nothing` or a typed singleton such as `SameScale()`. | -| P2 | Domain selector detection by type name | `src/dependencies/hard_dependencies.jl` around line 8 | Use dispatch or a trait instead of `nameof(typeof(x)) in (...)`. | -| P2 | Policy handling by large `isa` branch chains | `src/time/runtime/input_resolution.jl` around lines 207, 320, 397, and 544 | Dispatch on policy type and share source-resolution helpers. | -| P3 | Scope selectors accept strings and hard-code built-in scale names | `src/time/runtime/scopes.jl` around lines 31, 63, and 78 | Prefer explicit selector objects/traits, or validate symbols at construction. | -| P3 | Normalizer fallbacks return unknown values unchanged | `src/mtg/ModelSpec.jl` around lines 230, 238, and 302 | Fallback methods should throw `ArgumentError`; support shorthands with explicit methods. | -| P3 | Broad `Any` and anonymous named tuples in runtime storage | `src/mtg/mapping/mapping.jl`; `src/mtg/GraphSimulation.jl`; `src/time/multirate.jl`; `src/time/runtime/output_export.jl` | Introduce small structs or type aliases for mapping metadata, temporal streams, and export plans. | -| P4 | Awkward container signatures with broad `AbstractArray` / verbose `where` clauses | `src/dataframe.jl`; `src/checks/dimensions.jl` | Prefer simpler signatures such as `AbstractVector{<:ModelMapping}` unless N-dimensional arrays are intentional. | +| Done | `ModelSpec(model::AbstractModel)` checks `model isa MultiScaleModel`, which is effectively dead | `src/mtg/ModelSpec.jl` | Added an explicit `ModelSpec(::MultiScaleModel)` method and removed the dead branch from the `AbstractModel` constructor. | +| Done | `Symbol("")` sentinel for same-scale/no-op mappings | `src/mtg/MultiScaleModel.jl`; `src/mtg/mapping/mapping.jl`; `src/dependencies/dependency_graph.jl` | Replaced the magic sentinel with the typed `SameScale()` marker and reject `Symbol("")` in new mappings. | +| Done | Domain selector detection by type name | `src/dependencies/hard_dependencies.jl`; `src/domains/domain_simulation.jl` | Added `AbstractDomainDependencySelector`; `AllDomains` and `HardDomains` subtype it, and detection now uses the marker type instead of type-name reflection. | +| Done | Policy handling by large `isa` branch chains | `src/time/runtime/input_resolution.jl` | Input resolution now dispatches on policy type through `_resolved_policy_value_for_source` and `_resolve_input_for_policy!`, with shared source-resolution helpers. | +| Done | Scope selectors accept strings and hard-code built-in scale names | `src/time/runtime/scopes.jl`; `src/mtg/ModelSpec.jl` | Scope selectors now reject strings at construction and runtime callable results must return `ScopeId` or `Symbol`. Built-in selector symbols remain explicit and validated. | +| Done | Normalizer fallbacks return unknown values unchanged | `src/mtg/ModelSpec.jl` | Fallbacks for input bindings, meteo bindings, and output routing now fail at construction with explicit errors. String scope selectors now error instead of being converted. | +| Done | Broad `Any` and anonymous named tuples in runtime storage | `src/mtg/mapping/mapping.jl`; `src/mtg/GraphSimulation.jl`; `src/time/multirate.jl`; `src/time/runtime/output_export.jl` | Added semantic aliases for model-rate declarations and temporal streams, typed reverse multiscale mappings as `Symbol => Symbol`, and replaced anonymous export-plan named tuples with `OutputExportPlan`. Remaining `Any` storage is for user-provided values/statuses and open extension hooks. | +| Done | Awkward container signatures with broad `AbstractArray` / verbose `where` clauses | `src/dataframe.jl`; `src/checks/dimensions.jl` | Simplified collection signatures to `AbstractVector`/`AbstractDict` forms without unnecessary `where` wrappers. | ## Brittle Or Overloaded Code | Priority | Location | Risk | Recommended cleanup | | --- | --- | --- | --- | -| P1 | `src/dependencies/soft_dependencies.jl` hard-dependency redirection | Nested hard-dependency redirection is duplicated, walks parents with a depth cap, and can match by process without enough scale context. | Extract an owner-resolution helper keyed by `(scale, process)` or stable node identity, and validate ownership once. | +| Done | `src/dependencies/soft_dependencies.jl` hard-dependency redirection | Nested hard-dependency redirection is duplicated, walks parents with a depth cap, and can match by process without enough scale context. | Extracted shared owner-resolution helpers for nested hard dependencies, with scale-aware matching, ambiguity checks, and finalized soft-node validation. | | P1 | `src/domains/domain_simulation.jl` runner | Scheduling, routing, environment sampling/scattering, graph runtime lifecycle, output publication, and post-scene phases are all mixed in one large file. | Split into domain scheduler, route materializer, environment bridge, graph-domain runner, and output publisher modules. | -| P2 | `src/time/runtime/input_resolution.jl` fallback resolution | Same-node, ancestor, and candidate-scan fallback can silently change behavior when topology or scope changes. | Build a shared source-status resolver with explicit cardinality outcomes and scalar-ambiguity validation. | -| P2 | `src/mtg/initialisation.jl` `RefVector` population | Vector input order depends on MTG traversal order and can drift after growth/removal. | Make ordering explicit, for example by node id or user-provided policy, and normalize after topology mutations. | -| P2 | `src/mtg/mapping/compute_mapping.jl` and `src/mtg/mapping/mapping.jl` mapping sentinels/invariants | Magic sentinel values make mapping control flow fragile. | Replace sentinels with typed mapping variants. Explicit validation now replaces the former source-side assertions. | -| P2 | Domain run order | Domain order is mostly declaration order with `kind == :scene` last. Route constraints are validated by producer position only. | Compile domains/routes into a small DAG scheduler and detect cycles/phase constraints explicitly. | -| P2 | `src/mtg/add_organ.jl` topology mutation | Add/remove/reparent updates local status and refs, but scope-derived temporal keys and environment indexes need centralized invalidation. | Centralize topology mutations around a single reindex/invalidation step. | -| P2 | `examples/maespa_domain_example.jl` scene model | The example mixes solver math, hard-domain target orchestration, publication, soil feedback, and carbon updates. | Split solver math from domain side effects and add tests for selector mismatch, convergence failure, publication counts, and post-scene feedback. | -| P3 | `src/dependencies/is_graph_cyclic.jl` cycle keys | Cycle detection keys nodes by model value and scale, which can conflate reused model objects. | Key traversal by `(scale, process)` or stable dependency-node identity. | -| P3 | `src/time/runtime/bindings.jl` and input binding inference | Producer candidates can lose renamed source-variable identity. | Preserve source variable in dependency metadata and return `(scale, process, source_var)` candidates. | +| Done | `src/time/runtime/input_resolution.jl` fallback resolution | Same-node, ancestor, and candidate-scan fallback can silently change behavior when topology or scope changes. | Built a shared source-status resolver that centralizes same-node, ancestor, vector, and unique-candidate fallback with scalar ambiguity validation. | +| Done | `src/mtg/initialisation.jl` `RefVector` population | Vector input order depends on MTG traversal order and can drift after growth/removal. | Added `reindex_runtime_topology!` to sort statuses by MTG node id and rebuild downstream `RefVector`s from current statuses after initialization and topology mutations. | +| Done | `src/mtg/mapping/compute_mapping.jl` and `src/mtg/mapping/mapping.jl` mapping sentinels/invariants | Magic sentinel values make mapping control flow fragile. | Same-scale mappings now use `SameScale()` instead of `Symbol("")`; explicit validation rejects the old sentinel. | +| Done | Domain run order | Domain order is mostly declaration order with `kind == :scene` last. Route constraints are validated by producer position only. | Added a stable domain DAG scheduler with declaration-order tie-breaking, explicit scene-phase edges, route producer-target edges, and cycle diagnostics. | +| Done | `src/mtg/add_organ.jl` topology mutation | Add/remove/reparent updates local status and refs, but scope-derived temporal keys and environment indexes need centralized invalidation. | Add/remove/reparent now centralize runtime topology reindexing; reparent clears temporal state for the moved subtree before rebuilding status and `RefVector` indexes. | +| Done | `examples/maespa_domain_example.jl` scene model | The example mixes solver math, hard-domain target orchestration, publication, soil feedback, and carbon updates. | Split scene energy-balance solving, leaf publication/carbon update, and soil feedback into separate helpers; added tests for selector mismatch, convergence failure, publication counts, and soil feedback. | +| Done | `src/dependencies/is_graph_cyclic.jl` cycle keys | Cycle detection keys nodes by model value and scale, which can conflate reused model objects. | Traversal now uses dependency-node identity through `IdDict`; cycle reports are converted back to `(model => scale)` for existing diagnostics. | +| Done | `src/time/runtime/bindings.jl` and input binding inference | Producer candidates can lose renamed source-variable identity. | Added `ProducerVariable(input, source)` dependency metadata for renamed multiscale producers, and candidate inference now matches on the consumer input while returning the producer source variable. | ## Suggested Cleanup Order -1. Remove shallow compatibility shims that do not affect internals: raw dict - multiscale `run!`, string scale names, old output indexing helpers, and old - type-promotion shorthand. -2. Remove dead constructor branches and other shallow non-idiomatic API checks. -3. Refactor low-risk duplicated helpers: horizon policy, reducer normalization, - meteo-window normalization, and domain phase predicates. -4. Refactor input-resolution source lookup behind tests. -5. Replace `ModelList` as the single-scale backing path. -6. Split the domain runner into scheduler, routes, environment, graph-domain, +1. Replace `ModelList` as the single-scale backing path. +2. Split the domain runner into scheduler, routes, environment, graph-domain, and publication modules. diff --git a/examples/ToyMultiScalePlantTutorial/ToyPlantHelpers.jl b/examples/ToyMultiScalePlantTutorial/ToyPlantHelpers.jl new file mode 100644 index 000000000..d8c36fdd7 --- /dev/null +++ b/examples/ToyMultiScalePlantTutorial/ToyPlantHelpers.jl @@ -0,0 +1,14 @@ +function get_root_end_node(node::MultiScaleTreeGraph.Node) + root = MultiScaleTreeGraph.get_root(node) + return MultiScaleTreeGraph.traverse(root, x -> x, symbol=:Root, filter_fun=MultiScaleTreeGraph.isleaf) +end + +function get_roots_count(node::MultiScaleTreeGraph.Node) + root = MultiScaleTreeGraph.get_root(node) + return length(MultiScaleTreeGraph.traverse(root, x -> x, symbol=:Root)) +end + +function get_n_leaves(node::MultiScaleTreeGraph.Node) + root = MultiScaleTreeGraph.get_root(node) + return length(MultiScaleTreeGraph.traverse(root, x -> 1, symbol=:Leaf)) +end diff --git a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl b/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl index 15b3ec356..d6e713c22 100644 --- a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl +++ b/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl @@ -6,21 +6,7 @@ # But it should illustrate the basics of simulating a growing multiscale plant with PlantSimEngine's model approach ########################################### -function get_root_end_node(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - return MultiScaleTreeGraph.traverse(root, x -> x, symbol=:Root, filter_fun=MultiScaleTreeGraph.isleaf) -end - -function get_roots_count(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - return length(MultiScaleTreeGraph.traverse(root, x -> x, symbol=:Root)) -end - -function get_n_leaves(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - nleaves = length(MultiScaleTreeGraph.traverse(root, x -> 1, symbol=:Leaf)) - return nleaves -end +include(joinpath(@__DIR__, "ToyPlantHelpers.jl")) PlantSimEngine.@process "organ_emergence" verbose = false diff --git a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl b/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl index bdd4071b5..6c305cd5b 100644 --- a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl +++ b/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl @@ -6,21 +6,7 @@ # But it should illustrate the basics of simulating a growing multiscale plant with PlantSimEngine's model approach ########################################### -function get_root_end_node(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - return MultiScaleTreeGraph.traverse(root, x -> x, symbol=:Root, filter_fun=MultiScaleTreeGraph.isleaf) -end - -function get_roots_count(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - return length(MultiScaleTreeGraph.traverse(root, x -> x, symbol=:Root)) -end - -function get_n_leaves(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - nleaves = length(MultiScaleTreeGraph.traverse(root, x -> 1, symbol=:Leaf)) - return nleaves -end +include(joinpath(@__DIR__, "ToyPlantHelpers.jl")) PlantSimEngine.@process "organ_emergence" verbose = false diff --git a/src/PlantSimEngine.jl b/src/PlantSimEngine.jl index 0d5397170..d578e67b7 100644 --- a/src/PlantSimEngine.jl +++ b/src/PlantSimEngine.jl @@ -36,6 +36,7 @@ include("doc_templates/mtg-related.jl") # Models: include("Abstract_model_structs.jl") +include("mtg/node_mapping_types.jl") # Multi-rate scaffolding: include("time/multirate.jl") @@ -130,7 +131,7 @@ export OutputCache, HoldLastCache, InterpolateCache, IntegrateCache, AggregateCa export TemporalState export OutputRequest, collect_outputs export effective_rate_summary -export ModelList, MultiScaleModel, ModelMapping, ModelSpec, Updates, TimeStepModel, InputBindings, MeteoBindings, MeteoWindow, OutputRouting, ScopeModel +export ModelList, MultiScaleModel, ModelMapping, ModelSpec, SameScale, Updates, TimeStepModel, InputBindings, MeteoBindings, MeteoWindow, OutputRouting, ScopeModel export resolved_model_specs, explain_model_specs export Domain, SimulationMapping, DomainSimulation, DomainModelKey, AllDomains, HardDomains export Route, DomainRouteTarget, RouteCardinality diff --git a/src/checks/dimensions.jl b/src/checks/dimensions.jl index 6cc929113..711f4e233 100644 --- a/src/checks/dimensions.jl +++ b/src/checks/dimensions.jl @@ -43,7 +43,7 @@ ERROR: DimensionMismatch: Component status has a vector variable : var1 implying check_dimensions(component, weather) = check_dimensions(DataFormat(weather), component, weather) # Here we add methods for applying to a component, an array or a dict of: -function check_dimensions(component::T, w) where {T<:ModelList} +function check_dimensions(component::ModelList, w) check_dimensions(status(component), w) end @@ -52,14 +52,14 @@ function check_dimensions(component::ModelMapping{SingleScale}, w) end # for several components as an array -function check_dimensions(component::T, weather) where {T<:AbstractArray{<:ModelList}} +function check_dimensions(component::AbstractVector{<:ModelList}, weather) for i in component check_dimensions(i, weather) end end # for several components as a Dict -function check_dimensions(component::T, weather) where {T<:AbstractDict{N,<:ModelList} where {N}} +function check_dimensions(component::AbstractDict{<:Any,<:ModelList}, weather) for (key, val) in component check_dimensions(val, weather) end diff --git a/src/component_models/ModelList.jl b/src/component_models/ModelList.jl index 3c97efbe3..aed069c90 100644 --- a/src/component_models/ModelList.jl +++ b/src/component_models/ModelList.jl @@ -75,11 +75,12 @@ julia> to_initialize(models) (process1 = (:var1, :var2), process2 = (:var1,)) ``` -We can now provide values for these variables in the `status` field, and simulate the `ModelList`, -*e.g.* for `process3` (coupled with `process1` and `process2`): +We can now provide values for these variables in the `status` field. Direct +`run!(::ModelList, ...)` has been removed; wrap the models in a `ModelMapping` +before running: ```jldoctest 1 -julia> models = ModelList(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0, var2=0.3)); +julia> mapping = ModelMapping(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0, var2=0.3)); ``` ```jldoctest 1 @@ -87,7 +88,7 @@ julia> meteo = Atmosphere(T = 22.0, Wind = 0.8333, P = 101.325, Rh = 0.4490995); ``` ```jldoctest 1 -julia> outputs_sim = run!(models,meteo); +julia> outputs_sim = run!(mapping, meteo); ``` ```jldoctest 1 @@ -99,7 +100,7 @@ julia> outputs_sim[:var6] If we want to use special types for the variables, we can use the `type_promotion` argument: ```jldoctest 1 -julia> models = ModelList(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0, var2=0.3), type_promotion = ModelMapping(Float64 => Float32)); +julia> models = ModelList(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0, var2=0.3), type_promotion = Dict(Float64 => Float32)); ``` We used `type_promotion` to force the status into Float32: diff --git a/src/component_models/Status.jl b/src/component_models/Status.jl index 60cc07ad5..ae3673eff 100644 --- a/src/component_models/Status.jl +++ b/src/component_models/Status.jl @@ -67,13 +67,22 @@ function Status(nt::NamedTuple{names}) where {names} Status(NamedTuple{names}(Ref.(values(nt)))) end +_status_vars(status) = getfield(status, :vars) +_status_values(status) = getindex.(values(_status_vars(status))) +_status_namedtuple(status) = NamedTuple{keys(status)}(values(status)) +_status_tuple(status) = values(status) +_status_iterate(status, iter=1) = iterate(NamedTuple(status), iter) +_status_firstindex(status) = 1 +_status_lastindex(status) = lastindex(NamedTuple(status)) +_status_indexed_iterate(status, i::Int, state=1) = Base.indexed_iterate(NamedTuple(status), i, state) + Base.keys(::Status{names}) where {names} = names -Base.values(st::Status) = getindex.(values(getfield(st, :vars))) +Base.values(st::Status) = _status_values(st) refvalues(mnt::Status) = values(getfield(mnt, :vars)) refvalue(mnt::Status, key::Symbol) = getfield(getfield(mnt, :vars), key) -Base.NamedTuple(mnt::Status) = NamedTuple{keys(mnt)}(values(mnt)) -Base.Tuple(mnt::Status) = values(mnt) +Base.NamedTuple(mnt::Status) = _status_namedtuple(mnt) +Base.Tuple(mnt::Status) = _status_tuple(mnt) function Base.show(io::IO, ::MIME"text/plain", t::Status) st_panel = Term.Panel( @@ -117,13 +126,13 @@ Base.propertynames(::Status{T,R}) where {T,R} = T Base.length(mnt::Status) = length(getfield(mnt, :vars)) Base.eltype(::Type{Status{N,T}}) where {N,T} = eltype.(eltype(T)) -Base.iterate(mnt::Status, iter=1) = iterate(NamedTuple(mnt), iter) +Base.iterate(mnt::Status, iter=1) = _status_iterate(mnt, iter) -Base.firstindex(mnt::Status) = 1 -Base.lastindex(mnt::Status) = lastindex(NamedTuple(mnt)) +Base.firstindex(mnt::Status) = _status_firstindex(mnt) +Base.lastindex(mnt::Status) = _status_lastindex(mnt) function Base.indexed_iterate(mnt::Status, i::Int, state=1) - Base.indexed_iterate(NamedTuple(mnt), i, state) + _status_indexed_iterate(mnt, i, state) end function Base.:(==)(s1::Status, s2::Status) @@ -169,4 +178,4 @@ function get_status_vector_max_length(s::Status) end end return max_len -end \ No newline at end of file +end diff --git a/src/component_models/StatusView.jl b/src/component_models/StatusView.jl index 1d74e099e..b51c4175f 100644 --- a/src/component_models/StatusView.jl +++ b/src/component_models/StatusView.jl @@ -97,9 +97,9 @@ function Base.setindex!(s::StatusView, value, name) end Base.keys(::StatusView{names}) where {names} = names -Base.values(s::StatusView) = getindex.(values(getfield(s, :vars))) -Base.NamedTuple(mnt::StatusView) = NamedTuple{keys(mnt)}(values(mnt)) -Base.Tuple(mnt::StatusView) = values(mnt) +Base.values(s::StatusView) = _status_values(s) +Base.NamedTuple(mnt::StatusView) = _status_namedtuple(mnt) +Base.Tuple(mnt::StatusView) = _status_tuple(mnt) function Base.show(io::IO, s::StatusView) length(s) == 0 && return @@ -138,10 +138,10 @@ end Base.propertynames(::StatusView{T,R}) where {T,R} = T Base.length(mnt::StatusView) = length(getfield(mnt, :vars)) Base.eltype(::Type{StatusView{T}}) where {T} = T -Base.iterate(mnt::StatusView, iter=1) = iterate(NamedTuple(mnt), iter) -Base.firstindex(mnt::StatusView) = 1 -Base.lastindex(mnt::StatusView) = lastindex(NamedTuple(mnt)) +Base.iterate(mnt::StatusView, iter=1) = _status_iterate(mnt, iter) +Base.firstindex(mnt::StatusView) = _status_firstindex(mnt) +Base.lastindex(mnt::StatusView) = _status_lastindex(mnt) function Base.indexed_iterate(mnt::StatusView, i::Int, state=1) - Base.indexed_iterate(NamedTuple(mnt), i, state) -end \ No newline at end of file + _status_indexed_iterate(mnt, i, state) +end diff --git a/src/dataframe.jl b/src/dataframe.jl index 2a9fba567..685279bb3 100644 --- a/src/dataframe.jl +++ b/src/dataframe.jl @@ -39,7 +39,7 @@ models = ModelMapping( df = DataFrame(models) ``` """ -function DataFrames.DataFrame(components::T) where {T<:AbstractArray{<:ModelMapping}} +function DataFrames.DataFrame(components::AbstractVector{<:ModelMapping}) df = DataFrame[] for (k, v) in enumerate(components) df_c = DataFrames.DataFrame(v) @@ -49,7 +49,7 @@ function DataFrames.DataFrame(components::T) where {T<:AbstractArray{<:ModelMapp reduce(vcat, df) end -function DataFrames.DataFrame(components::T) where {T<:AbstractDict{N,<:ModelMapping} where {N}} +function DataFrames.DataFrame(components::AbstractDict{<:Any,<:ModelMapping}) df = DataFrames.DataFrame[] for (k, v) in components df_c = DataFrames.DataFrame(v) @@ -64,6 +64,6 @@ end Implementation of `DataFrame` for a `ModelMapping` model with one time step. """ -function DataFrames.DataFrame(components::ModelMapping{T}) where {T} +function DataFrames.DataFrame(components::ModelMapping) DataFrames.DataFrame([NamedTuple(status(components)[1])]) end diff --git a/src/dependencies/dependency_graph.jl b/src/dependencies/dependency_graph.jl index 7063b77e9..1386169e8 100644 --- a/src/dependencies/dependency_graph.jl +++ b/src/dependencies/dependency_graph.jl @@ -1,5 +1,16 @@ abstract type AbstractDependencyNode end +""" + ProducerVariable(input, source) + +Dependency metadata for a producer variable that reaches a consumer input under +a different local name. +""" +struct ProducerVariable + input::Symbol + source::Symbol +end + mutable struct HardDependencyNode{T} <: AbstractDependencyNode value::T process::Symbol @@ -149,6 +160,10 @@ function _node_mapping(var_mapping::Pair{<:Union{AbstractString,Symbol},Symbol}) return SingleNodeMapping(first(var_mapping)), last(var_mapping) end +function _node_mapping(var_mapping::Pair{SameScale,Symbol}) + return first(var_mapping), last(var_mapping) +end + function _node_mapping(var_mapping) # Several organs are mapped to the variable: organ_mapped = MultiNodeMapping([first(i) for i in var_mapping]) diff --git a/src/dependencies/hard_dependencies.jl b/src/dependencies/hard_dependencies.jl index cb2f3704f..eeb49737a 100644 --- a/src/dependencies/hard_dependencies.jl +++ b/src/dependencies/hard_dependencies.jl @@ -5,20 +5,27 @@ Compute the hard dependencies between models. """ -_is_domain_dependency_selector(x) = nameof(typeof(x)) in (:AllDomains, :HardDomains) +abstract type AbstractDomainDependencySelector end + +_is_domain_dependency_selector(x) = x isa AbstractDomainDependencySelector function _normalize_hard_dependency_scales(scales, process::Symbol, dependency_process::Symbol) - if scales isa Symbol || scales isa AbstractString - return [Symbol(scales)] + if scales isa Symbol + return [scales] + elseif scales isa AbstractString + error( + "Invalid hard dependency scale declaration for process `$(process)` dependency `$(dependency_process)`: ", + "string scale names are removed. Use Symbol scales, e.g. `:Leaf`." + ) elseif scales isa Tuple || scales isa AbstractVector normalized = Symbol[] for s in scales - if s isa Symbol || s isa AbstractString - push!(normalized, Symbol(s)) + if s isa Symbol + push!(normalized, s) else error( "Invalid hard dependency scale declaration for process `$(process)` dependency `$(dependency_process)`: ", - "expected Symbol or String scales, got `$(typeof(s))`." + "expected Symbol scales, got `$(typeof(s))`." ) end end diff --git a/src/dependencies/is_graph_cyclic.jl b/src/dependencies/is_graph_cyclic.jl index 94fb0d928..1bfcd0e28 100644 --- a/src/dependencies/is_graph_cyclic.jl +++ b/src/dependencies/is_graph_cyclic.jl @@ -12,19 +12,16 @@ Check if the dependency graph is cyclic. Return a boolean indicating if the graph is cyclic, and the stack of nodes as a vector. """ function is_graph_cyclic(dependency_graph::DependencyGraph; full_stack=false, warn=true) - visited = Dict{Pair{Any,Symbol},Bool}() - recursion_stack = Dict{Pair{Any,Symbol},Bool}() - for node in values(dependency_graph.roots) - visited[node.value=>node.scale] = false - recursion_stack[node.value=>node.scale] = false - end + visited = IdDict{Any,Bool}() + recursion_stack = IdDict{Any,Bool}() for (root, node) in dependency_graph.roots - cycle_vec = Vector{Pair{Any,Symbol}}() - if is_graph_cyclic_(node, visited, recursion_stack, cycle_vec) + cycle_nodes = Any[] + if is_graph_cyclic_(node, visited, recursion_stack, cycle_nodes) + cycle_vec = _cycle_report_nodes(cycle_nodes) if full_stack - push!(cycle_vec, node.value => node.scale) + push!(cycle_vec, _cycle_report_node(node)) else # Keep just the cycle (the first node in the vector is the one that makes a cycle, we just detect the second time it happens on the stack): cycled_nodes = findall(x -> x == cycle_vec[1], cycle_vec) @@ -41,25 +38,26 @@ function is_graph_cyclic(dependency_graph::DependencyGraph; full_stack=false, wa end function is_graph_cyclic_(node, visited, recursion_stack, cycle_vec) - node_id = node.value => node.scale - visited[node_id] = true - recursion_stack[node_id] = true + visited[node] = true + recursion_stack[node] = true for child in node.children - child_id = child.value => child.scale - if !haskey(visited, child_id) && is_graph_cyclic_(child, visited, recursion_stack, cycle_vec) - push!(cycle_vec, child_id) + if !haskey(visited, child) && is_graph_cyclic_(child, visited, recursion_stack, cycle_vec) + push!(cycle_vec, child) return true - elseif haskey(recursion_stack, child_id) && recursion_stack[child_id] - push!(cycle_vec, child_id) + elseif get(recursion_stack, child, false) + push!(cycle_vec, child) return true end end - recursion_stack[node_id] = false + recursion_stack[node] = false return false end +_cycle_report_node(node) = node.value => node.scale +_cycle_report_nodes(nodes) = Pair{Any,Symbol}[_cycle_report_node(node) for node in nodes] + function print_cycle(cycle_vec) printed_cycle = Any[Term.RenderableText(string("{bold red}", last(cycle_vec[1]), ": ", typeof(first(cycle_vec[1]))))] leading_space = [1] diff --git a/src/dependencies/soft_dependencies.jl b/src/dependencies/soft_dependencies.jl index 377a50294..869e75c00 100644 --- a/src/dependencies/soft_dependencies.jl +++ b/src/dependencies/soft_dependencies.jl @@ -139,6 +139,66 @@ function soft_dependencies(d::DependencyGraph{Dict{Symbol,HardDependencyNode}}, end # For multiscale mapping: +function _owning_soft_dependency_node(node::AbstractDependencyNode) + owner = node + depth = 0 + while !(owner isa SoftDependencyNode) + depth += 1 + depth <= 50 || error( + "Could not resolve the owning soft dependency for nested hard dependency ", + "`$(node.process)` at scale `$(node.scale)`." + ) + owner.parent === nothing && error( + "Nested hard dependency `$(node.process)` at scale `$(node.scale)` has no owning soft dependency." + ) + owner = owner.parent + end + return owner +end + +function _hard_dependency_candidates(parent_process::Symbol, target_scale::Symbol, hard_dep_dict::Dict{Pair{Symbol,Symbol},HardDependencyNode}) + scale_matches = HardDependencyNode[] + process_matches = HardDependencyNode[] + for ((hd_process, hd_scale), hd_node) in hard_dep_dict + hd_process == parent_process || continue + push!(process_matches, hd_node) + (hd_scale == target_scale || hd_node.scale == target_scale) && push!(scale_matches, hd_node) + end + return isempty(scale_matches) ? process_matches : scale_matches +end + +function _soft_graph_at_scale(soft_dep_graphs_roots::DependencyGraph{Dict{Symbol,Any}}, scale::Symbol) + haskey(soft_dep_graphs_roots.roots, scale) || error("Scale `$scale` not found while resolving soft dependency parent.") + return soft_dep_graphs_roots.roots[scale][:soft_dep_graph] +end + +function _resolve_soft_parent_node( + soft_dep_graphs_roots::DependencyGraph{Dict{Symbol,Any}}, + target_scale::Symbol, + parent_process::Symbol, + hard_dep_dict::Dict{Pair{Symbol,Symbol},HardDependencyNode} +) + roots_at_target_scale = _soft_graph_at_scale(soft_dep_graphs_roots, target_scale) + haskey(roots_at_target_scale, parent_process) && return roots_at_target_scale[parent_process] + + candidates = _hard_dependency_candidates(parent_process, target_scale, hard_dep_dict) + isempty(candidates) && error( + "Parent process `$parent_process` at scale `$target_scale` is not located in soft roots or nested hard dependencies." + ) + length(candidates) == 1 || error( + "Parent process `$parent_process` is an ambiguous nested hard dependency for scale `$target_scale`. ", + "Matching hard-dependency scales: $(Tuple((candidate.scale for candidate in candidates)))." + ) + + owner = _owning_soft_dependency_node(only(candidates)) + owner_graph = _soft_graph_at_scale(soft_dep_graphs_roots, owner.scale) + haskey(owner_graph, owner.process) || error( + "Owning soft dependency `$(owner.process)` at scale `$(owner.scale)` was resolved for nested hard dependency ", + "`$parent_process`, but it is not present in the finalized soft graph." + ) + return owner_graph[owner.process] +end + function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dict{Symbol,Any}}, reverse_multiscale_mapping, hard_dep_dict::Dict{Pair{Symbol,Symbol},HardDependencyNode}) independant_process_root = Dict{Pair{Symbol,Symbol},SoftDependencyNode}() @@ -171,33 +231,7 @@ function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dic # and we need to add its parent(s) to the node, and the node as a child for (parent_soft_dep, soft_dep_vars) in pairs(soft_deps_not_hard) - # if the parent isn't registered as a soft dependency, it likely means the soft dependecy should be to an internal hard dependency to the parent - if (!haskey(soft_dep_graph, parent_soft_dep)) - - roots_at_given_scale = soft_dep_graphs_roots.roots[i.scale][:soft_dep_graph] - if !(parent_soft_dep in keys(roots_at_given_scale)) - master_node = () - for ((hd_key, hd_scale), hd) in hard_dep_dict - if parent_soft_dep == hd_key - master_node = hd - depth = 0 - # A cleaner way of preventing cycles or infinite loops would be more desirable - while !isa(master_node, SoftDependencyNode) && depth < 50 - master_node.parent === nothing && error("Finalised hard dependency has no parent") - master_node = master_node.parent - depth += 1 - end - - break - end - end - master_node == () && error("Parent is not located in hard deps, nor in roots, which should be the case when initalizing soft dependencies") - end - # NOTE : this may need to be propagated within internal hard dependencies' ancestors of this model... ? - parent_node = soft_dep_graphs_roots.roots[master_node.scale][:soft_dep_graph][master_node.process] - else - parent_node = soft_dep_graph[parent_soft_dep] - end + parent_node = _resolve_soft_parent_node(soft_dep_graphs_roots, i.scale, parent_soft_dep, hard_dep_dict) @@ -248,36 +282,7 @@ function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dic for org in keys(soft_deps_multiscale) for (parent_soft_dep, soft_dep_vars) in soft_deps_multiscale[org] - # if the node has a soft dependency on a node that is a nested hard dependency, - # have it point to the master node of that hard dependency instead of the internal node - # This check is meant in case the organ at the inspected scale is part of a hard dependency, - # and therefore already absent from the roots - - roots_at_given_scale = soft_dep_graphs_roots.roots[org][:soft_dep_graph] - if !(parent_soft_dep in keys(roots_at_given_scale)) - master_node = () - for ((hd_key, hd_scale), hd) in hard_dep_dict - if parent_soft_dep == hd_key - master_node = hd - depth = 0 - # A cleaner way of preventing cycles or infinite loops would be more desirable - while !isa(master_node, SoftDependencyNode) && depth < 50 - master_node.parent === nothing && error("Finalised hard dependency has no parent") - master_node = master_node.parent - depth += 1 - end - - break - end - end - - master_node == () && error("Parent is not located in hard deps, nor in roots, which should be the case when initalizing soft dependencies") - - # NOTE : this may need to be propagated within internal hard dependencies' ancestors of this model... ? - parent_node = soft_dep_graphs_roots.roots[master_node.scale][:soft_dep_graph][master_node.process] - else - parent_node = soft_dep_graphs_roots.roots[org][:soft_dep_graph][parent_soft_dep] - end + parent_node = _resolve_soft_parent_node(soft_dep_graphs_roots, org, parent_soft_dep, hard_dep_dict) # preventing a cyclic dependency: if the parent also has a dependency on the current node: if parent_node.parent !== nothing && any([i == p for p in parent_node.parent]) @@ -466,12 +471,12 @@ function search_inputs_in_multiscale_output(process, organ, inputs, soft_dep_gra # proc, organ, ins, soft_dep_graphs=soft_dep_graphs_roots.roots vars_input = flatten_vars(inputs[process]) - inputs_as_output_of_other_scale = Dict{Symbol,Dict{Symbol,Vector{Symbol}}}() + inputs_as_output_of_other_scale = Dict{Symbol,Dict{Symbol,Vector{ProducerVariable}}}() for (var, val) in pairs(vars_input) # e.g. var = :leaf_surfaces;val = vars_input[var] # The variable is a multiscale variable: if isa(val, MappedVar) var_organ = mapped_organ(val) - (isnothing(var_organ) || var_organ == Symbol("")) && continue # If the variable maps to nothing we skip it (e.g. [PreviousTimeStep(:var1)] or [:var => :new_var]) + isnothing(var_organ) && continue # If the variable maps to nothing we skip it (e.g. [PreviousTimeStep(:var1)] or [:var => :new_var]) if !isa(var_organ, AbstractVector) # In case the organ is given as a singleton (e.g. :Soil instead of [:Soil]) var_organ = [var_organ] @@ -513,6 +518,7 @@ end function add_input_as_output!(inputs_as_output_of_other_scale, soft_dep_graphs, organ_source, variable, value) + producer_var = ProducerVariable(value, variable) for (proc_output, pairs_vars_output) in soft_dep_graphs[organ_source][:outputs] # e.g. proc_output = :maintenance_respiration; pairs_vars_output = soft_dep_graphs_roots.roots[organ_source][:outputs][proc_output] vars_output = flatten_vars(pairs_vars_output) @@ -521,12 +527,12 @@ function add_input_as_output!(inputs_as_output_of_other_scale, soft_dep_graphs, # The variable is found at another scale: if haskey(inputs_as_output_of_other_scale, organ_source) if haskey(inputs_as_output_of_other_scale[organ_source], proc_output) - push!(inputs_as_output_of_other_scale[organ_source][proc_output], value) + push!(inputs_as_output_of_other_scale[organ_source][proc_output], producer_var) else - inputs_as_output_of_other_scale[organ_source][proc_output] = [value] + inputs_as_output_of_other_scale[organ_source][proc_output] = [producer_var] end else - inputs_as_output_of_other_scale[organ_source] = Dict(proc_output => [value]) + inputs_as_output_of_other_scale[organ_source] = Dict(proc_output => [producer_var]) end end end diff --git a/src/domains/domain_simulation.jl b/src/domains/domain_simulation.jl index c57e66915..362181112 100644 --- a/src/domains/domain_simulation.jl +++ b/src/domains/domain_simulation.jl @@ -68,7 +68,7 @@ different rate. `AllDomains` does not provide hard-dependency call-stack control; use [`HardDomains`](@ref) when the parent model must manually run the selected models. """ -struct AllDomains{P<:SchedulePolicy} +struct AllDomains{P<:SchedulePolicy} <: AbstractDomainDependencySelector kind::Union{Nothing,Symbol} domain::Union{Nothing,Symbol} scale::Union{Nothing,Symbol} @@ -99,7 +99,7 @@ Selector for models that are cross-domain hard dependencies. A model declaring can retrieve executable targets with [`dependency_targets`](@ref) and manually execute them with [`run_target!`](@ref). """ -struct HardDomains +struct HardDomains <: AbstractDomainDependencySelector kind::Union{Nothing,Symbol} domain::Union{Nothing,Symbol} scale::Union{Nothing,Symbol} @@ -837,22 +837,7 @@ function _validate_graph_route_order( routes::Vector{Route}, route_bindings::Vector{Vector{DomainModelKey}}, ) - ordered_domains = _domain_run_order(mapping) - domain_positions = Dict(domain.name => i for (i, domain) in enumerate(ordered_domains)) - for (i, route) in enumerate(routes) - target_domain = _domain_for_name(mapping, route.to.domain) - _is_graph_domain(target_domain) || continue - target_position = domain_positions[target_domain.name] - for producer in route_bindings[i] - producer_position = domain_positions[producer.domain] - producer_position < target_position || error( - "Route $(i) targets MTG-backed domain `$(target_domain.name)` from producer `$(producer)`, ", - "but graph-target routes require source domains to run earlier in the same timestep. ", - "Move source domain `$(producer.domain)` before target domain `$(target_domain.name)` in the ", - "`SimulationMapping`, or route the value through a later single-status scene domain instead." - ) - end - end + _domain_run_order(mapping, route_bindings) return nothing end @@ -917,11 +902,70 @@ function _domain_model_specs_by_scale(specs::Dict{DomainModelKey,ModelSpec}) return by_scale end -function _domain_run_order(mapping::SimulationMapping) - # Scene-like aggregators should observe plant/soil/environment updates from - # the current base step. Keep this simple and explicit for the first runner. - indexed_domains = collect(enumerate(mapping.domains)) - return last.(sort(indexed_domains; by=item -> (last(item).kind == :scene ? 1 : 0, first(item)))) +function _domain_order_edges(mapping::SimulationMapping, route_bindings=nothing) + edges = Dict(domain.name => Set{Symbol}() for domain in mapping.domains) + non_scene_domains = [domain.name for domain in mapping.domains if domain.kind != :scene] + scene_domains = [domain.name for domain in mapping.domains if domain.kind == :scene] + for source in non_scene_domains, target in scene_domains + source == target || push!(edges[source], target) + end + + if !isnothing(route_bindings) + for (i, route) in enumerate(mapping.routes) + target = route.to.domain + for producer in route_bindings[i] + producer.domain == target && continue + push!(edges[producer.domain], target) + end + end + end + + return edges +end + +function _domain_run_order(mapping::SimulationMapping, route_bindings=nothing) + domains_by_name = Dict(domain.name => domain for domain in mapping.domains) + declaration_index = Dict(domain.name => i for (i, domain) in enumerate(mapping.domains)) + edges = _domain_order_edges(mapping, route_bindings) + indegree = Dict(domain.name => 0 for domain in mapping.domains) + for targets in values(edges), target in targets + indegree[target] = get(indegree, target, 0) + 1 + end + + ready = sort!( + [name for (name, degree) in indegree if degree == 0]; + by=name -> declaration_index[name], + ) + ordered = Symbol[] + while !isempty(ready) + current = popfirst!(ready) + push!(ordered, current) + for target in sort!(collect(edges[current]); by=name -> declaration_index[name]) + indegree[target] -= 1 + if indegree[target] == 0 + push!(ready, target) + sort!(ready; by=name -> declaration_index[name]) + end + end + end + + if length(ordered) != length(mapping.domains) + cyclic = sort!( + [name for (name, degree) in indegree if degree > 0]; + by=name -> declaration_index[name], + ) + error( + "Cyclic domain run-order constraints detected among domains: ", + join((":" * string(name) for name in cyclic), ", "), + ". Check route sources/targets and `kind=:scene` phase constraints." + ) + end + + return [domains_by_name[name] for name in ordered] +end + +function _domain_run_order(simulation::DomainSimulation) + return _domain_run_order(simulation.mapping, simulation.route_bindings) end function _publish_domain_model_outputs!( @@ -1169,11 +1213,25 @@ function _find_dependency_node(graph::DependencyGraph, key::DomainModelKey) error("No dependency node found for domain model `$(key)`.") end +_domain_environment_support(domain::Domain, node::AbstractDependencyNode, status) = + EnvironmentSupport(domain.name, node.scale, node.process, status) + +_domain_environment_support(key::DomainModelKey, status) = + EnvironmentSupport(key.domain, key.scale, key.process, status) + +function _sample_domain_environment_at_time(simulation::DomainSimulation, support::EnvironmentSupport, t, model_spec::ModelSpec) + return sample_environment(simulation.environment, support, t, model_spec) +end + +function _sample_domain_environment_at_step(simulation::DomainSimulation, support::EnvironmentSupport, step::Int, model_spec::ModelSpec) + t = _time_from_step(step, simulation.timeline) + return _sample_domain_environment_at_time(simulation, support, t, model_spec) +end + function _dependency_target_meteo(simulation::DomainSimulation, key::DomainModelKey, st, step::Int) spec = simulation.model_specs[key] - support = EnvironmentSupport(key.domain, key.scale, key.process, st) - t = _time_from_step(step, simulation.timeline) - return sample_environment(simulation.environment, support, t, spec) + support = _domain_environment_support(key, st) + return _sample_domain_environment_at_step(simulation, support, step, spec) end function _single_status_dependency_targets(ctx::DomainRunContext, producer::DomainModelKey) @@ -1483,9 +1541,21 @@ function _domain_environment_for_model( status, step::Int ) - support = EnvironmentSupport(domain.name, node.scale, node.process, status) - t = _time_from_step(step, simulation.timeline) - return sample_environment(simulation.environment, support, t, model_spec) + support = _domain_environment_support(domain, node, status) + return _sample_domain_environment_at_step(simulation, support, step, model_spec) +end + +function _scatter_domain_environment_outputs_at_time!( + simulation::DomainSimulation, + domain::Domain, + node::AbstractDependencyNode, + model_spec::ModelSpec, + status, + t +) + isempty(keys(meteo_outputs_(model_spec))) && return nothing + support = _domain_environment_support(domain, node, status) + return scatter_environment_outputs!(simulation.environment, support, t, model_spec, status) end function _scatter_domain_environment_outputs!( @@ -1496,30 +1566,39 @@ function _scatter_domain_environment_outputs!( status, step::Int ) - isempty(keys(meteo_outputs_(model_spec))) && return nothing - support = EnvironmentSupport(domain.name, node.scale, node.process, status) t = _time_from_step(step, simulation.timeline) - return scatter_environment_outputs!(simulation.environment, support, t, model_spec, status) + return _scatter_domain_environment_outputs_at_time!(simulation, domain, node, model_spec, status, t) end -function _scatter_domain_hard_dependency_environment_outputs!( +function _scatter_domain_hard_dependency_environment_outputs_at_time!( simulation::DomainSimulation, domain::Domain, node::HardDependencyNode, status, - step::Int + t ) key = DomainModelKey(domain.name, node.scale, node.process) if haskey(simulation.model_specs, key) spec = simulation.model_specs[key] - _scatter_domain_environment_outputs!(simulation, domain, node, spec, status, step) + _scatter_domain_environment_outputs_at_time!(simulation, domain, node, spec, status, t) end for child in node.children - _scatter_domain_hard_dependency_environment_outputs!(simulation, domain, child, status, step) + _scatter_domain_hard_dependency_environment_outputs_at_time!(simulation, domain, child, status, t) end return nothing end +function _scatter_domain_hard_dependency_environment_outputs!( + simulation::DomainSimulation, + domain::Domain, + node::HardDependencyNode, + status, + step::Int +) + t = _time_from_step(step, simulation.timeline) + return _scatter_domain_hard_dependency_environment_outputs_at_time!(simulation, domain, node, status, t) +end + function _graph_domain_environment_for_model( simulation::DomainSimulation, domain::Domain, @@ -1535,8 +1614,8 @@ function _graph_domain_environment_for_model( if simulation.environment isa GlobalConstant return multirate ? _sample_meteo_for_model(meteo_sampler, meteo, round(Int, t), model_clock, model_spec) : meteo end - support = EnvironmentSupport(domain.name, node.scale, node.process, status) - return sample_environment(simulation.environment, support, t, model_spec) + support = _domain_environment_support(domain, node, status) + return _sample_domain_environment_at_time(simulation, support, t, model_spec) end function _scatter_graph_domain_environment_outputs!( @@ -1547,9 +1626,7 @@ function _scatter_graph_domain_environment_outputs!( status, t ) - isempty(keys(meteo_outputs_(model_spec))) && return nothing - support = EnvironmentSupport(domain.name, node.scale, node.process, status) - return scatter_environment_outputs!(simulation.environment, support, t, model_spec, status) + return _scatter_domain_environment_outputs_at_time!(simulation, domain, node, model_spec, status, t) end function _scatter_graph_domain_hard_dependency_environment_outputs!( @@ -1559,15 +1636,7 @@ function _scatter_graph_domain_hard_dependency_environment_outputs!( status, t ) - key = DomainModelKey(domain.name, node.scale, node.process) - if haskey(simulation.model_specs, key) - spec = simulation.model_specs[key] - _scatter_graph_domain_environment_outputs!(simulation, domain, node, spec, status, t) - end - for child in node.children - _scatter_graph_domain_hard_dependency_environment_outputs!(simulation, domain, child, status, t) - end - return nothing + return _scatter_domain_hard_dependency_environment_outputs_at_time!(simulation, domain, node, status, t) end function _domain_node_due(simulation::DomainSimulation, domain::Domain, node::SoftDependencyNode, step::Int) @@ -1598,6 +1667,12 @@ function _has_hard_domain_parent(simulation::DomainSimulation, domain::Domain, n return false end +function _phase_allows_hard_parent(phase::Symbol, has_hard_parent::Bool) + phase == :normal && return !has_hard_parent + phase == :post_scene && return has_hard_parent + error("Unknown domain scheduling phase `$(phase)`.") +end + function _should_visit_domain_node( simulation::DomainSimulation, domain::Domain, @@ -1607,9 +1682,7 @@ function _should_visit_domain_node( key = DomainModelKey(domain.name, node.scale, node.process) _is_hard_domain_dependency(simulation, key) && return false has_hard_parent = _has_hard_domain_parent(simulation, domain, node) - phase == :normal && return !has_hard_parent - phase == :post_scene && return has_hard_parent - error("Unknown domain scheduling phase `$(phase)`.") + return _phase_allows_hard_parent(phase, has_hard_parent) end function _has_hard_domain_parent(simulation::DomainSimulation, domain::Domain, key::DomainModelKey) @@ -1639,9 +1712,7 @@ function _should_publish_domain_key( _has_domain_soft_node(simulation, domain, key) || return false _is_hard_domain_dependency(simulation, key) && return false has_hard_parent = _has_hard_domain_parent(simulation, domain, key) - phase == :normal && return !has_hard_parent - phase == :post_scene && return has_hard_parent - error("Unknown domain publishing phase `$(phase)`.") + return _phase_allows_hard_parent(phase, has_hard_parent) end function _domain_has_post_scene_work(simulation::DomainSimulation, domain::Domain) @@ -1743,13 +1814,14 @@ function run!( ) simulation = _build_domain_simulation(mapping, meteo) nsteps = get_nsteps(simulation.environment) + run_order = _domain_run_order(simulation) for i in 1:nsteps - for domain in _domain_run_order(mapping) + for domain in run_order _materialize_routes_for_domain!(simulation, domain, i) _run_domain_models!(simulation, domain, constants, i) _update_domain_environment_index!(simulation, domain) end - for domain in _domain_run_order(mapping) + for domain in run_order domain.kind == :scene && continue _domain_has_post_scene_work(simulation, domain) || continue _materialize_routes_for_domain!(simulation, domain, i) @@ -1949,7 +2021,7 @@ function run!( simulation = _build_domain_simulation(mapping, meteo; staged_graph_domains=true) raw_meteo = _raw_meteo_for_staged_graph_domains(simulation.environment) isnothing(nsteps) && (nsteps = get_nsteps(simulation.environment)) - run_order = _domain_run_order(mapping) + run_order = _domain_run_order(simulation) graph_runtimes = Dict{Symbol,DomainGraphRuntime}() for step in 1:nsteps diff --git a/src/mtg/GraphSimulation.jl b/src/mtg/GraphSimulation.jl index 6a8ea15e7..60aaad0d6 100644 --- a/src/mtg/GraphSimulation.jl +++ b/src/mtg/GraphSimulation.jl @@ -23,7 +23,7 @@ struct GraphSimulation{T,S,U,O,V,TS,MS} graph::T statuses::S status_templates::Dict{Symbol,Dict{Symbol,Any}} - reverse_multiscale_mapping::Dict{Symbol,Dict{Symbol,Dict{Symbol,Any}}} + reverse_multiscale_mapping::ReverseMultiscaleMapping var_need_init::Dict{Symbol,V} dependency_graph::DependencyGraph models::Dict{Symbol,U} @@ -141,15 +141,6 @@ function convert_outputs(outs::Dict{Symbol,O} where O, sink; refvectors=false, n return ret end -# TODO adapt these to new output structure or remove them -function outputs(outs::Dict{Symbol,O} where O, key::Symbol) - Tables.columns(convert_outputs(outs, Vector{NamedTuple}))[key] -end - -function outputs(outs::Dict{Symbol,O} where O, i::T) where {T<:Integer} - Tables.columns(convert_outputs(outs, Vector{NamedTuple}))[i] -end - # ModelLists now return outputs as a TimeStepTable{Status}, conversion is straightforward function convert_outputs(out::TimeStepTable{T} where T, sink) if !Tables.istable(sink) diff --git a/src/mtg/ModelSpec.jl b/src/mtg/ModelSpec.jl index 2953f88ce..921fab852 100644 --- a/src/mtg/ModelSpec.jl +++ b/src/mtg/ModelSpec.jl @@ -88,14 +88,8 @@ function ModelSpec( updates=() ) base_model = model - base_multiscale = multiscale - if model isa MultiScaleModel - base_model = model_(model) - base_multiscale === nothing && (base_multiscale = mapped_variables_(model)) - end - - normalized_multiscale = _normalize_multiscale_mapping(base_model, base_multiscale) + normalized_multiscale = _normalize_multiscale_mapping(base_model, multiscale) normalized_input_bindings = _normalize_input_bindings(input_bindings) normalized_meteo_bindings = _normalize_meteo_bindings(meteo_bindings) normalized_meteo_window = _normalize_meteo_window(meteo_window) @@ -115,6 +109,31 @@ function ModelSpec( ) end +function ModelSpec( + model::MultiScaleModel; + multiscale=nothing, + timestep=nothing, + input_bindings=NamedTuple(), + meteo_bindings=NamedTuple(), + meteo_window=nothing, + output_routing=NamedTuple(), + scope=:global, + updates=() +) + base_multiscale = isnothing(multiscale) ? mapped_variables_(model) : multiscale + return ModelSpec( + model_(model); + multiscale=base_multiscale, + timestep=timestep, + input_bindings=input_bindings, + meteo_bindings=meteo_bindings, + meteo_window=meteo_window, + output_routing=output_routing, + scope=scope, + updates=updates + ) +end + function ModelSpec( spec::ModelSpec; model=spec.model, @@ -235,7 +254,9 @@ function _normalize_input_bindings(bindings::NamedTuple) return (; normalized...) end -_normalize_input_bindings(bindings) = bindings +function _normalize_input_bindings(bindings) + error("Unsupported InputBindings value `$(bindings)` of type `$(typeof(bindings))`. Use a NamedTuple, e.g. `InputBindings(; x=(process=:producer, var=:y))`.") +end function _normalize_meteo_binding(binding) if binding isa DataType @@ -265,7 +286,9 @@ function _normalize_meteo_bindings(bindings::NamedTuple) return (; normalized...) end -_normalize_meteo_bindings(bindings) = bindings +function _normalize_meteo_bindings(bindings) + error("Unsupported MeteoBindings value `$(bindings)` of type `$(typeof(bindings))`. Use a NamedTuple, e.g. `MeteoBindings(; T=MeanReducer())`.") +end function _normalize_meteo_window(window) if isnothing(window) @@ -299,11 +322,13 @@ function _normalize_output_routing(routing::NamedTuple) return (; normalized...) end -_normalize_output_routing(routing) = routing +function _normalize_output_routing(routing) + error("Unsupported OutputRouting value `$(routing)` of type `$(typeof(routing))`. Use a NamedTuple, e.g. `OutputRouting(; x=:stream_only)`.") +end function _normalize_scope_selector(scope) if scope isa AbstractString - return Symbol(scope) + error("String scope selectors are not supported. Use symbols such as `:global`, `:plant`, `:scene`, or `:self`.") end return scope end @@ -457,9 +482,9 @@ multi-rate simulations. # Arguments - `scope`: one of: - - selector symbols/strings: `:global`, `:plant`, `:scene`, `:self`, + - selector symbols: `:global`, `:plant`, `:scene`, `:self`, - a concrete `ScopeId`, - - a callable returning a scope selector/id at runtime. + - a callable returning a scope selector symbol or `ScopeId` at runtime. # Example ```julia diff --git a/src/mtg/MultiScaleModel.jl b/src/mtg/MultiScaleModel.jl index a5535af6f..7b6d1b66b 100644 --- a/src/mtg/MultiScaleModel.jl +++ b/src/mtg/MultiScaleModel.jl @@ -7,7 +7,7 @@ model and the nodes symbols from which the values are taken from. # Arguments - `model<:AbstractModel`: the model to make multi-scale -- `mapped_variables<:Vector{Pair{Symbol,Union{Symbol,AbstractString,Vector{<:Union{Symbol,AbstractString}}}}}`: +- `mapped_variables<:Vector{Pair{Symbol,Union{Symbol,Vector{Symbol}}}}`: a vector of pairs of model variables and source scale declarations. The mapped_variables argument can be of the form: @@ -18,7 +18,7 @@ The mapped_variables argument can be of the form: 4. `[:variable_name => (:Plant => :variable_name_in_plant_scale)]`: We take one value from another variable name in the Plant node 5. `[:variable_name => [:Leaf => :variable_name_1, :Internode => :variable_name_2]]`: We take a vector of values from the Leaf and Internode nodes with different names 6. `[PreviousTimeStep(:variable_name) => ...]`: We flag the variable to be initialized with the value from the previous time step, and we do not use it to build the dep graph -7. `[:variable_name => (Symbol() => :variable_name_from_another_model)]`: We take the value from another model at the same scale but rename it +7. `[:variable_name => (SameScale() => :variable_name_from_another_model)]`: We take the value from another model at the same scale but rename it 8. `[PreviousTimeStep(:variable_name),]`: We just flag the variable as a PreviousTimeStep to not use it to build the dep graph Details about the different forms: @@ -101,15 +101,7 @@ julia> PlantSimEngine.model_(multiscale_model) ToyCAllocationModel() ``` """ -struct MultiScaleModel{ - T<:AbstractModel, - V<:AbstractVector{ - Pair{ - A, - Union{Pair{Symbol,Symbol},Vector{Pair{Symbol,Symbol}}} - } - } where {A<:Union{Symbol,PreviousTimeStep}} -} +struct MultiScaleModel{T<:AbstractModel,V<:AbstractVector} model::T mapped_variables::V @@ -135,11 +127,11 @@ struct MultiScaleModel{ # 4. `[:variable_name => (:Plant => :variable_name_in_plant_scale)]` # We take one value from another variable name in the Plant node # 5. `[:variable_name => [:Leaf => :variable_name_1, :Internode => :variable_name_2]]` # We take a vector of values from the Leaf and Internode nodes with different names # 6. `[PreviousTimeStep(:variable_name) => ...]` # We flag the variable to be initialized with the value from the previous time step, and we do not use it to build the dep graph - # 7. `[:variable_name => (Symbol("") => :variable_name_from_another_model)]` # We take the value from another model at the same scale but rename it + # 7. `[:variable_name => (SameScale() => :variable_name_from_another_model)]` # We take the value from another model at the same scale but rename it # 8. `[PreviousTimeStep(:variable_name),]` # We just flag the variable as a PreviousTimeStep to not use it to build the dep graph process_ = process(model) - unfolded_mapping = Pair{Union{Symbol,PreviousTimeStep},Union{Pair{Symbol,Symbol},Vector{Pair{Symbol,Symbol}}}}[] + unfolded_mapping = Pair{Union{Symbol,PreviousTimeStep},Any}[] for i in mapped_variables push!(unfolded_mapping, _get_var(isa(i, PreviousTimeStep) ? i : Pair(i.first, i.second), process_)) # Note: We are using Pair(i.first, i.second) to make sure the Pair is specialized enough, because sometimes the vector in the mapping made the Pair not specialized enough e.g. [:v1 => :S => :v2,:v3 => :S] makes the pairs `Pair{Symbol, Any}`. @@ -154,35 +146,25 @@ function _get_var(i::Pair{Symbol,Any}, proc::Symbol=:unknown) return _get_var(first(i) => last(i), proc) end -@noinline function _warn_multiscale_model_string_scale() - Base.depwarn( - "String scale names in `MultiScaleModel(mapped_variables=...)` are deprecated and will be removed in a future release. Use Symbol scales, e.g. `:Leaf` instead of `\"Leaf\"`.", - :MultiScaleModel - ) +function _normalize_mapped_scale(scale::Symbol) + scale === Symbol("") && error("`Symbol(\"\")` same-scale mappings are removed. Use `SameScale()` instead.") + return scale end - -_normalize_mapped_scale(scale::Symbol) = scale -function _normalize_mapped_scale(scale::AbstractString) - _warn_multiscale_model_string_scale() - return Symbol(scale) -end - -# Case 1: [:variable_name => :Plant] (deprecated, coerced to symbol scale) -function _get_var(i::Pair{Symbol,S}, proc::Symbol=:unknown) where {S<:AbstractString} - scale = _normalize_mapped_scale(last(i)) - return first(i) => scale => first(i) +_normalize_mapped_scale(scale::SameScale) = scale +function _normalize_mapped_scale(scale) + error("Mapped scale names must be `Symbol`s, got `$(typeof(scale))` for `$(repr(scale))`.") end -function _get_var(i::Pair{Symbol,Pair{S,Symbol}}, proc::Symbol=:unknown) where {S<:Union{AbstractString,Symbol}} +function _get_var(i::Pair{Symbol,Pair{T,Symbol}}, proc::Symbol=:unknown) where {T<:Union{Symbol,SameScale}} return first(i) => (_normalize_mapped_scale(first(last(i))) => last(last(i))) end -function _get_var(i::Pair{Symbol,T}, proc::Symbol=:unknown) where {T<:AbstractVector{<:Union{AbstractString,Symbol}}} +function _get_var(i::Pair{Symbol,T}, proc::Symbol=:unknown) where {T<:AbstractVector{<:Symbol}} return first(i) => [_normalize_mapped_scale(scale) => first(i) for scale in last(i)] end # Case 5: [:variable_name => [:Leaf => :variable_name_1, :Internode => :variable_name_2]] -function _get_var(i::Pair{Symbol,T}, proc::Symbol=:unknown) where {T<:AbstractVector{<:Pair{<:Union{AbstractString,Symbol},Symbol}}} +function _get_var(i::Pair{Symbol,T}, proc::Symbol=:unknown) where {T<:AbstractVector{<:Pair{Symbol,Symbol}}} return first(i) => [_normalize_mapped_scale(first(mapping)) => last(mapping) for mapping in last(i)] end @@ -199,7 +181,7 @@ end # Case 8: [PreviousTimeStep(:variable_name),] function _get_var(i::PreviousTimeStep, proc::Symbol=:unknown) - return PreviousTimeStep(i.variable, proc) => Symbol("") => i.variable + return PreviousTimeStep(i.variable, proc) => SameScale() => i.variable end diff --git a/src/mtg/add_organ.jl b/src/mtg/add_organ.jl index fca4b9fe6..6d448b2b2 100644 --- a/src/mtg/add_organ.jl +++ b/src/mtg/add_organ.jl @@ -32,6 +32,7 @@ or the `test-mtg-dynamic.jl` test file for an example usage. function add_organ!(node::MultiScaleTreeGraph.Node, sim_object, link, symbol, scale; index=0, id=MultiScaleTreeGraph.new_id(MultiScaleTreeGraph.get_root(node)), attributes=Dict{Symbol,Any}(), check=true) new_node = MultiScaleTreeGraph.Node(id, node, MultiScaleTreeGraph.NodeMTG(link, symbol, index, scale), attributes) st = init_node_status!(new_node, sim_object.statuses, sim_object.status_templates, sim_object.reverse_multiscale_mapping, sim_object.var_need_init, check=check) + reindex_runtime_topology!(sim_object) return st end @@ -75,6 +76,21 @@ function _remove_temporal_state_for_node!(sim_object, nid::Int) return nothing end +function _subtree_node_ids(node::MultiScaleTreeGraph.Node) + ids = Int[] + MultiScaleTreeGraph.traverse!(node) do subtree_node + push!(ids, node_id(subtree_node)) + end + return ids +end + +function _remove_temporal_state_for_nodes!(sim_object, node_ids) + for nid in node_ids + _remove_temporal_state_for_node!(sim_object, nid) + end + return nothing +end + function _status_node_registered(sim_object, node::MultiScaleTreeGraph.Node) node_scale = symbol(node) haskey(sim_object.statuses, node_scale) || return false @@ -160,7 +176,9 @@ function remove_organ!(node::MultiScaleTreeGraph.Node, sim_object; attribute_nam _remove_status_from_scale!(sim_object.statuses, node_scale, st, nid) _remove_temporal_state_for_node!(sim_object, nid) pop!(node, attribute_name) - return MultiScaleTreeGraph.delete_node!(node) + deleted = MultiScaleTreeGraph.delete_node!(node) + reindex_runtime_topology!(sim_object) + return deleted end """ @@ -203,7 +221,10 @@ function reparent_organ!( ) old_parent = parent(node) + moved_node_ids = _subtree_node_ids(node) MultiScaleTreeGraph.reparent!(node, new_parent) _repair_reparent_child_links!(node, old_parent, new_parent) + _remove_temporal_state_for_nodes!(sim_object, moved_node_ids) + reindex_runtime_topology!(sim_object) return node end diff --git a/src/mtg/initialisation.jl b/src/mtg/initialisation.jl index 069c166e0..f15a6a523 100644 --- a/src/mtg/initialisation.jl +++ b/src/mtg/initialisation.jl @@ -48,6 +48,7 @@ function init_statuses(mtg, mapping, dependency_graph; type_promotion=nothing, v MultiScaleTreeGraph.traverse!(mtg) do node # e.g.: node = MultiScaleTreeGraph.get_node(mtg, 5) init_node_status!(node, statuses, mapped_vars, reverse_multiscale_mapping, vars_need_init, type_promotion, check=check) end + reindex_runtime_topology!(statuses, mapped_vars, reverse_multiscale_mapping) return (; statuses, mapped_vars, reverse_multiscale_mapping, vars_need_init) end @@ -172,6 +173,83 @@ function init_node_status!(node, statuses, mapped_vars, reverse_multiscale_mappi return st end +function _status_sort_key(st::Status) + hasproperty(st, :node) || return typemax(Int) + return node_id(st.node) +end + +function _sort_statuses_by_node_id!(statuses) + for statuses_at_scale in values(statuses) + sort!(statuses_at_scale; by=_status_sort_key) + end + return statuses +end + +function _empty_reverse_refvectors!(status_templates, reverse_multiscale_mapping) + for (_, target_scales) in reverse_multiscale_mapping + for (target_scale, vars) in target_scales + haskey(status_templates, target_scale) || continue + target_template = status_templates[target_scale] + for (_, target_var_) in vars + target_var = target_var_ isa PreviousTimeStep ? target_var_.variable : target_var_ + haskey(target_template, target_var) || continue + target_value = target_template[target_var] + target_value isa RefVector || continue + empty!(target_value) + end + end + end + return nothing +end + +function _rebuild_reverse_refvectors!(statuses, status_templates, reverse_multiscale_mapping) + _empty_reverse_refvectors!(status_templates, reverse_multiscale_mapping) + refs_by_target = Dict{Tuple{Symbol,Symbol},Vector{Tuple{Int,Base.RefValue}}}() + for (source_scale, statuses_at_scale) in statuses + haskey(reverse_multiscale_mapping, source_scale) || continue + for st in statuses_at_scale + for (target_scale, vars) in reverse_multiscale_mapping[source_scale] + haskey(status_templates, target_scale) || continue + target_template = status_templates[target_scale] + for (source_var, target_var_) in vars + source_var in propertynames(st) || continue + target_var = target_var_ isa PreviousTimeStep ? target_var_.variable : target_var_ + haskey(target_template, target_var) || continue + target_value = target_template[target_var] + target_value isa RefVector || continue + push!( + get!(refs_by_target, (target_scale, target_var), Tuple{Int,Base.RefValue}[]), + (_status_sort_key(st), refvalue(st, source_var)), + ) + end + end + end + end + for ((target_scale, target_var), refs) in refs_by_target + target_value = status_templates[target_scale][target_var] + sort!(refs; by=first) + for (_, ref) in refs + push!(target_value, ref) + end + end + return nothing +end + +function reindex_runtime_topology!(statuses, status_templates, reverse_multiscale_mapping) + _sort_statuses_by_node_id!(statuses) + _rebuild_reverse_refvectors!(statuses, status_templates, reverse_multiscale_mapping) + return nothing +end + +function reindex_runtime_topology!(sim_object) + reindex_runtime_topology!( + sim_object.statuses, + sim_object.status_templates, + sim_object.reverse_multiscale_mapping, + ) + return nothing +end + """ status_from_template(d::Dict{Symbol,Any}) diff --git a/src/mtg/mapping/compute_mapping.jl b/src/mtg/mapping/compute_mapping.jl index c4345f0d7..531e76d76 100644 --- a/src/mtg/mapping/compute_mapping.jl +++ b/src/mtg/mapping/compute_mapping.jl @@ -78,9 +78,6 @@ function variables_outputs_from_other_scale(mapped_vars) isnothing(orgs) && continue orgs_iterable = isa(orgs, Symbol) ? Symbol[orgs] : Symbol[orgs...] - filter!(o -> o != Symbol(""), orgs_iterable) - length(orgs_iterable) == 0 && continue # This can happen when we use a PreviousTimeStep alone - for o in orgs_iterable # The MappedVar can only have one value for the default, because it comes from the computing scale (the source scale): var_default_value = mapped_default(val) @@ -173,7 +170,6 @@ function transform_single_node_mapped_variables_as_self_node_output!(mapped_vars for (var, mapped_var) in pairs(vars) # e.g. var = :carbon_biomass; mapped_var = vars[var] if isa(mapped_var, MappedVar{SingleNodeMapping}) source_organ = mapped_organ(mapped_var) - source_organ == Symbol("") && continue # We skip the variables that are mapped to themselves (e.g. [PreviousTimeStep(:variable_name)], or just renaming a variable) if source_organ == organ error("Variable `$var` is mapped to its own scale in organ $organ. This is not allowed.") end @@ -223,10 +219,10 @@ function get_multiscale_default_value(mapped_vars, val, mapping_stacktrace=[], l if isa(val, MappedVar) && !isa(val, MappedVar{SelfNodeMapping}) # If the value is a MappedVar, we must find the default value of the variable it is mapping into. # Except if it is mapping to itself, in which case we return the value as is. + _is_same_scale_mapping(val) && return mapped_default(val) level += 1 # Find the default value of the variable from the scale it is mapping into (upper scale): m_organ = mapped_organ(val) - m_organ == Symbol("") && return mapped_default(val) # We skip the variables that are mapped to themselves (e.g. [PreviousTimeStep(:variable_name)], or just renaming a variable) if isa(m_organ, Symbol) m_organ = [m_organ] @@ -270,7 +266,7 @@ function default_variables_from_mapping(mapped_vars, verbose=true) mapped_vars_mutable = Dict{Symbol,Dict{Symbol,Any}}(k => Dict(pairs(v)) for (k, v) in mapped_vars) for (organ, vars) in mapped_vars # organ = :Leaf; vars = mapped_vars[organ] for (var, val) in pairs(vars) # var = :carbon_biomass; val = getproperty(vars,var) - if isa(val, MappedVar) && !isa(val, MappedVar{SelfNodeMapping}) && mapped_organ(val) != Symbol("") + if isa(val, MappedVar) && !isa(val, MappedVar{SelfNodeMapping}) && !_is_same_scale_mapping(val) mapping_stacktrace = Any[(mapped_organ=organ, mapped_variable=var, mapped_value=mapped_default(mapped_vars[organ][var]), level=1)] default_value = get_multiscale_default_value(mapped_vars, val, mapping_stacktrace) mapped_vars_mutable[organ][var] = MappedVar(source_organs(val), mapped_variable(val), source_variable(val), default_value) @@ -311,7 +307,6 @@ function convert_reference_values!(mapped_vars::Dict{Symbol,Dict{Symbol,Any}}) for (k, v) in vars # e.g.: k = :aPPFD_larger_scale; v = vars[k] if isa(v, MappedVar{SelfNodeMapping}) || isa(v, MappedVar{SingleNodeMapping}) mapped_org = isa(v, MappedVar{SelfNodeMapping}) ? organ : mapped_organ(v) - mapped_org == Symbol("") && continue key = mapped_org => source_variable(v) # First time we encounter this variable as a MappedVar, we create its value into the dict_mapped_vars Dict: @@ -351,7 +346,7 @@ function convert_reference_values!(mapped_vars::Dict{Symbol,Dict{Symbol,Any}}) # Third pass: getting the same reference for the variables that are mapped at the same scale to another variable (changing its name): for (organ, vars) in mapped_vars # e.g.: organ = :Plant; vars = mapped_vars[organ] for (k, v) in vars # e.g.: k = :carbon_allocation; v = vars[k] - if isa(v, MappedVar) && mapped_organ(v) == Symbol("") + if _is_same_scale_mapping(v) mapped_var = mapped_variable(v) isa(mapped_var, PreviousTimeStep) && (mapped_var = mapped_var.variable) if mapped_var == source_variable(v) diff --git a/src/mtg/mapping/mapping.jl b/src/mtg/mapping/mapping.jl index 0ac36f9e9..8f5c6aa4b 100755 --- a/src/mtg/mapping/mapping.jl +++ b/src/mtg/mapping/mapping.jl @@ -1,21 +1,6 @@ -""" - AbstractNodeMapping - -Abstract type for the type of node mapping, *e.g.* single node mapping or multiple node mapping. -""" -abstract type AbstractNodeMapping end - -@noinline function _warn_string_scale(context::Symbol) - Base.depwarn( - "String scale names are deprecated and will be removed in a future release. Use Symbol scales, e.g. `:Leaf` instead of `\"Leaf\"`.", - context - ) -end - _normalize_scale(scale::Symbol; warn::Bool=false, context::Symbol=:PlantSimEngine) = scale -function _normalize_scale(scale::AbstractString; warn::Bool=true, context::Symbol=:PlantSimEngine) - warn && _warn_string_scale(context) - return Symbol(scale) +function _normalize_scale(scale; warn::Bool=false, context::Symbol=:PlantSimEngine) + error("Scale names must be `Symbol`s, got `$(typeof(scale))` for `$(repr(scale))` in `$context`.") end """ @@ -28,9 +13,6 @@ struct SingleNodeMapping <: AbstractNodeMapping scale::Symbol end -SingleNodeMapping(scale::Union{Symbol,AbstractString}) = - SingleNodeMapping(_normalize_scale(scale; warn=scale isa AbstractString, context=:SingleNodeMapping)) - """ SelfNodeMapping() @@ -52,11 +34,9 @@ struct MultiNodeMapping <: AbstractNodeMapping scale::Vector{Symbol} end -MultiNodeMapping(scale::Union{Symbol,AbstractString}) = MultiNodeMapping([scale]) -function MultiNodeMapping(scale::AbstractVector{<:Union{Symbol,AbstractString}}) - normalized = Symbol[ - _normalize_scale(s; warn=s isa AbstractString, context=:MultiNodeMapping) for s in scale - ] +MultiNodeMapping(scale::Symbol) = MultiNodeMapping([scale]) +function MultiNodeMapping(scale::AbstractVector{<:Symbol}) + normalized = Symbol[_normalize_scale(s; context=:MultiNodeMapping) for s in scale] return MultiNodeMapping(normalized) end @@ -93,6 +73,7 @@ source_organs(m::MappedVar) = m.source_organ source_organs(m::MappedVar{O,V1,V2,T}) where {O<:AbstractNodeMapping,V1,V2,T} = nothing mapped_organ(m::MappedVar{O,V1,V2,T}) where {O,V1,V2,T} = source_organs(m).scale mapped_organ(m::MappedVar{O,V1,V2,T}) where {O<:SelfNodeMapping,V1,V2,T} = nothing +mapped_organ(m::MappedVar{O,V1,V2,T}) where {O<:SameScale,V1,V2,T} = nothing mapped_organ_type(m::MappedVar{O,V1,V2,T}) where {O<:AbstractNodeMapping,V1,V2,T} = O source_variable(m::MappedVar) = m.source_variable function source_variable(m::MappedVar{O,V1,V2,T}, organ) where {O<:SingleNodeMapping,V1,V2<:Symbol,T} @@ -113,12 +94,18 @@ mapped_default(m::MappedVar) = m.source_default mapped_default(m::MappedVar{O,V1,V2,T}, organ) where {O<:MultiNodeMapping,V1,V2<:Vector{Symbol},T} = m.source_default[findfirst(o -> o == organ, mapped_organ(m))] mapped_default(m) = m # For any variable that is not a MappedVar, we return it as is +_is_same_scale_mapping(m::MappedVar) = source_organs(m) isa SameScale +_is_same_scale_mapping(::Any) = false + # This defines the type of mapping setup: either single or multiscale. Used to dispatch methods for e.g. `dep` or `to_initialize`. abstract type AbstractScaleSetup end struct MultiScale <: AbstractScaleSetup end struct SingleScale <: AbstractScaleSetup end +const ModelRateDeclarations = Dict{Symbol,Any} +const ReverseMultiscaleMapping = Dict{Symbol,Dict{Symbol,Dict{Symbol,Symbol}}} + """ ModelMappingInfo @@ -132,7 +119,7 @@ struct ModelMappingInfo scales::Vector{Symbol} models_per_scale::Dict{Symbol,Int} processes_per_scale::Dict{Symbol,Vector{Symbol}} - declared_rates::Dict{Symbol,Any} + declared_rates::ModelRateDeclarations vars_need_init::Any model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}} recommendations::Vector{String} @@ -146,7 +133,7 @@ function _empty_model_mapping_info() Symbol[], Dict{Symbol,Int}(), Dict{Symbol,Vector{Symbol}}(), - Dict{Symbol,Any}(), + ModelRateDeclarations(), NamedTuple(), Dict{Symbol,Dict{Symbol,ModelSpec}}(), String[], @@ -307,20 +294,14 @@ Base.keys(::ModelMapping{SingleScale}) = (:Default,) Base.values(mapping::ModelMapping{SingleScale}) = ((values(mapping.data.models)..., status(mapping.data)),) Base.pairs(mapping::ModelMapping{SingleScale}) = (:Default => (values(mapping.data.models)..., status(mapping.data)),) Base.getindex(mapping::ModelMapping, key::Symbol) = mapping.data[key] -function Base.getindex(mapping::ModelMapping, key::AbstractString) - sym = _normalize_scale(key; warn=true, context=:ModelMapping) - return mapping.data[sym] -end function Base.getindex(mapping::ModelMapping{SingleScale}, key::Symbol) if key == :Default return (values(mapping.data.models)..., status(mapping.data)) end return getindex(mapping.data, key) end -Base.getindex(mapping::ModelMapping{SingleScale}, key::AbstractString) = getindex(mapping, _normalize_scale(key; warn=true, context=:ModelMapping)) Base.getindex(mapping::ModelMapping{SingleScale}, key::Integer) = getindex(mapping.data, key) Base.haskey(mapping::ModelMapping, key::Symbol) = haskey(mapping.data, key) -Base.haskey(mapping::ModelMapping, key::AbstractString) = haskey(mapping.data, _normalize_scale(key; warn=true, context=:ModelMapping)) Base.eltype(::Type{ModelMapping}) = Pair{Symbol,Tuple} Base.copy(mapping::ModelMapping{MultiScale}) = _build_model_mapping(MultiScale, copy(mapping.data); validated=mapping.info.validated, type_promotion=deepcopy(mapping.type_promotion)) Base.copy(mapping::ModelMapping{SingleScale}) = _build_model_mapping(SingleScale, copy(mapping.data); validated=mapping.info.validated, type_promotion=deepcopy(mapping.type_promotion)) @@ -372,33 +353,34 @@ mapping and applied when a `GraphSimulation` is initialized. """ function ModelMapping( args...; - scale::Union{Symbol,AbstractString}=:Default, + scale::Symbol=:Default, status=nothing, type_promotion::Union{Nothing,Dict}=nothing, check::Bool=true, processes... ) isempty(args) && isempty(processes) && error( - "No mapping or model was provided. Use `ModelMapping(\"Scale\" => models)` or pass models directly." + "No mapping or model was provided. Use `ModelMapping(:Scale => models)` or pass models directly." ) - - # Backwards compatibility: allow dict-like construction for type promotion maps, - # e.g. `ModelMapping(Float64 => Float32)`. - if !isempty(args) && all(arg -> arg isa Pair && !(first(arg) isa Union{AbstractString,Symbol}), args) - return Dict(args) - end + any(arg -> arg isa Pair && first(arg) isa AbstractString, args) && error( + "String scale names are removed. Use Symbol scales, e.g. `:Leaf` instead of `\"Leaf\"`." + ) + !isempty(args) && all(arg -> arg isa Pair && !(first(arg) isa Symbol), args) && error( + "`ModelMapping(Float64 => Float32)` type-promotion shorthand is removed. ", + "Use `Dict(Float64 => Float32)` as the `type_promotion` value." + ) if _all_scale_pairs(args) isempty(processes) || error( "Cannot mix scale-level pairs with process keyword arguments. ", - "Use either `\"Scale\" => models` pairs, or single-scale process/model arguments." + "Use either `:Scale => models` pairs, or single-scale process/model arguments." ) isnothing(status) || error( "`status` cannot be used with scale-level pair syntax. ", "Provide statuses inside each scale mapping instead." ) raw_mapping = Dict{Symbol,Any}( - _normalize_scale(first(pair); warn=first(pair) isa AbstractString, context=:ModelMapping) => last(pair) + _normalize_scale(first(pair); context=:ModelMapping) => last(pair) for pair in args ) return ModelMapping{MultiScale}(raw_mapping; check=check, type_promotion=type_promotion) @@ -460,11 +442,11 @@ type_promotion(::Any) = nothing _type_promotion(mapping) = type_promotion(mapping) function _all_scale_pairs(args) - !isempty(args) && all(arg -> arg isa Pair && first(arg) isa Union{AbstractString,Symbol}, args) + !isempty(args) && all(arg -> arg isa Pair && first(arg) isa Symbol, args) end function _contains_scale_like_pair(args) - any(arg -> arg isa Pair && first(arg) isa Union{AbstractString,Symbol}, args) + any(arg -> arg isa Pair && first(arg) isa Symbol, args) end function _single_scale_mapping_entries(args, processes, status) @@ -496,7 +478,7 @@ function _normalize_multiscale_mapping(mapping::AbstractDict) isempty(mapping) && error("ModelMapping cannot be empty. Provide at least one scale with models.") normalized = Dict{Symbol,Tuple}() for (scale, scale_mapping) in pairs(mapping) - scale_name = _normalize_scale(scale; warn=scale isa AbstractString, context=:ModelMapping) + scale_name = _normalize_scale(scale; context=:ModelMapping) normalized[scale_name] = _normalize_scale_mapping(scale_name, scale_mapping) end return normalized @@ -631,19 +613,15 @@ _mapping_item_mapped_variables(::Any) = Pair{Symbol,Symbol}[] _mapping_item_model(item::ModelSpec) = model_(item) _mapping_item_model(item::MultiScaleModel) = model_(item) -function _as_mapping_scale(source_scale::AbstractString) - isempty(source_scale) && return nothing - return _normalize_scale(source_scale; warn=true, context=:ModelMapping) -end - function _as_mapping_scale(source_scale::Symbol) - source_scale === Symbol("") && return nothing - return _normalize_scale(source_scale; warn=false, context=:ModelMapping) + source_scale === Symbol("") && error("`Symbol(\"\")` same-scale mappings are removed. Use `SameScale()` instead.") + return _normalize_scale(source_scale; context=:ModelMapping) end +_as_mapping_scale(::SameScale) = nothing -_as_mapping_sources(source::Pair{<:Union{AbstractString,Symbol},Symbol}) = +_as_mapping_sources(source::Pair{T,Symbol}) where {T<:Union{Symbol,SameScale}} = (_as_mapping_scale(first(source)) => last(source),) -_as_mapping_sources(source::AbstractVector{<:Pair{<:Union{AbstractString,Symbol},Symbol}}) = +_as_mapping_sources(source::AbstractVector{<:Pair}) = Tuple(_as_mapping_scale(first(item)) => last(item) for item in source) function _available_variables_by_scale(mapping::Dict{Symbol,Tuple}) @@ -682,7 +660,7 @@ function _available_variables_by_scale(mapping::Dict{Symbol,Tuple}) end function _declared_model_rates_by_scale(mapping::Dict{Symbol,Tuple}) - rates = Dict{Symbol,Any}() + rates = ModelRateDeclarations() for (scale, scale_mapping) in mapping declared_rates = unique(filter(!isnothing, map(model_rate, get_models(scale_mapping)))) if length(declared_rates) > 1 @@ -709,7 +687,7 @@ function _spec_declares_multirate(spec::ModelSpec) return false end -function _mapping_declares_multirate(model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}, declared_rates::Dict{Symbol,Any}) +function _mapping_declares_multirate(model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}, declared_rates::ModelRateDeclarations) any(!isnothing, values(declared_rates)) && return true for specs_at_scale in values(model_specs), spec in values(specs_at_scale) _spec_declares_multirate(spec) && return true @@ -757,7 +735,7 @@ function _build_model_mapping_info(::Type{SingleScale}, mapping::ModelList; vali ) ) - declared_rates = Dict{Symbol,Any}(:Default => nothing) + declared_rates = ModelRateDeclarations(:Default => nothing) vars_need_init = try to_initialize(mapping) catch @@ -786,7 +764,7 @@ function _build_model_mapping_info(::Type{MultiScale}, mapping::Dict{Symbol,Tupl declared_rates = try _declared_model_rates_by_scale(mapping) catch - Dict{Symbol,Any}(scale => nothing for scale in scales) + ModelRateDeclarations(scale => nothing for scale in scales) end model_specs = try _parse_model_specs_from_mapping(mapping) diff --git a/src/mtg/mapping/model_generation_from_status_vectors.jl b/src/mtg/mapping/model_generation_from_status_vectors.jl index 694de3018..512a10b23 100644 --- a/src/mtg/mapping/model_generation_from_status_vectors.jl +++ b/src/mtg/mapping/model_generation_from_status_vectors.jl @@ -66,7 +66,6 @@ PlantSimEngine.TimeStepDependencyTrait(::Type{<:GeneratedStatusVectorModel}) = P function replace_mapping_status_vectors_with_generated_models(mapping_with_vectors_in_status, timestep_model_organ_level, nsteps) timestep_model_organ_level = _normalize_scale( timestep_model_organ_level; - warn=timestep_model_organ_level isa AbstractString, context=:ModelMapping ) @@ -78,7 +77,7 @@ function replace_mapping_status_vectors_with_generated_models(mapping_with_vecto # we are now certain a model will be generated, and that the timestep models need to be inserted mapping = Dict( - _normalize_scale(organ; warn=organ isa AbstractString, context=:ModelMapping) => models + _normalize_scale(organ; context=:ModelMapping) => models for (organ, models) in mapping_with_vectors_in_status ) for (organ,models) in mapping @@ -129,8 +128,8 @@ function replace_mapping_status_vectors_with_generated_models(mapping_with_vecto end function generate_model_from_status_vector_variable(mapping, timestep_scale, status, organ, nsteps) - timestep_scale = _normalize_scale(timestep_scale; warn=timestep_scale isa AbstractString, context=:ModelMapping) - organ = _normalize_scale(organ; warn=organ isa AbstractString, context=:ModelMapping) + timestep_scale = _normalize_scale(timestep_scale; context=:ModelMapping) + organ = _normalize_scale(organ; context=:ModelMapping) # Ah, another point that remains to be seen is that those CSV.SentinelArrays.ChainedVector obtained from the meteo file isn't an AbstractVector # meaning currently we won't generate models from them unless the conversion is made before that @@ -279,5 +278,5 @@ function check_statuses_contain_no_remaining_vectors(mapping) end end end - return (Symbol(""), true) + return (nothing, true) end diff --git a/src/mtg/mapping/reverse_mapping.jl b/src/mtg/mapping/reverse_mapping.jl index aafca47a5..b74eb23c0 100644 --- a/src/mtg/mapping/reverse_mapping.jl +++ b/src/mtg/mapping/reverse_mapping.jl @@ -77,7 +77,7 @@ function reverse_mapping(mapping::AbstractDict{Symbol,T}; all=true) where {T<:An end function reverse_mapping(mapped_vars::Dict{Symbol,Dict{Symbol,Any}}; all=true) - reverse_multiscale_mapping = Dict{Symbol,Dict{Symbol,Dict{Symbol,Any}}}(org => Dict{Symbol,Dict{Symbol,Any}}() for org in keys(mapped_vars)) + reverse_multiscale_mapping = ReverseMultiscaleMapping(org => Dict{Symbol,Dict{Symbol,Symbol}}() for org in keys(mapped_vars)) for (organ, vars) in mapped_vars # e.g.: organ = :Plant; vars = mapped_vars[organ] for (var, val) in vars # e.g. var = :Rm_organs; val = vars[var] if isa(val, MappedVar) && !isa(val, MappedVar{SelfNodeMapping}) && (all || !isa(val, MappedVar{SingleNodeMapping})) @@ -90,16 +90,15 @@ function reverse_mapping(mapped_vars::Dict{Symbol,Dict{Symbol,Any}}; all=true) if mapped_orgs isa Symbol mapped_orgs = [mapped_orgs] elseif mapped_orgs isa AbstractString - mapped_orgs = [_normalize_scale(mapped_orgs; warn=true, context=:ModelMapping)] + error("String scale names are removed. Use Symbol scales, e.g. `:Leaf` instead of `\"Leaf\"`.") end for mapped_o in mapped_orgs # e.g.: mapped_o = :Leaf - mapped_o == Symbol("") && continue # if !haskey(reverse_multiscale_mapping, mapped_o) # reverse_multiscale_mapping[mapped_o] = Dict{Symbol,Vector{MappedVar}}() # end if !haskey(reverse_multiscale_mapping[mapped_o], organ) - reverse_multiscale_mapping[mapped_o][organ] = Dict{Symbol,Any}(source_variable(val, mapped_o) => mapped_variable(val)) + reverse_multiscale_mapping[mapped_o][organ] = Dict{Symbol,Symbol}(source_variable(val, mapped_o) => mapped_variable(val)) end push!(reverse_multiscale_mapping[mapped_o][organ], source_variable(val, mapped_o) => mapped_variable(val)) end diff --git a/src/mtg/model_spec_inference.jl b/src/mtg/model_spec_inference.jl index ef2701fa6..2376fed85 100644 --- a/src/mtg/model_spec_inference.jl +++ b/src/mtg/model_spec_inference.jl @@ -464,14 +464,14 @@ function _mapped_source_scales_for_input(spec::ModelSpec, input_var::Symbol) mapped_input == input_var || continue rhs = last(mv) - if rhs isa Pair{Symbol,Symbol} + if rhs isa Pair src_scale = first(rhs) - src_scale == Symbol("") || push!(scales, src_scale) + src_scale isa SameScale || push!(scales, src_scale) elseif rhs isa AbstractVector for item in rhs - item isa Pair{Symbol,Symbol} || continue + item isa Pair || continue src_scale = first(item) - src_scale == Symbol("") || push!(scales, src_scale) + src_scale isa SameScale || push!(scales, src_scale) end end end @@ -496,18 +496,18 @@ function _mapped_sources_for_input(spec::ModelSpec, input_var::Symbol) mapped = mapped_variables_(spec) isempty(mapped) && return Pair{Symbol,Symbol}[] - sources = Pair{Symbol,Symbol}[] + sources = Pair{Union{Symbol,SameScale},Symbol}[] for mv in mapped mapped_input = first(mv) mapped_input = mapped_input isa PreviousTimeStep ? mapped_input.variable : mapped_input mapped_input == input_var || continue rhs = last(mv) - if rhs isa Pair{Symbol,Symbol} + if rhs isa Pair push!(sources, rhs) elseif rhs isa AbstractVector for item in rhs - item isa Pair{Symbol,Symbol} || continue + item isa Pair || continue push!(sources, item) end end @@ -530,7 +530,7 @@ function _infer_binding_from_multiscale_mapping( mapped_sources = _mapped_sources_for_input(spec, input_var) # Mapping exists but does not point to another scale (self/same-scale aliasing): # avoid generic same-name inference in that case. - filtered_sources = filter(s -> first(s) != Symbol(""), mapped_sources) + filtered_sources = filter(s -> !(first(s) isa SameScale), mapped_sources) isempty(filtered_sources) && return :skip # Multi-source mapping (e.g. vectors from several scales) cannot be represented @@ -791,13 +791,9 @@ For a `GraphSimulation`, this returns the already resolved model specs used by t function resolved_model_specs(mapping::AbstractDict; infer::Bool=true, validate::Bool=true) model_specs = Dict{Symbol,Dict{Symbol,ModelSpec}}() for (scale, declarations) in pairs(mapping) - scale_sym = if scale isa Symbol - scale - elseif scale isa AbstractString - _normalize_scale(scale; warn=true, context=:ModelSpec) - else - error("Scale keys in `resolved_model_specs(mapping)` must be `Symbol` (preferred) or `String`, got `$(typeof(scale))`.") - end + scale_sym = scale isa Symbol ? + scale : + error("Scale keys in `resolved_model_specs(mapping)` must be `Symbol`s, got `$(typeof(scale))`.") model_specs[scale_sym] = parse_model_specs(declarations) end diff --git a/src/mtg/model_spec_validation.jl b/src/mtg/model_spec_validation.jl index 6fd210ee6..c3cbc9192 100644 --- a/src/mtg/model_spec_validation.jl +++ b/src/mtg/model_spec_validation.jl @@ -179,9 +179,9 @@ function _validate_binding_target( ) isnothing(source_scale) && return nothing - src_scale = source_scale isa AbstractString ? - _normalize_scale(source_scale; warn=true, context=:ModelSpec) : - source_scale + src_scale = source_scale isa Symbol ? + source_scale : + error("Source scale for input `$(input_var)` in process `$(process)` must be a `Symbol`, got `$(typeof(source_scale))`.") haskey(model_specs, src_scale) || error( "Unknown source scale `$(src_scale)` for input `$(input_var)` in process `$(process)` at scale `$(scale)`." ) @@ -232,9 +232,9 @@ function _validate_input_binding( end if haskey(binding, :scale) - isnothing(binding.scale) || binding.scale isa Symbol || binding.scale isa AbstractString || error( + isnothing(binding.scale) || binding.scale isa Symbol || error( "Invalid input binding for input `$(input_var)` in process `$(process)` at scale `$(scale)`: ", - "`scale` must be a Symbol, String or `nothing`, got `$(typeof(binding.scale))`." + "`scale` must be a Symbol or `nothing`, got `$(typeof(binding.scale))`." ) source_scale = binding.scale end diff --git a/src/mtg/node_mapping_types.jl b/src/mtg/node_mapping_types.jl new file mode 100644 index 000000000..9ef0dd753 --- /dev/null +++ b/src/mtg/node_mapping_types.jl @@ -0,0 +1,15 @@ +""" + AbstractNodeMapping + +Abstract type for node mapping markers, such as single-node, multi-node, self, +or same-scale mappings. +""" +abstract type AbstractNodeMapping end + +""" + SameScale() + +Marker used in multiscale variable mappings when a variable is aliased or +renamed from another variable at the same scale. +""" +struct SameScale <: AbstractNodeMapping end diff --git a/src/run.jl b/src/run.jl index ea1b3fb99..251f89662 100644 --- a/src/run.jl +++ b/src/run.jl @@ -114,18 +114,6 @@ function adjust_weather_timesteps_to_given_length(desired_length, meteo) end -function _all_modellists_collection(object) - if isa(object, AbstractArray) - return all(x -> x isa ModelList || x isa ModelMapping{SingleScale}, object) - elseif isa(object, AbstractDict) - return all(x -> x isa ModelList || x isa ModelMapping{SingleScale}, values(object)) - end - return false -end - -_single_scale_runtime_object(object) = object -_single_scale_runtime_object(mapping::ModelMapping) = _modellist_from_model_mapping(mapping) - function _modellist_from_model_mapping(mapping::ModelMapping{SingleScale}) mapping.data end @@ -134,10 +122,8 @@ function _modellist_from_model_mapping(::ModelMapping{MultiScale}) error("This `ModelMapping` is a multiscale mapping. ", "Use `run!(mtg, mapping, ...)` for multiscale mappings.") end -_modellist_from_model_mapping(mapping::ModelList) = mapping - function run!( - mapping::M, + mapping::ModelMapping{SingleScale}, meteo=nothing, constants=PlantMeteo.Constants(), extra=nothing; @@ -146,7 +132,7 @@ function run!( executor=ThreadedEx(), return_requested_outputs=false, requested_outputs_sink=DataFrames.DataFrame -) where {M<:Union{ModelMapping{SingleScale},ModelList}} +) _validate_meteo_duration(meteo) model_list = _modellist_from_model_mapping(mapping) _run_modellist_singleton( @@ -231,84 +217,11 @@ function run!( end ########################################################################################## -## ModelList (single-scale) simulations +## Single-scale simulations ########################################################################################## -# 1- several ModelList objects and several time-steps -function run!( - ::TableAlike, - object::T, - meteo::TimeStepTable{A}, - constants=PlantMeteo.Constants(), - extra=nothing; - tracked_outputs=nothing, - check=true, - executor=ThreadedEx(), - return_requested_outputs=false, - requested_outputs_sink=DataFrames.DataFrame -) where {T<:Union{AbstractArray,AbstractDict},A} - if _all_modellists_collection(object) - Base.depwarn( - "`run!` with a collection of `ModelList` is deprecated. Use a collection of `ModelMapping` objects instead.", - :run! - ) - end - - tracked_outputs isa OutputRequest && error("`OutputRequest` is only supported for MTG multi-rate simulations.") - tracked_outputs isa AbstractVector{<:OutputRequest} && error("`OutputRequest` is only supported for MTG multi-rate simulations.") - return_requested_outputs && error("`return_requested_outputs=true` is only supported for MTG multi-rate simulations.") - - if executor != SequentialEx() - @warn string( - "Parallelisation over objects was removed, (but may be reintroduced in the future). Parallelisation will only occur over timesteps." - ) maxlog = 1 - end - - outputs_collection = isa(object, AbstractArray) ? [] : isnothing(tracked_outputs) ? Dict() : Dict{TimeStepTable{Status{typeof(tracked_outputs)}}} - - # Each object: - for obj in object - - if isa(object, AbstractArray) - push!(outputs_collection, run!(obj, meteo, constants, extra, tracked_outputs=tracked_outputs, check=check, executor=executor)) - else - outputs_collection[obj.first] = run!(obj.second, meteo, constants, extra, tracked_outputs=tracked_outputs, check=check, executor=executor) - end - - end - return outputs_collection -end - # 2 - One object, one or multiple meteo time-step(s), with vectors provided in the status # (meaning a single meteo timestep might be expanded to fit the status vector size) -function run!( - ::SingletonAlike, - object::T, - meteo=nothing, - constants=PlantMeteo.Constants(), - extra=nothing; - tracked_outputs=nothing, - check=true, - executor=ThreadedEx(), - return_requested_outputs=false, - requested_outputs_sink=DataFrames.DataFrame -) where {T<:ModelList} - Base.depwarn( - "`run!(::ModelList, ...)` is deprecated. Use `run!(ModelMapping(...), ...)` instead.", - :run! - ) - _run_modellist_singleton( - object, - meteo, - constants, - extra; - tracked_outputs=tracked_outputs, - check=check, - executor=executor, - return_requested_outputs=return_requested_outputs - ) -end - function run!( ::SingletonAlike, object::T, @@ -430,72 +343,6 @@ function _run_modellist_singleton( return outputs_preallocated end -# 3- several objects and one meteo time-step -function run!( - ::TableAlike, - object::T, - meteo, - constants=PlantMeteo.Constants(), - extra=nothing; - tracked_outputs=nothing, - check=true, - executor=ThreadedEx(), - return_requested_outputs=false, - requested_outputs_sink=DataFrames.DataFrame -) where {T<:Union{AbstractArray,AbstractDict}} - if _all_modellists_collection(object) - Base.depwarn( - "`run!` with a collection of `ModelList` is deprecated. Use a collection of `ModelMapping` objects instead.", - :run! - ) - end - - tracked_outputs isa OutputRequest && error("`OutputRequest` is only supported for MTG multi-rate simulations.") - tracked_outputs isa AbstractVector{<:OutputRequest} && error("`OutputRequest` is only supported for MTG multi-rate simulations.") - return_requested_outputs && error("`return_requested_outputs=true` is only supported for MTG multi-rate simulations.") - runtime_objects = [_single_scale_runtime_object(obj) for obj in collect(values(object))] - dep_graphs = [dep(obj) for obj in runtime_objects] - #obj_parallelizable = all([object_parallelizable(graph) for graph in dep_graphs]) - - # Check if the simulation can be parallelized over objects: - if executor != SequentialEx() - @warn string( - "Parallelisation over objects was removed, (but may be reintroduced in the future). Parallelisation will only occur over timesteps." - ) maxlog = 1 - end - - # Each object: - for (i, obj) in enumerate(runtime_objects) - - if check - # Check if the meteo data and the status have the same length (or length 1) - check_dimensions(obj, meteo) - - if length(dep_graphs[i].not_found) > 0 - error( - "The following processes are missing to run the ModelList: ", - dep_graphs[i].not_found - ) - end - end - end - - outputs_collection = isa(object, AbstractArray) ? [] : isnothing(tracked_outputs) ? Dict() : Dict{TimeStepTable{Status{typeof(tracked_outputs)}}} - - # Each object: - for obj in object - if isa(object, AbstractArray) - push!(outputs_collection, run!(obj, meteo, constants, extra, tracked_outputs=tracked_outputs, check=check, executor=executor)) - else - outputs_collection[obj.first] = run!(obj.second, meteo, constants, extra, tracked_outputs=tracked_outputs, check=check, executor=executor) - end - - end - return outputs_collection -end - - - # Not exposed to the user : # for each dependency node in the graph (always one time-step, one object), actual workhorse function run_node!( @@ -606,40 +453,6 @@ function run!( return outputs(sim) end -function run!( - object::MultiScaleTreeGraph.Node, - mapping::AbstractDict{Symbol,T} where {T}, - meteo=nothing, - constants=PlantMeteo.Constants(), - extra=nothing; - nsteps=nothing, - tracked_outputs=nothing, - type_promotion=nothing, - check=true, - executor=ThreadedEx(), - return_requested_outputs=false, - requested_outputs_sink=DataFrames.DataFrame -) - Base.depwarn( - "`run!(mtg, mapping::AbstractDict, ...)` is deprecated. Use `run!(mtg, ModelMapping(mapping), ...)` or construct `ModelMapping(...)` directly.", - :run! - ) - run!( - object, - ModelMapping(mapping; type_promotion=type_promotion), - meteo, - constants, - extra; - nsteps=nsteps, - tracked_outputs=tracked_outputs, - type_promotion=type_promotion, - check=check, - executor=executor, - return_requested_outputs=return_requested_outputs, - requested_outputs_sink=requested_outputs_sink - ) -end - function run!( ::TreeAlike, object::GraphSimulation, diff --git a/src/time/multirate.jl b/src/time/multirate.jl index 1def05682..28aa12b10 100644 --- a/src/time/multirate.jl +++ b/src/time/multirate.jl @@ -222,6 +222,8 @@ mutable struct AggregateCache{T<:Real} <: OutputCache window_start::Float64 end +const OutputStream = Vector{Tuple{Float64,Any}} + """ ExportBuffer() @@ -244,6 +246,23 @@ end ExportBuffer(scale::Symbol, process::Symbol, var::Symbol) = ExportBuffer(scale, process, var, Int[], Int[], Any[]) +""" + OutputExportPlan + +Resolved online output export request used by the multi-rate runtime. +""" +struct OutputExportPlan{POL<:SchedulePolicy,C,MS} + name::Symbol + scale::Symbol + var::Symbol + process::Symbol + policy::POL + clock::C + model_spec::MS + source_dt::Float64 + source_sample_duration_seconds::Float64 +end + """ TemporalState(caches, last_run, streams, producer_horizons, export_plans, export_rows) TemporalState() @@ -261,9 +280,9 @@ interpolated policies. mutable struct TemporalState{ C<:AbstractDict{OutputKey,OutputCache}, L<:AbstractDict{ModelKey,Float64}, - S<:AbstractDict{OutputKey,Vector{Tuple{Float64,Any}}}, + S<:AbstractDict{OutputKey,OutputStream}, H<:AbstractDict{Tuple{Symbol,Symbol,Symbol},Float64}, - P<:AbstractVector, + P<:AbstractVector{<:OutputExportPlan}, R<:AbstractDict{Symbol,ExportBuffer} } caches::C @@ -277,8 +296,8 @@ end TemporalState() = TemporalState( Dict{OutputKey,OutputCache}(), Dict{ModelKey,Float64}(), - Dict{OutputKey,Vector{Tuple{Float64,Any}}}(), + Dict{OutputKey,OutputStream}(), Dict{Tuple{Symbol,Symbol,Symbol},Float64}(), - Any[], + OutputExportPlan[], Dict{Symbol,ExportBuffer}() ) diff --git a/src/time/runtime/bindings.jl b/src/time/runtime/bindings.jl index 25507bada..01ec5f484 100644 --- a/src/time/runtime/bindings.jl +++ b/src/time/runtime/bindings.jl @@ -7,6 +7,8 @@ Extract a symbol variable name from dependency graph variable descriptors function _symbol_from_dependency_var(x) if x isa Symbol return x + elseif x isa ProducerVariable + return x.input elseif x isa PreviousTimeStep return x.variable elseif x isa MappedVar @@ -17,10 +19,20 @@ function _symbol_from_dependency_var(x) end end +function _source_symbol_from_dependency_var(x) + if x isa ProducerVariable + return x.source + elseif x isa MappedVar + sv = source_variable(x) + return sv isa PreviousTimeStep ? sv.variable : sv + end + return _symbol_from_dependency_var(x) +end + """ _push_candidate_producer!(candidates, process_key, vars, input_var) -Append producer candidates `(process, input_var)` when `vars` contains +Append producer candidates `(process, source_var)` when `vars` contains `input_var`. """ function _push_candidate_producer!(candidates::Vector{Tuple{Symbol,Symbol}}, process_key, vars, input_var::Symbol) @@ -29,7 +41,8 @@ function _push_candidate_producer!(candidates::Vector{Tuple{Symbol,Symbol}}, pro s = _symbol_from_dependency_var(v) isnothing(s) && continue if s == input_var - push!(candidates, (process, input_var)) + source = _source_symbol_from_dependency_var(v) + source isa Symbol && push!(candidates, (process, source)) end end end @@ -78,8 +91,13 @@ function _parse_input_binding(binding) var = haskey(binding, :var) ? binding.var : nothing scale = if haskey(binding, :scale) sc = binding.scale - isnothing(sc) ? nothing : - (sc isa AbstractString ? _normalize_scale(sc; warn=true, context=:ModelSpec) : sc) + if isnothing(sc) + nothing + elseif sc isa Symbol + sc + else + error("Input binding scale must be a `Symbol`, got `$(typeof(sc))` for `$(repr(sc))`.") + end else nothing end diff --git a/src/time/runtime/environment_backends.jl b/src/time/runtime/environment_backends.jl index 85440b46f..9e777c90e 100644 --- a/src/time/runtime/environment_backends.jl +++ b/src/time/runtime/environment_backends.jl @@ -116,30 +116,12 @@ function validate_meteo_inputs(model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}, available = environment_variables(backend) isnothing(available) && return nothing - missing_rows = NamedTuple[] - for (scale, specs_at_scale) in model_specs - for (process, spec) in specs_at_scale - required = _raw_meteo_requirements_for_spec(spec) - missing = Symbol[var for var in required if !(var in available)] - isempty(missing) && continue - push!(missing_rows, (scale=scale, process=process, missing=Tuple(missing))) - end - end - - isempty(missing_rows) && return nothing - - details = join( - [ - string(row.scale, "/", row.process, " missing ", row.missing) - for row in missing_rows - ], - "; " - ) - error( - "Environment backend `$(typeof(backend))` is missing variables required by model `meteo_inputs_`: ", - details, - ". Add the variables to the backend, declare a `MeteoBindings(source=...)` remapping, ", - "or remove the unused meteo input from the model trait." + missing_rows = _collect_missing_meteo_rows(model_specs, var -> var in available) + return _error_missing_meteo_inputs( + missing_rows; + subject="Environment backend `$(typeof(backend))`", + noun="variables", + target="the backend" ) end diff --git a/src/time/runtime/input_resolution.jl b/src/time/runtime/input_resolution.jl index 759a44d71..105dd8eb6 100644 --- a/src/time/runtime/input_resolution.jl +++ b/src/time/runtime/input_resolution.jl @@ -111,10 +111,10 @@ function _resolved_interpolated_value_for_source( return samples[1][2], true end -function _resolve_window_reducer(reducer) +function _normalize_time_reducer(reducer; context::AbstractString="reducer") if reducer isa DataType reducer <: PlantMeteo.AbstractTimeReducer || error( - "Unsupported reducer type `$(reducer)`. Use a PlantMeteo reducer type/instance or a callable." + "Unsupported $(context) type `$(reducer)`. Use a PlantMeteo reducer type/instance or a callable." ) return reducer() elseif reducer isa PlantMeteo.AbstractTimeReducer @@ -124,11 +124,13 @@ function _resolve_window_reducer(reducer) end error( - "Unsupported reducer value `$(reducer)` of type `$(typeof(reducer))`. ", + "Unsupported $(context) value `$(reducer)` of type `$(typeof(reducer))`. ", "Use a PlantMeteo reducer type/instance or a callable." ) end +_resolve_window_reducer(reducer) = _normalize_time_reducer(reducer; context="reducer") + function _window_reduce(vals::AbstractVector{<:Real}, durations::AbstractVector{<:Real}, policy::SchedulePolicy) reducer = policy isa Integrate ? policy.reducer : (policy isa Aggregate ? policy.reducer : PlantMeteo.SumReducer()) f = _resolve_window_reducer(reducer) @@ -198,105 +200,267 @@ function _prefer_local_status_fallback(st::Status, input_var::Symbol, source_var return true end -""" - _resolve_input_windowed(sim, node, st, input_var, source_scale, source_process, source_var, t_start, t_end, policy) +function _resolved_policy_value_for_source( + sim::GraphSimulation, + source_scope::ScopeId, + source_scale::Symbol, + source_process::Symbol, + source_var::Symbol, + source_node_id::Int, + t::Float64, + policy::HoldLast, + t_start::Float64, + source_sample_duration_seconds::Float64 +) + return _resolved_value_for_source(sim, source_scope, source_scale, source_process, source_var, source_node_id, t) +end -Resolve one consumer input from producer temporal streams using a windowed -policy (`Integrate` or `Aggregate`) and write it into `st`. -""" -function _resolve_input_windowed( +function _resolved_policy_value_for_source( + sim::GraphSimulation, + source_scope::ScopeId, + source_scale::Symbol, + source_process::Symbol, + source_var::Symbol, + source_node_id::Int, + t::Float64, + policy::Interpolate, + t_start::Float64, + source_sample_duration_seconds::Float64 +) + return _resolved_interpolated_value_for_source(sim, source_scope, source_scale, source_process, source_var, source_node_id, t, policy) +end + +function _resolved_policy_value_for_source( + sim::GraphSimulation, + source_scope::ScopeId, + source_scale::Symbol, + source_process::Symbol, + source_var::Symbol, + source_node_id::Int, + t::Float64, + policy::Union{Integrate,Aggregate}, + t_start::Float64, + source_sample_duration_seconds::Float64 +) + return _resolved_windowed_value_for_source( + sim, + source_scope, + source_scale, + source_process, + source_var, + source_node_id, + t_start, + t, + policy, + source_sample_duration_seconds + ) +end + +_missing_vector_source_value(policy::SchedulePolicy) = nothing +_missing_vector_source_value(policy::Union{Integrate,Aggregate}) = 0.0 +_missing_scalar_source_value(policy::SchedulePolicy) = nothing +_missing_scalar_source_value(policy::Union{Integrate,Aggregate}) = 0.0 + +function _source_scope_matches(sim::GraphSimulation, source_model_spec, source_scale::Symbol, source_process::Symbol, src_st::Status, consumer_scope::ScopeId) + source_scope = _scope_for_status(sim, source_model_spec, source_scale, source_process, src_st.node) + return source_scope == consumer_scope, source_scope +end + +function _push_vector_source_value!( + vals::Vector{Any}, + sim::GraphSimulation, + src_st::Status, + consumer_scope::ScopeId, + source_model_spec, + source_scale::Symbol, + source_process::Symbol, + source_var::Symbol, + t::Float64, + policy::SchedulePolicy, + t_start::Float64, + source_sample_duration_seconds::Float64 +) + scope_matches, source_scope = _source_scope_matches(sim, source_model_spec, source_scale, source_process, src_st, consumer_scope) + scope_matches || return nothing + + src_node_id = node_id(src_st.node) + v, ok = _resolved_policy_value_for_source( + sim, source_scope, source_scale, source_process, source_var, src_node_id, t, policy, t_start, source_sample_duration_seconds + ) + if ok + push!(vals, v) + elseif source_var in keys(src_st) + push!(vals, src_st[source_var]) + else + missing_value = _missing_vector_source_value(policy) + isnothing(missing_value) || push!(vals, missing_value) + end + return nothing +end + +function _assign_vector_source_values!( sim::GraphSimulation, - node::SoftDependencyNode, st::Status, consumer_scope::ScopeId, source_model_spec, - prefer_local_status::Bool, input_var::Symbol, + source_statuses, source_scale::Symbol, source_process::Symbol, source_var::Symbol, + t::Float64, + policy::SchedulePolicy, t_start::Float64, - t_end::Float64, + source_sample_duration_seconds::Float64 +) + vals = Any[] + for src_st in source_statuses + _push_vector_source_value!( + vals, + sim, + src_st, + consumer_scope, + source_model_spec, + source_scale, + source_process, + source_var, + t, + policy, + t_start, + source_sample_duration_seconds + ) + end + length(vals) > 0 && _assign_input_value!(st, input_var, vals) + return nothing +end + +function _resolve_ancestor_source_value( + sim::GraphSimulation, + st::Status, + consumer_scope::ScopeId, + source_model_spec, + source_statuses, + source_scale::Symbol, + source_process::Symbol, + source_var::Symbol, + t::Float64, policy::SchedulePolicy, + t_start::Float64, source_sample_duration_seconds::Float64 ) - source_statuses = get(status(sim), source_scale, nothing) - isnothing(source_statuses) && return nothing + ancestor_node_id = _ancestor_node_id_for_scale(st.node, source_scale) + isnothing(ancestor_node_id) && return nothing, false + src_st = _status_for_node_id(source_statuses, ancestor_node_id) + isnothing(src_st) && return nothing, false - current_value = st[input_var] - if current_value isa AbstractVector - vals = Any[] - for src_st in source_statuses - src_node_id = node_id(src_st.node) - source_scope = _scope_for_status(sim, source_model_spec, source_scale, source_process, src_st.node) - source_scope == consumer_scope || continue - v, ok = _resolved_windowed_value_for_source( - sim, source_scope, source_scale, source_process, source_var, src_node_id, t_start, t_end, policy, source_sample_duration_seconds - ) - if ok - push!(vals, v) - elseif policy isa Integrate || policy isa Aggregate - push!(vals, 0.0) - elseif source_var in keys(src_st) - push!(vals, src_st[source_var]) - end - end - length(vals) > 0 && _assign_input_value!(st, input_var, vals) - return nothing + scope_matches, source_scope = _source_scope_matches(sim, source_model_spec, source_scale, source_process, src_st, consumer_scope) + scope_matches || return nothing, false + + vv, found = _resolved_policy_value_for_source( + sim, source_scope, source_scale, source_process, source_var, ancestor_node_id, t, policy, t_start, source_sample_duration_seconds + ) + found && return vv, true + source_var in keys(src_st) && return src_st[source_var], true + return nothing, false +end + +function _collect_unique_source_candidates( + sim::GraphSimulation, + consumer_scope::ScopeId, + source_model_spec, + source_statuses, + source_scale::Symbol, + source_process::Symbol, + source_var::Symbol, + t::Float64, + policy::SchedulePolicy, + t_start::Float64, + source_sample_duration_seconds::Float64 +) + candidates = Any[] + for src_st in source_statuses + scope_matches, source_scope = _source_scope_matches(sim, source_model_spec, source_scale, source_process, src_st, consumer_scope) + scope_matches || continue + src_node_id = node_id(src_st.node) + vv, found = _resolved_policy_value_for_source( + sim, source_scope, source_scale, source_process, source_var, src_node_id, t, policy, t_start, source_sample_duration_seconds + ) + found && push!(candidates, vv) end + return candidates +end +function _resolve_scalar_source_value!( + sim::GraphSimulation, + node::SoftDependencyNode, + st::Status, + consumer_scope::ScopeId, + source_model_spec, + prefer_local_status::Bool, + input_var::Symbol, + source_statuses, + source_scale::Symbol, + source_process::Symbol, + source_var::Symbol, + t::Float64, + policy::SchedulePolicy, + t_start::Float64, + source_sample_duration_seconds::Float64; + fallback_to_source_status::Bool +) _prefer_local_status_fallback(st, input_var, source_var, prefer_local_status) && return nothing consumer_node_id = node_id(st.node) - v, ok = _resolved_windowed_value_for_source( - sim, consumer_scope, source_scale, source_process, source_var, consumer_node_id, t_start, t_end, policy, source_sample_duration_seconds + v, ok = _resolved_policy_value_for_source( + sim, consumer_scope, source_scale, source_process, source_var, consumer_node_id, t, policy, t_start, source_sample_duration_seconds ) if ok _assign_input_value!(st, input_var, v) return nothing end - # Same-scale scalar fallback: prefer the value attached to the consumer node - # before scanning all source nodes (which can be ambiguous in dense scales). - if source_scale == node.scale - vv, found = _same_scale_status_value(source_statuses, consumer_node_id, source_var) - if found - _assign_input_value!(st, input_var, vv) - return nothing - end - else - ancestor_node_id = _ancestor_node_id_for_scale(st.node, source_scale) - if !isnothing(ancestor_node_id) - src_st = _status_for_node_id(source_statuses, ancestor_node_id) - if !isnothing(src_st) - source_scope = _scope_for_status(sim, source_model_spec, source_scale, source_process, src_st.node) - if source_scope == consumer_scope - vv, found = _resolved_windowed_value_for_source( - sim, source_scope, source_scale, source_process, source_var, ancestor_node_id, t_start, t_end, policy, source_sample_duration_seconds - ) - if found - _assign_input_value!(st, input_var, vv) - return nothing - elseif source_var in keys(src_st) - _assign_input_value!(st, input_var, src_st[source_var]) - return nothing - end - end + if fallback_to_source_status + if source_scale == node.scale + vv, found = _same_scale_status_value(source_statuses, consumer_node_id, source_var) + if found + _assign_input_value!(st, input_var, vv) + return nothing + end + else + vv, found = _resolve_ancestor_source_value( + sim, + st, + consumer_scope, + source_model_spec, + source_statuses, + source_scale, + source_process, + source_var, + t, + policy, + t_start, + source_sample_duration_seconds + ) + if found + _assign_input_value!(st, input_var, vv) + return nothing end end end - # Cross-scale scalar fallback: allow unique producer value at source scale. - candidates = Any[] - for src_st in source_statuses - src_node_id = node_id(src_st.node) - source_scope = _scope_for_status(sim, source_model_spec, source_scale, source_process, src_st.node) - source_scope == consumer_scope || continue - vv, found = _resolved_windowed_value_for_source( - sim, source_scope, source_scale, source_process, source_var, src_node_id, t_start, t_end, policy, source_sample_duration_seconds - ) - found && push!(candidates, vv) - end + candidates = _collect_unique_source_candidates( + sim, + consumer_scope, + source_model_spec, + source_statuses, + source_scale, + source_process, + source_var, + t, + policy, + t_start, + source_sample_duration_seconds + ) if length(candidates) == 1 _assign_input_value!(st, input_var, only(candidates)) elseif length(candidates) > 1 @@ -304,20 +468,15 @@ function _resolve_input_windowed( "Ambiguous cross-scale source values for input `$(input_var)` in process `$(node.process)` at scale `$(node.scale)`. ", "Please provide `InputBindings(...)` with explicit `scale`/source disambiguation." ) - elseif policy isa Integrate || policy isa Aggregate - _assign_input_value!(st, input_var, 0.0) + else + missing_value = _missing_scalar_source_value(policy) + isnothing(missing_value) || _assign_input_value!(st, input_var, missing_value) end return nothing end -""" - _resolve_input_interpolate(sim, node, st, input_var, source_scale, source_process, source_var, t, policy) - -Resolve one consumer input from producer temporal streams using interpolation -policy and write it into `st`. -""" -function _resolve_input_interpolate( +function _resolve_input_from_policy!( sim::GraphSimulation, node::SoftDependencyNode, st::Status, @@ -329,63 +488,130 @@ function _resolve_input_interpolate( source_process::Symbol, source_var::Symbol, t::Float64, - policy::Interpolate + policy::SchedulePolicy, + t_start::Float64, + source_sample_duration_seconds::Float64; + fallback_to_source_status::Bool=true ) source_statuses = get(status(sim), source_scale, nothing) isnothing(source_statuses) && return nothing current_value = st[input_var] if current_value isa AbstractVector - vals = Any[] - for src_st in source_statuses - src_node_id = node_id(src_st.node) - source_scope = _scope_for_status(sim, source_model_spec, source_scale, source_process, src_st.node) - source_scope == consumer_scope || continue - v, ok = _resolved_interpolated_value_for_source( - sim, source_scope, source_scale, source_process, source_var, src_node_id, t, policy - ) - if ok - push!(vals, v) - elseif source_var in keys(src_st) - push!(vals, src_st[source_var]) - end - end - length(vals) > 0 && _assign_input_value!(st, input_var, vals) - return nothing + return _assign_vector_source_values!( + sim, + st, + consumer_scope, + source_model_spec, + input_var, + source_statuses, + source_scale, + source_process, + source_var, + t, + policy, + t_start, + source_sample_duration_seconds + ) end - _prefer_local_status_fallback(st, input_var, source_var, prefer_local_status) && return nothing + return _resolve_scalar_source_value!( + sim, + node, + st, + consumer_scope, + source_model_spec, + prefer_local_status, + input_var, + source_statuses, + source_scale, + source_process, + source_var, + t, + policy, + t_start, + source_sample_duration_seconds; + fallback_to_source_status=fallback_to_source_status + ) +end - consumer_node_id = node_id(st.node) - v, ok = _resolved_interpolated_value_for_source( - sim, consumer_scope, source_scale, source_process, source_var, consumer_node_id, t, policy +""" + _resolve_input_windowed(sim, node, st, input_var, source_scale, source_process, source_var, t_start, t_end, policy) + +Resolve one consumer input from producer temporal streams using a windowed +policy (`Integrate` or `Aggregate`) and write it into `st`. +""" +function _resolve_input_windowed( + sim::GraphSimulation, + node::SoftDependencyNode, + st::Status, + consumer_scope::ScopeId, + source_model_spec, + prefer_local_status::Bool, + input_var::Symbol, + source_scale::Symbol, + source_process::Symbol, + source_var::Symbol, + t_start::Float64, + t_end::Float64, + policy::SchedulePolicy, + source_sample_duration_seconds::Float64 +) + return _resolve_input_from_policy!( + sim, + node, + st, + consumer_scope, + source_model_spec, + prefer_local_status, + input_var, + source_scale, + source_process, + source_var, + t_end, + policy, + t_start, + source_sample_duration_seconds ) - if ok - _assign_input_value!(st, input_var, v) - return nothing - end +end - # Cross-scale scalar fallback: allow unique producer value at source scale. - candidates = Any[] - for src_st in source_statuses - src_node_id = node_id(src_st.node) - source_scope = _scope_for_status(sim, source_model_spec, source_scale, source_process, src_st.node) - source_scope == consumer_scope || continue - vv, found = _resolved_interpolated_value_for_source( - sim, source_scope, source_scale, source_process, source_var, src_node_id, t, policy - ) - found && push!(candidates, vv) - end - if length(candidates) == 1 - _assign_input_value!(st, input_var, only(candidates)) - elseif length(candidates) > 1 - error( - "Ambiguous cross-scale source values for input `$(input_var)` in process `$(node.process)` at scale `$(node.scale)`. ", - "Please provide `InputBindings(...)` with explicit `scale`/source disambiguation." - ) - end +""" + _resolve_input_interpolate(sim, node, st, input_var, source_scale, source_process, source_var, t, policy) - return nothing +Resolve one consumer input from producer temporal streams using interpolation +policy and write it into `st`. +""" +function _resolve_input_interpolate( + sim::GraphSimulation, + node::SoftDependencyNode, + st::Status, + consumer_scope::ScopeId, + source_model_spec, + prefer_local_status::Bool, + input_var::Symbol, + source_scale::Symbol, + source_process::Symbol, + source_var::Symbol, + t::Float64, + policy::Interpolate +) + return _resolve_input_from_policy!( + sim, + node, + st, + consumer_scope, + source_model_spec, + prefer_local_status, + input_var, + source_scale, + source_process, + source_var, + t, + policy, + t, + 0.0; + fallback_to_source_status=false + ) end """ @@ -407,85 +633,141 @@ function _resolve_input_holdlast( source_var::Symbol, t::Float64 ) - source_statuses = get(status(sim), source_scale, nothing) - isnothing(source_statuses) && return nothing - - current_value = st[input_var] - if current_value isa AbstractVector - vals = Any[] - for src_st in source_statuses - src_node_id = node_id(src_st.node) - source_scope = _scope_for_status(sim, source_model_spec, source_scale, source_process, src_st.node) - source_scope == consumer_scope || continue - v, ok = _resolved_value_for_source(sim, source_scope, source_scale, source_process, source_var, src_node_id, t) - if ok - push!(vals, v) - else - if source_var in keys(src_st) - push!(vals, src_st[source_var]) - end - end - end - length(vals) > 0 && _assign_input_value!(st, input_var, vals) - return nothing - end - - _prefer_local_status_fallback(st, input_var, source_var, prefer_local_status) && return nothing + return _resolve_input_from_policy!( + sim, + node, + st, + consumer_scope, + source_model_spec, + prefer_local_status, + input_var, + source_scale, + source_process, + source_var, + t, + HoldLast(), + t, + 0.0 + ) +end - consumer_node_id = node_id(st.node) - v, ok = _resolved_value_for_source(sim, consumer_scope, source_scale, source_process, source_var, consumer_node_id, t) - if ok - _assign_input_value!(st, input_var, v) - return nothing - end +function _resolve_input_for_policy!( + sim::GraphSimulation, + node::SoftDependencyNode, + st::Status, + consumer_scope::ScopeId, + source_model_spec, + prefer_local_status::Bool, + input_var::Symbol, + source_scale::Symbol, + source_process::Symbol, + source_var::Symbol, + t::Float64, + t_start::Float64, + policy::HoldLast, + source_sample_duration_seconds::Float64 +) + return _resolve_input_holdlast( + sim, + node, + st, + consumer_scope, + source_model_spec, + prefer_local_status, + input_var, + source_scale, + source_process, + source_var, + t + ) +end - # Same-scale scalar fallback: prefer the value attached to the consumer node - # before scanning all source nodes (which can be ambiguous in dense scales). - if source_scale == node.scale - vv, found = _same_scale_status_value(source_statuses, consumer_node_id, source_var) - if found - _assign_input_value!(st, input_var, vv) - return nothing - end - else - ancestor_node_id = _ancestor_node_id_for_scale(st.node, source_scale) - if !isnothing(ancestor_node_id) - src_st = _status_for_node_id(source_statuses, ancestor_node_id) - if !isnothing(src_st) - source_scope = _scope_for_status(sim, source_model_spec, source_scale, source_process, src_st.node) - if source_scope == consumer_scope - vv, found = _resolved_value_for_source(sim, source_scope, source_scale, source_process, source_var, ancestor_node_id, t) - if found - _assign_input_value!(st, input_var, vv) - return nothing - elseif source_var in keys(src_st) - _assign_input_value!(st, input_var, src_st[source_var]) - return nothing - end - end - end - end - end +function _resolve_input_for_policy!( + sim::GraphSimulation, + node::SoftDependencyNode, + st::Status, + consumer_scope::ScopeId, + source_model_spec, + prefer_local_status::Bool, + input_var::Symbol, + source_scale::Symbol, + source_process::Symbol, + source_var::Symbol, + t::Float64, + t_start::Float64, + policy::Interpolate, + source_sample_duration_seconds::Float64 +) + return _resolve_input_interpolate( + sim, + node, + st, + consumer_scope, + source_model_spec, + prefer_local_status, + input_var, + source_scale, + source_process, + source_var, + t, + policy + ) +end - # Cross-scale scalar fallback: allow unique producer value at source scale. - candidates = Any[] - for src_st in source_statuses - src_node_id = node_id(src_st.node) - source_scope = _scope_for_status(sim, source_model_spec, source_scale, source_process, src_st.node) - source_scope == consumer_scope || continue - vv, found = _resolved_value_for_source(sim, source_scope, source_scale, source_process, source_var, src_node_id, t) - found && push!(candidates, vv) - end - if length(candidates) == 1 - _assign_input_value!(st, input_var, only(candidates)) - elseif length(candidates) > 1 - error( - "Ambiguous cross-scale source values for input `$(input_var)` in process `$(node.process)` at scale `$(node.scale)`. ", - "Please provide `InputBindings(...)` with explicit `scale`/source disambiguation." - ) - end +function _resolve_input_for_policy!( + sim::GraphSimulation, + node::SoftDependencyNode, + st::Status, + consumer_scope::ScopeId, + source_model_spec, + prefer_local_status::Bool, + input_var::Symbol, + source_scale::Symbol, + source_process::Symbol, + source_var::Symbol, + t::Float64, + t_start::Float64, + policy::Union{Integrate,Aggregate}, + source_sample_duration_seconds::Float64 +) + return _resolve_input_windowed( + sim, + node, + st, + consumer_scope, + source_model_spec, + prefer_local_status, + input_var, + source_scale, + source_process, + source_var, + t_start, + t, + policy, + source_sample_duration_seconds + ) +end - return nothing +function _resolve_input_for_policy!( + sim::GraphSimulation, + node::SoftDependencyNode, + st::Status, + consumer_scope::ScopeId, + source_model_spec, + prefer_local_status::Bool, + input_var::Symbol, + source_scale::Symbol, + source_process::Symbol, + source_var::Symbol, + t::Float64, + t_start::Float64, + policy::SchedulePolicy, + source_sample_duration_seconds::Float64 +) + error( + "Unsupported input temporal policy `$(typeof(policy))` for input `$(input_var)` ", + "in process `$(node.process)` at scale `$(node.scale)`." + ) end """ @@ -541,28 +823,22 @@ function resolve_inputs_from_temporal_state!(sim::GraphSimulation, node::SoftDep policy = _policy_for_output(model_(source_model_spec), source_var) end - if policy isa HoldLast - _resolve_input_holdlast(sim, node, st, consumer_scope, source_model_spec, prefer_local_status, input_var, source_scale, source_process, source_var, t) - elseif policy isa Interpolate - _resolve_input_interpolate(sim, node, st, consumer_scope, source_model_spec, prefer_local_status, input_var, source_scale, source_process, source_var, t, policy) - elseif policy isa Integrate || policy isa Aggregate - _resolve_input_windowed( - sim, - node, - st, - consumer_scope, - source_model_spec, - prefer_local_status, - input_var, - source_scale, - source_process, - source_var, - t_start, - t, - policy, - source_sample_duration_seconds - ) - end + _resolve_input_for_policy!( + sim, + node, + st, + consumer_scope, + source_model_spec, + prefer_local_status, + input_var, + source_scale, + source_process, + source_var, + t, + t_start, + policy, + source_sample_duration_seconds + ) end return nothing diff --git a/src/time/runtime/meteo_sampling.jl b/src/time/runtime/meteo_sampling.jl index 83898e53b..c46d08e3d 100644 --- a/src/time/runtime/meteo_sampling.jl +++ b/src/time/runtime/meteo_sampling.jl @@ -11,22 +11,7 @@ function _prepare_meteo_sampler(meteo) end function _runtime_meteo_window(window) - if isnothing(window) - return nothing - elseif window isa PlantMeteo.AbstractSamplingWindow - return window - elseif window isa DataType - window <: PlantMeteo.AbstractSamplingWindow || error( - "Unsupported MeteoWindow type `$(window)`. ", - "Use a PlantMeteo sampling-window type/instance." - ) - return window() - end - - error( - "Unsupported MeteoWindow value `$(window)` of type `$(typeof(window))`. ", - "Use a PlantMeteo sampling-window type/instance." - ) + return _normalize_meteo_window(window) end function _meteo_sampling_window(clock::ClockSpec, model_spec) @@ -42,24 +27,7 @@ function _meteo_sampling_window(clock::ClockSpec, model_spec) return window end -function _normalize_meteo_reducer(reducer) - if reducer isa DataType - reducer <: PlantMeteo.AbstractTimeReducer || error( - "Unsupported meteo reducer type `$(reducer)`. ", - "Use a PlantMeteo reducer type/instance or a callable." - ) - return reducer() - elseif reducer isa PlantMeteo.AbstractTimeReducer - return reducer - elseif reducer isa Function - return reducer - end - - error( - "Unsupported meteo reducer value `$(reducer)` of type `$(typeof(reducer))`. ", - "Use a PlantMeteo reducer type/instance or a callable." - ) -end +_normalize_meteo_reducer(reducer) = _normalize_time_reducer(reducer; context="meteo reducer") function _normalize_meteo_binding_rule(target::Symbol, rule) if rule isa NamedTuple @@ -118,46 +86,69 @@ end _meteo_has_field(row, var::Symbol) = hasproperty(row, var) _meteo_has_field(row::NamedTuple, var::Symbol) = haskey(row, var) -""" - validate_meteo_inputs(model_specs, meteo) - -Validate declared `meteo_inputs_` against the available meteorological fields. - -The check is intentionally field-based and independent from units/backends. When -`MeteoBindings` remap a declared model input from another source variable, the -source variable is checked on the raw meteo object. -""" -function validate_meteo_inputs(model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}, meteo) - row = _first_meteo_row(meteo) - isnothing(row) && return nothing - +function _collect_missing_meteo_rows(model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}, has_meteo_variable) missing_rows = NamedTuple[] for (scale, specs_at_scale) in model_specs for (process, spec) in specs_at_scale required = _raw_meteo_requirements_for_spec(spec) - missing = Symbol[var for var in required if !_meteo_has_field(row, var)] + missing = Symbol[var for var in required if !has_meteo_variable(var)] isempty(missing) && continue push!(missing_rows, (scale=scale, process=process, missing=Tuple(missing))) end end + return missing_rows +end - isempty(missing_rows) && return nothing - - details = join( +function _format_missing_meteo_rows(missing_rows) + return join( [ string(row.scale, "/", row.process, " missing ", row.missing) for row in missing_rows ], "; " ) +end + +function _error_missing_meteo_inputs(missing_rows; subject::AbstractString, noun::AbstractString, target::AbstractString) + isempty(missing_rows) && return nothing + error( - "Meteorology is missing fields required by model `meteo_inputs_`: ", - details, - ". Add the fields to meteo, declare a `MeteoBindings(source=...)` remapping, ", + subject, + " is missing ", + noun, + " required by model `meteo_inputs_`: ", + _format_missing_meteo_rows(missing_rows), + ". Add the ", + noun, + " to ", + target, + ", declare a `MeteoBindings(source=...)` remapping, ", "or remove the unused meteo input from the model trait." ) end +""" + validate_meteo_inputs(model_specs, meteo) + +Validate declared `meteo_inputs_` against the available meteorological fields. + +The check is intentionally field-based and independent from units/backends. When +`MeteoBindings` remap a declared model input from another source variable, the +source variable is checked on the raw meteo object. +""" +function validate_meteo_inputs(model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}, meteo) + row = _first_meteo_row(meteo) + isnothing(row) && return nothing + + missing_rows = _collect_missing_meteo_rows(model_specs, var -> _meteo_has_field(row, var)) + return _error_missing_meteo_inputs( + missing_rows; + subject="Meteorology", + noun="fields", + target="meteo" + ) +end + function validate_meteo_inputs(model_specs::AbstractDict{Symbol,<:AbstractDict}, meteo) normalized_specs = Dict{Symbol,Dict{Symbol,ModelSpec}}() for (scale, specs_at_scale) in pairs(model_specs) diff --git a/src/time/runtime/output_export.jl b/src/time/runtime/output_export.jl index 55fd37d63..53e603ad1 100644 --- a/src/time/runtime/output_export.jl +++ b/src/time/runtime/output_export.jl @@ -58,24 +58,6 @@ function OutputRequest( return OutputRequest(scale, var, name, proc, policy, clock) end -function OutputRequest( - scale::AbstractString, - var::Symbol; - name::Symbol=var, - process=nothing, - policy::SchedulePolicy=HoldLast(), - clock=nothing -) - return OutputRequest( - _normalize_scale(scale; warn=true, context=:OutputRequest), - var; - name=name, - process=process, - policy=policy, - clock=clock - ) -end - function _export_clock(request::OutputRequest, timeline::TimelineContext) isnothing(request.clock) && return ClockSpec(1.0, 0.0) c = _clock_from_spec_timestep(request.clock, timeline) @@ -134,7 +116,7 @@ Resolve and register online export requests for the current run. function prepare_output_requests!(sim::GraphSimulation, requests, timeline::TimelineContext) reqs = _normalize_output_requests(requests) - plans = Any[] + plans = OutputExportPlan[] rows = Dict{Symbol,ExportBuffer}() for req in reqs @@ -149,17 +131,20 @@ function prepare_output_requests!(sim::GraphSimulation, requests, timeline::Time "Duplicate output request name `$(req.name)`. Request names must be unique." ) - push!(plans, ( - name=req.name, - scale=scale, - var=req.var, - process=process, - policy=req.policy, - clock=clock, - model_spec=model_spec, - source_dt=float(source_clock.dt), - source_sample_duration_seconds=float(source_clock.dt) * timeline.base_step_seconds, - )) + push!( + plans, + OutputExportPlan( + req.name, + scale, + req.var, + process, + req.policy, + clock, + model_spec, + float(source_clock.dt), + float(source_clock.dt) * timeline.base_step_seconds, + ), + ) rows[req.name] = ExportBuffer(scale, process, req.var) end @@ -169,13 +154,7 @@ function prepare_output_requests!(sim::GraphSimulation, requests, timeline::Time end function _required_horizon_for_export_policy(policy::SchedulePolicy, clock::ClockSpec, source_dt::Float64) - if policy isa Union{Integrate,Aggregate} - return max(1.0, float(clock.dt)) - elseif policy isa Interpolate - return max(2.0, source_dt + 1.0) - end - # HoldLast export is served from caches and does not require streams. - return 0.0 + return _required_horizon_for_policy(policy, float(clock.dt), source_dt) end """ diff --git a/src/time/runtime/publishers.jl b/src/time/runtime/publishers.jl index cecf5b2ca..ce044dc5c 100644 --- a/src/time/runtime/publishers.jl +++ b/src/time/runtime/publishers.jl @@ -107,9 +107,9 @@ function _consumer_horizon_requirements(sim::GraphSimulation, timeline::Timeline source_process = parsed.process source_var = isnothing(parsed.var) ? input_var : parsed.var source_scale = isnothing(parsed.scale) ? _source_scale_for_process(node, source_process) : parsed.scale - source_scale = source_scale isa AbstractString ? - _normalize_scale(source_scale; warn=true, context=:ModelSpec) : - source_scale + source_scale isa Symbol || error( + "Source scale for input `$(input_var)` in process `$(node.value)` must be a `Symbol`, got `$(typeof(source_scale))`." + ) source_model_spec = _model_spec_for_process(sim, source_scale, source_process) source_model = get_models(sim)[source_scale][source_process] source_clock = _model_clock(source_model_spec, source_model, timeline) @@ -141,7 +141,7 @@ function configure_temporal_buffers!(sim::GraphSimulation, timeline::TimelineCon return nothing end -function _trim_stream!(samples::Vector{Tuple{Float64,Any}}, t::Float64, horizon::Float64) +function _trim_stream!(samples::OutputStream, t::Float64, horizon::Float64) horizon <= 0.0 && (empty!(samples); return nothing) t_min = t - horizon + 1.0 - 1e-8 first_keep = findfirst(s -> s[1] >= t_min, samples) @@ -172,7 +172,7 @@ function update_temporal_state_outputs!(sim::GraphSimulation, node::SoftDependen producer_key = _producer_signature(node.scale, node.process, out_var) if haskey(sim.temporal_state.producer_horizons, producer_key) - samples = get!(sim.temporal_state.streams, key, Vector{Tuple{Float64,Any}}()) + samples = get!(sim.temporal_state.streams, key, OutputStream()) push!(samples, (t, val)) _trim_stream!(samples, t, sim.temporal_state.producer_horizons[producer_key]) end diff --git a/src/time/runtime/scopes.jl b/src/time/runtime/scopes.jl index 2c5c00e4b..7a46d490b 100644 --- a/src/time/runtime/scopes.jl +++ b/src/time/runtime/scopes.jl @@ -26,7 +26,6 @@ function _find_ancestor_by_symbol(node, target::Symbol) end return nothing end -_find_ancestor_by_symbol(node, target::AbstractString) = _find_ancestor_by_symbol(node, Symbol(target)) function _scope_from_builtin(selector::Symbol, node, scale::Symbol, process::Symbol) if selector == :global @@ -60,12 +59,10 @@ function _scope_from_selector_result(result, node, scale::Symbol, process::Symbo return result elseif result isa Symbol return _scope_from_builtin(result, node, scale, process) - elseif result isa AbstractString - return _scope_from_builtin(Symbol(result), node, scale, process) end error( - "Scope selector for process `$(process)` at scale `$(scale)` must return `ScopeId`, `Symbol`, or `String`, ", + "Scope selector for process `$(process)` at scale `$(scale)` must return `ScopeId` or `Symbol`, ", "got `$(typeof(result))`." ) end @@ -75,8 +72,6 @@ function _scope_from_selector(selector, node, scale::Symbol, process::Symbol) return selector elseif selector isa Symbol return _scope_from_builtin(selector, node, scale, process) - elseif selector isa AbstractString - return _scope_from_builtin(Symbol(selector), node, scale, process) elseif selector isa Function result = if applicable(selector, node, scale, process) selector(node, scale, process) diff --git a/test/helper-functions.jl b/test/helper-functions.jl index 0e1524b66..588857b56 100644 --- a/test/helper-functions.jl +++ b/test/helper-functions.jl @@ -44,6 +44,19 @@ function compare_outputs_modellist_mapping(filtered_outputs_modellist, graphsim) return modellist_sorted == mapping_sorted end +function run_graphsim_for_comparison(mtg, mapping, nsteps, outputs_mapping, meteo) + graphsim = PlantSimEngine.GraphSimulation(mtg, mapping, nsteps=nsteps, check=true, outputs=outputs_mapping) + run!( + graphsim, + meteo, + PlantMeteo.Constants(), + nothing; + check=true, + executor=SequentialEx(), + ) + return graphsim +end + # Helper used to compare a single-scale `ModelMapping` run with its generated # multiscale equivalent. function check_multiscale_simulation_is_equivalent_begin(mapping::ModelMapping, meteo) @@ -55,16 +68,7 @@ function check_multiscale_simulation_is_equivalent_begin(mapping::ModelMapping, end function check_multiscale_simulation_is_equivalent_end(modellist_outputs, mtg, mapping, out, meteo) - graph_sim = PlantSimEngine.GraphSimulation(mtg, mapping, nsteps=PlantSimEngine.get_nsteps(meteo), check=true, outputs=out) - - sim = run!(graph_sim, - meteo, - PlantMeteo.Constants(), - nothing; - check=true, - executor=SequentialEx() - ) - + graph_sim = run_graphsim_for_comparison(mtg, mapping, PlantSimEngine.get_nsteps(meteo), out, meteo) return compare_outputs_modellist_mapping(modellist_outputs, graph_sim) end @@ -394,15 +398,7 @@ function test_filtered_output_begin(m::ModelMapping, status_tuple, requested_out end function test_filtered_output(mtg, mapping, nsteps, outputs_mapping, meteo, filtered_outputs_modellist) - graphsim = PlantSimEngine.GraphSimulation(mtg, mapping, nsteps=nsteps, check=true, outputs=outputs_mapping) - - sim2 = run!(graphsim, - meteo, - PlantMeteo.Constants(), - nothing; - check=true, - executor=SequentialEx() - ) + graphsim = run_graphsim_for_comparison(mtg, mapping, nsteps, outputs_mapping, meteo) return compare_outputs_modellist_mapping(filtered_outputs_modellist, graphsim) end diff --git a/test/test-MultiScaleModel.jl b/test/test-MultiScaleModel.jl index 5096fa886..a8e02cbaa 100644 --- a/test/test-MultiScaleModel.jl +++ b/test/test-MultiScaleModel.jl @@ -11,9 +11,9 @@ @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => (:Plant => :plant_surfaces)) == (PreviousTimeStep(:plant_surfaces, :unknown) => :Plant => :plant_surfaces) # Case 6 @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => :Plant => :surface) == (PreviousTimeStep(:plant_surfaces, :unknown) => :Plant => :surface) # Case 6 @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => [:Plant => :surface, :Leaf => :surface]) == (PreviousTimeStep(:plant_surfaces, :unknown) => [:Plant => :surface, :Leaf => :surface]) # Case 6 - @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces)) == (PreviousTimeStep(:plant_surfaces, :unknown) => Symbol("") => :plant_surfaces) # Case 7 - @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => (Symbol("") => :surface)) == (PreviousTimeStep(:plant_surfaces, :unknown) => Symbol("") => :surface) - @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => (Symbol("") => :surface), :test) == (PreviousTimeStep(:plant_surfaces, :test) => Symbol("") => :surface) + @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces)) == (PreviousTimeStep(:plant_surfaces, :unknown) => SameScale() => :plant_surfaces) # Case 7 + @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => (SameScale() => :surface)) == (PreviousTimeStep(:plant_surfaces, :unknown) => SameScale() => :surface) + @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => (SameScale() => :surface), :test) == (PreviousTimeStep(:plant_surfaces, :test) => SameScale() => :surface) end; @testset "MultiScaleModel: case 1" begin diff --git a/test/test-domain-simulation.jl b/test/test-domain-simulation.jl index f04dff6d4..553b8f17f 100644 --- a/test/test-domain-simulation.jl +++ b/test/test-domain-simulation.jl @@ -821,6 +821,46 @@ end @test all(row -> row.target_var == :plant_transpirations, route_rows) @test all(row -> row.cardinality == ManyToOneVector, route_rows) + reordered_collector_mapping = ModelMapping( + ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStepModel(Dates.Hour(1)), + status=(plant_transpirations=[0.0], routed_total=0.0), + ) + reordered_route = Route( + from=AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration), + to=DomainRouteTarget(:collector, var=:plant_transpirations, process=:domain_scene_routed_vector), + cardinality=ManyToOneVector(), + ) + reordered_sim = run!( + SimulationMapping( + Domain(:collector, reordered_collector_mapping; kind=:soil), + Domain(:oil_palm, oil_palm_mapping; kind=:plant); + routes=(reordered_route,), + ), + hourly_meteo, + check=true, + ) + @test status(reordered_sim, :collector).plant_transpirations ≈ [0.5] + @test status(reordered_sim, :collector).routed_total ≈ 0.5 + + cyclic_scene_source_mapping = ModelMapping( + ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStepModel(Dates.Hour(1)), + status=(plant_transpirations=[0.0], routed_total=0.0), + ) + cyclic_route = Route( + from=AllDomains(kind=:scene, process=:domain_scene_routed_vector, var=:routed_total), + to=DomainRouteTarget(:oil_palm, var=:absorbed_radiation, process=:domain_plant_transpiration), + cardinality=ManyToOneAggregate(sum), + ) + @test_throws "Cyclic domain run-order constraints" run!( + SimulationMapping( + Domain(:oil_palm, oil_palm_mapping; kind=:plant), + Domain(:scene, cyclic_scene_source_mapping; kind=:scene); + routes=(cyclic_route,), + ), + hourly_meteo, + check=true, + ) + daily_scene_mapping = ModelMapping( ModelSpec(DomainSceneRoutedAggregateModel()) |> TimeStepModel(Dates.Day(1)), status=(daily_plant_transpiration=0.0, daily_routed_total=0.0), @@ -1012,7 +1052,7 @@ end @test only(status(soil_to_graph_sim, :oil_palm, :Leaf)).soil_signal ≈ expected_soil_signals[2] @test soil_to_graph_sim.outputs[(DomainModelKey(:oil_palm, :Leaf, :domain_mtg_leaf_soil_flux), :leaf_flux)] == [[2.0 * expected_soil_signals[1]], [2.0 * expected_soil_signals[2]]] - @test_throws "source domains to run earlier" run!( + reordered_soil_to_graph_sim = run!( scene, SimulationMapping( Domain(:oil_palm, soil_leaf_mapping; kind=:plant, selector=oil_palm), @@ -1022,6 +1062,8 @@ end meteo, check=true, ) + @test only(status(reordered_soil_to_graph_sim, :oil_palm, :Leaf)).soil_signal ≈ expected_soil_signals[2] + @test reordered_soil_to_graph_sim.outputs[(DomainModelKey(:oil_palm, :Leaf, :domain_mtg_leaf_soil_flux), :leaf_flux)] == [[2.0 * expected_soil_signals[1]], [2.0 * expected_soil_signals[2]]] end @testset "Hard-domain targets from MTG-backed domains" begin diff --git a/test/test-maespa-domain-example.jl b/test/test-maespa-domain-example.jl index 20b42f733..42bbde2ac 100644 --- a/test/test-maespa-domain-example.jl +++ b/test/test-maespa-domain-example.jl @@ -35,4 +35,34 @@ include("../examples/maespa_domain_example.jl") @test length(sim.outputs[(DomainModelKey(:plant_A, :Leaf, :leaf_eb), :E)]) == 2 * 24 @test length(sim.outputs[(DomainModelKey(:plant_B, :Leaf, :leaf_eb), :E)]) == 3 * 24 @test length(sim.outputs[(DomainModelKey(:soil, :Default, :soil_water), :psi_soil)]) == 24 + @test length(sim.outputs[(DomainModelKey(:scene, :Default, :scene_eb), :scene_transpiration)]) == 24 + @test status(sim, :soil).transpiration ≈ scene_status.scene_transpiration + @test status(sim, :soil).psi_soil ≈ scene_status.psi_soil +end + +@testset "MAESPA-style domain example validation" begin + mtg = build_maespa_scene() + meteo = maespa_meteo(; nhours=1) + + soil_mapping = ModelMapping( + ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75, true)) |> TimeStepModel(Dates.Hour(1)), + status=(theta1=0.33, theta2=0.36, psi_soil=-0.10, transpiration=0.0, infiltration=0.0), + ) + scene_mapping = ModelMapping( + ModelSpec(SceneEB(25, 0.03, 0.005)) |> TimeStepModel(Dates.Hour(1)), + status=(canopy_Tair=20.0, canopy_VPD=1.0, scene_transpiration=0.0, scene_assimilation=0.0, psi_soil=-0.1, iterations=0), + ) + missing_leaf_mapping = SimulationMapping( + Domain(:soil, soil_mapping; kind=:soil), + Domain(:scene, scene_mapping; kind=:scene), + ) + @test_throws "Hard domain dependency `leaf_eb`" run!(mtg, missing_leaf_mapping, meteo, check=true, executor=SequentialEx()) + + soil_target = (status=Status(psi_soil=-0.1),) + @test_throws "SceneEB did not converge after 0 iterations" _solve_scene_energy_balance!( + SceneEB(0, 0.03, 0.005), + ModelTarget[], + soil_target, + first(meteo), + ) end diff --git a/test/test-meteo-traits.jl b/test/test-meteo-traits.jl index 4111003e2..1e983ec21 100644 --- a/test/test-meteo-traits.jl +++ b/test/test-meteo-traits.jl @@ -54,7 +54,7 @@ end ) === nothing @test PlantSimEngine.validate_meteo_inputs( - Dict("Leaf" => (MeteoTraitConsumerModel(),)), + Dict(:Leaf => (MeteoTraitConsumerModel(),)), (T=20.0, CO2=410.0, duration=Dates.Hour(1)) ) === nothing end diff --git a/test/test-mtg-dynamic.jl b/test/test-mtg-dynamic.jl index 28d7ef2d9..efcfa9962 100644 --- a/test/test-mtg-dynamic.jl +++ b/test/test-mtg-dynamic.jl @@ -79,6 +79,12 @@ out = run!(sim,meteo) @test length(mtg) == 9 @test length(st[:Scene]) == length(st[:Soil]) == length(st[:Plant]) == 1 @test length(st[:Internode]) == length(st[:Leaf]) == 3 + @test node_id.(getproperty.(st[:Leaf], :node)) == sort(node_id.(getproperty.(st[:Leaf], :node))) + leaf_assimilation_refs = [PlantSimEngine.refvalue(leaf_status, :carbon_assimilation) for leaf_status in st[:Leaf]] + @test all( + ref_pair -> first(ref_pair) === last(ref_pair), + zip(parent(sim.status_templates[:Plant][:carbon_assimilation]), leaf_assimilation_refs), + ) @test st[:Internode][1].TT_cu_emergence == 0.0 @test st[:Internode][end].TT_cu_emergence == 25.0 diff --git a/test/test-mtg-multiscale-cyclic-dep.jl b/test/test-mtg-multiscale-cyclic-dep.jl index c9f9a4c88..1f92c6311 100644 --- a/test/test-mtg-multiscale-cyclic-dep.jl +++ b/test/test-mtg-multiscale-cyclic-dep.jl @@ -114,7 +114,7 @@ end #out = @test_nowarn run!(mtg, mapping_nocyclic, meteo, tracked_outputs=out_vars, executor=SequentialEx()) nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg, mapping, nsteps=nsteps, check=true, outputs=out_vars) + sim = PlantSimEngine.GraphSimulation(mtg, mapping_nocyclic, nsteps=nsteps, check=true, outputs=out_vars) out = @test_nowarn run!(sim,meteo) st = status(sim) diff --git a/test/test-multirate-output-export.jl b/test/test-multirate-output-export.jl index 9b1b2e3f4..0c373518f 100644 --- a/test/test-multirate-output-export.jl +++ b/test/test-multirate-output-export.jl @@ -174,7 +174,7 @@ end # Optional direct export return from run! on MTG + mapping entry point. out_status_mtg, out_requested_mtg = run!( mtg, - Dict( + ModelMapping( :Leaf => ( ModelSpec(MRExportSourceModel(Ref(0))) |> TimeStepModel(1.0), diff --git a/test/test-multirate-runtime.jl b/test/test-multirate-runtime.jl index 4768794a3..0a6004df6 100644 --- a/test/test-multirate-runtime.jl +++ b/test/test-multirate-runtime.jl @@ -435,6 +435,21 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test sim_ok.temporal_state.caches[key_dir].v == 10.0 @test sim_ok.temporal_state.caches[key_auto].v == 10.0 + mapping_renamed_auto = ModelMapping( + :Leaf => MRSourceModel(), + :Plant => ( + ModelSpec(MRConsumerModel()) |> + MultiScaleModel([:C => (:Leaf => :S)]) |> + TimeStepModel(ClockSpec(1.0, 0.0)), + ), + ) + sim_renamed_auto = PlantSimEngine.GraphSimulation(mtg, mapping_renamed_auto, nsteps=1, check=true) + consumer_node = only(filter( + n -> n.scale == :Plant && n.process == :mrconsumer, + PlantSimEngine.traverse_dependency_graph(dep(sim_renamed_auto), false), + )) + @test PlantSimEngine._candidate_producers(consumer_node, :C) == [(:mrsource, :S)] + mapping_conflict = ModelMapping( :Leaf => ( ModelSpec(MRConflict1Model()) |> TimeStepModel(1.0), diff --git a/test/test-multirate-scaffolding.jl b/test/test-multirate-scaffolding.jl index 54f188e72..f34bdcd45 100644 --- a/test/test-multirate-scaffolding.jl +++ b/test/test-multirate-scaffolding.jl @@ -1,5 +1,6 @@ using PlantSimEngine using PlantSimEngine.Examples +using MultiScaleTreeGraph using Test @testset "Multi-rate scaffolding" begin @@ -20,6 +21,16 @@ using Test @test output_routing(ModelSpec(m)) == NamedTuple() @test model_scope(ModelSpec(m)) == :global @test updates(ModelSpec(m)) == () + @test_throws "String scope selectors are not supported" ModelSpec(m; scope="plant") + @test_throws "String scope selectors are not supported" ScopeModel("plant")(m) + + scope_node = Node(MultiScaleTreeGraph.NodeMTG("/", :Leaf, 1, 1)) + @test_throws "must return `ScopeId` or `Symbol`" PlantSimEngine._scope_from_selector( + (node, scale, process) -> "plant", + scope_node, + :Leaf, + :process1, + ) mapping = Dict(:Leaf => (m,)) resolved_specs = resolved_model_specs(mapping) diff --git a/test/test-simulation.jl b/test/test-simulation.jl index 1086c0b2a..cb17acc1f 100644 --- a/test/test-simulation.jl +++ b/test/test-simulation.jl @@ -33,14 +33,21 @@ end; meteo = Atmosphere(T=20.0, Wind=1.0, Rh=0.65) run!(models, meteo) - @test_deprecated run!([models], meteo) + legacy_models = PlantSimEngine.ModelList( + process1=Process1Model(1.0), + process2=Process2Model(), + process3=Process3Model(), + status=(var1=15.0, var2=0.3) + ) + @test_throws MethodError run!(legacy_models, meteo) + @test_throws MethodError run!([models], meteo) @test_throws ErrorException run!(ModelMapping("mod1" => models), meteo) mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Leaf, 1, 1)) mtg[:var1] = 15.0 mtg[:var2] = 0.3 mapping_dict = Dict(:Leaf => (Process1Model(1.0), Process2Model(), Process3Model())) - @test_deprecated run!(mtg, mapping_dict, meteo) + @test_throws ErrorException run!(mtg, mapping_dict, meteo) end @testset "Removed multirate keyword for single-scale" begin @@ -105,13 +112,13 @@ end; meteo = Atmosphere(T=20.0, Wind=1.0, Rh=0.65) @testset "simulation with an array of objects" begin - outputs_vector = run!([mapping, mapping2], meteo) + outputs_vector = [run!(m, meteo) for m in (mapping, mapping2)] @test [outputs_vector[1][i][1] for i in keys(outputs_vector[1])] == [34.95, 22.0, 56.95, 15.0, 5.5, 0.3] @test [outputs_vector[2][i][1] for i in keys(outputs_vector[2])] == [36.95, 26.0, 62.95, 15.0, 6.5, 0.3] end @testset "simulation with a dict of objects" begin - outputs_vector = run!(Dict("mod1" => mapping, "mod2" => mapping2), meteo) + outputs_vector = Dict("mod1" => run!(mapping, meteo), "mod2" => run!(mapping2, meteo)) @test [outputs_vector["mod1"][1][i] for i in keys(outputs_vector["mod1"])] == [34.95, 22.0, 56.95, 15.0, 5.5, 0.3] @test [outputs_vector["mod2"][1][i] for i in keys(outputs_vector["mod2"])] == [36.95, 26.0, 62.95, 15.0, 6.5, 0.3] end @@ -195,7 +202,7 @@ end; ) @testset "simulation with an array of objects" begin - outputs_vector = run!([mapping, mapping2], meteo) + outputs_vector = [run!(m, meteo) for m in (mapping, mapping2)] @test [outputs_vector[1][i] for i in keys(outputs_vector[1])] == [ [34.95, 40.0], [22.0, 23.2], [56.95, 63.2], [15.0, 16.0], [5.5, 5.8], [0.3, 0.3] ] @@ -205,7 +212,7 @@ end; end @testset "simulation with a dict of objects" begin - outputs_vector = run!(Dict("mod1" => mapping, "mod2" => mapping2), meteo) + outputs_vector = Dict("mod1" => run!(mapping, meteo), "mod2" => run!(mapping2, meteo)) @test [[outputs_vector["mod1"][1][i], outputs_vector["mod1"][2][i]] for i in keys(outputs_vector["mod1"])] == [ [34.95, 40.0], [22.0, 23.2], [56.95, 63.2], [15.0, 16.0], [5.5, 5.8], [0.3, 0.3] ] From 06c909dba8c3a7cba7cd0a8571d7110a9a0efa3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Thu, 4 Jun 2026 13:25:11 +0200 Subject: [PATCH 06/33] Use true PlantBiophysics models in the examples --- examples/maespa_domain_example.jl | 264 +++---- examples/plantbiophysics_subsample/FvCB.jl | 182 +++++ .../plantbiophysics_subsample/Monteith.jl | 737 ++++++++++++++++++ examples/plantbiophysics_subsample/Tuzet.jl | 128 +++ test/test-maespa-domain-example.jl | 16 +- 5 files changed, 1164 insertions(+), 163 deletions(-) create mode 100644 examples/plantbiophysics_subsample/FvCB.jl create mode 100644 examples/plantbiophysics_subsample/Monteith.jl create mode 100644 examples/plantbiophysics_subsample/Tuzet.jl diff --git a/examples/maespa_domain_example.jl b/examples/maespa_domain_example.jl index 9833fd335..6898ddc13 100644 --- a/examples/maespa_domain_example.jl +++ b/examples/maespa_domain_example.jl @@ -3,11 +3,13 @@ using PlantMeteo using PlantSimEngine using MultiScaleTreeGraph -PlantSimEngine.@process "tuzet" verbose = false -PlantSimEngine.@process "fvcb" verbose = false -PlantSimEngine.@process "leaf_eb" verbose = false +include(joinpath(@__DIR__, "plantbiophysics_subsample", "Tuzet.jl")) +include(joinpath(@__DIR__, "plantbiophysics_subsample", "FvCB.jl")) +include(joinpath(@__DIR__, "plantbiophysics_subsample", "Monteith.jl")) + PlantSimEngine.@process "soil_water" verbose = false PlantSimEngine.@process "scene_eb" verbose = false +PlantSimEngine.@process "leaf_state" verbose = false PlantSimEngine.@process "alloc_a" verbose = false PlantSimEngine.@process "alloc_b" verbose = false @@ -16,103 +18,6 @@ vpd_kpa(meteo) = max(0.02, sat_vp_kpa(meteo.T) * (1.0 - meteo.Rh)) rh_from_vpd(T, vpd) = clamp(1.0 - vpd / sat_vp_kpa(T), 0.05, 0.99) duration_seconds(meteo) = Dates.value(Dates.Millisecond(meteo.duration)) / 1000.0 -struct Tuzet <: AbstractTuzetModel - sf::Float64 - psi50::Float64 - g1::Float64 -end - -PlantSimEngine.inputs_(::Tuzet) = (psi_leaf=-0.5,) -PlantSimEngine.outputs_(::Tuzet) = (fpsi=1.0, g1_eff=4.0) - -function PlantSimEngine.run!(m::Tuzet, models, status, meteo, constants, extra=nothing) - status.fpsi = clamp((1.0 + exp(m.sf * m.psi50)) / (1.0 + exp(m.sf * (m.psi50 - status.psi_leaf))), 0.0, 1.0) - status.g1_eff = m.g1 * status.fpsi - return nothing -end - -struct FvCB <: AbstractFvcbModel - Vcmax::Float64 - Jmax::Float64 - Rd::Float64 - gamma_star::Float64 - Kc::Float64 - g0::Float64 -end - -PlantSimEngine.dep(::FvCB) = (tuzet=AbstractTuzetModel,) -PlantSimEngine.inputs_(::FvCB) = (apar=0.0, Cs=400.0, Tleaf=20.0, psi_leaf=-0.5) -PlantSimEngine.outputs_(::FvCB) = (An=0.0, gs=0.02, fpsi=1.0) - -function PlantSimEngine.run!(m::FvCB, models, status, meteo, constants, extra=nothing) - run_target!(models, status, :tuzet; meteo=meteo, constants=constants, extra=extra) - temp_factor = exp(0.06 * (status.Tleaf - 25.0)) - vcmax = m.Vcmax * temp_factor - j = m.Jmax * status.apar / (status.apar + 2.1 * m.Jmax + eps()) - wc = vcmax * max(status.Cs - m.gamma_star, 0.0) / (status.Cs + m.Kc) - wj = j * max(status.Cs - m.gamma_star, 0.0) / (4.0 * (status.Cs + 2.0 * m.gamma_star)) - status.An = max(0.0, min(wc, wj) - m.Rd) - status.gs = max(m.g0, m.g0 + status.g1_eff * status.An / max(status.Cs, 80.0)) - return nothing -end - -struct LeafEB <: AbstractLeaf_EbModel - absorptance::Float64 - emissive_loss::Float64 - maxiter::Int - tol::Float64 -end - -PlantSimEngine.dep(::LeafEB) = (fvcb=AbstractFvcbModel,) -PlantSimEngine.inputs_(::LeafEB) = ( - Tair=20.0, - VPD=1.0, - wind=1.0, - par=600.0, - psi_soil=-0.2, - Kplant=6.0, - leaf_area=0.015, -) -PlantSimEngine.outputs_(::LeafEB) = ( - apar=0.0, - Tleaf=20.0, - Cs=400.0, - Dl=1.0, - E=0.0, - H=0.0, - Rn=0.0, - An=0.0, - gs=0.02, - fpsi=1.0, - psi_leaf=-0.2, - leaf_carbon=0.0, -) - -function PlantSimEngine.run!(m::LeafEB, models, status, meteo, constants, extra=nothing) - Tleaf = isfinite(status.Tleaf) ? status.Tleaf : status.Tair - E = max(status.E, 0.0) - for _ in 1:m.maxiter - status.Tleaf = Tleaf - status.psi_leaf = status.psi_soil - E / max(status.Kplant, 1.0e-6) - status.apar = m.absorptance * status.par - status.Dl = max(0.02, status.VPD * sat_vp_kpa(Tleaf) / sat_vp_kpa(status.Tair)) - status.Cs = clamp(400.0 - 1.6 * status.An / max(status.gs, 0.02), 120.0, 420.0) - run_target!(models, status, :fvcb; meteo=meteo, constants=constants, extra=extra) - - gb = 0.12 + 0.08 * sqrt(max(status.wind, 0.05)) - status.Rn = m.absorptance * 0.48 * status.par - m.emissive_loss * (Tleaf - status.Tair) - E = max(0.0, 0.20 * status.gs * status.Dl) - lambdaE = 44.0 * E - status.H = 22.0 * gb * (Tleaf - status.Tair) - Tnew = status.Tair + (status.Rn - lambdaE) / max(22.0 * gb + 4.0, 1.0) - abs(Tnew - Tleaf) < m.tol && (Tleaf = Tnew; break) - Tleaf = 0.55 * Tleaf + 0.45 * Tnew - end - status.Tleaf = Tleaf - status.E = E - return nothing -end - struct SoilWater <: AbstractSoil_WaterModel theta_sat::Float64 psi_e::Float64 @@ -137,6 +42,13 @@ function PlantSimEngine.run!(m::SoilWater, models, status, meteo, constants, ext return nothing end +struct LeafState <: AbstractLeaf_StateModel end + +PlantSimEngine.inputs_(::LeafState) = NamedTuple() +PlantSimEngine.outputs_(::LeafState) = (leaf_area=0.0, leaf_carbon=0.0) + +PlantSimEngine.run!(::LeafState, models, status, meteo, constants, extra=nothing) = nothing + struct SceneEB <: AbstractScene_EbModel maxiter::Int tol_T::Float64 @@ -144,7 +56,7 @@ struct SceneEB <: AbstractScene_EbModel end PlantSimEngine.dep(::SceneEB) = ( - leaf_eb=HardDomains(kind=:plant, scale=:Leaf, process=:leaf_eb), + energy_balance=HardDomains(kind=:plant, scale=:Leaf, process=:energy_balance), soil=HardDomains(kind=:soil, process=:soil_water), ) PlantSimEngine.inputs_(::SceneEB) = NamedTuple() @@ -157,72 +69,111 @@ PlantSimEngine.outputs_(::SceneEB) = ( iterations=0, ) -function PlantSimEngine.run!(m::SceneEB, models, status, meteo, constants, extra) - leaf_targets = dependency_targets(extra, :leaf_eb) - soil_target = only(dependency_targets(extra, :soil)) +struct SceneEBSolverResult + Tair::Float64 + VPD::Float64 + psi_soil::Float64 + final_meteo + iterations::Int +end + +function _scene_leaf_meteo(meteo, Tair, VPD) + return Atmosphere( + T=Tair, + Rh=rh_from_vpd(Tair, VPD), + Wind=meteo.Wind, + P=meteo.P, + Cₐ=meteo.Cₐ, + Ri_PAR_f=meteo.Ri_PAR_f, + Ri_SW_f=meteo.Ri_SW_f, + duration=meteo.duration, + ) +end + +function _prepare_scene_leaf_target!(target, meteo, Tair, VPD, psi_soil) + target.status.Ra_SW_f = meteo.Ri_SW_f + target.status.aPPFD = meteo.Ri_PAR_f + target.status.Ψₗ = psi_soil + return nothing +end + +function _run_scene_leaf_targets!(leaf_targets, meteo, Tair, VPD, psi_soil; publish=false) + total_H = 0.0 + total_E = 0.0 + total_A = 0.0 + local_meteo = _scene_leaf_meteo(meteo, Tair, VPD) + for target in leaf_targets + _prepare_scene_leaf_target!(target, meteo, Tair, VPD, psi_soil) + run_target!(target; meteo=local_meteo, publish=publish) + total_H += target.status.H * target.status.leaf_area + total_E += λE_to_E(target.status.λE, local_meteo.λ) * target.status.leaf_area + total_A += target.status.A * target.status.leaf_area + end + return (H=total_H, E=total_E, A=total_A, meteo=local_meteo) +end + +function _solve_scene_energy_balance!(m::SceneEB, leaf_targets, soil_target, meteo) Tair = meteo.T VPD = vpd_kpa(meteo) psi_soil = soil_target.status.psi_soil final_meteo = meteo for iter in 1:m.maxiter - total_H = 0.0 - total_E = 0.0 - total_A = 0.0 - local_meteo = Atmosphere( - T=Tair, - Rh=rh_from_vpd(Tair, VPD), - Wind=meteo.Wind, - Ri_PAR_f=meteo.Ri_PAR_f, - duration=meteo.duration, - ) - for target in leaf_targets - target.status.Tair = Tair - target.status.VPD = VPD - target.status.wind = meteo.Wind - target.status.par = meteo.Ri_PAR_f - target.status.psi_soil = psi_soil - run_target!(target; meteo=local_meteo) - total_H += target.status.H * target.status.leaf_area - total_E += target.status.E * target.status.leaf_area - total_A += target.status.An * target.status.leaf_area - end - - Tnew = meteo.T + 0.30 * total_H / max(length(leaf_targets), 1) - VPDnew = max(0.02, vpd_kpa(meteo) + 0.04 * (Tnew - meteo.T) - 0.30 * total_E) - status.iterations = iter - final_meteo = local_meteo + fluxes = _run_scene_leaf_targets!(leaf_targets, meteo, Tair, VPD, psi_soil) + Tnew = meteo.T + 0.30 * fluxes.H / max(length(leaf_targets), 1) + VPDnew = max(0.02, vpd_kpa(meteo) + 0.04 * (Tnew - meteo.T) - 0.30 * fluxes.E) + final_meteo = fluxes.meteo if abs(Tnew - Tair) < m.tol_T && abs(VPDnew - VPD) < m.tol_VPD Tair = Tnew VPD = VPDnew - break + return SceneEBSolverResult(Tair, VPD, psi_soil, _scene_leaf_meteo(meteo, Tair, VPD), iter) end Tair = 0.50 * Tair + 0.50 * Tnew VPD = 0.50 * VPD + 0.50 * VPDnew end - total_E = 0.0 - total_A = 0.0 + error( + "SceneEB did not converge after $(m.maxiter) iterations ", + "(tol_T=$(m.tol_T), tol_VPD=$(m.tol_VPD))." + ) +end + +function _publish_scene_leaf_solution!(leaf_targets, solution::SceneEBSolverResult, meteo) + fluxes = _run_scene_leaf_targets!( + leaf_targets, + meteo, + solution.Tair, + solution.VPD, + solution.psi_soil; + publish=true, + ) for target in leaf_targets - target.status.Tair = Tair - target.status.VPD = VPD - target.status.psi_soil = psi_soil - run_target!(target; meteo=final_meteo, publish=true) - target.status.leaf_carbon += target.status.An * target.status.leaf_area * duration_seconds(meteo) * 12.0e-6 - total_E += target.status.E * target.status.leaf_area - total_A += target.status.An * target.status.leaf_area + target.status.leaf_carbon += target.status.A * target.status.leaf_area * duration_seconds(meteo) * 12.0e-6 end + return fluxes +end - transpiration_mm = total_E * duration_seconds(meteo) * 18.0e-6 +function _run_scene_soil_feedback!(soil_target, transpiration_mm) soil_target.status.transpiration = transpiration_mm soil_target.status.infiltration = 0.0 run_target!(soil_target; publish=true) + return soil_target.status.psi_soil +end + +function PlantSimEngine.run!(m::SceneEB, models, status, meteo, constants, extra) + leaf_targets = dependency_targets(extra, :energy_balance) + soil_target = only(dependency_targets(extra, :soil)) + solution = _solve_scene_energy_balance!(m, leaf_targets, soil_target, meteo) + fluxes = _publish_scene_leaf_solution!(leaf_targets, solution, meteo) + transpiration_mm = fluxes.E * duration_seconds(meteo) * 18.0e-6 + psi_soil = _run_scene_soil_feedback!(soil_target, transpiration_mm) - status.canopy_Tair = Tair - status.canopy_VPD = VPD + status.canopy_Tair = solution.Tair + status.canopy_VPD = solution.VPD status.scene_transpiration = transpiration_mm - status.scene_assimilation = total_A - status.psi_soil = soil_target.status.psi_soil + status.scene_assimilation = fluxes.A + status.psi_soil = psi_soil + status.iterations = solution.iterations return nothing end @@ -283,18 +234,20 @@ catch false end -function maespa_mapping() +function maespa_mapping(; scene_model=SceneEB(25, 0.03, 0.005)) leaf_a = ( - ModelSpec(LeafEB(0.86, 4.5, 20, 0.02)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(FvCB(72.0, 135.0, 1.1, 42.0, 404.0, 0.015)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(Tuzet(3.2, -1.4, 4.8)) |> TimeStepModel(Dates.Hour(1)), - Status(Tair=20.0, VPD=1.0, wind=1.0, par=0.0, psi_soil=-0.1, Kplant=7.5, leaf_area=0.018, leaf_carbon=0.0), + ModelSpec(Monteith(; ε=0.955, maxiter=20, ΔT=0.02)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(Fvcb(; VcMaxRef=72.0, JMaxRef=135.0, RdRef=1.1)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(Tuzet(; g0=0.015, g1=4.8, Ψᵥ=-1.4, sf=3.2, Γ=42.0)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(LeafState()) |> TimeStepModel(Dates.Hour(1)), + Status(Ra_SW_f=0.0, sky_fraction=1.0, d=0.035, aPPFD=0.0, Ψₗ=-0.1, leaf_area=0.018, leaf_carbon=0.0), ) leaf_b = ( - ModelSpec(LeafEB(0.82, 4.0, 20, 0.02)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(FvCB(58.0, 110.0, 1.3, 42.0, 404.0, 0.012)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(Tuzet(3.8, -1.1, 3.5)) |> TimeStepModel(Dates.Hour(1)), - Status(Tair=20.0, VPD=1.0, wind=1.0, par=0.0, psi_soil=-0.1, Kplant=5.2, leaf_area=0.014, leaf_carbon=0.0), + ModelSpec(Monteith(; ε=0.955, maxiter=20, ΔT=0.02)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(Fvcb(; VcMaxRef=58.0, JMaxRef=110.0, RdRef=1.3)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(Tuzet(; g0=0.012, g1=3.5, Ψᵥ=-1.1, sf=3.8, Γ=42.0)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(LeafState()) |> TimeStepModel(Dates.Hour(1)), + Status(Ra_SW_f=0.0, sky_fraction=0.8, d=0.028, aPPFD=0.0, Ψₗ=-0.1, leaf_area=0.014, leaf_carbon=0.0), ) plant_a = ModelMapping( @@ -320,7 +273,7 @@ function maespa_mapping() status=(theta1=0.33, theta2=0.36, psi_soil=-0.10, transpiration=0.0, infiltration=0.0), ) scene = ModelMapping( - ModelSpec(SceneEB(25, 0.03, 0.005)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(scene_model) |> TimeStepModel(Dates.Hour(1)), status=(canopy_Tair=20.0, canopy_VPD=1.0, scene_transpiration=0.0, scene_assimilation=0.0, psi_soil=-0.1, iterations=0), ) @@ -339,6 +292,7 @@ function maespa_meteo(; nhours=24) Rh=clamp(0.72 - 0.22 * sinpi((hour - 7) / 12), 0.35, 0.90), Wind=1.2 + 0.3 * sinpi(hour / 12), Ri_PAR_f=max(0.0, 900.0 * sinpi((hour - 6) / 12)), + Ri_SW_f=max(0.0, 450.0 * sinpi((hour - 6) / 12)), duration=Dates.Hour(1), ) for hour in 1:nhours diff --git a/examples/plantbiophysics_subsample/FvCB.jl b/examples/plantbiophysics_subsample/FvCB.jl new file mode 100644 index 000000000..cd23055a7 --- /dev/null +++ b/examples/plantbiophysics_subsample/FvCB.jl @@ -0,0 +1,182 @@ +# Generate all methods for the photosynthesis process: several meteo time-steps, components, +# over an MTG, and the mutating /non-mutating versions +@process "photosynthesis" verbose = false + +# Default policy for assimilation rates when consumed at coarser clocks. +# Mapping-level InputBindings policy still overrides this default when provided. +PlantSimEngine.output_policy(::Type{<:AbstractPhotosynthesisModel}) = (A=PlantSimEngine.Integrate(PlantMeteo.DurationSumReducer()),) + + +""" +Farquhar–von Caemmerer–Berry (FvCB) model for C3 photosynthesis (Farquhar et al., 1980; +von Caemmerer and Farquhar, 1981) coupled with a conductance model. +""" +struct Fvcb{T} <: AbstractPhotosynthesisModel + Tᵣ::T + VcMaxRef::T + JMaxRef::T + RdRef::T + TPURef::T + Eₐᵣ::T + O₂::T + Eₐⱼ::T + Hdⱼ::T + Δₛⱼ::T + Eₐᵥ::T + Hdᵥ::T + Δₛᵥ::T + α::T + θ::T +end + +function Fvcb(; Tᵣ=25.0, VcMaxRef=200.0, JMaxRef=250.0, RdRef=0.6, TPURef=9999.0, Eₐᵣ=46390.0, + O₂=210.0, Eₐⱼ=29680.0, Hdⱼ=200000.0, Δₛⱼ=631.88, Eₐᵥ=58550.0, Hdᵥ=200000.0, + Δₛᵥ=629.26, α=0.425, θ=0.7) + + Fvcb(promote(Tᵣ, VcMaxRef, JMaxRef, RdRef, TPURef, Eₐᵣ, O₂, Eₐⱼ, Hdⱼ, Δₛⱼ, Eₐᵥ, Hdᵥ, Δₛᵥ, α, θ)...) +end + +function PlantSimEngine.inputs_(::Fvcb) + (aPPFD=-Inf, Tₗ=-Inf, Cₛ=-Inf) +end + +function PlantSimEngine.outputs_(::Fvcb) + (A=-Inf, Gₛ=-Inf, Cᵢ=-Inf) +end + +Base.eltype(x::Fvcb) = typeof(x).parameters[1] + +PlantSimEngine.dep(::Fvcb) = (stomatal_conductance=AbstractStomatal_ConductanceModel,) +PlantSimEngine.ObjectDependencyTrait(::Type{<:Fvcb}) = PlantSimEngine.IsObjectIndependent() +PlantSimEngine.TimeStepDependencyTrait(::Type{<:Fvcb}) = PlantSimEngine.IsTimeStepIndependent() +PlantSimEngine.timestep_hint(::Type{<:Fvcb}) = ( + required=(Dates.Minute(1), Dates.Hour(6)), + preferred=Dates.Hour(1) +) + +PlantSimEngine.output_policy(::Type{<:Fvcb}) = ( + A=PlantSimEngine.Integrate(PlantMeteo.DurationSumReducer()), # from μmol m-2 s-1 to μmol m-2 timerstep-1 + Cᵢ=PlantSimEngine.Integrate(PlantMeteo.MeanReducer()), + Gₛ=PlantSimEngine.Integrate(PlantMeteo.DurationSumReducer()), +) + +function arrhenius(kref, Eₐ, Tₖ, Tᵣₖ, R) + kref * exp(Eₐ * (Tₖ - Tᵣₖ) / (R * Tₖ * Tᵣₖ)) +end + +function arrhenius(kref, Eₐ, Tₖ, Tᵣₖ, Hd, Δₛ, R) + activation = arrhenius(kref, Eₐ, Tₖ, Tᵣₖ, R) + deactivation_ref = 1.0 + exp((Tᵣₖ * Δₛ - Hd) / (R * Tᵣₖ)) + deactivation = 1.0 + exp((Tₖ * Δₛ - Hd) / (R * Tₖ)) + return activation * deactivation_ref / deactivation +end + +function Γ_star(Tₖ, Tᵣₖ, R=PlantMeteo.Constants().R) + arrhenius(oftype(Tₖ, 42.75), oftype(Tₖ, 37830.0), Tₖ, Tᵣₖ, R) +end + +function get_km(Tₖ, Tᵣₖ, O₂, R=PlantMeteo.Constants().R) + KC = arrhenius(oftype(Tₖ, 404.9), oftype(Tₖ, 79430.0), Tₖ, Tᵣₖ, R) + KO = arrhenius(oftype(Tₖ, 278.4), oftype(Tₖ, 36380.0), Tₖ, Tᵣₖ, R) + return KC * (1.0 + O₂ / KO) +end + +function PlantSimEngine.run!(m::Fvcb, models, status, meteo, constants=PlantMeteo.Constants(), extra=nothing) + + # Tranform Celsius temperatures in Kelvin: + Tₖ = status.Tₗ - constants.K₀ + Tᵣₖ = m.Tᵣ - constants.K₀ + + # Temperature dependence of the parameters: + Γˢ = Γ_star(Tₖ, Tᵣₖ, constants.R) # Gamma star (CO2 compensation point) in μmol mol-1 + Km = get_km(Tₖ, Tᵣₖ, m.O₂, constants.R) # effective Michaelis–Menten coefficient for CO2 + + # Maximum electron transport rate at the given leaf temperature (μmol m-2 s-1): + JMax = arrhenius(m.JMaxRef, m.Eₐⱼ, Tₖ, Tᵣₖ, m.Hdⱼ, m.Δₛⱼ, constants.R) + # Maximum rate of Rubisco activity at the given models temperature (μmol m-2 s-1): + VcMax = arrhenius(m.VcMaxRef, m.Eₐᵥ, Tₖ, Tᵣₖ, m.Hdᵥ, m.Δₛᵥ, constants.R) + # Rate of mitochondrial respiration at the given leaf temperature (μmol m-2 s-1): + Rd = arrhenius(m.RdRef, m.Eₐᵣ, Tₖ, Tᵣₖ, constants.R) + # Rd is also described as the CO2 release in the light by processes other than the PCO + # cycle, and termed "day" respiration, or "light respiration" (Harley et al., 1986). + + # Actual electron transport rate (considering intercepted PAR and leaf temperature): + J = get_J(status.aPPFD, JMax, m.α, m.θ) # in μmol m-2 s-1 + # RuBP regeneration + Vⱼ = J / 4 + + # Stomatal conductance (mol[CO₂] m-2 s-1), dispatched on type of first argument (gs_closure): + st_closure = gs_closure(models.stomatal_conductance, models, status, meteo, extra) + + Cᵢⱼ = get_Cᵢⱼ(Vⱼ, Γˢ, status.Cₛ, Rd, models.stomatal_conductance.g0, st_closure) + + # Electron-transport-limited rate of CO2 assimilation (RuBP regeneration-limited): + Wⱼ = Vⱼ * (Cᵢⱼ - Γˢ) / (Cᵢⱼ + 2.0 * Γˢ) # also called Aⱼ + # See Von Caemmerer, Susanna. 2000. Biochemical models of leaf photosynthesis. + # Csiro publishing, eq. 2.23. + # NB: here the equation is modified because we use Vⱼ instead of J, but it is the same. + + # If Rd is larger than Wⱼ, no assimilation: + if Wⱼ - Rd < 1.0e-6 + Cᵢⱼ = Γˢ + Wⱼ = Vⱼ * (Cᵢⱼ - Γˢ) / (Cᵢⱼ + 2.0 * Γˢ) + end + + Cᵢᵥ = get_Cᵢᵥ(VcMax, Γˢ, status.Cₛ, Rd, models.stomatal_conductance.g0, st_closure, Km) + + # Rubisco-carboxylation-limited rate of CO₂ assimilation (RuBP activity-limited): + if Cᵢᵥ <= 0.0 || Cᵢᵥ > status.Cₛ + Wᵥ = 0.0 + else + Wᵥ = VcMax * (Cᵢᵥ - Γˢ) / (Cᵢᵥ + Km) + end + + # Net assimilation (μmol m-2 s-1) + status.A = min(Wᵥ, Wⱼ, 3 * m.TPURef) - Rd + + # Stomatal conductance (mol[CO₂] m-2 s-1) + PlantSimEngine.run!(models.stomatal_conductance, models, status, st_closure, extra) + + # Intercellular CO₂ concentration (Cᵢ, μmol mol) + status.Cᵢ = min(status.Cₛ, status.Cₛ - status.A / status.Gₛ) + nothing +end + +function get_J(aPPFD, JMax, α, θ) + (α * aPPFD + JMax - sqrt((α * aPPFD + JMax)^2 - 4 * α * θ * aPPFD * JMax)) / (2 * θ) +end + +function get_Cᵢⱼ(Vⱼ, Γˢ, Cₛ, Rd, g0, st_closure) + a = g0 + st_closure * (Vⱼ - Rd) + b = (1.0 - Cₛ * st_closure) * (Vⱼ - Rd) + g0 * (2.0 * Γˢ - Cₛ) - + st_closure * (Vⱼ * Γˢ + 2.0 * Γˢ * Rd) + c = -(1.0 - Cₛ * st_closure) * Γˢ * (Vⱼ + 2.0 * Rd) - + g0 * 2.0 * Γˢ * Cₛ + + return positive_root(a, b, c) +end + +function get_Cᵢᵥ(VcMAX, Γˢ, Cₛ, Rd, g0, st_closure, Km) + a = g0 + st_closure * (VcMAX - Rd) + b = (1.0 - Cₛ * st_closure) * (VcMAX - Rd) + g0 * (Km - Cₛ) - st_closure * (VcMAX * Γˢ + Km * Rd) + c = -(1.0 - Cₛ * st_closure) * (VcMAX * Γˢ + Km * Rd) - g0 * Km * Cₛ + + return positive_root(a, b, c) +end + +function max_root(a, b, c) + Δ = b^2.0 - 4.0 * a * c + x1 = (-b + sqrt(Δ)) / (2.0 * a) + x2 = (-b - sqrt(Δ)) / (2.0 * a) + return max(x1, x2) +end + +function positive_root(a, b, c) + Δ = b^2.0 - 4.0 * a * c + return Δ >= 0.0 ? (-b + sqrt(Δ)) / (2.0 * a) : 0.0 +end + +function negative_root(a, b, c) + Δ = b^2.0 - 4.0 * a * c + return Δ >= 0.0 ? (-b - sqrt(Δ)) / (2.0 * a) : 0.0 +end diff --git a/examples/plantbiophysics_subsample/Monteith.jl b/examples/plantbiophysics_subsample/Monteith.jl new file mode 100644 index 000000000..93a4367ec --- /dev/null +++ b/examples/plantbiophysics_subsample/Monteith.jl @@ -0,0 +1,737 @@ +#! Careful: this file is a copy/paste from the original model implementation in PlantBiophysics.jl (v0.16.2). It is only used for testing. +#! If you want to use this model, use the one from PlantBiophysics.jl instead, which is more up to date and maintained. + +@process "energy_balance" verbose = false +""" + black_body(T, K₀, σ) + black_body(T) + +Thermal infrared, *i.e.* longwave radiation emitted from a black body at temperature T. + +- `T`: temperature of the object in Celsius degree +- `K₀`: absolute zero (°C) +- `σ` (``W\\ m^{-2}\\ K^{-4}``) [Stefan-Boltzmann constant](https://en.wikipedia.org/wiki/Stefan%E2%80%93Boltzmann_law) + +# Note + +`K₀` and `σ` are taken from `PlantMeteo.Constants` if not provided. + +""" +function black_body(T, K₀, σ) + Tₖ = T - K₀ + σ * (Tₖ^4.0) +end + +function black_body(T) + constants = PlantMeteo.Constants() + black_body(T, constants.K₀, constants.σ) +end + + +""" +Thermal infrared, *i.e.* longwave radiation emitted from an object at temperature T. + +- `T`: temperature of the object in Celsius degree +- `ε` object [emissivity](https://en.wikipedia.org/wiki/Emissivity) (not to confuse with ε the +ratio of molecular weights from `PlantMeteo.Constants`). A typical value for a leaf is 0.955. +- `K₀`: absolute zero (°C) +- `σ` (``W\\ m^{-2}\\ K^{-4}``) [Stefan-Boltzmann constant](https://en.wikipedia.org/wiki/Stefan%E2%80%93Boltzmann_law) + +# Note + +`K₀` and `σ` are taken from `PlantMeteo.Constants` if not provided. + +# Examples + +```julia +# Thermal infrared radiation of water at 25 °C: +grey_body(25.0, 0.96) +``` +""" +function grey_body(T, ε, K₀, σ) + ε * black_body(T, K₀, σ) +end + +function grey_body(T, ε) + constants = PlantMeteo.Constants() + grey_body(T, ε, constants.K₀, constants.σ) +end + + +""" + net_longwave_radiation(T₁,T₂,ε₁,ε₂,F₁,K₀,σ) + net_longwave_radiation(T₁,T₂,ε₁,ε₂,F₁) + +Net longwave radiation fluxes (*i.e.* thermal radiation, W m-2) between an object and another. +The object of interest is at temperature T₁ and has an emissivity ε₁, and the object with +which it exchanges energy is at temperature T₂ and has an emissivity ε₂. + +If the result is positive, then the object of interest gain energy. + +# Arguments + +- `T₁` (Celsius degree): temperature of the target object (object 1) +- `T₂` (Celsius degree): temperature of the object with which there is potential exchange (object 2) +- `ε₁`: object 1 emissivity +- `ε₂`: object 2 emissivity +- `F₁`: view factor (0-1), *i.e.* visible fraction of object 2 from object 1 (see note) +- `K₀`: absolute zero (°C) +- `σ` (``W\\ m^{-2}\\ K^{-4}``) [Stefan-Boltzmann constant](https://en.wikipedia.org/wiki/Stefan%E2%80%93Boltzmann_law) + +# Note + +`F₁`, the view factor (also called shape factor) is a coefficient applied to the semi-hemisphere +field of view of object 1 that "sees" object 2. E.g. a leaf can be viewed as a plane. If one side +of the leaf sees only object 2 in its field of view (e.g. the sky), then `F₁ = 1`. +Then the net longwave radiation flux for this part of the leaf is multiplied by its actual +surface to get the exchange. Note that we apply reciprocity between the two objects for +the view factor (they have the same value), *i.e.*: A₁F₁₂ = A₂F₂₁. + +Then, if we take a leaf as object 1, and the sky as object 2, the visible fraction of +sky viewed by the leaf would be: + +- `0.5` if the leaf is on top of the canopy, *i.e.* the upper side of the leaf sees the sky, +the side bellow sees other leaves and the soil. +- between 0 and 0.5 if it is within the canopy and partly shaded by other objects. + +Note that `A₁` for a leaf is twice its common used leaf area, because `A₁` is the **total** +leaf area of the object that exchange energy. + +```julia +# Net thermal radiation fluxes between a leaf and the sky considering the leaf at the top of +# the canopy: +Tₗ = 25.0 ; Tₐ = 20.0 +ε₁ = 0.955 ; ε₂ = 1.0 +Ra_LW_f = net_longwave_radiation(Tₗ,Tₐ,ε₁,ε₂,1.0) +Ra_LW_f + +# Ra_LW_f is the net longwave radiation flux between the leaf and the atmosphere per surface area. +# To get the actual net longwave radiation flux we need to multiply by the surface of the +# leaf, e.g. for a leaf of 2cm²: +leaf_area = 2e-4 # in m² +Ra_LW_f * leaf_area + +# The leaf lose ~0.0055 W towards the atmosphere. +``` + +# References + +Cengel, Y, et Transfer Mass Heat. 2003. A practical approach. New York, NY, USA: McGraw-Hill. +""" +function net_longwave_radiation(T₁, T₂, ε₁, ε₂, F₁, K₀, σ) + (black_body(T₂, K₀, σ) - black_body(T₁, K₀, σ)) / (1.0 / ε₁ + 1.0 / ε₂ - 1.0) * F₁ +end + +function net_longwave_radiation(T₁, T₂, ε₁, ε₂, F₁) + constants = PlantMeteo.Constants() + net_longwave_radiation(T₁, T₂, ε₁, ε₂, F₁, constants.K₀, constants.σ) +end + +""" + gbₕ_free(Tₐ,Tₗ,d,Dₕ₀) + gbₕ_free(Tₐ,Tₗ,d) + +Leaf boundary layer conductance for heat under **free** convection (m s-1). + +# Arguments + +- `Tₐ` (°C): air temperature +- `Tₗ` (°C): leaf temperature +- `d` (m): characteristic dimension, *e.g.* leaf width (see eq. 10.9 from Monteith and Unsworth, 2013). +- `Dₕ₀ = 21.5e-6`: molecular diffusivity for heat at base temperature. Use value from +`PlantMeteo.Constants` if not provided. + +# Note + +`R` and `Dₕ₀` can be found using `PlantMeteo.Constants`. To transform in ``mol\\ m^{-2}\\ s^{-1}``, +use [`ms_to_mol`](@ref). + +# References + +Leuning, R., F. M. Kelliher, DGG de Pury, et E.-D. SCHULZE. 1995. « Leaf nitrogen, +photosynthesis, conductance and transpiration: scaling from leaves to canopies ». Plant, +Cell & Environment 18 (10): 1183‑1200. + +Monteith, John, et Mike Unsworth. 2013. Principles of environmental physics: plants, +animals, and the atmosphere. Academic Press. Paragraph 10.1.3, eq. 10.9. +""" +function gbₕ_free(Tₐ, Tₗ, d, Dₕ₀=PlantMeteo.Constants().Dₕ₀) + zeroT = zero(Tₐ) # make it type stable + + if abs(Tₗ - Tₐ) > zeroT + Gr = 1.58e8 * d^3.0 * abs(Tₗ - Tₐ) # Grashof number (Monteith and Unsworth, 2013) + # !Note: Leuning et al. (1995) use 1.6e8 (eq. E4). + # Leuning et al. (1995) eq. E3: + Gbₕ_free = 0.5 * get_Dₕ(Tₐ, Dₕ₀) * (Gr^0.25) / d + else + Gbₕ_free = zeroT + end + + return Gbₕ_free +end + + +""" + gbₕ_forced(Wind,d) + +Boundary layer conductance for heat under **forced** convection (m s-1). See eq. E1 from +Leuning et al. (1995) for more details. + +# Arguments + +- `Wind` (m s-1): wind speed +- `d` (m): characteristic dimension, *e.g.* leaf width (see eq. 10.9 from Monteith and Unsworth, 2013). + +# Notes + +`d` is the minimal dimension of the surface of an object in contact with the air. + +# References + +Leuning, R., F. M. Kelliher, DGG de Pury, et E.-D. SCHULZE. 1995. « Leaf nitrogen, +photosynthesis, conductance and transpiration: scaling from leaves to canopies ». Plant, +Cell & Environment 18 (10): 1183‑1200. +""" +function gbₕ_forced(Wind, d) + 0.003 * sqrt(Wind / d) +end + + +""" + get_Dₕ(T,Dₕ₀) + get_Dₕ(T) + +Dₕ -molecular diffusivity for heat at base temperature- from Dₕ₀ (corrected by temperature). +See Monteith and Unsworth (2013, eq. 3.10). + +# Arguments + +- `Tₐ` (°C): temperature +- `Dₕ₀`: molecular diffusivity for heat at base temperature. Use value from `PlantMeteo.Constants` +if not provided. + +# References + +Monteith, John, et Mike Unsworth. 2013. Principles of environmental physics: plants, +animals, and the atmosphere. Academic Press. Paragraph 10.1.3. +""" +function get_Dₕ(T, Dₕ₀=PlantMeteo.Constants().Dₕ₀) + Dₕ₀ * (1 + 0.007 * T) +end + +""" + ms_to_mol(G,T,P,R,K₀) + ms_to_mol(G,T,P) + +Conversion of a conductance `G` from ``m\\ s^{-1}`` to ``mol\\ m^{-2}\\ s^{-1}``. + +# Arguments + +- `G` (``m\\ s^{-1}``): conductance +- `T` (°C): air temperature +- `P` (kPa): air pressure +- `R` (``J\\ mol^{-1}\\ K^{-1}``): universal gas constant. +- `K₀` (°C): absolute zero + +# See also + +[`mol_to_ms`](@ref) for the inverse process. +""" +function ms_to_mol(G, T, P, R, K₀) + G * f_ms_to_mol(T, P, R, K₀) +end + +function ms_to_mol(G, T, P) + constants = PlantMeteo.Constants() + ms_to_mol(G, T, P, constants.R, constants.K₀) +end + +""" + ms_to_mol(G,T,P,R,K₀) + ms_to_mol(G,T,P) + +Conversion of a conductance `G` from ``mol\\ m^{-2}\\ s^{-1}`` to ``m\\ s^{-1}``. + +# Arguments + +- `G` (``m\\ s^{-1}``): conductance +- `T` (°C): air temperature +- `P` (kPa): air pressure +- `R` (``J\\ mol^{-1}\\ K^{-1}``): universal gas constant. +- `K₀` (°C): absolute zero + +# See also + +[`ms_to_mol`](@ref) for the inverse process. +""" +function mol_to_ms(G, T, P, R, K₀) + G / f_ms_to_mol(T, P, R, K₀) +end + +function mol_to_ms(G, T, P) + constants = PlantMeteo.Constants() + mol_to_ms(G, T, P, constants.R, constants.K₀) +end + +""" +Conversion factor between conductance in ``m\\ s^{-1}`` to ``mol\\ m^{-2}\\ s^{-1}``. + +# Arguments + +- `T` (°C): air temperature +- `P` (kPa): air pressure +- `R` (``J\\ mol^{-1}\\ K^{-1}``): universal gas constant. +- `K₀` (°C): absolute zero +""" +function f_ms_to_mol(T, P, R, K₀) + (P * 1000) / (R * (T - K₀)) +end + +""" + gbh_to_gbw(gbh, Gbₕ_to_Gbₕ₂ₒ = PlantMeteo.Constants().Gbₕ_to_Gbₕ₂ₒ) + gbw_to_gbh(gbh, Gbₕ_to_Gbₕ₂ₒ = PlantMeteo.Constants().Gbₕ_to_Gbₕ₂ₒ) + +Boundary layer conductance for water vapor from boundary layer conductance for heat. + +# Arguments + +- `gbh` (m s-1): boundary layer conductance for heat under mixed convection. +- `Gbₕ_to_Gbₕ₂ₒ`: conversion factor. + +# Note + +Gbₕ is the sum of free and forced convection. See [`gbₕ_free`](@ref) and [`gbₕ_forced`](@ref). +""" +function gbh_to_gbw(gbh, Gbₕ_to_Gbₕ₂ₒ=PlantMeteo.Constants().Gbₕ_to_Gbₕ₂ₒ) + gbh * Gbₕ_to_Gbₕ₂ₒ +end + +function gbw_to_gbh(gbh, Gbₕ_to_Gbₕ₂ₒ=PlantMeteo.Constants().Gbₕ_to_Gbₕ₂ₒ) + gbh / Gbₕ_to_Gbₕ₂ₒ +end + + +""" + gsc_to_gsw(Gₛ, Gsc_to_Gsw = PlantMeteo.Constants().Gsc_to_Gsw) + +Conversion of a stomatal conductance for CO₂ into stomatal conductance for H₂O. +""" +function gsc_to_gsw(Gₛ, Gsc_to_Gsw=PlantMeteo.Constants().Gsc_to_Gsw) + Gₛ * Gsc_to_Gsw +end + +""" + gsw_to_gsc(Gₛ, Gsc_to_Gsw = PlantMeteo.Constants().Gsc_to_Gsw) + +Conversion of a stomatal conductance for H₂O into stomatal conductance for CO₂. +""" +function gsw_to_gsc(Gₛ, Gsc_to_Gsw=PlantMeteo.Constants().Gsc_to_Gsw) + Gₛ / Gsc_to_Gsw +end + +""" +γ_star(γ, a_sh, a_s, rbv, Rsᵥ, Rbₕ) + +γ∗, the apparent value of psychrometer constant (kPa K−1). + +# Arguments + +- `γ` (kPa K−1): psychrometer constant +- `aₛₕ` (1,2): number of faces exchanging heat fluxes (see Schymanski et al., 2017) +- `aₛᵥ` (1,2): number of faces exchanging water fluxes (see Schymanski et al., 2017) +- `Rbᵥ` (s m-1): boundary layer resistance to water vapor +- `Rsᵥ` (s m-1): stomatal resistance to water vapor +- `Rbₕ` (s m-1): boundary layer resistance to heat + +# Note + +Using the corrigendum from Schymanski et al. (2017) in here so the definition of +[`latent_heat`](@ref) remains generic. + +Not to be confused with [`Γ_star`](@ref) the CO₂ compensation point. + +# References + +Monteith, John L., et Mike H. Unsworth. 2013. « Chapter 13 - Steady-State Heat Balance: (i) +Water Surfaces, Soil, and Vegetation ». In Principles of Environmental Physics (Fourth Edition), +edited by John L. Monteith et Mike H. Unsworth, 217‑47. Boston: Academic Press. + +Schymanski, Stanislaus J., et Dani Or. 2017. Leaf-Scale Experiments Reveal an Important +Omission in the Penman–Monteith Equation ». Hydrology and Earth System Sciences 21 (2): 685‑706. +https://doi.org/10.5194/hess-21-685-2017. +""" +function γ_star(γ, aₛₕ, aₛᵥ, Rbᵥ, Rsᵥ, Rbₕ) + γ * aₛₕ / aₛᵥ * (Rbᵥ + Rsᵥ) / Rbₕ # rv + Rsᵥ= Boundary + stomatal conductance to water vapour +end + +""" + λE_to_E(λE, λ, Mₕ₂ₒ=PlantMeteo.Constants().Mₕ₂ₒ) + E_to_λE(E, λ, Mₕ₂ₒ=PlantMeteo.Constants().Mₕ₂ₒ) + +Conversion from latent heat (W m-2) to evaporation (mol[H₂O] m-2 s-1) or the +opposite (`E_to_λE`). + +# Arguments + +- `λE`: latent heat flux (W m-2) +- `E`: water evaporation (mol[H₂O] m-2 s-1) +- `λ` (J kg-1): latent heat of vaporization +- `Mₕ₂ₒ = 18.0e-3` (kg mol-1): Molar mass for water. + +# Note + +`λ` can be computed using: + + λ = latent_heat_vaporization(T, constants.λ₀) + +It is also directly available from the [`Atmosphere`](https://palmstudio.github.io/PlantMeteo.jl/stable/#PlantMeteo.Atmosphere) structure, and by extention in [`Weather`](https://palmstudio.github.io/PlantMeteo.jl/stable/#PlantMeteo.Weather). + +To convert E from mol[H₂O] m-2 s-1 to mm s-1 you can simply do: + + E_mms = E_mol / constants.Mₕ₂ₒ + +mm[H₂O] s-1 is equivalent to kg[H₂O] m-2 s-1, wich is equivalent to l[H₂O] m-2 s-1. + +""" +function λE_to_E(λE, λ, Mₕ₂ₒ=PlantMeteo.Constants().Mₕ₂ₒ) + λE / λ * Mₕ₂ₒ +end + +function E_to_λE(E, λ, Mₕ₂ₒ=PlantMeteo.Constants().Mₕ₂ₒ) + E / Mₕ₂ₒ * λ +end + +""" +Struct to hold parameter and values for the energy model close to the one in +Monteith and Unsworth (2013) + +# Arguments + +- `aₛₕ = 2`: number of faces of the object that exchange sensible heat fluxes +- `aₛᵥ = 1`: number of faces of the object that exchange latent heat fluxes (hypostomatous => 1) +- `ε = 0.955`: emissivity of the object +- `maxiter = 10`: maximal number of iterations allowed to close the energy balance +- `ΔT = 0.01` (°C): maximum difference in object temperature between two iterations to consider convergence + +# Examples + +```julia +energy_model = Monteith() # a leaf in an illuminated chamber +``` +""" +struct Monteith{T,S} <: AbstractEnergy_BalanceModel + aₛₕ::S + aₛᵥ::S + ε::T + maxiter::S + ΔT::T +end + +function Monteith(; aₛₕ=2, aₛᵥ=1, ε=0.955, maxiter=10, ΔT=0.01) + param_int = promote(aₛₕ, aₛᵥ, maxiter) + param_float = promote(ε, ΔT) + Monteith(param_int[1], param_int[2], param_float[1], param_int[3], param_float[2]) +end + +function PlantSimEngine.inputs_(::Monteith) + (Ra_SW_f=-Inf, sky_fraction=-Inf, d=-Inf) +end + +function PlantSimEngine.outputs_(::Monteith) + ( + Tₗ=-Inf, Rn=-Inf, Ra_LW_f=-Inf, H=-Inf, λE=-Inf, Cₛ=-Inf, Cᵢ=-Inf, + A=-Inf, Gₛ=-Inf, Gbₕ=-Inf, Dₗ=-Inf, Gbc=-Inf, iter=typemin(Int) + ) +end + +Base.eltype(x::Monteith) = typeof(x).parameters[1] +PlantSimEngine.ObjectDependencyTrait(::Type{<:Monteith}) = PlantSimEngine.IsObjectIndependent() +PlantSimEngine.TimeStepDependencyTrait(::Type{<:Monteith}) = PlantSimEngine.IsTimeStepIndependent() +# Multi-rate default for energy balance: keep relatively fine cadence. +PlantSimEngine.timestep_hint(::Type{<:Monteith}) = ( + required=(Dates.Minute(1), Dates.Hour(2)), + preferred=Dates.Hour(1) +) +PlantSimEngine.output_policy(::Type{<:Monteith}) = ( + A=PlantSimEngine.Integrate(PlantMeteo.DurationSumReducer()), + Tₗ=PlantSimEngine.Integrate(PlantMeteo.MeanReducer()), + Rn=PlantSimEngine.Integrate(PlantMeteo.RadiationEnergy()), # W m-2 to MJ m-2 timestep-1 + Ra_LW_f=PlantSimEngine.Integrate(PlantMeteo.RadiationEnergy()), + H=PlantSimEngine.Integrate(PlantMeteo.RadiationEnergy()), + λE=PlantSimEngine.Integrate(PlantMeteo.RadiationEnergy()), + Cₛ=PlantSimEngine.Integrate(PlantMeteo.MeanReducer()), + Cᵢ=PlantSimEngine.Integrate(PlantMeteo.MeanReducer()), + Gₛ=PlantSimEngine.Integrate(PlantMeteo.DurationSumReducer()), + Gbₕ=PlantSimEngine.Integrate(PlantMeteo.DurationSumReducer()), + Dₗ=PlantSimEngine.Integrate(PlantMeteo.MeanReducer()), + Gbc=PlantSimEngine.Integrate(PlantMeteo.DurationSumReducer()), + iter=PlantSimEngine.Integrate(PlantMeteo.MeanReducer()) +) + +PlantSimEngine.dep(::Monteith) = (photosynthesis=AbstractPhotosynthesisModel,) + +""" + run!(::Monteith, models, status, meteo, constants=Constants()) + +Leaf energy balance according to Monteith and Unsworth (2013), and corrigendum from +Schymanski et al. (2017). The computation is close to the one from the MAESPA model (Duursma +et al., 2012, Vezy et al., 2018) here. The leaf temperature is computed iteratively to close +the energy balance using the mass flux (~ Rn - λE). + +# Arguments + +- `::Monteith`: a Monteith model, usually from a model list (*i.e.* m.energy_balance) +- `models`: A `ModelMapping` struct holding the parameters for the model with +initialisations for: + - `Ra_SW_f` (W m-2): net shortwave radiation (PAR + NIR). Often computed from a light interception model + - `sky_fraction` (0-2): view factor between the object and the sky for both faces (see details). + - `d` (m): characteristic dimension, *e.g.* leaf width (see eq. 10.9 from Monteith and Unsworth, 2013). +- `status`: the status of the model, usually the model list status (*i.e.* leaf.status) +- `meteo`: meteorology structure, see [`Atmosphere`](https://palmstudio.github.io/PlantMeteo.jl/stable/#PlantMeteo.Atmosphere) +- `constants = PlantMeteo.Constants()`: physical constants. See `PlantMeteo.Constants` for more details + +# Details + +The sky_fraction in the variables is equal to 2 if all the leaf is viewing is sky (e.g. in a +controlled chamber), 1 if the leaf is *e.g.* up on the canopy where the upper side of the +leaf sees the sky, and the side bellow sees soil + other leaves that are all considered at +the same temperature than the leaf, or less than 1 if it is partly shaded. + +# Notes + +If you want the algorithm to print a message whenever it does not reach convergence, use the +debugging mode by executing this in the REPL: `ENV["JULIA_DEBUG"] = PlantBiophysics`. + +More information [here](https://docs.julialang.org/en/v1/stdlib/Logging/#Environment-variables). + +# Examples + +```julia +meteo = Atmosphere(T = 22.0, Wind = 0.8333, P = 101.325, Rh = 0.4490995) + +# Using a constant value for Gs: +leaf = ModelMapping( + energy_balance = Monteith(), + photosynthesis = Fvcb(), + stomatal_conductance = ConstantGs(0.0, 0.0011), + status = (Ra_SW_f = 13.747, sky_fraction = 1.0, d = 0.03) +) + +run!(leaf,meteo) +leaf.status.Rn +julia> 12.902547446281233 + +# Using the model from Medlyn et al. (2011) for Gs: +leaf = ModelMapping( + energy_balance = Monteith(), + photosynthesis = Fvcb(), + stomatal_conductance = Medlyn(0.03, 12.0), + status = (Ra_SW_f = 13.747, sky_fraction = 1.0, aPPFD = 1500.0, d = 0.03) +) + +out_sim = run!(leaf,meteo) +out_sim[:Rn] +out_sim[:Ra_LW_f] +out_sim[:A] + +df = PlantSimEngine.convert_outputs(out_sim, DataFrame) +``` + +# References + +Duursma, R. A., et B. E. Medlyn. 2012. « MAESPA: a model to study interactions between water +limitation, environmental drivers and vegetation function at tree and stand levels, with an +example application to [CO2] × drought interactions ». Geoscientific Model Development 5 (4): +919‑40. https://doi.org/10.5194/gmd-5-919-2012. + +Monteith, John L., et Mike H. Unsworth. 2013. « Chapter 13 - Steady-State Heat Balance: (i) +Water Surfaces, Soil, and Vegetation ». In Principles of Environmental Physics (Fourth Edition), +edited by John L. Monteith et Mike H. Unsworth, 217‑47. Boston: Academic Press. + +Schymanski, Stanislaus J., et Dani Or. 2017. « Leaf-Scale Experiments Reveal an Important +Omission in the Penman–Monteith Equation ». Hydrology and Earth System Sciences 21 (2): 685‑706. +https://doi.org/10.5194/hess-21-685-2017. + +Vezy, Rémi, Mathias Christina, Olivier Roupsard, Yann Nouvellon, Remko Duursma, Belinda Medlyn, +Maxime Soma, et al. 2018. « Measuring and modelling energy partitioning in canopies of varying +complexity using MAESPA model ». Agricultural and Forest Meteorology 253‑254 (printemps): 203‑17. +https://doi.org/10.1016/j.agrformet.2018.02.005. +""" +function PlantSimEngine.run!(::Monteith, models, status, meteo, constants=PlantMeteo.Constants(), extra=nothing) + + # Initialisations + status.Tₗ = meteo.T - 0.2 + Tₗ_new = zero(meteo.T) + status.Cₛ = meteo.Cₐ + status.Dₗ = PlantMeteo.e_sat(status.Tₗ) - PlantMeteo.e_sat(meteo.T) * meteo.Rh + γˢ = Rbₕ = Δ = zero(meteo.T) + status.Rn = status.Ra_SW_f + iter = 0 + # ?NB: We use iter = 0 and not 1 to get the right number of iterations at the end + # of the for loop, because we use iter += 1 at the end (so it increments once again) + + # Iterative resolution of the energy balance + for i in 1:models.energy_balance.maxiter + + # Update A, Gₛ, Cᵢ from models.status: + PlantSimEngine.run!(models.photosynthesis, models, status, meteo, constants, extra) + + # Stomatal resistance to water vapor + Rsᵥ = 1.0 / (gsc_to_gsw(mol_to_ms(status.Gₛ, meteo.T, meteo.P, constants.R, constants.K₀), + constants.Gsc_to_Gsw)) + + # Re-computing the net radiation according to simulated leaf temperature: + status.Ra_LW_f = net_longwave_radiation(status.Tₗ, meteo.T, models.energy_balance.ε, meteo.ε, + status.sky_fraction, constants.K₀, constants.σ) + #= ? NB: we use the sky fraction here (0-2) instead of the view factor (0-1) because: + - we consider both sides of the leaf at the same time (1 -> leaf sees sky on one face) + - we consider all objects in the scene have the same temperature as the leaf + of interest except the atmosphere. So the leaf exchange thermal energy_balance only with + the atmosphere. =# + # status.Ra_LW_f = (grey_body(meteo.T,1.0) - grey_body(status.Tₗ, 1.0))*status.sky_fraction + + status.Rn = status.Ra_SW_f + status.Ra_LW_f + + # Leaf boundary conductance for heat (m s-1), one sided: + status.Gbₕ = gbₕ_free(meteo.T, status.Tₗ, status.d, constants.Dₕ₀) + + gbₕ_forced(meteo.Wind, status.d) + # NB, in MAESPA we use Rni so we add the radiation conductance also (not here) + + # Leaf boundary resistance for heat (s m-1): + Rbₕ = 1 / status.Gbₕ + + # Leaf boundary resistance for water vapor (s m-1): + Rbᵥ = 1 / gbh_to_gbw(status.Gbₕ) + + # Leaf boundary conductance for CO₂ (mol[CO₂] m-2 s-1): + status.Gbc = ms_to_mol(status.Gbₕ, meteo.T, meteo.P, constants.R, constants.K₀) / + constants.Gbc_to_Gbₕ + + # Update Cₛ using boundary layer conductance to CO₂ and assimilation: + status.Cₛ = min(meteo.Cₐ, meteo.Cₐ - status.A / (status.Gbc * models.energy_balance.aₛᵥ)) + + # Apparent value of psychrometer constant (kPa K−1) + γˢ = γ_star(meteo.γ, models.energy_balance.aₛₕ, models.energy_balance.aₛᵥ, Rbᵥ, Rsᵥ, Rbₕ) + + status.λE = latent_heat(status.Rn, meteo.VPD, γˢ, Rbₕ, meteo.Δ, meteo.ρ, + models.energy_balance.aₛₕ, constants.Cₚ) + + # If potential evaporation is needed, here is how to compute it: + # γˢₑ = γ_star(meteo.γ, energy_balance.aₛₕ, 1, Rbᵥ, 1.0e-9, Rbₕ) # Rsᵥ is inf. low + # Ev = latent_heat(status.Rn, meteo.VPD, γˢₑ, Rbₕ, meteo.Δ, meteo.ρ, energy_balance.aₛₕ, constants.Cₚ) + + Tₗ_new = meteo.T + (status.Rn - status.λE) / + (meteo.ρ * constants.Cₚ * (models.energy_balance.aₛₕ / Rbₕ)) + + if abs(Tₗ_new - status.Tₗ) <= models.energy_balance.ΔT + break + end + + status.Tₗ = Tₗ_new + + # Vapour pressure difference between the surface and the saturation vapour pressure: + status.Dₗ = PlantMeteo.e_sat(status.Tₗ) - PlantMeteo.e_sat(meteo.T) * meteo.Rh + + iter += 1 + end + + status.H = sensible_heat(status.Rn, meteo.VPD, γˢ, Rbₕ, meteo.Δ, meteo.ρ, + models.energy_balance.aₛₕ, constants.Cₚ) + + status.iter = iter + + @debug begin + if iter == models.energy_balance.maxiter + "`run!` algorithm did not converge. Please check the value." + end + end + + # Transpiration (mol[H₂O] m-2 s-1): + # ET = status.λE / meteo.λ * constants.Mₕ₂ₒ + # ET / constants.Mₕ₂ₒ to get mm s-1 <=> kg m-2 s-1 <=> l m-2 s-1 + + nothing +end + +""" + latent_heat(Rn, VPD, γˢ, Rbₕ, Δ, ρ, aₛₕ, Cₚ) + latent_heat(Rn, VPD, γˢ, Rbₕ, Δ, ρ, aₛₕ) + +λE -the latent heat flux (W m-2)- using the Monteith and Unsworth (2013) definition corrected by +Schymanski et al. (2017), eq.22. + +- `Rn` (W m-2): net radiation. Carefull: not the isothermal net radiation +- `VPD` (kPa): air vapor pressure deficit +- `γˢ` (kPa K−1): apparent value of psychrometer constant (see `PlantMeteo.γ_star`) +- `Rbₕ` (s m-1): resistance for heat transfer by convection, i.e. resistance to sensible heat +- `Δ` (KPa K-1): rate of change of saturation vapor pressure with temperature (see `PlantMeteo.e_sat_slope`) +- `ρ` (kg m-3): air density of moist air. +- `aₛₕ` (1,2): number of sides that exchange energy for heat (2 for leaves) +- `Cₚ` (J K-1 kg-1): specific heat of air for constant pressure + +# References + +Monteith, J. and Unsworth, M., 2013. Principles of environmental physics: plants, animals, and the atmosphere. Academic Press. See eq. 13.33. + +Schymanski et al. (2017), Leaf-scale experiments reveal an important omission in the Penman–Monteith equation, +Hydrology and Earth System Sciences. DOI: https://doi.org/10.5194/hess-21-685-2017. See equ. 22. + +# Examples + +```julia +Tₐ = 20.0 ; P = 100.0 ; +ρ = air_density(Tₐ, P) # in kg m-3 +Δ = e_sat_slope(Tₐ) + +latent_heat(300.0, 2.0, 0.1461683, 50.0, Δ, ρ, 2.0) +``` +""" +function latent_heat(Rn, VPD, γˢ, Rbₕ, Δ, ρ, aₛₕ, Cₚ) + (Δ * Rn + ρ * Cₚ * VPD * (aₛₕ / Rbₕ)) / (Δ + γˢ) +end + +function latent_heat(Rn, VPD, γˢ, Rbₕ, Δ, ρ, aₛₕ) + latent_heat(Rn, VPD, γˢ, Rbₕ, Δ, ρ, aₛₕ, PlantMeteo.Constants().Cₚ) +end + + +""" + sensible_heat(Rn, VPD, γˢ, Rbₕ, Δ, ρ, aₛₕ, Cₚ) + sensible_heat(Rn, VPD, γˢ, Rbₕ, Δ, ρ, aₛₕ) + +H -the sensible heat flux (W m-2)- using the Monteith and Unsworth (2013) definition corrected by +Schymanski et al. (2017), eq.22. + +- `Rn` (W m-2): net radiation. Carefull: not the isothermal net radiation +- `VPD` (kPa): air vapor pressure deficit +- `γˢ` (kPa K−1): apparent value of psychrometer constant (see `PlantMeteo.γ_star`) +- `Rbₕ` (s m-1): resistance for heat transfer by convection, i.e. resistance to sensible heat +- `Δ` (KPa K-1): rate of change of saturation vapor pressure with temperature (see `PlantMeteo.e_sat_slope`) +- `ρ` (kg m-3): air density of moist air. +- `aₛₕ` (1,2): number of sides that exchange energy for heat (2 for leaves) +- `Cₚ` (J K-1 kg-1): specific heat of air for constant pressure + +# References + +Monteith, J. and Unsworth, M., 2013. Principles of environmental physics: plants, animals, and the atmosphere. Academic Press. See eq. 13.33. + +Schymanski et al. (2017), Leaf-scale experiments reveal an important omission in the Penman–Monteith equation, +Hydrology and Earth System Sciences. DOI: https://doi.org/10.5194/hess-21-685-2017. See equ. 22. + +# Examples + +```julia +Tₐ = 20.0 ; P = 100.0 ; +ρ = air_density(Tₐ, P) # in kg m-3 +Δ = PlantMeteo.e_sat_slope(Tₐ) + +sensible_heat(300.0, 2.0, 0.1461683, 50.0, Δ, ρ, 2.0) +``` +""" +function sensible_heat(Rn, VPD, γˢ, Rbₕ, Δ, ρ, aₛₕ, Cₚ) + (γˢ * Rn - ρ * Cₚ * VPD * (aₛₕ / Rbₕ)) / (Δ + γˢ) +end + +function sensible_heat(Rn, VPD, γˢ, Rbₕ, Δ, ρ, aₛₕ) + sensible_heat(Rn, VPD, γˢ, Rbₕ, Δ, ρ, aₛₕ, PlantMeteo.Constants().Cₚ) +end diff --git a/examples/plantbiophysics_subsample/Tuzet.jl b/examples/plantbiophysics_subsample/Tuzet.jl new file mode 100644 index 000000000..c677fd34f --- /dev/null +++ b/examples/plantbiophysics_subsample/Tuzet.jl @@ -0,0 +1,128 @@ +#! Careful: this file is more or less a copy/paste from the original model implementation in PlantBiophysics.jl (v0.16.2). It is only used for testing. +#! If you want to use this model, use the one from PlantBiophysics.jl instead, which is more up to date and maintained. + +# Generate all methods for the stomatal conductance process: several meteo time-steps, components, +# over an MTG, and the mutating /non-mutating versions +@process "stomatal_conductance" verbose = false + +# Default policy for stomatal conductance when consumed at coarser clocks. +# Conductance is typically summarized over a window rather than accumulated. +PlantSimEngine.output_policy(::Type{<:AbstractStomatal_ConductanceModel}) = (Gₛ=PlantSimEngine.Aggregate(PlantMeteo.DurationSumReducer()),) + +# Gs is used a little bit differently compared to the other processes. We use two forms: +# the stomatal closure and the full computation of Gs +function PlantSimEngine.run!(Gs::Gsm, models, status, gs_closure, extra) where {Gsm<:AbstractStomatal_ConductanceModel} + status.Gₛ = max( + models.stomatal_conductance.gs_min, + models.stomatal_conductance.g0 + gs_closure * status.A + ) +end + +function PlantSimEngine.run!(Gs::Gsm, models, status, meteo, constants, extra) where {Gsm<:AbstractStomatal_ConductanceModel} + status.Gₛ = max( + models.stomatal_conductance.gs_min, + models.stomatal_conductance.g0 + gs_closure(models.stomatal_conductance, models, status, meteo, constants, extra) * status.A + ) +end + +""" +Tuzet et al. (2003) stomatal conductance model for CO₂. + +# Arguments + +- `g0`: intercept (μmol m⁻² s⁻¹). +- `g1`: slope. +- `Ψᵥ`: leaf water potential at which stomatal conductance is halved (MPa). +- `sf`: sensitivity factor for stomatal closure. +- `Γ`: CO₂ compensation point (mol mol⁻¹). +- `gs_min`: residual conductance (μmol m⁻² s⁻¹). + +# Variables + +- `Ψₗ`: leaf water potential (MPa). +- `Cₛ`: CO₂ concentration at the leaf surface (μmol mol⁻¹). +- `A`: CO₂ assimilation rate (μmol m⁻² s⁻¹). +- `Gₛ`: stomatal conductance (μmol m⁻² s⁻¹). + +# Note + +The CO₂ compensation point represents the concentration of CO₂ at which photosynthesis and respiration are balanced, +and it is typically a small positive value around 30–50 μmol mol⁻¹ under normal atmospheric conditions. + +This implementation uses Cₛ instead of Cᵢ. + +# Examples + +```julia +using PlantMeteo, PlantSimEngine, PlantBiophysics +meteo = Atmosphere(T = 20.0, Wind = 1.0, P = 101.3, Rh = 0.65) + +leaf = + ModelMapping( + stomatal_conductance = Tuzet(0.03, 12.0, -1.5, 2.0, 30.0), + status = (Cₛ = 380.0, Ψₗ = -1.0) + ) +run!(leaf, meteo) +``` + +# References + +Tuzet, A., Perrier, A., & Leuning, R. (2003). A coupled model of stomatal conductance, photosynthesis and transpiration. Plant, Cell & Environment, 26(7), 1097-1116. +""" +struct Tuzet{T} <: AbstractStomatal_ConductanceModel + g0::T + g1::T + Ψᵥ::T + sf::T + Γ::T + gs_min::T +end + +Tuzet(g0, g1, Ψᵥ, sf, Γ, gs_min=oftype(g0, 0.001)) = Tuzet(promote(g0, g1, Ψᵥ, sf, Γ, gs_min)) +Tuzet(; g0, g1, Ψᵥ, sf, Γ, gs_min=0.001) = Tuzet(g0, g1, Ψᵥ, sf, Γ, gs_min) + +function PlantSimEngine.inputs_(::Tuzet) + (Ψₗ=-Inf, Cₛ=-Inf) +end + +function PlantSimEngine.outputs_(::Tuzet) + (Gₛ=-Inf,) +end + +Base.eltype(::Tuzet{T}) where T = T + +""" + gs_closure(::Tuzet, models, status, meteo, constants=nothing, extra=nothing) + +Stomatal closure for CO₂ according to Tuzet et al. (2003). + +# Arguments + +- `::Tuzet`: an instance of the `Tuzet` model type. +- `models::ModelMapping`: A `ModelMapping` struct holding the parameters for the models. +- `status`: A status struct holding the variables for the models. +- `meteo`: meteorology structure, see [`Atmosphere`](https://palmstudio.github.io/PlantMeteo.jl/stable/#PlantMeteo.Atmosphere). Is not used in this model. +- `constants`: A constants struct holding the constants for the models. Is not used in this model. +- `extra`: A struct holding the extra variables for the models. Is not used in this model. + +# Details + +The stomatal conductance is calculated as: + + FPSIF = (1 + exp(sf * psiv)) / (1 + exp(sf * (psiv - Ψₗ))) + GSDIVA = g0 + (g1 / (Cₛ - Γ)) * FPSIF + +where `Γ` is the CO₂ compensation point. +""" +function gs_closure(m::Tuzet, models, status, meteo, constants=nothing, extra=nothing) + fpsif = (1 + exp(m.sf * m.Ψᵥ)) / + (1 + exp(m.sf * (m.Ψᵥ - status.Ψₗ))) + (m.g1 / (status.Cₛ - m.Γ)) * fpsif +end + +PlantSimEngine.ObjectDependencyTrait(::Type{<:Tuzet}) = PlantSimEngine.IsObjectIndependent() +PlantSimEngine.TimeStepDependencyTrait(::Type{<:Tuzet}) = PlantSimEngine.IsTimeStepIndependent() +PlantSimEngine.timestep_hint(::Type{<:Tuzet}) = ( + required=(Dates.Minute(1), Dates.Hour(6)), + preferred=Dates.Hour(1) +) diff --git a/test/test-maespa-domain-example.jl b/test/test-maespa-domain-example.jl index 42bbde2ac..4c1533959 100644 --- a/test/test-maespa-domain-example.jl +++ b/test/test-maespa-domain-example.jl @@ -16,10 +16,10 @@ include("../examples/maespa_domain_example.jl") @test scene_status.iterations > 0 leaf_statuses = status(sim, :Leaf) - @test all(st -> isfinite(st.Tleaf), leaf_statuses) - @test all(st -> isfinite(st.An), leaf_statuses) - @test all(st -> isfinite(st.E), leaf_statuses) - @test any(st -> abs(st.Tleaf - st.Tair) > 1.0e-6, leaf_statuses) + @test all(st -> isfinite(st.Tₗ), leaf_statuses) + @test all(st -> isfinite(st.A), leaf_statuses) + @test all(st -> isfinite(st.λE), leaf_statuses) + @test any(st -> abs(st.Tₗ - scene_status.canopy_Tair) > 1.0e-6, leaf_statuses) plant_a_status = only(status(sim, :plant_A, :Plant)) plant_b_status = only(status(sim, :plant_B, :Plant)) @@ -29,11 +29,11 @@ include("../examples/maespa_domain_example.jl") @test plant_a_status.leaf_pool != plant_b_status.leaf_pool deps = explain_domain_dependencies(sim) - @test count(row -> row.mode == :hard_domain && row.dependency == :leaf_eb, deps) == 2 + @test count(row -> row.mode == :hard_domain && row.dependency == :energy_balance, deps) == 2 @test count(row -> row.mode == :hard_domain && row.dependency == :soil, deps) == 1 - @test length(sim.outputs[(DomainModelKey(:plant_A, :Leaf, :leaf_eb), :E)]) == 2 * 24 - @test length(sim.outputs[(DomainModelKey(:plant_B, :Leaf, :leaf_eb), :E)]) == 3 * 24 + @test length(sim.outputs[(DomainModelKey(:plant_A, :Leaf, :energy_balance), :λE)]) == 2 * 24 + @test length(sim.outputs[(DomainModelKey(:plant_B, :Leaf, :energy_balance), :λE)]) == 3 * 24 @test length(sim.outputs[(DomainModelKey(:soil, :Default, :soil_water), :psi_soil)]) == 24 @test length(sim.outputs[(DomainModelKey(:scene, :Default, :scene_eb), :scene_transpiration)]) == 24 @test status(sim, :soil).transpiration ≈ scene_status.scene_transpiration @@ -56,7 +56,7 @@ end Domain(:soil, soil_mapping; kind=:soil), Domain(:scene, scene_mapping; kind=:scene), ) - @test_throws "Hard domain dependency `leaf_eb`" run!(mtg, missing_leaf_mapping, meteo, check=true, executor=SequentialEx()) + @test_throws "Hard domain dependency `energy_balance`" run!(mtg, missing_leaf_mapping, meteo, check=true, executor=SequentialEx()) soil_target = (status=Status(psi_soil=-0.1),) @test_throws "SceneEB did not converge after 0 iterations" _solve_scene_energy_balance!( From 287187c3da3d8ee94f9ba4e2b922bbe3d12b882c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Thu, 4 Jun 2026 13:26:54 +0200 Subject: [PATCH 07/33] Use two layers for the soil in maespa example --- examples/maespa_domain_example.jl | 26 ++++++++++++-------------- test/test-maespa-domain-example.jl | 2 +- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/examples/maespa_domain_example.jl b/examples/maespa_domain_example.jl index 6898ddc13..d453fbc59 100644 --- a/examples/maespa_domain_example.jl +++ b/examples/maespa_domain_example.jl @@ -24,7 +24,6 @@ struct SoilWater <: AbstractSoil_WaterModel b::Float64 depth1::Float64 depth2::Float64 - use_second_layer::Bool end PlantSimEngine.inputs_(::SoilWater) = (transpiration=0.0, infiltration=0.0) @@ -34,9 +33,7 @@ function PlantSimEngine.run!(m::SoilWater, models, status, meteo, constants, ext withdrawal = max(status.transpiration, 0.0) recharge = max(status.infiltration, 0.0) status.theta1 = clamp(status.theta1 + (recharge - 0.7 * withdrawal) / max(m.depth1 * 1000.0, 1.0), 0.04, m.theta_sat) - if m.use_second_layer - status.theta2 = clamp(status.theta2 - 0.3 * withdrawal / max(m.depth2 * 1000.0, 1.0), 0.04, m.theta_sat) - end + status.theta2 = clamp(status.theta2 - 0.3 * withdrawal / max(m.depth2 * 1000.0, 1.0), 0.04, m.theta_sat) rel = clamp(status.theta1 / m.theta_sat, 0.05, 1.0) status.psi_soil = m.psi_e * rel^(-m.b) return nothing @@ -228,11 +225,12 @@ function build_maespa_scene() return scene end -has_species(node, species) = try - node[:species] == species -catch - false -end +has_species(node, species) = + try + node[:species] == species + catch + false + end function maespa_mapping(; scene_model=SceneEB(25, 0.03, 0.005)) leaf_a = ( @@ -253,8 +251,8 @@ function maespa_mapping(; scene_model=SceneEB(25, 0.03, 0.005)) plant_a = ModelMapping( :Plant => ( ModelSpec(AllocA(0.35, 0.55)) |> - MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]]) |> - TimeStepModel(ClockSpec(24.0, 0.0)), + MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]]) |> + TimeStepModel(ClockSpec(24.0, 0.0)), Status(leaf_pool=0.0, wood_pool=0.0), ), :Leaf => leaf_a, @@ -262,14 +260,14 @@ function maespa_mapping(; scene_model=SceneEB(25, 0.03, 0.005)) plant_b = ModelMapping( :Plant => ( ModelSpec(AllocB(0.55, 0.35)) |> - MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]]) |> - TimeStepModel(ClockSpec(24.0, 0.0)), + MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]]) |> + TimeStepModel(ClockSpec(24.0, 0.0)), Status(leaf_pool=0.0, wood_pool=0.0), ), :Leaf => leaf_b, ) soil = ModelMapping( - ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75, true)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75)) |> TimeStepModel(Dates.Hour(1)), status=(theta1=0.33, theta2=0.36, psi_soil=-0.10, transpiration=0.0, infiltration=0.0), ) scene = ModelMapping( diff --git a/test/test-maespa-domain-example.jl b/test/test-maespa-domain-example.jl index 4c1533959..76240868b 100644 --- a/test/test-maespa-domain-example.jl +++ b/test/test-maespa-domain-example.jl @@ -45,7 +45,7 @@ end meteo = maespa_meteo(; nhours=1) soil_mapping = ModelMapping( - ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75, true)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75)) |> TimeStepModel(Dates.Hour(1)), status=(theta1=0.33, theta2=0.36, psi_soil=-0.10, transpiration=0.0, infiltration=0.0), ) scene_mapping = ModelMapping( From 38eef0baaaf52ef9dc5b81d3f9a4a3fafcc47513 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Thu, 4 Jun 2026 14:19:29 +0200 Subject: [PATCH 08/33] Update maespa_domain_example.jl --- examples/maespa_domain_example.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/maespa_domain_example.jl b/examples/maespa_domain_example.jl index d453fbc59..9d4215a43 100644 --- a/examples/maespa_domain_example.jl +++ b/examples/maespa_domain_example.jl @@ -101,7 +101,7 @@ function _run_scene_leaf_targets!(leaf_targets, meteo, Tair, VPD, psi_soil; publ local_meteo = _scene_leaf_meteo(meteo, Tair, VPD) for target in leaf_targets _prepare_scene_leaf_target!(target, meteo, Tair, VPD, psi_soil) - run_target!(target; meteo=local_meteo, publish=publish) + run_target!(target; meteo=local_meteo, publish=publish) # Publish is false because we iterate here and only want to publish the final solution at the end total_H += target.status.H * target.status.leaf_area total_E += λE_to_E(target.status.λE, local_meteo.λ) * target.status.leaf_area total_A += target.status.A * target.status.leaf_area @@ -116,7 +116,9 @@ function _solve_scene_energy_balance!(m::SceneEB, leaf_targets, soil_target, met final_meteo = meteo for iter in 1:m.maxiter + # Run the energy balance of each leaf, and aggregate the fluxes at the canopy scale: fluxes = _run_scene_leaf_targets!(leaf_targets, meteo, Tair, VPD, psi_soil) + # Update the canopy-scale meteo based on the leaf fluxes, and check for convergence: Tnew = meteo.T + 0.30 * fluxes.H / max(length(leaf_targets), 1) VPDnew = max(0.02, vpd_kpa(meteo) + 0.04 * (Tnew - meteo.T) - 0.30 * fluxes.E) final_meteo = fluxes.meteo @@ -125,8 +127,8 @@ function _solve_scene_energy_balance!(m::SceneEB, leaf_targets, soil_target, met VPD = VPDnew return SceneEBSolverResult(Tair, VPD, psi_soil, _scene_leaf_meteo(meteo, Tair, VPD), iter) end - Tair = 0.50 * Tair + 0.50 * Tnew - VPD = 0.50 * VPD + 0.50 * VPDnew + Tair = mean(Tair, Tnew) # take the average to help convergence + VPD = mean(VPD, VPDnew) end error( From ef8d5d1609fb0feddf96e77da4919e0238c8aabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Thu, 4 Jun 2026 15:21:11 +0200 Subject: [PATCH 09/33] MAESPA example: make the model a bit more process-based + scene LAI as a proper model --- examples/maespa_domain_example.jl | 293 +++++++++++++++++++++++------ test/test-maespa-domain-example.jl | 69 ++++++- 2 files changed, 303 insertions(+), 59 deletions(-) diff --git a/examples/maespa_domain_example.jl b/examples/maespa_domain_example.jl index 9d4215a43..b9a78d2ae 100644 --- a/examples/maespa_domain_example.jl +++ b/examples/maespa_domain_example.jl @@ -10,6 +10,7 @@ include(joinpath(@__DIR__, "plantbiophysics_subsample", "Monteith.jl")) PlantSimEngine.@process "soil_water" verbose = false PlantSimEngine.@process "scene_eb" verbose = false PlantSimEngine.@process "leaf_state" verbose = false +PlantSimEngine.@process "lai_dynamic" verbose = false PlantSimEngine.@process "alloc_a" verbose = false PlantSimEngine.@process "alloc_b" verbose = false @@ -17,13 +18,14 @@ sat_vp_kpa(T) = 0.6108 * exp(17.27 * T / (T + 237.3)) vpd_kpa(meteo) = max(0.02, sat_vp_kpa(meteo.T) * (1.0 - meteo.Rh)) rh_from_vpd(T, vpd) = clamp(1.0 - vpd / sat_vp_kpa(T), 0.05, 0.99) duration_seconds(meteo) = Dates.value(Dates.Millisecond(meteo.duration)) / 1000.0 - -struct SoilWater <: AbstractSoil_WaterModel - theta_sat::Float64 - psi_e::Float64 - b::Float64 - depth1::Float64 - depth2::Float64 +e_sat_kpa(T) = PlantMeteo.e_sat(T) + +struct SoilWater{T} <: AbstractSoil_WaterModel + theta_sat::T + psi_e::T + b::T + depth1::T + depth2::T end PlantSimEngine.inputs_(::SoilWater) = (transpiration=0.0, infiltration=0.0) @@ -46,20 +48,86 @@ PlantSimEngine.outputs_(::LeafState) = (leaf_area=0.0, leaf_carbon=0.0) PlantSimEngine.run!(::LeafState, models, status, meteo, constants, extra=nothing) = nothing -struct SceneEB <: AbstractScene_EbModel - maxiter::Int - tol_T::Float64 - tol_VPD::Float64 +""" + LAIModel(area) + +Compute scene leaf area and leaf area index from all leaves routed into the +scene domain. +""" +struct LAIModel{T} <: AbstractLai_DynamicModel + area::T +end + +function LAIModel(area) + area > 0.0 || throw(ArgumentError("`area` must be strictly positive.")) + return LAIModel{Float64}(Float64(area)) +end + +PlantSimEngine.inputs_(::LAIModel) = (leaf_areas=[-Inf],) +PlantSimEngine.outputs_(::LAIModel) = (lai=-Inf, leaf_area=-Inf) + +function PlantSimEngine.run!(m::LAIModel, models, status, meteo, constants, extra=nothing) + status.leaf_area = sum(status.leaf_areas) + status.lai = status.leaf_area / m.area + return nothing +end + +struct SceneEB{I,T} <: AbstractScene_EbModel + maxiter::I + tol_t::T + tol_vpd::T + tree_height::T + zht::T + zpd::T + z0ht::T + ground_area::T + qc::T + gbcan_min::T + von_karman::T +end + +function SceneEB( + maxiter, + tol_t, + tol_vpd; + tree_height=2.0, + zht=4.0, + zpd=0.75 * tree_height, + z0ht=0.1 * tree_height, + ground_area=1.0, + qc=0.0, + gbcan_min=0.0123, + von_karman=0.41, +) + ground_area > 0.0 || throw(ArgumentError("`ground_area` must be strictly positive.")) + tree_height > 0.0 || throw(ArgumentError("`tree_height` must be strictly positive.")) + zht > 0.0 || throw(ArgumentError("`zht` must be strictly positive.")) + return SceneEB( + maxiter, + promote(tol_t, + tol_vpd, + tree_height, + zht, + zpd, + z0ht, + ground_area, + qc, + gbcan_min, + von_karman)... + ) end PlantSimEngine.dep(::SceneEB) = ( energy_balance=HardDomains(kind=:plant, scale=:Leaf, process=:energy_balance), soil=HardDomains(kind=:soil, process=:soil_water), ) -PlantSimEngine.inputs_(::SceneEB) = NamedTuple() +PlantSimEngine.inputs_(::SceneEB) = (lai=0.0, leaf_area=0.0) PlantSimEngine.outputs_(::SceneEB) = ( - canopy_Tair=20.0, - canopy_VPD=1.0, + canopy_tair=20.0, + canopy_vpd=1.0, + canopy_rh=0.7, + canopy_htot=0.0, + canopy_gcanop=0.0, scene_transpiration=0.0, scene_assimilation=0.0, psi_soil=-0.1, @@ -67,17 +135,21 @@ PlantSimEngine.outputs_(::SceneEB) = ( ) struct SceneEBSolverResult - Tair::Float64 - VPD::Float64 + tair::Float64 + vpd::Float64 + rh::Float64 psi_soil::Float64 final_meteo iterations::Int + htot::Float64 + gcanop::Float64 + lai::Float64 end -function _scene_leaf_meteo(meteo, Tair, VPD) +function _scene_leaf_meteo(meteo, tair_canopy, vpd_canopy) return Atmosphere( - T=Tair, - Rh=rh_from_vpd(Tair, VPD), + T=tair_canopy, + Rh=rh_from_vpd(tair_canopy, vpd_canopy), Wind=meteo.Wind, P=meteo.P, Cₐ=meteo.Cₐ, @@ -87,63 +159,150 @@ function _scene_leaf_meteo(meteo, Tair, VPD) ) end -function _prepare_scene_leaf_target!(target, meteo, Tair, VPD, psi_soil) +function _prepare_scene_leaf_target!(target, meteo, tair_canopy, vpd_canopy, psi_soil) target.status.Ra_SW_f = meteo.Ri_SW_f target.status.aPPFD = meteo.Ri_PAR_f target.status.Ψₗ = psi_soil return nothing end -function _run_scene_leaf_targets!(leaf_targets, meteo, Tair, VPD, psi_soil; publish=false) - total_H = 0.0 - total_E = 0.0 - total_A = 0.0 - local_meteo = _scene_leaf_meteo(meteo, Tair, VPD) +function _run_scene_leaf_targets!(leaf_targets, meteo, tair_canopy, vpd_canopy, psi_soil, ground_area; publish=false) + total_rn = 0.0 + total_lambda_e = 0.0 + total_h = 0.0 + total_a = 0.0 + local_meteo = _scene_leaf_meteo(meteo, tair_canopy, vpd_canopy) for target in leaf_targets - _prepare_scene_leaf_target!(target, meteo, Tair, VPD, psi_soil) + _prepare_scene_leaf_target!(target, meteo, tair_canopy, vpd_canopy, psi_soil) run_target!(target; meteo=local_meteo, publish=publish) # Publish is false because we iterate here and only want to publish the final solution at the end - total_H += target.status.H * target.status.leaf_area - total_E += λE_to_E(target.status.λE, local_meteo.λ) * target.status.leaf_area - total_A += target.status.A * target.status.leaf_area + leaf_area = target.status.leaf_area + total_rn += target.status.Rn * leaf_area + total_lambda_e += target.status.λE * leaf_area + total_h += target.status.H * leaf_area + total_a += target.status.A * leaf_area + end + return ( + rn=total_rn / ground_area, + lambda_e=total_lambda_e / ground_area, + h=total_h / ground_area, + a=total_a / ground_area, + meteo=local_meteo, + ) +end + +function gbcanms(wind, zht, z0ht, zpd, tree_height, lai; gbcan_min=0.0123, von_karman=0.41) + zpd2 = 0.75 * tree_height + z0 = 0.1 * tree_height + zstar = max(zht, eps(Float64)) + wind2 = max(wind, 1.0e-6) + + if zstar <= tree_height + wind2 *= exp(0.13155 * (tree_height / zstar - 1.0)) + zstar = 2.0 * tree_height end - return (H=total_H, E=total_E, A=total_A, meteo=local_meteo) + + zstar = max(zstar, zpd2 + z0 + 1.0e-6) + windstar = wind2 * von_karman / log((zstar - zpd2) / z0) + alpha1 = 1.5 + zw = zpd2 + alpha1 * (tree_height - zpd2) + gbcanmsini = windstar * von_karman / log((zstar - zpd2) / (zw - zpd2)) + gbcanmsrou = windstar * von_karman / ((zw - tree_height) / (zw - zpd2)) + canopy_air_ms = max(1.0 / (1.0 / gbcanmsini + 1.0 / gbcanmsrou), gbcan_min) + + alpha = 2.0 + z0ht2 = 0.01 + kh = alpha1 * von_karman * windstar * (tree_height - zpd2) + soil_denominator = tree_height * exp(alpha) * + (exp(-alpha * z0ht2 / tree_height) - exp(-alpha * (zpd2 + z0) / tree_height)) + soil_canopy_ms = max(alpha * kh / soil_denominator, 0.0) + return (canopy_air_ms=canopy_air_ms, soil_canopy_ms=soil_canopy_ms) +end + +function tvpdcanopcalc(m::SceneEB, fluxes, meteo_above, canopy_meteo, constants) + lai = fluxes.lai + gbs = gbcanms( + meteo_above.Wind, + m.zht, + m.z0ht, + m.zpd, + m.tree_height, + lai; + gbcan_min=m.gbcan_min, + von_karman=m.von_karman, + ) + gbcan_ms = gbs.canopy_air_ms + tair_above = meteo_above.T + vpd_above = max(0.01, meteo_above.VPD) + qn = fluxes.rn + qe = fluxes.lambda_e + rad_interc = get(fluxes, :rad_interc, 0.0) + rnettot = qn + rad_interc + etot = qe + htot = rnettot - etot - m.qc + heat_conductance = constants.Cₚ * canopy_meteo.ρ * gbcan_ms + + tair_new = tair_above + htot / heat_conductance + tair_new = clamp(tair_new, tair_above - 10.0, tair_above + 10.0) + + vpair_above = e_sat_kpa(tair_above) - vpd_above + vpair_canopy = vpair_above + etot * canopy_meteo.γ / heat_conductance + vpd_new = max(0.01, e_sat_kpa(tair_new) - vpair_canopy) + vpd_new = clamp(vpd_new, max(0.01, vpd_above - 1.5), vpd_above + 1.5) + rh_new = clamp(1.0 - vpd_new / e_sat_kpa(tair_new), 0.0, 1.0) + return (tair=tair_new, vpd=vpd_new, rh=rh_new, htot=htot, gcanop=gbcan_ms) end -function _solve_scene_energy_balance!(m::SceneEB, leaf_targets, soil_target, meteo) - Tair = meteo.T - VPD = vpd_kpa(meteo) +function _solve_scene_energy_balance!(m::SceneEB, leaf_targets, soil_target, status, meteo, constants=PlantMeteo.Constants()) + tair_above = meteo.T + vpd_above = max(0.01, meteo.VPD) + tair_canopy = tair_above + vpd_canopy = vpd_above psi_soil = soil_target.status.psi_soil final_meteo = meteo + last_update = (tair=tair_canopy, vpd=vpd_canopy, rh=meteo.Rh, htot=0.0, gcanop=0.0) for iter in 1:m.maxiter # Run the energy balance of each leaf, and aggregate the fluxes at the canopy scale: - fluxes = _run_scene_leaf_targets!(leaf_targets, meteo, Tair, VPD, psi_soil) + fluxes = _run_scene_leaf_targets!(leaf_targets, meteo, tair_canopy, vpd_canopy, psi_soil, m.ground_area) + fluxes = merge(fluxes, (lai=status.lai, rad_interc=0.0)) # Update the canopy-scale meteo based on the leaf fluxes, and check for convergence: - Tnew = meteo.T + 0.30 * fluxes.H / max(length(leaf_targets), 1) - VPDnew = max(0.02, vpd_kpa(meteo) + 0.04 * (Tnew - meteo.T) - 0.30 * fluxes.E) final_meteo = fluxes.meteo - if abs(Tnew - Tair) < m.tol_T && abs(VPDnew - VPD) < m.tol_VPD - Tair = Tnew - VPD = VPDnew - return SceneEBSolverResult(Tair, VPD, psi_soil, _scene_leaf_meteo(meteo, Tair, VPD), iter) + update = tvpdcanopcalc(m, fluxes, meteo, final_meteo, constants) + last_update = update + if abs(update.tair - tair_canopy) < m.tol_t && abs(update.vpd - vpd_canopy) < m.tol_vpd + tair_canopy = update.tair + vpd_canopy = update.vpd + return SceneEBSolverResult( + tair_canopy, + vpd_canopy, + update.rh, + psi_soil, + _scene_leaf_meteo(meteo, tair_canopy, vpd_canopy), + iter, + update.htot, + update.gcanop, + status.lai, + ) end - Tair = mean(Tair, Tnew) # take the average to help convergence - VPD = mean(VPD, VPDnew) + tair_canopy = 0.5 * (tair_canopy + update.tair) # take the average to help convergence + vpd_canopy = 0.5 * (vpd_canopy + update.vpd) end error( "SceneEB did not converge after $(m.maxiter) iterations ", - "(tol_T=$(m.tol_T), tol_VPD=$(m.tol_VPD))." + "(tol_t=$(m.tol_t), tol_vpd=$(m.tol_vpd), ", + "last_tair=$(last_update.tair), last_vpd=$(last_update.vpd))." ) end -function _publish_scene_leaf_solution!(leaf_targets, solution::SceneEBSolverResult, meteo) +function _publish_scene_leaf_solution!(leaf_targets, solution::SceneEBSolverResult, meteo, ground_area) fluxes = _run_scene_leaf_targets!( leaf_targets, meteo, - solution.Tair, - solution.VPD, - solution.psi_soil; + solution.tair, + solution.vpd, + solution.psi_soil, + ground_area; publish=true, ) for target in leaf_targets @@ -162,15 +321,18 @@ end function PlantSimEngine.run!(m::SceneEB, models, status, meteo, constants, extra) leaf_targets = dependency_targets(extra, :energy_balance) soil_target = only(dependency_targets(extra, :soil)) - solution = _solve_scene_energy_balance!(m, leaf_targets, soil_target, meteo) - fluxes = _publish_scene_leaf_solution!(leaf_targets, solution, meteo) - transpiration_mm = fluxes.E * duration_seconds(meteo) * 18.0e-6 + solution = _solve_scene_energy_balance!(m, leaf_targets, soil_target, status, meteo, constants) + fluxes = _publish_scene_leaf_solution!(leaf_targets, solution, meteo, m.ground_area) + transpiration_mm = λE_to_E(fluxes.lambda_e, solution.final_meteo.λ) * duration_seconds(meteo) * 18.0e-6 psi_soil = _run_scene_soil_feedback!(soil_target, transpiration_mm) - status.canopy_Tair = solution.Tair - status.canopy_VPD = solution.VPD + status.canopy_tair = solution.tair + status.canopy_vpd = solution.vpd + status.canopy_rh = solution.rh + status.canopy_htot = solution.htot + status.canopy_gcanop = solution.gcanop status.scene_transpiration = transpiration_mm - status.scene_assimilation = fluxes.A + status.scene_assimilation = fluxes.a status.psi_soil = psi_soil status.iterations = solution.iterations return nothing @@ -272,16 +434,37 @@ function maespa_mapping(; scene_model=SceneEB(25, 0.03, 0.005)) ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75)) |> TimeStepModel(Dates.Hour(1)), status=(theta1=0.33, theta2=0.36, psi_soil=-0.10, transpiration=0.0, infiltration=0.0), ) + ground_area = scene_model.ground_area scene = ModelMapping( + ModelSpec(LAIModel(ground_area)) |> TimeStepModel(Dates.Day(1)), ModelSpec(scene_model) |> TimeStepModel(Dates.Hour(1)), - status=(canopy_Tair=20.0, canopy_VPD=1.0, scene_transpiration=0.0, scene_assimilation=0.0, psi_soil=-0.1, iterations=0), + status=( + leaf_areas=[0.0], + leaf_area=0.0, + lai=0.0, + canopy_tair=20.0, + canopy_vpd=1.0, + canopy_rh=0.7, + canopy_htot=0.0, + canopy_gcanop=0.0, + scene_transpiration=0.0, + scene_assimilation=0.0, + psi_soil=-0.1, + iterations=0, + ), + ) + leaf_area_route = Route( + from=AllDomains(kind=:plant, scale=:Leaf, process=:leaf_state, var=:leaf_area), + to=DomainRouteTarget(:scene, var=:leaf_areas, process=:lai_dynamic), + cardinality=ManyToOneVector(), ) return SimulationMapping( Domain(:plant_A, plant_a; kind=:plant, selector=node -> MultiScaleTreeGraph.symbol(node) == :Plant && has_species(node, :A)), Domain(:plant_B, plant_b; kind=:plant, selector=node -> MultiScaleTreeGraph.symbol(node) == :Plant && has_species(node, :B)), Domain(:soil, soil; kind=:soil), - Domain(:scene, scene; kind=:scene), + Domain(:scene, scene; kind=:scene); + routes=(leaf_area_route,), ) end diff --git a/test/test-maespa-domain-example.jl b/test/test-maespa-domain-example.jl index 76240868b..b601820cd 100644 --- a/test/test-maespa-domain-example.jl +++ b/test/test-maespa-domain-example.jl @@ -9,17 +9,31 @@ include("../examples/maespa_domain_example.jl") @test length(status(sim, :plant_B, :Leaf)) == 3 scene_status = status(sim, :scene) - @test isfinite(scene_status.canopy_Tair) - @test isfinite(scene_status.canopy_VPD) + last_meteo = last(maespa_meteo(; nhours=24)) + vpd_above = max(0.01, last_meteo.VPD) + @test isfinite(scene_status.canopy_tair) + @test isfinite(scene_status.canopy_vpd) + @test isfinite(scene_status.canopy_rh) + @test isfinite(scene_status.canopy_htot) + @test isfinite(scene_status.canopy_gcanop) + @test scene_status.canopy_tair >= last_meteo.T - 10.0 + @test scene_status.canopy_tair <= last_meteo.T + 10.0 + @test scene_status.canopy_vpd >= max(0.01, vpd_above - 1.5) + @test scene_status.canopy_vpd <= vpd_above + 1.5 + @test scene_status.canopy_vpd > 0.0 + @test 0.0 <= scene_status.canopy_rh <= 1.0 @test isfinite(scene_status.scene_transpiration) @test scene_status.scene_transpiration > 0.0 @test scene_status.iterations > 0 + @test scene_status.leaf_area ≈ sum(st.leaf_area for st in status(sim, :Leaf)) + @test scene_status.lai ≈ scene_status.leaf_area + @test scene_status.leaf_areas ≈ getproperty.(status(sim, :Leaf), :leaf_area) leaf_statuses = status(sim, :Leaf) @test all(st -> isfinite(st.Tₗ), leaf_statuses) @test all(st -> isfinite(st.A), leaf_statuses) @test all(st -> isfinite(st.λE), leaf_statuses) - @test any(st -> abs(st.Tₗ - scene_status.canopy_Tair) > 1.0e-6, leaf_statuses) + @test any(st -> abs(st.Tₗ - scene_status.canopy_tair) > 1.0e-6, leaf_statuses) plant_a_status = only(status(sim, :plant_A, :Plant)) plant_b_status = only(status(sim, :plant_B, :Plant)) @@ -35,6 +49,7 @@ include("../examples/maespa_domain_example.jl") @test length(sim.outputs[(DomainModelKey(:plant_A, :Leaf, :energy_balance), :λE)]) == 2 * 24 @test length(sim.outputs[(DomainModelKey(:plant_B, :Leaf, :energy_balance), :λE)]) == 3 * 24 @test length(sim.outputs[(DomainModelKey(:soil, :Default, :soil_water), :psi_soil)]) == 24 + @test length(sim.outputs[(DomainModelKey(:scene, :Default, :lai_dynamic), :lai)]) == 24 @test length(sim.outputs[(DomainModelKey(:scene, :Default, :scene_eb), :scene_transpiration)]) == 24 @test status(sim, :soil).transpiration ≈ scene_status.scene_transpiration @test status(sim, :soil).psi_soil ≈ scene_status.psi_soil @@ -49,8 +64,22 @@ end status=(theta1=0.33, theta2=0.36, psi_soil=-0.10, transpiration=0.0, infiltration=0.0), ) scene_mapping = ModelMapping( + ModelSpec(LAIModel(1.0)) |> TimeStepModel(Dates.Hour(1)), ModelSpec(SceneEB(25, 0.03, 0.005)) |> TimeStepModel(Dates.Hour(1)), - status=(canopy_Tair=20.0, canopy_VPD=1.0, scene_transpiration=0.0, scene_assimilation=0.0, psi_soil=-0.1, iterations=0), + status=( + leaf_areas=[0.0], + leaf_area=0.0, + lai=0.0, + canopy_tair=20.0, + canopy_vpd=1.0, + canopy_rh=0.7, + canopy_htot=0.0, + canopy_gcanop=0.0, + scene_transpiration=0.0, + scene_assimilation=0.0, + psi_soil=-0.1, + iterations=0, + ), ) missing_leaf_mapping = SimulationMapping( Domain(:soil, soil_mapping; kind=:soil), @@ -59,10 +88,42 @@ end @test_throws "Hard domain dependency `energy_balance`" run!(mtg, missing_leaf_mapping, meteo, check=true, executor=SequentialEx()) soil_target = (status=Status(psi_soil=-0.1),) + scene_status = Status(lai=0.0, leaf_area=0.0) @test_throws "SceneEB did not converge after 0 iterations" _solve_scene_energy_balance!( SceneEB(0, 0.03, 0.005), ModelTarget[], soil_target, + scene_status, first(meteo), ) end + +@testset "MAESPA-style canopy helper functions" begin + lai_model = LAIModel(2.0) + lai_status = Status(leaf_areas=[0.5, 1.0], leaf_area=0.0, lai=0.0) + PlantSimEngine.run!(lai_model, nothing, lai_status, nothing, nothing) + @test lai_status.leaf_area ≈ 1.5 + @test lai_status.lai ≈ 0.75 + + m = SceneEB(25, 0.03, 0.005; ground_area=2.0) + + low_wind = gbcanms(0.0, m.zht, m.z0ht, m.zpd, m.tree_height, 0.75; gbcan_min=m.gbcan_min, von_karman=m.von_karman) + @test low_wind.canopy_air_ms ≈ m.gbcan_min + @test isfinite(low_wind.soil_canopy_ms) + + meteo_above = Atmosphere(T=25.0, Rh=0.50, Wind=1.2, Ri_PAR_f=800.0, Ri_SW_f=400.0, duration=Dates.Hour(1)) + canopy_meteo = Atmosphere(T=25.0, Rh=0.50, Wind=1.2, P=meteo_above.P, Ri_PAR_f=800.0, Ri_SW_f=400.0, duration=Dates.Hour(1)) + hot_fluxes = (rn=5000.0, lambda_e=-5000.0, a=0.0, lai=0.75, rad_interc=0.0) + hot = tvpdcanopcalc(m, hot_fluxes, meteo_above, canopy_meteo, PlantMeteo.Constants()) + @test hot.tair <= meteo_above.T + 10.0 + @test hot.vpd <= max(0.01, meteo_above.VPD) + 1.5 + + wet_fluxes = (rn=-5000.0, lambda_e=5000.0, a=0.0, lai=0.75, rad_interc=0.0) + wet = tvpdcanopcalc(m, wet_fluxes, meteo_above, canopy_meteo, PlantMeteo.Constants()) + @test wet.tair >= meteo_above.T - 10.0 + @test wet.vpd >= max(0.01, meteo_above.VPD - 1.5) + @test wet.vpd >= 0.01 + @test 0.0 <= wet.rh <= 1.0 + @test meteo_above.T == 25.0 + @test max(0.01, meteo_above.VPD) == meteo_above.VPD +end From 6f7663ebc880644c9e2c120086910b3cbf9019a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Thu, 4 Jun 2026 17:02:57 +0200 Subject: [PATCH 10/33] Auto-detect mapped variables between domains (not required in the user status anymore) --- docs/src/domain_simulation.md | 6 ++++ examples/maespa_domain_example.jl | 29 +++++++---------- src/domains/domain_simulation.jl | 50 ++++++++++++++++++++++++++++++ test/test-domain-simulation.jl | 3 +- test/test-maespa-domain-example.jl | 3 +- 5 files changed, 71 insertions(+), 20 deletions(-) diff --git a/docs/src/domain_simulation.md b/docs/src/domain_simulation.md index 226d6bec0..914c2ef42 100644 --- a/docs/src/domain_simulation.md +++ b/docs/src/domain_simulation.md @@ -204,6 +204,12 @@ producer, and `ManyToOneAggregate(f)` when it needs a scalar reduction. For an MTG-backed target domain, `OneToManyBroadcast()` can broadcast one source value into every status at the target scale before that domain runs. +When a route targets a single-status domain variable consumed by one target +model, the target status slot is created from that model's `inputs_` default if +the user did not initialize it explicitly. Variables that are only route +materialization slots and are not model inputs still need to be initialized in +the target status. + When graph-domain values are aggregated across time, PlantSimEngine aligns them by MTG node id. Growth and pruning inside the aggregation window therefore do not require every timestep to publish vectors with the same length. diff --git a/examples/maespa_domain_example.jl b/examples/maespa_domain_example.jl index b9a78d2ae..1fbcf5850 100644 --- a/examples/maespa_domain_example.jl +++ b/examples/maespa_domain_example.jl @@ -60,11 +60,11 @@ end function LAIModel(area) area > 0.0 || throw(ArgumentError("`area` must be strictly positive.")) - return LAIModel{Float64}(Float64(area)) + return LAIModel(area) end PlantSimEngine.inputs_(::LAIModel) = (leaf_areas=[-Inf],) -PlantSimEngine.outputs_(::LAIModel) = (lai=-Inf, leaf_area=-Inf) +PlantSimEngine.outputs_(::LAIModel) = (lai=0.0, leaf_area=-Inf) function PlantSimEngine.run!(m::LAIModel, models, status, meteo, constants, extra=nothing) status.leaf_area = sum(status.leaf_areas) @@ -190,8 +190,8 @@ function _run_scene_leaf_targets!(leaf_targets, meteo, tair_canopy, vpd_canopy, ) end -function gbcanms(wind, zht, z0ht, zpd, tree_height, lai; gbcan_min=0.0123, von_karman=0.41) - zpd2 = 0.75 * tree_height +function gbcanms(wind, zht, tree_height; gbcan_min=0.0123, von_karman=0.41) + zpd = 0.75 * tree_height z0 = 0.1 * tree_height zstar = max(zht, eps(Float64)) wind2 = max(wind, 1.0e-6) @@ -201,32 +201,28 @@ function gbcanms(wind, zht, z0ht, zpd, tree_height, lai; gbcan_min=0.0123, von_k zstar = 2.0 * tree_height end - zstar = max(zstar, zpd2 + z0 + 1.0e-6) - windstar = wind2 * von_karman / log((zstar - zpd2) / z0) + zstar = max(zstar, zpd + z0 + 1.0e-6) + windstar = wind2 * von_karman / log((zstar - zpd) / z0) alpha1 = 1.5 - zw = zpd2 + alpha1 * (tree_height - zpd2) - gbcanmsini = windstar * von_karman / log((zstar - zpd2) / (zw - zpd2)) - gbcanmsrou = windstar * von_karman / ((zw - tree_height) / (zw - zpd2)) + zw = zpd + alpha1 * (tree_height - zpd) + gbcanmsini = windstar * von_karman / log((zstar - zpd) / (zw - zpd)) + gbcanmsrou = windstar * von_karman / ((zw - tree_height) / (zw - zpd)) canopy_air_ms = max(1.0 / (1.0 / gbcanmsini + 1.0 / gbcanmsrou), gbcan_min) alpha = 2.0 z0ht2 = 0.01 - kh = alpha1 * von_karman * windstar * (tree_height - zpd2) + kh = alpha1 * von_karman * windstar * (tree_height - zpd) soil_denominator = tree_height * exp(alpha) * - (exp(-alpha * z0ht2 / tree_height) - exp(-alpha * (zpd2 + z0) / tree_height)) + (exp(-alpha * z0ht2 / tree_height) - exp(-alpha * (zpd + z0) / tree_height)) soil_canopy_ms = max(alpha * kh / soil_denominator, 0.0) return (canopy_air_ms=canopy_air_ms, soil_canopy_ms=soil_canopy_ms) end function tvpdcanopcalc(m::SceneEB, fluxes, meteo_above, canopy_meteo, constants) - lai = fluxes.lai gbs = gbcanms( meteo_above.Wind, m.zht, - m.z0ht, - m.zpd, - m.tree_height, - lai; + m.tree_height; gbcan_min=m.gbcan_min, von_karman=m.von_karman, ) @@ -439,7 +435,6 @@ function maespa_mapping(; scene_model=SceneEB(25, 0.03, 0.005)) ModelSpec(LAIModel(ground_area)) |> TimeStepModel(Dates.Day(1)), ModelSpec(scene_model) |> TimeStepModel(Dates.Hour(1)), status=( - leaf_areas=[0.0], leaf_area=0.0, lai=0.0, canopy_tair=20.0, diff --git a/src/domains/domain_simulation.jl b/src/domains/domain_simulation.jl index 362181112..01d8ab698 100644 --- a/src/domains/domain_simulation.jl +++ b/src/domains/domain_simulation.jl @@ -763,6 +763,55 @@ function _resolve_route_bindings(mapping::SimulationMapping, specs::Dict{DomainM return bindings end +function _route_target_input_default(route::Route, specs::Dict{DomainModelKey,ModelSpec}) + consumer_key = _route_target_consumer_key(route, specs) + isnothing(consumer_key) && return nothing + consumer_inputs = inputs_(specs[consumer_key]) + target_var = route.to.var + target_var in keys(consumer_inputs) || return nothing + return getproperty(consumer_inputs, target_var) +end + +function _route_target_status_defaults(mapping::SimulationMapping, domain::Domain, specs::Dict{DomainModelKey,ModelSpec}) + defaults = NamedTuple() + target_mapping = _domain_mapping(domain) + target_mapping isa ModelMapping{SingleScale} || return defaults + target_status = status(target_mapping) + + for route in mapping.routes + target = route.to + target.domain == domain.name || continue + target.scale == :Default || continue + target.var in propertynames(target_status) && continue + target.var in keys(defaults) && continue + + default = _route_target_input_default(route, specs) + isnothing(default) && continue + defaults = merge(defaults, NamedTuple{(target.var,)}((default,))) + end + + return defaults +end + +function _add_route_target_status_defaults(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) + domains = Domain[] + changed = false + + for domain in mapping.domains + target_mapping = _domain_mapping(domain) + defaults = _route_target_status_defaults(mapping, domain, specs) + if target_mapping isa ModelMapping{SingleScale} && !isempty(keys(defaults)) + augmented_status = Status(merge(defaults, NamedTuple(status(target_mapping)))) + target_mapping = copy(target_mapping, augmented_status) + changed = true + end + push!(domains, Domain(domain.name, domain.kind, target_mapping, domain.selector)) + end + + changed || return mapping + return SimulationMapping(domains...; routes=mapping.routes) +end + function _route_target_consumer_key(route::Route, specs::Dict{DomainModelKey,ModelSpec}) target = route.to if !isnothing(target.process) @@ -863,6 +912,7 @@ function _build_domain_simulation(mapping::SimulationMapping, meteo; staged_grap for domain in mapping.domains merge!(specs, _domain_model_specs(domain)) end + mapping = _add_route_target_status_defaults(mapping, specs) model_clocks = _domain_model_clocks(specs, timeline) graphs = _domain_dependency_graphs(mapping) diff --git a/test/test-domain-simulation.jl b/test/test-domain-simulation.jl index 553b8f17f..52d56c5e7 100644 --- a/test/test-domain-simulation.jl +++ b/test/test-domain-simulation.jl @@ -793,7 +793,7 @@ end hourly_scene_mapping = ModelMapping( ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStepModel(Dates.Hour(1)), - status=(plant_transpirations=[0.0], routed_total=0.0), + status=(routed_total=0.0,), ) vector_route = Route( @@ -813,6 +813,7 @@ end check=true, ) hourly_plant_sum = 0.01 * 0.5 * 100.0 + 0.02 * 0.3 * 100.0 + @test :plant_transpirations in propertynames(status(vector_sim, :scene)) @test status(vector_sim, :scene).plant_transpirations ≈ [0.5, 0.6] @test status(vector_sim, :scene).routed_total ≈ hourly_plant_sum diff --git a/test/test-maespa-domain-example.jl b/test/test-maespa-domain-example.jl index b601820cd..bf4178a34 100644 --- a/test/test-maespa-domain-example.jl +++ b/test/test-maespa-domain-example.jl @@ -49,7 +49,7 @@ include("../examples/maespa_domain_example.jl") @test length(sim.outputs[(DomainModelKey(:plant_A, :Leaf, :energy_balance), :λE)]) == 2 * 24 @test length(sim.outputs[(DomainModelKey(:plant_B, :Leaf, :energy_balance), :λE)]) == 3 * 24 @test length(sim.outputs[(DomainModelKey(:soil, :Default, :soil_water), :psi_soil)]) == 24 - @test length(sim.outputs[(DomainModelKey(:scene, :Default, :lai_dynamic), :lai)]) == 24 + @test length(sim.outputs[(DomainModelKey(:scene, :Default, :lai_dynamic), :lai)]) == 1 @test length(sim.outputs[(DomainModelKey(:scene, :Default, :scene_eb), :scene_transpiration)]) == 24 @test status(sim, :soil).transpiration ≈ scene_status.scene_transpiration @test status(sim, :soil).psi_soil ≈ scene_status.psi_soil @@ -67,7 +67,6 @@ end ModelSpec(LAIModel(1.0)) |> TimeStepModel(Dates.Hour(1)), ModelSpec(SceneEB(25, 0.03, 0.005)) |> TimeStepModel(Dates.Hour(1)), status=( - leaf_areas=[0.0], leaf_area=0.0, lai=0.0, canopy_tair=20.0, From 89f097166a9938675acb78e69e995e420495df92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Thu, 4 Jun 2026 17:22:34 +0200 Subject: [PATCH 11/33] Update maespa_domain_example.jl Fix recursive call leading to stack overflow --- examples/maespa_domain_example.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/maespa_domain_example.jl b/examples/maespa_domain_example.jl index 1fbcf5850..bb879cafa 100644 --- a/examples/maespa_domain_example.jl +++ b/examples/maespa_domain_example.jl @@ -56,11 +56,11 @@ scene domain. """ struct LAIModel{T} <: AbstractLai_DynamicModel area::T -end -function LAIModel(area) - area > 0.0 || throw(ArgumentError("`area` must be strictly positive.")) - return LAIModel(area) + function LAIModel(area::T) where {T} + area > 0 || throw(ArgumentError("`area` must be strictly positive.")) + new{T}(area) + end end PlantSimEngine.inputs_(::LAIModel) = (leaf_areas=[-Inf],) From 26df6a5b695ebec6052c80df6b00d469fedfe400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Thu, 4 Jun 2026 17:23:27 +0200 Subject: [PATCH 12/33] Split Route types and route materialization into different scripts --- docs/src/dev/code_cleanup_audit.md | 2 +- src/domains/domain_simulation.jl | 401 +---------------------------- src/domains/route_runtime.jl | 310 ++++++++++++++++++++++ src/domains/routes.jl | 89 +++++++ test/test-maespa-domain-example.jl | 2 +- 5 files changed, 403 insertions(+), 401 deletions(-) create mode 100644 src/domains/route_runtime.jl create mode 100644 src/domains/routes.jl diff --git a/docs/src/dev/code_cleanup_audit.md b/docs/src/dev/code_cleanup_audit.md index 27d69677a..6becfbd88 100644 --- a/docs/src/dev/code_cleanup_audit.md +++ b/docs/src/dev/code_cleanup_audit.md @@ -68,7 +68,7 @@ output preallocation, and the domain runtime. | Priority | Location | Risk | Recommended cleanup | | --- | --- | --- | --- | | Done | `src/dependencies/soft_dependencies.jl` hard-dependency redirection | Nested hard-dependency redirection is duplicated, walks parents with a depth cap, and can match by process without enough scale context. | Extracted shared owner-resolution helpers for nested hard dependencies, with scale-aware matching, ambiguity checks, and finalized soft-node validation. | -| P1 | `src/domains/domain_simulation.jl` runner | Scheduling, routing, environment sampling/scattering, graph runtime lifecycle, output publication, and post-scene phases are all mixed in one large file. | Split into domain scheduler, route materializer, environment bridge, graph-domain runner, and output publisher modules. | +| P1 | `src/domains/domain_simulation.jl` runner | Scheduling, environment sampling/scattering, graph runtime lifecycle, output publication, and post-scene phases are still mixed in one large file. Route types and route materialization have been extracted to `src/domains/routes.jl` and `src/domains/route_runtime.jl`. | Continue splitting into domain scheduler, environment bridge, graph-domain runner, and output publisher modules. | | Done | `src/time/runtime/input_resolution.jl` fallback resolution | Same-node, ancestor, and candidate-scan fallback can silently change behavior when topology or scope changes. | Built a shared source-status resolver that centralizes same-node, ancestor, vector, and unique-candidate fallback with scalar ambiguity validation. | | Done | `src/mtg/initialisation.jl` `RefVector` population | Vector input order depends on MTG traversal order and can drift after growth/removal. | Added `reindex_runtime_topology!` to sort statuses by MTG node id and rebuild downstream `RefVector`s from current statuses after initialization and topology mutations. | | Done | `src/mtg/mapping/compute_mapping.jl` and `src/mtg/mapping/mapping.jl` mapping sentinels/invariants | Magic sentinel values make mapping control flow fragile. | Same-scale mappings now use `SameScale()` instead of `Symbol("")`; explicit validation rejects the old sentinel. | diff --git a/src/domains/domain_simulation.jl b/src/domains/domain_simulation.jl index 01d8ab698..b3d5e33a3 100644 --- a/src/domains/domain_simulation.jl +++ b/src/domains/domain_simulation.jl @@ -146,95 +146,7 @@ function Base.show(io::IO, selector::HardDomains) print(io, "HardDomains(", join(terms, ", "), ")") end -abstract type RouteCardinality end - -""" - ManyToOneVector() - -Route cardinality that materializes one value per resolved producer. -""" -struct ManyToOneVector <: RouteCardinality end - -""" - ManyToOneAggregate(reducer=sum) - -Route cardinality that reduces resolved producer values to one scalar. -""" -struct ManyToOneAggregate{F} <: RouteCardinality - reducer::F -end - -ManyToOneAggregate() = ManyToOneAggregate(sum) - -""" - OneToManyBroadcast() - SpatialSample() - SpatialScatterAdd() - -Reserved route cardinalities for the MTG/spatial domain runner. -""" -struct OneToManyBroadcast <: RouteCardinality end -struct SpatialSample <: RouteCardinality end -struct SpatialScatterAdd <: RouteCardinality end - -""" - DomainRouteTarget(domain; scale=:Default, var, process=nothing) - -Target of an explicit cross-domain route. -""" -struct DomainRouteTarget - domain::Symbol - scale::Symbol - var::Symbol - process::Union{Nothing,Symbol} -end - -function Base.show(io::IO, target::DomainRouteTarget) - terms = ["domain=:$(target.domain)"] - target.scale == :Default || push!(terms, "scale=:$(target.scale)") - push!(terms, "var=:$(target.var)") - isnothing(target.process) || push!(terms, "process=:$(target.process)") - print(io, "DomainRouteTarget(", join(terms, ", "), ")") -end - -function DomainRouteTarget( - domain::Union{Symbol,AbstractString}; - scale::Union{Symbol,AbstractString}=:Default, - var, - process=nothing, -) - return DomainRouteTarget( - Symbol(domain), - Symbol(scale), - Symbol(var), - isnothing(process) ? nothing : Symbol(process), - ) -end - -""" - Route(from, to; cardinality=ManyToOneVector(), policy=nothing) - Route(; from, to, cardinality=ManyToOneVector(), policy=nothing) - -Explicit cross-domain route materialized into a target domain status before -that domain runs. -""" -struct Route{F,T,C<:RouteCardinality,P<:SchedulePolicy} - from::F - to::T - cardinality::C - policy::P -end - -function Route(from::AllDomains, to::DomainRouteTarget; cardinality::RouteCardinality=ManyToOneVector(), policy=nothing) - route_policy = isnothing(policy) ? from.policy : _as_schedule_policy(policy; context="Route policy") - isnothing(from.var) && error( - "Route source `AllDomains(...)` must declare `var=:source_variable` so the runtime knows what to materialize." - ) - return Route(from, to, cardinality, route_policy) -end - -Route(; from, to, cardinality::RouteCardinality=ManyToOneVector(), policy=nothing) = - Route(from, to; cardinality=cardinality, policy=policy) +include("routes.jl") """ SimulationMapping(domains...) @@ -727,181 +639,7 @@ function _keys_by_domain(specs::Dict{DomainModelKey,ModelSpec}) return keys_by_domain end -function _resolve_selector_matches( - mapping::SimulationMapping, - specs::Dict{DomainModelKey,ModelSpec}, - selector::Union{AllDomains,HardDomains}; - context::String, -) - resolved = DomainModelKey[] - keys_by_domain = _keys_by_domain(specs) - for domain in mapping.domains - for producer_key in get(keys_by_domain, domain.name, DomainModelKey[]) - producer_spec = specs[producer_key] - _matches(selector, domain, producer_key, producer_spec) && push!(resolved, producer_key) - end - end - isempty(resolved) && error( - _selector_match_error(mapping, specs, selector; context=context) - ) - return resolved -end - -function _resolve_route_bindings(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) - bindings = Vector{DomainModelKey}[] - for (i, route) in enumerate(mapping.routes) - push!( - bindings, - _resolve_selector_matches( - mapping, - specs, - route.from; - context="Route $(i) from `$(route.from)`", - ), - ) - end - return bindings -end - -function _route_target_input_default(route::Route, specs::Dict{DomainModelKey,ModelSpec}) - consumer_key = _route_target_consumer_key(route, specs) - isnothing(consumer_key) && return nothing - consumer_inputs = inputs_(specs[consumer_key]) - target_var = route.to.var - target_var in keys(consumer_inputs) || return nothing - return getproperty(consumer_inputs, target_var) -end - -function _route_target_status_defaults(mapping::SimulationMapping, domain::Domain, specs::Dict{DomainModelKey,ModelSpec}) - defaults = NamedTuple() - target_mapping = _domain_mapping(domain) - target_mapping isa ModelMapping{SingleScale} || return defaults - target_status = status(target_mapping) - - for route in mapping.routes - target = route.to - target.domain == domain.name || continue - target.scale == :Default || continue - target.var in propertynames(target_status) && continue - target.var in keys(defaults) && continue - - default = _route_target_input_default(route, specs) - isnothing(default) && continue - defaults = merge(defaults, NamedTuple{(target.var,)}((default,))) - end - - return defaults -end - -function _add_route_target_status_defaults(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) - domains = Domain[] - changed = false - - for domain in mapping.domains - target_mapping = _domain_mapping(domain) - defaults = _route_target_status_defaults(mapping, domain, specs) - if target_mapping isa ModelMapping{SingleScale} && !isempty(keys(defaults)) - augmented_status = Status(merge(defaults, NamedTuple(status(target_mapping)))) - target_mapping = copy(target_mapping, augmented_status) - changed = true - end - push!(domains, Domain(domain.name, domain.kind, target_mapping, domain.selector)) - end - - changed || return mapping - return SimulationMapping(domains...; routes=mapping.routes) -end - -function _route_target_consumer_key(route::Route, specs::Dict{DomainModelKey,ModelSpec}) - target = route.to - if !isnothing(target.process) - key = DomainModelKey(target.domain, target.scale, target.process) - haskey(specs, key) || error( - "Route target process `$(key)` does not exist." - ) - target.var in keys(inputs_(specs[key])) || error( - "Route target process `$(key)` does not consume variable `$(target.var)`. ", - "Use a process that declares `$(target.var)` in `inputs_`, or omit `process=...` ", - "if the route should only materialize a domain status variable." - ) - return key - end - - consumers = DomainModelKey[] - for (key, spec) in specs - key.domain == target.domain || continue - key.scale == target.scale || continue - target.var in keys(inputs_(spec)) || continue - push!(consumers, key) - end - - isempty(consumers) && return nothing - length(consumers) == 1 || error( - "Route target `$(target.domain)/$(target.scale)/$(target.var)` is consumed by several models: ", - join(consumers, ", "), - ". Specify `process=...` in `DomainRouteTarget` so the route clock is unambiguous." - ) - return only(consumers) -end - -function _validate_route_targets(mapping::SimulationMapping, routes::Vector{Route}, specs::Dict{DomainModelKey,ModelSpec}; staged_graph_domains=false) - for (i, route) in enumerate(routes) - target = route.to - domain = _domain_for_name(mapping, target.domain) - target_mapping = _domain_mapping(domain) - if target_mapping isa ModelMapping{MultiScale} - staged_graph_domains || error( - "Route $(i) targets MTG-backed domain `$(target.domain)`, but this runner only supports single-status domains." - ) - route.cardinality isa OneToManyBroadcast || error( - "Route $(i) targets MTG-backed domain `$(target.domain)`. ", - "The MTG-domain runner only supports `OneToManyBroadcast()` routes into graph domains." - ) - target.scale == :Default && error( - "Route $(i) targets MTG-backed domain `$(target.domain)` but uses `scale=:Default`. ", - "Specify the target graph scale, for example `scale=:Leaf`." - ) - isnothing(_route_target_consumer_key(route, specs)) && error( - "Route $(i) targets graph variable `$(target.var)` in `$(target.domain)/$(target.scale)`, ", - "but no target process consumes that variable. Specify `process=...` or add the variable to one model's `inputs_`." - ) - continue - end - target.scale == :Default || error( - "Route $(i) target `$(target.domain)/$(target.scale)/$(target.var)` is not supported by the single-status domain runner. ", - "Use `scale=:Default` for single-status targets, or target an MTG-backed domain with a supported graph route cardinality." - ) - st = status(target_mapping) - target.var in propertynames(st) || error( - "Route $(i) target status `$(target.domain)/$(target.scale)` does not contain variable `$(target.var)`. ", - "Initialize it in the target domain status so the route can materialize its value." - ) - _route_target_consumer_key(route, specs) - end - return nothing -end - -function _validate_graph_route_order( - mapping::SimulationMapping, - routes::Vector{Route}, - route_bindings::Vector{Vector{DomainModelKey}}, -) - _domain_run_order(mapping, route_bindings) - return nothing -end - -function _route_clocks(routes::Vector{Route}, specs::Dict{DomainModelKey,ModelSpec}, model_clocks) - clocks = ClockSpec[] - for route in routes - consumer_key = _route_target_consumer_key(route, specs) - if isnothing(consumer_key) - push!(clocks, ClockSpec(1.0, 0.0)) - else - push!(clocks, model_clocks[consumer_key]) - end - end - return clocks -end +include("route_runtime.jl") function _build_domain_simulation(mapping::SimulationMapping, meteo; staged_graph_domains=false) environment = environment_backend(meteo) @@ -1443,141 +1181,6 @@ end run_target!(models, status, dependency_name::AbstractString; kwargs...) = run_target!(models, status, Symbol(dependency_name); kwargs...) -function _route_due(simulation::DomainSimulation, route_index::Int, step::Int) - clock = simulation.route_clocks[route_index] - return _should_run_at_time(clock, float(step)) -end - -function _route_producer_values(simulation::DomainSimulation, route_index::Int, step::Int) - route = simulation.mapping.routes[route_index] - source_var = route.from.var - steps = _window_steps(step, simulation.route_clocks[route_index]) - return Any[ - _apply_dependency_policy(_resolve_stream_values(simulation, producer, source_var, steps), route.policy) - for producer in simulation.route_bindings[route_index] - ] -end - -function _route_value_items(values::Vector{Any}) - items = Any[] - for value in values - isnothing(value) && continue - if value isa DomainNodeValues - append!(items, value.values) - else - push!(items, value) - end - end - return items -end - -function _materialize_route_value(values::Vector{Any}, cardinality::ManyToOneVector) - return _route_value_items(values) -end - -function _materialize_route_value(values::Vector{Any}, cardinality::ManyToOneAggregate) - return cardinality.reducer(_route_value_items(values)) -end - -function _materialize_graph_broadcast_value(values::Vector{Any}, route_index::Int) - items = _route_value_items(values) - length(items) == 1 || error( - "Route $(route_index) uses `OneToManyBroadcast()` into an MTG-backed domain and resolved $(length(items)) values. ", - "Use a selector that resolves one source value, or aggregate upstream before broadcasting." - ) - return only(items) -end - -function _materialize_route_value(values::Vector{Any}, cardinality::RouteCardinality) - error( - "Route cardinality `$(typeof(cardinality))` is declared but not implemented in the single-status domain runner. ", - "Use `ManyToOneVector()` or `ManyToOneAggregate(...)` for single-status targets. ", - "`OneToManyBroadcast()` is supported for MTG-backed target domains." - ) -end - -function _set_route_target_value!(simulation::DomainSimulation, route::Route, value) - target = route.to - domain = _domain_for_name(simulation.mapping, target.domain) - target.scale == :Default || error( - "Route target `$(target.domain)/$(target.scale)/$(target.var)` is not supported by the single-status domain runner. ", - "Use `scale=:Default` for single-status targets, or target an MTG-backed domain with a supported graph route cardinality." - ) - st = status(simulation, domain.name) - target.var in propertynames(st) || error( - "Route target status `$(target.domain)/$(target.scale)` does not contain variable `$(target.var)`. ", - "Initialize it in the target domain status so the route can materialize its value." - ) - st[target.var] = value - return nothing -end - -function _materialize_routes_for_domain!(simulation::DomainSimulation, domain::Domain, step::Int) - for (i, route) in enumerate(simulation.mapping.routes) - route.to.domain == domain.name || continue - _route_due(simulation, i, step) || continue - values = _route_producer_values(simulation, i, step) - value = _materialize_route_value(values, route.cardinality) - _set_route_target_value!(simulation, route, value) - end - return nothing -end - -function _materialize_graph_routes_for_domain!( - simulation::DomainSimulation, - domain::Domain, - graph_simulation::GraphSimulation, - step::Int, -) - for (i, route) in enumerate(simulation.mapping.routes) - route.to.domain == domain.name || continue - route.cardinality isa OneToManyBroadcast || continue - _route_due(simulation, i, step) || continue - target = route.to - graph_statuses = status(graph_simulation) - haskey(graph_statuses, target.scale) || error( - "Route $(i) targets `$(target.domain)/$(target.scale)/$(target.var)`, ", - "but the selected graph domain has no statuses at scale `$(target.scale)`." - ) - values = _route_producer_values(simulation, i, step) - value = _materialize_graph_broadcast_value(values, i) - for st in graph_statuses[target.scale] - target.var in propertynames(st) || error( - "Route $(i) targets `$(target.domain)/$(target.scale)/$(target.var)`, ", - "but one target status does not contain variable `$(target.var)`." - ) - st[target.var] = value - end - end - return nothing -end - -function _materialize_graph_route_attributes_for_domain!( - simulation::DomainSimulation, - domain::Domain, - root, - step::Int, -) - for (i, route) in enumerate(simulation.mapping.routes) - route.to.domain == domain.name || continue - route.cardinality isa OneToManyBroadcast || continue - target = route.to - values = _route_producer_values(simulation, i, step) - value = _materialize_graph_broadcast_value(values, i) - matched = 0 - MultiScaleTreeGraph.traverse!(root) do node - symbol(node) == target.scale || return - node[target.var] = value - matched += 1 - end - matched > 0 || error( - "Route $(i) targets `$(target.domain)/$(target.scale)/$(target.var)`, ", - "but the selected graph domain has no nodes at scale `$(target.scale)`." - ) - end - return nothing -end - function _domain_context_for(simulation::DomainSimulation, domain::Domain, node::SoftDependencyNode, step::Int, constants=nothing) key = DomainModelKey(domain.name, node.scale, node.process) return DomainRunContext(simulation, key, step, simulation.model_clocks[key], constants) diff --git a/src/domains/route_runtime.jl b/src/domains/route_runtime.jl new file mode 100644 index 000000000..1fbf13263 --- /dev/null +++ b/src/domains/route_runtime.jl @@ -0,0 +1,310 @@ +function _resolve_selector_matches( + mapping::SimulationMapping, + specs::Dict{DomainModelKey,ModelSpec}, + selector::Union{AllDomains,HardDomains}; + context::String, +) + resolved = DomainModelKey[] + keys_by_domain = _keys_by_domain(specs) + for domain in mapping.domains + for producer_key in get(keys_by_domain, domain.name, DomainModelKey[]) + producer_spec = specs[producer_key] + _matches(selector, domain, producer_key, producer_spec) && push!(resolved, producer_key) + end + end + isempty(resolved) && error( + _selector_match_error(mapping, specs, selector; context=context) + ) + return resolved +end + +function _resolve_route_bindings(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) + bindings = Vector{DomainModelKey}[] + for (i, route) in enumerate(mapping.routes) + push!( + bindings, + _resolve_selector_matches( + mapping, + specs, + route.from; + context="Route $(i) from `$(route.from)`", + ), + ) + end + return bindings +end + +function _route_target_input_default(route::Route, specs::Dict{DomainModelKey,ModelSpec}) + consumer_key = _route_target_consumer_key(route, specs) + isnothing(consumer_key) && return nothing + consumer_inputs = inputs_(specs[consumer_key]) + target_var = route.to.var + target_var in keys(consumer_inputs) || return nothing + return getproperty(consumer_inputs, target_var) +end + +function _route_target_status_defaults(mapping::SimulationMapping, domain::Domain, specs::Dict{DomainModelKey,ModelSpec}) + defaults = NamedTuple() + target_mapping = _domain_mapping(domain) + target_mapping isa ModelMapping{SingleScale} || return defaults + target_status = status(target_mapping) + + for route in mapping.routes + target = route.to + target.domain == domain.name || continue + target.scale == :Default || continue + target.var in propertynames(target_status) && continue + target.var in keys(defaults) && continue + + default = _route_target_input_default(route, specs) + isnothing(default) && continue + defaults = merge(defaults, NamedTuple{(target.var,)}((default,))) + end + + return defaults +end + +function _add_route_target_status_defaults(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) + domains = Domain[] + changed = false + + for domain in mapping.domains + target_mapping = _domain_mapping(domain) + defaults = _route_target_status_defaults(mapping, domain, specs) + if target_mapping isa ModelMapping{SingleScale} && !isempty(keys(defaults)) + augmented_status = Status(merge(defaults, NamedTuple(status(target_mapping)))) + target_mapping = copy(target_mapping, augmented_status) + changed = true + end + push!(domains, Domain(domain.name, domain.kind, target_mapping, domain.selector)) + end + + changed || return mapping + return SimulationMapping(domains...; routes=mapping.routes) +end + +function _route_target_consumer_key(route::Route, specs::Dict{DomainModelKey,ModelSpec}) + target = route.to + if !isnothing(target.process) + key = DomainModelKey(target.domain, target.scale, target.process) + haskey(specs, key) || error( + "Route target process `$(key)` does not exist." + ) + target.var in keys(inputs_(specs[key])) || error( + "Route target process `$(key)` does not consume variable `$(target.var)`. ", + "Use a process that declares `$(target.var)` in `inputs_`, or omit `process=...` ", + "if the route should only materialize a domain status variable." + ) + return key + end + + consumers = DomainModelKey[] + for (key, spec) in specs + key.domain == target.domain || continue + key.scale == target.scale || continue + target.var in keys(inputs_(spec)) || continue + push!(consumers, key) + end + + isempty(consumers) && return nothing + length(consumers) == 1 || error( + "Route target `$(target.domain)/$(target.scale)/$(target.var)` is consumed by several models: ", + join(consumers, ", "), + ". Specify `process=...` in `DomainRouteTarget` so the route clock is unambiguous." + ) + return only(consumers) +end + +function _validate_route_targets(mapping::SimulationMapping, routes::Vector{Route}, specs::Dict{DomainModelKey,ModelSpec}; staged_graph_domains=false) + for (i, route) in enumerate(routes) + target = route.to + domain = _domain_for_name(mapping, target.domain) + target_mapping = _domain_mapping(domain) + if target_mapping isa ModelMapping{MultiScale} + staged_graph_domains || error( + "Route $(i) targets MTG-backed domain `$(target.domain)`, but this runner only supports single-status domains." + ) + route.cardinality isa OneToManyBroadcast || error( + "Route $(i) targets MTG-backed domain `$(target.domain)`. ", + "The MTG-domain runner only supports `OneToManyBroadcast()` routes into graph domains." + ) + target.scale == :Default && error( + "Route $(i) targets MTG-backed domain `$(target.domain)` but uses `scale=:Default`. ", + "Specify the target graph scale, for example `scale=:Leaf`." + ) + isnothing(_route_target_consumer_key(route, specs)) && error( + "Route $(i) targets graph variable `$(target.var)` in `$(target.domain)/$(target.scale)`, ", + "but no target process consumes that variable. Specify `process=...` or add the variable to one model's `inputs_`." + ) + continue + end + target.scale == :Default || error( + "Route $(i) target `$(target.domain)/$(target.scale)/$(target.var)` is not supported by the single-status domain runner. ", + "Use `scale=:Default` for single-status targets, or target an MTG-backed domain with a supported graph route cardinality." + ) + st = status(target_mapping) + target.var in propertynames(st) || error( + "Route $(i) target status `$(target.domain)/$(target.scale)` does not contain variable `$(target.var)`. ", + "Initialize it in the target domain status so the route can materialize its value." + ) + _route_target_consumer_key(route, specs) + end + return nothing +end + +function _validate_graph_route_order( + mapping::SimulationMapping, + routes::Vector{Route}, + route_bindings::Vector{Vector{DomainModelKey}}, +) + _domain_run_order(mapping, route_bindings) + return nothing +end + +function _route_clocks(routes::Vector{Route}, specs::Dict{DomainModelKey,ModelSpec}, model_clocks) + clocks = ClockSpec[] + for route in routes + consumer_key = _route_target_consumer_key(route, specs) + if isnothing(consumer_key) + push!(clocks, ClockSpec(1.0, 0.0)) + else + push!(clocks, model_clocks[consumer_key]) + end + end + return clocks +end + +function _route_due(simulation::DomainSimulation, route_index::Int, step::Int) + clock = simulation.route_clocks[route_index] + return _should_run_at_time(clock, float(step)) +end + +function _route_producer_values(simulation::DomainSimulation, route_index::Int, step::Int) + route = simulation.mapping.routes[route_index] + source_var = route.from.var + steps = _window_steps(step, simulation.route_clocks[route_index]) + return Any[ + _apply_dependency_policy(_resolve_stream_values(simulation, producer, source_var, steps), route.policy) + for producer in simulation.route_bindings[route_index] + ] +end + +function _route_value_items(values::Vector{Any}) + items = Any[] + for value in values + isnothing(value) && continue + if value isa DomainNodeValues + append!(items, value.values) + else + push!(items, value) + end + end + return items +end + +function _materialize_route_value(values::Vector{Any}, cardinality::ManyToOneVector) + return _route_value_items(values) +end + +function _materialize_route_value(values::Vector{Any}, cardinality::ManyToOneAggregate) + return cardinality.reducer(_route_value_items(values)) +end + +function _materialize_graph_broadcast_value(values::Vector{Any}, route_index::Int) + items = _route_value_items(values) + length(items) == 1 || error( + "Route $(route_index) uses `OneToManyBroadcast()` into an MTG-backed domain and resolved $(length(items)) values. ", + "Use a selector that resolves one source value, or aggregate upstream before broadcasting." + ) + return only(items) +end + +function _materialize_route_value(values::Vector{Any}, cardinality::RouteCardinality) + error( + "Route cardinality `$(typeof(cardinality))` is declared but not implemented in the single-status domain runner. ", + "Use `ManyToOneVector()` or `ManyToOneAggregate(...)` for single-status targets. ", + "`OneToManyBroadcast()` is supported for MTG-backed target domains." + ) +end + +function _set_route_target_value!(simulation::DomainSimulation, route::Route, value) + target = route.to + domain = _domain_for_name(simulation.mapping, target.domain) + target.scale == :Default || error( + "Route target `$(target.domain)/$(target.scale)/$(target.var)` is not supported by the single-status domain runner. ", + "Use `scale=:Default` for single-status targets, or target an MTG-backed domain with a supported graph route cardinality." + ) + st = status(simulation, domain.name) + target.var in propertynames(st) || error( + "Route target status `$(target.domain)/$(target.scale)` does not contain variable `$(target.var)`. ", + "Initialize it in the target domain status so the route can materialize its value." + ) + st[target.var] = value + return nothing +end + +function _materialize_routes_for_domain!(simulation::DomainSimulation, domain::Domain, step::Int) + for (i, route) in enumerate(simulation.mapping.routes) + route.to.domain == domain.name || continue + _route_due(simulation, i, step) || continue + values = _route_producer_values(simulation, i, step) + value = _materialize_route_value(values, route.cardinality) + _set_route_target_value!(simulation, route, value) + end + return nothing +end + +function _materialize_graph_routes_for_domain!( + simulation::DomainSimulation, + domain::Domain, + graph_simulation::GraphSimulation, + step::Int, +) + for (i, route) in enumerate(simulation.mapping.routes) + route.to.domain == domain.name || continue + route.cardinality isa OneToManyBroadcast || continue + _route_due(simulation, i, step) || continue + target = route.to + graph_statuses = status(graph_simulation) + haskey(graph_statuses, target.scale) || error( + "Route $(i) targets `$(target.domain)/$(target.scale)/$(target.var)`, ", + "but the selected graph domain has no statuses at scale `$(target.scale)`." + ) + values = _route_producer_values(simulation, i, step) + value = _materialize_graph_broadcast_value(values, i) + for st in graph_statuses[target.scale] + target.var in propertynames(st) || error( + "Route $(i) targets `$(target.domain)/$(target.scale)/$(target.var)`, ", + "but one target status does not contain variable `$(target.var)`." + ) + st[target.var] = value + end + end + return nothing +end + +function _materialize_graph_route_attributes_for_domain!( + simulation::DomainSimulation, + domain::Domain, + root, + step::Int, +) + for (i, route) in enumerate(simulation.mapping.routes) + route.to.domain == domain.name || continue + route.cardinality isa OneToManyBroadcast || continue + target = route.to + values = _route_producer_values(simulation, i, step) + value = _materialize_graph_broadcast_value(values, i) + matched = 0 + MultiScaleTreeGraph.traverse!(root) do node + symbol(node) == target.scale || return + node[target.var] = value + matched += 1 + end + matched > 0 || error( + "Route $(i) targets `$(target.domain)/$(target.scale)/$(target.var)`, ", + "but the selected graph domain has no nodes at scale `$(target.scale)`." + ) + end + return nothing +end diff --git a/src/domains/routes.jl b/src/domains/routes.jl new file mode 100644 index 000000000..ad4da48d1 --- /dev/null +++ b/src/domains/routes.jl @@ -0,0 +1,89 @@ +abstract type RouteCardinality end + +""" + ManyToOneVector() + +Route cardinality that materializes one value per resolved producer. +""" +struct ManyToOneVector <: RouteCardinality end + +""" + ManyToOneAggregate(reducer=sum) + +Route cardinality that reduces resolved producer values to one scalar. +""" +struct ManyToOneAggregate{F} <: RouteCardinality + reducer::F +end + +ManyToOneAggregate() = ManyToOneAggregate(sum) + +""" + OneToManyBroadcast() + SpatialSample() + SpatialScatterAdd() + +Reserved route cardinalities for the MTG/spatial domain runner. +""" +struct OneToManyBroadcast <: RouteCardinality end +struct SpatialSample <: RouteCardinality end +struct SpatialScatterAdd <: RouteCardinality end + +""" + DomainRouteTarget(domain; scale=:Default, var, process=nothing) + +Target of an explicit cross-domain route. +""" +struct DomainRouteTarget + domain::Symbol + scale::Symbol + var::Symbol + process::Union{Nothing,Symbol} +end + +function Base.show(io::IO, target::DomainRouteTarget) + terms = ["domain=:$(target.domain)"] + target.scale == :Default || push!(terms, "scale=:$(target.scale)") + push!(terms, "var=:$(target.var)") + isnothing(target.process) || push!(terms, "process=:$(target.process)") + print(io, "DomainRouteTarget(", join(terms, ", "), ")") +end + +function DomainRouteTarget( + domain::Union{Symbol,AbstractString}; + scale::Union{Symbol,AbstractString}=:Default, + var, + process=nothing, +) + return DomainRouteTarget( + Symbol(domain), + Symbol(scale), + Symbol(var), + isnothing(process) ? nothing : Symbol(process), + ) +end + +""" + Route(from, to; cardinality=ManyToOneVector(), policy=nothing) + Route(; from, to, cardinality=ManyToOneVector(), policy=nothing) + +Explicit cross-domain route materialized into a target domain status before +that domain runs. +""" +struct Route{F,T,C<:RouteCardinality,P<:SchedulePolicy} + from::F + to::T + cardinality::C + policy::P +end + +function Route(from::AllDomains, to::DomainRouteTarget; cardinality::RouteCardinality=ManyToOneVector(), policy=nothing) + route_policy = isnothing(policy) ? from.policy : _as_schedule_policy(policy; context="Route policy") + isnothing(from.var) && error( + "Route source `AllDomains(...)` must declare `var=:source_variable` so the runtime knows what to materialize." + ) + return Route(from, to, cardinality, route_policy) +end + +Route(; from, to, cardinality::RouteCardinality=ManyToOneVector(), policy=nothing) = + Route(from, to; cardinality=cardinality, policy=policy) diff --git a/test/test-maespa-domain-example.jl b/test/test-maespa-domain-example.jl index bf4178a34..b6378ed03 100644 --- a/test/test-maespa-domain-example.jl +++ b/test/test-maespa-domain-example.jl @@ -106,7 +106,7 @@ end m = SceneEB(25, 0.03, 0.005; ground_area=2.0) - low_wind = gbcanms(0.0, m.zht, m.z0ht, m.zpd, m.tree_height, 0.75; gbcan_min=m.gbcan_min, von_karman=m.von_karman) + low_wind = gbcanms(0.0, m.zht, m.tree_height; gbcan_min=m.gbcan_min, von_karman=m.von_karman) @test low_wind.canopy_air_ms ≈ m.gbcan_min @test isfinite(low_wind.soil_canopy_ms) From 82d881cb68369fb651cf87c92d1e7ed788136467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Fri, 5 Jun 2026 06:46:34 +0200 Subject: [PATCH 13/33] Remove all references to ModelList --- docs/src/dev/code_cleanup_audit.md | 15 +- docs/src/working_with_data/inputs.md | 2 +- src/PlantSimEngine.jl | 6 +- src/checks/dimensions.jl | 6 +- .../{ModelList.jl => SingleScaleModelSet.jl} | 90 +-- src/component_models/Status.jl | 2 +- src/component_models/StatusView.jl | 2 +- src/component_models/TimeStepTable.jl | 4 +- src/component_models/get_status.jl | 18 +- src/dependencies/dependencies.jl | 4 +- src/dependencies/hard_dependencies.jl | 4 +- src/dependencies/soft_dependencies.jl | 2 +- src/domains/domain_run_loops.jl | 89 +++ src/domains/domain_scheduler.jl | 167 +++++ src/domains/domain_simulation.jl | 686 +----------------- src/domains/environment_bridge.jl | 171 +++++ src/domains/graph_domain_runner.jl | 135 ++++ src/domains/output_publisher.jl | 104 +++ src/evaluation/fit.jl | 4 +- src/mtg/GraphSimulation.jl | 2 +- src/mtg/mapping/mapping.jl | 21 +- .../model_generation_from_status_vectors.jl | 2 +- src/mtg/mapping/reverse_mapping.jl | 4 +- src/mtg/save_results.jl | 2 +- src/processes/model_initialisation.jl | 6 +- src/run.jl | 24 +- src/traits/table_traits.jl | 6 +- test/test-simulation.jl | 8 +- 28 files changed, 798 insertions(+), 788 deletions(-) rename src/component_models/{ModelList.jl => SingleScaleModelSet.jl} (84%) create mode 100644 src/domains/domain_run_loops.jl create mode 100644 src/domains/domain_scheduler.jl create mode 100644 src/domains/environment_bridge.jl create mode 100644 src/domains/graph_domain_runner.jl create mode 100644 src/domains/output_publisher.jl diff --git a/docs/src/dev/code_cleanup_audit.md b/docs/src/dev/code_cleanup_audit.md index 6becfbd88..c0fad9a4d 100644 --- a/docs/src/dev/code_cleanup_audit.md +++ b/docs/src/dev/code_cleanup_audit.md @@ -36,7 +36,7 @@ and some are still internal dependencies rather than shallow exported shims. | Priority | Compatibility surface | Evidence | Migration note | | --- | --- | --- | --- | -| P0 | `ModelList` public API and legacy backing type | `src/PlantSimEngine.jl`; `src/component_models/ModelList.jl`; `src/mtg/mapping/mapping.jl` | `ModelList` has been removed. Use `ModelMapping(model...; status=..., type_promotion=...)` for single-scale simulations. | +| Done | `ModelList` public API and legacy backing type | Formerly in `src/PlantSimEngine.jl`; `src/component_models/ModelList.jl`; `src/mtg/mapping/mapping.jl` | `ModelList` has been removed. Use `ModelMapping(model...; status=..., type_promotion=...)` for single-scale simulations. | | Done | `run!(::ModelList, ...)` | Formerly in `src/run.jl` direct `ModelList` methods | `run!(::ModelList, ...)` has been removed. Wrap models in `ModelMapping` before running. | | Done | Batch `run!` for collections of `ModelList` or single-scale mappings | Formerly in `src/run.jl` collection methods | Batch `run!([mapping1, mapping2], meteo)` and `run!(Dict(...), meteo)` are removed. Use an explicit loop or comprehension and call `run!` per mapping. | | Done | `run!(mtg, mapping::AbstractDict, ...)` | Formerly in `src/run.jl` | Passing a raw `Dict` to multiscale `run!` is removed. Construct `ModelMapping(dict)` first, or use `ModelMapping(:Scale => models, ...)`. | @@ -44,10 +44,8 @@ and some are still internal dependencies rather than shallow exported shims. | Done | `ModelMapping(Float64 => Float32)` as old type-promotion shorthand | Formerly in `src/mtg/mapping/mapping.jl` | `ModelMapping(Float64 => Float32)` is removed. Use `Dict(Float64 => Float32)` as the `type_promotion` value. | | Done | Old output indexing helpers on multiscale output dictionaries | Formerly in `src/mtg/GraphSimulation.jl` | `outputs(out_dict, key)` and `outputs(out_dict, i)` are removed. Use `convert_outputs(out_dict, sink)` and index the converted table or dictionary explicitly. | -Important: full `ModelList` removal is the largest cleanup. `ModelMapping{SingleScale}` -currently delegates to it internally, so complete removal requires a replacement -single-scale backing path in `ModelMapping`, `run!`, dependency/status helpers, -output preallocation, and the domain runtime. +`ModelMapping{SingleScale}` now uses an internal single-scale backing container +instead of the removed public `ModelList` API. ## Non-Idiomatic Julia Patterns @@ -68,7 +66,7 @@ output preallocation, and the domain runtime. | Priority | Location | Risk | Recommended cleanup | | --- | --- | --- | --- | | Done | `src/dependencies/soft_dependencies.jl` hard-dependency redirection | Nested hard-dependency redirection is duplicated, walks parents with a depth cap, and can match by process without enough scale context. | Extracted shared owner-resolution helpers for nested hard dependencies, with scale-aware matching, ambiguity checks, and finalized soft-node validation. | -| P1 | `src/domains/domain_simulation.jl` runner | Scheduling, environment sampling/scattering, graph runtime lifecycle, output publication, and post-scene phases are still mixed in one large file. Route types and route materialization have been extracted to `src/domains/routes.jl` and `src/domains/route_runtime.jl`. | Continue splitting into domain scheduler, environment bridge, graph-domain runner, and output publisher modules. | +| Done | `src/domains/domain_simulation.jl` runner | Scheduling, environment sampling/scattering, route materialization, output publication, graph runtime lifecycle, and post-scene run loops were mixed in one large file. | Split domain routes, route runtime, environment bridge, output publication, scheduler helpers, graph-domain runner lifecycle, and shared run-loop orchestration into focused `src/domains/*` modules while keeping public run entry points in `domain_simulation.jl`. | | Done | `src/time/runtime/input_resolution.jl` fallback resolution | Same-node, ancestor, and candidate-scan fallback can silently change behavior when topology or scope changes. | Built a shared source-status resolver that centralizes same-node, ancestor, vector, and unique-candidate fallback with scalar ambiguity validation. | | Done | `src/mtg/initialisation.jl` `RefVector` population | Vector input order depends on MTG traversal order and can drift after growth/removal. | Added `reindex_runtime_topology!` to sort statuses by MTG node id and rebuild downstream `RefVector`s from current statuses after initialization and topology mutations. | | Done | `src/mtg/mapping/compute_mapping.jl` and `src/mtg/mapping/mapping.jl` mapping sentinels/invariants | Magic sentinel values make mapping control flow fragile. | Same-scale mappings now use `SameScale()` instead of `Symbol("")`; explicit validation rejects the old sentinel. | @@ -80,6 +78,5 @@ output preallocation, and the domain runtime. ## Suggested Cleanup Order -1. Replace `ModelList` as the single-scale backing path. -2. Split the domain runner into scheduler, routes, environment, graph-domain, - and publication modules. +All originally listed cleanup items are now marked done. Keep this page as the +release-note source list and add new rows only for newly discovered cleanup work. diff --git a/docs/src/working_with_data/inputs.md b/docs/src/working_with_data/inputs.md index 3c94c985b..d2540035b 100644 --- a/docs/src/working_with_data/inputs.md +++ b/docs/src/working_with_data/inputs.md @@ -30,7 +30,7 @@ models = ModelMapping( ) ``` -For single-scale mappings, `type_promotion` is applied while the backing status is constructed. It follows the same semantics as the deprecated [`ModelList`](@ref): model-provided default values are converted, while values explicitly passed in `status` keep the type chosen by the user. If those values should also be `Float32`, pass them as `Float32` values directly. +For single-scale mappings, `type_promotion` is applied while the backing status is constructed: model-provided default values are converted, while values explicitly passed in `status` keep the type chosen by the user. If those values should also be `Float32`, pass them as `Float32` values directly. For multiscale mappings, the per-node statuses do not exist when [`ModelMapping`](@ref) is constructed. The promotion map is stored on the mapping and applied when the MTG simulation is initialized: diff --git a/src/PlantSimEngine.jl b/src/PlantSimEngine.jl index d578e67b7..6a383ff70 100644 --- a/src/PlantSimEngine.jl +++ b/src/PlantSimEngine.jl @@ -51,8 +51,8 @@ include("component_models/TimeStepTable.jl") # Declaring the dependency graph include("dependencies/dependency_graph.jl") -# List of models: -include("component_models/ModelList.jl") # deprecated, to be removed in favor of ModelMapping +# Single-scale model container used internally by ModelMapping: +include("component_models/SingleScaleModelSet.jl") include("mtg/MultiScaleModel.jl") include("mtg/ModelSpec.jl") include("mtg/mapping/mapping.jl") @@ -131,7 +131,7 @@ export OutputCache, HoldLastCache, InterpolateCache, IntegrateCache, AggregateCa export TemporalState export OutputRequest, collect_outputs export effective_rate_summary -export ModelList, MultiScaleModel, ModelMapping, ModelSpec, SameScale, Updates, TimeStepModel, InputBindings, MeteoBindings, MeteoWindow, OutputRouting, ScopeModel +export MultiScaleModel, ModelMapping, ModelSpec, SameScale, Updates, TimeStepModel, InputBindings, MeteoBindings, MeteoWindow, OutputRouting, ScopeModel export resolved_model_specs, explain_model_specs export Domain, SimulationMapping, DomainSimulation, DomainModelKey, AllDomains, HardDomains export Route, DomainRouteTarget, RouteCardinality diff --git a/src/checks/dimensions.jl b/src/checks/dimensions.jl index 711f4e233..605e5ca1c 100644 --- a/src/checks/dimensions.jl +++ b/src/checks/dimensions.jl @@ -43,7 +43,7 @@ ERROR: DimensionMismatch: Component status has a vector variable : var1 implying check_dimensions(component, weather) = check_dimensions(DataFormat(weather), component, weather) # Here we add methods for applying to a component, an array or a dict of: -function check_dimensions(component::ModelList, w) +function check_dimensions(component::SingleScaleModelSet, w) check_dimensions(status(component), w) end @@ -52,14 +52,14 @@ function check_dimensions(component::ModelMapping{SingleScale}, w) end # for several components as an array -function check_dimensions(component::AbstractVector{<:ModelList}, weather) +function check_dimensions(component::AbstractVector{<:SingleScaleModelSet}, weather) for i in component check_dimensions(i, weather) end end # for several components as a Dict -function check_dimensions(component::AbstractDict{<:Any,<:ModelList}, weather) +function check_dimensions(component::AbstractDict{<:Any,<:SingleScaleModelSet}, weather) for (key, val) in component check_dimensions(val, weather) end diff --git a/src/component_models/ModelList.jl b/src/component_models/SingleScaleModelSet.jl similarity index 84% rename from src/component_models/ModelList.jl rename to src/component_models/SingleScaleModelSet.jl index aed069c90..7f400b65b 100644 --- a/src/component_models/ModelList.jl +++ b/src/component_models/SingleScaleModelSet.jl @@ -1,7 +1,7 @@ """ - ModelList(models::M, status::S) - ModelList(; + SingleScaleModelSet(models::M, status::S) + SingleScaleModelSet(; status=nothing, type_promotion=nothing, variables_check=true, @@ -36,7 +36,7 @@ the `status` field in the input, you'll have to implement a method for `add_mode adds the models variables to the type in case it is not fully initialized. The default method is compatible with any type that implements the `Tables.jl` interface (*e.g.* DataFrame), and `NamedTuples`. -Note that `ModelList`makes a copy of the input `status` if it does not list all needed variables. +Note that `SingleScaleModelSet`makes a copy of the input `status` if it does not list all needed variables. ## Examples @@ -44,54 +44,54 @@ We'll use the dummy models from the `dummy.jl` in the examples folder of the pac implements three dummy processes: `Process1Model`, `Process2Model` and `Process3Model`, with one model implementation each: `Process1Model`, `Process2Model` and `Process3Model`. -```jldoctest 1 +```julia julia> using PlantSimEngine; ``` Including example processes and models: -```jldoctest 1 +```julia julia> using PlantSimEngine.Examples; ``` -```jldoctest 1 -julia> models = ModelList(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model()); +```julia +julia> models = SingleScaleModelSet(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model()); [ Info: Some variables must be initialized before simulation: (process1 = (:var1, :var2), process2 = (:var1,)) (see `to_initialize()`) ``` -```jldoctest 1 +```julia julia> typeof(models) -ModelList{@NamedTuple{process1::Process1Model, process2::Process2Model, process3::Process3Model}, Status{(:var5, :var4, :var6, :var1, :var3, :var2), NTuple{6, Base.RefValue{Float64}}}} +SingleScaleModelSet{@NamedTuple{process1::Process1Model, process2::Process2Model, process3::Process3Model}, Status{(:var5, :var4, :var6, :var1, :var3, :var2), NTuple{6, Base.RefValue{Float64}}}} ``` -No variables were given as keyword arguments, that means that the status of the ModelList is not +No variables were given as keyword arguments, that means that the status of the SingleScaleModelSet is not set yet, and all variables are initialized to their default values given in the inputs and outputs (usually `typemin(Type)`, *i.e.* `-Inf` for floating point numbers). This component cannot be simulated yet. To know which variables we need to initialize for a simulation, we use [`to_initialize`](@ref): -```jldoctest 1 +```julia julia> to_initialize(models) (process1 = (:var1, :var2), process2 = (:var1,)) ``` We can now provide values for these variables in the `status` field. Direct -`run!(::ModelList, ...)` has been removed; wrap the models in a `ModelMapping` +`run!(::SingleScaleModelSet, ...)` has been removed; wrap the models in a `ModelMapping` before running: -```jldoctest 1 +```julia julia> mapping = ModelMapping(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0, var2=0.3)); ``` -```jldoctest 1 +```julia julia> meteo = Atmosphere(T = 22.0, Wind = 0.8333, P = 101.325, Rh = 0.4490995); ``` -```jldoctest 1 +```julia julia> outputs_sim = run!(mapping, meteo); ``` -```jldoctest 1 +```julia julia> outputs_sim[:var6] 1-element Vector{Float64}: 58.0138985 @@ -99,13 +99,13 @@ julia> outputs_sim[:var6] If we want to use special types for the variables, we can use the `type_promotion` argument: -```jldoctest 1 -julia> models = ModelList(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0, var2=0.3), type_promotion = Dict(Float64 => Float32)); +```julia +julia> models = SingleScaleModelSet(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0, var2=0.3), type_promotion = Dict(Float64 => Float32)); ``` We used `type_promotion` to force the status into Float32: -```jldoctest 1 +```julia julia> [typeof(models[i][1]) for i in keys(status(models))] 6-element Vector{DataType}: Float32 @@ -121,13 +121,13 @@ were converted to Float32, the two other variables that we gave were not convert because we want to give the ability to users to give any type for the variables they provide in the status. If we want all variables to be converted to Float32, we can pass them as Float32: -```jldoctest 1 -julia> models = ModelList(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0f0, var2=0.3f0), type_promotion = Dict(Float64 => Float32)); +```julia +julia> models = SingleScaleModelSet(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0f0, var2=0.3f0), type_promotion = Dict(Float64 => Float32)); ``` We used `type_promotion` to force the status into Float32: -```jldoctest 1 +```julia julia> [typeof(models[i][1]) for i in keys(status(models))] 6-element Vector{DataType}: Float32 @@ -138,19 +138,19 @@ julia> [typeof(models[i][1]) for i in keys(status(models))] Float32 ``` """ -struct ModelList{M<:NamedTuple,S} +struct SingleScaleModelSet{M<:NamedTuple,S} models::M status::S type_promotion::Union{Nothing,Dict} dependency_graph::DependencyGraph end -#=function ModelList(models::M, status::Status) where {M<:NamedTuple{names,T} where {names,T<:NTuple{N,<:AbstractModel} where {N}}} - ModelList(models, status) +#=function SingleScaleModelSet(models::M, status::Status) where {M<:NamedTuple{names,T} where {names,T<:NTuple{N,<:AbstractModel} where {N}}} + SingleScaleModelSet(models, status) end=# # General interface: -function ModelList( +function SingleScaleModelSet( args...; status=nothing, type_promotion::Union{Nothing,Dict}=nothing, @@ -180,7 +180,7 @@ function ModelList( ts_kwargs = homogeneous_ts_kwargs(status) ts_kwargs = add_model_vars(ts_kwargs, mods, type_promotion) - model_list = ModelList( + model_list = SingleScaleModelSet( mods, ts_kwargs, type_promotion, @@ -191,7 +191,7 @@ function ModelList( return model_list end -outputs(m::ModelList) = m.outputs +outputs(m::SingleScaleModelSet) = m.outputs parse_models(m) = NamedTuple([process(i) => i for i in m]) @@ -287,10 +287,10 @@ function homogeneous_ts_kwargs(kwargs::NamedTuple{N,T}) where {N,T} end """ - Base.copy(l::ModelList) - Base.copy(l::ModelList, status) + Base.copy(l::SingleScaleModelSet) + Base.copy(l::SingleScaleModelSet, status) -Copy a [`ModelList`](@ref), eventually with new values for the status. +Copy a [`SingleScaleModelSet`](@ref), eventually with new values for the status. # Examples @@ -301,7 +301,7 @@ using PlantSimEngine using PlantSimEngine.Examples; # Create a model list: -models = ModelList( +models = SingleScaleModelSet( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -315,8 +315,8 @@ ml2 = copy(models) ml3 = copy(models, TimeStepTable([Status(var1=20.0, var2=0.5))]) ``` """ -function Base.copy(m::T) where {T<:ModelList} - ModelList( +function Base.copy(m::T) where {T<:SingleScaleModelSet} + SingleScaleModelSet( m.models, deepcopy(m.status), deepcopy(m.type_promotion), @@ -324,8 +324,8 @@ function Base.copy(m::T) where {T<:ModelList} ) end -function Base.copy(m::T, status) where {T<:ModelList} - ModelList( +function Base.copy(m::T, status) where {T<:SingleScaleModelSet} + SingleScaleModelSet( m.models, status, deepcopy(m.type_promotion), @@ -334,20 +334,20 @@ function Base.copy(m::T, status) where {T<:ModelList} end """ - Base.copy(l::AbstractArray{<:ModelList}) + Base.copy(l::AbstractArray{<:SingleScaleModelSet}) -Copy an array-alike of [`ModelList`](@ref) +Copy an array-alike of [`SingleScaleModelSet`](@ref) """ -function Base.copy(l::T) where {T<:AbstractArray{<:ModelList}} +function Base.copy(l::T) where {T<:AbstractArray{<:SingleScaleModelSet}} return [copy(i) for i in l] end """ - Base.copy(l::AbstractDict{N,<:ModelList} where N) + Base.copy(l::AbstractDict{N,<:SingleScaleModelSet} where N) -Copy a Dict-alike [`ModelList`](@ref) +Copy a Dict-alike [`SingleScaleModelSet`](@ref) """ -function Base.copy(l::T) where {T<:AbstractDict{N,<:ModelList} where {N}} +function Base.copy(l::T) where {T<:AbstractDict{N,<:SingleScaleModelSet} where {N}} return Dict([k => v for (k, v) in l]) end @@ -460,13 +460,13 @@ function convert_vars!(mapped_vars::Dict{Symbol,Dict{Symbol,Any}}, type_promotio end end -function Base.show(io::IO, m::MIME"text/plain", t::ModelList) +function Base.show(io::IO, m::MIME"text/plain", t::SingleScaleModelSet) show(io, m, dep(t)) println(io, "") show(io, m, status(t)) end # Short form printing (e.g. inside another object) -function Base.show(io::IO, t::ModelList) - print(io, "ModelList", (; zip(keys(t.models), typeof.(values(t.models)))...)) +function Base.show(io::IO, t::SingleScaleModelSet) + print(io, "SingleScaleModelSet", (; zip(keys(t.models), typeof.(values(t.models)))...)) end diff --git a/src/component_models/Status.jl b/src/component_models/Status.jl index ae3673eff..bdff60078 100644 --- a/src/component_models/Status.jl +++ b/src/component_models/Status.jl @@ -3,7 +3,7 @@ Status type used to store the values of the variables during simulation. It is mainly used as the structure to store the variables in the `TimeStepRow` of a `TimeStepTable` (see -[`PlantMeteo.jl` docs](https://palmstudio.github.io/PlantMeteo.jl/stable/)) of a [`ModelList`](@ref). +[`PlantMeteo.jl` docs](https://palmstudio.github.io/PlantMeteo.jl/stable/)) of a [`SingleScaleModelSet`](@ref). Most of the code is taken from MasonProtter/MutableNamedTuples.jl, so `Status` is a MutableNamedTuples with a few modifications, so in essence, it is a stuct that stores a `NamedTuple` of the references to the values of the variables, which makes it mutable. diff --git a/src/component_models/StatusView.jl b/src/component_models/StatusView.jl index b51c4175f..ceb1319fb 100644 --- a/src/component_models/StatusView.jl +++ b/src/component_models/StatusView.jl @@ -5,7 +5,7 @@ An equivalent of the `Status` struct, but with views instead of Refs. Allows to other data structures present elsewhere, and that we want to update on mutation. Like the `Status`, `StatusView` is used to store the values of the variables during a simulation, mainly as the structure to store the variables -in the `TimeStepRow` of a `TimeStepTable` (see [`PlantMeteo.jl` docs](https://palmstudio.github.io/PlantMeteo.jl/stable/)) of a [`ModelList`](@ref). +in the `TimeStepRow` of a `TimeStepTable` (see [`PlantMeteo.jl` docs](https://palmstudio.github.io/PlantMeteo.jl/stable/)) of a [`SingleScaleModelSet`](@ref). # Examples diff --git a/src/component_models/TimeStepTable.jl b/src/component_models/TimeStepTable.jl index d02253533..a7ce79516 100644 --- a/src/component_models/TimeStepTable.jl +++ b/src/component_models/TimeStepTable.jl @@ -7,7 +7,7 @@ from a `DataFrame`, but with each row being a `Status`. # Note -[`ModelList`](@ref) uses `TimeStepTable{Status}` by default (see examples below). +[`SingleScaleModelSet`](@ref) uses `TimeStepTable{Status}` by default (see examples below). # Examples @@ -25,7 +25,7 @@ TimeStepTable{Status}(df) # A leaf with several values for at least one of its variable will automatically use # TimeStepTable{Status} with the time steps: -models = ModelList( +models = SingleScaleModelSet( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), diff --git a/src/component_models/get_status.jl b/src/component_models/get_status.jl index 55327614a..7b6bb0398 100644 --- a/src/component_models/get_status.jl +++ b/src/component_models/get_status.jl @@ -1,9 +1,9 @@ """ status(m) - status(m::AbstractArray{<:ModelList}) - status(m::AbstractDict{T,<:ModelList}) + status(m::AbstractArray{<:SingleScaleModelSet}) + status(m::AbstractDict{T,<:SingleScaleModelSet}) -Get a ModelList status, *i.e.* the state of the input (and output) variables. +Get a SingleScaleModelSet status, *i.e.* the state of the input (and output) variables. See also [`is_initialized`](@ref) and [`to_initialize`](@ref) @@ -15,7 +15,7 @@ using PlantSimEngine # Including example models and processes: using PlantSimEngine.Examples; -# Create a ModelList +# Create a SingleScaleModelSet models = ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), @@ -67,8 +67,8 @@ function status(m, key::T) where {T<:Integer} end """ - getindex(component<:ModelList, key::Symbol) - getindex(component<:ModelList, key) + getindex(component<:SingleScaleModelSet, key::Symbol) + getindex(component<:SingleScaleModelSet, key) Indexing a component models structure: - with an integer, will return the status at the ith time-step @@ -79,7 +79,7 @@ Indexing a component models structure: ```julia using PlantSimEngine -lm = ModelList( +lm = SingleScaleModelSet( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -95,10 +95,10 @@ lm[:var1][2] # Equivalent of the above 16.0 ``` """ -function Base.getindex(component::T, key) where {T<:ModelList} +function Base.getindex(component::T, key) where {T<:SingleScaleModelSet} status(component, key) end -function Base.setindex!(component::T, value, key) where {T<:ModelList} +function Base.setindex!(component::T, value, key) where {T<:SingleScaleModelSet} setproperty!(status(component), key, value) end diff --git a/src/dependencies/dependencies.jl b/src/dependencies/dependencies.jl index b5862613a..4ff1e2b12 100644 --- a/src/dependencies/dependencies.jl +++ b/src/dependencies/dependencies.jl @@ -82,11 +82,11 @@ function dep(nsteps=1; verbose::Bool=true, vars...) return deps end -function dep(m::ModelList) +function dep(m::SingleScaleModelSet) m.dependency_graph end -function dep!(m::ModelList, nsteps=1) +function dep!(m::SingleScaleModelSet, nsteps=1) traverse_dependency_graph!(m.dependency_graph; visit_hard_dep=false) do node if length(node.simulation_id) != nsteps node.simulation_id = fill(0, nsteps) diff --git a/src/dependencies/hard_dependencies.jl b/src/dependencies/hard_dependencies.jl index eeb49737a..ccb7342d2 100644 --- a/src/dependencies/hard_dependencies.jl +++ b/src/dependencies/hard_dependencies.jl @@ -61,7 +61,7 @@ function hard_dependencies(models; scale=nothing, verbose::Bool=true) push!(dep_not_found, p => (parent_process=process, type=first(depend), scales=target_scales)) continue else - # If we are not in a multi-scale setup e.g. in a ModelList, we shouldn't use a multiscale model. + # If we are not in a multi-scale setup, we shouldn't use a multiscale model. # But we still authorize it with a warning, and then proceed searching the dependency in this model list. verbose && @warn "Model $i has a multiscale hard dependency on $(first(depend)): $depend. Trying to find the model in this scale instead." depend = first(depend) @@ -100,7 +100,7 @@ function hard_dependencies(models; scale=nothing, verbose::Bool=true) "Model ", typeof(model_(i)).name.name, " from process ", process, isnothing(scale) ? "" : " at scale $scale", " needs a model that is a subtype of ", depend, " in process ", - p, ", but the process is not parameterized in the ModelList." + p, ", but the process is not parameterized in the ModelMapping." ) end push!(dep_not_found, p => depend) diff --git a/src/dependencies/soft_dependencies.jl b/src/dependencies/soft_dependencies.jl index 869e75c00..cf203e5eb 100644 --- a/src/dependencies/soft_dependencies.jl +++ b/src/dependencies/soft_dependencies.jl @@ -18,7 +18,7 @@ using PlantSimEngine using PlantSimEngine.Examples # Create a model list: -models = ModelList( +models = SingleScaleModelSet( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), diff --git a/src/domains/domain_run_loops.jl b/src/domains/domain_run_loops.jl new file mode 100644 index 000000000..bf08bcd02 --- /dev/null +++ b/src/domains/domain_run_loops.jl @@ -0,0 +1,89 @@ +function _run_single_status_domain_simulation!( + simulation::DomainSimulation, + constants, + nsteps::Int +) + run_order = _domain_run_order(simulation) + for i in 1:nsteps + for domain in run_order + _materialize_routes_for_domain!(simulation, domain, i) + _run_domain_models!(simulation, domain, constants, i) + _update_domain_environment_index!(simulation, domain) + end + for domain in run_order + domain.kind == :scene && continue + _domain_has_post_scene_work(simulation, domain) || continue + _materialize_routes_for_domain!(simulation, domain, i) + _run_domain_models!(simulation, domain, constants, i; phase=:post_scene) + _update_domain_environment_index!(simulation, domain) + end + end + return simulation +end + +function _run_staged_graph_domain_simulation!( + simulation::DomainSimulation, + object, + raw_meteo, + constants, + nsteps::Int; + check=true, + executor=SequentialEx(), + type_promotion=nothing, +) + run_order = _domain_run_order(simulation) + graph_runtimes = Dict{Symbol,DomainGraphRuntime}() + + for step in 1:nsteps + for scene_phase in (false, true) + for domain in run_order + (domain.kind == :scene) == scene_phase || continue + if _is_graph_domain(domain) + domain.kind == :scene && error( + "Scene domain `$(domain.name)` is MTG-backed. The MTG-domain runner currently supports ", + "single-status scene domains only." + ) + runtime = get(graph_runtimes, domain.name, nothing) + if isnothing(runtime) + domain_type_promotion = isnothing(type_promotion) ? _type_promotion(_domain_mapping(domain)) : type_promotion + runtime = _prepare_graph_domain_runtime!( + simulation, + domain, + object, + raw_meteo, + constants, + nsteps; + check=check, + executor=executor, + type_promotion=domain_type_promotion, + ) + graph_runtimes[domain.name] = runtime + end + _run_graph_domain_step!(simulation, domain, runtime, step, nsteps; check=check) + else + _materialize_routes_for_domain!(simulation, domain, step) + _run_domain_models!(simulation, domain, constants, step) + _update_domain_environment_index!(simulation, domain) + end + end + end + for domain in run_order + domain.kind == :scene && continue + _domain_has_post_scene_work(simulation, domain) || continue + if _is_graph_domain(domain) + runtime = graph_runtimes[domain.name] + _run_graph_domain_step!(simulation, domain, runtime, step, nsteps; check=check, phase=:post_scene) + else + _materialize_routes_for_domain!(simulation, domain, step) + _run_domain_models!(simulation, domain, constants, step; phase=:post_scene) + _update_domain_environment_index!(simulation, domain) + end + end + end + + for runtime in values(graph_runtimes) + _finalize_graph_domain_runtime!(runtime) + end + + return simulation +end diff --git a/src/domains/domain_scheduler.jl b/src/domains/domain_scheduler.jl new file mode 100644 index 000000000..3bdff2680 --- /dev/null +++ b/src/domains/domain_scheduler.jl @@ -0,0 +1,167 @@ +function _domain_order_edges(mapping::SimulationMapping, route_bindings=nothing) + edges = Dict(domain.name => Set{Symbol}() for domain in mapping.domains) + non_scene_domains = [domain.name for domain in mapping.domains if domain.kind != :scene] + scene_domains = [domain.name for domain in mapping.domains if domain.kind == :scene] + for source in non_scene_domains, target in scene_domains + source == target || push!(edges[source], target) + end + + if !isnothing(route_bindings) + for (i, route) in enumerate(mapping.routes) + target = route.to.domain + for producer in route_bindings[i] + producer.domain == target && continue + push!(edges[producer.domain], target) + end + end + end + + return edges +end + +function _domain_run_order(mapping::SimulationMapping, route_bindings=nothing) + domains_by_name = Dict(domain.name => domain for domain in mapping.domains) + declaration_index = Dict(domain.name => i for (i, domain) in enumerate(mapping.domains)) + edges = _domain_order_edges(mapping, route_bindings) + indegree = Dict(domain.name => 0 for domain in mapping.domains) + for targets in values(edges), target in targets + indegree[target] = get(indegree, target, 0) + 1 + end + + ready = sort!( + [name for (name, degree) in indegree if degree == 0]; + by=name -> declaration_index[name], + ) + ordered = Symbol[] + while !isempty(ready) + current = popfirst!(ready) + push!(ordered, current) + for target in sort!(collect(edges[current]); by=name -> declaration_index[name]) + indegree[target] -= 1 + if indegree[target] == 0 + push!(ready, target) + sort!(ready; by=name -> declaration_index[name]) + end + end + end + + if length(ordered) != length(mapping.domains) + cyclic = sort!( + [name for (name, degree) in indegree if degree > 0]; + by=name -> declaration_index[name], + ) + error( + "Cyclic domain run-order constraints detected among domains: ", + join((":" * string(name) for name in cyclic), ", "), + ". Check route sources/targets and `kind=:scene` phase constraints." + ) + end + + return [domains_by_name[name] for name in ordered] +end + +function _domain_run_order(simulation::DomainSimulation) + return _domain_run_order(simulation.mapping, simulation.route_bindings) +end + +function _domain_node_due(simulation::DomainSimulation, domain::Domain, node::SoftDependencyNode, step::Int) + key = DomainModelKey(domain.name, node.scale, node.process) + clock = simulation.model_clocks[key] + return _should_run_at_time(clock, float(step)) +end + +function _hard_domain_dependency_keys(simulation::DomainSimulation) + keys = Set{DomainModelKey}() + for producers in values(simulation.hard_domain_dependency_bindings) + union!(keys, producers) + end + return keys +end + +function _is_hard_domain_dependency(simulation::DomainSimulation, key::DomainModelKey) + key in _hard_domain_dependency_keys(simulation) +end + +function _has_hard_domain_parent(simulation::DomainSimulation, domain::Domain, node::SoftDependencyNode) + AbstractTrees.isroot(node) && return false + hard_keys = _hard_domain_dependency_keys(simulation) + for parent in node.parent + parent_key = DomainModelKey(domain.name, parent.scale, parent.process) + parent_key in hard_keys && return true + end + return false +end + +function _phase_allows_hard_parent(phase::Symbol, has_hard_parent::Bool) + phase == :normal && return !has_hard_parent + phase == :post_scene && return has_hard_parent + error("Unknown domain scheduling phase `$(phase)`.") +end + +function _should_visit_domain_node( + simulation::DomainSimulation, + domain::Domain, + node::SoftDependencyNode; + phase::Symbol, +) + key = DomainModelKey(domain.name, node.scale, node.process) + _is_hard_domain_dependency(simulation, key) && return false + has_hard_parent = _has_hard_domain_parent(simulation, domain, node) + return _phase_allows_hard_parent(phase, has_hard_parent) +end + +function _has_hard_domain_parent(simulation::DomainSimulation, domain::Domain, key::DomainModelKey) + node = try + _find_dependency_node(simulation.dependency_graphs[domain.name], key) + catch + return false + end + return _has_hard_domain_parent(simulation, domain, node) +end + +function _has_domain_soft_node(simulation::DomainSimulation, domain::Domain, key::DomainModelKey) + try + _find_dependency_node(simulation.dependency_graphs[domain.name], key) + return true + catch + return false + end +end + +function _should_publish_domain_key( + simulation::DomainSimulation, + domain::Domain, + key::DomainModelKey; + phase::Symbol, +) + _has_domain_soft_node(simulation, domain, key) || return false + _is_hard_domain_dependency(simulation, key) && return false + has_hard_parent = _has_hard_domain_parent(simulation, domain, key) + return _phase_allows_hard_parent(phase, has_hard_parent) +end + +function _domain_has_post_scene_work(simulation::DomainSimulation, domain::Domain) + for key in keys(simulation.model_specs) + key.domain == domain.name || continue + _is_hard_domain_dependency(simulation, key) && continue + _has_hard_domain_parent(simulation, domain, key) && return true + end + return false +end + +function _domain_parents_ready( + simulation::DomainSimulation, + domain::Domain, + node::SoftDependencyNode, + step::Int, + ran::Set{DomainModelKey} +) + AbstractTrees.isroot(node) && return true + for parent in node.parent + _domain_node_due(simulation, domain, parent, step) || continue + parent_key = DomainModelKey(domain.name, parent.scale, parent.process) + _is_hard_domain_dependency(simulation, parent_key) && continue + parent_key in ran || return false + end + return true +end diff --git a/src/domains/domain_simulation.jl b/src/domains/domain_simulation.jl index b3d5e33a3..5bf80fc66 100644 --- a/src/domains/domain_simulation.jl +++ b/src/domains/domain_simulation.jl @@ -640,6 +640,11 @@ function _keys_by_domain(specs::Dict{DomainModelKey,ModelSpec}) end include("route_runtime.jl") +include("environment_bridge.jl") +include("output_publisher.jl") +include("domain_scheduler.jl") +include("graph_domain_runner.jl") +include("domain_run_loops.jl") function _build_domain_simulation(mapping::SimulationMapping, meteo; staged_graph_domains=false) environment = environment_backend(meteo) @@ -690,167 +695,6 @@ function _domain_model_specs_by_scale(specs::Dict{DomainModelKey,ModelSpec}) return by_scale end -function _domain_order_edges(mapping::SimulationMapping, route_bindings=nothing) - edges = Dict(domain.name => Set{Symbol}() for domain in mapping.domains) - non_scene_domains = [domain.name for domain in mapping.domains if domain.kind != :scene] - scene_domains = [domain.name for domain in mapping.domains if domain.kind == :scene] - for source in non_scene_domains, target in scene_domains - source == target || push!(edges[source], target) - end - - if !isnothing(route_bindings) - for (i, route) in enumerate(mapping.routes) - target = route.to.domain - for producer in route_bindings[i] - producer.domain == target && continue - push!(edges[producer.domain], target) - end - end - end - - return edges -end - -function _domain_run_order(mapping::SimulationMapping, route_bindings=nothing) - domains_by_name = Dict(domain.name => domain for domain in mapping.domains) - declaration_index = Dict(domain.name => i for (i, domain) in enumerate(mapping.domains)) - edges = _domain_order_edges(mapping, route_bindings) - indegree = Dict(domain.name => 0 for domain in mapping.domains) - for targets in values(edges), target in targets - indegree[target] = get(indegree, target, 0) + 1 - end - - ready = sort!( - [name for (name, degree) in indegree if degree == 0]; - by=name -> declaration_index[name], - ) - ordered = Symbol[] - while !isempty(ready) - current = popfirst!(ready) - push!(ordered, current) - for target in sort!(collect(edges[current]); by=name -> declaration_index[name]) - indegree[target] -= 1 - if indegree[target] == 0 - push!(ready, target) - sort!(ready; by=name -> declaration_index[name]) - end - end - end - - if length(ordered) != length(mapping.domains) - cyclic = sort!( - [name for (name, degree) in indegree if degree > 0]; - by=name -> declaration_index[name], - ) - error( - "Cyclic domain run-order constraints detected among domains: ", - join((":" * string(name) for name in cyclic), ", "), - ". Check route sources/targets and `kind=:scene` phase constraints." - ) - end - - return [domains_by_name[name] for name in ordered] -end - -function _domain_run_order(simulation::DomainSimulation) - return _domain_run_order(simulation.mapping, simulation.route_bindings) -end - -function _publish_domain_model_outputs!( - simulation::DomainSimulation, - domain::Domain, - node::AbstractDependencyNode, - status, - step::Int -) - key = DomainModelKey(domain.name, node.scale, node.process) - haskey(simulation.model_specs, key) || return nothing - spec = simulation.model_specs[key] - for out_var in keys(outputs_(spec)) - stream_key = (key, out_var) - value = status[out_var] - push!(get!(simulation.streams, stream_key, Pair{Int,Any}[]), step => value) - push!(get!(simulation.outputs, stream_key, Any[]), value) - end - return nothing -end - -function _publish_domain_hard_dependency_outputs!( - simulation::DomainSimulation, - domain::Domain, - node::HardDependencyNode, - status, - step::Int -) - _publish_domain_model_outputs!(simulation, domain, node, status, step) - for child in node.children - _publish_domain_hard_dependency_outputs!(simulation, domain, child, status, step) - end - return nothing -end - -function _publish_graph_domain_step_outputs!( - simulation::DomainSimulation, - domain::Domain, - graph_state::DomainGraphState, - step::Int; - effective_multirate::Bool=false, - phase::Symbol=:normal, -) - graph_statuses = status(graph_state) - for (key, spec) in simulation.model_specs - key.domain == domain.name || continue - _should_publish_domain_key(simulation, domain, key; phase=phase) || continue - if effective_multirate - clock = simulation.model_clocks[key] - _should_run_at_time(clock, float(step)) || continue - end - haskey(graph_statuses, key.scale) || continue - for out_var in keys(outputs_(spec)) - stream_key = (key, out_var) - ids = Int[] - values = Any[] - for st in graph_statuses[key.scale] - out_var in propertynames(st) || continue - push!(ids, node_id(st.node)) - push!(values, st[out_var]) - end - push!(get!(simulation.streams, stream_key, Pair{Int,Any}[]), step => DomainNodeValues(ids, values)) - push!(get!(simulation.outputs, stream_key, Any[]), values) - end - end - return nothing -end - -function _domain_environment_entities(simulation::DomainSimulation, domain::Domain) - state = get(simulation.domain_states, domain.name, nothing) - entities = NamedTuple[] - if state isa DomainGraphState - for (scale, statuses_at_scale) in status(state) - push!(entities, ( - domain=domain.name, - kind=domain.kind, - scale=scale, - statuses=statuses_at_scale, - state=state, - )) - end - elseif state isa ModelMapping{SingleScale} - push!(entities, ( - domain=domain.name, - kind=domain.kind, - scale=:Default, - statuses=Status[status(state)], - state=state, - )) - end - return entities -end - -function _update_domain_environment_index!(simulation::DomainSimulation, domain::Domain) - return update_index!(simulation.environment, _domain_environment_entities(simulation, domain)) -end - function _resolve_stream_values(simulation::DomainSimulation, producer::DomainModelKey, var::Symbol, steps) stream = get(simulation.streams, (producer, var), Pair{Int,Any}[]) vals = Any[] @@ -1001,31 +845,10 @@ function _find_dependency_node(graph::DependencyGraph, key::DomainModelKey) error("No dependency node found for domain model `$(key)`.") end -_domain_environment_support(domain::Domain, node::AbstractDependencyNode, status) = - EnvironmentSupport(domain.name, node.scale, node.process, status) - -_domain_environment_support(key::DomainModelKey, status) = - EnvironmentSupport(key.domain, key.scale, key.process, status) - -function _sample_domain_environment_at_time(simulation::DomainSimulation, support::EnvironmentSupport, t, model_spec::ModelSpec) - return sample_environment(simulation.environment, support, t, model_spec) -end - -function _sample_domain_environment_at_step(simulation::DomainSimulation, support::EnvironmentSupport, step::Int, model_spec::ModelSpec) - t = _time_from_step(step, simulation.timeline) - return _sample_domain_environment_at_time(simulation, support, t, model_spec) -end - -function _dependency_target_meteo(simulation::DomainSimulation, key::DomainModelKey, st, step::Int) - spec = simulation.model_specs[key] - support = _domain_environment_support(key, st) - return _sample_domain_environment_at_step(simulation, support, step, spec) -end - function _single_status_dependency_targets(ctx::DomainRunContext, producer::DomainModelKey) sim = ctx.simulation domain = _domain_for_name(sim.mapping, producer.domain) - model_list = _modellist_from_model_mapping(_domain_mapping(domain)) + model_list = _single_scale_model_set_from_mapping(_domain_mapping(domain)) node = _find_dependency_node(sim.dependency_graphs[producer.domain], producer) st = status(model_list) producer_context = DomainRunContext(sim, producer, ctx.step, sim.model_clocks[producer], ctx.constants) @@ -1107,45 +930,6 @@ end dependency_targets(ctx::DomainRunContext, dependency_name::AbstractString) = dependency_targets(ctx, Symbol(dependency_name)) -function _publish_graph_target!(target::ModelTarget) - spec = target.simulation.model_specs[target.key] - domain = _domain_for_name(target.simulation.mapping, target.key.domain) - t = _time_from_step(target.step, target.simulation.timeline) - _scatter_graph_domain_environment_outputs!(target.simulation, domain, target.node, spec, target.status, t) - for hard_child in target.node.hard_dependency - _scatter_graph_domain_hard_dependency_environment_outputs!(target.simulation, domain, hard_child, target.status, t) - end - for out_var in keys(outputs_(spec)) - out_var in propertynames(target.status) || continue - stream_key = (target.key, out_var) - value = target.status[out_var] - push!(get!(target.simulation.streams, stream_key, Pair{Int,Any}[]), target.step => value) - push!(get!(target.simulation.outputs, stream_key, Any[]), value) - end - for hard_child in target.node.hard_dependency - _publish_domain_hard_dependency_outputs!(target.simulation, domain, hard_child, target.status, target.step) - end - return nothing -end - -function _publish_target!(target::ModelTarget) - isnothing(target.simulation) && error( - "`publish=true` requires a target created by `dependency_targets(extra, name)` in a domain simulation." - ) - target.extra isa GraphSimulation && return _publish_graph_target!(target) - domain = _domain_for_name(target.simulation.mapping, target.key.domain) - spec = target.simulation.model_specs[target.key] - _scatter_domain_environment_outputs!(target.simulation, domain, target.node, spec, target.status, target.step) - for hard_child in target.node.hard_dependency - _scatter_domain_hard_dependency_environment_outputs!(target.simulation, domain, hard_child, target.status, target.step) - end - _publish_domain_model_outputs!(target.simulation, domain, target.node, target.status, target.step) - for hard_child in target.node.hard_dependency - _publish_domain_hard_dependency_outputs!(target.simulation, domain, hard_child, target.status, target.step) - end - return nothing -end - """ run_target!(target; meteo=target.meteo, constants=target.constants, extra=target.extra, publish=false) @@ -1186,219 +970,11 @@ function _domain_context_for(simulation::DomainSimulation, domain::Domain, node: return DomainRunContext(simulation, key, step, simulation.model_clocks[key], constants) end -function _domain_environment_for_model( - simulation::DomainSimulation, - domain::Domain, - node::SoftDependencyNode, - model_spec::ModelSpec, - status, - step::Int -) - support = _domain_environment_support(domain, node, status) - return _sample_domain_environment_at_step(simulation, support, step, model_spec) -end - -function _scatter_domain_environment_outputs_at_time!( - simulation::DomainSimulation, - domain::Domain, - node::AbstractDependencyNode, - model_spec::ModelSpec, - status, - t -) - isempty(keys(meteo_outputs_(model_spec))) && return nothing - support = _domain_environment_support(domain, node, status) - return scatter_environment_outputs!(simulation.environment, support, t, model_spec, status) -end - -function _scatter_domain_environment_outputs!( - simulation::DomainSimulation, - domain::Domain, - node::AbstractDependencyNode, - model_spec::ModelSpec, - status, - step::Int -) - t = _time_from_step(step, simulation.timeline) - return _scatter_domain_environment_outputs_at_time!(simulation, domain, node, model_spec, status, t) -end - -function _scatter_domain_hard_dependency_environment_outputs_at_time!( - simulation::DomainSimulation, - domain::Domain, - node::HardDependencyNode, - status, - t -) - key = DomainModelKey(domain.name, node.scale, node.process) - if haskey(simulation.model_specs, key) - spec = simulation.model_specs[key] - _scatter_domain_environment_outputs_at_time!(simulation, domain, node, spec, status, t) - end - for child in node.children - _scatter_domain_hard_dependency_environment_outputs_at_time!(simulation, domain, child, status, t) - end - return nothing -end - -function _scatter_domain_hard_dependency_environment_outputs!( - simulation::DomainSimulation, - domain::Domain, - node::HardDependencyNode, - status, - step::Int -) - t = _time_from_step(step, simulation.timeline) - return _scatter_domain_hard_dependency_environment_outputs_at_time!(simulation, domain, node, status, t) -end - -function _graph_domain_environment_for_model( - simulation::DomainSimulation, - domain::Domain, - node::SoftDependencyNode, - status, - t, - model_clock::ClockSpec, - model_spec::ModelSpec, - meteo, - meteo_sampler, - multirate::Bool -) - if simulation.environment isa GlobalConstant - return multirate ? _sample_meteo_for_model(meteo_sampler, meteo, round(Int, t), model_clock, model_spec) : meteo - end - support = _domain_environment_support(domain, node, status) - return _sample_domain_environment_at_time(simulation, support, t, model_spec) -end - -function _scatter_graph_domain_environment_outputs!( - simulation::DomainSimulation, - domain::Domain, - node::AbstractDependencyNode, - model_spec::ModelSpec, - status, - t -) - return _scatter_domain_environment_outputs_at_time!(simulation, domain, node, model_spec, status, t) -end - -function _scatter_graph_domain_hard_dependency_environment_outputs!( - simulation::DomainSimulation, - domain::Domain, - node::HardDependencyNode, - status, - t -) - return _scatter_domain_hard_dependency_environment_outputs_at_time!(simulation, domain, node, status, t) -end - -function _domain_node_due(simulation::DomainSimulation, domain::Domain, node::SoftDependencyNode, step::Int) - key = DomainModelKey(domain.name, node.scale, node.process) - clock = simulation.model_clocks[key] - return _should_run_at_time(clock, float(step)) -end - -function _hard_domain_dependency_keys(simulation::DomainSimulation) - keys = Set{DomainModelKey}() - for producers in values(simulation.hard_domain_dependency_bindings) - union!(keys, producers) - end - return keys -end - -function _is_hard_domain_dependency(simulation::DomainSimulation, key::DomainModelKey) - key in _hard_domain_dependency_keys(simulation) -end - -function _has_hard_domain_parent(simulation::DomainSimulation, domain::Domain, node::SoftDependencyNode) - AbstractTrees.isroot(node) && return false - hard_keys = _hard_domain_dependency_keys(simulation) - for parent in node.parent - parent_key = DomainModelKey(domain.name, parent.scale, parent.process) - parent_key in hard_keys && return true - end - return false -end - -function _phase_allows_hard_parent(phase::Symbol, has_hard_parent::Bool) - phase == :normal && return !has_hard_parent - phase == :post_scene && return has_hard_parent - error("Unknown domain scheduling phase `$(phase)`.") -end - -function _should_visit_domain_node( - simulation::DomainSimulation, - domain::Domain, - node::SoftDependencyNode; - phase::Symbol, -) - key = DomainModelKey(domain.name, node.scale, node.process) - _is_hard_domain_dependency(simulation, key) && return false - has_hard_parent = _has_hard_domain_parent(simulation, domain, node) - return _phase_allows_hard_parent(phase, has_hard_parent) -end - -function _has_hard_domain_parent(simulation::DomainSimulation, domain::Domain, key::DomainModelKey) - node = try - _find_dependency_node(simulation.dependency_graphs[domain.name], key) - catch - return false - end - return _has_hard_domain_parent(simulation, domain, node) -end - -function _has_domain_soft_node(simulation::DomainSimulation, domain::Domain, key::DomainModelKey) - try - _find_dependency_node(simulation.dependency_graphs[domain.name], key) - return true - catch - return false - end -end - -function _should_publish_domain_key( - simulation::DomainSimulation, - domain::Domain, - key::DomainModelKey; - phase::Symbol, -) - _has_domain_soft_node(simulation, domain, key) || return false - _is_hard_domain_dependency(simulation, key) && return false - has_hard_parent = _has_hard_domain_parent(simulation, domain, key) - return _phase_allows_hard_parent(phase, has_hard_parent) -end - -function _domain_has_post_scene_work(simulation::DomainSimulation, domain::Domain) - for key in keys(simulation.model_specs) - key.domain == domain.name || continue - _is_hard_domain_dependency(simulation, key) && continue - _has_hard_domain_parent(simulation, domain, key) && return true - end - return false -end - -function _domain_parents_ready( - simulation::DomainSimulation, - domain::Domain, - node::SoftDependencyNode, - step::Int, - ran::Set{DomainModelKey} -) - AbstractTrees.isroot(node) && return true - for parent in node.parent - _domain_node_due(simulation, domain, parent, step) || continue - parent_key = DomainModelKey(domain.name, parent.scale, parent.process) - _is_hard_domain_dependency(simulation, parent_key) && continue - parent_key in ran || return false - end - return true -end - function _run_domain_node!( simulation::DomainSimulation, domain::Domain, node::SoftDependencyNode, - model_list::ModelList, + model_list::SingleScaleModelSet, constants, step::Int, ran::Set{DomainModelKey}; @@ -1443,7 +1019,7 @@ function _run_domain_models!( phase::Symbol=:normal, ) mapping = _domain_mapping(domain) - model_list = _modellist_from_model_mapping(mapping) + model_list = _single_scale_model_set_from_mapping(mapping) ran = Set{DomainModelKey}() for (_, root) in simulation.dependency_graphs[domain.name].roots _run_domain_node!(simulation, domain, root, model_list, constants, step, ran; phase=phase) @@ -1467,186 +1043,7 @@ function run!( ) simulation = _build_domain_simulation(mapping, meteo) nsteps = get_nsteps(simulation.environment) - run_order = _domain_run_order(simulation) - for i in 1:nsteps - for domain in run_order - _materialize_routes_for_domain!(simulation, domain, i) - _run_domain_models!(simulation, domain, constants, i) - _update_domain_environment_index!(simulation, domain) - end - for domain in run_order - domain.kind == :scene && continue - _domain_has_post_scene_work(simulation, domain) || continue - _materialize_routes_for_domain!(simulation, domain, i) - _run_domain_models!(simulation, domain, constants, i; phase=:post_scene) - _update_domain_environment_index!(simulation, domain) - end - end - return simulation -end - -function _raw_meteo_for_staged_graph_domains(environment::GlobalConstant) - return environment_meteo(environment) -end - -function _raw_meteo_for_staged_graph_domains(environment::AbstractEnvironmentBackend) - return environment -end - -function _run_single_status_domain_all_steps!( - simulation::DomainSimulation, - domain::Domain, - constants, - nsteps::Int -) - for step in 1:nsteps - _run_domain_models!(simulation, domain, constants, step) - end - return nothing -end - -function _prepare_graph_domain_runtime!( - simulation::DomainSimulation, - domain::Domain, - object, - meteo, - constants, - nsteps::Int; - check=true, - executor=SequentialEx(), - type_promotion=_type_promotion(_domain_mapping(domain)), -) - roots = _domain_graph_roots(object, domain) - graph_simulations = GraphSimulation[] - for root in roots - _materialize_graph_route_attributes_for_domain!(simulation, domain, root, 1) - push!( - graph_simulations, - GraphSimulation( - root, - _domain_mapping(domain); - nsteps=nsteps, - check=check, - outputs=nothing, - type_promotion=type_promotion, - ), - ) - end - graph_state = DomainGraphState(graph_simulations) - simulation.domain_states[domain.name] = graph_state - representative_simulation = first(graph_simulations) - effective_multirate = _effective_multirate(representative_simulation) - dep_graph = dep(representative_simulation) - timeline = _timeline_context(meteo) - meteo_sampler = effective_multirate ? _prepare_meteo_sampler(meteo) : nothing - runtime_clock_rows = _runtime_clock_rows(representative_simulation, timeline, dep_graph) - effective_executor = executor - validate_meteo_inputs(get_model_specs(representative_simulation), meteo) - _validate_meteo_derived_timestep_requirements!(runtime_clock_rows, timeline) - if effective_multirate - if executor != SequentialEx() - @warn string( - "Multi-rate MTG domain `$(domain.name)` currently executes sequentially. ", - "Provided `executor=$(executor)` is ignored in this mode. ", - "Use `executor=SequentialEx()` to silence this warning." - ) maxlog = 1 - effective_executor = SequentialEx() - end - _warn_if_no_model_runs_at_base_timestep(runtime_clock_rows, timeline) - for graph_simulation in graph_simulations - validate_canonical_publishers(graph_simulation) - configure_temporal_buffers!(graph_simulation, timeline) - end - end - return DomainGraphRuntime(graph_state, meteo, constants, effective_multirate, timeline, meteo_sampler, effective_executor) -end - -function _meteo_for_graph_step(meteo, step::Int, nsteps::Int) - return _meteo_row_at_step(meteo, step) -end - -function _meteo_for_graph_step(backend::AbstractEnvironmentBackend, step::Int, nsteps::Int) - return backend -end - -function _run_graph_domain_step!( - simulation::DomainSimulation, - domain::Domain, - runtime::DomainGraphRuntime, - step::Int, - nsteps::Int; - check=true, - phase::Symbol=:normal, -) - meteo_i = _meteo_for_graph_step(runtime.meteo, step, nsteps) - meteo_provider = (node, status, i, t, model_clock, model_spec, meteo, meteo_sampler, multirate) -> - _graph_domain_environment_for_model( - simulation, - domain, - node, - status, - t, - model_clock, - model_spec, - meteo, - meteo_sampler, - multirate, - ) - after_model_run = (node, model_spec, status, i, t) -> begin - _scatter_graph_domain_environment_outputs!(simulation, domain, node, model_spec, status, t) - for hard_child in node.hard_dependency - _scatter_graph_domain_hard_dependency_environment_outputs!(simulation, domain, hard_child, status, t) - end - nothing - end - skip_model_run = node -> !_should_visit_domain_node(simulation, domain, node; phase=phase) - for graph_simulation in runtime.state.simulations - _materialize_graph_routes_for_domain!(simulation, domain, graph_simulation, step) - roots = collect(dep(graph_simulation).roots) - models = get_models(graph_simulation) - for (_, dependency_node) in roots - run_node_multiscale!( - graph_simulation, - dependency_node, - step, - models, - meteo_i, - runtime.constants, - graph_simulation, - check, - runtime.executor, - runtime.effective_multirate, - runtime.timeline, - runtime.meteo_sampler; - meteo_provider=meteo_provider, - after_model_run=after_model_run, - skip_model_run=skip_model_run, - ) - end - if phase == :normal - runtime.effective_multirate && update_requested_outputs!(graph_simulation, _time_from_step(step, runtime.timeline)) - save_results!(graph_simulation, step) - end - end - _publish_graph_domain_step_outputs!( - simulation, - domain, - runtime.state, - step; - effective_multirate=runtime.effective_multirate, - phase=phase, - ) - _update_domain_environment_index!(simulation, domain) - return runtime.state -end - -function _finalize_graph_domain_runtime!(runtime::DomainGraphRuntime) - for graph_simulation in runtime.state.simulations - for (organ, index) in graph_simulation.outputs_index - resize!(outputs(graph_simulation)[organ], index - 1) - end - end - return runtime.state + return _run_single_status_domain_simulation!(simulation, constants, nsteps) end """ @@ -1674,61 +1071,16 @@ function run!( simulation = _build_domain_simulation(mapping, meteo; staged_graph_domains=true) raw_meteo = _raw_meteo_for_staged_graph_domains(simulation.environment) isnothing(nsteps) && (nsteps = get_nsteps(simulation.environment)) - run_order = _domain_run_order(simulation) - graph_runtimes = Dict{Symbol,DomainGraphRuntime}() - - for step in 1:nsteps - for scene_phase in (false, true) - for domain in run_order - (domain.kind == :scene) == scene_phase || continue - if _is_graph_domain(domain) - domain.kind == :scene && error( - "Scene domain `$(domain.name)` is MTG-backed. The MTG-domain runner currently supports ", - "single-status scene domains only." - ) - runtime = get(graph_runtimes, domain.name, nothing) - if isnothing(runtime) - domain_type_promotion = isnothing(type_promotion) ? _type_promotion(_domain_mapping(domain)) : type_promotion - runtime = _prepare_graph_domain_runtime!( - simulation, - domain, - object, - raw_meteo, - constants, - nsteps; - check=check, - executor=executor, - type_promotion=domain_type_promotion, - ) - graph_runtimes[domain.name] = runtime - end - _run_graph_domain_step!(simulation, domain, runtime, step, nsteps; check=check) - else - _materialize_routes_for_domain!(simulation, domain, step) - _run_domain_models!(simulation, domain, constants, step) - _update_domain_environment_index!(simulation, domain) - end - end - end - for domain in run_order - domain.kind == :scene && continue - _domain_has_post_scene_work(simulation, domain) || continue - if _is_graph_domain(domain) - runtime = graph_runtimes[domain.name] - _run_graph_domain_step!(simulation, domain, runtime, step, nsteps; check=check, phase=:post_scene) - else - _materialize_routes_for_domain!(simulation, domain, step) - _run_domain_models!(simulation, domain, constants, step; phase=:post_scene) - _update_domain_environment_index!(simulation, domain) - end - end - end - - for runtime in values(graph_runtimes) - _finalize_graph_domain_runtime!(runtime) - end - - return simulation + return _run_staged_graph_domain_simulation!( + simulation, + object, + raw_meteo, + constants, + nsteps; + check=check, + executor=executor, + type_promotion=type_promotion, + ) end """ diff --git a/src/domains/environment_bridge.jl b/src/domains/environment_bridge.jl new file mode 100644 index 000000000..d68d9baef --- /dev/null +++ b/src/domains/environment_bridge.jl @@ -0,0 +1,171 @@ +function _domain_environment_entities(simulation::DomainSimulation, domain::Domain) + state = get(simulation.domain_states, domain.name, nothing) + entities = NamedTuple[] + if state isa DomainGraphState + for (scale, statuses_at_scale) in status(state) + push!(entities, ( + domain=domain.name, + kind=domain.kind, + scale=scale, + statuses=statuses_at_scale, + state=state, + )) + end + elseif state isa ModelMapping{SingleScale} + push!(entities, ( + domain=domain.name, + kind=domain.kind, + scale=:Default, + statuses=Status[status(state)], + state=state, + )) + end + return entities +end + +function _update_domain_environment_index!(simulation::DomainSimulation, domain::Domain) + return update_index!(simulation.environment, _domain_environment_entities(simulation, domain)) +end + +_domain_environment_support(domain::Domain, node::AbstractDependencyNode, status) = + EnvironmentSupport(domain.name, node.scale, node.process, status) + +_domain_environment_support(key::DomainModelKey, status) = + EnvironmentSupport(key.domain, key.scale, key.process, status) + +function _sample_domain_environment_at_time(simulation::DomainSimulation, support::EnvironmentSupport, t, model_spec::ModelSpec) + return sample_environment(simulation.environment, support, t, model_spec) +end + +function _sample_domain_environment_at_step(simulation::DomainSimulation, support::EnvironmentSupport, step::Int, model_spec::ModelSpec) + t = _time_from_step(step, simulation.timeline) + return _sample_domain_environment_at_time(simulation, support, t, model_spec) +end + +function _dependency_target_meteo(simulation::DomainSimulation, key::DomainModelKey, st, step::Int) + spec = simulation.model_specs[key] + support = _domain_environment_support(key, st) + return _sample_domain_environment_at_step(simulation, support, step, spec) +end + +function _domain_environment_for_model( + simulation::DomainSimulation, + domain::Domain, + node::SoftDependencyNode, + model_spec::ModelSpec, + status, + step::Int +) + support = _domain_environment_support(domain, node, status) + return _sample_domain_environment_at_step(simulation, support, step, model_spec) +end + +function _scatter_domain_environment_outputs_at_time!( + simulation::DomainSimulation, + domain::Domain, + node::AbstractDependencyNode, + model_spec::ModelSpec, + status, + t +) + isempty(keys(meteo_outputs_(model_spec))) && return nothing + support = _domain_environment_support(domain, node, status) + return scatter_environment_outputs!(simulation.environment, support, t, model_spec, status) +end + +function _scatter_domain_environment_outputs!( + simulation::DomainSimulation, + domain::Domain, + node::AbstractDependencyNode, + model_spec::ModelSpec, + status, + step::Int +) + t = _time_from_step(step, simulation.timeline) + return _scatter_domain_environment_outputs_at_time!(simulation, domain, node, model_spec, status, t) +end + +function _scatter_domain_hard_dependency_environment_outputs_at_time!( + simulation::DomainSimulation, + domain::Domain, + node::HardDependencyNode, + status, + t +) + key = DomainModelKey(domain.name, node.scale, node.process) + if haskey(simulation.model_specs, key) + spec = simulation.model_specs[key] + _scatter_domain_environment_outputs_at_time!(simulation, domain, node, spec, status, t) + end + for child in node.children + _scatter_domain_hard_dependency_environment_outputs_at_time!(simulation, domain, child, status, t) + end + return nothing +end + +function _scatter_domain_hard_dependency_environment_outputs!( + simulation::DomainSimulation, + domain::Domain, + node::HardDependencyNode, + status, + step::Int +) + t = _time_from_step(step, simulation.timeline) + return _scatter_domain_hard_dependency_environment_outputs_at_time!(simulation, domain, node, status, t) +end + +function _raw_meteo_for_staged_graph_domains(environment::GlobalConstant) + return environment_meteo(environment) +end + +function _raw_meteo_for_staged_graph_domains(environment::AbstractEnvironmentBackend) + return environment +end + +function _graph_domain_environment_for_model( + simulation::DomainSimulation, + domain::Domain, + node::SoftDependencyNode, + status, + t, + model_clock::ClockSpec, + model_spec::ModelSpec, + meteo, + meteo_sampler, + multirate::Bool +) + if simulation.environment isa GlobalConstant + return multirate ? _sample_meteo_for_model(meteo_sampler, meteo, round(Int, t), model_clock, model_spec) : meteo + end + support = _domain_environment_support(domain, node, status) + return _sample_domain_environment_at_time(simulation, support, t, model_spec) +end + +function _meteo_for_graph_step(meteo, step::Int, nsteps::Int) + return _meteo_row_at_step(meteo, step) +end + +function _meteo_for_graph_step(backend::AbstractEnvironmentBackend, step::Int, nsteps::Int) + return backend +end + +function _scatter_graph_domain_environment_outputs!( + simulation::DomainSimulation, + domain::Domain, + node::AbstractDependencyNode, + model_spec::ModelSpec, + status, + t +) + return _scatter_domain_environment_outputs_at_time!(simulation, domain, node, model_spec, status, t) +end + +function _scatter_graph_domain_hard_dependency_environment_outputs!( + simulation::DomainSimulation, + domain::Domain, + node::HardDependencyNode, + status, + t +) + return _scatter_domain_hard_dependency_environment_outputs_at_time!(simulation, domain, node, status, t) +end diff --git a/src/domains/graph_domain_runner.jl b/src/domains/graph_domain_runner.jl new file mode 100644 index 000000000..a9955ef5c --- /dev/null +++ b/src/domains/graph_domain_runner.jl @@ -0,0 +1,135 @@ +function _prepare_graph_domain_runtime!( + simulation::DomainSimulation, + domain::Domain, + object, + meteo, + constants, + nsteps::Int; + check=true, + executor=SequentialEx(), + type_promotion=_type_promotion(_domain_mapping(domain)), +) + roots = _domain_graph_roots(object, domain) + graph_simulations = GraphSimulation[] + for root in roots + _materialize_graph_route_attributes_for_domain!(simulation, domain, root, 1) + push!( + graph_simulations, + GraphSimulation( + root, + _domain_mapping(domain); + nsteps=nsteps, + check=check, + outputs=nothing, + type_promotion=type_promotion, + ), + ) + end + graph_state = DomainGraphState(graph_simulations) + simulation.domain_states[domain.name] = graph_state + representative_simulation = first(graph_simulations) + effective_multirate = _effective_multirate(representative_simulation) + dep_graph = dep(representative_simulation) + timeline = _timeline_context(meteo) + meteo_sampler = effective_multirate ? _prepare_meteo_sampler(meteo) : nothing + runtime_clock_rows = _runtime_clock_rows(representative_simulation, timeline, dep_graph) + effective_executor = executor + validate_meteo_inputs(get_model_specs(representative_simulation), meteo) + _validate_meteo_derived_timestep_requirements!(runtime_clock_rows, timeline) + if effective_multirate + if executor != SequentialEx() + @warn string( + "Multi-rate MTG domain `$(domain.name)` currently executes sequentially. ", + "Provided `executor=$(executor)` is ignored in this mode. ", + "Use `executor=SequentialEx()` to silence this warning." + ) maxlog = 1 + effective_executor = SequentialEx() + end + _warn_if_no_model_runs_at_base_timestep(runtime_clock_rows, timeline) + for graph_simulation in graph_simulations + validate_canonical_publishers(graph_simulation) + configure_temporal_buffers!(graph_simulation, timeline) + end + end + return DomainGraphRuntime(graph_state, meteo, constants, effective_multirate, timeline, meteo_sampler, effective_executor) +end + +function _run_graph_domain_step!( + simulation::DomainSimulation, + domain::Domain, + runtime::DomainGraphRuntime, + step::Int, + nsteps::Int; + check=true, + phase::Symbol=:normal, +) + meteo_i = _meteo_for_graph_step(runtime.meteo, step, nsteps) + meteo_provider = (node, status, i, t, model_clock, model_spec, meteo, meteo_sampler, multirate) -> + _graph_domain_environment_for_model( + simulation, + domain, + node, + status, + t, + model_clock, + model_spec, + meteo, + meteo_sampler, + multirate, + ) + after_model_run = (node, model_spec, status, i, t) -> begin + _scatter_graph_domain_environment_outputs!(simulation, domain, node, model_spec, status, t) + for hard_child in node.hard_dependency + _scatter_graph_domain_hard_dependency_environment_outputs!(simulation, domain, hard_child, status, t) + end + nothing + end + skip_model_run = node -> !_should_visit_domain_node(simulation, domain, node; phase=phase) + for graph_simulation in runtime.state.simulations + _materialize_graph_routes_for_domain!(simulation, domain, graph_simulation, step) + roots = collect(dep(graph_simulation).roots) + models = get_models(graph_simulation) + for (_, dependency_node) in roots + run_node_multiscale!( + graph_simulation, + dependency_node, + step, + models, + meteo_i, + runtime.constants, + graph_simulation, + check, + runtime.executor, + runtime.effective_multirate, + runtime.timeline, + runtime.meteo_sampler; + meteo_provider=meteo_provider, + after_model_run=after_model_run, + skip_model_run=skip_model_run, + ) + end + if phase == :normal + runtime.effective_multirate && update_requested_outputs!(graph_simulation, _time_from_step(step, runtime.timeline)) + save_results!(graph_simulation, step) + end + end + _publish_graph_domain_step_outputs!( + simulation, + domain, + runtime.state, + step; + effective_multirate=runtime.effective_multirate, + phase=phase, + ) + _update_domain_environment_index!(simulation, domain) + return runtime.state +end + +function _finalize_graph_domain_runtime!(runtime::DomainGraphRuntime) + for graph_simulation in runtime.state.simulations + for (organ, index) in graph_simulation.outputs_index + resize!(outputs(graph_simulation)[organ], index - 1) + end + end + return runtime.state +end diff --git a/src/domains/output_publisher.jl b/src/domains/output_publisher.jl new file mode 100644 index 000000000..51b5feb2b --- /dev/null +++ b/src/domains/output_publisher.jl @@ -0,0 +1,104 @@ +function _publish_domain_model_outputs!( + simulation::DomainSimulation, + domain::Domain, + node::AbstractDependencyNode, + status, + step::Int +) + key = DomainModelKey(domain.name, node.scale, node.process) + haskey(simulation.model_specs, key) || return nothing + spec = simulation.model_specs[key] + for out_var in keys(outputs_(spec)) + stream_key = (key, out_var) + value = status[out_var] + push!(get!(simulation.streams, stream_key, Pair{Int,Any}[]), step => value) + push!(get!(simulation.outputs, stream_key, Any[]), value) + end + return nothing +end + +function _publish_domain_hard_dependency_outputs!( + simulation::DomainSimulation, + domain::Domain, + node::HardDependencyNode, + status, + step::Int +) + _publish_domain_model_outputs!(simulation, domain, node, status, step) + for child in node.children + _publish_domain_hard_dependency_outputs!(simulation, domain, child, status, step) + end + return nothing +end + +function _publish_graph_domain_step_outputs!( + simulation::DomainSimulation, + domain::Domain, + graph_state::DomainGraphState, + step::Int; + effective_multirate::Bool=false, + phase::Symbol=:normal, +) + graph_statuses = status(graph_state) + for (key, spec) in simulation.model_specs + key.domain == domain.name || continue + _should_publish_domain_key(simulation, domain, key; phase=phase) || continue + if effective_multirate + clock = simulation.model_clocks[key] + _should_run_at_time(clock, float(step)) || continue + end + haskey(graph_statuses, key.scale) || continue + for out_var in keys(outputs_(spec)) + stream_key = (key, out_var) + ids = Int[] + values = Any[] + for st in graph_statuses[key.scale] + out_var in propertynames(st) || continue + push!(ids, node_id(st.node)) + push!(values, st[out_var]) + end + push!(get!(simulation.streams, stream_key, Pair{Int,Any}[]), step => DomainNodeValues(ids, values)) + push!(get!(simulation.outputs, stream_key, Any[]), values) + end + end + return nothing +end + +function _publish_graph_target!(target::ModelTarget) + spec = target.simulation.model_specs[target.key] + domain = _domain_for_name(target.simulation.mapping, target.key.domain) + t = _time_from_step(target.step, target.simulation.timeline) + _scatter_graph_domain_environment_outputs!(target.simulation, domain, target.node, spec, target.status, t) + for hard_child in target.node.hard_dependency + _scatter_graph_domain_hard_dependency_environment_outputs!(target.simulation, domain, hard_child, target.status, t) + end + for out_var in keys(outputs_(spec)) + out_var in propertynames(target.status) || continue + stream_key = (target.key, out_var) + value = target.status[out_var] + push!(get!(target.simulation.streams, stream_key, Pair{Int,Any}[]), target.step => value) + push!(get!(target.simulation.outputs, stream_key, Any[]), value) + end + for hard_child in target.node.hard_dependency + _publish_domain_hard_dependency_outputs!(target.simulation, domain, hard_child, target.status, target.step) + end + return nothing +end + +function _publish_target!(target::ModelTarget) + isnothing(target.simulation) && error( + "`publish=true` requires a target created by `dependency_targets(extra, name)` in a domain simulation." + ) + target.extra isa GraphSimulation && return _publish_graph_target!(target) + domain = _domain_for_name(target.simulation.mapping, target.key.domain) + spec = target.simulation.model_specs[target.key] + _scatter_domain_environment_outputs!(target.simulation, domain, target.node, spec, target.status, target.step) + for hard_child in target.node.hard_dependency + _scatter_domain_hard_dependency_environment_outputs!(target.simulation, domain, hard_child, target.status, target.step) + end + _publish_domain_model_outputs!(target.simulation, domain, target.node, target.status, target.step) + for hard_child in target.node.hard_dependency + _publish_domain_hard_dependency_outputs!(target.simulation, domain, hard_child, target.status, target.step) + end + return nothing +end diff --git a/src/evaluation/fit.jl b/src/evaluation/fit.jl index 7998d6a5d..873127244 100644 --- a/src/evaluation/fit.jl +++ b/src/evaluation/fit.jl @@ -29,7 +29,7 @@ and `Ri_PAR_f`. # Including example processes and models: using PlantSimEngine.Examples; -m = ModelList(Beer(0.6), status=(LAI=2.0,)) +m = ModelMapping(Beer(0.6), status=(LAI=2.0,)) meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_f=300.0) run!(m, meteo) df = DataFrame(aPPFD=m[:aPPFD][1], LAI=m.status.LAI[1], Ri_PAR_f=meteo.Ri_PAR_f[1]) @@ -40,4 +40,4 @@ Note that this is a dummy example to show that the fitting method works, as we s using the Beer-Lambert law with a value of `k=0.6`, and then use the simulated aPPFD to fit the `k` parameter again, which gives the same value as the one used on the simulation. """ -function fit end \ No newline at end of file +function fit end diff --git a/src/mtg/GraphSimulation.jl b/src/mtg/GraphSimulation.jl index 60aaad0d6..5ba81b4f1 100644 --- a/src/mtg/GraphSimulation.jl +++ b/src/mtg/GraphSimulation.jl @@ -141,7 +141,7 @@ function convert_outputs(outs::Dict{Symbol,O} where O, sink; refvectors=false, n return ret end -# ModelLists now return outputs as a TimeStepTable{Status}, conversion is straightforward +# Single-scale mappings return outputs as a TimeStepTable{Status}, conversion is straightforward function convert_outputs(out::TimeStepTable{T} where T, sink) if !Tables.istable(sink) error("The sink argument must be compatible with the Tables.jl interface (`Tables.istable(sink)` must return `true`, *e.g.* `DataFrame`)") diff --git a/src/mtg/mapping/mapping.jl b/src/mtg/mapping/mapping.jl index 8f5c6aa4b..c8404ad4d 100755 --- a/src/mtg/mapping/mapping.jl +++ b/src/mtg/mapping/mapping.jl @@ -104,7 +104,8 @@ struct MultiScale <: AbstractScaleSetup end struct SingleScale <: AbstractScaleSetup end const ModelRateDeclarations = Dict{Symbol,Any} -const ReverseMultiscaleMapping = Dict{Symbol,Dict{Symbol,Dict{Symbol,Symbol}}} +const ReverseMappingTarget = Union{Symbol,PreviousTimeStep} +const ReverseMultiscaleMapping = Dict{Symbol,Dict{Symbol,Dict{Symbol,ReverseMappingTarget}}} """ ModelMappingInfo @@ -162,7 +163,7 @@ configuration errors: The type behaves like a read-only dictionary keyed by scale name (`Symbol`). Use `Dict(mapping)` to recover a plain dictionary. """ -struct ModelMapping{S<:AbstractScaleSetup,D} <: AbstractDict{Symbol,Tuple} where {D<:Union{Dict{Symbol,Tuple},ModelList}} +struct ModelMapping{S<:AbstractScaleSetup,D} <: AbstractDict{Symbol,Tuple} where {D<:Union{Dict{Symbol,Tuple},SingleScaleModelSet}} data::D info::ModelMappingInfo type_promotion::Union{Nothing,Dict} @@ -344,7 +345,7 @@ end Convenience constructors for [`ModelMapping`](@ref): - pass `scale => models` pairs directly (dict-like syntax), -- or pass models/processes directly for a single scale (old `ModelList` syntax). +- or pass models/processes directly for a single scale. `type_promotion` may be a dictionary of source type to target type, for example `Dict(Float64 => Float32)`. In single-scale mappings it is applied when the @@ -405,12 +406,12 @@ function ModelMapping( return _build_model_mapping( SingleScale, - ModelList(flat_args...; status=status, type_promotion=type_promotion, variables_check=check, processes...); + SingleScaleModelSet(flat_args...; status=status, type_promotion=type_promotion, variables_check=check, processes...); validated=check, type_promotion=type_promotion ) - #TODO: Use the following when we merge the ModelList and ModelMapping paths (create a fake scale): + #TODO: Use the following when we merge the single-scale and multiscale mapping paths (create a fake scale): single_scale_models = _single_scale_mapping_entries(args, processes, status) # return ModelMapping{SingleScale}(Dict(_normalize_scale(scale) => single_scale_models), check=check) end @@ -484,7 +485,7 @@ function _normalize_multiscale_mapping(mapping::AbstractDict) return normalized end -function _normalize_scale_mapping(scale::Symbol, scale_mapping::ModelList) +function _normalize_scale_mapping(scale::Symbol, scale_mapping::SingleScaleModelSet) return _normalize_scale_mapping(scale, (values(scale_mapping.models)..., status(scale_mapping))) end @@ -499,14 +500,14 @@ end function _normalize_scale_mapping(scale::Symbol, scale_mapping::Tuple) normalized_items = Any[] for item in scale_mapping - if item isa ModelList + if item isa SingleScaleModelSet append!(normalized_items, values(item.models)) push!(normalized_items, status(item)) elseif item isa Union{AbstractModel,MultiScaleModel,ModelSpec,Status} push!(normalized_items, item) else error( - "Invalid mapping entry at scale `$scale`: expected models/ModelSpec, Status, or ModelList, got $(typeof(item))." + "Invalid mapping entry at scale `$scale`: expected models, ModelSpec, Status, or single-scale ModelMapping, got $(typeof(item))." ) end end @@ -515,7 +516,7 @@ end function _normalize_scale_mapping(scale::Symbol, scale_mapping) error( - "Invalid mapping entry at scale `$scale`: expected a model/ModelSpec, tuple of models/Status, or ModelList, got $(typeof(scale_mapping))." + "Invalid mapping entry at scale `$scale`: expected a model, ModelSpec, tuple of models/Status, or single-scale ModelMapping, got $(typeof(scale_mapping))." ) end @@ -728,7 +729,7 @@ function _build_model_mapping_recommendations( return recommendations end -function _build_model_mapping_info(::Type{SingleScale}, mapping::ModelList; validated::Bool) +function _build_model_mapping_info(::Type{SingleScale}, mapping::SingleScaleModelSet; validated::Bool) specs = Dict( :Default => Dict{Symbol,ModelSpec}( process(model) => as_model_spec(model) for model in values(mapping.models) diff --git a/src/mtg/mapping/model_generation_from_status_vectors.jl b/src/mtg/mapping/model_generation_from_status_vectors.jl index 512a10b23..e0c3cad47 100644 --- a/src/mtg/mapping/model_generation_from_status_vectors.jl +++ b/src/mtg/mapping/model_generation_from_status_vectors.jl @@ -183,7 +183,7 @@ end # This is a helper function only for testing purposes. -function modellist_to_mapping(modellist_original::ModelList, modellist_status; nsteps=nothing, outputs=nothing) +function modellist_to_mapping(modellist_original::SingleScaleModelSet, modellist_status; nsteps=nothing, outputs=nothing) modellist = Base.copy(modellist_original, modellist_original.status) diff --git a/src/mtg/mapping/reverse_mapping.jl b/src/mtg/mapping/reverse_mapping.jl index b74eb23c0..ccf6255e9 100644 --- a/src/mtg/mapping/reverse_mapping.jl +++ b/src/mtg/mapping/reverse_mapping.jl @@ -77,7 +77,7 @@ function reverse_mapping(mapping::AbstractDict{Symbol,T}; all=true) where {T<:An end function reverse_mapping(mapped_vars::Dict{Symbol,Dict{Symbol,Any}}; all=true) - reverse_multiscale_mapping = ReverseMultiscaleMapping(org => Dict{Symbol,Dict{Symbol,Symbol}}() for org in keys(mapped_vars)) + reverse_multiscale_mapping = ReverseMultiscaleMapping(org => Dict{Symbol,Dict{Symbol,ReverseMappingTarget}}() for org in keys(mapped_vars)) for (organ, vars) in mapped_vars # e.g.: organ = :Plant; vars = mapped_vars[organ] for (var, val) in vars # e.g. var = :Rm_organs; val = vars[var] if isa(val, MappedVar) && !isa(val, MappedVar{SelfNodeMapping}) && (all || !isa(val, MappedVar{SingleNodeMapping})) @@ -98,7 +98,7 @@ function reverse_mapping(mapped_vars::Dict{Symbol,Dict{Symbol,Any}}; all=true) # reverse_multiscale_mapping[mapped_o] = Dict{Symbol,Vector{MappedVar}}() # end if !haskey(reverse_multiscale_mapping[mapped_o], organ) - reverse_multiscale_mapping[mapped_o][organ] = Dict{Symbol,Symbol}(source_variable(val, mapped_o) => mapped_variable(val)) + reverse_multiscale_mapping[mapped_o][organ] = Dict{Symbol,ReverseMappingTarget}(source_variable(val, mapped_o) => mapped_variable(val)) end push!(reverse_multiscale_mapping[mapped_o][organ], source_variable(val, mapped_o) => mapped_variable(val)) end diff --git a/src/mtg/save_results.jl b/src/mtg/save_results.jl index f403fda60..b1b040699 100644 --- a/src/mtg/save_results.jl +++ b/src/mtg/save_results.jl @@ -321,7 +321,7 @@ function copy_tracked_outputs_into_vector!(outs_organ, i, statuses_organ, tracke return j end -function pre_allocate_outputs(m::ModelList, outs, nsteps; type_promotion=nothing, check=true) +function pre_allocate_outputs(m::SingleScaleModelSet, outs, nsteps; type_promotion=nothing, check=true) st, = flatten_status(status(m)) out_vars_all = convert_vars(st, type_promotion) diff --git a/src/processes/model_initialisation.jl b/src/processes/model_initialisation.jl index 7f642dbe6..1c1d2fde6 100755 --- a/src/processes/model_initialisation.jl +++ b/src/processes/model_initialisation.jl @@ -66,7 +66,7 @@ mapping = ModelMapping( to_initialize(mapping) ``` """ -function to_initialize(m::ModelList) +function to_initialize(m::SingleScaleModelSet) needed_variables = to_initialize(dep(m)) to_init = Dict{Symbol,Tuple}() for (process, vars) in needed_variables @@ -246,7 +246,7 @@ function init_variables(model::T; verbose::Bool=true) where {T<:AbstractModel} return vars end -function init_variables(m::ModelList; verbose::Bool=true) +function init_variables(m::SingleScaleModelSet; verbose::Bool=true) init_variables(dep(m)) end @@ -301,7 +301,7 @@ models = ModelMapping( is_initialized(models) ``` """ -function is_initialized(m::T; verbose=true) where {T<:ModelList} +function is_initialized(m::T; verbose=true) where {T<:SingleScaleModelSet} var_names = to_initialize(m) if any([length(to_init) > 0 for (process, to_init) in pairs(var_names)]) diff --git a/src/run.jl b/src/run.jl index 251f89662..cfe4a0b79 100644 --- a/src/run.jl +++ b/src/run.jl @@ -114,11 +114,11 @@ function adjust_weather_timesteps_to_given_length(desired_length, meteo) end -function _modellist_from_model_mapping(mapping::ModelMapping{SingleScale}) +function _single_scale_model_set_from_mapping(mapping::ModelMapping{SingleScale}) mapping.data end -function _modellist_from_model_mapping(::ModelMapping{MultiScale}) +function _single_scale_model_set_from_mapping(::ModelMapping{MultiScale}) error("This `ModelMapping` is a multiscale mapping. ", "Use `run!(mtg, mapping, ...)` for multiscale mappings.") end @@ -134,9 +134,9 @@ function run!( requested_outputs_sink=DataFrames.DataFrame ) _validate_meteo_duration(meteo) - model_list = _modellist_from_model_mapping(mapping) - _run_modellist_singleton( - model_list, + model_set = _single_scale_model_set_from_mapping(mapping) + _run_single_scale_model_set( + model_set, meteo, constants, extra; @@ -234,10 +234,10 @@ function run!( return_requested_outputs=false, requested_outputs_sink=DataFrames.DataFrame ) where {T<:ModelMapping{SingleScale}} - model_list = _modellist_from_model_mapping(object) + model_set = _single_scale_model_set_from_mapping(object) - _run_modellist_singleton( - model_list, + _run_single_scale_model_set( + model_set, meteo, constants, extra; @@ -248,8 +248,8 @@ function run!( ) end -function _run_modellist_singleton( - object::ModelList, +function _run_single_scale_model_set( + object::SingleScaleModelSet, meteo=nothing, constants=PlantMeteo.Constants(), extra=nothing; @@ -275,7 +275,7 @@ function _run_modellist_singleton( if length(dep_graph.not_found) > 0 error( - "The following processes are missing to run the ModelList: ", + "The following processes are missing to run the single-scale ModelMapping: ", dep_graph.not_found ) end @@ -353,7 +353,7 @@ function run_node!( meteo, constants, extra -) where {T<:ModelList} +) where {T<:SingleScaleModelSet} # Check if all the parents have been called before the child: if !AbstractTrees.isroot(node) && any([p.simulation_id[i] <= node.simulation_id[i] for p in node.parent]) diff --git a/src/traits/table_traits.jl b/src/traits/table_traits.jl index e4eab2f83..2ba21e17a 100644 --- a/src/traits/table_traits.jl +++ b/src/traits/table_traits.jl @@ -17,7 +17,7 @@ how to iterate over the data. The following data formats are supported: The default implementation returns `TableAlike` for `AbstractDataFrame`, `TimeStepTable`, `AbstractVector` and `Dict`, `TreeAlike` for `GraphSimulation`, -`SingletonAlike` for `Status`, `ModelList`, `NamedTuple` and `TimeStepRow`. +`SingletonAlike` for `Status`, `SingleScaleModelSet`, `NamedTuple` and `TimeStepRow`. The default implementation for `Any` throws an error. Users that want to use another input should define this trait for the new data format, e.g.: @@ -51,13 +51,13 @@ DataFormat(::Type{<:DataFrames.AbstractDataFrame}) = TableAlike() DataFormat(::Type{<:PlantMeteo.TimeStepTable}) = TableAlike() DataFormat(::Type{<:PlantMeteo.TimeStepRows}) = TableAlike() -# Giving a ModelList as a vector or a dict of objects: +# Giving a SingleScaleModelSet as a vector or a dict of objects: DataFormat(::Type{<:AbstractVector}) = TableAlike() DataFormat(::Type{<:Dict}) = TableAlike() DataFormat(::Type{<:NamedTuple}) = SingletonAlike() DataFormat(::Type{<:Status}) = SingletonAlike() -DataFormat(::Type{<:ModelList{Mo,S} where {Mo,S}}) = SingletonAlike() +DataFormat(::Type{<:SingleScaleModelSet{Mo,S} where {Mo,S}}) = SingletonAlike() DataFormat(::Type{<:ModelMapping{SingleScale}}) = SingletonAlike() DataFormat(::Type{<:GraphSimulation}) = TreeAlike() diff --git a/test/test-simulation.jl b/test/test-simulation.jl index cb17acc1f..f433c1c11 100644 --- a/test/test-simulation.jl +++ b/test/test-simulation.jl @@ -33,13 +33,7 @@ end; meteo = Atmosphere(T=20.0, Wind=1.0, Rh=0.65) run!(models, meteo) - legacy_models = PlantSimEngine.ModelList( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=15.0, var2=0.3) - ) - @test_throws MethodError run!(legacy_models, meteo) + @test !isdefined(PlantSimEngine, :ModelList) @test_throws MethodError run!([models], meteo) @test_throws ErrorException run!(ModelMapping("mod1" => models), meteo) From b377c9c5d9f8fe29dc6d07b4215845dfb543adac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Fri, 5 Jun 2026 17:50:10 +0200 Subject: [PATCH 14/33] Starting implementing the new scene API --- docs/src/dev/code_cleanup_audit.md | 8 + docs/src/dev/maespa_domain_handoff.md | 162 +- .../src/dev/multi_domain_simulation_design.md | 12 + docs/src/dev/multi_domain_simulation_plan.md | 8 + docs/src/dev/release_notes_handoff.md | 174 ++ docs/src/dev/unified_scene_object_design.md | 624 ++++++ ...nified_scene_object_implementation_plan.md | 646 ++++++ examples/maespa_domain_example.jl | 28 +- src/PlantSimEngine.jl | 22 +- src/domains/domain_simulation.jl | 38 +- src/domains/route_runtime.jl | 76 + src/mtg/ModelSpec.jl | 181 +- src/mtg/model_spec_inference.jl | 27 +- src/processes/models_inputs_outputs.jl | 35 + src/scene_object_api.jl | 1753 +++++++++++++++++ test/runtests.jl | 4 + test/test-domain-simulation.jl | 58 + test/test-maespa-domain-example.jl | 7 +- test/test-unified-scene-object-api.jl | 868 ++++++++ 19 files changed, 4667 insertions(+), 64 deletions(-) create mode 100644 docs/src/dev/release_notes_handoff.md create mode 100644 docs/src/dev/unified_scene_object_design.md create mode 100644 docs/src/dev/unified_scene_object_implementation_plan.md create mode 100644 src/scene_object_api.jl create mode 100644 test/test-unified-scene-object-api.jl diff --git a/docs/src/dev/code_cleanup_audit.md b/docs/src/dev/code_cleanup_audit.md index c0fad9a4d..7b9cb7794 100644 --- a/docs/src/dev/code_cleanup_audit.md +++ b/docs/src/dev/code_cleanup_audit.md @@ -4,6 +4,14 @@ This page records cleanup candidates found during the multi-domain experimental branch audit. It is intentionally biased toward code health and release-note planning rather than immediate implementation. +See also: + +- `release_notes_handoff.md` for the consolidated release-note source. +- `unified_scene_object_design.md` for the planned breaking replacement of the + current domain/route and multiscale-mapping split. +- `unified_scene_object_implementation_plan.md` for the implementation handoff + of that future design. + Priority meanings: - P0: architectural compatibility removal or high-impact breaking cleanup. diff --git a/docs/src/dev/maespa_domain_handoff.md b/docs/src/dev/maespa_domain_handoff.md index c27e0cac0..4e27471a1 100644 --- a/docs/src/dev/maespa_domain_handoff.md +++ b/docs/src/dev/maespa_domain_handoff.md @@ -1,39 +1,127 @@ # MAESPA-Style Domain Example Handoff -The example in `examples/maespa_domain_example.jl` is the target API for the -new hard-domain design. - -## API Expectations - -- `HardDomains(kind=:plant, scale=:Leaf, process=:leaf_eb)` selects models that - are hard dependencies of the declaring model. -- Hard-domain targets are excluded from normal domain scheduling. -- Parent models retrieve selected targets with - `dependency_targets(extra, :dependency_name)`. -- Parent models execute selected targets with `run_target!(target)`. -- `run_target!(target; publish=true)` publishes the hard-run model outputs - to domain streams and `DomainSimulation.outputs`. -- Trial target runs still mutate the target status. Irreversible accumulators - such as growth/carbon pools should be committed only after the accepted final - target run, not inside every trial iteration. -- `explain_domain_dependencies(sim)` reports hard-domain dependencies with - `mode=:hard_domain`. - -## Scheduler Expectations - -- `AllDomains` keeps stream-reading semantics for normally scheduled producers. -- `HardDomains` keeps hard-dependency semantics for manually run producers. -- Normal plant allocation models are not hard-called by scene models. -- Daily allocation models run through the normal dependency graph after scene - hard-runs have published leaf outputs. - -## Example Verification - -- Species A has two leaves. -- Species B has three leaves. -- `LeafEB`, `FvCB`, `Tuzet`, and `SoilWater` are driven through `SceneEB`, not - through normal scheduling. -- `AllocA` and `AllocB` run normally on a daily clock. -- Leaf temperature, assimilation, transpiration, canopy air state, and soil - water potential remain finite and change during the run. -- Allocation differs between species because their parameters differ. +The example in `examples/maespa_domain_example.jl` is the executable test bed +for the current hard-domain and scene microclimate prototype. It is also a good +migration target for the future unified scene/object design. + +## Current Example Shape + +- Two MTG-backed plant domains share scale names such as `:Plant` and `:Leaf`. +- Each plant domain uses copied PlantBiophysics subsample models: + `Monteith` for `:energy_balance`, `Fvcb` for `:photosynthesis`, and `Tuzet` + for `:stomatal_conductance`. +- `LeafState` owns `leaf_area` and `leaf_carbon`, because those are plant + bookkeeping variables and not PlantBiophysics model outputs. +- `LAIModel` runs in the scene domain and now receives leaf areas through + `ModelSpec(...) |> Inputs(...)`, which is bridged internally to the current + route materialization runtime. +- `SceneEB` runs hourly and now uses `ModelSpec(...) |> Calls(...)` to + manually run leaf `:energy_balance` targets and the shared soil + `:soil_water` target. +- Plant allocation runs daily through the normal plant-local dependency graph. + +## Manual Call Expectations + +- `Calls(:energy_balance => Many(kind=:plant, scale=:Leaf, process=:energy_balance))` + selects one target per matching leaf status through the current hard-domain + bridge. +- `Calls(:soil => One(kind=:soil, process=:soil_water))` selects the shared + soil model through the current hard-domain bridge. +- `dependency_targets(extra, :energy_balance)` returns executable leaf targets. +- `run_call!(target; publish=false)` is used during trial iterations. +- `run_call!(target; publish=true)` is used for the accepted final solution + so outputs are appended once to domain streams and `DomainSimulation.outputs`. +- Trial target runs mutate target status. Irreversible accumulators such as + `leaf_carbon` are updated only after the accepted solution. + +## MAESPA-Style Canopy Microclimate + +Input meteorology is treated as above-canopy forcing: + +- `meteo.T` is above-canopy air temperature. +- `meteo.VPD` is above-canopy VPD. +- `meteo.Wind`, `meteo.P`, and radiation variables are above-canopy drivers. + +Scene status stores below-canopy microclimate: + +- `canopy_tair` +- `canopy_vpd` +- `canopy_rh` +- `canopy_htot` +- `canopy_gcanop` + +The helper `tvpdcanopcalc(...)` ports the MAESPA-style canopy T/VPD update, and +`gbcanms(...)` ports the canopy aerodynamic conductance shape. The example uses +PlantMeteo kPa conventions and clipping equivalent to MAESPA's Pa clipping. + +`SceneEB` computes total leaf fluxes per ground area and uses those values for +the canopy microclimate update. Total leaf area is routed to `LAIModel`, which +computes `leaf_area` and `lai` in the scene domain. + +## Verification Expectations + +The focused test `test/test-maespa-domain-example.jl` should verify: + +- Species A has two leaves and species B has three leaves. +- `:energy_balance` hard-domain outputs are published once per leaf per hour. +- Soil `psi_soil` is updated through the scene hard target. +- `canopy_tair` and `canopy_vpd` remain finite and within MAESPA clipping + bounds relative to the above-canopy meteo. +- `canopy_rh` remains between 0 and 1. +- `LAIModel` sees every leaf area through the route and computes scene LAI. +- Daily allocation differs between the two plant species because their + allocation parameters differ. + +## Future Migration Target + +The MAESPA example has already moved away from explicit user-level +`Route(...)` and `HardDomains(...)`: leaf area materialization is declared with +`Inputs(...)`, and manual energy-balance calls are declared with `Calls(...)`. + +Expected migration: + +```julia +ModelSpec(LAIModel(ground_area)) |> + AppliesTo(One(scale=:Scene)) |> + Inputs(:leaf_areas => Many(kind=:plant, scale=:Leaf, process=:leaf_state, var=:leaf_area)) + +ModelSpec(SceneEB()) |> + AppliesTo(One(scale=:Scene)) |> + Calls(:leaf_energy => Many(kind=:plant, scale=:Leaf, process=:energy_balance)) |> + Calls(:soil => One(kind=:soil, process=:soil_water)) +``` + +Plant-local allocation should similarly move from `MultiScaleModel(...)` to a +scope-relative input: + +```julia +ModelSpec(AllocA(...)) |> + AppliesTo(Many(kind=:plant, scale=:Plant)) |> + Inputs(:leaf_carbon => Many(scale=:Leaf, within=Self(), var=:leaf_carbon)) +``` + +If this plant-local dependency is the normal behavior of the allocation model, +the model can provide it as a default trait instead: + +```julia +dep(::AllocA) = ( + leaf_carbon = Input(Many(scale=:Leaf, within=Self(), var=:leaf_carbon)), +) +``` + +The same applies to manual calls. A leaf energy-balance model can use `dep` to +declare its usual stomatal-conductance call, while the scene `ModelSpec` can +still override or specialize the call selection with `Calls(...)` when the +model is assembled into a MAESPA-style scene. + +Here `Self()` means the current model application object or scope. Because +`AllocA` runs at the plant scale, this selects leaves inside the current plant. +For a model running below the plant scale, use `SelfPlant()` or +`Ancestor(scale=:Plant)` when the intended scope is the containing plant. + +The migration target should treat `SceneEB`, `LAIModel`, and plant allocation +as model applications. `AppliesTo(...)` declares where each application runs; +`Inputs(...)` provides values scheduled by the runtime; `Calls(...)` provides +manual call handles for the iterative scene energy-balance solver. This keeps +the MAESPA example aligned with the unified design and avoids recreating a +separate domain-specific path. diff --git a/docs/src/dev/multi_domain_simulation_design.md b/docs/src/dev/multi_domain_simulation_design.md index 63fcb116d..184fbd7d3 100644 --- a/docs/src/dev/multi_domain_simulation_design.md +++ b/docs/src/dev/multi_domain_simulation_design.md @@ -1,5 +1,17 @@ # Multi-Domain Simulation Design +!!! warning "Current prototype, not the target breaking design" + This page documents the current multi-domain prototype built around + `Domain`, `SimulationMapping`, `Route`, `AllDomains`, and `HardDomains`. + The target breaking redesign is now documented in + `unified_scene_object_design.md`. New architecture work should use the + unified scene/object design as the source of truth. In that redesign, + `dep(model)` is kept as the model-level default dependency trait: current + `AllDomains(...)` value dependencies migrate toward `Input(...)` defaults, + current `HardDomains(...)` manual dependencies migrate toward `Call(...)` + defaults, and scenario-level `ModelSpec` declarations remain the final + override point. + This page is the working design for extending PlantSimEngine from one plant or one MTG mapping to reusable plant, soil, scene, and environment domains. diff --git a/docs/src/dev/multi_domain_simulation_plan.md b/docs/src/dev/multi_domain_simulation_plan.md index f4aebc1f7..1b6f799c8 100644 --- a/docs/src/dev/multi_domain_simulation_plan.md +++ b/docs/src/dev/multi_domain_simulation_plan.md @@ -1,5 +1,13 @@ # Multi-Domain Simulation Implementation Plan +!!! warning "Current prototype plan" + This page tracks what has been implemented in the current multi-domain + prototype. The next breaking architecture plan lives in + `unified_scene_object_implementation_plan.md`. That plan keeps `dep(model)` + as the model-level default dependency trait, with future `Input(...)` and + `Call(...)` defaults overridden by scenario-level `ModelSpec` + configuration. + This page tracks the implementation plan for the multi-domain work. The design reference is `multi_domain_simulation_design.md`. diff --git a/docs/src/dev/release_notes_handoff.md b/docs/src/dev/release_notes_handoff.md new file mode 100644 index 000000000..f4fda8173 --- /dev/null +++ b/docs/src/dev/release_notes_handoff.md @@ -0,0 +1,174 @@ +# Release Notes Handoff + +This page is the persistent release-note source for work done during the +multi-domain and cleanup branch. Keep it factual: mark what is implemented, +what is removed, and what is only planned. + +## Implemented Breaking Cleanup + +Source details live in `code_cleanup_audit.md`. + +- Removed public `ModelList` usage. Use `ModelMapping(model...; status=...)` + for single-scale simulations. +- Removed direct `run!(::ModelList, ...)`; wrap models in `ModelMapping`. +- Removed batch `run!` over collections/dictionaries of single-scale mappings; + use explicit loops or comprehensions. +- Removed raw `Dict` multiscale `run!(mtg, dict, ...)`; construct + `ModelMapping(dict)` first. +- Removed string scale names. Use symbols, for example `:Leaf`. +- Removed `ModelMapping(Float64 => Float32)` promotion shorthand. Use a + `Dict(Float64 => Float32)` as the `type_promotion` value. +- Removed old multiscale output indexing helpers. Convert outputs explicitly + before indexing. +- Replaced the `Symbol("")` same-scale sentinel with `SameScale()`. +- Replaced many source-side validation `@assert`s with explicit errors. +- Added `Updates(:var; after=:process)` for ordered duplicate writers. + +## Implemented Multi-Domain / Scene Prototype Features + +These features exist in the current prototype, but may be replaced by the +unified scene/object API in a future breaking pass. + +- `Domain`, `SimulationMapping`, `DomainSimulation`, and `DomainModelKey`. +- Single-status and MTG-backed domains. +- Domain selectors that can select one or more MTG subtree roots. +- Domain-local and global status views: + `status(sim, :plant_A, :Leaf)` and `status(sim, :Leaf)`. +- Multi-rate domain execution using `Dates` periods. +- `AllDomains(...)` stream/value dependencies. +- `Route(...)` materialization with `ManyToOneVector`, + `ManyToOneAggregate`, and limited `OneToManyBroadcast` graph support. +- `HardDomains(...)`, `dependency_targets`, `ModelTarget`, and + `run_target!` for manually controlled hard-domain calls. +- Environment backend protocol: + `AbstractEnvironmentBackend`, `GlobalConstant`, `EnvironmentSupport`, + `sample_environment`, `scatter!`, `update_index!`, `get_nsteps`, and + `base_step_seconds`. +- `meteo_inputs_` and `meteo_outputs_` declarations and validation. +- Dynamic MTG add/remove/reparent runtime reindexing. +- Domain explanation helpers: + `explain_domains`, `explain_domain_models`, `explain_domain_statuses`, + `explain_schedule`, `explain_domain_dependencies`, and `explain_routes`. + +## Implemented MAESPA-Style Example Changes + +The current `examples/maespa_domain_example.jl` is the main executable example +for multi-plant scene coupling. + +- Uses copied PlantBiophysics subsample models: + `Monteith`, `Fvcb`, and `Tuzet`. +- Uses two MTG-backed plant domains with different parameters and shared scale + names such as `:Plant` and `:Leaf`. +- Uses a shared soil model. +- Uses `SceneEB` with `ModelSpec(...) |> Calls(...)` to manually run leaf + `:energy_balance` and soil `:soil_water` targets through the current + hard-domain bridge. +- Ports MAESPA-style canopy air temperature and VPD update through + `tvpdcanopcalc` and `gbcanms`. +- Treats input meteorology as above-canopy forcing and writes below-canopy + microclimate to scene status fields: + `canopy_tair`, `canopy_vpd`, `canopy_rh`, `canopy_htot`, and + `canopy_gcanop`. +- Adds `LAIModel` and declares plant leaf-area materialization with + `ModelSpec(...) |> Inputs(...)`, bridged internally to the current route + runtime. +- Computes plant allocation daily from plant-local `leaf_carbon` vectors. +- Adds `run_call!` as the unified scene/object spelling for manually executing + current `ModelTarget` call handles. +- Adds model-level `Input(...)` and `Call(...)` dependency defaults through + `dep(model)`, with scenario-level `Inputs(...)` and `Calls(...)` overriding + those defaults in `ModelSpec`. +- Adds initial registry-backed scene selector resolution with + `resolve_object_ids` and `resolve_objects` for global, self-relative, + plant-relative, ancestor-relative, and named-scope object selections. +- Adds the first compiled scene/object view with `compile_scene`, + `CompiledScene`, `CompiledSceneApplication`, `CompiledSceneInputBinding`, + `CompiledSceneCallBinding`, `explain_scene_applications`, + `explain_bindings`, and `explain_calls`. +- The compiled scene view resolves `AppliesTo(...)`, `Inputs(...)`, and + `Calls(...)` to object ids ahead of runtime, and reports temporal policy, + window, carrier hints, and callee application ids for agent-readable + diagnostics. +- Adds status-backed compiled input carriers for the scene/object view: + scalar shared refs, homogeneous `RefVector`s, and `ObjectRefVector` fallback + carriers. `input_carrier`, `input_value`, and `has_reference_carrier` expose + them for tests, diagnostics, and future runtime execution. +- Adds scene binding cache helpers: + `refresh_bindings!`, `bindings_dirty`, `compiled_bindings`, and + `scene_revision`. Object registration, removal, and reparenting invalidate + cached compiled bindings before the next refresh. +- Adds scene/object environment binding cache helpers: + `refresh_environment_bindings!`, `compile_environment_bindings`, + `CompiledEnvironmentBinding`, `CompiledEnvironmentBindings`, + `environment_bindings_dirty`, `compiled_environment_bindings`, + `environment_revision`, and `explain_environment_bindings`. +- Adds `geometry`, `position`, and `bounds` accessors for scene objects/statuses. + Environment binding refreshes call `update_index!(backend, entities)` before + binding objects to backend cells/layers, so spatial backends can precompute + scene-wide lookup structures. +- Object movement now invalidates environment bindings without rebuilding the + structural object/model binding cache. +- Adds the first scene/object runtime with `run!(scene; steps=...)`. + It materializes compiled `Inputs(...)` carriers, samples bound environment + inputs, and executes generic model kernels on object `Status` values. +- Scene/object compiler now infers simple same-object value bindings from + `inputs_`/`outputs_` when one producer is unambiguous. `explain_bindings` + reports each binding origin, including `:declared` and + `:inferred_same_object`. +- Scene/object runtime now publishes model outputs to scene-local temporal + streams and resolves temporal `Inputs(...)` with `HoldLast`, `Integrate`, + and `Aggregate` policies before consumer execution. +- Scene/object runtime now scatters values declared by `meteo_outputs_(model)` + back to the bound environment backend after each model call, using the + existing `scatter_environment_outputs!` backend protocol. +- Scene/object root applications now honor `TimeStep(...)` values backed by + `Dates.Period` scheduling. `explain_schedule` reports normalized clocks and + whether an application is root-scheduled or manual-call-only. +- Adds `SceneRunContext` and `SceneCallTarget`; scene/object models can use + `dependency_target(s)(extra, :name)` plus `run_call!` for manual + `Calls(...)` execution. +- Applications selected by `Calls(...)` are skipped by the root + `run!(scene)` loop and execute only through explicit `run_call!`, preserving + parent-controlled hard-call execution. +- Adds scene/object duplicate-writer validation in `compile_scene`. A variable + may have only one canonical writer per object unless later writers declare + `Updates(:var; after=...)`, where `after` can match a previous application + id/name or process. +- Adds `explain_writers(compiled)` to report object-variable writer groups, + duplicate writers, and the `Updates(...)` declarations that validate ordered + updates. + +## Planned Future Breaking Redesign + +The target design is documented in: + +- `unified_scene_object_design.md` +- `unified_scene_object_implementation_plan.md` + +Expected future migration: + +- model mappings should be described as model applications: + `ModelSpec(model; name=...) |> AppliesTo(...) |> Inputs(...) |> Calls(...)`; +- `MultiScaleModel(...)` -> `Inputs(...)`. +- `Route(...)` for normal value coupling -> consumer-side `Inputs(...)`. +- `AllDomains(...)` selectors -> object selectors inside `Inputs(...)`. +- `HardDomains(...)` -> `Calls(...)`. +- `dep(model)` remains the model-level trait for default dependency intent: + defaults can become `Input(...)` value bindings or `Call(...)` manual model + calls, and scenario-level `ModelSpec` configuration overrides them. +- `Domain(...)` as user-facing assembly -> scene object templates and + instances. +- model target scales/domains -> `AppliesTo(...)` object selectors. +- `InputBindings(...)` -> source, policy, and window information on + `Inputs(...)`. +- `MeteoBindings(...)` and `MeteoWindow(...)` -> automatic environment + binding plus optional `Environment(...)` overrides. +- `OutputRouting(...)` -> model-application output policy. +- `ScopeModel(...)` -> `AppliesTo(...)` plus selector scopes. +- `PreviousTimeStep(...)` remains supported as a temporal/cycle-breaking + marker in the unified object-address graph. +- explicit per-model meteo wiring -> automatic environment resolver plus + cached environment bindings. + +Important: the future redesign is not implemented yet. Do not describe it as +released behavior until the old examples have been migrated and tests pass. diff --git a/docs/src/dev/unified_scene_object_design.md b/docs/src/dev/unified_scene_object_design.md new file mode 100644 index 000000000..f78b75e13 --- /dev/null +++ b/docs/src/dev/unified_scene_object_design.md @@ -0,0 +1,624 @@ +# Unified Scene/Object Design + +This page records the target breaking design discussed after the multi-domain +prototype. It intentionally supersedes the user-facing distinction between +`MultiScaleModel(...)` mappings and `Route(...)` cross-domain materialization. + +The central idea is: + +> Domains and scales are not fundamentally different concepts. They are both +> selections over objects in one scene. + +The engine should expose one way to say "this model input comes from these +objects" and one way to say "this model must manually call these models". The +compiler can then choose whether the runtime carrier is a `Ref`, `RefVector`, +temporal stream, route materialization, or callable model handle. + +The public API should be simple enough to remember as: + +```julia +Scene +Object +ModelSpec + +AppliesTo(...) +Inputs(...) +Calls(...) +Updates(...) +TimeStep(...) +Environment(...) +``` + +Everything else should either be a selector, a trait declared by the model +author, or an internal compiled carrier. + +## Core Concepts + +### Scene + +A `Scene` is the whole simulation universe. It contains: + +- simulated objects; +- model applications; +- environment providers; +- time/runtime state; +- caches for object selections and environment bindings. + +Plants, soil, atmosphere, microclimate grids, organs, sensors, and artificial +objects all live in the same scene-level object graph. + +### Object + +An object is any simulated entity with identity. It may have: + +- a unique object id; +- one or more labels, such as `scale=:Leaf`, `kind=:plant`, + `species=:oil_palm`; +- parent/child links; +- geometry or position; +- status variables; +- model applications. + +The engine must not prescribe a plant architecture. A plant can be described as +`Plant -> Internode -> Leaf`, `Plant -> Axis -> Segment -> Leaf`, +`Plant -> Metamer -> Organ`, or another topology. The engine only needs object +identity, labels, and relations. + +### Scale + +A scale is a label on objects, not a separate runtime layer. Examples: + +```julia +:Scene +:Plant +:Axis +:Internode +:Leaf +:Soil +:SoilLayer +:Voxel +``` + +### Scope + +A scope is a named or inferred subset of objects. Examples: + +```julia +SceneScope() +Self() +SelfPlant() +Ancestor(scale=:Plant) +Scope(:oil_palm) +Kind(:plant) +Species(:oil_palm) +``` + +`Self()` means the current model application object or scope. It does not +always mean "the current plant". If a model runs on a `:Plant`, `Self()` means +that plant object or subtree. If a model runs on an `:Axis`, it means that axis. +If a model runs on a `:Leaf`, it means that leaf. If a model runs on the scene +object, it means the scene object/scope. + +`SelfPlant()` is the nearest containing plant scope. The more generic form is +`Ancestor(scale=:Plant)`. Use these when a model running below the plant scale +must access siblings or state inside the containing plant. + +Reusable plant models should default to scope-relative queries. If an +allocation model is applied to each `:Plant`, `Many(scale=:Leaf, within=Self())` +means "the leaves inside this plant", not all leaves in the scene. The same +query applied to an axis-scale model would mean "the leaves inside this axis". + +Scene-level models widen the scope explicitly with `within=SceneScope()`. + +### Object Template And Instance + +An object template is a reusable model/parameter bundle, for example one oil +palm species model. An object instance is one concrete object in the scene. + +The same template can be mounted several times: + +```julia +oil_palm = ObjectTemplate( + kind=:plant, + species=:oil_palm, + mapping=oil_palm_mapping, + parameters=oil_palm_parameters, +) + +scene = Scene( + ObjectInstance(:palm_1, oil_palm; root=node1), + ObjectInstance(:palm_2, oil_palm; root=node2), + ObjectInstance(:palm_3, oil_palm; root=node3), + ObjectInstance(:palm_4, oil_palm; root=node4), +) +``` + +Models and parameters can be overridden at instance or object level: + +```julia +ObjectInstance(:palm_2, oil_palm; overrides=( + stomatal_conductance = Tuzet(; g1=3.2), +)) + +Override( + object=:leaf_12, + process=:photosynthesis, + model=Fvcb(; VcMaxRef=90.0), +) +``` + +### Model Kernel And Model Application + +A model kernel is the reusable model implementation written by a modeler. It +defines a process, parameters, `inputs_`, `outputs_`, optional `dep` defaults, +optional environment traits, and `run!`. + +A model application is the scenario-specific use of that kernel on selected +objects, at a selected rate, with selected value inputs, model calls, update +rules, output routing, and environment binding behavior. + +The model kernel should not need to know: + +- the species it will be used with; +- the scene it will be embedded in; +- the timestep chosen by the user; +- whether its inputs come from local state, another scale, another object, a + temporal stream, units, automatic differentiation values, or uncertainty + wrappers. + +The scenario owns those decisions through `ModelSpec`. + +Target shape: + +```julia +ModelSpec(LeafEnergyBalance(); name=:leaf_energy) |> + AppliesTo(Many(kind=:plant, scale=:Leaf)) |> + Inputs(...) |> + Calls(...) |> + TimeStep(Hour(1)) +``` + +Application names are optional but important when the same process appears more +than once in the same object set. Other declarations can target either +`process=:photosynthesis` or `name=:sunlit_photosynthesis` when a process alone +is ambiguous. + +## Unified Model Configuration + +`ModelSpec` should become the single scenario wrapper. `MultiScaleModel(...)`, +`AllDomains(...)`, `HardDomains(...)`, and user-written `Route(...)` should be +replaced by explicit value inputs and callable model calls. + +### Applies To + +Use `AppliesTo(...)` to declare the object set where a model application runs. +This should be first-class, not inferred from a domain or mapping key. + +```julia +ModelSpec(LeafState()) |> + AppliesTo(Many(kind=:plant, scale=:Leaf)) + +ModelSpec(AllocationModel()) |> + AppliesTo(Many(kind=:plant, scale=:Plant)) + +ModelSpec(SceneEB()) |> + AppliesTo(One(scale=:Scene)) +``` + +The same model kernel can be applied several times with different selectors, +parameters, timesteps, or input bindings. The compiler should normalize each +application to a stable application id. + +### Dependency Defaults From Traits + +Model authors should still declare `inputs_`, `outputs_`, and `dep`. In the +new design, `dep(model)` becomes the model-level place for default dependency +intent, for both the current `ModelMapping` use case and the future scene/object +runtime. + +The rule is: + +- `inputs_(model)` declares the variables the model needs; +- `outputs_(model)` declares the variables the model computes; +- `dep(model)` declares default value sources or manual model calls when the + model author knows a sensible coupling pattern; +- `ModelSpec(...) |> Inputs(...)` and `ModelSpec(...) |> Calls(...)` override + or specialize those defaults for a specific simulation. + +For example, a plant allocation model can provide a plant-local default: + +```julia +dep(::PlantAllocationModel) = ( + leaf_carbon = Input(Many(scale=:Leaf, within=Self(), var=:leaf_carbon)), +) +``` + +An energy-balance model can declare that it usually calls a stomatal +conductance model manually: + +```julia +dep(::LeafEnergyBalanceModel) = ( + stomatal_conductance = Call(process=:stomatal_conductance), +) +``` + +These trait defaults are not absolute wiring. They are model-author defaults +that make common cases work without repeating configuration, while scenario +authors keep final authority through `ModelSpec`. + +Compiler order: + +1. read `inputs_`, `outputs_`, and `dep`; +2. infer simple same-object value dependencies when unambiguous; +3. apply `dep(model)` defaults for value inputs and model calls; +4. apply `ModelSpec` overrides last. + +This order is part of the public contract. It keeps modeler defaults useful +without making them final wiring. Missing or ambiguous inputs after this pass +are errors, not incidental fallback behavior. + +### Value Inputs + +Use `Inputs(...)` when a model needs values before its `run!` method executes: + +```julia +ModelSpec(LAIModel(ground_area)) |> + Inputs(:leaf_areas => Many(scale=:Leaf, within=SceneScope(), var=:leaf_area)) +``` + +Reusable plant allocation: + +```julia +ModelSpec(AllocationModel()) |> + AppliesTo(Many(scale=:Plant)) |> + Inputs(:leaf_carbon => Many(scale=:Leaf, within=Self(), var=:leaf_carbon)) +``` + +The same declaration must compile to: + +- direct `Ref`/`RefVector` wiring when producer and consumer live in the same + object graph and rate; +- temporal stream reads when producer and consumer run at different rates; +- current route materialization when target status must be assigned before a + model runs; +- source-status lookup for graph-backed object selections. + +The important user rule is: + +- `Inputs(...)` means "give this model values"; the runtime schedules or + samples producers; +- the receiving model never manually calls the producer because of an + `Inputs(...)` declaration. + +### Carrier And Copy Semantics + +The compiler chooses the carrier, but the semantics must be documented and +explainable: + +| Situation | Preferred carrier | Copy behavior | +| --- | --- | --- | +| same-rate scalar input | shared `Ref` or local alias | no copy when possible | +| same-rate `Many(...)` input | `RefVector` or equivalent typed reference collection | no copy for live values | +| cross-rate input | temporal stream sample | value materialized for the consumer timestep | +| `Integrate` or `Aggregate` input | temporal window reduction | reduced value materialized | +| route-like target status input | compiler-generated materialization | assigned before consumer run | +| environment input | cached `EnvironmentBinding` sample | backend-defined value sample | + +This table is a required part of the design because performance, units, +automatic differentiation, and error propagation depend on preserving user +value types and avoiding hidden copies. + +PlantSimEngine should not force `Float64` internally. Status values, +parameters, meteo values, and outputs must be allowed to use units, dual +numbers, uncertainty wrappers, tracked arrays, or other numeric-like types. +Compiled carriers should be parametric and type stable whenever the object set +and value type are known at initialization. + +### Multirate Inputs + +Multirate must be supported by the same `Inputs(...)` declaration, not a +separate mapping language. The public time language should remain `Dates` +periods. + +Example: + +```julia +ModelSpec(PlantAllocation()) |> + AppliesTo(Many(kind=:plant, scale=:Plant)) |> + Inputs(:leaf_assimilation => Many( + scale=:Leaf, + within=Self(), + var=:assimilation, + policy=Integrate(), + window=Day(1), + )) |> + TimeStep(Day(1)) +``` + +Policy precedence should stay explicit: + +1. input-level policy in `Inputs(...)`; +2. producer `output_policy(model)`; +3. default `HoldLast()`. + +Cross-rate links must go through temporal state even when they point to objects +that could otherwise be reference-wired. + +### Model Calls + +Use `Calls(...)` when a model must manually run selected models, typically +inside an iterative solver. This is the required public API name and must be +implemented as part of the unified scene/object redesign, not left as a later +rename. + +```julia +ModelSpec(SceneEB()) |> + Calls(:leaf_energy => Many( + kind=:plant, + scale=:Leaf, + process=:energy_balance, + )) |> + Calls(:soil => One(kind=:soil, process=:soil_water)) +``` + +Inside `run!`, the scene model receives call handles and calls +`run_call!(call; publish=false)` during trial iterations, then +`publish=true` for the accepted final solution. + +The important user rule is: + +- `Calls(...)` means "give this model callable model handles"; +- the parent model owns the call stack and can iterate, reject, or accept trial + calls; +- call outputs are published only according to the call publication contract. + +### Multiplicity + +Selection multiplicity is explicit: + +```julia +One(...) +Many(...) +OptionalOne(...) +``` + +The compiler validates that `One(...)` resolves to exactly one producer per +consumer scope. `Many(...)` returns a vector-like value or target collection. + +### Address Normalization + +All source and target declarations normalize to an internal address: + +```julia +ObjectAddress( + scope, + kind, + species, + scale, + name, + process, + var, + relation, + multiplicity, +) +``` + +Only the compiler works with this normalized address. Users should not need to +construct it manually. + +## Object Lifecycle And Spatial Contracts + +Growth, pruning, organ creation, reparenting, and moving organs must all update +the same compiled caches: + +- object selections used by `AppliesTo`, `Inputs`, and `Calls`; +- `RefVector` or equivalent many-object carriers; +- temporal stream ownership; +- writer validation; +- environment bindings. + +The public mutation API should make cache invalidation explicit and centralized: + +```julia +register_object!(scene, object; parent) +remove_object!(scene, object) +reparent_object!(scene, object, new_parent) +move_object!(scene, object, geometry_or_position) +refresh_bindings!(scene) +``` + +Spatial environment backends should depend on a small geometry contract, not on +a particular plant representation: + +```julia +position(object_or_status) +geometry(object_or_status) +bounds(object_or_status) +``` + +Packages can provide richer geometry, octrees, voxel grids, or layers, but +PlantSimEngine should only require enough information to bind an object to an +environment provider. + +## Duplicate Writers And Updates + +Most variables should have one canonical writer per object and timestep. When a +variable is intentionally updated by several models, the scenario should say so +where the model applications are assembled: + +```julia +ModelSpec(PruningModel()) |> + AppliesTo(Many(scale=:Leaf)) |> + Updates(:leaf_biomass; after=:carbon_allocation) +``` + +`Updates(...)` should be rare and explicit. It is a scenario-level ordering +rule, because a model author cannot predict every model that will later update +the same variable. + +## Environment And Microclimate + +Meteorology should remain automatic unless a model or scenario needs special +behavior. Models declare environment variables: + +```julia +meteo_inputs_(::LeafEnergyModel) = ( + T=0.0, + Rh=0.0, + Wind=0.0, + Ri_PAR_f=0.0, + CO2=0.0, +) +``` + +The runtime resolves those variables through the scene environment service. + +Default resolution: + +1. A global/table meteo backend gives every object the current meteo row. +2. A voxel, octree, layered, or grid backend samples the cell bound to the + object. +3. If the object has no position, use the parent position. +4. If no spatial binding can be made, fall back to global meteo or error when + the environment variable is required. + +Users can override the binding contract: + +```julia +EnvironmentResolver( + bind=(scene, object) -> containing_cell(scene.microclimate, position(object)), +) +``` + +PlantSimEngine should define the protocol and caching hooks, not the voxel or +octree implementation. Specialized packages should provide concrete spatial +backends. + +The environment backend protocol should be small and backend-oriented: + +```julia +bind_environment(scene, backend, object) +sample_environment(backend, binding, time, variables) +scatter_environment!(backend, binding, values) +refresh_environment!(backend, scene) +``` + +`meteo_inputs_(model)` declares what a model reads from the active environment +provider. `meteo_outputs_(model)` declares what a model can write back to a +mutable microclimate provider. Simple global meteorology remains the default +provider. + +### Cached Environment Bindings + +Spatial lookup must not happen for every model call. At initialization and when +objects are created, the runtime builds an environment binding cache: + +```julia +EnvironmentBinding( + object_id, + provider=:microclimate_grid, + cell_id, + variables=(:T, :Rh, :Wind, :Ri_PAR_f), +) +``` + +Runtime sampling is: + +```text +object -> cached binding -> environment cell -> current values +``` + +Invalidation events: + +- object created; +- object removed; +- object moved; +- geometry changed; +- environment grid rebuilt or refined; +- model environment requirements changed. + +Geometry APIs should provide ergonomic invalidation: + +```julia +mark_environment_binding_dirty!(scene, object) +update_geometry!(object, geometry; invalidate_environment=true) +``` + +Before each timestep, dirty bindings are refreshed in batch. + +## Compilation Strategy + +The compiler should build one global dependency graph over object addresses. +The graph includes: + +- value dependencies from `Inputs(...)`; +- callable dependencies from `Calls(...)`; +- model update edges from `Updates(...)`; +- temporal policy edges; +- environment reads and writes; +- object-scope selection caches. + +The runtime representation is an implementation detail: + +- same-rate local links can stay as aliases; +- cross-rate links use temporal state; +- many-object links use `RefVector` or node-value streams; +- call links use `ModelCall` or an equivalent callable runtime handle; +- environment links use cached `EnvironmentBinding`s. + +The public explanation API must describe the normalized graph, not the internal +carrier choice. + +## Agent-Facing Requirements + +The final design must be understandable by agents through structured +explanation helpers: + +```julia +explain_objects(scene) +explain_scopes(scene) +explain_bindings(sim) +explain_calls(sim) +explain_environment_bindings(sim) +explain_schedule(sim) +explain_writers(sim) +``` + +These helpers should return stable structured data, not only pretty text. A +binding row should include at least: + +- consumer application id; +- consumer object id; +- consumer variable; +- source selector; +- resolved producer application id or environment provider id; +- resolved producer object ids; +- process/name filters; +- temporal policy and window; +- carrier kind; +- copy/reference semantics; +- reason the binding was chosen; +- whether it came from inference, `dep(model)`, or `ModelSpec`. + +Errors should report concrete object labels, scope selectors, process names, +variables, and suggested fixes. + +## Compatibility Position + +This is a breaking target design. It should preserve model kernels and the +`run!(model, models, status, meteo, constants, extra)` contract when possible, +but it may replace the scenario configuration surface: + +- `MultiScaleModel(...)` becomes `Inputs(...)`; +- `Route(...)` becomes a compiler-generated carrier for `Inputs(...)`; +- `AllDomains(...)` becomes a selector used inside `Inputs(...)`; +- `HardDomains(...)` becomes `Calls(...)`; +- `Domain(...)` becomes an object scope/template/instance concept; +- `InputBindings(...)` becomes explicit policy and source information on + `Inputs(...)`; +- `MeteoBindings(...)` becomes automatic environment binding plus optional + `Environment(...)` overrides; +- `OutputRouting(...)` remains model-application output configuration or is + folded into a clearer output policy modifier; +- `PreviousTimeStep(...)` remains a temporal policy/cycle-breaking marker in + the unified graph. diff --git a/docs/src/dev/unified_scene_object_implementation_plan.md b/docs/src/dev/unified_scene_object_implementation_plan.md new file mode 100644 index 000000000..8dc12a6e4 --- /dev/null +++ b/docs/src/dev/unified_scene_object_implementation_plan.md @@ -0,0 +1,646 @@ +# Unified Scene/Object Implementation Plan + +This plan is the persistent handoff for replacing the current domain/route and +multiscale-mapping split with one scene/object address system. + +The implementation can be incremental internally, but the target API is +breaking. Do not try to preserve `MultiScaleModel(...)`, `Route(...)`, +`AllDomains(...)`, or `HardDomains(...)` as primary user-facing concepts in the +final design. + +The target public surface should be centered on a small set of concepts: + +```julia +Scene +Object +ModelSpec + +AppliesTo(...) +Inputs(...) +Calls(...) +Updates(...) +TimeStep(...) +Environment(...) +``` + +This is the API memory target for users, modelers, and agents. Additional +types should be selectors, model traits, or internal compiled carriers. + +## Current State To Preserve Until Replacement + +The current experimental branch already implements useful behavior: + +- `Domain`, `SimulationMapping`, and `DomainSimulation`; +- single-status and MTG-backed domains; +- graph-domain selectors for several plant instances/species; +- `Route(...)` materialization into scene status; +- `AllDomains(...)` stream/value dependencies; +- `HardDomains(...)` targets and `run_target!`; +- multi-rate domain scheduling with `Dates`; +- environment backend protocol and constant meteo backend; +- dynamic MTG add/remove/reparent support; +- `Updates(...)` for ordered duplicate writers; +- structured domain explanation helpers. + +Those features are the behavioral test bed for the new design. The new API +should reproduce the same capabilities through unified object selections. + +## Implementation Progress + +- Started Phase 0 by adding the public API vocabulary as real typed metadata: + `AppliesTo(...)`, `Inputs(...)`, `Calls(...)`, `TimeStep(...)`, and + `Environment(...)` can now be applied to `ModelSpec`. +- Added `ModelSpec(model; name=...)` application names and getters: + `application_name`, `applies_to`, `value_inputs`, `model_calls`, and + `environment_config`. +- Added initial selector and address types: + `SceneScope`, `Self`, `SelfPlant`, `Ancestor`, `Scope`, `Kind`, `Species`, + `Scale`, `Relation`, `One`, `OptionalOne`, `Many`, and `ObjectAddress`. +- Added initial `Scene`/`Object` registry types and lifecycle hooks: + `register_object!`, `remove_object!`, `reparent_object!`, `move_object!`, + and `refresh_bindings!`. +- Added registry-backed selector resolution with `resolve_object_ids` and + `resolve_objects` for `SceneScope()`, `Self()`, `SelfPlant()`, + `Ancestor(...)`, `Scope(...)`, positional selectors such as + `Kind(:plant)`/`Scale(:Leaf)`, and `One`/`OptionalOne`/`Many` cardinality + checks. +- Started the object-address compiler with `compile_scene(scene, specs)` and + compiled scene application/binding carriers. The compiler now resolves + `AppliesTo(...)` target object ids, object-relative `Inputs(...)` source + object ids, and object-relative `Calls(...)` callee object/application ids + before runtime. +- Added `explain_scene_applications`, `explain_bindings`, and `explain_calls` + for the compiled scene view. These explanations expose application ids, + processes, target ids, input source ids, call callee ids, temporal policy, + window, and carrier hints. +- Added status-backed compiled input carriers. When source objects already + hold `Status` values, `Inputs(...)` bindings now precompile a scalar shared + `Ref`, a homogeneous `RefVector`, or an `ObjectRefVector` fallback for + heterogeneous reference-preserving vectors. `input_carrier`, `input_value`, + and `has_reference_carrier` expose these carriers, and `explain_bindings` + reports carrier type and reference availability. +- Added conservative same-object input inference in the scene compiler. When a + model declares an `inputs_` variable that is not covered by explicit/default + `Inputs(...)`, and exactly one other application on the same object outputs + the same variable, `compile_scene` creates an inferred reference binding. + `explain_bindings` now reports binding `origin` values such as + `:declared` and `:inferred_same_object`. +- Carrier compilation preserves source `Status` references and arbitrary value + types; tests cover both scalar refs and many-object vectors with a custom + non-`Float64` value type. +- Added call ambiguity validation in the compiled scene view: a call can select + by process when unique, and must use `application=:name` when several model + applications with the same process match the same object. +- Added a scene binding cache with `refresh_bindings!`, `bindings_dirty`, + `compiled_bindings`, and `scene_revision`. Object creation, removal, + and reparenting now invalidate the compiled binding cache and bump a scene + revision before the next refresh. +- Added an environment binding cache with `refresh_environment_bindings!`, + `compile_environment_bindings`, `CompiledEnvironmentBinding`, + `CompiledEnvironmentBindings`, `environment_bindings_dirty`, + `compiled_environment_bindings`, `environment_revision`, and + `explain_environment_bindings`. The compiler resolves each + application/object environment provider, backend, required + `meteo_inputs_`, produced `meteo_outputs_`, support descriptor, and backend + cell before runtime. +- Added the minimal scene geometry contract: `geometry(object_or_status)`, + `position(object_or_status)`, and `bounds(object_or_status)`. Environment + binding refreshes now call `update_index!(backend, entities)` once per + distinct backend before `bind_environment`, giving spatial backends a current + scene-wide object/entity list for precomputed microclimate lookup. +- Object creation, removal, and reparenting invalidate both structural and + environment bindings. Object movement invalidates only environment bindings, + so moving a leaf or changing its geometry can refresh microclimate lookup + without rebuilding object/model binding carriers. +- Started scene/object execution with `run!(scene; steps=...)`. + The runtime refreshes compiled object bindings and environment bindings, + materializes precompiled `Inputs(...)` carriers into consumer `Status` + fields, samples the bound environment backend, and calls generic model + kernels through the existing `run!` contract. +- Scene/object execution now publishes model outputs to scene-local temporal + streams. Compiled `Inputs(...)` bindings marked as `:temporal_stream` can + materialize `HoldLast`, `Integrate`, and `Aggregate` values before the + consumer runs, using selector source ids, source variables, windows, and the + scene base timestep. +- Scene/object execution now scatters mutable environment outputs declared by + `meteo_outputs_(model)` back to the bound backend after each model call. + This reuses `scatter_environment_outputs!`, so environment writers keep the + existing generic model contract: compute a same-named status value, and let + the runtime push it to the active microclimate backend. +- Added root application scheduling from `TimeStep(...)` using `Dates.Period` + values and the scene environment base step. `explain_schedule` on a + `CompiledScene` now reports each application clock, phase, timestep in base + steps, timestep duration in seconds, and whether the application is scheduled + as a root application or is manual-call-only. +- Added `SceneRunContext` and `SceneCallTarget`. Models can retrieve manual + `Calls(...)` targets with `dependency_target(s)(extra, :name)` and execute + them with `run_call!`, preserving explicit call-stack control in the + scene/object runtime. Manual calls execute immediately under the parent call + stack; applications selected by `Calls(...)` are skipped by the root + `run!(scene)` loop and only execute through `run_call!`. +- Added scene/object duplicate-writer validation. During `compile_scene`, each + `(object, output variable)` now has one canonical writer unless later + writers declare `Updates(:var; after=...)`. The `after` token can match a + previous application id/name or process, so scenario authors can express + cases such as pruning after carbon allocation without changing either model + implementation. +- Added `explain_writers(compiled)`. It reports each object/variable writer + group, duplicate-writer status, writer application ids/processes, and the + `Updates(...)` declarations used to validate ordered updates. +- Extended `explain_model_specs` rows with application name, target selector, + value inputs, manual calls, and environment metadata. +- Started Phase 3 by bridging simple `Inputs(...)` declarations into the + existing MTG multiscale mapping carrier when the selector is representable as + a pure scale/variable mapping, for example + `Inputs(:x => Many(scale=:Leaf, var=:y))`. +- Added model-level `Input(...)` defaults from `dep(model)` into + `ModelSpec` value inputs. Scenario-level `ModelSpec(...) |> Inputs(...)` + overrides those defaults and also replaces the corresponding internal legacy + mapping carrier for the same input. +- Extended the Phase 3 bridge for domain simulations by generating internal + `Route(...)` carriers from supported consumer-side `Inputs(...)` + declarations, for example + `Inputs(:leaf_areas => Many(kind=:plant, scale=:Leaf, process=:leaf_state, var=:leaf_area))`. +- Started Phase 4 by bridging `Calls(...)` declarations into the current + hard-domain dependency resolver when selectors can be represented by + `kind`, `domain`, `scale`, and `process`. Added `run_call!` as the unified + spelling over the current `ModelTarget` execution path. +- Added model-level `Call(...)` defaults from `dep(model)` into + `ModelSpec` manual-call metadata. Scenario-level + `ModelSpec(...) |> Calls(...)` overrides those defaults, and + `dep(::ModelSpec)` excludes raw `Call(...)` trait entries so default calls + are normalized through the same bridge as explicit calls. +- Migrated the MAESPA example's scene energy-balance hard calls from + model-level `HardDomains(...)` to scenario-level + `ModelSpec(scene_model) |> Calls(...)`. +- Migrated the MAESPA example's scene LAI leaf-area route from user-written + `Route(...)` to consumer-side `ModelSpec(LAIModel(...)) |> Inputs(...)`. + +This progress is still a bridge over the existing compiler, not the final +object-address compiler. Supported `Inputs(...)` and `Calls(...)` selectors are +translated to current carriers where possible. Unsupported object-relative +selectors remain structured metadata until the object-address compiler lands. + +## Phase 0: Public Contract Freeze + +Goal: decide the small public vocabulary before implementing internals. + +Define: + +- `ModelSpec(model; name=nothing)` as the model-application wrapper. +- `AppliesTo(selector)` as the target object-set declaration. +- `Inputs(...)` for value dependencies. +- `Calls(...)` for manual call-stack dependencies. +- `Updates(...)` for rare ordered duplicate writers. +- `TimeStep(period::Dates.Period)` and related multirate policies. +- `Environment(...)` for optional environment resolver/backend overrides. + +Rules: + +- a model kernel remains generic and declares `inputs_`, `outputs_`, optional + `dep`, optional `meteo_inputs_`/`meteo_outputs_`, and `run!`; +- a model application decides where the kernel runs, at what rate, and how its + inputs, calls, updates, outputs, and environment are bound; +- application ids are stable and can be generated from explicit `name`, + process, object selector, and occurrence index; +- if several applications provide the same process on the same object set, + selectors must disambiguate by application name or another explicit filter. + +Acceptance tests: + +- a model can be applied twice to the same leaf objects with different names; +- a dependency selector can choose by process when unique and by name when not; +- structured explanations expose model kernel type, process, application name, + and target object ids. + +## Phase 1: Scene Object Registry + +Goal: introduce the internal object model without changing public behavior yet. + +Implement: + +- `ObjectId` as the stable identity key for every runtime object. +- `SceneObject` metadata with labels: + `scale`, `kind`, `species`, optional `name`, parent id, child ids, and + optional geometry/position handle. +- `SceneRegistry` storing objects, parent/child relations, and indexes by + label. +- adapters from the current MTG/domain state into the registry: + each selected domain root and each MTG node gets an object id; + single-status domains get one object with `scale=:Default`. +- object lifecycle hooks for add/remove/reparent that mirror the existing MTG + runtime reindexing. + +Acceptance tests: + +- the MAESPA example registers five leaf objects, two plant objects, one soil + object, and one scene object; +- `status(sim, :plant_A, :Leaf)` and `status(sim, :Leaf)` can be expressed as + registry queries; +- add/remove/reparent updates object registry relations and status views. + +## Phase 2: Selector And Scope Language + +Goal: make "which objects?" explicit and reusable. + +Implement selector types: + +```julia +SceneScope() +Self() +SelfPlant() +Ancestor(scale=:Plant) +Scope(name) +Kind(kind) +Species(species) +Scale(scale) +Relation(...) +``` + +Implement multiplicity wrappers: + +```julia +One(selector...) +OptionalOne(selector...) +Many(selector...) +``` + +Selectors must normalize to `ObjectAddress` objects with enough context to be +resolved relative to a consuming object. + +Implement `AppliesTo(...)` using the same selector system. The target object +set of a model application must never be hidden inside a domain key, mapping +key, or implicit scale table. + +Definitions: + +- `Self()` means the current model application object or scope. It is the + current plant only when the model is applied at `scale=:Plant`. +- `SelfPlant()` means the nearest containing plant scope. +- `Ancestor(scale=:Plant)` is the generic selector form for `SelfPlant()`. +- `SceneScope()` means the whole scene. +- `Scope(name)` means a named scope or object collection. + +Rules: + +- unqualified selectors inside a reusable plant mapping default to + `within=Self()`; +- scene-level selectors default to `within=SceneScope()`; +- `One(...)` errors unless exactly one object resolves per consumer; +- `Many(...)` preserves stable object-id order; +- object-id order replaces incidental traversal order as the semantic default. +- selectors are resolved during compilation or binding refresh, not inside the + inner model loop. + +Acceptance tests: + +- plant allocation on four oil palms reads only leaves under each plant; +- scene LAI reads leaves across all plant objects; +- a species-specific scene model can read only `species=:oil_palm` leaves; +- a model application target set declared with `AppliesTo(...)` produces stable + application/object pairs; +- selector errors report available labels and near matches. + +## Phase 3: Unified Value Inputs + +Goal: replace `MultiScaleModel(...)` and user-written `Route(...)` with +`Inputs(...)`, while using the existing `dep` trait as the model-level source +of default dependency intent. + +Target API: + +```julia +ModelSpec(AllocationModel()) |> + AppliesTo(Many(kind=:plant, scale=:Plant)) |> + Inputs(:leaf_carbon => Many(scale=:Leaf, within=Self(), var=:leaf_carbon)) + +ModelSpec(LAIModel(area)) |> + AppliesTo(One(scale=:Scene)) |> + Inputs(:leaf_areas => Many(kind=:plant, scale=:Leaf, within=SceneScope(), var=:leaf_area)) +``` + +Implement: + +- `Inputs(...)` as `ModelSpec` configuration. +- `Input(...)` or an equivalent internal wrapper that lets `dep(model)` + provide default value-input bindings. +- normalized input bindings from target variable to `ObjectAddress`. +- compiler pass that decides carrier: + direct reference, `RefVector`, temporal stream, or route-like + materialization. +- status-default insertion for materialized target variables using the + consumer model's `inputs_` default. +- temporal policies on value inputs: + `HoldLast`, `Interpolate`, `Integrate`, `Aggregate`. +- `Dates.Period` windows on value inputs, for example `window=Day(1)`. +- copy/reference semantics reporting for every compiled input binding. + +Rules: + +- model authors still declare `inputs_`; scenario authors decide where those + inputs come from; +- `dep(model)` may provide defaults for common value-input bindings, for both + current `ModelMapping`-style composition and future scene/object composition; +- scenario-level `ModelSpec(...) |> Inputs(...)` always wins over `dep(model)` + defaults; +- same-rate local links should keep reference semantics where possible; +- cross-rate links always go through temporal state; +- duplicate source candidates are errors unless the selector disambiguates; +- route structs may remain internally but should not be required in examples. +- same-rate scalar and many-object links should avoid copies when they can use + aliases, shared refs, `RefVector`, or an equivalent typed carrier; +- PlantSimEngine must preserve arbitrary value types, including units, + automatic differentiation numbers, uncertainty wrappers, and other + numeric-like values. + +Carrier expectations: + +| Binding kind | Runtime carrier | +| --- | --- | +| same-rate scalar | shared `Ref` or local alias | +| same-rate many-object | `RefVector` or equivalent typed reference collection | +| cross-rate | temporal stream sample | +| integrate/aggregate | temporal window reduction | +| materialized route | generated pre-run status assignment | +| environment | cached environment binding sample | + +Acceptance tests: + +- the MAESPA scene LAI route becomes an `Inputs(...)` declaration and produces + the same `lai` and `leaf_area`; +- current plant allocation `MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]])` + becomes `Inputs(...)` and remains plant-local; +- a same-scale rename currently expressed with `SameScale()` works through + `Inputs(...)`; +- multi-rate value inputs integrate graph-domain node streams by object id. +- same-rate many-object bindings do not allocate per timestep in a benchmarked + hot loop beyond unavoidable model work. +- unitful or dual-number status values survive `Inputs(...)` without forced + conversion to `Float64`. + +## Phase 4: Unified Model Calls + +Goal: replace `HardDomains(...)` with `Calls(...)`. `Calls(...)` is the +required public API for manually controlled model execution and must be +implemented in this phase, not deferred to a future rename. The same mechanism +must also be usable from `dep(model)` so current hard-dependency traits become +default call declarations. + +Target API: + +```julia +ModelSpec(SceneEB()) |> + AppliesTo(One(scale=:Scene)) |> + Calls(:leaf_energy => Many(kind=:plant, scale=:Leaf, process=:energy_balance)) |> + Calls(:soil => One(kind=:soil, process=:soil_water)) +``` + +Implement: + +- `Calls(...)` as `ModelSpec` configuration. +- `Call(...)` or an equivalent internal wrapper that lets `dep(model)` provide + default manual-call dependencies. +- call resolution from `ObjectAddress` to concrete `ModelCall` handles, or an + equivalent callable runtime object if the final internal type name differs. +- same-status hard dependency calls using the same public API. +- publication semantics: + trial `run_call!(call)` mutates status only; + final `run_call!(call; publish=true)` appends outputs and temporal + streams. +- structured call explanations with parent application id, selected callee + application ids, selected object ids, selector, and publication behavior. + +Rules: + +- calls are manual call-stack dependencies and are not independently + scheduled under the parent; +- `dep(model)` call defaults are model-author defaults, not final wiring; +- scenario-level `ModelSpec(...) |> Calls(...)` overrides `dep(model)` defaults; +- hard target outputs still participate in dependency graph compilation through + the owning parent when needed; +- call selection must be visible through explanation helpers. + +Acceptance tests: + +- MAESPA scene energy balance uses `Calls(...)` and still controls iterative + leaf energy calls; +- missing call selectors report `kind`, `scale`, `process`, and available + matches; +- final accepted calls publish exactly once per timestep. +- an iterative scene model can run selected leaf and soil calls several times + with `publish=false` and publish only the accepted state. + +## Phase 5: Object Templates, Instances, And Overrides + +Goal: support several plants of the same species with shared default models and +selective per-instance differences. + +Target API: + +```julia +oil_palm = ObjectTemplate( + kind=:plant, + species=:oil_palm, + mapping=oil_palm_mapping, +) + +scene = Scene( + ObjectInstance(:palm_1, oil_palm; root=node1), + ObjectInstance(:palm_2, oil_palm; root=node2, overrides=( + stomatal_conductance = Tuzet(; g1=3.2), + )), +) +``` + +Implement: + +- template-level model specs and parameters; +- instance-level model/parameter overrides by process; +- object-level overrides for exceptional organs; +- conflict validation when two overrides target the same process/object. +- shared parameter/model storage when template instances do not override + anything, with explicit copy/ownership behavior when they do. + +Rules: + +- templates do not prescribe topology; they attach mappings to whatever object + tree the instance provides; +- default `Self()` selectors resolve inside the current instance; +- scene-wide models must opt into wider scope. + +Acceptance tests: + +- four oil palm instances share model objects/parameters when not overridden; +- one palm instance can override one process parameter; +- allocation remains per plant while scene LAI sees all leaves. + +## Phase 5B: Object Lifecycle And Cache Invalidation + +Goal: make growth, pruning, and moving organs update every compiled binding +through one mutation path. + +Implement public lifecycle hooks: + +```julia +register_object!(scene, object; parent) +remove_object!(scene, object) +reparent_object!(scene, object, new_parent) +move_object!(scene, object, geometry_or_position) +refresh_bindings!(scene) +``` + +Implement invalidation for: + +- object selector caches; +- model application target sets; +- `RefVector` or equivalent many-object carriers; +- temporal stream ownership; +- writer validation; +- environment bindings. + +Rules: + +- topology and geometry changes do not silently leave stale carriers; +- object creation should bind the new object to model applications selected by + `AppliesTo(...)` before the next timestep; +- moving an object should refresh environment bindings without rebuilding + unrelated model bindings unless the move changes object relations or labels. + +Acceptance tests: + +- creating a new leaf adds it to plant-local allocation and scene LAI before + the next timestep; +- pruning/removing a leaf removes it from many-object carriers and temporal + stream ownership; +- changing a leaf insertion angle can refresh only the affected environment + binding when topology is unchanged. + +## Phase 6: Environment Binding Cache + +Goal: make meteo/microclimate automatic and fast. + +Implement: + +- `EnvironmentBinding` cache: + object id, backend/provider id, cell/layer id, required variables. +- default environment resolver: + global meteo for non-spatial backends; + object position for spatial backends; + parent position fallback; + global fallback or validation error. +- dirty flags and batched refresh: + `mark_environment_binding_dirty!`; + `update_geometry!(...; invalidate_environment=true)`; + automatic dirty marking on object creation, removal, reparenting, and + environment grid rebuild. +- explanation helper: + `explain_environment_bindings(sim)`. +- minimal geometry accessors or traits: + `position`, `geometry`, and `bounds`. +- backend protocol: + `bind_environment`, `sample_environment`, `scatter_environment!`, and + `refresh_environment!`. +- `Environment(...)` overrides for scenario-specific resolver/backend choices. + +Runtime rule: + +```text +object -> cached binding -> backend cell/layer -> current meteo values +``` + +Spatial lookup must happen only during binding refresh, not inside every model +call. + +Acceptance tests: + +- global meteo gives the same values to all objects; +- mock grid backend binds leaves to cells once at initialization; +- moving one leaf marks only that leaf binding dirty and refreshes it before + the next timestep; +- model `meteo_inputs_` changes update required variables without recomputing + spatial links unless necessary. +- `meteo_outputs_` can write mutable microclimate variables back to the active + backend through `scatter_environment!`. + +## Phase 7: Compiler, Scheduler, And Explanation Cleanup + +Goal: make the unified graph the source of truth. + +Implement: + +- one compiler that builds a global dependency graph over object addresses; +- route/materialization and multiscale reference wiring as internal carriers; +- domain DAG scheduling replaced by object/scope dependency scheduling; +- writer validation through the same graph, including `Updates(...)`; +- model application scheduling from `AppliesTo(...)` target sets; +- multirate scheduling based on `Dates.Period` values in `TimeStep(...)` and + input windows; +- typed compiled bindings that avoid selector resolution in timestep hot loops; +- arbitrary value type preservation through status, input carriers, temporal + storage, and environment samples; +- structured explanation: + `explain_objects`, `explain_scopes`, `explain_bindings`, + `explain_calls`, `explain_environment_bindings`, `explain_schedule`, + `explain_writers`. + +Acceptance tests: + +- old `MultiScaleModel` examples rewritten with `Inputs(...)` produce matching + outputs; +- old `Route(...)` examples rewritten with `Inputs(...)` produce matching + outputs; +- MAESPA hard-call example rewritten with `Calls(...)` produces matching + outputs; +- explanation helpers include enough concrete object ids, scales, processes, + and variables for an AI agent to repair bad mappings. +- `explain_bindings(sim)` reports whether each dependency came from inference, + `dep(model)`, or `ModelSpec`, and reports carrier/copy semantics. +- no selector resolution occurs inside the per-object, per-model timestep loop + for static scenes. +- multirate simulations use the same object-address graph as same-rate + simulations. + +## Phase 8: Breaking API Removal And Migration Docs + +Goal: remove the old configuration surface once parity is proven. + +Remove or demote: + +- `MultiScaleModel(...)` as public scenario configuration; +- public `Route(...)` authoring for normal value inputs; +- `AllDomains(...)` and `HardDomains(...)` as primary user API; +- `Domain(...)` as a user-facing container when object templates/instances are + available. + +Write migration notes: + +- `MultiScaleModel([:x => [:Leaf => :y]])` -> `Inputs(:x => Many(scale=:Leaf, var=:y))`; +- `Route(from=AllDomains(...), to=...)` -> consumer `Inputs(...)`; +- `HardDomains(...)` -> `Calls(...)`; +- repeated species domains -> `ObjectTemplate` plus `ObjectInstance`; +- explicit meteo routes -> environment resolver/binding backend. +- `InputBindings(...)` -> source and temporal policy information inside + `Inputs(...)`; +- `MeteoBindings(...)` and `MeteoWindow(...)` -> `Environment(...)` and + environment sampling/window policy; +- `OutputRouting(...)` -> model-application output policy; +- `PreviousTimeStep(...)` -> temporal policy/cycle-breaking marker in the + unified graph; +- `ScopeModel(...)` -> `AppliesTo(...)` plus selector scope. + +Regression tests must cover all migrated examples before removal. + +## Open Decisions + +- Final names for `SceneScope`, `Self`, `SelfPlant`, `Kind`, `Species`, and + `Scale`. +- Whether `Inputs(...)` should be a pipeable `ModelSpec` modifier only, or also + accepted as a keyword in `ModelSpec`. +- Whether `TimeStep(...)` is the final name, or whether the existing + `TimeStepModel(...)` should be kept as an alias during the transition. +- Whether `Environment(...)` owns only backend/resolver choices or also + per-model meteo window policies. +- Whether object templates should own topology construction helpers or only + consume prebuilt MTGs/object trees. +- How much of the old API remains as compatibility wrappers after the breaking + release. diff --git a/examples/maespa_domain_example.jl b/examples/maespa_domain_example.jl index bb879cafa..e9822168e 100644 --- a/examples/maespa_domain_example.jl +++ b/examples/maespa_domain_example.jl @@ -117,10 +117,6 @@ function SceneEB( ) end -PlantSimEngine.dep(::SceneEB) = ( - energy_balance=HardDomains(kind=:plant, scale=:Leaf, process=:energy_balance), - soil=HardDomains(kind=:soil, process=:soil_water), -) PlantSimEngine.inputs_(::SceneEB) = (lai=0.0, leaf_area=0.0) PlantSimEngine.outputs_(::SceneEB) = ( canopy_tair=20.0, @@ -174,7 +170,7 @@ function _run_scene_leaf_targets!(leaf_targets, meteo, tair_canopy, vpd_canopy, local_meteo = _scene_leaf_meteo(meteo, tair_canopy, vpd_canopy) for target in leaf_targets _prepare_scene_leaf_target!(target, meteo, tair_canopy, vpd_canopy, psi_soil) - run_target!(target; meteo=local_meteo, publish=publish) # Publish is false because we iterate here and only want to publish the final solution at the end + run_call!(target; meteo=local_meteo, publish=publish) # Publish is false because we iterate here and only want to publish the final solution at the end leaf_area = target.status.leaf_area total_rn += target.status.Rn * leaf_area total_lambda_e += target.status.λE * leaf_area @@ -310,7 +306,7 @@ end function _run_scene_soil_feedback!(soil_target, transpiration_mm) soil_target.status.transpiration = transpiration_mm soil_target.status.infiltration = 0.0 - run_target!(soil_target; publish=true) + run_call!(soil_target; publish=true) return soil_target.status.psi_soil end @@ -432,8 +428,15 @@ function maespa_mapping(; scene_model=SceneEB(25, 0.03, 0.005)) ) ground_area = scene_model.ground_area scene = ModelMapping( - ModelSpec(LAIModel(ground_area)) |> TimeStepModel(Dates.Day(1)), - ModelSpec(scene_model) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(LAIModel(ground_area)) |> + Inputs(:leaf_areas => Many(kind=:plant, scale=:Leaf, process=:leaf_state, var=:leaf_area)) |> + TimeStep(Dates.Day(1)), + ModelSpec(scene_model) |> + Calls( + :energy_balance => Many(kind=:plant, scale=:Leaf, process=:energy_balance), + :soil => One(kind=:soil, process=:soil_water), + ) |> + TimeStep(Dates.Hour(1)), status=( leaf_area=0.0, lai=0.0, @@ -448,18 +451,11 @@ function maespa_mapping(; scene_model=SceneEB(25, 0.03, 0.005)) iterations=0, ), ) - leaf_area_route = Route( - from=AllDomains(kind=:plant, scale=:Leaf, process=:leaf_state, var=:leaf_area), - to=DomainRouteTarget(:scene, var=:leaf_areas, process=:lai_dynamic), - cardinality=ManyToOneVector(), - ) - return SimulationMapping( Domain(:plant_A, plant_a; kind=:plant, selector=node -> MultiScaleTreeGraph.symbol(node) == :Plant && has_species(node, :A)), Domain(:plant_B, plant_b; kind=:plant, selector=node -> MultiScaleTreeGraph.symbol(node) == :Plant && has_species(node, :B)), Domain(:soil, soil; kind=:soil), - Domain(:scene, scene; kind=:scene); - routes=(leaf_area_route,), + Domain(:scene, scene; kind=:scene), ) end diff --git a/src/PlantSimEngine.jl b/src/PlantSimEngine.jl index 6a383ff70..5625c7198 100644 --- a/src/PlantSimEngine.jl +++ b/src/PlantSimEngine.jl @@ -12,6 +12,7 @@ import CSV # For reading csv files with variables() import AbstractTrees import Term import Markdown +import Base: position # For multi-threading: import FLoops: @floop, @init, ThreadedEx, SequentialEx, DistributedEx @@ -45,6 +46,9 @@ include("time/multirate.jl") include("component_models/Status.jl") include("component_models/RefVector.jl") +# Unified scene/object API: +include("scene_object_api.jl") + # Simulation table (time-step table, from PlantMeteo): include("component_models/TimeStepTable.jl") @@ -131,12 +135,28 @@ export OutputCache, HoldLastCache, InterpolateCache, IntegrateCache, AggregateCa export TemporalState export OutputRequest, collect_outputs export effective_rate_summary +export Scene, Object, ObjectId, SceneRegistry +export register_object!, remove_object!, reparent_object!, move_object!, refresh_bindings! +export bindings_dirty, environment_bindings_dirty, scene_revision, environment_revision +export compiled_bindings, compiled_environment_bindings, mark_environment_binding_dirty! +export refresh_environment_bindings!, compile_environment_bindings, bind_environment +export object_ids, scene_objects, resolve_object_ids, resolve_objects, explain_objects +export geometry, position, bounds +export CompiledScene, CompiledSceneApplication, CompiledSceneInputBinding, CompiledSceneCallBinding +export compile_scene, explain_scene_applications, explain_bindings, explain_calls, explain_writers +export ObjectRefVector, input_carrier, input_value, has_reference_carrier +export SceneRunContext, SceneCallTarget +export CompiledEnvironmentBinding, CompiledEnvironmentBindings, explain_environment_bindings +export SceneScope, Self, SelfPlant, Ancestor, Scope, Kind, Species, Scale, Relation +export One, OptionalOne, Many, ObjectAddress, object_address +export Input, Call, AppliesTo, Inputs, Calls, TimeStep, Environment +export application_name, applies_to, value_inputs, model_calls, environment_config export MultiScaleModel, ModelMapping, ModelSpec, SameScale, Updates, TimeStepModel, InputBindings, MeteoBindings, MeteoWindow, OutputRouting, ScopeModel export resolved_model_specs, explain_model_specs export Domain, SimulationMapping, DomainSimulation, DomainModelKey, AllDomains, HardDomains export Route, DomainRouteTarget, RouteCardinality export ManyToOneVector, ManyToOneAggregate, OneToManyBroadcast, SpatialSample, SpatialScatterAdd -export dependency_values, dependency_targets, dependency_target, model_target, run_target!, ModelTarget, explain_domains, explain_domain_models, explain_domain_statuses, explain_schedule, explain_domain_dependencies, explain_routes +export dependency_values, dependency_targets, dependency_target, model_target, run_target!, run_call!, ModelTarget, explain_domains, explain_domain_models, explain_domain_statuses, explain_schedule, explain_domain_dependencies, explain_routes export RMSE, NRMSE, EF, dr export Status, TimeStepTable, status export init_status! diff --git a/src/domains/domain_simulation.jl b/src/domains/domain_simulation.jl index 5bf80fc66..9f7d3654d 100644 --- a/src/domains/domain_simulation.jl +++ b/src/domains/domain_simulation.jl @@ -117,6 +117,29 @@ end HardDomains(process::Union{Symbol,AbstractString}; kwargs...) = HardDomains(; process=Symbol(process), kwargs...) +function _hard_domains_from_call_selector(selector::AbstractObjectMultiplicity) + c = criteria(selector) + unsupported = (:species, :name, :var, :relation, :within, :policy, :window) + any(key -> haskey(c, key) && !isnothing(getproperty(c, key)), unsupported) && error( + "Current `Calls(...)` hard-domain bridge only supports `kind`, `domain`, `scale`, and `process` selectors. ", + "Unsupported selector criteria: $(filter(key -> haskey(c, key), unsupported))." + ) + return HardDomains( + kind=haskey(c, :kind) ? c.kind : nothing, + domain=haskey(c, :domain) ? c.domain : nothing, + scale=haskey(c, :scale) ? c.scale : nothing, + process=haskey(c, :process) ? c.process : nothing, + ) +end + +function _model_spec_dependency_selector(dep_name::Symbol, selector::Call) + return _hard_domains_from_call_selector(selector.selector) +end + +function _model_spec_dependency_selector(dep_name::Symbol, selector::AbstractObjectMultiplicity) + return _hard_domains_from_call_selector(selector) +end + function _push_selector_term!(terms::Vector{String}, name::Symbol, value) isnothing(value) || push!(terms, "$(name)=:$(value)") return terms @@ -571,7 +594,7 @@ function _resolve_domain_dependencies(mapping::SimulationMapping, specs::Dict{Do keys_by_domain = _keys_by_domain(specs) for (consumer_key, spec) in specs - model_deps = dep(model_(spec)) + model_deps = dep(spec) for (dep_name, selector) in pairs(model_deps) selector isa AllDomains || continue resolved = DomainModelKey[] @@ -605,7 +628,7 @@ function _resolve_hard_domain_dependencies(mapping::SimulationMapping, specs::Di keys_by_domain = _keys_by_domain(specs) for (consumer_key, spec) in specs - model_deps = dep(model_(spec)) + model_deps = dep(spec) for (dep_name, selector) in pairs(model_deps) selector isa HardDomains || continue resolved = DomainModelKey[] @@ -655,6 +678,7 @@ function _build_domain_simulation(mapping::SimulationMapping, meteo; staged_grap for domain in mapping.domains merge!(specs, _domain_model_specs(domain)) end + mapping = _add_input_routes(mapping, specs) mapping = _add_route_target_status_defaults(mapping, specs) model_clocks = _domain_model_clocks(specs, timeline) @@ -965,6 +989,16 @@ end run_target!(models, status, dependency_name::AbstractString; kwargs...) = run_target!(models, status, Symbol(dependency_name); kwargs...) +""" + run_call!(target; kwargs...) + run_call!(models, status, dependency_name; kwargs...) + +Unified scene/object spelling for manually executing a model call handle. +This currently delegates to `run_target!` while `ModelTarget` remains the +runtime carrier for hard-domain calls. +""" +run_call!(args...; kwargs...) = run_target!(args...; kwargs...) + function _domain_context_for(simulation::DomainSimulation, domain::Domain, node::SoftDependencyNode, step::Int, constants=nothing) key = DomainModelKey(domain.name, node.scale, node.process) return DomainRunContext(simulation, key, step, simulation.model_clocks[key], constants) diff --git a/src/domains/route_runtime.jl b/src/domains/route_runtime.jl index 1fbf13263..eb8091936 100644 --- a/src/domains/route_runtime.jl +++ b/src/domains/route_runtime.jl @@ -34,6 +34,82 @@ function _resolve_route_bindings(mapping::SimulationMapping, specs::Dict{DomainM return bindings end +function _has_route_to_input(routes::Vector{Route}, consumer_key::DomainModelKey, input_var::Symbol) + any(routes) do route + route.to.domain == consumer_key.domain && + route.to.scale == consumer_key.scale && + route.to.var == input_var && + (isnothing(route.to.process) || route.to.process == consumer_key.process) + end +end + +function _all_domains_from_input_selector(selector::AbstractObjectMultiplicity, input_var::Symbol) + c = criteria(selector) + unsupported = (:name, :relation, :within, :window) + any(key -> haskey(c, key) && !isnothing(getproperty(c, key)), unsupported) && return nothing + + source_var = haskey(c, :var) ? c.var : input_var + policy = haskey(c, :policy) ? _as_schedule_policy(c.policy; context="Inputs route policy for `$(input_var)`") : HoldLast() + return AllDomains( + kind=haskey(c, :kind) ? c.kind : nothing, + domain=haskey(c, :domain) ? c.domain : nothing, + scale=haskey(c, :scale) ? c.scale : nothing, + process=haskey(c, :process) ? c.process : nothing, + var=source_var, + policy=policy, + ) +end + +function _single_value_route_reducer(values) + length(values) == 1 || error( + "`One(...)` input route expected exactly one resolved value, got $(length(values))." + ) + return only(values) +end + +function _route_cardinality_from_input_selector(selector::AbstractObjectMultiplicity) + selector isa Many && return ManyToOneVector() + selector isa Union{One,OptionalOne} && return ManyToOneAggregate(_single_value_route_reducer) + return nothing +end + +function _routes_from_value_inputs(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) + routes = Route[] + existing_routes = copy(mapping.routes) + for (consumer_key, spec) in specs + bindings = value_inputs(spec) + bindings isa NamedTuple || continue + for (input_var, selector) in pairs(bindings) + input_sym = Symbol(input_var) + input_sym in keys(inputs_(spec)) || continue + selector isa AbstractObjectMultiplicity || continue + _has_route_to_input(existing_routes, consumer_key, input_sym) && continue + + source = _all_domains_from_input_selector(selector, input_sym) + isnothing(source) && continue + cardinality = _route_cardinality_from_input_selector(selector) + isnothing(cardinality) && continue + + target = DomainRouteTarget( + consumer_key.domain; + scale=consumer_key.scale, + var=input_sym, + process=consumer_key.process, + ) + route = Route(from=source, to=target, cardinality=cardinality) + push!(routes, route) + push!(existing_routes, route) + end + end + return routes +end + +function _add_input_routes(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) + generated_routes = _routes_from_value_inputs(mapping, specs) + isempty(generated_routes) && return mapping + return SimulationMapping(mapping.domains...; routes=(mapping.routes..., generated_routes...)) +end + function _route_target_input_default(route::Route, specs::Dict{DomainModelKey,ModelSpec}) consumer_key = _route_target_consumer_key(route, specs) isnothing(consumer_key) && return nothing diff --git a/src/mtg/ModelSpec.jl b/src/mtg/ModelSpec.jl index 921fab852..a4a4eeae0 100644 --- a/src/mtg/ModelSpec.jl +++ b/src/mtg/ModelSpec.jl @@ -1,5 +1,5 @@ """ - ModelSpec(model; multiscale=nothing, timestep=nothing, input_bindings=NamedTuple(), meteo_bindings=NamedTuple(), meteo_window=nothing, output_routing=NamedTuple(), scope=:global, updates=()) + ModelSpec(model; name=nothing, applies_to=nothing, inputs=NamedTuple(), calls=NamedTuple(), environment=nothing, multiscale=nothing, timestep=nothing, input_bindings=NamedTuple(), meteo_bindings=NamedTuple(), meteo_window=nothing, output_routing=NamedTuple(), scope=:global, updates=()) User-side model configuration wrapper for mapping/model list composition. @@ -7,8 +7,13 @@ User-side model configuration wrapper for mapping/model list composition. This allows modelers to publish reusable models while users decide how models are coupled in their simulation setup. """ -struct ModelSpec{M,MS,TS,IB,MB,MW,OR,SC,UP} +struct ModelSpec{M,N,AT,IN,CA,EV,MS,TS,IB,MB,MW,OR,SC,UP} model::M + name::N + applies_to::AT + inputs::IN + calls::CA + environment::EV multiscale::MS timestep::TS input_bindings::IB @@ -78,6 +83,11 @@ end function ModelSpec( model::AbstractModel; + name=nothing, + applies_to=nothing, + inputs=NamedTuple(), + calls=NamedTuple(), + environment=nothing, multiscale=nothing, timestep=nothing, input_bindings=NamedTuple(), @@ -89,15 +99,27 @@ function ModelSpec( ) base_model = model - normalized_multiscale = _normalize_multiscale_mapping(base_model, multiscale) + normalized_name = _normalize_application_name(name) + default_inputs = _model_default_value_inputs(base_model) + normalized_inputs = _merge_value_inputs(default_inputs, _normalize_application_bindings(inputs)) + default_calls = _model_default_model_calls(base_model) + normalized_calls = _merge_value_inputs(default_calls, _normalize_application_bindings(calls)) + derived_multiscale = _legacy_multiscale_from_value_inputs(normalized_inputs, base_model) + combined_multiscale = _merge_legacy_multiscale(multiscale, derived_multiscale) + normalized_multiscale = _normalize_multiscale_mapping(base_model, combined_multiscale) normalized_input_bindings = _normalize_input_bindings(input_bindings) normalized_meteo_bindings = _normalize_meteo_bindings(meteo_bindings) normalized_meteo_window = _normalize_meteo_window(meteo_window) normalized_output_routing = _normalize_output_routing(output_routing) normalized_scope = _normalize_scope_selector(scope) normalized_updates = _normalize_updates(updates) - return ModelSpec{typeof(base_model),typeof(normalized_multiscale),typeof(timestep),typeof(normalized_input_bindings),typeof(normalized_meteo_bindings),typeof(normalized_meteo_window),typeof(normalized_output_routing),typeof(normalized_scope),typeof(normalized_updates)}( + return ModelSpec{typeof(base_model),typeof(normalized_name),typeof(applies_to),typeof(normalized_inputs),typeof(normalized_calls),typeof(environment),typeof(normalized_multiscale),typeof(timestep),typeof(normalized_input_bindings),typeof(normalized_meteo_bindings),typeof(normalized_meteo_window),typeof(normalized_output_routing),typeof(normalized_scope),typeof(normalized_updates)}( base_model, + normalized_name, + applies_to, + normalized_inputs, + normalized_calls, + environment, normalized_multiscale, timestep, normalized_input_bindings, @@ -111,6 +133,11 @@ end function ModelSpec( model::MultiScaleModel; + name=nothing, + applies_to=nothing, + inputs=NamedTuple(), + calls=NamedTuple(), + environment=nothing, multiscale=nothing, timestep=nothing, input_bindings=NamedTuple(), @@ -123,6 +150,11 @@ function ModelSpec( base_multiscale = isnothing(multiscale) ? mapped_variables_(model) : multiscale return ModelSpec( model_(model); + name=name, + applies_to=applies_to, + inputs=inputs, + calls=calls, + environment=environment, multiscale=base_multiscale, timestep=timestep, input_bindings=input_bindings, @@ -137,6 +169,11 @@ end function ModelSpec( spec::ModelSpec; model=spec.model, + name=spec.name, + applies_to=spec.applies_to, + inputs=spec.inputs, + calls=spec.calls, + environment=spec.environment, multiscale=spec.multiscale, timestep=spec.timestep, input_bindings=spec.input_bindings, @@ -146,13 +183,63 @@ function ModelSpec( scope=spec.scope, updates=spec.updates ) - ModelSpec(model; multiscale=multiscale, timestep=timestep, input_bindings=input_bindings, meteo_bindings=meteo_bindings, meteo_window=meteo_window, output_routing=output_routing, scope=scope, updates=updates) + ModelSpec(model; name=name, applies_to=applies_to, inputs=inputs, calls=calls, environment=environment, multiscale=multiscale, timestep=timestep, input_bindings=input_bindings, meteo_bindings=meteo_bindings, meteo_window=meteo_window, output_routing=output_routing, scope=scope, updates=updates) end as_model_spec(spec::ModelSpec) = spec as_model_spec(model::AbstractModel) = ModelSpec(model) as_model_spec(model::MultiScaleModel) = ModelSpec(model_(model); multiscale=mapped_variables_(model)) +""" + with_name(model_or_spec, name) + +Return a `ModelSpec` with an explicit model-application name. +""" +function with_name(model_or_spec, name) + spec = as_model_spec(model_or_spec) + return ModelSpec(spec; name=_normalize_application_name(name)) +end + +""" + with_applies_to(model_or_spec, selector) + +Return a `ModelSpec` with an explicit model-application target selector. +""" +function with_applies_to(model_or_spec, selector) + spec = as_model_spec(model_or_spec) + return ModelSpec(spec; applies_to=selector) +end + +""" + with_inputs(model_or_spec, bindings) + +Return a `ModelSpec` with unified scene/object value-input bindings. +""" +function with_inputs(model_or_spec, bindings) + spec = as_model_spec(model_or_spec) + return ModelSpec(spec; inputs=_normalize_application_bindings(bindings)) +end + +""" + with_calls(model_or_spec, bindings) + +Return a `ModelSpec` with unified scene/object manual model-call bindings. +""" +function with_calls(model_or_spec, bindings) + spec = as_model_spec(model_or_spec) + return ModelSpec(spec; calls=_normalize_application_bindings(bindings)) +end + +""" + with_environment(model_or_spec, environment) + +Return a `ModelSpec` with scene/object environment configuration metadata. +""" +function with_environment(model_or_spec, environment) + spec = as_model_spec(model_or_spec) + return ModelSpec(spec; environment=environment) +end + """ with_multiscale(model_or_spec, mapped_variables) @@ -333,6 +420,54 @@ function _normalize_scope_selector(scope) return scope end +""" + AppliesTo(selector) + +Pipe-style transform that sets the object selector where a model application +runs in the unified scene/object API. +""" +AppliesTo(selector) = x -> with_applies_to(x, selector) + +""" + Inputs(bindings...) + Inputs(; kwargs...) + +Pipe-style transform that sets value-input bindings in the unified +scene/object API. +""" +Inputs(bindings::Pair...) = x -> with_inputs(x, bindings) +Inputs(bindings::NamedTuple) = x -> with_inputs(x, bindings) +Inputs(; kwargs...) = Inputs((; kwargs...)) + +""" + Calls(bindings...) + Calls(; kwargs...) + +Pipe-style transform that sets manual model-call bindings in the unified +scene/object API. +""" +Calls(bindings::Pair...) = x -> with_calls(x, bindings) +Calls(bindings::NamedTuple) = x -> with_calls(x, bindings) +Calls(; kwargs...) = Calls((; kwargs...)) + +""" + TimeStep(timestep) + +Pipe-style transform that sets a user-selected timestep in the unified +scene/object API. This is the breaking-design name for `TimeStepModel(...)`. +""" +TimeStep(timestep) = x -> with_timestep(x, timestep) + +""" + Environment(config) + Environment(; kwargs...) + +Pipe-style transform that stores scene/object environment configuration +metadata on a `ModelSpec`. +""" +Environment(config) = x -> with_environment(x, config isa EnvironmentConfig ? config : EnvironmentConfig(config)) +Environment(; kwargs...) = Environment((; kwargs...)) + """ MultiScaleModel(mapped_variables) @@ -503,7 +638,41 @@ process(m::ModelSpec) = process(model_(m)) timestep(m::ModelSpec) = m.timestep inputs_(m::ModelSpec) = inputs_(model_(m)) outputs_(m::ModelSpec) = outputs_(model_(m)) -dep(m::ModelSpec) = dep(model_(m)) + +function _model_spec_dependency_selector(dep_name::Symbol, selector) + return selector +end + +function _model_spec_dependency_selector(dep_name::Symbol, selector::Input) + return nothing +end + +function _normalize_model_spec_dependencies(deps::NamedTuple) + normalized = Pair{Symbol,Any}[] + for (dep_name, selector) in pairs(deps) + selector isa Union{Input,Call} && continue + normalized_selector = _model_spec_dependency_selector(dep_name, selector) + isnothing(normalized_selector) && continue + push!(normalized, dep_name => normalized_selector) + end + return (; normalized...) +end + +function _model_spec_call_dependencies(spec::ModelSpec) + calls = model_calls(spec) + calls isa NamedTuple || return NamedTuple() + normalized = Pair{Symbol,Any}[] + for (dep_name, selector) in pairs(calls) + push!(normalized, dep_name => _model_spec_dependency_selector(dep_name, selector)) + end + return (; normalized...) +end + +function dep(m::ModelSpec) + model_deps = _normalize_model_spec_dependencies(dep(model_(m))) + call_deps = _model_spec_call_dependencies(m) + return (; pairs(model_deps)..., pairs(call_deps)...) +end init_variables(m::ModelSpec; verbose::Bool=true) = init_variables(model_(m); verbose=verbose) meteo_inputs_(m::ModelSpec) = meteo_inputs_(model_(m)) meteo_outputs_(m::ModelSpec) = meteo_outputs_(model_(m)) diff --git a/src/mtg/model_spec_inference.jl b/src/mtg/model_spec_inference.jl index 2376fed85..94814876f 100644 --- a/src/mtg/model_spec_inference.jl +++ b/src/mtg/model_spec_inference.jl @@ -833,6 +833,11 @@ function _model_specs_rows(model_specs) scale=scale, process=process, model=typeof(model_(spec)), + application_name=application_name(spec), + applies_to=applies_to(spec), + value_inputs=value_inputs(spec), + model_calls=model_calls(spec), + environment=environment_config(spec), timestep=timestep(spec), timespec_default=timespec(model_(spec)), timestep_resolution=resolution, @@ -858,6 +863,11 @@ Summary fields: - `scale` - `process` - `model` +- `application_name` +- `applies_to` +- `value_inputs` +- `model_calls` +- `environment` - `timestep` - `input_bindings` - `meteo_bindings` @@ -890,6 +900,11 @@ function explain_model_specs(target; io::IO=stdout, infer::Bool=true, validate:: meteo_inputs_desc = (row.meteo_inputs isa NamedTuple && isempty(keys(row.meteo_inputs))) ? "(none)" : _stringify_compact(row.meteo_inputs) meteo_outputs_desc = (row.meteo_outputs isa NamedTuple && isempty(keys(row.meteo_outputs))) ? "(none)" : _stringify_compact(row.meteo_outputs) updates_desc = isempty(row.updates) ? "(none)" : _stringify_compact(row.updates) + application_name_desc = isnothing(row.application_name) ? "(unnamed)" : string(row.application_name) + applies_to_desc = isnothing(row.applies_to) ? "(implicit legacy target)" : _stringify_compact(row.applies_to) + value_inputs_desc = (row.value_inputs isa NamedTuple && isempty(keys(row.value_inputs))) ? "(none)" : _stringify_compact(row.value_inputs) + model_calls_desc = (row.model_calls isa NamedTuple && isempty(keys(row.model_calls))) ? "(none)" : _stringify_compact(row.model_calls) + environment_desc = isnothing(row.environment) ? "(default)" : _stringify_compact(row.environment) println( io, " - ", @@ -898,7 +913,17 @@ function explain_model_specs(target; io::IO=stdout, infer::Bool=true, validate:: row.process, " [", row.model, - "]: timestep=", + "]: name=", + application_name_desc, + ", applies_to=", + applies_to_desc, + ", value_inputs=", + value_inputs_desc, + ", model_calls=", + model_calls_desc, + ", environment=", + environment_desc, + ", timestep=", timestep_desc, ", input_bindings=", input_bindings_desc, diff --git a/src/processes/models_inputs_outputs.jl b/src/processes/models_inputs_outputs.jl index 5f496d380..ec8c4253c 100644 --- a/src/processes/models_inputs_outputs.jl +++ b/src/processes/models_inputs_outputs.jl @@ -59,6 +59,41 @@ or `Interpolate`) for each producer output. output_policy(model::AbstractModel) = output_policy(typeof(model)) output_policy(::Type{<:AbstractModel}) = NamedTuple() +""" + application_name(spec::ModelSpec) + +Optional stable name for one model application in the unified scene/object API. +""" +application_name(spec::ModelSpec) = spec.name + +""" + applies_to(spec::ModelSpec) + +Object selector where a model application runs in the unified scene/object API. +""" +applies_to(spec::ModelSpec) = spec.applies_to + +""" + value_inputs(spec::ModelSpec) + +Unified scene/object value-input bindings declared with `Inputs(...)`. +""" +value_inputs(spec::ModelSpec) = spec.inputs + +""" + model_calls(spec::ModelSpec) + +Unified scene/object manual call bindings declared with `Calls(...)`. +""" +model_calls(spec::ModelSpec) = spec.calls + +""" + environment_config(spec::ModelSpec) + +Optional scene/object environment configuration declared with `Environment(...)`. +""" +environment_config(spec::ModelSpec) = spec.environment + """ input_bindings(spec::ModelSpec) diff --git a/src/scene_object_api.jl b/src/scene_object_api.jl new file mode 100644 index 000000000..a71c2dbd8 --- /dev/null +++ b/src/scene_object_api.jl @@ -0,0 +1,1753 @@ +abstract type AbstractObjectSelector end +abstract type AbstractObjectMultiplicity end + +struct ObjectRefVector{R} <: AbstractVector{Any} + refs::R +end + +Base.size(v::ObjectRefVector) = size(v.refs) +Base.length(v::ObjectRefVector) = length(v.refs) +Base.getindex(v::ObjectRefVector, i::Int) = v.refs[i][] +Base.setindex!(v::ObjectRefVector, value, i::Int) = (v.refs[i][] = value) +Base.parent(v::ObjectRefVector) = v.refs + +struct ObjectId{T} + value::T +end +ObjectId(id::ObjectId) = id +ObjectId(id::AbstractString) = ObjectId(Symbol(id)) + +mutable struct Object + id::ObjectId + scale::Union{Nothing,Symbol} + kind::Union{Nothing,Symbol} + species::Union{Nothing,Symbol} + name::Union{Nothing,Symbol} + parent::Union{Nothing,ObjectId} + children::Vector{ObjectId} + geometry::Any + status::Any + applications::Any +end + +function Object( + id; + scale=nothing, + kind=nothing, + species=nothing, + name=nothing, + parent=nothing, + children=ObjectId[], + geometry=nothing, + status=nothing, + applications=(), +) + return Object( + ObjectId(id), + _maybe_symbol(scale), + _maybe_symbol(kind), + _maybe_symbol(species), + _maybe_symbol(name), + isnothing(parent) ? nothing : ObjectId(parent), + ObjectId[ObjectId(child) for child in children], + geometry, + status, + applications, + ) +end + +mutable struct SceneRegistry + objects::Dict{ObjectId,Any} + by_scale::Dict{Symbol,Set{ObjectId}} + by_kind::Dict{Symbol,Set{ObjectId}} + by_species::Dict{Symbol,Set{ObjectId}} + by_name::Dict{Symbol,ObjectId} +end + +SceneRegistry() = SceneRegistry( + Dict{ObjectId,Any}(), + Dict{Symbol,Set{ObjectId}}(), + Dict{Symbol,Set{ObjectId}}(), + Dict{Symbol,Set{ObjectId}}(), + Dict{Symbol,ObjectId}(), +) + +mutable struct Scene{R,A,E} + registry::R + applications::A + environment::E + binding_cache::Any + environment_binding_cache::Any + bindings_dirty::Bool + environment_bindings_dirty::Bool + revision::Int + environment_revision::Int +end + +function Scene(objects::Object...; applications=(), environment=nothing) + scene = Scene(SceneRegistry(), applications, environment, nothing, nothing, true, true, 0, 0) + for object in objects + register_object!(scene, object) + end + return scene +end + +function _mark_environment_bindings_dirty!(scene::Scene) + scene.environment_binding_cache = nothing + scene.environment_bindings_dirty = true + scene.environment_revision += 1 + return scene +end + +function _mark_bindings_dirty!(scene::Scene) + scene.binding_cache = nothing + scene.bindings_dirty = true + scene.revision += 1 + return _mark_environment_bindings_dirty!(scene) +end + +bindings_dirty(scene::Scene) = scene.bindings_dirty +environment_bindings_dirty(scene::Scene) = scene.environment_bindings_dirty +scene_revision(scene::Scene) = scene.revision +environment_revision(scene::Scene) = scene.environment_revision +compiled_bindings(scene::Scene) = scene.binding_cache +compiled_environment_bindings(scene::Scene) = scene.environment_binding_cache +mark_environment_binding_dirty!(scene::Scene) = _mark_environment_bindings_dirty!(scene) + +function _push_index!(index::Dict{Symbol,Set{ObjectId}}, key, id::ObjectId) + isnothing(key) && return nothing + push!(get!(index, key, Set{ObjectId}()), id) + return nothing +end + +function _delete_index!(index::Dict{Symbol,Set{ObjectId}}, key, id::ObjectId) + isnothing(key) && return nothing + ids = get(index, key, nothing) + isnothing(ids) && return nothing + delete!(ids, id) + isempty(ids) && delete!(index, key) + return nothing +end + +function _index_object!(registry::SceneRegistry, object::Object) + _push_index!(registry.by_scale, object.scale, object.id) + _push_index!(registry.by_kind, object.kind, object.id) + _push_index!(registry.by_species, object.species, object.id) + isnothing(object.name) || (registry.by_name[object.name] = object.id) + return nothing +end + +function _deindex_object!(registry::SceneRegistry, object::Object) + _delete_index!(registry.by_scale, object.scale, object.id) + _delete_index!(registry.by_kind, object.kind, object.id) + _delete_index!(registry.by_species, object.species, object.id) + if !isnothing(object.name) && get(registry.by_name, object.name, nothing) == object.id + delete!(registry.by_name, object.name) + end + return nothing +end + +function _scene_object(scene::Scene, id) + oid = ObjectId(id) + haskey(scene.registry.objects, oid) || error("No scene object with id `$(oid.value)`.") + return scene.registry.objects[oid] +end + +function register_object!(scene::Scene, object::Object; parent=object.parent) + registry = scene.registry + haskey(registry.objects, object.id) && error("Scene already contains object id `$(object.id.value)`.") + object.parent = isnothing(parent) ? nothing : ObjectId(parent) + registry.objects[object.id] = object + _index_object!(registry, object) + if !isnothing(object.parent) + parent_object = _scene_object(scene, object.parent) + object.id in parent_object.children || push!(parent_object.children, object.id) + end + _mark_bindings_dirty!(scene) + return object +end + +function _remove_child_link!(scene::Scene, parent_id, child_id::ObjectId) + isnothing(parent_id) && return nothing + parent_object = _scene_object(scene, parent_id) + filter!(!=(child_id), parent_object.children) + return nothing +end + +function remove_object!(scene::Scene, id; recursive::Bool=true) + object = _scene_object(scene, id) + if !recursive && !isempty(object.children) + error("Cannot remove object `$(object.id.value)` with children unless `recursive=true`.") + end + for child in copy(object.children) + remove_object!(scene, child; recursive=true) + end + _remove_child_link!(scene, object.parent, object.id) + _deindex_object!(scene.registry, object) + delete!(scene.registry.objects, object.id) + _mark_bindings_dirty!(scene) + return object +end + +function reparent_object!(scene::Scene, id, new_parent) + object = _scene_object(scene, id) + new_parent_id = isnothing(new_parent) ? nothing : ObjectId(new_parent) + if !isnothing(new_parent_id) + haskey(scene.registry.objects, new_parent_id) || error("No scene object with id `$(new_parent_id.value)`.") + end + _remove_child_link!(scene, object.parent, object.id) + object.parent = new_parent_id + if !isnothing(new_parent_id) + parent_object = _scene_object(scene, new_parent_id) + object.id in parent_object.children || push!(parent_object.children, object.id) + end + _mark_bindings_dirty!(scene) + return object +end + +function move_object!(scene::Scene, id, geometry_or_position) + object = _scene_object(scene, id) + object.geometry = geometry_or_position + _mark_environment_bindings_dirty!(scene) + return object +end + +geometry(object::Object) = object.geometry +geometry(status::Status) = hasproperty(status, :geometry) ? status.geometry : nothing +geometry(x) = hasproperty(x, :geometry) ? getproperty(x, :geometry) : nothing + +function _geometry_position(g::NamedTuple) + haskey(g, :position) && return getproperty(g, :position) + if haskey(g, :x) && haskey(g, :y) && haskey(g, :z) + return (x=g.x, y=g.y, z=g.z) + elseif haskey(g, :x) && haskey(g, :y) + return (x=g.x, y=g.y) + end + return nothing +end +_geometry_position(_) = nothing + +position(object::Object) = _geometry_position(geometry(object)) +position(status::Status) = _geometry_position(geometry(status)) + +_geometry_bounds(g::NamedTuple) = haskey(g, :bounds) ? getproperty(g, :bounds) : nothing +_geometry_bounds(_) = nothing +bounds(object::Object) = _geometry_bounds(geometry(object)) +bounds(status::Status) = _geometry_bounds(geometry(status)) + +function refresh_bindings!(scene::Scene, specs=scene.applications; force::Bool=false) + uses_scene_applications = specs === scene.applications + if !uses_scene_applications + return compile_scene(scene, specs) + end + if force || scene.bindings_dirty || isnothing(scene.binding_cache) + scene.binding_cache = compile_scene(scene, scene.applications) + scene.bindings_dirty = false + end + return scene.binding_cache +end + +function refresh_environment_bindings!(scene::Scene, compiled=refresh_bindings!(scene); force::Bool=false) + if force || scene.environment_bindings_dirty || isnothing(scene.environment_binding_cache) + scene.environment_binding_cache = compile_environment_bindings(scene, compiled) + scene.environment_bindings_dirty = false + end + return scene.environment_binding_cache +end + +function object_ids(scene::Scene; scale=nothing, kind=nothing, species=nothing, name=nothing) + registry = scene.registry + sets = Set{ObjectId}[] + isnothing(scale) || push!(sets, copy(get(registry.by_scale, Symbol(scale), Set{ObjectId}()))) + isnothing(kind) || push!(sets, copy(get(registry.by_kind, Symbol(kind), Set{ObjectId}()))) + isnothing(species) || push!(sets, copy(get(registry.by_species, Symbol(species), Set{ObjectId}()))) + if !isnothing(name) + id = get(registry.by_name, Symbol(name), nothing) + push!(sets, isnothing(id) ? Set{ObjectId}() : Set([id])) + end + isempty(sets) && return sort!(collect(keys(registry.objects)); by=id -> string(id.value)) + ids = reduce(intersect, sets) + return sort!(collect(ids); by=id -> string(id.value)) +end + +scene_objects(scene::Scene; kwargs...) = [_scene_object(scene, id) for id in object_ids(scene; kwargs...)] + +function explain_objects(scene::Scene) + return [ + ( + id=object.id.value, + scale=object.scale, + kind=object.kind, + species=object.species, + name=object.name, + parent=isnothing(object.parent) ? nothing : object.parent.value, + children=[child.value for child in object.children], + has_geometry=!isnothing(object.geometry), + has_status=!isnothing(object.status), + n_applications=length(object.applications), + ) + for object in scene_objects(scene) + ] +end + +struct SceneScope <: AbstractObjectSelector end +struct Self <: AbstractObjectSelector end +struct SelfPlant <: AbstractObjectSelector end + +struct Ancestor <: AbstractObjectSelector + scale::Union{Nothing,Symbol} +end +Ancestor(; scale=nothing) = Ancestor(_maybe_symbol(scale)) + +struct Scope <: AbstractObjectSelector + name::Symbol +end +Scope(name::Union{Symbol,AbstractString}) = Scope(Symbol(name)) + +struct Kind <: AbstractObjectSelector + kind::Symbol +end +Kind(kind::Union{Symbol,AbstractString}) = Kind(Symbol(kind)) + +struct Species <: AbstractObjectSelector + species::Symbol +end +Species(species::Union{Symbol,AbstractString}) = Species(Symbol(species)) + +struct Scale <: AbstractObjectSelector + scale::Symbol +end +Scale(scale::Union{Symbol,AbstractString}) = Scale(Symbol(scale)) + +struct Relation <: AbstractObjectSelector + relation::Symbol +end +Relation(relation::Union{Symbol,AbstractString}) = Relation(Symbol(relation)) + +_maybe_symbol(x) = isnothing(x) ? nothing : Symbol(x) + +const _OBJECT_ADDRESS_SYMBOL_FIELDS = (:kind, :domain, :species, :scale, :name, :process, :var, :relation, :application) + +function _normalize_object_selector_value(key::Symbol, value) + key in _OBJECT_ADDRESS_SYMBOL_FIELDS && return _maybe_symbol(value) + return value +end + +function _normalize_selector_kwargs(kwargs) + return NamedTuple{keys(kwargs)}( + Tuple(_normalize_object_selector_value(k, v) for (k, v) in pairs(kwargs)) + ) +end + +function _normalize_selector_criteria(args::Tuple; kwargs...) + selectors = Tuple(args) + all(selector -> selector isa AbstractObjectSelector, selectors) || error( + "Object selector positional arguments must be selector objects such as `Kind(:plant)` or `Scale(:Leaf)`." + ) + normalized_kwargs = _normalize_selector_kwargs((; kwargs...)) + return (; selectors=selectors, normalized_kwargs...) +end + +struct One{C<:NamedTuple} <: AbstractObjectMultiplicity + criteria::C +end +struct OptionalOne{C<:NamedTuple} <: AbstractObjectMultiplicity + criteria::C +end +struct Many{C<:NamedTuple} <: AbstractObjectMultiplicity + criteria::C +end + +One(args...; kwargs...) = One(_normalize_selector_criteria(args; kwargs...)) +OptionalOne(args...; kwargs...) = OptionalOne(_normalize_selector_criteria(args; kwargs...)) +Many(args...; kwargs...) = Many(_normalize_selector_criteria(args; kwargs...)) + +criteria(selector::AbstractObjectMultiplicity) = selector.criteria +multiplicity(::One) = :one +multiplicity(::OptionalOne) = :optional_one +multiplicity(::Many) = :many + +_sort_object_ids!(ids) = sort!(ids; by=id -> string(id.value)) + +function _object_id_from_context(context) + isnothing(context) && return nothing + context isa Object && return context.id + return ObjectId(context) +end + +function _descendant_ids(scene::Scene, root_id::ObjectId) + ids = ObjectId[root_id] + object = _scene_object(scene, root_id) + for child_id in object.children + append!(ids, _descendant_ids(scene, child_id)) + end + return ids +end + +function _ancestor_id(scene::Scene, current_id::ObjectId; scale=nothing, kind=nothing) + id = current_id + while true + object = _scene_object(scene, id) + scale_match = isnothing(scale) || object.scale == Symbol(scale) + kind_match = isnothing(kind) || object.kind == Symbol(kind) + scale_match && kind_match && return id + isnothing(object.parent) && return nothing + id = object.parent + end +end + +function _selector_scope_from_positional(selectors) + scopes = filter( + selector -> selector isa Union{SceneScope,Self,SelfPlant,Ancestor,Scope}, + selectors, + ) + isempty(scopes) && return nothing + length(scopes) == 1 || error("Only one scope selector can be used in one object selector.") + return only(scopes) +end + +function _selector_value_from_positional(selectors, ::Type{Kind}) + values = [selector.kind for selector in selectors if selector isa Kind] + isempty(values) && return nothing + length(unique(values)) == 1 || error("Conflicting `Kind(...)` selector values: $(values).") + return only(unique(values)) +end + +function _selector_value_from_positional(selectors, ::Type{Species}) + values = [selector.species for selector in selectors if selector isa Species] + isempty(values) && return nothing + length(unique(values)) == 1 || error("Conflicting `Species(...)` selector values: $(values).") + return only(unique(values)) +end + +function _selector_value_from_positional(selectors, ::Type{Scale}) + values = [selector.scale for selector in selectors if selector isa Scale] + isempty(values) && return nothing + length(unique(values)) == 1 || error("Conflicting `Scale(...)` selector values: $(values).") + return only(unique(values)) +end + +function _selector_value_from_positional(selectors, ::Type{Relation}) + values = [selector.relation for selector in selectors if selector isa Relation] + isempty(values) && return nothing + length(unique(values)) == 1 || error("Conflicting `Relation(...)` selector values: $(values).") + return only(unique(values)) +end + +function _criteria_value(criteria, key::Symbol, selector_type) + positional = _selector_value_from_positional(criteria.selectors, selector_type) + keyword = haskey(criteria, key) ? getproperty(criteria, key) : nothing + if !isnothing(positional) && !isnothing(keyword) && positional != keyword + error("Conflicting selector values for `$(key)`: `$(positional)` and `$(keyword)`.") + end + return isnothing(keyword) ? positional : keyword +end + +function _criteria_scope(criteria) + positional = _selector_scope_from_positional(criteria.selectors) + keyword = haskey(criteria, :within) ? criteria.within : nothing + if !isnothing(positional) && !isnothing(keyword) && typeof(positional) != typeof(keyword) + error("Conflicting scope selectors: `$(positional)` and `$(keyword)`.") + end + return isnothing(keyword) ? positional : keyword +end + +function _scope_object_ids(scene::Scene, scope, context) + if isnothing(scope) || scope isa SceneScope + return ObjectId[keys(scene.registry.objects)...] + end + + current_id = _object_id_from_context(context) + if scope isa Self + isnothing(current_id) && error("`Self()` selectors require a current object context.") + return _descendant_ids(scene, current_id) + elseif scope isa SelfPlant + isnothing(current_id) && error("`SelfPlant()` selectors require a current object context.") + plant_id = _ancestor_id(scene, current_id; scale=:Plant) + isnothing(plant_id) && error("No `scale=:Plant` ancestor found for object `$(current_id.value)`.") + return _descendant_ids(scene, plant_id) + elseif scope isa Ancestor + isnothing(current_id) && error("`Ancestor(...)` selectors require a current object context.") + ancestor_id = _ancestor_id(scene, current_id; scale=scope.scale) + if isnothing(ancestor_id) + error("No matching ancestor found for object `$(current_id.value)` and selector `$(scope)`.") + end + return _descendant_ids(scene, ancestor_id) + elseif scope isa Scope + root_id = get(scene.registry.by_name, scope.name, nothing) + if isnothing(root_id) + candidate = ObjectId(scope.name) + root_id = haskey(scene.registry.objects, candidate) ? candidate : nothing + end + isnothing(root_id) && error("No named scope or object `$(scope.name)` found in the scene registry.") + return _descendant_ids(scene, root_id) + end + + error("Unsupported object scope selector `$(scope)` of type `$(typeof(scope))`.") +end + +function _matches_object_criteria(object::Object; scale=nothing, kind=nothing, species=nothing, name=nothing) + isnothing(scale) || object.scale == scale || return false + isnothing(kind) || object.kind == kind || return false + isnothing(species) || object.species == species || return false + isnothing(name) || object.name == name || return false + return true +end + +function resolve_object_ids(scene::Scene, selector::AbstractObjectMultiplicity; context=nothing) + return _resolve_object_ids(scene, selector; context=context) +end + +function _resolve_object_ids(scene::Scene, selector::AbstractObjectMultiplicity; context=nothing, default_to_context::Bool=false) + criteria_ = criteria(selector) + relation = _criteria_value(criteria_, :relation, Relation) + isnothing(relation) || error("`Relation(...)` selector resolution is not implemented yet.") + + scope = _criteria_scope(criteria_) + scale = _criteria_value(criteria_, :scale, Scale) + kind = _criteria_value(criteria_, :kind, Kind) + species = _criteria_value(criteria_, :species, Species) + name = haskey(criteria_, :name) ? criteria_.name : nothing + + if default_to_context && + isnothing(scope) && + isnothing(scale) && + isnothing(kind) && + isnothing(species) && + isnothing(name) && + !isnothing(context) + return ObjectId[_object_id_from_context(context)] + end + + candidate_ids = _scope_object_ids(scene, scope, context) + ids = ObjectId[ + id for id in candidate_ids + if _matches_object_criteria(_scene_object(scene, id); scale=scale, kind=kind, species=species, name=name) + ] + _sort_object_ids!(ids) + + if selector isa One && length(ids) != 1 + error("Expected exactly one object for selector `$(selector)`, got $(length(ids)).") + elseif selector isa OptionalOne && length(ids) > 1 + error("Expected zero or one object for selector `$(selector)`, got $(length(ids)).") + end + return ids +end + +resolve_objects(scene::Scene, selector::AbstractObjectMultiplicity; context=nothing) = + [_scene_object(scene, id) for id in resolve_object_ids(scene, selector; context=context)] + +struct CompiledSceneApplication{S,AT,TS,CL} + id::Symbol + spec::S + process::Symbol + name::Union{Nothing,Symbol} + target_ids::Vector{ObjectId} + applies_to::AT + timestep::TS + clock::CL +end + +struct CompiledSceneInputBinding{SEL,P,W,C} + application_id::Symbol + consumer_id::ObjectId + input::Symbol + selector::SEL + origin::Symbol + source_ids::Vector{ObjectId} + source_var::Symbol + multiplicity::Symbol + policy::P + window::W + carrier_hint::Symbol + carrier::C +end + +struct CompiledSceneCallBinding{SEL} + application_id::Symbol + consumer_id::ObjectId + call::Symbol + selector::SEL + callee_object_ids::Vector{ObjectId} + callee_application_ids::Vector{Symbol} + process::Union{Nothing,Symbol} + application::Union{Nothing,Symbol} + multiplicity::Symbol +end + +struct CompiledEnvironmentBinding{B,C,S} + application_id::Symbol + object_id::ObjectId + provider::Symbol + backend::B + cell::C + required_inputs::Vector{Symbol} + produced_outputs::Vector{Symbol} + support::S + config::Any +end + +struct CompiledEnvironmentBindings{SC,B} + scene::SC + bindings::B + scene_revision::Int + environment_revision::Int +end + +struct CompiledScene{SC,AP,IB,CB} + scene::SC + applications::AP + input_bindings::IB + call_bindings::CB + revision::Int +end + +function compile_scene(scene::Scene) + return compile_scene(scene, scene.applications) +end + +function compile_scene(scene::Scene, specs::Tuple) + return _compile_scene(scene, specs) +end + +function compile_scene(scene::Scene, specs::AbstractVector) + return _compile_scene(scene, Tuple(specs)) +end + +function compile_scene(scene::Scene, specs...) + return _compile_scene(scene, specs) +end + +function _scene_timeline(scene::Scene) + backend = environment_backend(scene.environment) + try + return _timeline_context(backend) + catch + return TimelineContext(3600.0) + end +end + +function _compile_scene(scene::Scene, raw_specs) + timeline = _scene_timeline(scene) + applications = _compile_scene_applications(scene, raw_specs, timeline) + _validate_scene_writers!(applications) + input_bindings = _compile_scene_input_bindings(scene, applications) + call_bindings = _compile_scene_call_bindings(scene, applications) + return CompiledScene(scene, applications, input_bindings, call_bindings, scene.revision) +end + +function _compile_scene_applications(scene::Scene, raw_specs, timeline) + process_counts = Dict{Symbol,Int}() + ids = Set{Symbol}() + applications = CompiledSceneApplication[] + for raw_spec in raw_specs + spec = as_model_spec(raw_spec) + selector = applies_to(spec) + isnothing(selector) && error( + "Model application for process `$(process(spec))` has no `AppliesTo(...)` selector." + ) + selector isa AbstractObjectMultiplicity || error( + "`AppliesTo(...)` for process `$(process(spec))` must be an object selector such as `Many(scale=:Leaf)`." + ) + proc = process(spec) + occurrence = get(process_counts, proc, 0) + 1 + process_counts[proc] = occurrence + name = application_name(spec) + app_id = isnothing(name) ? (occurrence == 1 ? proc : Symbol(string(proc), "_", occurrence)) : name + app_id in ids && error("Duplicate compiled scene application id `$(app_id)`.") + push!(ids, app_id) + target_ids = resolve_object_ids(scene, selector) + push!( + applications, + CompiledSceneApplication( + app_id, + spec, + proc, + name, + target_ids, + selector, + timestep(spec), + _scene_application_clock(spec, timeline), + ), + ) + end + return applications +end + +function _scene_output_names(application::CompiledSceneApplication) + return Symbol[Symbol(var) for var in keys(outputs_(application.spec))] +end + +function _scene_writer_groups(applications) + groups = Dict{Tuple{ObjectId,Symbol},Vector{Tuple{Int,Any}}}() + for (index, application) in pairs(applications) + for object_id in application.target_ids + for variable in _scene_output_names(application) + push!(get!(groups, (object_id, variable), Tuple{Int,Any}[]), (index, application)) + end + end + end + return groups +end + +function _application_match_labels(application::CompiledSceneApplication) + labels = Set{Symbol}([application.id, application.process]) + isnothing(application.name) || push!(labels, application.name) + return labels +end + +function _update_variables(update) + return Tuple(Symbol(variable) for variable in getproperty(update, :variables)) +end + +function _update_after(update) + return Tuple(Symbol(label) for label in getproperty(update, :after)) +end + +function _matching_updates(spec, variable::Symbol) + return [update for update in updates(spec) if variable in _update_variables(update)] +end + +function _update_after_labels(spec, variable::Symbol) + labels = Symbol[] + for update in _matching_updates(spec, variable) + append!(labels, _update_after(update)) + end + unique!(labels) + return labels +end + +function _updates_after_previous_writer(spec, variable::Symbol, previous_applications) + matching = _matching_updates(spec, variable) + isempty(matching) && return false + previous_labels = Set{Symbol}() + for application in previous_applications + union!(previous_labels, _application_match_labels(application)) + end + for update in matching + after = _update_after(update) + isempty(after) && continue + any(label -> label in previous_labels, after) && return true + end + return false +end + +function _declares_update_without_previous_writer(spec, variable::Symbol, previous_applications) + isempty(previous_applications) || return false + for update in _matching_updates(spec, variable) + isempty(_update_after(update)) || return true + end + return false +end + +function _validate_scene_writers!(applications) + for ((object_id, variable), indexed_writers) in _scene_writer_groups(applications) + length(indexed_writers) <= 1 && continue + sort!(indexed_writers; by=first) + previous = CompiledSceneApplication[] + for (_, application) in indexed_writers + if _declares_update_without_previous_writer(application.spec, variable, previous) + error( + "Application `$(application.id)` declares `Updates($(variable))` for object ", + "`$(object_id.value)`, but no previous writer for `$(variable)` exists. ", + "Move it after the producer named in `after=...`." + ) + end + if !isempty(previous) && !_updates_after_previous_writer(application.spec, variable, previous) + previous_labels = sort!(collect(reduce(union!, (_application_match_labels(app) for app in previous); init=Set{Symbol}()))) + error( + "Variable `$(variable)` on object `$(object_id.value)` is written by multiple ", + "applications. Application `$(application.id)` must declare ", + "`Updates(:$(variable); after=...)` matching one of the previous writers ", + "`$(previous_labels)`." + ) + end + push!(previous, application) + end + end + return nothing +end + +function _scene_application_clock(spec, timeline) + clock = _clock_from_spec_timestep(timestep(spec), timeline) + isnothing(clock) && return ClockSpec(1.0, 0.0) + return clock +end + +function _criteria_get(criteria, key::Symbol, default=nothing) + return haskey(criteria, key) ? getproperty(criteria, key) : default +end + +function _selector_policy(selector::AbstractObjectMultiplicity) + return _criteria_get(criteria(selector), :policy, HoldLast()) +end + +function _selector_window(selector::AbstractObjectMultiplicity) + return _criteria_get(criteria(selector), :window, nothing) +end + +function _selector_var(selector::AbstractObjectMultiplicity, fallback::Symbol) + return _criteria_get(criteria(selector), :var, fallback) +end + +function _selector_application(selector::AbstractObjectMultiplicity) + return _criteria_get(criteria(selector), :application, nothing) +end + +function _dependency_object_ids(scene::Scene, selector::AbstractObjectMultiplicity, context::ObjectId) + return _resolve_object_ids(scene, selector; context=context, default_to_context=true) +end + +function _carrier_hint(selector::AbstractObjectMultiplicity, policy, window) + !isnothing(window) && return :temporal_stream + policy isa HoldLast || return :temporal_stream + selector isa Many && return :ref_vector + return :shared_ref +end + +function _status_ref_or_nothing(status, var::Symbol) + status isa Status || return nothing + var in propertynames(status) || return nothing + return refvalue(status, var) +end + +function _input_carrier(scene::Scene, selector::AbstractObjectMultiplicity, source_ids::Vector{ObjectId}, source_var::Symbol) + refs = Base.RefValue[] + for source_id in source_ids + object = _scene_object(scene, source_id) + source_ref = _status_ref_or_nothing(object.status, source_var) + isnothing(source_ref) && return nothing + push!(refs, source_ref) + end + if selector isa Many + isempty(refs) && return RefVector{Any}() + return _ref_vector_carrier(refs) + end + return isempty(refs) ? nothing : only(refs) +end + +function _ref_vector_carrier(refs) + T = typeof(refs[1][]) + typed_refs = Base.RefValue{T}[] + for source_ref in refs + source_ref isa Base.RefValue{T} || return ObjectRefVector(refs) + push!(typed_refs, source_ref) + end + return RefVector(typed_refs) +end + +input_carrier(binding::CompiledSceneInputBinding) = binding.carrier +has_reference_carrier(binding::CompiledSceneInputBinding) = !isnothing(binding.carrier) +input_value(binding::CompiledSceneInputBinding) = _input_value(binding.carrier) +_input_value(::Nothing) = nothing +_input_value(carrier::Base.RefValue) = carrier[] +_input_value(carrier::RefVector) = carrier +_input_value(carrier::ObjectRefVector) = carrier + +function _compile_scene_input_bindings(scene::Scene, applications) + bindings = CompiledSceneInputBinding[] + by_object = _applications_by_object(applications) + for application in applications + for consumer_id in application.target_ids + declared_inputs = value_inputs(application.spec) + declared_inputs isa NamedTuple || (declared_inputs = NamedTuple()) + for (input_name, selector) in pairs(declared_inputs) + input_sym = Symbol(input_name) + selector isa AbstractObjectMultiplicity || error( + "Input binding `$(input_sym)` on application `$(application.id)` must use an object selector." + ) + _push_scene_input_binding!( + bindings, + scene, + application, + consumer_id, + input_sym, + selector, + :declared, + ) + end + _append_inferred_scene_input_bindings!(bindings, scene, application, consumer_id, declared_inputs, by_object) + end + end + return bindings +end + +function _push_scene_input_binding!( + bindings, + scene::Scene, + application::CompiledSceneApplication, + consumer_id::ObjectId, + input_sym::Symbol, + selector::AbstractObjectMultiplicity, + origin::Symbol, + source_ids_override=nothing, +) + source_ids = isnothing(source_ids_override) ? _dependency_object_ids(scene, selector, consumer_id) : source_ids_override + policy = _selector_policy(selector) + window = _selector_window(selector) + source_var = _selector_var(selector, input_sym) + carrier = _input_carrier(scene, selector, source_ids, source_var) + push!( + bindings, + CompiledSceneInputBinding( + application.id, + consumer_id, + input_sym, + selector, + origin, + source_ids, + source_var, + multiplicity(selector), + policy, + window, + _carrier_hint(selector, policy, window), + carrier, + ), + ) + return bindings +end + +function _scene_input_names(application::CompiledSceneApplication) + return Symbol[Symbol(var) for var in keys(inputs_(application.spec))] +end + +function _same_object_output_applications(applications_by_object, application::CompiledSceneApplication, object_id::ObjectId, variable::Symbol) + matches = CompiledSceneApplication[] + for candidate in get(applications_by_object, object_id, Any[]) + candidate.id == application.id && continue + variable in _scene_output_names(candidate) || continue + push!(matches, candidate) + end + return matches +end + +function _append_inferred_scene_input_bindings!( + bindings, + scene::Scene, + application::CompiledSceneApplication, + consumer_id::ObjectId, + declared_inputs, + applications_by_object, +) + declared_names = declared_inputs isa NamedTuple ? Set(Symbol.(keys(declared_inputs))) : Set{Symbol}() + for input_sym in _scene_input_names(application) + input_sym in declared_names && continue + matches = _same_object_output_applications(applications_by_object, application, consumer_id, input_sym) + isempty(matches) && continue + if length(matches) > 1 + error( + "Input `$(input_sym)` on application `$(application.id)` for object `$(consumer_id.value)` ", + "has ambiguous same-object producers: `$([match.id for match in matches])`. ", + "Add `Inputs(:$(input_sym) => One(...))` to disambiguate." + ) + end + producer = only(matches) + selector = One(within=Self(), process=producer.process, application=producer.id, var=input_sym) + _push_scene_input_binding!( + bindings, + scene, + application, + consumer_id, + input_sym, + selector, + :inferred_same_object, + ObjectId[consumer_id], + ) + end + return bindings +end + +function _applications_by_object(applications) + by_object = Dict{ObjectId,Vector{Any}}() + for application in applications + for object_id in application.target_ids + push!(get!(by_object, object_id, Any[]), application) + end + end + return by_object +end + +function _matching_callee_applications(applications, object_id::ObjectId, proc, application_name_filter) + matches = Symbol[] + for application in get(applications, object_id, Any[]) + isnothing(proc) || application.process == proc || continue + isnothing(application_name_filter) || application.id == application_name_filter || continue + push!(matches, application.id) + end + return matches +end + +function _compile_scene_call_bindings(scene::Scene, applications) + by_object = _applications_by_object(applications) + bindings = CompiledSceneCallBinding[] + for application in applications + calls = model_calls(application.spec) + calls isa NamedTuple || continue + for consumer_id in application.target_ids + for (call_name, selector) in pairs(calls) + call_sym = Symbol(call_name) + selector isa AbstractObjectMultiplicity || error( + "Call binding `$(call_sym)` on application `$(application.id)` must use an object selector." + ) + callee_object_ids = _dependency_object_ids(scene, selector, consumer_id) + proc = _criteria_get(criteria(selector), :process, nothing) + app_name = _selector_application(selector) + callee_application_ids = Symbol[] + for object_id in callee_object_ids + append!( + callee_application_ids, + _matching_callee_applications(by_object, object_id, proc, app_name), + ) + end + unique!(callee_application_ids) + if isempty(callee_application_ids) + error( + "Call `$(call_sym)` on application `$(application.id)` matched objects ", + "$([id.value for id in callee_object_ids]) but no model application", + isnothing(proc) ? "." : " with process `$(proc)`.", + ) + end + if selector isa One && length(callee_application_ids) != 1 + error( + "Call `$(call_sym)` on application `$(application.id)` expected one callee application, ", + "got `$(callee_application_ids)`. Add `application=:name` to disambiguate." + ) + elseif selector isa OptionalOne && length(callee_application_ids) > 1 + error( + "Call `$(call_sym)` on application `$(application.id)` expected zero or one callee application, ", + "got `$(callee_application_ids)`. Add `application=:name` to disambiguate." + ) + end + push!( + bindings, + CompiledSceneCallBinding( + application.id, + consumer_id, + call_sym, + selector, + callee_object_ids, + callee_application_ids, + proc, + app_name, + multiplicity(selector), + ), + ) + end + end + end + return bindings +end + +function explain_scene_applications(compiled::CompiledScene) + return [ + ( + application_id=application.id, + process=application.process, + name=application.name, + target_ids=[id.value for id in application.target_ids], + applies_to=application.applies_to, + timestep=application.timestep, + clock=application.clock, + model_type=typeof(model_(application.spec)), + ) + for application in compiled.applications + ] +end + +function explain_schedule(compiled::CompiledScene) + timeline = _scene_timeline(compiled.scene) + manual_application_ids = _manual_call_application_ids(compiled) + return [ + ( + application_id=application.id, + process=application.process, + timestep=application.timestep, + clock=application.clock, + dt_steps=application.clock.dt, + phase=application.clock.phase, + dt_seconds=float(application.clock.dt) * timeline.base_step_seconds, + target_ids=[id.value for id in application.target_ids], + root_scheduled=!(application.id in manual_application_ids), + manual_call_only=application.id in manual_application_ids, + ) + for application in compiled.applications + ] +end + +function explain_bindings(compiled::CompiledScene) + return [ + ( + application_id=binding.application_id, + consumer_id=binding.consumer_id.value, + input=binding.input, + origin=binding.origin, + source_ids=[id.value for id in binding.source_ids], + source_var=binding.source_var, + multiplicity=binding.multiplicity, + policy=binding.policy, + window=binding.window, + carrier_hint=binding.carrier_hint, + has_reference_carrier=has_reference_carrier(binding), + carrier_type=isnothing(binding.carrier) ? nothing : typeof(binding.carrier), + selector=binding.selector, + ) + for binding in compiled.input_bindings + ] +end + +function explain_calls(compiled::CompiledScene) + return [ + ( + application_id=binding.application_id, + consumer_id=binding.consumer_id.value, + call=binding.call, + callee_object_ids=[id.value for id in binding.callee_object_ids], + callee_application_ids=binding.callee_application_ids, + process=binding.process, + application=binding.application, + multiplicity=binding.multiplicity, + selector=binding.selector, + ) + for binding in compiled.call_bindings + ] +end + +function explain_writers(compiled::CompiledScene) + groups = _scene_writer_groups(compiled.applications) + rows = NamedTuple[] + for ((object_id, variable), indexed_writers) in groups + sort!(indexed_writers; by=first) + applications = [application for (_, application) in indexed_writers] + push!( + rows, + ( + object_id=object_id.value, + variable=variable, + application_ids=[application.id for application in applications], + processes=[application.process for application in applications], + update_application_ids=[ + application.id for application in applications + if !isempty(_matching_updates(application.spec, variable)) + ], + update_after=[ + application.id => _update_after_labels(application.spec, variable) + for application in applications + if !isempty(_matching_updates(application.spec, variable)) + ], + duplicate=length(applications) > 1, + ), + ) + end + sort!(rows; by=row -> (string(row.object_id), string(row.variable))) + return rows +end + +function _environment_config_payload(config) + config isa EnvironmentConfig && return config.config + return config +end + +function _environment_backend_from_config(scene::Scene, config) + payload = _environment_config_payload(config) + isnothing(payload) && return environment_backend(scene.environment) + payload isa NamedTuple && haskey(payload, :backend) && return environment_backend(payload.backend) + payload isa AbstractEnvironmentBackend && return payload + return environment_backend(scene.environment) +end + +function _environment_provider_from_config(config, backend) + payload = _environment_config_payload(config) + payload isa NamedTuple && haskey(payload, :provider) && return Symbol(payload.provider) + payload isa Symbol && return payload + isnothing(backend) && return :none + return :scene +end + +function _object_environment_support(application::CompiledSceneApplication, object::Object) + scale = isnothing(object.scale) ? :Default : object.scale + return EnvironmentSupport(application.id, scale, application.process, object.status) +end + +bind_environment(backend, object::Object, support, config=nothing) = :global + +function _scene_environment_entities(scene::Scene) + return [ + ( + id=object.id.value, + object=object, + scale=object.scale, + kind=object.kind, + species=object.species, + name=object.name, + parent=isnothing(object.parent) ? nothing : object.parent.value, + geometry=geometry(object), + position=position(object), + bounds=bounds(object), + status=object.status, + ) + for object in scene_objects(scene) + ] +end + +function _scene_environment_backends(scene::Scene, compiled::CompiledScene) + backends = Any[] + seen = Set{UInt}() + for application in compiled.applications + backend = _environment_backend_from_config(scene, environment_config(application.spec)) + isnothing(backend) && continue + id = objectid(backend) + id in seen && continue + push!(seen, id) + push!(backends, backend) + end + return backends +end + +function _update_scene_environment_indices!(scene::Scene, compiled::CompiledScene) + entities = _scene_environment_entities(scene) + for backend in _scene_environment_backends(scene, compiled) + update_index!(backend, entities) + end + return nothing +end + +function _environment_variable_names(vars) + return Symbol[Symbol(var) for var in keys(vars)] +end + +function _compile_environment_bindings(scene::Scene, compiled::CompiledScene) + bindings = CompiledEnvironmentBinding[] + for application in compiled.applications + config = environment_config(application.spec) + backend = _environment_backend_from_config(scene, config) + provider = _environment_provider_from_config(config, backend) + required_inputs = _environment_variable_names(meteo_inputs_(application.spec)) + produced_outputs = _environment_variable_names(meteo_outputs_(application.spec)) + for object_id in application.target_ids + object = _scene_object(scene, object_id) + support = _object_environment_support(application, object) + cell = bind_environment(backend, object, support, _environment_config_payload(config)) + push!( + bindings, + CompiledEnvironmentBinding( + application.id, + object_id, + provider, + backend, + cell, + required_inputs, + produced_outputs, + support, + config, + ), + ) + end + end + return bindings +end + +function compile_environment_bindings(scene::Scene, compiled::CompiledScene=refresh_bindings!(scene)) + _update_scene_environment_indices!(scene, compiled) + return CompiledEnvironmentBindings( + scene, + _compile_environment_bindings(scene, compiled), + scene.revision, + scene.environment_revision, + ) +end + +function explain_environment_bindings(compiled::CompiledEnvironmentBindings) + return [ + ( + application_id=binding.application_id, + object_id=binding.object_id.value, + provider=binding.provider, + backend_type=isnothing(binding.backend) ? nothing : typeof(binding.backend), + cell=binding.cell, + required_inputs=binding.required_inputs, + produced_outputs=binding.produced_outputs, + support=binding.support, + config=binding.config, + ) + for binding in compiled.bindings + ] +end + +function explain_environment_bindings(scene::Scene) + return explain_environment_bindings(refresh_environment_bindings!(scene)) +end + +struct SceneRunContext{CS,EB,A,TS,C} + compiled::CS + environment_bindings::EB + application::A + object_id::ObjectId + temporal_streams::TS + time::Float64 + constants::C +end + +struct SceneCallTarget{CS,EB,A,S,TS,C} + compiled::CS + environment_bindings::EB + application::A + object_id::ObjectId + model + status::S + temporal_streams::TS + time::Float64 + constants::C +end + +function _compiled_application_by_id(compiled::CompiledScene, id::Symbol) + for application in compiled.applications + application.id == id && return application + end + error("No compiled scene application with id `$(id)`.") +end + +function _environment_binding_for(env_bindings::CompiledEnvironmentBindings, application_id::Symbol, object_id::ObjectId) + for binding in env_bindings.bindings + binding.application_id == application_id && binding.object_id == object_id && return binding + end + return nothing +end + +function _scene_object_status(scene::Scene, object_id::ObjectId) + object = _scene_object(scene, object_id) + object.status isa Status || error( + "Scene object `$(object_id.value)` has no `Status`; scene runtime requires status-backed objects." + ) + return object.status +end + +function _set_status_if_present!(status::Status, variable::Symbol, value) + variable in propertynames(status) || error( + "Cannot materialize input `$(variable)` because consumer status has no such variable." + ) + status[variable] = value + return status +end + +_scene_stream_key(object_id::ObjectId, variable::Symbol) = (object_id, variable) + +function _scene_publish_outputs!(streams, application::CompiledSceneApplication, object_id::ObjectId, status, time::Real) + isnothing(streams) && return nothing + for variable in keys(outputs_(application.spec)) + var = Symbol(variable) + hasproperty(status, var) || error( + "Application `$(application.id)` declares output `$(var)`, but object ", + "`$(object_id.value)` status has no such variable." + ) + key = _scene_stream_key(object_id, var) + samples = get!(streams, key, Tuple{Float64,Any}[]) + filter!(sample -> !isapprox(sample[1], float(time); atol=1.0e-8, rtol=0.0), samples) + push!(samples, (float(time), getproperty(status, var))) + end + return nothing +end + +function _scene_latest_sample(samples, time::Real) + latest = nothing + latest_t = -Inf + for (sample_t, value) in samples + sample_t <= float(time) || continue + sample_t >= latest_t || continue + latest = value + latest_t = sample_t + end + return latest +end + +function _scene_window_samples(samples, t_start::Real, t_end::Real) + return Any[value for (sample_t, value) in samples if float(t_start) <= sample_t <= float(t_end)] +end + +function _scene_window_reduce(values, durations, policy) + isempty(values) && return 0.0 + reducer = policy isa Integrate ? policy.reducer : (policy isa Aggregate ? policy.reducer : PlantMeteo.SumReducer()) + f = _normalize_policy_reducer(reducer) + applicable(f, values, durations) && return f(values, durations) + applicable(f, values) && return f(values) + reducer isa PlantMeteo.SumReducer && return sum(values) + reducer isa PlantMeteo.MeanReducer && return Statistics.mean(values) + reducer isa PlantMeteo.MinReducer && return minimum(values) + reducer isa PlantMeteo.MaxReducer && return maximum(values) + reducer isa PlantMeteo.FirstReducer && return first(values) + reducer isa PlantMeteo.LastReducer && return last(values) + error( + "Reducer `$(reducer)` is not callable on scene temporal input values for policy ", + "`$(typeof(policy))`. Expected `(values)` or `(values, durations_seconds)`." + ) +end + +function _scene_duration_steps(duration, timeline) + if duration isa Dates.Period || duration isa Dates.CompoundPeriod || duration isa Real + seconds = _duration_to_seconds(duration) + isnothing(seconds) && return nothing + steps = seconds / timeline.base_step_seconds + steps >= 1.0 || error( + "Input window `$(duration)` is shorter than the scene base step ", + "($(timeline.base_step_seconds) seconds)." + ) + return steps + end + return nothing +end + +function _scene_input_window_steps(binding::CompiledSceneInputBinding, application::CompiledSceneApplication, timeline) + explicit = _scene_duration_steps(binding.window, timeline) + !isnothing(explicit) && return explicit + binding.policy isa Union{Integrate,Aggregate} && return float(application.clock.dt) + return 1.0 +end + +function _scene_temporal_source_value(streams, source_id::ObjectId, source_var::Symbol, time::Real, policy, t_start::Real, timeline) + samples = get(streams, _scene_stream_key(source_id, source_var), nothing) + isnothing(samples) && return policy isa Union{Integrate,Aggregate} ? 0.0 : nothing + if policy isa HoldLast + return _scene_latest_sample(samples, time) + elseif policy isa Union{Integrate,Aggregate} + values = _scene_window_samples(samples, t_start, time) + durations = fill(timeline.base_step_seconds, length(values)) + return _scene_window_reduce(values, durations, policy) + elseif policy isa Interpolate + return _scene_latest_sample(samples, time) + end + error("Unsupported scene temporal input policy `$(typeof(policy))`.") +end + +function _scene_temporal_input_value(binding::CompiledSceneInputBinding, application::CompiledSceneApplication, streams, time::Real, timeline) + window_steps = _scene_input_window_steps(binding, application, timeline) + t_start = float(time) - float(window_steps) + 1.0 + if binding.multiplicity == :many + return [ + _scene_temporal_source_value(streams, source_id, binding.source_var, time, binding.policy, t_start, timeline) + for source_id in binding.source_ids + ] + end + source_id = only(binding.source_ids) + value = _scene_temporal_source_value(streams, source_id, binding.source_var, time, binding.policy, t_start, timeline) + isnothing(value) && error( + "No temporal scene value available for input `$(binding.input)` from ", + "`$(source_id.value).$(binding.source_var)` at t=$(time)." + ) + return value +end + +function _scene_assign_input_value!(status::Status, variable::Symbol, value) + variable in propertynames(status) || error( + "Cannot materialize input `$(variable)` because consumer status has no such variable." + ) + current = status[variable] + if current isa RefVector && value isa AbstractVector + length(current) != length(value) && resize!(current, length(value)) + for i in eachindex(value) + current[i] = value[i] + end + return status + end + status[variable] = value + return status +end + +function _materialize_scene_inputs!( + compiled::CompiledScene, + application::CompiledSceneApplication, + object_id::ObjectId, + streams=nothing, + time::Real=1, +) + status = _scene_object_status(compiled.scene, object_id) + timeline = _scene_timeline(compiled.scene) + for binding in compiled.input_bindings + binding.application_id == application.id || continue + binding.consumer_id == object_id || continue + if binding.carrier_hint == :temporal_stream + isnothing(streams) && continue + value = _scene_temporal_input_value(binding, application, streams, time, timeline) + _scene_assign_input_value!(status, binding.input, value) + elseif has_reference_carrier(binding) + _set_status_if_present!(status, binding.input, input_value(binding)) + end + end + return status +end + +function _scene_meteo_for_model( + env_bindings::CompiledEnvironmentBindings, + application::CompiledSceneApplication, + object_id::ObjectId, + time::Real, +) + binding = _environment_binding_for(env_bindings, application.id, object_id) + isnothing(binding) && return nothing + isnothing(binding.backend) && return nothing + return sample_environment(binding.backend, binding.support, time, application.spec) +end + +function _scatter_scene_environment_outputs!( + env_bindings::CompiledEnvironmentBindings, + application::CompiledSceneApplication, + object_id::ObjectId, + status, + time::Real, +) + isempty(keys(meteo_outputs_(application.spec))) && return nothing + binding = _environment_binding_for(env_bindings, application.id, object_id) + isnothing(binding) && return nothing + isnothing(binding.backend) && return nothing + return scatter_environment_outputs!(binding.backend, binding.support, time, application.spec, status) +end + +function _run_scene_application!( + compiled::CompiledScene, + env_bindings::CompiledEnvironmentBindings, + application::CompiledSceneApplication, + object_id::ObjectId; + time::Real=1, + constants=nothing, + temporal_streams=nothing, + publish::Bool=true, +) + status = _materialize_scene_inputs!(compiled, application, object_id, temporal_streams, time) + meteo = _scene_meteo_for_model(env_bindings, application, object_id, time) + context = SceneRunContext(compiled, env_bindings, application, object_id, temporal_streams, float(time), constants) + run!(application.spec, nothing, status, meteo, constants, context) + if publish + _scatter_scene_environment_outputs!(env_bindings, application, object_id, status, time) + _scene_publish_outputs!(temporal_streams, application, object_id, status, time) + end + return status +end + +_scene_application_should_run(application::CompiledSceneApplication, t::Real) = + _should_run_at_time(application.clock, float(t)) + +function _manual_call_application_ids(compiled::CompiledScene) + ids = Set{Symbol}() + for binding in compiled.call_bindings + union!(ids, binding.callee_application_ids) + end + return ids +end + +function _scene_call_targets(context::SceneRunContext, name::Symbol) + targets = SceneCallTarget[] + for binding in context.compiled.call_bindings + binding.application_id == context.application.id || continue + binding.consumer_id == context.object_id || continue + binding.call == name || continue + for application_id in binding.callee_application_ids + callee_application = _compiled_application_by_id(context.compiled, application_id) + for object_id in binding.callee_object_ids + object_id in callee_application.target_ids || continue + status = _scene_object_status(context.compiled.scene, object_id) + push!( + targets, + SceneCallTarget( + context.compiled, + context.environment_bindings, + callee_application, + object_id, + model_(callee_application.spec), + status, + context.temporal_streams, + context.time, + context.constants, + ), + ) + end + end + end + return targets +end + +dependency_targets(context::SceneRunContext, name::Symbol) = _scene_call_targets(context, name) +dependency_target(context::SceneRunContext, name::Symbol) = only(dependency_targets(context, name)) + +function run_call!(target::SceneCallTarget; publish::Bool=true) + _run_scene_application!( + target.compiled, + target.environment_bindings, + target.application, + target.object_id; + time=target.time, + constants=target.constants, + temporal_streams=target.temporal_streams, + publish=publish, + ) + return target +end + +function run!(scene::Scene; steps::Integer=1, constants=nothing) + compiled = refresh_bindings!(scene) + env_bindings = refresh_environment_bindings!(scene, compiled) + manual_application_ids = _manual_call_application_ids(compiled) + temporal_streams = Dict{Tuple{ObjectId,Symbol},Vector{Tuple{Float64,Any}}}() + for step in 1:steps + for application in compiled.applications + application.id in manual_application_ids && continue + _scene_application_should_run(application, step) || continue + for object_id in application.target_ids + _run_scene_application!( + compiled, + env_bindings, + application, + object_id; + time=step, + constants=constants, + temporal_streams=temporal_streams, + publish=true, + ) + end + end + end + return scene +end + +struct ObjectAddress{SC,K,SP,S,N,P,V,R,M} + scope::SC + kind::K + species::SP + scale::S + name::N + process::P + var::V + relation::R + multiplicity::M +end + +function ObjectAddress(selector::AbstractObjectMultiplicity) + c = criteria(selector) + scope = haskey(c, :within) ? c.within : nothing + kind = haskey(c, :kind) ? c.kind : nothing + species = haskey(c, :species) ? c.species : nothing + scale = haskey(c, :scale) ? c.scale : nothing + name = haskey(c, :name) ? c.name : nothing + process = haskey(c, :process) ? c.process : nothing + var = haskey(c, :var) ? c.var : nothing + relation = haskey(c, :relation) ? c.relation : nothing + return ObjectAddress(scope, kind, species, scale, name, process, var, relation, multiplicity(selector)) +end + +object_address(selector::AbstractObjectMultiplicity) = ObjectAddress(selector) + +struct Input{S} + selector::S +end +Input(; kwargs...) = Input(One(; kwargs...)) + +struct Call{S} + selector::S +end +Call(; kwargs...) = Call(One(; kwargs...)) + +struct EnvironmentConfig{C} + config::C +end + +_normalize_application_name(name) = isnothing(name) ? nothing : Symbol(name) + +function _normalize_application_bindings(bindings::NamedTuple) + return bindings +end + +function _normalize_application_bindings(bindings::Tuple) + pairs = Pair{Symbol,Any}[] + for binding in bindings + binding isa Pair || error( + "Expected `var => selector` pairs in `Inputs(...)` or `Calls(...)`, got `$(typeof(binding))`." + ) + first(binding) isa Union{Symbol,AbstractString} || error( + "Binding names in `Inputs(...)` and `Calls(...)` must be symbols or strings." + ) + push!(pairs, Symbol(first(binding)) => last(binding)) + end + return (; pairs...) +end + +function _normalize_application_bindings(binding::Pair) + return _normalize_application_bindings((binding,)) +end + +function _normalize_application_bindings(bindings) + error( + "Unsupported binding declaration `$(bindings)` of type `$(typeof(bindings))`. ", + "Use pairs such as `:x => Many(...)` or keyword arguments." + ) +end + +function _model_default_value_inputs(model) + defaults = Pair{Symbol,Any}[] + for (dep_name, selector) in pairs(dep(model)) + selector isa Input || continue + push!(defaults, Symbol(dep_name) => selector.selector) + end + return (; defaults...) +end + +function _model_default_model_calls(model) + defaults = Pair{Symbol,Any}[] + for (dep_name, selector) in pairs(dep(model)) + selector isa Call || continue + push!(defaults, Symbol(dep_name) => selector.selector) + end + return (; defaults...) +end + +function _merge_value_inputs(defaults::NamedTuple, explicit::NamedTuple) + return (; pairs(defaults)..., pairs(explicit)...) +end + +function _legacy_multiscale_rhs_from_input_selector(selector::AbstractObjectMultiplicity) + c = criteria(selector) + haskey(c, :scale) || return nothing + + # The current MTG mapping layer only understands scale/variable mappings. + # Keep richer object filters as unified metadata for the future compiler. + unsupported = (:kind, :domain, :species, :name, :process, :relation) + any(key -> haskey(c, key) && !isnothing(getproperty(c, key)), unsupported) && return nothing + + scale = c.scale + src_var = haskey(c, :var) ? c.var : nothing + if selector isa Many + return isnothing(src_var) ? [scale] : [scale => src_var] + elseif selector isa One || selector isa OptionalOne + return isnothing(src_var) ? scale : (scale => src_var) + end + return nothing +end + +function _legacy_multiscale_from_value_inputs(bindings::NamedTuple, model=nothing) + mapped = Pair{Symbol,Any}[] + model_input_names = isnothing(model) ? nothing : Set(keys(inputs_(model))) + for (input_var, selector) in pairs(bindings) + input_sym = Symbol(input_var) + if !isnothing(model_input_names) && !(input_sym in model_input_names) + continue + end + selector isa AbstractObjectMultiplicity || continue + rhs = _legacy_multiscale_rhs_from_input_selector(selector) + isnothing(rhs) && continue + push!(mapped, input_sym => rhs) + end + return mapped +end + +function _merge_legacy_multiscale(existing, derived::Vector{Pair{Symbol,Any}}) + isempty(derived) && return existing + if isnothing(existing) + return derived + end + derived_inputs = Set(first(item) for item in derived) + merged = Pair{Any,Any}[] + for item in collect(existing) + mapped_input = first(item) + mapped_input = mapped_input isa PreviousTimeStep ? mapped_input.variable : mapped_input + mapped_input in derived_inputs && continue + push!(merged, item) + end + append!(merged, derived) + return merged +end diff --git a/test/runtests.jl b/test/runtests.jl index 093e0c860..454f6d120 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -30,6 +30,10 @@ include("helper-functions.jl") include("test-multirate-scaffolding.jl") end + @testset "Unified scene/object API" begin + include("test-unified-scene-object-api.jl") + end + @testset "Multi-rate runtime" begin include("test-multirate-runtime.jl") end diff --git a/test/test-domain-simulation.jl b/test/test-domain-simulation.jl index 52d56c5e7..fc25ff57f 100644 --- a/test/test-domain-simulation.jl +++ b/test/test-domain-simulation.jl @@ -11,6 +11,7 @@ PlantSimEngine.@process "domain_hard_leaf_energy" verbose = false PlantSimEngine.@process "domain_scene_conductance_sum" verbose = false PlantSimEngine.@process "domain_hard_target_signal" verbose = false PlantSimEngine.@process "domain_scene_hard_target_sum" verbose = false +PlantSimEngine.@process "domain_scene_calls_target_sum" verbose = false PlantSimEngine.@process "domain_hard_target_leaf_counter" verbose = false PlantSimEngine.@process "domain_scene_hard_target_leaf_sum" verbose = false PlantSimEngine.@process "domain_scene_routed_vector" verbose = false @@ -191,6 +192,21 @@ function PlantSimEngine.run!(::DomainSceneHardTargetSumModel, models, status, me return nothing end +struct DomainSceneCallsTargetSumModel <: AbstractDomain_Scene_Calls_Target_SumModel end + +PlantSimEngine.inputs_(::DomainSceneCallsTargetSumModel) = NamedTuple() +PlantSimEngine.outputs_(::DomainSceneCallsTargetSumModel) = (calls_target_total=0.0,) + +function PlantSimEngine.run!(::DomainSceneCallsTargetSumModel, models, status, meteo, constants=nothing, extra=nothing) + targets = dependency_targets(extra, :plant_signal) + for target in targets + run_call!(target) + run_call!(target) + end + status.calls_target_total = sum(target.status.signal for target in targets) + return nothing +end + struct DomainHardTargetLeafCounterModel{T} <: AbstractDomain_Hard_Target_Leaf_CounterModel coefficient::T end @@ -708,6 +724,25 @@ end @test status(hard_target_sim, :scene).hard_target_total ≈ 4.0 @test only(explain_domain_dependencies(hard_target_sim)).mode == :hard_domain + calls_target_scene_mapping = ModelMapping( + ModelSpec(DomainSceneCallsTargetSumModel()) |> + Calls(:plant_signal => Many(kind=:plant, process=:domain_hard_target_signal)) |> + TimeStep(Dates.Hour(1)), + status=(calls_target_total=0.0,), + ) + calls_target_sim = run!( + SimulationMapping( + Domain(:hard_target_plant, hard_target_plant_mapping; kind=:plant), + Domain(:scene, calls_target_scene_mapping; kind=:scene), + ), + Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)), + check=true, + ) + @test status(calls_target_sim, :hard_target_plant).call_count == 2 + @test status(calls_target_sim, :hard_target_plant).signal ≈ 4.0 + @test status(calls_target_sim, :scene).calls_target_total ≈ 4.0 + @test only(explain_domain_dependencies(calls_target_sim)).mode == :hard_domain + route_source = AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration) bad_route_source = AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:missing_output) route_selector_error = try @@ -822,6 +857,29 @@ end @test all(row -> row.target_var == :plant_transpirations, route_rows) @test all(row -> row.cardinality == ManyToOneVector, route_rows) + inputs_route_scene_mapping = ModelMapping( + ModelSpec(DomainSceneRoutedVectorModel()) |> + Inputs(:plant_transpirations => Many(kind=:plant, process=:domain_plant_transpiration, var=:transpiration)) |> + TimeStep(Dates.Hour(1)), + status=(routed_total=0.0,), + ) + inputs_route_sim = run!( + SimulationMapping( + Domain(:oil_palm, oil_palm_mapping; kind=:plant), + Domain(:maize, maize_mapping; kind=:plant), + Domain(:scene, inputs_route_scene_mapping; kind=:scene), + ), + hourly_meteo, + check=true, + ) + @test :plant_transpirations in propertynames(status(inputs_route_sim, :scene)) + @test status(inputs_route_sim, :scene).plant_transpirations ≈ [0.5, 0.6] + @test status(inputs_route_sim, :scene).routed_total ≈ hourly_plant_sum + input_route_rows = explain_routes(inputs_route_sim) + @test length(input_route_rows) == 2 + @test all(row -> row.target_var == :plant_transpirations, input_route_rows) + @test all(row -> row.cardinality == ManyToOneVector, input_route_rows) + reordered_collector_mapping = ModelMapping( ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStepModel(Dates.Hour(1)), status=(plant_transpirations=[0.0], routed_total=0.0), diff --git a/test/test-maespa-domain-example.jl b/test/test-maespa-domain-example.jl index b6378ed03..e4c741d65 100644 --- a/test/test-maespa-domain-example.jl +++ b/test/test-maespa-domain-example.jl @@ -65,7 +65,12 @@ end ) scene_mapping = ModelMapping( ModelSpec(LAIModel(1.0)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(SceneEB(25, 0.03, 0.005)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(SceneEB(25, 0.03, 0.005)) |> + Calls( + :energy_balance => Many(kind=:plant, scale=:Leaf, process=:energy_balance), + :soil => One(kind=:soil, process=:soil_water), + ) |> + TimeStep(Dates.Hour(1)), status=( leaf_area=0.0, lai=0.0, diff --git a/test/test-unified-scene-object-api.jl b/test/test-unified-scene-object-api.jl new file mode 100644 index 000000000..c4369790a --- /dev/null +++ b/test/test-unified-scene-object-api.jl @@ -0,0 +1,868 @@ +using Dates +using PlantSimEngine +using PlantSimEngine.Examples +using Test + +PlantSimEngine.@process "scene_object_default_input_consumer" verbose = false + +struct SceneObjectDefaultInputConsumerModel <: AbstractScene_Object_Default_Input_ConsumerModel end + +PlantSimEngine.inputs_(::SceneObjectDefaultInputConsumerModel) = (leaf_carbon=[0.0],) +PlantSimEngine.outputs_(::SceneObjectDefaultInputConsumerModel) = (plant_carbon=0.0,) +PlantSimEngine.dep(::SceneObjectDefaultInputConsumerModel) = ( + leaf_carbon=Input(Many(scale=:Leaf, within=Self(), var=:leaf_carbon)), +) + +PlantSimEngine.@process "scene_object_default_call_consumer" verbose = false + +struct SceneObjectDefaultCallConsumerModel <: AbstractScene_Object_Default_Call_ConsumerModel end + +PlantSimEngine.inputs_(::SceneObjectDefaultCallConsumerModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectDefaultCallConsumerModel) = (energy_balance=0.0,) +PlantSimEngine.dep(::SceneObjectDefaultCallConsumerModel) = ( + stomata=Call(scale=:Leaf, process=:stomatal_conductance), +) + +PlantSimEngine.@process "scene_object_stomata" verbose = false + +struct SceneObjectStomataModel <: AbstractScene_Object_StomataModel end + +PlantSimEngine.inputs_(::SceneObjectStomataModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectStomataModel) = (gs=0.0,) + +PlantSimEngine.@process "scene_object_leaf_energy" verbose = false + +struct SceneObjectLeafEnergyModel <: AbstractScene_Object_Leaf_EnergyModel end + +PlantSimEngine.inputs_(::SceneObjectLeafEnergyModel) = (leaf_areas=[0.0],) +PlantSimEngine.outputs_(::SceneObjectLeafEnergyModel) = (leaf_temperature=25.0,) +PlantSimEngine.dep(::SceneObjectLeafEnergyModel) = ( + stomata=Call(process=:scene_object_stomata), +) + +PlantSimEngine.@process "scene_object_carrier_consumer" verbose = false + +struct SceneObjectCarrierConsumerModel <: AbstractScene_Object_Carrier_ConsumerModel end + +PlantSimEngine.inputs_(::SceneObjectCarrierConsumerModel) = (leaf_areas=[0.0], leaf_tokens=Any[]) +PlantSimEngine.outputs_(::SceneObjectCarrierConsumerModel) = (carrier_total=0.0,) + +function PlantSimEngine.run!(::SceneObjectCarrierConsumerModel, models, status, meteo, constants=nothing, extra=nothing) + status.carrier_total = sum(status.leaf_areas) + return nothing +end + +PlantSimEngine.@process "scene_object_environment_probe" verbose = false + +struct SceneObjectEnvironmentProbeModel <: AbstractScene_Object_Environment_ProbeModel end + +PlantSimEngine.inputs_(::SceneObjectEnvironmentProbeModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectEnvironmentProbeModel) = (temperature_seen=0.0,) +PlantSimEngine.meteo_inputs_(::SceneObjectEnvironmentProbeModel) = (T=0.0, CO2=0.0) + +function PlantSimEngine.run!(::SceneObjectEnvironmentProbeModel, models, status, meteo, constants=nothing, extra=nothing) + status.temperature_seen = meteo.T + return nothing +end + +PlantSimEngine.@process "scene_object_environment_update" verbose = false + +struct SceneObjectEnvironmentUpdateModel <: AbstractScene_Object_Environment_UpdateModel end + +PlantSimEngine.inputs_(::SceneObjectEnvironmentUpdateModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectEnvironmentUpdateModel) = (temperature_update=0.0,) +PlantSimEngine.meteo_inputs_(::SceneObjectEnvironmentUpdateModel) = (T=0.0,) +PlantSimEngine.meteo_outputs_(::SceneObjectEnvironmentUpdateModel) = (T=0.0,) + +function PlantSimEngine.run!(::SceneObjectEnvironmentUpdateModel, models, status, meteo, constants=nothing, extra=nothing) + status.temperature_update = meteo.T + 1.0 + status.T = status.temperature_update + return nothing +end + +PlantSimEngine.@process "scene_object_signal_source" verbose = false + +struct SceneObjectSignalSourceModel <: AbstractScene_Object_Signal_SourceModel end + +PlantSimEngine.inputs_(::SceneObjectSignalSourceModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectSignalSourceModel) = (signal=0.0,) + +function PlantSimEngine.run!(::SceneObjectSignalSourceModel, models, status, meteo, constants=nothing, extra=nothing) + status.signal += 1.0 + return nothing +end + +PlantSimEngine.@process "scene_object_signal_caller" verbose = false + +struct SceneObjectSignalCallerModel <: AbstractScene_Object_Signal_CallerModel end + +PlantSimEngine.inputs_(::SceneObjectSignalCallerModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectSignalCallerModel) = (called_signal=0.0,) +PlantSimEngine.dep(::SceneObjectSignalCallerModel) = ( + signal=Call(process=:scene_object_signal_source), +) + +function PlantSimEngine.run!(::SceneObjectSignalCallerModel, models, status, meteo, constants=nothing, extra=nothing) + target = dependency_target(extra, :signal) + run_call!(target) + status.called_signal = target.status.signal + return nothing +end + +PlantSimEngine.@process "scene_object_signal_consumer" verbose = false + +struct SceneObjectSignalConsumerModel <: AbstractScene_Object_Signal_ConsumerModel end + +PlantSimEngine.inputs_(::SceneObjectSignalConsumerModel) = (signal=0.0,) +PlantSimEngine.outputs_(::SceneObjectSignalConsumerModel) = (observed_signal=0.0,) + +function PlantSimEngine.run!(::SceneObjectSignalConsumerModel, models, status, meteo, constants=nothing, extra=nothing) + status.observed_signal = status.signal + return nothing +end + +PlantSimEngine.@process "scene_object_temporal_sum" verbose = false + +struct SceneObjectTemporalSumModel <: AbstractScene_Object_Temporal_SumModel end + +PlantSimEngine.inputs_(::SceneObjectTemporalSumModel) = (signal_sum=0.0,) +PlantSimEngine.outputs_(::SceneObjectTemporalSumModel) = (temporal_total=0.0,) + +function PlantSimEngine.run!(::SceneObjectTemporalSumModel, models, status, meteo, constants=nothing, extra=nothing) + status.temporal_total = status.signal_sum + return nothing +end + +PlantSimEngine.@process "scene_object_biomass_source" verbose = false + +struct SceneObjectBiomassSourceModel <: AbstractScene_Object_Biomass_SourceModel end + +PlantSimEngine.inputs_(::SceneObjectBiomassSourceModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectBiomassSourceModel) = (biomass=0.0,) + +function PlantSimEngine.run!(::SceneObjectBiomassSourceModel, models, status, meteo, constants=nothing, extra=nothing) + status.biomass = 10.0 + return nothing +end + +PlantSimEngine.@process "scene_object_biomass_pruner" verbose = false + +struct SceneObjectBiomassPrunerModel <: AbstractScene_Object_Biomass_PrunerModel end + +PlantSimEngine.inputs_(::SceneObjectBiomassPrunerModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectBiomassPrunerModel) = (biomass=0.0,) + +function PlantSimEngine.run!(::SceneObjectBiomassPrunerModel, models, status, meteo, constants=nothing, extra=nothing) + status.biomass = 0.0 + return nothing +end + +mutable struct SceneObjectGridBackend <: PlantSimEngine.AbstractEnvironmentBackend + binds::Vector{Any} + index_updates::Vector{Any} +end + +SceneObjectGridBackend(binds::Vector{Any}=Any[]) = SceneObjectGridBackend(binds, Any[]) + +struct SceneObjectTaggedValue + value::Int +end + +PlantSimEngine.base_step_seconds(::SceneObjectGridBackend) = 3600.0 +PlantSimEngine.get_nsteps(::SceneObjectGridBackend) = 1 +PlantSimEngine.environment_variables(::SceneObjectGridBackend) = Set([:T, :CO2]) + +function PlantSimEngine.bind_environment( + backend::SceneObjectGridBackend, + object::Object, + support, + config, +) + object_geometry = geometry(object) + cell = isnothing(object_geometry) ? :global : object_geometry.cell + push!(backend.binds, (object=object.id.value, application=support.domain, cell=cell, config=config)) + return cell +end + +function PlantSimEngine.update_index!(backend::SceneObjectGridBackend, entities) + push!( + backend.index_updates, + [ + ( + id=entity.id, + scale=entity.scale, + kind=entity.kind, + geometry=entity.geometry, + position=entity.position, + bounds=entity.bounds, + ) + for entity in entities + ], + ) + return nothing +end + +mutable struct SceneObjectMutableEnvironmentBackend <: PlantSimEngine.AbstractEnvironmentBackend + values::Dict{Symbol,Float64} + cells_by_status::Dict{UInt,Symbol} + writes::Vector{Any} +end + +SceneObjectMutableEnvironmentBackend(values::Pair...) = + SceneObjectMutableEnvironmentBackend(Dict{Symbol,Float64}(values), Dict{UInt,Symbol}(), Any[]) + +PlantSimEngine.base_step_seconds(::SceneObjectMutableEnvironmentBackend) = 3600.0 +PlantSimEngine.get_nsteps(::SceneObjectMutableEnvironmentBackend) = 1 +PlantSimEngine.environment_variables(::SceneObjectMutableEnvironmentBackend) = Set([:T, :CO2]) + +function PlantSimEngine.bind_environment( + backend::SceneObjectMutableEnvironmentBackend, + object::Object, + support, + config, +) + cell = object.geometry.cell + backend.cells_by_status[objectid(object.status)] = cell + return cell +end + +function PlantSimEngine.sample( + backend::SceneObjectMutableEnvironmentBackend, + variable::Symbol, + support::EnvironmentSupport, + time, +) + variable == :CO2 && return 410.0 + variable == :T || error("Unexpected variable `$(variable)`.") + cell = backend.cells_by_status[objectid(support.status)] + return backend.values[cell] +end + +function PlantSimEngine.scatter!( + backend::SceneObjectMutableEnvironmentBackend, + variable::Symbol, + support::EnvironmentSupport, + value, + time, +) + variable == :T || error("Unexpected variable `$(variable)`.") + cell = backend.cells_by_status[objectid(support.status)] + backend.values[cell] = value + push!( + backend.writes, + ( + application=support.domain, + process=support.process, + cell=cell, + variable=variable, + value=value, + time=time, + ), + ) + return nothing +end + +@testset "Unified scene/object API" begin + scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:plant_1; scale=:Plant, kind=:plant, species=:oil_palm, parent=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1, geometry=(x=1.0, y=0.0)), + Object(:leaf_2; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1), + ) + + @test object_ids(scene; scale=:Leaf) == [ObjectId(:leaf_1), ObjectId(:leaf_2)] + @test object_ids(scene; kind=:plant, species=:oil_palm) == [ObjectId(:leaf_1), ObjectId(:leaf_2), ObjectId(:plant_1)] + @test only(scene_objects(scene; scale=:Scene)).id == ObjectId(:scene) + + leaf_2 = move_object!(scene, :leaf_2, (x=2.0, y=0.0)) + @test leaf_2.geometry == (x=2.0, y=0.0) + @test geometry(leaf_2) == (x=2.0, y=0.0) + @test position(leaf_2) == (x=2.0, y=0.0) + @test isnothing(bounds(leaf_2)) + bounded_leaf = Object(:bounded_leaf; scale=:Leaf, geometry=(position=(x=1.0, y=2.0, z=3.0), bounds=(radius=0.5,))) + @test position(bounded_leaf) == (x=1.0, y=2.0, z=3.0) + @test bounds(bounded_leaf) == (radius=0.5,) + + new_axis = register_object!(scene, Object(:axis_1; scale=:Axis, kind=:plant, species=:oil_palm); parent=:plant_1) + @test new_axis.parent == ObjectId(:plant_1) + @test ObjectId(:axis_1) in only(scene_objects(scene; scale=:Plant)).children + + reparent_object!(scene, :leaf_2, :axis_1) + @test only(scene_objects(scene; name=nothing, scale=:Axis)).children == [ObjectId(:leaf_2)] + @test ObjectId(:leaf_2) ∉ only(scene_objects(scene; scale=:Plant)).children + + removed_axis = remove_object!(scene, :axis_1) + @test removed_axis.id == ObjectId(:axis_1) + @test object_ids(scene; scale=:Axis) == ObjectId[] + @test object_ids(scene; name=:leaf_2) == ObjectId[] + + object_rows = explain_objects(scene) + @test length(object_rows) == 3 + @test any(row -> row.id == :leaf_1 && row.has_geometry, object_rows) + @test any(row -> row.id == :plant_1 && row.children == [ObjectId(:leaf_1).value], object_rows) + + selector_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:plant_1; scale=:Plant, kind=:plant, species=:oil_palm, name=:palm_1, parent=:scene), + Object(:axis_1; scale=:Axis, kind=:plant, species=:oil_palm, parent=:plant_1), + Object(:leaf_1; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1), + Object(:leaf_2; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:axis_1), + Object(:plant_2; scale=:Plant, kind=:plant, species=:oil_palm, name=:palm_2, parent=:scene), + Object(:leaf_3; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_2), + Object(:soil; scale=:Soil, kind=:soil, parent=:scene), + ) + + @test resolve_object_ids(selector_scene, Many(scale=:Leaf)) == + [ObjectId(:leaf_1), ObjectId(:leaf_2), ObjectId(:leaf_3)] + @test only(resolve_objects(selector_scene, One(scale=:Scene))).id == ObjectId(:scene) + @test resolve_object_ids(selector_scene, Many(Kind(:plant), Scale(:Leaf))) == + [ObjectId(:leaf_1), ObjectId(:leaf_2), ObjectId(:leaf_3)] + @test resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Self()); context=:plant_1) == + [ObjectId(:leaf_1), ObjectId(:leaf_2)] + @test resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Self()); context=:leaf_2) == + [ObjectId(:leaf_2)] + @test resolve_object_ids(selector_scene, Many(scale=:Leaf, within=SelfPlant()); context=:leaf_2) == + [ObjectId(:leaf_1), ObjectId(:leaf_2)] + @test resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Ancestor(scale=:Axis)); context=:leaf_2) == + [ObjectId(:leaf_2)] + @test resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Scope(:palm_2))) == + [ObjectId(:leaf_3)] + @test resolve_object_ids(selector_scene, OptionalOne(scale=:Flower)) == ObjectId[] + @test_throws ErrorException resolve_object_ids(selector_scene, One(scale=:Flower)) + @test_throws ErrorException resolve_object_ids(selector_scene, One(scale=:Leaf)) + @test_throws ErrorException resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Self())) + + leaf_selector = Many( + kind="plant", + scale=:Leaf, + within=Self(), + process="leaf_state", + var="leaf_area", + policy=Integrate(), + window=Day(1), + ) + + @test leaf_selector.criteria.kind == :plant + @test leaf_selector.criteria.scale == :Leaf + @test leaf_selector.criteria.within isa Self + @test leaf_selector.criteria.process == :leaf_state + @test leaf_selector.criteria.var == :leaf_area + @test leaf_selector.criteria.policy isa Integrate + @test leaf_selector.criteria.window == Day(1) + + address = object_address(leaf_selector) + @test address.scope isa Self + @test address.kind == :plant + @test address.scale == :Leaf + @test address.process == :leaf_state + @test address.var == :leaf_area + @test address.multiplicity == :many + + @test One(Kind(:plant), Scale(:Leaf)).criteria.selectors == (Kind(:plant), Scale(:Leaf)) + @test object_address(OptionalOne(scale=:Scene)).multiplicity == :optional_one + + default_input = Input(Many(scale=:Leaf, within=Self(), var=:leaf_carbon)) + @test default_input.selector isa Many + @test default_input.selector.criteria.within isa Self + + default_call = Call(process=:stomatal_conductance) + @test default_call.selector isa One + @test object_address(default_call.selector).process == :stomatal_conductance + + m = Process1Model(1.0) + spec = ModelSpec(m; name=:leaf_energy) |> + AppliesTo(Many(kind=:plant, scale=:Leaf)) |> + Inputs( + :leaf_areas => Many(kind=:plant, scale=:Leaf, within=SceneScope(), var=:leaf_area), + :leaf_carbon => Many(scale=:Leaf, within=Self(), var=:leaf_carbon, policy=Integrate(), window=Day(1)), + ) |> + Calls(:stomata => One(scale=:Leaf, process=:stomatal_conductance)) |> + TimeStep(Hour(1)) |> + Environment(provider=:global) + + @test PlantSimEngine.model_(spec) === m + @test application_name(spec) == :leaf_energy + @test applies_to(spec) isa Many + @test applies_to(spec).criteria.kind == :plant + @test value_inputs(spec).leaf_areas isa Many + @test value_inputs(spec).leaf_carbon.criteria.policy isa Integrate + @test value_inputs(spec).leaf_carbon.criteria.window == Day(1) + @test model_calls(spec).stomata isa One + @test object_address(model_calls(spec).stomata).process == :stomatal_conductance + call_dep = dep(spec).stomata + @test call_dep isa HardDomains + @test call_dep.scale == :Leaf + @test call_dep.process == :stomatal_conductance + @test PlantSimEngine.timestep(spec) == Hour(1) + @test environment_config(spec) isa PlantSimEngine.EnvironmentConfig + @test environment_config(spec).config.provider == :global + + # The old multirate metadata stays available while the compiler is migrated. + legacy_and_unified = spec |> + InputBindings(; var1=(process=:process1, var=:var3)) |> + OutputRouting(; var3=:stream_only) |> + ScopeModel(:plant) |> + Updates(:var3; after=:process1) + @test input_bindings(legacy_and_unified).var1.process == :process1 + @test output_routing(legacy_and_unified).var3 == :stream_only + @test model_scope(legacy_and_unified) == :plant + @test updates(legacy_and_unified)[1].after == (:process1,) + @test value_inputs(legacy_and_unified) == value_inputs(spec) + @test model_calls(legacy_and_unified) == model_calls(spec) + + rows = explain_model_specs(Dict(:Leaf => (spec,)); io=IOBuffer()) + @test length(rows) == 1 + @test rows[1].application_name == :leaf_energy + @test rows[1].applies_to === applies_to(spec) + @test rows[1].value_inputs == value_inputs(spec) + @test rows[1].model_calls == model_calls(spec) + @test rows[1].environment === environment_config(spec) + + leaf_assim = ModelSpec(ToyAssimModel()) |> + AppliesTo(Many(scale=:Leaf)) |> + Inputs(:soil_water_content => One(scale=:Soil, var=:soil_water_content)) + @test length(PlantSimEngine.get_mapped_variables(leaf_assim)) == 1 + + mapping = Dict( + :Leaf => (leaf_assim,), + :Soil => (ToySoilWaterModel(),), + ) + resolved = resolved_model_specs(mapping) + binding = input_bindings(resolved[:Leaf][:carbon_assimilation]).soil_water_content + @test binding.scale == :Soil + @test binding.var == :soil_water_content + @test binding.process == :soil_water + + rich_selector_spec = ModelSpec(ToyAssimModel()) |> + Inputs(:soil_water_content => One(kind=:soil, scale=:Soil, var=:soil_water_content)) + @test isempty(PlantSimEngine.get_mapped_variables(rich_selector_spec)) + @test value_inputs(rich_selector_spec).soil_water_content.criteria.kind == :soil + + default_input_spec = ModelSpec(SceneObjectDefaultInputConsumerModel()) + @test value_inputs(default_input_spec).leaf_carbon isa Many + @test value_inputs(default_input_spec).leaf_carbon.criteria.within isa Self + @test !haskey(dep(default_input_spec), :leaf_carbon) + @test length(PlantSimEngine.get_mapped_variables(default_input_spec)) == 1 + + override_input_spec = ModelSpec(SceneObjectDefaultInputConsumerModel()) |> + Inputs(:leaf_carbon => Many(scale=:Leaf, var=:carbon_override)) + @test value_inputs(override_input_spec).leaf_carbon.criteria.var == :carbon_override + mapped = only(PlantSimEngine.get_mapped_variables(override_input_spec)) + @test first(mapped) == :leaf_carbon + @test last(mapped) == [:Leaf => :carbon_override] + + default_call_spec = ModelSpec(SceneObjectDefaultCallConsumerModel()) + @test model_calls(default_call_spec).stomata isa One + @test model_calls(default_call_spec).stomata.criteria.scale == :Leaf + @test model_calls(default_call_spec).stomata.criteria.process == :stomatal_conductance + default_call_dep = dep(default_call_spec).stomata + @test default_call_dep isa HardDomains + @test default_call_dep.scale == :Leaf + @test default_call_dep.process == :stomatal_conductance + + override_call_spec = ModelSpec(SceneObjectDefaultCallConsumerModel()) |> + Calls(:stomata => One(scale=:Internode, process=:water_status)) + @test model_calls(override_call_spec).stomata.criteria.scale == :Internode + @test model_calls(override_call_spec).stomata.criteria.process == :water_status + override_call_dep = dep(override_call_spec).stomata + @test override_call_dep isa HardDomains + @test override_call_dep.scale == :Internode + @test override_call_dep.process == :water_status + + compiled_specs = ( + ModelSpec(SceneObjectStomataModel(); name=:stomata) |> + AppliesTo(Many(scale=:Leaf)), + ModelSpec(SceneObjectLeafEnergyModel(); name=:leaf_energy) |> + AppliesTo(Many(scale=:Leaf)) |> + Inputs(:leaf_areas => Many(scale=:Leaf, within=SelfPlant(), var=:leaf_area, policy=Integrate(), window=Day(1))), + ) + compiled = compile_scene(selector_scene, compiled_specs) + application_rows = explain_scene_applications(compiled) + @test length(application_rows) == 2 + @test only(row for row in application_rows if row.application_id == :stomata).target_ids == + [:leaf_1, :leaf_2, :leaf_3] + @test only(row for row in application_rows if row.application_id == :leaf_energy).target_ids == + [:leaf_1, :leaf_2, :leaf_3] + + binding_rows = explain_bindings(compiled) + @test length(binding_rows) == 3 + leaf_2_binding = only(row for row in binding_rows if row.consumer_id == :leaf_2) + @test leaf_2_binding.application_id == :leaf_energy + @test leaf_2_binding.input == :leaf_areas + @test leaf_2_binding.source_ids == [:leaf_1, :leaf_2] + @test leaf_2_binding.source_var == :leaf_area + @test leaf_2_binding.carrier_hint == :temporal_stream + + call_rows = explain_calls(compiled) + @test length(call_rows) == 3 + leaf_2_call = only(row for row in call_rows if row.consumer_id == :leaf_2) + @test leaf_2_call.application_id == :leaf_energy + @test leaf_2_call.call == :stomata + @test leaf_2_call.callee_object_ids == [:leaf_2] + @test leaf_2_call.callee_application_ids == [:stomata] + @test leaf_2_call.process == :scene_object_stomata + + ambiguous_call_specs = ( + ModelSpec(SceneObjectStomataModel(); name=:sunlit_stomata) |> + AppliesTo(Many(scale=:Leaf)), + ModelSpec(SceneObjectStomataModel(); name=:shaded_stomata) |> + AppliesTo(Many(scale=:Leaf)), + ModelSpec(SceneObjectLeafEnergyModel(); name=:leaf_energy) |> + AppliesTo(Many(scale=:Leaf)), + ) + @test_throws ErrorException compile_scene(selector_scene, ambiguous_call_specs) + + disambiguated_call_specs = ( + ModelSpec(SceneObjectStomataModel(); name=:sunlit_stomata) |> + AppliesTo(Many(scale=:Leaf)), + ModelSpec(SceneObjectStomataModel(); name=:shaded_stomata) |> + AppliesTo(Many(scale=:Leaf)) |> + Updates(:gs; after=:sunlit_stomata), + ModelSpec(SceneObjectLeafEnergyModel(); name=:leaf_energy) |> + AppliesTo(Many(scale=:Leaf)) |> + Calls(:stomata => One(process=:scene_object_stomata, application=:sunlit_stomata)), + ) + disambiguated = compile_scene(selector_scene, disambiguated_call_specs) + disambiguated_call = only(row for row in explain_calls(disambiguated) if row.consumer_id == :leaf_2) + @test disambiguated_call.callee_application_ids == [:sunlit_stomata] + @test disambiguated_call.application == :sunlit_stomata + + inferred_input_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0, observed_signal=0.0)), + ) + inferred_input_specs = ( + ModelSpec(SceneObjectSignalSourceModel(); name=:signal_source) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_consumer) |> + AppliesTo(One(scale=:Leaf)), + ) + inferred_compiled = compile_scene(inferred_input_scene, inferred_input_specs) + inferred_binding = only(explain_bindings(inferred_compiled)) + @test inferred_binding.application_id == :signal_consumer + @test inferred_binding.input == :signal + @test inferred_binding.origin == :inferred_same_object + @test inferred_binding.source_ids == [:leaf_1] + @test inferred_binding.has_reference_carrier + inferred_input_scene_with_apps = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0, observed_signal=0.0)); + applications=inferred_input_specs, + ) + run!(inferred_input_scene_with_apps) + @test only(scene_objects(inferred_input_scene_with_apps; scale=:Leaf)).status.observed_signal == 1.0 + + carrier_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:plant_1; scale=:Plant, kind=:plant, species=:oil_palm, parent=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1, status=Status(leaf_area=1.0, leaf_token=SceneObjectTaggedValue(1))), + Object(:leaf_2; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1, status=Status(leaf_area=2.0, leaf_token=SceneObjectTaggedValue(2))), + Object(:soil; scale=:Soil, kind=:soil, parent=:scene, status=Status(soil_water_content=0.31)), + ) + carrier_specs = ( + ModelSpec(SceneObjectCarrierConsumerModel(); name=:carrier_consumer) |> + AppliesTo(Many(scale=:Leaf)) |> + Inputs( + :leaf_areas => Many(scale=:Leaf, within=SelfPlant(), var=:leaf_area), + :leaf_tokens => Many(scale=:Leaf, within=SelfPlant(), var=:leaf_token), + ), + ModelSpec(ToyAssimModel(); name=:assim) |> + AppliesTo(Many(scale=:Leaf)) |> + Inputs(:soil_water_content => One(scale=:Soil, var=:soil_water_content)), + ) + carrier_compiled = compile_scene(carrier_scene, carrier_specs) + carrier_rows = explain_bindings(carrier_compiled) + leaf_area_binding = only( + binding for binding in carrier_compiled.input_bindings + if binding.application_id == :carrier_consumer && binding.consumer_id == ObjectId(:leaf_1) && binding.input == :leaf_areas + ) + @test has_reference_carrier(leaf_area_binding) + @test input_carrier(leaf_area_binding) isa PlantSimEngine.RefVector + @test input_value(leaf_area_binding)[1] == 1.0 + input_value(leaf_area_binding)[1] = 4.0 + leaf_1_object = only(object for object in scene_objects(carrier_scene; scale=:Leaf) if object.id == ObjectId(:leaf_1)) + @test leaf_1_object.status.leaf_area == 4.0 + @test only(row for row in carrier_rows if row.application_id == :carrier_consumer && row.consumer_id == :leaf_1 && row.input == :leaf_areas).has_reference_carrier + + token_binding = only( + binding for binding in carrier_compiled.input_bindings + if binding.application_id == :carrier_consumer && binding.consumer_id == ObjectId(:leaf_1) && binding.input == :leaf_tokens + ) + @test input_value(token_binding)[2] == SceneObjectTaggedValue(2) + input_value(token_binding)[2] = SceneObjectTaggedValue(20) + leaf_2_object = only(object for object in scene_objects(carrier_scene; scale=:Leaf) if object.id == ObjectId(:leaf_2)) + @test leaf_2_object.status.leaf_token == SceneObjectTaggedValue(20) + + scalar_binding = only( + binding for binding in carrier_compiled.input_bindings + if binding.application_id == :assim && binding.consumer_id == ObjectId(:leaf_1) + ) + @test has_reference_carrier(scalar_binding) + @test input_carrier(scalar_binding) isa Base.RefValue + @test input_value(scalar_binding) == 0.31 + input_carrier(scalar_binding)[] = 0.42 + @test only(scene_objects(carrier_scene; scale=:Soil)).status.soil_water_content == 0.42 + + cache_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:plant_1; scale=:Plant, kind=:plant, species=:oil_palm, name=:palm_1, parent=:scene), + Object(:axis_1; scale=:Axis, kind=:plant, species=:oil_palm, parent=:plant_1), + Object(:leaf_1; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1), + Object(:leaf_2; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:axis_1), + Object(:plant_2; scale=:Plant, kind=:plant, species=:oil_palm, name=:palm_2, parent=:scene), + Object(:leaf_3; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_2), + Object(:soil; scale=:Soil, kind=:soil, parent=:scene); + applications=compiled_specs, + ) + @test bindings_dirty(cache_scene) + cached_a = refresh_bindings!(cache_scene) + @test cached_a isa CompiledScene + @test !bindings_dirty(cache_scene) + @test compiled_bindings(cache_scene) === cached_a + @test cached_a.revision == scene_revision(cache_scene) + @test refresh_bindings!(cache_scene) === cached_a + + register_object!(cache_scene, Object(:leaf_4; scale=:Leaf, kind=:plant, species=:oil_palm); parent=:plant_2) + @test bindings_dirty(cache_scene) + @test isnothing(compiled_bindings(cache_scene)) + cached_b = refresh_bindings!(cache_scene) + @test cached_b !== cached_a + @test cached_b.revision == scene_revision(cache_scene) + @test only(row for row in explain_scene_applications(cached_b) if row.application_id == :leaf_energy).target_ids == + [:leaf_1, :leaf_2, :leaf_3, :leaf_4] + @test only(row for row in explain_bindings(cached_b) if row.consumer_id == :leaf_3).source_ids == + [:leaf_3, :leaf_4] + + move_object!(cache_scene, :leaf_4, (x=3.0, y=0.0)) + @test !bindings_dirty(cache_scene) + @test environment_bindings_dirty(cache_scene) + @test refresh_bindings!(cache_scene) === cached_b + mark_environment_binding_dirty!(cache_scene) + @test !bindings_dirty(cache_scene) + + reparent_object!(cache_scene, :leaf_4, :plant_1) + cached_c = refresh_bindings!(cache_scene) + @test only(row for row in explain_bindings(cached_c) if row.consumer_id == :leaf_4).source_ids == + [:leaf_1, :leaf_2, :leaf_4] + + remove_object!(cache_scene, :leaf_4) + cached_d = refresh_bindings!(cache_scene) + @test only(row for row in explain_scene_applications(cached_d) if row.application_id == :leaf_energy).target_ids == + [:leaf_1, :leaf_2, :leaf_3] + + grid_backend = SceneObjectGridBackend(Any[]) + environment_specs = ( + ModelSpec(SceneObjectEnvironmentProbeModel(); name=:probe) |> + AppliesTo(Many(scale=:Leaf)) |> + Environment(provider=:grid), + ModelSpec(SceneObjectEnvironmentUpdateModel(); name=:temperature_update) |> + AppliesTo(Many(scale=:Leaf)) |> + Environment(provider=:grid), + ) + environment_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:plant_1; scale=:Plant, kind=:plant, species=:oil_palm, name=:palm_1, parent=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1, geometry=(cell=:cell_a,)), + Object(:leaf_2; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1, geometry=(cell=:cell_b,)); + applications=environment_specs, + environment=grid_backend, + ) + compiled_environment = refresh_environment_bindings!(environment_scene) + @test compiled_environment isa CompiledEnvironmentBindings + @test !environment_bindings_dirty(environment_scene) + @test compiled_environment_bindings(environment_scene) === compiled_environment + @test length(grid_backend.index_updates) == 1 + @test any(entity -> entity.id == :leaf_1 && entity.geometry == (cell=:cell_a,), grid_backend.index_updates[1]) + @test any(entity -> entity.id == :plant_1 && entity.scale == :Plant, grid_backend.index_updates[1]) + environment_rows = explain_environment_bindings(compiled_environment) + @test length(environment_rows) == 4 + leaf_1_probe = only(row for row in environment_rows if row.application_id == :probe && row.object_id == :leaf_1) + @test leaf_1_probe.provider == :grid + @test leaf_1_probe.cell == :cell_a + @test leaf_1_probe.required_inputs == [:T, :CO2] + @test leaf_1_probe.produced_outputs == Symbol[] + leaf_2_update = only(row for row in environment_rows if row.application_id == :temperature_update && row.object_id == :leaf_2) + @test leaf_2_update.cell == :cell_b + @test leaf_2_update.required_inputs == [:T] + @test leaf_2_update.produced_outputs == [:T] + + structural_environment_cache = refresh_bindings!(environment_scene) + move_object!(environment_scene, :leaf_2, (cell=:cell_c,)) + @test !bindings_dirty(environment_scene) + @test environment_bindings_dirty(environment_scene) + @test refresh_bindings!(environment_scene) === structural_environment_cache + refreshed_environment = refresh_environment_bindings!(environment_scene) + @test !environment_bindings_dirty(environment_scene) + @test length(grid_backend.index_updates) == 2 + @test any(entity -> entity.id == :leaf_2 && entity.geometry == (cell=:cell_c,), grid_backend.index_updates[2]) + @test only(row for row in explain_environment_bindings(refreshed_environment) if row.application_id == :probe && row.object_id == :leaf_2).cell == :cell_c + + register_object!(environment_scene, Object(:leaf_3; scale=:Leaf, kind=:plant, species=:oil_palm, geometry=(cell=:cell_d,)); parent=:plant_1) + @test bindings_dirty(environment_scene) + @test environment_bindings_dirty(environment_scene) + refreshed_with_new_leaf = refresh_environment_bindings!(environment_scene) + @test length(grid_backend.index_updates) == 3 + @test any(entity -> entity.id == :leaf_3 && entity.geometry == (cell=:cell_d,), grid_backend.index_updates[3]) + @test only(row for row in explain_scene_applications(refresh_bindings!(environment_scene)) if row.application_id == :probe).target_ids == + [:leaf_1, :leaf_2, :leaf_3] + @test only(row for row in explain_environment_bindings(refreshed_with_new_leaf) if row.application_id == :probe && row.object_id == :leaf_3).cell == :cell_d + + mutable_environment_backend = SceneObjectMutableEnvironmentBackend(:cell_a => 20.0, :cell_b => 30.0) + mutable_environment_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, geometry=(cell=:cell_a,), status=Status(T=0.0, temperature_update=0.0, temperature_seen=0.0)), + Object(:leaf_2; scale=:Leaf, kind=:plant, parent=:scene, geometry=(cell=:cell_b,), status=Status(T=0.0, temperature_update=0.0, temperature_seen=0.0)); + applications=( + ModelSpec(SceneObjectEnvironmentUpdateModel(); name=:temperature_update_runtime) |> + AppliesTo(Many(scale=:Leaf)) |> + Environment(provider=:grid), + ModelSpec(SceneObjectEnvironmentProbeModel(); name=:probe_after_update) |> + AppliesTo(Many(scale=:Leaf)) |> + Environment(provider=:grid), + ), + environment=mutable_environment_backend, + ) + run!(mutable_environment_scene) + @test mutable_environment_backend.values == Dict(:cell_a => 21.0, :cell_b => 31.0) + @test mutable_environment_backend.writes == [ + (application=:temperature_update_runtime, process=:scene_object_environment_update, cell=:cell_a, variable=:T, value=21.0, time=1), + (application=:temperature_update_runtime, process=:scene_object_environment_update, cell=:cell_b, variable=:T, value=31.0, time=1), + ] + mutable_environment_statuses = Dict(object.id.value => object.status for object in scene_objects(mutable_environment_scene; scale=:Leaf)) + @test mutable_environment_statuses[:leaf_1].temperature_seen == 21.0 + @test mutable_environment_statuses[:leaf_2].temperature_seen == 31.0 + + runtime_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:plant_1, status=Status(leaf_area=1.5, leaf_areas=[0.0], carrier_total=0.0, temperature_seen=0.0)), + Object(:leaf_2; scale=:Leaf, kind=:plant, parent=:plant_1, status=Status(leaf_area=2.5, leaf_areas=[0.0], carrier_total=0.0, temperature_seen=0.0)); + applications=( + ModelSpec(SceneObjectCarrierConsumerModel(); name=:carrier_runtime) |> + AppliesTo(Many(scale=:Leaf)) |> + Inputs(:leaf_areas => Many(scale=:Leaf, within=SelfPlant(), var=:leaf_area)), + ModelSpec(SceneObjectEnvironmentProbeModel(); name=:probe_runtime) |> + AppliesTo(Many(scale=:Leaf)) |> + Environment(provider=:global), + ), + environment=(T=27.5, CO2=410.0), + ) + run!(runtime_scene) + @test all(object.status.carrier_total == 4.0 for object in scene_objects(runtime_scene; scale=:Leaf)) + @test all(object.status.temperature_seen == 27.5 for object in scene_objects(runtime_scene; scale=:Leaf)) + + call_runtime_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0, called_signal=0.0)); + applications=( + ModelSpec(SceneObjectSignalSourceModel(); name=:signal_source) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(SceneObjectSignalCallerModel(); name=:signal_caller) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + run!(call_runtime_scene) + call_status = only(scene_objects(call_runtime_scene; scale=:Leaf)).status + @test call_status.signal == 1.0 + @test call_status.called_signal == 1.0 + call_schedule = explain_schedule(refresh_bindings!(call_runtime_scene)) + @test only(row for row in call_schedule if row.application_id == :signal_source).manual_call_only + @test !only(row for row in call_schedule if row.application_id == :signal_source).root_scheduled + @test only(row for row in call_schedule if row.application_id == :signal_caller).root_scheduled + + temporal_input_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene, status=Status(signal_sum=0.0, temporal_total=0.0)), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0)); + applications=( + ModelSpec(SceneObjectSignalSourceModel(); name=:hourly_signal) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(1)), + ModelSpec(SceneObjectTemporalSumModel(); name=:scene_temporal_sum) |> + AppliesTo(One(scale=:Scene)) |> + Inputs(:signal_sum => One(scale=:Leaf, var=:signal, policy=Integrate(), window=Hour(2))) |> + TimeStep(Hour(2)), + ), + environment=(duration=Hour(1),), + ) + temporal_binding = only( + row for row in explain_bindings(refresh_bindings!(temporal_input_scene)) + if row.application_id == :scene_temporal_sum && row.input == :signal_sum + ) + @test temporal_binding.carrier_hint == :temporal_stream + run!(temporal_input_scene; steps=3) + @test only(scene_objects(temporal_input_scene; scale=:Leaf)).status.signal == 3.0 + @test only(scene_objects(temporal_input_scene; scale=:Scene)).status.temporal_total == 5.0 + + temporal_holdlast_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene, status=Status(signal_sum=0.0, temporal_total=0.0)), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0)); + applications=( + ModelSpec(SceneObjectSignalSourceModel(); name=:hourly_signal) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(1)), + ModelSpec(SceneObjectTemporalSumModel(); name=:scene_temporal_latest) |> + AppliesTo(One(scale=:Scene)) |> + Inputs(:signal_sum => One(scale=:Leaf, var=:signal, policy=HoldLast(), window=Hour(2))) |> + TimeStep(Hour(2)), + ), + environment=(duration=Hour(1),), + ) + run!(temporal_holdlast_scene; steps=3) + @test only(scene_objects(temporal_holdlast_scene; scale=:Scene)).status.temporal_total == 3.0 + + writer_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(biomass=-1.0)), + ) + biomass_source = + ModelSpec(SceneObjectBiomassSourceModel(); name=:carbon_allocation) |> + AppliesTo(One(scale=:Leaf)) + biomass_pruner = + ModelSpec(SceneObjectBiomassPrunerModel(); name=:leaf_pruning) |> + AppliesTo(One(scale=:Leaf)) + + @test_throws ErrorException compile_scene(writer_scene, (biomass_source, biomass_pruner)) + @test_throws ErrorException compile_scene( + writer_scene, + (biomass_source, biomass_pruner |> Updates(:biomass; after=:water_status)), + ) + @test_throws ErrorException compile_scene( + writer_scene, + (biomass_pruner |> Updates(:biomass; after=:carbon_allocation), biomass_source), + ) + + ordered_pruner = biomass_pruner |> Updates(:biomass; after=:carbon_allocation) + writer_compiled = compile_scene(writer_scene, (biomass_source, ordered_pruner)) + writer_row = only(row for row in explain_writers(writer_compiled) if row.variable == :biomass) + @test writer_row.object_id == :leaf_1 + @test writer_row.duplicate + @test writer_row.application_ids == [:carbon_allocation, :leaf_pruning] + @test writer_row.update_application_ids == [:leaf_pruning] + @test writer_row.update_after == [:leaf_pruning => [:carbon_allocation]] + + writer_runtime_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(biomass=-1.0)); + applications=(biomass_source, ordered_pruner), + ) + run!(writer_runtime_scene) + @test only(scene_objects(writer_runtime_scene; scale=:Leaf)).status.biomass == 0.0 + + multirate_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0)); + applications=( + ModelSpec(SceneObjectSignalSourceModel(); name=:hourly_signal) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(2)), + ), + environment=(duration=Hour(1),), + ) + multirate_compiled = refresh_bindings!(multirate_scene) + schedule_rows = explain_schedule(multirate_compiled) + @test only(schedule_rows).application_id == :hourly_signal + @test only(schedule_rows).dt_steps == 2.0 + @test only(schedule_rows).dt_seconds == 7200.0 + run!(multirate_scene; steps=5) + @test only(scene_objects(multirate_scene; scale=:Leaf)).status.signal == 3.0 +end From c4609287a3ae58b9b4694f944e789fdad58a218c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Sat, 6 Jun 2026 10:59:43 +0200 Subject: [PATCH 15/33] Add producer application metadata and selector validation in Inputs. --- docs/src/dev/release_notes_handoff.md | 3 ++ ...nified_scene_object_implementation_plan.md | 5 +++ src/scene_object_api.jl | 43 +++++++++++++++++++ test/test-unified-scene-object-api.jl | 25 +++++++++++ 4 files changed, 76 insertions(+) diff --git a/docs/src/dev/release_notes_handoff.md b/docs/src/dev/release_notes_handoff.md index f4fda8173..cb03bc817 100644 --- a/docs/src/dev/release_notes_handoff.md +++ b/docs/src/dev/release_notes_handoff.md @@ -115,6 +115,9 @@ for multi-plant scene coupling. `inputs_`/`outputs_` when one producer is unambiguous. `explain_bindings` reports each binding origin, including `:declared` and `:inferred_same_object`. +- Compiled input bindings now validate `Inputs(...)` `process=`/`application=` + filters when they are provided, and `explain_bindings` reports + `source_application_ids`, `process`, and `application`. - Scene/object runtime now publishes model outputs to scene-local temporal streams and resolves temporal `Inputs(...)` with `HoldLast`, `Integrate`, and `Aggregate` policies before consumer execution. diff --git a/docs/src/dev/unified_scene_object_implementation_plan.md b/docs/src/dev/unified_scene_object_implementation_plan.md index 8dc12a6e4..9e751e946 100644 --- a/docs/src/dev/unified_scene_object_implementation_plan.md +++ b/docs/src/dev/unified_scene_object_implementation_plan.md @@ -85,6 +85,11 @@ should reproduce the same capabilities through unified object selections. the same variable, `compile_scene` creates an inferred reference binding. `explain_bindings` now reports binding `origin` values such as `:declared` and `:inferred_same_object`. +- Compiled input bindings now carry producer metadata. When an `Inputs(...)` + selector uses `process=` or `application=`, `compile_scene` validates that a + matching source application exists for the selected source objects. + `explain_bindings` reports `source_application_ids`, `process`, and + `application` for agent-readable dependency diagnostics. - Carrier compilation preserves source `Status` references and arbitrary value types; tests cover both scalar refs and many-object vectors with a custom non-`Float64` value type. diff --git a/src/scene_object_api.jl b/src/scene_object_api.jl index a71c2dbd8..0c10ee676 100644 --- a/src/scene_object_api.jl +++ b/src/scene_object_api.jl @@ -555,7 +555,10 @@ struct CompiledSceneInputBinding{SEL,P,W,C} selector::SEL origin::Symbol source_ids::Vector{ObjectId} + source_application_ids::Vector{Symbol} source_var::Symbol + process::Union{Nothing,Symbol} + application::Union{Nothing,Symbol} multiplicity::Symbol policy::P window::W @@ -844,6 +847,28 @@ _input_value(carrier::Base.RefValue) = carrier[] _input_value(carrier::RefVector) = carrier _input_value(carrier::ObjectRefVector) = carrier +function _matching_input_source_applications(applications_by_object, source_ids, source_var::Symbol, process_filter, application_filter) + matches = Symbol[] + for source_id in source_ids + for application in get(applications_by_object, source_id, Any[]) + source_var in _scene_output_names(application) || continue + isnothing(process_filter) || application.process == process_filter || continue + isnothing(application_filter) || application.id == application_filter || continue + push!(matches, application.id) + end + end + unique!(matches) + if (!isnothing(process_filter) || !isnothing(application_filter)) && isempty(matches) + error( + "Input selector for source variable `$(source_var)` requested", + isnothing(process_filter) ? "" : " process `$(process_filter)`", + isnothing(application_filter) ? "" : " application `$(application_filter)`", + ", but no matching source application was found." + ) + end + return matches +end + function _compile_scene_input_bindings(scene::Scene, applications) bindings = CompiledSceneInputBinding[] by_object = _applications_by_object(applications) @@ -864,6 +889,7 @@ function _compile_scene_input_bindings(scene::Scene, applications) input_sym, selector, :declared, + by_object, ) end _append_inferred_scene_input_bindings!(bindings, scene, application, consumer_id, declared_inputs, by_object) @@ -880,12 +906,22 @@ function _push_scene_input_binding!( input_sym::Symbol, selector::AbstractObjectMultiplicity, origin::Symbol, + applications_by_object, source_ids_override=nothing, ) source_ids = isnothing(source_ids_override) ? _dependency_object_ids(scene, selector, consumer_id) : source_ids_override policy = _selector_policy(selector) window = _selector_window(selector) source_var = _selector_var(selector, input_sym) + process_filter = _criteria_get(criteria(selector), :process, nothing) + application_filter = _selector_application(selector) + source_application_ids = _matching_input_source_applications( + applications_by_object, + source_ids, + source_var, + process_filter, + application_filter, + ) carrier = _input_carrier(scene, selector, source_ids, source_var) push!( bindings, @@ -896,7 +932,10 @@ function _push_scene_input_binding!( selector, origin, source_ids, + source_application_ids, source_var, + process_filter, + application_filter, multiplicity(selector), policy, window, @@ -951,6 +990,7 @@ function _append_inferred_scene_input_bindings!( input_sym, selector, :inferred_same_object, + applications_by_object, ObjectId[consumer_id], ) end @@ -1082,7 +1122,10 @@ function explain_bindings(compiled::CompiledScene) input=binding.input, origin=binding.origin, source_ids=[id.value for id in binding.source_ids], + source_application_ids=binding.source_application_ids, source_var=binding.source_var, + process=binding.process, + application=binding.application, multiplicity=binding.multiplicity, policy=binding.policy, window=binding.window, diff --git a/test/test-unified-scene-object-api.jl b/test/test-unified-scene-object-api.jl index c4369790a..e1d15cfc1 100644 --- a/test/test-unified-scene-object-api.jl +++ b/test/test-unified-scene-object-api.jl @@ -543,6 +543,9 @@ end @test inferred_binding.input == :signal @test inferred_binding.origin == :inferred_same_object @test inferred_binding.source_ids == [:leaf_1] + @test inferred_binding.source_application_ids == [:signal_source] + @test inferred_binding.process == :scene_object_signal_source + @test inferred_binding.application == :signal_source @test inferred_binding.has_reference_carrier inferred_input_scene_with_apps = Scene( Object(:scene; scale=:Scene, kind=:scene), @@ -552,6 +555,28 @@ end run!(inferred_input_scene_with_apps) @test only(scene_objects(inferred_input_scene_with_apps; scale=:Leaf)).status.observed_signal == 1.0 + filtered_input_specs = ( + ModelSpec(SceneObjectSignalSourceModel(); name=:signal_source) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_consumer) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs(:signal => One(scale=:Leaf, var=:signal, process=:scene_object_signal_source, application=:signal_source)), + ) + filtered_binding = only(explain_bindings(compile_scene(inferred_input_scene, filtered_input_specs))) + @test filtered_binding.origin == :declared + @test filtered_binding.source_application_ids == [:signal_source] + @test filtered_binding.process == :scene_object_signal_source + @test filtered_binding.application == :signal_source + @test_throws ErrorException compile_scene( + inferred_input_scene, + ( + filtered_input_specs[1], + ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_consumer) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs(:signal => One(scale=:Leaf, var=:signal, application=:missing_source)), + ), + ) + carrier_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:plant_1; scale=:Plant, kind=:plant, species=:oil_palm, parent=:scene), From 0b4e5f4d071f2c4ccefda31ae6fbfc38cfb0ee79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Sat, 6 Jun 2026 11:08:07 +0200 Subject: [PATCH 16/33] Better error catching for Inputs compile_scene (line 638) now validates required inputs_(model) variables. A required input is valid if it has a compiled Inputs(...)/inferred binding or already exists on the target object Status. Missing inputs now error with application id, object id, and input name. Added tests for missing inputs, ambiguous same-object producers, and status-provided inputs. --- docs/src/dev/release_notes_handoff.md | 3 ++ ...nified_scene_object_implementation_plan.md | 4 ++ src/scene_object_api.jl | 51 +++++++++++++++++++ test/test-unified-scene-object-api.jl | 32 ++++++++++-- 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/docs/src/dev/release_notes_handoff.md b/docs/src/dev/release_notes_handoff.md index cb03bc817..4d0220ee0 100644 --- a/docs/src/dev/release_notes_handoff.md +++ b/docs/src/dev/release_notes_handoff.md @@ -118,6 +118,9 @@ for multi-plant scene coupling. - Compiled input bindings now validate `Inputs(...)` `process=`/`application=` filters when they are provided, and `explain_bindings` reports `source_application_ids`, `process`, and `application`. +- `compile_scene` now errors for required `inputs_(model)` variables that are + neither bound through `Inputs(...)`/inference nor present on the target object + `Status`. - Scene/object runtime now publishes model outputs to scene-local temporal streams and resolves temporal `Inputs(...)` with `HoldLast`, `Integrate`, and `Aggregate` policies before consumer execution. diff --git a/docs/src/dev/unified_scene_object_implementation_plan.md b/docs/src/dev/unified_scene_object_implementation_plan.md index 9e751e946..0b99c87cf 100644 --- a/docs/src/dev/unified_scene_object_implementation_plan.md +++ b/docs/src/dev/unified_scene_object_implementation_plan.md @@ -90,6 +90,10 @@ should reproduce the same capabilities through unified object selections. matching source application exists for the selected source objects. `explain_bindings` reports `source_application_ids`, `process`, and `application` for agent-readable dependency diagnostics. +- `compile_scene` now validates required status inputs from `inputs_(model)`. + Each required input must either have a compiled binding or already exist on + the target object `Status`; otherwise compilation errors with the concrete + application id, object id, and input variable. - Carrier compilation preserves source `Status` references and arbitrary value types; tests cover both scalar refs and many-object vectors with a custom non-`Float64` value type. diff --git a/src/scene_object_api.jl b/src/scene_object_api.jl index 0c10ee676..c24d7fbb2 100644 --- a/src/scene_object_api.jl +++ b/src/scene_object_api.jl @@ -635,6 +635,7 @@ function _compile_scene(scene::Scene, raw_specs) applications = _compile_scene_applications(scene, raw_specs, timeline) _validate_scene_writers!(applications) input_bindings = _compile_scene_input_bindings(scene, applications) + _validate_scene_required_inputs!(scene, applications, input_bindings) call_bindings = _compile_scene_call_bindings(scene, applications) return CompiledScene(scene, applications, input_bindings, call_bindings, scene.revision) end @@ -997,6 +998,56 @@ function _append_inferred_scene_input_bindings!( return bindings end +function _bound_scene_inputs(input_bindings) + bound = Set{Tuple{Symbol,ObjectId,Symbol}}() + for binding in input_bindings + push!(bound, (binding.application_id, binding.consumer_id, binding.input)) + end + return bound +end + +function _status_has_variable(scene::Scene, object_id::ObjectId, variable::Symbol) + object = _scene_object(scene, object_id) + object.status isa Status || return false + return variable in propertynames(object.status) +end + +function _validate_scene_required_inputs!(scene::Scene, applications, input_bindings) + bound = _bound_scene_inputs(input_bindings) + missing = NamedTuple[] + for application in applications + for object_id in application.target_ids + for input in _scene_input_names(application) + (application.id, object_id, input) in bound && continue + _status_has_variable(scene, object_id, input) && continue + push!( + missing, + ( + application_id=application.id, + object_id=object_id.value, + input=input, + process=application.process, + ), + ) + end + end + end + isempty(missing) && return nothing + details = join( + [ + "`$(row.application_id)` on object `$(row.object_id)` requires `$(row.input)`" + for row in missing + ], + "; ", + ) + error( + "Missing required scene/object input(s): ", + details, + ". Provide the variable on object `Status`, add an `Inputs(...)` binding, ", + "or add an unambiguous same-object producer." + ) +end + function _applications_by_object(applications) by_object = Dict{ObjectId,Vector{Any}}() for application in applications diff --git a/test/test-unified-scene-object-api.jl b/test/test-unified-scene-object-api.jl index e1d15cfc1..1326414dc 100644 --- a/test/test-unified-scene-object-api.jl +++ b/test/test-unified-scene-object-api.jl @@ -520,6 +520,7 @@ end Updates(:gs; after=:sunlit_stomata), ModelSpec(SceneObjectLeafEnergyModel(); name=:leaf_energy) |> AppliesTo(Many(scale=:Leaf)) |> + Inputs(:leaf_areas => Many(scale=:Leaf, within=SelfPlant(), var=:leaf_area)) |> Calls(:stomata => One(process=:scene_object_stomata, application=:sunlit_stomata)), ) disambiguated = compile_scene(selector_scene, disambiguated_call_specs) @@ -555,6 +556,29 @@ end run!(inferred_input_scene_with_apps) @test only(scene_objects(inferred_input_scene_with_apps; scale=:Leaf)).status.observed_signal == 1.0 + @test_throws ErrorException compile_scene( + Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(observed_signal=0.0)), + ), + ( + ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_consumer) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + @test_throws ErrorException compile_scene( + inferred_input_scene, + ( + ModelSpec(SceneObjectSignalSourceModel(); name=:sunlit_signal) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(SceneObjectSignalSourceModel(); name=:shaded_signal) |> + AppliesTo(One(scale=:Leaf)) |> + Updates(:signal; after=:sunlit_signal), + ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_consumer) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + filtered_input_specs = ( ModelSpec(SceneObjectSignalSourceModel(); name=:signal_source) |> AppliesTo(One(scale=:Leaf)), @@ -580,8 +604,8 @@ end carrier_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:plant_1; scale=:Plant, kind=:plant, species=:oil_palm, parent=:scene), - Object(:leaf_1; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1, status=Status(leaf_area=1.0, leaf_token=SceneObjectTaggedValue(1))), - Object(:leaf_2; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1, status=Status(leaf_area=2.0, leaf_token=SceneObjectTaggedValue(2))), + Object(:leaf_1; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1, status=Status(leaf_area=1.0, leaf_token=SceneObjectTaggedValue(1), aPPFD=100.0)), + Object(:leaf_2; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1, status=Status(leaf_area=2.0, leaf_token=SceneObjectTaggedValue(2), aPPFD=100.0)), Object(:soil; scale=:Soil, kind=:soil, parent=:scene, status=Status(soil_water_content=0.31)), ) carrier_specs = ( @@ -760,8 +784,8 @@ end runtime_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene), - Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:plant_1, status=Status(leaf_area=1.5, leaf_areas=[0.0], carrier_total=0.0, temperature_seen=0.0)), - Object(:leaf_2; scale=:Leaf, kind=:plant, parent=:plant_1, status=Status(leaf_area=2.5, leaf_areas=[0.0], carrier_total=0.0, temperature_seen=0.0)); + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:plant_1, status=Status(leaf_area=1.5, leaf_areas=[0.0], leaf_tokens=Any[], carrier_total=0.0, temperature_seen=0.0)), + Object(:leaf_2; scale=:Leaf, kind=:plant, parent=:plant_1, status=Status(leaf_area=2.5, leaf_areas=[0.0], leaf_tokens=Any[], carrier_total=0.0, temperature_seen=0.0)); applications=( ModelSpec(SceneObjectCarrierConsumerModel(); name=:carrier_runtime) |> AppliesTo(Many(scale=:Leaf)) |> From df679dc537878b056aeffcc747350a5883cb60ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Sat, 6 Jun 2026 11:46:57 +0200 Subject: [PATCH 17/33] Implements default scope inference for compiled scene/object dependencies. Unscoped Inputs(...) / Calls(...) dependency selectors now infer scope from the consumer object: scene consumers default to SceneScope() non-scene consumers default to Self() Direct public resolve_object_ids(scene, Many(...)) remains scene-wide unless within=... is explicit. Shared cross-scope dependencies from organs now need explicit within=SceneScope(), as in the updated shared-soil test. Added tests showing plant-level unscoped leaf inputs are plant-local, while scene-level unscoped leaf inputs are scene-wide. --- docs/src/dev/release_notes_handoff.md | 21 ++ ...nified_scene_object_implementation_plan.md | 25 ++- src/PlantSimEngine.jl | 4 +- src/scene_object_api.jl | 199 +++++++++++++++++- test/test-unified-scene-object-api.jl | 107 +++++++++- 5 files changed, 339 insertions(+), 17 deletions(-) diff --git a/docs/src/dev/release_notes_handoff.md b/docs/src/dev/release_notes_handoff.md index 4d0220ee0..9de687831 100644 --- a/docs/src/dev/release_notes_handoff.md +++ b/docs/src/dev/release_notes_handoff.md @@ -81,6 +81,9 @@ for multi-plant scene coupling. - Adds initial registry-backed scene selector resolution with `resolve_object_ids` and `resolve_objects` for global, self-relative, plant-relative, ancestor-relative, and named-scope object selections. +- Adds `explain_scopes(scene)` for structured scope diagnostics. It reports + the scene scope, object subtree scopes, named `Scope(...)` entries, and + scale/kind/species label groups with concrete object ids. - Adds the first compiled scene/object view with `compile_scene`, `CompiledScene`, `CompiledSceneApplication`, `CompiledSceneInputBinding`, `CompiledSceneCallBinding`, `explain_scene_applications`, @@ -89,10 +92,17 @@ for multi-plant scene coupling. `Calls(...)` to object ids ahead of runtime, and reports temporal policy, window, carrier hints, and callee application ids for agent-readable diagnostics. +- Unscoped scene/object dependency selectors now infer scope from the consumer: + scene consumers default to `SceneScope()`, while non-scene consumers default + to `Self()`. Cross-scope shared dependencies, such as leaf models reading + soil state, should use `within=SceneScope()` explicitly. - Adds status-backed compiled input carriers for the scene/object view: scalar shared refs, homogeneous `RefVector`s, and `ObjectRefVector` fallback carriers. `input_carrier`, `input_value`, and `has_reference_carrier` expose them for tests, diagnostics, and future runtime execution. +- `explain_bindings` now reports stable carrier kind and copy/reference + semantics, making reference-wired inputs and materialized temporal values + explicit for users and agents. - Adds scene binding cache helpers: `refresh_bindings!`, `bindings_dirty`, `compiled_bindings`, and `scene_revision`. Object registration, removal, and reparenting invalidate @@ -108,6 +118,11 @@ for multi-plant scene coupling. scene-wide lookup structures. - Object movement now invalidates environment bindings without rebuilding the structural object/model binding cache. +- Adds public geometry lifecycle helpers: + `update_geometry!(scene, object, geometry; invalidate_environment=true)` and + object-scoped `mark_environment_binding_dirty!(scene, object)`. They + currently invalidate the scene environment binding cache and leave room for + finer-grained dirty tracking later. - Adds the first scene/object runtime with `run!(scene; steps=...)`. It materializes compiled `Inputs(...)` carriers, samples bound environment inputs, and executes generic model kernels on object `Status` values. @@ -121,6 +136,12 @@ for multi-plant scene coupling. - `compile_scene` now errors for required `inputs_(model)` variables that are neither bound through `Inputs(...)`/inference nor present on the target object `Status`. +- `compile_scene` now rejects `Inputs(...)` entries whose receiving variable is + not declared by the model's `inputs_`, making binding typos explicit at + compile time. +- `compile_scene` now validates status-backed non-temporal `Inputs(...)` + source availability, so bindings that select existing source objects but no + source `Status` reference fail at compile time instead of becoming no-ops. - Scene/object runtime now publishes model outputs to scene-local temporal streams and resolves temporal `Inputs(...)` with `HoldLast`, `Integrate`, and `Aggregate` policies before consumer execution. diff --git a/docs/src/dev/unified_scene_object_implementation_plan.md b/docs/src/dev/unified_scene_object_implementation_plan.md index 0b99c87cf..7747bd9f7 100644 --- a/docs/src/dev/unified_scene_object_implementation_plan.md +++ b/docs/src/dev/unified_scene_object_implementation_plan.md @@ -64,6 +64,10 @@ should reproduce the same capabilities through unified object selections. `Ancestor(...)`, `Scope(...)`, positional selectors such as `Kind(:plant)`/`Scale(:Leaf)`, and `One`/`OptionalOne`/`Many` cardinality checks. +- Added `explain_scopes(scene)` for agent-readable scope diagnostics. It + reports the global scene scope, each object subtree, each named + `Scope(...)`, and label groups by scale, kind, and species with concrete + resolved object ids. - Started the object-address compiler with `compile_scene(scene, specs)` and compiled scene application/binding carriers. The compiler now resolves `AppliesTo(...)` target object ids, object-relative `Inputs(...)` source @@ -78,7 +82,8 @@ should reproduce the same capabilities through unified object selections. `Ref`, a homogeneous `RefVector`, or an `ObjectRefVector` fallback for heterogeneous reference-preserving vectors. `input_carrier`, `input_value`, and `has_reference_carrier` expose these carriers, and `explain_bindings` - reports carrier type and reference availability. + reports carrier kind, copy/reference semantics, carrier type, and reference + availability. - Added conservative same-object input inference in the scene compiler. When a model declares an `inputs_` variable that is not covered by explicit/default `Inputs(...)`, and exactly one other application on the same object outputs @@ -90,10 +95,23 @@ should reproduce the same capabilities through unified object selections. matching source application exists for the selected source objects. `explain_bindings` reports `source_application_ids`, `process`, and `application` for agent-readable dependency diagnostics. +- Dependency selectors in `Inputs(...)` and `Calls(...)` now infer a default + scope from the consumer object when no explicit `within=...` is provided: + scene objects default to `SceneScope()`, while non-scene objects default to + `Self()`. Shared scene/soil dependencies from organs should therefore use + `within=SceneScope()` explicitly. - `compile_scene` now validates required status inputs from `inputs_(model)`. Each required input must either have a compiled binding or already exist on the target object `Status`; otherwise compilation errors with the concrete application id, object id, and input variable. +- `compile_scene` now rejects `Inputs(...)` declarations whose left-hand + variable is not declared by the target model's `inputs_`. This catches + misspelled or stale scenario bindings before they create silent unused + metadata. +- `compile_scene` now validates source availability for status-backed + non-temporal `Inputs(...)` bindings. When selected source objects already + have `Status` values, the requested source variable must resolve to + references instead of silently compiling to an unused/no-op binding. - Carrier compilation preserves source `Status` references and arbitrary value types; tests cover both scalar refs and many-object vectors with a custom non-`Float64` value type. @@ -121,6 +139,11 @@ should reproduce the same capabilities through unified object selections. environment bindings. Object movement invalidates only environment bindings, so moving a leaf or changing its geometry can refresh microclimate lookup without rebuilding object/model binding carriers. +- Added public geometry invalidation helpers: + `update_geometry!(scene, object, geometry; invalidate_environment=true)` + and object-scoped `mark_environment_binding_dirty!(scene, object)`. + These currently route to the scene environment binding cache invalidation; + finer-grained per-object dirty tracking can be added behind the same API. - Started scene/object execution with `run!(scene; steps=...)`. The runtime refreshes compiled object bindings and environment bindings, materializes precompiled `Inputs(...)` carriers into consumer `Status` diff --git a/src/PlantSimEngine.jl b/src/PlantSimEngine.jl index 5625c7198..04787e650 100644 --- a/src/PlantSimEngine.jl +++ b/src/PlantSimEngine.jl @@ -136,11 +136,11 @@ export TemporalState export OutputRequest, collect_outputs export effective_rate_summary export Scene, Object, ObjectId, SceneRegistry -export register_object!, remove_object!, reparent_object!, move_object!, refresh_bindings! +export register_object!, remove_object!, reparent_object!, move_object!, update_geometry!, refresh_bindings! export bindings_dirty, environment_bindings_dirty, scene_revision, environment_revision export compiled_bindings, compiled_environment_bindings, mark_environment_binding_dirty! export refresh_environment_bindings!, compile_environment_bindings, bind_environment -export object_ids, scene_objects, resolve_object_ids, resolve_objects, explain_objects +export object_ids, scene_objects, resolve_object_ids, resolve_objects, explain_objects, explain_scopes export geometry, position, bounds export CompiledScene, CompiledSceneApplication, CompiledSceneInputBinding, CompiledSceneCallBinding export compile_scene, explain_scene_applications, explain_bindings, explain_calls, explain_writers diff --git a/src/scene_object_api.jl b/src/scene_object_api.jl index c24d7fbb2..25dbea1ea 100644 --- a/src/scene_object_api.jl +++ b/src/scene_object_api.jl @@ -113,6 +113,13 @@ environment_revision(scene::Scene) = scene.environment_revision compiled_bindings(scene::Scene) = scene.binding_cache compiled_environment_bindings(scene::Scene) = scene.environment_binding_cache mark_environment_binding_dirty!(scene::Scene) = _mark_environment_bindings_dirty!(scene) +function mark_environment_binding_dirty!(scene::Scene, id) + _scene_object(scene, id) + return _mark_environment_bindings_dirty!(scene) +end +function mark_environment_binding_dirty!(scene::Scene, object::Object) + return mark_environment_binding_dirty!(scene, object.id) +end function _push_index!(index::Dict{Symbol,Set{ObjectId}}, key, id::ObjectId) isnothing(key) && return nothing @@ -206,9 +213,18 @@ function reparent_object!(scene::Scene, id, new_parent) end function move_object!(scene::Scene, id, geometry_or_position) + return update_geometry!(scene, id, geometry_or_position) +end + +function update_geometry!(scene::Scene, id, geometry_or_position; invalidate_environment::Bool=true) object = _scene_object(scene, id) object.geometry = geometry_or_position - _mark_environment_bindings_dirty!(scene) + invalidate_environment && _mark_environment_bindings_dirty!(scene) + return object +end + +function update_geometry!(object::Object, geometry_or_position) + object.geometry = geometry_or_position return object end @@ -290,6 +306,91 @@ function explain_objects(scene::Scene) ] end +function _object_id_values(ids) + return [id.value for id in _sort_object_ids!(collect(ids))] +end + +function _label_scope_rows(scene::Scene, scope_type::Symbol, label::Symbol, index) + return [ + ( + scope_type=scope_type, + selector=label => key, + context=nothing, + root_id=nothing, + scale=label == :scale ? key : nothing, + kind=label == :kind ? key : nothing, + species=label == :species ? key : nothing, + name=nothing, + object_ids=_object_id_values(ids), + n_objects=length(ids), + ) + for (key, ids) in sort!(collect(index); by=pair -> string(first(pair))) + ] +end + +function explain_scopes(scene::Scene) + rows = NamedTuple[] + all_ids = object_ids(scene) + push!( + rows, + ( + scope_type=:scene, + selector=SceneScope(), + context=nothing, + root_id=nothing, + scale=nothing, + kind=nothing, + species=nothing, + name=nothing, + object_ids=[id.value for id in all_ids], + n_objects=length(all_ids), + ), + ) + for object in scene_objects(scene) + descendant_ids = _object_id_values(_descendant_ids(scene, object.id)) + push!( + rows, + ( + scope_type=:object_subtree, + selector=Self(), + context=object.id.value, + root_id=object.id.value, + scale=object.scale, + kind=object.kind, + species=object.species, + name=object.name, + object_ids=descendant_ids, + n_objects=length(descendant_ids), + ), + ) + object_scope_name = object.id.value isa Symbol ? object.id.value : Symbol(string(object.id.value)) + scope_names = Symbol[object_scope_name] + isnothing(object.name) || push!(scope_names, object.name) + unique!(scope_names) + for scope_name in scope_names + push!( + rows, + ( + scope_type=:named_scope, + selector=Scope(scope_name), + context=nothing, + root_id=object.id.value, + scale=object.scale, + kind=object.kind, + species=object.species, + name=scope_name, + object_ids=descendant_ids, + n_objects=length(descendant_ids), + ), + ) + end + end + append!(rows, _label_scope_rows(scene, :scale, :scale, scene.registry.by_scale)) + append!(rows, _label_scope_rows(scene, :kind, :kind, scene.registry.by_kind)) + append!(rows, _label_scope_rows(scene, :species, :species, scene.registry.by_species)) + return rows +end + struct SceneScope <: AbstractObjectSelector end struct Self <: AbstractObjectSelector end struct SelfPlant <: AbstractObjectSelector end @@ -498,19 +599,26 @@ function resolve_object_ids(scene::Scene, selector::AbstractObjectMultiplicity; return _resolve_object_ids(scene, selector; context=context) end -function _resolve_object_ids(scene::Scene, selector::AbstractObjectMultiplicity; context=nothing, default_to_context::Bool=false) +function _resolve_object_ids( + scene::Scene, + selector::AbstractObjectMultiplicity; + context=nothing, + default_to_context::Bool=false, + default_scope=nothing, +) criteria_ = criteria(selector) relation = _criteria_value(criteria_, :relation, Relation) isnothing(relation) || error("`Relation(...)` selector resolution is not implemented yet.") - scope = _criteria_scope(criteria_) + explicit_scope = _criteria_scope(criteria_) + scope = isnothing(explicit_scope) ? default_scope : explicit_scope scale = _criteria_value(criteria_, :scale, Scale) kind = _criteria_value(criteria_, :kind, Kind) species = _criteria_value(criteria_, :species, Species) name = haskey(criteria_, :name) ? criteria_.name : nothing if default_to_context && - isnothing(scope) && + isnothing(explicit_scope) && isnothing(scale) && isnothing(kind) && isnothing(species) && @@ -537,6 +645,12 @@ end resolve_objects(scene::Scene, selector::AbstractObjectMultiplicity; context=nothing) = [_scene_object(scene, id) for id in resolve_object_ids(scene, selector; context=context)] +function _default_dependency_scope(scene::Scene, context::ObjectId) + object = _scene_object(scene, context) + (object.scale == :Scene || object.kind == :scene) && return SceneScope() + return Self() +end + struct CompiledSceneApplication{S,AT,TS,CL} id::Symbol spec::S @@ -799,7 +913,13 @@ function _selector_application(selector::AbstractObjectMultiplicity) end function _dependency_object_ids(scene::Scene, selector::AbstractObjectMultiplicity, context::ObjectId) - return _resolve_object_ids(scene, selector; context=context, default_to_context=true) + return _resolve_object_ids( + scene, + selector; + context=context, + default_to_context=true, + default_scope=_default_dependency_scope(scene, context), + ) end function _carrier_hint(selector::AbstractObjectMultiplicity, policy, window) @@ -879,6 +999,7 @@ function _compile_scene_input_bindings(scene::Scene, applications) declared_inputs isa NamedTuple || (declared_inputs = NamedTuple()) for (input_name, selector) in pairs(declared_inputs) input_sym = Symbol(input_name) + _validate_declared_scene_input_name!(application, input_sym) selector isa AbstractObjectMultiplicity || error( "Input binding `$(input_sym)` on application `$(application.id)` must use an object selector." ) @@ -911,6 +1032,7 @@ function _push_scene_input_binding!( source_ids_override=nothing, ) source_ids = isnothing(source_ids_override) ? _dependency_object_ids(scene, selector, consumer_id) : source_ids_override + isempty(source_ids) && selector isa OptionalOne && return bindings policy = _selector_policy(selector) window = _selector_window(selector) source_var = _selector_var(selector, input_sym) @@ -924,6 +1046,17 @@ function _push_scene_input_binding!( application_filter, ) carrier = _input_carrier(scene, selector, source_ids, source_var) + carrier_hint = _carrier_hint(selector, policy, window) + _validate_scene_input_source!( + scene, + application, + consumer_id, + input_sym, + source_var, + source_ids, + carrier, + carrier_hint, + ) push!( bindings, CompiledSceneInputBinding( @@ -940,7 +1073,7 @@ function _push_scene_input_binding!( multiplicity(selector), policy, window, - _carrier_hint(selector, policy, window), + carrier_hint, carrier, ), ) @@ -951,6 +1084,40 @@ function _scene_input_names(application::CompiledSceneApplication) return Symbol[Symbol(var) for var in keys(inputs_(application.spec))] end +function _validate_scene_input_source!( + scene::Scene, + application::CompiledSceneApplication, + consumer_id::ObjectId, + input_sym::Symbol, + source_var::Symbol, + source_ids::Vector{ObjectId}, + carrier, + carrier_hint::Symbol, +) + carrier_hint == :temporal_stream && return nothing + !isnothing(carrier) && return nothing + status_source_ids = ObjectId[ + source_id for source_id in source_ids + if _scene_object(scene, source_id).status isa Status + ] + isempty(status_source_ids) && return nothing + error( + "Input binding `$(input_sym)` on application `$(application.id)` for object ", + "`$(consumer_id.value)` reads `$(source_var)` from objects ", + "`$([id.value for id in status_source_ids])`, but no source `Status` reference is available." + ) +end + +function _validate_declared_scene_input_name!(application::CompiledSceneApplication, input_sym::Symbol) + input_names = Set(_scene_input_names(application)) + input_sym in input_names && return nothing + error( + "Input binding `$(input_sym)` on application `$(application.id)` is not declared by ", + "`inputs_` for process `$(application.process)`. Declared model inputs are ", + "`$(sort!(collect(input_names)))`." + ) +end + function _same_object_output_applications(applications_by_object, application::CompiledSceneApplication, object_id::ObjectId, variable::Symbol) matches = CompiledSceneApplication[] for candidate in get(applications_by_object, object_id, Any[]) @@ -1165,6 +1332,24 @@ function explain_schedule(compiled::CompiledScene) ] end +function _scene_binding_carrier_kind(binding::CompiledSceneInputBinding) + binding.carrier_hint == :temporal_stream && return :temporal_stream + carrier = binding.carrier + isnothing(carrier) && return :unresolved + carrier isa Base.RefValue && return :ref + carrier isa RefVector && return :ref_vector + carrier isa ObjectRefVector && return :object_ref_vector + return :custom +end + +function _scene_binding_copy_semantics(binding::CompiledSceneInputBinding) + kind = _scene_binding_carrier_kind(binding) + kind in (:ref, :ref_vector, :object_ref_vector) && return :live_references + kind == :temporal_stream && return :materialized_temporal_value + kind == :unresolved && return :not_materialized + return :backend_defined +end + function explain_bindings(compiled::CompiledScene) return [ ( @@ -1181,6 +1366,8 @@ function explain_bindings(compiled::CompiledScene) policy=binding.policy, window=binding.window, carrier_hint=binding.carrier_hint, + carrier_kind=_scene_binding_carrier_kind(binding), + copy_semantics=_scene_binding_copy_semantics(binding), has_reference_carrier=has_reference_carrier(binding), carrier_type=isnothing(binding.carrier) ? nothing : typeof(binding.carrier), selector=binding.selector, diff --git a/test/test-unified-scene-object-api.jl b/test/test-unified-scene-object-api.jl index 1326414dc..d86c64d2d 100644 --- a/test/test-unified-scene-object-api.jl +++ b/test/test-unified-scene-object-api.jl @@ -312,6 +312,23 @@ end Object(:soil; scale=:Soil, kind=:soil, parent=:scene), ) + scope_rows = explain_scopes(selector_scene) + scene_scope = only(row for row in scope_rows if row.scope_type == :scene) + @test scene_scope.selector isa SceneScope + @test scene_scope.object_ids == [:axis_1, :leaf_1, :leaf_2, :leaf_3, :plant_1, :plant_2, :scene, :soil] + plant_1_scope = only(row for row in scope_rows if row.scope_type == :object_subtree && row.root_id == :plant_1) + @test plant_1_scope.selector isa Self + @test plant_1_scope.object_ids == [:axis_1, :leaf_1, :leaf_2, :plant_1] + palm_2_scope = only(row for row in scope_rows if row.scope_type == :named_scope && row.name == :palm_2) + @test palm_2_scope.selector isa Scope + @test palm_2_scope.root_id == :plant_2 + @test palm_2_scope.object_ids == [:leaf_3, :plant_2] + leaf_label_scope = only(row for row in scope_rows if row.scope_type == :scale && row.scale == :Leaf) + @test leaf_label_scope.selector == (:scale => :Leaf) + @test leaf_label_scope.object_ids == [:leaf_1, :leaf_2, :leaf_3] + oil_palm_scope = only(row for row in scope_rows if row.scope_type == :species && row.species == :oil_palm) + @test oil_palm_scope.object_ids == [:axis_1, :leaf_1, :leaf_2, :leaf_3, :plant_1, :plant_2] + @test resolve_object_ids(selector_scene, Many(scale=:Leaf)) == [ObjectId(:leaf_1), ObjectId(:leaf_2), ObjectId(:leaf_3)] @test only(resolve_objects(selector_scene, One(scale=:Scene))).id == ObjectId(:scene) @@ -331,6 +348,8 @@ end @test_throws ErrorException resolve_object_ids(selector_scene, One(scale=:Flower)) @test_throws ErrorException resolve_object_ids(selector_scene, One(scale=:Leaf)) @test_throws ErrorException resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Self())) + @test resolve_object_ids(selector_scene, Many(scale=:Leaf); context=:plant_1) == + [ObjectId(:leaf_1), ObjectId(:leaf_2), ObjectId(:leaf_3)] leaf_selector = Many( kind="plant", @@ -492,6 +511,8 @@ end @test leaf_2_binding.source_ids == [:leaf_1, :leaf_2] @test leaf_2_binding.source_var == :leaf_area @test leaf_2_binding.carrier_hint == :temporal_stream + @test leaf_2_binding.carrier_kind == :temporal_stream + @test leaf_2_binding.copy_semantics == :materialized_temporal_value call_rows = explain_calls(compiled) @test length(call_rows) == 3 @@ -528,6 +549,36 @@ end @test disambiguated_call.callee_application_ids == [:sunlit_stomata] @test disambiguated_call.application == :sunlit_stomata + default_scope_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene, status=Status(signal_sum=0.0, temporal_total=0.0)), + Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene, status=Status(signal_sum=0.0, temporal_total=0.0)), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:plant_1, status=Status(leaf_area=1.0)), + Object(:leaf_2; scale=:Leaf, kind=:plant, parent=:plant_1, status=Status(leaf_area=2.0)), + Object(:plant_2; scale=:Plant, kind=:plant, parent=:scene, status=Status(signal_sum=0.0, temporal_total=0.0)), + Object(:leaf_3; scale=:Leaf, kind=:plant, parent=:plant_2, status=Status(leaf_area=3.0)), + ) + plant_default_scope = compile_scene( + default_scope_scene, + ( + ModelSpec(SceneObjectTemporalSumModel(); name=:plant_leaf_sum) |> + AppliesTo(Many(scale=:Plant)) |> + Inputs(:signal_sum => Many(scale=:Leaf, var=:leaf_area)), + ), + ) + @test only(row for row in explain_bindings(plant_default_scope) if row.consumer_id == :plant_1).source_ids == + [:leaf_1, :leaf_2] + @test only(row for row in explain_bindings(plant_default_scope) if row.consumer_id == :plant_2).source_ids == + [:leaf_3] + scene_default_scope = compile_scene( + default_scope_scene, + ( + ModelSpec(SceneObjectTemporalSumModel(); name=:scene_leaf_sum) |> + AppliesTo(One(scale=:Scene)) |> + Inputs(:signal_sum => Many(scale=:Leaf, var=:leaf_area)), + ), + ) + @test only(explain_bindings(scene_default_scope)).source_ids == [:leaf_1, :leaf_2, :leaf_3] + inferred_input_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0, observed_signal=0.0)), @@ -548,6 +599,8 @@ end @test inferred_binding.process == :scene_object_signal_source @test inferred_binding.application == :signal_source @test inferred_binding.has_reference_carrier + @test inferred_binding.carrier_kind == :ref + @test inferred_binding.copy_semantics == :live_references inferred_input_scene_with_apps = Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0, observed_signal=0.0)); @@ -600,12 +653,30 @@ end Inputs(:signal => One(scale=:Leaf, var=:signal, application=:missing_source)), ), ) + @test_throws ErrorException compile_scene( + inferred_input_scene, + ( + filtered_input_specs[1], + ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_consumer) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs(:siggnal => One(scale=:Leaf, var=:signal, application=:signal_source)), + ), + ) + @test_throws ErrorException compile_scene( + inferred_input_scene, + ( + filtered_input_specs[1], + ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_consumer) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs(:signal => One(scale=:Leaf, var=:missing_signal)), + ), + ) carrier_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:plant_1; scale=:Plant, kind=:plant, species=:oil_palm, parent=:scene), Object(:leaf_1; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1, status=Status(leaf_area=1.0, leaf_token=SceneObjectTaggedValue(1), aPPFD=100.0)), - Object(:leaf_2; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1, status=Status(leaf_area=2.0, leaf_token=SceneObjectTaggedValue(2), aPPFD=100.0)), + Object(:leaf_2; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1, status=Status(leaf_area=2.0, leaf_token=2, aPPFD=100.0)), Object(:soil; scale=:Soil, kind=:soil, parent=:scene, status=Status(soil_water_content=0.31)), ) carrier_specs = ( @@ -617,7 +688,7 @@ end ), ModelSpec(ToyAssimModel(); name=:assim) |> AppliesTo(Many(scale=:Leaf)) |> - Inputs(:soil_water_content => One(scale=:Soil, var=:soil_water_content)), + Inputs(:soil_water_content => One(scale=:Soil, within=SceneScope(), var=:soil_water_content)), ) carrier_compiled = compile_scene(carrier_scene, carrier_specs) carrier_rows = explain_bindings(carrier_compiled) @@ -631,16 +702,22 @@ end input_value(leaf_area_binding)[1] = 4.0 leaf_1_object = only(object for object in scene_objects(carrier_scene; scale=:Leaf) if object.id == ObjectId(:leaf_1)) @test leaf_1_object.status.leaf_area == 4.0 - @test only(row for row in carrier_rows if row.application_id == :carrier_consumer && row.consumer_id == :leaf_1 && row.input == :leaf_areas).has_reference_carrier + leaf_area_row = only(row for row in carrier_rows if row.application_id == :carrier_consumer && row.consumer_id == :leaf_1 && row.input == :leaf_areas) + @test leaf_area_row.has_reference_carrier + @test leaf_area_row.carrier_kind == :ref_vector + @test leaf_area_row.copy_semantics == :live_references token_binding = only( binding for binding in carrier_compiled.input_bindings if binding.application_id == :carrier_consumer && binding.consumer_id == ObjectId(:leaf_1) && binding.input == :leaf_tokens ) - @test input_value(token_binding)[2] == SceneObjectTaggedValue(2) - input_value(token_binding)[2] = SceneObjectTaggedValue(20) + @test input_value(token_binding)[2] == 2 + input_value(token_binding)[2] = 20 leaf_2_object = only(object for object in scene_objects(carrier_scene; scale=:Leaf) if object.id == ObjectId(:leaf_2)) - @test leaf_2_object.status.leaf_token == SceneObjectTaggedValue(20) + @test leaf_2_object.status.leaf_token == 20 + token_row = only(row for row in carrier_rows if row.application_id == :carrier_consumer && row.consumer_id == :leaf_1 && row.input == :leaf_tokens) + @test token_row.carrier_kind == :object_ref_vector + @test token_row.copy_semantics == :live_references scalar_binding = only( binding for binding in carrier_compiled.input_bindings @@ -651,6 +728,9 @@ end @test input_value(scalar_binding) == 0.31 input_carrier(scalar_binding)[] = 0.42 @test only(scene_objects(carrier_scene; scale=:Soil)).status.soil_water_content == 0.42 + scalar_row = only(row for row in carrier_rows if row.application_id == :assim && row.consumer_id == :leaf_1) + @test scalar_row.carrier_kind == :ref + @test scalar_row.copy_semantics == :live_references cache_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), @@ -746,12 +826,23 @@ end @test any(entity -> entity.id == :leaf_2 && entity.geometry == (cell=:cell_c,), grid_backend.index_updates[2]) @test only(row for row in explain_environment_bindings(refreshed_environment) if row.application_id == :probe && row.object_id == :leaf_2).cell == :cell_c + update_geometry!(environment_scene, :leaf_1, (cell=:cell_e,); invalidate_environment=false) + @test geometry(only(object for object in scene_objects(environment_scene; scale=:Leaf) if object.id == ObjectId(:leaf_1))) == (cell=:cell_e,) + @test !environment_bindings_dirty(environment_scene) + mark_environment_binding_dirty!(environment_scene, :leaf_1) + @test environment_bindings_dirty(environment_scene) + refreshed_after_mark = refresh_environment_bindings!(environment_scene) + @test !environment_bindings_dirty(environment_scene) + @test length(grid_backend.index_updates) == 3 + @test any(entity -> entity.id == :leaf_1 && entity.geometry == (cell=:cell_e,), grid_backend.index_updates[3]) + @test only(row for row in explain_environment_bindings(refreshed_after_mark) if row.application_id == :probe && row.object_id == :leaf_1).cell == :cell_e + register_object!(environment_scene, Object(:leaf_3; scale=:Leaf, kind=:plant, species=:oil_palm, geometry=(cell=:cell_d,)); parent=:plant_1) @test bindings_dirty(environment_scene) @test environment_bindings_dirty(environment_scene) refreshed_with_new_leaf = refresh_environment_bindings!(environment_scene) - @test length(grid_backend.index_updates) == 3 - @test any(entity -> entity.id == :leaf_3 && entity.geometry == (cell=:cell_d,), grid_backend.index_updates[3]) + @test length(grid_backend.index_updates) == 4 + @test any(entity -> entity.id == :leaf_3 && entity.geometry == (cell=:cell_d,), grid_backend.index_updates[4]) @test only(row for row in explain_scene_applications(refresh_bindings!(environment_scene)) if row.application_id == :probe).target_ids == [:leaf_1, :leaf_2, :leaf_3] @test only(row for row in explain_environment_bindings(refreshed_with_new_leaf) if row.application_id == :probe && row.object_id == :leaf_3).cell == :cell_d From 07e958a2c51c0c7eb8411157d4f8b3f8a76632ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Sat, 6 Jun 2026 17:00:40 +0200 Subject: [PATCH 18/33] Implements the remaining "Phase 5" mechanics Exceptional-organ Override(...). Instance and object-level model replacement validation. Concrete dispatch preserved for same-type parameter overrides. explain_instances(scene) and instance membership in explain_objects. New organs inherit instance kind and species. Hard-called overridden models use the correct per-object implementation. --- docs/src/dev/release_notes_handoff.md | 40 + docs/src/dev/unified_scene_object_design.md | 17 + ...nified_scene_object_implementation_plan.md | 66 ++ src/PlantSimEngine.jl | 4 +- src/scene_object_api.jl | 800 +++++++++++++++++- test/test-unified-scene-object-api.jl | 364 ++++++++ 6 files changed, 1258 insertions(+), 33 deletions(-) diff --git a/docs/src/dev/release_notes_handoff.md b/docs/src/dev/release_notes_handoff.md index 9de687831..98f624cdd 100644 --- a/docs/src/dev/release_notes_handoff.md +++ b/docs/src/dev/release_notes_handoff.md @@ -151,6 +151,20 @@ for multi-plant scene coupling. - Scene/object root applications now honor `TimeStep(...)` values backed by `Dates.Period` scheduling. `explain_schedule` reports normalized clocks and whether an application is root-scheduled or manual-call-only. +- Scene/object execution now uses a stable topological application order + compiled from `Inputs(...)` producer edges and `Updates(...)` ordering. + Dependencies on manual-call-only applications are redirected to their parent + caller, same-timestep cycles fail during compilation, and + `explain_schedule` reports `execution_index`. +- `CompiledScene` now pre-indexes input and call bindings by application and + object id. Runtime input materialization and hard-call lookup no longer scan + all scene bindings for every object/model invocation. +- `CompiledScene` now pre-indexes applications by application id, removing + application scans from hard-call target resolution and dictionary rebuilding + from ordered execution setup. +- `CompiledEnvironmentBindings` now pre-indexes environment bindings by + application and object id, removing the scene-wide binding scan from + environment sampling and output scattering. - Adds `SceneRunContext` and `SceneCallTarget`; scene/object models can use `dependency_target(s)(extra, :name)` plus `run_call!` for manual `Calls(...)` execution. @@ -164,6 +178,32 @@ for multi-plant scene coupling. - Adds `explain_writers(compiled)` to report object-variable writer groups, duplicate writers, and the `Updates(...)` declarations that validate ordered updates. +- Adds the first reusable object-template path with `ObjectTemplate` and + `ObjectInstance`. Templates bundle reusable `ModelSpec`s and default + `kind`/`species` labels; instances mount them inside a named object subtree. +- `Scene(...)` accepts mounted instances whose roots are either owned objects + or references to separately supplied scene objects. +- Template applications are scoped to their instance and receive stable + instance-prefixed application ids. Unmodified instances share the template's + model objects, while instance overrides can replace one application by name + or process when the replacement implements the same process. +- Adds `Override(...)` and `ObjectInstance(...; object_overrides=...)` for + exceptional organs. Overrides are resolved during compilation to concrete + object ids without splitting the logical application or changing its + dependency bindings. +- Template models, template parameter metadata, and replacement models are + retained by reference. The runtime does not copy models or mutate fields to + apply parameter overrides. +- Override validation requires the same process and declared status/environment + variable names. Application explanations report model storage, dispatch + mode, overridden object ids, and replacement model types. +- Adds `explain_instances(scene)` and instance membership in + `explain_objects(scene)`. Instance rows expose roots, current object + membership, mounted applications, overrides, template labels, and + reference-based parameter ownership. +- Objects created below a mounted instance inherit missing template `kind` and + `species` labels. Membership explanations use the current topology rather + than a copied instance object list. ## Planned Future Breaking Redesign diff --git a/docs/src/dev/unified_scene_object_design.md b/docs/src/dev/unified_scene_object_design.md index f78b75e13..e92fffa7e 100644 --- a/docs/src/dev/unified_scene_object_design.md +++ b/docs/src/dev/unified_scene_object_design.md @@ -147,6 +147,22 @@ Override( ) ``` +Ownership is reference-based and explicit: + +- a template retains the supplied model and parameter objects without copying; +- unchanged instances share those exact objects; +- an instance override replaces one complete model application with another + user-owned model object; +- an object override replaces that application only for the selected object; +- PlantSimEngine does not mutate model fields or implicitly merge parameter + dictionaries. + +Overrides must preserve the model contract: process identity and declared +status/environment variable names cannot change. Parameter-only overrides of +the same concrete model type retain concrete runtime dispatch. Heterogeneous +alternative implementations are supported but may require dynamic dispatch for +the exceptional application. + ### Model Kernel And Model Application A model kernel is the reusable model implementation written by a modeler. It @@ -576,6 +592,7 @@ explanation helpers: ```julia explain_objects(scene) +explain_instances(scene) explain_scopes(scene) explain_bindings(sim) explain_calls(sim) diff --git a/docs/src/dev/unified_scene_object_implementation_plan.md b/docs/src/dev/unified_scene_object_implementation_plan.md index 7747bd9f7..1ba7e8df7 100644 --- a/docs/src/dev/unified_scene_object_implementation_plan.md +++ b/docs/src/dev/unified_scene_object_implementation_plan.md @@ -164,6 +164,23 @@ should reproduce the same capabilities through unified object selections. `CompiledScene` now reports each application clock, phase, timestep in base steps, timestep duration in seconds, and whether the application is scheduled as a root application or is manual-call-only. +- `compile_scene` now computes a stable topological application order from + resolved `Inputs(...)` producer edges and `Updates(...)` writer-order edges. + Inputs produced by manual-call-only applications are redirected to the parent + application that owns the `Calls(...)` call stack. `run!(scene)` uses this + precompiled order instead of user declaration order, cycles fail at compile + time, and `explain_schedule` reports `execution_index`. +- `CompiledScene` now pre-indexes input and call bindings by + `(application_id, object_id)`. Per-object input materialization and + `dependency_target(s)` lookup use these indexes instead of scanning every + binding in the scene at each model call. +- `CompiledScene` now also pre-indexes applications by application id. + Hard-call target resolution and stable ordered-application materialization + use this index instead of scanning or rebuilding lookup dictionaries. +- `CompiledEnvironmentBindings` now pre-indexes environment bindings by + `(application_id, object_id)`. Environment sampling and mutable environment + output scattering use direct lookup instead of scanning all environment + bindings for every model invocation. - Added `SceneRunContext` and `SceneCallTarget`. Models can retrieve manual `Calls(...)` targets with `dependency_target(s)(extra, :name)` and execute them with `run_call!`, preserving explicit call-stack control in the @@ -207,6 +224,50 @@ should reproduce the same capabilities through unified object selections. `ModelSpec(scene_model) |> Calls(...)`. - Migrated the MAESPA example's scene LAI leaf-area route from user-written `Route(...)` to consumer-side `ModelSpec(LAIModel(...)) |> Inputs(...)`. +- Started Phase 5 with `ObjectTemplate` and `ObjectInstance`. A template stores + reusable scene/object `ModelSpec`s plus default object labels, and an + instance mounts those specs inside one named object subtree. +- `Scene(...)` accepts `ObjectInstance` values directly or through its + `instances` keyword. An instance root can be an owned `Object` or the id of + an object supplied separately to the scene. +- Mounted template applications receive stable instance-prefixed application + names and an implicit `Scope(instance_name)` on unqualified + `AppliesTo(...)` selectors. Their `Inputs(...)`, `Calls(...)`, scheduling, + writer validation, and execution use the normal compiled scene/object path. +- Instance overrides can replace one template application by application name + or process. Overrides must be unambiguous and preserve process identity. + Instances without overrides retain the exact shared model object from the + template. +- Template labels fill missing `kind` and `species` metadata throughout the + mounted subtree, while the root receives the instance name used by + `Scope(...)`. Tests cover four instances, plant-local aggregation, shared + model storage, and one process-level model override. +- Added explicit exceptional-organ overrides with + `Override(object=..., application=... or process=..., model=...)` through + `ObjectInstance(...; object_overrides=...)`. The override must resolve to one + template application, belong to the instance subtree, and preserve process, + input, output, and environment-variable declarations. +- Object overrides remain one logical model application: the compiler stores + the selected replacement model by target object id. Dependency bindings, + writer ownership, application names, and manual calls therefore remain + unchanged, and no selector resolution occurs in the runtime loop. +- Parameter/model ownership is explicit. Templates retain user-supplied model + and `parameters` objects by reference; unchanged instances share them. + Instance and object overrides retain their user-supplied replacement model + by reference. PlantSimEngine does not copy models or mutate model fields to + merge parameter overrides. +- Same-concrete-type object overrides use a concretely typed object-to-model + table. Structured application explanations report shared/per-object storage, + concrete versus heterogeneous dispatch, overridden object ids, and model + types. +- `Scene` retains mounted instance metadata and `explain_instances(scene)` + reports each instance root, current subtree object ids, mounted application + ids, instance/object overrides, template labels, and parameter ownership. + `explain_objects(scene)` also reports instance membership. +- New objects registered below an instance automatically inherit missing + template `kind` and `species` labels. Instance explanations derive membership + from the current topology, so growth, pruning, and reparenting do not leave a + separate stale membership list. This progress is still a bridge over the existing compiler, not the final object-address compiler. Supported `Inputs(...)` and `Calls(...)` selectors are @@ -506,6 +567,11 @@ Acceptance tests: - one palm instance can override one process parameter; - allocation remains per plant while scene LAI sees all leaves. +Current remaining work: + +- migrate the repeated plant construction in the MAESPA example to templates + once its complete object topology is represented by the unified runtime. + ## Phase 5B: Object Lifecycle And Cache Invalidation Goal: make growth, pruning, and moving organs update every compiled binding diff --git a/src/PlantSimEngine.jl b/src/PlantSimEngine.jl index 04787e650..b13fdf334 100644 --- a/src/PlantSimEngine.jl +++ b/src/PlantSimEngine.jl @@ -135,12 +135,12 @@ export OutputCache, HoldLastCache, InterpolateCache, IntegrateCache, AggregateCa export TemporalState export OutputRequest, collect_outputs export effective_rate_summary -export Scene, Object, ObjectId, SceneRegistry +export Scene, Object, ObjectId, SceneRegistry, ObjectTemplate, ObjectInstance, Override export register_object!, remove_object!, reparent_object!, move_object!, update_geometry!, refresh_bindings! export bindings_dirty, environment_bindings_dirty, scene_revision, environment_revision export compiled_bindings, compiled_environment_bindings, mark_environment_binding_dirty! export refresh_environment_bindings!, compile_environment_bindings, bind_environment -export object_ids, scene_objects, resolve_object_ids, resolve_objects, explain_objects, explain_scopes +export object_ids, scene_objects, resolve_object_ids, resolve_objects, explain_objects, explain_instances, explain_scopes export geometry, position, bounds export CompiledScene, CompiledSceneApplication, CompiledSceneInputBinding, CompiledSceneCallBinding export compile_scene, explain_scene_applications, explain_bindings, explain_calls, explain_writers diff --git a/src/scene_object_api.jl b/src/scene_object_api.jl index 25dbea1ea..9959b7903 100644 --- a/src/scene_object_api.jl +++ b/src/scene_object_api.jl @@ -30,6 +30,135 @@ mutable struct Object applications::Any end +""" + ObjectTemplate(applications=(); kind=nothing, species=nothing, mapping=applications, parameters=NamedTuple()) + +Reusable model-application bundle for one kind of scene object, such as a plant +species. Each mounted `ObjectInstance` scopes unqualified `AppliesTo(...)` +selectors to its own object subtree. Model objects are shared between instances +unless an instance supplies an override. + +`parameters` stores template-level metadata. Parameter-field merging is not +implicit: use an instance model override when model parameters differ. +""" +struct ObjectTemplate{A,P} + kind::Union{Nothing,Symbol} + species::Union{Nothing,Symbol} + applications::A + parameters::P +end + +_as_tuple(value::Tuple) = value +_as_tuple(value::AbstractVector) = Tuple(value) +_as_tuple(value) = (value,) + +function ObjectTemplate( + applications=(); + kind=nothing, + species=nothing, + mapping=applications, + parameters=NamedTuple(), +) + normalized_applications = _as_tuple(mapping) + return ObjectTemplate( + _maybe_symbol(kind), + _maybe_symbol(species), + normalized_applications, + parameters, + ) +end + +""" + Override(; object, model, process=nothing, application=nothing) + +Replace one template model application on one exceptional object. Select the +template application by its process, explicit application name, or both. +The replacement must implement the same process and variable contract. +""" +struct Override{M<:AbstractModel} + object::ObjectId + process::Union{Nothing,Symbol} + application::Union{Nothing,Symbol} + model::M +end + +function Override(; object, model::AbstractModel, process=nothing, application=nothing) + normalized_process = _maybe_symbol(process) + normalized_application = _maybe_symbol(application) + if isnothing(normalized_process) && isnothing(normalized_application) + error("`Override(...)` requires `process=...`, `application=...`, or both.") + end + return Override( + ObjectId(object), + normalized_process, + normalized_application, + model, + ) +end + +""" + ObjectInstance(name, template; root, objects=(), overrides=NamedTuple(), object_overrides=()) + +Mount an `ObjectTemplate` on one concrete scene-object subtree. + +`root` may be an `Object` owned by the instance or the id of an object supplied +separately to `Scene`. `objects` contains additional owned descendants. +`overrides` maps one template application name or process to a replacement +model implementing the same process. `object_overrides` contains `Override` +entries for exceptional organs. +""" +struct ObjectInstance{T,R,O,OV,OOV} + name::Symbol + template::T + root::R + objects::O + overrides::OV + object_overrides::OOV +end + +function ObjectInstance( + name, + template::ObjectTemplate; + root, + objects=(), + overrides=NamedTuple(), + object_overrides=(), +) + normalized_objects = _as_tuple(objects) + normalized_object_overrides = _as_tuple(object_overrides) + all(object -> object isa Object, normalized_objects) || error( + "`ObjectInstance(...; objects=...)` must contain `Object` values." + ) + overrides isa NamedTuple || error( + "`ObjectInstance(...; overrides=...)` must be a NamedTuple keyed by application name or process." + ) + all(override -> override isa Override, normalized_object_overrides) || error( + "`ObjectInstance(...; object_overrides=...)` must contain `Override` values." + ) + return ObjectInstance( + Symbol(name), + template, + root, + normalized_objects, + overrides, + normalized_object_overrides, + ) +end + +struct ObjectModelOverrides{M,O} <: AbstractModel + base::M + overrides::O +end + +process(model::ObjectModelOverrides) = process(model.base) +inputs_(model::ObjectModelOverrides) = inputs_(model.base) +outputs_(model::ObjectModelOverrides) = outputs_(model.base) +dep(model::ObjectModelOverrides, nsteps=1) = dep(model.base, nsteps) +timespec(model::ObjectModelOverrides) = timespec(model.base) +output_policy(model::ObjectModelOverrides) = output_policy(model.base) +meteo_inputs_(model::ObjectModelOverrides) = meteo_inputs_(model.base) +meteo_outputs_(model::ObjectModelOverrides) = meteo_outputs_(model.base) + function Object( id; scale=nothing, @@ -72,10 +201,11 @@ SceneRegistry() = SceneRegistry( Dict{Symbol,ObjectId}(), ) -mutable struct Scene{R,A,E} +mutable struct Scene{R,A,E,I} registry::R applications::A environment::E + instances::I binding_cache::Any environment_binding_cache::Any bindings_dirty::Bool @@ -84,14 +214,145 @@ mutable struct Scene{R,A,E} environment_revision::Int end -function Scene(objects::Object...; applications=(), environment=nothing) - scene = Scene(SceneRegistry(), applications, environment, nothing, nothing, true, true, 0, 0) +function _normalize_object_instances(instances) + instances isa ObjectInstance && return (instances,) + normalized = _as_tuple(instances) + all(instance -> instance isa ObjectInstance, normalized) || error( + "Scene instances must be `ObjectInstance` values." + ) + return normalized +end + +function _instance_root_id(instance::ObjectInstance) + return instance.root isa Object ? instance.root.id : ObjectId(instance.root) +end + +function _collect_scene_items(items, instances) + objects = Object[] + mounted_instances = ObjectInstance[] + for item in items + if item isa Object + push!(objects, item) + elseif item isa ObjectInstance + push!(mounted_instances, item) + else + error("A `Scene` can contain only `Object` and `ObjectInstance` values, got `$(typeof(item))`.") + end + end + append!(mounted_instances, _normalize_object_instances(instances)) + for instance in mounted_instances + instance.root isa Object && push!(objects, instance.root) + append!(objects, instance.objects) + end + ids = Set{ObjectId}() for object in objects - register_object!(scene, object) + object.id in ids && error("Scene contains object id `$(object.id.value)` more than once.") + push!(ids, object.id) + end + return objects, mounted_instances +end + +function _object_descendant_ids(objects_by_id, root_id::ObjectId) + ids = ObjectId[root_id] + frontier = ObjectId[root_id] + while !isempty(frontier) + parent_id = popfirst!(frontier) + for object in values(objects_by_id) + object.parent == parent_id || continue + object.id in ids && continue + push!(ids, object.id) + push!(frontier, object.id) + end + end + return ids +end + +function _prepare_object_instances!(objects, instances) + objects_by_id = Dict(object.id => object for object in objects) + claimed_ids = Dict{ObjectId,Symbol}() + instance_ids = Dict{Symbol,Vector{ObjectId}}() + for instance in instances + root_id = _instance_root_id(instance) + haskey(objects_by_id, root_id) || error( + "Object instance `$(instance.name)` refers to missing root object `$(root_id.value)`." + ) + ids = _object_descendant_ids(objects_by_id, root_id) + for id in ids + if haskey(claimed_ids, id) + error( + "Object `$(id.value)` belongs to both instances `$(claimed_ids[id])` and `$(instance.name)`." + ) + end + claimed_ids[id] = instance.name + object = objects_by_id[id] + isnothing(object.kind) && (object.kind = instance.template.kind) + isnothing(object.species) && (object.species = instance.template.species) + end + root = objects_by_id[root_id] + if !isnothing(root.name) && root.name != instance.name + error( + "Object instance `$(instance.name)` root `$(root_id.value)` already has the conflicting name `$(root.name)`." + ) + end + root.name = instance.name + instance_ids[instance.name] = ids + end + length(instance_ids) == length(instances) || error("Object instance names must be unique within a scene.") + return instance_ids +end + +function _register_scene_objects!(scene::Scene, objects) + pending = copy(objects) + while !isempty(pending) + registered = false + for index in reverse(eachindex(pending)) + object = pending[index] + if isnothing(object.parent) || haskey(scene.registry.objects, object.parent) + register_object!(scene, object) + deleteat!(pending, index) + registered = true + end + end + registered && continue + unresolved = [(object.id.value, isnothing(object.parent) ? nothing : object.parent.value) for object in pending] + error("Cannot register scene objects because parent objects are missing or cyclic: $(unresolved).") end return scene end +""" + Scene(items...; applications=(), instances=(), environment=nothing) + +Create a scene from `Object` and `ObjectInstance` values. Global applications +and applications mounted from object instances are compiled through the same +scene/object dependency graph. +""" +function Scene( + items::Union{Object,ObjectInstance}...; + applications=(), + instances=(), + environment=nothing, +) + objects, mounted_instances = _collect_scene_items(items, instances) + instance_ids = _prepare_object_instances!(objects, mounted_instances) + mounted_applications = _mount_object_instance_applications(mounted_instances, instance_ids) + normalized_applications = _as_tuple(applications) + all_applications = (normalized_applications..., mounted_applications...) + scene = Scene( + SceneRegistry(), + all_applications, + environment, + mounted_instances, + nothing, + nothing, + true, + true, + 0, + 0, + ) + return _register_scene_objects!(scene, objects) +end + function _mark_environment_bindings_dirty!(scene::Scene) scene.environment_binding_cache = nothing scene.environment_bindings_dirty = true @@ -140,7 +401,15 @@ function _index_object!(registry::SceneRegistry, object::Object) _push_index!(registry.by_scale, object.scale, object.id) _push_index!(registry.by_kind, object.kind, object.id) _push_index!(registry.by_species, object.species, object.id) - isnothing(object.name) || (registry.by_name[object.name] = object.id) + if !isnothing(object.name) + existing = get(registry.by_name, object.name, nothing) + if !isnothing(existing) && existing != object.id + error( + "Scene object name `$(object.name)` is already used by object `$(existing.value)`." + ) + end + registry.by_name[object.name] = object.id + end return nothing end @@ -160,14 +429,46 @@ function _scene_object(scene::Scene, id) return scene.registry.objects[oid] end +function _instance_for_object(scene::Scene, id) + current_id = ObjectId(id) + while haskey(scene.registry.objects, current_id) + for instance in scene.instances + _instance_root_id(instance) == current_id && return instance + end + parent = scene.registry.objects[current_id].parent + isnothing(parent) && return nothing + current_id = parent + end + return nothing +end + +function _apply_instance_labels!(object::Object, instance) + isnothing(instance) && return object + isnothing(object.kind) && (object.kind = instance.template.kind) + isnothing(object.species) && (object.species = instance.template.species) + return object +end + function register_object!(scene::Scene, object::Object; parent=object.parent) registry = scene.registry haskey(registry.objects, object.id) && error("Scene already contains object id `$(object.id.value)`.") - object.parent = isnothing(parent) ? nothing : ObjectId(parent) + parent_id = isnothing(parent) ? nothing : ObjectId(parent) + if !isnothing(parent_id) && !haskey(registry.objects, parent_id) + error("No scene object with id `$(parent_id.value)`.") + end + if !isnothing(object.name) + existing = get(registry.by_name, object.name, nothing) + isnothing(existing) || error( + "Scene object name `$(object.name)` is already used by object `$(existing.value)`." + ) + end + instance = isnothing(parent_id) ? nothing : _instance_for_object(scene, parent_id) + _apply_instance_labels!(object, instance) + object.parent = parent_id registry.objects[object.id] = object _index_object!(registry, object) if !isnothing(object.parent) - parent_object = _scene_object(scene, object.parent) + parent_object = registry.objects[object.parent] object.id in parent_object.children || push!(parent_object.children, object.id) end _mark_bindings_dirty!(scene) @@ -288,6 +589,17 @@ end scene_objects(scene::Scene; kwargs...) = [_scene_object(scene, id) for id in object_ids(scene; kwargs...)] +function _instance_object_ids(scene::Scene, instance::ObjectInstance) + root_id = _instance_root_id(instance) + haskey(scene.registry.objects, root_id) || return ObjectId[] + return _sort_object_ids!(_descendant_ids(scene, root_id)) +end + +function _object_instance_name(scene::Scene, object_id::ObjectId) + instance = _instance_for_object(scene, object_id) + return isnothing(instance) ? nothing : instance.name +end + function explain_objects(scene::Scene) return [ ( @@ -296,6 +608,7 @@ function explain_objects(scene::Scene) kind=object.kind, species=object.species, name=object.name, + instance=_object_instance_name(scene, object.id), parent=isnothing(object.parent) ? nothing : object.parent.value, children=[child.value for child in object.children], has_geometry=!isnothing(object.geometry), @@ -306,6 +619,43 @@ function explain_objects(scene::Scene) ] end +function _instance_application_ids(scene::Scene, instance::ObjectInstance) + prefix = string(instance.name, "__") + ids = Symbol[] + for application in scene.applications + name = application_name(as_model_spec(application)) + isnothing(name) && continue + startswith(string(name), prefix) && push!(ids, name) + end + return sort!(ids; by=string) +end + +function explain_instances(scene::Scene) + return [ + ( + name=instance.name, + root_id=_instance_root_id(instance).value, + kind=instance.template.kind, + species=instance.template.species, + object_ids=[id.value for id in _instance_object_ids(scene, instance)], + application_ids=_instance_application_ids(scene, instance), + instance_overrides=sort!(Symbol[Symbol(name) for name in keys(instance.overrides)]; by=string), + object_overrides=[ + ( + object_id=override.object.value, + process=override.process, + application=override.application, + model_type=typeof(override.model), + ) + for override in instance.object_overrides + ], + parameters_type=typeof(instance.template.parameters), + parameters_shared_by_reference=true, + ) + for instance in scene.instances + ] +end + function _object_id_values(ids) return [id.value for id in _sort_object_ids!(collect(ids))] end @@ -468,6 +818,212 @@ multiplicity(::One) = :one multiplicity(::OptionalOne) = :optional_one multiplicity(::Many) = :many +_rebuild_selector(::One, criteria) = One{typeof(criteria)}(criteria) +_rebuild_selector(::OptionalOne, criteria) = OptionalOne{typeof(criteria)}(criteria) +_rebuild_selector(::Many, criteria) = Many{typeof(criteria)}(criteria) + +function _selector_with_scope(selector::AbstractObjectMultiplicity, scope) + selector_criteria = criteria(selector) + haskey(selector_criteria, :within) && return selector + return _rebuild_selector(selector, merge(selector_criteria, (; within=scope))) +end + +function _selector_with_application_prefix( + selector::AbstractObjectMultiplicity, + instance_name::Symbol, + template_application_names::Set{Symbol}, +) + selector_criteria = criteria(selector) + haskey(selector_criteria, :application) || return selector + application = selector_criteria.application + isnothing(application) && return selector + application in template_application_names || return selector + mounted_name = Symbol(instance_name, "__", application) + return _rebuild_selector(selector, merge(selector_criteria, (; application=mounted_name))) +end + +function _mounted_application_name(spec, index::Int) + name = application_name(spec) + return isnothing(name) ? process(spec) : name +end + +function _instance_override_matches(spec, key::Symbol) + name = application_name(spec) + return key == process(spec) || (!isnothing(name) && key == name) +end + +function _instance_override_models(instance::ObjectInstance, specs) + selected = Dict{Int,AbstractModel}() + for (key, replacement) in pairs(instance.overrides) + replacement isa AbstractModel || error( + "Override `$(key)` for instance `$(instance.name)` must be an `AbstractModel`, got `$(typeof(replacement))`." + ) + matches = findall(spec -> _instance_override_matches(spec, Symbol(key)), specs) + isempty(matches) && error( + "Override `$(key)` for instance `$(instance.name)` does not match a template application name or process." + ) + length(matches) == 1 || error( + "Override `$(key)` for instance `$(instance.name)` matches several template applications; use a unique application name." + ) + index = only(matches) + haskey(selected, index) && error( + "Several overrides target template application `$(_mounted_application_name(specs[index], index))` in instance `$(instance.name)`." + ) + _validate_model_override_contract!( + model_(specs[index]), + replacement; + description="Override `$(key)` for instance `$(instance.name)`", + ) + selected[index] = replacement + end + return selected +end + +function _model_contract(model) + return ( + process=process(model), + inputs=Tuple(Symbol.(keys(inputs_(model)))), + outputs=Tuple(Symbol.(keys(outputs_(model)))), + meteo_inputs=Tuple(Symbol.(keys(meteo_inputs_(model)))), + meteo_outputs=Tuple(Symbol.(keys(meteo_outputs_(model)))), + ) +end + +function _validate_model_override_contract!(base, replacement; description) + base_contract = _model_contract(base) + replacement_contract = _model_contract(replacement) + base_contract == replacement_contract && return nothing + error( + "$(description) has an incompatible model contract. Expected ", + "`$(base_contract)`, got `$(replacement_contract)`. Object and instance ", + "overrides may change parameters or implementation, but not process or declared variables." + ) +end + +function _object_override_matches(spec, override::Override) + process_match = isnothing(override.process) || process(spec) == override.process + name = application_name(spec) + application_match = isnothing(override.application) || + (!isnothing(name) && name == override.application) + return process_match && application_match +end + +function _object_override_models(instance::ObjectInstance, specs, instance_ids) + entries = Dict{Int,Vector{Pair{ObjectId,AbstractModel}}}() + valid_ids = Set(instance_ids) + for override in instance.object_overrides + override.object in valid_ids || error( + "Object override for `$(override.object.value)` does not belong to instance `$(instance.name)`." + ) + matches = findall(spec -> _object_override_matches(spec, override), specs) + isempty(matches) && error( + "Object override for `$(override.object.value)` in instance `$(instance.name)` ", + "does not match a template application." + ) + length(matches) == 1 || error( + "Object override for `$(override.object.value)` in instance `$(instance.name)` ", + "matches several template applications; add `application=...`." + ) + index = only(matches) + object_models = get!(entries, index, Pair{ObjectId,AbstractModel}[]) + any(entry -> first(entry) == override.object, object_models) && error( + "Several object overrides target application `$(_mounted_application_name(specs[index], index))` ", + "on object `$(override.object.value)` in instance `$(instance.name)`." + ) + _validate_model_override_contract!( + model_(specs[index]), + override.model; + description="Object override for `$(override.object.value)` in instance `$(instance.name)`", + ) + push!(object_models, override.object => override.model) + end + selected = Dict{Int,Any}() + for (index, object_models) in entries + selected[index] = _typed_object_model_dict(object_models) + end + return selected +end + +function _typed_object_model_dict(entries) + isempty(entries) && return Dict{ObjectId,AbstractModel}() + model_type = typeof(last(first(entries))) + if all(entry -> typeof(last(entry)) == model_type, entries) + models = Dict{ObjectId,model_type}() + for (object_id, model) in entries + models[object_id] = model + end + return models + end + return Dict{ObjectId,AbstractModel}(entries) +end + +function _map_selector_bindings(bindings::NamedTuple, f) + mapped = Pair{Symbol,Any}[] + for (name, selector) in pairs(bindings) + push!(mapped, Symbol(name) => (selector isa AbstractObjectMultiplicity ? f(selector) : selector)) + end + return (; mapped...) +end + +function _mount_object_instance_applications(instance::ObjectInstance, instance_ids) + specs = Tuple(as_model_spec(application) for application in instance.template.applications) + base_names = Set(_mounted_application_name(spec, index) for (index, spec) in pairs(specs)) + instance_overrides = _instance_override_models(instance, specs) + object_overrides = _object_override_models(instance, specs, instance_ids) + mounted = Any[] + for (index, spec) in pairs(specs) + base_name = _mounted_application_name(spec, index) + mounted_name = Symbol(instance.name, "__", base_name) + target = applies_to(spec) + isnothing(target) && error( + "Template application `$(base_name)` has no `AppliesTo(...)` selector." + ) + mounted_target = _selector_with_scope(target, Scope(instance.name)) + prefix_application = selector -> _selector_with_application_prefix( + selector, + instance.name, + base_names, + ) + mounted_inputs = _map_selector_bindings(value_inputs(spec), prefix_application) + mounted_calls = _map_selector_bindings(model_calls(spec), prefix_application) + mounted_model = get(instance_overrides, index, model_(spec)) + if haskey(object_overrides, index) + object_models = object_overrides[index] + for replacement in values(object_models) + _validate_model_override_contract!( + mounted_model, + replacement; + description="Object override for template application `$(base_name)`", + ) + end + mounted_model = ObjectModelOverrides(mounted_model, object_models) + end + push!( + mounted, + ModelSpec( + spec; + model=mounted_model, + name=mounted_name, + applies_to=mounted_target, + inputs=mounted_inputs, + calls=mounted_calls, + ), + ) + end + return Tuple(mounted) +end + +function _mount_object_instance_applications(instances, instance_ids) + mounted = Any[] + for instance in instances + append!( + mounted, + _mount_object_instance_applications(instance, instance_ids[instance.name]), + ) + end + return Tuple(mounted) +end + _sort_object_ids!(ids) = sort!(ids; by=id -> string(id.value)) function _object_id_from_context(context) @@ -651,7 +1207,7 @@ function _default_dependency_scope(scene::Scene, context::ObjectId) return Self() end -struct CompiledSceneApplication{S,AT,TS,CL} +struct CompiledSceneApplication{S,AT,TS,CL,MO} id::Symbol spec::S process::Symbol @@ -660,6 +1216,7 @@ struct CompiledSceneApplication{S,AT,TS,CL} applies_to::AT timestep::TS clock::CL + model_overrides::MO end struct CompiledSceneInputBinding{SEL,P,W,C} @@ -704,21 +1261,38 @@ struct CompiledEnvironmentBinding{B,C,S} config::Any end -struct CompiledEnvironmentBindings{SC,B} +struct CompiledEnvironmentBindings{SC,B,I} scene::SC bindings::B + by_target::I scene_revision::Int environment_revision::Int end -struct CompiledScene{SC,AP,IB,CB} +struct CompiledScene{SC,AP,AI,IB,CB,IBI,CBI,AO} scene::SC applications::AP + applications_by_id::AI input_bindings::IB call_bindings::CB + input_bindings_by_target::IBI + call_bindings_by_target::CBI + application_order::AO revision::Int end +function _index_scene_bindings(bindings, application_field::Symbol, object_field::Symbol) + grouped = Dict{Tuple{Symbol,ObjectId},Vector{Any}}() + for binding in bindings + key = ( + getproperty(binding, application_field), + getproperty(binding, object_field), + ) + push!(get!(grouped, key, Any[]), binding) + end + return Dict(key => Tuple(values) for (key, values) in grouped) +end + function compile_scene(scene::Scene) return compile_scene(scene, scene.applications) end @@ -751,7 +1325,21 @@ function _compile_scene(scene::Scene, raw_specs) input_bindings = _compile_scene_input_bindings(scene, applications) _validate_scene_required_inputs!(scene, applications, input_bindings) call_bindings = _compile_scene_call_bindings(scene, applications) - return CompiledScene(scene, applications, input_bindings, call_bindings, scene.revision) + input_bindings_by_target = _index_scene_bindings(input_bindings, :application_id, :consumer_id) + call_bindings_by_target = _index_scene_bindings(call_bindings, :application_id, :consumer_id) + application_order = _compile_scene_application_order(applications, input_bindings, call_bindings) + applications_by_id = Dict(application.id => application for application in applications) + return CompiledScene( + scene, + applications, + applications_by_id, + input_bindings, + call_bindings, + input_bindings_by_target, + call_bindings_by_target, + application_order, + scene.revision, + ) end function _compile_scene_applications(scene::Scene, raw_specs, timeline) @@ -775,6 +1363,7 @@ function _compile_scene_applications(scene::Scene, raw_specs, timeline) app_id in ids && error("Duplicate compiled scene application id `$(app_id)`.") push!(ids, app_id) target_ids = resolve_object_ids(scene, selector) + model_overrides = _compiled_object_model_overrides(spec, target_ids, app_id) push!( applications, CompiledSceneApplication( @@ -786,12 +1375,39 @@ function _compile_scene_applications(scene::Scene, raw_specs, timeline) selector, timestep(spec), _scene_application_clock(spec, timeline), + model_overrides, ), ) end return applications end +function _compiled_object_model_overrides(spec, target_ids, application_id::Symbol) + model = model_(spec) + model isa ObjectModelOverrides || return nothing + target_set = Set(target_ids) + unmatched = ObjectId[id for id in keys(model.overrides) if !(id in target_set)] + isempty(unmatched) || error( + "Object override(s) `$([id.value for id in unmatched])` for application ", + "`$(application_id)` do not match its `AppliesTo(...)` target set." + ) + return model.overrides +end + +_application_default_model(application::CompiledSceneApplication) = + model_(application.spec) isa ObjectModelOverrides ? + model_(application.spec).base : + model_(application.spec) + +function _application_model(application::CompiledSceneApplication, object_id::ObjectId) + isnothing(application.model_overrides) && return _application_default_model(application) + return get( + application.model_overrides, + object_id, + _application_default_model(application), + ) +end + function _scene_output_names(application::CompiledSceneApplication) return Symbol[Symbol(var) for var in keys(outputs_(application.spec))] end @@ -1296,6 +1912,94 @@ function _compile_scene_call_bindings(scene::Scene, applications) return bindings end +function _scene_call_owners(call_bindings) + owners = Dict{Symbol,Set{Symbol}}() + for binding in call_bindings + for callee_id in binding.callee_application_ids + push!(get!(owners, callee_id, Set{Symbol}()), binding.application_id) + end + end + return owners +end + +function _add_scene_application_edge!(children, parent::Symbol, child::Symbol) + parent == child && return nothing + push!(get!(children, parent, Set{Symbol}()), child) + return nothing +end + +function _scene_input_order_edges!(children, input_bindings, call_owners) + for binding in input_bindings + for source_id in binding.source_application_ids + owners = get(call_owners, source_id, nothing) + if isnothing(owners) + _add_scene_application_edge!(children, source_id, binding.application_id) + else + for owner_id in owners + _add_scene_application_edge!(children, owner_id, binding.application_id) + end + end + end + end + return children +end + +function _scene_update_order_edges!(children, applications) + for indexed_writers in values(_scene_writer_groups(applications)) + length(indexed_writers) > 1 || continue + sort!(indexed_writers; by=first) + for index in 2:length(indexed_writers) + previous_application = indexed_writers[index - 1][2] + application = indexed_writers[index][2] + _add_scene_application_edge!(children, previous_application.id, application.id) + end + end + return children +end + +function _stable_topological_application_order(applications, children) + application_ids = Symbol[application.id for application in applications] + positions = Dict(application_id => index for (index, application_id) in pairs(application_ids)) + indegree = Dict(application_id => 0 for application_id in application_ids) + for child_ids in values(children) + for child_id in child_ids + indegree[child_id] = get(indegree, child_id, 0) + 1 + end + end + ready = Symbol[application_id for application_id in application_ids if indegree[application_id] == 0] + order = Symbol[] + while !isempty(ready) + sort!(ready; by=application_id -> positions[application_id]) + application_id = popfirst!(ready) + push!(order, application_id) + child_ids = sort!(collect(get(children, application_id, Set{Symbol}())); by=child_id -> positions[child_id]) + for child_id in child_ids + indegree[child_id] -= 1 + indegree[child_id] == 0 && push!(ready, child_id) + end + end + if length(order) != length(application_ids) + remaining = Symbol[application_id for application_id in application_ids if indegree[application_id] > 0] + error( + "Scene application dependency cycle detected among applications `$(remaining)`. ", + "Break the same-timestep cycle with a temporal policy or revise `Inputs(...)`/`Updates(...)`." + ) + end + return order +end + +function _compile_scene_application_order(applications, input_bindings, call_bindings) + children = Dict{Symbol,Set{Symbol}}() + call_owners = _scene_call_owners(call_bindings) + _scene_input_order_edges!(children, input_bindings, call_owners) + _scene_update_order_edges!(children, applications) + return _stable_topological_application_order(applications, children) +end + +function _ordered_scene_applications(compiled::CompiledScene) + return [compiled.applications_by_id[application_id] for application_id in compiled.application_order] +end + function explain_scene_applications(compiled::CompiledScene) return [ ( @@ -1306,19 +2010,44 @@ function explain_scene_applications(compiled::CompiledScene) applies_to=application.applies_to, timestep=application.timestep, clock=application.clock, - model_type=typeof(model_(application.spec)), + model_type=typeof(_application_default_model(application)), + model_storage=isnothing(application.model_overrides) ? :shared_application : :per_object_override, + model_dispatch=_application_model_dispatch(application), + object_overrides=isnothing(application.model_overrides) ? + NamedTuple[] : + [ + ( + object_id=object_id.value, + model_type=typeof(model), + ) + for (object_id, model) in sort!( + collect(application.model_overrides); + by=pair -> string(first(pair).value), + ) + ], ) for application in compiled.applications ] end +function _application_model_dispatch(application::CompiledSceneApplication) + isnothing(application.model_overrides) && return :concrete_shared + override_type = valtype(typeof(application.model_overrides)) + default_type = typeof(_application_default_model(application)) + return isconcretetype(override_type) && default_type == override_type ? + :concrete_per_object : + :heterogeneous_per_object +end + function explain_schedule(compiled::CompiledScene) timeline = _scene_timeline(compiled.scene) manual_application_ids = _manual_call_application_ids(compiled) + execution_positions = Dict(application_id => index for (index, application_id) in pairs(compiled.application_order)) return [ ( application_id=application.id, process=application.process, + execution_index=execution_positions[application.id], timestep=application.timestep, clock=application.clock, dt_steps=application.clock.dt, @@ -1328,7 +2057,7 @@ function explain_schedule(compiled::CompiledScene) root_scheduled=!(application.id in manual_application_ids), manual_call_only=application.id in manual_application_ids, ) - for application in compiled.applications + for application in _ordered_scene_applications(compiled) ] end @@ -1529,9 +2258,18 @@ end function compile_environment_bindings(scene::Scene, compiled::CompiledScene=refresh_bindings!(scene)) _update_scene_environment_indices!(scene, compiled) + bindings = _compile_environment_bindings(scene, compiled) + by_target = Dict( + (binding.application_id, binding.object_id) => binding + for binding in bindings + ) + length(by_target) == length(bindings) || error( + "Environment binding compilation produced duplicate `(application_id, object_id)` targets." + ) return CompiledEnvironmentBindings( scene, - _compile_environment_bindings(scene, compiled), + bindings, + by_target, scene.revision, scene.environment_revision, ) @@ -1581,17 +2319,13 @@ struct SceneCallTarget{CS,EB,A,S,TS,C} end function _compiled_application_by_id(compiled::CompiledScene, id::Symbol) - for application in compiled.applications - application.id == id && return application - end - error("No compiled scene application with id `$(id)`.") + application = get(compiled.applications_by_id, id, nothing) + isnothing(application) && error("No compiled scene application with id `$(id)`.") + return application end function _environment_binding_for(env_bindings::CompiledEnvironmentBindings, application_id::Symbol, object_id::ObjectId) - for binding in env_bindings.bindings - binding.application_id == application_id && binding.object_id == object_id && return binding - end - return nothing + return get(env_bindings.by_target, (application_id, object_id), nothing) end function _scene_object_status(scene::Scene, object_id::ObjectId) @@ -1741,9 +2475,8 @@ function _materialize_scene_inputs!( ) status = _scene_object_status(compiled.scene, object_id) timeline = _scene_timeline(compiled.scene) - for binding in compiled.input_bindings - binding.application_id == application.id || continue - binding.consumer_id == object_id || continue + bindings = get(compiled.input_bindings_by_target, (application.id, object_id), ()) + for binding in bindings if binding.carrier_hint == :temporal_stream isnothing(streams) && continue value = _scene_temporal_input_value(binding, application, streams, time, timeline) @@ -1794,7 +2527,8 @@ function _run_scene_application!( status = _materialize_scene_inputs!(compiled, application, object_id, temporal_streams, time) meteo = _scene_meteo_for_model(env_bindings, application, object_id, time) context = SceneRunContext(compiled, env_bindings, application, object_id, temporal_streams, float(time), constants) - run!(application.spec, nothing, status, meteo, constants, context) + model = _application_model(application, object_id) + run!(model, nothing, status, meteo, constants, context) if publish _scatter_scene_environment_outputs!(env_bindings, application, object_id, status, time) _scene_publish_outputs!(temporal_streams, application, object_id, status, time) @@ -1815,9 +2549,12 @@ end function _scene_call_targets(context::SceneRunContext, name::Symbol) targets = SceneCallTarget[] - for binding in context.compiled.call_bindings - binding.application_id == context.application.id || continue - binding.consumer_id == context.object_id || continue + bindings = get( + context.compiled.call_bindings_by_target, + (context.application.id, context.object_id), + (), + ) + for binding in bindings binding.call == name || continue for application_id in binding.callee_application_ids callee_application = _compiled_application_by_id(context.compiled, application_id) @@ -1831,7 +2568,7 @@ function _scene_call_targets(context::SceneRunContext, name::Symbol) context.environment_bindings, callee_application, object_id, - model_(callee_application.spec), + _application_model(callee_application, object_id), status, context.temporal_streams, context.time, @@ -1866,8 +2603,9 @@ function run!(scene::Scene; steps::Integer=1, constants=nothing) env_bindings = refresh_environment_bindings!(scene, compiled) manual_application_ids = _manual_call_application_ids(compiled) temporal_streams = Dict{Tuple{ObjectId,Symbol},Vector{Tuple{Float64,Any}}}() + ordered_applications = _ordered_scene_applications(compiled) for step in 1:steps - for application in compiled.applications + for application in ordered_applications application.id in manual_application_ids && continue _scene_application_should_run(application, step) || continue for object_id in application.target_ids diff --git a/test/test-unified-scene-object-api.jl b/test/test-unified-scene-object-api.jl index d86c64d2d..4887a8254 100644 --- a/test/test-unified-scene-object-api.jl +++ b/test/test-unified-scene-object-api.jl @@ -92,6 +92,30 @@ function PlantSimEngine.run!(::SceneObjectSignalSourceModel, models, status, met return nothing end +struct SceneObjectParameterizedSignalModel{T} <: AbstractScene_Object_Signal_SourceModel + increment::T +end + +PlantSimEngine.inputs_(::SceneObjectParameterizedSignalModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectParameterizedSignalModel) = (signal=0.0,) + +function PlantSimEngine.run!(model::SceneObjectParameterizedSignalModel, models, status, meteo, constants=nothing, extra=nothing) + status.signal += model.increment + return nothing +end + +PlantSimEngine.@process "scene_object_plant_signal_sum" verbose = false + +struct SceneObjectPlantSignalSumModel <: AbstractScene_Object_Plant_Signal_SumModel end + +PlantSimEngine.inputs_(::SceneObjectPlantSignalSumModel) = (signals=[0.0],) +PlantSimEngine.outputs_(::SceneObjectPlantSignalSumModel) = (signal_total=0.0,) + +function PlantSimEngine.run!(::SceneObjectPlantSignalSumModel, models, status, meteo, constants=nothing, extra=nothing) + status.signal_total = sum(status.signals) + return nothing +end + PlantSimEngine.@process "scene_object_signal_caller" verbose = false struct SceneObjectSignalCallerModel <: AbstractScene_Object_Signal_CallerModel end @@ -121,6 +145,20 @@ function PlantSimEngine.run!(::SceneObjectSignalConsumerModel, models, status, m return nothing end +PlantSimEngine.@process "scene_object_cycle_a" verbose = false + +struct SceneObjectCycleAModel <: AbstractScene_Object_Cycle_AModel end + +PlantSimEngine.inputs_(::SceneObjectCycleAModel) = (cycle_b=0.0,) +PlantSimEngine.outputs_(::SceneObjectCycleAModel) = (cycle_a=0.0,) + +PlantSimEngine.@process "scene_object_cycle_b" verbose = false + +struct SceneObjectCycleBModel <: AbstractScene_Object_Cycle_BModel end + +PlantSimEngine.inputs_(::SceneObjectCycleBModel) = (cycle_a=0.0,) +PlantSimEngine.outputs_(::SceneObjectCycleBModel) = (cycle_b=0.0,) + PlantSimEngine.@process "scene_object_temporal_sum" verbose = false struct SceneObjectTemporalSumModel <: AbstractScene_Object_Temporal_SumModel end @@ -351,6 +389,273 @@ end @test resolve_object_ids(selector_scene, Many(scale=:Leaf); context=:plant_1) == [ObjectId(:leaf_1), ObjectId(:leaf_2), ObjectId(:leaf_3)] + shared_signal_model = SceneObjectParameterizedSignalModel(1.0) + shared_template_parameters = Dict(:signal_increment => 1.0) + plant_template = ObjectTemplate( + ( + ModelSpec(shared_signal_model; name=:signal_source) |> + AppliesTo(Many(scale=:Leaf)), + ModelSpec(SceneObjectPlantSignalSumModel(); name=:plant_total) |> + AppliesTo(One(scale=:Plant)) |> + Inputs( + :signals => Many( + scale=:Leaf, + within=Self(), + process=:scene_object_signal_source, + var=:signal, + ), + ), + ); + kind=:plant, + species=:oil_palm, + parameters=shared_template_parameters, + ) + palm_1_leaf_override = SceneObjectParameterizedSignalModel(3.0) + palm_1 = ObjectInstance( + :palm_1, + plant_template; + root=Object(:templated_plant_1; scale=:Plant, parent=:scene, status=Status(signals=[0.0], signal_total=0.0)), + objects=( + Object(:templated_leaf_1; scale=:Leaf, parent=:templated_plant_1, status=Status(signal=0.0)), + Object(:templated_leaf_1_exception; scale=:Leaf, parent=:templated_plant_1, status=Status(signal=0.0)), + ), + object_overrides=( + Override( + object=:templated_leaf_1_exception, + application=:signal_source, + model=palm_1_leaf_override, + ), + ), + ) + palm_2_override = SceneObjectParameterizedSignalModel(2.0) + palm_2 = ObjectInstance( + :palm_2, + plant_template; + root=Object(:templated_plant_2; scale=:Plant, parent=:scene, status=Status(signals=[0.0], signal_total=0.0)), + objects=(Object(:templated_leaf_2; scale=:Leaf, parent=:templated_plant_2, status=Status(signal=0.0)),), + overrides=(scene_object_signal_source=palm_2_override,), + ) + palm_3 = ObjectInstance( + :palm_3, + plant_template; + root=Object(:templated_plant_3; scale=:Plant, parent=:scene, status=Status(signals=[0.0], signal_total=0.0)), + objects=(Object(:templated_leaf_3; scale=:Leaf, parent=:templated_plant_3, status=Status(signal=0.0)),), + ) + templated_plant_4 = Object( + :templated_plant_4; + scale=:Plant, + parent=:scene, + status=Status(signals=[0.0], signal_total=0.0), + ) + templated_leaf_4 = Object( + :templated_leaf_4; + scale=:Leaf, + parent=:templated_plant_4, + status=Status(signal=0.0), + ) + palm_4 = ObjectInstance(:palm_4, plant_template; root=:templated_plant_4) + template_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + templated_plant_4, + templated_leaf_4, + palm_1, + palm_2, + palm_3; + instances=(palm_4,), + ) + @test length(template_scene.applications) == 8 + @test plant_template.parameters === shared_template_parameters + @test only(scene_objects(template_scene; name=:palm_1)).id == ObjectId(:templated_plant_1) + @test object_ids(template_scene; species=:oil_palm) == [ + ObjectId(:templated_leaf_1), + ObjectId(:templated_leaf_1_exception), + ObjectId(:templated_leaf_2), + ObjectId(:templated_leaf_3), + ObjectId(:templated_leaf_4), + ObjectId(:templated_plant_1), + ObjectId(:templated_plant_2), + ObjectId(:templated_plant_3), + ObjectId(:templated_plant_4), + ] + template_compiled = compile_scene(template_scene) + template_application_rows = explain_scene_applications(template_compiled) + @test only(row for row in template_application_rows if row.application_id == :palm_1__signal_source).target_ids == + [:templated_leaf_1, :templated_leaf_1_exception] + @test only(row for row in template_application_rows if row.application_id == :palm_2__signal_source).target_ids == + [:templated_leaf_2] + @test only(row for row in template_application_rows if row.application_id == :palm_3__plant_total).target_ids == + [:templated_plant_3] + palm_1_signal_row = only( + row for row in template_application_rows + if row.application_id == :palm_1__signal_source + ) + @test palm_1_signal_row.model_type == typeof(shared_signal_model) + @test palm_1_signal_row.model_storage == :per_object_override + @test palm_1_signal_row.model_dispatch == :concrete_per_object + @test palm_1_signal_row.object_overrides == [ + ( + object_id=:templated_leaf_1_exception, + model_type=typeof(palm_1_leaf_override), + ), + ] + @test (@inferred PlantSimEngine._application_model( + template_compiled.applications_by_id[:palm_1__signal_source], + ObjectId(:templated_leaf_1), + )) === shared_signal_model + @test (@inferred PlantSimEngine._application_model( + template_compiled.applications_by_id[:palm_1__signal_source], + ObjectId(:templated_leaf_1_exception), + )) === palm_1_leaf_override + @test PlantSimEngine.model_(template_compiled.applications_by_id[:palm_3__signal_source].spec) === shared_signal_model + @test PlantSimEngine.model_(template_compiled.applications_by_id[:palm_4__signal_source].spec) === shared_signal_model + @test PlantSimEngine.model_(template_compiled.applications_by_id[:palm_2__signal_source].spec) === palm_2_override + template_instance_rows = explain_instances(template_scene) + palm_1_instance_row = only(row for row in template_instance_rows if row.name == :palm_1) + @test palm_1_instance_row.root_id == :templated_plant_1 + @test palm_1_instance_row.object_ids == + [:templated_leaf_1, :templated_leaf_1_exception, :templated_plant_1] + @test palm_1_instance_row.application_ids == + [:palm_1__plant_total, :palm_1__signal_source] + @test palm_1_instance_row.object_overrides == [ + ( + object_id=:templated_leaf_1_exception, + process=nothing, + application=:signal_source, + model_type=typeof(palm_1_leaf_override), + ), + ] + @test palm_1_instance_row.parameters_shared_by_reference + @test only(row for row in explain_objects(template_scene) if row.id == :templated_leaf_1).instance == + :palm_1 + run!(template_scene; steps=1) + @test only(scene_objects(template_scene; name=:palm_1)).status.signal_total == 4.0 + @test only(scene_objects(template_scene; name=:palm_2)).status.signal_total == 2.0 + @test only(scene_objects(template_scene; name=:palm_3)).status.signal_total == 1.0 + @test only(scene_objects(template_scene; name=:palm_4)).status.signal_total == 1.0 + registered_template_leaf = register_object!( + template_scene, + Object(:templated_leaf_new; scale=:Leaf, status=Status(signal=0.0)); + parent=:templated_plant_2, + ) + @test registered_template_leaf.kind == :plant + @test registered_template_leaf.species == :oil_palm + @test :templated_leaf_new in only( + row.object_ids for row in explain_instances(template_scene) + if row.name == :palm_2 + ) + refreshed_template = refresh_bindings!(template_scene) + @test ObjectId(:templated_leaf_new) in + refreshed_template.applications_by_id[:palm_2__signal_source].target_ids + remove_object!(template_scene, :templated_leaf_new) + @test_throws ErrorException Scene( + Object(:scene; scale=:Scene, kind=:scene), + ObjectInstance( + :invalid_palm, + plant_template; + root=Object(:invalid_plant; scale=:Plant, parent=:scene), + overrides=(missing_process=shared_signal_model,), + ), + ) + @test_throws ErrorException Scene( + Object(:scene; scale=:Scene, kind=:scene), + ObjectInstance( + :invalid_palm, + plant_template; + root=Object(:invalid_plant; scale=:Plant, parent=:scene), + overrides=(signal_source=Process1Model(1.0),), + ), + ) + @test_throws ErrorException Scene( + Object(:scene; scale=:Scene, kind=:scene), + ObjectInstance( + :invalid_palm, + plant_template; + root=Object(:invalid_plant; scale=:Plant, parent=:scene), + objects=(Object(:invalid_leaf; scale=:Leaf, parent=:invalid_plant),), + object_overrides=( + Override( + object=:outside_instance, + application=:signal_source, + model=shared_signal_model, + ), + ), + ), + ) + @test_throws ErrorException Scene( + Object(:scene; scale=:Scene, kind=:scene), + ObjectInstance( + :invalid_palm, + plant_template; + root=Object(:invalid_plant; scale=:Plant, parent=:scene), + objects=(Object(:invalid_leaf; scale=:Leaf, parent=:invalid_plant),), + object_overrides=( + Override( + object=:invalid_leaf, + application=:signal_source, + model=shared_signal_model, + ), + Override( + object=:invalid_leaf, + process=:scene_object_signal_source, + model=shared_signal_model, + ), + ), + ), + ) + unmatched_override_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + ObjectInstance( + :invalid_palm, + plant_template; + root=Object(:invalid_plant; scale=:Plant, parent=:scene), + objects=(Object(:invalid_leaf; scale=:Leaf, parent=:invalid_plant, status=Status(signal=0.0)),), + object_overrides=( + Override( + object=:invalid_plant, + application=:signal_source, + model=shared_signal_model, + ), + ), + ), + ) + @test_throws ErrorException compile_scene(unmatched_override_scene) + + call_template = ObjectTemplate( + ( + ModelSpec(shared_signal_model; name=:signal_source) |> + AppliesTo(Many(scale=:Leaf)), + ModelSpec(SceneObjectSignalCallerModel(); name=:signal_caller) |> + AppliesTo(Many(scale=:Leaf)), + ); + kind=:plant, + species=:oil_palm, + ) + call_override_model = SceneObjectParameterizedSignalModel(4.0) + call_override_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + ObjectInstance( + :call_palm, + call_template; + root=Object(:call_plant; scale=:Plant, parent=:scene), + objects=( + Object(:call_leaf_1; scale=:Leaf, parent=:call_plant, status=Status(signal=0.0, called_signal=0.0)), + Object(:call_leaf_2; scale=:Leaf, parent=:call_plant, status=Status(signal=0.0, called_signal=0.0)), + ), + object_overrides=( + Override( + object=:call_leaf_2, + application=:signal_source, + model=call_override_model, + ), + ), + ), + ) + run!(call_override_scene) + call_leaf_1 = only(object for object in scene_objects(call_override_scene; scale=:Leaf) if object.id == ObjectId(:call_leaf_1)) + call_leaf_2 = only(object for object in scene_objects(call_override_scene; scale=:Leaf) if object.id == ObjectId(:call_leaf_2)) + @test call_leaf_1.status.called_signal == 1.0 + @test call_leaf_2.status.called_signal == 4.0 + leaf_selector = Many( kind="plant", scale=:Leaf, @@ -548,6 +853,9 @@ end disambiguated_call = only(row for row in explain_calls(disambiguated) if row.consumer_id == :leaf_2) @test disambiguated_call.callee_application_ids == [:sunlit_stomata] @test disambiguated_call.application == :sunlit_stomata + leaf_2_call_bindings = disambiguated.call_bindings_by_target[(:leaf_energy, ObjectId(:leaf_2))] + @test length(leaf_2_call_bindings) == 1 + @test only(leaf_2_call_bindings).call == :stomata default_scope_scene = Scene( Object(:scene; scale=:Scene, kind=:scene, status=Status(signal_sum=0.0, temporal_total=0.0)), @@ -609,6 +917,35 @@ end run!(inferred_input_scene_with_apps) @test only(scene_objects(inferred_input_scene_with_apps; scale=:Leaf)).status.observed_signal == 1.0 + reversed_dependency_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0, observed_signal=0.0)); + applications=reverse(inferred_input_specs), + ) + reversed_compiled = refresh_bindings!(reversed_dependency_scene) + @test length(reversed_compiled.applications_by_id) == length(reversed_compiled.applications) + @test reversed_compiled.applications_by_id[:signal_source].process == :scene_object_signal_source + @test reversed_compiled.applications_by_id[:signal_consumer].process == :scene_object_signal_consumer + @test reversed_compiled.application_order == [:signal_source, :signal_consumer] + @test [row.application_id for row in explain_schedule(reversed_compiled)] == + [:signal_source, :signal_consumer] + @test [row.execution_index for row in explain_schedule(reversed_compiled)] == [1, 2] + run!(reversed_dependency_scene) + @test only(scene_objects(reversed_dependency_scene; scale=:Leaf)).status.observed_signal == 1.0 + + @test_throws ErrorException compile_scene( + Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(cycle_a=0.0, cycle_b=0.0)), + ), + ( + ModelSpec(SceneObjectCycleAModel(); name=:cycle_a) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(SceneObjectCycleBModel(); name=:cycle_b) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + @test_throws ErrorException compile_scene( Scene( Object(:scene; scale=:Scene, kind=:scene), @@ -692,6 +1029,10 @@ end ) carrier_compiled = compile_scene(carrier_scene, carrier_specs) carrier_rows = explain_bindings(carrier_compiled) + leaf_1_carrier_bindings = carrier_compiled.input_bindings_by_target[(:carrier_consumer, ObjectId(:leaf_1))] + @test length(leaf_1_carrier_bindings) == 2 + @test Set(binding.input for binding in leaf_1_carrier_bindings) == Set((:leaf_areas, :leaf_tokens)) + @test length(carrier_compiled.input_bindings_by_target[(:assim, ObjectId(:leaf_1))]) == 1 leaf_area_binding = only( binding for binding in carrier_compiled.input_bindings if binding.application_id == :carrier_consumer && binding.consumer_id == ObjectId(:leaf_1) && binding.input == :leaf_areas @@ -800,6 +1141,9 @@ end @test compiled_environment isa CompiledEnvironmentBindings @test !environment_bindings_dirty(environment_scene) @test compiled_environment_bindings(environment_scene) === compiled_environment + @test length(compiled_environment.by_target) == length(compiled_environment.bindings) + @test compiled_environment.by_target[(:probe, ObjectId(:leaf_1))].cell == :cell_a + @test compiled_environment.by_target[(:temperature_update, ObjectId(:leaf_2))].cell == :cell_b @test length(grid_backend.index_updates) == 1 @test any(entity -> entity.id == :leaf_1 && entity.geometry == (cell=:cell_a,), grid_backend.index_updates[1]) @test any(entity -> entity.id == :plant_1 && entity.scale == :Plant, grid_backend.index_updates[1]) @@ -910,6 +1254,26 @@ end @test !only(row for row in call_schedule if row.application_id == :signal_source).root_scheduled @test only(row for row in call_schedule if row.application_id == :signal_caller).root_scheduled + hard_call_order_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0, called_signal=0.0, observed_signal=0.0)); + applications=( + ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_consumer) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(SceneObjectSignalSourceModel(); name=:signal_source) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(SceneObjectSignalCallerModel(); name=:signal_caller) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + hard_call_order = refresh_bindings!(hard_call_order_scene) + @test hard_call_order.applications_by_id[:signal_caller].process == :scene_object_signal_caller + @test hard_call_order.application_order == [:signal_source, :signal_caller, :signal_consumer] + run!(hard_call_order_scene) + hard_call_order_status = only(scene_objects(hard_call_order_scene; scale=:Leaf)).status + @test hard_call_order_status.signal == 1.0 + @test hard_call_order_status.observed_signal == 1.0 + temporal_input_scene = Scene( Object(:scene; scale=:Scene, kind=:scene, status=Status(signal_sum=0.0, temporal_total=0.0)), Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0)); From 42106a315a547ef6c120e220622c345968156d7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Sat, 13 Jun 2026 14:54:45 +0200 Subject: [PATCH 19/33] Implements the goal of multi-plants and spatial (mutable) microclimate --- CHANGELOG.md | 18 +- README.md | 458 ++-- benchmark/test-PSE-benchmark.jl | 2 +- benchmark/test-multirate-buffer-benchmark.jl | 2 +- benchmark/test-plantbiophysics.jl | 8 +- docs/make.jl | 16 +- docs/src/API/API_public.md | 206 +- docs/src/FAQ/translate_a_model.md | 2 +- docs/src/agent_skill.md | 28 +- docs/src/dev/code_cleanup_audit.md | 6 +- docs/src/dev/maespa_domain_handoff.md | 116 +- docs/src/dev/release_notes_handoff.md | 279 +- .../unified_scene_object_completion_audit.md | 100 + docs/src/dev/unified_scene_object_design.md | 110 +- ...nified_scene_object_implementation_plan.md | 422 ++- docs/src/domain_simulation.md | 89 +- docs/src/index.md | 464 ++-- docs/src/migration_scene_object.md | 481 ++++ .../src/model_coupling/model_coupling_user.md | 12 +- docs/src/model_execution.md | 459 ++-- docs/src/model_traits.md | 16 +- docs/src/multirate/advanced_configuration.md | 44 +- docs/src/multirate/introduction.md | 26 +- docs/src/multirate/multirate_tutorial.md | 45 +- docs/src/multiscale/multiscale.md | 29 +- .../multiscale/multiscale_considerations.md | 18 +- docs/src/multiscale/multiscale_coupling.md | 13 +- docs/src/multiscale/multiscale_cyclic.md | 24 +- docs/src/multiscale/multiscale_example_1.md | 8 +- docs/src/multiscale/multiscale_example_2.md | 8 +- docs/src/multiscale/multiscale_example_3.md | 20 +- docs/src/multiscale/single_to_multiscale.md | 28 +- .../installing_plantsimengine.md | 2 +- docs/src/scene_object/quickstart.md | 254 ++ docs/src/step_by_step/advanced_coupling.md | 170 +- .../step_by_step/detailed_first_example.md | 360 +-- docs/src/step_by_step/implement_a_model.md | 18 +- docs/src/step_by_step/model_switching.md | 174 +- .../step_by_step/quick_and_dirty_examples.md | 189 +- .../src/step_by_step/simple_model_coupling.md | 210 +- .../implicit_contracts.md | 4 +- ...lantsimengine_and_julia_troubleshooting.md | 48 +- .../tips_and_workarounds.md | 6 +- docs/src/working_with_data/fitting.md | 4 +- .../floating_point_accumulation_error.md | 12 +- docs/src/working_with_data/inputs.md | 21 +- docs/src/working_with_data/reducing_dof.md | 6 +- .../working_with_data/visualising_outputs.md | 4 +- examples/Beer.jl | 4 +- .../ToyPlantSimulation1.jl | 6 +- .../ToyPlantSimulation2.jl | 8 +- .../ToyPlantSimulation3.jl | 6 +- examples/ToySingleToMultiScale.jl | 8 +- examples/benchmark.jl | 4 +- examples/maespa_domain_example.jl | 223 +- .../plantbiophysics_subsample/Monteith.jl | 4 +- examples/plantbiophysics_subsample/Tuzet.jl | 2 +- skills/plantsimengine/SKILL.md | 291 ++- src/PlantSimEngine.jl | 14 +- src/checks/dimensions.jl | 2 +- src/component_models/SingleScaleModelSet.jl | 2 +- src/component_models/get_status.jl | 2 +- src/dataframe.jl | 8 +- src/dependencies/dependencies.jl | 4 +- src/doc_templates/mtg-related.jl | 8 +- src/domains/domain_simulation.jl | 12 + src/evaluation/fit.jl | 2 +- src/mtg/ModelSpec.jl | 74 +- src/mtg/mapping/getters.jl | 4 +- src/mtg/mapping/reverse_mapping.jl | 6 +- src/mtg/model_spec_inference.jl | 8 + src/mtg/save_results.jl | 8 +- src/processes/model_initialisation.jl | 18 +- src/processes/models_inputs_outputs.jl | 4 +- src/run.jl | 2 +- src/scene_object_api.jl | 2113 ++++++++++++++- src/time/runtime/environment_backends.jl | 77 +- src/time/runtime/output_export.jl | 30 +- test/helper-functions.jl | 82 +- test/test-ModelMapping.jl | 26 +- test/test-MultiScaleModel.jl | 18 +- test/test-Status.jl | 2 +- test/test-corner-cases.jl | 24 +- test/test-dimensions.jl | 4 +- test/test-domain-simulation.jl | 594 ++--- test/test-environment-backends.jl | 70 +- test/test-fitting.jl | 2 +- test/test-maespa-domain-example.jl | 142 +- test/test-mapping.jl | 44 +- test/test-meteo-traits.jl | 4 +- test/test-mtg-dynamic.jl | 56 +- test/test-mtg-multiscale-cyclic-dep.jl | 40 +- test/test-mtg-multiscale.jl | 66 +- test/test-multirate-output-export.jl | 59 +- test/test-multirate-runtime.jl | 302 +-- test/test-multirate-scaffolding.jl | 10 +- test/test-performance.jl | 6 +- test/test-simulation.jl | 28 +- test/test-toy_models.jl | 30 +- test/test-unified-scene-object-api.jl | 2300 ++++++++++++++++- test/test-updates.jl | 8 +- 101 files changed, 9296 insertions(+), 2614 deletions(-) create mode 100644 docs/src/dev/unified_scene_object_completion_audit.md create mode 100644 docs/src/migration_scene_object.md create mode 100644 docs/src/scene_object/quickstart.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ac4e3652..10a9f363a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,7 +21,7 @@ and substantially expands the documentation. The main user-facing breaking change in this release is the move toward `Symbol`-based scale names in mappings and multi-scale configuration. Code that still uses string scales such as `"Leaf"` or `"Plant"` should be updated to use -symbols such as `:Leaf` and `:Plant`, especially in `ModelMapping(...)`, +symbols such as `:Leaf` and `:Plant`, especially in `PlantSimEngine.ModelMapping(...)`, `MultiScaleModel(...)`, and explicit multi-rate bindings. `ModelList` is also on the deprecation path in favor of `ModelMapping`, so this release is a good time to migrate mapping code to the newer API. @@ -77,12 +77,12 @@ to migrate mapping code to the newer API. ### Deprecated -- `run!(::ModelList, ...)` is deprecated. Use `run!(ModelMapping(...), ...)` +- `run!(::ModelList, ...)` is deprecated. Use `run!(PlantSimEngine.ModelMapping(...), ...)` instead. - `run!` with collections of `ModelList` is deprecated. Use collections of `ModelMapping` instead. - `run!(mtg, mapping::AbstractDict, ...)` is deprecated. Construct a - `ModelMapping(...)` first, or call `run!(mtg, ModelMapping(mapping), ...)`. + `PlantSimEngine.ModelMapping(...)` first, or call `run!(mtg, PlantSimEngine.ModelMapping(mapping), ...)`. - String scale names are deprecated in multi-scale mapping APIs. Use `Symbol` scales such as `:Leaf` instead of `"Leaf"`. - `ModelList` remains available for now but is being phased out in favor of @@ -93,7 +93,7 @@ to migrate mapping code to the newer API. #### 1. Replace ad hoc mappings with `ModelMapping` If you previously used `ModelList(...)` directly for single-scale runs, or a -plain `Dict` for MTG runs, migrate to `ModelMapping(...)`. +plain `Dict` for MTG runs, migrate to `PlantSimEngine.ModelMapping(...)`. Before: @@ -113,13 +113,13 @@ mapping = Dict( After: ```julia -leaf = ModelMapping( +leaf = PlantSimEngine.ModelMapping( process1 = Process1Model(), process2 = Process2Model(), status = (x = 1.0,), ) -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Leaf => (ToyAssimModel(),), :Plant => (ToyGrowthModel(),), ) @@ -133,7 +133,7 @@ When a model should run at a cadence different from the meteo, wrap it in Typical pattern: ```julia -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(HourlyLeafModel()) |> TimeStepModel(1.0), ), @@ -180,7 +180,7 @@ If your mappings still use string scales, migrate them to symbols. Before: ```julia -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( "Leaf" => (ToyAssimModel(),), ) @@ -191,7 +191,7 @@ MultiScaleModel([:A => "Leaf"]) After: ```julia -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Leaf => (ToyAssimModel(),), ) diff --git a/README.md b/README.md index 58958f4ec..8789a4ac5 100644 --- a/README.md +++ b/README.md @@ -9,369 +9,225 @@ [![DOI](https://zenodo.org/badge/571659510.svg)](https://zenodo.org/badge/latestdoi/571659510) [![JOSS](https://joss.theoj.org/papers/137e3e6c2ddc349bec39e06bb04e4e09/status.svg)](https://joss.theoj.org/papers/137e3e6c2ddc349bec39e06bb04e4e09) -- [PlantSimEngine](#plantsimengine) - - [Overview](#overview) - - [Unique Features](#unique-features) - - [Automatic Model Coupling](#automatic-model-coupling) - - [Flexibility with Precision Control](#flexibility-with-precision-control) - - [Multi-rate Execution](#multi-rate-execution) - - [Batteries included](#batteries-included) - - [Ask Questions](#ask-questions) - - [Installation](#installation) - - [Example usage](#example-usage) - - [Simple example](#simple-example) - - [Model coupling](#model-coupling) - - [Multiscale modelling](#multiscale-modelling) - - [Multi-rate modelling](#multi-rate-modelling) - - [Projects that use PlantSimEngine](#projects-that-use-plantsimengine) - - [Performance](#performance) - - [Make it yours](#make-it-yours) +PlantSimEngine is a Julia framework for composing soil-plant-atmosphere +simulations from reusable process models. -## Overview +A modeler writes generic kernels with: -`PlantSimEngine` is a comprehensive framework for building models of the soil-plant-atmosphere continuum. It includes everything you need to **prototype, evaluate, test, and deploy** plant/crop models at any scale, with a strong emphasis on performance and efficiency, so you can focus on building and refining your models. +- `inputs_` +- `outputs_` +- optional `dep`, `timespec`, `output_policy`, `meteo_inputs_`, and + `meteo_outputs_` traits +- `run!(model, models, status, meteo, constants, extra)` -**Why choose PlantSimEngine?** +A simulation author assembles those kernels on objects in a scene with: -- **Simplicity**: Write less code, focus on your model's logic, and let the framework handle the rest. -- **Modularity**: Each model component can be developed, tested, and improved independently. Assemble complex simulations by reusing pre-built, high-quality modules. -- **Standardisation**: Clear, enforceable guidelines ensure that all models adhere to best practices. This built-in consistency means that once you implement a model, it works seamlessly with others in the ecosystem. -- **Optimised Performance**: Don't re-invent the wheel. Delegating low-level tasks to PlantSimEngine guarantees that your model will benefit from every improvement in the framework. Enjoy faster prototyping, robust simulations, and efficient execution using Julia's high-performance capabilities. - -## Unique Features - -### Automatic Model Coupling - -**Seamless Integration:** PlantSimEngine leverages Julia's multiple-dispatch capabilities to automatically compute the dependency graph between models. This allows researchers to effortlessly couple models without writing complex connection code or manually managing dependencies. - -**Intuitive Multi-Scale Support:** The framework naturally handles models operating at different scales—from organelle to ecosystem—connecting them with minimal effort and maintaining consistency across scales. - -### Flexibility with Precision Control - -**Effortless Model Switching:** Researchers can switch between different component models using a simple syntax without rewriting the underlying model code. This enables rapid comparison between different hypotheses and model versions, accelerating the scientific discovery process. - -### Multi-rate Execution - -**Mix model cadences in one simulation:** PlantSimEngine can run models at different timesteps within the same MTG simulation. This makes it possible to combine, for example, hourly leaf processes with daily plant balances and weekly reporting models without writing custom scheduling glue. - -**Explicit bindings between rates:** `TimeStepModel`, `InputBindings`, `MeteoBindings`, `ScopeModel`, and `OutputRequest` let you declare how model inputs, meteorology, and exported outputs should behave when rates differ. - -## Batteries included - -- **Automated Management**: Seamlessly handle inputs, outputs, time-steps, objects, and dependency resolution. -- **Iterative Development**: Fast and interactive prototyping of models with built-in constraints to avoid errors and sensible defaults to streamline the model writing process. -- **Control Your Degrees of Freedom**: Fix variables to constant values or force to observations, use simpler models for specific processes to reduce complexity. -- **Multi-Rate Scheduling**: Combine hourly, daily, and coarser models in the same simulation, with explicit policies for input aggregation and meteorological sampling. -- **High-Speed Computations**: Achieve impressive performance with benchmarks showing operations in the 100th of nanoseconds range for complex models (see this [benchmark script](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/examples/benchmark.jl)). -- **Parallelize and Distribute Computing**: Out-of-the-box support for sequential, multi-threaded, or distributed computations over objects, time-steps, and independent processes, thanks to [Floops.jl](https://juliafolds.github.io/FLoops.jl/stable/). -- **Scale Effortlessly**: Methods for computing over objects, time-steps, and [Multi-Scale Tree Graphs](https://github.com/VEZY/MultiScaleTreeGraph.jl). -- **Compose Freely**: Use any types as inputs, including [Unitful](https://github.com/PainterQubits/Unitful.jl) for unit propagation and [MonteCarloMeasurements.jl](https://github.com/baggepinnen/MonteCarloMeasurements.jl) for measurement error propagation. - -## Ask Questions - -If you have any questions or feedback, [open an issue](https://github.com/VirtualPlantLab/PlantSimEngine.jl/issues) or ask on [discourse](https://fspm.discourse.group/c/software/virtual-plant-lab). +```julia +Scene +Object +ModelSpec +AppliesTo +Inputs +Calls +Updates +TimeStep +Environment +``` + +This API is the primary path for new multiscale, multi-plant, soil, +microclimate, and scene-scale simulations. Older `ModelMapping`, +`MultiScaleModel`, domain, and route APIs are retained only as qualified +compatibility tools while existing examples are migrated. ## Installation -To install the package, enter the Julia package manager mode by pressing `]` in the REPL, and execute the following command: +In Julia package mode: ```julia add PlantSimEngine ``` -To use the package, execute this command from the Julia REPL: +Then: ```julia using PlantSimEngine ``` -## Example usage - -The package is designed to be easy to use, and to help users avoid errors when implementing, coupling and simulating models. +## Quickstart -### Simple example +This example runs three existing toy models on one scene object: -Here's a simple example of a model that simulates the growth of a plant, using a simple exponential growth model: +1. `ToyDegreeDaysCumulModel` computes daily thermal time. +2. `ToyLAIModel` consumes cumulative thermal time and computes LAI. +3. `Beer` consumes LAI and meteorology to compute absorbed PAR. ```julia -# ] add PlantSimEngine -using PlantSimEngine - -# Include the model definition from the examples sub-module: +using PlantSimEngine, PlantMeteo, Dates, DataFrames using PlantSimEngine.Examples -# Define the model: -model = ModelMapping( - ToyLAIModel(), - status=(TT_cu=1.0:2000.0,), # Pass the cumulated degree-days as input to the model +meteo_day = read_weather( + joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"); + duration=Dates.Day, ) -run!(model) # run the model - -status(model) # extract the status, i.e. the output of the model -``` - -Which gives: - -``` -TimeStepTable{Status{(:TT_cu, :LAI...}(1300 x 2): -╭─────┬────────────────┬────────────╮ -│ Row │ TT_cu │ LAI │ -│ │ Float64 │ Float64 │ -├─────┼────────────────┼────────────┤ -│ 1 │ 1.0 │ 0.00560052 │ -│ 2 │ 2.0 │ 0.00565163 │ -│ 3 │ 3.0 │ 0.00570321 │ -│ 4 │ 4.0 │ 0.00575526 │ -│ 5 │ 5.0 │ 0.00580778 │ -│ ⋮ │ ⋮ │ ⋮ │ -╰─────┴────────────────┴────────────╯ - 1295 rows omitted -``` - -> **Note** -> The `ToyLAIModel` is available from the [examples folder](https://github.com/VirtualPlantLab/PlantSimEngine.jl/tree/main/examples), and is a simple exponential growth model. It is used here for the sake of simplicity, but you can use any model you want, as long as it follows `PlantSimEngine` interface. +scene = Scene( + Object(:scene; scale=:Scene, kind=:scene); + applications=( + ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), -Of course you can plot the outputs quite easily: + ModelSpec(ToyLAIModel(); name=:lai) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), -```julia -# ] add CairoMakie -using CairoMakie + ModelSpec(Beer(0.6); name=:light_interception) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ), + environment=meteo_day, +) -lines(model[:TT_cu], model[:LAI], color=:green, axis=(ylabel="LAI (m² m⁻²)", xlabel="Cumulated growing degree days since sowing (°C)")) +sim = run!(scene; steps=30, constants=Constants()) +out = DataFrame(collect_outputs(sim; sink=nothing)) +first(out, 6) ``` -![LAI Growth](examples/LAI_growth.png) - -### Model coupling - -Model coupling is done automatically by the package, and is based on the dependency graph between the models. To couple models, we just have to add them to the `ModelMapping`. For example, let's couple the `ToyLAIModel` with a model for light interception based on Beer's law: +The compiler infers the unambiguous same-object bindings from each model's +declared inputs and outputs: `ToyLAIModel` receives `TT_cu` from +`:degree_days`, and `Beer` receives `LAI` from `:lai`. ```julia -# ] add PlantSimEngine, PlantMeteo, Dates -using PlantSimEngine, PlantMeteo, Dates - -# Include the model definition from the examples folder: -using PlantSimEngine.Examples - -# Import the example meteorological data: -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) - -# Define the list of models for coupling: -model = ModelMapping( - ToyLAIModel(), - Beer(0.6), - status=(TT_cu=cumsum(meteo_day[:, :TT]),), # Pass the cumulated degree-days as input to `ToyLAIModel`, this could also be done using another model +select( + DataFrame(explain_bindings(refresh_bindings!(scene))), + :application_id, + :input, + :source_application_ids, + :carrier_kind, + :copy_semantics, ) ``` -The `ModelMapping` couples the models by automatically computing the dependency graph of the models. The resulting dependency graph is: +## Multi-Object Coupling -``` -╭──── Dependency graph ──────────────────────────────────────────╮ -│ ╭──── LAI_Dynamic ─────────────────────────────────────────╮ │ -│ │ ╭──── Main model ────────╮ │ │ -│ │ │ Process: LAI_Dynamic │ │ │ -│ │ │ Model: ToyLAIModel │ │ │ -│ │ │ Dep: │ │ │ -│ │ ╰────────────────────────╯ │ │ -│ │ │ ╭──── Soft-coupled model ─────────╮ │ │ -│ │ │ │ Process: light_interception │ │ │ -│ │ └──│ Model: Beer │ │ │ -│ │ │ Dep: (LAI_Dynamic = (:LAI,),) │ │ │ -│ │ ╰─────────────────────────────────╯ │ │ -│ ╰──────────────────────────────────────────────────────────╯ │ -╰────────────────────────────────────────────────────────────────╯ -``` +Use `Inputs(...)` when a model needs values from selected objects. This +scene-scale LAI model reads live references to the surface of every plant in +the scene: ```julia -# Run the simulation: -run!(model, meteo_day) - -status(model) -``` - -Which returns: - -``` -TimeStepTable{Status{(:TT_cu, :LAI...}(365 x 3): -╭─────┬────────────────┬────────────┬───────────╮ -│ Row │ TT_cu │ LAI │ aPPFD │ -│ │ Float64 │ Float64 │ Float64 │ -├─────┼────────────────┼────────────┼───────────┤ -│ 1 │ 0.0 │ 0.00554988 │ 0.0476221 │ -│ 2 │ 0.0 │ 0.00554988 │ 0.0260688 │ -│ 3 │ 0.0 │ 0.00554988 │ 0.0377774 │ -│ 4 │ 0.0 │ 0.00554988 │ 0.0468871 │ -│ 5 │ 0.0 │ 0.00554988 │ 0.0545266 │ -│ ⋮ │ ⋮ │ ⋮ │ ⋮ │ -╰─────┴────────────────┴────────────┴───────────╯ - 360 rows omitted -``` - -```julia -# Plot the results: -using CairoMakie - -fig = Figure(resolution=(800, 600)) -ax = Axis(fig[1, 1], ylabel="LAI (m² m⁻²)") -lines!(ax, model[:TT_cu], model[:LAI], color=:mediumseagreen) - -ax2 = Axis(fig[2, 1], xlabel="Cumulated growing degree days since sowing (°C)", ylabel="aPPFD (mol m⁻² d⁻¹)") -lines!(ax2, model[:TT_cu], model[:aPPFD], color=:firebrick1) +plant_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene, + status=Status(surface=12.0)), + Object(:plant_2; scale=:Plant, kind=:plant, parent=:scene, + status=Status(surface=8.0)); + applications=( + ModelSpec(ToyLAIfromLeafAreaModel(100.0); name=:scene_lai) |> + AppliesTo(One(scale=:Scene)) |> + Inputs( + :plant_surfaces => Many( + scale=:Plant, + within=SceneScope(), + var=:surface, + ), + ), + ), +) -fig +run!(plant_scene) +scene_status = only(scene_objects(plant_scene; scale=:Scene)).status +(total_surface=scene_status.total_surface, LAI=scene_status.LAI) ``` -![LAI Growth and light interception](examples/LAI_growth2.png) - -### Multiscale modelling +Use `within=Self()` for plant-local aggregations, for example a plant +allocation model summing only the leaves inside the current plant. Use +`within=SceneScope()` for scene-wide aggregation. -> See the Multi-scale modeling section of the docs for more details. +## Manual Calls -The package is designed to be easily scalable, and can be used to simulate models at different scales. For example, you can simulate a model at the leaf scale, and then couple it with models at any other scale, *e.g.* internode, plant, soil, scene scales. Here's an example of a simple model that simulates plant growth using sub-models operating at different scales: +Use `Calls(...)` when a parent model must directly run selected child models, +for example a scene energy-balance solver that iterates leaf temperatures: ```julia -mapping = ModelMapping( - :Scene => ToyDegreeDaysCumulModel(), - :Plant => ( - MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[ - :TT_cu => :Scene, - ], +ModelSpec(SceneEnergyBalance(); name=:scene_energy) |> + AppliesTo(One(scale=:Scene)) |> + Calls( + :leaf_energy => Many( + kind=:plant, + scale=:Leaf, + within=SceneScope(), + process=:energy_balance, ), - Beer(0.6), - MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => :Soil], + :soil => One( + kind=:soil, + scale=:Soil, + within=SceneScope(), + process=:soil_water, ), - MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - :carbon_demand => [:Leaf, :Internode], - :carbon_allocation => [:Leaf, :Internode] - ], - ), - MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - ), - :Internode => ( - MultiScaleModel( - model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - mapped_variables=[:TT => :Scene,], - ), - MultiScaleModel( - model=ToyInternodeEmergence(TT_emergence=20.0), - mapped_variables=[:TT_cu => :Scene], - ), - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - Status(carbon_biomass=1.0) - ), - :Leaf => ( - MultiScaleModel( - model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - mapped_variables=[:TT => :Scene,], - ), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - Status(carbon_biomass=1.0) - ), - :Soil => ( - ToySoilWaterModel(), - ), -); + ) |> + TimeStep(Hour(1)) ``` -We can import an example plant from the package: +Inside the parent model, `call_targets(extra, :leaf_energy)` returns executable +targets. `run_call!(target; publish=false)` is the default for trial +iterations, and `run_call!(target; publish=true)` publishes the accepted state. -```julia -mtg = import_mtg_example() -``` - -Make a fake meteorological data: - -```julia -meteo = Weather( - [ - Atmosphere(T=20.0, Wind=1.0, Rh=0.65, Ri_PAR_f=300.0), - Atmosphere(T=25.0, Wind=0.5, Rh=0.8, Ri_PAR_f=500.0) -] -); -``` - -And run the simulation: - -```julia -out_vars = ModelMapping( - :Scene => (:TT_cu,), - :Plant => (:carbon_allocation, :carbon_assimilation, :soil_water_content, :aPPFD, :TT_cu, :LAI), - :Leaf => (:carbon_demand, :carbon_allocation), - :Internode => (:carbon_demand, :carbon_allocation), - :Soil => (:soil_water_content,), -) +## What PlantSimEngine Handles -out = run!(mtg, mapping, meteo, outputs=out_vars, executor=SequentialEx()); -``` +- object graphs with arbitrary plant architecture; +- several plant species and repeated plant instances through templates; +- same-rate reference wiring and typed many-object carriers; +- multirate scheduling with `Dates.Period` values; +- temporal policies such as `HoldLast`, `Interpolate`, `Integrate`, and + `Aggregate`; +- automatic global or spatial environment binding; +- mutable microclimate outputs through `meteo_outputs_`; +- growth, pruning, reparenting, and movement with binding-cache refresh; +- structured explanations for users and agents. -We can then extract the outputs in a `DataFrame` and sort them: +Useful inspection helpers include: ```julia -using DataFrames -df_out = convert_outputs(out, DataFrame) -sort!(df_out, [:timestep, :node]) +explain_objects(scene) +explain_scopes(scene) +explain_bindings(refresh_bindings!(scene)) +explain_calls(refresh_bindings!(scene)) +explain_environment_bindings(scene) +explain_schedule(refresh_bindings!(scene)) +explain_execution_plan(scene) ``` -| **timestep**
`Int64` | **organ**
`String` | **node**
`Int64` | **carbon\_allocation**
`U{Nothing, Float64}` | **TT\_cu**
`U{Nothing, Float64}` | **carbon\_assimilation**
`U{Nothing, Float64}` | **aPPFD**
`U{Nothing, Float64}` | **LAI**
`U{Nothing, Float64}` | **soil\_water\_content**
`U{Nothing, Float64}` | **carbon\_demand**
`U{Nothing, Float64}` | -|------------------------:|----------------------:|--------------------:|-----------------------------------------------------------------------------------:|------------------------------------:|--------------------------------------------------:|-----------------------------------:|---------------------------------:|--------------------------------------------------:|--------------------------------------------:| -| 1 | Scene | 1 | | 10.0 | | | | | | -| 1 | Soil | 2 | | | | | | 0.3 | | -| 1 | Plant | 3 | | 10.0 | 0.299422 | 4.99037 | 0.00607765 | 0.3 | | -| 1 | Internode | 4 | 0.0742793 | | | | | | 0.5 | -| 1 | Leaf | 5 | 0.0742793 | | | | | | 0.5 | -| 1 | Internode | 6 | 0.0742793 | | | | | | 0.5 | -| 1 | Leaf | 7 | 0.0742793 | | | | | | 0.5 | -| 2 | Scene | 1 | | 25.0 | | | | | | -| 2 | Soil | 2 | | | | | | 0.2 | | -| 2 | Plant | 3 | | 25.0 | 0.381154 | 9.52884 | 0.00696482 | 0.2 | | -| 2 | Internode | 4 | 0.0627036 | | | | | | 0.75 | -| 2 | Leaf | 5 | 0.0627036 | | | | | | 0.75 | -| 2 | Internode | 6 | 0.0627036 | | | | | | 0.75 | -| 2 | Leaf | 7 | 0.0627036 | | | | | | 0.75 | -| 2 | Internode | 8 | 0.0627036 | | | | | | 0.75 | -| 2 | Leaf | 9 | 0.0627036 | | | | | | 0.75 | - -An example output of a multiscale simulation is shown in the documentation of PlantBiophysics.jl: - -![Plant growth simulation](docs/src/www/image.png) - -### Multi-rate modelling +## Documentation -PlantSimEngine also supports multi-rate MTG simulations, where different models run at different cadences inside the same execution. A typical use case is to run leaf-scale processes hourly, aggregate them into daily plant-scale balances, and then export weekly summary series from the same simulation. +- [Stable documentation](https://VirtualPlantLab.github.io/PlantSimEngine.jl/stable) +- [Development documentation](https://VirtualPlantLab.github.io/PlantSimEngine.jl/dev) +- [Scene/object quickstart](https://VirtualPlantLab.github.io/PlantSimEngine.jl/dev/scene_object/quickstart/) +- [Scene/object migration guide](https://VirtualPlantLab.github.io/PlantSimEngine.jl/dev/migration_scene_object/) +- [Public API reference](https://VirtualPlantLab.github.io/PlantSimEngine.jl/dev/API/API_public/) -The dedicated documentation now has three pages: a short introduction to the -core ideas, a fuller step-by-step tutorial, and an advanced configuration page: +## Projects That Use PlantSimEngine -- [Introduction to multi-rate execution](https://VirtualPlantLab.github.io/PlantSimEngine.jl/stable/multirate/introduction/) -- [Step-by-step hourly, daily, weekly simulation](https://VirtualPlantLab.github.io/PlantSimEngine.jl/stable/multirate/multirate_tutorial/) -- [Advanced multi-rate configuration](https://VirtualPlantLab.github.io/PlantSimEngine.jl/stable/multirate/advanced_configuration/) - -## Projects that use PlantSimEngine - -Take a look at these projects that use PlantSimEngine: - -- [PlantBiophysics.jl](https://github.com/VEZY/PlantBiophysics.jl) - For the simulation of biophysical processes for plants such as photosynthesis, conductance, energy fluxes, and temperature -- [XPalm](https://github.com/PalmStudio/XPalm.jl) - An experimental crop model for oil palm +- [PlantBiophysics.jl](https://github.com/VEZY/PlantBiophysics.jl) for + plant biophysical processes such as photosynthesis, conductance, energy + fluxes, and temperature. +- [XPalm](https://github.com/PalmStudio/XPalm.jl), an experimental crop model + for oil palm. ## Performance -PlantSimEngine delivers impressive performance for plant modeling tasks. On an M1 MacBook Pro, a toy model for leaf area over a year at daily time-scale took only 260 μs to perform (about 688 ns per day), and 275 μs (756 ns per day) when coupled to a light interception model. These benchmarks demonstrate performance on par with compiled languages like Fortran or C, far outpacing typical interpreted language implementations. - -For example, PlantBiophysics.jl, which implements ecophysiological models using PlantSimEngine, has been measured to run up to 38,000 times faster than equivalent implementations in other scientific computing languages. +PlantSimEngine keeps model kernels close to regular Julia functions while the +runtime handles dependency scheduling, object selection, temporal aggregation, +and environment sampling. On an M1 MacBook Pro, toy daily simulations run in +hundreds of microseconds, and PlantBiophysics.jl models using PlantSimEngine +have been measured much faster than equivalent implementations in typical +scientific scripting languages. -## Make it yours +For performance-sensitive scenes, inspect the compiled representation with +`explain_execution_plan(scene)` to see homogeneous batches and concrete carrier +types. -The package is developed so anyone can easily implement plant/crop models, use it freely and as you want thanks to its MIT license. +## License And Contributions -If you develop such tools and it is not on the list yet, please make a PR or contact me so we can add it! 😃 Make sure to read the community guidelines before in case you're not familiar with such things. \ No newline at end of file +PlantSimEngine is distributed under the MIT license. Questions and bug reports +are welcome on [GitHub issues](https://github.com/VirtualPlantLab/PlantSimEngine.jl/issues) +or the [FSPM discourse](https://fspm.discourse.group/c/software/virtual-plant-lab). diff --git a/benchmark/test-PSE-benchmark.jl b/benchmark/test-PSE-benchmark.jl index 080f02b1a..60c6cfd78 100644 --- a/benchmark/test-PSE-benchmark.jl +++ b/benchmark/test-PSE-benchmark.jl @@ -59,7 +59,7 @@ function do_benchmark_on_heavier_mtg() meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Day) #similar to the mtg growth test but with a much lower emergence threshold - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( :Scene => ToyDegreeDaysCumulModel(), :Plant => ( MultiScaleModel( diff --git a/benchmark/test-multirate-buffer-benchmark.jl b/benchmark/test-multirate-buffer-benchmark.jl index 2bc67d5a5..8f47c6b14 100644 --- a/benchmark/test-multirate-buffer-benchmark.jl +++ b/benchmark/test-multirate-buffer-benchmark.jl @@ -45,7 +45,7 @@ end function setup_multirate_buffer_benchmark(; nleaves=2000, ndays=30) mtg = _build_multirate_benchmark_mtg(nleaves) - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRBenchSourceModel(Ref(0))) |> TimeStepModel(1.0), ), diff --git a/benchmark/test-plantbiophysics.jl b/benchmark/test-plantbiophysics.jl index 83b6f0c21..071d5d93d 100644 --- a/benchmark/test-plantbiophysics.jl +++ b/benchmark/test-plantbiophysics.jl @@ -64,7 +64,7 @@ function benchmark_plantbiophysics() constants = Constants() #time_PB = Vector{Float64}(undef, N*microbenchmark_steps) for i = 1:N - leaf = ModelMapping( + leaf = PlantSimEngine.ModelMapping( energy_balance=Monteith(), photosynthesis=Fvcb( VcMaxRef=set.VcMaxRef[i], @@ -138,9 +138,9 @@ function setup_benchmark_plantbiophysics_multitimestep() @. set[!, :vpd] = e_sat(set.T) - vapor_pressure(set.T, set.Rh) @. set[!, :aPPFD] = set.Ra_SW_f * 0.48 * 4.57 - leaf = Vector{ModelMapping}(undef, N) + leaf = Vector{PlantSimEngine.ModelMapping}(undef, N) for i = 1:N - leaf[i] = ModelMapping( + leaf[i] = PlantSimEngine.ModelMapping( energy_balance=Monteith(), photosynthesis=Fvcb( VcMaxRef=set.VcMaxRef[i], @@ -179,4 +179,4 @@ function benchmark_plantbiophysics_multitimestep_ST(leaf, meteo) for i in 1:N run!(leaf[i], meteo, Constants(), nothing; executor=SequentialEx()) end -end \ No newline at end of file +end diff --git a/docs/make.jl b/docs/make.jl index 6890c07ef..a33a3acd9 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -30,6 +30,10 @@ makedocs(; "Key Concepts" => "./prerequisites/key_concepts.md", "Julia language basics" => "./prerequisites/julia_basics.md", ], + "Scene/Object simulations" => [ + "Quickstart" => "./scene_object/quickstart.md", + "Migrating from mappings and domains" => "migration_scene_object.md", + ], "Step by step - Single-scale simulations" => [ "Detailed first simulation" => "./step_by_step/detailed_first_example.md", "Coupling" => "./step_by_step/simple_model_coupling.md", @@ -41,8 +45,7 @@ makedocs(; "Advanced coupling and hard dependencies" => "./step_by_step/advanced_coupling.md", "Implementing a model : additional notes" => "./step_by_step/implement_a_model_additional.md", ], - "Execution" => "model_execution.md", - "Domain simulations" => "domain_simulation.md", + "Model execution" => "model_execution.md", "Model traits" => "model_traits.md", "AI agent skill" => "agent_skill.md", "Working with data" => [ @@ -52,7 +55,7 @@ makedocs(; "Visualizing outputs and data" => "./working_with_data/visualising_outputs.md", "Floating-point considerations" => "./working_with_data/floating_point_accumulation_error.md", ], - "Moving to multiscale" => [ + "Legacy MTG mapping tutorials" => [ "Multiscale considerations" => "./multiscale/multiscale_considerations.md", "Converting a simulation to multi-scale" => "./multiscale/single_to_multiscale.md", "More variable mapping examples" => "./multiscale/multiscale.md", @@ -65,7 +68,7 @@ makedocs(; ], "Visualizing our toy plant with PlantGeom" => "./multiscale/multiscale_example_4.md", ], - "Multi-rate tutorials" => [ + "Legacy mapping multi-rate tutorials" => [ "Introduction to multi-rate execution" => "./multirate/introduction.md", "Step-by-step hourly/daily/weekly simulation" => "./multirate/multirate_tutorial.md", "Advanced multi-rate configuration" => "./multirate/advanced_configuration.md", @@ -82,8 +85,13 @@ makedocs(; "Development designs" => [ "Multi-domain simulation design" => "./dev/multi_domain_simulation_design.md", "Multi-domain simulation plan" => "./dev/multi_domain_simulation_plan.md", + "Unified scene/object design" => "./dev/unified_scene_object_design.md", + "Unified scene/object implementation plan" => "./dev/unified_scene_object_implementation_plan.md", + "Unified scene/object completion audit" => "./dev/unified_scene_object_completion_audit.md", "MAESPA-style domain example handoff" => "./dev/maespa_domain_handoff.md", + "Legacy domain regression API" => "domain_simulation.md", "Code cleanup audit" => "./dev/code_cleanup_audit.md", + "Release notes handoff" => "./dev/release_notes_handoff.md", ], "Developer guidelines" => "developers.md", "Roadmap" => "planned_features.md", diff --git a/docs/src/API/API_public.md b/docs/src/API/API_public.md index b36380e4c..163f776b9 100644 --- a/docs/src/API/API_public.md +++ b/docs/src/API/API_public.md @@ -1,5 +1,129 @@ # Public API +## Unified Scene/Object API + +New multiscale and multi-object scenarios should start with the following +groups. + +### Scenario and model applications + +- `Scene` stores the runtime object graph, model applications, instances, and + environment. +- `Object` represents one runtime entity with stable identity, labels, + topology, optional geometry, and status. +- `ObjectTemplate` and `ObjectInstance` reuse one plant or object model across + several concrete instances. +- `ModelSpec(model; name=...)` identifies one model application. +- `AppliesTo(...)` selects the objects where that application runs. + +### Value coupling and manual calls + +- `Inputs(...)` declares consumer-side value dependencies. +- `Calls(...)` gives a parent model manually executable child targets. +- `Updates(:variable; after=...)` orders intentional duplicate writers. +- `Input(...)` and `Call(...)` express model-author defaults through + `dep(model)`. +- `run_call!(target; publish=false)` executes a trial hard call. + +### Selectors and scopes + +- Multiplicity: `One(...)`, `OptionalOne(...)`, and `Many(...)`. +- Scope: `SceneScope()`, `Self()`, `SelfPlant()`, `Ancestor(...)`, and + `Scope(name)`. +- Labels and topology: `Kind(...)`, `Species(...)`, `Scale(...)`, and + `Relation(...)`. +- `ObjectAddress` is the normalized compiled selector representation. + +### Time and environment + +- `TimeStep(period::Dates.Period)` sets an application cadence and overrides a + model `timespec(...)` trait when both are present. +- `timespec(::Type{<:AbstractModel})` can provide a model-author default + cadence for scene/object applications. +- `timestep_hint(::Type{<:AbstractModel})` can validate base-step-derived + scene cadences when `TimeStep(...)` and non-default `timespec(...)` are + absent. +- `HoldLast`, `Interpolate`, `Integrate`, and `Aggregate` define temporal input + behavior. +- `output_policy(::Type{<:AbstractModel})` can provide a producer-side default + temporal policy when an `Inputs(...)` selector omits `policy=...`; explicit + selector policies take precedence. +- `Environment(...)` optionally overrides automatic environment provider, + resolver selection, or source remapping such as + `Environment(; sources=(CO2=:Ca,))`. +- Models declare environment variables with `meteo_inputs_` and + `meteo_outputs_`. +- `meteo_hint(::Type{<:AbstractModel})` can provide model-author default + environment source remaps through its `bindings` field; scenario + `Environment(; sources=...)` overrides those defaults. +- `OutputRequest(...)` can be passed to `run!(scene; tracked_outputs=...)` to + collect resampled scene output streams after the run. Use `process=...` + when the process identifies one publisher, or `application=...` when the + same process is applied more than once. An explicit application may select a + `:stream_only` publisher. + +### Lifecycle and compilation + +- `objects_from_mtg` and `Scene(mtg; ...)` adapt an existing MTG subtree to the + unified object registry. +- `register_object!`, `remove_object!`, and `reparent_object!` change + topology. +- `move_object!` and `update_geometry!` change spatial state. +- `refresh_bindings!` compiles selectors, carriers, calls, writer order, and + schedules. +- `refresh_environment_bindings!` compiles cached object-to-environment + bindings. +- `run!(scene; steps=...)` returns a `SceneSimulation`. +- `scene_outputs(sim)` returns retained typed output streams. +- `collect_outputs(sim)` returns all retained streams, or requested outputs + when `tracked_outputs` was provided. + +```julia +request = OutputRequest( + :Leaf, + :transpiration; + name=:leaf_transpiration_2h, + application=:sunlit_leaf_energy, + policy=Integrate(), + clock=Dates.Hour(2), +) + +sim = run!(scene; steps=24, tracked_outputs=request) +out = collect_outputs(sim, :leaf_transpiration_2h; sink=DataFrames.DataFrame) +``` + +For scene/object runs, `tracked_outputs` is materialized from retained +scene-local output streams. With explicit output requests, the runtime retains +only requested application/variable streams plus streams required by temporal +`Inputs(...)`; `tracked_outputs=OutputRequest[]` retains no output streams +unless temporal dependencies require one. Dynamic objects are exported only +over their own published sample interval. Use `explain_output_retention(sim)` +to inspect why a stream was retained. Dependency-only streams are bounded to +the history required by their temporal policy; requested streams retain their +complete history. Retention explanations report the compiled +`retention_steps`, or `nothing` for full-history streams. + +### Structured explanations + +Use these instead of inspecting internal dictionaries: + +- `explain_objects` +- `explain_instances` +- `explain_scopes` +- `explain_scene_applications` +- `explain_bindings` +- `explain_calls` +- `explain_environment_bindings` +- `explain_schedule` +- `explain_writers` +- `explain_model_bundles` +- `explain_execution_plan` +- `explain_output_retention` +- `explain_outputs` + +See [Migrating To The Scene/Object API](../migration_scene_object.md) for +complete old-to-new translations. + ## Index ```@index @@ -13,17 +137,25 @@ Modules = [PlantSimEngine] Private = false ``` -## Multi-rate policy examples +## Legacy Mapping Multi-Rate Examples + +!!! warning "Legacy configuration surface" + The examples below document the mapping/MTG runtime retained during the + breaking migration. For new scene/object scenarios, use `TimeStep(...)` + and put source, policy, and window information directly on `Inputs(...)`. + `ModelMapping` is no longer exported; retained compatibility code must use + `PlantSimEngine.ModelMapping(...)`. For mapping-level multi-rate configuration, combine: +- `PlantSimEngine.ModelMapping(...)` - `ModelSpec(...)` -- `TimeStepModel(...)` -- `InputBindings(...)` -- `MeteoBindings(...)` -- `MeteoWindow(...)` +- `PlantSimEngine.TimeStepModel(...)` +- `PlantSimEngine.InputBindings(...)` +- `PlantSimEngine.MeteoBindings(...)` +- `PlantSimEngine.MeteoWindow(...)` - `OutputRouting(...)` -- `ScopeModel(...)` +- `PlantSimEngine.ScopeModel(...)` - `timespec(::Type{<:AbstractModel})` (optional trait) - `output_policy(::Type{<:AbstractModel})` (optional trait) - `timestep_hint(::Type{<:AbstractModel})` (optional trait) @@ -32,27 +164,27 @@ For mapping-level multi-rate configuration, combine: - `explain_model_specs(mapping_or_sim)` (utility) - `OutputRequest(...)` in `tracked_outputs` for resampled exports -`TimeStepModel(...)` accepts: +`PlantSimEngine.TimeStepModel(...)` accepts: - `Real` step counts - `ClockSpec` - fixed `Dates` periods (`Dates.Second`, `Dates.Minute`, `Dates.Hour`, `Dates.Day`, ...) Period conversion detail: - Period-based timesteps are converted using the meteo base step `duration`. -- Example: `TimeStepModel(Dates.Day(1))` with hourly meteo (`Dates.Hour(1)`) maps to `ClockSpec(24.0, 1.0)`, +- Example: `PlantSimEngine.TimeStepModel(Dates.Day(1))` with hourly meteo (`Dates.Hour(1)`) maps to `ClockSpec(24.0, 1.0)`, so execution times are `t = 1, 25, 49, ...`. Trait-based inference detail: -- If `TimeStepModel(...)` is omitted, runtime resolves timestep from: +- If `PlantSimEngine.TimeStepModel(...)` is omitted, runtime resolves timestep from: : `timespec(model)` when non-default, otherwise meteo `duration`. - `timestep_hint(::Type{<:Model})` is then interpreted as: : `required` = hard compatibility constraint, `preferred` = informational only. -- If `InputBindings(...)` is omitted, same-name sources are inferred automatically from +- If `PlantSimEngine.InputBindings(...)` is omitted, same-name sources are inferred automatically from : unique producers (same scale first, then cross-scale). Ambiguous cases require explicit bindings. - For inferred bindings, policy defaults to producer `output_policy` when defined, otherwise `HoldLast()`. -- Explicit `InputBindings(..., policy=...)` always overrides trait defaults. +- Explicit `PlantSimEngine.InputBindings(..., policy=...)` always overrides trait defaults. - `output_policy` is hint-only: it is applied only when an output is actually consumed/exported. -- If `MeteoBindings(...)` / `MeteoWindow(...)` are omitted, `meteo_hint(::Type{<:Model})` +- If `PlantSimEngine.MeteoBindings(...)` / `PlantSimEngine.MeteoWindow(...)` are omitted, `meteo_hint(::Type{<:Model})` : may provide `(; bindings=..., window=...)`. - Explicit mapping-level configuration always overrides hints. @@ -62,10 +194,10 @@ Compatibility checks: - `timestep_hint.preferred` never sets runtime timestep by itself. Scope selection detail: -- `ScopeModel(:global)` is the default and shares streams across the whole simulation. -- `ScopeModel(:plant)` isolates streams within each plant subtree. -- `ScopeModel(:scene)` isolates by scene ancestor. -- `ScopeModel(:self)` isolates by node id. +- `PlantSimEngine.ScopeModel(:global)` is the default and shares streams across the whole simulation. +- `PlantSimEngine.ScopeModel(:plant)` isolates streams within each plant subtree. +- `PlantSimEngine.ScopeModel(:scene)` isolates by scene ancestor. +- `PlantSimEngine.ScopeModel(:self)` isolates by node id. ### Exporting variables at requested rates @@ -92,24 +224,24 @@ out_status, out = run!( ```julia ModelSpec(ConsumerModel()) |> -TimeStepModel(ClockSpec(2.0, 1.0)) |> -InputBindings(; x=(process=:producer, var=:x)) +PlantSimEngine.TimeStepModel(ClockSpec(2.0, 1.0)) |> +PlantSimEngine.InputBindings(; x=(process=:producer, var=:x)) ``` ### Meteo aggregation bindings ```julia ModelSpec(DailyModel()) |> -TimeStepModel(ClockSpec(24.0, 1.0)) |> -MeteoWindow(CalendarWindow(:day; anchor=:current_period, week_start=1, completeness=:strict)) |> -MeteoBindings( +PlantSimEngine.TimeStepModel(ClockSpec(24.0, 1.0)) |> +PlantSimEngine.MeteoWindow(CalendarWindow(:day; anchor=:current_period, week_start=1, completeness=:strict)) |> +PlantSimEngine.MeteoBindings( T=MeanWeighted(), # default source is :T Ri_SW_f=RadiationEnergy(), # integrate W m-2 to MJ m-2 over the model window custom_peak=(source=:custom_var, reducer=MaxReducer()), ) ``` -`MeteoWindow(...)` options: +`PlantSimEngine.MeteoWindow(...)` options: - `RollingWindow()` (default): trailing rolling window driven by `dt`. - `CalendarWindow(period; anchor, week_start, completeness)` with: : `period` in `:day`, `:week`, `:month` @@ -125,29 +257,29 @@ Use `Integrate` for accumulation semantics and `Aggregate` for summary-statistic ```julia ModelSpec(DailyModel()) |> -TimeStepModel(ClockSpec(24.0, 1.0)) |> -InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate(SumReducer()))) +PlantSimEngine.TimeStepModel(ClockSpec(24.0, 1.0)) |> +PlantSimEngine.InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate(SumReducer()))) ModelSpec(DailyModel()) |> -TimeStepModel(ClockSpec(24.0, 1.0)) |> -InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Aggregate(MaxReducer()))) +PlantSimEngine.TimeStepModel(ClockSpec(24.0, 1.0)) |> +PlantSimEngine.InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Aggregate(MaxReducer()))) ModelSpec(DailyModel()) |> -TimeStepModel(ClockSpec(24.0, 1.0)) |> -InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate(vals -> maximum(vals) - minimum(vals)))) +PlantSimEngine.TimeStepModel(ClockSpec(24.0, 1.0)) |> +PlantSimEngine.InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate(vals -> maximum(vals) - minimum(vals)))) ModelSpec(DailyModel()) |> -TimeStepModel(ClockSpec(24.0, 1.0)) |> -InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate((vals, durations) -> sum(vals .* durations)))) +PlantSimEngine.TimeStepModel(ClockSpec(24.0, 1.0)) |> +PlantSimEngine.InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate((vals, durations) -> sum(vals .* durations)))) ModelSpec(DailyModel()) |> -TimeStepModel(ClockSpec(24.0, 1.0)) |> -InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate(PlantMeteo.DurationSumReducer()))) +PlantSimEngine.TimeStepModel(ClockSpec(24.0, 1.0)) |> +PlantSimEngine.InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate(PlantMeteo.DurationSumReducer()))) ``` Built-in reducer types are: `SumReducer()`, `MeanReducer()`, `MaxReducer()`, `MinReducer()`, `FirstReducer()`, `LastReducer()`. -The same reducer objects are also used by `MeteoBindings(...)`. +The same reducer objects are also used by `PlantSimEngine.MeteoBindings(...)`. Custom reducers/callables can accept either `(values)` or `(values, durations_seconds)`. ### Parameterized interpolation mode @@ -156,10 +288,10 @@ Custom reducers/callables can accept either `(values)` or `(values, durations_se ```julia ModelSpec(FastModel()) |> -TimeStepModel(1.0) |> -InputBindings(; x=(process=:slow_source, var=:x, policy=Interpolate())) +PlantSimEngine.TimeStepModel(1.0) |> +PlantSimEngine.InputBindings(; x=(process=:slow_source, var=:x, policy=Interpolate())) ModelSpec(FastModel()) |> -TimeStepModel(1.0) |> -InputBindings(; x=(process=:slow_source, var=:x, policy=Interpolate(; mode=:hold, extrapolation=:hold))) +PlantSimEngine.TimeStepModel(1.0) |> +PlantSimEngine.InputBindings(; x=(process=:slow_source, var=:x, policy=Interpolate(; mode=:hold, extrapolation=:hold))) ``` diff --git a/docs/src/FAQ/translate_a_model.md b/docs/src/FAQ/translate_a_model.md index 89b2b6a4c..df24a6cec 100644 --- a/docs/src/FAQ/translate_a_model.md +++ b/docs/src/FAQ/translate_a_model.md @@ -146,7 +146,7 @@ meteo_day = to_daily(meteo, :TT => (x -> sum(x) / 24) => :TT) Then we can define our list of models, passing the values for `TT_cu` in the status at initialization: ```@example mymodel -m = ModelMapping( +m = PlantSimEngine.ModelMapping( ToyLAIModel(), status = (TT_cu = cumsum(meteo_day.TT),), ) diff --git a/docs/src/agent_skill.md b/docs/src/agent_skill.md index e432ba383..156299651 100644 --- a/docs/src/agent_skill.md +++ b/docs/src/agent_skill.md @@ -8,12 +8,30 @@ The skill file is stored in the repository at: skills/plantsimengine/SKILL.md ``` -Users can download the `skills/plantsimengine` folder and tell their agent to use the `plantsimengine` skill when working with PlantSimEngine.jl. The skill gives agents the package-specific conventions they need for: +Users can download the `skills/plantsimengine` folder and tell their agent to +use the `plantsimengine` skill when working with PlantSimEngine.jl. The skill +gives agents the package-specific conventions they need for: -- composing existing models with `ModelMapping`; -- declaring spatial multiscale mappings with scale symbols and `MultiScaleModel`; -- configuring multirate simulations with `ModelSpec`, `TimeStepModel`, `InputBindings`, and temporal policies; -- implementing or wrapping models with `@process`, `inputs_`, `outputs_`, `run!`, hard dependencies, and model traits. +- building object graphs with `Scene`, `Object`, `ObjectTemplate`, and + `ObjectInstance`; +- applying models with `ModelSpec` and `AppliesTo`; +- coupling values and manual model calls with `Inputs` and `Calls`; +- configuring multirate simulations with `TimeStep`, `Dates.Period` values, + and temporal policies; +- binding global or spatial microclimate through `Environment`; +- reasoning about model-clock weather aggregation, `meteo_hint` reducers, and + scenario source overrides; +- inspecting compiled scenarios with structured explanation helpers; +- inspecting homogeneous runtime batches with `explain_execution_plan`; +- collecting raw or requested scene outputs with `scene_outputs`, + `OutputRequest`, `collect_outputs`, and `explain_output_retention`; +- implementing or wrapping models with `@process`, `inputs_`, `outputs_`, + `run!`, hard dependencies, and model traits. + +The legacy `ModelMapping`, `MultiScaleModel`, domain, and route APIs remain +useful when maintaining existing simulations, but agents should not select them +for new scene/object scenarios. See +[Migrating To The Scene/Object API](migration_scene_object.md). The canonical source is [`skills/plantsimengine/SKILL.md`](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/skills/plantsimengine/SKILL.md). diff --git a/docs/src/dev/code_cleanup_audit.md b/docs/src/dev/code_cleanup_audit.md index 7b9cb7794..68fcce4ce 100644 --- a/docs/src/dev/code_cleanup_audit.md +++ b/docs/src/dev/code_cleanup_audit.md @@ -44,12 +44,12 @@ and some are still internal dependencies rather than shallow exported shims. | Priority | Compatibility surface | Evidence | Migration note | | --- | --- | --- | --- | -| Done | `ModelList` public API and legacy backing type | Formerly in `src/PlantSimEngine.jl`; `src/component_models/ModelList.jl`; `src/mtg/mapping/mapping.jl` | `ModelList` has been removed. Use `ModelMapping(model...; status=..., type_promotion=...)` for single-scale simulations. | +| Done | `ModelList` public API and legacy backing type | Formerly in `src/PlantSimEngine.jl`; `src/component_models/ModelList.jl`; `src/mtg/mapping/mapping.jl` | `ModelList` has been removed. Use `PlantSimEngine.ModelMapping(model...; status=..., type_promotion=...)` for single-scale simulations. | | Done | `run!(::ModelList, ...)` | Formerly in `src/run.jl` direct `ModelList` methods | `run!(::ModelList, ...)` has been removed. Wrap models in `ModelMapping` before running. | | Done | Batch `run!` for collections of `ModelList` or single-scale mappings | Formerly in `src/run.jl` collection methods | Batch `run!([mapping1, mapping2], meteo)` and `run!(Dict(...), meteo)` are removed. Use an explicit loop or comprehension and call `run!` per mapping. | -| Done | `run!(mtg, mapping::AbstractDict, ...)` | Formerly in `src/run.jl` | Passing a raw `Dict` to multiscale `run!` is removed. Construct `ModelMapping(dict)` first, or use `ModelMapping(:Scale => models, ...)`. | +| Done | `run!(mtg, mapping::AbstractDict, ...)` | Formerly in `src/run.jl` | Passing a raw `Dict` to multiscale `run!` is removed. Construct `PlantSimEngine.ModelMapping(dict)` first, or use `PlantSimEngine.ModelMapping(:Scale => models, ...)`. | | Done | String scale names | `src/mtg/mapping/mapping.jl`; `src/mtg/MultiScaleModel.jl`; `src/mtg/model_spec_inference.jl`; `src/time/runtime/bindings.jl` | String scale names are removed. Use symbols everywhere, for example `:Leaf` instead of `"Leaf"`. | -| Done | `ModelMapping(Float64 => Float32)` as old type-promotion shorthand | Formerly in `src/mtg/mapping/mapping.jl` | `ModelMapping(Float64 => Float32)` is removed. Use `Dict(Float64 => Float32)` as the `type_promotion` value. | +| Done | `PlantSimEngine.ModelMapping(Float64 => Float32)` as old type-promotion shorthand | Formerly in `src/mtg/mapping/mapping.jl` | `PlantSimEngine.ModelMapping(Float64 => Float32)` is removed. Use `Dict(Float64 => Float32)` as the `type_promotion` value. | | Done | Old output indexing helpers on multiscale output dictionaries | Formerly in `src/mtg/GraphSimulation.jl` | `outputs(out_dict, key)` and `outputs(out_dict, i)` are removed. Use `convert_outputs(out_dict, sink)` and index the converted table or dictionary explicitly. | `ModelMapping{SingleScale}` now uses an internal single-scale backing container diff --git a/docs/src/dev/maespa_domain_handoff.md b/docs/src/dev/maespa_domain_handoff.md index 4e27471a1..478725db9 100644 --- a/docs/src/dev/maespa_domain_handoff.md +++ b/docs/src/dev/maespa_domain_handoff.md @@ -1,8 +1,9 @@ -# MAESPA-Style Domain Example Handoff +# MAESPA-Style Domain And Scene Example Handoff The example in `examples/maespa_domain_example.jl` is the executable test bed -for the current hard-domain and scene microclimate prototype. It is also a good -migration target for the future unified scene/object design. +for the current hard-domain prototype and the unified scene/object migration. +The domain path remains as regression coverage while the scene/object path is +the target API acceptance example. ## Current Example Shape @@ -20,6 +21,66 @@ migration target for the future unified scene/object design. `:soil_water` target. - Plant allocation runs daily through the normal plant-local dependency graph. +## Unified Scene/Object Example Shape + +The same file now also provides `build_maespa_unified_scene(...)` and +`run_maespa_scene_example(...)`. + +- The scene is a single object graph with a `:Scene` object, one shared + `:Soil` object, and two mounted plant instances. +- Species A and B are reusable `ObjectTemplate`s. Each instance mounts the + same leaf model stack shape inside a named plant subtree, with different + species parameters. +- The leaf stack uses the copied PlantBiophysics subsample models: + `Monteith`, `Fvcb`, and `Tuzet`. +- `Monteith` declares a scene `Calls(:photosynthesis => ...)` application, and + `Fvcb` declares `Calls(:stomatal_conductance => ...)`. The scene runtime + builds the small hard-dependency `models` bundle expected by these generic + kernels. +- `SceneEB` is a scene application: + +```julia +ModelSpec(scene_model; name=:scene_eb) |> + AppliesTo(One(scale=:Scene)) |> + Calls( + :energy_balance => Many(kind=:plant, scale=:Leaf, process=:energy_balance), + :soil => One(kind=:soil, scale=:Soil, process=:soil_water), + ) |> + TimeStep(Dates.Hour(1)) +``` + +- `LAIModel` is a scene application with a single `Inputs(...)` leaf-area + binding: + +```julia +ModelSpec(LAIModel(ground_area); name=:lai_dynamic) |> + AppliesTo(One(scale=:Scene)) |> + Inputs(:leaf_areas => Many(kind=:plant, scale=:Leaf, within=SceneScope(), + process=:leaf_state, var=:leaf_area)) |> + TimeStep(Dates.Day(1)) +``` + +- Plant allocation is a plant-local application: + +```julia +ModelSpec(AllocA(...); name=:allocation) |> + AppliesTo(One(scale=:Plant)) |> + Inputs(:leaf_carbon => Many(scale=:Leaf, within=Self(), var=:leaf_carbon)) |> + TimeStep(Dates.Day(1)) +``` + +The scene runtime still uses the existing generic model kernel signature: + +```julia +run!(model, models, status, meteo, constants, extra) +``` + +For manual scene calls, `run_call!(target; meteo=local_meteo)` lets a parent +solver pass trial microclimate directly into the callee. The default is +`publish=false`, so trial calls mutate target status without publishing +temporal samples or environment outputs. This is how `SceneEB` controls the +iterative canopy T/VPD solution. + ## Manual Call Expectations - `Calls(:energy_balance => Many(kind=:plant, scale=:Leaf, process=:energy_balance))` @@ -27,8 +88,8 @@ migration target for the future unified scene/object design. bridge. - `Calls(:soil => One(kind=:soil, process=:soil_water))` selects the shared soil model through the current hard-domain bridge. -- `dependency_targets(extra, :energy_balance)` returns executable leaf targets. -- `run_call!(target; publish=false)` is used during trial iterations. +- `call_targets(extra, :energy_balance)` returns executable leaf targets. +- `run_call!(target)` is used during trial iterations. - `run_call!(target; publish=true)` is used for the accepted final solution so outputs are appended once to domain streams and `DomainSimulation.outputs`. - Trial target runs mutate target status. Irreversible accumulators such as @@ -60,7 +121,10 @@ computes `leaf_area` and `lai` in the scene domain. ## Verification Expectations -The focused test `test/test-maespa-domain-example.jl` should verify: +The focused test `test/test-maespa-domain-example.jl` verifies both the domain +regression path and the unified scene/object path. + +For the domain path it should verify: - Species A has two leaves and species B has three leaves. - `:energy_balance` hard-domain outputs are published once per leaf per hour. @@ -72,13 +136,39 @@ The focused test `test/test-maespa-domain-example.jl` should verify: - Daily allocation differs between the two plant species because their allocation parameters differ. -## Future Migration Target - -The MAESPA example has already moved away from explicit user-level -`Route(...)` and `HardDomains(...)`: leaf area materialization is declared with -`Inputs(...)`, and manual energy-balance calls are declared with `Calls(...)`. - -Expected migration: +For the unified scene/object path it should verify: + +- The object graph contains five leaves, two plant instances, one shared soil + object, and one scene object. +- `explain_instances(scene)` reports species A and B instance membership and + their mounted application ids. +- `explain_calls(compiled)` reports the scene energy-balance calls to all leaf + energy-balance applications and the shared soil application. +- Nested leaf calls report `Monteith -> Fvcb -> Tuzet`. +- `explain_model_bundles(compiled)` confirms that every leaf energy-balance + target receives the precompiled `energy_balance`, `photosynthesis`, and + `stomatal_conductance` model bundle expected by the copied PlantBiophysics + kernels. +- `explain_bindings(compiled)` reports live-reference leaf-area and + plant-local leaf-carbon bindings. +- `explain_schedule(compiled)` reports hourly scene energy balance, daily LAI + and allocation clocks, and manual-call-only leaf/soil applications. +- `run_maespa_scene_example(...)` returns a `SceneSimulation` in + `result.simulation`, so `collect_outputs(result.simulation)` and + `explain_outputs(result.simulation)` expose the scene-local output streams. +- If `OutputRequest(...)` values are passed through the scene run, requested + exports should be available from `collect_outputs(result.simulation, + :request_name)` using the same retained scene output streams. +- Scene microclimate, leaf energy, plant allocation, and soil feedback remain + finite and coupled after a 25-hour run. + +## Remaining Migration Target + +The MAESPA example has now moved away from explicit user-level `Route(...)` and +`HardDomains(...)` in the scene/object path. The domain path still exists as +regression coverage until the old domain runtime is removed. + +The target public form is: ```julia ModelSpec(LAIModel(ground_area)) |> diff --git a/docs/src/dev/release_notes_handoff.md b/docs/src/dev/release_notes_handoff.md index 98f624cdd..c3b919e5a 100644 --- a/docs/src/dev/release_notes_handoff.md +++ b/docs/src/dev/release_notes_handoff.md @@ -8,16 +8,21 @@ what is removed, and what is only planned. Source details live in `code_cleanup_audit.md`. -- Removed public `ModelList` usage. Use `ModelMapping(model...; status=...)` - for single-scale simulations. -- Removed direct `run!(::ModelList, ...)`; wrap models in `ModelMapping`. +- Removed public `ModelList` usage. New simulations should use `Scene` and + model applications. Retained mapping compatibility code can use + `PlantSimEngine.ModelMapping(model...; status=...)`. +- Removed direct `run!(::ModelList, ...)`; retained mapping code must wrap + models in `PlantSimEngine.ModelMapping`. - Removed batch `run!` over collections/dictionaries of single-scale mappings; use explicit loops or comprehensions. - Removed raw `Dict` multiscale `run!(mtg, dict, ...)`; construct - `ModelMapping(dict)` first. + `PlantSimEngine.ModelMapping(dict)` first. - Removed string scale names. Use symbols, for example `:Leaf`. -- Removed `ModelMapping(Float64 => Float32)` promotion shorthand. Use a +- Removed `PlantSimEngine.ModelMapping(Float64 => Float32)` promotion shorthand. Use a `Dict(Float64 => Float32)` as the `type_promotion` value. +- Removed `ModelMapping` from exports. Historical mapping simulations remain + available through the explicitly qualified + `PlantSimEngine.ModelMapping(...)` compatibility API. - Removed old multiscale output indexing helpers. Convert outputs explicitly before indexing. - Replaced the `Symbol("")` same-scale sentinel with `SameScale()`. @@ -84,6 +89,16 @@ for multi-plant scene coupling. - Adds `explain_scopes(scene)` for structured scope diagnostics. It reports the scene scope, object subtree scopes, named `Scope(...)` entries, and scale/kind/species label groups with concrete object ids. +- Selector failures now include context, matched object ids, requested + criteria, available labels, and near-match suggestions. Misspelled labels + such as `scale=:Leef` therefore suggest `:Leaf` instead of returning only a + cardinality count. +- `Relation(...)` now supports `:self`, `:parent`, `:children`, `:ancestors`, + `:descendants`, and `:siblings` in `AppliesTo`, `Inputs`, and `Calls` + selectors. Relation results are compiled to concrete object ids and may be + constrained by an explicit scope. +- `ObjectAddress` explanations now preserve positional selector criteria such + as `Scale(:Leaf)` and `Relation(:parent)`. - Adds the first compiled scene/object view with `compile_scene`, `CompiledScene`, `CompiledSceneApplication`, `CompiledSceneInputBinding`, `CompiledSceneCallBinding`, `explain_scene_applications`, @@ -100,6 +115,17 @@ for multi-plant scene coupling. scalar shared refs, homogeneous `RefVector`s, and `ObjectRefVector` fallback carriers. `input_carrier`, `input_value`, and `has_reference_carrier` expose them for tests, diagnostics, and future runtime execution. +- Same-rate scene inputs are now wired into consumer `Status` references once + during compilation. Scalar and `Many(...)` inputs remain live references, + missing bound input fields are compiler-generated, and repeated non-temporal + input materialization is allocation-free. +- Same-rate `Inputs(...)` carriers preserve arbitrary concrete value types. + Regression coverage passes a dual-like `BigFloat` wrapper through a typed + `RefVector`, model arithmetic, source mutation, and output publication + without conversion to `Float64`. +- Same-object variable renaming now uses normal `Inputs(...)` syntax instead of + `SameScale()`. Renamed inputs share the producer reference and contribute the + expected producer-to-consumer scheduling edge. - `explain_bindings` now reports stable carrier kind and copy/reference semantics, making reference-wired inputs and materialized temporal values explicit for users and agents. @@ -116,6 +142,24 @@ for multi-plant scene coupling. Environment binding refreshes call `update_index!(backend, entities)` before binding objects to backend cells/layers, so spatial backends can precompute scene-wide lookup structures. +- Spatial environment binding now falls back to the nearest ancestor geometry + for objects without their own geometry. Binding explanations expose the + geometry provenance, and moving an ancestor refreshes only descendants that + inherit its geometry. +- Environment binding refresh can now update changed `meteo_inputs_` and + `meteo_outputs_` metadata without repeating spatial indexing or cell lookup + when the application/object/provider/geometry contract is otherwise + unchanged. +- `Environment(; sources=(CO2=:Ca,))` now remaps model-facing environment + variables to backend source variables. Scene environment binding refresh + validates missing source variables for enumerable backends such as + `GlobalConstant`, and explanations expose both `required_inputs` and + `source_inputs`. +- `validate_meteo_inputs(scene)` and + `validate_meteo_inputs(compiled_scene, meteo_or_backend)` now validate + scene/object environment contracts directly. Missing-variable diagnostics use + scene application ids, and validation honors both scenario + `Environment(; sources=...)` remaps and model-author `meteo_hint` defaults. - Object movement now invalidates environment bindings without rebuilding the structural object/model binding cache. - Adds public geometry lifecycle helpers: @@ -128,7 +172,7 @@ for multi-plant scene coupling. inputs, and executes generic model kernels on object `Status` values. - Scene/object compiler now infers simple same-object value bindings from `inputs_`/`outputs_` when one producer is unambiguous. `explain_bindings` - reports each binding origin, including `:declared` and + reports each binding origin, including `:model_default`, `:model_spec`, and `:inferred_same_object`. - Compiled input bindings now validate `Inputs(...)` `process=`/`application=` filters when they are provided, and `explain_bindings` reports @@ -136,6 +180,11 @@ for multi-plant scene coupling. - `compile_scene` now errors for required `inputs_(model)` variables that are neither bound through `Inputs(...)`/inference nor present on the target object `Status`. +- `compile_scene` now prepares model-owned status schemas automatically: + model-targeted objects may omit `Status`, declared model/environment outputs + are inserted from trait defaults, and bound consumer inputs are generated + from `inputs_` defaults. External unbound inputs still require explicit + initialization. - `compile_scene` now rejects `Inputs(...)` entries whose receiving variable is not declared by the model's `inputs_`, making binding typos explicit at compile time. @@ -145,12 +194,24 @@ for multi-plant scene coupling. - Scene/object runtime now publishes model outputs to scene-local temporal streams and resolves temporal `Inputs(...)` with `HoldLast`, `Integrate`, and `Aggregate` policies before consumer execution. +- Scene temporal `Inputs(...)` now use producer `output_policy(...)` traits as + the default when the selector omits `policy=...` and resolves to a unique + source application. Explicit selector policies override the trait. +- Scene applications now infer model-author default environment source remaps + from `meteo_hint(...).bindings` when the scenario does not provide explicit + meteo bindings. Scenario `Environment(; sources=...)` remains the override. - Scene/object runtime now scatters values declared by `meteo_outputs_(model)` back to the bound environment backend after each model call, using the existing `scatter_environment_outputs!` backend protocol. - Scene/object root applications now honor `TimeStep(...)` values backed by `Dates.Period` scheduling. `explain_schedule` reports normalized clocks and whether an application is root-scheduled or manual-call-only. +- Scene/object root applications now also honor `timespec(...)` model traits + when no explicit `TimeStep(...)` is provided. Scenario-level `TimeStep(...)` + remains the override. +- Scene/object root applications now validate `timestep_hint(...)` required + bounds for clocks derived from the scene base step. Hints remain + compatibility constraints, not scheduling overrides. - Scene/object execution now uses a stable topological application order compiled from `Inputs(...)` producer edges and `Updates(...)` ordering. Dependencies on manual-call-only applications are redirected to their parent @@ -166,8 +227,8 @@ for multi-plant scene coupling. application and object id, removing the scene-wide binding scan from environment sampling and output scattering. - Adds `SceneRunContext` and `SceneCallTarget`; scene/object models can use - `dependency_target(s)(extra, :name)` plus `run_call!` for manual - `Calls(...)` execution. + `call_target(s)(extra, :name)` plus `run_call!` for manual `Calls(...)` + execution. - Applications selected by `Calls(...)` are skipped by the root `run!(scene)` loop and execute only through explicit `run_call!`, preserving parent-controlled hard-call execution. @@ -204,16 +265,158 @@ for multi-plant scene coupling. - Objects created below a mounted instance inherit missing template `kind` and `species` labels. Membership explanations use the current topology rather than a copied instance object list. +- Scene hard calls now accept explicit local meteorology: + `run_call!(target; meteo=local_meteo, publish=false)`. This lets iterative + parent models pass trial microclimate to manually called child models without + resampling the scene environment. +- Scene hard calls now default to `publish=false`. Trial calls mutate target + status without publishing temporal samples or environment writes; accepted + states must use `run_call!(target; publish=true)`. Iterative-call tests verify + that several trials followed by one accepted call publish exactly once. +- `explain_calls(compiled)` now exposes the manual-call publication contract + through `publication_policy`, `default_publish`, and `accepted_publish` + fields. +- `ModelSpec` now keeps provenance for `Inputs(...)` and `Calls(...)`. + Declarations coming from `dep(model)` are `:model_default`, scenario-level + declarations and overrides are `:model_spec`, and structured explanations + expose these origins for release-note and migration diagnostics. +- Compiled `OptionalOne(...)` inputs and calls now accept zero matches. + Optional inputs keep their declared model default, optional calls return an + empty target collection, and both remain visible in structured explanations. +- Scene runtime now builds a process-keyed `models` bundle from compiled + `Calls(...)` edges before invoking a model kernel. Existing hard-dependency + kernels such as `Monteith` and `Fvcb` can therefore keep using + `models.photosynthesis` and `models.stomatal_conductance`. +- Scene duplicate-writer validation now ignores manual-call-only applications + when validating canonical root writers, so hard-dependency children are not + treated as independent root writers for variables they update inside a parent + call stack. +- Adds `build_maespa_unified_scene(...)` and `run_maespa_scene_example(...)`. + This unified scene/object MAESPA path uses `ObjectTemplate`, + `ObjectInstance`, `AppliesTo`, `Inputs`, `Calls`, and + `TimeStep(Dates.Period)` with two plant species, one shared soil object, + scene LAI, and scene energy balance. +- `test/test-maespa-domain-example.jl` now verifies the unified scene/object + MAESPA path alongside the domain regression path. +- `run!(scene)` now returns a `SceneSimulation` wrapper containing the mutated + scene, compiled object bindings, compiled environment bindings, and + scene-local temporal output streams. +- Adds scene output inspection helpers: + `scene_outputs(sim::SceneSimulation)`, `collect_outputs(sim)`, and + `explain_outputs(sim)`. These expose object ids, variables, publishing + application ids, sample counts, time bounds, and value types. +- `run!(scene; tracked_outputs=...)` now accepts `OutputRequest` for + scene/object runs. Requested outputs are collected from retained typed scene + streams after the run, can be read with `collect_outputs(sim)` or + `collect_outputs(sim, :request_name)`, support the standard temporal + policies and `Dates.Period` export clocks, and respect dynamic object + lifetimes by exporting each object only across its own sample interval. This + now prunes retained streams at publisher level: `tracked_outputs=nothing` + keeps all streams, explicit requests keep requested application/variable + streams plus streams required by temporal `Inputs(...)`, and + `tracked_outputs=OutputRequest[]` keeps no streams unless temporal + dependencies require them. Dependency-only streams now have bounded + policy-specific histories: latest-only for `HoldLast`, the required window + for `Integrate`/`Aggregate`, and sufficient recent source samples for + `Interpolate`/`PreviousTimeStep`. Requested and default retain-all streams + still preserve complete histories, and export remains post-run rather than + fully online. +- Adds `explain_output_retention(sim)` for structured diagnostics of retained + scene output streams, their reasons, and the compiled retention horizon for + dependency-only streams. +- Scene temporal streams are now keyed by application id, object id, and + variable, so two applications can publish the same variable on the same + object without overwriting each other's stream samples. +- Scene output-export tests now cover requested-output `DataFrame` + materialization, canonical publisher inference without `process=...`, + rejection when only stream-only publishers exist, and ambiguity when an + explicit process matches both a stream-only and a canonical publisher. +- `OutputRequest(...)` now accepts `application=...` for scene/object runs. + This disambiguates repeated applications of the same process and permits + explicit export of a named `:stream_only` publisher. Legacy + `GraphSimulation` output export rejects this scene-specific selector. +- Scene temporal streams now retain a concrete value type per + application/object/output stream. Type changes fail explicitly, while + generic values such as `BigFloat` remain typed through publication, + interpolation, and integration. +- Scene temporal `Inputs(...)` now implement the complete `Interpolate(...)` + policy used by the existing multirate runtime: linear interpolation when + samples bracket the requested time, online linear extrapolation from the + last two samples, and configurable hold behavior. Interpolation modes are + validated during scene compilation, and arithmetic preserves generic value + types such as `BigFloat` instead of coercing model values to `Float64`. +- Scene `Inputs(...)` accepts + `PreviousTimeStep(:input) => One(...)` or `Many(...)` for explicit lagged + dependencies. These bindings read the previous scene timestep, use the + consumer status initialization before history exists, and are excluded from + same-timestep dependency edges so feedback loops can be compiled. +- Scene `OutputRouting(; var=:stream_only)` is honored by canonical writer + validation and same-object input inference. Stream-only outputs are excluded + from canonical ownership, but remain available in output streams and explicit + `Inputs(..., application=:name)` bindings. +- Scene execution now refreshes dirty structural bindings between timesteps. + Objects created, removed, or reparented by a model update application target + sets, input carriers, call targets, writer validation, and scheduling before + the next timestep. +- Geometry-only changes refresh environment bindings at the next timestep + without rebuilding structural bindings. `SceneSimulation` returns the final + compiled structural and environment state, including changes made during the + last timestep. +- Geometry-only environment invalidation is now object-scoped. Moving or + explicitly marking one organ dirty preserves unaffected compiled environment + bindings and rebinds only model applications targeting that object; + structural scene changes still trigger a full rebuild. +- Added runtime lifecycle coverage for organ creation, pruning, plant-local + `RefVector` refresh, historical output retention for removed objects, and + movement between mock microclimate cells. +- `CompiledScene` now precompiles one process-keyed model bundle per + application/object target. Generic hard-dependency kernels receive this + cached bundle through the existing `models` argument, avoiding recursive + `Calls(...)` traversal and temporary collection allocation in the timestep + hot loop. +- Adds `explain_model_bundles(compiled)` for structured inspection of the + process names and model types passed to each application/object kernel. +- Scene root execution now uses compiled homogeneous target batches. Models, + statuses, model bundles, input bindings, and environment bindings are + prebound, so dynamic dispatch happens once per batch instead of once per + object. Heterogeneous object overrides split into ordered concrete batches. +- Adds `explain_execution_plan(scene_or_simulation)` and a zero-allocation + warmed 128-leaf inner-loop regression gate. +- Manual `Calls(...)` handles now use the public + `call_target(extra, name)`/`call_targets(extra, name)` lookup API followed by + `run_call!`. The older `dependency_target(s)` and `run_target!` spellings + remain internal compatibility helpers. +- The legacy domain/route authoring surface is no longer exported: + `Domain`, `SimulationMapping`, `DomainSimulation`, `AllDomains`, + `HardDomains`, `Route`, route cardinalities, domain target helpers, and + domain explanation helpers require qualified `PlantSimEngine.*` access for + regression and migration work. +- Adds `objects_from_mtg(root; ...)` and `Scene(mtg; ...)` so existing MTG + topology can be adapted once into the unified registry while preserving + node-derived identity, parent relations, labels, geometry, and existing + status objects. +- Scene applications now sample global tabular meteorology at their compiled + `Dates.Period` clock. PlantMeteo reducers and windows from `meteo_hint` are + honored; `Environment(; sources=...)` overrides the source while preserving + the reducer. Prepared samplers are shared, and one sampled row is cached per + application/timestep for all selected objects. -## Planned Future Breaking Redesign +## Compatibility Boundary -The target design is documented in: +The scene/object runtime and its MAESPA acceptance path are implemented. The +legacy configuration surface is retained only as an explicitly qualified +compatibility and regression layer. It is not exported or presented as the +primary API. The design, implementation history, and completion evidence are +documented in: - `unified_scene_object_design.md` - `unified_scene_object_implementation_plan.md` +- `unified_scene_object_completion_audit.md` -Expected future migration: +The completed public migration is: +- replace historical qualified compatibility tutorials with native + scene/object tutorials where long-term coverage is still valuable; - model mappings should be described as model applications: `ModelSpec(model; name=...) |> AppliesTo(...) |> Inputs(...) |> Calls(...)`; - `MultiScaleModel(...)` -> `Inputs(...)`. @@ -229,7 +432,7 @@ Expected future migration: - `InputBindings(...)` -> source, policy, and window information on `Inputs(...)`. - `MeteoBindings(...)` and `MeteoWindow(...)` -> automatic environment - binding plus optional `Environment(...)` overrides. + binding plus `Environment(...)` provider/source overrides. - `OutputRouting(...)` -> model-application output policy. - `ScopeModel(...)` -> `AppliesTo(...)` plus selector scopes. - `PreviousTimeStep(...)` remains supported as a temporal/cycle-breaking @@ -237,5 +440,53 @@ Expected future migration: - explicit per-model meteo wiring -> automatic environment resolver plus cached environment bindings. -Important: the future redesign is not implemented yet. Do not describe it as -released behavior until the old examples have been migrated and tests pass. +Historical examples and tests that remain are intentionally retained as a +separate qualified compatibility layer. Their continued existence does not +make them part of the public scene/object API. + +## Migration Documentation Added + +- Added `docs/src/migration_scene_object.md` as the user-facing migration guide + from mappings, routes, and domains to the scene/object API. +- Updated documentation navigation, home-page guidance, legacy domain and + multiscale warnings, and the canonical repository agent skill to direct new + scenarios toward `Scene`, `Object`, `AppliesTo`, `Inputs`, `Calls`, + `Updates`, `TimeStep`, and `Environment`. +- Replaced the documentation home-page quickstart with executable + scene/object examples. The page now introduces `Scene`, `Object`, + `ModelSpec`, `AppliesTo`, `Inputs`, `TimeStep`, inferred same-object + bindings, multi-object `Many(...)` inputs, and manual `Calls(...)` syntax + before linking to legacy mapping reference pages. +- Replaced the repository README examples with scene/object-first examples. + The README now introduces `Scene`, `Object`, model applications, + multi-object `Inputs(...)`, and `Calls(...)`, and treats `ModelMapping` / + `MultiScaleModel` as compatibility APIs. +- Added a native scene/object quickstart page to the main documentation + navigation. It provides docs-tested examples for one-object model chaining, + inferred bindings, requested output retention, multi-object `Inputs(...)`, + reference carrier explanations, and manual `Calls(...)` syntax. +- Rewrote the model execution page as the current scene/object execution + guide. It now covers compilation, reference carriers, temporal `Inputs(...)`, + manual `Calls(...)`, `Updates(...)`, `TimeStep(...)`, environment binding, + retained outputs, lifecycle invalidation, and compatibility translations for + old mapping/domain constructs. +- Rewrote the detailed first simulation tutorial to use the scene/object API. + It now introduces `Scene`, `Object`, `ModelSpec`, `AppliesTo`, `TimeStep`, + compiled applications, inferred same-object bindings, scene outputs, and a + compatibility note for historical `ModelMapping` examples. +- Rewrote the quick examples page to use native scene/object snippets for + Beer light interception, degree-days/LAI/light coupling, biomass growth, and + retained `OutputRequest` exports. Historical `ModelMapping` usage is now + confined to the compatibility note. +- Rewrote the standard model coupling, model switching, and coupling more + complex models tutorials around the scene/object API. These pages now show + inferred same-object value bindings, switching one `ModelSpec` application, + execution-plan explanations, and `Calls(...)` manual-call wiring before + mentioning `PlantSimEngine.ModelMapping(...)` as compatibility syntax. +- Removed legacy mapping transforms from exports: + `MultiScaleModel`, `SameScale`, `TimeStepModel`, `InputBindings`, + `MeteoBindings`, `MeteoWindow`, and `ScopeModel`. Historical executable + examples use qualified compatibility names and are grouped under legacy + documentation sections. +- Added a curated unified scene/object map to the public API page and labeled + the remaining mapping-level multirate reference as legacy. diff --git a/docs/src/dev/unified_scene_object_completion_audit.md b/docs/src/dev/unified_scene_object_completion_audit.md new file mode 100644 index 000000000..ffa3088da --- /dev/null +++ b/docs/src/dev/unified_scene_object_completion_audit.md @@ -0,0 +1,100 @@ +# Unified Scene/Object Completion Audit + +This audit records the evidence used to assess the breaking scene/object +redesign against `unified_scene_object_design.md` and +`unified_scene_object_implementation_plan.md`. + +Audit date: June 12, 2026. + +## Result + +The unified scene/object redesign is implemented as the primary public +configuration API. + +The retained mapping and domain implementations form an explicitly qualified +compatibility and regression layer. They are not exported, are grouped under +legacy documentation sections, and are not used by the native MAESPA +acceptance path. + +## Public Contract + +The exported scenario vocabulary centers on: + +```julia +Scene +Object +ModelSpec +AppliesTo +Inputs +Calls +Updates +TimeStep +Environment +``` + +Legacy scenario constructors such as `ModelMapping`, `MultiScaleModel`, +`Domain`, `Route`, `AllDomains`, and `HardDomains` are not exported. + +Evidence: + +- `src/PlantSimEngine.jl` +- `docs/src/API/API_public.md` +- `docs/src/migration_scene_object.md` +- `README.md` + +## Requirement Evidence + +| Requirement | Evidence | +| --- | --- | +| One scene object registry without prescribing plant topology | `Scene`, `Object`, selectors, relations, scopes, and `objects_from_mtg` in `src/scene_object_api.jl`; selector and MTG adapter tests in `test/test-unified-scene-object-api.jl` | +| Reusable species models and repeated plant instances | `ObjectTemplate`, `ObjectInstance`, `Override`, shared model ownership, and homogeneous/heterogeneous batches; four-instance and override tests | +| Soft/value dependencies | Compiled `Inputs(...)` bindings, same-object inference, scalar references, `RefVector`, `ObjectRefVector`, temporal carriers, renaming, and optional inputs | +| Manual hard dependencies | Compiled `Calls(...)`, `SceneCallTarget`, `call_target(s)`, and `run_call!`; trial calls default to `publish=false` and accepted calls publish explicitly | +| Model-author dependency defaults | `Input(...)` and `Call(...)` entries from `dep(model)`, with `ModelSpec` overrides and binding provenance in explanations | +| Multirate execution | `TimeStep(Dates.Period)`, model `timespec`, input policies, explicit windows, `PreviousTimeStep`, and stable compiled scheduling | +| Generic value types | Reference and temporal tests using `SceneObjectDualLike{BigFloat}` and `BigFloat` interpolation/integration without `Float64` conversion | +| Reference semantics and low-copy execution | Shared scalar references, typed many-object carriers, preinstalled status bindings, zero-allocation materialization tests, and a zero-allocation warmed 128-object execution batch | +| Duplicate writers | Canonical writer validation and `Updates(:variable; after=...)`, including pruning-after-allocation tests | +| Growth, pruning, reparenting, and movement | Central lifecycle APIs, structural/environment cache invalidation, dynamic target/carrier rebuilding, removed-object output history, and object-scoped geometry refresh tests | +| Automatic meteorology and microclimate | `meteo_inputs_`, `meteo_outputs_`, global and spatial backends, ancestor geometry fallback, source remapping, model hints, tabular aggregation, cached bindings, and mutable backend scattering | +| Structured agent explanations | Object, instance, scope, application, binding, call, model-bundle, environment, schedule, writer, execution-plan, output, and retention explanations returning structured rows | +| Output ownership and retention | Application-qualified streams, `OutputRouting`, `OutputRequest(application=...)`, dynamic-object exports, and bounded policy-specific dependency histories | +| MAESPA acceptance case | `build_maespa_unified_scene` and `run_maespa_scene_example` use `ObjectTemplate`, `ObjectInstance`, `AppliesTo`, `Inputs`, `Calls`, and `TimeStep`; verified by `test/test-maespa-domain-example.jl` | +| Documentation and migration | Scene/object-first README, home page, quickstart, execution guide, public API, migration guide, and explicitly labeled legacy reference sections | + +## Verification + +The following gates passed from a clean, controllable Kaimon Julia session: + +```text +test/test-unified-scene-object-api.jl +test/test-maespa-domain-example.jl +test/test-multirate-output-export.jl +test/runtests.jl +docs/make.jl +git diff --check +``` + +The complete package suite finished successfully in approximately ten minutes. +The documentation build, including examples and doctests, also completed +successfully. + +`Aqua.jl` was not installed in the active environment, so the optional +`lint_package` gate could not be run. This is not a package test dependency and +does not weaken the behavioral verification above. + +## Compatibility Boundary + +Historical mapping/domain source and tests remain intentionally available for: + +- migration comparison; +- regression evidence; +- downstream users that need a qualified transition path. + +They must remain qualified as `PlantSimEngine.*` and must not be presented as +the primary API. Removing this compatibility layer entirely is a separate +cleanup decision, not unfinished scene/object functionality. + +Requested output histories are still materialized after a run. Dependency-only +temporal histories are bounded, but a fully online output sink would be an +additional optimization rather than a missing requirement of this redesign. diff --git a/docs/src/dev/unified_scene_object_design.md b/docs/src/dev/unified_scene_object_design.md index e92fffa7e..e8a22c838 100644 --- a/docs/src/dev/unified_scene_object_design.md +++ b/docs/src/dev/unified_scene_object_design.md @@ -64,6 +64,11 @@ The engine must not prescribe a plant architecture. A plant can be described as `Plant -> Metamer -> Organ`, or another topology. The engine only needs object identity, labels, and relations. +Existing `MultiScaleTreeGraph.Node` topologies enter the same registry through +`objects_from_mtg(root; ...)` or `Scene(root; ...)`. The adapter traverses once +and accepts accessors for ids, labels, status, and geometry; the timestep +runtime does not query the MTG topology. + ### Scale A scale is a label on objects, not a separate runtime layer. Examples: @@ -110,6 +115,22 @@ query applied to an axis-scale model would mean "the leaves inside this axis". Scene-level models widen the scope explicitly with `within=SceneScope()`. +Topology-relative selections use `Relation(...)`: + +```julia +One(Relation(:parent)) +Many(Relation(:children)) +Many(Relation(:ancestors), Scale(:Plant)) +Many(Relation(:descendants), Scale(:Leaf)) +Many(Relation(:siblings)) +``` + +Supported relations are `:self`, `:parent`, `:children`, `:ancestors`, +`:descendants`, and `:siblings`. They resolve relative to the current model +application object. An explicit `within=...` scope intersects the relation +result; inferred default scopes do not hide parents or siblings. Relation +results are normalized to stable object-id order before bindings are compiled. + ### Object Template And Instance An object template is a reusable model/parameter bundle, for example one oil @@ -360,6 +381,25 @@ Policy precedence should stay explicit: Cross-rate links must go through temporal state even when they point to objects that could otherwise be reference-wired. +Same-timestep feedback cycles are broken explicitly on the receiving input: + +```julia +ModelSpec(CarbonState()) |> + Inputs( + PreviousTimeStep(:carbon_biomass) => One( + scale=:Plant, + process=:carbon_allocation, + var=:carbon_biomass, + ), + ) +``` + +`PreviousTimeStep(:x)` removes the producer-to-consumer edge from the current +timestep graph and reads the latest source sample at or before the previous +scene timestep. Before a source sample exists, the initialized consumer status +value for `x` is used. This makes initialization part of the scenario contract +instead of silently inventing a zero value. + ### Model Calls Use `Calls(...)` when a model must manually run selected models, typically @@ -378,8 +418,10 @@ ModelSpec(SceneEB()) |> ``` Inside `run!`, the scene model receives call handles and calls -`run_call!(call; publish=false)` during trial iterations, then -`publish=true` for the accepted final solution. +`run_call!(call)` during trial iterations, then +`run_call!(call; publish=true)` for the accepted final solution. The default is +deliberately `publish=false`: trial calls mutate target status for convergence +checks but do not append temporal samples or write environment outputs. The important user rule is: @@ -388,6 +430,19 @@ The important user rule is: calls; - call outputs are published only according to the call publication contract. +`explain_calls(compiled)` exposes this as +`publication_policy=:explicit_accept`, with `default_publish=false` and +`accepted_publish=true`. + +Binding and call explanations also report where each dependency declaration +came from: + +- `origin=:inferred_same_object` for compiler-inferred value dependencies; +- `origin=:model_default` for `Input(...)` or `Call(...)` declarations coming + from `dep(model)`; +- `origin=:model_spec` for scenario-level `Inputs(...)` or `Calls(...)`, + including declarations that override a model default. + ### Multiplicity Selection multiplicity is explicit: @@ -398,6 +453,11 @@ Many(...) OptionalOne(...) ``` +`OptionalOne(...)` resolves to zero or one dependency. With zero matches, an +input keeps its `inputs_` default and a call returns an empty +`call_targets(...)` collection. Explanations retain these unresolved +optional bindings instead of hiding them. + The compiler validates that `One(...)` resolves to exactly one producer per consumer scope. `Many(...)` returns a vector-like value or target collection. @@ -524,6 +584,34 @@ provider. `meteo_outputs_(model)` declares what a model can write back to a mutable microclimate provider. Simple global meteorology remains the default provider. +Scenario-level environment source remapping belongs on `Environment(...)`, for +example: + +```julia +ModelSpec(LeafGasExchange(); name=:gas_exchange) |> + AppliesTo(Many(scale=:Leaf)) |> + Environment(provider=:global, sources=(CO2=:Ca,)) +``` + +Here the model still reads `meteo.CO2` because that is its declared generic +contract, while the active environment backend samples the source variable +`:Ca`. Environment binding refresh validates source availability when the +backend can enumerate variables, and explanations report both +`required_inputs` and `source_inputs`. + +Global tabular meteorology follows the model application's compiled +`TimeStep(...)`. PlantMeteo samples the table with the reducer and window from +`meteo_hint(...)` when the model runs more slowly than the weather base step. +An `Environment(; sources=...)` override replaces only the source variable; it +does not discard the model-author reducer. The prepared weather sampler is +compiled once, and one sampled row is reused by every object targeted by the +same application at that timestep. + +Spatial or mutable backends retain control of their own temporal semantics. +PlantSimEngine supplies the compiled object/cell binding and current simulation +time; a specialized microclimate backend decides whether its local state is +instantaneous, interpolated, or internally integrated. + ### Cached Environment Bindings Spatial lookup must not happen for every model call. At initialization and when @@ -582,6 +670,13 @@ The runtime representation is an implementation detail: - call links use `ModelCall` or an equivalent callable runtime handle; - environment links use cached `EnvironmentBinding`s. +The final execution plan should group contiguous targets with the same concrete +model, status, model-bundle, input-binding, and environment-binding types. +Dynamic dispatch may occur once at the application/batch boundary, but not for +every leaf in a homogeneous target set. Exceptional model overrides form +separate concrete batches while preserving stable object order. Lifecycle or +environment refreshes rebuild these batches before the next timestep. + The public explanation API must describe the normalized graph, not the internal carrier choice. @@ -599,6 +694,8 @@ explain_calls(sim) explain_environment_bindings(sim) explain_schedule(sim) explain_writers(sim) +explain_execution_plan(sim) +explain_output_retention(sim) ``` These helpers should return stable structured data, not only pretty text. A @@ -617,6 +714,15 @@ binding row should include at least: - reason the binding was chosen; - whether it came from inference, `dep(model)`, or `ModelSpec`. +Execution-plan rows should additionally expose the selected object ids, +concrete model/status/carrier types, batch size, and whether the inner loop is +homogeneous and specialized. + +Output-retention rows should expose the retained application id, variable, +retention reasons, compiled retention horizon, and current target count so +agents can distinguish default retain-all behavior, requested output streams, +and bounded temporal-dependency streams. + Errors should report concrete object labels, scope selectors, process names, variables, and suggested fixes. diff --git a/docs/src/dev/unified_scene_object_implementation_plan.md b/docs/src/dev/unified_scene_object_implementation_plan.md index 1ba7e8df7..910eba055 100644 --- a/docs/src/dev/unified_scene_object_implementation_plan.md +++ b/docs/src/dev/unified_scene_object_implementation_plan.md @@ -68,6 +68,18 @@ should reproduce the same capabilities through unified object selections. reports the global scene scope, each object subtree, each named `Scope(...)`, and label groups by scale, kind, and species with concrete resolved object ids. +- Selector cardinality and named-scope failures now report the consumer + context, matched object ids, requested criteria, available scales, kinds, + species, and names inside the resolved scope, plus bounded edit-distance + suggestions. +- `Relation(...)` selectors now resolve `:self`, `:parent`, `:children`, + `:ancestors`, `:descendants`, and `:siblings` relative to the consuming + object. Explicit scopes constrain relation results, default dependency scopes + do not erase parent/sibling queries, and compiled `Inputs(...)` can use these + relations without runtime selector resolution. +- `ObjectAddress(selector)` now normalizes positional `Kind`, `Species`, + `Scale`, scope, and `Relation` selectors instead of recording only keyword + criteria. - Started the object-address compiler with `compile_scene(scene, specs)` and compiled scene application/binding carriers. The compiler now resolves `AppliesTo(...)` target object ids, object-relative `Inputs(...)` source @@ -89,7 +101,7 @@ should reproduce the same capabilities through unified object selections. `Inputs(...)`, and exactly one other application on the same object outputs the same variable, `compile_scene` creates an inferred reference binding. `explain_bindings` now reports binding `origin` values such as - `:declared` and `:inferred_same_object`. + `:model_default`, `:model_spec`, and `:inferred_same_object`. - Compiled input bindings now carry producer metadata. When an `Inputs(...)` selector uses `process=` or `application=`, `compile_scene` validates that a matching source application exists for the selected source objects. @@ -104,6 +116,10 @@ should reproduce the same capabilities through unified object selections. Each required input must either have a compiled binding or already exist on the target object `Status`; otherwise compilation errors with the concrete application id, object id, and input variable. +- Scene compilation creates an empty `Status` for model-targeted objects when + status is omitted, inserts missing `outputs_` and `meteo_outputs_` fields + from model defaults, and inserts explicitly or implicitly bound input fields + from `inputs_` defaults. Unbound external inputs remain compilation errors. - `compile_scene` now rejects `Inputs(...)` declarations whose left-hand variable is not declared by the target model's `inputs_`. This catches misspelled or stale scenario bindings before they create silent unused @@ -113,8 +129,22 @@ should reproduce the same capabilities through unified object selections. have `Status` values, the requested source variable must resolve to references instead of silently compiling to an unused/no-op binding. - Carrier compilation preserves source `Status` references and arbitrary value - types; tests cover both scalar refs and many-object vectors with a custom - non-`Float64` value type. + types; tests cover scalar refs, heterogeneous many-object vectors, and a + homogeneous dual-like `BigFloat` value through `RefVector`, model arithmetic, + source mutation, and typed output publication. +- Same-object renaming is supported directly by `Inputs(...)`, for example + `Inputs(:renamed_signal => One(within=Self(), var=:signal))`. The compiler + aliases the source `Ref`, records the renamed source variable in + `explain_bindings`, and schedules the producer before the consumer. +- Same-rate input carriers are installed directly into consumer `Status` + reference cells during scene compilation. Scalar bindings share the source + `Ref`; many-object bindings store the compiled `RefVector` or + `ObjectRefVector` once. The timestep runtime performs no assignment for + these bindings, and a focused `Many(...)` materialization gate verifies zero + allocations after compilation. +- Reference wiring adds a missing bound input field to the consumer `Status` + schema when needed, instead of requiring users to duplicate compiler-owned + input placeholders. - Added call ambiguity validation in the compiled scene view: a call can select by process when unique, and must use `application=:name` when several model applications with the same process match the same object. @@ -135,6 +165,40 @@ should reproduce the same capabilities through unified object selections. binding refreshes now call `update_index!(backend, entities)` once per distinct backend before `bind_environment`, giving spatial backends a current scene-wide object/entity list for precomputed microclimate lookup. +- Automatic spatial binding now uses the nearest ancestor geometry when a + target object has no geometry of its own. Existing backends still receive an + `Object` carrying the target id/status, while its binding-time geometry comes + from the ancestor. Explanations report `geometry_source=:self`, `:ancestor`, + or `:global` and the source object id. +- Moving an object invalidates environment bindings for descendants that + inherit its geometry, stopping at descendants with their own geometry. This + preserves unaffected cached bindings. +- Environment refresh now reconciles model environment contracts against + cached spatial bindings. If only `meteo_inputs_` or `meteo_outputs_` changes + while application id, object, process, provider, backend, status, and geometry + provenance remain unchanged, required/output metadata is updated while the + cached cell is reused without `update_index!` or `bind_environment`. +- `validate_meteo_inputs(scene)` and + `validate_meteo_inputs(compiled_scene, meteo_or_backend)` now validate + scene/object model application `meteo_inputs_` against the active + environment or an explicit replacement. Errors report scene application ids, + so duplicate process applications remain diagnosable. +- `Environment(; sources=(target=:source,))` now remaps model-facing + environment variables to backend source variables. Environment binding + refresh validates missing source variables when the backend can enumerate its + variables, and `explain_environment_bindings` reports both `required_inputs` + and `source_inputs`. +- Scene applications now infer model-author default environment source remaps + from `meteo_hint(...).bindings` when the scenario does not provide explicit + meteo bindings. Scenario `Environment(; sources=...)` keeps precedence over + the trait. +- Global tabular meteorology is now sampled at each scene application's + compiled clock. `meteo_hint(...).bindings` reducers and windows are applied + through PlantMeteo, while `Environment(; sources=...)` replaces only the + source variable and preserves the selected reducer. Prepared weather + samplers are shared by applications using the same weather table, and each + application/timestep sample is cached once per run so all target objects + reuse it. - Object creation, removal, and reparenting invalidate both structural and environment bindings. Object movement invalidates only environment bindings, so moving a leaf or changing its geometry can refresh microclimate lookup @@ -142,8 +206,9 @@ should reproduce the same capabilities through unified object selections. - Added public geometry invalidation helpers: `update_geometry!(scene, object, geometry; invalidate_environment=true)` and object-scoped `mark_environment_binding_dirty!(scene, object)`. - These currently route to the scene environment binding cache invalidation; - finer-grained per-object dirty tracking can be added behind the same API. + Geometry-only changes record the affected object ids and refresh only their + compiled environment bindings; descendants inheriting moved ancestor + geometry are invalidated as part of the same object-scoped refresh. - Started scene/object execution with `run!(scene; steps=...)`. The runtime refreshes compiled object bindings and environment bindings, materializes precompiled `Inputs(...)` carriers into consumer `Status` @@ -151,9 +216,25 @@ should reproduce the same capabilities through unified object selections. kernels through the existing `run!` contract. - Scene/object execution now publishes model outputs to scene-local temporal streams. Compiled `Inputs(...)` bindings marked as `:temporal_stream` can - materialize `HoldLast`, `Integrate`, and `Aggregate` values before the - consumer runs, using selector source ids, source variables, windows, and the - scene base timestep. + materialize `HoldLast`, `Interpolate`, `Integrate`, and `Aggregate` values + before the consumer runs, using selector source ids, source variables, + windows, and the scene base timestep. +- Scene temporal `Inputs(...)` now honor producer `output_policy(...)` traits + when the selector omits `policy=...` and resolves to a unique source + application. Explicit selector policies remain scenario-level overrides. +- Scene `Interpolate(...)` matches the established multirate runtime: + bracketed samples use linear interpolation, online consumers use linear + extrapolation from the last two samples when requested, and insufficient or + non-interpolable values fall back to hold-last. `mode=:hold` and + `extrapolation=:hold` are supported, invalid modes fail during scene + compilation, and interpolation arithmetic preserves generic numeric value + types without converting model values to `Float64`. +- Unified `Inputs(...)` now supports explicit lagged dependencies with + `PreviousTimeStep(:input) => selector`. Lagged bindings use temporal streams, + read source samples at or before `t - 1`, preserve the initialized consumer + status value until history exists, and do not add a same-timestep scheduling + edge. This allows feedback cycles to compile without changing generic model + kernels. - Scene/object execution now scatters mutable environment outputs declared by `meteo_outputs_(model)` back to the bound backend after each model call. This reuses `scatter_environment_outputs!`, so environment writers keep the @@ -164,6 +245,12 @@ should reproduce the same capabilities through unified object selections. `CompiledScene` now reports each application clock, phase, timestep in base steps, timestep duration in seconds, and whether the application is scheduled as a root application or is manual-call-only. +- Scene application scheduling now also honors a model's `timespec(...)` trait + when `TimeStep(...)` is omitted. Scenario-level `TimeStep(...)` keeps + precedence over the model trait, matching the established multirate runtime. +- Scene application scheduling now validates `timestep_hint(...)` required + bounds for base-step-derived clocks. Hints remain compatibility constraints; + they do not override explicit `TimeStep(...)` or non-default `timespec(...)`. - `compile_scene` now computes a stable topological application order from resolved `Inputs(...)` producer edges and `Updates(...)` writer-order edges. Inputs produced by manual-call-only applications are redirected to the parent @@ -172,7 +259,7 @@ should reproduce the same capabilities through unified object selections. time, and `explain_schedule` reports `execution_index`. - `CompiledScene` now pre-indexes input and call bindings by `(application_id, object_id)`. Per-object input materialization and - `dependency_target(s)` lookup use these indexes instead of scanning every + `call_target(s)` lookup uses these indexes instead of scanning every binding in the scene at each model call. - `CompiledScene` now also pre-indexes applications by application id. Hard-call target resolution and stable ordered-application materialization @@ -182,7 +269,7 @@ should reproduce the same capabilities through unified object selections. output scattering use direct lookup instead of scanning all environment bindings for every model invocation. - Added `SceneRunContext` and `SceneCallTarget`. Models can retrieve manual - `Calls(...)` targets with `dependency_target(s)(extra, :name)` and execute + `Calls(...)` targets with `call_target(s)(extra, :name)` and execute them with `run_call!`, preserving explicit call-stack control in the scene/object runtime. Manual calls execute immediately under the parent call stack; applications selected by `Calls(...)` are skipped by the root @@ -268,11 +355,129 @@ should reproduce the same capabilities through unified object selections. template `kind` and `species` labels. Instance explanations derive membership from the current topology, so growth, pruning, and reparenting do not leave a separate stale membership list. - -This progress is still a bridge over the existing compiler, not the final -object-address compiler. Supported `Inputs(...)` and `Calls(...)` selectors are -translated to current carriers where possible. Unsupported object-relative -selectors remain structured metadata until the object-address compiler lands. +- Scene hard calls now accept explicit local meteorology: + `run_call!(target; meteo=local_meteo, publish=false)`. The callee receives + the supplied meteo object instead of resampling the bound environment, while + `publish=false` still suppresses output publication and environment + scattering. This supports iterative microclimate solvers such as the MAESPA + scene energy-balance loop. +- Scene runtime now builds a small process-keyed `models` bundle from compiled + `Calls(...)` edges before invoking a model kernel. This lets existing generic + hard-dependency kernels such as `Monteith` continue to call + `models.photosynthesis`, and lets `Fvcb` call + `models.stomatal_conductance`, without wrapping or rewriting the model + implementation. +- Scene duplicate-writer validation now ignores manual-call-only applications + when validating canonical root writers. This keeps hard-dependency children + from being treated as independent root writers when they intentionally update + the same object status inside their parent call stack. +- Added a unified scene/object MAESPA example path: + `build_maespa_unified_scene(...)` and `run_maespa_scene_example(...)`. + It uses `ObjectTemplate`, `ObjectInstance`, `AppliesTo`, `Inputs`, `Calls`, + and `TimeStep(Dates.Period)` with two plant species, one shared soil object, + scene LAI, and scene energy balance. +- `test/test-maespa-domain-example.jl` now verifies the unified scene/object + MAESPA path alongside the existing domain regression path. +- `run!(scene)` now returns a `SceneSimulation` wrapper with the mutated + `Scene`, compiled scene bindings, compiled environment bindings, and the + scene-local temporal output streams. This keeps existing status mutation + behavior but makes scene outputs inspectable after a run. +- Added `scene_outputs(sim::SceneSimulation)`, `collect_outputs(sim)`, and + `explain_outputs(sim)` for scene/object runs. The explanation reports object + ids, variables, publishing application ids, sample counts, time bounds, and + value types. +- `run!(scene; tracked_outputs=...)` now accepts `OutputRequest` and returns + requested scene outputs through `collect_outputs(sim)` or + `collect_outputs(sim, :request_name)`. Scene requests are materialized from + retained typed temporal streams after the run. They support `HoldLast`, + `Interpolate`, `Integrate`, and `Aggregate`, `Dates.Period` export clocks, + canonical-publisher inference when unique, explicit `process=...` + selection, and dynamic objects over each object's own published sample + interval. +- Scene output requests now compile a publisher-level retention plan. With + `tracked_outputs=nothing`, the scene runtime retains all output streams for + historical inspection. With explicit `tracked_outputs`, including an empty + request vector, it retains only requested publisher streams plus streams + required by temporal `Inputs(...)`. Dependency-only streams are pruned after + publication to the compiled policy horizon: latest-only for `HoldLast`, the + input window for `Integrate`/`Aggregate`, and enough source history for + `Interpolate`/`PreviousTimeStep`. Explicitly requested streams retain their + complete histories for post-run export. Export is therefore not yet fully + online, but unrequested temporal dependencies no longer grow for the full + simulation. +- Added `explain_output_retention(sim)` to report which application/variable + streams are retained and whether the reason is default retention, an output + request, or a temporal dependency. Dependency-only rows also report their + compiled `retention_steps`; unbounded requested/default rows report + `nothing`. +- Scene temporal streams are now keyed by application id, object id, and + variable. Multiple applications can publish the same variable on the same + object without overwriting each other's stream samples. +- Scene output-export tests now cover requested-output `DataFrame` + materialization, canonical publisher inference when `process=` is omitted, + rejection when only stream-only publishers exist, and ambiguity when an + explicit process matches both a stream-only and a canonical publisher. +- Scene `OutputRequest(...)` now accepts `application=...` to select an + explicit application id/name when the same process is mounted more than + once. Explicit application selection can retain and export a + `:stream_only` publisher; legacy `GraphSimulation` export rejects this + scene-specific selector instead of ignoring it. +- Each scene temporal stream owns a concrete `Vector{Tuple{Float64,T}}` + selected from its first published value rather than boxing all values as + `Any`. Output type changes fail explicitly, and `Interpolate`/`Integrate` + tests verify `BigFloat` histories and reduced values remain `BigFloat`. +- Scene `OutputRouting(; var=:stream_only)` now matches the unified graph + semantics: stream-only outputs are excluded from canonical writer validation + and same-object input inference, while remaining available in output streams + and explicit `Inputs(..., application=:name)` selections. +- `run!(scene; steps=...)` now refreshes dirty structural bindings at timestep + boundaries. Objects created, removed, or reparented by a model during one + timestep update `AppliesTo(...)` target sets, input carriers, call targets, + writer validation, and scheduling before the next timestep. +- Geometry-only mutations refresh environment bindings at the next timestep + without recompiling structural bindings. The returned `SceneSimulation` + always contains final compiled structural and environment bindings, including + mutations performed on the last step. +- Environment dirty tracking is now object-scoped for geometry-only changes. + `move_object!`, `update_geometry!`, and + `mark_environment_binding_dirty!(scene, object)` retain unaffected compiled + bindings and re-run `bind_environment` only for applications targeting the + changed object. Structural changes and provider-wide invalidation still + rebuild the complete environment cache. +- Runtime lifecycle tests cover a model-created leaf joining a leaf + application and plant-local `RefVector`, a pruned leaf leaving both before + the next step, and a moved leaf switching mock microclimate cells. +- `CompiledScene` now precompiles a process-keyed model bundle for every + `(application_id, object_id)` target. Generic hard-dependency kernels receive + this cached bundle through their existing `models` argument without + traversing `Calls(...)` or allocating temporary collections in the timestep + hot loop. +- Added `explain_model_bundles(compiled)` to report the process names and model + types available to every application/object kernel call. Tests verify + zero-allocation repeated bundle lookup and the MAESPA leaf bundle + `energy_balance -> photosynthesis -> stomatal_conductance`. +- Root scene execution now compiles contiguous homogeneous target batches. + Each target prebinds its concrete model, `Status`, process-keyed model bundle, + input-binding tuple, and environment binding. Runtime dispatch occurs once + at the batch function barrier; the inner object loop is specialized on a + concrete target type. +- Exceptional object overrides with another concrete model implementation + become separate batches without changing stable object execution order. + Structural or environment binding refresh recompiles the execution plan + before the next timestep. +- Added `explain_execution_plan(scene_or_simulation)`. It reports batch object + ids, concrete model/status/carrier types, batch sizes, and inner-loop dispatch + semantics. A focused 128-leaf gate verifies zero allocations inside a warmed + homogeneous no-output batch. +- Added `objects_from_mtg(root; ...)` and `Scene(root::MultiScaleTreeGraph.Node; + ...)`. Existing MTG topology is traversed once into the unified registry, + preserving stable node-derived ids, parent relations, labels, geometry, and + existing `:plantsimengine_status` objects through configurable accessors. + +The scene/object compiler is now executable: selectors normalize to object +addresses, resolve before runtime, and compile into reference, temporal, call, +writer, and environment carriers. The older mapping/domain compilers remain +only as qualified compatibility implementations. ## Phase 0: Public Contract Freeze @@ -523,6 +728,28 @@ Acceptance tests: - an iterative scene model can run selected leaf and soil calls several times with `publish=false` and publish only the accepted state. +Implemented: + +- `run_call!(::SceneCallTarget)` now defaults to `publish=false`, matching the + manual-call contract and the legacy domain target behavior. +- One-shot accepted calls use `publish=true` explicitly. +- An iterative hard-call regression executes two default non-publishing trials + followed by one accepted call and verifies exactly one environment write and + one temporal output sample for the accepted state. +- `explain_calls(compiled)` reports + `publication_policy=:explicit_accept`, `default_publish=false`, and + `accepted_publish=true` for every compiled call edge. +- `ModelSpec` now retains per-binding provenance for value inputs and manual + calls. Bindings from `dep(model)` are reported as `:model_default`, + scenario-level `Inputs(...)` and `Calls(...)` are reported as `:model_spec`, + and compiler-created same-object value links are reported as + `:inferred_same_object`. `explain_bindings`, `explain_calls`, and + `explain_model_specs` expose these origins for agent-readable diagnostics. +- Zero-match `OptionalOne(...)` dependencies remain compiled and visible. + Optional inputs retain the consumer `inputs_` default with + `carrier_kind=:optional_default`; optional calls expose an empty target set + and `resolved=false` instead of failing compilation. + ## Phase 5: Object Templates, Instances, And Overrides Goal: support several plants of the same species with shared default models and @@ -567,10 +794,11 @@ Acceptance tests: - one palm instance can override one process parameter; - allocation remains per plant while scene LAI sees all leaves. -Current remaining work: +MAESPA status: -- migrate the repeated plant construction in the MAESPA example to templates - once its complete object topology is represented by the unified runtime. +- the unified scene/object MAESPA path now uses `ObjectTemplate` and + `ObjectInstance` for species A and B. The old domain path remains only as + regression coverage until the domain runtime can be removed. ## Phase 5B: Object Lifecycle And Cache Invalidation @@ -639,6 +867,8 @@ Implement: `bind_environment`, `sample_environment`, `scatter_environment!`, and `refresh_environment!`. - `Environment(...)` overrides for scenario-specific resolver/backend choices. +- `Environment(; sources=(CO2=:Ca,))` for scenario-specific environment source + remapping without changing model kernels. Runtime rule: @@ -652,6 +882,17 @@ call. Acceptance tests: - global meteo gives the same values to all objects; +- missing global meteo variables fail during environment binding refresh when + the backend can enumerate variables; +- `Environment(; sources=...)` remaps backend variables to model-facing + `meteo_inputs_` names and is visible in explanations; +- a model running every two hours over hourly global meteorology receives a + windowed weather sample rather than only the current raw row; +- model `meteo_hint` reducers/windows are honored, and an + `Environment(; sources=...)` override changes the source without discarding + the reducer; +- all objects targeted by one application reuse one global weather sample per + application/timestep; - mock grid backend binds leaves to cells once at initialization; - moving one leaf marks only that leaf binding dirty and refreshes it before the next timestep; @@ -674,6 +915,8 @@ Implement: - multirate scheduling based on `Dates.Period` values in `TimeStep(...)` and input windows; - typed compiled bindings that avoid selector resolution in timestep hot loops; +- typed homogeneous execution batches that move dynamic dispatch outside the + per-object inner loop while preserving ordered heterogeneous overrides; - arbitrary value type preservation through status, input carriers, temporal storage, and environment samples; - structured explanation: @@ -695,6 +938,8 @@ Acceptance tests: `dep(model)`, or `ModelSpec`, and reports carrier/copy semantics. - no selector resolution occurs inside the per-object, per-model timestep loop for static scenes. +- a warmed homogeneous execution batch performs no allocations beyond model, + output-stream, or backend work requested by the application itself; - multirate simulations use the same object-address graph as same-rate simulations. @@ -728,17 +973,130 @@ Write migration notes: Regression tests must cover all migrated examples before removal. -## Open Decisions - -- Final names for `SceneScope`, `Self`, `SelfPlant`, `Kind`, `Species`, and - `Scale`. -- Whether `Inputs(...)` should be a pipeable `ModelSpec` modifier only, or also - accepted as a keyword in `ModelSpec`. -- Whether `TimeStep(...)` is the final name, or whether the existing - `TimeStepModel(...)` should be kept as an alias during the transition. -- Whether `Environment(...)` owns only backend/resolver choices or also - per-model meteo window policies. -- Whether object templates should own topology construction helpers or only - consume prebuilt MTGs/object trees. -- How much of the old API remains as compatibility wrappers after the breaking - release. +Migration documentation progress: + +- Added `docs/src/migration_scene_object.md` with direct translations for + `MultiScaleModel`, `Route`, `AllDomains`, `HardDomains`, repeated `Domain` + instances, `TimeStepModel`, `InputBindings`, `MeteoBindings`, + `ScopeModel`, and `SameScale`. +- Documentation navigation and the home page now identify the scene/object API + as the target for new multiscale and multi-plant work. Legacy domain and + multiscale pages are explicitly labeled. +- The documentation home page now uses executable scene/object examples as the + primary quickstart. It shows `Scene`, `Object`, `ModelSpec`, `AppliesTo`, + `Inputs`, `TimeStep`, automatic same-object binding inference, multi-object + `Many(...)` inputs, and manual `Calls(...)` syntax before linking to legacy + mapping tutorials. +- The repository README now mirrors the scene/object entry point instead of + teaching `ModelMapping` first. It includes smoke-tested `Scene`/`Object` + quickstart code, `Inputs(...)` multi-object coupling, conceptual + `Calls(...)` syntax, and links to the migration guide for compatibility + APIs. +- Added `docs/src/scene_object/quickstart.md` as the first native + scene/object tutorial page and promoted it in the documentation navigation. + The page contains docs-tested examples for one-object model chaining, + inferred same-object bindings, `OutputRequest` retention, multi-object + `Inputs(...)`, `RefVector` carrier explanations, and manual `Calls(...)` + syntax. +- The repository agent skill now teaches the unified public vocabulary first + and treats the old mapping/domain stack as legacy-maintenance knowledge. +- The public API page now starts with curated scene/object groups for scenario + construction, selectors, coupling, lifecycle, environment, runtime, and + structured explanations. Mapping-level multirate examples are labeled as + legacy. + +Current removal audit: + +- The legacy domain scenario surface is no longer exported: + `Domain`, `SimulationMapping`, `DomainSimulation`, `AllDomains`, + `HardDomains`, `Route`, route cardinalities, domain target helpers, and + domain explanation helpers remain available only through qualified + `PlantSimEngine.*` names for regression coverage. +- Public manual-call control now uses `call_target`, `call_targets`, and + `run_call!`. The old `dependency_target(s)` and `run_target!` spellings are + compatibility internals. +- `SceneRunContext` now defines `call_target(s)` directly, including + string-name support. The old `dependency_target(s)` scene methods are thin + compatibility wrappers around the scene-native lookup. +- The legacy mapping transforms are no longer exported: + `MultiScaleModel`, `SameScale`, `TimeStepModel`, `InputBindings`, + `MeteoBindings`, `MeteoWindow`, and `ScopeModel` require qualified + `PlantSimEngine.*` access in compatibility tests and historical examples. +- `ModelMapping` is no longer exported. Retained mapping tests, examples, + benchmarks, doctests, and historical tutorials explicitly use + `PlantSimEngine.ModelMapping(...)`, making the compatibility boundary + visible without deleting regression coverage. +- Historical MTG mapping and mapping-level multirate pages are grouped under + explicitly named legacy sections in the documentation. Executable examples + use qualified compatibility constructors so they cannot be mistaken for the + exported public API. +- The home page, execution page, MTG mapping tutorials, and mapping-level + multirate tutorials now identify their compatibility status and direct new + work to the scene/object API. A future documentation cleanup can replace + these historical pages with equivalent scene/object tutorials rather than + retaining them as migration reference. +- The model execution page has been rewritten as a scene/object-first guide. + It now documents compilation, same-rate reference carriers, temporal + `Inputs(...)`, manual `Calls(...)`, `Updates(...)`, `TimeStep(...)`, + environment binding, output retention, lifecycle cache invalidation, and + compatibility translations from the old mapping/domain runtime. +- The detailed first simulation tutorial now starts from the scene/object API + instead of `ModelMapping`. It introduces model kernels, object status, + compiled applications, inferred same-object bindings, scene outputs, and a + short compatibility note for historical mapping examples. +- The quick examples page now uses copy-pasteable scene/object examples for + Beer light interception, degree-days/LAI/light coupling, biomass growth, and + retained `OutputRequest` exports. `ModelMapping` appears only in the + compatibility note. +- The standard model coupling, model switching, and coupling more complex + models step-by-step tutorials now teach `Scene`, `Object`, `ModelSpec`, + `AppliesTo`, `TimeStep`, inferred soft `Inputs(...)`, and manual + `Calls(...)` first. Historical `PlantSimEngine.ModelMapping(...)` appears + only in compatibility notes on those pages. +- The home page has been replaced by native scene/object examples. The + repository README has also been replaced by native scene/object examples, + and a dedicated scene/object quickstart is now available in the main + documentation navigation. +- Legacy multirate and mapping tests remain intentionally as qualified + compatibility regression coverage. Equivalent scene/object tests now cover + scheduling, temporal policies, binding inference and overrides, meteo + contracts and aggregation, output routing and application-qualified export, + and structured explanations. The legacy constructors are already absent + from exports. +- Scene/object tests now include public meteo-contract validation parity for + missing environment variables, explicit `Environment(; sources=...)` + remapping, model-author `meteo_hint` source defaults, and validation against + an explicit replacement meteo object/backend. +- Test code has been migrated from `TimeStepModel(...)` to the canonical + `TimeStep(...)` spelling. Remaining legacy transform tests now qualify + legacy-only helpers such as `PlantSimEngine.InputBindings`, + `PlantSimEngine.ScopeModel`, and `PlantSimEngine.MultiScaleModel` in the + focused scaffolding test, reducing reliance on those names as exported + public API. +- The unified MAESPA path is implemented and tested, so the legacy MAESPA + domain path is regression-only evidence. Its legacy constructors are + qualified explicitly, while the migrated kernel uses `AppliesTo`, `Inputs`, + `Calls`, `call_target(s)`, and `run_call!`. + +## Resolved API Decisions + +- `SceneScope`, `Self`, `SelfPlant`, `Kind`, `Species`, and `Scale` are the + public selector names. +- `Inputs(...)` is the preferred pipeable modifier. `ModelSpec(...; inputs=...)` + is also accepted for programmatic construction. +- `TimeStep(...)` is the canonical exported name. `TimeStepModel(...)` is an + internal compatibility transform for qualified historical code. +- `Environment(...)` owns provider/resolver/source configuration. Temporal + value windows belong to the consuming `Inputs(...)` selector. +- Object templates own reusable model applications and parameters, not plant + topology construction. They consume explicit object trees or MTGs adapted + through `objects_from_mtg`. +- The old domain, route, multiscale, and mapping-transform implementations are + retained as an explicitly qualified compatibility and regression layer. + Their scenario constructors are not exported and are not part of the primary + API. + +## Completion Evidence + +The requirement-by-requirement evidence and final verification commands are +recorded in `unified_scene_object_completion_audit.md`. diff --git a/docs/src/domain_simulation.md b/docs/src/domain_simulation.md index 914c2ef42..baff9134e 100644 --- a/docs/src/domain_simulation.md +++ b/docs/src/domain_simulation.md @@ -1,5 +1,12 @@ # Domain simulations +!!! warning "Legacy configuration API" + This page documents the domain prototype retained for regression coverage. + Its constructors are not exported and require qualified + `PlantSimEngine.*` names. + New scenarios should use the unified scene/object API described in + [Migrating To The Scene/Object API](migration_scene_object.md). + Domain simulations let you reuse complete plant, soil, scene, or environment model mappings without renaming their internal scales and processes. @@ -16,7 +23,7 @@ chosen only to show domain coupling and multi-rate execution. First we define a few small models. The plant domains compute absorbed radiation and transpiration hourly. The soil domain computes water content and evaporation hourly. The scene domain runs daily and consumes all plant transpiration plus -soil evaporation through explicit `AllDomains(...)` value dependencies. These +soil evaporation through explicit `PlantSimEngine.AllDomains(...)` value dependencies. These dependencies read already-published producer streams; they do not manually run the producer models. @@ -88,13 +95,13 @@ PlantSimEngine.inputs_(::DocDomainSceneEvapotranspirationModel) = NamedTuple() PlantSimEngine.outputs_(::DocDomainSceneEvapotranspirationModel) = (evapotranspiration=0.0,) PlantSimEngine.dep(::DocDomainSceneEvapotranspirationModel) = ( - plant_transpiration=AllDomains(kind=:plant, process=:doc_domain_plant_transpiration, var=:transpiration, policy=Integrate()), - soil_evaporation=AllDomains(kind=:soil, process=:doc_domain_soil_evaporation, var=:evaporation, policy=Integrate()), + plant_transpiration=PlantSimEngine.AllDomains(kind=:plant, process=:doc_domain_plant_transpiration, var=:transpiration, policy=Integrate()), + soil_evaporation=PlantSimEngine.AllDomains(kind=:soil, process=:doc_domain_soil_evaporation, var=:evaporation, policy=Integrate()), ) function PlantSimEngine.run!(::DocDomainSceneEvapotranspirationModel, models, status, meteo, constants=nothing, extra=nothing) - plant_values = dependency_values(extra, :plant_transpiration) - soil_values = dependency_values(extra, :soil_evaporation) + plant_values = PlantSimEngine.dependency_values(extra, :plant_transpiration) + soil_values = PlantSimEngine.dependency_values(extra, :soil_evaporation) status.evapotranspiration = sum(filter(x -> !isnothing(x), plant_values)) + sum(filter(x -> !isnothing(x), soil_values)) @@ -109,34 +116,34 @@ could be completely different plant models, with different processes and different internal mappings. ```@example domains -oil_palm_mapping = ModelMapping( - ModelSpec(DocDomainAbsorbedRadiationModel(0.5)) |> TimeStepModel(Hour(1)), - ModelSpec(DocDomainPlantTranspirationModel(0.01)) |> TimeStepModel(Hour(1)), +oil_palm_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DocDomainAbsorbedRadiationModel(0.5)) |> PlantSimEngine.TimeStepModel(Hour(1)), + ModelSpec(DocDomainPlantTranspirationModel(0.01)) |> PlantSimEngine.TimeStepModel(Hour(1)), status=(absorbed_radiation=0.0, transpiration=0.0), ) -maize_mapping = ModelMapping( - ModelSpec(DocDomainAbsorbedRadiationModel(0.3)) |> TimeStepModel(Hour(1)), - ModelSpec(DocDomainPlantTranspirationModel(0.02)) |> TimeStepModel(Hour(1)), +maize_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DocDomainAbsorbedRadiationModel(0.3)) |> PlantSimEngine.TimeStepModel(Hour(1)), + ModelSpec(DocDomainPlantTranspirationModel(0.02)) |> PlantSimEngine.TimeStepModel(Hour(1)), status=(absorbed_radiation=0.0, transpiration=0.0), ) -soil_mapping = ModelMapping( - ModelSpec(DocDomainSoilWaterModel(0.35)) |> TimeStepModel(Hour(1)), - ModelSpec(DocDomainSoilEvaporationModel(0.2)) |> TimeStepModel(Hour(1)), +soil_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DocDomainSoilWaterModel(0.35)) |> PlantSimEngine.TimeStepModel(Hour(1)), + ModelSpec(DocDomainSoilEvaporationModel(0.2)) |> PlantSimEngine.TimeStepModel(Hour(1)), status=(soil_water_content=0.0, evaporation=0.0), ) -scene_mapping = ModelMapping( - ModelSpec(DocDomainSceneEvapotranspirationModel()) |> TimeStepModel(Day(1)), +scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DocDomainSceneEvapotranspirationModel()) |> PlantSimEngine.TimeStepModel(Day(1)), status=(evapotranspiration=0.0,), ) -simulation_mapping = SimulationMapping( - Domain(:oil_palm, oil_palm_mapping; kind=:plant), - Domain(:maize, maize_mapping; kind=:plant), - Domain(:soil, soil_mapping; kind=:soil), - Domain(:scene, scene_mapping; kind=:scene), +simulation_mapping = PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:oil_palm, oil_palm_mapping; kind=:plant), + PlantSimEngine.Domain(:maize, maize_mapping; kind=:plant), + PlantSimEngine.Domain(:soil, soil_mapping; kind=:soil), + PlantSimEngine.Domain(:scene, scene_mapping; kind=:scene), ) nothing ``` @@ -163,7 +170,7 @@ Domain simulations expose small structured explanation helpers. These are meant for users and for agents that need to repair a mapping from concrete symbols. ```@example domains -sort([(row.domain, row.kind) for row in explain_domains(sim)]) +sort([(row.domain, row.kind) for row in PlantSimEngine.explain_domains(sim)]) ``` ```@example domains @@ -172,7 +179,7 @@ sort([(row.domain, row.process, row.dt_seconds) for row in explain_schedule(sim) ```@example domains sort( - [(row.dependency, string(row.producer), row.policy) for row in explain_domain_dependencies(sim)]; + [(row.dependency, string(row.producer), row.policy) for row in PlantSimEngine.explain_domain_dependencies(sim)]; by=string, ) ``` @@ -182,26 +189,26 @@ The model-level explanation also includes the weather variables declared with `meteo_outputs_`: ```@example domains -[(row.domain, row.process, collect(keys(row.meteo_inputs))) for row in explain_domain_models(sim) if !isempty(row.meteo_inputs)] +[(row.domain, row.process, collect(keys(row.meteo_inputs))) for row in PlantSimEngine.explain_domain_models(sim) if !isempty(row.meteo_inputs)] ``` ## Explicit routes -`AllDomains(...)` value dependencies are the most direct way for a scene model +`PlantSimEngine.AllDomains(...)` value dependencies are the most direct way for a scene model to consume several domain outputs. Routes are another option when you want to materialize values into the target status before the target model runs. ```julia -Route( - from=AllDomains(kind=:plant, process=:plant_transpiration, var=:transpiration), - to=DomainRouteTarget(:scene, var=:plant_transpirations, process=:scene_evapotranspiration), - cardinality=ManyToOneVector(), +PlantSimEngine.Route( + from=PlantSimEngine.AllDomains(kind=:plant, process=:plant_transpiration, var=:transpiration), + to=PlantSimEngine.DomainRouteTarget(:scene, var=:plant_transpirations, process=:scene_evapotranspiration), + cardinality=PlantSimEngine.ManyToOneVector(), ) ``` -Use `ManyToOneVector()` when the target model needs one value per matching -producer, and `ManyToOneAggregate(f)` when it needs a scalar reduction. For an -MTG-backed target domain, `OneToManyBroadcast()` can broadcast one source value +Use `PlantSimEngine.ManyToOneVector()` when the target model needs one value per matching +producer, and `PlantSimEngine.ManyToOneAggregate(f)` when it needs a scalar reduction. For an +MTG-backed target domain, `PlantSimEngine.OneToManyBroadcast()` can broadcast one source value into every status at the target scale before that domain runs. When a route targets a single-status domain variable consumed by one target @@ -216,19 +223,19 @@ not require every timestep to publish vectors with the same length. ## Hard-domain dependencies -Use `HardDomains(...)` when a model must manually run selected producer +Use `PlantSimEngine.HardDomains(...)` when a model must manually run selected producer models, for example to control an iterative energy-balance solver: ```julia PlantSimEngine.dep(::SceneEnergyBalanceModel) = ( - leaf_energy=HardDomains(kind=:plant, scale=:Leaf, process=:leaf_energy_balance), + leaf_energy=PlantSimEngine.HardDomains(kind=:plant, scale=:Leaf, process=:leaf_energy_balance), ) function PlantSimEngine.run!(::SceneEnergyBalanceModel, models, status, meteo, constants=nothing, extra=nothing) - leaf_targets = dependency_targets(extra, :leaf_energy) + leaf_targets = PlantSimEngine.dependency_targets(extra, :leaf_energy) for iteration in 1:10 for target in leaf_targets - run_target!(target) + PlantSimEngine.run_target!(target) end converged(status) && break end @@ -248,7 +255,7 @@ calls: ```julia function PlantSimEngine.run!(::PhyllochronModel, models, status, meteo, constants, extra) - run_target!(models, status, :phytomer_emission; meteo=meteo, constants=constants, extra=extra) + PlantSimEngine.run_target!(models, status, :phytomer_emission; meteo=meteo, constants=constants, extra=extra) return nothing end ``` @@ -260,9 +267,9 @@ When running on an MTG, the `selector` decides which subtree roots belong to the domain: ```julia -oil_palm = Domain(:oil_palm, xpalm_mapping; kind=:plant, selector=node -> node[:species] == :oil_palm) -maize = Domain(:maize, maize_mapping; kind=:plant, selector=node -> node[:species] == :maize) -sim = run!(scene_mtg, SimulationMapping(oil_palm, maize, scene), meteo) +oil_palm = PlantSimEngine.Domain(:oil_palm, xpalm_mapping; kind=:plant, selector=node -> node[:species] == :oil_palm) +maize = PlantSimEngine.Domain(:maize, maize_mapping; kind=:plant, selector=node -> node[:species] == :maize) +sim = run!(scene_mtg, PlantSimEngine.SimulationMapping(oil_palm, maize, scene), meteo) ``` Status inspection keeps both the domain-local view and the global scale view: @@ -275,7 +282,7 @@ status(sim, :Leaf) # all leaves across graph-backed domains If a declared graph scale currently has no active statuses, for example after pruning all leaves, these status queries return `Status[]`. The same empty scale -is reported by `explain_domain_statuses(sim)` with `nstatuses=0`. +is reported by `PlantSimEngine.explain_domain_statuses(sim)` with `nstatuses=0`. Selectors can also match several non-overlapping roots, for example `selector=:Plant` to run one mapping over every plant subtree. Selectors that diff --git a/docs/src/index.md b/docs/src/index.md index 631157d41..6a82da6e5 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -2,33 +2,6 @@ CurrentModule = PlantSimEngine ``` -```@setup readme -using PlantSimEngine, PlantMeteo, Dates - -# Import the examples defined in the `Examples` sub-module: -using PlantSimEngine.Examples - -# Import the example meteorological data: -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) - -# Define the model mapping: -model = ModelMapping( - ToyLAIModel(); - status=(TT_cu=1.0:2000.0,), # Pass the cumulated degree-days as input to the model -) - -out = run!(model) - -# Define the mapping for coupled models: -model2 = ModelMapping( - ToyLAIModel(), - Beer(0.6); - status=(TT_cu=cumsum(meteo_day[:, :TT]),), # Pass the cumulated degree-days as input to `ToyLAIModel`, this could also be done using another model -) -out2 = run!(model2, meteo_day) - -``` - # PlantSimEngine [![Build Status](https://github.com/VirtualPlantLab/PlantSimEngine.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/VirtualPlantLab/PlantSimEngine.jl/actions/workflows/CI.yml?query=branch%3Amain) @@ -40,323 +13,270 @@ out2 = run!(model2, meteo_day) ```@contents Pages = ["index.md"] -Depth = 5 +Depth = 4 ``` -## Overview - -`PlantSimEngine` is a comprehensive framework for building models of the soil-plant-atmosphere continuum. It includes everything you need to **prototype, evaluate, test, and deploy** plant/crop models at any scale, with a strong emphasis on performance and efficiency, so you can focus on building and refining your models. - -**Why choose PlantSimEngine?** - -- **Simplicity**: Write less code, focus on your model's logic, and let the framework handle the rest. -- **Modularity**: Each model component can be developed, tested, and improved independently. Assemble complex simulations by reusing pre-built, high-quality modules. -- **Standardisation**: Clear, enforceable guidelines ensure that all models adhere to best practices. This built-in consistency means that once you implement a model, it works seamlessly with others in the ecosystem. -- **Optimised Performance**: Don't re-invent the wheel. Delegating low-level tasks to PlantSimEngine guarantees that your model will benefit from every improvement in the framework. Enjoy faster prototyping, robust simulations, and efficient execution using Julia's high-performance capabilities. - -## Unique Features - -### Automatic Model Coupling - -**Seamless Integration:** PlantSimEngine leverages Julia's multiple-dispatch capabilities to automatically compute the dependency graph between models. This allows researchers to effortlessly couple models without writing complex connection code or manually managing dependencies. - -**Intuitive Multi-Scale Support:** The framework naturally handles models operating at different scales—from organelle to ecosystem—connecting them with minimal effort and maintaining consistency across scales. - -### Flexibility with Precision Control - -**Effortless Model Switching:** Researchers can switch between different component models using a simple syntax without rewriting the underlying model code. This enables rapid comparison between different hypotheses and model versions, accelerating the scientific discovery process. - -### Multi-rate Execution - -**Mix model cadences in one simulation:** PlantSimEngine can run models at different timesteps within the same MTG simulation. This makes it possible to combine, for example, hourly leaf processes with daily plant balances and weekly reporting models without writing custom scheduling glue. +!!! warning "Configuration API migration" + New multiscale, multi-plant, soil, scene, and microclimate scenarios should + use the unified `Scene`/`Object` API with `AppliesTo`, `Inputs`, `Calls`, + `Updates`, `TimeStep`, and `Environment`. See + [Migrating To The Scene/Object API](migration_scene_object.md). The old + domain, route, and mapping constructors are no longer exported; their + implementation remains temporarily available under qualified + `PlantSimEngine.*` names for regression coverage and migration work. -**Explicit bindings between rates:** `TimeStepModel`, `InputBindings`, `MeteoBindings`, `ScopeModel`, and `OutputRequest` let you declare how model inputs, meteorology, and exported outputs should behave when rates differ. - -## Batteries included - -- **Automated Management**: Seamlessly handle inputs, outputs, time-steps, objects, and dependency resolution. -- **Iterative Development**: Fast and interactive prototyping of models with built-in constraints to avoid errors and sensible defaults to streamline the model writing process. -- **Control Your Degrees of Freedom**: Fix variables to constant values or force to observations, use simpler models for specific processes to reduce complexity. -- **Multi-Rate Scheduling**: Combine hourly, daily, and coarser models in the same simulation, with explicit policies for input aggregation and meteorological sampling. -- **High-Speed Computations**: Achieve impressive performance with benchmarks showing operations in the 100th of nanoseconds range for complex models (see this [benchmark script](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/examples/benchmark.jl)). -- **Parallelize and Distribute Computing**: Out-of-the-box support for sequential, multi-threaded, or distributed computations over objects, time-steps, and independent processes, thanks to [Floops.jl](https://juliafolds.github.io/FLoops.jl/stable/). -- **Scale Effortlessly**: Methods for computing over objects, time-steps, and [Multi-Scale Tree Graphs](https://github.com/VEZY/MultiScaleTreeGraph.jl). -- **Compose Freely**: Use any types as inputs, including [Unitful](https://github.com/PainterQubits/Unitful.jl) for unit propagation and [MonteCarloMeasurements.jl](https://github.com/baggepinnen/MonteCarloMeasurements.jl) for measurement error propagation. - -## Performance - -PlantSimEngine delivers impressive performance for plant modeling tasks. On an M1 MacBook Pro: +## Overview -- A toy model for leaf area over a year at daily time-scale took only 260 μs (about 688 ns per day) -- The same model coupled to a light interception model took 275 μs (756 ns per day) +`PlantSimEngine` is a Julia framework for building soil-plant-atmosphere +simulations from small process models. A modeler writes reusable kernels with +`inputs_`, `outputs_`, optional dependency traits, and `run!`. A simulation +author then assembles those kernels on objects in a `Scene`. -These benchmarks demonstrate performance on par with compiled languages like Fortran or C, far outpacing typical interpreted language implementations. For example, PlantBiophysics.jl, which implements ecophysiological models using PlantSimEngine, has been measured to run up to 38,000 times faster than equivalent implementations in other scientific computing languages. +The current public scenario API is organized around: -## Ask Questions +```julia +Scene +Object +ModelSpec +AppliesTo +Inputs +Calls +Updates +TimeStep +Environment +``` -If you have any questions or feedback, [open an issue](https://github.com/VirtualPlantLab/PlantSimEngine.jl/issues) or ask on [discourse](https://fspm.discourse.group/c/software/virtual-plant-lab). +This means the same model can be reused on one object, many leaves, several +plant species, a shared soil object, or a scene-scale energy-balance solver +without changing the model implementation. + +## Why PlantSimEngine? + +- **Modular models**: each process model can be developed, tested, calibrated, + and replaced independently. +- **Explicit coupling**: `Inputs(...)` declares value dependencies, while + `Calls(...)` gives iterative parent solvers manual control over hard model + calls. +- **Object-based multiscale scenes**: scales are labels on objects, so a plant + can be described as plants, axes, internodes, leaves, roots, voxels, or any + topology the model requires. +- **Multirate execution**: use `TimeStep(Dates.Hour(1))`, + `TimeStep(Dates.Day(1))`, and temporal policies such as `Integrate()` or + `HoldLast()` in the same scene. +- **Automatic environment binding**: global weather and spatial microclimate + backends are bound through `Environment(...)` and model `meteo_inputs_` / + `meteo_outputs_` traits. +- **Performance-oriented internals**: selectors and bindings are compiled + before the timestep loop, same-rate inputs use references when possible, and + homogeneous object batches are specialized. +- **Generic values**: status, model parameters, meteorology, and outputs can + carry units, automatic-differentiation values, uncertainty wrappers, or other + compatible Julia types. ## Installation -To install the package, enter the Julia package manager mode by pressing `]` in the REPL, and execute the following command: +To install the package, enter Julia package mode by pressing `]` in the REPL, +then run: ```julia add PlantSimEngine ``` -To use the package, execute this command from the Julia REPL: +Use it from Julia with: ```julia using PlantSimEngine ``` -## Example usage +## Quickstart: One Scene Object -The package is designed to be easy to use, and to help users avoid errors when implementing, coupling and simulating models. +This example runs three existing toy models on one scene object: -### Simple example +1. `ToyDegreeDaysCumulModel` computes daily thermal time. +2. `ToyLAIModel` consumes cumulative thermal time and computes LAI. +3. `Beer` consumes LAI and meteorology to compute absorbed PAR. -Here's a simple example of a model that simulates the growth of a plant, using a simple exponential growth model: +The model kernels are unchanged; the scene application layer says where they +run and at which cadence. ```@example readme -# ] add PlantSimEngine -using PlantSimEngine - -# Import the examples defined in the `Examples` sub-module +using PlantSimEngine, PlantMeteo, Dates, DataFrames using PlantSimEngine.Examples -# Define the model mapping: -model = ModelMapping( - ToyLAIModel(); - status=(TT_cu=1.0:2000.0,), # Pass the cumulated degree-days as input to the model +meteo_day = read_weather( + joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"); + duration=Dates.Day, ) -out = run!(model) # run the model and extract its outputs +scene = Scene( + Object(:scene; scale=:Scene, kind=:scene); + applications=( + ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), -out[1:3,:] -``` - -> **Note** -> The `ToyLAIModel` is available from the [examples folder](https://github.com/VirtualPlantLab/PlantSimEngine.jl/tree/main/examples), and is a simple exponential growth model. It is used here for the sake of simplicity, but you can use any model you want, as long as it follows `PlantSimEngine` interface. - -Of course you can plot the outputs quite easily: + ModelSpec(ToyLAIModel(); name=:lai) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), -```@example readme -# ] add CairoMakie -using CairoMakie + ModelSpec(Beer(0.6); name=:light_interception) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ), + environment=meteo_day, +) -lines(out[:TT_cu], out[:LAI], color=:green, axis=(ylabel="LAI (m² m⁻²)", xlabel="Cumulated growing degree days since sowing (°C)")) +sim = run!(scene; steps=30, constants=Constants()) +out = DataFrame(collect_outputs(sim; sink=nothing)) +first(out, 6) ``` -### Model coupling - -Model coupling is done automatically by the package, and is based on the dependency graph between the models. To couple models, we just have to add them to the `ModelMapping`. For example, let's couple the `ToyLAIModel` with a model for light interception based on Beer's law: +`ToyLAIModel` does not know where `TT_cu` comes from, and `Beer` does not know +where `LAI` comes from. The compiler infers the unambiguous same-object +bindings from each model's declared inputs and outputs: ```@example readme -# ] add PlantSimEngine, PlantMeteo -using PlantSimEngine, PlantMeteo, Dates - -# Import the examples defined in the `Examples` sub-module -using PlantSimEngine.Examples - -# Import the example meteorological data: -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) - -# Define the mapping for coupled models: -model2 = ModelMapping( - ToyLAIModel(), - Beer(0.6); - status=(TT_cu=cumsum(meteo_day[:, :TT]),), # Pass the cumulated degree-days as input to `ToyLAIModel`, this could also be done using another model +select( + DataFrame(explain_bindings(refresh_bindings!(scene))), + :application_id, + :input, + :source_application_ids, + :carrier_kind, + :copy_semantics, ) - -# Run the simulation: -out2 = run!(model2, meteo_day) -out2[1:3,:] -``` - -The `ModelMapping` couples the models by automatically computing the dependency graph of the models. The resulting dependency graph is: - -``` -╭──── Dependency graph ──────────────────────────────────────────╮ -│ ╭──── LAI_Dynamic ─────────────────────────────────────────╮ │ -│ │ ╭──── Main model ────────╮ │ │ -│ │ │ Process: LAI_Dynamic │ │ │ -│ │ │ Model: ToyLAIModel │ │ │ -│ │ │ Dep: nothing │ │ │ -│ │ ╰────────────────────────╯ │ │ -│ │ │ ╭──── Soft-coupled model ─────────╮ │ │ -│ │ │ │ Process: light_interception │ │ │ -│ │ └──│ Model: Beer │ │ │ -│ │ │ Dep: (LAI_Dynamic = (:LAI,),) │ │ │ -│ │ ╰─────────────────────────────────╯ │ │ -│ ╰──────────────────────────────────────────────────────────╯ │ -╰────────────────────────────────────────────────────────────────╯ ``` -We can plot the results by indexing the outputs with the variable name (e.g. `out2[:LAI]`): +The outputs can be plotted like any other tabular result: ```@example readme using CairoMakie -fig = Figure(resolution=(800, 600)) -ax = Axis(fig[1, 1], ylabel="LAI (m² m⁻²)") -lines!(ax, out2[:TT_cu], out2[:LAI], color=:mediumseagreen) +lai = out[out.variable .== :LAI, :] +appfd = out[out.variable .== :aPPFD, :] -ax2 = Axis(fig[2, 1], xlabel="Cumulated growing degree days since sowing (°C)", ylabel="aPPFD (mol m⁻² d⁻¹)") -lines!(ax2, out2[:TT_cu], out2[:aPPFD], color=:firebrick1) +fig = Figure(size=(800, 520)) +ax1 = Axis(fig[1, 1], ylabel="LAI") +lines!(ax1, lai.timestep, Float64.(lai.value), color=:mediumseagreen) + +ax2 = Axis(fig[2, 1], xlabel="Day", ylabel="aPPFD") +lines!(ax2, appfd.timestep, Float64.(appfd.value), color=:firebrick) fig ``` -### Multi-scale modeling - -> See the [Multi-scale modeling](#multi-scale-modeling) section for more details. +## Multi-Object Inputs -The package is designed to be easily scalable, and can be used to simulate models at different scales. For example, you can simulate a model at the leaf scale, and then couple it with models at any other scale, *e.g.* internode, plant, soil, scene scales. Here's an example of a simple model that simulates plant growth using sub-models operating at different scales: +Use `Inputs(...)` when a model needs values from selected objects. Here the +scene-scale LAI model reads live references to all plant surfaces in the scene: ```@example readme -mapping = ModelMapping( - :Scene => ToyDegreeDaysCumulModel(), - :Plant => ( - MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[ - :TT_cu => :Scene, - ], - ), - Beer(0.6), - MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => :Soil], - ), - MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - :carbon_demand => [:Leaf, :Internode], - :carbon_allocation => [:Leaf, :Internode] - ], - ), - MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - ), - :Internode => ( - MultiScaleModel( - model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - mapped_variables=[:TT => :Scene,], - ), - MultiScaleModel( - model=ToyInternodeEmergence(TT_emergence=20.0), - mapped_variables=[:TT_cu => :Scene], - ), - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - Status(carbon_biomass=1.0) - ), - :Leaf => ( - MultiScaleModel( - model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - mapped_variables=[:TT => :Scene,], - ), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - Status(carbon_biomass=1.0) +plant_scene = PlantSimEngine.Scene( + PlantSimEngine.Object(:scene; scale=:Scene, kind=:scene), + PlantSimEngine.Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene, + status=Status(surface=12.0)), + PlantSimEngine.Object(:plant_2; scale=:Plant, kind=:plant, parent=:scene, + status=Status(surface=8.0)); + applications=( + ModelSpec(ToyLAIfromLeafAreaModel(100.0); name=:scene_lai) |> + AppliesTo(One(scale=:Scene)) |> + Inputs( + :plant_surfaces => Many( + scale=:Plant, + within=SceneScope(), + var=:surface, + ), + ), ), - :Soil => ( - ToySoilWaterModel(), - ), -); -nothing # hide -``` - -We can import an example plant from the package: - -```@example readme -mtg = import_mtg_example() -``` - -Make a fake meteorological data: - -```@example readme -meteo = Weather( - [ - Atmosphere(T=20.0, Wind=1.0, Rh=0.65, Ri_PAR_f=300.0), - Atmosphere(T=25.0, Wind=0.5, Rh=0.8, Ri_PAR_f=500.0) -] -); -nothing # hide -``` - -And run the simulation: - -```@example readme -out_vars = Dict( - :Scene => (:TT_cu,), - :Plant => (:carbon_allocation, :carbon_assimilation, :soil_water_content, :aPPFD, :TT_cu, :LAI), - :Leaf => (:carbon_demand, :carbon_allocation), - :Internode => (:carbon_demand, :carbon_allocation), - :Soil => (:soil_water_content,), ) -out = run!(mtg, mapping, meteo, tracked_outputs=out_vars, executor=SequentialEx()); -nothing # hide -``` - -We can then extract the outputs and convert them to a `DataFrame` for each scale and sort them: - -```@example readme -using DataFrames -df_outputs = convert_outputs(out, DataFrame) -leaf_df = df_outputs isa AbstractDict ? df_outputs[:Leaf] : df_outputs -sort!(leaf_df, [:timestep, :node]) +run!(plant_scene) +scene_status = only(scene_objects(plant_scene; scale=:Scene)).status +(total_surface=scene_status.total_surface, LAI=scene_status.LAI) ``` -An example output of a multiscale simulation is shown in the documentation of PlantBiophysics.jl: - -![Plant growth simulation](www/image.png) +The same `Many(...)` selector would be plant-local if the consumer ran on a +plant and used `within=Self()`. This is the same mechanism used for plant +allocation models that sum their own leaves, scene models that aggregate all +plants, and microclimate solvers that select objects inside one environment +cell. -### Multi-rate modeling +## Manual Calls For Iterative Solvers -PlantSimEngine also supports multi-rate MTG simulations, where different models run at different cadences inside the same execution. A typical use case is to run leaf-scale processes hourly, aggregate them into daily plant-scale balances, and then export weekly summary series from the same simulation. +Use `Calls(...)` when a parent model must directly run another model, for +example a scene energy-balance solver that iterates leaf temperatures until +convergence: -The dedicated documentation now has three pages: a short introduction to the -core ideas, a fuller step-by-step tutorial, and an advanced configuration page: +```julia +ModelSpec(SceneEnergyBalance(); name=:scene_energy) |> + AppliesTo(One(scale=:Scene)) |> + Calls( + :leaf_energy => Many( + kind=:plant, + scale=:Leaf, + within=SceneScope(), + process=:energy_balance, + ), + :soil => One( + kind=:soil, + scale=:Soil, + within=SceneScope(), + process=:soil_water, + ), + ) |> + TimeStep(Hour(1)) +``` -- [Introduction to multi-rate execution](./multirate/introduction.md) -- [Step-by-step hourly, daily, weekly simulation](./multirate/multirate_tutorial.md) -- [Advanced multi-rate configuration](./multirate/advanced_configuration.md) +Inside `run!`, the parent model uses `call_targets(extra, :leaf_energy)` and +`run_call!(target; publish=false)` for trial iterations. The accepted state +uses `run_call!(target; publish=true)` once, so temporal outputs and mutable +environment writes are published exactly once. -## State of the field +## Where To Go Next -PlantSimEngine is a state-of-the-art plant simulation software that offers significant advantages over existing tools such as OpenAlea, STICS, APSIM, or DSSAT. +- [Scene/Object Quickstart](scene_object/quickstart.md) gives a compact + runnable workflow using the new API. +- [Migrating To The Scene/Object API](migration_scene_object.md) translates old + `ModelMapping`, `MultiScaleModel`, `Route`, and domain examples. +- [Public API](API/API_public.md) lists the scene/object constructors, + selectors, lifecycle hooks, and explanation helpers. +- [Model traits](model_traits.md) explains `inputs_`, `outputs_`, `dep`, + `timespec`, `output_policy`, `meteo_inputs_`, and `meteo_outputs_`. +- [Legacy MTG mapping tutorials](multiscale/multiscale_considerations.md) are + retained for existing simulations while the scene/object tutorials mature. -The use of Julia programming language in PlantSimEngine allows for: +## Performance -- Quick and easy prototyping compared to compiled languages -- Significantly better performance than typical interpreted languages -- No need for translation into another compiled language +PlantSimEngine keeps model kernels close to regular Julia functions while the +runtime handles dependency scheduling, object selection, temporal aggregation, +and environment sampling. On an M1 MacBook Pro, toy daily simulations run in +hundreds of microseconds, and PlantBiophysics.jl models using PlantSimEngine +have been measured much faster than equivalent implementations in typical +scientific scripting languages. -Julia's features enable PlantSimEngine to provide: +For performance-sensitive scenes, inspect the compiled representation: -- Multiple-dispatch for automatic computation of model dependency graphs -- Type stability for optimized performance -- Seamless compatibility with powerful tools like MultiScaleTreeGraph.jl for multi-scale computations +```julia +compiled = refresh_bindings!(scene) +explain_bindings(compiled) +explain_schedule(compiled) +explain_execution_plan(scene) +``` -PlantSimEngine's approach streamlines the process of model development by automatically managing: +These helpers expose resolved objects, carriers, copy/reference semantics, +application clocks, and homogeneous execution batches. -- Model coupling with automated dependency graph computation -- Time-steps and parallelization -- Input and output variables -- Various types of objects used for simulations (vectors, dictionaries, multi-scale tree graphs) +## Ask Questions -## Projects that use PlantSimEngine +If you have questions or feedback, [open an issue](https://github.com/VirtualPlantLab/PlantSimEngine.jl/issues) +or ask on [discourse](https://fspm.discourse.group/c/software/virtual-plant-lab). -Take a look at these projects that use PlantSimEngine: +## Projects That Use PlantSimEngine - [PlantBiophysics.jl](https://github.com/VEZY/PlantBiophysics.jl) - [XPalm](https://github.com/PalmStudio/XPalm.jl) -## Make it yours - -The package is developed so anyone can easily implement plant/crop models, use it freely and as you want thanks to its MIT license. +## Make It Yours -If you develop such tools and it is not on the list yet, please make a PR or contact me so we can add it! 😃 +PlantSimEngine is distributed under the MIT license. If you develop a package +or model suite that uses it and want it listed here, please open a pull request +or contact the maintainers. diff --git a/docs/src/migration_scene_object.md b/docs/src/migration_scene_object.md new file mode 100644 index 000000000..b471fc944 --- /dev/null +++ b/docs/src/migration_scene_object.md @@ -0,0 +1,481 @@ +# Migrating To The Scene/Object API + +The scene/object API replaces the separate multiscale mapping and domain +configuration systems with one object-address graph. + +New scenario code should be organized around: + +```julia +Scene +Object +ModelSpec +AppliesTo +Inputs +Calls +Updates +TimeStep +Environment +``` + +Model implementations do not need to know about scenes, plants, domains, or +timesteps. They keep the existing kernel contract: + +```julia +inputs_(model) +outputs_(model) +dep(model) +meteo_inputs_(model) +meteo_outputs_(model) +run!(model, models, status, meteo, constants, extra) +``` + +This page maps the legacy configuration concepts to their scene/object +equivalents. + +## Scenario Structure + +Legacy simulations split configuration between `ModelMapping`, +`MultiScaleModel`, `Domain`, and `SimulationMapping`. The unified API stores +runtime entities in one `Scene`: + +`ModelMapping` is no longer exported. While migrating historical code, use +`PlantSimEngine.ModelMapping(...)` explicitly; new scenarios should use the +scene/object form below. + +```julia +scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:plant_1), + Object(:soil; scale=:Soil, kind=:soil, parent=:scene); + applications=( + ModelSpec(LeafModel(); name=:leaf_model) |> + AppliesTo(Many(scale=:Leaf)), + + ModelSpec(SoilModel(); name=:soil_model) |> + AppliesTo(One(scale=:Soil)), + ), + environment=(T=25.0, Rh=0.6, Wind=1.0), +) +``` + +`Object` labels describe runtime entities. They do not prescribe plant +topology. A plant may use any hierarchy of plants, axes, internodes, segments, +leaves, roots, fruits, or application-specific objects. + +### Existing MTG Topologies + +An existing MTG can be adapted without rebuilding its topology manually: + +```julia +scene = Scene( + mtg; + applications=applications, + environment=meteo, + id=node -> Symbol(symbol(node), "_", node_id(node)), + kind=node -> node_kind(node), + species=node -> node_species(node), + geometry=node -> node_geometry(node), +) +``` + +`objects_from_mtg(mtg; ...)` exposes the intermediate object list when it is +useful to inspect or modify labels before constructing the scene. By default, +the adapter uses MTG node ids and scales, and reuses an existing +`:plantsimengine_status` attribute when present. + +## Multiscale Inputs + +Replace `MultiScaleModel(...)` variable mappings with consumer-side +`Inputs(...)`. + +Legacy: + +```julia +MultiScaleModel( + AllocationModel(), + [:leaf_carbon => [:Leaf => :leaf_carbon]], +) +``` + +Unified: + +```julia +ModelSpec(AllocationModel(); name=:allocation) |> + AppliesTo(Many(scale=:Plant)) |> + Inputs( + :leaf_carbon => Many( + scale=:Leaf, + within=Self(), + var=:leaf_carbon, + ), + ) +``` + +`Self()` is relative to the object where the consumer runs. A plant-scale +allocation model therefore reads only leaves inside that plant. Use +`SceneScope()` for scene-wide aggregation and `SelfPlant()` to select the +nearest containing plant from an organ. + +Same-object renaming uses the same syntax: + +```julia +Inputs( + :consumer_name => One( + within=Self(), + application=:producer, + var=:producer_name, + ), +) +``` + +Same-rate bindings use shared references or reference vectors when possible. +Cross-rate bindings use typed temporal streams. + +## Cross-Domain Values And Routes + +Replace `AllDomains(...)` and user-written `Route(...)` with an input selector +on the consuming application. + +Legacy: + +```julia +Route( + from=AllDomains( + kind=:plant, + scale=:Leaf, + process=:transpiration, + var=:transpiration, + ), + to=DomainRouteTarget(:scene, var=:leaf_transpiration), + cardinality=ManyToOneVector(), +) +``` + +Unified: + +```julia +ModelSpec(SceneWaterBalance(); name=:scene_water) |> + AppliesTo(One(scale=:Scene)) |> + Inputs( + :leaf_transpiration => Many( + kind=:plant, + scale=:Leaf, + within=SceneScope(), + process=:transpiration, + var=:transpiration, + ), + ) +``` + +The compiler chooses the carrier. Scenario authors declare the source objects, +source variable, and temporal policy rather than a route implementation. + +## Manual Hard Calls + +Replace `HardDomains(...)` with `Calls(...)`. + +```julia +ModelSpec(SceneEnergyBalance(); name=:scene_energy) |> + AppliesTo(One(scale=:Scene)) |> + Calls( + :leaf_energy => Many( + kind=:plant, + scale=:Leaf, + within=SceneScope(), + process=:energy_balance, + ), + :soil => One( + kind=:soil, + scale=:Soil, + within=SceneScope(), + process=:soil_water, + ), + ) +``` + +The parent model controls execution: + +```julia +function PlantSimEngine.run!(model::SceneEnergyBalance, models, status, meteo, + constants, extra) + leaf_targets = call_targets(extra, :leaf_energy) + + for iteration in 1:model.max_iterations + for target in leaf_targets + run_call!(target; meteo=trial_meteo(model, status)) + end + converged(model, status, leaf_targets) && break + end + + for target in leaf_targets + run_call!(target; meteo=accepted_meteo(model, status), publish=true) + end + return nothing +end +``` + +`run_call!` defaults to `publish=false`. Trial calls mutate target status but +do not append temporal samples or write environment outputs. The accepted +state must use `publish=true`. + +## Multiple Plants And Species + +Replace repeated plant domains with `ObjectTemplate` and `ObjectInstance`. + +```julia +oil_palm = ObjectTemplate( + ( + ModelSpec(LeafEnergy()) |> + AppliesTo(Many(scale=:Leaf)), + + ModelSpec(Allocation()) |> + AppliesTo(One(scale=:Plant)) |> + Inputs( + :leaf_carbon => Many( + scale=:Leaf, + within=Self(), + var=:leaf_carbon, + ), + ), + ); + kind=:plant, + species=:oil_palm, +) + +palm_1 = ObjectInstance( + :palm_1, + oil_palm; + root=Object(:plant_1; scale=:Plant, parent=:scene), + objects=(Object(:palm_1_leaf_1; scale=:Leaf, parent=:plant_1),), +) + +palm_2 = ObjectInstance( + :palm_2, + oil_palm; + root=Object(:plant_2; scale=:Plant, parent=:scene), + objects=(Object(:palm_2_leaf_1; scale=:Leaf, parent=:plant_2),), +) + +scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + palm_1, + palm_2, +) +``` + +Unmodified instances share model objects and parameters. Use instance +overrides for one plant and `Override(...)` for exceptional organs. + +## Multirate Inputs + +Replace `TimeStepModel(...)` with `TimeStep(...)`. Put temporal policy and +window information on the consuming `Inputs(...)` selector. + +```julia +ModelSpec(HourlyLeafModel(); name=:leaf_flux) |> + AppliesTo(Many(scale=:Leaf)) |> + TimeStep(Hour(1)) + +ModelSpec(DailyPlantModel(); name=:daily_plant) |> + AppliesTo(Many(scale=:Plant)) |> + Inputs( + :leaf_fluxes => Many( + scale=:Leaf, + within=Self(), + process=:leaf_flux, + var=:flux, + policy=Integrate(), + window=Day(1), + ), + ) |> + TimeStep(Day(1)) +``` + +Use `HoldLast()`, `Interpolate()`, `Integrate()`, or `Aggregate()` according to +the physical meaning of the input. `PreviousTimeStep(:x) => selector` expresses +an explicit lag and breaks a same-timestep dependency cycle. + +If the input selector omits `policy=...`, the scene compiler uses the +producer's `output_policy(...)` trait for the selected source variable when the +publisher is unique. An explicit selector policy always wins over the trait. + +If a model defines `timespec(::Type{<:MyModel})`, the scene scheduler uses that +cadence when the application has no explicit `TimeStep(...)`. A scenario-level +`TimeStep(...)` always wins over the model trait. + +If the clock falls back to the scene base step, `timestep_hint(...)` required +bounds are validated against that base step. The hint is a compatibility +constraint, not a scheduling override. + +## Ordered Variable Updates + +When several models intentionally write the same variable, declare the order +on the later application: + +```julia +ModelSpec(CarbonAllocation(); name=:allocation) |> + AppliesTo(Many(scale=:Leaf)) + +ModelSpec(LeafPruning(); name=:pruning) |> + AppliesTo(Many(scale=:Leaf)) |> + Updates(:leaf_biomass; after=:allocation) +``` + +Do not encode this coupling in either model implementation. The scenario owns +the writer order. + +## Environment And Microclimate + +Models declare environment variables with `meteo_inputs_` and +`meteo_outputs_`. The scene binds each object to the active environment +backend: + +```julia +ModelSpec(LeafEnergy(); name=:leaf_energy) |> + AppliesTo(Many(scale=:Leaf)) |> + Environment(provider=:grid) +``` + +Spatial bindings are cached. The default resolver uses the object's geometry, +then the nearest ancestor geometry, then the backend's global behavior. +Changing geometry invalidates only affected environment bindings. + +Per-model source remapping also moves to `Environment(...)`: + +```julia +ModelSpec(LeafGasExchange(); name=:gas_exchange) |> + AppliesTo(Many(scale=:Leaf)) |> + Environment(provider=:global, sources=(CO2=:Ca,)) +``` + +The model still declares and reads `CO2`; the scene samples `Ca` from the +active environment backend and exposes it to the model as `meteo.CO2`. +`explain_environment_bindings(...)` reports both `required_inputs` and +`source_inputs`, so remapped meteorology is visible to users and agents. + +Model authors can also provide default source remaps with +`meteo_hint(::Type{<:Model}) = (bindings=(CO2=(source=:Ca,),),)`. Scene +applications use those defaults when the scenario does not provide an explicit +source for the same variable. `Environment(; sources=...)` remains the +scenario-level override. + +For global `Weather` tables, sampling follows the application's +`TimeStep(...)`. A slower model receives a PlantMeteo windowed sample using its +`meteo_hint` reducer and window instead of receiving only the current raw +weather row. A scenario source override preserves that reducer: + +```julia +meteo_hint(::Type{<:GasExchange}) = ( + bindings=(CO2=(source=:Ca, reducer=MeanReducer()),), +) + +ModelSpec(GasExchange()) |> + AppliesTo(Many(scale=:Leaf)) |> + TimeStep(Hour(2)) |> + Environment(provider=:global, sources=(CO2=:canopy_CO2,)) +``` + +Every leaf still reads `meteo.CO2`; the two-hour mean is computed from +`:canopy_CO2`. The sampled row is computed once per application and timestep, +then reused for all selected leaves. + +## Growth, Pruning, And Movement + +Use the public lifecycle operations: + +```julia +register_object!(scene, new_leaf; parent=:plant_1) +remove_object!(scene, :old_leaf) +reparent_object!(scene, :leaf_2, :axis_3) +move_object!(scene, :leaf_3, new_geometry) +``` + +Structural changes refresh application targets, input carriers, call targets, +writer validation, and schedules before the next timestep. Geometry-only +changes refresh environment bindings without rebuilding unrelated structural +bindings. + +The refreshed runtime also rebuilds homogeneous execution batches. Use +`explain_execution_plan(scene_or_simulation)` to inspect the concrete +model/status/carrier types and the objects grouped into each specialized inner +loop. Exceptional per-object model overrides appear as separate ordered +batches. + +## Output Collection + +`run!(scene; steps=...)` returns a `SceneSimulation`. Use +`scene_outputs(sim)` for the retained typed streams, `explain_outputs(sim)` for +structured diagnostics, and `collect_outputs(sim)` for tabular rows. + +```julia +request = OutputRequest( + :Leaf, + :transpiration; + name=:leaf_transpiration_daily, + process=:leaf_energy, + policy=Integrate(), + clock=Day(1), +) + +sim = run!(scene; steps=48, tracked_outputs=request) +daily = collect_outputs(sim, :leaf_transpiration_daily) +``` + +Scene output requests are materialized from retained temporal streams after +the run. They use the same temporal policies as multirate inputs and export +dynamic objects only over the interval where that object published samples. +If several scene applications implement the same process, add +`application=:application_name` to select one explicitly. This is also the +way to request a named `:stream_only` publisher. +When `tracked_outputs` is explicit, the runtime retains only requested +application/variable streams plus streams needed by temporal `Inputs(...)`. +Passing `tracked_outputs=OutputRequest[]` therefore keeps no output streams +unless a temporal dependency requires one. Use `explain_output_retention(sim)` +to inspect why each retained stream was kept. Dependency-only streams retain a +bounded policy-specific horizon, while requested streams keep complete +histories for post-run export. Export is not yet a fully online path. + +## Inspecting The Compiled Scenario + +Use structured explanations instead of inspecting internal dictionaries: + +```julia +compiled = refresh_bindings!(scene) + +explain_objects(scene) +explain_instances(scene) +explain_scopes(scene) +explain_scene_applications(compiled) +explain_bindings(compiled) +explain_calls(compiled) +explain_environment_bindings(refresh_environment_bindings!(scene, compiled)) +explain_schedule(compiled) +explain_writers(compiled) +explain_model_bundles(compiled) +``` + +These functions return structured rows with concrete object ids, application +ids, processes, variables, temporal policies, carrier semantics, and resolved +targets. They are intended for both users and coding agents. + +## Migration Table + +| Legacy configuration | Scene/object replacement | +| --- | --- | +| `ModelMapping` scale/domain assembly | `Scene` objects plus model applications | +| `MultiScaleModel(...)` | consumer `Inputs(...)` | +| `Route(...)` | compiler-selected carrier from `Inputs(...)` | +| `AllDomains(...)` | object selector inside `Inputs(...)` | +| `HardDomains(...)` | `Calls(...)` | +| `Domain(...)` | `ObjectTemplate` and `ObjectInstance` | +| `TimeStepModel(...)` | `TimeStep(...)` | +| `InputBindings(...)` | source, policy, and window on `Inputs(...)` | +| `MeteoBindings(...)` | automatic environment binding or `Environment(...)` | +| `ScopeModel(...)` | `AppliesTo(...)` and selector scopes | +| `SameScale()` rename | `Inputs(:local => One(within=Self(), var=:source))` | + +The executable MAESPA migration in +`examples/maespa_domain_example.jl` demonstrates two plant species, shared +soil, plant-local aggregation, scene-wide iterative energy balance, hourly and +daily models, and automatic environment binding. diff --git a/docs/src/model_coupling/model_coupling_user.md b/docs/src/model_coupling/model_coupling_user.md index b24be6931..f940d0cf1 100644 --- a/docs/src/model_coupling/model_coupling_user.md +++ b/docs/src/model_coupling/model_coupling_user.md @@ -5,7 +5,7 @@ using PlantSimEngine, PlantMeteo, Dates # Import the example models defined in the `Examples` sub-module: using PlantSimEngine.Examples -m = ModelMapping( +m = PlantSimEngine.ModelMapping( Process1Model(2.0), Process2Model(), Process3Model(), @@ -44,7 +44,7 @@ using PlantSimEngine.Examples Here is how we can make the model coupling: ```@example usepkg -m = ModelMapping(Process1Model(2.0), Process2Model(), Process3Model()) +m = PlantSimEngine.ModelMapping(Process1Model(2.0), Process2Model(), Process3Model()) nothing # hide ``` @@ -75,7 +75,7 @@ outputs(Process2Model()) So considering those two models, we only need `var1` and `var2` to be initialized, as `var3` is computed. This is why we recommend [`to_initialize`](@ref) instead of [`inputs`](@ref), because it returns only the variables that need to be initialized, considering that some inputs are duplicated between models, and some are computed by other models (they are outputs of a model): ```@example usepkg -m = ModelMapping( +m = PlantSimEngine.ModelMapping( Process1Model(2.0), Process2Model(), Process3Model(), @@ -88,7 +88,7 @@ to_initialize(m) The most straightforward way of initializing a model list is by giving the initializations to the `status` keyword argument during instantiation: ```@example usepkg -m = ModelMapping( +m = PlantSimEngine.ModelMapping( Process1Model(2.0), Process2Model(), Process3Model(), @@ -118,7 +118,7 @@ All following models (`Process4Model` to `Process7Model`) do not call explicitly Let's make a new model list including the soft-coupled models: ```@example usepkg -m = ModelMapping( +m = PlantSimEngine.ModelMapping( Process1Model(2.0), Process2Model(), Process3Model(), @@ -139,7 +139,7 @@ to_initialize(m) We can initialize it like so: ```@example usepkg -m = ModelMapping( +m = PlantSimEngine.ModelMapping( Process1Model(2.0), Process2Model(), Process3Model(), diff --git a/docs/src/model_execution.md b/docs/src/model_execution.md index 3d1c575f3..8c8310123 100644 --- a/docs/src/model_execution.md +++ b/docs/src/model_execution.md @@ -1,219 +1,354 @@ -# Model execution +# Model Execution -## Simulation order +This page describes how the native scene/object runtime executes model +applications. Use this path for new multi-object, multi-plant, soil, +microclimate, and multirate simulations. -`PlantSimEngine.jl` uses the [`ModelMapping`](@ref) to automatically compute a dependency graph between the models and run the simulation in the correct order. When running a simulation with [`run!`](@ref), the models are then executed following this simple set of rules: +The public configuration surface is: -1. Independent models are run first. A model is independent if it can be run independently from other models, only using initializations (or nothing). -2. Then, models that have a dependency on other models are run. The first ones are the ones that depend on an independent model. Then the ones that are children of the second ones, and then their children ... until no children are found anymore. There are two types of children models (*i.e.* dependencies): hard and soft dependencies: - 1. Hard dependencies are always run before soft dependencies. A hard dependency is a model that is directly called by another model. It is declared as such by its parent that lists its hard-dependencies as `dep`. See [this example](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/3d91bb053ddbd087d38dcffcedd33a9db35a0fcc/examples/dummy.jl#L39) that shows `Process2Model` defining a hard dependency on any model that simulates `process1`. - 2. Soft dependencies are then run sequentially. A model has a soft dependency on another model if one or more of its inputs is computed by another model. If a soft dependency has several parent nodes (*e.g.* two different models compute two inputs of the model), it is run only if all its parent nodes have been run already. In practice, when we visit a node that has one of its parent that did not run already, we stop the visit of this branch. The node will eventually be visited from the branch of the last parent that was run. +```julia +Scene +Object +ModelSpec +AppliesTo +Inputs +Calls +Updates +TimeStep +Environment +``` -## Multi-rate model configuration (experimental) - -For multiscale simulations, model usage is configured in the mapping through `ModelSpec` transforms: - -- `TimeStepModel(...)`: sets model execution clock. -- `InputBindings(...)`: sets producer, source variable, optional source scale, and policy for each consumer input. -- `MeteoBindings(...)`: sets weather aggregation rules at the model clock for meteo variables. -- `MeteoWindow(...)`: sets weather row selection strategy (`RollingWindow()` or `CalendarWindow(...)`). -- `OutputRouting(...)`: sets whether an output is canonical (`:canonical`) or stream-only (`:stream_only`). -- `ScopeModel(...)`: partitions producer streams by scope (`:global`, `:plant`, `:scene`, `:self`) for multi-entity simulations. +Legacy `ModelMapping`, `MultiScaleModel`, domain, and route APIs are retained +as qualified compatibility tools while old examples are migrated. New +scenarios should start from `Scene` and model applications. -For a compact overview of all model traits and precedence rules, see [Model traits](model_traits.md). +## Model Kernels And Applications -If users do not provide `MeteoBindings(...)` or `MeteoWindow(...)`, -the runtime can infer defaults from model traits: -- `timespec(::Type{<:MyModel})` -- `output_policy(::Type{<:MyModel})` -- `timestep_hint(::Type{<:MyModel})` -- `meteo_hint(::Type{<:MyModel})` +A model kernel is still an ordinary PlantSimEngine model: -For timestep specifically, runtime is meteo-first (see decision flow below): `timestep_hint` -is used for compatibility validation (and user guidance), not to auto-assign model clocks. +- `inputs_(model)` declares required status variables; +- `outputs_(model)` declares variables the model computes; +- `meteo_inputs_(model)` declares environment variables it reads; +- `meteo_outputs_(model)` declares mutable environment variables it writes; +- `dep(model)` may declare model-author defaults; +- `run!(model, models, status, meteo, constants, extra)` contains the model + equations. -If users do not provide `InputBindings(...)`, runtime infers same-name bindings: -- first from a unique producer at the same scale; -- otherwise from a unique producer at another scale; -- if no producer exists, input stays unresolved (so initialization/forced values can be used); -- if multiple producers are possible, runtime errors and asks for explicit `InputBindings(...)`. +The scene/object layer does not change that kernel contract. It adds a +scenario-specific application around the kernel: -For inferred bindings, default policy is resolved as: -- producer `output_policy` for the source output when defined; -- otherwise `HoldLast()`. +```julia +ModelSpec(LeafEnergyBalance(); name=:leaf_energy) |> + AppliesTo(Many(kind=:plant, scale=:Leaf)) |> + Inputs(...) |> + Calls(...) |> + TimeStep(Dates.Hour(1)) |> + Environment(provider=:canopy) +``` -`output_policy` is a default hint, applied only when an output stream is actually read -by another model input (or output export). Unused outputs do not trigger integration/reduction work. +`ModelSpec` decides where the model runs, where its inputs come from, which +models it may call manually, which timestep it uses, and which environment +provider is bound to it. The model implementation stays reusable. -Explicit mapping policies still have priority (`InputBindings(..., policy=...)`) and can -complement trait defaults by defining additional bindings with different policies. +## Compilation Before Runtime -For timestep hints: -- `timestep_hint.required` is a hard compatibility constraint when runtime uses meteo-derived timestep. -- `timestep_hint.preferred` is informational only (it does not set runtime timestep by itself). -- Explicit `TimeStepModel(...)` always takes precedence. +Before the timestep loop, `compile_scene(scene)` and `refresh_bindings!(scene)` +resolve the scenario into concrete runtime carriers: -For meteo hints: -- return `(; bindings=..., window=...)` where `bindings` matches `MeteoBindings(...)` - and `window` matches `MeteoWindow(...)`. -- Explicit `MeteoBindings(...)` / `MeteoWindow(...)` always take precedence. +1. `AppliesTo(...)` selectors are resolved to stable object ids. +2. `Inputs(...)` selectors are resolved to source object/application ids. +3. Same-rate inputs are wired as shared `Ref`s, `RefVector`s, or + `ObjectRefVector`s. +4. Temporal inputs are compiled as stream lookups with a policy such as + `HoldLast`, `Interpolate`, `Integrate`, or `Aggregate`. +5. `Calls(...)` declarations are compiled to callable target lists. +6. `Environment(...)` is bound to backend cells, layers, voxels, or global + weather providers. +7. The root application order is topologically sorted from value inputs and + `Updates(...)` ordering. +8. Root execution batches are grouped by concrete model/status/environment + types where possible. -Inspection helpers: -- `resolved_model_specs(mapping)` returns resolved specs after inference/validation. -- `explain_model_specs(mapping_or_sim)` prints a compact summary (`timestep`, - `input_bindings`, `meteo_bindings`, `meteo_window`) for each model process. +Selectors are not resolved in the hot loop. Runtime execution uses the +compiled indexes and carriers. -Policy parameterization: -- `Integrate()` defaults to `SumReducer()`; you can pass another reducer, e.g. `Integrate(MeanReducer())` or `Integrate(vals -> maximum(vals) - minimum(vals))`. -- `Aggregate()` defaults to `MeanReducer()`; you can pass reducers such as `Aggregate(MaxReducer())`. -- Difference between `Integrate` and `Aggregate`: with the same reducer they are runtime-equivalent. - In practice, only defaults and naming intent differ (`Integrate` for accumulation, `Aggregate` for summary statistics). -- `Interpolate()` defaults to `mode=:linear, extrapolation=:linear`; use `Interpolate(; mode=:hold, extrapolation=:hold)` for hold behavior. -- The same reducer objects are reused by meteo sampling (`MeteoBindings`) and by windowed policies (`Integrate`, `Aggregate`). -- Custom reducers/callables can accept either `(values)` or `(values, durations_seconds)`. -- For flux-to-amount conversions, use `Integrate(PlantMeteo.DurationSumReducer())` - (equivalent to `sum(values .* durations_seconds)`), instead of hardcoding a fixed factor. +Useful inspection helpers: -`TimeStepModel(...)` accepts either step counts (`Real`), `ClockSpec`, or fixed `Dates` periods -(for example `Dates.Hour(1)`, `Dates.Day(1)`). Fixed periods are converted internally using -the meteo base timestep duration. +```julia +compiled = refresh_bindings!(scene) + +explain_scene_applications(compiled) +explain_bindings(compiled) +explain_calls(compiled) +explain_environment_bindings(refresh_environment_bindings!(scene, compiled)) +explain_schedule(compiled) +explain_execution_plan(scene) +explain_writers(compiled) +``` -### Timestep decision flow +These explanations are intended for both users and agents. They report the +compiled object ids, applications, carriers, clocks, environment bindings, and +manual-call targets that the runtime will use. + +## Soft Dependencies With Inputs + +Soft dependencies are value dependencies. A consumer model reads a variable +produced by another model through `Inputs(...)`. + +```julia +ModelSpec(SceneLAI(ground_area); name=:scene_lai) |> + AppliesTo(One(scale=:Scene)) |> + Inputs( + :leaf_areas => Many( + kind=:plant, + scale=:Leaf, + within=SceneScope(), + process=:leaf_state, + var=:leaf_area, + ), + ) +``` -When meteo is provided, `duration` is mandatory for each row (or the simulation errors). +For same-rate inputs, the runtime installs a reference carrier into the +consumer status during compilation. A scene-scale model reading all leaf areas +therefore sees a `RefVector`-like object: reading pulls current values from +source leaf statuses, and writing through the carrier mutates source refs when +the carrier supports it. -Runtime picks each model effective clock with this order: +If an input is not explicitly declared with `Inputs(...)`, the compiler can +infer simple same-object bindings when exactly one producer on the same object +outputs the same variable. Ambiguous producers are errors and should be +disambiguated with `process=...`, `application=...`, or `var=...`. -1. If `ModelSpec` has `TimeStepModel(...)`, use it. -2. Else if `timespec(model)` is non-default, use it. -3. Else use meteo base timestep (`duration`) for that model. +Use `PreviousTimeStep(:x) => selector` when a feedback dependency should read +the previous sample instead of creating a same-timestep scheduling edge. -Then runtime applies constraints: +## Hard Calls With Calls -1. If the model clock is meteo-derived (rule 3), `timestep_hint.required` is validated: - - fixed required period: meteo timestep must match exactly; - - required range: meteo timestep must be inside the range. -2. `timestep_hint.preferred` never overrides the clock when timestep is unset. -3. Meteo aggregation/integration is applied only when effective model timestep is coarser than meteo timestep. +Hard dependencies are manual calls. Use `Calls(...)` when a parent model must +control the call stack, for example during an iterative energy-balance solve. -Practical consequences: +```julia +ModelSpec(SceneEnergyBalance(); name=:scene_energy) |> + AppliesTo(One(scale=:Scene)) |> + Calls( + :leaf_energy => Many( + kind=:plant, + scale=:Leaf, + within=SceneScope(), + process=:energy_balance, + ), + :soil => One( + kind=:soil, + scale=:Soil, + within=SceneScope(), + process=:soil_water, + ), + ) |> + TimeStep(Dates.Hour(1)) +``` -- Unset `TimeStepModel` + required includes meteo + preferred is coarser: - model still runs at meteo timestep. -- Explicit coarser `TimeStepModel(Dates.Hour(2))` with hourly meteo: - model runs every 2 hours and receives aggregated meteo over that window. -- Unset `TimeStepModel` + required excludes meteo: - runtime errors with an actionable compatibility message. - -Developer note on period conversion: -- Runtime time is indexed on a 1-based timeline (`t = 1, 2, 3, ...`). -- `TimeStepModel(Dates.Day(1))` is converted to a clock step count using: - `dt = day_seconds / meteo_step_seconds`. -- For hourly meteo (`duration = Dates.Hour(1)`), this gives `dt = 24` and the default phase is `1`, - so the model runs at `t = 1, 25, 49, ...`. -- This is equivalent to `ClockSpec(24.0, 1.0)`. -- If you need runs at `t = 24, 48, 72, ...`, set an explicit phase with `ClockSpec(24.0, 0.0)`. - -Typical pipeline form: +Inside `run!`, the parent retrieves executable targets and decides when to +publish the accepted state: ```julia -ModelSpec(MyModel()) |> -TimeStepModel(ClockSpec(24.0, 1.0)) |> -MeteoWindow(CalendarWindow(:day; anchor=:current_period, week_start=1, completeness=:strict)) |> -MeteoBindings(; T=MeanWeighted()) |> -InputBindings(; x=(process=:producer, var=:y, policy=HoldLast())) |> -OutputRouting(; z=:stream_only) +function PlantSimEngine.run!(model::SceneEnergyBalance, models, status, meteo, + constants, extra) + for target in call_targets(extra, :leaf_energy) + run_call!(target; meteo=trial_meteo(model, status)) + end + + for target in call_targets(extra, :leaf_energy) + run_call!(target; meteo=accepted_meteo(model, status), publish=true) + end + + return nothing +end ``` -### Calendar-aligned meteo windows +`run_call!` defaults to `publish=false`. Trial calls mutate target statuses but +do not publish temporal samples or scatter mutable environment outputs. Call +`run_call!(target; publish=true)` once for the accepted state. -`MeteoWindow(...)` controls how rows are selected before reducers are applied: -- `RollingWindow()` (default): trailing window based on `dt` (for example "last 24 steps"). -- `CalendarWindow(period; anchor, week_start, completeness)`: -: `period` in `:day`, `:week`, `:month` -: `anchor` in `:current_period`, `:previous_complete_period` -: `week_start` in `1:7` (1 = Monday) -: `completeness` in `:allow_partial`, `:strict` +Applications selected only by `Calls(...)` are marked manual-call-only in +`explain_schedule(compiled)` and are skipped by the root `run!(scene)` loop. -`CalendarWindow(:day; anchor=:current_period, ...)` guarantees that a model running inside a day sees -aggregates over that civil day (including later timesteps from that day when available). +## Duplicate Writers With Updates -### Hold-last coupling (default policy) +By default, one application owns each `(object, output variable)` canonical +writer. If a scenario intentionally lets several models update the same +variable, later writers must declare that order explicitly: ```julia -mapping = ModelMapping( - :Leaf => ( - ModelSpec(LeafSourceModel()) |> TimeStepModel(1.0), - ModelSpec(LeafConsumerModel()) |> - TimeStepModel(ClockSpec(2.0, 1.0)) |> - InputBindings(; C=(process=:leafsource, var=:S)), - ), -) +ModelSpec(CarbonAllocation(); name=:carbon_allocation) |> + AppliesTo(Many(scale=:Leaf)) + +ModelSpec(LeafPruning(); name=:leaf_pruning) |> + AppliesTo(Many(scale=:Leaf)) |> + Updates(:leaf_biomass; after=:carbon_allocation) ``` -### Daily integration from hourly stream +This keeps ordinary duplicate outputs as errors while allowing cases such as +allocation followed by pruning. `explain_writers(compiled)` reports writer +groups and the `Updates(...)` declarations that validate them. + +## Multirate Execution + +Use `TimeStep(...)` with `Dates.Period` values for model application clocks: ```julia -mapping = ModelMapping( - :Leaf => ( - ModelSpec(HourlyAssimModel()) |> TimeStepModel(1.0), - ), - :Plant => ( - ModelSpec(DailyCarbonOfferModel()) |> - TimeStepModel(ClockSpec(24.0, 1.0)) |> - InputBindings(; A=(process=:hourlyassim, var=:A, scale=:Leaf, policy=Integrate())), - ), -) +ModelSpec(HourlyLeafAssimilation(); name=:leaf_assim) |> + AppliesTo(Many(scale=:Leaf)) |> + TimeStep(Dates.Hour(1)) + +ModelSpec(DailyPlantAllocation(); name=:allocation) |> + AppliesTo(Many(scale=:Plant)) |> + Inputs( + :leaf_assimilation => Many( + scale=:Leaf, + within=Self(), + process=:leaf_assimilation, + var=:A, + policy=Integrate(), + window=Dates.Day(1), + ), + ) |> + TimeStep(Dates.Day(1)) ``` -### Interpolate slow producer to fast consumer +Clock precedence is: + +1. explicit `TimeStep(...)` on the `ModelSpec`; +2. non-default `timespec(model)` trait; +3. the scene environment base step. + +`timestep_hint(model)` is a compatibility constraint and explanation hint. It +does not silently choose a clock. If a model uses the environment base step and +that step violates `timestep_hint.required`, scene compilation errors. + +Temporal input policy precedence is: + +1. explicit selector policy, such as `policy=Integrate()`; +2. producer `output_policy(model)` for that output; +3. `HoldLast()`. + +Supported policies are: + +- `HoldLast()`: use the latest producer sample; +- `Interpolate()`: interpolate or extrapolate from producer samples; +- `Integrate()`: reduce values over a window, defaulting to `SumReducer()`; +- `Aggregate()`: reduce values over a window, defaulting to `MeanReducer()`. + +`Integrate(...)` and `Aggregate(...)` accept reducer objects or callables that +take either `(values)` or `(values, durations_seconds)`. + +## Environment Sampling + +`Environment(...)` chooses a provider and optional source-variable remapping: ```julia -mapping = ModelMapping( - :Leaf => ( - ModelSpec(SlowSourceModel()) |> TimeStepModel(ClockSpec(2.0, 1.0)), - ModelSpec(FastConsumerModel()) |> - TimeStepModel(1.0) |> - InputBindings(; X=(process=:slowsource, var=:X, policy=Interpolate())), - ), -) +ModelSpec(CO2Probe(); name=:co2_probe) |> + AppliesTo(Many(scale=:Leaf)) |> + Environment(provider=:canopy, sources=(CO2=:Ca,)) ``` -When the `ModelMapping` declares multirate configuration, the runtime resolves inputs from producer temporal streams according to these policies. -Meteo rows are also sampled at each model clock. By default, meteo variables are aggregated from -the finest weather step (for example `T` and `Rh` as weighted means, `Tmin/Tmax`, and radiation -quantity aliases such as `Ri_SW_q` in MJ m-2). You can override these rules with `MeteoBindings(...)` -on each `ModelSpec`. +The compiler binds each application/object pair to the selected backend before +runtime. Constant weather, global tabular meteorology, grid, layer, voxel, or +octree-style microclimate backends all use the same contract: -### Current limitations +- `meteo_inputs_(model)` says what the model reads; +- `meteo_outputs_(model)` says what the model writes back; +- `Environment(; sources=...)` maps model-facing names to backend names; +- geometry and position are used by spatial backends when available; +- object-to-environment links are cached and refreshed when objects move. -- Multi-rate MTG runs currently execute sequentially. Passing `executor=ThreadedEx()` or `executor=DistributedEx()` falls back to sequential execution with a warning. -- Sub-step execution is currently unsupported: model timesteps shorter than the meteo base step (for example `TimeStepModel(Dates.Minute(30))` with hourly meteo) raise an error. +Model-level `meteo_hint(...)` can provide default source bindings and +aggregation rules. Scenario-level `Environment(...)` keeps precedence for +source names, while explicit sampling policy on `Inputs(...)` controls +model-to-model temporal values. + +## Running And Outputs + +Run a scene with: + +```julia +sim = run!(scene; steps=30, constants=Constants()) +``` -## Multi-rate output export (experimental) +The returned `SceneSimulation` contains the mutated scene, compiled bindings, +environment bindings, execution plan, and retained temporal output streams. -You can export selected variables at a requested rate from temporal streams: +By default, scene runs retain all published streams. For large scenes, pass +`OutputRequest` values to retain only selected outputs and temporal dependency +streams: ```julia -req = OutputRequest(:Leaf, :carbon_assimilation; - name=:A_daily, - process=:toyassim, +request = OutputRequest( + :Leaf, + :A; + name=:leaf_assimilation_daily, + process=:leaf_assimilation, policy=Integrate(), - clock=ClockSpec(24.0, 1.0) + clock=Dates.Day(1), ) -run!(sim, meteo; tracked_outputs=[req], executor=SequentialEx()) -exported = collect_outputs(sim; sink=DataFrame) +sim = run!(scene; steps=72, tracked_outputs=request) +collect_outputs(sim, :leaf_assimilation_daily; sink=nothing) +explain_output_retention(sim) ``` -`tracked_outputs` accepts `OutputRequest` values for these resampled exports. -You can also return them directly from `run!`: +When several applications publish the same process and variable, use +`application=:application_name` in the request. This selects the named +application directly and can also request an explicitly named +`:stream_only` publisher. + +Set `tracked_outputs=OutputRequest[]` to retain no output streams unless they +are required by temporal dependencies. + +Temporal dependency streams that are not explicitly requested retain only the +history required by their input policy. `HoldLast` keeps the latest sample, +`Integrate` and `Aggregate` keep their input window, and `Interpolate` and +`PreviousTimeStep` keep sufficient recent source samples. Requested streams +retain complete histories for post-run export. `explain_output_retention(sim)` +reports `retention_steps` for bounded dependency-only streams and `nothing` +for full-history streams. + +## Lifecycle Changes + +Scene objects may be added, removed, reparented, moved, or have their geometry +updated between or during timesteps: ```julia -out_status, exported = run!( - sim, - meteo; - tracked_outputs=[req], - return_requested_outputs=true, -) +register_object!(scene, Object(:new_leaf; scale=:Leaf); parent=:plant_1) +remove_object!(scene, :old_leaf) +reparent_object!(scene, :leaf_3; parent=:plant_2) +move_object!(scene, :leaf_4; geometry=new_geometry) +update_geometry!(scene, :leaf_5, new_geometry) ``` + +Structural changes invalidate compiled object/model bindings. Movement and +geometry changes invalidate environment bindings without rebuilding structural +input carriers. The next `run!(scene)` step refreshes the necessary caches. + +## Compatibility Runtime + +Historical `ModelMapping` and MTG mapping simulations still work through +qualified compatibility constructors such as +`PlantSimEngine.ModelMapping(...)`, `PlantSimEngine.MultiScaleModel(...)`, +`PlantSimEngine.InputBindings(...)`, and +`PlantSimEngine.TimeStepModel(...)`. + +Those names are no longer the primary API. For new work, use: + +- `ModelMapping(...)` -> `Scene(...)` plus object-local `ModelSpec(...)` + applications; +- `MultiScaleModel(...)` -> `Inputs(...)`; +- `InputBindings(...)` -> selector fields inside `Inputs(...)`; +- `MeteoBindings(...)` / `MeteoWindow(...)` -> `Environment(...)`, + `meteo_hint(...)`, and model/application clocks; +- `HardDomains(...)` -> `Calls(...)`; +- `Route(...)` and `AllDomains(...)` -> consumer-side `Inputs(...)`; +- `TimeStepModel(...)` -> `TimeStep(...)`. + +See [Migrating To The Scene/Object API](migration_scene_object.md) for worked +translation patterns. diff --git a/docs/src/model_traits.md b/docs/src/model_traits.md index c8fd7bc12..2ac02bdbb 100644 --- a/docs/src/model_traits.md +++ b/docs/src/model_traits.md @@ -34,7 +34,7 @@ Behavior: - unspecified outputs fall back to `HoldLast()`; - used by runtime when resolving cross-clock reads; -- used as default policy for inferred `InputBindings(...)` when users do not provide explicit bindings; +- used as default policy for inferred `PlantSimEngine.InputBindings(...)` when users do not provide explicit bindings; - hint-only and lazy: policy is applied only for outputs that are actually consumed/exported. Declaring a policy for an unused output does not trigger integration work. @@ -51,7 +51,7 @@ Users can always override or complement this trait at mapping level: ```julia ModelSpec(MyConsumerModel()) |> -InputBindings( +PlantSimEngine.InputBindings( ; carbon_assimilation=(process=:myproducer, var=:carbon_assimilation, policy=HoldLast()), # override trait default carbon_assimilation_max=(process=:myproducer, var=:carbon_assimilation, policy=Aggregate(MaxReducer())), # complement with extra derived input @@ -60,7 +60,7 @@ InputBindings( ### `timestep_hint(::Type{<:MyModel})` -Optional compatibility hint when `TimeStepModel(...)` is not provided. +Optional compatibility hint when `PlantSimEngine.TimeStepModel(...)` is not provided. Default: @@ -95,8 +95,8 @@ Expected value: Where: -- `bindings` is compatible with `MeteoBindings(...)`, -- `window` is compatible with `MeteoWindow(...)`. +- `bindings` is compatible with `PlantSimEngine.MeteoBindings(...)`, +- `window` is compatible with `PlantSimEngine.MeteoWindow(...)`. ### `meteo_inputs_(::MyModel)` ### `meteo_outputs_(::MyModel)` @@ -149,11 +149,11 @@ Defaults are conservative (`dependent`) and can be overridden when safe. Runtime precedence is intentionally explicit: 1. Input policy: - explicit `InputBindings(..., policy=...)` > inferred from producer `output_policy` > `HoldLast()`. + explicit `PlantSimEngine.InputBindings(..., policy=...)` > inferred from producer `output_policy` > `HoldLast()`. 1. Timestep: - `TimeStepModel(...)` > `timespec(model)` when non-default > meteo base step. + `PlantSimEngine.TimeStepModel(...)` > `timespec(model)` when non-default > meteo base step. 1. Meteo sampling: - explicit `MeteoBindings(...)`/`MeteoWindow(...)` > `meteo_hint(...)` > runtime defaults. + explicit `PlantSimEngine.MeteoBindings(...)`/`PlantSimEngine.MeteoWindow(...)` > `meteo_hint(...)` > runtime defaults. ## Is everything documented? diff --git a/docs/src/multirate/advanced_configuration.md b/docs/src/multirate/advanced_configuration.md index 28197648b..7458d22fb 100644 --- a/docs/src/multirate/advanced_configuration.md +++ b/docs/src/multirate/advanced_configuration.md @@ -1,5 +1,11 @@ # Advanced multi-rate configuration +!!! warning "Legacy mapping configuration" + The qualified transforms on this page are compatibility internals. New + scene/object configurations place source and temporal policy in + `Inputs(...)`, cadence in `TimeStep(...)`, and meteo policy in + `Environment(...)`. + This page collects the multi-rate features that were intentionally kept in the background on the first two pages: @@ -18,25 +24,25 @@ declarations to a mapping. PlantSimEngine tries to keep simple mappings concise: -- if a model does not declare `TimeStepModel(...)`, it follows the meteo +- if a model does not declare `PlantSimEngine.TimeStepModel(...)`, it follows the meteo cadence; -- if an input has a unique producer, `InputBindings(...)` can often be omitted; +- if an input has a unique producer, `PlantSimEngine.InputBindings(...)` can often be omitted; - if a model consumes common `Atmosphere` variables at a coarser cadence, - PlantMeteo default transforms can often replace explicit `MeteoBindings(...)`; + PlantMeteo default transforms can often replace explicit `PlantSimEngine.MeteoBindings(...)`; - if an exported variable has a unique canonical publisher, `OutputRequest(...)` can often omit `process=`. The sections below focus on the cases where that implicit behavior becomes too ambiguous or too limiting. -## 2. Explicit model-to-model bindings with `InputBindings(...)` +## 2. Explicit model-to-model bindings with `PlantSimEngine.InputBindings(...)` The tutorial pages rely on unique-producer inference plus `output_policy(...)` declared on the source models. That is the simplest setup, but it stops being enough as soon as several candidate producers exist or when you want to override the default resampling rule. -Use explicit `InputBindings(...)` when: +Use explicit `PlantSimEngine.InputBindings(...)` when: - several models can produce the same input variable; - the same process exists at several reachable scales; @@ -50,8 +56,8 @@ the day: ```julia plant_daily_spec = ModelSpec(TutorialPlantDailyModel()) |> - TimeStepModel(ClockSpec(24.0, 0.0)) |> - InputBindings(; + PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)) |> + PlantSimEngine.InputBindings(; leaf_assim_h=( process=:tutorialleafhourly, scale=:Leaf, @@ -65,14 +71,14 @@ This is more verbose than inference, but the resulting mapping is also more explicit: anyone reading it can see exactly where the data comes from and how it is reduced. -## 3. Explicit meteorological aggregation with `MeteoBindings(...)` +## 3. Explicit meteorological aggregation with `PlantSimEngine.MeteoBindings(...)` For common `Atmosphere` variables, PlantSimEngine delegates weather sampling to PlantMeteo, and PlantMeteo already defines default transforms. In practice, this -means you often do not need `MeteoBindings(...)` for variables such as `T`, +means you often do not need `PlantSimEngine.MeteoBindings(...)` for variables such as `T`, `Rh`, or aliases like `Ri_SW_q`. -Add explicit `MeteoBindings(...)` when: +Add explicit `PlantSimEngine.MeteoBindings(...)` when: - you want a non-default reducer; - the target variable should come from a differently named source variable; @@ -84,8 +90,8 @@ shortwave radiation energy: ```julia plant_daily_spec = ModelSpec(TutorialPlantDailyModel()) |> - TimeStepModel(ClockSpec(24.0, 0.0)) |> - MeteoBindings( + PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)) |> + PlantSimEngine.MeteoBindings( ; T=MeanWeighted(), Ri_SW_q=(source=:Ri_SW_f, reducer=RadiationEnergy()), @@ -96,31 +102,31 @@ And this variant shows a more genuinely custom rule: ```julia plant_daily_spec = ModelSpec(TutorialPlantDailyModel()) |> - TimeStepModel(ClockSpec(24.0, 0.0)) |> - MeteoBindings( + PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)) |> + PlantSimEngine.MeteoBindings( ; T=(source=:T, reducer=MaxReducer()), rad_peak=(source=:Ri_SW_f, reducer=MaxReducer()), ) ``` -The important point is that `MeteoBindings(...)` is not only about reducing +The important point is that `PlantSimEngine.MeteoBindings(...)` is not only about reducing weather from fast to slow. It is also a way to state the semantics of that reduction explicitly. -## 4. Calendar-aligned windows with `MeteoWindow(...)` +## 4. Calendar-aligned windows with `PlantSimEngine.MeteoWindow(...)` By default, coarser meteo sampling uses rolling windows that follow the model clock. That is often sufficient, but some models are tied to civil periods such as "the current day" or "the current week". -In those cases, use `MeteoWindow(...)` to replace the default trailing window +In those cases, use `PlantSimEngine.MeteoWindow(...)` to replace the default trailing window with a calendar-aligned one: ```julia plant_daily_spec = ModelSpec(TutorialPlantDailyModel()) |> - TimeStepModel(ClockSpec(24.0, 0.0)) |> - MeteoWindow( + PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)) |> + PlantSimEngine.MeteoWindow( CalendarWindow( :day; anchor=:current_period, diff --git a/docs/src/multirate/introduction.md b/docs/src/multirate/introduction.md index a0556ceae..55d5fec71 100644 --- a/docs/src/multirate/introduction.md +++ b/docs/src/multirate/introduction.md @@ -1,5 +1,11 @@ # Introduction to multi-rate execution +!!! warning "Legacy mapping configuration" + The scheduling concepts remain valid, but this page uses qualified + compatibility transforms. New scene/object simulations configure rates + with `TimeStep(...)`, value transfer with `Inputs(...)`, and meteorology + with `Environment(...)`. + This page introduces the basic ideas behind multi-rate execution in PlantSimEngine. @@ -24,7 +30,7 @@ Before building a larger example, it helps to establish two important rules: ### Simple example with implicit meteo cadence -Model may define a trait calles `timestep_hint` that describes the acceptable and preferred cadences for that model. However, that trait is purely descriptive: it does not force the model to run at any particular rate. If you want to force a model to run at a specific cadence, you must declare an explicit `TimeStepModel(...)` in the mapping. Otherwise, the model will simply run whenever the meteo cadence allows it to, and the `timestep_hint` can be used for validation or explanation but does not silently reschedule the model. +Model may define a trait calles `timestep_hint` that describes the acceptable and preferred cadences for that model. However, that trait is purely descriptive: it does not force the model to run at any particular rate. If you want to force a model to run at a specific cadence, you must declare an explicit `PlantSimEngine.TimeStepModel(...)` in the mapping. Otherwise, the model will simply run whenever the meteo cadence allows it to, and the `timestep_hint` can be used for validation or explanation but does not silently reschedule the model. Let's define a tiny model that simply counts how many times it ran, then feed it three 30-minute weather rows: @@ -54,10 +60,10 @@ end PlantSimEngine.timestep_hint(::Type{<:TutorialMeteoDrivenModel}) = (; required=(Minute(30), Hour(2)), preferred=Hour(1)) ``` -This model is designed to run between every 30 minutes and every 2 hours, with a preferred cadence of 1 hour. Let's make a mapping with the model but without an explicit `TimeStepModel(...)`: +This model is designed to run between every 30 minutes and every 2 hours, with a preferred cadence of 1 hour. Let's make a mapping with the model but without an explicit `PlantSimEngine.TimeStepModel(...)`: ```@example multirate_timestep_flow -mapping = ModelMapping(:Leaf => (TutorialMeteoDrivenModel(Ref(0)),)) +mapping = PlantSimEngine.ModelMapping(:Leaf => (TutorialMeteoDrivenModel(Ref(0)),)) ``` Let's define a 30-minute weather table with three rows: @@ -100,7 +106,7 @@ that, PlantSimEngine needs instructions for two distinct questions: - how to combine 30-minute meteorological rows into the hourly meteo seen by the coarse model. -That is what `InputBindings(...)` and `MeteoBindings(...)` are for. +That is what `PlantSimEngine.InputBindings(...)` and `PlantSimEngine.MeteoBindings(...)` are for. In this tiny example, we keep the mapping simple by declaring the default reduction policy on the source model itself with `output_policy(...)`. Since `A` has a unique producer on the same scale, PlantSimEngine can infer the source @@ -145,22 +151,22 @@ end Now we can declare a mapping that says the hourly model runs every hour, even though its source data arrives every 30 minutes. We also declare how to reduce the meteorological inputs to match the hourly cadence: ```@example multirate_timestep_flow -mapping_coarse = ModelMapping( +mapping_coarse = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(TutorialHalfHourSourceModel(Ref(0))), ModelSpec(TutorialHourlyIntegratorModel()) |> - TimeStepModel(Hour(1)) |> - MeteoBindings(; T=MeanWeighted()), + PlantSimEngine.TimeStepModel(Hour(1)) |> + PlantSimEngine.MeteoBindings(; T=MeanWeighted()), ), ) ``` -Setting the `TimeStepModel(Hour(1))` forces the second model to run hourly. Since it consumes `A` from the first model, PlantSimEngine looks at the source model's `output_policy(...)` and sees that it should integrate `A` over the hour using the duration of each 30-minute row as weights. +Setting the `PlantSimEngine.TimeStepModel(Hour(1))` forces the second model to run hourly. Since it consumes `A` from the first model, PlantSimEngine looks at the source model's `output_policy(...)` and sees that it should integrate `A` over the hour using the duration of each 30-minute row as weights. !!! note - If we had omitted `TimeStepModel(Hour(1))`, the hourly model would have simply run on each 30-minute row, and the `output_policy(...)` on the source model would not have been triggered. The hourly model would have received the original 30-minute `A` values instead of an hourly aggregate. This illustrates the key point: `TimeStepModel(...)` is what triggers the multi-rate coupling and the use of reduction policies. + If we had omitted `PlantSimEngine.TimeStepModel(Hour(1))`, the hourly model would have simply run on each 30-minute row, and the `output_policy(...)` on the source model would not have been triggered. The hourly model would have received the original 30-minute `A` values instead of an hourly aggregate. This illustrates the key point: `PlantSimEngine.TimeStepModel(...)` is what triggers the multi-rate coupling and the use of reduction policies. -In our example, the hourly model does not declare a `timestep_hint`, so it can run at any cadence. By declaring `TimeStepModel(Hour(1))`, we explicitly force it to run hourly, which means it will receive aggregated inputs and meteo. +In our example, the hourly model does not declare a `timestep_hint`, so it can run at any cadence. By declaring `PlantSimEngine.TimeStepModel(Hour(1))`, we explicitly force it to run hourly, which means it will receive aggregated inputs and meteo. !!! note Because our hourly model does not declare a `timestep_hint`, it is flexible and can run at any cadence. However, if we had declared a `timestep_hint` that did not include hourly as an acceptable cadence, then PlantSimEngine would have raised an error when we tried to force it to run hourly. Consequently, it is usually a good practice to declare a `timestep_hint` when writing a model, because it helps to ensure that the model is used in a way that is consistent with its design and intended use. diff --git a/docs/src/multirate/multirate_tutorial.md b/docs/src/multirate/multirate_tutorial.md index 49c76dd58..7bc2c8fc7 100644 --- a/docs/src/multirate/multirate_tutorial.md +++ b/docs/src/multirate/multirate_tutorial.md @@ -1,5 +1,10 @@ # Step-by-step multi-rate tutorial (hourly + daily + weekly) +!!! warning "Legacy mapping tutorial" + This tutorial is retained as compatibility coverage. New simulations should + express the same scheduling with `Scene`, `AppliesTo`, `Inputs`, + `TimeStep`, and `Environment`. + This page builds a more complete MTG simulation that mixes three model rates: - hourly at `Leaf`, - daily at `Plant`, @@ -11,7 +16,7 @@ If you want the conceptual overview first, start with [Introduction to multi-rate execution](introduction.md). This page assumes you already understand the two basic ideas introduced there: -1. without `TimeStepModel(...)`, a model follows the meteo cadence; +1. without `PlantSimEngine.TimeStepModel(...)`, a model follows the meteo cadence; 2. once a model is forced to run more coarsely than its inputs, PlantSimEngine must reduce both model outputs and meteorological inputs to match that slower cadence. @@ -175,7 +180,7 @@ This is the heart of the tutorial. The mapping below does three things at once: Two pieces are especially important here: -- `TimeStepModel(...)` states the model cadence; +- `PlantSimEngine.TimeStepModel(...)` states the model cadence; - PlantMeteo reduces meteorological inputs automatically when a model runs more coarsely than the weather data. @@ -198,14 +203,14 @@ the leaf model is the fastest model in this example and directly consumes the hourly weather rows: ```@example multirate_tutorial -leaf_spec = TutorialLeafHourlyModel() |> ModelSpec |> TimeStepModel(hourly) +leaf_spec = TutorialLeafHourlyModel() |> ModelSpec |> PlantSimEngine.TimeStepModel(hourly) ``` So at this point we have simply said: "run the leaf model every hour" The daily plant model is where multi-rate coupling becomes visible. It: -- receives `leaf_assim_h` from the `:Leaf` scale through `MultiScaleModel(...)`; +- receives `leaf_assim_h` from the `:Leaf` scale through `PlantSimEngine.MultiScaleModel(...)`; - runs daily; - receives daily meteorological aggregates from the hourly weather automatically. @@ -221,8 +226,8 @@ directly. Instead, it sees a daily view of those data: plant_daily_spec = TutorialPlantDailyModel() |> ModelSpec |> - MultiScaleModel([:leaf_assim_h => :Leaf]) |> - TimeStepModel(daily) + PlantSimEngine.MultiScaleModel([:leaf_assim_h => :Leaf]) |> + PlantSimEngine.TimeStepModel(daily) ``` This block is the first place where the "multi-rate" behavior is really visible: @@ -238,7 +243,7 @@ explicit binding here: plant_weekly_spec = TutorialPlantWeeklyModel() |> ModelSpec |> - TimeStepModel(weekly) + PlantSimEngine.TimeStepModel(weekly) ``` So this weekly model effectively says: "take the daily plant assimilation stream, @@ -247,7 +252,7 @@ reduce it again to my weekly cadence, and run once per week." We can now assemble the full mapping: ```@example multirate_tutorial -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Leaf => (leaf_spec,), :Plant => (plant_daily_spec, plant_weekly_spec), ) @@ -257,30 +262,30 @@ Reading this mapping from top to bottom: - the `Leaf` model runs hourly and produces `leaf_assim_h`; - the daily `Plant` model receives leaf values from the `Leaf` scale through - `MultiScaleModel([:leaf_assim_h => :Leaf])`, then integrates them over a day; + `PlantSimEngine.MultiScaleModel([:leaf_assim_h => :Leaf])`, then integrates them over a day; - that same daily model also receives daily meteorological summaries through the default PlantMeteo sampling rules; - the weekly `Plant` model integrates the daily plant output into one weekly value. !!! note - In this tutorial, explicit `InputBindings(...)` are omitted because each + In this tutorial, explicit `PlantSimEngine.InputBindings(...)` are omitted because each input has a unique, inferable producer and the default reduction policy is declared on the source model with `output_policy(...)`. - In more complex mappings, you should use explicit `InputBindings(process=..., scale=..., var=..., policy=...)` when: + In more complex mappings, you should use explicit `PlantSimEngine.InputBindings(process=..., scale=..., var=..., policy=...)` when: - several models can produce the same input variable; - the same process exists at several reachable scales; - the source variable has a different name than the consumer input; - you want to override the producer's default policy for a specific mapping. !!! note - `MeteoBindings(...)` is also omitted on purpose in the main example. + `PlantSimEngine.MeteoBindings(...)` is also omitted on purpose in the main example. PlantSimEngine delegates weather sampling to PlantMeteo, which already defines default transformations for common `Atmosphere` variables such as `T`, `Rh`, and radiation aliases like `Ri_SW_q`. - Add explicit `MeteoBindings(...)` when: + Add explicit `PlantSimEngine.MeteoBindings(...)` when: - you want a non-default reducer; - the model expects a target variable with a different source name; - the variable is not covered by PlantMeteo default transforms; @@ -289,9 +294,9 @@ Reading this mapping from top to bottom: ```@example multirate_tutorial # The same daily model, with weather aggregation rules written explicitly. plant_daily_spec_explicit_meteo = ModelSpec(TutorialPlantDailyModel()) |> - MultiScaleModel([:leaf_assim_h => :Leaf]) |> - TimeStepModel(daily) |> - MeteoBindings( + PlantSimEngine.MultiScaleModel([:leaf_assim_h => :Leaf]) |> + PlantSimEngine.TimeStepModel(daily) |> + PlantSimEngine.MeteoBindings( ; T=MeanWeighted(), Ri_SW_q=(source=:Ri_SW_f, reducer=RadiationEnergy()), @@ -418,11 +423,11 @@ This page keeps the main walkthrough focused on a complete but still compact example. Once that example is clear, the next step is usually to learn the explicit configuration tools that become useful in larger mappings: -- `InputBindings(...)` when inference is ambiguous or too implicit; -- `MeteoBindings(...)` when PlantMeteo defaults are not enough; -- `MeteoWindow(...)` for calendar-aligned aggregation; +- `PlantSimEngine.InputBindings(...)` when inference is ambiguous or too implicit; +- `PlantSimEngine.MeteoBindings(...)` when PlantMeteo defaults are not enough; +- `PlantSimEngine.MeteoWindow(...)` for calendar-aligned aggregation; - `OutputRequest(...)` when you want explicit export-time clocks and policies; -- `ScopeModel(...)`, `explain_model_specs(...)`, and `resolved_model_specs(...)` +- `PlantSimEngine.ScopeModel(...)`, `explain_model_specs(...)`, and `resolved_model_specs(...)` for larger and harder-to-debug MTGs. Those topics are grouped in diff --git a/docs/src/multiscale/multiscale.md b/docs/src/multiscale/multiscale.md index 062520811..4ee1ab9dc 100644 --- a/docs/src/multiscale/multiscale.md +++ b/docs/src/multiscale/multiscale.md @@ -1,5 +1,10 @@ # Multi-scale variable mapping +!!! warning "Legacy MTG mapping configuration" + This page is retained for compatibility and migration reference. Use + `Scene`, `ObjectTemplate`, `ObjectInstance`, `AppliesTo`, and `Inputs` for + new multiscale simulations. + The previous page showed how to convert a single-scale simulation to multi-scale. This page provides another example showcasing the nuances in variable mapping, with a more complex fully multiscale version of a prior simulation. The models will all be taken form the [examples folder](https://github.com/VirtualPlantLab/PlantSimEngine.jl/tree/main/examples). @@ -24,7 +29,7 @@ It resembles the ToyAssimGrowth model used in the single-scale simulation [Model Our mapping between scale and model is therefore: ```@example usepkg -mapping = ModelMapping(:Leaf => ToyAssimModel()) +mapping = PlantSimEngine.ModelMapping(:Leaf => ToyAssimModel()) ``` Just like in single-scale simulations, we can call `to_initialize` to check whether variables need to be initialised. It will this time index by scale: @@ -38,7 +43,7 @@ In this example, the ToyAssimModel needs `:aPPFD` and `:soil_water_content` as i The initialization values for the variables can be passed along via a [`Status`](@ref) object: ```@example usepkg -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Leaf => ( ToyAssimModel(), Status(aPPFD=1300.0, soil_water_content=0.5), @@ -61,10 +66,10 @@ It also makes sense to have that model operate at a different scale than the :Le ToyAssimModel is now makes use of the `soil_water_content` variable from the `:Soil` scale, instead of at its own scale via the `Status` initialization. We therefore need to map `soil_water_content` from the :Soil to the :Leaf scale by wrapping `ToyAssimModel` in a `MultiScaleModel`: ```@example usepkg -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Soil => ToySoilWaterModel(), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => :Soil => :soil_water_content,], ), @@ -91,17 +96,17 @@ Once again, `to_initialize` returns an empty dictionary, meaning the mapping is Let's now expand this mapping, to showcase other ways in which variables can be mapped from one scale to another. We'll keep the first two models, and add several more to simulate a couple of other processes within our plant. ```@example usepkg -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Scene => ToyDegreeDaysCumulModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[ :TT_cu => :Scene, ], ), Beer(0.6), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ :carbon_assimilation => [:Leaf], @@ -109,13 +114,13 @@ mapping = ModelMapping( :carbon_allocation => [:Leaf, :Internode] ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], ), ), :Internode => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), mapped_variables=[:TT => :Scene,], ), @@ -123,11 +128,11 @@ mapping = ModelMapping( Status(carbon_biomass=1.0), ), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => :Soil, :aPPFD => :Plant], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), mapped_variables=[:TT => :Scene,], ), @@ -144,7 +149,7 @@ nothing # hide This mapping might seem a little more daunting than previous examples, but several models should be recognizable in passing. In fact, you can consider this mapping to be an enhanced and more complex multi-scale version of a previous single-scale example, the coupling between photosynthesis model, a LAI model and a carbon biomass increment model, used in the [Model switching](@ref) subsection. ```julia -models2 = ModelMapping( +models2 = PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.5), ToyAssimGrowthModel(); diff --git a/docs/src/multiscale/multiscale_considerations.md b/docs/src/multiscale/multiscale_considerations.md index 226ff0c96..549aceda4 100644 --- a/docs/src/multiscale/multiscale_considerations.md +++ b/docs/src/multiscale/multiscale_considerations.md @@ -54,9 +54,13 @@ When users define which models they use, PlantSimEngine cannot determine in adva The user therefore needs to indicate for a simulation's which models are related to which scale. -A multi-scale mapping links models to the scale at which they operate, and is also implemented in a [`ModelMapping`](@ref), tying a scale, such as :Leaf to models operating at that scale, such as "LeafSurfaceAreaModel". +A legacy multi-scale mapping links models to the scale at which they operate, +and is implemented with `PlantSimEngine.ModelMapping(...)`, tying a scale, such +as `:Leaf`, to models operating at that scale, such as +`LeafSurfaceAreaModel`. New multiscale work should prefer `Scene`, `Object`, +`AppliesTo(...)`, and `Inputs(...)`. -Multi-scale models can be similar models to the ones found in earlier sections, or, if they need to make use of variables at other scales, may need to be wrapped as part of a [`MultiScaleModel`](@ref) object. Many models are not tied to a particular scale, which means those models can be reused at different scales or in single-scale simulations. +Multi-scale models can be similar models to the ones found in earlier sections, or, if they need to make use of variables at other scales, may need to be wrapped as part of a [`PlantSimEngine.MultiScaleModel`](@ref) object. Many models are not tied to a particular scale, which means those models can be reused at different scales or in single-scale simulations. ## The simulation operates on an MTG @@ -68,7 +72,7 @@ This has two **important** consequences in terms of running a simulation : - First, **any scale absent from the MTG will not be run**. If your MTG contains no leaves, then no model operating at the scale :Leaf will be able to run until a :Leaf organ is created and a node is added in the MTG. Otherwise, it has no MTG node to operate on. The only exceptions are hard dependency models which can be called from a different scale, since they can be called directly by a model on a node at a different existing scale, even if there is no node at their own scale. -- Secondly, models only have access to **local** organ information. The [`status`](@ref) argument in the [`run!`](@ref) function only contains variables **at the model's scale**, unless variables from other scales are mapped via a [`MultiScaleModel`](@ref) wrapping. +- Secondly, models only have access to **local** organ information. The [`status`](@ref) argument in the [`run!`](@ref) function only contains variables **at the model's scale**, unless variables from other scales are mapped via a [`PlantSimEngine.MultiScaleModel`](@ref) wrapping. ## The run! function's signature @@ -78,7 +82,11 @@ The [`run!`](@ref) function differs slightly from its single-scale version. The run!(mtg, mapping::ModelMapping, meteo, constants, extra; nsteps, tracked_outputs) ``` -Instead of a just the [`ModelMapping`](@ref), it also takes an MTG as the first argument. The optional `meteo` and `constants` argument are identical to the single-scale version. The `extra` argument is now reserved and should not be used. A new `nsteps` keyword argument is available to restrict the simulation to a specified number of steps. +Instead of just the compatibility mapping, it also takes an MTG as the first +argument. The optional `meteo` and `constants` arguments are identical to the +single-scale version. The `extra` argument is now reserved and should not be +used. A new `nsteps` keyword argument is available to restrict the simulation +to a specified number of steps. ## Multi-scale output data structure @@ -149,7 +157,7 @@ Multi-scale simulations, especially for plants which have thousands of leaves, i Those tracked variables also need to be indexed by scale to avoid ambiguity: ```julia -outs = ModelMapping( +outs = PlantSimEngine.ModelMapping( :Scene => (:TT, :TT_cu,), :Plant => (:aPPFD, :LAI), :Leaf => (:carbon_assimilation, :carbon_demand, :carbon_allocation, :TT), diff --git a/docs/src/multiscale/multiscale_coupling.md b/docs/src/multiscale/multiscale_coupling.md index 15d75ec29..64e50715e 100644 --- a/docs/src/multiscale/multiscale_coupling.md +++ b/docs/src/multiscale/multiscale_coupling.md @@ -1,6 +1,11 @@ # Handling dependencies in a multiscale context +!!! warning "Legacy MTG mapping configuration" + This page describes compatibility mapping internals. New value coupling + uses `Inputs(...)`; manually controlled hard coupling uses `Calls(...)`, + `call_target(s)`, and `run_call!`. + ```@contents Pages = ["multiscale_coupling.md"] Depth = 3 @@ -12,14 +17,14 @@ In the detailed example discussed previously [Multi-scale variable mapping](@ref ```julia :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[ :TT_cu => :Scene, ], ), ... - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ :carbon_assimilation => [:Leaf], @@ -91,10 +96,10 @@ Here's a concrete example in [XPalm](https://github.com/PalmStudio/XPalm.jl), an The user-mapping includes the required models at specific organ levels. Here's the relevant portion of the mapping for the male reproductive organ : ```julia -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( ... :Male => - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=XPalm.InitiationAgeFromPlantAge(), mapped_variables=[:plant_age => :Plant,], ), diff --git a/docs/src/multiscale/multiscale_cyclic.md b/docs/src/multiscale/multiscale_cyclic.md index f0c62bb72..976ede8ae 100644 --- a/docs/src/multiscale/multiscale_cyclic.md +++ b/docs/src/multiscale/multiscale_cyclic.md @@ -1,5 +1,9 @@ # Avoiding cyclic dependencies +!!! warning "Legacy MTG mapping configuration" + This page uses the compatibility mapping runtime. The unified scene/object + graph supports `PreviousTimeStep(...)` directly inside `Inputs(...)`. + When defining a mapping between models and scales, it is important to avoid cyclic dependencies. A cyclic dependency occurs when a model at a given scale depends on a model at another scale that depends on the first model. Cyclic dependencies are bad because they lead to an infinite loop in the simulation (the dependency graph keeps cycling indefinitely). PlantSimEngine will detect cyclic dependencies and raise an error if one is found. The error message indicates the models involved in the cycle, and the model that is causing the cycle will be highlighted in red. @@ -10,16 +14,16 @@ For example the following mapping will raise an error: Example mapping ```julia - mapping_cyclic = ModelMapping( + mapping_cyclic = PlantSimEngine.ModelMapping( :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ :carbon_demand => [:Leaf, :Internode], :carbon_allocation => [:Leaf, :Internode] ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], ), @@ -72,16 +76,16 @@ We can fix our previous mapping by computing the organs respiration using the ca !!! details ```@julia - mapping_nocyclic = ModelMapping( + mapping_nocyclic = PlantSimEngine.ModelMapping( :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapping=[ :carbon_demand => [:Leaf, :Internode], :carbon_allocation => [:Leaf, :Internode] ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], ), @@ -89,7 +93,7 @@ We can fix our previous mapping by computing the organs respiration using the ca ), :Internode => ( ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), mapped_variables=[PreviousTimeStep(:carbon_biomass),], #! this is where we break the cyclic dependency (first break) ), @@ -97,7 +101,7 @@ We can fix our previous mapping by computing the organs respiration using the ca ), :Leaf => ( ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), mapped_variables=[PreviousTimeStep(:carbon_biomass),], #! this is where we break the cyclic dependency (second break) ), @@ -108,8 +112,8 @@ We can fix our previous mapping by computing the organs respiration using the ca nothing # hide ``` -The `ToyMaintenanceRespirationModel` models are now defined as [`MultiScaleModel`](@ref), and the `carbon_biomass` variable is wrapped in a `PreviousTimeStep` structure. This structure tells PlantSimEngine to take the value of the variable from the previous time step, breaking the cyclic dependency. +The `ToyMaintenanceRespirationModel` models are now defined as [`PlantSimEngine.MultiScaleModel`](@ref), and the `carbon_biomass` variable is wrapped in a `PreviousTimeStep` structure. This structure tells PlantSimEngine to take the value of the variable from the previous time step, breaking the cyclic dependency. !!! note [`PreviousTimeStep`](@ref) tells PlantSimEngine to take the value of the previous time step for the variable it wraps, or the value at initialization for the first time step. The value at initialization is the one provided by default in the models inputs, but is usually provided in the [`Status`](@ref) structure to override this default. - A [`PreviousTimeStep`](@ref) is used to wrap the **input** variable of a model, with or without a mapping to another scale *e.g.* `PreviousTimeStep(:carbon_biomass) => :Leaf`. \ No newline at end of file + A [`PreviousTimeStep`](@ref) is used to wrap the **input** variable of a model, with or without a mapping to another scale *e.g.* `PreviousTimeStep(:carbon_biomass) => :Leaf`. diff --git a/docs/src/multiscale/multiscale_example_1.md b/docs/src/multiscale/multiscale_example_1.md index 19dd671e0..8433b02b5 100644 --- a/docs/src/multiscale/multiscale_example_1.md +++ b/docs/src/multiscale/multiscale_example_1.md @@ -55,7 +55,7 @@ Let's also add a very artificial limiting factor: if the total leaf surface area We can expect the simulation mapping to look like a more complex version of the following: ```julia -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Scene => ToyDegreeDaysCumulModel(), :Plant => ToyStockComputationModel(), :Internode => ToyCustomInternodeEmergence(), @@ -218,10 +218,10 @@ as opposed to the single-valued carbon stock mapped variable : And of course, some variables need to be initialized in the status: ```@example usepkg -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Scene => ToyDegreeDaysCumulModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyStockComputationModel(), mapped_variables=[ :carbon_captured=>[:Leaf], @@ -231,7 +231,7 @@ mapping = ModelMapping( Status(carbon_stock = 0.0) ), :Internode => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCustomInternodeEmergence(),#TT_emergence=20.0), mapped_variables=[:TT_cu => :Scene, PreviousTimeStep(:carbon_stock)=>:Plant], diff --git a/docs/src/multiscale/multiscale_example_2.md b/docs/src/multiscale/multiscale_example_2.md index 539399ddf..4e7cd815b 100644 --- a/docs/src/multiscale/multiscale_example_2.md +++ b/docs/src/multiscale/multiscale_example_2.md @@ -196,10 +196,10 @@ The resource storage and internode emergence models now need a couple of extra w The :Root organ is added to the mapping with its own models. New parameters need to be initialized. ```@example usepkg -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Scene => ToyDegreeDaysCumulModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyStockComputationModel(), mapped_variables=[ :carbon_captured=>[:Leaf], @@ -212,7 +212,7 @@ mapping = ModelMapping( Status(water_stock = 0.0, carbon_stock = 0.0) ), :Internode => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCustomInternodeEmergence(),#TT_emergence=20.0), mapped_variables=[:TT_cu => :Scene, PreviousTimeStep(:water_stock)=>:Plant, @@ -220,7 +220,7 @@ mapping = ModelMapping( ), Status(carbon_organ_creation_consumed=0.0), ), -:Root => ( MultiScaleModel( +:Root => ( PlantSimEngine.MultiScaleModel( model=ToyRootGrowthModel(10.0, 50.0, 10), mapped_variables=[PreviousTimeStep(:carbon_stock)=>:Plant, PreviousTimeStep(:water_stock)=>:Plant], diff --git a/docs/src/multiscale/multiscale_example_3.md b/docs/src/multiscale/multiscale_example_3.md index 582ec984c..18dd03acf 100644 --- a/docs/src/multiscale/multiscale_example_3.md +++ b/docs/src/multiscale/multiscale_example_3.md @@ -175,10 +175,10 @@ end PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyLeafCarbonCaptureModel}) = PlantSimEngine.IsObjectIndependent() PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyLeafCarbonCaptureModel}) = PlantSimEngine.IsTimeStepIndependent() -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Scene => ToyDegreeDaysCumulModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyStockComputationModel(), mapped_variables=[ :carbon_captured=>[:Leaf], @@ -191,7 +191,7 @@ mapping = ModelMapping( Status(water_stock = 0.0, carbon_stock = 0.0) ), :Internode => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCustomInternodeEmergence(),#TT_emergence=20.0), mapped_variables=[:TT_cu => :Scene, PreviousTimeStep(:water_stock)=>:Plant, @@ -199,7 +199,7 @@ mapping = ModelMapping( ), Status(carbon_organ_creation_consumed=0.0), ), -:Root => ( MultiScaleModel( +:Root => ( PlantSimEngine.MultiScaleModel( model=ToyRootGrowthModel(10.0, 50.0, 10), mapped_variables=[PreviousTimeStep(:carbon_stock)=>:Plant, PreviousTimeStep(:water_stock)=>:Plant], @@ -416,10 +416,10 @@ end The new mapping only has straightforward changes. Some models cease to be multi-scale, others require new variables to be mapped for them. `carbon_root_creation_consumed` ceases to be a vector mapping and is a scalar variable. ```julia -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Scene => ToyDegreeDaysCumulModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyStockComputationModel(), mapped_variables=[ :carbon_captured=>[:Leaf], @@ -429,13 +429,13 @@ mapping = ModelMapping( ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyRootGrowthDecisionModel(10.0, 50.0), ), Status(water_stock = 0.0, carbon_stock = 0.0) ), :Internode => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCustomInternodeEmergence(),#TT_emergence=20.0), mapped_variables=[:TT_cu => :Scene, :water_stock=>:Plant, @@ -471,10 +471,10 @@ The solution is hopefully quite intuitive : when we compute resource stocks, we The relevant part of the mapping that needs to be updated is the following: ```julia -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( ... :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyStockComputationModel(), mapped_variables=[ :carbon_captured=>[:Leaf], diff --git a/docs/src/multiscale/single_to_multiscale.md b/docs/src/multiscale/single_to_multiscale.md index 7e8af7caf..09afeaa65 100644 --- a/docs/src/multiscale/single_to_multiscale.md +++ b/docs/src/multiscale/single_to_multiscale.md @@ -1,5 +1,10 @@ # Converting a single-scale simulation to multi-scale +!!! warning "Legacy multiscale configuration" + This page documents `ModelMapping` and `MultiScaleModel`. New multiscale + scenarios should use `Scene`, `Object`, `AppliesTo`, and `Inputs`; see + [Migrating To The Scene/Object API](../migration_scene_object.md). + ```@meta CurrentModule = PlantSimEngine ``` @@ -10,7 +15,7 @@ using PlantSimEngine using PlantSimEngine.Examples using MultiScaleTreeGraph meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) -models_singlescale = ModelMapping( +models_singlescale = PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.5), ToyRUEGrowthModel(0.2); @@ -31,7 +36,10 @@ Depth = 3 # From single to multi-scale mapping -For example, let's return to the [`ModelMapping`](@ref) coupling a light interception model, a Leaf Area Index model, and a carbon biomass increment model that was discussed in the [Model switching](@ref) subsection: +For example, let's return to the legacy +`PlantSimEngine.ModelMapping(...)` compatibility example coupling a light +interception model, a Leaf Area Index model, and a carbon biomass increment +model: ```@example usepkg using PlantMeteo, Dates @@ -40,7 +48,7 @@ using PlantSimEngine.Examples meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) -models_singlescale = ModelMapping( +models_singlescale = PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.5), ToyRUEGrowthModel(0.2); @@ -56,7 +64,7 @@ Those models all operate on a simplified model of a single plant, without any or We can therefore convert this into the following mapping: ```@example usepkg -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Plant => ( ToyLAIModel(), Beer(0.5), @@ -130,7 +138,7 @@ end ``` !!! note - The only accessible variables in the [`run!`](@ref) function via the status are the ones that are local to the :Scene scale. This isn't explicit at first glance, but very important to keep in mind when developing models, or using them at different scales. If variables from other scales are required, then they need to be mapped via a [`MultiScaleModel`](@ref), or sometimes a more complex coupling is necessary. + The only accessible variables in the [`run!`](@ref) function via the status are the ones that are local to the :Scene scale. This isn't explicit at first glance, but very important to keep in mind when developing models, or using them at different scales. If variables from other scales are required, then they need to be mapped via a [`PlantSimEngine.MultiScaleModel`](@ref), or sometimes a more complex coupling is necessary. ### Linking the new TT_cu model to a scale in the mapping @@ -149,14 +157,14 @@ mtg_multiscale = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Scen The cumulated thermal time (`:TT_cu`) which was previously provided to the LAI model as a simulation parameter now needs to be mapped from the :Scene scale level. -This is done by wrapping our ToyLAIModel in a dedicated structure called a [`MultiScaleModel`](@ref). A [`MultiScaleModel`](@ref) requires two keyword arguments : `model`, indicating the model for which some variables are mapped, and `mapped_variables`, indicating which scale link to which variables, and potentially renaming them. +This is done by wrapping our ToyLAIModel in a dedicated structure called a [`PlantSimEngine.MultiScaleModel`](@ref). A [`PlantSimEngine.MultiScaleModel`](@ref) requires two keyword arguments : `model`, indicating the model for which some variables are mapped, and `mapped_variables`, indicating which scale link to which variables, and potentially renaming them. There can be different kinds of variable mapping with slightly different syntax, but in our case, only a single scalar value of the TT_cu is passed from the :Scene to the :Plant scale. -This gives us the following declaration with the [`MultiScaleModel`](@ref) wrapper for our LAI model: +This gives us the following declaration with the [`PlantSimEngine.MultiScaleModel`](@ref) wrapper for our LAI model: ```@example usepkg -MultiScaleModel( +PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[ :TT_cu => :Scene, @@ -166,10 +174,10 @@ MultiScaleModel( and the new mapping with two scales: ```@example usepkg -mapping_multiscale = ModelMapping( +mapping_multiscale = PlantSimEngine.ModelMapping( :Scene => ToyTt_CuModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[ :TT_cu => :Scene, diff --git a/docs/src/prerequisites/installing_plantsimengine.md b/docs/src/prerequisites/installing_plantsimengine.md index d992f7a20..9cca3c015 100644 --- a/docs/src/prerequisites/installing_plantsimengine.md +++ b/docs/src/prerequisites/installing_plantsimengine.md @@ -68,7 +68,7 @@ Assuming you've setup you're environement, correctly added `PlantMeteo` and `Pla using PlantSimEngine, PlantMeteo, Dates using PlantSimEngine.Examples meteo = Atmosphere(T = 20.0, Wind = 1.0, Rh = 0.65, Ri_PAR_f = 500.0) -leaf = ModelMapping(Beer(0.5), status = (LAI = 2.0,)) +leaf = PlantSimEngine.ModelMapping(Beer(0.5), status = (LAI = 2.0,)) out_sim = run!(leaf, meteo) ``` diff --git a/docs/src/scene_object/quickstart.md b/docs/src/scene_object/quickstart.md new file mode 100644 index 000000000..8dc4e8068 --- /dev/null +++ b/docs/src/scene_object/quickstart.md @@ -0,0 +1,254 @@ +# Scene/Object Quickstart + +This page is the shortest path to a native scene/object simulation. + +Use this API for new multiscale, multi-plant, soil, microclimate, and +scene-scale simulations: + +```julia +Scene +Object +ModelSpec +AppliesTo +Inputs +Calls +Updates +TimeStep +Environment +``` + +Legacy `ModelMapping`, `MultiScaleModel`, domain, and route APIs are retained +as compatibility tools while old examples are migrated. New scenarios should +start from `Scene` and model applications. + +```@setup scene_object_quickstart +using PlantSimEngine, PlantMeteo, Dates, DataFrames +using PlantSimEngine.Examples +``` + +## One Object, Several Models + +The first scene has one object, `:scene`, and three model applications: + +- `ToyDegreeDaysCumulModel` computes daily thermal time; +- `ToyLAIModel` consumes cumulative thermal time and computes LAI; +- `Beer` consumes LAI and meteorology to compute absorbed PAR. + +The model implementations are ordinary PlantSimEngine kernels. The scene +application layer decides where they run and at which cadence. + +```@example scene_object_quickstart +meteo_day = read_weather( + joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"); + duration=Dates.Day, +) + +scene = Scene( + Object(:scene; scale=:Scene, kind=:scene); + applications=( + ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(ToyLAIModel(); name=:lai) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(Beer(0.6); name=:light_interception) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ), + environment=meteo_day, +) + +sim = run!(scene; steps=30, constants=Constants()) +out = DataFrame(collect_outputs(sim; sink=nothing)) +first(out, 6) +``` + +The scene object now holds the latest status values: + +```@example scene_object_quickstart +scene_status = only(scene_objects(scene; scale=:Scene)).status +(TT_cu=scene_status.TT_cu, LAI=scene_status.LAI, aPPFD=scene_status.aPPFD) +``` + +## Inspect The Compiled Bindings + +The compiler infers unambiguous same-object dependencies from declared model +inputs and outputs: + +- `:lai` reads `TT_cu` from `:degree_days`; +- `:light_interception` reads `LAI` from `:lai`. + +```@example scene_object_quickstart +select( + DataFrame(explain_bindings(refresh_bindings!(scene))), + :application_id, + :input, + :source_application_ids, + :carrier_kind, + :copy_semantics, +) +``` + +`carrier_kind = :ref` and `copy_semantics = :live_references` mean the +consumer sees a shared reference rather than a copied value. + +For runtime performance diagnostics, inspect the execution plan: + +```@example scene_object_quickstart +select( + DataFrame(explain_execution_plan(scene)), + :application_id, + :object_ids, + :batch_size, + :inner_loop_dispatch, +) +``` + +## Request Outputs + +By default, scene runs retain all published streams. For large scenes, pass +`OutputRequest` values to retain and materialize only selected publisher +streams plus any streams needed by temporal inputs. + +```@example scene_object_quickstart +request = OutputRequest( + :Scene, + :LAI; + name=:lai_every_two_days, + process=:LAI_Dynamic, + policy=HoldLast(), + clock=Day(2), +) + +requested_sim = run!( + scene; + steps=30, + constants=Constants(), + tracked_outputs=request, +) + +collect_outputs(requested_sim, :lai_every_two_days; sink=nothing)[1:4] +``` + +The retention explanation reports why a stream was kept: + +```@example scene_object_quickstart +explain_output_retention(requested_sim) +``` + +## Many Objects As Inputs + +Use `Inputs(...)` when a model needs values from selected objects. This +scene-scale LAI model reads live references to the surface of every plant: + +```@example scene_object_quickstart +plant_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :plant_1; + scale=:Plant, + kind=:plant, + parent=:scene, + status=Status(surface=12.0), + ), + Object( + :plant_2; + scale=:Plant, + kind=:plant, + parent=:scene, + status=Status(surface=8.0), + ); + applications=( + ModelSpec(ToyLAIfromLeafAreaModel(100.0); name=:scene_lai) |> + AppliesTo(One(scale=:Scene)) |> + Inputs( + :plant_surfaces => Many( + scale=:Plant, + within=SceneScope(), + var=:surface, + ), + ), + ), +) + +run!(plant_scene) +plant_scene_status = only(scene_objects(plant_scene; scale=:Scene)).status +(total_surface=plant_scene_status.total_surface, LAI=plant_scene_status.LAI) +``` + +The compiled binding shows a `RefVector` carrier: + +```@example scene_object_quickstart +select( + DataFrame(explain_bindings(refresh_bindings!(plant_scene))), + :application_id, + :input, + :source_ids, + :carrier_kind, + :copy_semantics, +) +``` + +If the consumer model runs on each plant, use `within=Self()` to read only +objects inside the current plant. Use `within=SceneScope()` when a scene model +must aggregate all matching objects. + +## Manual Calls + +Use `Calls(...)` when a parent model must directly run selected child models. +This is the mechanism for iterative solvers such as scene energy balance: + +```julia +ModelSpec(SceneEnergyBalance(); name=:scene_energy) |> + AppliesTo(One(scale=:Scene)) |> + Calls( + :leaf_energy => Many( + kind=:plant, + scale=:Leaf, + within=SceneScope(), + process=:energy_balance, + ), + :soil => One( + kind=:soil, + scale=:Soil, + within=SceneScope(), + process=:soil_water, + ), + ) |> + TimeStep(Hour(1)) +``` + +Inside the parent model: + +```julia +function PlantSimEngine.run!(model::SceneEnergyBalance, models, status, meteo, + constants, extra) + for target in call_targets(extra, :leaf_energy) + run_call!(target; meteo=trial_meteo(model, status)) + end + + for target in call_targets(extra, :leaf_energy) + run_call!(target; meteo=accepted_meteo(model, status), publish=true) + end + + return nothing +end +``` + +`run_call!` defaults to `publish=false`, so trial calls mutate target statuses +without publishing temporal streams or mutable environment outputs. The +accepted state should use `publish=true`. + +## Next Steps + +- [Migrating To The Scene/Object API](../migration_scene_object.md) translates + old `ModelMapping`, `MultiScaleModel`, domain, and route scenarios. +- [Public API](../API/API_public.md) lists constructors, selectors, lifecycle + helpers, environment helpers, and explanation helpers. +- [Model traits](../model_traits.md) documents `inputs_`, `outputs_`, `dep`, + `timespec`, `output_policy`, `meteo_inputs_`, and `meteo_outputs_`. +- [MAESPA-style domain example handoff](../dev/maespa_domain_handoff.md) + records the current multi-plant scene energy-balance acceptance example. diff --git a/docs/src/step_by_step/advanced_coupling.md b/docs/src/step_by_step/advanced_coupling.md index 9fc612cff..98701ee34 100644 --- a/docs/src/step_by_step/advanced_coupling.md +++ b/docs/src/step_by_step/advanced_coupling.md @@ -1,64 +1,154 @@ # Coupling more complex models -```@setup usepkg -using PlantSimEngine, PlantMeteo, Dates -# Import the example models defined in the `Examples` sub-module: +```@setup scene_advanced_coupling +using PlantSimEngine, PlantMeteo, Dates, DataFrames using PlantSimEngine.Examples -m = ModelMapping( - Process1Model(2.0), - Process2Model(), - Process3Model(), - Process4Model(), - Process5Model(), - Process6Model(), - Process7Model(), +meteo_day = read_weather( + joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"); + duration=Dates.Day, ) ``` -When two or more models have a two-way interdependency (rather than variables flowing out only one-way from one model into the next), we describe it as a [hard dependency](@ref hard_dependency_def). +Most model coupling is a value dependency: one model writes an output, another +model reads it as an input. Some models need tighter control. For example, an +energy-balance model may call photosynthesis and stomatal-conductance models +several times while it iterates leaf temperature. + +That second case is a manual call dependency. In the scene/object API it is +declared with `Calls(...)`. + +## Soft inputs and manual calls + +Use `Inputs(...)` or inferred same-object bindings when a model only needs a +value. Use `Calls(...)` when the parent model must directly run another model +inside its own `run!` method. + +The example process models in `examples/dummy.jl` contain both patterns: + +- `Process4Model` computes `var1` and `var2`; +- `Process1Model` consumes `var1` and `var2` and computes `var3`; +- `Process2Model` manually calls process 1, then computes `var4` and `var5`; +- `Process3Model` manually calls process 2, then computes `var6`; +- `Process5Model`, `Process6Model`, and `Process7Model` use regular soft + value dependencies. + +## Declaring manual calls in the scenario + +`Calls(...)` is scenario-level wiring. The model kernel remains generic; the +scenario decides which concrete application is called. + +```@example scene_advanced_coupling +complex_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene, status=Status(var0=2.0)); + applications=( + ModelSpec(Process4Model(); name=:prepare_inputs) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(Process1Model(2.0); name=:process1) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(Process2Model(); name=:process2) |> + AppliesTo(One(scale=:Scene)) |> + Calls(:process1 => One(scale=:Scene, process=:process1)) |> + TimeStep(Day(1)), + + ModelSpec(Process3Model(); name=:process3) |> + AppliesTo(One(scale=:Scene)) |> + Calls(:process2 => One(scale=:Scene, process=:process2)) |> + TimeStep(Day(1)), + + ModelSpec(Process5Model(); name=:process5) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(Process7Model(); name=:process7) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(Process6Model(); name=:process6) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ), + environment=meteo_day, +) -This kind of interdependency requires a little more work from the user/modeler for PlantSimEngine to be able to automatically create the dependency graph. +compiled = refresh_bindings!(complex_scene) +select( + DataFrame(explain_calls(compiled)), + :application_id, + :call, + :callee_application_ids, + :callee_object_ids, + :publication_policy, +) +``` -## Declaring hard dependencies +Applications selected by `Calls(...)` are not scheduled as independent root +applications under their caller. They run only when the parent calls them. +This gives the parent full call-stack control. -A model that explicitly and directly calls another process in its [`run!`](@ref) function is part of a hard dependency, or a hard-coupled model. +## Running the coupled scene -Let's go through the example processes and models from a script provided by the package here [examples/dummy.jl](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/examples/dummy.jl) +The regular soft dependencies are still inferred from `inputs_` and +`outputs_`. The scheduler combines those soft edges with the call ownership +rules: -In this script, we declare seven processes and seven models, one for each process. The processes are simply called "process1", "process2"..., and the model implementations are called `Process1Model`, `Process2Model`... +```@example scene_advanced_coupling +select( + DataFrame(explain_schedule(compiled)), + :application_id, + :manual_call_only, + :execution_index, + :clock, +) +``` -When run, `Process2Model` calls another process's [`run!`](@ref) function explicitely, which requires defining that process as a hard-dependency of `Process2Model` : +Run one timestep: -```julia -function PlantSimEngine.run!(::Process2Model, models, status, meteo, constants, extra) - # computing var3 using process1: - run_target!(models, status, :process1; meteo=meteo, constants=constants, extra=extra) - # computing var4 and var5: - status.var4 = status.var3 * 2.0 - status.var5 = status.var4 + 1.0 * meteo.T + 2.0 * meteo.Wind + 3.0 * meteo.Rh -end +```@example scene_advanced_coupling +complex_sim = run!(complex_scene; steps=1, constants=Constants()) +complex_status = only(scene_objects(complex_scene; scale=:Scene)).status +( + var3=complex_status.var3, + var5=complex_status.var5, + var6=complex_status.var6, + var8=complex_status.var8, +) ``` -`Process2Model` is coupled to another process (`process1`), and calls its model's `run` function. The [`run!`](@ref) function is called with the same arguments as the [`run!`](@ref) function of the model that calls it, except that we pass the process we want to simulate as the first argument. - -!!! note - We don't enforce any type of model to simulate `process1`. This is the reason why we can switch so easily between model implementations for any process, by just changing the model in the [`ModelMapping`](@ref). +## Writing new hard-coupled models -A hard-dependency must always be declared to PlantSimEngine. This is done by adding a method to the `dep` function when implementing the model. For example, the hard-dependency to `process1` into `Process2Model` is declared as follows: +For new scene/object models, retrieve manual-call targets from the runtime +context and execute them explicitly: ```julia -PlantSimEngine.dep(::Process2Model) = (process1=AbstractProcess1Model,) -``` - -This way PlantSimEngine knows that `Process2Model` needs a model for the simulation of the `process1` process. To avoid imposing a specific model to be coupled with `Process2Model`, the dependency only requires a model that is a subtype of the abstract parent type `AbstractProcess1Model`. This avoids constraining to the specific `Process1Model` implementation, meaning an alternate model computing the same variables for the same process is still interchangeable with `Process1Model`. +function PlantSimEngine.run!(model::SceneEnergyBalance, models, status, meteo, + constants, extra) + for target in call_targets(extra, :leaf_energy) + run_call!(target; meteo=trial_meteo(model, status)) + end -While not encouraged, if you have a valid reason to force the coupling with a particular model, you can force the dependency to require that model specifically. For example, if we want to use only `Process1Model` for the simulation of `process1`, we would declare the dependency as follows: + for target in call_targets(extra, :leaf_energy) + run_call!(target; meteo=accepted_meteo(model, status), publish=true) + end -```julia -PlantSimEngine.dep(::Process2Model) = (process1=Process1Model,) + return nothing +end ``` -## Examples in the wild +`run_call!` defaults to `publish=false`, which is useful for trial iterations. +Use `publish=true` for the accepted state so temporal streams and mutable +environment outputs are published once. + +The MAESPA-style example uses the same mechanism: a scene energy-balance model +calls all selected leaf energy-balance models and the shared soil model while +it solves canopy microclimate. -You can find a typical example in a companion package: [PlantBioPhysics.jl](https://github.com/VEZY/PlantBiophysics.jl). An energy balance model, the [Monteith model](https://github.com/VEZY/PlantBiophysics.jl/blob/master/src/processes/energy/Monteith.jl), needs to [iteratively run a photosynthesis model](https://github.com/VEZY/PlantBiophysics.jl/blob/c1a75f294109d52dc619f764ce51c6ca1ea897e8/src/processes/energy/Monteith.jl#L154) in its [`run!`](@ref) function. +Older code may still show `dep(model)` with hard dependencies and +`PlantSimEngine.ModelMapping(...)`. Those mechanisms are compatibility +implementation details for old examples. New scenario wiring should use +`Calls(...)`; model authors should keep kernels generic and only require manual +calls when the model really needs call-stack control. diff --git a/docs/src/step_by_step/detailed_first_example.md b/docs/src/step_by_step/detailed_first_example.md index 068341b5e..ec5b940a9 100644 --- a/docs/src/step_by_step/detailed_first_example.md +++ b/docs/src/step_by_step/detailed_first_example.md @@ -1,17 +1,21 @@ -# [Detailed walkthrough of a simple simulation](@id detailed-walkthrough-of-a-simple-simulation) +# [Detailed Walkthrough Of A Simple Simulation](@id detailed-walkthrough-of-a-simple-simulation) -This page walks you through the ins and outs of a basic simulation, mostly aimed at people who have less experience programming, to showcase the various concepts presented earlier and requirements for a simulation in context. +This page walks through a small scene/object simulation. It is written for +readers who are still getting comfortable with Julia and PlantSimEngine. -A working trimmed-down script can be found further down in the [Example simulation](@ref), and other subsections in this page will detail setup and helper functions, and querying outputs. +If you only want examples to copy and modify, see [Quick examples](quick_and_dirty_examples.md). For +multi-object and multi-plant simulations, the same API scales up: add objects, +select them with `AppliesTo(...)`, connect values with `Inputs(...)`, and use +`Calls(...)` when a parent model must manually run child models. -If you simply wish to copy-paste examples and tinker with them, you can find a few examples on the [Quick examples](@ref) page. - -```@setup usepkg -using PlantSimEngine, PlantMeteo, Dates +```@setup detailed_scene +using PlantSimEngine, PlantMeteo, Dates, DataFrames using PlantSimEngine.Examples -meteo = Atmosphere(T = 20.0, Wind = 1.0, Rh = 0.65, Ri_PAR_f = 500.0) -leaf = ModelMapping(Beer(0.5), status = (LAI = 2.0,)) -out_sim = run!(leaf, meteo) + +meteo_day = read_weather( + joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"); + duration=Dates.Day, +) ``` ```@contents @@ -19,226 +23,238 @@ Pages = ["detailed_first_example.md"] Depth = 3 ``` -## Setting up your environment - -For every script in this documentation, you will always need a working Julia environment with PlantSimengine added to it, and usually several other companion packages. Details for getting to that point are provided on the [Installing and running PlantSimEngine](@ref) page. - -## Definitions - -### Processes - -A process in this package defines a biological or physical phenomena. Think of any process happening in a system, such as light interception, photosynthesis, water, carbon and energy fluxes, growth, yield or even electricity produced by solar panels. - -A process is "declared", meaning we define a process, and then implement models for its simulation. In this example, we will make use of a process that was already defined, and for which there already is a model implementation. - -### Models (ModelMapping) - -A process is simulated using a particular implementation, or **a model**. Each model is implemented using a structure that lists the parameters of the model. For example, PlantBiophysics provides the [`Beer`](https://vezy.github.io/PlantBiophysics.jl/stable/functions/#PlantBiophysics.Beer) structure for the implementation of the Beer-Lambert law of light extinction. The process of `light_interception` and the `Beer` model are provided as an example -script in this package too at [`examples/Beer.jl`](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/master/examples/Beer.jl). - -Models can use several types of entries: - -- Parameters -- Meteorological information -- Variables -- Constants -- Extras - -**Parameters** are constant values that are used by the model to compute its outputs, and are exclusive to that model. - -**Meteorological information** contains values that are provided by the user and are used as inputs to the model. It is defined for one time-step, and `PlantSimEngine.jl` takes care of applying the model to each time-steps given by the user. - -**Variables** are either used or computed by the model and can optionally be initialized before the simulation. They can be part of multiple models, computed by one and then used as an input by another. They can also be a global simulation output, or be provided at the start of a simulation by the user. +## Setting Up Your Environment -**Constants** are constant values, usually common between models, *e.g.* the universal gas constant. +Every script needs a Julia environment with PlantSimEngine installed. Most +examples also use companion packages such as PlantMeteo for weather data and +DataFrames for tabular outputs. Installation details are in +[Installing and running PlantSimEngine](@ref). -And **extras** are just extra values that can be used by a model, or serves as a placeholder for internal data. +## The Simulation Pieces -Users declare a set of models used for simulation, as well as the necessary parameters for each model, and whatever variables need to be initialized. This is done using a [`ModelMapping`](@ref) structure. +### Processes And Models -For example let's instantiate a [`ModelMapping`](@ref) with a single model : the Beer-Lambert model of light extinction, used to simulate the light interception process. The model is implemented with the [`Beer`](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/master/examples/Beer.jl) structure and only has one parameter: the extinction coefficient (`k`). +A process is something you want to simulate, such as light interception, +photosynthesis, water flux, growth, yield, or energy balance. -Importing the package: +A model is one implementation of a process. In this page we use the example +`Beer` model, which implements a Beer-Lambert light-interception equation. +Its only parameter is the extinction coefficient `k`. -```@example usepkg -using PlantSimEngine +```@example detailed_scene +fieldnames(Beer) ``` -Import the examples defined in the [`Examples`](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/examples) sub-module (`light_interception` and `Beer`): +The model implementation declares the status variables it reads and writes: -```julia -using PlantSimEngine.Examples +```@example detailed_scene +inputs(Beer(0.5)) ``` -And then declare a [`ModelMapping`](@ref) with the `Beer` model: - -```@example usepkg -m = ModelMapping(Beer(0.5)) +```@example detailed_scene +outputs(Beer(0.5)) ``` -What happened here? We provided an instance of the `Beer` model to a [`ModelMapping`](@ref) to simulate the light interception process. - -## Parameters - -A parameter is a value constant for a simulation that is internal to a model and used for its computations. For example, the Beer-Lambert model uses the extinction coefficient (`k`) to compute the light extinction. The `Beer` structure in the Beer-Lambert model implementation, only has one field: `k`. We can see that using `fieldnames` on the model structure: - -```@example usepkg -fieldnames(Beer) +These declarations are the modeler's contract. The scene/object layer decides +where the model runs and where those values come from. + +### Scene Objects + +A `Scene` contains simulated `Object`s. An object can represent a scene, plant, +axis, leaf, soil layer, sensor, voxel, or any other simulated entity. + +For a first example, we use one object representing the whole scene. The `Beer` +model reads `LAI`, so we initialize that variable on the object status. + +```@example detailed_scene +scene = Scene( + Object( + :scene; + scale=:Scene, + kind=:scene, + status=Status(LAI=2.0), + ); + applications=( + ModelSpec(Beer(0.5); name=:light_interception) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ), + environment=meteo_day, +) ``` -## Variables (inputs, outputs) +`ModelSpec(...)` wraps a reusable model kernel with scenario-level decisions: -Variables are either inputs or outputs (*i.e.* computed) of models. Variables and their values are stored in the [`ModelMapping`](@ref) structure, and are initialized automatically or manually. +- `name=:light_interception` gives the application a stable name; +- `AppliesTo(One(scale=:Scene))` says it runs on the scene object; +- `TimeStep(Day(1))` says it runs daily; +- `environment=meteo_day` supplies weather values such as radiation. -For example, the `Beer` model needs the leaf area index (`LAI`, m² m⁻²) to run. +## Inspecting The Compiled Scene -We can see which variables are passed in as inputs using [`inputs`](@ref): +Before runtime, PlantSimEngine resolves selectors and builds a compiled scene. +This avoids resolving object selections inside the timestep loop. -```@example usepkg -inputs(Beer(0.5)) +```@example detailed_scene +compiled = refresh_bindings!(scene) +select( + DataFrame(explain_scene_applications(compiled)), + :application_id, + :process, + :target_ids, +) ``` -and which are computed outputs of the model using [`outputs`](@ref): +`Beer` has no model-to-model value input in this first scene because `LAI` was +initialized directly on the object status: -```@example usepkg -outputs(Beer(0.5)) +```@example detailed_scene +explain_bindings(compiled) ``` -The [`ModelMapping`](@ref) structure will keep track of every variable's current state when running the simulation, storing them in a field called `status`. We can inspect that field with the [`status`](@ref) function and see that in our example it has two variables: `LAI` and `PPFD`. The first is an input, the second an output (*i.e.* it is computed by the model). +The schedule tells us when each application runs: -```@example usepkg -m = ModelMapping(Beer(0.5)) -keys(status(m)) +```@example detailed_scene +select( + DataFrame(explain_schedule(compiled)), + :application_id, + :dt_seconds, + :root_scheduled, + :manual_call_only, +) ``` -To know which variables should be initialized, we can use [`to_initialize`](@ref): - -```@example usepkg -m = ModelMapping(Beer(0.5)) -to_initialize(m) -``` +## Running The Simulation -Their values are uninitialized though (hence the warnings): +Run the scene with [`run!`](@ref): -```@example usepkg -(m[:LAI], m[:aPPFD]) +```@example detailed_scene +sim = run!(scene; steps=3, constants=Constants()) ``` -Uninitialized variables are initialized to the value given in the [`inputs`](@ref) or [`outputs`](@ref) methods in the model's implementation code, which is usually equal to `typemin()`, *e.g.* `-Inf` for `Float64`. +The object status stores the latest value: -!!! tip - Prefer using [`to_initialize`](@ref) rather than [`inputs`](@ref) to check which variables should be initialized. [`inputs`](@ref) returns every variable that is needed by the model to run, but in multi-model simulations, some of them may already be computed by other models and not require initialization. [`to_initialize`](@ref) returns **only** the variables that are needed by the model to run and that are not initialized in the [`ModelMapping`](@ref). - -We can initialize the required variables by providing their starting values to the status when declaring the `ModelMapping`: - -```@example usepkg -m = ModelMapping(Beer(0.5), status = (LAI = 2.0,)) +```@example detailed_scene +scene_status = only(scene_objects(scene; scale=:Scene)).status +(LAI=scene_status.LAI, aPPFD=scene_status.aPPFD) ``` -Or after instantiation using [`init_status!`](@ref): +The returned `SceneSimulation` stores retained output streams: -```@example usepkg -m = ModelMapping(Beer(0.5)) - -init_status!(m, LAI = 2.0) +```@example detailed_scene +first(collect_outputs(sim; sink=nothing), 3) ``` -We can check if a component is correctly initialized using [`is_initialized`](@ref): +For a table, use the default `DataFrame` sink: -```@example usepkg -is_initialized(m) +```@example detailed_scene +first(collect_outputs(sim), 3) ``` -Some variables are inputs of models, but outputs of other models. When we couple models, [`to_initialize`](@ref) only requests the variables that are not computed by other models. - -## Climate forcing - -To make a simulation, we usually need the climatic/meteorological conditions measured close to the object or component. - -Users are strongly encouraged to use [`PlantMeteo.jl`](https://github.com/PalmStudio/PlantMeteo.jl), the companion package that helps manage such data, with default pre-computations and structures for efficient computations. The most basic data structure from this package is a type called [`Atmosphere`](https://palmstudio.github.io/PlantMeteo.jl/stable/#PlantMeteo.Atmosphere), which defines steady-state atmospheric conditions, *i.e.* the conditions are considered at equilibrium. Another structure is available to define different consecutive time-steps: [`TimeStepTable`](https://palmstudio.github.io/PlantMeteo.jl/stable/#PlantMeteo.TimeStepTable). - -The mandatory variables to provide for an [`Atmosphere`](https://palmstudio.github.io/PlantMeteo.jl/stable/#PlantMeteo.Atmosphere) are: `T` (air temperature in °C), `Rh` (relative humidity, 0-1) and `Wind` (the wind speed, m s⁻¹). In our example, we also need the incoming photosynthetically active radiation flux (`Ri_PAR_f`, W m⁻²). We can declare such conditions like so: - -```@example usepkg -using PlantMeteo -meteo = Atmosphere(T = 20.0, Wind = 1.0, Rh = 0.65, Ri_PAR_f = 500.0) +## Adding A Model Coupling + +Now let a daily LAI model compute `LAI` before the light-interception model +runs. `ToyLAIModel` reads cumulative thermal time `TT_cu` and writes `LAI`. +Because `Beer` reads `LAI`, the compiler can infer the same-object binding. + +```@example detailed_scene +coupled_scene = Scene( + Object( + :scene; + scale=:Scene, + kind=:scene, + status=Status(TT_cu=0.0), + ); + applications=( + ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(ToyLAIModel(); name=:lai) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(Beer(0.5); name=:light_interception) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ), + environment=meteo_day, +) + +select( + DataFrame(explain_bindings(refresh_bindings!(coupled_scene))), + :application_id, + :input, + :source_application_ids, + :carrier_kind, + :copy_semantics, +) ``` -This `meteo` variable will therefore provide a single weather timeframe that can be used in a simulation. - -More details are available from the [package documentation](https://vezy.github.io/PlantMeteo.jl/stable). - -## Simulation - -### Simulation of processes +The `LAI` binding uses a live reference carrier, so the light-interception +model sees the value written by the LAI model without copying it. -To run a simulation, you can call the [`run!`](@ref) method on the [`ModelMapping`](@ref). If some meteorological data is required for models to be simulated over several timesteps, that can be passed in as an optional argument as well. +Run the coupled scene: -Your call to the function would then look like this: - -```julia -run!(model_list, meteo) +```@example detailed_scene +coupled_sim = run!(coupled_scene; steps=5, constants=Constants()) +first(collect_outputs(coupled_sim), 8) ``` -The first argument is the model mapping (see [`ModelMapping`](@ref)), and the second defines the micro-climatic conditions. - -The [`ModelMapping`](@ref) should already be initialized for the given process before calling the function. Refer to the earlier subsection [Variables (inputs, outputs)](@ref) for more details. - -### Example simulation +The final object status contains the latest values from the coupled models: -For example we can simulate the `light_interception` of a leaf like so: - -```@example usepkg -using PlantSimEngine, PlantMeteo, Dates - -# Import the examples defined in the `Examples` sub-module -using PlantSimEngine.Examples - -meteo = Atmosphere(T = 20.0, Wind = 1.0, Rh = 0.65, Ri_PAR_f = 500.0) - -leaf = ModelMapping(Beer(0.5), status = (LAI = 2.0,)) - -outputs_example = run!(leaf, meteo) - -outputs_example[:aPPFD] +```@example detailed_scene +coupled_status = only(scene_objects(coupled_scene; scale=:Scene)).status +(TT_cu=coupled_status.TT_cu, LAI=coupled_status.LAI, aPPFD=coupled_status.aPPFD) ``` -### Outputs +## What Needs Initialization? -The [`status`](@ref) field of a [`ModelMapping`](@ref) is used to initialize the variables before simulation and then to keep track of their values during and after the simulation. We can extract outputs of the very last timestep of a simulation using the [`status`](@ref) function. +Model `inputs_(...)` lists every variable a model may need, but not all of +those variables need user initialization. In a coupled scene, some inputs are +computed by upstream models. -The actual full output data is returned by the [`run!`](@ref) function. Data is usually stored in a [`TimeStepTable`](@ref) structure from `PlantMeteo.jl`, which is a fast DataFrame-like structure with each time step being a [`Status`](@ref). It can be also be any `Tables.jl` structure, such as a regular `DataFrame`. The weather is also usually stored in a [`TimeStepTable`](@ref) but with each time step being an `Atmosphere`. +Use the compiler explanations to distinguish the two cases: -In our example, the simulation was only provided one weather timestep, so the outputs returned by [`run!`](@ref) and the ModelMapping's [`status`](@ref) field are identical. -Let's look at the outputs structure of our previous simulated leaf: +- a variable already present on object `Status` is user-provided state; +- a row in `explain_bindings(...)` is compiler-owned coupling; +- a compile error means a required input is neither initialized nor bound. -```@setup usepkg -outputs_example -``` - -We can extract the value of one variable by indexing into it, *e.g.* for the intercepted light: - -```@example usepkg -outputs_example[:aPPFD] -``` +For example, if we remove `TT_cu` from the scene status, compilation fails +because no model in this scene computes it before `ToyLAIModel` reads it: -Or similarly using the dot syntax: +```@example detailed_scene +bad_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene); + applications=( + ModelSpec(ToyLAIModel(); name=:lai) |> + AppliesTo(One(scale=:Scene)), + ), + environment=meteo_day, +) -```@example usepkg -outputs_example.aPPFD +try + refresh_bindings!(bad_scene) +catch err + first(sprint(showerror, err), 300) +end ``` -You can then print the outputs, convert them to another format, or visualize them, using other Julia packages. You can read more on how to do that in the [Visualizing outputs and data](@ref) page. - -Another convenient way to get the results is to transform the outputs into a `DataFrame`. Which is very easy because the [`TimeStepTable`](@ref) implements the Tables.jl interface: +## Compatibility Note -```@example usepkg -using DataFrames -convert_outputs(outputs_example, DataFrame) -``` +Older tutorials used `PlantSimEngine.ModelMapping(...)` for single-scale +simulations. That compatibility API remains available for historical examples +and regression tests, but new simulations should use `Scene`, `Object`, +`ModelSpec`, `AppliesTo`, `Inputs`, `Calls`, `Updates`, `TimeStep`, and +`Environment`. -## Model coupling +See [Migrating To The Scene/Object API](../migration_scene_object.md) for the +translation from old mapping and domain constructs. -A model can work either independently or in conjunction with other models. For example a stomatal conductance model is often associated with a photosynthesis model, *i.e.* it is called from the photosynthesis model. +## Next Steps -`PlantSimEngine.jl` is designed to make model coupling painless for modelers and users. Please see [Standard model coupling](@ref) and [Coupling more complex models](@ref) for more details, or [Handling dependencies in a multiscale context](@ref) for multi-scale specific coupling considerations. +- [Standard model coupling](@ref) shows more coupling patterns. +- [Scene/Object Quickstart](../scene_object/quickstart.md) is the shortest + copy-pasteable path for the new API. +- [Model execution](../model_execution.md) explains scheduling, temporal inputs, hard calls, + output retention, and lifecycle refreshes. diff --git a/docs/src/step_by_step/implement_a_model.md b/docs/src/step_by_step/implement_a_model.md index afaf888f3..2478f25b6 100644 --- a/docs/src/step_by_step/implement_a_model.md +++ b/docs/src/step_by_step/implement_a_model.md @@ -170,14 +170,20 @@ These functions are internal, and end with an "\_". Users instead use [`inputs`] ### The run! method -When running a simulation with [`run!`](@ref), each model is run in turn at every timestep, following whatever order was deduced from the `ModelMapping` definition and Status. Each model also has its [`run!`](@ref) method for that purpose that update the simulation's current state, with a slightly different signature. The function takes six arguments: +When running a simulation with [`run!`](@ref), each model is run at its +scheduled timestep, following the dependency order compiled from model +applications, inputs, and manual calls. Each model has its own [`run!`](@ref) +method for updating the current state. The function takes six arguments: ```julia function run!(::Beer, models, status, meteo, constants, extras) ``` - the model's type -- models: a [`ModelMapping`](@ref) object, which contains all the models of the simulation +- models: the process-keyed model bundle passed by the runtime. In legacy + mapping simulations this comes from `PlantSimEngine.ModelMapping`; in + scene/object simulations it is compiled from the model application and its + `Calls(...)`. - status: a [`Status`](@ref) object, which contains the current values (*i.e.* state) of the variables for **one** time-step (e.g. the value of the plant LAI at time t) - meteo: (usually) an `Atmosphere` object, or a row of the meteorological data, which contains the current values of the meteorological variables for **one** time-step (*e.g.* the value of the PAR at time t) - constants: a `Constants` object, or a `NamedTuple`, which contains the values of the constants for the simulation (*e.g.* the value of the Stefan-Boltzmann constant, unit-conversion constants...) @@ -185,7 +191,9 @@ function run!(::Beer, models, status, meteo, constants, extras) A typical [`run!`](@ref) function can therefore make use of simulation constants, input/output variables accessible through the [`Status`](@ref object, or weather data. -Here is the [`run!`](@ref) implementation of the light interception for a [`ModelMapping`](@ref) component models. Note that the input and output variable are accessed through the [`status`](@ref) argument : +Here is the [`run!`](@ref) implementation of the light interception model. +Note that the input and output variables are accessed through the +[`status`](@ref) argument: ```@example usepkg function run!(::Beer, models, status, meteo, constants, extras) @@ -203,7 +211,9 @@ To use this model, users will have to make sure that the variables for that mode !!! Note [`Status`](@ref) objects contain the current state of the simulation. It is not, by default, possible to make use of earlier variable states, unless a custom model is written for that purpose. -Model parameters are available from the [`ModelMapping`](@ref) that is passed via the `models` argument. Index by the process name, then the parameter name. For example, the `k` parameter of the `Beer` model is found in `models.light_interception.k`. +Model parameters are available from the process-keyed `models` argument. Index +by the process name, then the parameter name. For example, the `k` parameter of +the `Beer` model is found in `models.light_interception.k`. !!! warning You need to import all the functions you want to extend, so Julia knows your intention of adding a method to the function from PlantSimEngine, and not defining your own function. To do so, you have to prefix the said functions by the package name, or import them before *e.g.*: `import PlantSimEngine: inputs_, outputs_`. The troubleshooting subsection [Implementing a model: forgetting to import or prefix functions](@ref) showcases output errors that can occur when you forget to prefix. diff --git a/docs/src/step_by_step/model_switching.md b/docs/src/step_by_step/model_switching.md index c63a31c8f..bafe3fc30 100644 --- a/docs/src/step_by_step/model_switching.md +++ b/docs/src/step_by_step/model_switching.md @@ -1,102 +1,110 @@ # Model switching -```@setup usepkg -using PlantSimEngine, PlantMeteo, Dates -# Import the examples defined in the `Examples` sub-module +```@setup scene_model_switching +using PlantSimEngine, PlantMeteo, Dates, DataFrames using PlantSimEngine.Examples -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) - -models = ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(0.2), - status=(TT_cu=cumsum(meteo_day.TT),), +meteo_day = read_weather( + joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"); + duration=Dates.Day, ) -run!(models, meteo_day) -models2 = ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyAssimGrowthModel(), - status=(TT_cu=cumsum(meteo_day.TT),), -) -run!(models2, meteo_day) ``` -One of the main objective of PlantSimEngine is allowing users to switch between model implementations for a given process **without making any change to the PlantSimEngine codebase**. - -The package was designed around this idea to make easy changes easy and efficient. Switch models in the [`ModelMapping`](@ref), and call the [`run!`](@ref) function again. No other changes are required if no new variables are introduced. - -## A first simulation as a starting point - -With a working environment, let's create a [`ModelMapping`](@ref) with several models from the example scripts in the [`examples`](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/master/examples/) folder: - -Importing the models from the scripts: - -```julia -using PlantSimEngine -# Import the examples defined in the `Examples` sub-module: -using PlantSimEngine.Examples +One main objective of PlantSimEngine is to let users switch between model +implementations for a process without changing the engine or the other model +kernels. + +In the scene/object API, the switch happens at the model-application layer: +replace the model inside a `ModelSpec`, keep the same `AppliesTo(...)` +selector, and keep the same input contract when the replacement model needs the +same variables. + +## A first simulation + +This scene computes degree-days, LAI, absorbed PAR, and growth on one scene +object: + +```@example scene_model_switching +function plant_scene_with_growth(growth_model; growth_name=:growth) + Scene( + Object(:scene; scale=:Scene, kind=:scene); + applications=( + ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(ToyLAIModel(); name=:lai) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(Beer(0.5); name=:light_interception) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(growth_model; name=growth_name) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ), + environment=meteo_day, + ) +end + +rue_scene = plant_scene_with_growth(ToyRUEGrowthModel(0.2)) +rue_sim = run!(rue_scene; steps=10, constants=Constants()) +rue_status = only(scene_objects(rue_scene; scale=:Scene)).status +(growth_model=:ToyRUEGrowthModel, biomass=rue_status.biomass) ``` -Coupling the models in a [`ModelMapping`](@ref): - -```@example usepkg -models = ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(0.2), - status=(TT_cu=cumsum(meteo_day.TT),), +The compiler infers the same-object bindings from the model declarations. The +growth model reads `aPPFD`, which is produced by the light interception model: + +```@example scene_model_switching +select( + DataFrame(explain_bindings(refresh_bindings!(rue_scene))), + :application_id, + :input, + :source_application_ids, + :origin, + :carrier_kind, ) - -nothing # hide -``` - -We can the simulation by calling the [`run!`](@ref) function with meteorology data. Here we use an example data set: - -```@example usepkg -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) -nothing # hide ``` -We can now run the simulation: - -```@example usepkg -output_initial = run!(models, meteo_day) -output_initial[1:3,:] # show the first 3 rows of the output -``` - -## Switching one model in the simulation - -Now what if we want to switch the model that computes growth ? We can do this by simply replacing the model in the [`ModelMapping`](@ref), and PlantSimEngine will automatically update the dependency graph, and adapt the simulation to the new model. - -Let's switch ToyRUEGrowthModel with ToyAssimGrowthModel: - -```@example usepkg -models2 = ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyAssimGrowthModel(), # This was `ToyRUEGrowthModel(0.2)` before - status=(TT_cu=cumsum(meteo_day.TT),), +## Switching the growth model + +`ToyAssimGrowthModel` implements the same `:growth` process, reads the same +`aPPFD` input, and computes additional outputs such as carbon assimilation and +respiration. The rest of the scene does not need to change: + +```@example scene_model_switching +assim_scene = plant_scene_with_growth(ToyAssimGrowthModel()) +assim_sim = run!(assim_scene; steps=10, constants=Constants()) +assim_status = only(scene_objects(assim_scene; scale=:Scene)).status +( + growth_model=:ToyAssimGrowthModel, + carbon_assimilation=assim_status.carbon_assimilation, + Rm=assim_status.Rm, + biomass=assim_status.biomass, ) - -nothing # hide ``` -ToyAssimGrowthModel is a little bit more complex than `ToyRUEGrowthModel`](@ref), as it also computes the maintenance and growth respiration of the plant, so it has more parameters (we use the default values here). - -We can run a new simulation and see that the simulation's results are different from the previous simulation: +The dependency graph and execution plan are rebuilt from the new application +set: -```@example usepkg -output_updated = run!(models2, meteo_day) -output_updated[1:3,:] # show the first 3 rows of the output +```@example scene_model_switching +select( + DataFrame(explain_execution_plan(assim_sim)), + :application_id, + :object_ids, + :batch_size, + :inner_loop_dispatch, +) ``` -And that's it! We can switch between models without changing the code, and without having to recompute the dependency graph manually. This is a very powerful feature of PlantSimEngine!💪 - -!!! note - This was a very standard but straightforward example. Sometimes other models will require to add other models to the [`ModelMapping`](@ref). For example ToyAssimGrowthModel could have required a maintenance respiration model. In this case `PlantSimEngine` will indicate what kind of model is required for the simulation. - -!!! note - In our example we replaced what we call a [soft-dependency coupling](@ref hard_dependency_def), but the same principle applies to [hard-dependencies](@ref hard_dependency_def). Hard and Soft dependencies are concepts related to model coupling, and are discussed in more detail in [Standard model coupling](@ref) and [Coupling more complex models](@ref). +This is the same principle used in larger scenes: switch one process +implementation by replacing one `ModelSpec` or by using an +`ObjectInstance(...; overrides=...)` when the change applies to one plant +instance or one organ. +Older tutorials showed the same idea with `PlantSimEngine.ModelMapping(...)`. +That spelling remains available for compatibility code, but new scenario code +should express model switching through scene model applications. diff --git a/docs/src/step_by_step/quick_and_dirty_examples.md b/docs/src/step_by_step/quick_and_dirty_examples.md index e871e8518..6f627e17a 100644 --- a/docs/src/step_by_step/quick_and_dirty_examples.md +++ b/docs/src/step_by_step/quick_and_dirty_examples.md @@ -1,95 +1,158 @@ -# Quick examples +# Quick Examples -This page is meant for people who have set up their environment and just want to copy-paste an example or two, see what the REPL returns and start tinkering. +This page is for copy-paste experimentation with the native scene/object API. +If you want a slower explanation of the same ideas, see +[Detailed Walkthrough Of A Simple Simulation](@ref detailed-walkthrough-of-a-simple-simulation). -If you are less comfortable with Julia, or need to set up an environment first, see this page : [Getting started with Julia](@ref). -If you wish for a more detailed rundown of the examples, you can instead have a look at the [step by step](#step_by_step) section, which will go into more detail. +The examples use one scene object, but the same pattern scales to plants, +organs, soil objects, and microclimate grids by adding more `Object`s and +selecting them with `AppliesTo(...)` and `Inputs(...)`. -These examples are all for single-scale simulations. For multi-scale modelling tutorials and examples, refer to [this section][#multiscale] +```@setup quick_scene_examples +using PlantSimEngine, PlantMeteo, Dates, DataFrames +using PlantSimEngine.Examples -You can find the implementation for all the example models, as well as other toy models [in the examples folder](https://github.com/VirtualPlantLab/PlantSimEngine.jl/tree/main/examples). +meteo_day = read_weather( + joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"); + duration=Dates.Day, +) +``` ```@contents Pages = ["quick_and_dirty_examples.md"] Depth = 2 ``` -## Environment - -These examples assume you have a working Julia environment with PlantSimengine added to it, as well as the other packages used in these examples. Details for getting to that point are provided on the [Installing and running PlantSimEngine](@ref) page. +## One Light Interception Model + +```@example quick_scene_examples +scene = Scene( + Object( + :scene; + scale=:Scene, + kind=:scene, + status=Status(LAI=2.0), + ); + applications=( + ModelSpec(Beer(0.5); name=:light_interception) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ), + environment=meteo_day, +) +sim = run!(scene; steps=3, constants=Constants()) +first(collect_outputs(sim), 3) +``` -## Example with a single light interception model and a single weather timestep +## LAI And Light Interception + +Here, `ToyDegreeDaysCumulModel` computes cumulative thermal time, `ToyLAIModel` +computes `LAI`, and `Beer` consumes `LAI`. The compiler infers the same-object +value bindings from model inputs and outputs. + +```@example quick_scene_examples +lai_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene, status=Status(TT_cu=0.0)); + applications=( + ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ModelSpec(ToyLAIModel(); name=:lai) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ModelSpec(Beer(0.5); name=:light_interception) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ), + environment=meteo_day, +) -```@example usepkg -using PlantSimEngine, PlantMeteo, Dates -using PlantSimEngine.Examples -meteo = Atmosphere(T = 20.0, Wind = 1.0, Rh = 0.65, Ri_PAR_f = 500.0) -leaf = ModelMapping(Beer(0.5), status = (LAI = 2.0,)) -out = run!(leaf, meteo) +lai_sim = run!(lai_scene; steps=5, constants=Constants()) +first(collect_outputs(lai_sim), 8) ``` -## Coupling the light interception model with a Leaf Area Index model +Inspect the inferred coupling: -The weather data in this example contains data over 365 days, meaning the simulation will have as many timesteps. - -```@example usepkg -using PlantSimEngine -using PlantMeteo, Dates -using PlantSimEngine.Examples - -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) +```@example quick_scene_examples +select( + DataFrame(explain_bindings(refresh_bindings!(lai_scene))), + :application_id, + :input, + :source_application_ids, + :carrier_kind, +) +``` -models = ModelMapping( - ToyLAIModel(), - Beer(0.5), - status=(TT_cu=cumsum(meteo_day.TT),), +## Add Biomass Growth + +`ToyRUEGrowthModel` consumes absorbed light and accumulates biomass. No extra +input binding is needed because `Beer` is the unique producer of `aPPFD` on the +same object. + +```@example quick_scene_examples +growth_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene, status=Status(TT_cu=0.0)); + applications=( + ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ModelSpec(ToyLAIModel(); name=:lai) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ModelSpec(Beer(0.5); name=:light_interception) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ModelSpec(ToyRUEGrowthModel(0.2); name=:growth) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ), + environment=meteo_day, ) -outputs_coupled = run!(models, meteo_day) -outputs_coupled[1:3,:] # show the first 3 rows of the output +growth_sim = run!(growth_scene; steps=5, constants=Constants()) +growth_status = only(scene_objects(growth_scene; scale=:Scene)).status +(LAI=growth_status.LAI, aPPFD=growth_status.aPPFD, biomass=growth_status.biomass) ``` -## Coupling the light interception and Leaf Area Index models with a biomass increment model +## Keep Only One Requested Output +For larger simulations, request only the streams you want to keep: -```@example usepkg -using PlantSimEngine -using PlantMeteo, Dates -using PlantSimEngine.Examples - -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) +```@example quick_scene_examples +request = OutputRequest( + :Scene, + :biomass; + name=:biomass_daily, + process=:growth, + policy=HoldLast(), + clock=Day(1), +) -models = ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(0.2), - status=(TT_cu=cumsum(meteo_day.TT),), +requested_sim = run!( + growth_scene; + steps=5, + constants=Constants(), + tracked_outputs=request, ) -outputs_coupled = run!(models, meteo_day) -outputs_coupled[1:3,:] # show the first 3 rows of the output +first(collect_outputs(requested_sim, :biomass_daily), 5) ``` -## Example using PlantBioPhysics - -A companion package, PlantBioPhysics, uses PlantSimEngine, and contains other models used in ecophysiological simulations. - -You can have a look at its documentation [here](https://vezy.github.io/PlantBiophysics.jl/stable/) - -Several example simulations are provided there. Here's one taken from [this page](https://vezy.github.io/PlantBiophysics.jl/stable/simulation/first_simulation/) : +## PlantBiophysics -```julia -using PlantBiophysics, PlantSimEngine +The same scene/object API can host models from companion packages such as +PlantBiophysics. A typical PlantBiophysics energy-balance setup uses +`Calls(...)` so an iterative parent model can manually run photosynthesis and +stomatal-conductance models, then call `run_call!(target; publish=true)` once +for the accepted solution. -meteo = Atmosphere(T = 22.0, Wind = 0.8333, P = 101.325, Rh = 0.4490995) +See [MAESPA-style domain example handoff](../dev/maespa_domain_handoff.md) for +the current multi-plant energy-balance acceptance example. -leaf = ModelMapping( - Monteith(), - Fvcb(), - Medlyn(0.03, 12.0), - status = (Ra_SW_f = 13.747, sky_fraction = 1.0, aPPFD = 1500.0, d = 0.03) - ) +## Compatibility Note -out = run!(leaf,meteo) -``` \ No newline at end of file +Older examples used `PlantSimEngine.ModelMapping(...)`. That compatibility +API remains available for historical material and regression tests, but new +simulations should start from `Scene`, `Object`, `ModelSpec`, `AppliesTo`, +`Inputs`, `Calls`, `Updates`, `TimeStep`, and `Environment`. diff --git a/docs/src/step_by_step/simple_model_coupling.md b/docs/src/step_by_step/simple_model_coupling.md index 179b36d6f..0a11bd6a3 100644 --- a/docs/src/step_by_step/simple_model_coupling.md +++ b/docs/src/step_by_step/simple_model_coupling.md @@ -1,127 +1,131 @@ # Standard model coupling -```@setup usepkg +```@setup scene_coupling using PlantSimEngine using PlantSimEngine.Examples -using PlantMeteo, Dates - -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) -models = ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(0.2), - status=(TT_cu=cumsum(meteo_day.TT),), +using PlantMeteo, Dates, DataFrames + +meteo_day = read_weather( + joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"); + duration=Dates.Day, ) -nothing ``` -## Setting up your environment - -Again, make sure you have a working Julia environment with PlantSimengine added to it, and the other recommended companion packages. Details for getting to that point are provided on the [Installing and running PlantSimEngine](@ref) page. - -## ModelMapping +This page shows the standard coupling case: one model computes a variable that +another model reads. In the scene/object API, the user describes model +applications on objects, and the compiler wires the value dependencies. -The [`ModelMapping`](@ref) is a container that holds a list of models, their parameter values, and the status of the variables associated to them. - -If one looks at prior examples, the ModelMappings so far have only contained a single model, whose input variables are initialised in the ModelMapping [`status`](@ref) keyword argument. - -Example models are all taken from the example scripts in the [`examples`](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/master/examples/) folder. +## Setting up your environment -Here's a first [`ModelMapping`](@ref) declaration with a light interception model, requiring input Leaf Area Index (LAI): +Make sure you have a working Julia environment with PlantSimEngine and the +recommended companion packages. Details are provided on the +[Installing and running PlantSimEngine](@ref) page. + +## One object and one model + +A scene contains objects. A model application says where a model runs and at +which timestep. Here a light interception model runs on the scene object and +reads `LAI` from that object's status: + +```@example scene_coupling +light_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene, status=Status(LAI=2.0)); + applications=( + ModelSpec(Beer(0.5); name=:light_interception) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ), + environment=meteo_day, +) -```julia -modellist_coupling_part_1 = ModelMapping(Beer(0.5), status = (LAI = 2.0,)) +light_sim = run!(light_scene; steps=3, constants=Constants()) +first(DataFrame(collect_outputs(light_sim; sink=nothing)), 3) ``` -Here's a second one with a Leaf Area Index model, with some example Cumulated Thermal Time as input. (This TT_cu is usually computed from weather data): - -```julia -modellist_coupling_part_2 = ModelMapping( - ToyLAIModel(), - status=(TT_cu=1.0:2000.0,), # Pass the cumulated degree-days as input to the model +## Coupling two models + +Suppose we want `ToyLAIModel` to compute `LAI` for `Beer`. Both models can run +on the same object. `ToyLAIModel` produces `LAI`, and `Beer` declares `LAI` as +an input, so the scene compiler infers the binding: + +```@example scene_coupling +coupled_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene); + applications=( + ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(ToyLAIModel(); name=:lai) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(Beer(0.5); name=:light_interception) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ), + environment=meteo_day, ) -``` - -## Combining models - -Suppose we want our ToyLAIModel to compute the `LAI` for the light interception model. -We can couple the two models by having them be part of a single [`ModelMapping`](@ref). The `LAI` variable will then be a coupled output computed by the ToyLAIModel, then used as input by `Beer`. It will no longer need to be declared as part of the [`status` . - -This is an instance of what we call a ["soft dependency" coupling](@ref hard_dependency_def): a model depends on another model's outputs for its inputs. - -Here's a first attempt : - -```@example usepkg -using PlantSimEngine -# Import the examples defined in the `Examples` sub-module: -using PlantSimEngine.Examples - -# A ModelMapping with two coupled models -models = ModelMapping( - ToyLAIModel(), - Beer(0.5), - status=(TT_cu=1.0:2000.0,), +compiled = refresh_bindings!(coupled_scene) +select( + DataFrame(explain_bindings(compiled)), + :application_id, + :input, + :source_application_ids, + :origin, + :carrier_kind, + :copy_semantics, ) -struct UnexpectedSuccess <: Exception end #hack to enable checking an error without failing docbuild #hide -# see https://github.com/JuliaDocs/Documenter.jl/issues/1420 #hide -try #hide -run!(models) -throw(UnexpectedSuccess()) #hide -catch err; err isa UnexpectedSuccess ? rethrow(err) : showerror(stderr, err); end #hide -``` - -Oops, we get an error related to the weather data, with the detailed output being: - -```julia -ERROR: type NamedTuple has no field Ri_PAR_f -Stacktrace: - [1] getindex(mnt::Atmosphere{(), Tuple{}}, i::Symbol) - @ PlantMeteo ~/Path/to/PlantMeteo/src/structs/atmosphere.jl:147 - [2] getcolumn(row::PlantMeteo.TimeStepRow{Atmosphere{(), Tuple{}}}, nm::Symbol) - @ PlantMeteo ~/Path/to/PlantMeteo/src/structs/TimeStepTable.jl:205 - ... ``` -The `Beer` model requires a specific meteorological parameter. Let's fix that by importing the example weather data : - -```@example usepkg -using PlantSimEngine +The `:inferred_same_object` rows are soft dependencies: the consumer input is +provided by another model output. Same-rate local links use live references, so +the timestep loop does not copy values between models. -# PlantMeteo and CSV packages are now used -using PlantMeteo, Dates +Run the coupled scene: -# Import the examples defined in the `Examples` sub-module: -using PlantSimEngine.Examples - -# Import example weather data -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) +```@example scene_coupling +coupled_sim = run!(coupled_scene; steps=5, constants=Constants()) +coupled_status = only(scene_objects(coupled_scene; scale=:Scene)).status +(TT_cu=coupled_status.TT_cu, LAI=coupled_status.LAI, aPPFD=coupled_status.aPPFD) +``` -# A ModelMapping with two coupled models -models = ModelMapping( - ToyLAIModel(), - Beer(0.5), - status=(TT_cu=cumsum(meteo_day.TT),), # We can now compute a genuine cumulative thermal time from the weather data +## Adding another model + +Additional models are just additional applications. `ToyRUEGrowthModel` +consumes `aPPFD`, which is produced by `Beer`, so the compiler infers another +same-object binding: + +```@example scene_coupling +growth_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene); + applications=( + ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(ToyLAIModel(); name=:lai) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(Beer(0.5); name=:light_interception) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + + ModelSpec(ToyRUEGrowthModel(0.2); name=:growth) |> + AppliesTo(One(scale=:Scene)) |> + TimeStep(Day(1)), + ), + environment=meteo_day, ) -# Add the weather data to the run! call -outputs_coupled = run!(models, meteo_day) -outputs_coupled[1:3,:] +growth_sim = run!(growth_scene; steps=5, constants=Constants()) +growth_status = only(scene_objects(growth_scene; scale=:Scene)).status +(LAI=growth_status.LAI, aPPFD=growth_status.aPPFD, biomass=growth_status.biomass) ``` -And there you have it. The light interception model made its computations using the Leaf Area Index computed by ToyLAIModel. - -## Further coupling - -Of course, one can keep adding models. Here's an example `ModelMapping` with another model, `ToyRUEGrowthModel`, which computes the carbon biomass increment caused by photosynthesis. - -```julia -models = ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(0.2), - status=(TT_cu=cumsum(meteo_day.TT),), -) - -nothing # hide -``` \ No newline at end of file +Older examples used `PlantSimEngine.ModelMapping(...)` for this workflow. That +constructor is retained as a qualified compatibility API, but new scenarios +should start from `Scene`, `Object`, `ModelSpec`, `AppliesTo`, `Inputs`, +`Calls`, and `TimeStep`. diff --git a/docs/src/troubleshooting_and_testing/implicit_contracts.md b/docs/src/troubleshooting_and_testing/implicit_contracts.md index b65b4a9da..1e1067f4c 100644 --- a/docs/src/troubleshooting_and_testing/implicit_contracts.md +++ b/docs/src/troubleshooting_and_testing/implicit_contracts.md @@ -22,7 +22,7 @@ Many models are considered to be steady-state over that timeframe, but not all : If your meteo has 30-minute rows but a model appears to run hourly, check timestep resolution order: -1. If model has explicit `TimeStepModel(...)`, it is used. +1. If model has explicit `PlantSimEngine.TimeStepModel(...)`, it is used. 2. Else if model `timespec(model)` is non-default, it is used. 3. Else model uses meteo `duration`. @@ -33,7 +33,7 @@ Then compatibility rules apply: 3. Meteo aggregation/integration happens only for models with coarser effective clocks. Common cause: -- model has explicit hourly `TimeStepModel(...)`, so 30-minute rows are intentionally aggregated to hourly runs. +- model has explicit hourly `PlantSimEngine.TimeStepModel(...)`, so 30-minute rows are intentionally aggregated to hourly runs. Quick diagnostics: - Run `explain_model_specs(mapping_or_sim)` to see, per process, whether runtime clock comes from explicit `ModelSpec`, model `timespec`, or meteo base step. diff --git a/docs/src/troubleshooting_and_testing/plantsimengine_and_julia_troubleshooting.md b/docs/src/troubleshooting_and_testing/plantsimengine_and_julia_troubleshooting.md index 602045e04..406daa0dd 100644 --- a/docs/src/troubleshooting_and_testing/plantsimengine_and_julia_troubleshooting.md +++ b/docs/src/troubleshooting_and_testing/plantsimengine_and_julia_troubleshooting.md @@ -102,7 +102,7 @@ Most of the following errors occur exclusively in multi-scale simulations, which ### ModelMapping: providing a type name instead of a constructed instance ```julia -m = ModelMapping(day=MyToyModel, week=MyToyModel2) +m = PlantSimEngine.ModelMapping(day=MyToyModel, week=MyToyModel2) ``` This line is incorrect and will return ```julia @@ -111,7 +111,7 @@ MethodError: no method matching inputs_(::Type{MyToyDayModel}) The correct syntax is (assuming the corresponding constructor exists) : ```julia -m = ModelMapping(day=MyToyModel(), week=MyToyModel2()) +m = PlantSimEngine.ModelMapping(day=MyToyModel(), week=MyToyModel2()) ``` ### Implementing a model: forgetting to import or prefix functions @@ -148,7 +148,7 @@ meteo = Weather([ Atmosphere(T=18.0, Wind=1.0, Rh=0.65, Ri_PAR_f=100.0), ]) -model = ModelMapping( +model = PlantSimEngine.ModelMapping( ToyToyModel(1), status = ( a = 1, b = 0, c = 0), ) @@ -188,7 +188,7 @@ Stacktrace: A MultiScaleModel requires two kwargs, model and mapped_variables : ```julia -models = MultiScaleModel( +models = PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[:TT_cu => :Scene,], ) @@ -197,34 +197,34 @@ models = MultiScaleModel( Forgetting 'model=' : ```julia -models = MultiScaleModel( +models = PlantSimEngine.MultiScaleModel( ToyLAIModel(), mapped_variables=[:TT_cu => :Scene,], ) -ERROR: MethodError: no method matching MultiScaleModel(::ToyLAIModel; mapped_variables::Vector{Pair{Symbol, String}}) +ERROR: MethodError: no method matching PlantSimEngine.MultiScaleModel(::ToyLAIModel; mapped_variables::Vector{Pair{Symbol, String}}) The type `MultiScaleModel` exists, but no method is defined for this combination of argument types when trying to construct it. Closest candidates are: - MultiScaleModel(::T, ::Any) where T<:AbstractModel got unsupported keyword argument "mapped_variables" + PlantSimEngine.MultiScaleModel(::T, ::Any) where T<:AbstractModel got unsupported keyword argument "mapped_variables" @ PlantSimEngine PlantSimEngine/src/mtg/MultiScaleModel.jl:188 - MultiScaleModel(; model, mapped_variables) + PlantSimEngine.MultiScaleModel(; model, mapped_variables) @ PlantSimEngine PlantSimEngine/src/mtg/MultiScaleModel.jl:191 ``` Forgetting 'mapped_variables=' : ```julia -models = MultiScaleModel( +models = PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), [:TT_cu => :Scene,], ) -ERROR: MethodError: no method matching MultiScaleModel(::Vector{Pair{Symbol, String}}; model::ToyLAIModel) +ERROR: MethodError: no method matching PlantSimEngine.MultiScaleModel(::Vector{Pair{Symbol, String}}; model::ToyLAIModel) The type `MultiScaleModel` exists, but no method is defined for this combination of argument types when trying to construct it. Closest candidates are: - MultiScaleModel(; model, mapping) + PlantSimEngine.MultiScaleModel(; model, mapping) @ PlantSimEngine PlantSimEngine/src/mtg/MultiScaleModel.jl:191 - MultiScaleModel(::T, ::Any) where T<:AbstractModel got unsupported keyword argument "model" + PlantSimEngine.MultiScaleModel(::T, ::Any) where T<:AbstractModel got unsupported keyword argument "model" ``` The message 'got unsupported keyword argument "model"' can be misleading, as in the error in this case is not that a kwarg is *unsupported*, but rather that a keyword argument is *missing*. @@ -234,8 +234,8 @@ The message 'got unsupported keyword argument "model"' can be misleading, as in A possible cause for this error is that a variable was declared instead of a symbol in a mapping for a multiscale model : ```julia -mapping = ModelMapping(:Scale => -MultiScaleModel( +mapping = PlantSimEngine.ModelMapping(:Scale => +PlantSimEngine.MultiScaleModel( model = ToyModel(), mapped_variables = [should_be_symbol => :Other_Scale] # should_be_symbol is a variable, likely not found in the current module ), @@ -245,8 +245,8 @@ MultiScaleModel( Here's the correct version : ```julia -mapping = ModelMapping(:Scale => -MultiScaleModel( +mapping = PlantSimEngine.ModelMapping(:Scale => +PlantSimEngine.MultiScaleModel( model = ToyModel(), mapped_variables=[:should_be_symbol => :Other_Scale] # should_be_symbol is now a symbol ), @@ -265,7 +265,7 @@ meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.cs mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Plant, 1, 1)) var1 = 15.0 -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Leaf => ( Process1Model(1.0), Process2Model(), @@ -350,7 +350,7 @@ However, the model provided in the examples, Process2Model is absent from the ma ```julia simple_mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Plant, 1, 1)) -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Leaf => ( Process3Model(), Status(var5=15.0,) @@ -378,7 +378,7 @@ One current problem with PlantSimEngine's API is that declaring a simulation's S Returning to the example in [Implementing a model: forgetting to import or prefix functions](@ref), the single-scale mapping status was declared like this: ```julia -model = ModelMapping( +model = PlantSimEngine.ModelMapping( ToyToyModel(1), status = ( a = 1, b = 0, c = 0), ) @@ -404,7 +404,7 @@ Stacktrace: If you do the opposite in a multi-scale simulation by replacing the necessary `Status(...)` with `status = ...`, you may get an `ERROR: syntax: invalid named tuple element` error. Here's some output when tinkering with the Toy Plant tutorial's mapping: ```julia -ERROR: syntax: invalid named tuple element "MultiScaleModel(...)" around /path/to/Pkg/PlantSimEngine/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl:196 +ERROR: syntax: invalid named tuple element "PlantSimEngine.MultiScaleModel(...)" around /path/to/Pkg/PlantSimEngine/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl:196 Stacktrace: [1] top-level scope @ ~/path/to/pkg/PlantSimEngine/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl:196 @@ -425,7 +425,7 @@ If there is a need to collect variables at two different scales, and one scale i # No models at the E3 scale in the mapping ! :E2 => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model = HardDepSameScaleEchelle2Model(), mapped_variables=[:c => :E1 => :c, :e3 => :E3 => :e3, :f3 => :E3 => :f3,], ), @@ -451,7 +451,7 @@ ERROR: ArgumentError: AbstractDict(kv): kv needs to be an iterator of 2-tuples o may occur when forgetting the parenthesis after '=>' in a mapping declaration, and combining it with another parenthesis error. ```julia -mapping = ModelMapping( "Scale" => (ToyAssimGrowthModel(0.0, 0.0, 0.0), ToyCAllocationModel(), Status( TT_cu=Vector(cumsum(meteo_day.TT))), ), ) +mapping = PlantSimEngine.ModelMapping( "Scale" => (ToyAssimGrowthModel(0.0, 0.0, 0.0), ToyCAllocationModel(), Status( TT_cu=Vector(cumsum(meteo_day.TT))), ), ) ``` Other errors such as: @@ -490,10 +490,10 @@ function PlantSimEngine.outputs_(::ToyTt_CuModel) (TT_cu=-Inf,) end -mapping_multiscale = ModelMapping( +mapping_multiscale = PlantSimEngine.ModelMapping( :Scene => ToyTt_CuModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[ :TT_cu => :Scene, diff --git a/docs/src/troubleshooting_and_testing/tips_and_workarounds.md b/docs/src/troubleshooting_and_testing/tips_and_workarounds.md index 27f19a067..7724393f5 100644 --- a/docs/src/troubleshooting_and_testing/tips_and_workarounds.md +++ b/docs/src/troubleshooting_and_testing/tips_and_workarounds.md @@ -50,7 +50,9 @@ This change in design avoids model order ambiguity and also improves readability !!! note This section is a little more advanced and not recommended for beginners -You may have noticed that sometimes a vector (1-dimensional array) variable is passed into the [`status`](@ref) component of a [`ModelMapping`](@ref) in documentation examples (An example here with cumulative thermal time : [Model switching](@ref)). +You may have noticed that some legacy documentation examples pass a vector +(1-dimensional array) variable into the [`status`](@ref) component of +`PlantSimEngine.ModelMapping(...)` compatibility simulations. This is practical for simple simulations, or when quickly prototyping, to avoid having to write a model specifically for it. Whatever models make use of that variable are provided with one element corresponding to the current timestep every iteration. @@ -84,7 +86,7 @@ using PlantMeteo, Dates meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) # Direct translation of the single-scale simulation -mapping_pseudo_multiscale = ModelMapping( +mapping_pseudo_multiscale = PlantSimEngine.ModelMapping( :Plant => ( ToyLAIModel(), Beer(0.5), diff --git a/docs/src/working_with_data/fitting.md b/docs/src/working_with_data/fitting.md index 0232acb21..e6e09dc3d 100644 --- a/docs/src/working_with_data/fitting.md +++ b/docs/src/working_with_data/fitting.md @@ -5,7 +5,7 @@ using PlantSimEngine, PlantMeteo, Dates, Statistics, DataFrames using PlantSimEngine.Examples meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_f=300.0) -m = ModelMapping(Beer(0.6), status=(LAI=2.0,)) +m = PlantSimEngine.ModelMapping(Beer(0.6), status=(LAI=2.0,)) run!(m, meteo) df = DataFrame(aPPFD=m[:aPPFD][1], LAI=m.status.LAI[1], Ri_PAR_f=meteo.Ri_PAR_f[1]) @@ -56,7 +56,7 @@ meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_f=300.0) Computing the `PPFD` values from the `Ri_PAR_f` values using the `Beer` model (with `k=0.6`): ```@example usepkg -m = ModelMapping(Beer(0.6), status=(LAI=2.0,)) +m = PlantSimEngine.ModelMapping(Beer(0.6), status=(LAI=2.0,)) run!(m, meteo) ``` diff --git a/docs/src/working_with_data/floating_point_accumulation_error.md b/docs/src/working_with_data/floating_point_accumulation_error.md index 6153fdc62..4d1073cd0 100644 --- a/docs/src/working_with_data/floating_point_accumulation_error.md +++ b/docs/src/working_with_data/floating_point_accumulation_error.md @@ -6,7 +6,7 @@ using PlantSimEngine.Examples using PlantMeteo, Dates, MultiScaleTreeGraph meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) -models = ModelMapping( +models = PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.5), ToyRUEGrowthModel(0.2); @@ -24,7 +24,7 @@ In the [Converting a single-scale simulation to multi-scale](@ref) page, a singl ```@example usepkg meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) -models_singlescale = ModelMapping( +models_singlescale = PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.5), ToyRUEGrowthModel(0.2); @@ -55,10 +55,10 @@ function PlantSimEngine.outputs_(::ToyTt_CuModel) (TT_cu=0.0,) end -mapping_multiscale = ModelMapping( +mapping_multiscale = PlantSimEngine.ModelMapping( :Scene => ToyTt_CuModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[ :TT_cu => :Scene, @@ -78,10 +78,10 @@ outputs_multiscale = run!(mtg_multiscale, mapping_multiscale, meteo_day) ### Output comparison ```@setup usepkg -mapping_multiscale = ModelMapping( +mapping_multiscale = PlantSimEngine.ModelMapping( :Scene => ToyTt_CuModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[ :TT_cu => :Scene, diff --git a/docs/src/working_with_data/inputs.md b/docs/src/working_with_data/inputs.md index d2540035b..addafdb16 100644 --- a/docs/src/working_with_data/inputs.md +++ b/docs/src/working_with_data/inputs.md @@ -1,6 +1,11 @@ # Input types -[`run!`](@ref) usually takes two inputs: a [`ModelMapping`](@ref) and data for the meteorology. The data for the meteorology is usually provided for one time step using an `Atmosphere`, or for several time-steps using a `TimeStepTable{Atmosphere}`. The [`ModelMapping`](@ref) can also be provided as a singleton, or as a vector or dictionary of. +In scene/object simulations, [`run!`](@ref) usually takes a `Scene` and the +meteorology is supplied through the scene `environment`. In legacy +compatibility simulations, [`run!`](@ref) takes a +`PlantSimEngine.ModelMapping(...)` and meteorological data. The meteorology is +usually provided for one timestep using an `Atmosphere`, or for several +timesteps using a `TimeStepTable{Atmosphere}`. [`run!`](@ref) knows how to handle these data formats via the [`PlantSimEngine.DataFormat`](@ref) trait (see [this blog post](https://www.juliabloggers.com/the-emergent-features-of-julialang-part-ii-traits/) to learn more about traits). For example, we tell PlantSimEngine that a `TimeStepTable` should be handled like a table by implementing the following trait: @@ -18,10 +23,12 @@ There are two other traits available: `SingletonAlike` for a data format represe ## Promoting status variable types -Use the `type_promotion` keyword on [`ModelMapping`](@ref) when the default input and output values declared by models should be converted to another type: +Use the `type_promotion` keyword on the qualified compatibility constructor +`PlantSimEngine.ModelMapping(...)` when the default input and output values +declared by models should be converted to another type: ```julia -models = ModelMapping( +models = PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.5), ToyRUEGrowthModel(0.2); @@ -32,13 +39,15 @@ models = ModelMapping( For single-scale mappings, `type_promotion` is applied while the backing status is constructed: model-provided default values are converted, while values explicitly passed in `status` keep the type chosen by the user. If those values should also be `Float32`, pass them as `Float32` values directly. -For multiscale mappings, the per-node statuses do not exist when [`ModelMapping`](@ref) is constructed. The promotion map is stored on the mapping and applied when the MTG simulation is initialized: +For legacy multiscale mappings, the per-node statuses do not exist when +`PlantSimEngine.ModelMapping(...)` is constructed. The promotion map is stored +on the mapping and applied when the MTG simulation is initialized: ```julia -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Scene => ToyTt_CuModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[ :TT_cu => :Scene, diff --git a/docs/src/working_with_data/reducing_dof.md b/docs/src/working_with_data/reducing_dof.md index 1a4b925de..380523adc 100644 --- a/docs/src/working_with_data/reducing_dof.md +++ b/docs/src/working_with_data/reducing_dof.md @@ -49,7 +49,7 @@ using PlantSimEngine, PlantMeteo, Dates using PlantSimEngine.Examples meteo = Atmosphere(T = 20.0, Wind = 1.0, P = 101.3, Rh = 0.65) -m = ModelMapping( +m = PlantSimEngine.ModelMapping( Process1Model(2.0), Process2Model(), Process3Model(), @@ -68,7 +68,7 @@ status(m) Let's say that `m` is our complete model, and that we want to reduce the degrees of freedom by constraining the value of `var9` to a measurement, which was previously computed by `Process7Model`, a soft-dependency model. It is very easy to do this in PlantSimEngine: just remove the model from the model list and give the value of the measurement in the status: ```@example usepkg -m2 = ModelMapping( +m2 = PlantSimEngine.ModelMapping( Process1Model(2.0), Process2Model(), Process3Model(), @@ -103,7 +103,7 @@ end Now we can create a new model list with the new model for `process7`: ```@example usepkg -m3 = ModelMapping( +m3 = PlantSimEngine.ModelMapping( ForceProcess1Model(), Process2Model(), Process3Model(), diff --git a/docs/src/working_with_data/visualising_outputs.md b/docs/src/working_with_data/visualising_outputs.md index e7d833e2a..94b69fb53 100644 --- a/docs/src/working_with_data/visualising_outputs.md +++ b/docs/src/working_with_data/visualising_outputs.md @@ -9,7 +9,7 @@ using PlantSimEngine.Examples meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) # Define the list of models for coupling: -model = ModelMapping( +model = PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.6), status=(TT_cu=cumsum(meteo_day[:, :TT]),), # Pass the cumulated degree-days as input to `ToyLAIModel`, this could also be done using another model @@ -39,7 +39,7 @@ using PlantSimEngine.Examples meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) # Define the list of models for coupling: -models = ModelMapping( +models = PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.6), status=(TT_cu=cumsum(meteo_day[:, :TT]),), # Pass the cumulated degree-days as input to `ToyLAIModel`, this could also be done using another model diff --git a/examples/Beer.jl b/examples/Beer.jl index c7069c536..bdad8cbf0 100644 --- a/examples/Beer.jl +++ b/examples/Beer.jl @@ -45,7 +45,7 @@ initialisations for `LAI` (m² m⁻²): the leaf area index. # Examples ```julia -m = ModelMapping(Beer(0.5), status=(LAI=2.0,)) +m = PlantSimEngine.ModelMapping(Beer(0.5), status=(LAI=2.0,)) meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_q=300.0) @@ -95,7 +95,7 @@ using PlantSimEngine.Examples Create a model list with a Beer model, and fit it to the data: ```julia -m = ModelMapping(Beer(0.6), status=(LAI=2.0,)) +m = PlantSimEngine.ModelMapping(Beer(0.6), status=(LAI=2.0,)) meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_f=300.0) run!(m, meteo) df = DataFrame(aPPFD=m[:aPPFD][1], LAI=m.status.LAI[1], Ri_PAR_f=meteo.Ri_PAR_f[1]) diff --git a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation1.jl b/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation1.jl index 120ad9f95..925f366ca 100644 --- a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation1.jl +++ b/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation1.jl @@ -96,10 +96,10 @@ end PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyLeafCarbonCaptureModel}) = PlantSimEngine.IsObjectIndependent() PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyLeafCarbonCaptureModel}) = PlantSimEngine.IsTimeStepIndependent() -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Scene => ToyDegreeDaysCumulModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyStockComputationModel(), mapped_variables=[ :carbon_captured => [:Leaf], @@ -109,7 +109,7 @@ mapping = ModelMapping( Status(carbon_stock=0.0) ), :Internode => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCustomInternodeEmergence(),#TT_emergence=20.0), mapped_variables=[:TT_cu => (:Scene => :TT_cu), PreviousTimeStep(:carbon_stock) => (:Plant => :carbon_stock)], diff --git a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl b/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl index d6e713c22..38a1cd2e5 100644 --- a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl +++ b/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl @@ -162,10 +162,10 @@ end PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyLeafCarbonCaptureModel}) = PlantSimEngine.IsObjectIndependent() PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyLeafCarbonCaptureModel}) = PlantSimEngine.IsTimeStepIndependent() -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Scene => ToyDegreeDaysCumulModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyStockComputationModel(), mapped_variables=[ :carbon_captured => [:Leaf], @@ -176,7 +176,7 @@ mapping = ModelMapping( Status(water_stock=0.0, carbon_stock=0.0) ), :Internode => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCustomInternodeEmergence(),#TT_emergence=20.0), mapped_variables=[:TT_cu => (:Scene => :TT_cu), PreviousTimeStep(:water_stock) => (:Plant => :water_stock), @@ -184,7 +184,7 @@ mapping = ModelMapping( ), Status(carbon_organ_creation_consumed=0.0), ), - :Root => (MultiScaleModel( + :Root => (PlantSimEngine.MultiScaleModel( model=ToyRootGrowthModel(10.0, 50.0, 10), mapped_variables=[PreviousTimeStep(:carbon_stock) => (:Plant => :carbon_stock), PreviousTimeStep(:water_stock) => (:Plant => :water_stock)], diff --git a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl b/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl index 6c305cd5b..4f68cbf16 100644 --- a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl +++ b/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl @@ -179,10 +179,10 @@ function PlantSimEngine.run!(::ToyLeafCarbonCaptureModel, models, status, meteo, end -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Scene => ToyDegreeDaysCumulModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyStockComputationModel(), mapped_variables=[ :carbon_captured => [:Leaf], @@ -195,7 +195,7 @@ mapping = ModelMapping( Status(water_stock=0.0, carbon_stock=0.0) ), :Internode => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCustomInternodeEmergence(),#TT_emergence=20.0), mapped_variables=[:TT_cu => (:Scene => :TT_cu), :water_stock => (:Plant => :water_stock), diff --git a/examples/ToySingleToMultiScale.jl b/examples/ToySingleToMultiScale.jl index e5389c60e..e14fb38c7 100644 --- a/examples/ToySingleToMultiScale.jl +++ b/examples/ToySingleToMultiScale.jl @@ -17,7 +17,7 @@ meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), ### Single-scale simulation ############################## -models_singlescale = ModelMapping( +models_singlescale = PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.5), ToyRUEGrowthModel(0.2), @@ -29,7 +29,7 @@ outputs_singlescale = run!(models_singlescale, meteo_day) ############################## #### Direct translation of the single-scale simulation ############################## -mapping_pseudo_multiscale = ModelMapping( +mapping_pseudo_multiscale = PlantSimEngine.ModelMapping( :Plant => ( ToyLAIModel(), Beer(0.5), @@ -69,13 +69,13 @@ end #### Actual multiscale version of the single-scale simulation ############################## -mapping_multiscale = ModelMapping( +mapping_multiscale = PlantSimEngine.ModelMapping( :Scene => ( ToyTt_CuModel(), Status(TT_cu=0.0), ), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[ :TT_cu => (:Scene => :TT_cu), diff --git a/examples/benchmark.jl b/examples/benchmark.jl index a372ad467..8f3182f80 100644 --- a/examples/benchmark.jl +++ b/examples/benchmark.jl @@ -5,7 +5,7 @@ using PlantSimEngine, PlantMeteo, DataFrames, CSV, Dates, Statistics # using PlantSimEngine.Examples meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Day) -models = ModelMapping( +models = PlantSimEngine.ModelMapping( ToyLAIModel(), status=(TT_cu=cumsum(meteo_day.TT),), ) @@ -20,7 +20,7 @@ time_run_seq = @benchmark run!($models, $meteo_day, executor=$(SequentialEx())) median_time_seq_ns = median(time_run_seq.times) / nrow(meteo_day) # Coupled model: -models_coupled = ModelMapping( +models_coupled = PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.5), status=(TT_cu=cumsum(meteo_day.TT),), diff --git a/examples/maespa_domain_example.jl b/examples/maespa_domain_example.jl index e9822168e..21cb50938 100644 --- a/examples/maespa_domain_example.jl +++ b/examples/maespa_domain_example.jl @@ -170,7 +170,7 @@ function _run_scene_leaf_targets!(leaf_targets, meteo, tair_canopy, vpd_canopy, local_meteo = _scene_leaf_meteo(meteo, tair_canopy, vpd_canopy) for target in leaf_targets _prepare_scene_leaf_target!(target, meteo, tair_canopy, vpd_canopy, psi_soil) - run_call!(target; meteo=local_meteo, publish=publish) # Publish is false because we iterate here and only want to publish the final solution at the end + run_call!(target; meteo=local_meteo, publish=publish) leaf_area = target.status.leaf_area total_rn += target.status.Rn * leaf_area total_lambda_e += target.status.λE * leaf_area @@ -311,8 +311,8 @@ function _run_scene_soil_feedback!(soil_target, transpiration_mm) end function PlantSimEngine.run!(m::SceneEB, models, status, meteo, constants, extra) - leaf_targets = dependency_targets(extra, :energy_balance) - soil_target = only(dependency_targets(extra, :soil)) + leaf_targets = call_targets(extra, :energy_balance) + soil_target = call_target(extra, :soil) solution = _solve_scene_energy_balance!(m, leaf_targets, soil_target, status, meteo, constants) fluxes = _publish_scene_leaf_solution!(leaf_targets, solution, meteo, m.ground_area) transpiration_mm = λE_to_E(fluxes.lambda_e, solution.final_meteo.λ) * duration_seconds(meteo) * 18.0e-6 @@ -390,44 +390,44 @@ has_species(node, species) = function maespa_mapping(; scene_model=SceneEB(25, 0.03, 0.005)) leaf_a = ( - ModelSpec(Monteith(; ε=0.955, maxiter=20, ΔT=0.02)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(Fvcb(; VcMaxRef=72.0, JMaxRef=135.0, RdRef=1.1)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(Tuzet(; g0=0.015, g1=4.8, Ψᵥ=-1.4, sf=3.2, Γ=42.0)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(LeafState()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(Monteith(; ε=0.955, maxiter=20, ΔT=0.02)) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), + ModelSpec(Fvcb(; VcMaxRef=72.0, JMaxRef=135.0, RdRef=1.1)) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), + ModelSpec(Tuzet(; g0=0.015, g1=4.8, Ψᵥ=-1.4, sf=3.2, Γ=42.0)) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), + ModelSpec(LeafState()) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), Status(Ra_SW_f=0.0, sky_fraction=1.0, d=0.035, aPPFD=0.0, Ψₗ=-0.1, leaf_area=0.018, leaf_carbon=0.0), ) leaf_b = ( - ModelSpec(Monteith(; ε=0.955, maxiter=20, ΔT=0.02)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(Fvcb(; VcMaxRef=58.0, JMaxRef=110.0, RdRef=1.3)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(Tuzet(; g0=0.012, g1=3.5, Ψᵥ=-1.1, sf=3.8, Γ=42.0)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(LeafState()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(Monteith(; ε=0.955, maxiter=20, ΔT=0.02)) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), + ModelSpec(Fvcb(; VcMaxRef=58.0, JMaxRef=110.0, RdRef=1.3)) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), + ModelSpec(Tuzet(; g0=0.012, g1=3.5, Ψᵥ=-1.1, sf=3.8, Γ=42.0)) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), + ModelSpec(LeafState()) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), Status(Ra_SW_f=0.0, sky_fraction=0.8, d=0.028, aPPFD=0.0, Ψₗ=-0.1, leaf_area=0.014, leaf_carbon=0.0), ) - plant_a = ModelMapping( + plant_a = PlantSimEngine.ModelMapping( :Plant => ( ModelSpec(AllocA(0.35, 0.55)) |> - MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]]) |> - TimeStepModel(ClockSpec(24.0, 0.0)), + PlantSimEngine.MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]]) |> + PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)), Status(leaf_pool=0.0, wood_pool=0.0), ), :Leaf => leaf_a, ) - plant_b = ModelMapping( + plant_b = PlantSimEngine.ModelMapping( :Plant => ( ModelSpec(AllocB(0.55, 0.35)) |> - MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]]) |> - TimeStepModel(ClockSpec(24.0, 0.0)), + PlantSimEngine.MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]]) |> + PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)), Status(leaf_pool=0.0, wood_pool=0.0), ), :Leaf => leaf_b, ) - soil = ModelMapping( - ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75)) |> TimeStepModel(Dates.Hour(1)), + soil = PlantSimEngine.ModelMapping( + ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75)) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), status=(theta1=0.33, theta2=0.36, psi_soil=-0.10, transpiration=0.0, infiltration=0.0), ) ground_area = scene_model.ground_area - scene = ModelMapping( + scene = PlantSimEngine.ModelMapping( ModelSpec(LAIModel(ground_area)) |> Inputs(:leaf_areas => Many(kind=:plant, scale=:Leaf, process=:leaf_state, var=:leaf_area)) |> TimeStep(Dates.Day(1)), @@ -451,11 +451,171 @@ function maespa_mapping(; scene_model=SceneEB(25, 0.03, 0.005)) iterations=0, ), ) - return SimulationMapping( - Domain(:plant_A, plant_a; kind=:plant, selector=node -> MultiScaleTreeGraph.symbol(node) == :Plant && has_species(node, :A)), - Domain(:plant_B, plant_b; kind=:plant, selector=node -> MultiScaleTreeGraph.symbol(node) == :Plant && has_species(node, :B)), - Domain(:soil, soil; kind=:soil), - Domain(:scene, scene; kind=:scene), + return PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:plant_A, plant_a; kind=:plant, selector=node -> MultiScaleTreeGraph.symbol(node) == :Plant && has_species(node, :A)), + PlantSimEngine.Domain(:plant_B, plant_b; kind=:plant, selector=node -> MultiScaleTreeGraph.symbol(node) == :Plant && has_species(node, :B)), + PlantSimEngine.Domain(:soil, soil; kind=:soil), + PlantSimEngine.Domain(:scene, scene; kind=:scene), + ) +end + +function _maespa_leaf_status(; leaf_area, sky_fraction, d) + return Status( + Ra_SW_f=0.0, + sky_fraction=sky_fraction, + d=d, + aPPFD=0.0, + Ψₗ=-0.1, + leaf_area=leaf_area, + leaf_carbon=0.0, + Tₗ=20.0, + Rn=0.0, + Ra_LW_f=0.0, + H=0.0, + λE=0.0, + Cₛ=400.0, + Cᵢ=300.0, + A=0.0, + Gₛ=0.0, + Gbₕ=0.0, + Dₗ=0.0, + Gbc=0.0, + iter=0, + ) +end + +_maespa_plant_status() = Status(leaf_carbon=[0.0], daily_growth=0.0, leaf_pool=0.0, wood_pool=0.0) + +function _maespa_scene_status() + return Status( + leaf_areas=[0.0], + leaf_area=0.0, + lai=0.0, + canopy_tair=20.0, + canopy_vpd=1.0, + canopy_rh=0.7, + canopy_htot=0.0, + canopy_gcanop=0.0, + scene_transpiration=0.0, + scene_assimilation=0.0, + psi_soil=-0.1, + iterations=0, + ) +end + +function _maespa_soil_status() + return Status(theta1=0.33, theta2=0.36, psi_soil=-0.10, transpiration=0.0, infiltration=0.0) +end + +function _maespa_species_template(species; monteith, fvcb, tuzet, allocation) + return ObjectTemplate( + ( + ModelSpec(monteith; name=:energy_balance) |> + AppliesTo(Many(scale=:Leaf)) |> + Calls(:photosynthesis => One(scale=:Leaf, process=:photosynthesis)) |> + TimeStep(Dates.Hour(1)), + ModelSpec(fvcb; name=:photosynthesis) |> + AppliesTo(Many(scale=:Leaf)) |> + Calls(:stomatal_conductance => One(scale=:Leaf, process=:stomatal_conductance)) |> + TimeStep(Dates.Hour(1)), + ModelSpec(tuzet; name=:stomatal_conductance) |> + AppliesTo(Many(scale=:Leaf)) |> + TimeStep(Dates.Hour(1)), + ModelSpec(LeafState(); name=:leaf_state) |> + AppliesTo(Many(scale=:Leaf)) |> + TimeStep(Dates.Hour(1)), + ModelSpec(allocation; name=:allocation) |> + AppliesTo(One(scale=:Plant)) |> + Inputs(:leaf_carbon => Many(scale=:Leaf, within=Self(), var=:leaf_carbon)) |> + TimeStep(Dates.Day(1)), + ); + kind=:plant, + species=species, + ) +end + +function _maespa_plant_instance(name, template; nleaves, leaf_area, sky_fraction, d) + plant_id = Symbol(name) + axis_id = Symbol(name, "_axis") + leaves = ntuple(nleaves) do index + Object( + Symbol(name, "_leaf_", index); + scale=:Leaf, + parent=axis_id, + status=_maespa_leaf_status(; leaf_area=leaf_area, sky_fraction=sky_fraction, d=d), + ) + end + return ObjectInstance( + name, + template; + root=Object(plant_id; scale=:Plant, parent=:scene, status=_maespa_plant_status()), + objects=( + Object(axis_id; scale=:Internode, parent=plant_id), + leaves..., + ), + ) +end + +function build_maespa_unified_scene(; scene_model=SceneEB(25, 0.03, 0.005), meteo=maespa_meteo()) + template_a = _maespa_species_template( + :A; + monteith=Monteith(; ε=0.955, maxiter=20, ΔT=0.02), + fvcb=Fvcb(; VcMaxRef=72.0, JMaxRef=135.0, RdRef=1.1), + tuzet=Tuzet(; g0=0.015, g1=4.8, Ψᵥ=-1.4, sf=3.2, Γ=42.0), + allocation=AllocA(0.35, 0.55), + ) + template_b = _maespa_species_template( + :B; + monteith=Monteith(; ε=0.955, maxiter=20, ΔT=0.02), + fvcb=Fvcb(; VcMaxRef=58.0, JMaxRef=110.0, RdRef=1.3), + tuzet=Tuzet(; g0=0.012, g1=3.5, Ψᵥ=-1.1, sf=3.8, Γ=42.0), + allocation=AllocB(0.55, 0.35), + ) + ground_area = scene_model.ground_area + return Scene( + Object(:scene; scale=:Scene, kind=:scene, status=_maespa_scene_status()), + Object(:soil; scale=:Soil, kind=:soil, parent=:scene, status=_maespa_soil_status()), + _maespa_plant_instance( + :plant_A, + template_a; + nleaves=2, + leaf_area=0.018, + sky_fraction=1.0, + d=0.035, + ), + _maespa_plant_instance( + :plant_B, + template_b; + nleaves=3, + leaf_area=0.014, + sky_fraction=0.8, + d=0.028, + ); + applications=( + ModelSpec(LAIModel(ground_area); name=:lai_dynamic) |> + AppliesTo(One(scale=:Scene)) |> + Inputs( + :leaf_areas => Many( + kind=:plant, + scale=:Leaf, + within=SceneScope(), + process=:leaf_state, + var=:leaf_area, + ), + ) |> + TimeStep(Dates.Day(1)), + ModelSpec(scene_model; name=:scene_eb) |> + AppliesTo(One(scale=:Scene)) |> + Calls( + :energy_balance => Many(kind=:plant, scale=:Leaf, process=:energy_balance), + :soil => One(kind=:soil, scale=:Soil, process=:soil_water), + ) |> + TimeStep(Dates.Hour(1)), + ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75); name=:soil_water) |> + AppliesTo(One(kind=:soil, scale=:Soil)) |> + TimeStep(Dates.Hour(1)), + ), + environment=meteo, ) end @@ -480,6 +640,19 @@ function run_maespa_example(; nhours=24, check=true) return (mtg=mtg, mapping=mapping, simulation=sim) end +function run_maespa_scene_example(; nhours=24, check=true) + scene = build_maespa_unified_scene(; meteo=maespa_meteo(; nhours=nhours)) + compiled = compile_scene(scene) + check && refresh_environment_bindings!(scene, compiled) + simulation = run!(scene; steps=nhours, constants=PlantMeteo.Constants()) + return ( + scene=scene, + compiled=simulation.compiled, + environment=simulation.environment_bindings, + simulation=simulation, + ) +end + if abspath(PROGRAM_FILE) == @__FILE__ result = run_maespa_example() sim = result.simulation diff --git a/examples/plantbiophysics_subsample/Monteith.jl b/examples/plantbiophysics_subsample/Monteith.jl index 93a4367ec..fdf94ebc0 100644 --- a/examples/plantbiophysics_subsample/Monteith.jl +++ b/examples/plantbiophysics_subsample/Monteith.jl @@ -510,7 +510,7 @@ More information [here](https://docs.julialang.org/en/v1/stdlib/Logging/#Environ meteo = Atmosphere(T = 22.0, Wind = 0.8333, P = 101.325, Rh = 0.4490995) # Using a constant value for Gs: -leaf = ModelMapping( +leaf = PlantSimEngine.ModelMapping( energy_balance = Monteith(), photosynthesis = Fvcb(), stomatal_conductance = ConstantGs(0.0, 0.0011), @@ -522,7 +522,7 @@ leaf.status.Rn julia> 12.902547446281233 # Using the model from Medlyn et al. (2011) for Gs: -leaf = ModelMapping( +leaf = PlantSimEngine.ModelMapping( energy_balance = Monteith(), photosynthesis = Fvcb(), stomatal_conductance = Medlyn(0.03, 12.0), diff --git a/examples/plantbiophysics_subsample/Tuzet.jl b/examples/plantbiophysics_subsample/Tuzet.jl index c677fd34f..04bc8220f 100644 --- a/examples/plantbiophysics_subsample/Tuzet.jl +++ b/examples/plantbiophysics_subsample/Tuzet.jl @@ -58,7 +58,7 @@ using PlantMeteo, PlantSimEngine, PlantBiophysics meteo = Atmosphere(T = 20.0, Wind = 1.0, P = 101.3, Rh = 0.65) leaf = - ModelMapping( + PlantSimEngine.ModelMapping( stomatal_conductance = Tuzet(0.03, 12.0, -1.5, 2.0, 30.0), status = (Cₛ = 380.0, Ψₗ = -1.0) ) diff --git a/skills/plantsimengine/SKILL.md b/skills/plantsimengine/SKILL.md index ebf01e04c..c922b7eec 100644 --- a/skills/plantsimengine/SKILL.md +++ b/skills/plantsimengine/SKILL.md @@ -1,132 +1,210 @@ --- name: plantsimengine -description: Use PlantSimEngine.jl to compose existing process models with ModelMapping, spatial multiscale MTG mappings, multirate ModelSpec configuration, and to implement or wrap new models by defining processes, inputs_, outputs_, run!, hard dependencies, and model traits. +description: Use PlantSimEngine.jl to compose models with the unified Scene/Object API, AppliesTo, Inputs, Calls, TimeStep, Environment, and to implement or wrap generic model kernels with inputs_, outputs_, dep, meteo traits, and run!. --- # PlantSimEngine Skill -Use this skill when helping with PlantSimEngine.jl simulations, model mappings, multiscale MTG coupling, multirate execution, or implementing/wrapping models. +Use this skill when helping with PlantSimEngine.jl scene/object simulations, +multiscale or multi-plant coupling, multirate execution, microclimate binding, +or implementing and wrapping models. PlantSimEngine has two main user roles: -- **Users** compose existing models. They mostly need `ModelMapping`, `MultiScaleModel`/`ModelSpec`, status initialization, variable mappings, spatial scale symbols, and multirate policies. -- **Modelers** implement or wrap models. They need process identity, `inputs_`, `outputs_`, `run!`, hard dependencies, model traits, and tests that prove the model composes correctly. +- **Users** compose existing models. They mostly need `Scene`, `Object`, + `ModelSpec`, `AppliesTo`, `Inputs`, `Calls`, `Updates`, `TimeStep`, and + `Environment`. +- **Modelers** implement or wrap generic kernels. They need process identity, + `inputs_`, `outputs_`, `dep`, `meteo_inputs_`, `meteo_outputs_`, `run!`, + model traits, and focused tests. -Prefer current APIs: `ModelMapping`, `ModelSpec`, `MultiScaleModel`, `Status`, `PreviousTimeStep`, and `run!`. Treat legacy `ModelList` as compatibility plumbing unless the user is working on legacy code. +For new multiscale, multi-plant, soil, scene, or microclimate work, prefer the +unified scene/object API. Use `ModelMapping`, `MultiScaleModel`, `Domain`, +`Route`, `AllDomains`, and `HardDomains` only when maintaining legacy +simulations during the breaking migration. `ModelMapping` is not exported, so +that compatibility code must spell it `PlantSimEngine.ModelMapping(...)`. ## First Steps -1. Identify whether the request is user-side mapping work or modeler-side implementation work. +1. Identify whether the request is user-side scenario composition or + modeler-side implementation. 2. Inspect existing model declarations before inventing names: - Search for process definitions with `rg "@process|abstract type Abstract.*Model" src examples docs test`. - Search for model APIs with `rg "inputs_\\(|outputs_\\(|PlantSimEngine.run!|dep\\(" src examples test`. 3. Check model IO with `inputs(model)`, `outputs(model)`, `variables(model)`, and process identity with `process(model)` when available. -4. Validate mappings early with `dep(mapping)`, `to_initialize(mapping[, mtg])`, `resolved_model_specs(mapping)`, and `explain_model_specs(mapping)` when relevant. +4. Compile scenarios early with `refresh_bindings!(scene)` and inspect + `explain_scene_applications`, `explain_bindings`, `explain_calls`, + `explain_schedule`, `explain_writers`, and environment bindings. ## User Workflow: Existing Models -### Single-scale mapping +### Build the object graph -Use `ModelMapping` when all models share one status. +Represent every runtime entity as an `Object` with stable identity and useful +labels. Plant topology remains scenario-defined. ```julia -mapping = ModelMapping( - ModelA(args...), - ModelB(args...); - status=(x=1.0, y=0.0), +scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:plant_1), + Object(:soil; scale=:Soil, kind=:soil, parent=:scene); + environment=(T=25.0, Rh=0.6, Wind=1.0), ) - -out = run!(mapping, meteo) ``` Rules: -- Inputs are matched to outputs by variable name after model declarations are flattened. -- Variables not produced by another model must be initialized through `status`, model defaults, meteo, or another supported input source. -- If two models produce the same canonical variable, inspect the graph and disambiguate before relying on incidental order. -- `Status` values are reference-backed. Writing `status.x = value` mutates the cell used by coupled models. +- `scale`, `kind`, `species`, and `name` are selector labels, not a fixed plant + ontology. +- Use any hierarchy required by the simulated object. +- `Status` is reference-backed. Same-rate coupling should preserve those + references instead of copying values. +- Use `ObjectTemplate` and `ObjectInstance` for repeated plants with shared + model objects and parameters. +- Adapt an existing MTG with `Scene(mtg; applications=..., environment=...)`, + or inspect `objects_from_mtg(mtg; ...)` before constructing the scene. + Accessors can translate MTG attributes into ids, labels, status, and + geometry. -### Spatial multiscale mapping +### Apply models -Use `ModelMapping` keyed by scale symbols when running on an MTG. Prefer symbol scales such as `:Scene`, `:Plant`, `:Leaf`, `:Internode`; string scale names are deprecated. +Wrap each scenario application in `ModelSpec` and select its target objects +with `AppliesTo`. ```julia -mapping = ModelMapping( - :Scene => ( - SceneModel(), - ), - :Plant => ( - MultiScaleModel( - PlantModel(), - [:TT_cu => (:Scene => :TT_cu)], - ), - ), - :Leaf => ( - LeafModel(), - Status(carbon_biomass=1.0), - ), +applications = ( + ModelSpec(LeafModel(); name=:leaf_model) |> + AppliesTo(Many(scale=:Leaf)), + + ModelSpec(SoilModel(); name=:soil_model) |> + AppliesTo(One(scale=:Soil)), ) -out = run!(mtg, mapping, meteo) +scene = Scene(scene_objects...; applications=applications, environment=backend) ``` -Each scale tuple can contain models, `ModelSpec`s, `MultiScaleModel`s, and optional `Status(...)` initializers for variables local to that scale. A model only sees variables in its local status unless they are mapped from another scale or supplied by runtime input binding. +Use explicit application names when a process is applied more than once to the +same object set. Selectors can disambiguate producers by `process=` and +`application=`. -### Variable mapping forms +### Couple values with Inputs -Use `MultiScaleModel(model, mapped_variables)` or pipe through `ModelSpec(model) |> MultiScaleModel(mapped_variables)`. - -Common forms: +Declare each consumer's value source with `Inputs`. ```julia -:x => :Plant # scalar read from :Plant, same variable name -:x => (:Plant => :y) # scalar read from :Plant variable :y -:x => [:Leaf] # vector read from all :Leaf nodes -:x => [:Leaf, :Internode] # vector read from several scales -:x => [:Leaf => :a, :Internode => :b] # vector read with per-scale renaming -:x => (Symbol("") => :y) # same-scale rename or alias -PreviousTimeStep(:x) # break current-step dependency inference -PreviousTimeStep(:x) => (:Plant => :y) +ModelSpec(AllocationModel(); name=:allocation) |> + AppliesTo(Many(scale=:Plant)) |> + Inputs( + :leaf_carbon => Many( + scale=:Leaf, + within=Self(), + process=:leaf_carbon, + var=:leaf_carbon, + ), + ) ``` Semantics: -- Scalar cross-scale mappings share a `Ref`; the source scale is expected to be unique at runtime. -- Vector mappings create a `RefVector`; models must handle vector inputs, and order follows MTG traversal order. -- Same-scale renaming creates a per-status alias, not a graph-wide shared variable. -- `PreviousTimeStep` prevents same-step dependency edges and is the standard way to break cycles. +- `One(...)`, `OptionalOne(...)`, and `Many(...)` make multiplicity explicit. +- `Self()` is the consumer object or subtree. `SelfPlant()` is the nearest + containing plant. `SceneScope()` selects across the scene. +- Same-rate scalar and many-object inputs use shared `Ref`s or reference + vectors where possible. +- Cross-rate values use typed temporal streams. +- Rename variables with + `Inputs(:local_name => One(within=Self(), var=:source_name))`. +- Use `PreviousTimeStep(:x) => selector` for an explicit lag and cycle break. +- An unresolved `OptionalOne` input keeps its `inputs_` default. -### Multirate configuration +### Control manual model calls -Use `ModelSpec` when models run at different clocks, consume streams with temporal policies, aggregate meteo, or need scoped streams. +Use `Calls` when the parent model must own the call stack, such as an iterative +energy balance. ```julia -daily = ClockSpec(24.0, 1.0) - -plant_spec = - ModelSpec(PlantDailyModel()) |> - MultiScaleModel([:leaf_assim => [:Leaf => :A]]) |> - TimeStepModel(daily) |> - InputBindings(; - leaf_assim=(process=:leafassimilation, scale=:Leaf, var=:A, policy=Integrate()), - ) |> - ScopeModel(:plant) +ModelSpec(SceneEnergyBalance(); name=:scene_energy) |> + AppliesTo(One(scale=:Scene)) |> + Calls( + :leaf_energy => Many( + scale=:Leaf, + within=SceneScope(), + process=:energy_balance, + ), + ) ``` -Policies: +Inside `run!`, retrieve targets with `call_target(s)(extra, :name)`. +`run_call!` defaults to `publish=false` for trial iterations. Use +`publish=true` once for the accepted state. + +### Configure rates and environment + +Use `Dates.Period` values directly: -- `HoldLast()` uses the latest producer value. -- `Interpolate()` interpolates or holds/extrapolates producer streams. -- `Integrate()` reduces over the consumer window, usually for fluxes or accumulations. -- `Aggregate()` reduces over the consumer window, usually for means, extrema, or summaries. +```julia +ModelSpec(DailyPlantModel(); name=:daily_plant) |> + AppliesTo(Many(scale=:Plant)) |> + Inputs( + :leaf_fluxes => Many( + scale=:Leaf, + within=Self(), + var=:flux, + policy=Integrate(), + window=Dates.Day(1), + ), + ) |> + TimeStep(Dates.Day(1)) +``` -Precedence: +Policies are `HoldLast`, `Interpolate`, `Integrate`, and `Aggregate`. When an +`Inputs(...)` selector omits `policy=...`, a unique producer's +`output_policy(::Type{<:Model})` trait supplies the default policy; explicit +selector policies override the trait. +Environment variables come from `meteo_inputs_` and `meteo_outputs_`. +`meteo_hint(...).bindings` can provide model-author default source remaps, and +`Environment(; sources=...)` is the scenario-level override. Use +`Environment(provider=:grid)` only when overriding automatic binding. +For global `Weather` tables, scene applications sample meteorology at their +compiled clock using the `meteo_hint` reducer/window. An +`Environment(; sources=...)` override changes the source but preserves that +reducer, and all objects in one application reuse the same sampled row for a +given timestep. Spatial backends define their own temporal sampling semantics. +When no `TimeStep(...)` is provided, the scene scheduler honors +`timespec(::Type{<:Model})`; an explicit `TimeStep(...)` remains the +scenario-level override. If the clock falls back to the scene base step, +`timestep_hint(::Type{<:Model})` required bounds are validated as compatibility +constraints. + +### Handle lifecycle changes + +Use `register_object!`, `remove_object!`, `reparent_object!`, and +`move_object!`. Structural changes refresh selectors, carriers, calls, +writers, and schedules before the next timestep. Geometry-only changes refresh +the affected environment bindings. + +### Validate the compiled scenario -1. Input policy: explicit `InputBindings(..., policy=...)` > producer `output_policy` > `HoldLast()`. -2. Timestep: `TimeStepModel(...)` > non-default `timespec(model)` > meteo base step. -3. Meteo sampling: explicit `MeteoBindings(...)`/`MeteoWindow(...)` > `meteo_hint(...)` > runtime defaults. +```julia +compiled = refresh_bindings!(scene) +explain_scene_applications(compiled) +explain_bindings(compiled) +explain_calls(compiled) +explain_schedule(compiled) +explain_writers(compiled) +explain_model_bundles(compiled) +``` -Use explicit `InputBindings` when several models/scales can produce the same variable, names differ, or the default temporal policy is not correct. Use `OutputRouting(; x=:stream_only)` when a producer should publish a stream without becoming the canonical status owner for `x`. +Run with `simulation = run!(scene; steps=n)` and inspect +`collect_outputs(simulation)` or `explain_outputs(simulation)`. Use +`run!(scene; tracked_outputs=OutputRequest(...))` when the user needs +resampled scene outputs; requests are materialized from retained typed streams +after the run, and dynamic objects are exported only across their own sample +interval. With explicit `tracked_outputs`, retained streams are pruned to +requested application/variable streams plus temporal `Inputs(...)` +dependencies; use `explain_output_retention(simulation)` to inspect that +decision. ## Modeler Workflow: New Or Wrapped Models @@ -180,28 +258,32 @@ When wrapping an external or existing model: ### Hard dependencies -Use hard dependencies when a parent model directly calls a required submodel inside its own `run!`. The runtime records the dependency but does not automatically execute it for the parent. +Use a `Call(...)` dependency default when a parent model directly calls a +required submodel inside its own `run!`. Scenario-level `Calls(...)` can +override the default selector without changing the kernel. ```julia PlantSimEngine.dep(::ParentModel) = ( - child_process=AbstractChild_ProcessModel, + child=Call(process=:child_process), ) function PlantSimEngine.run!(m::ParentModel, models, status, meteo, constants, extra=nothing) - run!(models.child_process, models, status, meteo, constants, extra) + child = call_target(extra, :child) + run_call!(child) status.parent_output = g(status.child_output) end ``` -For multiscale hard dependencies, declare the target scale: +The scenario decides the concrete target objects: ```julia -PlantSimEngine.dep(::ParentModel) = ( - child_process=AbstractChild_ProcessModel => (:Leaf,), -) +ModelSpec(ParentModel()) |> + AppliesTo(One(scale=:Scene)) |> + Calls(:child => Many(scale=:Leaf, process=:child_process)) ``` -Then call the child model explicitly on the correct target status, usually via `extra.statuses[:Leaf]` and `extra.models[:Leaf]`. Be careful: hard-dependency IO still participates in graph compilation through the owning soft node. +Hard calls are never automatically executed for the parent. Trial +`run_call!` calls do not publish; pass `publish=true` for the accepted state. ### Model traits @@ -233,28 +315,43 @@ Parallel traits are mainly for single-scale execution. Multirate MTG runs are cu ## Validation Checklist -For user mappings: - -- `to_initialize(mapping)` or `to_initialize(mapping, mtg)` lists only variables the user should really provide. -- `dep(mapping)` succeeds and the dependency graph matches the expected coupling. -- `explain_model_specs(mapping)` is sensible for multirate runs. -- Cycles are either absent or intentionally broken with `PreviousTimeStep`. -- Ambiguous multirate producers are resolved with `InputBindings`. +For user scenarios: + +- `refresh_bindings!(scene)` succeeds. +- `explain_scene_applications` shows the expected application/object pairs. +- `explain_bindings` shows the intended source ids, source applications, + temporal policies, and carrier semantics. +- `explain_calls`, `explain_schedule`, and `explain_writers` match the intended + manual call stack and execution order. +- `explain_execution_plan` groups large homogeneous object sets into concrete + batches; unexpected one-object batches usually indicate heterogeneous model, + status, binding, or environment types. +- Cycles are absent or intentionally broken with `PreviousTimeStep`. +- Ambiguous producers are resolved with `process=` or `application=`. +- Environment explanations show the expected provider, cell, geometry source, + source variables, and whether a temporal sampler is compiled. For model implementations: - Unit-test `inputs_`, `outputs_`, and a direct `run!` call with a minimal `Status`. -- Test single-scale composition when the model is meant to couple by variable name. -- Test MTG/multiscale mapping when the model expects scalar refs, `RefVector` inputs, or cross-scale writes. -- Test multirate behavior when traits, `InputBindings`, `MeteoBindings`, or `OutputRouting` matter. +- Test scene composition when the model is meant to couple by variable name. +- Test `Inputs(...)` when the model expects scalar refs, `RefVector` inputs, or + renamed variables. +- Test multirate behavior when `TimeStep`, temporal policies, windows, or + output routing matter. - Check hard dependencies by proving the parent actually calls the child and uses the child's outputs. ## Common Pitfalls - Do not confuse hard dependencies with soft dependency scheduling. Hard dependencies are manual calls. -- Do not rely on MTG topology for model execution order. Soft dependency order controls model order. -- Do not assume `RefVector` order has biological meaning. -- Do not map scalar reads from a scale that can have several runtime nodes unless the model really expects the chosen unique source behavior. +- Do not rely on object topology or declaration order for model execution + order. Compiled input and update edges control scheduling. +- Do not attach biological meaning to incidental collection order. Selectors + use stable object-id order. +- Do not use `One(...)` when several objects can match; use `Many(...)` or + disambiguate explicitly. - Do not use strings for new scale declarations. Use symbols. -- Do not mutate MTG topology after status initialization unless you reinitialize or use supported dynamic helpers. +- Do not mutate object topology outside `register_object!`, `remove_object!`, + or `reparent_object!`; bypassing lifecycle hooks leaves caches stale. +- Do not publish every iterative hard call. Publish only the accepted state. - Do not use `PreviousTimeStep` as a numerical lag unless the initial value and expected temporal semantics are explicit. diff --git a/src/PlantSimEngine.jl b/src/PlantSimEngine.jl index b13fdf334..b947de218 100644 --- a/src/PlantSimEngine.jl +++ b/src/PlantSimEngine.jl @@ -140,23 +140,21 @@ export register_object!, remove_object!, reparent_object!, move_object!, update_ export bindings_dirty, environment_bindings_dirty, scene_revision, environment_revision export compiled_bindings, compiled_environment_bindings, mark_environment_binding_dirty! export refresh_environment_bindings!, compile_environment_bindings, bind_environment -export object_ids, scene_objects, resolve_object_ids, resolve_objects, explain_objects, explain_instances, explain_scopes +export objects_from_mtg, object_ids, scene_objects, resolve_object_ids, resolve_objects, explain_objects, explain_instances, explain_scopes export geometry, position, bounds export CompiledScene, CompiledSceneApplication, CompiledSceneInputBinding, CompiledSceneCallBinding -export compile_scene, explain_scene_applications, explain_bindings, explain_calls, explain_writers +export compile_scene, explain_scene_applications, explain_bindings, explain_calls, explain_model_bundles, explain_writers export ObjectRefVector, input_carrier, input_value, has_reference_carrier -export SceneRunContext, SceneCallTarget +export SceneRunContext, SceneCallTarget, SceneSimulation, scene_outputs, explain_outputs +export explain_execution_plan, explain_output_retention export CompiledEnvironmentBinding, CompiledEnvironmentBindings, explain_environment_bindings export SceneScope, Self, SelfPlant, Ancestor, Scope, Kind, Species, Scale, Relation export One, OptionalOne, Many, ObjectAddress, object_address export Input, Call, AppliesTo, Inputs, Calls, TimeStep, Environment export application_name, applies_to, value_inputs, model_calls, environment_config -export MultiScaleModel, ModelMapping, ModelSpec, SameScale, Updates, TimeStepModel, InputBindings, MeteoBindings, MeteoWindow, OutputRouting, ScopeModel +export ModelSpec, Updates, OutputRouting export resolved_model_specs, explain_model_specs -export Domain, SimulationMapping, DomainSimulation, DomainModelKey, AllDomains, HardDomains -export Route, DomainRouteTarget, RouteCardinality -export ManyToOneVector, ManyToOneAggregate, OneToManyBroadcast, SpatialSample, SpatialScatterAdd -export dependency_values, dependency_targets, dependency_target, model_target, run_target!, run_call!, ModelTarget, explain_domains, explain_domain_models, explain_domain_statuses, explain_schedule, explain_domain_dependencies, explain_routes +export call_target, call_targets, run_call!, explain_schedule export RMSE, NRMSE, EF, dr export Status, TimeStepTable, status export init_status! diff --git a/src/checks/dimensions.jl b/src/checks/dimensions.jl index 605e5ca1c..c56b665e5 100644 --- a/src/checks/dimensions.jl +++ b/src/checks/dimensions.jl @@ -16,7 +16,7 @@ using PlantSimEngine.Examples w = Atmosphere(T = 20.0, Rh = 0.5, Wind = 1.0) # Creating a dummy component: -models = ModelMapping( +models = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), diff --git a/src/component_models/SingleScaleModelSet.jl b/src/component_models/SingleScaleModelSet.jl index 7f400b65b..31bb6ce98 100644 --- a/src/component_models/SingleScaleModelSet.jl +++ b/src/component_models/SingleScaleModelSet.jl @@ -80,7 +80,7 @@ We can now provide values for these variables in the `status` field. Direct before running: ```julia -julia> mapping = ModelMapping(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0, var2=0.3)); +julia> mapping = PlantSimEngine.ModelMapping(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0, var2=0.3)); ``` ```julia diff --git a/src/component_models/get_status.jl b/src/component_models/get_status.jl index 7b6bb0398..04d1da689 100644 --- a/src/component_models/get_status.jl +++ b/src/component_models/get_status.jl @@ -16,7 +16,7 @@ using PlantSimEngine using PlantSimEngine.Examples; # Create a SingleScaleModelSet -models = ModelMapping( +models = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), diff --git a/src/dataframe.jl b/src/dataframe.jl index 685279bb3..73daf4b11 100644 --- a/src/dataframe.jl +++ b/src/dataframe.jl @@ -11,7 +11,7 @@ using PlantSimEngine using DataFrames # Creating a ModelMapping -models = ModelMapping( +models = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -22,13 +22,13 @@ models = ModelMapping( df = DataFrame(models) # Converting to a Dict of ModelMappings -models = ModelMapping( - :Leaf => ModelMapping( +models = PlantSimEngine.ModelMapping( + :Leaf => PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model() ), - :InterNode => ModelMapping( + :InterNode => PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model() diff --git a/src/dependencies/dependencies.jl b/src/dependencies/dependencies.jl index 4ff1e2b12..0fd936e54 100644 --- a/src/dependencies/dependencies.jl +++ b/src/dependencies/dependencies.jl @@ -31,7 +31,7 @@ Note that in the 5th case, we still need to check if a variable is needed from a used as a child of the process at the other scale. Note there can be several levels of hard dependency graph, so this is done recursively. How do we do all that? We identify the hard dependencies first. Then we link the inputs/outputs of the hard dependencies roots -to other scales if needed. Then we transform all these nodes into soft dependencies, that we put into a Dict of Scale => ModelMapping(process => SoftDependencyNode). +to other scales if needed. Then we transform all these nodes into soft dependencies, that we put into a Dict of Scale => PlantSimEngine.ModelMapping(process => SoftDependencyNode). Then we traverse all these and we set nodes that need outputs from other nodes as inputs as children/parents. If a node has no dependency, it is set as a root node and pushed into a new Dict (independant_process_root). This Dict is the returned dependency graph. And it presents root nodes as independent starting points for the sub-graphs, which are the models that are coupled together. We can then traverse each of @@ -50,7 +50,7 @@ using PlantSimEngine # Including example processes and models: using PlantSimEngine.Examples; -models = ModelMapping( +models = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), diff --git a/src/doc_templates/mtg-related.jl b/src/doc_templates/mtg-related.jl index 352b4bb8e..96cb86bee 100644 --- a/src/doc_templates/mtg-related.jl +++ b/src/doc_templates/mtg-related.jl @@ -30,9 +30,9 @@ leaf2 = Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)); const MAPPING_EXAMPLE = """ ```@example -mapping = ModelMapping( \ +mapping = PlantSimEngine.ModelMapping( \ :Plant => ( \ - MultiScaleModel( \ + PlantSimEngine.MultiScaleModel( \ model=ToyCAllocationModel(), \ mapped_variables=[ \ :carbon_assimilation => [:Leaf], \ @@ -40,7 +40,7 @@ mapping = ModelMapping( \ :carbon_allocation => [:Leaf, :Internode] \ ], \ ), - MultiScaleModel( \ + PlantSimEngine.MultiScaleModel( \ model=ToyPlantRmModel(), \ mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],] \ ), \ @@ -51,7 +51,7 @@ mapping = ModelMapping( \ Status(TT=10.0) \ ), \ :Leaf => ( \ - MultiScaleModel( \ + PlantSimEngine.MultiScaleModel( \ model=ToyAssimModel(), \ mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], \ ), \ diff --git a/src/domains/domain_simulation.jl b/src/domains/domain_simulation.jl index 9f7d3654d..0a65d8f3e 100644 --- a/src/domains/domain_simulation.jl +++ b/src/domains/domain_simulation.jl @@ -989,6 +989,18 @@ end run_target!(models, status, dependency_name::AbstractString; kwargs...) = run_target!(models, status, Symbol(dependency_name); kwargs...) +""" + call_targets(extra, name) + call_target(extra, name) + +Return executable handles compiled from a `Calls(...)` declaration. +`call_target` requires exactly one matching handle. Execute handles with +[`run_call!`](@ref), optionally several times before publishing an accepted +state. +""" +call_targets(args...; kwargs...) = dependency_targets(args...; kwargs...) +call_target(args...; kwargs...) = dependency_target(args...; kwargs...) + """ run_call!(target; kwargs...) run_call!(models, status, dependency_name; kwargs...) diff --git a/src/evaluation/fit.jl b/src/evaluation/fit.jl index 873127244..de0652ea0 100644 --- a/src/evaluation/fit.jl +++ b/src/evaluation/fit.jl @@ -29,7 +29,7 @@ and `Ri_PAR_f`. # Including example processes and models: using PlantSimEngine.Examples; -m = ModelMapping(Beer(0.6), status=(LAI=2.0,)) +m = PlantSimEngine.ModelMapping(Beer(0.6), status=(LAI=2.0,)) meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_f=300.0) run!(m, meteo) df = DataFrame(aPPFD=m[:aPPFD][1], LAI=m.status.LAI[1], Ri_PAR_f=meteo.Ri_PAR_f[1]) diff --git a/src/mtg/ModelSpec.jl b/src/mtg/ModelSpec.jl index a4a4eeae0..a6c51e51c 100644 --- a/src/mtg/ModelSpec.jl +++ b/src/mtg/ModelSpec.jl @@ -7,12 +7,14 @@ User-side model configuration wrapper for mapping/model list composition. This allows modelers to publish reusable models while users decide how models are coupled in their simulation setup. """ -struct ModelSpec{M,N,AT,IN,CA,EV,MS,TS,IB,MB,MW,OR,SC,UP} +struct ModelSpec{M,N,AT,IN,IO,CA,CO,EV,MS,TS,IB,MB,MW,OR,SC,UP} model::M name::N applies_to::AT inputs::IN + input_origins::IO calls::CA + call_origins::CO environment::EV multiscale::MS timestep::TS @@ -86,7 +88,9 @@ function ModelSpec( name=nothing, applies_to=nothing, inputs=NamedTuple(), + input_origins=nothing, calls=NamedTuple(), + call_origins=nothing, environment=nothing, multiscale=nothing, timestep=nothing, @@ -101,9 +105,17 @@ function ModelSpec( normalized_name = _normalize_application_name(name) default_inputs = _model_default_value_inputs(base_model) - normalized_inputs = _merge_value_inputs(default_inputs, _normalize_application_bindings(inputs)) + explicit_inputs = _normalize_application_bindings(inputs) + normalized_inputs = _merge_value_inputs(default_inputs, explicit_inputs) + normalized_input_origins = isnothing(input_origins) ? + _binding_origins(default_inputs, explicit_inputs) : + _normalize_binding_origins(input_origins, normalized_inputs) default_calls = _model_default_model_calls(base_model) - normalized_calls = _merge_value_inputs(default_calls, _normalize_application_bindings(calls)) + explicit_calls = _normalize_application_bindings(calls) + normalized_calls = _merge_value_inputs(default_calls, explicit_calls) + normalized_call_origins = isnothing(call_origins) ? + _binding_origins(default_calls, explicit_calls) : + _normalize_binding_origins(call_origins, normalized_calls) derived_multiscale = _legacy_multiscale_from_value_inputs(normalized_inputs, base_model) combined_multiscale = _merge_legacy_multiscale(multiscale, derived_multiscale) normalized_multiscale = _normalize_multiscale_mapping(base_model, combined_multiscale) @@ -113,12 +125,14 @@ function ModelSpec( normalized_output_routing = _normalize_output_routing(output_routing) normalized_scope = _normalize_scope_selector(scope) normalized_updates = _normalize_updates(updates) - return ModelSpec{typeof(base_model),typeof(normalized_name),typeof(applies_to),typeof(normalized_inputs),typeof(normalized_calls),typeof(environment),typeof(normalized_multiscale),typeof(timestep),typeof(normalized_input_bindings),typeof(normalized_meteo_bindings),typeof(normalized_meteo_window),typeof(normalized_output_routing),typeof(normalized_scope),typeof(normalized_updates)}( + return ModelSpec{typeof(base_model),typeof(normalized_name),typeof(applies_to),typeof(normalized_inputs),typeof(normalized_input_origins),typeof(normalized_calls),typeof(normalized_call_origins),typeof(environment),typeof(normalized_multiscale),typeof(timestep),typeof(normalized_input_bindings),typeof(normalized_meteo_bindings),typeof(normalized_meteo_window),typeof(normalized_output_routing),typeof(normalized_scope),typeof(normalized_updates)}( base_model, normalized_name, applies_to, normalized_inputs, + normalized_input_origins, normalized_calls, + normalized_call_origins, environment, normalized_multiscale, timestep, @@ -136,7 +150,9 @@ function ModelSpec( name=nothing, applies_to=nothing, inputs=NamedTuple(), + input_origins=nothing, calls=NamedTuple(), + call_origins=nothing, environment=nothing, multiscale=nothing, timestep=nothing, @@ -153,7 +169,9 @@ function ModelSpec( name=name, applies_to=applies_to, inputs=inputs, + input_origins=input_origins, calls=calls, + call_origins=call_origins, environment=environment, multiscale=base_multiscale, timestep=timestep, @@ -172,7 +190,9 @@ function ModelSpec( name=spec.name, applies_to=spec.applies_to, inputs=spec.inputs, + input_origins=spec.input_origins, calls=spec.calls, + call_origins=spec.call_origins, environment=spec.environment, multiscale=spec.multiscale, timestep=spec.timestep, @@ -183,7 +203,7 @@ function ModelSpec( scope=spec.scope, updates=spec.updates ) - ModelSpec(model; name=name, applies_to=applies_to, inputs=inputs, calls=calls, environment=environment, multiscale=multiscale, timestep=timestep, input_bindings=input_bindings, meteo_bindings=meteo_bindings, meteo_window=meteo_window, output_routing=output_routing, scope=scope, updates=updates) + ModelSpec(model; name=name, applies_to=applies_to, inputs=inputs, input_origins=input_origins, calls=calls, call_origins=call_origins, environment=environment, multiscale=multiscale, timestep=timestep, input_bindings=input_bindings, meteo_bindings=meteo_bindings, meteo_window=meteo_window, output_routing=output_routing, scope=scope, updates=updates) end as_model_spec(spec::ModelSpec) = spec @@ -217,7 +237,9 @@ Return a `ModelSpec` with unified scene/object value-input bindings. """ function with_inputs(model_or_spec, bindings) spec = as_model_spec(model_or_spec) - return ModelSpec(spec; inputs=_normalize_application_bindings(bindings)) + explicit = _normalize_application_bindings(bindings) + origins = _binding_origins(_model_default_value_inputs(model_(spec)), explicit) + return ModelSpec(spec; inputs=explicit, input_origins=origins) end """ @@ -227,7 +249,9 @@ Return a `ModelSpec` with unified scene/object manual model-call bindings. """ function with_calls(model_or_spec, bindings) spec = as_model_spec(model_or_spec) - return ModelSpec(spec; calls=_normalize_application_bindings(bindings)) + explicit = _normalize_application_bindings(bindings) + origins = _binding_origins(_model_default_model_calls(model_(spec)), explicit) + return ModelSpec(spec; calls=explicit, call_origins=origins) end """ @@ -469,22 +493,22 @@ Environment(config) = x -> with_environment(x, config isa EnvironmentConfig ? co Environment(; kwargs...) = Environment((; kwargs...)) """ - MultiScaleModel(mapped_variables) + PlantSimEngine.MultiScaleModel(mapped_variables) Pipe-style transform that updates multiscale mapping on a model/spec. """ MultiScaleModel(mapped_variables) = x -> with_multiscale(x, mapped_variables) """ - TimeStepModel(timestep) + PlantSimEngine.TimeStepModel(timestep) Pipe-style transform that sets a user-selected timestep on a model/spec. """ TimeStepModel(timestep) = x -> with_timestep(x, timestep) """ - InputBindings(bindings) - InputBindings(; kwargs...) + PlantSimEngine.InputBindings(bindings) + PlantSimEngine.InputBindings(; kwargs...) Pipe-style transform that sets explicit producer bindings for model inputs. @@ -507,21 +531,21 @@ Each binding descriptor can be: - `policy` (`SchedulePolicy` instance/type, optional, default `HoldLast()`). When omitted fields cannot be inferred uniquely, runtime errors and asks for an -explicit `InputBindings(...)`. +explicit `PlantSimEngine.InputBindings(...)`. # Example ```julia ModelSpec(ConsumerModel()) |> -TimeStepModel(ClockSpec(24.0, 0.0)) |> -InputBindings(; A=(process=:assim, var=:carbon_assimilation, scale=:Leaf, policy=Integrate())) +PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)) |> +PlantSimEngine.InputBindings(; A=(process=:assim, var=:carbon_assimilation, scale=:Leaf, policy=Integrate())) ``` """ InputBindings(bindings) = x -> with_input_bindings(x, bindings) InputBindings(; kwargs...) = InputBindings((; kwargs...)) """ - MeteoBindings(bindings) - MeteoBindings(; kwargs...) + PlantSimEngine.MeteoBindings(bindings) + PlantSimEngine.MeteoBindings(; kwargs...) Pipe-style transform that sets weather-variable aggregation rules per model. @@ -544,8 +568,8 @@ Each rule value can be: # Example ```julia ModelSpec(DailyModel()) |> -TimeStepModel(ClockSpec(24.0, 0.0)) |> -MeteoBindings( +PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)) |> +PlantSimEngine.MeteoBindings( ; T=MeanWeighted(), Rh=MeanWeighted(), @@ -557,12 +581,12 @@ MeteoBindings(bindings) = x -> with_meteo_bindings(x, bindings) MeteoBindings(; kwargs...) = MeteoBindings((; kwargs...)) """ - MeteoWindow(window) + PlantSimEngine.MeteoWindow(window) Pipe-style transform that sets the weather row-selection window for one model. -This controls which meteo rows are sampled before `MeteoBindings` reducers are -applied. +This controls which meteo rows are sampled before +`PlantSimEngine.MeteoBindings` reducers are applied. # Arguments - `window`: a `PlantMeteo.AbstractSamplingWindow` instance/type. @@ -573,8 +597,8 @@ applied. # Example ```julia ModelSpec(DailyModel()) |> -TimeStepModel(ClockSpec(24.0, 0.0)) |> -MeteoWindow(CalendarWindow(:day; anchor=:current_period, week_start=1, completeness=:strict)) +PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)) |> +PlantSimEngine.MeteoWindow(CalendarWindow(:day; anchor=:current_period, week_start=1, completeness=:strict)) ``` """ MeteoWindow(window) = x -> with_meteo_window(x, window) @@ -608,7 +632,7 @@ OutputRouting(routing) = x -> with_output_routing(x, routing) OutputRouting(; kwargs...) = OutputRouting((; kwargs...)) """ - ScopeModel(scope) + PlantSimEngine.ScopeModel(scope) Pipe-style transform that sets stream scope selection for a model. @@ -624,7 +648,7 @@ multi-rate simulations. # Example ```julia ModelSpec(LeafSourceModel()) |> -ScopeModel(:plant) +PlantSimEngine.ScopeModel(:plant) ``` """ ScopeModel(scope) = x -> with_scope(x, scope) diff --git a/src/mtg/mapping/getters.jl b/src/mtg/mapping/getters.jl index 1b9b4a2b0..f3559b2d0 100644 --- a/src/mtg/mapping/getters.jl +++ b/src/mtg/mapping/getters.jl @@ -24,7 +24,7 @@ julia> using PlantSimEngine.Examples; If we just give a MultiScaleModel, we get its model as a one-element vector: ```jldoctest mylabel -julia> models = MultiScaleModel( \ +julia> models = PlantSimEngine.MultiScaleModel( \ model=ToyCAllocationModel(), \ mapped_variables=[ \ :carbon_assimilation => [:Leaf], \ @@ -44,7 +44,7 @@ If we give a tuple of models, we get each model in a vector: ```jldoctest mylabel julia> models2 = ( \ - MultiScaleModel( \ + PlantSimEngine.MultiScaleModel( \ model=ToyAssimModel(), \ mapped_variables=[:soil_water_content => :Soil,], \ ), \ diff --git a/src/mtg/mapping/reverse_mapping.jl b/src/mtg/mapping/reverse_mapping.jl index ccf6255e9..3e9ecfe08 100644 --- a/src/mtg/mapping/reverse_mapping.jl +++ b/src/mtg/mapping/reverse_mapping.jl @@ -31,9 +31,9 @@ julia> using PlantSimEngine.Examples; ``` ```jldoctest mylabel -julia> mapping = ModelMapping( \ +julia> mapping = PlantSimEngine.ModelMapping( \ :Plant => \ - MultiScaleModel( \ + PlantSimEngine.MultiScaleModel( \ model=ToyCAllocationModel(), \ mapped_variables=[ \ :carbon_assimilation => [:Leaf], \ @@ -43,7 +43,7 @@ julia> mapping = ModelMapping( \ ), \ :Internode => ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), \ :Leaf => ( \ - MultiScaleModel( \ + PlantSimEngine.MultiScaleModel( \ model=ToyAssimModel(), \ mapped_variables=[:soil_water_content => :Soil => :soil_water_content,], \ ), \ diff --git a/src/mtg/model_spec_inference.jl b/src/mtg/model_spec_inference.jl index 94814876f..06de5e18a 100644 --- a/src/mtg/model_spec_inference.jl +++ b/src/mtg/model_spec_inference.jl @@ -836,7 +836,9 @@ function _model_specs_rows(model_specs) application_name=application_name(spec), applies_to=applies_to(spec), value_inputs=value_inputs(spec), + input_origins=input_origins(spec), model_calls=model_calls(spec), + call_origins=call_origins(spec), environment=environment_config(spec), timestep=timestep(spec), timespec_default=timespec(model_(spec)), @@ -903,7 +905,9 @@ function explain_model_specs(target; io::IO=stdout, infer::Bool=true, validate:: application_name_desc = isnothing(row.application_name) ? "(unnamed)" : string(row.application_name) applies_to_desc = isnothing(row.applies_to) ? "(implicit legacy target)" : _stringify_compact(row.applies_to) value_inputs_desc = (row.value_inputs isa NamedTuple && isempty(keys(row.value_inputs))) ? "(none)" : _stringify_compact(row.value_inputs) + input_origins_desc = isempty(keys(row.input_origins)) ? "(none)" : _stringify_compact(row.input_origins) model_calls_desc = (row.model_calls isa NamedTuple && isempty(keys(row.model_calls))) ? "(none)" : _stringify_compact(row.model_calls) + call_origins_desc = isempty(keys(row.call_origins)) ? "(none)" : _stringify_compact(row.call_origins) environment_desc = isnothing(row.environment) ? "(default)" : _stringify_compact(row.environment) println( io, @@ -919,8 +923,12 @@ function explain_model_specs(target; io::IO=stdout, infer::Bool=true, validate:: applies_to_desc, ", value_inputs=", value_inputs_desc, + ", input_origins=", + input_origins_desc, ", model_calls=", model_calls_desc, + ", call_origins=", + call_origins_desc, ", environment=", environment_desc, ", timestep=", diff --git a/src/mtg/save_results.jl b/src/mtg/save_results.jl index b1b040699..9f743aed7 100644 --- a/src/mtg/save_results.jl +++ b/src/mtg/save_results.jl @@ -34,9 +34,9 @@ julia> using PlantSimEngine.Examples; Define the models mapping: ```jldoctest mylabel -julia> mapping = ModelMapping( \ +julia> mapping = PlantSimEngine.ModelMapping( \ :Plant => ( \ - MultiScaleModel( \ + PlantSimEngine.MultiScaleModel( \ model=ToyCAllocationModel(), \ mapped_variables=[ \ :carbon_assimilation => [:Leaf], \ @@ -44,7 +44,7 @@ julia> mapping = ModelMapping( \ :carbon_allocation => [:Leaf, :Internode] \ ], \ ), - MultiScaleModel( \ + PlantSimEngine.MultiScaleModel( \ model=ToyPlantRmModel(), \ mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],] \ ), \ @@ -55,7 +55,7 @@ julia> mapping = ModelMapping( \ Status(TT=10.0, carbon_biomass=1.0) \ ), \ :Leaf => ( \ - MultiScaleModel( \ + PlantSimEngine.MultiScaleModel( \ model=ToyAssimModel(), \ mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], \ ), \ diff --git a/src/processes/model_initialisation.jl b/src/processes/model_initialisation.jl index 1c1d2fde6..4b20e51e6 100755 --- a/src/processes/model_initialisation.jl +++ b/src/processes/model_initialisation.jl @@ -30,10 +30,10 @@ using PlantSimEngine.Examples to_initialize(process1=Process1Model(1.0), process2=Process2Model()) # Or using a component directly: -models = ModelMapping(process1=Process1Model(1.0), process2=Process2Model()) +models = PlantSimEngine.ModelMapping(process1=Process1Model(1.0), process2=Process2Model()) to_initialize(models) -m = ModelMapping( +m = PlantSimEngine.ModelMapping( ( process1=Process1Model(1.0), process2=Process2Model() @@ -52,13 +52,13 @@ using PlantSimEngine # Load the dummy models given as example in the package: using PlantSimEngine.Examples -mapping = ModelMapping( - :Leaf => ModelMapping( +mapping = PlantSimEngine.ModelMapping( + :Leaf => PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model() ), - :Internode => ModelMapping( + :Internode => PlantSimEngine.ModelMapping( process1=Process1Model(1.0), ) ) @@ -179,12 +179,12 @@ using PlantSimEngine using PlantSimEngine.Examples models = Dict( - :Leaf => ModelMapping( + :Leaf => PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model() ), - :Internode => ModelMapping( + :Internode => PlantSimEngine.ModelMapping( process1=Process1Model(1.0), ) ) @@ -292,7 +292,7 @@ using PlantSimEngine # Load the dummy models given as example in the package: using PlantSimEngine.Examples -models = ModelMapping( +models = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model() @@ -367,7 +367,7 @@ using PlantSimEngine # Load the dummy models given as example in the package: using PlantSimEngine.Examples -models = ModelMapping( +models = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model() diff --git a/src/processes/models_inputs_outputs.jl b/src/processes/models_inputs_outputs.jl index ec8c4253c..47c3c6756 100644 --- a/src/processes/models_inputs_outputs.jl +++ b/src/processes/models_inputs_outputs.jl @@ -79,6 +79,7 @@ applies_to(spec::ModelSpec) = spec.applies_to Unified scene/object value-input bindings declared with `Inputs(...)`. """ value_inputs(spec::ModelSpec) = spec.inputs +input_origins(spec::ModelSpec) = spec.input_origins """ model_calls(spec::ModelSpec) @@ -86,6 +87,7 @@ value_inputs(spec::ModelSpec) = spec.inputs Unified scene/object manual call bindings declared with `Calls(...)`. """ model_calls(spec::ModelSpec) = spec.calls +call_origins(spec::ModelSpec) = spec.call_origins """ environment_config(spec::ModelSpec) @@ -114,7 +116,7 @@ For example, consumer input `:C` can be mapped from producer output `:S`. # Example ```julia -InputBindings(; C=(process=:myproducer, var=:S)) +PlantSimEngine.InputBindings(; C=(process=:myproducer, var=:S)) ``` """ input_bindings(spec::ModelSpec) = spec.input_bindings diff --git a/src/run.jl b/src/run.jl index cfe4a0b79..9b8f6122c 100644 --- a/src/run.jl +++ b/src/run.jl @@ -66,7 +66,7 @@ julia> using PlantSimEngine.Examples; Create a model mapping: ```jldoctest run -julia> mapping = ModelMapping(Process1Model(1.0), Process2Model(), Process3Model(); status = (var1=1.0, var2=2.0)); +julia> mapping = PlantSimEngine.ModelMapping(Process1Model(1.0), Process2Model(), Process3Model(); status = (var1=1.0, var2=2.0)); ``` Create meteo data: diff --git a/src/scene_object_api.jl b/src/scene_object_api.jl index 9959b7903..881dbeb54 100644 --- a/src/scene_object_api.jl +++ b/src/scene_object_api.jl @@ -210,6 +210,7 @@ mutable struct Scene{R,A,E,I} environment_binding_cache::Any bindings_dirty::Bool environment_bindings_dirty::Bool + environment_dirty_objects::Union{Nothing,Set{ObjectId}} revision::Int environment_revision::Int end @@ -347,14 +348,91 @@ function Scene( nothing, true, true, + nothing, 0, 0, ) return _register_scene_objects!(scene, objects) end -function _mark_environment_bindings_dirty!(scene::Scene) - scene.environment_binding_cache = nothing +function _mtg_attribute(node, key::Symbol, default=nothing) + try + return node[key] + catch + return default + end +end + +""" + objects_from_mtg(root; id=node_id, scale=symbol, kind=..., species=..., + name=..., geometry=..., status=...) + +Adapt one MTG subtree to scene `Object` values. The MTG is traversed once; +node ids and parent relations become stable scene-object identities and +relations. Accessors may attach labels, geometry, and existing status objects +without prescribing a plant architecture. +""" +function objects_from_mtg( + root::MultiScaleTreeGraph.Node; + id=node_id, + scale=symbol, + kind=node -> _mtg_attribute(node, :kind, nothing), + species=node -> _mtg_attribute(node, :species, nothing), + name=node -> _mtg_attribute(node, :name, nothing), + geometry=node -> _mtg_attribute(node, :geometry, nothing), + status=node -> _mtg_attribute(node, :plantsimengine_status, nothing), +) + objects = Object[] + MultiScaleTreeGraph.traverse!(root) do node + node_parent = parent(node) + parent_id = node === root || isnothing(node_parent) ? nothing : id(node_parent) + push!( + objects, + Object( + id(node); + scale=scale(node), + kind=kind(node), + species=species(node), + name=name(node), + parent=parent_id, + geometry=geometry(node), + status=status(node), + ), + ) + end + return objects +end + +""" + Scene(root::MultiScaleTreeGraph.Node; applications=(), instances=(), + environment=nothing, kwargs...) + +Build a unified scene directly from an MTG subtree. Extra keyword arguments +are forwarded to [`objects_from_mtg`](@ref). +""" +function Scene( + root::MultiScaleTreeGraph.Node; + applications=(), + instances=(), + environment=nothing, + kwargs..., +) + objects = objects_from_mtg(root; kwargs...) + return Scene( + objects...; + applications=applications, + instances=instances, + environment=environment, + ) +end + +function _mark_environment_bindings_dirty!(scene::Scene, object_id::Union{Nothing,ObjectId}=nothing) + if isnothing(object_id) || isnothing(scene.environment_binding_cache) + scene.environment_binding_cache = nothing + scene.environment_dirty_objects = nothing + elseif !isnothing(scene.environment_dirty_objects) + push!(scene.environment_dirty_objects, object_id) + end scene.environment_bindings_dirty = true scene.environment_revision += 1 return scene @@ -375,8 +453,9 @@ compiled_bindings(scene::Scene) = scene.binding_cache compiled_environment_bindings(scene::Scene) = scene.environment_binding_cache mark_environment_binding_dirty!(scene::Scene) = _mark_environment_bindings_dirty!(scene) function mark_environment_binding_dirty!(scene::Scene, id) - _scene_object(scene, id) - return _mark_environment_bindings_dirty!(scene) + object_id = ObjectId(id) + _scene_object(scene, object_id) + return _mark_environment_bindings_dirty!(scene, object_id) end function mark_environment_binding_dirty!(scene::Scene, object::Object) return mark_environment_binding_dirty!(scene, object.id) @@ -520,7 +599,12 @@ end function update_geometry!(scene::Scene, id, geometry_or_position; invalidate_environment::Bool=true) object = _scene_object(scene, id) object.geometry = geometry_or_position - invalidate_environment && _mark_environment_bindings_dirty!(scene) + if invalidate_environment + _mark_environment_bindings_dirty!(scene, object.id) + for descendant_id in _geometry_inheriting_descendants(scene, object.id) + _mark_environment_bindings_dirty!(scene, descendant_id) + end + end return object end @@ -552,6 +636,17 @@ _geometry_bounds(_) = nothing bounds(object::Object) = _geometry_bounds(geometry(object)) bounds(status::Status) = _geometry_bounds(geometry(status)) +function _geometry_inheriting_descendants(scene::Scene, root_id::ObjectId) + ids = ObjectId[] + for child_id in _scene_object(scene, root_id).children + child = _scene_object(scene, child_id) + isnothing(geometry(child)) || continue + push!(ids, child_id) + append!(ids, _geometry_inheriting_descendants(scene, child_id)) + end + return ids +end + function refresh_bindings!(scene::Scene, specs=scene.applications; force::Bool=false) uses_scene_applications = specs === scene.applications if !uses_scene_applications @@ -566,8 +661,29 @@ end function refresh_environment_bindings!(scene::Scene, compiled=refresh_bindings!(scene); force::Bool=false) if force || scene.environment_bindings_dirty || isnothing(scene.environment_binding_cache) - scene.environment_binding_cache = compile_environment_bindings(scene, compiled) + if !force && + !isnothing(scene.environment_binding_cache) && + !isnothing(scene.environment_dirty_objects) + scene.environment_binding_cache = _refresh_environment_bindings_for_objects( + scene, + compiled, + scene.environment_binding_cache, + scene.environment_dirty_objects, + ) + else + scene.environment_binding_cache = compile_environment_bindings(scene, compiled) + end scene.environment_bindings_dirty = false + scene.environment_dirty_objects = Set{ObjectId}() + else + reconciled = _reconcile_environment_binding_metadata( + scene, + compiled, + scene.environment_binding_cache, + ) + scene.environment_binding_cache = isnothing(reconciled) ? + compile_environment_bindings(scene, compiled) : + reconciled end return scene.environment_binding_cache end @@ -772,8 +888,15 @@ Scale(scale::Union{Symbol,AbstractString}) = Scale(Symbol(scale)) struct Relation <: AbstractObjectSelector relation::Symbol + function Relation(relation::Symbol) + relation in _OBJECT_RELATIONS || error( + "Unsupported object relation `$(relation)`. Supported relations are $(_OBJECT_RELATIONS)." + ) + return new(relation) + end end -Relation(relation::Union{Symbol,AbstractString}) = Relation(Symbol(relation)) +const _OBJECT_RELATIONS = (:self, :parent, :children, :ancestors, :descendants, :siblings) +Relation(relation::AbstractString) = Relation(Symbol(relation)) _maybe_symbol(x) = isnothing(x) ? nothing : Symbol(x) @@ -842,6 +965,16 @@ function _selector_with_application_prefix( return _rebuild_selector(selector, merge(selector_criteria, (; application=mounted_name))) end +function _selector_with_previous_timestep( + selector::AbstractObjectMultiplicity, + previous::PreviousTimeStep, +) + return _rebuild_selector( + selector, + merge(criteria(selector), (; policy=previous)), + ) +end + function _mounted_application_name(spec, index::Int) name = application_name(spec) return isnothing(name) ? process(spec) : name @@ -1053,6 +1186,37 @@ function _ancestor_id(scene::Scene, current_id::ObjectId; scale=nothing, kind=no end end +function _ancestor_ids(scene::Scene, current_id::ObjectId) + ids = ObjectId[] + parent_id = _scene_object(scene, current_id).parent + while !isnothing(parent_id) + push!(ids, parent_id) + parent_id = _scene_object(scene, parent_id).parent + end + return ids +end + +function _relation_object_ids(scene::Scene, relation::Symbol, context) + current_id = _object_id_from_context(context) + isnothing(current_id) && error( + "`Relation(:$(relation))` selectors require a current object context." + ) + object = _scene_object(scene, current_id) + relation == :self && return ObjectId[current_id] + relation == :parent && return isnothing(object.parent) ? ObjectId[] : ObjectId[object.parent] + relation == :children && return copy(object.children) + relation == :ancestors && return _ancestor_ids(scene, current_id) + relation == :descendants && return _descendant_ids(scene, current_id)[2:end] + if relation == :siblings + isnothing(object.parent) && return ObjectId[] + return ObjectId[ + sibling_id for sibling_id in _scene_object(scene, object.parent).children + if sibling_id != current_id + ] + end + error("Unsupported object relation `$(relation)`.") +end + function _selector_scope_from_positional(selectors) scopes = filter( selector -> selector isa Union{SceneScope,Self,SelfPlant,Ancestor,Scope}, @@ -1136,13 +1300,101 @@ function _scope_object_ids(scene::Scene, scope, context) candidate = ObjectId(scope.name) root_id = haskey(scene.registry.objects, candidate) ? candidate : nothing end - isnothing(root_id) && error("No named scope or object `$(scope.name)` found in the scene registry.") + if isnothing(root_id) + available = sort!(unique(Symbol[ + keys(scene.registry.by_name)..., + (id.value for id in keys(scene.registry.objects))..., + ]); by=string) + suggestions = _near_symbol_matches(scope.name, available) + error( + "No named scope or object `$(scope.name)` found in the scene registry. ", + "available=$(available), suggestions=$(suggestions)." + ) + end return _descendant_ids(scene, root_id) end error("Unsupported object scope selector `$(scope)` of type `$(typeof(scope))`.") end +function _symbol_edit_distance(left::Symbol, right::Symbol) + a = collect(string(left)) + b = collect(string(right)) + previous = collect(0:length(b)) + current = similar(previous) + for i in eachindex(a) + current[1] = i + for j in eachindex(b) + substitution = previous[j] + (a[i] == b[j] ? 0 : 1) + current[j + 1] = min(current[j] + 1, previous[j + 1] + 1, substitution) + end + previous, current = current, previous + end + return previous[end] +end + +function _near_symbol_matches(requested, available) + isnothing(requested) && return Symbol[] + requested_symbol = Symbol(requested) + threshold = max(2, cld(length(string(requested_symbol)), 3)) + ranked = Tuple{Int,Symbol}[ + (_symbol_edit_distance(requested_symbol, candidate), candidate) + for candidate in available + ] + filter!(pair -> first(pair) <= threshold, ranked) + sort!(ranked; by=pair -> (first(pair), string(last(pair)))) + return Symbol[last(pair) for pair in Iterators.take(ranked, 3)] +end + +function _available_selector_labels(scene::Scene, candidate_ids) + objects = (_scene_object(scene, id) for id in candidate_ids) + scales = Symbol[] + kinds = Symbol[] + species = Symbol[] + names = Symbol[] + for object in objects + isnothing(object.scale) || push!(scales, object.scale) + isnothing(object.kind) || push!(kinds, object.kind) + isnothing(object.species) || push!(species, object.species) + isnothing(object.name) || push!(names, object.name) + end + return ( + scales=sort!(unique!(scales); by=string), + kinds=sort!(unique!(kinds); by=string), + species=sort!(unique!(species); by=string), + names=sort!(unique!(names); by=string), + ) +end + +function _selector_resolution_error( + scene::Scene, + selector, + candidate_ids, + matched_ids; + context=nothing, + scale=nothing, + kind=nothing, + species=nothing, + name=nothing, +) + available = _available_selector_labels(scene, candidate_ids) + suggestions = ( + scale=_near_symbol_matches(scale, available.scales), + kind=_near_symbol_matches(kind, available.kinds), + species=_near_symbol_matches(species, available.species), + name=_near_symbol_matches(name, available.names), + ) + expected = selector isa One ? "exactly one" : "zero or one" + context_id = _object_id_from_context(context) + error( + "Expected $(expected) object for selector `$(selector)`, got $(length(matched_ids)). ", + "context=$(isnothing(context_id) ? nothing : context_id.value), ", + "matched_ids=$([id.value for id in matched_ids]), ", + "requested=(scale=$(scale), kind=$(kind), species=$(species), name=$(name)), ", + "available=$(available), suggestions=$(suggestions)." + ) +end + function _matches_object_criteria(object::Object; scale=nothing, kind=nothing, species=nothing, name=nothing) isnothing(scale) || object.scale == scale || return false isnothing(kind) || object.kind == kind || return false @@ -1164,10 +1416,15 @@ function _resolve_object_ids( ) criteria_ = criteria(selector) relation = _criteria_value(criteria_, :relation, Relation) - isnothing(relation) || error("`Relation(...)` selector resolution is not implemented yet.") explicit_scope = _criteria_scope(criteria_) - scope = isnothing(explicit_scope) ? default_scope : explicit_scope + scope = if !isnothing(explicit_scope) + explicit_scope + elseif isnothing(relation) + default_scope + else + nothing + end scale = _criteria_value(criteria_, :scale, Scale) kind = _criteria_value(criteria_, :kind, Kind) species = _criteria_value(criteria_, :species, Species) @@ -1175,6 +1432,7 @@ function _resolve_object_ids( if default_to_context && isnothing(explicit_scope) && + isnothing(relation) && isnothing(scale) && isnothing(kind) && isnothing(species) && @@ -1183,7 +1441,17 @@ function _resolve_object_ids( return ObjectId[_object_id_from_context(context)] end - candidate_ids = _scope_object_ids(scene, scope, context) + candidate_ids = if isnothing(relation) + _scope_object_ids(scene, scope, context) + else + related_ids = _relation_object_ids(scene, relation, context) + if isnothing(explicit_scope) + related_ids + else + scoped_ids = Set(_scope_object_ids(scene, explicit_scope, context)) + ObjectId[id for id in related_ids if id in scoped_ids] + end + end ids = ObjectId[ id for id in candidate_ids if _matches_object_criteria(_scene_object(scene, id); scale=scale, kind=kind, species=species, name=name) @@ -1191,9 +1459,29 @@ function _resolve_object_ids( _sort_object_ids!(ids) if selector isa One && length(ids) != 1 - error("Expected exactly one object for selector `$(selector)`, got $(length(ids)).") + _selector_resolution_error( + scene, + selector, + candidate_ids, + ids; + context=context, + scale=scale, + kind=kind, + species=species, + name=name, + ) elseif selector isa OptionalOne && length(ids) > 1 - error("Expected zero or one object for selector `$(selector)`, got $(length(ids)).") + _selector_resolution_error( + scene, + selector, + candidate_ids, + ids; + context=context, + scale=scale, + kind=kind, + species=species, + name=name, + ) end return ids end @@ -1242,6 +1530,7 @@ struct CompiledSceneCallBinding{SEL} consumer_id::ObjectId call::Symbol selector::SEL + origin::Symbol callee_object_ids::Vector{ObjectId} callee_application_ids::Vector{Symbol} process::Union{Nothing,Symbol} @@ -1256,20 +1545,25 @@ struct CompiledEnvironmentBinding{B,C,S} backend::B cell::C required_inputs::Vector{Symbol} + source_inputs::Vector{Symbol} produced_outputs::Vector{Symbol} support::S + geometry_source_object_id::Union{Nothing,ObjectId} + geometry_source::Symbol config::Any end -struct CompiledEnvironmentBindings{SC,B,I} +struct CompiledEnvironmentBindings{SC,B,I,S,C} scene::SC bindings::B by_target::I + samplers_by_application::S + sample_cache::C scene_revision::Int environment_revision::Int end -struct CompiledScene{SC,AP,AI,IB,CB,IBI,CBI,AO} +struct CompiledScene{SC,AP,AI,IB,CB,IBI,CBI,MBI,AO} scene::SC applications::AP applications_by_id::AI @@ -1277,6 +1571,7 @@ struct CompiledScene{SC,AP,AI,IB,CB,IBI,CBI,AO} call_bindings::CB input_bindings_by_target::IBI call_bindings_by_target::CBI + model_bundles_by_target::MBI application_order::AO revision::Int end @@ -1321,14 +1616,22 @@ end function _compile_scene(scene::Scene, raw_specs) timeline = _scene_timeline(scene) applications = _compile_scene_applications(scene, raw_specs, timeline) - _validate_scene_writers!(applications) + call_bindings = _compile_scene_call_bindings(scene, applications) + _validate_scene_writers!(applications, call_bindings) + _prepare_scene_output_statuses!(scene, applications) input_bindings = _compile_scene_input_bindings(scene, applications) + _prepare_scene_bound_input_statuses!(scene, applications, input_bindings) + _wire_scene_input_carriers!(scene, input_bindings) _validate_scene_required_inputs!(scene, applications, input_bindings) - call_bindings = _compile_scene_call_bindings(scene, applications) input_bindings_by_target = _index_scene_bindings(input_bindings, :application_id, :consumer_id) call_bindings_by_target = _index_scene_bindings(call_bindings, :application_id, :consumer_id) application_order = _compile_scene_application_order(applications, input_bindings, call_bindings) applications_by_id = Dict(application.id => application for application in applications) + model_bundles_by_target = _compile_scene_model_bundles( + applications, + applications_by_id, + call_bindings_by_target, + ) return CompiledScene( scene, applications, @@ -1337,6 +1640,7 @@ function _compile_scene(scene::Scene, raw_specs) call_bindings, input_bindings_by_target, call_bindings_by_target, + model_bundles_by_target, application_order, scene.revision, ) @@ -1363,6 +1667,11 @@ function _compile_scene_applications(scene::Scene, raw_specs, timeline) app_id in ids && error("Duplicate compiled scene application id `$(app_id)`.") push!(ids, app_id) target_ids = resolve_object_ids(scene, selector) + spec = _scene_spec_with_meteo_hints( + scene, + spec, + _scene_application_hint_scale(scene, target_ids), + ) model_overrides = _compiled_object_model_overrides(spec, target_ids, app_id) push!( applications, @@ -1374,7 +1683,7 @@ function _compile_scene_applications(scene::Scene, raw_specs, timeline) target_ids, selector, timestep(spec), - _scene_application_clock(spec, timeline), + _scene_application_clock(scene, spec, target_ids, timeline), model_overrides, ), ) @@ -1382,6 +1691,51 @@ function _compile_scene_applications(scene::Scene, raw_specs, timeline) return applications end +function _scene_spec_with_meteo_hints(scene::Scene, spec, scale::Symbol) + hint = _normalize_meteo_hint(scale, process(spec), meteo_hint(model_(spec))) + + current_bindings = meteo_bindings(spec) + has_explicit_bindings = !(current_bindings isa NamedTuple && isempty(keys(current_bindings))) + new_bindings = has_explicit_bindings || isnothing(hint.bindings) ? current_bindings : hint.bindings + new_bindings = _scene_meteo_bindings_with_environment_sources(spec, new_bindings) + + current_window = meteo_window(spec) + new_window = isnothing(current_window) && !isnothing(hint.window) ? hint.window : current_window + + (new_bindings === current_bindings && new_window === current_window) && return spec + return ModelSpec(spec; meteo_bindings=new_bindings, meteo_window=new_window) +end + +function _scene_meteo_bindings_with_environment_sources(spec, bindings) + sources = _environment_source_overrides(spec) + isempty(keys(sources)) && return bindings + + bindings = bindings isa NamedTuple ? bindings : NamedTuple() + model_inputs = Set(Symbol.(keys(meteo_inputs_(spec)))) + unknown = Symbol[target for target in keys(sources) if !(Symbol(target) in model_inputs)] + isempty(unknown) || error( + "`Environment(; sources=...)` for process `$(process(spec))` contains ", + "unknown model-facing meteo input(s) `$(Tuple(unknown))`. Declared ", + "`meteo_inputs_` are `$(Tuple(sort!(collect(model_inputs); by=string)))`." + ) + + targets = Symbol[Symbol(target) for target in keys(bindings)] + for target in keys(sources) + target = Symbol(target) + target in targets || push!(targets, target) + end + + resolved = Pair{Symbol,Any}[] + for target in targets + rule = haskey(bindings, target) ? + _normalize_meteo_binding_rule(target, bindings[target]) : + (source=target, reducer=PlantMeteo.MeanWeighted()) + source = haskey(sources, target) ? Symbol(sources[target]) : rule.source + push!(resolved, target => (source=source, reducer=rule.reducer)) + end + return (; resolved...) +end + function _compiled_object_model_overrides(spec, target_ids, application_id::Symbol) model = model_(spec) model isa ObjectModelOverrides || return nothing @@ -1412,11 +1766,19 @@ function _scene_output_names(application::CompiledSceneApplication) return Symbol[Symbol(var) for var in keys(outputs_(application.spec))] end -function _scene_writer_groups(applications) +function _scene_canonical_output_names(application::CompiledSceneApplication) + return Symbol[ + variable for variable in _scene_output_names(application) + if _publish_mode_for_output(application.spec, variable) == :canonical + ] +end + +function _scene_writer_groups(applications, skipped_application_ids=Set{Symbol}()) groups = Dict{Tuple{ObjectId,Symbol},Vector{Tuple{Int,Any}}}() for (index, application) in pairs(applications) + application.id in skipped_application_ids && continue for object_id in application.target_ids - for variable in _scene_output_names(application) + for variable in _scene_canonical_output_names(application) push!(get!(groups, (object_id, variable), Tuple{Int,Any}[]), (index, application)) end end @@ -1474,8 +1836,17 @@ function _declares_update_without_previous_writer(spec, variable::Symbol, previo return false end -function _validate_scene_writers!(applications) - for ((object_id, variable), indexed_writers) in _scene_writer_groups(applications) +function _manual_call_application_ids(call_bindings) + ids = Set{Symbol}() + for binding in call_bindings + union!(ids, binding.callee_application_ids) + end + return ids +end + +function _validate_scene_writers!(applications, call_bindings=()) + manual_application_ids = _manual_call_application_ids(call_bindings) + for ((object_id, variable), indexed_writers) in _scene_writer_groups(applications, manual_application_ids) length(indexed_writers) <= 1 && continue sort!(indexed_writers; by=first) previous = CompiledSceneApplication[] @@ -1502,9 +1873,20 @@ function _validate_scene_writers!(applications) return nothing end -function _scene_application_clock(spec, timeline) - clock = _clock_from_spec_timestep(timestep(spec), timeline) - isnothing(clock) && return ClockSpec(1.0, 0.0) +function _scene_application_hint_scale(scene::Scene, target_ids::Vector{ObjectId}) + isempty(target_ids) && return :Scene + scales = unique!([_scene_object(scene, object_id).scale for object_id in target_ids]) + length(scales) == 1 && return only(scales) + return :Mixed +end + +function _scene_application_clock(scene::Scene, spec, target_ids::Vector{ObjectId}, timeline) + model = model_(spec) + source = _runtime_clock_source_for_spec(spec) + source == :meteo_base_step || return _model_clock(spec, model, timeline) + scale = _scene_application_hint_scale(scene, target_ids) + clock, hint_reason = _resolve_meteo_hint_clock(scale, process(spec), model, timeline) + isnothing(hint_reason) || error(hint_reason) return clock end @@ -1516,6 +1898,39 @@ function _selector_policy(selector::AbstractObjectMultiplicity) return _criteria_get(criteria(selector), :policy, HoldLast()) end +_selector_has_policy(selector::AbstractObjectMultiplicity) = + haskey(criteria(selector), :policy) + +function _scene_policy_from_source_application(applications_by_id, application_id::Symbol, source_var::Symbol) + application = get(applications_by_id, application_id, nothing) + isnothing(application) && error( + "No compiled scene application with id `$(application_id)` while resolving ", + "default output policy for `$(source_var)`." + ) + return _policy_for_output(_application_default_model(application), source_var) +end + +function _scene_selector_policy(selector::AbstractObjectMultiplicity, applications_by_id, source_application_ids, source_var::Symbol) + _selector_has_policy(selector) && return _selector_policy(selector) + isempty(source_application_ids) && return HoldLast() + length(source_application_ids) == 1 && return _scene_policy_from_source_application( + applications_by_id, + only(source_application_ids), + source_var, + ) + policies = [ + _scene_policy_from_source_application(applications_by_id, application_id, source_var) + for application_id in source_application_ids + ] + first_policy = first(policies) + all(policy -> policy == first_policy, policies) && return first_policy + error( + "Cannot infer default policy for scene input from `$(source_var)` because ", + "selector resolves several source applications `$(source_application_ids)` with ", + "different output policies. Add `policy=...` to `Inputs(...)`." + ) +end + function _selector_window(selector::AbstractObjectMultiplicity) return _criteria_get(criteria(selector), :window, nothing) end @@ -1576,6 +1991,78 @@ function _ref_vector_carrier(refs) return RefVector(typed_refs) end +function _status_with_reference(status::Status, variable::Symbol, reference::Base.RefValue) + names = propertynames(status) + if variable in names + references = ntuple( + index -> names[index] == variable ? reference : refvalue(status, names[index]), + length(names), + ) + return Status(NamedTuple{names}(references)) + end + extended_names = (names..., variable) + references = (ntuple(index -> refvalue(status, names[index]), length(names))..., reference) + return Status(NamedTuple{extended_names}(references)) +end + +function _status_with_default(status::Status, variable::Symbol, value) + variable in propertynames(status) && return status + return _status_with_reference(status, variable, Ref(value)) +end + +function _ensure_scene_object_status!(scene::Scene, object_id::ObjectId) + object = _scene_object(scene, object_id) + isnothing(object.status) && (object.status = Status()) + object.status isa Status || error( + "Scene object `$(object_id.value)` uses model applications but its status has type ", + "`$(typeof(object.status))`. Use `Status(...)` or leave status as `nothing`." + ) + return object.status +end + +function _prepare_scene_output_statuses!(scene::Scene, applications) + for application in applications + defaults = merge(outputs_(application.spec), meteo_outputs_(application.spec)) + for object_id in application.target_ids + status = _ensure_scene_object_status!(scene, object_id) + for (variable, value) in pairs(defaults) + status = _status_with_default(status, Symbol(variable), value) + end + _scene_object(scene, object_id).status = status + end + end + return scene +end + +function _prepare_scene_bound_input_statuses!(scene::Scene, applications, bindings) + applications_by_id = Dict(application.id => application for application in applications) + for binding in bindings + status = _ensure_scene_object_status!(scene, binding.consumer_id) + binding.input in propertynames(status) && continue + application = applications_by_id[binding.application_id] + defaults = inputs_(application.spec) + binding.input in keys(defaults) || error( + "Bound input `$(binding.input)` is not declared by application `$(binding.application_id)`." + ) + _scene_object(scene, binding.consumer_id).status = + _status_with_default(status, binding.input, getproperty(defaults, binding.input)) + end + return scene +end + +function _wire_scene_input_carriers!(scene::Scene, bindings) + for binding in bindings + binding.carrier_hint == :temporal_stream && continue + isnothing(binding.carrier) && continue + object = _scene_object(scene, binding.consumer_id) + status = object.status + status isa Status || continue + reference = binding.carrier isa Base.RefValue ? binding.carrier : Ref(binding.carrier) + object.status = _status_with_reference(status, binding.input, reference) + end + return scene +end + input_carrier(binding::CompiledSceneInputBinding) = binding.carrier has_reference_carrier(binding::CompiledSceneInputBinding) = !isnothing(binding.carrier) input_value(binding::CompiledSceneInputBinding) = _input_value(binding.carrier) @@ -1584,7 +2071,14 @@ _input_value(carrier::Base.RefValue) = carrier[] _input_value(carrier::RefVector) = carrier _input_value(carrier::ObjectRefVector) = carrier -function _matching_input_source_applications(applications_by_object, source_ids, source_var::Symbol, process_filter, application_filter) +function _matching_input_source_applications( + applications_by_object, + source_ids, + source_var::Symbol, + process_filter, + application_filter; + allow_empty::Bool=false, +) matches = Symbol[] for source_id in source_ids for application in get(applications_by_object, source_id, Any[]) @@ -1595,7 +2089,9 @@ function _matching_input_source_applications(applications_by_object, source_ids, end end unique!(matches) - if (!isnothing(process_filter) || !isnothing(application_filter)) && isempty(matches) + if !allow_empty && + (!isnothing(process_filter) || !isnothing(application_filter)) && + isempty(matches) error( "Input selector for source variable `$(source_var)` requested", isnothing(process_filter) ? "" : " process `$(process_filter)`", @@ -1609,6 +2105,7 @@ end function _compile_scene_input_bindings(scene::Scene, applications) bindings = CompiledSceneInputBinding[] by_object = _applications_by_object(applications) + by_id = Dict(application.id => application for application in applications) for application in applications for consumer_id in application.target_ids declared_inputs = value_inputs(application.spec) @@ -1626,11 +2123,12 @@ function _compile_scene_input_bindings(scene::Scene, applications) consumer_id, input_sym, selector, - :declared, + get(input_origins(application.spec), input_sym, :model_spec), by_object, + by_id, ) end - _append_inferred_scene_input_bindings!(bindings, scene, application, consumer_id, declared_inputs, by_object) + _append_inferred_scene_input_bindings!(bindings, scene, application, consumer_id, declared_inputs, by_object, by_id) end end return bindings @@ -1645,11 +2143,10 @@ function _push_scene_input_binding!( selector::AbstractObjectMultiplicity, origin::Symbol, applications_by_object, + applications_by_id, source_ids_override=nothing, ) source_ids = isnothing(source_ids_override) ? _dependency_object_ids(scene, selector, consumer_id) : source_ids_override - isempty(source_ids) && selector isa OptionalOne && return bindings - policy = _selector_policy(selector) window = _selector_window(selector) source_var = _selector_var(selector, input_sym) process_filter = _criteria_get(criteria(selector), :process, nothing) @@ -1660,9 +2157,33 @@ function _push_scene_input_binding!( source_var, process_filter, application_filter, + allow_empty=selector isa OptionalOne, ) + if selector isa OptionalOne && + (!isnothing(process_filter) || !isnothing(application_filter)) && + isempty(source_application_ids) + source_ids = ObjectId[] + end + policy = _scene_selector_policy(selector, applications_by_id, source_application_ids, source_var) + if policy isa PreviousTimeStep + policy.variable == input_sym || error( + "PreviousTimeStep marker for input `$(input_sym)` on application ", + "`$(application.id)` names `$(policy.variable)`. Use ", + "`PreviousTimeStep(:$(input_sym))`." + ) + else + _validate_policy_instance( + _scene_object(scene, consumer_id).scale, + application.process, + input_sym, + policy, + ) + end carrier = _input_carrier(scene, selector, source_ids, source_var) - carrier_hint = _carrier_hint(selector, policy, window) + carrier_hint = + isempty(source_ids) && selector isa OptionalOne ? + :optional_default : + _carrier_hint(selector, policy, window) _validate_scene_input_source!( scene, application, @@ -1738,7 +2259,7 @@ function _same_object_output_applications(applications_by_object, application::C matches = CompiledSceneApplication[] for candidate in get(applications_by_object, object_id, Any[]) candidate.id == application.id && continue - variable in _scene_output_names(candidate) || continue + variable in _scene_canonical_output_names(candidate) || continue push!(matches, candidate) end return matches @@ -1751,6 +2272,7 @@ function _append_inferred_scene_input_bindings!( consumer_id::ObjectId, declared_inputs, applications_by_object, + applications_by_id, ) declared_names = declared_inputs isa NamedTuple ? Set(Symbol.(keys(declared_inputs))) : Set{Symbol}() for input_sym in _scene_input_names(application) @@ -1775,6 +2297,7 @@ function _append_inferred_scene_input_bindings!( selector, :inferred_same_object, applications_by_object, + applications_by_id, ObjectId[consumer_id], ) end @@ -1874,7 +2397,7 @@ function _compile_scene_call_bindings(scene::Scene, applications) ) end unique!(callee_application_ids) - if isempty(callee_application_ids) + if isempty(callee_application_ids) && !(selector isa OptionalOne) error( "Call `$(call_sym)` on application `$(application.id)` matched objects ", "$([id.value for id in callee_object_ids]) but no model application", @@ -1899,6 +2422,7 @@ function _compile_scene_call_bindings(scene::Scene, applications) consumer_id, call_sym, selector, + get(call_origins(application.spec), call_sym, :model_spec), callee_object_ids, callee_application_ids, proc, @@ -1930,6 +2454,7 @@ end function _scene_input_order_edges!(children, input_bindings, call_owners) for binding in input_bindings + binding.policy isa PreviousTimeStep && continue for source_id in binding.source_application_ids owners = get(call_owners, source_id, nothing) if isnothing(owners) @@ -2063,6 +2588,7 @@ end function _scene_binding_carrier_kind(binding::CompiledSceneInputBinding) binding.carrier_hint == :temporal_stream && return :temporal_stream + binding.carrier_hint == :optional_default && return :optional_default carrier = binding.carrier isnothing(carrier) && return :unresolved carrier isa Base.RefValue && return :ref @@ -2075,6 +2601,7 @@ function _scene_binding_copy_semantics(binding::CompiledSceneInputBinding) kind = _scene_binding_carrier_kind(binding) kind in (:ref, :ref_vector, :object_ref_vector) && return :live_references kind == :temporal_stream && return :materialized_temporal_value + kind == :optional_default && return :consumer_default kind == :unresolved && return :not_materialized return :backend_defined end @@ -2111,19 +2638,39 @@ function explain_calls(compiled::CompiledScene) application_id=binding.application_id, consumer_id=binding.consumer_id.value, call=binding.call, + origin=binding.origin, callee_object_ids=[id.value for id in binding.callee_object_ids], callee_application_ids=binding.callee_application_ids, process=binding.process, application=binding.application, multiplicity=binding.multiplicity, + publication_policy=:explicit_accept, + default_publish=false, + accepted_publish=true, + resolved=!isempty(binding.callee_application_ids), selector=binding.selector, ) for binding in compiled.call_bindings ] end +function explain_model_bundles(compiled::CompiledScene) + return [ + ( + application_id=application_id, + object_id=object_id.value, + processes=collect(keys(models)), + model_types=[typeof(model) for model in values(models)], + ) + for ((application_id, object_id), models) in sort!( + collect(compiled.model_bundles_by_target); + by=pair -> (string(first(pair)[1]), string(first(pair)[2].value)), + ) + ] +end + function explain_writers(compiled::CompiledScene) - groups = _scene_writer_groups(compiled.applications) + groups = _scene_writer_groups(compiled.applications, _manual_call_application_ids(compiled)) rows = NamedTuple[] for ((object_id, variable), indexed_writers) in groups sort!(indexed_writers; by=first) @@ -2180,6 +2727,31 @@ end bind_environment(backend, object::Object, support, config=nothing) = :global +function _environment_binding_object(scene::Scene, object::Object) + !isnothing(geometry(object)) && return object, object.id, :self + ancestor_id = object.parent + while !isnothing(ancestor_id) + ancestor = _scene_object(scene, ancestor_id) + if !isnothing(geometry(ancestor)) + proxy = Object( + object.id; + scale=object.scale, + kind=object.kind, + species=object.species, + name=object.name, + parent=object.parent, + children=object.children, + geometry=geometry(ancestor), + status=object.status, + applications=object.applications, + ) + return proxy, ancestor.id, :ancestor + end + ancestor_id = ancestor.parent + end + return object, nothing, :global +end + function _scene_environment_entities(scene::Scene) return [ ( @@ -2225,18 +2797,30 @@ function _environment_variable_names(vars) return Symbol[Symbol(var) for var in keys(vars)] end -function _compile_environment_bindings(scene::Scene, compiled::CompiledScene) +function _environment_source_variable_names(model_spec) + return Symbol[Symbol(source) for (_, source) in _environment_sampling_rules(model_spec)] +end + +function _compile_environment_bindings_for_applications(scene::Scene, applications) bindings = CompiledEnvironmentBinding[] - for application in compiled.applications + for application in applications config = environment_config(application.spec) backend = _environment_backend_from_config(scene, config) provider = _environment_provider_from_config(config, backend) required_inputs = _environment_variable_names(meteo_inputs_(application.spec)) + source_inputs = _environment_source_variable_names(application.spec) produced_outputs = _environment_variable_names(meteo_outputs_(application.spec)) for object_id in application.target_ids object = _scene_object(scene, object_id) support = _object_environment_support(application, object) - cell = bind_environment(backend, object, support, _environment_config_payload(config)) + binding_object, geometry_source_object_id, geometry_source = + _environment_binding_object(scene, object) + cell = bind_environment( + backend, + binding_object, + support, + _environment_config_payload(config), + ) push!( bindings, CompiledEnvironmentBinding( @@ -2246,8 +2830,11 @@ function _compile_environment_bindings(scene::Scene, compiled::CompiledScene) backend, cell, required_inputs, + source_inputs, produced_outputs, support, + geometry_source_object_id, + geometry_source, config, ), ) @@ -2256,9 +2843,95 @@ function _compile_environment_bindings(scene::Scene, compiled::CompiledScene) return bindings end +_compile_environment_bindings(scene::Scene, compiled::CompiledScene) = + _compile_environment_bindings_for_applications(scene, compiled.applications) + +function _validate_scene_environment_inputs!(bindings, applications_by_id) + missing_rows = NamedTuple[] + for binding in bindings + available = environment_variables(binding.backend) + isnothing(available) && continue + application = applications_by_id[binding.application_id] + for (target, source) in _environment_sampling_rules(application.spec) + source in available && continue + push!( + missing_rows, + ( + application_id=binding.application_id, + object_id=binding.object_id.value, + process=application.process, + target=target, + source=source, + available=Tuple(sort!(collect(available); by=string)), + ), + ) + end + end + isempty(missing_rows) && return nothing + details = join( + [ + string( + row.application_id, + "/", + row.object_id, + " (", + row.process, + ") needs `", + row.target, + "` from source `", + row.source, + "`; available=", + row.available, + ) + for row in missing_rows + ], + "; ", + ) + error("Scene environment is missing required meteo inputs: ", details) +end + +function _scene_environment_samplers(bindings) + samplers_by_application = Dict{Symbol,Any}() + prepared_sources = Tuple{Any,Any}[] + for binding in bindings + haskey(samplers_by_application, binding.application_id) && continue + binding.backend isa GlobalConstant || continue + meteo = environment_meteo(binding.backend) + source_index = findfirst(entry -> entry[1] === meteo, prepared_sources) + sampler = if isnothing(source_index) + prepared = _prepare_meteo_sampler(meteo) + push!(prepared_sources, (meteo, prepared)) + prepared + else + prepared_sources[source_index][2] + end + samplers_by_application[binding.application_id] = sampler + end + return samplers_by_application +end + +function _compiled_environment_bindings( + scene::Scene, + bindings, + by_target, + samplers_by_application=_scene_environment_samplers(bindings), + sample_cache=Dict{Tuple{Symbol,Int},Any}(), +) + return CompiledEnvironmentBindings( + scene, + bindings, + by_target, + samplers_by_application, + sample_cache, + scene.revision, + scene.environment_revision, + ) +end + function compile_environment_bindings(scene::Scene, compiled::CompiledScene=refresh_bindings!(scene)) _update_scene_environment_indices!(scene, compiled) bindings = _compile_environment_bindings(scene, compiled) + _validate_scene_environment_inputs!(bindings, compiled.applications_by_id) by_target = Dict( (binding.application_id, binding.object_id) => binding for binding in bindings @@ -2266,25 +2939,153 @@ function compile_environment_bindings(scene::Scene, compiled::CompiledScene=refr length(by_target) == length(bindings) || error( "Environment binding compilation produced duplicate `(application_id, object_id)` targets." ) - return CompiledEnvironmentBindings( - scene, - bindings, - by_target, - scene.revision, - scene.environment_revision, - ) + return _compiled_environment_bindings(scene, bindings, by_target) end -function explain_environment_bindings(compiled::CompiledEnvironmentBindings) - return [ - ( - application_id=binding.application_id, - object_id=binding.object_id.value, - provider=binding.provider, - backend_type=isnothing(binding.backend) ? nothing : typeof(binding.backend), - cell=binding.cell, +function _same_environment_backend(a, b) + a === b && return true + if a isa GlobalConstant && b isa GlobalConstant + return environment_meteo(a) === environment_meteo(b) + end + return false +end + +function _same_environment_support(a, b) + return a.domain == b.domain && + a.scale == b.scale && + a.process == b.process && + a.status === b.status +end + +function _reconcile_environment_binding_metadata( + scene::Scene, + compiled::CompiledScene, + cached::CompiledEnvironmentBindings, +) + expected_count = sum(length(application.target_ids) for application in compiled.applications) + expected_count == length(cached.bindings) || return nothing + + bindings = CompiledEnvironmentBinding[] + changed = false + for application in compiled.applications + config = environment_config(application.spec) + backend = _environment_backend_from_config(scene, config) + provider = _environment_provider_from_config(config, backend) + required_inputs = _environment_variable_names(meteo_inputs_(application.spec)) + source_inputs = _environment_source_variable_names(application.spec) + produced_outputs = _environment_variable_names(meteo_outputs_(application.spec)) + for object_id in application.target_ids + key = (application.id, object_id) + old = get(cached.by_target, key, nothing) + isnothing(old) && return nothing + object = _scene_object(scene, object_id) + support = _object_environment_support(application, object) + _, geometry_source_object_id, geometry_source = + _environment_binding_object(scene, object) + _same_environment_backend(old.backend, backend) || return nothing + old.provider == provider || return nothing + isequal(old.config, config) || return nothing + old.geometry_source_object_id == geometry_source_object_id || + return nothing + old.geometry_source == geometry_source || return nothing + _same_environment_support(old.support, support) || return nothing + + if old.required_inputs == required_inputs && + old.source_inputs == source_inputs && + old.produced_outputs == produced_outputs + push!(bindings, old) + else + changed = true + push!( + bindings, + CompiledEnvironmentBinding( + application.id, + object_id, + provider, + backend, + old.cell, + required_inputs, + source_inputs, + produced_outputs, + support, + geometry_source_object_id, + geometry_source, + config, + ), + ) + end + end + end + changed || return cached + _validate_scene_environment_inputs!(bindings, compiled.applications_by_id) + by_target = Dict( + (binding.application_id, binding.object_id) => binding + for binding in bindings + ) + return _compiled_environment_bindings(scene, bindings, by_target) +end + +function _refresh_environment_bindings_for_objects( + scene::Scene, + compiled::CompiledScene, + cached::CompiledEnvironmentBindings, + dirty_object_ids, +) + _update_scene_environment_indices!(scene, compiled) + dirty = Set(dirty_object_ids) + replacements = Dict{Tuple{Symbol,ObjectId},CompiledEnvironmentBinding}() + for application in compiled.applications + selected_ids = ObjectId[id for id in application.target_ids if id in dirty] + isempty(selected_ids) && continue + partial_application = CompiledSceneApplication( + application.id, + application.spec, + application.process, + application.name, + selected_ids, + application.applies_to, + application.timestep, + application.clock, + application.model_overrides, + ) + for binding in _compile_environment_bindings_for_applications(scene, (partial_application,)) + replacements[(binding.application_id, binding.object_id)] = binding + end + end + bindings = CompiledEnvironmentBinding[ + get(replacements, (binding.application_id, binding.object_id), binding) + for binding in cached.bindings + ] + _validate_scene_environment_inputs!(bindings, compiled.applications_by_id) + by_target = Dict( + (binding.application_id, binding.object_id) => binding for binding in bindings + ) + return _compiled_environment_bindings( + scene, + bindings, + by_target, + cached.samplers_by_application, + cached.sample_cache, + ) +end + +function explain_environment_bindings(compiled::CompiledEnvironmentBindings) + return [ + ( + application_id=binding.application_id, + object_id=binding.object_id.value, + provider=binding.provider, + backend_type=isnothing(binding.backend) ? nothing : typeof(binding.backend), + cell=binding.cell, required_inputs=binding.required_inputs, + source_inputs=binding.source_inputs, produced_outputs=binding.produced_outputs, + temporal_sampler=!isnothing( + get(compiled.samplers_by_application, binding.application_id, nothing), + ), + geometry_source_object_id=isnothing(binding.geometry_source_object_id) ? + nothing : binding.geometry_source_object_id.value, + geometry_source=binding.geometry_source, support=binding.support, config=binding.config, ) @@ -2296,17 +3097,25 @@ function explain_environment_bindings(scene::Scene) return explain_environment_bindings(refresh_environment_bindings!(scene)) end -struct SceneRunContext{CS,EB,A,TS,C} +struct SceneOutputRetentionPlan + retain_all::Bool + temporal_dependencies::Set{Tuple{Symbol,Symbol}} + requested_outputs::Set{Tuple{Symbol,Symbol}} + dependency_horizons::Dict{Tuple{Symbol,Symbol},Float64} +end + +struct SceneRunContext{CS,EB,A,TS,OR,C} compiled::CS environment_bindings::EB application::A object_id::ObjectId temporal_streams::TS + output_retention::OR time::Float64 constants::C end -struct SceneCallTarget{CS,EB,A,S,TS,C} +struct SceneCallTarget{CS,EB,A,S,TS,OR,C} compiled::CS environment_bindings::EB application::A @@ -2314,10 +3123,45 @@ struct SceneCallTarget{CS,EB,A,S,TS,C} model status::S temporal_streams::TS + output_retention::OR time::Float64 constants::C end +abstract type AbstractSceneExecutionBatch end + +struct CompiledSceneExecutionTarget{M,S,MB,IB,EB} + object_id::ObjectId + model::M + status::S + models::MB + input_bindings::IB + environment_binding::EB +end + +struct CompiledSceneExecutionBatch{A,T<:AbstractVector} <: AbstractSceneExecutionBatch + application::A + targets::T +end + +struct CompiledSceneExecutionPlan{B} + batches::B + scene_revision::Int + environment_revision::Int +end + +struct SceneSimulation{S,CS,EB,EP,OR,TS,R} + scene::S + compiled::CS + environment_bindings::EB + execution_plan::EP + output_retention::OR + temporal_streams::TS + output_requests::R +end + +scene_outputs(sim::SceneSimulation) = sim.temporal_streams + function _compiled_application_by_id(compiled::CompiledScene, id::Symbol) application = get(compiled.applications_by_id, id, nothing) isnothing(application) && error("No compiled scene application with id `$(id)`.") @@ -2344,20 +3188,84 @@ function _set_status_if_present!(status::Status, variable::Symbol, value) return status end -_scene_stream_key(object_id::ObjectId, variable::Symbol) = (object_id, variable) +_scene_stream_key(application_id::Symbol, object_id::ObjectId, variable::Symbol) = + (application_id, object_id, variable) -function _scene_publish_outputs!(streams, application::CompiledSceneApplication, object_id::ObjectId, status, time::Real) +function _scene_retain_output( + retention::SceneOutputRetentionPlan, + application_id::Symbol, + variable::Symbol, +) + retention.retain_all && return true + key = (application_id, variable) + return key in retention.temporal_dependencies || + key in retention.requested_outputs +end + +_scene_retain_output(::Nothing, application_id::Symbol, variable::Symbol) = true + +function _scene_prune_dependency_stream!( + samples, + retention::SceneOutputRetentionPlan, + application_id::Symbol, + variable::Symbol, + time::Real, +) + retention.retain_all && return samples + key = (application_id, variable) + key in retention.requested_outputs && return samples + key in retention.temporal_dependencies || return samples + horizon = get(retention.dependency_horizons, key, 0.0) + if horizon <= 0.0 + length(samples) > 1 && deleteat!(samples, 1:(length(samples) - 1)) + return samples + end + cutoff = float(time) - horizon + 1.0 + filter!(sample -> sample[1] >= cutoff - 1.0e-8, samples) + return samples +end + +_scene_prune_dependency_stream!(samples, ::Nothing, application_id, variable, time) = + samples + +function _scene_publish_outputs!( + streams, + application::CompiledSceneApplication, + object_id::ObjectId, + status, + time::Real, + retention=nothing, +) isnothing(streams) && return nothing for variable in keys(outputs_(application.spec)) var = Symbol(variable) + _scene_retain_output(retention, application.id, var) || continue hasproperty(status, var) || error( "Application `$(application.id)` declares output `$(var)`, but object ", "`$(object_id.value)` status has no such variable." ) - key = _scene_stream_key(object_id, var) - samples = get!(streams, key, Tuple{Float64,Any}[]) + key = _scene_stream_key(application.id, object_id, var) + value = getproperty(status, var) + samples = get(streams, key, nothing) + if isnothing(samples) + samples = Tuple{Float64,typeof(value)}[] + streams[key] = samples + elseif !(value isa fieldtype(eltype(samples), 2)) + error( + "Output `$(application.id).$(var)` on object `$(object_id.value)` changed ", + "value type from `$(fieldtype(eltype(samples), 2))` to `$(typeof(value))`. ", + "Scene temporal streams require a stable output type." + ) + end filter!(sample -> !isapprox(sample[1], float(time); atol=1.0e-8, rtol=0.0), samples) - push!(samples, (float(time), getproperty(status, var))) + push!(samples, (float(time), value)) + _scene_prune_dependency_stream!( + samples, + retention, + application.id, + var, + time, + ) end return nothing end @@ -2374,8 +3282,54 @@ function _scene_latest_sample(samples, time::Real) return latest end +function _scene_linear_value(v_left, v_right, α) + applicable(-, v_right, v_left) || return nothing + delta = v_right - v_left + increment = if applicable(*, α, delta) + α * delta + elseif applicable(*, delta, α) + delta * α + else + return nothing + end + applicable(+, v_left, increment) || return nothing + return v_left + increment +end + +function _scene_interpolated_sample(samples, time::Real, policy::Interpolate) + isempty(samples) && return nothing + t = float(time) + prev_idx = findlast(sample -> sample[1] <= t + 1.0e-8, samples) + next_idx = findfirst(sample -> sample[1] >= t - 1.0e-8, samples) + + if !isnothing(prev_idx) && !isnothing(next_idx) + t_prev, v_prev = samples[prev_idx] + t_next, v_next = samples[next_idx] + isapprox(t_prev, t_next; atol=1.0e-8, rtol=0.0) && return v_prev + policy.mode == :hold && return v_prev + α = (t - t_prev) / (t_next - t_prev) + interpolated = _scene_linear_value(v_prev, v_next, α) + return isnothing(interpolated) ? v_prev : interpolated + end + + if !isnothing(prev_idx) + t_last, v_last = samples[prev_idx] + if policy.extrapolation == :linear && prev_idx >= 2 + t_prev, v_prev = samples[prev_idx - 1] + if !isapprox(t_last, t_prev; atol=1.0e-8, rtol=0.0) + α = (t - t_last) / (t_last - t_prev) + extrapolated = _scene_linear_value(v_prev, v_last, one(α) + α) + !isnothing(extrapolated) && return extrapolated + end + end + return v_last + end + + return first(samples)[2] +end + function _scene_window_samples(samples, t_start::Real, t_end::Real) - return Any[value for (sample_t, value) in samples if float(t_start) <= sample_t <= float(t_end)] + return [value for (sample_t, value) in samples if float(t_start) <= sample_t <= float(t_end)] end function _scene_window_reduce(values, durations, policy) @@ -2417,32 +3371,102 @@ function _scene_input_window_steps(binding::CompiledSceneInputBinding, applicati return 1.0 end -function _scene_temporal_source_value(streams, source_id::ObjectId, source_var::Symbol, time::Real, policy, t_start::Real, timeline) - samples = get(streams, _scene_stream_key(source_id, source_var), nothing) +function _scene_temporal_source_value( + streams, + application_id::Symbol, + source_id::ObjectId, + source_var::Symbol, + time::Real, + policy, + t_start::Real, + timeline, +) + samples = get(streams, _scene_stream_key(application_id, source_id, source_var), nothing) isnothing(samples) && return policy isa Union{Integrate,Aggregate} ? 0.0 : nothing if policy isa HoldLast return _scene_latest_sample(samples, time) + elseif policy isa PreviousTimeStep + return _scene_latest_sample(samples, float(time) - 1.0) elseif policy isa Union{Integrate,Aggregate} values = _scene_window_samples(samples, t_start, time) durations = fill(timeline.base_step_seconds, length(values)) return _scene_window_reduce(values, durations, policy) elseif policy isa Interpolate - return _scene_latest_sample(samples, time) + return _scene_interpolated_sample(samples, time, policy) end error("Unsupported scene temporal input policy `$(typeof(policy))`.") end -function _scene_temporal_input_value(binding::CompiledSceneInputBinding, application::CompiledSceneApplication, streams, time::Real, timeline) +function _scene_temporal_source_application(compiled::CompiledScene, binding::CompiledSceneInputBinding, source_id::ObjectId) + isempty(binding.source_application_ids) && error( + "Temporal scene input `$(binding.input)` from `$(source_id.value).$(binding.source_var)` ", + "has no resolved source application. Add `process=` or `application=` to `Inputs(...)`." + ) + matches = Symbol[ + application_id for application_id in binding.source_application_ids + if source_id in _compiled_application_by_id(compiled, application_id).target_ids + ] + if length(matches) == 1 + return only(matches) + elseif isempty(matches) + error( + "Temporal scene input `$(binding.input)` from `$(source_id.value).$(binding.source_var)` ", + "has no source application matching object `$(source_id.value)`." + ) + end + error( + "Temporal scene input `$(binding.input)` from `$(source_id.value).$(binding.source_var)` ", + "has ambiguous source applications `$(matches)`. Add `application=...` to `Inputs(...)`." + ) +end + +function _scene_temporal_input_value( + compiled::CompiledScene, + binding::CompiledSceneInputBinding, + application::CompiledSceneApplication, + status::Status, + streams, + time::Real, + timeline, +) window_steps = _scene_input_window_steps(binding, application, timeline) t_start = float(time) - float(window_steps) + 1.0 + initial = status[binding.input] if binding.multiplicity == :many - return [ - _scene_temporal_source_value(streams, source_id, binding.source_var, time, binding.policy, t_start, timeline) + values = [ + _scene_temporal_source_value( + streams, + _scene_temporal_source_application(compiled, binding, source_id), + source_id, + binding.source_var, + time, + binding.policy, + t_start, + timeline, + ) for source_id in binding.source_ids ] + if binding.policy isa PreviousTimeStep + initial_values = initial isa AbstractVector ? initial : () + return [ + isnothing(value) && index <= length(initial_values) ? initial_values[index] : value + for (index, value) in pairs(values) + ] + end + return values end source_id = only(binding.source_ids) - value = _scene_temporal_source_value(streams, source_id, binding.source_var, time, binding.policy, t_start, timeline) + value = _scene_temporal_source_value( + streams, + _scene_temporal_source_application(compiled, binding, source_id), + source_id, + binding.source_var, + time, + binding.policy, + t_start, + timeline, + ) + isnothing(value) && binding.policy isa PreviousTimeStep && return initial isnothing(value) && error( "No temporal scene value available for input `$(binding.input)` from ", "`$(source_id.value).$(binding.source_var)` at t=$(time)." @@ -2450,6 +3474,26 @@ function _scene_temporal_input_value(binding::CompiledSceneInputBinding, applica return value end +function _scene_temporal_input_value( + compiled::CompiledScene, + binding::CompiledSceneInputBinding, + application::CompiledSceneApplication, + streams, + time::Real, + timeline, +) + status = _scene_object_status(compiled.scene, binding.consumer_id) + return _scene_temporal_input_value( + compiled, + binding, + application, + status, + streams, + time, + timeline, + ) +end + function _scene_assign_input_value!(status::Status, variable::Symbol, value) variable in propertynames(status) || error( "Cannot materialize input `$(variable)` because consumer status has no such variable." @@ -2474,15 +3518,50 @@ function _materialize_scene_inputs!( time::Real=1, ) status = _scene_object_status(compiled.scene, object_id) - timeline = _scene_timeline(compiled.scene) bindings = get(compiled.input_bindings_by_target, (application.id, object_id), ()) + timeline = nothing for binding in bindings if binding.carrier_hint == :temporal_stream isnothing(streams) && continue - value = _scene_temporal_input_value(binding, application, streams, time, timeline) + isnothing(timeline) && (timeline = _scene_timeline(compiled.scene)) + value = _scene_temporal_input_value( + compiled, + binding, + application, + status, + streams, + time, + timeline, + ) + _scene_assign_input_value!(status, binding.input, value) + end + end + return status +end + +function _materialize_scene_inputs!( + status::Status, + bindings::Tuple, + compiled::CompiledScene, + application::CompiledSceneApplication, + streams=nothing, + time::Real=1, +) + timeline = nothing + for binding in bindings + if binding.carrier_hint == :temporal_stream + isnothing(streams) && continue + isnothing(timeline) && (timeline = _scene_timeline(compiled.scene)) + value = _scene_temporal_input_value( + compiled, + binding, + application, + status, + streams, + time, + timeline, + ) _scene_assign_input_value!(status, binding.input, value) - elseif has_reference_carrier(binding) - _set_status_if_present!(status, binding.input, input_value(binding)) end end return status @@ -2495,8 +3574,37 @@ function _scene_meteo_for_model( time::Real, ) binding = _environment_binding_for(env_bindings, application.id, object_id) + return _scene_meteo_for_binding(env_bindings, application, binding, time) +end + +function _scene_meteo_for_binding( + env_bindings::CompiledEnvironmentBindings, + application::CompiledSceneApplication, + binding, + time::Real, +) isnothing(binding) && return nothing isnothing(binding.backend) && return nothing + if binding.backend isa GlobalConstant + sampler = get(env_bindings.samplers_by_application, application.id, nothing) + if !isnothing(sampler) + step = Int(round(time)) + key = (application.id, step) + haskey(env_bindings.sample_cache, key) && + return env_bindings.sample_cache[key] + meteo = environment_meteo(binding.backend) + raw_row = _meteo_row_at_step(meteo, step) + sampled = _sample_meteo_for_model( + sampler, + raw_row, + step, + application.clock, + application.spec, + ) + env_bindings.sample_cache[key] = sampled + return sampled + end + end return sample_environment(binding.backend, binding.support, time, application.spec) end @@ -2509,11 +3617,129 @@ function _scatter_scene_environment_outputs!( ) isempty(keys(meteo_outputs_(application.spec))) && return nothing binding = _environment_binding_for(env_bindings, application.id, object_id) + return _scatter_scene_environment_outputs!( + application, + binding, + status, + time, + ) +end + +function _scatter_scene_environment_outputs!( + application::CompiledSceneApplication, + binding, + status, + time::Real, +) + isempty(keys(meteo_outputs_(application.spec))) && return nothing isnothing(binding) && return nothing isnothing(binding.backend) && return nothing return scatter_environment_outputs!(binding.backend, binding.support, time, application.spec, status) end +function _push_scene_model_entry!(pairs, names::Set{Symbol}, name::Symbol, model) + name in names && return nothing + push!(pairs, name => model) + push!(names, name) + return nothing +end + +function _append_scene_model_dependencies!( + pairs, + names::Set{Symbol}, + applications_by_id, + call_bindings_by_target, + application::CompiledSceneApplication, + object_id::ObjectId, + seen::Set{Tuple{Symbol,ObjectId}}, +) + key = (application.id, object_id) + key in seen && return nothing + push!(seen, key) + _push_scene_model_entry!( + pairs, + names, + application.process, + _application_model(application, object_id), + ) + bindings = get(call_bindings_by_target, key, ()) + for binding in bindings + for application_id in binding.callee_application_ids + callee_application = get(applications_by_id, application_id, nothing) + isnothing(callee_application) && error( + "No compiled scene application with id `$(application_id)`." + ) + matching_object_ids = ObjectId[ + callee_object_id for callee_object_id in binding.callee_object_ids + if callee_object_id in callee_application.target_ids + ] + # Old-style hard-dependency kernels expect one model object per + # process field. Multi-object scene calls are exposed through + # `call_targets(extra, name)` instead. + length(matching_object_ids) == 1 || continue + _append_scene_model_dependencies!( + pairs, + names, + applications_by_id, + call_bindings_by_target, + callee_application, + only(matching_object_ids), + seen, + ) + end + end + return nothing +end + +function _compile_scene_model_bundle( + applications_by_id, + call_bindings_by_target, + application::CompiledSceneApplication, + object_id::ObjectId, +) + pairs = Pair{Symbol,Any}[] + names = Set{Symbol}() + seen = Set{Tuple{Symbol,ObjectId}}() + _append_scene_model_dependencies!( + pairs, + names, + applications_by_id, + call_bindings_by_target, + application, + object_id, + seen, + ) + return NamedTuple{Tuple(first(pair) for pair in pairs)}(Tuple(last(pair) for pair in pairs)) +end + +function _compile_scene_model_bundles(applications, applications_by_id, call_bindings_by_target) + bundles = Dict{Tuple{Symbol,ObjectId},Any}() + for application in applications + for object_id in application.target_ids + bundles[(application.id, object_id)] = _compile_scene_model_bundle( + applications_by_id, + call_bindings_by_target, + application, + object_id, + ) + end + end + return bundles +end + +function _scene_models_for_application( + compiled::CompiledScene, + application::CompiledSceneApplication, + object_id::ObjectId, +) + key = (application.id, object_id) + models = get(compiled.model_bundles_by_target, key, nothing) + isnothing(models) && error( + "No compiled model bundle for application `$(application.id)` on object `$(object_id.value)`." + ) + return models +end + function _run_scene_application!( compiled::CompiledScene, env_bindings::CompiledEnvironmentBindings, @@ -2522,20 +3748,116 @@ function _run_scene_application!( time::Real=1, constants=nothing, temporal_streams=nothing, + output_retention=nothing, publish::Bool=true, + meteo=nothing, ) status = _materialize_scene_inputs!(compiled, application, object_id, temporal_streams, time) - meteo = _scene_meteo_for_model(env_bindings, application, object_id, time) - context = SceneRunContext(compiled, env_bindings, application, object_id, temporal_streams, float(time), constants) + meteo_value = isnothing(meteo) ? _scene_meteo_for_model(env_bindings, application, object_id, time) : meteo + context = SceneRunContext( + compiled, + env_bindings, + application, + object_id, + temporal_streams, + output_retention, + float(time), + constants, + ) model = _application_model(application, object_id) - run!(model, nothing, status, meteo, constants, context) + models = _scene_models_for_application(compiled, application, object_id) + run!(model, models, status, meteo_value, constants, context) if publish _scatter_scene_environment_outputs!(env_bindings, application, object_id, status, time) - _scene_publish_outputs!(temporal_streams, application, object_id, status, time) + _scene_publish_outputs!( + temporal_streams, + application, + object_id, + status, + time, + output_retention, + ) end return status end +function _run_scene_execution_target!( + compiled::CompiledScene, + env_bindings::CompiledEnvironmentBindings, + application::CompiledSceneApplication, + target::CompiledSceneExecutionTarget; + time::Real=1, + constants=nothing, + temporal_streams=nothing, + output_retention=nothing, +) + status = _materialize_scene_inputs!( + target.status, + target.input_bindings, + compiled, + application, + temporal_streams, + time, + ) + meteo = _scene_meteo_for_binding( + env_bindings, + application, + target.environment_binding, + time, + ) + context = SceneRunContext( + compiled, + env_bindings, + application, + target.object_id, + temporal_streams, + output_retention, + float(time), + constants, + ) + run!(target.model, target.models, status, meteo, constants, context) + _scatter_scene_environment_outputs!( + application, + target.environment_binding, + status, + time, + ) + _scene_publish_outputs!( + temporal_streams, + application, + target.object_id, + status, + time, + output_retention, + ) + return status +end + +function _run_scene_execution_batch!( + batch::CompiledSceneExecutionBatch, + compiled::CompiledScene, + env_bindings::CompiledEnvironmentBindings; + time::Real=1, + constants=nothing, + temporal_streams=nothing, + output_retention=nothing, +) + _scene_application_should_run(batch.application, time) || return nothing + for target in batch.targets + _run_scene_execution_target!( + compiled, + env_bindings, + batch.application, + target; + time=time, + constants=constants, + temporal_streams=temporal_streams, + output_retention=output_retention, + ) + end + return nothing +end + _scene_application_should_run(application::CompiledSceneApplication, t::Real) = _should_run_at_time(application.clock, float(t)) @@ -2547,6 +3869,236 @@ function _manual_call_application_ids(compiled::CompiledScene) return ids end +function _compiled_scene_execution_target( + compiled::CompiledScene, + env_bindings::CompiledEnvironmentBindings, + application::CompiledSceneApplication, + object_id::ObjectId, +) + status = _scene_object_status(compiled.scene, object_id) + model = _application_model(application, object_id) + models = _scene_models_for_application(compiled, application, object_id) + input_bindings = get( + compiled.input_bindings_by_target, + (application.id, object_id), + (), + ) + environment_binding = _environment_binding_for( + env_bindings, + application.id, + object_id, + ) + return CompiledSceneExecutionTarget( + object_id, + model, + status, + models, + input_bindings, + environment_binding, + ) +end + +function _typed_scene_execution_targets(targets, first_index::Int, last_index::Int) + target_type = typeof(targets[first_index]) + typed = Vector{target_type}(undef, last_index - first_index + 1) + for (destination, source) in enumerate(first_index:last_index) + typed[destination] = targets[source] + end + return typed +end + +function _append_scene_execution_batches!( + batches, + application::CompiledSceneApplication, + targets, +) + isempty(targets) && return batches + first_index = firstindex(targets) + target_type = typeof(targets[first_index]) + for index in (first_index + 1):lastindex(targets) + typeof(targets[index]) == target_type && continue + push!( + batches, + CompiledSceneExecutionBatch( + application, + _typed_scene_execution_targets(targets, first_index, index - 1), + ), + ) + first_index = index + target_type = typeof(targets[index]) + end + push!( + batches, + CompiledSceneExecutionBatch( + application, + _typed_scene_execution_targets(targets, first_index, lastindex(targets)), + ), + ) + return batches +end + +function compile_scene_execution_plan( + compiled::CompiledScene, + env_bindings::CompiledEnvironmentBindings, +) + manual_application_ids = _manual_call_application_ids(compiled) + batches = AbstractSceneExecutionBatch[] + for application in _ordered_scene_applications(compiled) + application.id in manual_application_ids && continue + targets = Any[ + _compiled_scene_execution_target( + compiled, + env_bindings, + application, + object_id, + ) + for object_id in application.target_ids + ] + _append_scene_execution_batches!(batches, application, targets) + end + return CompiledSceneExecutionPlan( + batches, + compiled.revision, + env_bindings.environment_revision, + ) +end + +function explain_execution_plan(plan::CompiledSceneExecutionPlan) + return [ + ( + batch_index=index, + application_id=batch.application.id, + process=batch.application.process, + object_ids=[target.object_id.value for target in batch.targets], + batch_size=length(batch.targets), + target_type=eltype(batch.targets), + model_type=fieldtype(eltype(batch.targets), :model), + status_type=fieldtype(eltype(batch.targets), :status), + model_bundle_type=fieldtype(eltype(batch.targets), :models), + input_bindings_type=fieldtype(eltype(batch.targets), :input_bindings), + environment_binding_type=fieldtype( + eltype(batch.targets), + :environment_binding, + ), + inner_loop_dispatch=:concrete_homogeneous_batch, + ) + for (index, batch) in pairs(plan.batches) + ] +end + +function explain_execution_plan(sim::SceneSimulation) + return explain_execution_plan(sim.execution_plan) +end + +function explain_execution_plan(scene::Scene) + compiled = refresh_bindings!(scene) + environment_bindings = refresh_environment_bindings!(scene, compiled) + return explain_execution_plan( + compile_scene_execution_plan(compiled, environment_bindings), + ) +end + +function compile_scene_output_retention( + compiled::CompiledScene, + output_requests; + retain_all::Bool=false, +) + temporal_dependencies = Set{Tuple{Symbol,Symbol}}() + dependency_horizons = Dict{Tuple{Symbol,Symbol},Float64}() + timeline = _scene_timeline(compiled.scene) + for binding in compiled.input_bindings + binding.carrier_hint == :temporal_stream || continue + consumer = _compiled_application_by_id(compiled, binding.application_id) + window_steps = _scene_input_window_steps(binding, consumer, timeline) + for application_id in binding.source_application_ids + source = _compiled_application_by_id(compiled, application_id) + required = if binding.policy isa Union{Integrate,Aggregate} + float(window_steps) + elseif binding.policy isa Union{Interpolate,PreviousTimeStep} + max(2.0, float(source.clock.dt) + 1.0) + else + 0.0 + end + key = (application_id, binding.source_var) + push!( + temporal_dependencies, + key, + ) + dependency_horizons[key] = max( + get(dependency_horizons, key, 0.0), + required, + ) + end + end + + requested_outputs = Set{Tuple{Symbol,Symbol}}() + names = Set{Symbol}() + for request in output_requests + request.name in names && error( + "Duplicate output request name `$(request.name)`. Request names must be unique." + ) + push!(names, request.name) + application = _scene_request_application( + compiled.scene, + compiled, + request, + ) + push!(requested_outputs, (application.id, request.var)) + end + return SceneOutputRetentionPlan( + retain_all, + temporal_dependencies, + requested_outputs, + dependency_horizons, + ) +end + +function explain_output_retention(sim::SceneSimulation) + plan = sim.output_retention + keys_to_explain = if plan.retain_all + Set( + (application.id, Symbol(variable)) + for application in sim.compiled.applications + for variable in keys(outputs_(application.spec)) + ) + else + union(plan.temporal_dependencies, plan.requested_outputs) + end + return [ + ( + application_id=application_id, + variable=variable, + reasons=plan.retain_all ? + (:default_all,) : + Tuple( + reason for (reason, keys) in ( + :temporal_dependency => plan.temporal_dependencies, + :output_request => plan.requested_outputs, + ) + if (application_id, variable) in keys + ), + retention_steps=plan.retain_all || + (application_id, variable) in plan.requested_outputs ? + nothing : + get( + plan.dependency_horizons, + (application_id, variable), + 0.0, + ), + current_target_count=length( + _compiled_application_by_id( + sim.compiled, + application_id, + ).target_ids, + ), + ) + for (application_id, variable) in sort!( + collect(keys_to_explain); + by=key -> (string(first(key)), string(last(key))), + ) + ] +end + function _scene_call_targets(context::SceneRunContext, name::Symbol) targets = SceneCallTarget[] bindings = get( @@ -2571,6 +4123,7 @@ function _scene_call_targets(context::SceneRunContext, name::Symbol) _application_model(callee_application, object_id), status, context.temporal_streams, + context.output_retention, context.time, context.constants, ), @@ -2581,10 +4134,36 @@ function _scene_call_targets(context::SceneRunContext, name::Symbol) return targets end -dependency_targets(context::SceneRunContext, name::Symbol) = _scene_call_targets(context, name) -dependency_target(context::SceneRunContext, name::Symbol) = only(dependency_targets(context, name)) +""" + call_targets(context::SceneRunContext, name) + call_target(context::SceneRunContext, name) + +Return manually executable targets declared with `Calls(...)` for the current +scene/object model call. `call_target` requires exactly one matching target. +Execute returned targets with [`run_call!`](@ref). +""" +call_targets(context::SceneRunContext, name::Symbol) = _scene_call_targets(context, name) +call_targets(context::SceneRunContext, name::AbstractString) = + call_targets(context, Symbol(name)) +call_target(context::SceneRunContext, name::Symbol) = only(call_targets(context, name)) +call_target(context::SceneRunContext, name::AbstractString) = + call_target(context, Symbol(name)) + +dependency_targets(context::SceneRunContext, name::Symbol) = call_targets(context, name) +dependency_targets(context::SceneRunContext, name::AbstractString) = + call_targets(context, name) +dependency_target(context::SceneRunContext, name::Symbol) = call_target(context, name) +dependency_target(context::SceneRunContext, name::AbstractString) = + call_target(context, name) + +""" + run_call!(target::SceneCallTarget; publish=false, meteo=nothing) -function run_call!(target::SceneCallTarget; publish::Bool=true) +Run a manually selected model call. By default, the call mutates its target +status without publishing outputs or environment updates, which is suitable +for trial iterations. Pass `publish=true` once for the accepted state. +""" +function run_call!(target::SceneCallTarget; publish::Bool=false, meteo=nothing) _run_scene_application!( target.compiled, target.environment_bindings, @@ -2593,36 +4172,317 @@ function run_call!(target::SceneCallTarget; publish::Bool=true) time=target.time, constants=target.constants, temporal_streams=target.temporal_streams, + output_retention=target.output_retention, publish=publish, + meteo=meteo, ) return target end -function run!(scene::Scene; steps::Integer=1, constants=nothing) +function run!( + scene::Scene; + steps::Integer=1, + constants=nothing, + tracked_outputs=nothing, +) compiled = refresh_bindings!(scene) env_bindings = refresh_environment_bindings!(scene, compiled) - manual_application_ids = _manual_call_application_ids(compiled) - temporal_streams = Dict{Tuple{ObjectId,Symbol},Vector{Tuple{Float64,Any}}}() - ordered_applications = _ordered_scene_applications(compiled) + empty!(env_bindings.sample_cache) + execution_plan = compile_scene_execution_plan(compiled, env_bindings) + output_requests = _normalize_output_requests(tracked_outputs) + output_retention = compile_scene_output_retention( + compiled, + output_requests; + retain_all=isnothing(tracked_outputs), + ) + temporal_streams = Dict{Tuple{Symbol,ObjectId,Symbol},Any}() for step in 1:steps - for application in ordered_applications - application.id in manual_application_ids && continue - _scene_application_should_run(application, step) || continue - for object_id in application.target_ids - _run_scene_application!( - compiled, - env_bindings, - application, - object_id; - time=step, - constants=constants, - temporal_streams=temporal_streams, - publish=true, - ) - end + if bindings_dirty(scene) + compiled = refresh_bindings!(scene) + env_bindings = refresh_environment_bindings!(scene, compiled) + execution_plan = compile_scene_execution_plan(compiled, env_bindings) + output_retention = compile_scene_output_retention( + compiled, + output_requests; + retain_all=isnothing(tracked_outputs), + ) + elseif environment_bindings_dirty(scene) + env_bindings = refresh_environment_bindings!(scene, compiled) + execution_plan = compile_scene_execution_plan(compiled, env_bindings) + end + for batch in execution_plan.batches + _run_scene_execution_batch!( + batch, + compiled, + env_bindings; + time=step, + constants=constants, + temporal_streams=temporal_streams, + output_retention=output_retention, + ) end end - return scene + if bindings_dirty(scene) + compiled = refresh_bindings!(scene) + env_bindings = refresh_environment_bindings!(scene, compiled) + execution_plan = compile_scene_execution_plan(compiled, env_bindings) + output_retention = compile_scene_output_retention( + compiled, + output_requests; + retain_all=isnothing(tracked_outputs), + ) + elseif environment_bindings_dirty(scene) + env_bindings = refresh_environment_bindings!(scene, compiled) + execution_plan = compile_scene_execution_plan(compiled, env_bindings) + end + return SceneSimulation( + scene, + compiled, + env_bindings, + execution_plan, + output_retention, + temporal_streams, + output_requests, + ) +end + +function _scene_output_rows(sim::SceneSimulation, filter_object=nothing, filter_var=nothing) + rows = NamedTuple[] + for ((application_id, object_id, variable), samples) in sort!( + collect(sim.temporal_streams); + by=pair -> (string(first(pair)[1]), string(first(pair)[2].value), string(first(pair)[3])), + ) + isnothing(filter_object) || object_id == ObjectId(filter_object) || continue + isnothing(filter_var) || variable == Symbol(filter_var) || continue + for (time, value) in samples + push!( + rows, + ( + timestep=Int(round(time)), + time=time, + application_id=application_id, + object_id=object_id.value, + variable=variable, + value=value, + ), + ) + end + end + sort!(rows; by=row -> (row.timestep, string(row.object_id), string(row.variable))) + return rows +end + +function _materialize_scene_output_rows(rows, sink) + isnothing(sink) && return rows + sink === DataFrames.DataFrame && return DataFrames.DataFrame(rows) + return sink(rows) +end + +function _scene_request_application(scene::Scene, compiled::CompiledScene, request) + candidates = CompiledSceneApplication[] + for application in compiled.applications + request.var in keys(outputs_(application.spec)) || continue + isnothing(request.process) || application.process == request.process || continue + isnothing(request.application) || + application.id == request.application || + application.name == request.application || + continue + _scene_application_matches_scale(scene, application, request.scale) || continue + if isnothing(request.process) && + isnothing(request.application) && + _publish_mode_for_output(application.spec, request.var) == :stream_only + continue + end + push!(candidates, application) + end + if isempty(candidates) + error( + "No scene output publisher found for `$(request.scale).$(request.var)`", + isnothing(request.application) ? + (isnothing(request.process) ? "." : " from process `$(request.process)`.") : + " from application `$(request.application)`.", + ) + elseif length(candidates) > 1 + error( + "Ambiguous scene output publishers for `$(request.scale).$(request.var)`: ", + join((application.id for application in candidates), ", "), + ". Provide `application=` or make one publisher canonical.", + ) + end + return only(candidates) +end + +function _scene_request_application(sim::SceneSimulation, request) + return _scene_request_application(sim.scene, sim.compiled, request) +end + +function _selector_declared_scale(selector::AbstractObjectMultiplicity) + selector_criteria = criteria(selector) + scale = _criteria_get(selector_criteria, :scale, nothing) + !isnothing(scale) && return scale + for positional_selector in _criteria_get(selector_criteria, :selectors, ()) + positional_selector isa Scale && return positional_selector.scale + end + return nothing +end + +function _scene_application_matches_scale( + scene::Scene, + application::CompiledSceneApplication, + scale::Symbol, +) + declared_scale = _selector_declared_scale(application.applies_to) + declared_scale == scale && return true + for object_id in application.target_ids + object = get(scene.registry.objects, object_id, nothing) + !isnothing(object) && object.scale == scale && return true + end + return false +end + +function _scene_stream_object_matches_scale( + scene::Scene, + application::CompiledSceneApplication, + object_id::ObjectId, + scale::Symbol, +) + object = get(scene.registry.objects, object_id, nothing) + !isnothing(object) && return object.scale == scale + declared_scale = _selector_declared_scale(application.applies_to) + return isnothing(declared_scale) || declared_scale == scale +end + +function _scene_request_clock(request, timeline) + isnothing(request.clock) && return ClockSpec(1.0, 0.0) + clock = _clock_from_spec_timestep(request.clock, timeline) + isnothing(clock) && error( + "Unsupported clock specification `$(typeof(request.clock))` in ", + "OutputRequest `$(request.name)`.", + ) + return clock +end + +function _scene_requested_value(samples, time, t_start, policy, timeline) + if policy isa HoldLast + value = _scene_latest_sample(samples, time) + return isnothing(value) ? missing : value + elseif policy isa Interpolate + value = _scene_interpolated_sample(samples, time, policy) + return isnothing(value) ? missing : value + elseif policy isa Union{Integrate,Aggregate} + values = _scene_window_samples(samples, t_start, time) + isempty(values) && return missing + durations = fill(timeline.base_step_seconds, length(values)) + return _scene_window_reduce(values, durations, policy) + end + error("Unsupported scene output request policy `$(typeof(policy))`.") +end + +function _scene_requested_output_rows( + sim::SceneSimulation, + request, +) + application = _scene_request_application(sim, request) + timeline = _scene_timeline(sim.scene) + clock = _scene_request_clock(request, timeline) + source_rows = [ + (object_id=object_id, samples=samples) + for ((application_id, object_id, variable), samples) in sim.temporal_streams + if application_id == application.id && + variable == request.var && + _scene_stream_object_matches_scale(sim.scene, application, object_id, request.scale) + ] + isempty(source_rows) && return NamedTuple[] + nonempty_source_rows = [row for row in source_rows if !isempty(row.samples)] + isempty(nonempty_source_rows) && return NamedTuple[] + max_time = maximum(last(row.samples)[1] for row in nonempty_source_rows) + rows = NamedTuple[] + for time in 1:Int(floor(max_time)) + _should_run_at_time(clock, float(time)) || continue + t_start = float(time) - float(clock.dt) + 1.0 + for row in sort!(nonempty_source_rows; by=row -> string(row.object_id.value)) + first_sample_time = first(row.samples)[1] + last_sample_time = last(row.samples)[1] + first_sample_time <= float(time) <= last_sample_time || continue + push!( + rows, + ( + timestep=time, + time=float(time), + scale=request.scale, + process=application.process, + application_id=application.id, + variable=request.var, + object_id=row.object_id.value, + value=_scene_requested_value( + row.samples, + float(time), + t_start, + request.policy, + timeline, + ), + ), + ) + end + end + return rows +end + +function _collect_scene_requested_outputs(sim::SceneSimulation, sink) + outputs = Dict{Symbol,Any}() + for request in sim.output_requests + haskey(outputs, request.name) && error( + "Duplicate output request name `$(request.name)`. Request names must be unique." + ) + outputs[request.name] = _materialize_scene_output_rows( + _scene_requested_output_rows(sim, request), + sink, + ) + end + return outputs +end + +function collect_outputs(sim::SceneSimulation; sink=DataFrames.DataFrame) + isempty(sim.output_requests) || return _collect_scene_requested_outputs(sim, sink) + return _materialize_scene_output_rows(_scene_output_rows(sim), sink) +end + +function collect_outputs(sim::SceneSimulation, name::Symbol; sink=DataFrames.DataFrame) + matches = [request for request in sim.output_requests if request.name == name] + isempty(matches) && error( + "No scene output request named `$(name)`. Available request names are ", + isempty(sim.output_requests) ? "none." : join((request.name for request in sim.output_requests), ", "), + ) + length(matches) == 1 || error( + "Duplicate scene output request name `$(name)`. Request names must be unique." + ) + request = only(matches) + return _materialize_scene_output_rows( + _scene_requested_output_rows(sim, request), + sink, + ) +end + +function collect_outputs(sim::SceneSimulation, object_id, variable::Symbol; sink=DataFrames.DataFrame) + return _materialize_scene_output_rows(_scene_output_rows(sim, object_id, variable), sink) +end + +function explain_outputs(sim::SceneSimulation) + return [ + ( + application_id=application_id, + object_id=object_id.value, + variable=variable, + nsamples=length(samples), + first_time=isempty(samples) ? nothing : first(samples)[1], + last_time=isempty(samples) ? nothing : last(samples)[1], + value_type=isempty(samples) ? Union{} : typeof(last(samples)[2]), + ) + for ((application_id, object_id, variable), samples) in sort!( + collect(sim.temporal_streams); + by=pair -> (string(first(pair)[1]), string(first(pair)[2].value), string(first(pair)[3])), + ) + ] end struct ObjectAddress{SC,K,SP,S,N,P,V,R,M} @@ -2639,14 +4499,14 @@ end function ObjectAddress(selector::AbstractObjectMultiplicity) c = criteria(selector) - scope = haskey(c, :within) ? c.within : nothing - kind = haskey(c, :kind) ? c.kind : nothing - species = haskey(c, :species) ? c.species : nothing - scale = haskey(c, :scale) ? c.scale : nothing + scope = _criteria_scope(c) + kind = _criteria_value(c, :kind, Kind) + species = _criteria_value(c, :species, Species) + scale = _criteria_value(c, :scale, Scale) name = haskey(c, :name) ? c.name : nothing process = haskey(c, :process) ? c.process : nothing var = haskey(c, :var) ? c.var : nothing - relation = haskey(c, :relation) ? c.relation : nothing + relation = _criteria_value(c, :relation, Relation) return ObjectAddress(scope, kind, species, scale, name, process, var, relation, multiplicity(selector)) end @@ -2678,10 +4538,24 @@ function _normalize_application_bindings(bindings::Tuple) binding isa Pair || error( "Expected `var => selector` pairs in `Inputs(...)` or `Calls(...)`, got `$(typeof(binding))`." ) - first(binding) isa Union{Symbol,AbstractString} || error( - "Binding names in `Inputs(...)` and `Calls(...)` must be symbols or strings." - ) - push!(pairs, Symbol(first(binding)) => last(binding)) + key = first(binding) + selector = last(binding) + if key isa PreviousTimeStep + selector isa AbstractObjectMultiplicity || error( + "A `PreviousTimeStep(...)` input must map to `One(...)`, ", + "`OptionalOne(...)`, or `Many(...)`." + ) + push!( + pairs, + key.variable => _selector_with_previous_timestep(selector, key), + ) + else + key isa Union{Symbol,AbstractString} || error( + "Binding names in `Inputs(...)` and `Calls(...)` must be symbols, ", + "strings, or `PreviousTimeStep(:input)` markers." + ) + push!(pairs, Symbol(key) => selector) + end end return (; pairs...) end @@ -2719,6 +4593,29 @@ function _merge_value_inputs(defaults::NamedTuple, explicit::NamedTuple) return (; pairs(defaults)..., pairs(explicit)...) end +function _binding_origins(defaults::NamedTuple, explicit::NamedTuple) + origins = Pair{Symbol,Symbol}[] + for name in keys(defaults) + push!(origins, Symbol(name) => :model_default) + end + for name in keys(explicit) + push!(origins, Symbol(name) => :model_spec) + end + return (; origins...) +end + +function _normalize_binding_origins(origins::NamedTuple, bindings::NamedTuple) + normalized = Pair{Symbol,Symbol}[] + for name in keys(bindings) + origin = haskey(origins, name) ? Symbol(getproperty(origins, name)) : :model_spec + origin in (:model_default, :model_spec) || error( + "Unsupported binding origin `$(origin)` for `$(name)`." + ) + push!(normalized, Symbol(name) => origin) + end + return (; normalized...) +end + function _legacy_multiscale_rhs_from_input_selector(selector::AbstractObjectMultiplicity) c = criteria(selector) haskey(c, :scale) || return nothing diff --git a/src/time/runtime/environment_backends.jl b/src/time/runtime/environment_backends.jl index 9e777c90e..013ba4092 100644 --- a/src/time/runtime/environment_backends.jl +++ b/src/time/runtime/environment_backends.jl @@ -125,6 +125,61 @@ function validate_meteo_inputs(model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}, ) end +function _scene_validation_scale(scene::Scene, application::CompiledSceneApplication) + scales = Set{Symbol}() + for object_id in application.target_ids + object = _scene_object(scene, object_id) + isnothing(object.scale) || push!(scales, object.scale) + end + isempty(scales) && return :Scene + length(scales) == 1 && return only(scales) + return :Scene +end + +function _scene_model_specs_by_application(compiled::CompiledScene) + specs = Dict{Symbol,Dict{Symbol,ModelSpec}}() + for application in compiled.applications + scale = _scene_validation_scale(compiled.scene, application) + specs_at_scale = get!(specs, scale, Dict{Symbol,ModelSpec}()) + specs_at_scale[application.id] = application.spec + end + return specs +end + +""" + validate_meteo_inputs(scene::Scene) + validate_meteo_inputs(compiled::CompiledScene) + validate_meteo_inputs(compiled::CompiledScene, meteo_or_backend) + +Validate declared scene/object `meteo_inputs_`. + +The one-argument methods validate each application against its actual compiled +environment backend, including application-level `Environment(...)` overrides. +The two-argument method validates every compiled application against an +explicit replacement meteorology/environment backend. Diagnostics report model +application ids, so several applications for the same process can be diagnosed +independently. +""" +function validate_meteo_inputs(compiled::CompiledScene, meteo_or_backend) + backend = environment_backend(meteo_or_backend) + return validate_meteo_inputs(_scene_model_specs_by_application(compiled), backend) +end + +function validate_meteo_inputs(compiled::CompiledScene) + refresh_environment_bindings!(compiled.scene, compiled) + return nothing +end + +function validate_meteo_inputs(scene::Scene) + compiled = refresh_bindings!(scene) + return validate_meteo_inputs(compiled) +end + +function validate_meteo_inputs(scene::Scene, meteo_or_backend) + compiled = refresh_bindings!(scene) + return validate_meteo_inputs(compiled, meteo_or_backend) +end + """ sample(backend, variable, support, time) @@ -192,6 +247,7 @@ end function _environment_sampling_rules(model_spec::ModelSpec) bindings = meteo_bindings(model_spec) bindings = bindings isa NamedTuple ? bindings : NamedTuple() + environment_sources = _environment_source_overrides(model_spec) rules = Pair{Symbol,Symbol}[] for target in keys(meteo_inputs_(model_spec)) source = target @@ -199,11 +255,28 @@ function _environment_sampling_rules(model_spec::ModelSpec) rule = bindings[target] rule isa NamedTuple && haskey(rule, :source) && (source = Symbol(rule.source)) end + if haskey(environment_sources, target) + source = Symbol(environment_sources[target]) + end push!(rules, target => source) end return rules end +function _environment_source_overrides(model_spec::ModelSpec) + config = environment_config(model_spec) + payload = config isa EnvironmentConfig ? config.config : config + if payload isa NamedTuple && haskey(payload, :sources) + sources = payload.sources + sources isa NamedTuple || error( + "Environment `sources` must be a NamedTuple, for example ", + "`Environment(; sources=(CO2=:Ca,))`." + ) + return sources + end + return NamedTuple() +end + function sample_environment( backend::AbstractEnvironmentBackend, support::EnvironmentSupport, @@ -226,7 +299,9 @@ function sample_environment( row = _meteo_row_at_step(environment_meteo(backend), Int(round(time))) bindings = meteo_bindings(model_spec) has_bindings = bindings isa NamedTuple && !isempty(keys(bindings)) - !has_bindings && return row + environment_sources = _environment_source_overrides(model_spec) + has_environment_sources = !isempty(keys(environment_sources)) + !has_bindings && !has_environment_sources && return row pairs = Pair{Symbol,Any}[] for (target, source) in _environment_sampling_rules(model_spec) diff --git a/src/time/runtime/output_export.jl b/src/time/runtime/output_export.jl index 53e603ad1..3f48c51cb 100644 --- a/src/time/runtime/output_export.jl +++ b/src/time/runtime/output_export.jl @@ -1,10 +1,18 @@ """ - OutputRequest(scale, var; name=var, process=nothing, policy=HoldLast(), clock=nothing) + OutputRequest( + scale, + var; + name=var, + process=nothing, + application=nothing, + policy=HoldLast(), + clock=nothing, + ) -Describe one online-exported multi-rate output series for MTG multi-rate runs. +Describe one resampled output series for multi-rate or scene/object runs. -Use this type in `run!(...; tracked_outputs=...)` to export -resampled temporal streams while simulation is running. +Use this type in `run!(...; tracked_outputs=...)` to retain and materialize +resampled temporal streams. # Arguments - `scale::Symbol`: producer scale (for example `:Leaf` or `:Plant`). @@ -16,6 +24,10 @@ resampled temporal streams while simulation is running. - `process=nothing`: producer process name (`Symbol`/`String`) or `nothing`. When `nothing`, runtime tries to use the unique canonical publisher for `(scale, var)` and errors on ambiguity. +- `application=nothing`: scene/object application name or compiled application + id. Use this when the same process is applied more than once. An explicit + application may select a `:stream_only` publisher. Legacy `GraphSimulation` + output export does not support this selector. - `policy::SchedulePolicy=HoldLast()`: resampling policy applied at export time. Common values are `HoldLast()`, `Integrate(...)`, `Aggregate(...)`, `Interpolate(...)`. @@ -37,11 +49,12 @@ req_daily = OutputRequest( ) ``` """ -struct OutputRequest{P<:Union{Nothing,Symbol},POL<:SchedulePolicy,C} +struct OutputRequest{P<:Union{Nothing,Symbol},A<:Union{Nothing,Symbol},POL<:SchedulePolicy,C} scale::Symbol var::Symbol name::Symbol process::P + application::A policy::POL clock::C end @@ -51,11 +64,13 @@ function OutputRequest( var::Symbol; name::Symbol=var, process=nothing, + application=nothing, policy::SchedulePolicy=HoldLast(), clock=nothing ) proc = isnothing(process) ? nothing : Symbol(process) - return OutputRequest(scale, var, name, proc, policy, clock) + app = isnothing(application) ? nothing : Symbol(application) + return OutputRequest(scale, var, name, proc, app, policy, clock) end function _export_clock(request::OutputRequest, timeline::TimelineContext) @@ -120,6 +135,9 @@ function prepare_output_requests!(sim::GraphSimulation, requests, timeline::Time rows = Dict{Symbol,ExportBuffer}() for req in reqs + isnothing(req.application) || error( + "`application=` in `OutputRequest` is only supported by scene/object simulations." + ) scale = req.scale process = isnothing(req.process) ? _canonical_source_process(sim, scale, req.var) : req.process model_spec = _model_spec_for_process(sim, scale, process) diff --git a/test/helper-functions.jl b/test/helper-functions.jl index 588857b56..cb35b8188 100644 --- a/test/helper-functions.jl +++ b/test/helper-functions.jl @@ -59,10 +59,13 @@ end # Helper used to compare a single-scale `ModelMapping` run with its generated # multiscale equivalent. -function check_multiscale_simulation_is_equivalent_begin(mapping::ModelMapping, meteo) +function check_multiscale_simulation_is_equivalent_begin( + mapping::PlantSimEngine.ModelMapping, + meteo, +) _, models_at_scale = only(pairs(mapping)) status_nt = NamedTuple(something(PlantSimEngine.get_status(models_at_scale), Status())) - models = ModelMapping(PlantSimEngine.get_models(models_at_scale)...; status=status_nt) + models = PlantSimEngine.ModelMapping(PlantSimEngine.get_models(models_at_scale)...; status=status_nt) mtg, mapping, out = PlantSimEngine.modellist_to_mapping(models, status_nt; nsteps=length(meteo), outputs=nothing) return mtg, mapping, out end @@ -72,14 +75,21 @@ function check_multiscale_simulation_is_equivalent_end(modellist_outputs, mtg, m return compare_outputs_modellist_mapping(modellist_outputs, graph_sim) end -function check_multiscale_simulation_is_equivalent(mapping::ModelMapping, meteo) +function check_multiscale_simulation_is_equivalent( + mapping::PlantSimEngine.ModelMapping, + meteo, +) modellist_outputs = run!(mapping, meteo) mtg, mapping_mt, out = check_multiscale_simulation_is_equivalent_begin(mapping, meteo) return check_multiscale_simulation_is_equivalent_end(modellist_outputs, mtg, mapping_mt, out, meteo) end # Quick and naive first version. Doesn't check if everything is timestep parallelizable, doesn't check for nthreads etc. -function run_single_and_multi_thread_modellist(mapping::ModelMapping, tracked_outputs, meteo) +function run_single_and_multi_thread_modellist( + mapping::PlantSimEngine.ModelMapping, + tracked_outputs, + meteo, +) out_seq = run!(mapping, meteo; tracked_outputs=tracked_outputs, executor=SequentialEx()) mapping_mt = copy(mapping) out_mt = run!(mapping_mt, meteo; tracked_outputs=tracked_outputs, executor=ThreadedEx()) @@ -135,22 +145,22 @@ function get_modellist_bank() status_tuples = [vals, vals2, vals3, vals4, vals5, vals6] - models = [ModelMapping( + models = [PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), status=vals ), - ModelMapping( + PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.5), ToyRUEGrowthModel(rue), status=vals2, - ), ModelMapping( + ), PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=vals3 - ), ModelMapping( + ), PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -159,7 +169,7 @@ function get_modellist_bank() process6=Process6Model(), # process7=Process7Model(), status=vals4 - ), ModelMapping( + ), PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -168,7 +178,7 @@ function get_modellist_bank() process6=Process6Model(), process7=Process7Model(), status=vals5 - ), ModelMapping( + ), PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -208,24 +218,24 @@ function get_modelmapping_bank() status_tuples = [vals, vals2, vals3, vals4, vals5, vals6] mappings = [ - ModelMapping( + PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), status=vals ), - ModelMapping( + PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.5), ToyRUEGrowthModel(rue); status=vals2 ), - ModelMapping( + PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=vals3 ), - ModelMapping( + PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -234,7 +244,7 @@ function get_modelmapping_bank() process6=Process6Model(), status=vals4 ), - ModelMapping( + PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -244,7 +254,7 @@ function get_modelmapping_bank() process7=Process7Model(), status=vals5 ), - ModelMapping( + PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -269,50 +279,50 @@ end # Could add some mtg variation too function get_simple_mapping_bank() mappings = [ - ModelMapping( + PlantSimEngine.ModelMapping( :Scene => ToyDegreeDaysCumulModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[:TT_cu => (:Scene => :TT_cu),],), Beer(0.6), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ :carbon_assimilation => [:Leaf], :carbon_demand => [:Leaf, :Internode], :carbon_allocation => [:Leaf, :Internode],],), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],],),), :Internode => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), mapped_variables=[:TT => (:Scene => :TT),],), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyInternodeEmergence(TT_emergence=20.0), mapped_variables=[:TT_cu => (:Scene => :TT_cu)],), ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), Status(carbon_biomass=1.0)), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content), :aPPFD => (:Plant => :aPPFD)],), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), mapped_variables=[:TT => (:Scene => :TT),],), ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), Status(carbon_biomass=1.0)), :Soil => (ToySoilWaterModel(),),), ########## - ModelMapping( + PlantSimEngine.ModelMapping( :Default => ( Process1Model(1.0), Status(var1=15.0, var2=0.3,),),), ########## - ModelMapping( + PlantSimEngine.ModelMapping( :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ # inputs @@ -320,7 +330,7 @@ function get_simple_mapping_bank() :carbon_demand => [:Leaf, :Internode], # outputs :carbon_allocation => [:Leaf, :Internode],],), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],],),), :Internode => ( @@ -328,7 +338,7 @@ function get_simple_mapping_bank() ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), Status(TT=10.0, carbon_biomass=1.0)), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], # Notice we provide :Soil, not [:Soil], so a single value is expected here @@ -375,7 +385,12 @@ function get_simple_mapping_bank() end -function test_filtered_output_begin(m::ModelMapping, status_tuple, requested_outputs, meteo) +function test_filtered_output_begin( + m::PlantSimEngine.ModelMapping, + status_tuple, + requested_outputs, + meteo, +) nsteps = PlantSimEngine.get_nsteps(meteo) preallocated_outputs = PlantSimEngine.pre_allocate_outputs(m, requested_outputs, nsteps) @@ -402,7 +417,12 @@ function test_filtered_output(mtg, mapping, nsteps, outputs_mapping, meteo, filt return compare_outputs_modellist_mapping(filtered_outputs_modellist, graphsim) end -function test_filtered_output(m::ModelMapping, status_tuple, requested_outputs, meteo) +function test_filtered_output( + m::PlantSimEngine.ModelMapping, + status_tuple, + requested_outputs, + meteo, +) mtg, mapping, outputs_mapping, nsteps, filtered_outputs_modellist = test_filtered_output_begin(m, status_tuple, requested_outputs, meteo) diff --git a/test/test-ModelMapping.jl b/test/test-ModelMapping.jl index 7d6e37f96..43c2bba79 100644 --- a/test/test-ModelMapping.jl +++ b/test/test-ModelMapping.jl @@ -2,7 +2,7 @@ # Defining a list of models without status: @testset "ModelMapping with no status" begin - leaf = ModelMapping( + leaf = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model() ) @@ -15,7 +15,7 @@ @test length(status(leaf)) == 5 # Requiring 3 steps for initialization: - leaf = ModelMapping( + leaf = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), ) @@ -46,12 +46,12 @@ end; end @testset "ModelMapping with no process names" begin - with_names = ModelMapping( + with_names = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model() ) - without_names = ModelMapping( + without_names = PlantSimEngine.ModelMapping( Process1Model(1.0), Process2Model() ) @@ -62,7 +62,7 @@ end end; @testset "ModelMapping with a partially initialized status" begin - leaf = ModelMapping( + leaf = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), status=(var1=15.0,) @@ -76,7 +76,7 @@ end; @test to_initialize(leaf) == (process1=(:var2,),) # Requiring 3 steps for initialization: - leaf = ModelMapping( + leaf = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), status=(var1=15.0,), @@ -88,7 +88,7 @@ end; @testset "ModelMapping with fully initialized status" begin vals = (var1=15.0, var2=0.3) - leaf = ModelMapping( + leaf = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), status=vals @@ -109,7 +109,7 @@ end; @testset "ModelMapping with independant models (and missing one in the middle)" begin vals = (var1=15.0, var2=0.3) - leaf = ModelMapping( + leaf = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process3=Process3Model(), status=vals @@ -127,7 +127,7 @@ end; @testset "Copy a ModelMapping" begin vars = (var1=15.0, var2=0.3) # Create a model list: - models = ModelMapping( + models = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -169,7 +169,7 @@ end; process3_same = PlantSimEngine.convert_vars(ref_vars.process3, nothing) @test process3_same == ref_vars.process3 - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(); @@ -183,7 +183,7 @@ end @testset "ModelMapping dependencies" begin - models = ModelMapping( + models = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -238,7 +238,7 @@ end=# @testset "ModelMapping outputs preallocation" begin meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18) vals = (var1=15.0, var2=0.3, TT_cu=cumsum(meteo_day.TT)) - leaf = ModelMapping( + leaf = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), status=vals @@ -319,5 +319,5 @@ function PlantSimEngine.outputs_(::Reeb) end @testset "ModelMapping simple cyclic dependency detection" begin - @test_throws "Cyclic" m = ModelMapping(Beer(0.5), Reeb(0.5)) + @test_throws "Cyclic" m = PlantSimEngine.ModelMapping(Beer(0.5), Reeb(0.5)) end diff --git a/test/test-MultiScaleModel.jl b/test/test-MultiScaleModel.jl index a8e02cbaa..c27b0402d 100644 --- a/test/test-MultiScaleModel.jl +++ b/test/test-MultiScaleModel.jl @@ -11,13 +11,13 @@ @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => (:Plant => :plant_surfaces)) == (PreviousTimeStep(:plant_surfaces, :unknown) => :Plant => :plant_surfaces) # Case 6 @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => :Plant => :surface) == (PreviousTimeStep(:plant_surfaces, :unknown) => :Plant => :surface) # Case 6 @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => [:Plant => :surface, :Leaf => :surface]) == (PreviousTimeStep(:plant_surfaces, :unknown) => [:Plant => :surface, :Leaf => :surface]) # Case 6 - @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces)) == (PreviousTimeStep(:plant_surfaces, :unknown) => SameScale() => :plant_surfaces) # Case 7 - @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => (SameScale() => :surface)) == (PreviousTimeStep(:plant_surfaces, :unknown) => SameScale() => :surface) - @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => (SameScale() => :surface), :test) == (PreviousTimeStep(:plant_surfaces, :test) => SameScale() => :surface) + @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces)) == (PreviousTimeStep(:plant_surfaces, :unknown) => PlantSimEngine.SameScale() => :plant_surfaces) # Case 7 + @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => (PlantSimEngine.SameScale() => :surface)) == (PreviousTimeStep(:plant_surfaces, :unknown) => PlantSimEngine.SameScale() => :surface) + @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => (PlantSimEngine.SameScale() => :surface), :test) == (PreviousTimeStep(:plant_surfaces, :test) => PlantSimEngine.SameScale() => :surface) end; @testset "MultiScaleModel: case 1" begin - models = MultiScaleModel( + models = PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[:TT_cu => (:Scene => :TT_cu),], ) @@ -27,7 +27,7 @@ end; end; @testset "MultiScaleModel: case 2" begin - models = MultiScaleModel( + models = PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[:TT_cu => [:Plant],], ) @@ -36,7 +36,7 @@ end; @test models.mapped_variables == [:TT_cu => [:Plant => :TT_cu]] - models = MultiScaleModel( + models = PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[:TT_cu => [:Leaf, :Internode],], ) @@ -47,7 +47,7 @@ end; @testset "MultiScaleModel: case 2, several variables with different format" begin - models = MultiScaleModel( + models = PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[:carbon_assimilation => [:Leaf], :carbon_demand => [:Leaf, :Internode], :Rm => (:Plant => :Rm_plant)], ) @@ -58,7 +58,7 @@ end; @testset "MultiScaleModel: case with PreviousTimeStep => ..." begin - models = MultiScaleModel( + models = PlantSimEngine.MultiScaleModel( model=ToyLAIfromLeafAreaModel(1.0), mapped_variables=[ PreviousTimeStep(:plant_surfaces) => :Plant => :surface, @@ -70,7 +70,7 @@ end; end; @testset "MultiScaleModel: several types of mapping" begin - models = MultiScaleModel( + models = PlantSimEngine.MultiScaleModel( model=ToyLightPartitioningModel(), mapped_variables=[ :aPPFD_larger_scale => (:Scene => :aPPFD), diff --git a/test/test-Status.jl b/test/test-Status.jl index 0bdcfac35..ce107c581 100644 --- a/test/test-Status.jl +++ b/test/test-Status.jl @@ -32,7 +32,7 @@ end @testset "Testing ModelMapping Status" begin # Create a ModelMapping - models = ModelMapping( + models = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), diff --git a/test/test-corner-cases.jl b/test/test-corner-cases.jl index bf92bbe87..334454a8b 100644 --- a/test/test-corner-cases.jl +++ b/test/test-corner-cases.jl @@ -172,13 +172,13 @@ end @testset "Multiscale nested hard dependencies" begin - mapping3Lvl = ModelMapping(:E1 => ( + mapping3Lvl = PlantSimEngine.ModelMapping(:E1 => ( Msg3LvlScaleAmontModel(), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=Msg3LvlScaleAvalModel(), mapped_variables=[:e3 => :E3 => :e3, :b2 => :E2 => :b2, :g2 => :E2 => :g2], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=Msg3LvlScaleEchelle1Model(), mapped_variables=[:e2 => :E2 => :e2, :f2 => :E2 => :f2,], ), Status(a=1.0,)# y = 1.0, z = 1.0) @@ -186,14 +186,14 @@ end :E2 => ( Msg3LvlScaleAmont2Model(), Msg3LvlScaleAval2Model(), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=Msg3LvlScaleEchelle2Model(), mapped_variables=[:c => :E1 => :c, :e3 => :E3 => :e3, :f3 => :E3 => :f3,], ), Status(a2=1.0, i2=1.0,) ), :E3 => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=Msg3LvlScaleEchelle3Model(), mapped_variables=[:c => :E1 => :c,], ), @@ -334,9 +334,9 @@ end # actual testset @testset "Soft dependency whose parent is a hard dependency of a parent at a different scale" begin - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( :E1 => (HardDepSameScaleEchelle1Model(), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=HardDepSameScaleEchelle1bisModel(), mapped_variables=[:e3 => :E3 => :e3], ), @@ -451,7 +451,7 @@ end @testset "Process/model reuse at different scales" begin - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( :E1 => ( SingleModelScale1(), Status(in=1.0, in1=1.0), @@ -465,7 +465,7 @@ end Status(in=1.0, in2bis=1.0), ), :E3 => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=SingleModelScale3(), mapped_variables=[:out1 => :E1 => :out1, :out2 => :E2 => :out2,], ), @@ -520,7 +520,7 @@ end using PlantSimEngine, PlantMeteo, DataFrames using PlantSimEngine.Examples mtg = import_mtg_example() - m = ModelMapping( + m = PlantSimEngine.ModelMapping( :Leaf => ( Process1Model(1.0), Status(var1=10.0, var2=1.0,) @@ -556,7 +556,7 @@ end outs = Dict(:Default => (:var1,)) mtg = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Default, 0, 0),) - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( :Default => ( Process1Model(1.0), Status(var1=15.0, var2=0.3,), @@ -607,7 +607,7 @@ end Atmosphere(T=18.0, Wind=1.0, Rh=0.65, Ri_PAR_f=100.0), ]) - model = ModelMapping( + model = PlantSimEngine.ModelMapping( ToyToyModel(1), status=(a=1, b=0, c=0), #nsteps = length(meteo) diff --git a/test/test-dimensions.jl b/test/test-dimensions.jl index 01eeae5a7..b6b13735e 100644 --- a/test/test-dimensions.jl +++ b/test/test-dimensions.jl @@ -31,14 +31,14 @@ # @test_throws DimensionMismatch PlantSimEngine.check_dimensions(tst3, w2) # ModelMapping and Weather must be checked for equal length - m1 = ModelMapping( + m1 = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), status=(var1=1.0, var2=2.0) ) @test PlantSimEngine.check_dimensions(m1, w1) === nothing - m2 = ModelMapping( + m2 = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), status=(var1=[1.0, 2.0], var2=2.0) diff --git a/test/test-domain-simulation.jl b/test/test-domain-simulation.jl index fc25ff57f..3bdc45138 100644 --- a/test/test-domain-simulation.jl +++ b/test/test-domain-simulation.jl @@ -93,13 +93,13 @@ PlantSimEngine.inputs_(::DomainSceneEvapotranspirationModel) = NamedTuple() PlantSimEngine.outputs_(::DomainSceneEvapotranspirationModel) = (evapotranspiration=0.0,) PlantSimEngine.dep(::DomainSceneEvapotranspirationModel) = ( - plant_transpiration=AllDomains(kind=:plant, process=:domain_plant_transpiration, policy=Integrate()), - soil_evaporation=AllDomains(kind=:soil, process=:domain_soil_evaporation, policy=Integrate()), + plant_transpiration=PlantSimEngine.AllDomains(kind=:plant, process=:domain_plant_transpiration, policy=Integrate()), + soil_evaporation=PlantSimEngine.AllDomains(kind=:soil, process=:domain_soil_evaporation, policy=Integrate()), ) function PlantSimEngine.run!(::DomainSceneEvapotranspirationModel, models, status, meteo, constants=nothing, extra=nothing) - plant_values = dependency_values(extra, :plant_transpiration, :transpiration) - soil_values = dependency_values(extra, :soil_evaporation, :evaporation) + plant_values = PlantSimEngine.dependency_values(extra, :plant_transpiration, :transpiration) + soil_values = PlantSimEngine.dependency_values(extra, :soil_evaporation, :evaporation) status.evapotranspiration = sum(filter(x -> !isnothing(x), plant_values)) + sum(filter(x -> !isnothing(x), soil_values)) return nothing end @@ -111,11 +111,11 @@ PlantSimEngine.inputs_(::DomainScenePlantEvapotranspirationModel) = NamedTuple() PlantSimEngine.outputs_(::DomainScenePlantEvapotranspirationModel) = (plant_evapotranspiration=0.0,) PlantSimEngine.dep(::DomainScenePlantEvapotranspirationModel) = ( - plant_transpiration=AllDomains(kind=:plant, process=:domain_plant_transpiration, policy=Integrate()), + plant_transpiration=PlantSimEngine.AllDomains(kind=:plant, process=:domain_plant_transpiration, policy=Integrate()), ) function PlantSimEngine.run!(::DomainScenePlantEvapotranspirationModel, models, status, meteo, constants=nothing, extra=nothing) - plant_values = dependency_values(extra, :plant_transpiration, :transpiration) + plant_values = PlantSimEngine.dependency_values(extra, :plant_transpiration, :transpiration) status.plant_evapotranspiration = sum(filter(x -> !isnothing(x), plant_values)) return nothing end @@ -139,7 +139,7 @@ PlantSimEngine.inputs_(::DomainHardLeafEnergyModel) = NamedTuple() PlantSimEngine.outputs_(::DomainHardLeafEnergyModel) = (leaf_temperature=0.0,) function PlantSimEngine.run!(::DomainHardLeafEnergyModel, models, status, meteo, constants=nothing, extra=nothing) - run_target!(models, status, :domain_hard_leaf_conductance; meteo=meteo, constants=constants, extra=extra) + PlantSimEngine.run_target!(models, status, :domain_hard_leaf_conductance; meteo=meteo, constants=constants, extra=extra) status.leaf_temperature = 20.0 + status.conductance return nothing end @@ -151,11 +151,11 @@ PlantSimEngine.inputs_(::DomainSceneConductanceSumModel) = NamedTuple() PlantSimEngine.outputs_(::DomainSceneConductanceSumModel) = (conductance_sum=0.0,) PlantSimEngine.dep(::DomainSceneConductanceSumModel) = ( - conductance=AllDomains(kind=:plant, process=:domain_hard_leaf_conductance, var=:conductance, policy=Integrate()), + conductance=PlantSimEngine.AllDomains(kind=:plant, process=:domain_hard_leaf_conductance, var=:conductance, policy=Integrate()), ) function PlantSimEngine.run!(::DomainSceneConductanceSumModel, models, status, meteo, constants=nothing, extra=nothing) - conductance_values = dependency_values(extra, :conductance) + conductance_values = PlantSimEngine.dependency_values(extra, :conductance) status.conductance_sum = sum(filter(x -> !isnothing(x), conductance_values)) return nothing end @@ -179,14 +179,14 @@ PlantSimEngine.inputs_(::DomainSceneHardTargetSumModel) = NamedTuple() PlantSimEngine.outputs_(::DomainSceneHardTargetSumModel) = (hard_target_total=0.0,) PlantSimEngine.dep(::DomainSceneHardTargetSumModel) = ( - plant_signal=HardDomains(kind=:plant, process=:domain_hard_target_signal), + plant_signal=PlantSimEngine.HardDomains(kind=:plant, process=:domain_hard_target_signal), ) function PlantSimEngine.run!(::DomainSceneHardTargetSumModel, models, status, meteo, constants=nothing, extra=nothing) - targets = dependency_targets(extra, :plant_signal) + targets = PlantSimEngine.dependency_targets(extra, :plant_signal) for target in targets - run_target!(target) - run_target!(target) + PlantSimEngine.run_target!(target) + PlantSimEngine.run_target!(target) end status.hard_target_total = sum(target.status.signal for target in targets) return nothing @@ -198,7 +198,7 @@ PlantSimEngine.inputs_(::DomainSceneCallsTargetSumModel) = NamedTuple() PlantSimEngine.outputs_(::DomainSceneCallsTargetSumModel) = (calls_target_total=0.0,) function PlantSimEngine.run!(::DomainSceneCallsTargetSumModel, models, status, meteo, constants=nothing, extra=nothing) - targets = dependency_targets(extra, :plant_signal) + targets = PlantSimEngine.dependency_targets(extra, :plant_signal) for target in targets run_call!(target) run_call!(target) @@ -226,13 +226,13 @@ PlantSimEngine.inputs_(::DomainSceneHardTargetLeafSumModel) = NamedTuple() PlantSimEngine.outputs_(::DomainSceneHardTargetLeafSumModel) = (leaf_hard_target_total=0.0,) PlantSimEngine.dep(::DomainSceneHardTargetLeafSumModel) = ( - leaf_calls=HardDomains(kind=:plant, scale=:Leaf, process=:domain_hard_target_leaf_counter), + leaf_calls=PlantSimEngine.HardDomains(kind=:plant, scale=:Leaf, process=:domain_hard_target_leaf_counter), ) function PlantSimEngine.run!(::DomainSceneHardTargetLeafSumModel, models, status, meteo, constants=nothing, extra=nothing) - targets = dependency_targets(extra, :leaf_calls) + targets = PlantSimEngine.dependency_targets(extra, :leaf_calls) for target in targets - run_target!(target; publish=true) + PlantSimEngine.run_target!(target; publish=true) end status.leaf_hard_target_total = sum(target.status.leaf_signal for target in targets) return nothing @@ -323,12 +323,12 @@ PlantSimEngine.inputs_(::DomainSceneDependencyFluxSumModel) = NamedTuple() PlantSimEngine.outputs_(::DomainSceneDependencyFluxSumModel) = (dependency_total=0.0, grouped_dependency_total=0.0,) PlantSimEngine.dep(::DomainSceneDependencyFluxSumModel) = ( - leaf_fluxes=AllDomains(kind=:plant, scale=:Leaf, process=:domain_mtg_leaf_flux, var=:leaf_flux), + leaf_fluxes=PlantSimEngine.AllDomains(kind=:plant, scale=:Leaf, process=:domain_mtg_leaf_flux, var=:leaf_flux), ) function PlantSimEngine.run!(::DomainSceneDependencyFluxSumModel, models, status, meteo, constants=nothing, extra=nothing) - grouped_values = dependency_values(extra, :leaf_fluxes) - flattened_values = dependency_values(extra, :leaf_fluxes; flatten=true) + grouped_values = PlantSimEngine.dependency_values(extra, :leaf_fluxes) + flattened_values = PlantSimEngine.dependency_values(extra, :leaf_fluxes; flatten=true) status.grouped_dependency_total = sum(sum, grouped_values) status.dependency_total = sum(flattened_values) return nothing @@ -340,11 +340,11 @@ PlantSimEngine.inputs_(::DomainSceneGrowthFluxSumModel) = NamedTuple() PlantSimEngine.outputs_(::DomainSceneGrowthFluxSumModel) = (growth_flux_total=0.0,) PlantSimEngine.dep(::DomainSceneGrowthFluxSumModel) = ( - leaf_fluxes=AllDomains(kind=:plant, scale=:Leaf, process=:domain_growth_leaf_flux, var=:leaf_flux), + leaf_fluxes=PlantSimEngine.AllDomains(kind=:plant, scale=:Leaf, process=:domain_growth_leaf_flux, var=:leaf_flux), ) function PlantSimEngine.run!(::DomainSceneGrowthFluxSumModel, models, status, meteo, constants=nothing, extra=nothing) - status.growth_flux_total = sum(dependency_values(extra, :leaf_fluxes; flatten=true)) + status.growth_flux_total = sum(PlantSimEngine.dependency_values(extra, :leaf_fluxes; flatten=true)) return nothing end @@ -534,41 +534,41 @@ end for _ in 1:25 ]) - oil_palm_mapping = ModelMapping( - ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStepModel(Dates.Hour(1)), + oil_palm_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStep(Dates.Hour(1)), + ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStep(Dates.Hour(1)), status=(absorbed_radiation=0.0, transpiration=0.0), ) - maize_mapping = ModelMapping( - ModelSpec(DomainAbsorbedRadiationModel(0.3)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(DomainPlantTranspirationModel(0.02)) |> TimeStepModel(Dates.Hour(1)), + maize_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainAbsorbedRadiationModel(0.3)) |> TimeStep(Dates.Hour(1)), + ModelSpec(DomainPlantTranspirationModel(0.02)) |> TimeStep(Dates.Hour(1)), status=(absorbed_radiation=0.0, transpiration=0.0), ) - soil_mapping = ModelMapping( - ModelSpec(DomainSoilWaterModel(0.35)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(DomainSoilEvaporationModel(0.2)) |> TimeStepModel(Dates.Hour(1)), + soil_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSoilWaterModel(0.35)) |> TimeStep(Dates.Hour(1)), + ModelSpec(DomainSoilEvaporationModel(0.2)) |> TimeStep(Dates.Hour(1)), status=(soil_water_content=0.0, evaporation=0.0), ) - scene_mapping = ModelMapping( - ModelSpec(DomainSceneEvapotranspirationModel()) |> TimeStepModel(Dates.Day(1)), + scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSceneEvapotranspirationModel()) |> TimeStep(Dates.Day(1)), status=(evapotranspiration=0.0,), ) - simulation_mapping = SimulationMapping( - Domain(:oil_palm, oil_palm_mapping; kind=:plant), - Domain(:maize, maize_mapping; kind=:plant), - Domain(:soil, soil_mapping; kind=:soil), - Domain(:scene, scene_mapping; kind=:scene), + simulation_mapping = PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:oil_palm, oil_palm_mapping; kind=:plant), + PlantSimEngine.Domain(:maize, maize_mapping; kind=:plant), + PlantSimEngine.Domain(:soil, soil_mapping; kind=:soil), + PlantSimEngine.Domain(:scene, scene_mapping; kind=:scene), ) - domain_rows = explain_domains(simulation_mapping) + domain_rows = PlantSimEngine.explain_domains(simulation_mapping) @test length(domain_rows) == 4 @test any(row -> row.domain == :oil_palm && row.kind == :plant, domain_rows) - model_rows = explain_domain_models(simulation_mapping) + model_rows = PlantSimEngine.explain_domain_models(simulation_mapping) @test length(model_rows) == 7 @test any(row -> row.domain == :oil_palm && row.process == :domain_absorbed_radiation && haskey(row.meteo_inputs, :Ri_PAR_f), model_rows) @@ -580,13 +580,13 @@ end @test any(row -> row.domain == :scene && row.dt_seconds == 86_400.0, schedule) @test any(row -> row.domain == :oil_palm && row.dt_seconds == 3_600.0, schedule) - deps = explain_domain_dependencies(sim) + deps = PlantSimEngine.explain_domain_dependencies(sim) @test length(deps) == 3 @test count(row -> row.dependency == :plant_transpiration, deps) == 2 @test count(row -> row.dependency == :soil_evaporation, deps) == 1 @test all(row -> isnothing(row.variable), deps) - scene_key = DomainModelKey(:scene, :Default, :domain_scene_evapotranspiration) + scene_key = PlantSimEngine.DomainModelKey(:scene, :Default, :domain_scene_evapotranspiration) scene_values = sim.outputs[(scene_key, :evapotranspiration)] # Dates.Day(1) currently aligns to step 1, then step 25 when the base step is hourly. @@ -605,54 +605,54 @@ end for _ in 1:2 ]) - plant_mapping = ModelMapping( - ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStepModel(Dates.Hour(1)), + plant_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStep(Dates.Hour(1)), + ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStep(Dates.Hour(1)), status=(absorbed_radiation=0.0, transpiration=0.0), ) - raw_domain = Domain( + raw_domain = PlantSimEngine.Domain( :plant_kw; kind=:plant, mapping=( - ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStep(Dates.Hour(1)), + ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStep(Dates.Hour(1)), Status(absorbed_radiation=0.0, transpiration=0.0), ), ) - @test raw_domain.mapping isa ModelMapping + @test raw_domain.mapping isa PlantSimEngine.ModelMapping - @test_throws ErrorException SimulationMapping( - Domain(:plant, plant_mapping; kind=:plant), - Domain(:plant, plant_mapping; kind=:plant), + @test_throws ErrorException PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:plant, plant_mapping; kind=:plant), + PlantSimEngine.Domain(:plant, plant_mapping; kind=:plant), ) daily_meteo = Weather([ Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) for _ in 1:25 ]) - mixed_rate_mapping = ModelMapping( - ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStepModel(Dates.Day(1)), + mixed_rate_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStep(Dates.Hour(1)), + ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStep(Dates.Day(1)), status=(absorbed_radiation=0.0, transpiration=0.0), ) mixed_sim = run!( - SimulationMapping(Domain(:mixed_plant, mixed_rate_mapping; kind=:plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:mixed_plant, mixed_rate_mapping; kind=:plant)), daily_meteo, check=true, ) - absorbed_key = DomainModelKey(:mixed_plant, :Default, :domain_absorbed_radiation) - transpiration_key = DomainModelKey(:mixed_plant, :Default, :domain_plant_transpiration) + absorbed_key = PlantSimEngine.DomainModelKey(:mixed_plant, :Default, :domain_absorbed_radiation) + transpiration_key = PlantSimEngine.DomainModelKey(:mixed_plant, :Default, :domain_plant_transpiration) @test length(mixed_sim.outputs[(absorbed_key, :absorbed_radiation)]) == 25 @test length(mixed_sim.outputs[(transpiration_key, :transpiration)]) == 2 - unmatched_scene_mapping = ModelMapping( - ModelSpec(DomainSceneEvapotranspirationModel()) |> TimeStepModel(Dates.Day(1)), + unmatched_scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSceneEvapotranspirationModel()) |> TimeStep(Dates.Day(1)), status=(evapotranspiration=0.0,), ) unmatched_error = try run!( - SimulationMapping(Domain(:scene, unmatched_scene_mapping; kind=:scene)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:scene, unmatched_scene_mapping; kind=:scene)), hourly_meteo, check=true, ) @@ -665,56 +665,56 @@ end @test occursin("AllDomains(kind=:plant, process=:domain_plant_transpiration, policy=Integrate())", unmatched_error) @test occursin("Available producers:", unmatched_error) - multi_process_scene_mapping = ModelMapping( - ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(DomainScenePlantEvapotranspirationModel()) |> TimeStepModel(Dates.Hour(1)), + multi_process_scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStep(Dates.Hour(1)), + ModelSpec(DomainScenePlantEvapotranspirationModel()) |> TimeStep(Dates.Hour(1)), status=(absorbed_radiation=0.0, plant_evapotranspiration=0.0), ) multi_scene_sim = run!( - SimulationMapping( - Domain(:plant, plant_mapping; kind=:plant), - Domain(:scene, multi_process_scene_mapping; kind=:scene), + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:plant, plant_mapping; kind=:plant), + PlantSimEngine.Domain(:scene, multi_process_scene_mapping; kind=:scene), ), hourly_meteo, check=true, ) @test status(multi_scene_sim, :scene).plant_evapotranspiration > 0.0 - hard_plant_mapping = ModelMapping( - ModelSpec(DomainHardLeafConductanceModel()) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(DomainHardLeafEnergyModel()) |> TimeStepModel(Dates.Hour(1)), + hard_plant_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainHardLeafConductanceModel()) |> TimeStep(Dates.Hour(1)), + ModelSpec(DomainHardLeafEnergyModel()) |> TimeStep(Dates.Hour(1)), status=(conductance=0.0, leaf_temperature=0.0), ) - conductance_scene_mapping = ModelMapping( - ModelSpec(DomainSceneConductanceSumModel()) |> TimeStepModel(Dates.Hour(1)), + conductance_scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSceneConductanceSumModel()) |> TimeStep(Dates.Hour(1)), status=(conductance_sum=0.0,), ) hard_sim = run!( - SimulationMapping( - Domain(:hard_plant, hard_plant_mapping; kind=:plant), - Domain(:scene, conductance_scene_mapping; kind=:scene), + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:hard_plant, hard_plant_mapping; kind=:plant), + PlantSimEngine.Domain(:scene, conductance_scene_mapping; kind=:scene), ), hourly_meteo, check=true, ) - conductance_key = DomainModelKey(:hard_plant, :Default, :domain_hard_leaf_conductance) + conductance_key = PlantSimEngine.DomainModelKey(:hard_plant, :Default, :domain_hard_leaf_conductance) @test hard_sim.outputs[(conductance_key, :conductance)] == [2.0, 2.0] @test status(hard_sim, :scene).conductance_sum == 2.0 - hard_deps = explain_domain_dependencies(hard_sim) + hard_deps = PlantSimEngine.explain_domain_dependencies(hard_sim) @test only(hard_deps).variable == :conductance - hard_target_plant_mapping = ModelMapping( - ModelSpec(DomainHardTargetSignalModel(2.0)) |> TimeStepModel(Dates.Hour(1)), + hard_target_plant_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainHardTargetSignalModel(2.0)) |> TimeStep(Dates.Hour(1)), status=(call_count=0, signal=0.0), ) - hard_target_scene_mapping = ModelMapping( - ModelSpec(DomainSceneHardTargetSumModel()) |> TimeStepModel(Dates.Hour(1)), + hard_target_scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSceneHardTargetSumModel()) |> TimeStep(Dates.Hour(1)), status=(hard_target_total=0.0,), ) hard_target_sim = run!( - SimulationMapping( - Domain(:hard_target_plant, hard_target_plant_mapping; kind=:plant), - Domain(:scene, hard_target_scene_mapping; kind=:scene), + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:hard_target_plant, hard_target_plant_mapping; kind=:plant), + PlantSimEngine.Domain(:scene, hard_target_scene_mapping; kind=:scene), ), Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)), check=true, @@ -722,18 +722,18 @@ end @test status(hard_target_sim, :hard_target_plant).call_count == 2 @test status(hard_target_sim, :hard_target_plant).signal ≈ 4.0 @test status(hard_target_sim, :scene).hard_target_total ≈ 4.0 - @test only(explain_domain_dependencies(hard_target_sim)).mode == :hard_domain + @test only(PlantSimEngine.explain_domain_dependencies(hard_target_sim)).mode == :hard_domain - calls_target_scene_mapping = ModelMapping( + calls_target_scene_mapping = PlantSimEngine.ModelMapping( ModelSpec(DomainSceneCallsTargetSumModel()) |> Calls(:plant_signal => Many(kind=:plant, process=:domain_hard_target_signal)) |> TimeStep(Dates.Hour(1)), status=(calls_target_total=0.0,), ) calls_target_sim = run!( - SimulationMapping( - Domain(:hard_target_plant, hard_target_plant_mapping; kind=:plant), - Domain(:scene, calls_target_scene_mapping; kind=:scene), + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:hard_target_plant, hard_target_plant_mapping; kind=:plant), + PlantSimEngine.Domain(:scene, calls_target_scene_mapping; kind=:scene), ), Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)), check=true, @@ -741,18 +741,18 @@ end @test status(calls_target_sim, :hard_target_plant).call_count == 2 @test status(calls_target_sim, :hard_target_plant).signal ≈ 4.0 @test status(calls_target_sim, :scene).calls_target_total ≈ 4.0 - @test only(explain_domain_dependencies(calls_target_sim)).mode == :hard_domain + @test only(PlantSimEngine.explain_domain_dependencies(calls_target_sim)).mode == :hard_domain - route_source = AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration) - bad_route_source = AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:missing_output) + route_source = PlantSimEngine.AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration) + bad_route_source = PlantSimEngine.AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:missing_output) route_selector_error = try run!( - SimulationMapping( - Domain(:plant, plant_mapping; kind=:plant), - Domain(:scene, multi_process_scene_mapping; kind=:scene); - routes=(Route( + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:plant, plant_mapping; kind=:plant), + PlantSimEngine.Domain(:scene, multi_process_scene_mapping; kind=:scene); + routes=(PlantSimEngine.Route( from=bad_route_source, - to=DomainRouteTarget(:scene, var=:plant_transpirations), + to=PlantSimEngine.DomainRouteTarget(:scene, var=:plant_transpirations), ),), ), hourly_meteo, @@ -766,34 +766,34 @@ end @test occursin("Models matching all selector fields except `var=:missing_output`", route_selector_error) @test occursin("plant/Default/domain_plant_transpiration outputs=(:transpiration)", route_selector_error) - missing_target_scene_mapping = ModelMapping( - ModelSpec(DomainSceneRoutedAggregateModel()) |> TimeStepModel(Dates.Hour(1)), + missing_target_scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSceneRoutedAggregateModel()) |> TimeStep(Dates.Hour(1)), status=(daily_plant_transpiration=0.0, daily_routed_total=0.0), ) @test_throws "does not contain variable `plant_transpirations`" run!( - SimulationMapping( - Domain(:plant, plant_mapping; kind=:plant), - Domain(:scene, missing_target_scene_mapping; kind=:scene); - routes=(Route( + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:plant, plant_mapping; kind=:plant), + PlantSimEngine.Domain(:scene, missing_target_scene_mapping; kind=:scene); + routes=(PlantSimEngine.Route( from=route_source, - to=DomainRouteTarget(:scene, var=:plant_transpirations), + to=PlantSimEngine.DomainRouteTarget(:scene, var=:plant_transpirations), ),), ), hourly_meteo, check=true, ) - wrong_process_scene_mapping = ModelMapping( - ModelSpec(DomainSceneRoutedAggregateModel()) |> TimeStepModel(Dates.Hour(1)), + wrong_process_scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSceneRoutedAggregateModel()) |> TimeStep(Dates.Hour(1)), status=(plant_transpirations=[0.0], daily_plant_transpiration=0.0, daily_routed_total=0.0), ) @test_throws "does not consume variable `plant_transpirations`" run!( - SimulationMapping( - Domain(:plant, plant_mapping; kind=:plant), - Domain(:scene, wrong_process_scene_mapping; kind=:scene); - routes=(Route( + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:plant, plant_mapping; kind=:plant), + PlantSimEngine.Domain(:scene, wrong_process_scene_mapping; kind=:scene); + routes=(PlantSimEngine.Route( from=route_source, - to=DomainRouteTarget(:scene, var=:plant_transpirations, process=:domain_scene_routed_aggregate), + to=PlantSimEngine.DomainRouteTarget(:scene, var=:plant_transpirations, process=:domain_scene_routed_aggregate), ),), ), hourly_meteo, @@ -802,7 +802,7 @@ end @test_throws "has a selector but uses a single-status ModelMapping" run!( Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)), - SimulationMapping(Domain(:plant, plant_mapping; kind=:plant, selector=:Plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant, plant_mapping; kind=:plant, selector=:Plant)), hourly_meteo, check=true, ) @@ -814,34 +814,34 @@ end for _ in 1:25 ]) - oil_palm_mapping = ModelMapping( - ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStepModel(Dates.Hour(1)), + oil_palm_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStep(Dates.Hour(1)), + ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStep(Dates.Hour(1)), status=(absorbed_radiation=0.0, transpiration=0.0), ) - maize_mapping = ModelMapping( - ModelSpec(DomainAbsorbedRadiationModel(0.3)) |> TimeStepModel(Dates.Hour(1)), - ModelSpec(DomainPlantTranspirationModel(0.02)) |> TimeStepModel(Dates.Hour(1)), + maize_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainAbsorbedRadiationModel(0.3)) |> TimeStep(Dates.Hour(1)), + ModelSpec(DomainPlantTranspirationModel(0.02)) |> TimeStep(Dates.Hour(1)), status=(absorbed_radiation=0.0, transpiration=0.0), ) - hourly_scene_mapping = ModelMapping( - ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStepModel(Dates.Hour(1)), + hourly_scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStep(Dates.Hour(1)), status=(routed_total=0.0,), ) - vector_route = Route( - from=AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration), - to=DomainRouteTarget(:scene, var=:plant_transpirations, process=:domain_scene_routed_vector), - cardinality=ManyToOneVector(), + vector_route = PlantSimEngine.Route( + from=PlantSimEngine.AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration), + to=PlantSimEngine.DomainRouteTarget(:scene, var=:plant_transpirations, process=:domain_scene_routed_vector), + cardinality=PlantSimEngine.ManyToOneVector(), ) vector_sim = run!( - SimulationMapping( - Domain(:oil_palm, oil_palm_mapping; kind=:plant), - Domain(:maize, maize_mapping; kind=:plant), - Domain(:scene, hourly_scene_mapping; kind=:scene); + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:oil_palm, oil_palm_mapping; kind=:plant), + PlantSimEngine.Domain(:maize, maize_mapping; kind=:plant), + PlantSimEngine.Domain(:scene, hourly_scene_mapping; kind=:scene); routes=(vector_route,), ), hourly_meteo, @@ -852,22 +852,22 @@ end @test status(vector_sim, :scene).plant_transpirations ≈ [0.5, 0.6] @test status(vector_sim, :scene).routed_total ≈ hourly_plant_sum - route_rows = explain_routes(vector_sim) + route_rows = PlantSimEngine.explain_routes(vector_sim) @test length(route_rows) == 2 @test all(row -> row.target_var == :plant_transpirations, route_rows) - @test all(row -> row.cardinality == ManyToOneVector, route_rows) + @test all(row -> row.cardinality == PlantSimEngine.ManyToOneVector, route_rows) - inputs_route_scene_mapping = ModelMapping( + inputs_route_scene_mapping = PlantSimEngine.ModelMapping( ModelSpec(DomainSceneRoutedVectorModel()) |> Inputs(:plant_transpirations => Many(kind=:plant, process=:domain_plant_transpiration, var=:transpiration)) |> TimeStep(Dates.Hour(1)), status=(routed_total=0.0,), ) inputs_route_sim = run!( - SimulationMapping( - Domain(:oil_palm, oil_palm_mapping; kind=:plant), - Domain(:maize, maize_mapping; kind=:plant), - Domain(:scene, inputs_route_scene_mapping; kind=:scene), + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:oil_palm, oil_palm_mapping; kind=:plant), + PlantSimEngine.Domain(:maize, maize_mapping; kind=:plant), + PlantSimEngine.Domain(:scene, inputs_route_scene_mapping; kind=:scene), ), hourly_meteo, check=true, @@ -875,24 +875,24 @@ end @test :plant_transpirations in propertynames(status(inputs_route_sim, :scene)) @test status(inputs_route_sim, :scene).plant_transpirations ≈ [0.5, 0.6] @test status(inputs_route_sim, :scene).routed_total ≈ hourly_plant_sum - input_route_rows = explain_routes(inputs_route_sim) + input_route_rows = PlantSimEngine.explain_routes(inputs_route_sim) @test length(input_route_rows) == 2 @test all(row -> row.target_var == :plant_transpirations, input_route_rows) - @test all(row -> row.cardinality == ManyToOneVector, input_route_rows) + @test all(row -> row.cardinality == PlantSimEngine.ManyToOneVector, input_route_rows) - reordered_collector_mapping = ModelMapping( - ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStepModel(Dates.Hour(1)), + reordered_collector_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStep(Dates.Hour(1)), status=(plant_transpirations=[0.0], routed_total=0.0), ) - reordered_route = Route( - from=AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration), - to=DomainRouteTarget(:collector, var=:plant_transpirations, process=:domain_scene_routed_vector), - cardinality=ManyToOneVector(), + reordered_route = PlantSimEngine.Route( + from=PlantSimEngine.AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration), + to=PlantSimEngine.DomainRouteTarget(:collector, var=:plant_transpirations, process=:domain_scene_routed_vector), + cardinality=PlantSimEngine.ManyToOneVector(), ) reordered_sim = run!( - SimulationMapping( - Domain(:collector, reordered_collector_mapping; kind=:soil), - Domain(:oil_palm, oil_palm_mapping; kind=:plant); + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:collector, reordered_collector_mapping; kind=:soil), + PlantSimEngine.Domain(:oil_palm, oil_palm_mapping; kind=:plant); routes=(reordered_route,), ), hourly_meteo, @@ -901,42 +901,42 @@ end @test status(reordered_sim, :collector).plant_transpirations ≈ [0.5] @test status(reordered_sim, :collector).routed_total ≈ 0.5 - cyclic_scene_source_mapping = ModelMapping( - ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStepModel(Dates.Hour(1)), + cyclic_scene_source_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStep(Dates.Hour(1)), status=(plant_transpirations=[0.0], routed_total=0.0), ) - cyclic_route = Route( - from=AllDomains(kind=:scene, process=:domain_scene_routed_vector, var=:routed_total), - to=DomainRouteTarget(:oil_palm, var=:absorbed_radiation, process=:domain_plant_transpiration), - cardinality=ManyToOneAggregate(sum), + cyclic_route = PlantSimEngine.Route( + from=PlantSimEngine.AllDomains(kind=:scene, process=:domain_scene_routed_vector, var=:routed_total), + to=PlantSimEngine.DomainRouteTarget(:oil_palm, var=:absorbed_radiation, process=:domain_plant_transpiration), + cardinality=PlantSimEngine.ManyToOneAggregate(sum), ) @test_throws "Cyclic domain run-order constraints" run!( - SimulationMapping( - Domain(:oil_palm, oil_palm_mapping; kind=:plant), - Domain(:scene, cyclic_scene_source_mapping; kind=:scene); + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:oil_palm, oil_palm_mapping; kind=:plant), + PlantSimEngine.Domain(:scene, cyclic_scene_source_mapping; kind=:scene); routes=(cyclic_route,), ), hourly_meteo, check=true, ) - daily_scene_mapping = ModelMapping( - ModelSpec(DomainSceneRoutedAggregateModel()) |> TimeStepModel(Dates.Day(1)), + daily_scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSceneRoutedAggregateModel()) |> TimeStep(Dates.Day(1)), status=(daily_plant_transpiration=0.0, daily_routed_total=0.0), ) - aggregate_route = Route( - from=AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration), - to=DomainRouteTarget(:scene, var=:daily_plant_transpiration, process=:domain_scene_routed_aggregate), - cardinality=ManyToOneAggregate(sum), + aggregate_route = PlantSimEngine.Route( + from=PlantSimEngine.AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration), + to=PlantSimEngine.DomainRouteTarget(:scene, var=:daily_plant_transpiration, process=:domain_scene_routed_aggregate), + cardinality=PlantSimEngine.ManyToOneAggregate(sum), policy=Integrate(), ) aggregate_sim = run!( - SimulationMapping( - Domain(:oil_palm, oil_palm_mapping; kind=:plant), - Domain(:maize, maize_mapping; kind=:plant), - Domain(:scene, daily_scene_mapping; kind=:scene); + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:oil_palm, oil_palm_mapping; kind=:plant), + PlantSimEngine.Domain(:maize, maize_mapping; kind=:plant), + PlantSimEngine.Domain(:scene, daily_scene_mapping; kind=:scene); routes=(aggregate_route,), ), hourly_meteo, @@ -945,10 +945,10 @@ end @test status(aggregate_sim, :scene).daily_plant_transpiration ≈ 24.0 * hourly_plant_sum @test status(aggregate_sim, :scene).daily_routed_total ≈ 24.0 * hourly_plant_sum - aggregate_rows = explain_routes(aggregate_sim) + aggregate_rows = PlantSimEngine.explain_routes(aggregate_sim) @test length(aggregate_rows) == 2 @test all(row -> row.dt_seconds == 86_400.0, aggregate_rows) - @test all(row -> row.cardinality <: ManyToOneAggregate, aggregate_rows) + @test all(row -> row.cardinality <: PlantSimEngine.ManyToOneAggregate, aggregate_rows) end @testset "Domain graph dependency policy with changing topology" begin @@ -985,32 +985,32 @@ end Atmosphere(T=25.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)), ]) - oil_leaf_mapping = ModelMapping( + oil_leaf_mapping = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(DomainMTGLeafFluxModel(0.5)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainMTGLeafFluxModel(0.5)) |> TimeStep(Dates.Hour(1)), ), ) - maize_leaf_mapping = ModelMapping( + maize_leaf_mapping = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(DomainMTGLeafFluxModel(0.7)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainMTGLeafFluxModel(0.7)) |> TimeStep(Dates.Hour(1)), ), ) - scene_mapping = ModelMapping( - ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStepModel(Dates.Hour(1)), + scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStep(Dates.Hour(1)), status=(plant_transpirations=[0.0], routed_total=0.0), ) - route = Route( - from=AllDomains(kind=:plant, scale=:Leaf, process=:domain_mtg_leaf_flux, var=:leaf_flux), - to=DomainRouteTarget(:scene, var=:plant_transpirations, process=:domain_scene_routed_vector), - cardinality=ManyToOneVector(), + route = PlantSimEngine.Route( + from=PlantSimEngine.AllDomains(kind=:plant, scale=:Leaf, process=:domain_mtg_leaf_flux, var=:leaf_flux), + to=PlantSimEngine.DomainRouteTarget(:scene, var=:plant_transpirations, process=:domain_scene_routed_vector), + cardinality=PlantSimEngine.ManyToOneVector(), ) sim = run!( scene, - SimulationMapping( - Domain(:oil_palm, oil_leaf_mapping; kind=:plant, selector=oil_palm), - Domain(:maize, maize_leaf_mapping; kind=:plant, selector=maize), - Domain(:scene, scene_mapping; kind=:scene); + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:oil_palm, oil_leaf_mapping; kind=:plant, selector=oil_palm), + PlantSimEngine.Domain(:maize, maize_leaf_mapping; kind=:plant, selector=maize), + PlantSimEngine.Domain(:scene, scene_mapping; kind=:scene); routes=(route,), ), meteo, @@ -1023,23 +1023,23 @@ end @test length(status(sim, :Default)) == 1 @test status(sim, :scene).plant_transpirations ≈ [0.5, 0.7] @test status(sim, :scene).routed_total ≈ 1.2 - @test sim.outputs[(DomainModelKey(:scene, :Default, :domain_scene_routed_vector), :routed_total)] ≈ [1.2, 1.2] - @test sim.outputs[(DomainModelKey(:oil_palm, :Leaf, :domain_mtg_leaf_flux), :leaf_flux)] == [[0.5], [0.5]] - @test sim.outputs[(DomainModelKey(:maize, :Leaf, :domain_mtg_leaf_flux), :leaf_flux)] == [[0.7], [0.7]] - status_rows = explain_domain_statuses(sim) + @test sim.outputs[(PlantSimEngine.DomainModelKey(:scene, :Default, :domain_scene_routed_vector), :routed_total)] ≈ [1.2, 1.2] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:oil_palm, :Leaf, :domain_mtg_leaf_flux), :leaf_flux)] == [[0.5], [0.5]] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:maize, :Leaf, :domain_mtg_leaf_flux), :leaf_flux)] == [[0.7], [0.7]] + status_rows = PlantSimEngine.explain_domain_statuses(sim) @test sum(row -> row.scale == :Leaf, status_rows) == 2 @test only(row.nstatuses for row in status_rows if row.domain == :scene && row.scale == :Default) == 1 - forest_leaf_mapping = ModelMapping( + forest_leaf_mapping = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(DomainMTGLeafFluxModel(0.4)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainMTGLeafFluxModel(0.4)) |> TimeStep(Dates.Hour(1)), ), ) forest_sim = run!( scene, - SimulationMapping( - Domain(:forest, forest_leaf_mapping; kind=:plant, selector=:Plant), - Domain(:scene, scene_mapping; kind=:scene); + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:forest, forest_leaf_mapping; kind=:plant, selector=:Plant), + PlantSimEngine.Domain(:scene, scene_mapping; kind=:scene); routes=(route,), ), meteo, @@ -1049,13 +1049,13 @@ end @test length(status(forest_sim, :Leaf)) == 2 @test status(forest_sim, :scene).plant_transpirations ≈ [0.4, 0.4] @test status(forest_sim, :scene).routed_total ≈ 0.8 - @test forest_sim.outputs[(DomainModelKey(:forest, :Leaf, :domain_mtg_leaf_flux), :leaf_flux)] == [[0.4, 0.4], [0.4, 0.4]] - @test only(row.nstatuses for row in explain_domain_statuses(forest_sim) if row.domain == :forest && row.scale == :Leaf) == 2 + @test forest_sim.outputs[(PlantSimEngine.DomainModelKey(:forest, :Leaf, :domain_mtg_leaf_flux), :leaf_flux)] == [[0.4, 0.4], [0.4, 0.4]] + @test only(row.nstatuses for row in PlantSimEngine.explain_domain_statuses(forest_sim) if row.domain == :forest && row.scale == :Leaf) == 2 @test_throws "matched overlapping MTG roots" run!( scene, - SimulationMapping( - Domain( + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain( :overlapping_forest, forest_leaf_mapping; kind=:plant, @@ -1066,16 +1066,16 @@ end check=true, ) - dependency_scene_mapping = ModelMapping( - ModelSpec(DomainSceneDependencyFluxSumModel()) |> TimeStepModel(Dates.Hour(1)), + dependency_scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSceneDependencyFluxSumModel()) |> TimeStep(Dates.Hour(1)), status=(dependency_total=0.0, grouped_dependency_total=0.0), ) dependency_sim = run!( scene, - SimulationMapping( - Domain(:oil_palm, oil_leaf_mapping; kind=:plant, selector=oil_palm), - Domain(:maize, maize_leaf_mapping; kind=:plant, selector=maize), - Domain(:scene, dependency_scene_mapping; kind=:scene), + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:oil_palm, oil_leaf_mapping; kind=:plant, selector=oil_palm), + PlantSimEngine.Domain(:maize, maize_leaf_mapping; kind=:plant, selector=maize), + PlantSimEngine.Domain(:scene, dependency_scene_mapping; kind=:scene), ), meteo, check=true, @@ -1083,25 +1083,25 @@ end @test status(dependency_sim, :scene).dependency_total ≈ 1.2 @test status(dependency_sim, :scene).grouped_dependency_total ≈ 1.2 - soil_mapping = ModelMapping( - ModelSpec(DomainSoilWaterModel(0.35)) |> TimeStepModel(Dates.Hour(1)), + soil_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSoilWaterModel(0.35)) |> TimeStep(Dates.Hour(1)), status=(soil_water_content=0.0,), ) - soil_leaf_mapping = ModelMapping( + soil_leaf_mapping = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(DomainMTGLeafSoilFluxModel(2.0)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainMTGLeafSoilFluxModel(2.0)) |> TimeStep(Dates.Hour(1)), ), ) - soil_route = Route( - from=AllDomains(kind=:soil, process=:domain_soil_water, var=:soil_water_content), - to=DomainRouteTarget(:oil_palm, scale=:Leaf, var=:soil_signal, process=:domain_mtg_leaf_soil_flux), - cardinality=OneToManyBroadcast(), + soil_route = PlantSimEngine.Route( + from=PlantSimEngine.AllDomains(kind=:soil, process=:domain_soil_water, var=:soil_water_content), + to=PlantSimEngine.DomainRouteTarget(:oil_palm, scale=:Leaf, var=:soil_signal, process=:domain_mtg_leaf_soil_flux), + cardinality=PlantSimEngine.OneToManyBroadcast(), ) soil_to_graph_sim = run!( scene, - SimulationMapping( - Domain(:soil, soil_mapping; kind=:soil), - Domain(:oil_palm, soil_leaf_mapping; kind=:plant, selector=oil_palm); + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:soil, soil_mapping; kind=:soil), + PlantSimEngine.Domain(:oil_palm, soil_leaf_mapping; kind=:plant, selector=oil_palm); routes=(soil_route,), ), meteo, @@ -1109,20 +1109,20 @@ end ) expected_soil_signals = [0.35 - 0.001 * 20.0, 0.35 - 0.001 * 25.0] @test only(status(soil_to_graph_sim, :oil_palm, :Leaf)).soil_signal ≈ expected_soil_signals[2] - @test soil_to_graph_sim.outputs[(DomainModelKey(:oil_palm, :Leaf, :domain_mtg_leaf_soil_flux), :leaf_flux)] == [[2.0 * expected_soil_signals[1]], [2.0 * expected_soil_signals[2]]] + @test soil_to_graph_sim.outputs[(PlantSimEngine.DomainModelKey(:oil_palm, :Leaf, :domain_mtg_leaf_soil_flux), :leaf_flux)] == [[2.0 * expected_soil_signals[1]], [2.0 * expected_soil_signals[2]]] reordered_soil_to_graph_sim = run!( scene, - SimulationMapping( - Domain(:oil_palm, soil_leaf_mapping; kind=:plant, selector=oil_palm), - Domain(:soil, soil_mapping; kind=:soil); + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:oil_palm, soil_leaf_mapping; kind=:plant, selector=oil_palm), + PlantSimEngine.Domain(:soil, soil_mapping; kind=:soil); routes=(soil_route,), ), meteo, check=true, ) @test only(status(reordered_soil_to_graph_sim, :oil_palm, :Leaf)).soil_signal ≈ expected_soil_signals[2] - @test reordered_soil_to_graph_sim.outputs[(DomainModelKey(:oil_palm, :Leaf, :domain_mtg_leaf_soil_flux), :leaf_flux)] == [[2.0 * expected_soil_signals[1]], [2.0 * expected_soil_signals[2]]] + @test reordered_soil_to_graph_sim.outputs[(PlantSimEngine.DomainModelKey(:oil_palm, :Leaf, :domain_mtg_leaf_soil_flux), :leaf_flux)] == [[2.0 * expected_soil_signals[1]], [2.0 * expected_soil_signals[2]]] end @testset "Hard-domain targets from MTG-backed domains" begin @@ -1132,22 +1132,22 @@ end Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 2, 2)) meteo = Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) - plant_mapping = ModelMapping( + plant_mapping = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(DomainHardTargetLeafCounterModel(1.5)) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainHardTargetLeafCounterModel(1.5)) |> TimeStep(Dates.Hour(1)), Status(call_count=0, leaf_signal=0.0), ), ) - scene_mapping = ModelMapping( - ModelSpec(DomainSceneHardTargetLeafSumModel()) |> TimeStepModel(Dates.Hour(1)), + scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSceneHardTargetLeafSumModel()) |> TimeStep(Dates.Hour(1)), status=(leaf_hard_target_total=0.0,), ) sim = run!( scene, - SimulationMapping( - Domain(:hard_target_plant, plant_mapping; kind=:plant, selector=plant), - Domain(:scene, scene_mapping; kind=:scene), + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:hard_target_plant, plant_mapping; kind=:plant, selector=plant), + PlantSimEngine.Domain(:scene, scene_mapping; kind=:scene), ), meteo, check=true, @@ -1158,7 +1158,7 @@ end @test all(st -> st.call_count == 1, leaf_statuses) @test all(st -> st.leaf_signal ≈ 1.5, leaf_statuses) @test status(sim, :scene).leaf_hard_target_total ≈ 3.0 - @test sim.outputs[(DomainModelKey(:hard_target_plant, :Leaf, :domain_hard_target_leaf_counter), :leaf_signal)] == [1.5, 1.5] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:hard_target_plant, :Leaf, :domain_hard_target_leaf_counter), :leaf_signal)] == [1.5, 1.5] end @testset "MTG-backed domain growth registration" begin @@ -1170,26 +1170,26 @@ end for _ in 1:2 ]) - growth_mapping = ModelMapping( + growth_mapping = PlantSimEngine.ModelMapping( :Plant => ( - ModelSpec(DomainGrowthLeafEmergenceModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainGrowthLeafEmergenceModel()) |> TimeStep(Dates.Hour(1)), ), :Leaf => ( ModelSpec(DomainGrowthLeafFluxModel(0.9)) |> - MultiScaleModel([:grown_leaves => (:Plant => :grown_leaves)]) |> - TimeStepModel(Dates.Hour(1)), + PlantSimEngine.MultiScaleModel([:grown_leaves => (:Plant => :grown_leaves)]) |> + TimeStep(Dates.Hour(1)), ), ) - scene_mapping = ModelMapping( - ModelSpec(DomainSceneGrowthFluxSumModel()) |> TimeStepModel(Dates.Hour(1)), + scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(DomainSceneGrowthFluxSumModel()) |> TimeStep(Dates.Hour(1)), status=(growth_flux_total=0.0,), ) sim = run!( scene, - SimulationMapping( - Domain(:growing_plant, growth_mapping; kind=:plant, selector=plant), - Domain(:scene, scene_mapping; kind=:scene), + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:growing_plant, growth_mapping; kind=:plant, selector=plant), + PlantSimEngine.Domain(:scene, scene_mapping; kind=:scene), ), meteo, check=true, @@ -1200,9 +1200,9 @@ end @test only(status(sim, :growing_plant, :Leaf)).grown_leaves == 1.0 @test only(status(sim, :growing_plant, :Leaf)).leaf_flux ≈ 0.9 @test status(sim, :scene).growth_flux_total ≈ 0.9 - @test sim.outputs[(DomainModelKey(:growing_plant, :Leaf, :domain_growth_leaf_flux), :leaf_flux)] == [[0.9], [0.9]] - @test sim.outputs[(DomainModelKey(:scene, :Default, :domain_scene_growth_flux_sum), :growth_flux_total)] ≈ [0.9, 0.9] - @test only(row.nstatuses for row in explain_domain_statuses(sim) if row.domain == :growing_plant && row.scale == :Leaf) == 1 + @test sim.outputs[(PlantSimEngine.DomainModelKey(:growing_plant, :Leaf, :domain_growth_leaf_flux), :leaf_flux)] == [[0.9], [0.9]] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:scene, :Default, :domain_scene_growth_flux_sum), :growth_flux_total)] ≈ [0.9, 0.9] + @test only(row.nstatuses for row in PlantSimEngine.explain_domain_statuses(sim) if row.domain == :growing_plant && row.scale == :Leaf) == 1 multirate_scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) multirate_plant = Node(multirate_scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) @@ -1210,30 +1210,30 @@ end Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) for _ in 1:25 ]) - multirate_mapping = ModelMapping( + multirate_mapping = PlantSimEngine.ModelMapping( :Plant => ( - ModelSpec(DomainGrowthLeafEmergenceModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainGrowthLeafEmergenceModel()) |> TimeStep(Dates.Hour(1)), ModelSpec(DomainGrowthIntegratedFluxModel()) |> - MultiScaleModel([:leaf_flux => [:Leaf]]) |> - InputBindings(; leaf_flux=(process=:domain_growth_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=Integrate())) |> - TimeStepModel(Dates.Day(1)), + PlantSimEngine.MultiScaleModel([:leaf_flux => [:Leaf]]) |> + PlantSimEngine.InputBindings(; leaf_flux=(process=:domain_growth_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=Integrate())) |> + TimeStep(Dates.Day(1)), ), :Leaf => ( ModelSpec(DomainGrowthLeafFluxModel(0.9)) |> - MultiScaleModel([:grown_leaves => (:Plant => :grown_leaves)]) |> - TimeStepModel(Dates.Hour(1)), + PlantSimEngine.MultiScaleModel([:grown_leaves => (:Plant => :grown_leaves)]) |> + TimeStep(Dates.Hour(1)), ), ) multirate_sim = run!( multirate_scene, - SimulationMapping(Domain(:growing_plant, multirate_mapping; kind=:plant, selector=multirate_plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:growing_plant, multirate_mapping; kind=:plant, selector=multirate_plant)), multirate_meteo, check=true, ) @test length(status(multirate_sim, :growing_plant, :Leaf)) == 1 @test only(status(multirate_sim, :growing_plant, :Plant)).integrated_leaf_flux ≈ 24.0 * 0.9 - integrated_outputs = multirate_sim.outputs[(DomainModelKey(:growing_plant, :Plant, :domain_growth_integrated_flux), :integrated_leaf_flux)] + integrated_outputs = multirate_sim.outputs[(PlantSimEngine.DomainModelKey(:growing_plant, :Plant, :domain_growth_integrated_flux), :integrated_leaf_flux)] @test only.(integrated_outputs) ≈ [0.9, 24.0 * 0.9] @test length(integrated_outputs) == 2 end @@ -1248,13 +1248,13 @@ end for _ in 1:2 ]) - update_mapping = ModelMapping( + update_mapping = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(DomainUpdateAllocationModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainUpdateAllocationModel()) |> TimeStep(Dates.Hour(1)), ModelSpec(DomainUpdatePruningModel()) |> Updates(:leaf_biomass; after=:domain_update_allocation) |> - TimeStepModel(Dates.Hour(1)), - ModelSpec(DomainUpdateObserverModel()) |> TimeStepModel(Dates.Hour(1)), + TimeStep(Dates.Hour(1)), + ModelSpec(DomainUpdateObserverModel()) |> TimeStep(Dates.Hour(1)), ), ) @@ -1264,7 +1264,7 @@ end sim = run!( scene, - SimulationMapping(Domain(:updated_plant, update_mapping; kind=:plant, selector=plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:updated_plant, update_mapping; kind=:plant, selector=plant)), meteo, check=true, ) @@ -1272,8 +1272,8 @@ end leaf_status = only(status(sim, :updated_plant, :Leaf)) @test leaf_status.leaf_biomass == 0.0 @test leaf_status.observed_biomass == 0.0 - @test sim.outputs[(DomainModelKey(:updated_plant, :Leaf, :domain_update_pruning), :leaf_biomass)] == [[0.0], [0.0]] - @test sim.outputs[(DomainModelKey(:updated_plant, :Leaf, :domain_update_observer), :observed_biomass)] == [[0.0], [0.0]] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:updated_plant, :Leaf, :domain_update_pruning), :leaf_biomass)] == [[0.0], [0.0]] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:updated_plant, :Leaf, :domain_update_observer), :observed_biomass)] == [[0.0], [0.0]] end @testset "MTG-backed domain leaf removal registration" begin @@ -1287,21 +1287,21 @@ end for _ in 1:2 ]) - removal_mapping = ModelMapping( + removal_mapping = PlantSimEngine.ModelMapping( :Plant => ( ModelSpec(DomainRemovalPruningModel()) |> - MultiScaleModel([:leaf_flux => [:Leaf]]) |> - TimeStepModel(Dates.Hour(1)), + PlantSimEngine.MultiScaleModel([:leaf_flux => [:Leaf]]) |> + TimeStep(Dates.Hour(1)), Status(removed_count=0, removed_node_id=0, remaining_leaf_flux=0.0), ), :Leaf => ( - ModelSpec(DomainRemovalLeafFluxModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainRemovalLeafFluxModel()) |> TimeStep(Dates.Hour(1)), ), ) sim = run!( scene, - SimulationMapping(Domain(:pruned_plant, removal_mapping; kind=:plant, selector=plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:pruned_plant, removal_mapping; kind=:plant, selector=plant)), meteo, check=true, ) @@ -1313,30 +1313,30 @@ end @test plant_status.removed_count == 1 @test plant_status.removed_node_id > 0 @test plant_status.remaining_leaf_flux ≈ 1.0 - @test sim.outputs[(DomainModelKey(:pruned_plant, :Leaf, :domain_removal_leaf_flux), :leaf_flux)] == [[1.0], [1.0]] - @test sim.outputs[(DomainModelKey(:pruned_plant, :Plant, :domain_removal_pruning), :remaining_leaf_flux)] == [[1.0], [1.0]] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:pruned_plant, :Leaf, :domain_removal_leaf_flux), :leaf_flux)] == [[1.0], [1.0]] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:pruned_plant, :Plant, :domain_removal_pruning), :remaining_leaf_flux)] == [[1.0], [1.0]] @test_throws ErrorException remove_organ!(plant, only(sim.domain_states[:pruned_plant].simulations)) multirate_scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) multirate_plant = Node(multirate_scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) Node(multirate_plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) Node(multirate_plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 2, 2)) - multirate_removal_mapping = ModelMapping( + multirate_removal_mapping = PlantSimEngine.ModelMapping( :Plant => ( ModelSpec(DomainRemovalPruningModel()) |> - MultiScaleModel([:leaf_flux => [:Leaf]]) |> - InputBindings(; leaf_flux=(process=:domain_removal_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=Integrate())) |> - TimeStepModel(Dates.Day(1)), + PlantSimEngine.MultiScaleModel([:leaf_flux => [:Leaf]]) |> + PlantSimEngine.InputBindings(; leaf_flux=(process=:domain_removal_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=Integrate())) |> + TimeStep(Dates.Day(1)), Status(removed_count=0, removed_node_id=0, remaining_leaf_flux=0.0), ), :Leaf => ( - ModelSpec(DomainRemovalLeafFluxModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainRemovalLeafFluxModel()) |> TimeStep(Dates.Hour(1)), ), ) multirate_sim = run!( multirate_scene, - SimulationMapping(Domain(:pruned_plant, multirate_removal_mapping; kind=:plant, selector=multirate_plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:pruned_plant, multirate_removal_mapping; kind=:plant, selector=multirate_plant)), meteo, check=true, ) @@ -1358,22 +1358,22 @@ end for _ in 1:4 ]) - churn_mapping = ModelMapping( + churn_mapping = PlantSimEngine.ModelMapping( :Plant => ( ModelSpec(DomainChurnLeafControllerModel()) |> - MultiScaleModel([:leaf_flux => [:Leaf]]) |> - InputBindings(; leaf_flux=(process=:domain_churn_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=HoldLast())) |> - TimeStepModel(Dates.Hour(1)), + PlantSimEngine.MultiScaleModel([:leaf_flux => [:Leaf]]) |> + PlantSimEngine.InputBindings(; leaf_flux=(process=:domain_churn_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=HoldLast())) |> + TimeStep(Dates.Hour(1)), Status(created_count=0, removed_count=0, active_leaf_count=0, last_removed_node_id=0), ), :Leaf => ( - ModelSpec(DomainChurnLeafFluxModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainChurnLeafFluxModel()) |> TimeStep(Dates.Hour(1)), ), ) sim = run!( scene, - SimulationMapping(Domain(:churn_plant, churn_mapping; kind=:plant, selector=plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:churn_plant, churn_mapping; kind=:plant, selector=plant)), meteo, check=true, ) @@ -1388,12 +1388,12 @@ end @test get(status(sim.domain_states[:churn_plant]), :Leaf, Status[]) == Status[] @test status(sim, :churn_plant, :Leaf) == Status[] @test status(sim, :Leaf) == Status[] - @test only(row.nstatuses for row in explain_domain_statuses(sim) if row.domain == :churn_plant && row.scale == :Leaf) == 0 + @test only(row.nstatuses for row in PlantSimEngine.explain_domain_statuses(sim) if row.domain == :churn_plant && row.scale == :Leaf) == 0 @test all(key -> key.scale != :Leaf, keys(graph_sim.temporal_state.streams)) @test all(key -> key.scale != :Leaf, keys(graph_sim.temporal_state.caches)) - @test sim.outputs[(DomainModelKey(:churn_plant, :Plant, :domain_churn_leaf_controller), :created_count)] == [[1], [1], [2], [2]] - @test sim.outputs[(DomainModelKey(:churn_plant, :Plant, :domain_churn_leaf_controller), :removed_count)] == [[0], [1], [1], [2]] - @test sim.outputs[(DomainModelKey(:churn_plant, :Plant, :domain_churn_leaf_controller), :active_leaf_count)] == [[1], [0], [1], [0]] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:churn_plant, :Plant, :domain_churn_leaf_controller), :created_count)] == [[1], [1], [2], [2]] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:churn_plant, :Plant, :domain_churn_leaf_controller), :removed_count)] == [[0], [1], [1], [2]] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:churn_plant, :Plant, :domain_churn_leaf_controller), :active_leaf_count)] == [[1], [0], [1], [0]] end @testset "MTG-backed domain recursive subtree removal" begin @@ -1406,18 +1406,18 @@ end for _ in 1:2 ]) - subtree_mapping = ModelMapping( + subtree_mapping = PlantSimEngine.ModelMapping( :Plant => ( ModelSpec(DomainSubtreePruningModel()) |> - MultiScaleModel([ + PlantSimEngine.MultiScaleModel([ :internode_flux => [:Internode], :leaf_flux => [:Leaf], ]) |> - InputBindings(; + PlantSimEngine.InputBindings(; internode_flux=(process=:domain_subtree_internode_flux, scale=:Internode, var=:internode_flux, policy=HoldLast()), leaf_flux=(process=:domain_subtree_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=HoldLast()), ) |> - TimeStepModel(Dates.Hour(1)), + TimeStep(Dates.Hour(1)), Status( removed_count=0, removed_internode_id=0, @@ -1427,16 +1427,16 @@ end ), ), :Internode => ( - ModelSpec(DomainSubtreeInternodeFluxModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainSubtreeInternodeFluxModel()) |> TimeStep(Dates.Hour(1)), ), :Leaf => ( - ModelSpec(DomainSubtreeLeafFluxModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainSubtreeLeafFluxModel()) |> TimeStep(Dates.Hour(1)), ), ) sim = run!( scene, - SimulationMapping(Domain(:subtree_plant, subtree_mapping; kind=:plant, selector=plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:subtree_plant, subtree_mapping; kind=:plant, selector=plant)), meteo, check=true, ) @@ -1456,16 +1456,16 @@ end @test status(sim, :subtree_plant, :Leaf) == Status[] @test status(sim, :Internode) == Status[] @test status(sim, :Leaf) == Status[] - @test only(row.nstatuses for row in explain_domain_statuses(sim) if row.domain == :subtree_plant && row.scale == :Internode) == 0 - @test only(row.nstatuses for row in explain_domain_statuses(sim) if row.domain == :subtree_plant && row.scale == :Leaf) == 0 + @test only(row.nstatuses for row in PlantSimEngine.explain_domain_statuses(sim) if row.domain == :subtree_plant && row.scale == :Internode) == 0 + @test only(row.nstatuses for row in PlantSimEngine.explain_domain_statuses(sim) if row.domain == :subtree_plant && row.scale == :Leaf) == 0 @test all(key -> key.node_id != plant_status.removed_internode_id, keys(graph_sim.temporal_state.streams)) @test all(key -> key.node_id != plant_status.removed_leaf_id, keys(graph_sim.temporal_state.streams)) @test all(key -> key.node_id != plant_status.removed_internode_id, keys(graph_sim.temporal_state.caches)) @test all(key -> key.node_id != plant_status.removed_leaf_id, keys(graph_sim.temporal_state.caches)) - @test sim.outputs[(DomainModelKey(:subtree_plant, :Internode, :domain_subtree_internode_flux), :internode_flux)] == [[], []] - @test sim.outputs[(DomainModelKey(:subtree_plant, :Leaf, :domain_subtree_leaf_flux), :leaf_flux)] == [[], []] - @test sim.outputs[(DomainModelKey(:subtree_plant, :Plant, :domain_subtree_pruning), :remaining_internode_count)] == [[0], [0]] - @test sim.outputs[(DomainModelKey(:subtree_plant, :Plant, :domain_subtree_pruning), :remaining_leaf_count)] == [[0], [0]] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:subtree_plant, :Internode, :domain_subtree_internode_flux), :internode_flux)] == [[], []] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:subtree_plant, :Leaf, :domain_subtree_leaf_flux), :leaf_flux)] == [[], []] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:subtree_plant, :Plant, :domain_subtree_pruning), :remaining_internode_count)] == [[0], [0]] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:subtree_plant, :Plant, :domain_subtree_pruning), :remaining_leaf_count)] == [[0], [0]] end @testset "MTG-backed domain topology reparenting" begin @@ -1479,25 +1479,25 @@ end for _ in 1:2 ]) - reparent_mapping = ModelMapping( + reparent_mapping = PlantSimEngine.ModelMapping( :Plant => ( ModelSpec(DomainReparentLeafControllerModel()) |> - MultiScaleModel([:leaf_flux => [:Leaf]]) |> - InputBindings(; leaf_flux=(process=:domain_subtree_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=HoldLast())) |> - TimeStepModel(Dates.Hour(1)), + PlantSimEngine.MultiScaleModel([:leaf_flux => [:Leaf]]) |> + PlantSimEngine.InputBindings(; leaf_flux=(process=:domain_subtree_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=HoldLast())) |> + TimeStep(Dates.Hour(1)), Status(reparented_count=0, new_parent_id=0, leaf_parent_id=0, active_leaf_count=0), ), :Internode => ( - ModelSpec(DomainSubtreeInternodeFluxModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainSubtreeInternodeFluxModel()) |> TimeStep(Dates.Hour(1)), ), :Leaf => ( - ModelSpec(DomainSubtreeLeafFluxModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(DomainSubtreeLeafFluxModel()) |> TimeStep(Dates.Hour(1)), ), ) sim = run!( scene, - SimulationMapping(Domain(:reparented_plant, reparent_mapping; kind=:plant, selector=plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:reparented_plant, reparent_mapping; kind=:plant, selector=plant)), meteo, check=true, ) @@ -1515,7 +1515,7 @@ end @test length(status(sim, :reparented_plant, :Internode)) == 2 @test length(status(sim, :reparented_plant, :Leaf)) == 1 @test all(key -> key.node_id != 0, keys(graph_sim.temporal_state.streams)) - @test sim.outputs[(DomainModelKey(:reparented_plant, :Leaf, :domain_subtree_leaf_flux), :leaf_flux)] == [[1.0], [1.0]] - @test sim.outputs[(DomainModelKey(:reparented_plant, :Plant, :domain_reparent_leaf_controller), :leaf_parent_id)] == [[node_id(internode_2)], [node_id(internode_2)]] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:reparented_plant, :Leaf, :domain_subtree_leaf_flux), :leaf_flux)] == [[1.0], [1.0]] + @test sim.outputs[(PlantSimEngine.DomainModelKey(:reparented_plant, :Plant, :domain_reparent_leaf_controller), :leaf_parent_id)] == [[node_id(internode_2)], [node_id(internode_2)]] @test_throws ErrorException reparent_organ!(internode_2, leaf, graph_sim) end diff --git a/test/test-environment-backends.jl b/test/test-environment-backends.jl index 2d04fa783..1100da23d 100644 --- a/test/test-environment-backends.jl +++ b/test/test-environment-backends.jl @@ -62,15 +62,15 @@ function PlantSimEngine.run!(::EnvironmentGraphTemperatureUpdateModel, models, s end PlantSimEngine.dep(::EnvironmentSceneHardGraphUpdateModel) = ( - leaf_temperature=HardDomains(kind=:plant, scale=:Leaf, process=:environment_graph_temperature_update), + leaf_temperature=PlantSimEngine.HardDomains(kind=:plant, scale=:Leaf, process=:environment_graph_temperature_update), ) PlantSimEngine.inputs_(::EnvironmentSceneHardGraphUpdateModel) = NamedTuple() PlantSimEngine.outputs_(::EnvironmentSceneHardGraphUpdateModel) = (hard_temperature_sum=0.0,) function PlantSimEngine.run!(::EnvironmentSceneHardGraphUpdateModel, models, status, meteo, constants=nothing, extra=nothing) - targets = dependency_targets(extra, :leaf_temperature) + targets = PlantSimEngine.dependency_targets(extra, :leaf_temperature) for target in targets - run_target!(target; publish=true) + PlantSimEngine.run_target!(target; publish=true) end status.hard_temperature_sum = sum(target.status.T for target in targets) return nothing @@ -202,13 +202,13 @@ end @test base_step_seconds(global_backend) == 3600.0 @test environment_variables(GlobalConstant(nothing)) == Set{Symbol}() - mapping = ModelMapping( - ModelSpec(EnvironmentProbeModel()) |> TimeStepModel(Dates.Hour(1)), + mapping = PlantSimEngine.ModelMapping( + ModelSpec(EnvironmentProbeModel()) |> TimeStep(Dates.Hour(1)), status=(meteo_seen=0.0,), ) - simulation_mapping = SimulationMapping( - Domain(:plant_a, mapping; kind=:plant), - Domain(:plant_b, mapping; kind=:plant), + simulation_mapping = PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:plant_a, mapping; kind=:plant), + PlantSimEngine.Domain(:plant_b, mapping; kind=:plant), ) sim = run!(simulation_mapping, ProbeEnvironmentBackend(3, 3600.0), check=true) @@ -221,37 +221,37 @@ end @test environment.base_step_seconds == 3600.0 @test :CO2 in environment.variables - bound_mapping = ModelMapping( + bound_mapping = PlantSimEngine.ModelMapping( ModelSpec(EnvironmentProbeModel()) |> - TimeStepModel(Dates.Hour(1)) |> - MeteoBindings(; CO2=(source=:Ca, reducer=MeanReducer())), + TimeStep(Dates.Hour(1)) |> + PlantSimEngine.MeteoBindings(; CO2=(source=:Ca, reducer=MeanReducer())), status=(meteo_seen=0.0,), ) bound_sim = run!( - SimulationMapping(Domain(:plant_a, bound_mapping; kind=:plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, bound_mapping; kind=:plant)), ProbeEnvironmentBackend(1, 3600.0), check=true, ) @test status(bound_sim, :plant_a).meteo_seen ≈ 11.42 @test_throws "CO2" run!( - SimulationMapping(Domain(:plant_a, mapping; kind=:plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, mapping; kind=:plant)), MissingCO2EnvironmentBackend(), check=true, ) @test_throws "CO2" run!( - SimulationMapping(Domain(:plant_a, mapping; kind=:plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, mapping; kind=:plant)), nothing, check=true, ) - update_mapping = ModelMapping( - ModelSpec(EnvironmentTemperatureUpdateModel()) |> TimeStepModel(Dates.Hour(1)), + update_mapping = PlantSimEngine.ModelMapping( + ModelSpec(EnvironmentTemperatureUpdateModel()) |> TimeStep(Dates.Hour(1)), status=(T=0.0,), ) scattering_backend = ScatteringEnvironmentBackend(2, 3600.0, NamedTuple[], Any[]) scatter_sim = run!( - SimulationMapping(Domain(:plant_a, update_mapping; kind=:plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, update_mapping; kind=:plant)), scattering_backend, check=true, ) @@ -266,17 +266,17 @@ end ] @test_throws "GlobalConstant is immutable" run!( - SimulationMapping(Domain(:plant_a, update_mapping; kind=:plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, update_mapping; kind=:plant)), Atmosphere(T=20.0, Rh=0.65, Wind=1.0, duration=Dates.Hour(1)), check=true, ) - bad_mapping = ModelMapping( - ModelSpec(EnvironmentBadOutputModel()) |> TimeStepModel(Dates.Hour(1)), + bad_mapping = PlantSimEngine.ModelMapping( + ModelSpec(EnvironmentBadOutputModel()) |> TimeStep(Dates.Hour(1)), status=NamedTuple(), ) @test_throws "status does not contain" run!( - SimulationMapping(Domain(:plant_a, bad_mapping; kind=:plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, bad_mapping; kind=:plant)), ScatteringEnvironmentBackend(1, 3600.0, NamedTuple[], Any[]), check=true, ) @@ -286,19 +286,19 @@ end leaf_1 = Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) leaf_2 = Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 2, 2)) leaf_ids = sort([node_id(leaf_1), node_id(leaf_2)]) - graph_mapping = ModelMapping( + graph_mapping = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(EnvironmentGraphLeafProbeModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(EnvironmentGraphLeafProbeModel()) |> TimeStep(Dates.Hour(1)), ), ) graph_backend = GraphEnvironmentBackend(2, 3600.0, NamedTuple[], Any[]) graph_sim = run!( scene, - SimulationMapping(Domain(:plant_a, graph_mapping; kind=:plant, selector=plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, graph_mapping; kind=:plant, selector=plant)), graph_backend, check=true, ) - graph_values = graph_sim.outputs[(DomainModelKey(:plant_a, :Leaf, :environment_graph_leaf_probe), :meteo_seen)] + graph_values = graph_sim.outputs[(PlantSimEngine.DomainModelKey(:plant_a, :Leaf, :environment_graph_leaf_probe), :meteo_seen)] @test graph_values == [ [20.0 + 1.0 + 0.1 * leaf_ids[1] + 0.410, 20.0 + 1.0 + 0.1 * leaf_ids[2] + 0.410], [20.0 + 2.0 + 0.1 * leaf_ids[1] + 0.410, 20.0 + 2.0 + 0.1 * leaf_ids[2] + 0.410], @@ -311,15 +311,15 @@ end scatter_scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) scatter_plant = Node(scatter_scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) scatter_leaf = Node(scatter_plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - scatter_mapping = ModelMapping( + scatter_mapping = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(EnvironmentGraphTemperatureUpdateModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(EnvironmentGraphTemperatureUpdateModel()) |> TimeStep(Dates.Hour(1)), ), ) graph_scatter_backend = GraphEnvironmentBackend(1, 3600.0, NamedTuple[], Any[]) graph_scatter_sim = run!( scatter_scene, - SimulationMapping(Domain(:plant_a, scatter_mapping; kind=:plant, selector=scatter_plant)), + PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, scatter_mapping; kind=:plant, selector=scatter_plant)), graph_scatter_backend, check=true, ) @@ -339,21 +339,21 @@ end hard_scatter_scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) hard_scatter_plant = Node(hard_scatter_scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) hard_scatter_leaf = Node(hard_scatter_plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - hard_scatter_mapping = ModelMapping( + hard_scatter_mapping = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(EnvironmentGraphTemperatureUpdateModel()) |> TimeStepModel(Dates.Hour(1)), + ModelSpec(EnvironmentGraphTemperatureUpdateModel()) |> TimeStep(Dates.Hour(1)), ), ) - hard_scatter_scene_mapping = ModelMapping( - ModelSpec(EnvironmentSceneHardGraphUpdateModel()) |> TimeStepModel(Dates.Hour(1)), + hard_scatter_scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(EnvironmentSceneHardGraphUpdateModel()) |> TimeStep(Dates.Hour(1)), status=(hard_temperature_sum=0.0,), ) hard_graph_scatter_backend = GraphEnvironmentBackend(1, 3600.0, NamedTuple[], Any[]) hard_graph_scatter_sim = run!( hard_scatter_scene, - SimulationMapping( - Domain(:plant_a, hard_scatter_mapping; kind=:plant, selector=hard_scatter_plant), - Domain(:scene, hard_scatter_scene_mapping; kind=:scene), + PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:plant_a, hard_scatter_mapping; kind=:plant, selector=hard_scatter_plant), + PlantSimEngine.Domain(:scene, hard_scatter_scene_mapping; kind=:scene), ), hard_graph_scatter_backend, check=true, diff --git a/test/test-fitting.jl b/test/test-fitting.jl index 54a0337a4..140e43b51 100644 --- a/test/test-fitting.jl +++ b/test/test-fitting.jl @@ -3,7 +3,7 @@ @testset "Fitting Beer" begin k = 0.6 meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_f=300.0) - m = ModelMapping(Beer(k), status=(LAI=2.0,)) + m = PlantSimEngine.ModelMapping(Beer(k), status=(LAI=2.0,)) outs = run!(m, meteo) df = DataFrame(aPPFD=outs[:aPPFD][1], LAI=m.status.LAI[1], Ri_PAR_f=meteo.Ri_PAR_f[1]) diff --git a/test/test-maespa-domain-example.jl b/test/test-maespa-domain-example.jl index e4c741d65..b1b3d84a2 100644 --- a/test/test-maespa-domain-example.jl +++ b/test/test-maespa-domain-example.jl @@ -42,29 +42,143 @@ include("../examples/maespa_domain_example.jl") @test plant_a_status.daily_growth != plant_b_status.daily_growth @test plant_a_status.leaf_pool != plant_b_status.leaf_pool - deps = explain_domain_dependencies(sim) + deps = PlantSimEngine.explain_domain_dependencies(sim) @test count(row -> row.mode == :hard_domain && row.dependency == :energy_balance, deps) == 2 @test count(row -> row.mode == :hard_domain && row.dependency == :soil, deps) == 1 - @test length(sim.outputs[(DomainModelKey(:plant_A, :Leaf, :energy_balance), :λE)]) == 2 * 24 - @test length(sim.outputs[(DomainModelKey(:plant_B, :Leaf, :energy_balance), :λE)]) == 3 * 24 - @test length(sim.outputs[(DomainModelKey(:soil, :Default, :soil_water), :psi_soil)]) == 24 - @test length(sim.outputs[(DomainModelKey(:scene, :Default, :lai_dynamic), :lai)]) == 1 - @test length(sim.outputs[(DomainModelKey(:scene, :Default, :scene_eb), :scene_transpiration)]) == 24 + @test length(sim.outputs[(PlantSimEngine.DomainModelKey(:plant_A, :Leaf, :energy_balance), :λE)]) == 2 * 24 + @test length(sim.outputs[(PlantSimEngine.DomainModelKey(:plant_B, :Leaf, :energy_balance), :λE)]) == 3 * 24 + @test length(sim.outputs[(PlantSimEngine.DomainModelKey(:soil, :Default, :soil_water), :psi_soil)]) == 24 + @test length(sim.outputs[(PlantSimEngine.DomainModelKey(:scene, :Default, :lai_dynamic), :lai)]) == 1 + @test length(sim.outputs[(PlantSimEngine.DomainModelKey(:scene, :Default, :scene_eb), :scene_transpiration)]) == 24 @test status(sim, :soil).transpiration ≈ scene_status.scene_transpiration @test status(sim, :soil).psi_soil ≈ scene_status.psi_soil end +@testset "MAESPA-style unified scene example" begin + result = run_maespa_scene_example(; nhours=25, check=true) + scene = result.scene + compiled = result.compiled + + @test length(scene_objects(scene; scale=:Leaf)) == 5 + @test length(scene_objects(scene; scale=:Leaf, species=:A)) == 2 + @test length(scene_objects(scene; scale=:Leaf, species=:B)) == 3 + @test length(scene_objects(scene; scale=:Plant)) == 2 + @test length(scene_objects(scene; kind=:soil)) == 1 + + instance_rows = explain_instances(scene) + @test Set(row.name for row in instance_rows) == Set([:plant_A, :plant_B]) + plant_a_instance = only(row for row in instance_rows if row.name == :plant_A) + plant_b_instance = only(row for row in instance_rows if row.name == :plant_B) + @test plant_a_instance.object_ids == [:plant_A, :plant_A_axis, :plant_A_leaf_1, :plant_A_leaf_2] + @test plant_b_instance.object_ids == [:plant_B, :plant_B_axis, :plant_B_leaf_1, :plant_B_leaf_2, :plant_B_leaf_3] + @test :plant_A__energy_balance in plant_a_instance.application_ids + @test :plant_B__allocation in plant_b_instance.application_ids + + calls = explain_calls(compiled) + scene_energy_call = only(row for row in calls if row.application_id == :scene_eb && row.call == :energy_balance) + @test scene_energy_call.callee_object_ids == [ + :plant_A_leaf_1, + :plant_A_leaf_2, + :plant_B_leaf_1, + :plant_B_leaf_2, + :plant_B_leaf_3, + ] + @test Set(scene_energy_call.callee_application_ids) == Set([:plant_A__energy_balance, :plant_B__energy_balance]) + @test scene_energy_call.publication_policy == :explicit_accept + @test !scene_energy_call.default_publish + @test scene_energy_call.accepted_publish + scene_soil_call = only(row for row in calls if row.application_id == :scene_eb && row.call == :soil) + @test scene_soil_call.callee_object_ids == [:soil] + @test scene_soil_call.callee_application_ids == [:soil_water] + @test count(row -> row.call == :photosynthesis, calls) == 5 + @test count(row -> row.call == :stomatal_conductance, calls) == 5 + + leaf_energy_bundle = only( + row for row in explain_model_bundles(compiled) + if row.application_id == :plant_A__energy_balance && + row.object_id == :plant_A_leaf_1 + ) + @test leaf_energy_bundle.processes == [ + :energy_balance, + :photosynthesis, + :stomatal_conductance, + ] + @test leaf_energy_bundle.model_types == [Monteith{Float64,Int64}, Fvcb{Float64}, Tuzet{Float64}] + + bindings = explain_bindings(compiled) + lai_binding = only(row for row in bindings if row.application_id == :lai_dynamic && row.input == :leaf_areas) + @test lai_binding.carrier_kind == :ref_vector + @test lai_binding.copy_semantics == :live_references + @test lai_binding.source_ids == scene_energy_call.callee_object_ids + plant_a_allocation_binding = only(row for row in bindings if row.application_id == :plant_A__allocation) + plant_b_allocation_binding = only(row for row in bindings if row.application_id == :plant_B__allocation) + @test plant_a_allocation_binding.source_ids == [:plant_A_leaf_1, :plant_A_leaf_2] + @test plant_b_allocation_binding.source_ids == [:plant_B_leaf_1, :plant_B_leaf_2, :plant_B_leaf_3] + + schedule = explain_schedule(compiled) + @test only(row for row in schedule if row.application_id == :scene_eb).root_scheduled + @test only(row for row in schedule if row.application_id == :plant_A__energy_balance).manual_call_only + @test only(row for row in schedule if row.application_id == :soil_water).manual_call_only + @test only(row for row in schedule if row.application_id == :plant_A__allocation).dt_steps == 24.0 + @test only(row for row in schedule if row.application_id == :lai_dynamic).dt_steps == 24.0 + + scene_simulation = result.simulation + @test scene_simulation isa SceneSimulation + output_rows = collect_outputs(scene_simulation; sink=nothing) + @test count(row -> row.variable == :λE, output_rows) == 5 * 25 + @test count(row -> row.object_id == :soil && row.variable == :psi_soil, output_rows) == 25 + @test count(row -> row.object_id == :scene && row.variable == :scene_transpiration, output_rows) == 25 + @test count(row -> row.object_id == :scene && row.variable == :lai, output_rows) == 2 + @test count(row -> row.variable == :daily_growth, output_rows) == 2 * 2 + output_summary = explain_outputs(scene_simulation) + @test only(row for row in output_summary if row.object_id == :scene && row.variable == :scene_transpiration).nsamples == 25 + @test only(row for row in output_summary if row.object_id == :plant_A_leaf_1 && row.variable == :λE).application_id == :plant_A__energy_balance + + scene_status = only(scene_objects(scene; scale=:Scene)).status + last_meteo = last(maespa_meteo(; nhours=25)) + vpd_above = max(0.01, last_meteo.VPD) + @test isfinite(scene_status.canopy_tair) + @test isfinite(scene_status.canopy_vpd) + @test isfinite(scene_status.canopy_rh) + @test scene_status.canopy_tair >= last_meteo.T - 10.0 + @test scene_status.canopy_tair <= last_meteo.T + 10.0 + @test scene_status.canopy_vpd >= max(0.01, vpd_above - 1.5) + @test scene_status.canopy_vpd <= vpd_above + 1.5 + @test scene_status.scene_transpiration > 0.0 + @test scene_status.iterations > 0 + + leaf_statuses = [object.status for object in scene_objects(scene; scale=:Leaf)] + @test scene_status.leaf_area ≈ sum(st.leaf_area for st in leaf_statuses) + @test scene_status.lai ≈ scene_status.leaf_area + @test collect(scene_status.leaf_areas) ≈ getproperty.(leaf_statuses, :leaf_area) + @test all(st -> isfinite(st.Tₗ), leaf_statuses) + @test all(st -> isfinite(st.A), leaf_statuses) + @test all(st -> isfinite(st.λE), leaf_statuses) + @test any(st -> abs(st.Tₗ - scene_status.canopy_tair) > 1.0e-6, leaf_statuses) + + plant_a_status = only(scene_objects(scene; name=:plant_A)).status + plant_b_status = only(scene_objects(scene; name=:plant_B)).status + @test plant_a_status.daily_growth > 0.0 + @test plant_b_status.daily_growth > 0.0 + @test plant_a_status.daily_growth != plant_b_status.daily_growth + @test plant_a_status.leaf_pool != plant_b_status.leaf_pool + + soil_status = only(scene_objects(scene; kind=:soil)).status + @test soil_status.transpiration ≈ scene_status.scene_transpiration + @test soil_status.psi_soil ≈ scene_status.psi_soil +end + @testset "MAESPA-style domain example validation" begin mtg = build_maespa_scene() meteo = maespa_meteo(; nhours=1) - soil_mapping = ModelMapping( - ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75)) |> TimeStepModel(Dates.Hour(1)), + soil_mapping = PlantSimEngine.ModelMapping( + ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75)) |> TimeStep(Dates.Hour(1)), status=(theta1=0.33, theta2=0.36, psi_soil=-0.10, transpiration=0.0, infiltration=0.0), ) - scene_mapping = ModelMapping( - ModelSpec(LAIModel(1.0)) |> TimeStepModel(Dates.Hour(1)), + scene_mapping = PlantSimEngine.ModelMapping( + ModelSpec(LAIModel(1.0)) |> TimeStep(Dates.Hour(1)), ModelSpec(SceneEB(25, 0.03, 0.005)) |> Calls( :energy_balance => Many(kind=:plant, scale=:Leaf, process=:energy_balance), @@ -85,9 +199,9 @@ end iterations=0, ), ) - missing_leaf_mapping = SimulationMapping( - Domain(:soil, soil_mapping; kind=:soil), - Domain(:scene, scene_mapping; kind=:scene), + missing_leaf_mapping = PlantSimEngine.SimulationMapping( + PlantSimEngine.Domain(:soil, soil_mapping; kind=:soil), + PlantSimEngine.Domain(:scene, scene_mapping; kind=:scene), ) @test_throws "Hard domain dependency `energy_balance`" run!(mtg, missing_leaf_mapping, meteo, check=true, executor=SequentialEx()) @@ -95,7 +209,7 @@ end scene_status = Status(lai=0.0, leaf_area=0.0) @test_throws "SceneEB did not converge after 0 iterations" _solve_scene_energy_balance!( SceneEB(0, 0.03, 0.005), - ModelTarget[], + PlantSimEngine.ModelTarget[], soil_target, scene_status, first(meteo), diff --git a/test/test-mapping.jl b/test/test-mapping.jl index 09c9a6e2b..9c86d6a6e 100755 --- a/test/test-mapping.jl +++ b/test/test-mapping.jl @@ -1,6 +1,6 @@ -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ # inputs @@ -10,7 +10,7 @@ mapping = ModelMapping( :carbon_allocation => [:Leaf, :Internode] ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], ), @@ -21,7 +21,7 @@ mapping = ModelMapping( Status(TT=10.0) ), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], # Notice we provide :Soil, not [:Soil], so a single value is expected here @@ -75,12 +75,12 @@ end @test Set(keys(mapping_from_pairs)) == Set(keys(mapping)) mapping_with_specs = PlantSimEngine.ModelMapping( - :Scene => (ModelSpec(ToyDegreeDaysCumulModel()) |> TimeStepModel(ClockSpec(24.0, 1.0)),), - :Soil => (ModelSpec(ToySoilWaterModel()) |> TimeStepModel(ClockSpec(24.0, 1.0)),), + :Scene => (ModelSpec(ToyDegreeDaysCumulModel()) |> TimeStep(ClockSpec(24.0, 1.0)),), + :Soil => (ModelSpec(ToySoilWaterModel()) |> TimeStep(ClockSpec(24.0, 1.0)),), :Leaf => ( ModelSpec(ToyAssimModel()) |> - MultiScaleModel([:soil_water_content => (:Soil => :soil_water_content)]) |> - TimeStepModel(1.0), + PlantSimEngine.MultiScaleModel([:soil_water_content => (:Soil => :soil_water_content)]) |> + TimeStep(1.0), ), ) @test mapping_with_specs isa PlantSimEngine.ModelMapping @@ -108,7 +108,7 @@ end @test outputs(mapping_struct) == outputs(Dict(mapping_struct)) @test variables(mapping_struct) == variables(Dict(mapping_struct)) - ModelMapping_scale = ModelMapping( + ModelMapping_scale = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), status=(var1=1.0, var2=2.0) @@ -149,7 +149,7 @@ end missing_scale_mapping = Dict( :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content)], ), @@ -159,7 +159,7 @@ end missing_source_variable_mapping = Dict( :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content)], ), @@ -184,7 +184,7 @@ end @test_throws "duplicate process(es)" PlantSimEngine.ModelMapping(duplicate_process_mapping) meteo = Atmosphere(T=20.0, Wind=1.0, Rh=0.65) - models_single_scale = ModelMapping( + models_single_scale = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -244,7 +244,7 @@ end @testset "check_statuses_contain_no_remaining_vectors behaviour" begin meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18) - mapping_with_vector = ModelMapping( + mapping_with_vector = PlantSimEngine.ModelMapping( :Scale => (ToyAssimGrowthModel(0.0, 0.0, 0.0), ToyCAllocationModel(), @@ -256,7 +256,7 @@ end @test !last(PlantSimEngine.check_statuses_contain_no_remaining_vectors(mapping_with_vector)) @test_throws "call the function generate_models_from_status_vectors" PlantSimEngine.GraphSimulation(mtg, mapping_with_vector) - mapping_with_empty_status = ModelMapping( + mapping_with_empty_status = PlantSimEngine.ModelMapping( :Scale => (ToyAssimGrowthModel(0.0, 0.0, 0.0), ToyCAllocationModel(), @@ -273,8 +273,8 @@ end TT_cu_vec = Vector(cumsum(meteo_day.TT)) nsteps = length(meteo_day.TT) - mapping_with_vector = ModelMapping(:Plant => ( - MultiScaleModel( + mapping_with_vector = PlantSimEngine.ModelMapping(:Plant => ( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ # inputs @@ -284,7 +284,7 @@ end :carbon_allocation => [:Leaf, :Internode] ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], ), @@ -295,7 +295,7 @@ end Status(TT=TT_v, carbon_biomass=1.0) ), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], # Notice we provide :Soil, not [:Soil], so a single value is expected here @@ -331,8 +331,8 @@ end for i in nsteps carbon_biomass_vec[i] = 2.0 end - mapping_with_two_vectors = ModelMapping(:Plant => ( - MultiScaleModel( + mapping_with_two_vectors = PlantSimEngine.ModelMapping(:Plant => ( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ # inputs @@ -342,7 +342,7 @@ end :carbon_allocation => [:Leaf, :Internode] ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], ), @@ -353,7 +353,7 @@ end Status(TT=TT_v, carbon_biomass=1.0) ), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], ), diff --git a/test/test-meteo-traits.jl b/test/test-meteo-traits.jl index 1e983ec21..33faac85d 100644 --- a/test/test-meteo-traits.jl +++ b/test/test-meteo-traits.jl @@ -28,7 +28,7 @@ end bound_spec = ModelSpec(MeteoTraitConsumerModel()) |> - MeteoBindings(; CO2=(source=:Ca, reducer=MeanReducer())) + PlantSimEngine.MeteoBindings(; CO2=(source=:Ca, reducer=MeanReducer())) bound_specs = Dict(:Leaf => Dict(:meteo_trait_consumer => bound_spec)) @test_throws "Ca" PlantSimEngine.validate_meteo_inputs( @@ -40,7 +40,7 @@ end (T=20.0, Ca=410.0, duration=Dates.Hour(1)) ) === nothing - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( MeteoTraitConsumerModel(), status=(meteo_seen=0.0,), ) diff --git a/test/test-mtg-dynamic.jl b/test/test-mtg-dynamic.jl index efcfa9962..cf24cb6e9 100644 --- a/test/test-mtg-dynamic.jl +++ b/test/test-mtg-dynamic.jl @@ -10,17 +10,17 @@ meteo = Weather( ] ) -mapping = ModelMapping( +mapping = PlantSimEngine.ModelMapping( :Scene => ToyDegreeDaysCumulModel(), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyLAIModel(), mapped_variables=[ :TT_cu => (:Scene => :TT_cu), ], ), Beer(0.6), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ :carbon_assimilation => [:Leaf], @@ -28,17 +28,17 @@ mapping = ModelMapping( :carbon_allocation => [:Leaf, :Internode] ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], ), ), :Internode => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), mapped_variables=[:TT => (:Scene => :TT),], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyInternodeEmergence(TT_emergence=20.0), mapped_variables=[:TT_cu => (:Scene => :TT_cu)], ), @@ -46,11 +46,11 @@ mapping = ModelMapping( Status(carbon_biomass=1.0) ), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content), :aPPFD => (:Plant => :aPPFD)], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), mapped_variables=[:TT => (:Scene => :TT),], ), @@ -107,48 +107,48 @@ end mapping2 = Dict( :Scene => ( - ModelSpec(ToyDegreeDaysCumulModel()) |> TimeStepModel(daily), + ModelSpec(ToyDegreeDaysCumulModel()) |> TimeStep(daily), ), :Plant => ( ModelSpec(ToyLAIModel()) |> - MultiScaleModel([:TT_cu => (:Scene => :TT_cu)]) |> - TimeStepModel(daily), - ModelSpec(Beer(0.6)) |> TimeStepModel(hourly), + PlantSimEngine.MultiScaleModel([:TT_cu => (:Scene => :TT_cu)]) |> + TimeStep(daily), + ModelSpec(Beer(0.6)) |> TimeStep(hourly), ModelSpec(ToyCAllocationModel()) |> - MultiScaleModel([ + PlantSimEngine.MultiScaleModel([ :carbon_assimilation => [:Leaf], :carbon_demand => [:Leaf, :Internode], :carbon_allocation => [:Leaf, :Internode], ]) |> - InputBindings(; carbon_assimilation=(process=process(ToyAssimModel()), var=:carbon_assimilation, scale=:Leaf, policy=Integrate())) |> - TimeStepModel(daily), + PlantSimEngine.InputBindings(; carbon_assimilation=(process=process(ToyAssimModel()), var=:carbon_assimilation, scale=:Leaf, policy=Integrate())) |> + TimeStep(daily), ModelSpec(ToyPlantRmModel()) |> - MultiScaleModel([:Rm_organs => [:Leaf => :Rm, :Internode => :Rm]]) |> - TimeStepModel(daily), + PlantSimEngine.MultiScaleModel([:Rm_organs => [:Leaf => :Rm, :Internode => :Rm]]) |> + TimeStep(daily), ), :Internode => ( ModelSpec(ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0)) |> - MultiScaleModel([:TT => (:Scene => :TT)]) |> - TimeStepModel(daily), + PlantSimEngine.MultiScaleModel([:TT => (:Scene => :TT)]) |> + TimeStep(daily), # Keep emergence model in the stack (as in the dynamic test), but prevent growth in this scenario. ModelSpec(ToyInternodeEmergence(TT_emergence=1.0e6)) |> - MultiScaleModel([:TT_cu => (:Scene => :TT_cu)]) |> - TimeStepModel(daily), - ModelSpec(ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004)) |> TimeStepModel(daily), + PlantSimEngine.MultiScaleModel([:TT_cu => (:Scene => :TT_cu)]) |> + TimeStep(daily), + ModelSpec(ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004)) |> TimeStep(daily), Status(carbon_biomass=1.0), ), :Leaf => ( ModelSpec(ToyAssimModel()) |> - MultiScaleModel([:soil_water_content => (:Soil => :soil_water_content), :aPPFD => (:Plant => :aPPFD)]) |> - TimeStepModel(hourly), + PlantSimEngine.MultiScaleModel([:soil_water_content => (:Soil => :soil_water_content), :aPPFD => (:Plant => :aPPFD)]) |> + TimeStep(hourly), ModelSpec(ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0)) |> - MultiScaleModel([:TT => (:Scene => :TT)]) |> - TimeStepModel(daily), - ModelSpec(ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025)) |> TimeStepModel(daily), + PlantSimEngine.MultiScaleModel([:TT => (:Scene => :TT)]) |> + TimeStep(daily), + ModelSpec(ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025)) |> TimeStep(daily), Status(carbon_biomass=1.0), ), :Soil => ( - ModelSpec(ToySoilWaterModel()) |> TimeStepModel(daily), + ModelSpec(ToySoilWaterModel()) |> TimeStep(daily), ), ) diff --git a/test/test-mtg-multiscale-cyclic-dep.jl b/test/test-mtg-multiscale-cyclic-dep.jl index 1f92c6311..c12f6912c 100644 --- a/test/test-mtg-multiscale-cyclic-dep.jl +++ b/test/test-mtg-multiscale-cyclic-dep.jl @@ -15,16 +15,16 @@ out_vars = Dict( ) @testset "Cyclic dependency -> error" begin - mapping_cyclic = ModelMapping( + mapping_cyclic = PlantSimEngine.ModelMapping( :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ :carbon_demand => [:Leaf, :Internode], :carbon_allocation => [:Leaf, :Internode] ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], ), @@ -61,16 +61,16 @@ end @testset "Cyclic dependency -> fixed with `PreviousTimeStep`" begin - mapping_nocyclic = ModelMapping( + mapping_nocyclic = PlantSimEngine.ModelMapping( :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ :carbon_demand => [:Leaf, :Internode], :carbon_allocation => [:Leaf, :Internode] ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], ), @@ -78,7 +78,7 @@ end ), :Internode => ( ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), mapped_variables=[PreviousTimeStep(:carbon_biomass),], #! this is where we break the cyclic dependency (first break) ), @@ -86,7 +86,7 @@ end ), :Leaf => ( ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), mapped_variables=[PreviousTimeStep(:carbon_biomass),], #! this is where we break the cyclic dependency (second break) ), @@ -123,10 +123,10 @@ end end @testset "Mutiscale simulation -> cyclic dependency" begin - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( :Scene => ( ToyDegreeDaysCumulModel(), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyLAIfromLeafAreaModel(1.0), mapped_variables=[ :plant_surfaces => [:Plant => :surface], @@ -135,7 +135,7 @@ end Beer(0.6), ), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantLeafSurfaceModel(), mapped_variables=[PreviousTimeStep(:leaf_surfaces) => [:Leaf => :surface],], #! We use PreviousTimeStep to break the cyclic dependency between the LAI and the leaf surface @@ -143,41 +143,41 @@ end # will be the one from the previous time-step, and at the end of the time-step we will update # the leaf surface. ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyLightPartitioningModel(), mapped_variables=[ :aPPFD_larger_scale => (:Scene => :aPPFD), :total_surface => (:Scene => :total_surface) ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[ :soil_water_content => (:Soil => :soil_water_content), ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ :carbon_demand => [:Leaf, :Internode], :carbon_allocation => [:Leaf, :Internode] ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], ), ), :Internode => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), mapped_variables=[:TT => (:Scene => :TT),], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyInternodeEmergence(TT_emergence=20.0), mapped_variables=[:TT_cu => (:Scene => :TT_cu)], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), mapped_variables=[PreviousTimeStep(:carbon_biomass),], #! this is where we break the cyclic dependency (first break) ), @@ -185,11 +185,11 @@ end Status(carbon_biomass=0.0) ), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), mapped_variables=[:TT => (:Scene => :TT),], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), mapped_variables=[PreviousTimeStep(:carbon_biomass),], #! this is where we break the cyclic dependency (first break) ), diff --git a/test/test-mtg-multiscale.jl b/test/test-mtg-multiscale.jl index 922700f93..3c700a792 100644 --- a/test/test-mtg-multiscale.jl +++ b/test/test-mtg-multiscale.jl @@ -14,7 +14,7 @@ meteo = Weather( var2 = 0.3 leaf[:var2] = var2 - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( :Leaf => ( Process1Model(1.0), Process2Model(), @@ -31,9 +31,9 @@ meteo = Weather( end # A mapping that actually works (same as before but with the init for TT): -mapping_1 = ModelMapping( +mapping_1 = PlantSimEngine.ModelMapping( :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ # inputs @@ -43,7 +43,7 @@ mapping_1 = ModelMapping( :carbon_allocation => [:Leaf, :Internode] ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], ), @@ -54,7 +54,7 @@ mapping_1 = ModelMapping( Status(TT=10.0) ), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], # Notice we provide :Soil, not [:Soil], so a single value is expected here @@ -158,7 +158,7 @@ end @test isa(organs_statuses[:Leaf][1][:carbon_demand], Float32) @test isa(organs_statuses[:Soil][1][:soil_water_content], Float32) - mapping_promoted = ModelMapping(Dict(mapping_1); type_promotion=Dict(Float64 => Float32, Vector{Float64} => Vector{Float32})) + mapping_promoted = PlantSimEngine.ModelMapping(Dict(mapping_1); type_promotion=Dict(Float64 => Float32, Vector{Float64} => Vector{Float32})) sim_promoted = PlantSimEngine.GraphSimulation( mtg_init, mapping_promoted; @@ -237,9 +237,9 @@ end # Testing the mappings: @testset "Mapping: missing initialisation" begin - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( :Plant => - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ # inputs @@ -251,7 +251,7 @@ end ), :Internode => ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], # Notice we provide :Soil, not [:Soil], so a single value is expected here @@ -278,9 +278,9 @@ end end @testset "Mapping: missing organ in mapping (Soil)" begin - @test_throws "missing scale `Soil`" ModelMapping( + @test_throws "missing scale `Soil`" PlantSimEngine.ModelMapping( :Plant => - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ # inputs @@ -292,7 +292,7 @@ end ), :Internode => ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], # Notice we provide :Soil, not [:Soil], so a single value is expected here @@ -313,9 +313,9 @@ mtg_var = let end @testset "Mapping: missing model at other scale (soil_water_content) + missing init + var1 from MTG" begin - @test_throws "not available at scale `Soil`" ModelMapping( + @test_throws "not available at scale `Soil`" PlantSimEngine.ModelMapping( :Plant => - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ # inputs @@ -327,7 +327,7 @@ end ), :Internode => ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], # Notice we provide :Soil, not [:Soil], so a single value is expected here @@ -342,9 +342,9 @@ end end @testset "Mapping: missing init + var1 from MTG" begin - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( :Plant => - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ # inputs @@ -356,7 +356,7 @@ end ), :Internode => ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :var3),], # Notice we provide :Soil, not [:Soil], so a single value is expected here @@ -384,7 +384,7 @@ end @testset "run! on MTG: simple mapping" begin #out = @test_nowarn run!(mtg, Dict(:Soil => (ToySoilWaterModel(),)), meteo) nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg, ModelMapping(:Soil => (ToySoilWaterModel(),)), nsteps=nsteps, check=true, outputs=nothing) + sim = PlantSimEngine.GraphSimulation(mtg, PlantSimEngine.ModelMapping(:Soil => (ToySoilWaterModel(),)), nsteps=nsteps, check=true, outputs=nothing) out = run!(sim,meteo) @test sim.statuses[:Soil][1].node == soil @@ -394,7 +394,7 @@ end @test collect(keys(sim.dependency_graph.roots))[1] == Pair(:Soil, :soil_water) @test sim.graph == mtg - leaf_mapping = ModelMapping(:Leaf => (ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), Status(TT=10.0))) + leaf_mapping = PlantSimEngine.ModelMapping(:Leaf => (ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), Status(TT=10.0))) #out = run!(mtg, leaf_mapping, meteo) nsteps = PlantSimEngine.get_nsteps(meteo) @@ -412,9 +412,9 @@ end # A mapping with all different types of mapping (single, multi-scale, model as is, or tuple of): @testset "run! on MTG with complete mapping (missing init)" begin - mapping_all = ModelMapping( + mapping_all = PlantSimEngine.ModelMapping( :Plant => - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ # inputs @@ -426,7 +426,7 @@ end ), :Internode => ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], # Notice we provide :Soil, not [:Soil], so a single value is expected here @@ -541,7 +541,7 @@ end # Here we initialise var1 to a constant value: @testset "MTG initialisation" begin var1 = 1.0 - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( :Leaf => ( Process1Model(1.0), Process2Model(), @@ -556,7 +556,7 @@ end @test_throws "Variable `var2` is not computed by any model, not initialised by the user in the status, and not found in the MTG at scale Leaf (checked for MTG node 5)." PlantSimEngine.init_simulation(mtg, mapping) end - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( :Leaf => ( Process1Model(1.0), Process2Model(), @@ -577,9 +577,9 @@ end end @testset "MTG with complex mapping" begin - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ # inputs @@ -589,7 +589,7 @@ end :carbon_allocation => [:Leaf, :Internode] ] ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], ), @@ -600,7 +600,7 @@ end Status(TT=10.0, carbon_biomass=1.0) ), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], # Notice we provide :Soil, not [:Soil], so a single value is expected here @@ -634,9 +634,9 @@ end end @testset "MTG with dynamic output variables" begin - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyCAllocationModel(), mapped_variables=[ # inputs @@ -646,7 +646,7 @@ end :carbon_allocation => [:Leaf, :Internode] ], ), - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyPlantRmModel(), mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], ), @@ -657,7 +657,7 @@ end Status(TT=10.0, carbon_biomass=1.0) ), :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=ToyAssimModel(), mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], # Notice we provide :Soil, not [:Soil], so a single value is expected here diff --git a/test/test-multirate-output-export.jl b/test/test-multirate-output-export.jl index 0c373518f..f28f38b4f 100644 --- a/test/test-multirate-output-export.jl +++ b/test/test-multirate-output-export.jl @@ -97,10 +97,10 @@ end meteo4 = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65)], 4)) # Stream-only producer remains exportable when process is explicit. - mapping_stream = ModelMapping( + mapping_stream = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRExportSourceModel(Ref(0))) |> - TimeStepModel(1.0) |> + TimeStep(1.0) |> OutputRouting(; X=:stream_only), ), ) @@ -131,10 +131,10 @@ end ) # Canonical routing allows omitting process in requests. - mapping_canonical = ModelMapping( + mapping_canonical = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRExportSourceModel(Ref(0))) |> - TimeStepModel(1.0), + TimeStep(1.0), ), ) @@ -147,14 +147,27 @@ end ) exported_auto = collect_outputs(sim_canonical; sink=DataFrame) @test exported_auto[:x_auto][:, :value] == [1.0, 2.0, 3.0, 4.0] + @test_throws "`application=` in `OutputRequest` is only supported" run!( + sim_canonical, + meteo4, + executor=SequentialEx(), + tracked_outputs=[ + OutputRequest( + :Leaf, + :X; + name=:x_application_unsupported, + application=:leaf_source, + ), + ], + ) # Optional direct export return from run! on GraphSimulation. sim_direct = PlantSimEngine.GraphSimulation( mtg, - ModelMapping( + PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRExportSourceModel(Ref(0))) |> - TimeStepModel(1.0), + TimeStep(1.0), ), ), nsteps=4, @@ -174,10 +187,10 @@ end # Optional direct export return from run! on MTG + mapping entry point. out_status_mtg, out_requested_mtg = run!( mtg, - ModelMapping( + PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRExportSourceModel(Ref(0))) |> - TimeStepModel(1.0), + TimeStep(1.0), ), ), meteo4; @@ -198,17 +211,17 @@ end Atmosphere(T=30.0, Wind=1.0, Rh=0.65), Atmosphere(T=40.0, Wind=1.0, Rh=0.65), ]) - dynamic_mapping = ModelMapping( + dynamic_mapping = PlantSimEngine.ModelMapping( :Plant => ( ModelSpec(MRDynamicGrowModel()) |> - TimeStepModel(1.0) |> - ScopeModel(:plant), + TimeStep(1.0) |> + PlantSimEngine.ScopeModel(:plant), ), :Leaf => ( ModelSpec(MRDynamicLeafSourceModel()) |> - MultiScaleModel([:nleaves => (:Plant => :nleaves)]) |> - TimeStepModel(1.0) |> - ScopeModel(:plant), + PlantSimEngine.MultiScaleModel([:nleaves => (:Plant => :nleaves)]) |> + TimeStep(1.0) |> + PlantSimEngine.ScopeModel(:plant), ), ) dynamic_sim = PlantSimEngine.GraphSimulation( @@ -238,17 +251,17 @@ end # vectors when temporal outputs are exported. many_mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) many_plant = Node(many_mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - many_mapping = ModelMapping( + many_mapping = PlantSimEngine.ModelMapping( :Plant => ( ModelSpec(MRDynamicGrowManyModel()) |> - TimeStepModel(1.0) |> - ScopeModel(_mr_custom_plant_scope), + TimeStep(1.0) |> + PlantSimEngine.ScopeModel(_mr_custom_plant_scope), ), :Leaf => ( ModelSpec(MRDynamicLeafSourceModel()) |> - MultiScaleModel([:nleaves => (:Plant => :nleaves)]) |> - TimeStepModel(1.0) |> - ScopeModel(_mr_custom_plant_scope), + PlantSimEngine.MultiScaleModel([:nleaves => (:Plant => :nleaves)]) |> + TimeStep(1.0) |> + PlantSimEngine.ScopeModel(_mr_custom_plant_scope), ), ) many_sim = PlantSimEngine.GraphSimulation( @@ -283,15 +296,15 @@ end meteo8 = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65)], 8)) - mapping_defaults = ModelMapping( + mapping_defaults = PlantSimEngine.ModelMapping( :Leaf => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=MRDefaultLeafSourceModel(Ref(0)), mapped_variables=[:X => (:Plant => :X)], ), ), :Plant => ( - MultiScaleModel( + PlantSimEngine.MultiScaleModel( model=MRDefaultPlantAggModel(), mapped_variables=[:XP => (:Scene => :XP)], ), diff --git a/test/test-multirate-runtime.jl b/test/test-multirate-runtime.jl index 0a6004df6..f47f8e1f9 100644 --- a/test/test-multirate-runtime.jl +++ b/test/test-multirate-runtime.jl @@ -281,7 +281,7 @@ PlantSimEngine.dep(::MRHardParentModel) = (mrhardchild=AbstractMrhardchildModel, PlantSimEngine.inputs_(::MRHardParentModel) = NamedTuple() PlantSimEngine.outputs_(::MRHardParentModel) = (A=-Inf,) function PlantSimEngine.run!(::MRHardParentModel, models, status, meteo, constants=nothing, extra=nothing) - run_target!(models, status, :mrhardchild; meteo=meteo, constants=constants, extra=extra) + PlantSimEngine.run_target!(models, status, :mrhardchild; meteo=meteo, constants=constants, extra=extra) status.A = 5.0 end @@ -387,14 +387,14 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( internode = Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) Node(internode, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - mapping_ok = ModelMapping( + mapping_ok = PlantSimEngine.ModelMapping( :Leaf => ( MRSourceModel(), ModelSpec(MROverwriteModel()) |> OutputRouting(; C=:stream_only), ModelSpec(MRConsumerModel()) |> - InputBindings(; C=(process=:mrsource, var=:S)), + PlantSimEngine.InputBindings(; C=(process=:mrsource, var=:S)), ModelSpec(MRDirectConsumerModel()) |> - InputBindings(; S=(process=:mrsource, var=:S)), + PlantSimEngine.InputBindings(; S=(process=:mrsource, var=:S)), MRAutoSameNameModel(), ), ) @@ -435,12 +435,12 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test sim_ok.temporal_state.caches[key_dir].v == 10.0 @test sim_ok.temporal_state.caches[key_auto].v == 10.0 - mapping_renamed_auto = ModelMapping( + mapping_renamed_auto = PlantSimEngine.ModelMapping( :Leaf => MRSourceModel(), :Plant => ( ModelSpec(MRConsumerModel()) |> - MultiScaleModel([:C => (:Leaf => :S)]) |> - TimeStepModel(ClockSpec(1.0, 0.0)), + PlantSimEngine.MultiScaleModel([:C => (:Leaf => :S)]) |> + TimeStep(ClockSpec(1.0, 0.0)), ), ) sim_renamed_auto = PlantSimEngine.GraphSimulation(mtg, mapping_renamed_auto, nsteps=1, check=true) @@ -450,10 +450,10 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( )) @test PlantSimEngine._candidate_producers(consumer_node, :C) == [(:mrsource, :S)] - mapping_conflict = ModelMapping( + mapping_conflict = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRConflict1Model()) |> TimeStepModel(1.0), - ModelSpec(MRConflict2Model()) |> TimeStepModel(1.0), + ModelSpec(MRConflict1Model()) |> TimeStep(1.0), + ModelSpec(MRConflict2Model()) |> TimeStep(1.0), ), ) # Expectation 5: two canonical writers of the same output are rejected at graph construction. @@ -467,11 +467,11 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Expectation 6: models run at different clocks; slower model holds last value between runs. source_counter = Ref(0) - mapping_clock_trait = ModelMapping( + mapping_clock_trait = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRClockSourceModel(source_counter)) |> TimeStepModel(1.0), + ModelSpec(MRClockSourceModel(source_counter)) |> TimeStep(1.0), ModelSpec(MRClockConsumerModel()) |> - InputBindings(; X=(process=:mrclocksource, var=:X)), + PlantSimEngine.InputBindings(; X=(process=:mrclocksource, var=:X)), ), ) sim_clock_trait = PlantSimEngine.GraphSimulation(mtg, mapping_clock_trait, nsteps=4, check=true, outputs=Dict(:Leaf => (:X, :Y))) @@ -495,14 +495,14 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test sim_clock_trait.temporal_state.last_run[ModelKey(scope, :Leaf, :mrclocksource)] == 4.0 @test sim_clock_trait.temporal_state.last_run[ModelKey(scope, :Leaf, :mrclockconsumer)] == 3.0 - # Expectation 7: TimeStepModel override takes precedence over model timespec. + # Expectation 7: TimeStep override takes precedence over model timespec. source_counter_2 = Ref(0) - mapping_clock_override = ModelMapping( + mapping_clock_override = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRClockSourceModel(source_counter_2)) |> TimeStepModel(1.0), + ModelSpec(MRClockSourceModel(source_counter_2)) |> TimeStep(1.0), ModelSpec(MRClockConsumerModel()) |> - TimeStepModel(3.0) |> - InputBindings(; X=(process=:mrclocksource, var=:X)), + TimeStep(3.0) |> + PlantSimEngine.InputBindings(; X=(process=:mrclocksource, var=:X)), ), ) sim_clock_override = PlantSimEngine.GraphSimulation(mtg, mapping_clock_override, nsteps=4, check=true, outputs=Dict(:Leaf => (:X, :Y))) @@ -514,22 +514,22 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test sim_clock_override.temporal_state.last_run[ModelKey(scope, :Leaf, :mrclockconsumer)] == 3.0 # Expectation 7b: non-sequential executors warn and fall back to sequential behavior. - mapping_clock_fallback_seq = ModelMapping( + mapping_clock_fallback_seq = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRClockSourceModel(Ref(0))) |> TimeStepModel(1.0), + ModelSpec(MRClockSourceModel(Ref(0))) |> TimeStep(1.0), ModelSpec(MRClockConsumerModel()) |> - InputBindings(; X=(process=:mrclocksource, var=:X)), + PlantSimEngine.InputBindings(; X=(process=:mrclocksource, var=:X)), ), ) sim_clock_fallback_seq = PlantSimEngine.GraphSimulation(mtg, mapping_clock_fallback_seq, nsteps=4, check=true, outputs=Dict(:Leaf => (:X, :Y))) out_fallback_seq = run!(sim_clock_fallback_seq, meteo4, executor=SequentialEx()) out_fallback_seq_df = convert_outputs(out_fallback_seq, DataFrame) - mapping_clock_fallback_threaded = ModelMapping( + mapping_clock_fallback_threaded = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRClockSourceModel(Ref(0))) |> TimeStepModel(1.0), + ModelSpec(MRClockSourceModel(Ref(0))) |> TimeStep(1.0), ModelSpec(MRClockConsumerModel()) |> - InputBindings(; X=(process=:mrclocksource, var=:X)), + PlantSimEngine.InputBindings(; X=(process=:mrclocksource, var=:X)), ), ) sim_clock_fallback_threaded = PlantSimEngine.GraphSimulation(mtg, mapping_clock_fallback_threaded, nsteps=4, check=true, outputs=Dict(:Leaf => (:X, :Y))) @@ -543,15 +543,15 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Expectation 8: cross-scale hold-last resolution works with different clocks. # Leaf producer runs each step; Plant consumer runs every 2 steps (1, 3) and reads Leaf XS through multiscale mapping. source_counter_3 = Ref(0) - mapping_cross = ModelMapping( + mapping_cross = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRCrossSourceModel(source_counter_3)) |> TimeStepModel(1.0), + ModelSpec(MRCrossSourceModel(source_counter_3)) |> TimeStep(1.0), ), :Plant => ( ModelSpec(MRCrossConsumerModel()) |> - MultiScaleModel([:XS => [:Leaf]]) |> - TimeStepModel(ClockSpec(2.0, 1.0)) |> - InputBindings(; XS=(process=:mrcrosssource, var=:XS, scale=:Leaf)), + PlantSimEngine.MultiScaleModel([:XS => [:Leaf]]) |> + TimeStep(ClockSpec(2.0, 1.0)) |> + PlantSimEngine.InputBindings(; XS=(process=:mrcrosssource, var=:XS, scale=:Leaf)), ), ) sim_cross = PlantSimEngine.GraphSimulation(mtg, mapping_cross, nsteps=4, check=true, outputs=Dict(:Leaf => (:XS,), :Plant => (:XP,))) @@ -563,14 +563,14 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Expectation 8a: cross-scale producer is inferred automatically when unique. source_counter_3_auto = Ref(0) - mapping_cross_auto = ModelMapping( + mapping_cross_auto = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRCrossSourceModel(source_counter_3_auto)) |> TimeStepModel(1.0), + ModelSpec(MRCrossSourceModel(source_counter_3_auto)) |> TimeStep(1.0), ), :Plant => ( ModelSpec(MRCrossConsumerModel()) |> - MultiScaleModel([:XS => [:Leaf]]) |> - TimeStepModel(ClockSpec(2.0, 1.0)), + PlantSimEngine.MultiScaleModel([:XS => [:Leaf]]) |> + TimeStep(ClockSpec(2.0, 1.0)), ), ) sim_cross_auto = PlantSimEngine.GraphSimulation(mtg, mapping_cross_auto, nsteps=4, check=true, outputs=Dict(:Leaf => (:XS,), :Plant => (:XP,))) @@ -591,16 +591,16 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( Node(internode2_b, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) source_counter_scoped = Ref(0) - mapping_scoped = ModelMapping( + mapping_scoped = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRCrossSourceModel(source_counter_scoped)) |> TimeStepModel(1.0) |> ScopeModel(:plant), + ModelSpec(MRCrossSourceModel(source_counter_scoped)) |> TimeStep(1.0) |> PlantSimEngine.ScopeModel(:plant), ), :Plant => ( ModelSpec(MRCrossConsumerModel()) |> - MultiScaleModel([:XS => [:Leaf]]) |> - TimeStepModel(1.0) |> - ScopeModel(:plant) |> - InputBindings(; XS=(process=:mrcrosssource, var=:XS, scale=:Leaf)), + PlantSimEngine.MultiScaleModel([:XS => [:Leaf]]) |> + TimeStep(1.0) |> + PlantSimEngine.ScopeModel(:plant) |> + PlantSimEngine.InputBindings(; XS=(process=:mrcrosssource, var=:XS, scale=:Leaf)), ), ) sim_scoped = PlantSimEngine.GraphSimulation(scene2, mapping_scoped, nsteps=1, check=true, outputs=Dict(:Plant => (:XP,), :Leaf => (:XS,))) @@ -629,12 +629,12 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Consumer runs every step and receives XI through Interpolate: # expected YI over time is [1, 1, 3, 4, 5]. interp_counter = Ref(0) - mapping_interp = ModelMapping( + mapping_interp = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRInterpSourceModel(interp_counter)) |> TimeStepModel(ClockSpec(2.0, 1.0)), + ModelSpec(MRInterpSourceModel(interp_counter)) |> TimeStep(ClockSpec(2.0, 1.0)), ModelSpec(MRInterpConsumerModel()) |> - TimeStepModel(1.0) |> - InputBindings(; XI=(process=:mrinterpsource, var=:XI, policy=Interpolate())), + TimeStep(1.0) |> + PlantSimEngine.InputBindings(; XI=(process=:mrinterpsource, var=:XI, policy=Interpolate())), ), ) meteo5 = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65)], 5)) @@ -650,12 +650,12 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # - at t=3: window [2,3] => mean([2,3]) = 2.5 # Output YA over time is therefore [1, 1, 2.5, 2.5]. agg_counter = Ref(0) - mapping_agg = ModelMapping( + mapping_agg = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRAggSourceModel(agg_counter)) |> TimeStepModel(1.0), + ModelSpec(MRAggSourceModel(agg_counter)) |> TimeStep(1.0), ModelSpec(MRAggConsumerModel()) |> - TimeStepModel(ClockSpec(2.0, 1.0)) |> - InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Aggregate())), + TimeStep(ClockSpec(2.0, 1.0)) |> + PlantSimEngine.InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Aggregate())), ), ) sim_agg = PlantSimEngine.GraphSimulation(mtg, mapping_agg, nsteps=4, check=true, outputs=Dict(:Leaf => (:YA,))) @@ -673,12 +673,12 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Using hourly meteo rows, RadiationEnergy integrates XA values as value * 3600 s. meteo4_hourly = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65, duration=Dates.Hour(1))], 4)) agg_counter_duration = Ref(0) - mapping_agg_duration = ModelMapping( + mapping_agg_duration = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRAggSourceModel(agg_counter_duration)) |> TimeStepModel(1.0), + ModelSpec(MRAggSourceModel(agg_counter_duration)) |> TimeStep(1.0), ModelSpec(MRAggConsumerModel()) |> - TimeStepModel(ClockSpec(2.0, 1.0)) |> - InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Integrate(RadiationEnergy()))), + TimeStep(ClockSpec(2.0, 1.0)) |> + PlantSimEngine.InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Integrate(RadiationEnergy()))), ), ) sim_agg_duration = PlantSimEngine.GraphSimulation(mtg, mapping_agg_duration, nsteps=4, check=true, outputs=Dict(:Leaf => (:YA,))) @@ -688,12 +688,12 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Expectation 10aa: custom two-argument reducers can use durations directly. agg_counter_duration_callable = Ref(0) - mapping_agg_duration_callable = ModelMapping( + mapping_agg_duration_callable = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRAggSourceModel(agg_counter_duration_callable)) |> TimeStepModel(1.0), + ModelSpec(MRAggSourceModel(agg_counter_duration_callable)) |> TimeStep(1.0), ModelSpec(MRAggConsumerModel()) |> - TimeStepModel(ClockSpec(2.0, 1.0)) |> - InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Integrate((vals, durations) -> sum(vals .* durations)))), + TimeStep(ClockSpec(2.0, 1.0)) |> + PlantSimEngine.InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Integrate((vals, durations) -> sum(vals .* durations)))), ), ) sim_agg_duration_callable = PlantSimEngine.GraphSimulation( @@ -711,10 +711,10 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Source XT=[1,2,3,4], consumer runs at t=1,3 and has no explicit InputBindings. # output_policy(XT=Aggregate()) drives YT to [1,1,2.5,2.5]. trait_counter = Ref(0) - mapping_trait_default = ModelMapping( + mapping_trait_default = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRTraitAggSourceModel(trait_counter)) |> TimeStepModel(1.0), - ModelSpec(MRTraitAggConsumerModel()) |> TimeStepModel(ClockSpec(2.0, 1.0)), + ModelSpec(MRTraitAggSourceModel(trait_counter)) |> TimeStep(1.0), + ModelSpec(MRTraitAggConsumerModel()) |> TimeStep(ClockSpec(2.0, 1.0)), ), ) sim_trait_default = PlantSimEngine.GraphSimulation(mtg, mapping_trait_default, nsteps=4, check=true, outputs=Dict(:Leaf => (:YT,))) @@ -727,10 +727,10 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Here source policy declares V1 and V2 as Aggregate, but only V1 is consumed. # Runtime must allocate/integrate only V1 (no stream/horizon for V2). dual_trait_counter = Ref(0) - mapping_trait_partial_use = ModelMapping( + mapping_trait_partial_use = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRTraitDualAggSourceModel(dual_trait_counter)) |> TimeStepModel(1.0), - ModelSpec(MRUsesV1ConsumerModel()) |> TimeStepModel(ClockSpec(2.0, 1.0)), + ModelSpec(MRTraitDualAggSourceModel(dual_trait_counter)) |> TimeStep(1.0), + ModelSpec(MRUsesV1ConsumerModel()) |> TimeStep(ClockSpec(2.0, 1.0)), ), ) sim_trait_partial_use = PlantSimEngine.GraphSimulation(mtg, mapping_trait_partial_use, nsteps=4, check=true, outputs=Dict(:Leaf => (:YV1,))) @@ -751,12 +751,12 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Trait provides Aggregate for V1 and V2. # User overrides V1 to Integrate and adds V1_max as Aggregate(MaxReducer()). dual_trait_counter_priority = Ref(0) - mapping_trait_user_priority = ModelMapping( + mapping_trait_user_priority = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRTraitDualAggSourceModel(dual_trait_counter_priority)) |> TimeStepModel(1.0), + ModelSpec(MRTraitDualAggSourceModel(dual_trait_counter_priority)) |> TimeStep(1.0), ModelSpec(MRDualPolicyConsumerModel()) |> - TimeStepModel(ClockSpec(2.0, 1.0)) |> - InputBindings( + TimeStep(ClockSpec(2.0, 1.0)) |> + PlantSimEngine.InputBindings( ; V1=(process=:mrtraitdualaggsource, var=:V1, policy=Integrate()), V1_max=(process=:mrtraitdualaggsource, var=:V1, policy=Aggregate(MaxReducer())), @@ -784,12 +784,12 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Expectation 10c: explicit InputBindings policy overrides producer output_policy. trait_counter_override = Ref(0) - mapping_trait_override = ModelMapping( + mapping_trait_override = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRTraitAggSourceModel(trait_counter_override)) |> TimeStepModel(1.0), + ModelSpec(MRTraitAggSourceModel(trait_counter_override)) |> TimeStep(1.0), ModelSpec(MRTraitAggConsumerModel()) |> - TimeStepModel(ClockSpec(2.0, 1.0)) |> - InputBindings(; XT=(process=:mrtraitaggsource, var=:XT, policy=Integrate())), + TimeStep(ClockSpec(2.0, 1.0)) |> + PlantSimEngine.InputBindings(; XT=(process=:mrtraitaggsource, var=:XT, policy=Integrate())), ), ) sim_trait_override = PlantSimEngine.GraphSimulation(mtg, mapping_trait_override, nsteps=4, check=true, outputs=Dict(:Leaf => (:YT,))) @@ -801,12 +801,12 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Source XA=[1,2,3,4], consumer runs at t=1,3 with reducer=MaxReducer(). # YA over time is [1,1,3,3]. agg_counter_max = Ref(0) - mapping_agg_max = ModelMapping( + mapping_agg_max = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRAggSourceModel(agg_counter_max)) |> TimeStepModel(1.0), + ModelSpec(MRAggSourceModel(agg_counter_max)) |> TimeStep(1.0), ModelSpec(MRAggConsumerModel()) |> - TimeStepModel(ClockSpec(2.0, 1.0)) |> - InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Aggregate(MaxReducer()))), + TimeStep(ClockSpec(2.0, 1.0)) |> + PlantSimEngine.InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Aggregate(MaxReducer()))), ), ) sim_agg_max = PlantSimEngine.GraphSimulation(mtg, mapping_agg_max, nsteps=4, check=true, outputs=Dict(:Leaf => (:YA,))) @@ -818,12 +818,12 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Source XA=[1,2,3,4], consumer runs at t=1,3 with reducer=max-min. # YA over time is [0,0,1,1]. agg_counter_callable = Ref(0) - mapping_integrate_callable = ModelMapping( + mapping_integrate_callable = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRAggSourceModel(agg_counter_callable)) |> TimeStepModel(1.0), + ModelSpec(MRAggSourceModel(agg_counter_callable)) |> TimeStep(1.0), ModelSpec(MRAggConsumerModel()) |> - TimeStepModel(ClockSpec(2.0, 1.0)) |> - InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Integrate(vals -> maximum(vals) - minimum(vals)))), + TimeStep(ClockSpec(2.0, 1.0)) |> + PlantSimEngine.InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Integrate(vals -> maximum(vals) - minimum(vals)))), ), ) sim_integrate_callable = PlantSimEngine.GraphSimulation(mtg, mapping_integrate_callable, nsteps=4, check=true, outputs=Dict(:Leaf => (:YA,))) @@ -835,12 +835,12 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Source runs at t=1,3,5 with values 1,3,5. Consumer runs each step. # With Interpolate(mode=:hold, extrapolation=:hold), YI is [1,1,3,3,5,5]. interp_counter_hold = Ref(0) - mapping_interp_hold = ModelMapping( + mapping_interp_hold = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRInterpSourceModel(interp_counter_hold)) |> TimeStepModel(ClockSpec(2.0, 1.0)), + ModelSpec(MRInterpSourceModel(interp_counter_hold)) |> TimeStep(ClockSpec(2.0, 1.0)), ModelSpec(MRInterpConsumerModel()) |> - TimeStepModel(1.0) |> - InputBindings(; XI=(process=:mrinterpsource, var=:XI, policy=Interpolate(; mode=:hold, extrapolation=:hold))), + TimeStep(1.0) |> + PlantSimEngine.InputBindings(; XI=(process=:mrinterpsource, var=:XI, policy=Interpolate(; mode=:hold, extrapolation=:hold))), ), ) meteo6 = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65)], 6)) @@ -853,12 +853,12 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Source runs at t=1 and t=25 (ClockSpec(24,1)), consumer runs every step. # YD should stay at 1 for t=1..24, then switch to 2 at t=25. daily_counter = Ref(0) - mapping_daily_hourly = ModelMapping( + mapping_daily_hourly = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRDailySourceModel(daily_counter)) |> TimeStepModel(ClockSpec(24.0, 1.0)), + ModelSpec(MRDailySourceModel(daily_counter)) |> TimeStep(ClockSpec(24.0, 1.0)), ModelSpec(MRHourlyFromDailyConsumerModel()) |> - TimeStepModel(1.0) |> - InputBindings(; XD=(process=:mrdailysource, var=:XD)), + TimeStep(1.0) |> + PlantSimEngine.InputBindings(; XD=(process=:mrdailysource, var=:XD)), ), ) meteo26 = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65)], 26)) @@ -873,12 +873,12 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Expectation 15: period-based timestep uses timeline base-step conversion. # Meteo has duration=Hour(1), source uses Day(1) => runs on t=1 and t=25. daily_counter_period = Ref(0) - mapping_daily_period = ModelMapping( + mapping_daily_period = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRDailySourceModel(daily_counter_period)) |> TimeStepModel(Dates.Day(1)), + ModelSpec(MRDailySourceModel(daily_counter_period)) |> TimeStep(Dates.Day(1)), ModelSpec(MRHourlyFromDailyConsumerModel()) |> - TimeStepModel(1.0) |> - InputBindings(; XD=(process=:mrdailysource, var=:XD)), + TimeStep(1.0) |> + PlantSimEngine.InputBindings(; XD=(process=:mrdailysource, var=:XD)), ), ) meteo_hourly = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65, duration=Dates.Hour(1))], 26)) @@ -892,12 +892,12 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Expectation 15b: meteo duration can be a CompoundPeriod. # With duration=CompoundPeriod(Minute(30)), Day(1) runs at t=1 and t=49. daily_counter_compound = Ref(0) - mapping_daily_compound = ModelMapping( + mapping_daily_compound = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRDailySourceModel(daily_counter_compound)) |> TimeStepModel(Dates.Day(1)), + ModelSpec(MRDailySourceModel(daily_counter_compound)) |> TimeStep(Dates.Day(1)), ModelSpec(MRHourlyFromDailyConsumerModel()) |> - TimeStepModel(1.0) |> - InputBindings(; XD=(process=:mrdailysource, var=:XD)), + TimeStep(1.0) |> + PlantSimEngine.InputBindings(; XD=(process=:mrdailysource, var=:XD)), ), ) meteo_halfhourly = Weather( @@ -911,9 +911,9 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test sim_daily_compound.temporal_state.last_run[ModelKey(scope, :Leaf, :mrdailysource)] == 49.0 # Expectation 15c: missing meteo duration is rejected. - mapping_no_duration = ModelMapping( + mapping_no_duration = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRDailySourceModel(Ref(0))) |> TimeStepModel(Dates.Day(1)), + ModelSpec(MRDailySourceModel(Ref(0))) |> TimeStep(Dates.Day(1)), ), ) sim_no_duration = PlantSimEngine.GraphSimulation(mtg, mapping_no_duration, nsteps=1, check=true, outputs=Dict(:Leaf => (:XD,))) @@ -937,9 +937,9 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test_throws "strictly positive" run!(sim_no_duration, meteo_zero_duration, executor=SequentialEx()) # Expectation 16: model timesteps shorter than meteo base step are rejected. - mapping_substep_period = ModelMapping( + mapping_substep_period = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRDailySourceModel(Ref(0))) |> TimeStepModel(Dates.Minute(30)), + ModelSpec(MRDailySourceModel(Ref(0))) |> TimeStep(Dates.Minute(30)), ), ) sim_substep_period = PlantSimEngine.GraphSimulation(mtg, mapping_substep_period, nsteps=26, check=true, outputs=Dict(:Leaf => (:XD,))) @@ -950,11 +950,11 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( range_counter_a = Ref(0) range_counter_b = Ref(0) range_counter_forced = Ref(0) - mapping_timestep_hints = ModelMapping( + mapping_timestep_hints = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRRangeHintAModel(range_counter_a)), ModelSpec(MRRangeHintBModel(range_counter_b)), - ModelSpec(MRRangeHintForcedModel(range_counter_forced)) |> TimeStepModel(Dates.Hour(2)), + ModelSpec(MRRangeHintForcedModel(range_counter_forced)) |> TimeStep(Dates.Hour(2)), ), ) meteo8h = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65, duration=Dates.Hour(1))], 8)) @@ -977,7 +977,7 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Expectation 17b: required timestep bounds are enforced for meteo-derived clocks. strict_counter = Ref(0) - mapping_timestep_hints_strict = ModelMapping( + mapping_timestep_hints_strict = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRRangeHintStrictModel(strict_counter)), ), @@ -994,9 +994,9 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( # Expectation 17c: warn if no model runs at meteo base timestep. no_base_counter = Ref(0) - mapping_no_base_dt = ModelMapping( + mapping_no_base_dt = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRClockSourceModel(no_base_counter)) |> TimeStepModel(ClockSpec(2.0, 1.0)), + ModelSpec(MRClockSourceModel(no_base_counter)) |> TimeStep(ClockSpec(2.0, 1.0)), ), ) sim_no_base_dt = PlantSimEngine.GraphSimulation(mtg, mapping_no_base_dt, nsteps=4, check=true, outputs=Dict(:Leaf => (:X,))) @@ -1004,10 +1004,10 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( if _HAS_METEO_SAMPLER_API # Expectation 18: meteo is sampled at model clock using default weather aggregation. - mapping_meteo_default = ModelMapping( + mapping_meteo_default = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRMeteoDailyConsumerModel()) |> - TimeStepModel(ClockSpec(2.0, 1.0)), + TimeStep(ClockSpec(2.0, 1.0)), ), ) meteo_mr = Weather([ @@ -1045,11 +1045,11 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test isapprox(out_meteo_default_mtg_df[:Leaf][:, :MSWq][3], 1.8; atol=1.0e-9) # Expectation 19: meteo bindings allow custom reducers and variable remapping. - mapping_meteo_custom = ModelMapping( + mapping_meteo_custom = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRMeteoCustomConsumerModel()) |> - TimeStepModel(ClockSpec(2.0, 1.0)) |> - MeteoBindings( + TimeStep(ClockSpec(2.0, 1.0)) |> + PlantSimEngine.MeteoBindings( ; Ri_SW_f=RadiationEnergy(), custom_peak=(source=:custom_var, reducer=MaxReducer()), @@ -1075,9 +1075,9 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( ) for h in 1:24 ]) - mapping_meteo_hint = ModelMapping( + mapping_meteo_hint = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRMeteoHintConsumerModel()) |> TimeStepModel(Dates.Day(1)), + ModelSpec(MRMeteoHintConsumerModel()) |> TimeStep(Dates.Day(1)), ), ) sim_meteo_hint = PlantSimEngine.GraphSimulation(mtg, mapping_meteo_hint, nsteps=24, check=true, outputs=Dict(:Leaf => (:HT, :HSWQ))) @@ -1119,11 +1119,11 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( ], )) - mapping_meteo_calendar_current = ModelMapping( + mapping_meteo_calendar_current = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRMeteoDailyConsumerModel()) |> - TimeStepModel(1.0) |> - MeteoWindow(CalendarWindow(:day; anchor=:current_period, week_start=1, completeness=:allow_partial)), + TimeStep(1.0) |> + PlantSimEngine.MeteoWindow(CalendarWindow(:day; anchor=:current_period, week_start=1, completeness=:allow_partial)), ), ) sim_meteo_calendar_current = PlantSimEngine.GraphSimulation(mtg, mapping_meteo_calendar_current, nsteps=48, check=true, outputs=Dict(:Leaf => (:MT, :MTmin, :MTmax, :MRh, :MSW, :MSWq))) @@ -1140,11 +1140,11 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test isapprox(out_meteo_calendar_current_df[:Leaf][25, :MSWq], 17.28; atol=1.0e-9) # Expectation 22: CalendarWindow(:day, :previous_complete_period) uses previous day. - mapping_meteo_calendar_prev = ModelMapping( + mapping_meteo_calendar_prev = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRMeteoDailyConsumerModel()) |> - TimeStepModel(1.0) |> - MeteoWindow(CalendarWindow(:day; anchor=:previous_complete_period, week_start=1, completeness=:allow_partial)), + TimeStep(1.0) |> + PlantSimEngine.MeteoWindow(CalendarWindow(:day; anchor=:previous_complete_period, week_start=1, completeness=:allow_partial)), ), ) sim_meteo_calendar_prev = PlantSimEngine.GraphSimulation(mtg, mapping_meteo_calendar_prev, nsteps=48, check=true, outputs=Dict(:Leaf => (:MT, :MTmin, :MTmax, :MRh, :MSW, :MSWq))) @@ -1155,11 +1155,11 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test out_meteo_calendar_prev_df[:Leaf][30, :MTmax] == 24.0 # Expectation 23: strict previous-complete-period errors when unavailable. - mapping_meteo_calendar_prev_strict = ModelMapping( + mapping_meteo_calendar_prev_strict = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRMeteoDailyConsumerModel()) |> - TimeStepModel(1.0) |> - MeteoWindow(CalendarWindow(:day; anchor=:previous_complete_period, week_start=1, completeness=:strict)), + TimeStep(1.0) |> + PlantSimEngine.MeteoWindow(CalendarWindow(:day; anchor=:previous_complete_period, week_start=1, completeness=:strict)), ), ) sim_meteo_calendar_prev_strict = PlantSimEngine.GraphSimulation(mtg, mapping_meteo_calendar_prev_strict, nsteps=48, check=true, outputs=Dict(:Leaf => (:MT, :MTmin, :MTmax, :MRh, :MSW, :MSWq))) @@ -1167,7 +1167,7 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( end # Expectation 24: ambiguous same-name inferred producer is rejected at initialization. - mapping_ambiguous_infer = ModelMapping( + mapping_ambiguous_infer = PlantSimEngine.ModelMapping( :Leaf => ( MRConflict1Model(), MRConflict2Model(), @@ -1177,7 +1177,7 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test_throws "Ambiguous inferred producer for input `Z`" PlantSimEngine.GraphSimulation(mtg, mapping_ambiguous_infer, nsteps=1, check=true, outputs=Dict(:Leaf => (:ZZ,))) # Expectation 24a: stream-only publishers are ignored by auto input producer inference. - mapping_stream_only_infer = ModelMapping( + mapping_stream_only_infer = PlantSimEngine.ModelMapping( :Leaf => ( MRConflict1Model(), ModelSpec(MRConflict2Model()) |> OutputRouting(; Z=:stream_only), @@ -1191,7 +1191,7 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test input_bindings(spec_stream_only_infer).Z.process == :mrconflict1 # Expectation 24b: cross-scale inference ignores sibling scales not on the same lineage. - mapping_lineage_infer = ModelMapping( + mapping_lineage_infer = PlantSimEngine.ModelMapping( :Plant => ( MRAncestorSourceModel(), ), @@ -1200,7 +1200,7 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( ), :Leaf => ( ModelSpec(MRZConsumerModel()) |> - MultiScaleModel([:Z => (:Plant => :Z)]), + PlantSimEngine.MultiScaleModel([:Z => (:Plant => :Z)]), ), ) sim_lineage_infer = PlantSimEngine.GraphSimulation(mtg, mapping_lineage_infer, nsteps=1, check=true, outputs=Dict(:Leaf => (:ZZ,))) @@ -1211,7 +1211,7 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test input_bindings(spec_lineage_infer).Z.scale == :Plant # Expectation 24c: inferred bindings prefer same-scale producers when available. - mapping_same_scale_preferred = ModelMapping( + mapping_same_scale_preferred = PlantSimEngine.ModelMapping( :Plant => ( MRMultiScaleTSourceModel(100.0), ), @@ -1237,7 +1237,7 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test status(sim_same_scale_preferred)[:Leaf][1].TT_used == 1.0 # Expectation 24d: when no same-scale producer exists, repeated process names across scales require explicit scale mapping. - mapping_cross_scale_same_process_ambiguous = ModelMapping( + mapping_cross_scale_same_process_ambiguous = PlantSimEngine.ModelMapping( :Plant => ( MRMultiScaleTSourceModel(100.0), ), @@ -1257,11 +1257,11 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( ) # Expectation 24e: same-rate hard dependencies are ignored for auto bindings and canonical publisher checks. - mapping_hard_same_rate = ModelMapping( + mapping_hard_same_rate = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRHardParentModel()) |> TimeStepModel(1.0), - ModelSpec(MRHardChildModel()) |> TimeStepModel(1.0), - ModelSpec(MRHardConsumerModel()) |> TimeStepModel(1.0), + ModelSpec(MRHardParentModel()) |> TimeStep(1.0), + ModelSpec(MRHardChildModel()) |> TimeStep(1.0), + ModelSpec(MRHardConsumerModel()) |> TimeStep(1.0), ), ) sim_hard_same_rate = PlantSimEngine.GraphSimulation(mtg, mapping_hard_same_rate, nsteps=1, check=true, outputs=Dict(:Leaf => (:A, :B))) @@ -1271,11 +1271,11 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test status(sim_hard_same_rate)[:Leaf][1].B == 5.0 # Expectation 24f: hard dependency children cannot be scheduled independently from their parent. - mapping_hard_different_rate = ModelMapping( + mapping_hard_different_rate = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRHardParentModel()) |> TimeStepModel(1.0), - ModelSpec(MRHardChildModel()) |> TimeStepModel(2.0), - ModelSpec(MRHardConsumerModel()) |> TimeStepModel(1.0), + ModelSpec(MRHardParentModel()) |> TimeStep(1.0), + ModelSpec(MRHardChildModel()) |> TimeStep(2.0), + ModelSpec(MRHardConsumerModel()) |> TimeStep(1.0), ), ) @test_throws "Hard dependency `mrhardchild`" PlantSimEngine.GraphSimulation( @@ -1287,7 +1287,7 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( ) # Expectation 25: missing producer remains allowed; model can rely on initialized/forced inputs. - mapping_missing_input = ModelMapping( + mapping_missing_input = PlantSimEngine.ModelMapping( :Leaf => ( MRMissingInputConsumerModel(), Status(U=42.0), @@ -1298,24 +1298,24 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( @test status(sim_missing_input)[:Leaf][1].OU == 42.0 # Expectation 26: invalid mapping-level API configuration fails during GraphSimulation init. - mapping_bad_input = ModelMapping( + mapping_bad_input = PlantSimEngine.ModelMapping( :Leaf => ( MRSourceModel(), ModelSpec(MRConsumerModel()) |> - InputBindings(; Z=(process=:mrsource, var=:S)), + PlantSimEngine.InputBindings(; Z=(process=:mrsource, var=:S)), ), ) @test_throws "declares binding for input `Z`" PlantSimEngine.GraphSimulation(mtg, mapping_bad_input, nsteps=1, check=true, outputs=Dict(:Leaf => (:B,))) - mapping_bad_process = ModelMapping( + mapping_bad_process = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRConsumerModel()) |> - InputBindings(; C=(process=:unknown_process, var=:S)), + PlantSimEngine.InputBindings(; C=(process=:unknown_process, var=:S)), ), ) @test_throws "Unknown source process `unknown_process`" PlantSimEngine.GraphSimulation(mtg, mapping_bad_process, nsteps=1, check=true, outputs=Dict(:Leaf => (:B,))) - mapping_bad_routing = ModelMapping( + mapping_bad_routing = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRSourceModel()) |> OutputRouting(; Z=:stream_only), @@ -1323,50 +1323,50 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( ) @test_throws "declares routing for output `Z`" PlantSimEngine.GraphSimulation(mtg, mapping_bad_routing, nsteps=1, check=true, outputs=Dict(:Leaf => (:S,))) - mapping_bad_interp_mode = ModelMapping( + mapping_bad_interp_mode = PlantSimEngine.ModelMapping( :Leaf => ( MRSourceModel(), ModelSpec(MRConsumerModel()) |> - InputBindings(; C=(process=:mrsource, var=:S, policy=Interpolate(:spline))), + PlantSimEngine.InputBindings(; C=(process=:mrsource, var=:S, policy=Interpolate(:spline))), ), ) @test_throws "Invalid interpolation mode `spline`" PlantSimEngine.GraphSimulation(mtg, mapping_bad_interp_mode, nsteps=1, check=true, outputs=Dict(:Leaf => (:B,))) @test_throws "Unsupported reducer value" Aggregate(:median) - mapping_bad_period = ModelMapping( + mapping_bad_period = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRDailySourceModel(Ref(0))) |> TimeStepModel(Dates.Month(1)), + ModelSpec(MRDailySourceModel(Ref(0))) |> TimeStep(Dates.Month(1)), ), ) @test_throws "non-fixed periods are not supported" PlantSimEngine.GraphSimulation(mtg, mapping_bad_period, nsteps=1, check=true, outputs=Dict(:Leaf => (:XD,))) - mapping_bad_scope = ModelMapping( + mapping_bad_scope = PlantSimEngine.ModelMapping( :Leaf => ( - ModelSpec(MRSourceModel()) |> ScopeModel(:invalid_scope), + ModelSpec(MRSourceModel()) |> PlantSimEngine.ScopeModel(:invalid_scope), ), ) @test_throws "Invalid scope selector" PlantSimEngine.GraphSimulation(mtg, mapping_bad_scope, nsteps=1, check=true, outputs=Dict(:Leaf => (:S,))) - mapping_bad_meteo = ModelMapping( + mapping_bad_meteo = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRMeteoCustomConsumerModel()) |> - MeteoBindings(; Ri_SW_f=(source=:Ri_SW_f, badfield=:oops)), + PlantSimEngine.MeteoBindings(; Ri_SW_f=(source=:Ri_SW_f, badfield=:oops)), ), ) @test_throws "unsupported fields" PlantSimEngine.GraphSimulation(mtg, mapping_bad_meteo, nsteps=1, check=true, outputs=Dict(:Leaf => (:MRQ,))) - @test_throws "Unsupported MeteoBindings value" ModelMapping( + @test_throws "Unsupported MeteoBindings value" PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRMeteoCustomConsumerModel()) |> - MeteoBindings(; Ri_SW_f=:radiation_energy), + PlantSimEngine.MeteoBindings(; Ri_SW_f=:radiation_energy), ), ) - @test_throws "Unsupported MeteoWindow value" ModelMapping( + @test_throws "Unsupported MeteoWindow value" PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRMeteoCustomConsumerModel()) |> - MeteoWindow("day"), + PlantSimEngine.MeteoWindow("day"), ), ) @@ -1377,7 +1377,7 @@ PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( PlantSimEngine.run!(::MRBadHintModel, models, status, meteo, constants=nothing, extra=nothing) = (status.X = 1.0) PlantSimEngine.timestep_hint(::Type{<:MRBadHintModel}) = "hourly" - mapping_bad_hint = ModelMapping( + mapping_bad_hint = PlantSimEngine.ModelMapping( :Leaf => ( ModelSpec(MRBadHintModel()), ), diff --git a/test/test-multirate-scaffolding.jl b/test/test-multirate-scaffolding.jl index f34bdcd45..a459eb967 100644 --- a/test/test-multirate-scaffolding.jl +++ b/test/test-multirate-scaffolding.jl @@ -22,7 +22,7 @@ using Test @test model_scope(ModelSpec(m)) == :global @test updates(ModelSpec(m)) == () @test_throws "String scope selectors are not supported" ModelSpec(m; scope="plant") - @test_throws "String scope selectors are not supported" ScopeModel("plant")(m) + @test_throws "String scope selectors are not supported" PlantSimEngine.ScopeModel("plant")(m) scope_node = Node(MultiScaleTreeGraph.NodeMTG("/", :Leaf, 1, 1)) @test_throws "must return `ScopeId` or `Symbol`" PlantSimEngine._scope_from_selector( @@ -47,10 +47,10 @@ using Test @test occursin("input_bindings=", explain_txt) spec = ModelSpec(m) |> - TimeStepModel(24.0) |> - InputBindings(; var1=(process=:process1, var=:var3)) |> + TimeStep(24.0) |> + PlantSimEngine.InputBindings(; var1=(process=:process1, var=:var3)) |> OutputRouting(; var3=:stream_only) |> - ScopeModel(:plant) |> + PlantSimEngine.ScopeModel(:plant) |> Updates(:var3; after=:process1) |> Updates(:var3; after=:process2) @test PlantSimEngine.model_(spec) === m @@ -64,7 +64,7 @@ using Test @test updates(spec)[2].variables == (:var3,) @test updates(spec)[2].after == (:process2,) - mspec = ModelSpec(m) |> MultiScaleModel([:var1 => (:Leaf => :var1)]) + mspec = ModelSpec(m) |> PlantSimEngine.MultiScaleModel([:var1 => (:Leaf => :var1)]) @test length(PlantSimEngine.get_mapped_variables(mspec)) == 1 ts = TemporalState() diff --git a/test/test-performance.jl b/test/test-performance.jl index 462615370..33f8caf8d 100644 --- a/test/test-performance.jl +++ b/test/test-performance.jl @@ -22,8 +22,8 @@ nrows = nrow(meteo_day) vc = [0 for i in 1:nrows] -mapping1 = ModelMapping(process1=ToySleepModel(), status=(a=vc,)) -mapping2 = ModelMapping(process1=ToySleepModel(), status=(a=vc,)) +mapping1 = PlantSimEngine.ModelMapping(process1=ToySleepModel(), status=(a=vc,)) +mapping2 = PlantSimEngine.ModelMapping(process1=ToySleepModel(), status=(a=vc,)) @testset begin "Check number of threads" @@ -66,7 +66,7 @@ end using Dates meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Day) - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.5), status=(TT_cu=cumsum(meteo_day.TT),) diff --git a/test/test-simulation.jl b/test/test-simulation.jl index f433c1c11..d5f27b0b3 100644 --- a/test/test-simulation.jl +++ b/test/test-simulation.jl @@ -1,6 +1,6 @@ @testset "Check missing model" begin # No problem here: - @test_nowarn ModelMapping( + @test_nowarn PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -16,7 +16,7 @@ :info, "Some variables must be initialized before simulation: (process3 = (:var5,),) (see `to_initialize()`)" ) - ModelMapping( + PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process3=Process3Model(), status=(var1=15.0, var2=0.3) @@ -24,7 +24,7 @@ end; @testset "Deprecated run! entrypoints" begin - models = ModelMapping( + models = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -35,7 +35,7 @@ end; run!(models, meteo) @test !isdefined(PlantSimEngine, :ModelList) @test_throws MethodError run!([models], meteo) - @test_throws ErrorException run!(ModelMapping("mod1" => models), meteo) + @test_throws ErrorException run!(PlantSimEngine.ModelMapping("mod1" => models), meteo) mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Leaf, 1, 1)) mtg[:var1] = 15.0 @@ -45,7 +45,7 @@ end; end @testset "Removed multirate keyword for single-scale" begin - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -58,7 +58,7 @@ end end @testset "Simulation: 1 time-step, 0 Atmosphere" begin - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( Process1Model(1.0); status=(var1=15.0, var2=0.3) ) @@ -72,7 +72,7 @@ end; @testset "Simulation: 1 time-step, 1 Atmosphere" begin status_nt = (var1=15.0, var2=0.3) - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -89,14 +89,14 @@ end; end; @testset "Simulation: 1 time-step, 1 Atmosphere, 2 objects" begin - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0, var2=0.3) ) - mapping2 = ModelMapping( + mapping2 = PlantSimEngine.ModelMapping( process1=Process1Model(2.0), process2=Process2Model(), process3=Process3Model(), @@ -119,7 +119,7 @@ end; end; @testset "Simulation: 2 time-steps, 1 Atmosphere" begin - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -144,7 +144,7 @@ end; status_nt = (var1=[15.0, 16.0], var2=0.3) - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), @@ -174,14 +174,14 @@ end; @testset "Simulation: 2 time-steps, 2 Atmospheres, 2 objects" begin - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=[15.0, 16.0], var2=0.3) ) - mapping2 = ModelMapping( + mapping2 = PlantSimEngine.ModelMapping( process1=Process1Model(2.0), process2=Process2Model(), process3=Process3Model(), @@ -223,7 +223,7 @@ end; leaf[:var1] = [15.0, 16.0] leaf[:var2] = 0.3 - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( :Leaf => ( Process1Model(1.0), Process2Model(), diff --git a/test/test-toy_models.jl b/test/test-toy_models.jl index ad887e590..859be87bf 100644 --- a/test/test-toy_models.jl +++ b/test/test-toy_models.jl @@ -3,14 +3,14 @@ meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), # Note (smack) : The first test's behaviour is weird to me, because there is an [Info :] that correctly indicates # :LAI is not initialised, yet @test_nowarn doesn't capture it. I'm not sure what the intended test was, between 'Info' and 'Warn' @testset "ToyLAIModel" begin - @test_nowarn ModelMapping(ToyLAIModel()) - @test_nowarn ModelMapping(ToyLAIModel(); status=(TT_cu=10,)) - @test_nowarn ModelMapping( + @test_nowarn PlantSimEngine.ModelMapping(ToyLAIModel()) + @test_nowarn PlantSimEngine.ModelMapping(ToyLAIModel(); status=(TT_cu=10,)) + @test_nowarn PlantSimEngine.ModelMapping( ToyLAIModel(); status=(TT_cu=cumsum(meteo_day.TT),), ) - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( ToyLAIModel(); status=(TT_cu=cumsum(meteo_day.TT),), ) @@ -23,7 +23,7 @@ meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), end @testset "ToyLAIModel+Beer" begin - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.5), status=(TT_cu=cumsum(meteo_day.TT),) @@ -38,29 +38,29 @@ end @testset "ToyRUEGrowthModel" begin rue = 0.3 - @test_nowarn ModelMapping(ToyRUEGrowthModel(rue)) - @test_nowarn ModelMapping(ToyRUEGrowthModel(rue); status=(aPPFD=[10.0, 30.0, 25.0],)) + @test_nowarn PlantSimEngine.ModelMapping(ToyRUEGrowthModel(rue)) + @test_nowarn PlantSimEngine.ModelMapping(ToyRUEGrowthModel(rue); status=(aPPFD=[10.0, 30.0, 25.0],)) # One time step: - mapping = ModelMapping(ToyRUEGrowthModel(rue); status=(aPPFD=30.0,)) + mapping = PlantSimEngine.ModelMapping(ToyRUEGrowthModel(rue); status=(aPPFD=30.0,)) outputs = run!(mapping, executor=SequentialEx()) @test outputs[:biomass][1] ≈ rue * 30.0 # Several time steps: aPPFD = [10.0, 30.0, 25.0] - mapping = ModelMapping(ToyRUEGrowthModel(rue); status=(aPPFD=aPPFD,)) + mapping = PlantSimEngine.ModelMapping(ToyRUEGrowthModel(rue); status=(aPPFD=aPPFD,)) outputs = run!(mapping, executor=SequentialEx()) @test outputs[:biomass] ≈ cumsum(rue * aPPFD) end @testset "ToyAssimGrowthModel" begin - @test_nowarn ModelMapping(ToyAssimGrowthModel()) - @test_nowarn ModelMapping(ToyAssimGrowthModel(); status=(carbon_assimilation=[10.0, 30.0, 25.0],)) + @test_nowarn PlantSimEngine.ModelMapping(ToyAssimGrowthModel()) + @test_nowarn PlantSimEngine.ModelMapping(ToyAssimGrowthModel(); status=(carbon_assimilation=[10.0, 30.0, 25.0],)) # Uninitialized: - to_init_uninitialized = to_initialize(ModelMapping(ToyAssimGrowthModel())) + to_init_uninitialized = to_initialize(PlantSimEngine.ModelMapping(ToyAssimGrowthModel())) if to_init_uninitialized isa AbstractDict @test haskey(to_init_uninitialized, :Default) @test :aPPFD in to_init_uninitialized[:Default] @@ -70,7 +70,7 @@ end end # One time step: - mapping = ModelMapping(ToyAssimGrowthModel(); status=(aPPFD=30.0,)) + mapping = PlantSimEngine.ModelMapping(ToyAssimGrowthModel(); status=(aPPFD=30.0,)) @test isempty(to_initialize(mapping)) @@ -78,7 +78,7 @@ end @test outputs[:biomass] ≈ [4.5] # Several time steps: - mapping = ModelMapping(ToyAssimGrowthModel(); status=(aPPFD=[10.0, 30.0, 25.0],)) + mapping = PlantSimEngine.ModelMapping(ToyAssimGrowthModel(); status=(aPPFD=[10.0, 30.0, 25.0],)) outputs = run!(mapping) @test outputs[:biomass] ≈ cumsum(outputs[:biomass_increment]) @@ -87,7 +87,7 @@ end @testset "ToyLAIModel+Beer+ToyRUEGrowthModel" begin rue = 0.3 - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( ToyLAIModel(), Beer(0.5), ToyRUEGrowthModel(rue), diff --git a/test/test-unified-scene-object-api.jl b/test/test-unified-scene-object-api.jl index 4887a8254..d0a3c4a31 100644 --- a/test/test-unified-scene-object-api.jl +++ b/test/test-unified-scene-object-api.jl @@ -1,6 +1,7 @@ using Dates using PlantSimEngine using PlantSimEngine.Examples +using MultiScaleTreeGraph using Test PlantSimEngine.@process "scene_object_default_input_consumer" verbose = false @@ -65,6 +66,102 @@ function PlantSimEngine.run!(::SceneObjectEnvironmentProbeModel, models, status, return nothing end +PlantSimEngine.@process "scene_object_environment_co2_probe" verbose = false + +struct SceneObjectEnvironmentCO2ProbeModel <: + AbstractScene_Object_Environment_Co2_ProbeModel end + +PlantSimEngine.inputs_(::SceneObjectEnvironmentCO2ProbeModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectEnvironmentCO2ProbeModel) = + (temperature_seen=0.0, co2_seen=0.0) +PlantSimEngine.meteo_inputs_(::SceneObjectEnvironmentCO2ProbeModel) = + (T=0.0, CO2=0.0) + +function PlantSimEngine.run!( + ::SceneObjectEnvironmentCO2ProbeModel, + models, + status, + meteo, + constants=nothing, + extra=nothing, +) + status.temperature_seen = meteo.T + status.co2_seen = meteo.CO2 + return nothing +end + +struct SceneObjectEnvironmentCO2HintProbeModel <: + AbstractScene_Object_Environment_Co2_ProbeModel end + +PlantSimEngine.inputs_(::SceneObjectEnvironmentCO2HintProbeModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectEnvironmentCO2HintProbeModel) = + (temperature_seen=0.0, co2_seen=0.0) +PlantSimEngine.meteo_inputs_(::SceneObjectEnvironmentCO2HintProbeModel) = + (T=0.0, CO2=0.0) +PlantSimEngine.meteo_hint(::Type{<:SceneObjectEnvironmentCO2HintProbeModel}) = + (bindings=(CO2=(source=:Ca, reducer=MeanReducer()),),) + +function PlantSimEngine.run!( + ::SceneObjectEnvironmentCO2HintProbeModel, + models, + status, + meteo, + constants=nothing, + extra=nothing, +) + status.temperature_seen = meteo.T + status.co2_seen = meteo.CO2 + return nothing +end + +struct SceneObjectAggregatedEnvironmentProbeModel <: + AbstractScene_Object_Environment_Co2_ProbeModel end + +PlantSimEngine.inputs_(::SceneObjectAggregatedEnvironmentProbeModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectAggregatedEnvironmentProbeModel) = + (temperature_seen=0.0, co2_seen=0.0) +PlantSimEngine.meteo_inputs_(::SceneObjectAggregatedEnvironmentProbeModel) = + (T=0.0, CO2=0.0) +PlantSimEngine.meteo_hint(::Type{<:SceneObjectAggregatedEnvironmentProbeModel}) = ( + bindings=( + T=(source=:T, reducer=MaxReducer()), + CO2=(source=:Ca, reducer=MeanReducer()), + ), +) + +function PlantSimEngine.run!( + ::SceneObjectAggregatedEnvironmentProbeModel, + models, + status, + meteo, + constants=nothing, + extra=nothing, +) + status.temperature_seen = meteo.T + status.co2_seen = meteo.CO2 + return nothing +end + +struct SceneObjectTemperatureOnlyProbeModel <: + AbstractScene_Object_Environment_ProbeModel end + +PlantSimEngine.inputs_(::SceneObjectTemperatureOnlyProbeModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectTemperatureOnlyProbeModel) = + (temperature_seen=0.0,) +PlantSimEngine.meteo_inputs_(::SceneObjectTemperatureOnlyProbeModel) = (T=0.0,) + +function PlantSimEngine.run!( + ::SceneObjectTemperatureOnlyProbeModel, + models, + status, + meteo, + constants=nothing, + extra=nothing, +) + status.temperature_seen = meteo.T + return nothing +end + PlantSimEngine.@process "scene_object_environment_update" verbose = false struct SceneObjectEnvironmentUpdateModel <: AbstractScene_Object_Environment_UpdateModel end @@ -92,6 +189,55 @@ function PlantSimEngine.run!(::SceneObjectSignalSourceModel, models, status, met return nothing end +PlantSimEngine.@process "scene_object_trait_clock_source" verbose = false + +struct SceneObjectTraitClockSourceModel <: AbstractScene_Object_Trait_Clock_SourceModel end + +PlantSimEngine.inputs_(::SceneObjectTraitClockSourceModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectTraitClockSourceModel) = (signal=0.0,) +PlantSimEngine.timespec(::Type{<:SceneObjectTraitClockSourceModel}) = ClockSpec(2.0, 1.0) + +function PlantSimEngine.run!(::SceneObjectTraitClockSourceModel, models, status, meteo, constants=nothing, extra=nothing) + status.signal += 1.0 + return nothing +end + +PlantSimEngine.@process "scene_object_strict_hint_source" verbose = false + +struct SceneObjectStrictHintSourceModel <: AbstractScene_Object_Strict_Hint_SourceModel end + +PlantSimEngine.inputs_(::SceneObjectStrictHintSourceModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectStrictHintSourceModel) = (signal=0.0,) +PlantSimEngine.timestep_hint(::Type{<:SceneObjectStrictHintSourceModel}) = Dates.Day(1) + +function PlantSimEngine.run!(::SceneObjectStrictHintSourceModel, models, status, meteo, constants=nothing, extra=nothing) + status.signal += 1.0 + return nothing +end + +struct SceneObjectTimeSignalModel{T} <: AbstractScene_Object_Signal_SourceModel + prototype::T +end + +PlantSimEngine.inputs_(::SceneObjectTimeSignalModel) = NamedTuple() +PlantSimEngine.outputs_(model::SceneObjectTimeSignalModel) = (signal=zero(model.prototype),) + +function PlantSimEngine.run!(model::SceneObjectTimeSignalModel, models, status, meteo, constants=nothing, extra=nothing) + status.signal = convert(typeof(model.prototype), extra.time) + return nothing +end + +struct SceneObjectTraitPolicySignalModel <: AbstractScene_Object_Signal_SourceModel end + +PlantSimEngine.inputs_(::SceneObjectTraitPolicySignalModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectTraitPolicySignalModel) = (signal=0.0,) +PlantSimEngine.output_policy(::Type{<:SceneObjectTraitPolicySignalModel}) = (signal=Aggregate(),) + +function PlantSimEngine.run!(::SceneObjectTraitPolicySignalModel, models, status, meteo, constants=nothing, extra=nothing) + status.signal += 1.0 + return nothing +end + struct SceneObjectParameterizedSignalModel{T} <: AbstractScene_Object_Signal_SourceModel increment::T end @@ -104,6 +250,56 @@ function PlantSimEngine.run!(model::SceneObjectParameterizedSignalModel, models, return nothing end +struct SceneObjectAlternativeSignalModel <: AbstractScene_Object_Signal_SourceModel end + +PlantSimEngine.inputs_(::SceneObjectAlternativeSignalModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectAlternativeSignalModel) = (signal=0.0,) + +function PlantSimEngine.run!( + ::SceneObjectAlternativeSignalModel, + models, + status, + meteo, + constants=nothing, + extra=nothing, +) + status.signal += 7.0 + return nothing +end + +PlantSimEngine.@process "scene_object_batch_counter" verbose = false + +struct SceneObjectBatchCounterModel <: AbstractScene_Object_Batch_CounterModel + count::Base.RefValue{Int} +end + +PlantSimEngine.inputs_(::SceneObjectBatchCounterModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectBatchCounterModel) = NamedTuple() + +function PlantSimEngine.run!( + model::SceneObjectBatchCounterModel, + models, + status, + meteo, + constants=nothing, + extra=nothing, +) + model.count[] += 1 + return nothing +end + +struct SceneObjectSignalSetModel{T} <: AbstractScene_Object_Signal_SourceModel + value::T +end + +PlantSimEngine.inputs_(::SceneObjectSignalSetModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectSignalSetModel) = (signal=0.0,) + +function PlantSimEngine.run!(model::SceneObjectSignalSetModel, models, status, meteo, constants=nothing, extra=nothing) + status.signal = model.value + return nothing +end + PlantSimEngine.@process "scene_object_plant_signal_sum" verbose = false struct SceneObjectPlantSignalSumModel <: AbstractScene_Object_Plant_Signal_SumModel end @@ -127,12 +323,73 @@ PlantSimEngine.dep(::SceneObjectSignalCallerModel) = ( ) function PlantSimEngine.run!(::SceneObjectSignalCallerModel, models, status, meteo, constants=nothing, extra=nothing) - target = dependency_target(extra, :signal) - run_call!(target) + target = call_target(extra, "signal") + run_call!(target; publish=true) status.called_signal = target.status.signal return nothing end +PlantSimEngine.@process "scene_object_meteo_call_source" verbose = false + +struct SceneObjectMeteoCallSourceModel <: AbstractScene_Object_Meteo_Call_SourceModel end + +PlantSimEngine.inputs_(::SceneObjectMeteoCallSourceModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectMeteoCallSourceModel) = (temperature_seen=0.0,) +PlantSimEngine.meteo_inputs_(::SceneObjectMeteoCallSourceModel) = (T=0.0,) +PlantSimEngine.meteo_outputs_(::SceneObjectMeteoCallSourceModel) = (T=0.0,) + +function PlantSimEngine.run!(::SceneObjectMeteoCallSourceModel, models, status, meteo, constants=nothing, extra=nothing) + status.temperature_seen = meteo.T + status.T = meteo.T + 1.0 + return nothing +end + +PlantSimEngine.@process "scene_object_meteo_call_controller" verbose = false + +struct SceneObjectMeteoCallControllerModel{T} <: AbstractScene_Object_Meteo_Call_ControllerModel + local_temperature::T + publish::Bool +end + +PlantSimEngine.inputs_(::SceneObjectMeteoCallControllerModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectMeteoCallControllerModel) = (called_temperature=0.0,) + +function PlantSimEngine.run!(m::SceneObjectMeteoCallControllerModel, models, status, meteo, constants=nothing, extra=nothing) + target = call_target(extra, :source) + run_call!(target; meteo=(T=m.local_temperature,), publish=m.publish) + status.called_temperature = target.status.temperature_seen + return nothing +end + +PlantSimEngine.@process "scene_object_iterative_meteo_call_controller" verbose = false + +struct SceneObjectIterativeMeteoCallControllerModel{T} <: + AbstractScene_Object_Iterative_Meteo_Call_ControllerModel + trial_temperatures::NTuple{2,T} + accepted_temperature::T +end + +PlantSimEngine.inputs_(::SceneObjectIterativeMeteoCallControllerModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectIterativeMeteoCallControllerModel) = + (called_temperature=0.0,) + +function PlantSimEngine.run!( + m::SceneObjectIterativeMeteoCallControllerModel, + models, + status, + meteo, + constants=nothing, + extra=nothing, +) + target = call_target(extra, :source) + for temperature in m.trial_temperatures + run_call!(target; meteo=(T=temperature,)) + end + run_call!(target; meteo=(T=m.accepted_temperature,), publish=true) + status.called_temperature = target.status.temperature_seen + return nothing +end + PlantSimEngine.@process "scene_object_signal_consumer" verbose = false struct SceneObjectSignalConsumerModel <: AbstractScene_Object_Signal_ConsumerModel end @@ -145,6 +402,71 @@ function PlantSimEngine.run!(::SceneObjectSignalConsumerModel, models, status, m return nothing end +PlantSimEngine.@process "scene_object_renamed_signal_consumer" verbose = false + +struct SceneObjectRenamedSignalConsumerModel <: + AbstractScene_Object_Renamed_Signal_ConsumerModel end + +PlantSimEngine.inputs_(::SceneObjectRenamedSignalConsumerModel) = + (renamed_signal=0.0,) +PlantSimEngine.outputs_(::SceneObjectRenamedSignalConsumerModel) = + (observed_renamed_signal=0.0,) + +function PlantSimEngine.run!( + ::SceneObjectRenamedSignalConsumerModel, + models, + status, + meteo, + constants=nothing, + extra=nothing, +) + status.observed_renamed_signal = status.renamed_signal + return nothing +end + +PlantSimEngine.@process "scene_object_optional_input_consumer" verbose = false + +struct SceneObjectOptionalInputConsumerModel <: + AbstractScene_Object_Optional_Input_ConsumerModel end + +PlantSimEngine.inputs_(::SceneObjectOptionalInputConsumerModel) = (optional_signal=7.0,) +PlantSimEngine.outputs_(::SceneObjectOptionalInputConsumerModel) = + (observed_optional_signal=0.0,) + +function PlantSimEngine.run!( + ::SceneObjectOptionalInputConsumerModel, + models, + status, + meteo, + constants=nothing, + extra=nothing, +) + status.observed_optional_signal = status.optional_signal + return nothing +end + +PlantSimEngine.@process "scene_object_optional_call_consumer" verbose = false + +struct SceneObjectOptionalCallConsumerModel <: + AbstractScene_Object_Optional_Call_ConsumerModel end + +PlantSimEngine.inputs_(::SceneObjectOptionalCallConsumerModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectOptionalCallConsumerModel) = + (optional_call_count=0,) + +function PlantSimEngine.run!( + ::SceneObjectOptionalCallConsumerModel, + models, + status, + meteo, + constants=nothing, + extra=nothing, +) + status.optional_call_count = + length(call_targets(extra, "optional_source")) + return nothing +end + PlantSimEngine.@process "scene_object_cycle_a" verbose = false struct SceneObjectCycleAModel <: AbstractScene_Object_Cycle_AModel end @@ -152,6 +474,11 @@ struct SceneObjectCycleAModel <: AbstractScene_Object_Cycle_AModel end PlantSimEngine.inputs_(::SceneObjectCycleAModel) = (cycle_b=0.0,) PlantSimEngine.outputs_(::SceneObjectCycleAModel) = (cycle_a=0.0,) +function PlantSimEngine.run!(::SceneObjectCycleAModel, models, status, meteo, constants=nothing, extra=nothing) + status.cycle_a = status.cycle_b + 1.0 + return nothing +end + PlantSimEngine.@process "scene_object_cycle_b" verbose = false struct SceneObjectCycleBModel <: AbstractScene_Object_Cycle_BModel end @@ -159,6 +486,11 @@ struct SceneObjectCycleBModel <: AbstractScene_Object_Cycle_BModel end PlantSimEngine.inputs_(::SceneObjectCycleBModel) = (cycle_a=0.0,) PlantSimEngine.outputs_(::SceneObjectCycleBModel) = (cycle_b=0.0,) +function PlantSimEngine.run!(::SceneObjectCycleBModel, models, status, meteo, constants=nothing, extra=nothing) + status.cycle_b = 2.0 * status.cycle_a + return nothing +end + PlantSimEngine.@process "scene_object_temporal_sum" verbose = false struct SceneObjectTemporalSumModel <: AbstractScene_Object_Temporal_SumModel end @@ -195,6 +527,56 @@ function PlantSimEngine.run!(::SceneObjectBiomassPrunerModel, models, status, me return nothing end +PlantSimEngine.@process "scene_object_growth" verbose = false + +struct SceneObjectGrowthModel <: AbstractScene_Object_GrowthModel end + +PlantSimEngine.inputs_(::SceneObjectGrowthModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectGrowthModel) = (created_count=0,) + +function PlantSimEngine.run!(::SceneObjectGrowthModel, models, status, meteo, constants=nothing, extra=nothing) + scene = extra.compiled.scene + if isapprox(extra.time, 1.0) && !(ObjectId(:grown_leaf) in object_ids(scene; scale=:Leaf)) + register_object!( + scene, + Object(:grown_leaf; scale=:Leaf, parent=:plant_1, status=Status(signal=0.0)), + ) + status.created_count += 1 + end + return nothing +end + +PlantSimEngine.@process "scene_object_pruning" verbose = false + +struct SceneObjectPruningModel <: AbstractScene_Object_PruningModel end + +PlantSimEngine.inputs_(::SceneObjectPruningModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectPruningModel) = (removed_count=0,) + +function PlantSimEngine.run!(::SceneObjectPruningModel, models, status, meteo, constants=nothing, extra=nothing) + scene = extra.compiled.scene + if isapprox(extra.time, 2.0) && ObjectId(:leaf_2) in object_ids(scene; scale=:Leaf) + remove_object!(scene, :leaf_2) + status.removed_count += 1 + end + return nothing +end + +PlantSimEngine.@process "scene_object_geometry_mover" verbose = false + +struct SceneObjectGeometryMoverModel <: AbstractScene_Object_Geometry_MoverModel end + +PlantSimEngine.inputs_(::SceneObjectGeometryMoverModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectGeometryMoverModel) = (move_count=0,) + +function PlantSimEngine.run!(::SceneObjectGeometryMoverModel, models, status, meteo, constants=nothing, extra=nothing) + if isapprox(extra.time, 1.0) + update_geometry!(extra.compiled.scene, :leaf_1, (cell=:cell_b,)) + status.move_count += 1 + end + return nothing +end + mutable struct SceneObjectGridBackend <: PlantSimEngine.AbstractEnvironmentBackend binds::Vector{Any} index_updates::Vector{Any} @@ -206,6 +588,39 @@ struct SceneObjectTaggedValue value::Int end +struct SceneObjectDualLike{T} + value::T + derivative::T +end + +Base.zero(::Type{SceneObjectDualLike{T}}) where {T} = + SceneObjectDualLike(zero(T), zero(T)) +Base.:+(a::SceneObjectDualLike, b::SceneObjectDualLike) = + SceneObjectDualLike(a.value + b.value, a.derivative + b.derivative) +Base.:(==)(a::SceneObjectDualLike, b::SceneObjectDualLike) = + a.value == b.value && a.derivative == b.derivative + +PlantSimEngine.@process "scene_object_dual_like_sum" verbose = false + +struct SceneObjectDualLikeSumModel <: AbstractScene_Object_Dual_Like_SumModel end + +PlantSimEngine.inputs_(::SceneObjectDualLikeSumModel) = + (values=SceneObjectDualLike{BigFloat}[],) +PlantSimEngine.outputs_(::SceneObjectDualLikeSumModel) = + (total=zero(SceneObjectDualLike{BigFloat}),) + +function PlantSimEngine.run!( + ::SceneObjectDualLikeSumModel, + models, + status, + meteo, + constants=nothing, + extra=nothing, +) + status.total = sum(status.values) + return nothing +end + PlantSimEngine.base_step_seconds(::SceneObjectGridBackend) = 3600.0 PlantSimEngine.get_nsteps(::SceneObjectGridBackend) = 1 PlantSimEngine.environment_variables(::SceneObjectGridBackend) = Set([:T, :CO2]) @@ -301,6 +716,40 @@ function PlantSimEngine.scatter!( end @testset "Unified scene/object API" begin + mtg_root = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) + mtg_plant = Node(mtg_root, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) + mtg_leaf = Node(mtg_plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) + mtg_leaf_status = Status(signal=2.0) + mtg_leaf[:plantsimengine_status] = mtg_leaf_status + mtg_object_id = node -> Symbol(lowercase(string(symbol(node))), "_", node_id(node)) + adapted_objects = objects_from_mtg( + mtg_root; + id=mtg_object_id, + kind=node -> symbol(node) == :Scene ? :scene : :plant, + species=node -> symbol(node) == :Scene ? nothing : :oil_palm, + geometry=node -> symbol(node) == :Leaf ? (x=1.0, y=2.0) : nothing, + ) + @test [object.id for object in adapted_objects] == + ObjectId.([:scene_1, :plant_2, :leaf_3]) + @test only(object for object in adapted_objects if object.scale == :Leaf).status === + mtg_leaf_status + mtg_scene = Scene( + mtg_root; + id=mtg_object_id, + kind=node -> symbol(node) == :Scene ? :scene : :plant, + species=node -> symbol(node) == :Scene ? nothing : :oil_palm, + geometry=node -> symbol(node) == :Leaf ? (x=1.0, y=2.0) : nothing, + applications=( + ModelSpec(SceneObjectParameterizedSignalModel(1.0); name=:mtg_signal) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + @test only(scene_objects(mtg_scene; scale=:Leaf)).parent == ObjectId(:plant_2) + @test only(scene_objects(mtg_scene; scale=:Leaf)).status === mtg_leaf_status + @test position(only(scene_objects(mtg_scene; scale=:Leaf))) == (x=1.0, y=2.0) + run!(mtg_scene) + @test only(scene_objects(mtg_scene; scale=:Leaf)).status.signal == 3.0 + scene = Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:plant_1; scale=:Plant, kind=:plant, species=:oil_palm, parent=:scene), @@ -343,10 +792,10 @@ end Object(:scene; scale=:Scene, kind=:scene), Object(:plant_1; scale=:Plant, kind=:plant, species=:oil_palm, name=:palm_1, parent=:scene), Object(:axis_1; scale=:Axis, kind=:plant, species=:oil_palm, parent=:plant_1), - Object(:leaf_1; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1), - Object(:leaf_2; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:axis_1), + Object(:leaf_1; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_1, status=Status(leaf_area=1.0)), + Object(:leaf_2; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:axis_1, status=Status(leaf_area=2.0)), Object(:plant_2; scale=:Plant, kind=:plant, species=:oil_palm, name=:palm_2, parent=:scene), - Object(:leaf_3; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_2), + Object(:leaf_3; scale=:Leaf, kind=:plant, species=:oil_palm, parent=:plant_2, status=Status(leaf_area=3.0)), Object(:soil; scale=:Soil, kind=:soil, parent=:scene), ) @@ -382,13 +831,98 @@ end [ObjectId(:leaf_2)] @test resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Scope(:palm_2))) == [ObjectId(:leaf_3)] + @test resolve_object_ids(selector_scene, One(Relation(:parent)); context=:leaf_2) == + [ObjectId(:axis_1)] + @test resolve_object_ids(selector_scene, Many(Relation(:children)); context=:plant_1) == + [ObjectId(:axis_1), ObjectId(:leaf_1)] + @test resolve_object_ids(selector_scene, Many(Relation(:ancestors)); context=:leaf_2) == + [ObjectId(:axis_1), ObjectId(:plant_1), ObjectId(:scene)] + @test resolve_object_ids(selector_scene, Many(Relation(:descendants), Scale(:Leaf)); context=:plant_1) == + [ObjectId(:leaf_1), ObjectId(:leaf_2)] + @test resolve_object_ids(selector_scene, Many(Relation(:siblings)); context=:axis_1) == + [ObjectId(:leaf_1)] + @test resolve_object_ids( + selector_scene, + Many(Relation(:ancestors), Scale(:Plant), within=SceneScope()); + context=:leaf_2, + ) == [ObjectId(:plant_1)] + @test_throws "require a current object context" resolve_object_ids( + selector_scene, + Many(Relation(:children)), + ) + @test_throws "Unsupported object relation" Relation(:cousins) @test resolve_object_ids(selector_scene, OptionalOne(scale=:Flower)) == ObjectId[] @test_throws ErrorException resolve_object_ids(selector_scene, One(scale=:Flower)) @test_throws ErrorException resolve_object_ids(selector_scene, One(scale=:Leaf)) + typo_selector_error = try + resolve_object_ids(selector_scene, One(scale=:Leef)) + nothing + catch error + sprint(showerror, error) + end + @test contains(typo_selector_error, "requested=(scale=Leef") + @test contains(typo_selector_error, "available=(scales = [:Axis, :Leaf, :Plant, :Scene, :Soil]") + @test contains(typo_selector_error, "suggestions=(scale = [:Leaf]") + ambiguous_selector_error = try + resolve_object_ids(selector_scene, One(scale=:Leaf)) + nothing + catch error + sprint(showerror, error) + end + @test contains(ambiguous_selector_error, "matched_ids=[:leaf_1, :leaf_2, :leaf_3]") + scope_selector_error = try + resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Scope(:palm_3))) + nothing + catch error + sprint(showerror, error) + end + @test contains(scope_selector_error, "available=[:axis_1") + @test contains(scope_selector_error, "suggestions=[:palm_1, :palm_2]") @test_throws ErrorException resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Self())) @test resolve_object_ids(selector_scene, Many(scale=:Leaf); context=:plant_1) == [ObjectId(:leaf_1), ObjectId(:leaf_2), ObjectId(:leaf_3)] + relation_input_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :plant_1; + scale=:Plant, + kind=:plant, + parent=:scene, + status=Status(signal=0.0), + ), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:plant_1, + status=Status(signal=0.0, observed_signal=0.0), + ); + applications=( + ModelSpec(SceneObjectSignalSourceModel(); name=:plant_signal) |> + AppliesTo(One(scale=:Plant)), + ModelSpec(SceneObjectSignalConsumerModel(); name=:leaf_consumer) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs( + :signal => One( + Relation(:parent), + process=:scene_object_signal_source, + var=:signal, + ), + ), + ), + ) + relation_input_compiled = refresh_bindings!(relation_input_scene) + relation_binding = only( + row for row in explain_bindings(relation_input_compiled) + if row.application_id == :leaf_consumer + ) + @test relation_binding.source_ids == [:plant_1] + @test object_address(relation_binding.selector).relation == :parent + @test relation_input_compiled.application_order == [:plant_signal, :leaf_consumer] + run!(relation_input_scene) + @test only(scene_objects(relation_input_scene; scale=:Leaf)).status.observed_signal == 1.0 + shared_signal_model = SceneObjectParameterizedSignalModel(1.0) shared_template_parameters = Dict(:signal_increment => 1.0) plant_template = ObjectTemplate( @@ -709,12 +1243,15 @@ end @test applies_to(spec) isa Many @test applies_to(spec).criteria.kind == :plant @test value_inputs(spec).leaf_areas isa Many + @test PlantSimEngine.input_origins(spec).leaf_areas == :model_spec + @test PlantSimEngine.input_origins(spec).leaf_carbon == :model_spec @test value_inputs(spec).leaf_carbon.criteria.policy isa Integrate @test value_inputs(spec).leaf_carbon.criteria.window == Day(1) @test model_calls(spec).stomata isa One + @test PlantSimEngine.call_origins(spec).stomata == :model_spec @test object_address(model_calls(spec).stomata).process == :stomatal_conductance call_dep = dep(spec).stomata - @test call_dep isa HardDomains + @test call_dep isa PlantSimEngine.HardDomains @test call_dep.scale == :Leaf @test call_dep.process == :stomatal_conductance @test PlantSimEngine.timestep(spec) == Hour(1) @@ -723,9 +1260,9 @@ end # The old multirate metadata stays available while the compiler is migrated. legacy_and_unified = spec |> - InputBindings(; var1=(process=:process1, var=:var3)) |> + PlantSimEngine.InputBindings(; var1=(process=:process1, var=:var3)) |> OutputRouting(; var3=:stream_only) |> - ScopeModel(:plant) |> + PlantSimEngine.ScopeModel(:plant) |> Updates(:var3; after=:process1) @test input_bindings(legacy_and_unified).var1.process == :process1 @test output_routing(legacy_and_unified).var3 == :stream_only @@ -739,7 +1276,9 @@ end @test rows[1].application_name == :leaf_energy @test rows[1].applies_to === applies_to(spec) @test rows[1].value_inputs == value_inputs(spec) + @test rows[1].input_origins == PlantSimEngine.input_origins(spec) @test rows[1].model_calls == model_calls(spec) + @test rows[1].call_origins == PlantSimEngine.call_origins(spec) @test rows[1].environment === environment_config(spec) leaf_assim = ModelSpec(ToyAssimModel()) |> @@ -764,6 +1303,8 @@ end default_input_spec = ModelSpec(SceneObjectDefaultInputConsumerModel()) @test value_inputs(default_input_spec).leaf_carbon isa Many + @test PlantSimEngine.input_origins(default_input_spec).leaf_carbon == + :model_default @test value_inputs(default_input_spec).leaf_carbon.criteria.within isa Self @test !haskey(dep(default_input_spec), :leaf_carbon) @test length(PlantSimEngine.get_mapped_variables(default_input_spec)) == 1 @@ -771,25 +1312,61 @@ end override_input_spec = ModelSpec(SceneObjectDefaultInputConsumerModel()) |> Inputs(:leaf_carbon => Many(scale=:Leaf, var=:carbon_override)) @test value_inputs(override_input_spec).leaf_carbon.criteria.var == :carbon_override + @test PlantSimEngine.input_origins(override_input_spec).leaf_carbon == + :model_spec mapped = only(PlantSimEngine.get_mapped_variables(override_input_spec)) @test first(mapped) == :leaf_carbon @test last(mapped) == [:Leaf => :carbon_override] + default_input_origin_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:plant_1, + status=Status(leaf_carbon=2.0, carbon_override=3.0), + ), + ) + default_input_origin_compiled = compile_scene( + default_input_origin_scene, + ( + default_input_spec |> + AppliesTo(One(scale=:Plant)), + ), + ) + @test only(explain_bindings(default_input_origin_compiled)).origin == + :model_default + override_input_origin_compiled = compile_scene( + default_input_origin_scene, + ( + override_input_spec |> + AppliesTo(One(scale=:Plant)), + ), + ) + @test only(explain_bindings(override_input_origin_compiled)).origin == + :model_spec + default_call_spec = ModelSpec(SceneObjectDefaultCallConsumerModel()) @test model_calls(default_call_spec).stomata isa One + @test PlantSimEngine.call_origins(default_call_spec).stomata == + :model_default @test model_calls(default_call_spec).stomata.criteria.scale == :Leaf @test model_calls(default_call_spec).stomata.criteria.process == :stomatal_conductance default_call_dep = dep(default_call_spec).stomata - @test default_call_dep isa HardDomains + @test default_call_dep isa PlantSimEngine.HardDomains @test default_call_dep.scale == :Leaf @test default_call_dep.process == :stomatal_conductance override_call_spec = ModelSpec(SceneObjectDefaultCallConsumerModel()) |> Calls(:stomata => One(scale=:Internode, process=:water_status)) @test model_calls(override_call_spec).stomata.criteria.scale == :Internode + @test PlantSimEngine.call_origins(override_call_spec).stomata == + :model_spec @test model_calls(override_call_spec).stomata.criteria.process == :water_status override_call_dep = dep(override_call_spec).stomata - @test override_call_dep isa HardDomains + @test override_call_dep isa PlantSimEngine.HardDomains @test override_call_dep.scale == :Internode @test override_call_dep.process == :water_status @@ -812,6 +1389,7 @@ end @test length(binding_rows) == 3 leaf_2_binding = only(row for row in binding_rows if row.consumer_id == :leaf_2) @test leaf_2_binding.application_id == :leaf_energy + @test leaf_2_binding.origin == :model_spec @test leaf_2_binding.input == :leaf_areas @test leaf_2_binding.source_ids == [:leaf_1, :leaf_2] @test leaf_2_binding.source_var == :leaf_area @@ -823,10 +1401,173 @@ end @test length(call_rows) == 3 leaf_2_call = only(row for row in call_rows if row.consumer_id == :leaf_2) @test leaf_2_call.application_id == :leaf_energy + @test leaf_2_call.origin == :model_default @test leaf_2_call.call == :stomata @test leaf_2_call.callee_object_ids == [:leaf_2] @test leaf_2_call.callee_application_ids == [:stomata] @test leaf_2_call.process == :scene_object_stomata + @test leaf_2_call.publication_policy == :explicit_accept + @test !leaf_2_call.default_publish + @test leaf_2_call.accepted_publish + + leaf_2_application = compiled.applications_by_id[:leaf_energy] + leaf_2_models = compiled.model_bundles_by_target[(:leaf_energy, ObjectId(:leaf_2))] + @test keys(leaf_2_models) == (:scene_object_leaf_energy, :scene_object_stomata) + @test leaf_2_models.scene_object_leaf_energy === + PlantSimEngine._application_model(leaf_2_application, ObjectId(:leaf_2)) + @test leaf_2_models.scene_object_stomata === + PlantSimEngine._application_model(compiled.applications_by_id[:stomata], ObjectId(:leaf_2)) + @test PlantSimEngine._scene_models_for_application( + compiled, + leaf_2_application, + ObjectId(:leaf_2), + ) === leaf_2_models + PlantSimEngine._scene_models_for_application(compiled, leaf_2_application, ObjectId(:leaf_2)) + @test @allocated( + PlantSimEngine._scene_models_for_application( + compiled, + leaf_2_application, + ObjectId(:leaf_2), + ) + ) == 0 + bundle_row = only( + row for row in explain_model_bundles(compiled) + if row.application_id == :leaf_energy && row.object_id == :leaf_2 + ) + @test bundle_row.processes == [:scene_object_leaf_energy, :scene_object_stomata] + @test bundle_row.model_types == [SceneObjectLeafEnergyModel, SceneObjectStomataModel] + + compiled_environment = compile_environment_bindings(selector_scene, compiled) + execution_plan = + PlantSimEngine.compile_scene_execution_plan(compiled, compiled_environment) + execution_rows = explain_execution_plan(execution_plan) + @test length(execution_rows) == 1 + @test only(execution_rows).application_id == :leaf_energy + @test only(execution_rows).object_ids == [:leaf_1, :leaf_2, :leaf_3] + @test only(execution_rows).batch_size == 3 + @test only(execution_rows).inner_loop_dispatch == :concrete_homogeneous_batch + @test isconcretetype(only(execution_rows).target_type) + + batch_counter = Ref(0) + batch_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + ( + Object( + Symbol(:batch_leaf_, index); + scale=:Leaf, + kind=:plant, + parent=:scene, + status=Status(), + ) + for index in 1:128 + )...; + applications=( + ModelSpec( + SceneObjectBatchCounterModel(batch_counter); + name=:batch_counter, + ) |> + AppliesTo(Many(scale=:Leaf)), + ), + ) + batch_compiled = refresh_bindings!(batch_scene) + batch_environment = refresh_environment_bindings!(batch_scene, batch_compiled) + batch_plan = + PlantSimEngine.compile_scene_execution_plan(batch_compiled, batch_environment) + @test length(batch_plan.batches) == 1 + homogeneous_batch = only(batch_plan.batches) + @test isconcretetype(eltype(homogeneous_batch.targets)) + PlantSimEngine._run_scene_execution_batch!( + homogeneous_batch, + batch_compiled, + batch_environment; + time=1, + temporal_streams=nothing, + ) + @test @allocated( + PlantSimEngine._run_scene_execution_batch!( + homogeneous_batch, + batch_compiled, + batch_environment; + time=1, + temporal_streams=nothing, + ) + ) == 0 + @test batch_counter[] == 256 + + heterogeneous_template = ObjectTemplate( + ( + ModelSpec(SceneObjectParameterizedSignalModel(1.0); name=:signal) |> + AppliesTo(Many(scale=:Leaf)), + ); + kind=:plant, + ) + heterogeneous_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + ObjectInstance( + :heterogeneous_plant, + heterogeneous_template; + root=Object( + :heterogeneous_plant_object; + scale=:Plant, + parent=:scene, + ), + objects=( + Object( + :heterogeneous_leaf_a; + scale=:Leaf, + parent=:heterogeneous_plant_object, + status=Status(signal=0.0), + ), + Object( + :heterogeneous_leaf_b; + scale=:Leaf, + parent=:heterogeneous_plant_object, + status=Status(signal=0.0), + ), + Object( + :heterogeneous_leaf_c; + scale=:Leaf, + parent=:heterogeneous_plant_object, + status=Status(signal=0.0), + ), + ), + object_overrides=( + Override( + object=:heterogeneous_leaf_b, + application=:signal, + model=SceneObjectAlternativeSignalModel(), + ), + ), + ), + ) + heterogeneous_compiled = refresh_bindings!(heterogeneous_scene) + heterogeneous_environment = + refresh_environment_bindings!(heterogeneous_scene, heterogeneous_compiled) + heterogeneous_plan = PlantSimEngine.compile_scene_execution_plan( + heterogeneous_compiled, + heterogeneous_environment, + ) + heterogeneous_rows = explain_execution_plan(heterogeneous_plan) + @test getproperty.(heterogeneous_rows, :object_ids) == [ + [:heterogeneous_leaf_a], + [:heterogeneous_leaf_b], + [:heterogeneous_leaf_c], + ] + @test getproperty.(heterogeneous_rows, :model_type) == [ + SceneObjectParameterizedSignalModel{Float64}, + SceneObjectAlternativeSignalModel, + SceneObjectParameterizedSignalModel{Float64}, + ] + run!(heterogeneous_scene) + heterogeneous_values = Dict( + object.id.value => object.status.signal + for object in scene_objects(heterogeneous_scene; scale=:Leaf) + ) + @test heterogeneous_values == Dict( + :heterogeneous_leaf_a => 1.0, + :heterogeneous_leaf_b => 7.0, + :heterogeneous_leaf_c => 1.0, + ) ambiguous_call_specs = ( ModelSpec(SceneObjectStomataModel(); name=:sunlit_stomata) |> @@ -851,12 +1592,106 @@ end ) disambiguated = compile_scene(selector_scene, disambiguated_call_specs) disambiguated_call = only(row for row in explain_calls(disambiguated) if row.consumer_id == :leaf_2) + @test disambiguated_call.origin == :model_spec @test disambiguated_call.callee_application_ids == [:sunlit_stomata] @test disambiguated_call.application == :sunlit_stomata leaf_2_call_bindings = disambiguated.call_bindings_by_target[(:leaf_energy, ObjectId(:leaf_2))] @test length(leaf_2_call_bindings) == 1 @test only(leaf_2_call_bindings).call == :stomata + optional_dependency_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + status=Status(optional_signal=99.0), + ); + applications=( + ModelSpec( + SceneObjectOptionalInputConsumerModel(); + name=:optional_input_consumer, + ) |> + AppliesTo(One(scale=:Scene)) |> + Inputs( + :optional_signal => OptionalOne( + scale=:Leaf, + within=SceneScope(), + process=:missing_optional_source, + var=:optional_signal, + ), + ), + ModelSpec( + SceneObjectOptionalCallConsumerModel(); + name=:optional_call_consumer, + ) |> + AppliesTo(One(scale=:Scene)) |> + Calls( + :optional_source => OptionalOne( + scale=:Leaf, + within=SceneScope(), + process=:missing_optional_source, + ), + ), + ), + ) + optional_compiled = refresh_bindings!(optional_dependency_scene) + optional_binding = only(explain_bindings(optional_compiled)) + @test optional_binding.multiplicity == :optional_one + @test isempty(optional_binding.source_ids) + @test isempty(optional_binding.source_application_ids) + @test optional_binding.carrier_hint == :optional_default + @test optional_binding.carrier_kind == :optional_default + @test optional_binding.copy_semantics == :consumer_default + optional_call = only(explain_calls(optional_compiled)) + @test optional_call.multiplicity == :optional_one + @test optional_call.callee_object_ids == [:leaf_1] + @test isempty(optional_call.callee_application_ids) + @test !optional_call.resolved + run!(optional_dependency_scene) + optional_scene_status = + only(scene_objects(optional_dependency_scene; scale=:Scene)).status + @test optional_scene_status.optional_signal == 7.0 + @test optional_scene_status.observed_optional_signal == 7.0 + @test optional_scene_status.optional_call_count == 0 + + renamed_input_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene); + applications=( + ModelSpec( + SceneObjectRenamedSignalConsumerModel(); + name=:renamed_consumer, + ) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs( + :renamed_signal => One( + scale=:Leaf, + within=Self(), + application=:signal_source, + var=:signal, + ), + ), + ModelSpec(SceneObjectSignalSetModel(12.5); name=:signal_source) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + renamed_compiled = refresh_bindings!(renamed_input_scene) + renamed_binding = only(explain_bindings(renamed_compiled)) + @test renamed_binding.input == :renamed_signal + @test renamed_binding.source_var == :signal + @test renamed_binding.source_application_ids == [:signal_source] + @test renamed_binding.carrier_kind == :ref + @test renamed_binding.copy_semantics == :live_references + @test renamed_compiled.application_order == + [:signal_source, :renamed_consumer] + renamed_status = only(scene_objects(renamed_input_scene; scale=:Leaf)).status + @test PlantSimEngine.refvalue(renamed_status, :renamed_signal) === + PlantSimEngine.refvalue(renamed_status, :signal) + run!(renamed_input_scene) + @test renamed_status.observed_renamed_signal == 12.5 + default_scope_scene = Scene( Object(:scene; scale=:Scene, kind=:scene, status=Status(signal_sum=0.0, temporal_total=0.0)), Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene, status=Status(signal_sum=0.0, temporal_total=0.0)), @@ -909,6 +1744,12 @@ end @test inferred_binding.has_reference_carrier @test inferred_binding.carrier_kind == :ref @test inferred_binding.copy_semantics == :live_references + inferred_consumer_status = only(scene_objects(inferred_input_scene; scale=:Leaf)).status + inferred_compiled_binding = only( + binding for binding in inferred_compiled.input_bindings + if binding.application_id == :signal_consumer + ) + @test PlantSimEngine.refvalue(inferred_consumer_status, :signal) === input_carrier(inferred_compiled_binding) inferred_input_scene_with_apps = Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0, observed_signal=0.0)); @@ -917,6 +1758,29 @@ end run!(inferred_input_scene_with_apps) @test only(scene_objects(inferred_input_scene_with_apps; scale=:Leaf)).status.observed_signal == 1.0 + generated_status_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene); + applications=( + ModelSpec(SceneObjectSignalSourceModel(); name=:signal_source) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_consumer) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + generated_status_compiled = refresh_bindings!(generated_status_scene) + generated_status = only(scene_objects(generated_status_scene; scale=:Leaf)).status + @test generated_status isa Status + @test Set(propertynames(generated_status)) == + Set((:signal, :observed_signal)) + generated_binding = only( + binding for binding in generated_status_compiled.input_bindings + if binding.application_id == :signal_consumer + ) + @test PlantSimEngine.refvalue(generated_status, :signal) === input_carrier(generated_binding) + run!(generated_status_scene) + @test generated_status.observed_signal == 1.0 + reversed_dependency_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0, observed_signal=0.0)); @@ -946,10 +1810,100 @@ end ), ) - @test_throws ErrorException compile_scene( - Scene( + lagged_cycle_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + status=Status(cycle_a=0.0, cycle_b=1.0), + ); + applications=( + ModelSpec(SceneObjectCycleAModel(); name=:cycle_a) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs( + PreviousTimeStep(:cycle_b) => One( + scale=:Leaf, + process=:scene_object_cycle_b, + var=:cycle_b, + ), + ), + ModelSpec(SceneObjectCycleBModel(); name=:cycle_b) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + lagged_cycle_compiled = refresh_bindings!(lagged_cycle_scene) + @test lagged_cycle_compiled.application_order == [:cycle_a, :cycle_b] + lagged_binding = only( + row for row in explain_bindings(lagged_cycle_compiled) + if row.application_id == :cycle_a && row.input == :cycle_b + ) + @test lagged_binding.policy == PreviousTimeStep(:cycle_b) + @test lagged_binding.carrier_kind == :temporal_stream + @test lagged_binding.copy_semantics == :materialized_temporal_value + lagged_cycle_simulation = run!( + lagged_cycle_scene; + steps=3, + tracked_outputs=OutputRequest( + :Leaf, + :cycle_a; + name=:lagged_cycle_a, + application=:cycle_a, + ), + ) + lagged_cycle_status = only(scene_objects(lagged_cycle_scene; scale=:Leaf)).status + @test lagged_cycle_status.cycle_a == 11.0 + @test lagged_cycle_status.cycle_b == 22.0 + @test getproperty.( + collect_outputs( + lagged_cycle_simulation, + :leaf_1, + :cycle_a; + sink=nothing, + ), + :value, + ) == [2.0, 5.0, 11.0] + lagged_source_stream = scene_outputs(lagged_cycle_simulation)[ + (:cycle_b, ObjectId(:leaf_1), :cycle_b) + ] + @test getindex.(lagged_source_stream, 1) == [2.0, 3.0] + @test getindex.(lagged_source_stream, 2) == [10.0, 22.0] + @test only( + row for row in explain_output_retention(lagged_cycle_simulation) + if row.application_id == :cycle_b + ).retention_steps == 2.0 + + mismatched_lag_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + status=Status(cycle_a=0.0, cycle_b=1.0), + ); + applications=( + ModelSpec(SceneObjectCycleAModel(); name=:cycle_a) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs( + :cycle_b => One( + scale=:Leaf, + process=:scene_object_cycle_b, + var=:cycle_b, + policy=PreviousTimeStep(:other), + ), + ), + ModelSpec(SceneObjectCycleBModel(); name=:cycle_b) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + @test_throws "PreviousTimeStep marker for input `cycle_b`" refresh_bindings!(mismatched_lag_scene) + + @test_throws ErrorException compile_scene( + Scene( Object(:scene; scale=:Scene, kind=:scene), - Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(observed_signal=0.0)), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene), ), ( ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_consumer) |> @@ -977,7 +1931,7 @@ end Inputs(:signal => One(scale=:Leaf, var=:signal, process=:scene_object_signal_source, application=:signal_source)), ) filtered_binding = only(explain_bindings(compile_scene(inferred_input_scene, filtered_input_specs))) - @test filtered_binding.origin == :declared + @test filtered_binding.origin == :model_spec @test filtered_binding.source_application_ids == [:signal_source] @test filtered_binding.process == :scene_object_signal_source @test filtered_binding.application == :signal_source @@ -1073,6 +2027,58 @@ end @test scalar_row.carrier_kind == :ref @test scalar_row.copy_semantics == :live_references + dual_a = SceneObjectDualLike(big"1.25", big"0.5") + dual_b = SceneObjectDualLike(big"2.75", big"1.5") + generic_carrier_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + status=Status(dual_value=dual_a), + ), + Object( + :leaf_2; + scale=:Leaf, + kind=:plant, + parent=:scene, + status=Status(dual_value=dual_b), + ); + applications=( + ModelSpec(SceneObjectDualLikeSumModel(); name=:dual_sum) |> + AppliesTo(One(scale=:Scene)) |> + Inputs( + :values => Many( + scale=:Leaf, + within=SceneScope(), + var=:dual_value, + ), + ), + ), + ) + generic_carrier_compiled = refresh_bindings!(generic_carrier_scene) + generic_carrier_binding = only(generic_carrier_compiled.input_bindings) + @test input_carrier(generic_carrier_binding) isa + PlantSimEngine.RefVector{SceneObjectDualLike{BigFloat}} + @test eltype(input_carrier(generic_carrier_binding)) == + SceneObjectDualLike{BigFloat} + generic_carrier_sim = run!(generic_carrier_scene) + generic_scene_status = only(scene_objects(generic_carrier_scene; scale=:Scene)).status + @test generic_scene_status.values === input_carrier(generic_carrier_binding) + @test generic_scene_status.total == SceneObjectDualLike(big"4.0", big"2.0") + generic_leaf_1 = + only(object for object in scene_objects(generic_carrier_scene; scale=:Leaf) + if object.id == ObjectId(:leaf_1)) + generic_leaf_1.status.dual_value = SceneObjectDualLike(big"3.25", big"2.5") + @test generic_scene_status.values[1] == + SceneObjectDualLike(big"3.25", big"2.5") + @test eltype( + scene_outputs(generic_carrier_sim)[ + (:dual_sum, ObjectId(:scene), :total) + ], + ) == Tuple{Float64,SceneObjectDualLike{BigFloat}} + cache_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:plant_1; scale=:Plant, kind=:plant, species=:oil_palm, name=:palm_1, parent=:scene), @@ -1144,6 +2150,7 @@ end @test length(compiled_environment.by_target) == length(compiled_environment.bindings) @test compiled_environment.by_target[(:probe, ObjectId(:leaf_1))].cell == :cell_a @test compiled_environment.by_target[(:temperature_update, ObjectId(:leaf_2))].cell == :cell_b + @test length(grid_backend.binds) == 4 @test length(grid_backend.index_updates) == 1 @test any(entity -> entity.id == :leaf_1 && entity.geometry == (cell=:cell_a,), grid_backend.index_updates[1]) @test any(entity -> entity.id == :plant_1 && entity.scale == :Plant, grid_backend.index_updates[1]) @@ -1157,8 +2164,282 @@ end leaf_2_update = only(row for row in environment_rows if row.application_id == :temperature_update && row.object_id == :leaf_2) @test leaf_2_update.cell == :cell_b @test leaf_2_update.required_inputs == [:T] + @test leaf_2_update.source_inputs == [:T] @test leaf_2_update.produced_outputs == [:T] + missing_global_meteo_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene); + applications=( + ModelSpec(SceneObjectEnvironmentCO2ProbeModel(); name=:co2_probe) |> + AppliesTo(One(scale=:Leaf)) |> + Environment(provider=:global), + ), + environment=(T=20.0,), + ) + @test_throws "co2_probe" validate_meteo_inputs(missing_global_meteo_scene) + @test_throws "Scene environment is missing required meteo inputs" refresh_environment_bindings!(missing_global_meteo_scene) + @test_throws "source `CO2`" refresh_environment_bindings!(missing_global_meteo_scene) + + application_environment_backend = + SceneObjectMutableEnvironmentBackend(:cell_a => 23.0) + application_environment_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + geometry=(cell=:cell_a,), + ); + applications=( + ModelSpec(SceneObjectEnvironmentCO2ProbeModel(); name=:local_co2_probe) |> + AppliesTo(One(scale=:Leaf)) |> + Environment(backend=application_environment_backend), + ), + environment=(T=20.0,), + ) + @test validate_meteo_inputs(application_environment_scene) === nothing + @test validate_meteo_inputs(refresh_bindings!(application_environment_scene)) === + nothing + @test_throws "CO2" validate_meteo_inputs( + refresh_bindings!(application_environment_scene), + (T=20.0,), + ) + + remapped_global_meteo_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene); + applications=( + ModelSpec(SceneObjectEnvironmentCO2ProbeModel(); name=:co2_probe) |> + AppliesTo(One(scale=:Leaf)) |> + Environment(provider=:global, sources=(CO2=:Ca,)), + ), + environment=(T=20.0, Ca=415.0), + ) + @test validate_meteo_inputs(remapped_global_meteo_scene) === nothing + @test_throws "Ca" validate_meteo_inputs( + refresh_bindings!(remapped_global_meteo_scene), + (T=20.0, CO2=415.0), + ) + @test validate_meteo_inputs( + refresh_bindings!(remapped_global_meteo_scene), + (T=20.0, Ca=415.0), + ) === nothing + remapped_environment = refresh_environment_bindings!(remapped_global_meteo_scene) + remapped_row = only(explain_environment_bindings(remapped_environment)) + @test remapped_row.required_inputs == [:T, :CO2] + @test remapped_row.source_inputs == [:T, :Ca] + run!(remapped_global_meteo_scene) + remapped_status = only(scene_objects(remapped_global_meteo_scene; scale=:Leaf)).status + @test remapped_status.temperature_seen == 20.0 + @test remapped_status.co2_seen == 415.0 + + hinted_global_meteo_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene); + applications=( + ModelSpec(SceneObjectEnvironmentCO2HintProbeModel(); name=:co2_probe) |> + AppliesTo(One(scale=:Leaf)) |> + Environment(provider=:global), + ), + environment=(T=21.0, Ca=420.0), + ) + @test validate_meteo_inputs(hinted_global_meteo_scene) === nothing + hinted_environment = refresh_environment_bindings!(hinted_global_meteo_scene) + hinted_row = only(explain_environment_bindings(hinted_environment)) + @test hinted_row.required_inputs == [:T, :CO2] + @test hinted_row.source_inputs == [:T, :Ca] + hinted_application = refresh_bindings!(hinted_global_meteo_scene).applications_by_id[:co2_probe] + @test meteo_bindings(hinted_application.spec).CO2.source == :Ca + run!(hinted_global_meteo_scene) + hinted_status = only(scene_objects(hinted_global_meteo_scene; scale=:Leaf)).status + @test hinted_status.temperature_seen == 21.0 + @test hinted_status.co2_seen == 420.0 + + hinted_override_global_meteo_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene); + applications=( + ModelSpec(SceneObjectEnvironmentCO2HintProbeModel(); name=:co2_probe) |> + AppliesTo(One(scale=:Leaf)) |> + Environment(provider=:global, sources=(CO2=:Cb,)), + ), + environment=(T=22.0, Ca=420.0, Cb=430.0), + ) + hinted_override_row = only( + explain_environment_bindings(refresh_environment_bindings!(hinted_override_global_meteo_scene)) + ) + @test hinted_override_row.source_inputs == [:T, :Cb] + hinted_override_application = + refresh_bindings!(hinted_override_global_meteo_scene).applications_by_id[:co2_probe] + @test meteo_bindings(hinted_override_application.spec).CO2.source == :Cb + @test meteo_bindings(hinted_override_application.spec).CO2.reducer isa MeanReducer + run!(hinted_override_global_meteo_scene) + hinted_override_status = + only(scene_objects(hinted_override_global_meteo_scene; scale=:Leaf)).status + @test hinted_override_status.temperature_seen == 22.0 + @test hinted_override_status.co2_seen == 430.0 + + if PlantSimEngine._has_meteo_sampler_api() + windowed_weather = Weather([ + Atmosphere( + T=10.0, + Wind=1.0, + Rh=0.50, + P=100.0, + duration=Hour(1), + Ca=400.0, + Cb=410.0, + ), + Atmosphere( + T=20.0, + Wind=1.0, + Rh=0.60, + P=100.0, + duration=Hour(1), + Ca=410.0, + Cb=420.0, + ), + Atmosphere( + T=30.0, + Wind=1.0, + Rh=0.70, + P=100.0, + duration=Hour(1), + Ca=420.0, + Cb=430.0, + ), + Atmosphere( + T=40.0, + Wind=1.0, + Rh=0.80, + P=100.0, + duration=Hour(1), + Ca=430.0, + Cb=440.0, + ), + ]) + windowed_default_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene), + Object(:leaf_2; scale=:Leaf, kind=:plant, parent=:scene); + applications=( + ModelSpec(SceneObjectTemperatureOnlyProbeModel(); name=:temperature_probe) |> + AppliesTo(Many(scale=:Leaf)) |> + TimeStep(Hour(2)), + ), + environment=windowed_weather, + ) + windowed_default_sim = run!(windowed_default_scene; steps=4) + for leaf in scene_objects(windowed_default_scene; scale=:Leaf) + @test leaf.status.temperature_seen == 25.0 + values = getproperty.( + collect_outputs( + windowed_default_sim, + leaf.id.value, + :temperature_seen; + sink=nothing, + ), + :value, + ) + @test values == [10.0, 25.0] + end + @test length(windowed_default_sim.environment_bindings.sample_cache) == 2 + @test all( + row -> row.temporal_sampler, + explain_environment_bindings(windowed_default_sim.environment_bindings), + ) + + windowed_override_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene); + applications=( + ModelSpec( + SceneObjectAggregatedEnvironmentProbeModel(); + name=:aggregated_probe, + ) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(2)) |> + Environment(provider=:global, sources=(CO2=:Cb,)), + ), + environment=windowed_weather, + ) + windowed_override_application = + refresh_bindings!(windowed_override_scene).applications_by_id[:aggregated_probe] + @test meteo_bindings(windowed_override_application.spec).CO2.source == :Cb + @test meteo_bindings(windowed_override_application.spec).CO2.reducer isa MeanReducer + windowed_override_sim = run!(windowed_override_scene; steps=4) + temperature_values = getproperty.( + collect_outputs( + windowed_override_sim, + :leaf_1, + :temperature_seen; + sink=nothing, + ), + :value, + ) + co2_values = getproperty.( + collect_outputs( + windowed_override_sim, + :leaf_1, + :co2_seen; + sink=nothing, + ), + :value, + ) + @test temperature_values == [10.0, 30.0] + @test co2_values == [410.0, 425.0] + end + + contract_backend = SceneObjectGridBackend() + contract_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + geometry=(cell=:cell_a,), + ); + applications=( + ModelSpec(SceneObjectEnvironmentProbeModel(); name=:probe) |> + AppliesTo(One(scale=:Leaf)) |> + Environment(provider=:grid), + ), + environment=contract_backend, + ) + contract_compiled = refresh_bindings!(contract_scene) + original_contract_bindings = + refresh_environment_bindings!(contract_scene, contract_compiled) + original_contract_binding = + original_contract_bindings.by_target[(:probe, ObjectId(:leaf_1))] + @test original_contract_binding.required_inputs == [:T, :CO2] + @test length(contract_backend.binds) == 1 + @test length(contract_backend.index_updates) == 1 + + revised_contract_compiled = compile_scene( + contract_scene, + ( + ModelSpec(SceneObjectTemperatureOnlyProbeModel(); name=:probe) |> + AppliesTo(One(scale=:Leaf)) |> + Environment(provider=:grid), + ), + ) + revised_contract_bindings = + refresh_environment_bindings!(contract_scene, revised_contract_compiled) + revised_contract_binding = + revised_contract_bindings.by_target[(:probe, ObjectId(:leaf_1))] + @test revised_contract_binding.required_inputs == [:T] + @test revised_contract_binding.cell == original_contract_binding.cell + @test revised_contract_binding !== original_contract_binding + @test length(contract_backend.binds) == 1 + @test length(contract_backend.index_updates) == 1 + @test refresh_environment_bindings!( + contract_scene, + revised_contract_compiled, + ) === revised_contract_bindings + structural_environment_cache = refresh_bindings!(environment_scene) move_object!(environment_scene, :leaf_2, (cell=:cell_c,)) @test !bindings_dirty(environment_scene) @@ -1166,6 +2447,7 @@ end @test refresh_bindings!(environment_scene) === structural_environment_cache refreshed_environment = refresh_environment_bindings!(environment_scene) @test !environment_bindings_dirty(environment_scene) + @test length(grid_backend.binds) == 6 @test length(grid_backend.index_updates) == 2 @test any(entity -> entity.id == :leaf_2 && entity.geometry == (cell=:cell_c,), grid_backend.index_updates[2]) @test only(row for row in explain_environment_bindings(refreshed_environment) if row.application_id == :probe && row.object_id == :leaf_2).cell == :cell_c @@ -1177,6 +2459,7 @@ end @test environment_bindings_dirty(environment_scene) refreshed_after_mark = refresh_environment_bindings!(environment_scene) @test !environment_bindings_dirty(environment_scene) + @test length(grid_backend.binds) == 8 @test length(grid_backend.index_updates) == 3 @test any(entity -> entity.id == :leaf_1 && entity.geometry == (cell=:cell_e,), grid_backend.index_updates[3]) @test only(row for row in explain_environment_bindings(refreshed_after_mark) if row.application_id == :probe && row.object_id == :leaf_1).cell == :cell_e @@ -1185,12 +2468,63 @@ end @test bindings_dirty(environment_scene) @test environment_bindings_dirty(environment_scene) refreshed_with_new_leaf = refresh_environment_bindings!(environment_scene) + @test length(grid_backend.binds) == 14 @test length(grid_backend.index_updates) == 4 @test any(entity -> entity.id == :leaf_3 && entity.geometry == (cell=:cell_d,), grid_backend.index_updates[4]) @test only(row for row in explain_scene_applications(refresh_bindings!(environment_scene)) if row.application_id == :probe).target_ids == [:leaf_1, :leaf_2, :leaf_3] @test only(row for row in explain_environment_bindings(refreshed_with_new_leaf) if row.application_id == :probe && row.object_id == :leaf_3).cell == :cell_d + inherited_grid_backend = SceneObjectGridBackend() + inherited_environment_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :plant_1; + scale=:Plant, + kind=:plant, + parent=:scene, + geometry=(cell=:cell_a,), + ), + Object(:inherited_leaf; scale=:Leaf, kind=:plant, parent=:plant_1), + Object( + :positioned_leaf; + scale=:Leaf, + kind=:plant, + parent=:plant_1, + geometry=(cell=:cell_c,), + ); + applications=( + ModelSpec(SceneObjectEnvironmentProbeModel(); name=:inherited_probe) |> + AppliesTo(Many(scale=:Leaf)) |> + Environment(provider=:grid), + ), + environment=inherited_grid_backend, + ) + inherited_bindings = refresh_environment_bindings!(inherited_environment_scene) + inherited_rows = explain_environment_bindings(inherited_bindings) + inherited_row = only(row for row in inherited_rows if row.object_id == :inherited_leaf) + positioned_row = only(row for row in inherited_rows if row.object_id == :positioned_leaf) + @test inherited_row.cell == :cell_a + @test inherited_row.geometry_source == :ancestor + @test inherited_row.geometry_source_object_id == :plant_1 + @test positioned_row.cell == :cell_c + @test positioned_row.geometry_source == :self + positioned_binding = inherited_bindings.by_target[ + (:inherited_probe, ObjectId(:positioned_leaf)) + ] + + update_geometry!(inherited_environment_scene, :plant_1, (cell=:cell_b,)) + @test environment_bindings_dirty(inherited_environment_scene) + refreshed_inherited_bindings = + refresh_environment_bindings!(inherited_environment_scene) + refreshed_inherited_rows = explain_environment_bindings(refreshed_inherited_bindings) + @test only( + row for row in refreshed_inherited_rows if row.object_id == :inherited_leaf + ).cell == :cell_b + @test refreshed_inherited_bindings.by_target[ + (:inherited_probe, ObjectId(:positioned_leaf)) + ] === positioned_binding + mutable_environment_backend = SceneObjectMutableEnvironmentBackend(:cell_a => 20.0, :cell_b => 30.0) mutable_environment_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), @@ -1234,6 +2568,37 @@ end run!(runtime_scene) @test all(object.status.carrier_total == 4.0 for object in scene_objects(runtime_scene; scale=:Leaf)) @test all(object.status.temperature_seen == 27.5 for object in scene_objects(runtime_scene; scale=:Leaf)) + runtime_compiled = refresh_bindings!(runtime_scene) + runtime_application = runtime_compiled.applications_by_id[:carrier_runtime] + runtime_object_id = ObjectId(:leaf_1) + PlantSimEngine._materialize_scene_inputs!( + runtime_compiled, + runtime_application, + runtime_object_id, + nothing, + 1, + ) + runtime_status = PlantSimEngine._scene_object_status(runtime_scene, runtime_object_id) + runtime_bindings = runtime_compiled.input_bindings_by_target[ + (:carrier_runtime, runtime_object_id) + ] + runtime_binding = only(runtime_bindings) + @test runtime_status.leaf_areas === input_carrier(runtime_binding) + runtime_leaf_2 = only( + object for object in scene_objects(runtime_scene; scale=:Leaf) + if object.id == ObjectId(:leaf_2) + ) + runtime_leaf_2.status.leaf_area = 7.0 + @test runtime_status.leaf_areas[2] == 7.0 + @test @allocated( + PlantSimEngine._materialize_scene_inputs!( + runtime_compiled, + runtime_application, + runtime_object_id, + nothing, + 1, + ) + ) == 0 call_runtime_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), @@ -1254,6 +2619,110 @@ end @test !only(row for row in call_schedule if row.application_id == :signal_source).root_scheduled @test only(row for row in call_schedule if row.application_id == :signal_caller).root_scheduled + hard_call_meteo_backend = SceneObjectMutableEnvironmentBackend(:cell_a => 20.0) + hard_call_meteo_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + geometry=(cell=:cell_a,), + status=Status(T=0.0, temperature_seen=0.0, called_temperature=0.0), + ); + applications=( + ModelSpec(SceneObjectMeteoCallSourceModel(); name=:meteo_source) |> + AppliesTo(One(scale=:Leaf)) |> + Environment(provider=:grid), + ModelSpec(SceneObjectMeteoCallControllerModel(31.5, false); name=:meteo_controller) |> + AppliesTo(One(scale=:Leaf)) |> + Calls(:source => One(scale=:Leaf, process=:scene_object_meteo_call_source)), + ), + environment=hard_call_meteo_backend, + ) + run!(hard_call_meteo_scene) + hard_call_meteo_status = only(scene_objects(hard_call_meteo_scene; scale=:Leaf)).status + @test hard_call_meteo_status.temperature_seen == 31.5 + @test hard_call_meteo_status.called_temperature == 31.5 + @test hard_call_meteo_backend.values[:cell_a] == 20.0 + @test isempty(hard_call_meteo_backend.writes) + + publish_hard_call_meteo_backend = SceneObjectMutableEnvironmentBackend(:cell_a => 20.0) + publish_hard_call_meteo_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + geometry=(cell=:cell_a,), + status=Status(T=0.0, temperature_seen=0.0, called_temperature=0.0), + ); + applications=( + ModelSpec(SceneObjectMeteoCallSourceModel(); name=:meteo_source) |> + AppliesTo(One(scale=:Leaf)) |> + Environment(provider=:grid), + ModelSpec(SceneObjectMeteoCallControllerModel(32.5, true); name=:meteo_controller) |> + AppliesTo(One(scale=:Leaf)) |> + Calls(:source => One(scale=:Leaf, process=:scene_object_meteo_call_source)), + ), + environment=publish_hard_call_meteo_backend, + ) + run!(publish_hard_call_meteo_scene) + publish_hard_call_meteo_status = only(scene_objects(publish_hard_call_meteo_scene; scale=:Leaf)).status + @test publish_hard_call_meteo_status.temperature_seen == 32.5 + @test publish_hard_call_meteo_status.called_temperature == 32.5 + @test publish_hard_call_meteo_backend.values[:cell_a] == 33.5 + @test publish_hard_call_meteo_backend.writes == [ + (application=:meteo_source, process=:scene_object_meteo_call_source, cell=:cell_a, variable=:T, value=33.5, time=1), + ] + + iterative_hard_call_backend = SceneObjectMutableEnvironmentBackend(:cell_a => 20.0) + iterative_hard_call_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + geometry=(cell=:cell_a,), + status=Status(T=0.0, temperature_seen=0.0, called_temperature=0.0), + ); + applications=( + ModelSpec(SceneObjectMeteoCallSourceModel(); name=:meteo_source) |> + AppliesTo(One(scale=:Leaf)) |> + Environment(provider=:grid), + ModelSpec( + SceneObjectIterativeMeteoCallControllerModel((30.0, 31.0), 32.0); + name=:meteo_controller, + ) |> + AppliesTo(One(scale=:Leaf)) |> + Calls(:source => One(scale=:Leaf, process=:scene_object_meteo_call_source)), + ), + environment=iterative_hard_call_backend, + ) + iterative_hard_call_sim = run!(iterative_hard_call_scene) + iterative_hard_call_status = + only(scene_objects(iterative_hard_call_scene; scale=:Leaf)).status + @test iterative_hard_call_status.temperature_seen == 32.0 + @test iterative_hard_call_status.called_temperature == 32.0 + @test iterative_hard_call_backend.values[:cell_a] == 33.0 + @test iterative_hard_call_backend.writes == [ + ( + application=:meteo_source, + process=:scene_object_meteo_call_source, + cell=:cell_a, + variable=:T, + value=33.0, + time=1, + ), + ] + accepted_call_samples = scene_outputs(iterative_hard_call_sim)[ + (:meteo_source, ObjectId(:leaf_1), :temperature_seen) + ] + @test length(accepted_call_samples) == 1 + @test only(accepted_call_samples) == (1.0, 32.0) + hard_call_order_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0, called_signal=0.0, observed_signal=0.0)); @@ -1293,9 +2762,274 @@ end if row.application_id == :scene_temporal_sum && row.input == :signal_sum ) @test temporal_binding.carrier_hint == :temporal_stream - run!(temporal_input_scene; steps=3) + temporal_input_simulation = run!(temporal_input_scene; steps=3) + @test temporal_input_simulation isa SceneSimulation + @test temporal_input_simulation.scene === temporal_input_scene + @test temporal_input_simulation.compiled isa CompiledScene @test only(scene_objects(temporal_input_scene; scale=:Leaf)).status.signal == 3.0 @test only(scene_objects(temporal_input_scene; scale=:Scene)).status.temporal_total == 5.0 + temporal_output_rows = collect_outputs(temporal_input_simulation; sink=nothing) + @test length(temporal_output_rows) == 5 + @test size(collect_outputs(temporal_input_simulation), 1) == 5 + @test count(row -> row.object_id == :leaf_1 && row.variable == :signal, temporal_output_rows) == 3 + @test count(row -> row.object_id == :scene && row.variable == :temporal_total, temporal_output_rows) == 2 + @test collect_outputs(temporal_input_simulation, :leaf_1, :signal; sink=nothing)[end].value == 3.0 + temporal_output_summary = explain_outputs(temporal_input_simulation) + @test only(row for row in temporal_output_summary if row.object_id == :leaf_1 && row.variable == :signal).nsamples == 3 + @test only(row for row in temporal_output_summary if row.object_id == :scene && row.variable == :temporal_total).application_id == :scene_temporal_sum + + tracked_output_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + status=Status(signal=0.0, observed_signal=0.0), + ); + applications=( + ModelSpec(SceneObjectSignalSourceModel(); name=:hourly_signal) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(1)), + ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_observer) |> + AppliesTo(One(scale=:Leaf)), + ), + environment=(duration=Hour(1),), + ) + tracked_output_request = OutputRequest( + :Leaf, + :signal; + name=:signal_two_hour, + process=:scene_object_signal_source, + policy=Integrate(), + clock=Hour(2), + ) + tracked_output_simulation = run!( + tracked_output_scene; + steps=3, + tracked_outputs=tracked_output_request, + ) + tracked_output_rows = collect_outputs( + tracked_output_simulation, + :signal_two_hour; + sink=nothing, + ) + @test getproperty.(tracked_output_rows, :value) == [1.0, 5.0] + @test getproperty.(tracked_output_rows, :time) == [1.0, 3.0] + @test all(row -> row.object_id == :leaf_1, tracked_output_rows) + @test all(row -> row.application_id == :hourly_signal, tracked_output_rows) + @test Set(keys(scene_outputs(tracked_output_simulation))) == Set([ + (:hourly_signal, ObjectId(:leaf_1), :signal), + ]) + @test explain_output_retention(tracked_output_simulation) == [ + ( + application_id=:hourly_signal, + variable=:signal, + reasons=(:output_request,), + retention_steps=nothing, + current_target_count=1, + ), + ] + @test collect_outputs(tracked_output_simulation; sink=nothing)[:signal_two_hour] == + tracked_output_rows + tracked_output_frames = collect_outputs(tracked_output_simulation) + @test sort(collect(keys(tracked_output_frames))) == [:signal_two_hour] + @test tracked_output_frames[:signal_two_hour][!, :value] == [1.0, 5.0] + @test tracked_output_frames[:signal_two_hour][!, :application_id] == + [:hourly_signal, :hourly_signal] + @test_throws "No scene output request named `missing_request`" collect_outputs( + tracked_output_simulation, + :missing_request; + sink=nothing, + ) + + auto_tracked_output_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0)); + applications=( + ModelSpec(SceneObjectSignalSourceModel(); name=:hourly_signal) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(1)), + ), + environment=(duration=Hour(1),), + ) + auto_tracked_output_simulation = run!( + auto_tracked_output_scene; + steps=3, + tracked_outputs=OutputRequest(:Leaf, :signal; name=:signal_auto), + ) + auto_tracked_rows = collect_outputs( + auto_tracked_output_simulation, + :signal_auto; + sink=nothing, + ) + @test getproperty.(auto_tracked_rows, :value) == [1.0, 2.0, 3.0] + @test all(row -> row.application_id == :hourly_signal, auto_tracked_rows) + + empty_retention_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + status=Status(signal=0.0, observed_signal=0.0), + ); + applications=( + ModelSpec(SceneObjectSignalSourceModel(); name=:signal_source) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_observer) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + empty_retention_simulation = run!( + empty_retention_scene; + tracked_outputs=OutputRequest[], + ) + @test isempty(scene_outputs(empty_retention_simulation)) + @test isempty(explain_output_retention(empty_retention_simulation)) + @test only(scene_objects(empty_retention_scene; scale=:Leaf)).status.observed_signal == + 1.0 + + selective_temporal_scene = Scene( + Object( + :scene; + scale=:Scene, + kind=:scene, + status=Status(signal_sum=0.0, temporal_total=0.0), + ), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + status=Status(signal=0.0), + ); + applications=( + ModelSpec(SceneObjectSignalSourceModel(); name=:hourly_signal) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(1)), + ModelSpec(SceneObjectTemporalSumModel(); name=:scene_temporal_sum) |> + AppliesTo(One(scale=:Scene)) |> + Inputs( + :signal_sum => One( + scale=:Leaf, + var=:signal, + policy=Integrate(), + window=Hour(2), + ), + ) |> + TimeStep(Hour(2)), + ), + environment=(duration=Hour(1),), + ) + selective_temporal_request = OutputRequest( + :Scene, + :temporal_total; + name=:temporal_total_two_hour, + process=:scene_object_temporal_sum, + policy=HoldLast(), + clock=Hour(2), + ) + selective_temporal_simulation = run!( + selective_temporal_scene; + steps=3, + tracked_outputs=selective_temporal_request, + ) + @test Set(keys(scene_outputs(selective_temporal_simulation))) == Set([ + (:hourly_signal, ObjectId(:leaf_1), :signal), + (:scene_temporal_sum, ObjectId(:scene), :temporal_total), + ]) + selective_retention_rows = explain_output_retention( + selective_temporal_simulation, + ) + @test only( + row for row in selective_retention_rows + if row.application_id == :hourly_signal + ).reasons == (:temporal_dependency,) + @test only( + row for row in selective_retention_rows + if row.application_id == :hourly_signal + ).retention_steps == 2.0 + @test only( + row for row in selective_retention_rows + if row.application_id == :scene_temporal_sum + ).reasons == (:output_request,) + @test isnothing( + only( + row for row in selective_retention_rows + if row.application_id == :scene_temporal_sum + ).retention_steps, + ) + @test getproperty.( + collect_outputs( + selective_temporal_simulation, + :temporal_total_two_hour; + sink=nothing, + ), + :value, + ) == [1.0, 5.0] + + bounded_temporal_scene = Scene( + Object( + :scene; + scale=:Scene, + kind=:scene, + status=Status(signal_sum=0.0, temporal_total=0.0), + ), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + status=Status(signal=0.0), + ); + applications=( + ModelSpec(SceneObjectSignalSourceModel(); name=:hourly_signal) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(1)), + ModelSpec(SceneObjectTemporalSumModel(); name=:scene_temporal_sum) |> + AppliesTo(One(scale=:Scene)) |> + Inputs( + :signal_sum => One( + scale=:Leaf, + var=:signal, + policy=Integrate(), + window=Hour(2), + ), + ) |> + TimeStep(Hour(2)), + ), + environment=(duration=Hour(1),), + ) + bounded_temporal_request = OutputRequest( + :Scene, + :temporal_total; + name=:bounded_temporal_total, + application=:scene_temporal_sum, + policy=HoldLast(), + clock=Hour(2), + ) + bounded_temporal_simulation = run!( + bounded_temporal_scene; + steps=19, + tracked_outputs=bounded_temporal_request, + ) + bounded_source_samples = scene_outputs(bounded_temporal_simulation)[ + (:hourly_signal, ObjectId(:leaf_1), :signal) + ] + @test length(bounded_source_samples) == 2 + @test getindex.(bounded_source_samples, 1) == [18.0, 19.0] + @test getindex.(bounded_source_samples, 2) == [18.0, 19.0] + @test only(scene_objects(bounded_temporal_scene; scale=:Scene)).status.temporal_total == + 37.0 + @test length( + collect_outputs( + bounded_temporal_simulation, + :bounded_temporal_total; + sink=nothing, + ), + ) == 10 temporal_holdlast_scene = Scene( Object(:scene; scale=:Scene, kind=:scene, status=Status(signal_sum=0.0, temporal_total=0.0)), @@ -1311,8 +3045,342 @@ end ), environment=(duration=Hour(1),), ) - run!(temporal_holdlast_scene; steps=3) - @test only(scene_objects(temporal_holdlast_scene; scale=:Scene)).status.temporal_total == 3.0 + temporal_holdlast_simulation = run!( + temporal_holdlast_scene; + steps=9, + tracked_outputs=OutputRequest( + :Scene, + :temporal_total; + name=:holdlast_total, + application=:scene_temporal_latest, + ), + ) + @test only(scene_objects(temporal_holdlast_scene; scale=:Scene)).status.temporal_total == 9.0 + @test length( + scene_outputs(temporal_holdlast_simulation)[ + (:hourly_signal, ObjectId(:leaf_1), :signal) + ], + ) == 1 + + trait_policy_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene, status=Status(signal_sum=0.0, temporal_total=0.0)), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0)); + applications=( + ModelSpec(SceneObjectTraitPolicySignalModel(); name=:trait_policy_signal) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(1)), + ModelSpec(SceneObjectTemporalSumModel(); name=:trait_policy_consumer) |> + AppliesTo(One(scale=:Scene)) |> + Inputs(:signal_sum => One(scale=:Leaf, var=:signal)) |> + TimeStep(Hour(2)), + ), + environment=(duration=Hour(1),), + ) + trait_policy_binding = only( + row for row in explain_bindings(refresh_bindings!(trait_policy_scene)) + if row.application_id == :trait_policy_consumer && row.input == :signal_sum + ) + @test trait_policy_binding.policy isa Aggregate + @test trait_policy_binding.carrier_hint == :temporal_stream + trait_policy_simulation = run!(trait_policy_scene; steps=3) + @test only(scene_objects(trait_policy_scene; scale=:Scene)).status.temporal_total == 2.5 + @test getproperty.( + collect_outputs( + trait_policy_simulation, + :scene, + :temporal_total; + sink=nothing, + ), + :value, + ) == [1.0, 2.5] + + explicit_policy_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene, status=Status(signal_sum=0.0, temporal_total=0.0)), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0)); + applications=( + ModelSpec(SceneObjectTraitPolicySignalModel(); name=:trait_policy_signal) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(1)), + ModelSpec(SceneObjectTemporalSumModel(); name=:explicit_policy_consumer) |> + AppliesTo(One(scale=:Scene)) |> + Inputs(:signal_sum => One(scale=:Leaf, var=:signal, policy=Integrate())) |> + TimeStep(Hour(2)), + ), + environment=(duration=Hour(1),), + ) + explicit_policy_binding = only( + row for row in explain_bindings(refresh_bindings!(explicit_policy_scene)) + if row.application_id == :explicit_policy_consumer && row.input == :signal_sum + ) + @test explicit_policy_binding.policy isa Integrate + run!(explicit_policy_scene; steps=3) + @test only(scene_objects(explicit_policy_scene; scale=:Scene)).status.temporal_total == 5.0 + + generic_integrate_scene = Scene( + Object( + :scene; + scale=:Scene, + kind=:scene, + status=Status(signal_sum=big"0.0", temporal_total=big"0.0"), + ), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + status=Status(signal=big"0.0"), + ); + applications=( + ModelSpec(SceneObjectTimeSignalModel(big"0.0"); name=:big_signal) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(1)), + ModelSpec(SceneObjectTemporalSumModel(); name=:big_integral) |> + AppliesTo(One(scale=:Scene)) |> + Inputs( + :signal_sum => One( + scale=:Leaf, + process=:scene_object_signal_source, + var=:signal, + policy=Integrate(), + window=Hour(2), + ), + ) |> + TimeStep(Hour(2)), + ), + environment=(duration=Hour(1),), + ) + generic_integrate_simulation = run!(generic_integrate_scene; steps=3) + generic_integrate_values = getproperty.( + collect_outputs( + generic_integrate_simulation, + :scene, + :temporal_total; + sink=nothing, + ), + :value, + ) + @test generic_integrate_values == BigFloat[1, 5] + @test all(value -> value isa BigFloat, generic_integrate_values) + + interpolation_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + status=Status(signal=big"0.0", observed_signal=big"0.0"), + ); + applications=( + ModelSpec(SceneObjectTimeSignalModel(big"0.0"); name=:slow_signal) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(2)), + ModelSpec(SceneObjectSignalConsumerModel(); name=:fast_consumer) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs( + :signal => One( + scale=:Leaf, + process=:scene_object_signal_source, + var=:signal, + policy=Interpolate(), + ), + ) |> + TimeStep(Hour(1)), + ), + environment=(duration=Hour(1),), + ) + interpolation_simulation = run!( + interpolation_scene; + steps=5, + tracked_outputs=OutputRequest( + :Leaf, + :observed_signal; + name=:interpolated_signal, + application=:fast_consumer, + ), + ) + interpolation_stream = scene_outputs(interpolation_simulation)[ + (:slow_signal, ObjectId(:leaf_1), :signal) + ] + @test eltype(interpolation_stream) == Tuple{Float64,BigFloat} + @test getindex.(interpolation_stream, 1) == [3.0, 5.0] + interpolated_values = getproperty.( + collect_outputs( + interpolation_simulation, + :leaf_1, + :observed_signal; + sink=nothing, + ), + :value, + ) + @test interpolated_values == BigFloat[1, 1, 3, 4, 5] + @test all(value -> value isa BigFloat, interpolated_values) + + interpolation_hold_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + status=Status(signal=0.0, observed_signal=0.0), + ); + applications=( + ModelSpec(SceneObjectTimeSignalModel(0.0); name=:slow_signal) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(2)), + ModelSpec(SceneObjectSignalConsumerModel(); name=:fast_consumer) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs( + :signal => One( + scale=:Leaf, + process=:scene_object_signal_source, + var=:signal, + policy=Interpolate(; mode=:hold, extrapolation=:hold), + ), + ) |> + TimeStep(Hour(1)), + ), + environment=(duration=Hour(1),), + ) + interpolation_hold_simulation = run!(interpolation_hold_scene; steps=6) + @test getproperty.( + collect_outputs( + interpolation_hold_simulation, + :leaf_1, + :observed_signal; + sink=nothing, + ), + :value, + ) == [1.0, 1.0, 3.0, 3.0, 5.0, 5.0] + + invalid_interpolation_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + status=Status(signal=0.0, observed_signal=0.0), + ); + applications=( + ModelSpec(SceneObjectTimeSignalModel(0.0); name=:signal_source) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_consumer) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs( + :signal => One( + scale=:Leaf, + process=:scene_object_signal_source, + var=:signal, + policy=Interpolate(:spline), + ), + ), + ), + ) + @test_throws "Invalid interpolation mode `spline`" refresh_bindings!(invalid_interpolation_scene) + + stream_only_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0, observed_signal=0.0)); + applications=( + ModelSpec(SceneObjectSignalSetModel(10.0); name=:stream_signal) |> + AppliesTo(One(scale=:Leaf)) |> + OutputRouting(; signal=:stream_only), + ModelSpec(SceneObjectSignalSetModel(1.0); name=:canonical_signal) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_consumer) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + stream_only_compiled = refresh_bindings!(stream_only_scene) + stream_only_writer = only(row for row in explain_writers(stream_only_compiled) if row.variable == :signal) + @test stream_only_writer.application_ids == [:canonical_signal] + stream_only_binding = only(row for row in explain_bindings(stream_only_compiled) if row.application_id == :signal_consumer) + @test stream_only_binding.source_application_ids == [:canonical_signal] + stream_only_simulation = run!(stream_only_scene) + stream_only_status = only(scene_objects(stream_only_scene; scale=:Leaf)).status + @test stream_only_status.observed_signal == 1.0 + signal_rows = collect_outputs(stream_only_simulation, :leaf_1, :signal; sink=nothing) + @test Dict(row.application_id => row.value for row in signal_rows) == + Dict(:stream_signal => 10.0, :canonical_signal => 1.0) + @test Set(row.application_id for row in explain_outputs(stream_only_simulation) if row.variable == :signal) == + Set([:stream_signal, :canonical_signal]) + stream_only_only_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0)); + applications=( + ModelSpec(SceneObjectSignalSetModel(10.0); name=:stream_signal) |> + AppliesTo(One(scale=:Leaf)) |> + OutputRouting(; signal=:stream_only), + ), + ) + @test_throws "No scene output publisher found" run!( + stream_only_only_scene; + tracked_outputs=OutputRequest(:Leaf, :signal; name=:stream_signal_auto_fail), + ) + stream_only_requested = run!( + stream_only_scene; + tracked_outputs=OutputRequest(:Leaf, :signal; name=:canonical_signal_request), + ) + stream_only_requested_rows = collect_outputs( + stream_only_requested, + :canonical_signal_request; + sink=nothing, + ) + @test getproperty.(stream_only_requested_rows, :application_id) == [:canonical_signal] + @test getproperty.(stream_only_requested_rows, :value) == [1.0] + explicit_stream_application = run!( + stream_only_scene; + tracked_outputs=OutputRequest( + :Leaf, + :signal; + name=:stream_signal_by_application, + application=:stream_signal, + ), + ) + explicit_stream_rows = collect_outputs( + explicit_stream_application, + :stream_signal_by_application; + sink=nothing, + ) + @test getproperty.(explicit_stream_rows, :application_id) == [:stream_signal] + @test getproperty.(explicit_stream_rows, :value) == [10.0] + explicit_canonical_application = run!( + stream_only_scene; + tracked_outputs=OutputRequest( + :Leaf, + :signal; + name=:canonical_signal_by_application, + application=:canonical_signal, + ), + ) + explicit_canonical_rows = collect_outputs( + explicit_canonical_application, + :canonical_signal_by_application; + sink=nothing, + ) + @test getproperty.(explicit_canonical_rows, :application_id) == + [:canonical_signal] + @test getproperty.(explicit_canonical_rows, :value) == [1.0] + @test_throws "application `missing_signal`" run!( + stream_only_scene; + tracked_outputs=OutputRequest( + :Leaf, + :signal; + name=:missing_signal_application, + application=:missing_signal, + ), + ) + @test_throws "Ambiguous scene output publishers" run!( + stream_only_scene; + tracked_outputs=OutputRequest( + :Leaf, + :signal; + name=:ambiguous_signal_request, + process=:scene_object_signal_source, + ), + ) writer_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), @@ -1352,6 +3420,147 @@ end run!(writer_runtime_scene) @test only(scene_objects(writer_runtime_scene; scale=:Leaf)).status.biomass == 0.0 + lifecycle_scene = Scene( + Object( + :scene; + scale=:Scene, + kind=:scene, + status=Status(created_count=0, removed_count=0), + ), + Object( + :plant_1; + scale=:Plant, + kind=:plant, + parent=:scene, + status=Status(signals=[0.0], signal_total=0.0), + ), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:plant_1, status=Status(signal=0.0)), + Object(:leaf_2; scale=:Leaf, kind=:plant, parent=:plant_1, status=Status(signal=0.0)); + applications=( + ModelSpec(SceneObjectGrowthModel(); name=:growth) |> + AppliesTo(One(scale=:Scene)), + ModelSpec(SceneObjectSignalSourceModel(); name=:leaf_signal) |> + AppliesTo(Many(scale=:Leaf)), + ModelSpec(SceneObjectPlantSignalSumModel(); name=:plant_signal_total) |> + AppliesTo(One(scale=:Plant)) |> + Inputs(:signals => Many(scale=:Leaf, within=Self(), var=:signal)), + ModelSpec(SceneObjectPruningModel(); name=:pruning) |> + AppliesTo(One(scale=:Scene)), + ), + environment=(duration=Hour(1),), + ) + lifecycle_output_request = OutputRequest( + :Leaf, + :signal; + name=:leaf_signal_hourly, + process=:scene_object_signal_source, + policy=HoldLast(), + clock=Hour(1), + ) + lifecycle_simulation = run!( + lifecycle_scene; + steps=3, + tracked_outputs=lifecycle_output_request, + ) + @test !bindings_dirty(lifecycle_scene) + @test !environment_bindings_dirty(lifecycle_scene) + @test lifecycle_simulation.compiled.revision == scene_revision(lifecycle_scene) + @test Set(object_ids(lifecycle_scene; scale=:Leaf)) == + Set([ObjectId(:leaf_1), ObjectId(:grown_leaf)]) + lifecycle_status = only(scene_objects(lifecycle_scene; scale=:Scene)).status + @test lifecycle_status.created_count == 1 + @test lifecycle_status.removed_count == 1 + lifecycle_leaf_statuses = Dict(object.id.value => object.status for object in scene_objects(lifecycle_scene; scale=:Leaf)) + @test lifecycle_leaf_statuses[:leaf_1].signal == 3.0 + @test lifecycle_leaf_statuses[:grown_leaf].signal == 2.0 + @test only(scene_objects(lifecycle_scene; scale=:Plant)).status.signal_total == 5.0 + lifecycle_application = lifecycle_simulation.compiled.applications_by_id[:leaf_signal] + @test lifecycle_application.target_ids == [ObjectId(:grown_leaf), ObjectId(:leaf_1)] + lifecycle_execution_row = only( + row for row in explain_execution_plan(lifecycle_simulation) + if row.application_id == :leaf_signal + ) + @test lifecycle_execution_row.object_ids == [:grown_leaf, :leaf_1] + @test lifecycle_simulation.execution_plan.scene_revision == + scene_revision(lifecycle_scene) + @test haskey( + lifecycle_simulation.compiled.model_bundles_by_target, + (:leaf_signal, ObjectId(:grown_leaf)), + ) + lifecycle_binding = only( + row for row in explain_bindings(lifecycle_simulation.compiled) + if row.application_id == :plant_signal_total + ) + @test lifecycle_binding.source_ids == [:grown_leaf, :leaf_1] + @test all( + first(key) == :leaf_signal && last(key) == :signal + for key in keys(scene_outputs(lifecycle_simulation)) + ) + @test only(explain_output_retention(lifecycle_simulation)) == ( + application_id=:leaf_signal, + variable=:signal, + reasons=(:output_request,), + retention_steps=nothing, + current_target_count=2, + ) + @test length(collect_outputs(lifecycle_simulation, :leaf_1, :signal; sink=nothing)) == 3 + @test length(collect_outputs(lifecycle_simulation, :grown_leaf, :signal; sink=nothing)) == 2 + @test length(collect_outputs(lifecycle_simulation, :leaf_2, :signal; sink=nothing)) == 2 + lifecycle_requested_rows = collect_outputs( + lifecycle_simulation, + :leaf_signal_hourly; + sink=nothing, + ) + @test count(row -> row.object_id == :leaf_1, lifecycle_requested_rows) == 3 + @test count(row -> row.object_id == :grown_leaf, lifecycle_requested_rows) == 2 + @test count(row -> row.object_id == :leaf_2, lifecycle_requested_rows) == 2 + + moving_environment_backend = SceneObjectMutableEnvironmentBackend( + :cell_a => 20.0, + :cell_b => 30.0, + ) + moving_environment_scene = Scene( + Object( + :scene; + scale=:Scene, + kind=:scene, + geometry=(cell=:cell_a,), + status=Status(move_count=0), + ), + Object( + :leaf_1; + scale=:Leaf, + kind=:plant, + parent=:scene, + geometry=(cell=:cell_a,), + status=Status(temperature_seen=0.0), + ); + applications=( + ModelSpec(SceneObjectGeometryMoverModel(); name=:geometry_mover) |> + AppliesTo(One(scale=:Scene)), + ModelSpec(SceneObjectEnvironmentProbeModel(); name=:moving_probe) |> + AppliesTo(One(scale=:Leaf)) |> + Environment(provider=:grid), + ), + environment=moving_environment_backend, + ) + moving_environment_simulation = run!(moving_environment_scene; steps=2) + @test !bindings_dirty(moving_environment_scene) + @test !environment_bindings_dirty(moving_environment_scene) + @test only(scene_objects(moving_environment_scene; scale=:Scene)).status.move_count == 1 + @test only(scene_objects(moving_environment_scene; scale=:Leaf)).status.temperature_seen == 30.0 + moving_probe_rows = collect_outputs( + moving_environment_simulation, + :leaf_1, + :temperature_seen; + sink=nothing, + ) + @test getproperty.(moving_probe_rows, :value) == [20.0, 30.0] + @test only( + row for row in explain_environment_bindings(moving_environment_simulation.environment_bindings) + if row.application_id == :moving_probe + ).cell == :cell_b + multirate_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0)); @@ -1369,4 +3578,61 @@ end @test only(schedule_rows).dt_seconds == 7200.0 run!(multirate_scene; steps=5) @test only(scene_objects(multirate_scene; scale=:Leaf)).status.signal == 3.0 + + trait_clock_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0)); + applications=( + ModelSpec(SceneObjectTraitClockSourceModel(); name=:trait_clock_signal) |> + AppliesTo(One(scale=:Leaf)), + ), + environment=(duration=Hour(1),), + ) + trait_clock_compiled = refresh_bindings!(trait_clock_scene) + trait_clock_schedule = only(explain_schedule(trait_clock_compiled)) + @test trait_clock_schedule.application_id == :trait_clock_signal + @test trait_clock_schedule.dt_steps == 2.0 + @test trait_clock_schedule.phase == 1.0 + run!(trait_clock_scene; steps=5) + @test only(scene_objects(trait_clock_scene; scale=:Leaf)).status.signal == 3.0 + + trait_clock_override_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0)); + applications=( + ModelSpec(SceneObjectTraitClockSourceModel(); name=:override_signal) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(1)), + ), + environment=(duration=Hour(1),), + ) + override_schedule = only(explain_schedule(refresh_bindings!(trait_clock_override_scene))) + @test override_schedule.dt_steps == 1.0 + run!(trait_clock_override_scene; steps=5) + @test only(scene_objects(trait_clock_override_scene; scale=:Leaf)).status.signal == 5.0 + + strict_hint_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0)); + applications=( + ModelSpec(SceneObjectStrictHintSourceModel(); name=:strict_hint_signal) |> + AppliesTo(One(scale=:Leaf)), + ), + environment=(duration=Hour(1),), + ) + @test_throws "outside `timestep_hint.required=1 day`" refresh_bindings!(strict_hint_scene) + + strict_hint_override_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0)); + applications=( + ModelSpec(SceneObjectStrictHintSourceModel(); name=:strict_hint_override) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(1)), + ), + environment=(duration=Hour(1),), + ) + @test only(explain_schedule(refresh_bindings!(strict_hint_override_scene))).dt_steps == 1.0 + run!(strict_hint_override_scene; steps=2) + @test only(scene_objects(strict_hint_override_scene; scale=:Leaf)).status.signal == 2.0 end diff --git a/test/test-updates.jl b/test/test-updates.jl index 082864926..740c285ea 100644 --- a/test/test-updates.jl +++ b/test/test-updates.jl @@ -40,13 +40,13 @@ end @testset "ModelSpec Updates" begin meteo = Atmosphere(T=20.0, Rh=0.65, Wind=1.0, duration=Dates.Hour(1)) - @test_throws "Ambiguous canonical writers" ModelMapping( + @test_throws "Ambiguous canonical writers" PlantSimEngine.ModelMapping( UpdateCarbonAllocationModel(), UpdateLeafPruningModel(), status=(leaf_biomass=1.0,), ) - mapping = ModelMapping( + mapping = PlantSimEngine.ModelMapping( UpdateCarbonAllocationModel(), ModelSpec(UpdateLeafPruningModel()) |> Updates(:leaf_biomass; after=:update_carbon_allocation), @@ -62,7 +62,7 @@ end pruning_node = only(filter(node -> node.process == :update_leaf_pruning, graph_nodes)) @test any(parent -> parent.process == :update_carbon_allocation, pruning_node.parent) - @test_throws "without an ordering relation" ModelMapping( + @test_throws "without an ordering relation" PlantSimEngine.ModelMapping( UpdateCarbonAllocationModel(), ModelSpec(UpdateLeafPruningModel()) |> Updates(:leaf_biomass; after=:update_carbon_allocation), @@ -71,7 +71,7 @@ end status=(leaf_biomass=1.0,), ) - ordered_updates = ModelMapping( + ordered_updates = PlantSimEngine.ModelMapping( UpdateCarbonAllocationModel(), ModelSpec(UpdateLeafSenescenceModel()) |> Updates(:leaf_biomass; after=:update_carbon_allocation), From 6f52c7ea67f2cdddf4a0d43b8d77cb04507b6f88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Sat, 13 Jun 2026 17:12:31 +0200 Subject: [PATCH 20/33] Remove Domain, SimulationMapping, Route, AllDomains, HardDomains, and their helpers, runtime, tests, and documentation. This API was a failed tentative --- docs/make.jl | 7 +- docs/src/agent_skill.md | 2 +- docs/src/dev/code_cleanup_audit.md | 12 +- docs/src/dev/maespa_domain_handoff.md | 217 --- docs/src/dev/maespa_scene_handoff.md | 94 + .../src/dev/multi_domain_simulation_design.md | 506 ------ docs/src/dev/multi_domain_simulation_plan.md | 270 --- docs/src/dev/release_notes_handoff.md | 94 +- .../unified_scene_object_completion_audit.md | 22 +- docs/src/dev/unified_scene_object_design.md | 6 + ...nified_scene_object_implementation_plan.md | 77 +- docs/src/domain_simulation.md | 373 ---- docs/src/index.md | 4 +- docs/src/migration_scene_object.md | 46 +- docs/src/model_execution.md | 2 - docs/src/scene_object/quickstart.md | 2 +- .../step_by_step/quick_and_dirty_examples.md | 2 +- examples/dummy.jl | 4 +- ...ain_example.jl => maespa_scene_example.jl} | 128 +- skills/plantsimengine/SKILL.md | 10 +- src/PlantSimEngine.jl | 3 - src/dependencies/hard_dependencies.jl | 5 - src/domains/domain_run_loops.jl | 89 - src/domains/domain_scheduler.jl | 167 -- src/domains/domain_simulation.jl | 1302 -------------- src/domains/environment_bridge.jl | 171 -- src/domains/graph_domain_runner.jl | 135 -- src/domains/output_publisher.jl | 104 -- src/domains/route_runtime.jl | 386 ----- src/domains/routes.jl | 89 - src/mtg/ModelSpec.jl | 26 +- src/scene_object_api.jl | 13 +- src/time/runtime/environment_backends.jl | 14 +- test/runtests.jl | 8 +- test/test-domain-simulation.jl | 1521 ----------------- test/test-environment-backends.jl | 388 +---- ...xample.jl => test-maespa-scene-example.jl} | 98 +- test/test-multirate-runtime.jl | 2 +- test/test-unified-scene-object-api.jl | 27 +- 39 files changed, 273 insertions(+), 6153 deletions(-) delete mode 100644 docs/src/dev/maespa_domain_handoff.md create mode 100644 docs/src/dev/maespa_scene_handoff.md delete mode 100644 docs/src/dev/multi_domain_simulation_design.md delete mode 100644 docs/src/dev/multi_domain_simulation_plan.md delete mode 100644 docs/src/domain_simulation.md rename examples/{maespa_domain_example.jl => maespa_scene_example.jl} (77%) delete mode 100644 src/domains/domain_run_loops.jl delete mode 100644 src/domains/domain_scheduler.jl delete mode 100644 src/domains/domain_simulation.jl delete mode 100644 src/domains/environment_bridge.jl delete mode 100644 src/domains/graph_domain_runner.jl delete mode 100644 src/domains/output_publisher.jl delete mode 100644 src/domains/route_runtime.jl delete mode 100644 src/domains/routes.jl delete mode 100644 test/test-domain-simulation.jl rename test/{test-maespa-domain-example.jl => test-maespa-scene-example.jl} (64%) diff --git a/docs/make.jl b/docs/make.jl index a33a3acd9..d065ca459 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -32,7 +32,7 @@ makedocs(; ], "Scene/Object simulations" => [ "Quickstart" => "./scene_object/quickstart.md", - "Migrating from mappings and domains" => "migration_scene_object.md", + "Migrating from mappings" => "migration_scene_object.md", ], "Step by step - Single-scale simulations" => [ "Detailed first simulation" => "./step_by_step/detailed_first_example.md", @@ -83,13 +83,10 @@ makedocs(; "Example models" => "./API/API_examples.md", "Internal API" => "./API/API_private.md",], "Development designs" => [ - "Multi-domain simulation design" => "./dev/multi_domain_simulation_design.md", - "Multi-domain simulation plan" => "./dev/multi_domain_simulation_plan.md", "Unified scene/object design" => "./dev/unified_scene_object_design.md", "Unified scene/object implementation plan" => "./dev/unified_scene_object_implementation_plan.md", "Unified scene/object completion audit" => "./dev/unified_scene_object_completion_audit.md", - "MAESPA-style domain example handoff" => "./dev/maespa_domain_handoff.md", - "Legacy domain regression API" => "domain_simulation.md", + "MAESPA-style scene example handoff" => "./dev/maespa_scene_handoff.md", "Code cleanup audit" => "./dev/code_cleanup_audit.md", "Release notes handoff" => "./dev/release_notes_handoff.md", ], diff --git a/docs/src/agent_skill.md b/docs/src/agent_skill.md index 156299651..ab4547829 100644 --- a/docs/src/agent_skill.md +++ b/docs/src/agent_skill.md @@ -28,7 +28,7 @@ gives agents the package-specific conventions they need for: - implementing or wrapping models with `@process`, `inputs_`, `outputs_`, `run!`, hard dependencies, and model traits. -The legacy `ModelMapping`, `MultiScaleModel`, domain, and route APIs remain +The legacy `ModelMapping` and `MultiScaleModel` APIs remain useful when maintaining existing simulations, but agents should not select them for new scene/object scenarios. See [Migrating To The Scene/Object API](migration_scene_object.md). diff --git a/docs/src/dev/code_cleanup_audit.md b/docs/src/dev/code_cleanup_audit.md index 68fcce4ce..fa310c4bc 100644 --- a/docs/src/dev/code_cleanup_audit.md +++ b/docs/src/dev/code_cleanup_audit.md @@ -1,14 +1,13 @@ # Code Cleanup Audit -This page records cleanup candidates found during the multi-domain experimental +This page records cleanup candidates found during the scene/object experimental branch audit. It is intentionally biased toward code health and release-note planning rather than immediate implementation. See also: - `release_notes_handoff.md` for the consolidated release-note source. -- `unified_scene_object_design.md` for the planned breaking replacement of the - current domain/route and multiscale-mapping split. +- `unified_scene_object_design.md` for the scene/object redesign. - `unified_scene_object_implementation_plan.md` for the implementation handoff of that future design. @@ -31,8 +30,6 @@ target is duplicated control flow, not legitimate multiple dispatch. | Done | `validate_meteo_inputs(model_specs, meteo)` and `validate_meteo_inputs(model_specs, backend)` | `src/time/runtime/meteo_sampling.jl`; `src/time/runtime/environment_backends.jl` | Shared missing-row collection and diagnostic formatting through `_collect_missing_meteo_rows` and `_error_missing_meteo_inputs`. | | Done | `_required_horizon_for_export_policy` and `_required_horizon_for_policy` | `src/time/runtime/output_export.jl`; `src/time/runtime/publishers.jl` | Export code now delegates to `_required_horizon_for_policy(policy, clock.dt, source_dt)`. | | Done | `_normalize_meteo_window` and `_runtime_meteo_window` | `src/mtg/ModelSpec.jl`; `src/time/runtime/meteo_sampling.jl` | Runtime meteo-window normalization now calls `_normalize_meteo_window`. | -| Done | Domain environment helpers for single-status and graph domains | `src/domains/domain_simulation.jl` | Shared `EnvironmentSupport` construction, environment sampling, and scatter flow while keeping thin single-status and graph-domain entry points. | -| Done | `_should_visit_domain_node` and `_should_publish_domain_key` | `src/domains/domain_simulation.jl` | Extracted `_phase_allows_hard_parent` for the shared hard-domain phase rule. | | Done | `Status` and `StatusView` Base interface methods | `src/component_models/Status.jl`; `src/component_models/StatusView.jl` | Shared small private helpers for values, tuple conversion, iteration, and indexed iteration while keeping storage-specific access and mutation methods separate. | | Done | Tutorial helper functions repeated in toy multiscale examples | `examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl`; `examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl` | Extracted shared MTG navigation helpers to `examples/ToyMultiScalePlantTutorial/ToyPlantHelpers.jl` and included it with `@__DIR__` so tutorial scripts remain standalone. | | Done | Test helper flows that run a graph simulation and compare outputs | `test/helper-functions.jl` | Shared `run_graphsim_for_comparison` for generated multiscale comparison and filtered-output comparison paths. | @@ -62,7 +59,6 @@ instead of the removed public `ModelList` API. | Done | Source-side `@assert` used for user/data validation | Formerly in MTG initialization, mapping, output conversion, and save-result helpers | Converted to explicit `if` checks and `error` messages in this cleanup pass. Remaining `@assert` uses are limited to tests and documentation examples. | | Done | `ModelSpec(model::AbstractModel)` checks `model isa MultiScaleModel`, which is effectively dead | `src/mtg/ModelSpec.jl` | Added an explicit `ModelSpec(::MultiScaleModel)` method and removed the dead branch from the `AbstractModel` constructor. | | Done | `Symbol("")` sentinel for same-scale/no-op mappings | `src/mtg/MultiScaleModel.jl`; `src/mtg/mapping/mapping.jl`; `src/dependencies/dependency_graph.jl` | Replaced the magic sentinel with the typed `SameScale()` marker and reject `Symbol("")` in new mappings. | -| Done | Domain selector detection by type name | `src/dependencies/hard_dependencies.jl`; `src/domains/domain_simulation.jl` | Added `AbstractDomainDependencySelector`; `AllDomains` and `HardDomains` subtype it, and detection now uses the marker type instead of type-name reflection. | | Done | Policy handling by large `isa` branch chains | `src/time/runtime/input_resolution.jl` | Input resolution now dispatches on policy type through `_resolved_policy_value_for_source` and `_resolve_input_for_policy!`, with shared source-resolution helpers. | | Done | Scope selectors accept strings and hard-code built-in scale names | `src/time/runtime/scopes.jl`; `src/mtg/ModelSpec.jl` | Scope selectors now reject strings at construction and runtime callable results must return `ScopeId` or `Symbol`. Built-in selector symbols remain explicit and validated. | | Done | Normalizer fallbacks return unknown values unchanged | `src/mtg/ModelSpec.jl` | Fallbacks for input bindings, meteo bindings, and output routing now fail at construction with explicit errors. String scope selectors now error instead of being converted. | @@ -74,13 +70,11 @@ instead of the removed public `ModelList` API. | Priority | Location | Risk | Recommended cleanup | | --- | --- | --- | --- | | Done | `src/dependencies/soft_dependencies.jl` hard-dependency redirection | Nested hard-dependency redirection is duplicated, walks parents with a depth cap, and can match by process without enough scale context. | Extracted shared owner-resolution helpers for nested hard dependencies, with scale-aware matching, ambiguity checks, and finalized soft-node validation. | -| Done | `src/domains/domain_simulation.jl` runner | Scheduling, environment sampling/scattering, route materialization, output publication, graph runtime lifecycle, and post-scene run loops were mixed in one large file. | Split domain routes, route runtime, environment bridge, output publication, scheduler helpers, graph-domain runner lifecycle, and shared run-loop orchestration into focused `src/domains/*` modules while keeping public run entry points in `domain_simulation.jl`. | | Done | `src/time/runtime/input_resolution.jl` fallback resolution | Same-node, ancestor, and candidate-scan fallback can silently change behavior when topology or scope changes. | Built a shared source-status resolver that centralizes same-node, ancestor, vector, and unique-candidate fallback with scalar ambiguity validation. | | Done | `src/mtg/initialisation.jl` `RefVector` population | Vector input order depends on MTG traversal order and can drift after growth/removal. | Added `reindex_runtime_topology!` to sort statuses by MTG node id and rebuild downstream `RefVector`s from current statuses after initialization and topology mutations. | | Done | `src/mtg/mapping/compute_mapping.jl` and `src/mtg/mapping/mapping.jl` mapping sentinels/invariants | Magic sentinel values make mapping control flow fragile. | Same-scale mappings now use `SameScale()` instead of `Symbol("")`; explicit validation rejects the old sentinel. | -| Done | Domain run order | Domain order is mostly declaration order with `kind == :scene` last. Route constraints are validated by producer position only. | Added a stable domain DAG scheduler with declaration-order tie-breaking, explicit scene-phase edges, route producer-target edges, and cycle diagnostics. | | Done | `src/mtg/add_organ.jl` topology mutation | Add/remove/reparent updates local status and refs, but scope-derived temporal keys and environment indexes need centralized invalidation. | Add/remove/reparent now centralize runtime topology reindexing; reparent clears temporal state for the moved subtree before rebuilding status and `RefVector` indexes. | -| Done | `examples/maespa_domain_example.jl` scene model | The example mixes solver math, hard-domain target orchestration, publication, soil feedback, and carbon updates. | Split scene energy-balance solving, leaf publication/carbon update, and soil feedback into separate helpers; added tests for selector mismatch, convergence failure, publication counts, and soil feedback. | +| Done | `examples/maespa_scene_example.jl` scene model | The example mixes solver math, call-target orchestration, publication, soil feedback, and carbon updates. | Split scene energy-balance solving, leaf publication/carbon update, and soil feedback into separate helpers; added tests for selector mismatch, convergence failure, publication counts, and soil feedback. | | Done | `src/dependencies/is_graph_cyclic.jl` cycle keys | Cycle detection keys nodes by model value and scale, which can conflate reused model objects. | Traversal now uses dependency-node identity through `IdDict`; cycle reports are converted back to `(model => scale)` for existing diagnostics. | | Done | `src/time/runtime/bindings.jl` and input binding inference | Producer candidates can lose renamed source-variable identity. | Added `ProducerVariable(input, source)` dependency metadata for renamed multiscale producers, and candidate inference now matches on the consumer input while returning the producer source variable. | diff --git a/docs/src/dev/maespa_domain_handoff.md b/docs/src/dev/maespa_domain_handoff.md deleted file mode 100644 index 478725db9..000000000 --- a/docs/src/dev/maespa_domain_handoff.md +++ /dev/null @@ -1,217 +0,0 @@ -# MAESPA-Style Domain And Scene Example Handoff - -The example in `examples/maespa_domain_example.jl` is the executable test bed -for the current hard-domain prototype and the unified scene/object migration. -The domain path remains as regression coverage while the scene/object path is -the target API acceptance example. - -## Current Example Shape - -- Two MTG-backed plant domains share scale names such as `:Plant` and `:Leaf`. -- Each plant domain uses copied PlantBiophysics subsample models: - `Monteith` for `:energy_balance`, `Fvcb` for `:photosynthesis`, and `Tuzet` - for `:stomatal_conductance`. -- `LeafState` owns `leaf_area` and `leaf_carbon`, because those are plant - bookkeeping variables and not PlantBiophysics model outputs. -- `LAIModel` runs in the scene domain and now receives leaf areas through - `ModelSpec(...) |> Inputs(...)`, which is bridged internally to the current - route materialization runtime. -- `SceneEB` runs hourly and now uses `ModelSpec(...) |> Calls(...)` to - manually run leaf `:energy_balance` targets and the shared soil - `:soil_water` target. -- Plant allocation runs daily through the normal plant-local dependency graph. - -## Unified Scene/Object Example Shape - -The same file now also provides `build_maespa_unified_scene(...)` and -`run_maespa_scene_example(...)`. - -- The scene is a single object graph with a `:Scene` object, one shared - `:Soil` object, and two mounted plant instances. -- Species A and B are reusable `ObjectTemplate`s. Each instance mounts the - same leaf model stack shape inside a named plant subtree, with different - species parameters. -- The leaf stack uses the copied PlantBiophysics subsample models: - `Monteith`, `Fvcb`, and `Tuzet`. -- `Monteith` declares a scene `Calls(:photosynthesis => ...)` application, and - `Fvcb` declares `Calls(:stomatal_conductance => ...)`. The scene runtime - builds the small hard-dependency `models` bundle expected by these generic - kernels. -- `SceneEB` is a scene application: - -```julia -ModelSpec(scene_model; name=:scene_eb) |> - AppliesTo(One(scale=:Scene)) |> - Calls( - :energy_balance => Many(kind=:plant, scale=:Leaf, process=:energy_balance), - :soil => One(kind=:soil, scale=:Soil, process=:soil_water), - ) |> - TimeStep(Dates.Hour(1)) -``` - -- `LAIModel` is a scene application with a single `Inputs(...)` leaf-area - binding: - -```julia -ModelSpec(LAIModel(ground_area); name=:lai_dynamic) |> - AppliesTo(One(scale=:Scene)) |> - Inputs(:leaf_areas => Many(kind=:plant, scale=:Leaf, within=SceneScope(), - process=:leaf_state, var=:leaf_area)) |> - TimeStep(Dates.Day(1)) -``` - -- Plant allocation is a plant-local application: - -```julia -ModelSpec(AllocA(...); name=:allocation) |> - AppliesTo(One(scale=:Plant)) |> - Inputs(:leaf_carbon => Many(scale=:Leaf, within=Self(), var=:leaf_carbon)) |> - TimeStep(Dates.Day(1)) -``` - -The scene runtime still uses the existing generic model kernel signature: - -```julia -run!(model, models, status, meteo, constants, extra) -``` - -For manual scene calls, `run_call!(target; meteo=local_meteo)` lets a parent -solver pass trial microclimate directly into the callee. The default is -`publish=false`, so trial calls mutate target status without publishing -temporal samples or environment outputs. This is how `SceneEB` controls the -iterative canopy T/VPD solution. - -## Manual Call Expectations - -- `Calls(:energy_balance => Many(kind=:plant, scale=:Leaf, process=:energy_balance))` - selects one target per matching leaf status through the current hard-domain - bridge. -- `Calls(:soil => One(kind=:soil, process=:soil_water))` selects the shared - soil model through the current hard-domain bridge. -- `call_targets(extra, :energy_balance)` returns executable leaf targets. -- `run_call!(target)` is used during trial iterations. -- `run_call!(target; publish=true)` is used for the accepted final solution - so outputs are appended once to domain streams and `DomainSimulation.outputs`. -- Trial target runs mutate target status. Irreversible accumulators such as - `leaf_carbon` are updated only after the accepted solution. - -## MAESPA-Style Canopy Microclimate - -Input meteorology is treated as above-canopy forcing: - -- `meteo.T` is above-canopy air temperature. -- `meteo.VPD` is above-canopy VPD. -- `meteo.Wind`, `meteo.P`, and radiation variables are above-canopy drivers. - -Scene status stores below-canopy microclimate: - -- `canopy_tair` -- `canopy_vpd` -- `canopy_rh` -- `canopy_htot` -- `canopy_gcanop` - -The helper `tvpdcanopcalc(...)` ports the MAESPA-style canopy T/VPD update, and -`gbcanms(...)` ports the canopy aerodynamic conductance shape. The example uses -PlantMeteo kPa conventions and clipping equivalent to MAESPA's Pa clipping. - -`SceneEB` computes total leaf fluxes per ground area and uses those values for -the canopy microclimate update. Total leaf area is routed to `LAIModel`, which -computes `leaf_area` and `lai` in the scene domain. - -## Verification Expectations - -The focused test `test/test-maespa-domain-example.jl` verifies both the domain -regression path and the unified scene/object path. - -For the domain path it should verify: - -- Species A has two leaves and species B has three leaves. -- `:energy_balance` hard-domain outputs are published once per leaf per hour. -- Soil `psi_soil` is updated through the scene hard target. -- `canopy_tair` and `canopy_vpd` remain finite and within MAESPA clipping - bounds relative to the above-canopy meteo. -- `canopy_rh` remains between 0 and 1. -- `LAIModel` sees every leaf area through the route and computes scene LAI. -- Daily allocation differs between the two plant species because their - allocation parameters differ. - -For the unified scene/object path it should verify: - -- The object graph contains five leaves, two plant instances, one shared soil - object, and one scene object. -- `explain_instances(scene)` reports species A and B instance membership and - their mounted application ids. -- `explain_calls(compiled)` reports the scene energy-balance calls to all leaf - energy-balance applications and the shared soil application. -- Nested leaf calls report `Monteith -> Fvcb -> Tuzet`. -- `explain_model_bundles(compiled)` confirms that every leaf energy-balance - target receives the precompiled `energy_balance`, `photosynthesis`, and - `stomatal_conductance` model bundle expected by the copied PlantBiophysics - kernels. -- `explain_bindings(compiled)` reports live-reference leaf-area and - plant-local leaf-carbon bindings. -- `explain_schedule(compiled)` reports hourly scene energy balance, daily LAI - and allocation clocks, and manual-call-only leaf/soil applications. -- `run_maespa_scene_example(...)` returns a `SceneSimulation` in - `result.simulation`, so `collect_outputs(result.simulation)` and - `explain_outputs(result.simulation)` expose the scene-local output streams. -- If `OutputRequest(...)` values are passed through the scene run, requested - exports should be available from `collect_outputs(result.simulation, - :request_name)` using the same retained scene output streams. -- Scene microclimate, leaf energy, plant allocation, and soil feedback remain - finite and coupled after a 25-hour run. - -## Remaining Migration Target - -The MAESPA example has now moved away from explicit user-level `Route(...)` and -`HardDomains(...)` in the scene/object path. The domain path still exists as -regression coverage until the old domain runtime is removed. - -The target public form is: - -```julia -ModelSpec(LAIModel(ground_area)) |> - AppliesTo(One(scale=:Scene)) |> - Inputs(:leaf_areas => Many(kind=:plant, scale=:Leaf, process=:leaf_state, var=:leaf_area)) - -ModelSpec(SceneEB()) |> - AppliesTo(One(scale=:Scene)) |> - Calls(:leaf_energy => Many(kind=:plant, scale=:Leaf, process=:energy_balance)) |> - Calls(:soil => One(kind=:soil, process=:soil_water)) -``` - -Plant-local allocation should similarly move from `MultiScaleModel(...)` to a -scope-relative input: - -```julia -ModelSpec(AllocA(...)) |> - AppliesTo(Many(kind=:plant, scale=:Plant)) |> - Inputs(:leaf_carbon => Many(scale=:Leaf, within=Self(), var=:leaf_carbon)) -``` - -If this plant-local dependency is the normal behavior of the allocation model, -the model can provide it as a default trait instead: - -```julia -dep(::AllocA) = ( - leaf_carbon = Input(Many(scale=:Leaf, within=Self(), var=:leaf_carbon)), -) -``` - -The same applies to manual calls. A leaf energy-balance model can use `dep` to -declare its usual stomatal-conductance call, while the scene `ModelSpec` can -still override or specialize the call selection with `Calls(...)` when the -model is assembled into a MAESPA-style scene. - -Here `Self()` means the current model application object or scope. Because -`AllocA` runs at the plant scale, this selects leaves inside the current plant. -For a model running below the plant scale, use `SelfPlant()` or -`Ancestor(scale=:Plant)` when the intended scope is the containing plant. - -The migration target should treat `SceneEB`, `LAIModel`, and plant allocation -as model applications. `AppliesTo(...)` declares where each application runs; -`Inputs(...)` provides values scheduled by the runtime; `Calls(...)` provides -manual call handles for the iterative scene energy-balance solver. This keeps -the MAESPA example aligned with the unified design and avoids recreating a -separate domain-specific path. diff --git a/docs/src/dev/maespa_scene_handoff.md b/docs/src/dev/maespa_scene_handoff.md new file mode 100644 index 000000000..19102a58f --- /dev/null +++ b/docs/src/dev/maespa_scene_handoff.md @@ -0,0 +1,94 @@ +# MAESPA-Style Scene Example Handoff + +The executable acceptance example is `examples/maespa_scene_example.jl`, with +focused coverage in `test/test-maespa-scene-example.jl`. + +## Scene Shape + +- One `:Scene` object owns canopy microclimate and scene-scale fluxes. +- One shared `:Soil` object owns soil water state. +- Species A and B are reusable `ObjectTemplate`s mounted as independent + `ObjectInstance`s. +- Each plant instance contains one plant object, one internode object, and its + own leaf objects. +- Species parameters differ while the model application structure is shared. + +Leaf applications use the copied PlantBiophysics subsample models: + +- `Monteith` for `:energy_balance`; +- `Fvcb` for `:photosynthesis`; +- `Tuzet` for `:stomatal_conductance`. + +## Coupling + +The scene energy-balance application controls iterative leaf and soil calls: + +```julia +ModelSpec(scene_model; name=:scene_eb) |> + AppliesTo(One(scale=:Scene)) |> + Calls( + :energy_balance => + Many(kind=:plant, scale=:Leaf, process=:energy_balance), + :soil => + One(kind=:soil, scale=:Soil, process=:soil_water), + ) |> + TimeStep(Dates.Hour(1)) +``` + +Trial leaf calls use `run_call!(target)` and do not publish. The accepted +solution uses `run_call!(target; publish=true)`, so temporal outputs and +environment writes are emitted exactly once. + +Scene LAI receives live references to every leaf area: + +```julia +ModelSpec(LAIModel(ground_area); name=:lai_dynamic) |> + AppliesTo(One(scale=:Scene)) |> + Inputs( + :leaf_areas => Many( + kind=:plant, + scale=:Leaf, + within=SceneScope(), + process=:leaf_state, + var=:leaf_area, + ), + ) |> + TimeStep(Dates.Day(1)) +``` + +Allocation is plant-local because its leaf selector uses `within=Self()`: + +```julia +ModelSpec(allocation; name=:allocation) |> + AppliesTo(One(scale=:Plant)) |> + Inputs(:leaf_carbon => Many(scale=:Leaf, within=Self(), var=:leaf_carbon)) |> + TimeStep(Dates.Day(1)) +``` + +## Meteorology + +Input meteorology is above-canopy forcing. Scene status stores the resulting +below-canopy microclimate: + +- `canopy_tair`; +- `canopy_vpd`; +- `canopy_rh`; +- `canopy_htot`; +- `canopy_gcanop`. + +`tvpdcanopcalc(...)` and `gbcanms(...)` implement the MAESPA-style canopy +temperature, humidity, and aerodynamic-conductance update. + +## Acceptance Checks + +The focused test verifies: + +- five leaves across two species and one shared soil object; +- instance membership and mounted application ids; +- scene calls to all leaf energy-balance applications and the soil model; +- nested `Monteith -> Fvcb -> Tuzet` call bundles; +- live-reference LAI and plant-local allocation bindings; +- hourly energy balance and daily LAI/allocation schedules; +- exactly one accepted publication per manually called target and timestep; +- finite canopy microclimate, leaf energy, photosynthesis, soil feedback, and + species-specific allocation after a 25-hour run. diff --git a/docs/src/dev/multi_domain_simulation_design.md b/docs/src/dev/multi_domain_simulation_design.md deleted file mode 100644 index 184fbd7d3..000000000 --- a/docs/src/dev/multi_domain_simulation_design.md +++ /dev/null @@ -1,506 +0,0 @@ -# Multi-Domain Simulation Design - -!!! warning "Current prototype, not the target breaking design" - This page documents the current multi-domain prototype built around - `Domain`, `SimulationMapping`, `Route`, `AllDomains`, and `HardDomains`. - The target breaking redesign is now documented in - `unified_scene_object_design.md`. New architecture work should use the - unified scene/object design as the source of truth. In that redesign, - `dep(model)` is kept as the model-level default dependency trait: current - `AllDomains(...)` value dependencies migrate toward `Input(...)` defaults, - current `HardDomains(...)` manual dependencies migrate toward `Call(...)` - defaults, and scenario-level `ModelSpec` declarations remain the final - override point. - -This page is the working design for extending PlantSimEngine from one plant or -one MTG mapping to reusable plant, soil, scene, and environment domains. - -The goal is incremental: keep existing `ModelMapping`, `ModelSpec`, -`MultiScaleModel`, hard dependencies, and multi-rate machinery, then add one -composition layer above them. - -## Goals - -- Reuse complete plant models, such as XPalm, as independent species or variety - domains. -- Assemble several plant domains with shared soil, scene, and microclimate - domains. -- Keep plant-local mappings readable and reusable. -- Make cross-domain coupling explicit. -- Support multi-rate execution from the start using `Dates.FixedPeriod` - timesteps. -- Preserve fast reference-based coupling inside existing mappings. -- Make compiled simulations inspectable by humans and agents. -- Allow exceptional same-scale duplicate writers through scenario-level - `Updates(...)` declarations. -- Let meteorology be either constant/table-driven or provided by an external - microclimate backend. - -## Non-Goals For The First Implementation - -- Do not infer cross-domain dependencies from matching variable names. -- Do not implement octree or voxel microclimate in PlantSimEngine. -- Do not solve arbitrary dynamic-topology MTG registration in the first slice; - organ addition, terminal-organ removal, recursive subtree removal, and - same-simulation reparenting are covered. -- Do not rewrite model kernels or force model authors to adopt a new `run!` - signature. - -## Domain - -A `Domain` wraps an existing mapping and gives it an identity: - -```julia -oil_palm = Domain(:oil_palm, kind=:plant, mapping=xpalm) -maize = Domain(:maize, kind=:plant, mapping=maize) -soil = Domain(:soil, kind=:soil, mapping=soil_mapping) -scene = Domain(:scene, kind=:scene, mapping=scene_mapping) -microclimate = Domain(:microclimate, kind=:environment, mapping=microclimate_mapping) -``` - -Model identity inside the compiled simulation is: - -```julia -(domain, scale, process) -``` - -not only: - -```julia -(scale, process) -``` - -This avoids renaming `:Leaf` into species-specific scale names. XPalm can keep -using `:Plant`, `:Leaf`, `:Internode`, and so on. - -For MTG runs, domains will also need selectors: - -```julia -Domain( - :oil_palm, - kind=:plant, - mapping=xpalm, - selector=node -> node[:species] == :oil_palm, -) -``` - -The selector decides which MTG nodes belong to the domain-local view. - -## SimulationMapping - -`SimulationMapping` assembles domains: - -```julia -simulation_mapping = SimulationMapping(oil_palm, maize, soil, scene) -``` - -It should preserve both domain-local and global scale views: - -```julia -status(sim, :oil_palm, :Leaf) # oil palm leaves -status(sim, :maize, :Leaf) # maize leaves -status(sim, :Leaf) # all leaves across plant domains -status(sim, :soil, :Soil) # shared soil statuses -``` - -When one domain selector matches several subtree roots, the domain-local status -view is flattened across those roots: - -```julia -forest = Domain(:forest, kind=:plant, mapping=leaf_mapping, selector=:Plant) -status(sim, :forest, :Leaf) # leaves from every selected Plant subtree -``` - -The initial `run!(mapping::SimulationMapping, meteo)` path supports -single-status domains. The `run!(mtg, mapping::SimulationMapping, meteo)` path -also supports MTG-backed domains when a domain selector resolves to one or more -subtree roots. That path reuses the existing `GraphSimulation` engine for each -selected root, advances domains one base timestep at a time, then publishes -aggregated graph outputs into the domain stream layer so single-status scene -domains can consume them during the same timestep. It is still acyclic by -domain order: domains whose `kind` is not `:scene` run first, then `:scene` -domains run. -Routes into graph domains are supported when their source domain has already -run in the current timestep. Runtime status counts are inspectable with -`explain_domain_statuses(sim)`. - -## Cross-Domain Routes - -Local variable inference remains inside one domain. Cross-domain exchange is -explicit: - -```julia -Route( - from = AllDomains(kind=:plant, scale=:Leaf, var=:Tleaf), - to = DomainRouteTarget(:scene, scale=:Scene, var=:leaf_temperatures), - policy = Integrate(), -) -``` - -Route cardinality should be explicit where needed: - -```julia -ManyToOneVector() -ManyToOneAggregate(sum) -OneToManyBroadcast() -SpatialSample() -SpatialScatterAdd() -``` - -This prevents ambiguous behavior when several plant domains publish the same -scale, process, or variable. - -In the single-status runner, routes are executable for target -`scale=:Default`: - -```julia -Route( - from = AllDomains( - kind=:plant, - process=:plant_transpiration, - var=:transpiration, - ), - to = DomainRouteTarget( - :scene, - var=:plant_transpirations, - process=:scene_evapotranspiration, - ), - cardinality = ManyToOneVector(), -) -``` - -The target variable must already exist in the target domain status. If the -target process is omitted, PlantSimEngine tries to infer a unique model at the -target domain/scale that consumes the routed variable; otherwise the route clock -is hourly/base-step. `ManyToOneVector()` materializes one value per producer. -`ManyToOneAggregate(f)` reduces producer values with `f`. Spatial and broadcast -cardinalities are defined as public route types but are reserved for the -MTG/spatial runner. - -In the current single-status runner, initialize routed vector targets with a -typed placeholder such as `[0.0]` rather than an empty vector. The route -overwrites the value before the target model runs, but the underlying -single-scale `ModelMapping` still treats an empty vector status value as -not initialized. - -The MTG runner also supports one graph-target route form: -`OneToManyBroadcast()` into an MTG-backed domain. The route writes the resolved -source value into every status at the target scale before that graph domain -runs for the current timestep. On the first timestep, the runner also seeds the -selected MTG nodes with the routed attribute before `GraphSimulation` -initialization so existing graph initialization checks still apply. This is -useful for same-timestep coupling from an earlier domain, such as a shared soil -signal broadcast to leaves. Scene-to-plant feedback remains out of scope for -this acyclic order because scene domains run after plant domains. - -## Domain-Aware Value Dependencies - -Scene and environment models often need to consume outputs from all plant -domains. Stream/value dependencies use `AllDomains(...)` and should be explicit: - -```julia -PlantSimEngine.dep(::SceneEvapotranspirationModel) = ( - plant_transpiration = AllDomains( - kind=:plant, - process=:plant_transpiration, - var=:transpiration, - policy=Integrate(), - ), - soil_evaporation = AllDomains( - kind=:soil, - process=:soil_evaporation, - var=:evaporation, - policy=Integrate(), - ), -) -``` - -The runtime resolves this to concrete `(domain, scale, process)` producers. -Scene models consume resolved values, not manually index a single -`extra.models[:Leaf]`. - -Unmatched `AllDomains(...)` selectors are reported with the consumer -`domain/scale/process`, the selector fields, available producer outputs, and -near matches when only `var` is wrong. This is part of the agent-facing API: -errors should contain enough concrete symbols for a user or AI system to repair -the mapping without reverse-engineering the compiled graph. - -In the first executable slice, scene models can read producer values with: - -```julia -plant_values = dependency_values(extra, :plant_transpiration, :transpiration) -soil_values = dependency_values(extra, :soil_evaporation, :evaporation) -``` - -When the selector declares `var`, model code can use the shorter form: - -```julia -plant_values = dependency_values(extra, :plant_transpiration) -``` - -For MTG-backed producer domains, each resolved `(domain, scale, process)` -producer can represent several node statuses. The default -`dependency_values(...)` result therefore contains one value per producer, with -graph-domain producers unwrapped as vectors of node values. Scene models that -want one flat list across all selected domains can request: - -```julia -leaf_values = dependency_values(extra, :leaf_fluxes; flatten=true) -``` - -When graph-domain producer values are reduced over a multi-rate window, the -runtime carries MTG node ids alongside the values and aggregates by node id. -This makes growth and pruning inside the window well-defined: a newly appeared -organ contributes from the timestep where it exists, and a removed organ keeps -its already-published contribution without requiring all value vectors in the -window to have the same length. - -If a selected producer is nested as a hard dependency inside a domain model, its -outputs are published when the owning parent model runs. This keeps the model -visible to scene-level `AllDomains(...)` dependencies without making the hard -dependency independently scheduled. - -## Hard Cross-Domain Dependencies - -Some coupled models need true call-stack control. Energy balance is the typical -case: a scene-scale solver may need to run leaf-scale energy, stomatal -conductance, and photosynthesis models repeatedly until leaf temperatures -converge. That is not an `AllDomains(...)` value dependency. - -Hard cross-domain dependencies use `HardDomains(...)`: - -```julia -PlantSimEngine.dep(::SceneEnergyBalanceModel) = ( - leaf_energy = HardDomains( - kind=:plant, - scale=:Leaf, - process=:leaf_energy_balance, - ), -) -``` - -Inside `run!`, the parent requests targets and runs them manually: - -```julia -function PlantSimEngine.run!(::SceneEnergyBalanceModel, models, status, meteo, constants=nothing, extra=nothing) - leaf_targets = dependency_targets(extra, :leaf_energy) - for iteration in 1:max_iterations - for target in leaf_targets - run_target!(target) - end - converged(status) && break - end - return nothing -end -``` - -Each target selects one model on one runtime status. In single-status -domains, a producer usually gives one target. In MTG-backed domains, a producer -at `scale=:Leaf` gives one target per matching leaf status. The call mutates the -status, just like a normal hard dependency call. By default it does not publish -to domain streams or outputs, which keeps trial iterations from becoming -canonical temporal outputs. The final accepted hard-dependency call can opt into -`publish=true`. - -Hard dependencies remain manual calls owned by the parent model. A hard -dependency child cannot have an independent `ModelSpec` timestep; if the child -needs its own clock, it should be coupled as a soft dependency instead. - -## Multi-Rate Semantics - -User-facing time configuration should continue to use `Dates`: - -```julia -ModelSpec(model) |> TimeStepModel(Dates.Hour(1)) -ModelSpec(model) |> TimeStepModel(Dates.Day(1)) -``` - -For the first implementation, use `Dates.FixedPeriod` only. `Dates.Month` and -`Dates.Year` are calendar-dependent and should error unless a calendar-aware -runtime is added. - -Cross-domain dependencies and routes carry a temporal policy: - -```julia -HoldLast() -Interpolate() -Integrate() -Aggregate() -``` - -The domain key must be part of temporal state keys from the beginning to avoid -collisions between species, soil, and environment streams. - -## Variable Updates - -Duplicate writers remain errors by default. Exceptional updates are declared by -the scenario author, not the model author: - -```julia -ModelSpec(LeafPruning()) |> - Updates(:leaf_biomass; after=:carbon_allocation) -``` - -Rules: - -- one canonical producer is allowed; -- additional same-scale writers must declare `Updates(...)`; -- `after` adds an ordering edge; -- if several update writers target the same variable, they must also be ordered - relative to each other; -- if both models run at the same `DateTime`, the updater runs after the - producer; -- if only the updater runs, it updates the latest available state; -- downstream consumers read the updated value. -- in multi-rate input binding inference, an ordered update chain is treated as - one effective source by selecting the unique terminal updater for the updated - variable. - -Only variables with duplicate writers need annotation. - -## Meteorology And Microclimate - -Models should declare meteorological fields explicitly: - -```julia -meteo_inputs_(::LeafEnergyBalanceModel) = ( - T = -Inf, - Rh = -Inf, - Wind = -Inf, - Ri_PAR_f = -Inf, - CO2 = -Inf, -) - -meteo_outputs_(::MicroclimateModel) = ( - T = -Inf, - Rh = -Inf, - Wind = -Inf, - Ri_PAR_f = -Inf, - CO2 = -Inf, -) -``` - -`inputs_` and `outputs_` remain object status variables. `meteo_inputs_` and -`meteo_outputs_` describe weather/environment variables; `meteo_inputs` and -`meteo_outputs` are the public key accessors. - -PlantSimEngine should define the microclimate backend interface, not the octree -or voxel implementation: - -```julia -abstract type AbstractEnvironmentBackend end - -get_nsteps(backend) -base_step_seconds(backend) -sample(backend, variable, support, time) -sample_environment(backend, support, time, variables) -scatter!(backend, variable, support, value, time) -update_index!(backend, entities) -``` - -`GlobalConstant(meteo)` is the simple backend and preserves current behavior: -all models see the same meteo object or meteo row at a given timestep. Plain -meteo passed to `run!(SimulationMapping, meteo)` is wrapped automatically. - -Backends receive an `EnvironmentSupport(domain, scale, process, status)` so -they can decide whether a sampled value is global, plant-local, organ-local, or -spatially resolved. The same protocol is used for single-status domains and -MTG-backed domains; in graph domains, `status` is the current node status, so -the backend can inspect node geometry, scale, domain, or any status variable. -Octree, voxel, layered-canopy, and CFD backends should live in specialized -packages. - -After each domain step, the domain runtime calls: - -```julia -update_index!(backend, entities) -``` - -where `entities` is a small collection of `(domain, kind, scale, statuses, -state)` rows. Spatial backends can use this hook to refresh their entity index -after geometry changes, growth, pruning, or status updates. `GlobalConstant` -ignores the hook. - -When a model declares `meteo_outputs_`, the domain runtime scatters those values -to the backend after the model runs: - -```julia -meteo_outputs_(::MicroclimateUpdateModel) = (T = -Inf,) -outputs_(::MicroclimateUpdateModel) = (T = -Inf,) -``` - -The same variable must currently be available on the model status, usually by -declaring it in `outputs_`. This keeps the existing `run!` signature unchanged -while giving backends a clear `scatter!` hook. In MTG-backed domains, the -scatter hook is called once per node status after the model runs. `GlobalConstant` -is immutable and errors if a model tries to scatter into it. - -## Growth And Dynamic Entities - -Dynamic organ creation must go through a central registration API. When a new -organ is added, the runtime must update: - -- domain-local status views; -- global scale views; -- cross-domain routes; -- outputs; -- temporal buffers; -- environment spatial indexes. - -This should not be scattered through model code. In MTG-backed domains, models -should keep using the existing: - -```julia -add_organ!(parent_node, graph_simulation, link, symbol, scale; kwargs...) -``` - -from their `run!` implementation. The domain runner passes the underlying -`GraphSimulation` as `extra`, so existing growth models can register new organs -without knowing they are part of a `SimulationMapping`. - -For the current domain runner: - -- `add_organ!` initializes the new node status and updates multiscale - `RefVector` wiring through the existing MTG initialization helpers. -- `remove_organ!` supports terminal MTG nodes by default and internal-node - subtree deletion with `recursive=true`. It removes node statuses, detaches - references from downstream `RefVector`s, clears node-scoped temporal cache and - stream entries, and deletes the MTG nodes. -- `reparent_organ!` moves an already simulated node under another already - simulated parent in the same `GraphSimulation`, validating that both nodes are - registered and that the move does not create a cycle. Statuses and - `RefVector`s continue to reference the same node objects, so no status - rewiring is needed for this case. -- `status(sim, domain, scale)` and `status(sim, scale)` read the live - `GraphSimulation` status vectors, so newly added organs are visible after - registration. If a declared graph scale currently has no active statuses, - these helpers return `Status[]` rather than throwing a dictionary lookup - error, and `explain_domain_statuses(sim)` reports the scale with - `nstatuses=0`. -- graph-domain output publication runs after each graph-domain timestep and - includes newly registered statuses while skipping removed terminal organs. -- `update_index!(backend, entities)` is called after each domain step so - external microclimate backends can refresh their spatial/entity index. - -The current tests cover a new graph-domain leaf publishing an hourly stream -that is consumed by a daily model with `Integrate()`, online -`OutputRequest(...)` exports from dynamically created leaves, several leaves -created in the same timestep, custom callable scopes returning `ScopeId`, -terminal leaf removal in direct and multirate graph-domain coupling, repeated -terminal create/remove cycles, recursive internal-node subtree deletion, and -same-simulation topology reparenting. - -## Agent-Friendly Requirements - -The compiled simulation must be explainable as structured data: - -```julia -explain_domains(sim) -explain_routes(sim) -explain_schedule(sim) -explain_writers(sim) -explain_domain_dependencies(sim) -``` - -Errors should include the conflicting domains/scales/processes and a suggested -fix. For example, duplicate writers should suggest `Updates(:var; after=...)`. diff --git a/docs/src/dev/multi_domain_simulation_plan.md b/docs/src/dev/multi_domain_simulation_plan.md deleted file mode 100644 index 1b6f799c8..000000000 --- a/docs/src/dev/multi_domain_simulation_plan.md +++ /dev/null @@ -1,270 +0,0 @@ -# Multi-Domain Simulation Implementation Plan - -!!! warning "Current prototype plan" - This page tracks what has been implemented in the current multi-domain - prototype. The next breaking architecture plan lives in - `unified_scene_object_implementation_plan.md`. That plan keeps `dep(model)` - as the model-level default dependency trait, with future `Input(...)` and - `Call(...)` defaults overridden by scenario-level `ModelSpec` - configuration. - -This page tracks the implementation plan for the multi-domain work. The design -reference is `multi_domain_simulation_design.md`. - -## Milestone 1: Executable Single-Status Domain Slice - -Done when: - -- `Domain`, `SimulationMapping`, `DomainSimulation`, `DomainModelKey`, and - `AllDomains` exist. -- Domains can wrap existing single-scale `ModelMapping`s. -- A `SimulationMapping` can run plant and soil domains hourly and a scene - domain daily with `Dates.Hour(1)` and `Dates.Day(1)`. -- Single-status domains can contain models with different `ModelSpec` - timesteps. Implemented. -- Scene models can consume resolved cross-domain producer streams with - `dependency_values`. -- `AllDomains(...; var=:x)` can declare the consumed output variable, allowing - `dependency_values(extra, :dependency_name)` and earlier validation of - producer matches. Implemented for the single-status domain runner. -- Scene models can consume a model nested as a hard dependency inside a domain; - the nested model output is published when its owning parent runs. - Implemented for the single-status domain runner. -- `explain_domains`, `explain_schedule`, and - `explain_domain_dependencies` return structured rows. -- A test example covers two plant domains, one soil domain, and one scene - evapotranspiration model. - -Likely files: - -- `src/domains/domain_simulation.jl` -- `src/PlantSimEngine.jl` -- `test/test-domain-simulation.jl` -- `test/runtests.jl` - -## Milestone 2: Agent-Friendly Inspection And Validation - -Current status: implemented for the current domain runner surface. -`explain_domain_models(...)` returns expanded rows, the initial runner -validates unsupported cases early, mixed rates inside single-status domains are -scheduled independently, and explicit `Route(...)` materialization is -implemented for single-status domains with `ManyToOneVector()` and -`ManyToOneAggregate(...)`. - -Done when: - -- `explain_domain_models(simulation_mapping)` returns expanded domain model - rows. Implemented. -- Errors include domain, scale, process, variable, and suggested fixes. - Implemented for unmatched `AllDomains(...)` dependencies and routes, route - targets, duplicate domains, and unsupported domain/route shapes. -- Duplicate domain names error early. Implemented. -- Mixed timesteps inside one initial domain are scheduled independently. - Implemented for single-status domains. -- `AllDomains(...)` selectors report unmatched dependencies with context. - Implemented. -- `Route(...)` materializes cross-domain producer streams into target domain - status variables. Implemented for single-status domains. -- `explain_domain_dependencies(simulation)` reports resolved producers, - temporal policy, and the declared dependency variable. Implemented. -- `explain_routes(simulation)` reports resolved route producers, target - variables, cardinality, temporal policy, and effective route clocks. - Implemented. - -Likely tests: - -- duplicate domain name; -- unmatched `AllDomains`; -- scene domain with multiple scene processes; -- domain with mixed model rates in the initial runner. -- scene dependency targeting a hard-dependency child inside a plant domain. -- vector and aggregate cross-domain routes. - -## Milestone 3: `Updates(...)` In `ModelSpec` - -Current status: implemented for same-scale dependency graphs, single-scale -runs, and MTG-backed domain runs. Downstream input binding inference treats an -unambiguous ordered update chain as one effective producer by selecting the -terminal updater. - -Done when: - -- `Updates(:var; after=:process)` exists as a scenario-level annotation. - Implemented. -- Duplicate writers remain errors unless additional writers declare updates. - Implemented for canonical same-scale writers. -- `after` creates an ordering edge. - Implemented for executable soft dependency nodes. -- Downstream consumers read the terminal updated value when the update order is - unambiguous. Implemented for MTG-backed domains. -- Errors suggest the exact `Updates(:var; after=...)` fix. - Implemented for duplicate writers and invalid update declarations. -- Only variables with duplicate writers need annotation. - Implemented. - -Likely files: - -- `src/mtg/ModelSpec.jl` -- dependency graph construction files -- model spec validation files -- tests for duplicate writers and ordered updates - -## Milestone 4: Meteo Traits - -Current status: trait surface and runtime field validation are implemented. -Environment backend coupling is implemented for the single-status domain -runner through the protocol described in Milestone 6. - -Done when: - -- `meteo_inputs(model)` and `meteo_outputs(model)` default to empty keys from - `NamedTuple()`. Implemented. -- Existing meteo validation can check required fields when traits are present. - Implemented for constant/table meteo field presence, including - `MeteoBindings(source=...)` remapping and public - `validate_meteo_inputs(mapping, meteo)` checks. -- `explain_model_specs` or a new explanation helper reports meteo needs. - Implemented in `explain_model_specs` and `explain_domain_models`. -- Trait declarations support simple NamedTuple defaults first. - Implemented. - -Likely files: - -- `src/processes/models_inputs_outputs.jl` -- `src/time/runtime/meteo_sampling.jl` -- documentation for model authors - -## Milestone 5: MTG-Backed Domains - -Current status: timestep-interleaved MTG-backed domain execution is implemented -for domain selectors that resolve to one or more subtree roots. The runner -advances non-scene domains, including graph domains backed by the existing -`GraphSimulation`, then advances single-status scene domains in the same base -timestep. Graph-domain outputs are aggregated per domain and published into -per-scale streams for `Route(...)` and `AllDomains(...)` consumers. - -Done when: - -- `Domain(..., selector=...)` partitions MTG nodes into domain-local views. - Implemented for one or more selected subtree roots per graph domain. -- Domain-local statuses and global scale statuses are both available. - Domain-local statuses are available through `status(sim, domain, scale)`. - Global cross-domain views are available through `status(sim, scale)` when no domain - has the same name, and runtime counts are reported by - `explain_domain_statuses(sim)`. -- Cross-domain routes can consume all statuses from selected domains. - Implemented for graph-domain sources routed into single-status - domains. Also implemented for `OneToManyBroadcast()` routes into graph - domains when the source domain runs earlier in the current timestep. -- Existing single-domain MTG behavior still works. - Reused by the domain runner through `GraphSimulation`. - -Likely files: - -- `src/mtg/initialisation.jl` -- `src/mtg/GraphSimulation.jl` -- new domain graph simulation code -- tests with two plant species sharing `:Leaf` scale names - -## Milestone 6: Environment Provider Interface - -Current status: protocol and constant backend are implemented for the domain -runner. Custom backends are supported by single-status domains and MTG-backed -domains. Spatial backends remain external-package work. - -Done when: - -- PlantSimEngine defines the backend protocol for sampling and scattering - meteo/environment variables. Implemented with `AbstractEnvironmentBackend`, - `EnvironmentSupport`, `sample`, `sample_environment`, `scatter!`, - `update_index!`, `get_nsteps`, and `base_step_seconds`. -- A constant meteo backend supports current behavior. Implemented with - `GlobalConstant`, and plain meteo passed to `run!(SimulationMapping, meteo)` - is wrapped automatically. -- External packages can implement spatial backends without PlantSimEngine - knowing whether they use voxels, octrees, layers, or another structure. - Implemented for the protocol surface; full spatial examples remain future. -- Environment bindings are visible to explanation helpers. Implemented with - `explain_environment(simulation)` plus `meteo_inputs_` in - `explain_domain_models`. -- Domain models declaring `meteo_outputs_` scatter same-named status values into - mutable backends after `run!`. Implemented for the single-status domain - runner and MTG-backed domain runner; `GlobalConstant` errors because it is - immutable. -- Domain steps call `update_index!(backend, entities)` after model execution so - mutable/spatial backends can refresh their entity indexes. Implemented for - single-status domains and MTG-backed domains; `GlobalConstant` is a no-op. - -Likely API: - -```julia -sample(backend, variable, support, time) -scatter!(backend, variable, support, value, time) -update_index!(backend, entities) -``` - -## Milestone 7: Growth Registration - -Current status: implemented for MTG-backed domains that use the existing -`add_organ!` API inside model `run!` methods, and for terminal or recursive -subtree removal with `remove_organ!`, and for same-simulation topology -reparenting with `reparent_organ!`. Domain status views and graph-domain stream -publication observe the live `GraphSimulation` status vectors, dynamic producer -streams are extended lazily, removed organs are detached from downstream -`RefVector`s and temporal caches, reparented organs keep their existing status -and reference identity, and environment backends receive `update_index!` calls -after each domain step. - -Done when: - -- new organs are added through one runtime API. Implemented by reusing - `add_organ!` from graph-domain models. -- domain-local and global status views are updated. Implemented because - `DomainSimulation` reads the live graph-domain status vectors. -- graph-domain output publication includes newly registered statuses. - Implemented in the timestep-interleaved domain runner. -- environment indexes can be updated by the backend. Implemented through - `update_index!(backend, entities)` after each domain step. -- organs can be removed through one runtime API. Implemented with - `remove_organ!`, which removes the node status, detaches downstream - `RefVector` references, removes node-scoped temporal cache and stream entries, - and deletes the MTG node. Terminal deletion is the default; internal-node - subtree deletion is available with `recursive=true`. -- already simulated organs can be reparented through one runtime API. - Implemented with `reparent_organ!` for moves inside the same active - `GraphSimulation`. -- output and temporal buffers are resized or lazily extended for all covered - dynamic multi-rate cases. Regular graph outputs use the existing - `save_results!` resizing path. Dynamic producer streams are covered for an - hourly producer added during the run and consumed by a daily model with - `Integrate()`. Online `OutputRequest(...)` exports are covered for a - dynamically created leaf using plant scope, several leaves created in the - same timestep, and custom callable scopes returning `ScopeId`. Terminal - removal is covered for direct `RefVector` coupling, multirate temporal - streams, repeated terminal create/remove cycles, and recursive internal-node - subtree deletion. Same-simulation reparenting is covered for topology - mutation while preserving status and reference identity. - -## Executable Examples - -The first executable domain example in `docs/src/domain_simulation.md` uses: - -- two plant domains with different parameters and several hourly models each; -- one soil domain with several hourly models; -- one scene domain with a daily evapotranspiration model; -- explicit `AllDomains(...)` dependencies from the scene model to plant - transpiration and soil evaporation; -- `Dates.Hour(1)` for plant/soil domains and `Dates.Day(1)` for the scene - model. - -The MAESPA-style example in `examples/maespa_domain_example.jl` exercises the -hard-dependency path: - -- two MTG-backed plant domains with different models and parameters; -- one shared soil domain; -- one scene energy-balance model using `HardDomains(...)`; -- manual calls through `dependency_targets(...)` and `run_target!(...)`; -- trial iterations with `publish=false`, followed by final accepted calls with - `publish=true`; -- hourly scene/plant/soil energy-balance models and daily plant allocation - models. diff --git a/docs/src/dev/release_notes_handoff.md b/docs/src/dev/release_notes_handoff.md index c3b919e5a..c5290593d 100644 --- a/docs/src/dev/release_notes_handoff.md +++ b/docs/src/dev/release_notes_handoff.md @@ -29,45 +29,31 @@ Source details live in `code_cleanup_audit.md`. - Replaced many source-side validation `@assert`s with explicit errors. - Added `Updates(:var; after=:process)` for ordered duplicate writers. -## Implemented Multi-Domain / Scene Prototype Features +## Removed Unreleased Domain Prototype -These features exist in the current prototype, but may be replaced by the -unified scene/object API in a future breaking pass. +The experimental domain runtime was developed and replaced on this branch +before release. Its source, tests, examples, and documentation were removed +rather than retained as compatibility code. The removed surface included the +domain containers, cross-domain route types, domain dependency selectors, +domain target handles, and domain-specific explanation helpers. -- `Domain`, `SimulationMapping`, `DomainSimulation`, and `DomainModelKey`. -- Single-status and MTG-backed domains. -- Domain selectors that can select one or more MTG subtree roots. -- Domain-local and global status views: - `status(sim, :plant_A, :Leaf)` and `status(sim, :Leaf)`. -- Multi-rate domain execution using `Dates` periods. -- `AllDomains(...)` stream/value dependencies. -- `Route(...)` materialization with `ManyToOneVector`, - `ManyToOneAggregate`, and limited `OneToManyBroadcast` graph support. -- `HardDomains(...)`, `dependency_targets`, `ModelTarget`, and - `run_target!` for manually controlled hard-domain calls. -- Environment backend protocol: - `AbstractEnvironmentBackend`, `GlobalConstant`, `EnvironmentSupport`, - `sample_environment`, `scatter!`, `update_index!`, `get_nsteps`, and - `base_step_seconds`. -- `meteo_inputs_` and `meteo_outputs_` declarations and validation. -- Dynamic MTG add/remove/reparent runtime reindexing. -- Domain explanation helpers: - `explain_domains`, `explain_domain_models`, `explain_domain_statuses`, - `explain_schedule`, `explain_domain_dependencies`, and `explain_routes`. +The reusable behavior now lives in the scene/object runtime: object selectors, +compiled `Inputs(...)`, manual `Calls(...)`, `Dates`-based scheduling, +environment backends, dynamic object lifecycle handling, and structured +explanations. ## Implemented MAESPA-Style Example Changes -The current `examples/maespa_domain_example.jl` is the main executable example +The current `examples/maespa_scene_example.jl` is the main executable example for multi-plant scene coupling. - Uses copied PlantBiophysics subsample models: `Monteith`, `Fvcb`, and `Tuzet`. -- Uses two MTG-backed plant domains with different parameters and shared scale - names such as `:Plant` and `:Leaf`. +- Uses two plant instances with different parameters and shared scale names + such as `:Plant` and `:Leaf`. - Uses a shared soil model. - Uses `SceneEB` with `ModelSpec(...) |> Calls(...)` to manually run leaf - `:energy_balance` and soil `:soil_water` targets through the current - hard-domain bridge. + `:energy_balance` and soil `:soil_water` targets. - Ports MAESPA-style canopy air temperature and VPD update through `tvpdcanopcalc` and `gbcanms`. - Treats input meteorology as above-canopy forcing and writes below-canopy @@ -75,11 +61,9 @@ for multi-plant scene coupling. `canopy_tair`, `canopy_vpd`, `canopy_rh`, `canopy_htot`, and `canopy_gcanop`. - Adds `LAIModel` and declares plant leaf-area materialization with - `ModelSpec(...) |> Inputs(...)`, bridged internally to the current route - runtime. + `ModelSpec(...) |> Inputs(...)`. - Computes plant allocation daily from plant-local `leaf_carbon` vectors. -- Adds `run_call!` as the unified scene/object spelling for manually executing - current `ModelTarget` call handles. +- Adds `run_call!` for manually executing compiled scene call targets. - Adds model-level `Input(...)` and `Call(...)` dependency defaults through `dep(model)`, with scenario-level `Inputs(...)` and `Calls(...)` overriding those defaults in `ModelSpec`. @@ -291,13 +275,13 @@ for multi-plant scene coupling. when validating canonical root writers, so hard-dependency children are not treated as independent root writers for variables they update inside a parent call stack. -- Adds `build_maespa_unified_scene(...)` and `run_maespa_scene_example(...)`. +- Adds `build_maespa_scene(...)` and `run_maespa_example(...)`. This unified scene/object MAESPA path uses `ObjectTemplate`, `ObjectInstance`, `AppliesTo`, `Inputs`, `Calls`, and `TimeStep(Dates.Period)` with two plant species, one shared soil object, scene LAI, and scene energy balance. -- `test/test-maespa-domain-example.jl` now verifies the unified scene/object - MAESPA path alongside the domain regression path. +- `test/test-maespa-scene-example.jl` verifies the unified scene/object + MAESPA path. - `run!(scene)` now returns a `SceneSimulation` wrapper containing the mutated scene, compiled object bindings, compiled environment bindings, and scene-local temporal output streams. @@ -384,13 +368,9 @@ for multi-plant scene coupling. warmed 128-leaf inner-loop regression gate. - Manual `Calls(...)` handles now use the public `call_target(extra, name)`/`call_targets(extra, name)` lookup API followed by - `run_call!`. The older `dependency_target(s)` and `run_target!` spellings - remain internal compatibility helpers. -- The legacy domain/route authoring surface is no longer exported: - `Domain`, `SimulationMapping`, `DomainSimulation`, `AllDomains`, - `HardDomains`, `Route`, route cardinalities, domain target helpers, and - domain explanation helpers require qualified `PlantSimEngine.*` access for - regression and migration work. + `run_call!`. +- Removed the unreleased domain/route authoring and runtime subsystem after + scene/object feature parity was established. - Adds `objects_from_mtg(root; ...)` and `Scene(mtg; ...)` so existing MTG topology can be adapted once into the unified registry while preserving node-derived identity, parent relations, labels, geometry, and existing @@ -403,11 +383,10 @@ for multi-plant scene coupling. ## Compatibility Boundary -The scene/object runtime and its MAESPA acceptance path are implemented. The -legacy configuration surface is retained only as an explicitly qualified -compatibility and regression layer. It is not exported or presented as the -primary API. The design, implementation history, and completion evidence are -documented in: +The scene/object runtime and its MAESPA acceptance path are implemented. +Historical mapping APIs remain only as an explicitly qualified compatibility +and regression layer. The unreleased domain prototype was removed. The design, +implementation history, and completion evidence are documented in: - `unified_scene_object_design.md` - `unified_scene_object_implementation_plan.md` @@ -420,15 +399,10 @@ The completed public migration is: - model mappings should be described as model applications: `ModelSpec(model; name=...) |> AppliesTo(...) |> Inputs(...) |> Calls(...)`; - `MultiScaleModel(...)` -> `Inputs(...)`. -- `Route(...)` for normal value coupling -> consumer-side `Inputs(...)`. -- `AllDomains(...)` selectors -> object selectors inside `Inputs(...)`. -- `HardDomains(...)` -> `Calls(...)`. - `dep(model)` remains the model-level trait for default dependency intent: defaults can become `Input(...)` value bindings or `Call(...)` manual model calls, and scenario-level `ModelSpec` configuration overrides them. -- `Domain(...)` as user-facing assembly -> scene object templates and - instances. -- model target scales/domains -> `AppliesTo(...)` object selectors. +- model target scales -> `AppliesTo(...)` object selectors. - `InputBindings(...)` -> source, policy, and window information on `Inputs(...)`. - `MeteoBindings(...)` and `MeteoWindow(...)` -> automatic environment @@ -440,16 +414,16 @@ The completed public migration is: - explicit per-model meteo wiring -> automatic environment resolver plus cached environment bindings. -Historical examples and tests that remain are intentionally retained as a -separate qualified compatibility layer. Their continued existence does not -make them part of the public scene/object API. +Historical mapping examples and tests that remain are intentionally retained +as a separate qualified compatibility layer. Their continued existence does +not make them part of the public scene/object API. ## Migration Documentation Added - Added `docs/src/migration_scene_object.md` as the user-facing migration guide - from mappings, routes, and domains to the scene/object API. -- Updated documentation navigation, home-page guidance, legacy domain and - multiscale warnings, and the canonical repository agent skill to direct new + from historical mappings to the scene/object API. +- Updated documentation navigation, home-page guidance, multiscale warnings, + and the canonical repository agent skill to direct new scenarios toward `Scene`, `Object`, `AppliesTo`, `Inputs`, `Calls`, `Updates`, `TimeStep`, and `Environment`. - Replaced the documentation home-page quickstart with executable @@ -469,7 +443,7 @@ make them part of the public scene/object API. guide. It now covers compilation, reference carriers, temporal `Inputs(...)`, manual `Calls(...)`, `Updates(...)`, `TimeStep(...)`, environment binding, retained outputs, lifecycle invalidation, and compatibility translations for - old mapping/domain constructs. + historical mapping constructs. - Rewrote the detailed first simulation tutorial to use the scene/object API. It now introduces `Scene`, `Object`, `ModelSpec`, `AppliesTo`, `TimeStep`, compiled applications, inferred same-object bindings, scene outputs, and a diff --git a/docs/src/dev/unified_scene_object_completion_audit.md b/docs/src/dev/unified_scene_object_completion_audit.md index ffa3088da..72da34ce7 100644 --- a/docs/src/dev/unified_scene_object_completion_audit.md +++ b/docs/src/dev/unified_scene_object_completion_audit.md @@ -11,10 +11,9 @@ Audit date: June 12, 2026. The unified scene/object redesign is implemented as the primary public configuration API. -The retained mapping and domain implementations form an explicitly qualified -compatibility and regression layer. They are not exported, are grouped under -legacy documentation sections, and are not used by the native MAESPA -acceptance path. +The retained mapping implementation forms an explicitly qualified +compatibility and regression layer. The unreleased domain prototype was +removed after the scene/object runtime replaced it. ## Public Contract @@ -32,8 +31,8 @@ TimeStep Environment ``` -Legacy scenario constructors such as `ModelMapping`, `MultiScaleModel`, -`Domain`, `Route`, `AllDomains`, and `HardDomains` are not exported. +Legacy scenario constructors such as `ModelMapping` and `MultiScaleModel` are +not exported. Evidence: @@ -59,7 +58,7 @@ Evidence: | Automatic meteorology and microclimate | `meteo_inputs_`, `meteo_outputs_`, global and spatial backends, ancestor geometry fallback, source remapping, model hints, tabular aggregation, cached bindings, and mutable backend scattering | | Structured agent explanations | Object, instance, scope, application, binding, call, model-bundle, environment, schedule, writer, execution-plan, output, and retention explanations returning structured rows | | Output ownership and retention | Application-qualified streams, `OutputRouting`, `OutputRequest(application=...)`, dynamic-object exports, and bounded policy-specific dependency histories | -| MAESPA acceptance case | `build_maespa_unified_scene` and `run_maespa_scene_example` use `ObjectTemplate`, `ObjectInstance`, `AppliesTo`, `Inputs`, `Calls`, and `TimeStep`; verified by `test/test-maespa-domain-example.jl` | +| MAESPA acceptance case | `build_maespa_scene` and `run_maespa_example` use `ObjectTemplate`, `ObjectInstance`, `AppliesTo`, `Inputs`, `Calls`, and `TimeStep`; verified by `test/test-maespa-scene-example.jl` | | Documentation and migration | Scene/object-first README, home page, quickstart, execution guide, public API, migration guide, and explicitly labeled legacy reference sections | ## Verification @@ -68,7 +67,7 @@ The following gates passed from a clean, controllable Kaimon Julia session: ```text test/test-unified-scene-object-api.jl -test/test-maespa-domain-example.jl +test/test-maespa-scene-example.jl test/test-multirate-output-export.jl test/runtests.jl docs/make.jl @@ -85,15 +84,16 @@ does not weaken the behavioral verification above. ## Compatibility Boundary -Historical mapping/domain source and tests remain intentionally available for: +Historical mapping source and tests remain intentionally available for: - migration comparison; - regression evidence; - downstream users that need a qualified transition path. They must remain qualified as `PlantSimEngine.*` and must not be presented as -the primary API. Removing this compatibility layer entirely is a separate -cleanup decision, not unfinished scene/object functionality. +the primary API. The branch-only domain prototype and its tests were removed +because they were never released and no longer serve as a compatibility +boundary. Requested output histories are still materialized after a run. Dependency-only temporal histories are bounded, but a fully online output sink would be an diff --git a/docs/src/dev/unified_scene_object_design.md b/docs/src/dev/unified_scene_object_design.md index e8a22c838..c7d0845f1 100644 --- a/docs/src/dev/unified_scene_object_design.md +++ b/docs/src/dev/unified_scene_object_design.md @@ -1,5 +1,11 @@ # Unified Scene/Object Design +> **Historical design note** +> +> This document records the redesign process. Names such as `Domain`, +> `Route`, `AllDomains`, and `HardDomains` refer to an unreleased intermediate +> prototype that has been removed from the package. + This page records the target breaking design discussed after the multi-domain prototype. It intentionally supersedes the user-facing distinction between `MultiScaleModel(...)` mappings and `Route(...)` cross-domain materialization. diff --git a/docs/src/dev/unified_scene_object_implementation_plan.md b/docs/src/dev/unified_scene_object_implementation_plan.md index 910eba055..2ca9222f9 100644 --- a/docs/src/dev/unified_scene_object_implementation_plan.md +++ b/docs/src/dev/unified_scene_object_implementation_plan.md @@ -1,12 +1,12 @@ # Unified Scene/Object Implementation Plan -This plan is the persistent handoff for replacing the current domain/route and -multiscale-mapping split with one scene/object address system. +This plan is the persistent handoff for replacing the historical +multiscale-mapping system and an unreleased domain prototype with one +scene/object address system. The implementation can be incremental internally, but the target API is -breaking. Do not try to preserve `MultiScaleModel(...)`, `Route(...)`, -`AllDomains(...)`, or `HardDomains(...)` as primary user-facing concepts in the -final design. +breaking. Do not preserve experimental intermediate APIs as user-facing +concepts in the final design. The target public surface should be centered on a small set of concepts: @@ -26,24 +26,13 @@ Environment(...) This is the API memory target for users, modelers, and agents. Additional types should be selectors, model traits, or internal compiled carriers. -## Current State To Preserve Until Replacement +## Experimental Starting Point -The current experimental branch already implements useful behavior: - -- `Domain`, `SimulationMapping`, and `DomainSimulation`; -- single-status and MTG-backed domains; -- graph-domain selectors for several plant instances/species; -- `Route(...)` materialization into scene status; -- `AllDomains(...)` stream/value dependencies; -- `HardDomains(...)` targets and `run_target!`; -- multi-rate domain scheduling with `Dates`; -- environment backend protocol and constant meteo backend; -- dynamic MTG add/remove/reparent support; -- `Updates(...)` for ordered duplicate writers; -- structured domain explanation helpers. - -Those features are the behavioral test bed for the new design. The new API -should reproduce the same capabilities through unified object selections. +The branch used a temporary domain runtime as a behavioral test bed for +multi-object selection, cross-object values, manual child calls, multi-rate +scheduling, environment backends, lifecycle changes, ordered writers, and +structured explanations. The scene/object API reproduced those capabilities, +then the temporary runtime was removed before release. ## Implementation Progress @@ -372,12 +361,12 @@ should reproduce the same capabilities through unified object selections. from being treated as independent root writers when they intentionally update the same object status inside their parent call stack. - Added a unified scene/object MAESPA example path: - `build_maespa_unified_scene(...)` and `run_maespa_scene_example(...)`. + `build_maespa_scene(...)` and `run_maespa_example(...)`. It uses `ObjectTemplate`, `ObjectInstance`, `AppliesTo`, `Inputs`, `Calls`, and `TimeStep(Dates.Period)` with two plant species, one shared soil object, scene LAI, and scene energy balance. -- `test/test-maespa-domain-example.jl` now verifies the unified scene/object - MAESPA path alongside the existing domain regression path. +- `test/test-maespa-scene-example.jl` verifies the unified scene/object + MAESPA path. - `run!(scene)` now returns a `SceneSimulation` wrapper with the mutated `Scene`, compiled scene bindings, compiled environment bindings, and the scene-local temporal output streams. This keeps existing status mutation @@ -474,10 +463,10 @@ should reproduce the same capabilities through unified object selections. preserving stable node-derived ids, parent relations, labels, geometry, and existing `:plantsimengine_status` objects through configurable accessors. -The scene/object compiler is now executable: selectors normalize to object +The scene/object compiler is executable: selectors normalize to object addresses, resolve before runtime, and compile into reference, temporal, call, -writer, and environment carriers. The older mapping/domain compilers remain -only as qualified compatibility implementations. +writer, and environment carriers. The historical mapping compiler remains +only as a qualified compatibility implementation. ## Phase 0: Public Contract Freeze @@ -730,8 +719,8 @@ Acceptance tests: Implemented: -- `run_call!(::SceneCallTarget)` now defaults to `publish=false`, matching the - manual-call contract and the legacy domain target behavior. +- `run_call!(::SceneCallTarget)` defaults to `publish=false`, matching the + iterative manual-call contract. - One-shot accepted calls use `publish=true` explicitly. - An iterative hard-call regression executes two default non-publishing trials followed by one accepted call and verifies exactly one environment write and @@ -796,9 +785,9 @@ Acceptance tests: MAESPA status: -- the unified scene/object MAESPA path now uses `ObjectTemplate` and - `ObjectInstance` for species A and B. The old domain path remains only as - regression coverage until the domain runtime can be removed. +- the unified scene/object MAESPA path uses `ObjectTemplate` and + `ObjectInstance` for species A and B. The temporary domain path has been + removed. ## Phase 5B: Object Lifecycle And Cache Invalidation @@ -998,8 +987,8 @@ Migration documentation progress: inferred same-object bindings, `OutputRequest` retention, multi-object `Inputs(...)`, `RefVector` carrier explanations, and manual `Calls(...)` syntax. -- The repository agent skill now teaches the unified public vocabulary first - and treats the old mapping/domain stack as legacy-maintenance knowledge. +- The repository agent skill teaches the unified public vocabulary first and + treats the historical mapping stack as legacy-maintenance knowledge. - The public API page now starts with curated scene/object groups for scenario construction, selectors, coupling, lifecycle, environment, runtime, and structured explanations. Mapping-level multirate examples are labeled as @@ -1007,17 +996,13 @@ Migration documentation progress: Current removal audit: -- The legacy domain scenario surface is no longer exported: - `Domain`, `SimulationMapping`, `DomainSimulation`, `AllDomains`, - `HardDomains`, `Route`, route cardinalities, domain target helpers, and - domain explanation helpers remain available only through qualified - `PlantSimEngine.*` names for regression coverage. +- The unreleased domain scenario and runtime subsystem has been deleted, + including its route carriers, dependency selectors, target helpers, tests, + examples, and documentation. - Public manual-call control now uses `call_target`, `call_targets`, and - `run_call!`. The old `dependency_target(s)` and `run_target!` spellings are - compatibility internals. -- `SceneRunContext` now defines `call_target(s)` directly, including - string-name support. The old `dependency_target(s)` scene methods are thin - compatibility wrappers around the scene-native lookup. + `run_call!`. +- `SceneRunContext` defines `call_target(s)` directly, including string-name + support. - The legacy mapping transforms are no longer exported: `MultiScaleModel`, `SameScale`, `TimeStepModel`, `InputBindings`, `MeteoBindings`, `MeteoWindow`, and `ScopeModel` require qualified @@ -1039,7 +1024,7 @@ Current removal audit: It now documents compilation, same-rate reference carriers, temporal `Inputs(...)`, manual `Calls(...)`, `Updates(...)`, `TimeStep(...)`, environment binding, output retention, lifecycle cache invalidation, and - compatibility translations from the old mapping/domain runtime. + compatibility translations from the historical mapping runtime. - The detailed first simulation tutorial now starts from the scene/object API instead of `ModelMapping`. It introduces model kernels, object status, compiled applications, inferred same-object bindings, scene outputs, and a diff --git a/docs/src/domain_simulation.md b/docs/src/domain_simulation.md deleted file mode 100644 index baff9134e..000000000 --- a/docs/src/domain_simulation.md +++ /dev/null @@ -1,373 +0,0 @@ -# Domain simulations - -!!! warning "Legacy configuration API" - This page documents the domain prototype retained for regression coverage. - Its constructors are not exported and require qualified - `PlantSimEngine.*` names. - New scenarios should use the unified scene/object API described in - [Migrating To The Scene/Object API](migration_scene_object.md). - -Domain simulations let you reuse complete plant, soil, scene, or environment -model mappings without renaming their internal scales and processes. - -This is useful when a scene contains several plant species. Each species can -keep its own `ModelMapping`, parameters, hard dependencies, multiscale mappings, -and rates. A `SimulationMapping` then assembles these domains and makes -cross-domain coupling explicit. - -The example on this page is deliberately simple. The numbers are arbitrary and -chosen only to show domain coupling and multi-rate execution. - -## A two-plant scene with shared soil - -First we define a few small models. The plant domains compute absorbed radiation -and transpiration hourly. The soil domain computes water content and evaporation -hourly. The scene domain runs daily and consumes all plant transpiration plus -soil evaporation through explicit `PlantSimEngine.AllDomains(...)` value dependencies. These -dependencies read already-published producer streams; they do not manually run -the producer models. - -```@example domains -using PlantSimEngine -using PlantMeteo -using Dates - -PlantSimEngine.@process "doc_domain_absorbed_radiation" verbose=false -PlantSimEngine.@process "doc_domain_plant_transpiration" verbose=false -PlantSimEngine.@process "doc_domain_soil_water" verbose=false -PlantSimEngine.@process "doc_domain_soil_evaporation" verbose=false -PlantSimEngine.@process "doc_domain_scene_evapotranspiration" verbose=false - -struct DocDomainAbsorbedRadiationModel <: AbstractDoc_Domain_Absorbed_RadiationModel - coefficient::Float64 -end - -PlantSimEngine.inputs_(::DocDomainAbsorbedRadiationModel) = NamedTuple() -PlantSimEngine.outputs_(::DocDomainAbsorbedRadiationModel) = (absorbed_radiation=0.0,) -PlantSimEngine.meteo_inputs_(::DocDomainAbsorbedRadiationModel) = (Ri_PAR_f=0.0,) - -function PlantSimEngine.run!(model::DocDomainAbsorbedRadiationModel, models, status, meteo, constants=nothing, extra=nothing) - status.absorbed_radiation = model.coefficient * meteo.Ri_PAR_f - return nothing -end - -struct DocDomainPlantTranspirationModel <: AbstractDoc_Domain_Plant_TranspirationModel - coefficient::Float64 -end - -PlantSimEngine.inputs_(::DocDomainPlantTranspirationModel) = (absorbed_radiation=0.0,) -PlantSimEngine.outputs_(::DocDomainPlantTranspirationModel) = (transpiration=0.0,) - -function PlantSimEngine.run!(model::DocDomainPlantTranspirationModel, models, status, meteo, constants=nothing, extra=nothing) - status.transpiration = model.coefficient * status.absorbed_radiation - return nothing -end - -struct DocDomainSoilWaterModel <: AbstractDoc_Domain_Soil_WaterModel - baseline::Float64 -end - -PlantSimEngine.inputs_(::DocDomainSoilWaterModel) = NamedTuple() -PlantSimEngine.outputs_(::DocDomainSoilWaterModel) = (soil_water_content=0.0,) -PlantSimEngine.meteo_inputs_(::DocDomainSoilWaterModel) = (T=0.0,) - -function PlantSimEngine.run!(model::DocDomainSoilWaterModel, models, status, meteo, constants=nothing, extra=nothing) - status.soil_water_content = model.baseline - 0.001 * meteo.T - return nothing -end - -struct DocDomainSoilEvaporationModel <: AbstractDoc_Domain_Soil_EvaporationModel - coefficient::Float64 -end - -PlantSimEngine.inputs_(::DocDomainSoilEvaporationModel) = (soil_water_content=0.0,) -PlantSimEngine.outputs_(::DocDomainSoilEvaporationModel) = (evaporation=0.0,) -PlantSimEngine.meteo_inputs_(::DocDomainSoilEvaporationModel) = (T=0.0,) - -function PlantSimEngine.run!(model::DocDomainSoilEvaporationModel, models, status, meteo, constants=nothing, extra=nothing) - status.evaporation = model.coefficient * status.soil_water_content * meteo.T - return nothing -end - -struct DocDomainSceneEvapotranspirationModel <: AbstractDoc_Domain_Scene_EvapotranspirationModel end - -PlantSimEngine.inputs_(::DocDomainSceneEvapotranspirationModel) = NamedTuple() -PlantSimEngine.outputs_(::DocDomainSceneEvapotranspirationModel) = (evapotranspiration=0.0,) - -PlantSimEngine.dep(::DocDomainSceneEvapotranspirationModel) = ( - plant_transpiration=PlantSimEngine.AllDomains(kind=:plant, process=:doc_domain_plant_transpiration, var=:transpiration, policy=Integrate()), - soil_evaporation=PlantSimEngine.AllDomains(kind=:soil, process=:doc_domain_soil_evaporation, var=:evaporation, policy=Integrate()), -) - -function PlantSimEngine.run!(::DocDomainSceneEvapotranspirationModel, models, status, meteo, constants=nothing, extra=nothing) - plant_values = PlantSimEngine.dependency_values(extra, :plant_transpiration) - soil_values = PlantSimEngine.dependency_values(extra, :soil_evaporation) - status.evapotranspiration = - sum(filter(x -> !isnothing(x), plant_values)) + - sum(filter(x -> !isnothing(x), soil_values)) - return nothing -end -nothing -``` - -Now each domain can be built independently. The oil palm and maize mappings use -the same model types but different parameters. In a real application these -could be completely different plant models, with different processes and -different internal mappings. - -```@example domains -oil_palm_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DocDomainAbsorbedRadiationModel(0.5)) |> PlantSimEngine.TimeStepModel(Hour(1)), - ModelSpec(DocDomainPlantTranspirationModel(0.01)) |> PlantSimEngine.TimeStepModel(Hour(1)), - status=(absorbed_radiation=0.0, transpiration=0.0), -) - -maize_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DocDomainAbsorbedRadiationModel(0.3)) |> PlantSimEngine.TimeStepModel(Hour(1)), - ModelSpec(DocDomainPlantTranspirationModel(0.02)) |> PlantSimEngine.TimeStepModel(Hour(1)), - status=(absorbed_radiation=0.0, transpiration=0.0), -) - -soil_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DocDomainSoilWaterModel(0.35)) |> PlantSimEngine.TimeStepModel(Hour(1)), - ModelSpec(DocDomainSoilEvaporationModel(0.2)) |> PlantSimEngine.TimeStepModel(Hour(1)), - status=(soil_water_content=0.0, evaporation=0.0), -) - -scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DocDomainSceneEvapotranspirationModel()) |> PlantSimEngine.TimeStepModel(Day(1)), - status=(evapotranspiration=0.0,), -) - -simulation_mapping = PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:oil_palm, oil_palm_mapping; kind=:plant), - PlantSimEngine.Domain(:maize, maize_mapping; kind=:plant), - PlantSimEngine.Domain(:soil, soil_mapping; kind=:soil), - PlantSimEngine.Domain(:scene, scene_mapping; kind=:scene), -) -nothing -``` - -The meteorology is hourly. The plant and soil models run hourly, while the -scene model runs every day and integrates the producer streams. - -```@example domains -hourly_meteo = Weather([ - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Hour(1)) - for _ in 1:25 -]) - -sim = run!(simulation_mapping, hourly_meteo; check=true) -round(status(sim, :scene).evapotranspiration; digits=2) -``` - -The final value is the daily integral of the two plant transpiration streams and -the soil evaporation stream. - -## Inspecting the compiled simulation - -Domain simulations expose small structured explanation helpers. These are meant -for users and for agents that need to repair a mapping from concrete symbols. - -```@example domains -sort([(row.domain, row.kind) for row in PlantSimEngine.explain_domains(sim)]) -``` - -```@example domains -sort([(row.domain, row.process, row.dt_seconds) for row in explain_schedule(sim)]) -``` - -```@example domains -sort( - [(row.dependency, string(row.producer), row.policy) for row in PlantSimEngine.explain_domain_dependencies(sim)]; - by=string, -) -``` - -The model-level explanation also includes the weather variables declared with -`meteo_inputs_` and any mutable environment variables declared with -`meteo_outputs_`: - -```@example domains -[(row.domain, row.process, collect(keys(row.meteo_inputs))) for row in PlantSimEngine.explain_domain_models(sim) if !isempty(row.meteo_inputs)] -``` - -## Explicit routes - -`PlantSimEngine.AllDomains(...)` value dependencies are the most direct way for a scene model -to consume several domain outputs. Routes are another option when you want to -materialize values into the target status before the target model runs. - -```julia -PlantSimEngine.Route( - from=PlantSimEngine.AllDomains(kind=:plant, process=:plant_transpiration, var=:transpiration), - to=PlantSimEngine.DomainRouteTarget(:scene, var=:plant_transpirations, process=:scene_evapotranspiration), - cardinality=PlantSimEngine.ManyToOneVector(), -) -``` - -Use `PlantSimEngine.ManyToOneVector()` when the target model needs one value per matching -producer, and `PlantSimEngine.ManyToOneAggregate(f)` when it needs a scalar reduction. For an -MTG-backed target domain, `PlantSimEngine.OneToManyBroadcast()` can broadcast one source value -into every status at the target scale before that domain runs. - -When a route targets a single-status domain variable consumed by one target -model, the target status slot is created from that model's `inputs_` default if -the user did not initialize it explicitly. Variables that are only route -materialization slots and are not model inputs still need to be initialized in -the target status. - -When graph-domain values are aggregated across time, PlantSimEngine aligns them -by MTG node id. Growth and pruning inside the aggregation window therefore do -not require every timestep to publish vectors with the same length. - -## Hard-domain dependencies - -Use `PlantSimEngine.HardDomains(...)` when a model must manually run selected producer -models, for example to control an iterative energy-balance solver: - -```julia -PlantSimEngine.dep(::SceneEnergyBalanceModel) = ( - leaf_energy=PlantSimEngine.HardDomains(kind=:plant, scale=:Leaf, process=:leaf_energy_balance), -) - -function PlantSimEngine.run!(::SceneEnergyBalanceModel, models, status, meteo, constants=nothing, extra=nothing) - leaf_targets = PlantSimEngine.dependency_targets(extra, :leaf_energy) - for iteration in 1:10 - for target in leaf_targets - PlantSimEngine.run_target!(target) - end - converged(status) && break - end - return nothing -end -``` - -Each target represents one selected model on one status. For an MTG-backed -producer domain, this means one target per matching node status, such as one -target per leaf. `run_target!` mutates that status like a normal model call, -but it does not append to domain streams or outputs unless `publish=true` is -requested. Trial iterations should usually run with `publish=false`, then the -final accepted hard-dependency call can use `publish=true`. - -For same-status hard dependencies, the same target API can replace direct model -calls: - -```julia -function PlantSimEngine.run!(::PhyllochronModel, models, status, meteo, constants, extra) - PlantSimEngine.run_target!(models, status, :phytomer_emission; meteo=meteo, constants=constants, extra=extra) - return nothing -end -``` - -## MTG-backed plant domains - -The same `Domain` wrapper can be used around a scale-keyed `ModelMapping`. -When running on an MTG, the `selector` decides which subtree roots belong to -the domain: - -```julia -oil_palm = PlantSimEngine.Domain(:oil_palm, xpalm_mapping; kind=:plant, selector=node -> node[:species] == :oil_palm) -maize = PlantSimEngine.Domain(:maize, maize_mapping; kind=:plant, selector=node -> node[:species] == :maize) -sim = run!(scene_mtg, PlantSimEngine.SimulationMapping(oil_palm, maize, scene), meteo) -``` - -Status inspection keeps both the domain-local view and the global scale view: - -```julia -status(sim, :oil_palm, :Leaf) # only oil palm leaves -status(sim, :maize, :Leaf) # only maize leaves -status(sim, :Leaf) # all leaves across graph-backed domains -``` - -If a declared graph scale currently has no active statuses, for example after -pruning all leaves, these status queries return `Status[]`. The same empty scale -is reported by `PlantSimEngine.explain_domain_statuses(sim)` with `nstatuses=0`. - -Selectors can also match several non-overlapping roots, for example -`selector=:Plant` to run one mapping over every plant subtree. Selectors that -match overlapping roots are rejected because they would register the same organ -twice. - -## Meteorology and microclimate - -Plain meteorology passed to `run!(SimulationMapping, meteo)` is wrapped as a -`GlobalConstant` environment backend. This preserves the existing behavior: -every object sees the same weather row at a given timestep. -If a model declares `meteo_inputs_`, running without meteorology now fails -during validation with the missing environment variables, instead of failing -later inside the model code. - -Spatial or mutable microclimate should live in a specialized backend, not in -PlantSimEngine itself. A backend subtypes `AbstractEnvironmentBackend` and -implements the sampling and update hooks: - -```julia -PlantSimEngine.sample(backend, variable, support, time) -PlantSimEngine.scatter!(backend, variable, support, value, time) -PlantSimEngine.update_index!(backend, entities) -``` - -Models declare which environment variables they read and write: - -```julia -PlantSimEngine.meteo_inputs_(::LeafEnergyBalanceModel) = ( - T=0.0, - Rh=0.0, - Wind=0.0, - Ri_PAR_f=0.0, - CO2=400.0, -) - -PlantSimEngine.meteo_outputs_(::MicroclimateUpdateModel) = (T=0.0,) -``` - -`EnvironmentSupport(domain, scale, process, status)` is passed to the backend, -so an octree, voxel, layered-canopy, or CFD backend can decide how to sample the -environment for the current organ or plant. In graph-backed domains, `status` -is the current node status. - -## Updating a variable - -Duplicate canonical writers are still errors by default. If a model is meant to -update a variable already produced at the same scale, declare that scenario rule -on the updating `ModelSpec`: - -```julia -ModelSpec(LeafPruningModel()) |> - Updates(:leaf_biomass; after=:carbon_allocation) -``` - -Only variables with several writers need an `Updates(...)` declaration. The -declaration adds an ordering edge and downstream consumers infer the terminal -updater as the effective source. - -## Growth and topology changes - -MTG-backed domain models receive the underlying `GraphSimulation` as `extra`, -so existing growth models can keep using the MTG registration APIs: - -```julia -add_organ!(parent_node, extra, "+", :Leaf, 2; check=true) -remove_organ!(leaf_node, extra) -remove_organ!(internode_node, extra; recursive=true) -reparent_organ!(leaf_node, new_parent_node, extra) -``` - -These helpers update statuses, multiscale `RefVector`s, temporal streams, and -domain outputs. `update_index!(backend, entities)` is called after each domain -step so spatial environment backends can refresh their entity index after -growth, pruning, or geometry changes. - -## Current boundaries - -Cross-domain dependencies are explicit. PlantSimEngine does not infer them from -matching variable names across domains. - -The domain layer defines the environment backend protocol, but it does not -implement an octree, voxel grid, or energy-balance solver. Those belong in -domain packages that provide models or environment backends. - -Scene domains run after plant and soil domains in the current acyclic runner. -Routes that feed an MTG-backed target domain therefore need their source domain -to run earlier in the same timestep. diff --git a/docs/src/index.md b/docs/src/index.md index 6a82da6e5..8dd0c9866 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -235,8 +235,8 @@ environment writes are published exactly once. - [Scene/Object Quickstart](scene_object/quickstart.md) gives a compact runnable workflow using the new API. -- [Migrating To The Scene/Object API](migration_scene_object.md) translates old - `ModelMapping`, `MultiScaleModel`, `Route`, and domain examples. +- [Migrating To The Scene/Object API](migration_scene_object.md) translates + historical `ModelMapping` and `MultiScaleModel` examples. - [Public API](API/API_public.md) lists the scene/object constructors, selectors, lifecycle hooks, and explanation helpers. - [Model traits](model_traits.md) explains `inputs_`, `outputs_`, `dep`, diff --git a/docs/src/migration_scene_object.md b/docs/src/migration_scene_object.md index b471fc944..2f292b2f0 100644 --- a/docs/src/migration_scene_object.md +++ b/docs/src/migration_scene_object.md @@ -1,7 +1,7 @@ # Migrating To The Scene/Object API -The scene/object API replaces the separate multiscale mapping and domain -configuration systems with one object-address graph. +The scene/object API replaces the historical multiscale mapping system with +one object-address graph. New scenario code should be organized around: @@ -17,7 +17,7 @@ TimeStep Environment ``` -Model implementations do not need to know about scenes, plants, domains, or +Model implementations do not need to know about scenes, plants, objects, or timesteps. They keep the existing kernel contract: ```julia @@ -34,9 +34,8 @@ equivalents. ## Scenario Structure -Legacy simulations split configuration between `ModelMapping`, -`MultiScaleModel`, `Domain`, and `SimulationMapping`. The unified API stores -runtime entities in one `Scene`: +Legacy simulations split configuration between `ModelMapping` and +`MultiScaleModel`. The unified API stores runtime entities in one `Scene`: `ModelMapping` is no longer exported. While migrating historical code, use `PlantSimEngine.ModelMapping(...)` explicitly; new scenarios should use the @@ -132,27 +131,9 @@ Inputs( Same-rate bindings use shared references or reference vectors when possible. Cross-rate bindings use typed temporal streams. -## Cross-Domain Values And Routes +## Scene-Wide Values -Replace `AllDomains(...)` and user-written `Route(...)` with an input selector -on the consuming application. - -Legacy: - -```julia -Route( - from=AllDomains( - kind=:plant, - scale=:Leaf, - process=:transpiration, - var=:transpiration, - ), - to=DomainRouteTarget(:scene, var=:leaf_transpiration), - cardinality=ManyToOneVector(), -) -``` - -Unified: +Use an input selector on the consuming application: ```julia ModelSpec(SceneWaterBalance(); name=:scene_water) |> @@ -173,7 +154,7 @@ source variable, and temporal policy rather than a route implementation. ## Manual Hard Calls -Replace `HardDomains(...)` with `Calls(...)`. +Use `Calls(...)` when a parent model must control child execution. ```julia ModelSpec(SceneEnergyBalance(); name=:scene_energy) |> @@ -221,7 +202,8 @@ state must use `publish=true`. ## Multiple Plants And Species -Replace repeated plant domains with `ObjectTemplate` and `ObjectInstance`. +Represent repeated plant configurations with `ObjectTemplate` and +`ObjectInstance`. ```julia oil_palm = ObjectTemplate( @@ -463,12 +445,8 @@ targets. They are intended for both users and coding agents. | Legacy configuration | Scene/object replacement | | --- | --- | -| `ModelMapping` scale/domain assembly | `Scene` objects plus model applications | +| `ModelMapping` scale assembly | `Scene` objects plus model applications | | `MultiScaleModel(...)` | consumer `Inputs(...)` | -| `Route(...)` | compiler-selected carrier from `Inputs(...)` | -| `AllDomains(...)` | object selector inside `Inputs(...)` | -| `HardDomains(...)` | `Calls(...)` | -| `Domain(...)` | `ObjectTemplate` and `ObjectInstance` | | `TimeStepModel(...)` | `TimeStep(...)` | | `InputBindings(...)` | source, policy, and window on `Inputs(...)` | | `MeteoBindings(...)` | automatic environment binding or `Environment(...)` | @@ -476,6 +454,6 @@ targets. They are intended for both users and coding agents. | `SameScale()` rename | `Inputs(:local => One(within=Self(), var=:source))` | The executable MAESPA migration in -`examples/maespa_domain_example.jl` demonstrates two plant species, shared +`examples/maespa_scene_example.jl` demonstrates two plant species, shared soil, plant-local aggregation, scene-wide iterative energy balance, hourly and daily models, and automatic environment binding. diff --git a/docs/src/model_execution.md b/docs/src/model_execution.md index 8c8310123..8ccc3a424 100644 --- a/docs/src/model_execution.md +++ b/docs/src/model_execution.md @@ -346,8 +346,6 @@ Those names are no longer the primary API. For new work, use: - `InputBindings(...)` -> selector fields inside `Inputs(...)`; - `MeteoBindings(...)` / `MeteoWindow(...)` -> `Environment(...)`, `meteo_hint(...)`, and model/application clocks; -- `HardDomains(...)` -> `Calls(...)`; -- `Route(...)` and `AllDomains(...)` -> consumer-side `Inputs(...)`; - `TimeStepModel(...)` -> `TimeStep(...)`. See [Migrating To The Scene/Object API](migration_scene_object.md) for worked diff --git a/docs/src/scene_object/quickstart.md b/docs/src/scene_object/quickstart.md index 8dc4e8068..90c810f27 100644 --- a/docs/src/scene_object/quickstart.md +++ b/docs/src/scene_object/quickstart.md @@ -250,5 +250,5 @@ accepted state should use `publish=true`. helpers, environment helpers, and explanation helpers. - [Model traits](../model_traits.md) documents `inputs_`, `outputs_`, `dep`, `timespec`, `output_policy`, `meteo_inputs_`, and `meteo_outputs_`. -- [MAESPA-style domain example handoff](../dev/maespa_domain_handoff.md) +- [MAESPA-style scene example handoff](../dev/maespa_scene_handoff.md) records the current multi-plant scene energy-balance acceptance example. diff --git a/docs/src/step_by_step/quick_and_dirty_examples.md b/docs/src/step_by_step/quick_and_dirty_examples.md index 6f627e17a..3e0965d86 100644 --- a/docs/src/step_by_step/quick_and_dirty_examples.md +++ b/docs/src/step_by_step/quick_and_dirty_examples.md @@ -147,7 +147,7 @@ PlantBiophysics. A typical PlantBiophysics energy-balance setup uses stomatal-conductance models, then call `run_call!(target; publish=true)` once for the accepted solution. -See [MAESPA-style domain example handoff](../dev/maespa_domain_handoff.md) for +See [MAESPA-style scene example handoff](../dev/maespa_scene_handoff.md) for the current multi-plant energy-balance acceptance example. ## Compatibility Note diff --git a/examples/dummy.jl b/examples/dummy.jl index 82dcde9f1..4a4482a34 100644 --- a/examples/dummy.jl +++ b/examples/dummy.jl @@ -37,7 +37,7 @@ PlantSimEngine.outputs_(::Process2Model) = (var4=-Inf, var5=-Inf) PlantSimEngine.dep(::Process2Model) = (process1=AbstractProcess1Model,) function PlantSimEngine.run!(::Process2Model, models, status, meteo, constants=nothing, extra=nothing) # computing var3 using process1: - PlantSimEngine.run_target!(models, status, :process1; meteo=meteo, constants=constants, extra=extra) + PlantSimEngine.run!(models.process1, models, status, meteo, constants, extra) # computing var4 and var5: status.var4 = status.var3 * 2.0 status.var5 = status.var4 + 1.0 * meteo.T + 2.0 * meteo.Wind + 3.0 * meteo.Rh @@ -63,7 +63,7 @@ PlantSimEngine.outputs_(::Process3Model) = (var4=-Inf, var6=-Inf,) PlantSimEngine.dep(::Process3Model) = (process2=Process2Model,) function PlantSimEngine.run!(::Process3Model, models, status, meteo, constants=nothing, extra=nothing) # computing var3 using process1: - PlantSimEngine.run_target!(models, status, :process2; meteo=meteo, constants=constants, extra=extra) + PlantSimEngine.run!(models.process2, models, status, meteo, constants, extra) # re-computing var4: status.var4 = status.var4 * 2.0 status.var6 = status.var5 + status.var4 diff --git a/examples/maespa_domain_example.jl b/examples/maespa_scene_example.jl similarity index 77% rename from examples/maespa_domain_example.jl rename to examples/maespa_scene_example.jl index 21cb50938..9c4205e08 100644 --- a/examples/maespa_domain_example.jl +++ b/examples/maespa_scene_example.jl @@ -1,7 +1,6 @@ using Dates using PlantMeteo using PlantSimEngine -using MultiScaleTreeGraph include(joinpath(@__DIR__, "plantbiophysics_subsample", "Tuzet.jl")) include(joinpath(@__DIR__, "plantbiophysics_subsample", "FvCB.jl")) @@ -51,8 +50,7 @@ PlantSimEngine.run!(::LeafState, models, status, meteo, constants, extra=nothing """ LAIModel(area) -Compute scene leaf area and leaf area index from all leaves routed into the -scene domain. +Compute scene leaf area and leaf area index from all selected leaves. """ struct LAIModel{T} <: AbstractLai_DynamicModel area::T @@ -361,104 +359,6 @@ PlantSimEngine.run!(m::AllocA, models, status, meteo, constants, extra=nothing) PlantSimEngine.run!(m::AllocB, models, status, meteo, constants, extra=nothing) = allocate!(status, m.leaf_fraction, m.wood_fraction) -function build_maespa_scene() - scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant_a = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - plant_a[:species] = :A - axis_a = Node(plant_a, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - for i in 1:2 - leaf = Node(axis_a, MultiScaleTreeGraph.NodeMTG("+", :Leaf, i, 3)) - leaf[:species] = :A - end - - plant_b = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 2, 1)) - plant_b[:species] = :B - axis_b = Node(plant_b, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - for i in 1:3 - leaf = Node(axis_b, MultiScaleTreeGraph.NodeMTG("+", :Leaf, i, 3)) - leaf[:species] = :B - end - return scene -end - -has_species(node, species) = - try - node[:species] == species - catch - false - end - -function maespa_mapping(; scene_model=SceneEB(25, 0.03, 0.005)) - leaf_a = ( - ModelSpec(Monteith(; ε=0.955, maxiter=20, ΔT=0.02)) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), - ModelSpec(Fvcb(; VcMaxRef=72.0, JMaxRef=135.0, RdRef=1.1)) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), - ModelSpec(Tuzet(; g0=0.015, g1=4.8, Ψᵥ=-1.4, sf=3.2, Γ=42.0)) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), - ModelSpec(LeafState()) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), - Status(Ra_SW_f=0.0, sky_fraction=1.0, d=0.035, aPPFD=0.0, Ψₗ=-0.1, leaf_area=0.018, leaf_carbon=0.0), - ) - leaf_b = ( - ModelSpec(Monteith(; ε=0.955, maxiter=20, ΔT=0.02)) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), - ModelSpec(Fvcb(; VcMaxRef=58.0, JMaxRef=110.0, RdRef=1.3)) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), - ModelSpec(Tuzet(; g0=0.012, g1=3.5, Ψᵥ=-1.1, sf=3.8, Γ=42.0)) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), - ModelSpec(LeafState()) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), - Status(Ra_SW_f=0.0, sky_fraction=0.8, d=0.028, aPPFD=0.0, Ψₗ=-0.1, leaf_area=0.014, leaf_carbon=0.0), - ) - - plant_a = PlantSimEngine.ModelMapping( - :Plant => ( - ModelSpec(AllocA(0.35, 0.55)) |> - PlantSimEngine.MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]]) |> - PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)), - Status(leaf_pool=0.0, wood_pool=0.0), - ), - :Leaf => leaf_a, - ) - plant_b = PlantSimEngine.ModelMapping( - :Plant => ( - ModelSpec(AllocB(0.55, 0.35)) |> - PlantSimEngine.MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]]) |> - PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)), - Status(leaf_pool=0.0, wood_pool=0.0), - ), - :Leaf => leaf_b, - ) - soil = PlantSimEngine.ModelMapping( - ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75)) |> PlantSimEngine.TimeStepModel(Dates.Hour(1)), - status=(theta1=0.33, theta2=0.36, psi_soil=-0.10, transpiration=0.0, infiltration=0.0), - ) - ground_area = scene_model.ground_area - scene = PlantSimEngine.ModelMapping( - ModelSpec(LAIModel(ground_area)) |> - Inputs(:leaf_areas => Many(kind=:plant, scale=:Leaf, process=:leaf_state, var=:leaf_area)) |> - TimeStep(Dates.Day(1)), - ModelSpec(scene_model) |> - Calls( - :energy_balance => Many(kind=:plant, scale=:Leaf, process=:energy_balance), - :soil => One(kind=:soil, process=:soil_water), - ) |> - TimeStep(Dates.Hour(1)), - status=( - leaf_area=0.0, - lai=0.0, - canopy_tair=20.0, - canopy_vpd=1.0, - canopy_rh=0.7, - canopy_htot=0.0, - canopy_gcanop=0.0, - scene_transpiration=0.0, - scene_assimilation=0.0, - psi_soil=-0.1, - iterations=0, - ), - ) - return PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:plant_A, plant_a; kind=:plant, selector=node -> MultiScaleTreeGraph.symbol(node) == :Plant && has_species(node, :A)), - PlantSimEngine.Domain(:plant_B, plant_b; kind=:plant, selector=node -> MultiScaleTreeGraph.symbol(node) == :Plant && has_species(node, :B)), - PlantSimEngine.Domain(:soil, soil; kind=:soil), - PlantSimEngine.Domain(:scene, scene; kind=:scene), - ) -end - function _maespa_leaf_status(; leaf_area, sky_fraction, d) return Status( Ra_SW_f=0.0, @@ -556,7 +456,7 @@ function _maespa_plant_instance(name, template; nleaves, leaf_area, sky_fraction ) end -function build_maespa_unified_scene(; scene_model=SceneEB(25, 0.03, 0.005), meteo=maespa_meteo()) +function build_maespa_scene(; scene_model=SceneEB(25, 0.03, 0.005), meteo=maespa_meteo()) template_a = _maespa_species_template( :A; monteith=Monteith(; ε=0.955, maxiter=20, ΔT=0.02), @@ -634,14 +534,7 @@ function maespa_meteo(; nhours=24) end function run_maespa_example(; nhours=24, check=true) - mtg = build_maespa_scene() - mapping = maespa_mapping() - sim = run!(mtg, mapping, maespa_meteo(; nhours=nhours), check=check, executor=SequentialEx()) - return (mtg=mtg, mapping=mapping, simulation=sim) -end - -function run_maespa_scene_example(; nhours=24, check=true) - scene = build_maespa_unified_scene(; meteo=maespa_meteo(; nhours=nhours)) + scene = build_maespa_scene(; meteo=maespa_meteo(; nhours=nhours)) compiled = compile_scene(scene) check && refresh_environment_bindings!(scene, compiled) simulation = run!(scene; steps=nhours, constants=PlantMeteo.Constants()) @@ -655,10 +548,13 @@ end if abspath(PROGRAM_FILE) == @__FILE__ result = run_maespa_example() - sim = result.simulation - println("leaf_count = ", length(status(sim, :Leaf))) - println("scene_transpiration = ", status(sim, :scene).scene_transpiration) - println("psi_soil = ", status(sim, :scene).psi_soil) - println("plant_A = ", only(status(sim, :plant_A, :Plant)).daily_growth) - println("plant_B = ", only(status(sim, :plant_B, :Plant)).daily_growth) + scene = result.scene + println("leaf_count = ", length(scene_objects(scene; scale=:Leaf))) + println( + "scene_transpiration = ", + only(scene_objects(scene; scale=:Scene)).status.scene_transpiration, + ) + println("psi_soil = ", only(scene_objects(scene; kind=:soil)).status.psi_soil) + println("plant_A = ", only(scene_objects(scene; name=:plant_A)).status.daily_growth) + println("plant_B = ", only(scene_objects(scene; name=:plant_B)).status.daily_growth) end diff --git a/skills/plantsimengine/SKILL.md b/skills/plantsimengine/SKILL.md index c922b7eec..7b239e8ac 100644 --- a/skills/plantsimengine/SKILL.md +++ b/skills/plantsimengine/SKILL.md @@ -18,11 +18,11 @@ PlantSimEngine has two main user roles: `inputs_`, `outputs_`, `dep`, `meteo_inputs_`, `meteo_outputs_`, `run!`, model traits, and focused tests. -For new multiscale, multi-plant, soil, scene, or microclimate work, prefer the -unified scene/object API. Use `ModelMapping`, `MultiScaleModel`, `Domain`, -`Route`, `AllDomains`, and `HardDomains` only when maintaining legacy -simulations during the breaking migration. `ModelMapping` is not exported, so -that compatibility code must spell it `PlantSimEngine.ModelMapping(...)`. +For new multiscale, multi-plant, soil, scene, or microclimate work, use the +unified scene/object API. Historical `ModelMapping` and `MultiScaleModel` +simulations remain available for released-code migration. `ModelMapping` is +not exported, so compatibility code must spell it +`PlantSimEngine.ModelMapping(...)`. ## First Steps diff --git a/src/PlantSimEngine.jl b/src/PlantSimEngine.jl index b947de218..5e846d60b 100644 --- a/src/PlantSimEngine.jl +++ b/src/PlantSimEngine.jl @@ -111,9 +111,6 @@ include("time/runtime/output_export.jl") include("time/runtime/meteo_sampling.jl") include("time/runtime/environment_backends.jl") -# Domain-aware simulation scaffolding: -include("domains/domain_simulation.jl") - # Simulation: include("run.jl") diff --git a/src/dependencies/hard_dependencies.jl b/src/dependencies/hard_dependencies.jl index ccb7342d2..d0395eab2 100644 --- a/src/dependencies/hard_dependencies.jl +++ b/src/dependencies/hard_dependencies.jl @@ -5,10 +5,6 @@ Compute the hard dependencies between models. """ -abstract type AbstractDomainDependencySelector end - -_is_domain_dependency_selector(x) = x isa AbstractDomainDependencySelector - function _normalize_hard_dependency_scales(scales, process::Symbol, dependency_process::Symbol) if scales isa Symbol return [scales] @@ -50,7 +46,6 @@ function hard_dependencies(models; scale=nothing, verbose::Bool=true) length(level_1_dep) == 0 && continue # if there is no dependency we skip the iteration dep_graph[process].dependency = level_1_dep for (p, depend) in pairs(level_1_dep) # for each dependency of the model i. p=:leaf_rank; depend=pairs(level_1_dep)[p] - _is_domain_dependency_selector(depend) && continue # The dependency can be given as multiscale, e.g. `leaf_area=AbstractLeaf_AreaModel => [m.leaf_symbol],` # This means we should search this model in another scale. This is not done here, but after the call to this # function in the other method for `hard_dependencies` below. diff --git a/src/domains/domain_run_loops.jl b/src/domains/domain_run_loops.jl deleted file mode 100644 index bf08bcd02..000000000 --- a/src/domains/domain_run_loops.jl +++ /dev/null @@ -1,89 +0,0 @@ -function _run_single_status_domain_simulation!( - simulation::DomainSimulation, - constants, - nsteps::Int -) - run_order = _domain_run_order(simulation) - for i in 1:nsteps - for domain in run_order - _materialize_routes_for_domain!(simulation, domain, i) - _run_domain_models!(simulation, domain, constants, i) - _update_domain_environment_index!(simulation, domain) - end - for domain in run_order - domain.kind == :scene && continue - _domain_has_post_scene_work(simulation, domain) || continue - _materialize_routes_for_domain!(simulation, domain, i) - _run_domain_models!(simulation, domain, constants, i; phase=:post_scene) - _update_domain_environment_index!(simulation, domain) - end - end - return simulation -end - -function _run_staged_graph_domain_simulation!( - simulation::DomainSimulation, - object, - raw_meteo, - constants, - nsteps::Int; - check=true, - executor=SequentialEx(), - type_promotion=nothing, -) - run_order = _domain_run_order(simulation) - graph_runtimes = Dict{Symbol,DomainGraphRuntime}() - - for step in 1:nsteps - for scene_phase in (false, true) - for domain in run_order - (domain.kind == :scene) == scene_phase || continue - if _is_graph_domain(domain) - domain.kind == :scene && error( - "Scene domain `$(domain.name)` is MTG-backed. The MTG-domain runner currently supports ", - "single-status scene domains only." - ) - runtime = get(graph_runtimes, domain.name, nothing) - if isnothing(runtime) - domain_type_promotion = isnothing(type_promotion) ? _type_promotion(_domain_mapping(domain)) : type_promotion - runtime = _prepare_graph_domain_runtime!( - simulation, - domain, - object, - raw_meteo, - constants, - nsteps; - check=check, - executor=executor, - type_promotion=domain_type_promotion, - ) - graph_runtimes[domain.name] = runtime - end - _run_graph_domain_step!(simulation, domain, runtime, step, nsteps; check=check) - else - _materialize_routes_for_domain!(simulation, domain, step) - _run_domain_models!(simulation, domain, constants, step) - _update_domain_environment_index!(simulation, domain) - end - end - end - for domain in run_order - domain.kind == :scene && continue - _domain_has_post_scene_work(simulation, domain) || continue - if _is_graph_domain(domain) - runtime = graph_runtimes[domain.name] - _run_graph_domain_step!(simulation, domain, runtime, step, nsteps; check=check, phase=:post_scene) - else - _materialize_routes_for_domain!(simulation, domain, step) - _run_domain_models!(simulation, domain, constants, step; phase=:post_scene) - _update_domain_environment_index!(simulation, domain) - end - end - end - - for runtime in values(graph_runtimes) - _finalize_graph_domain_runtime!(runtime) - end - - return simulation -end diff --git a/src/domains/domain_scheduler.jl b/src/domains/domain_scheduler.jl deleted file mode 100644 index 3bdff2680..000000000 --- a/src/domains/domain_scheduler.jl +++ /dev/null @@ -1,167 +0,0 @@ -function _domain_order_edges(mapping::SimulationMapping, route_bindings=nothing) - edges = Dict(domain.name => Set{Symbol}() for domain in mapping.domains) - non_scene_domains = [domain.name for domain in mapping.domains if domain.kind != :scene] - scene_domains = [domain.name for domain in mapping.domains if domain.kind == :scene] - for source in non_scene_domains, target in scene_domains - source == target || push!(edges[source], target) - end - - if !isnothing(route_bindings) - for (i, route) in enumerate(mapping.routes) - target = route.to.domain - for producer in route_bindings[i] - producer.domain == target && continue - push!(edges[producer.domain], target) - end - end - end - - return edges -end - -function _domain_run_order(mapping::SimulationMapping, route_bindings=nothing) - domains_by_name = Dict(domain.name => domain for domain in mapping.domains) - declaration_index = Dict(domain.name => i for (i, domain) in enumerate(mapping.domains)) - edges = _domain_order_edges(mapping, route_bindings) - indegree = Dict(domain.name => 0 for domain in mapping.domains) - for targets in values(edges), target in targets - indegree[target] = get(indegree, target, 0) + 1 - end - - ready = sort!( - [name for (name, degree) in indegree if degree == 0]; - by=name -> declaration_index[name], - ) - ordered = Symbol[] - while !isempty(ready) - current = popfirst!(ready) - push!(ordered, current) - for target in sort!(collect(edges[current]); by=name -> declaration_index[name]) - indegree[target] -= 1 - if indegree[target] == 0 - push!(ready, target) - sort!(ready; by=name -> declaration_index[name]) - end - end - end - - if length(ordered) != length(mapping.domains) - cyclic = sort!( - [name for (name, degree) in indegree if degree > 0]; - by=name -> declaration_index[name], - ) - error( - "Cyclic domain run-order constraints detected among domains: ", - join((":" * string(name) for name in cyclic), ", "), - ". Check route sources/targets and `kind=:scene` phase constraints." - ) - end - - return [domains_by_name[name] for name in ordered] -end - -function _domain_run_order(simulation::DomainSimulation) - return _domain_run_order(simulation.mapping, simulation.route_bindings) -end - -function _domain_node_due(simulation::DomainSimulation, domain::Domain, node::SoftDependencyNode, step::Int) - key = DomainModelKey(domain.name, node.scale, node.process) - clock = simulation.model_clocks[key] - return _should_run_at_time(clock, float(step)) -end - -function _hard_domain_dependency_keys(simulation::DomainSimulation) - keys = Set{DomainModelKey}() - for producers in values(simulation.hard_domain_dependency_bindings) - union!(keys, producers) - end - return keys -end - -function _is_hard_domain_dependency(simulation::DomainSimulation, key::DomainModelKey) - key in _hard_domain_dependency_keys(simulation) -end - -function _has_hard_domain_parent(simulation::DomainSimulation, domain::Domain, node::SoftDependencyNode) - AbstractTrees.isroot(node) && return false - hard_keys = _hard_domain_dependency_keys(simulation) - for parent in node.parent - parent_key = DomainModelKey(domain.name, parent.scale, parent.process) - parent_key in hard_keys && return true - end - return false -end - -function _phase_allows_hard_parent(phase::Symbol, has_hard_parent::Bool) - phase == :normal && return !has_hard_parent - phase == :post_scene && return has_hard_parent - error("Unknown domain scheduling phase `$(phase)`.") -end - -function _should_visit_domain_node( - simulation::DomainSimulation, - domain::Domain, - node::SoftDependencyNode; - phase::Symbol, -) - key = DomainModelKey(domain.name, node.scale, node.process) - _is_hard_domain_dependency(simulation, key) && return false - has_hard_parent = _has_hard_domain_parent(simulation, domain, node) - return _phase_allows_hard_parent(phase, has_hard_parent) -end - -function _has_hard_domain_parent(simulation::DomainSimulation, domain::Domain, key::DomainModelKey) - node = try - _find_dependency_node(simulation.dependency_graphs[domain.name], key) - catch - return false - end - return _has_hard_domain_parent(simulation, domain, node) -end - -function _has_domain_soft_node(simulation::DomainSimulation, domain::Domain, key::DomainModelKey) - try - _find_dependency_node(simulation.dependency_graphs[domain.name], key) - return true - catch - return false - end -end - -function _should_publish_domain_key( - simulation::DomainSimulation, - domain::Domain, - key::DomainModelKey; - phase::Symbol, -) - _has_domain_soft_node(simulation, domain, key) || return false - _is_hard_domain_dependency(simulation, key) && return false - has_hard_parent = _has_hard_domain_parent(simulation, domain, key) - return _phase_allows_hard_parent(phase, has_hard_parent) -end - -function _domain_has_post_scene_work(simulation::DomainSimulation, domain::Domain) - for key in keys(simulation.model_specs) - key.domain == domain.name || continue - _is_hard_domain_dependency(simulation, key) && continue - _has_hard_domain_parent(simulation, domain, key) && return true - end - return false -end - -function _domain_parents_ready( - simulation::DomainSimulation, - domain::Domain, - node::SoftDependencyNode, - step::Int, - ran::Set{DomainModelKey} -) - AbstractTrees.isroot(node) && return true - for parent in node.parent - _domain_node_due(simulation, domain, parent, step) || continue - parent_key = DomainModelKey(domain.name, parent.scale, parent.process) - _is_hard_domain_dependency(simulation, parent_key) && continue - parent_key in ran || return false - end - return true -end diff --git a/src/domains/domain_simulation.jl b/src/domains/domain_simulation.jl deleted file mode 100644 index 0a65d8f3e..000000000 --- a/src/domains/domain_simulation.jl +++ /dev/null @@ -1,1302 +0,0 @@ -""" - Domain(name, mapping; kind=:generic, selector=nothing) - Domain(name; kind, mapping, selector=nothing) - -Reusable model domain used by [`SimulationMapping`](@ref). - -Domains can wrap either a single-status `ModelMapping` or a scale-keyed -multiscale `ModelMapping` backed by an MTG subtree selected with `selector`. -The domain identity is kept alongside scale/process identity so multi-plant, -soil, scene, and environment domains can be compiled into one simulation -without renaming scales. -""" -struct Domain{M,S} - name::Symbol - kind::Symbol - mapping::M - selector::S -end - -function _normalize_domain_mapping(mapping) - mapping isa ModelMapping && return mapping - if mapping isa Tuple - status_index = findlast(x -> x isa Status, mapping) - if !isnothing(status_index) - status_index == length(mapping) || error( - "Raw tuple domain mappings may contain a positional Status only as the last element." - ) - return ModelMapping(mapping[begin:(end - 1)]...; status=last(mapping)) - end - return ModelMapping(mapping...) - end - return ModelMapping(mapping) -end - -function Domain(name::Union{Symbol,AbstractString}, mapping; kind::Union{Symbol,AbstractString}=:generic, selector=nothing) - normalized_mapping = copy(_normalize_domain_mapping(mapping)) - return Domain(Symbol(name), Symbol(kind), normalized_mapping, selector) -end - -function Domain(name::Union{Symbol,AbstractString}; kind::Union{Symbol,AbstractString}=:generic, mapping, selector=nothing) - return Domain(name, mapping; kind=kind, selector=selector) -end - -_domain_mapping(domain::Domain) = _normalize_domain_mapping(domain.mapping) - -""" - DomainModelKey(domain, scale, process) - -Stable key for one model process inside one domain and scale. -""" -struct DomainModelKey - domain::Symbol - scale::Symbol - process::Symbol -end - -Base.show(io::IO, key::DomainModelKey) = print(io, key.domain, "/", key.scale, "/", key.process) - -""" - AllDomains(; kind=nothing, domain=nothing, scale=nothing, process=nothing, var=nothing, policy=HoldLast()) - AllDomains(process; kwargs...) - -Value selector for scene-level models that intentionally consume output streams -from matching models in several domains. - -`policy` controls how producer streams are sampled when the consumer runs at a -different rate. `AllDomains` does not provide hard-dependency call-stack -control; use [`HardDomains`](@ref) when the parent model must manually run -the selected models. -""" -struct AllDomains{P<:SchedulePolicy} <: AbstractDomainDependencySelector - kind::Union{Nothing,Symbol} - domain::Union{Nothing,Symbol} - scale::Union{Nothing,Symbol} - process::Union{Nothing,Symbol} - var::Union{Nothing,Symbol} - policy::P -end - -function AllDomains(; kind=nothing, domain=nothing, scale=nothing, process=nothing, var=nothing, policy::SchedulePolicy=HoldLast()) - return AllDomains( - isnothing(kind) ? nothing : Symbol(kind), - isnothing(domain) ? nothing : Symbol(domain), - isnothing(scale) ? nothing : Symbol(scale), - isnothing(process) ? nothing : Symbol(process), - isnothing(var) ? nothing : Symbol(var), - policy, - ) -end - -AllDomains(process::Union{Symbol,AbstractString}; kwargs...) = AllDomains(; process=Symbol(process), kwargs...) - -""" - HardDomains(; kind=nothing, domain=nothing, scale=nothing, process=nothing) - HardDomains(process; kwargs...) - -Selector for models that are cross-domain hard dependencies. A model declaring -`dep(model) = (; name=HardDomains(...))` -can retrieve executable targets with [`dependency_targets`](@ref) and manually -execute them with [`run_target!`](@ref). -""" -struct HardDomains <: AbstractDomainDependencySelector - kind::Union{Nothing,Symbol} - domain::Union{Nothing,Symbol} - scale::Union{Nothing,Symbol} - process::Union{Nothing,Symbol} -end - -function HardDomains(; kind=nothing, domain=nothing, scale=nothing, process=nothing) - return HardDomains( - isnothing(kind) ? nothing : Symbol(kind), - isnothing(domain) ? nothing : Symbol(domain), - isnothing(scale) ? nothing : Symbol(scale), - isnothing(process) ? nothing : Symbol(process), - ) -end - -HardDomains(process::Union{Symbol,AbstractString}; kwargs...) = HardDomains(; process=Symbol(process), kwargs...) - -function _hard_domains_from_call_selector(selector::AbstractObjectMultiplicity) - c = criteria(selector) - unsupported = (:species, :name, :var, :relation, :within, :policy, :window) - any(key -> haskey(c, key) && !isnothing(getproperty(c, key)), unsupported) && error( - "Current `Calls(...)` hard-domain bridge only supports `kind`, `domain`, `scale`, and `process` selectors. ", - "Unsupported selector criteria: $(filter(key -> haskey(c, key), unsupported))." - ) - return HardDomains( - kind=haskey(c, :kind) ? c.kind : nothing, - domain=haskey(c, :domain) ? c.domain : nothing, - scale=haskey(c, :scale) ? c.scale : nothing, - process=haskey(c, :process) ? c.process : nothing, - ) -end - -function _model_spec_dependency_selector(dep_name::Symbol, selector::Call) - return _hard_domains_from_call_selector(selector.selector) -end - -function _model_spec_dependency_selector(dep_name::Symbol, selector::AbstractObjectMultiplicity) - return _hard_domains_from_call_selector(selector) -end - -function _push_selector_term!(terms::Vector{String}, name::Symbol, value) - isnothing(value) || push!(terms, "$(name)=:$(value)") - return terms -end - -function _policy_display(policy::SchedulePolicy) - return string(nameof(typeof(policy)), "()") -end - -function Base.show(io::IO, selector::AllDomains) - terms = String[] - _push_selector_term!(terms, :kind, selector.kind) - _push_selector_term!(terms, :domain, selector.domain) - _push_selector_term!(terms, :scale, selector.scale) - _push_selector_term!(terms, :process, selector.process) - _push_selector_term!(terms, :var, selector.var) - selector.policy isa HoldLast || push!(terms, "policy=$(_policy_display(selector.policy))") - print(io, "AllDomains(", join(terms, ", "), ")") -end - -function Base.show(io::IO, selector::HardDomains) - terms = String[] - _push_selector_term!(terms, :kind, selector.kind) - _push_selector_term!(terms, :domain, selector.domain) - _push_selector_term!(terms, :scale, selector.scale) - _push_selector_term!(terms, :process, selector.process) - print(io, "HardDomains(", join(terms, ", "), ")") -end - -include("routes.jl") - -""" - SimulationMapping(domains...) - -Top-level composition of reusable domains. This is the incremental entry point -for multi-plant/soil/scene simulations. -""" -struct SimulationMapping - domains::Vector{Domain} - routes::Vector{Route} -end - -function SimulationMapping(domains::Domain...; routes=()) - names = [domain.name for domain in domains] - duplicates = unique(filter(name -> count(==(name), names) > 1, names)) - isempty(duplicates) || error("Duplicate domain name(s) in SimulationMapping: $(join(duplicates, ", ")).") - normalized_routes = Route[] - for route in routes - route isa Route || error("SimulationMapping routes must be `Route` objects, got `$(typeof(route))`.") - push!(normalized_routes, route) - end - return SimulationMapping(collect(domains), normalized_routes) -end - -""" - DomainRunContext - -Runtime context passed as `extra` to models run by [`SimulationMapping`](@ref). -Scene models can call [`dependency_values`](@ref) to consume resolved -`AllDomains` value dependencies, or [`dependency_targets`](@ref) to manually -run resolved `HardDomains` dependencies. -""" -struct DomainRunContext - simulation - consumer::DomainModelKey - step::Int - clock::ClockSpec - constants -end - -struct ModelTarget - simulation - key - node - step::Int - model - models - status - meteo - constants - extra -end - -struct DomainNodeValues{I,T<:AbstractVector} - ids::I - values::T -end - -DomainNodeValues(values::AbstractVector) = DomainNodeValues(nothing, values) - -""" - model_target(model, models, status, meteo=nothing, constants=nothing, extra=nothing; kwargs...) - -Build one executable model target. This is the low-level representation used by -hard-domain dependencies and can also be used by ordinary same-status hard -dependencies. -""" -function model_target( - model, - models, - status, - meteo=nothing, - constants=nothing, - extra=nothing; - simulation=nothing, - key=nothing, - node=nothing, - step::Integer=1, -) - return ModelTarget(simulation, key, node, Int(step), model, models, status, meteo, constants, extra) -end - -function dependency_targets( - models, - status, - dependency_name::Symbol; - meteo=nothing, - constants=nothing, - extra=nothing, -) - hasproperty(models, dependency_name) || error( - "No model named `$(dependency_name)` is available in `models`. ", - "Declare the hard dependency with `dep(model)` and include a model for that process in the mapping." - ) - return ModelTarget[ - model_target(getproperty(models, dependency_name), models, status, meteo, constants, extra), - ] -end - -dependency_targets(models, status, dependency_name::AbstractString; kwargs...) = - dependency_targets(models, status, Symbol(dependency_name); kwargs...) - -dependency_target(args...; kwargs...) = only(dependency_targets(args...; kwargs...)) - -struct DomainGraphState{S<:AbstractVector} - simulations::S -end - -struct DomainGraphRuntime - state::DomainGraphState - meteo - constants - effective_multirate::Bool - timeline::TimelineContext - meteo_sampler - executor -end - -""" - DomainSimulation - -Result and mutable runtime state for a [`SimulationMapping`](@ref) run. -""" -mutable struct DomainSimulation - mapping::SimulationMapping - environment::AbstractEnvironmentBackend - model_specs::Dict{DomainModelKey,ModelSpec} - model_clocks::Dict{DomainModelKey,ClockSpec} - dependency_graphs::Dict{Symbol,DependencyGraph} - dependency_bindings::Dict{Tuple{DomainModelKey,Symbol},Vector{DomainModelKey}} - dependency_variables::Dict{Tuple{DomainModelKey,Symbol},Union{Nothing,Symbol}} - dependency_policies::Dict{Tuple{DomainModelKey,Symbol},SchedulePolicy} - hard_domain_dependency_bindings::Dict{Tuple{DomainModelKey,Symbol},Vector{DomainModelKey}} - route_bindings::Vector{Vector{DomainModelKey}} - route_clocks::Vector{ClockSpec} - streams::Dict{Tuple{DomainModelKey,Symbol},Vector{Pair{Int,Any}}} - outputs::Dict{Tuple{DomainModelKey,Symbol},Vector{Any}} - domain_states::Dict{Symbol,Any} - timeline::TimelineContext -end - -outputs(simulation::DomainSimulation) = simulation.outputs - -function status(state::DomainGraphState) - statuses = Dict{Symbol,Vector{Status}}() - for graph_simulation in state.simulations - for (scale, statuses_at_scale) in status(graph_simulation) - append!(get!(statuses, scale, Status[]), statuses_at_scale) - end - end - return statuses -end - -function _global_scale_statuses(simulation::DomainSimulation, scale::Symbol) - statuses = Status[] - for state in values(simulation.domain_states) - if state isa DomainGraphState - graph_statuses = status(state) - haskey(graph_statuses, scale) || continue - append!(statuses, graph_statuses[scale]) - elseif state isa ModelMapping{SingleScale} && scale == :Default - push!(statuses, status(state)) - end - end - return statuses -end - -function _domain_declares_scale(domain::Domain, scale::Symbol) - mapping = _domain_mapping(domain) - mapping isa ModelMapping{MultiScale} || return false - return any(entry -> first(entry) == scale, pairs(mapping)) -end - -function _simulation_declares_graph_scale(simulation::DomainSimulation, scale::Symbol) - return any(domain -> _domain_declares_scale(domain, scale), simulation.mapping.domains) -end - -function status(simulation::DomainSimulation, domain_name::Symbol) - state = get(simulation.domain_states, domain_name, nothing) - if isnothing(state) - statuses = _global_scale_statuses(simulation, domain_name) - isempty(statuses) && _simulation_declares_graph_scale(simulation, domain_name) && return statuses - isempty(statuses) && error( - "No domain named `$(domain_name)` and no global scale `$(domain_name)` exists in this DomainSimulation." - ) - return statuses - end - state isa ModelMapping{SingleScale} || error( - "Domain `$(domain_name)` is MTG-backed. Use `status(simulation, domain, scale)` to inspect statuses at one scale." - ) - return status(state) -end - -status(simulation::DomainSimulation, domain_name::AbstractString) = status(simulation, Symbol(domain_name)) - -function status(simulation::DomainSimulation, domain_name::Symbol, scale::Symbol) - state = get(simulation.domain_states, domain_name, nothing) - isnothing(state) && error("Domain `$(domain_name)` has no runtime state.") - state isa DomainGraphState || error( - "Domain `$(domain_name)` is single-status. Use `status(simulation, domain)` without a scale." - ) - graph_statuses = status(state) - haskey(graph_statuses, scale) && return graph_statuses[scale] - domain = _domain_for_name(simulation.mapping, domain_name) - _domain_declares_scale(domain, scale) && return Status[] - error("Domain `$(domain_name)` has no runtime statuses and no declared mapping at scale `$(scale)`.") -end - -status(simulation::DomainSimulation, domain_name::AbstractString, scale::Union{Symbol,AbstractString}) = - status(simulation, Symbol(domain_name), Symbol(scale)) - -function _domain_entries(domain::Domain) - mapping = _domain_mapping(domain) - return collect(pairs(mapping)) -end - -function _validate_initial_domain_mapping(domain::Domain) - mapping = _domain_mapping(domain) - mapping isa ModelMapping{SingleScale} || error( - "Domain `$(domain.name)` uses a multiscale ModelMapping. ", - "The initial SimulationMapping runner only supports single-status domains; ", - "the MTG/domain runner will handle nested multiscale plant domains." - ) - return nothing -end - -_is_single_status_domain(domain::Domain) = _domain_mapping(domain) isa ModelMapping{SingleScale} -_is_graph_domain(domain::Domain) = _domain_mapping(domain) isa ModelMapping{MultiScale} - -function _validate_staged_domain_mapping(domain::Domain) - mapping = _domain_mapping(domain) - mapping isa ModelMapping || error("Domain `$(domain.name)` does not wrap a valid ModelMapping.") - if mapping isa ModelMapping{SingleScale} && !isnothing(domain.selector) - error( - "Domain `$(domain.name)` has a selector but uses a single-status ModelMapping. ", - "Selectors are only supported for MTG-backed, scale-keyed domain mappings in the MTG-domain runner." - ) - end - return nothing -end - -function _collect_matching_nodes(root, predicate) - matches = Any[] - MultiScaleTreeGraph.traverse!(root) do node - predicate(node) && push!(matches, node) - end - return matches -end - -function _is_ancestor_node(ancestor, node) - current = parent(node) - while !isnothing(current) - current === ancestor && return true - current = parent(current) - end - return false -end - -function _validate_domain_graph_roots!(domain::Domain, roots) - for i in eachindex(roots), j in (i + 1):lastindex(roots) - left = roots[i] - right = roots[j] - if _is_ancestor_node(left, right) || _is_ancestor_node(right, left) - error( - "Selector for MTG-backed domain `$(domain.name)` matched overlapping MTG roots ", - "`$(node_id(left))` and `$(node_id(right))`. ", - "Select non-overlapping domain roots, for example plant roots rather than both plants and organs." - ) - end - end - return roots -end - -function _domain_graph_roots(object, domain::Domain) - selector = domain.selector - isnothing(selector) && return Any[object] - selector isa MultiScaleTreeGraph.Node && return Any[selector] - if selector isa Symbol - matches = _collect_matching_nodes(object, node -> symbol(node) == selector) - elseif selector isa Function - matches = _collect_matching_nodes(object, selector) - else - error( - "Unsupported selector for MTG-backed domain `$(domain.name)`: `$(typeof(selector))`. ", - "Use `selector=nothing`, a `MultiScaleTreeGraph.Node`, a scale `Symbol`, or a predicate function." - ) - end - isempty(matches) && error( - "Selector for MTG-backed domain `$(domain.name)` matched $(length(matches)) nodes. ", - "Use a selector that identifies at least one domain root for this MTG-domain runner." - ) - return _validate_domain_graph_roots!(domain, matches) -end - -function _domain_model_specs(domain::Domain) - specs = Dict{DomainModelKey,ModelSpec}() - for (scale, declarations) in _domain_entries(domain) - for (process, spec) in pairs(parse_model_specs(declarations)) - specs[DomainModelKey(domain.name, scale, process)] = spec - end - end - return specs -end - -function _domain_model_clocks(specs::Dict{DomainModelKey,ModelSpec}, timeline::TimelineContext) - clocks = Dict{DomainModelKey,ClockSpec}() - for (key, spec) in specs - clocks[key] = _model_clock(spec, model_(spec), timeline) - end - return clocks -end - -function _domain_dependency_graphs(mapping::SimulationMapping) - graphs = Dict{Symbol,DependencyGraph}() - for domain in mapping.domains - graph = dep(_domain_mapping(domain)) - if !isempty(graph.not_found) - error("Domain `$(domain.name)` has unresolved dependencies: $(graph.not_found).") - end - graphs[domain.name] = graph - end - return graphs -end - -function _domain_for_name(mapping::SimulationMapping, name::Symbol) - for domain in mapping.domains - domain.name == name && return domain - end - error("Unknown domain `$(name)`.") -end - -function _matches(selector::AllDomains, domain::Domain, key::DomainModelKey, spec::ModelSpec; check_var=true) - isnothing(selector.kind) || selector.kind == domain.kind || return false - isnothing(selector.domain) || selector.domain == domain.name || return false - isnothing(selector.scale) || selector.scale == key.scale || return false - isnothing(selector.process) || selector.process == key.process || return false - !check_var || isnothing(selector.var) || selector.var in keys(outputs_(spec)) || return false - return true -end - -function _matches(selector::HardDomains, domain::Domain, key::DomainModelKey, spec::ModelSpec; check_var=false) - isnothing(selector.kind) || selector.kind == domain.kind || return false - isnothing(selector.domain) || selector.domain == domain.name || return false - isnothing(selector.scale) || selector.scale == key.scale || return false - isnothing(selector.process) || selector.process == key.process || return false - return true -end - -function _format_symbol_keys(keys_iter) - syms = sort!(collect(Symbol.(keys_iter)); by=string) - isempty(syms) && return "none" - return join((":" * string(sym) for sym in syms), ", ") -end - -function _domain_candidate_rows( - mapping::SimulationMapping, - specs::Dict{DomainModelKey,ModelSpec}; - selector=nothing, - check_var=true, - max_rows=12, -) - rows = String[] - keys_by_domain = _keys_by_domain(specs) - for domain in mapping.domains - producer_keys = sort!( - copy(get(keys_by_domain, domain.name, DomainModelKey[])); - by=key -> (string(key.scale), string(key.process)), - ) - for producer_key in producer_keys - producer_spec = specs[producer_key] - if selector isa Union{AllDomains,HardDomains} - _matches(selector, domain, producer_key, producer_spec; check_var=check_var) || continue - end - push!( - rows, - "$(producer_key) outputs=($(_format_symbol_keys(keys(outputs_(producer_spec)))))", - ) - end - end - - length(rows) <= max_rows && return join(rows, "\n ") - shown = rows[1:max_rows] - push!(shown, "... and $(length(rows) - max_rows) more") - return join(shown, "\n ") -end - -function _selector_match_error( - mapping::SimulationMapping, - specs::Dict{DomainModelKey,ModelSpec}, - selector::Union{AllDomains,HardDomains}; - context::String, -) - candidates = _domain_candidate_rows(mapping, specs) - message = string( - context, - " did not match any model for selector `", - selector, - "`. Suggested fixes: check `kind`, `domain`, `scale`, and `process`; ", - "if `var` is set, use one of the producer outputs listed below.\n", - "Available producers:\n ", - isempty(candidates) ? "none" : candidates, - ) - if selector isa AllDomains && !isnothing(selector.var) - near_matches = _domain_candidate_rows(mapping, specs; selector=selector, check_var=false) - if !isempty(near_matches) - message = string( - message, - "\nModels matching all selector fields except `var=:", - selector.var, - "`:\n ", - near_matches, - ) - end - end - return message -end - -function _resolve_domain_dependencies(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) - bindings = Dict{Tuple{DomainModelKey,Symbol},Vector{DomainModelKey}}() - policies = Dict{Tuple{DomainModelKey,Symbol},SchedulePolicy}() - variables = Dict{Tuple{DomainModelKey,Symbol},Union{Nothing,Symbol}}() - keys_by_domain = _keys_by_domain(specs) - - for (consumer_key, spec) in specs - model_deps = dep(spec) - for (dep_name, selector) in pairs(model_deps) - selector isa AllDomains || continue - resolved = DomainModelKey[] - for domain in mapping.domains - for producer_key in get(keys_by_domain, domain.name, DomainModelKey[]) - producer_key == consumer_key && continue - producer_spec = specs[producer_key] - _matches(selector, domain, producer_key, producer_spec) && push!(resolved, producer_key) - end - end - isempty(resolved) && error( - _selector_match_error( - mapping, - specs, - selector; - context="Domain dependency `$(dep_name)` for consumer `$(consumer_key)`", - ) - ) - binding_key = (consumer_key, dep_name) - bindings[binding_key] = resolved - policies[binding_key] = selector.policy - variables[binding_key] = selector.var - end - end - - return bindings, policies, variables -end - -function _resolve_hard_domain_dependencies(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) - bindings = Dict{Tuple{DomainModelKey,Symbol},Vector{DomainModelKey}}() - keys_by_domain = _keys_by_domain(specs) - - for (consumer_key, spec) in specs - model_deps = dep(spec) - for (dep_name, selector) in pairs(model_deps) - selector isa HardDomains || continue - resolved = DomainModelKey[] - for domain in mapping.domains - for producer_key in get(keys_by_domain, domain.name, DomainModelKey[]) - producer_key == consumer_key && continue - producer_spec = specs[producer_key] - _matches(selector, domain, producer_key, producer_spec) && push!(resolved, producer_key) - end - end - isempty(resolved) && error( - _selector_match_error( - mapping, - specs, - selector; - context="Hard domain dependency `$(dep_name)` for consumer `$(consumer_key)`", - ) - ) - bindings[(consumer_key, dep_name)] = resolved - end - end - - return bindings -end - -function _keys_by_domain(specs::Dict{DomainModelKey,ModelSpec}) - keys_by_domain = Dict{Symbol,Vector{DomainModelKey}}() - for key in keys(specs) - push!(get!(keys_by_domain, key.domain, DomainModelKey[]), key) - end - return keys_by_domain -end - -include("route_runtime.jl") -include("environment_bridge.jl") -include("output_publisher.jl") -include("domain_scheduler.jl") -include("graph_domain_runner.jl") -include("domain_run_loops.jl") - -function _build_domain_simulation(mapping::SimulationMapping, meteo; staged_graph_domains=false) - environment = environment_backend(meteo) - _validate_meteo_duration(environment) - timeline = _timeline_context(environment) - foreach(staged_graph_domains ? _validate_staged_domain_mapping : _validate_initial_domain_mapping, mapping.domains) - specs = Dict{DomainModelKey,ModelSpec}() - for domain in mapping.domains - merge!(specs, _domain_model_specs(domain)) - end - mapping = _add_input_routes(mapping, specs) - mapping = _add_route_target_status_defaults(mapping, specs) - - model_clocks = _domain_model_clocks(specs, timeline) - graphs = _domain_dependency_graphs(mapping) - bindings, policies, variables = _resolve_domain_dependencies(mapping, specs) - hard_domain_bindings = _resolve_hard_domain_dependencies(mapping, specs) - route_bindings = _resolve_route_bindings(mapping, specs) - _validate_route_targets(mapping, mapping.routes, specs; staged_graph_domains=staged_graph_domains) - staged_graph_domains && _validate_graph_route_order(mapping, mapping.routes, route_bindings) - route_clocks = _route_clocks(mapping.routes, specs, model_clocks) - validate_meteo_inputs(_domain_model_specs_by_scale(specs), environment) - return DomainSimulation( - mapping, - environment, - specs, - model_clocks, - graphs, - bindings, - variables, - policies, - hard_domain_bindings, - route_bindings, - route_clocks, - Dict{Tuple{DomainModelKey,Symbol},Vector{Pair{Int,Any}}}(), - Dict{Tuple{DomainModelKey,Symbol},Vector{Any}}(), - Dict{Symbol,Any}(domain.name => _domain_mapping(domain) for domain in mapping.domains if _domain_mapping(domain) isa ModelMapping{SingleScale}), - timeline, - ) -end - -function _domain_model_specs_by_scale(specs::Dict{DomainModelKey,ModelSpec}) - by_scale = Dict{Symbol,Dict{Symbol,ModelSpec}}() - for (key, spec) in specs - scale_key = Symbol(string(key.domain), "/", string(key.scale)) - scale_specs = get!(by_scale, scale_key, Dict{Symbol,ModelSpec}()) - scale_specs[key.process] = spec - end - return by_scale -end - -function _resolve_stream_values(simulation::DomainSimulation, producer::DomainModelKey, var::Symbol, steps) - stream = get(simulation.streams, (producer, var), Pair{Int,Any}[]) - vals = Any[] - for (step, value) in stream - step in steps && push!(vals, value) - end - return vals -end - -function _domain_node_values(values::Vector{Any}) - return DomainNodeValues[value for value in values if value isa DomainNodeValues] -end - -function _domain_node_values_have_ids(values::Vector{DomainNodeValues}) - return all(value -> !isnothing(value.ids) && length(value.ids) == length(value.values), values) -end - -function _combine_domain_node_values_by_position(values::Vector{DomainNodeValues}, reducer) - lengths = unique(length(value.values) for value in values) - length(lengths) == 1 || error( - "Cannot aggregate graph-domain values with changing vector lengths because node ids are unavailable. ", - "Publish graph-domain outputs through PlantSimEngine's graph-domain runtime so values can be aligned by node id." - ) - n = only(lengths) - return DomainNodeValues(Any[reducer(Any[value.values[i] for value in values]) for i in 1:n]) -end - -function _combine_domain_node_values_by_id(values::Vector{DomainNodeValues}, reducer) - grouped = Dict{Int,Vector{Any}}() - order = Int[] - for node_values in values - for (id, value) in zip(node_values.ids, node_values.values) - bucket = get!(grouped, id) do - push!(order, id) - Any[] - end - push!(bucket, value) - end - end - return DomainNodeValues(order, Any[reducer(grouped[id]) for id in order]) -end - -function _combine_domain_node_values(values::Vector{Any}, reducer) - node_values = _domain_node_values(values) - _domain_node_values_have_ids(node_values) && return _combine_domain_node_values_by_id(node_values, reducer) - return _combine_domain_node_values_by_position(node_values, reducer) -end - -function _apply_dependency_policy(values::Vector{Any}, policy::SchedulePolicy) - isempty(values) && return nothing - if policy isa HoldLast || policy isa Interpolate - return last(values) - elseif policy isa Integrate - if any(value -> value isa DomainNodeValues, values) - return _combine_domain_node_values(values, sum) - end - return sum(values) - elseif policy isa Aggregate - if any(value -> value isa DomainNodeValues, values) - return _combine_domain_node_values(values, vals -> sum(vals) / length(vals)) - end - return sum(values) / length(values) - end - return last(values) -end - -_public_dependency_value(value) = value isa DomainNodeValues ? value.values : value - -function _push_public_dependency_value!(dest::Vector{Any}, value, flatten::Bool) - isnothing(value) && return dest - if flatten && value isa DomainNodeValues - append!(dest, value.values) - else - push!(dest, _public_dependency_value(value)) - end - return dest -end - -function _window_steps(step::Int, clock::ClockSpec) - start = _window_start_for_clock(clock, float(step)) - return ceil(Int, start):step -end - -""" - dependency_values(ctx, dependency_name, variable) - dependency_values(ctx, dependency_name) - -Return one value per resolved producer for a scene-level `AllDomains` -dependency, applying the selector's temporal policy over the consumer window. -When `AllDomains(...; var=:x)` declares a variable, the two-argument form uses -that variable. -""" -function dependency_values(ctx::DomainRunContext, dependency_name::Symbol, variable=nothing; flatten=false) - sim = ctx.simulation - binding_key = (ctx.consumer, dependency_name) - producers = get(sim.dependency_bindings, binding_key, nothing) - isnothing(producers) && error( - "No resolved dependency named `$(dependency_name)` for `$(ctx.consumer)`. ", - "Declare it with `dep(model) = (; $(dependency_name)=AllDomains(...))`." - ) - declared_var = sim.dependency_variables[binding_key] - resolved_var = isnothing(variable) ? declared_var : Symbol(variable) - isnothing(resolved_var) && error( - "No variable was provided for domain dependency `$(dependency_name)` in `$(ctx.consumer)`. ", - "Call `dependency_values(extra, :$(dependency_name), :variable)` or declare ", - "`AllDomains(...; var=:variable)`." - ) - if !isnothing(declared_var) && !isnothing(variable) && Symbol(variable) != declared_var - error( - "Domain dependency `$(dependency_name)` in `$(ctx.consumer)` was declared with `var=:$(declared_var)`, ", - "but `dependency_values` was called for variable `$(Symbol(variable))`." - ) - end - for producer in producers - producer_spec = sim.model_specs[producer] - resolved_var in keys(outputs_(producer_spec)) || error( - "Domain dependency `$(dependency_name)` resolved producer `$(producer)`, ", - "but that producer does not output variable `$(resolved_var)`." - ) - end - policy = sim.dependency_policies[binding_key] - steps = _window_steps(ctx.step, ctx.clock) - values = Any[] - for producer in producers - value = _apply_dependency_policy(_resolve_stream_values(sim, producer, resolved_var, steps), policy) - _push_public_dependency_value!(values, value, flatten) - end - return values -end - -dependency_values(ctx::DomainRunContext, dependency_name::AbstractString, variable=nothing; flatten=false) = - dependency_values(ctx, Symbol(dependency_name), variable; flatten=flatten) - -function _find_dependency_node(node::SoftDependencyNode, key::DomainModelKey) - node.scale == key.scale && node.process == key.process && return node - for child in node.children - found = _find_dependency_node(child, key) - isnothing(found) || return found - end - return nothing -end - -function _find_dependency_node(graph::DependencyGraph, key::DomainModelKey) - for (_, root) in graph.roots - found = _find_dependency_node(root, key) - isnothing(found) || return found - end - error("No dependency node found for domain model `$(key)`.") -end - -function _single_status_dependency_targets(ctx::DomainRunContext, producer::DomainModelKey) - sim = ctx.simulation - domain = _domain_for_name(sim.mapping, producer.domain) - model_list = _single_scale_model_set_from_mapping(_domain_mapping(domain)) - node = _find_dependency_node(sim.dependency_graphs[producer.domain], producer) - st = status(model_list) - producer_context = DomainRunContext(sim, producer, ctx.step, sim.model_clocks[producer], ctx.constants) - return ModelTarget[ - ModelTarget( - sim, - producer, - node, - ctx.step, - node.value, - model_list.models, - st, - _dependency_target_meteo(sim, producer, st, ctx.step), - ctx.constants, - producer_context, - ), - ] -end - -function _graph_dependency_targets(ctx::DomainRunContext, producer::DomainModelKey) - sim = ctx.simulation - graph_state = sim.domain_states[producer.domain] - targets = ModelTarget[] - for graph_simulation in graph_state.simulations - graph_statuses = status(graph_simulation) - haskey(graph_statuses, producer.scale) || continue - node = _find_dependency_node(dep(graph_simulation), producer) - models_at_scale = get_models(graph_simulation)[producer.scale] - for st in graph_statuses[producer.scale] - push!( - targets, - ModelTarget( - sim, - producer, - node, - ctx.step, - node.value, - models_at_scale, - st, - _dependency_target_meteo(sim, producer, st, ctx.step), - ctx.constants, - graph_simulation, - ), - ) - end - end - return targets -end - -function _dependency_targets_for_producer(ctx::DomainRunContext, producer::DomainModelKey) - state = ctx.simulation.domain_states[producer.domain] - state isa ModelMapping{SingleScale} && return _single_status_dependency_targets(ctx, producer) - state isa DomainGraphState && return _graph_dependency_targets(ctx, producer) - error("Unsupported runtime state for domain `$(producer.domain)`: `$(typeof(state))`.") -end - -""" - dependency_targets(ctx, dependency_name) - -Return executable targets for a resolved `HardDomains` dependency. The parent -model controls when, how often, and in which order targets are executed by -calling [`run_target!`](@ref). -""" -function dependency_targets(ctx::DomainRunContext, dependency_name::Symbol) - sim = ctx.simulation - binding_key = (ctx.consumer, dependency_name) - producers = get(sim.hard_domain_dependency_bindings, binding_key, nothing) - isnothing(producers) && error( - "No hard-domain dependency named `$(dependency_name)` for `$(ctx.consumer)`. ", - "Declare it with `dep(model) = (; $(dependency_name)=HardDomains(...))`." - ) - targets = ModelTarget[] - for producer in producers - append!(targets, _dependency_targets_for_producer(ctx, producer)) - end - return targets -end - -dependency_targets(ctx::DomainRunContext, dependency_name::AbstractString) = - dependency_targets(ctx, Symbol(dependency_name)) - -""" - run_target!(target; meteo=target.meteo, constants=target.constants, extra=target.extra, publish=false) - -Run one executable model target. The call mutates the target's -status, just like a normal hard dependency call. It does not append to domain -streams or outputs unless `publish=true`. -""" -function run_target!( - target::ModelTarget; - meteo=target.meteo, - constants=target.constants, - extra=target.extra, - publish::Bool=false, -) - run!(target.model, target.models, target.status, meteo, constants, extra) - publish && _publish_target!(target) - return target.status -end - -function run_target!( - models, - status, - dependency_name::Symbol; - meteo=nothing, - constants=nothing, - extra=nothing, - publish::Bool=false, -) - target = dependency_target(models, status, dependency_name; meteo=meteo, constants=constants, extra=extra) - return run_target!(target; publish=publish) -end - -run_target!(models, status, dependency_name::AbstractString; kwargs...) = - run_target!(models, status, Symbol(dependency_name); kwargs...) - -""" - call_targets(extra, name) - call_target(extra, name) - -Return executable handles compiled from a `Calls(...)` declaration. -`call_target` requires exactly one matching handle. Execute handles with -[`run_call!`](@ref), optionally several times before publishing an accepted -state. -""" -call_targets(args...; kwargs...) = dependency_targets(args...; kwargs...) -call_target(args...; kwargs...) = dependency_target(args...; kwargs...) - -""" - run_call!(target; kwargs...) - run_call!(models, status, dependency_name; kwargs...) - -Unified scene/object spelling for manually executing a model call handle. -This currently delegates to `run_target!` while `ModelTarget` remains the -runtime carrier for hard-domain calls. -""" -run_call!(args...; kwargs...) = run_target!(args...; kwargs...) - -function _domain_context_for(simulation::DomainSimulation, domain::Domain, node::SoftDependencyNode, step::Int, constants=nothing) - key = DomainModelKey(domain.name, node.scale, node.process) - return DomainRunContext(simulation, key, step, simulation.model_clocks[key], constants) -end - -function _run_domain_node!( - simulation::DomainSimulation, - domain::Domain, - node::SoftDependencyNode, - model_list::SingleScaleModelSet, - constants, - step::Int, - ran::Set{DomainModelKey}; - phase::Symbol=:normal, -) - key = DomainModelKey(domain.name, node.scale, node.process) - if _domain_node_due(simulation, domain, node, step) && - _domain_parents_ready(simulation, domain, node, step, ran) && - _should_visit_domain_node(simulation, domain, node; phase=phase) && - !(key in ran) - ctx = _domain_context_for(simulation, domain, node, step, constants) - model_spec = simulation.model_specs[key] - meteo_for_model = _domain_environment_for_model( - simulation, - domain, - node, - model_spec, - status(model_list), - step, - ) - run!(node.value, model_list.models, status(model_list), meteo_for_model, constants, ctx) - _scatter_domain_environment_outputs!(simulation, domain, node, model_spec, status(model_list), step) - push!(ran, key) - _publish_domain_model_outputs!(simulation, domain, node, status(model_list), step) - for hard_child in node.hard_dependency - _scatter_domain_hard_dependency_environment_outputs!(simulation, domain, hard_child, status(model_list), step) - _publish_domain_hard_dependency_outputs!(simulation, domain, hard_child, status(model_list), step) - end - end - - for child in node.children - _run_domain_node!(simulation, domain, child, model_list, constants, step, ran; phase=phase) - end - return nothing -end - -function _run_domain_models!( - simulation::DomainSimulation, - domain::Domain, - constants, - step::Int; - phase::Symbol=:normal, -) - mapping = _domain_mapping(domain) - model_list = _single_scale_model_set_from_mapping(mapping) - ran = Set{DomainModelKey}() - for (_, root) in simulation.dependency_graphs[domain.name].roots - _run_domain_node!(simulation, domain, root, model_list, constants, step, ran; phase=phase) - end - return ran -end - -""" - run!(mapping::SimulationMapping, meteo=nothing, constants=PlantMeteo.Constants(); check=true) - -Run the initial domain-aware simulation path. This runner is deliberately -limited to single-status domains, but it schedules each model with its own -effective timestep. It is intended to make the multi-domain API executable -while the full MTG path is implemented. -""" -function run!( - mapping::SimulationMapping, - meteo=nothing, - constants=PlantMeteo.Constants(); - check=true -) - simulation = _build_domain_simulation(mapping, meteo) - nsteps = get_nsteps(simulation.environment) - return _run_single_status_domain_simulation!(simulation, constants, nsteps) -end - -""" - run!(mtg, mapping::SimulationMapping, meteo=nothing, constants=PlantMeteo.Constants(); ...) - -Run a multi-domain simulation where MTG-backed domains are selected from `mtg` -and executed with the existing `GraphSimulation` engine. - -The runner advances all domains one base timestep at a time. Domains whose -`kind` is not `:scene` run first in mapping order, then `:scene` domains run so -they can consume plant, soil, and graph-domain streams from the same timestep. -Routes into graph domains are supported for `OneToManyBroadcast()` when the -source domain runs earlier in the timestep. -""" -function run!( - object::MultiScaleTreeGraph.Node, - mapping::SimulationMapping, - meteo=nothing, - constants=PlantMeteo.Constants(); - nsteps=nothing, - check=true, - executor=SequentialEx(), - type_promotion=nothing, -) - simulation = _build_domain_simulation(mapping, meteo; staged_graph_domains=true) - raw_meteo = _raw_meteo_for_staged_graph_domains(simulation.environment) - isnothing(nsteps) && (nsteps = get_nsteps(simulation.environment)) - return _run_staged_graph_domain_simulation!( - simulation, - object, - raw_meteo, - constants, - nsteps; - check=check, - executor=executor, - type_promotion=type_promotion, - ) -end - -""" - explain_domains(mapping_or_simulation) - -Return structured rows describing domains in a simulation mapping. -""" -function explain_domains(mapping::SimulationMapping) - return [ - (domain=domain.name, kind=domain.kind, mapping=typeof(domain.mapping), selector=domain.selector) - for domain in mapping.domains - ] -end - -explain_domains(simulation::DomainSimulation) = explain_domains(simulation.mapping) - -function _domain_model_rows(mapping::SimulationMapping) - rows = NamedTuple[] - for domain in mapping.domains - for (scale, declarations) in _domain_entries(domain) - for (process, spec) in pairs(parse_model_specs(declarations)) - key = DomainModelKey(domain.name, scale, process) - push!(rows, ( - key=key, - domain=domain.name, - kind=domain.kind, - scale=scale, - process=process, - model=typeof(model_(spec)), - timestep=timestep(spec), - inputs=inputs_(spec), - outputs=outputs_(spec), - meteo_inputs=meteo_inputs_(spec), - meteo_outputs=meteo_outputs_(spec), - updates=updates(spec), - )) - end - end - end - return rows -end - -""" - explain_domain_models(mapping_or_simulation) - -Return structured rows for every model process inside every domain. -""" -explain_domain_models(mapping::SimulationMapping) = _domain_model_rows(mapping) -explain_domain_models(simulation::DomainSimulation) = explain_domain_models(simulation.mapping) - -""" - explain_domain_statuses(simulation) - -Return structured rows describing runtime status counts by domain and scale. -For graph domains, one row is returned per scale. For single-status domains, -the scale is `:Default`. -""" -function explain_domain_statuses(simulation::DomainSimulation) - rows = NamedTuple[] - for domain in simulation.mapping.domains - state = get(simulation.domain_states, domain.name, nothing) - if state isa DomainGraphState - graph_statuses = status(state) - declared_scales = Symbol[first(entry) for entry in _domain_entries(domain)] - runtime_scales = collect(keys(graph_statuses)) - for scale in sort!(unique!(vcat(declared_scales, runtime_scales)); by=string) - statuses_at_scale = get(graph_statuses, scale, Status[]) - push!(rows, ( - domain=domain.name, - kind=domain.kind, - scale=scale, - nstatuses=length(statuses_at_scale), - state=typeof(state), - )) - end - elseif state isa ModelMapping{SingleScale} - push!(rows, ( - domain=domain.name, - kind=domain.kind, - scale=:Default, - nstatuses=1, - state=typeof(state), - )) - end - end - return rows -end - -""" - explain_schedule(simulation) - -Return structured rows describing effective per-domain schedules. -""" -function explain_schedule(simulation::DomainSimulation) - rows = NamedTuple[] - for domain in simulation.mapping.domains, (key, clock) in simulation.model_clocks - key.domain == domain.name || continue - push!(rows, ( - domain=domain.name, - kind=domain.kind, - scale=key.scale, - process=key.process, - dt_steps=clock.dt, - phase=clock.phase, - dt_seconds=clock.dt * simulation.timeline.base_step_seconds, - )) - end - return rows -end - -""" - explain_domain_dependencies(simulation) - -Return structured rows describing resolved cross-domain dependencies. -""" -function explain_domain_dependencies(simulation::DomainSimulation) - rows = NamedTuple[] - for ((consumer, name), producers) in simulation.dependency_bindings - policy = simulation.dependency_policies[(consumer, name)] - variable = simulation.dependency_variables[(consumer, name)] - for producer in producers - push!(rows, ( - mode=:value, - consumer=consumer, - dependency=name, - producer=producer, - variable=variable, - policy=typeof(policy), - )) - end - end - for ((consumer, name), producers) in simulation.hard_domain_dependency_bindings - for producer in producers - push!(rows, ( - mode=:hard_domain, - consumer=consumer, - dependency=name, - producer=producer, - variable=nothing, - policy=nothing, - )) - end - end - return rows -end - -""" - explain_routes(simulation) - -Return structured rows describing resolved explicit cross-domain routes. -""" -function explain_routes(simulation::DomainSimulation) - rows = NamedTuple[] - for (i, route) in enumerate(simulation.mapping.routes) - clock = simulation.route_clocks[i] - for producer in simulation.route_bindings[i] - push!(rows, ( - route=i, - from=route.from, - to=route.to, - producer=producer, - source_var=route.from.var, - target_var=route.to.var, - cardinality=typeof(route.cardinality), - policy=typeof(route.policy), - dt_steps=clock.dt, - phase=clock.phase, - dt_seconds=clock.dt * simulation.timeline.base_step_seconds, - )) - end - end - return rows -end diff --git a/src/domains/environment_bridge.jl b/src/domains/environment_bridge.jl deleted file mode 100644 index d68d9baef..000000000 --- a/src/domains/environment_bridge.jl +++ /dev/null @@ -1,171 +0,0 @@ -function _domain_environment_entities(simulation::DomainSimulation, domain::Domain) - state = get(simulation.domain_states, domain.name, nothing) - entities = NamedTuple[] - if state isa DomainGraphState - for (scale, statuses_at_scale) in status(state) - push!(entities, ( - domain=domain.name, - kind=domain.kind, - scale=scale, - statuses=statuses_at_scale, - state=state, - )) - end - elseif state isa ModelMapping{SingleScale} - push!(entities, ( - domain=domain.name, - kind=domain.kind, - scale=:Default, - statuses=Status[status(state)], - state=state, - )) - end - return entities -end - -function _update_domain_environment_index!(simulation::DomainSimulation, domain::Domain) - return update_index!(simulation.environment, _domain_environment_entities(simulation, domain)) -end - -_domain_environment_support(domain::Domain, node::AbstractDependencyNode, status) = - EnvironmentSupport(domain.name, node.scale, node.process, status) - -_domain_environment_support(key::DomainModelKey, status) = - EnvironmentSupport(key.domain, key.scale, key.process, status) - -function _sample_domain_environment_at_time(simulation::DomainSimulation, support::EnvironmentSupport, t, model_spec::ModelSpec) - return sample_environment(simulation.environment, support, t, model_spec) -end - -function _sample_domain_environment_at_step(simulation::DomainSimulation, support::EnvironmentSupport, step::Int, model_spec::ModelSpec) - t = _time_from_step(step, simulation.timeline) - return _sample_domain_environment_at_time(simulation, support, t, model_spec) -end - -function _dependency_target_meteo(simulation::DomainSimulation, key::DomainModelKey, st, step::Int) - spec = simulation.model_specs[key] - support = _domain_environment_support(key, st) - return _sample_domain_environment_at_step(simulation, support, step, spec) -end - -function _domain_environment_for_model( - simulation::DomainSimulation, - domain::Domain, - node::SoftDependencyNode, - model_spec::ModelSpec, - status, - step::Int -) - support = _domain_environment_support(domain, node, status) - return _sample_domain_environment_at_step(simulation, support, step, model_spec) -end - -function _scatter_domain_environment_outputs_at_time!( - simulation::DomainSimulation, - domain::Domain, - node::AbstractDependencyNode, - model_spec::ModelSpec, - status, - t -) - isempty(keys(meteo_outputs_(model_spec))) && return nothing - support = _domain_environment_support(domain, node, status) - return scatter_environment_outputs!(simulation.environment, support, t, model_spec, status) -end - -function _scatter_domain_environment_outputs!( - simulation::DomainSimulation, - domain::Domain, - node::AbstractDependencyNode, - model_spec::ModelSpec, - status, - step::Int -) - t = _time_from_step(step, simulation.timeline) - return _scatter_domain_environment_outputs_at_time!(simulation, domain, node, model_spec, status, t) -end - -function _scatter_domain_hard_dependency_environment_outputs_at_time!( - simulation::DomainSimulation, - domain::Domain, - node::HardDependencyNode, - status, - t -) - key = DomainModelKey(domain.name, node.scale, node.process) - if haskey(simulation.model_specs, key) - spec = simulation.model_specs[key] - _scatter_domain_environment_outputs_at_time!(simulation, domain, node, spec, status, t) - end - for child in node.children - _scatter_domain_hard_dependency_environment_outputs_at_time!(simulation, domain, child, status, t) - end - return nothing -end - -function _scatter_domain_hard_dependency_environment_outputs!( - simulation::DomainSimulation, - domain::Domain, - node::HardDependencyNode, - status, - step::Int -) - t = _time_from_step(step, simulation.timeline) - return _scatter_domain_hard_dependency_environment_outputs_at_time!(simulation, domain, node, status, t) -end - -function _raw_meteo_for_staged_graph_domains(environment::GlobalConstant) - return environment_meteo(environment) -end - -function _raw_meteo_for_staged_graph_domains(environment::AbstractEnvironmentBackend) - return environment -end - -function _graph_domain_environment_for_model( - simulation::DomainSimulation, - domain::Domain, - node::SoftDependencyNode, - status, - t, - model_clock::ClockSpec, - model_spec::ModelSpec, - meteo, - meteo_sampler, - multirate::Bool -) - if simulation.environment isa GlobalConstant - return multirate ? _sample_meteo_for_model(meteo_sampler, meteo, round(Int, t), model_clock, model_spec) : meteo - end - support = _domain_environment_support(domain, node, status) - return _sample_domain_environment_at_time(simulation, support, t, model_spec) -end - -function _meteo_for_graph_step(meteo, step::Int, nsteps::Int) - return _meteo_row_at_step(meteo, step) -end - -function _meteo_for_graph_step(backend::AbstractEnvironmentBackend, step::Int, nsteps::Int) - return backend -end - -function _scatter_graph_domain_environment_outputs!( - simulation::DomainSimulation, - domain::Domain, - node::AbstractDependencyNode, - model_spec::ModelSpec, - status, - t -) - return _scatter_domain_environment_outputs_at_time!(simulation, domain, node, model_spec, status, t) -end - -function _scatter_graph_domain_hard_dependency_environment_outputs!( - simulation::DomainSimulation, - domain::Domain, - node::HardDependencyNode, - status, - t -) - return _scatter_domain_hard_dependency_environment_outputs_at_time!(simulation, domain, node, status, t) -end diff --git a/src/domains/graph_domain_runner.jl b/src/domains/graph_domain_runner.jl deleted file mode 100644 index a9955ef5c..000000000 --- a/src/domains/graph_domain_runner.jl +++ /dev/null @@ -1,135 +0,0 @@ -function _prepare_graph_domain_runtime!( - simulation::DomainSimulation, - domain::Domain, - object, - meteo, - constants, - nsteps::Int; - check=true, - executor=SequentialEx(), - type_promotion=_type_promotion(_domain_mapping(domain)), -) - roots = _domain_graph_roots(object, domain) - graph_simulations = GraphSimulation[] - for root in roots - _materialize_graph_route_attributes_for_domain!(simulation, domain, root, 1) - push!( - graph_simulations, - GraphSimulation( - root, - _domain_mapping(domain); - nsteps=nsteps, - check=check, - outputs=nothing, - type_promotion=type_promotion, - ), - ) - end - graph_state = DomainGraphState(graph_simulations) - simulation.domain_states[domain.name] = graph_state - representative_simulation = first(graph_simulations) - effective_multirate = _effective_multirate(representative_simulation) - dep_graph = dep(representative_simulation) - timeline = _timeline_context(meteo) - meteo_sampler = effective_multirate ? _prepare_meteo_sampler(meteo) : nothing - runtime_clock_rows = _runtime_clock_rows(representative_simulation, timeline, dep_graph) - effective_executor = executor - validate_meteo_inputs(get_model_specs(representative_simulation), meteo) - _validate_meteo_derived_timestep_requirements!(runtime_clock_rows, timeline) - if effective_multirate - if executor != SequentialEx() - @warn string( - "Multi-rate MTG domain `$(domain.name)` currently executes sequentially. ", - "Provided `executor=$(executor)` is ignored in this mode. ", - "Use `executor=SequentialEx()` to silence this warning." - ) maxlog = 1 - effective_executor = SequentialEx() - end - _warn_if_no_model_runs_at_base_timestep(runtime_clock_rows, timeline) - for graph_simulation in graph_simulations - validate_canonical_publishers(graph_simulation) - configure_temporal_buffers!(graph_simulation, timeline) - end - end - return DomainGraphRuntime(graph_state, meteo, constants, effective_multirate, timeline, meteo_sampler, effective_executor) -end - -function _run_graph_domain_step!( - simulation::DomainSimulation, - domain::Domain, - runtime::DomainGraphRuntime, - step::Int, - nsteps::Int; - check=true, - phase::Symbol=:normal, -) - meteo_i = _meteo_for_graph_step(runtime.meteo, step, nsteps) - meteo_provider = (node, status, i, t, model_clock, model_spec, meteo, meteo_sampler, multirate) -> - _graph_domain_environment_for_model( - simulation, - domain, - node, - status, - t, - model_clock, - model_spec, - meteo, - meteo_sampler, - multirate, - ) - after_model_run = (node, model_spec, status, i, t) -> begin - _scatter_graph_domain_environment_outputs!(simulation, domain, node, model_spec, status, t) - for hard_child in node.hard_dependency - _scatter_graph_domain_hard_dependency_environment_outputs!(simulation, domain, hard_child, status, t) - end - nothing - end - skip_model_run = node -> !_should_visit_domain_node(simulation, domain, node; phase=phase) - for graph_simulation in runtime.state.simulations - _materialize_graph_routes_for_domain!(simulation, domain, graph_simulation, step) - roots = collect(dep(graph_simulation).roots) - models = get_models(graph_simulation) - for (_, dependency_node) in roots - run_node_multiscale!( - graph_simulation, - dependency_node, - step, - models, - meteo_i, - runtime.constants, - graph_simulation, - check, - runtime.executor, - runtime.effective_multirate, - runtime.timeline, - runtime.meteo_sampler; - meteo_provider=meteo_provider, - after_model_run=after_model_run, - skip_model_run=skip_model_run, - ) - end - if phase == :normal - runtime.effective_multirate && update_requested_outputs!(graph_simulation, _time_from_step(step, runtime.timeline)) - save_results!(graph_simulation, step) - end - end - _publish_graph_domain_step_outputs!( - simulation, - domain, - runtime.state, - step; - effective_multirate=runtime.effective_multirate, - phase=phase, - ) - _update_domain_environment_index!(simulation, domain) - return runtime.state -end - -function _finalize_graph_domain_runtime!(runtime::DomainGraphRuntime) - for graph_simulation in runtime.state.simulations - for (organ, index) in graph_simulation.outputs_index - resize!(outputs(graph_simulation)[organ], index - 1) - end - end - return runtime.state -end diff --git a/src/domains/output_publisher.jl b/src/domains/output_publisher.jl deleted file mode 100644 index 51b5feb2b..000000000 --- a/src/domains/output_publisher.jl +++ /dev/null @@ -1,104 +0,0 @@ -function _publish_domain_model_outputs!( - simulation::DomainSimulation, - domain::Domain, - node::AbstractDependencyNode, - status, - step::Int -) - key = DomainModelKey(domain.name, node.scale, node.process) - haskey(simulation.model_specs, key) || return nothing - spec = simulation.model_specs[key] - for out_var in keys(outputs_(spec)) - stream_key = (key, out_var) - value = status[out_var] - push!(get!(simulation.streams, stream_key, Pair{Int,Any}[]), step => value) - push!(get!(simulation.outputs, stream_key, Any[]), value) - end - return nothing -end - -function _publish_domain_hard_dependency_outputs!( - simulation::DomainSimulation, - domain::Domain, - node::HardDependencyNode, - status, - step::Int -) - _publish_domain_model_outputs!(simulation, domain, node, status, step) - for child in node.children - _publish_domain_hard_dependency_outputs!(simulation, domain, child, status, step) - end - return nothing -end - -function _publish_graph_domain_step_outputs!( - simulation::DomainSimulation, - domain::Domain, - graph_state::DomainGraphState, - step::Int; - effective_multirate::Bool=false, - phase::Symbol=:normal, -) - graph_statuses = status(graph_state) - for (key, spec) in simulation.model_specs - key.domain == domain.name || continue - _should_publish_domain_key(simulation, domain, key; phase=phase) || continue - if effective_multirate - clock = simulation.model_clocks[key] - _should_run_at_time(clock, float(step)) || continue - end - haskey(graph_statuses, key.scale) || continue - for out_var in keys(outputs_(spec)) - stream_key = (key, out_var) - ids = Int[] - values = Any[] - for st in graph_statuses[key.scale] - out_var in propertynames(st) || continue - push!(ids, node_id(st.node)) - push!(values, st[out_var]) - end - push!(get!(simulation.streams, stream_key, Pair{Int,Any}[]), step => DomainNodeValues(ids, values)) - push!(get!(simulation.outputs, stream_key, Any[]), values) - end - end - return nothing -end - -function _publish_graph_target!(target::ModelTarget) - spec = target.simulation.model_specs[target.key] - domain = _domain_for_name(target.simulation.mapping, target.key.domain) - t = _time_from_step(target.step, target.simulation.timeline) - _scatter_graph_domain_environment_outputs!(target.simulation, domain, target.node, spec, target.status, t) - for hard_child in target.node.hard_dependency - _scatter_graph_domain_hard_dependency_environment_outputs!(target.simulation, domain, hard_child, target.status, t) - end - for out_var in keys(outputs_(spec)) - out_var in propertynames(target.status) || continue - stream_key = (target.key, out_var) - value = target.status[out_var] - push!(get!(target.simulation.streams, stream_key, Pair{Int,Any}[]), target.step => value) - push!(get!(target.simulation.outputs, stream_key, Any[]), value) - end - for hard_child in target.node.hard_dependency - _publish_domain_hard_dependency_outputs!(target.simulation, domain, hard_child, target.status, target.step) - end - return nothing -end - -function _publish_target!(target::ModelTarget) - isnothing(target.simulation) && error( - "`publish=true` requires a target created by `dependency_targets(extra, name)` in a domain simulation." - ) - target.extra isa GraphSimulation && return _publish_graph_target!(target) - domain = _domain_for_name(target.simulation.mapping, target.key.domain) - spec = target.simulation.model_specs[target.key] - _scatter_domain_environment_outputs!(target.simulation, domain, target.node, spec, target.status, target.step) - for hard_child in target.node.hard_dependency - _scatter_domain_hard_dependency_environment_outputs!(target.simulation, domain, hard_child, target.status, target.step) - end - _publish_domain_model_outputs!(target.simulation, domain, target.node, target.status, target.step) - for hard_child in target.node.hard_dependency - _publish_domain_hard_dependency_outputs!(target.simulation, domain, hard_child, target.status, target.step) - end - return nothing -end diff --git a/src/domains/route_runtime.jl b/src/domains/route_runtime.jl deleted file mode 100644 index eb8091936..000000000 --- a/src/domains/route_runtime.jl +++ /dev/null @@ -1,386 +0,0 @@ -function _resolve_selector_matches( - mapping::SimulationMapping, - specs::Dict{DomainModelKey,ModelSpec}, - selector::Union{AllDomains,HardDomains}; - context::String, -) - resolved = DomainModelKey[] - keys_by_domain = _keys_by_domain(specs) - for domain in mapping.domains - for producer_key in get(keys_by_domain, domain.name, DomainModelKey[]) - producer_spec = specs[producer_key] - _matches(selector, domain, producer_key, producer_spec) && push!(resolved, producer_key) - end - end - isempty(resolved) && error( - _selector_match_error(mapping, specs, selector; context=context) - ) - return resolved -end - -function _resolve_route_bindings(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) - bindings = Vector{DomainModelKey}[] - for (i, route) in enumerate(mapping.routes) - push!( - bindings, - _resolve_selector_matches( - mapping, - specs, - route.from; - context="Route $(i) from `$(route.from)`", - ), - ) - end - return bindings -end - -function _has_route_to_input(routes::Vector{Route}, consumer_key::DomainModelKey, input_var::Symbol) - any(routes) do route - route.to.domain == consumer_key.domain && - route.to.scale == consumer_key.scale && - route.to.var == input_var && - (isnothing(route.to.process) || route.to.process == consumer_key.process) - end -end - -function _all_domains_from_input_selector(selector::AbstractObjectMultiplicity, input_var::Symbol) - c = criteria(selector) - unsupported = (:name, :relation, :within, :window) - any(key -> haskey(c, key) && !isnothing(getproperty(c, key)), unsupported) && return nothing - - source_var = haskey(c, :var) ? c.var : input_var - policy = haskey(c, :policy) ? _as_schedule_policy(c.policy; context="Inputs route policy for `$(input_var)`") : HoldLast() - return AllDomains( - kind=haskey(c, :kind) ? c.kind : nothing, - domain=haskey(c, :domain) ? c.domain : nothing, - scale=haskey(c, :scale) ? c.scale : nothing, - process=haskey(c, :process) ? c.process : nothing, - var=source_var, - policy=policy, - ) -end - -function _single_value_route_reducer(values) - length(values) == 1 || error( - "`One(...)` input route expected exactly one resolved value, got $(length(values))." - ) - return only(values) -end - -function _route_cardinality_from_input_selector(selector::AbstractObjectMultiplicity) - selector isa Many && return ManyToOneVector() - selector isa Union{One,OptionalOne} && return ManyToOneAggregate(_single_value_route_reducer) - return nothing -end - -function _routes_from_value_inputs(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) - routes = Route[] - existing_routes = copy(mapping.routes) - for (consumer_key, spec) in specs - bindings = value_inputs(spec) - bindings isa NamedTuple || continue - for (input_var, selector) in pairs(bindings) - input_sym = Symbol(input_var) - input_sym in keys(inputs_(spec)) || continue - selector isa AbstractObjectMultiplicity || continue - _has_route_to_input(existing_routes, consumer_key, input_sym) && continue - - source = _all_domains_from_input_selector(selector, input_sym) - isnothing(source) && continue - cardinality = _route_cardinality_from_input_selector(selector) - isnothing(cardinality) && continue - - target = DomainRouteTarget( - consumer_key.domain; - scale=consumer_key.scale, - var=input_sym, - process=consumer_key.process, - ) - route = Route(from=source, to=target, cardinality=cardinality) - push!(routes, route) - push!(existing_routes, route) - end - end - return routes -end - -function _add_input_routes(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) - generated_routes = _routes_from_value_inputs(mapping, specs) - isempty(generated_routes) && return mapping - return SimulationMapping(mapping.domains...; routes=(mapping.routes..., generated_routes...)) -end - -function _route_target_input_default(route::Route, specs::Dict{DomainModelKey,ModelSpec}) - consumer_key = _route_target_consumer_key(route, specs) - isnothing(consumer_key) && return nothing - consumer_inputs = inputs_(specs[consumer_key]) - target_var = route.to.var - target_var in keys(consumer_inputs) || return nothing - return getproperty(consumer_inputs, target_var) -end - -function _route_target_status_defaults(mapping::SimulationMapping, domain::Domain, specs::Dict{DomainModelKey,ModelSpec}) - defaults = NamedTuple() - target_mapping = _domain_mapping(domain) - target_mapping isa ModelMapping{SingleScale} || return defaults - target_status = status(target_mapping) - - for route in mapping.routes - target = route.to - target.domain == domain.name || continue - target.scale == :Default || continue - target.var in propertynames(target_status) && continue - target.var in keys(defaults) && continue - - default = _route_target_input_default(route, specs) - isnothing(default) && continue - defaults = merge(defaults, NamedTuple{(target.var,)}((default,))) - end - - return defaults -end - -function _add_route_target_status_defaults(mapping::SimulationMapping, specs::Dict{DomainModelKey,ModelSpec}) - domains = Domain[] - changed = false - - for domain in mapping.domains - target_mapping = _domain_mapping(domain) - defaults = _route_target_status_defaults(mapping, domain, specs) - if target_mapping isa ModelMapping{SingleScale} && !isempty(keys(defaults)) - augmented_status = Status(merge(defaults, NamedTuple(status(target_mapping)))) - target_mapping = copy(target_mapping, augmented_status) - changed = true - end - push!(domains, Domain(domain.name, domain.kind, target_mapping, domain.selector)) - end - - changed || return mapping - return SimulationMapping(domains...; routes=mapping.routes) -end - -function _route_target_consumer_key(route::Route, specs::Dict{DomainModelKey,ModelSpec}) - target = route.to - if !isnothing(target.process) - key = DomainModelKey(target.domain, target.scale, target.process) - haskey(specs, key) || error( - "Route target process `$(key)` does not exist." - ) - target.var in keys(inputs_(specs[key])) || error( - "Route target process `$(key)` does not consume variable `$(target.var)`. ", - "Use a process that declares `$(target.var)` in `inputs_`, or omit `process=...` ", - "if the route should only materialize a domain status variable." - ) - return key - end - - consumers = DomainModelKey[] - for (key, spec) in specs - key.domain == target.domain || continue - key.scale == target.scale || continue - target.var in keys(inputs_(spec)) || continue - push!(consumers, key) - end - - isempty(consumers) && return nothing - length(consumers) == 1 || error( - "Route target `$(target.domain)/$(target.scale)/$(target.var)` is consumed by several models: ", - join(consumers, ", "), - ". Specify `process=...` in `DomainRouteTarget` so the route clock is unambiguous." - ) - return only(consumers) -end - -function _validate_route_targets(mapping::SimulationMapping, routes::Vector{Route}, specs::Dict{DomainModelKey,ModelSpec}; staged_graph_domains=false) - for (i, route) in enumerate(routes) - target = route.to - domain = _domain_for_name(mapping, target.domain) - target_mapping = _domain_mapping(domain) - if target_mapping isa ModelMapping{MultiScale} - staged_graph_domains || error( - "Route $(i) targets MTG-backed domain `$(target.domain)`, but this runner only supports single-status domains." - ) - route.cardinality isa OneToManyBroadcast || error( - "Route $(i) targets MTG-backed domain `$(target.domain)`. ", - "The MTG-domain runner only supports `OneToManyBroadcast()` routes into graph domains." - ) - target.scale == :Default && error( - "Route $(i) targets MTG-backed domain `$(target.domain)` but uses `scale=:Default`. ", - "Specify the target graph scale, for example `scale=:Leaf`." - ) - isnothing(_route_target_consumer_key(route, specs)) && error( - "Route $(i) targets graph variable `$(target.var)` in `$(target.domain)/$(target.scale)`, ", - "but no target process consumes that variable. Specify `process=...` or add the variable to one model's `inputs_`." - ) - continue - end - target.scale == :Default || error( - "Route $(i) target `$(target.domain)/$(target.scale)/$(target.var)` is not supported by the single-status domain runner. ", - "Use `scale=:Default` for single-status targets, or target an MTG-backed domain with a supported graph route cardinality." - ) - st = status(target_mapping) - target.var in propertynames(st) || error( - "Route $(i) target status `$(target.domain)/$(target.scale)` does not contain variable `$(target.var)`. ", - "Initialize it in the target domain status so the route can materialize its value." - ) - _route_target_consumer_key(route, specs) - end - return nothing -end - -function _validate_graph_route_order( - mapping::SimulationMapping, - routes::Vector{Route}, - route_bindings::Vector{Vector{DomainModelKey}}, -) - _domain_run_order(mapping, route_bindings) - return nothing -end - -function _route_clocks(routes::Vector{Route}, specs::Dict{DomainModelKey,ModelSpec}, model_clocks) - clocks = ClockSpec[] - for route in routes - consumer_key = _route_target_consumer_key(route, specs) - if isnothing(consumer_key) - push!(clocks, ClockSpec(1.0, 0.0)) - else - push!(clocks, model_clocks[consumer_key]) - end - end - return clocks -end - -function _route_due(simulation::DomainSimulation, route_index::Int, step::Int) - clock = simulation.route_clocks[route_index] - return _should_run_at_time(clock, float(step)) -end - -function _route_producer_values(simulation::DomainSimulation, route_index::Int, step::Int) - route = simulation.mapping.routes[route_index] - source_var = route.from.var - steps = _window_steps(step, simulation.route_clocks[route_index]) - return Any[ - _apply_dependency_policy(_resolve_stream_values(simulation, producer, source_var, steps), route.policy) - for producer in simulation.route_bindings[route_index] - ] -end - -function _route_value_items(values::Vector{Any}) - items = Any[] - for value in values - isnothing(value) && continue - if value isa DomainNodeValues - append!(items, value.values) - else - push!(items, value) - end - end - return items -end - -function _materialize_route_value(values::Vector{Any}, cardinality::ManyToOneVector) - return _route_value_items(values) -end - -function _materialize_route_value(values::Vector{Any}, cardinality::ManyToOneAggregate) - return cardinality.reducer(_route_value_items(values)) -end - -function _materialize_graph_broadcast_value(values::Vector{Any}, route_index::Int) - items = _route_value_items(values) - length(items) == 1 || error( - "Route $(route_index) uses `OneToManyBroadcast()` into an MTG-backed domain and resolved $(length(items)) values. ", - "Use a selector that resolves one source value, or aggregate upstream before broadcasting." - ) - return only(items) -end - -function _materialize_route_value(values::Vector{Any}, cardinality::RouteCardinality) - error( - "Route cardinality `$(typeof(cardinality))` is declared but not implemented in the single-status domain runner. ", - "Use `ManyToOneVector()` or `ManyToOneAggregate(...)` for single-status targets. ", - "`OneToManyBroadcast()` is supported for MTG-backed target domains." - ) -end - -function _set_route_target_value!(simulation::DomainSimulation, route::Route, value) - target = route.to - domain = _domain_for_name(simulation.mapping, target.domain) - target.scale == :Default || error( - "Route target `$(target.domain)/$(target.scale)/$(target.var)` is not supported by the single-status domain runner. ", - "Use `scale=:Default` for single-status targets, or target an MTG-backed domain with a supported graph route cardinality." - ) - st = status(simulation, domain.name) - target.var in propertynames(st) || error( - "Route target status `$(target.domain)/$(target.scale)` does not contain variable `$(target.var)`. ", - "Initialize it in the target domain status so the route can materialize its value." - ) - st[target.var] = value - return nothing -end - -function _materialize_routes_for_domain!(simulation::DomainSimulation, domain::Domain, step::Int) - for (i, route) in enumerate(simulation.mapping.routes) - route.to.domain == domain.name || continue - _route_due(simulation, i, step) || continue - values = _route_producer_values(simulation, i, step) - value = _materialize_route_value(values, route.cardinality) - _set_route_target_value!(simulation, route, value) - end - return nothing -end - -function _materialize_graph_routes_for_domain!( - simulation::DomainSimulation, - domain::Domain, - graph_simulation::GraphSimulation, - step::Int, -) - for (i, route) in enumerate(simulation.mapping.routes) - route.to.domain == domain.name || continue - route.cardinality isa OneToManyBroadcast || continue - _route_due(simulation, i, step) || continue - target = route.to - graph_statuses = status(graph_simulation) - haskey(graph_statuses, target.scale) || error( - "Route $(i) targets `$(target.domain)/$(target.scale)/$(target.var)`, ", - "but the selected graph domain has no statuses at scale `$(target.scale)`." - ) - values = _route_producer_values(simulation, i, step) - value = _materialize_graph_broadcast_value(values, i) - for st in graph_statuses[target.scale] - target.var in propertynames(st) || error( - "Route $(i) targets `$(target.domain)/$(target.scale)/$(target.var)`, ", - "but one target status does not contain variable `$(target.var)`." - ) - st[target.var] = value - end - end - return nothing -end - -function _materialize_graph_route_attributes_for_domain!( - simulation::DomainSimulation, - domain::Domain, - root, - step::Int, -) - for (i, route) in enumerate(simulation.mapping.routes) - route.to.domain == domain.name || continue - route.cardinality isa OneToManyBroadcast || continue - target = route.to - values = _route_producer_values(simulation, i, step) - value = _materialize_graph_broadcast_value(values, i) - matched = 0 - MultiScaleTreeGraph.traverse!(root) do node - symbol(node) == target.scale || return - node[target.var] = value - matched += 1 - end - matched > 0 || error( - "Route $(i) targets `$(target.domain)/$(target.scale)/$(target.var)`, ", - "but the selected graph domain has no nodes at scale `$(target.scale)`." - ) - end - return nothing -end diff --git a/src/domains/routes.jl b/src/domains/routes.jl deleted file mode 100644 index ad4da48d1..000000000 --- a/src/domains/routes.jl +++ /dev/null @@ -1,89 +0,0 @@ -abstract type RouteCardinality end - -""" - ManyToOneVector() - -Route cardinality that materializes one value per resolved producer. -""" -struct ManyToOneVector <: RouteCardinality end - -""" - ManyToOneAggregate(reducer=sum) - -Route cardinality that reduces resolved producer values to one scalar. -""" -struct ManyToOneAggregate{F} <: RouteCardinality - reducer::F -end - -ManyToOneAggregate() = ManyToOneAggregate(sum) - -""" - OneToManyBroadcast() - SpatialSample() - SpatialScatterAdd() - -Reserved route cardinalities for the MTG/spatial domain runner. -""" -struct OneToManyBroadcast <: RouteCardinality end -struct SpatialSample <: RouteCardinality end -struct SpatialScatterAdd <: RouteCardinality end - -""" - DomainRouteTarget(domain; scale=:Default, var, process=nothing) - -Target of an explicit cross-domain route. -""" -struct DomainRouteTarget - domain::Symbol - scale::Symbol - var::Symbol - process::Union{Nothing,Symbol} -end - -function Base.show(io::IO, target::DomainRouteTarget) - terms = ["domain=:$(target.domain)"] - target.scale == :Default || push!(terms, "scale=:$(target.scale)") - push!(terms, "var=:$(target.var)") - isnothing(target.process) || push!(terms, "process=:$(target.process)") - print(io, "DomainRouteTarget(", join(terms, ", "), ")") -end - -function DomainRouteTarget( - domain::Union{Symbol,AbstractString}; - scale::Union{Symbol,AbstractString}=:Default, - var, - process=nothing, -) - return DomainRouteTarget( - Symbol(domain), - Symbol(scale), - Symbol(var), - isnothing(process) ? nothing : Symbol(process), - ) -end - -""" - Route(from, to; cardinality=ManyToOneVector(), policy=nothing) - Route(; from, to, cardinality=ManyToOneVector(), policy=nothing) - -Explicit cross-domain route materialized into a target domain status before -that domain runs. -""" -struct Route{F,T,C<:RouteCardinality,P<:SchedulePolicy} - from::F - to::T - cardinality::C - policy::P -end - -function Route(from::AllDomains, to::DomainRouteTarget; cardinality::RouteCardinality=ManyToOneVector(), policy=nothing) - route_policy = isnothing(policy) ? from.policy : _as_schedule_policy(policy; context="Route policy") - isnothing(from.var) && error( - "Route source `AllDomains(...)` must declare `var=:source_variable` so the runtime knows what to materialize." - ) - return Route(from, to, cardinality, route_policy) -end - -Route(; from, to, cardinality::RouteCardinality=ManyToOneVector(), policy=nothing) = - Route(from, to; cardinality=cardinality, policy=policy) diff --git a/src/mtg/ModelSpec.jl b/src/mtg/ModelSpec.jl index a6c51e51c..d89dd8bed 100644 --- a/src/mtg/ModelSpec.jl +++ b/src/mtg/ModelSpec.jl @@ -663,39 +663,17 @@ timestep(m::ModelSpec) = m.timestep inputs_(m::ModelSpec) = inputs_(model_(m)) outputs_(m::ModelSpec) = outputs_(model_(m)) -function _model_spec_dependency_selector(dep_name::Symbol, selector) - return selector -end - -function _model_spec_dependency_selector(dep_name::Symbol, selector::Input) - return nothing -end - function _normalize_model_spec_dependencies(deps::NamedTuple) normalized = Pair{Symbol,Any}[] for (dep_name, selector) in pairs(deps) selector isa Union{Input,Call} && continue - normalized_selector = _model_spec_dependency_selector(dep_name, selector) - isnothing(normalized_selector) && continue - push!(normalized, dep_name => normalized_selector) - end - return (; normalized...) -end - -function _model_spec_call_dependencies(spec::ModelSpec) - calls = model_calls(spec) - calls isa NamedTuple || return NamedTuple() - normalized = Pair{Symbol,Any}[] - for (dep_name, selector) in pairs(calls) - push!(normalized, dep_name => _model_spec_dependency_selector(dep_name, selector)) + push!(normalized, dep_name => selector) end return (; normalized...) end function dep(m::ModelSpec) - model_deps = _normalize_model_spec_dependencies(dep(model_(m))) - call_deps = _model_spec_call_dependencies(m) - return (; pairs(model_deps)..., pairs(call_deps)...) + return _normalize_model_spec_dependencies(dep(model_(m))) end init_variables(m::ModelSpec; verbose::Bool=true) = init_variables(model_(m); verbose=verbose) meteo_inputs_(m::ModelSpec) = meteo_inputs_(model_(m)) diff --git a/src/scene_object_api.jl b/src/scene_object_api.jl index 881dbeb54..61a7a971f 100644 --- a/src/scene_object_api.jl +++ b/src/scene_object_api.jl @@ -900,7 +900,7 @@ Relation(relation::AbstractString) = Relation(Symbol(relation)) _maybe_symbol(x) = isnothing(x) ? nothing : Symbol(x) -const _OBJECT_ADDRESS_SYMBOL_FIELDS = (:kind, :domain, :species, :scale, :name, :process, :var, :relation, :application) +const _OBJECT_ADDRESS_SYMBOL_FIELDS = (:kind, :species, :scale, :name, :process, :var, :relation, :application) function _normalize_object_selector_value(key::Symbol, value) key in _OBJECT_ADDRESS_SYMBOL_FIELDS && return _maybe_symbol(value) @@ -2951,7 +2951,7 @@ function _same_environment_backend(a, b) end function _same_environment_support(a, b) - return a.domain == b.domain && + return a.application == b.application && a.scale == b.scale && a.process == b.process && a.status === b.status @@ -4149,13 +4149,6 @@ call_target(context::SceneRunContext, name::Symbol) = only(call_targets(context, call_target(context::SceneRunContext, name::AbstractString) = call_target(context, Symbol(name)) -dependency_targets(context::SceneRunContext, name::Symbol) = call_targets(context, name) -dependency_targets(context::SceneRunContext, name::AbstractString) = - call_targets(context, name) -dependency_target(context::SceneRunContext, name::Symbol) = call_target(context, name) -dependency_target(context::SceneRunContext, name::AbstractString) = - call_target(context, name) - """ run_call!(target::SceneCallTarget; publish=false, meteo=nothing) @@ -4622,7 +4615,7 @@ function _legacy_multiscale_rhs_from_input_selector(selector::AbstractObjectMult # The current MTG mapping layer only understands scale/variable mappings. # Keep richer object filters as unified metadata for the future compiler. - unsupported = (:kind, :domain, :species, :name, :process, :relation) + unsupported = (:kind, :species, :name, :process, :relation) any(key -> haskey(c, key) && !isnothing(getproperty(c, key)), unsupported) && return nothing scale = c.scale diff --git a/src/time/runtime/environment_backends.jl b/src/time/runtime/environment_backends.jl index 013ba4092..f439fd375 100644 --- a/src/time/runtime/environment_backends.jl +++ b/src/time/runtime/environment_backends.jl @@ -10,13 +10,13 @@ packages can subtype this and implement `sample`, `scatter!`, `update_index!`, abstract type AbstractEnvironmentBackend end """ - EnvironmentSupport(domain, scale, process, status) + EnvironmentSupport(application, scale, process, status) Minimal support descriptor passed to environment backends when a model samples or scatters environmental variables. """ struct EnvironmentSupport{S} - domain::Symbol + application::Symbol scale::Symbol process::Symbol status::S @@ -196,7 +196,7 @@ function sample(backend::GlobalConstant, variable::Symbol, support::EnvironmentS meteo = _meteo_row_at_step(environment_meteo(backend), Int(round(time))) isnothing(meteo) && return nothing hasproperty(meteo, variable) || error( - "GlobalConstant meteo does not provide variable `$(variable)` for `$(support.domain)/$(support.scale)/$(support.process)`." + "GlobalConstant meteo does not provide variable `$(variable)` for `$(support.application)/$(support.scale)/$(support.process)`." ) return getproperty(meteo, variable) end @@ -215,7 +215,7 @@ end scatter!(backend::GlobalConstant, variable::Symbol, support::EnvironmentSupport, value, time) = error( "GlobalConstant is immutable and cannot receive environment output `$(variable)` from ", - "`$(support.domain)/$(support.scale)/$(support.process)`." + "`$(support.application)/$(support.scale)/$(support.process)`." ) """ @@ -306,12 +306,12 @@ function sample_environment( pairs = Pair{Symbol,Any}[] for (target, source) in _environment_sampling_rules(model_spec) isnothing(row) && error( - "GlobalConstant meteo is `nothing`, but `$(support.domain)/$(support.scale)/$(support.process)` ", + "GlobalConstant meteo is `nothing`, but `$(support.application)/$(support.scale)/$(support.process)` ", "requires meteo variable `$(target)`." ) hasproperty(row, source) || error( "GlobalConstant meteo does not provide source variable `$(source)` for model-facing variable ", - "`$(target)` in `$(support.domain)/$(support.scale)/$(support.process)`." + "`$(target)` in `$(support.application)/$(support.scale)/$(support.process)`." ) push!(pairs, target => getproperty(row, source)) end @@ -324,7 +324,7 @@ end function _environment_output_value(status, variable::Symbol, support::EnvironmentSupport) hasproperty(status, variable) && return getproperty(status, variable) error( - "Model `$(support.domain)/$(support.scale)/$(support.process)` declares environment output ", + "Model `$(support.application)/$(support.scale)/$(support.process)` declares environment output ", "`$(variable)` in `meteo_outputs_`, but its status does not contain `$(variable)`. ", "Expose the computed value as a same-named `outputs_` variable or initialize it in the status." ) diff --git a/test/runtests.jl b/test/runtests.jl index 454f6d120..db9fb837e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -54,12 +54,8 @@ include("helper-functions.jl") include("test-environment-backends.jl") end - @testset "Domain simulation" begin - include("test-domain-simulation.jl") - end - - @testset "MAESPA-style domain example" begin - include("test-maespa-domain-example.jl") + @testset "MAESPA-style scene example" begin + include("test-maespa-scene-example.jl") end @testset "MultiScaleModel" begin diff --git a/test/test-domain-simulation.jl b/test/test-domain-simulation.jl deleted file mode 100644 index 3bdc45138..000000000 --- a/test/test-domain-simulation.jl +++ /dev/null @@ -1,1521 +0,0 @@ -using Dates - -PlantSimEngine.@process "domain_absorbed_radiation" verbose = false -PlantSimEngine.@process "domain_plant_transpiration" verbose = false -PlantSimEngine.@process "domain_soil_water" verbose = false -PlantSimEngine.@process "domain_soil_evaporation" verbose = false -PlantSimEngine.@process "domain_scene_evapotranspiration" verbose = false -PlantSimEngine.@process "domain_scene_plant_evapotranspiration" verbose = false -PlantSimEngine.@process "domain_hard_leaf_conductance" verbose = false -PlantSimEngine.@process "domain_hard_leaf_energy" verbose = false -PlantSimEngine.@process "domain_scene_conductance_sum" verbose = false -PlantSimEngine.@process "domain_hard_target_signal" verbose = false -PlantSimEngine.@process "domain_scene_hard_target_sum" verbose = false -PlantSimEngine.@process "domain_scene_calls_target_sum" verbose = false -PlantSimEngine.@process "domain_hard_target_leaf_counter" verbose = false -PlantSimEngine.@process "domain_scene_hard_target_leaf_sum" verbose = false -PlantSimEngine.@process "domain_scene_routed_vector" verbose = false -PlantSimEngine.@process "domain_scene_routed_aggregate" verbose = false -PlantSimEngine.@process "domain_mtg_leaf_flux" verbose = false -PlantSimEngine.@process "domain_scene_dependency_flux_sum" verbose = false -PlantSimEngine.@process "domain_mtg_leaf_soil_flux" verbose = false -PlantSimEngine.@process "domain_growth_leaf_emergence" verbose = false -PlantSimEngine.@process "domain_growth_leaf_flux" verbose = false -PlantSimEngine.@process "domain_growth_integrated_flux" verbose = false -PlantSimEngine.@process "domain_scene_growth_flux_sum" verbose = false -PlantSimEngine.@process "domain_update_allocation" verbose = false -PlantSimEngine.@process "domain_update_pruning" verbose = false -PlantSimEngine.@process "domain_update_observer" verbose = false -PlantSimEngine.@process "domain_removal_leaf_flux" verbose = false -PlantSimEngine.@process "domain_removal_pruning" verbose = false -PlantSimEngine.@process "domain_churn_leaf_flux" verbose = false -PlantSimEngine.@process "domain_churn_leaf_controller" verbose = false -PlantSimEngine.@process "domain_subtree_internode_flux" verbose = false -PlantSimEngine.@process "domain_subtree_leaf_flux" verbose = false -PlantSimEngine.@process "domain_subtree_pruning" verbose = false -PlantSimEngine.@process "domain_reparent_leaf_controller" verbose = false - -struct DomainAbsorbedRadiationModel <: AbstractDomain_Absorbed_RadiationModel - coefficient::Float64 -end - -PlantSimEngine.inputs_(::DomainAbsorbedRadiationModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainAbsorbedRadiationModel) = (absorbed_radiation=0.0,) -PlantSimEngine.meteo_inputs_(::DomainAbsorbedRadiationModel) = (Ri_PAR_f=0.0,) - -function PlantSimEngine.run!(model::DomainAbsorbedRadiationModel, models, status, meteo, constants=nothing, extra=nothing) - status.absorbed_radiation = model.coefficient * meteo.Ri_PAR_f - return nothing -end - -struct DomainPlantTranspirationModel <: AbstractDomain_Plant_TranspirationModel - coefficient::Float64 -end - -PlantSimEngine.inputs_(::DomainPlantTranspirationModel) = (absorbed_radiation=0.0,) -PlantSimEngine.outputs_(::DomainPlantTranspirationModel) = (transpiration=0.0,) - -function PlantSimEngine.run!(model::DomainPlantTranspirationModel, models, status, meteo, constants=nothing, extra=nothing) - status.transpiration = model.coefficient * status.absorbed_radiation - return nothing -end - -struct DomainSoilWaterModel <: AbstractDomain_Soil_WaterModel - baseline::Float64 -end - -PlantSimEngine.inputs_(::DomainSoilWaterModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainSoilWaterModel) = (soil_water_content=0.0,) -PlantSimEngine.meteo_inputs_(::DomainSoilWaterModel) = (T=0.0,) - -function PlantSimEngine.run!(model::DomainSoilWaterModel, models, status, meteo, constants=nothing, extra=nothing) - status.soil_water_content = model.baseline - 0.001 * meteo.T - return nothing -end - -struct DomainSoilEvaporationModel <: AbstractDomain_Soil_EvaporationModel - coefficient::Float64 -end - -PlantSimEngine.inputs_(::DomainSoilEvaporationModel) = (soil_water_content=0.0,) -PlantSimEngine.outputs_(::DomainSoilEvaporationModel) = (evaporation=0.0,) -PlantSimEngine.meteo_inputs_(::DomainSoilEvaporationModel) = (T=0.0,) - -function PlantSimEngine.run!(model::DomainSoilEvaporationModel, models, status, meteo, constants=nothing, extra=nothing) - status.evaporation = model.coefficient * status.soil_water_content * meteo.T - return nothing -end - -struct DomainSceneEvapotranspirationModel <: AbstractDomain_Scene_EvapotranspirationModel -end - -PlantSimEngine.inputs_(::DomainSceneEvapotranspirationModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainSceneEvapotranspirationModel) = (evapotranspiration=0.0,) - -PlantSimEngine.dep(::DomainSceneEvapotranspirationModel) = ( - plant_transpiration=PlantSimEngine.AllDomains(kind=:plant, process=:domain_plant_transpiration, policy=Integrate()), - soil_evaporation=PlantSimEngine.AllDomains(kind=:soil, process=:domain_soil_evaporation, policy=Integrate()), -) - -function PlantSimEngine.run!(::DomainSceneEvapotranspirationModel, models, status, meteo, constants=nothing, extra=nothing) - plant_values = PlantSimEngine.dependency_values(extra, :plant_transpiration, :transpiration) - soil_values = PlantSimEngine.dependency_values(extra, :soil_evaporation, :evaporation) - status.evapotranspiration = sum(filter(x -> !isnothing(x), plant_values)) + sum(filter(x -> !isnothing(x), soil_values)) - return nothing -end - -struct DomainScenePlantEvapotranspirationModel <: AbstractDomain_Scene_Plant_EvapotranspirationModel -end - -PlantSimEngine.inputs_(::DomainScenePlantEvapotranspirationModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainScenePlantEvapotranspirationModel) = (plant_evapotranspiration=0.0,) - -PlantSimEngine.dep(::DomainScenePlantEvapotranspirationModel) = ( - plant_transpiration=PlantSimEngine.AllDomains(kind=:plant, process=:domain_plant_transpiration, policy=Integrate()), -) - -function PlantSimEngine.run!(::DomainScenePlantEvapotranspirationModel, models, status, meteo, constants=nothing, extra=nothing) - plant_values = PlantSimEngine.dependency_values(extra, :plant_transpiration, :transpiration) - status.plant_evapotranspiration = sum(filter(x -> !isnothing(x), plant_values)) - return nothing -end - -struct DomainHardLeafConductanceModel <: AbstractDomain_Hard_Leaf_ConductanceModel -end - -PlantSimEngine.inputs_(::DomainHardLeafConductanceModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainHardLeafConductanceModel) = (conductance=0.0,) - -function PlantSimEngine.run!(::DomainHardLeafConductanceModel, models, status, meteo, constants=nothing, extra=nothing) - status.conductance = 2.0 - return nothing -end - -struct DomainHardLeafEnergyModel <: AbstractDomain_Hard_Leaf_EnergyModel -end - -PlantSimEngine.dep(::DomainHardLeafEnergyModel) = (domain_hard_leaf_conductance=AbstractDomain_Hard_Leaf_ConductanceModel,) -PlantSimEngine.inputs_(::DomainHardLeafEnergyModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainHardLeafEnergyModel) = (leaf_temperature=0.0,) - -function PlantSimEngine.run!(::DomainHardLeafEnergyModel, models, status, meteo, constants=nothing, extra=nothing) - PlantSimEngine.run_target!(models, status, :domain_hard_leaf_conductance; meteo=meteo, constants=constants, extra=extra) - status.leaf_temperature = 20.0 + status.conductance - return nothing -end - -struct DomainSceneConductanceSumModel <: AbstractDomain_Scene_Conductance_SumModel -end - -PlantSimEngine.inputs_(::DomainSceneConductanceSumModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainSceneConductanceSumModel) = (conductance_sum=0.0,) - -PlantSimEngine.dep(::DomainSceneConductanceSumModel) = ( - conductance=PlantSimEngine.AllDomains(kind=:plant, process=:domain_hard_leaf_conductance, var=:conductance, policy=Integrate()), -) - -function PlantSimEngine.run!(::DomainSceneConductanceSumModel, models, status, meteo, constants=nothing, extra=nothing) - conductance_values = PlantSimEngine.dependency_values(extra, :conductance) - status.conductance_sum = sum(filter(x -> !isnothing(x), conductance_values)) - return nothing -end - -struct DomainHardTargetSignalModel{T} <: AbstractDomain_Hard_Target_SignalModel - coefficient::T -end - -PlantSimEngine.inputs_(::DomainHardTargetSignalModel) = (call_count=0,) -PlantSimEngine.outputs_(::DomainHardTargetSignalModel) = (call_count=0, signal=0.0,) - -function PlantSimEngine.run!(model::DomainHardTargetSignalModel, models, status, meteo, constants=nothing, extra=nothing) - status.call_count += 1 - status.signal = model.coefficient * status.call_count - return nothing -end - -struct DomainSceneHardTargetSumModel <: AbstractDomain_Scene_Hard_Target_SumModel end - -PlantSimEngine.inputs_(::DomainSceneHardTargetSumModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainSceneHardTargetSumModel) = (hard_target_total=0.0,) - -PlantSimEngine.dep(::DomainSceneHardTargetSumModel) = ( - plant_signal=PlantSimEngine.HardDomains(kind=:plant, process=:domain_hard_target_signal), -) - -function PlantSimEngine.run!(::DomainSceneHardTargetSumModel, models, status, meteo, constants=nothing, extra=nothing) - targets = PlantSimEngine.dependency_targets(extra, :plant_signal) - for target in targets - PlantSimEngine.run_target!(target) - PlantSimEngine.run_target!(target) - end - status.hard_target_total = sum(target.status.signal for target in targets) - return nothing -end - -struct DomainSceneCallsTargetSumModel <: AbstractDomain_Scene_Calls_Target_SumModel end - -PlantSimEngine.inputs_(::DomainSceneCallsTargetSumModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainSceneCallsTargetSumModel) = (calls_target_total=0.0,) - -function PlantSimEngine.run!(::DomainSceneCallsTargetSumModel, models, status, meteo, constants=nothing, extra=nothing) - targets = PlantSimEngine.dependency_targets(extra, :plant_signal) - for target in targets - run_call!(target) - run_call!(target) - end - status.calls_target_total = sum(target.status.signal for target in targets) - return nothing -end - -struct DomainHardTargetLeafCounterModel{T} <: AbstractDomain_Hard_Target_Leaf_CounterModel - coefficient::T -end - -PlantSimEngine.inputs_(::DomainHardTargetLeafCounterModel) = (call_count=0,) -PlantSimEngine.outputs_(::DomainHardTargetLeafCounterModel) = (call_count=0, leaf_signal=0.0,) - -function PlantSimEngine.run!(model::DomainHardTargetLeafCounterModel, models, status, meteo, constants=nothing, extra=nothing) - status.call_count += 1 - status.leaf_signal = model.coefficient * status.call_count - return nothing -end - -struct DomainSceneHardTargetLeafSumModel <: AbstractDomain_Scene_Hard_Target_Leaf_SumModel end - -PlantSimEngine.inputs_(::DomainSceneHardTargetLeafSumModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainSceneHardTargetLeafSumModel) = (leaf_hard_target_total=0.0,) - -PlantSimEngine.dep(::DomainSceneHardTargetLeafSumModel) = ( - leaf_calls=PlantSimEngine.HardDomains(kind=:plant, scale=:Leaf, process=:domain_hard_target_leaf_counter), -) - -function PlantSimEngine.run!(::DomainSceneHardTargetLeafSumModel, models, status, meteo, constants=nothing, extra=nothing) - targets = PlantSimEngine.dependency_targets(extra, :leaf_calls) - for target in targets - PlantSimEngine.run_target!(target; publish=true) - end - status.leaf_hard_target_total = sum(target.status.leaf_signal for target in targets) - return nothing -end - -struct DomainSceneRoutedVectorModel <: AbstractDomain_Scene_Routed_VectorModel end - -PlantSimEngine.inputs_(::DomainSceneRoutedVectorModel) = (plant_transpirations=Float64[],) -PlantSimEngine.outputs_(::DomainSceneRoutedVectorModel) = (routed_total=0.0,) - -function PlantSimEngine.run!(::DomainSceneRoutedVectorModel, models, status, meteo, constants=nothing, extra=nothing) - status.routed_total = sum(status.plant_transpirations) - return nothing -end - -struct DomainSceneRoutedAggregateModel <: AbstractDomain_Scene_Routed_AggregateModel end - -PlantSimEngine.inputs_(::DomainSceneRoutedAggregateModel) = (daily_plant_transpiration=0.0,) -PlantSimEngine.outputs_(::DomainSceneRoutedAggregateModel) = (daily_routed_total=0.0,) - -function PlantSimEngine.run!(::DomainSceneRoutedAggregateModel, models, status, meteo, constants=nothing, extra=nothing) - status.daily_routed_total = status.daily_plant_transpiration - return nothing -end - -struct DomainMTGLeafFluxModel{T} <: AbstractDomain_Mtg_Leaf_FluxModel - coefficient::T -end - -PlantSimEngine.inputs_(::DomainMTGLeafFluxModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainMTGLeafFluxModel) = (leaf_flux=0.0,) - -function PlantSimEngine.run!(model::DomainMTGLeafFluxModel, models, status, meteo, constants=nothing, extra=nothing) - status.leaf_flux = model.coefficient - return nothing -end - -struct DomainMTGLeafSoilFluxModel{T} <: AbstractDomain_Mtg_Leaf_Soil_FluxModel - coefficient::T -end - -PlantSimEngine.inputs_(::DomainMTGLeafSoilFluxModel) = (soil_signal=0.0,) -PlantSimEngine.outputs_(::DomainMTGLeafSoilFluxModel) = (leaf_flux=0.0,) - -function PlantSimEngine.run!(model::DomainMTGLeafSoilFluxModel, models, status, meteo, constants=nothing, extra=nothing) - status.leaf_flux = model.coefficient * status.soil_signal - return nothing -end - -struct DomainGrowthLeafEmergenceModel <: AbstractDomain_Growth_Leaf_EmergenceModel end - -PlantSimEngine.inputs_(::DomainGrowthLeafEmergenceModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainGrowthLeafEmergenceModel) = (grown_leaves=0.0,) - -function PlantSimEngine.run!(::DomainGrowthLeafEmergenceModel, models, status, meteo, constants=nothing, extra=nothing) - if length(PlantSimEngine.status(extra)[:Leaf]) == 0 - add_organ!(status.node, extra, "+", :Leaf, 2; check=true) - end - status.grown_leaves = length(PlantSimEngine.status(extra)[:Leaf]) - return nothing -end - -struct DomainGrowthLeafFluxModel{T} <: AbstractDomain_Growth_Leaf_FluxModel - coefficient::T -end - -PlantSimEngine.inputs_(::DomainGrowthLeafFluxModel) = (grown_leaves=0.0,) -PlantSimEngine.outputs_(::DomainGrowthLeafFluxModel) = (leaf_flux=0.0,) - -function PlantSimEngine.run!(model::DomainGrowthLeafFluxModel, models, status, meteo, constants=nothing, extra=nothing) - status.leaf_flux = model.coefficient * status.grown_leaves - return nothing -end - -struct DomainGrowthIntegratedFluxModel <: AbstractDomain_Growth_Integrated_FluxModel end - -PlantSimEngine.inputs_(::DomainGrowthIntegratedFluxModel) = (leaf_flux=-Inf,) -PlantSimEngine.outputs_(::DomainGrowthIntegratedFluxModel) = (integrated_leaf_flux=0.0,) - -function PlantSimEngine.run!(::DomainGrowthIntegratedFluxModel, models, status, meteo, constants=nothing, extra=nothing) - status.integrated_leaf_flux = sum(status.leaf_flux) - return nothing -end - -struct DomainSceneDependencyFluxSumModel <: AbstractDomain_Scene_Dependency_Flux_SumModel end - -PlantSimEngine.inputs_(::DomainSceneDependencyFluxSumModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainSceneDependencyFluxSumModel) = (dependency_total=0.0, grouped_dependency_total=0.0,) - -PlantSimEngine.dep(::DomainSceneDependencyFluxSumModel) = ( - leaf_fluxes=PlantSimEngine.AllDomains(kind=:plant, scale=:Leaf, process=:domain_mtg_leaf_flux, var=:leaf_flux), -) - -function PlantSimEngine.run!(::DomainSceneDependencyFluxSumModel, models, status, meteo, constants=nothing, extra=nothing) - grouped_values = PlantSimEngine.dependency_values(extra, :leaf_fluxes) - flattened_values = PlantSimEngine.dependency_values(extra, :leaf_fluxes; flatten=true) - status.grouped_dependency_total = sum(sum, grouped_values) - status.dependency_total = sum(flattened_values) - return nothing -end - -struct DomainSceneGrowthFluxSumModel <: AbstractDomain_Scene_Growth_Flux_SumModel end - -PlantSimEngine.inputs_(::DomainSceneGrowthFluxSumModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainSceneGrowthFluxSumModel) = (growth_flux_total=0.0,) - -PlantSimEngine.dep(::DomainSceneGrowthFluxSumModel) = ( - leaf_fluxes=PlantSimEngine.AllDomains(kind=:plant, scale=:Leaf, process=:domain_growth_leaf_flux, var=:leaf_flux), -) - -function PlantSimEngine.run!(::DomainSceneGrowthFluxSumModel, models, status, meteo, constants=nothing, extra=nothing) - status.growth_flux_total = sum(PlantSimEngine.dependency_values(extra, :leaf_fluxes; flatten=true)) - return nothing -end - -struct DomainUpdateAllocationModel <: AbstractDomain_Update_AllocationModel end - -PlantSimEngine.inputs_(::DomainUpdateAllocationModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainUpdateAllocationModel) = (leaf_biomass=0.0,) - -function PlantSimEngine.run!(::DomainUpdateAllocationModel, models, status, meteo, constants=nothing, extra=nothing) - status.leaf_biomass = 10.0 - return nothing -end - -struct DomainUpdatePruningModel <: AbstractDomain_Update_PruningModel end - -PlantSimEngine.inputs_(::DomainUpdatePruningModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainUpdatePruningModel) = (leaf_biomass=0.0,) - -function PlantSimEngine.run!(::DomainUpdatePruningModel, models, status, meteo, constants=nothing, extra=nothing) - status.leaf_biomass = 0.0 - return nothing -end - -struct DomainUpdateObserverModel <: AbstractDomain_Update_ObserverModel end - -PlantSimEngine.inputs_(::DomainUpdateObserverModel) = (leaf_biomass=0.0,) -PlantSimEngine.outputs_(::DomainUpdateObserverModel) = (observed_biomass=0.0,) - -function PlantSimEngine.run!(::DomainUpdateObserverModel, models, status, meteo, constants=nothing, extra=nothing) - status.observed_biomass = status.leaf_biomass - return nothing -end - -struct DomainRemovalLeafFluxModel <: AbstractDomain_Removal_Leaf_FluxModel end - -PlantSimEngine.inputs_(::DomainRemovalLeafFluxModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainRemovalLeafFluxModel) = (leaf_flux=0.0,) - -function PlantSimEngine.run!(::DomainRemovalLeafFluxModel, models, status, meteo, constants=nothing, extra=nothing) - status.leaf_flux = 1.0 - return nothing -end - -struct DomainRemovalPruningModel <: AbstractDomain_Removal_PruningModel end - -PlantSimEngine.inputs_(::DomainRemovalPruningModel) = (leaf_flux=Float64[], removed_count=0, removed_node_id=0,) -PlantSimEngine.outputs_(::DomainRemovalPruningModel) = (remaining_leaf_flux=0.0, removed_count=0, removed_node_id=0,) - -function PlantSimEngine.run!(::DomainRemovalPruningModel, models, status, meteo, constants=nothing, extra=nothing) - if status.removed_count == 0 && length(status.leaf_flux) > 1 - leaf_status = first(PlantSimEngine.status(extra)[:Leaf]) - status.removed_node_id = node_id(leaf_status.node) - remove_organ!(leaf_status.node, extra) - status.removed_count = 1 - end - status.remaining_leaf_flux = sum(status.leaf_flux) - return nothing -end - -struct DomainChurnLeafFluxModel <: AbstractDomain_Churn_Leaf_FluxModel end - -PlantSimEngine.inputs_(::DomainChurnLeafFluxModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainChurnLeafFluxModel) = (leaf_flux=0.0,) - -function PlantSimEngine.run!(::DomainChurnLeafFluxModel, models, status, meteo, constants=nothing, extra=nothing) - status.leaf_flux = 1.0 - return nothing -end - -struct DomainChurnLeafControllerModel <: AbstractDomain_Churn_Leaf_ControllerModel end - -PlantSimEngine.inputs_(::DomainChurnLeafControllerModel) = ( - leaf_flux=Float64[], - created_count=0, - removed_count=0, - active_leaf_count=0, - last_removed_node_id=0, -) -PlantSimEngine.outputs_(::DomainChurnLeafControllerModel) = ( - created_count=0, - removed_count=0, - active_leaf_count=0, - last_removed_node_id=0, -) - -function PlantSimEngine.run!(::DomainChurnLeafControllerModel, models, status, meteo, constants=nothing, extra=nothing) - if isempty(status.leaf_flux) - add_organ!(status.node, extra, "+", :Leaf, 2; check=true) - status.created_count += 1 - else - leaf_status = first(PlantSimEngine.status(extra)[:Leaf]) - status.last_removed_node_id = node_id(leaf_status.node) - remove_organ!(leaf_status.node, extra) - status.removed_count += 1 - end - status.active_leaf_count = length(status.leaf_flux) - return nothing -end - -struct DomainSubtreeInternodeFluxModel <: AbstractDomain_Subtree_Internode_FluxModel end -struct DomainSubtreeLeafFluxModel <: AbstractDomain_Subtree_Leaf_FluxModel end - -PlantSimEngine.inputs_(::DomainSubtreeInternodeFluxModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainSubtreeInternodeFluxModel) = (internode_flux=0.0,) - -function PlantSimEngine.run!(::DomainSubtreeInternodeFluxModel, models, status, meteo, constants=nothing, extra=nothing) - status.internode_flux = 2.0 - return nothing -end - -PlantSimEngine.inputs_(::DomainSubtreeLeafFluxModel) = NamedTuple() -PlantSimEngine.outputs_(::DomainSubtreeLeafFluxModel) = (leaf_flux=0.0,) - -function PlantSimEngine.run!(::DomainSubtreeLeafFluxModel, models, status, meteo, constants=nothing, extra=nothing) - status.leaf_flux = 1.0 - return nothing -end - -struct DomainSubtreePruningModel <: AbstractDomain_Subtree_PruningModel end - -PlantSimEngine.inputs_(::DomainSubtreePruningModel) = ( - internode_flux=Float64[], - leaf_flux=Float64[], - removed_count=0, - removed_internode_id=0, - removed_leaf_id=0, - remaining_internode_count=0, - remaining_leaf_count=0, -) -PlantSimEngine.outputs_(::DomainSubtreePruningModel) = ( - removed_count=0, - removed_internode_id=0, - removed_leaf_id=0, - remaining_internode_count=0, - remaining_leaf_count=0, -) - -function PlantSimEngine.run!(::DomainSubtreePruningModel, models, status, meteo, constants=nothing, extra=nothing) - graph_status = PlantSimEngine.status(extra) - if status.removed_count == 0 && !isempty(get(graph_status, :Internode, Status[])) - internode_status = first(graph_status[:Internode]) - leaf_status = first(graph_status[:Leaf]) - status.removed_internode_id = node_id(internode_status.node) - status.removed_leaf_id = node_id(leaf_status.node) - remove_organ!(internode_status.node, extra; recursive=true) - status.removed_count = 1 - end - status.remaining_internode_count = length(status.internode_flux) - status.remaining_leaf_count = length(status.leaf_flux) - return nothing -end - -struct DomainReparentLeafControllerModel <: AbstractDomain_Reparent_Leaf_ControllerModel end - -PlantSimEngine.inputs_(::DomainReparentLeafControllerModel) = ( - leaf_flux=Float64[], - reparented_count=0, - new_parent_id=0, - leaf_parent_id=0, - active_leaf_count=0, -) -PlantSimEngine.outputs_(::DomainReparentLeafControllerModel) = ( - reparented_count=0, - new_parent_id=0, - leaf_parent_id=0, - active_leaf_count=0, -) - -function PlantSimEngine.run!(::DomainReparentLeafControllerModel, models, status, meteo, constants=nothing, extra=nothing) - graph_status = PlantSimEngine.status(extra) - if status.reparented_count == 0 - leaf_status = only(graph_status[:Leaf]) - new_parent_status = graph_status[:Internode][2] - reparent_organ!(leaf_status.node, new_parent_status.node, extra) - status.reparented_count = 1 - status.new_parent_id = node_id(new_parent_status.node) - end - leaf_status = only(graph_status[:Leaf]) - status.leaf_parent_id = node_id(parent(leaf_status.node)) - status.active_leaf_count = length(status.leaf_flux) - return nothing -end - -@testset "Domain simulation: two plants, soil, and daily scene aggregation" begin - hourly_meteo = Weather([ - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) - for _ in 1:25 - ]) - - oil_palm_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStep(Dates.Hour(1)), - ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStep(Dates.Hour(1)), - status=(absorbed_radiation=0.0, transpiration=0.0), - ) - - maize_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainAbsorbedRadiationModel(0.3)) |> TimeStep(Dates.Hour(1)), - ModelSpec(DomainPlantTranspirationModel(0.02)) |> TimeStep(Dates.Hour(1)), - status=(absorbed_radiation=0.0, transpiration=0.0), - ) - - soil_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSoilWaterModel(0.35)) |> TimeStep(Dates.Hour(1)), - ModelSpec(DomainSoilEvaporationModel(0.2)) |> TimeStep(Dates.Hour(1)), - status=(soil_water_content=0.0, evaporation=0.0), - ) - - scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneEvapotranspirationModel()) |> TimeStep(Dates.Day(1)), - status=(evapotranspiration=0.0,), - ) - - simulation_mapping = PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:oil_palm, oil_palm_mapping; kind=:plant), - PlantSimEngine.Domain(:maize, maize_mapping; kind=:plant), - PlantSimEngine.Domain(:soil, soil_mapping; kind=:soil), - PlantSimEngine.Domain(:scene, scene_mapping; kind=:scene), - ) - - domain_rows = PlantSimEngine.explain_domains(simulation_mapping) - @test length(domain_rows) == 4 - @test any(row -> row.domain == :oil_palm && row.kind == :plant, domain_rows) - - model_rows = PlantSimEngine.explain_domain_models(simulation_mapping) - @test length(model_rows) == 7 - @test any(row -> row.domain == :oil_palm && row.process == :domain_absorbed_radiation && haskey(row.meteo_inputs, :Ri_PAR_f), model_rows) - - sim = run!(simulation_mapping, hourly_meteo, check=true) - @test status(sim, :oil_palm).transpiration ≈ 0.01 * 0.5 * 100.0 - @test outputs(sim) === sim.outputs - - schedule = explain_schedule(sim) - @test any(row -> row.domain == :scene && row.dt_seconds == 86_400.0, schedule) - @test any(row -> row.domain == :oil_palm && row.dt_seconds == 3_600.0, schedule) - - deps = PlantSimEngine.explain_domain_dependencies(sim) - @test length(deps) == 3 - @test count(row -> row.dependency == :plant_transpiration, deps) == 2 - @test count(row -> row.dependency == :soil_evaporation, deps) == 1 - @test all(row -> isnothing(row.variable), deps) - - scene_key = PlantSimEngine.DomainModelKey(:scene, :Default, :domain_scene_evapotranspiration) - scene_values = sim.outputs[(scene_key, :evapotranspiration)] - - # Dates.Day(1) currently aligns to step 1, then step 25 when the base step is hourly. - # The second scene value integrates producer values from steps 2:25. - hourly_plant_sum = 0.01 * 0.5 * 100.0 + 0.02 * 0.3 * 100.0 - hourly_soil = 0.2 * (0.35 - 0.001 * 20.0) * 20.0 - expected_daily_et = 24.0 * (hourly_plant_sum + hourly_soil) - - @test length(scene_values) == 2 - @test scene_values[2] ≈ expected_daily_et -end - -@testset "Domain simulation validation" begin - hourly_meteo = Weather([ - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) - for _ in 1:2 - ]) - - plant_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStep(Dates.Hour(1)), - ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStep(Dates.Hour(1)), - status=(absorbed_radiation=0.0, transpiration=0.0), - ) - - raw_domain = PlantSimEngine.Domain( - :plant_kw; - kind=:plant, - mapping=( - ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStep(Dates.Hour(1)), - ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStep(Dates.Hour(1)), - Status(absorbed_radiation=0.0, transpiration=0.0), - ), - ) - @test raw_domain.mapping isa PlantSimEngine.ModelMapping - - @test_throws ErrorException PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:plant, plant_mapping; kind=:plant), - PlantSimEngine.Domain(:plant, plant_mapping; kind=:plant), - ) - - daily_meteo = Weather([ - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) - for _ in 1:25 - ]) - mixed_rate_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStep(Dates.Hour(1)), - ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStep(Dates.Day(1)), - status=(absorbed_radiation=0.0, transpiration=0.0), - ) - mixed_sim = run!( - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:mixed_plant, mixed_rate_mapping; kind=:plant)), - daily_meteo, - check=true, - ) - absorbed_key = PlantSimEngine.DomainModelKey(:mixed_plant, :Default, :domain_absorbed_radiation) - transpiration_key = PlantSimEngine.DomainModelKey(:mixed_plant, :Default, :domain_plant_transpiration) - @test length(mixed_sim.outputs[(absorbed_key, :absorbed_radiation)]) == 25 - @test length(mixed_sim.outputs[(transpiration_key, :transpiration)]) == 2 - - unmatched_scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneEvapotranspirationModel()) |> TimeStep(Dates.Day(1)), - status=(evapotranspiration=0.0,), - ) - unmatched_error = try - run!( - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:scene, unmatched_scene_mapping; kind=:scene)), - hourly_meteo, - check=true, - ) - "" - catch err - sprint(showerror, err) - end - @test occursin("Domain dependency `plant_transpiration`", unmatched_error) - @test occursin("consumer `scene/Default/domain_scene_evapotranspiration`", unmatched_error) - @test occursin("AllDomains(kind=:plant, process=:domain_plant_transpiration, policy=Integrate())", unmatched_error) - @test occursin("Available producers:", unmatched_error) - - multi_process_scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStep(Dates.Hour(1)), - ModelSpec(DomainScenePlantEvapotranspirationModel()) |> TimeStep(Dates.Hour(1)), - status=(absorbed_radiation=0.0, plant_evapotranspiration=0.0), - ) - multi_scene_sim = run!( - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:plant, plant_mapping; kind=:plant), - PlantSimEngine.Domain(:scene, multi_process_scene_mapping; kind=:scene), - ), - hourly_meteo, - check=true, - ) - @test status(multi_scene_sim, :scene).plant_evapotranspiration > 0.0 - - hard_plant_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainHardLeafConductanceModel()) |> TimeStep(Dates.Hour(1)), - ModelSpec(DomainHardLeafEnergyModel()) |> TimeStep(Dates.Hour(1)), - status=(conductance=0.0, leaf_temperature=0.0), - ) - conductance_scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneConductanceSumModel()) |> TimeStep(Dates.Hour(1)), - status=(conductance_sum=0.0,), - ) - hard_sim = run!( - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:hard_plant, hard_plant_mapping; kind=:plant), - PlantSimEngine.Domain(:scene, conductance_scene_mapping; kind=:scene), - ), - hourly_meteo, - check=true, - ) - conductance_key = PlantSimEngine.DomainModelKey(:hard_plant, :Default, :domain_hard_leaf_conductance) - @test hard_sim.outputs[(conductance_key, :conductance)] == [2.0, 2.0] - @test status(hard_sim, :scene).conductance_sum == 2.0 - hard_deps = PlantSimEngine.explain_domain_dependencies(hard_sim) - @test only(hard_deps).variable == :conductance - - hard_target_plant_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainHardTargetSignalModel(2.0)) |> TimeStep(Dates.Hour(1)), - status=(call_count=0, signal=0.0), - ) - hard_target_scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneHardTargetSumModel()) |> TimeStep(Dates.Hour(1)), - status=(hard_target_total=0.0,), - ) - hard_target_sim = run!( - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:hard_target_plant, hard_target_plant_mapping; kind=:plant), - PlantSimEngine.Domain(:scene, hard_target_scene_mapping; kind=:scene), - ), - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)), - check=true, - ) - @test status(hard_target_sim, :hard_target_plant).call_count == 2 - @test status(hard_target_sim, :hard_target_plant).signal ≈ 4.0 - @test status(hard_target_sim, :scene).hard_target_total ≈ 4.0 - @test only(PlantSimEngine.explain_domain_dependencies(hard_target_sim)).mode == :hard_domain - - calls_target_scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneCallsTargetSumModel()) |> - Calls(:plant_signal => Many(kind=:plant, process=:domain_hard_target_signal)) |> - TimeStep(Dates.Hour(1)), - status=(calls_target_total=0.0,), - ) - calls_target_sim = run!( - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:hard_target_plant, hard_target_plant_mapping; kind=:plant), - PlantSimEngine.Domain(:scene, calls_target_scene_mapping; kind=:scene), - ), - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)), - check=true, - ) - @test status(calls_target_sim, :hard_target_plant).call_count == 2 - @test status(calls_target_sim, :hard_target_plant).signal ≈ 4.0 - @test status(calls_target_sim, :scene).calls_target_total ≈ 4.0 - @test only(PlantSimEngine.explain_domain_dependencies(calls_target_sim)).mode == :hard_domain - - route_source = PlantSimEngine.AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration) - bad_route_source = PlantSimEngine.AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:missing_output) - route_selector_error = try - run!( - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:plant, plant_mapping; kind=:plant), - PlantSimEngine.Domain(:scene, multi_process_scene_mapping; kind=:scene); - routes=(PlantSimEngine.Route( - from=bad_route_source, - to=PlantSimEngine.DomainRouteTarget(:scene, var=:plant_transpirations), - ),), - ), - hourly_meteo, - check=true, - ) - "" - catch err - sprint(showerror, err) - end - @test occursin("Route 1 from `AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:missing_output)`", route_selector_error) - @test occursin("Models matching all selector fields except `var=:missing_output`", route_selector_error) - @test occursin("plant/Default/domain_plant_transpiration outputs=(:transpiration)", route_selector_error) - - missing_target_scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneRoutedAggregateModel()) |> TimeStep(Dates.Hour(1)), - status=(daily_plant_transpiration=0.0, daily_routed_total=0.0), - ) - @test_throws "does not contain variable `plant_transpirations`" run!( - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:plant, plant_mapping; kind=:plant), - PlantSimEngine.Domain(:scene, missing_target_scene_mapping; kind=:scene); - routes=(PlantSimEngine.Route( - from=route_source, - to=PlantSimEngine.DomainRouteTarget(:scene, var=:plant_transpirations), - ),), - ), - hourly_meteo, - check=true, - ) - - wrong_process_scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneRoutedAggregateModel()) |> TimeStep(Dates.Hour(1)), - status=(plant_transpirations=[0.0], daily_plant_transpiration=0.0, daily_routed_total=0.0), - ) - @test_throws "does not consume variable `plant_transpirations`" run!( - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:plant, plant_mapping; kind=:plant), - PlantSimEngine.Domain(:scene, wrong_process_scene_mapping; kind=:scene); - routes=(PlantSimEngine.Route( - from=route_source, - to=PlantSimEngine.DomainRouteTarget(:scene, var=:plant_transpirations, process=:domain_scene_routed_aggregate), - ),), - ), - hourly_meteo, - check=true, - ) - - @test_throws "has a selector but uses a single-status ModelMapping" run!( - Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)), - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant, plant_mapping; kind=:plant, selector=:Plant)), - hourly_meteo, - check=true, - ) -end - -@testset "Domain simulation routes" begin - hourly_meteo = Weather([ - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) - for _ in 1:25 - ]) - - oil_palm_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainAbsorbedRadiationModel(0.5)) |> TimeStep(Dates.Hour(1)), - ModelSpec(DomainPlantTranspirationModel(0.01)) |> TimeStep(Dates.Hour(1)), - status=(absorbed_radiation=0.0, transpiration=0.0), - ) - - maize_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainAbsorbedRadiationModel(0.3)) |> TimeStep(Dates.Hour(1)), - ModelSpec(DomainPlantTranspirationModel(0.02)) |> TimeStep(Dates.Hour(1)), - status=(absorbed_radiation=0.0, transpiration=0.0), - ) - - hourly_scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStep(Dates.Hour(1)), - status=(routed_total=0.0,), - ) - - vector_route = PlantSimEngine.Route( - from=PlantSimEngine.AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration), - to=PlantSimEngine.DomainRouteTarget(:scene, var=:plant_transpirations, process=:domain_scene_routed_vector), - cardinality=PlantSimEngine.ManyToOneVector(), - ) - - vector_sim = run!( - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:oil_palm, oil_palm_mapping; kind=:plant), - PlantSimEngine.Domain(:maize, maize_mapping; kind=:plant), - PlantSimEngine.Domain(:scene, hourly_scene_mapping; kind=:scene); - routes=(vector_route,), - ), - hourly_meteo, - check=true, - ) - hourly_plant_sum = 0.01 * 0.5 * 100.0 + 0.02 * 0.3 * 100.0 - @test :plant_transpirations in propertynames(status(vector_sim, :scene)) - @test status(vector_sim, :scene).plant_transpirations ≈ [0.5, 0.6] - @test status(vector_sim, :scene).routed_total ≈ hourly_plant_sum - - route_rows = PlantSimEngine.explain_routes(vector_sim) - @test length(route_rows) == 2 - @test all(row -> row.target_var == :plant_transpirations, route_rows) - @test all(row -> row.cardinality == PlantSimEngine.ManyToOneVector, route_rows) - - inputs_route_scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneRoutedVectorModel()) |> - Inputs(:plant_transpirations => Many(kind=:plant, process=:domain_plant_transpiration, var=:transpiration)) |> - TimeStep(Dates.Hour(1)), - status=(routed_total=0.0,), - ) - inputs_route_sim = run!( - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:oil_palm, oil_palm_mapping; kind=:plant), - PlantSimEngine.Domain(:maize, maize_mapping; kind=:plant), - PlantSimEngine.Domain(:scene, inputs_route_scene_mapping; kind=:scene), - ), - hourly_meteo, - check=true, - ) - @test :plant_transpirations in propertynames(status(inputs_route_sim, :scene)) - @test status(inputs_route_sim, :scene).plant_transpirations ≈ [0.5, 0.6] - @test status(inputs_route_sim, :scene).routed_total ≈ hourly_plant_sum - input_route_rows = PlantSimEngine.explain_routes(inputs_route_sim) - @test length(input_route_rows) == 2 - @test all(row -> row.target_var == :plant_transpirations, input_route_rows) - @test all(row -> row.cardinality == PlantSimEngine.ManyToOneVector, input_route_rows) - - reordered_collector_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStep(Dates.Hour(1)), - status=(plant_transpirations=[0.0], routed_total=0.0), - ) - reordered_route = PlantSimEngine.Route( - from=PlantSimEngine.AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration), - to=PlantSimEngine.DomainRouteTarget(:collector, var=:plant_transpirations, process=:domain_scene_routed_vector), - cardinality=PlantSimEngine.ManyToOneVector(), - ) - reordered_sim = run!( - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:collector, reordered_collector_mapping; kind=:soil), - PlantSimEngine.Domain(:oil_palm, oil_palm_mapping; kind=:plant); - routes=(reordered_route,), - ), - hourly_meteo, - check=true, - ) - @test status(reordered_sim, :collector).plant_transpirations ≈ [0.5] - @test status(reordered_sim, :collector).routed_total ≈ 0.5 - - cyclic_scene_source_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStep(Dates.Hour(1)), - status=(plant_transpirations=[0.0], routed_total=0.0), - ) - cyclic_route = PlantSimEngine.Route( - from=PlantSimEngine.AllDomains(kind=:scene, process=:domain_scene_routed_vector, var=:routed_total), - to=PlantSimEngine.DomainRouteTarget(:oil_palm, var=:absorbed_radiation, process=:domain_plant_transpiration), - cardinality=PlantSimEngine.ManyToOneAggregate(sum), - ) - @test_throws "Cyclic domain run-order constraints" run!( - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:oil_palm, oil_palm_mapping; kind=:plant), - PlantSimEngine.Domain(:scene, cyclic_scene_source_mapping; kind=:scene); - routes=(cyclic_route,), - ), - hourly_meteo, - check=true, - ) - - daily_scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneRoutedAggregateModel()) |> TimeStep(Dates.Day(1)), - status=(daily_plant_transpiration=0.0, daily_routed_total=0.0), - ) - - aggregate_route = PlantSimEngine.Route( - from=PlantSimEngine.AllDomains(kind=:plant, process=:domain_plant_transpiration, var=:transpiration), - to=PlantSimEngine.DomainRouteTarget(:scene, var=:daily_plant_transpiration, process=:domain_scene_routed_aggregate), - cardinality=PlantSimEngine.ManyToOneAggregate(sum), - policy=Integrate(), - ) - - aggregate_sim = run!( - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:oil_palm, oil_palm_mapping; kind=:plant), - PlantSimEngine.Domain(:maize, maize_mapping; kind=:plant), - PlantSimEngine.Domain(:scene, daily_scene_mapping; kind=:scene); - routes=(aggregate_route,), - ), - hourly_meteo, - check=true, - ) - @test status(aggregate_sim, :scene).daily_plant_transpiration ≈ 24.0 * hourly_plant_sum - @test status(aggregate_sim, :scene).daily_routed_total ≈ 24.0 * hourly_plant_sum - - aggregate_rows = PlantSimEngine.explain_routes(aggregate_sim) - @test length(aggregate_rows) == 2 - @test all(row -> row.dt_seconds == 86_400.0, aggregate_rows) - @test all(row -> row.cardinality <: PlantSimEngine.ManyToOneAggregate, aggregate_rows) -end - -@testset "Domain graph dependency policy with changing topology" begin - first_step = PlantSimEngine.DomainNodeValues([11, 12], Any[1.0, 2.0]) - second_step = PlantSimEngine.DomainNodeValues([11, 13], Any[3.0, 4.0]) - - integrated = PlantSimEngine._apply_dependency_policy(Any[first_step, second_step], Integrate()) - @test integrated.ids == [11, 12, 13] - @test integrated.values ≈ [4.0, 2.0, 4.0] - - aggregated = PlantSimEngine._apply_dependency_policy(Any[first_step, second_step], Aggregate()) - @test aggregated.ids == [11, 12, 13] - @test aggregated.values ≈ [2.0, 2.0, 4.0] - - no_ids_first_step = PlantSimEngine.DomainNodeValues(Any[1.0, 2.0]) - no_ids_second_step = PlantSimEngine.DomainNodeValues(Any[3.0]) - @test_throws "changing vector lengths" PlantSimEngine._apply_dependency_policy( - Any[no_ids_first_step, no_ids_second_step], - Integrate(), - ) -end - -@testset "Staged MTG-backed domain simulation" begin - scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - oil_palm = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - oil_axis = Node(oil_palm, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - Node(oil_axis, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 3)) - maize = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 2, 1)) - maize_axis = Node(maize, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - Node(maize_axis, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 3)) - - meteo = Weather([ - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)), - Atmosphere(T=25.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)), - ]) - - oil_leaf_mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(DomainMTGLeafFluxModel(0.5)) |> TimeStep(Dates.Hour(1)), - ), - ) - maize_leaf_mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(DomainMTGLeafFluxModel(0.7)) |> TimeStep(Dates.Hour(1)), - ), - ) - scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneRoutedVectorModel()) |> TimeStep(Dates.Hour(1)), - status=(plant_transpirations=[0.0], routed_total=0.0), - ) - route = PlantSimEngine.Route( - from=PlantSimEngine.AllDomains(kind=:plant, scale=:Leaf, process=:domain_mtg_leaf_flux, var=:leaf_flux), - to=PlantSimEngine.DomainRouteTarget(:scene, var=:plant_transpirations, process=:domain_scene_routed_vector), - cardinality=PlantSimEngine.ManyToOneVector(), - ) - - sim = run!( - scene, - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:oil_palm, oil_leaf_mapping; kind=:plant, selector=oil_palm), - PlantSimEngine.Domain(:maize, maize_leaf_mapping; kind=:plant, selector=maize), - PlantSimEngine.Domain(:scene, scene_mapping; kind=:scene); - routes=(route,), - ), - meteo, - check=true, - ) - - @test length(status(sim, :oil_palm, :Leaf)) == 1 - @test length(status(sim, :maize, :Leaf)) == 1 - @test length(status(sim, :Leaf)) == 2 - @test length(status(sim, :Default)) == 1 - @test status(sim, :scene).plant_transpirations ≈ [0.5, 0.7] - @test status(sim, :scene).routed_total ≈ 1.2 - @test sim.outputs[(PlantSimEngine.DomainModelKey(:scene, :Default, :domain_scene_routed_vector), :routed_total)] ≈ [1.2, 1.2] - @test sim.outputs[(PlantSimEngine.DomainModelKey(:oil_palm, :Leaf, :domain_mtg_leaf_flux), :leaf_flux)] == [[0.5], [0.5]] - @test sim.outputs[(PlantSimEngine.DomainModelKey(:maize, :Leaf, :domain_mtg_leaf_flux), :leaf_flux)] == [[0.7], [0.7]] - status_rows = PlantSimEngine.explain_domain_statuses(sim) - @test sum(row -> row.scale == :Leaf, status_rows) == 2 - @test only(row.nstatuses for row in status_rows if row.domain == :scene && row.scale == :Default) == 1 - - forest_leaf_mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(DomainMTGLeafFluxModel(0.4)) |> TimeStep(Dates.Hour(1)), - ), - ) - forest_sim = run!( - scene, - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:forest, forest_leaf_mapping; kind=:plant, selector=:Plant), - PlantSimEngine.Domain(:scene, scene_mapping; kind=:scene); - routes=(route,), - ), - meteo, - check=true, - ) - @test length(status(forest_sim, :forest, :Leaf)) == 2 - @test length(status(forest_sim, :Leaf)) == 2 - @test status(forest_sim, :scene).plant_transpirations ≈ [0.4, 0.4] - @test status(forest_sim, :scene).routed_total ≈ 0.8 - @test forest_sim.outputs[(PlantSimEngine.DomainModelKey(:forest, :Leaf, :domain_mtg_leaf_flux), :leaf_flux)] == [[0.4, 0.4], [0.4, 0.4]] - @test only(row.nstatuses for row in PlantSimEngine.explain_domain_statuses(forest_sim) if row.domain == :forest && row.scale == :Leaf) == 2 - - @test_throws "matched overlapping MTG roots" run!( - scene, - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain( - :overlapping_forest, - forest_leaf_mapping; - kind=:plant, - selector=node -> symbol(node) in (:Plant, :Leaf), - ), - ), - meteo, - check=true, - ) - - dependency_scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneDependencyFluxSumModel()) |> TimeStep(Dates.Hour(1)), - status=(dependency_total=0.0, grouped_dependency_total=0.0), - ) - dependency_sim = run!( - scene, - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:oil_palm, oil_leaf_mapping; kind=:plant, selector=oil_palm), - PlantSimEngine.Domain(:maize, maize_leaf_mapping; kind=:plant, selector=maize), - PlantSimEngine.Domain(:scene, dependency_scene_mapping; kind=:scene), - ), - meteo, - check=true, - ) - @test status(dependency_sim, :scene).dependency_total ≈ 1.2 - @test status(dependency_sim, :scene).grouped_dependency_total ≈ 1.2 - - soil_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSoilWaterModel(0.35)) |> TimeStep(Dates.Hour(1)), - status=(soil_water_content=0.0,), - ) - soil_leaf_mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(DomainMTGLeafSoilFluxModel(2.0)) |> TimeStep(Dates.Hour(1)), - ), - ) - soil_route = PlantSimEngine.Route( - from=PlantSimEngine.AllDomains(kind=:soil, process=:domain_soil_water, var=:soil_water_content), - to=PlantSimEngine.DomainRouteTarget(:oil_palm, scale=:Leaf, var=:soil_signal, process=:domain_mtg_leaf_soil_flux), - cardinality=PlantSimEngine.OneToManyBroadcast(), - ) - soil_to_graph_sim = run!( - scene, - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:soil, soil_mapping; kind=:soil), - PlantSimEngine.Domain(:oil_palm, soil_leaf_mapping; kind=:plant, selector=oil_palm); - routes=(soil_route,), - ), - meteo, - check=true, - ) - expected_soil_signals = [0.35 - 0.001 * 20.0, 0.35 - 0.001 * 25.0] - @test only(status(soil_to_graph_sim, :oil_palm, :Leaf)).soil_signal ≈ expected_soil_signals[2] - @test soil_to_graph_sim.outputs[(PlantSimEngine.DomainModelKey(:oil_palm, :Leaf, :domain_mtg_leaf_soil_flux), :leaf_flux)] == [[2.0 * expected_soil_signals[1]], [2.0 * expected_soil_signals[2]]] - - reordered_soil_to_graph_sim = run!( - scene, - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:oil_palm, soil_leaf_mapping; kind=:plant, selector=oil_palm), - PlantSimEngine.Domain(:soil, soil_mapping; kind=:soil); - routes=(soil_route,), - ), - meteo, - check=true, - ) - @test only(status(reordered_soil_to_graph_sim, :oil_palm, :Leaf)).soil_signal ≈ expected_soil_signals[2] - @test reordered_soil_to_graph_sim.outputs[(PlantSimEngine.DomainModelKey(:oil_palm, :Leaf, :domain_mtg_leaf_soil_flux), :leaf_flux)] == [[2.0 * expected_soil_signals[1]], [2.0 * expected_soil_signals[2]]] -end - -@testset "Hard-domain targets from MTG-backed domains" begin - scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 2, 2)) - - meteo = Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) - plant_mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(DomainHardTargetLeafCounterModel(1.5)) |> TimeStep(Dates.Hour(1)), - Status(call_count=0, leaf_signal=0.0), - ), - ) - scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneHardTargetLeafSumModel()) |> TimeStep(Dates.Hour(1)), - status=(leaf_hard_target_total=0.0,), - ) - - sim = run!( - scene, - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:hard_target_plant, plant_mapping; kind=:plant, selector=plant), - PlantSimEngine.Domain(:scene, scene_mapping; kind=:scene), - ), - meteo, - check=true, - ) - - leaf_statuses = status(sim, :hard_target_plant, :Leaf) - @test length(leaf_statuses) == 2 - @test all(st -> st.call_count == 1, leaf_statuses) - @test all(st -> st.leaf_signal ≈ 1.5, leaf_statuses) - @test status(sim, :scene).leaf_hard_target_total ≈ 3.0 - @test sim.outputs[(PlantSimEngine.DomainModelKey(:hard_target_plant, :Leaf, :domain_hard_target_leaf_counter), :leaf_signal)] == [1.5, 1.5] -end - -@testset "MTG-backed domain growth registration" begin - scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - - meteo = Weather([ - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) - for _ in 1:2 - ]) - - growth_mapping = PlantSimEngine.ModelMapping( - :Plant => ( - ModelSpec(DomainGrowthLeafEmergenceModel()) |> TimeStep(Dates.Hour(1)), - ), - :Leaf => ( - ModelSpec(DomainGrowthLeafFluxModel(0.9)) |> - PlantSimEngine.MultiScaleModel([:grown_leaves => (:Plant => :grown_leaves)]) |> - TimeStep(Dates.Hour(1)), - ), - ) - scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(DomainSceneGrowthFluxSumModel()) |> TimeStep(Dates.Hour(1)), - status=(growth_flux_total=0.0,), - ) - - sim = run!( - scene, - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:growing_plant, growth_mapping; kind=:plant, selector=plant), - PlantSimEngine.Domain(:scene, scene_mapping; kind=:scene), - ), - meteo, - check=true, - ) - - @test length(status(sim, :growing_plant, :Leaf)) == 1 - @test length(status(sim, :Leaf)) == 1 - @test only(status(sim, :growing_plant, :Leaf)).grown_leaves == 1.0 - @test only(status(sim, :growing_plant, :Leaf)).leaf_flux ≈ 0.9 - @test status(sim, :scene).growth_flux_total ≈ 0.9 - @test sim.outputs[(PlantSimEngine.DomainModelKey(:growing_plant, :Leaf, :domain_growth_leaf_flux), :leaf_flux)] == [[0.9], [0.9]] - @test sim.outputs[(PlantSimEngine.DomainModelKey(:scene, :Default, :domain_scene_growth_flux_sum), :growth_flux_total)] ≈ [0.9, 0.9] - @test only(row.nstatuses for row in PlantSimEngine.explain_domain_statuses(sim) if row.domain == :growing_plant && row.scale == :Leaf) == 1 - - multirate_scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - multirate_plant = Node(multirate_scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - multirate_meteo = Weather([ - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) - for _ in 1:25 - ]) - multirate_mapping = PlantSimEngine.ModelMapping( - :Plant => ( - ModelSpec(DomainGrowthLeafEmergenceModel()) |> TimeStep(Dates.Hour(1)), - ModelSpec(DomainGrowthIntegratedFluxModel()) |> - PlantSimEngine.MultiScaleModel([:leaf_flux => [:Leaf]]) |> - PlantSimEngine.InputBindings(; leaf_flux=(process=:domain_growth_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=Integrate())) |> - TimeStep(Dates.Day(1)), - ), - :Leaf => ( - ModelSpec(DomainGrowthLeafFluxModel(0.9)) |> - PlantSimEngine.MultiScaleModel([:grown_leaves => (:Plant => :grown_leaves)]) |> - TimeStep(Dates.Hour(1)), - ), - ) - multirate_sim = run!( - multirate_scene, - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:growing_plant, multirate_mapping; kind=:plant, selector=multirate_plant)), - multirate_meteo, - check=true, - ) - - @test length(status(multirate_sim, :growing_plant, :Leaf)) == 1 - @test only(status(multirate_sim, :growing_plant, :Plant)).integrated_leaf_flux ≈ 24.0 * 0.9 - integrated_outputs = multirate_sim.outputs[(PlantSimEngine.DomainModelKey(:growing_plant, :Plant, :domain_growth_integrated_flux), :integrated_leaf_flux)] - @test only.(integrated_outputs) ≈ [0.9, 24.0 * 0.9] - @test length(integrated_outputs) == 2 -end - -@testset "MTG-backed domain variable updates" begin - scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - - meteo = Weather([ - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) - for _ in 1:2 - ]) - - update_mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(DomainUpdateAllocationModel()) |> TimeStep(Dates.Hour(1)), - ModelSpec(DomainUpdatePruningModel()) |> - Updates(:leaf_biomass; after=:domain_update_allocation) |> - TimeStep(Dates.Hour(1)), - ModelSpec(DomainUpdateObserverModel()) |> TimeStep(Dates.Hour(1)), - ), - ) - - resolved_update_specs = resolved_model_specs(update_mapping) - inferred_update_binding = input_bindings(resolved_update_specs[:Leaf][:domain_update_observer]).leaf_biomass - @test inferred_update_binding.process == :domain_update_pruning - - sim = run!( - scene, - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:updated_plant, update_mapping; kind=:plant, selector=plant)), - meteo, - check=true, - ) - - leaf_status = only(status(sim, :updated_plant, :Leaf)) - @test leaf_status.leaf_biomass == 0.0 - @test leaf_status.observed_biomass == 0.0 - @test sim.outputs[(PlantSimEngine.DomainModelKey(:updated_plant, :Leaf, :domain_update_pruning), :leaf_biomass)] == [[0.0], [0.0]] - @test sim.outputs[(PlantSimEngine.DomainModelKey(:updated_plant, :Leaf, :domain_update_observer), :observed_biomass)] == [[0.0], [0.0]] -end - -@testset "MTG-backed domain leaf removal registration" begin - scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 2, 2)) - - meteo = Weather([ - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) - for _ in 1:2 - ]) - - removal_mapping = PlantSimEngine.ModelMapping( - :Plant => ( - ModelSpec(DomainRemovalPruningModel()) |> - PlantSimEngine.MultiScaleModel([:leaf_flux => [:Leaf]]) |> - TimeStep(Dates.Hour(1)), - Status(removed_count=0, removed_node_id=0, remaining_leaf_flux=0.0), - ), - :Leaf => ( - ModelSpec(DomainRemovalLeafFluxModel()) |> TimeStep(Dates.Hour(1)), - ), - ) - - sim = run!( - scene, - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:pruned_plant, removal_mapping; kind=:plant, selector=plant)), - meteo, - check=true, - ) - - plant_status = only(status(sim, :pruned_plant, :Plant)) - @test length(status(sim, :pruned_plant, :Leaf)) == 1 - @test length(status(sim, :Leaf)) == 1 - @test length(plant_status.leaf_flux) == 1 - @test plant_status.removed_count == 1 - @test plant_status.removed_node_id > 0 - @test plant_status.remaining_leaf_flux ≈ 1.0 - @test sim.outputs[(PlantSimEngine.DomainModelKey(:pruned_plant, :Leaf, :domain_removal_leaf_flux), :leaf_flux)] == [[1.0], [1.0]] - @test sim.outputs[(PlantSimEngine.DomainModelKey(:pruned_plant, :Plant, :domain_removal_pruning), :remaining_leaf_flux)] == [[1.0], [1.0]] - @test_throws ErrorException remove_organ!(plant, only(sim.domain_states[:pruned_plant].simulations)) - - multirate_scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - multirate_plant = Node(multirate_scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - Node(multirate_plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - Node(multirate_plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 2, 2)) - multirate_removal_mapping = PlantSimEngine.ModelMapping( - :Plant => ( - ModelSpec(DomainRemovalPruningModel()) |> - PlantSimEngine.MultiScaleModel([:leaf_flux => [:Leaf]]) |> - PlantSimEngine.InputBindings(; leaf_flux=(process=:domain_removal_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=Integrate())) |> - TimeStep(Dates.Day(1)), - Status(removed_count=0, removed_node_id=0, remaining_leaf_flux=0.0), - ), - :Leaf => ( - ModelSpec(DomainRemovalLeafFluxModel()) |> TimeStep(Dates.Hour(1)), - ), - ) - - multirate_sim = run!( - multirate_scene, - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:pruned_plant, multirate_removal_mapping; kind=:plant, selector=multirate_plant)), - meteo, - check=true, - ) - - multirate_plant_status = only(status(multirate_sim, :pruned_plant, :Plant)) - multirate_graph = only(multirate_sim.domain_states[:pruned_plant].simulations) - @test length(status(multirate_sim, :pruned_plant, :Leaf)) == 1 - @test length(multirate_plant_status.leaf_flux) == 1 - @test multirate_plant_status.remaining_leaf_flux ≈ 1.0 - @test all(key -> key.node_id != multirate_plant_status.removed_node_id, keys(multirate_graph.temporal_state.streams)) - @test all(key -> key.node_id != multirate_plant_status.removed_node_id, keys(multirate_graph.temporal_state.caches)) -end - -@testset "MTG-backed domain repeated create/remove churn" begin - scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - meteo = Weather([ - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) - for _ in 1:4 - ]) - - churn_mapping = PlantSimEngine.ModelMapping( - :Plant => ( - ModelSpec(DomainChurnLeafControllerModel()) |> - PlantSimEngine.MultiScaleModel([:leaf_flux => [:Leaf]]) |> - PlantSimEngine.InputBindings(; leaf_flux=(process=:domain_churn_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=HoldLast())) |> - TimeStep(Dates.Hour(1)), - Status(created_count=0, removed_count=0, active_leaf_count=0, last_removed_node_id=0), - ), - :Leaf => ( - ModelSpec(DomainChurnLeafFluxModel()) |> TimeStep(Dates.Hour(1)), - ), - ) - - sim = run!( - scene, - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:churn_plant, churn_mapping; kind=:plant, selector=plant)), - meteo, - check=true, - ) - - plant_status = only(status(sim, :churn_plant, :Plant)) - graph_sim = only(sim.domain_states[:churn_plant].simulations) - @test plant_status.created_count == 2 - @test plant_status.removed_count == 2 - @test plant_status.active_leaf_count == 0 - @test length(plant_status.leaf_flux) == 0 - @test get(status(graph_sim), :Leaf, Status[]) == Status[] - @test get(status(sim.domain_states[:churn_plant]), :Leaf, Status[]) == Status[] - @test status(sim, :churn_plant, :Leaf) == Status[] - @test status(sim, :Leaf) == Status[] - @test only(row.nstatuses for row in PlantSimEngine.explain_domain_statuses(sim) if row.domain == :churn_plant && row.scale == :Leaf) == 0 - @test all(key -> key.scale != :Leaf, keys(graph_sim.temporal_state.streams)) - @test all(key -> key.scale != :Leaf, keys(graph_sim.temporal_state.caches)) - @test sim.outputs[(PlantSimEngine.DomainModelKey(:churn_plant, :Plant, :domain_churn_leaf_controller), :created_count)] == [[1], [1], [2], [2]] - @test sim.outputs[(PlantSimEngine.DomainModelKey(:churn_plant, :Plant, :domain_churn_leaf_controller), :removed_count)] == [[0], [1], [1], [2]] - @test sim.outputs[(PlantSimEngine.DomainModelKey(:churn_plant, :Plant, :domain_churn_leaf_controller), :active_leaf_count)] == [[1], [0], [1], [0]] -end - -@testset "MTG-backed domain recursive subtree removal" begin - scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - internode = Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - leaf = Node(internode, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 3)) - meteo = Weather([ - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) - for _ in 1:2 - ]) - - subtree_mapping = PlantSimEngine.ModelMapping( - :Plant => ( - ModelSpec(DomainSubtreePruningModel()) |> - PlantSimEngine.MultiScaleModel([ - :internode_flux => [:Internode], - :leaf_flux => [:Leaf], - ]) |> - PlantSimEngine.InputBindings(; - internode_flux=(process=:domain_subtree_internode_flux, scale=:Internode, var=:internode_flux, policy=HoldLast()), - leaf_flux=(process=:domain_subtree_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=HoldLast()), - ) |> - TimeStep(Dates.Hour(1)), - Status( - removed_count=0, - removed_internode_id=0, - removed_leaf_id=0, - remaining_internode_count=0, - remaining_leaf_count=0, - ), - ), - :Internode => ( - ModelSpec(DomainSubtreeInternodeFluxModel()) |> TimeStep(Dates.Hour(1)), - ), - :Leaf => ( - ModelSpec(DomainSubtreeLeafFluxModel()) |> TimeStep(Dates.Hour(1)), - ), - ) - - sim = run!( - scene, - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:subtree_plant, subtree_mapping; kind=:plant, selector=plant)), - meteo, - check=true, - ) - - plant_status = only(status(sim, :subtree_plant, :Plant)) - graph_sim = only(sim.domain_states[:subtree_plant].simulations) - @test plant_status.removed_count == 1 - @test plant_status.removed_internode_id == node_id(internode) - @test plant_status.removed_leaf_id == node_id(leaf) - @test plant_status.remaining_internode_count == 0 - @test plant_status.remaining_leaf_count == 0 - @test length(plant_status.internode_flux) == 0 - @test length(plant_status.leaf_flux) == 0 - @test get(status(graph_sim), :Internode, Status[]) == Status[] - @test get(status(graph_sim), :Leaf, Status[]) == Status[] - @test status(sim, :subtree_plant, :Internode) == Status[] - @test status(sim, :subtree_plant, :Leaf) == Status[] - @test status(sim, :Internode) == Status[] - @test status(sim, :Leaf) == Status[] - @test only(row.nstatuses for row in PlantSimEngine.explain_domain_statuses(sim) if row.domain == :subtree_plant && row.scale == :Internode) == 0 - @test only(row.nstatuses for row in PlantSimEngine.explain_domain_statuses(sim) if row.domain == :subtree_plant && row.scale == :Leaf) == 0 - @test all(key -> key.node_id != plant_status.removed_internode_id, keys(graph_sim.temporal_state.streams)) - @test all(key -> key.node_id != plant_status.removed_leaf_id, keys(graph_sim.temporal_state.streams)) - @test all(key -> key.node_id != plant_status.removed_internode_id, keys(graph_sim.temporal_state.caches)) - @test all(key -> key.node_id != plant_status.removed_leaf_id, keys(graph_sim.temporal_state.caches)) - @test sim.outputs[(PlantSimEngine.DomainModelKey(:subtree_plant, :Internode, :domain_subtree_internode_flux), :internode_flux)] == [[], []] - @test sim.outputs[(PlantSimEngine.DomainModelKey(:subtree_plant, :Leaf, :domain_subtree_leaf_flux), :leaf_flux)] == [[], []] - @test sim.outputs[(PlantSimEngine.DomainModelKey(:subtree_plant, :Plant, :domain_subtree_pruning), :remaining_internode_count)] == [[0], [0]] - @test sim.outputs[(PlantSimEngine.DomainModelKey(:subtree_plant, :Plant, :domain_subtree_pruning), :remaining_leaf_count)] == [[0], [0]] -end - -@testset "MTG-backed domain topology reparenting" begin - scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - internode_1 = Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - internode_2 = Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Internode, 2, 2)) - leaf = Node(internode_1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 3)) - meteo = Weather([ - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, Ri_PAR_f=100.0, duration=Dates.Hour(1)) - for _ in 1:2 - ]) - - reparent_mapping = PlantSimEngine.ModelMapping( - :Plant => ( - ModelSpec(DomainReparentLeafControllerModel()) |> - PlantSimEngine.MultiScaleModel([:leaf_flux => [:Leaf]]) |> - PlantSimEngine.InputBindings(; leaf_flux=(process=:domain_subtree_leaf_flux, scale=:Leaf, var=:leaf_flux, policy=HoldLast())) |> - TimeStep(Dates.Hour(1)), - Status(reparented_count=0, new_parent_id=0, leaf_parent_id=0, active_leaf_count=0), - ), - :Internode => ( - ModelSpec(DomainSubtreeInternodeFluxModel()) |> TimeStep(Dates.Hour(1)), - ), - :Leaf => ( - ModelSpec(DomainSubtreeLeafFluxModel()) |> TimeStep(Dates.Hour(1)), - ), - ) - - sim = run!( - scene, - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:reparented_plant, reparent_mapping; kind=:plant, selector=plant)), - meteo, - check=true, - ) - - plant_status = only(status(sim, :reparented_plant, :Plant)) - graph_sim = only(sim.domain_states[:reparented_plant].simulations) - @test parent(leaf) === internode_2 - @test !any(node -> node === leaf, children(internode_1)) - @test count(node -> node === leaf, children(internode_2)) == 1 - @test plant_status.reparented_count == 1 - @test plant_status.new_parent_id == node_id(internode_2) - @test plant_status.leaf_parent_id == node_id(internode_2) - @test plant_status.active_leaf_count == 1 - @test length(plant_status.leaf_flux) == 1 - @test length(status(sim, :reparented_plant, :Internode)) == 2 - @test length(status(sim, :reparented_plant, :Leaf)) == 1 - @test all(key -> key.node_id != 0, keys(graph_sim.temporal_state.streams)) - @test sim.outputs[(PlantSimEngine.DomainModelKey(:reparented_plant, :Leaf, :domain_subtree_leaf_flux), :leaf_flux)] == [[1.0], [1.0]] - @test sim.outputs[(PlantSimEngine.DomainModelKey(:reparented_plant, :Plant, :domain_reparent_leaf_controller), :leaf_parent_id)] == [[node_id(internode_2)], [node_id(internode_2)]] - @test_throws ErrorException reparent_organ!(internode_2, leaf, graph_sim) -end diff --git a/test/test-environment-backends.jl b/test/test-environment-backends.jl index 1100da23d..366a03fb5 100644 --- a/test/test-environment-backends.jl +++ b/test/test-environment-backends.jl @@ -1,375 +1,33 @@ using Dates -PlantSimEngine.@process "environment_probe" verbose = false -PlantSimEngine.@process "environment_temperature_update" verbose = false -PlantSimEngine.@process "environment_bad_output" verbose = false -PlantSimEngine.@process "environment_graph_leaf_probe" verbose = false -PlantSimEngine.@process "environment_graph_temperature_update" verbose = false -PlantSimEngine.@process "environment_scene_hard_graph_update" verbose = false - -struct EnvironmentProbeModel <: AbstractEnvironment_ProbeModel end -struct EnvironmentTemperatureUpdateModel <: AbstractEnvironment_Temperature_UpdateModel end -struct EnvironmentBadOutputModel <: AbstractEnvironment_Bad_OutputModel end -struct EnvironmentGraphLeafProbeModel <: AbstractEnvironment_Graph_Leaf_ProbeModel end -struct EnvironmentGraphTemperatureUpdateModel <: AbstractEnvironment_Graph_Temperature_UpdateModel end -struct EnvironmentSceneHardGraphUpdateModel <: AbstractEnvironment_Scene_Hard_Graph_UpdateModel end - -PlantSimEngine.inputs_(::EnvironmentProbeModel) = NamedTuple() -PlantSimEngine.outputs_(::EnvironmentProbeModel) = (meteo_seen=0.0,) -PlantSimEngine.meteo_inputs_(::EnvironmentProbeModel) = (T=0.0, CO2=400.0) - -function PlantSimEngine.run!(::EnvironmentProbeModel, models, status, meteo, constants=nothing, extra=nothing) - status.meteo_seen = meteo.T + 0.001 * meteo.CO2 - return nothing -end - -PlantSimEngine.inputs_(::EnvironmentTemperatureUpdateModel) = NamedTuple() -PlantSimEngine.outputs_(::EnvironmentTemperatureUpdateModel) = (T=0.0,) -PlantSimEngine.meteo_inputs_(::EnvironmentTemperatureUpdateModel) = (T=0.0,) -PlantSimEngine.meteo_outputs_(::EnvironmentTemperatureUpdateModel) = (T=0.0,) - -function PlantSimEngine.run!(::EnvironmentTemperatureUpdateModel, models, status, meteo, constants=nothing, extra=nothing) - status.T = meteo.T + 1.0 - return nothing -end - -PlantSimEngine.inputs_(::EnvironmentBadOutputModel) = NamedTuple() -PlantSimEngine.outputs_(::EnvironmentBadOutputModel) = NamedTuple() -PlantSimEngine.meteo_inputs_(::EnvironmentBadOutputModel) = (T=0.0,) -PlantSimEngine.meteo_outputs_(::EnvironmentBadOutputModel) = (T=0.0,) - -function PlantSimEngine.run!(::EnvironmentBadOutputModel, models, status, meteo, constants=nothing, extra=nothing) - return nothing -end - -PlantSimEngine.inputs_(::EnvironmentGraphLeafProbeModel) = NamedTuple() -PlantSimEngine.outputs_(::EnvironmentGraphLeafProbeModel) = (meteo_seen=0.0,) -PlantSimEngine.meteo_inputs_(::EnvironmentGraphLeafProbeModel) = (T=0.0, CO2=400.0) - -function PlantSimEngine.run!(::EnvironmentGraphLeafProbeModel, models, status, meteo, constants=nothing, extra=nothing) - status.meteo_seen = meteo.T + 0.001 * meteo.CO2 - return nothing -end - -PlantSimEngine.inputs_(::EnvironmentGraphTemperatureUpdateModel) = NamedTuple() -PlantSimEngine.outputs_(::EnvironmentGraphTemperatureUpdateModel) = (T=0.0,) -PlantSimEngine.meteo_inputs_(::EnvironmentGraphTemperatureUpdateModel) = (T=0.0,) -PlantSimEngine.meteo_outputs_(::EnvironmentGraphTemperatureUpdateModel) = (T=0.0,) - -function PlantSimEngine.run!(::EnvironmentGraphTemperatureUpdateModel, models, status, meteo, constants=nothing, extra=nothing) - status.T = meteo.T + 2.0 - return nothing -end - -PlantSimEngine.dep(::EnvironmentSceneHardGraphUpdateModel) = ( - leaf_temperature=PlantSimEngine.HardDomains(kind=:plant, scale=:Leaf, process=:environment_graph_temperature_update), -) -PlantSimEngine.inputs_(::EnvironmentSceneHardGraphUpdateModel) = NamedTuple() -PlantSimEngine.outputs_(::EnvironmentSceneHardGraphUpdateModel) = (hard_temperature_sum=0.0,) - -function PlantSimEngine.run!(::EnvironmentSceneHardGraphUpdateModel, models, status, meteo, constants=nothing, extra=nothing) - targets = PlantSimEngine.dependency_targets(extra, :leaf_temperature) - for target in targets - PlantSimEngine.run_target!(target; publish=true) - end - status.hard_temperature_sum = sum(target.status.T for target in targets) - return nothing -end - -struct ProbeEnvironmentBackend <: AbstractEnvironmentBackend - nsteps::Int - base_seconds::Float64 -end - -PlantSimEngine.get_nsteps(backend::ProbeEnvironmentBackend) = backend.nsteps -PlantSimEngine.base_step_seconds(backend::ProbeEnvironmentBackend) = backend.base_seconds -PlantSimEngine.environment_variables(::ProbeEnvironmentBackend) = Set([:T, :CO2, :Ca]) - -function PlantSimEngine.sample( - ::ProbeEnvironmentBackend, - variable::Symbol, - support::EnvironmentSupport, - time -) - variable == :CO2 && return 410.0 - variable == :Ca && return 420.0 - variable == :T || error("Unexpected variable `$(variable)`.") - offset = support.domain == :plant_b ? 100.0 : 0.0 - return offset + 10.0 + float(time) -end - -struct MissingCO2EnvironmentBackend <: AbstractEnvironmentBackend end - -PlantSimEngine.get_nsteps(::MissingCO2EnvironmentBackend) = 1 -PlantSimEngine.base_step_seconds(::MissingCO2EnvironmentBackend) = 3600.0 -PlantSimEngine.environment_variables(::MissingCO2EnvironmentBackend) = Set([:T]) -PlantSimEngine.sample(::MissingCO2EnvironmentBackend, variable::Symbol, support::EnvironmentSupport, time) = 20.0 - -mutable struct ScatteringEnvironmentBackend <: AbstractEnvironmentBackend - nsteps::Int - base_seconds::Float64 - writes::Vector{NamedTuple} - index_updates::Vector{Any} -end - -PlantSimEngine.get_nsteps(backend::ScatteringEnvironmentBackend) = backend.nsteps -PlantSimEngine.base_step_seconds(backend::ScatteringEnvironmentBackend) = backend.base_seconds -PlantSimEngine.environment_variables(::ScatteringEnvironmentBackend) = Set([:T]) -PlantSimEngine.sample(::ScatteringEnvironmentBackend, variable::Symbol, support::EnvironmentSupport, time) = 20.0 + float(time) - -function PlantSimEngine.scatter!( - backend::ScatteringEnvironmentBackend, - variable::Symbol, - support::EnvironmentSupport, - value, - time -) - push!(backend.writes, (domain=support.domain, process=support.process, variable=variable, value=value, time=time)) - return nothing -end - -function PlantSimEngine.update_index!(backend::ScatteringEnvironmentBackend, entities) - push!( - backend.index_updates, - [ - (domain=entity.domain, kind=entity.kind, scale=entity.scale, nstatuses=length(entity.statuses)) - for entity in entities - ], - ) - return nothing -end - -mutable struct GraphEnvironmentBackend <: AbstractEnvironmentBackend - nsteps::Int - base_seconds::Float64 - writes::Vector{NamedTuple} - index_updates::Vector{Any} -end - -PlantSimEngine.get_nsteps(backend::GraphEnvironmentBackend) = backend.nsteps -PlantSimEngine.base_step_seconds(backend::GraphEnvironmentBackend) = backend.base_seconds -PlantSimEngine.environment_variables(::GraphEnvironmentBackend) = Set([:T, :CO2]) - -function PlantSimEngine.sample( - ::GraphEnvironmentBackend, - variable::Symbol, - support::EnvironmentSupport, - time -) - variable == :CO2 && return 410.0 - variable == :T || error("Unexpected variable `$(variable)`.") - return 20.0 + float(time) + 0.1 * node_id(support.status.node) -end - -function PlantSimEngine.scatter!( - backend::GraphEnvironmentBackend, - variable::Symbol, - support::EnvironmentSupport, - value, - time -) - push!( - backend.writes, - ( - domain=support.domain, - scale=support.scale, - process=support.process, - node_id=node_id(support.status.node), - variable=variable, - value=value, - time=time, +@testset "Environment backend contract" begin + support = EnvironmentSupport(:leaf_energy, :Leaf, :energy_balance, nothing) + backend = GlobalConstant( + Atmosphere( + T=20.0, + Rh=0.65, + Wind=1.0, + CO2=410.0, + duration=Dates.Hour(1), ), ) - return nothing -end - -function PlantSimEngine.update_index!(backend::GraphEnvironmentBackend, entities) - push!( - backend.index_updates, - [ - (domain=entity.domain, kind=entity.kind, scale=entity.scale, nstatuses=length(entity.statuses)) - for entity in entities - ], - ) - return nothing -end -@testset "Environment backends" begin - support = EnvironmentSupport(:plant_a, :Default, :environment_probe, nothing) - global_backend = GlobalConstant(Atmosphere(T=20.0, Rh=0.65, Wind=1.0, CO2=410.0, duration=Dates.Hour(1))) - @test sample(global_backend, :T, support, 1.0) == 20.0 - @test PlantSimEngine.get_nsteps(global_backend) == 1 - @test base_step_seconds(global_backend) == 3600.0 + @test sample(backend, :T, support, 1.0) == 20.0 + @test PlantSimEngine.get_nsteps(backend) == 1 + @test base_step_seconds(backend) == 3600.0 @test environment_variables(GlobalConstant(nothing)) == Set{Symbol}() - mapping = PlantSimEngine.ModelMapping( - ModelSpec(EnvironmentProbeModel()) |> TimeStep(Dates.Hour(1)), - status=(meteo_seen=0.0,), - ) - simulation_mapping = PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:plant_a, mapping; kind=:plant), - PlantSimEngine.Domain(:plant_b, mapping; kind=:plant), - ) - - sim = run!(simulation_mapping, ProbeEnvironmentBackend(3, 3600.0), check=true) - @test status(sim, :plant_a).meteo_seen ≈ 13.41 - @test status(sim, :plant_b).meteo_seen ≈ 113.41 - - environment = explain_environment(sim) - @test environment.backend == ProbeEnvironmentBackend - @test environment.nsteps == 3 - @test environment.base_step_seconds == 3600.0 - @test :CO2 in environment.variables - - bound_mapping = PlantSimEngine.ModelMapping( - ModelSpec(EnvironmentProbeModel()) |> - TimeStep(Dates.Hour(1)) |> - PlantSimEngine.MeteoBindings(; CO2=(source=:Ca, reducer=MeanReducer())), - status=(meteo_seen=0.0,), - ) - bound_sim = run!( - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, bound_mapping; kind=:plant)), - ProbeEnvironmentBackend(1, 3600.0), - check=true, - ) - @test status(bound_sim, :plant_a).meteo_seen ≈ 11.42 - - @test_throws "CO2" run!( - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, mapping; kind=:plant)), - MissingCO2EnvironmentBackend(), - check=true, - ) - @test_throws "CO2" run!( - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, mapping; kind=:plant)), - nothing, - check=true, - ) - - update_mapping = PlantSimEngine.ModelMapping( - ModelSpec(EnvironmentTemperatureUpdateModel()) |> TimeStep(Dates.Hour(1)), - status=(T=0.0,), - ) - scattering_backend = ScatteringEnvironmentBackend(2, 3600.0, NamedTuple[], Any[]) - scatter_sim = run!( - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, update_mapping; kind=:plant)), - scattering_backend, - check=true, - ) - @test status(scatter_sim, :plant_a).T == 23.0 - @test scattering_backend.writes == [ - (domain=:plant_a, process=:environment_temperature_update, variable=:T, value=22.0, time=1.0), - (domain=:plant_a, process=:environment_temperature_update, variable=:T, value=23.0, time=2.0), - ] - @test scattering_backend.index_updates == [ - [(domain=:plant_a, kind=:plant, scale=:Default, nstatuses=1)], - [(domain=:plant_a, kind=:plant, scale=:Default, nstatuses=1)], - ] - - @test_throws "GlobalConstant is immutable" run!( - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, update_mapping; kind=:plant)), - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, duration=Dates.Hour(1)), - check=true, - ) - - bad_mapping = PlantSimEngine.ModelMapping( - ModelSpec(EnvironmentBadOutputModel()) |> TimeStep(Dates.Hour(1)), - status=NamedTuple(), - ) - @test_throws "status does not contain" run!( - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, bad_mapping; kind=:plant)), - ScatteringEnvironmentBackend(1, 3600.0, NamedTuple[], Any[]), - check=true, - ) - - scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - leaf_1 = Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - leaf_2 = Node(plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 2, 2)) - leaf_ids = sort([node_id(leaf_1), node_id(leaf_2)]) - graph_mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(EnvironmentGraphLeafProbeModel()) |> TimeStep(Dates.Hour(1)), - ), - ) - graph_backend = GraphEnvironmentBackend(2, 3600.0, NamedTuple[], Any[]) - graph_sim = run!( - scene, - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, graph_mapping; kind=:plant, selector=plant)), - graph_backend, - check=true, + @test_throws "leaf_energy/Leaf/energy_balance" sample( + backend, + :missing, + support, + 1.0, ) - graph_values = graph_sim.outputs[(PlantSimEngine.DomainModelKey(:plant_a, :Leaf, :environment_graph_leaf_probe), :meteo_seen)] - @test graph_values == [ - [20.0 + 1.0 + 0.1 * leaf_ids[1] + 0.410, 20.0 + 1.0 + 0.1 * leaf_ids[2] + 0.410], - [20.0 + 2.0 + 0.1 * leaf_ids[1] + 0.410, 20.0 + 2.0 + 0.1 * leaf_ids[2] + 0.410], - ] - @test graph_backend.index_updates == [ - [(domain=:plant_a, kind=:plant, scale=:Leaf, nstatuses=2)], - [(domain=:plant_a, kind=:plant, scale=:Leaf, nstatuses=2)], - ] - - scatter_scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - scatter_plant = Node(scatter_scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - scatter_leaf = Node(scatter_plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - scatter_mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(EnvironmentGraphTemperatureUpdateModel()) |> TimeStep(Dates.Hour(1)), - ), + @test_throws "GlobalConstant is immutable" scatter!( + backend, + :T, + support, + 21.0, + 1.0, ) - graph_scatter_backend = GraphEnvironmentBackend(1, 3600.0, NamedTuple[], Any[]) - graph_scatter_sim = run!( - scatter_scene, - PlantSimEngine.SimulationMapping(PlantSimEngine.Domain(:plant_a, scatter_mapping; kind=:plant, selector=scatter_plant)), - graph_scatter_backend, - check=true, - ) - @test only(status(graph_scatter_sim, :plant_a, :Leaf)).T ≈ 20.0 + 1.0 + 0.1 * node_id(scatter_leaf) + 2.0 - @test graph_scatter_backend.writes == [ - ( - domain=:plant_a, - scale=:Leaf, - process=:environment_graph_temperature_update, - node_id=node_id(scatter_leaf), - variable=:T, - value=20.0 + 1.0 + 0.1 * node_id(scatter_leaf) + 2.0, - time=1.0, - ), - ] - - hard_scatter_scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - hard_scatter_plant = Node(hard_scatter_scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - hard_scatter_leaf = Node(hard_scatter_plant, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - hard_scatter_mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(EnvironmentGraphTemperatureUpdateModel()) |> TimeStep(Dates.Hour(1)), - ), - ) - hard_scatter_scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(EnvironmentSceneHardGraphUpdateModel()) |> TimeStep(Dates.Hour(1)), - status=(hard_temperature_sum=0.0,), - ) - hard_graph_scatter_backend = GraphEnvironmentBackend(1, 3600.0, NamedTuple[], Any[]) - hard_graph_scatter_sim = run!( - hard_scatter_scene, - PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:plant_a, hard_scatter_mapping; kind=:plant, selector=hard_scatter_plant), - PlantSimEngine.Domain(:scene, hard_scatter_scene_mapping; kind=:scene), - ), - hard_graph_scatter_backend, - check=true, - ) - expected_hard_T = 20.0 + 1.0 + 0.1 * node_id(hard_scatter_leaf) + 2.0 - @test only(status(hard_graph_scatter_sim, :plant_a, :Leaf)).T ≈ expected_hard_T - @test status(hard_graph_scatter_sim, :scene).hard_temperature_sum ≈ expected_hard_T - @test hard_graph_scatter_backend.writes == [ - ( - domain=:plant_a, - scale=:Leaf, - process=:environment_graph_temperature_update, - node_id=node_id(hard_scatter_leaf), - variable=:T, - value=expected_hard_T, - time=1.0, - ), - ] end diff --git a/test/test-maespa-domain-example.jl b/test/test-maespa-scene-example.jl similarity index 64% rename from test/test-maespa-domain-example.jl rename to test/test-maespa-scene-example.jl index b1b3d84a2..99a174f86 100644 --- a/test/test-maespa-domain-example.jl +++ b/test/test-maespa-scene-example.jl @@ -1,62 +1,7 @@ -include("../examples/maespa_domain_example.jl") +include("../examples/maespa_scene_example.jl") -@testset "MAESPA-style domain example" begin - result = run_maespa_example(; nhours=24, check=true) - sim = result.simulation - - @test length(status(sim, :Leaf)) == 5 - @test length(status(sim, :plant_A, :Leaf)) == 2 - @test length(status(sim, :plant_B, :Leaf)) == 3 - - scene_status = status(sim, :scene) - last_meteo = last(maespa_meteo(; nhours=24)) - vpd_above = max(0.01, last_meteo.VPD) - @test isfinite(scene_status.canopy_tair) - @test isfinite(scene_status.canopy_vpd) - @test isfinite(scene_status.canopy_rh) - @test isfinite(scene_status.canopy_htot) - @test isfinite(scene_status.canopy_gcanop) - @test scene_status.canopy_tair >= last_meteo.T - 10.0 - @test scene_status.canopy_tair <= last_meteo.T + 10.0 - @test scene_status.canopy_vpd >= max(0.01, vpd_above - 1.5) - @test scene_status.canopy_vpd <= vpd_above + 1.5 - @test scene_status.canopy_vpd > 0.0 - @test 0.0 <= scene_status.canopy_rh <= 1.0 - @test isfinite(scene_status.scene_transpiration) - @test scene_status.scene_transpiration > 0.0 - @test scene_status.iterations > 0 - @test scene_status.leaf_area ≈ sum(st.leaf_area for st in status(sim, :Leaf)) - @test scene_status.lai ≈ scene_status.leaf_area - @test scene_status.leaf_areas ≈ getproperty.(status(sim, :Leaf), :leaf_area) - - leaf_statuses = status(sim, :Leaf) - @test all(st -> isfinite(st.Tₗ), leaf_statuses) - @test all(st -> isfinite(st.A), leaf_statuses) - @test all(st -> isfinite(st.λE), leaf_statuses) - @test any(st -> abs(st.Tₗ - scene_status.canopy_tair) > 1.0e-6, leaf_statuses) - - plant_a_status = only(status(sim, :plant_A, :Plant)) - plant_b_status = only(status(sim, :plant_B, :Plant)) - @test plant_a_status.daily_growth > 0.0 - @test plant_b_status.daily_growth > 0.0 - @test plant_a_status.daily_growth != plant_b_status.daily_growth - @test plant_a_status.leaf_pool != plant_b_status.leaf_pool - - deps = PlantSimEngine.explain_domain_dependencies(sim) - @test count(row -> row.mode == :hard_domain && row.dependency == :energy_balance, deps) == 2 - @test count(row -> row.mode == :hard_domain && row.dependency == :soil, deps) == 1 - - @test length(sim.outputs[(PlantSimEngine.DomainModelKey(:plant_A, :Leaf, :energy_balance), :λE)]) == 2 * 24 - @test length(sim.outputs[(PlantSimEngine.DomainModelKey(:plant_B, :Leaf, :energy_balance), :λE)]) == 3 * 24 - @test length(sim.outputs[(PlantSimEngine.DomainModelKey(:soil, :Default, :soil_water), :psi_soil)]) == 24 - @test length(sim.outputs[(PlantSimEngine.DomainModelKey(:scene, :Default, :lai_dynamic), :lai)]) == 1 - @test length(sim.outputs[(PlantSimEngine.DomainModelKey(:scene, :Default, :scene_eb), :scene_transpiration)]) == 24 - @test status(sim, :soil).transpiration ≈ scene_status.scene_transpiration - @test status(sim, :soil).psi_soil ≈ scene_status.psi_soil -end - -@testset "MAESPA-style unified scene example" begin - result = run_maespa_scene_example(; nhours=25, check=true) +@testset "MAESPA-style scene example" begin + result = run_maespa_example(; nhours=25, check=true) scene = result.scene compiled = result.compiled @@ -169,47 +114,14 @@ end @test soil_status.psi_soil ≈ scene_status.psi_soil end -@testset "MAESPA-style domain example validation" begin - mtg = build_maespa_scene() +@testset "MAESPA-style scene example validation" begin meteo = maespa_meteo(; nhours=1) - soil_mapping = PlantSimEngine.ModelMapping( - ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75)) |> TimeStep(Dates.Hour(1)), - status=(theta1=0.33, theta2=0.36, psi_soil=-0.10, transpiration=0.0, infiltration=0.0), - ) - scene_mapping = PlantSimEngine.ModelMapping( - ModelSpec(LAIModel(1.0)) |> TimeStep(Dates.Hour(1)), - ModelSpec(SceneEB(25, 0.03, 0.005)) |> - Calls( - :energy_balance => Many(kind=:plant, scale=:Leaf, process=:energy_balance), - :soil => One(kind=:soil, process=:soil_water), - ) |> - TimeStep(Dates.Hour(1)), - status=( - leaf_area=0.0, - lai=0.0, - canopy_tair=20.0, - canopy_vpd=1.0, - canopy_rh=0.7, - canopy_htot=0.0, - canopy_gcanop=0.0, - scene_transpiration=0.0, - scene_assimilation=0.0, - psi_soil=-0.1, - iterations=0, - ), - ) - missing_leaf_mapping = PlantSimEngine.SimulationMapping( - PlantSimEngine.Domain(:soil, soil_mapping; kind=:soil), - PlantSimEngine.Domain(:scene, scene_mapping; kind=:scene), - ) - @test_throws "Hard domain dependency `energy_balance`" run!(mtg, missing_leaf_mapping, meteo, check=true, executor=SequentialEx()) - soil_target = (status=Status(psi_soil=-0.1),) scene_status = Status(lai=0.0, leaf_area=0.0) @test_throws "SceneEB did not converge after 0 iterations" _solve_scene_energy_balance!( SceneEB(0, 0.03, 0.005), - PlantSimEngine.ModelTarget[], + SceneCallTarget[], soil_target, scene_status, first(meteo), diff --git a/test/test-multirate-runtime.jl b/test/test-multirate-runtime.jl index f47f8e1f9..ff1d91510 100644 --- a/test/test-multirate-runtime.jl +++ b/test/test-multirate-runtime.jl @@ -281,7 +281,7 @@ PlantSimEngine.dep(::MRHardParentModel) = (mrhardchild=AbstractMrhardchildModel, PlantSimEngine.inputs_(::MRHardParentModel) = NamedTuple() PlantSimEngine.outputs_(::MRHardParentModel) = (A=-Inf,) function PlantSimEngine.run!(::MRHardParentModel, models, status, meteo, constants=nothing, extra=nothing) - PlantSimEngine.run_target!(models, status, :mrhardchild; meteo=meteo, constants=constants, extra=extra) + PlantSimEngine.run!(models.mrhardchild, models, status, meteo, constants, extra) status.A = 5.0 end diff --git a/test/test-unified-scene-object-api.jl b/test/test-unified-scene-object-api.jl index d0a3c4a31..cf0046f65 100644 --- a/test/test-unified-scene-object-api.jl +++ b/test/test-unified-scene-object-api.jl @@ -633,7 +633,15 @@ function PlantSimEngine.bind_environment( ) object_geometry = geometry(object) cell = isnothing(object_geometry) ? :global : object_geometry.cell - push!(backend.binds, (object=object.id.value, application=support.domain, cell=cell, config=config)) + push!( + backend.binds, + ( + object=object.id.value, + application=support.application, + cell=cell, + config=config, + ), + ) return cell end @@ -704,7 +712,7 @@ function PlantSimEngine.scatter!( push!( backend.writes, ( - application=support.domain, + application=support.application, process=support.process, cell=cell, variable=variable, @@ -1250,10 +1258,7 @@ end @test model_calls(spec).stomata isa One @test PlantSimEngine.call_origins(spec).stomata == :model_spec @test object_address(model_calls(spec).stomata).process == :stomatal_conductance - call_dep = dep(spec).stomata - @test call_dep isa PlantSimEngine.HardDomains - @test call_dep.scale == :Leaf - @test call_dep.process == :stomatal_conductance + @test isempty(dep(spec)) @test PlantSimEngine.timestep(spec) == Hour(1) @test environment_config(spec) isa PlantSimEngine.EnvironmentConfig @test environment_config(spec).config.provider == :global @@ -1354,10 +1359,7 @@ end :model_default @test model_calls(default_call_spec).stomata.criteria.scale == :Leaf @test model_calls(default_call_spec).stomata.criteria.process == :stomatal_conductance - default_call_dep = dep(default_call_spec).stomata - @test default_call_dep isa PlantSimEngine.HardDomains - @test default_call_dep.scale == :Leaf - @test default_call_dep.process == :stomatal_conductance + @test isempty(dep(default_call_spec)) override_call_spec = ModelSpec(SceneObjectDefaultCallConsumerModel()) |> Calls(:stomata => One(scale=:Internode, process=:water_status)) @@ -1365,10 +1367,7 @@ end @test PlantSimEngine.call_origins(override_call_spec).stomata == :model_spec @test model_calls(override_call_spec).stomata.criteria.process == :water_status - override_call_dep = dep(override_call_spec).stomata - @test override_call_dep isa PlantSimEngine.HardDomains - @test override_call_dep.scale == :Internode - @test override_call_dep.process == :water_status + @test isempty(dep(override_call_spec)) compiled_specs = ( ModelSpec(SceneObjectStomataModel(); name=:stomata) |> From 4286cf19bc0c2e830d55c8dbededc4c41d1d5724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Thu, 18 Jun 2026 12:37:39 +0200 Subject: [PATCH 21/33] First end-to-end implementation of the new API --- AGENTS.md | 440 ++---- Project.toml | 8 - README.md | 6 +- docs/make.jl | 30 +- docs/src/API/API_public.md | 262 +--- docs/src/FAQ/translate_a_model.md | 157 -- docs/src/agent_skill.md | 7 +- docs/src/developers.md | 10 - docs/src/index.md | 8 +- docs/src/migration_scene_object.md | 3 +- .../src/model_coupling/model_coupling_user.md | 173 -- docs/src/model_execution.md | 14 +- docs/src/model_traits.md | 194 +-- docs/src/multirate/advanced_configuration.md | 214 --- docs/src/multirate/introduction.md | 206 --- docs/src/multirate/multirate_tutorial.md | 434 ------ docs/src/multiscale/multiscale.md | 260 ---- .../multiscale/multiscale_considerations.md | 171 -- docs/src/multiscale/multiscale_coupling.md | 176 --- docs/src/multiscale/multiscale_cyclic.md | 119 -- docs/src/multiscale/multiscale_example_1.md | 289 ---- docs/src/multiscale/multiscale_example_2.md | 266 ---- docs/src/multiscale/multiscale_example_3.md | 503 ------ docs/src/multiscale/multiscale_example_4.md | 226 --- docs/src/multiscale/single_to_multiscale.md | 241 --- docs/src/planned_features.md | 65 +- .../installing_plantsimengine.md | 101 +- docs/src/prerequisites/julia_basics.md | 6 +- docs/src/prerequisites/key_concepts.md | 223 +-- docs/src/scene_object/quickstart.md | 6 +- docs/src/step_by_step/advanced_coupling.md | 7 +- .../step_by_step/detailed_first_example.md | 14 +- docs/src/step_by_step/implement_a_model.md | 43 +- docs/src/step_by_step/model_switching.md | 5 +- docs/src/step_by_step/parallelization.md | 39 - .../step_by_step/quick_and_dirty_examples.md | 9 +- .../src/step_by_step/simple_model_coupling.md | 8 +- .../implicit_contracts.md | 96 -- ...lantsimengine_and_julia_troubleshooting.md | 515 ------ .../tips_and_workarounds.md | 114 -- docs/src/working_with_data/fitting.md | 108 +- .../floating_point_accumulation_error.md | 161 -- docs/src/working_with_data/inputs.md | 103 -- docs/src/working_with_data/reducing_dof.md | 121 -- .../working_with_data/visualising_outputs.md | 98 -- examples/Beer.jl | 41 +- examples/ToyAssimGrowthModel.jl | 3 - examples/ToyAssimModel.jl | 5 - examples/ToyCAllocationModel.jl | 1 - examples/ToyCBiomassModel.jl | 3 - examples/ToyCDemandModel.jl | 5 - examples/ToyDegreeDays.jl | 5 - examples/ToyInternodeEmergence.jl | 36 - examples/ToyLAIModel.jl | 3 - examples/ToyLeafSurfaceModel.jl | 5 - examples/ToyLightPartitioningModel.jl | 2 - .../ToyPlantHelpers.jl | 14 - .../ToyPlantSimulation1.jl | 140 -- .../ToyPlantSimulation2.jl | 221 --- .../ToyPlantSimulation3.jl | 237 --- .../ToyPlantSimulation4.jl | 148 -- examples/ToyRUEGrowthModel.jl | 1 - examples/ToySingleToMultiScale.jl | 120 -- examples/ToySoilModel.jl | 5 - examples/benchmark.jl | 31 - examples/dummy.jl | 14 - examples/plantbiophysics_subsample/FvCB.jl | 4 +- .../plantbiophysics_subsample/Monteith.jl | 37 +- examples/plantbiophysics_subsample/Tuzet.jl | 18 +- skills/plantsimengine/SKILL.md | 17 +- src/Abstract_model_structs.jl | 11 +- src/ModelSpec.jl | 422 +++++ src/PlantSimEngine.jl | 101 +- src/checks/dimensions.jl | 119 -- src/component_models/SingleScaleModelSet.jl | 472 ------ src/component_models/Status.jl | 2 +- src/component_models/StatusView.jl | 147 -- src/component_models/TimeStepTable.jl | 20 +- src/component_models/get_status.jl | 104 -- src/dataframe.jl | 69 - src/dependencies/dependencies.jl | 131 -- src/dependencies/dependency_graph.jl | 173 -- .../get_model_in_dependency_graph.jl | 43 - src/dependencies/hard_dependencies.jl | 296 ---- src/dependencies/is_graph_cyclic.jl | 77 - src/dependencies/printing.jl | 146 -- src/dependencies/soft_dependencies.jl | 590 ------- src/dependencies/traversal.jl | 174 --- src/dependencies/update_dependencies.jl | 189 --- src/doc_templates/mtg-related.jl | 67 - src/evaluation/fit.jl | 24 +- src/examples_import.jl | 5 +- src/mtg/GraphSimulation.jl | 150 -- src/mtg/ModelSpec.jl | 684 -------- src/mtg/MultiScaleModel.jl | 201 --- src/mtg/add_organ.jl | 230 --- src/mtg/initialisation.jl | 474 ------ src/mtg/mapping/compute_mapping.jl | 364 ----- src/mtg/mapping/getters.jl | 133 -- src/mtg/mapping/mapping.jl | 794 ---------- .../model_generation_from_status_vectors.jl | 282 ---- src/mtg/mapping/reverse_mapping.jl | 109 -- src/mtg/model_spec_inference.jl | 951 ----------- src/mtg/model_spec_validation.jl | 444 ------ src/mtg/node_mapping_types.jl | 15 - src/mtg/save_results.jl | 375 ----- src/processes/model_initialisation.jl | 385 ----- src/processes/models_inputs_outputs.jl | 119 +- src/processes/process_generation.jl | 23 +- src/run.jl | 617 -------- src/scene_object_api.jl | 140 +- src/time/multirate.jl | 289 +--- src/time/runtime/bindings.jl | 129 -- src/time/runtime/clocks.jl | 525 +++---- src/time/runtime/environment_backends.jl | 8 +- src/time/runtime/input_resolution.jl | 845 ---------- src/time/runtime/meteo_sampling.jl | 22 +- src/time/runtime/output_export.jl | 315 +--- src/time/runtime/publishers.jl | 186 --- src/time/runtime/scopes.jl | 104 -- src/traits/parallel_traits.jl | 302 ---- src/traits/table_traits.jl | 16 +- src/variables_wrappers.jl | 35 +- test/Project.toml | 1 - test/helper-functions.jl | 431 ----- test/runtests.jl | 48 - test/test-ModelMapping.jl | 323 ---- test/test-MultiScaleModel.jl | 83 - test/test-Status.jl | 44 - test/test-corner-cases.jl | 619 -------- test/test-dimensions.jl | 48 - test/test-fitting.jl | 32 +- test/test-mapping.jl | 384 ----- test/test-meteo-traits.jl | 30 +- test/test-mtg-dynamic.jl | 203 --- test/test-mtg-multiscale-cyclic-dep.jl | 252 --- test/test-mtg-multiscale.jl | 728 --------- test/test-multirate-output-export.jl | 340 ---- test/test-multirate-runtime.jl | 1386 ----------------- test/test-multirate-scaffolding.jl | 94 -- test/test-performance.jl | 116 -- test/test-simulation.jl | 328 ---- test/test-toy_models.jl | 140 +- test/test-unified-scene-object-api.jl | 113 +- test/test-updates.jl | 86 +- 145 files changed, 1479 insertions(+), 24373 deletions(-) delete mode 100644 docs/src/FAQ/translate_a_model.md delete mode 100644 docs/src/model_coupling/model_coupling_user.md delete mode 100644 docs/src/multirate/advanced_configuration.md delete mode 100644 docs/src/multirate/introduction.md delete mode 100644 docs/src/multirate/multirate_tutorial.md delete mode 100644 docs/src/multiscale/multiscale.md delete mode 100644 docs/src/multiscale/multiscale_considerations.md delete mode 100644 docs/src/multiscale/multiscale_coupling.md delete mode 100644 docs/src/multiscale/multiscale_cyclic.md delete mode 100644 docs/src/multiscale/multiscale_example_1.md delete mode 100644 docs/src/multiscale/multiscale_example_2.md delete mode 100644 docs/src/multiscale/multiscale_example_3.md delete mode 100644 docs/src/multiscale/multiscale_example_4.md delete mode 100644 docs/src/multiscale/single_to_multiscale.md delete mode 100644 docs/src/step_by_step/parallelization.md delete mode 100644 docs/src/troubleshooting_and_testing/implicit_contracts.md delete mode 100644 docs/src/troubleshooting_and_testing/plantsimengine_and_julia_troubleshooting.md delete mode 100644 docs/src/troubleshooting_and_testing/tips_and_workarounds.md delete mode 100644 docs/src/working_with_data/floating_point_accumulation_error.md delete mode 100644 docs/src/working_with_data/inputs.md delete mode 100644 docs/src/working_with_data/reducing_dof.md delete mode 100644 docs/src/working_with_data/visualising_outputs.md delete mode 100644 examples/ToyInternodeEmergence.jl delete mode 100644 examples/ToyMultiScalePlantTutorial/ToyPlantHelpers.jl delete mode 100644 examples/ToyMultiScalePlantTutorial/ToyPlantSimulation1.jl delete mode 100644 examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl delete mode 100644 examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl delete mode 100644 examples/ToyMultiScalePlantTutorial/ToyPlantSimulation4.jl delete mode 100644 examples/ToySingleToMultiScale.jl delete mode 100644 examples/benchmark.jl create mode 100644 src/ModelSpec.jl delete mode 100644 src/checks/dimensions.jl delete mode 100644 src/component_models/SingleScaleModelSet.jl delete mode 100644 src/component_models/StatusView.jl delete mode 100644 src/component_models/get_status.jl delete mode 100644 src/dataframe.jl delete mode 100644 src/dependencies/dependencies.jl delete mode 100644 src/dependencies/dependency_graph.jl delete mode 100644 src/dependencies/get_model_in_dependency_graph.jl delete mode 100644 src/dependencies/hard_dependencies.jl delete mode 100644 src/dependencies/is_graph_cyclic.jl delete mode 100644 src/dependencies/printing.jl delete mode 100644 src/dependencies/soft_dependencies.jl delete mode 100644 src/dependencies/traversal.jl delete mode 100644 src/dependencies/update_dependencies.jl delete mode 100644 src/doc_templates/mtg-related.jl delete mode 100644 src/mtg/GraphSimulation.jl delete mode 100644 src/mtg/ModelSpec.jl delete mode 100644 src/mtg/MultiScaleModel.jl delete mode 100644 src/mtg/add_organ.jl delete mode 100644 src/mtg/initialisation.jl delete mode 100644 src/mtg/mapping/compute_mapping.jl delete mode 100644 src/mtg/mapping/getters.jl delete mode 100755 src/mtg/mapping/mapping.jl delete mode 100644 src/mtg/mapping/model_generation_from_status_vectors.jl delete mode 100644 src/mtg/mapping/reverse_mapping.jl delete mode 100644 src/mtg/model_spec_inference.jl delete mode 100644 src/mtg/model_spec_validation.jl delete mode 100644 src/mtg/node_mapping_types.jl delete mode 100644 src/mtg/save_results.jl delete mode 100755 src/processes/model_initialisation.jl delete mode 100644 src/run.jl delete mode 100644 src/time/runtime/bindings.jl delete mode 100644 src/time/runtime/input_resolution.jl delete mode 100644 src/time/runtime/publishers.jl delete mode 100644 src/time/runtime/scopes.jl delete mode 100644 src/traits/parallel_traits.jl delete mode 100644 test/helper-functions.jl delete mode 100644 test/test-ModelMapping.jl delete mode 100644 test/test-MultiScaleModel.jl delete mode 100644 test/test-corner-cases.jl delete mode 100644 test/test-dimensions.jl delete mode 100755 test/test-mapping.jl delete mode 100644 test/test-mtg-dynamic.jl delete mode 100644 test/test-mtg-multiscale-cyclic-dep.jl delete mode 100644 test/test-mtg-multiscale.jl delete mode 100644 test/test-multirate-output-export.jl delete mode 100644 test/test-multirate-runtime.jl delete mode 100644 test/test-multirate-scaffolding.jl delete mode 100644 test/test-performance.jl delete mode 100644 test/test-simulation.jl diff --git a/AGENTS.md b/AGENTS.md index 119f35fd3..14b480dc6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,340 +1,188 @@ # PlantSimEngine Agent And Developer Guide -This file is the maintainer-facing summary of how PlantSimEngine works internally. -It is meant for humans and coding agents making changes to the package. - -PlantSimEngine is a Julia engine for composing process models on either: - -- a single shared status (`ModelMapping{SingleScale}` / legacy `ModelList`) -- a multiscale MTG scene (`GraphSimulation`) - -The package is built around four ideas: - -1. Models declare `inputs_`, `outputs_`, and optionally `dep`. -2. The engine compiles a dependency graph from those declarations. -3. Runtime state is reference-based (`Status`, `RefVector`), so coupling is often aliasing, not copying. -4. Multiscale and multirate configuration can change where an input comes from, how it is transported, and when it is sampled. - -## What The Package Supports - -- Single-scale process composition with automatic soft-dependency inference. -- Hard dependencies declared explicitly and called manually from model code. -- MTG-based multiscale simulations with cross-scale variable mappings. -- Cross-scale scalar sharing through shared `Ref`s. -- Cross-scale multi-node sharing through `RefVector`s. -- Cross-scale writes, where a variable computed at one scale is materialized as an input at another scale. -- Same-scale variable aliasing and renaming. -- Cycle breaking through `PreviousTimeStep`. -- Multi-rate execution through `ModelSpec`, `ClockSpec`, and temporal policies. -- Explicit or inferred `InputBindings` between producers and consumers. -- Meteo resampling/aggregation per model in multi-rate MTG runs. -- Output routing (`:canonical` vs `:stream_only`) and online output export (`OutputRequest`). -- Parallel single-scale execution when model traits allow it. - -## Core Runtime Objects - -### Processes and models - -- All models subtype `AbstractModel`. -- `@process` creates an abstract process type such as `AbstractGrowthModel`. -- Process identity comes from the abstract process type, not the concrete model name. -- The model execution contract is: +PlantSimEngine composes process models over a unified scene/object registry. +The repository contains one scenario compiler and runtime: the scene/object +API. + +## Core Model Contract + +- Every model subtypes `AbstractModel`. +- `@process` defines the abstract process type. +- `process(model)` identifies the process. +- `inputs_(model)` and `outputs_(model)` declare status variables. +- `meteo_inputs_(model)` and `meteo_outputs_(model)` declare environment + variables. +- `dep(model)` optionally returns model-author defaults using `Input(...)` and + `Call(...)`. +- Kernels implement: ```julia PlantSimEngine.run!(model, models, status, meteo, constants, extra) ``` -- `inputs_(model)` and `outputs_(model)` are the authoritative declarations. -- `variables(model)` is `merge(inputs_(model), outputs_(model))`. -- Do not rely on a variable being both an input and an output under the same name: `merge` means the later declaration wins. - -### Status - -- `Status` is a wrapper around a `NamedTuple` of `Ref`s. -- Reading a field dereferences it. Writing a field mutates the underlying `Ref`. -- This aliasing behavior is intentional and is the basis of most coupling. -- In single-scale runs, vector-valued user inputs are flattened to one timestep value and updated per timestep with `set_variables_at_timestep!`. - -### RefVector - -- `RefVector` is an `AbstractVector` of `Base.RefValue`s. -- It is used when one model input must see a vector of references coming from many statuses. -- Reading a `RefVector` dereferences each underlying status cell. -- Writing into a `RefVector` mutates the source statuses. -- `RefVector` order follows MTG traversal order during initialization, not a semantic plant order. - -### Mapping wrappers - -- `MultiScaleModel` wraps one model plus a multiscale mapping declaration. -- `ModelSpec` wraps one model plus scenario-level runtime configuration: - `multiscale`, `timestep`, `input_bindings`, `meteo_bindings`, `meteo_window`, `output_routing`, and `scope`. -- `ModelMapping` is the normalized mapping container used by current entry points. -- Legacy `ModelList` still exists, but it is compatibility plumbing and should not be treated as the main abstraction for new work. - -### Simulation wrappers +`models` is a process-keyed bundle compiled from `Calls(...)`. `extra` is a +`SceneRunContext` and provides `call_target`, `call_targets`, and lifecycle +access. -- `DependencyGraph` holds root dependency nodes plus unresolved dependencies. -- `GraphSimulation` holds the MTG, statuses, status templates, reverse mappings, dependency graph, models, model specs, outputs, and temporal state. +## Scene Structure -## Dependency Graph Under The Hood +- `Scene` owns a `SceneRegistry`, model applications, instances, and an + environment. +- `Object` is one runtime entity with stable `ObjectId`, labels, parent, + geometry, and `Status`. +- Plant architecture is not prescribed. Users choose scales and topology. +- `ObjectTemplate` and `ObjectInstance` reuse the same model definitions across + several plants or objects. +- `Override` replaces one application model for selected objects without + splitting the logical application. +- `objects_from_mtg` and `Scene(mtg; ...)` adapt MTG topology into the same + registry. -### Hard dependencies +## Model Applications -- Hard dependencies are declared with `dep(::ModelType)`. -- A hard dependency means: "this model directly calls another model from inside its own `run!` implementation." -- Hard dependencies are represented by `HardDependencyNode`. -- They are executed manually by the parent model. The runtime does not automatically recurse into hard dependencies. -- Hard dependencies can be same-scale or explicitly multiscale. +Use one configuration grammar: -Important nuance: - -- A hard dependency does not become an independent soft-dependency node under the parent. -- But it still matters for graph construction, because the graph compiler aggregates the root model's hard-dependency subtree when computing that root's effective inputs and outputs. -- In multiscale graph building, if another model depends on a process that exists only as a nested hard dependency, the code resolves that dependency back to the master soft node that owns that hard subtree. - -So "hard dependencies do not directly participate in the soft graph" is true for execution structure, but false if interpreted as "their IO is irrelevant to graph compilation." - -### Soft dependencies - -- Soft dependencies are inferred by matching model inputs against outputs. -- Matching is name-based after variable flattening, not based on a richer semantic contract. -- Same-scale soft dependencies are built after hard-dependency trees are known. -- A process cannot also list one of its hard dependencies as a soft dependency. -- `PreviousTimeStep` variables are removed from current-step soft dependency inference. -- Soft dependencies are represented by `SoftDependencyNode`. -- A soft node may have multiple parents. -- A node is considered runnable once all of its parent nodes have already run for the current traversal. -- If no producer output matches an input, no soft edge is added. Soft-edge construction does not itself fail on missing producers. - -### Single-scale graph build - -Single-scale graph construction is: - -1. Build `HardDependencyNode`s for each declared process. -2. Attach explicit hard-dependency children under their parents. -3. Traverse each hard-dependency root and collect its effective inputs and outputs. -4. Build one `SoftDependencyNode` per hard-dependency root. -5. Infer parent and child links by matching inputs to outputs. - -### Multiscale graph build - -Multiscale graph construction is more involved: - -1. Normalize the user mapping into `ModelMapping`. -2. Build per-scale hard-dependency graphs. -3. Resolve multiscale hard dependencies declared across scales. -4. Compute per-scale effective inputs and outputs for each hard-dependency root. -5. Build one `SoftDependencyNode` per root process per scale. -6. Compile mapped variables and reverse mappings. -7. Infer same-scale soft dependencies. -8. Infer cross-scale soft dependencies from mapped variables and reverse mappings. -9. If a dependency points to a nested hard dependency, redirect it to the owning soft node. -10. Check the final graph for cycles. - -### Cycle handling - -- The graph is expected to be acyclic. -- The official way to break a same-step cycle is `PreviousTimeStep`. -- `PreviousTimeStep` breaks cycles by suppressing current-step edge creation, not by adding special scheduler logic. -- In multiscale runs, cycle detection happens after the cross-scale graph is assembled. -- Single-scale `dep(...)` relies mostly on builder-time guards. Multiscale `dep(mapping)` also runs an explicit global cycle check on the final soft graph. - -## Multiscale Mapping Model +```julia +ModelSpec(model; name=:application) |> +AppliesTo(selector) |> +Inputs(...) |> +Calls(...) |> +TimeStep(Dates.Hour(1)) |> +Environment(...) +``` -### Mapping modes +- `AppliesTo` selects where the model runs. +- `Inputs` declares value dependencies. +- `Calls` declares manually executable hard dependencies. +- `Updates(:x; after=:producer)` orders intentional duplicate writers. +- `OutputRouting(; x=:stream_only)` excludes an output from canonical + ownership while retaining its stream. -PlantSimEngine distinguishes three mapping modes: +## Selectors -- `SingleNodeMapping(scale)`: one scalar value is read from one source scale. -- `MultiNodeMapping(scales)`: one input reads a vector of values from many source nodes. -- `SelfNodeMapping()`: a source scale must expose a scalar reference to itself so other scales can share it. +Multiplicity: -The runtime carrier is `MappedVar`, which stores: +- `One(...)` +- `OptionalOne(...)` +- `Many(...)` -- the mapping mode -- the local variable name -- the source variable name -- the resolved default value +Scope and topology: -### Supported mapping forms +- `SceneScope()` +- `Self()`: the current object +- `SelfPlant()`: the current plant instance/root +- `Ancestor(...)` +- `Scope(name)` +- `Kind(...)`, `Species(...)`, `Scale(...)`, `Relation(...)` -These are the important user-level forms and what they become internally: +`Self()` never means the model, species, or plant unless the current object is +itself that plant. -| User form | Meaning | Runtime shape | -| --- | --- | --- | -| `:x => :Plant` | scalar read from one `:Plant` node | shared `Ref` | -| `:x => (:Plant => :y)` | scalar read with renaming | shared `Ref` | -| `:x => [:Leaf]` | vector read from all `:Leaf` nodes | `RefVector` | -| `:x => [:Leaf, :Internode]` | vector read from several scales | `RefVector` | -| `:x => [:Leaf => :a, :Internode => :b]` | vector read with per-scale renaming | `RefVector` | -| `PreviousTimeStep(:x) => ...` | lagged mapping, excluded from same-step dependency build | lagged input | -| `PreviousTimeStep(:x)` | pure cycle-breaking marker | local/default value | -| `:x => (Symbol(\"\") => :y)` | same-scale rename | `RefVariable` alias | +## Value Coupling -### Mapping compilation pipeline +The compiler resolves `Inputs(...)` to reference carriers: -`mapped_variables(...)` does not just mirror user syntax. It compiles it. +- one source uses a shared `Ref`; +- many homogeneous sources use `RefVector`; +- heterogeneous sources use `ObjectRefVector`; +- temporal policies read typed output streams. -The main passes are: +Use `input_carrier`, `input_value`, `explain_bindings`, and +`has_reference_carrier` instead of inspecting internal fields. -1. Start from effective per-scale inputs and outputs collected from hard-dependency roots. -2. Add variables that are outputs of one scale but must appear as inputs at another scale. -3. Convert scalar cross-scale reads into self-mapped outputs on the source scale so one shared `Ref` exists. -4. Resolve default values recursively back to the ultimate producer. -5. Convert mapping descriptors into runtime carriers: - - scalar mappings become shared `Ref`s - - multi-node mappings become empty `RefVector`s - - same-scale renames become `RefVariable` +Same-object input/output matches are inferred when unique. Cross-object +coupling should be explicit with `Inputs(...)`. -### Reverse mapping and status wiring +## Hard Calls -- Reverse mapping is computed before the reference conversion pass. -- Reverse mapping answers: "when a source node is initialized, which target scale/vector inputs should receive a reference to this source variable?" -- Reverse mapping excludes scalar `SingleNodeMapping` edges when `all=false`, because scalar sharing is already handled by shared `Ref`s. +Hard dependencies are parent-controlled: -During `init_node_status!`: +1. Declare them with model-level `Call(...)` or scenario-level `Calls(...)`. +2. Resolve a compiled target with `call_target(extra, name)` or + `call_targets(extra, name)`. +3. Execute it with `run_call!`. -1. A copy of the scale template is made. -2. `:node => Ref(node)` is injected. -3. Remaining uninitialized variables may be filled from MTG attributes. -4. The template becomes a `Status`. -5. The status is pushed into `statuses[scale]`. -6. If this node feeds any downstream `RefVector`, its `Ref`s are pushed into those target vectors. -7. The status is stored on the MTG node under `:plantsimengine_status`. +`run_call!` defaults to `publish=false`, which is appropriate for iterative +trial states. Publish only the accepted state with `publish=true`. -### Copies vs references +Applications used exclusively as call targets are not run by the root +scheduler and do not receive inferred soft bindings. -- MTG attribute initialization copies plain values into the status. -- If the MTG attribute itself is already a `Ref`, that `Ref` is preserved. -- The runtime cannot create a live reference directly into a dict-backed MTG attribute. -- Cross-scale sharing is reference-based once the status exists. +## Time -## Multi-Rate Runtime - -Multi-rate behavior is layered on top of the multiscale MTG runtime. +- `TimeStep(Dates.Period)` configures application cadence. +- `timespec(model)` provides a model default. +- `timestep_hint(model)` validates compatibility when cadence comes from the + environment base step. +- `HoldLast`, `Interpolate`, `Integrate`, and `Aggregate` configure temporal + input policies. +- `PreviousTimeStep(:x)` breaks same-step cycles. -### Timing and policies +Dates periods are converted using meteorology `duration`. Model code should not +know its scenario timestep unless the scientific model explicitly requires it. -- `timespec(model)` defines the model's default clock. The default is `ClockSpec(1.0, 0.0)`. -- `ModelSpec.timestep` can override runtime clock selection. -- `output_policy(model)` declares per-output temporal policy defaults. - -Supported schedule policies are: - -- `HoldLast()`: use the latest available producer value. -- `Interpolate()`: interpolate or hold/extrapolate producer streams. -- `Integrate()`: reduce values over the consumer window, default reducer is `SumReducer()`. -- `Aggregate()`: reduce values over the consumer window, default reducer is `MeanReducer()`. +## Environment -### ModelSpec configuration surface +- `Environment(...)` configures provider selection and source remapping. +- Global meteorology and spatial backends use the same model-facing contract. +- Spatial object-to-cell bindings are compiled and cached. +- `move_object!`, `update_geometry!`, or + `mark_environment_binding_dirty!` invalidate affected bindings. +- `scatter_environment_outputs!` writes model-produced microclimate variables + back to mutable backends. -`ModelSpec` is the configuration point for scenario-specific runtime behavior. +## Lifecycle -It can define: +- `register_object!`, `remove_object!`, and `reparent_object!` mutate topology. +- Structural changes refresh application targets, value carriers, call + targets, writer checks, and schedules between timesteps. +- Geometry changes refresh only affected environment bindings when possible. +- Removed objects keep their historical output samples. -- `multiscale`: mapping declaration -- `timestep`: runtime clock -- `input_bindings`: explicit producer selection for consumer inputs -- `meteo_bindings`: per-model weather aggregation -- `meteo_window`: weather window selection strategy -- `output_routing`: `:canonical` or `:stream_only` -- `scope`: `:global`, `:self`, `:plant`, `:scene`, `ScopeId`, or callable +## Outputs -### Input binding inference +- `run!(scene)` returns `SceneSimulation`. +- `scene_outputs(sim)` exposes retained typed streams. +- `OutputRequest` selects retained/resampled outputs. +- `collect_outputs(sim)` materializes output rows. +- `explain_output_retention(sim)` reports why each stream is retained. -- If explicit `InputBindings` are absent, the package tries to infer bindings from the dependency graph and mapping. -- Unique same-scale producers win first. -- Unique cross-scale producers are accepted when unambiguous. -- Existing multiscale mapping hints can disambiguate some cross-scale cases. -- Ambiguity is an error and must be resolved explicitly. - -### Runtime sequence in multi-rate MTG mode - -For each dependency node and each status at that node's scale: - -1. Decide whether the model should run at the current time according to its clock. -2. Resolve consumer inputs from temporal state with explicit or inferred bindings. -3. Sample or aggregate meteo for the model. -4. Call the model's `run!`. -5. Publish outputs back into temporal caches and streams. -6. Materialize any requested online exports. - -Important consequences: - -- In non-multirate MTG runs, cross-scale coupling is mostly direct aliasing through shared refs. -- In multirate MTG runs, temporal state can overwrite consumer inputs just before execution. -- Multi-rate MTG runs are currently forced to sequential execution. - -## Configurations Developers Must Keep In Mind - -A variable seen by a model may be in any of these supported configurations: +Streams are keyed by application, object, and variable so repeated processes do +not overwrite each other. -- Plain local status value initialized by the user. -- Plain local status value initialized from MTG node attributes. -- Output computed locally at the same scale. -- Same-scale alias of another local variable through `RefVariable`. -- Scalar value mapped from another scale through a shared `Ref`. -- Vector of references mapped from one or many other scales through `RefVector`. -- Output computed at one scale and written into another scale, which means it is injected as an input on the receiving scale during mapping compilation. -- Value marked as `PreviousTimeStep`, which removes it from same-step dependency inference. -- Input resolved from a hard dependency that is called manually inside another model. -- Input resolved from temporal streams instead of directly from the current status value. -- Input bound explicitly with `InputBindings`. -- Input bound implicitly by inference from producers and mappings. -- Input sampled with `HoldLast`, `Interpolate`, `Integrate`, or `Aggregate`. -- Output published canonically into status state. -- Output published as `:stream_only`, meaning it participates in temporal streams but not canonical output ownership. -- Value partitioned by scope (`:global`, `:self`, `:plant`, `:scene`, or custom scope function). +## Performance Rules -When changing dependency, mapping, or runtime code, assume all of these modes can exist in the same simulation. - -## Execution Semantics And Important Caveats - -- Soft-dependency order controls model order. MTG topology does not define execution order within a scale. -- Within one scale, execution order follows the order of `statuses[scale]`, which comes from MTG traversal at initialization time. -- `SingleNodeMapping` assumes the source node is unique at runtime. The mapping layer does not enforce uniqueness. -- `RefVector` ordering is traversal order, not a guaranteed biological ordering. -- Hard dependencies are manual calls. If model code stops calling them, the declared hard dependency no longer executes. -- Hard dependencies still influence graph compilation through their effective inputs and outputs. -- Multiscale redirection from nested hard dependencies back to the owning soft node is implemented with upward walking through parent links and a defensive depth guard. Treat that path as fragile. -- MTG topology changes after `init_statuses` leave `statuses`, node attributes, and populated `RefVector`s stale. Reinitialize after topology changes. -- Same-scale renaming does not create a graph-wide shared ref. It creates a per-status alias. -- `parent_vars` is dependency metadata, not a full provenance graph, and in multiscale builds it can be overwritten when a node has both same-scale and cross-scale parents. -- Duplicate canonical publishers for one `(scale, variable)` are invalid in multi-rate mode unless non-canonical producers are marked `:stream_only`. -- User `extra` arguments are not allowed in MTG runs because `GraphSimulation` already occupies that slot. -- String scale names still work in many places but are deprecated. Prefer `Symbol` scales. -- `ModelList` is deprecated as the primary API. Prefer `ModelMapping`. -- `run_node_multiscale!` currently uses `node.simulation_id[1]` as the visitation guard. Treat that code carefully if you touch traversal semantics. -- Some variable collection helpers use set-like flattening, so collection order is not always stable. Do not attach semantics to incidental variable ordering. +- Keep model parameters and status values generic; do not force `Float64`. +- Preserve concrete model, status, carrier, and stream types. +- Do not copy values when a reference carrier is sufficient. +- Keep dynamic dispatch at compiled batch boundaries, not per object. +- Preserve cached model bundles and homogeneous execution batches. +- Test allocations for hot loops that run over many organs. ## High-Signal Files -- `src/PlantSimEngine.jl`: module layout and exports. -- `src/Abstract_model_structs.jl`: `AbstractModel` and `process`. -- `src/processes/process_generation.jl`: `@process`. -- `src/processes/models_inputs_outputs.jl`: model declarations and runtime traits. -- `src/variables_wrappers.jl`: `UninitializedVar`, `PreviousTimeStep`, `RefVariable`. -- `src/component_models/Status.jl`: reference-based status container. -- `src/component_models/RefVector.jl`: vector of references. -- `src/dependencies/*`: hard and soft dependency graph construction and traversal. -- `src/mtg/MultiScaleModel.jl`: mapping syntax normalization. -- `src/mtg/ModelSpec.jl`: runtime configuration wrapper. -- `src/mtg/mapping/*`: mapping compilation, reverse mapping, initialization helpers. -- `src/mtg/initialisation.jl`: status creation and MTG wiring. -- `src/mtg/GraphSimulation.jl`: simulation wrapper. -- `src/time/multirate.jl`: clocks, policies, temporal storage types. -- `src/time/runtime/*`: input resolution, scopes, publishers, meteo sampling, output export. -- `src/run.jl`: single-scale and multiscale execution. - -## Practical Rule For Future Changes - -If you change dependency, mapping, or runtime behavior, re-check all of these questions: - -1. Does it still work for both single-scale and MTG runs? -2. Does it preserve aliasing semantics for `Status` and `RefVector`? -3. Does it preserve the distinction between hard dependencies and soft dependencies? -4. Does it still handle scalar mappings, vector mappings, same-scale aliasing, and cross-scale writes? -5. Does it still behave correctly with `PreviousTimeStep`? -6. Does it still work when input bindings are inferred instead of explicit? -7. Does it still work in multi-rate mode with temporal policies and scoped streams? -8. Does it remain correct if the producer is nested under a hard dependency? +- `src/scene_object_api.jl`: registry, selectors, compilation, execution, + lifecycle, hard calls, temporal streams, and output collection. +- `src/ModelSpec.jl`: model application configuration. +- `src/component_models/Status.jl`: reference-based status. +- `src/component_models/RefVector.jl`: homogeneous reference vectors. +- `src/time/multirate.jl`: clocks and temporal policies. +- `src/time/runtime/clocks.jl`: Dates-based timing. +- `src/time/runtime/meteo_sampling.jl`: weather sampling. +- `src/time/runtime/environment_backends.jl`: environment backend contract. +- `test/test-unified-scene-object-api.jl`: primary behavioral coverage. + +## Change Checklist + +When changing compilation or runtime behavior, verify: + +1. one object and many objects; +2. same-object and cross-object inputs; +3. `One`, `OptionalOne`, and `Many`; +4. hard calls and iterative publication; +5. duplicate writers and `Updates`; +6. multirate policies and `PreviousTimeStep`; +7. global and spatial environments; +8. object creation, removal, reparenting, and movement; +9. templates, instances, and overrides; +10. generic numeric types and allocation-sensitive execution. diff --git a/Project.toml b/Project.toml index 7db8b1067..db0b8d1a1 100644 --- a/Project.toml +++ b/Project.toml @@ -4,31 +4,23 @@ version = "0.14.0" authors = ["Rémi Vezy and contributors"] [deps] -AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" -DataAPI = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" -FLoops = "cc61a311-1640-44b5-9fba-1b764f453329" Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" MultiScaleTreeGraph = "dd4a991b-8a45-4075-bede-262ee62d5583" PlantMeteo = "4630fe09-e0fb-4da5-a846-781cb73437b6" -SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" Term = "22787eb5-b846-44ae-b979-8e399b8463ab" [compat] -AbstractTrees = "0.4" CSV = "0.10" -DataAPI = "1.15" DataFrames = "1" Dates = "1.10" -FLoops = "0.2" Markdown = "1.10" MultiScaleTreeGraph = "0.15.1" PlantMeteo = "0.8.2" -SHA = "0.7.0" Statistics = "1.10" Tables = "1" Term = "2" diff --git a/README.md b/README.md index 8789a4ac5..f872e1346 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,8 @@ TimeStep Environment ``` -This API is the primary path for new multiscale, multi-plant, soil, -microclimate, and scene-scale simulations. Older `ModelMapping`, -`MultiScaleModel`, domain, and route APIs are retained only as qualified -compatibility tools while existing examples are migrated. +This is the package API for multiscale, multi-plant, soil, microclimate, and +scene-scale simulations. ## Installation diff --git a/docs/make.jl b/docs/make.jl index d065ca459..4d810867e 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -41,43 +41,15 @@ makedocs(; "Quick examples" => "./step_by_step/quick_and_dirty_examples.md", "Implementing a process" => "./step_by_step/implement_a_process.md", "Implementing a model" => "./step_by_step/implement_a_model.md", - "Parallelization" => "./step_by_step/parallelization.md", "Advanced coupling and hard dependencies" => "./step_by_step/advanced_coupling.md", "Implementing a model : additional notes" => "./step_by_step/implement_a_model_additional.md", ], "Model execution" => "model_execution.md", "Model traits" => "model_traits.md", "AI agent skill" => "agent_skill.md", - "Working with data" => [ - "Reducing DoF" => "./working_with_data/reducing_dof.md", - "Fitting" => "./working_with_data/fitting.md", - "Input types" => "./working_with_data/inputs.md", - "Visualizing outputs and data" => "./working_with_data/visualising_outputs.md", - "Floating-point considerations" => "./working_with_data/floating_point_accumulation_error.md", - ], - "Legacy MTG mapping tutorials" => [ - "Multiscale considerations" => "./multiscale/multiscale_considerations.md", - "Converting a simulation to multi-scale" => "./multiscale/single_to_multiscale.md", - "More variable mapping examples" => "./multiscale/multiscale.md", - "Handling cyclic dependencies" => "./multiscale/multiscale_cyclic.md", - "Multiscale coupling considerations" => "./multiscale/multiscale_coupling.md", - "Building a simple plant" => [ - "A rudimentary plant simulation" => "./multiscale/multiscale_example_1.md", - "Expanding the plant simulation" => "./multiscale/multiscale_example_2.md", - "Fixing bugs in the plant simulation" => "./multiscale/multiscale_example_3.md", - ], - "Visualizing our toy plant with PlantGeom" => "./multiscale/multiscale_example_4.md", - ], - "Legacy mapping multi-rate tutorials" => [ - "Introduction to multi-rate execution" => "./multirate/introduction.md", - "Step-by-step hourly/daily/weekly simulation" => "./multirate/multirate_tutorial.md", - "Advanced multi-rate configuration" => "./multirate/advanced_configuration.md", - ], + "Parameter fitting" => "./working_with_data/fitting.md", "Troubleshooting and testing" => [ - "Troubleshooting" => "./troubleshooting_and_testing/plantsimengine_and_julia_troubleshooting.md", "Automated testing" => "./troubleshooting_and_testing/downstream_tests.md", - "Tips and Workarounds" => "./troubleshooting_and_testing/tips_and_workarounds.md", - "Implicit contracts" => "./troubleshooting_and_testing/implicit_contracts.md", ], "API" => [ "Public API" => "./API/API_public.md", "Example models" => "./API/API_examples.md", diff --git a/docs/src/API/API_public.md b/docs/src/API/API_public.md index 163f776b9..f20cfa503 100644 --- a/docs/src/API/API_public.md +++ b/docs/src/API/API_public.md @@ -2,110 +2,55 @@ ## Unified Scene/Object API -New multiscale and multi-object scenarios should start with the following -groups. - ### Scenario and model applications -- `Scene` stores the runtime object graph, model applications, instances, and - environment. -- `Object` represents one runtime entity with stable identity, labels, - topology, optional geometry, and status. -- `ObjectTemplate` and `ObjectInstance` reuse one plant or object model across - several concrete instances. +- `Scene` stores objects, model applications, instances, and environment. +- `Object` represents one runtime entity with stable identity and status. +- `ObjectTemplate` and `ObjectInstance` reuse a model across instances. - `ModelSpec(model; name=...)` identifies one model application. -- `AppliesTo(...)` selects the objects where that application runs. +- `AppliesTo(...)` selects its target objects. -### Value coupling and manual calls +### Coupling -- `Inputs(...)` declares consumer-side value dependencies. -- `Calls(...)` gives a parent model manually executable child targets. +- `Inputs(...)` declares value dependencies. +- `Calls(...)` declares manually executable child models. - `Updates(:variable; after=...)` orders intentional duplicate writers. -- `Input(...)` and `Call(...)` express model-author defaults through - `dep(model)`. +- `Input(...)` and `Call(...)` express model defaults through `dep(model)`. - `run_call!(target; publish=false)` executes a trial hard call. -### Selectors and scopes +### Selectors - Multiplicity: `One(...)`, `OptionalOne(...)`, and `Many(...)`. - Scope: `SceneScope()`, `Self()`, `SelfPlant()`, `Ancestor(...)`, and `Scope(name)`. - Labels and topology: `Kind(...)`, `Species(...)`, `Scale(...)`, and `Relation(...)`. -- `ObjectAddress` is the normalized compiled selector representation. ### Time and environment -- `TimeStep(period::Dates.Period)` sets an application cadence and overrides a - model `timespec(...)` trait when both are present. -- `timespec(::Type{<:AbstractModel})` can provide a model-author default - cadence for scene/object applications. -- `timestep_hint(::Type{<:AbstractModel})` can validate base-step-derived - scene cadences when `TimeStep(...)` and non-default `timespec(...)` are - absent. -- `HoldLast`, `Interpolate`, `Integrate`, and `Aggregate` define temporal input - behavior. -- `output_policy(::Type{<:AbstractModel})` can provide a producer-side default - temporal policy when an `Inputs(...)` selector omits `policy=...`; explicit - selector policies take precedence. -- `Environment(...)` optionally overrides automatic environment provider, - resolver selection, or source remapping such as - `Environment(; sources=(CO2=:Ca,))`. +- `TimeStep(period)` sets an application cadence. +- `HoldLast`, `Interpolate`, `Integrate`, and `Aggregate` define temporal + input policies. +- `Environment(...)` configures environment providers and source remapping. - Models declare environment variables with `meteo_inputs_` and `meteo_outputs_`. -- `meteo_hint(::Type{<:AbstractModel})` can provide model-author default - environment source remaps through its `bindings` field; scenario - `Environment(; sources=...)` overrides those defaults. -- `OutputRequest(...)` can be passed to `run!(scene; tracked_outputs=...)` to - collect resampled scene output streams after the run. Use `process=...` - when the process identifies one publisher, or `application=...` when the - same process is applied more than once. An explicit application may select a - `:stream_only` publisher. +- `OutputRequest(...)` selects retained and resampled scene output streams. -### Lifecycle and compilation +### Lifecycle -- `objects_from_mtg` and `Scene(mtg; ...)` adapt an existing MTG subtree to the - unified object registry. +- `objects_from_mtg` and `Scene(mtg; ...)` adapt an MTG into the object + registry. - `register_object!`, `remove_object!`, and `reparent_object!` change topology. - `move_object!` and `update_geometry!` change spatial state. -- `refresh_bindings!` compiles selectors, carriers, calls, writer order, and - schedules. -- `refresh_environment_bindings!` compiles cached object-to-environment - bindings. +- `refresh_bindings!` recompiles structural bindings. +- `refresh_environment_bindings!` recompiles spatial environment bindings. - `run!(scene; steps=...)` returns a `SceneSimulation`. -- `scene_outputs(sim)` returns retained typed output streams. -- `collect_outputs(sim)` returns all retained streams, or requested outputs - when `tracked_outputs` was provided. - -```julia -request = OutputRequest( - :Leaf, - :transpiration; - name=:leaf_transpiration_2h, - application=:sunlit_leaf_energy, - policy=Integrate(), - clock=Dates.Hour(2), -) - -sim = run!(scene; steps=24, tracked_outputs=request) -out = collect_outputs(sim, :leaf_transpiration_2h; sink=DataFrames.DataFrame) -``` - -For scene/object runs, `tracked_outputs` is materialized from retained -scene-local output streams. With explicit output requests, the runtime retains -only requested application/variable streams plus streams required by temporal -`Inputs(...)`; `tracked_outputs=OutputRequest[]` retains no output streams -unless temporal dependencies require one. Dynamic objects are exported only -over their own published sample interval. Use `explain_output_retention(sim)` -to inspect why a stream was retained. Dependency-only streams are bounded to -the history required by their temporal policy; requested streams retain their -complete history. Retention explanations report the compiled -`retention_steps`, or `nothing` for full-history streams. +- `collect_outputs(sim)` materializes retained output streams. -### Structured explanations +### Explanations -Use these instead of inspecting internal dictionaries: +Use structured explanation helpers instead of inspecting internals: - `explain_objects` - `explain_instances` @@ -122,7 +67,7 @@ Use these instead of inspecting internal dictionaries: - `explain_outputs` See [Migrating To The Scene/Object API](../migration_scene_object.md) for -complete old-to-new translations. +translations from removed APIs. ## Index @@ -130,168 +75,9 @@ complete old-to-new translations. Pages = ["API_public.md"] ``` -## API documentation +## API Documentation ```@autodocs Modules = [PlantSimEngine] Private = false ``` - -## Legacy Mapping Multi-Rate Examples - -!!! warning "Legacy configuration surface" - The examples below document the mapping/MTG runtime retained during the - breaking migration. For new scene/object scenarios, use `TimeStep(...)` - and put source, policy, and window information directly on `Inputs(...)`. - `ModelMapping` is no longer exported; retained compatibility code must use - `PlantSimEngine.ModelMapping(...)`. - -For mapping-level multi-rate configuration, combine: - -- `PlantSimEngine.ModelMapping(...)` -- `ModelSpec(...)` -- `PlantSimEngine.TimeStepModel(...)` -- `PlantSimEngine.InputBindings(...)` -- `PlantSimEngine.MeteoBindings(...)` -- `PlantSimEngine.MeteoWindow(...)` -- `OutputRouting(...)` -- `PlantSimEngine.ScopeModel(...)` -- `timespec(::Type{<:AbstractModel})` (optional trait) -- `output_policy(::Type{<:AbstractModel})` (optional trait) -- `timestep_hint(::Type{<:AbstractModel})` (optional trait) -- `meteo_hint(::Type{<:AbstractModel})` (optional trait) -- `resolved_model_specs(mapping)` (utility) -- `explain_model_specs(mapping_or_sim)` (utility) -- `OutputRequest(...)` in `tracked_outputs` for resampled exports - -`PlantSimEngine.TimeStepModel(...)` accepts: -- `Real` step counts -- `ClockSpec` -- fixed `Dates` periods (`Dates.Second`, `Dates.Minute`, `Dates.Hour`, `Dates.Day`, ...) - -Period conversion detail: -- Period-based timesteps are converted using the meteo base step `duration`. -- Example: `PlantSimEngine.TimeStepModel(Dates.Day(1))` with hourly meteo (`Dates.Hour(1)`) maps to `ClockSpec(24.0, 1.0)`, - so execution times are `t = 1, 25, 49, ...`. - -Trait-based inference detail: -- If `PlantSimEngine.TimeStepModel(...)` is omitted, runtime resolves timestep from: -: `timespec(model)` when non-default, otherwise meteo `duration`. -- `timestep_hint(::Type{<:Model})` is then interpreted as: -: `required` = hard compatibility constraint, `preferred` = informational only. -- If `PlantSimEngine.InputBindings(...)` is omitted, same-name sources are inferred automatically from -: unique producers (same scale first, then cross-scale). Ambiguous cases require explicit bindings. -- For inferred bindings, policy defaults to producer `output_policy` when defined, otherwise `HoldLast()`. -- Explicit `PlantSimEngine.InputBindings(..., policy=...)` always overrides trait defaults. -- `output_policy` is hint-only: it is applied only when an output is actually consumed/exported. -- If `PlantSimEngine.MeteoBindings(...)` / `PlantSimEngine.MeteoWindow(...)` are omitted, `meteo_hint(::Type{<:Model})` -: may provide `(; bindings=..., window=...)`. -- Explicit mapping-level configuration always overrides hints. - -Compatibility checks: -- Meteo `duration` is mandatory when meteo is provided. -- For models with meteo-derived timestep, runtime enforces `timestep_hint.required`. -- `timestep_hint.preferred` never sets runtime timestep by itself. - -Scope selection detail: -- `PlantSimEngine.ScopeModel(:global)` is the default and shares streams across the whole simulation. -- `PlantSimEngine.ScopeModel(:plant)` isolates streams within each plant subtree. -- `PlantSimEngine.ScopeModel(:scene)` isolates by scene ancestor. -- `PlantSimEngine.ScopeModel(:self)` isolates by node id. - -### Exporting variables at requested rates - -```julia -req_hold = OutputRequest(:Leaf, :A; name=:A_hourly, process=:assim, policy=HoldLast()) -req_day = OutputRequest(:Leaf, :A; name=:A_daily_sum, process=:assim, policy=Integrate(), clock=ClockSpec(24.0, 1.0)) -run!(sim, meteo; tracked_outputs=[req_hold, req_day], executor=SequentialEx()) -out = collect_outputs(sim; sink=DataFrame) - -# or directly: -out_status, out = run!( - sim, - meteo; - tracked_outputs=[req_hold, req_day], - return_requested_outputs=true, -) -``` - -- `process` is optional when the source is canonical and unique. -- `policy` defines how source streams are resampled at export time. -- `clock` defines the export schedule; omit it to export every simulation step. - -### Default hold-last - -```julia -ModelSpec(ConsumerModel()) |> -PlantSimEngine.TimeStepModel(ClockSpec(2.0, 1.0)) |> -PlantSimEngine.InputBindings(; x=(process=:producer, var=:x)) -``` - -### Meteo aggregation bindings - -```julia -ModelSpec(DailyModel()) |> -PlantSimEngine.TimeStepModel(ClockSpec(24.0, 1.0)) |> -PlantSimEngine.MeteoWindow(CalendarWindow(:day; anchor=:current_period, week_start=1, completeness=:strict)) |> -PlantSimEngine.MeteoBindings( - T=MeanWeighted(), # default source is :T - Ri_SW_f=RadiationEnergy(), # integrate W m-2 to MJ m-2 over the model window - custom_peak=(source=:custom_var, reducer=MaxReducer()), -) -``` - -`PlantSimEngine.MeteoWindow(...)` options: -- `RollingWindow()` (default): trailing rolling window driven by `dt`. -- `CalendarWindow(period; anchor, week_start, completeness)` with: -: `period` in `:day`, `:week`, `:month` -: `anchor` in `:current_period`, `:previous_complete_period` -: `week_start` in `1:7` (1 = Monday) -: `completeness` in `:allow_partial`, `:strict` - -### Parameterized window reducers - -`Integrate()` defaults to `SumReducer()`; `Aggregate()` defaults to `MeanReducer()`. -With the same reducer, they are runtime-equivalent. -Use `Integrate` for accumulation semantics and `Aggregate` for summary-statistics semantics. - -```julia -ModelSpec(DailyModel()) |> -PlantSimEngine.TimeStepModel(ClockSpec(24.0, 1.0)) |> -PlantSimEngine.InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate(SumReducer()))) - -ModelSpec(DailyModel()) |> -PlantSimEngine.TimeStepModel(ClockSpec(24.0, 1.0)) |> -PlantSimEngine.InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Aggregate(MaxReducer()))) - -ModelSpec(DailyModel()) |> -PlantSimEngine.TimeStepModel(ClockSpec(24.0, 1.0)) |> -PlantSimEngine.InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate(vals -> maximum(vals) - minimum(vals)))) - -ModelSpec(DailyModel()) |> -PlantSimEngine.TimeStepModel(ClockSpec(24.0, 1.0)) |> -PlantSimEngine.InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate((vals, durations) -> sum(vals .* durations)))) - -ModelSpec(DailyModel()) |> -PlantSimEngine.TimeStepModel(ClockSpec(24.0, 1.0)) |> -PlantSimEngine.InputBindings(; a=(process=:hourly_assim, var=:A, scale=:Leaf, policy=Integrate(PlantMeteo.DurationSumReducer()))) -``` - -Built-in reducer types are: -`SumReducer()`, `MeanReducer()`, `MaxReducer()`, `MinReducer()`, `FirstReducer()`, `LastReducer()`. -The same reducer objects are also used by `PlantSimEngine.MeteoBindings(...)`. -Custom reducers/callables can accept either `(values)` or `(values, durations_seconds)`. - -### Parameterized interpolation mode - -`Interpolate()` defaults to `mode=:linear, extrapolation=:linear`. - -```julia -ModelSpec(FastModel()) |> -PlantSimEngine.TimeStepModel(1.0) |> -PlantSimEngine.InputBindings(; x=(process=:slow_source, var=:x, policy=Interpolate())) - -ModelSpec(FastModel()) |> -PlantSimEngine.TimeStepModel(1.0) |> -PlantSimEngine.InputBindings(; x=(process=:slow_source, var=:x, policy=Interpolate(; mode=:hold, extrapolation=:hold))) -``` diff --git a/docs/src/FAQ/translate_a_model.md b/docs/src/FAQ/translate_a_model.md deleted file mode 100644 index df24a6cec..000000000 --- a/docs/src/FAQ/translate_a_model.md +++ /dev/null @@ -1,157 +0,0 @@ -# I want to use PlantSimEngine for my model - -```@setup mymodel -using PlantSimEngine -using CairoMakie -# Import the example models defined in the `Examples` sub-module: -using PlantSimEngine.Examples -using PlantMeteo, Dates - -function lai_toymodel(TT_cu; max_lai=8.0, dd_incslope=500, inc_slope=70, dd_decslope=1000, dec_slope=20) - LAI = max_lai * (1 / (1 + exp((dd_incslope - TT_cu) / inc_slope)) - 1 / (1 + exp((dd_decslope - TT_cu) / dec_slope))) - if LAI < 0 - LAI = 0 - end - return LAI -end - -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) -``` - -If you already have a model, you can easily use `PlantSimEngine` to couple it with other models with minor adjustments. - -## Toy LAI Model - -### Model description - -Let's take an example with a simple LAI model that we define below: - -```julia -""" -Simulate leaf area index (LAI, m² m⁻²) for a crop based on the amount of degree-days since sowing with a simple double-logistic function. - -# Arguments - -- `TT_cu`: degree-days since sowing -- `max_lai=8`: Maximum value for LAI -- `dd_incslope=500`: degree-days at which we get the maximal increase in LAI -- `inc_slope=5`: slope of the increasing part of the LAI curve -- `dd_decslope=1000`: degree-days at which we get the maximal decrease in LAI -- `dec_slope=2`: slope of the decreasing part of the LAI curve -""" -function lai_toymodel(TT_cu; max_lai=8.0, dd_incslope=500, inc_slope=70, dd_decslope=1000, dec_slope=20) - LAI = max_lai * (1 / (1 + exp((dd_incslope - TT_cu) / inc_slope)) - 1 / (1 + exp((dd_decslope - TT_cu) / dec_slope))) - if LAI < 0 - LAI = 0 - end - return LAI -end -``` - -This model takes the number of days since sowing as input and returns the simulated LAI. We can plot the simulated LAI for a year: - -```@example mymodel -using CairoMakie - -lines(1:1300, lai_toymodel.(1:1300), color=:green, axis=(ylabel="LAI (m² m⁻²)", xlabel="Days since sowing")) -``` - -### Changes for PlantSimEngine - -The model can be implemented using `PlantSimEngine` as follows: - -#### Define a process - -If the process of LAI dynamic is not implemented yet, we can define it like so: - -```julia -@process LAI_Dynamic -``` - -#### Define the model - -We have to define a structure for our model that will contain the parameters of the model: - -```julia -struct ToyLAIModel <: AbstractLai_DynamicModel - max_lai::Float64 - dd_incslope::Int - inc_slope::Float64 - dd_decslope::Int - dec_slope::Float64 -end -``` - -We can also define default values for the parameters by defining a method with keyword arguments: - -```julia -ToyLAIModel(; max_lai=8.0, dd_incslope=500, inc_slope=70, dd_decslope=1000, dec_slope=20) = ToyLAIModel(max_lai, dd_incslope, inc_slope, dd_decslope, dec_slope) -``` - -This way users can create a model with default parameters just by calling `ToyLAIModel()`, or they can specify only the parameters they want to change, *e.g.* `ToyLAIModel(inc_slope=80.0)` - -#### Define inputs / outputs - -Then we can define the inputs and outputs of the model, and the default value at initialization: - -```julia -PlantSimEngine.inputs_(::ToyLAIModel) = (TT_cu=-Inf,) -PlantSimEngine.outputs_(::ToyLAIModel) = (LAI=-Inf,) -``` - -!!! note - Note that we use `-Inf` for the default value, it is the recommended value for `Float64` (-999 for `Int`), as it is a valid value for this type, and is easy to catch in the outputs if not properly set because it propagates nicely. You can also use `NaN` instead. - -#### Define the model function - -Finally, we can define the model function that will be called at each time step: - -```julia -function PlantSimEngine.run!(::ToyLAIModel, models, status, meteo, constants=nothing, extra=nothing) - status.LAI = models.LAI_Dynamic.max_lai * (1 / (1 + exp((models.LAI_Dynamic.dd_incslope - status.TT_cu) / model.LAI_Dynamic.inc_slope)) - 1 / (1 + exp((models.LAI_Dynamic.dd_decslope - status.TT_cu) / models.LAI_Dynamic.dec_slope))) - - if status.LAI < 0 - status.LAI = 0 - end -end -``` - -!!! note - Note that we don't return the value of the LAI in the definition of the function. This is because we rather update its value in the status directly. The status is a structure that efficiently stores the state of the model at each time step, and it contains all variables either declared as inputs or outputs of the model. This way, we can access the value of the LAI at any time step by calling `status.LAI`. - -!!! note - The function is defined for **one time step** only, and is called at each time step automatically by PlantSimEngine. This means that we don't have to loop over the time steps in the function. - -#### [Running a simulation](@id defining_the_meteo) - -Now that we have everything set up, we can run a simulation. The first step here is to define the weather: - -```julia -# Import the packages we need: -using PlantMeteo, Dates - -# Define the period of the simulation: -period = [Dates.Date("2021-01-01"), Dates.Date("2021-12-31")] - -# Get the weather data for CIRAD's site in Montpellier, France: -meteo = get_weather(43.649777, 3.869889, period, sink = DataFrame) - -# Compute the degree-days with a base temperature of 10°C: -meteo.TT = max.(meteo.T .- 10.0, 0.0) - -# Aggregate the weather data to daily values: -meteo_day = to_daily(meteo, :TT => (x -> sum(x) / 24) => :TT) -``` - -Then we can define our list of models, passing the values for `TT_cu` in the status at initialization: - -```@example mymodel -m = PlantSimEngine.ModelMapping( - ToyLAIModel(), - status = (TT_cu = cumsum(meteo_day.TT),), -) - -outputs_sim = run!(m) - -lines(outputs_sim[:TT_cu], outputs_sim[:LAI], color=:green, axis=(ylabel="LAI (m² m⁻²)", xlabel="Days since sowing")) -``` diff --git a/docs/src/agent_skill.md b/docs/src/agent_skill.md index ab4547829..db92a15ae 100644 --- a/docs/src/agent_skill.md +++ b/docs/src/agent_skill.md @@ -28,10 +28,9 @@ gives agents the package-specific conventions they need for: - implementing or wrapping models with `@process`, `inputs_`, `outputs_`, `run!`, hard dependencies, and model traits. -The legacy `ModelMapping` and `MultiScaleModel` APIs remain -useful when maintaining existing simulations, but agents should not select them -for new scene/object scenarios. See -[Migrating To The Scene/Object API](migration_scene_object.md). +The superseded `ModelMapping` and `MultiScaleModel` runtimes have been removed. +Use [Migrating To The Scene/Object API](migration_scene_object.md) when +translating historical code. The canonical source is [`skills/plantsimengine/SKILL.md`](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/skills/plantsimengine/SKILL.md). diff --git a/docs/src/developers.md b/docs/src/developers.md index 0e6981384..d5523aedc 100644 --- a/docs/src/developers.md +++ b/docs/src/developers.md @@ -95,16 +95,6 @@ were editing. ## Implementation notes -### Generated models from status vectors - -Some multiscale helpers turn status vectors into internal runtime models so that -they can be used in mapping-based simulations. The implementation is kept -deliberately data-driven to avoid top-level `eval()` and world-age issues. - -The relevant code lives in `src/mtg/mapping/model_generation_from_status_vectors.jl`. -If you touch that area, preserve the ability to generate the mapping and build a -`GraphSimulation` within the same function scope. - ### Coverage gaps to keep in mind Not every combination of weather structure, status shape, mapping layout, and diff --git a/docs/src/index.md b/docs/src/index.md index 8dd0c9866..90d4cce04 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -20,10 +20,8 @@ Depth = 4 New multiscale, multi-plant, soil, scene, and microclimate scenarios should use the unified `Scene`/`Object` API with `AppliesTo`, `Inputs`, `Calls`, `Updates`, `TimeStep`, and `Environment`. See - [Migrating To The Scene/Object API](migration_scene_object.md). The old - domain, route, and mapping constructors are no longer exported; their - implementation remains temporarily available under qualified - `PlantSimEngine.*` names for regression coverage and migration work. + [Migrating To The Scene/Object API](migration_scene_object.md). Superseded + mapping constructors and their implementation have been removed. ## Overview @@ -241,8 +239,6 @@ environment writes are published exactly once. selectors, lifecycle hooks, and explanation helpers. - [Model traits](model_traits.md) explains `inputs_`, `outputs_`, `dep`, `timespec`, `output_policy`, `meteo_inputs_`, and `meteo_outputs_`. -- [Legacy MTG mapping tutorials](multiscale/multiscale_considerations.md) are - retained for existing simulations while the scene/object tutorials mature. ## Performance diff --git a/docs/src/migration_scene_object.md b/docs/src/migration_scene_object.md index 2f292b2f0..1ebe0af84 100644 --- a/docs/src/migration_scene_object.md +++ b/docs/src/migration_scene_object.md @@ -37,8 +37,7 @@ equivalents. Legacy simulations split configuration between `ModelMapping` and `MultiScaleModel`. The unified API stores runtime entities in one `Scene`: -`ModelMapping` is no longer exported. While migrating historical code, use -`PlantSimEngine.ModelMapping(...)` explicitly; new scenarios should use the +`ModelMapping` has been removed. Historical code must be translated to the scene/object form below. ```julia diff --git a/docs/src/model_coupling/model_coupling_user.md b/docs/src/model_coupling/model_coupling_user.md deleted file mode 100644 index f940d0cf1..000000000 --- a/docs/src/model_coupling/model_coupling_user.md +++ /dev/null @@ -1,173 +0,0 @@ -# Model coupling for users - -```@setup usepkg -using PlantSimEngine, PlantMeteo, Dates -# Import the example models defined in the `Examples` sub-module: -using PlantSimEngine.Examples - -m = PlantSimEngine.ModelMapping( - Process1Model(2.0), - Process2Model(), - Process3Model(), - Process4Model(), - Process5Model(), - Process6Model(), - Process7Model(), -) -``` - -`PlantSimEngine.jl` is designed to make model coupling simple for both the modeler and the user. For example, `PlantBiophysics.jl` implements the [`Fvcb`](https://vezy.github.io/PlantBiophysics.jl/stable/functions/#PlantBiophysics.Fvcb) model to simulate the photosynthesis process. This model needs the stomatal conductance process to be simulated, so it calls again `run!` inside its implementation at some point. Note that it does not force any kind of conductance model over another, just that there is one to simulate the process. This ensures that users can choose whichever model they want to use for this simulation, independent of the photosynthesis model. - -We provide an example script that implements seven dummy processes in [`examples/dummy`](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/examples/dummy.jl). The processes are simply called "process1", "process2"..., and the model implementations are called `Process1Model`, `Process2Model`... - -## Hard coupled models - -The `Process3Model` calls `Process2Model`, and `Process2Model` calls `Process1Model`. This explicit call is called a hard-dependency in PlantSimEngine. - -The other models for the other processes are called `Process4Model`, `Process5Model`... and they do not call explicitly other models when running, but some outputs of the models are used as inputs of other models. This is called a soft-dependency in PlantSimEngine. - -!!! tip - Hard-coupling of models is usually done when there are some kind of iterative computation in one of the models that depend on one another. This is not the case in our example here as it is obviously just a simple one. In this case the coupling is not really necessary as models could just be called sequentially one after the other. For a more representative example, you can look at the energy balance computation of Monteith in `PlantBiophysics.jl`, which is hard-coupled to a photosynthesis model. - -Back to our example, using `Process3Model` requires a "process2" model, and in our case the only model available is `Process2Model`. The latter also requires a "process1" model, and again we only have one model implementation for this process, which is `Process1Model`. - -Let's use the `Examples` sub-module so we can play around: - -```julia -# Import the example models defined in the `Examples` sub-module: -using PlantSimEngine.Examples -``` - -!!! tip - Use subtype(x) to know which models are available for a process, e.g. for "process1" you can do `subtypes(AbstractProcess1Model)`. - -Here is how we can make the model coupling: - -```@example usepkg -m = PlantSimEngine.ModelMapping(Process1Model(2.0), Process2Model(), Process3Model()) -nothing # hide -``` - -We can see that only the first model has a parameter. You can usually know that by looking at the help of the structure (*e.g.* `?Process1Model`), else, you can still look at the field names of the structure like so `fieldnames(Process1Model)`. - -Note that the user only declares the models, not the way the models are coupled because `PlantSimEngine.jl` deals with that automatically. - -Now the example above returns some warnings saying we need to initialize some variables: `var1` and `var2`. `PlantSimEngine.jl` automatically computes which variables should be initialized based on the inputs and outputs of all models, considering their hard or soft-coupling. - -For example, `Process1Model` requires the following variables as inputs: - -```@example usepkg -inputs(Process1Model(2.0)) -``` - -And `Process2Model` requires the following variables: - -```@example usepkg -inputs(Process2Model()) -``` - -We see that `var1` is needed as inputs of both models, but we also see that `var3` is an output of `Process2Model`: - -```@example usepkg -outputs(Process2Model()) -``` - -So considering those two models, we only need `var1` and `var2` to be initialized, as `var3` is computed. This is why we recommend [`to_initialize`](@ref) instead of [`inputs`](@ref), because it returns only the variables that need to be initialized, considering that some inputs are duplicated between models, and some are computed by other models (they are outputs of a model): - -```@example usepkg -m = PlantSimEngine.ModelMapping( - Process1Model(2.0), - Process2Model(), - Process3Model(), - variables_check=false # Just so we don't have the warning printed out -) - -to_initialize(m) -``` - -The most straightforward way of initializing a model list is by giving the initializations to the `status` keyword argument during instantiation: - -```@example usepkg -m = PlantSimEngine.ModelMapping( - Process1Model(2.0), - Process2Model(), - Process3Model(), - status = (var1=15.0, var2=0.3) -) -nothing # hide -``` - -Our component models structure is now fully parameterized and initialized for a simulation! - -Let's simulate it: - -```@example usepkg -using PlantMeteo -meteo = Atmosphere(T = 22.0, Wind = 0.8333, P = 101.325, Rh = 0.4490995) - -run!(m, meteo) - -m[:var5] -``` - - -## Soft coupled models - -All following models (`Process4Model` to `Process7Model`) do not call explicitly other models when running, but some outputs of the models are used as inputs of other models. This is called a soft-dependency in PlantSimEngine. - -Let's make a new model list including the soft-coupled models: - -```@example usepkg -m = PlantSimEngine.ModelMapping( - Process1Model(2.0), - Process2Model(), - Process3Model(), - Process4Model(), - Process5Model(), - Process6Model(), - Process7Model(), -) -nothing # hide -``` - -With this list of models, we only need to initialize `var0`, that is an input of `Process4Model` and `Process7Model`: - -```@example usepkg -to_initialize(m) -``` - -We can initialize it like so: - -```@example usepkg -m = PlantSimEngine.ModelMapping( - Process1Model(2.0), - Process2Model(), - Process3Model(), - Process4Model(), - Process5Model(), - Process6Model(), - Process7Model(), - status = (var0=15.0,) -) -nothing # hide -``` - -Let's simulate it: - -```@example usepkg -using PlantMeteo -meteo = Atmosphere(T = 22.0, Wind = 0.8333, P = 101.325, Rh = 0.4490995) - -run!(m, meteo) - -status(m) -``` - -## Simulation order - -When calling `run!`, the models are run in the right order using a dependency graph that is computed automatically based on the hard and soft dependencies of the models following a simple set of rules: - -1. Independent models are run first. A model is independent if it can be run alone, or only using initializations. It is not dependent on any other model. -2. From their children dependencies: - 1. Hard dependencies are always run before soft dependencies. Inner hard dependency graphs are considered as a whole, *i.e.* as a single soft dependency. - 2. Soft dependencies are then run sequentially. If a soft dependency has several parent nodes (*i.e.* its inputs are computed by several models), it is run only if all its parent nodes have been run already. In practice, when we visit a node that has one of its parent that did not run already, we stop the visit of this branch. The node will eventually be visited from the branch of the last parent that was run. diff --git a/docs/src/model_execution.md b/docs/src/model_execution.md index 8ccc3a424..eda6a90ab 100644 --- a/docs/src/model_execution.md +++ b/docs/src/model_execution.md @@ -18,9 +18,7 @@ TimeStep Environment ``` -Legacy `ModelMapping`, `MultiScaleModel`, domain, and route APIs are retained -as qualified compatibility tools while old examples are migrated. New -scenarios should start from `Scene` and model applications. +Scenarios start from `Scene` and model applications. ## Model Kernels And Applications @@ -330,15 +328,9 @@ Structural changes invalidate compiled object/model bindings. Movement and geometry changes invalidate environment bindings without rebuilding structural input carriers. The next `run!(scene)` step refreshes the necessary caches. -## Compatibility Runtime +## Historical API Translation -Historical `ModelMapping` and MTG mapping simulations still work through -qualified compatibility constructors such as -`PlantSimEngine.ModelMapping(...)`, `PlantSimEngine.MultiScaleModel(...)`, -`PlantSimEngine.InputBindings(...)`, and -`PlantSimEngine.TimeStepModel(...)`. - -Those names are no longer the primary API. For new work, use: +The historical mapping runtime has been removed. Translate old code as follows: - `ModelMapping(...)` -> `Scene(...)` plus object-local `ModelSpec(...)` applications; diff --git a/docs/src/model_traits.md b/docs/src/model_traits.md index 2ac02bdbb..357d4dbda 100644 --- a/docs/src/model_traits.md +++ b/docs/src/model_traits.md @@ -1,120 +1,66 @@ -# Model traits +# Model Traits -This page centralizes the model-level traits that can be defined in `PlantSimEngine`. -It complements: +Model traits describe intrinsic model behavior. Scenario-specific coupling +belongs in `ModelSpec` through `AppliesTo`, `Inputs`, `Calls`, `TimeStep`, +`Environment`, `OutputRouting`, and `Updates`. -- [Model execution](model_execution.md) for runtime behavior, -- [Parallelization](step_by_step/parallelization.md) for execution over objects/time-steps. +## Variables -## Trait inventory for models - -### `timespec(::Type{<:MyModel})` - -Defines the default execution clock of a model. - -Default: - -```julia -PlantSimEngine.timespec(::Type{<:AbstractModel}) = ClockSpec(1.0, 0.0) -``` - -Use it when your model has a natural native clock (for example daily by default). - -### `output_policy(::Type{<:MyModel})` - -Defines per-output default schedule policy for produced streams. - -Default: +Implement `inputs_(model)` and `outputs_(model)` with default values: ```julia -PlantSimEngine.output_policy(::Type{<:AbstractModel}) = NamedTuple() +PlantSimEngine.inputs_(::MyModel) = (leaf_area=0.0,) +PlantSimEngine.outputs_(::MyModel) = (assimilation=0.0,) ``` -Behavior: +The declarations are used for status initialization, dependency inference, +validation, and type construction. -- unspecified outputs fall back to `HoldLast()`; -- used by runtime when resolving cross-clock reads; -- used as default policy for inferred `PlantSimEngine.InputBindings(...)` when users do not provide explicit bindings; -- hint-only and lazy: policy is applied only for outputs that are actually consumed/exported. - Declaring a policy for an unused output does not trigger integration work. +## Manual Dependencies -Example: +Implement `dep(model)` only when the model directly calls another process from +inside its own `run!` method: ```julia -PlantSimEngine.output_policy(::Type{<:MyModel}) = ( - carbon_assimilation=Integrate(), - leaf_temperature=Aggregate(MeanReducer()), +PlantSimEngine.dep(::EnergyBalance) = ( + photosynthesis=AbstractPhotosynthesisModel, ) ``` -Users can always override or complement this trait at mapping level: +The scenario binds the dependency with `Calls(...)`. The parent controls trial +iterations and accepted publication through `run_call!`. -```julia -ModelSpec(MyConsumerModel()) |> -PlantSimEngine.InputBindings( - ; - carbon_assimilation=(process=:myproducer, var=:carbon_assimilation, policy=HoldLast()), # override trait default - carbon_assimilation_max=(process=:myproducer, var=:carbon_assimilation, policy=Aggregate(MaxReducer())), # complement with extra derived input -) -``` - -### `timestep_hint(::Type{<:MyModel})` +## Timing -Optional compatibility hint when `PlantSimEngine.TimeStepModel(...)` is not provided. - -Default: +`timespec(model)` declares the model's default clock. The default is +`ClockSpec(1.0, 0.0)`. ```julia -PlantSimEngine.timestep_hint(::Type{<:AbstractModel}) = nothing +PlantSimEngine.timespec(::Type{<:DailyGrowth}) = ClockSpec(Dates.Day(1)) ``` -Supported forms include: - -- fixed period: `Dates.Hour(1)`; -- range: `(Dates.Minute(30), Dates.Hour(2))`; -- named tuple: `(; required=..., preferred=...)`. - -`required` is enforced when runtime uses meteo-derived timestep. -`preferred` is informational only. - -### `meteo_hint(::Type{<:MyModel})` - -Optional inference trait for weather sampling configuration. - -Default: - -```julia -PlantSimEngine.meteo_hint(::Type{<:AbstractModel}) = nothing -``` - -Expected value: +`output_policy(model)` declares the default temporal policy per output: ```julia -(; bindings=..., window=...) +PlantSimEngine.output_policy(::Type{<:MyModel}) = ( + assimilation=Integrate(), + leaf_temperature=Aggregate(MeanReducer()), +) ``` -Where: - -- `bindings` is compatible with `PlantSimEngine.MeteoBindings(...)`, -- `window` is compatible with `PlantSimEngine.MeteoWindow(...)`. - -### `meteo_inputs_(::MyModel)` -### `meteo_outputs_(::MyModel)` +Unspecified outputs use `HoldLast()`. A scenario can select another clock with +`TimeStep(...)` and another input policy in `Inputs(...)`. -Declare meteorology or microclimate variables separately from object status -variables. +`timestep_hint(model)` can declare required or preferred timestep constraints. +`meteo_hint(model)` can provide default environment sampling configuration. -Default: - -```julia -PlantSimEngine.meteo_inputs_(::AbstractModel) = NamedTuple() -PlantSimEngine.meteo_outputs_(::AbstractModel) = NamedTuple() -``` +## Environment Variables -Use `meteo_inputs_` for variables read from the weather or environment backend: +Use `meteo_inputs_(model)` for variables sampled from the active environment +backend: ```julia -PlantSimEngine.meteo_inputs_(::LeafEnergyBalanceModel) = ( +PlantSimEngine.meteo_inputs_(::LeafEnergyBalance) = ( T=0.0, Rh=0.0, Wind=0.0, @@ -123,70 +69,14 @@ PlantSimEngine.meteo_inputs_(::LeafEnergyBalanceModel) = ( ) ``` -Use `meteo_outputs_` when a model updates a mutable environment backend, for -example a microclimate model updating local air temperature: - -```julia -PlantSimEngine.outputs_(::MicroclimateUpdateModel) = (T=0.0,) -PlantSimEngine.meteo_outputs_(::MicroclimateUpdateModel) = (T=0.0,) -``` - -The current runtime scatters `meteo_outputs_` from status variables, so the -same variable should usually be declared in `outputs_` as well. - -### `TimeStepDependencyTrait(::Type{<:MyModel})` -### `ObjectDependencyTrait(::Type{<:MyModel})` - -Parallelization traits (single-scale runtime): - -- `TimeStepDependencyTrait`: depends or not on other timesteps; -- `ObjectDependencyTrait`: depends or not on other objects. - -Defaults are conservative (`dependent`) and can be overridden when safe. - -## Precedence rules - -Runtime precedence is intentionally explicit: - -1. Input policy: - explicit `PlantSimEngine.InputBindings(..., policy=...)` > inferred from producer `output_policy` > `HoldLast()`. -1. Timestep: - `PlantSimEngine.TimeStepModel(...)` > `timespec(model)` when non-default > meteo base step. -1. Meteo sampling: - explicit `PlantSimEngine.MeteoBindings(...)`/`PlantSimEngine.MeteoWindow(...)` > `meteo_hint(...)` > runtime defaults. - -## Is everything documented? - -For model-level traits, the documented set is now: - -- `timespec`, -- `output_policy`, -- `timestep_hint`, -- `meteo_hint`, -- `meteo_inputs_`, -- `meteo_outputs_`, -- `TimeStepDependencyTrait`, -- `ObjectDependencyTrait`. - -Outside model traits, `PlantSimEngine` also exposes data-format traits such as `DataFormat` for input containers (see [Input types](working_with_data/inputs.md)). - -## Naming conventions and API consistency - -Current API uses two naming styles on purpose: - -- snake_case for trait/query functions (`timespec`, `output_policy`, `timestep_hint`, `meteo_hint`); -- CamelCase for `ModelSpec` pipeline transforms (`TimeStepModel`, `InputBindings`, `MeteoBindings`, `MeteoWindow`, `OutputRouting`, `ScopeModel`). - -This distinction reflects role: +Use `meteo_outputs_(model)` for variables scattered back into a mutable +microclimate backend. Such variables are normally also declared in +`outputs_(model)` because the current runtime reads their values from status. -- snake_case: "what the model declares"; -- CamelCase: "what the mapping config applies". +## Precedence -For future unification, a non-breaking path would be: +Scenario configuration has precedence over model defaults: -1. keep existing names as stable API, -1. avoid plain snake_case aliases that would collide with existing getter names - (`input_bindings`, `meteo_bindings`, `output_routing`, `model_scope`), -1. if needed, add explicit config-oriented aliases with distinct names - (for example `*_config` forms) and keep current constructors, -1. evaluate deprecations only after one full release cycle and user feedback. +1. `Inputs(...)` policy, then producer `output_policy`, then `HoldLast()`. +2. `TimeStep(...)`, then `timespec(model)`, then the environment base step. +3. `Environment(...)`, then `meteo_hint(model)`, then backend defaults. diff --git a/docs/src/multirate/advanced_configuration.md b/docs/src/multirate/advanced_configuration.md deleted file mode 100644 index 7458d22fb..000000000 --- a/docs/src/multirate/advanced_configuration.md +++ /dev/null @@ -1,214 +0,0 @@ -# Advanced multi-rate configuration - -!!! warning "Legacy mapping configuration" - The qualified transforms on this page are compatibility internals. New - scene/object configurations place source and temporal policy in - `Inputs(...)`, cadence in `TimeStep(...)`, and meteo policy in - `Environment(...)`. - -This page collects the multi-rate features that were intentionally kept in the -background on the first two pages: - -- [Introduction to multi-rate execution](introduction.md) explains the core - scheduling rules; -- [Step-by-step multi-rate tutorial](multirate_tutorial.md) shows a complete - hourly/daily/weekly MTG example with minimal configuration; -- this page covers the explicit configuration tools you reach for when defaults - are no longer enough. - -The goal here is not to build another full simulation from scratch. Instead, the -objective is to explain when and why you should add more explicit multi-rate -declarations to a mapping. - -## 1. When the defaults are enough - -PlantSimEngine tries to keep simple mappings concise: - -- if a model does not declare `PlantSimEngine.TimeStepModel(...)`, it follows the meteo - cadence; -- if an input has a unique producer, `PlantSimEngine.InputBindings(...)` can often be omitted; -- if a model consumes common `Atmosphere` variables at a coarser cadence, - PlantMeteo default transforms can often replace explicit `PlantSimEngine.MeteoBindings(...)`; -- if an exported variable has a unique canonical publisher, `OutputRequest(...)` - can often omit `process=`. - -The sections below focus on the cases where that implicit behavior becomes too -ambiguous or too limiting. - -## 2. Explicit model-to-model bindings with `PlantSimEngine.InputBindings(...)` - -The tutorial pages rely on unique-producer inference plus `output_policy(...)` -declared on the source models. That is the simplest setup, but it stops being -enough as soon as several candidate producers exist or when you want to override -the default resampling rule. - -Use explicit `PlantSimEngine.InputBindings(...)` when: - -- several models can produce the same input variable; -- the same process exists at several reachable scales; -- the source variable has a different name than the consumer input; -- the producer default policy is not the policy you want for this particular - connection. - -For example, a daily plant model may need to say explicitly that it consumes the -hourly leaf assimilation stream from the `:Leaf` scale and integrates it over -the day: - -```julia -plant_daily_spec = ModelSpec(TutorialPlantDailyModel()) |> - PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)) |> - PlantSimEngine.InputBindings(; - leaf_assim_h=( - process=:tutorialleafhourly, - scale=:Leaf, - var=:leaf_assim_h, - policy=Integrate(), - ), - ) -``` - -This is more verbose than inference, but the resulting mapping is also more -explicit: anyone reading it can see exactly where the data comes from and how it -is reduced. - -## 3. Explicit meteorological aggregation with `PlantSimEngine.MeteoBindings(...)` - -For common `Atmosphere` variables, PlantSimEngine delegates weather sampling to -PlantMeteo, and PlantMeteo already defines default transforms. In practice, this -means you often do not need `PlantSimEngine.MeteoBindings(...)` for variables such as `T`, -`Rh`, or aliases like `Ri_SW_q`. - -Add explicit `PlantSimEngine.MeteoBindings(...)` when: - -- you want a non-default reducer; -- the target variable should come from a differently named source variable; -- the variable is not covered by PlantMeteo defaults; -- you want the mapping itself to document the intended weather aggregation rule. - -For example, this daily model makes the defaults explicit for temperature and -shortwave radiation energy: - -```julia -plant_daily_spec = ModelSpec(TutorialPlantDailyModel()) |> - PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)) |> - PlantSimEngine.MeteoBindings( - ; - T=MeanWeighted(), - Ri_SW_q=(source=:Ri_SW_f, reducer=RadiationEnergy()), - ) -``` - -And this variant shows a more genuinely custom rule: - -```julia -plant_daily_spec = ModelSpec(TutorialPlantDailyModel()) |> - PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)) |> - PlantSimEngine.MeteoBindings( - ; - T=(source=:T, reducer=MaxReducer()), - rad_peak=(source=:Ri_SW_f, reducer=MaxReducer()), - ) -``` - -The important point is that `PlantSimEngine.MeteoBindings(...)` is not only about reducing -weather from fast to slow. It is also a way to state the semantics of that -reduction explicitly. - -## 4. Calendar-aligned windows with `PlantSimEngine.MeteoWindow(...)` - -By default, coarser meteo sampling uses rolling windows that follow the model -clock. That is often sufficient, but some models are tied to civil periods such -as "the current day" or "the current week". - -In those cases, use `PlantSimEngine.MeteoWindow(...)` to replace the default trailing window -with a calendar-aligned one: - -```julia -plant_daily_spec = ModelSpec(TutorialPlantDailyModel()) |> - PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)) |> - PlantSimEngine.MeteoWindow( - CalendarWindow( - :day; - anchor=:current_period, - week_start=1, - completeness=:strict, - ), - ) -``` - -This becomes important when a daily or weekly model should aggregate over civil -days or weeks rather than over "the last 24 hours" or "the last 168 hours". - -## 5. Exporting streams with `OutputRequest(...)` - -The second tutorial page uses `OutputRequest(...)` to materialize clean -hourly/daily/weekly tables from the simulation streams. The simple form works -well when the requested variable has a unique canonical publisher: - -```julia -req_plant_daily = OutputRequest(:Plant, :plant_assim_d; - name=:plant_assim_daily, - clock=ClockSpec(24.0, 0.0), -) -``` - -More complex mappings often need more explicit requests. In particular, add -`process=` when several models can publish the same variable, and add `policy=` -when you need a specific export-time resampling behavior: - -```julia -req_daily_energy = OutputRequest(:Leaf, :leaf_assim_h; - name=:leaf_energy_daily, - process=:tutorialleafhourly, - policy=Integrate(), - clock=ClockSpec(24.0, 0.0), -) - -req_hourly_hold = OutputRequest(:Plant, :plant_assim_d; - name=:plant_assim_hold_hourly, - process=:tutorialplantdaily, - policy=HoldLast(), - clock=ClockSpec(1.0, 0.0), -) -``` - -So `OutputRequest(...)` is not just a way to rename a column. It is also a -declaration of which stream you want, at which cadence, and with which -resampling policy. - -## 6. Inspect resolved configuration - -When a mapping mixes inferred bindings, explicit bindings, custom meteo -aggregation, scopes, and export requests, it becomes difficult to reason about -the final resolved configuration by inspection alone. - -That is where `explain_model_specs(...)` and `resolved_model_specs(...)` become -useful: - -```julia -explain_model_specs(mapping) - -resolved = resolved_model_specs(mapping) -resolved[:Plant] -``` - -These helpers let you confirm: - -- the effective timestep of each model; -- the resolved input bindings; -- the resolved meteo bindings; -- the active meteo window. - -In practice, this is often the fastest way to debug a multi-rate mapping before -running a larger simulation. - -## 7. How to choose between the three pages - -Use the pages in this order: - -1. start with [Introduction to multi-rate execution](introduction.md) if you - want to understand the scheduling rules; -2. continue with [Step-by-step multi-rate tutorial](multirate_tutorial.md) for - a complete but compact MTG example; -3. come back to this page when you need explicit bindings, explicit meteo - aggregation, custom export requests, scopes, or debugging helpers. diff --git a/docs/src/multirate/introduction.md b/docs/src/multirate/introduction.md deleted file mode 100644 index 55d5fec71..000000000 --- a/docs/src/multirate/introduction.md +++ /dev/null @@ -1,206 +0,0 @@ -# Introduction to multi-rate execution - -!!! warning "Legacy mapping configuration" - The scheduling concepts remain valid, but this page uses qualified - compatibility transforms. New scene/object simulations configure rates - with `TimeStep(...)`, value transfer with `Inputs(...)`, and meteorology - with `Environment(...)`. - -This page introduces the basic ideas behind multi-rate execution in -PlantSimEngine. - -The goal here is not to build a realistic plant model. Instead, the objective is -to make the mechanics of multi-rate execution easy to see: - -- how PlantSimEngine decides when a model runs; -- how values are transferred from a faster model to a slower one; -- how meteorological inputs are reduced over a coarse time window. - -Once those ideas are clear, the -[step-by-step multi-rate tutorial](multirate_tutorial.md) shows how to assemble -a more complete hourly/daily/weekly MTG simulation. - -## Decision flow quick examples - -Before building a larger example, it helps to establish two important rules: - -1. if a model does not declare an explicit timestep, it follows the meteo cadence; -2. if a model is forced to run more coarsely than its inputs, then explicit input - and meteo binding policies determine how information is aggregated. - -### Simple example with implicit meteo cadence - -Model may define a trait calles `timestep_hint` that describes the acceptable and preferred cadences for that model. However, that trait is purely descriptive: it does not force the model to run at any particular rate. If you want to force a model to run at a specific cadence, you must declare an explicit `PlantSimEngine.TimeStepModel(...)` in the mapping. Otherwise, the model will simply run whenever the meteo cadence allows it to, and the `timestep_hint` can be used for validation or explanation but does not silently reschedule the model. - -Let's define a tiny model that simply counts how many times it ran, then feed it -three 30-minute weather rows: - -```@example multirate_timestep_flow -using PlantSimEngine -using PlantMeteo -using MultiScaleTreeGraph -using DataFrames -using Dates - -mtg = Node(NodeMTG("/", :Scene, 1, 0)) -plant = Node(mtg, NodeMTG("+", :Plant, 1, 1)) -internode = Node(plant, NodeMTG("/", :Internode, 1, 2)) -Node(internode, NodeMTG("+", :Leaf, 1, 2)) - -PlantSimEngine.@process "tutorialmeteodriven" verbose=false -struct TutorialMeteoDrivenModel <: AbstractTutorialmeteodrivenModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::TutorialMeteoDrivenModel) = NamedTuple() -PlantSimEngine.outputs_(::TutorialMeteoDrivenModel) = (count=-Inf,) -function PlantSimEngine.run!(m::TutorialMeteoDrivenModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.count = float(m.n[]) -end -PlantSimEngine.timestep_hint(::Type{<:TutorialMeteoDrivenModel}) = (; required=(Minute(30), Hour(2)), preferred=Hour(1)) -``` - -This model is designed to run between every 30 minutes and every 2 hours, with a preferred cadence of 1 hour. Let's make a mapping with the model but without an explicit `PlantSimEngine.TimeStepModel(...)`: - -```@example multirate_timestep_flow -mapping = PlantSimEngine.ModelMapping(:Leaf => (TutorialMeteoDrivenModel(Ref(0)),)) -``` - -Let's define a 30-minute weather table with three rows: - -```@example multirate_timestep_flow -meteo_30min = Weather([ - Atmosphere(date=DateTime(2025, 6, 12, 12, 0, 0), duration=Minute(30), T=20.0, Wind=1.0, Rh=0.6), - Atmosphere(date=DateTime(2025, 6, 12, 12, 30, 0), duration=Minute(30), T=21.0, Wind=1.0, Rh=0.6), - Atmosphere(date=DateTime(2025, 6, 12, 13, 0, 0), duration=Minute(30), T=22.0, Wind=1.0, Rh=0.6), -]) -``` - -Now we run the model and check how many times it ran over those three 30-minute rows: - -```@example multirate_timestep_flow -out_meteo_driven = run!( - mtg, - mapping, - meteo_30min; - executor=SequentialEx(), - tracked_outputs=Dict(:Leaf => (:count,)), -) -out_meteo_driven[:Leaf][end] -``` - -The last value for `:count` is `3.0`, showing the model ran on all three 30-minute meteo rows, -even though `preferred=Hour(1)`. - -That is the key point: without `TimeStepModel`, the model still follows the -incoming meteo table. The preferred timestep can be used for validation or for -explanation, but it does not silently reschedule the model. - -### Using `TimeStepModel` to manage multi-rate coupling - -The second example shows the complementary case. Here we explicitly ask one model -to run hourly, even though its source data arrives every 30 minutes. Once we do -that, PlantSimEngine needs instructions for two distinct questions: - -- how to combine the 30-minute source output into an hourly model input; -- how to combine 30-minute meteorological rows into the hourly meteo seen by the - coarse model. - -That is what `PlantSimEngine.InputBindings(...)` and `PlantSimEngine.MeteoBindings(...)` are for. -In this tiny example, we keep the mapping simple by declaring the default -reduction policy on the source model itself with `output_policy(...)`. Since `A` -has a unique producer on the same scale, PlantSimEngine can infer the source -automatically and reuse that policy. - -Let's define a simple 30-minute source model that produces a constant value `A=1.0` every time it runs, and declare that its output should be integrated when consumed by a slower model: - -```@example multirate_timestep_flow -PlantSimEngine.@process "tutorialhalfhoursource" verbose=false -struct TutorialHalfHourSourceModel <: AbstractTutorialhalfhoursourceModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::TutorialHalfHourSourceModel) = NamedTuple() -PlantSimEngine.outputs_(::TutorialHalfHourSourceModel) = (A=-Inf,) -function PlantSimEngine.run!(m::TutorialHalfHourSourceModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.A = 1.0 # umol m-2 s-1 -end -PlantSimEngine.output_policy(::Type{<:TutorialHalfHourSourceModel}) = (; A=Integrate(DurationSumReducer())) -``` - -Note that `output_policy(...)` says that when a slower model consumes `A`, the default is to integrate it over the coarser time window, using the duration of each source row as weights. - -Now we define a simple hourly model that consumes `A` and also reads hourly mean temperature from the meteo: - -```@example multirate_timestep_flow -PlantSimEngine.@process "tutorialhourlyintegrator" verbose=false -struct TutorialHourlyIntegratorModel <: AbstractTutorialhourlyintegratorModel end -PlantSimEngine.inputs_(::TutorialHourlyIntegratorModel) = (A=-Inf,) -PlantSimEngine.outputs_(::TutorialHourlyIntegratorModel) = (A_hourly=-Inf, T_hourly=-Inf,) -function PlantSimEngine.run!(::TutorialHourlyIntegratorModel, models, status, meteo, constants=nothing, extra=nothing) - status.A_hourly = status.A - status.T_hourly = meteo.T -end -``` - -!!! note - We make two deliberate simplifications here to keep the example compact: - 1. The hourly model simply copies the integrated `A` value into a new variable called `A_hourly`. This is bad design in a real model because it creates unnecessary variables and makes the data flow less transparent. In a real model, you would typically consume `A` directly and let the integrated value be called `A` as well. However, here we create a separate variable to make it obvious that the hourly model is receiving an aggregated version of the original `A`. - 2. We don't define an `output_policy(...)` for the hourly model, because it is not consumed by any slower model. Usually, developers are encouraged to define `output_policy(...)` for all models, but here we omit it for the hourly model to keep the example compact. - -Now we can declare a mapping that says the hourly model runs every hour, even though its source data arrives every 30 minutes. We also declare how to reduce the meteorological inputs to match the hourly cadence: - -```@example multirate_timestep_flow -mapping_coarse = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(TutorialHalfHourSourceModel(Ref(0))), - ModelSpec(TutorialHourlyIntegratorModel()) |> - PlantSimEngine.TimeStepModel(Hour(1)) |> - PlantSimEngine.MeteoBindings(; T=MeanWeighted()), - ), -) -``` - -Setting the `PlantSimEngine.TimeStepModel(Hour(1))` forces the second model to run hourly. Since it consumes `A` from the first model, PlantSimEngine looks at the source model's `output_policy(...)` and sees that it should integrate `A` over the hour using the duration of each 30-minute row as weights. - -!!! note - If we had omitted `PlantSimEngine.TimeStepModel(Hour(1))`, the hourly model would have simply run on each 30-minute row, and the `output_policy(...)` on the source model would not have been triggered. The hourly model would have received the original 30-minute `A` values instead of an hourly aggregate. This illustrates the key point: `PlantSimEngine.TimeStepModel(...)` is what triggers the multi-rate coupling and the use of reduction policies. - -In our example, the hourly model does not declare a `timestep_hint`, so it can run at any cadence. By declaring `PlantSimEngine.TimeStepModel(Hour(1))`, we explicitly force it to run hourly, which means it will receive aggregated inputs and meteo. - -!!! note - Because our hourly model does not declare a `timestep_hint`, it is flexible and can run at any cadence. However, if we had declared a `timestep_hint` that did not include hourly as an acceptable cadence, then PlantSimEngine would have raised an error when we tried to force it to run hourly. Consequently, it is usually a good practice to declare a `timestep_hint` when writing a model, because it helps to ensure that the model is used in a way that is consistent with its design and intended use. - -Let's now run the simulation: - -```@example multirate_timestep_flow -meteo_30min_4 = Weather([ - Atmosphere(date=DateTime(2025, 6, 12, 12, 0, 0), duration=Minute(30), T=20.0, Wind=1.0, Rh=0.6), - Atmosphere(date=DateTime(2025, 6, 12, 12, 30, 0), duration=Minute(30), T=22.0, Wind=1.0, Rh=0.6), - Atmosphere(date=DateTime(2025, 6, 12, 13, 0, 0), duration=Minute(30), T=24.0, Wind=1.0, Rh=0.6), - Atmosphere(date=DateTime(2025, 6, 12, 13, 30, 0), duration=Minute(30), T=26.0, Wind=1.0, Rh=0.6), -]) - -out_coarse = run!( - mtg, - mapping_coarse, - meteo_30min_4; - executor=SequentialEx(), - tracked_outputs=Dict(:Leaf => (:A_hourly, :T_hourly)), -) -out_coarse[:Leaf][end] -``` - -The final timestep outputs are `3600.0` for `A_hourly` and `23.0` for `T_hourly`: hourly integrated assimilation -(`sum(A .* duration_seconds)` over two 30-minute rows) and hourly mean temperature over the coarse window. - -So this example already captures the core multi-rate idea: the fast model still -runs at the fine cadence, while the coarse model sees explicitly reduced inputs -and meteorology at its own cadence. - -From here, there are two natural next steps: - -- [Step-by-step multi-rate tutorial](multirate_tutorial.md) for a more complete - MTG example; -- [Advanced multi-rate configuration](advanced_configuration.md) for explicit - bindings, meteo windows, export requests, scopes, and debugging helpers. diff --git a/docs/src/multirate/multirate_tutorial.md b/docs/src/multirate/multirate_tutorial.md deleted file mode 100644 index 7bc2c8fc7..000000000 --- a/docs/src/multirate/multirate_tutorial.md +++ /dev/null @@ -1,434 +0,0 @@ -# Step-by-step multi-rate tutorial (hourly + daily + weekly) - -!!! warning "Legacy mapping tutorial" - This tutorial is retained as compatibility coverage. New simulations should - express the same scheduling with `Scene`, `AppliesTo`, `Inputs`, - `TimeStep`, and `Environment`. - -This page builds a more complete MTG simulation that mixes three model rates: -- hourly at `Leaf`, -- daily at `Plant`, -- weekly at `Plant`. - -It runs for one week and exports clean series at each rate. - -If you want the conceptual overview first, start with -[Introduction to multi-rate execution](introduction.md). This page assumes you -already understand the two basic ideas introduced there: - -1. without `PlantSimEngine.TimeStepModel(...)`, a model follows the meteo cadence; -2. once a model is forced to run more coarsely than its inputs, PlantSimEngine - must reduce both model outputs and meteorological inputs to match that slower - cadence. - -The goal of this second page is to put those ideas into a more contextualized -MTG example, where we mix hourly, daily, and weekly models in the same -simulation and export clean time series at each rate. - -## 1. Setup and example data - -This tutorial is more contextualized than the previous one. To keep the mechanics -readable, we work with a minimal MTG containing only one plant and one leaf. That -way the exported tables stay small enough to inspect directly. - -We also reuse package example assets instead of inventing new input files. In particular, we use a weather file available from the package examples: - -- `examples/meteo_day.csv` for weather. - -We start by importing the packages we need and by creating a very small MTG with -only four nodes: a `Scene`, a `Plant`, one `Internode`, and one `Leaf`. - -```@example multirate_tutorial -using PlantSimEngine -using PlantMeteo -using MultiScaleTreeGraph -using DataFrames -using CSV -using Dates - -# Minimal plant: Scene -> Plant -> Internode -> Leaf -mtg = Node(NodeMTG("/", :Scene, 1, 0)) -plant = Node(mtg, NodeMTG("+", :Plant, 1, 1)) -internode = Node(plant, NodeMTG("/", :Internode, 1, 2)) -Node(internode, NodeMTG("+", :Leaf, 1, 2)) -``` - -Next, we point to the bundled weather file and confirm that it exists: - -```@example multirate_tutorial -meteo_path = joinpath(pkgdir(PlantSimEngine), "examples", "meteo_day.csv") -@assert isfile(meteo_path) -``` - -The weather file bundled with the package is daily. Since this tutorial is about -mixing several rates, we first convert one week of daily weather into an -hourly weather table. The values are simply repeated within each day, which is -perfectly fine here because the purpose is to illustrate scheduling and data flow -rather than to create a realistic forcing dataset. - -The first step is to read the file and keep only one week of rows: - -```@example multirate_tutorial -daily_df = CSV.read(meteo_path, DataFrame, header=18) -week_df = first(daily_df, 7) -``` - -We then expand each day into 24 hourly `Atmosphere` rows: - -```@example multirate_tutorial -hourly_rows = Atmosphere[] -for row in eachrow(week_df) - for h in 0:23 - push!(hourly_rows, - Atmosphere( - date=DateTime(row.date) + Hour(h), - duration=Hour(1), - T=row.T, - Wind=row.Wind, - P=row.P, - Rh=row.Rh, - Ri_PAR_f=row.Ri_PAR_f, - Ri_SW_f=row.Ri_SW_f, - ) - ) - end -end -``` - -Finally, we wrap those rows into a `Weather` object, which is what `run!` expects: - -```@example multirate_tutorial -meteo_hourly = Weather(hourly_rows) -meteo_hourly[1:3] # show the first 3 rows of the hourly weather table -``` - -## 2. Defining simple models - -Next we define three deliberately simple models: - -- an hourly `Leaf` model that turns incoming radiation into an hourly - assimilation value; -- a daily `Plant` model that sums hourly leaf assimilation over a day and also - consumes daily meteorological aggregates; -- a weekly `Plant` model that sums daily plant assimilation into one weekly - value. - -These models are intentionally minimal. Their role is to make the rate changes -and aggregation policies obvious. - -We begin with the hourly leaf model. It reads hourly meteorological radiation and -produces an hourly assimilation value: - -```@example multirate_tutorial -PlantSimEngine.@process "tutorialleafhourly" verbose=false -struct TutorialLeafHourlyModel <: AbstractTutorialleafhourlyModel end -PlantSimEngine.inputs_(::TutorialLeafHourlyModel) = NamedTuple() -PlantSimEngine.outputs_(::TutorialLeafHourlyModel) = (leaf_assim_h=0.0,) -function PlantSimEngine.run!(::TutorialLeafHourlyModel, models, status, meteo, constants=nothing, extra=nothing) - status.leaf_assim_h = 0.004 * meteo.Ri_PAR_f -end -PlantSimEngine.output_policy(::Type{<:TutorialLeafHourlyModel}) = (; leaf_assim_h=Integrate()) -``` - -The `output_policy(...)` declaration matters for multi-rate use: it says that -when a slower model consumes `leaf_assim_h`, the natural default is to integrate -it over the coarser time window. - -Now we define the daily plant model. It receives leaf assimilation values, -aggregates them over a day, and also reads daily reduced meteo variables: - -```@example multirate_tutorial -PlantSimEngine.@process "tutorialplantdaily" verbose=false -struct TutorialPlantDailyModel <: AbstractTutorialplantdailyModel end -PlantSimEngine.inputs_(::TutorialPlantDailyModel) = (leaf_assim_h=[0.0],) -PlantSimEngine.outputs_(::TutorialPlantDailyModel) = (plant_assim_d=0.0, rad_sw_day=0.0, T=0.0) -function PlantSimEngine.run!(::TutorialPlantDailyModel, models, status, meteo, constants=nothing, extra=nothing) - status.plant_assim_d = sum(status.leaf_assim_h) - status.rad_sw_day = meteo.Ri_SW_q - status.T = meteo.T -end -PlantSimEngine.output_policy(::Type{<:TutorialPlantDailyModel}) = (; plant_assim_d=Integrate()) -``` - -Again, `output_policy(...)` is used so that a coarser consumer can infer the -appropriate default behavior for `plant_assim_d`. - -Finally, we define the weekly plant model. It simply sums the daily plant -assimilation values over one week: - -```@example multirate_tutorial -PlantSimEngine.@process "tutorialplantweekly" verbose=false -struct TutorialPlantWeeklyModel <: AbstractTutorialplantweeklyModel end -PlantSimEngine.inputs_(::TutorialPlantWeeklyModel) = (plant_assim_d=[0.0],) -PlantSimEngine.outputs_(::TutorialPlantWeeklyModel) = (plant_assim_w=0.0,) -function PlantSimEngine.run!(::TutorialPlantWeeklyModel, models, status, meteo, constants=nothing, extra=nothing) - status.plant_assim_w = sum(status.plant_assim_d) -end -``` - -At this point nothing is multi-rate yet. We have simply defined three processes -whose intended cadences are hourly, daily, and weekly. The multi-rate behavior is -declared in the mapping. - -## 3. Configure multi-rate mapping - -This is the heart of the tutorial. The mapping below does three things at once: - -1. it assigns each model to a scale; -2. it declares the timestep at which each model should run; -3. it defines how values move between rates and between scales. - -Two pieces are especially important here: - -- `PlantSimEngine.TimeStepModel(...)` states the model cadence; -- PlantMeteo reduces meteorological inputs automatically when a model runs more - coarsely than the weather data. - -For model-to-model bindings, this tutorial relies on automatic source inference -plus `output_policy(...)` on the source models. That keeps the main example -compact while still exercising multi-rate input aggregation. - -We start by defining the three clocks used in the simulation. These are the -cadences that will later be assigned to the three models: - -```@example multirate_tutorial -hourly = 1.0 -daily = ClockSpec(24.0, 0.0) -weekly = ClockSpec(168.0, 0.0) -``` - -The leaf model is straightforward: it runs hourly and is scoped to the current -plant. There is no multiscale mapping or meteo reduction to declare here, because -the leaf model is the fastest model in this example and directly consumes the -hourly weather rows: - -```@example multirate_tutorial -leaf_spec = TutorialLeafHourlyModel() |> ModelSpec |> PlantSimEngine.TimeStepModel(hourly) -``` - -So at this point we have simply said: "run the leaf model every hour" - -The daily plant model is where multi-rate coupling becomes visible. It: - -- receives `leaf_assim_h` from the `:Leaf` scale through `PlantSimEngine.MultiScaleModel(...)`; -- runs daily; -- receives daily meteorological aggregates from the hourly weather automatically. - -The important idea is that this model does not read the raw hourly values -directly. Instead, it sees a daily view of those data: - -- `leaf_assim_h` is integrated over the daily window because of the source - model's `output_policy(...)`; -- `T` is turned into a daily mean by the default PlantMeteo sampling rules; -- `Ri_SW_q` is computed by integrating `Ri_SW_f` over the day. - -```@example multirate_tutorial -plant_daily_spec = - TutorialPlantDailyModel() |> - ModelSpec |> - PlantSimEngine.MultiScaleModel([:leaf_assim_h => :Leaf]) |> - PlantSimEngine.TimeStepModel(daily) -``` - -This block is the first place where the "multi-rate" behavior is really visible: -one model consumes fine-grained biological outputs and fine-grained meteorology, -but only after both have been reduced to the model's own daily cadence. - -The weekly plant model is simpler again: it only needs to run weekly and receive -the daily plant output automatically. Since `plant_assim_d` has a unique producer -and already declares its own `output_policy(...)`, we do not need to add any -explicit binding here: - -```@example multirate_tutorial -plant_weekly_spec = - TutorialPlantWeeklyModel() |> - ModelSpec |> - PlantSimEngine.TimeStepModel(weekly) -``` - -So this weekly model effectively says: "take the daily plant assimilation stream, -reduce it again to my weekly cadence, and run once per week." - -We can now assemble the full mapping: - -```@example multirate_tutorial -mapping = PlantSimEngine.ModelMapping( - :Leaf => (leaf_spec,), - :Plant => (plant_daily_spec, plant_weekly_spec), -) -``` - -Reading this mapping from top to bottom: - -- the `Leaf` model runs hourly and produces `leaf_assim_h`; -- the daily `Plant` model receives leaf values from the `Leaf` scale through - `PlantSimEngine.MultiScaleModel([:leaf_assim_h => :Leaf])`, then integrates them over a day; -- that same daily model also receives daily meteorological summaries through the - default PlantMeteo sampling rules; -- the weekly `Plant` model integrates the daily plant output into one weekly - value. - -!!! note - In this tutorial, explicit `PlantSimEngine.InputBindings(...)` are omitted because each - input has a unique, inferable producer and the default reduction policy is - declared on the source model with `output_policy(...)`. - - In more complex mappings, you should use explicit `PlantSimEngine.InputBindings(process=..., scale=..., var=..., policy=...)` when: - - several models can produce the same input variable; - - the same process exists at several reachable scales; - - the source variable has a different name than the consumer input; - - you want to override the producer's default policy for a specific mapping. - -!!! note - `PlantSimEngine.MeteoBindings(...)` is also omitted on purpose in the main example. - PlantSimEngine delegates weather sampling to PlantMeteo, which already - defines default transformations for common `Atmosphere` variables such as - `T`, `Rh`, and radiation aliases like `Ri_SW_q`. - - Add explicit `PlantSimEngine.MeteoBindings(...)` when: - - you want a non-default reducer; - - the model expects a target variable with a different source name; - - the variable is not covered by PlantMeteo default transforms; - - you want the mapping to state the weather aggregation rule explicitly. - - ```@example multirate_tutorial - # The same daily model, with weather aggregation rules written explicitly. - plant_daily_spec_explicit_meteo = ModelSpec(TutorialPlantDailyModel()) |> - PlantSimEngine.MultiScaleModel([:leaf_assim_h => :Leaf]) |> - PlantSimEngine.TimeStepModel(daily) |> - PlantSimEngine.MeteoBindings( - ; - T=MeanWeighted(), - Ri_SW_q=(source=:Ri_SW_f, reducer=RadiationEnergy()), - ) - ``` - -## 4. Run and export hourly/daily/weekly series - -Now we run the simulation and request three exported series. This is a good place -to distinguish two related outputs returned by `run!`: - -- the regular simulation outputs (`out_status` below), which still contain the - model outputs tracked during the run; -- the explicitly requested exported series (`exported` below), which are the - clean hourly/daily/weekly tables we asked PlantSimEngine to materialize. - -We use `OutputRequest(...)` to say which variable we want and on which clock. -Here again we keep the example minimal: `process=` is omitted because each -requested output has a unique canonical publisher. - -We first declare the export requests. One request keeps the hourly leaf series, -another exports the daily plant series, and the last one exports the weekly plant -series. - -The point of these requests is to obtain three clean tables that each live at a -single rate, instead of having to reconstruct those time series manually from -the full simulation outputs: - -```@example multirate_tutorial -req_leaf_hourly = OutputRequest(:Leaf, :leaf_assim_h; - name=:leaf_assim_hourly, -) - -req_plant_daily = OutputRequest(:Plant, :plant_assim_d; - name=:plant_assim_daily, - clock=daily, -) - -req_plant_weekly = OutputRequest(:Plant, :plant_assim_w; - name=:plant_assim_weekly, - clock=weekly, -) -``` - -Then we run the simulation and ask PlantSimEngine to return both the regular -simulation outputs and the explicitly requested exported series: - -- `out_status` contains the regular tracked outputs of the simulation; -- `exported` contains the resampled, per-request tables defined above. - -```@example multirate_tutorial -out_status, exported = run!( - mtg, - mapping, - meteo_hourly; - executor=SequentialEx(), - tracked_outputs=[req_leaf_hourly, req_plant_daily, req_plant_weekly], - return_requested_outputs=true, -) -``` - -Finally, we extract the exported tables we want to inspect. At this point we are -no longer dealing with abstract stream definitions: we now have actual `DataFrame` -objects containing hourly, daily, and weekly series. - -```@example multirate_tutorial -leaf_hourly_df = exported[:leaf_assim_hourly] -plant_daily_df = exported[:plant_assim_daily] -plant_weekly_df = exported[:plant_assim_weekly] -``` - -The exported tables already have the cadence we asked for, so they are much -easier to inspect than a single mixed output table. - -We can start with a few basic checks on the number of rows. These checks are a -simple way to confirm that the export clocks did what we expected: - -```@example multirate_tutorial -@show nrow(leaf_hourly_df) # 168 (1 leaf x 168 hours) -@show nrow(plant_daily_df) # 7 (1 plant x 7 days) -@show nrow(plant_weekly_df) # 1 (1 plant x 1 week) -``` - -The hourly table has one row per hour, the daily table one row per day, and the -weekly table one row for the whole run. - -To compare the hourly and daily outputs directly, we group the hourly series by -day and sum it manually. This lets us check that the daily plant model really did -receive the integrated hourly leaf assimilation: - -```@example multirate_tutorial -leaf_hourly_df.day = repeat(1:7, inner=24) -leaf_hourly_sum = combine(groupby(leaf_hourly_df, :day), :value => sum => :leaf_assim_h_sum) -``` - -Those row counts match the intended design of the example: one hourly series for -seven days, one daily series for seven days, and one weekly aggregate for the -whole run. - -We can also manually recompute the daily sums from the hourly exported series and -compare them with the daily model output: - -```@example multirate_tutorial -plant_daily_df -``` - -This confirms that the daily assimilation values correspond to the sum of the -hourly leaf assimilation collected over each day. - -The regular outputs returned by `run!` are still available as well, and can be -converted to `DataFrame`s in the usual way. This is useful when you want both: - -- clean resampled exports for analysis; -- the usual simulation outputs for debugging or broader inspection. - -```@example multirate_tutorial -outs = convert_outputs(out_status, DataFrame) -outs[:Plant][1:3,:] -``` - -## 5. Where to go next - -This page keeps the main walkthrough focused on a complete but still compact -example. Once that example is clear, the next step is usually to learn the -explicit configuration tools that become useful in larger mappings: - -- `PlantSimEngine.InputBindings(...)` when inference is ambiguous or too implicit; -- `PlantSimEngine.MeteoBindings(...)` when PlantMeteo defaults are not enough; -- `PlantSimEngine.MeteoWindow(...)` for calendar-aligned aggregation; -- `OutputRequest(...)` when you want explicit export-time clocks and policies; -- `PlantSimEngine.ScopeModel(...)`, `explain_model_specs(...)`, and `resolved_model_specs(...)` - for larger and harder-to-debug MTGs. - -Those topics are grouped in -[Advanced multi-rate configuration](advanced_configuration.md). diff --git a/docs/src/multiscale/multiscale.md b/docs/src/multiscale/multiscale.md deleted file mode 100644 index 4ee1ab9dc..000000000 --- a/docs/src/multiscale/multiscale.md +++ /dev/null @@ -1,260 +0,0 @@ -# Multi-scale variable mapping - -!!! warning "Legacy MTG mapping configuration" - This page is retained for compatibility and migration reference. Use - `Scene`, `ObjectTemplate`, `ObjectInstance`, `AppliesTo`, and `Inputs` for - new multiscale simulations. - -The previous page showed how to convert a single-scale simulation to multi-scale. - -This page provides another example showcasing the nuances in variable mapping, with a more complex fully multiscale version of a prior simulation. The models will all be taken form the [examples folder](https://github.com/VirtualPlantLab/PlantSimEngine.jl/tree/main/examples). - -```@contents -Pages = ["multiscale.md"] -Depth = 3 -``` - -## Starting with a single-model mapping - -Let's import the `PlantSimEngine` package and all the example models we will use in this tutorial: - -```@example usepkg -using PlantSimEngine -using PlantSimEngine.Examples # Import some example models -``` - -Let's create a simple mapping with only one initial model, the carbon assimilation process ToyAssimModel, which will operate on leaves. -It resembles the ToyAssimGrowth model used in the single-scale simulation [Model switching](@ref) subsection. - -Our mapping between scale and model is therefore: - -```@example usepkg -mapping = PlantSimEngine.ModelMapping(:Leaf => ToyAssimModel()) -``` - -Just like in single-scale simulations, we can call `to_initialize` to check whether variables need to be initialised. It will this time index by scale: - -```@example usepkg -to_initialize(mapping) -``` - -In this example, the ToyAssimModel needs `:aPPFD` and `:soil_water_content` as inputs, which aren't initialised in our mapping. - -The initialization values for the variables can be passed along via a [`Status`](@ref) object: - -```@example usepkg -mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - ToyAssimModel(), - Status(aPPFD=1300.0, soil_water_content=0.5), - ), -) -``` - -If we call [`to_initialize`](@ref) on this new mapping, it returns an empty dictionary, meaning the mapping is valid, and we can start the simulation: - -```@example usepkg -to_initialize(mapping) -``` - -## Multiscale mapping between models and scales - -The `soil_water_content` variable was provided via the mapping. No model affects it, so it is constant in the above example. We could instead provide a model that computes it based on weather data, and/or a more realistic physical process. - -It also makes sense to have that model operate at a different scale than the :Leaf scale. There is a dummy soil model called `ToySoilModel` in the examples folder. Let's put it at a new :Soil scale level. - -ToyAssimModel is now makes use of the `soil_water_content` variable from the `:Soil` scale, instead of at its own scale via the `Status` initialization. We therefore need to map `soil_water_content` from the :Soil to the :Leaf scale by wrapping `ToyAssimModel` in a `MultiScaleModel`: - -```@example usepkg -mapping = PlantSimEngine.ModelMapping( - :Soil => ToySoilWaterModel(), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => :Soil => :soil_water_content,], - ), - Status(aPPFD=1300.0), - ), -); -nothing # hide -``` - -In this example, we map the `soil_water_content` variable at scale :Leaf to the `soil_water_content` variable at the `:Soil` scale. If the name of the variable is the same between both scales, we can omit the variable name at the origin scale, *e.g.* `[:soil_water_content => :Soil]`. - -The variable `aPPFD` is still provided in the `Status` type as a constant value. - -We can check again if the mapping is valid by calling [`to_initialize`](@ref): - -```@example usepkg -to_initialize(mapping) -``` - -Once again, `to_initialize` returns an empty dictionary, meaning the mapping is valid. - -## A more elaborate multiscale model mapping - -Let's now expand this mapping, to showcase other ways in which variables can be mapped from one scale to another. We'll keep the first two models, and add several more to simulate a couple of other processes within our plant. - -```@example usepkg -mapping = PlantSimEngine.ModelMapping( - :Scene => ToyDegreeDaysCumulModel(), - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[ - :TT_cu => :Scene, - ], - ), - Beer(0.6), - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - :carbon_allocation => [:Leaf, :Internode] - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - ), - :Internode => ( - PlantSimEngine.MultiScaleModel( - model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - mapped_variables=[:TT => :Scene,], - ), - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - Status(carbon_biomass=1.0), - ), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => :Soil, :aPPFD => :Plant], - ), - PlantSimEngine.MultiScaleModel( - model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - mapped_variables=[:TT => :Scene,], - ), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - Status(carbon_biomass=0.5), - ), - :Soil => ( - ToySoilWaterModel(), - ), -); -nothing # hide -``` - -This mapping might seem a little more daunting than previous examples, but several models should be recognizable in passing. In fact, you can consider this mapping to be an enhanced and more complex multi-scale version of a previous single-scale example, the coupling between photosynthesis model, a LAI model and a carbon biomass increment model, used in the [Model switching](@ref) subsection. - -```julia -models2 = PlantSimEngine.ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyAssimGrowthModel(); - status=(TT_cu=cumsum(meteo_day.TT),), -) -``` - -The multi-scale models simulate carbon capture via photosynthesis and carbon allocation for the plant organs' maintenance respiration and development. - -The LAI and photosynthesis models are the same as in the single-scale mapping example. The [`ToyDegreeDaysCumulModel`](@ref) provides the Cumulative Thermal Time to the plant. - -The newly introduced models have the following dynamic : - -Carbon allocation is determined (ToyCAllocationModel) for the different organs of the plant (`:Leaf` and `:Internode`) from the assimilation at the `:Leaf` scale (*i.e.* the offer) and their carbon demand (ToyCDemandModel). The `:Soil` scale is used to compute the soil water content (`ToySoilWaterModel`](@ref)), which is needed to calculate the assimilation at the `:Leaf` scale (ToyAssimModel). Also note that maintenance respiration at computed at the `:Leaf` and `:Internode` scales (ToyMaintenanceRespirationModel), and aggregated to compute the total maintenance respiration at the `:Plant` scale (ToyPlantRmModel). - -## Different possible variable mappings - -The above mapping showcases the different ways to define how the variables are mapped in a `MultiScaleModel` : - -```julia - mapped_variables=[:TT_cu => :Scene,], -``` - -- At the :Plant scale, the TT_cu variable is mapped as a scalar from the :Scene scale. There is only a single :Scene node in the MTG, and only a single "TT_cu" value per timestep for the simulation. - -```julia -:carbon_allocation => [:Leaf] -``` - -- On the other hand, we have `:carbon_allocation => [:Leaf]` at the plant scale for `ToyCAllocationModel`. The `carbon_assimilation` variable is mapped as a vector: there are multiple :Leaf nodes, but only one :Plant node, which aggregrates the value over every single leaf. This gives us a 'many-to-one' vector mapping, and in the [`run!`](@ref) functions for models at that scale `carbon_allocation` will be available in the `status` as a vector. - -```julia -:carbon_allocation => [:Leaf, :Internode] -``` - -- A third type of the mapping would be `:carbon_allocation => [:Leaf, :Internode]`, which provides values for a variable from several other scales simultaneously. In this case, the values are also available as a vector in the `carbon_assimilation` variable of the [`status`](@ref) inside the model, sorted in the same order as nodes are traversed in the graph. - -```julia -:Rm_organs => [:Leaf => :Rm, :Internode => :Rm] -``` - -- Finally, to map to a specific variable name at the target scale, *e.g.* `:Rm_organs => [:Leaf => :Rm, :Internode => :Rm]`. This syntax is useful when the variable name is different between scales, and we want to map to a specific variable name at the target scale. In this example, the variable `Rm_organs` at plant scale takes its values (is mapped) from the variable `Rm` at the `:Leaf` and `:Internode` scales. - -## Running a simulation - -Now that we have a valid mapping, we can run a simulation. Running a multiscale simulation requires a plant graph and the definition of the output variables we want dynamically for each scale. - -### Plant graph - -We can import an example multi-scale tree graph like so: - -```@example usepkg -mtg = import_mtg_example() -``` - -!!! note - You can use `import_mtg_example` only if you previously imported the `Examples` sub-module of PlantSimEngine, *i.e.* `using PlantSimEngine.Examples`. - -This graph has a root node that defines a scene, then a soil, and a plant with two internodes and two leaves. - -### Output variables - -For long simulations on plants with many organs, the output data can be very significant. It's possible to restrict the output variables that are tracked for the whole simulation to a subset of all the variables: - -```@example usepkg -outs = Dict( - :Scene => (:TT, :TT_cu,), - :Plant => (:aPPFD, :LAI), - :Leaf => (:carbon_assimilation, :carbon_demand, :carbon_allocation, :TT), - :Internode => (:carbon_allocation,), - :Soil => (:soil_water_content,), -) -``` - -This dictionary can be passed to the simulation via the optional `tracked_outputs` keyword argument to the [`run!`](@ref) function (see the next part). If no dictionary is provided, every variable will be tracked. - -These variables will be available in the output returned by [`run!`](@ref), with a value for each time step. The corresponding timestep and node in the MTG are also returned. - -### Meteorological data - -As for mono-scale models, we need to provide meteorological data to run a simulation. We can use the `PlantMeteo` package to generate some dummy data for two time steps: - -```@example usepkg -meteo = Weather( - [ - Atmosphere(T=20.0, Wind=1.0, Rh=0.65, Ri_PAR_f = 200.0), - Atmosphere(T=25.0, Wind=0.5, Rh=0.8, Ri_PAR_f = 180.0) -] -) -``` - -### Simulation - -Let's make a simulation using the graph and outputs we just defined: - -```@example usepkg -outputs_sim = run!(mtg, mapping, meteo, tracked_outputs = outs); -nothing # hide -``` - -And that's it! We can now access the outputs for each scale as a dictionary of vectors of NamedTuple objects. - -Or as a `DataFrame` dictionary using the [`DataFrames`](https://dataframes.juliadata.org) package: - -```@example usepkg -using DataFrames -df_dict = convert_outputs(outputs_sim, DataFrame) -``` diff --git a/docs/src/multiscale/multiscale_considerations.md b/docs/src/multiscale/multiscale_considerations.md deleted file mode 100644 index 549aceda4..000000000 --- a/docs/src/multiscale/multiscale_considerations.md +++ /dev/null @@ -1,171 +0,0 @@ -# Multi-scale considerations - -```@contents -Pages = ["multiscale_considerations.md"] -Depth = 3 -``` - -This page briefly details the subtle ways in which multi-scale simulations differ from prior single-scale simulations. The next few pages will showcase some of these subtleties with examples. - -Declaring and running a multi-scale simulation follows the same general workflow as the single-scale version, but multi-scale simulations do have some differences : - -- a simulation requires a Multi-scale Tree Graph (MTG) to run and operates on that graph -- when running, models are tied to a scale and only access local information -- models can run multiple times per timestep - -The simulation dependency graph will still be computed automatically and handle most couplings, meaning users don't need to specify the order of model execution once the extra code to declare the models is written. You will still need to declare hard dependencies, with extra considerations for multi-scale hard dependencies. - -Multi-scale simulations also tend to require more extra ad hoc models to prepare some variables for some models. - -## Related pages - -Other pages in the multiscale section describe : - -- How convert a single-scale ModelMapping to a multi-scale one: [Converting a single-scale simulation to multi-scale](@ref), -- A more complex multi-scale version of the single-scale simulation showcasing different variable mappings between scales: [Multi-scale variable mapping](@ref), -- A three-part tutorial describing how to build up a combination of models to simulate a growing toy plant: [Writing a multiscale simulation](@ref), -- Ways to handle situations where a variable ends up causing a cyclic dependency: [Avoiding cyclic dependencies](@ref), -- Multi-scale specific coupling considerations and subtleties:[Handling dependencies in a multiscale context](@ref) - -## Multi-scale tree graphs - -Functional-Structural Plant Models are often about simulating plant growth. A multi-scale simulation is implicitely expected to operate on a plant-like object, represented by a multi-scale tree graph. - -A multi-scale tree graph (MTG) object (see the [Multi-scale Tree Graphs](@ref) subsection for a quick description) is therefore required to run a multi-scale simulations. It can be a dummy MTG if the simulation doesn't actually affect it, but is nevertheless a required argument to the multi-scale [`run!`](@ref) function. - -All the multi-scale examples make use of the companion package [MultiScaleTreeGraph.jl](https://github.com/VEZY/MultiScaleTreeGraph.jl), which we therefore recommend for running your own multi-scale simulations. Visualizing a Multi-scale Tree Graph can be done using [PlantGeom](https://github.com/VEZY/PlantGeom.jl). - -!!! note - Multi-scale Tree Graphs make use of conflicting terminology with PlantSimEngine's concepts, which is discussed in [Scale/symbol terminology ambiguity](@ref). If you are new to those concepts, make sure to read that section and keep note of it. - -## Models run once per organ instance, not once per organ level - -Some models, like the ones we've seen in single-scale simulations, work on a very simple model of a whole plant. - -More fine-grained models can be tied to a specific plant organ. - -For instance, a model computing a leaf's surface area depending on its age would operate at the `:Leaf` scale, and be called **for every leaf** at every timestep. On the other hand, a model computing the plant's total leaf area only needs to be run once per timestep, and can be run at the `:Plant` scale. - -This is a major difference between a single-scale simulation and a multi-scale one. By default, any model in a single-scale simulation will only run **once** per timestep. However, in multi-scale, if a plant has several instances of an organ type -say it has a hundred leaves- then any model operating at the :Leaf scale will by default run one hundred times per timestep, unless it is explicitely controlled by another model (which can happen in hard dependency configurations). - -## Mappings - -When users define which models they use, PlantSimEngine cannot determine in advance which scale level they operate at. This is partly because the plant organs in an MTG do not have standardized names, and partly because some plant organs might not be part of the initial MTG, so parsing it isn't enough to infer what scales are used. - -The user therefore needs to indicate for a simulation's which models are related to which scale. - -A legacy multi-scale mapping links models to the scale at which they operate, -and is implemented with `PlantSimEngine.ModelMapping(...)`, tying a scale, such -as `:Leaf`, to models operating at that scale, such as -`LeafSurfaceAreaModel`. New multiscale work should prefer `Scene`, `Object`, -`AppliesTo(...)`, and `Inputs(...)`. - -Multi-scale models can be similar models to the ones found in earlier sections, or, if they need to make use of variables at other scales, may need to be wrapped as part of a [`PlantSimEngine.MultiScaleModel`](@ref) object. Many models are not tied to a particular scale, which means those models can be reused at different scales or in single-scale simulations. - -## The simulation operates on an MTG - -Unlike in single-scale simulations, which make use of a [`Status`](@ref) object to store the current state of every variable in a simulation, multi-scale simulations operate on a per-organ basis. - -This means every organ instance has its own [`Status`](@ref), with scale-specific attributes. - -This has two **important** consequences in terms of running a simulation : - -- First, **any scale absent from the MTG will not be run**. If your MTG contains no leaves, then no model operating at the scale :Leaf will be able to run until a :Leaf organ is created and a node is added in the MTG. Otherwise, it has no MTG node to operate on. The only exceptions are hard dependency models which can be called from a different scale, since they can be called directly by a model on a node at a different existing scale, even if there is no node at their own scale. - -- Secondly, models only have access to **local** organ information. The [`status`](@ref) argument in the [`run!`](@ref) function only contains variables **at the model's scale**, unless variables from other scales are mapped via a [`PlantSimEngine.MultiScaleModel`](@ref) wrapping. - -## The run! function's signature - -The [`run!`](@ref) function differs slightly from its single-scale version. The current structure (excluding a couple of advanced/deprecated kwargs) is the following: - -```julia -run!(mtg, mapping::ModelMapping, meteo, constants, extra; nsteps, tracked_outputs) -``` - -Instead of just the compatibility mapping, it also takes an MTG as the first -argument. The optional `meteo` and `constants` arguments are identical to the -single-scale version. The `extra` argument is now reserved and should not be -used. A new `nsteps` keyword argument is available to restrict the simulation -to a specified number of steps. - -## Multi-scale output data structure - -The output structure, like the mapping, is a Julia `Dict` structure indexed by the scale name. Values are a per-scale `Vector{NamedTuple}` which lists the requested variables for every node at that scale, for every timestep in the simulation. Timestep and Multiscale Tree Graph nodes are also added to the output data, as a `:timestep`and a `:node` entry. - -This dictionary structure makes the outputs as-is a little more verbose to inspect than in single-scale, but the general usage is similar, and it is both compact, and fast to convert to a `Dict{String, DataFrame}` which can make queries easier. - -!!! note - Some of the mapped variables -those that map from scalar to vector- will not be added to the outputs to save some memory and space since they are redundant. - -To illustrate, here's an example output from part 3 of the Toy plant tutorial, zeroing in on a variable at the :Root scale: [Fixing bugs in the plant simulation](@ref): - -```julia -julia> outs - -Dict{String, Vector} with 5 entries: - :Internode => @NamedTuple{timestep::Int64, node::Node{NodeMTG, Dict{Symbol, Any}}, carbon_root_creation_consumed::Float64, TT_cu::Float64, carbon_… - :Root => @NamedTuple{timestep::Int64, node::Node{NodeMTG, Dict{Symbol, Any}}, carbon_root_creation_consumed::Float64, water_absorbed::Float64… - :Scene => @NamedTuple{timestep::Int64, node::Node{NodeMTG, Dict{Symbol, Any}}, TT_cu::Float64, TT::Float64}[(timestep = 1, node = / 1: Scene… - :Plant => @NamedTuple{timestep::Int64, node::Node{NodeMTG, Dict{Symbol, Any}}, carbon_root_creation_consumed::Float64, carbon_stock::Float64, … - :Leaf => @NamedTuple{timestep::Int64, node::Node{NodeMTG, Dict{Symbol, Any}}, carbon_captured::Float64}[(timestep = 1, node = + 4: Leaf… - -julia> outs[:Root] -3257-element Vector{@NamedTuple{timestep::Int64, node::Node{NodeMTG, Dict{Symbol, Any}}, carbon_root_creation_consumed::Float64, water_absorbed::Float64, root_water_assimilation::Float64}}: - (timestep = 1, node = + 9: Root -└─ < 10: Root - └─ < 11: Root - └─ < 12: Root - └─ < 13: Root - └─ < 14: Root - └─ < 15: Root - └─ < 16: Root - └─ < 17: Root -, carbon_root_creation_consumed = 50.0, water_absorbed = 0.5, root_water_assimilation = 1.0) - ⋮ -``` - -Values are more complex to query than in a single-scale simulation since the indexing isn't straightforward to map to a timestep: - -```julia -julia> [Pair(outs[:Root][i][:timestep], outs[:Root][i][:carbon_root_creation_consumed]) for i in 1:length(outs[:Root])] -3257-element Vector{Pair{Int64, Float64}}: - 1 => 50.0 - 1 => 50.0 - 2 => 50.0 - 2 => 50.0 - 2 => 50.0 - ⋮ - 365 => 50.0 - 365 => 50.0 - 365 => 50.0 - 365 => 50.0 - 365 => 50.0 - 365 => 50.0 - 365 => 50.0 - 365 => 50.0 - 365 => 50.0 -``` - -Converting to a dictionary of DataFrame objects can make such queries easier to write. - -!!! warning - Currently, the `:node` entry only shallow copies nodes. The `:node` values at each scale for every timestep actually reflect the final state of the node, meaning attribute values may not correspond to the value at that timestep. You may need to output these values via a dedicated model to keep track of them properly. - Nodes can be removed from an active graph simulation with [`remove_organ!`](@ref), but already-exported outputs are historical records. A pruned/dead/aborted organ may still appear in past output rows, while current statuses and future outputs no longer include it. - -Multi-scale simulations, especially for plants which have thousands of leaves, internodes, root branches, buds and fruits, may compute huge amounts of data. Just like in single-scale simulations, it is possible to keep only variables whose values you want to track for every timestep, and filter the rest out, using the `tracked_outputs` keyword argument for the [`run!`](@ref) function. - -Those tracked variables also need to be indexed by scale to avoid ambiguity: - -```julia -outs = PlantSimEngine.ModelMapping( - :Scene => (:TT, :TT_cu,), - :Plant => (:aPPFD, :LAI), - :Leaf => (:carbon_assimilation, :carbon_demand, :carbon_allocation, :TT), - :Internode => (:carbon_allocation,), - :Soil => (:soil_water_content,), -) -``` - -## Coupling and multi-scale hard dependencies - -Multi-scale brings new types of coupling: mappings are part of the approach used to handle variables used by models at different scales. A model can also have a hard dependency on another model that operates at another scale. This multi-scale-specific complexity is discussed in [Handling dependencies in a multiscale context](@ref) diff --git a/docs/src/multiscale/multiscale_coupling.md b/docs/src/multiscale/multiscale_coupling.md deleted file mode 100644 index 64e50715e..000000000 --- a/docs/src/multiscale/multiscale_coupling.md +++ /dev/null @@ -1,176 +0,0 @@ - -# Handling dependencies in a multiscale context - -!!! warning "Legacy MTG mapping configuration" - This page describes compatibility mapping internals. New value coupling - uses `Inputs(...)`; manually controlled hard coupling uses `Calls(...)`, - `call_target(s)`, and `run_call!`. - -```@contents -Pages = ["multiscale_coupling.md"] -Depth = 3 -``` - -## Scalar and vector variable mappings - -In the detailed example discussed previously [Multi-scale variable mapping](@ref), there were several instances of mapping a variable from one scale to another, which we'll briefly describe again to help transition to the next and more advanced subsection. Here's a relevant exerpt from the mapping : - -```julia -:Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[ - :TT_cu => :Scene, - ], - ), - ... - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - :carbon_allocation => [:Leaf, :Internode] - ], - ), - ... - ), -``` - -For flexibility reasons, instead of explicitely linking most models from different scales together, one only declares which variables are meant to be taken from another scale (or more accurately, a model at a different scale outputting those variables). This keeps the convenience of switching models while making few changes to the mapping. - -However, PlantSimEngine cannot infer which scales have multiple instances, and which are single-instance, as the scale names are user-defined. - -In the above example, there is only one scene at the :Scene, and one plant at the :Plant scale, meaning the `TT_cu` variable mapped between the two has a one-to-one scalar-to-scalar correspondance. - -On the other hand, the `carbon_assimilation` variable is computed for **every** leaf, of which there could be hundreds, or thousands, giving a scalar-to-vector correspondance. The carbon assimilation model runs many times every timestep, whereas the carbon allocation model only runs once per timestep. There may be initially be only a single leaf, though, meaning PlantSimEngine cannot currently guess from the initial configuration that there might be multiple leaves created during the simulation. - -Hence the difference in mapping declaration : `TT_cu`is declared as a scalar correspondence : -```julia -:TT_cu => :Scene, -``` -whereas `carbon_assimilation` (and other variables) will be declared as a vector correspondence : -```julia -:carbon_assimilation => [:Leaf], -``` - -Note that there may be instances where you might wish to write your own model to aggregate a variable from a multi-instance scale. - -## Hard dependencies between models at different scale levels - -If a model requires some input variable that is computed at another scale, then providing the appropriate mapping for that variable will resolve name conflicts and enable that model to run with no further steps for the user or the modeler when the coupling is a 'soft dependency'. - -In the case of a hard dependency that operates **at the same scale as its parent**, declaring the hard dependency is exactly the same as in single-scale simulations and there are also no new extra steps on the user-side: - -- The parent model directly handles the call to its hard dependency model(s), meaning they are not explicitely managed by the top-level dependency graph. -- This means only the owning model of that dependency is visible in the graph, and its hard dependency nodes are internal. -- When the caller (or any downstream model that requires some variables from the hard dependency model) operates at the same scale, variables are easily accessible, and no mapping is required. - -On the other hand, modelers do need to bear in mind a couple of subtleties when developing models that possess hard dependencies that operate **at a different organ level from their parent**: - -If an model needs to be directly called by a parent but operates at a different scale/organ level, a modeler must declare hard dependencies with their respective organ level, similarly to the way the user provides a mapping. - -Conceptually : - -```julia - PlantSimEngine.dep(m::ParentModel) = ( - name_provided_in_the_mapping=AbstractHardDependencyModel => [:Organ_Name_1], -) -``` - -### An example from the toy plant simulation tutorial - -You can find an example of a hard dependency discussed in the [A multi-scale hard dependency appears](@ref) subsection of the third part of toy plant tutorial. - -### An example from XPalm.jl - -Here's a concrete example in [XPalm](https://github.com/PalmStudio/XPalm.jl), an oil palm model developed on top of PlantSimEngine. - Organs are produced at the phytomer scale, but need to run an age model and a biomass model at the reproductive organs' scales. - -```julia - PlantSimEngine.dep(m::ReproductiveOrganEmission) = ( - initiation_age=AbstractInitiation_AgeModel => [m.male_symbol, m.female_symbol], - final_potential_biomass=AbstractFinal_Potential_BiomassModel => [m.male_symbol, m.female_symbol], -) -``` - -The user-mapping includes the required models at specific organ levels. Here's the relevant portion of the mapping for the male reproductive organ : - -```julia -mapping = PlantSimEngine.ModelMapping( - ... - :Male => - PlantSimEngine.MultiScaleModel( - model=XPalm.InitiationAgeFromPlantAge(), - mapped_variables=[:plant_age => :Plant,], - ), - ... - XPalm.MaleFinalPotentialBiomass( - p.parameters[:male][:male_max_biomass], - p.parameters[:male][:age_mature_male], - p.parameters[:male][:fraction_biomass_first_male], - ), - ... -) -``` - -The model's constructor provides convenient default names for the scale corresponding to the reproductive organs. A user may override that if their naming schemes or MTG attributes differ. - -```julia -function ReproductiveOrganEmission(mtg::MultiScaleTreeGraph.Node; phytomer_symbol=:Phytomer, male_symbol=:Male, female_symbol=:Female) - ... -end -``` - -## Implementation details: accessing a hard dependency's variables from a different scale - -But how does a model M calling a hard dependency H provide H's variables when calling H's [`run!`](@ref) function ? The [`status`](@ref) argument the user provides M operates at M's organ level, so if used to call H's run! function any required variable for H will be missing. - -PlantSimEngine provides what are called Status Templates in the simulation graph. Each organ level has its own Status template listing the available variables at that scale. -So when a model M calls a hard dependency H's [`run!`](@ref) function, any required variables can be accessed through the status template of H's organ level. - -### Back to the XPalm example - -Using the same example in XPalm, the oil palm FSPM: - -```julia -# Note that the function's 'status' parameter does NOT contain the variables required by the hard dependencies as the calling model's organ level is "Phytomer", not :Male or "Female" - -function PlantSimEngine.run!(m::ReproductiveOrganEmission, models, status, meteo, constants, sim_object) - ... - status.graph_node_count += 1 - - # Create the new organ as a child of the phytomer: - st_repro_organ = add_organ!( - status.node[1], # The phytomer's internode is its first child - sim_object, # The simulation object, so we can add the new status - "+", status.sex, 4; - index=status.phytomer_count, - id=status.graph_node_count, - attributes=Dict{Symbol,Any}() - ) - - # Compute the initiation age of the organ: - PlantSimEngine.run!(sim_object.models[status.sex].initiation_age, sim_object.models[status.sex], st_repro_organ, meteo, constants, sim_object) - PlantSimEngine.run!(sim_object.models[status.sex].final_potential_biomass, sim_object.models[status.sex], st_repro_organ, meteo, constants, sim_object) -end -``` - -In the above example the organ and its status template are created on the fly. -When that isn't the case, the status template can be accessed through the simulation graph : - -```julia -function PlantSimEngine.run!(m::ReproductiveOrganEmission, models, status, meteo, constants, sim_object) - - ... - - if status.sex == :Male - - status_male = sim_object.statuses[:Male][1] - run!(sim_object.models[:Male].initiation_age, models, status_male, meteo, constants, sim_object) - run!(sim_object.models[:Male].final_potential_biomass, models, status_male, meteo, constants, sim_object) - else - # Female - ... - end -end -``` diff --git a/docs/src/multiscale/multiscale_cyclic.md b/docs/src/multiscale/multiscale_cyclic.md deleted file mode 100644 index 976ede8ae..000000000 --- a/docs/src/multiscale/multiscale_cyclic.md +++ /dev/null @@ -1,119 +0,0 @@ -# Avoiding cyclic dependencies - -!!! warning "Legacy MTG mapping configuration" - This page uses the compatibility mapping runtime. The unified scene/object - graph supports `PreviousTimeStep(...)` directly inside `Inputs(...)`. - -When defining a mapping between models and scales, it is important to avoid cyclic dependencies. A cyclic dependency occurs when a model at a given scale depends on a model at another scale that depends on the first model. Cyclic dependencies are bad because they lead to an infinite loop in the simulation (the dependency graph keeps cycling indefinitely). - -PlantSimEngine will detect cyclic dependencies and raise an error if one is found. The error message indicates the models involved in the cycle, and the model that is causing the cycle will be highlighted in red. - -For example the following mapping will raise an error: - -!!! details - Example mapping - - ```julia - mapping_cyclic = PlantSimEngine.ModelMapping( - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - :carbon_demand => [:Leaf, :Internode], - :carbon_allocation => [:Leaf, :Internode] - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - Status(total_surface=0.001, aPPFD=1300.0, soil_water_content=0.6), - ), - :Internode => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - Status(TT=10.0, carbon_biomass=1.0), - ), - :Leaf => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - ToyCBiomassModel(1.2), - Status(TT=10.0), - ) - ) - ``` - -Let's see what happens when we try to build the dependency graph for this mapping: - -```julia -julia> dep(mapping_cyclic) -ERROR: Cyclic dependency detected in the graph. Cycle: - Plant: ToyPlantRmModel - └ Leaf: ToyMaintenanceRespirationModel - └ Leaf: ToyCBiomassModel - └ Plant: ToyCAllocationModel - └ Plant: ToyPlantRmModel - - You can break the cycle using the `PreviousTimeStep` variable in the mapping. -``` - -How can we interpret the message? We have a list of five models involved in the cycle. The first model is the one causing the cycle, and the others are the ones that depend on it. In this case, the `ToyPlantRmModel` is the one causing the cycle, and the others are inter-dependent. We can read this as follows: - -1. `ToyPlantRmModel` depends on `ToyMaintenanceRespirationModel`, the plant-scale respiration sums up all organs respiration; -2. `ToyMaintenanceRespirationModel` depends on `ToyCBiomassModel`, the organs respiration depends on the organs biomass; -3. `ToyCBiomassModel` depends on `ToyCAllocationModel`, the organs biomass depends on the organs carbon allocation; -4. And finally `ToyCAllocationModel` depends on `ToyPlantRmModel` again, hence the cycle because the carbon allocation depends on the plant scale respiration. - -The models can not be ordered in a way that satisfies all dependencies, so the cycle can not be broken. To solve this issue, we need to re-think how models are mapped together, and break the cycle. - -There are several ways to break a cyclic dependency: - -- **Merge models**: If two models depend on each other because they need *e.g.* recursive computations, they can be merged into a third model that handles the computation and takes the two models as hard dependencies. Hard dependencies are models that are explicitly called by another model and do not participate on the building of the dependency graph. -- **Change models**: Of course models can be interchanged to avoid cyclic dependencies, but this is not really a solution, it is more a workaround. -- **PreviousTimeStep**: We can break the dependency graph by defining some variables as taken from the previous time step. A very well known example is the computation of the light interception by a plant that depends on the leaf area, which is usually the result of a model that also depends on the light interception. The cyclic dependency is usually broken by using the leaf area from the previous time step in the interception model, which is a good approximation for most cases. - -We can fix our previous mapping by computing the organs respiration using the carbon biomass from the previous time step instead. Let's see how to fix the cyclic dependency in our mapping (look at the leaf and internode scales): - -!!! details - ```@julia - mapping_nocyclic = PlantSimEngine.ModelMapping( - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapping=[ - :carbon_demand => [:Leaf, :Internode], - :carbon_allocation => [:Leaf, :Internode] - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - Status(total_surface=0.001, aPPFD=1300.0, soil_water_content=0.6, carbon_assimilation=5.0), - ), - :Internode => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - PlantSimEngine.MultiScaleModel( - model=ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - mapped_variables=[PreviousTimeStep(:carbon_biomass),], #! this is where we break the cyclic dependency (first break) - ), - Status(TT=10.0, carbon_biomass=1.0), - ), - :Leaf => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - PlantSimEngine.MultiScaleModel( - model=ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - mapped_variables=[PreviousTimeStep(:carbon_biomass),], #! this is where we break the cyclic dependency (second break) - ), - ToyCBiomassModel(1.2), - Status(TT=10.0), - ) - ); - nothing # hide - ``` - -The `ToyMaintenanceRespirationModel` models are now defined as [`PlantSimEngine.MultiScaleModel`](@ref), and the `carbon_biomass` variable is wrapped in a `PreviousTimeStep` structure. This structure tells PlantSimEngine to take the value of the variable from the previous time step, breaking the cyclic dependency. - -!!! note - [`PreviousTimeStep`](@ref) tells PlantSimEngine to take the value of the previous time step for the variable it wraps, or the value at initialization for the first time step. The value at initialization is the one provided by default in the models inputs, but is usually provided in the [`Status`](@ref) structure to override this default. - A [`PreviousTimeStep`](@ref) is used to wrap the **input** variable of a model, with or without a mapping to another scale *e.g.* `PreviousTimeStep(:carbon_biomass) => :Leaf`. diff --git a/docs/src/multiscale/multiscale_example_1.md b/docs/src/multiscale/multiscale_example_1.md deleted file mode 100644 index 8433b02b5..000000000 --- a/docs/src/multiscale/multiscale_example_1.md +++ /dev/null @@ -1,289 +0,0 @@ -# Writing a multiscale simulation - -This three-part subsection walks you through building a multi-scale simulation from scratch. It is meant as an illustration of the iterative process you might go through when building and slowly tuning a Functional-Structural Plant Model, where previous multi-scale examples focused more on the API syntax. - -You can find the full script for the first part's toy simulation in the [ToyMultiScalePlantModel](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/examples/ToyMultiScalePlantModel/ToyPlantSimulation1.jl) subfolder of the examples folder. - -```@contents -Pages = ["multiscale_example_1.md"] -Depth = 3 -``` - -## Disclaimer - -The actual plant being created, as well as some of the custom models, have no real physical meaning and are very much ad hoc (which is why most of them aren't standalone in the examples folder). Similarly, some of the parameter values are pulled out of thin air, and have no ties to research papers or data. - -The main purpose here is to showcase PlantSimEngine's multi-scale features and how to structure your models, not accuracy, realism or performance. - -## Initial setup - -We'll need to make use of a few packages, as usual, after adding them to our Julia environment: - -```@example usepkg -using PlantSimEngine -using PlantSimEngine.Examples # to import the ToyDegreeDaysCumulModel model -using PlantMeteo, Dates -using MultiScaleTreeGraph # multi-scale -``` - -## A basic growing plant - -At minimum, to simulate some kind of fake growth, we need : - -- A Multi-scale Tree Graph representing the plant -- Some way of adding organs to the plant -- Some kind of temporality to spread this growth over multiple timesteps - -Let's have some concept of 'leaves' that capture the (carbon) resource necessary for organ growth, and let's have the organ emergence happen at the 'internode' level, to illustrate multiple organs with different behavior. - -We'll make the assumption that the internodes make use of carbon from a common pool. We'll also make use of thermal time as a growth delay factor. - -To sum up, we have: -- a MTG with growing internodes and leaves -- Individual leaves that capture carbon fed into a common pool -- Internodes which take from that pool to create new organs, with a thermal time constraint. - -One way of modeling this approach translates into several scales and models: - -- a Scene scale, for thermal time. The [`ToyDegreeDaysCumulModel`](@ref) from the [examples folder](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/examples/ToyDegreeDays.jl) provides thermal time from temperature data -- a Plant scale, where we'll define the carbon pool -- an Internode scale, which draws from the pool to create new organs -- a Leaf scale, which captures carbon - -Let's also add a very artificial limiting factor: if the total leaf surface area is above a threshold no new organs are created. - -We can expect the simulation mapping to look like a more complex version of the following: - -```julia -mapping = PlantSimEngine.ModelMapping( -:Scene => ToyDegreeDaysCumulModel(), -:Plant => ToyStockComputationModel(), -:Internode => ToyCustomInternodeEmergence(), -:Leaf => ToyLeafCarbonCaptureModel(), -) -``` - -Some of the models will need to gather variables from scales other than their own, meaning they will need to be converted into MultiScaleModels. - -## Implementation - -### Carbon Capture - -Let's start with the simplest model. Our fake leaves will continuously capture some constant amount of carbon every timestep. No inputs or parameters are required. - -```@example usepkg -PlantSimEngine.@process "leaf_carbon_capture" verbose = false - -struct ToyLeafCarbonCaptureModel<: AbstractLeaf_Carbon_CaptureModel end - -function PlantSimEngine.inputs_(::ToyLeafCarbonCaptureModel) - NamedTuple() # No inputs -end - -function PlantSimEngine.outputs_(::ToyLeafCarbonCaptureModel) - (carbon_captured=0.0,) -end - -function PlantSimEngine.run!(::ToyLeafCarbonCaptureModel, models, status, meteo, constants, extra) - status.carbon_captured = 40 -end -``` - -### Resource storage - -The model storing resources for the whole plant needs a couple of inputs: the amount of carbon captured by the leaves, as well as the amount consumed by the creation of new organs. It outputs the current stock. - -```@example usepkg -PlantSimEngine.@process "resource_stock_computation" verbose = false - -struct ToyStockComputationModel <: AbstractResource_Stock_ComputationModel -end - -PlantSimEngine.inputs_(::ToyStockComputationModel) = -(carbon_captured=0.0,carbon_organ_creation_consumed=0.0) - -PlantSimEngine.outputs_(::ToyStockComputationModel) = (carbon_stock=-Inf,) - -function PlantSimEngine.run!(m::ToyStockComputationModel, models, status, meteo, constants=nothing, extra=nothing) - status.carbon_stock += sum(status.carbon_captured) - sum(status.carbon_organ_creation_consumed) -end -``` - -### Organ creation - -This model is a modified version of the ToyInternodeEmergence model found [in the examples folder](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/examples/ToyInternodeEmergence.jl). An internode produces two leaves and a new internode. - -Let's first define a helper function that iterates across a Multiscale Tree Graph and returns the number of leaves : - -```@example usepkg -function get_n_leaves(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - nleaves = length(MultiScaleTreeGraph.traverse(root, x->1, symbol=:Leaf)) - return nleaves -end -``` - -Now that we have that, let's define a few parameters to the model. It requires : -- a thermal time emergence threshold -- a carbon cost for organ creation - -We'll also add a couple of other parameters, which could go elsewhere : -- the surface area of a leaf (no variation, no growth stages) -- the max leaf surface area beyond which organ creation stops - -```@example usepkg -PlantSimEngine.@process "organ_emergence" verbose = false - -struct ToyCustomInternodeEmergence{T} <: AbstractOrgan_EmergenceModel - TT_emergence::T - carbon_internode_creation_cost::T - leaf_surface_area::T - leaves_max_surface_area::T -end -``` - -!!! note - We make use of parametric types instead of the intuitive Float64 for flexibility. See [Parametric types](@ref) for a more in-depth explanation - -And give them some default values : - -```@example usepkg -ToyCustomInternodeEmergence(;TT_emergence=300.0, carbon_internode_creation_cost=200.0, leaf_surface_area=3.0, leaves_max_surface_area=100.0) = ToyCustomInternodeEmergence(TT_emergence, carbon_internode_creation_cost, leaf_surface_area, leaves_max_surface_area) -``` - -Our internode model requires thermal time, and the amount of available carbon, and outputs the amount of carbon consumed, as well as the last thermal time where emergence happened (this is useful when new organs can be produced multiple times, which won't be the case here). - -```@example usepkg -PlantSimEngine.inputs_(m::ToyCustomInternodeEmergence) = (TT_cu=0.0, carbon_stock=0.0) -PlantSimEngine.outputs_(m::ToyCustomInternodeEmergence) = (TT_cu_emergence=0.0, carbon_organ_creation_consumed=0.0) -``` -Finally, the [`run!`](@ref) function checks that conditions are met for new organ creation : -- thermal time threshold exceeded -- total leaf surface area not above limit -- carbon available -- no organs already created by that internode - -and then updates the MTG. - -```@example usepkg -function PlantSimEngine.run!(m::ToyCustomInternodeEmergence, models, status, meteo, constants=nothing, sim_object=nothing) - - leaves_surface_area = m.leaf_surface_area * get_n_leaves(status.node) - status.carbon_organ_creation_consumed = 0.0 - - if leaves_surface_area > m.leaves_max_surface_area - return nothing - end - - # if not enough carbon, no organ creation - if status.carbon_stock < m.carbon_internode_creation_cost - return nothing - end - - if length(MultiScaleTreeGraph.children(status.node)) == 2 && - status.TT_cu - status.TT_cu_emergence >= m.TT_emergence - status_new_internode = add_organ!(status.node, sim_object, "<", :Internode, 2, index=1) - add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1) - add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1) - - status_new_internode.TT_cu_emergence = m.TT_emergence - status.TT_cu - status.carbon_organ_creation_consumed = m.carbon_internode_creation_cost - end - - return nothing -end -``` - -### Updated mapping - -We can now define the final mapping for this simulation. - -The carbon capture and thermal time models don't need to be changed from the earlier version. -The organ creation model at the :Internode scale needs the carbon stock from the :Plant scale, as well as thermal time from the :Scene scale. -The resource storing model at the :Plant scale needs the carbon captured by **every** leaf, and the carbon consumed by **every** internode that created new organs this timestep. This requires mapping vector variables : - -```julia - mapped_variables=[ - :carbon_captured=>[:Leaf], - :carbon_organ_creation_consumed=>[:Internode] - ], -``` -as opposed to the single-valued carbon stock mapped variable : - -```julia - mapped_variables=[:TT_cu => :Scene, - PreviousTimeStep(:carbon_stock)=>:Plant], -``` - -And of course, some variables need to be initialized in the status: - -```@example usepkg -mapping = PlantSimEngine.ModelMapping( -:Scene => ToyDegreeDaysCumulModel(), -:Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyStockComputationModel(), - mapped_variables=[ - :carbon_captured=>[:Leaf], - :carbon_organ_creation_consumed=>[:Internode] - ], - ), - Status(carbon_stock = 0.0) - ), -:Internode => ( - PlantSimEngine.MultiScaleModel( - model=ToyCustomInternodeEmergence(),#TT_emergence=20.0), - mapped_variables=[:TT_cu => :Scene, - PreviousTimeStep(:carbon_stock)=>:Plant], - ), - Status(carbon_organ_creation_consumed=0.0), - ), -:Leaf => ToyLeafCarbonCaptureModel(), -) -``` - -!!! note - This excerpt (and the complete script file) showcase the final properly initialized mapping, but when developing, you are encouraged to make liberal use of the helper function [`to_initialize`](@ref) and check the PlantSimEngine user errors. - -### Running a simulation - -We only need an MTG, and some weather data, and then we'll be set. Let's create a simple MTG : - -```@example usepkg -mtg = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) -plant = MultiScaleTreeGraph.Node(mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - -internode1 = MultiScaleTreeGraph.Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) -MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) -MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - -internode2 = MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("<", :Internode, 1, 2)) -MultiScaleTreeGraph.Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) -MultiScaleTreeGraph.Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) -``` - -Import some weather data: - -```@example usepkg -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) -nothing # hide -``` - -And we're good to go ! - -```@example usepkg -outs = run!(mtg, mapping, meteo_day) -``` - -If you query or display the MTG after simulation, you'll see it expanded and grew multiple internodes and leaves : - -```@example usepkg -mtg -#get_n_leaves(mtg) -``` - -And that's it ! Feel free to tinker with the parameters and see when things break down, to get a feel for the simulation. - -Of course, this is a very crude and unrealistic simulation, with many dubious assumptions and parameters. But significantly more complex modelling is possible using the same approach : XPalm runs using a few dozen models spread out over nine scales. - -This is a three-part tutorial and continues in the [Expanding on the multiscale simulation](@ref) page. \ No newline at end of file diff --git a/docs/src/multiscale/multiscale_example_2.md b/docs/src/multiscale/multiscale_example_2.md deleted file mode 100644 index 4e7cd815b..000000000 --- a/docs/src/multiscale/multiscale_example_2.md +++ /dev/null @@ -1,266 +0,0 @@ -# Expanding on the multiscale simulation - -Let's build on the previous example and add some other organ growth, as well as some very mild coupling between the two. - -You can find the full script for this simulation in the [ToyMultiScalePlantModel](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/examples/ToyMultiScalePlantModel/ToyPlantSimulation2.jl) subfolder of the examples folder. - -```@contents -Pages = ["multiscale_example_2.md"] -Depth = 3 -``` - -## Setup - -Once again, with a properly set-up Julia environment: - -```@example usepkg -using PlantSimEngine -using PlantSimEngine.Examples -using PlantMeteo, Dates, Dates -using MultiScaleTreeGraph - -PlantSimEngine.@process "leaf_carbon_capture" verbose = false - -struct ToyLeafCarbonCaptureModel<: AbstractLeaf_Carbon_CaptureModel end - -function PlantSimEngine.inputs_(::ToyLeafCarbonCaptureModel) - NamedTuple() # No inputs -end - -function PlantSimEngine.outputs_(::ToyLeafCarbonCaptureModel) - (carbon_captured=0.0,) -end - -function PlantSimEngine.run!(::ToyLeafCarbonCaptureModel, models, status, meteo, constants, extra) - status.carbon_captured = 40 -end - -function get_n_leaves(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - nleaves = length(MultiScaleTreeGraph.traverse(root, x->1, symbol=:Leaf)) - return nleaves -end -``` - -## Adding roots to our plant - -We'll add a root that extracts water and adds it to the stock. Initial water stocks are low, so root growth is prioritized, then the plant also grows leaves and a new internode like it did before. Roots only grow up to a certain point, and don't branch. - -This leads to adding a new scale, :Root to the mapping, as well as two more models, one for water absorption, the other for root growth. Other models are updated here and there to account for water. The carbon capture model remains unchanged, and so is the `get_n_leaves` helper function. - -## Root models - -### Water absorption - -Let's implement a very fake model of root water absorption. It'll capture the amount of precipitation in the weather data multiplied by some assimilation factor. - -```@example usepkg -PlantSimEngine.@process "water_absorption" verbose = false - -struct ToyWaterAbsorptionModel <: AbstractWater_AbsorptionModel -end - -PlantSimEngine.inputs_(::ToyWaterAbsorptionModel) = (root_water_assimilation=1.0,) -PlantSimEngine.outputs_(::ToyWaterAbsorptionModel) = (water_absorbed=0.0,) - -function PlantSimEngine.run!(m::ToyWaterAbsorptionModel, models, status, meteo, constants=nothing, extra=nothing) - status.water_absorbed = meteo.Precipitations * status.root_water_assimilation -end -``` - -### Root growth - -The root growth model is similar to the internode growth one : it checks for a water threshold and that there is enough carbon, and adds a new organ to the MTG if the maximum length hasn't been reached. - -It also makes use of a couple of helper functions to find the end root and compute root length : - -```@example usepkg -function get_root_end_node(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - return MultiScaleTreeGraph.traverse(root, x->x, symbol=:Root, filter_fun = MultiScaleTreeGraph.isleaf) -end - -function get_roots_count(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - return length(MultiScaleTreeGraph.traverse(root, x->x, symbol=:Root)) -end - -PlantSimEngine.@process "root_growth" verbose = false - -struct ToyRootGrowthModel{T} <: AbstractRoot_GrowthModel - water_threshold::T - carbon_root_creation_cost::T - root_max_len::Int -end - -PlantSimEngine.inputs_(::ToyRootGrowthModel) = (water_stock=0.0,carbon_stock=0.0,) -PlantSimEngine.outputs_(::ToyRootGrowthModel) = (carbon_root_creation_consumed=0.0,) - -function PlantSimEngine.run!(m::ToyRootGrowthModel, models, status, meteo, constants=nothing, extra=nothing) - if status.water_stock < m.water_threshold && status.carbon_stock > m.carbon_root_creation_cost - - root_end = get_root_end_node(status.node) - - if length(root_end) != 1 - throw(AssertionError("Couldn't find MTG leaf node with symbol \"Root\"")) - end - root_len = get_roots_count(root_end[1]) - if root_len < m.root_max_len - st = add_organ!(root_end[1], extra, "<", :Root, 2, index=1) - status.carbon_root_creation_consumed = m.carbon_root_creation_cost - end - else - status.carbon_root_creation_consumed = 0.0 - end -end -``` - -## Updating other models to account for water - -### Resource storage - -Water absorbed must now be accumulated, and root carbon creation costs taken into account. - -```@example usepkg -PlantSimEngine.@process "resource_stock_computation" verbose = false - -struct ToyStockComputationModel <: AbstractResource_Stock_ComputationModel -end - -PlantSimEngine.inputs_(::ToyStockComputationModel) = -(water_absorbed=0.0,carbon_captured=0.0,carbon_organ_creation_consumed=0.0,carbon_root_creation_consumed=0.0) - -PlantSimEngine.outputs_(::ToyStockComputationModel) = (water_stock=-Inf,carbon_stock=-Inf) - -function PlantSimEngine.run!(m::ToyStockComputationModel, models, status, meteo, constants=nothing, extra=nothing) - status.water_stock += sum(status.water_absorbed) - status.carbon_stock += sum(status.carbon_captured) - sum(status.carbon_organ_creation_consumed) - sum(status.carbon_root_creation_consumed) -end -``` - -### Internode creation - -The minor change is that new organs are now created only if the water stock is above a given threshold. - -```@example usepkg -struct ToyCustomInternodeEmergence{T} <: AbstractOrgan_EmergenceModel - TT_emergence::T - carbon_internode_creation_cost::T - leaf_surface_area::T - leaves_max_surface_area::T - water_leaf_threshold::T -end - -ToyCustomInternodeEmergence(;TT_emergence=300.0, carbon_internode_creation_cost=200.0, leaf_surface_area=3.0,leaves_max_surface_area=100.0, -water_leaf_threshold=30.0) = ToyCustomInternodeEmergence(TT_emergence, carbon_internode_creation_cost, leaf_surface_area, leaves_max_surface_area, water_leaf_threshold) - -PlantSimEngine.inputs_(m::ToyCustomInternodeEmergence) = (TT_cu=0.0,water_stock=0.0, carbon_stock=0.0) -PlantSimEngine.outputs_(m::ToyCustomInternodeEmergence) = (TT_cu_emergence=0.0, carbon_organ_creation_consumed=0.0) - -function PlantSimEngine.run!(m::ToyCustomInternodeEmergence, models, status, meteo, constants=nothing, sim_object=nothing) - - leaves_surface_area = m.leaf_surface_area * get_n_leaves(status.node) - status.carbon_organ_creation_consumed = 0.0 - - if leaves_surface_area > m.leaves_max_surface_area - return nothing - end - - # if water levels are low, prioritise roots - if status.water_stock < m.water_leaf_threshold - return nothing - end - - # if not enough carbon, no organ creation - if status.carbon_stock < m.carbon_internode_creation_cost - return nothing - end - - if length(MultiScaleTreeGraph.children(status.node)) == 2 && - status.TT_cu - status.TT_cu_emergence >= m.TT_emergence - status_new_internode = add_organ!(status.node, sim_object, "<", :Internode, 2, index=1) - add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1) - add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1) - - status_new_internode.TT_cu_emergence = m.TT_emergence - status.TT_cu - status.carbon_organ_creation_consumed = m.carbon_internode_creation_cost - end - - return nothing -end -``` - -## Updating the mapping - -The resource storage and internode emergence models now need a couple of extra water-related mapped variables. -The :Root organ is added to the mapping with its own models. New parameters need to be initialized. - -```@example usepkg -mapping = PlantSimEngine.ModelMapping( -:Scene => ToyDegreeDaysCumulModel(), -:Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyStockComputationModel(), - mapped_variables=[ - :carbon_captured=>[:Leaf], - :water_absorbed=>[:Root], - :carbon_root_creation_consumed=>[:Root], - :carbon_organ_creation_consumed=>[:Internode] - - ], - ), - Status(water_stock = 0.0, carbon_stock = 0.0) - ), -:Internode => ( - PlantSimEngine.MultiScaleModel( - model=ToyCustomInternodeEmergence(),#TT_emergence=20.0), - mapped_variables=[:TT_cu => :Scene, - PreviousTimeStep(:water_stock)=>:Plant, - PreviousTimeStep(:carbon_stock)=>:Plant], - ), - Status(carbon_organ_creation_consumed=0.0), - ), -:Root => ( PlantSimEngine.MultiScaleModel( - model=ToyRootGrowthModel(10.0, 50.0, 10), - mapped_variables=[PreviousTimeStep(:carbon_stock)=>:Plant, - PreviousTimeStep(:water_stock)=>:Plant], - ), - ToyWaterAbsorptionModel(), - Status(carbon_root_creation_consumed=0.0, root_water_assimilation=1.0), - ), -:Leaf => ( ToyLeafCarbonCaptureModel(),), -) -``` - -## Running the simulation - -Running this new simulation is almost the same as before. The weather data is unchanged, but a new :Root node was added to the MTG. - -```@example usepkg -mtg = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant = MultiScaleTreeGraph.Node(mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - - internode1 = MultiScaleTreeGraph.Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - - internode2 = MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("<", :Internode, 1, 2)) - MultiScaleTreeGraph.Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - MultiScaleTreeGraph.Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - - plant_root_start = MultiScaleTreeGraph.Node( - plant, - MultiScaleTreeGraph.NodeMTG("+", :Root, 1, 3), - ) - -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) - -outs = run!(mtg, mapping, meteo_day) -mtg -``` - -And that's it ! - -...Or is it ? - -If you inspect the code and output data closely, you may notice some distinctive problems with the way the simulation runs... Some things aren't quite right. If you wish to know more, onwards to the next chapter: [Fixing bugs in the plant simulation](@ref) \ No newline at end of file diff --git a/docs/src/multiscale/multiscale_example_3.md b/docs/src/multiscale/multiscale_example_3.md deleted file mode 100644 index 18dd03acf..000000000 --- a/docs/src/multiscale/multiscale_example_3.md +++ /dev/null @@ -1,503 +0,0 @@ -# Fixing bugs in the plant simulation - -```@setup usepkg -using PlantSimEngine -using PlantSimEngine.Examples -using PlantMeteo, Dates -using MultiScaleTreeGraph -function get_root_end_node(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - return MultiScaleTreeGraph.traverse(root, x->x, symbol=:Root, filter_fun = MultiScaleTreeGraph.isleaf) -end - -function get_roots_count(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - return length(MultiScaleTreeGraph.traverse(root, x->x, symbol=:Root)) -end - -function get_n_leaves(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - nleaves = length(MultiScaleTreeGraph.traverse(root, x->1, symbol=:Leaf)) - return nleaves -end - -PlantSimEngine.@process "organ_emergence" verbose = false - -struct ToyCustomInternodeEmergence{T} <: AbstractOrgan_EmergenceModel - TT_emergence::T - carbon_internode_creation_cost::T - leaf_surface_area::T - leaves_max_surface_area::T - water_leaf_threshold::T -end - -ToyCustomInternodeEmergence(;TT_emergence=300.0, carbon_internode_creation_cost=200.0, leaf_surface_area=3.0,leaves_max_surface_area=100.0, -water_leaf_threshold=30.0) = ToyCustomInternodeEmergence(TT_emergence, carbon_internode_creation_cost, leaf_surface_area, leaves_max_surface_area, water_leaf_threshold) - -PlantSimEngine.inputs_(m::ToyCustomInternodeEmergence) = (TT_cu=0.0,water_stock=0.0, carbon_stock=0.0) -PlantSimEngine.outputs_(m::ToyCustomInternodeEmergence) = (TT_cu_emergence=0.0, carbon_organ_creation_consumed=0.0) - -function PlantSimEngine.run!(m::ToyCustomInternodeEmergence, models, status, meteo, constants=nothing, sim_object=nothing) - - leaves_surface_area = m.leaf_surface_area * get_n_leaves(status.node) - status.carbon_organ_creation_consumed = 0.0 - - if leaves_surface_area > m.leaves_max_surface_area - return nothing - end - - # if water levels are low, prioritise roots - if status.water_stock < m.water_leaf_threshold - return nothing - end - - # if not enough carbon, no organ creation - if status.carbon_stock < m.carbon_internode_creation_cost - return nothing - end - - if length(MultiScaleTreeGraph.children(status.node)) == 2 && - status.TT_cu - status.TT_cu_emergence >= m.TT_emergence - status_new_internode = add_organ!(status.node, sim_object, "<", :Internode, 2, index=1) - add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1) - add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1) - - status_new_internode.TT_cu_emergence = m.TT_emergence - status.TT_cu - status.carbon_organ_creation_consumed = m.carbon_internode_creation_cost - end - - return nothing -end - -############################ -# Naive water absorption model -# Absorbs precipitation water depending on quantity of roots -############################ -PlantSimEngine.@process "water_absorption" verbose = false - -struct ToyWaterAbsorptionModel <: AbstractWater_AbsorptionModel -end - -PlantSimEngine.inputs_(::ToyWaterAbsorptionModel) = (root_water_assimilation=1.0,) -PlantSimEngine.outputs_(::ToyWaterAbsorptionModel) = (water_absorbed=0.0,) - -function PlantSimEngine.run!(m::ToyWaterAbsorptionModel, models, status, meteo, constants=nothing, extra=nothing) - #root_end = get_root_end_node(status.node) - #root_len = root_end[:Root_len] - status.water_absorbed = meteo.Precipitations * status.root_water_assimilation #* root_len -end - -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyWaterAbsorptionModel}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyWaterAbsorptionModel}) = PlantSimEngine.IsObjectIndependent() - - -########################## -### Root growth : when water stocks are low, expand root -########################## - -PlantSimEngine.@process "root_growth" verbose = false - -struct ToyRootGrowthModel{T} <: AbstractRoot_GrowthModel - water_threshold::T - carbon_root_creation_cost::T - root_max_len::Int -end - -PlantSimEngine.inputs_(::ToyRootGrowthModel) = (water_stock=0.0,carbon_stock=0.0,) -PlantSimEngine.outputs_(::ToyRootGrowthModel) = (carbon_root_creation_consumed=0.0,) - -function PlantSimEngine.run!(m::ToyRootGrowthModel, models, status, meteo, constants=nothing, extra=nothing) - if status.water_stock < m.water_threshold && status.carbon_stock > m.carbon_root_creation_cost - - root_end = get_root_end_node(status.node) - - if length(root_end) != 1 - throw(AssertionError("Couldn't find MTG leaf node with symbol \"Root\"")) - end - root_len = get_roots_count(root_end[1]) - if root_len < m.root_max_len - st = add_organ!(root_end[1], extra, "<", :Root, 2, index=1) - status.carbon_root_creation_consumed = m.carbon_root_creation_cost - end - else - status.carbon_root_creation_consumed = 0.0 - end -end - -########################## -### Model accumulating carbon and water resources -########################## - -PlantSimEngine.@process "resource_stock_computation" verbose = false - -struct ToyStockComputationModel <: AbstractResource_Stock_ComputationModel -end -#status.water_stock += meteo.precipitations * root_water_assimilation_ratio - -PlantSimEngine.inputs_(::ToyStockComputationModel) = -(water_absorbed=0.0,carbon_captured=0.0,carbon_organ_creation_consumed=0.0,carbon_root_creation_consumed=0.0) - -PlantSimEngine.outputs_(::ToyStockComputationModel) = (water_stock=-Inf,carbon_stock=-Inf) - -function PlantSimEngine.run!(m::ToyStockComputationModel, models, status, meteo, constants=nothing, extra=nothing) - status.water_stock += sum(status.water_absorbed) #- status.water_transpiration - status.carbon_stock += sum(status.carbon_captured) - sum(status.carbon_organ_creation_consumed) - sum(status.carbon_root_creation_consumed) - - if status.water_stock < 0.0 - status.water_stock = 0.0 - end -end - -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyStockComputationModel}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyStockComputationModel}) = PlantSimEngine.IsObjectIndependent() - -######################## -## Leaf model capturing some arbitrary carbon quantity -######################## - -PlantSimEngine.@process "leaf_carbon_capture" verbose = false - -struct ToyLeafCarbonCaptureModel<: AbstractLeaf_Carbon_CaptureModel end - -function PlantSimEngine.inputs_(::ToyLeafCarbonCaptureModel) - NamedTuple()#(TT_cu=-Inf) -end - -function PlantSimEngine.outputs_(::ToyLeafCarbonCaptureModel) - (carbon_captured=0.0,) -end - -function PlantSimEngine.run!(::ToyLeafCarbonCaptureModel, models, status, meteo, constants, extra) - # very crude approximation with LAI of 1 and constant PPFD - status.carbon_captured = 200.0 *(1.0 - exp(-0.2)) -end - -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyLeafCarbonCaptureModel}) = PlantSimEngine.IsObjectIndependent() -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyLeafCarbonCaptureModel}) = PlantSimEngine.IsTimeStepIndependent() - -mapping = PlantSimEngine.ModelMapping( -:Scene => ToyDegreeDaysCumulModel(), -:Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyStockComputationModel(), - mapped_variables=[ - :carbon_captured=>[:Leaf], - :water_absorbed=>[:Root], - :carbon_root_creation_consumed=>[:Root], - :carbon_organ_creation_consumed=>[:Internode] - - ], - ), - Status(water_stock = 0.0, carbon_stock = 0.0) - ), -:Internode => ( - PlantSimEngine.MultiScaleModel( - model=ToyCustomInternodeEmergence(),#TT_emergence=20.0), - mapped_variables=[:TT_cu => :Scene, - PreviousTimeStep(:water_stock)=>:Plant, - PreviousTimeStep(:carbon_stock)=>:Plant], - ), - Status(carbon_organ_creation_consumed=0.0), - ), -:Root => ( PlantSimEngine.MultiScaleModel( - model=ToyRootGrowthModel(10.0, 50.0, 10), - mapped_variables=[PreviousTimeStep(:carbon_stock)=>:Plant, - PreviousTimeStep(:water_stock)=>:Plant], - ), - ToyWaterAbsorptionModel(), - Status(carbon_root_creation_consumed=0.0, root_water_assimilation=1.0), - ), -:Leaf => ( ToyLeafCarbonCaptureModel(),), -) - -mtg = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - -plant = MultiScaleTreeGraph.Node(mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - -internode1 = MultiScaleTreeGraph.Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) -MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) -MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - -internode2 = MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("<", :Internode, 1, 2)) -MultiScaleTreeGraph.Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) -MultiScaleTreeGraph.Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - -plant_root_start = MultiScaleTreeGraph.Node( - plant, - MultiScaleTreeGraph.NodeMTG("+", :Root, 1, 3), -) - -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) - -``` - -There are two major issues hinted at in last chapter's implementation, which we'll discuss and resolve here. - -You can find the full script for this simulation in the [ToyMultiScalePlantModel](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/examples/ToyMultiScalePlantModel/ToyPlantSimulation3.jl) subfolder of the examples folder. - -```@contents -Pages = ["multiscale_example_3.md"] -Depth = 3 -``` - -## An organ creation problem - -There is one quirk you may have noticed when inspecting the data : when a root expands, the new root is immediately active, and some models may act on it immediately... including the root growth model. Meaning this new root may also sprout another root in the same timestep, and so on. - -You can notice this by looking at the simulation's state during the first two timesteps: - -```@example usepkg -outs = run!(mtg, mapping, first(meteo_day, 2)) - -root_nodes_per_timestep = [0, 0] -for i in 1:length(outs[:Root]) - if outs[:Root][i].timestep < 3 - root_nodes_per_timestep[outs[:Root][i].timestep] += 1 - end -end - -root_nodes_per_timestep -``` - -Our root grew to full length within one timestep. Oops. - -This is an implementation decision in PlantSimEngine. **By default, newly created organs are active**, and models can affect them **as soon as they are created**. - -In our case, internode growth depends on a threshold thermal time value, which accumulates over several timesteps, so even though new internodes are immediately active, they can't themselves grow new organs within the same timestep. But as we've just showcased, we have a root problem. - -This quirk is also handled in [XPalm.jl](https://github.com/PalmStudio/XPalm.jl), a package using PlantSimEngine: some organs make use of state machines, and are considered "immature" when they are created. Immature organs cannot grow new organs until some conditions are met for their state to change. There are also other conditions governing organ emergence, such as specific threshold values relating to Thermal Time (see [here](https://github.com/PalmStudio/XPalm.jl/blob/433e1c47c743e7a53e764672818a43ed8feb10c6/src/plant/phytomer/leaves/phyllochron.jl#L46) for an example). - -!!! note - This implementation decision for new organs to be immediately active may be subject to change in future versions of PlantSimEngine. Also note that the way the dependency graph is structured determines the order in which models run. Meaning that which models are run before or after organ creation might change with new additions and updates to your mapping. Some models might run "one timestep later", see [Simulation order instability when adding models](@ref) for more details. - -!!! note - MTG node output data has a couple of subtleties, see [Multi-scale output data structure](@ref) for more details - -### Delaying organ maturity - -How do we avoid this extreme instant growth ? We can, of course, add some thermal time constraint. We could arbitrarily tinker with water resources. - -We can otherwise add a simple state machine variable to our root and internodes in the MTG, indicating a newly added organ is immature and cannot grow on the same timestep. Since our root doesn't branch, we can simply keep track of a single state variable. See the [State machines](@ref) section for some examples. - -In fact, we could change the scale at which the check is made to extend the root, and have another model call this one directly. This enables running this model only for the end root when those occasional timesteps when root growth is possible, instead of at every timestep for every root node. - -## A resource distribution bug - -Another problem you may have noticed, is that the water and carbon stock are computed by aggregating photosynthesis over leaves and absorption over roots... But they aren't always properly decremented when consumed ! - -If the end root grows, it outputs a `carbon_root_creation_consumed` value, but under certain conditions, we might also create other roots and internodes even when there shouldn't be enough carbon left for them. - -Indeed, if both the root and leaf water thresholds are met, and there is enough carbon for a single root or internode but not for both, and the root model runs before the internode model, both will use the carbon_stock variable prior to organ emission. The internode emission model won't account for the root carbon consumption. - -This occurs because `carbon_stock` is only computed once, and won't update until the next timestep. - -### Fixing resource computation: a root growth decision model - -To avoid that problem in our specific case, we can couple the root growth model and the internode emission model, and pass the `carbon_root_creation_consumed` variable to the internode emission model so that it can use an updated carbon stock. Or we could have an intermediate model recompute the new stock to pass along to the internode emission model. - -There is a section in the [Tips and workarounds] page discussing this situation and other potential solutions: [Having a variable simultaneously as input and output of a model](@ref). - -We'll go for the first option and couple the root growth and internode emission model. - -### Internode emission adjustments - -The only change required for our internode emission model is to take into account `carbon_root_creation_consumed` as a new input, map that variable from the :Root scale in our mapping, and compute the adjusted carbon stock. Here's the relevant excerpt in the [`run!`](@ref) function. - -```julia - # take into account that the stock may already be depleted - carbon_stock_updated_after_roots = status.carbon_stock - status.carbon_root_creation_consumed - - # if not enough carbon, no organ creation - if carbon_stock_updated_after_roots < m.carbon_internode_creation_cost - return nothing - end -``` - -### A multi-scale hard dependency appears - -Our root growth decision model inherits some of the responsibility from last chapter's root growth model, so inputs, parameters and condition checks will be similar. We'll let the root growth model keep the length check and only focus on resources. - -Since the decision model is now directly responsible for calling the actual root growth model, we need to declare that it requires a root growth model as a hard dependency and cannot be run standalone. - -This hard dependency is in fact multiscale, since both models operate at different scales, :Plant and :Root. You can read more about multi-scale hard dependencies in the [Handling dependencies in a multiscale context](@ref) page. - -Compared to the single-scale equivalent, the multi-scale declaration additionally requires mapping the scale: - -```julia -PlantSimEngine.dep(::ToyRootGrowthDecisionModel) = (root_growth=AbstractRoot_GrowthModel=>[:Root],) -``` - -The `status` argument [`run!`](@ref) function of the root growth decision model only contains variables from the :Plant scale, or explicitely mapped to this scale, which isn't the case for the root growth's variables. To make use of the root growth model's variables, we need to recover the [`status`](@ref) at the :Root scale. It is accessible from the `extra` argument in [`run!`](@ref)'s signature. - -In multi-scale simulations, this `extra` argument implicitely contains an object storing the simulation state. It contains the statuses at various scales, and all the models indexed per scale and process name. - -Access to the :Root status within the root growth decision model [`run!`](@ref) function is done like so: - -```julia -status_Root= extra_args.statuses[:Root][1] -``` - -It is then possible to call the root growth model from the parent's [`run!`](@ref) function: - -```julia -PlantSimEngine.run!(extra.models[:Root].root_growth, models, status_Root, meteo, constants, extra) -``` - -Which will enable writing the rest of the [`run!`](@ref) function. - -### Root growth decision model implementation - -With that new coupling consideration properly handled, we can complete the full model implementation: - -```julia -PlantSimEngine.@process "root_growth_decision" verbose = false - -struct ToyRootGrowthDecisionModel{T} <: AbstractRoot_Growth_DecisionModel - water_threshold::T - carbon_root_creation_cost::T -end - -PlantSimEngine.inputs_(::ToyRootGrowthDecisionModel) = -(water_stock=0.0,carbon_stock=0.0) - -PlantSimEngine.outputs_(::ToyRootGrowthDecisionModel) = NamedTuple() - -PlantSimEngine.dep(::ToyRootGrowthDecisionModel) = (root_growth=AbstractRoot_GrowthModel=>[:Root],) - -# "status" is at the :Plant scale -function PlantSimEngine.run!(m::ToyRootGrowthDecisionModel, models, status, meteo, constants=nothing, extra=nothing) - - if status.water_stock < m.water_threshold && status.carbon_stock > m.carbon_root_creation_cost - # Obtain "status" at :Root scale - status_Root= extra_args.statuses[:Root][1] - # Call the hard dependency model directly with its status - PlantSimEngine.run!(extra.models[:Root].root_growth, models, status_Root, meteo, constants, extra) - end -end -``` - -The root growth model will output the `carbon_root_creation_consumed` computation, but it'll still be exposed to downstream models despite the root growth model being a 'hidden' model in the dependency graph due to its hard dependency nature. - -With this new coupling, we will only be creating at most a single new root per timestep, as the root growth decision will only be called once per timestep. - -### Root growth - -This iteration turns into a simplifed version of last chapter's. - -```julia -PlantSimEngine.@process "root_growth" verbose = false - -struct ToyRootGrowthModel <: AbstractRoot_GrowthModel - root_max_len::Int -end - -PlantSimEngine.inputs_(::ToyRootGrowthModel) = NamedTuple() -PlantSimEngine.outputs_(::ToyRootGrowthModel) = (carbon_root_creation_consumed=0.0,) - -function PlantSimEngine.run!(m::ToyRootGrowthModel, models, status, meteo, constants=nothing, extra=nothing) - status.carbon_root_creation_consumed = 0.0 - - root_end = get_root_end_node(status.node) - - if length(root_end) != 1 - throw(AssertionError("Couldn't find MTG leaf node with symbol \"Root\"")) - end - - root_len = get_roots_count(root_end[1]) - if root_len < m.root_max_len - st = add_organ!(root_end[1], extra, "<", :Root, 2, index=1) - status.carbon_root_creation_consumed = m.carbon_root_creation_cost - end -end -``` - -### Mapping adjustments - -The new mapping only has straightforward changes. Some models cease to be multi-scale, others require new variables to be mapped for them. `carbon_root_creation_consumed` ceases to be a vector mapping and is a scalar variable. - -```julia -mapping = PlantSimEngine.ModelMapping( -:Scene => ToyDegreeDaysCumulModel(), -:Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyStockComputationModel(), - mapped_variables=[ - :carbon_captured=>[:Leaf], - :water_absorbed=>[:Root], - :carbon_root_creation_consumed=>:Root, - :carbon_organ_creation_consumed=>[:Internode] - - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyRootGrowthDecisionModel(10.0, 50.0), - ), - Status(water_stock = 0.0, carbon_stock = 0.0) - ), -:Internode => ( - PlantSimEngine.MultiScaleModel( - model=ToyCustomInternodeEmergence(),#TT_emergence=20.0), - mapped_variables=[:TT_cu => :Scene, - :water_stock=>:Plant, - :carbon_stock=>:Plant, - :carbon_root_creation_consumed=>:Root], - ), - Status(carbon_organ_creation_consumed=0.0), - ), -:Root => (ToyRootGrowthModel(10), - ToyWaterAbsorptionModel(), - Status(carbon_root_creation_consumed=0.0, root_water_assimilation=1.0), - ), -:Leaf => ( ToyLeafCarbonCaptureModel(),), -) -``` - -We can now run our simulation as we did previously... or can we ? - -```julia -ERROR: Cyclic dependency detected for process resource_stock_computation: resource_stock_computation for organ Plant depends on root_growth from organ Root, which depends on the first one. This is not allowed, you may need to develop a new process that does the whole computation by itself. -``` - -Ah, it looks like our additional usage of the root carbon cost creates a cyclic dependency. - -### Breaking the dependency cycle - -Fortunately, the logic here is quite straightforward. We can't be computing our current timestep's resource stock with `carbon_root_creation_consumed`, and then updating it right after root creation again using a new value of `carbon_root_creation_consumed`. - -The solution is hopefully quite intuitive : when we compute resource stocks, we should be computing it using the previous timestep's values. Then root creation happens (or doesn't), and the computed `carbon_root_creation_consumed` corresponds to the current timestep value. We could also do the same for water to be consistent. - -### Updated mapping - -The relevant part of the mapping that needs to be updated is the following: - -```julia -mapping = PlantSimEngine.ModelMapping( -... -:Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyStockComputationModel(), - mapped_variables=[ - :carbon_captured=>[:Leaf], - :water_absorbed=>[:Root], - PreviousTimeStep(:carbon_root_creation_consumed)=>:Root, - PreviousTimeStep(:carbon_organ_creation_consumed)=>[:Internode], - ], - ), - ToyRootGrowthDecisionModel(10.0, 50.0), - Status(water_stock = 0.0, carbon_stock = 0.0) - ), -... -) -``` - -## Final words - -And you're now ready to run the simulation. - -The full script can be found [here](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/examples/ToyMultiScalePlantModel/ToyPlantSimulation3.jl), in the ToyMultiScalePlantModel subfolder of the examples folder. - -We now have a plant with two different growth directions. Roots are added at the beginning, until water is considered abundant enough. - -Of course, there are still several design issues with this implementation. It is as utterly unrealistic as the previous one, and doesn't even consume water. Some condition checking is a little ad hoc and could be made more robust. More sanity checks could be added, and the model and variable names could definitely be made more clear. - -But once again, this example is only made to illustrate what is possible with this framework, and doesn't strive for ecophysiological consistency. And the approach can be made increasingly more complex by refining models and simulation parameters, and feeding in new information about your plant, and ramp up to realistic, production-ready and predictive simulations. diff --git a/docs/src/multiscale/multiscale_example_4.md b/docs/src/multiscale/multiscale_example_4.md deleted file mode 100644 index f5105e84b..000000000 --- a/docs/src/multiscale/multiscale_example_4.md +++ /dev/null @@ -1,226 +0,0 @@ -# Visualizing a plant using PlantGeom - -We've created our toy plant, part of the fun is to actually visualize it ! - -Let's see how to do so with the [PlantGeom](https://github.com/VEZY/PlantGeom.jl) companion package. - -We'll be reusing the mtg from part 3 of the plant tutorial: [Fixing bugs in the plant simulation](@ref), so you need to run that simulation first, or to include the script file into your current code (which is what we'll do here): - -```julia -using PlantSimEngine -using MultiScaleTreeGraph -using PlantSimEngine.Examples -using Pkg -Pkg.add("CSV") -using CSV -include("ToyPlantSimulation3.jl") -``` - -You'll need to add PlantGeom and a compatible visualization package to your environment. We'll use Plots: - -```julia -using Plots -using PlantGeom -``` - -That's enough to get a nicer display of the MTG than the console-based printing. You'll only need to type the following line: - -```julia -RecipesBase.plot(mtg) -``` - -This provides the following visualization: -![MTG Plots visualization](../www/mtg_plot_1.svg) - -And that's it ! - -We can see the root expansion in one direction, and the internodes with their leaves in the other. - -Of course, that's good and all, but going beyond that would be nice. - -PlantGeom is able to render geometry from what it finds in the MTG. If a node in the tree graph has a `:geometry` attribute with a mesh and a transformation, it can make use of that to build a plant. That mesh can be unique per node, or based on a reference mesh that is copied and transformed for every node. - -!!! note - This page simply aims to illustrate PlantGeom's features and doesn't aim for a particularly realistic or aesthetic look. A little randomness could go a long way to make the plant look a little more life-like, but would also be very ad-hoc and make the code less clear. - -Our MTG doesn't have any such attribute, so we'll need to iterate on our nodes, provide them with a mesh and calculate appropriate transformations. We'll use one reference mesh for each plant-related scale, Internode, Root and Leaf. - -We'll make use of some of the Meshes primitives and transformations, as well as some helper functions from the packages TransformsBase and Rotations. For leaves, we'll read a .ply file using the PlyIO package, which contains a very unrealistic leaf + petiole mesh. - -We'll also make our plant opposite decussate: leaves come in pairs, and pairs are rotated by 90 degrees along the stem. - -The function that'll provide the geometry to the node is: -```julia -PlantGeom.Geometry(; ref_mesh<:RefMesh, transformation=Identity(), dUp=1.0, dDwn=1.0, mesh::Union{SimpleMesh,Nothing}=nothing) -``` - -We only care about the first two parameters in our case, and we can use a simple cylinder for each node of our single Internode stem and single Root. - -```julia -using PlantGeom.Meshes - -# Internodes and roots will use a cylinder as a mesh - -cylinder() = Meshes.CylinderSurface(1.0) |> Meshes.discretize |> Meshes.simplexify - -refmesh_internode = PlantGeom.RefMesh(:Internode, cylinder()) -refmesh_root = PlantGeom.RefMesh(:Root, cylinder()) -``` - -A simple function to read the vertices and faces from the .ply file for our leaves: - -```julia -Pkg.add("PlyIO") -using PlyIO -function read_ply(fname) - ply = PlyIO.load_ply(fname) - x = ply["vertex"]["x"] - y = ply["vertex"]["y"] - z = ply["vertex"]["z"] - points = Meshes.Point.(x, y, z) - connec = [Meshes.connect(Tuple(c .+ 1)) for c in ply["face"]["vertex_indices"]] - Meshes.SimpleMesh(points, connec) -end - -leaf_ply = read_ply("examples/leaf_with_petiole.ply") -refmesh_leaf = PlantGeom.RefMesh(:Leaf, leaf_ply) -``` - -```julia -Pkg.add("TransformsBase") -Pkg.add("Rotations") -import TransformsBase: → -import Rotations: RotY, RotZ, RotX -``` - -!!! note - We'll use X, Y, Z as standard cartersian coordinate axes, with Z pointing upwards. - -We can then write the function that adds the geometry to our MTG. - -It traverses the MTG, starting from the base, and adds a transformation for each encountered node. - -The following just operates on internodes, for clarity: - -```julia -# Add the geometry to the MTG, with transformations -function add_geometry!(mtg, refmesh_internode) - - # incremental offset - internode_height = 0.0 - - # relative scale of the base mesh - internode_width = 0.5 - - # length of the base mesh - internode_length = 1.0 - - traverse!(mtg) do node - if symbol(node) == :Internode - # Set to scale, then translate by the total height - mesh_transformation = Meshes.Scale(internode_width, internode_width, internode_length) → Meshes.Translate(0.0, 0.0, internode_height) - node.geometry = PlantGeom.Geometry(ref_mesh=refmesh_internode, transformation=mesh_transformation) - - internode_height += node_length - end - end -end -``` - -We simply need to choose a given width for our stem, and increment the height to place our next internode at as we traverse it. - -Note that the default cylinder provided by Meshes.jl points upwards, which is why there is no need for rotation. Roots function likewise, but are simply translated down, and need to start below the origin. - -We can visualize this simple stem, using GLMakie as a rendering backend: - -```julia -add_geometry!(mtg, refmesh_internode) - -# Visualize the mesh -using GLMakie -viz(mtg) -``` - -![Toy Plant - stem only](../www/toy_plant_stem_only.png) - -On the other hand, the leaf mesh will need to be rotated, but it is aligned along the X axis, so there is no need for an initial reorientation (which would have been required if it was pointing upwards like the cylinders). The petiole starts at the origin, so on top of translating them to leaf height we also need to translate them away from the Z axis by the internode radius. The mesh also needs to be scaled, as it is only 0.1 unit lengths long compared to our 0.5-width internode. - -Let's also rotate our leaves so that they point upwards slightly. - -If you make use of other meshes, bear in mind the initial starting translation, orientation and scale. You may need to test and calibrate scales and transformations before you get it right. - -The full code that generates geometry for all the organs of our toy plant is the following: -```julia -# Add the geometry to the MTG, with transformations -function add_geometry!(mtg, refmesh_internode, refmesh_root, refmesh_leaf) - - # incremental offset - internode_height = 0.0 - root_depth = 0.0 - - # relative scale of the base mesh (base cylinder is of height 1 and radius 1) - internode_width = 0.5 - root_width = 0.2 - - # length of the base mesh - internode_length = 1.0 - root_length = 1.0 - - # ad hoc value to adjust the leaf mesh to the scene scale - leaf_mesh_scale = 25 - - leaf_scale_width = 0.4*leaf_mesh_scale - leaf_scale_height = 0.4*leaf_mesh_scale - - # Helpers to make the leaves opposite decussate - leaf_rotation = MathConstants.pi / 2.0 - i = 0 - - traverse!(mtg) do node - if symbol(node) == :Internode - # Set to scale, then translate by the total height - mesh_transformation = Meshes.Scale(internode_width, internode_width, internode_length) → Meshes.Translate(0.0, 0.0, internode_height) - node.geometry = PlantGeom.Geometry(ref_mesh=refmesh_internode, transformation=mesh_transformation) - - internode_height += internode_length - - # Leaves are placed relatively to the parent internode, halfway along it - for chnode in children(node) - if symbol(chnode) == :Leaf - mesh_transformation = Meshes.Scale(leaf_scale_width, leaf_scale_width, leaf_scale_height) → Meshes.Rotate(RotX(-MathConstants.pi / 6.0)) → Meshes.Translate(0.0, -internode_width, internode_height - internode_length / 2.0) → Meshes.Rotate(RotZ(leaf_rotation)) - chnode.geometry = PlantGeom.Geometry(ref_mesh=refmesh_leaf, transformation=mesh_transformation) - # Set the second leaf in a pair opposite to the first one => add a 180° rotation - leaf_rotation += MathConstants.pi - end - end - - # Opposite decussate => 90° rotation between pairs - i += 1 - if i % 2 == 0 - leaf_rotation = MathConstants.pi / 2.0 - else - leaf_rotation = MathConstants.pi - end - - elseif symbol(node) == :Root - mesh_transformation = Meshes.Scale(root_width, root_width, root_length) → Meshes.Translate(0.0, 0.0, root_depth) → Meshes.Rotate(RotZ(MathConstants.pi)) - node.geometry = PlantGeom.Geometry(ref_mesh=refmesh_root, transformation=mesh_transformation) - root_depth -= root_length - end - end -end -``` - -And now, let's visualize our fully-grown, fully-featured plant: - -```julia -# Visualize the mesh -using GLMakie -viz(mtg) -``` - -Which gives us the following image scene: - -![Toy Plant with root and leaves](../www/toy_plant.png) - -Feel free to try and make this plant prettier, more colourful, or more physically realistic, using more realistic models on the PlantSimEngine side, or better geometry on the Plantgeom end. \ No newline at end of file diff --git a/docs/src/multiscale/single_to_multiscale.md b/docs/src/multiscale/single_to_multiscale.md deleted file mode 100644 index 09afeaa65..000000000 --- a/docs/src/multiscale/single_to_multiscale.md +++ /dev/null @@ -1,241 +0,0 @@ -# Converting a single-scale simulation to multi-scale - -!!! warning "Legacy multiscale configuration" - This page documents `ModelMapping` and `MultiScaleModel`. New multiscale - scenarios should use `Scene`, `Object`, `AppliesTo`, and `Inputs`; see - [Migrating To The Scene/Object API](../migration_scene_object.md). - -```@meta -CurrentModule = PlantSimEngine -``` - -```@setup usepkg -using PlantMeteo, Dates -using PlantSimEngine -using PlantSimEngine.Examples -using MultiScaleTreeGraph -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) -models_singlescale = PlantSimEngine.ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(0.2); - status=(TT_cu=cumsum(meteo_day.TT),), -) -``` - -A single-scale simulation can be turned into a 'pseudo-multi-scale' simulation by providing a simple multi-scale tree graph, and declaring a mapping linking all models to a unique scale level. - -This page showcases how to do the conversion, and then adds a model at a new scale to make the simulation genuinely multi-scale. - -The full script for the example can be found in the examples folder, [here](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/examples/ToySingleToMultiScale.jl) - -```@contents -Pages = ["single_to_multiscale.md"] -Depth = 3 -``` - -# From single to multi-scale mapping - -For example, let's return to the legacy -`PlantSimEngine.ModelMapping(...)` compatibility example coupling a light -interception model, a Leaf Area Index model, and a carbon biomass increment -model: - -```@example usepkg -using PlantMeteo, Dates -using PlantSimEngine -using PlantSimEngine.Examples - -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) - -models_singlescale = PlantSimEngine.ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(0.2); - status=(TT_cu=cumsum(meteo_day.TT),), -) - -outputs_singlescale = run!(models_singlescale, meteo_day) -outputs_singlescale[1:3,:] # show the first 3 rows of the output -``` - -Those models all operate on a simplified model of a single plant, without any organ-local information. We can therefore consider them to be working at the 'whole plant' scale. Their variables also operate at that `:Plant` scale, so there is no need to map any variable to other scales. - -We can therefore convert this into the following mapping: - -```@example usepkg -mapping = PlantSimEngine.ModelMapping( -:Plant => ( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(0.2), - Status(TT_cu=cumsum(meteo_day.TT),) - ), -) -``` - -Note the slight difference in syntax for the [`Status`](@ref). This is because each scale has its own variables, so we must provide the values to each scale independently. - -## Adding a new package for our plant graph - -None of these models operate on a multi-scale tree graph, either. There is no concept of organ creation or growth. We still need to provide a multi-scale tree graph to a multi-scale simulation, so we can -for now- declare a very simple MTG, with a single node: - -```@example usepkg -using MultiScaleTreeGraph - -mtg = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Plant, 0, 0),) -``` - -!!! note - You will need to add the `MultiScaleTreeGraph` package to your environment. See [Installing and running PlantSimEngine](@ref) if you are not yet comfortable with Julia or need a refresher. - -## Running the multi-scale simulation ? - -We now have **almost** everything we need to run the multiscale simulation. - -This first conversion step can be a starting point for a more elaborate multi-scale simulation. - -The signature of the [`run!`](@ref) function in multi-scale differs slightly from the single-scale version: - -```julia -out_multiscale = run!(mtg, mapping, meteo_day) -``` - -(Some of the optional arguments also change slightly) - -Passing in a vector through the [`Status`](@ref) field is still possible in multi-scale mode, but more involving than in single-scale mode. If you need to go this way, you can find a detailed example [here](@ref multiscale_vector), although we don't recommend it for beginners. In any case, it's simpler to write a model to provide the thermal time per timestep as a variable, instead of as a single vector in the [`Status`](@ref). - -Our 'pseudo-multiscale' first approach will therefore turn into a genuine multi-scale simulation. - -## Adding a second scale - -Let's have a model provide the Cumulated Thermal Time to our Leaf Area Index model, instead of initializing it through the [`Status`](@ref). - -Let's instead implement our own `ToyTT_cuModel`. - -### TT_cu model implementation - -This model doesn't require any outside data or input variables, it only operates on the weather data and outputs our desired TT_cu. The implementation doesn't require any advanced coupling and is very straightforward. - -```@example usepkg -PlantSimEngine.@process "tt_cu" verbose = false - -struct ToyTt_CuModel <: AbstractTt_CuModel -end - -function PlantSimEngine.run!(::ToyTt_CuModel, models, status, meteo, constants, extra=nothing) - status.TT_cu += - meteo.TT -end - -function PlantSimEngine.inputs_(::ToyTt_CuModel) - NamedTuple() # No input variables -end - -function PlantSimEngine.outputs_(::ToyTt_CuModel) - (TT_cu=0.0,) -end -``` - -!!! note - The only accessible variables in the [`run!`](@ref) function via the status are the ones that are local to the :Scene scale. This isn't explicit at first glance, but very important to keep in mind when developing models, or using them at different scales. If variables from other scales are required, then they need to be mapped via a [`PlantSimEngine.MultiScaleModel`](@ref), or sometimes a more complex coupling is necessary. - -### Linking the new TT_cu model to a scale in the mapping - -We now have our model implementation. How does it fit into our mapping ? - -Our new model doesn't really relate to a specific organ of our plant. In fact, this model doesn't represent a physiological process of the plant, but rather an environmental process affecting its physiology. We could therefore have it operate at a different scale unrelated to the plant, which we'll call :Scene. This makes sense. - -Note that we now need to add a :Scene node to our Multi-scale Tree Graph, otherwise our model will not run, since no other model calls it and :Plant nodes will only call models at the :Plant scale. See [Empty status vectors in multi-scale simulations](@ref) for more details. - -```@example usepkg -mtg_multiscale = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 0, 0),) - plant = MultiScaleTreeGraph.Node(mtg_multiscale, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) -``` - -### Mapping between scales : the MultiScaleModel wrapper - -The cumulated thermal time (`:TT_cu`) which was previously provided to the LAI model as a simulation parameter now needs to be mapped from the :Scene scale level. - -This is done by wrapping our ToyLAIModel in a dedicated structure called a [`PlantSimEngine.MultiScaleModel`](@ref). A [`PlantSimEngine.MultiScaleModel`](@ref) requires two keyword arguments : `model`, indicating the model for which some variables are mapped, and `mapped_variables`, indicating which scale link to which variables, and potentially renaming them. - -There can be different kinds of variable mapping with slightly different syntax, but in our case, only a single scalar value of the TT_cu is passed from the :Scene to the :Plant scale. - -This gives us the following declaration with the [`PlantSimEngine.MultiScaleModel`](@ref) wrapper for our LAI model: - -```@example usepkg -PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[ - :TT_cu => :Scene, - ], - ) -``` -and the new mapping with two scales: - -```@example usepkg -mapping_multiscale = PlantSimEngine.ModelMapping( - :Scene => ToyTt_CuModel(), - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[ - :TT_cu => :Scene, - ], - ), - Beer(0.5), - ToyRUEGrowthModel(0.2), - ), -) -``` - -### Running the multi-scale simulation - -We can then run the multiscale simulation, with our two-node MTG : - -```@example usepkg -outputs_multiscale = run!(mtg_multiscale, mapping_multiscale, meteo_day) -``` - -### Comparing outputs between single- and multi-scale - -The outputs structures are slightly different : multi-scale outputs are indexed by scale, and a variable has a value for every node of the scale it operates at (for instance, there would be a "leaf_surface" value for every leaf in a plant), stored in an array. - -In our simple example, we only have one MTG scene node and one plant node, so the arrays for each variable in the multi-scale output only contain one value. - -We can access the output variables at the :Scene scale by indexing our outputs: - -```@example usepkg -outputs_multiscale[:Scene] -``` -We have a `Vector{NamedTuple}`structure. Our single-scale output is a `Vector{T}`: -```@example usepkg -outputs_singlescale.TT_cu -``` - - Let's extract the multi-scale `:TT_cu`: -```@example usepkg -computed_TT_cu_multiscale = [outputs_multiscale[:Scene][i].TT_cu for i in 1:length(outputs_multiscale[:Scene])] -``` - -We can now compare them value-by-value and do a piecewise approximate equality test : -```@example usepkg -for i in 1:length(computed_TT_cu_multiscale) - if !(computed_TT_cu_multiscale[i] ≈ outputs_singlescale.TT_cu[i]) - println(i) - end -end -``` -or equivalently, with broadcasting, we can write : -```@example usepkg -is_approx_equal = length(unique(computed_TT_cu_multiscale .≈ outputs_singlescale.TT_cu)) == 1 -``` - -!!! note - You may be wondering why we check for approximate equality rather than strict equality. The reason for that is due to floating-point accumulation errors, which are discussed in more detail in [Floating-point considerations](@ref). - -## ToyDegreeDaysCumulModel - -There is a model able to provide Thermal Time based on weather temperature data, [`ToyDegreeDaysCumulModel`](@ref), which can also be found in the examples folder. - -We didn't make use of it here for learning purposes. It also computes a thermal time based on default parameters that don't correspond to the thermal time in the example weather data, so results differ from the thermal time already present in the weather data without tinkering with the parameters. diff --git a/docs/src/planned_features.md b/docs/src/planned_features.md index ba02656e2..8dd0f801c 100644 --- a/docs/src/planned_features.md +++ b/docs/src/planned_features.md @@ -1,51 +1,20 @@ # Roadmap -This page summarizes work that is still in progress or intentionally left for -future releases. It is not a guarantee of delivery order. - -## Current focus areas - -### Multi-rate MTG simulations - -Model-level varying timesteps are available experimentally for MTG simulations -through mapping-declared multi-rate execution and `ModelSpec` transforms such as -`TimeStepModel`, `InputBindings`, `OutputRouting`, and `ScopeModel`. - -Known gaps in the current implementation: - -- no sub-step execution below the meteorological base-step duration; -- no dedicated event scheduler for irregular or non-fixed calendar execution; -- no threaded or distributed multi-rate MTG execution path yet. - -### Multi-plant and multi-species simulations - -PlantSimEngine can already express multi-scale simulations, but practical support -for scenes containing several plants or several species with overlapping model -sets is still limited. Future work in this area is expected to focus on more -flexible mapping and parameter declaration. - -## API and ergonomics - -- a more consistent mapping API and clearer multiscale dependency declaration; -- improved user-facing errors and diagnostics; -- better dependency graph visualization and traversal helpers; -- broader examples for fitting, type conversion, and error propagation; -- clearer weather-data validation when a simulation requires meteorological inputs. - -## Testing and release engineering - -- broader downstream coverage and better release gating; -- additional checks for memory usage and type stability; -- state-machine or invariant-style validation for runtime outputs; -- graph fuzzing for multiscale corner cases. - -## Lower-priority ideas - -- API support for iterative construction and validation of `ModelMapping`; -- optional code-generation or build steps for validated mappings; -- improved parallel execution strategies; -- reintroducing multi-object parallelism in single-scale runs if the execution - model can stay predictable and testable. - -The full list of open issues is available on +PlantSimEngine now has one scene/object runtime for single-object, multiscale, +multi-plant, soil, microclimate, and multirate simulations. + +Current priorities are: + +- migrate downstream model packages to `Scene`, `ObjectTemplate`, + `ObjectInstance`, and `ModelSpec`; +- strengthen type-stability and allocation tests for million-object workloads; +- add broader lifecycle tests for object creation, removal, movement, and + environment-index refresh; +- improve diagnostics for ambiguous selectors, writer conflicts, and temporal + policies; +- validate mutable voxel, layer, and octree microclimate backends; +- expand downstream release gates and performance benchmarks; +- evaluate parallel execution for independent compiled application batches. + +The full issue list is available on [GitHub](https://github.com/VirtualPlantLab/PlantSimEngine.jl/issues). diff --git a/docs/src/prerequisites/installing_plantsimengine.md b/docs/src/prerequisites/installing_plantsimengine.md index 9cca3c015..ff88587be 100644 --- a/docs/src/prerequisites/installing_plantsimengine.md +++ b/docs/src/prerequisites/installing_plantsimengine.md @@ -1,78 +1,49 @@ -# Installing and running PlantSimEngine +# Installing PlantSimEngine -```@contents -Pages = ["installing_plantsimengine.md"] -Depth = 3 -``` - -This page is meant to help along people newer to Julia. If you are quite accustomed to Julia, installing PlantSimEngine should be par for the course, and you can [move on to the next section](#step_by_step), or read about PlantSimEngine's [Key Concepts](@ref). - -## Installing Julia - -The direct download link can be found [here](https://julialang.org/downloads/), and some additional pointers [in the official manual](https://docs.julialang.org/en/v1/manual/installation/). - -## Installing VSCode - -You can get by using a REPL, but if writing a larger piece of software you may prefer using an IDE. PlantSimEngine is developed using VSCode, which you can install by following instruction [on this page](https://code.visualstudio.com/docs/setup/setup-overview). A documentation section specific to using Julia in VSCode can be found [here](https://code.visualstudio.com/docs/languages/julia). - -## Installing PlantSimEngine and its dependencies - -### Julia environments - -Julia package management is done via the Pkg.jl package. You can find more in-depth sections detailing its usage, and working with Julia environments [in its documentation](https://pkgdocs.julialang.org/v1/) - -If you find this page insufficient to get started, [this tutorial](https://jkrumbiegel.com/pages/2022-08-26-pkg-introduction/) explains in detail the subtleties of Julia environments. - -### Running an environment - -Once your environment is set up, you can launch a command prompt and type `julia`. This will launch Julia, and you should see `julia>` in the command prompt. - -You can always type `?` from there to enter help mode, and type the name of a function or language feature you wish to know more about. - -You can find out which directory you are in by typing `pwd()` in a Julia session. - -Handling environments and dependencies is done in Julia through a specific Package called Pkg, which comes with the base install. You can either call Pkg features the same way you would for another package, or enter Pkg mode by typing `]`, which will change the display from `julia>` to something like `(@v1.11)` pkg>, indicating your current environment (in this case, the default julia environment, which we don't recommend bloating). +Install Julia from the +[official download page](https://julialang.org/downloads/), create a project +environment, and add PlantSimEngine: -Once in Pkg mode, you can choose to create an environment by typing `activate path/to/environment`. - -You can then add packages that have been added to Julia's online global registry by typing `add packagename` and you can remove them by typing `remove packagename`. Typing `status` or `st` will indicate what your current environment is comprised of. To update packages in need of updating (a `^` symbol will display next to their name), type `update`… or `up`. - -If you are editing/developing a package or using one locally, typing `develop path/to/package source/` (or `dev path/to/package/source`) will cause your environment to use that version instead of the registered one. - -Typing `instantiate` will download all the packages declared in the manifest file (if it exists) of an environment. - -For instance, PlantSimEngine has a test folder used in development. If you wanted to run tests, you would type `]` then `activate ../path/to/PlantSimEngine/test` then `instantiate` -and then you would be ready to run some scripts. - -So if you wish to use PlantSimEngine, you can enter Pkg mode (`]`), choose an environment folder, then activate that environment with `activate ../path/to/your_environment`, add PlantSimEngine to it with `add PlantSimEngine` then download the package and its dependencies with `instantiate`. - -### Companion packages - -You'll also, for most of our examples, need `PlantMeteo`. For several multi-scale simulations, you'll need `MultiScaleTreeGraph`. - -Some of the weather data examples make use of the `CSV` package, some output data is manipulated as a DataFrame, which is part of the `DataFrames` package. - -### Using the example models +```julia +using Pkg +Pkg.activate("my_simulation") +Pkg.add("PlantSimEngine") +``` -Example models are exported as a distinct submodule of PlantSimEngine, meaning they aren't part of the main API. You can use them by typing: +Most simulations also use PlantMeteo: ```julia -using PlantSimEngine.Examples +Pkg.add("PlantMeteo") ``` -## Running a test simulation - -Assuming you've setup you're environement, correctly added `PlantMeteo` and `PlantSimEngine` to that environment, and downloaded everything with `instantiate`, you'll be able to run a test example in your REPL by typing line-by-line: +## First Simulation -```@example mypkg +```@example install using PlantSimEngine, PlantMeteo, Dates using PlantSimEngine.Examples -meteo = Atmosphere(T = 20.0, Wind = 1.0, Rh = 0.65, Ri_PAR_f = 500.0) -leaf = PlantSimEngine.ModelMapping(Beer(0.5), status = (LAI = 2.0,)) -out_sim = run!(leaf, meteo) + +meteo = Atmosphere( + T=20.0, + Wind=1.0, + Rh=0.65, + Ri_PAR_f=500.0, + duration=Hour(1), +) + +scene = Scene( + Object(:leaf; scale=:Leaf, status=Status(LAI=2.0)); + applications=( + ModelSpec(Beer(0.5)) |> AppliesTo(One(scale=:Leaf)), + ), + environment=meteo, +) + +run!(scene) +only(scene_objects(scene; scale=:Leaf)).status.aPPFD ``` -## Environments in VSCode +Example models are provided by the `PlantSimEngine.Examples` submodule. They +are useful for learning and tests but are not part of the core modeling API. -There is detailed documentation explaining how to make use of Julia with VSCode with one section indicating how to handle environments in VSCode: [https://www.julia-vscode.org/docs/stable/userguide/env/](https://www.julia-vscode.org/docs/stable/userguide/env/) - \ No newline at end of file +For local package development, use `Pkg.develop(path="...")`. Run the package +tests with `Pkg.test("PlantSimEngine")`. diff --git a/docs/src/prerequisites/julia_basics.md b/docs/src/prerequisites/julia_basics.md index bb22f92ee..707ad6122 100644 --- a/docs/src/prerequisites/julia_basics.md +++ b/docs/src/prerequisites/julia_basics.md @@ -15,7 +15,7 @@ It is not meant as a full-fledged from-scratch Julia tutorial. If you are comple ## Installing packages and setting up and environment For PlantSimEngine, you can check our documentation page on the topic: -[Installing and running PlantSimEngine](@ref) +[Installing PlantSimEngine](installing_plantsimengine.md). ## Cheatsheets @@ -23,8 +23,6 @@ You can also find a few cheatsheets [here](https://palmstudio.github.io/Biophysi ## Troubleshooting -There is a documentation page showcasing some of the common errors than can occur when using PlantSimEngine, which may be worth checking if you are encountering issues: [Troubleshooting error messages](@ref). - For more Julia learning-related difficulties, you will find quick responses on the Discourse forum: [https://discourse.julialang.org](https://discourse.julialang.org). ### Noteworthy differences with other languages: @@ -52,4 +50,4 @@ Also of importance: Many of these are also briefly presented in [this Julia Data Science](https://juliadatascience.io/julia_basics) guide, which also happens to focus on the DataFrames.jl package. -Understanding more about methods, parametric types and the typing system is usually worthwhile, when working with Julia packages. \ No newline at end of file +Understanding more about methods, parametric types and the typing system is usually worthwhile, when working with Julia packages. diff --git a/docs/src/prerequisites/key_concepts.md b/docs/src/prerequisites/key_concepts.md index 93bef82e6..8cf47d2a3 100644 --- a/docs/src/prerequisites/key_concepts.md +++ b/docs/src/prerequisites/key_concepts.md @@ -1,185 +1,100 @@ # Key Concepts -You'll find a brief description of some of the main concepts and terminology related to and used in PlantSimEngine. +## Processes And Models -```@contents -Pages = ["key_concepts.md"] -Depth = 4 -``` - -## Crop models - -## FSPM - -## PlantSimEngine terminology - -This page provides a general description of the concepts and terminology used in PlantSimEngine. For a more implementation-guided description of the design and some of the terms presented here, see the [Detailed walkthrough of a simple simulation](@ref detailed-walkthrough-of-a-simple-simulation) - -!!! Note - Some terminology has different meanings in different contexts. This is particularly true of the terms organ, scale and symbol, which have a different meaning for [Multi-scale Tree Graphs](@ref) than the rest of PlantSimEngine (see [Scale/symbol terminology ambiguity](@ref) further down). Make sure to double-check those subsections, and relevant examples if you encounter issues relating to these terms. - -### Processes - -A process in this package defines a biological or physical phenomena. Think of any process happening in a system, such as light interception, photosynthesis, water, carbon and energy fluxes, growth, yield or even electricity produced by solar panels. - -See [Implementing a new process](@ref) for a brief explanation on how to declare a new process. - -### Models - -A model is a particular implementation for the simulation of a process. - -There may be different models that can be used for the same process; for instance, there are multiple hypotheses and ways of modeling photosynthesis, with different granularity and accuracy. A simple photosynthesis model might apply a simple formula and apply it to the total leaf surface, a more complex one might calculate interception and light extinction. - -!!! note - The companion package PlantBiophysics.jl provides the [`Beer`](https://vezy.github.io/PlantBiophysics.jl/stable/functions/#PlantBiophysics.Beer) structure for the implementation of the Beer-Lambert law of light extinction. The process of `light_interception` and the `Beer` model are provided as an example script in this package too at [`examples/Beer.jl`](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/master/examples/Beer.jl). - -Models can also be used for ad hoc computations that aren't directly tied to a specific literature-defined physiological process. In PlantSimEngine, everything is a model. There are many instances where a custom model might be practical to aggregate some computations or handle other information. To illustrate, XPalm, the Oil Palm model, has a few models that handle the state of different organs, and a model to handle leaf pruning, which you can find [here](https://github.com/PalmStudio/XPalm.jl/blob/main/src/plant/phytomer/leaves/leaf_pruning.jl). - -To prepare a simulation, you declare a ModelMapping with whatever models you wish to make use of and initialize necessary parameters: see the [step by step](@ref detailed-walkthrough-of-a-simple-simulation) section to learn how to use them in practice. - -For multi-scale simulations, models need to be tied to a particular scale when used. See the [Multiscale modeling](@ref) section below, or the [Multi-scale considerations](@ref) page for a more detailed description of multi-scale peculiarities. - -### Variables, inputs, outputs, and model coupling - -A model used in a simulation requires some input data and parameters, and will compute some other data which may be used by other models. Depending on what models are combined in a simulation, some variables may be inputs of some models, outputs of other models, only be part of intermediary computations, or be a user input to the whole simulation. - -Here's a conceptual model coupling; each "node" is equivalent to a distinct PlantSimEngine model, "compute()" is equivalent to the model's "run!" function: - -![Model coupling example](../www/GUID-12E2DDAD-7B20-4FE2-AA36-7FAC950382A6-low.png) -(Source: [Autodesk](https://help.autodesk.com/view/MAYAUL/2016/ENU/?guid=__files_GUID_A9070270_9B5D_4511_8012_BC948149884D_htm")) - -### Dependency graphs - -Coupling models together in this fashion creates what is known as a [Directed Acyclic Graph](https://en.wikipedia.org/wiki/Directed_acyclic_graph) or DAG, a type of [dependency graph](https://en.wikipedia.org/wiki/Dependency_graph). The order in which models are run is determined by the ordering of these models in that graph. - -![Example DAG](../www/dags_acyclic_vs_cyclic-d1a669bf1b8b6bfa8ac3041788e81171.png) -A simple Directed Acyclic Graph, note the required absence of cycles. Source: [Astronomer](https://www.astronomer.io/docs/learn/dags/) (Note: "Not Acyclic" is simply "Cyclic"). - -PlantSimEngine creates this Directed Acyclic Graph automatically by plugging the right variables in the right models. Users therefore only need to declare models, they do not need to write the code to connect them as PlantSimEngine does that work for them, as long as the model coupling has no cyclic dependency. - -### ["Hard" and "Soft" dependencies](@id hard_dependency_def) - -Linking models by setting output variables from one model as input of another model handles many typical couplings (with more situations occurring with multi-scale models and variables), but what if two models are interdependent? What if they need to iterate on some computation and pass variables back and forth? - -You can find a typical example in a companion package: [PlantBioPhysics.jl](https://github.com/VEZY/PlantBiophysics.jl). An energy balance model, the [Monteith model](https://github.com/VEZY/PlantBiophysics.jl/blob/master/src/processes/energy/Monteith.jl), needs to [iteratively run a photosynthesis model](https://github.com/VEZY/PlantBiophysics.jl/blob/c1a75f294109d52dc619f764ce51c6ca1ea897e8/src/processes/energy/Monteith.jl#L154) in its [`run!`](@ref) function. - -See the illustration below of the way these models are interdependent: +A process identifies a biological or physical phenomenon, such as +photosynthesis, growth, water balance, or energy balance. A model is one +implementation of a process. -![Example of a coupling with cycles](../www/ecophysio_coupling_diagram.png) +Models subtype `AbstractModel` and declare: -Example of a coupling with a cycle. Source: PlantBioPhysics.jl +- `inputs_`: values read from object status; +- `outputs_`: values written to object status; +- `meteo_inputs_`: values sampled from the environment; +- `meteo_outputs_`: values written to a mutable environment; +- `dep`: processes called manually by the model, when required. -Model couplings that cause simulation to flow both ways break the 'acyclic' assumption of the dependency graph. - -PlantSimEngine handles this internally by not having those "heavily-coupled" models -called **hard dependencies** from now on- be part of the main dependency graph. Instead, modelers should call these models manually from within a model. This way, they are made to be children nodes of the parent/ancestor model, which handles them internally, so they aren't tied to other nodes of the dependency graph. The resulting higher-level graph therefore only links models without any two-way interdependencies, and remains a directed graph, enabling a cohesive simulation order. The simpler couplings in that top-level graph are called "soft dependencies". - -![Hard dependency coupling visualization in PlantSimEngine](../www/PBP_dependency_graph.png) -The previous coupling, handled by PlantSimEngine - -How PlantSimEngine links these models under the hood. The red models ("hard dependencies") are not exposed in the final dependency graph, which only contains the blue "soft dependencies", and has no cycles. - -This approach does have implications when developing interdependent models: hard dependencies need to be made explicit, and the ancestor needs to call the hard dependency model's [`run!`](@ref) function explicitely in its own [`run!`](@ref) function. Hard dependency models therefore must have only one parent model. - -This reliance on another process makes these models slightly more complex to develop and validate, but keep the versatility of the implementation, as any model implementing the hard-dependency process can be passed by the user. - -Note that hard dependencies can also have their own hard dependencies, and some complex couplings can happen. - -### Weather data - -To run a simulation, we usually need the climatic/meteorological conditions measured close to the object or component. - -Users are strongly encouraged to use [`PlantMeteo.jl`](https://github.com/PalmStudio/PlantMeteo.jl), the companion package that helps manage such data, with default pre-computations and structures for efficient computations. We will make constant use of it throughout the documentation, and recommend working with it. - -The most basic data structure from this package is a type called [`Atmosphere`](https://palmstudio.github.io/PlantMeteo.jl/stable/#PlantMeteo.Atmosphere), which defines steady-state atmospheric conditions, *i.e.* the conditions are considered at equilibrium. Another structure is available to define different consecutive time-steps: [`TimeStepTable`](https://palmstudio.github.io/PlantMeteo.jl/stable/#PlantMeteo.TimeStepTable). - -The mandatory variables to provide for an [`Atmosphere`](https://palmstudio.github.io/PlantMeteo.jl/stable/#PlantMeteo.Atmosphere) are: `T` (air temperature in °C), `Rh` (relative humidity, 0-1) and `Wind` (the wind speed, m s⁻¹). - -In the example below, we also pass in the -optional- incoming photosynthetically active radiation flux (`Ri_PAR_f`, W m⁻²). We can declare such conditions like so: - -```@example usepkg -using PlantMeteo -meteo = Atmosphere(T = 20.0, Wind = 1.0, Rh = 0.65, Ri_PAR_f = 500.0) -``` - -More details are available from the [package documentation](https://vezy.github.io/PlantMeteo.jl/stable). If you do not wish to make use of this package, you can alternately provide your own data, as long as it respects the [Tables.jl interface](https://tables.juliadata.org/stable/#Implementing-the-Interface-(i.e.-becoming-a-Tables.jl-source)) (*e.g.* use a `DataFrame`). - -If you wish to make use of more fine-grained weather data, it will likely require more advanced model creation and MTG manipulation, and more involved work on the modeling side. - -### Organ/Scale - -Plants have different organs with distinct physiological properties and processes. When doing more fine-grained simulations of plant growth, many models will be tied to a particular organ of a plant. Models handling flowering state or root water absorption are such examples. Others, such as carbon allocation and demand, might be reused in slightly different ways for multiple organs of the plant. - -PlantSimEngine documentation tends to use the terms "organ" and "scale" mostly interchangeably. "Scale" is a bit more general and accurate, since some models might not operate at a specific organ level, but (for example) at the scene level, so a "Scene" scale might be present in the MTG, and in the user-provided data. - -When working with multi-scale data, the scale will often need to be specified to map variables, or to indicate at what scale level models work out. You will see some code resembling this : +The numerical kernel is implemented with: ```julia -:Root => (RootGrowthModel(), OrganAgeModel()), -:Leaf => (LightInterceptionModel(), OrganAgeModel()), -:Plant => (TotalBiomassModel(),), +PlantSimEngine.run!(model, models, status, meteo, constants, extra) ``` -This example excerpt links from specific models to a specific scale. Note that one model is reused at two different scales, and note that `:Plant` isn't an actual organ, hence the preferred usage of the term "scale". - -### Multiscale modeling - -Multi-scale modeling is the process of simulating a system at multiple levels of detail simultaneously. Some models might run at the organ scale while others run at the plot scale. Each model can access variables at its scale and other scales if needed, allowing for a more comprehensive system representation. It can also help identify emergent properties that are not apparent at a single level of detail. - -For example, a model of photosynthesis at the leaf scale can be combined with a model of carbon allocation at the plant scale to simulate the growth and development of the plant. Another example is a combination of models to simulate the energy balance of a forest. To simulate it, you need a model for each organ type of the plant, another for the soil, and finally, one at the plot scale, integrating all others. - -When running multi-scale simulations which contain models operating at different organ levels for the plant, extra information needs to be provided by the user to run models. Since some models are reused at different organ levels, it is necessary to indicate which organ level a model operates at. - -This is why multi-scale simulations make use of a 'mapping' in the `ModelMapping`, as well as links between models/variables in different scales, *e.g.* if an input variable comes from another scale, it is required to indicate which scale it is mapped from. - -You can read more about some practical differences as a user between single- and multi-scale simulations here: [Multi-scale considerations](@ref). - -### Multi-scale Tree Graphs - -![Grassy plant and equivalent MTG](../www/Grassy_plant_MTG_vertical.svg) +## Scenes And Objects -A Grassy plant and its equivalent MTG +A `Scene` contains objects and model applications. An `Object` can represent a +scene, plant, soil volume, axis, internode, leaf, sensor, or any other simulated +entity. PlantSimEngine does not impose one plant architecture. -Multi-scale Tree Graphs (MTG) are a data structure used to represent plants. A more detailed introduction to the format and its attributes can be found [in the MultiScaleTreeGraph.jl package documentation](https://vezy.github.io/MultiScaleTreeGraph.jl/stable/the_mtg/mtg_concept/). +Objects can carry: -Multi-scale simulations can operate on MTG objects; new nodes are added corresponding to new organs created during the plant's growth. +- a stable identifier; +- scale, kind, species, and name metadata; +- parent-child relationships; +- geometry and position; +- mutable `Status`; +- object-local model applications. -You can see a basic display of an MTG by simply typing its name in the REPL: +`ObjectTemplate` packages reusable applications for a species or object type. +`ObjectInstance` mounts the template in a scene. Several instances can share +models and parameters while declaring targeted overrides for exceptional +objects. -![example display of an MTG in PlantSimEngine](../www/MTG_output.png) +## Model Applications -!!! note - Another companion package, [PlantGeom.jl](https://github.com/VEZY/PlantGeom.jl), can also create MTG objects from .opf files (corresponding to the [Open Plant Format](https://amap-dev.cirad.fr/projects/xplo/wiki/The_opf_format_(*opf)), an alternate means of describing plants computationally). +`ModelSpec` configures one use of a model: -#### Scale/symbol terminology ambiguity +- `AppliesTo(...)` selects target objects; +- `Inputs(...)` selects producers for value dependencies; +- `Calls(...)` binds manually controlled model calls; +- `TimeStep(...)` selects the execution cadence; +- `Environment(...)` configures environment sampling; +- `Updates(...)` orders intentional additional writers; +- `OutputRouting(...)` controls output publication. -Multi-scale tree graphs have different terminology (see [Organ/Scale](@ref)): +This keeps model implementations generic. Models do not need to know which +scene, object, timestep, or coupling scenario will use them. -- the MTG node **symbol** represents "something" like a `:Plant`, `:Root`, `:Scene` or `:Leaf`. It corresponds to a PlantSimEngine *scale* and has nothing to do with the Julia programming language's definition of symbol (*e.g.* `:var`) -- the MTG node **scale**, is an integer passed to the Node constructor, and describes the level of description of the tree graph object. They don't always have a one-to-one correspondence to the symbol (or PlantSimEngine's scale), but are similar. +## Soft And Manual Dependencies -![Three scale levels on an MTG, which differ from typical PlantSimEngine concept of scale](../www/Grassy_plant_scales.svg) +Ordinary dependencies are inferred by matching model inputs with outputs and +are compiled into an acyclic execution order. `Inputs(...)` is used when the +source is cross-object, renamed, temporal, or otherwise ambiguous. -You can find a brief description of the MTG concepts [here](https://vezy.github.io/MultiScaleTreeGraph.jl/stable/the_mtg/mtg_concept/#Node-MTG-and-attributes). +Some algorithms need direct call-stack control. For example, a scene energy +balance may repeatedly call leaf energy-balance models until canopy +microclimate converges. Such dependencies are bound with `Calls(...)`; the +parent invokes them with `run_call!`. -Other words are unfortunately reused in various contexts with different meanings: tree/leaf/root have a different meaning when talking about computer science data structure (*e.g.*, graphs, dependency graphs and trees). +## Status And References -!!! note - In the majority of cases, you can assume the tree-related terminology refers to the biological terms, and that "organ" refer to plant organs, and "single-scale", "multi-scale" and "scale" to PlantSimEngine's concept of scales described in [Organ/Scale](@ref). MTG objects are mostly manipulated on a per-node basis (the graph node, not the botanical node), unless a model makes use of functions relating to MTG traversal, in which case you may expect computer science terminology. +`Status` stores variables in references. Same-rate coupling normally shares +those references instead of copying values. A many-object input uses a +reference vector, so aggregation models read current source values directly. -#### TLDR +Temporal coupling uses published streams when producer and consumer clocks +differ. Policies include `HoldLast`, `Interpolate`, `Integrate`, `Aggregate`, +and `PreviousTimeStep`. -In summary: +## Environment -- In PlantSimEngine a scale is a level of description defined by a name (`String`). In MTG, a scale is an integer describing the level of description of the node, and a symbol is a name for that node. So symbol in the MTG == scale in PlantSimEngine; -- The word "node" is always used to refer to the Multiscale Tree Graph node, not the botanical node. +The active environment backend may be: -### State machines +- one constant atmosphere shared by all objects; +- a time-indexed weather table; +- a mutable layer, voxel, grid, or octree microclimate. -A state machine is a computational concept used to model mechanisms and devices, which may be of interest for your simulations. +Object-to-environment support is compiled and cached. Geometry changes mark the +binding dirty so it can be refreshed without recomputing spatial lookup at +every timestep. -![State machine image](../www/Turnstile_state_machine_colored.svg.png) -A simple state machine. See the [wikipedia page](https://en.wikipedia.org/wiki/Finite-state_machine) for more examples. +## Multiscale Plant Structure -State machines can be useful to model organ state: some organs in [XPalm.jl](https://github.com/PalmStudio/XPalm.jl), a package modelling the oil palm using PlantSimEngine, have a `state` variable behaving like a state machine, indicating whether an organ is mature, pruned, flowering, etc. +PlantSimEngine treats scale and object hierarchy as scenario data. A plant may +use leaves directly under a plant, or axes, segments, internodes, roots, and +other intermediate levels. Selectors express relationships such as one source, +many descendants, the current plant, an ancestor, or all matching objects in +the scene. -You can find an example model (amongst other such models) affecting the `state` variable of some organs depending on their age and thermal time in the XPalm oil palm FSPM [here](https://github.com/PalmStudio/XPalm.jl/blob/main/src/plant/phytomer/phytomer/state.jl). +MultiScaleTreeGraph objects can be imported with `objects_from_mtg`, but the +runtime operates on the same scene/object representation afterward. diff --git a/docs/src/scene_object/quickstart.md b/docs/src/scene_object/quickstart.md index 90c810f27..708039bad 100644 --- a/docs/src/scene_object/quickstart.md +++ b/docs/src/scene_object/quickstart.md @@ -17,9 +17,7 @@ TimeStep Environment ``` -Legacy `ModelMapping`, `MultiScaleModel`, domain, and route APIs are retained -as compatibility tools while old examples are migrated. New scenarios should -start from `Scene` and model applications. +Scenarios are defined with `Scene` and model applications. ```@setup scene_object_quickstart using PlantSimEngine, PlantMeteo, Dates, DataFrames @@ -245,7 +243,7 @@ accepted state should use `publish=true`. ## Next Steps - [Migrating To The Scene/Object API](../migration_scene_object.md) translates - old `ModelMapping`, `MultiScaleModel`, domain, and route scenarios. + scenarios written with removed APIs. - [Public API](../API/API_public.md) lists constructors, selectors, lifecycle helpers, environment helpers, and explanation helpers. - [Model traits](../model_traits.md) documents `inputs_`, `outputs_`, `dep`, diff --git a/docs/src/step_by_step/advanced_coupling.md b/docs/src/step_by_step/advanced_coupling.md index 98701ee34..4fd10e381 100644 --- a/docs/src/step_by_step/advanced_coupling.md +++ b/docs/src/step_by_step/advanced_coupling.md @@ -147,8 +147,5 @@ The MAESPA-style example uses the same mechanism: a scene energy-balance model calls all selected leaf energy-balance models and the shared soil model while it solves canopy microclimate. -Older code may still show `dep(model)` with hard dependencies and -`PlantSimEngine.ModelMapping(...)`. Those mechanisms are compatibility -implementation details for old examples. New scenario wiring should use -`Calls(...)`; model authors should keep kernels generic and only require manual -calls when the model really needs call-stack control. +Scenario wiring uses `Calls(...)`. Model authors should keep kernels generic +and only require manual calls when the model really needs call-stack control. diff --git a/docs/src/step_by_step/detailed_first_example.md b/docs/src/step_by_step/detailed_first_example.md index ec5b940a9..1af466624 100644 --- a/docs/src/step_by_step/detailed_first_example.md +++ b/docs/src/step_by_step/detailed_first_example.md @@ -28,7 +28,7 @@ Depth = 3 Every script needs a Julia environment with PlantSimEngine installed. Most examples also use companion packages such as PlantMeteo for weather data and DataFrames for tabular outputs. Installation details are in -[Installing and running PlantSimEngine](@ref). +[Installing PlantSimEngine](../prerequisites/installing_plantsimengine.md). ## The Simulation Pieces @@ -240,16 +240,14 @@ catch err end ``` -## Compatibility Note +## Migration Note -Older tutorials used `PlantSimEngine.ModelMapping(...)` for single-scale -simulations. That compatibility API remains available for historical examples -and regression tests, but new simulations should use `Scene`, `Object`, -`ModelSpec`, `AppliesTo`, `Inputs`, `Calls`, `Updates`, `TimeStep`, and -`Environment`. +The previous mapping runtime has been removed. Simulations use `Scene`, +`Object`, `ModelSpec`, `AppliesTo`, `Inputs`, `Calls`, `Updates`, `TimeStep`, +and `Environment`. See [Migrating To The Scene/Object API](../migration_scene_object.md) for the -translation from old mapping and domain constructs. +translation from the historical mapping API. ## Next Steps diff --git a/docs/src/step_by_step/implement_a_model.md b/docs/src/step_by_step/implement_a_model.md index 2478f25b6..1982db3a9 100644 --- a/docs/src/step_by_step/implement_a_model.md +++ b/docs/src/step_by_step/implement_a_model.md @@ -49,13 +49,6 @@ function run!(::Beer, models, status, meteo, constants, extras) end ``` -Determine if parallelization is possible, and which traits to declare : - -```@example usepkg -PlantSimEngine.ObjectDependencyTrait(::Type{<:Beer}) = PlantSimEngine.IsObjectIndependent() -PlantSimEngine.TimeStepDependencyTrait(::Type{<:Beer}) = PlantSimEngine.IsTimeStepIndependent() -``` - And that is all you need to get going, for this example with a single parameter and no interdependencies. The [`@process`](@ref) macro does some boilerplate work described [here](@ref under_the_hood) @@ -180,20 +173,20 @@ function run!(::Beer, models, status, meteo, constants, extras) ``` - the model's type -- models: the process-keyed model bundle passed by the runtime. In legacy - mapping simulations this comes from `PlantSimEngine.ModelMapping`; in - scene/object simulations it is compiled from the model application and its +- models: the process-keyed model bundle compiled from the application and its `Calls(...)`. - status: a [`Status`](@ref) object, which contains the current values (*i.e.* state) of the variables for **one** time-step (e.g. the value of the plant LAI at time t) - meteo: (usually) an `Atmosphere` object, or a row of the meteorological data, which contains the current values of the meteorological variables for **one** time-step (*e.g.* the value of the PAR at time t) - constants: a `Constants` object, or a `NamedTuple`, which contains the values of the constants for the simulation (*e.g.* the value of the Stefan-Boltzmann constant, unit-conversion constants...) - extras: any other object you want to pass to your model, mostly for advanced usage, not detailed here -A typical [`run!`](@ref) function can therefore make use of simulation constants, input/output variables accessible through the [`Status`](@ref object, or weather data. +A typical [`run!`](@ref) function can therefore use simulation constants, +input/output variables accessible through the [`Status`](@ref) object, or +weather data. Here is the [`run!`](@ref) implementation of the light interception model. Note that the input and output variables are accessed through the -[`status`](@ref) argument: +`status` argument: ```@example usepkg function run!(::Beer, models, status, meteo, constants, extras) @@ -216,28 +209,10 @@ by the process name, then the parameter name. For example, the `k` parameter of the `Beer` model is found in `models.light_interception.k`. !!! warning - You need to import all the functions you want to extend, so Julia knows your intention of adding a method to the function from PlantSimEngine, and not defining your own function. To do so, you have to prefix the said functions by the package name, or import them before *e.g.*: `import PlantSimEngine: inputs_, outputs_`. The troubleshooting subsection [Implementing a model: forgetting to import or prefix functions](@ref) showcases output errors that can occur when you forget to prefix. - -### Parallelization traits - -`PlantSimEngine` defines traits to get additional information about the models. At the moment, there are two traits implemented that help the package to know if a model can be run in parallel over space (*i.e.* objects) and/or time (*i.e.* time-steps). - -By default, all models are assumed to be **not** parallelizable over objects and time-steps, because it is the safest default. If your model is parallelizable, you should add the trait to the model. - -For example, if we want to add the trait for parallelization over objects to our `Beer` model, we would do: - -```@example usepkg -PlantSimEngine.ObjectDependencyTrait(::Type{<:Beer}) = PlantSimEngine.IsObjectIndependent() -``` - -And if we want to add the trait for parallelization over time-steps to our `Beer` model, we would do: - -```@example usepkg -PlantSimEngine.TimeStepDependencyTrait(::Type{<:Beer}) = PlantSimEngine.IsTimeStepIndependent() -``` - -!!! note - A model is parallelizable over objects if it does not call another model directly inside its code. Similarly, a model is parallelizable over time-steps if it does not get values from other time-steps directly inside its code. In practice, most of the models are parallelizable one way or another, but it is safer to assume they are not. + Prefix functions you extend with `PlantSimEngine.`, or import them first, + for example `import PlantSimEngine: inputs_, outputs_`. Otherwise Julia + defines an unrelated function in your module instead of adding a method to + PlantSimEngine's function. OK that's it! We now a full new model implementation for the light interception process! Other models might be more complex in terms of what computations they do, or how they couple with other models, but the approach remains the same. diff --git a/docs/src/step_by_step/model_switching.md b/docs/src/step_by_step/model_switching.md index bafe3fc30..5af6b3118 100644 --- a/docs/src/step_by_step/model_switching.md +++ b/docs/src/step_by_step/model_switching.md @@ -105,6 +105,5 @@ implementation by replacing one `ModelSpec` or by using an `ObjectInstance(...; overrides=...)` when the change applies to one plant instance or one organ. -Older tutorials showed the same idea with `PlantSimEngine.ModelMapping(...)`. -That spelling remains available for compatibility code, but new scenario code -should express model switching through scene model applications. +The removed mapping runtime expressed the same idea differently. New scenario +code expresses model switching through scene model applications. diff --git a/docs/src/step_by_step/parallelization.md b/docs/src/step_by_step/parallelization.md deleted file mode 100644 index 6c590059b..000000000 --- a/docs/src/step_by_step/parallelization.md +++ /dev/null @@ -1,39 +0,0 @@ -## Parallel execution - -!!! note - This page is likely to change and become outdated. In any case, parallel execution only currently applies to single-scale simulations (multi-scale simulations' changing MTGs and extra complexity don't allow for straightforward parallelisation) - -### FLoops - -`PlantSimEngine.jl` uses the [`Floops`](https://juliafolds.github.io/FLoops.jl/stable/) package to run the simulation in sequential, parallel (multi-threaded) or distributed (multi-process) computations over objects, time-steps and independent processes. - -That means that you can provide any compatible executor to the `executor` argument of [`run!`](@ref). By default, [`run!`](@ref) uses the [`ThreadedEx`](https://juliafolds.github.io/FLoops.jl/stable/reference/api/#executor) executor, which is a multi-threaded executor. You can also use the [`SequentialEx`](https://juliafolds.github.io/Transducers.jl/dev/reference/manual/#Transducers.SequentialEx)for sequential execution (non-parallel), or [`DistributedEx`](https://juliafolds.github.io/Transducers.jl/dev/reference/manual/#Transducers.DistributedEx) for distributed computations. - -### Parallel traits - -`PlantSimEngine.jl` uses [Holy traits](https://invenia.github.io/blog/2019/11/06/julialang-features-part-2/) to define if a model can be run in parallel. -See also [Model traits](../model_traits.md) for a full inventory of model-level traits. - -!!! note - A model is executable in parallel over time-steps if it does not uses or set values from other time-steps, and over objects if it does not uses or set values from other objects. - -You can define a model as executable in parallel by defining the traits for time-steps and objects. For example, the ToyLAIModel model from the [examples folder](https://github.com/VirtualPlantLab/PlantSimEngine.jl/tree/main/examples) can be run in parallel over time-steps and objects, so it defines the following traits: - -```julia -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyLAIModel}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyLAIModel}) = PlantSimEngine.IsObjectIndependent() -``` - -By default all models are considered not executable in parallel, because it is the safest option to avoid bugs that are difficult to catch, so you only need to define these traits if it is executable in parallel for them. - -!!! tip - A model that is defined executable in parallel will not necessarily will. First, the user has to pass a parallel `executor` to [`run!`](@ref) (*e.g.* `ThreadedEx`). Second, if the model is coupled with another model that is not executable in parallel, `PlantSimEngine` will run all models in sequential. - -### Further executors - -You can also take a look at [FoldsThreads.jl](https://github.com/JuliaFolds/FoldsThreads.jl) for extra thread-based executors, [FoldsDagger.jl](https://github.com/JuliaFolds/FoldsDagger.jl) for -Transducers.jl-compatible parallel fold implemented using the Dagger.jl framework, and soon [FoldsCUDA.jl](https://github.com/JuliaFolds/FoldsCUDA.jl) for GPU computations -(see [this issue](https://github.com/VirtualPlantLab/PlantSimEngine.jl/issues/22)) and [FoldsKernelAbstractions.jl](https://github.com/JuliaFolds/FoldsKernelAbstractions.jl). You can also take a look at -[ParallelMagics.jl](https://github.com/JuliaFolds/ParallelMagics.jl) to check if automatic parallelization is possible. - -Finally, you can take a look into [Transducers.jl's documentation](https://github.com/JuliaFolds/Transducers.jl) for more information, for example if you don't know what is an executor, you can look into [this explanation](https://juliafolds.github.io/Transducers.jl/stable/explanation/glossary/#glossary-executor). diff --git a/docs/src/step_by_step/quick_and_dirty_examples.md b/docs/src/step_by_step/quick_and_dirty_examples.md index 3e0965d86..a9dede486 100644 --- a/docs/src/step_by_step/quick_and_dirty_examples.md +++ b/docs/src/step_by_step/quick_and_dirty_examples.md @@ -150,9 +150,8 @@ for the accepted solution. See [MAESPA-style scene example handoff](../dev/maespa_scene_handoff.md) for the current multi-plant energy-balance acceptance example. -## Compatibility Note +## Migration Note -Older examples used `PlantSimEngine.ModelMapping(...)`. That compatibility -API remains available for historical material and regression tests, but new -simulations should start from `Scene`, `Object`, `ModelSpec`, `AppliesTo`, -`Inputs`, `Calls`, `Updates`, `TimeStep`, and `Environment`. +The previous mapping runtime has been removed. Simulations start from `Scene`, +`Object`, `ModelSpec`, `AppliesTo`, `Inputs`, `Calls`, `Updates`, `TimeStep`, +and `Environment`. diff --git a/docs/src/step_by_step/simple_model_coupling.md b/docs/src/step_by_step/simple_model_coupling.md index 0a11bd6a3..c24ca16f1 100644 --- a/docs/src/step_by_step/simple_model_coupling.md +++ b/docs/src/step_by_step/simple_model_coupling.md @@ -19,7 +19,8 @@ applications on objects, and the compiler wires the value dependencies. Make sure you have a working Julia environment with PlantSimEngine and the recommended companion packages. Details are provided on the -[Installing and running PlantSimEngine](@ref) page. +[Installing PlantSimEngine](../prerequisites/installing_plantsimengine.md) +page. ## One object and one model @@ -125,7 +126,6 @@ growth_status = only(scene_objects(growth_scene; scale=:Scene)).status (LAI=growth_status.LAI, aPPFD=growth_status.aPPFD, biomass=growth_status.biomass) ``` -Older examples used `PlantSimEngine.ModelMapping(...)` for this workflow. That -constructor is retained as a qualified compatibility API, but new scenarios -should start from `Scene`, `Object`, `ModelSpec`, `AppliesTo`, `Inputs`, +Older examples used the removed mapping runtime for this workflow. New +scenarios start from `Scene`, `Object`, `ModelSpec`, `AppliesTo`, `Inputs`, `Calls`, and `TimeStep`. diff --git a/docs/src/troubleshooting_and_testing/implicit_contracts.md b/docs/src/troubleshooting_and_testing/implicit_contracts.md deleted file mode 100644 index 1e1067f4c..000000000 --- a/docs/src/troubleshooting_and_testing/implicit_contracts.md +++ /dev/null @@ -1,96 +0,0 @@ -This page summarizes some of the assumptions, coupling constraints and inner workings of PlantSimEngine which may be particular relevant when implementing new models. - -If you are unsure of an implementation subtlety, check this page out to see whether it answers your question. - -```@contents -Pages = ["implicit_contracts.md"] -Depth = 2 -``` - -## Weather data provides the simulation timestep, but models can veer away from it - -The weather data timesteps, whether hourly or daily, provide the pace at which most other models run. - -In XPalm, weather data for most models is provided daily, meaning biomass calculations are also provided daily. - -Many models are considered to be steady-state over that timeframe, but not all : the leaf pruning model pertubes the plant in a non-steady state fashion, for example. Models that require computations over several iterations to stabilise (often part of hard dependencies) might also have a timestep unrelated to the weather data. - -!!! Note - Implicitely, this means any vector variables given as input to the simulation must be consistent with the number of weather timesteps. Providing one weather value but a larger vector variable is an exception : the weather data is replicated over each timestep. (This may be subject to change in the future when support for different timesteps in a single simulation is implemented) - -## Why does my model skip half-hour rows? - -If your meteo has 30-minute rows but a model appears to run hourly, check timestep resolution order: - -1. If model has explicit `PlantSimEngine.TimeStepModel(...)`, it is used. -2. Else if model `timespec(model)` is non-default, it is used. -3. Else model uses meteo `duration`. - -Then compatibility rules apply: - -1. `timestep_hint.required` is enforced for meteo-derived clocks. -2. `timestep_hint.preferred` is informational only. -3. Meteo aggregation/integration happens only for models with coarser effective clocks. - -Common cause: -- model has explicit hourly `PlantSimEngine.TimeStepModel(...)`, so 30-minute rows are intentionally aggregated to hourly runs. - -Quick diagnostics: -- Run `explain_model_specs(mapping_or_sim)` to see, per process, whether runtime clock comes from explicit `ModelSpec`, model `timespec`, or meteo base step. -- Ensure meteo `duration` is present and valid on every row (mandatory when meteo is provided). - -## Weather data must be interpolated prior to simulation - -If your weather data isn't adjusted to conform to a regular timestep, you will need to adjust it to fit that constraint. PlantSimEngine does no interpolation prior to simulation and expects regular weather timesteps. - -## No cyclic dependencies in the simplified dependency graph - -The model dependency graph used for running the simulation is comprised of soft and hard dependency nodes, and the final version only links soft dependency nodes together, and is expected to contain no cycles. - -Any user model coupling which causes a cyclic dependency to occur will require some extra tinkering to run : either design models differently, create a hard dependency with some of the problematic models, or break the cycle by having a variable take the previous timestep's value as input. - -See [Dependency graphs](@ref) and the following subsections for more discussion related to dependency graph constraints. - -Note : Only the previous timestep is accessible in PlantSimEngine without any kind of dedicated model. How to create a model to store more past timesteps of a specific variable is described in the [Tips and workarounds](@ref) page: [Making use of past states in multi-scale simulations](@ref) - -## Hard dependencies need to be declared in the model definition - -Hard dependencies are handled internally by their owning soft dependency model, ie the hard dep's run! function is directly called by the soft dependency's run!. - -The current way in which PlantSimEngine creates its dependency graph requires users to declare what process is required in the hard dependency and which scale it pulls the model and its variables from. - -## Parallelisation opportunities must be part of the model definition - -Traits that indicate that a model is independent or objects need to be part of the model definition. Modelers need to keep this in mind when implementing new models. - -This is currently mostly a concern for single-scale simulations, as multi-scale simulations are not currently parallelised ; a more involved scheduler would need to be implemented when MTGs are modified by models, and to handle more interesting parallelisation opportunities at specific scales. - -There may be new parallelisation features for multi-plant simulations further down the road. - -## Hard dependencies can only have one parent in the dependency graph - -The final dependency graph is comprised only of soft dependency nodes, and is guaranteed to contain no cycles. Hard dependencies are handled internally by their soft dependency ancestor. To avoid any ambiguity in terms of processing order, only one soft dependency node can 'own' a hard dependency And similarly, nested hard dependencies only have a single soft dependency ancestor. - -This is not solely an implementation detail of PlantSimEngine's internal mechanisms ; if your simulation requires complex coupling, you might need to carefully consider how to manage your hard dependencies, or insert an extra intermediate model to simplify things. - -## A model can only be used once per scale - -Similarly, to avoid depedency graph ambiguity (and for simulation cohesion), PlantSimEngine currently assumes a model describing a process only occurs once per scale. - -Model renaming and duplicating works around this assumption. It may change once multi-plant/multi-species features are implemented. - -## No two variables with the same name at the same scale - -This rule avoids potential ambiguity which could then cause both problems in terms of model ordering during the simulation, as well as incorrectly coupling models with the wrong variable. - -A workaround for some of the situations where this occurs is described here : [Having a variable simultaneously as input and output of a model](@ref) - -## Simulation order instability when adding models - -An important aspect to bear in mind is that PlantSimEngine automatically determines an order in which models are run from the dependency graph it generates by coupling models together. - -This order of simulation depends on the way the models link together. If you replace a model by a new set of models, or pass in new variables that create new links between models, you may change the simulation order. - -When iterating and slowly making a simulation more physiologically realistic and complex, it is therefore fully possible that the order in which two models are run is flipped by a user change. - -This design choice implementation -a concession made for ease of use and flexibility when developing a simulation- means that until your set of models is fully stabilized and you know which variables are `PreviousTimestep` and what order models run in, as you expand and change the set you might see differences of execution of one timestep for some models. It isn't a conceptual problem as most models are steady-state, and simulation order is stable for a given set of models, but it does mean PlantSimEngine will be less conveient for some types of simulation. diff --git a/docs/src/troubleshooting_and_testing/plantsimengine_and_julia_troubleshooting.md b/docs/src/troubleshooting_and_testing/plantsimengine_and_julia_troubleshooting.md deleted file mode 100644 index 406daa0dd..000000000 --- a/docs/src/troubleshooting_and_testing/plantsimengine_and_julia_troubleshooting.md +++ /dev/null @@ -1,515 +0,0 @@ -# Troubleshooting error messages - -PlantSimEngine attempts to be as comfortable and easy to use as possible for the user, and many kinds of user error will be caught and explanations provided to resolve them, but there are still blind spots, as well as syntax errors that will often generate a Julia error (which can be less intuitive to decrypt) rather than a PlantSimEngine error. - -To help people newer to Julia with troubleshooting, here are a few common 'easy-to-make' mistakes with the current API that might not be obvious to interpret, and pointers on how to fix them. - -They are listed by 'nature of error', rather than by error message, so you may need to search the page to find your specific error. - -If you need more help to decode Julia errors, you can find help on the [Julia Discourse forums](https://discourse.julialang.org). -If you need some advice on the FSPM side, the research community has [its own discourse forum](https://fspm.discourse.group). - -If the issue seems PlantSimEngine-related, or you have questions regarding modeling or have suggestions, you can also [file an issue](https://github.com/VirtualPlantLab/PlantSimEngine.jl/issues) on Github. - -```@contents -Pages = ["plantsimengine_and_julia_troubleshooting.md"] -Depth = 3 -``` - -## Tips and workflow - -Some errors are very specific as to their cause, and the PlantSimEngine errors tend to be explicit about which parameter / variable / organ is causing the error, helping narrow down its origin. - -Some generic-looking errors usually do contain some extra information to help focus the debugging hunt. For instance, a dispatch failure on run! caused by some issue with args/kwargs may highlight explicitely indicate which arguments are currently causing conflict. In VSCode, such arguments are highlighted in red (the first and last arguments in the example below): - -```julia -a = 1 -run!(a, simple_mtg, mapping, meteo_day, a) - -ERROR: MethodError: no method matching run!(::Int64, ::Node{NodeMTG, Dict{…}}, ::Dict{String, Tuple{…}}, ::DataFrame, ::Int64) -The function [`run!`](@ref) exists, but no method is defined for this combination of argument types. - -Closest candidates are: - run!(::ToyPlantLeafSurfaceModel, ::Any, ::Any, ::Any, ::Any, ::Any) - @ PlantSimEngine /PlantSimEngine/examples/ToyLeafSurfaceModel.jl:75 - ... -``` - -If you wish to search for a specific error in the current page, copy the part of the description that is not specific to your script, and Ctrl+F it here. In the above example, the generic part would be : -```julia -ERROR: MethodError: no method matching -``` - -## Common Julia errors - -### NamedTuples with a single value require a comma : - -This one is easy to miss. - -Empty NamedTuple objects are initialised with x = NamedTuple(). Ones with more than one variable can be initialised like this : -```julia -a = (var1 = 0, var2 = 0) -``` -or like this : -```julia -a = (var1 = 0, var2 = 0,) -``` -The second comma being optional. - -However, if there is only a single variable, notation has to be : -```julia -a = (var1 = 0,) -``` -The comma is compulsory. If it is forgotten : -```julia -a = (var1 = 0) -``` -the line will be interpreted as setting the variable a to the value var1 is set to, hence a will be an Int64 of value 0. - -This is a liability when writing custom models as some functions work with NamedTuples : -```julia -function PlantSimEngine.inputs_(::HardDepSameScaleAvalModel) - (e2 = -Inf,) -end -``` - -The error returned will likely be a Julia error along the lines of : -```julia -[ERROR: MethodError: no method matching merge(::Float64, ::@NamedTuple{g::Float64}) - -Closest candidates are: -merge(::NamedTuple{()}, ::NamedTuple) -@ Base namedtuple.jl:337 -merge(::NamedTuple{an}, ::NamedTuple{bn}) where {an, bn} -@ Base namedtuple.jl:324 -merge(::NamedTuple, ::NamedTuple, NamedTuple...) -@ Base namedtuple.jl:343 - -Stacktrace: -[1] variables_multiscale(node::PlantSimEngine.HardDependencyNode{…}, organ::String, vars_mapping::Dict{…}, st::@NamedTuple{}) -... -``` -It is sometimes properly detected and explained on PlantSimEngine's side (when passing in tracked_outputs, for instance), but may also occur when declaring statuses. - -### Incorrectly declaring empty inputs or outputs - -The syntax for an empty NamedTuple is `NamedTuple()`. If instead one types `()` or `(,)`an error returned respectively by PlantSimEngine or Julia will be returned. - -## PlantSimEngine user errors - -Most of the following errors occur exclusively in multi-scale simulations, which has a slightly more complex API, but some are common to both single- and multi-scale simulations. - -### ModelMapping: providing a type name instead of a constructed instance - -```julia -m = PlantSimEngine.ModelMapping(day=MyToyModel, week=MyToyModel2) -``` -This line is incorrect and will return -```julia -MethodError: no method matching inputs_(::Type{MyToyDayModel}) -``` - -The correct syntax is (assuming the corresponding constructor exists) : -```julia -m = PlantSimEngine.ModelMapping(day=MyToyModel(), week=MyToyModel2()) -``` - -### Implementing a model: forgetting to import or prefix functions - -When implementing a model, you need to make sure that your implementation is correctly recognised as extending `PlantSimEngine` methods and types, and not writing new independent ones. - -In the following working toy model implementation, note that the `inputs_`, `outputs_` and [`run!`](@ref) function are all prefixed with the module name. If there were hard dependencies to manage, the [`dep`](@ref) function would also be identically prefixed. - -```julia -using PlantSimEngine -@process "toy" verbose = false - -struct ToyToyModel{T} <: AbstractToyModel - internal_constant::T -end - -function PlantSimEngine.inputs_(::ToyToyModel) - (a = -Inf, b = -Inf, c = -Inf) -end - -function PlantSimEngine.outputs_(::ToyToyModel) - (d = -Inf, e = -Inf) -end - - -function PlantSimEngine.run!(m::ToyToyModel, models, status, meteo, constants=nothing, extra_args=nothing) - status.d = m.internal_constant * status.a - status.e += m.internal_constant -end - -meteo = Weather([ - Atmosphere(T=20.0, Wind=1.0, Rh=0.65, Ri_PAR_f=200.0), - Atmosphere(T=20.0, Wind=1.0, Rh=0.65, Ri_PAR_f=200.0), - Atmosphere(T=18.0, Wind=1.0, Rh=0.65, Ri_PAR_f=100.0), -]) - -model = PlantSimEngine.ModelMapping( - ToyToyModel(1), - status = ( a = 1, b = 0, c = 0), -) -to_initialize(model) -sim = PlantSimEngine.run!(model, meteo) -``` - -If you declare these functions without importing them first, or prefixing them with the module name, they will be considered to be part of your current environment, and won't be extending PlantSimEngine methods, which means PlantSimEngine will not be able to properly make use of your functions, and simulations are likely to error, or run incorrectly. - -Forgetting to prefix the [`run!`](@ref) function definition gives the following error : -```julia -ERROR: MethodError: no method matching run!(::ModelMapping{...}, ::TimeStepTable{Atmosphere{…}}) -The function [`run!`](@ref) exists, but no method is defined for this combination of argument types. - -Closest candidates are: - run!(::ToyToyModel, ::Any, ::Any, ::Any, ::Any, ::Any) - @ Main ~/path/to/file.jl:20 -``` - -Forgetting to prefix the `inputs_`or `outputs_` functions for your model might not always generate an error, depending on whether the variables declared in this function are present in your mapping's corresponding Status. - -In cases where they do throw an error, you may get the following kind of output: -```julia -ERROR: type NamedTuple has no field d -Stacktrace: - [1] setproperty!(mnt::Status{(:a, :b, :c), Tuple{…}}, s::Symbol, x::Int64) - @ PlantSimEngine ~/path/to/package/PlantSimEngine/src/component_models/Status.jl:100 - [2] run!(m::ToyToyModel{…}, models::@NamedTuple{…}, status::Status{…}, meteo::PlantMeteo.TimeStepRow{…}, constants::Constants{…}, extra_args::Nothing) - ... -``` - -!!! note - There may be more we can do on our end in the future to make the issue more obvious, but in the meantime it is safest to consistently prefix the methods you need to declare and call with `PlantSimEngine.`, or to explicitely import the functions you wish to extend, *e.g.*: `import PlantSimEngine: inputs_, outputs_`. - -### MultiScaleModel : forgetting a kwarg in the declaration - -A MultiScaleModel requires two kwargs, model and mapped_variables : - -```julia -models = PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[:TT_cu => :Scene,], - ) -``` - -Forgetting 'model=' : - -```julia -models = PlantSimEngine.MultiScaleModel( - ToyLAIModel(), - mapped_variables=[:TT_cu => :Scene,], - ) -ERROR: MethodError: no method matching PlantSimEngine.MultiScaleModel(::ToyLAIModel; mapped_variables::Vector{Pair{Symbol, String}}) -The type `MultiScaleModel` exists, but no method is defined for this combination of argument types when trying to construct it. - -Closest candidates are: - PlantSimEngine.MultiScaleModel(::T, ::Any) where T<:AbstractModel got unsupported keyword argument "mapped_variables" - @ PlantSimEngine PlantSimEngine/src/mtg/MultiScaleModel.jl:188 - PlantSimEngine.MultiScaleModel(; model, mapped_variables) - @ PlantSimEngine PlantSimEngine/src/mtg/MultiScaleModel.jl:191 -``` - -Forgetting 'mapped_variables=' : -```julia -models = PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - [:TT_cu => :Scene,], - ) - -ERROR: MethodError: no method matching PlantSimEngine.MultiScaleModel(::Vector{Pair{Symbol, String}}; model::ToyLAIModel) -The type `MultiScaleModel` exists, but no method is defined for this combination of argument types when trying to construct it. - -Closest candidates are: - PlantSimEngine.MultiScaleModel(; model, mapping) - @ PlantSimEngine PlantSimEngine/src/mtg/MultiScaleModel.jl:191 - PlantSimEngine.MultiScaleModel(::T, ::Any) where T<:AbstractModel got unsupported keyword argument "model" -``` - -The message 'got unsupported keyword argument "model"' can be misleading, as in the error in this case is not that a kwarg is *unsupported*, but rather that a keyword argument is *missing*. - -### MultiScaleModel : variable not defined in Module - -A possible cause for this error is that a variable was declared instead of a symbol in a mapping for a multiscale model : - -```julia -mapping = PlantSimEngine.ModelMapping(:Scale => -PlantSimEngine.MultiScaleModel( - model = ToyModel(), - mapped_variables = [should_be_symbol => :Other_Scale] # should_be_symbol is a variable, likely not found in the current module -), -... -), -``` - -Here's the correct version : -```julia -mapping = PlantSimEngine.ModelMapping(:Scale => -PlantSimEngine.MultiScaleModel( - model = ToyModel(), - mapped_variables=[:should_be_symbol => :Other_Scale] # should_be_symbol is now a symbol -), -... -), -``` - -### Kwarg and arg parameter issues when calling run! - -There are, unfortunately, multiple ways of passing in arguments to the run! functions that will confuse dynamic dispatch. Some of it is due to imperfections in type declarations on PlantSimEngine's end and may be improved upon in the future. - -Here are a few examples when modifying the usual multiscale run! call in this working example: - -```julia -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) -mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Plant, 1, 1)) -var1 = 15.0 - -mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - Process1Model(1.0), - Process2Model(), - Process3Model(), - Status(var1=var1,) - ) -) - -outs = Dict( - :Leaf => (:var1,), # :non_existing_variable is not computed by any model -) - -run!(mtg, mapping, meteo_day, PlantMeteo.Constants(), tracked_outputs=outs) -``` - -The exact signature is this : -```julia -function run!( - object::MultiScaleTreeGraph.Node, - mapping::ModelMapping, - meteo=nothing, - constants=PlantMeteo.Constants(), - extra=nothing; - nsteps=nothing, - tracked_outputs=nothing, - check=true, - executor=ThreadedEx() -``` - -Arguments after the mtg and mapping all have a default value and are optional, and arguments after the ';' delimiter are kwargs and need to be named. - -If one forgets the mtg, a flaw in the way run! is defined will lead to this error : -```julia -run!(mapping, meteo_day, PlantMeteo.Constants(), tracked_outputs=outs) - -ERROR: MethodError: no method matching check_dimensions(::PlantSimEngine.TableAlike, ::Tuple{…}, ::DataFrame) -The function `check_dimensions` exists, but no method is defined for this combination of argument types. - -Closest candidates are: - check_dimensions(::Any, ::Any) - @ PlantSimEngine PlantSimEngine/src/checks/dimensions.jl:43 - ... -``` - -If one forgets the necessary 'tracked_outputs=' in the definition, outs will be interpreted as the 'extra' arg instead of a kwarg. 'extra' usually defaults to nothing, and is reserved in multiscale mode, leading to the following error : - -```julia -run!(mtg, mapping, meteo_day, PlantMeteo.Constants(), outs) - -ERROR: Extra parameters are not allowed for the simulation of an MTG (already used for statuses). -Stacktrace: - [1] error(s::String) - @ Base ./error.jl:35 - [2] run!(::PlantSimEngine.TreeAlike, object::PlantSimEngine.GraphSimulation{…}, meteo::DataFrames.DataFrameRows{…}, constants::Constants{…}, extra::Dict{…}; tracked_outputs::Nothing, check::Bool, executor::ThreadedEx{…}) -``` - -In case of a more generic error that returns a -For example, if one does the opposite and adds a non-existent kwarg, the generic dispatch failure has some more specific information : -`got unsupported keyword argument "constants"` - -```julia -run!(mtg, mapping, meteo_day, constants=PlantMeteo.Constants(), tracked_outputs=outs) - -ERROR: MethodError: no method matching run!(::Node{…}, ::Dict{…}, ::DataFrame, ::Dict{…}, ::Nothing; constants::Constants{…}) -This error has been manually thrown, explicitly, so the method may exist but be intentionally marked as unimplemented. - -Closest candidates are: - run!(::Node, ::Dict{String}, ::Any, ::Any, ::Any; nsteps, tracked_outputs, check, executor) got unsupported keyword argument "constants" -``` - -### Hard dependency process not present in the mapping - -Another weakness in the current error checking leads to an unclear Julia error if a model A is present in a mapping and has a hard dependency on a model B, but B is absent from the mapping. - -In the following example, A corresponds to Process3Model, which requires a model B implementing 'Process2Model' and referred to as 'process2'. -Looking at the source code for Process3Model, the hard dependency is declared here : -```julia -PlantSimEngine.dep(::Process3Model) = (process2=Process2Model,) -``` - -However, the model provided in the examples, Process2Model is absent from the mapping : - -```julia -simple_mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Plant, 1, 1)) -mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - Process3Model(), - Status(var5=15.0,) - ) -) -outs = Dict( - :Leaf => (:var5,), -) -run!(simple_mtg, mapping, meteo_day, tracked_outputs=outs) - -ERROR: type NamedTuple has no field process2 -Stacktrace: - [1] getproperty(x::@NamedTuple{process3::Process3Model}, f::Symbol) - @ Base ./Base.jl:49 - [2] run!(::Process3Model, models::@NamedTuple{…}, status::Status{…}, meteo::DataFrameRow{…}, constants::Constants{…}, extra::PlantSimEngine.GraphSimulation{…}) - ... -``` - -The fix is to add Process2Model() -or another model for the same process- to the mapping. - -### Status API ambiguity - -One current problem with PlantSimEngine's API is that declaring a simulation's Status or Statuses differs between single- and multi-scale. - -Returning to the example in [Implementing a model: forgetting to import or prefix functions](@ref), the single-scale mapping status was declared like this: - -```julia -model = PlantSimEngine.ModelMapping( - ToyToyModel(1), - status = ( a = 1, b = 0, c = 0), -) -``` -If instead you replace `status = ...`with the multi-scale declaration: `Status(...)`, you will get the following error: - -```julia -ERROR: MethodError: no method matching process(::Status{(:a, :b, :c), Tuple{Base.RefValue{Int64}, Base.RefValue{Int64}, Base.RefValue{Int64}}}) -The function `process` exists, but no method is defined for this combination of argument types. - -Closest candidates are: - process(::Pair{Symbol, A}) where A<:AbstractModel - @ PlantSimEngine ~/path/to/pkg/PlantSimEngine/src/Abstract_model_structs.jl:16 - process(::A) where A<:AbstractModel - @ PlantSimEngine ~/path/to/pkg/PlantSimEngine/src/Abstract_model_structs.jl:13 - -Stacktrace: - [1] (::PlantSimEngine.var"#5#6")(i::Status{(:a, :b, :c), Tuple{Base.RefValue{…}, Base.RefValue{…}, Base.RefValue{…}}}) - @ PlantSimEngine ./none:0 - [2] iterate -``` - -If you do the opposite in a multi-scale simulation by replacing the necessary `Status(...)` with `status = ...`, you may get an `ERROR: syntax: invalid named tuple element` error. Here's some output when tinkering with the Toy Plant tutorial's mapping: - -```julia -ERROR: syntax: invalid named tuple element "PlantSimEngine.MultiScaleModel(...)" around /path/to/Pkg/PlantSimEngine/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl:196 -Stacktrace: - [1] top-level scope - @ ~/path/to/pkg/PlantSimEngine/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl:196 -``` -or -```julia -ERROR: syntax: invalid named tuple element "ToyRootGrowthModel(50, 10)" around /path/to/Pkg/PlantSimEngine/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl:196 -Stacktrace: - [1] top-level scope - @ ~/path/to/Pkg/PlantSimEngine/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl:196 -``` - -## Forgetting to declare a scale in the mapping but having variables point to it - -If there is a need to collect variables at two different scales, and one scale is completely absent from the mapping, the error currently occurs on the Julia side : - -```julia -# No models at the E3 scale in the mapping ! - -:E2 => ( - PlantSimEngine.MultiScaleModel( - model = HardDepSameScaleEchelle2Model(), - mapped_variables=[:c => :E1 => :c, :e3 => :E3 => :e3, :f3 => :E3 => :f3,], - ), - ), - -Exception has occurred: KeyError -* -KeyError: key :E3 not found -Stacktrace: -[1] hard_dependencies(mapping::Dict{String, Tuple{Any, Any}}; verbose::Bool) -@ PlantSimEngine ......./src/dependencies/hard_dependencies.jl:175 -... -``` - -### Parenthesis placement when declaring a mapping - -An unintuitive error encountered in the past when defining a mapping : - -```julia -ERROR: ArgumentError: AbstractDict(kv): kv needs to be an iterator of 2-tuples or pairs -``` - -may occur when forgetting the parenthesis after '=>' in a mapping declaration, and combining it with another parenthesis error. - -```julia -mapping = PlantSimEngine.ModelMapping( "Scale" => (ToyAssimGrowthModel(0.0, 0.0, 0.0), ToyCAllocationModel(), Status( TT_cu=Vector(cumsum(meteo_day.TT))), ), ) -``` - -Other errors such as: - -```julia -ERROR: MethodError: no method matching Dict(::Pair{String, ToyAssimGrowthModel{Float64}}, ::ToyCAllocationModel, ::Status{(:TT_cu,), Tuple{Base.RefValue{…}}}) -The type `Dict` exists, but no method is defined for this combination of argument types when trying to construct it. - -Closest candidates are: - Dict(::Pair{K, V}...) where {K, V} -``` - -often indicate a likely syntax error somewhere in the mapping definition. - -### Empty status vectors in multi-scale simulations - -This situation won't trigger an error. Unexpectedly empty vectors can be returned as outputs if you happen to forget to a node at the corresponding scale in the MTG, and no organ creation occurs for that node. - -Here's an example taken from the [Converting a single-scale simulation to multi-scale](@ref) page. It was modified by removing the :Plant node in the dummy MTG passed into the [`run!`](@ref)function. Without that :Plant node, only :Scene-scale models can run initially, and since no nodes are created, :Plant-scale models will never be run. - -```julia -PlantSimEngine.@process "tt_cu" verbose = false - -struct ToyTt_CuModel <: AbstractTt_CuModel end - -function PlantSimEngine.run!(::ToyTt_CuModel, models, status, meteo, constants, extra=nothing) - status.TT_cu += - meteo.TT -end - -function PlantSimEngine.inputs_(::ToyTt_CuModel) - NamedTuple() # No input variables -end - -function PlantSimEngine.outputs_(::ToyTt_CuModel) - (TT_cu=-Inf,) -end - -mapping_multiscale = PlantSimEngine.ModelMapping( - :Scene => ToyTt_CuModel(), - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[ - :TT_cu => :Scene, - ], - ), - Beer(0.5), - ToyRUEGrowthModel(0.2), - ), -) - -mtg_multiscale = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Plant, 0, 0),) -#plant = MultiScaleTreeGraph.Node(mtg_multiscale, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - -out_multiscale = run!(mtg_multiscale, mapping_multiscale, meteo_day) - -out_multiscale[:Plant][:LAI] -``` - -In the above code, uncommenting the second line will add a :Plant node to the MTG, and the simulation will then behave as intuitively expected. diff --git a/docs/src/troubleshooting_and_testing/tips_and_workarounds.md b/docs/src/troubleshooting_and_testing/tips_and_workarounds.md deleted file mode 100644 index 7724393f5..000000000 --- a/docs/src/troubleshooting_and_testing/tips_and_workarounds.md +++ /dev/null @@ -1,114 +0,0 @@ -# Tips and workarounds - -## PlantSimEngine is actively being developed - -PlantSimEngine, despite the somewhat abstract codebase and generic simulation ambitions, is quite grounded in reality. There IS a desire to accomodate for a wide range of possible simulations, without constraining the user too much, but most features are developed on an as-needed basis, and grow out of necessity, partly from the requirements of an increasingly complex and refined implementation of an oil palm model, [XPalm](https://github.com/PalmStudio/XPalm.jl). - -Since the oil palm model is actively being developed, and some features aren't ready in PlantSimEngine, or require a lot of rewriting that we're not certain would be worth it (especially if it ends up constraining the codebase or what the user can do), some workarounds and shortcuts are occasionally used to circumvent a limitation. - -There are also a couple of features that are quick hacks or that are meant for quick and dirty prototyping, not for production. - -We'll list a few of them here, and will likely add some entry in the future listing some built-in limitations or implicit expectations of the package. - -```@contents -Pages = ["tips_and_workarounds.md"] -Depth = 2 -``` - -## Making use of past states in multi-scale simulations - -It is possible to make use of the value of a variable in the past simulation timestep via the [`PreviousTimeStep`](@ref) mechanism in the mapping API (In fact, as mentioned elsewhere, it is the default way to break undesirable cyclic dependencies that can come up when coupling models, see : [Avoiding cyclic dependencies](@ref)). - -However, it is not possible to go beyond that through the mapping API. Something like `PreviousTimeStep(PreviousTimeStep(PreviousTimeStep(:carbon_biomass)))` is not supported. Don't do that. - -One way to access prior variable states is simply to write an ad hoc model that stores a few values into an array or however many variables you might need, which you can then update every timestep and feed into other models that might need it. - -## Having a variable simultaneously as input and output of a model - -One current limitation of `PlantSimEngine` that can be occasionally awkward is that using the same variable name as input and output in a single model is unsupported. - -(On a related note : it is not possible to have two variables with the same name *in the same scale*. They are considered as the same variable.) - -The reason being that it is usually impossible to automatically determine how the coupling is supposed to work out, when other dependencies latch onto such a model. The user would have to explicitely declare some order of simulation between several models, and some amount of programmer work would also be necessary to implement that extra API feature into `PlantSimEngine`. - -We haven't found an approach that was fully satisfactory from both a code simplicity and an API convenience POV. Especially when prototyping and adding in new models, as that might require redeclaring the simulation order for those specific variables. - -There are two workarounds : - -- One possibly awkward approach is to rename one of the variables. It is not ideal, of course, as it means you might not be able to use a predefined model 'out of the box', but it does not have any of the tradeoffs and constraints mentioned above. - -- In many other situations one can work with what PlantSimEngine already provides. - -For example, one model in [XPalm.jl](https://github.com/PalmStudio/XPalm.jl/blob/main/src/plant/phytomer/leaves/leaf_pruning.jl) handles leaf pruning, affecting biomass. A straightforward implementation would be to have a `leaf_biomass` variable as both input and output. The workaround is to instead output a variable `leaf_biomass_pruning_loss` and to have that as input in the next timestep to compute the new leaf biomass. - -[Part 3](../multiscale/multiscale_example_3.md) of the Toy Plant tutorial does something similar for its carbon stock. The `carbon_stock` variable indicates how much carbon is available for root and internode growth, but instead of updating it and passing it along after the root growth decision model decided whether or not roots should be added, that model computes a `carbon_stock_updated_after_roots` which is then used by the internode growth model. - -This change in design avoids model order ambiguity and also improves readability, and makes sense in terms of PlantSimEngine's philosophy. - -## [Multiscale : passing in a vector in a mapping status at a specific scale](@id multiscale_vector) - -!!! note - This section is a little more advanced and not recommended for beginners - -You may have noticed that some legacy documentation examples pass a vector -(1-dimensional array) variable into the [`status`](@ref) component of -`PlantSimEngine.ModelMapping(...)` compatibility simulations. - -This is practical for simple simulations, or when quickly prototyping, to avoid having to write a model specifically for it. Whatever models make use of that variable are provided with one element corresponding to the current timestep every iteration. - -In multi-scale simulations, this feature is also supported, though not part of the main API. The way outputs and statuses work is a little different, so that little convenience feature is not as straightforward. - -It remains a convenience path for prototyping, and it is still not tested for -more complex interactions, so it may interact badly with variables that are -mapped to different scales or in unusual dependency couplings. - -The way to use this is as follows: - -Call the function `replace_mapping_status_vectors_with_generated_models(mapping_with_vectors_in_status, timestep_model_organ_level, nsteps)`on your mapping. - -It will parse your mapping, generate custom models to store and feed the vector values each timestep, and return the new mapping you can then use for your simulation. It also slips in a couple of internal models that provide the timestep index to these models (so note that symbols `:current_timestep` and `:next_timestep` will be declared for that mapping). You can decide which scale/organ level you want those models to be in via the `timestep_model_organ_level`parameter. `nsteps` is used as a sanity check, and expects you to provide the amount of simulation timesteps. - -!!! warning - Only subtypes of AbstractVector present in statuses will be affected. In some cases, meteo values might need a small conversion. For instance : - ``` - meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) - status(TT_cu=cumsum(meteo_day.TT),)``` - - cumsum(meteo_day.TT) actually returns a CSV.SentinelArray.ChainedVectors{T, Vector{T}}, which is not a subtype of AbstractVector. - Replacing it with Vector(cumsum(meteo_day.TT)) will provide an adequate type. - -Here's an example usage, fixing the first attempt at [Converting a single-scale simulation to multi-scale](@ref): - -```julia -using PlantSimEngine -using PlantSimEngine.Examples -using PlantMeteo, Dates -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) - -# Direct translation of the single-scale simulation -mapping_pseudo_multiscale = PlantSimEngine.ModelMapping( -:Plant => ( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(0.2), - Status(TT_cu=cumsum(meteo_day.TT),) - ), -) - -mtg = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Plant, 1, 0),) - -# will generate an error as vectors can't be directly passed into a Status in multi-scale simulations -#out_pseudo_multiscale_error = run!(mtg, mapping_pseudo_multiscale, meteo_day) - -mapping_pseudo_multiscale_adjusted = PlantSimEngine.replace_mapping_status_vectors_with_generated_models(mapping_pseudo_multiscale, :Plant, PlantSimEngine.get_nsteps(meteo_day)) - -out_pseudo_multiscale_successful = run!(mtg, mapping_pseudo_multiscale_adjusted, meteo_day) - -``` - - -This feature is likely to break in simulations that make use of planned future features (such as mixing models with different timesteps), without guarantee of a fix on a short notice. Again, bear in mind it is mostly a convenient shortcut for prototyping, when doing multi-scale simulations. - -## Cyclic dependencies in single-scale simulations - -Cyclic dependencies can happen in single-scale simulations, but the PreviousTimestep feature currently isn't available. Hard dependencies are one way to deal with them, creating a multi-scale simulation with a single effective scale is also an option. diff --git a/docs/src/working_with_data/fitting.md b/docs/src/working_with_data/fitting.md index e6e09dc3d..f49f989b2 100644 --- a/docs/src/working_with_data/fitting.md +++ b/docs/src/working_with_data/fitting.md @@ -1,76 +1,56 @@ -# Parameter fitting +# Parameter Fitting -```@setup usepkg -using PlantSimEngine, PlantMeteo, Dates, Statistics, DataFrames -using PlantSimEngine.Examples - -meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_f=300.0) -m = PlantSimEngine.ModelMapping(Beer(0.6), status=(LAI=2.0,)) -run!(m, meteo) - -df = DataFrame(aPPFD=m[:aPPFD][1], LAI=m.status.LAI[1], Ri_PAR_f=meteo.Ri_PAR_f[1]) -``` - -## The fit method - -Models are often calibrated using data, but the calibration process is not always the same depending on the model, and the data available to the user. - -`PlantSimEngine` defines a generic [`fit`](@ref) function that allows modelers provide a fitting algorithm for their model, and for users to use this method to calibrate the model using data. - -The function does nothing in this package, it is only defined to provide a common interface for all the models. It is up to the modeler to implement the method for their model. - -The method is implemented as a function with the following design pattern: the call to the function should take the model type as the first argument (T::Type{<:AbstractModel}), the data as the second argument (as a `Table.jl` compatible type, such as `DataFrame`), and any more information as keyword arguments, *e.g.* constants or parameters initializations with default values when necessary. - -## Example with Beer - -The example script (see `src/examples/Beer.jl`) that implements the `Beer` model provides an example of how to implement the `fit` method for a model: +`PlantSimEngine.fit` is a shared interface for model-specific calibration. +Model packages implement a method whose first argument is the model type and +whose second argument is Tables.jl-compatible observations. ```julia -function PlantSimEngine.fit(::Type{Beer}, df; J_to_umol=PlantMeteo.Constants().J_to_umol) - k = Statistics.mean(log.(df.Ri_PAR_f ./ (df.PPFD ./ J_to_umol)) ./ df.LAI) +function PlantSimEngine.fit( + ::Type{Beer}, + data; + J_to_umol=PlantMeteo.Constants().J_to_umol, +) + k = Statistics.mean( + log.(data.Ri_PAR_f ./ (data.aPPFD ./ J_to_umol)) ./ data.LAI, + ) return (k=k,) end ``` -The function takes a `Beer` type as the first argument, the data as a `Tables.jl` -compatible type, such as a `DataFrame` as the second argument, and the `J_to_umol` constant as a keyword argument, which is used to convert between μ mol m⁻² s⁻¹ and J m⁻² s⁻¹. - -`df` should contain the columns `PPFD` (μ mol m⁻² s⁻¹), `LAI` (m² m⁻²) and `Ri_PAR_f` (W m⁻²). The function then computes `k` based on these values, and returns it as a `NamedTuple` of the form `(parameter_name=parameter_value,)`. - -Here's an example of how to use the `fit` method: +The result should be a `NamedTuple` of fitted parameters. -Importing the script first: - -```julia -using PlantSimEngine, PlantMeteo, Dates, DataFrames, Statistics -# Import the examples defined in the `Examples` sub-module: +```@example fitting +using PlantSimEngine, PlantMeteo, Dates, DataFrames using PlantSimEngine.Examples -``` - -Defining the meteo data: - -```@example usepkg -meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_f=300.0) -``` - -Computing the `PPFD` values from the `Ri_PAR_f` values using the `Beer` model (with `k=0.6`): - -```@example usepkg -m = PlantSimEngine.ModelMapping(Beer(0.6), status=(LAI=2.0,)) -run!(m, meteo) -``` - -Now we can define the "data" to fit the model using the simulated `PPFD` values: - -```@example usepkg -df = DataFrame(aPPFD=m[:aPPFD][1], LAI=m.status.LAI[1], Ri_PAR_f=meteo.Ri_PAR_f[1]) -``` - -And finally we can fit the model using the `fit` method: -```@example usepkg -fit(Beer, df) +meteo = Atmosphere( + T=20.0, + Wind=1.0, + P=101.3, + Rh=0.65, + Ri_PAR_f=300.0, + duration=Hour(1), +) + +scene = Scene( + Object(:leaf; scale=:Leaf, status=Status(LAI=2.0)); + applications=( + ModelSpec(Beer(0.6)) |> AppliesTo(One(scale=:Leaf)), + ), + environment=meteo, +) + +run!(scene) +leaf = only(scene_objects(scene; scale=:Leaf)) +data = DataFrame( + aPPFD=[leaf.status.aPPFD], + LAI=[leaf.status.LAI], + Ri_PAR_f=[meteo.Ri_PAR_f[1]], +) + +fit(Beer, data) ``` -!!! note - This is a dummy example to show that the fitting method works. A real application would fit the parameter values on the data directly. \ No newline at end of file +This example recovers the parameter used to generate the synthetic +observation. Real calibration methods can use any optimizer or uncertainty +framework and may return additional diagnostics. diff --git a/docs/src/working_with_data/floating_point_accumulation_error.md b/docs/src/working_with_data/floating_point_accumulation_error.md deleted file mode 100644 index 4d1073cd0..000000000 --- a/docs/src/working_with_data/floating_point_accumulation_error.md +++ /dev/null @@ -1,161 +0,0 @@ -# Floating-point considerations - -```@setup usepkg -using PlantSimEngine -using PlantSimEngine.Examples -using PlantMeteo, Dates, MultiScaleTreeGraph -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) - -models = PlantSimEngine.ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(0.2); - status=(TT_cu=cumsum(meteo_day.TT),), -) - -out_singlescale = run!(models, meteo_day) -``` -## Investigating a discrepancy - -In the [Converting a single-scale simulation to multi-scale](@ref) page, a single-scale simulation was converted to an equivalent multiscale simulation, and outputs were compared. One detail that was glossed over, but important to bear in mind as a PlantSimEngine user is related to floating-point approximations. - -### Single-scale simulation - -```@example usepkg -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) - -models_singlescale = PlantSimEngine.ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(0.2); - status=(TT_cu=cumsum(meteo_day.TT),), -) - -outputs_singlescale = run!(models_singlescale, meteo_day) -outputs_singlescale[1:3,:] # show the first 3 rows of the output -``` - -### Multi-scale equivalent - -```@example usepkg -PlantSimEngine.@process "tt_cu" verbose = false - -struct ToyTt_CuModel <: AbstractTt_CuModel end - -function PlantSimEngine.run!(::ToyTt_CuModel, models, status, meteo, constants, extra=nothing) - status.TT_cu += - meteo.TT -end - -function PlantSimEngine.inputs_(::ToyTt_CuModel) - NamedTuple() # No input variables -end - -function PlantSimEngine.outputs_(::ToyTt_CuModel) - (TT_cu=0.0,) -end - -mapping_multiscale = PlantSimEngine.ModelMapping( - :Scene => ToyTt_CuModel(), - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[ - :TT_cu => :Scene, - ], - ), - Beer(0.5), - ToyRUEGrowthModel(0.2), - ), -) - -mtg_multiscale = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Plant, 0, 0),) - plant = MultiScaleTreeGraph.Node(mtg_multiscale, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - -outputs_multiscale = run!(mtg_multiscale, mapping_multiscale, meteo_day) -``` - -### Output comparison - -```@setup usepkg -mapping_multiscale = PlantSimEngine.ModelMapping( - :Scene => ToyTt_CuModel(), - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[ - :TT_cu => :Scene, - ], - ), - Beer(0.5), - ToyRUEGrowthModel(0.2), - ), -) - -mtg_multiscale = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 0, 0),) - plant = MultiScaleTreeGraph.Node(mtg_multiscale, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - -outputs_multiscale = run!(mtg_multiscale, mapping_multiscale, meteo_day) -``` - -```@example usepkg - -computed_TT_cu_multiscale = [outputs_multiscale[:Scene][i].TT_cu for i in 1:length(outputs_multiscale[:Scene])] -is_approx_equal = length(unique(computed_TT_cu_multiscale .≈ outputs_singlescale.TT_cu)) == 1 -``` - -Why was the comparison only approximate ? Why `≈` instead of `==`? - -Let's try it out. What if write instead: - -```@example usepkg -computed_TT_cu_multiscale = [outputs_multiscale[:Scene][i].TT_cu for i in 1:length(outputs_multiscale[:Scene])] -is_perfectly_equal = length(unique(computed_TT_cu_multiscale .== outputs_singlescale.TT_cu)) == 1 -``` - -Why is this false? Let's look at the data. - -Looking more closely at the output, we can notice that values are identical up to timestep #105 : - -```@example usepkg -(computed_TT_cu_multiscale .== outputs_singlescale.TT_cu)[104] -``` - -```@example usepkg -(computed_TT_cu_multiscale .== outputs_singlescale.TT_cu)[105] -``` - -We have the values 132.33333333333331 (multi-scale) and 132.33333333333334 (single-scale). The final output values are : 2193.8166666666643 (multi-scale) and 2193.816666666666 (single-scale). - -The divergence isn't huge, but in other situations or over more timesteps it could start becoming a problem. - -## Floating-point summation - -The reason values aren't identical, is due to the fact that many numbers do not have an exact floating point representation. A classical example is the fact that [0.1 + 0.2 != 0.3](https://blog.reverberate.org/2016/02/06/floating-point-demystified-part2.html) : - -```@example usepkg -println(0.1 + 0.2 - 0.3) -``` - -When summing many numbers, depnding on the order in which they are summed, floating-point approximation errors may aggregate more or less quickly. - -The default summation per-timestep in our example `Toy_Tt_CuModel` was a naive summation. The `cumsum` function used in the single-scale simulation to directly compute the TT_cu uses a pairwise summation method that provides approximation error on fewer digits compared to naive summation. Errors aggregate more slowly. - -In our simple example, using Float64 values, the difference wasn't significant enough to matter, but if you are writing a simulation over many timesteps or aggregating a value over many nodes, you may need to alter models to avoid numerical errors blowing up due to floating-point accuracy. - -Depending on what value is being computed and the mathematical operations used, changes may range from applying a simple scale to a range of values, to significant refactoring. - - -## Other links related to floating-point numerical concerns - -Note that many of the examples in these blogposts discuss Float32 accuracy. Float64 values have several extra precision bits to work. - -A series of blog posts on floating-point accuracy: [https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/) -Floating-Point Visually Explained : [https://fabiensanglard.net/floating_point_visually_explained/](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/) -Examples of floating point problems: [https://jvns.ca/blog/2023/01/13/examples-of-floating-point-problems/](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/) - -Relating specifically to floating-point sums: - -Pairwise summation: [https://en.wikipedia.org/wiki/Pairwise_summation](https://en.wikipedia.org/wiki/Pairwise_summation) -Kahan summation: [https://en.wikipedia.org/wiki/Kahan_summation_algorithm](https://en.wikipedia.org/wiki/Kahan_summation_algorithm) -Taming Floating-Point Sums: [https://orlp.net/blog/taming-float-sums/](https://orlp.net/blog/taming-float-sums/) diff --git a/docs/src/working_with_data/inputs.md b/docs/src/working_with_data/inputs.md deleted file mode 100644 index addafdb16..000000000 --- a/docs/src/working_with_data/inputs.md +++ /dev/null @@ -1,103 +0,0 @@ -# Input types - -In scene/object simulations, [`run!`](@ref) usually takes a `Scene` and the -meteorology is supplied through the scene `environment`. In legacy -compatibility simulations, [`run!`](@ref) takes a -`PlantSimEngine.ModelMapping(...)` and meteorological data. The meteorology is -usually provided for one timestep using an `Atmosphere`, or for several -timesteps using a `TimeStepTable{Atmosphere}`. - -[`run!`](@ref) knows how to handle these data formats via the [`PlantSimEngine.DataFormat`](@ref) trait (see [this blog post](https://www.juliabloggers.com/the-emergent-features-of-julialang-part-ii-traits/) to learn more about traits). For example, we tell PlantSimEngine that a `TimeStepTable` should be handled like a table by implementing the following trait: - -```julia -DataFormat(::Type{<:PlantMeteo.TimeStepTable}) = TableAlike() -``` - -If you need to use a different data format for the meteorology, you can implement a new trait for it. For example, if you have a table-alike data format, you can implement the trait like this: - -```julia -DataFormat(::Type{<:MyTableFormat}) = TableAlike() -``` - -There are two other traits available: `SingletonAlike` for a data format representing one time-step only, and `TreeAlike` for trees, which is used for MultiScaleTreeGraphs nodes (not generic at this time). - -## Promoting status variable types - -Use the `type_promotion` keyword on the qualified compatibility constructor -`PlantSimEngine.ModelMapping(...)` when the default input and output values -declared by models should be converted to another type: - -```julia -models = PlantSimEngine.ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(0.2); - status=(TT_cu=cumsum(meteo_day.TT),), - type_promotion=Dict(Real => Float32), -) -``` - -For single-scale mappings, `type_promotion` is applied while the backing status is constructed: model-provided default values are converted, while values explicitly passed in `status` keep the type chosen by the user. If those values should also be `Float32`, pass them as `Float32` values directly. - -For legacy multiscale mappings, the per-node statuses do not exist when -`PlantSimEngine.ModelMapping(...)` is constructed. The promotion map is stored -on the mapping and applied when the MTG simulation is initialized: - -```julia -mapping = PlantSimEngine.ModelMapping( - :Scene => ToyTt_CuModel(), - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[ - :TT_cu => :Scene, - ], - ), - Beer(0.5), - ToyRUEGrowthModel(0.2), - ); - type_promotion=Dict(Float64 => Float32, Vector{Float64} => Vector{Float32}), -) - -outputs = run!(mtg, mapping, meteo_day) -``` - -The same promotion can also be passed at MTG run time: - -```julia -outputs = run!( - mtg, - mapping, - meteo_day; - type_promotion=Dict(Float64 => Float32, Vector{Float64} => Vector{Float32}), -) -``` - -In multiscale runs, type promotion is used by `GraphSimulation` during status template creation, `RefVector` creation, output preallocation, and initialization from MTG node attributes. - - -## Special considerations for new input types - -If you want to use a custom data format for the inputs, you need to make sure some methods are implemented for your data format depending on your use-cases. - -For example if you use models that need to get data from a different time step (*e.g.* a model that needs to get the previous day's temperature), you need to make sure that the data from the other time-steps can be accessed from the current time-step. - -To do so, you need to implement the following methods for your structure that defines your rows: - -- `Base.parent`: return the parent table of the row, *e.g.* the full DataFrame -- `PlantMeteo.rownumber`: return the row number of the row in the parent table, *e.g.* the row number in the DataFrame -- (Optionnally) `PlantMeteo.row_from_parent(row, i)`: return row `i` from the parent table, *e.g.* the row `i` from the DataFrame. This is only needed if you want high performance, the default implementation calls `Tables.rows(parent(row))[i]`. - -!!! compat - `PlantMeteo.rownumber` is temporary. It soon will be replaced by `DataAPI.rownumber` instead, which will be also used by *e.g.* DataFrames.jl. See [this Pull Request](https://github.com/JuliaData/DataAPI.jl/issues/60). - -## Working with weather data - -Here's a quick example showcasing how to export the example weather data to your own file : - -```julia -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) -PlantMeteo.write_weather("examples/meteo_day.csv", meteo_day, duration = Dates.Day) -``` - -If you wish to filter weather data, reshape it, adjust it, write it, you'll find some more examples in PlantMeteo's [API reference](https://palmstudio.github.io/PlantMeteo.jl/stable/API/). diff --git a/docs/src/working_with_data/reducing_dof.md b/docs/src/working_with_data/reducing_dof.md deleted file mode 100644 index 380523adc..000000000 --- a/docs/src/working_with_data/reducing_dof.md +++ /dev/null @@ -1,121 +0,0 @@ -# Reducing the DoF - -```@setup usepkg -using PlantSimEngine, PlantMeteo, Dates -# Import the examples defined in the `Examples` sub-module: -using PlantSimEngine.Examples - -meteo = Atmosphere(T = 20.0, Wind = 1.0, P = 101.3, Rh = 0.65) -struct ForceProcess1Model <: AbstractProcess1Model end -PlantSimEngine.inputs_(::ForceProcess1Model) = (var3=-Inf,) -PlantSimEngine.outputs_(::ForceProcess1Model) = (var3=-Inf,) -function PlantSimEngine.run!(::ForceProcess1Model, models, status, meteo, constants=nothing, extra=nothing) - return nothing -end -``` - -## Introduction - -### Why reduce the degrees of freedom - -Reducing the degrees of freedom in a model, by forcing certain variables to measurements, can be useful for several reasons: - -1. It can prevent overfitting by constraining the model and making it less complex. -2. It can help to better calibrate the other components of the model by reducing the co-variability of the variables (see [Parameter degeneracy](@ref)). -3. It can lead to more interpretable models by identifying the most important variables and relationships. -4. It can improve the computational efficiency of the model by reducing the number of variables that need to be estimated. -5. It can also help to ensure that the model is consistent with known physical or observational constraints and improve the credibility of the model and its predictions. -6. It is important to note that over-constraining a model can also lead to poor fits and false conclusions, so it is essential to carefully consider which variables to constrain and to what measurements. - -## Parameter degeneracy - -The concept of "degeneracy" or "parameter degeneracy" in a model occurs when two or more variables in a model are highly correlated, and small changes in one variable can be compensated by small changes in another variable, so that the overall predictions of the model remain unchanged. Degeneracy can make it difficult to estimate the true values of the variables and to determine the unique solutions of the model. It also makes the model sensitive to the initial conditions (*e.g.* the parameters) and the optimization algorithm used. - -Degeneracy is related to the concept of "co-variability" or "collinearity", which refers to the degree of linear relationship between two or more variables. In a degenerate model, two or more variables are highly co-variate, meaning that they are highly correlated and can produce similar predictions. By fixing one variable to a measured value, the model will have less flexibility to adjust the other variables, which can help to reduce the co-variability and improve the robustness of the model. - -This is an important topic in plant/crop modelling, as the models are very often degenerate. It is most often referred to as "multicollinearity" in the field. In the context of model calibration, it is also known as "parameter degeneracy" or "parameter collinearity". In the context of model reduction, it is also known as "redundancy" or "redundant variables". - -## Reducing the DoF in PlantSimEngine - -### Soft-coupled models - -PlantSimEngine provides a simple way to reduce the degrees of freedom in a model by constraining the values of some variables to measurements. - -Let's define a model list as usual with the seven processes from `examples/dummy.jl`: - -```@example usepkg -using PlantSimEngine, PlantMeteo, Dates -# Import the examples defined in the `Examples` sub-module: -using PlantSimEngine.Examples - -meteo = Atmosphere(T = 20.0, Wind = 1.0, P = 101.3, Rh = 0.65) -m = PlantSimEngine.ModelMapping( - Process1Model(2.0), - Process2Model(), - Process3Model(), - Process4Model(), - Process5Model(), - Process6Model(), - Process7Model(), - status=(var0 = 0.5,) -) - -run!(m, meteo) - -status(m) -``` - -Let's say that `m` is our complete model, and that we want to reduce the degrees of freedom by constraining the value of `var9` to a measurement, which was previously computed by `Process7Model`, a soft-dependency model. It is very easy to do this in PlantSimEngine: just remove the model from the model list and give the value of the measurement in the status: - -```@example usepkg -m2 = PlantSimEngine.ModelMapping( - Process1Model(2.0), - Process2Model(), - Process3Model(), - Process4Model(), - Process5Model(), - Process6Model(), - status=(var0 = 0.5, var9 = 10.0), -) - -out = run!(m2, meteo) -``` - -And that's it ! The models that depend on `var9` will now use the measured value of `var9` instead of the one computed by `Process7Model`. - -### Hard-coupled models - -It is a bit more complicated to reduce the degrees of freedom in a model that is hard-coupled to another model, because it calls the [`run!`](@ref) method of the other model. - -In this case, we need to replace the old model with a new model that forces the value of the variable to the measurement. This is done by giving the measurements as inputs of the new model, and returning nothing so the value is unchanged. - -Starting from the model list with the seven processes from above, but this time let's say that we want to reduce the degrees of freedom by constraining the value of `var3` to a measurement, which was previously computed by `Process1Model`, a hard-dependency model. It is very easy to do this in PlantSimEngine: just replace the model by a new model that forces the value of `var3` to the measurement: - -```@example usepkg -struct ForceProcess1Model <: AbstractProcess1Model end -PlantSimEngine.inputs_(::ForceProcess1Model) = (var3=-Inf,) -PlantSimEngine.outputs_(::ForceProcess1Model) = (var3=-Inf,) -function PlantSimEngine.run!(::ForceProcess1Model, models, status, meteo, constants=nothing, extra=nothing) - return nothing -end -``` - -Now we can create a new model list with the new model for `process7`: - -```@example usepkg -m3 = PlantSimEngine.ModelMapping( - ForceProcess1Model(), - Process2Model(), - Process3Model(), - Process4Model(), - Process5Model(), - Process6Model(), - Process7Model(), - status = (var0=0.5,var3 = 10.0) -) - -out = run!(m3, meteo) -``` - -!!! note - We could also eventually provide the measured variable using the meteo data, but it is not recommended. The meteo data is meant to be used for the meteo variables only, and not for the model variables. It is better to use the status for that. diff --git a/docs/src/working_with_data/visualising_outputs.md b/docs/src/working_with_data/visualising_outputs.md deleted file mode 100644 index 94b69fb53..000000000 --- a/docs/src/working_with_data/visualising_outputs.md +++ /dev/null @@ -1,98 +0,0 @@ -```@setup usepkg -# ] add PlantSimEngine, PlantMeteo -using PlantSimEngine, PlantMeteo, Dates - -# Include the model definition from the examples folder: -using PlantSimEngine.Examples - -# Import the example meteorological data: -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) - -# Define the list of models for coupling: -model = PlantSimEngine.ModelMapping( - ToyLAIModel(), - Beer(0.6), - status=(TT_cu=cumsum(meteo_day[:, :TT]),), # Pass the cumulated degree-days as input to `ToyLAIModel`, this could also be done using another model -) - -# Run the simulation: -sim_out = run!(model, meteo_day) - -``` - -# Visualizing outputs and data - -## Output structure - -PlantSimEngine's run! functions return for each timestep the state of the variables that were requested using the `tracked_outputs` kwarg (or the state of every variable if this kwarg was left unspecified). Multi-scale simulations also indicate which organ and MTG node these state variables are related to. - -Here's an example indicating how to plot output data using CairoMakie, a package used for plotting. - -```@example usepkg -# ] add PlantSimEngine, PlantMeteo -using PlantSimEngine, PlantMeteo, Dates - -# Include the model definition from the examples folder: -using PlantSimEngine.Examples - -# Import the example meteorological data: -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Dates.Day) - -# Define the list of models for coupling: -models = PlantSimEngine.ModelMapping( - ToyLAIModel(), - Beer(0.6), - status=(TT_cu=cumsum(meteo_day[:, :TT]),), # Pass the cumulated degree-days as input to `ToyLAIModel`, this could also be done using another model -) - -# Run the simulation: -sim_outputs = run!(models, meteo_day) -sim_outputs[1:3,:] # show the first 3 rows of the output -``` - -The output data is displayed as a by default as a `TimeStepTable`. It is also possible to filter which variables are kept via the optional `tracked_outputs` keyword argument. - -## Plotting outputs - -Using CairoMakie, one can plot out selected variables : - -!!! note - You will need to add CairoMakie to your environment through Pkg mode first. - -```@example usepkg -# Plot the results: -using CairoMakie - -fig = Figure(resolution=(800, 600)) -ax = Axis(fig[1, 1], ylabel="LAI (m² m⁻²)") -lines!(ax, sim_outputs[:TT_cu], sim_outputs[:LAI], color=:mediumseagreen) - -ax2 = Axis(fig[2, 1], xlabel="Cumulated growing degree days since sowing (°C)", ylabel="aPPFD (mol m⁻² d⁻¹)") -lines!(ax2, sim_outputs[:TT_cu], sim_outputs[:aPPFD], color=:firebrick1) - -fig -``` - -## TimeStepTables and DataFrames - -```@setup usepkg -sim_out = run!(model, meteo_day) -``` - -The output data is usually stored in a `TimeStepTable` structure defined in `PlantMeteo.jl`, which is a fast DataFrame-like structure with each time step being a [`Status`](@ref). It can be also be any `Tables.jl` structure, such as a regular `DataFrame`. Weather data is also usually stored in a `TimeStepTable` but with each time step being an `Atmosphere`. - -Another simple way to get the results is to transform the outputs into a `DataFrame`. Which is very easy because the `TimeStepTable` implements the Tables.jl interface: - -```@example usepkg -using DataFrames -sim_outputs_df = PlantSimEngine.convert_outputs(sim_outputs, DataFrame) -sim_outputs_df[[1, 2, 3, 363, 364, 365], :] -``` - -It is also possible to create DataFrames from specific variables: - -```julia -df = DataFrame(aPPFD=sim_outputs[:aPPFD][1], LAI=sim_outputs.LAI[1], Ri_PAR_f=meteo.Ri_PAR_f[1]) -``` - -Which can also be useful for [Parameter fitting ](@ref). \ No newline at end of file diff --git a/examples/Beer.jl b/examples/Beer.jl index bdad8cbf0..c96bb90db 100644 --- a/examples/Beer.jl +++ b/examples/Beer.jl @@ -21,9 +21,6 @@ struct Beer{T} <: AbstractLight_InterceptionModel k::T end -# Beer is parallelizable over time-steps and objects, so we can declare it as such using the trait: -PlantSimEngine.TimeStepDependencyTrait(::Type{<:Beer}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:Beer}) = PlantSimEngine.IsObjectIndependent() """ run!(::Beer, object, meteo, constants=Constants(), extra=nothing) @@ -35,8 +32,7 @@ of light extinction. # Arguments - `::Beer`: a Beer model, from the model list (*i.e.* m.light_interception) -- `models`: A `ModelMapping` struct holding the parameters for the model with -initialisations for `LAI` (m² m⁻²): the leaf area index. +- `models`: the process-keyed model bundle supplied by the scene runtime. - `status`: the status of the model, usually the model list status (*i.e.* m.status) - `meteo`: meteorology structure, see [`Atmosphere`](https://palmstudio.github.io/PlantMeteo.jl/stable/#PlantMeteo.Atmosphere) - `constants = PlantMeteo.Constants()`: physical constants. See `PlantMeteo.Constants` for more details @@ -45,13 +41,20 @@ initialisations for `LAI` (m² m⁻²): the leaf area index. # Examples ```julia -m = PlantSimEngine.ModelMapping(Beer(0.5), status=(LAI=2.0,)) - -meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_q=300.0) - -run!(m, meteo) - -m[:aPPFD] +scene = Scene( + Object(:leaf; scale=:Leaf, status=Status(LAI=2.0)); + applications=(ModelSpec(Beer(0.5)) |> AppliesTo(One(scale=:Leaf)),), + environment=Atmosphere( + T=20.0, + Wind=1.0, + P=101.3, + Rh=0.65, + Ri_PAR_f=300.0, + duration=Hour(1), + ), +) +run!(scene) +only(scene_objects(scene; scale=:Leaf)).status.aPPFD ``` """ function PlantSimEngine.run!(::Beer, models, status, meteo, constants, extra=nothing) @@ -95,14 +98,18 @@ using PlantSimEngine.Examples Create a model list with a Beer model, and fit it to the data: ```julia -m = PlantSimEngine.ModelMapping(Beer(0.6), status=(LAI=2.0,)) -meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_f=300.0) -run!(m, meteo) -df = DataFrame(aPPFD=m[:aPPFD][1], LAI=m.status.LAI[1], Ri_PAR_f=meteo.Ri_PAR_f[1]) +scene = Scene( + Object(:leaf; scale=:Leaf, status=Status(LAI=2.0)); + applications=(ModelSpec(Beer(0.6)) |> AppliesTo(One(scale=:Leaf)),), + environment=meteo, +) +run!(scene) +leaf = only(scene_objects(scene; scale=:Leaf)) +df = DataFrame(aPPFD=leaf.status.aPPFD, LAI=leaf.status.LAI, Ri_PAR_f=meteo.Ri_PAR_f[1]) fit(Beer, df) ``` """ function PlantSimEngine.fit(::Type{Beer}, df; J_to_umol=PlantMeteo.Constants().J_to_umol) k = Statistics.mean(-log.(1 .- df.aPPFD ./ (J_to_umol .* df.Ri_PAR_f)) ./ df.LAI) return (k=k,) -end \ No newline at end of file +end diff --git a/examples/ToyAssimGrowthModel.jl b/examples/ToyAssimGrowthModel.jl index cda5f167f..dc33ce282 100644 --- a/examples/ToyAssimGrowthModel.jl +++ b/examples/ToyAssimGrowthModel.jl @@ -73,6 +73,3 @@ function PlantSimEngine.run!(::ToyAssimGrowthModel, models, status, meteo, const # The biomass is the biomass from the previous time-step plus the biomass increment: status.biomass += status.biomass_increment end - -# And optionally, we can tell PlantSimEngine that we can safely parallelize our model over space (objects): -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyAssimGrowthModel}) = PlantSimEngine.IsObjectIndependent() \ No newline at end of file diff --git a/examples/ToyAssimModel.jl b/examples/ToyAssimModel.jl index 7ad0f2ed8..8e6a04997 100644 --- a/examples/ToyAssimModel.jl +++ b/examples/ToyAssimModel.jl @@ -55,8 +55,3 @@ function PlantSimEngine.run!(::ToyAssimModel, models, status, meteo, constants, # The assimilation is simply the absorbed photosynthetic photon flux density (aPPFD) times the light use efficiency (LUE): status.carbon_assimilation = status.aPPFD * models.carbon_assimilation.LUE * status.soil_water_content end - -# And optionally, we can tell PlantSimEngine that we can safely parallelize our model over space (objects): -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyAssimModel}) = PlantSimEngine.IsObjectIndependent() -# And also over time (time-steps): -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyAssimModel}) = PlantSimEngine.IsTimeStepIndependent() \ No newline at end of file diff --git a/examples/ToyCAllocationModel.jl b/examples/ToyCAllocationModel.jl index db6666152..ba9064a8d 100644 --- a/examples/ToyCAllocationModel.jl +++ b/examples/ToyCAllocationModel.jl @@ -69,4 +69,3 @@ function PlantSimEngine.run!(::ToyCAllocationModel, models, status, meteo, const end # Can be parallelized over time-steps, but not objects (we have vectors of values coming from other objects as input): -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyCAllocationModel}) = PlantSimEngine.IsTimeStepIndependent() diff --git a/examples/ToyCBiomassModel.jl b/examples/ToyCBiomassModel.jl index d2dd19d8d..db11843dc 100644 --- a/examples/ToyCBiomassModel.jl +++ b/examples/ToyCBiomassModel.jl @@ -41,6 +41,3 @@ function PlantSimEngine.run!(m::ToyCBiomassModel, models, status, meteo, constan status.carbon_biomass += status.carbon_biomass_increment status.growth_respiration = status.carbon_allocation - status.carbon_biomass_increment end - -# Can be parallelized over organs (but not time-steps, as it is incrementally updating the biomass in the status): -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyCBiomassModel}) = PlantSimEngine.IsObjectIndependent() \ No newline at end of file diff --git a/examples/ToyCDemandModel.jl b/examples/ToyCDemandModel.jl index b376aa83c..77cfe9f06 100644 --- a/examples/ToyCDemandModel.jl +++ b/examples/ToyCDemandModel.jl @@ -52,8 +52,3 @@ function PlantSimEngine.run!(::ToyCDemandModel, models, status, meteo, constants # The carbon demand is simply the biomass under optimal conditions divided by the duration of the development: status.carbon_demand = status.TT * models.carbon_demand.optimal_biomass / models.carbon_demand.development_duration end - -# And optionally, we can tell PlantSimEngine that we can safely parallelize our model over space (objects): -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyCDemandModel}) = PlantSimEngine.IsObjectIndependent() -# And also over time (time-steps): -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyCDemandModel}) = PlantSimEngine.IsTimeStepIndependent() \ No newline at end of file diff --git a/examples/ToyDegreeDays.jl b/examples/ToyDegreeDays.jl index 72d77b70c..e8621cb30 100644 --- a/examples/ToyDegreeDays.jl +++ b/examples/ToyDegreeDays.jl @@ -29,8 +29,3 @@ function PlantSimEngine.run!(m::ToyDegreeDaysCumulModel, models, status, meteo, status.TT = max(0.0, min(meteo.T, m.T_max) - m.T_base) status.TT_cu += status.TT end - -# The computation of ToyDegreeDaysCumulModel dependents on previous values, but it is independent of other objects. -# The default trait is that models are dependent of other time-steps and object. So we need to change the default trait -# for objects: -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyDegreeDaysCumulModel}) = PlantSimEngine.IsObjectIndependent() \ No newline at end of file diff --git a/examples/ToyInternodeEmergence.jl b/examples/ToyInternodeEmergence.jl deleted file mode 100644 index b43cfd58d..000000000 --- a/examples/ToyInternodeEmergence.jl +++ /dev/null @@ -1,36 +0,0 @@ - -# Declaring the process of LAI dynamic: -PlantSimEngine.@process "organ_emergence" verbose = false - -# Declaring the model of LAI dynamic with its parameter values: - -""" - ToyInternodeEmergence(;init_TT=0.0, TT_emergence = 300) - -Computes the organ emergence based on cumulated thermal time since last event. -""" -struct ToyInternodeEmergence <: AbstractOrgan_EmergenceModel - TT_emergence::Float64 -end - -# Defining default values: -ToyInternodeEmergence(; TT_emergence=300.0) = ToyInternodeEmergence(TT_emergence) - -# Defining the inputs and outputs of the model: -PlantSimEngine.inputs_(m::ToyInternodeEmergence) = (TT_cu=-Inf,) -PlantSimEngine.outputs_(m::ToyInternodeEmergence) = (TT_cu_emergence=0.0,) - -# Implementing the actual algorithm by adding a method to the run! function for our model: -function PlantSimEngine.run!(m::ToyInternodeEmergence, models, status, meteo, constants=nothing, sim_object=nothing) - - if length(MultiScaleTreeGraph.children(status.node)) == 1 && status.TT_cu - status.TT_cu_emergence >= m.TT_emergence - # NB: the node can produce one leaf, and one internode only, so we check that it did not produce - # any internode yet. - status_new_internode = add_organ!(status.node, sim_object, "<", :Internode, 2, index=1) - add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1) - - status_new_internode.TT_cu_emergence = status.TT_cu - end - - return nothing -end \ No newline at end of file diff --git a/examples/ToyLAIModel.jl b/examples/ToyLAIModel.jl index 081c8824f..8fba0ff74 100644 --- a/examples/ToyLAIModel.jl +++ b/examples/ToyLAIModel.jl @@ -57,8 +57,6 @@ end # The computation of ToyLAIModel is independant of previous values and other objects. We can add this information as # traits to the model to tell PlantSimEngine that it is safe to run the models in parallel: -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyLAIModel}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyLAIModel}) = PlantSimEngine.IsObjectIndependent() @@ -96,4 +94,3 @@ function PlantSimEngine.run!(m::ToyLAIfromLeafAreaModel, models, status, meteo, end # The computation of ToyLAIfromLeafAreaModel is independant of previous values so we can compute it in parallel over time-steps: -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyLAIfromLeafAreaModel}) = PlantSimEngine.IsTimeStepIndependent() diff --git a/examples/ToyLeafSurfaceModel.jl b/examples/ToyLeafSurfaceModel.jl index b120745c4..268c87ec3 100644 --- a/examples/ToyLeafSurfaceModel.jl +++ b/examples/ToyLeafSurfaceModel.jl @@ -40,8 +40,6 @@ function PlantSimEngine.run!(m::ToyLeafSurfaceModel, models, status, meteo, cons end # Can be parallelized over organs and time-steps: -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyLeafSurfaceModel}) = PlantSimEngine.IsObjectIndependent() -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyLeafSurfaceModel}) = PlantSimEngine.IsTimeStepDependent() @@ -75,6 +73,3 @@ end function PlantSimEngine.run!(m::ToyPlantLeafSurfaceModel, models, status, meteo, constants, extra_args) status.surface = sum(status.leaf_surfaces) end - -# Can be parallelized over time-steps: -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyPlantLeafSurfaceModel}) = PlantSimEngine.IsTimeStepDependent() \ No newline at end of file diff --git a/examples/ToyLightPartitioningModel.jl b/examples/ToyLightPartitioningModel.jl index 3f43c009f..f8fc00c42 100644 --- a/examples/ToyLightPartitioningModel.jl +++ b/examples/ToyLightPartitioningModel.jl @@ -32,5 +32,3 @@ PlantSimEngine.outputs_(::ToyLightPartitioningModel) = (aPPFD=-Inf,) function PlantSimEngine.run!(::ToyLightPartitioningModel, models, status, meteo, constants, extra) status.aPPFD = status.aPPFD_larger_scale * status.surface / status.total_surface end - -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyLightPartitioningModel}) = PlantSimEngine.IsTimeStepIndependent() \ No newline at end of file diff --git a/examples/ToyMultiScalePlantTutorial/ToyPlantHelpers.jl b/examples/ToyMultiScalePlantTutorial/ToyPlantHelpers.jl deleted file mode 100644 index d8c36fdd7..000000000 --- a/examples/ToyMultiScalePlantTutorial/ToyPlantHelpers.jl +++ /dev/null @@ -1,14 +0,0 @@ -function get_root_end_node(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - return MultiScaleTreeGraph.traverse(root, x -> x, symbol=:Root, filter_fun=MultiScaleTreeGraph.isleaf) -end - -function get_roots_count(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - return length(MultiScaleTreeGraph.traverse(root, x -> x, symbol=:Root)) -end - -function get_n_leaves(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - return length(MultiScaleTreeGraph.traverse(root, x -> 1, symbol=:Leaf)) -end diff --git a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation1.jl b/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation1.jl deleted file mode 100644 index 925f366ca..000000000 --- a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation1.jl +++ /dev/null @@ -1,140 +0,0 @@ - -########################################### -# Toy plant model -# Physiologically meaningless but illustrates organ creation -########################################### - -function get_n_leaves(node::MultiScaleTreeGraph.Node) - root = MultiScaleTreeGraph.get_root(node) - nleaves = length(MultiScaleTreeGraph.traverse(root, x -> 1, symbol=:Leaf)) - return nleaves -end - -PlantSimEngine.@process "organ_emergence" verbose = false - -struct ToyCustomInternodeEmergence{T} <: AbstractOrgan_EmergenceModel - TT_emergence::T - carbon_internode_creation_cost::T - leaf_surface_area::T - leaves_max_surface_area::T -end - -ToyCustomInternodeEmergence(; TT_emergence=300.0, carbon_internode_creation_cost=200.0, leaf_surface_area=3.0, leaves_max_surface_area=100.0) = ToyCustomInternodeEmergence(TT_emergence, carbon_internode_creation_cost, leaf_surface_area, leaves_max_surface_area) - -PlantSimEngine.inputs_(m::ToyCustomInternodeEmergence) = (TT_cu=0.0, carbon_stock=0.0) -PlantSimEngine.outputs_(m::ToyCustomInternodeEmergence) = (TT_cu_emergence=0.0, carbon_organ_creation_consumed=0.0) - -function PlantSimEngine.run!(m::ToyCustomInternodeEmergence, models, status, meteo, constants=nothing, sim_object=nothing) - - leaves_surface_area = m.leaf_surface_area * get_n_leaves(status.node) - status.carbon_organ_creation_consumed = 0.0 - - if leaves_surface_area > m.leaves_max_surface_area - return nothing - end - - # if not enough carbon, no organ creation - if status.carbon_stock < m.carbon_internode_creation_cost - return nothing - end - - if length(MultiScaleTreeGraph.children(status.node)) == 2 && - status.TT_cu - status.TT_cu_emergence >= m.TT_emergence - status_new_internode = add_organ!(status.node, sim_object, "<", :Internode, 2, index=1) - add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1) - add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1) - - status_new_internode.TT_cu_emergence = m.TT_emergence - status.TT_cu - status.carbon_organ_creation_consumed = m.carbon_internode_creation_cost - end - - return nothing -end - -########################## -### Model accumulating carbon resources -########################## - -PlantSimEngine.@process "resource_stock_computation" verbose = false - -struct ToyStockComputationModel <: AbstractResource_Stock_ComputationModel -end - -PlantSimEngine.inputs_(::ToyStockComputationModel) = - (carbon_captured=0.0, carbon_organ_creation_consumed=0.0) - -PlantSimEngine.outputs_(::ToyStockComputationModel) = (carbon_stock=-Inf,) - -function PlantSimEngine.run!(m::ToyStockComputationModel, models, status, meteo, constants=nothing, extra=nothing) - status.carbon_stock += sum(status.carbon_captured) - sum(status.carbon_organ_creation_consumed) -end - -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyStockComputationModel}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyStockComputationModel}) = PlantSimEngine.IsObjectIndependent() - -######################## -## Leaf model capturing some arbitrary carbon quantity -######################## - -PlantSimEngine.@process "leaf_carbon_capture" verbose = false - -struct ToyLeafCarbonCaptureModel <: AbstractLeaf_Carbon_CaptureModel end - -function PlantSimEngine.inputs_(::ToyLeafCarbonCaptureModel) - NamedTuple()#(TT_cu=-Inf) -end - -function PlantSimEngine.outputs_(::ToyLeafCarbonCaptureModel) - (carbon_captured=0.0,) -end - -function PlantSimEngine.run!(::ToyLeafCarbonCaptureModel, models, status, meteo, constants, extra) - # very crude approximation with LAI of 1 and constant aPPFD - status.carbon_captured = 200.0 * (1.0 - exp(-0.2)) -end - -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyLeafCarbonCaptureModel}) = PlantSimEngine.IsObjectIndependent() -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyLeafCarbonCaptureModel}) = PlantSimEngine.IsTimeStepIndependent() - -mapping = PlantSimEngine.ModelMapping( - :Scene => ToyDegreeDaysCumulModel(), - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyStockComputationModel(), - mapped_variables=[ - :carbon_captured => [:Leaf], - :carbon_organ_creation_consumed => [:Internode] - ], - ), - Status(carbon_stock=0.0) - ), - :Internode => ( - PlantSimEngine.MultiScaleModel( - model=ToyCustomInternodeEmergence(),#TT_emergence=20.0), - mapped_variables=[:TT_cu => (:Scene => :TT_cu), - PreviousTimeStep(:carbon_stock) => (:Plant => :carbon_stock)], - ), - Status(carbon_organ_creation_consumed=0.0), - ), - :Leaf => (ToyLeafCarbonCaptureModel(),), -) - -mtg = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) -#MultiScaleTreeGraph.Node(mtg, MultiScaleTreeGraph.NodeMTG("/", :Soil, 1, 1)) -plant = MultiScaleTreeGraph.Node(mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - -internode1 = MultiScaleTreeGraph.Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) -MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) -MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - -internode2 = MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("<", :Internode, 1, 2)) -MultiScaleTreeGraph.Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) -MultiScaleTreeGraph.Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - - -meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18) - -outs = run!(mtg, mapping, meteo_day) -mtg - -length(MultiScaleTreeGraph.traverse(mtg, x -> x, symbol=:Leaf)) diff --git a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl b/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl deleted file mode 100644 index 38a1cd2e5..000000000 --- a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl +++ /dev/null @@ -1,221 +0,0 @@ -########################################### -# Toy plant model -# Physiologically and physically completely meaningless -# (no dimension for units, arbitrary values, stores water and carbon in abstract stocks, -# arbitrary max leaf count and root length, constant and non-coupled photosynthesis and water absorption, ...) -# But it should illustrate the basics of simulating a growing multiscale plant with PlantSimEngine's model approach -########################################### - -include(joinpath(@__DIR__, "ToyPlantHelpers.jl")) - -PlantSimEngine.@process "organ_emergence" verbose = false - -struct ToyCustomInternodeEmergence{T} <: AbstractOrgan_EmergenceModel - TT_emergence::T - carbon_internode_creation_cost::T - leaf_surface_area::T - leaves_max_surface_area::T - water_leaf_threshold::T -end - -ToyCustomInternodeEmergence(; TT_emergence=300.0, carbon_internode_creation_cost=200.0, leaf_surface_area=3.0, leaves_max_surface_area=100.0, - water_leaf_threshold=30.0) = ToyCustomInternodeEmergence(TT_emergence, carbon_internode_creation_cost, leaf_surface_area, leaves_max_surface_area, water_leaf_threshold) - -PlantSimEngine.inputs_(m::ToyCustomInternodeEmergence) = (TT_cu=0.0, water_stock=0.0, carbon_stock=0.0) -PlantSimEngine.outputs_(m::ToyCustomInternodeEmergence) = (TT_cu_emergence=0.0, carbon_organ_creation_consumed=0.0) - -function PlantSimEngine.run!(m::ToyCustomInternodeEmergence, models, status, meteo, constants=nothing, sim_object=nothing) - - leaves_surface_area = m.leaf_surface_area * get_n_leaves(status.node) - status.carbon_organ_creation_consumed = 0.0 - - if leaves_surface_area > m.leaves_max_surface_area - return nothing - end - - # if water levels are low, prioritise roots - if status.water_stock < m.water_leaf_threshold - return nothing - end - - # if not enough carbon, no organ creation - if status.carbon_stock < m.carbon_internode_creation_cost - return nothing - end - - if length(MultiScaleTreeGraph.children(status.node)) == 2 && - status.TT_cu - status.TT_cu_emergence >= m.TT_emergence - status_new_internode = add_organ!(status.node, sim_object, "<", :Internode, 2, index=1) - add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1) - add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1) - - status_new_internode.TT_cu_emergence = m.TT_emergence - status.TT_cu - status.carbon_organ_creation_consumed = m.carbon_internode_creation_cost - end - - return nothing -end - -############################ -# Naive water absorption model -# Absorbs precipitation water depending on quantity of roots -############################ -PlantSimEngine.@process "water_absorption" verbose = false - -struct ToyWaterAbsorptionModel <: AbstractWater_AbsorptionModel -end - -PlantSimEngine.inputs_(::ToyWaterAbsorptionModel) = (root_water_assimilation=1.0,) -PlantSimEngine.outputs_(::ToyWaterAbsorptionModel) = (water_absorbed=0.0,) - -function PlantSimEngine.run!(m::ToyWaterAbsorptionModel, models, status, meteo, constants=nothing, extra=nothing) - #root_end = get_root_end_node(status.node) - #root_len = root_end[:Root_len] - status.water_absorbed = meteo.Precipitations * status.root_water_assimilation #* root_len -end - -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyWaterAbsorptionModel}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyWaterAbsorptionModel}) = PlantSimEngine.IsObjectIndependent() - - -########################## -### Root growth : when water stocks are low, expand root -########################## - -PlantSimEngine.@process "root_growth" verbose = false - -struct ToyRootGrowthModel{T} <: AbstractRoot_GrowthModel - water_threshold::T - carbon_root_creation_cost::T - root_max_len::Int -end - -PlantSimEngine.inputs_(::ToyRootGrowthModel) = (water_stock=0.0, carbon_stock=0.0,) -PlantSimEngine.outputs_(::ToyRootGrowthModel) = (carbon_root_creation_consumed=0.0,) - -function PlantSimEngine.run!(m::ToyRootGrowthModel, models, status, meteo, constants=nothing, extra=nothing) - if status.water_stock < m.water_threshold && status.carbon_stock > m.carbon_root_creation_cost - - root_end = get_root_end_node(status.node) - - if length(root_end) != 1 - throw(AssertionError("Couldn't find MTG leaf node with symbol \"Root\"")) - end - root_len = get_roots_count(root_end[1]) - if root_len < m.root_max_len - st = add_organ!(root_end[1], extra, "<", :Root, 2, index=1) - status.carbon_root_creation_consumed = m.carbon_root_creation_cost - end - else - status.carbon_root_creation_consumed = 0.0 - end -end - -########################## -### Model accumulating carbon and water resources -########################## - -PlantSimEngine.@process "resource_stock_computation" verbose = false - -struct ToyStockComputationModel <: AbstractResource_Stock_ComputationModel -end -#status.water_stock += meteo.precipitations * root_water_assimilation_ratio - -PlantSimEngine.inputs_(::ToyStockComputationModel) = - (water_absorbed=0.0, carbon_captured=0.0, carbon_organ_creation_consumed=0.0, carbon_root_creation_consumed=0.0) - -PlantSimEngine.outputs_(::ToyStockComputationModel) = (water_stock=-Inf, carbon_stock=-Inf) - -function PlantSimEngine.run!(m::ToyStockComputationModel, models, status, meteo, constants=nothing, extra=nothing) - status.water_stock += sum(status.water_absorbed) #- status.water_transpiration - status.carbon_stock += sum(status.carbon_captured) - sum(status.carbon_organ_creation_consumed) - sum(status.carbon_root_creation_consumed) - - if status.water_stock < 0.0 - status.water_stock = 0.0 - end -end - -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyStockComputationModel}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyStockComputationModel}) = PlantSimEngine.IsObjectIndependent() - -######################## -## Leaf model capturing some arbitrary carbon quantity -######################## - -PlantSimEngine.@process "leaf_carbon_capture" verbose = false - -struct ToyLeafCarbonCaptureModel <: AbstractLeaf_Carbon_CaptureModel end - -function PlantSimEngine.inputs_(::ToyLeafCarbonCaptureModel) - NamedTuple()#(TT_cu=-Inf) -end - -function PlantSimEngine.outputs_(::ToyLeafCarbonCaptureModel) - (carbon_captured=0.0,) -end - -function PlantSimEngine.run!(::ToyLeafCarbonCaptureModel, models, status, meteo, constants, extra) - # very crude approximation with LAI of 1 and constant aPPFD - status.carbon_captured = 200.0 * (1.0 - exp(-0.2)) -end - -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyLeafCarbonCaptureModel}) = PlantSimEngine.IsObjectIndependent() -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyLeafCarbonCaptureModel}) = PlantSimEngine.IsTimeStepIndependent() - -mapping = PlantSimEngine.ModelMapping( - :Scene => ToyDegreeDaysCumulModel(), - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyStockComputationModel(), - mapped_variables=[ - :carbon_captured => [:Leaf], - :water_absorbed => [:Root], - :carbon_root_creation_consumed => [:Root], - :carbon_organ_creation_consumed => [:Internode]], - ), - Status(water_stock=0.0, carbon_stock=0.0) - ), - :Internode => ( - PlantSimEngine.MultiScaleModel( - model=ToyCustomInternodeEmergence(),#TT_emergence=20.0), - mapped_variables=[:TT_cu => (:Scene => :TT_cu), - PreviousTimeStep(:water_stock) => (:Plant => :water_stock), - PreviousTimeStep(:carbon_stock) => (:Plant => :carbon_stock)], - ), - Status(carbon_organ_creation_consumed=0.0), - ), - :Root => (PlantSimEngine.MultiScaleModel( - model=ToyRootGrowthModel(10.0, 50.0, 10), - mapped_variables=[PreviousTimeStep(:carbon_stock) => (:Plant => :carbon_stock), - PreviousTimeStep(:water_stock) => (:Plant => :water_stock)], - ), - ToyWaterAbsorptionModel(), - Status(carbon_root_creation_consumed=0.0, root_water_assimilation=1.0), - ), - :Leaf => (ToyLeafCarbonCaptureModel(),), -) - -mtg = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - -plant = MultiScaleTreeGraph.Node(mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - -internode1 = MultiScaleTreeGraph.Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) -MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) -MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - -internode2 = MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("<", :Internode, 1, 2)) -MultiScaleTreeGraph.Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) -MultiScaleTreeGraph.Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - -plant_root_start = MultiScaleTreeGraph.Node( - plant, - MultiScaleTreeGraph.NodeMTG("+", :Root, 1, 3), -) - -meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18) - -outs = run!(mtg, mapping, meteo_day) -mtg - - -length(MultiScaleTreeGraph.traverse(mtg, x -> x, symbol=:Leaf)) diff --git a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl b/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl deleted file mode 100644 index 4f68cbf16..000000000 --- a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl +++ /dev/null @@ -1,237 +0,0 @@ -########################################### -# Toy plant model with an updated decision model for organ growth -# Physiologically and physically completely meaningless -# (no dimension for units, arbitrary values, stores water and carbon in abstract stocks, -# arbitrary max leaf count and root length, constant and non-coupled photosynthesis and water absorption, ...) -# But it should illustrate the basics of simulating a growing multiscale plant with PlantSimEngine's model approach -########################################### - -include(joinpath(@__DIR__, "ToyPlantHelpers.jl")) - -PlantSimEngine.@process "organ_emergence" verbose = false - -struct ToyCustomInternodeEmergence{T} <: AbstractOrgan_EmergenceModel - TT_emergence::T - carbon_internode_creation_cost::T - leaf_surface_area::T - leaves_max_surface_area::T - water_leaf_threshold::T -end - -ToyCustomInternodeEmergence(; TT_emergence=300.0, carbon_internode_creation_cost=200.0, leaf_surface_area=3.0, leaves_max_surface_area=100.0, - water_leaf_threshold=30.0) = ToyCustomInternodeEmergence(TT_emergence, carbon_internode_creation_cost, leaf_surface_area, leaves_max_surface_area, water_leaf_threshold) - -PlantSimEngine.inputs_(m::ToyCustomInternodeEmergence) = (TT_cu=0.0, water_stock=0.0, carbon_stock=0.0, carbon_root_creation_consumed=0.0) -PlantSimEngine.outputs_(m::ToyCustomInternodeEmergence) = (TT_cu_emergence=0.0, carbon_organ_creation_consumed=0.0) - -function PlantSimEngine.run!(m::ToyCustomInternodeEmergence, models, status, meteo, constants=nothing, sim_object=nothing) - - leaves_surface_area = m.leaf_surface_area * get_n_leaves(status.node) - status.carbon_organ_creation_consumed = 0.0 - - if leaves_surface_area > m.leaves_max_surface_area - return nothing - end - - # if water levels are low, prioritise roots - if status.water_stock < m.water_leaf_threshold - return nothing - end - - # take into account that the stock may already be depleted - carbon_stock_updated_after_roots = status.carbon_stock - status.carbon_root_creation_consumed - - # if not enough carbon, no organ creation - if carbon_stock_updated_after_roots < m.carbon_internode_creation_cost - return nothing - end - - if length(MultiScaleTreeGraph.children(status.node)) == 2 && - status.TT_cu - status.TT_cu_emergence >= m.TT_emergence - status_new_internode = add_organ!(status.node, sim_object, "<", :Internode, 2, index=1) - add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1) - add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1) - - status_new_internode.TT_cu_emergence = m.TT_emergence - status.TT_cu - status.carbon_organ_creation_consumed = m.carbon_internode_creation_cost - end - - return nothing -end - -############################ -# Naive water absorption model -# Absorbs precipitation water depending on quantity of roots -############################ -PlantSimEngine.@process "water_absorption" verbose = false - -struct ToyWaterAbsorptionModel <: AbstractWater_AbsorptionModel -end - -PlantSimEngine.inputs_(::ToyWaterAbsorptionModel) = (root_water_assimilation=1.0,) -PlantSimEngine.outputs_(::ToyWaterAbsorptionModel) = (water_absorbed=0.0,) - -function PlantSimEngine.run!(m::ToyWaterAbsorptionModel, models, status, meteo, constants=nothing, extra=nothing) - #root_end = get_root_end_node(status.node) - #root_len = root_end[:Root_len] - status.water_absorbed = meteo.Precipitations * status.root_water_assimilation #* root_len -end - -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToyWaterAbsorptionModel}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyWaterAbsorptionModel}) = PlantSimEngine.IsObjectIndependent() - - -########################## -### Root growth : when water stocks are low, expand root -########################## - -PlantSimEngine.@process "root_growth" verbose = false - -struct ToyRootGrowthModel{T} <: AbstractRoot_GrowthModel - carbon_root_creation_cost::T - root_max_len::Int -end - -PlantSimEngine.inputs_(::ToyRootGrowthModel) = NamedTuple() -PlantSimEngine.outputs_(::ToyRootGrowthModel) = (carbon_root_creation_consumed=0.0,) - -function PlantSimEngine.run!(m::ToyRootGrowthModel, models, status, meteo, constants=nothing, extra=nothing) - status.carbon_root_creation_consumed = 0.0 - - root_end = get_root_end_node(status.node) - - if length(root_end) != 1 - throw(AssertionError("Couldn't find MTG leaf node with symbol \"Root\"")) - end - - root_len = get_roots_count(root_end[1]) - if root_len < m.root_max_len - st = add_organ!(root_end[1], extra, "<", :Root, 2, index=1) - status.carbon_root_creation_consumed = m.carbon_root_creation_cost - end -end - -########################## -### Decision model controlling the root growth model -########################## -PlantSimEngine.@process "root_growth_decision" verbose = false - -struct ToyRootGrowthDecisionModel{T} <: AbstractRoot_Growth_DecisionModel - water_threshold::T - carbon_root_creation_cost::T -end - -PlantSimEngine.inputs_(::ToyRootGrowthDecisionModel) = - (water_stock=0.0, carbon_stock=0.0) - -PlantSimEngine.outputs_(::ToyRootGrowthDecisionModel) = NamedTuple() - -PlantSimEngine.dep(::ToyRootGrowthDecisionModel) = (root_growth=AbstractRoot_GrowthModel => [:Root],) - -function PlantSimEngine.run!(m::ToyRootGrowthDecisionModel, models, status, meteo, constants=nothing, extra=nothing) - - if status.water_stock < m.water_threshold && status.carbon_stock > m.carbon_root_creation_cost - status_Root = extra.statuses[:Root][1] - PlantSimEngine.run!(extra.models[:Root].root_growth, models, status_Root, meteo, constants, extra) - end -end - - -########################## -### Model accumulating carbon and water resources -########################## - -PlantSimEngine.@process "resource_stock_computation" verbose = false - -struct ToyStockComputationModel <: AbstractResource_Stock_ComputationModel -end - -PlantSimEngine.inputs_(::ToyStockComputationModel) = - (water_absorbed=0.0, carbon_captured=0.0, carbon_organ_creation_consumed=0.0, carbon_root_creation_consumed=0.0) - -PlantSimEngine.outputs_(::ToyStockComputationModel) = (water_stock=-Inf, carbon_stock=-Inf) - -function PlantSimEngine.run!(m::ToyStockComputationModel, models, status, meteo, constants=nothing, extra=nothing) - status.water_stock += sum(status.water_absorbed) - status.carbon_stock += sum(status.carbon_captured) - sum(status.carbon_organ_creation_consumed) - sum(status.carbon_root_creation_consumed) -end - - -######################## -## Leaf model capturing some arbitrary carbon quantity -######################## - -PlantSimEngine.@process "leaf_carbon_capture" verbose = false - -struct ToyLeafCarbonCaptureModel <: AbstractLeaf_Carbon_CaptureModel end - -function PlantSimEngine.inputs_(::ToyLeafCarbonCaptureModel) - NamedTuple() -end - -function PlantSimEngine.outputs_(::ToyLeafCarbonCaptureModel) - (carbon_captured=0.0,) -end - -function PlantSimEngine.run!(::ToyLeafCarbonCaptureModel, models, status, meteo, constants, extra) - # very crude approximation with LAI of 1 and constant aPPFD - status.carbon_captured = 200.0 * (1.0 - exp(-0.2)) -end - - -mapping = PlantSimEngine.ModelMapping( - :Scene => ToyDegreeDaysCumulModel(), - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyStockComputationModel(), - mapped_variables=[ - :carbon_captured => [:Leaf], - :water_absorbed => [:Root], - PreviousTimeStep(:carbon_root_creation_consumed) => (:Root => :carbon_root_creation_consumed), - PreviousTimeStep(:carbon_organ_creation_consumed) => [:Internode], - ], - ), - ToyRootGrowthDecisionModel(10.0, 50.0), - Status(water_stock=0.0, carbon_stock=0.0) - ), - :Internode => ( - PlantSimEngine.MultiScaleModel( - model=ToyCustomInternodeEmergence(),#TT_emergence=20.0), - mapped_variables=[:TT_cu => (:Scene => :TT_cu), - :water_stock => (:Plant => :water_stock), - :carbon_stock => (:Plant => :carbon_stock), - :carbon_root_creation_consumed => (:Root => :carbon_root_creation_consumed)], - ), - Status(carbon_organ_creation_consumed=0.0), - ), - :Root => (ToyRootGrowthModel(50.0, 10), - ToyWaterAbsorptionModel(), - Status(carbon_root_creation_consumed=0.0, root_water_assimilation=1.0), - ), - :Leaf => (ToyLeafCarbonCaptureModel(),), -) - -mtg = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - -plant = MultiScaleTreeGraph.Node(mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - -internode1 = MultiScaleTreeGraph.Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) -MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) -MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - -internode2 = MultiScaleTreeGraph.Node(internode1, MultiScaleTreeGraph.NodeMTG("<", :Internode, 1, 2)) -MultiScaleTreeGraph.Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) -MultiScaleTreeGraph.Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - -plant_root_start = MultiScaleTreeGraph.Node( - plant, - MultiScaleTreeGraph.NodeMTG("+", :Root, 1, 3), -) - -meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18) - -outs = run!(mtg, mapping, meteo_day) -mtg - - -length(MultiScaleTreeGraph.traverse(mtg, x -> x, symbol=:Leaf)) diff --git a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation4.jl b/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation4.jl deleted file mode 100644 index 1684ab44e..000000000 --- a/examples/ToyMultiScalePlantTutorial/ToyPlantSimulation4.jl +++ /dev/null @@ -1,148 +0,0 @@ -########################################### -# Toy plant model MTG visualisation using PlantGeom -########################################### -using PlantSimEngine - -using MultiScaleTreeGraph -using PlantSimEngine.Examples -using Pkg -Pkg.add("CSV") -using CSV -include("ToyPlantSimulation3.jl") - -using Plots -using PlantGeom -# reusing the mtg from part 3: -RecipesBase.plot(mtg) - -#= -using GLMakie -#using CairoMakie -using PlantGeom - -PlantGeom.diagram(mtg)=# - - -using PlantGeom.Meshes - -# Internodes and roots will use a cylinder as a mesh - -cylinder() = Meshes.CylinderSurface(1.0) |> Meshes.discretize |> Meshes.simplexify - -refmesh_internode = PlantGeom.RefMesh("Internode", cylinder()) -refmesh_root = PlantGeom.RefMesh("Root", cylinder()) - -# Leaves and petioles are a single mesh, read from a .ply file - -Pkg.add("PlyIO") -using PlyIO -function read_ply(fname) - ply = PlyIO.load_ply(fname) - x = ply["vertex"]["x"] - y = ply["vertex"]["y"] - z = ply["vertex"]["z"] - points = Meshes.Point.(x, y, z) - connec = [Meshes.connect(Tuple(c .+ 1)) for c in ply["face"]["vertex_indices"]] - Meshes.SimpleMesh(points, connec) -end - -leaf_ply = read_ply("examples/leaf_with_petiole.ply") -refmesh_leaf = PlantGeom.RefMesh("Leaf", leaf_ply) - -Pkg.add("TransformsBase") -Pkg.add("Rotations") -#using PlantGeom.TranformsBase -import TransformsBase: → -import Rotations: RotY, RotZ, RotX -# Add the geometry to the MTG, with transformations -function add_geometry!(mtg, refmesh_internode) - - # incremental offset - internode_height = 0.0 - - # relative scale of the base mesh - internode_width = 0.5 - - # length of the base mesh - internode_length = 1.0 - - traverse!(mtg) do node - if symbol(node) == :Internode - # Set to scale, then translate by the total height - mesh_transformation = Meshes.Scale(internode_width, internode_width, internode_length) → Meshes.Translate(0.0, 0.0, internode_height) - node.geometry = PlantGeom.Geometry(ref_mesh=refmesh_internode, transformation=mesh_transformation) - - internode_height += internode_length - end - end -end - -add_geometry!(mtg, refmesh_internode) - -# Visualize the mesh -using GLMakie -viz(mtg) - -function add_geometry!(mtg, refmesh_internode, refmesh_root, refmesh_leaf) - - # incremental offset - internode_height = 0.0 - root_depth = 0.0 - - # relative scale of the base mesh - internode_width = 0.5 - root_width = 0.2 - - # length of the base mesh - internode_length = 1.0 - root_length = 1.0 - - # ad hoc value to adjust the base mesh to the scene scale - leaf_mesh_scale = 25 - leaf_scale_width = 0.4*leaf_mesh_scale - leaf_scale_height = 0.4*leaf_mesh_scale - - # Helpers to make the leaves opposite decussate - leaf_rotation = MathConstants.pi / 2.0 - i = 0 - - traverse!(mtg) do node - if symbol(node) == :Internode - # Set to scale, then translate by the total height - mesh_transformation = Meshes.Scale(internode_width, internode_width, internode_length) → Meshes.Translate(0.0, 0.0, internode_height) - node.geometry = PlantGeom.Geometry(ref_mesh=refmesh_internode, transformation=mesh_transformation) - - internode_height += node_length - - # Leaves are placed relatively to the parent internode - for chnode in children(node) - if symbol(chnode) == :Leaf - # Leaves are placed halfway along the the parent internode - mesh_transformation = Meshes.Scale(leaf_scale_width, leaf_scale_width, leaf_scale_height) → Meshes.Rotate(RotX(-MathConstants.pi / 6.0)) → Meshes.Translate(0.0, -internode_width, internode_height - internode_length / 2.0) → Meshes.Rotate(RotZ(leaf_rotation)) - chnode.geometry = PlantGeom.Geometry(ref_mesh=refmesh_leaf, transformation=mesh_transformation) - # Set the second leaf in a pair opposite to the first one => add a 180° rotation - leaf_rotation += MathConstants.pi - end - end - - # Opposite decussate => 90° rotation between pairs - i += 1 - if i % 2 == 0 - leaf_rotation = MathConstants.pi / 2.0 - else - leaf_rotation = MathConstants.pi - end - - elseif symbol(node) == :Root - mesh_transformation = Meshes.Scale(root_width, root_width, root_length) → Meshes.Translate(0.0, 0.0, root_depth) → Meshes.Rotate(RotZ(MathConstants.pi)) - node.geometry = PlantGeom.Geometry(ref_mesh=refmesh_root, transformation=mesh_transformation) - root_depth -= root_length - end - end -end - -add_geometry!(mtg, refmesh_internode, refmesh_root, refmesh_leaf) - -# Visualize the mesh -using GLMakie -viz(mtg) \ No newline at end of file diff --git a/examples/ToyRUEGrowthModel.jl b/examples/ToyRUEGrowthModel.jl index c3db5cb6b..ab21356f8 100644 --- a/examples/ToyRUEGrowthModel.jl +++ b/examples/ToyRUEGrowthModel.jl @@ -48,6 +48,5 @@ function PlantSimEngine.run!(::ToyRUEGrowthModel, models, status, meteo, constan end # And optionally, we can tell PlantSimEngine that we can safely parallelize our model over space (objects): -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToyRUEGrowthModel}) = PlantSimEngine.IsObjectIndependent() # Note that this model cannot be parallelized over time because we use the biomass from the previous time-step. \ No newline at end of file diff --git a/examples/ToySingleToMultiScale.jl b/examples/ToySingleToMultiScale.jl deleted file mode 100644 index e14fb38c7..000000000 --- a/examples/ToySingleToMultiScale.jl +++ /dev/null @@ -1,120 +0,0 @@ -############################## -### Example single- to multi-scale conversion -############################## - -# Environment setup -using CSV -using DataFrames -using PlantSimEngine -using PlantMeteo -using PlantSimEngine.Examples -using MultiScaleTreeGraph - -# Weather data for all simulations -meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18) - -############################## -### Single-scale simulation -############################## - -models_singlescale = PlantSimEngine.ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(0.2), - status=(TT_cu=cumsum(meteo_day.TT),), -) - -outputs_singlescale = run!(models_singlescale, meteo_day) - -############################## -#### Direct translation of the single-scale simulation -############################## -mapping_pseudo_multiscale = PlantSimEngine.ModelMapping( - :Plant => ( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(0.2), - Status(TT_cu=cumsum(meteo_day.TT),) - ), -) - -mtg = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Plant, 1, 0),) - -# will generate an error as vectors can't be directly passed into a Status in multi-scale simulations -out_pseudo_multiscale = run!(mtg, mapping_pseudo_multiscale, meteo_day) - -############################## -#### Ad Hoc Cumulated Thermal Time Model -############################## - -PlantSimEngine.@process "tt_cu" verbose = false - -struct ToyTt_CuModel <: AbstractTt_CuModel -end - -function PlantSimEngine.run!(::ToyTt_CuModel, models, status, meteo, constants, extra=nothing) - status.TT_cu += - meteo.TT -end - -function PlantSimEngine.inputs_(::ToyTt_CuModel) - NamedTuple() -end - -function PlantSimEngine.outputs_(::ToyTt_CuModel) - (TT_cu=-Inf,) -end - -############################## -#### Actual multiscale version of the single-scale simulation -############################## - -mapping_multiscale = PlantSimEngine.ModelMapping( - :Scene => ( - ToyTt_CuModel(), - Status(TT_cu=0.0), - ), - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[ - :TT_cu => (:Scene => :TT_cu), - ], - ), - Beer(0.5), - ToyRUEGrowthModel(0.2), - ), -) - -# We now need two nodes for our MTG -mtg_multiscale = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) -plant = MultiScaleTreeGraph.Node(mtg_multiscale, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) -outputs_multiscale = run!(mtg_multiscale, mapping_multiscale, meteo_day) - -############################## -#### Output comparison -############################## - -computed_TT_cu_multiscale = collect(Base.Iterators.flatten(outputs_multiscale[:Scene][:TT_cu])) - -is_approx_equal_1 = true - -for i in 1:length(computed_TT_cu_multiscale) - if !(computed_TT_cu_multiscale[i] ≈ outputs_singlescale.TT_cu[i]) - is_approx_equal_1 = false - break - end -end - -is_approx_equal_1 - -is_approx_equal_2 = length(unique(computed_TT_cu_multiscale .≈ outputs_singlescale.TT_cu)) == 1 - - -# Note : it is also possible to get the weather data length via PlantSimEngine.get_nsteps(meteo_day) -# instead of checking for array length - -is_perfectly_equal = length(unique(computed_TT_cu_multiscale .== outputs_singlescale.TT_cu)) == 1 - -(computed_TT_cu_multiscale.==outputs_singlescale.TT_cu)[104] -(computed_TT_cu_multiscale.==outputs_singlescale.TT_cu)[105] diff --git a/examples/ToySoilModel.jl b/examples/ToySoilModel.jl index 7d0c20b3a..8ff602049 100644 --- a/examples/ToySoilModel.jl +++ b/examples/ToySoilModel.jl @@ -31,8 +31,3 @@ PlantSimEngine.outputs_(::ToySoilWaterModel) = (soil_water_content=-Inf,) function PlantSimEngine.run!(m::ToySoilWaterModel, models, status, meteo, constants=nothing, extra=nothing) status.soil_water_content = rand(m.values) end - -# The computation of ToySoilWaterModel is independant of previous values and other objects. We can add this information as -# traits to the model to tell PlantSimEngine that it is safe to run the models in parallel: -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToySoilWaterModel}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:ToySoilWaterModel}) = PlantSimEngine.IsObjectIndependent() \ No newline at end of file diff --git a/examples/benchmark.jl b/examples/benchmark.jl deleted file mode 100644 index 8f3182f80..000000000 --- a/examples/benchmark.jl +++ /dev/null @@ -1,31 +0,0 @@ -#]add BenchmarkTools - -using BenchmarkTools -using PlantSimEngine, PlantMeteo, DataFrames, CSV, Dates, Statistics -# using PlantSimEngine.Examples - -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Day) -models = PlantSimEngine.ModelMapping( - ToyLAIModel(), - status=(TT_cu=cumsum(meteo_day.TT),), -) - -# Match the warning on the executor, the default is ThreadedEx() but ToyRUEGrowthModel can't be run in parallel: -time_run = @benchmark run!($models, $meteo_day) - -median_time_ns = median(time_run.times) / nrow(meteo_day) - -# If we provide a serial executor, it works without a warning: -time_run_seq = @benchmark run!($models, $meteo_day, executor=$(SequentialEx())) -median_time_seq_ns = median(time_run_seq.times) / nrow(meteo_day) - -# Coupled model: -models_coupled = PlantSimEngine.ModelMapping( - ToyLAIModel(), - Beer(0.5), - status=(TT_cu=cumsum(meteo_day.TT),), -) - -# Match the warning on the executor, the default is ThreadedEx() but ToyRUEGrowthModel can't be run in parallel: -time_run_coupled = @benchmark run!($models_coupled, $meteo_day) -median_time_coupled_ns = median(time_run_coupled.times) / nrow(meteo_day) diff --git a/examples/dummy.jl b/examples/dummy.jl index 4a4482a34..f455bebb9 100644 --- a/examples/dummy.jl +++ b/examples/dummy.jl @@ -18,8 +18,6 @@ PlantSimEngine.outputs_(::Process1Model) = (var3=-Inf,) function PlantSimEngine.run!(::Process1Model, models, status, meteo, constants=nothing, extra=nothing) status.var3 = models.process1.a + status.var1 * status.var2 end -PlantSimEngine.TimeStepDependencyTrait(::Type{<:Process1Model}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:Process1Model}) = PlantSimEngine.IsObjectIndependent() # Defining a 2nd process called "process2", and a model @@ -42,8 +40,6 @@ function PlantSimEngine.run!(::Process2Model, models, status, meteo, constants=n status.var4 = status.var3 * 2.0 status.var5 = status.var4 + 1.0 * meteo.T + 2.0 * meteo.Wind + 3.0 * meteo.Rh end -PlantSimEngine.TimeStepDependencyTrait(::Type{<:Process2Model}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:Process2Model}) = PlantSimEngine.IsObjectIndependent() # Defining a 3d process called "process3", and a model # that implements an algorithm, and that depends on the second one (and @@ -68,8 +64,6 @@ function PlantSimEngine.run!(::Process3Model, models, status, meteo, constants=n status.var4 = status.var4 * 2.0 status.var6 = status.var5 + status.var4 end -PlantSimEngine.TimeStepDependencyTrait(::Type{<:Process3Model}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:Process3Model}) = PlantSimEngine.IsObjectIndependent() # Defining a 4th process called "process4", and a model # that implements an algorithm, and that computes the @@ -91,8 +85,6 @@ function PlantSimEngine.run!(::Process4Model, models, status, meteo, constants=n status.var1 = status.var0 + 0.01 status.var2 = status.var1 + 0.02 end -PlantSimEngine.TimeStepDependencyTrait(::Type{<:Process4Model}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:Process4Model}) = PlantSimEngine.IsObjectIndependent() # Defining a 5th process called "process5", and a model # that implements an algorithm, and that computes other @@ -111,8 +103,6 @@ PlantSimEngine.outputs_(::Process5Model) = (var7=-Inf,) function PlantSimEngine.run!(::Process5Model, models, status, meteo, constants=nothing, extra=nothing) status.var7 = status.var5 * status.var6 end -PlantSimEngine.TimeStepDependencyTrait(::Type{<:Process5Model}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:Process5Model}) = PlantSimEngine.IsObjectIndependent() # Defining a 6th process called "process6", and a model @@ -133,8 +123,6 @@ PlantSimEngine.outputs_(::Process6Model) = (var8=-Inf,) function PlantSimEngine.run!(::Process6Model, models, status, meteo, constants=nothing, extra=nothing) status.var8 = status.var7 + 1.0 end -PlantSimEngine.TimeStepDependencyTrait(::Type{<:Process6Model}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:Process6Model}) = PlantSimEngine.IsObjectIndependent() # Defining a 7th process called "process7", and a model # that depends on nothing but var0 so it is independant. @@ -155,5 +143,3 @@ PlantSimEngine.outputs_(::Process7Model) = (var9=-Inf,) function PlantSimEngine.run!(::Process7Model, models, status, meteo, constants=nothing, extra=nothing) status.var9 = status.var0 + 1.0 end -PlantSimEngine.TimeStepDependencyTrait(::Type{<:Process7Model}) = PlantSimEngine.IsTimeStepIndependent() -PlantSimEngine.ObjectDependencyTrait(::Type{<:Process7Model}) = PlantSimEngine.IsObjectIndependent() diff --git a/examples/plantbiophysics_subsample/FvCB.jl b/examples/plantbiophysics_subsample/FvCB.jl index cd23055a7..825010885 100644 --- a/examples/plantbiophysics_subsample/FvCB.jl +++ b/examples/plantbiophysics_subsample/FvCB.jl @@ -3,7 +3,7 @@ @process "photosynthesis" verbose = false # Default policy for assimilation rates when consumed at coarser clocks. -# Mapping-level InputBindings policy still overrides this default when provided. +# An explicit scene `Inputs(...)` policy overrides this default. PlantSimEngine.output_policy(::Type{<:AbstractPhotosynthesisModel}) = (A=PlantSimEngine.Integrate(PlantMeteo.DurationSumReducer()),) @@ -47,8 +47,6 @@ end Base.eltype(x::Fvcb) = typeof(x).parameters[1] PlantSimEngine.dep(::Fvcb) = (stomatal_conductance=AbstractStomatal_ConductanceModel,) -PlantSimEngine.ObjectDependencyTrait(::Type{<:Fvcb}) = PlantSimEngine.IsObjectIndependent() -PlantSimEngine.TimeStepDependencyTrait(::Type{<:Fvcb}) = PlantSimEngine.IsTimeStepIndependent() PlantSimEngine.timestep_hint(::Type{<:Fvcb}) = ( required=(Dates.Minute(1), Dates.Hour(6)), preferred=Dates.Hour(1) diff --git a/examples/plantbiophysics_subsample/Monteith.jl b/examples/plantbiophysics_subsample/Monteith.jl index fdf94ebc0..e7b029d0d 100644 --- a/examples/plantbiophysics_subsample/Monteith.jl +++ b/examples/plantbiophysics_subsample/Monteith.jl @@ -445,8 +445,6 @@ function PlantSimEngine.outputs_(::Monteith) end Base.eltype(x::Monteith) = typeof(x).parameters[1] -PlantSimEngine.ObjectDependencyTrait(::Type{<:Monteith}) = PlantSimEngine.IsObjectIndependent() -PlantSimEngine.TimeStepDependencyTrait(::Type{<:Monteith}) = PlantSimEngine.IsTimeStepIndependent() # Multi-rate default for energy balance: keep relatively fine cadence. PlantSimEngine.timestep_hint(::Type{<:Monteith}) = ( required=(Dates.Minute(1), Dates.Hour(2)), @@ -481,7 +479,7 @@ the energy balance using the mass flux (~ Rn - λE). # Arguments - `::Monteith`: a Monteith model, usually from a model list (*i.e.* m.energy_balance) -- `models`: A `ModelMapping` struct holding the parameters for the model with +- `models`: the process-keyed model bundle supplied by the scene runtime, with initialisations for: - `Ra_SW_f` (W m-2): net shortwave radiation (PAR + NIR). Often computed from a light interception model - `sky_fraction` (0-2): view factor between the object and the sky for both faces (see details). @@ -504,39 +502,6 @@ debugging mode by executing this in the REPL: `ENV["JULIA_DEBUG"] = PlantBiophys More information [here](https://docs.julialang.org/en/v1/stdlib/Logging/#Environment-variables). -# Examples - -```julia -meteo = Atmosphere(T = 22.0, Wind = 0.8333, P = 101.325, Rh = 0.4490995) - -# Using a constant value for Gs: -leaf = PlantSimEngine.ModelMapping( - energy_balance = Monteith(), - photosynthesis = Fvcb(), - stomatal_conductance = ConstantGs(0.0, 0.0011), - status = (Ra_SW_f = 13.747, sky_fraction = 1.0, d = 0.03) -) - -run!(leaf,meteo) -leaf.status.Rn -julia> 12.902547446281233 - -# Using the model from Medlyn et al. (2011) for Gs: -leaf = PlantSimEngine.ModelMapping( - energy_balance = Monteith(), - photosynthesis = Fvcb(), - stomatal_conductance = Medlyn(0.03, 12.0), - status = (Ra_SW_f = 13.747, sky_fraction = 1.0, aPPFD = 1500.0, d = 0.03) -) - -out_sim = run!(leaf,meteo) -out_sim[:Rn] -out_sim[:Ra_LW_f] -out_sim[:A] - -df = PlantSimEngine.convert_outputs(out_sim, DataFrame) -``` - # References Duursma, R. A., et B. E. Medlyn. 2012. « MAESPA: a model to study interactions between water diff --git a/examples/plantbiophysics_subsample/Tuzet.jl b/examples/plantbiophysics_subsample/Tuzet.jl index 04bc8220f..47f07f9a1 100644 --- a/examples/plantbiophysics_subsample/Tuzet.jl +++ b/examples/plantbiophysics_subsample/Tuzet.jl @@ -51,20 +51,6 @@ and it is typically a small positive value around 30–50 μmol mol⁻¹ under n This implementation uses Cₛ instead of Cᵢ. -# Examples - -```julia -using PlantMeteo, PlantSimEngine, PlantBiophysics -meteo = Atmosphere(T = 20.0, Wind = 1.0, P = 101.3, Rh = 0.65) - -leaf = - PlantSimEngine.ModelMapping( - stomatal_conductance = Tuzet(0.03, 12.0, -1.5, 2.0, 30.0), - status = (Cₛ = 380.0, Ψₗ = -1.0) - ) -run!(leaf, meteo) -``` - # References Tuzet, A., Perrier, A., & Leuning, R. (2003). A coupled model of stomatal conductance, photosynthesis and transpiration. Plant, Cell & Environment, 26(7), 1097-1116. @@ -99,7 +85,7 @@ Stomatal closure for CO₂ according to Tuzet et al. (2003). # Arguments - `::Tuzet`: an instance of the `Tuzet` model type. -- `models::ModelMapping`: A `ModelMapping` struct holding the parameters for the models. +- `models`: the process-keyed model bundle supplied by the scene runtime. - `status`: A status struct holding the variables for the models. - `meteo`: meteorology structure, see [`Atmosphere`](https://palmstudio.github.io/PlantMeteo.jl/stable/#PlantMeteo.Atmosphere). Is not used in this model. - `constants`: A constants struct holding the constants for the models. Is not used in this model. @@ -120,8 +106,6 @@ function gs_closure(m::Tuzet, models, status, meteo, constants=nothing, extra=no (m.g1 / (status.Cₛ - m.Γ)) * fpsif end -PlantSimEngine.ObjectDependencyTrait(::Type{<:Tuzet}) = PlantSimEngine.IsObjectIndependent() -PlantSimEngine.TimeStepDependencyTrait(::Type{<:Tuzet}) = PlantSimEngine.IsTimeStepIndependent() PlantSimEngine.timestep_hint(::Type{<:Tuzet}) = ( required=(Dates.Minute(1), Dates.Hour(6)), preferred=Dates.Hour(1) diff --git a/skills/plantsimengine/SKILL.md b/skills/plantsimengine/SKILL.md index 7b239e8ac..2991f609f 100644 --- a/skills/plantsimengine/SKILL.md +++ b/skills/plantsimengine/SKILL.md @@ -18,11 +18,9 @@ PlantSimEngine has two main user roles: `inputs_`, `outputs_`, `dep`, `meteo_inputs_`, `meteo_outputs_`, `run!`, model traits, and focused tests. -For new multiscale, multi-plant, soil, scene, or microclimate work, use the -unified scene/object API. Historical `ModelMapping` and `MultiScaleModel` -simulations remain available for released-code migration. `ModelMapping` is -not exported, so compatibility code must spell it -`PlantSimEngine.ModelMapping(...)`. +Use the unified scene/object API for multiscale, multi-plant, soil, scene, and +microclimate work. Translate released mapping-era code using +`docs/src/migration_scene_object.md`. ## First Steps @@ -242,7 +240,8 @@ Rules: - Use `NamedTuple()` for no inputs or no outputs. - Read and write model state through `status`. Do not store timestep-varying state in the model object. - Read weather through `meteo` and physical constants through `constants`. -- In MTG runs, `extra` is the `GraphSimulation`; do not use user-defined `extra` arguments for MTG APIs. +- In scene runs, `extra` is a `SceneRunContext`. Use its public hard-call and + lifecycle APIs rather than attaching unrelated user data. - If a variable appears in both `inputs_` and `outputs_` with the same name, remember that `variables(model)` merges declarations and later output declarations win. ### Wrapping existing code @@ -290,12 +289,6 @@ Hard calls are never automatically executed for the parent. Trial Add traits only when they are true for the model implementation, not merely convenient for one scenario. ```julia -PlantSimEngine.TimeStepDependencyTrait(::Type{<:MyModel}) = - PlantSimEngine.IsTimeStepIndependent() - -PlantSimEngine.ObjectDependencyTrait(::Type{<:MyModel}) = - PlantSimEngine.IsObjectIndependent() - PlantSimEngine.timespec(::Type{<:MyDailyModel}) = ClockSpec(24.0, 1.0) PlantSimEngine.output_policy(::Type{<:MyFluxModel}) = ( diff --git a/src/Abstract_model_structs.jl b/src/Abstract_model_structs.jl index 9cef7b087..7af82df2c 100644 --- a/src/Abstract_model_structs.jl +++ b/src/Abstract_model_structs.jl @@ -17,12 +17,11 @@ process(x::Pair{Symbol,A}) where {A<:AbstractModel} = first(x) process_(x) = error("process() is not defined for $(x)") """ - model_(m::AbstractModel) + dep(model) -Get the model of an AbstractModel (it is the model itself if it is not a MultiScaleModel). +Return model-level default `Input(...)` and `Call(...)` declarations. Models +without explicit coupling requirements return an empty `NamedTuple`. """ +dep(::AbstractModel) = NamedTuple() + model_(m::AbstractModel) = m -get_models(m::AbstractModel) = [model_(m)] # Get the models of an AbstractModel -# Note: it is returning a vector of models, because in this case the user provided a single model instead of a vector of. -get_status(m::AbstractModel) = nothing -get_mapped_variables(m::AbstractModel) = Pair{Symbol,String}[] \ No newline at end of file diff --git a/src/ModelSpec.jl b/src/ModelSpec.jl new file mode 100644 index 000000000..c21c3a81a --- /dev/null +++ b/src/ModelSpec.jl @@ -0,0 +1,422 @@ +""" + ModelSpec(model; name=nothing, applies_to=nothing, inputs=NamedTuple(), + calls=NamedTuple(), environment=nothing, timestep=nothing, + meteo_bindings=NamedTuple(), meteo_window=nothing, + output_routing=NamedTuple(), updates=()) + +Configuration for one model application in a `Scene`. + +`ModelSpec` keeps model implementation and scenario-specific usage metadata in one place. +This allows modelers to publish reusable models while users decide how models are coupled in +their simulation setup. +""" +struct ModelSpec{M,N,AT,IN,IO,CA,CO,EV,TS,MB,MW,OR,UP} + model::M + name::N + applies_to::AT + inputs::IN + input_origins::IO + calls::CA + call_origins::CO + environment::EV + timestep::TS + meteo_bindings::MB + meteo_window::MW + output_routing::OR + updates::UP +end + +""" + Updates(vars...; after=nothing) + +Scenario-level declaration that a model updates variables which may also be +computed by another model at the same scale. + +`after` is intentionally scenario-level metadata: the model implementation stays +reusable, while the simulation setup can declare ordering constraints that only +exist in this coupling. +""" +struct Updates{V,A} + variables::V + after::A +end + +function Updates(vars::Vararg{Union{Symbol,AbstractString}}; after=nothing) + isempty(vars) && error("Updates(...) requires at least one variable.") + return Updates(Tuple(Symbol(v) for v in vars), _normalize_update_after(after)) +end + +function _normalize_update_after(after) + isnothing(after) && return () + after isa Union{Symbol,AbstractString} && return (Symbol(after),) + return Tuple(Symbol(v) for v in after) +end + +_normalize_updates(updates::Updates) = (updates,) + +function _normalize_updates(updates::Tuple) + all(update -> update isa Updates, updates) || error( + "Unsupported updates tuple. Use `Updates(:var; after=:process)` entries." + ) + return updates +end + +function _normalize_updates(updates::AbstractVector) + all(update -> update isa Updates, updates) || error( + "Unsupported updates vector. Use `Updates(:var; after=:process)` entries." + ) + return Tuple(updates) +end + +function _normalize_updates(updates) + updates == NamedTuple() && return () + updates == () && return () + error( + "Unsupported updates metadata `$(updates)` of type `$(typeof(updates))`. ", + "Use `Updates(:var; after=:process)` or a tuple/vector of `Updates`." + ) +end + +function ModelSpec( + model::AbstractModel; + name=nothing, + applies_to=nothing, + inputs=NamedTuple(), + input_origins=nothing, + calls=NamedTuple(), + call_origins=nothing, + environment=nothing, + timestep=nothing, + meteo_bindings=NamedTuple(), + meteo_window=nothing, + output_routing=NamedTuple(), + updates=() +) + base_model = model + + normalized_name = _normalize_application_name(name) + default_inputs = _model_default_value_inputs(base_model) + explicit_inputs = _normalize_application_bindings(inputs) + normalized_inputs = _merge_value_inputs(default_inputs, explicit_inputs) + normalized_input_origins = isnothing(input_origins) ? + _binding_origins(default_inputs, explicit_inputs) : + _normalize_binding_origins(input_origins, normalized_inputs) + default_calls = _model_default_model_calls(base_model) + explicit_calls = _normalize_application_bindings(calls) + normalized_calls = _merge_value_inputs(default_calls, explicit_calls) + normalized_call_origins = isnothing(call_origins) ? + _binding_origins(default_calls, explicit_calls) : + _normalize_binding_origins(call_origins, normalized_calls) + normalized_meteo_bindings = _normalize_meteo_bindings(meteo_bindings) + normalized_meteo_window = _normalize_meteo_window(meteo_window) + normalized_output_routing = _normalize_output_routing(output_routing) + normalized_updates = _normalize_updates(updates) + return ModelSpec{typeof(base_model),typeof(normalized_name),typeof(applies_to),typeof(normalized_inputs),typeof(normalized_input_origins),typeof(normalized_calls),typeof(normalized_call_origins),typeof(environment),typeof(timestep),typeof(normalized_meteo_bindings),typeof(normalized_meteo_window),typeof(normalized_output_routing),typeof(normalized_updates)}( + base_model, + normalized_name, + applies_to, + normalized_inputs, + normalized_input_origins, + normalized_calls, + normalized_call_origins, + environment, + timestep, + normalized_meteo_bindings, + normalized_meteo_window, + normalized_output_routing, + normalized_updates + ) +end + +function ModelSpec( + spec::ModelSpec; + model=spec.model, + name=spec.name, + applies_to=spec.applies_to, + inputs=spec.inputs, + input_origins=spec.input_origins, + calls=spec.calls, + call_origins=spec.call_origins, + environment=spec.environment, + timestep=spec.timestep, + meteo_bindings=spec.meteo_bindings, + meteo_window=spec.meteo_window, + output_routing=spec.output_routing, + updates=spec.updates +) + ModelSpec(model; name=name, applies_to=applies_to, inputs=inputs, input_origins=input_origins, calls=calls, call_origins=call_origins, environment=environment, timestep=timestep, meteo_bindings=meteo_bindings, meteo_window=meteo_window, output_routing=output_routing, updates=updates) +end + +as_model_spec(spec::ModelSpec) = spec +as_model_spec(model::AbstractModel) = ModelSpec(model) + +function _with_spec(model_or_spec; kwargs...) + return ModelSpec(as_model_spec(model_or_spec); kwargs...) +end + +function _with_spec_bindings(model_or_spec, bindings, defaults) + spec = as_model_spec(model_or_spec) + explicit = _normalize_application_bindings(bindings) + origins = _binding_origins(defaults(model_(spec)), explicit) + return spec, explicit, origins +end + +""" + with_name(model_or_spec, name) + +Return a `ModelSpec` with an explicit model-application name. +""" +function with_name(model_or_spec, name) + return _with_spec(model_or_spec; name=_normalize_application_name(name)) +end + +""" + with_applies_to(model_or_spec, selector) + +Return a `ModelSpec` with an explicit model-application target selector. +""" +function with_applies_to(model_or_spec, selector) + return _with_spec(model_or_spec; applies_to=selector) +end + +""" + with_inputs(model_or_spec, bindings) + +Return a `ModelSpec` with unified scene/object value-input bindings. +""" +function with_inputs(model_or_spec, bindings) + spec, explicit, origins = + _with_spec_bindings(model_or_spec, bindings, _model_default_value_inputs) + return ModelSpec(spec; inputs=explicit, input_origins=origins) +end + +""" + with_calls(model_or_spec, bindings) + +Return a `ModelSpec` with unified scene/object manual model-call bindings. +""" +function with_calls(model_or_spec, bindings) + spec, explicit, origins = + _with_spec_bindings(model_or_spec, bindings, _model_default_model_calls) + return ModelSpec(spec; calls=explicit, call_origins=origins) +end + +""" + with_environment(model_or_spec, environment) + +Return a `ModelSpec` with scene/object environment configuration metadata. +""" +function with_environment(model_or_spec, environment) + return _with_spec(model_or_spec; environment=environment) +end + +""" + with_timestep(model_or_spec, timestep) + +Return a `ModelSpec` with an explicit user-selected timestep. +""" +function with_timestep(model_or_spec, timestep) + return _with_spec(model_or_spec; timestep=timestep) +end + +""" + with_meteo_bindings(model_or_spec, bindings) + +Return a `ModelSpec` with explicit meteo aggregation bindings. +""" +with_meteo_bindings(model_or_spec, bindings) = + _with_spec(model_or_spec; meteo_bindings=_normalize_meteo_bindings(bindings)) + +""" + with_meteo_window(model_or_spec, window) + +Return a `ModelSpec` with explicit weather-window selection strategy. +""" +with_meteo_window(model_or_spec, window) = + _with_spec(model_or_spec; meteo_window=_normalize_meteo_window(window)) + +""" + with_output_routing(model_or_spec, routing) + +Return a `ModelSpec` with explicit user-defined output routing. +""" +with_output_routing(model_or_spec, routing) = + _with_spec(model_or_spec; output_routing=_normalize_output_routing(routing)) + +""" + with_updates(model_or_spec, updates) + +Return a `ModelSpec` with explicit variable-update metadata. +""" +function with_updates(model_or_spec, updates) + spec = as_model_spec(model_or_spec) + return ModelSpec(spec; updates=(spec.updates..., _normalize_updates(updates)...)) +end + +(updates::Updates)(model_or_spec) = with_updates(model_or_spec, updates) + +function _normalize_meteo_binding(binding) + if binding isa DataType + binding <: PlantMeteo.AbstractTimeReducer || error( + "Unsupported environment reducer type `$(binding)`. ", + "Use a PlantMeteo reducer type/instance, callable, or NamedTuple(source=..., reducer=...)." + ) + return binding + elseif binding isa PlantMeteo.AbstractTimeReducer + return binding + elseif binding isa Function + return binding + elseif binding isa NamedTuple + return binding + end + error( + "Unsupported environment binding `$(binding)` of type `$(typeof(binding))`. ", + "Use a PlantMeteo reducer type/instance, callable, or NamedTuple(source=..., reducer=...)." + ) +end + +function _normalize_meteo_bindings(bindings::NamedTuple) + normalized = Pair{Symbol,Any}[] + for (k, v) in pairs(bindings) + push!(normalized, k => _normalize_meteo_binding(v)) + end + return (; normalized...) +end + +function _normalize_meteo_bindings(bindings) + error("Unsupported environment bindings `$(bindings)` of type `$(typeof(bindings))`.") +end + +function _normalize_meteo_window(window) + if isnothing(window) + return nothing + elseif window isa DataType + window <: PlantMeteo.AbstractSamplingWindow || error( + "Unsupported environment sampling-window type `$(window)`. ", + "Use a PlantMeteo sampling-window type/instance." + ) + return window() + elseif window isa PlantMeteo.AbstractSamplingWindow + return window + end + + error( + "Unsupported environment sampling window `$(window)` of type `$(typeof(window))`. ", + "Use a PlantMeteo sampling-window type/instance." + ) +end + +function _normalize_output_routing(routing::NamedTuple) + normalized = Pair{Symbol,Symbol}[] + for (k, v) in pairs(routing) + mode = Symbol(v) + mode in (:canonical, :stream_only) || error( + "Unsupported output routing mode `$(mode)` for output `$(k)`. ", + "Allowed values are `:canonical` and `:stream_only`." + ) + push!(normalized, k => mode) + end + return (; normalized...) +end + +function _normalize_output_routing(routing) + error("Unsupported OutputRouting value `$(routing)` of type `$(typeof(routing))`. Use a NamedTuple, e.g. `OutputRouting(; x=:stream_only)`.") +end + +""" + AppliesTo(selector) + +Pipe-style transform that sets the object selector where a model application +runs in the unified scene/object API. +""" +AppliesTo(selector) = x -> with_applies_to(x, selector) + +""" + Inputs(bindings...) + Inputs(; kwargs...) + +Pipe-style transform that sets value-input bindings in the unified +scene/object API. +""" +Inputs(bindings::Pair...) = x -> with_inputs(x, bindings) +Inputs(bindings::NamedTuple) = x -> with_inputs(x, bindings) +Inputs(; kwargs...) = Inputs((; kwargs...)) + +""" + Calls(bindings...) + Calls(; kwargs...) + +Pipe-style transform that sets manual model-call bindings in the unified +scene/object API. +""" +Calls(bindings::Pair...) = x -> with_calls(x, bindings) +Calls(bindings::NamedTuple) = x -> with_calls(x, bindings) +Calls(; kwargs...) = Calls((; kwargs...)) + +""" + TimeStep(timestep) + +Pipe-style transform that sets a user-selected timestep for a model +application. +""" +TimeStep(timestep) = x -> with_timestep(x, timestep) + +""" + Environment(config) + Environment(; kwargs...) + +Pipe-style transform that stores scene/object environment configuration +metadata on a `ModelSpec`. +""" +Environment(config) = x -> with_environment(x, config isa EnvironmentConfig ? config : EnvironmentConfig(config)) +Environment(; kwargs...) = Environment((; kwargs...)) + +""" + OutputRouting(routing) + OutputRouting(; kwargs...) + +Pipe-style transform that sets output publication mode for a model. + +This is mainly used to disambiguate publishers in multi-rate runs when several +models write variables with the same name. + +# Arguments +- `routing::NamedTuple`: maps output variable symbols to routing mode. +- `kwargs...`: keyword shorthand equivalent to a `NamedTuple`. + +Allowed routing values: +- `:canonical` (default): output is considered canonical at that scale and can + be auto-selected as source/export publisher. +- `:stream_only`: output is kept only in temporal streams and excluded from + canonical publisher resolution. + +# Example +```julia +ModelSpec(AltSourceModel()) |> +OutputRouting(; C=:stream_only) +``` +""" +OutputRouting(routing) = x -> with_output_routing(x, routing) +OutputRouting(; kwargs...) = OutputRouting((; kwargs...)) + +model_(m::ModelSpec) = m.model +process(m::ModelSpec) = process(model_(m)) +timestep(m::ModelSpec) = m.timestep +inputs_(m::ModelSpec) = inputs_(model_(m)) +outputs_(m::ModelSpec) = outputs_(model_(m)) + +function dep(spec::ModelSpec) + dependencies = dep(model_(spec)) + kept = Pair{Symbol,Any}[] + for (name, selector) in pairs(dependencies) + selector isa Union{Input,Call} && continue + push!(kept, name => selector) + end + return (; kept...) +end +meteo_inputs_(m::ModelSpec) = meteo_inputs_(model_(m)) +meteo_outputs_(m::ModelSpec) = meteo_outputs_(model_(m)) + +function run!(m::ModelSpec, models, status, meteo, constants=nothing, extra=nothing) + return run!(model_(m), models, status, meteo, constants, extra) +end diff --git a/src/PlantSimEngine.jl b/src/PlantSimEngine.jl index 5e846d60b..e09bf7eda 100644 --- a/src/PlantSimEngine.jl +++ b/src/PlantSimEngine.jl @@ -1,137 +1,77 @@ module PlantSimEngine -# FOr data formatting: +# Data formatting: import DataFrames import Tables -import DataAPI import Dates -import CSV # For reading csv files with variables() +# Reading CSV files in `variables`. +import CSV -# For graph dependency: -import AbstractTrees import Term import Markdown import Base: position -# For multi-threading: -import FLoops: @floop, @init, ThreadedEx, SequentialEx, DistributedEx - # For MTG compatibility: import MultiScaleTreeGraph import MultiScaleTreeGraph: symbol, node_id -# To compute mean: +# Statistics helpers: import Statistics -# For avoiding name conflicts when generating models from status vectors -import SHA: sha1 - +# Keep PlantMeteo names available for re-export without local wrapper types. using PlantMeteo -# UninitializedVar + PreviousTimeStep: +# Temporal input marker: include("variables_wrappers.jl") -# Docs templates: -include("doc_templates/mtg-related.jl") - # Models: include("Abstract_model_structs.jl") -include("mtg/node_mapping_types.jl") # Multi-rate scaffolding: include("time/multirate.jl") -# Simulation row (status): +# Object status and reference carriers: include("component_models/Status.jl") include("component_models/RefVector.jl") -# Unified scene/object API: +# Scene/object compiler and runtime: include("scene_object_api.jl") -# Simulation table (time-step table, from PlantMeteo): +# Time-step table adapter: include("component_models/TimeStepTable.jl") -# Declaring the dependency graph -include("dependencies/dependency_graph.jl") - -# Single-scale model container used internally by ModelMapping: -include("component_models/SingleScaleModelSet.jl") -include("mtg/MultiScaleModel.jl") -include("mtg/ModelSpec.jl") -include("mtg/mapping/mapping.jl") - -# Getters / setters for status: -include("component_models/get_status.jl") - -# Transform into a dataframe: -include("dataframe.jl") - -# Computing model dependencies: -include("dependencies/soft_dependencies.jl") -include("dependencies/hard_dependencies.jl") -include("dependencies/traversal.jl") -include("dependencies/is_graph_cyclic.jl") -include("dependencies/printing.jl") -include("dependencies/update_dependencies.jl") -include("dependencies/dependencies.jl") -include("dependencies/get_model_in_dependency_graph.jl") - -# MTG compatibility: -include("mtg/GraphSimulation.jl") -include("mtg/mapping/getters.jl") -include("mtg/mapping/compute_mapping.jl") -include("mtg/mapping/reverse_mapping.jl") -include("mtg/model_spec_inference.jl") -include("mtg/model_spec_validation.jl") -include("mtg/initialisation.jl") -include("mtg/save_results.jl") -include("mtg/add_organ.jl") +# Model application configuration: +include("ModelSpec.jl") # Model evaluation (statistics): include("evaluation/statistics.jl") # Traits include("traits/table_traits.jl") -include("traits/parallel_traits.jl") # Processes: -include("processes/model_initialisation.jl") include("processes/models_inputs_outputs.jl") include("processes/process_generation.jl") -include("checks/dimensions.jl") -# Multi-rate runtime: +# Scene timing and environment runtime: include("time/runtime/clocks.jl") -include("time/runtime/scopes.jl") -include("time/runtime/bindings.jl") -include("time/runtime/input_resolution.jl") -include("time/runtime/publishers.jl") include("time/runtime/output_export.jl") include("time/runtime/meteo_sampling.jl") include("time/runtime/environment_backends.jl") -# Simulation: -include("run.jl") - # Fitting include("evaluation/fit.jl") -# Utilities for mapping initialisation -include("mtg/mapping/model_generation_from_status_vectors.jl") - # Examples include("examples_import.jl") export PreviousTimeStep export AbstractModel -export ScopeId, ClockSpec, ModelKey, OutputKey +export ClockSpec export SchedulePolicy, HoldLast, Interpolate, Integrate, Aggregate export AbstractTimeReducer, MeanWeighted, MeanReducer, SumReducer, MinReducer, MaxReducer, FirstReducer, LastReducer, RadiationEnergy -export OutputCache, HoldLastCache, InterpolateCache, IntegrateCache, AggregateCache -export TemporalState export OutputRequest, collect_outputs -export effective_rate_summary export Scene, Object, ObjectId, SceneRegistry, ObjectTemplate, ObjectInstance, Override export register_object!, remove_object!, reparent_object!, move_object!, update_geometry!, refresh_bindings! export bindings_dirty, environment_bindings_dirty, scene_revision, environment_revision @@ -150,17 +90,14 @@ export One, OptionalOne, Many, ObjectAddress, object_address export Input, Call, AppliesTo, Inputs, Calls, TimeStep, Environment export application_name, applies_to, value_inputs, model_calls, environment_config export ModelSpec, Updates, OutputRouting -export resolved_model_specs, explain_model_specs export call_target, call_targets, run_call!, explain_schedule export RMSE, NRMSE, EF, dr -export Status, TimeStepTable, status -export init_status! -export add_organ!, remove_organ!, reparent_organ! +export Status, TimeStepTable export @process, process -export to_initialize, is_initialized, init_variables, dep -export inputs, outputs, variables, convert_outputs +export init_variables, dep +export inputs, outputs, variables export timespec, output_policy, timestep_hint, meteo_hint -export input_bindings, meteo_bindings, meteo_window, output_routing, model_scope, updates +export meteo_bindings, meteo_window, output_routing, updates export meteo_inputs, meteo_inputs_, meteo_outputs, meteo_outputs_ export validate_meteo_inputs export AbstractEnvironmentBackend, EnvironmentSupport, GlobalConstant @@ -172,8 +109,6 @@ export run! export fit # Re-exporting PlantMeteo main functions: -export Atmosphere, TimeStepTable, Constants, Weather +export Atmosphere, Constants, Weather -# Re-exporting FLoops executors: -export SequentialEx, ThreadedEx, DistributedEx end diff --git a/src/checks/dimensions.jl b/src/checks/dimensions.jl deleted file mode 100644 index c56b665e5..000000000 --- a/src/checks/dimensions.jl +++ /dev/null @@ -1,119 +0,0 @@ -""" - check_dimensions(component,weather) - check_dimensions(status,weather) - -Checks if a component status (or a status directly) and the weather have the same length, or if they can be -recycled (length 1 for one of them). - -# Examples -```jldoctest -using PlantSimEngine, PlantMeteo - -# Including an example script that implements dummy processes and models: -using PlantSimEngine.Examples - -# Creating a dummy weather: -w = Atmosphere(T = 20.0, Rh = 0.5, Wind = 1.0) - -# Creating a dummy component: -models = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=[15.0, 16.0], var2=0.3) -) - -# Checking that the number of time-steps are compatible (here, they are, it returns nothing): -PlantSimEngine.check_dimensions(models, w) - -# Creating a dummy weather with 3 time-steps: -w = Weather([ - Atmosphere(T = 20.0, Rh = 0.5, Wind = 1.0), - Atmosphere(T = 25.0, Rh = 0.5, Wind = 1.0), - Atmosphere(T = 30.0, Rh = 0.5, Wind = 1.0) -]) - -# Checking that the number of time-steps are compatible (here, they are not, it throws an error): -PlantSimEngine.check_dimensions(models, w) - -# output -ERROR: DimensionMismatch: Component status has a vector variable : var1 implying multiple timesteps but weather data only provides a single timestep. -``` -""" -check_dimensions(component, weather) = check_dimensions(DataFormat(weather), component, weather) - -# Here we add methods for applying to a component, an array or a dict of: -function check_dimensions(component::SingleScaleModelSet, w) - check_dimensions(status(component), w) -end - -function check_dimensions(component::ModelMapping{SingleScale}, w) - check_dimensions(status(component), w) -end - -# for several components as an array -function check_dimensions(component::AbstractVector{<:SingleScaleModelSet}, weather) - for i in component - check_dimensions(i, weather) - end -end - -# for several components as a Dict -function check_dimensions(component::AbstractDict{<:Any,<:SingleScaleModelSet}, weather) - for (key, val) in component - check_dimensions(val, weather) - end -end - - -# TODO multi timestep handling - -# A Status (one time-step) is always authorized with a Weather (it is recycled). -# The status is updated at each time-step, but no intermediate saving though! -function check_dimensions(::TableAlike, st::Status, weather) - weather_len = get_nsteps(weather) - - for (var, value) in zip(keys(st), st) - if length(value) > 1 - if length(value) != weather_len - throw(DimensionMismatch("Component status has a vector variable : $(var) of length $(length(value)) but the weather data expects $(weather_len) timesteps.")) - end - end - end - - return nothing -end - -function check_dimensions(::SingletonAlike, st::Status, weather) - for (var, value) in zip(keys(st), st) - if length(value) > 1 - throw(DimensionMismatch("Component status has a vector variable : $(var) implying multiple timesteps but weather data only provides a single timestep.")) - end - end - - return nothing -end - -function check_dimensions(::SingletonAlike, ::SingletonAlike, st, weather) - return nothing -end - - -""" - get_nsteps(t) - -Get the number of steps in the object. -""" -function get_nsteps(t) - get_nsteps(DataFormat(t), t) -end - -function get_nsteps(::SingletonAlike, t) - 1 -end - -function get_nsteps(::TableAlike, t) - DataAPI.nrow(t) -end - -get_nsteps(::TableAlike, t::PlantMeteo.TimeStepRows) = length(t) diff --git a/src/component_models/SingleScaleModelSet.jl b/src/component_models/SingleScaleModelSet.jl deleted file mode 100644 index 31bb6ce98..000000000 --- a/src/component_models/SingleScaleModelSet.jl +++ /dev/null @@ -1,472 +0,0 @@ - -""" - SingleScaleModelSet(models::M, status::S) - SingleScaleModelSet(; - status=nothing, - type_promotion=nothing, - variables_check=true, - kwargs... - ) - -List the models for a simulation (`models`), and does all boilerplate for variable initialization, -type promotion, time steps handling. - -!!! note - The status field depends on the input models. You can get the variables needed by a model - using [`variables`](@ref) on the instantiation of a model. You can also use [`inputs`](@ref) - and [`outputs`](@ref) instead. - -# Arguments - -- `models`: a list of models. Usually given as a `NamedTuple`, but can be any other structure that -implements `getproperty`. -- `status`: a structure containing the initializations for the variables of the models. Usually a NamedTuple -when given as a kwarg, or any structure that implements the Tables interface from `Tables.jl` (*e.g.* DataFrame, see details). -- `type_promotion`: optional type conversion for the variables with default values. -`nothing` by default, *i.e.* no conversion. Note that conversion is not applied to the -variables input by the user as `kwargs` (need to do it manually). -Should be provided as a Dict with current type as keys and new type as values. -- `variables_check=true`: check that all needed variables are initialized by the user. -- `kwargs`: the models, named after the process they simulate. - -# Details - -If you need to input a custom Type for the status and make your users able to only partially initialize -the `status` field in the input, you'll have to implement a method for `add_model_vars!`, a function that -adds the models variables to the type in case it is not fully initialized. The default method is compatible -with any type that implements the `Tables.jl` interface (*e.g.* DataFrame), and `NamedTuples`. - -Note that `SingleScaleModelSet`makes a copy of the input `status` if it does not list all needed variables. - -## Examples - -We'll use the dummy models from the `dummy.jl` in the examples folder of the package. It -implements three dummy processes: `Process1Model`, `Process2Model` and `Process3Model`, with -one model implementation each: `Process1Model`, `Process2Model` and `Process3Model`. - -```julia -julia> using PlantSimEngine; -``` - -Including example processes and models: - -```julia -julia> using PlantSimEngine.Examples; -``` - -```julia -julia> models = SingleScaleModelSet(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model()); -[ Info: Some variables must be initialized before simulation: (process1 = (:var1, :var2), process2 = (:var1,)) (see `to_initialize()`) -``` - -```julia -julia> typeof(models) -SingleScaleModelSet{@NamedTuple{process1::Process1Model, process2::Process2Model, process3::Process3Model}, Status{(:var5, :var4, :var6, :var1, :var3, :var2), NTuple{6, Base.RefValue{Float64}}}} -``` - -No variables were given as keyword arguments, that means that the status of the SingleScaleModelSet is not -set yet, and all variables are initialized to their default values given in the inputs and outputs (usually `typemin(Type)`, *i.e.* `-Inf` for floating -point numbers). This component cannot be simulated yet. - -To know which variables we need to initialize for a simulation, we use [`to_initialize`](@ref): - -```julia -julia> to_initialize(models) -(process1 = (:var1, :var2), process2 = (:var1,)) -``` - -We can now provide values for these variables in the `status` field. Direct -`run!(::SingleScaleModelSet, ...)` has been removed; wrap the models in a `ModelMapping` -before running: - -```julia -julia> mapping = PlantSimEngine.ModelMapping(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0, var2=0.3)); -``` - -```julia -julia> meteo = Atmosphere(T = 22.0, Wind = 0.8333, P = 101.325, Rh = 0.4490995); -``` - -```julia -julia> outputs_sim = run!(mapping, meteo); -``` - -```julia -julia> outputs_sim[:var6] -1-element Vector{Float64}: - 58.0138985 -``` - -If we want to use special types for the variables, we can use the `type_promotion` argument: - -```julia -julia> models = SingleScaleModelSet(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0, var2=0.3), type_promotion = Dict(Float64 => Float32)); -``` - -We used `type_promotion` to force the status into Float32: - -```julia -julia> [typeof(models[i][1]) for i in keys(status(models))] -6-element Vector{DataType}: - Float32 - Float32 - Float32 - Float64 - Float64 - Float32 -``` - -But we see that only the default variables (the ones that are not given in the status arguments) -were converted to Float32, the two other variables that we gave were not converted. This is -because we want to give the ability to users to give any type for the variables they provide -in the status. If we want all variables to be converted to Float32, we can pass them as Float32: - -```julia -julia> models = SingleScaleModelSet(process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model(), status=(var1=15.0f0, var2=0.3f0), type_promotion = Dict(Float64 => Float32)); -``` - -We used `type_promotion` to force the status into Float32: - -```julia -julia> [typeof(models[i][1]) for i in keys(status(models))] -6-element Vector{DataType}: - Float32 - Float32 - Float32 - Float32 - Float32 - Float32 -``` -""" -struct SingleScaleModelSet{M<:NamedTuple,S} - models::M - status::S - type_promotion::Union{Nothing,Dict} - dependency_graph::DependencyGraph -end - -#=function SingleScaleModelSet(models::M, status::Status) where {M<:NamedTuple{names,T} where {names,T<:NTuple{N,<:AbstractModel} where {N}}} - SingleScaleModelSet(models, status) -end=# - -# General interface: -function SingleScaleModelSet( - args...; - status=nothing, - type_promotion::Union{Nothing,Dict}=nothing, - variables_check::Bool=true, - kwargs... -) - # Get all the variables needed by the models and their default values: - if length(args) > 0 - args = parse_models(args) - else - args = NamedTuple() - end - - if length(kwargs) > 0 - kwargs = (; kwargs...) - else - kwargs = () - end - - if length(args) == 0 && length(kwargs) == 0 - error("No models were given") - end - - mods = merge(args, kwargs) - - # Make a vector of NamedTuples from the input (please implement yours if you need it) - ts_kwargs = homogeneous_ts_kwargs(status) - ts_kwargs = add_model_vars(ts_kwargs, mods, type_promotion) - - model_list = SingleScaleModelSet( - mods, - ts_kwargs, - type_promotion, - dep(; verbose=true, mods...) - ) - variables_check && !is_initialized(model_list) - - return model_list -end - -outputs(m::SingleScaleModelSet) = m.outputs - -parse_models(m) = NamedTuple([process(i) => i for i in m]) - -""" - add_model_vars(x, models, type_promotion) - -Check which variables in `x` are not initialized considering a set of `models` and the variables -needed for their simulation. If some variables are uninitialized, initialize them to their default values. - -This function needs to be implemented for each type of `x`. The default method works for -any Tables.jl-compatible `x` and for NamedTuples. - -Careful, the function makes a copy of the input `x` if it does not list all needed variables. -""" -function add_model_vars(x, models, type_promotion) - ref_vars = merge(init_variables(models; verbose=false)...) - # If no variable is required, we return the input: - length(ref_vars) == 0 && return isa(x, Status) ? x : Status(x) - - # If the user gave a status, we check if all the variables are already initialized: - vars_in_x = status_keys(x) - status_x = - all([k in vars_in_x for k in keys(ref_vars)]) && return isa(x, Status) ? x : Status(x) # If so, we return the input - - # Else, we add the variables by making a new object (carefull, this is a copy so it takes more time): - - # Convert model variables types to the one required by the user: - ref_vars = convert_vars(ref_vars, type_promotion) - - # If the user gave an empty status, we initialize all variables to their default values: - if x === nothing - return Status(ref_vars) - end - - if Tables.istable(x) - # This situation only occurs if the user provided a table instead of a status - # Meaning we have a status of vector values, all initialized up to a certain point - # Unsure this is desirable, as that means run! does nothing or overwrites everything - # Anyway, we wish to create a NamedTuple() of Vectors here - x_full = (; zip(propertynames(x), Tables.columns(x))...) - x_full = merge(ref_vars, x_full) - - else - x_full = merge(ref_vars, NamedTuple(x)) - end - #x_full = merge(ref_vars, NamedTuple(x)) - - return Status(x_full) -end - -function status_keys(st) - Tables.istable(st) && return Tables.columnnames(st) - return keys(st) -end - -status_keys(::Nothing) = NamedTuple() - -# If the user doesn't give any initializations, we initialize all variables to their default values: -function add_model_vars(x::Nothing, models, type_promotion) - ref_vars = merge(init_variables(models; verbose=false)...) - length(ref_vars) == 0 && return x - # Convert model variables types to the one required by the user: - return Status(convert_vars(ref_vars, type_promotion)) -end - -""" - homogeneous_ts_kwargs(kwargs) - -By default, the function returns its argument. -""" -homogeneous_ts_kwargs(kwargs) = kwargs - -""" - kwargs_to_timestep(kwargs::NamedTuple{N,T}) where {N,T} - -Takes a NamedTuple with optionnaly vector of values for each variable, and makes a -vector of NamedTuple, with each being a time step. -It is used to be able to *e.g.* give constant values for all time-steps for one variable. - -# Examples - -```@example -PlantSimEngine.homogeneous_ts_kwargs((Tₗ=[25.0, 26.0], aPPFD=1000.0)) -``` -""" -function homogeneous_ts_kwargs(kwargs::NamedTuple{N,T}) where {N,T} - length(kwargs) == 0 && return kwargs - vars_vals = collect(Any, values(kwargs)) - - vars_array = NamedTuple{keys(kwargs)}(j for j in vars_vals) - - return vars_array -end - -""" - Base.copy(l::SingleScaleModelSet) - Base.copy(l::SingleScaleModelSet, status) - -Copy a [`SingleScaleModelSet`](@ref), eventually with new values for the status. - -# Examples - -```@example -using PlantSimEngine - -# Including example processes and models: -using PlantSimEngine.Examples; - -# Create a model list: -models = SingleScaleModelSet( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=15.0, var2=0.3) -) - -# Copy the model list: -ml2 = copy(models) - -# Copy the model list with new status: -ml3 = copy(models, TimeStepTable([Status(var1=20.0, var2=0.5))]) -``` -""" -function Base.copy(m::T) where {T<:SingleScaleModelSet} - SingleScaleModelSet( - m.models, - deepcopy(m.status), - deepcopy(m.type_promotion), - deepcopy(m.dependency_graph) - ) -end - -function Base.copy(m::T, status) where {T<:SingleScaleModelSet} - SingleScaleModelSet( - m.models, - status, - deepcopy(m.type_promotion), - deepcopy(m.dependency_graph) - ) -end - -""" - Base.copy(l::AbstractArray{<:SingleScaleModelSet}) - -Copy an array-alike of [`SingleScaleModelSet`](@ref) -""" -function Base.copy(l::T) where {T<:AbstractArray{<:SingleScaleModelSet}} - return [copy(i) for i in l] -end - -""" - Base.copy(l::AbstractDict{N,<:SingleScaleModelSet} where N) - -Copy a Dict-alike [`SingleScaleModelSet`](@ref) -""" -function Base.copy(l::T) where {T<:AbstractDict{N,<:SingleScaleModelSet} where {N}} - return Dict([k => v for (k, v) in l]) -end - - -""" - convert_vars(ref_vars, type_promotion::Dict{DataType,DataType}) - convert_vars(ref_vars, type_promotion::Nothing) - convert_vars!(ref_vars::Dict{Symbol}, type_promotion::Dict{DataType,DataType}) - convert_vars!(ref_vars::Dict{Symbol}, type_promotion::Nothing) - -Convert the status variables to the type specified in the type promotion dictionary. -*Note: the mutating version only works with a dictionary of variables.* - -# Examples - -If we want all the variables that are Reals to be Float32, we can use: - -```julia -using PlantSimEngine - -# Including example processes and models: -using PlantSimEngine.Examples; - -ref_vars = init_variables( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), -) -type_promotion = Dict(Real => Float32) - -PlantSimEngine.convert_vars(type_promotion, ref_vars.process3) -``` -""" -convert_vars, convert_vars! - -function convert_vars(ref_vars, type_promotion::Dict{DataType,DataType}) - dict_ref_vars = Dict{Symbol,Any}(zip(keys(ref_vars), values(ref_vars))) - for (suptype, newtype) in type_promotion - vars = [] - for var in keys(ref_vars) - if isa(dict_ref_vars[var], suptype) - dict_ref_vars[var] = convert(newtype, dict_ref_vars[var]) - push!(vars, var) - end - end - # length(vars) > 1 && @info "$(join(vars, ", ")) are $suptype and were promoted to $newtype" - end - - return NamedTuple(dict_ref_vars) -end - -# Mutating version of the function, needs a dictionary of variables: -function convert_vars!(ref_vars::Dict{Symbol,Any}, type_promotion::Dict) - for (suptype, newtype) in type_promotion - for var in keys(ref_vars) - if isa(ref_vars[var], suptype) - ref_vars[var] = convert(newtype, ref_vars[var]) - elseif isa(ref_vars[var], MappedVar) && isa(mapped_default(ref_vars[var]), suptype) - ref_mapped_var = ref_vars[var] - old_default = mapped_default(ref_vars[var]) - - if isa(old_default, AbstractArray) - new_val = [convert(newtype, i) for i in old_default] - else - new_val = convert(newtype, old_default) - end - - ref_vars[var] = MappedVar( - source_organs(ref_mapped_var), - mapped_variable(ref_mapped_var), - source_variable(ref_mapped_var), - new_val, - ) - elseif isa(ref_vars[var], UninitializedVar) && isa(ref_vars[var].value, suptype) - ref_mapped_var = ref_vars[var] - old_default = ref_vars[var].value - - if isa(old_default, AbstractArray) - new_val = [convert(newtype, i) for i in old_default] - else - new_val = convert(newtype, old_default) - end - - ref_vars[var] = UninitializedVar(var, new_val) - end - end - end -end - -# This is the generic one, with no convertion: -function convert_vars(ref_vars, type_promotion::Nothing) - return ref_vars -end - -function convert_vars!(ref_vars::Dict{Symbol,Dict{Symbol,Any}}, type_promotion::Nothing) - return ref_vars -end - -""" - convert_vars!(mapped_vars::Dict{Symbol,Dict{Symbol,Any}}, type_promotion) - -Converts the types of the variables in a mapping (`mapped_vars`) using the `type_promotion` dictionary. - -The mapping should be a dictionary with organ name as keys and a dictionary of variables as values, -with variable names as symbols and variable value as value. -""" -function convert_vars!(mapped_vars::Dict{Symbol,Dict{Symbol,Any}}, type_promotion) - for (organ, vars) in mapped_vars - convert_vars!(vars, type_promotion) - end -end - -function Base.show(io::IO, m::MIME"text/plain", t::SingleScaleModelSet) - show(io, m, dep(t)) - println(io, "") - show(io, m, status(t)) -end - -# Short form printing (e.g. inside another object) -function Base.show(io::IO, t::SingleScaleModelSet) - print(io, "SingleScaleModelSet", (; zip(keys(t.models), typeof.(values(t.models)))...)) -end diff --git a/src/component_models/Status.jl b/src/component_models/Status.jl index bdff60078..87750f31b 100644 --- a/src/component_models/Status.jl +++ b/src/component_models/Status.jl @@ -3,7 +3,7 @@ Status type used to store the values of the variables during simulation. It is mainly used as the structure to store the variables in the `TimeStepRow` of a `TimeStepTable` (see -[`PlantMeteo.jl` docs](https://palmstudio.github.io/PlantMeteo.jl/stable/)) of a [`SingleScaleModelSet`](@ref). +[`PlantMeteo.jl` docs](https://palmstudio.github.io/PlantMeteo.jl/stable/)). Most of the code is taken from MasonProtter/MutableNamedTuples.jl, so `Status` is a MutableNamedTuples with a few modifications, so in essence, it is a stuct that stores a `NamedTuple` of the references to the values of the variables, which makes it mutable. diff --git a/src/component_models/StatusView.jl b/src/component_models/StatusView.jl deleted file mode 100644 index ceb1319fb..000000000 --- a/src/component_models/StatusView.jl +++ /dev/null @@ -1,147 +0,0 @@ -""" - StatusView(vars) - -An equivalent of the `Status` struct, but with views instead of Refs. Allows to use the same syntax as Status, but initialised with values of -other data structures present elsewhere, and that we want to update on mutation. - -Like the `Status`, `StatusView` is used to store the values of the variables during a simulation, mainly as the structure to store the variables -in the `TimeStepRow` of a `TimeStepTable` (see [`PlantMeteo.jl` docs](https://palmstudio.github.io/PlantMeteo.jl/stable/)) of a [`SingleScaleModelSet`](@ref). - -# Examples - -Making the reference data as an array: - -```jldoctest st1 -julia> ref_data = [13.747, 1.0, 0.03, 1500.0]; -``` - -Making a view of the reference data: - -```jldoctest st1 -ref_data_view = NamedTuple{(:Ra_SW_f, :sky_fraction, :d, :aPPFD)}(ntuple(i->view(ref_data, i), 4)) -``` - -Making the StatusView: - -```jldoctest st1 -julia> st = PlantSimEngine.StatusView(ref_data_view); -``` - -All these indexing methods are valid: - -```jldoctest st1 -julia> st[:Ra_SW_f] -13.747 -``` - -```jldoctest st1 -julia> st.Ra_SW_f -13.747 -``` - -```jldoctest st1 -julia> st[1] -13.747 -``` - -Setting a StatusView variable is very easy: - -```jldoctest st1 -julia> st[:Ra_SW_f] = 20.0 -20.0 -``` - -```jldoctest st1 -julia> st.Ra_SW_f = 21.0 -21.0 -``` - -```jldoctest st1 -julia> st[1] = 22.0 -22.0 -``` - -The reference data is updated: - -```jldoctest st1 -julia> ref_data -4-element Array{Float64,1}: - 22.0 - 1.0 - 0.03 - 1500.0 -``` -""" -struct StatusView{N,T<:Tuple{Vararg{<:SubArray}}} - vars::NamedTuple{N,T} -end - -function Base.getproperty(s::StatusView, name::Symbol) - return getfield(s, :vars)[name][] -end - -function Base.setproperty!(s::StatusView, name, value) - getfield(s, :vars)[name][] = value -end - -function Base.getindex(s::StatusView, name::Symbol) - return getfield(s, :vars)[name][] -end - -function Base.getindex(s::StatusView, i::Int) - return getfield(s, :vars)[i][] -end - -function Base.setindex!(s::StatusView, value, name) - getfield(s, :vars)[name][] = value -end - -Base.keys(::StatusView{names}) where {names} = names -Base.values(s::StatusView) = _status_values(s) -Base.NamedTuple(mnt::StatusView) = _status_namedtuple(mnt) -Base.Tuple(mnt::StatusView) = _status_tuple(mnt) - -function Base.show(io::IO, s::StatusView) - length(s) == 0 && return - print(io, "StatusView(") - for (i, (k, v)) in enumerate(getfield(s, :vars)) - print(io, k, "=", v[]) - if i < length(getfield(s, :vars)) - print(io, ", ") - end - end - print(io, ")") -end - -function Base.show(io::IO, ::MIME"text/plain", t::StatusView) - st_panel = Term.Panel( - Term.highlight(PlantMeteo.show_long_format_row(t)), - title="StatusView", - style="red", - fit=false, - ) - print(io, st_panel) -end - -# function Base.show(io::IO, ::MIME"text/html", s::StatusView) -# print(io, "StatusView(") -# for (i, (k, v)) in enumerate(getfield(s, :vars)) -# print(io, k, "=", v[]) -# if i < length(getfield(s, :vars)) -# print(io, ", ") -# end -# end -# print(io, ")") -# end - - -Base.propertynames(::StatusView{T,R}) where {T,R} = T -Base.length(mnt::StatusView) = length(getfield(mnt, :vars)) -Base.eltype(::Type{StatusView{T}}) where {T} = T -Base.iterate(mnt::StatusView, iter=1) = _status_iterate(mnt, iter) -Base.firstindex(mnt::StatusView) = _status_firstindex(mnt) -Base.lastindex(mnt::StatusView) = _status_lastindex(mnt) - -function Base.indexed_iterate(mnt::StatusView, i::Int, state=1) - _status_indexed_iterate(mnt, i, state) -end diff --git a/src/component_models/TimeStepTable.jl b/src/component_models/TimeStepTable.jl index a7ce79516..50e88ab8c 100644 --- a/src/component_models/TimeStepTable.jl +++ b/src/component_models/TimeStepTable.jl @@ -5,10 +5,6 @@ Method to build a `TimeStepTable` (from [PlantMeteo.jl](https://palmstudio.github.io/PlantMeteo.jl/stable/)) from a `DataFrame`, but with each row being a `Status`. -# Note - -[`SingleScaleModelSet`](@ref) uses `TimeStepTable{Status}` by default (see examples below). - # Examples ```julia @@ -23,19 +19,7 @@ df = DataFrame( ) TimeStepTable{Status}(df) -# A leaf with several values for at least one of its variable will automatically use -# TimeStepTable{Status} with the time steps: -models = SingleScaleModelSet( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=15.0, var2=0.3) -) - -# The status of the leaf is a TimeStepTable: -status(models) - -# Of course we can also create a TimeStepTable with Status manually: +# A TimeStepTable can also be built directly from Status values: TimeStepTable( [ Status(Tₗ=25.0, aPPFD=1000.0, Cₛ=400.0, Dₗ=1.0), @@ -59,4 +43,4 @@ end # # # Tables.Schema(names(m), DataType[i.types[1] for i in T.parameters[2].parameters]) # # Tables.Schema(names(m), col_types) -# end \ No newline at end of file +# end diff --git a/src/component_models/get_status.jl b/src/component_models/get_status.jl deleted file mode 100644 index 04d1da689..000000000 --- a/src/component_models/get_status.jl +++ /dev/null @@ -1,104 +0,0 @@ -""" - status(m) - status(m::AbstractArray{<:SingleScaleModelSet}) - status(m::AbstractDict{T,<:SingleScaleModelSet}) - -Get a SingleScaleModelSet status, *i.e.* the state of the input (and output) variables. - -See also [`is_initialized`](@ref) and [`to_initialize`](@ref) - -# Examples - -```jldoctest -using PlantSimEngine - -# Including example models and processes: -using PlantSimEngine.Examples; - -# Create a SingleScaleModelSet -models = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status = (var1=[15.0, 16.0], var2=0.3) -); - -status(models) - -# Or just one variable: -status(models,:var1) - - -# Or the status at the ith time-step: -status(models, 2) - -# Or even more simply: -models[:var1] -# output -2-element Vector{Float64}: - 15.0 - 16.0 -``` -""" -function status(m) - m.status -end - -status(m::ModelMapping{SingleScale}) = status(m.data) -status(m::ModelMapping{SingleScale}, key::Symbol) = status(m.data, key) -status(m::ModelMapping{SingleScale}, key::T) where {T<:Integer} = status(m.data, key) - -function status(m::T) where {T<:AbstractArray{M} where {M}} - [status(i) for i in m] -end - -function status(m::T) where {T<:AbstractDict{N,M} where {N,M}} - Dict([k => status(v) for (k, v) in m]) -end - -# Status with a variable would return the variable value. -function status(m, key::Symbol) - getproperty(m.status, key) -end - -# Status with an integer returns the ith status. -function status(m, key::T) where {T<:Integer} - getindex(m.status, key) -end - -""" - getindex(component<:SingleScaleModelSet, key::Symbol) - getindex(component<:SingleScaleModelSet, key) - -Indexing a component models structure: - - with an integer, will return the status at the ith time-step - - with anything else (Symbol, String) will return the required variable from the status - -# Examples - -```julia -using PlantSimEngine - -lm = SingleScaleModelSet( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status = (var1=[15.0, 16.0], var2=0.3) -); - -lm[:var1] # Returns the value of the Tₗ variable -lm[2] # Returns the status at the second time-step -lm[2][:var1] # Returns the value of Tₗ at the second time-step -lm[:var1][2] # Equivalent of the above - -# output -16.0 -``` -""" -function Base.getindex(component::T, key) where {T<:SingleScaleModelSet} - status(component, key) -end - -function Base.setindex!(component::T, value, key) where {T<:SingleScaleModelSet} - setproperty!(status(component), key, value) -end diff --git a/src/dataframe.jl b/src/dataframe.jl deleted file mode 100644 index 73daf4b11..000000000 --- a/src/dataframe.jl +++ /dev/null @@ -1,69 +0,0 @@ -""" - DataFrame(components <: AbstractArray{<:ModelMapping}) - DataFrame(components <: AbstractDict{N,<:ModelMapping}) - -Fetch the data from a [`ModelMapping`](@ref) (or an Array/Dict of) status into a DataFrame. - -# Examples - -```@example -using PlantSimEngine -using DataFrames - -# Creating a ModelMapping -models = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=15.0, var2=0.3) -) - -# Converting to a DataFrame -df = DataFrame(models) - -# Converting to a Dict of ModelMappings -models = PlantSimEngine.ModelMapping( - :Leaf => PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model() - ), - :InterNode => PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model() - ) -) - -# Converting to a DataFrame -df = DataFrame(models) -``` -""" -function DataFrames.DataFrame(components::AbstractVector{<:ModelMapping}) - df = DataFrame[] - for (k, v) in enumerate(components) - df_c = DataFrames.DataFrame(v) - df_c[!, :component] .= k - push!(df, df_c) - end - reduce(vcat, df) -end - -function DataFrames.DataFrame(components::AbstractDict{<:Any,<:ModelMapping}) - df = DataFrames.DataFrame[] - for (k, v) in components - df_c = DataFrames.DataFrame(v) - df_c[!, :component] .= k - push!(df, df_c) - end - reduce(vcat, df) -end - -""" - DataFrame(components::ModelMapping{T,S}) where {T,S<:Status} - -Implementation of `DataFrame` for a `ModelMapping` model with one time step. -""" -function DataFrames.DataFrame(components::ModelMapping) - DataFrames.DataFrame([NamedTuple(status(components)[1])]) -end diff --git a/src/dependencies/dependencies.jl b/src/dependencies/dependencies.jl deleted file mode 100644 index 0fd936e54..000000000 --- a/src/dependencies/dependencies.jl +++ /dev/null @@ -1,131 +0,0 @@ -dep(::T, nsteps=1) where {T<:AbstractModel} = NamedTuple() - -""" - dep(mapping::ModelMapping; verbose=true) - dep(mapping::AbstractDict{Symbol,T}; verbose=true) - dep!(m::ModelMapping, nsteps=1) - -Get the model dependency graph given a ModelMapping or a multiscale model mapping. If one graph is returned, -then all models are coupled. If several graphs are returned, then only the models inside each graph are coupled, and -the models in different graphs are not coupled. -`nsteps` is the number of steps the dependency graph will be used over. It is used to determine -the length of the `simulation_id` argument for each soft dependencies in the graph. It is set to `1` in the case of a -multiscale mapping. - -# Details - -The dependency graph is computed by searching the inputs of each process in the outputs of its own scale, or the other scales. There are five cases -for every model (one model simulates one process): - -1. The process has no inputs. It is completely independent, and is placed as one of the roots of the dependency graph. -2. The process needs inputs from models at its own scale. We put it as a child of this other process. -3. The process needs inputs from another scale. We put it as a child of this process at another scale. -4. The process needs inputs from its own scale and another scale. We put it as a child of both. -5. The process is a hard dependency of another process (only possible at the same scale). In this case, the process is set as a hard-dependency of the -other process, and its simulation is handled directly from this process. - -For the 4th case, the process have two parent processes. This is OK because the process will only be computed once during simulation as we check if both -parents were run before running the process. - -Note that in the 5th case, we still need to check if a variable is needed from another scale. In this case, the parent node is -used as a child of the process at the other scale. Note there can be several levels of hard dependency graph, so this is done recursively. - -How do we do all that? We identify the hard dependencies first. Then we link the inputs/outputs of the hard dependencies roots -to other scales if needed. Then we transform all these nodes into soft dependencies, that we put into a Dict of Scale => PlantSimEngine.ModelMapping(process => SoftDependencyNode). -Then we traverse all these and we set nodes that need outputs from other nodes as inputs as children/parents. -If a node has no dependency, it is set as a root node and pushed into a new Dict (independant_process_root). This Dict is the returned dependency graph. And -it presents root nodes as independent starting points for the sub-graphs, which are the models that are coupled together. We can then traverse each of -these graphs independently to r - -# Notes - -The difference between `dep(m::ModelMapping)` and `dep!(m::ModelMapping, nsteps)` is that the first one returns the dependency graph found in the model list, while the -second one returns the dependency graph with the specified number of steps, modifying the simulation IDs of each node in the graph (`simulation_id=fill(0, nsteps)`). - -# Examples - -```@example -using PlantSimEngine - -# Including example processes and models: -using PlantSimEngine.Examples; - -models = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=15.0, var2=0.3) -) - -dep(models) - -# or directly with the processes: -models = ( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - process4=Process4Model(), - process5=Process5Model(), - process6=Process6Model(), - process7=Process7Model(), -) - -dep(;models...) -``` -""" -function dep(nsteps=1; verbose::Bool=true, vars...) - hard_dep = hard_dependencies((; vars...), verbose=verbose) - deps = soft_dependencies(hard_dep, nsteps) - apply_update_dependencies!(deps, _model_specs_for_dependency_updates((; vars...))) - - # Return the dependency graph - return deps -end - -function dep(m::SingleScaleModelSet) - m.dependency_graph -end - -function dep!(m::SingleScaleModelSet, nsteps=1) - traverse_dependency_graph!(m.dependency_graph; visit_hard_dep=false) do node - if length(node.simulation_id) != nsteps - node.simulation_id = fill(0, nsteps) - end - end - - return m.dependency_graph -end - - -function dep(m::NamedTuple, nsteps=1; verbose::Bool=true) - dep(nsteps; verbose=verbose, m...) -end - -function dep(mapping::AbstractDict{Symbol,T}; verbose::Bool=true) where {T} - # First step, get the hard-dependency graph and create SoftDependencyNodes for each hard-dependency root. In other word, we want - # only the nodes that are not hard-dependency of other nodes. These nodes are taken as roots for the soft-dependency graph because they - # are independant. - soft_dep_graphs_roots, hard_dep_dict = hard_dependencies(mapping; verbose=verbose) - - mapped_vars = mapped_variables(mapping, soft_dep_graphs_roots, verbose=false) - reverse_multiscale_mapping = reverse_mapping(mapped_vars, all=false) - - # Second step, compute the soft-dependency graph between SoftDependencyNodes computed in the first step. To do so, we search the - # inputs of each process into the outputs of the other processes, at the same scale, but also between scales. Then we keep only the - # nodes that have no soft-dependencies, and we set them as root nodes of the soft-dependency graph. The other nodes are set as children - # of the nodes that they depend on. - dep_graph = soft_dependencies_multiscale(soft_dep_graphs_roots, reverse_multiscale_mapping, hard_dep_dict) - apply_update_dependencies!(dep_graph, _model_specs_for_dependency_updates(mapping)) - # During the building of the soft-dependency graph, we identified the inputs and outputs of each dependency node, - # and also defined **inputs** as MappedVar if they are multiscale, i.e. if they take their values from another scale. - # What we are missing is that we need to also define **outputs** as multiscale if they are needed by another scale. - - # Checking that the graph is acyclic: - iscyclic, cycle_vec = is_graph_cyclic(dep_graph; warn=false) - # Note: we could do that in `soft_dependencies_multiscale` but we prefer to keep the function as simple as possible, and - # usable on its own. - - iscyclic && error("Cyclic dependency detected in the graph. Cycle: \n $(print_cycle(cycle_vec)) \n You can break the cycle using the `PreviousTimeStep` variable in the mapping.") - # Third step, we identify which - return dep_graph -end diff --git a/src/dependencies/dependency_graph.jl b/src/dependencies/dependency_graph.jl deleted file mode 100644 index 1386169e8..000000000 --- a/src/dependencies/dependency_graph.jl +++ /dev/null @@ -1,173 +0,0 @@ -abstract type AbstractDependencyNode end - -""" - ProducerVariable(input, source) - -Dependency metadata for a producer variable that reaches a consumer input under -a different local name. -""" -struct ProducerVariable - input::Symbol - source::Symbol -end - -mutable struct HardDependencyNode{T} <: AbstractDependencyNode - value::T - process::Symbol - dependency::NamedTuple - missing_dependency::Vector{Int} - scale::Symbol - inputs - outputs - parent::Union{Nothing,<:AbstractDependencyNode} - children::Vector{HardDependencyNode} -end - -mutable struct SoftDependencyNode{T} <: AbstractDependencyNode - value::T - process::Symbol - scale::Symbol - inputs - outputs - hard_dependency::Vector{HardDependencyNode} - parent::Union{Nothing,Vector{SoftDependencyNode}} - parent_vars::Union{Nothing,NamedTuple} - children::Vector{SoftDependencyNode} - simulation_id::Vector{Int} # id of the simulation -end - -# Add methods to check if a node is parallelizable: -object_parallelizable(x::T) where {T<:AbstractDependencyNode} = x.value => object_parallelizable(x.value) -timestep_parallelizable(x::T) where {T<:AbstractDependencyNode} = x.value => timestep_parallelizable(x.value) - -""" - DependencyGraph{T}(roots::T, not_found::Dict{Symbol,DataType}) - -A graph of dependencies between models. - -# Arguments - -- `roots::T`: the root nodes of the graph. -- `not_found::Dict{Symbol,DataType}`: the models that were not found in the graph. -""" -struct DependencyGraph{T,N} - roots::T - not_found::Dict{Symbol,N} -end - -# Add methods to check if a node is parallelizable: -function which_timestep_parallelizable(x::T) where {T<:DependencyGraph} - return traverse_dependency_graph(x, timestep_parallelizable) -end - -function which_object_parallelizable(x::T) where {T<:DependencyGraph} - return traverse_dependency_graph(x, object_parallelizable) -end - -object_parallelizable(x::T) where {T<:DependencyGraph} = all([i.second.second for i in which_object_parallelizable(x)]) -timestep_parallelizable(x::T) where {T<:DependencyGraph} = all([i.second.second for i in which_timestep_parallelizable(x)]) - -AbstractTrees.children(t::AbstractDependencyNode) = t.children -AbstractTrees.nodevalue(t::AbstractDependencyNode) = t.value # needs recent AbstractTrees -AbstractTrees.ParentLinks(::Type{<:AbstractDependencyNode}) = AbstractTrees.StoredParents() -AbstractTrees.parent(t::AbstractDependencyNode) = t.parent -AbstractTrees.printnode(io::IO, node::HardDependencyNode{T}) where {T} = print(io, T) -AbstractTrees.printnode(io::IO, node::SoftDependencyNode{T}) where {T} = print(io, T) -Base.show(io::IO, t::AbstractDependencyNode) = AbstractTrees.print_tree(io, t) -Base.length(t::AbstractDependencyNode) = length(collect(AbstractTrees.PreOrderDFS(t))) -Base.length(t::DependencyGraph) = length(traverse_dependency_graph(t)) -AbstractTrees.children(t::DependencyGraph) = collect(t.roots) - -# Long form printing -function Base.show(io::IO, ::MIME"text/plain", t::DependencyGraph) - # If the graph is cyclic, we print the cycle because we can't print indefinitely: - iscyclic, cycle_vec = is_graph_cyclic(t; warn=false, full_stack=true) - if iscyclic - print(io, "⚠ Cyclic dependency graph: \n $(print_cycle(cycle_vec))") - return nothing - else - draw_dependency_graph(io, t) - end -end - -""" - variables_multiscale(node, organ, mapping, st=NamedTuple()) - -Get the variables of a HardDependencyNode, taking into account the multiscale mapping, *i.e.* -defining variables as `MappedVar` if they are mapped to another scale. The default values are -taken from the model if not given by the user (`st`), and are marked as `UninitializedVar` if -they are inputs of the node. - -Return a NamedTuple with the variables and their default values. - -# Arguments - -- `node::HardDependencyNode`: the node to get the variables from. -- `organ::Symbol`: the organ type, *e.g.* :`Leaf`. -- `vars_mapping::Dict{String,T}`: the mapping of the models (see details below). -- `st::NamedTuple`: an optional named tuple with default values for the variables. - -# Details - -The `vars_mapping` is a dictionary with the organ type as key and a dictionary as value. It is -computed from the user mapping like so: -""" -function variables_multiscale(node, organ, vars_mapping, st=NamedTuple()) - node_vars = variables(node) # e.g. (inputs = (:var1=-Inf, :var2=-Inf), outputs = (:var3=-Inf,)) - ins = node_vars.inputs - ins_variables = keys(ins) - outs_variables = keys(node_vars.outputs) - defaults = merge(node_vars...) - map((inputs=ins_variables, outputs=outs_variables)) do vars # Map over vars from :inputs and vars from :outputs - vars_ = Vector{Pair{Symbol,Any}}() - for var in vars # e.g. var = :carbon_biomass - if var in keys(st) - #If the user has given a status, we use it as default value. - default = st[var] - elseif var in ins_variables - # Otherwise, we use the default value given by the model: - # If the variable is an input, we mark it as uninitialized: - default = UninitializedVar(var, defaults[var]) - else - # If the variable is an output, we use the default value given by the model: - default = defaults[var] - end - - if haskey(vars_mapping[organ], var) - organ_mapped, organ_mapped_var = _node_mapping(vars_mapping[organ][var]) - push!(vars_, var => MappedVar(organ_mapped, var, organ_mapped_var, default)) - #* We still check if the variable also exists wrapped in PreviousTimeStep, because one model could use the current - #* values, and another one the previous values. - if haskey(vars_mapping[organ], PreviousTimeStep(var, node.process)) - organ_mapped, organ_mapped_var = _node_mapping(vars_mapping[organ][PreviousTimeStep(var, node.process)]) - push!(vars_, var => MappedVar(organ_mapped, PreviousTimeStep(var, node.process), organ_mapped_var, default)) - end - elseif haskey(vars_mapping[organ], PreviousTimeStep(var, node.process)) - # If not found in the current time step, we check if the variable is mapped to the previous time step: - organ_mapped, organ_mapped_var = _node_mapping(vars_mapping[organ][PreviousTimeStep(var, node.process)]) - push!(vars_, var => MappedVar(organ_mapped, PreviousTimeStep(var, node.process), organ_mapped_var, default)) - else - # Else we take the default value: - push!(vars_, var => default) - end - end - return (; vars_...,) - end -end - -function _node_mapping(var_mapping::Pair{<:Union{AbstractString,Symbol},Symbol}) - # One organ is mapped to the variable: - return SingleNodeMapping(first(var_mapping)), last(var_mapping) -end - -function _node_mapping(var_mapping::Pair{SameScale,Symbol}) - return first(var_mapping), last(var_mapping) -end - -function _node_mapping(var_mapping) - # Several organs are mapped to the variable: - organ_mapped = MultiNodeMapping([first(i) for i in var_mapping]) - organ_mapped_var = [last(i) for i in var_mapping] - - return organ_mapped, organ_mapped_var -end diff --git a/src/dependencies/get_model_in_dependency_graph.jl b/src/dependencies/get_model_in_dependency_graph.jl deleted file mode 100644 index cf153e98c..000000000 --- a/src/dependencies/get_model_in_dependency_graph.jl +++ /dev/null @@ -1,43 +0,0 @@ -""" - get_model_nodes(dep_graph::DependencyGraph, model) - -Get the nodes in the dependency graph implementing a type of model. - -# Arguments - -- `dep_graph::DependencyGraph`: the dependency graph. -- `model`: the model type to look for. - -# Returns - -- An array of nodes implementing the model type. - -# Examples - -```julia -PlantSimEngine.get_model_nodes(dependency_graph, Beer) -``` -""" -function get_model_nodes(dep_graph::DependencyGraph, model) - model_node = Union{SoftDependencyNode,HardDependencyNode}[] - - traverse_dependency_graph!(dep_graph) do node - if isa(node.value, model) - push!(model_node, node) - end - end - - return model_node -end - -function get_model_nodes(dep_graph::DependencyGraph, process::Symbol) - process_node = Union{SoftDependencyNode,HardDependencyNode}[] - - traverse_dependency_graph!(dep_graph) do node - if node.process == process - push!(process_node, node) - end - end - - return process_node -end \ No newline at end of file diff --git a/src/dependencies/hard_dependencies.jl b/src/dependencies/hard_dependencies.jl deleted file mode 100644 index d0395eab2..000000000 --- a/src/dependencies/hard_dependencies.jl +++ /dev/null @@ -1,296 +0,0 @@ -""" - hard_dependencies(models; verbose::Bool=true) - hard_dependencies(mapping::ModelMapping; verbose::Bool=true) - hard_dependencies(mapping::AbstractDict{Symbol,T}; verbose::Bool=true) - -Compute the hard dependencies between models. -""" -function _normalize_hard_dependency_scales(scales, process::Symbol, dependency_process::Symbol) - if scales isa Symbol - return [scales] - elseif scales isa AbstractString - error( - "Invalid hard dependency scale declaration for process `$(process)` dependency `$(dependency_process)`: ", - "string scale names are removed. Use Symbol scales, e.g. `:Leaf`." - ) - elseif scales isa Tuple || scales isa AbstractVector - normalized = Symbol[] - for s in scales - if s isa Symbol - push!(normalized, s) - else - error( - "Invalid hard dependency scale declaration for process `$(process)` dependency `$(dependency_process)`: ", - "expected Symbol scales, got `$(typeof(s))`." - ) - end - end - isempty(normalized) && error( - "Invalid hard dependency scale declaration for process `$(process)` dependency `$(dependency_process)`: ", - "at least one target scale must be provided." - ) - return normalized - end - - error( - "Invalid hard dependency scale declaration for process `$(process)` dependency `$(dependency_process)`: ", - "expected Symbol/String or a tuple/vector of them, got `$(typeof(scales))`." - ) -end - -function hard_dependencies(models; scale=nothing, verbose::Bool=true) - dep_graph = initialise_all_as_hard_dependency_node(models, scale) - dep_not_found = Dict{Symbol,Any}() - for (process, i) in pairs(models) # for each model in the model list. process=:state; i=pairs(models)[process] - level_1_dep = dep(i) # we get the required types for the model dependencies - length(level_1_dep) == 0 && continue # if there is no dependency we skip the iteration - dep_graph[process].dependency = level_1_dep - for (p, depend) in pairs(level_1_dep) # for each dependency of the model i. p=:leaf_rank; depend=pairs(level_1_dep)[p] - # The dependency can be given as multiscale, e.g. `leaf_area=AbstractLeaf_AreaModel => [m.leaf_symbol],` - # This means we should search this model in another scale. This is not done here, but after the call to this - # function in the other method for `hard_dependencies` below. - if isa(depend, Pair) - if !isnothing(scale) - # We skip this hard-dependency if it is multiscale, we compute this afterwards in this case - target_scales = _normalize_hard_dependency_scales(last(depend), process, p) - push!(dep_not_found, p => (parent_process=process, type=first(depend), scales=target_scales)) - continue - else - # If we are not in a multi-scale setup, we shouldn't use a multiscale model. - # But we still authorize it with a warning, and then proceed searching the dependency in this model list. - verbose && @warn "Model $i has a multiscale hard dependency on $(first(depend)): $depend. Trying to find the model in this scale instead." - depend = first(depend) - end - end - - if hasproperty(models, p) - candidate_model = getfield(models, p) - if model_(candidate_model) isa depend - parent_dep = dep_graph[process] - push!(parent_dep.children, dep_graph[p]) - for child in parent_dep.children - child.parent = parent_dep - end - else - if verbose - @info string( - "Model ", typeof(model_(i)).name.name, " from process ", process, - isnothing(scale) ? "" : " at scale $scale", - " needs a model that is a subtype of ", depend, " in process ", - p - ) - end - - push!(dep_not_found, p => depend) - - push!( - dep_graph[process].missing_dependency, - findfirst(x -> x == p, keys(level_1_dep)) - ) # index of the missing dep - # NB: we can retreive missing deps using dep_graph[process].dependency[dep_graph[process].missing_dependency] - end - else - if verbose - @info string( - "Model ", typeof(model_(i)).name.name, " from process ", process, - isnothing(scale) ? "" : " at scale $scale", - " needs a model that is a subtype of ", depend, " in process ", - p, ", but the process is not parameterized in the ModelMapping." - ) - end - push!(dep_not_found, p => depend) - - push!( - dep_graph[process].missing_dependency, - findfirst(x -> x == p, keys(level_1_dep)) - ) # index of the missing dep - # NB: we can retreive missing deps using dep_graph[process].dependency[dep_graph[process].missing_dependency] - end - end - end - - roots = [AbstractTrees.getroot(i) for i in values(dep_graph)] - # Keeping only the graphs with no common root nodes, i.e. remove graphs that are part of a - # bigger dependency graph: - unique_roots = Dict{Symbol,HardDependencyNode}() - for (p, m) in dep_graph - if m in roots - push!(unique_roots, p => m) - end - end - - return DependencyGraph(unique_roots, dep_not_found) -end - -""" - initialise_all_as_hard_dependency_node(models) - -Take a set of models and initialise them all as a hard dependency node, and -return a dictionary of `:process => HardDependencyNode`. -""" -function initialise_all_as_hard_dependency_node(models, scale) - node_scale = isnothing(scale) ? :Default : Symbol(scale) - dep_graph = Dict( - p => HardDependencyNode( - i, - p, - NamedTuple(), - Int[], - node_scale, - inputs_(i), - outputs_(i), - nothing, - HardDependencyNode[] - ) for (p, i) in pairs(models) - ) - - return dep_graph -end - - -# When we use a mapping (multiscale), we return the set of soft-dependencies (we put the hard-dependencies as their children): -function hard_dependencies(mapping::AbstractDict{Symbol,T}; verbose::Bool=true) where {T} - full_vars_mapping = Dict(first(mod) => Dict(get_mapped_variables(last(mod))) for mod in mapping) - soft_dep_graphs = Dict{Symbol,Any}() - not_found = Dict{Symbol,DataType}() - - mods = Dict(organ => parse_models(get_models(model)) for (organ, model) in mapping) - - # For each scale, move the hard-dependency models as children of the its parent model. - # Note: this is mono-scale at this point (computes each scale independently) - # Since the hard dependencies are inserted into the soft dependency graph as children and aren't referenced elsewhere - # it becomes harder to keep track of them as needed without traversing the graph - # so keep tabs on them during initialisation until they're no longer needed - hard_dependency_dict = Dict{Pair{Symbol,Symbol},HardDependencyNode}() - - hard_deps = Dict(organ => hard_dependencies(mods_scale, scale=organ, verbose=false) for (organ, mods_scale) in mods) - - # Compute the inputs and outputs of all "root" node of the hard dependencies, so the root - # node that takes control over other models appears to have the union of its own inputs (resp. outputs) - # and the ones from its hard dependencies. - #* Note that we compute this before computing the multiscale hard dependencies because the inputs/outputs - #* of hard-dependency models should remain in their own scale. Note that the variables from the hard - #* dependency may not appear in its own scale, but this is treated in the soft-dependency computation - inputs_process = Dict{Symbol,Dict{Symbol,Vector}}() - outputs_process = Dict{Symbol,Dict{Symbol,Vector}}() - for (organ, model) in mapping - # Get the status given by the user, that is used to set the default values of the variables in the mapping: - st_scale_user = get_status(model) - if isnothing(st_scale_user) - st_scale_user = NamedTuple() - else - st_scale_user = NamedTuple(st_scale_user) - end - - status_scale = Dict{Symbol,Vector{Pair{Symbol,NamedTuple}}}() - for (procname, node) in hard_deps[organ].roots # procname = :leaf_surface ; node = hard_deps.roots[procname] - var = Pair{Symbol,NamedTuple}[] - traverse_dependency_graph!(node, x -> variables_multiscale(x, organ, full_vars_mapping, st_scale_user), var) - push!(status_scale, procname => var) - end - - inputs_process[organ] = Dict(key => [j.first => j.second.inputs for j in val] for (key, val) in status_scale) - outputs_process[organ] = Dict(key => [j.first => j.second.outputs for j in val] for (key, val) in status_scale) - end - - # If some models needed as hard-dependency are not found in their own scale, check the other scales: - for (organ, model) in mapping - # organ = :Plant; model = mapping[organ] - # filtering the hard dependency that were defined as multiscale (NamedTuple with information) - multiscale_hard_dep = filter(x -> isa(last(x), NamedTuple), hard_deps[organ].not_found) - for (p, (parent_process, model_type, scales)) in multiscale_hard_dep - # debug: p = :initiation_age; parent_process, model_type, scales = multiscale_hard_dep[p] - parent_node = get_model_nodes(hard_deps[organ], parent_process) - if length(parent_node) == 0 - continue - end - parent_node = only(parent_node) - # The parent node is the one that needs the hard dependency we are searching - is_found = Ref(false) # Flag to check if the model was found in the other scales - for s in scales # s="Phytomer" - dep_node_model = filter(x -> x.scale == s, get_model_nodes(hard_deps[s], p)) - # Note: here we apply a filter because we modify the graph dynamically, and sometimes - # we have already computed multiscale hard-dependencies, which can show up here, - # so we only keep the models that were declared at the scale we are looking. - - if length(dep_node_model) > 0 - is_found[] = true - else - error("Model `$(typeof(parent_node.value))` from scale $organ requires a model of type `$model_type` at scale $s as a hard dependency, but no model was found for this process.") - end - dep_node_model = only(dep_node_model) - - if !(model_(dep_node_model.value) isa model_type) - error("Model `$(typeof(model_(parent_node.value)))` from scale $organ requires a model of type `$model_type` at scale $s as a hard dependency, but the model found for this process is of type $(typeof(model_(dep_node_model.value))).") - end - - # We make a new node out of the previous one: - new_node = HardDependencyNode( - dep_node_model.value, - dep_node_model.process, - dep_node_model.dependency, - dep_node_model.missing_dependency, - dep_node_model.scale, - dep_node_model.inputs, - dep_node_model.outputs, - parent_node, - dep_node_model.children - ) - - # Add our new node as a child of the parent node (the one that requires it as a hard dependency) - push!(parent_node.children, new_node) - - # previously created nested hard dependency nodes' ancestors that have the new_node model as their caller now point to an outdated parent - # (and hard dependency node in an outdated state), so their grandparent when traversing upwards might incorrectly be set to nothing - # update their parent to the correct new node - for ((hd_sym, hd_scale), hd_node) in hard_dependency_dict - - if (hd_node.parent.process == p) && (hd_node.scale == hd_scale) - hd_node.parent = new_node - end - end - - # add the new node to the flat list of hard deps, as they aren't trivial to access in the dep graph, and we might need them later for a couple of things - hard_dependency_dict[Pair(p, new_node.scale)] = new_node - - # If it was a root node, we delete it as a root node. - if dep_node_model in values(hard_deps[s].roots) - delete!(hard_deps[s].roots, p) # We delete the value that has the process as key - end - end - # If the model was found in at least one another scale, delete it from the not_found Dict - is_found[] && delete!(hard_deps[organ].not_found, p) - end - end - - for (organ, model) in mapping - soft_dep_graph = Dict( - process_ => SoftDependencyNode( - soft_dep_vars.value, - process_, # process name - organ, # scale - inputs_process[organ][process_], # These are the inputs, potentially multiscale - outputs_process[organ][process_], # Same for outputs - AbstractTrees.children(soft_dep_vars), # hard dependencies - nothing, - nothing, - SoftDependencyNode[], - [0] # Vector of zeros of length = number of time-steps - ) - for (process_, soft_dep_vars) in hard_deps[organ].roots # proc_ = :carbon_assimilation ; soft_dep_vars = hard_deps.roots[proc_] - ) - - # Update the parent node of the hard dependency nodes to be the new SoftDependencyNode instead of the old - # HardDependencyNode. - for (p, node) in soft_dep_graph - for n in node.hard_dependency - n.parent = node - end - end - - soft_dep_graphs[organ] = (soft_dep_graph=soft_dep_graph, inputs=inputs_process[organ], outputs=outputs_process[organ]) - not_found = merge(not_found, hard_deps[organ].not_found) - end - - return (DependencyGraph(soft_dep_graphs, not_found), hard_dependency_dict) -end diff --git a/src/dependencies/is_graph_cyclic.jl b/src/dependencies/is_graph_cyclic.jl deleted file mode 100644 index 1bfcd0e28..000000000 --- a/src/dependencies/is_graph_cyclic.jl +++ /dev/null @@ -1,77 +0,0 @@ -""" - is_graph_cyclic(dependency_graph::DependencyGraph; full_stack=false, verbose=true) - -Check if the dependency graph is cyclic. - -# Arguments - -- `dependency_graph::DependencyGraph`: the dependency graph to check. -- `full_stack::Bool=false`: if `true`, return the full stack of nodes that makes the cycle, otherwise return only the cycle. -- `warn::Bool=true`: if `true`, print a stylised warning message when a cycle is detected. - -Return a boolean indicating if the graph is cyclic, and the stack of nodes as a vector. -""" -function is_graph_cyclic(dependency_graph::DependencyGraph; full_stack=false, warn=true) - visited = IdDict{Any,Bool}() - recursion_stack = IdDict{Any,Bool}() - - for (root, node) in dependency_graph.roots - cycle_nodes = Any[] - if is_graph_cyclic_(node, visited, recursion_stack, cycle_nodes) - cycle_vec = _cycle_report_nodes(cycle_nodes) - - if full_stack - push!(cycle_vec, _cycle_report_node(node)) - else - # Keep just the cycle (the first node in the vector is the one that makes a cycle, we just detect the second time it happens on the stack): - cycled_nodes = findall(x -> x == cycle_vec[1], cycle_vec) - cycle_vec = cycle_vec[1:cycled_nodes[2]] - end - - warn && @warn "Cyclic dependency detected in the graph: \n $(print_cycle(cycle_vec))" - - return true, cycle_vec - end - end - - return false, visited -end - -function is_graph_cyclic_(node, visited, recursion_stack, cycle_vec) - visited[node] = true - recursion_stack[node] = true - - for child in node.children - if !haskey(visited, child) && is_graph_cyclic_(child, visited, recursion_stack, cycle_vec) - push!(cycle_vec, child) - return true - elseif get(recursion_stack, child, false) - push!(cycle_vec, child) - return true - end - end - - recursion_stack[node] = false - return false -end - -_cycle_report_node(node) = node.value => node.scale -_cycle_report_nodes(nodes) = Pair{Any,Symbol}[_cycle_report_node(node) for node in nodes] - -function print_cycle(cycle_vec) - printed_cycle = Any[Term.RenderableText(string("{bold red}", last(cycle_vec[1]), ": ", typeof(first(cycle_vec[1]))))] - leading_space = [1] - for (m, s) in cycle_vec[2:end] - node_print = string(repeat(" ", leading_space[1]), "└ ", s, ": ", typeof(m)) - if (m => s) == cycle_vec[1] - node_print = Term.RenderableText("{bold red}$node_print") - else - node_print = Term.RenderableText(node_print) - end - - push!(printed_cycle, node_print) - leading_space[1] += 1 - end - - return join(printed_cycle, "") -end diff --git a/src/dependencies/printing.jl b/src/dependencies/printing.jl deleted file mode 100644 index 39c38f061..000000000 --- a/src/dependencies/printing.jl +++ /dev/null @@ -1,146 +0,0 @@ -function draw_dependency_graph( - io, - graphs::DependencyGraph; - title=string("Dependency graph (", length(graphs), " models)"), - max_depth::Int=1, - title_style::String="#FFA726 italic", - guides_style::String="#42A5F5", - dep_graph_guides=(space=" ", vline="│", branch="├", leaf="└", hline="─") -) - - dep_graph_guides = map((g) -> Term.apply_style("{$guides_style}$g{/$guides_style}"), dep_graph_guides) - - max_depth_reached = Ref(max_depth) - - graph_panel = [] - for (p, graph) in graphs.roots - if max_depth_reached[] <= 0 - push!(graph_panel, "...") - break - end - node = [] - # p = :process2; graph = graphs.roots[p] - # typeof(deps[:process4].children[1].hard_dependency.children[1]) - draw_panel(node, graph, "", dep_graph_guides, graph, max_depth_reached; title="Main model") - push!(graph_panel, Term.Panel(node...; fit=true, title=string(p), style="green dim")) - max_depth_reached[] -= 1 - end - - print( - io, - Term.Panel( - graph_panel...; - fit=true, - title="{$(title_style)}$(title){/$(title_style)}", - style="$(title_style) dim" - ) - ) -end - -""" - draw_panel(node, graph, prefix, dep_graph_guides, parent; title="Soft-coupled model") - -Draw the panels for all dependencies -""" -function draw_panel(node, graph, prefix, dep_graph_guides, parent, max_depth_reached=Ref(5); title="Soft-coupled model") - - if max_depth_reached[] <= 0 - push!(node, "...") - return nothing - end - - # If the node has a sibling, draw a branching guide + a horizontal line: - if length(parent.children) <= 1 - is_leaf = true - else - is_leaf = false - end - - panel_hright = string(prefix, repeat(" ", 8)) - panel = draw_model_panel(graph; title=title) - - if graph.parent === nothing && parent == graph - # The current node is the root of the graph: - push!(node, prefix * panel) - else - push!( - node, - draw_guide( - panel.measure.h ÷ 2, - 3, - panel_hright, - is_leaf, - dep_graph_guides - ) * panel - ) - end - # Draw the hard dependencies if any: - if graph isa SoftDependencyNode - # draw a branching guide if there's more soft dependencies after this one: - for child in graph.hard_dependency - draw_panel(node, child, panel_hright, dep_graph_guides, graph, max_depth_reached; title="Hard-coupled model") - max_depth_reached[] -= 1 - end - elseif isa(parent, SoftDependencyNode) && length(parent.children) > 0 - # The current node is a hard dependency of a soft dependency. - # If the parent has more soft dependency children, draw a vline also: - panel_hright = string(prefix, repeat(" ", 8), dep_graph_guides.vline) - end - - # Recursive call: - for child in AbstractTrees.children(graph) - draw_panel(node, child, panel_hright, dep_graph_guides, graph, max_depth_reached; title=title_panel(child)) - max_depth_reached[] -= 1 - end -end - -title_panel(i::SoftDependencyNode) = "Soft-coupled model" -title_panel(i::HardDependencyNode) = "Hard-coupled model" - -function draw_model_panel(i::SoftDependencyNode{T}; title=nothing) where {T} - Term.Panel( - title=title, - string( - "Process: $(i.process)\n", - "Model: $(T)\n", - "Dep: $(i.parent_vars)" - ); - fit=false, - style="blue dim" - ) -end - - -function draw_model_panel(i::HardDependencyNode{T}; title=nothing) where {T} - Term.Panel( - title=title, - string( - "Process: $(i.process)\n", - "Model: $(T)", - length(i.missing_dependency) == 0 ? "" : string( - "\n{red underline}Missing dependencies: ", - join([i.dependency[j] for j in i.missing_dependency], ", "), - "{/red underline}" - ) - ); - fit=true, - style="red dim" - ) -end - - -""" - draw_guide(h, w, prefix, isleaf, guides) - -Draw the line guide for one node of the dependency graph. -""" -function draw_guide(h, w, prefix, isleaf, guides) - header_width = string(prefix, guides.vline, repeat(guides.space, w - 1), "\n") - header = h > 1 ? repeat(header_width, h) : "" - if isleaf - return header * prefix * guides.leaf * repeat(guides.hline, w - 1) - else - footer = h > 1 ? header_width[1:end-1] : "" # NB: we remove the last \n - return header * prefix * guides.branch * repeat(guides.hline, w - 1) * "\n" * footer - end -end diff --git a/src/dependencies/soft_dependencies.jl b/src/dependencies/soft_dependencies.jl deleted file mode 100644 index cf203e5eb..000000000 --- a/src/dependencies/soft_dependencies.jl +++ /dev/null @@ -1,590 +0,0 @@ -""" - soft_dependencies(d::DependencyGraph) - -Return a [`DependencyGraph`](@ref) with the soft dependencies of the processes in the dependency graph `d`. -A soft dependency is a dependency that is not explicitely defined in the model, but that -can be inferred from the inputs and outputs of the processes. - -# Arguments - -- `d::DependencyGraph`: the hard-dependency graph. - -# Example - -```julia -using PlantSimEngine - -# Load the dummy models given as example in the package: -using PlantSimEngine.Examples - -# Create a model list: -models = SingleScaleModelSet( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - process4=Process4Model(), - process5=Process5Model(), - process6=Process6Model(), -) - -# Create the hard-dependency graph: -hard_dep = hard_dependencies(models.models, verbose=true) - -# Get the soft dependencies graph: -soft_dep = soft_dependencies(hard_dep) -``` -""" -function soft_dependencies(d::DependencyGraph{Dict{Symbol,HardDependencyNode}}, nsteps=1) - - # Compute the variables of each node in the hard-dependency graph: - d_vars = Dict{Symbol,Vector{Pair{Symbol,NamedTuple}}}() - for (procname, node) in d.roots - var = Pair{Symbol,NamedTuple}[] - nodes_visited = Set{AbstractDependencyNode}() - traverse_dependency_graph!(node, variables, var; node_visited=nodes_visited) - push!(d_vars, procname => var) - end - - # Note: all variables are collected at once for each hard-coupled nodes - # because they are treated as one process afterwards (see below) - - # Get all nodes of the dependency graph (hard and soft): - # all_nodes = Dict(traverse_dependency_graph(d, x -> x)) - - # Compute the inputs and outputs of each process graph in the dependency graph - inputs_process = Dict{Symbol,Vector{Pair{Symbol,Tuple{Vararg{Symbol}}}}}( - key => [j.first => keys(j.second.inputs) for j in val] for (key, val) in d_vars - ) - outputs_process = Dict{Symbol,Vector{Pair{Symbol,Tuple{Vararg{Symbol}}}}}( - key => [j.first => keys(j.second.outputs) for j in val] for (key, val) in d_vars - ) - - soft_dep_graph = Dict( - process_ => SoftDependencyNode( - soft_dep_vars.value, - process_, # process name - soft_dep_vars.scale, - inputs_(soft_dep_vars.value), - outputs_(soft_dep_vars.value), - AbstractTrees.children(soft_dep_vars), # hard dependencies - nothing, - nothing, - SoftDependencyNode[], - fill(0, nsteps) - ) - for (process_, soft_dep_vars) in d.roots - ) - - independant_process_root = Dict{Symbol,SoftDependencyNode}() - for (proc, i) in soft_dep_graph - # proc = :process3; i = soft_dep_graph[proc] - # Search if the process has soft dependencies: - soft_deps = search_inputs_in_output(proc, inputs_process, outputs_process) - - # Remove the hard dependencies from the soft dependencies: - soft_deps_not_hard = drop_process(soft_deps, [hd.process for hd in i.hard_dependency]) - # NB: if a node is already a hard dependency of the node, it cannot be a soft dependency - - if length(soft_deps_not_hard) == 0 && i.process in keys(d.roots) - # If the process has no soft dependencies, then it is independant (so it is a root) - # Note that the process is only independent if it is also a root in the hard-dependency graph - independant_process_root[proc] = i - else - # If the process has soft dependencies, then it is not independant - # and we need to add its parent(s) to the node, and the node as a child - for (parent_soft_dep, soft_dep_vars) in pairs(soft_deps_not_hard) - # parent_soft_dep = :process5; soft_dep_vars = soft_deps[parent_soft_dep] - - # preventing a cyclic dependency - if parent_soft_dep == proc - error("Cyclic model dependency detected for process $proc") - end - - # preventing a cyclic dependency: if the parent also has a dependency on the current node: - if soft_dep_graph[parent_soft_dep].parent !== nothing && i in soft_dep_graph[parent_soft_dep].parent - error( - "Cyclic dependency detected for process $proc:", - " $proc depends on $parent_soft_dep, which depends on $proc.", - " This is not allowed, but is possible via a hard dependency." - ) - end - - # preventing a cyclic dependency: if the current node has the parent node as a child: - if i.children !== nothing && soft_dep_graph[parent_soft_dep] in i.children - error( - "Cyclic dependency detected for process $proc:", - " $proc depends on $parent_soft_dep, which depends on $proc.", - " This is not allowed, but is possible via a hard dependency." - ) - end - - # Add the current node as a child of the node on which it depends - push!(soft_dep_graph[parent_soft_dep].children, i) - - # Add the node on which the current node depends as a parent - if i.parent === nothing - # If the node had no parent already, it is nothing, so we change into a vector - i.parent = [soft_dep_graph[parent_soft_dep]] - else - push!(i.parent, soft_dep_graph[parent_soft_dep]) - end - - # Add the soft dependencies (variables) of the parent to the current node - i.parent_vars = soft_deps - end - end - end - - return DependencyGraph(independant_process_root, d.not_found) -end - -# For multiscale mapping: -function _owning_soft_dependency_node(node::AbstractDependencyNode) - owner = node - depth = 0 - while !(owner isa SoftDependencyNode) - depth += 1 - depth <= 50 || error( - "Could not resolve the owning soft dependency for nested hard dependency ", - "`$(node.process)` at scale `$(node.scale)`." - ) - owner.parent === nothing && error( - "Nested hard dependency `$(node.process)` at scale `$(node.scale)` has no owning soft dependency." - ) - owner = owner.parent - end - return owner -end - -function _hard_dependency_candidates(parent_process::Symbol, target_scale::Symbol, hard_dep_dict::Dict{Pair{Symbol,Symbol},HardDependencyNode}) - scale_matches = HardDependencyNode[] - process_matches = HardDependencyNode[] - for ((hd_process, hd_scale), hd_node) in hard_dep_dict - hd_process == parent_process || continue - push!(process_matches, hd_node) - (hd_scale == target_scale || hd_node.scale == target_scale) && push!(scale_matches, hd_node) - end - return isempty(scale_matches) ? process_matches : scale_matches -end - -function _soft_graph_at_scale(soft_dep_graphs_roots::DependencyGraph{Dict{Symbol,Any}}, scale::Symbol) - haskey(soft_dep_graphs_roots.roots, scale) || error("Scale `$scale` not found while resolving soft dependency parent.") - return soft_dep_graphs_roots.roots[scale][:soft_dep_graph] -end - -function _resolve_soft_parent_node( - soft_dep_graphs_roots::DependencyGraph{Dict{Symbol,Any}}, - target_scale::Symbol, - parent_process::Symbol, - hard_dep_dict::Dict{Pair{Symbol,Symbol},HardDependencyNode} -) - roots_at_target_scale = _soft_graph_at_scale(soft_dep_graphs_roots, target_scale) - haskey(roots_at_target_scale, parent_process) && return roots_at_target_scale[parent_process] - - candidates = _hard_dependency_candidates(parent_process, target_scale, hard_dep_dict) - isempty(candidates) && error( - "Parent process `$parent_process` at scale `$target_scale` is not located in soft roots or nested hard dependencies." - ) - length(candidates) == 1 || error( - "Parent process `$parent_process` is an ambiguous nested hard dependency for scale `$target_scale`. ", - "Matching hard-dependency scales: $(Tuple((candidate.scale for candidate in candidates)))." - ) - - owner = _owning_soft_dependency_node(only(candidates)) - owner_graph = _soft_graph_at_scale(soft_dep_graphs_roots, owner.scale) - haskey(owner_graph, owner.process) || error( - "Owning soft dependency `$(owner.process)` at scale `$(owner.scale)` was resolved for nested hard dependency ", - "`$parent_process`, but it is not present in the finalized soft graph." - ) - return owner_graph[owner.process] -end - -function soft_dependencies_multiscale(soft_dep_graphs_roots::DependencyGraph{Dict{Symbol,Any}}, reverse_multiscale_mapping, hard_dep_dict::Dict{Pair{Symbol,Symbol},HardDependencyNode}) - - independant_process_root = Dict{Pair{Symbol,Symbol},SoftDependencyNode}() - for (organ, (soft_dep_graph, ins, outs)) in soft_dep_graphs_roots.roots # e.g. organ = :Plant; soft_dep_graph, ins, outs = soft_dep_graphs_roots.roots[organ] - for (proc, i) in soft_dep_graph - # proc = :leaf_surface; i = soft_dep_graph[proc] - # Search if the process has soft dependencies: - soft_deps = search_inputs_in_output(proc, ins, outs) - - # Remove the hard dependencies from the soft dependencies: - soft_deps_not_hard = drop_process(soft_deps, [hd.process for hd in i.hard_dependency]) - - hard_dependencies_from_other_scale = [hd for hd in i.hard_dependency if hd.scale != i.scale] - - # NB: if a node is already a hard dependency of the node, it cannot be a soft dependency - - # Check if the process has soft dependencies at other scales: - soft_deps_multiscale = search_inputs_in_multiscale_output(proc, organ, ins, soft_dep_graphs_roots.roots, reverse_multiscale_mapping, hard_dependencies_from_other_scale) - # Example output: :Soil => Dict(:soil_water=>[:soil_water_content]), which means that the variable :soil_water_content - # is computed by the process :soil_water at the scale :Soil. - - if length(soft_deps_not_hard) == 0 && i.process in keys(soft_dep_graph) && length(soft_deps_multiscale) == 0 - # If the process has no soft (multiscale) dependencies, then it is independant (so it is a root) - # Note that the process is only independent if it is also a root in the hard-dependency graph - independant_process_root[organ=>proc] = i - else - # If the process has soft dependencies at its scale, add it: - if length(soft_deps_not_hard) > 0 - # If the process has soft dependencies, then it is not independant - # and we need to add its parent(s) to the node, and the node as a child - for (parent_soft_dep, soft_dep_vars) in pairs(soft_deps_not_hard) - - parent_node = _resolve_soft_parent_node(soft_dep_graphs_roots, i.scale, parent_soft_dep, hard_dep_dict) - - - - # preventing a cyclic dependency - if parent_soft_dep == proc - error("Cyclic model dependency detected for process $proc from organ $organ.") - end - - # preventing a cyclic dependency: if the parent also has a dependency on the current node: - if parent_node.parent !== nothing && i in parent_node.parent - error( - "Cyclic dependency detected for process $proc from organ $organ:", - " $proc depends on $parent_soft_dep, which depends on $proc.", - " This is not allowed, but is possible via a hard dependency." - ) - end - - # preventing a cyclic dependency: if the current node has the parent node as a child: - if i.children !== nothing && parent_node in i.children - error( - "Cyclic dependency detected for process $proc from organ $organ:", - " $proc depends on $parent_soft_dep, which depends on $proc.", - " This is not allowed, but is possible via a hard dependency." - ) - end - - i in parent_node.children && error("Cyclic dependency detected for process $proc from organ $organ.") - - # Add the current node as a child of the node on which it depends - push!(parent_node.children, i) - - # Add the node on which the current node depends as a parent - if i.parent === nothing - # If the node had no parent already, it is nothing, so we change into a vector - i.parent = [parent_node] - else - parent_node in i.parent && error("Cyclic dependency detected for process $proc from organ $organ.") - push!(i.parent, parent_node) - end - - # Add the soft dependencies (variables) of the parent to the current node - i.parent_vars = soft_deps - end - end - - # If the node has soft dependencies at other scales, add it as child of the other scale (and add its parent too): - if length(soft_deps_multiscale) > 0 - for org in keys(soft_deps_multiscale) - for (parent_soft_dep, soft_dep_vars) in soft_deps_multiscale[org] - - parent_node = _resolve_soft_parent_node(soft_dep_graphs_roots, org, parent_soft_dep, hard_dep_dict) - - # preventing a cyclic dependency: if the parent also has a dependency on the current node: - if parent_node.parent !== nothing && any([i == p for p in parent_node.parent]) - error( - "Cyclic dependency detected for process $proc:", - " $proc for organ $organ depends on $parent_soft_dep from organ $org, which depends on the first one", - " This is not allowed, you may need to develop a new process that does the whole computation by itself." - ) - end - - # preventing a cyclic dependency: if the current node has the parent node as a child: - if i.children !== nothing && parent_node in i.children - error( - "Cyclic dependency detected for process $proc:", - " $proc for organ $organ depends on $parent_soft_dep from organ $org, which depends on the first one.", - " This is not allowed, you may need to develop a new process that does the whole computation by itself." - ) - end - - - if !(i in parent_node.children) # && error("Cyclic dependency detected for process $proc from organ $organ.") - - # Add the current node as a child of the node on which it depends: - push!(parent_node.children, i) - end - # Add the node on which the current node depends as a parent - if i.parent === nothing - # If the node had no parent already, it is nothing, so we change into a vector - i.parent = [parent_node] - else - if !(parent_node in i.parent) # && error("Cyclic dependency detected for process $proc from organ $organ.") - push!(i.parent, parent_node) - end - end - - # Add the multiscale soft dependencies variables of the parent to the current node - i.parent_vars = NamedTuple(Symbol(k) => NamedTuple(v) for (k, v) in soft_deps_multiscale) - end - end - end - end - end - end - - return DependencyGraph(independant_process_root, soft_dep_graphs_roots.not_found) -end - - -""" - drop_process(proc_vars, process) - -Return a new `NamedTuple` with the process `process` removed from the `NamedTuple` `proc_vars`. - -# Arguments - -- `proc_vars::NamedTuple`: the `NamedTuple` from which we want to remove the process `process`. -- `process::Symbol`: the process we want to remove from the `NamedTuple` `proc_vars`. - -# Returns - -A new `NamedTuple` with the process `process` removed from the `NamedTuple` `proc_vars`. - -# Example - -```julia -julia> drop_process((a = 1, b = 2, c = 3), :b) -(a = 1, c = 3) - -julia> drop_process((a = 1, b = 2, c = 3), (:a, :c)) -(b = 2,) -``` -""" -drop_process(proc_vars, process::Symbol) = Base.structdiff(proc_vars, NamedTuple{(process,)}) -drop_process(proc_vars, process) = Base.structdiff(proc_vars, NamedTuple{(process...,)}) - -""" - search_inputs_in_output(process, inputs, outputs) - -Return a dictionary with the soft dependencies of the processes in the dependency graph `d`. -A soft dependency is a dependency that is not explicitely defined in the model, but that -can be inferred from the inputs and outputs of the processes. - -# Arguments - -- `process::Symbol`: the process for which we want to find the soft dependencies. -- `inputs::Dict{Symbol, Vector{Pair{Symbol}, Tuple{Symbol, Vararg{Symbol}}}}`: a dict of process => symbols of inputs per process. -- `outputs::Dict{Symbol, Tuple{Symbol, Vararg{Symbol}}}`: a dict of process => symbols of outputs per process. - -# Details - -The inputs (and similarly, outputs) give the inputs of each process, classified by the process it comes from. It can -come from itself (its own inputs), or from another process that is a hard-dependency. - -# Returns - -A dictionary with the soft dependencies for the processes. - -# Example - -```julia -in_ = Dict( - :process3 => [:process3=>(:var4, :var5), :process2=>(:var1, :var3), :process1=>(:var1, :var2)], - :process4 => [:process4=>(:var0,)], - :process6 => [:process6=>(:var7, :var9)], - :process5 => [:process5=>(:var5, :var6)], -) - -out_ = Dict( - :process3 => Pair{Symbol}[:process3=>(:var4, :var6), :process2=>(:var4, :var5), :process1=>(:var3,)], - :process4 => [:process4=>(:var1, :var2)], - :process6 => [:process6=>(:var8,)], - :process5 => [:process5=>(:var7,)], -) - -search_inputs_in_output(:process3, in_, out_) -(process4 = (:var1, :var2),) -``` -""" -function search_inputs_in_output(process, inputs, outputs) - # proc, ins, outs - # get the inputs of the node: - vars_input = flatten_vars(inputs[process]) - - inputs_as_output_of_process = Dict() - for (proc_output, pairs_vars_output) in outputs # e.g. proc_output = :carbon_biomass; pairs_vars_output = outs[proc_output] - if process != proc_output - vars_output = flatten_vars(pairs_vars_output) - inputs_in_outputs = vars_in_variables(vars_input, vars_output) - - if any(inputs_in_outputs) - ins_in_outs = [vars_input...][inputs_in_outputs] - - # Remove the variables that are computed at the previous time step (used to break a cyclic dependency): - filter!(x -> !isa(x, MappedVar) || !isa(mapped_variable(x), PreviousTimeStep), ins_in_outs) - - # variables in the inputs of proc_input that are in the outputs of proc_output: - length(ins_in_outs) > 0 && push!(inputs_as_output_of_process, proc_output => Tuple(ins_in_outs)) - # Note: proc_output is the process that computes the inputs of proc_input - # These inputs are given by `vars_input[inputs_in_outputs]` - end - end - end - - return NamedTuple(inputs_as_output_of_process) -end - -function vars_in_variables(vars::T1, variables::T2) where {T1<:NamedTuple,T2<:NamedTuple} - [i in keys(variables) for i in keys(vars)] -end - -function vars_in_variables(vars, variables) - [i in variables for i in vars] -end - -""" - search_inputs_in_multiscale_output(process, organ, inputs, soft_dep_graphs) - -# Arguments - -- `process::Symbol`: the process for which we want to find the soft dependencies at other scales. -- `organ::Symbol`: the organ for which we want to find the soft dependencies. -- `inputs::Dict{Symbol, Vector{Pair{Symbol}, Tuple{Symbol, Vararg{Symbol}}}}`: a dict of process => [:subprocess => (:var1, :var2)]. -- `soft_dep_graphs::Dict{Symbol, ...}`: a dict of organ => (soft_dep_graph, inputs, outputs). -- `rev_mapping::Dict{Symbol, Symbol}`: a dict of mapped variable => source variable (this is the reverse mapping). -- 'hard_dependencies_from_other_scale' : a vector of HardDependencyNode to provide access to the hard dependencies without traversing the whole graph - -# Details - -The inputs (and similarly, outputs) give the inputs of each process, classified by the process it comes from. It can -come from itself (its own inputs), or from another process that is a hard-dependency. - -# Returns - -A dictionary with the soft dependencies variables found in outputs of other scales for each process, e.g.: - -```julia -Dict{Symbol, Dict{Symbol, Vector{Symbol}}} with 2 entries: - :Internode => Dict(:carbon_demand=>[:carbon_demand]) - :Leaf => Dict(:carbon_assimilation=>[:carbon_assimilation], :carbon_demand=>[:carbon_demand]) -``` - -This means that the variable `:carbon_demand` is computed by the process `:carbon_demand` at the scale `:Internode`, and the variable `:carbon_assimilation` -is computed by the process `:carbon_assimilation` at the scale `:Leaf`. Those variables are used as inputs for the process that we just passed. -""" -function search_inputs_in_multiscale_output(process, organ, inputs, soft_dep_graphs, rev_mapping, hard_dependencies_from_other_scale) - # proc, organ, ins, soft_dep_graphs=soft_dep_graphs_roots.roots - vars_input = flatten_vars(inputs[process]) - - inputs_as_output_of_other_scale = Dict{Symbol,Dict{Symbol,Vector{ProducerVariable}}}() - for (var, val) in pairs(vars_input) # e.g. var = :leaf_surfaces;val = vars_input[var] - # The variable is a multiscale variable: - if isa(val, MappedVar) - var_organ = mapped_organ(val) - isnothing(var_organ) && continue # If the variable maps to nothing we skip it (e.g. [PreviousTimeStep(:var1)] or [:var => :new_var]) - if !isa(var_organ, AbstractVector) - # In case the organ is given as a singleton (e.g. :Soil instead of [:Soil]) - var_organ = [var_organ] - end - - if !all(var_o != organ for var_o in var_organ) - error("$var in process $process is set to be multiscale, but points to its own scale ($organ). This is not allowed.") - end - for org in var_organ # e.g. org = :Leaf - # The variable is a multiscale variable: - haskey(soft_dep_graphs, org) || error("Scale $org not found in the mapping, but mapped to the $organ scale.") - mapped_var = mapped_variable(val) - isa(mapped_var, PreviousTimeStep) && continue # Because we don't want to add the previous time step as a dependency - - # Avoid collecting variables at other scales if they come from a hard dependency - # They are handled internally by the hard dep, so if a hard dependency contains that variable, don't add it - # (This only needs to be done one level beneath the soft dependency nodes, any hard dependencies internal to another one don't expose their variables here) - - in_hard_dep::Bool = false - hd_os_current_scale = filter(x -> x.scale == org, hard_dependencies_from_other_scale) - for hd_os in hd_os_current_scale - hd_os_output_vars = [first(p) for p in pairs(hd_os.outputs)] - in_hard_dep |= length(filter(x -> x == var, hd_os_output_vars)) > 0 - end - !in_hard_dep && add_input_as_output!(inputs_as_output_of_other_scale, soft_dep_graphs, org, source_variable(val, org), mapped_var) - end - elseif isa(val, UninitializedVar) && haskey(rev_mapping, organ) - # The variable may be a variable written by another scale: - for (organ_source, proc_vars_dict) in rev_mapping[organ] - if haskey(proc_vars_dict, var) - add_input_as_output!(inputs_as_output_of_other_scale, soft_dep_graphs, organ_source, var, proc_vars_dict[var]) - end - end - end - end - - return inputs_as_output_of_other_scale -end - - -function add_input_as_output!(inputs_as_output_of_other_scale, soft_dep_graphs, organ_source, variable, value) - producer_var = ProducerVariable(value, variable) - for (proc_output, pairs_vars_output) in soft_dep_graphs[organ_source][:outputs] # e.g. proc_output = :maintenance_respiration; pairs_vars_output = soft_dep_graphs_roots.roots[organ_source][:outputs][proc_output] - vars_output = flatten_vars(pairs_vars_output) - - # If the variable is found in the outputs of the process at the other scale: - if variable in keys(vars_output) - # The variable is found at another scale: - if haskey(inputs_as_output_of_other_scale, organ_source) - if haskey(inputs_as_output_of_other_scale[organ_source], proc_output) - push!(inputs_as_output_of_other_scale[organ_source][proc_output], producer_var) - else - inputs_as_output_of_other_scale[organ_source][proc_output] = [producer_var] - end - else - inputs_as_output_of_other_scale[organ_source] = Dict(proc_output => [producer_var]) - end - end - end -end -""" - flatten_vars(vars) - -Return a set of the variables in the `vars` dictionary. - -# Arguments - -- `vars::Dict{Symbol, Tuple{Symbol, Vararg{Symbol}}}`: a dict of process => namedtuple of variables => value. - -# Returns - -A set of the variables in the `vars` dictionary. - -# Example - -```julia -julia> flatten_vars(Dict(:process1 => (:var1, :var2), :process2 => (:var3, :var4))) -Set{Symbol} with 4 elements: - :var4 - :var3 - :var2 - :var1 -``` - -```julia -julia> flatten_vars([:process1 => (var1 = -Inf, var2 = -Inf), :process2 => (var3 = -Inf, var4 = -Inf)]) -(var2 = -Inf, var4 = -Inf, var3 = -Inf, var1 = -Inf) -``` -""" -function flatten_vars(vars) - vars_input = Set() - for (key, val) in vars - flatten_vars(val, vars_input) - end - format_flatten((vars_input...,)) -end - -function flatten_vars(val::NamedTuple, vars_input::Set) - for (k, j) in pairs(val) - push!(vars_input, k => j) - end -end - -function flatten_vars(val::Tuple, vars_input::Set) - for j in val - push!(vars_input, j) - end -end - -format_flatten(vars::Tuple{Vararg{Pair}}) = NamedTuple(vars) -format_flatten(vars) = vars diff --git a/src/dependencies/traversal.jl b/src/dependencies/traversal.jl deleted file mode 100644 index d61a0133f..000000000 --- a/src/dependencies/traversal.jl +++ /dev/null @@ -1,174 +0,0 @@ -""" - traverse_dependency_graph(graph::DependencyGraph, f::Function; visit_hard_dep=true) - traverse_dependency_graph(graph; visit_hard_dep=true) - -Traverse the dependency `graph` and apply the function `f` to each node, or just return the nodes if `f` is not provided. - -The first-level soft-dependencies are traversed first, then their hard-dependencies (if `visit_hard_dep=true`), and then -the children of the soft-dependencies. - -Return a vector of pairs of the node and the result of the function `f`. - -# Example - -```julia -using PlantSimEngine - -# Including example processes and models: -using PlantSimEngine.Examples; - -function f(node) - node.value -end - -vars = ( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - process4=Process4Model(), - process5=Process5Model(), - process6=Process6Model(), - process7=Process7Model(), -) - -graph = dep(vars) -traverse_dependency_graph(graph, f) -``` -""" -function traverse_dependency_graph( - graph::DependencyGraph, - f::Function; - visit_hard_dep=true -) - - var = Pair{Symbol,Any}[] - nodes_types = visit_hard_dep ? AbstractDependencyNode : SoftDependencyNode - node_visited = Set{nodes_types}() - for (p, root) in graph.roots - traverse_dependency_graph!(root, f, var; visit_hard_dep=visit_hard_dep, node_visited=node_visited) - end - - return var -end - -function traverse_dependency_graph( - graph::DependencyGraph, - visit_hard_dep=true -) - nodes_types = visit_hard_dep ? AbstractDependencyNode : SoftDependencyNode - var = Pair{Symbol,nodes_types}[] - node_visited = Set{nodes_types}() - for (p, root) in graph.roots - traverse_dependency_graph!(root, x -> x, var; visit_hard_dep=visit_hard_dep, node_visited=node_visited) - end - - return last.(var) -end - -function traverse_dependency_graph!(f::Function, graph::DependencyGraph; visit_hard_dep=true, node_visited::Set=Set{AbstractDependencyNode}()) - for (p, root) in graph.roots - traverse_dependency_graph!(f, root, visit_hard_dep=visit_hard_dep, node_visited=node_visited) - end -end - -function traverse_dependency_graph!(f::Function, node::SoftDependencyNode; visit_hard_dep=true, node_visited::Set=Set{AbstractDependencyNode}()) - if node in node_visited - return nothing - end - - f(node) - - push!(node_visited, node) - - # Traverse the hard dependencies of the SoftDependencyNode if any: - if visit_hard_dep && node isa SoftDependencyNode - # draw a branching guide if there's more soft dependencies after this one: - for child in node.hard_dependency - traverse_dependency_graph!(f, child; visit_hard_dep=visit_hard_dep, node_visited=node_visited) - end - end - - for child in node.children - traverse_dependency_graph!(f, child; visit_hard_dep=visit_hard_dep, node_visited=node_visited) - end -end - -function traverse_dependency_graph!(f::Function, node::HardDependencyNode; visit_hard_dep=true, node_visited::Set=Set{AbstractDependencyNode}()) - if node in node_visited - return nothing - end - - f(node) - - push!(node_visited, node) - - # Traverse all hard dependencies: - for child in node.children - traverse_dependency_graph!(f, child; visit_hard_dep=visit_hard_dep, node_visited=node_visited) - end -end - - -""" - traverse_dependency_graph(node::SoftDependencyNode, f::Function, var::Vector; visit_hard_dep=true) - -Apply function `f` to `node`, visit its hard dependency nodes (if `visit_hard_dep=true`), and -then its soft dependency children. - -Mutate the vector `var` by pushing a pair of the node process name and the result of the function `f`. -""" -function traverse_dependency_graph!( - node::SoftDependencyNode, - f::Function, - var::Vector; - visit_hard_dep=true, - node_visited::Set=Set{AbstractDependencyNode}() -) - if node in node_visited - return nothing - end - - push!(var, node.process => f(node)) - - push!(node_visited, node) - - # Traverse the hard dependencies of the SoftDependencyNode if any: - if visit_hard_dep && node isa SoftDependencyNode - # draw a branching guide if there's more soft dependencies after this one: - for child in node.hard_dependency - traverse_dependency_graph!(child, f, var; visit_hard_dep=visit_hard_dep, node_visited=node_visited) - end - end - - for child in node.children - traverse_dependency_graph!(child, f, var; visit_hard_dep=visit_hard_dep, node_visited=node_visited) - end -end - -""" - traverse_dependency_graph(node::HardDependencyNode, f::Function, var::Vector) - -Apply function `f` to `node`, and then its children (hard-dependency nodes). - -Mutate the vector `var` by pushing a pair of the node process name and the result of the function `f`. -""" -function traverse_dependency_graph!( - node::HardDependencyNode, - f::Function, - var::Vector; - visit_hard_dep=true, # Just to be compatible with a call shared with SoftDependencyNode method - node_visited::Set=Set{HardDependencyNode}() -) - - if node in node_visited - return nothing - end - - push!(var, node.process => f(node)) - - push!(node_visited, node) - - for child in node.children - traverse_dependency_graph!(child, f, var; visit_hard_dep=visit_hard_dep, node_visited=node_visited) - end -end diff --git a/src/dependencies/update_dependencies.jl b/src/dependencies/update_dependencies.jl deleted file mode 100644 index 4d6f74cec..000000000 --- a/src/dependencies/update_dependencies.jl +++ /dev/null @@ -1,189 +0,0 @@ -function _model_specs_for_dependency_updates(vars::NamedTuple) - return Dict( - :Default => Dict{Symbol,ModelSpec}( - process(model) => as_model_spec(model) for model in values(vars) - ) - ) -end - -_model_specs_for_dependency_updates(mapping::AbstractDict{Symbol,T}) where {T} = - Dict(scale => parse_model_specs(declarations) for (scale, declarations) in pairs(mapping)) - -function _update_variables_for_spec(spec::ModelSpec) - vars = Set{Symbol}() - for update in updates(spec) - union!(vars, update.variables) - end - return vars -end - -function _update_after_for_var(spec::ModelSpec, var::Symbol) - after = Symbol[] - for update in updates(spec) - var in update.variables || continue - append!(after, update.after) - end - return unique(after) -end - -function _canonical_output_vars(spec::ModelSpec) - vars = Symbol[] - routing = output_routing(spec) - for var in keys(outputs_(spec)) - mode = var in keys(routing) ? routing[var] : :canonical - mode == :stream_only && continue - push!(vars, var) - end - return vars -end - -function _validate_updates_for_scale(scale::Symbol, specs_at_scale::Dict{Symbol,ModelSpec}, ignored_processes::Set{Symbol}=Set{Symbol}()) - canonical_writers = Dict{Symbol,Vector{Symbol}}() - - for (process, spec) in specs_at_scale - model_outputs = Set(keys(outputs_(spec))) - for update in updates(spec) - isempty(update.after) && error( - "Updates declaration for process `$(process)` at scale `$(scale)` must specify `after=...`. ", - "Use for example `Updates(:var; after=:producer_process)`." - ) - for var in update.variables - var in model_outputs || error( - "Updates declaration for process `$(process)` at scale `$(scale)` mentions variable `$(var)`, ", - "but this model does not output `$(var)`." - ) - for after_process in update.after - haskey(specs_at_scale, after_process) || error( - "Updates declaration for variable `$(var)` in process `$(process)` at scale `$(scale)` ", - "references unknown process `$(after_process)`." - ) - var in keys(outputs_(specs_at_scale[after_process])) || error( - "Updates declaration `Updates(:$(var); after=:$(after_process))` in process `$(process)` ", - "at scale `$(scale)` requires `$(after_process)` to output `$(var)`." - ) - end - end - end - - process in ignored_processes && continue - for var in _canonical_output_vars(spec) - push!(get!(canonical_writers, var, Symbol[]), process) - end - end - - for (var, writers) in canonical_writers - length(writers) <= 1 && continue - updater_flags = Dict(process => (var in _update_variables_for_spec(specs_at_scale[process])) for process in writers) - primary_writers = [process for process in writers if !updater_flags[process]] - update_writers = [process for process in writers if updater_flags[process]] - - length(primary_writers) == 1 || error( - "Ambiguous canonical writers for variable `$(var)` at scale `$(scale)`: ", - join(writers, ", "), - ". Keep one primary writer and declare additional writers with `Updates(:$(var); after=:primary_process)`." - ) - - primary = only(primary_writers) - for updater in update_writers - after = _update_after_for_var(specs_at_scale[updater], var) - primary in after || error( - "Update writer `$(updater)` for variable `$(var)` at scale `$(scale)` must declare its primary producer in `after`. ", - "Use `Updates(:$(var); after=:$(primary))` or include `:$(primary)` in the `after` list." - ) - end - - for i in eachindex(update_writers), j in (i+1):length(update_writers) - left = update_writers[i] - right = update_writers[j] - left_after = _update_after_for_var(specs_at_scale[left], var) - right_after = _update_after_for_var(specs_at_scale[right], var) - (left in right_after || right in left_after) || error( - "Update writers `$(left)` and `$(right)` both update variable `$(var)` at scale `$(scale)` ", - "without an ordering relation. Declare one updater `after` the other." - ) - end - end - - return nothing -end - -function validate_update_dependencies( - model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}; - ignored_processes_by_scale::Dict{Symbol,Set{Symbol}}=Dict{Symbol,Set{Symbol}}() -) - for (scale, specs_at_scale) in model_specs - _validate_updates_for_scale(scale, specs_at_scale, get(ignored_processes_by_scale, scale, Set{Symbol}())) - end - return nothing -end - -function _soft_nodes_by_scale_process(graph::DependencyGraph) - nodes = Dict{Tuple{Symbol,Symbol},SoftDependencyNode}() - for node in traverse_dependency_graph(graph, false) - nodes[(node.scale, node.process)] = node - end - return nodes -end - -function _delete_root_for_node!(graph::DependencyGraph, node::SoftDependencyNode) - if haskey(graph.roots, node.process) - delete!(graph.roots, node.process) - end - key = node.scale => node.process - if haskey(graph.roots, key) - delete!(graph.roots, key) - end - return nothing -end - -function _add_update_edge!(graph::DependencyGraph, parent_node::SoftDependencyNode, child_node::SoftDependencyNode) - parent_node === child_node && error( - "Invalid Updates dependency: process `$(child_node.process)` cannot be ordered after itself." - ) - - child_node in parent_node.children || push!(parent_node.children, child_node) - if child_node.parent === nothing - child_node.parent = [parent_node] - elseif !(parent_node in child_node.parent) - push!(child_node.parent, parent_node) - end - _delete_root_for_node!(graph, child_node) - return nothing -end - -function apply_update_dependencies!(graph::DependencyGraph, model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}) - validate_hard_dependency_timestep_consistency(model_specs, graph) - ignored_processes_by_scale = _hard_dependency_children(graph) - validate_update_dependencies(model_specs; ignored_processes_by_scale=ignored_processes_by_scale) - has_updates = any(!isempty(updates(spec)) for specs_at_scale in values(model_specs) for spec in values(specs_at_scale)) - has_updates || return graph - - nodes = _soft_nodes_by_scale_process(graph) - - for (scale, specs_at_scale) in model_specs - ignored_processes = get(ignored_processes_by_scale, scale, Set{Symbol}()) - for (process, spec) in specs_at_scale - process in ignored_processes && continue - child_node = get(nodes, (scale, process), nothing) - isnothing(child_node) && continue - for update in updates(spec) - for after_process in update.after - parent_node = get(nodes, (scale, after_process), nothing) - isnothing(parent_node) && error( - "Updates declaration for process `$(process)` at scale `$(scale)` references `$(after_process)`, ", - "but that process is not an executable dependency node. It may be nested as a hard dependency." - ) - _add_update_edge!(graph, parent_node, child_node) - end - end - end - end - - iscyclic, cycle_vec = is_graph_cyclic(graph; warn=false) - iscyclic && error( - "Cyclic dependency detected after applying Updates declarations. Cycle: \n", - print_cycle(cycle_vec) - ) - - return graph -end diff --git a/src/doc_templates/mtg-related.jl b/src/doc_templates/mtg-related.jl deleted file mode 100644 index 96cb86bee..000000000 --- a/src/doc_templates/mtg-related.jl +++ /dev/null @@ -1,67 +0,0 @@ -const MTG_EXAMPLE = """ -```@example -mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)); -``` - -```@example -soil = Node(mtg, MultiScaleTreeGraph.NodeMTG("/", :Soil, 1, 1)); -``` - -```@example -plant = Node(mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)); -``` - -```@example -internode1 = Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)); -``` - -```@example -leaf1 = Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)); -``` - -```@example -internode2 = Node(internode1, MultiScaleTreeGraph.NodeMTG("<", :Internode, 1, 2)); -``` - -```@example -leaf2 = Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)); -``` -""" - -const MAPPING_EXAMPLE = """ -```@example -mapping = PlantSimEngine.ModelMapping( \ - :Plant => ( \ - PlantSimEngine.MultiScaleModel( \ - model=ToyCAllocationModel(), \ - mapped_variables=[ \ - :carbon_assimilation => [:Leaf], \ - :carbon_demand => [:Leaf, :Internode], \ - :carbon_allocation => [:Leaf, :Internode] \ - ], \ - ), - PlantSimEngine.MultiScaleModel( \ - model=ToyPlantRmModel(), \ - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],] \ - ), \ - ),\ - :Internode => ( \ - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), \ - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), \ - Status(TT=10.0) \ - ), \ - :Leaf => ( \ - PlantSimEngine.MultiScaleModel( \ - model=ToyAssimModel(), \ - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], \ - ), \ - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), \ - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), \ - Status(aPPFD=1300.0, TT=10.0), \ - ), \ - :Soil => ( \ - ToySoilWaterModel(), \ - ), \ -) -``` -""" diff --git a/src/evaluation/fit.jl b/src/evaluation/fit.jl index de0652ea0..6428c48d6 100644 --- a/src/evaluation/fit.jl +++ b/src/evaluation/fit.jl @@ -29,10 +29,26 @@ and `Ri_PAR_f`. # Including example processes and models: using PlantSimEngine.Examples; -m = PlantSimEngine.ModelMapping(Beer(0.6), status=(LAI=2.0,)) -meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_f=300.0) -run!(m, meteo) -df = DataFrame(aPPFD=m[:aPPFD][1], LAI=m.status.LAI[1], Ri_PAR_f=meteo.Ri_PAR_f[1]) +meteo = Atmosphere( + T=20.0, + Wind=1.0, + P=101.3, + Rh=0.65, + Ri_PAR_f=300.0, + duration=Hour(1), +) +scene = Scene( + Object(:leaf; scale=:Leaf, status=Status(LAI=2.0)); + applications=(ModelSpec(Beer(0.6)) |> AppliesTo(One(scale=:Leaf)),), + environment=meteo, +) +run!(scene) +leaf = only(scene_objects(scene; scale=:Leaf)) +df = DataFrame( + aPPFD=leaf.status.aPPFD, + LAI=leaf.status.LAI, + Ri_PAR_f=meteo.Ri_PAR_f[1], +) fit(Beer, df) ``` diff --git a/src/examples_import.jl b/src/examples_import.jl index be89fc4d3..d3460c39f 100644 --- a/src/examples_import.jl +++ b/src/examples_import.jl @@ -33,7 +33,6 @@ include(joinpath(@__DIR__, "../examples/ToyRUEGrowthModel.jl")) include(joinpath(@__DIR__, "../examples/ToyCAllocationModel.jl")) include(joinpath(@__DIR__, "../examples/ToyMaintenanceRespirationModel.jl")) include(joinpath(@__DIR__, "../examples/ToySoilModel.jl")) -include(joinpath(@__DIR__, "../examples/ToyInternodeEmergence.jl")) include(joinpath(@__DIR__, "../examples/ToyCBiomassModel.jl")) include(joinpath(@__DIR__, "../examples/ToyLeafSurfaceModel.jl")) include(joinpath(@__DIR__, "../examples/ToyLightPartitioningModel.jl")) @@ -82,7 +81,6 @@ export AbstractLai_DynamicModel, AbstractLeaf_SurfaceModel export AbstractDegreedaysModel export AbstractCarbon_AssimilationModel, AbstractCarbon_AllocationModel, AbstractCarbon_DemandModel, AbstractCarbon_BiomassModel export AbstractSoil_WaterModel, AbstractGrowthModel -export AbstractOrgan_EmergenceModel export AbstractMaintenance_RespirationModel # Models: @@ -92,6 +90,5 @@ export ToyAssimGrowthModel, ToyRUEGrowthModel, ToyMaintenanceRespirationModel, T export Process1Model, Process2Model, Process3Model, Process4Model, Process5Model export Process6Model, Process7Model -export ToyInternodeEmergence export import_mtg_example -end \ No newline at end of file +end diff --git a/src/mtg/GraphSimulation.jl b/src/mtg/GraphSimulation.jl deleted file mode 100644 index 5ba81b4f1..000000000 --- a/src/mtg/GraphSimulation.jl +++ /dev/null @@ -1,150 +0,0 @@ -""" - GraphSimulation(graph, mapping) - GraphSimulation(graph, statuses, dependency_graph, models, outputs) - -A type that holds all information for a simulation over a graph. - -# Arguments - -- `graph`: an graph, such as an MTG -- `mapping`: a dictionary of model mapping -- `statuses`: a structure that defines the status of each node in the graph -- `status_templates`: a dictionary of status templates -- `reverse_multiscale_mapping`: a dictionary of mapping for other scales -- `var_need_init`: a dictionary indicating if a variable needs to be initialized -- `dependency_graph`: the dependency graph of the models applied to the graph -- `models`: a dictionary of models -- `model_specs`: a dictionary of normalized model usage specifications -- `outputs`: a dictionary of outputs -- `temporal_state`: multi-rate temporal storage used at runtime for producer streams, - input resolution caches, run clocks, and requested output export buffers -""" -struct GraphSimulation{T,S,U,O,V,TS,MS} - graph::T - statuses::S - status_templates::Dict{Symbol,Dict{Symbol,Any}} - reverse_multiscale_mapping::ReverseMultiscaleMapping - var_need_init::Dict{Symbol,V} - dependency_graph::DependencyGraph - models::Dict{Symbol,U} - model_specs::MS - outputs::Dict{Symbol,O} - outputs_index::Dict{Symbol,Int} - temporal_state::TS - is_multirate::Bool -end - -function GraphSimulation(graph, mapping; nsteps=1, outputs=nothing, type_promotion=_type_promotion(mapping), check=true, verbose=false) - mapping_checked = mapping isa ModelMapping ? mapping : ModelMapping(mapping) - GraphSimulation(init_simulation(graph, mapping_checked; nsteps=nsteps, outputs=outputs, type_promotion=type_promotion, check=check, verbose=verbose)...) -end - -dep(g::GraphSimulation) = g.dependency_graph -status(g::GraphSimulation) = g.statuses -status_template(g::GraphSimulation) = g.status_templates -reverse_mapping(g::GraphSimulation) = g.reverse_multiscale_mapping -var_need_init(g::GraphSimulation) = g.var_need_init -get_models(g::GraphSimulation) = g.models -get_model_specs(g::GraphSimulation) = g.model_specs -outputs(g::GraphSimulation) = g.outputs -temporal_state(g::GraphSimulation) = g.temporal_state -is_multirate(g::GraphSimulation) = g.is_multirate - -""" - convert_outputs(sim_outputs::Dict{Symbol,O} where O, sink; refvectors=false, no_value=nothing) - convert_outputs(sim_outputs::TimeStepTable{T} where T, sink) - -Convert the outputs returned by a simulation made on a plant graph into another format. - -# Details - -The first method operates on the outputs of a multiscale simulation, the second one on those of a typical single-scale simulation. -The sink function determines the format used, for exemple a `DataFrame`. - -# Arguments - -- `sim_outputs : the outputs of a prior simulation, typically returned by `run!`. -- `sink`: a sink compatible with the Tables.jl interface (*e.g.* a `DataFrame`) -- `refvectors`: if `false` (default), the function will remove the RefVector values, otherwise it will keep them -- `no_value`: the value to replace `nothing` values. Default is `nothing`. Usually used to replace `nothing` values -by `missing` in DataFrames. - -# Examples - -```@example -using PlantSimEngine, MultiScaleTreeGraph, DataFrames, PlantSimEngine.Examples -``` - -Import example models (can be found in the `examples` folder of the package, or in the `Examples` sub-modules): - -```jldoctest mylabel -julia> using PlantSimEngine.Examples; -``` - -$MAPPING_EXAMPLE - -```@example -mtg = import_mtg_example(); -``` - -```@example -out = run!(mtg, mapping, meteo, tracked_outputs = Dict( - :Leaf => (:carbon_assimilation, :carbon_demand, :soil_water_content, :carbon_allocation), - :Internode => (:carbon_allocation,), - :Plant => (:carbon_allocation,), - :Soil => (:soil_water_content,), -)); -``` - -```@example -convert_outputs(out, DataFrames) -``` -""" -# Another, possibly better way would be to just create the DataFrame directly from the outputs -# and then remove the RefVector columns and replace the node one, hmm -function convert_outputs(outs::Dict{Symbol,O} where O, sink; refvectors=false, no_value=nothing) - ret = Dict{Symbol,sink}() - for (organ, status_vector) in outs - # remove RefVector variables - refv = () - if length(status_vector) > 0 - for (var, val) in pairs(status_vector[1]) - if !refvectors && isa(val, RefVector) - refv = (refv..., var) - end - if var == :node - refv = (refv..., var) - end - end - else - @warn "No instance found at the $organ scale, no output available, removing it from the Dict" - continue - end - - # Get the new NamedTuple type - refv_nt = NamedTuple{refv} - - # Piddle around with the first element to get the final type to be able to allocate the exact vector size with a definite element type - vector_named_tuple_1 = NamedTuple(status_vector[1]) - - # replace the MTG node var with the id (MTG nodes aren't CSV-friendly) - filtered_named_tuple = (; node=MultiScaleTreeGraph.node_id(vector_named_tuple_1.node), Base.structdiff(vector_named_tuple_1, refv_nt)...) - filtered_vector_named_tuple = Vector{typeof(filtered_named_tuple)}(undef, length(status_vector)) - - for i in 1:length(status_vector) - vector_named_tuple_i = NamedTuple(status_vector[i]) - filtered_vector_named_tuple[i] = (; node=MultiScaleTreeGraph.node_id(vector_named_tuple_i.node), Base.structdiff(vector_named_tuple_i, refv_nt)...) - end - - ret[organ] = sink(filtered_vector_named_tuple) - end - return ret -end - -# Single-scale mappings return outputs as a TimeStepTable{Status}, conversion is straightforward -function convert_outputs(out::TimeStepTable{T} where T, sink) - if !Tables.istable(sink) - error("The sink argument must be compatible with the Tables.jl interface (`Tables.istable(sink)` must return `true`, *e.g.* `DataFrame`)") - end - return sink(out) -end diff --git a/src/mtg/ModelSpec.jl b/src/mtg/ModelSpec.jl deleted file mode 100644 index d89dd8bed..000000000 --- a/src/mtg/ModelSpec.jl +++ /dev/null @@ -1,684 +0,0 @@ -""" - ModelSpec(model; name=nothing, applies_to=nothing, inputs=NamedTuple(), calls=NamedTuple(), environment=nothing, multiscale=nothing, timestep=nothing, input_bindings=NamedTuple(), meteo_bindings=NamedTuple(), meteo_window=nothing, output_routing=NamedTuple(), scope=:global, updates=()) - -User-side model configuration wrapper for mapping/model list composition. - -`ModelSpec` keeps model implementation and scenario-specific usage metadata in one place. -This allows modelers to publish reusable models while users decide how models are coupled in -their simulation setup. -""" -struct ModelSpec{M,N,AT,IN,IO,CA,CO,EV,MS,TS,IB,MB,MW,OR,SC,UP} - model::M - name::N - applies_to::AT - inputs::IN - input_origins::IO - calls::CA - call_origins::CO - environment::EV - multiscale::MS - timestep::TS - input_bindings::IB - meteo_bindings::MB - meteo_window::MW - output_routing::OR - scope::SC - updates::UP -end - -""" - Updates(vars...; after=nothing) - -Scenario-level declaration that a model updates variables which may also be -computed by another model at the same scale. - -`after` is intentionally mapping-level metadata: the model implementation stays -reusable, while the simulation setup can declare ordering constraints that only -exist in this coupling. -""" -struct Updates{V,A} - variables::V - after::A -end - -function Updates(vars::Vararg{Union{Symbol,AbstractString}}; after=nothing) - isempty(vars) && error("Updates(...) requires at least one variable.") - return Updates(Tuple(Symbol(v) for v in vars), _normalize_update_after(after)) -end - -function _normalize_update_after(after) - isnothing(after) && return () - after isa Union{Symbol,AbstractString} && return (Symbol(after),) - return Tuple(Symbol(v) for v in after) -end - -function _normalize_multiscale_mapping(model::AbstractModel, mapped_variables) - mapped_variables === nothing && return nothing - mapped = MultiScaleModel(model, mapped_variables) - return mapped_variables_(mapped) -end - -_normalize_updates(updates::Updates) = (updates,) - -function _normalize_updates(updates::Tuple) - all(update -> update isa Updates, updates) || error( - "Unsupported updates tuple. Use `Updates(:var; after=:process)` entries." - ) - return updates -end - -function _normalize_updates(updates::AbstractVector) - all(update -> update isa Updates, updates) || error( - "Unsupported updates vector. Use `Updates(:var; after=:process)` entries." - ) - return Tuple(updates) -end - -function _normalize_updates(updates) - updates == NamedTuple() && return () - updates == () && return () - error( - "Unsupported updates metadata `$(updates)` of type `$(typeof(updates))`. ", - "Use `Updates(:var; after=:process)` or a tuple/vector of `Updates`." - ) -end - -function ModelSpec( - model::AbstractModel; - name=nothing, - applies_to=nothing, - inputs=NamedTuple(), - input_origins=nothing, - calls=NamedTuple(), - call_origins=nothing, - environment=nothing, - multiscale=nothing, - timestep=nothing, - input_bindings=NamedTuple(), - meteo_bindings=NamedTuple(), - meteo_window=nothing, - output_routing=NamedTuple(), - scope=:global, - updates=() -) - base_model = model - - normalized_name = _normalize_application_name(name) - default_inputs = _model_default_value_inputs(base_model) - explicit_inputs = _normalize_application_bindings(inputs) - normalized_inputs = _merge_value_inputs(default_inputs, explicit_inputs) - normalized_input_origins = isnothing(input_origins) ? - _binding_origins(default_inputs, explicit_inputs) : - _normalize_binding_origins(input_origins, normalized_inputs) - default_calls = _model_default_model_calls(base_model) - explicit_calls = _normalize_application_bindings(calls) - normalized_calls = _merge_value_inputs(default_calls, explicit_calls) - normalized_call_origins = isnothing(call_origins) ? - _binding_origins(default_calls, explicit_calls) : - _normalize_binding_origins(call_origins, normalized_calls) - derived_multiscale = _legacy_multiscale_from_value_inputs(normalized_inputs, base_model) - combined_multiscale = _merge_legacy_multiscale(multiscale, derived_multiscale) - normalized_multiscale = _normalize_multiscale_mapping(base_model, combined_multiscale) - normalized_input_bindings = _normalize_input_bindings(input_bindings) - normalized_meteo_bindings = _normalize_meteo_bindings(meteo_bindings) - normalized_meteo_window = _normalize_meteo_window(meteo_window) - normalized_output_routing = _normalize_output_routing(output_routing) - normalized_scope = _normalize_scope_selector(scope) - normalized_updates = _normalize_updates(updates) - return ModelSpec{typeof(base_model),typeof(normalized_name),typeof(applies_to),typeof(normalized_inputs),typeof(normalized_input_origins),typeof(normalized_calls),typeof(normalized_call_origins),typeof(environment),typeof(normalized_multiscale),typeof(timestep),typeof(normalized_input_bindings),typeof(normalized_meteo_bindings),typeof(normalized_meteo_window),typeof(normalized_output_routing),typeof(normalized_scope),typeof(normalized_updates)}( - base_model, - normalized_name, - applies_to, - normalized_inputs, - normalized_input_origins, - normalized_calls, - normalized_call_origins, - environment, - normalized_multiscale, - timestep, - normalized_input_bindings, - normalized_meteo_bindings, - normalized_meteo_window, - normalized_output_routing, - normalized_scope, - normalized_updates - ) -end - -function ModelSpec( - model::MultiScaleModel; - name=nothing, - applies_to=nothing, - inputs=NamedTuple(), - input_origins=nothing, - calls=NamedTuple(), - call_origins=nothing, - environment=nothing, - multiscale=nothing, - timestep=nothing, - input_bindings=NamedTuple(), - meteo_bindings=NamedTuple(), - meteo_window=nothing, - output_routing=NamedTuple(), - scope=:global, - updates=() -) - base_multiscale = isnothing(multiscale) ? mapped_variables_(model) : multiscale - return ModelSpec( - model_(model); - name=name, - applies_to=applies_to, - inputs=inputs, - input_origins=input_origins, - calls=calls, - call_origins=call_origins, - environment=environment, - multiscale=base_multiscale, - timestep=timestep, - input_bindings=input_bindings, - meteo_bindings=meteo_bindings, - meteo_window=meteo_window, - output_routing=output_routing, - scope=scope, - updates=updates - ) -end - -function ModelSpec( - spec::ModelSpec; - model=spec.model, - name=spec.name, - applies_to=spec.applies_to, - inputs=spec.inputs, - input_origins=spec.input_origins, - calls=spec.calls, - call_origins=spec.call_origins, - environment=spec.environment, - multiscale=spec.multiscale, - timestep=spec.timestep, - input_bindings=spec.input_bindings, - meteo_bindings=spec.meteo_bindings, - meteo_window=spec.meteo_window, - output_routing=spec.output_routing, - scope=spec.scope, - updates=spec.updates -) - ModelSpec(model; name=name, applies_to=applies_to, inputs=inputs, input_origins=input_origins, calls=calls, call_origins=call_origins, environment=environment, multiscale=multiscale, timestep=timestep, input_bindings=input_bindings, meteo_bindings=meteo_bindings, meteo_window=meteo_window, output_routing=output_routing, scope=scope, updates=updates) -end - -as_model_spec(spec::ModelSpec) = spec -as_model_spec(model::AbstractModel) = ModelSpec(model) -as_model_spec(model::MultiScaleModel) = ModelSpec(model_(model); multiscale=mapped_variables_(model)) - -""" - with_name(model_or_spec, name) - -Return a `ModelSpec` with an explicit model-application name. -""" -function with_name(model_or_spec, name) - spec = as_model_spec(model_or_spec) - return ModelSpec(spec; name=_normalize_application_name(name)) -end - -""" - with_applies_to(model_or_spec, selector) - -Return a `ModelSpec` with an explicit model-application target selector. -""" -function with_applies_to(model_or_spec, selector) - spec = as_model_spec(model_or_spec) - return ModelSpec(spec; applies_to=selector) -end - -""" - with_inputs(model_or_spec, bindings) - -Return a `ModelSpec` with unified scene/object value-input bindings. -""" -function with_inputs(model_or_spec, bindings) - spec = as_model_spec(model_or_spec) - explicit = _normalize_application_bindings(bindings) - origins = _binding_origins(_model_default_value_inputs(model_(spec)), explicit) - return ModelSpec(spec; inputs=explicit, input_origins=origins) -end - -""" - with_calls(model_or_spec, bindings) - -Return a `ModelSpec` with unified scene/object manual model-call bindings. -""" -function with_calls(model_or_spec, bindings) - spec = as_model_spec(model_or_spec) - explicit = _normalize_application_bindings(bindings) - origins = _binding_origins(_model_default_model_calls(model_(spec)), explicit) - return ModelSpec(spec; calls=explicit, call_origins=origins) -end - -""" - with_environment(model_or_spec, environment) - -Return a `ModelSpec` with scene/object environment configuration metadata. -""" -function with_environment(model_or_spec, environment) - spec = as_model_spec(model_or_spec) - return ModelSpec(spec; environment=environment) -end - -""" - with_multiscale(model_or_spec, mapped_variables) - -Return a `ModelSpec` with updated multiscale mapping. -""" -function with_multiscale(model_or_spec, mapped_variables) - spec = as_model_spec(model_or_spec) - return ModelSpec(spec; multiscale=mapped_variables) -end - -""" - with_timestep(model_or_spec, timestep) - -Return a `ModelSpec` with an explicit user-selected timestep. -""" -function with_timestep(model_or_spec, timestep) - spec = as_model_spec(model_or_spec) - return ModelSpec(spec; timestep=timestep) -end - -""" - with_input_bindings(model_or_spec, bindings) - -Return a `ModelSpec` with explicit user-defined input-to-producer bindings. -""" -function with_input_bindings(model_or_spec, bindings) - spec = as_model_spec(model_or_spec) - return ModelSpec(spec; input_bindings=_normalize_input_bindings(bindings)) -end - -""" - with_meteo_bindings(model_or_spec, bindings) - -Return a `ModelSpec` with explicit meteo aggregation bindings. -""" -function with_meteo_bindings(model_or_spec, bindings) - spec = as_model_spec(model_or_spec) - return ModelSpec(spec; meteo_bindings=_normalize_meteo_bindings(bindings)) -end - -""" - with_meteo_window(model_or_spec, window) - -Return a `ModelSpec` with explicit weather-window selection strategy. -""" -function with_meteo_window(model_or_spec, window) - spec = as_model_spec(model_or_spec) - return ModelSpec(spec; meteo_window=_normalize_meteo_window(window)) -end - -""" - with_output_routing(model_or_spec, routing) - -Return a `ModelSpec` with explicit user-defined output routing. -""" -function with_output_routing(model_or_spec, routing) - spec = as_model_spec(model_or_spec) - return ModelSpec(spec; output_routing=_normalize_output_routing(routing)) -end - -""" - with_scope(model_or_spec, scope) - -Return a `ModelSpec` with explicit scope selection for multi-rate stream keys. -""" -function with_scope(model_or_spec, scope) - spec = as_model_spec(model_or_spec) - return ModelSpec(spec; scope=_normalize_scope_selector(scope)) -end - -""" - with_updates(model_or_spec, updates) - -Return a `ModelSpec` with explicit variable-update metadata. -""" -function with_updates(model_or_spec, updates) - spec = as_model_spec(model_or_spec) - return ModelSpec(spec; updates=(spec.updates..., _normalize_updates(updates)...)) -end - -(updates::Updates)(model_or_spec) = with_updates(model_or_spec, updates) - -function _normalize_input_binding(binding) - if binding isa NamedTuple - return haskey(binding, :policy) ? binding : (; binding..., policy=HoldLast()) - elseif binding isa Pair{Symbol,Symbol} - return (process=first(binding), var=last(binding), policy=HoldLast()) - elseif binding isa Symbol - return (process=binding, policy=HoldLast()) - end - return binding -end - -function _normalize_input_bindings(bindings::NamedTuple) - normalized = Pair{Symbol,Any}[] - for (k, v) in pairs(bindings) - push!(normalized, k => _normalize_input_binding(v)) - end - return (; normalized...) -end - -function _normalize_input_bindings(bindings) - error("Unsupported InputBindings value `$(bindings)` of type `$(typeof(bindings))`. Use a NamedTuple, e.g. `InputBindings(; x=(process=:producer, var=:y))`.") -end - -function _normalize_meteo_binding(binding) - if binding isa DataType - binding <: PlantMeteo.AbstractTimeReducer || error( - "Unsupported MeteoBindings reducer type `$(binding)`. ", - "Use a PlantMeteo reducer type/instance, callable, or NamedTuple(source=..., reducer=...)." - ) - return binding - elseif binding isa PlantMeteo.AbstractTimeReducer - return binding - elseif binding isa Function - return binding - elseif binding isa NamedTuple - return binding - end - error( - "Unsupported MeteoBindings value `$(binding)` of type `$(typeof(binding))`. ", - "Use a PlantMeteo reducer type/instance, callable, or NamedTuple(source=..., reducer=...)." - ) -end - -function _normalize_meteo_bindings(bindings::NamedTuple) - normalized = Pair{Symbol,Any}[] - for (k, v) in pairs(bindings) - push!(normalized, k => _normalize_meteo_binding(v)) - end - return (; normalized...) -end - -function _normalize_meteo_bindings(bindings) - error("Unsupported MeteoBindings value `$(bindings)` of type `$(typeof(bindings))`. Use a NamedTuple, e.g. `MeteoBindings(; T=MeanReducer())`.") -end - -function _normalize_meteo_window(window) - if isnothing(window) - return nothing - elseif window isa DataType - window <: PlantMeteo.AbstractSamplingWindow || error( - "Unsupported MeteoWindow type `$(window)`. ", - "Use a PlantMeteo sampling-window type/instance." - ) - return window() - elseif window isa PlantMeteo.AbstractSamplingWindow - return window - end - - error( - "Unsupported MeteoWindow value `$(window)` of type `$(typeof(window))`. ", - "Use a PlantMeteo sampling-window type/instance." - ) -end - -function _normalize_output_routing(routing::NamedTuple) - normalized = Pair{Symbol,Symbol}[] - for (k, v) in pairs(routing) - mode = Symbol(v) - mode in (:canonical, :stream_only) || error( - "Unsupported output routing mode `$(mode)` for output `$(k)`. ", - "Allowed values are `:canonical` and `:stream_only`." - ) - push!(normalized, k => mode) - end - return (; normalized...) -end - -function _normalize_output_routing(routing) - error("Unsupported OutputRouting value `$(routing)` of type `$(typeof(routing))`. Use a NamedTuple, e.g. `OutputRouting(; x=:stream_only)`.") -end - -function _normalize_scope_selector(scope) - if scope isa AbstractString - error("String scope selectors are not supported. Use symbols such as `:global`, `:plant`, `:scene`, or `:self`.") - end - return scope -end - -""" - AppliesTo(selector) - -Pipe-style transform that sets the object selector where a model application -runs in the unified scene/object API. -""" -AppliesTo(selector) = x -> with_applies_to(x, selector) - -""" - Inputs(bindings...) - Inputs(; kwargs...) - -Pipe-style transform that sets value-input bindings in the unified -scene/object API. -""" -Inputs(bindings::Pair...) = x -> with_inputs(x, bindings) -Inputs(bindings::NamedTuple) = x -> with_inputs(x, bindings) -Inputs(; kwargs...) = Inputs((; kwargs...)) - -""" - Calls(bindings...) - Calls(; kwargs...) - -Pipe-style transform that sets manual model-call bindings in the unified -scene/object API. -""" -Calls(bindings::Pair...) = x -> with_calls(x, bindings) -Calls(bindings::NamedTuple) = x -> with_calls(x, bindings) -Calls(; kwargs...) = Calls((; kwargs...)) - -""" - TimeStep(timestep) - -Pipe-style transform that sets a user-selected timestep in the unified -scene/object API. This is the breaking-design name for `TimeStepModel(...)`. -""" -TimeStep(timestep) = x -> with_timestep(x, timestep) - -""" - Environment(config) - Environment(; kwargs...) - -Pipe-style transform that stores scene/object environment configuration -metadata on a `ModelSpec`. -""" -Environment(config) = x -> with_environment(x, config isa EnvironmentConfig ? config : EnvironmentConfig(config)) -Environment(; kwargs...) = Environment((; kwargs...)) - -""" - PlantSimEngine.MultiScaleModel(mapped_variables) - -Pipe-style transform that updates multiscale mapping on a model/spec. -""" -MultiScaleModel(mapped_variables) = x -> with_multiscale(x, mapped_variables) - -""" - PlantSimEngine.TimeStepModel(timestep) - -Pipe-style transform that sets a user-selected timestep on a model/spec. -""" -TimeStepModel(timestep) = x -> with_timestep(x, timestep) - -""" - PlantSimEngine.InputBindings(bindings) - PlantSimEngine.InputBindings(; kwargs...) - -Pipe-style transform that sets explicit producer bindings for model inputs. - -This is used in multi-rate mappings to tell runtime where each input should be -read from (process, optional source variable, optional source scale, and policy). - -# Arguments -- `bindings::NamedTuple`: maps each consumer input variable (`Symbol`) to a - binding descriptor. -- `kwargs...`: keyword shorthand equivalent to a `NamedTuple`. - -Each binding descriptor can be: -- `Symbol`: producer process (`policy=HoldLast()` and source variable inferred). -- `Pair{Symbol,Symbol}`: `producer_process => source_var` - (`policy=HoldLast()`). -- `NamedTuple`: explicit fields: - - `process` (`Symbol`/`String`, optional if uniquely inferable), - - `var` (`Symbol`, optional, defaults to same-name input when inferable), - - `scale` (`String`/`Symbol`, optional, useful for cross-scale disambiguation), - - `policy` (`SchedulePolicy` instance/type, optional, default `HoldLast()`). - -When omitted fields cannot be inferred uniquely, runtime errors and asks for an -explicit `PlantSimEngine.InputBindings(...)`. - -# Example -```julia -ModelSpec(ConsumerModel()) |> -PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)) |> -PlantSimEngine.InputBindings(; A=(process=:assim, var=:carbon_assimilation, scale=:Leaf, policy=Integrate())) -``` -""" -InputBindings(bindings) = x -> with_input_bindings(x, bindings) -InputBindings(; kwargs...) = InputBindings((; kwargs...)) - -""" - PlantSimEngine.MeteoBindings(bindings) - PlantSimEngine.MeteoBindings(; kwargs...) - -Pipe-style transform that sets weather-variable aggregation rules per model. - -Each key is the target weather variable name as seen by the model (for example -`:T`, `:Rh`, `:Ri_SW_q`). - -# Arguments -- `bindings::NamedTuple`: per-target meteo binding rules. -- `kwargs...`: keyword shorthand equivalent to a `NamedTuple`. - -Each rule value can be: -- a `PlantMeteo.AbstractTimeReducer` instance/type - (for example `MeanWeighted()`, `MaxReducer`, `RadiationEnergy()`), -- a callable reducer (`Function`) receiving sampled values, -- a `NamedTuple` with: - - `source` (`Symbol`/`String`, optional, defaults to target key), - - `reducer` (reducer type/instance/callable, optional, defaults to - `MeanWeighted()`). - -# Example -```julia -ModelSpec(DailyModel()) |> -PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)) |> -PlantSimEngine.MeteoBindings( - ; - T=MeanWeighted(), - Rh=MeanWeighted(), - Ri_SW_q=(source=:Ri_SW_f, reducer=RadiationEnergy()), -) -``` -""" -MeteoBindings(bindings) = x -> with_meteo_bindings(x, bindings) -MeteoBindings(; kwargs...) = MeteoBindings((; kwargs...)) - -""" - PlantSimEngine.MeteoWindow(window) - -Pipe-style transform that sets the weather row-selection window for one model. - -This controls which meteo rows are sampled before -`PlantSimEngine.MeteoBindings` reducers are applied. - -# Arguments -- `window`: a `PlantMeteo.AbstractSamplingWindow` instance/type. - Typical values are: - - `PlantMeteo.RollingWindow()` (default trailing window), - - `PlantMeteo.CalendarWindow(...)` (calendar-aligned day/week/month windows). - -# Example -```julia -ModelSpec(DailyModel()) |> -PlantSimEngine.TimeStepModel(ClockSpec(24.0, 0.0)) |> -PlantSimEngine.MeteoWindow(CalendarWindow(:day; anchor=:current_period, week_start=1, completeness=:strict)) -``` -""" -MeteoWindow(window) = x -> with_meteo_window(x, window) - -""" - OutputRouting(routing) - OutputRouting(; kwargs...) - -Pipe-style transform that sets output publication mode for a model. - -This is mainly used to disambiguate publishers in multi-rate runs when several -models write variables with the same name. - -# Arguments -- `routing::NamedTuple`: maps output variable symbols to routing mode. -- `kwargs...`: keyword shorthand equivalent to a `NamedTuple`. - -Allowed routing values: -- `:canonical` (default): output is considered canonical at that scale and can - be auto-selected as source/export publisher. -- `:stream_only`: output is kept only in temporal streams and excluded from - canonical publisher resolution. - -# Example -```julia -ModelSpec(AltSourceModel()) |> -OutputRouting(; C=:stream_only) -``` -""" -OutputRouting(routing) = x -> with_output_routing(x, routing) -OutputRouting(; kwargs...) = OutputRouting((; kwargs...)) - -""" - PlantSimEngine.ScopeModel(scope) - -Pipe-style transform that sets stream scope selection for a model. - -Scope controls how temporal streams are partitioned/resolved across entities in -multi-rate simulations. - -# Arguments -- `scope`: one of: - - selector symbols: `:global`, `:plant`, `:scene`, `:self`, - - a concrete `ScopeId`, - - a callable returning a scope selector symbol or `ScopeId` at runtime. - -# Example -```julia -ModelSpec(LeafSourceModel()) |> -PlantSimEngine.ScopeModel(:plant) -``` -""" -ScopeModel(scope) = x -> with_scope(x, scope) - -model_(m::ModelSpec) = m.model -mapped_variables_(m::ModelSpec) = isnothing(m.multiscale) ? Pair{Symbol,String}[] : m.multiscale -get_models(m::ModelSpec) = [model_(m)] -get_status(m::ModelSpec) = nothing -get_mapped_variables(m::ModelSpec) = mapped_variables_(m) -process(m::ModelSpec) = process(model_(m)) -timestep(m::ModelSpec) = m.timestep -inputs_(m::ModelSpec) = inputs_(model_(m)) -outputs_(m::ModelSpec) = outputs_(model_(m)) - -function _normalize_model_spec_dependencies(deps::NamedTuple) - normalized = Pair{Symbol,Any}[] - for (dep_name, selector) in pairs(deps) - selector isa Union{Input,Call} && continue - push!(normalized, dep_name => selector) - end - return (; normalized...) -end - -function dep(m::ModelSpec) - return _normalize_model_spec_dependencies(dep(model_(m))) -end -init_variables(m::ModelSpec; verbose::Bool=true) = init_variables(model_(m); verbose=verbose) -meteo_inputs_(m::ModelSpec) = meteo_inputs_(model_(m)) -meteo_outputs_(m::ModelSpec) = meteo_outputs_(model_(m)) - -function run!(m::ModelSpec, models, status, meteo, constants=nothing, extra=nothing) - return run!(model_(m), models, status, meteo, constants, extra) -end diff --git a/src/mtg/MultiScaleModel.jl b/src/mtg/MultiScaleModel.jl deleted file mode 100644 index 7b6d1b66b..000000000 --- a/src/mtg/MultiScaleModel.jl +++ /dev/null @@ -1,201 +0,0 @@ -""" - MultiScaleModel(model, mapped_variables) - -A structure to make a model multi-scale. It defines a mapping between the variables of a -model and the nodes symbols from which the values are taken from. - -# Arguments - -- `model<:AbstractModel`: the model to make multi-scale -- `mapped_variables<:Vector{Pair{Symbol,Union{Symbol,Vector{Symbol}}}}`: - a vector of pairs of model variables and source scale declarations. - -The mapped_variables argument can be of the form: - -1. `[:variable_name => (:Plant => :variable_name)]`: We take one value from the Plant node -2. `[:variable_name => [:Leaf]]`: We take a vector of values from the Leaf nodes -3. `[:variable_name => [:Leaf, :Internode]]`: We take a vector of values from the Leaf and Internode nodes -4. `[:variable_name => (:Plant => :variable_name_in_plant_scale)]`: We take one value from another variable name in the Plant node -5. `[:variable_name => [:Leaf => :variable_name_1, :Internode => :variable_name_2]]`: We take a vector of values from the Leaf and Internode nodes with different names -6. `[PreviousTimeStep(:variable_name) => ...]`: We flag the variable to be initialized with the value from the previous time step, and we do not use it to build the dep graph -7. `[:variable_name => (SameScale() => :variable_name_from_another_model)]`: We take the value from another model at the same scale but rename it -8. `[PreviousTimeStep(:variable_name),]`: We just flag the variable as a PreviousTimeStep to not use it to build the dep graph - -Details about the different forms: - -1. The variable `variable_name` of the model will be taken from the `Plant` node, assuming only one node has the `Plant` symbol. -In this case the value available from the status will be a scalar, and so the user must guaranty that only one node of type `Plant` is available in the MTG. - -2. The variable `variable_name` of the model will be taken from the `Leaf` nodes. Notice it is given as a vector, indicating that the values will be taken -from all the nodes of type `Leaf`. The model should be able to handle a vector of values. Note that even if there is only one node of type `Leaf`, the value -will be taken as a vector of one element. - -3. The variable `variable_name` of the model will be taken from the `Leaf` and `Internode` nodes. The values will be taken from all the nodes of type `Leaf` -and `Internode`. - -4. The variable `variable_name` of the model will be taken from the variable called `variable_name_in_plant_scale` in the `Plant` node. This is useful -when the variable name in the model is different from the variable name in the scale it is taken from. - -5. The variable `variable_name` of the model will be taken from the variable called `variable_name_1` in the `Leaf` node and `variable_name_2` in the `Internode` node. - -6. The variable `variable_name` of the model uses the value computed on the previous time-step. This implies that the variable is not used to build the dependency graph -because the dependency graph only applies on the current time-step. This is used to avoid circular dependencies when a variable depends on itself. The value can be initialized -in the Status if needed. - -7. The variable `variable_name` of the model will be taken from another model at the same scale, but with another variable name. - -8. The variable `variable_name` of the model is just flagged as a PreviousTimeStep variable, so it is not used to build the dependency graph. - - - -Note that the mapping does not make any copy of the values, it only references them. This means that if the values are updated in the status -of one node, they will be updated in the other nodes. - -# Examples - -```jldoctest mylabel -julia> using PlantSimEngine; -``` - -Including example processes and models: - -```jldoctest mylabel -julia> using PlantSimEngine.Examples; -``` - -Let's take a model: - -```jldoctest mylabel -julia> model = ToyCAllocationModel() -ToyCAllocationModel() -``` - -We can make it multi-scale by defining a mapping between the variables of the model and the nodes symbols from which the values are taken from: - -For example, if the `carbon_allocation` comes from the `Leaf` and `Internode` nodes, we can define the mapping as follows: - -```jldoctest mylabel -julia> mapped_variables = [:carbon_allocation => [:Leaf, :Internode]] -1-element Vector{Pair{Symbol, Vector{Symbol}}}: - :carbon_allocation => [:Leaf, :Internode] -``` - -The mapped_variables argument is a vector of pairs of symbols and symbol scales. In this case, we have only one pair to define the mapping -between the `carbon_allocation` variable and the `Leaf` and `Internode` nodes. - -We can now make the model multi-scale by passing the model and the mapped variables to the `MultiScaleModel` constructor : - -```jldoctest mylabel -julia> multiscale_model = PlantSimEngine.MultiScaleModel(model, mapped_variables); -``` - -We can access the mapped variables and the model: - -```jldoctest mylabel -julia> PlantSimEngine.mapped_variables_(multiscale_model) == [:carbon_allocation => [:Leaf => :carbon_allocation, :Internode => :carbon_allocation]] -true -``` - -```jldoctest mylabel -julia> PlantSimEngine.model_(multiscale_model) -ToyCAllocationModel() -``` -""" -struct MultiScaleModel{T<:AbstractModel,V<:AbstractVector} - model::T - mapped_variables::V - - function MultiScaleModel{T}(model::T, mapped_variables) where {T<:AbstractModel} - # Check that the variables in the mapping are variables of the model: - model_variables = keys(variables(model)) - for i in mapped_variables - # If the var is a PreviousTimeStep, we take the variable name, else take the first element of the pair: - var = isa(i, PreviousTimeStep) ? i.variable : first(i) - - # If it was a pair, the first element can still be a PreviousTimeStep, so we take the variable name: - var = isa(var, PreviousTimeStep) ? var.variable : var - - if !(var in model_variables) - error("Mapping for model $model defines variable $var, but it is not a variable of the model.") - end - end - - # If the name of the variable mapped from the other scale is not given, we add it as the same of the variable name in the model. Cases: - # 1. `[:variable_name => (:Plant => :variable_name)]` # We take one value from the Plant node - # 2. `[:variable_name => [:Leaf]]` # We take a vector of values from the Leaf nodes - # 3. `[:variable_name => [:Leaf, :Internode]]` # We take a vector of values from the Leaf and Internode nodes - # 4. `[:variable_name => (:Plant => :variable_name_in_plant_scale)]` # We take one value from another variable name in the Plant node - # 5. `[:variable_name => [:Leaf => :variable_name_1, :Internode => :variable_name_2]]` # We take a vector of values from the Leaf and Internode nodes with different names - # 6. `[PreviousTimeStep(:variable_name) => ...]` # We flag the variable to be initialized with the value from the previous time step, and we do not use it to build the dep graph - # 7. `[:variable_name => (SameScale() => :variable_name_from_another_model)]` # We take the value from another model at the same scale but rename it - # 8. `[PreviousTimeStep(:variable_name),]` # We just flag the variable as a PreviousTimeStep to not use it to build the dep graph - - process_ = process(model) - unfolded_mapping = Pair{Union{Symbol,PreviousTimeStep},Any}[] - for i in mapped_variables - push!(unfolded_mapping, _get_var(isa(i, PreviousTimeStep) ? i : Pair(i.first, i.second), process_)) - # Note: We are using Pair(i.first, i.second) to make sure the Pair is specialized enough, because sometimes the vector in the mapping made the Pair not specialized enough e.g. [:v1 => :S => :v2,:v3 => :S] makes the pairs `Pair{Symbol, Any}`. - end - - new{T,typeof(unfolded_mapping)}(model, unfolded_mapping) - end -end - -# Method used when the vector in the mapping made the Pair not specialized enough e.g. [:v1 => :S => :v2,:v3 => :S] makes the pairs `Pair{Symbol, Any}`. -function _get_var(i::Pair{Symbol,Any}, proc::Symbol=:unknown) - return _get_var(first(i) => last(i), proc) -end - -function _normalize_mapped_scale(scale::Symbol) - scale === Symbol("") && error("`Symbol(\"\")` same-scale mappings are removed. Use `SameScale()` instead.") - return scale -end -_normalize_mapped_scale(scale::SameScale) = scale -function _normalize_mapped_scale(scale) - error("Mapped scale names must be `Symbol`s, got `$(typeof(scale))` for `$(repr(scale))`.") -end - -function _get_var(i::Pair{Symbol,Pair{T,Symbol}}, proc::Symbol=:unknown) where {T<:Union{Symbol,SameScale}} - return first(i) => (_normalize_mapped_scale(first(last(i))) => last(last(i))) -end - -function _get_var(i::Pair{Symbol,T}, proc::Symbol=:unknown) where {T<:AbstractVector{<:Symbol}} - return first(i) => [_normalize_mapped_scale(scale) => first(i) for scale in last(i)] -end - -# Case 5: [:variable_name => [:Leaf => :variable_name_1, :Internode => :variable_name_2]] -function _get_var(i::Pair{Symbol,T}, proc::Symbol=:unknown) where {T<:AbstractVector{<:Pair{Symbol,Symbol}}} - return first(i) => [_normalize_mapped_scale(first(mapping)) => last(mapping) for mapping in last(i)] -end - -# Case 6: [PreviousTimeStep(:variable_name) => ...] -function _get_var(i::Pair{PreviousTimeStep,T}, proc::Symbol=:unknown) where {T} - vars = _get_var(i.first.variable => i.second, proc) # Leverage the other methods here - return PreviousTimeStep(first(i).variable, proc) => last(vars) # And put back the PreviousTimeStep as the first element -end - -# Case 7: [:variable_name => :Plant] -function _get_var(i::Pair{Symbol,Symbol}, proc::Symbol=:unknown) - return first(i) => last(i) => first(i) -end - -# Case 8: [PreviousTimeStep(:variable_name),] -function _get_var(i::PreviousTimeStep, proc::Symbol=:unknown) - return PreviousTimeStep(i.variable, proc) => SameScale() => i.variable -end - - - -function MultiScaleModel(model::T, mapped_variables) where {T<:AbstractModel} - MultiScaleModel{T}(model, mapped_variables) -end -MultiScaleModel(; model, mapped_variables) = MultiScaleModel(model, mapped_variables) - -mapped_variables_(m::MultiScaleModel) = m.mapped_variables -model_(m::MultiScaleModel) = m.model -inputs_(m::MultiScaleModel) = inputs_(m.model) -outputs_(m::MultiScaleModel) = outputs_(m.model) -get_models(m::MultiScaleModel) = [model_(m)] # Get the models of a MultiScaleModel: -# Note: it is returning a vector of models, because in this case the user provided a single MultiScaleModel instead of a vector of. -get_status(m::MultiScaleModel) = nothing -get_mapped_variables(m::MultiScaleModel{T,S}) where {T,S} = mapped_variables_(m) diff --git a/src/mtg/add_organ.jl b/src/mtg/add_organ.jl deleted file mode 100644 index 6d448b2b2..000000000 --- a/src/mtg/add_organ.jl +++ /dev/null @@ -1,230 +0,0 @@ -""" - add_organ!(node::MultiScaleTreeGraph.Node, sim_object, link, symbol, scale; index=0, id=MultiScaleTreeGraph.new_id(MultiScaleTreeGraph.get_root(node)), attributes=Dict{Symbol,Any}(), check=true) - -Add an organ to the graph, automatically taking care of initialising the status of the organ (multiscale-)variables. - -This function should be called from a model that implements organ emergence, for example in function of thermal time. - -# Arguments - -* `node`: the node to which the organ is added (the parent organ of the new organ) -* `sim_object`: the simulation object, e.g. the `GraphSimulation` object from the `extra` argument of a model. -* `link`: the link type between the new node and the organ: - * `"<"`: the new node is following the parent organ - * `"+"`: the new node is branching the parent organ - * `"/"`: the new node is decomposing the parent organ, *i.e.* we change scale -* `symbol`: the symbol of the organ, *e.g.* `:Leaf` -* `scale`: the scale of the organ, *e.g.* `2`. -* `index`: the index of the organ, *e.g.* `1`. The index may be used to easily identify branching order, or growth unit index on the axis. It is different from the node `id` that is unique. -* `id`: the unique id of the new node. If not provided, a new id is generated. -* `attributes`: the attributes of the new node. If not provided, an empty dictionary is used. -* `check`: a boolean indicating if variables initialisation should be checked. Passed to `init_node_status!`. - -# Returns - -* `status`: the status of the new node - -# Examples - -See the `ToyInternodeEmergence` example model from the `Examples` module (also found in the `examples` folder), -or the `test-mtg-dynamic.jl` test file for an example usage. -""" -function add_organ!(node::MultiScaleTreeGraph.Node, sim_object, link, symbol, scale; index=0, id=MultiScaleTreeGraph.new_id(MultiScaleTreeGraph.get_root(node)), attributes=Dict{Symbol,Any}(), check=true) - new_node = MultiScaleTreeGraph.Node(id, node, MultiScaleTreeGraph.NodeMTG(link, symbol, index, scale), attributes) - st = init_node_status!(new_node, sim_object.statuses, sim_object.status_templates, sim_object.reverse_multiscale_mapping, sim_object.var_need_init, check=check) - reindex_runtime_topology!(sim_object) - - return st -end - -function _delete_ref_from_refvector!(rv::RefVector, ref) - filter!(stored_ref -> stored_ref !== ref, parent(rv)) - return rv -end - -function _remove_status_from_scale!(statuses::Dict, scale::Symbol, st::Status, nid::Int) - haskey(statuses, scale) || return nothing - deleteat!(statuses[scale], findall(candidate -> candidate === st || node_id(candidate.node) == nid, statuses[scale])) - return nothing -end - -function _remove_reverse_refs_for_status!(sim_object, node_scale::Symbol, st::Status) - haskey(sim_object.reverse_multiscale_mapping, node_scale) || return nothing - for (target_scale, vars) in sim_object.reverse_multiscale_mapping[node_scale] - haskey(sim_object.status_templates, target_scale) || continue - target_template = sim_object.status_templates[target_scale] - for (source_var, target_var_) in vars - source_var in propertynames(st) || continue - target_var = target_var_ isa PreviousTimeStep ? target_var_.variable : target_var_ - haskey(target_template, target_var) || continue - target_value = target_template[target_var] - target_value isa RefVector || continue - _delete_ref_from_refvector!(target_value, refvalue(st, source_var)) - end - end - return nothing -end - -function _remove_temporal_state_for_node!(sim_object, nid::Int) - temporal = temporal_state(sim_object) - for key in collect(keys(temporal.caches)) - key.node_id == nid && delete!(temporal.caches, key) - end - for key in collect(keys(temporal.streams)) - key.node_id == nid && delete!(temporal.streams, key) - end - return nothing -end - -function _subtree_node_ids(node::MultiScaleTreeGraph.Node) - ids = Int[] - MultiScaleTreeGraph.traverse!(node) do subtree_node - push!(ids, node_id(subtree_node)) - end - return ids -end - -function _remove_temporal_state_for_nodes!(sim_object, node_ids) - for nid in node_ids - _remove_temporal_state_for_node!(sim_object, nid) - end - return nothing -end - -function _status_node_registered(sim_object, node::MultiScaleTreeGraph.Node) - node_scale = symbol(node) - haskey(sim_object.statuses, node_scale) || return false - nid = node_id(node) - return any(st -> hasproperty(st, :node) && node_id(st.node) == nid && st.node === node, sim_object.statuses[node_scale]) -end - -function _is_descendant_node(candidate::MultiScaleTreeGraph.Node, ancestor::MultiScaleTreeGraph.Node) - current = parent(candidate) - while !isnothing(current) - current === ancestor && return true - current = parent(current) - end - return false -end - -function _children_without_node(parent_node::MultiScaleTreeGraph.Node, node::MultiScaleTreeGraph.Node) - children_without_node = empty(AbstractTrees.children(parent_node)) - for child in AbstractTrees.children(parent_node) - child === node || push!(children_without_node, child) - end - return children_without_node -end - -function _repair_reparent_child_links!( - node::MultiScaleTreeGraph.Node, - old_parent, - new_parent::MultiScaleTreeGraph.Node, -) - if !isnothing(old_parent) && old_parent !== new_parent - MultiScaleTreeGraph.rechildren!(old_parent, _children_without_node(old_parent, node)) - end - - new_children = _children_without_node(new_parent, node) - push!(new_children, node) - MultiScaleTreeGraph.rechildren!(new_parent, new_children) - return nothing -end - -""" - remove_organ!(node::MultiScaleTreeGraph.Node, sim_object; attribute_name=:plantsimengine_status, recursive=false) - -Remove a simulated organ from an active [`GraphSimulation`](@ref). - -The wrapper updates PlantSimEngine runtime state before delegating to -`MultiScaleTreeGraph.delete_node!`: it removes the node status from -`sim_object.statuses`, removes references from downstream `RefVector`s, clears -temporal caches/streams for the removed node, and then deletes the MTG node. - -Only leaf/terminal nodes are removed by default. Pass `recursive=true` to delete -an internal node and its whole subtree. Reparenting children is intentionally not -handled here because it requires caller-specific biological and topological -policy. -""" -function remove_organ!(node::MultiScaleTreeGraph.Node, sim_object; attribute_name=:plantsimengine_status, recursive=false) - children = collect(AbstractTrees.children(node)) - if !recursive - isempty(children) || error( - "remove_organ!(...; recursive=false) only supports leaf/terminal MTG nodes. ", - "Pass `recursive=true` to delete node $(node_id(node)) and its descendants, ", - "or move descendants first." - ) - else - for child in children - remove_organ!(child, sim_object; attribute_name=attribute_name, recursive=true) - end - end - - haskey(node, attribute_name) || error( - "Cannot remove MTG node $(node_id(node)) ($(symbol(node))) from PlantSimEngine runtime: ", - "the node has no `$(attribute_name)` status." - ) - - st = node[attribute_name] - st isa Status || error( - "Cannot remove MTG node $(node_id(node)) ($(symbol(node))) from PlantSimEngine runtime: ", - "`$(attribute_name)` is not a Status." - ) - - nid = node_id(node) - node_scale = symbol(node) - _remove_reverse_refs_for_status!(sim_object, node_scale, st) - _remove_status_from_scale!(sim_object.statuses, node_scale, st, nid) - _remove_temporal_state_for_node!(sim_object, nid) - pop!(node, attribute_name) - deleted = MultiScaleTreeGraph.delete_node!(node) - reindex_runtime_topology!(sim_object) - return deleted -end - -""" - reparent_organ!(node::MultiScaleTreeGraph.Node, new_parent::MultiScaleTreeGraph.Node, sim_object; attribute_name=:plantsimengine_status) - -Move an already-simulated MTG node under another already-simulated parent in the -same active [`GraphSimulation`](@ref). - -The node status and downstream `RefVector`s keep pointing to the same node -object, so no status rewiring is needed when the subtree remains in the same -simulation. This wrapper validates that both nodes are registered in -PlantSimEngine runtime state and rejects moves that would create a cycle. -""" -function reparent_organ!( - node::MultiScaleTreeGraph.Node, - new_parent::MultiScaleTreeGraph.Node, - sim_object; - attribute_name=:plantsimengine_status, -) - node === new_parent && error("Cannot reparent MTG node $(node_id(node)) to itself.") - _is_descendant_node(new_parent, node) && error( - "Cannot reparent MTG node $(node_id(node)) under descendant node $(node_id(new_parent)); ", - "this would create a cycle." - ) - haskey(node, attribute_name) || error( - "Cannot reparent MTG node $(node_id(node)) ($(symbol(node))) in PlantSimEngine runtime: ", - "the node has no `$(attribute_name)` status." - ) - haskey(new_parent, attribute_name) || error( - "Cannot reparent MTG node $(node_id(node)) under node $(node_id(new_parent)) ($(symbol(new_parent))) ", - "in PlantSimEngine runtime: the new parent has no `$(attribute_name)` status." - ) - _status_node_registered(sim_object, node) || error( - "Cannot reparent MTG node $(node_id(node)) ($(symbol(node))): ", - "it is not registered in this GraphSimulation." - ) - _status_node_registered(sim_object, new_parent) || error( - "Cannot reparent MTG node $(node_id(node)) under node $(node_id(new_parent)) ($(symbol(new_parent))): ", - "the new parent is not registered in this GraphSimulation." - ) - - old_parent = parent(node) - moved_node_ids = _subtree_node_ids(node) - MultiScaleTreeGraph.reparent!(node, new_parent) - _repair_reparent_child_links!(node, old_parent, new_parent) - _remove_temporal_state_for_nodes!(sim_object, moved_node_ids) - reindex_runtime_topology!(sim_object) - return node -end diff --git a/src/mtg/initialisation.jl b/src/mtg/initialisation.jl deleted file mode 100644 index f15a6a523..000000000 --- a/src/mtg/initialisation.jl +++ /dev/null @@ -1,474 +0,0 @@ -""" - init_statuses(mtg, mapping, dependency_graph=dep(mapping); type_promotion=nothing, verbose=true, check=true) - -Get the status of each node in the MTG by node type, pre-initialised considering multi-scale variables. - -# Arguments - -- `mtg`: the plant graph -- `mapping`: a dictionary of model mapping -- `dependency_graph::DependencyGraph`: the first-order dependency graph where each model in the mapping is assigned a node. -However, models that are identified as hard-dependencies are not given individual nodes. Instead, they are nested as child -nodes under other models. -- `type_promotion`: the type promotion to use for the variables -- `verbose`: print information when compiling the mapping -- `check`: whether to check the mapping for errors. Passed to `init_node_status!`. - -# Return - -A NamedTuple of status by node type, a dictionary of status templates by node type, a dictionary of variables mapped to other scales, -a dictionary of variables that need to be initialised or computed by other models, and a vector of nodes that have a model defined for their symbol: - -`(;statuses, status_templates, reverse_multiscale_mapping, vars_need_init, nodes_with_models)` -""" -function init_statuses(mtg, mapping, dependency_graph; type_promotion=nothing, verbose=false, check=true) - # We compute the variables mapping for each scale: - mapped_vars = mapped_variables(mapping, dependency_graph, verbose=verbose) - - # Update the types of the variables as desired by the user: - convert_vars!(mapped_vars, type_promotion) - - # Compute the reverse multiscale dependencies, *i.e.* for each scale, which variable is mapped to the other scale - reverse_multiscale_mapping = reverse_mapping(mapped_vars, all=false) - # Note: this is used when we add a node value for such variable in the RefVector of the other scale. - # Note 2: we use the `all=false` option to only get the variables that are mapped to another scale as a vector. - # Note 3: we do it before `convert_reference_values!` because we need the variables to be MappedVar{MultiNodeMapping} to get the reverse mapping. - - # Convert the MappedVar{SelfNodeMapping} or MappedVar{SingleNodeMapping} to RefValues, and MappedVar{MultiNodeMapping} to RefVectors: - convert_reference_values!(mapped_vars) - - # Get the variables that are not initialised or computed by other models in the output: - vars_need_init = Dict(org => filter(x -> isa(last(x), UninitializedVar), vars) |> keys for (org, vars) in mapped_vars) |> - filter(x -> length(last(x)) > 0) - - # Note: these variables may be present in the MTG attributes, we check that below when traversing the MTG. - - # We traverse the MTG to initialise the statuses linked to the nodes: - statuses = Dict(i => Status[] for i in collect(keys(mapped_vars))) - MultiScaleTreeGraph.traverse!(mtg) do node # e.g.: node = MultiScaleTreeGraph.get_node(mtg, 5) - init_node_status!(node, statuses, mapped_vars, reverse_multiscale_mapping, vars_need_init, type_promotion, check=check) - end - reindex_runtime_topology!(statuses, mapped_vars, reverse_multiscale_mapping) - - return (; statuses, mapped_vars, reverse_multiscale_mapping, vars_need_init) -end - - -""" - init_node_status!( - node, - statuses, - mapped_vars, - reverse_multiscale_mapping, - vars_need_init=Dict{Symbol,Any}(), - type_promotion=nothing; - check=true, - attribute_name=:plantsimengine_status) - ) - -Initialise the status of a plant graph node, taking into account the multiscale mapping, and add it to the statuses dictionary. - -# Arguments - -- `node`: the node to initialise -- `statuses`: the dictionary of statuses by node type -- `mapped_vars`: the template of status for each node type -- `reverse_multiscale_mapping`: the variables that are mapped to other scales -- `var_need_init`: the variables that are not initialised or computed by other models -- `nodes_with_models`: the nodes that have a model defined for their symbol -- `type_promotion`: the type promotion to use for the variables -- `check`: whether to check the mapping for errors (see details) -- `attribute_name`: the name of the attribute to store the status in the node, by default: `:plantsimengine_status` - -# Details - -Most arguments can be computed from the graph and the mapping: -- `statuses` is given by the first initialisation: `statuses = Dict(i => Status[] for i in nodes_with_models)` -- `mapped_vars` is computed using `mapped_variables()`, see code in `init_statuses` -- `vars_need_init` is computed using `vars_need_init = Dict(org => filter(x -> isa(last(x), UninitializedVar), vars) |> keys for (org, vars) in mapped_vars) |> -filter(x -> length(last(x)) > 0)` - -The `check` argument is a boolean indicating if variables initialisation should be checked. In the case that some variables need initialisation (partially initialized mapping), we check if the value can be found -in the node attributes (using the variable name). If `true`, the function returns an error if the attribute is missing, otherwise it uses the default value from the model. - -""" -function init_node_status!(node, statuses, mapped_vars, reverse_multiscale_mapping, vars_need_init=Dict{Symbol,Any}(), type_promotion=nothing; check=true, attribute_name=:plantsimengine_status) - node_scale = symbol(node) - - # Check if the node has a model defined for its symbol, if not, no need to compute - haskey(mapped_vars, node_scale) || return - - # We make a copy of the template status for this node: - st_template = copy(mapped_vars[node_scale]) - - # We add a reference to the node into the status, so that we can access it from the models if needed. - push!(st_template, :node => Ref(node)) - - # If some variables still need to be instantiated in the status, look into the MTG node if we can find them, - # and if so, use their value in the status: - if haskey(vars_need_init, node_scale) && length(vars_need_init[node_scale]) > 0 - for var in vars_need_init[node_scale] # e.g. var = :carbon_biomass - if !haskey(node, var) - if !check - # If we don't check, we use the default value from the model (and if it's an UninitializedVar we take its default value): - if isa(st_template[var], UninitializedVar) - st_template[var] = st_template[var].value - end - continue - end - error("Variable `$(var)` is not computed by any model, not initialised by the user in the status, and not found in the MTG at scale $(symbol(node)) (checked for MTG node $(node_id(node))).") - end - # Applying the type promotion to the node attribute if needed: - if isnothing(type_promotion) - node_var = node[var] - else - node_var = - try - promoted_var_type = [] - for (subtype, newtype) in type_promotion - if isa(node[var], subtype) - converted_var = convert(newtype, node[var]) - @warn "Promoting `$(var)` value taken from MTG node $(node_id(node)) ($(symbol(node))) from $subtype to $newtype: $converted_var ($(typeof(converted_var)))" maxlog = 5 - push!(promoted_var_type, converted_var) - end - end - length(promoted_var_type) > 0 ? promoted_var_type[1] : node[var] - catch e - error("Failed to convert variable `$(var)` in MTG node $(node_id(node)) ($(symbol(node))) from type `$(typeof(node[var]))` to type `$(eltype(st_template[var]))`: $(e)") - end - end - if typeof(node_var) != eltype(st_template[var]) - error( - "Initializing variable `$(var)` using MTG node $(node_id(node)) ($(symbol(node))): expected type $(eltype(st_template[var])), found $(typeof(node_var)). ", - "Please check the type of the variable in the MTG, and make it a $(eltype(st_template[var])) by updating the model, or by using `type_promotion`." - ) - end - st_template[var] = node_var - # NB: the variable is not a reference to the value in the MTG, but a copy of it. - # This is because we can't reference a value in a Dict. If we need a ref, the user can use a RefValue in the MTG directly, - # and it will be automatically passed as is. - end - end - - # Make the node status from the template: - st = status_from_template(st_template) - - push!(statuses[node_scale], st) - - # Instantiate the RefVectors on the fly for other scales that map into this scale, *i.e.* - # add a reference to the value of any variable that is used by another scale into its RefVector: - if haskey(reverse_multiscale_mapping, node_scale) - for (organ, vars) in reverse_multiscale_mapping[node_scale] # e.g.: organ = :Leaf; vars = reverse_multiscale_mapping[symbol(node)][organ] - for (var_source, var_target_) in vars # e.g.: var_source = :soil_water_content; var_target = vars[var_source] - var_target = var_target_ isa PreviousTimeStep ? var_target_.variable : var_target_ - push!(mapped_vars[organ][var_target], refvalue(st, var_source)) - end - end - end - - - # Finally, we add the status to the node: - node[attribute_name] = st - - return st -end - -function _status_sort_key(st::Status) - hasproperty(st, :node) || return typemax(Int) - return node_id(st.node) -end - -function _sort_statuses_by_node_id!(statuses) - for statuses_at_scale in values(statuses) - sort!(statuses_at_scale; by=_status_sort_key) - end - return statuses -end - -function _empty_reverse_refvectors!(status_templates, reverse_multiscale_mapping) - for (_, target_scales) in reverse_multiscale_mapping - for (target_scale, vars) in target_scales - haskey(status_templates, target_scale) || continue - target_template = status_templates[target_scale] - for (_, target_var_) in vars - target_var = target_var_ isa PreviousTimeStep ? target_var_.variable : target_var_ - haskey(target_template, target_var) || continue - target_value = target_template[target_var] - target_value isa RefVector || continue - empty!(target_value) - end - end - end - return nothing -end - -function _rebuild_reverse_refvectors!(statuses, status_templates, reverse_multiscale_mapping) - _empty_reverse_refvectors!(status_templates, reverse_multiscale_mapping) - refs_by_target = Dict{Tuple{Symbol,Symbol},Vector{Tuple{Int,Base.RefValue}}}() - for (source_scale, statuses_at_scale) in statuses - haskey(reverse_multiscale_mapping, source_scale) || continue - for st in statuses_at_scale - for (target_scale, vars) in reverse_multiscale_mapping[source_scale] - haskey(status_templates, target_scale) || continue - target_template = status_templates[target_scale] - for (source_var, target_var_) in vars - source_var in propertynames(st) || continue - target_var = target_var_ isa PreviousTimeStep ? target_var_.variable : target_var_ - haskey(target_template, target_var) || continue - target_value = target_template[target_var] - target_value isa RefVector || continue - push!( - get!(refs_by_target, (target_scale, target_var), Tuple{Int,Base.RefValue}[]), - (_status_sort_key(st), refvalue(st, source_var)), - ) - end - end - end - end - for ((target_scale, target_var), refs) in refs_by_target - target_value = status_templates[target_scale][target_var] - sort!(refs; by=first) - for (_, ref) in refs - push!(target_value, ref) - end - end - return nothing -end - -function reindex_runtime_topology!(statuses, status_templates, reverse_multiscale_mapping) - _sort_statuses_by_node_id!(statuses) - _rebuild_reverse_refvectors!(statuses, status_templates, reverse_multiscale_mapping) - return nothing -end - -function reindex_runtime_topology!(sim_object) - reindex_runtime_topology!( - sim_object.statuses, - sim_object.status_templates, - sim_object.reverse_multiscale_mapping, - ) - return nothing -end - -""" - status_from_template(d::Dict{Symbol,Any}) - -Create a status from a template dictionary of variables and values. If the values -are already RefValues or RefVectors, they are used as is, else they are converted to Refs. - -# Arguments - -- `d::Dict{Symbol,Any}`: A dictionary of variables and values. - -# Returns - -- A [`Status`](@ref). - -# Examples - -```jldoctest mylabel -julia> using PlantSimEngine -``` - -```jldoctest mylabel -julia> a, b = PlantSimEngine.status_from_template(Dict(:a => 1.0, :b => 2.0)); -``` - -```jldoctest mylabel -julia> a -1.0 -``` - -```jldoctest mylabel -julia> b -2.0 -``` -""" -function status_from_template(d::Dict{Symbol,T} where {T}) - # Sort vars to put the values that are of type PerStatusRef at the end (we need the pass on the other ones first): - sorted_vars = Dict{Symbol,Any}(sort([pairs(d)...], by=v -> last(v) isa RefVariable ? 1 : 0)) - # Note: PerStatusRef are used to reference variables in the same status for renaming. - - # We create the status with the right references for variables, and for PerStatusRef (we reference the reference variable): - for (k, v) in sorted_vars - if isa(v, RefVariable) - sorted_vars[k] = sorted_vars[v.reference_variable] - else - sorted_vars[k] = ref_var(v) - end - end - - return Status(NamedTuple(sorted_vars)) -end - -""" - ref_var(v) - -Create a reference to a variable. If the variable is already a `Base.RefValue`, -it is returned as is, else it is returned as a Ref to the copy of the value, -or a Ref to the `RefVector` (in case `v` is a `RefVector`). - -# Examples - -```jldoctest mylabel -julia> using PlantSimEngine; -``` - -```jldoctest mylabel -julia> PlantSimEngine.ref_var(1.0) -Base.RefValue{Float64}(1.0) -``` - -```jldoctest mylabel -julia> PlantSimEngine.ref_var([1.0]) -Base.RefValue{Vector{Float64}}([1.0]) -``` - -```jldoctest mylabel -julia> PlantSimEngine.ref_var(Base.RefValue(1.0)) -Base.RefValue{Float64}(1.0) -``` - -```jldoctest mylabel -julia> PlantSimEngine.ref_var(Base.RefValue([1.0])) -Base.RefValue{Vector{Float64}}([1.0]) -``` - -```jldoctest mylabel -julia> PlantSimEngine.ref_var(PlantSimEngine.RefVector([Ref(1.0), Ref(2.0), Ref(3.0)])) -Base.RefValue{PlantSimEngine.RefVector{Float64}}(RefVector{Float64}[1.0, 2.0, 3.0]) -``` -""" -ref_var(v) = Base.Ref(copy(v)) -ref_var(v::T) where {T<:AbstractString} = Base.Ref(v) # No copy method for strings, so directly making a Ref out of it -ref_var(v::T) where {T<:Symbol} = Base.Ref(v) # No copy method for strings, so directly making a Ref out of it -ref_var(v::T) where {T<:Base.RefValue} = v -ref_var(v::T) where {T<:RefVector} = Base.Ref(v) -ref_var(v::T) where {T<:RefVariable} = v -ref_var(v::UninitializedVar) = Base.Ref(copy(v.value)) - -""" - init_simulation(mtg, mapping; nsteps=1, outputs=nothing, type_promotion=nothing, check=true, verbose=true) - -Initialise the simulation. Returns: - -- the mtg -- a status for each node by organ type, considering multi-scale variables -- the dependency graph of the models -- the models parsed as a Dict of organ type => NamedTuple of process => model mapping -- the pre-allocated outputs - -# Arguments - -- `mtg`: the MTG -- `mapping::ModelMapping` (or dictionary-like mapping): associates scales to models/status. -- `nsteps`: the number of steps of the simulation -- `outputs`: the dynamic outputs needed for the simulation -- `type_promotion`: the type promotion to use for the variables -- `check`: whether to check the mapping for errors. Passed to `init_node_status!`. -- `verbose`: print information about errors in the mapping - -# Details - -The function first computes a template of status for each organ type that has a model in the mapping. -This template is used to initialise the status of each node of the MTG, taking into account the user-defined -initialisation, and the (multiscale) mapping. The mapping is used to make references to the variables -that are defined at another scale, so that the values are automatically updated when the variable is changed at -the other scale. Two types of multiscale variables are available: `RefVector` and `MappedVar`. The first one is -used when the variable is mapped to a vector of nodes, and the second one when it is mapped to a single node. This -is given by the user through the mapping, using a symbol for a single node (*e.g.* `=> :Leaf`), and a vector of symbols for a vector of -nodes (*e.g.* `=> [:Leaf]` for one type of node or `=> [:Leaf, :Internode]` for several). - -The function also computes the dependency graph of the models, i.e. the order in which the models should be -called, considering the dependencies between them. The dependency graph is used to call the models in the right order -when the simulation is run. - -Note that if a variable is not computed by models or initialised from the mapping, it is searched in the MTG attributes. -The value is not a reference to the one in the attribute of the MTG, but a copy of it. This is because we can't reference -a value in a Dict. If you need a reference, you can use a `Ref` for your variable in the MTG directly, and it will be -automatically passed as is. -""" -function init_simulation(mtg, mapping; nsteps=1, outputs=nothing, type_promotion=nothing, check=true, verbose=false) - - # Ensure the user called the model generation function to handle vectors passed into a status - # before we keep going - (organ_with_vector, no_vectors_found) = (check_statuses_contain_no_remaining_vectors(mapping)) - if !no_vectors_found - error( - "Error : Mapping status at $organ_with_vector level contains a vector. ", - "If this was intentional, call the function generate_models_from_status_vectors on your mapping before calling run!. ", - "And bear in mind this is not meant for production. If this wasn't intentional, then it's likely an issue on the mapping definition, or an unusual model." - ) - end - - models = Dict(first(m) => parse_models(get_models(last(m))) for m in mapping) - model_specs = if mapping isa ModelMapping && !isempty(mapping.info.model_specs) - deepcopy(mapping.info.model_specs) - else - Dict(first(m) => parse_model_specs(last(m)) for m in mapping) - end - - soft_dep_graphs_roots, hard_dep_dict = hard_dependencies(mapping; verbose=false) - - scale_reachability = _scale_reachability_from_mtg(mtg) - _infer_timestep_hints!(model_specs) - validate_hard_dependency_timestep_consistency(model_specs, soft_dep_graphs_roots) - ignored_hard_children = _hard_dependency_children(soft_dep_graphs_roots) - active_processes_by_scale = _active_processes_for_inference(model_specs, ignored_hard_children) - infer_model_specs_configuration!( - model_specs; - scale_reachability=scale_reachability, - active_processes_by_scale=active_processes_by_scale - ) - validate_model_specs_configuration(model_specs; ignored_processes_by_scale=ignored_hard_children) - - # Get the status of each node by node type, pre-initialised considering multi-scale variables: - statuses, status_templates, reverse_multiscale_mapping, vars_need_init = - init_statuses(mtg, mapping, soft_dep_graphs_roots; type_promotion=type_promotion, verbose=verbose, check=check) - - # First step, get the hard-dependency graph and create SoftDependencyNodes for each hard-dependency root. In other word, we want - # only the nodes that are not hard-dependency of other nodes. These nodes are taken as roots for the soft-dependency graph because they - # are independant. - # Second step, compute the soft-dependency graph between SoftDependencyNodes computed in the first step. To do so, we search the - # inputs of each process into the outputs of the other processes, at the same scale, but also between scales. Then we keep only the - # nodes that have no soft-dependencies, and we set them as root nodes of the soft-dependency graph. The other nodes are set as children - # of the nodes that they depend on. - dep_graph = soft_dependencies_multiscale(soft_dep_graphs_roots, reverse_multiscale_mapping, hard_dep_dict) - # During the building of the soft-dependency graph, we identified the inputs and outputs of each dependency node, - # and also defined **inputs** as MappedVar if they are multiscale, i.e. if they take their values from another scale. - # What we are missing is that we need to also define **outputs** as multiscale if they are needed by another scale. - - # Checking that the graph is acyclic: - iscyclic, cycle_vec = is_graph_cyclic(dep_graph; warn=false) - # Note: we could do that in `soft_dependencies_multiscale` but we prefer to keep the function as simple as possible, and - # usable on its own. - - iscyclic && error("Cyclic dependency detected in the graph. Cycle: \n $(print_cycle(cycle_vec)) \n You can break the cycle using the `PreviousTimeStep` variable in the mapping.") - # Third step, we identify which - - # Print an info if models are declared for nodes that don't exist in the MTG: - if check && any(x -> length(last(x)) == 0, statuses) - model_no_node = join(findall(x -> length(x) == 0, statuses), ", ") - @info "Models given for $model_no_node, but no node with this symbol was found in the MTG." maxlog = 1 - end - - outputs = pre_allocate_outputs(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outputs, nsteps, type_promotion=type_promotion, check=check) - - outputs_index = Dict{Symbol,Int}(s => 1 for s in keys(outputs)) - temporal_state = TemporalState() - mapping_is_multirate = mapping isa ModelMapping ? is_multirate(mapping) : false - return (; - mtg, - statuses, - status_templates, - reverse_multiscale_mapping, - vars_need_init, - dependency_graph=dep_graph, - models, - model_specs, - outputs, - outputs_index, - temporal_state, - is_multirate=mapping_is_multirate - ) -end diff --git a/src/mtg/mapping/compute_mapping.jl b/src/mtg/mapping/compute_mapping.jl deleted file mode 100644 index 531e76d76..000000000 --- a/src/mtg/mapping/compute_mapping.jl +++ /dev/null @@ -1,364 +0,0 @@ -""" - mapped_variables(mapping, dependency_graph=first(hard_dependencies(mapping; verbose=false)); verbose=false) - -Get the variables for each organ type from a dependency graph, with `MappedVar`s for the multiscale mapping. - -# Arguments - -- `mapping::ModelMapping` (or dictionary-like mapping): the mapping between models and scales. -- `dependency_graph::DependencyGraph`: the first-order dependency graph where each model in the mapping is assigned a node. -However, models that are identified as hard-dependencies are not given individual nodes. Instead, they are nested as child -nodes under other models. -- `verbose::Bool`: whether to print the stacktrace of the search for the default value in the mapping. -""" -function mapped_variables(mapping, dependency_graph=first(hard_dependencies(mapping; verbose=false)); verbose=false) - # Initialise a dict that defines the multiscale variables for each organ type: - mapped_vars = mapped_variables_no_outputs_from_other_scale(mapping, dependency_graph) - - # Add the variables that are outputs from another scale and not computed at a scale otherwise, and add them to the organs_mapping: - add_mapped_variables_with_outputs_as_inputs!(mapped_vars) - # E.g.: carbon allocation is computed at the plant scale, but then allocated to each organ (Leaf and Internode) from the same model. - # Which means that the Leaf and Internodes scales should have the carbon allocation as an input variable. - - # Find variables that are inputs to other scales as a `SingleNodeMapping` and declare them as MappedVar from themselves in the source scale. - # This helps us declare it as a reference when we create the template status objects. - transform_single_node_mapped_variables_as_self_node_output!(mapped_vars) - - # We now merge inputs and outputs into a single dictionary: - mapped_vars_per_organ = merge(merge, mapped_vars[:inputs], mapped_vars[:outputs]) - #* Important note: the merge order is important, because if an input variable is uninitialized but computed by a model at the same scale, - #* we don't need any initialisation (it is computed), so we take the default value from the output (that is the default value from the model). - #* It is particularly important for variables that are PreviousTimeStep and mapping to another scale, because we need its initialisation from that other scale - #* that is an output. - mapped_vars = default_variables_from_mapping(mapped_vars_per_organ, verbose) - - return mapped_vars -end - -""" - mapped_variables_no_outputs_from_other_scale(mapping, dependency_graph=first(hard_dependencies(mapping; verbose=false))) - -Get the variables for each organ type from a dependency graph, without the variables that are outputs from another scale. - -# Arguments - -- `mapping::ModelMapping` (or dictionary-like mapping): the mapping between models and scales. -- `dependency_graph::DependencyGraph`: the first-order dependency graph where each model in the mapping is assigned a node. -However, models that are identified as hard-dependencies are not given individual nodes. Instead, they are nested as child -nodes under other models. - -# Details - -This function returns a dictionary with the (multiscale-) inputs and outputs variables for each organ type. - -Note that this function does not include the variables that are outputs from another scale and not computed by this scale, -see `mapped_variables_with_outputs_as_inputs` for that. - """ -function mapped_variables_no_outputs_from_other_scale(mapping, dependency_graph=first(hard_dependencies(mapping; verbose=false))) - nodes_insouts = Dict(organ => (inputs=ins, outputs=outs) for (organ, (soft_dep_graph, ins, outs)) in dependency_graph.roots) - ins = Dict{Symbol,NamedTuple}(organ => flatten_vars(vcat(values(ins)...)) for (organ, (ins, outs)) in nodes_insouts) - outs = Dict{Symbol,NamedTuple}(organ => flatten_vars(vcat(values(outs)...)) for (organ, (ins, outs)) in nodes_insouts) - - return Dict(:inputs => ins, :outputs => outs) -end - -""" - variables_outputs_from_other_scale(mapped_vars) - -For each organ in the `mapped_vars`, find the variables that are outputs from another scale and not computed at this scale otherwise. -This function is used with mapped_variables -""" -function variables_outputs_from_other_scale(mapped_vars) - vars_outputs_from_scales = Dict{Symbol,Vector{Pair{Symbol,Any}}}() - # Scale at which we have to add a variable => [(source_process, source_scale, variable), ...] - for (organ, outs) in mapped_vars[:outputs] # organ = :Leaf ; outs = mapped_vars[:outputs][organ] - for (var, val) in pairs(outs) # var = :carbon_biomass ; val = outs[1] - if isa(val, MappedVar) - orgs = mapped_organ(val) - isnothing(orgs) && continue - orgs_iterable = isa(orgs, Symbol) ? Symbol[orgs] : Symbol[orgs...] - - for o in orgs_iterable - # The MappedVar can only have one value for the default, because it comes from the computing scale (the source scale): - var_default_value = mapped_default(val) - - if mapped_organ_type(val) == MultiNodeMapping - # The variable is written to several organs, the default value must be a vector: - if isa(var_default_value, AbstractVector) - # Mapping into a vector of organs, the default value must be a vector: - if length(var_default_value) != 1 - error( - "The variable `$(mapped_variable(val))` is an output variable computed by scale `$organ` and written to organs at scale `$(join(mapped_organ(val), ", "))`, " * - "but the default value coming from `$organ` is not of length 1: $(var_default_value). " * - "Make sure the model that computes this variable at scale `$organ` has a vector of values of length 1 as " * - "default outputs for variable `$(mapped_variable(val))`." - ) - end - var_default_value = var_default_value[1] - else - error( - "The variable `$(mapped_variable(val))` is an output variable computed by scale `$organ` and written to organs at scale `$(join(mapped_organ(val), ", "))`, " * - "but the default value coming from `$organ` is of length 1: $(var_default_value) instead of a vector. " * - "Make sure the model that computes this variable at scale `$organ` has a vector of values of length 1 as " * - "default outputs for variable `$(mapped_variable(val))`." - ) - end - else - # The variables is mapped to a single organ, the default value must be a scalar: - if isa(var_default_value, AbstractVector) - error( - "The variable `$(mapped_variable(val))` is an output variable computed by scale `$organ` and written to organ at scale `$o`, " * - "but the default value coming from `$organ` is a vector: $(var_default_value). " * - "Make sure the model that computes this variable at scale `$organ` has a scalar value as " * - "default outputs for variable `$(mapped_variable(val))` (*e.g.* $(var_default_value[1])), or update your mapping to map the organ as a vector: " * - """`$(mapped_variable(val)) => ["$o"]`.""" - ) - end - end - # We make a MappedVar object to declare the variable as an input of this scale: - # mapped_var = MappedVar( - # SelfNodeMapping(), # The source organ is itself, we just do that so the variable exist in its status - # source_variable(val, o), - # source_variable(val, o), - # var_default_value, - # ) - - if !haskey(vars_outputs_from_scales, o) - vars_outputs_from_scales[o] = [source_variable(val, o) => var_default_value] - else - push!(vars_outputs_from_scales[o], source_variable(val, o) => var_default_value) - end - end - end - end - end - return vars_outputs_from_scales -end - - -""" - add_mapped_variables_with_outputs_as_inputs!(mapped_vars) - -Add the variables that are computed at a scale and written to another scale into the mapping. -""" -function add_mapped_variables_with_outputs_as_inputs!(mapped_vars) - # Get the variables computed by a scale and written to another scale (we have to add them as inputs to the "another" scale): - outputs_written_by_other_scales = variables_outputs_from_other_scale(mapped_vars) - - for (organ, vars) in outputs_written_by_other_scales # organ = "" ; vars = outputs_written_by_other_scales[organ] - if haskey(mapped_vars[:inputs], organ) - mapped_vars[:inputs][organ] = merge(mapped_vars[:inputs][organ], NamedTuple(first(v) => last(v) for v in vars)) - else - error("The scale $organ is mapped as an output scale from anothe scale, but is not declared in the mapping.") - end - end - - return mapped_vars -end - - -""" - transform_single_node_mapped_variables_as_self_node_output!(mapped_vars) - -Find variables that are inputs to other scales as a `SingleNodeMapping` and declare them as MappedVar from themselves in the source scale. -This helps us declare it as a reference when we create the template status objects. - -These node are found in the mapping as `[:variable_name => :Plant]` (notice that `:Plant` is a scalar value). -""" -function transform_single_node_mapped_variables_as_self_node_output!(mapped_vars) - for (organ, vars) in mapped_vars[:inputs] # e.g. organ = :Leaf; vars = mapped_vars[:inputs][organ] - for (var, mapped_var) in pairs(vars) # e.g. var = :carbon_biomass; mapped_var = vars[var] - if isa(mapped_var, MappedVar{SingleNodeMapping}) - source_organ = mapped_organ(mapped_var) - if source_organ == organ - error("Variable `$var` is mapped to its own scale in organ $organ. This is not allowed.") - end - - haskey(mapped_vars[:outputs], source_organ) || error("Scale $source_organ not found in the mapping, but mapped to the $organ scale.") - haskey(mapped_vars[:outputs][source_organ], source_variable(mapped_var)) || error( - "The variable `$(source_variable(mapped_var))` is mapped from scale `$source_organ` to " * - "scale `$organ`, but is not computed by any model at `$source_organ` scale." - ) - - # If the source variable was already defined as a `MappedVar{SelfNodeMapping}` by another scale, we skip it: - isa(mapped_vars[:outputs][source_organ][source_variable(mapped_var)], MappedVar{SelfNodeMapping}) && continue - # Note: this happens when a variable is mapped to several scales, e.g. soil_water_content computed at soil scale can be - # mapped at :Leaf and :Internode scale. - - # Transforming the variable into a MappedVar pointing to itself: - self_mapped_var = (; - source_variable(mapped_var) => - MappedVar( - SelfNodeMapping(), - source_variable(mapped_var), - source_variable(mapped_var), - mapped_vars[:outputs][source_organ][source_variable(mapped_var)], - ) - ) - mapped_vars[:outputs][source_organ] = merge(mapped_vars[:outputs][source_organ], self_mapped_var) - # Note: merge overwrites the LHS values with the RHS values if they have the same key. - end - end - end - - return mapped_vars -end - -""" - get_multiscale_default_value(mapped_vars, val, mapping_stacktrace=[]) - -Get the default value of a variable from a mapping. - -# Arguments - -- `mapped_vars::Dict{Symbol,Dict{Symbol,Any}}`: the variables mapped to each organ. -- `val::Any`: the variable to get the default value of. -- `mapping_stacktrace::Vector{Any}`: the stacktrace of the search for the value in ascendind the mapping. -""" -function get_multiscale_default_value(mapped_vars, val, mapping_stacktrace=[], level=1) - if isa(val, MappedVar) && !isa(val, MappedVar{SelfNodeMapping}) - # If the value is a MappedVar, we must find the default value of the variable it is mapping into. - # Except if it is mapping to itself, in which case we return the value as is. - _is_same_scale_mapping(val) && return mapped_default(val) - level += 1 - # Find the default value of the variable from the scale it is mapping into (upper scale): - m_organ = mapped_organ(val) - - if isa(m_organ, Symbol) - m_organ = [m_organ] - end - default_vals = [] - for o in m_organ # e.g. o = :Leaf - haskey(mapped_vars[o], source_variable(val, o)) || error("Variable `$(source_variable(val, o))` is mapped from scale `$o` to another scale, but is not computed by any model at `$o` scale.") - upper_value = mapped_vars[o][source_variable(val, o)] - push!(mapping_stacktrace, (mapped_organ=o, mapped_variable=source_variable(val, o), mapped_value=mapped_default(upper_value), level=level)) - # Recursively find the default value until the default value is not a MappedVar: - push!(default_vals, get_multiscale_default_value(mapped_vars, upper_value, mapping_stacktrace, level)) - end - - default_vals = unique(default_vals) - if length(default_vals) == 1 - return default_vals[1] - else - error( - "The variable `$(mapped_variable(val))` is mapped to several scales: $(m_organ), but the default values from the models that compute ", - "this variable at these scales is different: $(default_vals). Please make sure that the default values are the same for variable `$(mapped_variable(val))`.", - ) - end - elseif isa(val, MappedVar{SelfNodeMapping}) - return mapped_default(val) - else - return val - end -end - -""" - default_variables_from_mapping(mapped_vars, verbose=true) - -Get the default values for the mapped variables by recursively searching from the mapping to find the original mapped value. - -# Arguments - -- `mapped_vars::Dict{Symbol,Dict{Symbol,Any}}`: the variables mapped to each organ. -- `verbose::Bool`: whether to print the stacktrace of the search for the default value in the mapping. -""" -function default_variables_from_mapping(mapped_vars, verbose=true) - mapped_vars_mutable = Dict{Symbol,Dict{Symbol,Any}}(k => Dict(pairs(v)) for (k, v) in mapped_vars) - for (organ, vars) in mapped_vars # organ = :Leaf; vars = mapped_vars[organ] - for (var, val) in pairs(vars) # var = :carbon_biomass; val = getproperty(vars,var) - if isa(val, MappedVar) && !isa(val, MappedVar{SelfNodeMapping}) && !_is_same_scale_mapping(val) - mapping_stacktrace = Any[(mapped_organ=organ, mapped_variable=var, mapped_value=mapped_default(mapped_vars[organ][var]), level=1)] - default_value = get_multiscale_default_value(mapped_vars, val, mapping_stacktrace) - mapped_vars_mutable[organ][var] = MappedVar(source_organs(val), mapped_variable(val), source_variable(val), default_value) - if verbose - @info """Default value for $(var) in $(organ) is taken from a mapping, here is the stacktrace: """ maxlog = 1 - - @info Term.Tables.Table(Tables.matrix(reverse(mapping_stacktrace)); header=["Scale", "Variable name", "Default value", "Level"]) - - @info """The stacktrace shows the step-by-step search for the default value, the last row is the value starting from the organ itself, - and going up to the upper-most model in the dependency graph (first row). The `level` column indicates the level of the search, - with 1 being the upper-most model in the dependency graph. The default value is the value found in the first row of the stacktrace, - or the unique value between common levels. - """ maxlog = 1 - end - end - end - end - return mapped_vars_mutable -end - - -""" - convert_reference_values!(mapped_vars::Dict{Symbol,Dict{Symbol,Any}}) - -Convert the variables that are `MappedVar{SelfNodeMapping}` or `MappedVar{SingleNodeMapping}` to RefValues that reference a -common value for the variable; and convert `MappedVar{MultiNodeMapping}` to RefVectors that reference the values for the -variable in the source organs. -""" -function convert_reference_values!(mapped_vars::Dict{Symbol,Dict{Symbol,Any}}) - # For the variables that will be RefValues, i.e. referencing a value that exists for different scales, we need to first - # create a common reference to the value that we use wherever we need this value. These values are created in the dict_mapped_vars - # Dict, and then referenced from there every time we point to it. - # So in essence we replace the MappedVar{SelfNodeMapping} and MappedVar{SingleNodeMapping} by a RefValue to the actual variable. - dict_mapped_vars = Dict{Pair,Any}() - - # First pass: converting the MappedVar{SelfNodeMapping} and MappedVar{SingleNodeMapping} to RefValues: - for (organ, vars) in mapped_vars # e.g.: organ = :Plant; vars = mapped_vars[organ] - for (k, v) in vars # e.g.: k = :aPPFD_larger_scale; v = vars[k] - if isa(v, MappedVar{SelfNodeMapping}) || isa(v, MappedVar{SingleNodeMapping}) - mapped_org = isa(v, MappedVar{SelfNodeMapping}) ? organ : mapped_organ(v) - key = mapped_org => source_variable(v) - - # First time we encounter this variable as a MappedVar, we create its value into the dict_mapped_vars Dict: - if !haskey(dict_mapped_vars, key) - push!(dict_mapped_vars, key => Ref(mapped_default(vars[k]))) - end - - # Then we use the value for the particular variable to replace the MappedVar to a RefValue in the mapping: - vars[k] = dict_mapped_vars[key] - end - end - end - - # Second pass: converting the MappedVar{MultiNodeMapping} to RefVectors: - for (organ, vars) in mapped_vars # e.g.: organ = :Plant; vars = mapped_vars[organ] - for (k, v) in vars # e.g.: k = :carbon_allocation; v = vars[k] - if isa(v, MappedVar{MultiNodeMapping}) - # We have to create a RefVector for the target organ: - orgs_defaults = [mapped_vars[org][source_variable(v, org)] for org in mapped_organ(v)] |> unique - - if eltype(orgs_defaults) <: Ref - orgs_defaults = [org[] for org in orgs_defaults] |> unique - end - - if length(orgs_defaults) > 1 - error( - "In organ $organ, the variable `$(mapped_variable(v))` is mapped to several scales: $(mapped_organ(v)), but the default values from the models that compute ", - "this variable at these scales are different: $(orgs_defaults) (note that `type_promotion` has been applied). ", - "Please make sure that the default values are the same for variable `$(mapped_variable(v))`." - ) - end - vars[k] = RefVector{eltype(orgs_defaults)}() - end - end - end - - # Third pass: getting the same reference for the variables that are mapped at the same scale to another variable (changing its name): - for (organ, vars) in mapped_vars # e.g.: organ = :Plant; vars = mapped_vars[organ] - for (k, v) in vars # e.g.: k = :carbon_allocation; v = vars[k] - if _is_same_scale_mapping(v) - mapped_var = mapped_variable(v) - isa(mapped_var, PreviousTimeStep) && (mapped_var = mapped_var.variable) - if mapped_var == source_variable(v) - # Happens when the mapping is just [PreviousTimeStep(:variable_name)] - vars[k] = mapped_default(vars[k]) - else - # If we want to rename the variable at the same scale, use a reference to the origin variable. - # Note: we use a PerStatusRef to make sure that each status has its own reference to the value (the ref is not shared between statuses). - vars[k] = RefVariable(source_variable(v)) - end - end - end - end - return mapped_vars -end diff --git a/src/mtg/mapping/getters.jl b/src/mtg/mapping/getters.jl deleted file mode 100644 index f3559b2d0..000000000 --- a/src/mtg/mapping/getters.jl +++ /dev/null @@ -1,133 +0,0 @@ -""" - get_models(m) - -Get the models of a dictionary of model mapping. - -# Arguments - -- `m`: a scale mapping entry (for example one value from a [`ModelMapping`](@ref)) - -Returns a vector of models - -# Examples - -```jldoctest mylabel -julia> using PlantSimEngine; -``` - -Import example models (can be found in the `examples` folder of the package, or in the `Examples` sub-modules): - -```jldoctest mylabel -julia> using PlantSimEngine.Examples; -``` - -If we just give a MultiScaleModel, we get its model as a one-element vector: - -```jldoctest mylabel -julia> models = PlantSimEngine.MultiScaleModel( \ - model=ToyCAllocationModel(), \ - mapped_variables=[ \ - :carbon_assimilation => [:Leaf], \ - :carbon_demand => [:Leaf, :Internode], \ - :carbon_allocation => [:Leaf, :Internode] \ - ], \ - ); -``` - -```jldoctest mylabel -julia> PlantSimEngine.get_models(models) -1-element Vector{ToyCAllocationModel}: - ToyCAllocationModel() -``` - -If we give a tuple of models, we get each model in a vector: - -```jldoctest mylabel -julia> models2 = ( \ - PlantSimEngine.MultiScaleModel( \ - model=ToyAssimModel(), \ - mapped_variables=[:soil_water_content => :Soil,], \ - ), \ - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), \ - Status(aPPFD=1300.0, TT=10.0), \ - ); -``` - -Notice that we provide `:Soil`, not `[:Soil]` in the mapping because a single value is expected for the mapping here. - -```jldoctest mylabel -julia> PlantSimEngine.get_models(models2) -2-element Vector{AbstractModel}: - ToyAssimModel{Float64}(0.2) - ToyCDemandModel{Float64}(10.0, 200.0) -``` -""" -get_models(m) = [model_(i) for i in m if !isa(i, Status)] - -""" - get_model_specs(m) - -Normalize model declarations to `ModelSpec`. -Plain models and `MultiScaleModel` entries are converted to `ModelSpec`. -""" -get_model_specs(m::ModelSpec) = [m] -get_model_specs(m::AbstractModel) = [as_model_spec(m)] -get_model_specs(m::MultiScaleModel) = [as_model_spec(m)] -get_model_specs(m) = [as_model_spec(i) for i in m if !isa(i, Status)] - -""" - parse_model_specs(m) - -Return a process-indexed dictionary of normalized `ModelSpec`. -""" -parse_model_specs(m) = Dict{Symbol,ModelSpec}(process(model_(spec)) => spec for spec in get_model_specs(m)) - - -# Same, for the status (if any provided): - -""" - get_status(m) - -Get the status of a dictionary of model mapping. - -# Arguments - -- `m`: a scale mapping entry (for example one value from a [`ModelMapping`](@ref)) - -Returns a [`Status`](@ref) or `nothing`. - -# Examples - -See [`get_models`](@ref) for examples. -""" -function get_status(m) - st = Status[i for i in m if isa(i, Status)] - if length(st) > 1 - error("Only one status can be provided for each organ type.") - end - length(st) == 0 && return nothing - return first(st) -end - -""" - get_mapped_variables(m) - -Get the mapping of a dictionary of model mapping. - -# Arguments - -- `m`: a scale mapping entry (for example one value from a [`ModelMapping`](@ref)) - -Returns a vector of mapped-variable declarations using symbol scales. - -# Examples - -See [`get_models`](@ref) for examples. -""" -function get_mapped_variables(m) - mod_mapping = [get_mapped_variables(i) for i in m if !isa(i, Status)] - if length(mod_mapping) == 0 - return Pair{Symbol,String}[] - end - return reduce(vcat, mod_mapping) |> unique -end diff --git a/src/mtg/mapping/mapping.jl b/src/mtg/mapping/mapping.jl deleted file mode 100755 index c8404ad4d..000000000 --- a/src/mtg/mapping/mapping.jl +++ /dev/null @@ -1,794 +0,0 @@ -_normalize_scale(scale::Symbol; warn::Bool=false, context::Symbol=:PlantSimEngine) = scale -function _normalize_scale(scale; warn::Bool=false, context::Symbol=:PlantSimEngine) - error("Scale names must be `Symbol`s, got `$(typeof(scale))` for `$(repr(scale))` in `$context`.") -end - -""" - SingleNodeMapping(scale) - -Type for the single node mapping, *e.g.* `[:soil_water_content => :Soil,]`. Note that `:Soil` is given as a scalar, -which means that `:soil_water_content` will be a scalar value taken from the unique `:Soil` node in the plant graph. -""" -struct SingleNodeMapping <: AbstractNodeMapping - scale::Symbol -end - -""" - SelfNodeMapping() - -Type for the self node mapping, *i.e.* a node that maps onto itself. -This is used to flag variables that will be referenced as a scalar value by other models. It can happen in two conditions: - - the variable is computed by another scale, so we need this variable to exist as an input to this scale (it is not - computed at this scale otherwise) - - the variable is used as input to another scale but as a single value (scalar), so we need to reference it as a scalar. -""" -struct SelfNodeMapping <: AbstractNodeMapping end - -""" - MultiNodeMapping(scale) - -Type for the multiple node mapping, *e.g.* `[:carbon_assimilation => [:Leaf],]`. Note that `:Leaf` is given as a vector, -which means `:carbon_assimilation` will be a vector of values taken from each `:Leaf` in the plant graph. -""" -struct MultiNodeMapping <: AbstractNodeMapping - scale::Vector{Symbol} -end - -MultiNodeMapping(scale::Symbol) = MultiNodeMapping([scale]) -function MultiNodeMapping(scale::AbstractVector{<:Symbol}) - normalized = Symbol[_normalize_scale(s; context=:MultiNodeMapping) for s in scale] - return MultiNodeMapping(normalized) -end - -""" - MappedVar(source_organ, variable, source_variable, source_default) - -A variable mapped to another scale. - -# Arguments - -- `source_organ`: the organ(s) that are targeted by the mapping -- `variable`: the name of the variable that is mapped -- `source_variable`: the name of the variable from the source organ (the one that computes the variable) -- `source_default`: the default value of the variable - -# Examples - -```jldoctest mylabel -julia> using PlantSimEngine - -julia> PlantSimEngine.MappedVar(PlantSimEngine.SingleNodeMapping(:Leaf), :carbon_assimilation, :carbon_assimilation, 1.0) -PlantSimEngine.MappedVar{PlantSimEngine.SingleNodeMapping, Symbol, Symbol, Float64}(PlantSimEngine.SingleNodeMapping(:Leaf), :carbon_assimilation, :carbon_assimilation, 1.0) -``` -""" -struct MappedVar{O<:AbstractNodeMapping,V1<:Union{Symbol,PreviousTimeStep},V2<:Union{S,Vector{S}} where {S<:Symbol},T} - source_organ::O - variable::V1 - source_variable::V2 - source_default::T -end - -mapped_variable(m::MappedVar) = m.variable -source_organs(m::MappedVar) = m.source_organ -source_organs(m::MappedVar{O,V1,V2,T}) where {O<:AbstractNodeMapping,V1,V2,T} = nothing -mapped_organ(m::MappedVar{O,V1,V2,T}) where {O,V1,V2,T} = source_organs(m).scale -mapped_organ(m::MappedVar{O,V1,V2,T}) where {O<:SelfNodeMapping,V1,V2,T} = nothing -mapped_organ(m::MappedVar{O,V1,V2,T}) where {O<:SameScale,V1,V2,T} = nothing -mapped_organ_type(m::MappedVar{O,V1,V2,T}) where {O<:AbstractNodeMapping,V1,V2,T} = O -source_variable(m::MappedVar) = m.source_variable -function source_variable(m::MappedVar{O,V1,V2,T}, organ) where {O<:SingleNodeMapping,V1,V2<:Symbol,T} - if organ != mapped_organ(m) - error("Organ $organ not found in the mapping of the variable $(mapped_variable(m)).") - end - m.source_variable -end - -function source_variable(m::MappedVar{O,V1,V2,T}, organ) where {O<:MultiNodeMapping,V1,V2<:Vector{Symbol},T} - if !(organ in mapped_organ(m)) - error("Organ $organ not found in the mapping of the variable $(mapped_variable(m)).") - end - m.source_variable[findfirst(o -> o == organ, mapped_organ(m))] -end - -mapped_default(m::MappedVar) = m.source_default -mapped_default(m::MappedVar{O,V1,V2,T}, organ) where {O<:MultiNodeMapping,V1,V2<:Vector{Symbol},T} = m.source_default[findfirst(o -> o == organ, mapped_organ(m))] -mapped_default(m) = m # For any variable that is not a MappedVar, we return it as is - -_is_same_scale_mapping(m::MappedVar) = source_organs(m) isa SameScale -_is_same_scale_mapping(::Any) = false - -# This defines the type of mapping setup: either single or multiscale. Used to dispatch methods for e.g. `dep` or `to_initialize`. -abstract type AbstractScaleSetup end - -struct MultiScale <: AbstractScaleSetup end -struct SingleScale <: AbstractScaleSetup end - -const ModelRateDeclarations = Dict{Symbol,Any} -const ReverseMappingTarget = Union{Symbol,PreviousTimeStep} -const ReverseMultiscaleMapping = Dict{Symbol,Dict{Symbol,Dict{Symbol,ReverseMappingTarget}}} - -""" - ModelMappingInfo - -Cached metadata computed at `ModelMapping` construction time to avoid repeated -normalization/introspection work across runtime entrypoints. -""" -struct ModelMappingInfo - validated::Bool - is_valid::Bool - is_multirate::Bool - scales::Vector{Symbol} - models_per_scale::Dict{Symbol,Int} - processes_per_scale::Dict{Symbol,Vector{Symbol}} - declared_rates::ModelRateDeclarations - vars_need_init::Any - model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}} - recommendations::Vector{String} -end - -function _empty_model_mapping_info() - ModelMappingInfo( - false, - false, - false, - Symbol[], - Dict{Symbol,Int}(), - Dict{Symbol,Vector{Symbol}}(), - ModelRateDeclarations(), - NamedTuple(), - Dict{Symbol,Dict{Symbol,ModelSpec}}(), - String[], - ) -end - -""" - ModelMapping(mapping; check=true) - -Validated mapping between MTG scales and model definitions. - -Each scale entry may be provided as: -- a single model, -- a tuple of models with an optional [`Status`](@ref), - -At construction time, the mapping is normalized and checked to fail early on common -configuration errors: -- each scale must define at least one model, -- at most one `Status` is allowed per scale, -- mapped scales must exist in the mapping, -- mapped source variables must exist on the source scale (as a model output or status variable), -- duplicate process declarations at a given scale are rejected. - -# Notes - -The type behaves like a read-only dictionary keyed by scale name (`Symbol`). -Use `Dict(mapping)` to recover a plain dictionary. -""" -struct ModelMapping{S<:AbstractScaleSetup,D} <: AbstractDict{Symbol,Tuple} where {D<:Union{Dict{Symbol,Tuple},SingleScaleModelSet}} - data::D - info::ModelMappingInfo - type_promotion::Union{Nothing,Dict} -end - -function _build_model_mapping(::Type{S}, data; validated::Bool, type_promotion::Union{Nothing,Dict}=nothing) where {S<:AbstractScaleSetup} - info = try - _build_model_mapping_info(S, data; validated=validated) - catch - _empty_model_mapping_info() - end - ModelMapping{S,typeof(data)}(data, info, type_promotion) -end - -ModelMapping{S}(data) where {S<:AbstractScaleSetup} = _build_model_mapping(S, data; validated=false) - -""" - model_rate(model::AbstractModel) - -Optional model hook used by [`ModelMapping`](@ref) to check rate compatibility. - -By default it returns `nothing` (no explicit rate contract). Package users can provide -model-specific methods that return a comparable value (for example `Dates.Period`), and -`ModelMapping` will reject incompatible mapped couplings. -""" -model_rate(::AbstractModel) = nothing -model_rate(model::MultiScaleModel) = model_rate(model_(model)) - -Base.length(mapping::ModelMapping{MultiScale}) = length(mapping.data) -Base.length(::ModelMapping{SingleScale}) = 1 -Base.iterate(mapping::ModelMapping{MultiScale}, state...) = iterate(mapping.data, state...) -# Base.iterate(mapping::ModelMapping{SingleScale}, state...) = iterate(mapping.data.models, state...) -function Base.show(io::IO, mapping::ModelMapping) - print( - io, - "ModelMapping(", - length(mapping.info.scales), - " scale", - length(mapping.info.scales) == 1 ? "" : "s", - ", multirate=", - mapping.info.is_multirate, - ")" - ) -end - -function _isempty_vars_need_init(vars_need_init) - vars_need_init isa NamedTuple && return isempty(keys(vars_need_init)) - vars_need_init isa AbstractDict && return all(isempty, values(vars_need_init)) - vars_need_init isa AbstractVector && return isempty(vars_need_init) - return isnothing(vars_need_init) -end - -function _timing_group_label_for_spec(spec::ModelSpec) - if !isnothing(timestep(spec)) - return string("explicit ", timestep(spec), " (ModelSpec)") - end - - model_clock = timespec(model_(spec)) - if !_is_default_clock(model_clock) - return string("model timespec ", model_clock) - end - - return "meteo base step (inferred at runtime)" -end - -function _model_timing_groups(info::ModelMappingInfo) - groups = Dict{String,Int}() - for specs_at_scale in values(info.model_specs), spec in values(specs_at_scale) - label = _timing_group_label_for_spec(spec) - groups[label] = get(groups, label, 0) + 1 - end - return groups -end - -function _show_model_mapping_plain(io::IO, mapping::ModelMapping) - info = mapping.info - println(io, "ModelMapping") - println(io, " validated: ", info.validated, " (", info.is_valid ? "valid" : "invalid", ")") - println(io, " multirate: ", info.is_multirate) - println(io, " scales (", length(info.scales), "): ", join(info.scales, ", ")) - for scale in info.scales - print(io, " - ", scale, ": ", get(info.models_per_scale, scale, 0), " model(s)") - processes = get(info.processes_per_scale, scale, Symbol[]) - if !isempty(processes) - print(io, ", Processes=", join(string.(processes), ", ")) - end - rate = get(info.declared_rates, scale, nothing) - if !isnothing(rate) - print(io, ", Rate=", rate) - end - println(io) - end - timing_groups = _model_timing_groups(info) - if !isempty(timing_groups) - println(io, " Timing groups:") - for label in sort!(collect(keys(timing_groups)); by=string) - println(io, " - ", label, ": ", timing_groups[label], " model(s)") - end - println(io, " Get resolved timings with: `effective_rate_summary(modelmapping, meteo)`") - end - if _isempty_vars_need_init(info.vars_need_init) - println(io, " Variables to initialize: none") - else - println(io, " Variables to initialize: ", info.vars_need_init) - end - if !isempty(info.recommendations) - println(io, " Recommendations:") - for recommendation in info.recommendations - println(io, " - ", recommendation) - end - end -end - -function Base.show(io::IO, m::MIME"text/plain", mapping::ModelMapping) - if get(io, :compact, false) - return show(io, mapping) - end - _show_model_mapping_plain(io, mapping) - if mapping isa ModelMapping{SingleScale} - println(io, " status:") - show(io, m, mapping.data) - end -end - -Base.keys(mapping::ModelMapping) = keys(mapping.data) -Base.values(mapping::ModelMapping) = values(mapping.data) -Base.pairs(mapping::ModelMapping) = pairs(mapping.data) -Base.keys(::ModelMapping{SingleScale}) = (:Default,) -Base.values(mapping::ModelMapping{SingleScale}) = ((values(mapping.data.models)..., status(mapping.data)),) -Base.pairs(mapping::ModelMapping{SingleScale}) = (:Default => (values(mapping.data.models)..., status(mapping.data)),) -Base.getindex(mapping::ModelMapping, key::Symbol) = mapping.data[key] -function Base.getindex(mapping::ModelMapping{SingleScale}, key::Symbol) - if key == :Default - return (values(mapping.data.models)..., status(mapping.data)) - end - return getindex(mapping.data, key) -end -Base.getindex(mapping::ModelMapping{SingleScale}, key::Integer) = getindex(mapping.data, key) -Base.haskey(mapping::ModelMapping, key::Symbol) = haskey(mapping.data, key) -Base.eltype(::Type{ModelMapping}) = Pair{Symbol,Tuple} -Base.copy(mapping::ModelMapping{MultiScale}) = _build_model_mapping(MultiScale, copy(mapping.data); validated=mapping.info.validated, type_promotion=deepcopy(mapping.type_promotion)) -Base.copy(mapping::ModelMapping{SingleScale}) = _build_model_mapping(SingleScale, copy(mapping.data); validated=mapping.info.validated, type_promotion=deepcopy(mapping.type_promotion)) -Base.copy(mapping::ModelMapping{SingleScale}, status) = _build_model_mapping(SingleScale, copy(mapping.data, status); validated=mapping.info.validated, type_promotion=deepcopy(mapping.type_promotion)) -Base.Dict(mapping::ModelMapping) = copy(mapping.data) -Base.:(==)(left::ModelMapping{SingleScale}, right::ModelMapping{SingleScale}) = left.data == right.data && left.type_promotion == right.type_promotion - -function Base.getproperty(mapping::ModelMapping{SingleScale}, name::Symbol) - (name === :data || name === :info || name === :type_promotion) && return getfield(mapping, name) - return getproperty(getfield(mapping, :data), name) -end - -function ModelMapping{MultiScale}(mapping::T; check::Bool=true, type_promotion::Union{Nothing,Dict}=nothing) where {T<:AbstractDict} - normalized = _normalize_multiscale_mapping(mapping) - check && _check_multiscale_mapping!(normalized) - _build_model_mapping(MultiScale, normalized; validated=check, type_promotion=type_promotion) -end - -ModelMapping(mapping::AbstractDict; check::Bool=true, type_promotion::Union{Nothing,Dict}=nothing) = - ModelMapping{MultiScale}(mapping; check=check, type_promotion=type_promotion) - -function ModelMapping(mapping::ModelMapping{MultiScale}; check::Bool=true, type_promotion::Union{Nothing,Dict}=mapping.type_promotion) - if check || type_promotion != mapping.type_promotion - return ModelMapping(mapping.data; check=check, type_promotion=type_promotion) - end - return mapping -end - -function ModelMapping(mapping::ModelMapping{SingleScale}; check::Bool=true, type_promotion::Union{Nothing,Dict}=mapping.type_promotion) - if type_promotion != mapping.type_promotion - error("Cannot change `type_promotion` on an already constructed single-scale `ModelMapping`; rebuild it from models and status.") - end - return check ? _build_model_mapping(SingleScale, mapping.data; validated=true, type_promotion=mapping.type_promotion) : mapping -end - -""" - ModelMapping(scale_mapping_pairs...; check=true, type_promotion=nothing) - ModelMapping(models...; scale=:Default, status=nothing, type_promotion=nothing, check=true, processes...) - -Convenience constructors for [`ModelMapping`](@ref): - -- pass `scale => models` pairs directly (dict-like syntax), -- or pass models/processes directly for a single scale. - -`type_promotion` may be a dictionary of source type to target type, for example -`Dict(Float64 => Float32)`. In single-scale mappings it is applied when the -backing status is constructed. In multiscale mappings it is stored on the -mapping and applied when a `GraphSimulation` is initialized. -""" -function ModelMapping( - args...; - scale::Symbol=:Default, - status=nothing, - type_promotion::Union{Nothing,Dict}=nothing, - check::Bool=true, - processes... -) - isempty(args) && isempty(processes) && error( - "No mapping or model was provided. Use `ModelMapping(:Scale => models)` or pass models directly." - ) - any(arg -> arg isa Pair && first(arg) isa AbstractString, args) && error( - "String scale names are removed. Use Symbol scales, e.g. `:Leaf` instead of `\"Leaf\"`." - ) - !isempty(args) && all(arg -> arg isa Pair && !(first(arg) isa Symbol), args) && error( - "`ModelMapping(Float64 => Float32)` type-promotion shorthand is removed. ", - "Use `Dict(Float64 => Float32)` as the `type_promotion` value." - ) - - if _all_scale_pairs(args) - isempty(processes) || error( - "Cannot mix scale-level pairs with process keyword arguments. ", - "Use either `:Scale => models` pairs, or single-scale process/model arguments." - ) - isnothing(status) || error( - "`status` cannot be used with scale-level pair syntax. ", - "Provide statuses inside each scale mapping instead." - ) - raw_mapping = Dict{Symbol,Any}( - _normalize_scale(first(pair); context=:ModelMapping) => last(pair) - for pair in args - ) - return ModelMapping{MultiScale}(raw_mapping; check=check, type_promotion=type_promotion) - end - - _contains_scale_like_pair(args) && error( - "Invalid argument mix: scale-level pairs must not be mixed with model arguments." - ) - - flat_args = Any[] - for arg in args - if arg isa Pair && first(arg) isa Symbol - push!(flat_args, last(arg)) - elseif arg isa NamedTuple - append!(flat_args, values(arg)) - elseif arg isa Tuple - append!(flat_args, arg) - else - push!(flat_args, arg) - end - end - - return _build_model_mapping( - SingleScale, - SingleScaleModelSet(flat_args...; status=status, type_promotion=type_promotion, variables_check=check, processes...); - validated=check, - type_promotion=type_promotion - ) - - #TODO: Use the following when we merge the single-scale and multiscale mapping paths (create a fake scale): - single_scale_models = _single_scale_mapping_entries(args, processes, status) - # return ModelMapping{SingleScale}(Dict(_normalize_scale(scale) => single_scale_models), check=check) -end - -# Canonical API dispatches for model mappings. -dep(mapping::ModelMapping{SingleScale}; verbose::Bool=true) = dep(mapping.data) -dep(mapping::ModelMapping{MultiScale}; verbose::Bool=true) = dep(mapping.data; verbose=verbose) -hard_dependencies(mapping::ModelMapping{SingleScale}; verbose::Bool=true) = hard_dependencies(mapping.data) -hard_dependencies(mapping::ModelMapping{MultiScale}; verbose::Bool=true) = hard_dependencies(mapping.data; verbose=verbose) -inputs(mapping::ModelMapping) = inputs(mapping.data) -outputs(mapping::ModelMapping) = outputs(mapping.data) -variables(mapping::ModelMapping) = variables(mapping.data) -function to_initialize(mapping::ModelMapping, graph=nothing) - isnothing(graph) && return mapping.info.vars_need_init - return to_initialize(mapping.data, graph) -end -reverse_mapping(mapping::ModelMapping; all=true) = reverse_mapping(mapping.data; all=all) -init_variables(mapping::ModelMapping{SingleScale}; verbose=true) = init_variables(mapping.data; verbose=verbose) -to_initialize(mapping::ModelMapping{SingleScale}) = mapping.info.vars_need_init -to_initialize(mapping::ModelMapping{SingleScale}, graph) = to_initialize(mapping) -pre_allocate_outputs(mapping::ModelMapping{SingleScale}, outs, nsteps; type_promotion=nothing, check=true) = - pre_allocate_outputs(mapping.data, outs, nsteps; type_promotion=type_promotion, check=check) - -mapping_info(mapping::ModelMapping) = mapping.info -is_multirate(mapping::ModelMapping) = mapping.info.is_multirate -is_valid(mapping::ModelMapping) = mapping.info.is_valid -type_promotion(mapping::ModelMapping) = mapping.type_promotion -type_promotion(::Any) = nothing -_type_promotion(mapping) = type_promotion(mapping) - -function _all_scale_pairs(args) - !isempty(args) && all(arg -> arg isa Pair && first(arg) isa Symbol, args) -end - -function _contains_scale_like_pair(args) - any(arg -> arg isa Pair && first(arg) isa Symbol, args) -end - -function _single_scale_mapping_entries(args, processes, status) - models = Any[] - - for arg in args - if arg isa Pair && first(arg) isa Symbol - push!(models, last(arg)) - elseif arg isa NamedTuple - append!(models, values(arg)) - elseif arg isa Tuple - append!(models, arg) - else - push!(models, arg) - end - end - - append!(models, values(processes)) - - if !isnothing(status) - status_entry = status isa Status ? status : Status(status) - push!(models, status_entry) - end - - return tuple(models...) -end - -function _normalize_multiscale_mapping(mapping::AbstractDict) - isempty(mapping) && error("ModelMapping cannot be empty. Provide at least one scale with models.") - normalized = Dict{Symbol,Tuple}() - for (scale, scale_mapping) in pairs(mapping) - scale_name = _normalize_scale(scale; context=:ModelMapping) - normalized[scale_name] = _normalize_scale_mapping(scale_name, scale_mapping) - end - return normalized -end - -function _normalize_scale_mapping(scale::Symbol, scale_mapping::SingleScaleModelSet) - return _normalize_scale_mapping(scale, (values(scale_mapping.models)..., status(scale_mapping))) -end - -function _normalize_scale_mapping(scale::Symbol, scale_mapping::ModelMapping{SingleScale}) - return _normalize_scale_mapping(scale, scale_mapping.data) -end - -function _normalize_scale_mapping(scale::Symbol, scale_mapping::Union{AbstractModel,MultiScaleModel,ModelSpec}) - return (scale_mapping,) -end - -function _normalize_scale_mapping(scale::Symbol, scale_mapping::Tuple) - normalized_items = Any[] - for item in scale_mapping - if item isa SingleScaleModelSet - append!(normalized_items, values(item.models)) - push!(normalized_items, status(item)) - elseif item isa Union{AbstractModel,MultiScaleModel,ModelSpec,Status} - push!(normalized_items, item) - else - error( - "Invalid mapping entry at scale `$scale`: expected models, ModelSpec, Status, or single-scale ModelMapping, got $(typeof(item))." - ) - end - end - return tuple(normalized_items...) -end - -function _normalize_scale_mapping(scale::Symbol, scale_mapping) - error( - "Invalid mapping entry at scale `$scale`: expected a model, ModelSpec, tuple of models/Status, or single-scale ModelMapping, got $(typeof(scale_mapping))." - ) -end - -function _check_multiscale_mapping!(mapping::Dict{Symbol,Tuple}) - _check_scales_have_models!(mapping) - _check_scale_process_uniqueness!(mapping) - _check_mapped_sources_exist!(mapping) - return mapping -end - -function _check_scales_have_models!(mapping::Dict{Symbol,Tuple}) - for (scale, scale_mapping) in mapping - n_status = count(item -> item isa Status, scale_mapping) - n_status > 1 && error("Scale `$scale` defines $n_status statuses. Only one Status is allowed per scale.") - - models = get_models(scale_mapping) - isempty(models) && error( - "Scale `$scale` defines no model. Add at least one model, or remove this scale from the mapping." - ) - end -end - -function _check_scale_process_uniqueness!(mapping::Dict{Symbol,Tuple}) - for (scale, scale_mapping) in mapping - process_names = [_process_name_for_mapping_check(model) for model in get_models(scale_mapping)] - duplicates = unique(filter(p -> count(==(p), process_names) > 1, process_names)) - isempty(duplicates) && continue - duplicate_names = join(string.(duplicates), ", ") - error( - "Scale `$scale` defines duplicate process(es): $duplicate_names. ", - "Keep only one model per process at a given scale (or use hard dependencies)." - ) - end -end - -function _process_name_for_mapping_check(model) - try - return process(model) - catch - return Symbol(nameof(typeof(model))) - end -end - -function _check_mapped_sources_exist!(mapping::Dict{Symbol,Tuple}) - available_variables = _available_variables_by_scale(mapping) - scale_rates = _declared_model_rates_by_scale(mapping) - - for (target_scale, scale_mapping) in mapping - for item in scale_mapping - mapped_vars = _mapping_item_mapped_variables(item) - isempty(mapped_vars) && continue - - base_model = _mapping_item_model(item) - model_inputs = Set(keys(inputs_(base_model))) - model_outputs = Set(keys(outputs_(base_model))) - for mapped_var in mapped_vars - mapped_variable_name = first(mapped_var) isa PreviousTimeStep ? first(mapped_var).variable : first(mapped_var) - checks_source_value = (mapped_variable_name in model_inputs) && !(mapped_variable_name in model_outputs) - - for (source_scale_raw, source_variable) in _as_mapping_sources(last(mapped_var)) - source_scale = isnothing(source_scale_raw) ? target_scale : source_scale_raw - - haskey(mapping, source_scale) || error( - "Scale `$target_scale` maps variable `$(first(mapped_var))` to missing scale `$source_scale`. ", - "Add `$source_scale` to ModelMapping, or fix the mapped scale name." - ) - - if checks_source_value && source_variable ∉ available_variables[source_scale] - error( - "Scale `$target_scale` maps variable `$(first(mapped_var))` to `$source_scale.$source_variable`, ", - "but `$source_variable` is not available at scale `$source_scale` ", - "(neither model output, Status variable, nor mapped output from another scale). ", - "Define a model output for `$source_variable`, initialize it in the source scale Status, ", - "or map this variable from another scale into `$source_scale` before using it." - ) - end - - if checks_source_value && !_rates_compatible(scale_rates[target_scale], scale_rates[source_scale]) - error( - "Scale `$target_scale` declares model rate $(scale_rates[target_scale]) but maps input `$(first(mapped_var))` ", - "from scale `$source_scale` with model rate $(scale_rates[source_scale]). ", - "Align model rates between scales or remove explicit `model_rate` declarations." - ) - end - end - end - end - end -end - -_mapping_item_mapped_variables(item::ModelSpec) = mapped_variables_(item) -_mapping_item_mapped_variables(item::MultiScaleModel) = mapped_variables_(item) -_mapping_item_mapped_variables(::Any) = Pair{Symbol,Symbol}[] - -_mapping_item_model(item::ModelSpec) = model_(item) -_mapping_item_model(item::MultiScaleModel) = model_(item) - -function _as_mapping_scale(source_scale::Symbol) - source_scale === Symbol("") && error("`Symbol(\"\")` same-scale mappings are removed. Use `SameScale()` instead.") - return _normalize_scale(source_scale; context=:ModelMapping) -end -_as_mapping_scale(::SameScale) = nothing - -_as_mapping_sources(source::Pair{T,Symbol}) where {T<:Union{Symbol,SameScale}} = - (_as_mapping_scale(first(source)) => last(source),) -_as_mapping_sources(source::AbstractVector{<:Pair}) = - Tuple(_as_mapping_scale(first(item)) => last(item) for item in source) - -function _available_variables_by_scale(mapping::Dict{Symbol,Tuple}) - available = Dict{Symbol,Set{Symbol}}() - for (scale, scale_mapping) in mapping - vars = Set{Symbol}() - for model in get_models(scale_mapping) - union!(vars, keys(outputs_(model))) - end - st = get_status(scale_mapping) - !isnothing(st) && union!(vars, keys(st)) - available[scale] = vars - end - - # Variables produced at one scale and explicitly mapped as outputs to another - # scale are available at the target scale as runtime references. - for (source_scale, scale_mapping) in mapping - for item in scale_mapping - mapped_vars = _mapping_item_mapped_variables(item) - isempty(mapped_vars) && continue - base_model = _mapping_item_model(item) - model_outputs = Set(keys(outputs_(base_model))) - for mapped_var in mapped_vars - mapped_variable_name = first(mapped_var) isa PreviousTimeStep ? first(mapped_var).variable : first(mapped_var) - mapped_variable_name in model_outputs || continue - for (target_scale_raw, target_variable) in _as_mapping_sources(last(mapped_var)) - target_scale = isnothing(target_scale_raw) ? source_scale : target_scale_raw - haskey(available, target_scale) || continue - push!(available[target_scale], target_variable) - end - end - end - end - - return available -end - -function _declared_model_rates_by_scale(mapping::Dict{Symbol,Tuple}) - rates = ModelRateDeclarations() - for (scale, scale_mapping) in mapping - declared_rates = unique(filter(!isnothing, map(model_rate, get_models(scale_mapping)))) - if length(declared_rates) > 1 - error( - "Scale `$scale` declares incompatible model rates $(declared_rates). ", - "Use a single rate per scale, or leave `model_rate` undefined (`nothing`)." - ) - end - rates[scale] = isempty(declared_rates) ? nothing : only(declared_rates) - end - return rates -end - -_rates_compatible(rate1, rate2) = isnothing(rate1) || isnothing(rate2) || rate1 == rate2 - -function _spec_declares_multirate(spec::ModelSpec) - model = model_(spec) - !isnothing(timestep(spec)) && return true - # Explicit input bindings are also used for same-rate disambiguation, - # so they must not, by themselves, force multirate runtime. - !isnothing(meteo_window(spec)) && return true - !isempty(keys(output_routing(spec))) && return true - timespec(model) != ClockSpec(1.0, 0.0) && return true - return false -end - -function _mapping_declares_multirate(model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}, declared_rates::ModelRateDeclarations) - any(!isnothing, values(declared_rates)) && return true - for specs_at_scale in values(model_specs), spec in values(specs_at_scale) - _spec_declares_multirate(spec) && return true - end - return false -end - -function _model_summary_from_mapping(mapping::Dict{Symbol,Tuple}) - models_per_scale = Dict{Symbol,Int}() - processes_per_scale = Dict{Symbol,Vector{Symbol}}() - for (scale, scale_mapping) in mapping - models = get_models(scale_mapping) - models_per_scale[scale] = length(models) - processes_per_scale[scale] = [_process_name_for_mapping_check(model) for model in models] - end - return models_per_scale, processes_per_scale -end - -function _parse_model_specs_from_mapping(mapping::Dict{Symbol,Tuple}) - Dict(scale => parse_model_specs(scale_mapping) for (scale, scale_mapping) in mapping) -end - -function _build_model_mapping_recommendations( - validated::Bool, - is_multirate::Bool, - vars_need_init -) - recommendations = String[] - if !validated - push!(recommendations, "Built with `check=false`: rebuild with `check=true` to validate consistency.") - end - if !_isempty_vars_need_init(vars_need_init) - push!(recommendations, "Initialize required variables listed above (see `to_initialize(mapping)`).") - end - if is_multirate - push!(recommendations, "Multirate is enabled from mapping metadata; `run!(mtg, mapping, ...)` auto-detects it.") - end - return recommendations -end - -function _build_model_mapping_info(::Type{SingleScale}, mapping::SingleScaleModelSet; validated::Bool) - specs = Dict( - :Default => Dict{Symbol,ModelSpec}( - process(model) => as_model_spec(model) for model in values(mapping.models) - ) - ) - - declared_rates = ModelRateDeclarations(:Default => nothing) - vars_need_init = try - to_initialize(mapping) - catch - NamedTuple() - end - is_multirate = false - recommendations = _build_model_mapping_recommendations(validated, is_multirate, vars_need_init) - processes = [process(model) for model in values(mapping.models)] - return ModelMappingInfo( - validated, - validated, - is_multirate, - [:Default], - Dict(:Default => length(mapping.models)), - Dict(:Default => processes), - declared_rates, - vars_need_init, - specs, - recommendations, - ) -end - -function _build_model_mapping_info(::Type{MultiScale}, mapping::Dict{Symbol,Tuple}; validated::Bool) - scales = collect(keys(mapping)) - models_per_scale, processes_per_scale = _model_summary_from_mapping(mapping) - declared_rates = try - _declared_model_rates_by_scale(mapping) - catch - ModelRateDeclarations(scale => nothing for scale in scales) - end - model_specs = try - _parse_model_specs_from_mapping(mapping) - catch - Dict{Symbol,Dict{Symbol,ModelSpec}}() - end - vars_need_init = try - to_initialize(mapping, nothing) - catch - Dict{Symbol,Vector{Symbol}}() - end - is_multirate = _mapping_declares_multirate(model_specs, declared_rates) - recommendations = _build_model_mapping_recommendations(validated, is_multirate, vars_need_init) - return ModelMappingInfo( - validated, - validated, - is_multirate, - scales, - models_per_scale, - processes_per_scale, - declared_rates, - vars_need_init, - model_specs, - recommendations, - ) -end diff --git a/src/mtg/mapping/model_generation_from_status_vectors.jl b/src/mtg/mapping/model_generation_from_status_vectors.jl deleted file mode 100644 index e0c3cad47..000000000 --- a/src/mtg/mapping/model_generation_from_status_vectors.jl +++ /dev/null @@ -1,282 +0,0 @@ -# Utilities to handle vectors passed into a mapping's statuses -# This is a convenience when prototyping, not recommended for production or proper fitting -# (Write the actual finalised model explicitely instead) - - -# Status vectors are turned into regular runtime models so they can participate in -# dependency inference without relying on top-level eval or world-age-sensitive -# method generation. - -# There will still be brittleness given that it's not trivial to handle user/modeler errors : -# For instance, providing a vector that is called in a scale mapping is likely to cause things to go badly - -# May need some more complex timestep models in the future -# TODO : unhandled case : what if the timestep models are already in the provided modellist ? - -# These models might be worth exposing in the future ? -PlantSimEngine.@process "basic_current_timestep" verbose = false - -struct HelperCurrentTimestepModel <: AbstractBasic_Current_TimestepModel -end - -PlantSimEngine.inputs_(::HelperCurrentTimestepModel) = (next_timestep=1,) -PlantSimEngine.outputs_(m::HelperCurrentTimestepModel) = (current_timestep=1,) - -function PlantSimEngine.run!(m::HelperCurrentTimestepModel, models, status, meteo, constants=nothing, extra=nothing) - status.current_timestep = status.next_timestep - end - - PlantSimEngine.ObjectDependencyTrait(::Type{<:HelperCurrentTimestepModel}) = PlantSimEngine.IsObjectDependent() - PlantSimEngine.TimeStepDependencyTrait(::Type{<:HelperCurrentTimestepModel}) = PlantSimEngine.IsTimeStepDependent() - - PlantSimEngine.@process "basic_next_timestep" verbose = false - struct HelperNextTimestepModel <: AbstractBasic_Next_TimestepModel - end - - PlantSimEngine.inputs_(::HelperNextTimestepModel) = (current_timestep=1,) - PlantSimEngine.outputs_(m::HelperNextTimestepModel) = (next_timestep=1,) - - function PlantSimEngine.run!(m::HelperNextTimestepModel, models, status, meteo, constants=nothing, extra=nothing) - status.next_timestep = status.current_timestep + 1 - end - -PlantSimEngine.ObjectDependencyTrait(::Type{<:HelperNextTimestepModel}) = PlantSimEngine.IsObjectDependent() -PlantSimEngine.TimeStepDependencyTrait(::Type{<:HelperNextTimestepModel}) = PlantSimEngine.IsTimeStepDependent() - -struct GeneratedStatusVectorModel{V<:AbstractVector} <: AbstractModel - process_name::Symbol - output_name::Symbol - values::V -end - -process(model::GeneratedStatusVectorModel) = model.process_name -PlantSimEngine.inputs_(::GeneratedStatusVectorModel) = (current_timestep=1,) -PlantSimEngine.outputs_(model::GeneratedStatusVectorModel) = NamedTuple{(model.output_name,)}((first(model.values),)) - -function PlantSimEngine.run!(model::GeneratedStatusVectorModel, models, status, meteo, constants=nothing, extra_args=nothing) - status[model.output_name] = model.values[status.current_timestep] -end - -PlantSimEngine.ObjectDependencyTrait(::Type{<:GeneratedStatusVectorModel}) = PlantSimEngine.IsObjectDependent() -PlantSimEngine.TimeStepDependencyTrait(::Type{<:GeneratedStatusVectorModel}) = PlantSimEngine.IsTimeStepDependent() - - -# TODO should the new_status be copied ? -# Note : User specifies at which level they want the basic timestep model to be inserted at, as well as the meteo length -function replace_mapping_status_vectors_with_generated_models(mapping_with_vectors_in_status, timestep_model_organ_level, nsteps) - timestep_model_organ_level = _normalize_scale( - timestep_model_organ_level; - context=:ModelMapping - ) - - (organ, check) = check_statuses_contain_no_remaining_vectors(mapping_with_vectors_in_status) - if check - @warn "No vectors, or types deriving from AbstractVector found in statuses, returning mapping as is." - return mapping_with_vectors_in_status isa ModelMapping ? mapping_with_vectors_in_status : ModelMapping(mapping_with_vectors_in_status) - end - - # we are now certain a model will be generated, and that the timestep models need to be inserted - mapping = Dict( - _normalize_scale(organ; context=:ModelMapping) => models - for (organ, models) in mapping_with_vectors_in_status - ) - for (organ,models) in mapping - for status in models - if isa(status, Status) - # Generate models and remove corresponding vectors from status - new_status, generated_models = generate_model_from_status_vector_variable(mapping, timestep_model_organ_level, status, organ, nsteps) - - # Avoid inserting empty named tuples into the mapping - models_and_new_status = [model for model in models if !isa(model, Status)] - if length(new_status) != 0 - models_and_new_status = [models_and_new_status..., new_status] - end - - # The timestep models might be inserted elsewhere in the mapping, handle various cases - if length(generated_models) > 0 - mapping[organ] = ( - generated_models..., - models_and_new_status...,) - end - end - end - - # insert timestep models wherever they're required - if organ == timestep_model_organ_level - # mapping at a given level can be a tuple or a single model - if isa(mapping[organ], AbstractModel) || isa(mapping[organ], MultiScaleModel) || isa(mapping[organ], ModelSpec) - mapping[organ] = ( - HelperNextTimestepModel(), - MultiScaleModel( - model=HelperCurrentTimestepModel(), - mapped_variables=[PreviousTimeStep(:next_timestep),], - ), - mapping[organ], ) - else - mapping[organ] = ( - HelperNextTimestepModel(), - MultiScaleModel( - model=HelperCurrentTimestepModel(), - mapped_variables=[PreviousTimeStep(:next_timestep),], - ), - mapping[organ]..., ) - end - end - end - - return ModelMapping(mapping) -end - -function generate_model_from_status_vector_variable(mapping, timestep_scale, status, organ, nsteps) - timestep_scale = _normalize_scale(timestep_scale; context=:ModelMapping) - organ = _normalize_scale(organ; context=:ModelMapping) - - # Ah, another point that remains to be seen is that those CSV.SentinelArrays.ChainedVector obtained from the meteo file isn't an AbstractVector - # meaning currently we won't generate models from them unless the conversion is made before that - # So another minor potential improvement would be to return a warning to the user and do the conversion when generating the model - # See the test code in test-mapping.jl : cumsum(meteo_day.TT) returns such a data structure - - generated_models = Any[] - new_status_names = Symbol[] - new_status_values = Any[] - - for symbol in keys(status) - value = getproperty(status, symbol) - if isa(value, AbstractVector) - if length(value) == 0 - error("Error during generation of models from vector values provided at the $organ-level status : provided $symbol vector is empty") - end - # TODO : Might need to fiddle with timesteps here in the future in case of varying timestep models - if nsteps != length(value) - error("Error during generation of models from vector values provided at the $organ-level status : provided $symbol vector length doesn't match the expected # of timesteps") - end - - process_name = Symbol(lowercase(string(symbol) * bytes2hex(sha1(repr(value))))) - model = GeneratedStatusVectorModel(process_name, symbol, value) - - # if :current_timestep is not in the same scale - if timestep_scale != organ - push!( - generated_models, - MultiScaleModel( - model=model, - mapped_variables=[:current_timestep => (timestep_scale => :current_timestep)], - ) - ) - else - push!(generated_models, model) - end - else - push!(new_status_names, symbol) - push!(new_status_values, value) - end - end - - new_status = Status(NamedTuple{Tuple(new_status_names)}(Tuple(new_status_values))) - generated_models_tuple = Tuple(generated_models) - - if length(status) != length(new_status) + length(generated_models_tuple) - error("Error during generation of models from vector values provided at the $organ-level status") - end - return new_status, generated_models_tuple -end - - -# This is a helper function only for testing purposes. -function modellist_to_mapping(modellist_original::SingleScaleModelSet, modellist_status; nsteps=nothing, outputs=nothing) - - modellist = Base.copy(modellist_original, modellist_original.status) - - default_scale = :Default - mtg = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", default_scale, 0, 0),) - - models = modellist.models - - mapping_incomplete = isnothing(modellist_status) ? - ( - Dict( - default_scale => ( - models..., - MultiScaleModel( - model=HelperCurrentTimestepModel(), - mapped_variables=[PreviousTimeStep(:next_timestep),], - ), - Status((current_timestep=1,next_timestep=1,)) - ), - )) : ( - Dict( - default_scale => ( - models..., - MultiScaleModel( - model=HelperCurrentTimestepModel(), - mapped_variables=[PreviousTimeStep(:next_timestep),], - ), - Status((modellist_status..., current_timestep=1,next_timestep=1,)) - ), - ) - ) - timestep_scale = :Default - organ = :Default - - # todo improve on this - st = last(mapping_incomplete[:Default]) - new_status, generated_models = generate_model_from_status_vector_variable(mapping_incomplete, timestep_scale, st, organ, nsteps) - - mapping = Dict(default_scale => ( - models..., generated_models..., - HelperNextTimestepModel(), - MultiScaleModel( - model=HelperCurrentTimestepModel(), - mapped_variables=[PreviousTimeStep(:next_timestep),], - ), - new_status, - ), - ) - - if isnothing(outputs) - f = [] - for i in 1:length(modellist.models) - aa = init_variables(modellist.models[i]) - bb = keys(aa) - for j in 1:length(bb) - push!(f, bb[j]) - end - #f = (f..., bb...) - end - - f = unique!(f) - all_vars = (f...,) - #all_vars = merge((keys(init_variables(object.models[i])) for i in 1:length(object.models))...) - else - all_vars = outputs - # TODO sanity check - end - - return mtg, ModelMapping(mapping), Dict(default_scale => all_vars) -end - -function modellist_to_mapping(mapping::ModelMapping{SingleScale}, modellist_status; nsteps=nothing, outputs=nothing) - modellist_to_mapping(mapping.data, modellist_status; nsteps=nsteps, outputs=outputs) -end - -function check_statuses_contain_no_remaining_vectors(mapping) - for (organ,models) in mapping - - # Special case (scales that map to a single-model don't need to be declared as a tuple for user-convenience) - if isa(models, AbstractModel) || isa(models, MultiScaleModel) || isa(models, ModelSpec) - continue - end - - for status in models - if isa(status, Status) - for symbol in keys(status) - value = getproperty(status, symbol) - if isa(value, AbstractVector) - return (organ, false) - end - end - end - end - end - return (nothing, true) -end diff --git a/src/mtg/mapping/reverse_mapping.jl b/src/mtg/mapping/reverse_mapping.jl deleted file mode 100644 index 3e9ecfe08..000000000 --- a/src/mtg/mapping/reverse_mapping.jl +++ /dev/null @@ -1,109 +0,0 @@ -""" - reverse_mapping(mapping::ModelMapping; all=true) - reverse_mapping(mapping::AbstractDict{Symbol,Tuple{Any,Vararg{Any}}}; all=true) - reverse_mapping(mapped_vars::Dict{Symbol,Dict{Symbol,Any}}) - -Get the reverse mapping of a dictionary of model mapping, *i.e.* the variables that are mapped to other scales, or in other words, -what variables are given to other scales from a given scale. -This is used for *e.g.* knowing which scales are needed to add values to others. - -# Arguments - -- `mapping::ModelMapping` (or dictionary-like mapping): the model mapping. -- `all::Bool`: Whether to get all the variables that are mapped to other scales, including the ones that are mapped as single values. - -# Returns - -A dictionary of organs (keys) with a dictionary of organs => vector of pair of variables. You can read the output as: -"for each organ (source organ), to which other organ (target organ) it is giving values for its own variables. Then for each of these source organs, which variable it is -giving to the target organ (first symbol in the pair), and to which variable it is mapping the value into the target organ (second symbol in the pair)". - -# Examples - -```jldoctest mylabel -julia> using PlantSimEngine -``` - -Import example models (can be found in the `examples` folder of the package, or in the `Examples` sub-modules): - -```jldoctest mylabel -julia> using PlantSimEngine.Examples; -``` - -```jldoctest mylabel -julia> mapping = PlantSimEngine.ModelMapping( \ - :Plant => \ - PlantSimEngine.MultiScaleModel( \ - model=ToyCAllocationModel(), \ - mapped_variables=[ \ - :carbon_assimilation => [:Leaf], \ - :carbon_demand => [:Leaf, :Internode], \ - :carbon_allocation => [:Leaf, :Internode] \ - ], \ - ), \ - :Internode => ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), \ - :Leaf => ( \ - PlantSimEngine.MultiScaleModel( \ - model=ToyAssimModel(), \ - mapped_variables=[:soil_water_content => :Soil => :soil_water_content,], \ - ), \ - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), \ - Status(aPPFD=1300.0, TT=10.0), \ - ), \ - :Soil => ( \ - ToySoilWaterModel(), \ - ), \ - ); -``` - -Notice we provide `:Soil`, not `[:Soil]` in the mapping of the `ToyAssimModel` for the `Leaf`. This is because -we expect a single value for the `soil_water_content` to be mapped here (there is only one soil). This allows -to get the value as a singleton instead of a vector of values. - -```jldoctest mylabel -julia> rm = PlantSimEngine.reverse_mapping(mapping); - -julia> Set(keys(rm)) == Set([:Soil, :Internode, :Leaf]) -true - -julia> rm[:Soil][:Leaf][:soil_water_content] == :soil_water_content -true -``` -""" -function reverse_mapping(mapping::AbstractDict{Symbol,T}; all=true) where {T<:Any} - # Method for the reverse mapping applied directly on the mapping (not used in the code base) - mapped_vars = mapped_variables(mapping, first(hard_dependencies(mapping; verbose=false)), verbose=false) - reverse_mapping(mapped_vars, all=all) -end - -function reverse_mapping(mapped_vars::Dict{Symbol,Dict{Symbol,Any}}; all=true) - reverse_multiscale_mapping = ReverseMultiscaleMapping(org => Dict{Symbol,Dict{Symbol,ReverseMappingTarget}}() for org in keys(mapped_vars)) - for (organ, vars) in mapped_vars # e.g.: organ = :Plant; vars = mapped_vars[organ] - for (var, val) in vars # e.g. var = :Rm_organs; val = vars[var] - if isa(val, MappedVar) && !isa(val, MappedVar{SelfNodeMapping}) && (all || !isa(val, MappedVar{SingleNodeMapping})) - # Note: We skip the MappedVar{SelfNodeMapping} because it is a special case where the variable is mapped to itself - # and we don't want to add it to the reverse mapping. We also skip the MappedVar{SingleNodeMapping} if `all=false` - # because we don't want to add the variables that are mapped as single values to the reverse mapping. - - mapped_orgs = mapped_organ(val) - isnothing(mapped_orgs) && continue - if mapped_orgs isa Symbol - mapped_orgs = [mapped_orgs] - elseif mapped_orgs isa AbstractString - error("String scale names are removed. Use Symbol scales, e.g. `:Leaf` instead of `\"Leaf\"`.") - end - - for mapped_o in mapped_orgs # e.g.: mapped_o = :Leaf - # if !haskey(reverse_multiscale_mapping, mapped_o) - # reverse_multiscale_mapping[mapped_o] = Dict{Symbol,Vector{MappedVar}}() - # end - if !haskey(reverse_multiscale_mapping[mapped_o], organ) - reverse_multiscale_mapping[mapped_o][organ] = Dict{Symbol,ReverseMappingTarget}(source_variable(val, mapped_o) => mapped_variable(val)) - end - push!(reverse_multiscale_mapping[mapped_o][organ], source_variable(val, mapped_o) => mapped_variable(val)) - end - end - end - end - filter!(x -> length(last(x)) > 0, reverse_multiscale_mapping) -end diff --git a/src/mtg/model_spec_inference.jl b/src/mtg/model_spec_inference.jl deleted file mode 100644 index 06de5e18a..000000000 --- a/src/mtg/model_spec_inference.jl +++ /dev/null @@ -1,951 +0,0 @@ -const _TIMESTEP_HINT_FIELDS = (:required, :preferred) - -""" - timestep_hint(model::AbstractModel) - timestep_hint(::Type{<:AbstractModel}) - -Optional model trait used to declare runtime compatibility constraints when -`ModelSpec.timestep` is not provided. - -Supported return values: -- `nothing` (default): no hint -- `Dates.FixedPeriod`: fixed required timestep -- `(min_period, max_period)`: required timestep range (`Dates.FixedPeriod` pair) -- `NamedTuple`: with `required` (one of the forms above) and optional `preferred` - (`:finest`, `:coarsest`, or a `Dates.FixedPeriod` within the required range). - `preferred` is informational only when runtime derives timestep from meteo. -""" -timestep_hint(model::AbstractModel) = timestep_hint(typeof(model)) -timestep_hint(::Type{<:AbstractModel}) = nothing - -""" - meteo_hint(model::AbstractModel) - meteo_hint(::Type{<:AbstractModel}) - -Optional model trait used to infer weather sampling when `ModelSpec` does not provide -`MeteoBindings(...)` and/or `MeteoWindow(...)`. - -Expected return value is a `NamedTuple` with optional fields: -- `bindings`: compatible with `MeteoBindings(...)` -- `window`: compatible with `MeteoWindow(...)` -""" -meteo_hint(model::AbstractModel) = meteo_hint(typeof(model)) -meteo_hint(::Type{<:AbstractModel}) = nothing - -struct _ResolvedTimeStepHint - fixed::Union{Nothing,Dates.FixedPeriod} - range::Union{Nothing,Tuple{Dates.FixedPeriod,Dates.FixedPeriod}} - preferred::Union{Nothing,Symbol,Dates.FixedPeriod} -end - -_seconds_from_period(p::Dates.FixedPeriod) = float(Dates.value(Dates.Millisecond(p))) * 1.0e-3 - -function _normalize_required_timestep_hint(scale::Symbol, process::Symbol, required) - if required isa Dates.FixedPeriod - _seconds_from_period(required) > 0.0 || error( - "Invalid `timestep_hint` required period for process `$(process)` at scale `$(scale)`: ", - "period must be > 0, got `$(required)`." - ) - return required, nothing - elseif required isa Tuple - length(required) == 2 || error( - "Invalid `timestep_hint` required tuple for process `$(process)` at scale `$(scale)`: ", - "expected `(min_period, max_period)`." - ) - minp, maxp = required - minp isa Dates.FixedPeriod || error( - "Invalid `timestep_hint` min period for process `$(process)` at scale `$(scale)`: ", - "expected `Dates.FixedPeriod`, got `$(typeof(minp))`." - ) - maxp isa Dates.FixedPeriod || error( - "Invalid `timestep_hint` max period for process `$(process)` at scale `$(scale)`: ", - "expected `Dates.FixedPeriod`, got `$(typeof(maxp))`." - ) - min_sec = _seconds_from_period(minp) - max_sec = _seconds_from_period(maxp) - min_sec > 0.0 || error( - "Invalid `timestep_hint` range lower bound for process `$(process)` at scale `$(scale)`: ", - "period must be > 0, got `$(minp)`." - ) - max_sec > 0.0 || error( - "Invalid `timestep_hint` range upper bound for process `$(process)` at scale `$(scale)`: ", - "period must be > 0, got `$(maxp)`." - ) - min_sec <= max_sec || error( - "Invalid `timestep_hint` range for process `$(process)` at scale `$(scale)`: ", - "lower bound `$(minp)` must be <= upper bound `$(maxp)`." - ) - return nothing, (minp, maxp) - end - - error( - "Invalid `timestep_hint` required value for process `$(process)` at scale `$(scale)`: ", - "expected `Dates.FixedPeriod` or `(Dates.FixedPeriod, Dates.FixedPeriod)`, got `$(typeof(required))`." - ) -end - -function _normalize_timestep_hint(scale::Symbol, process::Symbol, hint) - isnothing(hint) && return _ResolvedTimeStepHint(nothing, nothing, nothing) - - if hint isa Dates.FixedPeriod || hint isa Tuple - fixed, range = _normalize_required_timestep_hint(scale, process, hint) - return _ResolvedTimeStepHint(fixed, range, nothing) - elseif hint isa NamedTuple - extra = setdiff(collect(keys(hint)), collect(_TIMESTEP_HINT_FIELDS)) - isempty(extra) || error( - "Invalid `timestep_hint` for process `$(process)` at scale `$(scale)`: ", - "unsupported fields $(extra)." - ) - haskey(hint, :required) || error( - "Invalid `timestep_hint` for process `$(process)` at scale `$(scale)`: ", - "field `required` is mandatory when using NamedTuple form." - ) - fixed, range = _normalize_required_timestep_hint(scale, process, hint.required) - preferred = haskey(hint, :preferred) ? hint.preferred : nothing - if !isnothing(preferred) - if preferred isa Symbol - preferred in (:finest, :coarsest) || error( - "Invalid `timestep_hint.preferred` for process `$(process)` at scale `$(scale)`: ", - "supported symbols are `:finest` and `:coarsest`." - ) - elseif preferred isa Dates.FixedPeriod - _seconds_from_period(preferred) > 0.0 || error( - "Invalid `timestep_hint.preferred` for process `$(process)` at scale `$(scale)`: ", - "period must be > 0, got `$(preferred)`." - ) - if !isnothing(range) - lo, hi = range - preferred_sec = _seconds_from_period(preferred) - lo_sec = _seconds_from_period(lo) - hi_sec = _seconds_from_period(hi) - lo_sec <= preferred_sec <= hi_sec || error( - "Invalid `timestep_hint.preferred=$(preferred)` for process `$(process)` at scale `$(scale)`: ", - "preferred period must be inside required range `($(lo), $(hi))`." - ) - elseif !isnothing(fixed) - _seconds_from_period(preferred) == _seconds_from_period(fixed) || error( - "Invalid `timestep_hint.preferred=$(preferred)` for process `$(process)` at scale `$(scale)`: ", - "when `required` is fixed (`$(fixed)`), `preferred` must match it." - ) - end - else - error( - "Invalid `timestep_hint.preferred` for process `$(process)` at scale `$(scale)`: ", - "expected `:finest`, `:coarsest`, or `Dates.FixedPeriod`, got `$(typeof(preferred))`." - ) - end - end - return _ResolvedTimeStepHint(fixed, range, preferred) - end - - error( - "Invalid `timestep_hint` for process `$(process)` at scale `$(scale)`: ", - "expected `nothing`, `Dates.FixedPeriod`, `(min,max)` tuple, or NamedTuple, got `$(typeof(hint))`." - ) -end - -function _infer_timestep_hints!(model_specs) - # `timestep_hint` is parsed/validated here but does not assign runtime dt. - # Runtime derives dt from: - # 1) explicit `ModelSpec.timestep` - # 2) model `timespec(model)` when non-default - # 3) meteo base step (default fallback) - for (scale, specs_at_scale) in pairs(model_specs) - for (process, spec) in pairs(specs_at_scale) - isnothing(timestep(spec)) || continue - _normalize_timestep_hint(scale, process, timestep_hint(model_(spec))) - end - end - - return nothing -end - -function _format_candidate_list(candidates) - isempty(candidates) && return "(none)" - return join(["$(c.scale)/$(c.process)" for c in candidates], ", ") -end - -function _is_stream_only_output(spec::ModelSpec, var::Symbol) - routing = output_routing(spec) - mode = var in keys(routing) ? routing[var] : :canonical - return mode == :stream_only -end - -function _scale_reachable(scale_reachability, consumer_scale::Symbol, source_scale::Symbol) - isnothing(scale_reachability) && return true - # If one of the scales is not present in the initial MTG, reachability is - # unknown at init time: keep candidate permissively. - haskey(scale_reachability, consumer_scale) || return true - haskey(scale_reachability, source_scale) || return true - allowed = scale_reachability[consumer_scale] - return source_scale in allowed -end - -function _scale_reachability_from_mtg(mtg) - scale_reachability = Dict{Symbol,Set{Symbol}}() - - MultiScaleTreeGraph.traverse!(mtg) do node - scale = symbol(node) - push!(get!(scale_reachability, scale, Set{Symbol}()), scale) - - ancestor = parent(node) - while !isnothing(ancestor) - ancestor_scale = symbol(ancestor) - # Scales are reachable only when they appear on the same MTG lineage - # (ancestor/descendant relation). Sibling-only scales are excluded. - push!(get!(scale_reachability, scale, Set{Symbol}()), ancestor_scale) - push!(get!(scale_reachability, ancestor_scale, Set{Symbol}()), scale) - ancestor = parent(ancestor) - end - end - - return scale_reachability -end - -function _effective_timestep_spec(spec::ModelSpec) - ts = timestep(spec) - return isnothing(ts) ? timespec(model_(spec)) : ts -end - -function _timestep_resolution_source(spec::ModelSpec) - !isnothing(timestep(spec)) && return :modelspec - return _same_timestep_signature( - _timestep_signature(timespec(model_(spec))), - _timestep_signature(ClockSpec(1.0, 0.0)) - ) ? :meteo_base_step : :model_timespec -end - -function _timestep_signature(ts) - if ts isa ClockSpec - return (:clock, float(ts.dt), float(ts.phase)) - elseif ts isa Real - return (:step, float(ts), 0.0) - elseif ts isa Dates.FixedPeriod - return (:period, _seconds_from_period(ts), 0.0) - end - return nothing -end - -function _same_timestep_signature(sig_a, sig_b) - isnothing(sig_a) && return false - isnothing(sig_b) && return false - - if sig_a[1] == :period || sig_b[1] == :period - return sig_a[1] == :period && - sig_b[1] == :period && - isapprox(sig_a[2], sig_b[2]; atol=1.0e-9, rtol=0.0) - end - - phase_a = sig_a[1] == :step ? 0.0 : sig_a[3] - phase_b = sig_b[1] == :step ? 0.0 : sig_b[3] - return isapprox(sig_a[2], sig_b[2]; atol=1.0e-9, rtol=0.0) && - isapprox(phase_a, phase_b; atol=1.0e-9, rtol=0.0) -end - -function _hard_dep_same_rate_as_parent(model_specs, parent_scale::Symbol, parent_process::Symbol, child_scale::Symbol, child_process::Symbol) - parent_specs = get(model_specs, parent_scale, nothing) - child_specs = get(model_specs, child_scale, nothing) - isnothing(parent_specs) && return false - isnothing(child_specs) && return false - parent_spec = get(parent_specs, parent_process, nothing) - child_spec = get(child_specs, child_process, nothing) - isnothing(parent_spec) && return false - isnothing(child_spec) && return false - - parent_sig = _timestep_signature(_effective_timestep_spec(parent_spec)) - child_sig = _timestep_signature(_effective_timestep_spec(child_spec)) - return _same_timestep_signature(parent_sig, child_sig) -end - -function _collect_same_rate_hard_dependency_children!( - ignored_processes_by_scale::Dict{Symbol,Set{Symbol}}, - model_specs, - parent_scale::Symbol, - parent_process::Symbol, - child::HardDependencyNode -) - if _hard_dep_same_rate_as_parent(model_specs, parent_scale, parent_process, child.scale, child.process) - push!(get!(ignored_processes_by_scale, child.scale, Set{Symbol}()), child.process) - end - - for nested in child.children - _collect_same_rate_hard_dependency_children!( - ignored_processes_by_scale, - model_specs, - child.scale, - child.process, - nested - ) - end - - return nothing -end - -function _collect_hard_dependency_children!( - ignored_processes_by_scale::Dict{Symbol,Set{Symbol}}, - child::HardDependencyNode -) - push!(get!(ignored_processes_by_scale, child.scale, Set{Symbol}()), child.process) - for nested in child.children - _collect_hard_dependency_children!(ignored_processes_by_scale, nested) - end - return nothing -end - -function _validate_hard_dependency_timestep_consistency!( - model_specs, - parent_scale::Symbol, - parent_process::Symbol, - child::HardDependencyNode -) - _hard_dep_same_rate_as_parent(model_specs, parent_scale, parent_process, child.scale, child.process) || error( - "Hard dependency `$(child.process)` at scale `$(child.scale)` has a different timestep than parent process ", - "`$(parent_process)` at scale `$(parent_scale)`. Hard dependencies are called manually by their parent, ", - "so their `ModelSpec` timestep cannot be scheduled independently. Align the timesteps, or make the child a soft dependency." - ) - - for nested in child.children - _validate_hard_dependency_timestep_consistency!( - model_specs, - child.scale, - child.process, - nested - ) - end - - return nothing -end - -function _soft_nodes_for_hard_dependency_analysis(dep_graph::DependencyGraph{Dict{Symbol,Any}}) - nodes = SoftDependencyNode[] - for (_, roots_at_scale) in pairs(dep_graph.roots) - haskey(roots_at_scale, :soft_dep_graph) || continue - append!(nodes, values(roots_at_scale[:soft_dep_graph])) - end - return nodes -end - -_soft_nodes_for_hard_dependency_analysis(dep_graph::DependencyGraph) = traverse_dependency_graph(dep_graph, false) - -function _same_rate_hard_dependency_children(model_specs, dep_graph::DependencyGraph) - ignored_processes_by_scale = Dict{Symbol,Set{Symbol}}() - - for soft_node in _soft_nodes_for_hard_dependency_analysis(dep_graph) - for child in soft_node.hard_dependency - _collect_same_rate_hard_dependency_children!( - ignored_processes_by_scale, - model_specs, - soft_node.scale, - soft_node.process, - child - ) - end - end - - return ignored_processes_by_scale -end - -function _hard_dependency_children(dep_graph::DependencyGraph) - ignored_processes_by_scale = Dict{Symbol,Set{Symbol}}() - - for soft_node in _soft_nodes_for_hard_dependency_analysis(dep_graph) - for child in soft_node.hard_dependency - _collect_hard_dependency_children!(ignored_processes_by_scale, child) - end - end - - return ignored_processes_by_scale -end - -function validate_hard_dependency_timestep_consistency(model_specs, dep_graph::DependencyGraph) - for soft_node in _soft_nodes_for_hard_dependency_analysis(dep_graph) - for child in soft_node.hard_dependency - _validate_hard_dependency_timestep_consistency!( - model_specs, - soft_node.scale, - soft_node.process, - child - ) - end - end - - return nothing -end - -function _active_processes_for_inference(model_specs, ignored_processes_by_scale::Dict{Symbol,Set{Symbol}}) - active = Dict{Symbol,Set{Symbol}}() - for (scale, specs_at_scale) in pairs(model_specs) - procs = Set{Symbol}(keys(specs_at_scale)) - ignored = get(ignored_processes_by_scale, scale, Set{Symbol}()) - for process in ignored - delete!(procs, process) - end - active[scale] = procs - end - return active -end - -function _input_candidates_for_var( - model_specs, - consumer_scale::Symbol, - consumer_process::Symbol, - input_var::Symbol; - scale_reachability=nothing, - active_processes_by_scale=nothing -) - same_scale = NamedTuple[] - cross_scale = NamedTuple[] - - for (scale, specs_at_scale) in pairs(model_specs) - for (process, spec) in pairs(specs_at_scale) - if !isnothing(active_processes_by_scale) - active = get(active_processes_by_scale, scale, Set{Symbol}()) - process in active || continue - end - scale == consumer_scale && process == consumer_process && continue - input_var in keys(outputs_(model_(spec))) || continue - _is_stream_only_output(spec, input_var) && continue - if scale != consumer_scale && !_scale_reachable(scale_reachability, consumer_scale, scale) - continue - end - c = (scale=scale, process=process, var=input_var) - if scale == consumer_scale - push!(same_scale, c) - else - push!(cross_scale, c) - end - end - end - - return same_scale, cross_scale -end - -function _default_policy_for_inferred_binding(model_specs, source_scale::Symbol, source_process::Symbol, source_var::Symbol) - source_spec = model_specs[source_scale][source_process] - source_model = model_(source_spec) - source_output_policy = output_policy(source_model) - source_var in keys(source_output_policy) || return HoldLast() - return _as_schedule_policy( - source_output_policy[source_var]; - context="output_policy for inferred binding from `$(source_scale)/$(source_process).$(source_var)`" - ) -end - -function _terminal_update_candidate(model_specs, candidates::Vector, var::Symbol) - length(candidates) > 1 || return nothing - - terminal_updates = NamedTuple[] - for candidate in candidates - candidate_spec = model_specs[candidate.scale][candidate.process] - var in _update_variables_for_spec(candidate_spec) || continue - has_later_update = any(candidates) do other - other.scale == candidate.scale || return false - other.process == candidate.process && return false - other.var == var || return false - other_spec = model_specs[other.scale][other.process] - var in _update_variables_for_spec(other_spec) || return false - return candidate.process in _update_after_for_var(other_spec, var) - end - has_later_update || push!(terminal_updates, candidate) - end - - length(terminal_updates) == 1 || return nothing - return only(terminal_updates) -end - -function _mapped_source_scales_for_input(spec::ModelSpec, input_var::Symbol) - mapped = mapped_variables_(spec) - isempty(mapped) && return Set{Symbol}() - - scales = Set{Symbol}() - for mv in mapped - mapped_input = first(mv) - mapped_input = mapped_input isa PreviousTimeStep ? mapped_input.variable : mapped_input - mapped_input == input_var || continue - - rhs = last(mv) - if rhs isa Pair - src_scale = first(rhs) - src_scale isa SameScale || push!(scales, src_scale) - elseif rhs isa AbstractVector - for item in rhs - item isa Pair || continue - src_scale = first(item) - src_scale isa SameScale || push!(scales, src_scale) - end - end - end - - return scales -end - -function _input_has_multiscale_mapping(spec::ModelSpec, input_var::Symbol) - mapped = mapped_variables_(spec) - isempty(mapped) && return false - - for mv in mapped - mapped_input = first(mv) - mapped_input = mapped_input isa PreviousTimeStep ? mapped_input.variable : mapped_input - mapped_input == input_var && return true - end - - return false -end - -function _mapped_sources_for_input(spec::ModelSpec, input_var::Symbol) - mapped = mapped_variables_(spec) - isempty(mapped) && return Pair{Symbol,Symbol}[] - - sources = Pair{Union{Symbol,SameScale},Symbol}[] - for mv in mapped - mapped_input = first(mv) - mapped_input = mapped_input isa PreviousTimeStep ? mapped_input.variable : mapped_input - mapped_input == input_var || continue - - rhs = last(mv) - if rhs isa Pair - push!(sources, rhs) - elseif rhs isa AbstractVector - for item in rhs - item isa Pair || continue - push!(sources, item) - end - end - end - - return sources -end - -function _infer_binding_from_multiscale_mapping( - model_specs, - scale::Symbol, - process::Symbol, - spec::ModelSpec, - input_var::Symbol; - active_processes_by_scale=nothing -) - has_mapping = _input_has_multiscale_mapping(spec, input_var) - has_mapping || return nothing - - mapped_sources = _mapped_sources_for_input(spec, input_var) - # Mapping exists but does not point to another scale (self/same-scale aliasing): - # avoid generic same-name inference in that case. - filtered_sources = filter(s -> !(first(s) isa SameScale), mapped_sources) - isempty(filtered_sources) && return :skip - - # Multi-source mapping (e.g. vectors from several scales) cannot be represented - # as one `InputBindings` entry; keep binding unresolved and skip generic inference. - length(filtered_sources) == 1 || return :skip - - src = only(filtered_sources) - src_scale = first(src) - src_var = last(src) - haskey(model_specs, src_scale) || return :skip - - candidates = NamedTuple[] - for (src_process, src_spec) in pairs(model_specs[src_scale]) - if !isnothing(active_processes_by_scale) - active = get(active_processes_by_scale, src_scale, Set{Symbol}()) - src_process in active || continue - end - src_var in keys(outputs_(model_(src_spec))) || continue - _is_stream_only_output(src_spec, src_var) && continue - push!(candidates, (scale=src_scale, process=src_process, var=src_var)) - end - - if length(candidates) == 1 - candidate = only(candidates) - else - candidate = _terminal_update_candidate(model_specs, candidates, src_var) - isnothing(candidate) && return :skip - end - - src_process = candidate.process - policy = _default_policy_for_inferred_binding(model_specs, src_scale, src_process, src_var) - return (process=src_process, var=src_var, scale=src_scale, policy=policy) -end - -function _infer_input_binding_for_var( - model_specs, - scale::Symbol, - process::Symbol, - input_var::Symbol; - scale_reachability=nothing, - active_processes_by_scale=nothing -) - same_scale, cross_scale = _input_candidates_for_var( - model_specs, - scale, - process, - input_var; - scale_reachability=scale_reachability, - active_processes_by_scale=active_processes_by_scale - ) - - if length(same_scale) == 1 - c = only(same_scale) - policy = _default_policy_for_inferred_binding(model_specs, c.scale, c.process, c.var) - return (process=c.process, var=c.var, scale=c.scale, policy=policy) - elseif length(same_scale) > 1 - terminal_update = _terminal_update_candidate(model_specs, same_scale, input_var) - if !isnothing(terminal_update) - policy = _default_policy_for_inferred_binding( - model_specs, - terminal_update.scale, - terminal_update.process, - terminal_update.var, - ) - return ( - process=terminal_update.process, - var=terminal_update.var, - scale=terminal_update.scale, - policy=policy, - ) - end - error( - "Ambiguous inferred producer for input `$(input_var)` in process `$(process)` at scale `$(scale)`. ", - "Multiple same-scale candidates were found: $(_format_candidate_list(same_scale)). ", - "Please provide explicit `InputBindings(...)`." - ) - end - - if length(cross_scale) == 1 - c = only(cross_scale) - policy = _default_policy_for_inferred_binding(model_specs, c.scale, c.process, c.var) - return (process=c.process, var=c.var, scale=c.scale, policy=policy) - elseif length(cross_scale) > 1 - by_process = Dict{Symbol,Vector{NamedTuple}}() - for c in cross_scale - push!(get!(by_process, c.process, NamedTuple[]), c) - end - - if length(by_process) == 1 - proc = only(keys(by_process)) - scales = unique(c.scale for c in by_process[proc]) - if length(scales) == 1 - src_scale = only(scales) - policy = _default_policy_for_inferred_binding(model_specs, src_scale, proc, input_var) - return (process=proc, var=input_var, scale=src_scale, policy=policy) - end - - # When multiscale mapping already declares a source scale for this - # input, use it to disambiguate instead of forcing explicit bindings. - consumer_spec = model_specs[scale][process] - mapped_scales = _mapped_source_scales_for_input(consumer_spec, input_var) - candidate_scales = Set(scales) - hinted_scales = intersect(mapped_scales, candidate_scales) - if length(hinted_scales) == 1 - src_scale = only(hinted_scales) - policy = _default_policy_for_inferred_binding(model_specs, src_scale, proc, input_var) - return (process=proc, var=input_var, scale=src_scale, policy=policy) - end - - error( - "Ambiguous inferred producer for input `$(input_var)` in process `$(process)` at scale `$(scale)`. ", - "Process `$(proc)` publishes this variable at multiple reachable scales: $(join(scales, ", ")). ", - "Please provide explicit `InputBindings(...)` with `scale`, ", - "or add a `MultiScaleModel(...)` mapping so the source scale is unambiguous." - ) - end - - error( - "Ambiguous inferred producer for input `$(input_var)` in process `$(process)` at scale `$(scale)`. ", - "Multiple cross-scale candidates were found: $(_format_candidate_list(cross_scale)). ", - "Please provide explicit `InputBindings(...)`." - ) - end - - # No producer found. Keep input unresolved so user-provided initialization/forced - # values can still drive the model. - return nothing -end - -function _infer_input_bindings!(model_specs; scale_reachability=nothing, active_processes_by_scale=nothing) - for (scale, specs_at_scale) in pairs(model_specs) - # When a scale is absent from the initial MTG, input producer inference at - # init time is unreliable (dynamic growth may introduce it later). Keep - # bindings unresolved and let runtime resolve from actual dependencies. - if !isnothing(scale_reachability) && !haskey(scale_reachability, scale) - continue - end - for (process, spec) in pairs(specs_at_scale) - if !isnothing(active_processes_by_scale) - active = get(active_processes_by_scale, scale, Set{Symbol}()) - process in active || continue - end - current_bindings = input_bindings(spec) - current_bindings isa NamedTuple || continue - - inferred = Pair{Symbol,Any}[] - model_inputs = keys(inputs_(model_(spec))) - - for input_var in model_inputs - input_var in keys(current_bindings) && continue - mapped_binding = _infer_binding_from_multiscale_mapping( - model_specs, - scale, - process, - spec, - input_var; - active_processes_by_scale=active_processes_by_scale - ) - if mapped_binding === :skip - continue - elseif !isnothing(mapped_binding) - push!(inferred, input_var => mapped_binding) - continue - end - inferred_binding = _infer_input_binding_for_var( - model_specs, - scale, - process, - input_var; - scale_reachability=scale_reachability, - active_processes_by_scale=active_processes_by_scale - ) - isnothing(inferred_binding) && continue - push!(inferred, input_var => inferred_binding) - end - - isempty(inferred) && continue - merged = (; pairs(current_bindings)..., inferred...) - specs_at_scale[process] = ModelSpec(spec; input_bindings=merged) - end - end - - return nothing -end - -function _normalize_meteo_hint(scale::Symbol, process::Symbol, hint) - isnothing(hint) && return (bindings=nothing, window=nothing) - - hint isa NamedTuple || error( - "Invalid `meteo_hint` for process `$(process)` at scale `$(scale)`: ", - "expected NamedTuple with optional fields `bindings` and `window`, got `$(typeof(hint))`." - ) - - allowed = (:bindings, :window) - extra = setdiff(collect(keys(hint)), collect(allowed)) - isempty(extra) || error( - "Invalid `meteo_hint` for process `$(process)` at scale `$(scale)`: ", - "unsupported fields $(extra)." - ) - - bindings = haskey(hint, :bindings) ? _normalize_meteo_bindings(hint.bindings) : nothing - window = haskey(hint, :window) ? _normalize_meteo_window(hint.window) : nothing - return (bindings=bindings, window=window) -end - -function _infer_meteo_hints!(model_specs) - for (scale, specs_at_scale) in pairs(model_specs) - for (process, spec) in pairs(specs_at_scale) - hint = _normalize_meteo_hint(scale, process, meteo_hint(model_(spec))) - - current_bindings = meteo_bindings(spec) - has_explicit_bindings = !(current_bindings isa NamedTuple && isempty(keys(current_bindings))) - new_bindings = has_explicit_bindings ? current_bindings : (isnothing(hint.bindings) ? current_bindings : hint.bindings) - - current_window = meteo_window(spec) - new_window = isnothing(current_window) ? (isnothing(hint.window) ? current_window : hint.window) : current_window - - if (new_bindings !== current_bindings) || (new_window !== current_window) - specs_at_scale[process] = ModelSpec(spec; meteo_bindings=new_bindings, meteo_window=new_window) - end - end - end - - return nothing -end - -""" - infer_model_specs_configuration!(model_specs) - -Fill missing `ModelSpec` fields from inference: -- auto input bindings from unique same-name producers - (including default policy from producer `output_policy`) -- model-level hint traits (`timestep_hint`, `meteo_hint`) -Explicit `ModelSpec` user values always take precedence over inferred values. -""" -function infer_model_specs_configuration!(model_specs; scale_reachability=nothing, active_processes_by_scale=nothing) - _infer_input_bindings!( - model_specs; - scale_reachability=scale_reachability, - active_processes_by_scale=active_processes_by_scale - ) - _infer_timestep_hints!(model_specs) - _infer_meteo_hints!(model_specs) - return model_specs -end - -""" - resolved_model_specs(mapping; infer=true, validate=true) - resolved_model_specs(sim::GraphSimulation) - -Return process-indexed `ModelSpec` dictionaries as used by runtime: -`Dict{Symbol, Dict{Symbol, ModelSpec}}`. - -For a mapping, this parses model declarations and optionally applies inference -(`timestep_hint`, `meteo_hint`) and validation. -For a `GraphSimulation`, this returns the already resolved model specs used by the simulation. -""" -function resolved_model_specs(mapping::AbstractDict; infer::Bool=true, validate::Bool=true) - model_specs = Dict{Symbol,Dict{Symbol,ModelSpec}}() - for (scale, declarations) in pairs(mapping) - scale_sym = scale isa Symbol ? - scale : - error("Scale keys in `resolved_model_specs(mapping)` must be `Symbol`s, got `$(typeof(scale))`.") - model_specs[scale_sym] = parse_model_specs(declarations) - end - - infer && infer_model_specs_configuration!(model_specs) - if validate - hard_dep_roots = try - first(hard_dependencies(mapping; verbose=false)) - catch - nothing - end - ignored_hard_children = if isnothing(hard_dep_roots) - Dict{Symbol,Set{Symbol}}() - else - validate_hard_dependency_timestep_consistency(model_specs, hard_dep_roots) - _hard_dependency_children(hard_dep_roots) - end - validate_model_specs_configuration(model_specs; ignored_processes_by_scale=ignored_hard_children) - end - return model_specs -end - -resolved_model_specs(sim::GraphSimulation; infer::Bool=true, validate::Bool=true) = get_model_specs(sim) - -function _stringify_compact(x; maxlen::Int=120) - s = sprint(show, x) - return ncodeunits(s) <= maxlen ? s : string(first(s, maxlen - 3), "...") -end - -function _model_specs_rows(model_specs) - rows = NamedTuple[] - for scale in sort!(collect(keys(model_specs))) - specs_at_scale = model_specs[scale] - for process in sort!(collect(keys(specs_at_scale)); by=string) - spec = specs_at_scale[process] - resolution = _timestep_resolution_source(spec) - push!(rows, ( - scale=scale, - process=process, - model=typeof(model_(spec)), - application_name=application_name(spec), - applies_to=applies_to(spec), - value_inputs=value_inputs(spec), - input_origins=input_origins(spec), - model_calls=model_calls(spec), - call_origins=call_origins(spec), - environment=environment_config(spec), - timestep=timestep(spec), - timespec_default=timespec(model_(spec)), - timestep_resolution=resolution, - input_bindings=input_bindings(spec), - meteo_bindings=meteo_bindings(spec), - meteo_window=meteo_window(spec), - meteo_inputs=meteo_inputs_(spec), - meteo_outputs=meteo_outputs_(spec), - updates=updates(spec), - )) - end - end - return rows -end - -""" - explain_model_specs(target; io=stdout, infer=true, validate=true) - -Print a compact per-model summary of resolved runtime configuration and return it -as a vector of named tuples. - -Summary fields: -- `scale` -- `process` -- `model` -- `application_name` -- `applies_to` -- `value_inputs` -- `model_calls` -- `environment` -- `timestep` -- `input_bindings` -- `meteo_bindings` -- `meteo_window` -- `meteo_inputs` -- `meteo_outputs` -- `updates` -""" -function explain_model_specs(target; io::IO=stdout, infer::Bool=true, validate::Bool=true) - specs = target isa GraphSimulation ? resolved_model_specs(target) : resolved_model_specs(target; infer=infer, validate=validate) - rows = _model_specs_rows(specs) - - println(io, "Resolved model specs:") - if isempty(rows) - println(io, " (no model specs)") - return rows - end - - for row in rows - timestep_desc = if row.timestep_resolution == :modelspec - string(_stringify_compact(row.timestep), " [explicit ModelSpec]") - elseif row.timestep_resolution == :model_timespec - string(_stringify_compact(row.timespec_default), " [model timespec]") - else - "(meteo base step at runtime)" - end - input_bindings_desc = (row.input_bindings isa NamedTuple && isempty(keys(row.input_bindings))) ? "(none)" : _stringify_compact(row.input_bindings) - meteo_bindings_desc = (row.meteo_bindings isa NamedTuple && isempty(keys(row.meteo_bindings))) ? "(none)" : _stringify_compact(row.meteo_bindings) - meteo_window_desc = isnothing(row.meteo_window) ? "(default rolling)" : _stringify_compact(row.meteo_window) - meteo_inputs_desc = (row.meteo_inputs isa NamedTuple && isempty(keys(row.meteo_inputs))) ? "(none)" : _stringify_compact(row.meteo_inputs) - meteo_outputs_desc = (row.meteo_outputs isa NamedTuple && isempty(keys(row.meteo_outputs))) ? "(none)" : _stringify_compact(row.meteo_outputs) - updates_desc = isempty(row.updates) ? "(none)" : _stringify_compact(row.updates) - application_name_desc = isnothing(row.application_name) ? "(unnamed)" : string(row.application_name) - applies_to_desc = isnothing(row.applies_to) ? "(implicit legacy target)" : _stringify_compact(row.applies_to) - value_inputs_desc = (row.value_inputs isa NamedTuple && isempty(keys(row.value_inputs))) ? "(none)" : _stringify_compact(row.value_inputs) - input_origins_desc = isempty(keys(row.input_origins)) ? "(none)" : _stringify_compact(row.input_origins) - model_calls_desc = (row.model_calls isa NamedTuple && isempty(keys(row.model_calls))) ? "(none)" : _stringify_compact(row.model_calls) - call_origins_desc = isempty(keys(row.call_origins)) ? "(none)" : _stringify_compact(row.call_origins) - environment_desc = isnothing(row.environment) ? "(default)" : _stringify_compact(row.environment) - println( - io, - " - ", - row.scale, - "/", - row.process, - " [", - row.model, - "]: name=", - application_name_desc, - ", applies_to=", - applies_to_desc, - ", value_inputs=", - value_inputs_desc, - ", input_origins=", - input_origins_desc, - ", model_calls=", - model_calls_desc, - ", call_origins=", - call_origins_desc, - ", environment=", - environment_desc, - ", timestep=", - timestep_desc, - ", input_bindings=", - input_bindings_desc, - ", meteo_bindings=", - meteo_bindings_desc, - ", meteo_window=", - meteo_window_desc, - ", meteo_inputs=", - meteo_inputs_desc, - ", meteo_outputs=", - meteo_outputs_desc, - ", updates=", - updates_desc - ) - end - return rows -end diff --git a/src/mtg/model_spec_validation.jl b/src/mtg/model_spec_validation.jl deleted file mode 100644 index c3cbc9192..000000000 --- a/src/mtg/model_spec_validation.jl +++ /dev/null @@ -1,444 +0,0 @@ -const _INPUT_BINDING_FIELDS = (:process, :var, :scale, :policy) -const _MODEL_SCOPE_SELECTORS = (:global, :plant, :scene, :self) -const _METEO_BINDING_FIELDS = (:source, :reducer) -const _CALENDAR_PERIODS = (:day, :week, :month) -const _CALENDAR_ANCHORS = (:current_period, :previous_complete_period) -const _CALENDAR_COMPLETENESS = (:allow_partial, :strict) - -function _validate_window_reducer(scale::Symbol, process::Symbol, input_var::Symbol, policy_name::Symbol, reducer) - vals_probe = [1.0, 2.0] - durations_probe = [1.0, 1.0] - - if reducer isa DataType - reducer <: PlantMeteo.AbstractTimeReducer || error( - "Invalid reducer type `$(reducer)` for policy `$(policy_name)` on input `$(input_var)` ", - "in process `$(process)` at scale `$(scale)`. ", - "Expected a PlantMeteo reducer type/instance or a callable." - ) - rr = try - reducer() - catch - error( - "Reducer type `$(reducer)` for policy `$(policy_name)` on input `$(input_var)` ", - "in process `$(process)` at scale `$(scale)` cannot be instantiated without arguments." - ) - end - (applicable(rr, vals_probe) || applicable(rr, vals_probe, durations_probe)) || error( - "Reducer type `$(reducer)` for policy `$(policy_name)` on input `$(input_var)` in process `$(process)` at scale `$(scale)` ", - "must be callable on `(values)` or `(values, durations)`." - ) - return nothing - elseif reducer isa PlantMeteo.AbstractTimeReducer - (applicable(reducer, vals_probe) || applicable(reducer, vals_probe, durations_probe)) || error( - "Reducer `$(typeof(reducer))` for policy `$(policy_name)` on input `$(input_var)` in process `$(process)` at scale `$(scale)` ", - "must be callable on `(values)` or `(values, durations)`." - ) - return nothing - elseif reducer isa Function - (applicable(reducer, vals_probe) || applicable(reducer, vals_probe, durations_probe)) || error( - "Reducer for policy `$(policy_name)` on input `$(input_var)` in process `$(process)` at scale `$(scale)` ", - "must be callable on `(values)` or `(values, durations)`." - ) - return nothing - end - - error( - "Invalid reducer value `$(reducer)` (type `$(typeof(reducer))`) for policy `$(policy_name)` ", - "on input `$(input_var)` in process `$(process)` at scale `$(scale)`. ", - "Expected a PlantMeteo reducer type/instance or a callable." - ) -end - -function _validate_policy_instance(scale::Symbol, process::Symbol, input_var::Symbol, policy::SchedulePolicy) - if policy isa HoldLast - return nothing - elseif policy isa Interpolate - policy.mode in _INTERPOLATE_MODES || error( - "Invalid interpolation mode `$(policy.mode)` for input `$(input_var)` in process `$(process)` at scale `$(scale)`. ", - "Supported modes are $(_INTERPOLATE_MODES)." - ) - policy.extrapolation in _INTERPOLATE_MODES || error( - "Invalid interpolation extrapolation `$(policy.extrapolation)` for input `$(input_var)` in process `$(process)` at scale `$(scale)`. ", - "Supported values are $(_INTERPOLATE_MODES)." - ) - return nothing - elseif policy isa Integrate - _validate_window_reducer(scale, process, input_var, :Integrate, policy.reducer) - return nothing - elseif policy isa Aggregate - _validate_window_reducer(scale, process, input_var, :Aggregate, policy.reducer) - return nothing - end - - return nothing -end - -function _validate_timestep_spec(scale::Symbol, process::Symbol, spec::ModelSpec) - ts = timestep(spec) - isnothing(ts) && return nothing - - if ts isa ClockSpec - float(ts.dt) > 0 || error( - "Invalid timestep for process `$(process)` at scale `$(scale)`: ", - "`ClockSpec.dt` must be > 0, got $(ts.dt)." - ) - return nothing - end - - if ts isa Real - float(ts) > 0 || error( - "Invalid timestep for process `$(process)` at scale `$(scale)`: ", - "numeric timestep must be > 0, got $(ts)." - ) - return nothing - end - - if ts isa Dates.Period - ts isa Dates.FixedPeriod || error( - "Invalid timestep for process `$(process)` at scale `$(scale)`: ", - "non-fixed periods are not supported (`$(typeof(ts))`). ", - "Use fixed periods such as `Second`, `Minute`, `Hour` or `Day`." - ) - Dates.value(Dates.Second(ts)) > 0 || error( - "Invalid timestep for process `$(process)` at scale `$(scale)`: ", - "period must be > 0, got $(ts)." - ) - return nothing - end - - error( - "Invalid timestep for process `$(process)` at scale `$(scale)`: ", - "expected `Real`, `ClockSpec` or `Dates.Period`, got `$(typeof(ts))`." - ) -end - -function _validate_scope_spec(scale::Symbol, process::Symbol, spec::ModelSpec) - selector = model_scope(spec) - if selector isa ScopeId - return nothing - elseif selector isa Symbol - selector in _MODEL_SCOPE_SELECTORS || error( - "Invalid scope selector `$(selector)` for process `$(process)` at scale `$(scale)`. ", - "Supported selectors are $(_MODEL_SCOPE_SELECTORS), `ScopeId`, or a callable." - ) - return nothing - elseif selector isa AbstractString - Symbol(selector) in _MODEL_SCOPE_SELECTORS || error( - "Invalid scope selector `$(selector)` for process `$(process)` at scale `$(scale)`. ", - "Supported selectors are $(_MODEL_SCOPE_SELECTORS), `ScopeId`, or a callable." - ) - return nothing - elseif selector isa Function - return nothing - end - - error( - "Invalid scope selector for process `$(process)` at scale `$(scale)`: ", - "expected `Symbol`, `String`, `ScopeId`, or callable, got `$(typeof(selector))`." - ) -end - -function _validate_binding_policy(scale::Symbol, process::Symbol, input_var::Symbol, policy) - if policy isa DataType - policy <: SchedulePolicy || error( - "Invalid policy for input `$(input_var)` in process `$(process)` at scale `$(scale)`: ", - "expected a `SchedulePolicy` type or instance, got `$(policy)`." - ) - p = try - policy() - catch - error( - "Invalid policy type `$(policy)` for input `$(input_var)` in process `$(process)` at scale `$(scale)`: ", - "this policy type cannot be instantiated without arguments. Provide a policy instance instead." - ) - end - _validate_policy_instance(scale, process, input_var, p) - return nothing - end - - policy isa SchedulePolicy || error( - "Invalid policy for input `$(input_var)` in process `$(process)` at scale `$(scale)`: ", - "expected a `SchedulePolicy` type or instance, got `$(typeof(policy))`." - ) - _validate_policy_instance(scale, process, input_var, policy) - - return nothing -end - -function _validate_binding_target( - scale::Symbol, - process::Symbol, - input_var::Symbol, - source_process::Symbol, - source_scale, - model_specs, - known_processes::Set{Symbol} -) - source_process in known_processes || error( - "Unknown source process `$(source_process)` for input `$(input_var)` in process `$(process)` at scale `$(scale)`." - ) - - isnothing(source_scale) && return nothing - src_scale = source_scale isa Symbol ? - source_scale : - error("Source scale for input `$(input_var)` in process `$(process)` must be a `Symbol`, got `$(typeof(source_scale))`.") - haskey(model_specs, src_scale) || error( - "Unknown source scale `$(src_scale)` for input `$(input_var)` in process `$(process)` at scale `$(scale)`." - ) - source_process in keys(model_specs[src_scale]) || error( - "Source process `$(source_process)` for input `$(input_var)` in process `$(process)` ", - "is not declared at scale `$(src_scale)`." - ) - return nothing -end - -function _validate_input_binding( - scale::Symbol, - process::Symbol, - input_var::Symbol, - binding, - model_specs, - known_processes::Set{Symbol} -) - source_process = nothing - source_scale = nothing - policy = HoldLast() - - if binding isa Symbol - source_process = binding - elseif binding isa Pair{Symbol,Symbol} - source_process = first(binding) - elseif binding isa NamedTuple - extra = setdiff(collect(keys(binding)), collect(_INPUT_BINDING_FIELDS)) - isempty(extra) || error( - "Invalid input binding for input `$(input_var)` in process `$(process)` at scale `$(scale)`: ", - "unsupported fields $(extra)." - ) - haskey(binding, :process) || error( - "Invalid input binding for input `$(input_var)` in process `$(process)` at scale `$(scale)`: ", - "field `process` is required." - ) - binding.process isa Symbol || error( - "Invalid input binding for input `$(input_var)` in process `$(process)` at scale `$(scale)`: ", - "`process` must be a Symbol, got `$(typeof(binding.process))`." - ) - source_process = binding.process - - if haskey(binding, :var) - isnothing(binding.var) || binding.var isa Symbol || error( - "Invalid input binding for input `$(input_var)` in process `$(process)` at scale `$(scale)`: ", - "`var` must be a Symbol or `nothing`, got `$(typeof(binding.var))`." - ) - end - - if haskey(binding, :scale) - isnothing(binding.scale) || binding.scale isa Symbol || error( - "Invalid input binding for input `$(input_var)` in process `$(process)` at scale `$(scale)`: ", - "`scale` must be a Symbol or `nothing`, got `$(typeof(binding.scale))`." - ) - source_scale = binding.scale - end - - policy = haskey(binding, :policy) ? binding.policy : HoldLast() - else - error( - "Invalid input binding for input `$(input_var)` in process `$(process)` at scale `$(scale)`: ", - "unsupported binding type `$(typeof(binding))`." - ) - end - - _validate_binding_policy(scale, process, input_var, policy) - _validate_binding_target(scale, process, input_var, source_process, source_scale, model_specs, known_processes) - return nothing -end - -function _validate_input_bindings_for_spec( - scale::Symbol, - process::Symbol, - spec::ModelSpec, - model_specs, - known_processes::Set{Symbol} -) - bindings = input_bindings(spec) - bindings isa NamedTuple || error( - "InputBindings for process `$(process)` at scale `$(scale)` must be a NamedTuple, got `$(typeof(bindings))`." - ) - - model_inputs = Set(keys(inputs_(model_(spec)))) - for (input_var, binding) in pairs(bindings) - input_var isa Symbol || error( - "InputBindings key for process `$(process)` at scale `$(scale)` must be a Symbol, got `$(typeof(input_var))`." - ) - input_var in model_inputs || error( - "InputBindings for process `$(process)` at scale `$(scale)` declares binding for input `$(input_var)`, ", - "but model inputs are $(collect(model_inputs))." - ) - _validate_input_binding(scale, process, input_var, binding, model_specs, known_processes) - end - return nothing -end - -function _validate_output_routing_for_spec(scale::Symbol, process::Symbol, spec::ModelSpec) - routing = output_routing(spec) - routing isa NamedTuple || error( - "OutputRouting for process `$(process)` at scale `$(scale)` must be a NamedTuple, got `$(typeof(routing))`." - ) - - model_outputs = Set(keys(outputs_(model_(spec)))) - for (out_var, mode) in pairs(routing) - out_var isa Symbol || error( - "OutputRouting key for process `$(process)` at scale `$(scale)` must be a Symbol, got `$(typeof(out_var))`." - ) - out_var in model_outputs || error( - "OutputRouting for process `$(process)` at scale `$(scale)` declares routing for output `$(out_var)`, ", - "but model outputs are $(collect(model_outputs))." - ) - - mode_sym = mode isa Symbol ? mode : (mode isa AbstractString ? Symbol(mode) : nothing) - isnothing(mode_sym) && error( - "OutputRouting mode for output `$(out_var)` in process `$(process)` at scale `$(scale)` ", - "must be `:canonical` or `:stream_only`." - ) - mode_sym in (:canonical, :stream_only) || error( - "OutputRouting mode `$(mode_sym)` for output `$(out_var)` in process `$(process)` at scale `$(scale)` ", - "is invalid. Allowed values: `:canonical`, `:stream_only`." - ) - end - - return nothing -end - -function _validate_meteo_binding(scale::Symbol, process::Symbol, target_var::Symbol, binding) - if binding isa Function || binding isa PlantMeteo.AbstractTimeReducer - return nothing - elseif binding isa DataType - binding <: PlantMeteo.AbstractTimeReducer || error( - "Invalid MeteoBindings reducer type for variable `$(target_var)` in process `$(process)` at scale `$(scale)`: ", - "expected a subtype of `PlantMeteo.AbstractTimeReducer`." - ) - try - binding() - catch - error( - "Invalid MeteoBindings reducer type for variable `$(target_var)` in process `$(process)` at scale `$(scale)`: ", - "type `$(binding)` cannot be instantiated without arguments." - ) - end - return nothing - elseif binding isa NamedTuple - extra = setdiff(collect(keys(binding)), collect(_METEO_BINDING_FIELDS)) - isempty(extra) || error( - "Invalid MeteoBindings for variable `$(target_var)` in process `$(process)` at scale `$(scale)`: ", - "unsupported fields $(extra)." - ) - - if haskey(binding, :source) - binding.source isa Symbol || binding.source isa AbstractString || error( - "Invalid MeteoBindings source for variable `$(target_var)` in process `$(process)` at scale `$(scale)`: ", - "`source` must be a Symbol or String." - ) - end - if haskey(binding, :reducer) - reducer = binding.reducer - if reducer isa DataType - reducer <: PlantMeteo.AbstractTimeReducer || error( - "Invalid MeteoBindings reducer for variable `$(target_var)` in process `$(process)` at scale `$(scale)`: ", - "`reducer` type must subtype `PlantMeteo.AbstractTimeReducer`." - ) - try - reducer() - catch - error( - "Invalid MeteoBindings reducer type for variable `$(target_var)` in process `$(process)` at scale `$(scale)`: ", - "type `$(reducer)` cannot be instantiated without arguments." - ) - end - else - (reducer isa PlantMeteo.AbstractTimeReducer || reducer isa Function) || error( - "Invalid MeteoBindings reducer for variable `$(target_var)` in process `$(process)` at scale `$(scale)`: ", - "`reducer` must be a reducer instance/type or a callable." - ) - end - end - return nothing - end - - error( - "Invalid MeteoBindings value for variable `$(target_var)` in process `$(process)` at scale `$(scale)`: ", - "unsupported type `$(typeof(binding))`." - ) -end -function _validate_meteo_bindings_for_spec(scale::Symbol, process::Symbol, spec::ModelSpec) - bindings = meteo_bindings(spec) - bindings isa NamedTuple || error( - "MeteoBindings for process `$(process)` at scale `$(scale)` must be a NamedTuple, got `$(typeof(bindings))`." - ) - - for (target_var, binding) in pairs(bindings) - target_var isa Symbol || error( - "MeteoBindings key for process `$(process)` at scale `$(scale)` must be a Symbol, got `$(typeof(target_var))`." - ) - _validate_meteo_binding(scale, process, target_var, binding) - end - return nothing -end - -function _validate_meteo_window_for_spec(scale::Symbol, process::Symbol, spec::ModelSpec) - window = meteo_window(spec) - isnothing(window) && return nothing - - window isa PlantMeteo.AbstractSamplingWindow || error( - "MeteoWindow for process `$(process)` at scale `$(scale)` must be a PlantMeteo sampling-window instance, got `$(typeof(window))`." - ) - - if window isa PlantMeteo.CalendarWindow - window.period in _CALENDAR_PERIODS || error( - "Invalid CalendarWindow period `$(window.period)` for process `$(process)` at scale `$(scale)`. ", - "Allowed values are $(_CALENDAR_PERIODS)." - ) - window.anchor in _CALENDAR_ANCHORS || error( - "Invalid CalendarWindow anchor `$(window.anchor)` for process `$(process)` at scale `$(scale)`. ", - "Allowed values are $(_CALENDAR_ANCHORS)." - ) - 1 <= window.week_start <= 7 || error( - "Invalid CalendarWindow week_start `$(window.week_start)` for process `$(process)` at scale `$(scale)`. ", - "Allowed values are integers in 1:7." - ) - window.completeness in _CALENDAR_COMPLETENESS || error( - "Invalid CalendarWindow completeness `$(window.completeness)` for process `$(process)` at scale `$(scale)`. ", - "Allowed values are $(_CALENDAR_COMPLETENESS)." - ) - end - - return nothing -end - -""" - validate_model_specs_configuration(model_specs) - -Validate mapping-level `ModelSpec` configuration before simulation runtime starts. -This catches invalid timestep declarations, input bindings and output routing early. -""" -function validate_model_specs_configuration( - model_specs; - ignored_processes_by_scale::Dict{Symbol,Set{Symbol}}=Dict{Symbol,Set{Symbol}}() -) - known_processes = Set{Symbol}() - for specs_at_scale in values(model_specs) - union!(known_processes, keys(specs_at_scale)) - end - - for (scale, specs_at_scale) in pairs(model_specs) - for (process, spec) in pairs(specs_at_scale) - _validate_timestep_spec(scale, process, spec) - _validate_scope_spec(scale, process, spec) - _validate_input_bindings_for_spec(scale, process, spec, model_specs, known_processes) - _validate_meteo_bindings_for_spec(scale, process, spec) - _validate_meteo_window_for_spec(scale, process, spec) - _validate_output_routing_for_spec(scale, process, spec) - end - end - validate_update_dependencies(model_specs; ignored_processes_by_scale=ignored_processes_by_scale) - - return nothing -end diff --git a/src/mtg/node_mapping_types.jl b/src/mtg/node_mapping_types.jl deleted file mode 100644 index 9ef0dd753..000000000 --- a/src/mtg/node_mapping_types.jl +++ /dev/null @@ -1,15 +0,0 @@ -""" - AbstractNodeMapping - -Abstract type for node mapping markers, such as single-node, multi-node, self, -or same-scale mappings. -""" -abstract type AbstractNodeMapping end - -""" - SameScale() - -Marker used in multiscale variable mappings when a variable is aliased or -renamed from another variable at the same scale. -""" -struct SameScale <: AbstractNodeMapping end diff --git a/src/mtg/save_results.jl b/src/mtg/save_results.jl deleted file mode 100644 index 9f743aed7..000000000 --- a/src/mtg/save_results.jl +++ /dev/null @@ -1,375 +0,0 @@ -""" - pre_allocate_outputs(statuses, outs, nsteps; check=true) - -Pre-allocate the outputs of needed variable for each node type in vectors of vectors. -The first level vectors have length nsteps, and the second level vectors have length n_nodes of this type. - -Note that we pre-allocate the vectors for the time-steps, but not for each organ, because we don't -know how many nodes will be in each organ in the future (organs can appear or disapear). - -# Arguments - -- `statuses`: a dictionary of status by node type -- `outs`: a dictionary of outputs by node type -- `nsteps`: the number of time-steps -- `check`: whether to check the mapping for errors. Default (`true`) returns an error if some variables do not exist. -If false and some variables are missing, return an info, remove the unknown variables and continue. - -# Returns - -- A dictionary of pre-allocated output of vector of time-step and vector of node of that type. - -# Examples - -```jldoctest mylabel -julia> using PlantSimEngine, MultiScaleTreeGraph, PlantSimEngine.Examples -``` - -Import example models (can be found in the `examples` folder of the package, or in the `Examples` sub-modules): - -```jldoctest mylabel -julia> using PlantSimEngine.Examples; -``` - -Define the models mapping: - -```jldoctest mylabel -julia> mapping = PlantSimEngine.ModelMapping( \ - :Plant => ( \ - PlantSimEngine.MultiScaleModel( \ - model=ToyCAllocationModel(), \ - mapped_variables=[ \ - :carbon_assimilation => [:Leaf], \ - :carbon_demand => [:Leaf, :Internode], \ - :carbon_allocation => [:Leaf, :Internode] \ - ], \ - ), - PlantSimEngine.MultiScaleModel( \ - model=ToyPlantRmModel(), \ - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],] \ - ), \ - ),\ - :Internode => ( \ - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), \ - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), \ - Status(TT=10.0, carbon_biomass=1.0) \ - ), \ - :Leaf => ( \ - PlantSimEngine.MultiScaleModel( \ - model=ToyAssimModel(), \ - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], \ - ), \ - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), \ - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), \ - Status(aPPFD=1300.0, TT=10.0, carbon_biomass=1.0), \ - ), \ - :Soil => ( \ - ToySoilWaterModel(), \ - ), \ -); -``` - -Importing an example MTG provided by the package: - -```jldoctest mylabel -julia> mtg = import_mtg_example(); -``` - -```jldoctest mylabel -julia> statuses, status_templates, reverse_multiscale_mapping, vars_need_init = PlantSimEngine.init_statuses(mtg, mapping); -``` - -```jldoctest mylabel -julia> outs = Dict(:Leaf => (:carbon_assimilation, :carbon_demand), :Soil => (:soil_water_content,)); -``` - -Pre-allocate the outputs as a dictionary: - -```jldoctest mylabel -julia> preallocated_vars = PlantSimEngine.pre_allocate_outputs(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outs, 2); -``` - -The dictionary has a key for each organ from which we want outputs: - -```jldoctest mylabel -julia> collect(keys(preallocated_vars)) -2-element Vector{Symbol}: - :Soil - :Leaf -``` - -Each organ has a dictionary of variables for which we want outputs from, -with the pre-allocated empty vectors (one per time-step that will be filled with one value per node): - -```jldoctest mylabel -julia> collect(keys(preallocated_vars[:Leaf])) -3-element Vector{Symbol}: - :carbon_assimilation - :node - :carbon_demand -``` -""" - -function pre_allocate_outputs(statuses, statuses_template, reverse_multiscale_mapping, vars_need_init, outs, nsteps; type_promotion=nothing, check=true) - outs_ = Dict{Symbol,Vector{Symbol}}() - - # default behaviour : track everything - if isnothing(outs) - for organ in keys(statuses) - outs_[organ] = [keys(statuses_template[organ])...] - end - # No outputs requested by user : just return the timestep and node - elseif length(outs) == 0 - for i in keys(statuses) - outs_[i] = [] - end - else - for i in keys(outs) # i = :Plant - i isa Symbol || error("Output scale keys must be `Symbol`, got `$(typeof(i))` for key `$(repr(i))`.") - if !isa(outs[i], Tuple{Vararg{Symbol}}) - error("""Outputs for scale $i should be a tuple of symbols, *e.g.* `$i => (:a, :b)`, found `$i => $(outs[i])` instead.""") - end - outs_[i] = [outs[i]...] - end - end - - len = Dict{Symbol,Int}() - for (organ, vals) in outs_ - len[organ] = length(outs_[organ]) - unique!(outs_[organ]) - end - - - for (organ, vals) in outs_ - if length(outs_[organ]) != len[organ] - @info "One or more requested output variable duplicated at scale $organ, removed it" - end - end - - statuses_ = copy(statuses_template) - # Checking that organs in outputs exist in the mtg (in the statuses): - if !all(i in keys(statuses) for i in keys(outs_)) - not_in_statuses = setdiff(keys(outs_), keys(statuses)) - e = string( - "You requested outputs for organs ", - join(keys(outs_), ", "), - ", but organs ", - join(not_in_statuses, ", "), - " have no models." - ) - - if check - error(e) - else - @info e - [delete!(outs_, i) for i in not_in_statuses] - end - end - - # Checking that variables in outputs exist in the statuses, and adding the :node variable: - for (organ, vars) in outs_ # organ = :Leaf; vars = outs_[organ] - if length(statuses[organ]) == 0 - # The organ is not found in the mtg, we return an info and get along (it might be created during the simulation): - check && @info "You required outputs for organ $organ, but this organ is not found in the provided MTG at this point." - end - if !all(i in collect(keys(statuses_[organ])) for i in vars) - not_in_statuses = (setdiff(vars, keys(statuses_[organ]))...,) - plural = length(not_in_statuses) == 1 ? "" : "s" - e = string( - "You requested outputs for variable", plural, " ", - join(not_in_statuses, ", "), - " in organ $organ, but ", - length(not_in_statuses) == 1 ? "it has no model." : "they have no models." - ) - if check - error(e) - else - @info e - existing_vars_requested = setdiff(outs_[organ], not_in_statuses) - if length(existing_vars_requested) == 0 - # None of the variables requested by the user exist at this scale for this set of models - delete!(outs_, organ) - else - # Some still exist, we only use the ones that do: - outs_[organ] = [existing_vars_requested...] - end - end - end - - if :node ∉ outs_[organ] - push!(outs_[organ], :node) - end - end - - node_types = [] - for o in keys(statuses) - if length(statuses[o]) > 0 - push!(node_types, typeof(statuses[o][1].node)) - end - end - - node_type = unique(node_types) - if length(node_type) != 1 - error("All plant graph nodes should have the same type, found $(unique(node_type)).") - end - node_type = only(node_type) - - # I don't know if this function barrier is necessary - preallocated_outputs = Dict{Symbol,Vector}() - complete_preallocation_from_types!(preallocated_outputs, nsteps, outs_, node_type, statuses_template) - return preallocated_outputs -end - -function complete_preallocation_from_types!(preallocated_outputs, nsteps, outs_, node_type, statuses_template) - types = Vector{DataType}() - for organ in keys(outs_) - - outs_no_node = filter(x -> x != :node, outs_[organ]) - - #types = [typeof(status_from_template(statuses_template[organ])[var]) for var in outs[organ]] - values = [status_from_template(statuses_template[organ])[var] for var in outs_no_node] - - #push!(types, node_type) - - # contains :node - symbols_tuple = (:timestep, :node, outs_no_node...,) - # using node_type.parameters[1] is clunky, but covers both NodeMTG and AbstractNodeMTG types - values_tuple = (1, MultiScaleTreeGraph.Node((node_type.parameters[1])("/", "Uninitialized", 0, 0),), values...,) - - # Dummy value to make accessing the type easier - # (empty arrays don't have references to an instance, so their types can't be inspected and manipulated as easily) - dummy_status = (; zip(symbols_tuple, values_tuple)...) - data = typeof(Status(dummy_status))[] - resize!(data, nsteps) - - for ii in 1:nsteps - data[ii] = Status(dummy_status) - end - preallocated_outputs[organ] = data - end -end - - -""" - save_results!(object::GraphSimulation, i) - -Save the results of the simulation for time-step `i` into the -object. For a `GraphSimulation` object, this will save the results -from the `status(object)` in the `outputs(object)`. -""" -function save_results!(object::GraphSimulation, i) - outs = outputs(object) - - if length(outs) == 0 - return - end - - statuses = status(object) - indexes = object.outputs_index - for organ in keys(outs) - - if length(outs[organ]) == 0 - continue - end - - index = indexes[organ] - - # Samuel : Simple resizing heuristic - # This can be made much more conservative with the right heuristic, or with user hints - # The array filling bit of code is clunky, but building NamedTuples on the fly was tanking performance - # And it wasn't straightforward to avoid Status Ref reallocations causing performance issues - # I then tried various approaches with Status, resizing, using fill, deepcopying, ... - # I may have gotten a little confused while fighting the type system - # So there may be possible simplifications (maybe no need for a function barrier, perhaps the resizing could be made a one-liner...) - # But this should work without causing visible performance regressions on XPalm - len = length(outs[organ]) - if length(statuses[organ]) + index - 1 > len - min_required = max(length(statuses[organ]) + index - len, index) - - extra_length = 2 * min_required - len - data = eltype(outs[organ])[] - resize!(data, extra_length) - dummy_value = NamedTuple(outs[organ][1]) - # TODO set timestep to 0 for clarity ? - - # Using fill! caused Ref issues, so call a Status constructor here instead of passing a prebuilt value - # This will avoid having all array entries point to the same ref but keep construction cost at a minimum - for new_entry in 1:extra_length - data[new_entry] = Status(dummy_value) - end - - outs[organ] = cat(outs[organ], data, dims=1) - #println("len : ", len, " statuses #", length(statuses[organ]), " index ", index) - #println("min_required : ", min_required, " extra_length ", extra_length, " new len ", length(outs[organ])) - end - - tracked_outputs = filter(i -> i != :timestep, keys(outs[organ][1])) - - indexes[organ] = copy_tracked_outputs_into_vector!(outs[organ], i, statuses[organ], tracked_outputs, indexes[organ]) - end -end - -function copy_tracked_outputs_into_vector!(outs_organ, i, statuses_organ, tracked_outputs, index) - j = index - for status in statuses_organ - outs_organ[j].timestep = i - for var in tracked_outputs - outs_organ[j][var] = status[var] - end - j += 1 - end - return j -end - -function pre_allocate_outputs(m::SingleScaleModelSet, outs, nsteps; type_promotion=nothing, check=true) - st, = flatten_status(status(m)) - out_vars_all = convert_vars(st, type_promotion) - - out_keys_requested = Symbol[] - if !isnothing(outs) - if length(outs) == 0 # no outputs desired, for some reason - return NamedTuple() - end - out_keys_requested = Symbol[outs...] - end - out_vars_requested = NamedTuple() - - # default implicit behaviour, track everything - if isempty(out_keys_requested) - # We already have the status here, just repeating its value: - out_vars_requested = NamedTuple(out_vars_all) - else - unexpected_outputs = setdiff(out_keys_requested, keys(st)) - - if !isempty(unexpected_outputs) - e = string( - "You requested as output ", - join(unexpected_outputs, " ,"), - " not found in any model." - ) - - if check - error(e) - else - @info e - [delete!(unexpected_outputs, i) for i in unexpected_outputs] - end - end - - out_defaults_requested = (out_vars_all[i] for i in out_keys_requested) - out_vars_requested = (; zip(out_keys_requested, out_defaults_requested)...) - end - - return TimeStepTable([Status(out_vars_requested) for i in Base.OneTo(nsteps)]) -end - -function save_results!(status_flattened::Status, outputs, i) - if length(outputs) == 0 - return - end - outs = outputs[i] - - for var in keys(outs) - setproperty!(outs, var, status_flattened[var]) - end -end diff --git a/src/processes/model_initialisation.jl b/src/processes/model_initialisation.jl deleted file mode 100755 index 4b20e51e6..000000000 --- a/src/processes/model_initialisation.jl +++ /dev/null @@ -1,385 +0,0 @@ -""" - to_initialize(; verbose=true, vars...) - to_initialize(m::T) where T <: ModelMapping - to_initialize(m::DependencyGraph) - to_initialize(mapping::ModelMapping, graph=nothing) - to_initialize(mapping::AbstractDict{Symbol,T}, graph=nothing) - -Return the variables that must be initialized providing a set of models and processes. The -function takes into account model coupling and only returns the variables that are needed -considering that some variables that are outputs of some models are used as inputs of others. - -# Arguments - -- `verbose`: if `true`, print information messages. -- `vars...`: the models and processes to consider. -- `m::T`: a [`ModelMapping`](@ref). -- `m::DependencyGraph`: a [`DependencyGraph`](@ref). -- `mapping::ModelMapping` (or dictionary-like mapping): associates models to organs/scales. -- `graph`: a graph representing a plant or a scene, *e.g.* a multiscale tree graph. The graph - is used to check if variables that are not initialized can be found in the graph nodes attributes. - -# Examples - -```@example -using PlantSimEngine - -# Load the dummy models given as example in the package: -using PlantSimEngine.Examples - -to_initialize(process1=Process1Model(1.0), process2=Process2Model()) - -# Or using a component directly: -models = PlantSimEngine.ModelMapping(process1=Process1Model(1.0), process2=Process2Model()) -to_initialize(models) - -m = PlantSimEngine.ModelMapping( - ( - process1=Process1Model(1.0), - process2=Process2Model() - ), - Status(var1 = 5.0, var2 = -Inf, var3 = -Inf, var4 = -Inf, var5 = -Inf) -) - -to_initialize(m) -``` - -Or with a mapping: - -```@example -using PlantSimEngine - -# Load the dummy models given as example in the package: -using PlantSimEngine.Examples - -mapping = PlantSimEngine.ModelMapping( - :Leaf => PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model() - ), - :Internode => PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - ) -) - -to_initialize(mapping) -``` -""" -function to_initialize(m::SingleScaleModelSet) - needed_variables = to_initialize(dep(m)) - to_init = Dict{Symbol,Tuple}() - for (process, vars) in needed_variables - # default_values = needed_variables[:process1] - # st = m.status - not_init = vars_not_init_(m.status, vars) - length(not_init) > 0 && push!(to_init, process => not_init) - end - return NamedTuple(to_init) -end - -function to_initialize(m::DependencyGraph) - dependencies = traverse_dependency_graph(m, to_initialize) - - outputs_all = Set{Symbol}() - for (key, value) in dependencies - outputs_all = union(outputs_all, keys(value.outputs)) - end - - needed_variables_process = Dict{Symbol,NamedTuple}() - for (key, value) in dependencies - for (key_in, val_in) in pairs(value.inputs) - if key_in ∉ outputs_all - input_default = NamedTuple{(key_in,)}((val_in,)) - if haskey(needed_variables_process, key) - needed_variables_process[key] = merge(needed_variables_process[key], input_default) - else - push!(needed_variables_process, key => input_default) - end - end - end - end - # note: needed_variables_process is e.g.: - # Dict{Symbol, NamedTuple} with 2 entries: - # :process1 => (var1 = -Inf, var2 = -Inf) - # :process2 => (var1 = -Inf,) - return needed_variables_process -end - - -#Return the variables that must be initialized providing a set of models and processes. The -#function just returns the inputs and outputs of each model, with their default values. -#To take into account model coupling, use the function at an upper-level instead, *i.e.* -# `to_initialize(m::ModelMapping)` or `to_initialize(m::DependencyGraph)`. -function to_initialize(m::AbstractDependencyNode) - return (inputs=inputs_(m.value), outputs=outputs_(m.value)) -end - -function to_initialize(m::T) where {T<:Dict{Symbol,ModelMapping}} - toinit = Dict{Symbol,NamedTuple}() - for (key, value) in m - # key = :Leaf; value = m[key] - toinit_ = to_initialize(value) - - if length(toinit_) > 0 - push!(toinit, key => toinit_) - end - end - - return toinit -end - - -function to_initialize(; verbose=true, vars...) - needed_variables = to_initialize(dep(; verbose=verbose, (; vars...)...)) - to_init = Dict{Symbol,Tuple}() - for (process, vars) in pairs(needed_variables) - not_init = keys(vars) - length(not_init) > 0 && push!(to_init, process => not_init) - end - return NamedTuple(to_init) -end - -# For the list of mapping given to an MTG: -function to_initialize(mapping::AbstractDict{Symbol,T}, graph=nothing) where {T} - # Get the variables in the MTG: - if isnothing(graph) - vars_in_mtg = Symbol[] - else - vars_in_mtg = names(graph) - end - - to_init = Dict(org => Symbol[] for org in keys(mapping)) - mapped_vars = mapped_variables(mapping, first(hard_dependencies(mapping; verbose=false)), verbose=false) - for (org, vars) in mapped_vars - for (var, val) in vars - if isa(val, UninitializedVar) && var ∉ vars_in_mtg - push!(to_init[org], var) - end - end - end - - filter!(x -> length(last(x)) > 0, to_init) - - return to_init -end - -""" - init_status!(object::Dict{Symbol,ModelMapping};vars...) - init_status!(component::ModelMapping;vars...) - -Initialise model variables for components with user input. - -# Examples - -```@example -using PlantSimEngine - -# Load the dummy models given as example in the package: -using PlantSimEngine.Examples - -models = Dict( - :Leaf => PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model() - ), - :Internode => PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - ) -) - -init_status!(models, var1=1.0 , var2=2.0) -status(models[:Leaf]) -``` -""" -function init_status!(object::Dict{Symbol,ModelMapping}; vars...) - new_vals = (; vars...) - - for (component_name, component) in object - for j in keys(new_vals) - if !in(j, keys(component.status)) - @info "Key $j not found as a variable for any provided models in $component_name" maxlog = 1 - continue - end - setproperty!(component.status, j, new_vals[j]) - end - end -end - -function init_status!(component::T; vars...) where {T<:ModelMapping} - new_vals = (; vars...) - for j in keys(new_vals) - if !in(j, keys(component.status)) - @info "Key $j not found as a variable for any provided models" - continue - end - setproperty!(component.status, j, new_vals[j]) - end -end - -""" - init_variables(models...) - -Initialized model variables with their default values. The variables are taken from the -inputs and outputs of the models. - -# Examples - -```@example -using PlantSimEngine - -# Load the dummy models given as example in the package: -using PlantSimEngine.Examples - -init_variables(Process1Model(2.0)) -init_variables(process1=Process1Model(2.0), process2=Process2Model()) -``` -""" -function init_variables(model::T; verbose::Bool=true) where {T<:AbstractModel} - # Only one model is provided: - in_vars = inputs_(model) - out_vars = outputs_(model) - # Merge both: - vars = merge(in_vars, out_vars) - - return vars -end - -function init_variables(m::SingleScaleModelSet; verbose::Bool=true) - init_variables(dep(m)) -end - -function init_variables(m::DependencyGraph) - dependencies = traverse_dependency_graph(m, init_variables) - return NamedTuple(dependencies) -end - -function init_variables(node::AbstractDependencyNode) - return init_variables(node.value) -end - -# Models are provided as keyword arguments: -function init_variables(; verbose::Bool=true, kwargs...) - mods = (; kwargs...) - init_variables(dep(; verbose=verbose, mods...)) -end - -# Models are provided as a NamedTuple: -function init_variables(models::T; verbose::Bool=true) where {T<:NamedTuple} - init_variables(dep(; verbose=verbose, models...)) -end - -""" - is_initialized(m::T) where T <: ModelMapping - is_initialized(m::T, models...) where T <: ModelMapping - -Check if the variables that must be initialized are, and return `true` if so, and `false` and -an information message if not. - -# Note - -There is no way to know before-hand which process will be simulated by the user, so if you -have a component with a model for each process, the variables to initialize are always the -smallest subset of all, meaning it is considered the user will simulate the variables needed -for other models. - -# Examples - -```@example -using PlantSimEngine - -# Load the dummy models given as example in the package: -using PlantSimEngine.Examples - -models = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model() -) - -is_initialized(models) -``` -""" -function is_initialized(m::T; verbose=true) where {T<:SingleScaleModelSet} - var_names = to_initialize(m) - - if any([length(to_init) > 0 for (process, to_init) in pairs(var_names)]) - verbose && @info "Some variables must be initialized before simulation: $var_names (see `to_initialize()`)" maxlog = 1 - return false - else - return true - end -end - -function is_initialized(models...; verbose=true) - var_names = to_initialize(models...) - if length(var_names) > 0 - verbose && @info "Some variables must be initialized before simulation: $(var_names) (see `to_initialize()`)" maxlog = 1 - return false - else - return true - end -end - -""" - vars_not_init_(st<:Status, var_names) - -Get which variable is not properly initialized in the status struct. -""" -function vars_not_init_(st::T, default_values) where {T<:Status} - length(default_values) == 0 && return () # no variables - - not_init = Symbol[] - for i in keys(default_values) - # if the variable value is equal to the default value, or if it is an uninitialized RefVector (length == 0): - if getproperty(st, i) == default_values[i] || (isa(getproperty(st, i), RefVector) && length(getproperty(st, i)) == 0) - push!(not_init, i) - end - end - return (not_init...,) -end - -# For components with a status with multiple time-steps: -function vars_not_init_(status, default_values) - length(default_values) == 0 && return () # no variables - - not_init = Set{Symbol}() - for st in Tables.rows(status), i in eachindex(default_values) - if getproperty(st, i) == getproperty(default_values, i) - push!(not_init, i) - end - end - - return Tuple(not_init) -end - -""" - init_variables_manual(models...;vars...) - -Return an initialisation of the model variables with given values. - -# Examples - -```@example -using PlantSimEngine - -# Load the dummy models given as example in the package: -using PlantSimEngine.Examples - -models = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model() -) - -PlantSimEngine.init_variables_manual(status(models), (var1=20.0,)) -``` -""" -function init_variables_manual(status, vars) - for i in keys(vars) - !in(i, keys(status)) && error("Key $i not found as a variable of the status.") - setproperty!(status, i, vars[i]) - end - status -end diff --git a/src/processes/models_inputs_outputs.jl b/src/processes/models_inputs_outputs.jl index 47c3c6756..3a6332959 100644 --- a/src/processes/models_inputs_outputs.jl +++ b/src/processes/models_inputs_outputs.jl @@ -59,6 +59,24 @@ or `Interpolate`) for each producer output. output_policy(model::AbstractModel) = output_policy(typeof(model)) output_policy(::Type{<:AbstractModel}) = NamedTuple() +function _policy_for_output(model, variable::Symbol) + policies = output_policy(model) + variable in keys(policies) || return HoldLast() + return _as_schedule_policy( + policies[variable]; + context="output_policy for `$(variable)` in $(typeof(model))", + ) +end + +function _publish_mode_for_output(spec::ModelSpec, variable::Symbol) + modes = output_routing(spec) + mode = variable in keys(modes) ? modes[variable] : :canonical + mode in (:canonical, :stream_only) || error( + "Unsupported output routing mode `$(mode)` for `$(variable)`." + ) + return mode +end + """ application_name(spec::ModelSpec) @@ -96,31 +114,6 @@ Optional scene/object environment configuration declared with `Environment(...)` """ environment_config(spec::ModelSpec) = spec.environment -""" - input_bindings(spec::ModelSpec) - -Optional explicit input-to-producer bindings used by multi-rate resolution. - -`ModelSpec` is the user-facing API for mapping-level coupling configuration. -Bindings are read from `ModelSpec` during multiscale multi-rate simulation. - -Expected return value is a `NamedTuple` keyed by consumer input variable names. -Each value must itself be a `NamedTuple` with: -- `process::Symbol`: producer process name (as generated by `@process`) -- `var::Symbol`: producer output variable name -- `policy`: scheduling policy instance (defaults to `HoldLast()` when omitted) - -This lets a consumer input read from a producer variable even when names differ. -For example, consumer input `:C` can be mapped from producer output `:S`. - -# Example - -```julia -PlantSimEngine.InputBindings(; C=(process=:myproducer, var=:S)) -``` -""" -input_bindings(spec::ModelSpec) = spec.input_bindings - """ output_routing(spec::ModelSpec) @@ -131,26 +124,18 @@ Allowed values are: """ output_routing(spec::ModelSpec) = spec.output_routing -""" - model_scope(spec::ModelSpec) - -Scope selector used by multi-rate runtime to partition producer streams. -Default is `:global`. -""" -model_scope(spec::ModelSpec) = spec.scope - """ updates(spec::ModelSpec) Scenario-level metadata for variables intentionally updated by this model after -another producer in the same mapping. +another producer on the same object. """ updates(spec::ModelSpec) = spec.updates """ meteo_bindings(spec::ModelSpec) -Optional explicit weather aggregation bindings used by multi-rate MTG runtime. +Optional explicit weather aggregation bindings used by the scene runtime. Each key is the target meteo variable exposed to the model at execution time. Each value can be: - PlantMeteo reducer instance/type (e.g. `MeanWeighted()`, `MaxReducer`) @@ -162,7 +147,7 @@ meteo_bindings(spec::ModelSpec) = spec.meteo_bindings """ meteo_window(spec::ModelSpec) -Optional weather window-selection strategy used by multi-rate MTG runtime. +Optional weather window-selection strategy used by the scene runtime. Defaults to `nothing` (runtime falls back to `PlantMeteo.RollingWindow()` behavior). """ meteo_window(spec::ModelSpec) = spec.meteo_window @@ -174,7 +159,7 @@ meteo_window(spec::ModelSpec) = spec.meteo_window Meteorological/environment variables read directly by a model. This trait is separate from `inputs_` because meteorology may be constant, -table-backed, or produced by a microclimate domain. The default is empty. +table-backed, or produced by a microclimate backend. The default is empty. """ meteo_inputs(model::AbstractModel) = keys(meteo_inputs_(model)) meteo_inputs(spec::ModelSpec) = keys(meteo_inputs_(spec)) @@ -186,29 +171,13 @@ meteo_inputs_(model::Missing) = NamedTuple() meteo_outputs_(model::AbstractModel) Meteorological/environment variables produced by a model, for example local -microclimate variables computed over a voxel/octree domain. +microclimate variables computed over a voxel or octree backend. """ meteo_outputs(model::AbstractModel) = keys(meteo_outputs_(model)) meteo_outputs(spec::ModelSpec) = keys(meteo_outputs_(spec)) meteo_outputs_(model::AbstractModel) = NamedTuple() meteo_outputs_(model::Missing) = NamedTuple() -""" - inputs(mapping::ModelMapping) - inputs(mapping::AbstractDict{Symbol,T}) - -Get the inputs of the models in a mapping, for each process and organ type. -""" -function inputs(mapping::AbstractDict{Symbol,T}) where {T} - vars = Dict{Symbol,NamedTuple}() - for organ in keys(mapping) - mods = pairs(parse_models(get_models(mapping[organ]))) - push!(vars, organ => (; (i.first => (inputs(i.second)...,) for i in mods)...)) - end - return vars -end - - """ outputs(model::AbstractModel) outputs(...) @@ -239,22 +208,6 @@ function outputs(v::T, vars...) where {T<:AbstractModel} length((vars...,)) > 0 ? union(outputs(v), outputs(vars...)) : outputs(v) end -""" - outputs(mapping::ModelMapping) - outputs(mapping::AbstractDict{Symbol,T}) - -Get the outputs of the models in a mapping, for each process and organ type. -""" -function outputs(mapping::AbstractDict{Symbol,T}) where {T} - vars = Dict{Symbol,NamedTuple}() - for organ in keys(mapping) - mods = pairs(parse_models(get_models(mapping[organ]))) - push!(vars, organ => (; (i.first => (outputs(i.second)...,) for i in mods)...)) - end - return vars -end - - function outputs_(model::AbstractModel) NamedTuple() end @@ -299,16 +252,6 @@ function variables(m::T, ms...) where {T<:Union{Missing,AbstractModel}} length((ms...,)) > 0 ? merge(variables(m), variables(ms...)) : merge(inputs_(m), outputs_(m)) end -function variables(m::SoftDependencyNode) - self_variables = (inputs=inputs_(m.value), outputs=outputs_(m.value)) - # hard_dep_vars = map(variables, m.hard_dependencies) - return self_variables -end - -function variables(m::HardDependencyNode) - return (inputs=inputs_(m.value), outputs=outputs_(m.value)) -end - """ variables(pkg::Module) @@ -336,20 +279,12 @@ function variables(pkg::Module) end """ - variables(mapping::ModelMapping) - variables(mapping::AbstractDict{Symbol,T}) + init_variables(model) -Get the variables (inputs and outputs) of the models in a mapping, for each -process and organ type. +Return the merged default input and output values declared by `model`. """ -function variables(mapping::AbstractDict{Symbol,T}) where {T} - vars = Dict{Symbol,NamedTuple}() - for organ in keys(mapping) - mods = pairs(parse_models(get_models(mapping[organ]))) - push!(vars, organ => (; (i.first => (; variables(i.second)...,) for i in mods)...)) - end - return vars -end +init_variables(model::AbstractModel; verbose::Bool=true) = variables(model) +init_variables(spec::ModelSpec; verbose::Bool=true) = init_variables(model_(spec); verbose=verbose) """ variables_typed(model) diff --git a/src/processes/process_generation.jl b/src/processes/process_generation.jl index 9bd1e90e8..74be3bc84 100644 --- a/src/processes/process_generation.jl +++ b/src/processes/process_generation.jl @@ -143,36 +143,21 @@ macro process(f, args...) end ``` - Note that {#8abeff}run!(){/#8abeff} takes six arguments: the model type (used for dispatch), the ModelMapping, the status, the meteorology, + Note that {#8abeff}run!(){/#8abeff} takes six arguments: the model type (used for dispatch), the called-model bundle, the status, the meteorology, the constants and any extra values. - Then we can use variables from the status as inputs or outputs, model parameters from the ModelMapping (indexing by process, here + Then we can use variables from the status as inputs or outputs, and model parameters from the called-model bundle (indexing by process, here using "$(process_name)" as the process name), and meteorology variables. Note that our example model has an hard-dependency on another process called `other_process_name` that is called using the {#8abeff}run!(){/#8abeff} function with the process as the first argument: `run!(model.other_process_name, models, status, meteo, constants, extra)`. - If your model can be run in parallel, you can also add traits to your model type so `PlantSimEngine` knows - it can safely parallelize the computation: - - - over space (*i.e.* over objects): - - ```@example usepkg - PlantSimEngine.ObjectDependencyTrait(::Type{<:$(dummy_type_name)}) = PlantSimEngine.IsObjectIndependent() - ``` - - - over time (*i.e.* time-steps): - - ```@example usepkg - PlantSimEngine.TimeStepDependencyTrait(::Type{<:$(dummy_type_name)}) = PlantSimEngine.IsTimeStepIndependent() - ``` - !!! tip "Variables and parameters usage" Note that {#8abeff}run!(){/#8abeff} takes six arguments: the model type (used - for dispatch), the ModelMapping, the status, the meteorology, the constants and + for dispatch), the called-model bundle, the status, the meteorology, the constants and any extra values. Then we can use variables from the status as inputs or outputs, model parameters - from the ModelMapping (indexing by process, here using "$(process_name)" as the + from the called-model bundle (indexing by process, here using "$(process_name)" as the process name), and meteorology variables. """ ) diff --git a/src/run.jl b/src/run.jl deleted file mode 100644 index 9b8f6122c..000000000 --- a/src/run.jl +++ /dev/null @@ -1,617 +0,0 @@ -""" - run!(object, meteo, constants, extra=nothing; check=true, executor=Floops.ThreadedEx()) - run!(object, mapping, meteo, constants, extra; nsteps, outputs, check, executor) - -Run the simulation for each model in the model list in the correct order, *i.e.* respecting -the dependency graph. - -If several time-steps are given, the models are run sequentially for each time-step. - -# Arguments - -- `object`: a [`ModelMapping`](@ref) for single-scale runs, or a plant graph (MTG) for multiscale runs. -- `meteo`: a [`PlantMeteo.TimeStepTable`](https://palmstudio.github.io/PlantMeteo.jl/stable/API/#PlantMeteo.TimeStepTable) of -[`PlantMeteo.Atmosphere`](https://palmstudio.github.io/PlantMeteo.jl/stable/API/#PlantMeteo.Atmosphere) or a single `PlantMeteo.Atmosphere`. - When meteo is provided, `duration` must be present and strictly positive. -- `constants`: a [`PlantMeteo.Constants`](https://palmstudio.github.io/PlantMeteo.jl/stable/API/#PlantMeteo.Constants) object, or a `NamedTuple` of constant keys and values. -- `extra`: extra parameters, not available for simulation of plant graphs (the simulation object is passed using this). -- `check`: if `true`, check the validity of the model list before running the simulation (takes a little bit of time), and return more information while running. -- `executor`: the [`Floops`](https://juliafolds.github.io/FLoops.jl/stable/) executor used to run the simulation either in sequential (`executor=SequentialEx()`), in a -multi-threaded way (`executor=ThreadedEx()`, the default), or in a distributed way (`executor=DistributedEx()`). -- `mapping`: a [`ModelMapping`](@ref) between MTG scales and models. -- `nsteps`: the number of time-steps to run, only needed if no meteo is given (else it is infered from it). -- `outputs`: the outputs to get in dynamic for each node type of the MTG. -- `return_requested_outputs`: when `true` in MTG multi-rate runs, return requested resampled outputs directly - as second return value. -- `requested_outputs_sink`: sink used to materialize requested outputs when `return_requested_outputs=true`. - -# Returns - -Returns status outputs (and optionally requested exports). -For MTG multi-rate runs with `return_requested_outputs=true`, returns -`(status_outputs, requested_outputs)`. - -# Details - -## Model execution - -The models are run according to the dependency graph. If a model has a soft dependency on another -model (*i.e.* its inputs are computed by another model), the other model is run first. If a model -has several soft dependencies, the parents (the soft dependencies) are always computed first. - -## Parallel execution - -Users can ask for parallel execution by providing a compatible executor to the `executor` argument. The package will also automatically -check if the execution can be parallelized. If it is not the case and the user asked for a parallel computation, it return a warning and run the simulation sequentially. -We use the [`Floops`](https://juliafolds.github.io/FLoops.jl/stable/) package to run the simulation in parallel. That means that you can provide any compatible executor to the `executor` argument. -You can take a look at [FoldsThreads.jl](https://github.com/JuliaFolds/FoldsThreads.jl) for extra thread-based executors, [FoldsDagger.jl](https://github.com/JuliaFolds/FoldsDagger.jl) for -Transducers.jl-compatible parallel fold implemented using the Dagger.jl framework, and soon [FoldsCUDA.jl](https://github.com/JuliaFolds/FoldsCUDA.jl) for GPU computations -(see [this issue](https://github.com/VirtualPlantLab/PlantSimEngine.jl/issues/22)) and [FoldsKernelAbstractions.jl](https://github.com/JuliaFolds/FoldsKernelAbstractions.jl). You can also take a look at -[ParallelMagics.jl](https://github.com/JuliaFolds/ParallelMagics.jl) to check if automatic parallelization is possible. - -# Example - -Import the packages: - -```jldoctest run -julia> using PlantSimEngine, PlantMeteo; -``` - -Load the dummy models given as example in the `Examples` sub-module: - -```jldoctest run -julia> using PlantSimEngine.Examples; -``` - -Create a model mapping: - -```jldoctest run -julia> mapping = PlantSimEngine.ModelMapping(Process1Model(1.0), Process2Model(), Process3Model(); status = (var1=1.0, var2=2.0)); -``` - -Create meteo data: - -```jldoctest run -julia> meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_f=300.0); -``` - -Run the simulation: - -```jldoctest run -julia> outputs_sim = run!(mapping, meteo); -``` - -Get the results: - -```jldoctest run -julia> (outputs_sim[:var4],outputs_sim[:var6]) -([12.0], [41.95]) -``` -""" -run! - -function adjust_weather_timesteps_to_given_length(desired_length, meteo) - # This isn't ideal in terms of codeflow, but check_dimensions will kick in later - # And determine whether there is a status vector length discrepancy - - if DataFormat(meteo) == TableAlike() - meteo_adjusted = TimeStepTable{Atmosphere}(meteo) - if get_nsteps(meteo) == 1 - return Tables.rows(meteo_adjusted)[1] - end - return Tables.rows(meteo_adjusted) - end - - if isnothing(meteo) - return Weather(repeat([Atmosphere(NamedTuple())], desired_length)) - elseif get_nsteps(meteo) == 1 && desired_length > 1 - if isa(meteo, Atmosphere) - return Weather(repeat([meteo], desired_length)) - end - else - return meteo # If e.g. single Atmosphere - end -end - - -function _single_scale_model_set_from_mapping(mapping::ModelMapping{SingleScale}) - mapping.data -end - -function _single_scale_model_set_from_mapping(::ModelMapping{MultiScale}) - error("This `ModelMapping` is a multiscale mapping. ", "Use `run!(mtg, mapping, ...)` for multiscale mappings.") -end - -function run!( - mapping::ModelMapping{SingleScale}, - meteo=nothing, - constants=PlantMeteo.Constants(), - extra=nothing; - tracked_outputs=nothing, - check=true, - executor=ThreadedEx(), - return_requested_outputs=false, - requested_outputs_sink=DataFrames.DataFrame -) - _validate_meteo_duration(meteo) - model_set = _single_scale_model_set_from_mapping(mapping) - _run_single_scale_model_set( - model_set, - meteo, - constants, - extra; - tracked_outputs=tracked_outputs, - check=check, - executor=executor, - return_requested_outputs=return_requested_outputs - ) -end - -function run!( - ::ModelMapping{MultiScale}, - meteo=nothing, - constants=PlantMeteo.Constants(), - extra=nothing; - tracked_outputs=nothing, - check=true, - executor=ThreadedEx(), - return_requested_outputs=false, - requested_outputs_sink=DataFrames.DataFrame -) - error("This `ModelMapping` is a multiscale mapping. ", "Use `run!(mtg, mapping, ...)` for multiscale mappings.") -end - -# User entry point, which uses traits to dispatch to the correct method. -# The traits are defined in table_traits.jl -# and define either TableAlike, TreeAlike or SingletonAlike objects. -function run!( - object, - meteo=nothing, - constants=PlantMeteo.Constants(), - extra=nothing; - tracked_outputs=nothing, - check=true, - executor=ThreadedEx(), - return_requested_outputs=false, - requested_outputs_sink=DataFrames.DataFrame -) - _validate_meteo_duration(meteo) - run!( - DataFormat(object), - object, - meteo, - constants, - extra; - tracked_outputs, - check, - executor, - return_requested_outputs, - requested_outputs_sink - ) -end - -function run!( - object::GraphSimulation, - meteo=nothing, - constants=PlantMeteo.Constants(), - extra=nothing; - tracked_outputs=nothing, - check=true, - executor=ThreadedEx(), - return_requested_outputs=false, - requested_outputs_sink=DataFrames.DataFrame -) - _validate_meteo_duration(meteo) - run!( - TreeAlike(), - object, - meteo, - constants, - extra; - tracked_outputs=tracked_outputs, - check=check, - executor=executor, - return_requested_outputs=return_requested_outputs, - requested_outputs_sink=requested_outputs_sink, - ) -end - -########################################################################################## -## Single-scale simulations -########################################################################################## - -# 2 - One object, one or multiple meteo time-step(s), with vectors provided in the status -# (meaning a single meteo timestep might be expanded to fit the status vector size) -function run!( - ::SingletonAlike, - object::T, - meteo=nothing, - constants=PlantMeteo.Constants(), - extra=nothing; - tracked_outputs=nothing, - check=true, - executor=ThreadedEx(), - return_requested_outputs=false, - requested_outputs_sink=DataFrames.DataFrame -) where {T<:ModelMapping{SingleScale}} - model_set = _single_scale_model_set_from_mapping(object) - - _run_single_scale_model_set( - model_set, - meteo, - constants, - extra; - tracked_outputs=tracked_outputs, - check=check, - executor=executor, - return_requested_outputs=return_requested_outputs - ) -end - -function _run_single_scale_model_set( - object::SingleScaleModelSet, - meteo=nothing, - constants=PlantMeteo.Constants(), - extra=nothing; - tracked_outputs=nothing, - check=true, - executor=ThreadedEx(), - return_requested_outputs=false -) - - tracked_outputs isa OutputRequest && error("`OutputRequest` is only supported for MTG multi-rate simulations.") - tracked_outputs isa AbstractVector{<:OutputRequest} && error("`OutputRequest` is only supported for MTG multi-rate simulations.") - return_requested_outputs && error("`return_requested_outputs=true` is only supported for MTG multi-rate simulations.") - - meteo_adjusted = adjust_weather_timesteps_to_given_length(get_status_vector_max_length(object.status), meteo) - nsteps = get_nsteps(meteo_adjusted) - validate_meteo_inputs(object.models, meteo_adjusted) - - dep_graph = dep!(object, nsteps) - - if check - # Check if the meteo data and the status have the same length (or length 1) - check_dimensions(object, meteo_adjusted) - - if length(dep_graph.not_found) > 0 - error( - "The following processes are missing to run the single-scale ModelMapping: ", - dep_graph.not_found - ) - end - end - - - if executor != SequentialEx() && nsteps > 1 - if !timestep_parallelizable(dep_graph) - is_ts_parallel = which_timestep_parallelizable(dep_graph) - mods_not_parallel = join([i.second.first for i in is_ts_parallel[findall(x -> x.second.second == false, is_ts_parallel)]], "; ") - - check && @warn string( - "A parallel executor was provided (`executor=$(executor)`) but some models cannot be run in parallel: $mods_not_parallel. ", - "The simulation will be run sequentially. Use `executor=SequentialEx()` to remove this warning." - ) maxlog = 1 - else - outputs_preallocated_mt = pre_allocate_outputs(object, tracked_outputs, nsteps; type_promotion=object.type_promotion, check=check) - local vars = length(outputs_preallocated_mt) > 0 ? keys(outputs_preallocated_mt[1]) : NamedTuple() - status_flattened_template, vector_variables_mt = flatten_status(object.status) - - # Computing time-steps in parallel: - @floop executor for i in 1:nsteps - @init begin - status_flattened = deepcopy(status_flattened_template) - roots = collect(dep_graph.roots) - end - meteo_i = meteo_adjusted[i] - set_variables_at_timestep!(status_flattened, status(object), vector_variables_mt, i) - for (process, node) in roots - run_node!(object, node, i, status_flattened, meteo_i, constants, extra) - end - for var in vars - setproperty!(outputs_preallocated_mt[i], var, status_flattened[var]) - end - end - return outputs_preallocated_mt - end - end - - outputs_preallocated = pre_allocate_outputs(object, tracked_outputs, nsteps; type_promotion=object.type_promotion, check=check) - status_flattened, vector_variables = flatten_status(status(object)) - - # Not parallelizable over time-steps, it means some values depend on the previous value. - # In this case we propagate the values of the variables from one time-step to the other, except for - # the variables the user provided for all time-steps. - roots = collect(dep_graph.roots) - - # this bit is necessary for DataFrameRow meteos, see XPalm tests - if nsteps == 1 - for (process, node) in roots - run_node!(object, node, 1, status_flattened, meteo_adjusted, constants, extra) - end - save_results!(status_flattened, outputs_preallocated, 1) - else - - for (i, meteo_i) in enumerate(meteo_adjusted) - for (process, node) in roots - run_node!(object, node, i, status_flattened, meteo_i, constants, extra) - end - save_results!(status_flattened, outputs_preallocated, i) - i + 1 <= nsteps && set_variables_at_timestep!(status_flattened, status(object), vector_variables, i + 1) - end - end - - return outputs_preallocated -end - -# Not exposed to the user : -# for each dependency node in the graph (always one time-step, one object), actual workhorse -function run_node!( - object::T, - node::SoftDependencyNode, - i, # time-step to index into the dependency node (to know if the model has been called already) - st, - meteo, - constants, - extra -) where {T<:SingleScaleModelSet} - - # Check if all the parents have been called before the child: - if !AbstractTrees.isroot(node) && any([p.simulation_id[i] <= node.simulation_id[i] for p in node.parent]) - # If not, this node should be called via another parent - return nothing - end - - # Actual call to the model: - run!(node.value, object.models, st, meteo, constants, extra) - node.simulation_id[i] += 1 # increment the simulation id, to know if the model has been called already - - # Recursively visit the children (soft dependencies only, hard dependencies are handled by the model itself): - for child in node.children - #! check if we can run this safely in a @floop loop. I would say no, - #! because we are running a parallel computation above already, modifying the node.simulation_id, - #! which is not thread-safe. - run_node!(object, child, i, st, meteo, constants, extra) - end -end - - -########################################################################################## -### Multiscale simulations -########################################################################################## - -# Another user entry point -# If we pass an MTG and a mapping, then we use them to compute a GraphSimulation object -# that we then use with the generic run! entry point. -function _multirate_tracked_outputs(tracked_outputs) - if isnothing(tracked_outputs) - return nothing, OutputRequest[] - elseif tracked_outputs isa OutputRequest - return nothing, OutputRequest[tracked_outputs] - elseif tracked_outputs isa AbstractVector{<:OutputRequest} - return nothing, collect(tracked_outputs) - end - return tracked_outputs, OutputRequest[] -end - -function _active_dependency_processes(dep_graph::DependencyGraph) - active = Set{Tuple{Symbol,Symbol}}() - for node in traverse_dependency_graph(dep_graph, false) - push!(active, (node.scale, node.process)) - end - return active -end - -function run!( - object::MultiScaleTreeGraph.Node, - mapping::ModelMapping, - meteo=nothing, - constants=PlantMeteo.Constants(), - extra=nothing; - nsteps=nothing, - tracked_outputs=nothing, - type_promotion=_type_promotion(mapping), - check=true, - executor=ThreadedEx(), - return_requested_outputs=false, - requested_outputs_sink=DataFrames.DataFrame -) - _validate_meteo_duration(meteo) - effective_multirate = _effective_multirate(mapping, meteo) - isnothing(nsteps) && (nsteps = get_nsteps(meteo)) - meteo_adjusted = if effective_multirate && meteo isa TimeStepTable{<:Atmosphere} - # Keep TimeStepTable intact in MTG multi-rate runs so model-clock meteo - # sampling/aggregation can use PlantMeteo sampler APIs. - meteo - elseif DataFormat(meteo) == TableAlike() - nsteps == 1 ? Tables.rows(meteo)[1] : meteo - else - adjust_weather_timesteps_to_given_length(nsteps, meteo) - end - status_outputs, output_requests = _multirate_tracked_outputs(tracked_outputs) - !effective_multirate && !isempty(output_requests) && error("`OutputRequest` requires a multirate `ModelMapping`.") - return_requested_outputs && !effective_multirate && error("`return_requested_outputs=true` requires a multirate `ModelMapping`.") - - # NOTE : replace_mapping_status_vectors_with_generated_models is assumed to have already run if used - # otherwise there might be vector length conflicts with timesteps - sim = GraphSimulation(object, mapping, nsteps=nsteps, check=check, outputs=status_outputs, type_promotion=type_promotion) - result = run!( - sim, - meteo_adjusted, - constants, - extra; - check=check, - executor=executor, - tracked_outputs=output_requests, - return_requested_outputs=return_requested_outputs, - requested_outputs_sink=requested_outputs_sink - ) - - if return_requested_outputs - return result - end - - return outputs(sim) -end - -function run!( - ::TreeAlike, - object::GraphSimulation, - meteo, - constants=PlantMeteo.Constants(), - extra=nothing; - tracked_outputs=nothing, - check=true, - executor=ThreadedEx(), - return_requested_outputs=false, - requested_outputs_sink=DataFrames.DataFrame -) - - effective_multirate = _effective_multirate(object) - dep_graph = object.dependency_graph - models = get_models(object) - _validate_meteo_duration(meteo) - timeline = _timeline_context(meteo) - meteo_sampler = effective_multirate ? _prepare_meteo_sampler(meteo) : nothing - runtime_clock_rows = _runtime_clock_rows(object, timeline, dep_graph) - effective_executor = executor - # st = status(object) - validate_meteo_inputs(get_model_specs(object), meteo) - _validate_meteo_derived_timestep_requirements!(runtime_clock_rows, timeline) - if effective_multirate - if executor != SequentialEx() - @warn string( - "Multi-rate MTG runs currently execute sequentially. ", - "Provided `executor=$(executor)` is ignored in this mode. ", - "Use `executor=SequentialEx()` to silence this warning." - ) maxlog = 1 - effective_executor = SequentialEx() - end - _warn_if_no_model_runs_at_base_timestep(runtime_clock_rows, timeline) - validate_canonical_publishers(object) - prepare_output_requests!(object, tracked_outputs, timeline) - configure_temporal_buffers!(object, timeline) - elseif return_requested_outputs - error("`return_requested_outputs=true` requires a multirate `ModelMapping`.") - end - - !isnothing(extra) && error("Extra parameters are not allowed for the simulation of an MTG (already used for statuses).") - - nsteps = get_nsteps(meteo) - - # if this function is called directly with an atmosphere, don't use the Rows interface - if nsteps == 1 - roots = collect(dep_graph.roots) - for (process_key, dependency_node) in roots - run_node_multiscale!(object, dependency_node, 1, models, meteo, constants, object, check, effective_executor, effective_multirate, timeline, meteo_sampler) - end - effective_multirate && update_requested_outputs!(object, _time_from_step(1, timeline)) - save_results!(object, 1) - else - for (i, meteo_i) in enumerate(Tables.rows(meteo)) - roots = collect(dep_graph.roots) - for (process_key, dependency_node) in roots - run_node_multiscale!(object, dependency_node, i, models, meteo_i, constants, object, check, effective_executor, effective_multirate, timeline, meteo_sampler) - end - effective_multirate && update_requested_outputs!(object, _time_from_step(i, timeline)) - # At the end of the time-step, we save the results of the simulation in the object: - save_results!(object, i) - end - end - - # save_results! resizes the outputs melodramatically because the total # of nodes at a given scale can't always be known - # if models create organs, so shrink it down to the final size here - for (organ, index) in object.outputs_index - resize!(outputs(object)[organ], index - 1) - end - - if return_requested_outputs - return outputs(object), collect_outputs(object; sink=requested_outputs_sink) - end - - return outputs(object) -end - - -# Function that runs on dependency graph nodes, actual workhorse : -function run_node_multiscale!( - object::T, - node::SoftDependencyNode, - i, # time-step to index into the dependency node (to know if the model has been called already) - models, - meteo, - constants, - extra::T, # we pass the simulation object as extra so we can access its parameters during simulation - check, - executor, - multirate, - timeline::TimelineContext, - meteo_sampler; - meteo_provider=nothing, - after_model_run=nothing, - skip_model_run=nothing, -) where {T<:GraphSimulation} # T is the status of each node by organ type - - # run!(status(object), dependency_node, meteo, constants, extra) - # Check if all the parents have been called before the child: - if !AbstractTrees.isroot(node) && any([p.simulation_id[1] <= node.simulation_id[1] for p in node.parent]) - # If not, this node should be called via another parent - return nothing - end - - node_statuses = status(object)[node.scale] # Get the status of the nodes at the current scale - models_at_scale = models[node.scale] - model_specs_at_scale = get_model_specs(object)[node.scale] - model_spec = get(model_specs_at_scale, node.process, as_model_spec(node.value)) - model_clock = _model_clock(model_spec, node.value, timeline) - t = _time_from_step(i, timeline) - - skip_node = !isnothing(skip_model_run) && skip_model_run(node) - if !skip_node - for st in node_statuses # for each node status at the current scale (potentially in parallel over nodes) - should_run = !multirate || _should_run_at_time(model_clock, t) - !should_run && continue - if multirate - resolve_inputs_from_temporal_state!(object, node, st, t, model_spec, timeline) - end - meteo_for_model = if isnothing(meteo_provider) - multirate ? _sample_meteo_for_model(meteo_sampler, meteo, i, model_clock, model_spec) : meteo - else - meteo_provider(node, st, i, t, model_clock, model_spec, meteo, meteo_sampler, multirate) - end - # Actual call to the model: - run!(node.value, models_at_scale, st, meteo_for_model, constants, extra) - if !isnothing(after_model_run) - after_model_run(node, model_spec, st, i, t) - end - if multirate - update_temporal_state_outputs!(object, node, model_spec, st, t) - end - end - end - - node.simulation_id[1] += 1 # increment the simulation id, to remember that the model has been called already - - # Recursively visit the children (soft dependencies only, hard dependencies are handled by the model itself): - for child in node.children - #! check if we can run this safely in a @floop loop. I would say no, - #! because we are running a parallel computation above already, modifying the node.simulation_id, - #! which is not thread-safe yet. - run_node_multiscale!( - object, - child, - i, - models, - meteo, - constants, - extra, - check, - executor, - multirate, - timeline, - meteo_sampler; - meteo_provider=meteo_provider, - after_model_run=after_model_run, - skip_model_run=skip_model_run, - ) - end -end diff --git a/src/scene_object_api.jl b/src/scene_object_api.jl index 61a7a971f..2b7ae41ee 100644 --- a/src/scene_object_api.jl +++ b/src/scene_object_api.jl @@ -31,7 +31,7 @@ mutable struct Object end """ - ObjectTemplate(applications=(); kind=nothing, species=nothing, mapping=applications, parameters=NamedTuple()) + ObjectTemplate(applications=(); kind=nothing, species=nothing, parameters=NamedTuple()) Reusable model-application bundle for one kind of scene object, such as a plant species. Each mounted `ObjectInstance` scopes unqualified `AppliesTo(...)` @@ -56,10 +56,9 @@ function ObjectTemplate( applications=(); kind=nothing, species=nothing, - mapping=applications, parameters=NamedTuple(), ) - normalized_applications = _as_tuple(mapping) + normalized_applications = _as_tuple(applications) return ObjectTemplate( _maybe_symbol(kind), _maybe_symbol(species), @@ -153,7 +152,7 @@ end process(model::ObjectModelOverrides) = process(model.base) inputs_(model::ObjectModelOverrides) = inputs_(model.base) outputs_(model::ObjectModelOverrides) = outputs_(model.base) -dep(model::ObjectModelOverrides, nsteps=1) = dep(model.base, nsteps) +dep(model::ObjectModelOverrides) = dep(model.base) timespec(model::ObjectModelOverrides) = timespec(model.base) output_policy(model::ObjectModelOverrides) = output_policy(model.base) meteo_inputs_(model::ObjectModelOverrides) = meteo_inputs_(model.base) @@ -337,11 +336,11 @@ function Scene( objects, mounted_instances = _collect_scene_items(items, instances) instance_ids = _prepare_object_instances!(objects, mounted_instances) mounted_applications = _mount_object_instance_applications(mounted_instances, instance_ids) - normalized_applications = _as_tuple(applications) - all_applications = (normalized_applications..., mounted_applications...) + normalized_applications = collect(Any, _as_tuple(applications)) + append!(normalized_applications, mounted_applications) scene = Scene( SceneRegistry(), - all_applications, + normalized_applications, environment, mounted_instances, nothing, @@ -902,11 +901,23 @@ _maybe_symbol(x) = isnothing(x) ? nothing : Symbol(x) const _OBJECT_ADDRESS_SYMBOL_FIELDS = (:kind, :species, :scale, :name, :process, :var, :relation, :application) +function _maybe_symbol_collection(value) + value isa Tuple && return Tuple(_maybe_symbol(item) for item in value) + value isa AbstractVector && return Tuple(_maybe_symbol(item) for item in value) + return _maybe_symbol(value) +end + function _normalize_object_selector_value(key::Symbol, value) - key in _OBJECT_ADDRESS_SYMBOL_FIELDS && return _maybe_symbol(value) + key in _OBJECT_ADDRESS_SYMBOL_FIELDS && return _maybe_symbol_collection(value) return value end +function _object_matches_selector_value(object_value, requested) + isnothing(requested) && return true + requested isa Tuple && return object_value in requested + return object_value == requested +end + function _normalize_selector_kwargs(kwargs) return NamedTuple{keys(kwargs)}( Tuple(_normalize_object_selector_value(k, v) for (k, v) in pairs(kwargs)) @@ -1396,10 +1407,10 @@ function _selector_resolution_error( end function _matches_object_criteria(object::Object; scale=nothing, kind=nothing, species=nothing, name=nothing) - isnothing(scale) || object.scale == scale || return false - isnothing(kind) || object.kind == kind || return false - isnothing(species) || object.species == species || return false - isnothing(name) || object.name == name || return false + _object_matches_selector_value(object.scale, scale) || return false + _object_matches_selector_value(object.kind, kind) || return false + _object_matches_selector_value(object.species, species) || return false + _object_matches_selector_value(object.name, name) || return false return true end @@ -1619,7 +1630,11 @@ function _compile_scene(scene::Scene, raw_specs) call_bindings = _compile_scene_call_bindings(scene, applications) _validate_scene_writers!(applications, call_bindings) _prepare_scene_output_statuses!(scene, applications) - input_bindings = _compile_scene_input_bindings(scene, applications) + input_bindings = _compile_scene_input_bindings( + scene, + applications, + _manual_call_application_ids(call_bindings), + ) _prepare_scene_bound_input_statuses!(scene, applications, input_bindings) _wire_scene_input_carriers!(scene, input_bindings) _validate_scene_required_inputs!(scene, applications, input_bindings) @@ -2102,7 +2117,11 @@ function _matching_input_source_applications( return matches end -function _compile_scene_input_bindings(scene::Scene, applications) +function _compile_scene_input_bindings( + scene::Scene, + applications, + manual_application_ids=Set{Symbol}(), +) bindings = CompiledSceneInputBinding[] by_object = _applications_by_object(applications) by_id = Dict(application.id => application for application in applications) @@ -2128,7 +2147,16 @@ function _compile_scene_input_bindings(scene::Scene, applications) by_id, ) end - _append_inferred_scene_input_bindings!(bindings, scene, application, consumer_id, declared_inputs, by_object, by_id) + application.id in manual_application_ids && continue + _append_inferred_scene_input_bindings!( + bindings, + scene, + application, + consumer_id, + declared_inputs, + by_object, + by_id, + ) end end return bindings @@ -3397,7 +3425,27 @@ function _scene_temporal_source_value( error("Unsupported scene temporal input policy `$(typeof(policy))`.") end +function _scene_temporal_source_value( + streams, + application_id::Nothing, + source_id::ObjectId, + source_var::Symbol, + time::Real, + policy, + t_start::Real, + timeline, +) + policy isa PreviousTimeStep && return nothing + error( + "Temporal scene input from `$(source_id.value).$(source_var)` has no ", + "source application for policy `$(typeof(policy))`." + ) +end + function _scene_temporal_source_application(compiled::CompiledScene, binding::CompiledSceneInputBinding, source_id::ObjectId) + if binding.policy isa PreviousTimeStep && isempty(binding.source_application_ids) + return nothing + end isempty(binding.source_application_ids) && error( "Temporal scene input `$(binding.input)` from `$(source_id.value).$(binding.source_var)` ", "has no resolved source application. Add `process=` or `application=` to `Inputs(...)`." @@ -3409,11 +3457,16 @@ function _scene_temporal_source_application(compiled::CompiledScene, binding::Co if length(matches) == 1 return only(matches) elseif isempty(matches) + binding.policy isa PreviousTimeStep && return nothing error( "Temporal scene input `$(binding.input)` from `$(source_id.value).$(binding.source_var)` ", "has no source application matching object `$(source_id.value)`." ) end + if binding.policy isa PreviousTimeStep + positions = Dict(application_id => index for (index, application_id) in pairs(compiled.application_order)) + return last(sort(matches; by=application_id -> get(positions, application_id, 0))) + end error( "Temporal scene input `$(binding.input)` from `$(source_id.value).$(binding.source_var)` ", "has ambiguous source applications `$(matches)`. Add `application=...` to `Inputs(...)`." @@ -3431,7 +3484,8 @@ function _scene_temporal_input_value( ) window_steps = _scene_input_window_steps(binding, application, timeline) t_start = float(time) - float(window_steps) + 1.0 - initial = status[binding.input] + initial = _input_value(binding.carrier) + isnothing(initial) && (initial = status[binding.input]) if binding.multiplicity == :many values = [ _scene_temporal_source_value( @@ -4175,7 +4229,7 @@ end function run!( scene::Scene; steps::Integer=1, - constants=nothing, + constants=PlantMeteo.Constants(), tracked_outputs=nothing, ) compiled = refresh_bindings!(scene) @@ -4608,55 +4662,3 @@ function _normalize_binding_origins(origins::NamedTuple, bindings::NamedTuple) end return (; normalized...) end - -function _legacy_multiscale_rhs_from_input_selector(selector::AbstractObjectMultiplicity) - c = criteria(selector) - haskey(c, :scale) || return nothing - - # The current MTG mapping layer only understands scale/variable mappings. - # Keep richer object filters as unified metadata for the future compiler. - unsupported = (:kind, :species, :name, :process, :relation) - any(key -> haskey(c, key) && !isnothing(getproperty(c, key)), unsupported) && return nothing - - scale = c.scale - src_var = haskey(c, :var) ? c.var : nothing - if selector isa Many - return isnothing(src_var) ? [scale] : [scale => src_var] - elseif selector isa One || selector isa OptionalOne - return isnothing(src_var) ? scale : (scale => src_var) - end - return nothing -end - -function _legacy_multiscale_from_value_inputs(bindings::NamedTuple, model=nothing) - mapped = Pair{Symbol,Any}[] - model_input_names = isnothing(model) ? nothing : Set(keys(inputs_(model))) - for (input_var, selector) in pairs(bindings) - input_sym = Symbol(input_var) - if !isnothing(model_input_names) && !(input_sym in model_input_names) - continue - end - selector isa AbstractObjectMultiplicity || continue - rhs = _legacy_multiscale_rhs_from_input_selector(selector) - isnothing(rhs) && continue - push!(mapped, input_sym => rhs) - end - return mapped -end - -function _merge_legacy_multiscale(existing, derived::Vector{Pair{Symbol,Any}}) - isempty(derived) && return existing - if isnothing(existing) - return derived - end - derived_inputs = Set(first(item) for item in derived) - merged = Pair{Any,Any}[] - for item in collect(existing) - mapped_input = first(item) - mapped_input = mapped_input isa PreviousTimeStep ? mapped_input.variable : mapped_input - mapped_input in derived_inputs && continue - push!(merged, item) - end - append!(merged, derived) - return merged -end diff --git a/src/time/multirate.jl b/src/time/multirate.jl index 28aa12b10..b6e5b6d75 100644 --- a/src/time/multirate.jl +++ b/src/time/multirate.jl @@ -1,29 +1,7 @@ """ - ScopeId(kind, id) + ClockSpec(dt, phase=0) -Identifier for a simulation scope (e.g. global scene or plant). -""" -struct ScopeId - kind::Symbol - id::Int -end - -""" - ClockSpec(dt, phase) - -Clock definition for a model/process. - -# Details - -`dt` is the execution interval and `phase` is the offset of the execution grid. -In the current runtime, simulation steps are indexed as `t = 1, 2, 3, ...` (1-based). -A model runs when `t` is aligned with its clock. - -# Examples - -With `dt=24`: -- `ClockSpec(24.0, 1.0)` runs at `t = 1, 25, 49, ...` -- `ClockSpec(24.0, 0.0)` runs at `t = 24, 48, 72, ...` +Execution interval and phase, expressed in simulation steps. """ struct ClockSpec{T<:Real} dt::T @@ -32,81 +10,34 @@ end ClockSpec(dt::T) where {T<:Real} = ClockSpec{T}(dt, zero(T)) -""" - ModelKey(scope, scale, process) - -Unique key for one model process in one scope and scale. -""" -struct ModelKey - scope::ScopeId - scale::Symbol - process::Symbol -end - -""" - OutputKey(scope, scale, node_id, process, var) - -Unique key for one producer output stream. -""" -struct OutputKey - scope::ScopeId - scale::Symbol - node_id::Int - process::Symbol - var::Symbol -end - abstract type SchedulePolicy end -""" - HoldLast() - -Use the latest available producer value. -""" +"""Use the latest available producer value.""" struct HoldLast <: SchedulePolicy end function _as_schedule_policy(policy; context::AbstractString="schedule policy") if policy isa DataType policy <: SchedulePolicy || error( - "Unsupported $(context) type `$(policy)`. ", - "Expected a `SchedulePolicy` type or instance." + "Unsupported $(context) type `$(policy)`. Expected a SchedulePolicy type or instance." ) return try policy() catch - error( - "Unsupported $(context) type `$(policy)`: ", - "this policy type cannot be instantiated without arguments. ", - "Provide a policy instance instead." - ) + error("Schedule policy type `$(policy)` requires constructor arguments.") end elseif policy isa SchedulePolicy return policy end - - error( - "Unsupported $(context) value `$(policy)` of type `$(typeof(policy))`. ", - "Expected a `SchedulePolicy` type or instance." - ) + error("Unsupported $(context) value `$(policy)` of type `$(typeof(policy))`.") end const _INTERPOLATE_MODES = (:linear, :hold) """ - Interpolate() - Interpolate(mode) - Interpolate(mode, extrapolation) Interpolate(; mode=:linear, extrapolation=:linear) -Interpolation policy for fast consumers reading slower producer streams. - -Supported modes: -- `:linear`: linear interpolation between bracket points for real values -- `:hold`: left-hold (previous sample) - -Supported extrapolation modes when no future sample exists: -- `:linear`: linear extrapolation from last two samples when possible -- `:hold`: keep the latest sample +Interpolate a slower producer stream. Both `mode` and `extrapolation` accept +`:linear` or `:hold`. """ struct Interpolate{M<:Symbol,E<:Symbol} <: SchedulePolicy mode::M @@ -114,28 +45,22 @@ struct Interpolate{M<:Symbol,E<:Symbol} <: SchedulePolicy end Interpolate(mode::Symbol) = Interpolate(mode, :linear) -Interpolate(; mode::Symbol=:linear, extrapolation::Symbol=:linear) = Interpolate(mode, extrapolation) - -""" - Integrate() - Integrate(reducer) - -Windowed policy for consumers running at coarser clocks. -Values in the consumer window are reduced with `reducer`. - -Intended meaning: integrate/accumulate quantities over a window (for example -hourly flux to daily total). Default reducer is `SumReducer()`. +Interpolate(; mode::Symbol=:linear, extrapolation::Symbol=:linear) = + Interpolate(mode, extrapolation) -Important: `Integrate(r)` and `Aggregate(r)` are runtime-equivalent when they -use the same reducer `r`; they only differ by default reducer and naming intent. +function _normalize_policy_reducer(reducer) + if reducer isa DataType + reducer <: PlantMeteo.AbstractTimeReducer || error( + "Unsupported reducer type `$(reducer)`." + ) + return reducer() + elseif reducer isa PlantMeteo.AbstractTimeReducer || reducer isa Function + return reducer + end + error("Unsupported reducer value `$(reducer)` of type `$(typeof(reducer))`.") +end -Built-in reducers can be shared with meteo sampling from `PlantMeteo`: -`SumReducer()`, `MeanReducer()`, `MaxReducer()`, `MinReducer()`, `FirstReducer()`, -`LastReducer()`. -You can also provide a callable taking either: -- `values` -- `values, durations_seconds` -""" +"""Reduce a producer window, using a sum by default.""" struct Integrate{R} <: SchedulePolicy reducer::R function Integrate(reducer) @@ -144,26 +69,9 @@ struct Integrate{R} <: SchedulePolicy end end -""" - Aggregate() - Aggregate(reducer) - -Windowed aggregation policy for consumers running at coarser clocks. -Values in the consumer window are reduced with `reducer`. - -Intended meaning: summarize window values as a statistic (for example mean/max). -Default reducer is `MeanReducer()`. - -Important: `Aggregate(r)` and `Integrate(r)` are runtime-equivalent when they -use the same reducer `r`; they only differ by default reducer and naming intent. +Integrate() = Integrate(PlantMeteo.SumReducer()) -Built-in reducers can be shared with meteo sampling from `PlantMeteo`: -`SumReducer()`, `MeanReducer()`, `MaxReducer()`, `MinReducer()`, `FirstReducer()`, -`LastReducer()`. -You can also provide a callable taking either: -- `values` -- `values, durations_seconds` -""" +"""Reduce a producer window, using a mean by default.""" struct Aggregate{R} <: SchedulePolicy reducer::R function Aggregate(reducer) @@ -172,132 +80,29 @@ struct Aggregate{R} <: SchedulePolicy end end -function _normalize_policy_reducer(reducer) - if reducer isa DataType - reducer <: PlantMeteo.AbstractTimeReducer || error( - "Unsupported reducer type `$(reducer)`. ", - "Use a PlantMeteo reducer type/instance or a callable." - ) - return reducer() - elseif reducer isa PlantMeteo.AbstractTimeReducer - return reducer - elseif reducer isa Function - return reducer - end - - error( - "Unsupported reducer value `$(reducer)` of type `$(typeof(reducer))`. ", - "Use a PlantMeteo reducer type/instance or a callable." - ) -end - -Integrate() = Integrate(PlantMeteo.SumReducer()) - Aggregate() = Aggregate(PlantMeteo.MeanReducer()) -abstract type OutputCache end - -mutable struct HoldLastCache{T} <: OutputCache - t::Float64 - v::T -end - -mutable struct InterpolateCache{T} <: OutputCache - t_prev::Float64 - v_prev::T - t_curr::Float64 - v_curr::T -end - -mutable struct IntegrateCache{T<:Real} <: OutputCache - t_prev::Float64 - v_prev::T - acc::T - window_start::Float64 -end - -mutable struct AggregateCache{T<:Real} <: OutputCache - acc::T - n::Int - window_start::Float64 -end - -const OutputStream = Vector{Tuple{Float64,Any}} - -""" - ExportBuffer() - -Compact in-memory storage for requested output rows during runtime. -""" -mutable struct ExportBuffer{ - P<:Symbol, - V<:Symbol, - TI<:AbstractVector{Int}, - NI<:AbstractVector{Int}, - VV<:AbstractVector{Any}, -} - scale::Symbol - process::P - var::V - timestep::TI - node::NI - value::VV -end - -ExportBuffer(scale::Symbol, process::Symbol, var::Symbol) = ExportBuffer(scale, process, var, Int[], Int[], Any[]) - -""" - OutputExportPlan - -Resolved online output export request used by the multi-rate runtime. -""" -struct OutputExportPlan{POL<:SchedulePolicy,C,MS} - name::Symbol - scale::Symbol - var::Symbol - process::Symbol - policy::POL - clock::C - model_spec::MS - source_dt::Float64 - source_sample_duration_seconds::Float64 -end - -""" - TemporalState(caches, last_run, streams, producer_horizons, export_plans, export_rows) - TemporalState() - -Temporal storage for multi-rate simulations. -`caches` stores producer hold-last outputs. -`last_run` stores last execution time per model key. -`streams` stores bounded producer `(time, value)` samples used for windowed and -interpolated policies. -`producer_horizons` stores required retention horizon per producer -`(scale, process, var)`. -`export_plans` stores resolved online export requests prepared before the run. -`export_rows` stores online-exported rows keyed by request name. -""" -mutable struct TemporalState{ - C<:AbstractDict{OutputKey,OutputCache}, - L<:AbstractDict{ModelKey,Float64}, - S<:AbstractDict{OutputKey,OutputStream}, - H<:AbstractDict{Tuple{Symbol,Symbol,Symbol},Float64}, - P<:AbstractVector{<:OutputExportPlan}, - R<:AbstractDict{Symbol,ExportBuffer} -} - caches::C - last_run::L - streams::S - producer_horizons::H - export_plans::P - export_rows::R -end - -TemporalState() = TemporalState( - Dict{OutputKey,OutputCache}(), - Dict{ModelKey,Float64}(), - Dict{OutputKey,OutputStream}(), - Dict{Tuple{Symbol,Symbol,Symbol},Float64}(), - OutputExportPlan[], - Dict{Symbol,ExportBuffer}() +function _validate_policy_instance( + scale::Symbol, + process::Symbol, + input::Symbol, + policy::SchedulePolicy, ) + if policy isa Interpolate + policy.mode in _INTERPOLATE_MODES || error( + "Invalid interpolation mode `$(policy.mode)` for $(scale)/$(process).$(input)." + ) + policy.extrapolation in _INTERPOLATE_MODES || error( + "Invalid extrapolation mode `$(policy.extrapolation)` for $(scale)/$(process).$(input)." + ) + elseif policy isa Union{Integrate,Aggregate} + reducer = policy.reducer + values = [1.0, 2.0] + durations = [1.0, 1.0] + applicable(reducer, values) || applicable(reducer, values, durations) || + error( + "Reducer for $(scale)/$(process).$(input) must accept values or values and durations." + ) + end + return nothing +end diff --git a/src/time/runtime/bindings.jl b/src/time/runtime/bindings.jl deleted file mode 100644 index 01ec5f484..000000000 --- a/src/time/runtime/bindings.jl +++ /dev/null @@ -1,129 +0,0 @@ -""" - _symbol_from_dependency_var(x) - -Extract a symbol variable name from dependency graph variable descriptors -(`Symbol`, `PreviousTimeStep`, `MappedVar`). -""" -function _symbol_from_dependency_var(x) - if x isa Symbol - return x - elseif x isa ProducerVariable - return x.input - elseif x isa PreviousTimeStep - return x.variable - elseif x isa MappedVar - mv = mapped_variable(x) - return mv isa PreviousTimeStep ? mv.variable : mv - else - return nothing - end -end - -function _source_symbol_from_dependency_var(x) - if x isa ProducerVariable - return x.source - elseif x isa MappedVar - sv = source_variable(x) - return sv isa PreviousTimeStep ? sv.variable : sv - end - return _symbol_from_dependency_var(x) -end - -""" - _push_candidate_producer!(candidates, process_key, vars, input_var) - -Append producer candidates `(process, source_var)` when `vars` contains -`input_var`. -""" -function _push_candidate_producer!(candidates::Vector{Tuple{Symbol,Symbol}}, process_key, vars, input_var::Symbol) - process = Symbol(process_key) - for v in vars - s = _symbol_from_dependency_var(v) - isnothing(s) && continue - if s == input_var - source = _source_symbol_from_dependency_var(v) - source isa Symbol && push!(candidates, (process, source)) - end - end -end - -""" - _collect_candidate_producers!(candidates, parent_vars, input_var) - -Recursively collect candidate producers from nested dependency metadata. -""" -function _collect_candidate_producers!(candidates::Vector{Tuple{Symbol,Symbol}}, parent_vars::NamedTuple, input_var::Symbol) - for (k, v) in pairs(parent_vars) - if v isa NamedTuple - _collect_candidate_producers!(candidates, v, input_var) - elseif v isa Tuple || v isa AbstractVector - _push_candidate_producer!(candidates, k, v, input_var) - end - end -end - -""" - _candidate_producers(node, input_var) - -Return unique `(process, var)` producer candidates for `input_var` from the -soft-dependency parents of `node`. -""" -function _candidate_producers(node::SoftDependencyNode, input_var::Symbol) - node.parent_vars === nothing && return Tuple{Symbol,Symbol}[] - c = Tuple{Symbol,Symbol}[] - _collect_candidate_producers!(c, node.parent_vars, input_var) - unique(c) -end - -""" - _parse_input_binding(binding) - -Normalize one `InputBindings` entry to `(process, var, scale, policy)`. -Accepts shorthand forms (`Symbol`, `Pair{Symbol,Symbol}`, `NamedTuple`). -""" -function _parse_input_binding(binding) - if binding isa Symbol - return (process=binding, var=nothing, scale=nothing, policy=HoldLast()) - elseif binding isa Pair{Symbol,Symbol} - return (process=first(binding), var=last(binding), scale=nothing, policy=HoldLast()) - elseif binding isa NamedTuple - process = haskey(binding, :process) ? binding.process : nothing - var = haskey(binding, :var) ? binding.var : nothing - scale = if haskey(binding, :scale) - sc = binding.scale - if isnothing(sc) - nothing - elseif sc isa Symbol - sc - else - error("Input binding scale must be a `Symbol`, got `$(typeof(sc))` for `$(repr(sc))`.") - end - else - nothing - end - policy = haskey(binding, :policy) ? binding.policy : HoldLast() - if policy isa DataType && policy <: SchedulePolicy - policy = policy() - end - return (process=process, var=var, scale=scale, policy=policy) - else - return nothing - end -end - -""" - _source_scale_for_process(node, process) - -Resolve the scale of a producer process from the current node parent links. -Falls back to the current node scale when no explicit parent match exists. -""" -function _source_scale_for_process(node::SoftDependencyNode, process::Symbol) - if node.parent !== nothing - for p in node.parent - if p.process == process - return p.scale - end - end - end - return node.scale -end diff --git a/src/time/runtime/clocks.jl b/src/time/runtime/clocks.jl index 47c7feff8..dfe57d6b4 100644 --- a/src/time/runtime/clocks.jl +++ b/src/time/runtime/clocks.jl @@ -1,408 +1,235 @@ -""" - TimelineContext(base_step_seconds) - -Internal timing context for one simulation run. -`base_step_seconds` is the duration of one simulation step in seconds. -""" +"""Internal timing context for one scene run.""" struct TimelineContext base_step_seconds::Float64 end -""" - _time_from_step(i, timeline) - -Convert a 1-based integer timestep index to the floating runtime time. -""" _time_from_step(i, ::TimelineContext) = float(i) -function _period_to_seconds(p::Dates.Period) - p isa Dates.FixedPeriod || error( - "Unsupported non-fixed period `$(typeof(p))` in timestep specification. ", - "Use fixed periods such as `Second`, `Minute`, `Hour`, `Day`." +timestep_hint(model::AbstractModel) = timestep_hint(typeof(model)) +timestep_hint(::Type{<:AbstractModel}) = nothing + +meteo_hint(model::AbstractModel) = meteo_hint(typeof(model)) +meteo_hint(::Type{<:AbstractModel}) = nothing + +struct _ResolvedTimeStepHint + fixed::Union{Nothing,Dates.FixedPeriod} + range::Union{Nothing,Tuple{Dates.FixedPeriod,Dates.FixedPeriod}} + preferred::Union{Nothing,Symbol,Dates.FixedPeriod} +end + +_seconds_from_period(period::Dates.FixedPeriod) = + float(Dates.value(Dates.Millisecond(period))) * 1.0e-3 + +function _normalize_required_timestep_hint(scale::Symbol, process::Symbol, required) + if required isa Dates.FixedPeriod + _seconds_from_period(required) > 0.0 || + error("Invalid timestep_hint for $(scale)/$(process): period must be positive.") + return required, nothing + elseif required isa Tuple && length(required) == 2 + lower, upper = required + lower isa Dates.FixedPeriod && upper isa Dates.FixedPeriod || error( + "Invalid timestep_hint for $(scale)/$(process): expected two fixed periods." + ) + lower_seconds = _seconds_from_period(lower) + upper_seconds = _seconds_from_period(upper) + 0.0 < lower_seconds <= upper_seconds || error( + "Invalid timestep_hint range for $(scale)/$(process)." + ) + return nothing, (lower, upper) + end + error( + "Invalid timestep_hint for $(scale)/$(process): expected a fixed period or period range." + ) +end + +function _normalize_timestep_hint(scale::Symbol, process::Symbol, hint) + isnothing(hint) && return _ResolvedTimeStepHint(nothing, nothing, nothing) + if hint isa Dates.FixedPeriod || hint isa Tuple + fixed, range = _normalize_required_timestep_hint(scale, process, hint) + return _ResolvedTimeStepHint(fixed, range, nothing) + elseif hint isa NamedTuple + haskey(hint, :required) || error( + "Invalid timestep_hint for $(scale)/$(process): `required` is mandatory." + ) + all(key -> key in (:required, :preferred), keys(hint)) || error( + "Invalid timestep_hint fields for $(scale)/$(process)." + ) + fixed, range = + _normalize_required_timestep_hint(scale, process, hint.required) + preferred = get(hint, :preferred, nothing) + if preferred isa Symbol + preferred in (:finest, :coarsest) || error( + "Invalid timestep_hint preference for $(scale)/$(process)." + ) + elseif !(isnothing(preferred) || preferred isa Dates.FixedPeriod) + error("Invalid timestep_hint preference for $(scale)/$(process).") + end + return _ResolvedTimeStepHint(fixed, range, preferred) + end + error("Invalid timestep_hint for $(scale)/$(process).") +end + +function _normalize_meteo_hint(scale::Symbol, process::Symbol, hint) + isnothing(hint) && return (bindings=nothing, window=nothing) + hint isa NamedTuple || error( + "Invalid meteo_hint for $(scale)/$(process): expected a NamedTuple." + ) + all(key -> key in (:bindings, :window), keys(hint)) || error( + "Invalid meteo_hint fields for $(scale)/$(process)." ) - sec = float(Dates.value(Dates.Second(p))) - sec > 0.0 || error("Invalid duration `$(p)`: expected a strictly positive period.") - return sec + bindings = + haskey(hint, :bindings) ? _normalize_meteo_bindings(hint.bindings) : nothing + window = haskey(hint, :window) ? _normalize_meteo_window(hint.window) : nothing + return (bindings=bindings, window=window) end -function _duration_to_seconds(d) - if d isa Dates.CompoundPeriod - periods = Dates.periods(d) - isempty(periods) && error("Unsupported empty `Dates.CompoundPeriod` in meteo duration.") - sec = sum(_period_to_seconds(p) for p in periods) - sec > 0.0 || error("Invalid meteo duration `$(d)`: expected a strictly positive value.") - return sec - elseif d isa Dates.Period - return _period_to_seconds(d) - elseif d isa Real - sec = float(d) - sec > 0.0 || error("Invalid meteo duration `$(d)`: expected a strictly positive value in seconds.") - return sec +function _period_to_seconds(period::Dates.Period) + period isa Dates.FixedPeriod || error( + "Unsupported non-fixed period `$(typeof(period))`. Use Second, Minute, Hour, or Day." + ) + seconds = float(Dates.value(Dates.Second(period))) + seconds > 0.0 || error("Expected a positive period, got `$(period)`.") + return seconds +end + +function _duration_to_seconds(duration) + if duration isa Dates.CompoundPeriod + periods = Dates.periods(duration) + isempty(periods) && error("Empty CompoundPeriod is not a valid duration.") + seconds = sum(_period_to_seconds(period) for period in periods) + seconds > 0.0 || error("Expected a positive duration, got `$(duration)`.") + return seconds + elseif duration isa Dates.Period + return _period_to_seconds(duration) + elseif duration isa Real + seconds = float(duration) + seconds > 0.0 || error("Expected a positive duration, got `$(duration)`.") + return seconds end return nothing end function _is_default_clock(clock::ClockSpec) - return isapprox(float(clock.dt), 1.0; atol=1.0e-9, rtol=0.0) && - isapprox(float(clock.phase), 0.0; atol=1.0e-9, rtol=0.0) + isapprox(float(clock.dt), 1.0; atol=1.0e-9, rtol=0.0) && + isapprox(float(clock.phase), 0.0; atol=1.0e-9, rtol=0.0) end -function _timestep_to_step_count(ts::Dates.Period, timeline::TimelineContext) - sec = _period_to_seconds(ts) - step = sec / timeline.base_step_seconds - step >= 1.0 || error( - "Model timestep `$(ts)` is shorter than simulation base step ($(timeline.base_step_seconds) seconds). ", - "This runtime does not support sub-step execution." +function _timestep_to_step_count(period::Dates.Period, timeline::TimelineContext) + steps = _period_to_seconds(period) / timeline.base_step_seconds + steps >= 1.0 || error( + "Model timestep `$(period)` is shorter than the simulation base step." ) - return step + return steps end -function _first_table_row(table; context::String="meteo") - rows = Tables.rows(table) - state = iterate(rows) - isnothing(state) && error( - "Cannot infer simulation timestep from empty $(context) table. ", - "Provide at least one row with a valid `duration`." - ) +function _first_table_row(table; context::String="meteorology") + state = iterate(Tables.rows(table)) + isnothing(state) && error("Cannot infer a timestep from an empty $(context) table.") return state[1] end -function _base_step_seconds_from_meteo_row(row; require_duration::Bool=false, context::String="meteo") +function _base_step_seconds_from_meteo_row( + row; + require_duration::Bool=false, + context::String="meteorology", +) if hasproperty(row, :duration) - d = getproperty(row, :duration) - sec = _duration_to_seconds(d) - !isnothing(sec) && return sec - require_duration && error( - "Invalid `duration=$(d)` (type `$(typeof(d))`) in $(context). ", - "Expected a positive Real (seconds), `Dates.Period`, or `Dates.CompoundPeriod`." - ) + duration = getproperty(row, :duration) + seconds = _duration_to_seconds(duration) + !isnothing(seconds) && return seconds + require_duration && error("Invalid duration `$(duration)` in $(context).") elseif require_duration - error( - "Missing required `duration` in $(context). ", - "Meteorology must define a valid per-row duration." - ) + error("Missing required `duration` in $(context).") end return 1.0 end function _validate_meteo_duration(meteo) isnothing(meteo) && return nothing - if meteo isa Atmosphere - _base_step_seconds_from_meteo_row(meteo; require_duration=true, context="meteo") - return nothing - end - - if meteo isa TimeStepTable || DataFormat(meteo) == TableAlike() + _base_step_seconds_from_meteo_row(meteo; require_duration=true) + elseif meteo isa TimeStepTable || DataFormat(meteo) == TableAlike() for (i, row) in enumerate(Tables.rows(meteo)) - _base_step_seconds_from_meteo_row(row; require_duration=true, context="meteo row $(i)") + _base_step_seconds_from_meteo_row( + row; + require_duration=true, + context="meteorology row $(i)", + ) end - return nothing - end - - if DataFormat(meteo) == SingletonAlike() && hasproperty(meteo, :duration) - _base_step_seconds_from_meteo_row(meteo; require_duration=true, context="meteo") - return nothing + elseif DataFormat(meteo) == SingletonAlike() && hasproperty(meteo, :duration) + _base_step_seconds_from_meteo_row(meteo; require_duration=true) end - - # Unknown formats are validated later by run-path specific checks. return nothing end function _timeline_context(meteo) - if meteo isa TimeStepTable - row = _first_table_row(meteo; context="meteo") - return TimelineContext(_base_step_seconds_from_meteo_row(row; require_duration=true, context="meteo")) - elseif meteo isa Atmosphere - return TimelineContext(_base_step_seconds_from_meteo_row(meteo; require_duration=true, context="meteo")) - elseif !isnothing(meteo) && DataFormat(meteo) == TableAlike() - row = _first_table_row(meteo; context="meteo") - return TimelineContext(_base_step_seconds_from_meteo_row(row; require_duration=true, context="meteo")) - elseif !isnothing(meteo) && DataFormat(meteo) == SingletonAlike() && hasproperty(meteo, :duration) - return TimelineContext(_base_step_seconds_from_meteo_row(meteo; require_duration=true, context="meteo")) + if meteo isa TimeStepTable || (!isnothing(meteo) && DataFormat(meteo) == TableAlike()) + row = _first_table_row(meteo) + return TimelineContext( + _base_step_seconds_from_meteo_row(row; require_duration=true) + ) + elseif meteo isa Atmosphere || + (!isnothing(meteo) && DataFormat(meteo) == SingletonAlike() && + hasproperty(meteo, :duration)) + return TimelineContext( + _base_step_seconds_from_meteo_row(meteo; require_duration=true) + ) end return TimelineContext(1.0) end -""" - _clock_from_spec_timestep(ts, timeline) - -Normalize a `ModelSpec.timestep` value to a `ClockSpec` when possible. -Returns `nothing` when no explicit clock can be derived. -""" -function _clock_from_spec_timestep(ts, timeline::TimelineContext) - if ts isa ClockSpec - return ts - elseif ts isa Real - return ClockSpec(float(ts), 0.0) - elseif ts isa Dates.Period - return ClockSpec(_timestep_to_step_count(ts, timeline), 1.0) - else - return nothing - end +function _clock_from_spec_timestep(timestep, timeline::TimelineContext) + timestep isa ClockSpec && return timestep + timestep isa Real && return ClockSpec(float(timestep), 0.0) + timestep isa Dates.Period && + return ClockSpec(_timestep_to_step_count(timestep, timeline), 1.0) + return nothing end -""" - _model_clock(model_spec, model) - -Return the effective execution clock for `model`, using user-provided -`ModelSpec` timestep override when available, otherwise `timespec(model)`. -""" function _model_clock(model_spec, model, timeline::TimelineContext) - spec_ts = isnothing(model_spec) ? nothing : PlantSimEngine.timestep(model_spec) - c = _clock_from_spec_timestep(spec_ts, timeline) - !isnothing(c) && return c - - model_clock = timespec(model) - _is_default_clock(model_clock) && return ClockSpec(1.0, 0.0) - return model_clock + selected = isnothing(model_spec) ? nothing : timestep(model_spec) + clock = _clock_from_spec_timestep(selected, timeline) + !isnothing(clock) && return clock + declared = timespec(model) + return _is_default_clock(declared) ? ClockSpec(1.0, 0.0) : declared end -""" - _should_run_at_time(clock, t) - -Decide whether a model with `clock` should execute at simulation time `t`. -""" -function _should_run_at_time(clock::ClockSpec, t::Float64) - dt = float(clock.dt) - phase = float(clock.phase) - dt <= 0 && error("Invalid model clock: `dt` must be > 0, got $(dt).") - dt <= 1.0 && return true - # Robust phase alignment check for floating clocks. - return isapprox(mod(t - phase, dt), 0.0; atol=1e-8, rtol=0.0) -end - -""" - _window_start_for_clock(clock, t) - -Return the left bound of the consumer window `[t_start, t]` used by windowed -policies (`Integrate`, `Aggregate`) for a given consumer clock. -""" -function _window_start_for_clock(clock::ClockSpec, t::Float64) - dt = float(clock.dt) - dt <= 1.0 && return t - return t - dt + 1.0 -end - -_effective_multirate(mapping::ModelMapping) = is_multirate(mapping) -_effective_multirate(mapping::ModelMapping, meteo) = is_multirate(mapping) -_effective_multirate(sim::GraphSimulation) = is_multirate(sim) - -function _format_seconds_label(sec::Float64) - ms = round(Int, sec * 1000.0) - if ms % 3_600_000 == 0 - return string(Dates.Hour(ms ÷ 3_600_000)) - elseif ms % 60_000 == 0 - return string(Dates.Minute(ms ÷ 60_000)) - elseif ms % 1000 == 0 - return string(Dates.Second(ms ÷ 1000)) - end - return string(Dates.Millisecond(ms)) -end - -struct EffectiveRateSummary - base_step_seconds::Float64 - rates::Dict{Float64,Vector{NamedTuple}} -end - -function Base.show(io::IO, ::MIME"text/plain", summary::EffectiveRateSummary) - println(io, "Effective model rates (base step: ", _format_seconds_label(summary.base_step_seconds), ")") - for rate_sec in sort!(collect(keys(summary.rates))) - entries = summary.rates[rate_sec] - println(io, " - ", _format_seconds_label(rate_sec), " (", length(entries), " model(s))") - for row in sort!(collect(entries); by=x -> (string(x.scale), string(x.process))) - println(io, " • ", row.scale, "/", row.process, " [", row.source, "]") - end - end -end - -""" - effective_rate_summary(mapping::ModelMapping{MultiScale}, meteo) - -Summarize the effective model execution rates implied by `mapping` and `meteo`. -Returns an `EffectiveRateSummary` struct with details on the effective rates and their sources. - -To get the value of the base step used for the simulation, use `summary.base_step_seconds`. -To get the effective rates and their sources, use `summary.rates`, which is a dictionary -mapping each effective rate (in seconds) to a vector of named tuples with keys `:scale`, -`:process`, `:source`, and `:model` describing the models running at that rate and their -source of timing. -""" -function effective_rate_summary(mapping::ModelMapping{MultiScale}, meteo) - _validate_meteo_duration(meteo) - timeline = _timeline_context(meteo) - rows = _runtime_clock_rows(mapping, timeline) - grouped = Dict{Float64,Vector{NamedTuple}}() - - for row in rows - rate_sec = float(row.clock.dt) * timeline.base_step_seconds - entry = (scale=row.scale, process=row.process, source=row.source, model=typeof(row.model)) - push!(get!(grouped, rate_sec, NamedTuple[]), entry) - end - - return EffectiveRateSummary(timeline.base_step_seconds, grouped) +function _runtime_clock_source_for_spec(spec::ModelSpec) + !isnothing(timestep(spec)) && return :modelspec + return _is_default_clock(timespec(model_(spec))) ? :meteo_base_step : + :model_timespec end -function _resolve_meteo_hint_clock(scale::Symbol, process::Symbol, model, timeline::TimelineContext) - base_sec = timeline.base_step_seconds +function _resolve_meteo_hint_clock( + scale::Symbol, + process::Symbol, + model, + timeline::TimelineContext, +) + base_seconds = timeline.base_step_seconds hint = _normalize_timestep_hint(scale, process, timestep_hint(model)) - desired_sec = base_sec reason = nothing - if !isnothing(hint.fixed) - required_sec = _seconds_from_period(hint.fixed) - if !isapprox(required_sec, base_sec; atol=1.0e-9, rtol=0.0) - reason = string( - "Meteo base step ", - _format_seconds_label(base_sec), - " is outside `timestep_hint.required=", - hint.fixed, - "` for `", - scale, - "/", - process, - "`." - ) - end + required = _seconds_from_period(hint.fixed) + isapprox(required, base_seconds; atol=1.0e-9, rtol=0.0) || + (reason = "Meteorology base step is outside `timestep_hint.required=$(hint.fixed)` for `$(scale)/$(process)`.") elseif !isnothing(hint.range) - lo, hi = hint.range - lo_sec = _seconds_from_period(lo) - hi_sec = _seconds_from_period(hi) - if base_sec < lo_sec || base_sec > hi_sec - reason = string( - "Meteo base step ", - _format_seconds_label(base_sec), - " is outside `timestep_hint.required=(", - lo, - ", ", - hi, - ")` for `", - scale, - "/", - process, - "`." - ) - end - end - - dt = desired_sec / base_sec - return ClockSpec(dt, 0.0), reason -end - -function _runtime_clock_rows(mapping::ModelMapping{MultiScale}, timeline::TimelineContext) - specs_by_scale = !isempty(mapping.info.model_specs) ? - mapping.info.model_specs : - Dict(scale => parse_model_specs(scale_mapping) for (scale, scale_mapping) in pairs(mapping)) - - rows = NamedTuple[] - for (scale, specs_at_scale) in pairs(specs_by_scale) - for (process, spec) in pairs(specs_at_scale) - model = model_(spec) - source = _runtime_clock_source_for_spec(spec) - clock = _model_clock(spec, model, timeline) - hint_reason = nothing - if source == :meteo_base_step - clock, hint_reason = _resolve_meteo_hint_clock(scale, process, model, timeline) - end - push!(rows, ( - scale=scale, - process=process, - source=source, - clock=clock, - model=model, - hint_reason=hint_reason, - )) - end - end - - return rows -end - -function _mapping_requires_runtime_multirate(mapping::ModelMapping{MultiScale}, meteo) - isnothing(meteo) && return false - timeline = _timeline_context(meteo) - rows = _runtime_clock_rows(mapping, timeline) - return any(!isapprox(float(row.clock.dt), 1.0; atol=1.0e-9, rtol=0.0) for row in rows) -end - -function _effective_multirate(mapping::ModelMapping{MultiScale}, meteo) - is_multirate(mapping) && return true - return _mapping_requires_runtime_multirate(mapping, meteo) -end - - - -function _runtime_clock_source_for_spec(spec::ModelSpec) - !isnothing(timestep(spec)) && return :modelspec - return _is_default_clock(timespec(model_(spec))) ? :meteo_base_step : :model_timespec -end - -function _runtime_clock_rows(object::GraphSimulation, timeline::TimelineContext, dep_graph::DependencyGraph) - active = _active_dependency_processes(dep_graph) - rows = NamedTuple[] - - for (scale, specs_at_scale) in pairs(get_model_specs(object)) - for (process, spec) in pairs(specs_at_scale) - (scale, process) in active || continue - model = model_(spec) - source = _runtime_clock_source_for_spec(spec) - clock = _model_clock(spec, model, timeline) - hint_reason = nothing - if source == :meteo_base_step - clock, hint_reason = _resolve_meteo_hint_clock(scale, process, model, timeline) - end - push!(rows, ( - scale=scale, - process=process, - source=source, - clock=clock, - model=model, - hint_reason=hint_reason, - )) - end + lower, upper = _seconds_from_period.(hint.range) + lower <= base_seconds <= upper || + (reason = "Meteorology base step is outside `timestep_hint.required=$(hint.range)` for `$(scale)/$(process)`.") end - - return rows + return ClockSpec(1.0, 0.0), reason end - -function _warn_if_no_model_runs_at_base_timestep(rows, timeline::TimelineContext) - isempty(rows) && return nothing - any(isapprox(float(row.clock.dt), 1.0; atol=1.0e-9, rtol=0.0) for row in rows) && return nothing - - source_label(source) = source == :modelspec ? "ModelSpec" : (source == :model_timespec ? "timespec(model)" : "meteo") - details = sort([ - string( - row.scale, - "/", - row.process, - "=", - round(float(row.clock.dt) * timeline.base_step_seconds; digits=6), - "s (", - source_label(row.source), - ")" - ) for row in rows - ]) - - @warn string( - "No model runs at the meteo base timestep (", - timeline.base_step_seconds, - " s). Resolved model timesteps: ", - join(details, ", "), - "." - ) maxlog = 1 - return nothing +function _should_run_at_time(clock::ClockSpec, time::Float64) + dt = float(clock.dt) + phase = float(clock.phase) + dt > 0.0 || error("Clock interval must be positive, got $(dt).") + dt <= 1.0 && return true + return isapprox(mod(time - phase, dt), 0.0; atol=1.0e-8, rtol=0.0) end - -function _validate_meteo_derived_timestep_requirements!(rows, timeline::TimelineContext) - for row in rows - row.source == :meteo_base_step || continue - - if !isnothing(row.hint_reason) - error(row.hint_reason) - end - end - - return nothing +function _window_start_for_clock(clock::ClockSpec, time::Float64) + dt = float(clock.dt) + return dt <= 1.0 ? time : time - dt + 1.0 end diff --git a/src/time/runtime/environment_backends.jl b/src/time/runtime/environment_backends.jl index f439fd375..4ebe55b06 100644 --- a/src/time/runtime/environment_backends.jl +++ b/src/time/runtime/environment_backends.jl @@ -80,8 +80,10 @@ _timeline_context(backend::GlobalConstant) = _timeline_context(environment_meteo function _meteo_row_at_step(meteo, i::Int) isnothing(meteo) && return nothing - get_nsteps(meteo) == 1 && return meteo - return first(Iterators.drop(Tables.rows(meteo), i - 1)) + if meteo isa TimeStepTable || DataFormat(meteo) == TableAlike() + return first(Iterators.drop(Tables.rows(meteo), i - 1)) + end + return meteo end function _available_meteo_variables(meteo) @@ -353,7 +355,7 @@ end """ explain_environment(simulation) -Return a compact description of the environment backend used by a domain +Return a compact description of the environment backend used by a scene simulation. """ function explain_environment(simulation) diff --git a/src/time/runtime/input_resolution.jl b/src/time/runtime/input_resolution.jl deleted file mode 100644 index 105dd8eb6..000000000 --- a/src/time/runtime/input_resolution.jl +++ /dev/null @@ -1,845 +0,0 @@ -""" - _resolved_value_for_source(sim, source_scope, source_scale, source_process, source_var, source_node_id, t) - -Resolve one producer value through hold-last cache lookup. -Returns `(value, found::Bool)`. -""" -function _resolved_value_for_source(sim::GraphSimulation, source_scope::ScopeId, source_scale::Symbol, source_process::Symbol, source_var::Symbol, source_node_id::Int, t::Float64) - key = OutputKey(source_scope, source_scale, source_node_id, source_process, source_var) - cache = get(sim.temporal_state.caches, key, nothing) - if cache isa HoldLastCache - return cache.v, true - end - return nothing, false -end - -_resolution_samples(sim::GraphSimulation, key::OutputKey) = get(sim.temporal_state.streams, key, nothing) - -""" - _resolved_windowed_value_for_source(sim, source_scope, source_scale, source_process, source_var, source_node_id, t_start, t_end, policy) - -Resolve one producer value over `[t_start, t_end]` for windowed policies. -Returns `(value, found::Bool)`. -""" -function _resolved_windowed_value_for_source( - sim::GraphSimulation, - source_scope::ScopeId, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - source_node_id::Int, - t_start::Float64, - t_end::Float64, - policy::SchedulePolicy, - source_sample_duration_seconds::Float64 -) - key = OutputKey(source_scope, source_scale, source_node_id, source_process, source_var) - samples = _resolution_samples(sim, key) - isnothing(samples) && return nothing, false - - if policy isa Union{Integrate,Aggregate} - vals_real = Float64[] - durations_real = Float64[] - for (ts, v) in samples - ts < t_start - 1e-8 && continue - ts > t_end + 1e-8 && continue - v isa Real || return nothing, false - push!(vals_real, float(v)) - push!(durations_real, source_sample_duration_seconds) - end - isempty(vals_real) && return nothing, false - return _window_reduce(vals_real, durations_real, policy), true - end - - return nothing, false -end - -""" - _resolved_interpolated_value_for_source(sim, source_scope, source_scale, source_process, source_var, source_node_id, t, policy) - -Resolve one producer value at time `t` using interpolation/extrapolation over -stored temporal samples. -Returns `(value, found::Bool)`. -""" -function _resolved_interpolated_value_for_source( - sim::GraphSimulation, - source_scope::ScopeId, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - source_node_id::Int, - t::Float64, - policy::Interpolate -) - key = OutputKey(source_scope, source_scale, source_node_id, source_process, source_var) - samples = _resolution_samples(sim, key) - isnothing(samples) && return nothing, false - isempty(samples) && return nothing, false - - prev_idx = findlast(s -> s[1] <= t + 1e-8, samples) - next_idx = findfirst(s -> s[1] >= t - 1e-8, samples) - - # Interpolate between known bracketing points when available. - if !isnothing(prev_idx) && !isnothing(next_idx) - t_prev, v_prev = samples[prev_idx] - t_next, v_next = samples[next_idx] - if isapprox(t_prev, t_next; atol=1e-8, rtol=0.0) - return v_prev, true - end - if policy.mode == :linear && v_prev isa Real && v_next isa Real - α = (t - t_prev) / (t_next - t_prev) - return v_prev + α * (v_next - v_prev), true - end - return v_prev, true - end - - # Real-time fallback when no future sample exists yet: - # use linear extrapolation from last two samples if possible, else hold-last. - if !isnothing(prev_idx) - t_last, v_last = samples[prev_idx] - if policy.extrapolation == :linear && prev_idx >= 2 - t_prev, v_prev = samples[prev_idx - 1] - if v_prev isa Real && v_last isa Real && !isapprox(t_last, t_prev; atol=1e-8, rtol=0.0) - α = (t - t_last) / (t_last - t_prev) - return v_last + α * (v_last - v_prev), true - end - end - return v_last, true - end - - # If only future data exists, use the earliest known value. - return samples[1][2], true -end - -function _normalize_time_reducer(reducer; context::AbstractString="reducer") - if reducer isa DataType - reducer <: PlantMeteo.AbstractTimeReducer || error( - "Unsupported $(context) type `$(reducer)`. Use a PlantMeteo reducer type/instance or a callable." - ) - return reducer() - elseif reducer isa PlantMeteo.AbstractTimeReducer - return reducer - elseif reducer isa Function - return reducer - end - - error( - "Unsupported $(context) value `$(reducer)` of type `$(typeof(reducer))`. ", - "Use a PlantMeteo reducer type/instance or a callable." - ) -end - -_resolve_window_reducer(reducer) = _normalize_time_reducer(reducer; context="reducer") - -function _window_reduce(vals::AbstractVector{<:Real}, durations::AbstractVector{<:Real}, policy::SchedulePolicy) - reducer = policy isa Integrate ? policy.reducer : (policy isa Aggregate ? policy.reducer : PlantMeteo.SumReducer()) - f = _resolve_window_reducer(reducer) - - if applicable(f, vals, durations) - return f(vals, durations) - end - - applicable(f, vals) || error( - "Reducer `$(reducer)` is not callable on collected window values for policy `$(typeof(policy))`. ", - "Expected `(values)` or `(values, durations_seconds)`." - ) - - return f(vals) -end - -""" - _assign_input_value!(st, input_var, value) - -Assign an input value into `Status`, preserving in-place updates for `RefVector` -inputs when both sides are vectors. -""" -function _assign_input_value!(st::Status, input_var::Symbol, value) - current = st[input_var] - if current isa RefVector && value isa AbstractVector - length(current) != length(value) && resize!(current, length(value)) - for i in eachindex(value) - current[i] = value[i] - end - return nothing - end - st[input_var] = value - return nothing -end - -function _same_scale_status_value(source_statuses, target_node_id::Int, source_var::Symbol) - for src_st in source_statuses - node_id(src_st.node) == target_node_id || continue - source_var in keys(src_st) || continue - return src_st[source_var], true - end - return nothing, false -end - -function _ancestor_node_id_for_scale(node, source_scale::Symbol) - ancestor = parent(node) - while !isnothing(ancestor) - if symbol(ancestor) == source_scale - return node_id(ancestor) - end - ancestor = parent(ancestor) - end - return nothing -end - -function _status_for_node_id(source_statuses, target_node_id::Int) - for src_st in source_statuses - node_id(src_st.node) == target_node_id && return src_st - end - return nothing -end - -function _prefer_local_status_fallback(st::Status, input_var::Symbol, source_var::Symbol, prefer_local_status::Bool) - prefer_local_status || return false - source_var in keys(st) || return false - _assign_input_value!(st, input_var, st[source_var]) - return true -end - -function _resolved_policy_value_for_source( - sim::GraphSimulation, - source_scope::ScopeId, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - source_node_id::Int, - t::Float64, - policy::HoldLast, - t_start::Float64, - source_sample_duration_seconds::Float64 -) - return _resolved_value_for_source(sim, source_scope, source_scale, source_process, source_var, source_node_id, t) -end - -function _resolved_policy_value_for_source( - sim::GraphSimulation, - source_scope::ScopeId, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - source_node_id::Int, - t::Float64, - policy::Interpolate, - t_start::Float64, - source_sample_duration_seconds::Float64 -) - return _resolved_interpolated_value_for_source(sim, source_scope, source_scale, source_process, source_var, source_node_id, t, policy) -end - -function _resolved_policy_value_for_source( - sim::GraphSimulation, - source_scope::ScopeId, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - source_node_id::Int, - t::Float64, - policy::Union{Integrate,Aggregate}, - t_start::Float64, - source_sample_duration_seconds::Float64 -) - return _resolved_windowed_value_for_source( - sim, - source_scope, - source_scale, - source_process, - source_var, - source_node_id, - t_start, - t, - policy, - source_sample_duration_seconds - ) -end - -_missing_vector_source_value(policy::SchedulePolicy) = nothing -_missing_vector_source_value(policy::Union{Integrate,Aggregate}) = 0.0 -_missing_scalar_source_value(policy::SchedulePolicy) = nothing -_missing_scalar_source_value(policy::Union{Integrate,Aggregate}) = 0.0 - -function _source_scope_matches(sim::GraphSimulation, source_model_spec, source_scale::Symbol, source_process::Symbol, src_st::Status, consumer_scope::ScopeId) - source_scope = _scope_for_status(sim, source_model_spec, source_scale, source_process, src_st.node) - return source_scope == consumer_scope, source_scope -end - -function _push_vector_source_value!( - vals::Vector{Any}, - sim::GraphSimulation, - src_st::Status, - consumer_scope::ScopeId, - source_model_spec, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - t::Float64, - policy::SchedulePolicy, - t_start::Float64, - source_sample_duration_seconds::Float64 -) - scope_matches, source_scope = _source_scope_matches(sim, source_model_spec, source_scale, source_process, src_st, consumer_scope) - scope_matches || return nothing - - src_node_id = node_id(src_st.node) - v, ok = _resolved_policy_value_for_source( - sim, source_scope, source_scale, source_process, source_var, src_node_id, t, policy, t_start, source_sample_duration_seconds - ) - if ok - push!(vals, v) - elseif source_var in keys(src_st) - push!(vals, src_st[source_var]) - else - missing_value = _missing_vector_source_value(policy) - isnothing(missing_value) || push!(vals, missing_value) - end - return nothing -end - -function _assign_vector_source_values!( - sim::GraphSimulation, - st::Status, - consumer_scope::ScopeId, - source_model_spec, - input_var::Symbol, - source_statuses, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - t::Float64, - policy::SchedulePolicy, - t_start::Float64, - source_sample_duration_seconds::Float64 -) - vals = Any[] - for src_st in source_statuses - _push_vector_source_value!( - vals, - sim, - src_st, - consumer_scope, - source_model_spec, - source_scale, - source_process, - source_var, - t, - policy, - t_start, - source_sample_duration_seconds - ) - end - length(vals) > 0 && _assign_input_value!(st, input_var, vals) - return nothing -end - -function _resolve_ancestor_source_value( - sim::GraphSimulation, - st::Status, - consumer_scope::ScopeId, - source_model_spec, - source_statuses, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - t::Float64, - policy::SchedulePolicy, - t_start::Float64, - source_sample_duration_seconds::Float64 -) - ancestor_node_id = _ancestor_node_id_for_scale(st.node, source_scale) - isnothing(ancestor_node_id) && return nothing, false - src_st = _status_for_node_id(source_statuses, ancestor_node_id) - isnothing(src_st) && return nothing, false - - scope_matches, source_scope = _source_scope_matches(sim, source_model_spec, source_scale, source_process, src_st, consumer_scope) - scope_matches || return nothing, false - - vv, found = _resolved_policy_value_for_source( - sim, source_scope, source_scale, source_process, source_var, ancestor_node_id, t, policy, t_start, source_sample_duration_seconds - ) - found && return vv, true - source_var in keys(src_st) && return src_st[source_var], true - return nothing, false -end - -function _collect_unique_source_candidates( - sim::GraphSimulation, - consumer_scope::ScopeId, - source_model_spec, - source_statuses, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - t::Float64, - policy::SchedulePolicy, - t_start::Float64, - source_sample_duration_seconds::Float64 -) - candidates = Any[] - for src_st in source_statuses - scope_matches, source_scope = _source_scope_matches(sim, source_model_spec, source_scale, source_process, src_st, consumer_scope) - scope_matches || continue - src_node_id = node_id(src_st.node) - vv, found = _resolved_policy_value_for_source( - sim, source_scope, source_scale, source_process, source_var, src_node_id, t, policy, t_start, source_sample_duration_seconds - ) - found && push!(candidates, vv) - end - return candidates -end - -function _resolve_scalar_source_value!( - sim::GraphSimulation, - node::SoftDependencyNode, - st::Status, - consumer_scope::ScopeId, - source_model_spec, - prefer_local_status::Bool, - input_var::Symbol, - source_statuses, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - t::Float64, - policy::SchedulePolicy, - t_start::Float64, - source_sample_duration_seconds::Float64; - fallback_to_source_status::Bool -) - _prefer_local_status_fallback(st, input_var, source_var, prefer_local_status) && return nothing - - consumer_node_id = node_id(st.node) - v, ok = _resolved_policy_value_for_source( - sim, consumer_scope, source_scale, source_process, source_var, consumer_node_id, t, policy, t_start, source_sample_duration_seconds - ) - if ok - _assign_input_value!(st, input_var, v) - return nothing - end - - if fallback_to_source_status - if source_scale == node.scale - vv, found = _same_scale_status_value(source_statuses, consumer_node_id, source_var) - if found - _assign_input_value!(st, input_var, vv) - return nothing - end - else - vv, found = _resolve_ancestor_source_value( - sim, - st, - consumer_scope, - source_model_spec, - source_statuses, - source_scale, - source_process, - source_var, - t, - policy, - t_start, - source_sample_duration_seconds - ) - if found - _assign_input_value!(st, input_var, vv) - return nothing - end - end - end - - candidates = _collect_unique_source_candidates( - sim, - consumer_scope, - source_model_spec, - source_statuses, - source_scale, - source_process, - source_var, - t, - policy, - t_start, - source_sample_duration_seconds - ) - if length(candidates) == 1 - _assign_input_value!(st, input_var, only(candidates)) - elseif length(candidates) > 1 - error( - "Ambiguous cross-scale source values for input `$(input_var)` in process `$(node.process)` at scale `$(node.scale)`. ", - "Please provide `InputBindings(...)` with explicit `scale`/source disambiguation." - ) - else - missing_value = _missing_scalar_source_value(policy) - isnothing(missing_value) || _assign_input_value!(st, input_var, missing_value) - end - - return nothing -end - -function _resolve_input_from_policy!( - sim::GraphSimulation, - node::SoftDependencyNode, - st::Status, - consumer_scope::ScopeId, - source_model_spec, - prefer_local_status::Bool, - input_var::Symbol, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - t::Float64, - policy::SchedulePolicy, - t_start::Float64, - source_sample_duration_seconds::Float64; - fallback_to_source_status::Bool=true -) - source_statuses = get(status(sim), source_scale, nothing) - isnothing(source_statuses) && return nothing - - current_value = st[input_var] - if current_value isa AbstractVector - return _assign_vector_source_values!( - sim, - st, - consumer_scope, - source_model_spec, - input_var, - source_statuses, - source_scale, - source_process, - source_var, - t, - policy, - t_start, - source_sample_duration_seconds - ) - end - - return _resolve_scalar_source_value!( - sim, - node, - st, - consumer_scope, - source_model_spec, - prefer_local_status, - input_var, - source_statuses, - source_scale, - source_process, - source_var, - t, - policy, - t_start, - source_sample_duration_seconds; - fallback_to_source_status=fallback_to_source_status - ) -end - -""" - _resolve_input_windowed(sim, node, st, input_var, source_scale, source_process, source_var, t_start, t_end, policy) - -Resolve one consumer input from producer temporal streams using a windowed -policy (`Integrate` or `Aggregate`) and write it into `st`. -""" -function _resolve_input_windowed( - sim::GraphSimulation, - node::SoftDependencyNode, - st::Status, - consumer_scope::ScopeId, - source_model_spec, - prefer_local_status::Bool, - input_var::Symbol, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - t_start::Float64, - t_end::Float64, - policy::SchedulePolicy, - source_sample_duration_seconds::Float64 -) - return _resolve_input_from_policy!( - sim, - node, - st, - consumer_scope, - source_model_spec, - prefer_local_status, - input_var, - source_scale, - source_process, - source_var, - t_end, - policy, - t_start, - source_sample_duration_seconds - ) -end - -""" - _resolve_input_interpolate(sim, node, st, input_var, source_scale, source_process, source_var, t, policy) - -Resolve one consumer input from producer temporal streams using interpolation -policy and write it into `st`. -""" -function _resolve_input_interpolate( - sim::GraphSimulation, - node::SoftDependencyNode, - st::Status, - consumer_scope::ScopeId, - source_model_spec, - prefer_local_status::Bool, - input_var::Symbol, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - t::Float64, - policy::Interpolate -) - return _resolve_input_from_policy!( - sim, - node, - st, - consumer_scope, - source_model_spec, - prefer_local_status, - input_var, - source_scale, - source_process, - source_var, - t, - policy, - t, - 0.0; - fallback_to_source_status=false - ) -end - -""" - _resolve_input_holdlast(sim, node, st, input_var, source_scale, source_process, source_var, t) - -Resolve one consumer input from producer hold-last values and write it into -`st`. -""" -function _resolve_input_holdlast( - sim::GraphSimulation, - node::SoftDependencyNode, - st::Status, - consumer_scope::ScopeId, - source_model_spec, - prefer_local_status::Bool, - input_var::Symbol, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - t::Float64 -) - return _resolve_input_from_policy!( - sim, - node, - st, - consumer_scope, - source_model_spec, - prefer_local_status, - input_var, - source_scale, - source_process, - source_var, - t, - HoldLast(), - t, - 0.0 - ) -end - -function _resolve_input_for_policy!( - sim::GraphSimulation, - node::SoftDependencyNode, - st::Status, - consumer_scope::ScopeId, - source_model_spec, - prefer_local_status::Bool, - input_var::Symbol, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - t::Float64, - t_start::Float64, - policy::HoldLast, - source_sample_duration_seconds::Float64 -) - return _resolve_input_holdlast( - sim, - node, - st, - consumer_scope, - source_model_spec, - prefer_local_status, - input_var, - source_scale, - source_process, - source_var, - t - ) -end - -function _resolve_input_for_policy!( - sim::GraphSimulation, - node::SoftDependencyNode, - st::Status, - consumer_scope::ScopeId, - source_model_spec, - prefer_local_status::Bool, - input_var::Symbol, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - t::Float64, - t_start::Float64, - policy::Interpolate, - source_sample_duration_seconds::Float64 -) - return _resolve_input_interpolate( - sim, - node, - st, - consumer_scope, - source_model_spec, - prefer_local_status, - input_var, - source_scale, - source_process, - source_var, - t, - policy - ) -end - -function _resolve_input_for_policy!( - sim::GraphSimulation, - node::SoftDependencyNode, - st::Status, - consumer_scope::ScopeId, - source_model_spec, - prefer_local_status::Bool, - input_var::Symbol, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - t::Float64, - t_start::Float64, - policy::Union{Integrate,Aggregate}, - source_sample_duration_seconds::Float64 -) - return _resolve_input_windowed( - sim, - node, - st, - consumer_scope, - source_model_spec, - prefer_local_status, - input_var, - source_scale, - source_process, - source_var, - t_start, - t, - policy, - source_sample_duration_seconds - ) -end - -function _resolve_input_for_policy!( - sim::GraphSimulation, - node::SoftDependencyNode, - st::Status, - consumer_scope::ScopeId, - source_model_spec, - prefer_local_status::Bool, - input_var::Symbol, - source_scale::Symbol, - source_process::Symbol, - source_var::Symbol, - t::Float64, - t_start::Float64, - policy::SchedulePolicy, - source_sample_duration_seconds::Float64 -) - error( - "Unsupported input temporal policy `$(typeof(policy))` for input `$(input_var)` ", - "in process `$(node.process)` at scale `$(node.scale)`." - ) -end - -""" - resolve_inputs_from_temporal_state!(sim, node, st, t, model_spec, timeline) - -Resolve all model inputs for `node` at time `t` using declared -`InputBindings(...)` and schedule policies, then mutate `st` in place. -""" -function resolve_inputs_from_temporal_state!(sim::GraphSimulation, node::SoftDependencyNode, st::Status, t::Float64, model_spec, timeline::TimelineContext) - model = node.value - ins = keys(inputs_(model)) - length(ins) == 0 && return nothing - consumer_clock = _model_clock(model_spec, model, timeline) - t_start = _window_start_for_clock(consumer_clock, t) - consumer_scope = _scope_for_status(sim, model_spec, node.scale, node.process, st.node) - - bindings = input_bindings(model_spec) - - for input_var in ins - binding = input_var in keys(bindings) ? _parse_input_binding(bindings[input_var]) : nothing - - source_process = nothing - source_var = input_var - policy = HoldLast() - policy_is_explicit = false - - if !isnothing(binding) && !isnothing(binding.process) - source_process = binding.process - source_var = isnothing(binding.var) ? input_var : binding.var - source_scale = isnothing(binding.scale) ? _source_scale_for_process(node, source_process) : binding.scale - policy = binding.policy - policy_is_explicit = true - else - candidates = _candidate_producers(node, input_var) - if length(candidates) == 1 - source_process, source_var = only(candidates) - source_scale = _source_scale_for_process(node, source_process) - elseif length(candidates) > 1 - error( - "Ambiguous producer for input `$(input_var)` in process `$(node.process)` at scale `$(node.scale)`. ", - "Please define explicit `InputBindings(...)` for this model in your mapping." - ) - else - continue - end - end - prefer_local_status = !policy_is_explicit - source_model_spec = _model_spec_for_process(sim, source_scale, source_process) - source_model = get_models(sim)[source_scale][source_process] - source_clock = _model_clock(source_model_spec, source_model, timeline) - source_sample_duration_seconds = float(source_clock.dt) * timeline.base_step_seconds - if !policy_is_explicit - policy = _policy_for_output(model_(source_model_spec), source_var) - end - - _resolve_input_for_policy!( - sim, - node, - st, - consumer_scope, - source_model_spec, - prefer_local_status, - input_var, - source_scale, - source_process, - source_var, - t, - t_start, - policy, - source_sample_duration_seconds - ) - end - - return nothing -end diff --git a/src/time/runtime/meteo_sampling.jl b/src/time/runtime/meteo_sampling.jl index c46d08e3d..c99ba7e6d 100644 --- a/src/time/runtime/meteo_sampling.jl +++ b/src/time/runtime/meteo_sampling.jl @@ -27,7 +27,7 @@ function _meteo_sampling_window(clock::ClockSpec, model_spec) return window end -_normalize_meteo_reducer(reducer) = _normalize_time_reducer(reducer; context="meteo reducer") +_normalize_meteo_reducer(reducer) = _normalize_policy_reducer(reducer) function _normalize_meteo_binding_rule(target::Symbol, rule) if rule isa NamedTuple @@ -122,7 +122,7 @@ function _error_missing_meteo_inputs(missing_rows; subject::AbstractString, noun noun, " to ", target, - ", declare a `MeteoBindings(source=...)` remapping, ", + ", declare an `Environment(; sources=...)` remapping, ", "or remove the unused meteo input from the model trait." ) end @@ -133,7 +133,7 @@ end Validate declared `meteo_inputs_` against the available meteorological fields. The check is intentionally field-based and independent from units/backends. When -`MeteoBindings` remap a declared model input from another source variable, the +Environment source bindings remap a declared model input from another variable; the source variable is checked on the raw meteo object. """ function validate_meteo_inputs(model_specs::Dict{Symbol,Dict{Symbol,ModelSpec}}, meteo) @@ -168,20 +168,6 @@ function validate_meteo_inputs(models::NamedTuple, meteo) return validate_meteo_inputs(specs, meteo) end -function validate_meteo_inputs(mapping::ModelMapping, meteo) - specs = Dict{Symbol,Dict{Symbol,ModelSpec}}( - scale => parse_model_specs(declarations) for (scale, declarations) in pairs(mapping) - ) - return validate_meteo_inputs(specs, meteo) -end - -function validate_meteo_inputs(mapping::AbstractDict, meteo) - specs = Dict{Symbol,Dict{Symbol,ModelSpec}}( - Symbol(scale) => parse_model_specs(declarations) for (scale, declarations) in pairs(mapping) - ) - return validate_meteo_inputs(specs, meteo) -end - function _meteo_transforms_for_model(model_spec) bindings = meteo_bindings(model_spec) isnothing(bindings) && return nothing @@ -208,7 +194,7 @@ function _sample_meteo_for_model( isnothing(meteo_sampler) && begin if !isnothing(transforms) || !isnothing(window) @warn string( - "MeteoBindings or MeteoWindow were provided but weather sampler API is unavailable or meteo is not TimeStepTable{Atmosphere}. ", + "Environment sampling rules were provided but the weather sampler API is unavailable or meteorology is not TimeStepTable{Atmosphere}. ", "Falling back to raw meteo rows." ) maxlog = 1 end diff --git a/src/time/runtime/output_export.jl b/src/time/runtime/output_export.jl index 3f48c51cb..fb1f4e8a7 100644 --- a/src/time/runtime/output_export.jl +++ b/src/time/runtime/output_export.jl @@ -1,53 +1,8 @@ """ - OutputRequest( - scale, - var; - name=var, - process=nothing, - application=nothing, - policy=HoldLast(), - clock=nothing, - ) - -Describe one resampled output series for multi-rate or scene/object runs. - -Use this type in `run!(...; tracked_outputs=...)` to retain and materialize -resampled temporal streams. - -# Arguments -- `scale::Symbol`: producer scale (for example `:Leaf` or `:Plant`). -- `var::Symbol`: source variable name published on `scale`. + OutputRequest(scale, var; name=var, process=nothing, application=nothing, + policy=HoldLast(), clock=nothing) -# Keyword arguments -- `name::Symbol=var`: name of the exported series in `collect_outputs(sim)` or - returned output dictionaries. Names must be unique across requests. -- `process=nothing`: producer process name (`Symbol`/`String`) or `nothing`. - When `nothing`, runtime tries to use the unique canonical publisher for - `(scale, var)` and errors on ambiguity. -- `application=nothing`: scene/object application name or compiled application - id. Use this when the same process is applied more than once. An explicit - application may select a `:stream_only` publisher. Legacy `GraphSimulation` - output export does not support this selector. -- `policy::SchedulePolicy=HoldLast()`: resampling policy applied at export time. - Common values are `HoldLast()`, `Integrate(...)`, `Aggregate(...)`, - `Interpolate(...)`. - `Integrate` and `Aggregate` are runtime-equivalent with the same reducer; - they differ by default reducer (`SumReducer` vs `MeanReducer`) and intent. -- `clock=nothing`: export clock. When `nothing`, export is evaluated at each - simulation step (`ClockSpec(1.0, 0.0)`). Accepted explicit values are the same - as model timestep specs (`Real`, `ClockSpec`, or fixed `Dates.Period`). - -# Example -```julia -req_daily = OutputRequest( - :Leaf, - :A; - name=:A_daily, - process=:toyassim, - policy=Integrate(), - clock=ClockSpec(24.0, 0.0), -) -``` +Request retention and optional resampling of one scene output stream. """ struct OutputRequest{P<:Union{Nothing,Symbol},A<:Union{Nothing,Symbol},POL<:SchedulePolicy,C} scale::Symbol @@ -66,7 +21,7 @@ function OutputRequest( process=nothing, application=nothing, policy::SchedulePolicy=HoldLast(), - clock=nothing + clock=nothing, ) proc = isnothing(process) ? nothing : Symbol(process) app = isnothing(application) ? nothing : Symbol(application) @@ -75,272 +30,18 @@ end function _export_clock(request::OutputRequest, timeline::TimelineContext) isnothing(request.clock) && return ClockSpec(1.0, 0.0) - c = _clock_from_spec_timestep(request.clock, timeline) - isnothing(c) && error( + clock = _clock_from_spec_timestep(request.clock, timeline) + isnothing(clock) && error( "Unsupported clock specification `$(typeof(request.clock))` in OutputRequest `$(request.name)`." ) - return c -end - -function _canonical_source_process(sim::GraphSimulation, scale::Symbol, var::Symbol) - haskey(get_models(sim), scale) || error("Unknown scale `$(scale)` in output export request.") - models_at_scale = get_models(sim)[scale] - specs_at_scale = get_model_specs(sim)[scale] - ignored_hard_children = _hard_dependency_children(dep(sim)) - ignored_at_scale = get(ignored_hard_children, scale, Set{Symbol}()) - - publishers = Symbol[] - for (process, model) in pairs(models_at_scale) - process in ignored_at_scale && continue - var in keys(outputs_(model)) || continue - spec = get(specs_at_scale, process, as_model_spec(model)) - _publish_mode_for_output(spec, var) == :stream_only && continue - push!(publishers, process) - end - - if isempty(publishers) - error( - "No canonical publisher found for variable `$(var)` at scale `$(scale)`. ", - "Provide `process=` in OutputRequest or mark one producer as canonical." - ) - elseif length(publishers) > 1 - error( - "Ambiguous canonical publishers for variable `$(var)` at scale `$(scale)`: ", - join(publishers, ", "), - ". Provide `process=` in OutputRequest." - ) - end - - return only(publishers) + return clock end function _normalize_output_requests(requests) isnothing(requests) && return OutputRequest[] requests isa OutputRequest && return OutputRequest[requests] requests isa AbstractVector{<:OutputRequest} || error( - "`tracked_outputs` (for multi-rate exports) must be `nothing`, an `OutputRequest`, or a vector of `OutputRequest`." + "`tracked_outputs` must be `nothing`, an `OutputRequest`, or a vector of `OutputRequest`." ) return collect(requests) end - -""" - prepare_output_requests!(sim, requests, timeline) - -Resolve and register online export requests for the current run. -""" -function prepare_output_requests!(sim::GraphSimulation, requests, timeline::TimelineContext) - reqs = _normalize_output_requests(requests) - - plans = OutputExportPlan[] - rows = Dict{Symbol,ExportBuffer}() - - for req in reqs - isnothing(req.application) || error( - "`application=` in `OutputRequest` is only supported by scene/object simulations." - ) - scale = req.scale - process = isnothing(req.process) ? _canonical_source_process(sim, scale, req.var) : req.process - model_spec = _model_spec_for_process(sim, scale, process) - source_model = get_models(sim)[scale][process] - source_clock = _model_clock(model_spec, source_model, timeline) - clock = _export_clock(req, timeline) - - haskey(rows, req.name) && error( - "Duplicate output request name `$(req.name)`. Request names must be unique." - ) - - push!( - plans, - OutputExportPlan( - req.name, - scale, - req.var, - process, - req.policy, - clock, - model_spec, - float(source_clock.dt), - float(source_clock.dt) * timeline.base_step_seconds, - ), - ) - rows[req.name] = ExportBuffer(scale, process, req.var) - end - - sim.temporal_state.export_plans = plans - sim.temporal_state.export_rows = rows - return nothing -end - -function _required_horizon_for_export_policy(policy::SchedulePolicy, clock::ClockSpec, source_dt::Float64) - return _required_horizon_for_policy(policy, float(clock.dt), source_dt) -end - -""" - export_horizon_requirements(sim) - -Return additional producer horizon requirements induced by configured online -export requests. -""" -function export_horizon_requirements(sim::GraphSimulation) - horizons = Dict{Tuple{Symbol,Symbol,Symbol},Float64}() - for plan in sim.temporal_state.export_plans - required = _required_horizon_for_export_policy(plan.policy, plan.clock, plan.source_dt) - required <= 0.0 && continue - key = (plan.scale, plan.process, plan.var) - horizons[key] = max(get(horizons, key, 0.0), required) - end - return horizons -end - -function _resolve_output_value_online( - sim::GraphSimulation, - policy::HoldLast, - scope::ScopeId, - scale::Symbol, - process::Symbol, - var::Symbol, - nodeid::Int, - t::Float64, - t_start::Float64, - source_sample_duration_seconds::Float64 -) - v, ok = _resolved_value_for_source(sim, scope, scale, process, var, nodeid, t) - return ok ? v : missing -end - -function _resolve_output_value_online( - sim::GraphSimulation, - policy::Interpolate, - scope::ScopeId, - scale::Symbol, - process::Symbol, - var::Symbol, - nodeid::Int, - t::Float64, - t_start::Float64, - source_sample_duration_seconds::Float64 -) - v, ok = _resolved_interpolated_value_for_source(sim, scope, scale, process, var, nodeid, t, policy) - return ok ? v : missing -end - -function _resolve_output_value_online( - sim::GraphSimulation, - policy::Union{Integrate,Aggregate}, - scope::ScopeId, - scale::Symbol, - process::Symbol, - var::Symbol, - nodeid::Int, - t::Float64, - t_start::Float64, - source_sample_duration_seconds::Float64 -) - v, ok = _resolved_windowed_value_for_source( - sim, - scope, - scale, - process, - var, - nodeid, - t_start, - t, - policy, - source_sample_duration_seconds - ) - return ok ? v : missing -end - -""" - update_requested_outputs!(sim, t) - -Materialize configured output requests online at runtime time `t`. -""" -function update_requested_outputs!(sim::GraphSimulation, t::Float64) - isempty(sim.temporal_state.export_plans) && return nothing - timestep = Int(round(t)) - - for plan in sim.temporal_state.export_plans - _should_run_at_time(plan.clock, t) || continue - source_statuses = get(status(sim), plan.scale, nothing) - isnothing(source_statuses) && continue - buf = sim.temporal_state.export_rows[plan.name] - - t_start = _window_start_for_clock(plan.clock, t) - for st in source_statuses - scope = _scope_for_status(sim, plan.model_spec, plan.scale, plan.process, st.node) - nodeid = node_id(st.node) - v = _resolve_output_value_online( - sim, - plan.policy, - scope, - plan.scale, - plan.process, - plan.var, - nodeid, - t, - t_start, - plan.source_sample_duration_seconds, - ) - - push!(buf.timestep, timestep) - push!(buf.node, nodeid) - push!(buf.value, v) - end - end - - return nothing -end - -function _materialize_output_rows(rows::ExportBuffer, sink) - n = length(rows.timestep) - scale_col = fill(rows.scale, n) - process_col = fill(rows.process, n) - var_col = fill(rows.var, n) - - if sink === DataFrames.DataFrame - return DataFrames.DataFrame( - timestep=rows.timestep, - scale=scale_col, - process=process_col, - var=var_col, - node=rows.node, - value=rows.value, - ) - end - - table = Vector{NamedTuple{(:timestep, :scale, :process, :var, :node, :value),Tuple{Int,Symbol,Symbol,Symbol,Int,Any}}}(undef, n) - @inbounds for i in 1:n - table[i] = ( - timestep=rows.timestep[i], - scale=scale_col[i], - process=process_col[i], - var=var_col[i], - node=rows.node[i], - value=rows.value[i], - ) - end - - isnothing(sink) && return table - return sink(table) -end - -""" - collect_outputs(sim; sink=DataFrame) - -Return online-exported output rows configured for the run. -""" -function collect_outputs(sim::GraphSimulation; sink=DataFrames.DataFrame) - out = Dict{Symbol,Any}() - for (name, rows) in sim.temporal_state.export_rows - out[name] = _materialize_output_rows(rows, sink) - end - return out -end - -function collect_outputs(sim::GraphSimulation, name::Symbol; sink=DataFrames.DataFrame) - haskey(sim.temporal_state.export_rows, name) || error( - "Unknown output request name `$(name)`." - ) - return _materialize_output_rows(sim.temporal_state.export_rows[name], sink) -end diff --git a/src/time/runtime/publishers.jl b/src/time/runtime/publishers.jl deleted file mode 100644 index ce044dc5c..000000000 --- a/src/time/runtime/publishers.jl +++ /dev/null @@ -1,186 +0,0 @@ -""" - _policy_for_output(model, var) - -Return the per-output schedule policy for `var`, defaulting to `HoldLast()`. -""" -function _policy_for_output(model, var::Symbol) - pol = output_policy(model) - var in keys(pol) || return HoldLast() - return _as_schedule_policy(pol[var]; context="output_policy for output `$(var)` in model `$(typeof(model))`") -end - -""" - _publish_mode_for_output(model_spec, var) - -Return output routing mode for `var` (`:canonical` or `:stream_only`), with -validation and default `:canonical`. -""" -function _publish_mode_for_output(model_spec, var::Symbol) - modes = output_routing(model_spec) - mode = var in keys(modes) ? modes[var] : :canonical - mode in (:canonical, :stream_only) || error( - "Unsupported output routing mode `$(mode)` for output `$(var)`. ", - "Allowed values are `:canonical` and `:stream_only`." - ) - return mode -end - -""" - validate_canonical_publishers(sim) - -Ensure that each `(scale, variable)` has at most one canonical publisher. -Throws when multiple producers publish the same canonical output. -""" -function validate_canonical_publishers(sim::GraphSimulation) - ignored_hard_children = _hard_dependency_children(dep(sim)) - for (scale, models_at_scale) in get_models(sim) - specs_at_scale = get_model_specs(sim)[scale] - ignored_at_scale = get(ignored_hard_children, scale, Set{Symbol}()) - publishers = Dict{Symbol,Vector{Symbol}}() - for (process, model) in pairs(models_at_scale) - process in ignored_at_scale && continue - model_spec = get(specs_at_scale, process, as_model_spec(model)) - for var in keys(outputs_(model)) - _publish_mode_for_output(model_spec, var) == :stream_only && continue - if !haskey(publishers, var) - publishers[var] = Symbol[process] - else - push!(publishers[var], process) - end - end - end - - for (var, procs) in publishers - if length(procs) > 1 - updater_flags = Dict( - process => (var in _update_variables_for_spec(get(specs_at_scale, process, as_model_spec(models_at_scale[process])))) - for process in procs - ) - primary_procs = [process for process in procs if !updater_flags[process]] - if length(primary_procs) == 1 - update_procs = [process for process in procs if updater_flags[process]] - primary = only(primary_procs) - all(process -> primary in _update_after_for_var(specs_at_scale[process], var), update_procs) && continue - end - error( - "Ambiguous canonical publishers for variable `$(var)` at scale `$(scale)`: ", - join(procs, ", "), - ". Declare `OutputRouting(; $(var)=:stream_only)` for non-canonical producers, ", - "or `Updates(:$(var); after=:primary_process)` for intentional state updates." - ) - end - end - end - return nothing -end - -_producer_signature(scale::Symbol, process::Symbol, var::Symbol) = (scale, process, var) - -function _max_horizon!(horizons::Dict{Tuple{Symbol,Symbol,Symbol},Float64}, key::Tuple{Symbol,Symbol,Symbol}, required::Float64) - horizons[key] = max(get(horizons, key, 0.0), required) - return nothing -end - -function _required_horizon_for_policy(policy::SchedulePolicy, consumer_dt::Float64, source_dt::Float64) - if policy isa Union{Integrate,Aggregate} - return max(1.0, consumer_dt) - elseif policy isa Interpolate - # Keep at least two source samples for interpolation/extrapolation. - return max(2.0, source_dt + 1.0) - end - return 0.0 -end - -function _consumer_horizon_requirements(sim::GraphSimulation, timeline::TimelineContext) - horizons = Dict{Tuple{Symbol,Symbol,Symbol},Float64}() - - dep_nodes = traverse_dependency_graph(dep(sim), false) - for node in dep_nodes - model_spec = _model_spec_for_process(sim, node.scale, node.process) - consumer_clock = _model_clock(model_spec, node.value, timeline) - consumer_dt = float(consumer_clock.dt) - - for (input_var, raw_binding) in pairs(input_bindings(model_spec)) - parsed = _parse_input_binding(raw_binding) - isnothing(parsed) && continue - isnothing(parsed.process) && continue - source_process = parsed.process - source_var = isnothing(parsed.var) ? input_var : parsed.var - source_scale = isnothing(parsed.scale) ? _source_scale_for_process(node, source_process) : parsed.scale - source_scale isa Symbol || error( - "Source scale for input `$(input_var)` in process `$(node.value)` must be a `Symbol`, got `$(typeof(source_scale))`." - ) - source_model_spec = _model_spec_for_process(sim, source_scale, source_process) - source_model = get_models(sim)[source_scale][source_process] - source_clock = _model_clock(source_model_spec, source_model, timeline) - source_dt = float(source_clock.dt) - required = _required_horizon_for_policy(parsed.policy, consumer_dt, source_dt) - required <= 0.0 && continue - key = _producer_signature(source_scale, source_process, source_var) - _max_horizon!(horizons, key, required) - end - end - - return horizons -end - -""" - configure_temporal_buffers!(sim, timeline) - -Prepare bounded producer streams used at runtime and online export. -""" -function configure_temporal_buffers!(sim::GraphSimulation, timeline::TimelineContext) - horizons = _consumer_horizon_requirements(sim, timeline) - for (key, required) in export_horizon_requirements(sim) - _max_horizon!(horizons, key, required) - end - sim.temporal_state.producer_horizons = horizons - empty!(sim.temporal_state.streams) - empty!(sim.temporal_state.caches) - empty!(sim.temporal_state.last_run) - return nothing -end - -function _trim_stream!(samples::OutputStream, t::Float64, horizon::Float64) - horizon <= 0.0 && (empty!(samples); return nothing) - t_min = t - horizon + 1.0 - 1e-8 - first_keep = findfirst(s -> s[1] >= t_min, samples) - isnothing(first_keep) && (empty!(samples); return nothing) - first_keep > 1 && deleteat!(samples, 1:first_keep-1) - return nothing -end - -""" - update_temporal_state_outputs!(sim, node, model_spec, st, t) - -Store producer outputs at time `t` into temporal streams and hold-last caches. -Also updates `last_run` for the emitting model key. -""" -function update_temporal_state_outputs!(sim::GraphSimulation, node::SoftDependencyNode, model_spec, st::Status, t::Float64) - model = node.value - outs = keys(outputs_(model)) - length(outs) == 0 && return nothing - - scope = _scope_for_status(sim, model_spec, node.scale, node.process, st.node) - nodeid = node_id(st.node) - mkey = ModelKey(scope, node.scale, node.process) - sim.temporal_state.last_run[mkey] = t - - for out_var in outs - val = st[out_var] - key = OutputKey(scope, node.scale, nodeid, node.process, out_var) - producer_key = _producer_signature(node.scale, node.process, out_var) - - if haskey(sim.temporal_state.producer_horizons, producer_key) - samples = get!(sim.temporal_state.streams, key, OutputStream()) - push!(samples, (t, val)) - _trim_stream!(samples, t, sim.temporal_state.producer_horizons[producer_key]) - end - - policy = _policy_for_output(model, out_var) - policy isa HoldLast || continue - sim.temporal_state.caches[key] = HoldLastCache(t, val) - end - - return nothing -end diff --git a/src/time/runtime/scopes.jl b/src/time/runtime/scopes.jl deleted file mode 100644 index 7a46d490b..000000000 --- a/src/time/runtime/scopes.jl +++ /dev/null @@ -1,104 +0,0 @@ -const _BUILTIN_SCOPE_SELECTORS = (:global, :plant, :scene, :self) - -""" - _model_spec_for_process(sim, scale, process) - -Return the normalized `ModelSpec` for one `(scale, process)` pair. -""" -function _model_spec_for_process(sim::GraphSimulation, scale::Symbol, process::Symbol) - specs_at_scale = get_model_specs(sim)[scale] - if haskey(specs_at_scale, process) - return specs_at_scale[process] - end - - models_at_scale = get_models(sim)[scale] - haskey(models_at_scale, process) || error( - "Cannot resolve model spec for process `$(process)` at scale `$(scale)`." - ) - return as_model_spec(models_at_scale[process]) -end - -function _find_ancestor_by_symbol(node, target::Symbol) - current = node - while !isnothing(current) - symbol(current) == target && return current - current = parent(current) - end - return nothing -end - -function _scope_from_builtin(selector::Symbol, node, scale::Symbol, process::Symbol) - if selector == :global - return ScopeId(:global, 1) - elseif selector == :self - return ScopeId(:self, node_id(node)) - elseif selector == :plant - plant = _find_ancestor_by_symbol(node, :Plant) - isnothing(plant) && error( - "Scope selector `:plant` for process `$(process)` at scale `$(scale)` ", - "could not find a `Plant` ancestor for node `$(node_id(node))`." - ) - return ScopeId(:plant, node_id(plant)) - elseif selector == :scene - scene = _find_ancestor_by_symbol(node, :Scene) - isnothing(scene) && error( - "Scope selector `:scene` for process `$(process)` at scale `$(scale)` ", - "could not find a `Scene` ancestor for node `$(node_id(node))`." - ) - return ScopeId(:scene, node_id(scene)) - end - - error( - "Unsupported scope selector `$(selector)` for process `$(process)` at scale `$(scale)`. ", - "Supported selectors are $(_BUILTIN_SCOPE_SELECTORS), `ScopeId`, or a callable." - ) -end - -function _scope_from_selector_result(result, node, scale::Symbol, process::Symbol) - if result isa ScopeId - return result - elseif result isa Symbol - return _scope_from_builtin(result, node, scale, process) - end - - error( - "Scope selector for process `$(process)` at scale `$(scale)` must return `ScopeId` or `Symbol`, ", - "got `$(typeof(result))`." - ) -end - -function _scope_from_selector(selector, node, scale::Symbol, process::Symbol) - if selector isa ScopeId - return selector - elseif selector isa Symbol - return _scope_from_builtin(selector, node, scale, process) - elseif selector isa Function - result = if applicable(selector, node, scale, process) - selector(node, scale, process) - elseif applicable(selector, node, scale) - selector(node, scale) - elseif applicable(selector, node) - selector(node) - else - error( - "Scope callable for process `$(process)` at scale `$(scale)` must accept `(node)`, `(node, scale)` ", - "or `(node, scale, process)`." - ) - end - return _scope_from_selector_result(result, node, scale, process) - end - - error( - "Unsupported scope selector type `$(typeof(selector))` for process `$(process)` at scale `$(scale)`." - ) -end - -""" - _scope_for_status(sim, model_spec, scale, process, node) - -Resolve the effective `ScopeId` for one node status and one model process. -""" -function _scope_for_status(sim::GraphSimulation, model_spec, scale::Symbol, process::Symbol, node) - selector = isnothing(model_spec) ? :global : model_scope(model_spec) - return _scope_from_selector(selector, node, scale, process) -end diff --git a/src/traits/parallel_traits.jl b/src/traits/parallel_traits.jl deleted file mode 100644 index b4c927e4b..000000000 --- a/src/traits/parallel_traits.jl +++ /dev/null @@ -1,302 +0,0 @@ -""" - DependencyTrait(T::Type) - -Returns information about the eventual dependence of a model `T` to other time-steps or objects -for its computation. The dependence trait is used to determine if a model is parallelizable -or not. - -The following dependence traits are supported: - -- `TimeStepDependencyTrait`: Trait that defines whether a model can be parallelizable over time-steps for its computation. -- `ObjectDependencyTrait`: Trait that defines whether a model can be parallelizable over objects for its computation. -""" -abstract type DependencyTrait end - -abstract type TimeStepDependencyTrait <: DependencyTrait end -struct IsTimeStepDependent <: TimeStepDependencyTrait end -struct IsTimeStepIndependent <: TimeStepDependencyTrait end - -""" - TimeStepDependencyTrait(::Type{T}) - -Defines the trait about the eventual dependence of a model `T` to other time-steps for its computation. -This dependency trait is used to determine if a model is parallelizable over time-steps or not. - -The following dependency traits are supported: - -- `IsTimeStepDependent`: The model depends on other time-steps for its computation, it cannot be run in parallel. -- `IsTimeStepIndependent`: The model does not depend on other time-steps for its computation, it can be run in parallel. - -All models are time-step dependent by default (*i.e.* `IsTimeStepDependent`). This is probably not right for the -majority of models, but: - -1. It is the safest default, as it will not lead to incorrect results if the user forgets to override this trait -which is not the case for the opposite (i.e. `IsTimeStepIndependent`) -2. It is easy to override this trait for models that are time-step independent - -# See also - -- [`timestep_parallelizable`](@ref): Returns `true` if the model is parallelizable over time-steps, and `false` otherwise. -- [`object_parallelizable`](@ref): Returns `true` if the model is parallelizable over objects, and `false` otherwise. -- [`parallelizable`](@ref): Returns `true` if the model is parallelizable, and `false` otherwise. -- [`ObjectDependencyTrait`](@ref): Defines the trait about the eventual dependence of a model to other objects for its computation. - -# Examples - -Define a dummy process: -```julia -using PlantSimEngine - -# Define a test process: -@process "TestProcess" -``` - -Define a model that is time-step independent: - -```julia -struct MyModel <: AbstractTestprocessModel end - -# Override the time-step dependency trait: -PlantSimEngine.TimeStepDependencyTrait(::Type{MyModel}) = IsTimeStepIndependent() -``` - -Check if the model is parallelizable over time-steps: - -```julia -timestep_parallelizable(MyModel()) # false -``` - -Define a model that is time-step dependent: - -```julia -struct MyModel2 <: AbstractTestprocessModel end - -# Override the time-step dependency trait: -PlantSimEngine.TimeStepDependencyTrait(::Type{MyModel2}) = IsTimeStepDependent() -``` - -Check if the model is parallelizable over time-steps: - -```julia -timestep_parallelizable(MyModel()) # true -``` -""" -TimeStepDependencyTrait(::Type) = IsTimeStepDependent() - -""" - timestep_parallelizable(x::T) - timestep_parallelizable(x::DependencyGraph) - -Returns `true` if the model `x` is parallelizable, i.e. if the model can be computed in parallel -over time-steps, or `false` otherwise. - -The default implementation returns `false` for all models. -If you develop a model that is parallelizable over time-steps, you should add a method to [`ObjectDependencyTrait`](@ref) -for your model. - -Note that this method can also be applied on a [`DependencyGraph`](@ref) directly, in which case it returns `true` if all -models in the graph are parallelizable, and `false` otherwise. - -# See also - -- [`object_parallelizable`](@ref): Returns `true` if the model is parallelizable over time-steps, and `false` otherwise. -- [`parallelizable`](@ref): Returns `true` if the model is parallelizable, and `false` otherwise. -- [`TimeStepDependencyTrait`](@ref): Defines the trait about the eventual dependence of a model to other time-steps for its computation. - -# Examples - -Define a dummy process: -```julia -using PlantSimEngine - -# Define a test process: -@process "TestProcess" -``` - -Define a model that is time-step independent: - -```julia -struct MyModel <: AbstractTestprocessModel end - -# Override the time-step dependency trait: -PlantSimEngine.TimeStepDependencyTrait(::Type{MyModel}) = IsTimeStepIndependent() -``` - -Check if the model is parallelizable over objects: - -```julia -timestep_parallelizable(MyModel()) # true -``` -""" -timestep_parallelizable(x::T) where {T} = timestep_parallelizable(TimeStepDependencyTrait(T), x) -timestep_parallelizable(::IsTimeStepDependent, x) = false -timestep_parallelizable(::IsTimeStepIndependent, x) = true - -""" - ObjectDependencyTrait(::Type{T}) - -Defines the trait about the eventual dependence of a model `T` to other objects for its computation. -This dependency trait is used to determine if a model is parallelizable over objects or not. - -The following dependency traits are supported: - -- `IsObjectDependent`: The model depends on other objects for its computation, it cannot be run in parallel. -- `IsObjectIndependent`: The model does not depend on other objects for its computation, it can be run in parallel. - -All models are object dependent by default (*i.e.* `IsObjectDependent`). This is probably not right for the -majority of models, but: - -1. It is the safest default, as it will not lead to incorrect results if the user forgets to override this trait -which is not the case for the opposite (i.e. `IsObjectIndependent`) -2. It is easy to override this trait for models that are object independent - -# See also - -- [`timestep_parallelizable`](@ref): Returns `true` if the model is parallelizable over time-steps, and `false` otherwise. -- [`object_parallelizable`](@ref): Returns `true` if the model is parallelizable over objects, and `false` otherwise. -- [`parallelizable`](@ref): Returns `true` if the model is parallelizable, and `false` otherwise. -- [`TimeStepDependencyTrait`](@ref): Defines the trait about the eventual dependence of a model to other time-steps for its computation. - -# Examples - -Define a dummy process: -```julia -using PlantSimEngine - -# Define a test process: -@process "TestProcess" -``` - -Define a model that is object independent: - -```julia -struct MyModel <: AbstractTestprocessModel end - -# Override the object dependency trait: -PlantSimEngine.ObjectDependencyTrait(::Type{MyModel}) = IsObjectIndependent() -``` - -Check if the model is parallelizable over objects: - -```julia -object_parallelizable(MyModel()) # false -``` - -Define a model that is object dependent: - -```julia -struct MyModel2 <: AbstractTestprocessModel end - -# Override the object dependency trait: -PlantSimEngine.ObjectDependencyTrait(::Type{MyModel2}) = IsObjectDependent() -``` - -Check if the model is parallelizable over objects: - -```julia -object_parallelizable(MyModel()) # true -``` -""" -abstract type ObjectDependencyTrait <: DependencyTrait end -struct IsObjectDependent <: ObjectDependencyTrait end -struct IsObjectIndependent <: ObjectDependencyTrait end -ObjectDependencyTrait(::Type) = IsObjectDependent() - -""" - object_parallelizable(x::T) - object_parallelizable(x::DependencyGraph) - -Returns `true` if the model `x` is parallelizable, i.e. if the model can be computed in parallel -for different objects, or `false` otherwise. - -The default implementation returns `false` for all models. -If you develop a model that is parallelizable over objects, you should add a method to [`ObjectDependencyTrait`](@ref) -for your model. - -Note that this method can also be applied on a [`DependencyGraph`](@ref) directly, in which case it returns `true` if all -models in the graph are parallelizable, and `false` otherwise. - -# See also - -- [`timestep_parallelizable`](@ref): Returns `true` if the model is parallelizable over time-steps, and `false` otherwise. -- [`parallelizable`](@ref): Returns `true` if the model is parallelizable, and `false` otherwise. -- [`ObjectDependencyTrait`](@ref): Defines the trait about the eventual dependence of a model to other objects for its computation. - -# Examples - -Define a dummy process: -```julia -using PlantSimEngine - -# Define a test process: -@process "TestProcess" -``` - -Define a model that is object independent: - -```julia -struct MyModel <: AbstractTestprocessModel end - -# Override the object dependency trait: -PlantSimEngine.ObjectDependencyTrait(::Type{MyModel}) = IsObjectIndependent() -``` - -Check if the model is parallelizable over objects: - -```julia -object_parallelizable(MyModel()) # true -``` -""" -object_parallelizable(x::T) where {T} = object_parallelizable(ObjectDependencyTrait(T), x) -object_parallelizable(::IsObjectDependent, x) = false -object_parallelizable(::IsObjectIndependent, x) = true - -""" - parallelizable(::T) - object_parallelizable(x::DependencyGraph) - -Returns `true` if the model `T` or the whole dependency graph is parallelizable, *i.e.* if the model can be computed in parallel -for different time-steps or objects. The default implementation returns `false` for all models. - -# See also - -- [`timestep_parallelizable`](@ref): Returns `true` if the model is parallelizable over time-steps, and `false` otherwise. -- [`object_parallelizable`](@ref): Returns `true` if the model is parallelizable over objects, and `false` otherwise. -- [`TimeStepDependencyTrait`](@ref): Defines the trait about the eventual dependence of a model to other time-steps for its computation. - -# Examples - -Define a dummy process: -```julia -using PlantSimEngine - -# Define a test process: -@process "TestProcess" -``` - -Define a model that is parallelizable: - -```julia -struct MyModel <: AbstractTestprocessModel end - -# Override the time-step dependency trait: -PlantSimEngine.TimeStepDependencyTrait(::Type{MyModel}) = IsTimeStepIndependent() - -# Override the object dependency trait: -PlantSimEngine.ObjectDependencyTrait(::Type{MyModel}) = IsObjectIndependent() -``` - -Check if the model is parallelizable: - -```julia -parallelizable(MyModel()) # true -``` - -Or if we want to be more explicit: - -```julia -timestep_parallelizable(MyModel()) -object_parallelizable(MyModel()) -``` -""" -parallelizable(x::T) where {T} = timestep_parallelizable(x) && object_parallelizable(x) \ No newline at end of file diff --git a/src/traits/table_traits.jl b/src/traits/table_traits.jl index 2ba21e17a..0adbe89dd 100644 --- a/src/traits/table_traits.jl +++ b/src/traits/table_traits.jl @@ -1,7 +1,6 @@ abstract type DataFormat end struct TableAlike <: DataFormat end struct SingletonAlike <: DataFormat end -struct TreeAlike <: DataFormat end """ DataFormat(T::Type) @@ -13,11 +12,9 @@ how to iterate over the data. The following data formats are supported: `TimeStepTable`. The data is iterated over by rows using the `Tables.jl` interface. - `SingletonAlike`: The data is a singleton-like object, e.g. a `NamedTuple` or a `TimeStepRow`. The data is iterated over by columns. -- `TreeAlike`: The data is a tree-like object, e.g. a `Node`. - The default implementation returns `TableAlike` for `AbstractDataFrame`, -`TimeStepTable`, `AbstractVector` and `Dict`, `TreeAlike` for `GraphSimulation`, -`SingletonAlike` for `Status`, `SingleScaleModelSet`, `NamedTuple` and `TimeStepRow`. +`TimeStepTable`, `AbstractVector`, and `Dict`; and `SingletonAlike` for +`Status`, `NamedTuple`, and `TimeStepRow`. The default implementation for `Any` throws an error. Users that want to use another input should define this trait for the new data format, e.g.: @@ -51,15 +48,11 @@ DataFormat(::Type{<:DataFrames.AbstractDataFrame}) = TableAlike() DataFormat(::Type{<:PlantMeteo.TimeStepTable}) = TableAlike() DataFormat(::Type{<:PlantMeteo.TimeStepRows}) = TableAlike() -# Giving a SingleScaleModelSet as a vector or a dict of objects: DataFormat(::Type{<:AbstractVector}) = TableAlike() DataFormat(::Type{<:Dict}) = TableAlike() DataFormat(::Type{<:NamedTuple}) = SingletonAlike() DataFormat(::Type{<:Status}) = SingletonAlike() -DataFormat(::Type{<:SingleScaleModelSet{Mo,S} where {Mo,S}}) = SingletonAlike() -DataFormat(::Type{<:ModelMapping{SingleScale}}) = SingletonAlike() -DataFormat(::Type{<:GraphSimulation}) = TreeAlike() DataFormat(::Type{<:PlantMeteo.AbstractAtmosphere}) = SingletonAlike() DataFormat(::Type{<:PlantMeteo.TimeStepRow}) = SingletonAlike() @@ -67,3 +60,8 @@ DataFormat(::Type{<:Nothing}) = SingletonAlike() # For meteo == Nothing DataFormat(T::Type{<:Any}) = error("Unknown data format: $T.\nPlease define a `DataFormat` method, e.g.: DataFormat(::Type{$T}) method.") DataFormat(x::T) where {T} = DataFormat(T) DataFormat(::Type{<:DataFrames.DataFrameRow}) = SingletonAlike() + +get_nsteps(value) = get_nsteps(DataFormat(value), value) +get_nsteps(::SingletonAlike, value) = 1 +get_nsteps(::TableAlike, value) = length(Tables.rows(value)) +get_nsteps(::TableAlike, value::PlantMeteo.TimeStepRows) = length(value) diff --git a/src/variables_wrappers.jl b/src/variables_wrappers.jl index b6accd3aa..9dbce12fa 100644 --- a/src/variables_wrappers.jl +++ b/src/variables_wrappers.jl @@ -1,28 +1,9 @@ -""" - UninitializedVar(variable, value) - -A variable that is not initialized yet, it is given a name and a default value. -""" -struct UninitializedVar{T} - variable::Symbol - value::T -end - -Base.eltype(u::UninitializedVar{T}) where {T} = T -source_variable(m::UninitializedVar) = m.variable -source_variable(m::UninitializedVar, org) = m.variable - """ PreviousTimeStep(variable) -A structure to manually flag a variable in a model to use the value computed on the previous time-step. -This implies that the variable is not used to build the dependency graph because the dependency graph only -applies on the current time-step. This is used to avoid circular dependencies when a variable depends on itself. +A structure to flag a model input as using the value computed on the previous +scene timestep. This breaks same-timestep coupling cycles. The value can be initialized in the Status if needed. - -The process is added when building the MultiScaleModel, to avoid conflicts between processes with the same variable name. -For exemple one process can define a variable `:carbon_biomass` as a `PreviousTimeStep`, but the othe process would use -the variable as a dependency for the current time-step (and it would be fine because theyr don't share the same issue of cyclic dependency). """ struct PreviousTimeStep variable::Symbol @@ -30,15 +11,3 @@ struct PreviousTimeStep end PreviousTimeStep(v::Symbol) = PreviousTimeStep(v, :unknown) - -""" - RefVariable(reference_variable) - -A structure to manually flag a variable in a model to use the value of another variable **at the same scale**. -This is used for variable renaming, when a variable is computed by a model but is used by another model with a different name. - -Note: we don't really rename the variable in the status (we need it for the other models), but we create a new one that is a reference to the first one. -""" -struct RefVariable - reference_variable::Symbol -end \ No newline at end of file diff --git a/test/Project.toml b/test/Project.toml index f806effc7..18fc22273 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -1,6 +1,5 @@ [deps] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" -BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" diff --git a/test/helper-functions.jl b/test/helper-functions.jl deleted file mode 100644 index cb35b8188..000000000 --- a/test/helper-functions.jl +++ /dev/null @@ -1,431 +0,0 @@ -# doesn't check for mtg equality -function compare_outputs_graphsim(graphsim, graphsim2) - outputs_df_dict = convert_outputs(graphsim.outputs, DataFrame) - outputs2_df_dict = convert_outputs(graphsim2.outputs, DataFrame) - - if length(outputs_df_dict) != length(outputs2_df_dict) - return false - end - - for (organ, vals) in outputs2_df_dict - outputs_df_sorted = outputs_df_dict[organ][:, sortperm(names(outputs_df_dict[organ]))] - outputs2_df_sorted = outputs2_df_dict[organ][:, sortperm(names(outputs2_df_dict[organ]))] - - if outputs_df_sorted != outputs2_df_sorted - return false - end - end - - return true -end - -function compare_outputs_modellists(filtered_outputs_1, filtered_outputs_2) - models_df_1 = DataFrame(filtered_outputs_1) - models_df_sorted_1 = models_df_1[:, sortperm(names(models_df_1))] - models_df_2 = DataFrame(filtered_outputs_2) - models_df_sorted_2 = models_df_2[:, sortperm(names(models_df_2))] - return models_df_sorted_2 == models_df_sorted_1 -end - -function compare_outputs_modellist_mapping(filtered_outputs_modellist, graphsim) - modellist_df = DataFrame(filtered_outputs_modellist) - modellist_sorted = modellist_df[:, sortperm(names(modellist_df))] - - outputs_df = convert_outputs(graphsim.outputs, DataFrame) - @assert haskey(outputs_df, :Default) - common_cols = filter(c -> c in names(outputs_df[:Default]), names(modellist_sorted)) - mapping_sorted = outputs_df[:Default][:, common_cols] - modellist_sorted = modellist_sorted[:, common_cols] - - # Keep deterministic order in case columns are provided in different orders. - mapping_sorted = mapping_sorted[:, sortperm(names(mapping_sorted))] - modellist_sorted = modellist_sorted[:, sortperm(names(modellist_sorted))] - - return modellist_sorted == mapping_sorted -end - -function run_graphsim_for_comparison(mtg, mapping, nsteps, outputs_mapping, meteo) - graphsim = PlantSimEngine.GraphSimulation(mtg, mapping, nsteps=nsteps, check=true, outputs=outputs_mapping) - run!( - graphsim, - meteo, - PlantMeteo.Constants(), - nothing; - check=true, - executor=SequentialEx(), - ) - return graphsim -end - -# Helper used to compare a single-scale `ModelMapping` run with its generated -# multiscale equivalent. -function check_multiscale_simulation_is_equivalent_begin( - mapping::PlantSimEngine.ModelMapping, - meteo, -) - _, models_at_scale = only(pairs(mapping)) - status_nt = NamedTuple(something(PlantSimEngine.get_status(models_at_scale), Status())) - models = PlantSimEngine.ModelMapping(PlantSimEngine.get_models(models_at_scale)...; status=status_nt) - mtg, mapping, out = PlantSimEngine.modellist_to_mapping(models, status_nt; nsteps=length(meteo), outputs=nothing) - return mtg, mapping, out -end - -function check_multiscale_simulation_is_equivalent_end(modellist_outputs, mtg, mapping, out, meteo) - graph_sim = run_graphsim_for_comparison(mtg, mapping, PlantSimEngine.get_nsteps(meteo), out, meteo) - return compare_outputs_modellist_mapping(modellist_outputs, graph_sim) -end - -function check_multiscale_simulation_is_equivalent( - mapping::PlantSimEngine.ModelMapping, - meteo, -) - modellist_outputs = run!(mapping, meteo) - mtg, mapping_mt, out = check_multiscale_simulation_is_equivalent_begin(mapping, meteo) - return check_multiscale_simulation_is_equivalent_end(modellist_outputs, mtg, mapping_mt, out, meteo) -end - -# Quick and naive first version. Doesn't check if everything is timestep parallelizable, doesn't check for nthreads etc. -function run_single_and_multi_thread_modellist( - mapping::PlantSimEngine.ModelMapping, - tracked_outputs, - meteo, -) - out_seq = run!(mapping, meteo; tracked_outputs=tracked_outputs, executor=SequentialEx()) - mapping_mt = copy(mapping) - out_mt = run!(mapping_mt, meteo; tracked_outputs=tracked_outputs, executor=ThreadedEx()) - return out_seq, out_mt -end - -# Could make use of PlantMeteo's online meteo data recovery feature for more numerous examples -# or the random meteo generation used for the PBP benchmark - -#=using PlantMeteo, Dates, DataFrames -# Define the period of the simulation: -period = [Dates.Date("2021-01-01"), Dates.Date("2021-12-31")] -# Get the weather data for CIRAD's site in Montpellier, France: -meteo = get_weather(43.649777, 3.869889, period, sink = DataFrame)=# - -function get_simple_meteo_bank() - - df = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18) - - - meteos = - [Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_f=300.0), #=nothing,=# - Weather([Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65)]), - Weather( - [ - Atmosphere(T=20.0, Wind=1.0, Rh=0.65, Ri_PAR_f=300.0), - Atmosphere(T=25.0, Wind=0.5, Rh=0.8, Ri_PAR_f=500.0) - ]), Weather([Atmosphere(T=20.0, Wind=1.0, Rh=0.65, Ri_PAR_f=200.0), - Atmosphere(T=18.0, Wind=1.0, Rh=0.65, Ri_PAR_f=100.0), - Atmosphere(T=19.0, Wind=1.0, Rh=0.65, Ri_PAR_f=200.0), - Atmosphere(T=30.0, Wind=0.5, Rh=0.6, Ri_PAR_f=100.0), - Atmosphere(T=20.0, Wind=1.0, Rh=0.6, Ri_PAR_f=200.0), - Atmosphere(T=25.0, Wind=1.0, Rh=0.6, Ri_PAR_f=200.0), - Atmosphere(T=10.0, Wind=0.5, Rh=0.6, Ri_PAR_f=200.0)]), - df, - df[1, :], - DataFrame(df[1, :]), - ] - return meteos -end - -function get_modellist_bank() - meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18) - - rue = 0.3 - - vals = (var1=15.0, var2=0.3)#, TT_cu=cumsum(meteo_day.TT)) - vals2 = (TT_cu=cumsum(meteo_day.TT),) - vals3 = (var1=15.0, var2=0.3) - vals4 = (var9=1.0, var0=1.0) - vals5 = (var0=1.0,) - vals6 = (var0=1.0,) - - status_tuples = [vals, vals2, vals3, vals4, vals5, vals6] - - models = [PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - status=vals - ), - PlantSimEngine.ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(rue), - status=vals2, - ), PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=vals3 - ), PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - process4=Process4Model(), - process5=Process5Model(), - process6=Process6Model(), - # process7=Process7Model(), - status=vals4 - ), PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - process4=Process4Model(), - process5=Process5Model(), - process6=Process6Model(), - process7=Process7Model(), - status=vals5 - ), PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - process4=Process4Model(), - process5=Process5Model(), - status=vals6 - ),] - - outputs_tuples_vectors = - [ - # this one has one tuple with a duplicate, and one with a nonexistent variable - [NamedTuple(), (:var1,), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var5), #=(:var1, :var1),=# - (:var1, :var2, :var3, :var4, :var5)], #=(:var2, :var7, :var3, :var1),=# - [NamedTuple(), (:TT_cu,), (:TT_cu, :LAI), (:biomass, :LAI), (:TT_cu, :LAI, :aPPFD, :biomass, :biomass_increment),], [NamedTuple(), (:var1,), (:var1, :var4), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var6, :var5), - (:var1, :var2, :var3, :var4, :var5, :var6)], #=(:var2, :var7, :var3, :var1),=# - [NamedTuple(), (:var1,), (:var1, :var4), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var6, :var5), - (:var2, :var7, :var3, :var1), (:var1, :var2, :var3, :var4, :var5, :var6)], [NamedTuple(), (:var1,), (:var1, :var4), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var6, :var5), - (:var2, :var7, :var3, :var1), (:var1, :var2, :var3, :var4, :var5, :var6), (:var1, :var2, :var3, :var4, :var5, :var6, :var7, :var8, :var9)], [NamedTuple(), (:var1,), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var6, :var5), #=(:var1, :var1),=# - (:var2, :var7, :var3, :var1), (:var1, :var2, :var3, :var4, :var5, :var6),], #=(:var1, :var2, :var3, :var4, :var5, :var6, :var7, :var8, :var9, :var0)=# - ] - - return models, status_tuples, outputs_tuples_vectors -end - -function get_modelmapping_bank() - meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18) - - rue = 0.3 - - vals = (var1=15.0, var2=0.3) - vals2 = (TT_cu=cumsum(meteo_day.TT),) - vals3 = (var1=15.0, var2=0.3) - vals4 = (var9=1.0, var0=1.0) - vals5 = (var0=1.0,) - vals6 = (var0=1.0,) - - status_tuples = [vals, vals2, vals3, vals4, vals5, vals6] - - mappings = [ - PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - status=vals - ), - PlantSimEngine.ModelMapping( - ToyLAIModel(), - Beer(0.5), - ToyRUEGrowthModel(rue); - status=vals2 - ), - PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=vals3 - ), - PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - process4=Process4Model(), - process5=Process5Model(), - process6=Process6Model(), - status=vals4 - ), - PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - process4=Process4Model(), - process5=Process5Model(), - process6=Process6Model(), - process7=Process7Model(), - status=vals5 - ), - PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - process4=Process4Model(), - process5=Process5Model(), - status=vals6 - ), - ] - - outputs_tuples_vectors = - [ - [NamedTuple(), (:var1,), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var5), - (:var1, :var2, :var3, :var4, :var5)], [NamedTuple(), (:TT_cu,), (:TT_cu, :LAI), (:biomass, :LAI), (:TT_cu, :LAI, :aPPFD, :biomass, :biomass_increment),], [NamedTuple(), (:var1,), (:var1, :var4), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var6, :var5), - (:var1, :var2, :var3, :var4, :var5, :var6)], [NamedTuple(), (:var1,), (:var1, :var4), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var6, :var5), - (:var2, :var7, :var3, :var1), (:var1, :var2, :var3, :var4, :var5, :var6)], [NamedTuple(), (:var1,), (:var1, :var4), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var6, :var5), - (:var2, :var7, :var3, :var1), (:var1, :var2, :var3, :var4, :var5, :var6), (:var1, :var2, :var3, :var4, :var5, :var6, :var7, :var8, :var9)], [NamedTuple(), (:var1,), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var6, :var5), - (:var2, :var7, :var3, :var1), (:var1, :var2, :var3, :var4, :var5, :var6)],] - - return mappings, status_tuples, outputs_tuples_vectors -end - -# Could add some mtg variation too -function get_simple_mapping_bank() - mappings = [ - PlantSimEngine.ModelMapping( - :Scene => ToyDegreeDaysCumulModel(), - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[:TT_cu => (:Scene => :TT_cu),],), - Beer(0.6), - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - :carbon_allocation => [:Leaf, :Internode],],), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],],),), - :Internode => ( - PlantSimEngine.MultiScaleModel( - model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - mapped_variables=[:TT => (:Scene => :TT),],), - PlantSimEngine.MultiScaleModel( - model=ToyInternodeEmergence(TT_emergence=20.0), - mapped_variables=[:TT_cu => (:Scene => :TT_cu)],), - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - Status(carbon_biomass=1.0)), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content), :aPPFD => (:Plant => :aPPFD)],), - PlantSimEngine.MultiScaleModel( - model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - mapped_variables=[:TT => (:Scene => :TT),],), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - Status(carbon_biomass=1.0)), - :Soil => (ToySoilWaterModel(),),), - ########## - PlantSimEngine.ModelMapping( - :Default => ( - Process1Model(1.0), - Status(var1=15.0, var2=0.3,),),), - ########## - PlantSimEngine.ModelMapping( - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - # inputs - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - # outputs - :carbon_allocation => [:Leaf, :Internode],],), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],],),), - :Internode => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - Status(TT=10.0, carbon_biomass=1.0)), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], - # Notice we provide :Soil, not [:Soil], so a single value is expected here - ), - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - Status(aPPFD=1300.0, TT=10.0, carbon_biomass=1.0), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025),), - :Soil => (ToySoilWaterModel(),),), - ################## - ] - - out_vars_vectors = [ - [nothing, - NamedTuple(), - Dict(), - #Dict(:Leaf => NamedTuple()), # incorrect - Dict(:Leaf => (:carbon_allocation,),), - Dict(:Leaf => (:carbon_demand,),), - Dict( - :Leaf => (:carbon_assimilation, :carbon_demand, :soil_water_content, :carbon_allocation), - :Internode => (:carbon_allocation, :TT_cu_emergence), - :Plant => (:carbon_allocation,), - :Soil => (:soil_water_content,),),], - ############# - [nothing, - NamedTuple(), - Dict(:Default => (:var1,)) - ], - ############# - [ - nothing, - NamedTuple(), - Dict( - :Leaf => (:carbon_assimilation, :carbon_demand), - :Soil => (:soil_water_content,), - ),], - ] - mtgs = [ - import_mtg_example(), - MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Default, 0, 0),), - import_mtg_example() - ] - return mtgs, mappings, out_vars_vectors -end - - -function test_filtered_output_begin( - m::PlantSimEngine.ModelMapping, - status_tuple, - requested_outputs, - meteo, -) - - nsteps = PlantSimEngine.get_nsteps(meteo) - preallocated_outputs = PlantSimEngine.pre_allocate_outputs(m, requested_outputs, nsteps) - @test length(preallocated_outputs) == nsteps - if length(requested_outputs) > 0 - @test length(preallocated_outputs[1]) == length(requested_outputs) - else - # don't compare with the status because unnecessary variables in the status are discarded in the filtered outputs - out_vars_all = merge(init_variables(m; verbose=false)...) - println(out_vars_all) - @test length(preallocated_outputs[1]) == length(out_vars_all) - end - - filtered_outputs_modellist = run!(m, meteo; tracked_outputs=requested_outputs, executor=SequentialEx()) - - # compare filtered output of a modellist with the filtered output of the equivalent simulation in multiscale mode - mtg, mapping, outputs_mapping = PlantSimEngine.modellist_to_mapping(m, status_tuple; nsteps=nsteps, outputs=requested_outputs) - - return mtg, mapping, outputs_mapping, nsteps, filtered_outputs_modellist -end - -function test_filtered_output(mtg, mapping, nsteps, outputs_mapping, meteo, filtered_outputs_modellist) - graphsim = run_graphsim_for_comparison(mtg, mapping, nsteps, outputs_mapping, meteo) - return compare_outputs_modellist_mapping(filtered_outputs_modellist, graphsim) -end - -function test_filtered_output( - m::PlantSimEngine.ModelMapping, - status_tuple, - requested_outputs, - meteo, -) - mtg, mapping, outputs_mapping, nsteps, filtered_outputs_modellist = - test_filtered_output_begin(m, status_tuple, requested_outputs, meteo) - - @test to_initialize(mapping) == Dict() - return test_filtered_output(mtg, mapping, nsteps, outputs_mapping, meteo, filtered_outputs_modellist) -end diff --git a/test/runtests.jl b/test/runtests.jl index db9fb837e..70a1e7bdd 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -7,8 +7,6 @@ using MultiScaleTreeGraph using PlantMeteo, Statistics using Documenter # for doctests -include("helper-functions.jl") - # There are 3 kinds of tests : # PSE functionality/feature tests # Integration tests (launched in Github Actions, they run PBP and XPalm tests) @@ -18,30 +16,10 @@ include("helper-functions.jl") Aqua.test_all(PlantSimEngine, ambiguities=false) Aqua.test_ambiguities([PlantSimEngine]) - @testset "ModelMapping: single scale" begin - include("test-ModelMapping.jl") - end - - @testset "ModelMapping: multi scale" begin - include("test-mapping.jl") - end - - @testset "Multi-rate scaffolding" begin - include("test-multirate-scaffolding.jl") - end - @testset "Unified scene/object API" begin include("test-unified-scene-object-api.jl") end - @testset "Multi-rate runtime" begin - include("test-multirate-runtime.jl") - end - - @testset "Multi-rate output export" begin - include("test-multirate-output-export.jl") - end - @testset "ModelSpec Updates" begin include("test-updates.jl") end @@ -58,10 +36,6 @@ include("helper-functions.jl") include("test-maespa-scene-example.jl") end - @testset "MultiScaleModel" begin - include("test-MultiScaleModel.jl") - end - @testset "Status" begin include("test-Status.jl") end @@ -70,14 +44,6 @@ include("helper-functions.jl") include("test-TimeStepTable.jl") end - @testset "Dimensions" begin - include("test-dimensions.jl") - end - - @testset "Simulations" begin - include("test-simulation.jl") - end - @testset "Statistics" begin include("test-statistics.jl") end @@ -90,20 +56,6 @@ include("helper-functions.jl") include("test-toy_models.jl") end - @testset "MTG with multiscale mapping" begin - include("test-mtg-multiscale.jl") - include("test-mtg-dynamic.jl") - include("test-mtg-multiscale-cyclic-dep.jl") - end - - @testset "Multiscale corner-cases" begin - include("test-corner-cases.jl") - end - - @testset "Multithreading" begin - include("test-performance.jl") - end - if VERSION >= v"1.10" # Some formating changed in Julia 1.10, e.g. @NamedTuple instead of NamedTuple. @testset "Doctests" begin diff --git a/test/test-ModelMapping.jl b/test/test-ModelMapping.jl deleted file mode 100644 index 43c2bba79..000000000 --- a/test/test-ModelMapping.jl +++ /dev/null @@ -1,323 +0,0 @@ -# Tests: -# Defining a list of models without status: - -@testset "ModelMapping with no status" begin - leaf = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model() - ) - - inits = merge(init_variables(leaf.models)...) - st = Status{keys(inits)}(values(inits)) - @test all(getproperty(leaf.status, i)[1] == getproperty(st, i) for i in keys(st)) - @test !is_initialized(leaf) - @test to_initialize(leaf) == (process1=(:var1, :var2), process2=(:var1,)) - @test length(status(leaf)) == 5 - - # Requiring 3 steps for initialization: - leaf = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - ) - - @test length(status(leaf)) == 5 - @test status(leaf, :var1) == -Inf -end; - - -@testset "process" begin - @test PlantSimEngine.process(Process1Model(1.0)) == :process1 - @test PlantSimEngine.process(:process1 => Process1Model(1.0)) == :process1 - - models = - ( - Process1Model(1.0), - Process2Model() - ) - - @test [(process(i), i) for i in models] == Tuple{Symbol,AbstractModel}[(:process1, Process1Model(1.0)), (:process2, Process2Model())] - - models_named = ( - process1=Process1Model(1.0), - process2=Process2Model() - ) - - @test [(process(i), i) for i in models_named] == [(process(i), i) for i in models] -end - -@testset "ModelMapping with no process names" begin - with_names = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model() - ) - - without_names = PlantSimEngine.ModelMapping( - Process1Model(1.0), - Process2Model() - ) - - @test with_names.models == without_names.models - @test with_names.status.var1 == without_names.status.var1 - @test with_names.status.var2 == without_names.status.var2 -end; - -@testset "ModelMapping with a partially initialized status" begin - leaf = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - status=(var1=15.0,) - ) - - inits = merge(init_variables(leaf.models)...) - st = Status{keys(inits)}(values(inits)) - st.var1 = 15.0 - @test all(getproperty(leaf.status, i)[1] == getproperty(st, i) for i in keys(st)) - @test !is_initialized(leaf) - @test to_initialize(leaf) == (process1=(:var2,),) - - # Requiring 3 steps for initialization: - leaf = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - status=(var1=15.0,), - ) - - @test length(status(leaf)) == 5 - @test status(leaf, :var1) == 15.0 -end; - -@testset "ModelMapping with fully initialized status" begin - vals = (var1=15.0, var2=0.3) - leaf = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - status=vals - ) - - inits = merge(init_variables(leaf.models)...) - st = Status{keys(inits)}(values(inits)) - - for i in keys(vals) - setproperty!(st, i, getproperty(vals, i)) - end - @test all(getproperty(leaf.status, i)[1] == getproperty(st, i) for i in keys(st)) - - @test is_initialized(leaf) - @test to_initialize(leaf) == NamedTuple() -end; - - -@testset "ModelMapping with independant models (and missing one in the middle)" begin - vals = (var1=15.0, var2=0.3) - leaf = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process3=Process3Model(), - status=vals - ) - - @test to_initialize(leaf) == (process3=(:var5,),) - - # NB: decompose this test because the order of the variables change with the Julia version - inits = init_variables(leaf) - sorted_vars = sort([keys(inits.process3)...]) - - @test [getfield(inits.process3, i) for i in sorted_vars] == fill(-Inf, 3) -end; - -@testset "Copy a ModelMapping" begin - vars = (var1=15.0, var2=0.3) - # Create a model list: - models = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=vars - ) - - # Copy the model list: - ml2 = copy(models) - - @test DataFrame(TimeStepTable([status(ml2)])) == DataFrame(TimeStepTable([status(models)])) - - # Copy the model list with new status: - st = Status(var1=20.0, var2=0.5) - ml3 = copy(models, st) - - @test status(ml3) == st - @test ml3.models == models.models - - - cp_models = copy([models, ml3]) - @test cp_models == [models, ml3] - - cp_models = copy(Dict("models" => models, "ml3" => ml3)) - @test cp_models == Dict("models" => models, "ml3" => ml3) -end; - -@testset "Convert ModelMapping status variables into new types" begin - ref_vars = init_variables( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - ) - type_promotion = Dict(Real => Float32) - - process3_Float32 = PlantSimEngine.convert_vars(ref_vars.process3, type_promotion) - - @test all([isa(getfield(process3_Float32, i), Float32) for i in keys(process3_Float32)]) - - process3_same = PlantSimEngine.convert_vars(ref_vars.process3, nothing) - @test process3_same == ref_vars.process3 - - mapping = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(); - type_promotion=type_promotion, - ) - - @test mapping.type_promotion == type_promotion - @test all(isa(status(mapping)[var], Float32) for var in keys(status(mapping))) -end - - -@testset "ModelMapping dependencies" begin - - models = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - process4=Process4Model(), - process5=Process5Model(), - process6=Process6Model(), - # process7=Process7Model(), - # status=(var1=15.0, var2=0.3) - ) - - deps = dep(models).roots - - @test collect(keys(deps)) == [:process4] - - @test deps[:process4].value == Process4Model() - @test isa(deps[:process4], PlantSimEngine.SoftDependencyNode) - - process3 = deps[:process4].children[1] - @test process3.value == Process3Model() - @test isa(process3, PlantSimEngine.SoftDependencyNode) - - @test process3.hard_dependency[1].value == Process2Model() - @test isa(process3.hard_dependency[1], PlantSimEngine.HardDependencyNode) - - @test process3.hard_dependency[1].children[1].value == Process1Model(1.0) - @test isa(process3.hard_dependency[1].children[1], PlantSimEngine.HardDependencyNode) - - @test process3.children[1].value == Process5Model() - @test isa(process3.children[1], PlantSimEngine.SoftDependencyNode) -end - - - - -# very naive function, doesn't generate full partition sets -# insert_errors : could duplicate a value, add a nonexistent one, make one the wrong type ? -#=function generate_output_tuple(vars_tuple, insert_errors, count) - - outputs_tuples_vector = [NamedTuple()] - - # number not exact, but trying every permutation sounds like a waste of time - for i in 1:max(count, length(vars_tuple)) - new_tuple = () - # TODO - new_tuple = (new_tuple..., new_var) - end - return outputs_tuples_vector -end=# - - - -@testset "ModelMapping outputs preallocation" begin - meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18) - vals = (var1=15.0, var2=0.3, TT_cu=cumsum(meteo_day.TT)) - leaf = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - status=vals - ) - outs = (:var3,) - - @test test_filtered_output(leaf, vals, outs, meteo_day) - - meteos = - [Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_f=300.0), - CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18), - ] - modellists, status_tuples, outs_vectors = get_modellist_bank() - - # remove some of the currently unhandled cases - outs_vectors = - [ - # this one has one tuple with a duplicate, and one with a nonexistent variable - [(:var1,), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var5), #=(:var1, :var1),=# - (:var1, :var2, :var3, :var4, :var5)], #=(:var2, :var7, :var3, :var1),=# - [(:TT_cu,), (:TT_cu, :LAI), (:biomass, :LAI), (:TT_cu, :LAI, :aPPFD, :biomass, :biomass_increment),], #=NamedTuple(),=# - [(:var1,), (:var1, :var4), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var6, :var5), #=NamedTuple(),=# - (:var1, :var2, :var3, :var4, :var5, :var6)], #=(:var2, :var7, :var3, :var1),=# - [(:var1,), (:var1, :var4), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var6, :var5), #=NamedTuple(),=# - (:var2, :var7, :var3, :var1), (:var1, :var2, :var3, :var4, :var5, :var6)], - [(:var1,), (:var1, :var4), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var6, :var5), #=NamedTuple(),=# - (:var2, :var7, :var3, :var1), (:var1, :var2, :var3, :var4, :var5, :var6), (:var1, :var2, :var3, :var4, :var5, :var6, :var7, :var8, :var9)], - [(:var1,), (:var1, :var2), (:var1, :var3), (:var1, :var4, :var6, :var5), #=(:var1, :var1),=# - (:var2, :var7, :var3, :var1), (:var1, :var2, :var3, :var4, :var5, :var6), (:var1, :var2, :var3, :var4, :var5, :var6, :var7, :var0)], #=:var8, :var9,=# - ] - - - - for i in 1:length(modellists) - - modellist = modellists[i] - status_tuple = status_tuples[i] - outs_vector = outs_vectors[i] - all_vars = init_variables(modellist) - - #insert_errors = true - #outs_vector = generate_output_tuple(all_vars, insert_errors) - - for j in 1:length(meteos) - meteo = meteos[j] - for k in 1:length(outs_vector) - out_tuple = outs_vector[k] - #print(i, " ", j, " ", k) - meteo_adjusted = PlantSimEngine.adjust_weather_timesteps_to_given_length( - PlantSimEngine.get_status_vector_max_length(modellist.status), meteo) - @test test_filtered_output(modellist, status_tuple, out_tuple, meteo_adjusted) - end - end - end - - #mtg, mapping, outputs_mapping, nsteps, filtered_outputs_modellist = test_filtered_output_begin(modellists[1], status_tuples[1], outs_vectors[1][1], meteos[1]) - #@test test_filtered_output(mtg, mapping, nsteps, outputs_mapping, meteo_day, filtered_outputs_modellist) -end - - -PlantSimEngine.@process "modellist_cycle" verbose = false - -struct Reeb{T} <: AbstractModellist_CycleModel - k::T -end - -function PlantSimEngine.run!(::Reeb, models, status, meteo, constants, extra=nothing) - status.LAI = - status.aPPFD + 0.4 * k -end - -function PlantSimEngine.inputs_(::Reeb) - (aPPFD=-Inf,) -end - -function PlantSimEngine.outputs_(::Reeb) - (LAI=-Inf,) -end - -@testset "ModelMapping simple cyclic dependency detection" begin - @test_throws "Cyclic" m = PlantSimEngine.ModelMapping(Beer(0.5), Reeb(0.5)) -end diff --git a/test/test-MultiScaleModel.jl b/test/test-MultiScaleModel.jl deleted file mode 100644 index c27b0402d..000000000 --- a/test/test-MultiScaleModel.jl +++ /dev/null @@ -1,83 +0,0 @@ -@testset "MultiScaleModel: mapping formatting" begin - std_mapping = :plant_surfaces => (:Plant => :plant_surfaces) - @test PlantSimEngine._get_var(:plant_surfaces => (:Plant => :plant_surfaces)) == std_mapping # Case 1 - @test PlantSimEngine._get_var(:plant_surfaces => [:Plant]) == (:plant_surfaces => [:Plant => :plant_surfaces]) # Case 2 - @test PlantSimEngine._get_var(:plant_surfaces => [:Plant, :Leaf]) == (:plant_surfaces => [:Plant => :plant_surfaces, :Leaf => :plant_surfaces]) # Case 3 - @test PlantSimEngine._get_var(:plant_surfaces => (:Plant => :plant_surfaces)) == std_mapping # Similar to case 1 - @test PlantSimEngine._get_var(:plant_surfaces => (:Plant => :surface)) == (:plant_surfaces => (:Plant => :surface)) # Case 4 - @test PlantSimEngine._get_var(:plant_surfaces => :Plant) == std_mapping # Case 1 shorthand with symbol scale - @test PlantSimEngine._get_var(:plant_surfaces => [:Plant => :surface, :Leaf => :surface]) == (:plant_surfaces => [:Plant => :surface, :Leaf => :surface]) # Case 5 - @test PlantSimEngine._get_var(:plant_surfaces => [:Plant => :surface_1, :Leaf => :surface_2]) == (:plant_surfaces => [:Plant => :surface_1, :Leaf => :surface_2]) # Case 5 - @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => (:Plant => :plant_surfaces)) == (PreviousTimeStep(:plant_surfaces, :unknown) => :Plant => :plant_surfaces) # Case 6 - @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => :Plant => :surface) == (PreviousTimeStep(:plant_surfaces, :unknown) => :Plant => :surface) # Case 6 - @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => [:Plant => :surface, :Leaf => :surface]) == (PreviousTimeStep(:plant_surfaces, :unknown) => [:Plant => :surface, :Leaf => :surface]) # Case 6 - @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces)) == (PreviousTimeStep(:plant_surfaces, :unknown) => PlantSimEngine.SameScale() => :plant_surfaces) # Case 7 - @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => (PlantSimEngine.SameScale() => :surface)) == (PreviousTimeStep(:plant_surfaces, :unknown) => PlantSimEngine.SameScale() => :surface) - @test PlantSimEngine._get_var(PreviousTimeStep(:plant_surfaces) => (PlantSimEngine.SameScale() => :surface), :test) == (PreviousTimeStep(:plant_surfaces, :test) => PlantSimEngine.SameScale() => :surface) -end; - -@testset "MultiScaleModel: case 1" begin - models = PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[:TT_cu => (:Scene => :TT_cu),], - ) - - @test models.model == ToyLAIModel() - @test models.mapped_variables == [:TT_cu => (:Scene => :TT_cu)] -end; - -@testset "MultiScaleModel: case 2" begin - models = PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[:TT_cu => [:Plant],], - ) - - @test models.model == ToyLAIModel() - @test models.mapped_variables == [:TT_cu => [:Plant => :TT_cu]] - - - models = PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[:TT_cu => [:Leaf, :Internode],], - ) - - @test models.model == ToyLAIModel() - @test models.mapped_variables == [:TT_cu => [:Leaf => :TT_cu, :Internode => :TT_cu]] -end; - - -@testset "MultiScaleModel: case 2, several variables with different format" begin - models = PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[:carbon_assimilation => [:Leaf], :carbon_demand => [:Leaf, :Internode], :Rm => (:Plant => :Rm_plant)], - ) - - @test models.model == ToyCAllocationModel() - @test models.mapped_variables == [:carbon_assimilation => [:Leaf => :carbon_assimilation], :carbon_demand => [:Leaf => :carbon_demand, :Internode => :carbon_demand], :Rm => (:Plant => :Rm_plant)] -end; - - -@testset "MultiScaleModel: case with PreviousTimeStep => ..." begin - models = PlantSimEngine.MultiScaleModel( - model=ToyLAIfromLeafAreaModel(1.0), - mapped_variables=[ - PreviousTimeStep(:plant_surfaces) => :Plant => :surface, - ], - ) - - @test models.model == ToyLAIfromLeafAreaModel(1.0) - @test models.mapped_variables == [PreviousTimeStep(:plant_surfaces, :LAI_Dynamic) => (:Plant => :surface)] -end; - -@testset "MultiScaleModel: several types of mapping" begin - models = PlantSimEngine.MultiScaleModel( - model=ToyLightPartitioningModel(), - mapped_variables=[ - :aPPFD_larger_scale => (:Scene => :aPPFD), - :total_surface => (:Scene => :total_surface) - ], - ) - - @test models.model == ToyLightPartitioningModel() - @test models.mapped_variables == [:aPPFD_larger_scale => (:Scene => :aPPFD), :total_surface => (:Scene => :total_surface)] -end diff --git a/test/test-Status.jl b/test/test-Status.jl index ce107c581..244f5cd96 100644 --- a/test/test-Status.jl +++ b/test/test-Status.jl @@ -29,47 +29,3 @@ mnt2[:b] = "hello" @test mnt2.b == "hello" end - -@testset "Testing ModelMapping Status" begin - # Create a ModelMapping - models = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=[15.0, 16.0], var2=0.3) - ) - - @test typeof(status(models)) == Status{ - (:var5, :var4, :var6, :var1, :var3, :var2), - Tuple{ - Base.RefValue{Float64},Base.RefValue{Float64},Base.RefValue{Float64}, - Base.RefValue{Vector{Float64}},Base.RefValue{Float64},Base.RefValue{Float64}} - } - - - @test status(models) == models.status - @test status(models)[1] == status(models, 1) - - @test typeof(status(models, 1)) == Float64 - @test typeof(status(models, 4)) == Vector{Float64} - - @test status(models, :var1)[1] == 15.0 - @test status(models, 6) == 0.3 - @test status(models, :var1) == [15.0, 16.0] - @test status(models, :var2) == 0.3 - - @test status(models, :var4) == -Inf - @test status(models, :var3) == -Inf - @test status(models, :var5) == -Inf - @test status(models, :var6) == -Inf - - # TODO this behaviour is now changed, ramifications hard to gauge - # Testing setindex: - #@test models[:var6] = [5.5, 5.8] - #@test status(models, :var6) == [5.5, 5.8] - - # Testing a vector of ModelMapping: - @test status([models, models]) == [models.status, models.status] - # Testing a Dict of ModelMapping: - @test status(Dict(:m1 => models, :m2 => models)) == Dict(:m1 => models.status, :m2 => models.status) -end \ No newline at end of file diff --git a/test/test-corner-cases.jl b/test/test-corner-cases.jl deleted file mode 100644 index 334454a8b..000000000 --- a/test/test-corner-cases.jl +++ /dev/null @@ -1,619 +0,0 @@ - -# Specific configurations that trigger specific codepaths, or relate to, say, past bugs exposed in XPalm over time -# Usually have some subtle coupling quirk that requires careful handling in the dependency graph -# The outputs and meteo values are irrelevant, those are here as guards that are likely to break if a larger rework -# fails to take those corner-cases into account, and more quickly checked than XPalm - -############################################################################################################### -## Multi-Scale setup with a hard dependency calling another hard dependency -############################################################################################################### - -# relates to #77 and #99 - -PlantSimEngine.@process "Msg3Lvl_amont" verbose = false -PlantSimEngine.@process "Msg3Lvl_amont2" verbose = false -PlantSimEngine.@process "Msg3Lvl_echelle1" verbose = false -PlantSimEngine.@process "Msg3Lvl_echelle2" verbose = false -PlantSimEngine.@process "Msg3Lvl_echelle3" verbose = false -PlantSimEngine.@process "Msg3Lvl_aval" verbose = false -PlantSimEngine.@process "Msg3Lvl_aval2" verbose = false - -# Roots : amont and amont2 -# amont2 points to aval -# aval has a hard dependency, aval2 -# ech3 is a hard dependency of ech2, itself a hard dependency of ech1 -# all 3 use variables from amont, making amont a soft dependency of ech1 - -# aval makes use of variables from amont2, aval2, ech1 and ech3 - -################# - -struct Msg3LvlScaleAmontModel <: AbstractMsg3Lvl_AmontModel -end - -function PlantSimEngine.inputs_(::Msg3LvlScaleAmontModel) - (a=-Inf,) -end - -function PlantSimEngine.outputs_(::Msg3LvlScaleAmontModel) - (b=-Inf, c=-Inf) -end - -function PlantSimEngine.run!(::Msg3LvlScaleAmontModel, models, status, meteo, constants=nothing, extra_args=nothing) - status.b = status.a - status.c = 1.0 -end - -################# - -struct Msg3LvlScaleAmont2Model <: AbstractMsg3Lvl_Amont2Model -end - -function PlantSimEngine.inputs_(::Msg3LvlScaleAmont2Model) - (a2=-Inf,) -end - -function PlantSimEngine.outputs_(::Msg3LvlScaleAmont2Model) - (b2=-Inf,) -end - -function PlantSimEngine.run!(::Msg3LvlScaleAmont2Model, models, status, meteo, constants=nothing, extra_args=nothing) - status.b2 = status.a2 + 1.0 -end - -################# - -struct Msg3LvlScaleEchelle3Model <: AbstractMsg3Lvl_Echelle3Model -end - -function PlantSimEngine.inputs_(::Msg3LvlScaleEchelle3Model) - #(b = -Inf, - (c=-Inf,) -end - -function PlantSimEngine.outputs_(::Msg3LvlScaleEchelle3Model) - (e3=-Inf, f3=-Inf) -end - -function PlantSimEngine.run!(::Msg3LvlScaleEchelle3Model, models, status, meteo, constants=nothing, extra_args=nothing) - status.e3 = 1.0#status.c> - status.f3 = 1.0 -end - -################# - -struct Msg3LvlScaleEchelle2Model <: AbstractMsg3Lvl_Echelle2Model -end - - -function PlantSimEngine.inputs_(::Msg3LvlScaleEchelle2Model) - (c=-Inf, e3=-Inf, f3=-Inf) -end - -function PlantSimEngine.outputs_(::Msg3LvlScaleEchelle2Model) - (e2=-Inf, f2=-Inf) -end - -PlantSimEngine.dep(::Msg3LvlScaleEchelle2Model) = (Msg3Lvl_echelle3=AbstractMsg3Lvl_Echelle3Model => (:E3,),) -function PlantSimEngine.run!(::Msg3LvlScaleEchelle2Model, models, status, meteo, constants=nothing, extra_args=nothing) - status_E3 = extra_args.statuses[:E3][1] - run!(extra_args.models[:E3].Msg3Lvl_echelle3, models, status_E3, meteo, constants) - status.e2 = status.e3 - status.e3 = status.e3 * 2.0 - status.f2 = status.e3 * 2.0 + status.f3 + status.c -end - -################# - -struct Msg3LvlScaleEchelle1Model <: AbstractMsg3Lvl_Echelle1Model -end - -function PlantSimEngine.inputs_(::Msg3LvlScaleEchelle1Model) - (b=-Inf, e2=-Inf, f2=-Inf) -end - -function PlantSimEngine.outputs_(::Msg3LvlScaleEchelle1Model) - (e1=-Inf, f1=-Inf)#, e3 = -Inf) -end - -PlantSimEngine.dep(::Msg3LvlScaleEchelle1Model) = (Msg3Lvl_echelle2=AbstractMsg3Lvl_Echelle2Model => (:E2,),) -function PlantSimEngine.run!(::Msg3LvlScaleEchelle1Model, models, status, meteo, constants=nothing, extra_args=nothing) - - status_E2 = extra_args.statuses[:E2][1] - run!(extra_args.models[:E2].Msg3Lvl_echelle2, models, status_E2, meteo, constants, extra_args) - status.e1 = status.e2 - status.e2 = status.e2 * 2.0 - status.f1 = status.e2 * 2.0 + status.f2 + status.b - #status.e3 = status.e3 * 7.0 -end - -################# - -struct Msg3LvlScaleAval2Model <: AbstractMsg3Lvl_Aval2Model -end - -function PlantSimEngine.inputs_(::Msg3LvlScaleAval2Model) - (i2=-Inf,) -end - -function PlantSimEngine.outputs_(::Msg3LvlScaleAval2Model) - (g2=-Inf,) -end - -function PlantSimEngine.run!(::Msg3LvlScaleAval2Model, models, status, meteo, constants=nothing, extra_args=nothing) - status.g2 = status.i2 -end - -################# - -struct Msg3LvlScaleAvalModel <: AbstractMsg3Lvl_AvalModel -end - -function PlantSimEngine.inputs_(::Msg3LvlScaleAvalModel) - (e1=-Inf, f1=-Inf, b2=-Inf, g2=-Inf, e3=-Inf) -end - -function PlantSimEngine.outputs_(::Msg3LvlScaleAvalModel) - (g=-Inf,) -end - -PlantSimEngine.dep(::Msg3LvlScaleAvalModel) = (Msg3Lvl_aval2=AbstractMsg3Lvl_Aval2Model => (:E2,),) - -function PlantSimEngine.run!(::Msg3LvlScaleAvalModel, models, status, meteo, constants=nothing, extra_args=nothing) - - status_E2 = extra_args.statuses[:E2][1] - run!(extra_args.models[:E2].Msg3Lvl_aval2, models, status_E2, meteo, constants, extra_args) - status.g = status.f1 + status.b2 + status_E2.g2 - status.e3 = status.e3 + 1.0 -end - -##################################################################################### -# actual testset - -@testset "Multiscale nested hard dependencies" begin - - mapping3Lvl = PlantSimEngine.ModelMapping(:E1 => ( - Msg3LvlScaleAmontModel(), - PlantSimEngine.MultiScaleModel( - model=Msg3LvlScaleAvalModel(), - mapped_variables=[:e3 => :E3 => :e3, :b2 => :E2 => :b2, :g2 => :E2 => :g2], - ), - PlantSimEngine.MultiScaleModel( - model=Msg3LvlScaleEchelle1Model(), - mapped_variables=[:e2 => :E2 => :e2, :f2 => :E2 => :f2,], - ), Status(a=1.0,)# y = 1.0, z = 1.0) - ), - :E2 => ( - Msg3LvlScaleAmont2Model(), - Msg3LvlScaleAval2Model(), - PlantSimEngine.MultiScaleModel( - model=Msg3LvlScaleEchelle2Model(), - mapped_variables=[:c => :E1 => :c, :e3 => :E3 => :e3, :f3 => :E3 => :f3,], - ), - Status(a2=1.0, i2=1.0,) - ), - :E3 => ( - PlantSimEngine.MultiScaleModel( - model=Msg3LvlScaleEchelle3Model(), - mapped_variables=[:c => :E1 => :c,], - ), - ), - ) - - outs3Lvl = Dict( - :E1 => (:g, :e1, :f1), - :E2 => (:e2, :f2,), - :E3 => (:e3,) - ) - - meteo3Lvl = Weather([Atmosphere(T=20.0, Wind=1.0, Rh=0.65, Ri_PAR_f=200.0), - Atmosphere(T=18.0, Wind=1.0, Rh=0.65, Ri_PAR_f=100.0), - Atmosphere(T=19.0, Wind=1.0, Rh=0.65, Ri_PAR_f=200.0), - Atmosphere(T=30.0, Wind=0.5, Rh=0.6, Ri_PAR_f=100.0), - Atmosphere(T=20.0, Wind=1.0, Rh=0.6, Ri_PAR_f=200.0), - Atmosphere(T=25.0, Wind=1.0, Rh=0.6, Ri_PAR_f=200.0), - Atmosphere(T=10.0, Wind=0.5, Rh=0.6, Ri_PAR_f=200.0)]) - - mtg3Lvl = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :E1, 0, 0),) - Node(mtg3Lvl, MultiScaleTreeGraph.NodeMTG("/", :E2, 0, 1)) - Node(mtg3Lvl, MultiScaleTreeGraph.NodeMTG("/", :E3, 0, 2)) - - #sim3Lvl = @test_nowarn PlantSimEngine.run!(mtg3Lvl, mapping3Lvl, meteo3Lvl, tracked_outputs=outs3Lvl, executor=SequentialEx()) - nsteps = PlantSimEngine.get_nsteps(meteo3Lvl) - sim3Lvl = PlantSimEngine.GraphSimulation(mtg3Lvl, mapping3Lvl, nsteps=nsteps, check=false, outputs=outs3Lvl) - out = run!(sim3Lvl, meteo3Lvl) - - @test length(sim3Lvl.dependency_graph.roots) == 2 - - roots = [last(x) for x in collect(sim3Lvl.dependency_graph.roots)] - idx = findfirst(r -> !isempty(r.children) && !isempty(r.children[1].hard_dependency), roots) - @test !isnothing(idx) - model_ech1 = roots[idx].children[1] - @test !isempty(model_ech1.hard_dependency) - @test all(hd.parent == model_ech1 for hd in model_ech1.hard_dependency) - -end - -################################################################################################################################################################################# - -####################################################################################################################### -## Hard dep at another scale, soft dep on the nested model (both at same scale) -####################################################################################################################### - -PlantSimEngine.@process "hard_dep_same_scale_echelle1" verbose = false -PlantSimEngine.@process "hard_dep_same_scale_echelle1bis" verbose = false -PlantSimEngine.@process "hard_dep_same_scale_echelle3" verbose = false -PlantSimEngine.@process "hard_dep_same_scale_aval" verbose = false - -################# - -struct HardDepSameScaleEchelle3Model <: AbstractHard_Dep_Same_Scale_Echelle3Model -end - -function PlantSimEngine.inputs_(::HardDepSameScaleEchelle3Model) - #(b = -Inf, - (d=-Inf,) -end - -function PlantSimEngine.outputs_(::HardDepSameScaleEchelle3Model) - (e3=-Inf, f3=-Inf) -end - -function PlantSimEngine.run!(::HardDepSameScaleEchelle3Model, models, status, meteo, constants=nothing, extra_args=nothing) - status.e3 = 1.0#status.c - status.f3 = 1.0 -end - -################# - -struct HardDepSameScaleEchelle1Model <: AbstractHard_Dep_Same_Scale_Echelle1Model -end - -function PlantSimEngine.inputs_(::HardDepSameScaleEchelle1Model) - (a=-Inf, e2=-Inf)# e3 = -Inf, f3 = -Inf) -end - -function PlantSimEngine.outputs_(::HardDepSameScaleEchelle1Model) - (e1=-Inf, f1=-Inf) -end - -#PlantSimEngine.dep(::HardDepSameScaleEchelle1Model) = (hard_dep_same_scale_echelle3=AbstractHard_Dep_Same_Scale_Echelle3Model => (:E3,),) - -# exta_args = sim_object -function PlantSimEngine.run!(::HardDepSameScaleEchelle1Model, models, status, meteo, constants=nothing, sim_object=nothing) - #run!(sim_object.models[:E3].hard_dep_same_scale_echelle3, models, status, meteo, constants) - status.e1 = 1.0#status.e3 - #status.e3 = status.e3 * 2.0 - status.f1 = status.a #status.e3 * 2.0 + status.f3 + status.a - #status.c = 1.0 + status.e2 -end - -################# - -struct HardDepSameScaleEchelle1bisModel <: AbstractHard_Dep_Same_Scale_Echelle1BisModel -end - -function PlantSimEngine.inputs_(::HardDepSameScaleEchelle1bisModel) - (e3=-Inf,) -end - -function PlantSimEngine.outputs_(::HardDepSameScaleEchelle1bisModel) - (e2=-Inf, f2=-Inf) -end - -PlantSimEngine.dep(::HardDepSameScaleEchelle1bisModel) = (hard_dep_same_scale_echelle3=AbstractHard_Dep_Same_Scale_Echelle3Model => (:E3,),) - -# exta_args = sim_object -function PlantSimEngine.run!(::HardDepSameScaleEchelle1bisModel, models, status, meteo, constants=nothing, sim_object=nothing) - status_E3 = sim_object.statuses[:E3][1] - run!(sim_object.models[:E3].hard_dep_same_scale_echelle3, models, status_E3, meteo, constants) - status.e2 = status_E3.e3 - status_E3.e3 = status_E3.e3 * 2.0 - status.f2 = status_E3.e3 * 2.0 -end - -################# - -struct HardDepSameScaleAvalModel <: AbstractHard_Dep_Same_Scale_AvalModel -end - -function PlantSimEngine.inputs_(::HardDepSameScaleAvalModel) - (e3=-Inf,) # f1 or f2 ? -end - -function PlantSimEngine.outputs_(::HardDepSameScaleAvalModel) - (g=-Inf,) -end - -function PlantSimEngine.run!(::HardDepSameScaleAvalModel, models, status, meteo, constants=nothing, extra_args=nothing) - status.g = status.e3 - #status.h = status.e1 -end - -#################################################################### -# actual testset - -@testset "Soft dependency whose parent is a hard dependency of a parent at a different scale" begin - mapping = PlantSimEngine.ModelMapping( - :E1 => (HardDepSameScaleEchelle1Model(), - PlantSimEngine.MultiScaleModel( - model=HardDepSameScaleEchelle1bisModel(), - mapped_variables=[:e3 => :E3 => :e3], - ), - Status(a=1.0),), - :E3 => ( - HardDepSameScaleEchelle3Model(), - HardDepSameScaleAvalModel(), Status(d=1.0,), - ), - ) - - @test to_initialize(mapping) == Dict() - - outs = Dict( - :E1 => (:e1, :f1, :e2, :f2), - :E3 => (:e3,) - ) - - meteo = Weather([ - Atmosphere(T=25.0, Wind=1.0, Rh=0.6, Ri_PAR_f=200.0), - Atmosphere(T=10.0, Wind=0.5, Rh=0.6, Ri_PAR_f=200.0)]) - - mtg = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :E1, 0, 0),) - Node(mtg, MultiScaleTreeGraph.NodeMTG("/", :E3, 0, 1)) - - #sim = @test_nowarn PlantSimEngine.run!(mtg, mapping, meteo, tracked_outputs=outs, executor=SequentialEx()) - nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg, mapping, nsteps=nsteps, check=false, outputs=outs) - out = run!(sim, meteo) - - model_1 = last(collect(sim.dependency_graph.roots)[1]) - - # Downscale soft dependency aval should point to the root node 1bis, instead of the 'real parent' 3, which is an inner hard dependency to 1bis - # so 1 and aval both point to 1bis - @test length(model_1.children) == 2 -end - - -################################################################################################################################################################################# - -####################################################################################################################### -## 2 different scales that make use of the *same* model -####################################################################################################################### - -PlantSimEngine.@process "single_model_multiple_scales" verbose = false - -struct SingleModelScale1 <: AbstractSingle_Model_Multiple_ScalesModel -end -struct SingleModelScale2 <: AbstractSingle_Model_Multiple_ScalesModel -end -struct SingleModelScale2bis <: AbstractSingle_Model_Multiple_ScalesModel -end -struct SingleModelScale3 <: AbstractSingle_Model_Multiple_ScalesModel -end - -function PlantSimEngine.inputs_(::SingleModelScale1) - (in=-Inf, in1=-Inf) -end -function PlantSimEngine.outputs_(::SingleModelScale1) - (out=-Inf, out1=-Inf) -end - -function PlantSimEngine.inputs_(::SingleModelScale2) - (in=-Inf, in2=-Inf) -end -function PlantSimEngine.outputs_(::SingleModelScale2) - (out=-Inf, out2=-Inf) -end - -function PlantSimEngine.inputs_(::SingleModelScale2bis) - (in=-Inf, in2bis=-Inf) -end -function PlantSimEngine.outputs_(::SingleModelScale2bis) - (out=-Inf, out2bis=-Inf) -end - -function PlantSimEngine.inputs_(::SingleModelScale3) - (in=-Inf, in3=-Inf, out2=-Inf, out1=-Inf) -end -function PlantSimEngine.outputs_(::SingleModelScale3) - (out=-Inf, out3=-Inf) -end - -PlantSimEngine.dep(::SingleModelScale1) = (single_model_multiple_scales=AbstractSingle_Model_Multiple_ScalesModel => (:E2bis, :E2),) - -# extra_args = sim_object -function PlantSimEngine.run!(::SingleModelScale1, models, status, meteo, constants=nothing, sim_object=nothing) - status_E2 = sim_object.statuses[:E2][1] - status_E2b = sim_object.statuses[:E2bis][1] - run!(sim_object.models[:E2].single_model_multiple_scales, models, status_E2, meteo, constants) - run!(sim_object.models[:E2bis].single_model_multiple_scales, models, status_E2b, meteo, constants) - status.out = status_E2.out + status_E2b.out + status.in - status.out1 = status_E2.out2 + status_E2b.out2bis + status.out1 -end - -function PlantSimEngine.run!(::SingleModelScale2, models, status, meteo, constants=nothing, sim_object=nothing) - status.out = status.in + 1.0 - status.out2 = status.in2 -end - -function PlantSimEngine.run!(::SingleModelScale2bis, models, status, meteo, constants=nothing, sim_object=nothing) - status.out = status.in + 2.0 - status.out2bis = status.in2bis -end - -function PlantSimEngine.run!(::SingleModelScale3, models, status, meteo, constants=nothing, sim_object=nothing) - status.out = status.in + status.in3 + status.out2 - status.out3 = status.in3 + status.out1 -end - -############################# -## Actual testset - -@testset "Process/model reuse at different scales" begin - - mapping = PlantSimEngine.ModelMapping( - :E1 => ( - SingleModelScale1(), - Status(in=1.0, in1=1.0), - ), - :E2 => ( - SingleModelScale2(), - Status(in=1.0, in2=1.0), - ), - :E2bis => ( - SingleModelScale2bis(), - Status(in=1.0, in2bis=1.0), - ), - :E3 => ( - PlantSimEngine.MultiScaleModel( - model=SingleModelScale3(), - mapped_variables=[:out1 => :E1 => :out1, :out2 => :E2 => :out2,], - ), - Status(in=1.0, in3=1.0,), - ), - ) - - @test to_initialize(mapping) == Dict() - - outs = Dict( - :E1 => (:out, :out1), - :E2 => (:out, :out2), - :E2bis => (:out,), # comment this line out, and remove nodes relating to E2 and E2bis to expose the issue in #103 - :E3 => (:out3,) - ) - - meteo = Weather([ - Atmosphere(T=25.0, Wind=1.0, Rh=0.6, Ri_PAR_f=200.0), - Atmosphere(T=10.0, Wind=0.5, Rh=0.6, Ri_PAR_f=200.0)]) - - mtg = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :E1, 0, 0),) - Node(mtg, MultiScaleTreeGraph.NodeMTG("/", :E3, 0, 1)) - Node(mtg, MultiScaleTreeGraph.NodeMTG("/", :E2, 0, 2)) - Node(mtg, MultiScaleTreeGraph.NodeMTG("/", :E2bis, 0, 3)) - - #sim = @test_nowarn PlantSimEngine.run!(mtg, mapping, meteo, tracked_outputs=outs, executor = SequentialEx()) - nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg, mapping, nsteps=nsteps, check=false, outputs=outs) - out = run!(sim, meteo) - - roots = sim.dependency_graph.roots - @test length(sim.dependency_graph.roots) == 1 - - model_1 = last(collect(roots)[1]) - - @test length(model_1.children) == 1 - @test length(model_1.hard_dependency) == 2 - @test model_1.children[1].parent[1] == model_1 - @test model_1.hard_dependency[1].parent == model_1 - @test model_1.hard_dependency[2].parent == model_1 - -end - - - -########################## -## No outputs when simulating a mapping with one meteo timestep #105 -########################## - -@testset "Issue 105 : no outputs when simulating a mapping with one meteo timestep" begin - - using PlantSimEngine, PlantMeteo, DataFrames - using PlantSimEngine.Examples - mtg = import_mtg_example() - m = PlantSimEngine.ModelMapping( - :Leaf => ( - Process1Model(1.0), - Status(var1=10.0, var2=1.0,) - ) - ) - - @test to_initialize(m) == Dict() - - vars = Dict{Symbol,Any}(:Leaf => (:var1,)) - out = run!(mtg, m, Atmosphere(T=20.0, Wind=1.0, Rh=0.65), tracked_outputs=vars, executor=SequentialEx()) - df_dict = convert_outputs(out, DataFrame) - @test DataFrames.nrow(df_dict[:Leaf]) == 2 - @test DataFrames.ncol(df_dict[:Leaf]) == 3 -end - -########################## -## Multiscale : outputs not saved when dependency graph only has one depth level #111 -########################## - -# Probably very similar to #105 -@testset "Issue 111 : Multiscale : outputs not saved when dependency graph only has one depth level" begin - - using PlantSimEngine - using PlantSimEngine.Examples - using MultiScaleTreeGraph - - status2 = (var1=15.0, var2=0.3) - - meteo = Weather([ - Atmosphere(T=25.0, Wind=1.0, Rh=0.6, Ri_PAR_f=200.0), - Atmosphere(T=10.0, Wind=0.5, Rh=0.6, Ri_PAR_f=200.0)]) - - outs = Dict(:Default => (:var1,)) - mtg = MultiScaleTreeGraph.Node(MultiScaleTreeGraph.NodeMTG("/", :Default, 0, 0),) - - mapping = PlantSimEngine.ModelMapping( - :Default => ( - Process1Model(1.0), - Status(var1=15.0, var2=0.3,), - ), - ) - @test to_initialize(mapping) == Dict() - - sim = run!(mtg, mapping, meteo; tracked_outputs=outs) - using DataFrames - df_dict = convert_outputs(sim, DataFrame) - @test DataFrames.nrow(df_dict[:Default]) == PlantSimEngine.get_nsteps(meteo) -end - - -############################################ -### #86 : BoundsError with a single model and several Weather timesteps -############################################ - -using PlantSimEngine -PlantSimEngine.@process "toy" verbose = false - -""" -Inputs : a, b, c -Outputs : d, e -""" - -struct ToyToyModel{T} <: AbstractToyModel - internal_constant::T -end - -function PlantSimEngine.inputs_(::ToyToyModel) - (a=-Inf, b=-Inf, c=-Inf) -end - -# note : here, d is set with = further down, but e is set with +=, ie inf + thingy, is this a bug on my end ? -function PlantSimEngine.outputs_(::ToyToyModel) - (d=-Inf, e=-Inf) -end - -function PlantSimEngine.run!(m::ToyToyModel, models, status, meteo, constants=nothing, extra_args=nothing) - status.d = m.internal_constant * status.a - status.e += m.internal_constant -end - -@testset "Issue #86 : BoundsError with a single model and several Weather timesteps" begin - meteo = Weather([ - Atmosphere(T=20.0, Wind=1.0, Rh=0.65, Ri_PAR_f=200.0), - Atmosphere(T=18.0, Wind=1.0, Rh=0.65, Ri_PAR_f=100.0), - ]) - - model = PlantSimEngine.ModelMapping( - ToyToyModel(1), - status=(a=1, b=0, c=0), - #nsteps = length(meteo) - ) - @test to_initialize(model) == NamedTuple() - - sim = run!(model, meteo) - @test DataFrames.nrow(sim) == PlantSimEngine.get_nsteps(meteo) -end diff --git a/test/test-dimensions.jl b/test/test-dimensions.jl deleted file mode 100644 index b6b13735e..000000000 --- a/test/test-dimensions.jl +++ /dev/null @@ -1,48 +0,0 @@ -@testset "Chech status and weather correspond" begin - st = Status(Ra_SW_f=13.747, sky_fraction=1.0, d=0.03, aPPFD=1500) - tst1 = TimeStepTable([st]) - tst2 = TimeStepTable([st, st]) - tst3 = TimeStepTable([st, st, st]) - - atm = Atmosphere(T=25.0, Wind=5.0, Rh=0.3) - w1 = Weather([atm]) - w2 = Weather([atm, atm]) - w3 = Weather([atm, atm, atm]) - - # Status and Atmosphere are always authorized - @test PlantSimEngine.check_dimensions(st, atm) === nothing - - # TimeStepTable and Atmosphere are always authorized - # @test PlantSimEngine.check_dimensions(tst1, atm) === nothing - # @test PlantSimEngine.check_dimensions(tst2, atm) === nothing - - # Status and Weather are always authorized - @test PlantSimEngine.check_dimensions(st, w1) === nothing - @test PlantSimEngine.check_dimensions(st, w2) === nothing - - # TimeStepTable and Weather must be checked for equal length - # @test PlantSimEngine.check_dimensions(tst1, w1) === nothing - # @test PlantSimEngine.check_dimensions(tst2, w2) === nothing - - # This still works because one time step is recycled: - # @test PlantSimEngine.check_dimensions(tst1, w2) === nothing - - # @test_throws DimensionMismatch PlantSimEngine.check_dimensions(tst2, w1) - # @test_throws DimensionMismatch PlantSimEngine.check_dimensions(tst3, w2) - - # ModelMapping and Weather must be checked for equal length - m1 = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - status=(var1=1.0, var2=2.0) - ) - @test PlantSimEngine.check_dimensions(m1, w1) === nothing - - m2 = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - status=(var1=[1.0, 2.0], var2=2.0) - ) - @test PlantSimEngine.check_dimensions(m2, w2) === nothing - @test_throws DimensionMismatch PlantSimEngine.check_dimensions(m2, w1) -end \ No newline at end of file diff --git a/test/test-fitting.jl b/test/test-fitting.jl index 140e43b51..19d60725f 100644 --- a/test/test-fitting.jl +++ b/test/test-fitting.jl @@ -1,14 +1,32 @@ +using Dates + # Tests: # Defining a list of models without status: @testset "Fitting Beer" begin k = 0.6 - meteo = Atmosphere(T=20.0, Wind=1.0, P=101.3, Rh=0.65, Ri_PAR_f=300.0) - m = PlantSimEngine.ModelMapping(Beer(k), status=(LAI=2.0,)) - outs = run!(m, meteo) - - df = DataFrame(aPPFD=outs[:aPPFD][1], LAI=m.status.LAI[1], Ri_PAR_f=meteo.Ri_PAR_f[1]) + meteo = Atmosphere( + T=20.0, + Wind=1.0, + P=101.3, + Rh=0.65, + Ri_PAR_f=300.0, + duration=Dates.Hour(1), + ) + scene = Scene( + Object(:leaf; scale=:Leaf, status=Status(LAI=2.0)); + applications=( + ModelSpec(Beer(k)) |> AppliesTo(One(scale=:Leaf)), + ), + environment=meteo, + ) + run!(scene) + leaf = only(scene_objects(scene; scale=:Leaf)) + df = DataFrame( + aPPFD=[leaf.status.aPPFD], + LAI=[leaf.status.LAI], + Ri_PAR_f=[meteo.Ri_PAR_f[1]], + ) k_fit = fit(PlantSimEngine.Examples.Beer, df).k @test k_fit == k -end; - +end diff --git a/test/test-mapping.jl b/test/test-mapping.jl deleted file mode 100755 index 9c86d6a6e..000000000 --- a/test/test-mapping.jl +++ /dev/null @@ -1,384 +0,0 @@ -mapping = PlantSimEngine.ModelMapping( - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - # inputs - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - # outputs - :carbon_allocation => [:Leaf, :Internode] - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - ), - :Internode => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - Status(TT=10.0) - ), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], - # Notice we provide :Soil, not [:Soil], so a single value is expected here - ), - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - Status(aPPFD=1300.0, TT=10.0), - ), - :Soil => ( - ToySoilWaterModel(), - ), -) - -dep_graph = dep(mapping) - -# The C allocation depends on the C demand at the leaf and internode levels, -# the maintenance respiration at the plant level, and the maintenance respiration at the plant level, -# which depends on the maintenance respiration at the leaf and internode levels. - -# Expected root dependency nodes: -root_models = Dict( - (:Soil => :soil_water) => mapping[:Soil][1], # The only model from the soil is completely independent - (:Internode => :carbon_demand) => mapping[:Internode][1], # The c allocation models dependent on TT, that is given as input: - (:Leaf => :carbon_demand) => mapping[:Leaf][2], # Same for the leaf - (:Internode => :maintenance_respiration) => mapping[:Internode][2], # The maintenance respiration model for the internode is independant - (:Leaf => :maintenance_respiration) => mapping[:Leaf][3], # The maintenance respiration model for the leaf is independant -) - -for (proc, node) in dep_graph.roots # proc = (:Soil => :soil_water) ; node = dep_graph.roots[proc] - @test root_models[proc] == node.value -end - -@testset "ModelMapping checks and normalization" begin - mapping_struct = PlantSimEngine.ModelMapping(Dict(mapping)) - @test mapping_struct isa PlantSimEngine.ModelMapping - @test Set(keys(mapping_struct)) == Set(keys(mapping)) - @test hasmethod(PlantSimEngine.dep, Tuple{PlantSimEngine.ModelMapping}) - @test hasmethod(PlantSimEngine.hard_dependencies, Tuple{PlantSimEngine.ModelMapping}) - @test hasmethod(PlantSimEngine.inputs, Tuple{PlantSimEngine.ModelMapping}) - @test hasmethod(PlantSimEngine.outputs, Tuple{PlantSimEngine.ModelMapping}) - @test hasmethod(PlantSimEngine.variables, Tuple{PlantSimEngine.ModelMapping}) - @test hasmethod(PlantSimEngine.to_initialize, Tuple{PlantSimEngine.ModelMapping}) - @test hasmethod(PlantSimEngine.reverse_mapping, Tuple{PlantSimEngine.ModelMapping}) - - mapping_from_pairs = PlantSimEngine.ModelMapping( - :Plant => mapping[:Plant], - :Internode => mapping[:Internode], - :Leaf => mapping[:Leaf], - :Soil => mapping[:Soil], - ) - @test Set(keys(mapping_from_pairs)) == Set(keys(mapping)) - - mapping_with_specs = PlantSimEngine.ModelMapping( - :Scene => (ModelSpec(ToyDegreeDaysCumulModel()) |> TimeStep(ClockSpec(24.0, 1.0)),), - :Soil => (ModelSpec(ToySoilWaterModel()) |> TimeStep(ClockSpec(24.0, 1.0)),), - :Leaf => ( - ModelSpec(ToyAssimModel()) |> - PlantSimEngine.MultiScaleModel([:soil_water_content => (:Soil => :soil_water_content)]) |> - TimeStep(1.0), - ), - ) - @test mapping_with_specs isa PlantSimEngine.ModelMapping - @test any(item -> item isa ModelSpec, mapping_with_specs[:Soil]) - @test mapping_with_specs.info.validated - @test mapping_with_specs.info.is_valid - @test mapping_with_specs.info.is_multirate - @test Set(mapping_with_specs.info.scales) == Set([:Scene, :Soil, :Leaf]) - @test mapping_with_specs.info.models_per_scale[:Leaf] == 1 - @test length(mapping_with_specs.info.processes_per_scale[:Leaf]) == 1 - @test haskey(mapping_with_specs.info.model_specs, :Leaf) - - io = IOBuffer() - show(io, MIME("text/plain"), mapping_with_specs) - summary_txt = String(take!(io)) - @test occursin("ModelMapping", summary_txt) - @test occursin("multirate: true", summary_txt) - @test occursin("scales (3)", summary_txt) - - dep_from_dict = dep(mapping) - dep_from_struct = dep(mapping_struct) - @test Set(keys(dep_from_dict.roots)) == Set(keys(dep_from_struct.roots)) - @test Set(keys(first(PlantSimEngine.hard_dependencies(mapping_struct)).roots)) == Set(keys(first(PlantSimEngine.hard_dependencies(Dict(mapping_struct))).roots)) - @test inputs(mapping_struct) == inputs(Dict(mapping_struct)) - @test outputs(mapping_struct) == outputs(Dict(mapping_struct)) - @test variables(mapping_struct) == variables(Dict(mapping_struct)) - - ModelMapping_scale = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - status=(var1=1.0, var2=2.0) - ) - merged_mapping = PlantSimEngine.ModelMapping(Dict(:Default => ModelMapping_scale)) - @test length(PlantSimEngine.get_models(merged_mapping[:Default])) == 2 - @test !isnothing(PlantSimEngine.get_status(merged_mapping[:Default])) - - single_scale_from_models = PlantSimEngine.ModelMapping( - Process1Model(1.0), - Process2Model(); - scale=:Default, - status=(var1=1.0, var2=2.0), - ) - @test Set(keys(single_scale_from_models)) == Set([:Default]) - @test length(PlantSimEngine.get_models(single_scale_from_models[:Default])) == 2 - @test PlantSimEngine.get_status(single_scale_from_models[:Default]).var1 == 1.0 - - single_scale_from_namedtuple = PlantSimEngine.ModelMapping( - (process1=Process1Model(1.0), process2=Process2Model()); - status=(var1=1.0, var2=2.0), - ) - @test Set(keys(single_scale_from_namedtuple)) == Set([:Default]) - @test length(PlantSimEngine.get_models(single_scale_from_namedtuple[:Default])) == 2 - - single_scale_from_kwargs = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - status=(var1=1.0, var2=2.0), - ) - @test Set(keys(single_scale_from_kwargs)) == Set([:Default]) - @test length(PlantSimEngine.get_models(single_scale_from_kwargs[:Default])) == 2 - - @test_throws "Cannot mix scale-level pairs" PlantSimEngine.ModelMapping( - :Leaf => (Process1Model(1.0),), - process2=Process2Model(), - ) - - missing_scale_mapping = Dict( - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content)], - ), - ), - ) - @test_throws "missing scale `Soil`" PlantSimEngine.ModelMapping(missing_scale_mapping) - - missing_source_variable_mapping = Dict( - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content)], - ), - ), - :Soil => ( - Process1Model(1.0), - ), - ) - @test_throws "not available at scale `Soil`" PlantSimEngine.ModelMapping(missing_source_variable_mapping) - - no_model_mapping = Dict( - :Soil => (Status(soil_water_content=0.2),), - ) - @test_throws "defines no model" PlantSimEngine.ModelMapping(no_model_mapping) - - duplicate_process_mapping = Dict( - :Leaf => ( - Process1Model(1.0), - Process1Model(2.0), - ), - ) - @test_throws "duplicate process(es)" PlantSimEngine.ModelMapping(duplicate_process_mapping) - - meteo = Atmosphere(T=20.0, Wind=1.0, Rh=0.65) - models_single_scale = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=15.0, var2=0.3) - ) - @test !models_single_scale.info.is_multirate - @test models_single_scale.info.scales == [:Default] - @test models_single_scale.info.models_per_scale[:Default] == 3 - baseline_outputs = run!(models_single_scale, meteo) - - outputs_from_models_args = run!( - PlantSimEngine.ModelMapping( - Process1Model(1.0), - Process2Model(), - Process3Model(); - status=(var1=15.0, var2=0.3), - ), - meteo - ) - @test outputs_from_models_args == baseline_outputs - - outputs_from_named_tuple = run!( - PlantSimEngine.ModelMapping( - (process1=Process1Model(1.0), process2=Process2Model(), process3=Process3Model()); - status=(var1=15.0, var2=0.3), - ), - meteo - ) - @test outputs_from_named_tuple == baseline_outputs - - outputs_from_kwargs = run!( - PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=15.0, var2=0.3), - ), - meteo - ) - @test outputs_from_kwargs == baseline_outputs - - @test_throws "Use `run!(mtg, mapping, ...)` for multiscale mappings." run!(mapping, meteo) -end - - - -########################### -### Single and multi-scale ModelMapping comparison -### and Mapping with custom models vs mapping with generated models for user-provided vector -########################### - -# Currently untested in 'real' multi-scale modes, or with complex configs (hard dependencies). -# Need to place the simple timestep models in PlantSimEngine, and probably provide more complex ones at some point - -# And then need to insert it at the graph sim generation level, and modify tests to consistently do single <-> multiple scale conversions -# And then implement tests with proper output filtering - -@testset "check_statuses_contain_no_remaining_vectors behaviour" begin - meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18) - mapping_with_vector = PlantSimEngine.ModelMapping( - :Scale => - (ToyAssimGrowthModel(0.0, 0.0, 0.0), - ToyCAllocationModel(), - Status(TT_cu=Vector(cumsum(meteo_day.TT))), - ), - ) - - mtg = import_mtg_example() - @test !last(PlantSimEngine.check_statuses_contain_no_remaining_vectors(mapping_with_vector)) - @test_throws "call the function generate_models_from_status_vectors" PlantSimEngine.GraphSimulation(mtg, mapping_with_vector) - - mapping_with_empty_status = PlantSimEngine.ModelMapping( - :Scale => - (ToyAssimGrowthModel(0.0, 0.0, 0.0), - ToyCAllocationModel(), - Status(), - ), - ) - - @test last(PlantSimEngine.check_statuses_contain_no_remaining_vectors(mapping_with_empty_status)) -end - -@testset "Vector in status in a multiscale context" begin - meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18) - TT_v = Vector(meteo_day.TT) - TT_cu_vec = Vector(cumsum(meteo_day.TT)) - nsteps = length(meteo_day.TT) - - mapping_with_vector = PlantSimEngine.ModelMapping(:Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - # inputs - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - # outputs - :carbon_allocation => [:Leaf, :Internode] - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - ), - :Internode => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - Status(TT=TT_v, carbon_biomass=1.0) - ), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], - # Notice we provide :Soil, not [:Soil], so a single value is expected here - ), - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - Status(aPPFD=1300.0, carbon_biomass=2.0, TT=10.0), # TODO try calling the generated TT output through a variable mapping - ), - :Soil => ( - ToySoilWaterModel(), - ), - ) - - out_multiscale = Dict(:Plant => (:Rm_organs,),) - mtg = import_mtg_example() - - mapping_without_vectors = PlantSimEngine.replace_mapping_status_vectors_with_generated_models(mapping_with_vector, :Soil, nsteps) - - @test to_initialize(mapping_without_vectors) == Dict() - - graph_sim_multiscale = @test_nowarn PlantSimEngine.GraphSimulation(mtg, mapping_without_vectors, nsteps=nsteps, check=true, outputs=out_multiscale) - - sim_multiscale = run!(graph_sim_multiscale, - meteo_day, - PlantMeteo.Constants(), - nothing; - check=true, - executor=SequentialEx() - ) - - #replace a value with a constant vector and ensure no changes happen in the simulation - carbon_biomass_vec = Vector{Float64}(undef, nsteps) - for i in nsteps - carbon_biomass_vec[i] = 2.0 - end - mapping_with_two_vectors = PlantSimEngine.ModelMapping(:Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - # inputs - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - # outputs - :carbon_allocation => [:Leaf, :Internode] - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - ), - :Internode => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - Status(TT=TT_v, carbon_biomass=1.0) - ), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], - ), - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - Status(aPPFD=1300.0, carbon_biomass=carbon_biomass_vec, TT=10.0), # Replaced with vector here - ), - :Soil => ( - ToySoilWaterModel(), - ), - ) - - mtg = import_mtg_example() - mapping_without_vectors_2 = PlantSimEngine.replace_mapping_status_vectors_with_generated_models(mapping_with_two_vectors, :Soil, nsteps) - graph_sim_multiscale_2 = @test_nowarn PlantSimEngine.GraphSimulation(mtg, mapping_without_vectors_2, nsteps=nsteps, check=true, outputs=out_multiscale) - - @test to_initialize(mapping_without_vectors_2) == Dict() - - sim_multiscale_2 = run!(graph_sim_multiscale_2, - meteo_day, - PlantMeteo.Constants(), - nothing; - check=true, - executor=SequentialEx() - ) - - @test compare_outputs_graphsim(graph_sim_multiscale, graph_sim_multiscale_2) -end diff --git a/test/test-meteo-traits.jl b/test/test-meteo-traits.jl index 33faac85d..e5304bc8a 100644 --- a/test/test-meteo-traits.jl +++ b/test/test-meteo-traits.jl @@ -26,9 +26,10 @@ end (T=20.0, CO2=410.0, duration=Dates.Hour(1)) ) === nothing - bound_spec = - ModelSpec(MeteoTraitConsumerModel()) |> - PlantSimEngine.MeteoBindings(; CO2=(source=:Ca, reducer=MeanReducer())) + bound_spec = ModelSpec( + MeteoTraitConsumerModel(); + meteo_bindings=(CO2=(source=:Ca, reducer=MeanReducer()),), + ) bound_specs = Dict(:Leaf => Dict(:meteo_trait_consumer => bound_spec)) @test_throws "Ca" PlantSimEngine.validate_meteo_inputs( @@ -40,21 +41,12 @@ end (T=20.0, Ca=410.0, duration=Dates.Hour(1)) ) === nothing - mapping = PlantSimEngine.ModelMapping( - MeteoTraitConsumerModel(), - status=(meteo_seen=0.0,), - ) - @test_throws "CO2" PlantSimEngine.validate_meteo_inputs( - mapping, - Atmosphere(T=20.0, Rh=0.65, Wind=1.0, duration=Dates.Hour(1)) + scene = Scene( + Object(:leaf; scale=:Leaf, status=Status(meteo_seen=0.0)); + applications=( + ModelSpec(MeteoTraitConsumerModel()) |> AppliesTo(One(scale=:Leaf)), + ), + environment=(T=20.0, CO2=410.0, duration=Dates.Hour(1)), ) - @test PlantSimEngine.validate_meteo_inputs( - mapping, - (T=20.0, CO2=410.0, duration=Dates.Hour(1)) - ) === nothing - - @test PlantSimEngine.validate_meteo_inputs( - Dict(:Leaf => (MeteoTraitConsumerModel(),)), - (T=20.0, CO2=410.0, duration=Dates.Hour(1)) - ) === nothing + @test validate_meteo_inputs(scene) === nothing end diff --git a/test/test-mtg-dynamic.jl b/test/test-mtg-dynamic.jl deleted file mode 100644 index cf24cb6e9..000000000 --- a/test/test-mtg-dynamic.jl +++ /dev/null @@ -1,203 +0,0 @@ -# using PlantSimEngine, DataFrames, MultiScaleTreeGraph -# using PlantSimEngine.Examples; -mtg = import_mtg_example(); - -# Example meteo: -meteo = Weather( - [ - Atmosphere(T=20.0, Wind=1.0, Rh=0.65), - Atmosphere(T=25.0, Wind=0.5, Rh=0.8) -] -) - -mapping = PlantSimEngine.ModelMapping( - :Scene => ToyDegreeDaysCumulModel(), - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[ - :TT_cu => (:Scene => :TT_cu), - ], - ), - Beer(0.6), - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - :carbon_allocation => [:Leaf, :Internode] - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - ), - :Internode => ( - PlantSimEngine.MultiScaleModel( - model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - mapped_variables=[:TT => (:Scene => :TT),], - ), - PlantSimEngine.MultiScaleModel( - model=ToyInternodeEmergence(TT_emergence=20.0), - mapped_variables=[:TT_cu => (:Scene => :TT_cu)], - ), - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - Status(carbon_biomass=1.0) - ), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content), :aPPFD => (:Plant => :aPPFD)], - ), - PlantSimEngine.MultiScaleModel( - model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - mapped_variables=[:TT => (:Scene => :TT),], - ), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - Status(carbon_biomass=1.0) - ), - :Soil => ( - ToySoilWaterModel(), - ), -) - -out_vars = Dict( - :Leaf => (:carbon_assimilation, :carbon_demand, :soil_water_content, :carbon_allocation), - :Internode => (:carbon_allocation, :TT_cu_emergence), - :Plant => (:carbon_allocation,), - :Soil => (:soil_water_content,), -) - -nsteps = PlantSimEngine.get_nsteps(meteo) -sim = PlantSimEngine.GraphSimulation(mtg, mapping, nsteps=nsteps, check=true, outputs=out_vars) -out = run!(sim,meteo) -#out = run!(mtg, mapping, meteo, tracked_outputs=out_vars, executor=SequentialEx()) - -@testset "MTG with dynamic growth" begin - st = sim.statuses - @test length(mtg) == 9 - @test length(st[:Scene]) == length(st[:Soil]) == length(st[:Plant]) == 1 - @test length(st[:Internode]) == length(st[:Leaf]) == 3 - @test node_id.(getproperty.(st[:Leaf], :node)) == sort(node_id.(getproperty.(st[:Leaf], :node))) - leaf_assimilation_refs = [PlantSimEngine.refvalue(leaf_status, :carbon_assimilation) for leaf_status in st[:Leaf]] - @test all( - ref_pair -> first(ref_pair) === last(ref_pair), - zip(parent(sim.status_templates[:Plant][:carbon_assimilation]), leaf_assimilation_refs), - ) - @test st[:Internode][1].TT_cu_emergence == 0.0 - @test st[:Internode][end].TT_cu_emergence == 25.0 - - out_df_dict = convert_outputs(out, DataFrame) - @test collect(keys(out_df_dict)) |> sort == [:Internode, :Leaf, :Plant, :Soil] - @test out_df_dict[:Internode][:, :TT_cu_emergence] == [0.0, 0.0, 0.0, 0.0, 25.0] - @test out_df_dict[:Leaf][:, :carbon_demand] == [0.5, 0.5, 0.75, 0.75, -Inf] -end - -@testset "MTG with mixed daily/hourly clocks (2 leaves)" begin - mtg2 = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant2 = Node(mtg2, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - Node(mtg2, MultiScaleTreeGraph.NodeMTG("+", :Soil, 1, 1)) - internode2 = Node(plant2, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - - daily = ClockSpec(24.0, 1.0) - hourly = 1.0 - - mapping2 = Dict( - :Scene => ( - ModelSpec(ToyDegreeDaysCumulModel()) |> TimeStep(daily), - ), - :Plant => ( - ModelSpec(ToyLAIModel()) |> - PlantSimEngine.MultiScaleModel([:TT_cu => (:Scene => :TT_cu)]) |> - TimeStep(daily), - ModelSpec(Beer(0.6)) |> TimeStep(hourly), - ModelSpec(ToyCAllocationModel()) |> - PlantSimEngine.MultiScaleModel([ - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - :carbon_allocation => [:Leaf, :Internode], - ]) |> - PlantSimEngine.InputBindings(; carbon_assimilation=(process=process(ToyAssimModel()), var=:carbon_assimilation, scale=:Leaf, policy=Integrate())) |> - TimeStep(daily), - ModelSpec(ToyPlantRmModel()) |> - PlantSimEngine.MultiScaleModel([:Rm_organs => [:Leaf => :Rm, :Internode => :Rm]]) |> - TimeStep(daily), - ), - :Internode => ( - ModelSpec(ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0)) |> - PlantSimEngine.MultiScaleModel([:TT => (:Scene => :TT)]) |> - TimeStep(daily), - # Keep emergence model in the stack (as in the dynamic test), but prevent growth in this scenario. - ModelSpec(ToyInternodeEmergence(TT_emergence=1.0e6)) |> - PlantSimEngine.MultiScaleModel([:TT_cu => (:Scene => :TT_cu)]) |> - TimeStep(daily), - ModelSpec(ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004)) |> TimeStep(daily), - Status(carbon_biomass=1.0), - ), - :Leaf => ( - ModelSpec(ToyAssimModel()) |> - PlantSimEngine.MultiScaleModel([:soil_water_content => (:Soil => :soil_water_content), :aPPFD => (:Plant => :aPPFD)]) |> - TimeStep(hourly), - ModelSpec(ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0)) |> - PlantSimEngine.MultiScaleModel([:TT => (:Scene => :TT)]) |> - TimeStep(daily), - ModelSpec(ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025)) |> TimeStep(daily), - Status(carbon_biomass=1.0), - ), - :Soil => ( - ModelSpec(ToySoilWaterModel()) |> TimeStep(daily), - ), - ) - - out_vars2 = Dict( - :Leaf => (:carbon_assimilation, :aPPFD, :carbon_demand), - :Plant => (:LAI, :carbon_offer, :Rm), - :Scene => (:TT, :TT_cu), - :Soil => (:soil_water_content,), - :Internode => (:carbon_demand, :TT_cu_emergence), - ) - - nsteps2 = 48 - meteo2 = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65, Ri_PAR_f=300.0)], nsteps2)) - sim2 = PlantSimEngine.GraphSimulation(mtg2, mapping2, nsteps=nsteps2, check=true, outputs=out_vars2) - out2 = run!(sim2, meteo2, executor=SequentialEx()) - - st2 = status(sim2) - @test length(st2[:Scene]) == length(st2[:Soil]) == length(st2[:Plant]) == length(st2[:Internode]) == 1 - @test length(st2[:Leaf]) == 2 - - scope = ScopeId(:global, 1) - last_run = sim2.temporal_state.last_run - p_dd = process(ToyDegreeDaysCumulModel()) - p_lai = process(ToyLAIModel()) - p_beer = process(Beer(0.6)) - p_assim = process(ToyAssimModel()) - p_alloc = process(ToyCAllocationModel()) - p_cdemand = process(ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0)) - p_soil = process(ToySoilWaterModel()) - - @test last_run[ModelKey(scope, :Plant, p_beer)] == 48.0 - @test last_run[ModelKey(scope, :Leaf, p_assim)] == 48.0 - - @test last_run[ModelKey(scope, :Scene, p_dd)] == 25.0 - @test last_run[ModelKey(scope, :Plant, p_lai)] == 25.0 - @test last_run[ModelKey(scope, :Plant, p_alloc)] == 25.0 - @test last_run[ModelKey(scope, :Leaf, p_cdemand)] == 25.0 - @test last_run[ModelKey(scope, :Soil, p_soil)] == 25.0 - - @test st2[:Scene][1].TT_cu == 20.0 - last_daily_run = last_run[ModelKey(scope, :Plant, p_alloc)] - window_start = last_daily_run - daily.dt + 1.0 - out2_df = convert_outputs(out2, DataFrame) - integrated_assim_window = sum( - row.carbon_assimilation for row in eachrow(out2_df[:Leaf]) - if row.timestep >= window_start - 1e-8 && row.timestep <= last_daily_run + 1e-8 - ) - @test integrated_assim_window > 0.0 - @test isfinite(st2[:Plant][1].carbon_offer) - @test st2[:Plant][1].carbon_offer > -st2[:Plant][1].Rm - @test !isempty(out2[:Leaf]) -end diff --git a/test/test-mtg-multiscale-cyclic-dep.jl b/test/test-mtg-multiscale-cyclic-dep.jl deleted file mode 100644 index c12f6912c..000000000 --- a/test/test-mtg-multiscale-cyclic-dep.jl +++ /dev/null @@ -1,252 +0,0 @@ - -mtg = import_mtg_example() -# Example meteo: -meteo = Weather( - [ - Atmosphere(T=20.0, Wind=1.0, Rh=0.65, Ri_PAR_f=300.0), - Atmosphere(T=25.0, Wind=0.5, Rh=0.8, Ri_PAR_f=500.0) -] -) - -out_vars = Dict( - :Plant => (:carbon_allocation,), - :Leaf => (:carbon_demand, :carbon_allocation, :carbon_biomass), - :Internode => (:carbon_demand, :carbon_allocation, :carbon_biomass), -) - -@testset "Cyclic dependency -> error" begin - mapping_cyclic = PlantSimEngine.ModelMapping( - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - :carbon_demand => [:Leaf, :Internode], - :carbon_allocation => [:Leaf, :Internode] - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - Status(total_surface=0.001, aPPFD=1300.0, soil_water_content=0.6), - ), - :Internode => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - Status(TT=10.0, carbon_biomass=1.0), - ), - :Leaf => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - ToyCBiomassModel(1.2), - Status(TT=10.0), - ) - ) - # In this mapping, we have a cyclic dependency with the carbon allocation and the carbon biomass. The carbon biomass depends on the carbon allocation, which itself depends on the - # plant Rm, which depends on the Rm organs, which depends on the carbon biomass. This is a cyclic dependency that we need to break somewhere. - - @test_throws "Cyclic dependency detected in the graph. Cycle:" dep(mapping_cyclic) - - soft_dep_graphs_roots, hard_dep_dict = PlantSimEngine.hard_dependencies(mapping_cyclic) - - mapped_vars = PlantSimEngine.mapped_variables(mapping_cyclic, soft_dep_graphs_roots, verbose=false) - reverse_mapping_cyclic = PlantSimEngine.reverse_mapping(mapped_vars, all=false) - - dep_graph = PlantSimEngine.soft_dependencies_multiscale(soft_dep_graphs_roots, reverse_mapping_cyclic, hard_dep_dict) - iscyclic, cycle_vec = PlantSimEngine.is_graph_cyclic(dep_graph; warn=false) - - @test iscyclic - @test cycle_vec == [ToyPlantRmModel() => :Plant, ToyMaintenanceRespirationModel{Float64}(2.1, 0.06, 25.0, 1.0, 0.025) => :Leaf, ToyCBiomassModel{Float64}(1.2) => :Leaf, ToyCAllocationModel() => :Plant, ToyPlantRmModel() => :Plant] -end - - -@testset "Cyclic dependency -> fixed with `PreviousTimeStep`" begin - mapping_nocyclic = PlantSimEngine.ModelMapping( - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - :carbon_demand => [:Leaf, :Internode], - :carbon_allocation => [:Leaf, :Internode] - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - Status(total_surface=0.001, aPPFD=1300.0, soil_water_content=0.6, carbon_assimilation=5.0), - ), - :Internode => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - PlantSimEngine.MultiScaleModel( - model=ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - mapped_variables=[PreviousTimeStep(:carbon_biomass),], #! this is where we break the cyclic dependency (first break) - ), - Status(TT=10.0, carbon_biomass=1.0), - ), - :Leaf => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - PlantSimEngine.MultiScaleModel( - model=ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - mapped_variables=[PreviousTimeStep(:carbon_biomass),], #! this is where we break the cyclic dependency (second break) - ), - ToyCBiomassModel(1.2), - Status(TT=10.0), - ) - ) - # In this mapping, we have a cyclic dependency with the carbon allocation and the carbon biomass like the test above, but we - # break the cyclic dependency by using the value of the biomass from the previous time-step instead of taking the current computation. - - @test_nowarn dep(mapping_nocyclic) - - soft_dep_graphs_roots, hard_dep_dict = PlantSimEngine.hard_dependencies(mapping_nocyclic) - # soft_dep_graphs_roots.roots[:Leaf].inputs - mapped_vars = PlantSimEngine.mapped_variables(mapping_nocyclic, soft_dep_graphs_roots, verbose=false) - reverse_mapping_nocyclic = PlantSimEngine.reverse_mapping(mapped_vars, all=false) - - dep_graph = PlantSimEngine.soft_dependencies_multiscale(soft_dep_graphs_roots, reverse_mapping_nocyclic, hard_dep_dict) - iscyclic, cycle_vec = PlantSimEngine.is_graph_cyclic(dep_graph; warn=false) - - @test !iscyclic - @test length(cycle_vec) == 7 - @test to_initialize(mapping_nocyclic) == Dict() - - - #out = @test_nowarn run!(mtg, mapping_nocyclic, meteo, tracked_outputs=out_vars, executor=SequentialEx()) - nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg, mapping_nocyclic, nsteps=nsteps, check=true, outputs=out_vars) - out = @test_nowarn run!(sim,meteo) - st = status(sim) - - st[:Leaf][1].carbon_biomass = 2.0 - @test st[:Leaf][2].carbon_biomass != 2.0 -end - -@testset "Mutiscale simulation -> cyclic dependency" begin - mapping = PlantSimEngine.ModelMapping( - :Scene => ( - ToyDegreeDaysCumulModel(), - PlantSimEngine.MultiScaleModel( - model=ToyLAIfromLeafAreaModel(1.0), - mapped_variables=[ - :plant_surfaces => [:Plant => :surface], - ], - ), - Beer(0.6), - ), - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyPlantLeafSurfaceModel(), - mapped_variables=[PreviousTimeStep(:leaf_surfaces) => [:Leaf => :surface],], - #! We use PreviousTimeStep to break the cyclic dependency between the LAI and the leaf surface - # that is computed as one of the latest sub-models. Now the LAI used for light interception - # will be the one from the previous time-step, and at the end of the time-step we will update - # the leaf surface. - ), - PlantSimEngine.MultiScaleModel( - model=ToyLightPartitioningModel(), - mapped_variables=[ - :aPPFD_larger_scale => (:Scene => :aPPFD), - :total_surface => (:Scene => :total_surface) - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[ - :soil_water_content => (:Soil => :soil_water_content), - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - :carbon_demand => [:Leaf, :Internode], - :carbon_allocation => [:Leaf, :Internode] - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - ), - :Internode => ( - PlantSimEngine.MultiScaleModel( - model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - mapped_variables=[:TT => (:Scene => :TT),], - ), - PlantSimEngine.MultiScaleModel( - model=ToyInternodeEmergence(TT_emergence=20.0), - mapped_variables=[:TT_cu => (:Scene => :TT_cu)], - ), - PlantSimEngine.MultiScaleModel( - model=ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - mapped_variables=[PreviousTimeStep(:carbon_biomass),], #! this is where we break the cyclic dependency (first break) - ), - ToyCBiomassModel(1.1), - Status(carbon_biomass=0.0) - ), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - mapped_variables=[:TT => (:Scene => :TT),], - ), - PlantSimEngine.MultiScaleModel( - model=ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - mapped_variables=[PreviousTimeStep(:carbon_biomass),], #! this is where we break the cyclic dependency (first break) - ), - ToyCBiomassModel(1.2), - ToyLeafSurfaceModel(0.1), - Status(carbon_biomass=0.0, surface=0.001,) - ), - :Soil => ( - ToySoilWaterModel(), - ), - ) - # In this mapping, we have a cyclic dependency. The leaf surface is computed using the leaf biomass, which is computed using the carbon allocation, which itself depends on the - # light interception, which is computed using the LAI at scene scale, itself coming from the plant total leaf surface, computed as the sum of all leaves surfaces. - # This is a cyclic dependency that we need to break somewhere. We can break it by using the value of the leaf surface from the previous time-step, which is a common practice in - # plant models. This is done by flagging the leaf surface as a PreviousTimeStep variable in the mapping of the leaf node. - - out_vars = Dict( - :Scene => (:TT_cu, :LAI, :plant_surfaces, :aPPFD), - :Plant => (:carbon_assimilation, :carbon_allocation, :aPPFD, :soil_water_content, :leaf_surfaces, :surface, :total_surface), - :Leaf => (:carbon_demand, :carbon_allocation, :carbon_biomass), - :Internode => (:carbon_demand, :carbon_allocation, :TT_cu_emergence, :carbon_biomass), - :Soil => (:soil_water_content,), - ) - mtg = import_mtg_example() - meteo = Weather( - [ - Atmosphere(T=20.0, Wind=1.0, Rh=0.65, Ri_PAR_f=300.0), - Atmosphere(T=25.0, Wind=0.5, Rh=0.8, Ri_PAR_f=500.0) - ] - ) - - d = @test_nowarn dep(mapping) - @test to_initialize(mapping) == Dict() - #out = @test_nowarn run!(mtg, mapping, meteo, tracked_outputs=out_vars, executor=SequentialEx()) - - nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg, mapping, nsteps=nsteps, check=true, outputs=out_vars) - out = run!(sim,meteo) - - # To update the reference: - ref_path = joinpath(pkgdir(PlantSimEngine), "test/references/ref_output_simulation.csv") - # CSV.write(ref_path, sort(convert_outputs(out, DataFrame, no_value=missing), [:timestep, :node]), transform=(col, val) -> something(val, missing)) - ref_df = CSV.read(ref_path, DataFrame) - - @test sort(unique(ref_df.organ)) == sort(string.(collect(keys(out)))) - - out_df_dict = convert_outputs(out, DataFrame, no_value=missing) - - for organ in keys(out) - reduced_ref_df = ref_df[(ref_df.organ .== string(organ)), Not(:organ)] - reduced_ref_df_no_missing = reduced_ref_df[:, any.(!ismissing, eachcol(reduced_ref_df))] - sorted_reduced_ref_df_no_missing = DataFrames.select(reduced_ref_df_no_missing, sort(propertynames(reduced_ref_df_no_missing))) - sorted_out_df_dict_organ = DataFrames.select(out_df_dict[organ], sort(propertynames(out_df_dict[organ]))) - @test size(sorted_reduced_ref_df_no_missing) == size(sorted_out_df_dict_organ) - @test propertynames(sorted_reduced_ref_df_no_missing) == propertynames(sorted_out_df_dict_organ) - for c in intersect([:node, :timestep], propertynames(sorted_out_df_dict_organ)) - @test sorted_reduced_ref_df_no_missing[:, c] == sorted_out_df_dict_organ[:, c] - end - end -end diff --git a/test/test-mtg-multiscale.jl b/test/test-mtg-multiscale.jl deleted file mode 100644 index 3c700a792..000000000 --- a/test/test-mtg-multiscale.jl +++ /dev/null @@ -1,728 +0,0 @@ -# Example meteo: -meteo = Weather( - [ - Atmosphere(T=20.0, Wind=1.0, Rh=0.65), - Atmosphere(T=25.0, Wind=0.5, Rh=0.8) -] -) - -@testset "MTG initialisation" begin - simple_mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Plant, 1, 1)) - internode = Node(simple_mtg, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - leaf = Node(simple_mtg, MultiScaleTreeGraph.NodeMTG("<", :Leaf, 1, 2)) - var1 = 15.0 - var2 = 0.3 - leaf[:var2] = var2 - - mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - Process1Model(1.0), - Process2Model(), - Process3Model(), - Status(var1=var1,) - ) - ) - - @test descendants(simple_mtg, :var1) == [nothing, nothing] - @test descendants(simple_mtg, :var2) == [nothing, var2] - - @test to_initialize(mapping) == Dict(:Leaf => [:var2]) # NB: :var1 is initialised in the status - @test to_initialize(mapping, simple_mtg) == Dict() -end - -# A mapping that actually works (same as before but with the init for TT): -mapping_1 = PlantSimEngine.ModelMapping( - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - # inputs - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - # outputs - :carbon_allocation => [:Leaf, :Internode] - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - ), - :Internode => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - Status(TT=10.0) - ), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], - # Notice we provide :Soil, not [:Soil], so a single value is expected here - ), - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - Status(aPPFD=1300.0, TT=10.0), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - ), - :Soil => ( - ToySoilWaterModel(), - ), -) - -# Example MTG: -mtg = begin - scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - soil = Node(scene, MultiScaleTreeGraph.NodeMTG("/", :Soil, 1, 1)) - plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - internode1 = Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - leaf1 = Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - internode2 = Node(internode1, MultiScaleTreeGraph.NodeMTG("<", :Internode, 1, 2)) - leaf2 = Node(internode2, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - scene -end - -# Another MTG with initialisation values for biomass: -mtg_init = deepcopy(mtg) -MultiScaleTreeGraph.transform!(mtg_init, (x -> 1.0) => :carbon_biomass, symbol=[:Internode, :Leaf]) - -@testset "Multiscale dependency graph" begin - d = dep(mapping_1) - c_allocation_plant_scale = d.roots[:Leaf=>:carbon_demand].children[1].outputs - carbon_allocation_vars = last(c_allocation_plant_scale[1]) - @test carbon_allocation_vars[:carbon_offer] == -Inf - @test isa(carbon_allocation_vars[:carbon_allocation], PlantSimEngine.MappedVar) - @test PlantSimEngine.mapped_organ(carbon_allocation_vars[:carbon_allocation]) == [:Leaf, :Internode] - @test PlantSimEngine.mapped_default(carbon_allocation_vars[:carbon_allocation]) == PlantSimEngine.outputs_(ToyCAllocationModel()).carbon_allocation - @test PlantSimEngine.mapped_variable(carbon_allocation_vars[:carbon_allocation]) == :carbon_allocation - @test PlantSimEngine.source_variable(carbon_allocation_vars[:carbon_allocation]) == [:carbon_allocation, :carbon_allocation] - - # Testing inputs and outputs of the nodes: - @test d.roots[:Soil=>:soil_water].inputs == [:soil_water => NamedTuple()] - # Testing if the status given by the user is used to set the default values of the variables in the nodes: - @test last(d.roots[:Soil=>:soil_water].children[1].inputs[1]).aPPFD == 1300.0 - # Testing that the outputs that are not multiscale are simply initialised with the default values from the models: - @test d.roots[:Internode=>:carbon_demand].outputs[1] |> last |> first == -Inf - # Testing that the intputs that are not multiscale are initialised with the default values from the models, as an `UninitializedVar`: - @test d.roots[:Internode=>:maintenance_respiration].inputs[1] |> last |> first == PlantSimEngine.UninitializedVar(:carbon_biomass, 0.0) -end - -@testset "inputs and outputs of a mapping" begin - ins = inputs(mapping_1) - - @test collect(keys(ins)) == collect(keys(mapping_1)) - @test ins[:Soil] == (soil_water=(),) - @test ins[:Leaf] == (carbon_assimilation=(:aPPFD, :soil_water_content), carbon_demand=(:TT,), maintenance_respiration=(:carbon_biomass,)) - - outs = outputs(mapping_1) - @test collect(keys(outs)) == collect(keys(mapping_1)) - @test outs[:Soil] == (soil_water=(:soil_water_content,),) - @test outs[:Leaf] == (carbon_assimilation=(:carbon_assimilation,), carbon_demand=(:carbon_demand,), maintenance_respiration=(:Rm,)) - @test outs[:Plant] == (carbon_allocation=(:carbon_offer, :carbon_allocation), maintenance_respiration=(:Rm,)) - - vars = variables(mapping_1) - @test collect(keys(vars)) == collect(keys(mapping_1)) - @test keys(vars[:Soil]) == keys(outs[:Soil]) - @test keys(values(vars[:Soil])[1]) == values(outs[:Soil])[1] - @test vars[:Plant] == (carbon_allocation=(carbon_assimilation=[-Inf], Rm=-Inf, carbon_demand=[-Inf], carbon_offer=-Inf, carbon_allocation=[-Inf]), maintenance_respiration=(Rm_organs=[-Inf], Rm=-Inf),) - @test vars[:Leaf] == (carbon_assimilation=(aPPFD=-Inf, soil_water_content=-Inf, carbon_assimilation=-Inf), carbon_demand=(TT=-Inf, carbon_demand=-Inf), maintenance_respiration=(carbon_biomass=0.0, Rm=-Inf)) -end - -@testset "Status initialisation" begin - # Samuel : internal function, suppressing REPL display errors - hard_dep_graph = first(PlantSimEngine.hard_dependencies(mapping_1; verbose=false)); - @test_throws "Variable `carbon_biomass` is not computed by any model, not initialised by the user in the status, and not found in the MTG at scale Internode (checked for MTG node 4)." PlantSimEngine.init_statuses(mtg, mapping_1, hard_dep_graph) - organs_statuses, others = PlantSimEngine.init_statuses(mtg_init, mapping_1, hard_dep_graph) - - @test Set(keys(organs_statuses)) == Set([:Soil, :Internode, :Plant, :Leaf]) - # Check that the soil_water_content is linked between the soil and the leaves: - @test length(organs_statuses[:Soil]) == length(organs_statuses[:Plant]) == 1 - @test length(organs_statuses[:Leaf]) == length(organs_statuses[:Internode]) == 2 - @test organs_statuses[:Soil][1][:soil_water_content] === -Inf - @test organs_statuses[:Leaf][1][:soil_water_content] === -Inf - @test organs_statuses[:Leaf][2][:soil_water_content] === -Inf - @test organs_statuses[:Leaf][2][:soil_water_content] === organs_statuses[:Soil][1][:soil_water_content] - - organs_statuses[:Soil][1][:soil_water_content] = 1.0 - @test organs_statuses[:Leaf][1][:soil_water_content][] == 1.0 - - @test organs_statuses[:Plant][1][:carbon_assimilation] == PlantSimEngine.RefVector{Float64}([Ref(-Inf), Ref(-Inf)]) - @test organs_statuses[:Plant][1][:carbon_allocation] == PlantSimEngine.RefVector{Float64}([Ref(-Inf), Ref(-Inf), Ref(-Inf), Ref(-Inf)]) - @test organs_statuses[:Internode][1][:carbon_allocation] == -Inf - @test organs_statuses[:Leaf][1][:carbon_demand] == -Inf - - # Testing with a different type: - organs_statuses, others = PlantSimEngine.init_statuses(mtg_init, mapping_1, hard_dep_graph, type_promotion=Dict(Float64 => Float32, Vector{Float64} => Vector{Float32})) - - @test isa(organs_statuses[:Plant][1][:carbon_assimilation], PlantSimEngine.RefVector{Float32}) - @test isa(organs_statuses[:Plant][1][:carbon_allocation], PlantSimEngine.RefVector{Float32}) - @test isa(organs_statuses[:Internode][1][:carbon_allocation], Float32) - @test isa(organs_statuses[:Leaf][1][:carbon_demand], Float32) - @test isa(organs_statuses[:Soil][1][:soil_water_content], Float32) - - mapping_promoted = PlantSimEngine.ModelMapping(Dict(mapping_1); type_promotion=Dict(Float64 => Float32, Vector{Float64} => Vector{Float32})) - sim_promoted = PlantSimEngine.GraphSimulation( - mtg_init, - mapping_promoted; - nsteps=1, - outputs=Dict(:Soil => (:soil_water_content,)), - check=true, - ) - - @test sim_promoted.statuses[:Soil][1][:soil_water_content] isa Float32 - @test sim_promoted.outputs[:Soil][1][:soil_water_content] isa Float32 -end - - -@testset "Multiscale initialisations and outputs" begin - outs = Dict( - :Flowers => (:carbon_assimilation, :carbon_demand), # There are no flowers in this MTG - :Leaf => (:carbon_assimilation, :carbon_demand, :non_existing_variable), # :non_existing_variable is not computed by any model - :Soil => (:soil_water_content,), - ) - - type_promotion = nothing - nsteps = 2 - dependency_graph = dep(mapping_1) - hard_dep_graph = first(PlantSimEngine.hard_dependencies(mapping_1; verbose=false)) - organs_statuses, others = PlantSimEngine.init_statuses(mtg_init, mapping_1, hard_dep_graph; type_promotion=type_promotion) - - @test Set(keys(organs_statuses)) == Set([:Soil, :Internode, :Plant, :Leaf]) - @test collect(keys(organs_statuses[:Soil][1])) == [:node, :soil_water_content] - @test collect(keys(organs_statuses[:Leaf][1])) == [:carbon_allocation, :carbon_assimilation, :TT, :carbon_biomass, :aPPFD, :node, :Rm, :soil_water_content, :carbon_demand] - @test collect(keys(organs_statuses[:Plant][1])) == [:Rm_organs, :carbon_allocation, :carbon_assimilation, :node, :carbon_offer, :Rm, :carbon_demand] - @test organs_statuses[:Soil][1][:soil_water_content][] === -Inf - @test organs_statuses[:Leaf][1][:carbon_allocation] === -Inf - @test organs_statuses[:Leaf][1][:TT] === 10.0 - @test typeof(organs_statuses[:Plant][1][:carbon_allocation]) === PlantSimEngine.RefVector{Float64} - - @test PlantSimEngine.reverse_mapping(mapping_1, all=true) == Dict{Symbol,Dict{Symbol,Dict{Symbol,Any}}}( - :Soil => Dict(:Leaf => Dict(:soil_water_content => :soil_water_content)), - :Internode => Dict(:Plant => Dict(:carbon_allocation => :carbon_allocation, :Rm => :Rm_organs, :carbon_demand => :carbon_demand)), - :Leaf => Dict(:Plant => Dict(:carbon_allocation => :carbon_allocation, :carbon_assimilation => :carbon_assimilation, :Rm => :Rm_organs, :carbon_demand => :carbon_demand)) - ) - - @test PlantSimEngine.reverse_mapping(mapping_1, all=false) == Dict{Symbol,Dict{Symbol,Dict{Symbol,Any}}}( - :Internode => Dict(:Plant => Dict(:carbon_allocation => :carbon_allocation, :Rm => :Rm_organs, :carbon_demand => :carbon_demand)), - :Leaf => Dict(:Plant => Dict(:carbon_allocation => :carbon_allocation, :carbon_assimilation => :carbon_assimilation, :Rm => :Rm_organs, :carbon_demand => :carbon_demand)) - ) - - @test PlantSimEngine.reverse_mapping(filter(x -> x.first == :Soil, mapping_1)) == Dict{Symbol,Dict{Symbol,Dict{Symbol,Any}}}() - @test PlantSimEngine.to_initialize(mapping_1, mtg) == Dict(:Internode => [:carbon_biomass], :Leaf => [:carbon_biomass]) - @test PlantSimEngine.to_initialize(mapping_1, mtg_init) == Dict{Symbol,Symbol}() - - statuses, status_templates, reverse_multiscale_mapping, vars_need_init = PlantSimEngine.init_statuses(mtg_init, mapping_1, hard_dep_graph) - @test Set(keys(statuses)) == Set([:Soil, :Internode, :Plant, :Leaf]) - - @test length(statuses[:Internode]) == length(statuses[:Leaf]) == 2 - @test length(statuses[:Soil]) == length(statuses[:Plant]) == 1 - - e_1 = "You requested outputs for organs Soil, Flowers, Leaf, but organs Flowers have no models." - e_2 = "You requested outputs for variables A, carbon_demand, non_existing_variable, but variables non_existing_variable have no models." - - # If check is true, this should return an error (some outputs are not computed): - if VERSION < v"1.8" # We test differently depending on the julia version because the format of the error message changed - @test_throws ErrorException PlantSimEngine.pre_allocate_outputs(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outs, nsteps) - else - @test_throws e_1 PlantSimEngine.pre_allocate_outputs(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outs, nsteps) - end - - outs_ = @test_logs (:info, "You requested outputs for organs Soil, Flowers, Leaf, but organs Flowers have no models.") (:info, "You requested outputs for variable non_existing_variable in organ Leaf, but it has no model.") PlantSimEngine.pre_allocate_outputs(statuses, status_templates, reverse_multiscale_mapping, vars_need_init, outs, nsteps, check=false) - - @test outs_ == Dict( - :Soil => [Status(timestep = 1, node = MultiScaleTreeGraph.Node(NodeMTG("/", :Uninitialized, 0, 0)), soil_water_content = -Inf), - Status(timestep = 1, node = MultiScaleTreeGraph.Node(NodeMTG("/", :Uninitialized, 0, 0),), soil_water_content = -Inf)], - :Leaf => [Status(timestep = 1, node = MultiScaleTreeGraph.Node(NodeMTG("/", :Uninitialized, 0, 0),), carbon_assimilation = -Inf, carbon_demand = -Inf), - Status(timestep = 1, node = MultiScaleTreeGraph.Node(NodeMTG("/", :Uninitialized, 0, 0),), carbon_assimilation = -Inf, carbon_demand = -Inf)] - ) -end - -# Testing the mappings: -@testset "Mapping: missing initialisation" begin - mapping = PlantSimEngine.ModelMapping( - :Plant => - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - # inputs - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - # outputs - :carbon_allocation => [:Leaf, :Internode] - ], - ), - :Internode => ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], - # Notice we provide :Soil, not [:Soil], so a single value is expected here - ), - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - Status(aPPFD=1300.0, TT=10.0), - ), - :Soil => ( - ToySoilWaterModel(), - ), - ) - - to_init = @test_nowarn to_initialize(mapping) - - @test to_init[:Internode] == [:TT] - - mapped_vars = PlantSimEngine.mapped_variables(mapping) - @test collect(keys(mapped_vars[:Plant])) == [:carbon_allocation, :carbon_assimilation, :carbon_offer, :Rm, :carbon_demand] - @test [PlantSimEngine.mapped_default(i) for i in values(mapped_vars[:Plant])] == [-Inf, -Inf, -Inf, PlantSimEngine.UninitializedVar{Float64}(:Rm, -Inf), -Inf] - @test collect(keys(mapped_vars[:Leaf])) == [:carbon_allocation, :carbon_assimilation, :TT, :aPPFD, :soil_water_content, :carbon_demand] - @test [PlantSimEngine.mapped_default(i) for i in values(mapped_vars[:Leaf])] == [-Inf, -Inf, 10.0, 1300.0, -Inf, -Inf] - @test collect(keys(mapped_vars[:Soil])) == [:soil_water_content] - @test [PlantSimEngine.mapped_default(i) for i in values(mapped_vars[:Soil])] == [-Inf] -end - -@testset "Mapping: missing organ in mapping (Soil)" begin - @test_throws "missing scale `Soil`" PlantSimEngine.ModelMapping( - :Plant => - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - # inputs - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - # outputs - :carbon_allocation => [:Leaf, :Internode] - ], - ), - :Internode => ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], - # Notice we provide :Soil, not [:Soil], so a single value is expected here - ), - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - Status(aPPFD=1300.0, TT=10.0), - ) - ) -end - -mtg_var = let - scene = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - soil = Node(scene, MultiScaleTreeGraph.NodeMTG("/", :Soil, 1, 1)) - plant = Node(scene, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - internode1 = Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - leaf1 = Node(internode1, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - scene -end - -@testset "Mapping: missing model at other scale (soil_water_content) + missing init + var1 from MTG" begin - @test_throws "not available at scale `Soil`" PlantSimEngine.ModelMapping( - :Plant => - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - # inputs - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - # outputs - :carbon_allocation => [:Leaf, :Internode] - ], - ), - :Internode => ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], - # Notice we provide :Soil, not [:Soil], so a single value is expected here - ), - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - Status(aPPFD=1300.0, TT=10.0), - ), - :Soil => ( - Process1Model(1.0), - ), - ) -end - -@testset "Mapping: missing init + var1 from MTG" begin - mapping = PlantSimEngine.ModelMapping( - :Plant => - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - # inputs - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - # outputs - :carbon_allocation => [:Leaf, :Internode] - ], - ), - :Internode => ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :var3),], - # Notice we provide :Soil, not [:Soil], so a single value is expected here - ), - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - Status(aPPFD=1300.0, TT=10.0), - ), - :Soil => ( - Process1Model(1.0), - ), - ) - - to_init = to_initialize(mapping, mtg_var) - - @test to_init[:Soil] == [:var1, :var2]# var1 would be here if not present in the MTG - - soil_node = mtg_var[1] - soil_node[:var1] = 1.0 - to_init = to_initialize(mapping, mtg_var) - @test to_init[:Soil] == [:var2]# var1 would be here if not present in the MTG - @test !haskey(to_init, :Leaf) - @test to_init[:Internode] == [:TT] -end -# Testing with a simple mapping (just the soil model, no multiscale mapping): -@testset "run! on MTG: simple mapping" begin - #out = @test_nowarn run!(mtg, Dict(:Soil => (ToySoilWaterModel(),)), meteo) - nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg, PlantSimEngine.ModelMapping(:Soil => (ToySoilWaterModel(),)), nsteps=nsteps, check=true, outputs=nothing) - out = run!(sim,meteo) - - @test sim.statuses[:Soil][1].node == soil - @test sim.models == Dict(:Soil => (soil_water=ToySoilWaterModel(sim.models[:Soil].soil_water.values),)) - @test sim.models[:Soil].soil_water.values == [0.5] - @test length(sim.dependency_graph.roots) == 1 - @test collect(keys(sim.dependency_graph.roots))[1] == Pair(:Soil, :soil_water) - @test sim.graph == mtg - - leaf_mapping = PlantSimEngine.ModelMapping(:Leaf => (ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), Status(TT=10.0))) - - #out = run!(mtg, leaf_mapping, meteo) - nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg, leaf_mapping, nsteps=nsteps, check=true, outputs=nothing) - out = run!(sim,meteo) - - @test collect(keys(sim.statuses)) == [:Leaf] - @test length(sim.statuses[:Leaf]) == 2 - @test sim.statuses[:Leaf][1].TT == 10.0 # As initialized in the mapping - @test sim.statuses[:Leaf][1].carbon_demand == 0.5 - - @test sim.statuses[:Leaf][1].node == leaf1 - @test sim.statuses[:Leaf][2].node == leaf2 -end - -# A mapping with all different types of mapping (single, multi-scale, model as is, or tuple of): -@testset "run! on MTG with complete mapping (missing init)" begin - mapping_all = PlantSimEngine.ModelMapping( - :Plant => - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - # inputs - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - # outputs - :carbon_allocation => [:Leaf, :Internode] - ], - ), - :Internode => ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], - # Notice we provide :Soil, not [:Soil], so a single value is expected here - ), - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - Status(aPPFD=1300.0, TT=10.0), - ), - :Soil => ( - ToySoilWaterModel(), - ), - ) - # The mapping above should throw an error because TT is not initialized for the Internode: - if VERSION < v"1.8" # We test differently depending on the julia version because the format of the error message changed - - nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg, mapping_all, nsteps=nsteps, check=true, outputs=nothing) - @test_throws ErrorException out = run!(sim,meteo) - else - @test_throws "Variable `Rm` is not computed by any model, not initialised by the user in the status, and not found in the MTG at scale Plant (checked for MTG node 3)." run!(mtg, mapping_all, meteo) - end - - # It should work if we don't check the mapping though: - #out = @test_nowarn run!(mtg, mapping_all, meteo, check=false) - nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg, mapping_all, nsteps=nsteps, check=false, outputs=nothing) - out = run!(sim,meteo) - # Note that the outputs are garbage because the TT is not initialized. - - @test sim.models == Dict{Symbol,NamedTuple}( - :Soil => (soil_water=ToySoilWaterModel(sim.models[:Soil].soil_water.values),), - :Internode => (carbon_demand=ToyCDemandModel{Float64}(10.0, 200.0),), - :Plant => (carbon_allocation=ToyCAllocationModel(),), - :Leaf => (carbon_assimilation=ToyAssimModel{Float64}(0.2), carbon_demand=ToyCDemandModel{Float64}(10.0, 200.0)) - ) - @test sim.models[:Soil].soil_water.values == [0.5] - @test length(sim.dependency_graph.roots) == 3 # 3 because the plant is not a root (its model has dependencies) - @test sim.statuses[:Internode][1].TT === -Inf - @test sim.statuses[:Internode][1].carbon_demand === -Inf - - st_leaf1 = sim.statuses[:Leaf][1] - @test st_leaf1.TT == 10.0 - @test st_leaf1.carbon_demand == 0.5 - # This one depends on the soil, which is random, so we test using the computation directly: - @test st_leaf1.carbon_assimilation == st_leaf1.aPPFD * sim.models[:Leaf].carbon_assimilation.LUE * st_leaf1.soil_water_content - @test st_leaf1.carbon_allocation == 0.0 -end - -@testset "run! on MTG with complete mapping (with init)" begin - - #out = @test_nowarn run!(mtg_init, mapping_1, meteo, executor=SequentialEx()) - nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg_init, mapping_1, nsteps=nsteps, check=false, outputs=nothing) - out = run!(sim,meteo) - - @test typeof(sim.statuses) == Dict{Symbol,Vector{Status}} - @test length(sim.statuses[:Plant]) == 1 - @test length(sim.statuses[:Leaf]) == 2 - @test length(sim.statuses[:Internode]) == 2 - @test length(sim.statuses[:Soil]) == 1 - @test sim.statuses[:Soil][1].node == get_node(mtg_init, 2) - @test sim.statuses[:Soil][1].soil_water_content !== -Inf - - # Testing that we get the link between the node and its status: - @test sim.statuses[:Soil][1] == get_node(mtg_init, 2).plantsimengine_status - # Testing if the value in the status of the leaves is the same as the one in the status of the soil: - @test sim.statuses[:Soil][1].soil_water_content === sim.statuses[:Leaf][1].soil_water_content - @test sim.statuses[:Soil][1].soil_water_content === sim.statuses[:Leaf][2].soil_water_content - - leaf1_status = sim.statuses[:Leaf][1] - - # This is the model that computes the assimilation (testing manually that we get the right result here): - @test leaf1_status.carbon_assimilation == leaf1_status.aPPFD * sim.models[:Leaf].carbon_assimilation.LUE * leaf1_status.soil_water_content - - @test sim.statuses[:Plant][1].carbon_demand[[1, 3]] == [i.carbon_demand for i in sim.statuses[:Internode]] - @test sim.statuses[:Plant][1].carbon_demand[[2, 4]] == [i.carbon_demand for i in sim.statuses[:Leaf]] - - # Testing the reference directly: - ref_values_cdemand = getfield(sim.statuses[:Plant][1].carbon_demand, :v) - - for (j, i) in enumerate([1, 3]) - @test ref_values_cdemand[i] === PlantSimEngine.refvalue(sim.statuses[:Internode][j], :carbon_demand) - end - - for (j, i) in enumerate([2, 4]) - @test ref_values_cdemand[i] === PlantSimEngine.refvalue(sim.statuses[:Leaf][j], :carbon_demand) - end - - # Testing that carbon allocation in Leaf and Internode was added as a variable from the model at the Plant scale: - - @test hasproperty(sim.statuses[:Internode][1], :carbon_allocation) - @test hasproperty(sim.statuses[:Leaf][1], :carbon_allocation) - - @test sim.statuses[:Internode][1].carbon_allocation == 0.5 - @test sim.statuses[:Leaf][1].carbon_allocation == 0.5 - - - # Testing that we get the link between the node and its status: - @test sim.statuses[:Leaf][2] == get_node(mtg_init, 7).plantsimengine_status - - # Testing the reference directly: - ref_values_callocation = getfield(sim.statuses[:Plant][1].carbon_allocation, :v) - - for (j, i) in enumerate([1, 3]) - @test ref_values_callocation[i] === PlantSimEngine.refvalue(sim.statuses[:Internode][j], :carbon_allocation) - end - - for (j, i) in enumerate([2, 4]) - @test ref_values_callocation[i] === PlantSimEngine.refvalue(sim.statuses[:Leaf][j], :carbon_allocation) - end -end - -# Here we initialise var1 to a constant value: -@testset "MTG initialisation" begin - var1 = 1.0 - mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - Process1Model(1.0), - Process2Model(), - Process3Model(), - Status(var1=var1,) - ) - ) - # Need init for var2, so it returns an error: - if VERSION < v"1.8" # We test differently depending on the julia version because the format of the error message changed - @test_throws ErrorException PlantSimEngine.init_simulation(mtg, mapping) - else - @test_throws "Variable `var2` is not computed by any model, not initialised by the user in the status, and not found in the MTG at scale Leaf (checked for MTG node 5)." PlantSimEngine.init_simulation(mtg, mapping) - end - - mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - Process1Model(1.0), - Process2Model(), - Process3Model(), - Status(var1=var1, var2=1.0) - ) - ) - - #out = @test_nowarn PlantSimEngine.run!(mtg, mapping, meteo) - nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg, mapping, nsteps=nsteps, check=false, outputs=nothing) - out = run!(sim,meteo) - - @test sim.statuses[:Leaf][1].var1 === var1 - @test sim.statuses[:Leaf][1].var2 === 1.0 - @test sim.statuses[:Leaf][1].var3 === 2.0 - @test sim.statuses[:Leaf][1].var6 === 40.4 -end - -@testset "MTG with complex mapping" begin - mapping = PlantSimEngine.ModelMapping( - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - # inputs - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - # outputs - :carbon_allocation => [:Leaf, :Internode] - ] - ), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - ), - :Internode => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - Status(TT=10.0, carbon_biomass=1.0) - ), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], - # Notice we provide :Soil, not [:Soil], so a single value is expected here - ), - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - Process1Model(1.0), - Process2Model(), - Process3Model(), - Process4Model(), - Process5Model(), - Process6Model(), - Status(aPPFD=1300.0, TT=10.0, var0=1.0, var9=1.0, carbon_biomass=1.0), - ), - :Soil => ( - ToySoilWaterModel(), - ), - ) - - #out = @test_nowarn PlantSimEngine.run!(mtg, mapping, meteo, executor=SequentialEx()) - nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg, mapping, nsteps=nsteps, check=false, outputs=nothing) - out = run!(sim,meteo) - - @test length(sim.dependency_graph.roots) == 6 - @test sim.statuses[:Leaf][1].var1 === 1.01 - @test sim.statuses[:Leaf][1].var2 === 1.03 - @test sim.statuses[:Leaf][1].var4 ≈ 8.1612000000000013 atol = 1e-6 - @test sim.statuses[:Leaf][1].var5 == 32.4806 - @test sim.statuses[:Leaf][1].var8 ≈ 1321.0700490800002 atol = 1e-6 -end - -@testset "MTG with dynamic output variables" begin - mapping = PlantSimEngine.ModelMapping( - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - # inputs - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - # outputs - :carbon_allocation => [:Leaf, :Internode] - ], - ), - PlantSimEngine.MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - ), - :Internode => ( - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - Status(TT=10.0, carbon_biomass=1.0) - ), - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => (:Soil => :soil_water_content),], - # Notice we provide :Soil, not [:Soil], so a single value is expected here - ), - ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - Process1Model(1.0), - Process2Model(), - Process3Model(), - Process4Model(), - Process5Model(), - Process6Model(), - Status(aPPFD=1300.0, TT=10.0, var0=1.0, var9=1.0, carbon_biomass=1.0), - ), - :Soil => ( - ToySoilWaterModel(), - ), - ) - - out_vars = Dict( - :Leaf => (:carbon_assimilation, :carbon_demand, :soil_water_content, :carbon_allocation), - :Internode => (:carbon_allocation,), - :Plant => (:carbon_allocation,), - :Soil => (:soil_water_content,), - ) - - #out = @test_nowarn PlantSimEngine.run!(mtg, mapping, meteo, tracked_outputs=out_vars, executor=SequentialEx()) - nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg, mapping, nsteps=nsteps, check=true, outputs=out_vars) - out = run!(sim,meteo) - - @test length(sim.dependency_graph.roots) == 6 - @test sim.statuses[:Leaf][1].var1 === 1.01 - @test sim.statuses[:Leaf][1].var2 === 1.03 - @test sim.statuses[:Leaf][1].var4 ≈ 8.1612000000000013 atol = 1e-6 - @test sim.statuses[:Leaf][1].var5 == 32.4806 - @test sim.statuses[:Leaf][1].var8 ≈ 1321.0700490800002 atol = 1e-6 - - @test unique([sim.outputs[:Leaf][i][:carbon_demand] == 0.5 for i in 1:4]) == [1] - @test sim.outputs[:Leaf][1][:soil_water_content] == sim.outputs[:Soil][1][:soil_water_content] - @test sim.outputs[:Leaf][2][:soil_water_content] == sim.outputs[:Soil][2][:soil_water_content] - - @test all([sim.outputs[:Leaf][i][:carbon_allocation] == sim.outputs[:Internode][i][:carbon_allocation] for i in 1:4])==1 - @test sim.outputs[:Plant][1][:carbon_allocation][1] === sim.outputs[:Internode][1][:carbon_allocation] - - # Testing the outputs if transformed into a DataFrame: - outs_df_dict = convert_outputs(out, DataFrame) - - @test isa(outs_df_dict, Dict{Symbol, DataFrame}) - @test size(outs_df_dict[:Leaf]) == (4, 6) - @test size(outs_df_dict[:Internode]) == (4, 3) - @test size(outs_df_dict[:Soil]) == (2, 3) - @test size(outs_df_dict[:Plant]) == (2, 2) - - @test sort(collect(keys(outs_df_dict))) == sort(collect(keys(out_vars))) - for organ in keys(outs_df_dict) - @test unique(outs_df_dict[organ][!, :timestep]) == [1, 2] - end - # output structure change makes this test obsolete without any trivial equivalent - #@test length(filter(x -> x !== nothing, outs.carbon_assimilation)) == length(filter(x -> x, traverse(mtg, node -> MultiScaleTreeGraph.scale(node) == 2))) - - # TODO, find some equivalent with new output structure - #A = outputs(out, :carbon_assimilation) - #@test A == outs.carbon_assimilation - - #A2 = outputs(out, 5) - #@test A == A2 -end diff --git a/test/test-multirate-output-export.jl b/test/test-multirate-output-export.jl deleted file mode 100644 index f28f38b4f..000000000 --- a/test/test-multirate-output-export.jl +++ /dev/null @@ -1,340 +0,0 @@ -using PlantSimEngine -using MultiScaleTreeGraph -using PlantMeteo -using DataFrames -using Test - -PlantSimEngine.@process "mrexportsource" verbose = false -struct MRExportSourceModel <: AbstractMrexportsourceModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::MRExportSourceModel) = NamedTuple() -PlantSimEngine.outputs_(::MRExportSourceModel) = (X=-Inf,) -function PlantSimEngine.run!(m::MRExportSourceModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.X = float(m.n[]) -end - -PlantSimEngine.@process "mrdefaultleafsource" verbose = false -struct MRDefaultLeafSourceModel <: AbstractMrdefaultleafsourceModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::MRDefaultLeafSourceModel) = NamedTuple() -PlantSimEngine.outputs_(::MRDefaultLeafSourceModel) = (X=-Inf,) -function PlantSimEngine.run!(m::MRDefaultLeafSourceModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.X = float(m.n[]) -end - -PlantSimEngine.@process "mrdefaultplantagg" verbose = false -struct MRDefaultPlantAggModel <: AbstractMrdefaultplantaggModel end -PlantSimEngine.inputs_(::MRDefaultPlantAggModel) = (X=-Inf,) -PlantSimEngine.outputs_(::MRDefaultPlantAggModel) = (XP=-Inf,) -function PlantSimEngine.run!(::MRDefaultPlantAggModel, models, status, meteo, constants=nothing, extra=nothing) - status.XP = sum(status.X) + 100.0 -end -PlantSimEngine.timespec(::Type{<:MRDefaultPlantAggModel}) = ClockSpec(2.0, 1.0) - -PlantSimEngine.@process "mrdefaultsceneagg" verbose = false -struct MRDefaultSceneAggModel <: AbstractMrdefaultsceneaggModel end -PlantSimEngine.inputs_(::MRDefaultSceneAggModel) = (XP=-Inf,) -PlantSimEngine.outputs_(::MRDefaultSceneAggModel) = (XS=-Inf,) -function PlantSimEngine.run!(::MRDefaultSceneAggModel, models, status, meteo, constants=nothing, extra=nothing) - status.XS = sum(status.XP) + 1000.0 -end -PlantSimEngine.timespec(::Type{<:MRDefaultSceneAggModel}) = ClockSpec(4.0, 1.0) - -PlantSimEngine.@process "mrdynamicgrow" verbose = false -struct MRDynamicGrowModel <: AbstractMrdynamicgrowModel end -PlantSimEngine.inputs_(::MRDynamicGrowModel) = NamedTuple() -PlantSimEngine.outputs_(::MRDynamicGrowModel) = (nleaves=0.0,) -function PlantSimEngine.run!(::MRDynamicGrowModel, models, status, meteo, constants=nothing, extra=nothing) - if length(PlantSimEngine.status(extra)[:Leaf]) == 0 - add_organ!(status.node, extra, "+", :Leaf, 2; check=true) - end - status.nleaves = length(PlantSimEngine.status(extra)[:Leaf]) - return nothing -end - -PlantSimEngine.@process "mrdynamicgrowmany" verbose = false -struct MRDynamicGrowManyModel <: AbstractMrdynamicgrowmanyModel end -PlantSimEngine.inputs_(::MRDynamicGrowManyModel) = NamedTuple() -PlantSimEngine.outputs_(::MRDynamicGrowManyModel) = (nleaves=0.0,) -function PlantSimEngine.run!(::MRDynamicGrowManyModel, models, status, meteo, constants=nothing, extra=nothing) - while length(PlantSimEngine.status(extra)[:Leaf]) < 2 - add_organ!(status.node, extra, "+", :Leaf, 2; check=true) - end - status.nleaves = length(PlantSimEngine.status(extra)[:Leaf]) - return nothing -end - -PlantSimEngine.@process "mrdynamicleafsource" verbose = false -struct MRDynamicLeafSourceModel <: AbstractMrdynamicleafsourceModel end -PlantSimEngine.inputs_(::MRDynamicLeafSourceModel) = (nleaves=0.0,) -PlantSimEngine.outputs_(::MRDynamicLeafSourceModel) = (X=-Inf,) -function PlantSimEngine.run!(::MRDynamicLeafSourceModel, models, status, meteo, constants=nothing, extra=nothing) - status.X = status.nleaves * meteo.T / 10.0 - return nothing -end - -function _mr_custom_plant_scope(node, scale, process) - current = node - while !isnothing(current) - if MultiScaleTreeGraph.symbol(current) == :Plant - return ScopeId(:custom_plant, MultiScaleTreeGraph.node_id(current)) - end - current = parent(current) - end - return ScopeId(:custom_plant, 0) -end - -@testset "Multi-rate output export API" begin - mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant = Node(mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - internode = Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - Node(internode, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - - meteo4 = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65)], 4)) - - # Stream-only producer remains exportable when process is explicit. - mapping_stream = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRExportSourceModel(Ref(0))) |> - TimeStep(1.0) |> - OutputRouting(; X=:stream_only), - ), - ) - - req_hold = OutputRequest(:Leaf, :X; name=:x_hold, process=:mrexportsource, policy=HoldLast()) - req_sum2 = OutputRequest(:Leaf, :X; name=:x_sum2, process=:mrexportsource, policy=Integrate(), clock=ClockSpec(2.0, 1.0)) - - sim_stream = PlantSimEngine.GraphSimulation(mtg, mapping_stream, nsteps=4, check=true, outputs=Dict(:Leaf => (:X,))) - run!( - sim_stream, - meteo4, - executor=SequentialEx(), - tracked_outputs=[req_hold, req_sum2], - ) - exported = collect_outputs(sim_stream; sink=DataFrame) - - @test exported[:x_hold][:, :timestep] == [1, 2, 3, 4] - @test exported[:x_hold][:, :value] == [1.0, 2.0, 3.0, 4.0] - @test exported[:x_sum2][:, :timestep] == [1, 3] - @test exported[:x_sum2][:, :value] == [1.0, 5.0] - - # Without process and with stream-only routing, canonical source resolution should fail. - @test_throws "No canonical publisher found" run!( - sim_stream, - meteo4, - executor=SequentialEx(), - tracked_outputs=[OutputRequest(:Leaf, :X; name=:x_auto_fail)], - ) - - # Canonical routing allows omitting process in requests. - mapping_canonical = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRExportSourceModel(Ref(0))) |> - TimeStep(1.0), - ), - ) - - sim_canonical = PlantSimEngine.GraphSimulation(mtg, mapping_canonical, nsteps=4, check=true, outputs=Dict(:Leaf => (:X,))) - run!( - sim_canonical, - meteo4, - executor=SequentialEx(), - tracked_outputs=[OutputRequest(:Leaf, :X; name=:x_auto, policy=HoldLast())], - ) - exported_auto = collect_outputs(sim_canonical; sink=DataFrame) - @test exported_auto[:x_auto][:, :value] == [1.0, 2.0, 3.0, 4.0] - @test_throws "`application=` in `OutputRequest` is only supported" run!( - sim_canonical, - meteo4, - executor=SequentialEx(), - tracked_outputs=[ - OutputRequest( - :Leaf, - :X; - name=:x_application_unsupported, - application=:leaf_source, - ), - ], - ) - - # Optional direct export return from run! on GraphSimulation. - sim_direct = PlantSimEngine.GraphSimulation( - mtg, - PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRExportSourceModel(Ref(0))) |> - TimeStep(1.0), - ), - ), - nsteps=4, - check=true, - outputs=Dict(:Leaf => (:X,)), - ) - out_status, out_requested = run!( - sim_direct, - meteo4, - executor=SequentialEx(), - tracked_outputs=[OutputRequest(:Leaf, :X; name=:x_direct, policy=HoldLast())], - return_requested_outputs=true, - ) - @test haskey(out_status, :Leaf) - @test out_requested[:x_direct][:, :value] == [1.0, 2.0, 3.0, 4.0] - - # Optional direct export return from run! on MTG + mapping entry point. - out_status_mtg, out_requested_mtg = run!( - mtg, - PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRExportSourceModel(Ref(0))) |> - TimeStep(1.0), - ), - ), - meteo4; - executor=SequentialEx(), - tracked_outputs=[OutputRequest(:Leaf, :X; name=:x_mtg, policy=HoldLast())], - return_requested_outputs=true, - ) - @test haskey(out_status_mtg, :Leaf) - @test out_requested_mtg[:x_mtg][:, :value] == [1.0, 2.0, 3.0, 4.0] - - # Dynamic statuses created after GraphSimulation initialization are visible - # to online OutputRequest exports. - dynamic_mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - dynamic_plant = Node(dynamic_mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - dynamic_meteo = Weather([ - Atmosphere(T=10.0, Wind=1.0, Rh=0.65), - Atmosphere(T=20.0, Wind=1.0, Rh=0.65), - Atmosphere(T=30.0, Wind=1.0, Rh=0.65), - Atmosphere(T=40.0, Wind=1.0, Rh=0.65), - ]) - dynamic_mapping = PlantSimEngine.ModelMapping( - :Plant => ( - ModelSpec(MRDynamicGrowModel()) |> - TimeStep(1.0) |> - PlantSimEngine.ScopeModel(:plant), - ), - :Leaf => ( - ModelSpec(MRDynamicLeafSourceModel()) |> - PlantSimEngine.MultiScaleModel([:nleaves => (:Plant => :nleaves)]) |> - TimeStep(1.0) |> - PlantSimEngine.ScopeModel(:plant), - ), - ) - dynamic_sim = PlantSimEngine.GraphSimulation( - dynamic_mtg, - dynamic_mapping, - nsteps=4, - check=true, - outputs=Dict(:Leaf => (:X,)), - ) - run!( - dynamic_sim, - dynamic_meteo, - executor=SequentialEx(), - tracked_outputs=[ - OutputRequest(:Leaf, :X; name=:dynamic_hold, process=:mrdynamicleafsource, policy=HoldLast()), - OutputRequest(:Leaf, :X; name=:dynamic_sum2, process=:mrdynamicleafsource, policy=Integrate(), clock=ClockSpec(2.0, 1.0)), - ], - ) - dynamic_exported = collect_outputs(dynamic_sim; sink=DataFrame) - @test length(status(dynamic_sim)[:Leaf]) == 1 - @test dynamic_exported[:dynamic_hold][:, :timestep] == [1, 2, 3, 4] - @test dynamic_exported[:dynamic_hold][:, :value] == [1.0, 2.0, 3.0, 4.0] - @test dynamic_exported[:dynamic_sum2][:, :timestep] == [1, 3] - @test dynamic_exported[:dynamic_sum2][:, :value] == [1.0, 5.0] - - # Several dynamic statuses and callable scopes are resolved from live status - # vectors when temporal outputs are exported. - many_mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - many_plant = Node(many_mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - many_mapping = PlantSimEngine.ModelMapping( - :Plant => ( - ModelSpec(MRDynamicGrowManyModel()) |> - TimeStep(1.0) |> - PlantSimEngine.ScopeModel(_mr_custom_plant_scope), - ), - :Leaf => ( - ModelSpec(MRDynamicLeafSourceModel()) |> - PlantSimEngine.MultiScaleModel([:nleaves => (:Plant => :nleaves)]) |> - TimeStep(1.0) |> - PlantSimEngine.ScopeModel(_mr_custom_plant_scope), - ), - ) - many_sim = PlantSimEngine.GraphSimulation( - many_mtg, - many_mapping, - nsteps=4, - check=true, - outputs=Dict(:Leaf => (:X,)), - ) - run!( - many_sim, - dynamic_meteo, - executor=SequentialEx(), - tracked_outputs=[ - OutputRequest(:Leaf, :X; name=:many_hold, process=:mrdynamicleafsource, policy=HoldLast()), - OutputRequest(:Leaf, :X; name=:many_sum2, process=:mrdynamicleafsource, policy=Integrate(), clock=ClockSpec(2.0, 1.0)), - ], - ) - many_exported = collect_outputs(many_sim; sink=DataFrame) - @test length(status(many_sim)[:Leaf]) == 2 - @test many_exported[:many_hold][:, :timestep] == [1, 1, 2, 2, 3, 3, 4, 4] - @test many_exported[:many_hold][:, :value] == [2.0, 2.0, 4.0, 4.0, 6.0, 6.0, 8.0, 8.0] - @test many_exported[:many_sum2][:, :timestep] == [1, 1, 3, 3] - @test many_exported[:many_sum2][:, :value] == [2.0, 2.0, 10.0, 10.0] -end - -@testset "Multi-rate output export defaults on multi-scale mapping with timespec traits" begin - mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant = Node(mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - internode = Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - Node(internode, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - - meteo8 = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65)], 8)) - - mapping_defaults = PlantSimEngine.ModelMapping( - :Leaf => ( - PlantSimEngine.MultiScaleModel( - model=MRDefaultLeafSourceModel(Ref(0)), - mapped_variables=[:X => (:Plant => :X)], - ), - ), - :Plant => ( - PlantSimEngine.MultiScaleModel( - model=MRDefaultPlantAggModel(), - mapped_variables=[:XP => (:Scene => :XP)], - ), - ), - :Scene => ( - MRDefaultSceneAggModel(), - ), - ) - - sim_defaults = PlantSimEngine.GraphSimulation( - mtg, - mapping_defaults, - nsteps=8, - check=true, - outputs=Dict(:Leaf => (:X,), :Plant => (:XP,), :Scene => (:XS,)), - ) - - run!( - sim_defaults, - meteo8, - executor=SequentialEx(), - tracked_outputs=[ - OutputRequest(:Plant, :XP), - OutputRequest(:Scene, :XS), - ], - ) - - exported_defaults = collect_outputs(sim_defaults; sink=DataFrame) - - @test sort(collect(keys(exported_defaults))) == [:XP, :XS] - @test exported_defaults[:XP][:, :timestep] == collect(1:8) - @test exported_defaults[:XS][:, :timestep] == collect(1:8) -end diff --git a/test/test-multirate-runtime.jl b/test/test-multirate-runtime.jl deleted file mode 100644 index ff1d91510..000000000 --- a/test/test-multirate-runtime.jl +++ /dev/null @@ -1,1386 +0,0 @@ -using PlantSimEngine -using PlantSimEngine.Examples -using MultiScaleTreeGraph -using PlantMeteo -using DataFrames -using Test -using Dates - -const _HAS_METEO_SAMPLER_API = isdefined(PlantMeteo, :prepare_weather_sampler) && - isdefined(PlantMeteo, :RollingWindow) && - isdefined(PlantMeteo, :sample_weather) - -# Producer stream: writes :S. -PlantSimEngine.@process "mrsource" verbose = false -struct MRSourceModel <: AbstractMrsourceModel end -PlantSimEngine.inputs_(::MRSourceModel) = NamedTuple() -PlantSimEngine.outputs_(::MRSourceModel) = (S=-Inf,) -function PlantSimEngine.run!(::MRSourceModel, models, status, meteo, constants=nothing, extra=nothing) - status.S = 10.0 -end - -# Writes :C in status, but is declared stream-only for canonical publication checks. -PlantSimEngine.@process "mroverwrite" verbose = false -struct MROverwriteModel <: AbstractMroverwriteModel end -PlantSimEngine.inputs_(::MROverwriteModel) = (S=-Inf,) -PlantSimEngine.outputs_(::MROverwriteModel) = (C=-Inf,) -function PlantSimEngine.run!(::MROverwriteModel, models, status, meteo, constants=nothing, extra=nothing) - status.C = -999.0 -end - -# Consumer reads :C and writes :B. -PlantSimEngine.@process "mrconsumer" verbose = false -struct MRConsumerModel <: AbstractMrconsumerModel end -PlantSimEngine.inputs_(::MRConsumerModel) = (C=-Inf,) -PlantSimEngine.outputs_(::MRConsumerModel) = (B=-Inf,) -function PlantSimEngine.run!(::MRConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.B = status.C -end - -# Direct mapping case: same variable name on producer and consumer (:S -> :S). -PlantSimEngine.@process "mrdirectconsumer" verbose = false -struct MRDirectConsumerModel <: AbstractMrdirectconsumerModel end -PlantSimEngine.inputs_(::MRDirectConsumerModel) = (S=-Inf,) -PlantSimEngine.outputs_(::MRDirectConsumerModel) = (D=-Inf,) -function PlantSimEngine.run!(::MRDirectConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.D = status.S -end - -PlantSimEngine.@process "mrautosamename" verbose = false -struct MRAutoSameNameModel <: AbstractMrautosamenameModel end -PlantSimEngine.inputs_(::MRAutoSameNameModel) = (S=-Inf,) -PlantSimEngine.outputs_(::MRAutoSameNameModel) = (E=-Inf,) -function PlantSimEngine.run!(::MRAutoSameNameModel, models, status, meteo, constants=nothing, extra=nothing) - status.E = status.S -end - -PlantSimEngine.@process "mrconflict1" verbose = false -struct MRConflict1Model <: AbstractMrconflict1Model end -PlantSimEngine.inputs_(::MRConflict1Model) = NamedTuple() -PlantSimEngine.outputs_(::MRConflict1Model) = (Z=-Inf,) -function PlantSimEngine.run!(::MRConflict1Model, models, status, meteo, constants=nothing, extra=nothing) - status.Z = 1.0 -end - -PlantSimEngine.@process "mrconflict2" verbose = false -struct MRConflict2Model <: AbstractMrconflict2Model end -PlantSimEngine.inputs_(::MRConflict2Model) = NamedTuple() -PlantSimEngine.outputs_(::MRConflict2Model) = (Z=-Inf,) -function PlantSimEngine.run!(::MRConflict2Model, models, status, meteo, constants=nothing, extra=nothing) - status.Z = 2.0 -end - -PlantSimEngine.@process "mrancestorsource" verbose = false -struct MRAncestorSourceModel <: AbstractMrancestorsourceModel end -PlantSimEngine.inputs_(::MRAncestorSourceModel) = NamedTuple() -PlantSimEngine.outputs_(::MRAncestorSourceModel) = (Z=-Inf,) -function PlantSimEngine.run!(::MRAncestorSourceModel, models, status, meteo, constants=nothing, extra=nothing) - status.Z = 11.0 -end - -PlantSimEngine.@process "mrsiblingsource" verbose = false -struct MRSiblingSourceModel <: AbstractMrsiblingsourceModel end -PlantSimEngine.inputs_(::MRSiblingSourceModel) = NamedTuple() -PlantSimEngine.outputs_(::MRSiblingSourceModel) = (Z=-Inf,) -function PlantSimEngine.run!(::MRSiblingSourceModel, models, status, meteo, constants=nothing, extra=nothing) - status.Z = 22.0 -end - -PlantSimEngine.@process "mrclocksource" verbose = false -struct MRClockSourceModel <: AbstractMrclocksourceModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::MRClockSourceModel) = NamedTuple() -PlantSimEngine.outputs_(::MRClockSourceModel) = (X=-Inf,) -function PlantSimEngine.run!(m::MRClockSourceModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.X = float(m.n[]) -end - -PlantSimEngine.@process "mrclockconsumer" verbose = false -struct MRClockConsumerModel <: AbstractMrclockconsumerModel end -PlantSimEngine.inputs_(::MRClockConsumerModel) = (X=-Inf,) -PlantSimEngine.outputs_(::MRClockConsumerModel) = (Y=-Inf,) -function PlantSimEngine.run!(::MRClockConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.Y = status.X -end -PlantSimEngine.timespec(::Type{<:MRClockConsumerModel}) = ClockSpec(2.0, 1.0) - -PlantSimEngine.@process "mrcrosssource" verbose = false -struct MRCrossSourceModel <: AbstractMrcrosssourceModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::MRCrossSourceModel) = NamedTuple() -PlantSimEngine.outputs_(::MRCrossSourceModel) = (XS=-Inf,) -function PlantSimEngine.run!(m::MRCrossSourceModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.XS = float(m.n[]) -end - -PlantSimEngine.@process "mrcrossconsumer" verbose = false -struct MRCrossConsumerModel <: AbstractMrcrossconsumerModel end -PlantSimEngine.inputs_(::MRCrossConsumerModel) = (XS=-Inf,) -PlantSimEngine.outputs_(::MRCrossConsumerModel) = (XP=-Inf,) -function PlantSimEngine.run!(::MRCrossConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.XP = sum(status.XS) -end - -PlantSimEngine.@process "mrinterpsource" verbose = false -struct MRInterpSourceModel <: AbstractMrinterpsourceModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::MRInterpSourceModel) = NamedTuple() -PlantSimEngine.outputs_(::MRInterpSourceModel) = (XI=-Inf,) -function PlantSimEngine.run!(m::MRInterpSourceModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.XI = 2.0 * m.n[] - 1.0 -end - -PlantSimEngine.@process "mrinterpconsumer" verbose = false -struct MRInterpConsumerModel <: AbstractMrinterpconsumerModel end -PlantSimEngine.inputs_(::MRInterpConsumerModel) = (XI=-Inf,) -PlantSimEngine.outputs_(::MRInterpConsumerModel) = (YI=-Inf,) -function PlantSimEngine.run!(::MRInterpConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.YI = status.XI -end - -PlantSimEngine.@process "mraggsource" verbose = false -struct MRAggSourceModel <: AbstractMraggsourceModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::MRAggSourceModel) = NamedTuple() -PlantSimEngine.outputs_(::MRAggSourceModel) = (XA=-Inf,) -function PlantSimEngine.run!(m::MRAggSourceModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.XA = float(m.n[]) -end - -PlantSimEngine.@process "mraggconsumer" verbose = false -struct MRAggConsumerModel <: AbstractMraggconsumerModel end -PlantSimEngine.inputs_(::MRAggConsumerModel) = (XA=-Inf,) -PlantSimEngine.outputs_(::MRAggConsumerModel) = (YA=-Inf,) -function PlantSimEngine.run!(::MRAggConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.YA = status.XA -end - -PlantSimEngine.@process "mrtraitaggsource" verbose = false -struct MRTraitAggSourceModel <: AbstractMrtraitaggsourceModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::MRTraitAggSourceModel) = NamedTuple() -PlantSimEngine.outputs_(::MRTraitAggSourceModel) = (XT=-Inf,) -function PlantSimEngine.run!(m::MRTraitAggSourceModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.XT = float(m.n[]) -end -PlantSimEngine.output_policy(::Type{<:MRTraitAggSourceModel}) = (XT=Aggregate(),) - -PlantSimEngine.@process "mrtraitaggconsumer" verbose = false -struct MRTraitAggConsumerModel <: AbstractMrtraitaggconsumerModel end -PlantSimEngine.inputs_(::MRTraitAggConsumerModel) = (XT=-Inf,) -PlantSimEngine.outputs_(::MRTraitAggConsumerModel) = (YT=-Inf,) -function PlantSimEngine.run!(::MRTraitAggConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.YT = status.XT -end - -PlantSimEngine.@process "mrtraitdualaggsource" verbose = false -struct MRTraitDualAggSourceModel <: AbstractMrtraitdualaggsourceModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::MRTraitDualAggSourceModel) = NamedTuple() -PlantSimEngine.outputs_(::MRTraitDualAggSourceModel) = (V1=-Inf, V2=-Inf) -function PlantSimEngine.run!(m::MRTraitDualAggSourceModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.V1 = float(m.n[]) - status.V2 = 100.0 + float(m.n[]) -end -PlantSimEngine.output_policy(::Type{<:MRTraitDualAggSourceModel}) = (V1=Aggregate(), V2=Aggregate()) - -PlantSimEngine.@process "mrusesvconsumer" verbose = false -struct MRUsesV1ConsumerModel <: AbstractMrusesvconsumerModel end -PlantSimEngine.inputs_(::MRUsesV1ConsumerModel) = (V1=-Inf,) -PlantSimEngine.outputs_(::MRUsesV1ConsumerModel) = (YV1=-Inf,) -function PlantSimEngine.run!(::MRUsesV1ConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.YV1 = status.V1 -end - -PlantSimEngine.@process "mrdualpolicyconsumer" verbose = false -struct MRDualPolicyConsumerModel <: AbstractMrdualpolicyconsumerModel end -PlantSimEngine.inputs_(::MRDualPolicyConsumerModel) = (V1=-Inf, V2=-Inf, V1_max=-Inf) -PlantSimEngine.outputs_(::MRDualPolicyConsumerModel) = (YV1=-Inf, YV2=-Inf, YV1_max=-Inf) -function PlantSimEngine.run!(::MRDualPolicyConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.YV1 = status.V1 - status.YV2 = status.V2 - status.YV1_max = status.V1_max -end - -PlantSimEngine.@process "mrdailysource" verbose = false -struct MRDailySourceModel <: AbstractMrdailysourceModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::MRDailySourceModel) = NamedTuple() -PlantSimEngine.outputs_(::MRDailySourceModel) = (XD=-Inf,) -function PlantSimEngine.run!(m::MRDailySourceModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.XD = float(m.n[]) -end - -PlantSimEngine.@process "mrhourlyfromdailyconsumer" verbose = false -struct MRHourlyFromDailyConsumerModel <: AbstractMrhourlyfromdailyconsumerModel end -PlantSimEngine.inputs_(::MRHourlyFromDailyConsumerModel) = (XD=-Inf,) -PlantSimEngine.outputs_(::MRHourlyFromDailyConsumerModel) = (YD=-Inf,) -function PlantSimEngine.run!(::MRHourlyFromDailyConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.YD = status.XD -end - -PlantSimEngine.@process "mrzconsumer" verbose = false -struct MRZConsumerModel <: AbstractMrzconsumerModel end -PlantSimEngine.inputs_(::MRZConsumerModel) = (Z=-Inf,) -PlantSimEngine.outputs_(::MRZConsumerModel) = (ZZ=-Inf,) -function PlantSimEngine.run!(::MRZConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.ZZ = status.Z -end - -PlantSimEngine.@process "mrmultiscaletsource" verbose = false -struct MRMultiScaleTSourceModel <: AbstractMrmultiscaletsourceModel - tt::Float64 -end -PlantSimEngine.inputs_(::MRMultiScaleTSourceModel) = NamedTuple() -PlantSimEngine.outputs_(::MRMultiScaleTSourceModel) = (TT=-Inf,) -function PlantSimEngine.run!(m::MRMultiScaleTSourceModel, models, status, meteo, constants=nothing, extra=nothing) - status.TT = m.tt -end - -PlantSimEngine.@process "mrleafttconsumer" verbose = false -struct MRLeafTTConsumerModel <: AbstractMrleafttconsumerModel end -PlantSimEngine.inputs_(::MRLeafTTConsumerModel) = (TT=-Inf,) -PlantSimEngine.outputs_(::MRLeafTTConsumerModel) = (TT_used=-Inf,) -function PlantSimEngine.run!(::MRLeafTTConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.TT_used = status.TT -end - -PlantSimEngine.@process "mrmissinginputconsumer" verbose = false -struct MRMissingInputConsumerModel <: AbstractMrmissinginputconsumerModel end -PlantSimEngine.inputs_(::MRMissingInputConsumerModel) = (U=-Inf,) -PlantSimEngine.outputs_(::MRMissingInputConsumerModel) = (OU=-Inf,) -function PlantSimEngine.run!(::MRMissingInputConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.OU = status.U -end - -PlantSimEngine.@process "mrhardchild" verbose = false -struct MRHardChildModel <: AbstractMrhardchildModel end -PlantSimEngine.inputs_(::MRHardChildModel) = NamedTuple() -PlantSimEngine.outputs_(::MRHardChildModel) = (A=-Inf,) -function PlantSimEngine.run!(::MRHardChildModel, models, status, meteo, constants=nothing, extra=nothing) - status.A = 1.0 -end - -PlantSimEngine.@process "mrhardparent" verbose = false -struct MRHardParentModel <: AbstractMrhardparentModel end -PlantSimEngine.dep(::MRHardParentModel) = (mrhardchild=AbstractMrhardchildModel,) -PlantSimEngine.inputs_(::MRHardParentModel) = NamedTuple() -PlantSimEngine.outputs_(::MRHardParentModel) = (A=-Inf,) -function PlantSimEngine.run!(::MRHardParentModel, models, status, meteo, constants=nothing, extra=nothing) - PlantSimEngine.run!(models.mrhardchild, models, status, meteo, constants, extra) - status.A = 5.0 -end - -PlantSimEngine.@process "mrhardconsumer" verbose = false -struct MRHardConsumerModel <: AbstractMrhardconsumerModel end -PlantSimEngine.inputs_(::MRHardConsumerModel) = (A=-Inf,) -PlantSimEngine.outputs_(::MRHardConsumerModel) = (B=-Inf,) -function PlantSimEngine.run!(::MRHardConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.B = status.A -end - -PlantSimEngine.@process "mrmeteodailyconsumer" verbose = false -struct MRMeteoDailyConsumerModel <: AbstractMrmeteodailyconsumerModel end -PlantSimEngine.inputs_(::MRMeteoDailyConsumerModel) = NamedTuple() -PlantSimEngine.outputs_(::MRMeteoDailyConsumerModel) = (MT=-Inf, MTmin=-Inf, MTmax=-Inf, MRh=-Inf, MSW=-Inf, MSWq=-Inf) -function PlantSimEngine.run!(::MRMeteoDailyConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.MT = meteo.T - status.MTmin = meteo.Tmin - status.MTmax = meteo.Tmax - status.MRh = meteo.Rh - status.MSW = meteo.Ri_SW_f - status.MSWq = meteo.Ri_SW_q -end - -PlantSimEngine.@process "mrmeteocustomconsumer" verbose = false -struct MRMeteoCustomConsumerModel <: AbstractMrmeteocustomconsumerModel end -PlantSimEngine.inputs_(::MRMeteoCustomConsumerModel) = NamedTuple() -PlantSimEngine.outputs_(::MRMeteoCustomConsumerModel) = (MRQ=-Inf, MCV=-Inf) -function PlantSimEngine.run!(::MRMeteoCustomConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.MRQ = meteo.Ri_SW_f - status.MCV = meteo.custom_peak -end - -PlantSimEngine.@process "mrrangehinta" verbose = false -struct MRRangeHintAModel <: AbstractMrrangehintaModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::MRRangeHintAModel) = NamedTuple() -PlantSimEngine.outputs_(::MRRangeHintAModel) = (XA=-Inf,) -function PlantSimEngine.run!(m::MRRangeHintAModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.XA = float(m.n[]) -end -PlantSimEngine.timestep_hint(::Type{<:MRRangeHintAModel}) = (; required=(Dates.Hour(1), Dates.Hour(4)), preferred=Dates.Hour(3)) - -PlantSimEngine.@process "mrrangehintb" verbose = false -struct MRRangeHintBModel <: AbstractMrrangehintbModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::MRRangeHintBModel) = NamedTuple() -PlantSimEngine.outputs_(::MRRangeHintBModel) = (XB=-Inf,) -function PlantSimEngine.run!(m::MRRangeHintBModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.XB = float(m.n[]) -end -PlantSimEngine.timestep_hint(::Type{<:MRRangeHintBModel}) = (; required=(Dates.Hour(1), Dates.Hour(6)), preferred=Dates.Hour(4)) - -PlantSimEngine.@process "mrrangehintforced" verbose = false -struct MRRangeHintForcedModel <: AbstractMrrangehintforcedModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::MRRangeHintForcedModel) = NamedTuple() -PlantSimEngine.outputs_(::MRRangeHintForcedModel) = (XF=-Inf,) -function PlantSimEngine.run!(m::MRRangeHintForcedModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.XF = float(m.n[]) -end -PlantSimEngine.timestep_hint(::Type{<:MRRangeHintForcedModel}) = (Dates.Hour(3), Dates.Hour(6)) - -PlantSimEngine.@process "mrrangehintstrict" verbose = false -struct MRRangeHintStrictModel <: AbstractMrrangehintstrictModel - n::Base.RefValue{Int} -end -PlantSimEngine.inputs_(::MRRangeHintStrictModel) = NamedTuple() -PlantSimEngine.outputs_(::MRRangeHintStrictModel) = (XS=-Inf,) -function PlantSimEngine.run!(m::MRRangeHintStrictModel, models, status, meteo, constants=nothing, extra=nothing) - m.n[] += 1 - status.XS = float(m.n[]) -end -PlantSimEngine.timestep_hint(::Type{<:MRRangeHintStrictModel}) = (Dates.Hour(2), Dates.Hour(4)) - -PlantSimEngine.@process "mrmeteohintconsumer" verbose = false -struct MRMeteoHintConsumerModel <: AbstractMrmeteohintconsumerModel end -PlantSimEngine.inputs_(::MRMeteoHintConsumerModel) = NamedTuple() -PlantSimEngine.outputs_(::MRMeteoHintConsumerModel) = (HT=-Inf, HSWQ=-Inf) -function PlantSimEngine.run!(::MRMeteoHintConsumerModel, models, status, meteo, constants=nothing, extra=nothing) - status.HT = meteo.T - status.HSWQ = meteo.Ri_SW_q -end -PlantSimEngine.timestep_hint(::Type{<:MRMeteoHintConsumerModel}) = Dates.Day(1) -PlantSimEngine.meteo_hint(::Type{<:MRMeteoHintConsumerModel}) = ( - bindings=( - T=(source=:T, reducer=MaxReducer()), - Ri_SW_q=(source=:Ri_SW_f, reducer=RadiationEnergy()), - ), - window=CalendarWindow(:day; anchor=:current_period, week_start=1, completeness=:allow_partial), -) - -@testset "Multi-rate runtime: HoldLast and conflict validation" begin - mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant = Node(mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - Node(mtg, MultiScaleTreeGraph.NodeMTG("+", :Soil, 1, 1)) - internode = Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - Node(internode, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - - mapping_ok = PlantSimEngine.ModelMapping( - :Leaf => ( - MRSourceModel(), - ModelSpec(MROverwriteModel()) |> OutputRouting(; C=:stream_only), - ModelSpec(MRConsumerModel()) |> - PlantSimEngine.InputBindings(; C=(process=:mrsource, var=:S)), - ModelSpec(MRDirectConsumerModel()) |> - PlantSimEngine.InputBindings(; S=(process=:mrsource, var=:S)), - MRAutoSameNameModel(), - ), - ) - - out_ok = Dict(:Leaf => (:S, :C, :B, :D, :E)) - sim_ok = PlantSimEngine.GraphSimulation(mtg, mapping_ok, nsteps=1, check=true, outputs=out_ok) - meteo = Atmosphere(T=20.0, Wind=1.0, Rh=0.65) - run!(sim_ok, meteo, executor=SequentialEx()) - - specs_leaf = PlantSimEngine.get_model_specs(sim_ok)[:Leaf] - @test input_bindings(specs_leaf[:mrconsumer]).C.var == :S - @test input_bindings(specs_leaf[:mrconsumer]).C.policy isa HoldLast - @test output_routing(specs_leaf[:mroverwrite]).C == :stream_only - @test input_bindings(specs_leaf[:mrautosamename]).S.process == :mrsource - @test input_bindings(specs_leaf[:mrautosamename]).S.var == :S - - st_leaf = status(sim_ok)[:Leaf][1] - # Expectation 1: consumer :C input is remapped from mrsource/:S via mapping-level InputBindings. - @test st_leaf.C == 10.0 - @test st_leaf.B == 10.0 - # Expectation 2: direct same-name binding (:S -> :S) also resolves and is visible in :D. - @test st_leaf.D == 10.0 - # Expectation 3: with no input_bindings method, same-name input :S is auto-resolved. - @test st_leaf.E == 10.0 - nid = MultiScaleTreeGraph.node_id(st_leaf.node) - scope = ScopeId(:global, 1) - key_src = OutputKey(scope, :Leaf, nid, :mrsource, :S) - key_ovr = OutputKey(scope, :Leaf, nid, :mroverwrite, :C) - key_dir = OutputKey(scope, :Leaf, nid, :mrdirectconsumer, :D) - key_auto = OutputKey(scope, :Leaf, nid, :mrautosamename, :E) - # Expectation 4: temporal stream caches track producer outputs (including stream-only outputs). - @test haskey(sim_ok.temporal_state.caches, key_src) - @test haskey(sim_ok.temporal_state.caches, key_ovr) - @test haskey(sim_ok.temporal_state.caches, key_dir) - @test haskey(sim_ok.temporal_state.caches, key_auto) - @test sim_ok.temporal_state.caches[key_src].v == 10.0 - @test sim_ok.temporal_state.caches[key_ovr].v == -999.0 - @test sim_ok.temporal_state.caches[key_dir].v == 10.0 - @test sim_ok.temporal_state.caches[key_auto].v == 10.0 - - mapping_renamed_auto = PlantSimEngine.ModelMapping( - :Leaf => MRSourceModel(), - :Plant => ( - ModelSpec(MRConsumerModel()) |> - PlantSimEngine.MultiScaleModel([:C => (:Leaf => :S)]) |> - TimeStep(ClockSpec(1.0, 0.0)), - ), - ) - sim_renamed_auto = PlantSimEngine.GraphSimulation(mtg, mapping_renamed_auto, nsteps=1, check=true) - consumer_node = only(filter( - n -> n.scale == :Plant && n.process == :mrconsumer, - PlantSimEngine.traverse_dependency_graph(dep(sim_renamed_auto), false), - )) - @test PlantSimEngine._candidate_producers(consumer_node, :C) == [(:mrsource, :S)] - - mapping_conflict = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRConflict1Model()) |> TimeStep(1.0), - ModelSpec(MRConflict2Model()) |> TimeStep(1.0), - ), - ) - # Expectation 5: two canonical writers of the same output are rejected at graph construction. - @test_throws "Ambiguous canonical writers" PlantSimEngine.GraphSimulation( - mtg, - mapping_conflict, - nsteps=1, - check=true, - outputs=Dict(:Leaf => (:Z,)) - ) - - # Expectation 6: models run at different clocks; slower model holds last value between runs. - source_counter = Ref(0) - mapping_clock_trait = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRClockSourceModel(source_counter)) |> TimeStep(1.0), - ModelSpec(MRClockConsumerModel()) |> - PlantSimEngine.InputBindings(; X=(process=:mrclocksource, var=:X)), - ), - ) - sim_clock_trait = PlantSimEngine.GraphSimulation(mtg, mapping_clock_trait, nsteps=4, check=true, outputs=Dict(:Leaf => (:X, :Y))) - meteo4 = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65)], 4)) - run!(sim_clock_trait, meteo4, executor=SequentialEx()) - source_counter[] = 0 - out_status_auto, out_requested_auto = run!( - mtg, - mapping_clock_trait, - meteo4; - executor=SequentialEx(), - tracked_outputs=[OutputRequest(:Leaf, :X; process=:mrclocksource, policy=HoldLast(), name=:x_auto)], - return_requested_outputs=true, - ) - @test haskey(out_status_auto, :Leaf) - @test out_requested_auto[:x_auto][:, :value] == [1.0, 2.0, 3.0, 4.0] - st_clock = status(sim_clock_trait)[:Leaf][1] - @test st_clock.X == 4.0 - @test st_clock.Y == 3.0 - scope = ScopeId(:global, 1) - @test sim_clock_trait.temporal_state.last_run[ModelKey(scope, :Leaf, :mrclocksource)] == 4.0 - @test sim_clock_trait.temporal_state.last_run[ModelKey(scope, :Leaf, :mrclockconsumer)] == 3.0 - - # Expectation 7: TimeStep override takes precedence over model timespec. - source_counter_2 = Ref(0) - mapping_clock_override = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRClockSourceModel(source_counter_2)) |> TimeStep(1.0), - ModelSpec(MRClockConsumerModel()) |> - TimeStep(3.0) |> - PlantSimEngine.InputBindings(; X=(process=:mrclocksource, var=:X)), - ), - ) - sim_clock_override = PlantSimEngine.GraphSimulation(mtg, mapping_clock_override, nsteps=4, check=true, outputs=Dict(:Leaf => (:X, :Y))) - run!(sim_clock_override, meteo4, executor=SequentialEx()) - st_clock_override = status(sim_clock_override)[:Leaf][1] - @test st_clock_override.X == 4.0 - @test st_clock_override.Y == 3.0 - @test sim_clock_override.temporal_state.last_run[ModelKey(scope, :Leaf, :mrclocksource)] == 4.0 - @test sim_clock_override.temporal_state.last_run[ModelKey(scope, :Leaf, :mrclockconsumer)] == 3.0 - - # Expectation 7b: non-sequential executors warn and fall back to sequential behavior. - mapping_clock_fallback_seq = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRClockSourceModel(Ref(0))) |> TimeStep(1.0), - ModelSpec(MRClockConsumerModel()) |> - PlantSimEngine.InputBindings(; X=(process=:mrclocksource, var=:X)), - ), - ) - sim_clock_fallback_seq = PlantSimEngine.GraphSimulation(mtg, mapping_clock_fallback_seq, nsteps=4, check=true, outputs=Dict(:Leaf => (:X, :Y))) - out_fallback_seq = run!(sim_clock_fallback_seq, meteo4, executor=SequentialEx()) - out_fallback_seq_df = convert_outputs(out_fallback_seq, DataFrame) - - mapping_clock_fallback_threaded = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRClockSourceModel(Ref(0))) |> TimeStep(1.0), - ModelSpec(MRClockConsumerModel()) |> - PlantSimEngine.InputBindings(; X=(process=:mrclocksource, var=:X)), - ), - ) - sim_clock_fallback_threaded = PlantSimEngine.GraphSimulation(mtg, mapping_clock_fallback_threaded, nsteps=4, check=true, outputs=Dict(:Leaf => (:X, :Y))) - @test_logs (:warn, r"Multi-rate MTG runs currently execute sequentially") begin - out_fallback_threaded = run!(sim_clock_fallback_threaded, meteo4, executor=ThreadedEx()) - out_fallback_threaded_df = convert_outputs(out_fallback_threaded, DataFrame) - @test out_fallback_threaded_df[:Leaf][:, :X] == out_fallback_seq_df[:Leaf][:, :X] - @test out_fallback_threaded_df[:Leaf][:, :Y] == out_fallback_seq_df[:Leaf][:, :Y] - end - - # Expectation 8: cross-scale hold-last resolution works with different clocks. - # Leaf producer runs each step; Plant consumer runs every 2 steps (1, 3) and reads Leaf XS through multiscale mapping. - source_counter_3 = Ref(0) - mapping_cross = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRCrossSourceModel(source_counter_3)) |> TimeStep(1.0), - ), - :Plant => ( - ModelSpec(MRCrossConsumerModel()) |> - PlantSimEngine.MultiScaleModel([:XS => [:Leaf]]) |> - TimeStep(ClockSpec(2.0, 1.0)) |> - PlantSimEngine.InputBindings(; XS=(process=:mrcrosssource, var=:XS, scale=:Leaf)), - ), - ) - sim_cross = PlantSimEngine.GraphSimulation(mtg, mapping_cross, nsteps=4, check=true, outputs=Dict(:Leaf => (:XS,), :Plant => (:XP,))) - run!(sim_cross, meteo4, executor=SequentialEx()) - st_leaf_cross = status(sim_cross)[:Leaf][1] - st_plant_cross = status(sim_cross)[:Plant][1] - @test st_leaf_cross.XS == 4.0 - @test st_plant_cross.XP == 3.0 - - # Expectation 8a: cross-scale producer is inferred automatically when unique. - source_counter_3_auto = Ref(0) - mapping_cross_auto = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRCrossSourceModel(source_counter_3_auto)) |> TimeStep(1.0), - ), - :Plant => ( - ModelSpec(MRCrossConsumerModel()) |> - PlantSimEngine.MultiScaleModel([:XS => [:Leaf]]) |> - TimeStep(ClockSpec(2.0, 1.0)), - ), - ) - sim_cross_auto = PlantSimEngine.GraphSimulation(mtg, mapping_cross_auto, nsteps=4, check=true, outputs=Dict(:Leaf => (:XS,), :Plant => (:XP,))) - run!(sim_cross_auto, meteo4, executor=SequentialEx()) - st_plant_cross_auto = status(sim_cross_auto)[:Plant][1] - @test st_plant_cross_auto.XP == 3.0 - spec_cross_auto = PlantSimEngine.get_model_specs(sim_cross_auto)[:Plant][:mrcrossconsumer] - @test input_bindings(spec_cross_auto).XS.process == :mrcrosssource - @test input_bindings(spec_cross_auto).XS.scale == :Leaf - - # Expectation 8b: scope partitioning isolates producer streams between plants. - scene2 = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant2_a = Node(scene2, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - plant2_b = Node(scene2, MultiScaleTreeGraph.NodeMTG("+", :Plant, 2, 1)) - internode2_a = Node(plant2_a, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - internode2_b = Node(plant2_b, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - Node(internode2_a, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - Node(internode2_b, MultiScaleTreeGraph.NodeMTG("+", :Leaf, 1, 2)) - - source_counter_scoped = Ref(0) - mapping_scoped = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRCrossSourceModel(source_counter_scoped)) |> TimeStep(1.0) |> PlantSimEngine.ScopeModel(:plant), - ), - :Plant => ( - ModelSpec(MRCrossConsumerModel()) |> - PlantSimEngine.MultiScaleModel([:XS => [:Leaf]]) |> - TimeStep(1.0) |> - PlantSimEngine.ScopeModel(:plant) |> - PlantSimEngine.InputBindings(; XS=(process=:mrcrosssource, var=:XS, scale=:Leaf)), - ), - ) - sim_scoped = PlantSimEngine.GraphSimulation(scene2, mapping_scoped, nsteps=1, check=true, outputs=Dict(:Plant => (:XP,), :Leaf => (:XS,))) - run!(sim_scoped, meteo, executor=SequentialEx()) - plant_vals = sort([st.XP for st in status(sim_scoped)[:Plant]]) - @test plant_vals == [1.0, 2.0] - - function plant_ancestor_id(node) - current = node - while !isnothing(current) && symbol(current) != :Plant - current = parent(current) - end - isnothing(current) && error("Expected a Plant ancestor in scoped test tree.") - return node_id(current) - end - - leaf_scoped_statuses = status(sim_scoped)[:Leaf] - leaf_scoped_keys = [ - OutputKey(ScopeId(:plant, plant_ancestor_id(st.node)), :Leaf, node_id(st.node), :mrcrosssource, :XS) - for st in leaf_scoped_statuses - ] - @test all(k -> haskey(sim_scoped.temporal_state.caches, k), leaf_scoped_keys) - - # Expectation 9: Interpolate policy resolves a slower producer for a faster consumer. - # Source runs at t=1,3,5 with values 1,3,5. - # Consumer runs every step and receives XI through Interpolate: - # expected YI over time is [1, 1, 3, 4, 5]. - interp_counter = Ref(0) - mapping_interp = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRInterpSourceModel(interp_counter)) |> TimeStep(ClockSpec(2.0, 1.0)), - ModelSpec(MRInterpConsumerModel()) |> - TimeStep(1.0) |> - PlantSimEngine.InputBindings(; XI=(process=:mrinterpsource, var=:XI, policy=Interpolate())), - ), - ) - meteo5 = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65)], 5)) - sim_interp = PlantSimEngine.GraphSimulation(mtg, mapping_interp, nsteps=5, check=true, outputs=Dict(:Leaf => (:YI,))) - out_interp = run!(sim_interp, meteo5, executor=SequentialEx()) - out_interp_df = convert_outputs(out_interp, DataFrame) - @test out_interp_df[:Leaf][:, :YI] == [1.0, 1.0, 3.0, 4.0, 5.0] - - # Expectation 10: Aggregate policy computes mean over the consumer window. - # Source runs every step with XA=[1,2,3,4]. - # Consumer runs on t=1,3 (ClockSpec(2,1)): - # - at t=1: window [0,1] => mean([1]) = 1 - # - at t=3: window [2,3] => mean([2,3]) = 2.5 - # Output YA over time is therefore [1, 1, 2.5, 2.5]. - agg_counter = Ref(0) - mapping_agg = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRAggSourceModel(agg_counter)) |> TimeStep(1.0), - ModelSpec(MRAggConsumerModel()) |> - TimeStep(ClockSpec(2.0, 1.0)) |> - PlantSimEngine.InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Aggregate())), - ), - ) - sim_agg = PlantSimEngine.GraphSimulation(mtg, mapping_agg, nsteps=4, check=true, outputs=Dict(:Leaf => (:YA,))) - out_agg = run!(sim_agg, meteo4, executor=SequentialEx()) - out_agg_df = convert_outputs(out_agg, DataFrame) - @test out_agg_df[:Leaf][:, :YA] == [1.0, 1.0, 2.5, 2.5] - @test status(sim_agg)[:Leaf][1].YA == 2.5 - nid_agg = node_id(status(sim_agg)[:Leaf][1].node) - key_agg = OutputKey(scope, :Leaf, nid_agg, :mraggsource, :XA) - @test haskey(sim_agg.temporal_state.streams, key_agg) - @test length(sim_agg.temporal_state.streams[key_agg]) <= 2 - @test sim_agg.temporal_state.producer_horizons[(:Leaf, :mraggsource, :XA)] == 2.0 - - # Expectation 10a: duration-aware reducers receive per-sample durations (seconds). - # Using hourly meteo rows, RadiationEnergy integrates XA values as value * 3600 s. - meteo4_hourly = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65, duration=Dates.Hour(1))], 4)) - agg_counter_duration = Ref(0) - mapping_agg_duration = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRAggSourceModel(agg_counter_duration)) |> TimeStep(1.0), - ModelSpec(MRAggConsumerModel()) |> - TimeStep(ClockSpec(2.0, 1.0)) |> - PlantSimEngine.InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Integrate(RadiationEnergy()))), - ), - ) - sim_agg_duration = PlantSimEngine.GraphSimulation(mtg, mapping_agg_duration, nsteps=4, check=true, outputs=Dict(:Leaf => (:YA,))) - out_agg_duration = run!(sim_agg_duration, meteo4_hourly, executor=SequentialEx()) - out_agg_duration_df = convert_outputs(out_agg_duration, DataFrame) - @test isapprox.(out_agg_duration_df[:Leaf][:, :YA], [0.0036, 0.0036, 0.018, 0.018], atol=1.0e-9) |> all - - # Expectation 10aa: custom two-argument reducers can use durations directly. - agg_counter_duration_callable = Ref(0) - mapping_agg_duration_callable = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRAggSourceModel(agg_counter_duration_callable)) |> TimeStep(1.0), - ModelSpec(MRAggConsumerModel()) |> - TimeStep(ClockSpec(2.0, 1.0)) |> - PlantSimEngine.InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Integrate((vals, durations) -> sum(vals .* durations)))), - ), - ) - sim_agg_duration_callable = PlantSimEngine.GraphSimulation( - mtg, - mapping_agg_duration_callable, - nsteps=4, - check=true, - outputs=Dict(:Leaf => (:YA,)) - ) - out_agg_duration_callable = run!(sim_agg_duration_callable, meteo4_hourly, executor=SequentialEx()) - out_agg_duration_callable_df = convert_outputs(out_agg_duration_callable, DataFrame) - @test out_agg_duration_callable_df[:Leaf][:, :YA] == [3600.0, 3600.0, 18000.0, 18000.0] - - # Expectation 10b: inferred bindings use producer output_policy by default. - # Source XT=[1,2,3,4], consumer runs at t=1,3 and has no explicit InputBindings. - # output_policy(XT=Aggregate()) drives YT to [1,1,2.5,2.5]. - trait_counter = Ref(0) - mapping_trait_default = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRTraitAggSourceModel(trait_counter)) |> TimeStep(1.0), - ModelSpec(MRTraitAggConsumerModel()) |> TimeStep(ClockSpec(2.0, 1.0)), - ), - ) - sim_trait_default = PlantSimEngine.GraphSimulation(mtg, mapping_trait_default, nsteps=4, check=true, outputs=Dict(:Leaf => (:YT,))) - out_trait_default = run!(sim_trait_default, meteo4, executor=SequentialEx()) - out_trait_default_df = convert_outputs(out_trait_default, DataFrame) - @test out_trait_default_df[:Leaf][:, :YT] == [1.0, 1.0, 2.5, 2.5] - @test input_bindings(PlantSimEngine.get_model_specs(sim_trait_default)[:Leaf][:mrtraitaggconsumer]).XT.policy isa Aggregate - - # Expectation 10bb: output_policy is consumed lazily per bound variable. - # Here source policy declares V1 and V2 as Aggregate, but only V1 is consumed. - # Runtime must allocate/integrate only V1 (no stream/horizon for V2). - dual_trait_counter = Ref(0) - mapping_trait_partial_use = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRTraitDualAggSourceModel(dual_trait_counter)) |> TimeStep(1.0), - ModelSpec(MRUsesV1ConsumerModel()) |> TimeStep(ClockSpec(2.0, 1.0)), - ), - ) - sim_trait_partial_use = PlantSimEngine.GraphSimulation(mtg, mapping_trait_partial_use, nsteps=4, check=true, outputs=Dict(:Leaf => (:YV1,))) - out_trait_partial_use = run!(sim_trait_partial_use, meteo4, executor=SequentialEx()) - out_trait_partial_use_df = convert_outputs(out_trait_partial_use, DataFrame) - @test out_trait_partial_use_df[:Leaf][:, :YV1] == [1.0, 1.0, 2.5, 2.5] - spec_trait_partial_use = PlantSimEngine.get_model_specs(sim_trait_partial_use)[:Leaf][:mrusesvconsumer] - @test input_bindings(spec_trait_partial_use).V1.policy isa Aggregate - @test sim_trait_partial_use.temporal_state.producer_horizons[(:Leaf, :mrtraitdualaggsource, :V1)] == 2.0 - @test !haskey(sim_trait_partial_use.temporal_state.producer_horizons, (:Leaf, :mrtraitdualaggsource, :V2)) - nid_trait_partial_use = node_id(status(sim_trait_partial_use)[:Leaf][1].node) - key_trait_v1 = OutputKey(scope, :Leaf, nid_trait_partial_use, :mrtraitdualaggsource, :V1) - key_trait_v2 = OutputKey(scope, :Leaf, nid_trait_partial_use, :mrtraitdualaggsource, :V2) - @test haskey(sim_trait_partial_use.temporal_state.streams, key_trait_v1) - @test !haskey(sim_trait_partial_use.temporal_state.streams, key_trait_v2) - - # Expectation 10bc: user bindings can override and complement trait policies. - # Trait provides Aggregate for V1 and V2. - # User overrides V1 to Integrate and adds V1_max as Aggregate(MaxReducer()). - dual_trait_counter_priority = Ref(0) - mapping_trait_user_priority = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRTraitDualAggSourceModel(dual_trait_counter_priority)) |> TimeStep(1.0), - ModelSpec(MRDualPolicyConsumerModel()) |> - TimeStep(ClockSpec(2.0, 1.0)) |> - PlantSimEngine.InputBindings( - ; - V1=(process=:mrtraitdualaggsource, var=:V1, policy=Integrate()), - V1_max=(process=:mrtraitdualaggsource, var=:V1, policy=Aggregate(MaxReducer())), - ), - Status(V1_max=0.0), - ), - ) - sim_trait_user_priority = PlantSimEngine.GraphSimulation( - mtg, - mapping_trait_user_priority, - nsteps=4, - check=true, - outputs=Dict(:Leaf => (:YV1, :YV2, :YV1_max)) - ) - out_trait_user_priority = run!(sim_trait_user_priority, meteo4, executor=SequentialEx()) - out_trait_user_priority_df = convert_outputs(out_trait_user_priority, DataFrame) - @test out_trait_user_priority_df[:Leaf][:, :YV1] == [1.0, 1.0, 5.0, 5.0] - @test out_trait_user_priority_df[:Leaf][:, :YV2] == [101.0, 101.0, 102.5, 102.5] - @test out_trait_user_priority_df[:Leaf][:, :YV1_max] == [1.0, 1.0, 3.0, 3.0] - spec_trait_user_priority = PlantSimEngine.get_model_specs(sim_trait_user_priority)[:Leaf][:mrdualpolicyconsumer] - @test input_bindings(spec_trait_user_priority).V1.policy isa Integrate - @test input_bindings(spec_trait_user_priority).V2.policy isa Aggregate - @test input_bindings(spec_trait_user_priority).V1_max.policy isa Aggregate - @test input_bindings(spec_trait_user_priority).V1_max.policy.reducer isa MaxReducer - - # Expectation 10c: explicit InputBindings policy overrides producer output_policy. - trait_counter_override = Ref(0) - mapping_trait_override = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRTraitAggSourceModel(trait_counter_override)) |> TimeStep(1.0), - ModelSpec(MRTraitAggConsumerModel()) |> - TimeStep(ClockSpec(2.0, 1.0)) |> - PlantSimEngine.InputBindings(; XT=(process=:mrtraitaggsource, var=:XT, policy=Integrate())), - ), - ) - sim_trait_override = PlantSimEngine.GraphSimulation(mtg, mapping_trait_override, nsteps=4, check=true, outputs=Dict(:Leaf => (:YT,))) - out_trait_override = run!(sim_trait_override, meteo4, executor=SequentialEx()) - out_trait_override_df = convert_outputs(out_trait_override, DataFrame) - @test out_trait_override_df[:Leaf][:, :YT] == [1.0, 1.0, 5.0, 5.0] - - # Expectation 11: parameterized Aggregate reducer is applied per window. - # Source XA=[1,2,3,4], consumer runs at t=1,3 with reducer=MaxReducer(). - # YA over time is [1,1,3,3]. - agg_counter_max = Ref(0) - mapping_agg_max = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRAggSourceModel(agg_counter_max)) |> TimeStep(1.0), - ModelSpec(MRAggConsumerModel()) |> - TimeStep(ClockSpec(2.0, 1.0)) |> - PlantSimEngine.InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Aggregate(MaxReducer()))), - ), - ) - sim_agg_max = PlantSimEngine.GraphSimulation(mtg, mapping_agg_max, nsteps=4, check=true, outputs=Dict(:Leaf => (:YA,))) - out_agg_max = run!(sim_agg_max, meteo4, executor=SequentialEx()) - out_agg_max_df = convert_outputs(out_agg_max, DataFrame) - @test out_agg_max_df[:Leaf][:, :YA] == [1.0, 1.0, 3.0, 3.0] - - # Expectation 12: parameterized Integrate reducer (callable) is applied per window. - # Source XA=[1,2,3,4], consumer runs at t=1,3 with reducer=max-min. - # YA over time is [0,0,1,1]. - agg_counter_callable = Ref(0) - mapping_integrate_callable = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRAggSourceModel(agg_counter_callable)) |> TimeStep(1.0), - ModelSpec(MRAggConsumerModel()) |> - TimeStep(ClockSpec(2.0, 1.0)) |> - PlantSimEngine.InputBindings(; XA=(process=:mraggsource, var=:XA, policy=Integrate(vals -> maximum(vals) - minimum(vals)))), - ), - ) - sim_integrate_callable = PlantSimEngine.GraphSimulation(mtg, mapping_integrate_callable, nsteps=4, check=true, outputs=Dict(:Leaf => (:YA,))) - out_integrate_callable = run!(sim_integrate_callable, meteo4, executor=SequentialEx()) - out_integrate_callable_df = convert_outputs(out_integrate_callable, DataFrame) - @test out_integrate_callable_df[:Leaf][:, :YA] == [0.0, 0.0, 1.0, 1.0] - - # Expectation 13: Interpolate policy supports hold mode and hold extrapolation. - # Source runs at t=1,3,5 with values 1,3,5. Consumer runs each step. - # With Interpolate(mode=:hold, extrapolation=:hold), YI is [1,1,3,3,5,5]. - interp_counter_hold = Ref(0) - mapping_interp_hold = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRInterpSourceModel(interp_counter_hold)) |> TimeStep(ClockSpec(2.0, 1.0)), - ModelSpec(MRInterpConsumerModel()) |> - TimeStep(1.0) |> - PlantSimEngine.InputBindings(; XI=(process=:mrinterpsource, var=:XI, policy=Interpolate(; mode=:hold, extrapolation=:hold))), - ), - ) - meteo6 = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65)], 6)) - sim_interp_hold = PlantSimEngine.GraphSimulation(mtg, mapping_interp_hold, nsteps=6, check=true, outputs=Dict(:Leaf => (:YI,))) - out_interp_hold = run!(sim_interp_hold, meteo6, executor=SequentialEx()) - out_interp_hold_df = convert_outputs(out_interp_hold, DataFrame) - @test out_interp_hold_df[:Leaf][:, :YI] == [1.0, 1.0, 3.0, 3.0, 5.0, 5.0] - - # Expectation 14: daily producer to hourly consumer within same day uses hold-last. - # Source runs at t=1 and t=25 (ClockSpec(24,1)), consumer runs every step. - # YD should stay at 1 for t=1..24, then switch to 2 at t=25. - daily_counter = Ref(0) - mapping_daily_hourly = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRDailySourceModel(daily_counter)) |> TimeStep(ClockSpec(24.0, 1.0)), - ModelSpec(MRHourlyFromDailyConsumerModel()) |> - TimeStep(1.0) |> - PlantSimEngine.InputBindings(; XD=(process=:mrdailysource, var=:XD)), - ), - ) - meteo26 = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65)], 26)) - sim_daily_hourly = PlantSimEngine.GraphSimulation(mtg, mapping_daily_hourly, nsteps=26, check=true, outputs=Dict(:Leaf => (:YD,))) - out_daily_hourly = run!(sim_daily_hourly, meteo26, executor=SequentialEx()) - out_daily_hourly_df = convert_outputs(out_daily_hourly, DataFrame) - @test out_daily_hourly_df[:Leaf][1:24, :YD] == fill(1.0, 24) - @test out_daily_hourly_df[:Leaf][25:26, :YD] == [2.0, 2.0] - @test sim_daily_hourly.temporal_state.last_run[ModelKey(scope, :Leaf, :mrdailysource)] == 25.0 - @test sim_daily_hourly.temporal_state.last_run[ModelKey(scope, :Leaf, :mrhourlyfromdailyconsumer)] == 26.0 - - # Expectation 15: period-based timestep uses timeline base-step conversion. - # Meteo has duration=Hour(1), source uses Day(1) => runs on t=1 and t=25. - daily_counter_period = Ref(0) - mapping_daily_period = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRDailySourceModel(daily_counter_period)) |> TimeStep(Dates.Day(1)), - ModelSpec(MRHourlyFromDailyConsumerModel()) |> - TimeStep(1.0) |> - PlantSimEngine.InputBindings(; XD=(process=:mrdailysource, var=:XD)), - ), - ) - meteo_hourly = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65, duration=Dates.Hour(1))], 26)) - sim_daily_period = PlantSimEngine.GraphSimulation(mtg, mapping_daily_period, nsteps=26, check=true, outputs=Dict(:Leaf => (:YD,))) - out_daily_period = run!(sim_daily_period, meteo_hourly, executor=SequentialEx()) - out_daily_period_df = convert_outputs(out_daily_period, DataFrame) - @test out_daily_period_df[:Leaf][1:24, :YD] == fill(1.0, 24) - @test out_daily_period_df[:Leaf][25:26, :YD] == [2.0, 2.0] - @test sim_daily_period.temporal_state.last_run[ModelKey(scope, :Leaf, :mrdailysource)] == 25.0 - - # Expectation 15b: meteo duration can be a CompoundPeriod. - # With duration=CompoundPeriod(Minute(30)), Day(1) runs at t=1 and t=49. - daily_counter_compound = Ref(0) - mapping_daily_compound = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRDailySourceModel(daily_counter_compound)) |> TimeStep(Dates.Day(1)), - ModelSpec(MRHourlyFromDailyConsumerModel()) |> - TimeStep(1.0) |> - PlantSimEngine.InputBindings(; XD=(process=:mrdailysource, var=:XD)), - ), - ) - meteo_halfhourly = Weather( - repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65, duration=Dates.CompoundPeriod(Dates.Minute(30)))], 50) - ) - sim_daily_compound = PlantSimEngine.GraphSimulation(mtg, mapping_daily_compound, nsteps=50, check=true, outputs=Dict(:Leaf => (:YD,))) - out_daily_compound = run!(sim_daily_compound, meteo_halfhourly, executor=SequentialEx()) - out_daily_compound_df = convert_outputs(out_daily_compound, DataFrame) - @test out_daily_compound_df[:Leaf][1:48, :YD] == fill(1.0, 48) - @test out_daily_compound_df[:Leaf][49:50, :YD] == [2.0, 2.0] - @test sim_daily_compound.temporal_state.last_run[ModelKey(scope, :Leaf, :mrdailysource)] == 49.0 - - # Expectation 15c: missing meteo duration is rejected. - mapping_no_duration = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRDailySourceModel(Ref(0))) |> TimeStep(Dates.Day(1)), - ), - ) - sim_no_duration = PlantSimEngine.GraphSimulation(mtg, mapping_no_duration, nsteps=1, check=true, outputs=Dict(:Leaf => (:XD,))) - meteo_table_no_duration = DataFrame(T=[20.0], Wind=[1.0], Rh=[0.65]) - @test_throws "Missing required `duration`" run!(sim_no_duration, meteo_table_no_duration, executor=SequentialEx()) - - # Expectation 15c-bis: all meteo rows are validated for duration. - meteo_table_partial_duration = DataFrame( - T=[20.0, 21.0], - Wind=[1.0, 1.0], - Rh=[0.65, 0.66], - duration=Any[Dates.Hour(1), missing], - ) - @test_throws "meteo row 2" run!(sim_no_duration, meteo_table_partial_duration, executor=SequentialEx()) - - meteo_table_bad_duration_type = DataFrame(T=[20.0], Wind=[1.0], Rh=[0.65], duration=["1h"]) - @test_throws "Expected a positive Real" run!(sim_no_duration, meteo_table_bad_duration_type, executor=SequentialEx()) - - # Expectation 15d: non-positive meteo duration is rejected. - meteo_zero_duration = Weather([Atmosphere(T=20.0, Wind=1.0, Rh=0.65, duration=Dates.Second(0))]) - @test_throws "strictly positive" run!(sim_no_duration, meteo_zero_duration, executor=SequentialEx()) - - # Expectation 16: model timesteps shorter than meteo base step are rejected. - mapping_substep_period = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRDailySourceModel(Ref(0))) |> TimeStep(Dates.Minute(30)), - ), - ) - sim_substep_period = PlantSimEngine.GraphSimulation(mtg, mapping_substep_period, nsteps=26, check=true, outputs=Dict(:Leaf => (:XD,))) - @test_throws "shorter than simulation base step" run!(sim_substep_period, meteo_hourly, executor=SequentialEx()) - - # Expectation 17: unset timestep uses meteo base-step (preferred hint is informational), - # while explicit TimeStepModel keeps user-selected clock. - range_counter_a = Ref(0) - range_counter_b = Ref(0) - range_counter_forced = Ref(0) - mapping_timestep_hints = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRRangeHintAModel(range_counter_a)), - ModelSpec(MRRangeHintBModel(range_counter_b)), - ModelSpec(MRRangeHintForcedModel(range_counter_forced)) |> TimeStep(Dates.Hour(2)), - ), - ) - meteo8h = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65, duration=Dates.Hour(1))], 8)) - sim_timestep_hints = PlantSimEngine.GraphSimulation(mtg, mapping_timestep_hints, nsteps=8, check=true, outputs=Dict(:Leaf => (:XA, :XB, :XF))) - run!(sim_timestep_hints, meteo8h, executor=SequentialEx()) - specs_hints = PlantSimEngine.get_model_specs(sim_timestep_hints)[:Leaf] - @test isnothing(PlantSimEngine.timestep(specs_hints[:mrrangehinta])) - @test isnothing(PlantSimEngine.timestep(specs_hints[:mrrangehintb])) - @test PlantSimEngine.timestep(specs_hints[:mrrangehintforced]) == Dates.Hour(2) - @test status(sim_timestep_hints)[:Leaf][1].XA == 8.0 - @test status(sim_timestep_hints)[:Leaf][1].XB == 8.0 - @test status(sim_timestep_hints)[:Leaf][1].XF == 4.0 - - io_hints = IOBuffer() - explained_hints = PlantSimEngine.explain_model_specs(sim_timestep_hints; io=io_hints) - explain_hints_txt = String(take!(io_hints)) - @test any(r -> r.process == :mrrangehinta && isnothing(r.timestep), explained_hints) - @test occursin("meteo base step at runtime", explain_hints_txt) - @test occursin("Leaf/mrrangehinta", explain_hints_txt) - - # Expectation 17b: required timestep bounds are enforced for meteo-derived clocks. - strict_counter = Ref(0) - mapping_timestep_hints_strict = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRRangeHintStrictModel(strict_counter)), - ), - ) - sim_timestep_hints_strict = PlantSimEngine.GraphSimulation(mtg, mapping_timestep_hints_strict, nsteps=8, check=true, outputs=Dict(:Leaf => (:XS,))) - err_strict = try - run!(sim_timestep_hints_strict, meteo8h, executor=SequentialEx()) - nothing - catch err - err - end - @test err_strict isa Exception - @test occursin("outside `timestep_hint.required=", sprint(showerror, err_strict)) - - # Expectation 17c: warn if no model runs at meteo base timestep. - no_base_counter = Ref(0) - mapping_no_base_dt = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRClockSourceModel(no_base_counter)) |> TimeStep(ClockSpec(2.0, 1.0)), - ), - ) - sim_no_base_dt = PlantSimEngine.GraphSimulation(mtg, mapping_no_base_dt, nsteps=4, check=true, outputs=Dict(:Leaf => (:X,))) - @test_logs (:warn, r"No model runs at the meteo base timestep") run!(sim_no_base_dt, meteo4, executor=SequentialEx()) - - if _HAS_METEO_SAMPLER_API - # Expectation 18: meteo is sampled at model clock using default weather aggregation. - mapping_meteo_default = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRMeteoDailyConsumerModel()) |> - TimeStep(ClockSpec(2.0, 1.0)), - ), - ) - meteo_mr = Weather([ - Atmosphere(T=10.0, Wind=1.0, Rh=0.50, P=100.0, Ri_SW_f=100.0, duration=Dates.Hour(1), custom_var=1.0), - Atmosphere(T=20.0, Wind=1.0, Rh=0.60, P=100.0, Ri_SW_f=200.0, duration=Dates.Hour(1), custom_var=2.0), - Atmosphere(T=30.0, Wind=1.0, Rh=0.70, P=100.0, Ri_SW_f=300.0, duration=Dates.Hour(1), custom_var=3.0), - Atmosphere(T=40.0, Wind=1.0, Rh=0.80, P=100.0, Ri_SW_f=400.0, duration=Dates.Hour(1), custom_var=4.0), - ]) - sim_meteo_default = PlantSimEngine.GraphSimulation(mtg, mapping_meteo_default, nsteps=4, check=true, outputs=Dict(:Leaf => (:MT, :MTmin, :MTmax, :MRh, :MSW, :MSWq))) - out_meteo_default = run!(sim_meteo_default, meteo_mr, executor=SequentialEx()) - out_meteo_default_df = convert_outputs(out_meteo_default, DataFrame) - @test out_meteo_default_df[:Leaf][:, :MT] == [10.0, 10.0, 25.0, 25.0] - @test out_meteo_default_df[:Leaf][:, :MTmin] == [10.0, 10.0, 20.0, 20.0] - @test out_meteo_default_df[:Leaf][:, :MTmax] == [10.0, 10.0, 30.0, 30.0] - @test out_meteo_default_df[:Leaf][:, :MRh] == [0.5, 0.5, 0.65, 0.65] - @test out_meteo_default_df[:Leaf][:, :MSW] == [100.0, 100.0, 250.0, 250.0] - @test isapprox(out_meteo_default_df[:Leaf][:, :MSWq][1], 0.36; atol=1.0e-9) - @test isapprox(out_meteo_default_df[:Leaf][:, :MSWq][3], 1.8; atol=1.0e-9) - - # Expectation 18b: MTG + mapping entrypoint preserves multi-rate meteo sampling. - out_meteo_default_mtg = run!( - mtg, - mapping_meteo_default, - meteo_mr; - executor=SequentialEx(), - tracked_outputs=Dict(:Leaf => (:MT, :MTmin, :MTmax, :MRh, :MSW, :MSWq)), - ) - out_meteo_default_mtg_df = convert_outputs(out_meteo_default_mtg, DataFrame) - @test out_meteo_default_mtg_df[:Leaf][:, :MT] == [10.0, 10.0, 25.0, 25.0] - @test out_meteo_default_mtg_df[:Leaf][:, :MTmin] == [10.0, 10.0, 20.0, 20.0] - @test out_meteo_default_mtg_df[:Leaf][:, :MTmax] == [10.0, 10.0, 30.0, 30.0] - @test out_meteo_default_mtg_df[:Leaf][:, :MRh] == [0.5, 0.5, 0.65, 0.65] - @test out_meteo_default_mtg_df[:Leaf][:, :MSW] == [100.0, 100.0, 250.0, 250.0] - @test isapprox(out_meteo_default_mtg_df[:Leaf][:, :MSWq][1], 0.36; atol=1.0e-9) - @test isapprox(out_meteo_default_mtg_df[:Leaf][:, :MSWq][3], 1.8; atol=1.0e-9) - - # Expectation 19: meteo bindings allow custom reducers and variable remapping. - mapping_meteo_custom = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRMeteoCustomConsumerModel()) |> - TimeStep(ClockSpec(2.0, 1.0)) |> - PlantSimEngine.MeteoBindings( - ; - Ri_SW_f=RadiationEnergy(), - custom_peak=(source=:custom_var, reducer=MaxReducer()), - ), - ), - ) - sim_meteo_custom = PlantSimEngine.GraphSimulation(mtg, mapping_meteo_custom, nsteps=4, check=true, outputs=Dict(:Leaf => (:MRQ, :MCV))) - out_meteo_custom = run!(sim_meteo_custom, meteo_mr, executor=SequentialEx()) - out_meteo_custom_df = convert_outputs(out_meteo_custom, DataFrame) - @test isapprox.(out_meteo_custom_df[:Leaf][:, :MRQ], [0.36, 0.36, 1.8, 1.8], atol=1.0e-9) |> all - @test out_meteo_custom_df[:Leaf][:, :MCV] == [1.0, 1.0, 3.0, 3.0] - - # Expectation 20: meteo hints infer default bindings/window when ModelSpec does not provide them. - meteo_hint_rows = Weather([ - Atmosphere( - date=DateTime(2025, 2, 1, h - 1, 0, 0), - duration=Dates.Hour(1), - T=float(h), - Wind=1.0, - Rh=0.50, - P=100.0, - Ri_SW_f=100.0, - ) - for h in 1:24 - ]) - mapping_meteo_hint = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRMeteoHintConsumerModel()) |> TimeStep(Dates.Day(1)), - ), - ) - sim_meteo_hint = PlantSimEngine.GraphSimulation(mtg, mapping_meteo_hint, nsteps=24, check=true, outputs=Dict(:Leaf => (:HT, :HSWQ))) - out_meteo_hint = run!(sim_meteo_hint, meteo_hint_rows, executor=SequentialEx()) - out_meteo_hint_df = convert_outputs(out_meteo_hint, DataFrame) - spec_meteo_hint = PlantSimEngine.get_model_specs(sim_meteo_hint)[:Leaf][:mrmeteohintconsumer] - @test PlantSimEngine.timestep(spec_meteo_hint) == Dates.Day(1) - @test meteo_window(spec_meteo_hint) isa CalendarWindow - @test meteo_bindings(spec_meteo_hint).T.reducer isa MaxReducer - @test out_meteo_hint_df[:Leaf][1, :HT] == 24.0 - @test isapprox(out_meteo_hint_df[:Leaf][1, :HSWQ], 8.64; atol=1.0e-9) - - # Expectation 21: CalendarWindow(:day, :current_period) aggregates over the civil day - # (including future timesteps in the same day). - meteo_calendar = Weather(vcat( - [ - Atmosphere( - date=DateTime(2025, 1, 1, h - 1, 0, 0), - duration=Dates.Hour(1), - T=float(h), - Wind=1.0, - Rh=0.50, - P=100.0, - Ri_SW_f=100.0 - ) - for h in 1:24 - ], - [ - Atmosphere( - date=DateTime(2025, 1, 2, h - 1, 0, 0), - duration=Dates.Hour(1), - T=float(100 + h), - Wind=1.0, - Rh=0.60, - P=100.0, - Ri_SW_f=200.0 - ) - for h in 1:24 - ], - )) - - mapping_meteo_calendar_current = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRMeteoDailyConsumerModel()) |> - TimeStep(1.0) |> - PlantSimEngine.MeteoWindow(CalendarWindow(:day; anchor=:current_period, week_start=1, completeness=:allow_partial)), - ), - ) - sim_meteo_calendar_current = PlantSimEngine.GraphSimulation(mtg, mapping_meteo_calendar_current, nsteps=48, check=true, outputs=Dict(:Leaf => (:MT, :MTmin, :MTmax, :MRh, :MSW, :MSWq))) - out_meteo_calendar_current = run!(sim_meteo_calendar_current, meteo_calendar, executor=SequentialEx()) - out_meteo_calendar_current_df = convert_outputs(out_meteo_calendar_current, DataFrame) - @test out_meteo_calendar_current_df[:Leaf][1, :MT] == 12.5 - @test out_meteo_calendar_current_df[:Leaf][10, :MT] == 12.5 - @test out_meteo_calendar_current_df[:Leaf][25, :MT] == 112.5 - @test out_meteo_calendar_current_df[:Leaf][1, :MTmin] == 1.0 - @test out_meteo_calendar_current_df[:Leaf][1, :MTmax] == 24.0 - @test out_meteo_calendar_current_df[:Leaf][25, :MTmin] == 101.0 - @test out_meteo_calendar_current_df[:Leaf][25, :MTmax] == 124.0 - @test isapprox(out_meteo_calendar_current_df[:Leaf][1, :MSWq], 8.64; atol=1.0e-9) - @test isapprox(out_meteo_calendar_current_df[:Leaf][25, :MSWq], 17.28; atol=1.0e-9) - - # Expectation 22: CalendarWindow(:day, :previous_complete_period) uses previous day. - mapping_meteo_calendar_prev = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRMeteoDailyConsumerModel()) |> - TimeStep(1.0) |> - PlantSimEngine.MeteoWindow(CalendarWindow(:day; anchor=:previous_complete_period, week_start=1, completeness=:allow_partial)), - ), - ) - sim_meteo_calendar_prev = PlantSimEngine.GraphSimulation(mtg, mapping_meteo_calendar_prev, nsteps=48, check=true, outputs=Dict(:Leaf => (:MT, :MTmin, :MTmax, :MRh, :MSW, :MSWq))) - out_meteo_calendar_prev = run!(sim_meteo_calendar_prev, meteo_calendar, executor=SequentialEx()) - out_meteo_calendar_prev_df = convert_outputs(out_meteo_calendar_prev, DataFrame) - @test out_meteo_calendar_prev_df[:Leaf][30, :MT] == 12.5 - @test out_meteo_calendar_prev_df[:Leaf][30, :MTmin] == 1.0 - @test out_meteo_calendar_prev_df[:Leaf][30, :MTmax] == 24.0 - - # Expectation 23: strict previous-complete-period errors when unavailable. - mapping_meteo_calendar_prev_strict = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRMeteoDailyConsumerModel()) |> - TimeStep(1.0) |> - PlantSimEngine.MeteoWindow(CalendarWindow(:day; anchor=:previous_complete_period, week_start=1, completeness=:strict)), - ), - ) - sim_meteo_calendar_prev_strict = PlantSimEngine.GraphSimulation(mtg, mapping_meteo_calendar_prev_strict, nsteps=48, check=true, outputs=Dict(:Leaf => (:MT, :MTmin, :MTmax, :MRh, :MSW, :MSWq))) - @test_throws "No period available" run!(sim_meteo_calendar_prev_strict, meteo_calendar, executor=SequentialEx()) - end - - # Expectation 24: ambiguous same-name inferred producer is rejected at initialization. - mapping_ambiguous_infer = PlantSimEngine.ModelMapping( - :Leaf => ( - MRConflict1Model(), - MRConflict2Model(), - MRZConsumerModel(), - ), - ) - @test_throws "Ambiguous inferred producer for input `Z`" PlantSimEngine.GraphSimulation(mtg, mapping_ambiguous_infer, nsteps=1, check=true, outputs=Dict(:Leaf => (:ZZ,))) - - # Expectation 24a: stream-only publishers are ignored by auto input producer inference. - mapping_stream_only_infer = PlantSimEngine.ModelMapping( - :Leaf => ( - MRConflict1Model(), - ModelSpec(MRConflict2Model()) |> OutputRouting(; Z=:stream_only), - MRZConsumerModel(), - ), - ) - sim_stream_only_infer = PlantSimEngine.GraphSimulation(mtg, mapping_stream_only_infer, nsteps=1, check=true, outputs=Dict(:Leaf => (:ZZ,))) - run!(sim_stream_only_infer, meteo, executor=SequentialEx()) - @test status(sim_stream_only_infer)[:Leaf][1].ZZ == 1.0 - spec_stream_only_infer = PlantSimEngine.get_model_specs(sim_stream_only_infer)[:Leaf][:mrzconsumer] - @test input_bindings(spec_stream_only_infer).Z.process == :mrconflict1 - - # Expectation 24b: cross-scale inference ignores sibling scales not on the same lineage. - mapping_lineage_infer = PlantSimEngine.ModelMapping( - :Plant => ( - MRAncestorSourceModel(), - ), - :Soil => ( - MRSiblingSourceModel(), - ), - :Leaf => ( - ModelSpec(MRZConsumerModel()) |> - PlantSimEngine.MultiScaleModel([:Z => (:Plant => :Z)]), - ), - ) - sim_lineage_infer = PlantSimEngine.GraphSimulation(mtg, mapping_lineage_infer, nsteps=1, check=true, outputs=Dict(:Leaf => (:ZZ,))) - run!(sim_lineage_infer, meteo, executor=SequentialEx()) - @test status(sim_lineage_infer)[:Leaf][1].ZZ == 11.0 - spec_lineage_infer = PlantSimEngine.get_model_specs(sim_lineage_infer)[:Leaf][:mrzconsumer] - @test input_bindings(spec_lineage_infer).Z.process == :mrancestorsource - @test input_bindings(spec_lineage_infer).Z.scale == :Plant - - # Expectation 24c: inferred bindings prefer same-scale producers when available. - mapping_same_scale_preferred = PlantSimEngine.ModelMapping( - :Plant => ( - MRMultiScaleTSourceModel(100.0), - ), - :Internode => ( - MRMultiScaleTSourceModel(10.0), - ), - :Leaf => ( - MRMultiScaleTSourceModel(1.0), - MRLeafTTConsumerModel(), - ), - ) - sim_same_scale_preferred = PlantSimEngine.GraphSimulation( - mtg, - mapping_same_scale_preferred, - nsteps=1, - check=true, - outputs=Dict(:Leaf => (:TT, :TT_used)) - ) - run!(sim_same_scale_preferred, meteo, executor=SequentialEx()) - spec_same_scale_preferred = PlantSimEngine.get_model_specs(sim_same_scale_preferred)[:Leaf][:mrleafttconsumer] - @test input_bindings(spec_same_scale_preferred).TT.process == :mrmultiscaletsource - @test input_bindings(spec_same_scale_preferred).TT.scale == :Leaf - @test status(sim_same_scale_preferred)[:Leaf][1].TT_used == 1.0 - - # Expectation 24d: when no same-scale producer exists, repeated process names across scales require explicit scale mapping. - mapping_cross_scale_same_process_ambiguous = PlantSimEngine.ModelMapping( - :Plant => ( - MRMultiScaleTSourceModel(100.0), - ), - :Internode => ( - MRMultiScaleTSourceModel(10.0), - ), - :Leaf => ( - MRLeafTTConsumerModel(), - ), - ) - @test_throws "Ambiguous inferred producer for input `TT`" PlantSimEngine.GraphSimulation( - mtg, - mapping_cross_scale_same_process_ambiguous, - nsteps=1, - check=true, - outputs=Dict(:Leaf => (:TT_used,)) - ) - - # Expectation 24e: same-rate hard dependencies are ignored for auto bindings and canonical publisher checks. - mapping_hard_same_rate = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRHardParentModel()) |> TimeStep(1.0), - ModelSpec(MRHardChildModel()) |> TimeStep(1.0), - ModelSpec(MRHardConsumerModel()) |> TimeStep(1.0), - ), - ) - sim_hard_same_rate = PlantSimEngine.GraphSimulation(mtg, mapping_hard_same_rate, nsteps=1, check=true, outputs=Dict(:Leaf => (:A, :B))) - run!(sim_hard_same_rate, meteo, executor=SequentialEx()) - spec_hard_same_rate = PlantSimEngine.get_model_specs(sim_hard_same_rate)[:Leaf][:mrhardconsumer] - @test input_bindings(spec_hard_same_rate).A.process == :mrhardparent - @test status(sim_hard_same_rate)[:Leaf][1].B == 5.0 - - # Expectation 24f: hard dependency children cannot be scheduled independently from their parent. - mapping_hard_different_rate = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRHardParentModel()) |> TimeStep(1.0), - ModelSpec(MRHardChildModel()) |> TimeStep(2.0), - ModelSpec(MRHardConsumerModel()) |> TimeStep(1.0), - ), - ) - @test_throws "Hard dependency `mrhardchild`" PlantSimEngine.GraphSimulation( - mtg, - mapping_hard_different_rate, - nsteps=1, - check=true, - outputs=Dict(:Leaf => (:A, :B)) - ) - - # Expectation 25: missing producer remains allowed; model can rely on initialized/forced inputs. - mapping_missing_input = PlantSimEngine.ModelMapping( - :Leaf => ( - MRMissingInputConsumerModel(), - Status(U=42.0), - ), - ) - sim_missing_input = PlantSimEngine.GraphSimulation(mtg, mapping_missing_input, nsteps=1, check=true, outputs=Dict(:Leaf => (:OU,))) - run!(sim_missing_input, meteo, executor=SequentialEx()) - @test status(sim_missing_input)[:Leaf][1].OU == 42.0 - - # Expectation 26: invalid mapping-level API configuration fails during GraphSimulation init. - mapping_bad_input = PlantSimEngine.ModelMapping( - :Leaf => ( - MRSourceModel(), - ModelSpec(MRConsumerModel()) |> - PlantSimEngine.InputBindings(; Z=(process=:mrsource, var=:S)), - ), - ) - @test_throws "declares binding for input `Z`" PlantSimEngine.GraphSimulation(mtg, mapping_bad_input, nsteps=1, check=true, outputs=Dict(:Leaf => (:B,))) - - mapping_bad_process = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRConsumerModel()) |> - PlantSimEngine.InputBindings(; C=(process=:unknown_process, var=:S)), - ), - ) - @test_throws "Unknown source process `unknown_process`" PlantSimEngine.GraphSimulation(mtg, mapping_bad_process, nsteps=1, check=true, outputs=Dict(:Leaf => (:B,))) - - mapping_bad_routing = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRSourceModel()) |> - OutputRouting(; Z=:stream_only), - ), - ) - @test_throws "declares routing for output `Z`" PlantSimEngine.GraphSimulation(mtg, mapping_bad_routing, nsteps=1, check=true, outputs=Dict(:Leaf => (:S,))) - - mapping_bad_interp_mode = PlantSimEngine.ModelMapping( - :Leaf => ( - MRSourceModel(), - ModelSpec(MRConsumerModel()) |> - PlantSimEngine.InputBindings(; C=(process=:mrsource, var=:S, policy=Interpolate(:spline))), - ), - ) - @test_throws "Invalid interpolation mode `spline`" PlantSimEngine.GraphSimulation(mtg, mapping_bad_interp_mode, nsteps=1, check=true, outputs=Dict(:Leaf => (:B,))) - - @test_throws "Unsupported reducer value" Aggregate(:median) - - mapping_bad_period = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRDailySourceModel(Ref(0))) |> TimeStep(Dates.Month(1)), - ), - ) - @test_throws "non-fixed periods are not supported" PlantSimEngine.GraphSimulation(mtg, mapping_bad_period, nsteps=1, check=true, outputs=Dict(:Leaf => (:XD,))) - - mapping_bad_scope = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRSourceModel()) |> PlantSimEngine.ScopeModel(:invalid_scope), - ), - ) - @test_throws "Invalid scope selector" PlantSimEngine.GraphSimulation(mtg, mapping_bad_scope, nsteps=1, check=true, outputs=Dict(:Leaf => (:S,))) - - mapping_bad_meteo = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRMeteoCustomConsumerModel()) |> - PlantSimEngine.MeteoBindings(; Ri_SW_f=(source=:Ri_SW_f, badfield=:oops)), - ), - ) - @test_throws "unsupported fields" PlantSimEngine.GraphSimulation(mtg, mapping_bad_meteo, nsteps=1, check=true, outputs=Dict(:Leaf => (:MRQ,))) - - @test_throws "Unsupported MeteoBindings value" PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRMeteoCustomConsumerModel()) |> - PlantSimEngine.MeteoBindings(; Ri_SW_f=:radiation_energy), - ), - ) - - @test_throws "Unsupported MeteoWindow value" PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRMeteoCustomConsumerModel()) |> - PlantSimEngine.MeteoWindow("day"), - ), - ) - - PlantSimEngine.@process "mrbadhintmodel" verbose = false - struct MRBadHintModel <: AbstractMrbadhintmodelModel end - PlantSimEngine.inputs_(::MRBadHintModel) = NamedTuple() - PlantSimEngine.outputs_(::MRBadHintModel) = (X=-Inf,) - PlantSimEngine.run!(::MRBadHintModel, models, status, meteo, constants=nothing, extra=nothing) = (status.X = 1.0) - PlantSimEngine.timestep_hint(::Type{<:MRBadHintModel}) = "hourly" - - mapping_bad_hint = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRBadHintModel()), - ), - ) - @test_throws "Invalid `timestep_hint`" PlantSimEngine.GraphSimulation(mtg, mapping_bad_hint, nsteps=1, check=true, outputs=Dict(:Leaf => (:X,))) -end diff --git a/test/test-multirate-scaffolding.jl b/test/test-multirate-scaffolding.jl deleted file mode 100644 index a459eb967..000000000 --- a/test/test-multirate-scaffolding.jl +++ /dev/null @@ -1,94 +0,0 @@ -using PlantSimEngine -using PlantSimEngine.Examples -using MultiScaleTreeGraph -using Test - -@testset "Multi-rate scaffolding" begin - m = Process1Model(1.0) - - clk = timespec(m) - @test clk.dt == 1.0 - @test clk.phase == 0.0 - - @test output_policy(m) == NamedTuple() - @test isnothing(timestep_hint(m)) - @test isnothing(meteo_hint(m)) - @test meteo_inputs(m) == () - @test meteo_outputs(m) == () - @test meteo_inputs(ModelSpec(m)) == () - @test meteo_outputs(ModelSpec(m)) == () - @test input_bindings(ModelSpec(m)) == NamedTuple() - @test output_routing(ModelSpec(m)) == NamedTuple() - @test model_scope(ModelSpec(m)) == :global - @test updates(ModelSpec(m)) == () - @test_throws "String scope selectors are not supported" ModelSpec(m; scope="plant") - @test_throws "String scope selectors are not supported" PlantSimEngine.ScopeModel("plant")(m) - - scope_node = Node(MultiScaleTreeGraph.NodeMTG("/", :Leaf, 1, 1)) - @test_throws "must return `ScopeId` or `Symbol`" PlantSimEngine._scope_from_selector( - (node, scale, process) -> "plant", - scope_node, - :Leaf, - :process1, - ) - - mapping = Dict(:Leaf => (m,)) - resolved_specs = resolved_model_specs(mapping) - @test haskey(resolved_specs, :Leaf) - @test haskey(resolved_specs[:Leaf], :process1) - - io = IOBuffer() - explained = explain_model_specs(mapping; io=io) - explain_txt = String(take!(io)) - @test length(explained) == 1 - @test explained[1].process == :process1 - @test occursin("Resolved model specs:", explain_txt) - @test occursin("Leaf/process1", explain_txt) - @test occursin("input_bindings=", explain_txt) - - spec = ModelSpec(m) |> - TimeStep(24.0) |> - PlantSimEngine.InputBindings(; var1=(process=:process1, var=:var3)) |> - OutputRouting(; var3=:stream_only) |> - PlantSimEngine.ScopeModel(:plant) |> - Updates(:var3; after=:process1) |> - Updates(:var3; after=:process2) - @test PlantSimEngine.model_(spec) === m - @test PlantSimEngine.timestep(spec) == 24.0 - @test input_bindings(spec).var1.process == :process1 - @test input_bindings(spec).var1.policy isa HoldLast - @test output_routing(spec).var3 == :stream_only - @test model_scope(spec) == :plant - @test updates(spec)[1].variables == (:var3,) - @test updates(spec)[1].after == (:process1,) - @test updates(spec)[2].variables == (:var3,) - @test updates(spec)[2].after == (:process2,) - - mspec = ModelSpec(m) |> PlantSimEngine.MultiScaleModel([:var1 => (:Leaf => :var1)]) - @test length(PlantSimEngine.get_mapped_variables(mspec)) == 1 - - ts = TemporalState() - @test isempty(ts.caches) - @test isempty(ts.last_run) - @test isempty(ts.streams) - @test isempty(ts.producer_horizons) - @test isempty(ts.export_plans) - @test isempty(ts.export_rows) - - scope = ScopeId(:global, 1) - key = OutputKey(scope, :Leaf, 7, :process1, :var3) - ts.caches[key] = HoldLastCache(1.0, 42.0) - @test ts.caches[key] isa HoldLastCache - @test ts.caches[key].v == 42.0 - - vals = [1.0, 2.0, 3.0] - durs = [1.0, 1.0, 1.0] - @test Integrate().reducer isa SumReducer - @test Aggregate().reducer isa MeanReducer - @test PlantSimEngine._window_reduce(vals, durs, Integrate()) == 6.0 - @test PlantSimEngine._window_reduce(vals, durs, Aggregate()) == 2.0 - @test PlantSimEngine._window_reduce(vals, durs, Integrate(MeanReducer())) == - PlantSimEngine._window_reduce(vals, durs, Aggregate(MeanReducer())) - @test PlantSimEngine._window_reduce(vals, durs, Integrate(SumReducer())) == - PlantSimEngine._window_reduce(vals, durs, Aggregate(SumReducer())) -end diff --git a/test/test-performance.jl b/test/test-performance.jl deleted file mode 100644 index 33f8caf8d..000000000 --- a/test/test-performance.jl +++ /dev/null @@ -1,116 +0,0 @@ - -using BenchmarkTools -using Dates - -PlantSimEngine.@process "sleep" verbose = false - -struct ToySleepModel <: AbstractSleepModel -end - -PlantSimEngine.inputs_(::ToySleepModel) = (a=-Inf,) -PlantSimEngine.outputs_(::ToySleepModel) = NamedTuple() - -function PlantSimEngine.run!(m::ToySleepModel, models, status, meteo, constants=nothing, extra=nothing) - # sleep for 0.01 seconds (not going to be perfectly accurate) - Base.sleep(0.001) -end - -PlantSimEngine.TimeStepDependencyTrait(::Type{<:ToySleepModel}) = PlantSimEngine.IsTimeStepIndependent() - -meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Day) -nrows = nrow(meteo_day) - -vc = [0 for i in 1:nrows] - -mapping1 = PlantSimEngine.ModelMapping(process1=ToySleepModel(), status=(a=vc,)) -mapping2 = PlantSimEngine.ModelMapping(process1=ToySleepModel(), status=(a=vc,)) - -@testset begin - "Check number of threads" - nthr = Threads.nthreads() - @test nthr >= 1 - - t_seq = @benchmark run!(mapping1, meteo_day; executor=SequentialEx()) - #t_seq = run!(models1, meteo_day; executor = SequentialEx()) - med_time_seq = median(t_seq).time - - #time is in nanoseconds - @test med_time_seq > nrows * 1000000 - - t_mt = @benchmark run!(mapping2, meteo_day; executor=ThreadedEx()) - #t_mt = run!(models2, meteo_day; executor = ThreadedEx()) - med_time_mt = median(t_mt).time - - if nthr > 1 - @test med_time_mt > nrows * 1000000 / nthr - end - - # Threads sleep/wakeup scheduling overhead causing inconsistencies ? - # In any case, sometimes MT beats ST on CI runners, and the mac runner seems to return puzzling false positives - # Deactivating it for now - # TODO there is a thread discussing unreliability of the sleep() function, need to check it - - #if !Sys.isapple() - # @test abs(nthr * med_time_mt - med_time_seq) < 0.2 * med_time_seq - #end - - # unsure how to recover outputs in benchmarked expressions to compare them, rerun the functions as a workaround for now - @test run!(mapping1, meteo_day; executor=SequentialEx()) == run!(mapping2, meteo_day; executor=ThreadedEx()) -end - -# TODO make sure a mt test with nthreads == 1 also is tested and is correct -@testset "Single and multi-threaded output consistency" begin - nthr = Threads.nthreads() - @test nthr >= 1 - - using Dates - meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Day) - - mapping = PlantSimEngine.ModelMapping( - ToyLAIModel(), - Beer(0.5), - status=(TT_cu=cumsum(meteo_day.TT),) - ) - - tracked_outputs = (:LAI,) - - out_seq, out_mt = run_single_and_multi_thread_modellist(mapping, tracked_outputs, meteo_day) - @test compare_outputs_modellists(out_seq, out_mt) - - mappings_single_scale, _, outs_vectors = get_modelmapping_bank() - meteos_all = get_simple_meteo_bank() - - # First meteo only has one timestep - meteos = meteos_all[2:length(meteos_all)] - - for i in 1:length(mappings_single_scale) - #i = 1 - mapping_template = mappings_single_scale[i] - outs_vector = outs_vectors[i] - for j in 1:length(meteos) - meteo = meteos[j] - for k in 1:length(outs_vector) - #k = 1 - out_tuple = outs_vector[k] - - try - mapping = deepcopy(mapping_template) - out_st, out_mt = run_single_and_multi_thread_modellist(mapping, out_tuple, meteo) - @test compare_outputs_modellists(out_st, out_mt) - catch e - #print(i," ", j, " ", k) - #println() - if isa(e, DimensionMismatch) - continue - elseif isa(e, ErrorException) - showerror(stdout, e) - @test false - else - showerror(stdout, e) - @test false - end - end - end - end - end -end diff --git a/test/test-simulation.jl b/test/test-simulation.jl deleted file mode 100644 index d5f27b0b3..000000000 --- a/test/test-simulation.jl +++ /dev/null @@ -1,328 +0,0 @@ -@testset "Check missing model" begin - # No problem here: - @test_nowarn PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=15.0, var2=0.3) - ) - - # Missing model for process2: - @test_logs ( - :info, - "Model Process3Model from process process3 needs a model that is a subtype of Process2Model in process process2, but the process is not parameterized in the ModelMapping." - ), - ( - :info, - "Some variables must be initialized before simulation: (process3 = (:var5,),) (see `to_initialize()`)" - ) - PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process3=Process3Model(), - status=(var1=15.0, var2=0.3) - ) -end; - -@testset "Deprecated run! entrypoints" begin - models = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=15.0, var2=0.3) - ) - meteo = Atmosphere(T=20.0, Wind=1.0, Rh=0.65) - - run!(models, meteo) - @test !isdefined(PlantSimEngine, :ModelList) - @test_throws MethodError run!([models], meteo) - @test_throws ErrorException run!(PlantSimEngine.ModelMapping("mod1" => models), meteo) - - mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Leaf, 1, 1)) - mtg[:var1] = 15.0 - mtg[:var2] = 0.3 - mapping_dict = Dict(:Leaf => (Process1Model(1.0), Process2Model(), Process3Model())) - @test_throws ErrorException run!(mtg, mapping_dict, meteo) -end - -@testset "Removed multirate keyword for single-scale" begin - mapping = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=15.0, var2=0.3) - ) - meteo = Atmosphere(T=20.0, Wind=1.0, Rh=0.65) - - @test_throws MethodError run!(mapping, meteo; multirate=true) - @test_throws MethodError run!([mapping], meteo; multirate=true) -end - -@testset "Simulation: 1 time-step, 0 Atmosphere" begin - mapping = PlantSimEngine.ModelMapping( - Process1Model(1.0); - status=(var1=15.0, var2=0.3) - ) - outputs = run!(mapping) - - vars = keys(outputs) - @test [outputs[i][1] for i in vars] == [15.0, 0.3, 5.5] -end; - - -@testset "Simulation: 1 time-step, 1 Atmosphere" begin - - status_nt = (var1=15.0, var2=0.3) - mapping = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=status_nt - ) - - meteo = Atmosphere(T=20.0, Wind=1.0, Rh=0.65) - - modellist_outputs = run!(mapping, meteo) - vars = keys(modellist_outputs) - @test [modellist_outputs[i][1] for i in vars] == [34.95, 22.0, 56.95, 15.0, 5.5, 0.3] - - @test check_multiscale_simulation_is_equivalent(mapping, meteo) -end; - -@testset "Simulation: 1 time-step, 1 Atmosphere, 2 objects" begin - mapping = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=15.0, var2=0.3) - ) - - mapping2 = PlantSimEngine.ModelMapping( - process1=Process1Model(2.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=15.0, var2=0.3) - ) - - meteo = Atmosphere(T=20.0, Wind=1.0, Rh=0.65) - - @testset "simulation with an array of objects" begin - outputs_vector = [run!(m, meteo) for m in (mapping, mapping2)] - @test [outputs_vector[1][i][1] for i in keys(outputs_vector[1])] == [34.95, 22.0, 56.95, 15.0, 5.5, 0.3] - @test [outputs_vector[2][i][1] for i in keys(outputs_vector[2])] == [36.95, 26.0, 62.95, 15.0, 6.5, 0.3] - end - - @testset "simulation with a dict of objects" begin - outputs_vector = Dict("mod1" => run!(mapping, meteo), "mod2" => run!(mapping2, meteo)) - @test [outputs_vector["mod1"][1][i] for i in keys(outputs_vector["mod1"])] == [34.95, 22.0, 56.95, 15.0, 5.5, 0.3] - @test [outputs_vector["mod2"][1][i] for i in keys(outputs_vector["mod2"])] == [36.95, 26.0, 62.95, 15.0, 6.5, 0.3] - end -end; - -@testset "Simulation: 2 time-steps, 1 Atmosphere" begin - mapping = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=[15.0, 16.0], var2=0.3) - ) - - meteo = Atmosphere(T=20.0, Wind=1.0, Rh=0.65) - - outputs = run!(mapping, meteo) - vars = keys(outputs) - @test [outputs[i] for i in vars] == [ - [34.95, 35.550000000000004], - [22.0, 23.2], - [56.95, 58.75], - [15.0, 16.0], - [5.5, 5.8], - [0.3, 0.3], - ] -end; - -@testset "Simulation: 2 time-steps, 2 Atmospheres" begin - - status_nt = (var1=[15.0, 16.0], var2=0.3) - - mapping = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=status_nt - ) - - meteo = Weather( - [ - Atmosphere(T=20.0, Wind=1.0, Rh=0.65), - Atmosphere(T=25.0, Wind=0.5, Rh=0.8) - ] - ) - - modellist_outputs = run!(mapping, meteo) - vars = keys(modellist_outputs) - @test [modellist_outputs[i] for i in vars] == [ - [34.95, 40.0], - [22.0, 23.2], - [56.95, 63.2], - [15.0, 16.0], - [5.5, 5.8], - [0.3, 0.3], - ] - - @test check_multiscale_simulation_is_equivalent(mapping, meteo) -end; - - -@testset "Simulation: 2 time-steps, 2 Atmospheres, 2 objects" begin - mapping = PlantSimEngine.ModelMapping( - process1=Process1Model(1.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=[15.0, 16.0], var2=0.3) - ) - - mapping2 = PlantSimEngine.ModelMapping( - process1=Process1Model(2.0), - process2=Process2Model(), - process3=Process3Model(), - status=(var1=[15.0, 16.0], var2=0.3) - ) - - meteo = Weather( - [ - Atmosphere(T=20.0, Wind=1.0, Rh=0.65), - Atmosphere(T=25.0, Wind=0.5, Rh=0.8) - ] - ) - - @testset "simulation with an array of objects" begin - outputs_vector = [run!(m, meteo) for m in (mapping, mapping2)] - @test [outputs_vector[1][i] for i in keys(outputs_vector[1])] == [ - [34.95, 40.0], [22.0, 23.2], [56.95, 63.2], [15.0, 16.0], [5.5, 5.8], [0.3, 0.3] - ] - @test [outputs_vector[2][i] for i in keys(outputs_vector[2])] == [ - [36.95, 42.0], [26.0, 27.2], [62.95, 69.2], [15.0, 16.0], [6.5, 6.8], [0.3, 0.3] - ] - end - - @testset "simulation with a dict of objects" begin - outputs_vector = Dict("mod1" => run!(mapping, meteo), "mod2" => run!(mapping2, meteo)) - @test [[outputs_vector["mod1"][1][i], outputs_vector["mod1"][2][i]] for i in keys(outputs_vector["mod1"])] == [ - [34.95, 40.0], [22.0, 23.2], [56.95, 63.2], [15.0, 16.0], [5.5, 5.8], [0.3, 0.3] - ] - @test [[outputs_vector["mod2"][1][i], outputs_vector["mod2"][2][i]] for i in keys(outputs_vector["mod2"])] == [ - [36.95, 42.0], [26.0, 27.2], [62.95, 69.2], [15.0, 16.0], [6.5, 6.8], [0.3, 0.3] - ] - end -end; - -@testset "Simulation: 2 time-steps, 2 Atmospheres, MTG" begin - mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Plant, 1, 1)) - internode = Node(mtg, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - leaf = Node(mtg, MultiScaleTreeGraph.NodeMTG("<", :Leaf, 1, 2)) - leaf[:var1] = [15.0, 16.0] - leaf[:var2] = 0.3 - - mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - Process1Model(1.0), - Process2Model(), - Process3Model() - ) - ) - - meteo = Weather( - [ - Atmosphere(T=20.0, Wind=1.0, Rh=0.65), - Atmosphere(T=25.0, Wind=0.5, Rh=0.8) - ] - ) - - # var1 is taken from the MTG attributes but is a vector instead of a scalar, expecting an error: - @test_throws ErrorException run!(mtg, mapping, meteo) - - leaf[:var1] = 15.0 - - #out = @test_nowarn run!(mtg, mapping, meteo) - nsteps = PlantSimEngine.get_nsteps(meteo) - sim = PlantSimEngine.GraphSimulation(mtg, mapping, nsteps=nsteps, check=true) - out = @test_nowarn run!(sim, meteo) - - vars = (:var4, :var6, :var5, :var1, :var2, :var3) - @test [sim.statuses[:Leaf][1][i] for i in vars] == [ - 22.0, 61.4, 39.4, 15.0, 0.3, 5.5 - ] -end; - - -@testset "Meteo+ModelMapping/mapping+outputs combos either valid or different status vector size vs meteo length either run successfully or return a DimensionMisMatch" begin - verbose = false # set to true to print the indices of the combinations that fail - meteos = get_simple_meteo_bank() - mappings_single_scale, _, outputs_tuples_vectors = get_modelmapping_bank() - - for i in 1:length(mappings_single_scale) - # i = 3 - mapping_template = mappings_single_scale[i] - outs_vector = outputs_tuples_vectors[i] - - for j in 1:length(meteos) - # j = 1 - meteo = meteos[j] - for k in 1:length(outs_vector) - # k = 7 - out_tuple = outs_vector[k] - @test try - mapping = deepcopy(mapping_template) - outs_modellist = run!(mapping, meteo; tracked_outputs=out_tuple) - true - catch e - verbose && print(i, " ", j, " ", k) - verbose && println() - if isa(e, DimensionMismatch) - true - elseif isa(e, ErrorException) - showerror(stdout, e) - false - else - showerror(stdout, e) - false - end - end - end - end - end - - mtgs, mappings, outs_tuples_vectors_mappings = get_simple_mapping_bank() - - for i in 1:length(mappings) - # i = 1 - mapping = mappings[i] - outs_vector = outs_tuples_vectors_mappings[i] - - for j in 1:length(meteos) - # j = 1 - meteo = meteos[j] - for k in 1:length(outs_vector) - # k = 4 - out_tuple = outs_vector[k] - - mtg = deepcopy(mtgs[i]) - try - outs_multiscale = run!(mtg, mapping, meteo; tracked_outputs=out_tuple) - @test true - catch e - verbose && print(i, " ", j, " ", k) - verbose && println() - if isa(e, DimensionMismatch) - @test true - #elseif isa(e, ErrorException) - else - #@enter outs_multiscale = run!(mtg, mapping, meteo; tracked_outputs=out_tuple) - showerror(stdout, e) - @test false - end - end - end - end - end -end diff --git a/test/test-toy_models.jl b/test/test-toy_models.jl index 859be87bf..39f74a54d 100644 --- a/test/test-toy_models.jl +++ b/test/test-toy_models.jl @@ -1,106 +1,56 @@ -meteo_day = CSV.read(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), DataFrame, header=18) +using Dates -# Note (smack) : The first test's behaviour is weird to me, because there is an [Info :] that correctly indicates -# :LAI is not initialised, yet @test_nowarn doesn't capture it. I'm not sure what the intended test was, between 'Info' and 'Warn' -@testset "ToyLAIModel" begin - @test_nowarn PlantSimEngine.ModelMapping(ToyLAIModel()) - @test_nowarn PlantSimEngine.ModelMapping(ToyLAIModel(); status=(TT_cu=10,)) - @test_nowarn PlantSimEngine.ModelMapping( - ToyLAIModel(); - status=(TT_cu=cumsum(meteo_day.TT),), - ) - - mapping = PlantSimEngine.ModelMapping( - ToyLAIModel(); - status=(TT_cu=cumsum(meteo_day.TT),), +function toy_scene(status, models...; environment=nothing) + applications = map(models) do model + ModelSpec(model) |> AppliesTo(One(scale=:Plant)) + end + Scene( + Object(:plant; scale=:Plant, kind=:plant, status=status); + applications=Tuple(applications), + environment=environment, ) - - outputs = @test_nowarn run!(mapping) - - @test outputs[:TT_cu] == cumsum(meteo_day.TT) - @test outputs[:LAI][begin] ≈ 0.00554987593080316 - @test outputs[:LAI][end] ≈ 0.0 end -@testset "ToyLAIModel+Beer" begin - mapping = PlantSimEngine.ModelMapping( - ToyLAIModel(), - Beer(0.5), - status=(TT_cu=cumsum(meteo_day.TT),) +@testset "Toy models through Scene" begin + lai_scene = toy_scene(Status(TT_cu=900.0), ToyLAIModel()) + run!(lai_scene) + lai_status = only(scene_objects(lai_scene; scale=:Plant)).status + @test 0.0 < lai_status.LAI < 8.0 + + meteo = Atmosphere( + T=20.0, + Wind=1.0, + P=101.3, + Rh=0.65, + Ri_PAR_f=300.0, + duration=Hour(1), ) + coupled = toy_scene(Status(TT_cu=900.0), ToyLAIModel(), Beer(0.5); environment=meteo) + run!(coupled) + coupled_status = only(scene_objects(coupled; scale=:Plant)).status + @test coupled_status.aPPFD > 0.0 - outputs = run!(mapping, meteo_day) - - @test mean(outputs[:aPPFD]) ≈ 9.511021781482347 - @test mean(outputs[:LAI]) ≈ 1.098492557536525 -end - - -@testset "ToyRUEGrowthModel" begin - rue = 0.3 - @test_nowarn PlantSimEngine.ModelMapping(ToyRUEGrowthModel(rue)) - @test_nowarn PlantSimEngine.ModelMapping(ToyRUEGrowthModel(rue); status=(aPPFD=[10.0, 30.0, 25.0],)) - - # One time step: - mapping = PlantSimEngine.ModelMapping(ToyRUEGrowthModel(rue); status=(aPPFD=30.0,)) - - outputs = run!(mapping, executor=SequentialEx()) - @test outputs[:biomass][1] ≈ rue * 30.0 - - # Several time steps: - aPPFD = [10.0, 30.0, 25.0] - mapping = PlantSimEngine.ModelMapping(ToyRUEGrowthModel(rue); status=(aPPFD=aPPFD,)) - - outputs = run!(mapping, executor=SequentialEx()) - @test outputs[:biomass] ≈ cumsum(rue * aPPFD) -end - -@testset "ToyAssimGrowthModel" begin - @test_nowarn PlantSimEngine.ModelMapping(ToyAssimGrowthModel()) - @test_nowarn PlantSimEngine.ModelMapping(ToyAssimGrowthModel(); status=(carbon_assimilation=[10.0, 30.0, 25.0],)) - - # Uninitialized: - to_init_uninitialized = to_initialize(PlantSimEngine.ModelMapping(ToyAssimGrowthModel())) - if to_init_uninitialized isa AbstractDict - @test haskey(to_init_uninitialized, :Default) - @test :aPPFD in to_init_uninitialized[:Default] - else - @test :growth in keys(to_init_uninitialized) - @test :aPPFD in to_init_uninitialized[:growth] - end - - # One time step: - mapping = PlantSimEngine.ModelMapping(ToyAssimGrowthModel(); status=(aPPFD=30.0,)) - - @test isempty(to_initialize(mapping)) - - outputs = run!(mapping) - @test outputs[:biomass] ≈ [4.5] - - # Several time steps: - mapping = PlantSimEngine.ModelMapping(ToyAssimGrowthModel(); status=(aPPFD=[10.0, 30.0, 25.0],)) - - outputs = run!(mapping) - @test outputs[:biomass] ≈ cumsum(outputs[:biomass_increment]) - @test outputs[:biomass_increment] ≈ [0.8333333333333334, 4.5, 3.5833333333333335] -end - -@testset "ToyLAIModel+Beer+ToyRUEGrowthModel" begin rue = 0.3 - mapping = PlantSimEngine.ModelMapping( + rue_scene = toy_scene(Status(aPPFD=30.0), ToyRUEGrowthModel(rue)) + run!(rue_scene) + rue_status = only(scene_objects(rue_scene; scale=:Plant)).status + @test rue_status.biomass ≈ rue * 30.0 + + assimilation_scene = toy_scene(Status(aPPFD=30.0), ToyAssimGrowthModel()) + run!(assimilation_scene) + assimilation_status = only(scene_objects(assimilation_scene; scale=:Plant)).status + @test assimilation_status.biomass ≈ 4.5 + + full_scene = toy_scene( + Status(TT_cu=900.0), ToyLAIModel(), Beer(0.5), - ToyRUEGrowthModel(rue), - status=(TT_cu=cumsum(meteo_day.TT),), + ToyRUEGrowthModel(rue); + environment=meteo, ) - - # Match the warning on the executor, the default is ThreadedEx() but ToyRUEGrowthModel can't be run in parallel: - @test_logs (:warn, r"A parallel executor was provided") run!(mapping, meteo_day) - - # If we provide a serial executor, it works without a warning: - outputs = @test_nowarn run!(mapping, meteo_day, executor=SequentialEx()) - - @test mean(outputs[:aPPFD]) ≈ 9.511021781482347 - @test mean(outputs[:LAI]) ≈ 1.098492557536525 - @test outputs[:biomass][end] ≈ 1041.4687939085675 rtol = 1e-4 + run!(full_scene) + full_status = only(scene_objects(full_scene; scale=:Plant)).status + @test full_status.LAI > 0.0 + @test full_status.aPPFD > 0.0 + @test full_status.biomass ≈ rue * full_status.aPPFD end diff --git a/test/test-unified-scene-object-api.jl b/test/test-unified-scene-object-api.jl index cf0046f65..741df023a 100644 --- a/test/test-unified-scene-object-api.jl +++ b/test/test-unified-scene-object-api.jl @@ -322,6 +322,22 @@ PlantSimEngine.dep(::SceneObjectSignalCallerModel) = ( signal=Call(process=:scene_object_signal_source), ) +PlantSimEngine.@process "scene_object_manual_pair_caller" verbose = false +struct SceneObjectManualPairCallerModel <: + AbstractScene_Object_Manual_Pair_CallerModel end +PlantSimEngine.inputs_(::SceneObjectManualPairCallerModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneObjectManualPairCallerModel) = NamedTuple() +function PlantSimEngine.run!( + ::SceneObjectManualPairCallerModel, + models, + status, + meteo, + constants=nothing, + extra=nothing, +) + return nothing +end + function PlantSimEngine.run!(::SceneObjectSignalCallerModel, models, status, meteo, constants=nothing, extra=nothing) target = call_target(extra, "signal") run_call!(target; publish=true) @@ -1263,47 +1279,15 @@ end @test environment_config(spec) isa PlantSimEngine.EnvironmentConfig @test environment_config(spec).config.provider == :global - # The old multirate metadata stays available while the compiler is migrated. - legacy_and_unified = spec |> - PlantSimEngine.InputBindings(; var1=(process=:process1, var=:var3)) |> - OutputRouting(; var3=:stream_only) |> - PlantSimEngine.ScopeModel(:plant) |> - Updates(:var3; after=:process1) - @test input_bindings(legacy_and_unified).var1.process == :process1 - @test output_routing(legacy_and_unified).var3 == :stream_only - @test model_scope(legacy_and_unified) == :plant - @test updates(legacy_and_unified)[1].after == (:process1,) - @test value_inputs(legacy_and_unified) == value_inputs(spec) - @test model_calls(legacy_and_unified) == model_calls(spec) - - rows = explain_model_specs(Dict(:Leaf => (spec,)); io=IOBuffer()) - @test length(rows) == 1 - @test rows[1].application_name == :leaf_energy - @test rows[1].applies_to === applies_to(spec) - @test rows[1].value_inputs == value_inputs(spec) - @test rows[1].input_origins == PlantSimEngine.input_origins(spec) - @test rows[1].model_calls == model_calls(spec) - @test rows[1].call_origins == PlantSimEngine.call_origins(spec) - @test rows[1].environment === environment_config(spec) - leaf_assim = ModelSpec(ToyAssimModel()) |> AppliesTo(Many(scale=:Leaf)) |> Inputs(:soil_water_content => One(scale=:Soil, var=:soil_water_content)) - @test length(PlantSimEngine.get_mapped_variables(leaf_assim)) == 1 - - mapping = Dict( - :Leaf => (leaf_assim,), - :Soil => (ToySoilWaterModel(),), - ) - resolved = resolved_model_specs(mapping) - binding = input_bindings(resolved[:Leaf][:carbon_assimilation]).soil_water_content - @test binding.scale == :Soil - @test binding.var == :soil_water_content - @test binding.process == :soil_water + @test value_inputs(leaf_assim).soil_water_content.criteria.scale == :Soil + @test value_inputs(leaf_assim).soil_water_content.criteria.var == + :soil_water_content rich_selector_spec = ModelSpec(ToyAssimModel()) |> Inputs(:soil_water_content => One(kind=:soil, scale=:Soil, var=:soil_water_content)) - @test isempty(PlantSimEngine.get_mapped_variables(rich_selector_spec)) @test value_inputs(rich_selector_spec).soil_water_content.criteria.kind == :soil default_input_spec = ModelSpec(SceneObjectDefaultInputConsumerModel()) @@ -1312,16 +1296,12 @@ end :model_default @test value_inputs(default_input_spec).leaf_carbon.criteria.within isa Self @test !haskey(dep(default_input_spec), :leaf_carbon) - @test length(PlantSimEngine.get_mapped_variables(default_input_spec)) == 1 override_input_spec = ModelSpec(SceneObjectDefaultInputConsumerModel()) |> Inputs(:leaf_carbon => Many(scale=:Leaf, var=:carbon_override)) @test value_inputs(override_input_spec).leaf_carbon.criteria.var == :carbon_override @test PlantSimEngine.input_origins(override_input_spec).leaf_carbon == :model_spec - mapped = only(PlantSimEngine.get_mapped_variables(override_input_spec)) - @test first(mapped) == :leaf_carbon - @test last(mapped) == [:Leaf => :carbon_override] default_input_origin_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), @@ -1369,6 +1349,35 @@ end @test model_calls(override_call_spec).stomata.criteria.process == :water_status @test isempty(dep(override_call_spec)) + manual_child_scene = Scene( + Object( + :manual_child_leaf; + scale=:Leaf, + status=Status(signal=0.0, observed_signal=0.0), + ); + applications=( + ModelSpec(SceneObjectManualPairCallerModel(); name=:manual_parent) |> + AppliesTo(One(scale=:Leaf)) |> + Calls( + :source => One(scale=:Leaf, application=:manual_source), + :consumer => One(scale=:Leaf, application=:manual_consumer), + ), + ModelSpec(SceneObjectSignalSetModel(1.0); name=:root_source) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(SceneObjectSignalSetModel(2.0); name=:manual_source) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(SceneObjectSignalConsumerModel(); name=:manual_consumer) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + manual_child_compiled = compile_scene(manual_child_scene) + @test isempty( + filter( + binding -> binding.application_id == :manual_consumer, + manual_child_compiled.input_bindings, + ), + ) + compiled_specs = ( ModelSpec(SceneObjectStomataModel(); name=:stomata) |> AppliesTo(Many(scale=:Leaf)), @@ -1873,6 +1882,32 @@ end if row.application_id == :cycle_b ).retention_steps == 2.0 + lagged_external_state_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene), + Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:plant_1, status=Status(signal=4.0)), + Object(:leaf_2; scale=:Leaf, kind=:plant, parent=:plant_1, status=Status(signal=6.0)); + applications=( + ModelSpec(SceneObjectPlantSignalSumModel(); name=:plant_signal_sum) |> + AppliesTo(One(scale=:Plant)) |> + Inputs( + PreviousTimeStep(:signals) => Many( + scale=:Leaf, + within=Self(), + var=:signal, + ), + ), + ), + ) + lagged_external_binding = only( + row for row in explain_bindings(refresh_bindings!(lagged_external_state_scene)) + if row.application_id == :plant_signal_sum && row.input == :signals + ) + @test lagged_external_binding.carrier_kind == :temporal_stream + @test isempty(lagged_external_binding.source_application_ids) + run!(lagged_external_state_scene; steps=2) + @test only(scene_objects(lagged_external_state_scene; scale=:Plant)).status.signal_total == 10.0 + mismatched_lag_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), Object( diff --git a/test/test-updates.jl b/test/test-updates.jl index 740c285ea..eae12d54c 100644 --- a/test/test-updates.jl +++ b/test/test-updates.jl @@ -37,50 +37,70 @@ function PlantSimEngine.run!(::UpdateBiomassObserverModel, models, status, meteo return nothing end -@testset "ModelSpec Updates" begin - meteo = Atmosphere(T=20.0, Rh=0.65, Wind=1.0, duration=Dates.Hour(1)) +function update_scene(applications...) + Scene( + Object( + :leaf; + scale=:Leaf, + status=Status(leaf_biomass=1.0, observed_biomass=-1.0), + ); + applications=applications, + environment=Atmosphere( + T=20.0, + Rh=0.65, + Wind=1.0, + duration=Dates.Hour(1), + ), + ) +end - @test_throws "Ambiguous canonical writers" PlantSimEngine.ModelMapping( - UpdateCarbonAllocationModel(), - UpdateLeafPruningModel(), - status=(leaf_biomass=1.0,), +@testset "ModelSpec Updates" begin + @test_throws "Ambiguous canonical writers" compile_scene( + update_scene( + ModelSpec(UpdateCarbonAllocationModel()) |> AppliesTo(One(scale=:Leaf)), + ModelSpec(UpdateLeafPruningModel()) |> AppliesTo(One(scale=:Leaf)), + ), ) - mapping = PlantSimEngine.ModelMapping( - UpdateCarbonAllocationModel(), + scene = update_scene( + ModelSpec(UpdateCarbonAllocationModel()) |> AppliesTo(One(scale=:Leaf)), ModelSpec(UpdateLeafPruningModel()) |> + AppliesTo(One(scale=:Leaf)) |> Updates(:leaf_biomass; after=:update_carbon_allocation), - UpdateBiomassObserverModel(), - status=(leaf_biomass=1.0, observed_biomass=-1.0), + ModelSpec(UpdateBiomassObserverModel()) |> AppliesTo(One(scale=:Leaf)), ) + run!(scene) + leaf = only(scene_objects(scene; scale=:Leaf)) + @test leaf.status.leaf_biomass == 0.0 + @test leaf.status.observed_biomass == 0.0 - outputs = run!(mapping, meteo; executor=SequentialEx()) - @test only(outputs[:leaf_biomass]) == 0.0 - @test only(outputs[:observed_biomass]) == 0.0 - - graph_nodes = PlantSimEngine.traverse_dependency_graph(dep(mapping), false) - pruning_node = only(filter(node -> node.process == :update_leaf_pruning, graph_nodes)) - @test any(parent -> parent.process == :update_carbon_allocation, pruning_node.parent) - - @test_throws "without an ordering relation" PlantSimEngine.ModelMapping( - UpdateCarbonAllocationModel(), - ModelSpec(UpdateLeafPruningModel()) |> - Updates(:leaf_biomass; after=:update_carbon_allocation), - ModelSpec(UpdateLeafSenescenceModel()) |> - Updates(:leaf_biomass; after=:update_carbon_allocation), - status=(leaf_biomass=1.0,), + @test_throws "without an ordering relation" compile_scene( + update_scene( + ModelSpec(UpdateCarbonAllocationModel()) |> AppliesTo(One(scale=:Leaf)), + ModelSpec(UpdateLeafPruningModel()) |> + AppliesTo(One(scale=:Leaf)) |> + Updates(:leaf_biomass; after=:update_carbon_allocation), + ModelSpec(UpdateLeafSenescenceModel()) |> + AppliesTo(One(scale=:Leaf)) |> + Updates(:leaf_biomass; after=:update_carbon_allocation), + ), ) - ordered_updates = PlantSimEngine.ModelMapping( - UpdateCarbonAllocationModel(), + ordered = update_scene( + ModelSpec(UpdateCarbonAllocationModel()) |> AppliesTo(One(scale=:Leaf)), ModelSpec(UpdateLeafSenescenceModel()) |> + AppliesTo(One(scale=:Leaf)) |> Updates(:leaf_biomass; after=:update_carbon_allocation), ModelSpec(UpdateLeafPruningModel()) |> - Updates(:leaf_biomass; after=(:update_carbon_allocation, :update_leaf_senescence)), - UpdateBiomassObserverModel(), - status=(leaf_biomass=1.0, observed_biomass=-1.0), + AppliesTo(One(scale=:Leaf)) |> + Updates( + :leaf_biomass; + after=(:update_carbon_allocation, :update_leaf_senescence), + ), + ModelSpec(UpdateBiomassObserverModel()) |> AppliesTo(One(scale=:Leaf)), ) - ordered_outputs = run!(ordered_updates, meteo; executor=SequentialEx()) - @test only(ordered_outputs[:leaf_biomass]) == 0.0 - @test only(ordered_outputs[:observed_biomass]) == 0.0 + run!(ordered) + ordered_leaf = only(scene_objects(ordered; scale=:Leaf)) + @test ordered_leaf.status.leaf_biomass == 0.0 + @test ordered_leaf.status.observed_biomass == 0.0 end From 21a1cd646d00266510e9bb7e111562521a98bc65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Thu, 18 Jun 2026 14:20:23 +0200 Subject: [PATCH 22/33] Add `add_organ!` again for an easier high-level function --- AGENTS.md | 5 + docs/src/API/API_public.md | 1 + docs/src/migration_scene_object.md | 13 +++ docs/src/model_execution.md | 19 +++- src/PlantSimEngine.jl | 2 +- src/scene_object_api.jl | 155 +++++++++++++++++++++++--- test/test-unified-scene-object-api.jl | 36 ++++++ 7 files changed, 214 insertions(+), 17 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 14b480dc6..3a4da4ff0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -133,7 +133,12 @@ know its scenario timestep unless the scientific model explicitly requires it. ## Lifecycle +- `add_organ!` is the high-level operation for MTG-backed growth. It creates + the node, reuses the scene's MTG status policy, applies initial values, + attaches the status, and registers the object. - `register_object!`, `remove_object!`, and `reparent_object!` mutate topology. +- Use `register_object!` directly only when the caller already owns a fully + initialized `Object`. - Structural changes refresh application targets, value carriers, call targets, writer checks, and schedules between timesteps. - Geometry changes refresh only affected environment bindings when possible. diff --git a/docs/src/API/API_public.md b/docs/src/API/API_public.md index f20cfa503..629d76617 100644 --- a/docs/src/API/API_public.md +++ b/docs/src/API/API_public.md @@ -40,6 +40,7 @@ - `objects_from_mtg` and `Scene(mtg; ...)` adapt an MTG into the object registry. +- `add_organ!` creates and initializes a new organ in an MTG-backed scene. - `register_object!`, `remove_object!`, and `reparent_object!` change topology. - `move_object!` and `update_geometry!` change spatial state. diff --git a/docs/src/migration_scene_object.md b/docs/src/migration_scene_object.md index 1ebe0af84..48a21891c 100644 --- a/docs/src/migration_scene_object.md +++ b/docs/src/migration_scene_object.md @@ -367,11 +367,24 @@ Use the public lifecycle operations: ```julia register_object!(scene, new_leaf; parent=:plant_1) +new_leaf_status = add_organ!( + parent_node, + scene, + :+, + :Leaf, + 3; + initial_status=(biomass=0.0,), +) remove_object!(scene, :old_leaf) reparent_object!(scene, :leaf_2, :axis_3) move_object!(scene, :leaf_3, new_geometry) ``` +For MTG-backed growth, prefer `add_organ!`: it creates the MTG node and its +scene object together and reuses the status initialization policy from +`Scene(mtg; status=...)`. Use `register_object!` when adapting another topology +backend or when a complete `Object` already exists. + Structural changes refresh application targets, input carriers, call targets, writer validation, and schedules before the next timestep. Geometry-only changes refresh environment bindings without rebuilding unrelated structural diff --git a/docs/src/model_execution.md b/docs/src/model_execution.md index eda6a90ab..c265c17dd 100644 --- a/docs/src/model_execution.md +++ b/docs/src/model_execution.md @@ -318,12 +318,27 @@ updated between or during timesteps: ```julia register_object!(scene, Object(:new_leaf; scale=:Leaf); parent=:plant_1) +leaf_status = add_organ!( + parent_node, + scene, + :+, + :Leaf, + 3; + index=4, + attributes=(area=0.01,), + initial_status=(biomass=0.0,), +) remove_object!(scene, :old_leaf) -reparent_object!(scene, :leaf_3; parent=:plant_2) -move_object!(scene, :leaf_4; geometry=new_geometry) +reparent_object!(scene, :leaf_3, :plant_2) +move_object!(scene, :leaf_4, new_geometry) update_geometry!(scene, :leaf_5, new_geometry) ``` +Use `add_organ!` for an MTG-backed scene. It creates the MTG node, initializes +and attaches its `Status` with the scene's MTG policy, registers the scene +object, and invalidates the affected bindings. `register_object!` is the +low-level operation for callers that already own a complete `Object`. + Structural changes invalidate compiled object/model bindings. Movement and geometry changes invalidate environment bindings without rebuilding structural input carriers. The next `run!(scene)` step refreshes the necessary caches. diff --git a/src/PlantSimEngine.jl b/src/PlantSimEngine.jl index e09bf7eda..6e8913140 100644 --- a/src/PlantSimEngine.jl +++ b/src/PlantSimEngine.jl @@ -73,7 +73,7 @@ export SchedulePolicy, HoldLast, Interpolate, Integrate, Aggregate export AbstractTimeReducer, MeanWeighted, MeanReducer, SumReducer, MinReducer, MaxReducer, FirstReducer, LastReducer, RadiationEnergy export OutputRequest, collect_outputs export Scene, Object, ObjectId, SceneRegistry, ObjectTemplate, ObjectInstance, Override -export register_object!, remove_object!, reparent_object!, move_object!, update_geometry!, refresh_bindings! +export add_organ!, register_object!, remove_object!, reparent_object!, move_object!, update_geometry!, refresh_bindings! export bindings_dirty, environment_bindings_dirty, scene_revision, environment_revision export compiled_bindings, compiled_environment_bindings, mark_environment_binding_dirty! export refresh_environment_bindings!, compile_environment_bindings, bind_environment diff --git a/src/scene_object_api.jl b/src/scene_object_api.jl index 2b7ae41ee..30dadb163 100644 --- a/src/scene_object_api.jl +++ b/src/scene_object_api.jl @@ -200,11 +200,22 @@ SceneRegistry() = SceneRegistry( Dict{Symbol,ObjectId}(), ) -mutable struct Scene{R,A,E,I} +struct MTGObjectAdapter{I,S,K,SP,N,G,ST} + id::I + scale::S + kind::K + species::SP + name::N + geometry::G + status::ST +end + +mutable struct Scene{R,A,E,I,SA} registry::R applications::A environment::E instances::I + source_adapter::SA binding_cache::Any environment_binding_cache::Any bindings_dirty::Bool @@ -332,6 +343,7 @@ function Scene( applications=(), instances=(), environment=nothing, + source_adapter=nothing, ) objects, mounted_instances = _collect_scene_items(items, instances) instance_ids = _prepare_object_instances!(objects, mounted_instances) @@ -343,6 +355,7 @@ function Scene( normalized_applications, environment, mounted_instances, + source_adapter, nothing, nothing, true, @@ -381,21 +394,26 @@ function objects_from_mtg( geometry=node -> _mtg_attribute(node, :geometry, nothing), status=node -> _mtg_attribute(node, :plantsimengine_status, nothing), ) + adapter = MTGObjectAdapter(id, scale, kind, species, name, geometry, status) + return _objects_from_mtg(root, adapter) +end + +function _objects_from_mtg(root::MultiScaleTreeGraph.Node, adapter::MTGObjectAdapter) objects = Object[] MultiScaleTreeGraph.traverse!(root) do node node_parent = parent(node) - parent_id = node === root || isnothing(node_parent) ? nothing : id(node_parent) + parent_id = node === root || isnothing(node_parent) ? nothing : adapter.id(node_parent) push!( objects, Object( - id(node); - scale=scale(node), - kind=kind(node), - species=species(node), - name=name(node), + adapter.id(node); + scale=adapter.scale(node), + kind=adapter.kind(node), + species=adapter.species(node), + name=adapter.name(node), parent=parent_id, - geometry=geometry(node), - status=status(node), + geometry=adapter.geometry(node), + status=adapter.status(node), ), ) end @@ -404,24 +422,32 @@ end """ Scene(root::MultiScaleTreeGraph.Node; applications=(), instances=(), - environment=nothing, kwargs...) + environment=nothing, id=node_id, scale=symbol, status=..., ...) -Build a unified scene directly from an MTG subtree. Extra keyword arguments -are forwarded to [`objects_from_mtg`](@ref). +Build a unified scene directly from an MTG subtree. The MTG accessors are +retained and reused by [`add_organ!`](@ref) when the topology grows. """ function Scene( root::MultiScaleTreeGraph.Node; applications=(), instances=(), environment=nothing, - kwargs..., + id=node_id, + scale=symbol, + kind=node -> _mtg_attribute(node, :kind, nothing), + species=node -> _mtg_attribute(node, :species, nothing), + name=node -> _mtg_attribute(node, :name, nothing), + geometry=node -> _mtg_attribute(node, :geometry, nothing), + status=node -> _mtg_attribute(node, :plantsimengine_status, nothing), ) - objects = objects_from_mtg(root; kwargs...) + adapter = MTGObjectAdapter(id, scale, kind, species, name, geometry, status) + objects = _objects_from_mtg(root, adapter) return Scene( objects...; applications=applications, instances=instances, environment=environment, + source_adapter=adapter, ) end @@ -553,6 +579,104 @@ function register_object!(scene::Scene, object::Object; parent=object.parent) return object end +_runtime_scene(scene::Scene) = scene + +function _status_data!(data::Dict{Symbol,Any}, values) + values === nothing && return data + source = values isa Status ? NamedTuple(values) : values + source isa Union{NamedTuple,AbstractDict,Base.Pairs} || error( + "Initial organ status must be a `Status`, `NamedTuple`, `AbstractDict`, ", + "`Base.Pairs`, or `nothing`, got `$(typeof(values))`." + ) + for (key, value) in pairs(source) + Symbol(key) == :plantsimengine_status && continue + data[Symbol(key)] = value + end + return data +end + +function _organ_status(adapter::MTGObjectAdapter, node, initial_status) + adapted_status = adapter.status(node) + data = Dict{Symbol,Any}() + _status_data!(data, MultiScaleTreeGraph.node_attributes(node)) + _status_data!(data, adapted_status) + data[:node] = node + _status_data!(data, initial_status) + data[:node] = node + return Status((; data...)) +end + +""" + add_organ!(parent, runtime, link, symbol, scale; index=0, id, attributes=(), + initial_status=(), kind=nothing, species=nothing, name=nothing) + +Create an MTG node and register its corresponding scene object as one operation. +`runtime` may be a [`Scene`](@ref), [`SceneRunContext`](@ref), or +[`SceneSimulation`](@ref). The scene reuses the MTG accessors and status +initializer supplied when it was constructed, then overlays `initial_status`. + +This is the public growth API. [`register_object!`](@ref) remains the low-level +registry operation for callers that already own a fully initialized `Object`. +""" +function add_organ!( + parent_node::MultiScaleTreeGraph.Node, + runtime, + link, + organ_symbol, + mtg_scale::Integer; + index::Integer=0, + id=MultiScaleTreeGraph.new_id(MultiScaleTreeGraph.get_root(parent_node)), + attributes=NamedTuple(), + initial_status=NamedTuple(), + kind=nothing, + species=nothing, + name=nothing, +) + scene = _runtime_scene(runtime) + adapter = scene.source_adapter + adapter isa MTGObjectAdapter || error( + "`add_organ!` requires a scene constructed from an MTG. Use ", + "`register_object!` for scenes built directly from `Object` values." + ) + parent_id = ObjectId(adapter.id(parent_node)) + _scene_object(scene, parent_id) + root = MultiScaleTreeGraph.get_root(parent_node) + isnothing(MultiScaleTreeGraph.get_node(root, Int(id))) || error( + "MTG node id `$(id)` already exists." + ) + + node = MultiScaleTreeGraph.Node( + Int(id), + parent_node, + MultiScaleTreeGraph.NodeMTG( + Symbol(link), + Symbol(organ_symbol), + Int(index), + Int(mtg_scale), + ), + attributes, + ) + try + status = _organ_status(adapter, node, initial_status) + node[:plantsimengine_status] = status + object = Object( + adapter.id(node); + scale=adapter.scale(node), + kind=isnothing(kind) ? adapter.kind(node) : kind, + species=isnothing(species) ? adapter.species(node) : species, + name=isnothing(name) ? adapter.name(node) : name, + parent=parent_id, + geometry=adapter.geometry(node), + status=status, + ) + register_object!(scene, object) + return status + catch + MultiScaleTreeGraph.delete_node!(node) + rethrow() + end +end + function _remove_child_link!(scene::Scene, parent_id, child_id::ObjectId) isnothing(parent_id) && return nothing parent_object = _scene_object(scene, parent_id) @@ -3188,6 +3312,9 @@ struct SceneSimulation{S,CS,EB,EP,OR,TS,R} output_requests::R end +_runtime_scene(context::SceneRunContext) = context.compiled.scene +_runtime_scene(simulation::SceneSimulation) = simulation.scene + scene_outputs(sim::SceneSimulation) = sim.temporal_streams function _compiled_application_by_id(compiled::CompiledScene, id::Symbol) diff --git a/test/test-unified-scene-object-api.jl b/test/test-unified-scene-object-api.jl index 741df023a..f055175e8 100644 --- a/test/test-unified-scene-object-api.jl +++ b/test/test-unified-scene-object-api.jl @@ -774,6 +774,42 @@ end run!(mtg_scene) @test only(scene_objects(mtg_scene; scale=:Leaf)).status.signal == 3.0 + new_leaf_status = add_organ!( + mtg_plant, + mtg_scene, + :+, + :Leaf, + 2; + index=2, + id=4, + attributes=(signal=4.0, color=:green), + initial_status=(signal=5.0, age=1), + kind=:plant, + ) + @test new_leaf_status.node[:plantsimengine_status] === new_leaf_status + @test new_leaf_status.signal == 5.0 + @test new_leaf_status.color == :green + @test new_leaf_status.age == 1 + new_leaf_object = only( + object for object in scene_objects(mtg_scene; scale=:Leaf) + if object.id == ObjectId(:leaf_4) + ) + @test new_leaf_object.status === new_leaf_status + @test new_leaf_object.parent == ObjectId(:plant_2) + @test bindings_dirty(mtg_scene) + + child_count = length(MultiScaleTreeGraph.children(mtg_plant)) + @test_throws ErrorException add_organ!( + mtg_plant, + mtg_scene, + :+, + :Leaf, + 2; + index=3, + id=4, + ) + @test length(MultiScaleTreeGraph.children(mtg_plant)) == child_count + scene = Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:plant_1; scale=:Plant, kind=:plant, species=:oil_palm, parent=:scene), From 2144f5ef6c5181e47953bc49aa7cc1055342b706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Fri, 19 Jun 2026 20:32:54 +0200 Subject: [PATCH 23/33] Adapt past tests and doc to the new API --- .gitignore | 5 +- AGENTS.md | 22 +- Project.toml | 11 + README.md | 2 +- benchmark/Project.toml | 4 +- benchmark/benchmarks.jl | 48 +- benchmark/test-PSE-benchmark.jl | 154 +- benchmark/test-multirate-buffer-benchmark.jl | 100 +- benchmark/test-plantbiophysics.jl | 236 +- benchmark/test-xpalm.jl | 111 +- benchmark/test/runtests.jl | 39 + docs/make.jl | 62 +- docs/paper/paper.md | 3 +- docs/src/API/API_public.md | 40 +- docs/src/API/public_symbols.md | 107 + docs/src/agent_skill.md | 4 + docs/src/dev/code_cleanup_audit.md | 177 +- docs/src/dev/maespa_scene_handoff.md | 4 +- .../public_api_refinement_completion_audit.md | 43 + .../dev/public_api_refinement_decisions.md | 108 + docs/src/dev/release_notes_handoff.md | 155 +- .../unified_scene_object_completion_audit.md | 56 +- docs/src/dev/unified_scene_object_design.md | 88 +- ...nified_scene_object_implementation_plan.md | 265 +- docs/src/developers.md | 5 +- docs/src/guides/coupling.md | 64 + docs/src/guides/data/environment_inputs.md | 11 + docs/src/guides/data/forcing_observations.md | 10 + docs/src/guides/data/numerical_reliability.md | 11 + docs/src/guides/data/outputs_plotting.md | 56 + .../guides/modelers/port_existing_model.md | 40 + docs/src/guides/modelers/stateful_models.md | 11 + docs/src/guides/multiscale/concepts.md | 31 + docs/src/guides/multiscale/from_one_object.md | 10 + docs/src/guides/multiscale/import_mtg.md | 10 + docs/src/guides/multiscale/manual_calls.md | 12 + docs/src/guides/multiscale/value_coupling.md | 10 + .../multiscale/visualizing_structure.md | 6 + .../guides/time/advanced_time_environment.md | 12 + docs/src/guides/time/hourly_daily_weekly.md | 58 + docs/src/guides/time/multirate_concepts.md | 13 + docs/src/index.md | 56 +- docs/src/introduction/why_plantsimengine.md | 5 +- docs/src/migration_scene_object.md | 87 +- docs/src/model_execution.md | 85 +- docs/src/prerequisites/key_concepts.md | 2 +- docs/src/scene_object/quickstart.md | 45 +- docs/src/step_by_step/advanced_coupling.md | 9 +- .../step_by_step/detailed_first_example.md | 11 +- docs/src/step_by_step/implement_a_model.md | 12 +- docs/src/step_by_step/model_switching.md | 2 +- .../step_by_step/quick_and_dirty_examples.md | 6 +- .../src/step_by_step/simple_model_coupling.md | 5 +- docs/src/troubleshooting/common_errors.md | 13 + docs/src/troubleshooting/dependency_cycles.md | 26 + docs/src/troubleshooting/runtime_contracts.md | 11 + .../tutorials/growing_plant/part1_growth.md | 32 + .../growing_plant/part2_roots_water.md | 34 + .../growing_plant/part3_debugging.md | 32 + docs/test/runtests.jl | 8 + examples/ToyCAllocationModel.jl | 3 +- examples/ToyLAIModel.jl | 9 +- examples/ToyRUEGrowthModel.jl | 4 +- examples/ToySoilModel.jl | 5 +- examples/maespa_scene_example.jl | 19 +- ext/PlantSimEngineGraphEditorExt.jl | 862 +++ frontend/README.md | 24 + frontend/dist/.vite/manifest.json | 11 + frontend/dist/assets/index-CRLmU9Qw.css | 1 + frontend/dist/assets/index-DryeyTC8.js | 38 + frontend/dist/index.html | 13 + frontend/e2e/graph-editor.spec.ts | 155 + frontend/e2e/graphEditorServer.ts | 82 + frontend/index.html | 12 + frontend/package-lock.json | 2618 +++++++++ frontend/package.json | 33 + frontend/playwright.config.ts | 24 + frontend/src/App.test.ts | 61 + frontend/src/App.tsx | 807 +++ frontend/src/ApplicationForm.tsx | 146 + frontend/src/BindingForm.tsx | 71 + frontend/src/DependencyEdge.tsx | 54 + frontend/src/ErrorBoundary.test.ts | 9 + frontend/src/ErrorBoundary.tsx | 24 + frontend/src/ModelNode.tsx | 212 + frontend/src/ObjectForm.tsx | 72 + frontend/src/OverrideForm.tsx | 71 + frontend/src/elkjs.d.ts | 3 + frontend/src/layout.ts | 70 + frontend/src/main.tsx | 10 + frontend/src/nodeSizing.ts | 12 + frontend/src/sampleGraph.ts | 83 + frontend/src/styles.css | 2617 +++++++++ frontend/src/types.ts | 256 + frontend/tsconfig.json | 20 + frontend/vite.config.ts | 11 + skills/plantsimengine/SKILL.md | 80 +- src/ModelSpec.jl | 13 +- src/PlantSimEngine.jl | 92 +- src/model_discovery.jl | 307 ++ src/scene_object/compilation.jl | 1540 ++++++ src/scene_object/environment_bindings.jl | 398 ++ src/scene_object/registry_topology.jl | 1065 ++++ src/scene_object/runtime_outputs.jl | 1611 ++++++ src/scene_object/scenario_dsl.jl | 130 + src/scene_object/selectors.jl | 672 +++ src/scene_object_api.jl | 4800 +---------------- src/time/runtime/clocks.jl | 13 +- src/time/runtime/output_export.jl | 28 +- src/visualization/scene_graph_editor_api.jl | 857 +++ src/visualization/scene_graph_view.jl | 1089 ++++ test/Project.toml | 2 + test/fixtures/graph_editor_e2e_server.jl | 31 + test/runtests.jl | 73 +- test/test-TimeStepTable.jl | 4 +- test/test-model-contract.jl | 42 + test/test-scene-api-stabilization.jl | 365 ++ test/test-scene-binding-inference.jl | 102 + test/test-scene-configuration-errors.jl | 78 + test/test-scene-graph-editor-extension.jl | 223 + test/test-scene-graph-view.jl | 431 ++ test/test-scene-hard-calls.jl | 200 + test/test-scene-meteo-sampling.jl | 82 + test/test-scene-multirate-integration.jl | 88 + test/test-scene-numerical-parity.jl | 240 + test/test-scene-output-boundaries.jl | 66 + test/test-scene-runtime-matrix.jl | 99 + test/test-scene-status-initialization.jl | 46 + test/test-scene-temporal-reducers.jl | 82 + test/test-scene-time-validation.jl | 61 + test/test-unified-scene-object-api.jl | 318 +- test/test-updates.jl | 24 +- 132 files changed, 20641 insertions(+), 5928 deletions(-) create mode 100644 benchmark/test/runtests.jl create mode 100644 docs/src/API/public_symbols.md create mode 100644 docs/src/dev/public_api_refinement_completion_audit.md create mode 100644 docs/src/dev/public_api_refinement_decisions.md create mode 100644 docs/src/guides/coupling.md create mode 100644 docs/src/guides/data/environment_inputs.md create mode 100644 docs/src/guides/data/forcing_observations.md create mode 100644 docs/src/guides/data/numerical_reliability.md create mode 100644 docs/src/guides/data/outputs_plotting.md create mode 100644 docs/src/guides/modelers/port_existing_model.md create mode 100644 docs/src/guides/modelers/stateful_models.md create mode 100644 docs/src/guides/multiscale/concepts.md create mode 100644 docs/src/guides/multiscale/from_one_object.md create mode 100644 docs/src/guides/multiscale/import_mtg.md create mode 100644 docs/src/guides/multiscale/manual_calls.md create mode 100644 docs/src/guides/multiscale/value_coupling.md create mode 100644 docs/src/guides/multiscale/visualizing_structure.md create mode 100644 docs/src/guides/time/advanced_time_environment.md create mode 100644 docs/src/guides/time/hourly_daily_weekly.md create mode 100644 docs/src/guides/time/multirate_concepts.md create mode 100644 docs/src/troubleshooting/common_errors.md create mode 100644 docs/src/troubleshooting/dependency_cycles.md create mode 100644 docs/src/troubleshooting/runtime_contracts.md create mode 100644 docs/src/tutorials/growing_plant/part1_growth.md create mode 100644 docs/src/tutorials/growing_plant/part2_roots_water.md create mode 100644 docs/src/tutorials/growing_plant/part3_debugging.md create mode 100644 docs/test/runtests.jl create mode 100644 ext/PlantSimEngineGraphEditorExt.jl create mode 100644 frontend/README.md create mode 100644 frontend/dist/.vite/manifest.json create mode 100644 frontend/dist/assets/index-CRLmU9Qw.css create mode 100644 frontend/dist/assets/index-DryeyTC8.js create mode 100644 frontend/dist/index.html create mode 100644 frontend/e2e/graph-editor.spec.ts create mode 100644 frontend/e2e/graphEditorServer.ts create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/playwright.config.ts create mode 100644 frontend/src/App.test.ts create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/ApplicationForm.tsx create mode 100644 frontend/src/BindingForm.tsx create mode 100644 frontend/src/DependencyEdge.tsx create mode 100644 frontend/src/ErrorBoundary.test.ts create mode 100644 frontend/src/ErrorBoundary.tsx create mode 100644 frontend/src/ModelNode.tsx create mode 100644 frontend/src/ObjectForm.tsx create mode 100644 frontend/src/OverrideForm.tsx create mode 100644 frontend/src/elkjs.d.ts create mode 100644 frontend/src/layout.ts create mode 100644 frontend/src/main.tsx create mode 100644 frontend/src/nodeSizing.ts create mode 100644 frontend/src/sampleGraph.ts create mode 100644 frontend/src/styles.css create mode 100644 frontend/src/types.ts create mode 100644 frontend/tsconfig.json create mode 100644 frontend/vite.config.ts create mode 100644 src/model_discovery.jl create mode 100644 src/scene_object/compilation.jl create mode 100644 src/scene_object/environment_bindings.jl create mode 100644 src/scene_object/registry_topology.jl create mode 100644 src/scene_object/runtime_outputs.jl create mode 100644 src/scene_object/scenario_dsl.jl create mode 100644 src/scene_object/selectors.jl create mode 100644 src/visualization/scene_graph_editor_api.jl create mode 100644 src/visualization/scene_graph_view.jl create mode 100644 test/fixtures/graph_editor_e2e_server.jl create mode 100644 test/test-model-contract.jl create mode 100644 test/test-scene-api-stabilization.jl create mode 100644 test/test-scene-binding-inference.jl create mode 100644 test/test-scene-configuration-errors.jl create mode 100644 test/test-scene-graph-editor-extension.jl create mode 100644 test/test-scene-graph-view.jl create mode 100644 test/test-scene-hard-calls.jl create mode 100644 test/test-scene-meteo-sampling.jl create mode 100644 test/test-scene-multirate-integration.jl create mode 100644 test/test-scene-numerical-parity.jl create mode 100644 test/test-scene-output-boundaries.jl create mode 100644 test/test-scene-runtime-matrix.jl create mode 100644 test/test-scene-status-initialization.jl create mode 100644 test/test-scene-temporal-reducers.jl create mode 100644 test/test-scene-time-validation.jl diff --git a/.gitignore b/.gitignore index f81ea322b..e6b06a6cf 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,7 @@ docs/Manifest.toml test/Manifest.toml docs/build/ benchmark/Manifest.toml -frontend \ No newline at end of file +/frontend/node_modules/ +/frontend/.vite/ +/frontend/test-results/ +/frontend/playwright-report/ diff --git a/AGENTS.md b/AGENTS.md index 3a4da4ff0..5b60c144f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -70,6 +70,7 @@ Scope and topology: - `SceneScope()` - `Self()`: the current object +- `Subtree()`: the current object and its descendants - `SelfPlant()`: the current plant instance/root - `Ancestor(...)` - `Scope(name)` @@ -146,7 +147,9 @@ know its scenario timestep unless the scientific model explicitly requires it. ## Outputs -- `run!(scene)` returns `SceneSimulation`. +- `run!(scene; outputs=:none)` starts a fresh timeline and returns + `SceneSimulation`; use `outputs=:all` or output requests to retain streams. +- `continue!(simulation)` and `step!(simulation)` advance the same timeline. - `scene_outputs(sim)` exposes retained typed streams. - `OutputRequest` selects retained/resampled outputs. - `collect_outputs(sim)` materializes output rows. @@ -166,8 +169,18 @@ not overwrite each other. ## High-Signal Files -- `src/scene_object_api.jl`: registry, selectors, compilation, execution, - lifecycle, hard calls, temporal streams, and output collection. +- `src/scene_object_api.jl`: dependency-ordered include boundary for the sole + Scene/Object compiler and runtime. +- `src/scene_object/registry_topology.jl`: objects, registry, templates, + instances, overrides, topology, and lifecycle ownership. +- `src/scene_object/selectors.jl`: selector normalization and resolution. +- `src/scene_object/compilation.jl`: applications, carriers, calls, writer + validation, schedules, and structured compilation explanations. +- `src/scene_object/environment_bindings.jl`: global/spatial environment + bindings and invalidation. +- `src/scene_object/runtime_outputs.jl`: execution, temporal streams, hard-call + publication, retention, and output collection. +- `src/scene_object/scenario_dsl.jl`: small scenario construction helpers. - `src/ModelSpec.jl`: model application configuration. - `src/component_models/Status.jl`: reference-based status. - `src/component_models/RefVector.jl`: homogeneous reference vectors. @@ -175,7 +188,8 @@ not overwrite each other. - `src/time/runtime/clocks.jl`: Dates-based timing. - `src/time/runtime/meteo_sampling.jl`: weather sampling. - `src/time/runtime/environment_backends.jl`: environment backend contract. -- `test/test-unified-scene-object-api.jl`: primary behavioral coverage. +- `test/test-unified-scene-object-api.jl`: broad integration coverage. +- `test/test-scene-*.jl`: focused Scene/Object behavioral contracts. ## Change Checklist diff --git a/Project.toml b/Project.toml index db0b8d1a1..2cb6982f1 100644 --- a/Project.toml +++ b/Project.toml @@ -7,6 +7,8 @@ authors = ["Rémi Vezy and contributors"] CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a" MultiScaleTreeGraph = "dd4a991b-8a45-4075-bede-262ee62d5583" PlantMeteo = "4630fe09-e0fb-4da5-a846-781cb73437b6" @@ -14,10 +16,19 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" Term = "22787eb5-b846-44ae-b979-8e399b8463ab" +[weakdeps] +HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" + +[extensions] +PlantSimEngineGraphEditorExt = "HTTP" + [compat] CSV = "0.10" DataFrames = "1" Dates = "1.10" +InteractiveUtils = "1.11.0" +JSON = "1.6.1" +HTTP = "1" Markdown = "1.10" MultiScaleTreeGraph = "0.15.1" PlantMeteo = "0.8.2" diff --git a/README.md b/README.md index f872e1346..1432f5a7f 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ scene = Scene( ) sim = run!(scene; steps=30, constants=Constants()) -out = DataFrame(collect_outputs(sim; sink=nothing)) +out = collect_outputs(sim; sink=DataFrame) first(out, 6) ``` diff --git a/benchmark/Project.toml b/benchmark/Project.toml index 16cbac02d..f5cf121fc 100644 --- a/benchmark/Project.toml +++ b/benchmark/Project.toml @@ -13,5 +13,5 @@ XPalm = "6b523e1e-d512-416c-8e51-a8fbef0064e7" [sources] PlantSimEngine = {path = ".."} -XPalm = {rev = "main", url = "https://github.com/PalmStudio/XPalm.jl"} -PlantBiophysics = {rev = "master", url = "https://github.com/VEZY/PlantBiophysics.jl"} +XPalm = {path = "../../XPalm"} +PlantBiophysics = {path = "../../PlantBiophysics"} diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index f3bdc06f7..eac16a2e1 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -20,32 +20,40 @@ const SUITE = BenchmarkGroup() SUITE[suite_name] = BenchmarkGroup(["PSE", "PBP", "XPalm"]) # "PSE benchmark" -include("test-PSE-benchmark.jl") -SUITE[suite_name]["PSE"] = @benchmarkable do_benchmark_on_heavier_mtg() - -if isdefined(PlantSimEngine, :ModelSpec) # Only in new versions - include("test-multirate-buffer-benchmark.jl") - mtg_mr, mapping_mr, meteo_mr, reqs_mr, tracked_mr, nsteps_mr = setup_multirate_buffer_benchmark() - SUITE[suite_name]["PSE_multirate_status_tracked_run"] = @benchmarkable benchmark_multirate_status_tracked_run($mtg_mr, $mapping_mr, $meteo_mr, $tracked_mr, $nsteps_mr) - SUITE[suite_name]["PSE_multirate_output_request_run"] = @benchmarkable benchmark_multirate_output_request_run($mtg_mr, $mapping_mr, $meteo_mr, $reqs_mr, $tracked_mr, $nsteps_mr) -end +include(joinpath(@__DIR__, "test-PSE-benchmark.jl")) +SUITE[suite_name]["PSE"] = @benchmarkable benchmark_heavier_scene( + scene, + requests, + nsteps, +) setup = ((scene, requests, nsteps) = setup_heavier_scene_benchmark()) + +include(joinpath(@__DIR__, "test-multirate-buffer-benchmark.jl")) +SUITE[suite_name]["PSE_multirate_retain_all_run"] = @benchmarkable benchmark_multirate_retain_all_run( + scene, + nsteps, +) setup = ((scene, ignored_requests, nsteps) = setup_multirate_buffer_benchmark()) +SUITE[suite_name]["PSE_multirate_output_request_run"] = @benchmarkable benchmark_multirate_output_request_run( + scene, + requests, + nsteps, +) setup = ((scene, requests, nsteps) = setup_multirate_buffer_benchmark()) + # "PBP benchmark" -include("test-plantbiophysics.jl") +include(joinpath(@__DIR__, "test-plantbiophysics.jl")) SUITE[suite_name]["PBP"] = @benchmarkable benchmark_plantbiophysics() - -leaf, meteo = setup_benchmark_plantbiophysics_multitimestep() -SUITE[suite_name]["PBP_multiple_timesteps_MT"] = @benchmarkable benchmark_plantbiophysics_multitimestep_MT($leaf, $meteo) -SUITE[suite_name]["PBP_multiple_timesteps_ST"] = @benchmarkable benchmark_plantbiophysics_multitimestep_ST($leaf, $meteo) +SUITE[suite_name]["PBP_batch_run"] = @benchmarkable benchmark_plantbiophysics_batch( + scenes, +) setup = (scenes = setup_benchmark_plantbiophysics_batch()) # "XPalm benchmark" -include("test-xpalm.jl") +include(joinpath(@__DIR__, "test-xpalm.jl")) SUITE[suite_name]["XPalm_setup"] = @benchmarkable xpalm_default_param_create() seconds = 120 -palm, models, out_vars, meteo = xpalm_default_param_create() -sim_outputs = xpalm_default_param_run(palm, models, out_vars, meteo) - -SUITE[suite_name]["XPalm_run"] = @benchmarkable xpalm_default_param_run(palm, models, out_vars, meteo) setup = ((palm, models, out_vars, meteo) = xpalm_default_param_create()) -SUITE[suite_name]["XPalm_convert_outputs"] = @benchmarkable xpalm_default_param_convert_outputs($sim_outputs) +SUITE[suite_name]["XPalm_run"] = @benchmarkable xpalm_default_param_run( + scene, + requests, + nsteps, +) setup = ((scene, requests, nsteps) = xpalm_default_param_create()) #tune!(SUITE) #results = run(SUITE, verbose=true) diff --git a/benchmark/test-PSE-benchmark.jl b/benchmark/test-PSE-benchmark.jl index 60c6cfd78..f9b687232 100644 --- a/benchmark/test-PSE-benchmark.jl +++ b/benchmark/test-PSE-benchmark.jl @@ -18,7 +18,9 @@ ToyInternodeCrazyEmergence(; TT_emergence=300.0) = ToyInternodeCrazyEmergence(TT PlantSimEngine.inputs_(m::ToyInternodeCrazyEmergence) = (TT_cu=-Inf,) PlantSimEngine.outputs_(m::ToyInternodeCrazyEmergence) = (TT_cu_emergence=0.0,) -function PlantSimEngine.run!(m::ToyInternodeCrazyEmergence, models, status, meteo, constants=nothing, sim_object=nothing) +function PlantSimEngine.run!(m::ToyInternodeCrazyEmergence, models, status, meteo, constants=nothing, extra=nothing) + + scene = runtime_scene(extra) #root = get_root(status.node) @@ -28,21 +30,21 @@ function PlantSimEngine.run!(m::ToyInternodeCrazyEmergence, models, status, mete if length(MultiScaleTreeGraph.children(status.node)) == 1 && status.TT_cu - status.TT_cu_emergence >= m.TT_emergence - status_new_internode = add_organ!(status.node, sim_object, "<", :Internode, 2, index=1) - add_organ!(status_new_internode.node, sim_object, "+", :Leaf, 2, index=1) + status_new_internode = add_organ!(status.node, scene, "<", :Internode, 2; index=1, initial_status=(carbon_biomass=1.0, TT_cu_emergence=0.0)) + add_organ!(status_new_internode.node, scene, "+", :Leaf, 2; index=1, initial_status=(carbon_biomass=1.0,)) status_new_internode.TT_cu_emergence = status.TT_cu elseif (length(MultiScaleTreeGraph.children(status.node)) >= 2 && length(MultiScaleTreeGraph.children(status.node)) < 7) && status.TT_cu - status.TT_cu_emergence >= m.TT_emergence - status_new_internode = add_organ!(status.node, sim_object, "<", :Internode, 2, index=1) - add_organ!(status.node, sim_object, "+", :Leaf, 2, index=4) - add_organ!(status.node, sim_object, "+", :Leaf, 2, index=5) + status_new_internode = add_organ!(status.node, scene, "<", :Internode, 2; index=1, initial_status=(carbon_biomass=1.0, TT_cu_emergence=0.0)) + add_organ!(status.node, scene, "+", :Leaf, 2; index=4, initial_status=(carbon_biomass=1.0,)) + add_organ!(status.node, scene, "+", :Leaf, 2; index=5, initial_status=(carbon_biomass=1.0,)) status_new_internode.TT_cu_emergence = status.TT_cu elseif (length(MultiScaleTreeGraph.children(status.node)) >= 7 && length(MultiScaleTreeGraph.children(status.node)) < 30) && status.TT_cu - status.TT_cu_emergence >= m.TT_emergence - add_organ!(status.node, sim_object, "+", :Leaf, 2, index=6) - add_organ!(status.node, sim_object, "+", :Leaf, 2, index=7) - add_organ!(status.node, sim_object, "+", :Leaf, 2, index=8) - add_organ!(status.node, sim_object, "+", :Leaf, 2, index=9) - add_organ!(status.node, sim_object, "+", :Leaf, 2, index=10) - add_organ!(status.node, sim_object, "+", :Leaf, 2, index=11) + add_organ!(status.node, scene, "+", :Leaf, 2; index=6, initial_status=(carbon_biomass=1.0,)) + add_organ!(status.node, scene, "+", :Leaf, 2; index=7, initial_status=(carbon_biomass=1.0,)) + add_organ!(status.node, scene, "+", :Leaf, 2; index=8, initial_status=(carbon_biomass=1.0,)) + add_organ!(status.node, scene, "+", :Leaf, 2; index=9, initial_status=(carbon_biomass=1.0,)) + add_organ!(status.node, scene, "+", :Leaf, 2; index=10, initial_status=(carbon_biomass=1.0,)) + add_organ!(status.node, scene, "+", :Leaf, 2; index=11, initial_status=(carbon_biomass=1.0,)) end @@ -50,73 +52,79 @@ function PlantSimEngine.run!(m::ToyInternodeCrazyEmergence, models, status, mete end -# Wrapped this into a function so that it doesn't plague the benchmark with variables on a global scope -#@check_allocs -function do_benchmark_on_heavier_mtg() +function _benchmark_mtg_status(node) + data = Dict{Symbol,Any}(:node => node) + scale = MultiScaleTreeGraph.symbol(node) + scale in (:Leaf, :Internode) && (data[:carbon_biomass] = 1.0) + scale == :Plant && (data[:carbon_allocation] = zeros(4)) + return Status((; data...)) +end + +function setup_heavier_scene_benchmark() mtg = import_mtg_example() - # Example meteo, 365 timesteps : meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Day) - - #similar to the mtg growth test but with a much lower emergence threshold - mapping = PlantSimEngine.ModelMapping( - :Scene => ToyDegreeDaysCumulModel(), - :Plant => ( - MultiScaleModel( - model=ToyLAIModel(), - mapped_variables=[ - :TT_cu => :Scene, - ], - ), - PlantSimEngine.Examples.Beer(0.6), - MultiScaleModel( - model=ToyCAllocationModel(), - mapped_variables=[ - :carbon_assimilation => [:Leaf], - :carbon_demand => [:Leaf, :Internode], - :carbon_allocation => [:Leaf, :Internode] - ], - ), - MultiScaleModel( - model=ToyPlantRmModel(), - mapped_variables=[:Rm_organs => [:Leaf => :Rm, :Internode => :Rm],], - ), - ), - :Internode => ( - MultiScaleModel( - model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - mapped_variables=[:TT => :Scene,], + applications = ( + ModelSpec(ToyDegreeDaysCumulModel(); name=:scene_degree_days) |> + AppliesTo(One(scale=:Scene)), + ModelSpec(ToyLAIModel(); name=:plant_lai) |> + AppliesTo(Many(scale=:Plant)) |> + Inputs(:TT_cu => One(scale=:Scene, within=SceneScope(), application=:scene_degree_days, var=:TT_cu)), + ModelSpec(PlantSimEngine.Examples.Beer(0.6); name=:plant_light) |> + AppliesTo(Many(scale=:Plant)), + ModelSpec(ToyPlantRmModel(); name=:plant_rm) |> + AppliesTo(Many(scale=:Plant)) |> + Inputs(:Rm_organs => Many(scale=(:Leaf, :Internode), within=Subtree(), var=:Rm)), + ModelSpec(ToyCAllocationModel(); name=:plant_allocation) |> + AppliesTo(Many(scale=:Plant)) |> + Inputs( + :carbon_assimilation => Many(scale=:Leaf, within=Subtree(), application=:leaf_assimilation, var=:carbon_assimilation), + :carbon_demand => Many(scale=(:Leaf, :Internode), within=Subtree(), var=:carbon_demand), ), - MultiScaleModel( - model=ToyInternodeCrazyEmergence(TT_emergence=1.0), - mapped_variables=[:TT_cu => :Scene], + ModelSpec(ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0); name=:internode_demand) |> + AppliesTo(Many(scale=:Internode)) |> + Inputs(:TT => One(scale=:Scene, within=SceneScope(), application=:scene_degree_days, var=:TT)), + ModelSpec(ToyInternodeCrazyEmergence(TT_emergence=1.0); name=:internode_emergence) |> + AppliesTo(Many(scale=:Internode)) |> + Inputs(:TT_cu => One(scale=:Scene, within=SceneScope(), application=:scene_degree_days, var=:TT_cu)), + ModelSpec(ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004); name=:internode_respiration) |> + AppliesTo(Many(scale=:Internode)), + ModelSpec(ToyAssimModel(); name=:leaf_assimilation) |> + AppliesTo(Many(scale=:Leaf)) |> + Inputs( + :soil_water_content => One(scale=:Soil, within=SceneScope(), application=:soil_water, var=:soil_water_content), + :aPPFD => One(scale=:Plant, within=Ancestor(scale=:Plant), application=:plant_light, var=:aPPFD), ), - ToyMaintenanceRespirationModel(1.5, 0.06, 25.0, 0.6, 0.004), - Status(carbon_biomass=1.0) - ), - :Leaf => ( - MultiScaleModel( - model=ToyAssimModel(), - mapped_variables=[:soil_water_content => :Soil, :aPPFD => :Plant], - ), - MultiScaleModel( - model=ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0), - mapped_variables=[:TT => :Scene,], - ), - ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025), - Status(carbon_biomass=1.0) - ), - :Soil => ( - ToySoilWaterModel(), - ), + ModelSpec(ToyCDemandModel(optimal_biomass=10.0, development_duration=200.0); name=:leaf_demand) |> + AppliesTo(Many(scale=:Leaf)) |> + Inputs(:TT => One(scale=:Scene, within=SceneScope(), application=:scene_degree_days, var=:TT)), + ModelSpec(ToyMaintenanceRespirationModel(2.1, 0.06, 25.0, 1.0, 0.025); name=:leaf_respiration) |> + AppliesTo(Many(scale=:Leaf)), + ModelSpec(ToySoilWaterModel(); name=:soil_water) |> + AppliesTo(One(scale=:Soil)), ) - out_vars = Dict( - :Leaf => (:carbon_assimilation, :carbon_demand, :soil_water_content, :carbon_allocation), - :Internode => (:carbon_allocation, :TT_cu_emergence), - :Plant => (:carbon_allocation,), - :Soil => (:soil_water_content,), + scene = Scene( + mtg; + applications=applications, + environment=meteo_day, + status=_benchmark_mtg_status, ) + requests = OutputRequest[ + OutputRequest(:Leaf, :carbon_assimilation; name=:leaf_assimilation, application=:leaf_assimilation), + OutputRequest(:Leaf, :carbon_demand; name=:leaf_demand, application=:leaf_demand), + OutputRequest(:Internode, :TT_cu_emergence; name=:internode_emergence, application=:internode_emergence), + OutputRequest(:Plant, :carbon_offer; name=:plant_carbon_offer, application=:plant_allocation), + OutputRequest(:Soil, :soil_water_content; name=:soil_water, application=:soil_water), + ] + return scene, requests, length(meteo_day) +end - out = run!(mtg, mapping, meteo_day, tracked_outputs=out_vars, executor=SequentialEx()) -end \ No newline at end of file +function benchmark_heavier_scene(scene, requests, nsteps) + return run!(scene; steps=nsteps, outputs=requests) +end + +function do_benchmark_on_heavier_mtg() + scene, requests, nsteps = setup_heavier_scene_benchmark() + return benchmark_heavier_scene(scene, requests, nsteps) +end diff --git a/benchmark/test-multirate-buffer-benchmark.jl b/benchmark/test-multirate-buffer-benchmark.jl index 8f47c6b14..3bc0d1c53 100644 --- a/benchmark/test-multirate-buffer-benchmark.jl +++ b/benchmark/test-multirate-buffer-benchmark.jl @@ -1,6 +1,4 @@ using PlantSimEngine -using MultiScaleTreeGraph -using PlantMeteo using Dates PlantSimEngine.@process "mrbenchsource" verbose = false @@ -30,69 +28,63 @@ function PlantSimEngine.run!(::MRBenchConsumer24Model, models, status, meteo, co status.Y24 = sum(status.X) end -function _build_multirate_benchmark_mtg(nleaves::Int) - mtg = Node(MultiScaleTreeGraph.NodeMTG("/", :Scene, 1, 0)) - plant = Node(mtg, MultiScaleTreeGraph.NodeMTG("+", :Plant, 1, 1)) - internode = Node(plant, MultiScaleTreeGraph.NodeMTG("/", :Internode, 1, 2)) - - for i in 1:nleaves - Node(internode, MultiScaleTreeGraph.NodeMTG("+", :Leaf, i, 2)) - end - - return mtg +function _build_multirate_benchmark_objects(nleaves::Int) + objects = Object[ + Object(:plant; scale=:Plant), + ] + append!( + objects, + [Object(Symbol(:leaf_, i); scale=:Leaf, parent=:plant) for i in 1:nleaves], + ) + return objects end function setup_multirate_buffer_benchmark(; nleaves=2000, ndays=30) - mtg = _build_multirate_benchmark_mtg(nleaves) - - mapping = PlantSimEngine.ModelMapping( - :Leaf => ( - ModelSpec(MRBenchSourceModel(Ref(0))) |> TimeStepModel(1.0), - ), - :Plant => ( - ModelSpec(MRBenchConsumer4Model()) |> - MultiScaleModel([:X => [:Leaf]]) |> - TimeStepModel(ClockSpec(4.0, 1.0)) |> - InputBindings(; X=(process=:mrbenchsource, var=:X, scale=:Leaf, policy=Integrate())), - ModelSpec(MRBenchConsumer24Model()) |> - MultiScaleModel([:X => [:Leaf]]) |> - TimeStepModel(ClockSpec(24.0, 1.0)) |> - InputBindings(; X=(process=:mrbenchsource, var=:X, scale=:Leaf, policy=Integrate())), - ), + objects = _build_multirate_benchmark_objects(nleaves) + applications = ( + ModelSpec(MRBenchSourceModel(Ref(0)); name=:hourly_source) |> + AppliesTo(Many(scale=:Leaf)) |> + TimeStep(Hour(1)), + ModelSpec(MRBenchConsumer4Model(); name=:four_hour_consumer) |> + AppliesTo(One(scale=:Plant)) |> + Inputs(:X => Many( + scale=:Leaf, + within=Subtree(), + application=:hourly_source, + var=:X, + policy=Integrate(), + window=Hour(4), + )) |> + TimeStep(Hour(4)), + ModelSpec(MRBenchConsumer24Model(); name=:daily_consumer) |> + AppliesTo(One(scale=:Plant)) |> + Inputs(:X => Many( + scale=:Leaf, + within=Subtree(), + application=:hourly_source, + var=:X, + policy=Integrate(), + window=Day(1), + )) |> + TimeStep(Day(1)), ) nsteps = 24 * ndays - meteo = Weather(repeat([Atmosphere(T=20.0, Wind=1.0, Rh=0.65)], nsteps)) + meteo = [(T=20.0, Wind=1.0, Rh=0.65, duration=Hour(1)) for _ in 1:nsteps] + scene = Scene(objects...; applications=applications, environment=meteo) reqs = [ - OutputRequest(:Leaf, :X; name=:x_hourly, process=:mrbenchsource, policy=HoldLast()), - OutputRequest(:Leaf, :X; name=:x_daily_sum, process=:mrbenchsource, policy=Integrate(), clock=ClockSpec(24.0, 1.0)), + OutputRequest(:Plant, :Y4; name=:four_hour_total, application=:four_hour_consumer), + OutputRequest(:Plant, :Y24; name=:daily_total, application=:daily_consumer), + OutputRequest(:Leaf, :X; name=:x_daily_sum, application=:hourly_source, policy=Integrate(), clock=Day(1)), ] - - tracked = Dict(:Plant => (:Y4, :Y24), :Leaf => (:X,)) - return mtg, mapping, meteo, reqs, tracked, nsteps + return scene, reqs, nsteps end -function benchmark_multirate_status_tracked_run(mtg, mapping, meteo, tracked, nsteps) - run!( - mtg, - mapping, - meteo, - nsteps=nsteps, - check=true, - executor=SequentialEx(), - tracked_outputs=tracked - ) +function benchmark_multirate_retain_all_run(scene, nsteps) + return run!(scene; steps=nsteps, outputs=:all) end -function benchmark_multirate_output_request_run(mtg, mapping, meteo, reqs, tracked, nsteps) - run!( - mtg, - mapping, - meteo, - nsteps=nsteps, - check=true, - executor=SequentialEx(), - tracked_outputs=reqs - ) +function benchmark_multirate_output_request_run(scene, reqs, nsteps) + return run!(scene; steps=nsteps, outputs=reqs) end diff --git a/benchmark/test-plantbiophysics.jl b/benchmark/test-plantbiophysics.jl index 071d5d93d..2c438068a 100644 --- a/benchmark/test-plantbiophysics.jl +++ b/benchmark/test-plantbiophysics.jl @@ -1,182 +1,76 @@ -# For local testing : -#using Pkg -#Pkg.develop("PlantSimEngine") -#using PlantSimEngine - -using Pkg -#Pkg.add(url="https://github.com/VEZY/PlantBiophysics.jl#dev") -#Pkg.instantiate() -using Statistics -#using DataFrames -#using CSV -using Random +using Dates +using DataFrames using PlantBiophysics -#using BenchmarkTools -#using Test -#using PlantMeteo - -function benchmark_plantbiophysics() - - Random.seed!(1) # Set random seed - microbenchmark_steps = 100 # Number of times the microbenchmark is run - microbenchmark_evals = 1 # N. times each sample is run to be sure of the output - N = 100 # Number of timesteps simulated for each microbenchmark step - - length_range = 10000 - Ra_SW_f = range(10, 500, length=length_range) - Ta = range(18, 40, length=length_range) - Wind = range(0.5, 20, length=length_range) - P = range(90, 101, length=length_range) - Rh = range(0.1, 0.98, length=length_range) - Ca = range(360, 900, length=length_range) - skyF = range(0.0, 1.0, length=length_range) - d = range(0.001, 0.5, length=length_range) - Jmax = range(200.0, 300.0, length=length_range) - Vmax = range(150.0, 250.0, length=length_range) - Rd = range(0.3, 2.0, length=length_range) - TPU = range(5.0, 20.0, length=length_range) - g0 = range(0.001, 2.0, length=length_range) - g1 = range(0.5, 15.0, length=length_range) - vars = hcat([Ta, Wind, P, Rh, Ca, Jmax, Vmax, Rd, Ra_SW_f, skyF, d, TPU, g0, g1]) - - set = [rand.(vars) for i = 1:N] - set = reshape(vcat(set...), (length(set[1]), length(set)))' - name = [ - "T", - "Wind", - "P", - "Rh", - "Ca", - "JMaxRef", - "VcMaxRef", - "RdRef", - "Ra_SW_f", - "sky_fraction", - "d", - "TPURef", - "g0", - "g1", - ] - set = DataFrame(set, name) - @. set[!, :vpd] = e_sat(set.T) - vapor_pressure(set.T, set.Rh) - @. set[!, :aPPFD] = set.Ra_SW_f * 0.48 * 4.57 - - constants = Constants() - #time_PB = Vector{Float64}(undef, N*microbenchmark_steps) - for i = 1:N - leaf = PlantSimEngine.ModelMapping( - energy_balance=Monteith(), - photosynthesis=Fvcb( - VcMaxRef=set.VcMaxRef[i], - JMaxRef=set.JMaxRef[i], - RdRef=set.RdRef[i], - TPURef=set.TPURef[i], - ), - stomatal_conductance=Medlyn(set.g0[i], set.g1[i]), - status=( - Ra_SW_f=set.Ra_SW_f[i], - sky_fraction=set.sky_fraction[i], - aPPFD=set.aPPFD[i], - d=set.d[i], - ), - ) - #deps = PlantSimEngine.dep(leaf) - meteo = Atmosphere(T=set.T[i], Wind=set.Wind[i], P=set.P[i], Rh=set.Rh[i], Cₐ=set.Ca[i]) - #st = PlantMeteo.row_struct(leaf.status[1]) - #b_PB = @benchmark run!($leaf, $meteo, $constants, nothing; executor = ThreadedEx()) evals = microbenchmark_evals samples = microbenchmark_steps - run!(leaf, meteo, constants, nothing; executor=ThreadedEx()) +using PlantMeteo +using Random - # transform in seconds - #=for j in 1:microbenchmark_steps - time_PB[microbenchmark_steps*(i-1) + j] = b_PB.times[j]*1e-9 - end=# - end - #return time_PB +function _plantbiophysics_forcing_set(n::Int) + Random.seed!(1) + length_range = 10_000 + ranges = ( + T=range(18, 40; length=length_range), + Wind=range(0.5, 20; length=length_range), + P=range(90, 101; length=length_range), + Rh=range(0.1, 0.98; length=length_range), + Ca=range(360, 900; length=length_range), + JMaxRef=range(200.0, 300.0; length=length_range), + VcMaxRef=range(150.0, 250.0; length=length_range), + RdRef=range(0.3, 2.0; length=length_range), + Ra_SW_f=range(10, 500; length=length_range), + sky_fraction=range(0.0, 1.0; length=length_range), + d=range(0.001, 0.5; length=length_range), + TPURef=range(5.0, 20.0; length=length_range), + g0=range(0.001, 2.0; length=length_range), + g1=range(0.5, 15.0; length=length_range), + ) + columns = (; ( + name => [rand(values) for _ in 1:n] + for (name, values) in pairs(ranges) + )...) + return DataFrame(columns) end -function setup_benchmark_plantbiophysics_multitimestep() - - Random.seed!(1) # Set random seed - N = 100 # Number of timesteps simulated for each microbenchmark step - - length_range = 10000 - Ra_SW_f = range(10, 500, length=length_range) - Ta = range(18, 40, length=length_range) - Wind = range(0.5, 20, length=length_range) - P = range(90, 101, length=length_range) - Rh = range(0.1, 0.98, length=length_range) - Ca = range(360, 900, length=length_range) - skyF = range(0.0, 1.0, length=length_range) - d = range(0.001, 0.5, length=length_range) - Jmax = range(200.0, 300.0, length=length_range) - Vmax = range(150.0, 250.0, length=length_range) - Rd = range(0.3, 2.0, length=length_range) - TPU = range(5.0, 20.0, length=length_range) - g0 = range(0.001, 2.0, length=length_range) - g1 = range(0.5, 15.0, length=length_range) - vars = hcat([Ta, Wind, P, Rh, Ca, Jmax, Vmax, Rd, Ra_SW_f, skyF, d, TPU, g0, g1]) - - set = [rand.(vars) for i = 1:N] - set = reshape(vcat(set...), (length(set[1]), length(set)))' - name = [ - "T", - "Wind", - "P", - "Rh", - "Ca", - "JMaxRef", - "VcMaxRef", - "RdRef", - "Ra_SW_f", - "sky_fraction", - "d", - "TPURef", - "g0", - "g1", - ] - set = DataFrame(set, name) - @. set[!, :vpd] = e_sat(set.T) - vapor_pressure(set.T, set.Rh) - @. set[!, :aPPFD] = set.Ra_SW_f * 0.48 * 4.57 - - leaf = Vector{PlantSimEngine.ModelMapping}(undef, N) - for i = 1:N - leaf[i] = PlantSimEngine.ModelMapping( - energy_balance=Monteith(), - photosynthesis=Fvcb( - VcMaxRef=set.VcMaxRef[i], - JMaxRef=set.JMaxRef[i], - RdRef=set.RdRef[i], - TPURef=set.TPURef[i], - ), - stomatal_conductance=Medlyn(set.g0[i], set.g1[i]), - status=( - Ra_SW_f=set.Ra_SW_f, - sky_fraction=set.sky_fraction, - aPPFD=set.aPPFD, - d=set.d, - ), - ) - end - - atm = Vector{Atmosphere}(undef, N) - for i in 1:N - atm[i] = Atmosphere(T=set.T[i], Wind=set.Wind[i], P=set.P[i], Rh=set.Rh[i], Cₐ=set.Ca[i]) - end - meteo = Weather(atm) +function _plantbiophysics_leaf_scene(row) + return PlantBiophysics.leaf_scene( + Monteith(), + Fvcb( + VcMaxRef=row.VcMaxRef, + JMaxRef=row.JMaxRef, + RdRef=row.RdRef, + TPURef=row.TPURef, + ), + Medlyn(row.g0, row.g1); + status=Status( + Ra_SW_f=row.Ra_SW_f, + sky_fraction=row.sky_fraction, + aPPFD=row.Ra_SW_f * 0.48 * 4.57, + d=row.d, + ), + environment=Atmosphere( + T=row.T, + Wind=row.Wind, + P=row.P, + Rh=row.Rh, + Cₐ=row.Ca, + duration=Hour(1), + ), + ) +end - return leaf, meteo +function setup_benchmark_plantbiophysics_batch(; n=100) + forcing = _plantbiophysics_forcing_set(n) + return [_plantbiophysics_leaf_scene(row) for row in eachrow(forcing)] end -function benchmark_plantbiophysics_multitimestep_MT(leaf, meteo) - N = length(meteo) - for i in 1:N - run!(leaf[i], meteo, Constants(), nothing; executor=ThreadedEx()) +function benchmark_plantbiophysics_batch(scenes) + constants = Constants() + for scene in scenes + run!(scene; constants=constants, outputs=:none) end + return nothing end -function benchmark_plantbiophysics_multitimestep_ST(leaf, meteo) - N = length(meteo) - for i in 1:N - run!(leaf[i], meteo, Constants(), nothing; executor=SequentialEx()) - end +function benchmark_plantbiophysics() + scenes = setup_benchmark_plantbiophysics_batch() + return benchmark_plantbiophysics_batch(scenes) end diff --git a/benchmark/test-xpalm.jl b/benchmark/test-xpalm.jl index e3d9f7c98..0ded4f94b 100644 --- a/benchmark/test-xpalm.jl +++ b/benchmark/test-xpalm.jl @@ -1,60 +1,77 @@ -#using Pkg -#Pkg.develop("PlantSimEngine") -#using PlantSimEngine - -using Pkg -#Pkg.add(url="https://github.com/PalmStudio/XPalm.jl#dev") -#Pkg.instantiate() -using Test -using PlantMeteo#, MultiScaleTreeGraph -#using CairoMakie, AlgebraOfGraphics -using DataFrames, CSV, Statistics +using BenchmarkTools +using CSV +using DataFrames using Dates +using PlantSimEngine using XPalm -using BenchmarkTools + +function _xpalm_output_requests(scene, vars) + applications = explain_scene_applications(scene) + requests = OutputRequest[] + for (scale, variables) in pairs(vars) + for variable in variables + scale_symbol = Symbol(scale) + variable_symbol = Symbol(variable) + candidates = [ + row.application_id + for row in applications + if ( + scale_symbol in row.target_scales || + object_address(row.applies_to).scale == scale_symbol + ) && variable_symbol in row.outputs + ] + isempty(candidates) && error( + "No XPalm benchmark output publisher for `$(scale_symbol).$(variable_symbol)`.", + ) + push!( + requests, + OutputRequest( + scale_symbol, + variable_symbol; + name=Symbol(scale, "__", variable), + application=last(candidates), + ), + ) + end + end + return requests +end function xpalm_default_param_create() - meteo = CSV.read(joinpath(dirname(dirname(pathof(XPalm))), "0-data", "meteo.csv"), DataFrame) - #meteo.duration = [Dates.Day(i[1:1]) for i in meteo.duration] - m = Weather(meteo) + meteo = CSV.read( + joinpath(dirname(dirname(pathof(XPalm))), "0-data", "meteo.csv"), + DataFrame, + ) + :duration in propertynames(meteo) || + (meteo.duration = fill(Day(1), nrow(meteo))) - out_vars = Dict{Symbol,Any}( + vars = Dict{Symbol,Any}( :Scene => (:lai,), - # :Scene => (:LAI, :scene_leaf_area, :aPPFD, :TEff), - # :Plant => (:plant_age, :ftsw, :newPhytomerEmergence, :aPPFD, :plant_leaf_area, :carbon_assimilation, :carbon_offer_after_rm, :Rm, :TT_since_init, :TEff, :phytomer_count, :newPhytomerEmergence), - :Leaf => (:Rm, :potential_area, :TT_since_init, :TEff, :biomass, :carbon_demand, :carbon_allocation,), - # :Leaf => (:Rm, :potential_area), - # :Internode => (:Rm, :carbon_allocation, :carbon_demand), + :Leaf => ( + :Rm, + :potential_area, + :TT_since_init, + :biomass, + :carbon_demand, + ), :Male => (:Rm,), - # :Female => (:biomass,), - # :Soil => (:TEff, :ftsw, :root_depth), ) - - # Example 1: Run the model with the default parameters (but output as a DataFrame): - palm = XPalm.Palm(initiation_age=0, parameters=XPalm.default_parameters()) - models = XPalm.model_mapping(palm) - return palm, models, out_vars, m + palm = XPalm.Palm( + initiation_age=0, + parameters=XPalm.default_parameters(), + ) + scene = XPalm.xpalm_scene(palm; environment=meteo) + return scene, _xpalm_output_requests(scene, vars), nrow(meteo) end -function xpalm_default_param_run(palm, models, out_vars, meteo) - sim_outputs = PlantSimEngine.run!(palm.mtg, models, meteo, tracked_outputs=out_vars, executor=PlantSimEngine.SequentialEx(), check=false) - return sim_outputs +function xpalm_default_param_run(scene, requests, nsteps) + return PlantSimEngine.run!( + scene; + steps=nsteps, + outputs=requests, + ) end -function xpalm_default_param_convert_outputs(sim_outputs) - df = PlantSimEngine.convert_outputs(sim_outputs, DataFrame, no_value=missing) - return df +function xpalm_default_param_collect_outputs(simulation) + return PlantSimEngine.collect_outputs(simulation; sink=DataFrame) end - - -println(Pkg.status("XPalm")) - -#=@testset "XPalm simple test" begin - # default number of seconds is 5 - b_XP = @benchmark xpalm_default_param_run() seconds = 120 - - #N = length(b_XP.times) - - @test mean(b_XP.times*1e-9) > 10 - @test mean(b_XP.times*1e-9) < 15 -end =# \ No newline at end of file diff --git a/benchmark/test/runtests.jl b/benchmark/test/runtests.jl new file mode 100644 index 000000000..796f35aec --- /dev/null +++ b/benchmark/test/runtests.jl @@ -0,0 +1,39 @@ +using CSV +using DataFrames +using Dates +using MultiScaleTreeGraph +using PlantMeteo +using PlantSimEngine +using PlantSimEngine.Examples +using Statistics +using Test + +@testset "PlantSimEngine benchmark API smoke" begin + include(joinpath(@__DIR__, "..", "test-PSE-benchmark.jl")) + scene, requests, _ = setup_heavier_scene_benchmark() + simulation = benchmark_heavier_scene(scene, requests, 1) + @test current_step(simulation) == 1 + @test !isempty(collect_outputs(simulation; sink=nothing)) +end + +@testset "multirate benchmark API smoke" begin + include(joinpath(@__DIR__, "..", "test-multirate-buffer-benchmark.jl")) + scene, requests, nsteps = setup_multirate_buffer_benchmark(; ndays=1, nleaves=4) + simulation = benchmark_multirate_output_request_run(scene, requests, nsteps) + @test current_step(simulation) == nsteps + @test !isempty(collect_outputs(simulation; sink=nothing)) +end + +@testset "PlantBiophysics benchmark API smoke" begin + include(joinpath(@__DIR__, "..", "test-plantbiophysics.jl")) + scenes = setup_benchmark_plantbiophysics_batch(; n=2) + @test isnothing(benchmark_plantbiophysics_batch(scenes)) +end + +@testset "XPalm benchmark API smoke" begin + include(joinpath(@__DIR__, "..", "test-xpalm.jl")) + scene, requests, _ = xpalm_default_param_create() + simulation = PlantSimEngine.run!(scene; steps=1, outputs=requests) + @test current_step(simulation) == 1 + @test !isempty(xpalm_default_param_collect_outputs(simulation)) +end diff --git a/docs/make.jl b/docs/make.jl index 4d810867e..b6aa0b46c 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -30,31 +30,63 @@ makedocs(; "Key Concepts" => "./prerequisites/key_concepts.md", "Julia language basics" => "./prerequisites/julia_basics.md", ], - "Scene/Object simulations" => [ + "Getting Started" => [ "Quickstart" => "./scene_object/quickstart.md", + "Detailed first simulation" => "./step_by_step/detailed_first_example.md", + "Port an existing model" => "./guides/modelers/port_existing_model.md", "Migrating from mappings" => "migration_scene_object.md", ], - "Step by step - Single-scale simulations" => [ - "Detailed first simulation" => "./step_by_step/detailed_first_example.md", - "Coupling" => "./step_by_step/simple_model_coupling.md", + "Building Scenarios" => [ + "Coupling models" => "./guides/coupling.md", "Model Switching" => "./step_by_step/model_switching.md", - "Quick examples" => "./step_by_step/quick_and_dirty_examples.md", "Implementing a process" => "./step_by_step/implement_a_process.md", "Implementing a model" => "./step_by_step/implement_a_model.md", "Advanced coupling and hard dependencies" => "./step_by_step/advanced_coupling.md", "Implementing a model : additional notes" => "./step_by_step/implement_a_model_additional.md", ], + "Multiscale Scenes" => [ + "How scenes execute" => "./guides/multiscale/concepts.md", + "From one object" => "./guides/multiscale/from_one_object.md", + "Value coupling" => "./guides/multiscale/value_coupling.md", + "Importing an MTG" => "./guides/multiscale/import_mtg.md", + "Manual calls" => "./guides/multiscale/manual_calls.md", + "Visualizing structure" => "./guides/multiscale/visualizing_structure.md", + ], + "Growing Plant Tutorial" => [ + "Part 1: growth" => "./tutorials/growing_plant/part1_growth.md", + "Part 2: roots and water" => "./tutorials/growing_plant/part2_roots_water.md", + "Part 3: debugging" => "./tutorials/growing_plant/part3_debugging.md", + ], + "Time And Environment" => [ + "Understanding cadence" => "./guides/time/multirate_concepts.md", + "Hourly, daily, and weekly" => "./guides/time/hourly_daily_weekly.md", + "Advanced configuration" => "./guides/time/advanced_time_environment.md", + ], + "Data And Analysis" => [ + "Environment inputs" => "./guides/data/environment_inputs.md", + "Collecting and plotting outputs" => "./guides/data/outputs_plotting.md", + "Forcing observations" => "./guides/data/forcing_observations.md", + "Numerical reliability" => "./guides/data/numerical_reliability.md", + "Parameter fitting" => "./working_with_data/fitting.md", + ], + "Troubleshooting" => [ + "Common errors" => "./troubleshooting/common_errors.md", + "Runtime contracts" => "./troubleshooting/runtime_contracts.md", + "Dependency cycles" => "./troubleshooting/dependency_cycles.md", + "State and repeated updates" => "./guides/modelers/stateful_models.md", + "Downstream testing" => "./troubleshooting_and_testing/downstream_tests.md", + ], "Model execution" => "model_execution.md", "Model traits" => "model_traits.md", "AI agent skill" => "agent_skill.md", - "Parameter fitting" => "./working_with_data/fitting.md", - "Troubleshooting and testing" => [ - "Automated testing" => "./troubleshooting_and_testing/downstream_tests.md", - ], "API" => [ + "API" => [ "Public API" => "./API/API_public.md", + "Public symbol inventory" => "./API/public_symbols.md", "Example models" => "./API/API_examples.md", "Internal API" => "./API/API_private.md",], "Development designs" => [ + "Public API refinement decisions" => "./dev/public_api_refinement_decisions.md", + "Public API refinement completion audit" => "./dev/public_api_refinement_completion_audit.md", "Unified scene/object design" => "./dev/unified_scene_object_design.md", "Unified scene/object implementation plan" => "./dev/unified_scene_object_implementation_plan.md", "Unified scene/object completion audit" => "./dev/unified_scene_object_completion_audit.md", @@ -67,8 +99,10 @@ makedocs(; ] ) -deploydocs(; - repo="github.com/VirtualPlantLab/PlantSimEngine.jl.git", - devbranch="main", - push_preview=true, # Visit https://VirtualPlantLab.github.io/PlantSimEngine.jl/previews/PR128 to visualize the preview of the PR #128 -) +if get(ENV, "PLANTSIMENGINE_DOCS_BUILD_ONLY", "false") != "true" + deploydocs(; + repo="github.com/VirtualPlantLab/PlantSimEngine.jl.git", + devbranch="main", + push_preview=true, # Visit https://VirtualPlantLab.github.io/PlantSimEngine.jl/previews/PR128 to visualize the preview of the PR #128 + ) +end diff --git a/docs/paper/paper.md b/docs/paper/paper.md index 974dec5a0..29f2b3a82 100644 --- a/docs/paper/paper.md +++ b/docs/paper/paper.md @@ -31,7 +31,8 @@ bibliography: paper.bib - Switch between models without changing any code, with a simple syntax to define the model to use for a given process - Reduce the degrees of freedom by fixing variables, passing measurements, or using a simpler model for a given process - Fast computation, with 100th of nanoseconds for one model, two coupled models (see this [benchmark script](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/examples/benchmark.jl)), or the full energy balance of a leaf using [PlantBiophysics.jl](https://github.com/VEZY/PlantBiophysics.jl) [@vezy_vezyplantbiophysicsjl_2023], a package that uses PlantSimEngine -- Out of the box sequential, parallel (multi-threaded) or distributed (multi-process) computations over objects, time-steps and independent processes (thanks to [Floops.jl](https://juliafolds.github.io/FLoops.jl/stable/)) +- Sequential scene execution through concrete application/object batches. A + public parallel or distributed executor is not currently part of the API. - Easily scalable, with methods for computing over objects, time-steps and even [Multi-Scale Tree Graphs](https://github.com/VEZY/MultiScaleTreeGraph.jl) [@vezy_multiscaletreegraphjl_2023] - Composable, allowing the use of any types as inputs such as [Unitful](https://github.com/PainterQubits/Unitful.jl) to propagate units, or [MonteCarloMeasurements.jl](https://github.com/baggepinnen/MonteCarloMeasurements.jl) [@carlson_montecarlomeasurementsjl_2020] to propagate measurement error diff --git a/docs/src/API/API_public.md b/docs/src/API/API_public.md index 629d76617..8966b1e72 100644 --- a/docs/src/API/API_public.md +++ b/docs/src/API/API_public.md @@ -5,6 +5,8 @@ ### Scenario and model applications - `Scene` stores objects, model applications, instances, and environment. +- `Scene(model, models...; status=...)` is the concise one-object form and + lowers to the same object/application representation. - `Object` represents one runtime entity with stable identity and status. - `ObjectTemplate` and `ObjectInstance` reuse a model across instances. - `ModelSpec(model; name=...)` identifies one model application. @@ -14,15 +16,15 @@ - `Inputs(...)` declares value dependencies. - `Calls(...)` declares manually executable child models. -- `Updates(:variable; after=...)` orders intentional duplicate writers. +- `Updates(:variable; after=:application_id)` orders intentional duplicate writers. - `Input(...)` and `Call(...)` express model defaults through `dep(model)`. - `run_call!(target; publish=false)` executes a trial hard call. ### Selectors - Multiplicity: `One(...)`, `OptionalOne(...)`, and `Many(...)`. -- Scope: `SceneScope()`, `Self()`, `SelfPlant()`, `Ancestor(...)`, and - `Scope(name)`. +- Scope: `SceneScope()`, `Self()`, `Subtree()`, `SelfPlant()`, + `Ancestor(...)`, and `Scope(name)`. - Labels and topology: `Kind(...)`, `Species(...)`, `Scale(...)`, and `Relation(...)`. @@ -34,19 +36,26 @@ - `Environment(...)` configures environment providers and source remapping. - Models declare environment variables with `meteo_inputs_` and `meteo_outputs_`. -- `OutputRequest(...)` selects retained and resampled scene output streams. +- `OutputRequest(selector, variable; ...)` selects retained and optionally + resampled streams using the same object selector grammar. ### Lifecycle - `objects_from_mtg` and `Scene(mtg; ...)` adapt an MTG into the object registry. - `add_organ!` creates and initializes a new organ in an MTG-backed scene. +- `runtime_scene(extra)` gives lifecycle-capable kernels sanctioned access to + the live scene from their `SceneRunContext`. - `register_object!`, `remove_object!`, and `reparent_object!` change topology. - `move_object!` and `update_geometry!` change spatial state. -- `refresh_bindings!` recompiles structural bindings. -- `refresh_environment_bindings!` recompiles spatial environment bindings. -- `run!(scene; steps=...)` returns a `SceneSimulation`. +- Supported lifecycle operations automatically invalidate and refresh the + affected structural or spatial bindings before the next timestep. +- `run!(scene; steps=..., outputs=:none)` starts a fresh result timeline and + returns a `SceneSimulation`. +- `continue!(simulation; steps=...)` and `step!(simulation)` advance an + existing timeline without resetting temporal state. +- `current_step(simulation)` reports the accepted timeline position. - `collect_outputs(sim)` materializes retained output streams. ### Explanations @@ -66,10 +75,27 @@ Use structured explanation helpers instead of inspecting internals: - `explain_execution_plan` - `explain_output_retention` - `explain_outputs` +- `explain_initialization` See [Migrating To The Scene/Object API](../migration_scene_object.md) for translations from removed APIs. +## Advanced compiler API + +```@docs +PlantSimEngine.Advanced +``` + +Compiler representations, cache refresh operations, and low-level binding +compilers live under `PlantSimEngine.Advanced`. They are intended for package +integration, diagnostics development, and compiler work rather than ordinary +scenario composition. Prefer the public `explain_*` functions, which accept a +`Scene` directly, over manually compiling and inspecting fields. + +Examples include `Advanced.compile_scene`, `Advanced.refresh_bindings!`, and +the `Advanced.CompiledScene` family. These qualified APIs may evolve more +quickly than the default modeling interface. + ## Index ```@index diff --git a/docs/src/API/public_symbols.md b/docs/src/API/public_symbols.md new file mode 100644 index 000000000..1f2d3842c --- /dev/null +++ b/docs/src/API/public_symbols.md @@ -0,0 +1,107 @@ +# Public Symbol Inventory + +This page records the supported default namespace. Compiler representations +and cache controls are intentionally listed separately under +[`PlantSimEngine.Advanced`](#advanced-namespace). + +## Scenario composition + +- Scene structure: `Scene`, `Object`, `ObjectId`, `ObjectTemplate`, + `ObjectInstance`, `Override`. +- Applications: `ModelSpec`, `AppliesTo`, `Inputs`, `Calls`, `TimeStep`, + `Environment`, `Updates`, `OutputRouting`. +- Application inspection: `application_name`, `applies_to`, `value_inputs`, + `model_calls`, `environment_config`, `output_routing`, `updates`. +- Dependency defaults: `Input`, `Call`, `PreviousTimeStep`. + +## Object selectors and queries + +- Multiplicity: `One`, `OptionalOne`, `Many`. +- Scope and topology: `SceneScope`, `Self`, `Subtree`, `SelfPlant`, `Ancestor`, + `Scope`, `Relation`. +- Labels: `Kind`, `Species`, `Scale`. +- Normalized addresses: `ObjectAddress`, `object_address`. +- Queries: `object_ids`, `scene_objects`, `resolve_object_ids`, + `resolve_objects`. +- Object data: `geometry`, `position`, `bounds`. + +## Execution, lifecycle, and outputs + +- Execution: `run!`, `continue!`, `step!`, `SceneSimulation`, `current_step`, + `runtime_scene`. +- Output selection and collection: `OutputRequest`, `scene_outputs`, + `collect_outputs`. +- Lifecycle: `register_object!`, `add_organ!`, `remove_object!`, + `reparent_object!`, `move_object!`, `update_geometry!`, + `mark_environment_binding_dirty!`, `objects_from_mtg`. +- Hard calls: `SceneRunContext`, `SceneCallTarget`, `call_target`, + `call_targets`, `run_call!`. + +## Structured explanations + +- Structure: `explain_objects`, `explain_instances`, `explain_scopes`. +- Compilation: `explain_scene_applications`, `explain_bindings`, + `explain_calls`, `explain_model_bundles`, `explain_writers`, + `explain_schedule`, `explain_execution_plan`. +- Initialization, environment, and outputs: `explain_initialization`, + `explain_environment`, `explain_environment_bindings`, + `explain_output_retention`, `explain_outputs`. +- Supported carrier inspection: `input_carrier`, `input_value`, + `has_reference_carrier`. + +## Model-author contract + +- Model identity: `AbstractModel`, `@process`, `process`. +- State: `Status`, `init_variables`, `dep`. +- Model IO: `inputs`, `outputs`, `variables`, `meteo_inputs`, `meteo_inputs_`, + `meteo_outputs`, `meteo_outputs_`, `validate_meteo_inputs`. +- Timing and routing traits: `timespec`, `output_policy`, `timestep_hint`, + `meteo_hint`, `meteo_bindings`, `meteo_window`. + +The underscore declarations `inputs_`, `outputs_`, `meteo_inputs_`, and +`meteo_outputs_` are extension functions model authors implement with qualified +definitions such as `PlantSimEngine.inputs_(model) = ...`. `inputs_` and +`outputs_` remain intentionally unexported to avoid collisions with common +user functions. + +## Time and reducers + +- Scheduling: `ClockSpec`, `SchedulePolicy`, `HoldLast`, `Interpolate`, + `Integrate`, `Aggregate`. +- Reducers: `AbstractTimeReducer`, `MeanWeighted`, `MeanReducer`, `SumReducer`, + `MinReducer`, `MaxReducer`, `FirstReducer`, `LastReducer`, + `RadiationEnergy`. + +## Environment extension interface + +- Backend contract: `AbstractEnvironmentBackend`, `EnvironmentSupport`, + `GlobalConstant`, `environment_backend`, `environment_variables`, + `base_step_seconds`. +- Sampling and mutation: `sample`, `sample_environment`, `scatter!`, + `scatter_environment_outputs!`, `update_index!`. +- PlantMeteo conveniences: `Atmosphere`, `Constants`, `Weather`. + +## Evaluation + +- Fitting and metrics: `fit`, `RMSE`, `NRMSE`, `EF`, `dr`. + +## Advanced namespace + +`PlantSimEngine.Advanced` contains the qualified compiler and cache API: + +- registries and compiled representations: `SceneRegistry`, `CompiledScene`, + `CompiledSceneApplication`, `CompiledSceneInputBinding`, + `CompiledSceneCallBinding`, `CompiledEnvironmentBinding`, + `CompiledEnvironmentBindings`; +- carrier and adapter implementation types: `ObjectRefVector`, + `TimeStepTable`; +- compiler and cache operations: `compile_scene`, `refresh_bindings!`, + `refresh_environment_bindings!`, `compile_environment_bindings`, + `bind_environment`; +- cache diagnostics: `bindings_dirty`, `environment_bindings_dirty`, + `scene_revision`, `environment_revision`, `compiled_bindings`, + `compiled_environment_bindings`. + +These names require explicit qualification or `using PlantSimEngine.Advanced`. +They are not part of the concise user namespace and may evolve with compiler +implementation requirements. diff --git a/docs/src/agent_skill.md b/docs/src/agent_skill.md index db92a15ae..67fdb97d8 100644 --- a/docs/src/agent_skill.md +++ b/docs/src/agent_skill.md @@ -22,6 +22,10 @@ gives agents the package-specific conventions they need for: - reasoning about model-clock weather aggregation, `meteo_hint` reducers, and scenario source overrides; - inspecting compiled scenarios with structured explanation helpers; +- reporting supplied, generated, bound, and unresolved variables with + `explain_initialization`; +- accessing the live scene from lifecycle-capable kernels with + `runtime_scene(extra)`; - inspecting homogeneous runtime batches with `explain_execution_plan`; - collecting raw or requested scene outputs with `scene_outputs`, `OutputRequest`, `collect_outputs`, and `explain_output_retention`; diff --git a/docs/src/dev/code_cleanup_audit.md b/docs/src/dev/code_cleanup_audit.md index fa310c4bc..b6ddda633 100644 --- a/docs/src/dev/code_cleanup_audit.md +++ b/docs/src/dev/code_cleanup_audit.md @@ -1,84 +1,97 @@ # Code Cleanup Audit -This page records cleanup candidates found during the scene/object experimental -branch audit. It is intentionally biased toward code health and release-note -planning rather than immediate implementation. - -See also: - -- `release_notes_handoff.md` for the consolidated release-note source. -- `unified_scene_object_design.md` for the scene/object redesign. -- `unified_scene_object_implementation_plan.md` for the implementation handoff - of that future design. - -Priority meanings: - -- P0: architectural compatibility removal or high-impact breaking cleanup. -- P1: should be handled before stabilizing the new API. -- P2: useful cleanup with moderate risk or blast radius. -- P3: lower-risk cleanup or follow-up once nearby code is touched. - -## Functions With Mergeable Intent - -These functions are not always wrong as separate Julia methods. The cleanup -target is duplicated control flow, not legitimate multiple dispatch. - -| Priority | Functions | Evidence | Recommended cleanup | -| --- | --- | --- | --- | -| Done | `_resolve_input_windowed`, `_resolve_input_interpolate`, `_resolve_input_holdlast` | `src/time/runtime/input_resolution.jl` | Policy-specific wrappers now delegate to `_resolve_input_from_policy!`, with shared vector/scalar source lookup and policy-specific sampling dispatch. | -| Done | `_normalize_meteo_reducer`, `_resolve_window_reducer` | `src/time/runtime/meteo_sampling.jl`; `src/time/runtime/input_resolution.jl` | Merged through `_normalize_time_reducer(...; context=...)`. | -| Done | `validate_meteo_inputs(model_specs, meteo)` and `validate_meteo_inputs(model_specs, backend)` | `src/time/runtime/meteo_sampling.jl`; `src/time/runtime/environment_backends.jl` | Shared missing-row collection and diagnostic formatting through `_collect_missing_meteo_rows` and `_error_missing_meteo_inputs`. | -| Done | `_required_horizon_for_export_policy` and `_required_horizon_for_policy` | `src/time/runtime/output_export.jl`; `src/time/runtime/publishers.jl` | Export code now delegates to `_required_horizon_for_policy(policy, clock.dt, source_dt)`. | -| Done | `_normalize_meteo_window` and `_runtime_meteo_window` | `src/mtg/ModelSpec.jl`; `src/time/runtime/meteo_sampling.jl` | Runtime meteo-window normalization now calls `_normalize_meteo_window`. | -| Done | `Status` and `StatusView` Base interface methods | `src/component_models/Status.jl`; `src/component_models/StatusView.jl` | Shared small private helpers for values, tuple conversion, iteration, and indexed iteration while keeping storage-specific access and mutation methods separate. | -| Done | Tutorial helper functions repeated in toy multiscale examples | `examples/ToyMultiScalePlantTutorial/ToyPlantSimulation2.jl`; `examples/ToyMultiScalePlantTutorial/ToyPlantSimulation3.jl` | Extracted shared MTG navigation helpers to `examples/ToyMultiScalePlantTutorial/ToyPlantHelpers.jl` and included it with `@__DIR__` so tutorial scripts remain standalone. | -| Done | Test helper flows that run a graph simulation and compare outputs | `test/helper-functions.jl` | Shared `run_graphsim_for_comparison` for generated multiscale comparison and filtered-output comparison paths. | - -## Backward Compatibility To Remove - -This section is the release-note source list. Removing these items is breaking, -and some are still internal dependencies rather than shallow exported shims. - -| Priority | Compatibility surface | Evidence | Migration note | -| --- | --- | --- | --- | -| Done | `ModelList` public API and legacy backing type | Formerly in `src/PlantSimEngine.jl`; `src/component_models/ModelList.jl`; `src/mtg/mapping/mapping.jl` | `ModelList` has been removed. Use `PlantSimEngine.ModelMapping(model...; status=..., type_promotion=...)` for single-scale simulations. | -| Done | `run!(::ModelList, ...)` | Formerly in `src/run.jl` direct `ModelList` methods | `run!(::ModelList, ...)` has been removed. Wrap models in `ModelMapping` before running. | -| Done | Batch `run!` for collections of `ModelList` or single-scale mappings | Formerly in `src/run.jl` collection methods | Batch `run!([mapping1, mapping2], meteo)` and `run!(Dict(...), meteo)` are removed. Use an explicit loop or comprehension and call `run!` per mapping. | -| Done | `run!(mtg, mapping::AbstractDict, ...)` | Formerly in `src/run.jl` | Passing a raw `Dict` to multiscale `run!` is removed. Construct `PlantSimEngine.ModelMapping(dict)` first, or use `PlantSimEngine.ModelMapping(:Scale => models, ...)`. | -| Done | String scale names | `src/mtg/mapping/mapping.jl`; `src/mtg/MultiScaleModel.jl`; `src/mtg/model_spec_inference.jl`; `src/time/runtime/bindings.jl` | String scale names are removed. Use symbols everywhere, for example `:Leaf` instead of `"Leaf"`. | -| Done | `PlantSimEngine.ModelMapping(Float64 => Float32)` as old type-promotion shorthand | Formerly in `src/mtg/mapping/mapping.jl` | `PlantSimEngine.ModelMapping(Float64 => Float32)` is removed. Use `Dict(Float64 => Float32)` as the `type_promotion` value. | -| Done | Old output indexing helpers on multiscale output dictionaries | Formerly in `src/mtg/GraphSimulation.jl` | `outputs(out_dict, key)` and `outputs(out_dict, i)` are removed. Use `convert_outputs(out_dict, sink)` and index the converted table or dictionary explicitly. | - -`ModelMapping{SingleScale}` now uses an internal single-scale backing container -instead of the removed public `ModelList` API. - -## Non-Idiomatic Julia Patterns - -| Priority | Pattern | Evidence | Recommended cleanup | -| --- | --- | --- | --- | -| Done | Source-side `@assert` used for user/data validation | Formerly in MTG initialization, mapping, output conversion, and save-result helpers | Converted to explicit `if` checks and `error` messages in this cleanup pass. Remaining `@assert` uses are limited to tests and documentation examples. | -| Done | `ModelSpec(model::AbstractModel)` checks `model isa MultiScaleModel`, which is effectively dead | `src/mtg/ModelSpec.jl` | Added an explicit `ModelSpec(::MultiScaleModel)` method and removed the dead branch from the `AbstractModel` constructor. | -| Done | `Symbol("")` sentinel for same-scale/no-op mappings | `src/mtg/MultiScaleModel.jl`; `src/mtg/mapping/mapping.jl`; `src/dependencies/dependency_graph.jl` | Replaced the magic sentinel with the typed `SameScale()` marker and reject `Symbol("")` in new mappings. | -| Done | Policy handling by large `isa` branch chains | `src/time/runtime/input_resolution.jl` | Input resolution now dispatches on policy type through `_resolved_policy_value_for_source` and `_resolve_input_for_policy!`, with shared source-resolution helpers. | -| Done | Scope selectors accept strings and hard-code built-in scale names | `src/time/runtime/scopes.jl`; `src/mtg/ModelSpec.jl` | Scope selectors now reject strings at construction and runtime callable results must return `ScopeId` or `Symbol`. Built-in selector symbols remain explicit and validated. | -| Done | Normalizer fallbacks return unknown values unchanged | `src/mtg/ModelSpec.jl` | Fallbacks for input bindings, meteo bindings, and output routing now fail at construction with explicit errors. String scope selectors now error instead of being converted. | -| Done | Broad `Any` and anonymous named tuples in runtime storage | `src/mtg/mapping/mapping.jl`; `src/mtg/GraphSimulation.jl`; `src/time/multirate.jl`; `src/time/runtime/output_export.jl` | Added semantic aliases for model-rate declarations and temporal streams, typed reverse multiscale mappings as `Symbol => Symbol`, and replaced anonymous export-plan named tuples with `OutputExportPlan`. Remaining `Any` storage is for user-provided values/statuses and open extension hooks. | -| Done | Awkward container signatures with broad `AbstractArray` / verbose `where` clauses | `src/dataframe.jl`; `src/checks/dimensions.jl` | Simplified collection signatures to `AbstractVector`/`AbstractDict` forms without unnecessary `where` wrappers. | - -## Brittle Or Overloaded Code - -| Priority | Location | Risk | Recommended cleanup | -| --- | --- | --- | --- | -| Done | `src/dependencies/soft_dependencies.jl` hard-dependency redirection | Nested hard-dependency redirection is duplicated, walks parents with a depth cap, and can match by process without enough scale context. | Extracted shared owner-resolution helpers for nested hard dependencies, with scale-aware matching, ambiguity checks, and finalized soft-node validation. | -| Done | `src/time/runtime/input_resolution.jl` fallback resolution | Same-node, ancestor, and candidate-scan fallback can silently change behavior when topology or scope changes. | Built a shared source-status resolver that centralizes same-node, ancestor, vector, and unique-candidate fallback with scalar ambiguity validation. | -| Done | `src/mtg/initialisation.jl` `RefVector` population | Vector input order depends on MTG traversal order and can drift after growth/removal. | Added `reindex_runtime_topology!` to sort statuses by MTG node id and rebuild downstream `RefVector`s from current statuses after initialization and topology mutations. | -| Done | `src/mtg/mapping/compute_mapping.jl` and `src/mtg/mapping/mapping.jl` mapping sentinels/invariants | Magic sentinel values make mapping control flow fragile. | Same-scale mappings now use `SameScale()` instead of `Symbol("")`; explicit validation rejects the old sentinel. | -| Done | `src/mtg/add_organ.jl` topology mutation | Add/remove/reparent updates local status and refs, but scope-derived temporal keys and environment indexes need centralized invalidation. | Add/remove/reparent now centralize runtime topology reindexing; reparent clears temporal state for the moved subtree before rebuilding status and `RefVector` indexes. | -| Done | `examples/maespa_scene_example.jl` scene model | The example mixes solver math, call-target orchestration, publication, soil feedback, and carbon updates. | Split scene energy-balance solving, leaf publication/carbon update, and soil feedback into separate helpers; added tests for selector mismatch, convergence failure, publication counts, and soil feedback. | -| Done | `src/dependencies/is_graph_cyclic.jl` cycle keys | Cycle detection keys nodes by model value and scale, which can conflate reused model objects. | Traversal now uses dependency-node identity through `IdDict`; cycle reports are converted back to `(model => scale)` for existing diagnostics. | -| Done | `src/time/runtime/bindings.jl` and input binding inference | Producer candidates can lose renamed source-variable identity. | Added `ProducerVariable(input, source)` dependency metadata for renamed multiscale producers, and candidate inference now matches on the consumer input while returning the producer source variable. | - -## Suggested Cleanup Order - -All originally listed cleanup items are now marked done. Keep this page as the -release-note source list and add new rows only for newly discovered cleanup work. +## Status + +The compatibility cleanup is implemented on the `multi-plants` branch. + +The package now has one scenario compiler and runtime: the scene/object API. +The following superseded implementations were removed rather than deprecated: + +- `ModelList` and `SingleScaleModelSet`; +- `ModelMapping` and `MultiScaleModel`; +- `DependencyGraph`, `HardDependencyNode`, and `SoftDependencyNode`; +- `GraphSimulation` and the MTG mapping runner; +- mapping-specific multirate input resolution and output export; +- the unreleased domain prototype that preceded the scene/object API, + including `Domain`, `SimulationMapping`, `Route`, `AllDomains`, + `HardDomains`, and its separate scheduler, runtime, environment bridge, and + output publisher; +- mapping-only initialization, dataframe, dimension, and topology helpers; +- unused parallel-executor traits after removal of the executor runtime; +- dead `UninitializedVar`, `RefVariable`, `TreeAlike`, and `StatusView` types; +- the unreleased `ObjectTemplate(...; mapping=...)` alias and legacy + selector-to-mapping conversion helpers; +- compatibility tests, tutorials, and executable examples. + +Migration details are retained in +[`migration_scene_object.md`](../migration_scene_object.md) and +[`release_notes_handoff.md`](release_notes_handoff.md). + +## Current Ownership + +| Concern | Owner | +| --- | --- | +| Object registry, selectors, compilation, execution, lifecycle | `src/scene_object_api.jl` | +| Model application configuration | `src/ModelSpec.jl` | +| Status and reference vectors | `src/component_models/Status.jl`, `src/component_models/RefVector.jl` | +| Dates-based clocks and policies | `src/time/multirate.jl`, `src/time/runtime/clocks.jl` | +| Weather sampling | `src/time/runtime/meteo_sampling.jl` | +| Environment backends | `src/time/runtime/environment_backends.jl` | +| Output request definition | `src/time/runtime/output_export.jl` | + +## Remaining Review Rules + +Future cleanup should reject: + +- a second scenario/runtime abstraction parallel to `Scene`; +- compatibility wrappers for unreleased APIs; +- package-specific behavior in PlantSimEngine; +- model kernels that know their scenario object, timestep, or coupling unless + the scientific algorithm requires a hard call; +- dynamic per-object dispatch or copying in hot loops when compiled typed + batches and reference carriers are available; +- undocumented public names or agent instructions that describe removed APIs. + +## Verification + +Current cleanup evidence: + +- the `src` tree contains only the scene/object runtime and supporting status, + time, environment, fitting, trait, and example files; +- empty directories left by removed subsystems were deleted; +- `git diff --check` passes; +- PlantSimEngine precompiles and loads from a clean Kaimon session; +- `test/test-unified-scene-object-api.jl` passes 576 tests; +- the complete package environment passes 885 tests, including Aqua and + doctests; +- `test/test-fitting.jl` passes; +- the documentation build passes, including executable examples and + cross-reference checks; +- repository search finds no superseded scenario-runtime definitions or + references in source, tests, examples, README, public docs, or the packaged + agent skill; +- remaining `ModelMapping` and `MultiScaleModel` references outside + development notes are migration text that explicitly points historical code + to the scene/object API. +- repository search finds no `Domain`, `SimulationMapping`, `Route`, + `AllDomains`, or `HardDomains` implementations, exports, tests, examples, or + public documentation. The only remaining references are development/release + notes that record their removal from this unreleased branch; +- ignored `.DS_Store` files were removed from the working tree outside `.git`. +- `ModelSpec` pipe-helper boilerplate was consolidated behind shared internal + helpers while keeping the public `AppliesTo`, `Inputs`, `Calls`, `TimeStep`, + `Environment`, `Updates`, and `OutputRouting` grammar unchanged. +- the duplicate `Advanced.TimeStepTable` export was removed from the PlantMeteo + re-export block; `Advanced.TimeStepTable` remains exported once from the core status + export list. +- stale `PlantSimEngine.Examples` exports for the deleted + `ToyInternodeEmergence` example were removed. + +Downstream verification: + +- PlantBiophysics passes 117/117 tests against this working tree. +- XPalm's uncommitted Scene/Object migration executes 74/75 assertions. The + remaining assertion retains the removed runtime's first-step LAI value, + while the explicit current dependency order produces zero from the initial + zero-biomass leaf before plant/scene aggregation. PlantSimEngine intentionally + contains no package-specific compatibility workaround for that stale fixture. diff --git a/docs/src/dev/maespa_scene_handoff.md b/docs/src/dev/maespa_scene_handoff.md index 19102a58f..043f12e60 100644 --- a/docs/src/dev/maespa_scene_handoff.md +++ b/docs/src/dev/maespa_scene_handoff.md @@ -56,12 +56,12 @@ ModelSpec(LAIModel(ground_area); name=:lai_dynamic) |> TimeStep(Dates.Day(1)) ``` -Allocation is plant-local because its leaf selector uses `within=Self()`: +Allocation is plant-local because its leaf selector uses `within=Subtree()`: ```julia ModelSpec(allocation; name=:allocation) |> AppliesTo(One(scale=:Plant)) |> - Inputs(:leaf_carbon => Many(scale=:Leaf, within=Self(), var=:leaf_carbon)) |> + Inputs(:leaf_carbon => Many(scale=:Leaf, within=Subtree(), var=:leaf_carbon)) |> TimeStep(Dates.Day(1)) ``` diff --git a/docs/src/dev/public_api_refinement_completion_audit.md b/docs/src/dev/public_api_refinement_completion_audit.md new file mode 100644 index 000000000..a81f99fae --- /dev/null +++ b/docs/src/dev/public_api_refinement_completion_audit.md @@ -0,0 +1,43 @@ +# Public API Refinement Completion Audit + +This audit records the supported contract and the evidence used to stabilize it. +It complements the [decision record](public_api_refinement_decisions.md) and the +[public symbol inventory](../API/public_symbols.md). + +## Contract evidence + +| Requirement | Supported contract | Evidence | +|:--|:--|:--| +| Public boundary | Composition, model-author, diagnostic, and extension symbols are exported by default; compiler/cache representations live under `PlantSimEngine.Advanced`. | `test-scene-api-stabilization.jl` checks the namespace boundary; Documenter's missing-doc check covers exported docstrings. | +| Application identity | Repeated process applications require explicit names. Inputs, calls, outputs, and `Updates(...; after=...)` use canonical application IDs. Singular process lookup is a deprecation bridge; `Many(process=...)` remains explicit discovery. | Stabilization, binding-inference, hard-call, output, and update tests cover repeated applications and actionable ambiguity errors. | +| Selector grammar | `Self()` is one object, `Subtree()` is that object plus descendants, `SelfPlant()` is the containing plant, and `SceneScope()` is the scene. `One`, `OptionalOne`, and `Many` share the same criteria across targeting, coupling, lookup, and outputs. | Multi-plant selector tests, instance/template tests, lifecycle tests, and XPalm downstream tests. | +| Outputs | `outputs=:none` is the safe default; `:all` and selector-based `OutputRequest`s are explicit. Request names are unique, application identity is preserved, and removed-object history remains collectable. | Output-boundary, runtime-matrix, multirate, lifecycle-history, and allocation tests. | +| Execution ownership | `run!` starts a fresh simulation; `continue!` and `step!` advance its live handle without resetting time, streams, schedules, or environment position. | Split-run equivalence, multirate-boundary, environment-resume, and lifecycle-continuation tests. | +| Construction and initialization | `Scene(models...; status=...)` lowers to ordinary objects and `ModelSpec`s. `explain_initialization` reports application, object, origin, defaults, expected/provided types, and remedies without running kernels. | Concise/explicit lowering equivalence and initialization report tests. | +| Diagnostics | Supported explanation functions accept `Scene` directly and compiled views where useful; simulation overloads avoid field inspection. Results are structured vectors that can be filtered with ordinary Julia predicates. | Structured explanation assertions throughout the scene test matrix and documentation examples. | +| Lifecycle | Registration, MTG growth, removal, reparenting, movement, and geometry updates are the supported mutation paths. Cycle/self-parent failures are atomic; structural and geometry invalidation remain targeted. | Stabilization, unified integration, environment, and lifecycle-output tests. | +| Model-author API | The kernel remains `run!(model, models, status, meteo, constants, extra)`. `runtime_scene`, call-target accessors, traits, and lifecycle helpers are the supported context surface. | `test-model-contract.jl`, hard-call tests, growing-plant tutorial, and downstream model suites. | +| Compatibility | `tracked_outputs`, singular scenario `process=` references, output-request `process=`, and process-only overrides have targeted warnings and documented replacements, scheduled for removal in 0.15. Mapping runtimes are not restored. | Migration guide plus compatibility tests. | + +## Validation matrix + +The release gate is: + +1. complete PlantSimEngine package tests, including allocation gates and doctests; +2. a full Documenter build with missing-doc and executable-example checks; +3. full PlantBiophysics and XPalm downstream suites against this checkout; +4. benchmark smoke tests for native, multirate, PlantBiophysics, and XPalm paths; +5. `git diff --check` and searches for transitional spellings outside explicit + migration/history documentation. + +This matrix covers one/many objects, all selector multiplicities, soft inputs, +hard calls, duplicate writers, temporal policies, global/spatial environments, +templates, instances, overrides, lifecycle mutation, generic values, output +retention modes, fresh/continued execution, and homogeneous hot-loop allocation. + +## Deliberate compatibility boundary + +Compiled structs and cache controls are qualified advanced APIs and may evolve. +Direct mutation of `Object` or `Scene` fields is unsupported. Historical +`ModelMapping`, executor, and status-vector runtimes are outside the compatibility +surface and must not be reintroduced. diff --git a/docs/src/dev/public_api_refinement_decisions.md b/docs/src/dev/public_api_refinement_decisions.md new file mode 100644 index 000000000..75fba4a22 --- /dev/null +++ b/docs/src/dev/public_api_refinement_decisions.md @@ -0,0 +1,108 @@ +# Public API refinement decisions + +This decision record defines the target public contract for the Scene/Object +API. The Scene/Object compiler and runtime remain the only supported scenario +runtime. + +## Terminology and identity + +- An **object** is one runtime entity with a stable `ObjectId`. +- A **model** is one scientific implementation of a process. +- A **process** is model metadata and may have several applications. +- An **application** is one named, configured occurrence of a model in a scene. +- User declarations that identify a producer, writer, update predecessor, call + target, or output stream use application identity. +- Process queries are discovery filters. They are not substitutes for an + application identifier when more than one application matches. +- Every application receives a deterministic identifier. An explicit + `ModelSpec(...; name=...)` is used verbatim. An unnamed application uses its + process name only when that identifier is unique; repeated unnamed + applications are rejected with instructions to name them. +- Mounted template applications are qualified as + `instance_name__application_name`. + +## Object selectors and scope + +The same `One`, `OptionalOne`, and `Many` selector values are accepted by +application targeting, inputs, calls, object queries, and output requests. + +Scope names have one meaning: + +- `Self()` selects only the current object. +- `Subtree()` selects the current object and all of its descendants. +- `SelfPlant()` selects the current object's plant root and its descendants. +- `Ancestor(...)` selects the matching ancestor's subtree. +- `SceneScope()` searches the whole scene. +- `Scope(name)` searches the named object's subtree. +- `Relation(...)` selects objects with the requested topological relationship. + +Selectors that require a current object fail when used without a context. +Cross-object coupling is always visible in the declaration through `Subtree`, +`SelfPlant`, `Ancestor`, `Scope`, `SceneScope`, or `Relation`. + +## Outputs + +`run!` uses an explicit `outputs` keyword: + +```julia +run!(scene; outputs=:none) +run!(scene; outputs=:all) +run!(scene; outputs=request) +run!(scene; outputs=requests) +``` + +The default is `outputs=:none`. Temporal dependency streams required by the +runtime are still retained with bounded histories; they are not user-retained +outputs. + +`tracked_outputs` is deprecated. During the deprecation period, +`tracked_outputs=nothing` maps to `outputs=:all`, an empty request vector maps +to `outputs=:none`, and other values map to `outputs=value`. + +An `OutputRequest` contains an object selector, a variable, an optional +application identifier, a unique result name, and optional temporal resampling +policy. `OutputRequest(:Leaf, :x)` remains a convenience spelling that lowers +to `OutputRequest(Many(scale=:Leaf), :x)` during migration. + +## Execution and continuation + +`run!(scene; steps=n, ...)` starts a fresh result timeline at step one while +mutating scene status. It returns a live `SceneSimulation` execution handle. + +`continue!(simulation; steps=n)` advances that simulation from its current +step, preserving retained streams, temporal dependency history, environment +position, and multirate clock phase. It returns the same simulation. + +`step!(simulation)` is equivalent to `continue!(simulation; steps=1)`. + +Calling `run!` on an already-mutated scene intentionally creates a new result +timeline. Users who intend temporal continuation use `continue!`; the distinct +operation prevents an accidental step-index reset. + +Lifecycle mutations between calls to `continue!` are compiled before the next +timestep using the existing targeted invalidation contract. + +## Public namespaces + +The default namespace is organized around: + +- scene composition and execution; +- model-author declarations and kernel helpers; +- supported structured explanations; +- documented environment extension interfaces. + +Compiled representation types, cache dirty flags, raw compiler stages, and +low-level invalidation helpers are qualified advanced/internal APIs unless a +documented external extension requires them. Removing an export does not make a +symbol inaccessible through `PlantSimEngine.Symbol`; it removes the accidental +promise that ordinary users should depend on it. + +## Compatibility policy + +- Removed legacy mapping/executor APIs are not restored. +- Current Scene/Object spellings receive targeted deprecations only when they + have a clear replacement. +- New canonical spellings are implemented and tested before deprecated aliases + are removed. +- Benchmarks, examples, documentation, PlantBiophysics, and XPalm target the + canonical API rather than deprecated compatibility paths. diff --git a/docs/src/dev/release_notes_handoff.md b/docs/src/dev/release_notes_handoff.md index c5290593d..e03a40e41 100644 --- a/docs/src/dev/release_notes_handoff.md +++ b/docs/src/dev/release_notes_handoff.md @@ -1,47 +1,70 @@ # Release Notes Handoff -This page is the persistent release-note source for work done during the -multi-domain and cleanup branch. Keep it factual: mark what is implemented, -what is removed, and what is only planned. +This page is the persistent release-note source for the scene/object redesign +and cleanup branch. Keep it factual: mark what is implemented, what is removed, +and what is only planned. ## Implemented Breaking Cleanup Source details live in `code_cleanup_audit.md`. -- Removed public `ModelList` usage. New simulations should use `Scene` and - model applications. Retained mapping compatibility code can use - `PlantSimEngine.ModelMapping(model...; status=...)`. -- Removed direct `run!(::ModelList, ...)`; retained mapping code must wrap - models in `PlantSimEngine.ModelMapping`. -- Removed batch `run!` over collections/dictionaries of single-scale mappings; - use explicit loops or comprehensions. -- Removed raw `Dict` multiscale `run!(mtg, dict, ...)`; construct - `PlantSimEngine.ModelMapping(dict)` first. +- Removed `ModelList`, `ModelMapping`, `GraphSimulation`, `MultiScaleModel`, + and the separate mapping dependency/runtime stack. Use `Scene`, `Object`, + and model applications. +- Removed direct and batch mapping `run!` methods. - Removed string scale names. Use symbols, for example `:Leaf`. -- Removed `PlantSimEngine.ModelMapping(Float64 => Float32)` promotion shorthand. Use a - `Dict(Float64 => Float32)` as the `type_promotion` value. -- Removed `ModelMapping` from exports. Historical mapping simulations remain - available through the explicitly qualified - `PlantSimEngine.ModelMapping(...)` compatibility API. +- Removed mapping-specific type-promotion configuration. +- Removed `ModelMapping` completely; it is not retained as a qualified + compatibility API. - Removed old multiscale output indexing helpers. Convert outputs explicitly before indexing. -- Replaced the `Symbol("")` same-scale sentinel with `SameScale()`. +- Replaced mapping-specific same-scale rename sentinels with + `Inputs(:local => One(within=Self(), var=:source))`. +- Removed unused parallel-executor traits after deleting the executor runtime. +- Removed dead mapping-era wrappers and traits: `UninitializedVar`, + `RefVariable`, `TreeAlike`, and `StatusView`. +- Removed the unreleased `ObjectTemplate(...; mapping=...)` alias and dead + selector-to-mapping conversion helpers. +- Removed stale `PlantSimEngine.Examples` exports for the deleted + `ToyInternodeEmergence` example. - Replaced many source-side validation `@assert`s with explicit errors. -- Added `Updates(:var; after=:process)` for ordered duplicate writers. +- Added `Updates(:var; after=:application)` for ordered duplicate writers. +- Added `runtime_scene(runtime)` as the sanctioned live-scene accessor for + `SceneRunContext` and `SceneSimulation`; kernels no longer need to inspect + `extra.compiled.scene`. +- Added `explain_initialization(scene)` with structured `:supplied`, + `:generated`, `:producer_bound`, `:environment_bound`, and `:unresolved` + dispositions. +- Added `Scene(model, models...; status=...)` as a thin one-object constructor + that lowers to the normal object and `ModelSpec` representation. +- Calendar-aligned windows remain unsupported. Temporal windows use + duration-based `Dates.Period` semantics. -## Removed Unreleased Domain Prototype +## Removed Unreleased Scenario Prototype -The experimental domain runtime was developed and replaced on this branch +An experimental scenario runtime was developed and replaced on this branch before release. Its source, tests, examples, and documentation were removed -rather than retained as compatibility code. The removed surface included the -domain containers, cross-domain route types, domain dependency selectors, -domain target handles, and domain-specific explanation helpers. +rather than retained as compatibility code. + +The removed API included `Domain`, `SimulationMapping`, `Route`, +`AllDomains`, and `HardDomains`, together with the domain scheduler, run loops, +route materialization, environment bridge, graph runner, and output publisher. +Because this API was never released, there is no compatibility layer or user +migration path for it. The reusable behavior now lives in the scene/object runtime: object selectors, compiled `Inputs(...)`, manual `Calls(...)`, `Dates`-based scheduling, environment backends, dynamic object lifecycle handling, and structured explanations. +Dynamic MTG growth now has one public high-level operation: `add_organ!`. +An MTG-backed `Scene` retains the accessors and status initializer used during +initial adaptation. `add_organ!` reuses that policy for new nodes, merges +explicit initial values, attaches the resulting `Status`, registers the scene +object, and invalidates runtime bindings. `register_object!` remains available +as the low-level registry operation. XPalm and PlantGeom were migrated away +from package-local wrappers that duplicated this lifecycle sequence. + ## Implemented MAESPA-Style Example Changes The current `examples/maespa_scene_example.jl` is the main executable example @@ -83,9 +106,9 @@ for multi-plant scene coupling. constrained by an explicit scope. - `ObjectAddress` explanations now preserve positional selector criteria such as `Scale(:Leaf)` and `Relation(:parent)`. -- Adds the first compiled scene/object view with `compile_scene`, - `CompiledScene`, `CompiledSceneApplication`, `CompiledSceneInputBinding`, - `CompiledSceneCallBinding`, `explain_scene_applications`, +- Adds the first compiled scene/object view with `Advanced.compile_scene`, + `Advanced.CompiledScene`, `Advanced.CompiledSceneApplication`, `Advanced.CompiledSceneInputBinding`, + `Advanced.CompiledSceneCallBinding`, `explain_scene_applications`, `explain_bindings`, and `explain_calls`. - The compiled scene view resolves `AppliesTo(...)`, `Inputs(...)`, and `Calls(...)` to object ids ahead of runtime, and reports temporal policy, @@ -96,7 +119,7 @@ for multi-plant scene coupling. to `Self()`. Cross-scope shared dependencies, such as leaf models reading soil state, should use `within=SceneScope()` explicitly. - Adds status-backed compiled input carriers for the scene/object view: - scalar shared refs, homogeneous `RefVector`s, and `ObjectRefVector` fallback + scalar shared refs, homogeneous `RefVector`s, and `Advanced.ObjectRefVector` fallback carriers. `input_carrier`, `input_value`, and `has_reference_carrier` expose them for tests, diagnostics, and future runtime execution. - Same-rate scene inputs are now wired into consumer `Status` references once @@ -114,14 +137,14 @@ for multi-plant scene coupling. semantics, making reference-wired inputs and materialized temporal values explicit for users and agents. - Adds scene binding cache helpers: - `refresh_bindings!`, `bindings_dirty`, `compiled_bindings`, and - `scene_revision`. Object registration, removal, and reparenting invalidate + `Advanced.refresh_bindings!`, `Advanced.bindings_dirty`, `Advanced.compiled_bindings`, and + `Advanced.scene_revision`. Object registration, removal, and reparenting invalidate cached compiled bindings before the next refresh. - Adds scene/object environment binding cache helpers: - `refresh_environment_bindings!`, `compile_environment_bindings`, - `CompiledEnvironmentBinding`, `CompiledEnvironmentBindings`, - `environment_bindings_dirty`, `compiled_environment_bindings`, - `environment_revision`, and `explain_environment_bindings`. + `Advanced.refresh_environment_bindings!`, `Advanced.compile_environment_bindings`, + `Advanced.CompiledEnvironmentBinding`, `Advanced.CompiledEnvironmentBindings`, + `Advanced.environment_bindings_dirty`, `Advanced.compiled_environment_bindings`, + `Advanced.environment_revision`, and `explain_environment_bindings`. - Adds `geometry`, `position`, and `bounds` accessors for scene objects/statuses. Environment binding refreshes call `update_index!(backend, entities)` before binding objects to backend cells/layers, so spatial backends can precompute @@ -161,18 +184,18 @@ for multi-plant scene coupling. - Compiled input bindings now validate `Inputs(...)` `process=`/`application=` filters when they are provided, and `explain_bindings` reports `source_application_ids`, `process`, and `application`. -- `compile_scene` now errors for required `inputs_(model)` variables that are +- `Advanced.compile_scene` now errors for required `inputs_(model)` variables that are neither bound through `Inputs(...)`/inference nor present on the target object `Status`. -- `compile_scene` now prepares model-owned status schemas automatically: +- `Advanced.compile_scene` now prepares model-owned status schemas automatically: model-targeted objects may omit `Status`, declared model/environment outputs are inserted from trait defaults, and bound consumer inputs are generated from `inputs_` defaults. External unbound inputs still require explicit initialization. -- `compile_scene` now rejects `Inputs(...)` entries whose receiving variable is +- `Advanced.compile_scene` now rejects `Inputs(...)` entries whose receiving variable is not declared by the model's `inputs_`, making binding typos explicit at compile time. -- `compile_scene` now validates status-backed non-temporal `Inputs(...)` +- `Advanced.compile_scene` now validates status-backed non-temporal `Inputs(...)` source availability, so bindings that select existing source objects but no source `Status` reference fail at compile time instead of becoming no-ops. - Scene/object runtime now publishes model outputs to scene-local temporal @@ -201,13 +224,13 @@ for multi-plant scene coupling. Dependencies on manual-call-only applications are redirected to their parent caller, same-timestep cycles fail during compilation, and `explain_schedule` reports `execution_index`. -- `CompiledScene` now pre-indexes input and call bindings by application and +- `Advanced.CompiledScene` now pre-indexes input and call bindings by application and object id. Runtime input materialization and hard-call lookup no longer scan all scene bindings for every object/model invocation. -- `CompiledScene` now pre-indexes applications by application id, removing +- `Advanced.CompiledScene` now pre-indexes applications by application id, removing application scans from hard-call target resolution and dictionary rebuilding from ordered execution setup. -- `CompiledEnvironmentBindings` now pre-indexes environment bindings by +- `Advanced.CompiledEnvironmentBindings` now pre-indexes environment bindings by application and object id, removing the scene-wide binding scan from environment sampling and output scattering. - Adds `SceneRunContext` and `SceneCallTarget`; scene/object models can use @@ -216,7 +239,7 @@ for multi-plant scene coupling. - Applications selected by `Calls(...)` are skipped by the root `run!(scene)` loop and execute only through explicit `run_call!`, preserving parent-controlled hard-call execution. -- Adds scene/object duplicate-writer validation in `compile_scene`. A variable +- Adds scene/object duplicate-writer validation in `Advanced.compile_scene`. A variable may have only one canonical writer per object unless later writers declare `Updates(:var; after=...)`, where `after` can match a previous application id/name or process. @@ -317,8 +340,7 @@ for multi-plant scene coupling. explicit process matches both a stream-only and a canonical publisher. - `OutputRequest(...)` now accepts `application=...` for scene/object runs. This disambiguates repeated applications of the same process and permits - explicit export of a named `:stream_only` publisher. Legacy - `GraphSimulation` output export rejects this scene-specific selector. + explicit export of a named `:stream_only` publisher. - Scene temporal streams now retain a concrete value type per application/object/output stream. Type changes fail explicitly, while generic values such as `BigFloat` remain typed through publication, @@ -353,7 +375,7 @@ for multi-plant scene coupling. - Added runtime lifecycle coverage for organ creation, pruning, plant-local `RefVector` refresh, historical output retention for removed objects, and movement between mock microclimate cells. -- `CompiledScene` now precompiles one process-keyed model bundle per +- `Advanced.CompiledScene` now precompiles one process-keyed model bundle per application/object target. Generic hard-dependency kernels receive this cached bundle through the existing `models` argument, avoiding recursive `Calls(...)` traversal and temporary collection allocation in the timestep @@ -369,7 +391,7 @@ for multi-plant scene coupling. - Manual `Calls(...)` handles now use the public `call_target(extra, name)`/`call_targets(extra, name)` lookup API followed by `run_call!`. -- Removed the unreleased domain/route authoring and runtime subsystem after +- Removed the unreleased intermediate authoring and runtime subsystem after scene/object feature parity was established. - Adds `objects_from_mtg(root; ...)` and `Scene(mtg; ...)` so existing MTG topology can be adapted once into the unified registry while preserving @@ -384,9 +406,9 @@ for multi-plant scene coupling. ## Compatibility Boundary The scene/object runtime and its MAESPA acceptance path are implemented. -Historical mapping APIs remain only as an explicitly qualified compatibility -and regression layer. The unreleased domain prototype was removed. The design, -implementation history, and completion evidence are documented in: +Historical mapping APIs and the unreleased intermediate prototype were +removed. The design, implementation history, and completion evidence are +documented in: - `unified_scene_object_design.md` - `unified_scene_object_implementation_plan.md` @@ -394,8 +416,8 @@ implementation history, and completion evidence are documented in: The completed public migration is: -- replace historical qualified compatibility tutorials with native - scene/object tutorials where long-term coverage is still valuable; +- replace historical tutorials with native scene/object tutorials where + long-term coverage is still valuable; - model mappings should be described as model applications: `ModelSpec(model; name=...) |> AppliesTo(...) |> Inputs(...) |> Calls(...)`; - `MultiScaleModel(...)` -> `Inputs(...)`. @@ -414,9 +436,9 @@ The completed public migration is: - explicit per-model meteo wiring -> automatic environment resolver plus cached environment bindings. -Historical mapping examples and tests that remain are intentionally retained -as a separate qualified compatibility layer. Their continued existence does -not make them part of the public scene/object API. +Historical mapping examples, tests, and runtime files were removed after the +scene/object acceptance path reached feature parity. Migration information is +kept in this release-note handoff and the user-facing migration guide. ## Migration Documentation Added @@ -430,11 +452,10 @@ not make them part of the public scene/object API. scene/object examples. The page now introduces `Scene`, `Object`, `ModelSpec`, `AppliesTo`, `Inputs`, `TimeStep`, inferred same-object bindings, multi-object `Many(...)` inputs, and manual `Calls(...)` syntax - before linking to legacy mapping reference pages. + before linking to the migration guide. - Replaced the repository README examples with scene/object-first examples. The README now introduces `Scene`, `Object`, model applications, - multi-object `Inputs(...)`, and `Calls(...)`, and treats `ModelMapping` / - `MultiScaleModel` as compatibility APIs. + multi-object `Inputs(...)`, and `Calls(...)`. - Added a native scene/object quickstart page to the main documentation navigation. It provides docs-tested examples for one-object model chaining, inferred bindings, requested output retention, multi-object `Inputs(...)`, @@ -442,25 +463,21 @@ not make them part of the public scene/object API. - Rewrote the model execution page as the current scene/object execution guide. It now covers compilation, reference carriers, temporal `Inputs(...)`, manual `Calls(...)`, `Updates(...)`, `TimeStep(...)`, environment binding, - retained outputs, lifecycle invalidation, and compatibility translations for + retained outputs, lifecycle invalidation, and migration translations for historical mapping constructs. - Rewrote the detailed first simulation tutorial to use the scene/object API. It now introduces `Scene`, `Object`, `ModelSpec`, `AppliesTo`, `TimeStep`, compiled applications, inferred same-object bindings, scene outputs, and a - compatibility note for historical `ModelMapping` examples. + migration note for historical examples. - Rewrote the quick examples page to use native scene/object snippets for Beer light interception, degree-days/LAI/light coupling, biomass growth, and - retained `OutputRequest` exports. Historical `ModelMapping` usage is now - confined to the compatibility note. + retained `OutputRequest` exports. Historical mapping usage is confined to + migration records. - Rewrote the standard model coupling, model switching, and coupling more complex models tutorials around the scene/object API. These pages now show inferred same-object value bindings, switching one `ModelSpec` application, - execution-plan explanations, and `Calls(...)` manual-call wiring before - mentioning `PlantSimEngine.ModelMapping(...)` as compatibility syntax. -- Removed legacy mapping transforms from exports: + execution-plan explanations, and `Calls(...)` manual-call wiring. +- Removed legacy mapping transforms and their runtime implementations: `MultiScaleModel`, `SameScale`, `TimeStepModel`, `InputBindings`, - `MeteoBindings`, `MeteoWindow`, and `ScopeModel`. Historical executable - examples use qualified compatibility names and are grouped under legacy - documentation sections. -- Added a curated unified scene/object map to the public API page and labeled - the remaining mapping-level multirate reference as legacy. + `MeteoBindings`, `MeteoWindow`, and `ScopeModel`. +- Added a curated unified scene/object map to the public API page. diff --git a/docs/src/dev/unified_scene_object_completion_audit.md b/docs/src/dev/unified_scene_object_completion_audit.md index 72da34ce7..38948cf9f 100644 --- a/docs/src/dev/unified_scene_object_completion_audit.md +++ b/docs/src/dev/unified_scene_object_completion_audit.md @@ -11,9 +11,8 @@ Audit date: June 12, 2026. The unified scene/object redesign is implemented as the primary public configuration API. -The retained mapping implementation forms an explicitly qualified -compatibility and regression layer. The unreleased domain prototype was -removed after the scene/object runtime replaced it. +The superseded mapping implementation and the unreleased intermediate +prototype were removed after the scene/object runtime replaced them. ## Public Contract @@ -31,8 +30,8 @@ TimeStep Environment ``` -Legacy scenario constructors such as `ModelMapping` and `MultiScaleModel` are -not exported. +Legacy scenario constructors such as `ModelMapping` and `MultiScaleModel` were +removed. Evidence: @@ -47,7 +46,7 @@ Evidence: | --- | --- | | One scene object registry without prescribing plant topology | `Scene`, `Object`, selectors, relations, scopes, and `objects_from_mtg` in `src/scene_object_api.jl`; selector and MTG adapter tests in `test/test-unified-scene-object-api.jl` | | Reusable species models and repeated plant instances | `ObjectTemplate`, `ObjectInstance`, `Override`, shared model ownership, and homogeneous/heterogeneous batches; four-instance and override tests | -| Soft/value dependencies | Compiled `Inputs(...)` bindings, same-object inference, scalar references, `RefVector`, `ObjectRefVector`, temporal carriers, renaming, and optional inputs | +| Soft/value dependencies | Compiled `Inputs(...)` bindings, same-object inference, scalar references, `RefVector`, `Advanced.ObjectRefVector`, temporal carriers, renaming, and optional inputs | | Manual hard dependencies | Compiled `Calls(...)`, `SceneCallTarget`, `call_target(s)`, and `run_call!`; trial calls default to `publish=false` and accepted calls publish explicitly | | Model-author dependency defaults | `Input(...)` and `Call(...)` entries from `dep(model)`, with `ModelSpec` overrides and binding provenance in explanations | | Multirate execution | `TimeStep(Dates.Period)`, model `timespec`, input policies, explicit windows, `PreviousTimeStep`, and stable compiled scheduling | @@ -57,6 +56,9 @@ Evidence: | Growth, pruning, reparenting, and movement | Central lifecycle APIs, structural/environment cache invalidation, dynamic target/carrier rebuilding, removed-object output history, and object-scoped geometry refresh tests | | Automatic meteorology and microclimate | `meteo_inputs_`, `meteo_outputs_`, global and spatial backends, ancestor geometry fallback, source remapping, model hints, tabular aggregation, cached bindings, and mutable backend scattering | | Structured agent explanations | Object, instance, scope, application, binding, call, model-bundle, environment, schedule, writer, execution-plan, output, and retention explanations returning structured rows | +| Initialization workflow | `explain_initialization` classifies supplied, generated, producer-bound, environment-bound, and unresolved variables without failing solely on unresolved values | +| Runtime scene access | `runtime_scene` is the public accessor used by lifecycle-capable kernels and accepts `Scene`, `SceneRunContext`, and `SceneSimulation` | +| One-object ergonomics | `Scene(model, models...; status=...)` lowers to the same object/application compiler and runtime as explicit construction | | Output ownership and retention | Application-qualified streams, `OutputRouting`, `OutputRequest(application=...)`, dynamic-object exports, and bounded policy-specific dependency histories | | MAESPA acceptance case | `build_maespa_scene` and `run_maespa_example` use `ObjectTemplate`, `ObjectInstance`, `AppliesTo`, `Inputs`, `Calls`, and `TimeStep`; verified by `test/test-maespa-scene-example.jl` | | Documentation and migration | Scene/object-first README, home page, quickstart, execution guide, public API, migration guide, and explicitly labeled legacy reference sections | @@ -66,34 +68,32 @@ Evidence: The following gates passed from a clean, controllable Kaimon Julia session: ```text -test/test-unified-scene-object-api.jl -test/test-maespa-scene-example.jl -test/test-multirate-output-export.jl -test/runtests.jl -docs/make.jl -git diff --check +test/test-unified-scene-object-api.jl 576 passed +test/runtests.jl 885 passed +docs/make.jl passed +PlantBiophysics/test/runtests.jl 117 passed +git diff --check passed ``` -The complete package suite finished successfully in approximately ten minutes. -The documentation build, including examples and doctests, also completed -successfully. +The complete package suite includes Aqua, all focused restoration files, the +broad unified runtime regression, examples, fitting, and doctests. The +documentation build completed its executable examples, doctests, +cross-references, document checks, and HTML rendering successfully. -`Aqua.jl` was not installed in the active environment, so the optional -`lint_package` gate could not be run. This is not a package test dependency and -does not weaken the behavioral verification above. +The current uncommitted XPalm Scene/Object migration loads this PlantSimEngine +worktree and executes 74 of 75 downstream assertions. Its remaining assertion +expects the removed runtime's first-step LAI (`0.000272`); the current compiled +dependency order correctly runs leaf area before plant and scene aggregation, +so the initial zero-biomass leaf produces `0.0`. This is a downstream fixture +expectation in a dirty migration worktree, not a package-specific behavior to +restore in PlantSimEngine. No XPalm workaround was added here. ## Compatibility Boundary -Historical mapping source and tests remain intentionally available for: - -- migration comparison; -- regression evidence; -- downstream users that need a qualified transition path. - -They must remain qualified as `PlantSimEngine.*` and must not be presented as -the primary API. The branch-only domain prototype and its tests were removed -because they were never released and no longer serve as a compatibility -boundary. +Historical mapping source and tests were removed. Migration guidance remains +in the documentation for downstream packages moving to the scene/object API. +The branch-only intermediate prototype was also removed because it was never +released and has no compatibility boundary. Requested output histories are still materialized after a run. Dependency-only temporal histories are bounded, but a fully online output sink would be an diff --git a/docs/src/dev/unified_scene_object_design.md b/docs/src/dev/unified_scene_object_design.md index c7d0845f1..05e7c583c 100644 --- a/docs/src/dev/unified_scene_object_design.md +++ b/docs/src/dev/unified_scene_object_design.md @@ -1,24 +1,16 @@ # Unified Scene/Object Design -> **Historical design note** -> -> This document records the redesign process. Names such as `Domain`, -> `Route`, `AllDomains`, and `HardDomains` refer to an unreleased intermediate -> prototype that has been removed from the package. - -This page records the target breaking design discussed after the multi-domain -prototype. It intentionally supersedes the user-facing distinction between -`MultiScaleModel(...)` mappings and `Route(...)` cross-domain materialization. +This page records the target breaking design for one scene/object +configuration and runtime API. The central idea is: -> Domains and scales are not fundamentally different concepts. They are both -> selections over objects in one scene. +> Structural groupings and scales are selections over objects in one scene. The engine should expose one way to say "this model input comes from these objects" and one way to say "this model must manually call these models". The compiler can then choose whether the runtime carrier is a `Ref`, `RefVector`, -temporal stream, route materialization, or callable model handle. +temporal stream, materialized value, or callable model handle. The public API should be simple enough to remember as: @@ -104,20 +96,17 @@ Kind(:plant) Species(:oil_palm) ``` -`Self()` means the current model application object or scope. It does not -always mean "the current plant". If a model runs on a `:Plant`, `Self()` means -that plant object or subtree. If a model runs on an `:Axis`, it means that axis. -If a model runs on a `:Leaf`, it means that leaf. If a model runs on the scene -object, it means the scene object/scope. +`Self()` means only the current model application object. `Subtree()` means +that object and its descendants. Neither spelling changes meaning with scale. `SelfPlant()` is the nearest containing plant scope. The more generic form is `Ancestor(scale=:Plant)`. Use these when a model running below the plant scale must access siblings or state inside the containing plant. -Reusable plant models should default to scope-relative queries. If an -allocation model is applied to each `:Plant`, `Many(scale=:Leaf, within=Self())` -means "the leaves inside this plant", not all leaves in the scene. The same -query applied to an axis-scale model would mean "the leaves inside this axis". +Reusable plant models should use scope-relative queries. If an allocation +model is applied to each `:Plant`, `Many(scale=:Leaf, within=Subtree())` means +"the leaves inside this plant", not all leaves in the scene. The same query +applied to an axis-scale model means "the leaves inside this axis". Scene-level models widen the scope explicitly with `within=SceneScope()`. @@ -148,7 +137,7 @@ The same template can be mounted several times: oil_palm = ObjectTemplate( kind=:plant, species=:oil_palm, - mapping=oil_palm_mapping, + applications=oil_palm_applications, parameters=oil_palm_parameters, ) @@ -228,14 +217,13 @@ is ambiguous. ## Unified Model Configuration -`ModelSpec` should become the single scenario wrapper. `MultiScaleModel(...)`, -`AllDomains(...)`, `HardDomains(...)`, and user-written `Route(...)` should be -replaced by explicit value inputs and callable model calls. +`ModelSpec` is the single scenario wrapper. Released mapping-era configuration +is replaced by explicit value inputs and callable model calls. ### Applies To Use `AppliesTo(...)` to declare the object set where a model application runs. -This should be first-class, not inferred from a domain or mapping key. +This should be first-class, not inferred from a container or mapping key. ```julia ModelSpec(LeafState()) |> @@ -255,9 +243,9 @@ application to a stable application id. ### Dependency Defaults From Traits Model authors should still declare `inputs_`, `outputs_`, and `dep`. In the -new design, `dep(model)` becomes the model-level place for default dependency -intent, for both the current `ModelMapping` use case and the future scene/object -runtime. +final design, `dep(model)` is the model-level place for default dependency +intent. Historical `ModelMapping` declarations are migration inputs to the +Scene/Object runtime, not a second supported path. The rule is: @@ -272,7 +260,7 @@ For example, a plant allocation model can provide a plant-local default: ```julia dep(::PlantAllocationModel) = ( - leaf_carbon = Input(Many(scale=:Leaf, within=Self(), var=:leaf_carbon)), + leaf_carbon = Input(Many(scale=:Leaf, within=Subtree(), var=:leaf_carbon)), ) ``` @@ -314,7 +302,7 @@ Reusable plant allocation: ```julia ModelSpec(AllocationModel()) |> AppliesTo(Many(scale=:Plant)) |> - Inputs(:leaf_carbon => Many(scale=:Leaf, within=Self(), var=:leaf_carbon)) + Inputs(:leaf_carbon => Many(scale=:Leaf, within=Subtree(), var=:leaf_carbon)) ``` The same declaration must compile to: @@ -322,7 +310,7 @@ The same declaration must compile to: - direct `Ref`/`RefVector` wiring when producer and consumer live in the same object graph and rate; - temporal stream reads when producer and consumer run at different rates; -- current route materialization when target status must be assigned before a +- materialization when target status must be assigned before a model runs; - source-status lookup for graph-backed object selections. @@ -344,7 +332,7 @@ explainable: | same-rate `Many(...)` input | `RefVector` or equivalent typed reference collection | no copy for live values | | cross-rate input | temporal stream sample | value materialized for the consumer timestep | | `Integrate` or `Aggregate` input | temporal window reduction | reduced value materialized | -| route-like target status input | compiler-generated materialization | assigned before consumer run | +| materialized target status input | compiler-generated assignment | assigned before consumer run | | environment input | cached `EnvironmentBinding` sample | backend-defined value sample | This table is a required part of the design because performance, units, @@ -370,7 +358,7 @@ ModelSpec(PlantAllocation()) |> AppliesTo(Many(kind=:plant, scale=:Plant)) |> Inputs(:leaf_assimilation => Many( scale=:Leaf, - within=Self(), + within=Subtree(), var=:assimilation, policy=Integrate(), window=Day(1), @@ -420,7 +408,7 @@ ModelSpec(SceneEB()) |> scale=:Leaf, process=:energy_balance, )) |> - Calls(:soil => One(kind=:soil, process=:soil_water)) + Calls(:soil => One(kind=:soil, application=:soil_water)) ``` Inside `run!`, the scene model receives call handles and calls @@ -506,7 +494,7 @@ register_object!(scene, object; parent) remove_object!(scene, object) reparent_object!(scene, object, new_parent) move_object!(scene, object, geometry_or_position) -refresh_bindings!(scene) +Advanced.refresh_bindings!(scene) ``` Spatial environment backends should depend on a small geometry contract, not on @@ -579,7 +567,7 @@ backends. The environment backend protocol should be small and backend-oriented: ```julia -bind_environment(scene, backend, object) +Advanced.bind_environment(scene, backend, object) sample_environment(backend, binding, time, variables) scatter_environment!(backend, binding, values) refresh_environment!(backend, scene) @@ -732,22 +720,10 @@ and bounded temporal-dependency streams. Errors should report concrete object labels, scope selectors, process names, variables, and suggested fixes. -## Compatibility Position - -This is a breaking target design. It should preserve model kernels and the -`run!(model, models, status, meteo, constants, extra)` contract when possible, -but it may replace the scenario configuration surface: - -- `MultiScaleModel(...)` becomes `Inputs(...)`; -- `Route(...)` becomes a compiler-generated carrier for `Inputs(...)`; -- `AllDomains(...)` becomes a selector used inside `Inputs(...)`; -- `HardDomains(...)` becomes `Calls(...)`; -- `Domain(...)` becomes an object scope/template/instance concept; -- `InputBindings(...)` becomes explicit policy and source information on - `Inputs(...)`; -- `MeteoBindings(...)` becomes automatic environment binding plus optional - `Environment(...)` overrides; -- `OutputRouting(...)` remains model-application output configuration or is - folded into a clearer output policy modifier; -- `PreviousTimeStep(...)` remains a temporal policy/cycle-breaking marker in - the unified graph. +## API Position + +This is a breaking design. It preserves model kernels and the +`run!(model, models, status, meteo, constants, extra)` contract while replacing +the scenario configuration surface with `Scene`, `Object`, `ModelSpec`, +selectors, `Inputs(...)`, `Calls(...)`, `TimeStep(...)`, and +`Environment(...)`. diff --git a/docs/src/dev/unified_scene_object_implementation_plan.md b/docs/src/dev/unified_scene_object_implementation_plan.md index 2ca9222f9..0f2bb04bb 100644 --- a/docs/src/dev/unified_scene_object_implementation_plan.md +++ b/docs/src/dev/unified_scene_object_implementation_plan.md @@ -1,8 +1,7 @@ # Unified Scene/Object Implementation Plan This plan is the persistent handoff for replacing the historical -multiscale-mapping system and an unreleased domain prototype with one -scene/object address system. +multiscale-mapping system with one scene/object address system. The implementation can be incremental internally, but the target API is breaking. Do not preserve experimental intermediate APIs as user-facing @@ -26,14 +25,6 @@ Environment(...) This is the API memory target for users, modelers, and agents. Additional types should be selectors, model traits, or internal compiled carriers. -## Experimental Starting Point - -The branch used a temporary domain runtime as a behavioral test bed for -multi-object selection, cross-object values, manual child calls, multi-rate -scheduling, environment backends, lifecycle changes, ordered writers, and -structured explanations. The scene/object API reproduced those capabilities, -then the temporary runtime was removed before release. - ## Implementation Progress - Started Phase 0 by adding the public API vocabulary as real typed metadata: @@ -47,7 +38,7 @@ then the temporary runtime was removed before release. `Scale`, `Relation`, `One`, `OptionalOne`, `Many`, and `ObjectAddress`. - Added initial `Scene`/`Object` registry types and lifecycle hooks: `register_object!`, `remove_object!`, `reparent_object!`, `move_object!`, - and `refresh_bindings!`. + and `Advanced.refresh_bindings!`. - Added registry-backed selector resolution with `resolve_object_ids` and `resolve_objects` for `SceneScope()`, `Self()`, `SelfPlant()`, `Ancestor(...)`, `Scope(...)`, positional selectors such as @@ -69,7 +60,7 @@ then the temporary runtime was removed before release. - `ObjectAddress(selector)` now normalizes positional `Kind`, `Species`, `Scale`, scope, and `Relation` selectors instead of recording only keyword criteria. -- Started the object-address compiler with `compile_scene(scene, specs)` and +- Started the object-address compiler with `Advanced.compile_scene(scene, specs)` and compiled scene application/binding carriers. The compiler now resolves `AppliesTo(...)` target object ids, object-relative `Inputs(...)` source object ids, and object-relative `Calls(...)` callee object/application ids @@ -80,7 +71,7 @@ then the temporary runtime was removed before release. window, and carrier hints. - Added status-backed compiled input carriers. When source objects already hold `Status` values, `Inputs(...)` bindings now precompile a scalar shared - `Ref`, a homogeneous `RefVector`, or an `ObjectRefVector` fallback for + `Ref`, a homogeneous `RefVector`, or an `Advanced.ObjectRefVector` fallback for heterogeneous reference-preserving vectors. `input_carrier`, `input_value`, and `has_reference_carrier` expose these carriers, and `explain_bindings` reports carrier kind, copy/reference semantics, carrier type, and reference @@ -88,11 +79,11 @@ then the temporary runtime was removed before release. - Added conservative same-object input inference in the scene compiler. When a model declares an `inputs_` variable that is not covered by explicit/default `Inputs(...)`, and exactly one other application on the same object outputs - the same variable, `compile_scene` creates an inferred reference binding. + the same variable, `Advanced.compile_scene` creates an inferred reference binding. `explain_bindings` now reports binding `origin` values such as `:model_default`, `:model_spec`, and `:inferred_same_object`. - Compiled input bindings now carry producer metadata. When an `Inputs(...)` - selector uses `process=` or `application=`, `compile_scene` validates that a + selector uses `process=` or `application=`, `Advanced.compile_scene` validates that a matching source application exists for the selected source objects. `explain_bindings` reports `source_application_ids`, `process`, and `application` for agent-readable dependency diagnostics. @@ -101,7 +92,7 @@ then the temporary runtime was removed before release. scene objects default to `SceneScope()`, while non-scene objects default to `Self()`. Shared scene/soil dependencies from organs should therefore use `within=SceneScope()` explicitly. -- `compile_scene` now validates required status inputs from `inputs_(model)`. +- `Advanced.compile_scene` now validates required status inputs from `inputs_(model)`. Each required input must either have a compiled binding or already exist on the target object `Status`; otherwise compilation errors with the concrete application id, object id, and input variable. @@ -109,11 +100,11 @@ then the temporary runtime was removed before release. status is omitted, inserts missing `outputs_` and `meteo_outputs_` fields from model defaults, and inserts explicitly or implicitly bound input fields from `inputs_` defaults. Unbound external inputs remain compilation errors. -- `compile_scene` now rejects `Inputs(...)` declarations whose left-hand +- `Advanced.compile_scene` now rejects `Inputs(...)` declarations whose left-hand variable is not declared by the target model's `inputs_`. This catches misspelled or stale scenario bindings before they create silent unused metadata. -- `compile_scene` now validates source availability for status-backed +- `Advanced.compile_scene` now validates source availability for status-backed non-temporal `Inputs(...)` bindings. When selected source objects already have `Status` values, the requested source variable must resolve to references instead of silently compiling to an unused/no-op binding. @@ -128,7 +119,7 @@ then the temporary runtime was removed before release. - Same-rate input carriers are installed directly into consumer `Status` reference cells during scene compilation. Scalar bindings share the source `Ref`; many-object bindings store the compiled `RefVector` or - `ObjectRefVector` once. The timestep runtime performs no assignment for + `Advanced.ObjectRefVector` once. The timestep runtime performs no assignment for these bindings, and a focused `Many(...)` materialization gate verifies zero allocations after compilation. - Reference wiring adds a missing bound input field to the consumer `Status` @@ -137,14 +128,14 @@ then the temporary runtime was removed before release. - Added call ambiguity validation in the compiled scene view: a call can select by process when unique, and must use `application=:name` when several model applications with the same process match the same object. -- Added a scene binding cache with `refresh_bindings!`, `bindings_dirty`, - `compiled_bindings`, and `scene_revision`. Object creation, removal, +- Added a scene binding cache with `Advanced.refresh_bindings!`, `Advanced.bindings_dirty`, + `Advanced.compiled_bindings`, and `Advanced.scene_revision`. Object creation, removal, and reparenting now invalidate the compiled binding cache and bump a scene revision before the next refresh. -- Added an environment binding cache with `refresh_environment_bindings!`, - `compile_environment_bindings`, `CompiledEnvironmentBinding`, - `CompiledEnvironmentBindings`, `environment_bindings_dirty`, - `compiled_environment_bindings`, `environment_revision`, and +- Added an environment binding cache with `Advanced.refresh_environment_bindings!`, + `Advanced.compile_environment_bindings`, `Advanced.CompiledEnvironmentBinding`, + `Advanced.CompiledEnvironmentBindings`, `Advanced.environment_bindings_dirty`, + `Advanced.compiled_environment_bindings`, `Advanced.environment_revision`, and `explain_environment_bindings`. The compiler resolves each application/object environment provider, backend, required `meteo_inputs_`, produced `meteo_outputs_`, support descriptor, and backend @@ -152,7 +143,7 @@ then the temporary runtime was removed before release. - Added the minimal scene geometry contract: `geometry(object_or_status)`, `position(object_or_status)`, and `bounds(object_or_status)`. Environment binding refreshes now call `update_index!(backend, entities)` once per - distinct backend before `bind_environment`, giving spatial backends a current + distinct backend before `Advanced.bind_environment`, giving spatial backends a current scene-wide object/entity list for precomputed microclimate lookup. - Automatic spatial binding now uses the nearest ancestor geometry when a target object has no geometry of its own. Existing backends still receive an @@ -166,7 +157,7 @@ then the temporary runtime was removed before release. cached spatial bindings. If only `meteo_inputs_` or `meteo_outputs_` changes while application id, object, process, provider, backend, status, and geometry provenance remain unchanged, required/output metadata is updated while the - cached cell is reused without `update_index!` or `bind_environment`. + cached cell is reused without `update_index!` or `Advanced.bind_environment`. - `validate_meteo_inputs(scene)` and `validate_meteo_inputs(compiled_scene, meteo_or_backend)` now validate scene/object model application `meteo_inputs_` against the active @@ -231,7 +222,7 @@ then the temporary runtime was removed before release. the runtime push it to the active microclimate backend. - Added root application scheduling from `TimeStep(...)` using `Dates.Period` values and the scene environment base step. `explain_schedule` on a - `CompiledScene` now reports each application clock, phase, timestep in base + `Advanced.CompiledScene` now reports each application clock, phase, timestep in base steps, timestep duration in seconds, and whether the application is scheduled as a root application or is manual-call-only. - Scene application scheduling now also honors a model's `timespec(...)` trait @@ -240,20 +231,20 @@ then the temporary runtime was removed before release. - Scene application scheduling now validates `timestep_hint(...)` required bounds for base-step-derived clocks. Hints remain compatibility constraints; they do not override explicit `TimeStep(...)` or non-default `timespec(...)`. -- `compile_scene` now computes a stable topological application order from +- `Advanced.compile_scene` now computes a stable topological application order from resolved `Inputs(...)` producer edges and `Updates(...)` writer-order edges. Inputs produced by manual-call-only applications are redirected to the parent application that owns the `Calls(...)` call stack. `run!(scene)` uses this precompiled order instead of user declaration order, cycles fail at compile time, and `explain_schedule` reports `execution_index`. -- `CompiledScene` now pre-indexes input and call bindings by +- `Advanced.CompiledScene` now pre-indexes input and call bindings by `(application_id, object_id)`. Per-object input materialization and `call_target(s)` lookup uses these indexes instead of scanning every binding in the scene at each model call. -- `CompiledScene` now also pre-indexes applications by application id. +- `Advanced.CompiledScene` now also pre-indexes applications by application id. Hard-call target resolution and stable ordered-application materialization use this index instead of scanning or rebuilding lookup dictionaries. -- `CompiledEnvironmentBindings` now pre-indexes environment bindings by +- `Advanced.CompiledEnvironmentBindings` now pre-indexes environment bindings by `(application_id, object_id)`. Environment sampling and mutable environment output scattering use direct lookup instead of scanning all environment bindings for every model invocation. @@ -263,7 +254,7 @@ then the temporary runtime was removed before release. scene/object runtime. Manual calls execute immediately under the parent call stack; applications selected by `Calls(...)` are skipped by the root `run!(scene)` loop and only execute through `run_call!`. -- Added scene/object duplicate-writer validation. During `compile_scene`, each +- Added scene/object duplicate-writer validation. During `Advanced.compile_scene`, each `(object, output variable)` now has one canonical writer unless later writers declare `Updates(:var; after=...)`. The `after` token can match a previous application id/name or process, so scenario authors can express @@ -274,32 +265,27 @@ then the temporary runtime was removed before release. `Updates(...)` declarations used to validate ordered updates. - Extended `explain_model_specs` rows with application name, target selector, value inputs, manual calls, and environment metadata. -- Started Phase 3 by bridging simple `Inputs(...)` declarations into the - existing MTG multiscale mapping carrier when the selector is representable as - a pure scale/variable mapping, for example +- Started Phase 3 by compiling simple `Inputs(...)` declarations to typed + scale/variable carriers, for example `Inputs(:x => Many(scale=:Leaf, var=:y))`. - Added model-level `Input(...)` defaults from `dep(model)` into `ModelSpec` value inputs. Scenario-level `ModelSpec(...) |> Inputs(...)` - overrides those defaults and also replaces the corresponding internal legacy - mapping carrier for the same input. -- Extended the Phase 3 bridge for domain simulations by generating internal - `Route(...)` carriers from supported consumer-side `Inputs(...)` - declarations, for example - `Inputs(:leaf_areas => Many(kind=:plant, scale=:Leaf, process=:leaf_state, var=:leaf_area))`. -- Started Phase 4 by bridging `Calls(...)` declarations into the current - hard-domain dependency resolver when selectors can be represented by - `kind`, `domain`, `scale`, and `process`. Added `run_call!` as the unified - spelling over the current `ModelTarget` execution path. + overrides those defaults before the native binding is compiled. +- Removed the intermediate scenario bridge after the scene/object compiler + gained native `Inputs(...)` support. Manual value-transfer carriers are not + retained as user-authored API. +- Removed the intermediate dependency resolver after `Calls(...)` became + native scene/object metadata. Manual model execution now goes through + `call_target`, `call_targets`, and `run_call!`. - Added model-level `Call(...)` defaults from `dep(model)` into `ModelSpec` manual-call metadata. Scenario-level `ModelSpec(...) |> Calls(...)` overrides those defaults, and `dep(::ModelSpec)` excludes raw `Call(...)` trait entries so default calls are normalized through the same bridge as explicit calls. -- Migrated the MAESPA example's scene energy-balance hard calls from - model-level `HardDomains(...)` to scenario-level - `ModelSpec(scene_model) |> Calls(...)`. -- Migrated the MAESPA example's scene LAI leaf-area route from user-written - `Route(...)` to consumer-side `ModelSpec(LAIModel(...)) |> Inputs(...)`. +- Migrated the MAESPA example's scene energy-balance hard calls to + scenario-level `ModelSpec(scene_model) |> Calls(...)`. +- Migrated the MAESPA example's scene LAI leaf-area transfer to consumer-side + `ModelSpec(LAIModel(...)) |> Inputs(...)`. - Started Phase 5 with `ObjectTemplate` and `ObjectInstance`. A template stores reusable scene/object `ModelSpec`s plus default object labels, and an instance mounts those specs inside one named object subtree. @@ -409,8 +395,7 @@ then the temporary runtime was removed before release. - Scene `OutputRequest(...)` now accepts `application=...` to select an explicit application id/name when the same process is mounted more than once. Explicit application selection can retain and export a - `:stream_only` publisher; legacy `GraphSimulation` export rejects this - scene-specific selector instead of ignoring it. + `:stream_only` publisher. - Each scene temporal stream owns a concrete `Vector{Tuple{Float64,T}}` selected from its first published value rather than boxing all values as `Any`. Output type changes fail explicitly, and `Interpolate`/`Integrate` @@ -430,13 +415,13 @@ then the temporary runtime was removed before release. - Environment dirty tracking is now object-scoped for geometry-only changes. `move_object!`, `update_geometry!`, and `mark_environment_binding_dirty!(scene, object)` retain unaffected compiled - bindings and re-run `bind_environment` only for applications targeting the + bindings and re-run `Advanced.bind_environment` only for applications targeting the changed object. Structural changes and provider-wide invalidation still rebuild the complete environment cache. - Runtime lifecycle tests cover a model-created leaf joining a leaf application and plant-local `RefVector`, a pruned leaf leaving both before the next step, and a moved leaf switching mock microclimate cells. -- `CompiledScene` now precompiles a process-keyed model bundle for every +- `Advanced.CompiledScene` now precompiles a process-keyed model bundle for every `(application_id, object_id)` target. Generic hard-dependency kernels receive this cached bundle through their existing `models` argument without traversing `Calls(...)` or allocating temporary collections in the timestep @@ -465,8 +450,8 @@ then the temporary runtime was removed before release. The scene/object compiler is executable: selectors normalize to object addresses, resolve before runtime, and compile into reference, temporal, call, -writer, and environment carriers. The historical mapping compiler remains -only as a qualified compatibility implementation. +writer, and environment carriers. The historical mapping compiler has been +removed. ## Phase 0: Public Contract Freeze @@ -510,11 +495,11 @@ Implement: - `SceneObject` metadata with labels: `scale`, `kind`, `species`, optional `name`, parent id, child ids, and optional geometry/position handle. -- `SceneRegistry` storing objects, parent/child relations, and indexes by +- `Advanced.SceneRegistry` storing objects, parent/child relations, and indexes by label. -- adapters from the current MTG/domain state into the registry: - each selected domain root and each MTG node gets an object id; - single-status domains get one object with `scale=:Default`. +- adapters from existing MTG state into the registry: + each selected root and each MTG node gets an object id; + single-status simulations get one object with `scale=:Default`. - object lifecycle hooks for add/remove/reparent that mirror the existing MTG runtime reindexing. @@ -556,13 +541,13 @@ Selectors must normalize to `ObjectAddress` objects with enough context to be resolved relative to a consuming object. Implement `AppliesTo(...)` using the same selector system. The target object -set of a model application must never be hidden inside a domain key, mapping -key, or implicit scale table. +set of a model application must never be hidden inside a mapping key or +implicit scale table. Definitions: -- `Self()` means the current model application object or scope. It is the - current plant only when the model is applied at `scale=:Plant`. +- `Self()` means only the current model application object. +- `Subtree()` means the current object and its descendants. - `SelfPlant()` means the nearest containing plant scope. - `Ancestor(scale=:Plant)` is the generic selector form for `SelfPlant()`. - `SceneScope()` means the whole scene. @@ -570,7 +555,7 @@ Definitions: Rules: -- unqualified selectors inside a reusable plant mapping default to +- unqualified selectors inside a reusable plant application bundle default to `within=Self()`; - scene-level selectors default to `within=SceneScope()`; - `One(...)` errors unless exactly one object resolves per consumer; @@ -590,16 +575,15 @@ Acceptance tests: ## Phase 3: Unified Value Inputs -Goal: replace `MultiScaleModel(...)` and user-written `Route(...)` with -`Inputs(...)`, while using the existing `dep` trait as the model-level source -of default dependency intent. +Goal: use `Inputs(...)` as the only user-facing value-dependency declaration. +Historical `MultiScaleModel(...)` mappings are migration sources only. Target API: ```julia ModelSpec(AllocationModel()) |> AppliesTo(Many(kind=:plant, scale=:Plant)) |> - Inputs(:leaf_carbon => Many(scale=:Leaf, within=Self(), var=:leaf_carbon)) + Inputs(:leaf_carbon => Many(scale=:Leaf, within=Subtree(), var=:leaf_carbon)) ModelSpec(LAIModel(area)) |> AppliesTo(One(scale=:Scene)) |> @@ -613,8 +597,7 @@ Implement: provide default value-input bindings. - normalized input bindings from target variable to `ObjectAddress`. - compiler pass that decides carrier: - direct reference, `RefVector`, temporal stream, or route-like - materialization. + direct reference, `RefVector`, temporal stream, or materialization. - status-default insertion for materialized target variables using the consumer model's `inputs_` default. - temporal policies on value inputs: @@ -626,14 +609,15 @@ Rules: - model authors still declare `inputs_`; scenario authors decide where those inputs come from; -- `dep(model)` may provide defaults for common value-input bindings, for both - current `ModelMapping`-style composition and future scene/object composition; +- `dep(model)` may provide defaults for common value-input bindings in + scene/object composition; - scenario-level `ModelSpec(...) |> Inputs(...)` always wins over `dep(model)` defaults; - same-rate local links should keep reference semantics where possible; - cross-rate links always go through temporal state; - duplicate source candidates are errors unless the selector disambiguates; -- route structs may remain internally but should not be required in examples. +- materialization carriers, when needed, are internal compiler details + and are not user-authored structs; - same-rate scalar and many-object links should avoid copies when they can use aliases, shared refs, `RefVector`, or an equivalent typed carrier; - PlantSimEngine must preserve arbitrary value types, including units, @@ -648,18 +632,18 @@ Carrier expectations: | same-rate many-object | `RefVector` or equivalent typed reference collection | | cross-rate | temporal stream sample | | integrate/aggregate | temporal window reduction | -| materialized route | generated pre-run status assignment | +| materialized cross-object input | generated pre-run status assignment | | environment | cached environment binding sample | Acceptance tests: -- the MAESPA scene LAI route becomes an `Inputs(...)` declaration and produces - the same `lai` and `leaf_area`; -- current plant allocation `MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]])` +- the MAESPA scene LAI cross-object input is declared with `Inputs(...)` and + produces the same `lai` and `leaf_area`; +- historical plant allocation `MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]])` becomes `Inputs(...)` and remains plant-local; - a same-scale rename currently expressed with `SameScale()` works through `Inputs(...)`; -- multi-rate value inputs integrate graph-domain node streams by object id. +- multi-rate value inputs integrate object streams by object id. - same-rate many-object bindings do not allocate per timestep in a benchmarked hot loop beyond unavoidable model work. - unitful or dual-number status values survive `Inputs(...)` without forced @@ -667,11 +651,9 @@ Acceptance tests: ## Phase 4: Unified Model Calls -Goal: replace `HardDomains(...)` with `Calls(...)`. `Calls(...)` is the -required public API for manually controlled model execution and must be -implemented in this phase, not deferred to a future rename. The same mechanism -must also be usable from `dep(model)` so current hard-dependency traits become -default call declarations. +Goal: use `Calls(...)` as the only user-facing manual model-call declaration. +The same mechanism must also be usable from `dep(model)` so hard-dependency +traits become default call declarations. Target API: @@ -679,7 +661,7 @@ Target API: ModelSpec(SceneEB()) |> AppliesTo(One(scale=:Scene)) |> Calls(:leaf_energy => Many(kind=:plant, scale=:Leaf, process=:energy_balance)) |> - Calls(:soil => One(kind=:soil, process=:soil_water)) + Calls(:soil => One(kind=:soil, application=:soil_water)) ``` Implement: @@ -786,8 +768,7 @@ Acceptance tests: MAESPA status: - the unified scene/object MAESPA path uses `ObjectTemplate` and - `ObjectInstance` for species A and B. The temporary domain path has been - removed. + `ObjectInstance` for species A and B. ## Phase 5B: Object Lifecycle And Cache Invalidation @@ -801,7 +782,7 @@ register_object!(scene, object; parent) remove_object!(scene, object) reparent_object!(scene, object, new_parent) move_object!(scene, object, geometry_or_position) -refresh_bindings!(scene) +Advanced.refresh_bindings!(scene) ``` Implement invalidation for: @@ -853,7 +834,7 @@ Implement: - minimal geometry accessors or traits: `position`, `geometry`, and `bounds`. - backend protocol: - `bind_environment`, `sample_environment`, `scatter_environment!`, and + `Advanced.bind_environment`, `sample_environment`, `scatter_environment!`, and `refresh_environment!`. - `Environment(...)` overrides for scenario-specific resolver/backend choices. - `Environment(; sources=(CO2=:Ca,))` for scenario-specific environment source @@ -897,8 +878,8 @@ Goal: make the unified graph the source of truth. Implement: - one compiler that builds a global dependency graph over object addresses; -- route/materialization and multiscale reference wiring as internal carriers; -- domain DAG scheduling replaced by object/scope dependency scheduling; +- materialization and multiscale reference wiring as internal carriers; +- object/scope dependency scheduling; - writer validation through the same graph, including `Updates(...)`; - model application scheduling from `AppliesTo(...)` target sets; - multirate scheduling based on `Dates.Period` values in `TimeStep(...)` and @@ -917,8 +898,8 @@ Acceptance tests: - old `MultiScaleModel` examples rewritten with `Inputs(...)` produce matching outputs; -- old `Route(...)` examples rewritten with `Inputs(...)` produce matching - outputs; +- historical cross-object examples rewritten with `Inputs(...)` produce + matching outputs; - MAESPA hard-call example rewritten with `Calls(...)` produces matching outputs; - explanation helpers include enough concrete object ids, scales, processes, @@ -936,21 +917,19 @@ Acceptance tests: Goal: remove the old configuration surface once parity is proven. -Remove or demote: +Removed: - `MultiScaleModel(...)` as public scenario configuration; -- public `Route(...)` authoring for normal value inputs; -- `AllDomains(...)` and `HardDomains(...)` as primary user API; -- `Domain(...)` as a user-facing container when object templates/instances are - available. +- superseded scenario containers and value-transfer authoring; +- superseded manual-dependency selectors. Write migration notes: - `MultiScaleModel([:x => [:Leaf => :y]])` -> `Inputs(:x => Many(scale=:Leaf, var=:y))`; -- `Route(from=AllDomains(...), to=...)` -> consumer `Inputs(...)`; -- `HardDomains(...)` -> `Calls(...)`; -- repeated species domains -> `ObjectTemplate` plus `ObjectInstance`; -- explicit meteo routes -> environment resolver/binding backend. +- cross-object value declarations -> consumer `Inputs(...)`; +- manual dependency declarations -> `Calls(...)`; +- repeated species assemblies -> `ObjectTemplate` plus `ObjectInstance`; +- explicit meteo wiring -> environment resolver/binding backend. - `InputBindings(...)` -> source and temporal policy information inside `Inputs(...)`; - `MeteoBindings(...)` and `MeteoWindow(...)` -> `Environment(...)` and @@ -965,59 +944,48 @@ Regression tests must cover all migrated examples before removal. Migration documentation progress: - Added `docs/src/migration_scene_object.md` with direct translations for - `MultiScaleModel`, `Route`, `AllDomains`, `HardDomains`, repeated `Domain` - instances, `TimeStepModel`, `InputBindings`, `MeteoBindings`, - `ScopeModel`, and `SameScale`. + `MultiScaleModel`, repeated object assemblies, + `TimeStepModel`, `InputBindings`, `MeteoBindings`, `ScopeModel`, and + `SameScale`. - Documentation navigation and the home page now identify the scene/object API - as the target for new multiscale and multi-plant work. Legacy domain and - multiscale pages are explicitly labeled. + as the target for new multiscale and multi-plant work. - The documentation home page now uses executable scene/object examples as the primary quickstart. It shows `Scene`, `Object`, `ModelSpec`, `AppliesTo`, `Inputs`, `TimeStep`, automatic same-object binding inference, multi-object - `Many(...)` inputs, and manual `Calls(...)` syntax before linking to legacy - mapping tutorials. + `Many(...)` inputs, and manual `Calls(...)` syntax. - The repository README now mirrors the scene/object entry point instead of teaching `ModelMapping` first. It includes smoke-tested `Scene`/`Object` quickstart code, `Inputs(...)` multi-object coupling, conceptual - `Calls(...)` syntax, and links to the migration guide for compatibility - APIs. + `Calls(...)` syntax, and links to the migration guide. - Added `docs/src/scene_object/quickstart.md` as the first native scene/object tutorial page and promoted it in the documentation navigation. The page contains docs-tested examples for one-object model chaining, inferred same-object bindings, `OutputRequest` retention, multi-object `Inputs(...)`, `RefVector` carrier explanations, and manual `Calls(...)` syntax. -- The repository agent skill teaches the unified public vocabulary first and - treats the historical mapping stack as legacy-maintenance knowledge. +- The repository agent skill teaches the unified public vocabulary. - The public API page now starts with curated scene/object groups for scenario construction, selectors, coupling, lifecycle, environment, runtime, and - structured explanations. Mapping-level multirate examples are labeled as - legacy. + structured explanations. Current removal audit: -- The unreleased domain scenario and runtime subsystem has been deleted, - including its route carriers, dependency selectors, target helpers, tests, +- The unreleased intermediate scenario and runtime subsystem has been deleted, + including its carriers, dependency selectors, target helpers, tests, examples, and documentation. - Public manual-call control now uses `call_target`, `call_targets`, and `run_call!`. - `SceneRunContext` defines `call_target(s)` directly, including string-name support. -- The legacy mapping transforms are no longer exported: +- The legacy mapping transforms are removed: `MultiScaleModel`, `SameScale`, `TimeStepModel`, `InputBindings`, - `MeteoBindings`, `MeteoWindow`, and `ScopeModel` require qualified - `PlantSimEngine.*` access in compatibility tests and historical examples. -- `ModelMapping` is no longer exported. Retained mapping tests, examples, - benchmarks, doctests, and historical tutorials explicitly use - `PlantSimEngine.ModelMapping(...)`, making the compatibility boundary - visible without deleting regression coverage. -- Historical MTG mapping and mapping-level multirate pages are grouped under - explicitly named legacy sections in the documentation. Executable examples - use qualified compatibility constructors so they cannot be mistaken for the - exported public API. -- The home page, execution page, MTG mapping tutorials, and mapping-level - multirate tutorials now identify their compatibility status and direct new - work to the scene/object API. A future documentation cleanup can replace + `MeteoBindings`, `MeteoWindow`, and `ScopeModel` are not retained as + compatibility constructors. +- `ModelMapping` is removed. Retained documentation mentions it only as + historical migration context. +- Historical MTG mapping and mapping-level multirate pages were removed from + the active documentation navigation. A future documentation cleanup can + replace these historical pages with equivalent scene/object tutorials rather than retaining them as migration reference. - The model execution page has been rewritten as a scene/object-first guide. @@ -1042,26 +1010,19 @@ Current removal audit: repository README has also been replaced by native scene/object examples, and a dedicated scene/object quickstart is now available in the main documentation navigation. -- Legacy multirate and mapping tests remain intentionally as qualified - compatibility regression coverage. Equivalent scene/object tests now cover - scheduling, temporal policies, binding inference and overrides, meteo - contracts and aggregation, output routing and application-qualified export, - and structured explanations. The legacy constructors are already absent - from exports. +- Scene/object tests cover scheduling, temporal policies, binding inference + and overrides, meteo contracts and aggregation, output routing and + application-qualified export, and structured explanations. Legacy mapping + regression tests were removed with the old runtime. - Scene/object tests now include public meteo-contract validation parity for missing environment variables, explicit `Environment(; sources=...)` remapping, model-author `meteo_hint` source defaults, and validation against an explicit replacement meteo object/backend. -- Test code has been migrated from `TimeStepModel(...)` to the canonical - `TimeStep(...)` spelling. Remaining legacy transform tests now qualify - legacy-only helpers such as `PlantSimEngine.InputBindings`, - `PlantSimEngine.ScopeModel`, and `PlantSimEngine.MultiScaleModel` in the - focused scaffolding test, reducing reliance on those names as exported - public API. -- The unified MAESPA path is implemented and tested, so the legacy MAESPA - domain path is regression-only evidence. Its legacy constructors are - qualified explicitly, while the migrated kernel uses `AppliesTo`, `Inputs`, - `Calls`, `call_target(s)`, and `run_call!`. +- Test code uses the canonical `TimeStep(...)` spelling and scene/object + modifiers. Legacy transform tests were removed with the old compatibility + constructors. +- The unified MAESPA path is implemented and tested through `AppliesTo`, + `Inputs`, `Calls`, `call_target(s)`, and `run_call!`. ## Resolved API Decisions @@ -1069,17 +1030,13 @@ Current removal audit: public selector names. - `Inputs(...)` is the preferred pipeable modifier. `ModelSpec(...; inputs=...)` is also accepted for programmatic construction. -- `TimeStep(...)` is the canonical exported name. `TimeStepModel(...)` is an - internal compatibility transform for qualified historical code. +- `TimeStep(...)` is the canonical timestep configuration. - `Environment(...)` owns provider/resolver/source configuration. Temporal value windows belong to the consuming `Inputs(...)` selector. - Object templates own reusable model applications and parameters, not plant topology construction. They consume explicit object trees or MTGs adapted through `objects_from_mtg`. -- The old domain, route, multiscale, and mapping-transform implementations are - retained as an explicitly qualified compatibility and regression layer. - Their scenario constructors are not exported and are not part of the primary - API. +- The old multiscale and mapping-transform implementations have been removed. ## Completion Evidence diff --git a/docs/src/developers.md b/docs/src/developers.md index d5523aedc..947e8d08f 100644 --- a/docs/src/developers.md +++ b/docs/src/developers.md @@ -36,8 +36,9 @@ Run the standard test suite from the repository root: julia --project=test test/runtests.jl ``` -Some tests exercise threaded execution, so it is worth running them with more -than one Julia thread when validating parallel behavior. +The current public runtime is sequential. Running with multiple Julia threads +does not enable a parallel Scene executor; parallel execution remains roadmap +work and requires dedicated correctness tests before it becomes public. ### Documentation diff --git a/docs/src/guides/coupling.md b/docs/src/guides/coupling.md new file mode 100644 index 000000000..68da4bb02 --- /dev/null +++ b/docs/src/guides/coupling.md @@ -0,0 +1,64 @@ +# Coupling Models + +Use `Inputs` when a model reads a value produced by another application. A +unique same-object producer is inferred; cross-object sources should use an +explicit `One`, `OptionalOne`, or `Many` selector. Inspect the resolved +references with `explain_bindings`. + +Use `Calls` only when a parent algorithm owns child execution or iteration. +Resolve targets through `call_target`/`call_targets` and execute them with +`run_call!`. Trial calls use `publish=false`; accepted state is published once. +Nested calls inherit publication suppression, so a descendant cannot publish +inside an unpublished ancestor trial. `explain_calls` and `explain_schedule` +show call-only targets and ordering. + +`explain_initialization(scene)` classifies values as supplied, generated, +producer-bound, environment-bound, or unresolved before execution. + +## Value coupling + +A consumer on the same object needs no scenario syntax when exactly one +canonical producer exists. Make cross-object intent explicit: + +```julia +ModelSpec(PlantBalance(); name=:balance) |> + AppliesTo(Many(scale=:Plant)) |> + Inputs( + :assimilation => Many( + scale=:Leaf, + within=Subtree(), + application=:photosynthesis, + var=:carbon, + ), + :soil_water => One( + scale=:Soil, + within=SceneScope(), + application=:soil, + var=:water, + ), + ) +``` + +`One` is a contract: zero or multiple matches are errors. Use `OptionalOne` +only when absence has a scientific meaning, and `Many` when aggregation is +part of the consumer model. `within=Subtree()` searches descendants of the +current target; `within=SelfPlant()` anchors repeated plant instances; and +`SceneScope()` is deliberately global. + +## Manual calls + +```julia +ModelSpec(Optimizer(); name=:optimizer) |> + AppliesTo(Many(scale=:Plant)) |> + Calls(:leaf_energy => Many(scale=:Leaf, within=Subtree())) +``` + +Inside `Optimizer`, iterate over `call_targets(extra, :leaf_energy)`. Run +candidate states with `run_call!(target; publish=false)` and the accepted state +with `publish=true`. A call-only target is excluded from root scheduling, and +an unpublished outer call suppresses publication by every nested descendant. + +After compilation, inspect `explain_bindings(compiled)` for source identity +and carrier type, `explain_calls(compiled)` for call-only targets, and +`explain_schedule(compiled)` for root execution order. These rows are the +supported diagnostic surface; compiled fields are internal. diff --git a/docs/src/guides/data/environment_inputs.md b/docs/src/guides/data/environment_inputs.md new file mode 100644 index 000000000..5622cf55c --- /dev/null +++ b/docs/src/guides/data/environment_inputs.md @@ -0,0 +1,11 @@ +# Weather And Environment Inputs + +An environment may be a constant named tuple, one tabular row, or regular +multi-row weather. Every row in a timed sequence needs a positive fixed +`duration`; inconsistent base durations and application substeps are rejected. + +Use `Environment(sources=...)` to map model-facing meteorological names to +provider columns. Values retain compatible user numeric types. Spatial +providers implement the same model-facing contract and refresh bindings after +object movement or geometry changes. + diff --git a/docs/src/guides/data/forcing_observations.md b/docs/src/guides/data/forcing_observations.md new file mode 100644 index 000000000..0b1448d5b --- /dev/null +++ b/docs/src/guides/data/forcing_observations.md @@ -0,0 +1,10 @@ +# Forcing Observed Variables + +Supply a constant observed value in object status when it does not vary. For a +time-varying observation, use a small environment-driven source model that +publishes the canonical variable; downstream applications remain unchanged. + +Replace a process for an entire template instance through instance overrides, +or use `Override` for one exceptional object. The replacement must implement +the same process and input/output contract. + diff --git a/docs/src/guides/data/numerical_reliability.md b/docs/src/guides/data/numerical_reliability.md new file mode 100644 index 000000000..6d5a6aec6 --- /dev/null +++ b/docs/src/guides/data/numerical_reliability.md @@ -0,0 +1,11 @@ +# Numerical Reliability + +Use exact assertions for deliberately exact integer/rational scenarios and +`isapprox` for floating-point scientific results. Splitting a computation +across objects may change reduction order without changing the model. + +PlantSimEngine preserves compatible numeric types through parameters, status, +carriers, meteorology, and streams. Avoid forced `Float64` conversion. For long +or ill-conditioned sums, use pairwise or compensated accumulation inside the +scientific model and test its error tolerance explicitly. + diff --git a/docs/src/guides/data/outputs_plotting.md b/docs/src/guides/data/outputs_plotting.md new file mode 100644 index 000000000..e80048f98 --- /dev/null +++ b/docs/src/guides/data/outputs_plotting.md @@ -0,0 +1,56 @@ +# Collecting And Plotting Outputs + +Run a scene to obtain `SceneSimulation`, then call `collect_outputs(sim)` for +ordinary analysis. Rows identify application, object, variable, timestep/time, +and value, so repeated processes cannot overwrite one another. Convert the rows +to a `DataFrame`, filter by application/object/variable, group, and plot. + +Runs default to `outputs=:none`. Use `outputs=:all` only when complete stream +history is intentional; selected requests are the memory-safe choice for large +scenes. Raw rows have the stable columns `timestep`, `time`, `application_id`, +`object_id`, `variable`, and `value`. Requested/resampled rows additionally +identify `scale` and `process`. A temporal request emits `missing` when its +policy cannot produce a value for a scheduled output time. +`time` is expressed in scene base-step coordinates; application clock metadata +is reported by `explain_schedule(simulation)`. Values retain their concrete +types, so unit-bearing model outputs remain unit-bearing in collected rows. + +`OutputRequest` controls requested retention or resampling. Dependency streams +may also be retained for runtime correctness. Use +`explain_output_retention(sim)` to see why each stream exists. Removed objects +retain accepted historical rows. + +```@example collect-output +using Dates +using DataFrames +using PlantSimEngine + +PlantSimEngine.@process "docs_output_counter" verbose = false +struct DocsOutputCounter <: AbstractDocs_Output_CounterModel end +PlantSimEngine.inputs_(::DocsOutputCounter) = NamedTuple() +PlantSimEngine.outputs_(::DocsOutputCounter) = (value=0,) +PlantSimEngine.run!(::DocsOutputCounter, models, status, meteo, constants, extra) = + (status.value += 1) + +scene = Scene(DocsOutputCounter(); environment=(duration=Hour(1),)) +simulation = run!( + scene; + steps=3, + outputs=OutputRequest( + One(scale=:Scene), + :value; + name=:counter, + application=:docs_output_counter, + ), +) +rows = collect_outputs(simulation, :counter; sink=nothing) +table = DataFrame(rows) +@assert table.value == [1, 2, 3] +table +``` + +For plotting, filter the table first and map `time` to the horizontal axis and +`value` to the vertical axis. Group by `application_id` and `object_id` before +drawing lines; grouping by variable alone can accidentally connect different +objects. CairoMakie and other plotting packages consume the resulting columns +without any PlantSimEngine-specific adapter. diff --git a/docs/src/guides/modelers/port_existing_model.md b/docs/src/guides/modelers/port_existing_model.md new file mode 100644 index 000000000..93eb8ae48 --- /dev/null +++ b/docs/src/guides/modelers/port_existing_model.md @@ -0,0 +1,40 @@ +# Port An Existing Model + +Start with a one-step scientific function and separate four concerns: immutable +parameters, object state, environment forcing, and produced values. Keep the +arithmetic generic; a model should not convert compatible values to `Float64`. + +```@example port-existing-model +using Dates +using PlantSimEngine + +PlantSimEngine.@process "docs_lai_growth" verbose = false + +struct DocsLAIGrowth{T} <: AbstractDocs_Lai_GrowthModel + rate::T +end +PlantSimEngine.inputs_(::DocsLAIGrowth) = (lai=0.0,) +PlantSimEngine.outputs_(::DocsLAIGrowth) = (lai_next=0.0,) +PlantSimEngine.meteo_inputs_(::DocsLAIGrowth) = (T=0.0,) +function PlantSimEngine.run!(m::DocsLAIGrowth, models, status, meteo, constants, extra) + status.lai_next = status.lai + m.rate * meteo.T +end + +scene = Scene( + DocsLAIGrowth(0.02); + status=(lai=1.0,), + environment=(T=10.0, duration=Day(1)), +) +simulation = run!(scene) +@assert only(scene_objects(scene)).status.lai_next == 1.2 +``` + +Test the scientific function first, then the kernel directly with a `Status`, +and finally the same model through a scene. These three levels separate a +scientific error from a model-contract error and a scenario-binding error. +`explain_initialization(scene)` should show `lai` as supplied, `T` as +environment-bound, and `lai_next` as generated. + +The concise constructor lowers to the ordinary Scene compiler; it is not a +separate runtime. Move to explicit `Object` and `ModelSpec` construction only +when the scenario needs multiple objects, selectors, or named applications. diff --git a/docs/src/guides/modelers/stateful_models.md b/docs/src/guides/modelers/stateful_models.md new file mode 100644 index 000000000..8ee821b06 --- /dev/null +++ b/docs/src/guides/modelers/stateful_models.md @@ -0,0 +1,11 @@ +# State, History, And Repeated Updates + +Object `Status` is current state, not timestep storage. Use +`PreviousTimeStep(:x)` for a one-step lag, or keep a model-owned ring buffer +when the algorithm requires deeper history. Accepted output streams provide +simulation history. + +Several writers to one canonical variable are rejected unless the application +declares `Updates`. Iterative trial execution belongs to `Calls`; use +`publish=false` until a state is accepted. + diff --git a/docs/src/guides/multiscale/concepts.md b/docs/src/guides/multiscale/concepts.md new file mode 100644 index 000000000..eed732369 --- /dev/null +++ b/docs/src/guides/multiscale/concepts.md @@ -0,0 +1,31 @@ +# How Multiscale Scenes Execute + +One application executes once for every object selected by `AppliesTo`. State +belongs to the object, while topology and labels belong to the scenario. +`Self()` is the current object, `SelfPlant()` is its plant-instance root, and +`SceneScope()` is scene-wide. Cardinality wrappers decide whether zero, one, +or many matches are valid. + +More objects mean more qualified streams. Removing an object stops future +execution but preserves its accepted historical samples. + +Canonical selector patterns are: + +| Relationship | Pattern | +| --- | --- | +| application targets every leaf | `AppliesTo(Many(scale=:Leaf))` | +| input from this same object | omit `Inputs` when the producer is unique | +| input from one ancestor | `One(Ancestor(scale=:Plant))` | +| input from this plant's leaves | `Many(scale=:Leaf, within=SelfPlant())` | +| input from shared soil | `One(scale=:Soil, within=SceneScope())` | +| optional named organ | `OptionalOne(name=:fruit, within=SelfPlant())` | + +`Self()` always means the current target object. It never implicitly means the +model, process, species, or plant. Prefer object IDs and labels for identity, +and use `Scope(name)` only when the scene explicitly defines that scope. + +One application produces a separate stream for every selected object and +output variable. Stream keys also include application identity, so repeated +applications of the same process cannot overwrite each other. Use +`explain_scene_applications`, `explain_objects`, and `explain_bindings` to +verify target and source multiplicities before a long run. diff --git a/docs/src/guides/multiscale/from_one_object.md b/docs/src/guides/multiscale/from_one_object.md new file mode 100644 index 000000000..d6857f58b --- /dev/null +++ b/docs/src/guides/multiscale/from_one_object.md @@ -0,0 +1,10 @@ +# From One Object To A Multiscale Scene + +First run all models on one object with the concise `Scene(models...; +status=...)` constructor. Then move organ-specific applications to leaf +objects and aggregate their outputs on a plant with `Many(..., +within=SelfPlant())` or an instance-local selector. + +Compare collected, application-qualified outputs with `isapprox`. Exact bitwise +identity is not generally a valid requirement after changing reduction order. + diff --git a/docs/src/guides/multiscale/import_mtg.md b/docs/src/guides/multiscale/import_mtg.md new file mode 100644 index 000000000..7db251d2a --- /dev/null +++ b/docs/src/guides/multiscale/import_mtg.md @@ -0,0 +1,10 @@ +# Importing An MTG + +`objects_from_mtg(root)` converts MTG topology and labels into ordinary scene +objects. `Scene(root; applications=...)` performs the same adaptation and then +uses the normal Scene compiler. The MTG is an input representation, not a +second runtime. + +For growth, prefer `add_organ!`: it creates the MTG node, applies the scene's +status policy, attaches status, and registers the corresponding object. + diff --git a/docs/src/guides/multiscale/manual_calls.md b/docs/src/guides/multiscale/manual_calls.md new file mode 100644 index 000000000..1e543ec47 --- /dev/null +++ b/docs/src/guides/multiscale/manual_calls.md @@ -0,0 +1,12 @@ +# Manual Calls Across Objects + +Declare parent-owned execution with `Calls(:name => One(...))` or +`Calls(:name => Many(...))`. In the kernel, obtain the resolved target from the +`SceneRunContext` and invoke `run_call!`. A target used only by calls is absent +from root scheduling. + +Explicit target cadence must match the caller. A target without an explicit +cadence inherits the caller's invocation timing. Structural mutations are +recompiled between timesteps, so new or removed objects affect calls on the +next timestep, never recursively inside the mutation-producing kernel call. + diff --git a/docs/src/guides/multiscale/value_coupling.md b/docs/src/guides/multiscale/value_coupling.md new file mode 100644 index 000000000..ac3bb0333 --- /dev/null +++ b/docs/src/guides/multiscale/value_coupling.md @@ -0,0 +1,10 @@ +# Coupling Values Across Objects + +- `One(...)` requires exactly one source. +- `OptionalOne(...)` accepts zero or one. +- `Many(...)` supplies a stable object-ID ordered carrier. + +Use `var=` to rename a source and `application=` to distinguish repeated +processes. Homogeneous many-source values use a `RefVector`; heterogeneous +values use an object-aware reference carrier. Inspect both through +`input_carrier`, `input_value`, and `explain_bindings`, not internal fields. diff --git a/docs/src/guides/multiscale/visualizing_structure.md b/docs/src/guides/multiscale/visualizing_structure.md new file mode 100644 index 000000000..1be756575 --- /dev/null +++ b/docs/src/guides/multiscale/visualizing_structure.md @@ -0,0 +1,6 @@ +# Visualizing Scene Structure + +Use `explain_objects`, `explain_scopes`, and `explain_instances` to +obtain stable rows for plotting. Draw nodes by object ID and edges from parent +to child; color by scale, species, or instance. Keep simulation visualization +outside model kernels so model code remains independent of topology packages. diff --git a/docs/src/guides/time/advanced_time_environment.md b/docs/src/guides/time/advanced_time_environment.md new file mode 100644 index 000000000..4d2da7449 --- /dev/null +++ b/docs/src/guides/time/advanced_time_environment.md @@ -0,0 +1,12 @@ +# Advanced Time And Environment Configuration + +Disambiguate a producer with an explicit selector containing `application`, +`var`, and `within`. Put temporal `policy` and `window` on that input. Configure +environment source renaming and reducers with `Environment`; scenario values +override model-level `meteo_hint` entries. + +Use `explain_bindings`, `explain_environment_bindings`, and +`explain_schedule` to inspect the final source, reducer, window, cadence, and +clock origin. All periods that require seconds must be fixed `Dates` periods; +`Month(1)` is intentionally rejected. + diff --git a/docs/src/guides/time/hourly_daily_weekly.md b/docs/src/guides/time/hourly_daily_weekly.md new file mode 100644 index 000000000..f57201b4f --- /dev/null +++ b/docs/src/guides/time/hourly_daily_weekly.md @@ -0,0 +1,58 @@ +# Hourly, Daily, And Weekly Models + +Use one hourly leaf application, a daily plant application with +`Many(scale=:Leaf, within=Subtree(), policy=Integrate(), window=Day(1))`, and a +weekly application consuming the daily stream. Each application remains a +normal `ModelSpec`; only its `TimeStep` and input policy differ. + +Runtime dependency streams are retained because consumers need them. Output +resampling is independent: create named `OutputRequest`s for hourly, daily, +and weekly analysis, then compare `explain_schedule` sample counts with rows +from `collect_outputs`. + +The following reduced example checks the important physical contract: two +leaf rates are integrated independently for 24 hourly samples and then summed +on their plant. + +```@example hourly-daily +using Dates +using PlantSimEngine + +PlantSimEngine.@process "docs_hourly_flux" verbose = false +PlantSimEngine.@process "docs_daily_total" verbose = false +struct DocsHourlyFlux <: AbstractDocs_Hourly_FluxModel end +struct DocsDailyTotal <: AbstractDocs_Daily_TotalModel end +PlantSimEngine.inputs_(::DocsHourlyFlux) = (rate=0.0,) +PlantSimEngine.outputs_(::DocsHourlyFlux) = (flux=0.0,) +PlantSimEngine.run!(::DocsHourlyFlux, models, status, meteo, constants, extra) = + (status.flux = status.rate) +PlantSimEngine.inputs_(::DocsDailyTotal) = (fluxes=[0.0],) +PlantSimEngine.outputs_(::DocsDailyTotal) = (total=0.0,) +PlantSimEngine.run!(::DocsDailyTotal, models, status, meteo, constants, extra) = + (status.total = sum(status.fluxes)) + +scene = Scene( + Object(:plant; scale=:Plant), + Object(:leaf_1; scale=:Leaf, parent=:plant, status=Status(rate=1.0)), + Object(:leaf_2; scale=:Leaf, parent=:plant, status=Status(rate=2.0)); + applications=( + ModelSpec(DocsHourlyFlux(); name=:hourly) |> + AppliesTo(Many(scale=:Leaf)) |> TimeStep(Hour(1)), + ModelSpec(DocsDailyTotal(); name=:daily) |> + AppliesTo(One(scale=:Plant)) |> + Inputs(:fluxes => Many( + scale=:Leaf, within=Subtree(), application=:hourly, var=:flux, + policy=Integrate(), window=Day(1), + )) |> TimeStep(Day(1)), + ), + environment=[(duration=Hour(1),) for _ in 1:25], +) +simulation = run!(scene; steps=25) +@assert only(object.status.total for object in scene_objects(scene) + if object.id == ObjectId(:plant)) == 72.0 +``` + +A weekly consumer uses the same pattern with `TimeStep(Week(1))` and a +seven-day window over the daily application. Keep `Integrate` for rates; +choose `Aggregate(reducer)` for states or observations whose physical meaning +is a mean, minimum, maximum, or custom statistic. diff --git a/docs/src/guides/time/multirate_concepts.md b/docs/src/guides/time/multirate_concepts.md new file mode 100644 index 000000000..d95a93b11 --- /dev/null +++ b/docs/src/guides/time/multirate_concepts.md @@ -0,0 +1,13 @@ +# Understanding Model Cadence + +`TimeStep(Dates.Period)` sets application cadence. Without it, an application +uses `timespec(model)` when non-default and otherwise the environment base +step. `timestep_hint` validates a scientifically acceptable range; it does not +silently change cadence. + +Temporal input policies have different physical meanings: `HoldLast` samples +the latest state, `Aggregate` reduces observations, and `Integrate` multiplies +rates by sample durations. `PreviousTimeStep` deliberately breaks a same-step +cycle. Windows are rolling fixed-duration windows. Civil-day or +previous-complete-calendar-period alignment is not a supported public feature. + diff --git a/docs/src/index.md b/docs/src/index.md index 90d4cce04..0455c0674 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -44,6 +44,24 @@ TimeStep Environment ``` +### Models And Applications + +A model is a reusable implementation of a process. A model application is one +configured use of that model in a scene: it gives the use a name, selects its +target objects, and configures its inputs, calls, timestep, and environment. + +| Concept | Meaning | +|:--|:--| +| Process | The biological or physical operation, such as light interception | +| Model | An implementation of that process, such as `Beer` | +| Application | One configured use of a model in a `Scene` | +| Target | An object on which that application executes | + +One application can target many objects. The same model can also be used in +several named applications with different parameters, selectors, or cadence. +During compilation, PlantSimEngine resolves each application into its concrete +`(application, object)` executions. + This means the same model can be reused on one object, many leaves, several plant species, a shared soil object, or a scene-scale energy-balance solver without changing the model implementation. @@ -124,8 +142,8 @@ scene = Scene( environment=meteo_day, ) -sim = run!(scene; steps=30, constants=Constants()) -out = DataFrame(collect_outputs(sim; sink=nothing)) +sim = run!(scene; steps=30, constants=Constants(), outputs=:all) +out = collect_outputs(sim; sink=DataFrame) first(out, 6) ``` @@ -135,7 +153,7 @@ bindings from each model's declared inputs and outputs: ```@example readme select( - DataFrame(explain_bindings(refresh_bindings!(scene))), + DataFrame(explain_bindings(scene)), :application_id, :input, :source_application_ids, @@ -149,16 +167,16 @@ The outputs can be plotted like any other tabular result: ```@example readme using CairoMakie -lai = out[out.variable .== :LAI, :] -appfd = out[out.variable .== :aPPFD, :] - -fig = Figure(size=(800, 520)) -ax1 = Axis(fig[1, 1], ylabel="LAI") -lines!(ax1, lai.timestep, Float64.(lai.value), color=:mediumseagreen) +lai = out[out.variable .== :LAI, :value] +appfd = out[out.variable .== :aPPFD, :value] +tt_cu = out[out.variable .== :TT_cu, :value] -ax2 = Axis(fig[2, 1], xlabel="Day", ylabel="aPPFD") -lines!(ax2, appfd.timestep, Float64.(appfd.value), color=:firebrick) +fig = Figure(resolution=(800, 600)) +ax = Axis(fig[1, 1], ylabel="LAI (m² m⁻²)") +lines!(ax, tt_cu, lai, color=:mediumseagreen) +ax2 = Axis(fig[2, 1], xlabel="Cumulated growing degree days since sowing (°C)", ylabel="aPPFD (mol m⁻² d⁻¹)") +lines!(ax2, tt_cu, appfd, color=:firebrick1) fig ``` @@ -189,11 +207,11 @@ plant_scene = PlantSimEngine.Scene( run!(plant_scene) scene_status = only(scene_objects(plant_scene; scale=:Scene)).status -(total_surface=scene_status.total_surface, LAI=scene_status.LAI) +scene_status ``` The same `Many(...)` selector would be plant-local if the consumer ran on a -plant and used `within=Self()`. This is the same mechanism used for plant +plant and used `within=Subtree()`. This is the same mechanism used for plant allocation models that sum their own leaves, scene models that aggregate all plants, and microclimate solvers that select objects inside one environment cell. @@ -212,13 +230,13 @@ ModelSpec(SceneEnergyBalance(); name=:scene_energy) |> kind=:plant, scale=:Leaf, within=SceneScope(), - process=:energy_balance, + application=:energy_balance, ), :soil => One( kind=:soil, scale=:Soil, within=SceneScope(), - process=:soil_water, + application=:soil_water, ), ) |> TimeStep(Hour(1)) @@ -249,12 +267,12 @@ hundreds of microseconds, and PlantBiophysics.jl models using PlantSimEngine have been measured much faster than equivalent implementations in typical scientific scripting languages. -For performance-sensitive scenes, inspect the compiled representation: +For performance-sensitive scenes, inspect the supported structured +explanations: ```julia -compiled = refresh_bindings!(scene) -explain_bindings(compiled) -explain_schedule(compiled) +explain_bindings(scene) +explain_schedule(scene) explain_execution_plan(scene) ``` diff --git a/docs/src/introduction/why_plantsimengine.md b/docs/src/introduction/why_plantsimengine.md index ed8759658..ba99485ff 100644 --- a/docs/src/introduction/why_plantsimengine.md +++ b/docs/src/introduction/why_plantsimengine.md @@ -82,7 +82,10 @@ PlantSimEngine's approach to plant modeling represents a paradigm shift in how s - **Automatic Dependency Resolution:** The system automatically determines the relationships between different models and processes, eliminating the need for manual coupling. -- **Seamless Parallelization:** Out-of-the-box support for parallel and distributed computation allows researchers to focus on the science rather than implementation details. +- **Concrete Batched Execution:** The scene compiler groups compatible object + targets into concrete execution batches. A public parallel or distributed + executor is not currently provided; parallel execution remains planned work + that requires explicit correctness and independence guarantees. - **Flexible Model Integration:** The ability to easily combine models from different sources and at different scales facilitates more comprehensive and realistic simulations. diff --git a/docs/src/migration_scene_object.md b/docs/src/migration_scene_object.md index 48a21891c..2c2573db0 100644 --- a/docs/src/migration_scene_object.md +++ b/docs/src/migration_scene_object.md @@ -1,5 +1,43 @@ # Migrating To The Scene/Object API +## Refining early Scene/Object code + +The stabilized public surface makes several early Scene/Object behaviors +explicit: + +| Early spelling or behavior | Stabilized API | +| --- | --- | +| `Self()` searched self and descendants | `Self()` selects only the current object; use `Subtree()` for self plus descendants | +| omitted `tracked_outputs` retained everything | use explicit `outputs=:all`; the safe default is `outputs=:none` | +| `tracked_outputs=requests` | `outputs=requests` | +| `OutputRequest(:Leaf, :x; process=:p)` | `OutputRequest(Many(scale=:Leaf), :x; application=:app)` | +| repeated unnamed applications gained numbered IDs | name every repeated application with `ModelSpec(...; name=...)` | +| calling `run!(scene)` again implicitly looked like continuation | use `continue!(simulation)` or `step!(simulation)` | +| compiler/cache types imported from the default namespace | qualify them through `PlantSimEngine.Advanced` | + +`tracked_outputs` remains a targeted deprecation bridge. It maps `nothing` to +`outputs=:all`, an empty request vector to `outputs=:none`, and requests to the +equivalent `outputs` value. `process=` in `OutputRequest` is likewise a bridge +to application-qualified requests. Singular scenario-level `Inputs` and +`Calls` also deprecate process-only filters; model-authored `Input`/`Call` +defaults may still discover a process because they cannot know scenario +application names. `Many(process=...)` remains an explicit discovery query for +collecting several applications, such as the mounted applications from several +instances. New singular scenario references should use `application=`. The +deprecated bridges are scheduled for removal in PlantSimEngine 0.15. + +Calling `run!(scene; ...)` always creates a fresh result timeline starting at +step one, even when object status has already been mutated by an earlier run. +Continue the same timeline, environment position, temporal histories, and +multirate phase with: + +```julia +simulation = run!(scene; steps=24, outputs=requests) +continue!(simulation; steps=24) +step!(simulation) +@assert current_step(simulation) == 49 +``` + The scene/object API replaces the historical multiscale mapping system with one object-address graph. @@ -104,16 +142,16 @@ ModelSpec(AllocationModel(); name=:allocation) |> Inputs( :leaf_carbon => Many( scale=:Leaf, - within=Self(), + within=Subtree(), var=:leaf_carbon, ), ) ``` -`Self()` is relative to the object where the consumer runs. A plant-scale -allocation model therefore reads only leaves inside that plant. Use +`Self()` selects only the object where the consumer runs. A plant-scale +allocation model uses `Subtree()` to read leaves below that plant. Use `SceneScope()` for scene-wide aggregation and `SelfPlant()` to select the -nearest containing plant from an organ. +nearest containing plant and its subtree from an organ. Same-object renaming uses the same syntax: @@ -142,7 +180,7 @@ ModelSpec(SceneWaterBalance(); name=:scene_water) |> kind=:plant, scale=:Leaf, within=SceneScope(), - process=:transpiration, + application=:transpiration, var=:transpiration, ), ) @@ -163,13 +201,13 @@ ModelSpec(SceneEnergyBalance(); name=:scene_energy) |> kind=:plant, scale=:Leaf, within=SceneScope(), - process=:energy_balance, + application=:energy_balance, ), :soil => One( kind=:soil, scale=:Soil, within=SceneScope(), - process=:soil_water, + application=:soil_water, ), ) ``` @@ -215,7 +253,7 @@ oil_palm = ObjectTemplate( Inputs( :leaf_carbon => Many( scale=:Leaf, - within=Self(), + within=Subtree(), var=:leaf_carbon, ), ), @@ -263,8 +301,8 @@ ModelSpec(DailyPlantModel(); name=:daily_plant) |> Inputs( :leaf_fluxes => Many( scale=:Leaf, - within=Self(), - process=:leaf_flux, + within=Subtree(), + application=:leaf_flux, var=:flux, policy=Integrate(), window=Day(1), @@ -404,15 +442,15 @@ structured diagnostics, and `collect_outputs(sim)` for tabular rows. ```julia request = OutputRequest( - :Leaf, + Many(scale=:Leaf), :transpiration; name=:leaf_transpiration_daily, - process=:leaf_energy, + application=:leaf_energy, policy=Integrate(), clock=Day(1), ) -sim = run!(scene; steps=48, tracked_outputs=request) +sim = run!(scene; steps=48, outputs=request) daily = collect_outputs(sim, :leaf_transpiration_daily) ``` @@ -422,10 +460,9 @@ dynamic objects only over the interval where that object published samples. If several scene applications implement the same process, add `application=:application_name` to select one explicitly. This is also the way to request a named `:stream_only` publisher. -When `tracked_outputs` is explicit, the runtime retains only requested -application/variable streams plus streams needed by temporal `Inputs(...)`. -Passing `tracked_outputs=OutputRequest[]` therefore keeps no output streams -unless a temporal dependency requires one. Use `explain_output_retention(sim)` +`outputs=:none` retains no user streams. Passing explicit requests retains only +their application/variable streams plus streams needed by temporal +`Inputs(...)`. Use `explain_output_retention(sim)` to inspect why each retained stream was kept. Dependency-only streams retain a bounded policy-specific horizon, while requested streams keep complete histories for post-run export. Export is not yet a fully online path. @@ -435,18 +472,16 @@ histories for post-run export. Export is not yet a fully online path. Use structured explanations instead of inspecting internal dictionaries: ```julia -compiled = refresh_bindings!(scene) - explain_objects(scene) explain_instances(scene) explain_scopes(scene) -explain_scene_applications(compiled) -explain_bindings(compiled) -explain_calls(compiled) -explain_environment_bindings(refresh_environment_bindings!(scene, compiled)) -explain_schedule(compiled) -explain_writers(compiled) -explain_model_bundles(compiled) +explain_scene_applications(scene) +explain_bindings(scene) +explain_calls(scene) +explain_environment_bindings(scene) +explain_schedule(scene) +explain_writers(scene) +explain_model_bundles(scene) ``` These functions return structured rows with concrete object ids, application diff --git a/docs/src/model_execution.md b/docs/src/model_execution.md index c265c17dd..be1950cb3 100644 --- a/docs/src/model_execution.md +++ b/docs/src/model_execution.md @@ -50,13 +50,13 @@ provider is bound to it. The model implementation stays reusable. ## Compilation Before Runtime -Before the timestep loop, `compile_scene(scene)` and `refresh_bindings!(scene)` -resolve the scenario into concrete runtime carriers: +Before the timestep loop, PlantSimEngine compiles the scene into concrete +runtime carriers: 1. `AppliesTo(...)` selectors are resolved to stable object ids. 2. `Inputs(...)` selectors are resolved to source object/application ids. 3. Same-rate inputs are wired as shared `Ref`s, `RefVector`s, or - `ObjectRefVector`s. + heterogeneous object-reference vectors. 4. Temporal inputs are compiled as stream lookups with a policy such as `HoldLast`, `Interpolate`, `Integrate`, or `Aggregate`. 5. `Calls(...)` declarations are compiled to callable target lists. @@ -73,15 +73,13 @@ compiled indexes and carriers. Useful inspection helpers: ```julia -compiled = refresh_bindings!(scene) - -explain_scene_applications(compiled) -explain_bindings(compiled) -explain_calls(compiled) -explain_environment_bindings(refresh_environment_bindings!(scene, compiled)) -explain_schedule(compiled) +explain_scene_applications(scene) +explain_bindings(scene) +explain_calls(scene) +explain_environment_bindings(scene) +explain_schedule(scene) explain_execution_plan(scene) -explain_writers(compiled) +explain_writers(scene) ``` These explanations are intended for both users and agents. They report the @@ -101,7 +99,7 @@ ModelSpec(SceneLAI(ground_area); name=:scene_lai) |> kind=:plant, scale=:Leaf, within=SceneScope(), - process=:leaf_state, + application=:leaf_state, var=:leaf_area, ), ) @@ -116,7 +114,7 @@ the carrier supports it. If an input is not explicitly declared with `Inputs(...)`, the compiler can infer simple same-object bindings when exactly one producer on the same object outputs the same variable. Ambiguous producers are errors and should be -disambiguated with `process=...`, `application=...`, or `var=...`. +disambiguated with `application=...` and, when names differ, `var=...`. Use `PreviousTimeStep(:x) => selector` when a feedback dependency should read the previous sample instead of creating a same-timestep scheduling edge. @@ -134,13 +132,13 @@ ModelSpec(SceneEnergyBalance(); name=:scene_energy) |> kind=:plant, scale=:Leaf, within=SceneScope(), - process=:energy_balance, + application=:energy_balance, ), :soil => One( kind=:soil, scale=:Soil, within=SceneScope(), - process=:soil_water, + application=:soil_water, ), ) |> TimeStep(Dates.Hour(1)) @@ -169,7 +167,7 @@ do not publish temporal samples or scatter mutable environment outputs. Call `run_call!(target; publish=true)` once for the accepted state. Applications selected only by `Calls(...)` are marked manual-call-only in -`explain_schedule(compiled)` and are skipped by the root `run!(scene)` loop. +`explain_schedule(scene)` and are skipped by the root `run!(scene)` loop. ## Duplicate Writers With Updates @@ -187,8 +185,10 @@ ModelSpec(LeafPruning(); name=:leaf_pruning) |> ``` This keeps ordinary duplicate outputs as errors while allowing cases such as -allocation followed by pruning. `explain_writers(compiled)` reports writer +allocation followed by pruning. `explain_writers(scene)` reports writer groups and the `Updates(...)` declarations that validate them. +The `after` value is the canonical application identifier shown by +`explain_scene_applications(scene)`, not the process name. ## Multirate Execution @@ -204,8 +204,8 @@ ModelSpec(DailyPlantAllocation(); name=:allocation) |> Inputs( :leaf_assimilation => Many( scale=:Leaf, - within=Self(), - process=:leaf_assimilation, + within=Subtree(), + application=:leaf_assim, var=:A, policy=Integrate(), window=Dates.Day(1), @@ -240,6 +240,10 @@ Supported policies are: `Integrate(...)` and `Aggregate(...)` accept reducer objects or callables that take either `(values)` or `(values, durations_seconds)`. +Temporal windows are duration-based rolling windows. Calendar-aligned civil +days and "previous complete period" selection are not part of the public API; +there is no `CalendarWindow` compatibility type. + ## Environment Sampling `Environment(...)` chooses a provider and optional source-variable remapping: @@ -276,21 +280,21 @@ sim = run!(scene; steps=30, constants=Constants()) The returned `SceneSimulation` contains the mutated scene, compiled bindings, environment bindings, execution plan, and retained temporal output streams. -By default, scene runs retain all published streams. For large scenes, pass -`OutputRequest` values to retain only selected outputs and temporal dependency -streams: +By default, scene runs retain no user output streams. Pass `outputs=:all` to +retain every published stream, or pass `OutputRequest` values to retain only +selected outputs and required temporal dependency streams: ```julia request = OutputRequest( - :Leaf, + Many(scale=:Leaf), :A; name=:leaf_assimilation_daily, - process=:leaf_assimilation, + application=:leaf_assimilation, policy=Integrate(), clock=Dates.Day(1), ) -sim = run!(scene; steps=72, tracked_outputs=request) +sim = run!(scene; steps=72, outputs=request) collect_outputs(sim, :leaf_assimilation_daily; sink=nothing) explain_output_retention(sim) ``` @@ -300,8 +304,18 @@ When several applications publish the same process and variable, use application directly and can also request an explicitly named `:stream_only` publisher. -Set `tracked_outputs=OutputRequest[]` to retain no output streams unless they -are required by temporal dependencies. +`outputs=:none` retains no user output streams. Histories required by temporal +dependencies are still maintained with bounded retention. + +`run!(scene; ...)` always starts a fresh result timeline. Continue an existing +simulation without resetting its step index, environment position, multirate +phase, or temporal histories with: + +```julia +continue!(sim; steps=24) +step!(sim) +current_step(sim) +``` Temporal dependency streams that are not explicitly requested retain only the history required by their input policy. `HoldLast` keeps the latest sample, @@ -341,7 +355,22 @@ low-level operation for callers that already own a complete `Object`. Structural changes invalidate compiled object/model bindings. Movement and geometry changes invalidate environment bindings without rebuilding structural -input carriers. The next `run!(scene)` step refreshes the necessary caches. +input carriers. The next `run!` or `continue!` timestep refreshes the necessary +caches. + +Do not mutate `Object` topology, labels, or geometry fields directly. Direct +field mutation bypasses registry indexes and cache invalidation and is +unsupported. Use the lifecycle functions above. They validate prerequisites +before mutating; in particular, `reparent_object!` rejects self-parenting and +descendant cycles without changing existing links. + +Inside a lifecycle-capable model kernel, use `runtime_scene(extra)` to obtain +the live scene. Objects created during a kernel call do not recursively execute +inside that call. Structural targets, value carriers, call targets, writer +validation, schedules, and output-request matches are refreshed at the next +timestep boundary. Geometry-only mutations refresh affected environment +bindings at that boundary; already published streams remain available for +removed objects. ## Historical API Translation diff --git a/docs/src/prerequisites/key_concepts.md b/docs/src/prerequisites/key_concepts.md index 8cf47d2a3..c6697701f 100644 --- a/docs/src/prerequisites/key_concepts.md +++ b/docs/src/prerequisites/key_concepts.md @@ -49,7 +49,7 @@ objects. - `Calls(...)` binds manually controlled model calls; - `TimeStep(...)` selects the execution cadence; - `Environment(...)` configures environment sampling; -- `Updates(...)` orders intentional additional writers; +- `Updates(...; after=:application_id)` orders intentional additional writers; - `OutputRouting(...)` controls output publication. This keeps model implementations generic. Models do not need to know which diff --git a/docs/src/scene_object/quickstart.md b/docs/src/scene_object/quickstart.md index 708039bad..d7121d0be 100644 --- a/docs/src/scene_object/quickstart.md +++ b/docs/src/scene_object/quickstart.md @@ -17,7 +17,11 @@ TimeStep Environment ``` -Scenarios are defined with `Scene` and model applications. +Scenarios are defined with `Scene` and model applications. A model is a +reusable process implementation; an application is one configured use of that +model, including its name, target objects, inputs, cadence, and environment. +One application may run on many objects, and the same model may appear in +several applications with different parameters or targets. ```@setup scene_object_quickstart using PlantSimEngine, PlantMeteo, Dates, DataFrames @@ -26,6 +30,16 @@ using PlantSimEngine.Examples ## One Object, Several Models +For models that all run on one object, the concise constructor lowers directly +to the ordinary Scene/Object representation: + +```julia +scene = Scene(ModelA(), ModelB(); status=(initial_value=1.0,)) +``` + +Use the explicit form below when applications need names, selectors, cadence, +or other scenario policies. + The first scene has one object, `:scene`, and three model applications: - `ToyDegreeDaysCumulModel` computes daily thermal time; @@ -60,7 +74,7 @@ scene = Scene( ) sim = run!(scene; steps=30, constants=Constants()) -out = DataFrame(collect_outputs(sim; sink=nothing)) +out = collect_outputs(sim; sink=DataFrame) first(out, 6) ``` @@ -73,6 +87,11 @@ scene_status = only(scene_objects(scene; scale=:Scene)).status ## Inspect The Compiled Bindings +Before running, `explain_initialization(scene)` classifies each variable as +`:supplied`, `:generated`, `:producer_bound`, `:environment_bound`, or +`:unresolved`. The report remains available when ordinary required values are +missing, so it can be used to finish configuring a scene. + The compiler infers unambiguous same-object dependencies from declared model inputs and outputs: @@ -81,7 +100,7 @@ inputs and outputs: ```@example scene_object_quickstart select( - DataFrame(explain_bindings(refresh_bindings!(scene))), + DataFrame(explain_bindings(scene)), :application_id, :input, :source_application_ids, @@ -107,16 +126,16 @@ select( ## Request Outputs -By default, scene runs retain all published streams. For large scenes, pass -`OutputRequest` values to retain and materialize only selected publisher -streams plus any streams needed by temporal inputs. +By default, scene runs retain no user output streams. Pass `outputs=:all` to +retain every publisher, or pass `OutputRequest` values to retain and +materialize selected streams plus those required by temporal inputs. ```@example scene_object_quickstart request = OutputRequest( - :Scene, + Many(scale=:Scene), :LAI; name=:lai_every_two_days, - process=:LAI_Dynamic, + application=:lai, policy=HoldLast(), clock=Day(2), ) @@ -125,7 +144,7 @@ requested_sim = run!( scene; steps=30, constants=Constants(), - tracked_outputs=request, + outputs=request, ) collect_outputs(requested_sim, :lai_every_two_days; sink=nothing)[1:4] @@ -181,7 +200,7 @@ The compiled binding shows a `RefVector` carrier: ```@example scene_object_quickstart select( - DataFrame(explain_bindings(refresh_bindings!(plant_scene))), + DataFrame(explain_bindings(plant_scene)), :application_id, :input, :source_ids, @@ -190,7 +209,7 @@ select( ) ``` -If the consumer model runs on each plant, use `within=Self()` to read only +If the consumer model runs on each plant, use `within=Subtree()` to read only objects inside the current plant. Use `within=SceneScope()` when a scene model must aggregate all matching objects. @@ -207,13 +226,13 @@ ModelSpec(SceneEnergyBalance(); name=:scene_energy) |> kind=:plant, scale=:Leaf, within=SceneScope(), - process=:energy_balance, + application=:energy_balance, ), :soil => One( kind=:soil, scale=:Soil, within=SceneScope(), - process=:soil_water, + application=:soil_water, ), ) |> TimeStep(Hour(1)) diff --git a/docs/src/step_by_step/advanced_coupling.md b/docs/src/step_by_step/advanced_coupling.md index 4fd10e381..1202b80e8 100644 --- a/docs/src/step_by_step/advanced_coupling.md +++ b/docs/src/step_by_step/advanced_coupling.md @@ -52,12 +52,12 @@ complex_scene = Scene( ModelSpec(Process2Model(); name=:process2) |> AppliesTo(One(scale=:Scene)) |> - Calls(:process1 => One(scale=:Scene, process=:process1)) |> + Calls(:process1 => One(scale=:Scene, application=:process1)) |> TimeStep(Day(1)), ModelSpec(Process3Model(); name=:process3) |> AppliesTo(One(scale=:Scene)) |> - Calls(:process2 => One(scale=:Scene, process=:process2)) |> + Calls(:process2 => One(scale=:Scene, application=:process2)) |> TimeStep(Day(1)), ModelSpec(Process5Model(); name=:process5) |> @@ -75,9 +75,8 @@ complex_scene = Scene( environment=meteo_day, ) -compiled = refresh_bindings!(complex_scene) select( - DataFrame(explain_calls(compiled)), + DataFrame(explain_calls(complex_scene)), :application_id, :call, :callee_application_ids, @@ -98,7 +97,7 @@ rules: ```@example scene_advanced_coupling select( - DataFrame(explain_schedule(compiled)), + DataFrame(explain_schedule(complex_scene)), :application_id, :manual_call_only, :execution_index, diff --git a/docs/src/step_by_step/detailed_first_example.md b/docs/src/step_by_step/detailed_first_example.md index 1af466624..7c4819a76 100644 --- a/docs/src/step_by_step/detailed_first_example.md +++ b/docs/src/step_by_step/detailed_first_example.md @@ -96,9 +96,8 @@ Before runtime, PlantSimEngine resolves selectors and builds a compiled scene. This avoids resolving object selections inside the timestep loop. ```@example detailed_scene -compiled = refresh_bindings!(scene) select( - DataFrame(explain_scene_applications(compiled)), + DataFrame(explain_scene_applications(scene)), :application_id, :process, :target_ids, @@ -109,14 +108,14 @@ select( initialized directly on the object status: ```@example detailed_scene -explain_bindings(compiled) +explain_bindings(scene) ``` The schedule tells us when each application runs: ```@example detailed_scene select( - DataFrame(explain_schedule(compiled)), + DataFrame(explain_schedule(scene)), :application_id, :dt_seconds, :root_scheduled, @@ -182,7 +181,7 @@ coupled_scene = Scene( ) select( - DataFrame(explain_bindings(refresh_bindings!(coupled_scene))), + DataFrame(explain_bindings(coupled_scene)), :application_id, :input, :source_application_ids, @@ -234,7 +233,7 @@ bad_scene = Scene( ) try - refresh_bindings!(bad_scene) + explain_bindings(bad_scene) catch err first(sprint(showerror, err), 300) end diff --git a/docs/src/step_by_step/implement_a_model.md b/docs/src/step_by_step/implement_a_model.md index 1982db3a9..6629c5791 100644 --- a/docs/src/step_by_step/implement_a_model.md +++ b/docs/src/step_by_step/implement_a_model.md @@ -41,11 +41,12 @@ end Write the [`run!`](@ref) function that operates on a single timestep : ```@example usepkg -function run!(::Beer, models, status, meteo, constants, extras) - status.PPFD = +function PlantSimEngine.run!(::Beer, models, status, meteo, constants, extra) + status.aPPFD = meteo.Ri_PAR_f * exp(-models.light_interception.k * status.LAI) * constants.J_to_umol + return nothing end ``` @@ -169,7 +170,7 @@ applications, inputs, and manual calls. Each model has its own [`run!`](@ref) method for updating the current state. The function takes six arguments: ```julia -function run!(::Beer, models, status, meteo, constants, extras) +function PlantSimEngine.run!(::Beer, models, status, meteo, constants, extra) ``` - the model's type @@ -189,11 +190,12 @@ Note that the input and output variables are accessed through the `status` argument: ```@example usepkg -function run!(::Beer, models, status, meteo, constants, extras) - status.PPFD = +function PlantSimEngine.run!(::Beer, models, status, meteo, constants, extra) + status.aPPFD = meteo.Ri_PAR_f * exp(-models.light_interception.k * status.LAI) * constants.J_to_umol + return nothing end ``` diff --git a/docs/src/step_by_step/model_switching.md b/docs/src/step_by_step/model_switching.md index 5af6b3118..3ff63fb8f 100644 --- a/docs/src/step_by_step/model_switching.md +++ b/docs/src/step_by_step/model_switching.md @@ -60,7 +60,7 @@ growth model reads `aPPFD`, which is produced by the light interception model: ```@example scene_model_switching select( - DataFrame(explain_bindings(refresh_bindings!(rue_scene))), + DataFrame(explain_bindings(rue_scene)), :application_id, :input, :source_application_ids, diff --git a/docs/src/step_by_step/quick_and_dirty_examples.md b/docs/src/step_by_step/quick_and_dirty_examples.md index a9dede486..00ae1965c 100644 --- a/docs/src/step_by_step/quick_and_dirty_examples.md +++ b/docs/src/step_by_step/quick_and_dirty_examples.md @@ -76,7 +76,7 @@ Inspect the inferred coupling: ```@example quick_scene_examples select( - DataFrame(explain_bindings(refresh_bindings!(lai_scene))), + DataFrame(explain_bindings(lai_scene)), :application_id, :input, :source_application_ids, @@ -124,7 +124,7 @@ request = OutputRequest( :Scene, :biomass; name=:biomass_daily, - process=:growth, + application=:growth, policy=HoldLast(), clock=Day(1), ) @@ -133,7 +133,7 @@ requested_sim = run!( growth_scene; steps=5, constants=Constants(), - tracked_outputs=request, + outputs=request, ) first(collect_outputs(requested_sim, :biomass_daily), 5) diff --git a/docs/src/step_by_step/simple_model_coupling.md b/docs/src/step_by_step/simple_model_coupling.md index c24ca16f1..53c1fc5e0 100644 --- a/docs/src/step_by_step/simple_model_coupling.md +++ b/docs/src/step_by_step/simple_model_coupling.md @@ -40,7 +40,7 @@ light_scene = Scene( ) light_sim = run!(light_scene; steps=3, constants=Constants()) -first(DataFrame(collect_outputs(light_sim; sink=nothing)), 3) +first(collect_outputs(light_sim; sink=DataFrame), 3) ``` ## Coupling two models @@ -68,9 +68,8 @@ coupled_scene = Scene( environment=meteo_day, ) -compiled = refresh_bindings!(coupled_scene) select( - DataFrame(explain_bindings(compiled)), + DataFrame(explain_bindings(coupled_scene)), :application_id, :input, :source_application_ids, diff --git a/docs/src/troubleshooting/common_errors.md b/docs/src/troubleshooting/common_errors.md new file mode 100644 index 000000000..3ce8ee12a --- /dev/null +++ b/docs/src/troubleshooting/common_errors.md @@ -0,0 +1,13 @@ +# Common Errors + +A missing-input error means neither supplied state, a unique producer, nor an +environment binding exists; start with `explain_initialization`. A cardinality +error lists selector matches; correct the scope or choose the intended +`OptionalOne`/`Many` multiplicity. An ambiguity requires an explicit +application/object selector. + +Duplicate-writer errors require either distinct output routing or an explicit +`Updates` order. Cadence errors require fixed `Dates` periods compatible with +the environment base step. Extend package functions as +`PlantSimEngine.run!(...)`, including the package qualification. + diff --git a/docs/src/troubleshooting/dependency_cycles.md b/docs/src/troubleshooting/dependency_cycles.md new file mode 100644 index 000000000..e3c4f7d6a --- /dev/null +++ b/docs/src/troubleshooting/dependency_cycles.md @@ -0,0 +1,26 @@ +# Diagnosing Dependency Cycles + +A same-step value cycle is rejected because no valid execution order exists. +Read the reported application, object, and variable edges. If the science uses +yesterday's value, put `PreviousTimeStep(:variable)` on that input. If the +science requires convergence in the current step, make one parent application +own child trials with `Calls`. Otherwise reformulate the coupled equations. + +Application declaration order is not a cycle-resolution mechanism. + +For example, if application `:leaf` reads same-step `water` from `:root` while +`:root` reads same-step `carbon` from `:leaf`, compilation fails before either +kernel runs. If root water scientifically affects tomorrow's leaf carbon, +change only that edge: + +```julia +Inputs( + PreviousTimeStep(:water) => + One(scale=:Root, application=:root, var=:water), +) +``` + +The receiving object's initial `water` value is used until the first accepted +historical sample exists. If both values must converge within the same step, +do not add a lag: make a parent model own `Calls` to the two trial models, +iterate with `publish=false`, and publish each accepted state once. diff --git a/docs/src/troubleshooting/runtime_contracts.md b/docs/src/troubleshooting/runtime_contracts.md new file mode 100644 index 000000000..c22f33485 --- /dev/null +++ b/docs/src/troubleshooting/runtime_contracts.md @@ -0,0 +1,11 @@ +# Runtime Contracts And Diagnostics + +Use `explain_initialization`, `explain_bindings`, `explain_calls`, +`explain_schedule`, `explain_environment_bindings`, and +`explain_output_retention` as the supported inspection surface. Do not inspect +compiled internal fields. + +Targets, carriers, calls, writer checks, and schedules refresh after structural +changes between timesteps. Movement and geometry changes invalidate affected +spatial environment bindings. Accepted streams are append-only. + diff --git a/docs/src/tutorials/growing_plant/part1_growth.md b/docs/src/tutorials/growing_plant/part1_growth.md new file mode 100644 index 000000000..cbecb9218 --- /dev/null +++ b/docs/src/tutorials/growing_plant/part1_growth.md @@ -0,0 +1,32 @@ +# Growing A Plant Scene + +Begin with a plant object and leaf objects whose carbon production is gathered +by a plant application through `Many(scale=:Leaf, within=Subtree())`. A growth +model calls `register_object!` after its carbon or thermal threshold is met. + +Structural changes become visible after the current timestep completes. The +new leaf receives status and application targets during refresh and starts on +the following timestep; it cannot consume the resource that created it in the +same kernel call. + +Build the initial registry explicitly so ownership remains visible: + +```julia +scene = Scene( + Object(:plant; scale=:Plant, status=Status(carbon=0.0)), + Object(:leaf_1; scale=:Leaf, parent=:plant, status=Status(area=1.0)); + applications=(leaf_application, plant_balance, growth_application), + environment=weather, +) +``` + +The plant balance gathers leaf production with +`Many(scale=:Leaf, within=Subtree())`. The growth kernel obtains the live scene +with `runtime_scene(extra)`, checks its carbon and thermal thresholds, creates +a fully initialized `Object`, and calls `register_object!`. It should deduct +the construction cost exactly once before registration. + +After each step, assert both biology and structure: remaining plant carbon, +the number of leaf objects, each new leaf's parent, and accepted historical +outputs. `explain_scene_applications` should show that the new leaf is absent +during its creation step and present after the between-step refresh. diff --git a/docs/src/tutorials/growing_plant/part2_roots_water.md b/docs/src/tutorials/growing_plant/part2_roots_water.md new file mode 100644 index 000000000..08866df6a --- /dev/null +++ b/docs/src/tutorials/growing_plant/part2_roots_water.md @@ -0,0 +1,34 @@ +# Adding Roots And Water + +Add root objects and gather absorption through a plant-local `Many` selector. +Keep shared carbon and water stocks on the plant, while leaf and root state +remains object-local. Environment precipitation is an environment input; root +creation is an explicit `register_object!` operation with initialized status. + +When several plants share one soil object, select it explicitly with a +scene-wide `One` selector rather than relying on traversal order. + +Keep stocks at the scale that owns conservation. A root model may publish an +absorption rate per root, while the plant model integrates all root rates and +updates one plant water stock. A soil model owns soil water; plants read it +through an explicit scene-wide selector. This avoids copying one stock into +every organ and makes duplicate writers visible. + +```julia +Inputs( + :root_uptake => Many( + scale=:Root, within=Subtree(), application=:root_absorption, + var=:uptake, policy=Integrate(), window=Day(1), + ), + :soil_water => One( + scale=:Soil, within=SceneScope(), application=:soil_water, + var=:water, + ), +) +``` + +Precipitation, temperature, and radiation remain environment variables, not +ordinary object outputs. Use `Environment(sources=...)` when provider column +names differ from model-facing names. When growth creates a root, initialize +all required root status values before `register_object!`; verify the next +timestep's carrier with `input_value` or `explain_bindings`. diff --git a/docs/src/tutorials/growing_plant/part3_debugging.md b/docs/src/tutorials/growing_plant/part3_debugging.md new file mode 100644 index 000000000..81afe7172 --- /dev/null +++ b/docs/src/tutorials/growing_plant/part3_debugging.md @@ -0,0 +1,32 @@ +# Debugging Growth And Resource Ordering + +If an organ appears to spend resources before it exists, inspect activation +timing and the compiled schedule. If two models intentionally update one stock, +declare `Updates(:stock; after=:producer)`. If a parent must test several child +states before accepting one, use `Calls` and publish only the accepted call. + +For cycles, choose a scientific meaning: lag one edge with +`PreviousTimeStep`, put convergence under a parent-owned hard call, or +reformulate the equations. Do not resolve a cycle by incidental application +ordering. + +Use this debugging order: + +1. `explain_initialization(scene)` for missing state or environment values. +2. `explain_bindings(scene)` for source scope and multiplicity. +3. `explain_writers(scene)` for competing canonical outputs. +4. `explain_calls(scene)` for call-only targets and target cardinality. +5. `explain_schedule(scene)` for cadence and root ordering. +6. `explain_outputs(simulation)` after execution for publication history. + +A trial call must not mutate accepted output history or scatter mutable +environment outputs. Nested trials inherit the outer publication decision. +Convergence and failure policy belongs to the parent model: it decides the +iteration limit, tolerance, fallback, and whether any state is accepted. + +Structural mutation is also transactional at the timestep boundary. A new +organ is registered immediately in the scene registry but does not recursively +run during the kernel that created it. Before the next timestep, compilation +refreshes targets, carriers, calls, writer validation, schedules, and requested +outputs. Geometry-only movement refreshes only affected spatial bindings where +possible. diff --git a/docs/test/runtests.jl b/docs/test/runtests.jl new file mode 100644 index 000000000..08d6c77ce --- /dev/null +++ b/docs/test/runtests.jl @@ -0,0 +1,8 @@ +using Test + +ENV["PLANTSIMENGINE_DOCS_BUILD_ONLY"] = "true" +include(joinpath(@__DIR__, "..", "make.jl")) + +@testset "documentation build" begin + @test isdir(joinpath(@__DIR__, "..", "build")) +end diff --git a/examples/ToyCAllocationModel.jl b/examples/ToyCAllocationModel.jl index ba9064a8d..afb060aa5 100644 --- a/examples/ToyCAllocationModel.jl +++ b/examples/ToyCAllocationModel.jl @@ -68,4 +68,5 @@ function PlantSimEngine.run!(::ToyCAllocationModel, models, status, meteo, const end end -# Can be parallelized over time-steps, but not objects (we have vectors of values coming from other objects as input): +# This model reads values from several objects, so object-level independence +# must not be assumed by a future executor. diff --git a/examples/ToyLAIModel.jl b/examples/ToyLAIModel.jl index 8fba0ff74..07b55d787 100644 --- a/examples/ToyLAIModel.jl +++ b/examples/ToyLAIModel.jl @@ -55,10 +55,8 @@ function PlantSimEngine.run!(::ToyLAIModel, models, status, meteo, constants=not end end -# The computation of ToyLAIModel is independant of previous values and other objects. We can add this information as -# traits to the model to tell PlantSimEngine that it is safe to run the models in parallel: - - +# ToyLAIModel is independent of previous values and other objects. The current +# public runtime remains sequential and owns execution policy. # A second model at scene scale: """ @@ -93,4 +91,5 @@ function PlantSimEngine.run!(m::ToyLAIfromLeafAreaModel, models, status, meteo, status.LAI = status.total_surface / m.scene_area end -# The computation of ToyLAIfromLeafAreaModel is independant of previous values so we can compute it in parallel over time-steps: +# ToyLAIfromLeafAreaModel is independent of previous values, but execution +# policy remains owned by the runtime. diff --git a/examples/ToyRUEGrowthModel.jl b/examples/ToyRUEGrowthModel.jl index ab21356f8..4c56b20e6 100644 --- a/examples/ToyRUEGrowthModel.jl +++ b/examples/ToyRUEGrowthModel.jl @@ -47,6 +47,4 @@ function PlantSimEngine.run!(::ToyRUEGrowthModel, models, status, meteo, constan status.biomass += status.biomass_increment end -# And optionally, we can tell PlantSimEngine that we can safely parallelize our model over space (objects): - -# Note that this model cannot be parallelized over time because we use the biomass from the previous time-step. \ No newline at end of file +# The model uses biomass from the previous timestep, so time steps are stateful. diff --git a/examples/ToySoilModel.jl b/examples/ToySoilModel.jl index 8ff602049..58d56193b 100644 --- a/examples/ToySoilModel.jl +++ b/examples/ToySoilModel.jl @@ -20,8 +20,9 @@ struct ToySoilWaterModel{T<:Union{AbstractRange{Float64},AbstractVector{Float64} values::T end -# Defining a method with keyword arguments and default values: -ToySoilWaterModel(values=[0.5]) = ToySoilWaterModel(values) +# Defining a zero-argument default without shadowing the generated positional +# constructor. +ToySoilWaterModel() = ToySoilWaterModel([0.5]) # Defining the inputs and outputs of the model: PlantSimEngine.inputs_(::ToySoilWaterModel) = NamedTuple() diff --git a/examples/maespa_scene_example.jl b/examples/maespa_scene_example.jl index 9c4205e08..7eb09ace2 100644 --- a/examples/maespa_scene_example.jl +++ b/examples/maespa_scene_example.jl @@ -412,11 +412,11 @@ function _maespa_species_template(species; monteith, fvcb, tuzet, allocation) ( ModelSpec(monteith; name=:energy_balance) |> AppliesTo(Many(scale=:Leaf)) |> - Calls(:photosynthesis => One(scale=:Leaf, process=:photosynthesis)) |> + Calls(:photosynthesis => One(scale=:Leaf, application=:photosynthesis)) |> TimeStep(Dates.Hour(1)), ModelSpec(fvcb; name=:photosynthesis) |> AppliesTo(Many(scale=:Leaf)) |> - Calls(:stomatal_conductance => One(scale=:Leaf, process=:stomatal_conductance)) |> + Calls(:stomatal_conductance => One(scale=:Leaf, application=:stomatal_conductance)) |> TimeStep(Dates.Hour(1)), ModelSpec(tuzet; name=:stomatal_conductance) |> AppliesTo(Many(scale=:Leaf)) |> @@ -426,7 +426,7 @@ function _maespa_species_template(species; monteith, fvcb, tuzet, allocation) TimeStep(Dates.Hour(1)), ModelSpec(allocation; name=:allocation) |> AppliesTo(One(scale=:Plant)) |> - Inputs(:leaf_carbon => Many(scale=:Leaf, within=Self(), var=:leaf_carbon)) |> + Inputs(:leaf_carbon => Many(scale=:Leaf, within=Subtree(), var=:leaf_carbon)) |> TimeStep(Dates.Day(1)), ); kind=:plant, @@ -508,7 +508,7 @@ function build_maespa_scene(; scene_model=SceneEB(25, 0.03, 0.005), meteo=maespa AppliesTo(One(scale=:Scene)) |> Calls( :energy_balance => Many(kind=:plant, scale=:Leaf, process=:energy_balance), - :soil => One(kind=:soil, scale=:Soil, process=:soil_water), + :soil => One(kind=:soil, scale=:Soil, application=:soil_water), ) |> TimeStep(Dates.Hour(1)), ModelSpec(SoilWater(0.45, -0.03, 4.4, 0.25, 0.75); name=:soil_water) |> @@ -535,9 +535,14 @@ end function run_maespa_example(; nhours=24, check=true) scene = build_maespa_scene(; meteo=maespa_meteo(; nhours=nhours)) - compiled = compile_scene(scene) - check && refresh_environment_bindings!(scene, compiled) - simulation = run!(scene; steps=nhours, constants=PlantMeteo.Constants()) + compiled = Advanced.compile_scene(scene) + check && Advanced.refresh_environment_bindings!(scene, compiled) + simulation = run!( + scene; + steps=nhours, + constants=PlantMeteo.Constants(), + outputs=:all, + ) return ( scene=scene, compiled=simulation.compiled, diff --git a/ext/PlantSimEngineGraphEditorExt.jl b/ext/PlantSimEngineGraphEditorExt.jl new file mode 100644 index 000000000..62e4115d4 --- /dev/null +++ b/ext/PlantSimEngineGraphEditorExt.jl @@ -0,0 +1,862 @@ +module PlantSimEngineGraphEditorExt + +import HTTP +import JSON +import PlantSimEngine +import PlantSimEngine: edit_graph, current_scene, apply_edit!, undo!, redo! + +mutable struct GraphEditorSession <: PlantSimEngine.AbstractSceneGraphEditorSession + scene::PlantSimEngine.Scene + history::Vector{Any} + future::Vector{Any} + server::Any + host::String + port::Int + token::String + url::String + autosave_path::Union{Nothing,String} + save_path::Union{Nothing,String} + allow_julia_eval::Bool + recent_paths::Vector{String} +end + +current_scene(session::GraphEditorSession) = session.scene + +function Base.close(session::GraphEditorSession) + try + isopen(session.server) && close(session.server) + catch + close(session.server) + end + return nothing +end + +function Base.show(io::IO, session::GraphEditorSession) + print(io, "GraphEditorSession(url=$(repr(session.url)), applications=$(length(session.scene.applications)))") +end + +function Base.show(io::IO, ::MIME"text/plain", session::GraphEditorSession) + println(io, "PlantSimEngineGraphEditorExt.GraphEditorSession") + println(io, " Open in browser: $(session.url)") + println(io, " State JSON: $(_state_url(session))") + println(io, " Current scene: current_scene(session)") + println(io, " Quit session: close(session)") + isnothing(session.autosave_path) || println(io, " Recovery autosave: $(session.autosave_path)") + isnothing(session.save_path) || println(io, " Saving changes to: $(session.save_path)") +end + +""" + edit_graph([scene]; host="127.0.0.1", port=0, open_browser=true, + autosave=true, allow_remote=false, allow_julia_eval=nothing) + +Start a local Scene graph editor. Julia owns the current Scene and applies all +semantic edits received from the browser. Call `edit_graph()` to start from an +empty Scene and `close(session)` to stop the server. +""" +function edit_graph( + scene::PlantSimEngine.Scene=PlantSimEngine.Scene(); + host::AbstractString="127.0.0.1", + port::Integer=0, + open_browser::Bool=true, + autosave::Bool=true, + autosave_path::Union{Nothing,AbstractString}=nothing, + save_path::Union{Nothing,AbstractString}=nothing, + allow_remote::Bool=false, + allow_julia_eval::Union{Nothing,Bool}=nothing, + recover_path::Union{Nothing,AbstractString}=nothing, + recent_paths=String[], +) + _is_loopback_host(host) || allow_remote || error( + "Graph editor sessions are limited to localhost by default. Pass `allow_remote=true` only for a trusted network.", + ) + effective_allow_julia_eval = isnothing(allow_julia_eval) ? !allow_remote : allow_julia_eval + initial_scene = isnothing(recover_path) ? deepcopy(scene) : _load_scene_file( + _normalized_path(recover_path); + allow_julia_eval=effective_allow_julia_eval, + ) + session_ref = Ref{Any}() + handler = stream -> _handle_http(session_ref[], stream) + server = HTTP.listen!(handler, host, port; listenany=true, verbose=false) + actual_port = HTTP.port(server) + token = _session_token(server) + autosave_file = autosave ? _normalized_path( + isnothing(autosave_path) ? _default_autosave_path() : autosave_path, + ) : nothing + session = GraphEditorSession( + initial_scene, + Any[], + Any[], + server, + String(host), + actual_port, + token, + "http://$(host):$(actual_port)/?token=$(token)", + autosave_file, + isnothing(save_path) ? nothing : _normalized_path(save_path), + effective_allow_julia_eval, + String[_normalized_path(path) for path in recent_paths], + ) + session_ref[] = session + isnothing(session.save_path) || _remember_path!(session, session.save_path) + isnothing(recover_path) || _remember_path!(session, _normalized_path(recover_path)) + _persist_scene!(session) + open_browser && _open_in_default_browser(session.url) + return session +end + +function apply_edit!(session::GraphEditorSession, edit::PlantSimEngine.AbstractSceneGraphEdit) + candidate = PlantSimEngine.apply_scene_graph_edit(session.scene, edit) + push!(session.history, session.scene) + empty!(session.future) + session.scene = candidate + _persist_scene!(session) + return session.scene +end + +function undo!(session::GraphEditorSession) + isempty(session.history) && return session.scene + push!(session.future, session.scene) + session.scene = pop!(session.history) + _persist_scene!(session) + return session.scene +end + +function redo!(session::GraphEditorSession) + isempty(session.future) && return session.scene + push!(session.history, session.scene) + session.scene = pop!(session.future) + _persist_scene!(session) + return session.scene +end + +_session_token(server) = string(hash((time_ns(), getpid(), objectid(server))); base=16) * + string(hash((objectid(server), time_ns(), getpid())); base=16) + +function _is_loopback_host(host) + return lowercase(strip(String(host))) in ( + "127.0.0.1", + "localhost", + "::1", + "[::1]", + "0:0:0:0:0:0:0:1", + ) +end + +_base_url(session) = "http://$(session.host):$(session.port)" +_state_url(session) = "$(_base_url(session))/state?token=$(session.token)" +_websocket_url(session) = "ws://$(session.host):$(session.port)/ws?token=$(session.token)" + +function _handle_http(session::GraphEditorSession, stream::HTTP.Stream) + request = stream.message + path = HTTP.URI(request.target).path + + if HTTP.WebSockets.isupgrade(request) + _authorized_request(session, request) || return _write_response(stream, 403, "text/plain", "Forbidden session token.") + _authorized_origin(session, request) || return _write_response(stream, 403, "text/plain", "Forbidden websocket origin.") + return HTTP.WebSockets.upgrade(stream) do websocket + _handle_websocket(session, websocket) + end + end + + if path == "/health" + return _write_response(stream, 200, "application/json", JSON.json(Dict("ok" => true))) + end + _authorized_request(session, request) || return _write_response(stream, 403, "text/plain", "Forbidden session token.") + if path == "/" || path == "/index.html" + return _write_response(stream, 200, "text/html; charset=utf-8", _editor_html(session)) + elseif path == "/state" + return _write_response(stream, 200, "application/json", _state_json(session)) + end + return _write_response(stream, 404, "text/plain", "Not found") +end + +function _write_response(stream, status, content_type, body) + HTTP.setstatus(stream, status) + HTTP.setheader(stream, "Content-Type" => content_type) + HTTP.setheader(stream, "Connection" => "close") + HTTP.setheader(stream, "Content-Length" => string(sizeof(body))) + HTTP.startwrite(stream) + write(stream, body) + return nothing +end + +function _authorized_request(session, request) + token = HTTP.header(request, "X-PlantSimEngine-Graph-Token", "") + isempty(token) && (token = something(_query_parameter(request.target, "token"), "")) + return token == session.token +end + +function _query_parameter(target, requested_name) + query = String(HTTP.URI(target).query) + isempty(query) && return nothing + for component in split(query, '&') + pair = split(component, '='; limit=2) + length(pair) == 2 || continue + first(pair) == requested_name && return last(pair) + end + return nothing +end + +function _authorized_origin(session, request) + origin = HTTP.header(request, "Origin", "") + return isempty(origin) || origin == _base_url(session) +end + +function _handle_websocket(session, websocket) + _send_websocket(websocket, _state_json(session)) || return nothing + try + for raw_message in websocket + command = JSON.parse(String(raw_message)) + response = _handle_command!(session, command) + _send_websocket(websocket, JSON.json(response)) || return nothing + end + catch err + _is_close_error(err) || _send_websocket( + websocket, + JSON.json(Dict("ok" => false, "diagnostics" => [sprint(showerror, err)])), + ) + end + return nothing +end + +function _send_websocket(websocket, payload) + try + HTTP.WebSockets.send(websocket, payload) + return true + catch err + _is_close_error(err) && return false + rethrow() + end +end + +_is_close_error(err) = err isa EOFError || err isa Base.IOError + +function _handle_command!(session, command) + action = String(get(command, "action", "")) + try + if action == "undo" + undo!(session) + elseif action == "redo" + redo!(session) + elseif action == "edit" + apply_edit!(session, _edit_from_command(session, command)) + elseif action == "save_scene_code" + session.save_path = _normalized_path(String(command["path"])) + _remember_path!(session, session.save_path) + _persist_scene!(session) + elseif action == "open_scene_code" + path = _normalized_path(String(command["path"])) + candidate = _load_scene_file(path; allow_julia_eval=session.allow_julia_eval) + push!(session.history, session.scene) + empty!(session.future) + session.scene = candidate + session.save_path = path + _remember_path!(session, path) + _persist_scene!(session) + elseif action in ("open_add_application", "begin_add_application") + # This command only focuses/prefills frontend state. The Scene is + # changed by a subsequent add_application edit. + else + error("Unsupported graph editor command action `$(action)`.") + end + return _state_payload(session) + catch err + return _state_payload(session; ok=false, diagnostics=[sprint(showerror, err)]) + end +end + +function _edit_from_command(session, command) + kind = String(get(command, "kind", "")) + application_id = Symbol(get(command, "applicationId", "")) + kind == "remove_application" && return PlantSimEngine.RemoveSceneApplication(application_id) + kind == "remove_template_application" && return PlantSimEngine.RemoveSceneTemplateApplication( + command["instance"], + application_id, + ) + kind == "mark_previous_timestep" && return PlantSimEngine.MarkScenePreviousTimeStep( + application_id, + Symbol(command["input"]), + ) + kind == "unmark_previous_timestep" && return PlantSimEngine.UnmarkScenePreviousTimeStep( + application_id, + Symbol(command["input"]), + ) + kind == "break_cycle" && return PlantSimEngine.BreakSceneCycle( + application_id, + Symbol(command["input"]), + Bool(get(command, "initializeMissing", false)), + _parameter_value(session, get(command, "initialValue", nothing)), + ) + kind == "set_application_targets" && return PlantSimEngine.SetSceneApplicationTargets( + application_id, + _selector_from_payload(command["selector"]), + ) + kind == "set_input_binding" && return PlantSimEngine.SetSceneInputBinding( + application_id, + Symbol(command["input"]), + _selector_from_payload(command["selector"]), + ) + kind == "remove_input_binding" && return PlantSimEngine.RemoveSceneInputBinding( + application_id, + Symbol(command["input"]), + ) + kind == "set_call_binding" && return PlantSimEngine.SetSceneCallBinding( + application_id, + Symbol(command["call"]), + _selector_from_payload(command["selector"]), + ) + kind == "remove_call_binding" && return PlantSimEngine.RemoveSceneCallBinding( + application_id, + Symbol(command["call"]), + ) + kind == "set_timestep" && return PlantSimEngine.SetSceneApplicationTimeStep( + application_id, + _timestep_from_payload(get(command, "timestep", nothing)), + ) + kind == "set_application_environment" && return PlantSimEngine.SetSceneApplicationEnvironment( + application_id, + _configuration_from_payload(session, get(command, "configuration", nothing)), + ) + kind == "set_output_routing" && return PlantSimEngine.SetSceneOutputRouting( + application_id, + Symbol(command["output"]), + Symbol(command["route"]), + ) + kind == "set_update_ordering" && return PlantSimEngine.SetSceneUpdateOrdering( + application_id, + _updates_from_payload(get(command, "updates", Any[])), + ) + kind == "set_object_status" && return PlantSimEngine.SetSceneObjectStatus( + command["objectId"], + Symbol(command["variable"]), + _parameter_value(session, command["value"]), + ) + kind == "set_object_statuses" && return PlantSimEngine.SetSceneObjectStatuses( + command["objectIds"], + Symbol(command["variable"]), + _parameter_value(session, command["value"]), + ) + kind == "remove_object_status" && return PlantSimEngine.RemoveSceneObjectStatus( + command["objectId"], + Symbol(command["variable"]), + ) + kind in ("set_object_metadata", "update_object") && return PlantSimEngine.SetSceneObjectMetadata( + PlantSimEngine.ObjectId(command["objectId"]), + _metadata_from_payload(get(command, "configuration", Dict())), + ) + kind == "add_object" && return PlantSimEngine.AddSceneObject( + _object_from_command(session, command), + ) + kind == "remove_object" && return PlantSimEngine.RemoveSceneObject( + command["objectId"]; + recursive=Bool(get(command, "recursive", true)), + ) + kind == "reparent_object" && return PlantSimEngine.ReparentSceneObject( + command["objectId"], + get(command, "parentId", nothing), + ) + kind == "set_instance_override" && return PlantSimEngine.SetSceneInstanceOverride( + command["instance"], + application_id, + _construct_model(session, command["modelType"], get(command, "parameters", Dict())), + ) + kind == "remove_instance_override" && return PlantSimEngine.RemoveSceneInstanceOverride( + command["instance"], + application_id, + ) + kind == "set_object_override" && return PlantSimEngine.SetSceneObjectOverride( + command["instance"], + command["objectId"], + application_id, + _construct_model(session, command["modelType"], get(command, "parameters", Dict())), + ) + kind == "remove_object_override" && return PlantSimEngine.RemoveSceneObjectOverride( + command["instance"], + command["objectId"], + application_id, + ) + kind == "add_application" && return _add_application_edit(session, command) + kind == "update_application" && return _update_application_edit(session, command) + kind == "update_template_application" && return PlantSimEngine.UpdateSceneTemplateApplication( + Symbol(command["instance"]), + application_id, + _construct_model(session, command["modelType"], get(command, "parameters", Dict())), + _selector_from_payload(command["selector"]), + _timestep_from_payload(get(command, "timestep", nothing)), + ) + kind == "replace_application_model" && return PlantSimEngine.ReplaceSceneApplicationModel( + application_id, + _construct_model(session, command["modelType"], get(command, "parameters", Dict())), + ) + error("Unsupported Scene graph edit kind `$(kind)`.") +end + +function _symbol_keyed_namedtuple(payload) + payload isa AbstractDict || error("Expected an object-valued configuration payload.") + return (; (Symbol(key) => value for (key, value) in payload)...) +end + +function _metadata_from_payload(payload) + payload isa AbstractDict || error("Expected an object metadata payload.") + return (; ( + Symbol(key) => (value isa AbstractString && isempty(strip(value)) ? nothing : value) + for (key, value) in payload + )...) +end + +function _configuration_from_payload(session, payload) + isnothing(payload) && return nothing + payload isa AbstractDict || return payload + values = Pair{Symbol,Any}[] + for (key, value) in payload + parsed = if value isa AbstractDict && haskey(value, "type") && haskey(value, "value") + _parameter_value(session, value) + elseif value isa AbstractDict + _configuration_from_payload(session, value) + elseif value isa AbstractVector + [_configuration_from_payload(session, item) for item in value] + else + value + end + push!(values, Symbol(key) => parsed) + end + return (; values...) +end + +function _updates_from_payload(payload) + payload isa AbstractVector || error("Update ordering must be an array.") + return Tuple( + PlantSimEngine.Updates( + Symbol.(get(item, "variables", Any[]))...; + after=Symbol.(get(item, "after", Any[])), + ) + for item in payload + ) +end + +function _object_from_command(session, command) + configuration = get(command, "configuration", Dict()) + status_payload = get(command, "status", Dict()) + status = if isempty(status_payload) + nothing + else + PlantSimEngine.Status((; ( + Symbol(name) => _parameter_value(session, value) + for (name, value) in status_payload + )...)) + end + return PlantSimEngine.Object( + command["objectId"]; + scale=get(configuration, "scale", nothing), + kind=get(configuration, "kind", nothing), + species=get(configuration, "species", nothing), + name=get(configuration, "name", nothing), + parent=get(configuration, "parent", nothing), + status=status, + ) +end + +function _update_application_edit(session, command) + application_id = Symbol(command["applicationId"]) + model = _construct_model(session, command["modelType"], get(command, "parameters", Dict())) + return PlantSimEngine.UpdateSceneApplication( + application_id, + model, + Symbol(get(command, "name", string(application_id))), + _selector_from_payload(command["selector"]), + _timestep_from_payload(get(command, "timestep", nothing)), + ) +end + +function _add_application_edit(session, command) + model = _construct_model(session, command["modelType"], get(command, "parameters", Dict())) + name = Symbol(command["name"]) + selector = _selector_from_payload(command["selector"]) + timestep = _timestep_from_payload(get(command, "timestep", nothing)) + spec = PlantSimEngine.ModelSpec( + model; + name=name, + applies_to=selector, + timestep=timestep, + ) + return PlantSimEngine.AddSceneApplication(spec) +end + +function _resolve_model_type(label) + text = String(label) + for model_type in PlantSimEngine.available_models() + text in (string(model_type), string(nameof(model_type))) && return model_type + end + error("No loaded model type matches `$(text)`. Load the defining package with `using PackageName` first.") +end + +function _construct_model(session, label, parameters) + model_type = _resolve_model_type(label) + descriptor = PlantSimEngine.model_constructor_descriptor(model_type) + fields = descriptor["fields"] + isempty(fields) && return model_type() + default_instance = try + model_type() + catch + nothing + end + values = Any[] + for field in fields + name = field["name"] + if haskey(parameters, name) + push!(values, _parameter_value(session, parameters[name])) + elseif !isnothing(default_instance) + push!(values, getfield(default_instance, Symbol(name))) + else + error("Missing constructor parameter `$(name)` for model `$(model_type)`.") + end + end + return model_type(values...) +end + +function _parameter_value(session, payload) + payload isa AbstractDict || return payload + choice = Symbol(get(payload, "type", "julia")) + raw = get(payload, "value", nothing) + choice == :float && return parse(Float64, string(raw)) + choice == :integer && return parse(Int, string(raw)) + choice == :boolean && return parse(Bool, string(raw)) + choice == :symbol && return Symbol(raw) + choice == :string && return String(raw) + choice == :nothing && return nothing + choice == :julia && session.allow_julia_eval || choice != :julia || error( + "Raw Julia parameter values are disabled for this session.", + ) + choice == :julia && return Core.eval(Main, Meta.parse(String(raw))) + return raw +end + +function _selector_from_payload(payload) + payload isa AbstractDict || error("A selector payload must be an object.") + multiplicity = Symbol(get(payload, "multiplicity", "many")) + criteria = get(payload, "criteria", Dict()) + selectors = PlantSimEngine.AbstractObjectSelector[ + _selector_atom_from_payload(value) + for value in get(criteria, "selectors", Any[]) + ] + keyword_pairs = Pair{Symbol,Any}[] + for (key, value) in criteria + key == "selectors" && continue + value === nothing && continue + push!(keyword_pairs, Symbol(key) => _selector_value(Symbol(key), value)) + end + keywords = (; keyword_pairs...) + multiplicity == :one && return PlantSimEngine.One(selectors...; keywords...) + multiplicity == :optional_one && return PlantSimEngine.OptionalOne(selectors...; keywords...) + multiplicity == :many && return PlantSimEngine.Many(selectors...; keywords...) + error("Unsupported selector multiplicity `$(multiplicity)`.") +end + +function _selector_value(key, value) + key in (:scale, :kind, :species, :name, :process, :var, :relation, :application) && + return value isa AbstractVector ? Symbol.(value) : Symbol(value) + key == :within && return _selector_atom_from_payload(value) + key == :policy && return _policy_from_payload(value) + return value +end + +function _selector_atom_from_payload(payload) + payload isa AbstractDict || error("A structured object selector must be an object.") + type = String(get(payload, "type", "")) + type == "SceneScope" && return PlantSimEngine.SceneScope() + type == "Self" && return PlantSimEngine.Self() + type == "Subtree" && return PlantSimEngine.Subtree() + type == "SelfPlant" && return PlantSimEngine.SelfPlant() + type == "Ancestor" && return PlantSimEngine.Ancestor(; scale=get(payload, "scale", nothing)) + type == "Scope" && return PlantSimEngine.Scope(payload["name"]) + type == "Kind" && return PlantSimEngine.Kind(payload["kind"]) + type == "Species" && return PlantSimEngine.Species(payload["species"]) + type == "Scale" && return PlantSimEngine.Scale(payload["scale"]) + type == "Relation" && return PlantSimEngine.Relation(payload["relation"]) + error("Unsupported structured object selector type `$(type)`.") +end + +function _policy_from_payload(payload) + payload isa AbstractDict || error("A temporal policy must be a structured object.") + type = String(get(payload, "type", "")) + type == "PreviousTimeStep" && return PlantSimEngine.PreviousTimeStep( + Symbol(payload["variable"]), + Symbol(get(payload, "process", "unknown")), + ) + type == "HoldLast" && return PlantSimEngine.HoldLast() + type == "Interpolate" && return PlantSimEngine.Interpolate( + ; + mode=Symbol(get(payload, "mode", "linear")), + extrapolation=Symbol(get(payload, "extrapolation", "linear")), + ) + type == "Integrate" && return PlantSimEngine.Integrate() + type == "Aggregate" && return PlantSimEngine.Aggregate() + error("Unsupported temporal policy type `$(type)`.") +end + +function _timestep_from_payload(payload) + isnothing(payload) && return nothing + payload isa AbstractDict || return payload + mode = String(get(payload, "mode", "default")) + mode == "default" && return nothing + mode == "clock" && return PlantSimEngine.ClockSpec( + parse(Float64, string(payload["dt"])), + parse(Float64, string(get(payload, "phase", 0.0))), + ) + error("Unsupported timestep mode `$(mode)`.") +end + +function _state_payload(session; ok=true, diagnostics=String[]) + graph = JSON.parse(PlantSimEngine.scene_graph_view_json(session.scene)) + return Dict{String,Any}( + "ok" => ok, + "diagnostics" => diagnostics, + "graph" => graph, + "canUndo" => !isempty(session.history), + "canRedo" => !isempty(session.future), + "url" => session.url, + "sceneCode" => _scene_to_julia(session.scene), + "autosavePath" => session.autosave_path, + "savePath" => session.save_path, + "recentPaths" => session.recent_paths, + ) +end + +function _remember_path!(session, path) + normalized = _normalized_path(path) + filter!(!=(normalized), session.recent_paths) + pushfirst!(session.recent_paths, normalized) + length(session.recent_paths) > 12 && resize!(session.recent_paths, 12) + return normalized +end + +function _load_scene_file(path; allow_julia_eval::Bool) + allow_julia_eval || error("Opening Julia Scene files is disabled for this editor session.") + isfile(path) || error("Scene file `$(path)` does not exist.") + module_name = Symbol("PlantSimEngineGraphRecovery_", string(time_ns(); base=16)) + workspace = Module(module_name) + Core.eval(workspace, :(using PlantSimEngine)) + Base.include(workspace, path) + isdefined(workspace, :scene) || error( + "Scene file `$(path)` must assign its final Scene to `scene`.", + ) + scene = getfield(workspace, :scene) + scene isa PlantSimEngine.Scene || error( + "Scene file `$(path)` assigned `scene` to `$(typeof(scene))`, expected `PlantSimEngine.Scene`.", + ) + return scene +end + +_state_json(session) = JSON.json(_state_payload(session)) + +function _editor_html(session) + view = PlantSimEngine.scene_graph_view(session.scene) + html = PlantSimEngine.scene_graph_view_html(view) + config = replace(JSON.json(Dict("websocketUrl" => _websocket_url(session))), " "<\\/") + script = "" + return replace(html, "" => "$(script)") +end + +function _scene_to_julia(scene) + io = IOBuffer() + diagnostics = String[] + modules = _scene_code_modules(scene) + for module_name in sort!(collect(modules)) + println(io, "using $(module_name)") + end + println(io) + if !isnothing(scene.source_adapter) + push!(diagnostics, "The Scene source_adapter is runtime-specific and is not reconstructed by generated code.") + end + for object in PlantSimEngine.scene_objects(scene) + isnothing(object.applications) || object.applications == () || push!( + diagnostics, + "Object $(repr(object.id.value)) has object-local applications that are not represented separately.", + ) + end + for diagnostic in unique(diagnostics) + println(io, "# WARNING: ", diagnostic) + end + isempty(diagnostics) || println(io) + println(io, "objects = (") + for object in PlantSimEngine.scene_objects(scene) + println(io, " ", _object_code(object), ",") + end + println(io, ")") + + templates = Any[] + for instance in scene.instances + any(template -> template === instance.template, templates) || push!(templates, instance.template) + end + for (index, template) in pairs(templates) + println(io) + println(io, "template_$(index) = ", _template_code(template)) + end + + if !isempty(scene.instances) + println(io) + println(io, "instances = (") + for instance in scene.instances + template_index = only(index for (index, template) in pairs(templates) if template === instance.template) + println(io, " ", _instance_code(instance, template_index), ",") + end + println(io, ")") + else + println(io) + println(io, "instances = ()") + end + + mounted_ids = Set{Symbol}() + for instance in scene.instances + union!(mounted_ids, PlantSimEngine._instance_application_ids(scene, instance)) + end + global_applications = [ + application for application in scene.applications + if PlantSimEngine._scene_edit_application_id(application) ∉ mounted_ids + ] + println(io, "applications = (") + for application in global_applications + println(io, " ", _application_code(PlantSimEngine.as_model_spec(application)), ",") + end + println(io, ")") + environment = isnothing(scene.environment) ? "nothing" : repr(scene.environment) + print(io, "scene = Scene(objects...; applications=applications, instances=instances, environment=$(environment))") + return String(take!(io)) +end + +function _scene_code_modules(scene) + modules = Set{String}(["PlantSimEngine"]) + add_model = function (model) + module_ = Base.moduleroot(parentmodule(typeof(model))) + module_ in (Base, Core, Main) || push!(modules, string(nameof(module_))) + end + for application in scene.applications + model = PlantSimEngine.model_(PlantSimEngine.as_model_spec(application)) + if model isa PlantSimEngine.ObjectModelOverrides + add_model(model.base) + foreach(add_model, values(model.overrides)) + else + add_model(model) + end + end + for instance in scene.instances + for application in instance.template.applications + add_model(PlantSimEngine.model_(PlantSimEngine.as_model_spec(application))) + end + foreach(add_model, values(instance.overrides)) + foreach(override -> add_model(override.model), instance.object_overrides) + end + return modules +end + +function _object_code(object) + keywords = String[ + "scale=$(repr(object.scale))", + "kind=$(repr(object.kind))", + "species=$(repr(object.species))", + "name=$(repr(object.name))", + "parent=$(isnothing(object.parent) ? "nothing" : repr(object.parent.value))", + ] + if object.status isa PlantSimEngine.Status + values = join(("$(name)=$(repr(object.status[name]))" for name in propertynames(object.status)), ", ") + push!(keywords, "status=Status(; $(values))") + end + isnothing(object.geometry) || push!(keywords, "geometry=$(repr(object.geometry))") + return "Object($(repr(object.id.value)); $(join(keywords, ", ")))" +end + +function _template_code(template) + applications = join( + (" " * _application_code(PlantSimEngine.as_model_spec(application)) * "," for application in template.applications), + "\n", + ) + return "ObjectTemplate((\n$(applications)\n ); kind=$(repr(template.kind)), species=$(repr(template.species)), parameters=$(repr(template.parameters)))" +end + +function _instance_code(instance, template_index) + overrides = if isempty(keys(instance.overrides)) + "NamedTuple()" + else + entries = join(("$(key)=$(repr(model))" for (key, model) in pairs(instance.overrides)), ", ") + "($(entries),)" + end + object_overrides = if isempty(instance.object_overrides) + "()" + else + entries = join((_object_override_code(override) for override in instance.object_overrides), ", ") + "($(entries),)" + end + return "ObjectInstance($(repr(instance.name)), template_$(template_index); root=$(repr(PlantSimEngine._instance_root_id(instance).value)), overrides=$(overrides), object_overrides=$(object_overrides))" +end + +function _object_override_code(override) + options = String["object=$(repr(override.object.value))"] + isnothing(override.process) || push!(options, "process=$(repr(override.process))") + isnothing(override.application) || push!(options, "application=$(repr(override.application))") + push!(options, "model=$(repr(override.model))") + return "Override(; $(join(options, ", ")))" +end + +function _application_code(spec) + code = "ModelSpec($(repr(PlantSimEngine.model_(spec))); name=$(repr(PlantSimEngine.application_name(spec))))" + selector = PlantSimEngine.applies_to(spec) + isnothing(selector) || (code *= " |> AppliesTo($(repr(selector)))") + isempty(keys(PlantSimEngine.value_inputs(spec))) || + (code *= " |> Inputs($(repr(PlantSimEngine.value_inputs(spec))))") + isempty(keys(PlantSimEngine.model_calls(spec))) || + (code *= " |> Calls($(repr(PlantSimEngine.model_calls(spec))))") + isnothing(spec.timestep) || (code *= " |> TimeStep($(repr(spec.timestep)))") + return code +end + +function _persist_scene!(session) + code = _scene_to_julia(session.scene) * "\n" + isnothing(session.autosave_path) || _atomic_write(session.autosave_path, code) + isnothing(session.save_path) || _atomic_write(session.save_path, code) + return nothing +end + +function _atomic_write(path, content) + path = _normalized_path(path) + mkpath(dirname(path)) + temporary = tempname(dirname(path)) + try + write(temporary, content) + mv(temporary, path; force=true) + finally + isfile(temporary) && rm(temporary; force=true) + end + return path +end + +_normalized_path(path) = isabspath(String(path)) ? normpath(String(path)) : normpath(joinpath(pwd(), String(path))) + +function _default_autosave_path() + return joinpath( + tempdir(), + "PlantSimEngineGraphEditor", + string("session-", time_ns()), + "scene.autosave.jl", + ) +end + +function _open_in_default_browser(url) + try + if Sys.isapple() + run(`open $url`) + elseif Sys.iswindows() + run(`cmd /c start "" $url`) + elseif !isnothing(Sys.which("xdg-open")) + run(`xdg-open $url`) + else + @warn "Could not locate a default-browser command." url + return false + end + return true + catch err + @warn "Could not open the graph editor automatically." url exception=(err, catch_backtrace()) + return false + end +end + +end diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 000000000..c31b3100a --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,24 @@ +# PlantSimEngine Dependency Graph Viewer + +This is the React Flow frontend for the PlantSimEngine dependency graph viewer. +It consumes the JSON emitted by `PlantSimEngine.graph_view_json`. + +## Development + +```sh +npm install +npm run dev +``` + +The app falls back to a small sample graph when no embedded +` + + + +
+ + diff --git a/frontend/e2e/graph-editor.spec.ts b/frontend/e2e/graph-editor.spec.ts new file mode 100644 index 000000000..ff6fd7ed5 --- /dev/null +++ b/frontend/e2e/graph-editor.spec.ts @@ -0,0 +1,155 @@ +import { expect, test, type APIRequestContext, type Locator, type Page } from "@playwright/test"; +import type { ApplicationGraphNode, EditorState } from "../src/types"; +import { startGraphEditorServer, type GraphEditorServer } from "./graphEditorServer"; + +test.describe.serial("PlantSimEngine Scene graph editor", () => { + let server: GraphEditorServer; + + test.beforeAll(async () => { + server = await startGraphEditorServer(); + }); + + test.afterAll(async () => { + await server?.stop(); + }); + + test("starts empty and adds an object", async ({ page, request }) => { + await page.goto(server.url); + await expect(page.getByText("Scene Graph")).toBeVisible(); + + let state = await getState(request, server.url); + expect(state.ok).toBe(true); + expect(state.graph.metadata.objectCount).toBe(0); + expect(state.graph.metadata.applicationCount).toBe(0); + + await page.getByTestId("add-object").click(); + await page.getByTestId("object-id").fill("leaf"); + await page.locator(".object-form label", { hasText: "Scale" }).getByRole("textbox").fill("Leaf"); + await page.locator(".object-form label", { hasText: "Kind" }).getByRole("textbox").fill("organ"); + await page.locator(".object-form label", { hasText: "Name" }).getByRole("textbox").fill("leaf"); + await page.getByTestId("object-submit").click(); + + state = await waitForState(request, server.url, (value) => value.graph.metadata.objectCount === 1); + expect(state.graph.objects[0].scale).toBe("Leaf"); + }); + + test("adds and updates an application", async ({ page, request }) => { + await page.goto(server.url); + await openAddApplication(page, "Beer"); + await page.getByTestId("application-name").fill("light"); + await page.getByTestId("application-submit").click(); + + let state = await waitForState(request, server.url, (value) => value.graph.metadata.applicationCount === 1); + let light = findApplication(state, "light"); + expect(light.modelName).toContain("Beer"); + + await page.getByTestId("application-node-light").click(); + await page.getByRole("button", { name: "Edit application" }).click(); + await page.getByTestId("application-param-k").fill("0.8"); + await page.getByTestId("application-submit").click(); + + state = await waitForState(request, server.url, (value) => findApplication(value, "light").modelParameters.k?.value === 0.8); + light = findApplication(state, "light"); + expect(light.targetIds).toEqual(["leaf"]); + }); + + test("creates and breaks a cycle directly in the graph", async ({ page, request }) => { + await page.goto(server.url); + await openAddApplication(page, "ReebE2E"); + await page.getByTestId("application-name").fill("reeb"); + await page.getByTestId("application-submit").click(); + + let state = await waitForState(request, server.url, (value) => value.graph.metadata.cyclic === true); + expect(state.graph.edges.filter((edge) => edge.cycle)).toHaveLength(2); + await expect(page.getByTestId("cycle-callout")).toBeVisible(); + + await page.getByTestId("choose-cycle-break").click(); + const scissors = page.locator("[data-testid^='cycle-break-']").first(); + await expect(scissors).toBeVisible(); + await scissors.click(); + await expect(page.getByTestId("cycle-break-dialog")).toBeVisible(); + const initialization = page.getByTestId("cycle-break-dialog").getByRole("textbox"); + if (await initialization.count()) await initialization.fill("0.0"); + await page.getByTestId("confirm-cycle-break").click(); + + state = await waitForState(request, server.url, (value) => value.graph.metadata.cyclic === false); + expect(state.graph.edges.some((edge) => edge.kind === "previous_timestep")).toBe(true); + expect(state.sceneCode).toContain("PreviousTimeStep"); + }); + + test("connects another consumer and supports undo, redo, remove, and save", async ({ page, request }, testInfo) => { + await page.goto(server.url); + await openAddApplication(page, "E2EConsumer"); + await page.getByTestId("application-name").fill("consumer"); + await page.getByTestId("application-submit").click(); + await waitForState(request, server.url, (value) => value.graph.applications.some((application) => application.applicationId === "consumer")); + + await page.getByTestId("port-output-aPPFD").first().getByRole("button").click(); + await page.locator(".candidate-card.existing", { hasText: "consumer" }).click(); + await expect(page.getByTestId("binding-form")).toBeVisible(); + await page.getByTestId("binding-submit").click(); + let state = await waitForState(request, server.url, (value) => value.graph.edges.some((edge) => edge.targetApplicationId === "consumer" && edge.targetVariable === "aPPFD")); + expect(state.graph.metadata.applicationCount).toBe(3); + + await page.getByTestId("application-node-consumer").click(); + await page.getByRole("button", { name: "Remove application" }).click(); + await waitForState(request, server.url, (value) => !value.graph.applications.some((application) => application.applicationId === "consumer")); + await page.getByRole("button", { name: "Undo" }).click(); + await waitForState(request, server.url, (value) => value.graph.applications.some((application) => application.applicationId === "consumer")); + await page.getByRole("button", { name: "Redo" }).click(); + await waitForState(request, server.url, (value) => !value.graph.applications.some((application) => application.applicationId === "consumer")); + + const savePath = testInfo.outputPath("scene.jl"); + await page.getByTestId("save-scene").click(); + await page.getByPlaceholder("/absolute/path/to/scene.jl").fill(savePath); + await page.getByRole("button", { name: "Save", exact: true }).click(); + state = await waitForState(request, server.url, (value) => value.savePath === savePath); + expect(state.recentPaths).toContain(savePath); + }); +}); + +async function openAddApplication(page: Page, modelName: string) { + await page.getByTestId("add-application").click(); + await selectOptionContaining(page.getByTestId("application-model-select"), modelName); +} + +async function getState(request: APIRequestContext, baseURL: string): Promise { + const response = await request.get(stateURL(baseURL)); + expect(response.ok()).toBe(true); + return await response.json() as EditorState; +} + +function stateURL(baseURL: string): string { + const url = new URL(baseURL); + const token = url.searchParams.get("token"); + url.pathname = "/state"; + url.search = ""; + if (token) url.searchParams.set("token", token); + return url.toString(); +} + +async function waitForState(request: APIRequestContext, baseURL: string, predicate: (state: EditorState) => boolean, timeoutMs = 20_000): Promise { + const deadline = Date.now() + timeoutMs; + let latest = await getState(request, baseURL); + while (Date.now() < deadline) { + if (predicate(latest)) return latest; + await new Promise((resolve) => setTimeout(resolve, 200)); + latest = await getState(request, baseURL); + } + throw new Error(`Timed out waiting for editor state:\n${JSON.stringify(latest, null, 2)}`); +} + +function findApplication(state: EditorState, id: string): ApplicationGraphNode { + const application = state.graph.applications.find((item) => item.applicationId === id); + expect(application, `Expected application ${id}`).toBeTruthy(); + return application!; +} + +async function selectOptionContaining(select: Locator, text: string) { + const value = await select.evaluate((element, needle) => { + const selectElement = element as HTMLSelectElement; + return [...selectElement.options].find((option) => option.textContent?.includes(needle))?.value ?? null; + }, text); + expect(value, `Expected option containing ${text}`).toBeTruthy(); + await select.selectOption(value!); +} diff --git a/frontend/e2e/graphEditorServer.ts b/frontend/e2e/graphEditorServer.ts new file mode 100644 index 000000000..9fcbc708c --- /dev/null +++ b/frontend/e2e/graphEditorServer.ts @@ -0,0 +1,82 @@ +import { spawn, type ChildProcessWithoutNullStreams } from "node:child_process"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const dirname = path.dirname(fileURLToPath(import.meta.url)); +const repoRoot = path.resolve(dirname, "../.."); +const serverScript = path.join(repoRoot, "test", "fixtures", "graph_editor_e2e_server.jl"); + +export type GraphEditorServer = { + url: string; + stop: () => Promise; +}; + +export async function startGraphEditorServer(): Promise { + if (process.env.PSE_GRAPH_EDITOR_URL) { + return { + url: process.env.PSE_GRAPH_EDITOR_URL, + stop: async () => {}, + }; + } + + const proc = spawn("julia", ["--project=test", "--startup-file=no", serverScript], { + cwd: repoRoot, + env: { ...process.env, JULIA_NUM_THREADS: process.env.JULIA_NUM_THREADS ?? "2" }, + }); + + let log = ""; + const url = await new Promise((resolve, reject) => { + let settled = false; + let timeout: ReturnType; + + const consume = (chunk: Buffer) => { + if (settled) return; + log += chunk.toString(); + const match = log.match(/PSE_GRAPH_EDITOR_URL=(http:\/\/127\.0\.0\.1:\d+[^\s]*)/); + if (match) { + settled = true; + clearTimeout(timeout); + resolve(match[1]); + } + }; + + const fail = (error: Error) => { + if (settled) return; + settled = true; + clearTimeout(timeout); + void stopProcess(proc).finally(() => reject(error)); + }; + + timeout = setTimeout(() => { + fail(new Error(`Timed out waiting for graph editor URL.\n${log}`)); + }, 90_000); + + proc.stdout.on("data", consume); + proc.stderr.on("data", consume); + proc.once("exit", (code, signal) => { + fail(new Error(`Graph editor process exited before startup: code=${code} signal=${signal}\n${log}`)); + }); + proc.once("error", (error) => { + fail(error); + }); + }); + + return { + url, + stop: () => stopProcess(proc), + }; +} + +function stopProcess(proc: ChildProcessWithoutNullStreams): Promise { + if (proc.killed || proc.exitCode !== null) return Promise.resolve(); + return new Promise((resolve) => { + const killTimer = setTimeout(() => { + if (!proc.killed && proc.exitCode === null) proc.kill("SIGKILL"); + }, 3_000); + proc.once("exit", () => { + clearTimeout(killTimer); + resolve(); + }); + proc.kill("SIGTERM"); + }); +} diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 000000000..0159e8aff --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + PlantSimEngine Dependency Graph + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 000000000..5fbe30597 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,2618 @@ +{ + "name": "plantsimengine-graph-viewer", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "plantsimengine-graph-viewer", + "version": "0.1.0", + "dependencies": { + "@xyflow/react": "^12.10.0", + "elkjs": "^0.11.0", + "lucide-react": "^0.468.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "zustand": "^5.0.0" + }, + "devDependencies": { + "@playwright/test": "^1.60.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.0.0", + "typescript": "^5.8.0", + "vite": "^7.0.0", + "vitest": "^3.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@playwright/test": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.60.0.tgz", + "integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", + "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", + "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", + "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", + "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", + "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", + "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", + "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", + "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", + "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", + "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", + "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", + "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", + "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", + "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", + "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", + "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", + "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", + "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", + "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", + "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", + "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", + "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", + "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", + "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", + "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@xyflow/react": { + "version": "12.10.2", + "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.10.2.tgz", + "integrity": "sha512-CgIi6HwlcHXwlkTpr0fxLv/0sRVNZ8IdwKLzzeCscaYBwpvfcH1QFOCeaTCuEn1FQEs/B8CjnTSjhs8udgmBgQ==", + "license": "MIT", + "dependencies": { + "@xyflow/system": "0.0.76", + "classcat": "^5.0.3", + "zustand": "^4.4.0" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@xyflow/react/node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@xyflow/system": { + "version": "0.0.76", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.76.tgz", + "integrity": "sha512-hvwvnRS1B3REwVDlWexsq7YQaPZeG3/mKo1jv38UmnpWmxihp14bW6VtEOuHEwJX2FvzFw8k77LyKSk/wiZVNA==", + "license": "MIT", + "dependencies": { + "@types/d3-drag": "^3.0.7", + "@types/d3-interpolate": "^3.0.4", + "@types/d3-selection": "^3.0.10", + "@types/d3-transition": "^3.0.8", + "@types/d3-zoom": "^3.0.8", + "d3-drag": "^3.0.0", + "d3-interpolate": "^3.0.1", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.23", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.23.tgz", + "integrity": "sha512-xwVXGqevyKPsiuQdLj+dZMVjidjJV508TBqexND5HrF89cGdCYCJFB3qhcxRHSeMctdCfbR1jrxBajhDy7o29g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001791", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz", + "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.344", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.344.tgz", + "integrity": "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==", + "dev": true, + "license": "ISC" + }, + "node_modules/elkjs": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.11.1.tgz", + "integrity": "sha512-zxxR9k+rx5ktMwT/FwyLdPCrq7xN6e4VGGHH8hA01vVYKjTFik7nHOxBnAYtrgYUB1RpAiLvA1/U2YraWxyKKg==", + "license": "EPL-2.0" + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.468.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz", + "integrity": "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", + "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", + "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/postcss": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", + "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", + "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", + "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.5" + } + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", + "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.2", + "@rollup/rollup-android-arm64": "4.60.2", + "@rollup/rollup-darwin-arm64": "4.60.2", + "@rollup/rollup-darwin-x64": "4.60.2", + "@rollup/rollup-freebsd-arm64": "4.60.2", + "@rollup/rollup-freebsd-x64": "4.60.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", + "@rollup/rollup-linux-arm-musleabihf": "4.60.2", + "@rollup/rollup-linux-arm64-gnu": "4.60.2", + "@rollup/rollup-linux-arm64-musl": "4.60.2", + "@rollup/rollup-linux-loong64-gnu": "4.60.2", + "@rollup/rollup-linux-loong64-musl": "4.60.2", + "@rollup/rollup-linux-ppc64-gnu": "4.60.2", + "@rollup/rollup-linux-ppc64-musl": "4.60.2", + "@rollup/rollup-linux-riscv64-gnu": "4.60.2", + "@rollup/rollup-linux-riscv64-musl": "4.60.2", + "@rollup/rollup-linux-s390x-gnu": "4.60.2", + "@rollup/rollup-linux-x64-gnu": "4.60.2", + "@rollup/rollup-linux-x64-musl": "4.60.2", + "@rollup/rollup-openbsd-x64": "4.60.2", + "@rollup/rollup-openharmony-arm64": "4.60.2", + "@rollup/rollup-win32-arm64-msvc": "4.60.2", + "@rollup/rollup-win32-ia32-msvc": "4.60.2", + "@rollup/rollup-win32-x64-gnu": "4.60.2", + "@rollup/rollup-win32-x64-msvc": "4.60.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/vite": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", + "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/zustand": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz", + "integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 000000000..c715dd081 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,33 @@ +{ + "name": "plantsimengine-graph-viewer", + "private": true, + "version": "0.1.0", + "type": "module", + "packageManager": "npm@11.6.1", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "test": "vitest run --passWithNoTests --exclude \"e2e/**\"", + "typecheck": "tsc --noEmit", + "test:e2e": "playwright test", + "test:e2e:ui": "playwright test --headed --debug" + }, + "dependencies": { + "@xyflow/react": "^12.10.0", + "elkjs": "^0.11.0", + "lucide-react": "^0.468.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "zustand": "^5.0.0" + }, + "devDependencies": { + "@playwright/test": "^1.60.0", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.0.0", + "typescript": "^5.8.0", + "vite": "^7.0.0", + "vitest": "^3.0.0" + } +} diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts new file mode 100644 index 000000000..532fc1152 --- /dev/null +++ b/frontend/playwright.config.ts @@ -0,0 +1,24 @@ +import { defineConfig, devices } from "@playwright/test"; + +export default defineConfig({ + testDir: "./e2e", + fullyParallel: false, + workers: 1, + timeout: 60_000, + expect: { + timeout: 10_000, + }, + reporter: process.env.CI ? [["dot"], ["html", { open: "never" }]] : "list", + use: { + baseURL: process.env.PSE_GRAPH_EDITOR_URL ?? "http://127.0.0.1:8765", + trace: "retain-on-failure", + screenshot: "only-on-failure", + video: "retain-on-failure", + }, + projects: [ + { + name: "chromium", + use: { ...devices["Desktop Chrome"] }, + }, + ], +}); diff --git a/frontend/src/App.test.ts b/frontend/src/App.test.ts new file mode 100644 index 000000000..f2207c616 --- /dev/null +++ b/frontend/src/App.test.ts @@ -0,0 +1,61 @@ +import { describe, expect, it } from "vitest"; +import { applicationPortId, applicationsForPort, deriveCandidatePortIds, endpointsForCandidate, modelsForPort, selectorSuggestion } from "./App"; +import type { ApplicationGraphNode, GraphPort, ModelDescriptor, SceneGraphView } from "./types"; + +const output: GraphPort = { id: applicationPortId("source", "output", "signal"), name: "signal", role: "output", default: 0, defaultJulia: "0", expectedType: "Int" }; +const input: GraphPort = { id: applicationPortId("consumer", "input", "signal"), name: "signal", role: "input", default: 0, defaultJulia: "0", expectedType: "Int" }; + +const source = application("source", [], [output], ["leaf"]); +const consumer = application("consumer", [input], [], ["leaf"]); + +describe("candidate composition", () => { + it("keeps plus controls for exact existing-application matches", () => { + const graph = graphView([source, consumer], []); + const ids = deriveCandidatePortIds(graph); + expect(ids.has(output.id)).toBe(true); + expect(ids.has(input.id)).toBe(true); + }); + + it("matches model descriptors by exact declared variable name", () => { + const exact = model("Exact", { signal: 0 }, {}); + const near = model("Near", { Signal: 0 }, {}); + expect(modelsForPort([near, exact], output).map((item) => item.name)).toEqual(["Exact"]); + }); + + it("separates existing applications and produces directed binding endpoints", () => { + const candidate = { application: source, port: output, x: 0, y: 0 }; + expect(applicationsForPort([source, consumer], candidate)).toEqual([consumer]); + const endpoints = endpointsForCandidate(candidate, consumer); + expect(endpoints.sourceApplication.applicationId).toBe("source"); + expect(endpoints.targetApplication.applicationId).toBe("consumer"); + expect(endpoints.targetPort.name).toBe("signal"); + }); +}); + +describe("selector suggestions", () => { + it("suggests a conservative target selector from one application", () => { + const suggestion = selectorSuggestion(source); + expect(suggestion.multiplicity).toBe("one"); + expect(suggestion.criteria.scale).toBe("Leaf"); + }); +}); + +function application(id: string, inputs: GraphPort[], outputs: GraphPort[], targetIds: unknown[]): ApplicationGraphNode { + return { + id: `application:${id}`, applicationId: id, name: id, process: id, modelType: id, modelName: id, module: "Main", package: null, + modelParameters: {}, selector: { type: "One", multiplicity: "one", criteria: { selectors: [], scale: "Leaf" }, julia: "" }, + targetIds, targetCount: targetIds.length, targetScales: ["Leaf"], targetKinds: [], targetSpecies: [], targetInstances: [], timestep: null, clock: null, + inputs, outputs, environmentInputs: [], environmentOutputs: [], modelStorage: "shared_application", objectOverrides: [], + }; +} + +function model(name: string, inputs: Record, outputs: Record): ModelDescriptor { + return { type: name, name, module: "Main", package: null, process: name, processType: name, inputs, outputs, environmentInputs: {}, environmentOutputs: {}, constructor: { fields: [], parameterGroups: {}, hasZeroArgConstructor: true, constructible: true } }; +} + +function graphView(applications: ApplicationGraphNode[], modelLibrary: ModelDescriptor[]): SceneGraphView { + return { + schemaVersion: 1, level: "applications", metadata: { title: "", sceneRevision: 0, objectCount: 1, instanceCount: 0, applicationCount: applications.length, executionCount: applications.length, bindingCount: 0, callCount: 0, unresolvedInitializationCount: 0, cyclic: false, strictlyCompiled: true }, + objects: [], instances: [], applications, executions: [], edges: [], modelLibrary, initialization: [], diagnostics: [], cycles: [], availableActions: [], + }; +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 000000000..20ef56a85 --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,807 @@ +import { useCallback, useEffect, useMemo, useState } from "react"; +import { + Background, + Controls, + MarkerType, + MiniMap, + ReactFlow, + useEdgesState, + useNodesState, + type Connection, + type Edge, + type Node, +} from "@xyflow/react"; +import "@xyflow/react/dist/style.css"; +import { + AlertTriangle, + Boxes, + CircleAlert, + Code2, + GitBranch, + FolderOpen, + Layers3, + Network, + Plus, + Scissors, + Search, + Save, + Undo2, + Redo2, + X, +} from "lucide-react"; +import { ApplicationForm, type ApplicationFormValue } from "./ApplicationForm"; +import { BindingForm, type BindingEndpoints, type BindingFormValue } from "./BindingForm"; +import { ObjectForm, type ObjectFormValue } from "./ObjectForm"; +import { OverrideForm, type OverrideFormValue } from "./OverrideForm"; +import { ApplicationNode, EntityNode } from "./ModelNode"; +import { DependencyEdge } from "./DependencyEdge"; +import { layoutGraph, type LayoutMode } from "./layout"; +import { sampleGraph } from "./sampleGraph"; +import type { + ApplicationGraphNode, + DetailMode, + EditorState, + ExecutionGraphNode, + GraphPort, + GraphViewMode, + ModelDescriptor, + ObjectGraphNode, + RuntimeApplicationNode, + RuntimeEntityNode, + SceneGraphEdge, + SceneGraphView, +} from "./types"; +import "./styles.css"; + +type FlowNode = Node; +type FlowEdge = Edge; +type CandidatePopover = { port: GraphPort; application: ApplicationGraphNode; x: number; y: number }; +type CycleBreakSelection = { application: ApplicationGraphNode; port: GraphPort }; +type InspectorSelection = ApplicationGraphNode | ObjectGraphNode | ExecutionGraphNode | SceneGraphEdge | null; +type ApplicationFormState = { + mode: "add" | "update"; + scope?: "application" | "template"; + instance?: string; + application?: ApplicationGraphNode; + initialModelType?: string; + suggestedSelector?: ApplicationGraphNode["selector"]; +}; +type ObjectFormState = { mode: "add" | "update"; object?: ObjectGraphNode }; + +const nodeTypes = { application: ApplicationNode, entity: EntityNode }; +const edgeTypes = { sceneEdge: DependencyEdge }; + +export default function App() { + const [graph, setGraph] = useState(loadInitialGraph); + const [view, setView] = useState(() => loadInitialGraph().level); + const [detailMode, setDetailMode] = useState(() => loadInitialGraph().metadata.applicationCount > 24 ? "overview" : "detail"); + const [query, setQuery] = useState(""); + const [selected, setSelected] = useState(null); + const [selectedPort, setSelectedPort] = useState(null); + const [candidate, setCandidate] = useState(null); + const [showDiagnostics, setShowDiagnostics] = useState(false); + const [showInitialization, setShowInitialization] = useState(false); + const [showSceneCode, setShowSceneCode] = useState(false); + const [showOpen, setShowOpen] = useState(false); + const [showSave, setShowSave] = useState(false); + const [sceneCode, setSceneCode] = useState(""); + const [autosavePath, setAutosavePath] = useState(null); + const [savePath, setSavePath] = useState(null); + const [recentPaths, setRecentPaths] = useState([]); + const [socket, setSocket] = useState(null); + const [connected, setConnected] = useState(false); + const [canUndo, setCanUndo] = useState(false); + const [canRedo, setCanRedo] = useState(false); + const [feedback, setFeedback] = useState(null); + const [applicationForm, setApplicationForm] = useState(null); + const [objectForm, setObjectForm] = useState(null); + const [overrideApplication, setOverrideApplication] = useState(null); + const [bindingForm, setBindingForm] = useState(null); + const [cycleBreakMode, setCycleBreakMode] = useState(false); + const [cycleBreakSelection, setCycleBreakSelection] = useState(null); + const [nodes, setNodes, onNodesChange] = useNodesState([]); + const [edges, setEdges, onEdgesChange] = useEdgesState([]); + + const editorConfig = useMemo(loadEditorConfig, []); + const applicationById = useMemo(() => new Map(graph.applications.map((application) => [application.applicationId, application])), [graph.applications]); + const unresolvedPortIds = useMemo(() => new Set( + graph.initialization + .filter((row) => row.role === "input" && row.disposition === "unresolved") + .map((row) => applicationPortId(row.applicationId, "input", row.variable)), + ), [graph.initialization]); + const previousPortIds = useMemo(() => new Set( + graph.initialization + .filter((row) => row.role === "input" && row.previousTimeStep) + .map((row) => applicationPortId(row.applicationId, "input", row.variable)), + ), [graph.initialization]); + const cyclicApplications = useMemo(() => new Set(graph.cycles.flatMap((cycle) => cycle.applicationIds)), [graph.cycles]); + const cycleBreakPortIds = useMemo(() => new Set( + graph.cycles.flatMap((cycle) => cycle.breakCandidates.map((candidate) => applicationPortId(candidate.applicationId, "input", candidate.input))), + ), [graph.cycles]); + const candidatePortIds = useMemo(() => deriveCandidatePortIds(graph), [graph]); + const candidateModels = useMemo(() => candidate ? modelsForPort(graph.modelLibrary, candidate.port) : [], [candidate, graph.modelLibrary]); + const candidateApplications = useMemo(() => candidate ? applicationsForPort(graph.applications, candidate) : [], [candidate, graph.applications]); + const portIndex = useMemo(() => { + const index = new Map(); + for (const application of graph.applications) { + for (const port of [...application.inputs, ...application.outputs]) index.set(port.id, { application, port }); + } + return index; + }, [graph.applications]); + + useEffect(() => { + if (!editorConfig?.websocketUrl) return; + const nextSocket = new WebSocket(editorConfig.websocketUrl); + setSocket(nextSocket); + nextSocket.addEventListener("open", () => { setConnected(true); setFeedback(null); }); + nextSocket.addEventListener("close", () => { setConnected(false); setFeedback("Editor connection closed."); }); + nextSocket.addEventListener("message", (event) => { + const payload = JSON.parse(event.data) as EditorState; + if (payload.graph) setGraph(payload.graph); + if (typeof payload.sceneCode === "string") setSceneCode(payload.sceneCode); + setAutosavePath(payload.autosavePath ?? null); + setSavePath(payload.savePath ?? null); + setRecentPaths(payload.recentPaths ?? []); + setCanUndo(Boolean(payload.canUndo)); + setCanRedo(Boolean(payload.canRedo)); + setFeedback(payload.ok === false ? payload.diagnostics?.[0] || "The edit failed." : null); + }); + return () => nextSocket.close(); + }, [editorConfig?.websocketUrl]); + + useEffect(() => { + if (!graph.metadata.cyclic) { + setCycleBreakMode(false); + setCycleBreakSelection(null); + } + }, [graph.metadata.cyclic]); + + const sendCommand = useCallback((command: Record) => { + if (!socket || socket.readyState !== WebSocket.OPEN) { + setFeedback("This action requires an interactive Julia editor session."); + return; + } + socket.send(JSON.stringify(command)); + }, [socket]); + + const openCandidates = useCallback((application: ApplicationGraphNode, port: GraphPort, anchor: { x: number; y: number }) => { + setSelected(application); + setSelectedPort(port); + setCandidate({ application, port, x: anchor.x, y: anchor.y }); + }, []); + + useEffect(() => { + const nextNodes = buildNodes({ + graph, + view, + detailMode, + query, + unresolvedPortIds, + previousPortIds, + candidatePortIds, + cyclicApplications, + cycleBreakPortIds, + cycleBreakMode, + openCandidates, + onPortClick: setSelectedPort, + onCycleBreak: (application, port) => setCycleBreakSelection({ application, port }), + }); + const nodeIds = new Set(nextNodes.map((node) => node.id)); + const nextEdges = buildEdges(graph, view).filter((edge) => nodeIds.has(edge.source) && nodeIds.has(edge.target)); + const layoutMode: LayoutMode = view === "topology" ? "topology" : detailMode === "overview" ? "overview" : "data_flow"; + layoutGraph(nextNodes, nextEdges, layoutMode).then(setNodes); + setEdges(nextEdges); + }, [candidatePortIds, cycleBreakMode, cycleBreakPortIds, cyclicApplications, detailMode, graph, openCandidates, previousPortIds, query, setEdges, setNodes, unresolvedPortIds, view]); + + const inspectSelection = useCallback((_: unknown, node: FlowNode) => { + if (node.data.nodeKind === "application") { + setSelected(applicationById.get(node.data.applicationId) ?? null); + } else { + setSelected(node.data.detail); + } + setSelectedPort(null); + }, [applicationById]); + + const selectCandidateModel = useCallback((model: ModelDescriptor) => { + if (!candidate) return; + setApplicationForm({ + mode: "add", + initialModelType: model.type, + suggestedSelector: selectorSuggestion(candidate.application), + }); + if (!connected) setFeedback(`${model.name} matches ${candidate.port.name}. Start an interactive Julia editor session to add it to the Scene.`); + setCandidate(null); + }, [candidate, connected]); + + const submitApplication = useCallback((value: ApplicationFormValue) => { + if (!connected) { + setFeedback("Adding or updating an application requires an interactive Julia editor session."); + setApplicationForm(null); + return; + } + sendCommand({ + action: "edit", + kind: value.applicationId ? applicationForm?.scope === "template" ? "update_template_application" : "update_application" : "add_application", + instance: applicationForm?.instance, + ...value, + }); + setApplicationForm(null); + }, [applicationForm?.instance, applicationForm?.scope, connected, sendCommand]); + + const submitBinding = useCallback((value: BindingFormValue) => { + if (!connected) { + setFeedback("Creating a binding requires an interactive Julia editor session."); + setBindingForm(null); + return; + } + sendCommand({ action: "edit", kind: "set_input_binding", ...value }); + setBindingForm(null); + }, [connected, sendCommand]); + + const submitObject = useCallback((value: ObjectFormValue) => { + if (!connected) { + setFeedback("Adding or updating an object requires an interactive Julia editor session."); + setObjectForm(null); + return; + } + sendCommand({ + action: "edit", + kind: objectForm?.mode === "update" ? "update_object" : "add_object", + objectId: value.objectId, + configuration: value.configuration, + }); + setObjectForm(null); + }, [connected, objectForm?.mode, sendCommand]); + + const submitOverride = useCallback((value: OverrideFormValue) => { + if (!connected) { + setFeedback("Creating an override requires an interactive Julia editor session."); + setOverrideApplication(null); + return; + } + sendCommand({ + action: "edit", + kind: value.scope === "instance" ? "set_instance_override" : "set_object_override", + ...value, + }); + setOverrideApplication(null); + }, [connected, sendCommand]); + + const connectPorts = useCallback((connection: Connection) => { + if (!connection.sourceHandle || !connection.targetHandle) return; + const source = portIndex.get(connection.sourceHandle); + const target = portIndex.get(connection.targetHandle); + if (!source || !target || source.port.role !== "output" || target.port.role !== "input") { + setFeedback("Connect an application output to an application input."); + return; + } + setBindingForm({ + sourceApplication: source.application, + sourcePort: source.port, + targetApplication: target.application, + targetPort: target.port, + }); + }, [portIndex]); + + const activeInitialization = useMemo(() => { + if (!selected) return graph.initialization; + if ("applicationId" in selected) return graph.initialization.filter((row) => row.applicationId === selected.applicationId); + if ("objectId" in selected) return graph.initialization.filter((row) => String(row.objectId) === String(selected.objectId)); + return graph.initialization; + }, [graph.initialization, selected]); + + return ( +
+
+
+ +
PLANTSIMENGINEScene Graph
+
+
+ + setQuery(event.target.value)} placeholder="Search application, object, or variable" /> + {query && } +
+
+ {graph.metadata.applicationCount} applications + {graph.metadata.objectCount} objects + {graph.metadata.unresolvedInitializationCount > 0 && ( + + )} + {graph.diagnostics.length > 0 && ( + + )} +
+ +
+ {editorConfig && } + {editorConfig && } + {view !== "topology" && ( + + )} + {editorConfig && } + {editorConfig && } + {editorConfig && } + {editorConfig && } + +
+
+ + {graph.metadata.cyclic && ( +
+ +
Current-step dependency cycleSelect a cycle input to read its previous accepted timestep value.
+ +
+ )} + {feedback &&
{feedback}
} + +
+
+ setSelected(edge.data ?? null)} + fitView + minZoom={0.05} + maxZoom={2} + > + + + + +
+ setApplicationForm({ + mode: "update", + scope: application.targetInstances.length > 0 ? "template" : "application", + instance: application.targetInstances[0], + application, + })} + onRemoveApplication={(application) => sendCommand({ + action: "edit", + kind: application.targetInstances.length > 0 ? "remove_template_application" : "remove_application", + instance: application.targetInstances[0], + applicationId: application.applicationId, + })} + onOverrideApplication={setOverrideApplication} + onEditObject={(object) => setObjectForm({ mode: "update", object })} + onRemoveObject={(object) => sendCommand({ action: "edit", kind: "remove_object", objectId: object.objectId, recursive: true })} + /> +
+ + {candidate && (candidateModels.length > 0 || candidateApplications.length > 0) && ( + { + setBindingForm(endpointsForCandidate(candidate, application)); + setCandidate(null); + }} + onClose={() => setCandidate(null)} + /> + )} + {showDiagnostics && setShowDiagnostics(false)} sendCommand={sendCommand} interactive={connected} />} + {showInitialization && setShowInitialization(false)} sendCommand={sendCommand} interactive={connected} />} + {showSceneCode && setShowSceneCode(false)} />} + {showOpen && { sendCommand({ action: "open_scene_code", path }); setShowOpen(false); }} onClose={() => setShowOpen(false)} />} + {showSave && { sendCommand({ action: "save_scene_code", path }); setShowSave(false); }} onClose={() => setShowSave(false)} />} + {applicationForm && ( + setApplicationForm(null)} + /> + )} + {bindingForm && ( + setBindingForm(null)} /> + )} + {objectForm && ( + setObjectForm(null)} /> + )} + {overrideApplication && ( + setOverrideApplication(null)} /> + )} + {cycleBreakSelection && ( + { + sendCommand({ + action: "edit", + kind: "break_cycle", + applicationId: cycleBreakSelection.application.applicationId, + input: cycleBreakSelection.port.name, + initializeMissing, + initialValue, + }); + setCycleBreakSelection(null); + }} + onClose={() => setCycleBreakSelection(null)} + /> + )} +
+ ); +} + +function buildNodes({ + graph, + view, + detailMode, + query, + unresolvedPortIds, + previousPortIds, + candidatePortIds, + cyclicApplications, + cycleBreakPortIds, + cycleBreakMode, + openCandidates, + onPortClick, + onCycleBreak, +}: { + graph: SceneGraphView; + view: GraphViewMode; + detailMode: DetailMode; + query: string; + unresolvedPortIds: Set; + previousPortIds: Set; + candidatePortIds: Set; + cyclicApplications: Set; + cycleBreakPortIds: Set; + cycleBreakMode: boolean; + openCandidates: (application: ApplicationGraphNode, port: GraphPort, anchor: { x: number; y: number }) => void; + onPortClick: (port: GraphPort) => void; + onCycleBreak: (application: ApplicationGraphNode, port: GraphPort) => void; +}): FlowNode[] { + const matches = (value: unknown) => !query || JSON.stringify(value).toLowerCase().includes(query.toLowerCase()); + if (view === "topology") { + return graph.objects.filter(matches).map((object) => ({ + id: object.id, + type: "entity", + position: { x: 0, y: 0 }, + data: { + nodeKind: "object", + title: object.name || String(object.objectId), + subtitle: [object.kind, object.scale, object.instance].filter(Boolean).join(" · "), + badges: [object.species, object.hasStatus ? "status" : null, object.hasGeometry ? "geometry" : null].filter(Boolean) as string[], + detail: object, + }, + })); + } + if (view === "resolved") { + const applications = new Map(graph.applications.map((application) => [application.applicationId, application])); + return graph.executions.filter(matches).map((execution) => { + const application = applications.get(execution.applicationId); + return { + id: execution.id, + type: "entity", + position: { x: 0, y: 0 }, + data: { + nodeKind: "execution", + title: execution.applicationId, + subtitle: `object ${String(execution.objectId)}`, + badges: [shortType(execution.modelType), execution.overridden ? "override" : "shared"], + inputPortIds: application?.inputs.map((port) => port.id) ?? [], + outputPortIds: application?.outputs.map((port) => port.id) ?? [], + detail: execution, + }, + }; + }); + } + return graph.applications.filter(matches).map((application) => ({ + id: application.id, + type: "application", + position: { x: 0, y: 0 }, + data: { + ...application, + nodeKind: "application", + detailMode, + cyclic: cyclicApplications.has(application.applicationId), + requiredInputPortIds: application.inputs.filter((port) => unresolvedPortIds.has(port.id)).map((port) => port.id), + candidatePortIds: [...application.inputs, ...application.outputs].filter((port) => candidatePortIds.has(port.id)).map((port) => port.id), + previousTimeStepPortIds: application.inputs.filter((port) => previousPortIds.has(port.id)).map((port) => port.id), + cycleBreakInputPortIds: application.inputs.filter((port) => cycleBreakPortIds.has(port.id)).map((port) => port.id), + cycleBreakMode, + onCandidateClick: (port, anchor) => openCandidates(application, port, anchor), + onPortClick, + onCycleBreak, + }, + })); +} + +function buildEdges(graph: SceneGraphView, view: GraphViewMode): FlowEdge[] { + return graph.edges + .filter((edge) => edgeProjectionMatches(edge, view)) + .map((edge) => ({ + id: edge.id, + source: edge.source, + target: edge.target, + sourceHandle: edge.sourcePort || undefined, + targetHandle: edge.targetPort || undefined, + type: "sceneEdge", + data: edge, + markerEnd: { type: MarkerType.ArrowClosed, color: edgeColor(edge), width: 16, height: 16 }, + style: { + stroke: edgeColor(edge), + strokeWidth: edge.cycle ? 4 : edge.kind === "manual_call" ? 2.5 : 1.8, + strokeDasharray: edge.kind === "previous_timestep" ? "7 5" : edge.kind === "manual_call" ? "3 4" : undefined, + }, + })); +} + +function edgeProjectionMatches(edge: SceneGraphEdge, view: GraphViewMode) { + const projection = (edge as SceneGraphEdge & { projection?: string }).projection; + if (view === "topology") return edge.kind === "object_topology"; + if (view === "resolved") return projection === "resolved"; + return projection === "applications" || (!projection && !["object_topology", "application_target"].includes(edge.kind)); +} + +function edgeColor(edge: SceneGraphEdge) { + if (edge.cycle) return "#cf4937"; + if (edge.kind === "previous_timestep") return "#317b62"; + if (edge.kind === "manual_call") return "#be6a54"; + if (edge.kind === "object_topology") return "#7b7167"; + return "#a59687"; +} + +export function deriveCandidatePortIds(graph: SceneGraphView) { + const result = new Set(); + for (const application of graph.applications) { + for (const input of application.inputs) { + const existing = graph.applications.some((other) => + other.applicationId !== application.applicationId && other.outputs.some((output) => output.name === input.name) + ); + if (existing || graph.modelLibrary.some((model) => Object.prototype.hasOwnProperty.call(model.outputs, input.name))) result.add(input.id); + } + for (const output of application.outputs) { + const existing = graph.applications.some((other) => + other.applicationId !== application.applicationId && other.inputs.some((input) => input.name === output.name) + ); + if (existing || graph.modelLibrary.some((model) => Object.prototype.hasOwnProperty.call(model.inputs, output.name))) result.add(output.id); + } + } + return result; +} + +export function modelsForPort(library: ModelDescriptor[], port: GraphPort) { + const field = port.role === "input" ? "outputs" : "inputs"; + return library + .filter((model) => Object.prototype.hasOwnProperty.call(model[field], port.name)) + .sort((left, right) => `${left.package}.${left.name}`.localeCompare(`${right.package}.${right.name}`)); +} + +function CandidatePopover({ + candidate, + models, + applications, + onSelectModel, + onSelectApplication, + onClose, +}: { + candidate: CandidatePopover; + models: ModelDescriptor[]; + applications: ApplicationGraphNode[]; + onSelectModel: (model: ModelDescriptor) => void; + onSelectApplication: (application: ApplicationGraphNode) => void; + onClose: () => void; +}) { + const title = candidate.port.role === "input" ? `Models that compute ${candidate.port.name}` : `Models that consume ${candidate.port.name}`; + return ( +
+
{title}Exact declared variable-name matches
+
+ {applications.length > 0 &&
Existing applications
} + {applications.map((application) => ( + + ))} + {models.length > 0 &&
Available models
} + {models.map((model) => ( + + ))} +
+
+ ); +} + +export function applicationsForPort(applications: ApplicationGraphNode[], candidate: CandidatePopover) { + return applications + .filter((application) => application.applicationId !== candidate.application.applicationId) + .filter((application) => { + const ports = candidate.port.role === "input" ? application.outputs : application.inputs; + return ports.some((port) => port.name === candidate.port.name); + }) + .sort((left, right) => left.applicationId.localeCompare(right.applicationId)); +} + +export function endpointsForCandidate(candidate: CandidatePopover, application: ApplicationGraphNode): BindingEndpoints { + if (candidate.port.role === "input") { + const sourcePort = application.outputs.find((port) => port.name === candidate.port.name); + if (!sourcePort) throw new Error(`Application ${application.applicationId} does not output ${candidate.port.name}.`); + return { sourceApplication: application, sourcePort, targetApplication: candidate.application, targetPort: candidate.port }; + } + const targetPort = application.inputs.find((port) => port.name === candidate.port.name); + if (!targetPort) throw new Error(`Application ${application.applicationId} does not input ${candidate.port.name}.`); + return { sourceApplication: candidate.application, sourcePort: candidate.port, targetApplication: application, targetPort }; +} + +function Inspector({ selection, port, initialization, interactive, onEditApplication, onRemoveApplication, onOverrideApplication, onEditObject, onRemoveObject }: { selection: InspectorSelection; port: GraphPort | null; initialization: SceneGraphView["initialization"]; interactive: boolean; onEditApplication: (application: ApplicationGraphNode) => void; onRemoveApplication: (application: ApplicationGraphNode) => void; onOverrideApplication: (application: ApplicationGraphNode) => void; onEditObject: (object: ObjectGraphNode) => void; onRemoveObject: (object: ObjectGraphNode) => void }) { + const application = selection && "applicationId" in selection && "selector" in selection ? selection as ApplicationGraphNode : null; + const object = selection && "objectId" in selection && !("applicationId" in selection) ? selection as ObjectGraphNode : null; + return ( + + ); +} + +function DiagnosticsPanel({ graph, onClose, sendCommand, interactive }: { graph: SceneGraphView; onClose: () => void; sendCommand: (command: Record) => void; interactive: boolean }) { + return + {graph.diagnostics.map((diagnostic) =>
{diagnostic.code}

{diagnostic.message}

{diagnostic.suggestions.map((suggestion) => {suggestion})}
)} + {graph.cycles.map((cycle) =>
{cycle.applicationIds.join(" → ")}

Choose an input to read from the previous timestep.

{cycle.breakCandidates.map((candidate) => )}
)} + {graph.diagnostics.length === 0 && graph.cycles.length === 0 &&

No diagnostics.

} +
; +} + +function InitializationPanel({ graph, onClose, sendCommand, interactive }: { graph: SceneGraphView; onClose: () => void; sendCommand: (command: Record) => void; interactive: boolean }) { + const unresolved = graph.initialization.filter((row) => row.disposition === "unresolved"); + const groups = new Map(); + for (const row of unresolved) { + const key = `${row.applicationId}:${row.variable}`; + groups.set(key, [...(groups.get(key) || []), row]); + } + return + {[...groups.entries()].map(([key, rows]) => )} + {unresolved.length === 0 &&

No unresolved initial values.

} +
; +} + +function InitializationGroup({ rows, interactive, sendCommand }: { rows: SceneGraphView["initialization"]; interactive: boolean; sendCommand: (command: Record) => void }) { + const [valueType, setValueType] = useState("float"); + const [value, setValue] = useState(""); + const first = rows[0]; + const typedValue = { type: valueType, value }; + return
+
{first.variable}{first.applicationId}
{rows.length} object{rows.length === 1 ? "" : "s"} · expected {first.expectedType}
+

Required because the input has no producer, environment source, status value, or usable temporal initialization.

+ {interactive &&
} +
{rows.map((row) =>
Object {String(row.objectId)}{row.origin}{interactive && }
)}
+
; +} + +function SceneCodePanel({ code, onClose }: { code: string; onClose: () => void }) { + return
{code || "Scene code is available from an interactive editor session."}
; +} + +function SceneFileDialog({ mode, recentPaths, currentPath, autosavePath, onSubmit, onClose }: { mode: "open" | "save"; recentPaths: string[]; currentPath: string | null; autosavePath: string | null; onSubmit: (path: string) => void; onClose: () => void }) { + const [path, setPath] = useState(currentPath || ""); + return +
+

{mode === "open" ? "Open a Julia script whose final binding is `scene = Scene(...)`. Future edits will be saved back to that file." : "After the first save, every successful graph edit automatically rewrites this Julia script."}

+ + {mode === "open" && recentPaths.length > 0 &&
Recent scenes
{recentPaths.map((recent) => )}
} + {mode === "open" && autosavePath &&
Recovery autosave
} + Use Git to version saved Scene scripts and review scientific configuration changes. +
+
; +} + +function CycleBreakDialog({ + selection, + initialization, + onSubmit, + onClose, +}: { + selection: CycleBreakSelection; + initialization: SceneGraphView["initialization"]; + onSubmit: (initializeMissing: boolean, initialValue: { type: string; value: string } | null) => void; + onClose: () => void; +}) { + const missing = initialization.filter((row) => + row.applicationId === selection.application.applicationId && + row.variable === selection.port.name && + row.disposition !== "supplied" + ); + const [valueType, setValueType] = useState("float"); + const [value, setValue] = useState(""); + return
+
event.stopPropagation()} data-testid="cycle-break-dialog"> +
Break the current-step cycle{selection.application.applicationId}.{selection.port.name}
+
+

This changes the application input to read its value from the previous accepted timestep. The model is disconnected from the current value during each run step.

+
Application-wide changeIt affects all {selection.application.targetCount} targets selected by this application.
+ {missing.length > 0 &&
Required initial value

{missing.length} target{missing.length === 1 ? "" : "s"} need a value before the first timestep.

} +
+
+
+
; +} + +function Overlay({ title, onClose, children }: { title: string; onClose: () => void; children: React.ReactNode }) { + return
event.stopPropagation()}>
{title}
{children}
; +} + +function selectionLabel(selection: Exclude) { + if ("applicationId" in selection) return selection.applicationId; + if ("objectId" in selection) return String(selection.objectId); + return selection.kind.replaceAll("_", " "); +} + +function shortType(type: string) { + return type.split(".").at(-1) || type; +} + +export function selectorSuggestion(application: ApplicationGraphNode): ApplicationGraphNode["selector"] { + const criteria: Record = { selectors: [] }; + if (application.targetInstances.length === 0 && application.targetScales.length === 1) criteria.scale = application.targetScales[0]; + if (application.targetKinds.length === 1) criteria.kind = application.targetKinds[0]; + if (application.targetSpecies.length === 1) criteria.species = application.targetSpecies[0]; + return { type: application.targetCount === 1 ? "One" : "Many", multiplicity: application.targetCount === 1 ? "one" : "many", criteria, julia: "" }; +} + +export function applicationPortId(applicationId: string, role: "input" | "output", variable: string) { + return `application:${applicationId}:${role}:${variable}`; +} + +function loadInitialGraph(): SceneGraphView { + const element = document.getElementById("pse-scene-graph-data"); + if (!element?.textContent) return sampleGraph; + try { return JSON.parse(element.textContent) as SceneGraphView; } catch { return sampleGraph; } +} + +function loadEditorConfig(): { websocketUrl: string } | null { + const element = document.getElementById("pse-editor-config"); + if (!element?.textContent) return null; + try { return JSON.parse(element.textContent) as { websocketUrl: string }; } catch { return null; } +} diff --git a/frontend/src/ApplicationForm.tsx b/frontend/src/ApplicationForm.tsx new file mode 100644 index 000000000..a887487cb --- /dev/null +++ b/frontend/src/ApplicationForm.tsx @@ -0,0 +1,146 @@ +import { useEffect, useMemo, useState } from "react"; +import { Check, X } from "lucide-react"; +import type { ApplicationGraphNode, ModelConstructorField, ModelDescriptor, ObjectGraphNode, SelectorDescriptor } from "./types"; + +export type ApplicationFormValue = { + applicationId?: string; + modelType: string; + name: string; + parameters: Record; + selector: SelectorDescriptor; + timestep: { mode: "default" } | { mode: "clock"; dt: string; phase: string }; +}; + +export function ApplicationForm({ + mode, + models, + objects, + application, + initialModelType, + suggestedSelector, + nameReadOnly=false, + onSubmit, + onClose, +}: { + mode: "add" | "update"; + models: ModelDescriptor[]; + objects: ObjectGraphNode[]; + application?: ApplicationGraphNode; + initialModelType?: string; + suggestedSelector?: SelectorDescriptor; + nameReadOnly?: boolean; + onSubmit: (value: ApplicationFormValue) => void; + onClose: () => void; +}) { + const initialType = application?.modelType || initialModelType || models[0]?.type || ""; + const [modelType, setModelType] = useState(initialType); + const model = models.find((item) => item.type === modelType) ?? null; + const [name, setName] = useState(application?.name || application?.applicationId || defaultApplicationName(model)); + const [parameters, setParameters] = useState>(() => parameterDefaults(model, application)); + const initialSelector = application?.selector || suggestedSelector || defaultSelector(objects); + const [multiplicity, setMultiplicity] = useState(initialSelector.multiplicity); + const [scale, setScale] = useState(stringCriterion(initialSelector, "scale")); + const [kind, setKind] = useState(stringCriterion(initialSelector, "kind")); + const [species, setSpecies] = useState(stringCriterion(initialSelector, "species")); + const [objectName, setObjectName] = useState(stringCriterion(initialSelector, "name")); + const [timestepMode, setTimestepMode] = useState<"default" | "clock">(application?.timestep ? "clock" : "default"); + const [dt, setDt] = useState("1.0"); + const [phase, setPhase] = useState("0.0"); + + useEffect(() => { + const selected = models.find((item) => item.type === modelType) ?? null; + setParameters(parameterDefaults(selected, mode === "update" ? application : undefined)); + if (mode === "add") setName(defaultApplicationName(selected)); + }, [application, mode, modelType, models]); + + const options = useMemo(() => ({ + scales: unique(objects.map((object) => object.scale)), + kinds: unique(objects.map((object) => object.kind)), + species: unique(objects.map((object) => object.species)), + names: unique(objects.map((object) => object.name)), + }), [objects]); + + const targetSummary = useMemo(() => { + const clauses = [scale && `scale ${scale}`, kind && `kind ${kind}`, species && `species ${species}`, objectName && `name ${objectName}`].filter(Boolean); + return clauses.length ? clauses.join(", ") : "all scene objects"; + }, [kind, objectName, scale, species]); + + const submit = () => { + const criteria: Record = { selectors: [] }; + if (scale) criteria.scale = scale; + if (kind) criteria.kind = kind; + if (species) criteria.species = species; + if (objectName) criteria.name = objectName; + onSubmit({ + applicationId: application?.applicationId, + modelType, + name: name.trim(), + parameters, + selector: { type: selectorType(multiplicity), multiplicity, criteria, julia: "" }, + timestep: timestepMode === "clock" ? { mode: "clock", dt, phase } : { mode: "default" }, + }); + }; + + return ( +
+
event.stopPropagation()} data-testid="application-form"> +
{mode === "add" ? "Add application" : `Update ${application?.applicationId}`}A configured use of a model on selected scene objects
+
+ + + + {model && model.constructor.fields.length > 0 &&
Model parameters
} + +
Target selector +
+ + + + + +
+

Julia will resolve {multiplicity.replace("_", " ")} target from {targetSummary}.

+
+ +
Timestep
{timestepMode === "clock" && <>}
+
+
+
+
+ ); +} + +export function ParameterFields({ fields, values, onChange }: { fields: ModelConstructorField[]; values: Record; onChange: (value: Record) => void }) { + const firstByGroup = new Map(); + for (const field of fields) if (field.typeParameter && !firstByGroup.has(field.typeParameter)) firstByGroup.set(field.typeParameter, field.name); + const updateType = (field: ModelConstructorField, type: string) => { + const names = field.typeParameter ? fields.filter((item) => item.typeParameter === field.typeParameter).map((item) => item.name) : [field.name]; + onChange(Object.fromEntries(Object.entries(values).map(([name, value]) => [name, names.includes(name) ? { ...value, type } : value]))); + }; + return
{fields.map((field) => { const value = values[field.name] || { type: field.inferredChoice, value: "" }; const showType = !field.typeParameter || firstByGroup.get(field.typeParameter) === field.name; return
{showType && }
; })}
; +} + +function SelectCriterion({ label, value, options, onChange }: { label: string; value: string; options: string[]; onChange: (value: string) => void }) { + return ; +} + +export function parameterDefaults(model: ModelDescriptor | null, application?: ApplicationGraphNode) { + if (!model) return {}; + return Object.fromEntries(model.constructor.fields.map((field) => { + const current = application?.modelParameters[field.name]; + const type = current?.type || field.inferredChoice; + const value = current ? current.julia : field.hasDefault ? type === "julia" ? field.defaultJulia || "" : displayDefault(field.default, type) : ""; + return [field.name, { type, value }]; + })); +} + +function displayDefault(value: unknown, type: string) { + const text = value === null || value === undefined ? "" : String(value); + return type === "symbol" ? text.replace(/^:/, "") : text; +} + +function defaultApplicationName(model: ModelDescriptor | null) { return model?.process || model?.name || "application"; } +function defaultSelector(objects: ObjectGraphNode[]): SelectorDescriptor { const scale = unique(objects.map((object) => object.scale))[0]; return { type: "Many", multiplicity: "many", criteria: scale ? { selectors: [], scale } : { selectors: [] }, julia: "" }; } +function stringCriterion(selector: SelectorDescriptor, key: string) { const value = selector.criteria[key]; return typeof value === "string" ? value : ""; } +function selectorType(value: SelectorDescriptor["multiplicity"]) { return value === "one" ? "One" : value === "optional_one" ? "OptionalOne" : "Many"; } +function unique(values: Array) { return [...new Set(values.filter((value): value is string => Boolean(value)))].sort(); } diff --git a/frontend/src/BindingForm.tsx b/frontend/src/BindingForm.tsx new file mode 100644 index 000000000..42bfb99e2 --- /dev/null +++ b/frontend/src/BindingForm.tsx @@ -0,0 +1,71 @@ +import { useState } from "react"; +import { Link2, X } from "lucide-react"; +import type { ApplicationGraphNode, GraphPort, ObjectGraphNode, SelectorDescriptor } from "./types"; + +export type BindingFormValue = { + applicationId: string; + input: string; + selector: SelectorDescriptor; +}; + +export type BindingEndpoints = { + sourceApplication: ApplicationGraphNode; + sourcePort: GraphPort; + targetApplication: ApplicationGraphNode; + targetPort: GraphPort; +}; + +export function BindingForm({ endpoints, objects, onSubmit, onClose }: { endpoints: BindingEndpoints; objects: ObjectGraphNode[]; onSubmit: (value: BindingFormValue) => void; onClose: () => void }) { + const sameTargets = sameValues(endpoints.sourceApplication.targetIds, endpoints.targetApplication.targetIds); + const [multiplicity, setMultiplicity] = useState(endpoints.sourceApplication.targetCount > 1 && endpoints.targetApplication.targetCount === 1 ? "many" : "one"); + const [relation, setRelation] = useState(sameTargets ? "self" : ""); + const [scale, setScale] = useState(onlyOrEmpty(endpoints.sourceApplication.targetScales)); + const [kind, setKind] = useState(onlyOrEmpty(endpoints.sourceApplication.targetKinds)); + const [sourceName, setSourceName] = useState(""); + const scales = unique(objects.map((object) => object.scale)); + const kinds = unique(objects.map((object) => object.kind)); + const names = unique(objects.map((object) => object.name)); + + const submit = () => { + const criteria: Record = { + selectors: [], + application: endpoints.sourceApplication.applicationId, + var: endpoints.sourcePort.name, + }; + if (relation) criteria.relation = relation; + if (scale) criteria.scale = scale; + if (kind) criteria.kind = kind; + if (sourceName) criteria.name = sourceName; + onSubmit({ + applicationId: endpoints.targetApplication.applicationId, + input: endpoints.targetPort.name, + selector: { type: selectorType(multiplicity), multiplicity, criteria, julia: "" }, + }); + }; + + return
+
event.stopPropagation()} data-testid="binding-form"> +
Connect applicationsJulia resolves this declaration into concrete object bindings
+
+
Producer{endpoints.sourceApplication.applicationId}{endpoints.sourcePort.name}
Consumer{endpoints.targetApplication.applicationId}{endpoints.targetPort.name}
+
Source object selector
+ + + + + +
+
+
+
+
; +} + +function Criterion({ label, value, options, onChange }: { label: string; value: string; options: string[]; onChange: (value: string) => void }) { + return ; +} + +function sameValues(left: unknown[], right: unknown[]) { return left.length === right.length && left.every((value) => right.some((other) => String(other) === String(value))); } +function onlyOrEmpty(values: string[]) { return values.length === 1 ? values[0] : ""; } +function selectorType(value: SelectorDescriptor["multiplicity"]) { return value === "one" ? "One" : value === "optional_one" ? "OptionalOne" : "Many"; } +function unique(values: Array) { return [...new Set(values.filter((value): value is string => Boolean(value)))].sort(); } diff --git a/frontend/src/DependencyEdge.tsx b/frontend/src/DependencyEdge.tsx new file mode 100644 index 000000000..a86d14891 --- /dev/null +++ b/frontend/src/DependencyEdge.tsx @@ -0,0 +1,54 @@ +import { BaseEdge, EdgeLabelRenderer, Position, getSmoothStepPath, type Edge, type EdgeProps } from "@xyflow/react"; +import type { SceneGraphEdge } from "./types"; + +type SceneFlowEdge = Edge; + +export function DependencyEdge({ + id, + sourceX, + sourceY, + targetX, + targetY, + sourcePosition = Position.Right, + targetPosition = Position.Left, + markerEnd, + style, + data, +}: EdgeProps) { + const [path, labelX, labelY] = getSmoothStepPath({ + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + borderRadius: 14, + offset: 24, + }); + const label = edgeLabel(data); + return ( + <> + + {label && ( + +
+ {label} +
+
+ )} + + ); +} + +function edgeLabel(data?: SceneGraphEdge) { + if (!data) return ""; + if (data.kind === "manual_call") return data.call || "call"; + if (data.kind === "object_topology" || data.kind === "application_target") return ""; + if (data.sourceVariable && data.targetVariable) { + return data.sourceVariable === data.targetVariable ? data.sourceVariable : `${data.sourceVariable} → ${data.targetVariable}`; + } + return data.kind.replaceAll("_", " "); +} diff --git a/frontend/src/ErrorBoundary.test.ts b/frontend/src/ErrorBoundary.test.ts new file mode 100644 index 000000000..d774feef6 --- /dev/null +++ b/frontend/src/ErrorBoundary.test.ts @@ -0,0 +1,9 @@ +import { describe, expect, it } from "vitest"; +import { ErrorBoundary } from "./ErrorBoundary"; + +describe("ErrorBoundary", () => { + it("captures a rendering error as recoverable state", () => { + const error = new Error("panel failed"); + expect(ErrorBoundary.getDerivedStateFromError(error)).toEqual({ error }); + }); +}); diff --git a/frontend/src/ErrorBoundary.tsx b/frontend/src/ErrorBoundary.tsx new file mode 100644 index 000000000..e5f8f66fc --- /dev/null +++ b/frontend/src/ErrorBoundary.tsx @@ -0,0 +1,24 @@ +import { AlertTriangle, RotateCcw } from "lucide-react"; +import { Component, type ErrorInfo, type ReactNode } from "react"; + +export class ErrorBoundary extends Component<{ children: ReactNode }, { error: Error | null }> { + state: { error: Error | null } = { error: null }; + + static getDerivedStateFromError(error: Error) { + return { error }; + } + + componentDidCatch(error: Error, info: ErrorInfo) { + console.error("PlantSimEngine Scene graph frontend failed", error, info); + } + + render() { + if (!this.state.error) return this.props.children; + return
+ +

The graph view could not be rendered

+

{this.state.error.message}

+ +
; + } +} diff --git a/frontend/src/ModelNode.tsx b/frontend/src/ModelNode.tsx new file mode 100644 index 000000000..9376fe2c1 --- /dev/null +++ b/frontend/src/ModelNode.tsx @@ -0,0 +1,212 @@ +import { Handle, Position, type Node, type NodeProps } from "@xyflow/react"; +import { Box, Clock3, Layers3, Plus, Scissors } from "lucide-react"; +import type { GraphPort, RuntimeApplicationNode, RuntimeEntityNode } from "./types"; +import { nodeWidth } from "./nodeSizing"; + +type ApplicationFlowNode = Node; +type EntityFlowNode = Node; + +export function ApplicationNode({ data, selected }: NodeProps) { + const overview = data.detailMode === "overview"; + const required = new Set(data.requiredInputPortIds); + const candidates = new Set(data.candidatePortIds); + const previous = new Set(data.previousTimeStepPortIds); + const cycleBreaks = new Set(data.cycleBreakInputPortIds); + const scope = scopeLabel(data); + + return ( +
+ {overview && } +
+
+
{data.name || data.applicationId}
+
{data.modelName}
+
+ +
+ {overview ? ( +
+ {data.targetCount} targets + {data.inputs.length} in + {data.outputs.length} out +
+ ) : ( + <> +
+ + {scope} + + + {rateLabel(data)} + +
+
+ {data.targetCount} concrete target{data.targetCount === 1 ? "" : "s"} +
+
+ + +
+ + )} +
+ ); +} + +export function EntityNode({ data, selected }: NodeProps) { + return ( +
+ {(data.inputPortIds?.length ? data.inputPortIds : [undefined]).map((id, index) => ( + + ))} +
+ {data.title} + {data.subtitle} +
+
+ {data.badges.map((badge) => {badge})} +
+ {(data.outputPortIds?.length ? data.outputPortIds : [undefined]).map((id, index) => ( + + ))} +
+ ); +} + +function PortColumn({ + title, + side, + ports, + required, + candidates, + previous, + cycleBreaks, + cycleBreakMode, + application, + onCandidateClick, + onPortClick, + onCycleBreak, +}: { + title: string; + side: "input" | "output"; + ports: GraphPort[]; + required: Set; + candidates: Set; + previous: Set; + cycleBreaks: Set; + cycleBreakMode: boolean; + application: RuntimeApplicationNode; + onCandidateClick?: RuntimeApplicationNode["onCandidateClick"]; + onPortClick?: RuntimeApplicationNode["onPortClick"]; + onCycleBreak?: RuntimeApplicationNode["onCycleBreak"]; +}) { + return ( +
+
{title}
+ {ports.map((port) => ( +
{ + event.stopPropagation(); + onPortClick?.(port); + }} + > + {side === "input" && } + {port.name} + {candidates.has(port.id) && ( + + )} + {side === "input" && cycleBreakMode && cycleBreaks.has(port.id) && ( + + )} + {previous.has(port.id) && t-1} + {side === "output" && } +
+ ))} +
+ ); +} + +function OverviewHandles({ inputs, outputs }: { inputs: GraphPort[]; outputs: GraphPort[] }) { + return ( + <> + {inputs.map((port, index) => ( + + ))} + {outputs.map((port, index) => ( + + ))} + + ); +} + +function handlePosition(index: number, total: number) { + return total <= 1 ? 52 : 28 + (index / (total - 1)) * 48; +} + +function scopeLabel(data: RuntimeApplicationNode) { + const labels = [...data.targetInstances, ...data.targetScales, ...data.targetKinds]; + return labels.length > 0 ? labels.slice(0, 2).join(" / ") : data.selector.type; +} + +function rateLabel(data: RuntimeApplicationNode) { + if (data.timestep === null || data.timestep === undefined) return "default rate"; + return String(data.timestep); +} diff --git a/frontend/src/ObjectForm.tsx b/frontend/src/ObjectForm.tsx new file mode 100644 index 000000000..81d31f939 --- /dev/null +++ b/frontend/src/ObjectForm.tsx @@ -0,0 +1,72 @@ +import { Check, X } from "lucide-react"; +import { useMemo, useState } from "react"; +import type { ObjectGraphNode } from "./types"; + +export type ObjectFormValue = { + objectId: string; + configuration: { + parent: string | null; + scale: string | null; + kind: string | null; + species: string | null; + name: string | null; + }; +}; + +export function ObjectForm({ + mode, + objects, + object, + onSubmit, + onClose, +}: { + mode: "add" | "update"; + objects: ObjectGraphNode[]; + object?: ObjectGraphNode; + onSubmit: (value: ObjectFormValue) => void; + onClose: () => void; +}) { + const [objectId, setObjectId] = useState(String(object?.objectId ?? "")); + const [parent, setParent] = useState(parentObjectId(object?.parent)); + const [scale, setScale] = useState(object?.scale || ""); + const [kind, setKind] = useState(object?.kind || ""); + const [species, setSpecies] = useState(object?.species || ""); + const [name, setName] = useState(object?.name || ""); + const parentOptions = useMemo( + () => objects.filter((item) => String(item.objectId) !== objectId), + [objectId, objects], + ); + + const submit = () => onSubmit({ + objectId: objectId.trim(), + configuration: { + parent: parent || null, + scale: scale.trim() || null, + kind: kind.trim() || null, + species: species.trim() || null, + name: name.trim() || null, + }, + }); + + return
+
event.stopPropagation()} data-testid="object-form"> +
{mode === "add" ? "Add scene object" : `Update object ${String(object?.objectId)}`}Objects define the concrete entities and topology targeted by applications
+
+ + +
+ + + + +
+
+
+
+
; +} + +function parentObjectId(parent: string | null | undefined) { + if (!parent) return ""; + return parent.startsWith("object:") ? parent.slice("object:".length) : parent; +} diff --git a/frontend/src/OverrideForm.tsx b/frontend/src/OverrideForm.tsx new file mode 100644 index 000000000..9dabe9d6e --- /dev/null +++ b/frontend/src/OverrideForm.tsx @@ -0,0 +1,71 @@ +import { Check, X } from "lucide-react"; +import { useMemo, useState } from "react"; +import { ParameterFields, parameterDefaults } from "./ApplicationForm"; +import type { ApplicationGraphNode, InstanceDescriptor, ModelDescriptor } from "./types"; + +export type OverrideFormValue = { + scope: "instance" | "object"; + instance: string; + objectId?: unknown; + applicationId: string; + modelType: string; + parameters: Record; +}; + +export function OverrideForm({ + application, + models, + instances, + onSubmit, + onClose, +}: { + application: ApplicationGraphNode; + models: ModelDescriptor[]; + instances: InstanceDescriptor[]; + onSubmit: (value: OverrideFormValue) => void; + onClose: () => void; +}) { + const matchingModels = useMemo( + () => models.filter((model) => model.process === application.process), + [application.process, models], + ); + const [scope, setScope] = useState<"instance" | "object">("instance"); + const [instanceName, setInstanceName] = useState(application.targetInstances[0] || instances[0]?.name || ""); + const instance = instances.find((item) => item.name === instanceName); + const objectIds = (instance?.objectIds || []).filter((id) => application.targetIds.some((target) => String(target) === String(id))); + const [objectId, setObjectId] = useState(objectIds[0] ?? ""); + const [modelType, setModelType] = useState(application.modelType); + const model = matchingModels.find((item) => item.type === modelType) || matchingModels[0] || null; + const [parameters, setParameters] = useState(() => parameterDefaults(model, application)); + + const selectModel = (value: string) => { + setModelType(value); + setParameters(parameterDefaults(matchingModels.find((item) => item.type === value) || null)); + }; + + return
+
event.stopPropagation()} data-testid="override-form"> +
Create a model overrideThe shared template remains unchanged outside the selected scope
+
+
+ + +
+
+ + {scope === "object" && } + +
+ {model && model.constructor.fields.length > 0 &&
Model parameters
} +
{scope === "instance" ? `Override ${instanceName}` : `Override object ${String(objectId)}`}Julia validates that the replacement keeps the same process and declared variable contract.
+
+
+
+
; +} diff --git a/frontend/src/elkjs.d.ts b/frontend/src/elkjs.d.ts new file mode 100644 index 000000000..519073945 --- /dev/null +++ b/frontend/src/elkjs.d.ts @@ -0,0 +1,3 @@ +declare module "elkjs/lib/elk.bundled.js" { + export { default } from "elkjs"; +} diff --git a/frontend/src/layout.ts b/frontend/src/layout.ts new file mode 100644 index 000000000..82538503f --- /dev/null +++ b/frontend/src/layout.ts @@ -0,0 +1,70 @@ +import ELK from "elkjs/lib/elk.bundled.js"; +import type { Edge, Node } from "@xyflow/react"; +import type { RuntimeApplicationNode, RuntimeEntityNode, SceneGraphEdge } from "./types"; +import { nodeWidth } from "./nodeSizing"; + +const elk = new ELK(); +export type LayoutMode = "data_flow" | "compact" | "overview" | "topology"; +type RuntimeNode = RuntimeApplicationNode | RuntimeEntityNode; + +export async function layoutGraph(nodes: Node[], edges: Edge[], mode: LayoutMode) { + const graph = { + id: "root", + layoutOptions: options(mode), + children: nodes.map((node) => ({ + id: node.id, + width: width(node.data), + height: height(node.data), + ports: node.data.nodeKind === "application" ? [ + ...node.data.inputs.map((port, index) => portDescriptor(port.id, "WEST", index)), + ...node.data.outputs.map((port, index) => portDescriptor(port.id, "EAST", index)), + ] : [ + ...(node.data.inputPortIds ?? []).map((id, index) => portDescriptor(id, "WEST", index)), + ...(node.data.outputPortIds ?? []).map((id, index) => portDescriptor(id, "EAST", index)), + ], + layoutOptions: { "org.eclipse.elk.portConstraints": "FIXED_ORDER" }, + })), + edges: edges.map((edge) => ({ + id: edge.id, + sources: [edge.sourceHandle ?? edge.source], + targets: [edge.targetHandle ?? edge.target], + })), + }; + const result = await elk.layout(graph); + const positions = new Map((result.children ?? []).map((child) => [child.id, { x: child.x ?? 0, y: child.y ?? 0 }])); + return nodes.map((node) => ({ ...node, position: positions.get(node.id) ?? node.position })); +} + +function options(mode: LayoutMode): Record { + return { + "elk.algorithm": mode === "topology" ? "mrtree" : "layered", + "elk.direction": mode === "topology" ? "DOWN" : "RIGHT", + "elk.spacing.nodeNode": mode === "overview" ? "24" : mode === "compact" ? "32" : "56", + "elk.layered.spacing.nodeNodeBetweenLayers": mode === "overview" ? "48" : mode === "compact" ? "60" : "110", + "elk.layered.nodePlacement.strategy": "BRANDES_KOEPF", + "elk.layered.crossingMinimization.semiInteractive": "true", + "elk.edgeRouting": "ORTHOGONAL", + }; +} + +function portDescriptor(id: string, side: "WEST" | "EAST", index: number) { + return { + id, + width: 9, + height: 9, + layoutOptions: { + "org.eclipse.elk.port.side": side, + "org.eclipse.elk.port.index": String(index), + }, + }; +} + +function width(data: RuntimeNode) { + return data.nodeKind === "application" ? nodeWidth(data) : 240; +} + +function height(data: RuntimeNode) { + if (data.nodeKind !== "application") return 112; + if (data.detailMode === "overview") return 108; + return Math.max(178, 142 + Math.max(data.inputs.length, data.outputs.length) * 27); +} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx new file mode 100644 index 000000000..1a7dc9525 --- /dev/null +++ b/frontend/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import App from "./App"; +import { ErrorBoundary } from "./ErrorBoundary"; + +createRoot(document.getElementById("root")!).render( + + + , +); diff --git a/frontend/src/nodeSizing.ts b/frontend/src/nodeSizing.ts new file mode 100644 index 000000000..3db28fc78 --- /dev/null +++ b/frontend/src/nodeSizing.ts @@ -0,0 +1,12 @@ +import type { RuntimeApplicationNode } from "./types"; + +export function nodeWidth(node: RuntimeApplicationNode) { + if (node.detailMode === "overview") return 190; + const longest = Math.max( + node.applicationId.length, + node.modelName.length, + ...node.inputs.map((port) => port.name.length), + ...node.outputs.map((port) => port.name.length), + ); + return Math.max(310, Math.min(540, 235 + longest * 7)); +} diff --git a/frontend/src/sampleGraph.ts b/frontend/src/sampleGraph.ts new file mode 100644 index 000000000..2daa25380 --- /dev/null +++ b/frontend/src/sampleGraph.ts @@ -0,0 +1,83 @@ +import type { ApplicationGraphNode, GraphPort, SceneGraphEdge, SceneGraphView } from "./types"; + +const source = application("source", "degree_days", "ToyDegreeDaysCumulModel", [], ["TT_cu"]); +const lai = application("lai", "lai_dynamic", "ToyLAIModel", ["TT_cu"], ["LAI"]); +const light = application("light", "light_interception", "Beer", ["LAI"], ["aPPFD"]); + +export const sampleGraph: SceneGraphView = { + schemaVersion: 1, + level: "applications", + metadata: { + title: "PlantSimEngine Scene Graph", + sceneRevision: 0, + objectCount: 1, + instanceCount: 0, + applicationCount: 3, + executionCount: 3, + bindingCount: 2, + callCount: 0, + unresolvedInitializationCount: 1, + cyclic: false, + strictlyCompiled: true, + }, + objects: [{ id: "object:plant", objectId: "plant", scale: "Plant", kind: "plant", species: null, name: "plant", instance: null, parent: null, children: [], hasGeometry: false, hasStatus: true }], + instances: [], + applications: [source, lai, light], + executions: [source, lai, light].map((item) => ({ id: `execution:${item.applicationId}:plant`, applicationId: item.applicationId, applicationNodeId: item.id, objectId: "plant", objectNodeId: "object:plant", modelType: item.modelType, modelParameters: {}, overridden: false })), + edges: [edge(source, "TT_cu", lai, "TT_cu"), edge(lai, "LAI", light, "LAI")], + modelLibrary: [], + initialization: [{ applicationId: "source", objectId: "plant", variable: "TT", role: "input", disposition: "unresolved", value: "-Inf", valueJulia: "-Inf", expectedType: "Float64", sourceApplicationIds: [], sourceObjectIds: [], sourceVariable: null, origin: "missing", previousTimeStep: false }], + diagnostics: [], + cycles: [], + availableActions: ["inspect"], +}; + +function application(applicationId: string, process: string, modelName: string, inputs: string[], outputs: string[]): ApplicationGraphNode { + return { + id: `application:${applicationId}`, + applicationId, + name: applicationId, + process, + modelType: modelName, + modelName, + module: "PlantSimEngine.Examples", + package: "PlantSimEngine", + modelParameters: {}, + selector: { type: "One", multiplicity: "one", criteria: { scale: "Plant" }, julia: "One(scale=:Plant)" }, + targetIds: ["plant"], + targetCount: 1, + targetScales: ["Plant"], + targetKinds: ["plant"], + targetSpecies: [], + targetInstances: [], + timestep: null, + clock: null, + inputs: inputs.map((name) => port(applicationId, "input", name)), + outputs: outputs.map((name) => port(applicationId, "output", name)), + environmentInputs: [], + environmentOutputs: [], + modelStorage: "shared_application", + objectOverrides: [], + }; +} + +function port(applicationId: string, role: "input" | "output", name: string): GraphPort { + return { id: `application:${applicationId}:${role}:${name}`, name, role, default: "-Inf", defaultJulia: "-Inf", expectedType: "Float64" }; +} + +function edge(sourceApplication: ApplicationGraphNode, sourceVariable: string, targetApplication: ApplicationGraphNode, targetVariable: string): SceneGraphEdge { + return { + id: `binding:${sourceApplication.applicationId}:${sourceVariable}:${targetApplication.applicationId}:${targetVariable}`, + source: sourceApplication.id, + target: targetApplication.id, + sourcePort: `application:${sourceApplication.applicationId}:output:${sourceVariable}`, + targetPort: `application:${targetApplication.applicationId}:input:${targetVariable}`, + sourceVariable, + targetVariable, + sourceApplicationId: sourceApplication.applicationId, + targetApplicationId: targetApplication.applicationId, + kind: "inferred_same_object", + cycle: false, + projection: "applications", + }; +} diff --git a/frontend/src/styles.css b/frontend/src/styles.css new file mode 100644 index 000000000..da40b96da --- /dev/null +++ b/frontend/src/styles.css @@ -0,0 +1,2617 @@ +:root { + --bg: #f3eee6; + --paper: #fffaf2; + --paper-strong: #fbf2e6; + --ink: #312721; + --muted: #80756c; + --line: #ded2c3; + --line-strong: #b7a696; + --accent: #1f7a53; + --accent-soft: rgba(31, 122, 83, 0.12); + --sage: #7f8f73; + --sage-dark: #596851; + --ochre: #c99035; + --clay: #bf6a54; + --shadow: rgba(56, 43, 35, 0.12); +} + +/* Scene/Object graph viewer */ +.scene-editor-shell { + height: 100vh; + min-height: 560px; + display: grid; + grid-template-rows: auto auto auto minmax(0, 1fr); + color: #302923; + background: #f3eee5; +} +.frontend-error { min-height: 100vh; display: grid; place-content: center; justify-items: center; gap: 10px; padding: 24px; color: #743128; background: #fff5ef; text-align: center; } +.frontend-error h1,.frontend-error p { margin: 0; } +.frontend-error p { max-width: 620px; color: #655850; } +.frontend-error button { display: inline-flex; align-items: center; gap: 6px; margin-top: 8px; padding: 8px 11px; border: 1px solid #b95040; border-radius: 5px; color: #fff; background: #b94435; } + +.scene-toolbar { + z-index: 20; + display: grid; + grid-template-columns: auto minmax(260px, 1fr) auto; + gap: 12px 18px; + align-items: center; + padding: 14px 18px; + background: #fffaf2; + border-bottom: 1px solid #d9cdbd; + box-shadow: 0 8px 24px rgba(60, 48, 37, 0.08); +} + +.scene-brand { display: flex; align-items: center; gap: 11px; } +.scene-brand .brand-mark { width: 5px; height: 42px; border-radius: 3px; background: #1f7a58; } +.scene-brand div { display: grid; } +.scene-brand small { color: #81766c; font: 10px/1.2 ui-monospace, monospace; letter-spacing: 0; } +.scene-brand strong { font-size: 20px; letter-spacing: 0; } + +.scene-search { + display: flex; + align-items: center; + gap: 8px; + min-width: 0; + padding: 8px 11px; + border: 1px solid #d9cdbd; + border-radius: 6px; + background: #fffdf8; +} +.scene-search input { width: 100%; min-width: 0; border: 0; outline: 0; background: transparent; font: inherit; } +.scene-search button, +.scene-toolbar button, +.overlay-panel button, +.candidate-popover button, +.editor-feedback button { border: 0; background: transparent; color: inherit; cursor: pointer; } + +.scene-counts { display: flex; align-items: center; justify-content: flex-end; gap: 7px; flex-wrap: wrap; } +.scene-counts > span, +.scene-counts > button { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 5px 8px; + border: 1px solid #d9cdbd; + border-radius: 5px; + background: #fffaf2; + font: 12px ui-monospace, monospace; +} +.scene-counts .count-warning { color: #a96a13; border-color: #dfbd83; } +.scene-counts .count-error { color: #b44e3c; border-color: #dfa496; } + +.view-tabs, +.scene-actions { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; } +.view-tabs { grid-column: 1 / 3; } +.scene-actions { justify-content: flex-end; } +.view-tabs button, +.scene-actions button, +.cycle-callout button { + display: inline-flex; + align-items: center; + gap: 6px; + min-height: 32px; + padding: 6px 10px; + border: 1px solid #d4c7b6; + border-radius: 5px; + background: #fffaf2; + color: #4c433b; +} +.view-tabs button.active { color: #176047; border-color: #83b59e; background: #e9f4ed; } +.scene-actions button:disabled { opacity: 0.4; cursor: default; } +.scene-actions .overview-cta { color: #176047; border-color: #86bba4; background: #e9f4ed; font-weight: 700; } + +.cycle-callout { + display: flex; + align-items: center; + gap: 12px; + padding: 10px 18px; + color: #8f2f24; + background: #fff0eb; + border-bottom: 1px solid #df9a8e; +} +.cycle-callout div { display: grid; margin-right: auto; } +.cycle-callout span { font-size: 12px; } +.cycle-callout button { border-color: #cf7668; color: #8f2f24; } +.cycle-callout button.active { color: #fff; border-color: #9d3428; background: #b94435; } + +.editor-feedback { + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + padding: 7px 16px; + background: #fff5d9; + border-bottom: 1px solid #dfc278; + color: #6e5315; + font-size: 12px; +} + +.scene-workspace { min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr) 310px; } +.flow-wrap { min-width: 0; min-height: 0; position: relative; } +.scene-inspector { overflow: auto; padding: 16px; background: #fffaf2; border-left: 1px solid #d9cdbd; } +.scene-inspector > header { display: grid; gap: 2px; margin-bottom: 14px; } +.scene-inspector > header span { color: #776c63; font: 11px ui-monospace, monospace; } +.scene-inspector pre { overflow-wrap: anywhere; white-space: pre-wrap; font-size: 10px; line-height: 1.45; } +.empty-inspector { display: grid; place-items: center; padding: 48px 20px; color: #8a7e74; text-align: center; } +.inspector-initialization > div { display: flex; justify-content: space-between; gap: 8px; padding: 6px 0; border-bottom: 1px solid #eee4d6; font-size: 11px; } +.inspector-initialization .unresolved { color: #b74635; font-weight: 700; } + +.application-node .target-summary { margin: -2px 0 10px; color: #766c63; font-size: 11px; } +.application-node .target-summary strong { color: #2d6652; } +.application-node .previous-label { margin-left: auto; color: #1f7a58; font: 10px ui-monospace, monospace; } +.cycle-port-break { + display: inline-grid; + place-items: center; + width: 24px; + height: 24px; + margin-left: auto; + border: 1px solid #c94e3e !important; + border-radius: 50%; + color: #a63428 !important; + background: #fff0eb !important; + box-shadow: 0 3px 9px rgba(140, 45, 35, 0.18); +} +.cycle-port-break:hover { color: #fff !important; background: #bd4435 !important; } + +.entity-node { + position: relative; + width: 240px; + min-height: 105px; + padding: 13px; + border: 1px solid #d8cbbb; + border-radius: 6px; + background: #fffaf2; + box-shadow: 0 7px 18px rgba(57, 46, 36, 0.12); +} +.entity-node.selected { border-color: #1f7a58; box-shadow: 0 0 0 2px rgba(31, 122, 88, 0.16); } +.entity-node header { display: grid; gap: 3px; } +.entity-node header span { color: #766c63; font-size: 11px; } +.entity-node .badges { display: flex; gap: 5px; flex-wrap: wrap; margin-top: 12px; } + +.candidate-popover { + position: fixed; + z-index: 80; + width: 370px; + max-height: 460px; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + border: 1px solid #cfbfaa; + border-radius: 7px; + background: #fffaf2; + box-shadow: 0 20px 54px rgba(52, 42, 32, 0.24); +} +.candidate-popover > header { display: flex; gap: 10px; padding: 12px; border-bottom: 1px solid #e1d5c5; } +.candidate-popover > header div { display: grid; margin-right: auto; } +.candidate-popover > header span { color: #7a7067; font-size: 10px; } +.candidate-list { overflow: auto; display: grid; gap: 7px; padding: 9px; } +.candidate-card { display: grid; grid-template-columns: 1fr auto; gap: 2px 8px; padding: 10px; border: 1px solid #ded2c2 !important; border-radius: 5px; background: #fffdf8 !important; text-align: left; } +.candidate-card:hover { border-color: #76a990 !important; background: #edf6f0 !important; } +.candidate-card span { color: #1f7053; font: 11px ui-monospace, monospace; } +.candidate-card small { color: #7a7067; } +.candidate-card div { grid-column: 1 / -1; color: #7a7067; font-size: 10px; } +.candidate-card.existing { border-left: 3px solid #1f7a58 !important; } +.candidate-section-label { + padding: 5px 3px 2px; + color: #71675e; + font: 700 10px ui-monospace, monospace; + text-transform: uppercase; +} + +.overlay-backdrop { position: fixed; z-index: 100; inset: 0; display: grid; place-items: center; padding: 24px; background: rgba(45, 37, 30, 0.38); } +.overlay-panel { width: min(720px, 100%); max-height: min(760px, 92vh); display: grid; grid-template-rows: auto minmax(0, 1fr); border: 1px solid #cdbda8; border-radius: 7px; background: #fffaf2; box-shadow: 0 24px 70px rgba(39, 31, 25, 0.3); } +.overlay-panel > header { display: flex; justify-content: space-between; align-items: center; padding: 14px 16px; border-bottom: 1px solid #ded2c2; } +.overlay-content { overflow: auto; padding: 15px; } +.diagnostic-card,.cycle-card,.initialization-card { display: grid; gap: 5px; margin-bottom: 10px; padding: 11px; border: 1px solid #ded2c2; border-radius: 5px; } +.diagnostic-card { border-left: 3px solid #c85240; } +.diagnostic-card p,.cycle-card p { margin: 0; } +.diagnostic-card small { color: #766c63; } +.cycle-card button { width: fit-content; padding: 6px 8px; border: 1px solid #ce7768; border-radius: 4px; color: #963529; } +.scene-code { min-height: 320px; margin: 0; padding: 14px; overflow: auto; border-radius: 5px; background: #292622; color: #f5eee5; } +.scene-file-dialog { display: grid; gap: 14px; } +.scene-file-dialog > p { margin: 0; line-height: 1.5; } +.scene-file-dialog > label { display: grid; gap: 5px; color: #5f554d; font-size: 11px; font-weight: 700; } +.scene-path-input { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 8px; } +.scene-path-input input { width: 100%; min-width: 0; padding: 8px 9px; border: 1px solid #d3c5b4; border-radius: 4px; background: #fffdf8; font: 12px ui-monospace, monospace; } +.scene-path-input button { padding: 8px 13px; border: 1px solid #1d7052 !important; border-radius: 4px; color: #fff !important; background: #1f7a58 !important; } +.scene-path-input button:disabled { opacity: .4; cursor: default; } +.scene-file-dialog section { display: grid; gap: 7px; } +.recent-scene-list { display: grid; gap: 6px; } +.recent-scene-list button { display: grid; gap: 2px; padding: 9px; border: 1px solid #d8cbbb !important; border-radius: 5px; background: #fffdf8 !important; text-align: left; } +.recent-scene-list button:hover { border-color: #73a88e !important; background: #edf6f0 !important; } +.recent-scene-list small,.scene-file-dialog > small { color: #776d64; overflow-wrap: anywhere; } +.recovery-path { padding: 9px; border: 1px dashed #c99035 !important; border-radius: 5px; color: #6d511d !important; background: #fff6e6 !important; text-align: left; overflow-wrap: anywhere; } +.initialization-group { display: grid; gap: 10px; margin-bottom: 12px; padding: 12px; border: 1px solid #d8cbbb; border-radius: 6px; background: #fffdf8; } +.initialization-group > header { display: flex; align-items: end; justify-content: space-between; gap: 12px; } +.initialization-group > header div { display: grid; } +.initialization-group > header span,.initialization-group > header small { color: #786d64; font: 10px ui-monospace, monospace; } +.initialization-group > p { margin: 0; color: #6c625a; font-size: 11px; line-height: 1.45; } +.initialization-value { display: grid; grid-template-columns: 120px minmax(0, 1fr) auto; gap: 8px; align-items: end; } +.initialization-value label { display: grid; gap: 4px; color: #5f554d; font-size: 10px; font-weight: 700; } +.initialization-value input,.initialization-value select { width: 100%; min-width: 0; padding: 7px 8px; border: 1px solid #d3c5b4; border-radius: 4px; background: #fff; } +.initialization-value button,.initialization-object-list button { padding: 7px 9px; border: 1px solid #85ad99 !important; border-radius: 4px; color: #176047 !important; background: #edf6f0 !important; } +.initialization-value button:disabled,.initialization-object-list button:disabled { opacity: .4; cursor: default; } +.initialization-object-list { display: grid; gap: 5px; } +.initialization-object-list > div { display: grid; grid-template-columns: minmax(0, 1fr) auto auto; gap: 8px; align-items: center; padding-top: 5px; border-top: 1px solid #ece1d2; font-size: 11px; } +.initialization-object-list code { color: #a33d31; } + +@media (max-width: 980px) { + .scene-toolbar { grid-template-columns: 1fr; } + .view-tabs { grid-column: auto; } + .scene-counts,.scene-actions { justify-content: flex-start; } + .scene-workspace { grid-template-columns: 1fr; } + .scene-inspector { display: none; } +} + +.application-form { width: min(780px, 100%); } +.object-form { width: min(640px, 100%); } +.override-form { width: min(760px, 100%); } +.application-form > header div { display: grid; gap: 2px; } +.object-form > header div { display: grid; gap: 2px; } +.override-form > header div { display: grid; gap: 2px; } +.application-form > header span { color: #786d64; font-size: 11px; } +.object-form > header span { color: #786d64; font-size: 11px; } +.override-form > header span { color: #786d64; font-size: 11px; } +.application-form-content { display: grid; gap: 14px; } +.object-form-content { display: grid; gap: 14px; } +.override-form-content { display: grid; gap: 14px; } +.application-form-content > label, +.application-form fieldset label, +.object-form-content label { display: grid; gap: 5px; color: #5f554d; font-size: 11px; font-weight: 700; } +.override-form-content label { display: grid; gap: 5px; color: #5f554d; font-size: 11px; font-weight: 700; } +.application-form input, +.application-form select, +.object-form input, +.object-form select { + width: 100%; + min-width: 0; + padding: 8px 9px; + border: 1px solid #d3c5b4; + border-radius: 4px; + background: #fffdf8; + color: #332c26; + font: 12px ui-monospace, monospace; +} +.override-form input,.override-form select { width: 100%; min-width: 0; padding: 8px 9px; border: 1px solid #d3c5b4; border-radius: 4px; background: #fffdf8; color: #332c26; font: 12px ui-monospace, monospace; } +.override-form fieldset { margin: 0; padding: 12px; border: 1px solid #ded2c2; border-radius: 5px; } +.override-form legend { padding: 0 6px; color: #2d6652; font-weight: 800; } +.override-form fieldset label { display: grid; gap: 5px; color: #5f554d; font-size: 11px; font-weight: 700; } +.override-scope-choice { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; } +.override-scope-choice button { display: grid; gap: 3px; padding: 11px; border: 1px solid #d4c7b6 !important; border-radius: 5px; background: #fffdf8 !important; text-align: left; } +.override-scope-choice button.active { border-color: #72a88d !important; background: #edf6f0 !important; } +.override-scope-choice span { color: #756a61; font-size: 10px; line-height: 1.4; } +.override-warning { display: grid; gap: 3px; padding: 10px; border-left: 3px solid #c99035; background: #fff6e6; } +.override-warning span { color: #74685e; font-size: 11px; } +.application-form fieldset { margin: 0; padding: 12px; border: 1px solid #ded2c2; border-radius: 5px; } +.application-form legend { padding: 0 6px; color: #2d6652; font-weight: 800; } +.form-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; } +.parameter-list { display: grid; gap: 9px; } +.parameter-row { display: grid; grid-template-columns: minmax(0, 1fr) 150px; gap: 9px; align-items: end; } +.parameter-row > label > span { color: #3c342e; } +.parameter-row small { color: #8a7e74; font-weight: 400; } +.selector-summary { margin: 10px 0 0; color: #796e64; font-size: 11px; } +.application-form > footer { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 15px; border-top: 1px solid #ded2c2; } +.object-form > footer { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 15px; border-top: 1px solid #ded2c2; } +.override-form > footer { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 15px; border-top: 1px solid #ded2c2; } +.application-form > footer button, +.object-form > footer button, +.override-form > footer button, +.inspector-actions button { display: inline-flex; align-items: center; gap: 6px; padding: 7px 10px; border: 1px solid #d2c4b3; border-radius: 4px; background: #fffdf8; } +.application-form > footer .primary, +.object-form > footer .primary { color: #fff; border-color: #1d7052; background: #1f7a58; } +.override-form > footer .primary { color: #fff; border-color: #1d7052; background: #1f7a58; } +.application-form > footer button:disabled { opacity: .45; } +.object-form > footer button:disabled { opacity: .45; } +.override-form > footer button:disabled { opacity: .45; } +.inspector-actions { display: flex; gap: 7px; margin: 10px 0 16px; } +.inspector-actions .danger { color: #a33d31; border-color: #d8a296; } + +.binding-form { width: min(680px, 100%); } +.binding-form > header div { display: grid; gap: 2px; } +.binding-form > header span { color: #786d64; font-size: 11px; } +.binding-form .overlay-content { display: grid; gap: 14px; } +.binding-route { + display: grid; + grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); + align-items: center; + gap: 14px; + padding: 12px; + border: 1px solid #d8cbbb; + border-radius: 5px; + background: #fffdf8; +} +.binding-route > div { display: grid; gap: 3px; min-width: 0; } +.binding-route > div:last-child { text-align: right; } +.binding-route small { color: #7c7168; text-transform: uppercase; font-size: 9px; } +.binding-route strong,.binding-route code { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.binding-route code { color: #1f7053; } +.binding-form fieldset { margin: 0; padding: 12px; border: 1px solid #ded2c2; border-radius: 5px; } +.binding-form legend { padding: 0 6px; color: #2d6652; font-weight: 800; } +.binding-form fieldset label { display: grid; gap: 5px; color: #5f554d; font-size: 11px; font-weight: 700; } +.binding-form select { + width: 100%; + min-width: 0; + padding: 8px 9px; + border: 1px solid #d3c5b4; + border-radius: 4px; + background: #fffdf8; + color: #332c26; + font: 12px ui-monospace, monospace; +} +.binding-form > footer { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 15px; border-top: 1px solid #ded2c2; } +.binding-form > footer button { display: inline-flex; align-items: center; gap: 6px; padding: 7px 10px; border: 1px solid #d2c4b3; border-radius: 4px; background: #fffdf8; } +.binding-form > footer .primary { color: #fff; border-color: #1d7052; background: #1f7a58; } + +.cycle-break-dialog { width: min(620px, 100%); } +.cycle-break-dialog > header div { display: grid; gap: 2px; } +.cycle-break-dialog > header span { color: #a33d31; font: 11px ui-monospace, monospace; } +.cycle-break-dialog .overlay-content { display: grid; gap: 13px; } +.cycle-break-dialog p { margin: 0; line-height: 1.5; } +.cycle-impact { display: grid; gap: 3px; padding: 10px; border-left: 3px solid #c54c3c; background: #fff0eb; } +.cycle-impact span { color: #705f57; font-size: 11px; } +.cycle-break-dialog fieldset { margin: 0; padding: 12px; border: 1px solid #d8cbbb; border-radius: 5px; } +.cycle-break-dialog legend { padding: 0 6px; color: #9a392e; font-weight: 800; } +.cycle-break-dialog label { display: grid; gap: 5px; color: #5f554d; font-size: 11px; font-weight: 700; } +.cycle-break-dialog input,.cycle-break-dialog select { width: 100%; min-width: 0; padding: 8px 9px; border: 1px solid #d3c5b4; border-radius: 4px; background: #fffdf8; } +.cycle-break-dialog > footer { display: flex; justify-content: flex-end; gap: 8px; padding: 12px 15px; border-top: 1px solid #ded2c2; } +.cycle-break-dialog > footer button { display: inline-flex; align-items: center; gap: 6px; padding: 7px 10px; border: 1px solid #d2c4b3; border-radius: 4px; background: #fffdf8; } +.cycle-break-dialog > footer .primary { color: #fff; border-color: #a8392d; background: #b94435; } +.cycle-break-dialog > footer button:disabled { opacity: .45; cursor: default; } + +@media (max-width: 700px) { + .form-grid { grid-template-columns: 1fr; } + .parameter-row { grid-template-columns: 1fr; } + .override-scope-choice { grid-template-columns: 1fr; } + .binding-route { grid-template-columns: 1fr; } + .binding-route > div:last-child { text-align: left; } + .initialization-value { grid-template-columns: 1fr; } +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + color: var(--ink); + background: + radial-gradient(circle at 20% 24%, rgba(31, 122, 83, 0.045), transparent 30%), + radial-gradient(circle at 78% 68%, rgba(201, 144, 53, 0.055), transparent 34%), + linear-gradient(180deg, rgba(255, 250, 242, 0.26), transparent 42%), + var(--bg); + font-family: "Avenir Next", "Trebuchet MS", "Segoe UI", sans-serif; +} + +.app-shell { + display: grid; + grid-template-columns: minmax(0, 1fr); + height: 100vh; + position: relative; +} + +.app-shell.has-side-panel { + grid-template-columns: minmax(0, 1fr) 340px; +} + +.graph-panel { + position: relative; + min-width: 0; +} + +.graph-panel::after { + content: ""; + position: absolute; + inset: 0; + z-index: 0; + pointer-events: none; + opacity: 0.2; + background-image: + radial-gradient(rgba(49, 39, 33, 0.11) 0.55px, transparent 0.55px); + background-size: 16px 16px; + mask-image: linear-gradient(to bottom, transparent 0%, black 22%, black 82%, transparent 100%); +} + +.topbar { + position: absolute; + z-index: 10; + top: 18px; + left: 18px; + right: 18px; + display: flex; + align-items: center; + gap: 12px; + padding: 11px 14px; + background: rgba(255, 250, 242, 0.92); + border: 1px solid var(--line); + border-radius: 14px; + box-shadow: 0 18px 45px var(--shadow); + backdrop-filter: blur(10px); +} + +.topbar::before { + content: ""; + align-self: stretch; + width: 5px; + border-radius: 999px; + background: var(--accent); +} + +.editor-feedback { + position: absolute; + z-index: 12; + top: 96px; + left: 18px; + right: 18px; + padding: 9px 12px; + border: 1px solid var(--line); + border-radius: 10px; + background: rgba(255, 250, 242, 0.95); + box-shadow: 0 12px 30px var(--shadow); + max-height: 132px; + overflow: auto; + font-size: 12px; + line-height: 1.45; + white-space: normal; + overflow-wrap: anywhere; +} + +.editor-feedback.error { + color: var(--clay); + border-color: rgba(191, 106, 84, 0.4); + background: rgba(255, 244, 238, 0.97); +} + +.editor-feedback.info { + color: var(--accent); + border-color: rgba(31, 122, 83, 0.35); + background: rgba(245, 252, 248, 0.97); +} + +.cycle-break-prompt { + position: absolute; + z-index: 14; + top: 98px; + left: 18px; + right: 18px; + display: flex; + align-items: center; + justify-content: space-between; + gap: 14px; + padding: 11px 12px; + border: 1px solid rgba(211, 66, 47, 0.32); + border-radius: 12px; + background: + linear-gradient(135deg, rgba(211, 66, 47, 0.12), transparent 62%), + rgba(255, 250, 242, 0.96); + box-shadow: 0 16px 36px rgba(56, 43, 35, 0.16); +} + +.cycle-break-prompt div { + display: grid; + gap: 3px; + min-width: 0; +} + +.cycle-break-prompt strong { + color: #7a2018; + font-size: 13px; +} + +.cycle-break-prompt span { + color: var(--muted); + font-size: 12px; + line-height: 1.35; +} + +.cycle-break-prompt code { + color: #7a2018; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; +} + +.cycle-break-prompt.active { + border-color: rgba(211, 66, 47, 0.52); + box-shadow: + 0 16px 36px rgba(56, 43, 35, 0.16), + 0 0 0 4px rgba(211, 66, 47, 0.08); +} + +.cycle-break-cta { + flex: 0 0 auto; + justify-content: center; + font-weight: 780; +} + +.graph-workbench { + flex-wrap: wrap; + align-items: flex-start; + max-height: min(36vh, 250px); + overflow: auto; + scrollbar-width: thin; +} + +.brand-block { + min-width: 150px; +} + +.brand-block h1 { + font-size: clamp(16px, 2.2vw, 20px); +} + +.search-box { + position: relative; + display: flex; + align-items: center; + gap: 8px; + flex: 1 1 280px; + max-width: 460px; + min-width: 220px; + padding: 7px 9px; + border: 1px solid var(--line); + border-radius: 11px; + background: rgba(255, 253, 247, 0.86); +} + +.search-box input { + width: 100%; + min-width: 0; + border: 0; + outline: 0; + color: var(--ink); + background: transparent; + font: inherit; + font-size: 13px; +} + +.clear-search { + display: grid; + place-items: center; + width: 20px; + height: 20px; + border: 0; + border-radius: 999px; + background: rgba(183, 166, 150, 0.18); + color: var(--muted); + cursor: pointer; +} + +.search-results { + position: absolute; + z-index: 80; + top: calc(100% + 8px); + left: 0; + right: 0; + display: grid; + gap: 6px; + max-height: 360px; + overflow: auto; + padding: 8px; + border: 1px solid var(--line); + border-radius: 12px; + background: rgba(255, 250, 242, 0.98); + box-shadow: 0 18px 45px var(--shadow); +} + +.search-result { + display: grid; + gap: 2px; + padding: 8px 9px; + border: 1px solid transparent; + border-radius: 9px; + background: transparent; + color: var(--ink); + text-align: left; + cursor: pointer; +} + +.search-result:hover, +.search-result:focus-visible { + border-color: rgba(31, 122, 83, 0.24); + background: var(--accent-soft); + outline: none; +} + +.search-result strong { + overflow-wrap: anywhere; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; + font-size: 12px; +} + +.search-result span { + color: var(--muted); + font-size: 11px; +} + +.eyebrow { + color: var(--muted); + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.08em; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; +} + +h1, +h2, +h3 { + margin: 0; + letter-spacing: 0; +} + +h1 { + font-size: 20px; + font-weight: 800; +} + +.metrics { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-left: auto; +} + +.metrics span, +.metric-button, +.node-meta span { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 5px 8px; + border: 1px solid var(--line); + border-radius: 9px; + background: rgba(255, 250, 242, 0.9); + font-size: 12px; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; +} + +.metric-button { + color: var(--ink); + cursor: pointer; +} + +.metric-button.active, +.metric-button:hover, +.metric-button:focus-visible { + outline: none; + background: #fff6f0; +} + +.view-mode-button.overview-cta { + border-color: rgba(31, 122, 83, 0.38); + color: var(--accent); + background: rgba(31, 122, 83, 0.1); + box-shadow: 0 8px 18px rgba(31, 122, 83, 0.1); + font-weight: 800; +} + +.meta-chip { + position: relative; +} + +.meta-chip::after { + content: attr(data-tooltip); + position: absolute; + z-index: 20; + left: 0; + bottom: calc(100% + 10px); + width: max-content; + max-width: 280px; + padding: 8px 10px; + color: var(--paper); + background: var(--ink); + border-radius: 10px; + box-shadow: 0 12px 28px rgba(56, 43, 35, 0.18); + font-family: "Avenir Next", "Trebuchet MS", "Segoe UI", sans-serif; + font-size: 12px; + line-height: 1.3; + white-space: normal; + opacity: 0; + pointer-events: none; + transform: translateY(4px); + transition: opacity 120ms ease, transform 120ms ease; +} + +.meta-chip::before { + content: ""; + position: absolute; + z-index: 21; + left: 16px; + bottom: calc(100% + 4px); + width: 10px; + height: 10px; + background: var(--ink); + opacity: 0; + pointer-events: none; + transform: rotate(45deg) translateY(4px); + transition: opacity 120ms ease, transform 120ms ease; +} + +.meta-chip:hover::after, +.meta-chip:hover::before, +.meta-chip:focus-visible::after, +.meta-chip:focus-visible::before { + opacity: 1; + transform: translateY(0); +} + +.meta-chip:hover::before, +.meta-chip:focus-visible::before { + transform: rotate(45deg) translateY(0); +} + +.metrics .warn { + color: var(--clay); + border-color: rgba(201, 97, 74, 0.45); +} + +.metrics .caution { + color: var(--ochre); + border-color: rgba(201, 144, 53, 0.45); +} + +.metrics .warn svg { + flex: 0 0 auto; +} + +.icon-button { + display: grid; + place-items: center; + width: 34px; + height: 34px; + border: 1px solid var(--line); + border-radius: 10px; + background: var(--paper); + color: var(--ink); + cursor: pointer; + box-shadow: 0 8px 18px rgba(56, 43, 35, 0.08); +} + +.icon-button.compact { + width: 28px; + height: 28px; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; + font-size: 14px; +} + +.toolbar-group { + display: flex; + align-items: center; + gap: 8px; +} + +.open-button { + flex: 0 0 auto; +} + +.panel-switch { + flex-wrap: wrap; +} + +.select-control { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 5px 8px; + border: 1px solid var(--line); + border-radius: 10px; + background: rgba(255, 250, 242, 0.9); + color: var(--muted); +} + +.select-control select { + max-width: 132px; + border: 0; + outline: 0; + background: transparent; + color: var(--ink); + font: inherit; + font-size: 12px; +} + +.relationship-legend { + position: absolute; + z-index: 35; + top: 116px; + left: 18px; + display: grid; + gap: 7px; + width: 190px; + padding: 10px; + border: 1px solid var(--line); + border-radius: 13px; + background: rgba(255, 250, 242, 0.92); + box-shadow: 0 18px 45px var(--shadow); + backdrop-filter: blur(10px); + max-height: min(480px, calc(100vh - 142px)); + overflow: auto; +} + +.legend-title, +.legend-note { + display: flex; + align-items: center; + gap: 6px; + color: var(--muted); + font-size: 11px; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; +} + +.relationship-legend button { + display: flex; + align-items: center; + gap: 8px; + padding: 6px 7px; + border: 1px solid transparent; + border-radius: 9px; + background: transparent; + color: var(--muted); + font: inherit; + font-size: 12px; + cursor: pointer; +} + +.relationship-legend button.active { + color: var(--ink); + border-color: rgba(31, 122, 83, 0.22); + background: var(--accent-soft); +} + +.legend-line { + width: 28px; + height: 0; + border-top: 2px solid var(--line-strong); +} + +.legend-line.mapped { + border-color: var(--accent); + border-style: dashed; +} + +.legend-line.call { + border-color: var(--clay); + border-style: dashed; +} + +.scale-controls { + position: absolute; + z-index: 35; + top: 116px; + left: 220px; + display: grid; + gap: 8px; + width: 190px; + padding: 10px; + border: 1px solid var(--line); + border-radius: 13px; + background: rgba(255, 250, 242, 0.92); + box-shadow: 0 18px 45px var(--shadow); + backdrop-filter: blur(10px); + max-height: min(560px, calc(100vh - 142px)); + overflow: auto; +} + +.scale-list { + display: grid; + gap: 6px; +} + +.scale-list button, +.scale-reset { + display: grid; + gap: 2px; + width: 100%; + padding: 7px 8px; + border: 1px solid transparent; + border-radius: 9px; + background: transparent; + color: var(--muted); + font: inherit; + text-align: left; + cursor: pointer; +} + +.scale-list button.active { + color: var(--ink); + border-color: rgba(31, 122, 83, 0.22); + background: var(--accent-soft); +} + +.scale-list button.collapsed { + border-color: rgba(183, 166, 150, 0.34); + border-style: dashed; + background: rgba(183, 166, 150, 0.08); +} + +.scale-list span { + overflow-wrap: anywhere; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; + font-size: 12px; + font-weight: 700; +} + +.scale-list small { + color: var(--muted); + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.06em; +} + +.scale-reset { + color: var(--accent); + border-color: rgba(31, 122, 83, 0.2); + background: rgba(31, 122, 83, 0.08); + text-align: center; +} + +.floating-panel { + position: absolute; + z-index: 40; + top: 116px; + right: 18px; + width: min(360px, calc(100vw - 36px)); + max-height: min(560px, calc(100vh - 142px)); + overflow: auto; + padding: 14px; + background: rgba(255, 250, 242, 0.96); + border: 1px solid rgba(191, 106, 84, 0.32); + border-radius: 14px; + box-shadow: 0 18px 45px var(--shadow); + backdrop-filter: blur(12px); +} + +.warnings-panel { + border-color: rgba(201, 144, 53, 0.35); +} + +.open-panel { + left: 18px; + right: auto; + border-color: rgba(31, 122, 83, 0.32); +} + +.floating-panel-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + margin-bottom: 12px; +} + +.floating-panel h2 { + font-size: 18px; +} + +.react-flow { + background: transparent; + z-index: 1; +} + +.react-flow__background { + display: none; +} + +.react-flow__edges { + z-index: 8; +} + +.react-flow__edges:has(.react-flow__edge.highlighted) { + z-index: 120; +} + +.react-flow__edgelabel-renderer { + z-index: 28; +} + +.react-flow__edgelabel-renderer:has(.edge-chip.highlighted) { + z-index: 121; +} + +.react-flow__nodes { + z-index: 20; +} + +.model-node { + position: relative; + overflow: visible; + background: rgba(255, 250, 242, 0.96); + border: 1px solid var(--line); + border-radius: 16px; + box-shadow: 0 18px 42px var(--shadow); +} + +.model-remove-button { + position: absolute; + z-index: 45; + top: -11px; + right: -11px; + display: grid; + place-items: center; + width: 30px; + height: 30px; + border: 1px solid rgba(191, 106, 84, 0.4); + border-radius: 999px; + color: #fff; + background: var(--clay); + box-shadow: 0 10px 24px rgba(191, 106, 84, 0.24); + cursor: pointer; +} + +.model-remove-button:hover, +.model-remove-button:focus-visible { + outline: none; + background: #9b3f2e; + transform: translateY(-1px); +} + +.node-header { + border-radius: 16px 16px 0 0; +} + +.model-node.selected { + border-color: rgba(31, 122, 83, 0.72); + box-shadow: 0 20px 48px rgba(31, 122, 83, 0.14); +} + +.model-node.focused { + border-color: rgba(31, 122, 83, 0.62); + box-shadow: + 0 20px 48px rgba(31, 122, 83, 0.14), + 0 0 0 4px rgba(31, 122, 83, 0.08); +} + +.model-node.dimmed { + opacity: 0.2; + filter: grayscale(0.2); +} + +.model-node.cyclic { + border-color: rgba(191, 106, 84, 0.62); + box-shadow: + 0 18px 42px var(--shadow), + 0 0 0 3px rgba(191, 106, 84, 0.08); +} + +.model-node.cyclic .node-header > svg { + color: var(--clay); +} + +.model-node.hard_dependency { + border-color: rgba(191, 106, 84, 0.55); + border-style: dashed; + background: rgba(255, 246, 240, 0.94); + box-shadow: 0 14px 30px rgba(191, 106, 84, 0.12); +} + +.model-node.hard_dependency .node-header { + background: + linear-gradient(90deg, rgba(191, 106, 84, 0.08), transparent 60%), + var(--paper-strong); +} + +.model-node.hard_dependency .node-header > svg { + color: var(--clay); +} + +.model-node.hard_dependency .process::before { + content: "↳ "; + color: var(--clay); +} + +.model-node.overview-node { + border-radius: 12px; + box-shadow: 0 10px 24px rgba(56, 43, 35, 0.1); +} + +.model-node.overview-node .node-header { + min-height: 64px; + padding: 9px 10px; + border-radius: 12px 12px 0 0; +} + +.model-node.overview-node .process { + font-size: 12px; + line-height: 1.18; + overflow-wrap: anywhere; +} + +.model-node.overview-node .model-type { + font-size: 10px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.overview-node-summary { + display: flex; + flex-wrap: wrap; + gap: 5px; + padding: 8px 10px 10px; +} + +.overview-node-summary span { + min-width: 0; + max-width: 100%; + padding: 3px 6px; + border: 1px solid rgba(183, 166, 150, 0.35); + border-radius: 999px; + color: var(--muted); + background: rgba(255, 253, 247, 0.76); + font-size: 10px; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.overview-port-handles .react-flow__handle { + width: 7px; + height: 7px; + border-color: rgba(255, 250, 242, 0.9); + background: var(--line-strong); +} + +.overview-port-handles .react-flow__handle-right { + background: var(--accent); +} + +.hard-chip { + color: var(--clay); + border-color: rgba(191, 106, 84, 0.28) !important; + background: rgba(255, 246, 240, 0.9) !important; +} + +.node-header { + display: grid; + grid-template-columns: 1fr auto; + align-items: flex-start; + gap: 11px; + padding: 11px 12px 12px; + color: var(--ink); + background: var(--paper-strong); + border-bottom: 1px solid var(--line); +} + +.node-header > svg { + color: var(--accent); +} + +.process { + font-weight: 820; + font-size: 15px; + letter-spacing: 0; +} + +.model-type { + margin-top: 2px; + font-size: 12px; + color: var(--muted); + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; +} + +.node-meta { + display: flex; + flex-wrap: wrap; + gap: 6px; + padding: 10px 12px 0; +} + +.ports-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; + padding: 12px; +} + +.port-title { + margin-bottom: 6px; + color: var(--muted); + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.08em; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; +} + +.port { + position: relative; + display: flex; + align-items: center; + gap: 5px; + min-height: 24px; + margin: 4px 0; + padding: 4px 7px; + border: 1px solid var(--line); + border-radius: 8px; + background: rgba(255, 253, 247, 0.9); + font-size: 12px; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; +} + +.port::after { + content: attr(data-default); + position: absolute; + z-index: 60; + left: 50%; + bottom: calc(100% + 8px); + width: max-content; + max-width: 240px; + padding: 7px 9px; + color: var(--paper); + background: var(--ink); + border-radius: 9px; + box-shadow: 0 12px 28px rgba(56, 43, 35, 0.18); + font-family: "Avenir Next", "Trebuchet MS", "Segoe UI", sans-serif; + font-size: 11px; + line-height: 1.3; + white-space: normal; + opacity: 0; + pointer-events: none; + transform: translate(-50%, 4px); + transition: opacity 120ms ease, transform 120ms ease; +} + +.port:hover::after, +.port.active::after { + opacity: 1; + transform: translate(-50%, 0); +} + +.app-shell.has-candidate-popover .port.active::after { + opacity: 0; +} + +.port.output { + justify-content: flex-end; +} + +.port.previous { + color: var(--clay); +} + +.port.mapped { + border-color: rgba(31, 122, 83, 0.38); +} + +.port-candidate-button { + position: relative; + z-index: 8; + display: inline-flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; + width: 16px; + height: 16px; + padding: 0; + color: var(--accent); + background: rgba(255, 253, 247, 0.94); + border: 1px solid rgba(31, 122, 83, 0.34); + border-radius: 999px; + cursor: pointer; +} + +.port-candidate-button:hover, +.port-candidate-button:focus-visible { + color: #fffdfa; + background: var(--accent); + outline: none; +} + +.port-cycle-break-button { + position: relative; + z-index: 9; + display: inline-flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; + width: 18px; + height: 18px; + padding: 0; + color: #9b2d22; + background: rgba(255, 241, 236, 0.96); + border: 1px solid rgba(211, 66, 47, 0.45); + border-radius: 999px; + cursor: pointer; + box-shadow: 0 5px 12px rgba(211, 66, 47, 0.16); +} + +.port-cycle-break-button:hover, +.port-cycle-break-button:focus-visible { + color: #fffdfa; + background: #d3422f; + outline: none; +} + +.port.active .port-candidate-button { + color: var(--accent); + background: #fffdfa; +} + +@keyframes cycleTargetPulse { + 0%, + 100% { + box-shadow: + inset 3px 0 0 rgba(211, 66, 47, 0.78), + 0 0 0 3px rgba(211, 66, 47, 0.08); + } + 50% { + box-shadow: + inset 3px 0 0 rgba(211, 66, 47, 0.92), + 0 0 0 6px rgba(211, 66, 47, 0.15); + } +} + +.candidate-popover { + position: fixed; + z-index: 1200; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + overflow: hidden; + color: var(--ink); + border: 1px solid rgba(31, 122, 83, 0.28); + border-radius: 8px; + background: + linear-gradient(135deg, rgba(31, 122, 83, 0.08), transparent 42%), + rgba(255, 253, 247, 0.98); + box-shadow: 0 18px 42px rgba(56, 43, 35, 0.18); +} + +.candidate-popover-header { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; + padding: 12px 12px 9px; + border-bottom: 1px solid rgba(183, 166, 150, 0.32); +} + +.candidate-popover-header h3 { + margin: 3px 0 0; + overflow-wrap: anywhere; + font-size: 15px; +} + +.candidate-popover-list { + display: grid; + gap: 8px; + overflow: auto; + padding: 10px; +} + +.candidate-model-card { + display: grid; + gap: 5px; + width: 100%; + padding: 9px 10px; + color: var(--ink); + border: 1px solid rgba(183, 166, 150, 0.38); + border-left: 4px solid var(--accent); + border-radius: 8px; + background: rgba(255, 255, 255, 0.7); + font: inherit; + text-align: left; + cursor: pointer; +} + +.candidate-model-card:hover, +.candidate-model-card:focus-visible { + background: rgba(255, 253, 247, 0.96); + border-color: rgba(31, 122, 83, 0.42); + outline: none; +} + +.candidate-model-card strong { + overflow-wrap: anywhere; + font-size: 12px; +} + +.candidate-model-card span { + color: var(--muted); + font-size: 11px; +} + +.candidate-model-card small { + overflow-wrap: anywhere; + color: var(--muted); + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.port.required-input { + border-color: rgba(191, 106, 84, 0.72); + background: + linear-gradient(90deg, rgba(191, 106, 84, 0.16), rgba(255, 253, 247, 0.92) 62%), + rgba(255, 253, 247, 0.9); + box-shadow: inset 3px 0 0 rgba(191, 106, 84, 0.74); +} + +.port.required-input .react-flow__handle { + border-color: var(--clay); + background: #fff6f0; + box-shadow: + 0 0 0 3px rgba(255, 250, 242, 0.92), + 0 0 0 6px rgba(191, 106, 84, 0.13); +} + +.port.cycle-break-target { + border-color: rgba(211, 66, 47, 0.72); + background: + linear-gradient(90deg, rgba(211, 66, 47, 0.18), rgba(255, 253, 247, 0.92) 62%), + rgba(255, 253, 247, 0.9); + box-shadow: + inset 3px 0 0 rgba(211, 66, 47, 0.78), + 0 0 0 3px rgba(211, 66, 47, 0.08); +} + +.cycle-break-mode .port.cycle-break-target { + animation: cycleTargetPulse 1.35s ease-in-out infinite; +} + +.port.cycle-break-target .react-flow__handle { + border-color: #d3422f; + background: #fff1ec; + box-shadow: + 0 0 0 3px rgba(255, 250, 242, 0.92), + 0 0 0 7px rgba(211, 66, 47, 0.15); +} + +.port.highlighted { + border-color: var(--line); + background: #fffdfa; +} + +.port.focused { + border-color: rgba(31, 122, 83, 0.58); + background: var(--accent-soft); +} + +.port.active { + color: #fffdfa; + border-color: var(--accent); + background: var(--accent); +} + +.port.cycle-break-target.active, +.port.cycle-break-target.focused, +.port.cycle-break-target.highlighted { + color: #7a2018; + border-color: rgba(211, 66, 47, 0.78); + background: + linear-gradient(90deg, rgba(211, 66, 47, 0.2), rgba(255, 253, 247, 0.94) 64%), + rgba(255, 253, 247, 0.94); + box-shadow: + inset 3px 0 0 rgba(211, 66, 47, 0.86), + 0 0 0 4px rgba(211, 66, 47, 0.12); +} + +.port.cycle-break-target.active::after { + background: #7a2018; +} + +.port.cycle-break-target.active .port-cycle-break-button, +.port.cycle-break-target.focused .port-cycle-break-button, +.port.cycle-break-target.highlighted .port-cycle-break-button { + color: #9b2d22; + background: rgba(255, 241, 236, 0.98); +} + +.react-flow__handle { + width: 9px; + height: 9px; + border: 1px solid var(--line-strong); + background: var(--paper); + z-index: 25; + box-shadow: 0 0 0 3px rgba(255, 250, 242, 0.92); +} + +.react-flow__handle.call-handle { + width: 12px; + height: 36px; + opacity: 0; + pointer-events: none; +} + +.react-flow__edge.multiscale path { + stroke-dasharray: 7 5; +} + +.react-flow__edge.mapped_variable path { + stroke: var(--accent); +} + +.react-flow__edge.hard_dependency path { + stroke: var(--clay); +} + +.react-flow__edge.cycle_dependency path, +.react-flow__edge.cycle_edge path { + stroke: #d3422f; + filter: drop-shadow(0 0 5px rgba(211, 66, 47, 0.26)); +} + +.react-flow__edge.call_edge path { + stroke-width: 1.7; + stroke-dasharray: 3 6; + opacity: 0.7; +} + +.react-flow__edge.highlighted path { + stroke-width: 3; + stroke: var(--accent); +} + +.react-flow__edge.focused path { + stroke-width: 2.8; +} + +.react-flow__edge.highlighted { + z-index: 80 !important; +} + +.react-flow__edge.focused { + z-index: 70 !important; +} + +.react-flow__edge.dimmed { + opacity: 0.04; +} + +.overview-mode .react-flow__edge.variable_edge path { + stroke-width: 1.45 !important; + opacity: 0.74; +} + +.overview-mode .react-flow__edge.call_edge path { + stroke-width: 1.15 !important; + opacity: 0.4; +} + +.overview-mode .react-flow__edge.highlighted path, +.overview-mode .react-flow__edge.focused path { + stroke-width: 2.8 !important; + opacity: 1; +} + +.edge-chip { + position: absolute; + z-index: -1; + display: inline-flex; + align-items: center; + gap: 6px; + padding: 3px 7px; + color: var(--ink); + background: rgba(255, 250, 242, 0.94); + border: 1px solid var(--line); + border-radius: 999px; + box-shadow: 0 8px 20px rgba(56, 43, 35, 0.1); + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; + font-size: 11px; + line-height: 1; + pointer-events: none; + white-space: nowrap; +} + +.edge-chip.mapped_variable, +.edge-chip.multiscale { + border-color: rgba(31, 122, 83, 0.3); + background: rgba(248, 250, 241, 0.96); +} + +.edge-chip.hard_dependency { + border-color: rgba(191, 106, 84, 0.32); + background: rgba(255, 246, 240, 0.96); +} + +.edge-chip.cycle_dependency, +.edge-chip.cycle_edge { + color: #7a2018; + border-color: rgba(211, 66, 47, 0.44); + background: rgba(255, 238, 233, 0.98); +} + +.edge-chip small { + color: var(--muted); + font-size: 9px; + letter-spacing: 0.02em; + text-transform: uppercase; +} + +.edge-chip.highlighted { + z-index: 80; + color: #fffdfa; + border-color: var(--accent); + background: var(--accent); + box-shadow: 0 12px 24px rgba(31, 122, 83, 0.22); +} + +.edge-chip.highlighted small { + color: rgba(255, 253, 247, 0.72); +} + +.edge-chip.dimmed { + opacity: 0.04; +} + +.overview-mode .edge-chip:not(.highlighted):not(.focused), +.overview-mode .edge-terminal:not(.highlighted):not(.focused) { + display: none; +} + +.edge-chip.focused { + z-index: 70; + border-color: rgba(31, 122, 83, 0.36); + box-shadow: 0 10px 22px rgba(31, 122, 83, 0.14); +} + +.edge-terminal { + position: absolute; + z-index: 32; + --terminal-color: var(--line-strong); + width: 18px; + height: 10px; + pointer-events: none; + opacity: 0.95; +} + +.edge-terminal::before { + content: ""; + position: absolute; + top: 4px; + left: 2px; + right: 2px; + height: 2px; + border-radius: 999px; + background: var(--terminal-color); +} + +.edge-terminal.target::after { + content: ""; + position: absolute; + top: 1px; + width: 0; + height: 0; + border-top: 4px solid transparent; + border-bottom: 4px solid transparent; +} + +.edge-terminal[data-side="left"]::before { + left: 7px; +} + +.edge-terminal[data-side="right"]::before { + right: 7px; +} + +.edge-terminal.target[data-side="left"]::after { + right: 0; + border-left: 7px solid var(--terminal-color); +} + +.edge-terminal.target[data-side="right"]::after { + left: 0; + border-right: 7px solid var(--terminal-color); +} + +.edge-terminal.highlighted::before { + height: 3px; +} + +.edge-terminal.dimmed { + opacity: 0.12; +} + +.cycle-edge-card { + border-color: rgba(211, 66, 47, 0.34); + background: + linear-gradient(135deg, rgba(211, 66, 47, 0.1), transparent 58%), + rgba(255, 250, 242, 0.88); +} + +.cycle-break-button { + justify-content: center; + margin: 8px 0 4px; +} + +.inspector { + border-left: 1px solid var(--line); + background: rgba(255, 250, 242, 0.82); + backdrop-filter: blur(14px); + padding: 18px; + overflow: auto; +} + +.inspector:focus { + outline: none; +} + +.inspector.guided-focus { + animation: guided-panel-focus 1.8s ease-out; +} + +@keyframes guided-panel-focus { + 0% { + box-shadow: inset 0 0 0 3px rgba(31, 122, 83, 0.48), -18px 0 44px rgba(31, 122, 83, 0.16); + background: rgba(250, 255, 247, 0.94); + } + 60% { + box-shadow: inset 0 0 0 3px rgba(31, 122, 83, 0.36), -12px 0 34px rgba(31, 122, 83, 0.12); + } + 100% { + box-shadow: none; + background: rgba(255, 250, 242, 0.82); + } +} + +.inspector header { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 14px; +} + +.mapping-code-panel { + display: grid; + gap: 10px; +} + +.row-with-actions { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +.mapping-code { + width: 100%; + min-height: 260px; + resize: vertical; + border: 1px solid var(--line); + border-radius: 10px; + background: rgba(255, 253, 247, 0.9); + color: var(--ink); + padding: 10px; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; + font-size: 12px; + line-height: 1.45; +} + +.storage-grid { + display: grid; + gap: 8px; +} + +.path-status { + display: grid; + gap: 3px; + border: 1px solid var(--line); + border-radius: 10px; + background: rgba(255, 250, 242, 0.72); + padding: 9px; +} + +.path-status span { + color: var(--muted); + font-size: 11px; + font-weight: 720; +} + +.path-status strong { + color: var(--ink); + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; + font-size: 11px; + overflow-wrap: anywhere; +} + +.recent-mappings { + display: grid; + gap: 8px; + margin-top: 4px; +} + +.open-mapping-panel { + display: grid; + gap: 12px; +} + +.recent-mapping-list { + display: grid; + gap: 7px; +} + +.recent-mapping-item { + display: grid; + gap: 3px; + text-align: left; + border: 1px solid var(--line); + border-radius: 10px; + background: rgba(255, 253, 247, 0.82); + color: var(--ink); + padding: 9px; + cursor: pointer; +} + +.recent-mapping-item:hover, +.recent-mapping-item:focus-visible { + border-color: rgba(31, 122, 83, 0.32); + background: rgba(31, 122, 83, 0.08); +} + +.recent-mapping-item span { + font-weight: 720; +} + +.recent-mapping-item small { + color: var(--muted); + overflow-wrap: anywhere; +} + +.inline-field { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + gap: 8px; +} + +.inspector h2 { + font-size: 17px; +} + +.inspector h3 { + margin-top: 22px; + margin-bottom: 8px; + color: var(--muted); + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.row { + display: grid; + grid-template-columns: 84px minmax(0, 1fr); + gap: 8px; + padding: 8px 0; + border-top: 1px solid rgba(183, 166, 150, 0.35); +} + +.row span { + color: var(--muted); +} + +.row strong { + overflow-wrap: anywhere; + font-weight: 620; +} + +.variable-card { + border: 1px solid var(--line); + border-radius: 12px; + background: rgba(255, 250, 242, 0.75); + padding: 10px; +} + +.edge-detail-card { + display: grid; + gap: 4px; + margin-bottom: 14px; + padding: 10px; + border: 1px solid rgba(31, 122, 83, 0.24); + border-radius: 12px; + background: + linear-gradient(135deg, rgba(31, 122, 83, 0.08), transparent 58%), + rgba(255, 250, 242, 0.82); +} + +.edge-detail-card .diagnostic, +.edge-detail-card .empty-state { + margin-top: 6px; +} + +.variable-card .row:first-of-type { + margin-top: 8px; +} + +.variable-card-title { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; +} + +.variable-card-title span { + overflow-wrap: anywhere; + font-weight: 720; +} + +.variable-card-title small { + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.08em; +} + +.diagnostic, +.edit-suggestion, +.initialization-note, +.empty-state { + border: 1px solid var(--line); + border-radius: 12px; + background: rgba(255, 250, 242, 0.75); + padding: 10px; + color: var(--muted); +} + +.diagnostic, +.edit-suggestion, +.initialization-note { + display: flex; + align-items: center; + gap: 7px; +} + +.diagnostic, +.edit-suggestion { + color: var(--clay); + border-color: rgba(201, 97, 74, 0.28); + background: rgba(201, 97, 74, 0.09); +} + +.initialization-note { + color: #87533b; + border-color: rgba(191, 106, 84, 0.32); + background: rgba(191, 106, 84, 0.1); +} + +.initialization-list { + display: grid; + gap: 8px; +} + +.initialization-list.compact { + gap: 6px; +} + +.initialization-group { + display: grid; + gap: 7px; +} + +.initialization-group h4, +.provenance-block h4 { + margin: 6px 0 0; + color: var(--muted); + font-size: 11px; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; + font-weight: 700; +} + +.initialization-item { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + gap: 10px; + width: 100%; + padding: 9px 10px; + color: var(--ink); + background: rgba(255, 250, 242, 0.78); + border: 1px solid rgba(191, 106, 84, 0.28); + border-left: 4px solid var(--clay); + border-radius: 10px; + font: inherit; + text-align: left; + cursor: pointer; +} + +.initialization-item:hover, +.initialization-item:focus-visible { + background: rgba(255, 246, 240, 0.96); + outline: none; +} + +.initialization-item span { + overflow: hidden; + color: var(--muted); + font-size: 12px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.initialization-item strong { + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; + font-size: 12px; +} + +.initialization-item small { + grid-column: 1 / -1; + color: var(--muted); + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.06em; +} + +.initialization-item.previous_time_step { + border-left-color: var(--ochre); +} + +.initialization-item.mapped_unresolved { + border-left-color: var(--accent); +} + +.warning-list, +.provenance-block { + display: grid; + gap: 8px; +} + +.warning-group { + display: grid; + gap: 7px; +} + +.warning-group h4 { + margin: 6px 0 0; + color: var(--muted); + font-size: 11px; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; + font-weight: 700; +} + +.warning-item, +.provenance-edge { + display: grid; + gap: 3px; + width: 100%; + padding: 9px 10px; + border: 1px solid rgba(201, 144, 53, 0.28); + border-left: 4px solid var(--ochre); + border-radius: 10px; + background: rgba(255, 250, 242, 0.78); + color: var(--ink); + font: inherit; + text-align: left; + cursor: pointer; +} + +.warning-item.error { + border-color: rgba(191, 106, 84, 0.34); + border-left-color: var(--clay); + background: rgba(191, 106, 84, 0.08); +} + +.warning-item.info { + border-color: rgba(127, 143, 115, 0.26); + border-left-color: var(--sage); + background: rgba(127, 143, 115, 0.08); +} + +.provenance-edge { + border-color: rgba(183, 166, 150, 0.42); + border-left-color: var(--line-strong); +} + +.provenance-edge.mapped_variable { + border-left-color: var(--accent); +} + +.provenance-edge.hard_dependency { + border-left-color: var(--clay); +} + +.warning-item:hover, +.warning-item:focus-visible, +.provenance-edge:hover, +.provenance-edge:focus-visible { + background: rgba(255, 246, 240, 0.96); + outline: none; +} + +.warning-item strong, +.provenance-edge strong { + overflow-wrap: anywhere; + font-size: 12px; +} + +.warning-item span, +.provenance-edge span, +.provenance-edge small { + color: var(--muted); + font-size: 11px; + line-height: 1.25; +} + +.provenance-block { + margin-top: 10px; +} + +.empty-state.compact { + padding: 7px 8px; + font-size: 11px; +} + +.live-session { + border-left: 1px solid rgba(183, 166, 150, 0.36); + padding-left: 10px; +} + +.live-pill { + display: inline-flex; + align-items: center; + height: 28px; + padding: 0 9px; + border: 1px solid rgba(191, 106, 84, 0.38); + border-radius: 999px; + color: var(--clay); + background: rgba(191, 106, 84, 0.08); + font-size: 11px; + font-weight: 800; + text-transform: uppercase; +} + +.live-pill.connected { + border-color: rgba(31, 122, 83, 0.34); + color: var(--accent); + background: rgba(31, 122, 83, 0.09); +} + +.metric-button:disabled { + cursor: not-allowed; + opacity: 0.42; +} + +.model-browser { + display: grid; + gap: 7px; +} + +.model-browser-control { + display: grid; + gap: 4px; +} + +.model-browser-control span, +.parameter-row label { + color: var(--muted); + font-size: 11px; + font-weight: 700; +} + +.model-browser-control select, +.model-browser-control input, +.parameter-row input, +.parameter-row select { + min-width: 0; + border: 1px solid rgba(183, 166, 150, 0.44); + border-radius: 8px; + background: rgba(255, 255, 255, 0.78); + color: var(--ink); + font: inherit; +} + +.model-browser-control select, +.model-browser-control input { + height: 32px; + padding: 0 8px; +} + +.model-browser-item { + display: grid; + gap: 7px; + padding: 8px 9px; + border: 1px solid rgba(183, 166, 150, 0.34); + border-radius: 10px; + background: rgba(255, 255, 255, 0.62); +} + +.model-browser-item.add-model-config { + gap: 10px; + padding-bottom: 0; +} + +.model-browser-title { + display: grid; + gap: 2px; +} + +.model-browser-item strong { + overflow-wrap: anywhere; + font-size: 12px; +} + +.model-browser-item span { + color: var(--muted); + font-size: 11px; +} + +.existing-model-editor { + display: grid; + gap: 8px; + margin-top: 12px; + padding-top: 10px; + border-top: 1px solid rgba(183, 166, 150, 0.32); +} + +.variable-mapping-editor { + display: grid; + gap: 8px; + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid rgba(183, 166, 150, 0.32); +} + +.variable-mapping-editor h4 { + margin: 0; + color: var(--muted); + font-size: 11px; + font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; + font-weight: 700; +} + +.initialization-editor { + display: grid; + gap: 12px; +} + +.initialization-editor-group { + display: grid; + gap: 8px; +} + +.initialization-editor-group h3 { + margin: 0; +} + +.initialization-editor-row { + display: grid; + grid-template-columns: minmax(0, 0.85fr) minmax(0, 1fr) minmax(82px, 0.72fr) auto; + gap: 7px; + align-items: center; + padding: 9px; + border: 1px solid rgba(191, 106, 84, 0.26); + border-left: 4px solid var(--clay); + border-radius: 8px; + background: rgba(255, 250, 242, 0.78); +} + +.initialization-editor-row.provided { + border-color: rgba(31, 122, 83, 0.24); + border-left-color: var(--accent); +} + +.initialization-editor-row label { + overflow-wrap: anywhere; + font-weight: 700; +} + +.initialization-editor-row input, +.initialization-editor-row select { + min-width: 0; + height: 30px; + border: 1px solid rgba(183, 166, 150, 0.44); + border-radius: 8px; + background: rgba(255, 255, 255, 0.78); + color: var(--ink); + font: inherit; +} + +.initialization-editor-row input { + padding: 0 8px; +} + +.initialization-editor-row small { + grid-column: 1 / -1; + color: var(--muted); + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.parameter-row { + display: grid; + grid-template-columns: minmax(0, 0.75fr) minmax(0, 1fr) minmax(78px, 0.8fr); + gap: 6px; + align-items: center; +} + +.parameter-row input, +.parameter-row select { + height: 28px; + padding: 0 7px; + font-size: 12px; +} + +.rate-editor { + display: grid; + gap: 6px; + padding: 7px; + border: 1px solid rgba(183, 166, 150, 0.28); + border-radius: 8px; + background: rgba(252, 249, 244, 0.72); +} + +.add-model-footer { + position: sticky; + bottom: -18px; + display: flex; + justify-content: flex-end; + margin: 2px -9px 0; + padding: 10px 9px; + border-top: 1px solid rgba(183, 166, 150, 0.34); + border-radius: 0 0 10px 10px; + background: rgba(255, 250, 242, 0.94); + backdrop-filter: blur(8px); +} + +.rate-summary { + color: var(--muted); + font-size: 11px; + line-height: 1.35; +} + +.rate-clock-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 6px; +} + +.rate-clock-row label { + display: grid; + gap: 4px; + color: var(--muted); + font-size: 11px; + font-weight: 700; +} + +.rate-clock-row input { + min-width: 0; + height: 28px; + border: 1px solid rgba(183, 166, 150, 0.44); + border-radius: 8px; + background: rgba(255, 255, 255, 0.78); + color: var(--ink); + font: inherit; + padding: 0 7px; +} + +.metric-button.danger { + border-color: rgba(191, 106, 84, 0.42); + color: #9b3f2e; + background: rgba(255, 242, 236, 0.86); +} + +@media (max-width: 900px) { + .app-shell { + grid-template-columns: 1fr; + } + + .inspector { + position: absolute; + z-index: 28; + top: 150px; + right: 16px; + bottom: 16px; + width: min(360px, calc(100% - 32px)); + border: 1px solid var(--line); + border-radius: 14px; + box-shadow: 0 18px 45px var(--shadow); + } + + .relationship-legend, + .scale-controls, + .floating-panel { + top: 150px; + } + + .scale-controls { + left: 220px; + } +} + +@media (max-width: 720px) { + .topbar { + top: 10px; + left: 10px; + right: 10px; + gap: 8px; + padding: 9px 10px; + } + + .topbar::before { + display: none; + } + + .brand-block { + min-width: 128px; + } + + .search-box { + flex-basis: 100%; + max-width: none; + min-width: 0; + } + + .metrics { + margin-left: 0; + } + + .relationship-legend, + .scale-controls, + .floating-panel { + top: 132px; + left: 10px; + right: 10px; + width: auto; + } + + .scale-controls { + top: 326px; + } + + .react-flow__minimap { + display: none; + } +} + +/* Mapping dialog */ +.mapping-dialog-overlay { + position: fixed; + inset: 0; + z-index: 1000; + background: rgba(49, 39, 33, 0.38); + display: flex; + align-items: center; + justify-content: center; +} + +.mapping-dialog { + background: var(--paper); + border: 1px solid var(--line-strong); + border-radius: 10px; + box-shadow: 0 8px 32px var(--shadow); + width: 360px; + max-width: calc(100vw - 32px); + display: flex; + flex-direction: column; + gap: 0; + overflow: hidden; +} + +.mapping-dialog-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 14px 16px 10px; + border-bottom: 1px solid var(--line); +} + +.mapping-dialog-header .eyebrow { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.07em; + color: var(--muted); +} + +.mapping-dialog-body { + padding: 14px 16px; + display: flex; + flex-direction: column; + gap: 14px; +} + +.mapping-port-summary { + display: flex; + align-items: center; + gap: 8px; + background: var(--paper-strong); + border: 1px solid var(--line); + border-radius: 6px; + padding: 10px 12px; +} + +.mapping-port { + display: flex; + flex-direction: column; + gap: 1px; + flex: 1; + min-width: 0; +} + +.mapping-port small { + font-size: 10px; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--muted); +} + +.mapping-port strong { + font-size: 12px; + font-weight: 700; + color: var(--accent); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.mapping-port span { + font-size: 11px; + color: var(--ink); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.mapping-port.target strong { + color: var(--clay); +} + +.mapping-arrow { + font-size: 18px; + color: var(--muted); + flex-shrink: 0; +} + +.mapping-mode-section { + display: flex; + flex-direction: column; + gap: 6px; +} + +.mapping-mode-label { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.06em; + color: var(--muted); + margin-bottom: 2px; +} + +.mapping-radio { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + cursor: pointer; + padding: 4px 0; +} + +.mapping-radio input[type="radio"] { + accent-color: var(--accent); + width: 14px; + height: 14px; + flex-shrink: 0; +} + +.mapping-scale-picker { + display: flex; + flex-direction: column; + gap: 4px; +} + +.mapping-checkbox { + display: flex; + align-items: center; + gap: 8px; + font-size: 13px; + cursor: pointer; + padding: 2px 0; +} + +.mapping-checkbox input[type="checkbox"] { + accent-color: var(--accent); + width: 14px; + height: 14px; + flex-shrink: 0; +} + +.mapping-dialog-footer { + display: flex; + justify-content: flex-end; + gap: 8px; + padding: 10px 16px 14px; + border-top: 1px solid var(--line); +} + +.accent-button { + background: var(--accent); + color: #fff; + border-color: transparent; +} + +.accent-button:hover:not(:disabled) { + background: var(--sage-dark); + border-color: transparent; +} diff --git a/frontend/src/types.ts b/frontend/src/types.ts new file mode 100644 index 000000000..bb802aabb --- /dev/null +++ b/frontend/src/types.ts @@ -0,0 +1,256 @@ +export type GraphPortRole = "input" | "output" | "environment_input" | "environment_output"; + +export type GraphPort = { + id: string; + name: string; + role: GraphPortRole; + default: unknown; + defaultJulia: string; + expectedType: string; +}; + +export type SelectorDescriptor = { + type: string; + multiplicity: "one" | "optional_one" | "many"; + criteria: Record; + julia: string; +}; + +export type ModelParameter = { + value: unknown; + julia: string; + type: string; + juliaType: string; +}; + +export type ApplicationGraphNode = { + id: string; + applicationId: string; + name: string | null; + process: string; + modelType: string; + modelName: string; + module: string; + package: string | null; + modelParameters: Record; + selector: SelectorDescriptor; + targetIds: unknown[]; + targetCount: number; + targetScales: string[]; + targetKinds: string[]; + targetSpecies: string[]; + targetInstances: string[]; + timestep: unknown; + clock: unknown; + inputs: GraphPort[]; + outputs: GraphPort[]; + environmentInputs: GraphPort[]; + environmentOutputs: GraphPort[]; + modelStorage: "shared_application" | "per_object_override"; + objectOverrides: Array>; +}; + +export type ObjectGraphNode = { + id: string; + objectId: unknown; + scale: string | null; + kind: string | null; + species: string | null; + name: string | null; + instance: string | null; + parent: string | null; + children: string[]; + hasGeometry: boolean; + hasStatus: boolean; +}; + +export type InstanceDescriptor = { + id: string; + name: string; + rootId: unknown; + kind: string | null; + species: string | null; + objectIds: unknown[]; + applicationIds: string[]; + instanceOverrides: string[]; + objectOverrides: Array>; + parametersType: string; +}; + +export type ExecutionGraphNode = { + id: string; + applicationId: string; + applicationNodeId: string; + objectId: unknown; + objectNodeId: string; + modelType: string; + modelParameters: Record; + overridden: boolean; +}; + +export type SceneGraphEdge = { + id: string; + source: string; + target: string; + sourcePort?: string | null; + targetPort?: string | null; + sourceVariable?: string | null; + targetVariable?: string | null; + sourceApplicationId?: string; + targetApplicationId?: string; + sourceObjectIds?: unknown[]; + targetObjectIds?: unknown[]; + kind: "value_binding" | "inferred_same_object" | "previous_timestep" | "manual_call" | "object_topology" | "application_target" | "update_order" | "environment_binding" | string; + origin?: string; + multiplicity?: string; + policy?: string; + selector?: SelectorDescriptor; + call?: string; + projection?: "applications" | "topology" | "resolved" | "targets"; + cycle: boolean; +}; + +export type InitializationDescriptor = { + applicationId: string; + objectId: unknown; + variable: string; + role: GraphPortRole; + disposition: "generated" | "producer_bound" | "supplied" | "environment_bound" | "unresolved"; + value: unknown; + valueJulia: string; + expectedType: string; + sourceApplicationIds: string[]; + sourceObjectIds: unknown[]; + sourceVariable: string | null; + origin: string; + previousTimeStep: boolean; +}; + +export type GraphDiagnostic = { + code: string; + severity: "error" | "warning" | "info"; + message: string; + phase: string; + applicationIds: string[]; + objectIds: unknown[]; + variable: string | null; + suggestions: string[]; +}; + +export type CycleDescriptor = { + id: string; + applicationIds: string[]; + edges: Array<{ sourceApplicationId: string; targetApplicationId: string }>; + breakCandidates: Array<{ + applicationId: string; + objectId: unknown; + input: string; + sourceApplicationIds: string[]; + sourceObjectIds: unknown[]; + sourceVariable: string; + selector: SelectorDescriptor; + }>; +}; + +export type ModelConstructorField = { + name: string; + declaredType: string; + hasDefault: boolean; + default: unknown; + defaultJulia: string | null; + defaultType: string | null; + typeParameter: string | null; + inferredChoice: string; + choices: string[]; +}; + +export type ModelDescriptor = { + type: string; + name: string; + module: string; + package: string | null; + process: string | null; + processType: string | null; + inputs: Record; + outputs: Record; + environmentInputs: Record; + environmentOutputs: Record; + timespec?: string | null; + timestepHint?: string | null; + meteoHint?: string | null; + outputPolicy?: string | null; + constructor: { + fields: ModelConstructorField[]; + parameterGroups: Record; + hasZeroArgConstructor: boolean; + constructible: boolean; + }; +}; + +export type SceneGraphView = { + schemaVersion: number; + level: "applications" | "topology" | "resolved"; + metadata: { + title: string; + sceneRevision: number; + objectCount: number; + instanceCount: number; + applicationCount: number; + executionCount: number; + bindingCount: number; + callCount: number; + unresolvedInitializationCount: number; + cyclic: boolean; + strictlyCompiled: boolean; + }; + objects: ObjectGraphNode[]; + instances: InstanceDescriptor[]; + applications: ApplicationGraphNode[]; + executions: ExecutionGraphNode[]; + edges: SceneGraphEdge[]; + modelLibrary: ModelDescriptor[]; + initialization: InitializationDescriptor[]; + diagnostics: GraphDiagnostic[]; + cycles: CycleDescriptor[]; + availableActions: string[]; +}; + +export type GraphViewMode = "applications" | "topology" | "resolved"; +export type DetailMode = "overview" | "detail"; + +export type RuntimeApplicationNode = ApplicationGraphNode & { + nodeKind: "application"; + detailMode: DetailMode; + cyclic: boolean; + requiredInputPortIds: string[]; + candidatePortIds: string[]; + previousTimeStepPortIds: string[]; + cycleBreakInputPortIds: string[]; + cycleBreakMode: boolean; + onCandidateClick?: (port: GraphPort, anchor: { x: number; y: number }) => void; + onPortClick?: (port: GraphPort) => void; + onCycleBreak?: (application: ApplicationGraphNode, port: GraphPort) => void; +}; + +export type RuntimeEntityNode = { + nodeKind: "object" | "execution"; + title: string; + subtitle: string; + badges: string[]; + inputPortIds?: string[]; + outputPortIds?: string[]; + detail: ObjectGraphNode | ExecutionGraphNode; +}; + +export type EditorState = { + ok: boolean; + graph: SceneGraphView; + diagnostics: string[]; + canUndo: boolean; + canRedo: boolean; + url: string; + sceneCode?: string; + autosavePath?: string | null; + savePath?: string | null; + recentPaths?: string[]; +}; diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 000000000..c0ea4e10f --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": ["src"] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 000000000..da209f813 --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +export default defineConfig({ + plugins: [react()], + build: { + outDir: "dist", + emptyOutDir: true, + manifest: true, + }, +}); diff --git a/skills/plantsimengine/SKILL.md b/skills/plantsimengine/SKILL.md index 2991f609f..166a4da33 100644 --- a/skills/plantsimengine/SKILL.md +++ b/skills/plantsimengine/SKILL.md @@ -30,14 +30,26 @@ microclimate work. Translate released mapping-era code using - Search for process definitions with `rg "@process|abstract type Abstract.*Model" src examples docs test`. - Search for model APIs with `rg "inputs_\\(|outputs_\\(|PlantSimEngine.run!|dep\\(" src examples test`. 3. Check model IO with `inputs(model)`, `outputs(model)`, `variables(model)`, and process identity with `process(model)` when available. -4. Compile scenarios early with `refresh_bindings!(scene)` and inspect +4. Validate scenarios early with `explain_initialization(scene)` and inspect `explain_scene_applications`, `explain_bindings`, `explain_calls`, `explain_schedule`, `explain_writers`, and environment bindings. +5. Use `explain_initialization(scene)` before running to distinguish supplied, + generated, producer-bound, environment-bound, and unresolved variables. ## User Workflow: Existing Models ### Build the object graph +For one object with ordinary same-object inference, use the thin constructor +that lowers directly to the same Scene/Object runtime: + +```julia +scene = Scene(ModelA(), ModelB(); status=(initial_value=1.0,)) +``` + +Use the explicit object graph below as soon as models require different target +sets or scenario policies. + Represent every runtime entity as an `Object` with stable identity and useful labels. Plant topology remains scenario-defined. @@ -83,8 +95,9 @@ scene = Scene(scene_objects...; applications=applications, environment=backend) ``` Use explicit application names when a process is applied more than once to the -same object set. Selectors can disambiguate producers by `process=` and -`application=`. +same object set. Singular producer references use `application=`. A +`Many(process=...)` filter is reserved for explicit discovery across several +applications, such as mounted template instances. ### Couple values with Inputs @@ -96,8 +109,8 @@ ModelSpec(AllocationModel(); name=:allocation) |> Inputs( :leaf_carbon => Many( scale=:Leaf, - within=Self(), - process=:leaf_carbon, + within=Subtree(), + application=:leaf_carbon, var=:leaf_carbon, ), ) @@ -106,8 +119,9 @@ ModelSpec(AllocationModel(); name=:allocation) |> Semantics: - `One(...)`, `OptionalOne(...)`, and `Many(...)` make multiplicity explicit. -- `Self()` is the consumer object or subtree. `SelfPlant()` is the nearest - containing plant. `SceneScope()` selects across the scene. +- `Self()` is only the consumer object. `Subtree()` is that object plus its + descendants. `SelfPlant()` is the nearest containing plant and its subtree. + `SceneScope()` selects across the scene. - Same-rate scalar and many-object inputs use shared `Ref`s or reference vectors where possible. - Cross-rate values use typed temporal streams. @@ -147,7 +161,7 @@ ModelSpec(DailyPlantModel(); name=:daily_plant) |> Inputs( :leaf_fluxes => Many( scale=:Leaf, - within=Self(), + within=Subtree(), var=:flux, policy=Integrate(), window=Dates.Day(1), @@ -185,24 +199,22 @@ the affected environment bindings. ### Validate the compiled scenario ```julia -compiled = refresh_bindings!(scene) -explain_scene_applications(compiled) -explain_bindings(compiled) -explain_calls(compiled) -explain_schedule(compiled) -explain_writers(compiled) -explain_model_bundles(compiled) +explain_scene_applications(scene) +explain_bindings(scene) +explain_calls(scene) +explain_schedule(scene) +explain_writers(scene) +explain_model_bundles(scene) ``` -Run with `simulation = run!(scene; steps=n)` and inspect -`collect_outputs(simulation)` or `explain_outputs(simulation)`. Use -`run!(scene; tracked_outputs=OutputRequest(...))` when the user needs -resampled scene outputs; requests are materialized from retained typed streams -after the run, and dynamic objects are exported only across their own sample -interval. With explicit `tracked_outputs`, retained streams are pruned to -requested application/variable streams plus temporal `Inputs(...)` -dependencies; use `explain_output_retention(simulation)` to inspect that -decision. +Run with `simulation = run!(scene; steps=n, outputs=:none)`. Use +`outputs=:all` or `outputs=OutputRequest(...)` when the user needs retained or +resampled outputs. Requests are materialized from retained typed streams after +the run, and dynamic objects are exported only across their own sample +interval. Continue the same time/environment/multirate state with +`continue!(simulation; steps=n)` or `step!(simulation)`. Use +`explain_output_retention(scene; outputs=...)` before a long run and +`explain_output_retention(simulation)` afterward. ## Modeler Workflow: New Or Wrapped Models @@ -241,7 +253,8 @@ Rules: - Read and write model state through `status`. Do not store timestep-varying state in the model object. - Read weather through `meteo` and physical constants through `constants`. - In scene runs, `extra` is a `SceneRunContext`. Use its public hard-call and - lifecycle APIs rather than attaching unrelated user data. + lifecycle APIs rather than attaching unrelated user data. Obtain the live + scene with `runtime_scene(extra)`; do not inspect `extra.compiled.scene`. - If a variable appears in both `inputs_` and `outputs_` with the same name, remember that `variables(model)` merges declarations and later output declarations win. ### Wrapping existing code @@ -304,13 +317,24 @@ PlantSimEngine.meteo_hint(::Type{<:MyModel}) = ( ) ``` -Parallel traits are mainly for single-scale execution. Multirate MTG runs are currently sequential. +There is currently no public parallel executor API. Do not promise parallel or +distributed execution; establish correctness and independence before any +future parallel implementation. + +### Source ownership + +- `src/scene_object_api.jl` is only the dependency-ordered include boundary. +- `src/scene_object/registry_topology.jl` owns objects, instances, and lifecycle. +- `src/scene_object/selectors.jl` owns selector resolution. +- `src/scene_object/compilation.jl` owns bindings, calls, writers, and schedules. +- `src/scene_object/environment_bindings.jl` owns environment coupling. +- `src/scene_object/runtime_outputs.jl` owns execution and output streams. ## Validation Checklist For user scenarios: -- `refresh_bindings!(scene)` succeeds. +- `explain_initialization(scene)` contains no unresolved required values. - `explain_scene_applications` shows the expected application/object pairs. - `explain_bindings` shows the intended source ids, source applications, temporal policies, and carrier semantics. @@ -320,7 +344,7 @@ For user scenarios: batches; unexpected one-object batches usually indicate heterogeneous model, status, binding, or environment types. - Cycles are absent or intentionally broken with `PreviousTimeStep`. -- Ambiguous producers are resolved with `process=` or `application=`. +- Ambiguous singular producers are resolved with `application=`. - Environment explanations show the expected provider, cell, geometry source, source variables, and whether a temporal sampler is compiled. diff --git a/src/ModelSpec.jl b/src/ModelSpec.jl index c21c3a81a..e119a12af 100644 --- a/src/ModelSpec.jl +++ b/src/ModelSpec.jl @@ -32,9 +32,10 @@ end Scenario-level declaration that a model updates variables which may also be computed by another model at the same scale. -`after` is intentionally scenario-level metadata: the model implementation stays -reusable, while the simulation setup can declare ordering constraints that only -exist in this coupling. +`after` contains canonical application identifiers. It is intentionally +scenario-level metadata: the model implementation stays reusable, while the +simulation setup can declare ordering constraints that only exist in this +coupling. """ struct Updates{V,A} variables::V @@ -56,14 +57,14 @@ _normalize_updates(updates::Updates) = (updates,) function _normalize_updates(updates::Tuple) all(update -> update isa Updates, updates) || error( - "Unsupported updates tuple. Use `Updates(:var; after=:process)` entries." + "Unsupported updates tuple. Use `Updates(:var; after=:application)` entries." ) return updates end function _normalize_updates(updates::AbstractVector) all(update -> update isa Updates, updates) || error( - "Unsupported updates vector. Use `Updates(:var; after=:process)` entries." + "Unsupported updates vector. Use `Updates(:var; after=:application)` entries." ) return Tuple(updates) end @@ -73,7 +74,7 @@ function _normalize_updates(updates) updates == () && return () error( "Unsupported updates metadata `$(updates)` of type `$(typeof(updates))`. ", - "Use `Updates(:var; after=:process)` or a tuple/vector of `Updates`." + "Use `Updates(:var; after=:application)` or a tuple/vector of `Updates`." ) end diff --git a/src/PlantSimEngine.jl b/src/PlantSimEngine.jl index 6e8913140..2e90a361b 100644 --- a/src/PlantSimEngine.jl +++ b/src/PlantSimEngine.jl @@ -10,6 +10,8 @@ import CSV import Term import Markdown +import JSON +import InteractiveUtils import Base: position # For MTG compatibility: @@ -54,45 +56,92 @@ include("traits/table_traits.jl") include("processes/models_inputs_outputs.jl") include("processes/process_generation.jl") +# Model discovery for static and interactive graph tooling: +include("model_discovery.jl") + # Scene timing and environment runtime: include("time/runtime/clocks.jl") include("time/runtime/output_export.jl") include("time/runtime/meteo_sampling.jl") include("time/runtime/environment_backends.jl") +# Static Scene graph compilation and visualization: +include("visualization/scene_graph_view.jl") +include("visualization/scene_graph_editor_api.jl") + # Fitting include("evaluation/fit.jl") # Examples include("examples_import.jl") +""" + PlantSimEngine.Advanced + +Qualified compiler, cache, and low-level runtime extension APIs. These symbols +are available for diagnostics, package integration, and compiler development, +but are intentionally not part of the default user namespace. +""" +module Advanced +import ..PlantSimEngine: + SceneRegistry, + CompiledScene, + CompiledSceneApplication, + CompiledSceneInputBinding, + CompiledSceneCallBinding, + CompiledEnvironmentBinding, + CompiledEnvironmentBindings, + ObjectRefVector, + TimeStepTable, + compile_scene, + refresh_bindings!, + refresh_environment_bindings!, + compile_environment_bindings, + bind_environment, + bindings_dirty, + environment_bindings_dirty, + scene_revision, + environment_revision, + compiled_bindings, + compiled_environment_bindings + +export SceneRegistry +export CompiledScene, CompiledSceneApplication +export CompiledSceneInputBinding, CompiledSceneCallBinding +export CompiledEnvironmentBinding, CompiledEnvironmentBindings +export ObjectRefVector, TimeStepTable +export compile_scene, refresh_bindings!, refresh_environment_bindings! +export compile_environment_bindings, bind_environment +export bindings_dirty, environment_bindings_dirty +export scene_revision, environment_revision +export compiled_bindings, compiled_environment_bindings +end + export PreviousTimeStep export AbstractModel export ClockSpec export SchedulePolicy, HoldLast, Interpolate, Integrate, Aggregate export AbstractTimeReducer, MeanWeighted, MeanReducer, SumReducer, MinReducer, MaxReducer, FirstReducer, LastReducer, RadiationEnergy export OutputRequest, collect_outputs -export Scene, Object, ObjectId, SceneRegistry, ObjectTemplate, ObjectInstance, Override -export add_organ!, register_object!, remove_object!, reparent_object!, move_object!, update_geometry!, refresh_bindings! -export bindings_dirty, environment_bindings_dirty, scene_revision, environment_revision -export compiled_bindings, compiled_environment_bindings, mark_environment_binding_dirty! -export refresh_environment_bindings!, compile_environment_bindings, bind_environment +export Scene, Object, ObjectId, ObjectTemplate, ObjectInstance, Override +export add_organ!, register_object!, remove_object!, reparent_object!, move_object!, update_geometry! +export mark_environment_binding_dirty! export objects_from_mtg, object_ids, scene_objects, resolve_object_ids, resolve_objects, explain_objects, explain_instances, explain_scopes export geometry, position, bounds -export CompiledScene, CompiledSceneApplication, CompiledSceneInputBinding, CompiledSceneCallBinding -export compile_scene, explain_scene_applications, explain_bindings, explain_calls, explain_model_bundles, explain_writers -export ObjectRefVector, input_carrier, input_value, has_reference_carrier -export SceneRunContext, SceneCallTarget, SceneSimulation, scene_outputs, explain_outputs +export explain_scene_applications, explain_bindings, explain_calls, explain_model_bundles, explain_writers +export input_carrier, input_value, has_reference_carrier +export SceneRunContext, SceneCallTarget, SceneSimulation, runtime_scene, current_step, scene_outputs, explain_outputs +export explain_initialization export explain_execution_plan, explain_output_retention -export CompiledEnvironmentBinding, CompiledEnvironmentBindings, explain_environment_bindings -export SceneScope, Self, SelfPlant, Ancestor, Scope, Kind, Species, Scale, Relation +export explain_environment_bindings +export SceneScope, Self, Subtree, SelfPlant, Ancestor, Scope, Kind, Species, Scale, Relation export One, OptionalOne, Many, ObjectAddress, object_address export Input, Call, AppliesTo, Inputs, Calls, TimeStep, Environment export application_name, applies_to, value_inputs, model_calls, environment_config export ModelSpec, Updates, OutputRouting export call_target, call_targets, run_call!, explain_schedule export RMSE, NRMSE, EF, dr -export Status, TimeStepTable +export Status export @process, process export init_variables, dep export inputs, outputs, variables @@ -100,13 +149,30 @@ export timespec, output_policy, timestep_hint, meteo_hint export meteo_bindings, meteo_window, output_routing, updates export meteo_inputs, meteo_inputs_, meteo_outputs, meteo_outputs_ export validate_meteo_inputs +export available_processes, available_models, model_descriptor, model_constructor_descriptor +export SceneGraphDiagnostic, SceneCompilationReport, SceneGraphView +export compile_scene_report, compile_scene_graph, scene_graph_view +export scene_graph_view_json, scene_graph_view_html, write_scene_graph_view +export AbstractSceneGraphEdit, AddSceneApplication, RemoveSceneApplication, RemoveSceneTemplateApplication +export ReplaceSceneApplicationModel, UpdateSceneApplication, UpdateSceneTemplateApplication +export RenameSceneApplication, SetSceneApplicationTargets +export SetSceneInputBinding, RemoveSceneInputBinding, SetSceneCallBinding, RemoveSceneCallBinding +export SetSceneApplicationTimeStep, SetSceneApplicationEnvironment +export SetSceneOutputRouting, SetSceneUpdateOrdering +export MarkScenePreviousTimeStep, UnmarkScenePreviousTimeStep, BreakSceneCycle +export AddSceneObject, RemoveSceneObject, ReparentSceneObject +export SetSceneObjectStatus, SetSceneObjectStatuses, RemoveSceneObjectStatus, SetSceneObjectMetadata +export SetSceneInstanceOverride, RemoveSceneInstanceOverride +export SetSceneObjectOverride, RemoveSceneObjectOverride, apply_scene_graph_edit +export AbstractSceneGraphEditorSession, edit_graph, current_scene, apply_edit!, undo!, redo! export AbstractEnvironmentBackend, EnvironmentSupport, GlobalConstant export environment_backend, environment_variables, base_step_seconds export sample, sample_environment, scatter!, update_index! export scatter_environment_outputs! export explain_environment -export run! +export run!, continue!, step! export fit +export Advanced # Re-exporting PlantMeteo main functions: export Atmosphere, Constants, Weather diff --git a/src/model_discovery.jl b/src/model_discovery.jl new file mode 100644 index 000000000..f483a5e10 --- /dev/null +++ b/src/model_discovery.jl @@ -0,0 +1,307 @@ +const _MODEL_PARAMETER_TYPE_CHOICES = ( + :float, + :integer, + :boolean, + :symbol, + :string, + :nothing, + :julia, +) + +const _MODEL_DISCOVERY_EXCLUDED_NAMES = Set{Symbol}([ + :ObjectModelOverrides, + :ModelSpec, +]) + +""" + available_processes() + +Return process abstract model types visible in the current Julia session. +Loading another package with `using PackageName` makes its process and model +types discoverable without a separate registry. +""" +function available_processes() + processes = Type[] + for type in _abstract_model_subtypes() + _is_process_type(type) || continue + push!(processes, type) + end + return sort!(unique(processes); by=type -> string(process_(type))) +end + +""" + available_models() + available_models(process::Symbol) + available_models(process_type::Type{<:AbstractModel}) + +Return concrete model implementation types visible in the current Julia +session. +""" +function available_models() + models = Type[] + for type in _abstract_model_subtypes() + _is_available_model_type(type) || continue + push!(models, type) + end + return sort!(unique(models); by=type -> string(_process_name_for_type(type), ".", nameof(type))) +end + +available_models(process_name::Symbol) = + filter(type -> _process_name_for_type(type) == process_name, available_models()) + +function available_models(process_type::Type{<:AbstractModel}) + process_name = _is_process_type(process_type) ? + process_(process_type) : + _process_name_for_type(process_type) + return available_models(process_name) +end + +""" + model_descriptor(::Type{<:AbstractModel}) + +Return JSON-compatible discovery metadata for a model implementation type. +Input and output metadata is inferred best-effort from a zero-argument or dummy +instance when the type can be constructed safely. +""" +function model_descriptor(::Type{T}) where {T<:AbstractModel} + process_type = _process_type_for_model(T) + process_name = isnothing(process_type) ? nothing : process_(process_type) + module_ = parentmodule(T) + return Dict{String,Any}( + "type" => string(T), + "name" => string(nameof(T)), + "module" => string(module_), + "package" => _model_package_name(module_), + "process" => isnothing(process_name) ? nothing : string(process_name), + "processType" => isnothing(process_type) ? nothing : string(process_type), + "inputs" => _model_var_descriptor(T, inputs_), + "outputs" => _model_var_descriptor(T, outputs_), + "environmentInputs" => _model_var_descriptor(T, meteo_inputs_), + "environmentOutputs" => _model_var_descriptor(T, meteo_outputs_), + "timespec" => _safe_string_trait(T, timespec), + "outputPolicy" => _safe_string_trait(T, output_policy), + "timestepHint" => _safe_string_trait(T, timestep_hint), + "meteoHint" => _safe_string_trait(T, meteo_hint), + "constructor" => model_constructor_descriptor(T), + ) +end + +""" + model_constructor_descriptor(::Type{<:AbstractModel}) + +Return constructor metadata inferred from struct fields and an optional +zero-argument constructor. Fields that share a type parameter share a type +choice in the editor. +""" +function model_constructor_descriptor(::Type{T}) where {T<:AbstractModel} + unwrapped_type = Base.unwrap_unionall(T) + names = collect(fieldnames(unwrapped_type)) + declared_types = collect(fieldtypes(unwrapped_type)) + default_instance = _try_zero_arg_model(T) + has_defaults = !isnothing(default_instance) + positional_values = _dummy_constructor_values(declared_types) + positional_constructible = !isnothing(positional_values) && + _can_construct_with(T, positional_values) + + fields = Dict{String,Any}[] + parameter_groups = Dict{String,Vector{String}}() + for (index, name) in pairs(names) + declared = declared_types[index] + default = has_defaults ? getfield(default_instance, name) : nothing + default_type = has_defaults ? typeof(default) : nothing + parameter_key = _field_type_parameter_key(declared) + field_name = string(name) + if !isnothing(parameter_key) + push!(get!(parameter_groups, parameter_key, String[]), field_name) + end + push!(fields, Dict{String,Any}( + "name" => field_name, + "declaredType" => string(declared), + "hasDefault" => has_defaults, + "default" => has_defaults ? _jsonable_model_value(default) : nothing, + "defaultJulia" => has_defaults ? repr(default) : nothing, + "defaultType" => isnothing(default_type) ? nothing : string(default_type), + "typeParameter" => parameter_key, + "inferredChoice" => string(_parameter_choice(default_type, declared)), + "choices" => string.(_MODEL_PARAMETER_TYPE_CHOICES), + )) + end + + return Dict{String,Any}( + "type" => string(T), + "name" => string(nameof(T)), + "fields" => fields, + "parameterGroups" => parameter_groups, + "hasZeroArgConstructor" => has_defaults, + "constructible" => has_defaults || positional_constructible, + "positional" => true, + "keyword" => false, + ) +end + +function _abstract_model_subtypes(root::Type=AbstractModel, seen=Set{Type}()) + found = Type[] + root in seen && return found + push!(seen, root) + for child in InteractiveUtils.subtypes(root) + child in seen && continue + push!(found, child) + append!(found, _abstract_model_subtypes(child, seen)) + end + return found +end + +function _is_process_type(type::Type) + isabstracttype(type) || return false + type === AbstractModel && return false + try + process_(type) + return true + catch + return false + end +end + +function _is_available_model_type(type::Type) + isabstracttype(type) && return false + nameof(type) in _MODEL_DISCOVERY_EXCLUDED_NAMES && return false + return !isnothing(_process_type_for_model(type)) +end + +function _process_type_for_model(type::Type) + current = type + while current !== Any && current !== AbstractModel + _is_process_type(current) && return current + current = supertype(current) + end + return nothing +end + +function _process_name_for_type(type::Type) + process_type = _process_type_for_model(type) + return isnothing(process_type) ? Symbol(nameof(type)) : process_(process_type) +end + +function _model_var_descriptor(::Type{T}, accessor) where {T<:AbstractModel} + instance = _try_zero_arg_model(T) + isnothing(instance) && (instance = _try_dummy_model(T)) + isnothing(instance) && return Dict{String,Any}() + variables = try + accessor(instance) + catch err + return Dict{String,Any}("_error" => sprint(showerror, err)) + end + return Dict(string(name) => _jsonable_model_value(value) for (name, value) in pairs(variables)) +end + +function _safe_string_trait(::Type{T}, trait) where {T<:AbstractModel} + value = try + trait(T) + catch + instance = _try_zero_arg_model(T) + isnothing(instance) && return nothing + try + trait(instance) + catch err + return string("error: ", sprint(showerror, err)) + end + end + return string(value) +end + +function _try_zero_arg_model(::Type{T}) where {T<:AbstractModel} + try + return T() + catch + return nothing + end +end + +function _try_dummy_model(::Type{T}) where {T<:AbstractModel} + unwrapped_type = Base.unwrap_unionall(T) + names = fieldnames(unwrapped_type) + isempty(names) && return nothing + values = _dummy_constructor_values(fieldtypes(unwrapped_type)) + isnothing(values) && return nothing + try + return T(values...) + catch + return nothing + end +end + +function _dummy_constructor_values(field_types) + values = Any[] + for field_type in field_types + value = _dummy_field_value(field_type) + isnothing(value) && return nothing + push!(values, value) + end + return values +end + +function _can_construct_with(::Type{T}, values) where {T<:AbstractModel} + try + T(values...) + return true + catch + return false + end +end + +_dummy_field_value(::TypeVar) = 0.0 + +function _dummy_field_value(type) + try + type === Any && return 0.0 + type === Bool && return false + type <: Integer && return zero(type) + type <: AbstractFloat && return zero(type) + type <: Real && return zero(type) + type <: Symbol && return :value + type <: AbstractString && return "" + catch + return nothing + end + return nothing +end + +_field_type_parameter_key(field_type) = field_type isa TypeVar ? string(field_type.name) : nothing + +function _parameter_choice(default_type, declared_type) + !isnothing(default_type) && return _parameter_choice_from_type(default_type) + return _parameter_choice_from_type(declared_type) +end + +function _parameter_choice_from_type(type) + try + type === Nothing && return :nothing + type === Any && return :float + type isa TypeVar && return :float + type === Bool && return :boolean + type <: Integer && return :integer + type <: AbstractFloat && return :float + type <: Real && return :float + type <: Symbol && return :symbol + type <: AbstractString && return :string + catch + return :float + end + return :julia +end + +function _jsonable_model_value(value) + value === nothing && return nothing + value isa Bool && return value + value isa Real && isfinite(value) && return value + value isa Symbol && return string(":", value) + value isa AbstractString && return value + value isa AbstractArray && return string(typeof(value), " length ", length(value)) + return string(value) +end + +function _model_package_name(module_::Module) + root = Base.moduleroot(module_) + root in (Base, Core, Main) && return nothing + return string(nameof(root)) +end diff --git a/src/scene_object/compilation.jl b/src/scene_object/compilation.jl new file mode 100644 index 000000000..654edab18 --- /dev/null +++ b/src/scene_object/compilation.jl @@ -0,0 +1,1540 @@ +struct CompiledSceneApplication{S,AT,TS,CL,MO} + id::Symbol + spec::S + process::Symbol + name::Union{Nothing,Symbol} + target_ids::Vector{ObjectId} + applies_to::AT + timestep::TS + clock::CL + model_overrides::MO +end + +struct CompiledSceneInputBinding{SEL,P,W,C} + application_id::Symbol + consumer_id::ObjectId + input::Symbol + selector::SEL + origin::Symbol + source_ids::Vector{ObjectId} + source_application_ids::Vector{Symbol} + source_var::Symbol + process::Union{Nothing,Symbol} + application::Union{Nothing,Symbol} + multiplicity::Symbol + policy::P + window::W + carrier_hint::Symbol + carrier::C +end + +struct CompiledSceneCallBinding{SEL} + application_id::Symbol + consumer_id::ObjectId + call::Symbol + selector::SEL + origin::Symbol + callee_object_ids::Vector{ObjectId} + callee_application_ids::Vector{Symbol} + process::Union{Nothing,Symbol} + application::Union{Nothing,Symbol} + multiplicity::Symbol +end + +struct CompiledEnvironmentBinding{B,C,S} + application_id::Symbol + object_id::ObjectId + provider::Symbol + backend::B + cell::C + required_inputs::Vector{Symbol} + source_inputs::Vector{Symbol} + produced_outputs::Vector{Symbol} + support::S + geometry_source_object_id::Union{Nothing,ObjectId} + geometry_source::Symbol + config::Any +end + +struct CompiledEnvironmentBindings{SC,B,I,S,C} + scene::SC + bindings::B + by_target::I + samplers_by_application::S + sample_cache::C + scene_revision::Int + environment_revision::Int +end + +struct CompiledScene{SC,AP,AI,IB,CB,IBI,CBI,MBI,AO} + scene::SC + applications::AP + applications_by_id::AI + input_bindings::IB + call_bindings::CB + input_bindings_by_target::IBI + call_bindings_by_target::CBI + model_bundles_by_target::MBI + application_order::AO + revision::Int +end + +function _index_scene_bindings(bindings, application_field::Symbol, object_field::Symbol) + grouped = Dict{Tuple{Symbol,ObjectId},Vector{Any}}() + for binding in bindings + key = ( + getproperty(binding, application_field), + getproperty(binding, object_field), + ) + push!(get!(grouped, key, Any[]), binding) + end + return Dict(key => Tuple(values) for (key, values) in grouped) +end + +""" + compile_scene(scene[, applications...]) + +Compile scene applications, selectors, value bindings, hard calls, writer +ordering, and schedules into the single Scene/Object runtime representation. +Most callers can use [`run!`](@ref) directly, which compiles as needed. +""" +function compile_scene(scene::Scene) + return compile_scene(scene, scene.applications) +end + +function compile_scene(scene::Scene, specs::Tuple) + return _compile_scene(scene, specs) +end + +function compile_scene(scene::Scene, specs::AbstractVector) + return _compile_scene(scene, Tuple(specs)) +end + +function compile_scene(scene::Scene, specs...) + return _compile_scene(scene, specs) +end + +function _scene_timeline(scene::Scene) + backend = environment_backend(scene.environment) + _validate_meteo_duration(backend) + return _timeline_context(backend) +end + +function _compile_scene(scene::Scene, raw_specs; validate_required_inputs::Bool=true) + timeline = _scene_timeline(scene) + applications = _compile_scene_applications(scene, raw_specs, timeline) + call_bindings = _compile_scene_call_bindings(scene, applications) + _validate_scene_call_cadences!(applications, call_bindings, timeline) + _validate_scene_writers!(applications, call_bindings) + _prepare_scene_output_statuses!(scene, applications) + input_bindings = _compile_scene_input_bindings( + scene, + applications, + _manual_call_application_ids(call_bindings), + ) + _prepare_scene_bound_input_statuses!(scene, applications, input_bindings) + _wire_scene_input_carriers!(scene, input_bindings) + validate_required_inputs && + _validate_scene_required_inputs!(scene, applications, input_bindings) + input_bindings_by_target = _index_scene_bindings(input_bindings, :application_id, :consumer_id) + call_bindings_by_target = _index_scene_bindings(call_bindings, :application_id, :consumer_id) + application_order = _compile_scene_application_order(applications, input_bindings, call_bindings) + applications_by_id = Dict(application.id => application for application in applications) + model_bundles_by_target = _compile_scene_model_bundles( + applications, + applications_by_id, + call_bindings_by_target, + ) + return CompiledScene( + scene, + applications, + applications_by_id, + input_bindings, + call_bindings, + input_bindings_by_target, + call_bindings_by_target, + model_bundles_by_target, + application_order, + scene.revision, + ) +end + +function _validate_scene_call_cadences!(applications, call_bindings, timeline) + applications_by_id = Dict(application.id => application for application in applications) + for binding in call_bindings + caller = applications_by_id[binding.application_id] + for callee_id in binding.callee_application_ids + callee = applications_by_id[callee_id] + # A call-only target with no model/scenario cadence declaration + # inherits the cadence of its parent call. An explicit target + # cadence is a scientific contract and must match the caller. + _runtime_clock_source_for_spec(callee.spec) == :meteo_base_step && + continue + same_dt = isapprox( + float(caller.clock.dt), + float(callee.clock.dt); + atol=1.0e-9, + rtol=0.0, + ) + same_phase = isapprox( + float(caller.clock.phase), + float(callee.clock.phase); + atol=1.0e-9, + rtol=0.0, + ) + same_dt && same_phase && continue + caller_seconds = float(caller.clock.dt) * timeline.base_step_seconds + callee_seconds = float(callee.clock.dt) * timeline.base_step_seconds + error( + "Hard call `$(binding.call)` from application `$(caller.id)` to ", + "application `$(callee.id)` has incompatible cadence: caller=", + "$(caller_seconds) seconds (phase=$(caller.clock.phase)), target=", + "$(callee_seconds) seconds (phase=$(callee.clock.phase)). ", + "Use matching TimeStep declarations or omit TimeStep on the ", + "manual-call-only target so it inherits the parent call cadence." + ) + end + end + return nothing +end + +""" + explain_initialization(scene::Scene) + +Return structured rows describing how every application variable is +initialized. `disposition` is one of: + +- `:supplied`: present on the object's status before compilation; +- `:generated`: created from a model output declaration; +- `:producer_bound`: connected through an explicit or inferred `Inputs` binding; +- `:environment_bound`: provided by the selected environment backend; +- `:unresolved`: still requires user or scenario configuration. + +Unlike [`compile_scene`](@ref), this report does not fail solely because a +required status or environment value is unresolved. Selector, writer, call, +and other invalid configuration errors remain errors. +""" +function explain_initialization(scene::Scene) + supplied = Dict( + object.id => Set{Symbol}( + object.status isa Status ? Symbol.(propertynames(object.status)) : Symbol[] + ) + for object in values(scene.registry.objects) + ) + compiled = _compile_scene( + scene, + Tuple(scene.applications); + validate_required_inputs=false, + ) + bindings = Dict( + (binding.application_id, binding.consumer_id, binding.input) => binding + for binding in compiled.input_bindings + ) + + environment_bindings = Dict( + (binding.application_id, binding.object_id) => binding + for binding in _compile_environment_bindings(scene, compiled) + ) + + rows = NamedTuple[] + for application in compiled.applications + model_outputs = outputs_(application.spec) + meteo_model_outputs = meteo_outputs_(application.spec) + model_inputs = inputs_(application.spec) + environment_inputs = meteo_inputs_(application.spec) + generated = Set(Symbol.(keys(model_outputs))) + union!(generated, Symbol.(keys(meteo_outputs_(application.spec)))) + for object_id in application.target_ids + for variable in sort!(collect(generated); by=string) + default_value = haskey(model_outputs, variable) ? + getproperty(model_outputs, variable) : + getproperty(meteo_model_outputs, variable) + push!(rows, ( + application_id=application.id, + object_id=object_id.value, + variable=variable, + role=:output, + disposition=:generated, + source_application_ids=Symbol[], + source_object_ids=Any[], + source_variable=nothing, + origin=:model_output, + expected_type=typeof(default_value), + default_value=default_value, + provided_type=nothing, + detail=nothing, + )) + end + for variable in sort!(Symbol.(collect(keys(inputs_(application.spec)))); by=string) + key = (application.id, object_id, variable) + binding = get(bindings, key, nothing) + disposition = if !isnothing(binding) + :producer_bound + elseif variable in get(supplied, object_id, Set{Symbol}()) + :supplied + else + :unresolved + end + default_value = getproperty(model_inputs, variable) + object = _scene_object(scene, object_id) + provided_type = if disposition == :supplied + typeof(getproperty(object.status, variable)) + else + nothing + end + push!(rows, ( + application_id=application.id, + object_id=object_id.value, + variable=variable, + role=:input, + disposition=disposition, + source_application_ids=isnothing(binding) ? Symbol[] : copy(binding.source_application_ids), + source_object_ids=isnothing(binding) ? Any[] : [id.value for id in binding.source_ids], + source_variable=isnothing(binding) ? nothing : binding.source_var, + origin=isnothing(binding) ? + (disposition == :supplied ? :status : :missing) : + binding.origin, + expected_type=typeof(default_value), + default_value=default_value, + provided_type=provided_type, + detail=disposition == :unresolved ? + "Provide `$(variable)` on object `$(object_id.value)` Status or add `Inputs(:$(variable) => ...)` to application `$(application.id)`." : + nothing, + )) + end + for variable in sort!(Symbol.(collect(keys(meteo_inputs_(application.spec)))); by=string) + environment_binding = get( + environment_bindings, + (application.id, object_id), + nothing, + ) + source = get( + _environment_source_overrides(application.spec), + variable, + variable, + ) + available = isnothing(environment_binding) ? + Set{Symbol}() : + environment_variables(environment_binding.backend) + bound = isnothing(available) || Symbol(source) in available + default_value = getproperty(environment_inputs, variable) + push!(rows, ( + application_id=application.id, + object_id=object_id.value, + variable=variable, + role=:environment_input, + disposition=bound ? :environment_bound : :unresolved, + source_application_ids=Symbol[], + source_object_ids=Any[], + source_variable=Symbol(source), + origin=:environment, + expected_type=typeof(default_value), + default_value=default_value, + provided_type=nothing, + detail=bound ? nothing : + "Environment source `$(source)` is not available for this application/object.", + )) + end + end + end + sort!(rows; by=row -> ( + string(row.application_id), + string(row.object_id), + string(row.role), + string(row.variable), + )) + return rows +end + +function _compile_scene_applications(scene::Scene, raw_specs, timeline) + specs = [as_model_spec(raw_spec) for raw_spec in raw_specs] + process_counts = Dict{Symbol,Int}() + for spec in specs + proc = process(spec) + process_counts[proc] = get(process_counts, proc, 0) + 1 + end + ids = Set{Symbol}() + applications = CompiledSceneApplication[] + for spec in specs + selector = applies_to(spec) + isnothing(selector) && error( + "Model application for process `$(process(spec))` has no `AppliesTo(...)` selector." + ) + selector isa AbstractObjectMultiplicity || error( + "`AppliesTo(...)` for process `$(process(spec))` must be an object selector such as `Many(scale=:Leaf)`." + ) + proc = process(spec) + name = application_name(spec) + if isnothing(name) && process_counts[proc] > 1 + error( + "Scene contains $(process_counts[proc]) unnamed applications for process `$(proc)`. ", + "Give every repeated application a unique name with `ModelSpec(model; name=:application_name)`.", + ) + end + app_id = isnothing(name) ? proc : name + app_id in ids && error("Duplicate compiled scene application id `$(app_id)`.") + push!(ids, app_id) + target_ids = resolve_object_ids(scene, selector) + spec = _scene_spec_with_meteo_hints( + scene, + spec, + _scene_application_hint_scale(scene, target_ids), + ) + model_overrides = _compiled_object_model_overrides(spec, target_ids, app_id) + push!( + applications, + CompiledSceneApplication( + app_id, + spec, + proc, + name, + target_ids, + selector, + timestep(spec), + _scene_application_clock(scene, spec, target_ids, timeline), + model_overrides, + ), + ) + end + return applications +end + +function _scene_spec_with_meteo_hints(scene::Scene, spec, scale::Symbol) + hint = _normalize_meteo_hint(scale, process(spec), meteo_hint(model_(spec))) + + current_bindings = meteo_bindings(spec) + has_explicit_bindings = !(current_bindings isa NamedTuple && isempty(keys(current_bindings))) + new_bindings = has_explicit_bindings || isnothing(hint.bindings) ? current_bindings : hint.bindings + new_bindings = _scene_meteo_bindings_with_environment_sources(spec, new_bindings) + + current_window = meteo_window(spec) + new_window = isnothing(current_window) && !isnothing(hint.window) ? hint.window : current_window + + (new_bindings === current_bindings && new_window === current_window) && return spec + return ModelSpec(spec; meteo_bindings=new_bindings, meteo_window=new_window) +end + +function _scene_meteo_bindings_with_environment_sources(spec, bindings) + sources = _environment_source_overrides(spec) + isempty(keys(sources)) && return bindings + + bindings = bindings isa NamedTuple ? bindings : NamedTuple() + model_inputs = Set(Symbol.(keys(meteo_inputs_(spec)))) + unknown = Symbol[target for target in keys(sources) if !(Symbol(target) in model_inputs)] + isempty(unknown) || error( + "`Environment(; sources=...)` for process `$(process(spec))` contains ", + "unknown model-facing meteo input(s) `$(Tuple(unknown))`. Declared ", + "`meteo_inputs_` are `$(Tuple(sort!(collect(model_inputs); by=string)))`." + ) + + targets = Symbol[Symbol(target) for target in keys(bindings)] + for target in keys(sources) + target = Symbol(target) + target in targets || push!(targets, target) + end + + resolved = Pair{Symbol,Any}[] + for target in targets + rule = haskey(bindings, target) ? + _normalize_meteo_binding_rule(target, bindings[target]) : + (source=target, reducer=PlantMeteo.MeanWeighted()) + source = haskey(sources, target) ? Symbol(sources[target]) : rule.source + push!(resolved, target => (source=source, reducer=rule.reducer)) + end + return (; resolved...) +end + +function _compiled_object_model_overrides(spec, target_ids, application_id::Symbol) + model = model_(spec) + model isa ObjectModelOverrides || return nothing + target_set = Set(target_ids) + unmatched = ObjectId[id for id in keys(model.overrides) if !(id in target_set)] + isempty(unmatched) || error( + "Object override(s) `$([id.value for id in unmatched])` for application ", + "`$(application_id)` do not match its `AppliesTo(...)` target set." + ) + return model.overrides +end + +_application_default_model(application::CompiledSceneApplication) = + model_(application.spec) isa ObjectModelOverrides ? + model_(application.spec).base : + model_(application.spec) + +function _application_model(application::CompiledSceneApplication, object_id::ObjectId) + isnothing(application.model_overrides) && return _application_default_model(application) + return get( + application.model_overrides, + object_id, + _application_default_model(application), + ) +end + +function _scene_output_names(application::CompiledSceneApplication) + return Symbol[Symbol(var) for var in keys(outputs_(application.spec))] +end + +function _scene_canonical_output_names(application::CompiledSceneApplication) + return Symbol[ + variable for variable in _scene_output_names(application) + if _publish_mode_for_output(application.spec, variable) == :canonical + ] +end + +function _scene_writer_groups(applications, skipped_application_ids=Set{Symbol}()) + groups = Dict{Tuple{ObjectId,Symbol},Vector{Tuple{Int,Any}}}() + for (index, application) in pairs(applications) + application.id in skipped_application_ids && continue + for object_id in application.target_ids + for variable in _scene_canonical_output_names(application) + push!(get!(groups, (object_id, variable), Tuple{Int,Any}[]), (index, application)) + end + end + end + return groups +end + +function _application_match_labels(application::CompiledSceneApplication) + labels = Set{Symbol}([application.id]) + isnothing(application.name) || push!(labels, application.name) + return labels +end + +function _update_matches_application(label::Symbol, application::CompiledSceneApplication) + label == application.id && return true + if label == application.process || (!isnothing(application.name) && label == application.name) + Base.depwarn( + "Matching `Updates(...; after=$(repr(label)))` by process or local name is deprecated. " * + "Use the canonical application identifier `$(application.id)`.", + :Updates, + ) + return true + end + return false +end + +function _update_variables(update) + return Tuple(Symbol(variable) for variable in getproperty(update, :variables)) +end + +function _update_after(update) + return Tuple(Symbol(label) for label in getproperty(update, :after)) +end + +function _matching_updates(spec, variable::Symbol) + return [update for update in updates(spec) if variable in _update_variables(update)] +end + +function _update_after_labels(spec, variable::Symbol) + labels = Symbol[] + for update in _matching_updates(spec, variable) + append!(labels, _update_after(update)) + end + unique!(labels) + return labels +end + +function _updates_after_previous_writer(spec, variable::Symbol, previous_applications) + matching = _matching_updates(spec, variable) + isempty(matching) && return false + for update in matching + after = _update_after(update) + isempty(after) && continue + any( + label -> any(application -> _update_matches_application(label, application), previous_applications), + after, + ) && return true + end + return false +end + +function _declares_update_without_previous_writer(spec, variable::Symbol, previous_applications) + isempty(previous_applications) || return false + for update in _matching_updates(spec, variable) + isempty(_update_after(update)) || return true + end + return false +end + +function _manual_call_application_ids(call_bindings) + ids = Set{Symbol}() + for binding in call_bindings + union!(ids, binding.callee_application_ids) + end + return ids +end + +function _validate_scene_writers!(applications, call_bindings=()) + manual_application_ids = _manual_call_application_ids(call_bindings) + for ((object_id, variable), indexed_writers) in _scene_writer_groups(applications, manual_application_ids) + length(indexed_writers) <= 1 && continue + sort!(indexed_writers; by=first) + previous = CompiledSceneApplication[] + for (_, application) in indexed_writers + if _declares_update_without_previous_writer(application.spec, variable, previous) + error( + "Application `$(application.id)` declares `Updates($(variable))` for object ", + "`$(object_id.value)`, but no previous writer for `$(variable)` exists. ", + "Move it after the producer named in `after=...`." + ) + end + if !isempty(previous) && isempty(_matching_updates(application.spec, variable)) + previous_labels = sort!(collect(reduce(union!, (_application_match_labels(app) for app in previous); init=Set{Symbol}()))) + error( + "Ambiguous canonical writers for variable `$(variable)` on object ", + "`$(object_id.value)`. ", + "applications. Application `$(application.id)` must declare ", + "`Updates(:$(variable); after=...)` matching one of the previous writers ", + "`$(previous_labels)`." + ) + end + if !isempty(previous) && + !_updates_after_previous_writer( + application.spec, + variable, + CompiledSceneApplication[last(previous)], + ) + previous_writer = last(previous) + error( + "Application `$(application.id)` updates `$(variable)` on object ", + "`$(object_id.value)` without an ordering relation to the immediately ", + "previous writer `$(previous_writer.id)`. Add that application identifier ", + "to `Updates(:$(variable); after=...)`." + ) + end + push!(previous, application) + end + end + return nothing +end + +function _scene_application_hint_scale(scene::Scene, target_ids::Vector{ObjectId}) + isempty(target_ids) && return :Scene + scales = unique!([_scene_object(scene, object_id).scale for object_id in target_ids]) + length(scales) == 1 && return only(scales) + return :Mixed +end + +function _scene_application_clock(scene::Scene, spec, target_ids::Vector{ObjectId}, timeline) + model = model_(spec) + source = _runtime_clock_source_for_spec(spec) + source == :meteo_base_step || return _model_clock(spec, model, timeline) + scale = _scene_application_hint_scale(scene, target_ids) + clock, hint_reason = _resolve_meteo_hint_clock(scale, process(spec), model, timeline) + isnothing(hint_reason) || error(hint_reason) + return clock +end + +function _criteria_get(criteria, key::Symbol, default=nothing) + return haskey(criteria, key) ? getproperty(criteria, key) : default +end + +function _selector_policy(selector::AbstractObjectMultiplicity) + return _criteria_get(criteria(selector), :policy, HoldLast()) +end + +_selector_has_policy(selector::AbstractObjectMultiplicity) = + haskey(criteria(selector), :policy) + +function _scene_policy_from_source_application(applications_by_id, application_id::Symbol, source_var::Symbol) + application = get(applications_by_id, application_id, nothing) + isnothing(application) && error( + "No compiled scene application with id `$(application_id)` while resolving ", + "default output policy for `$(source_var)`." + ) + return _policy_for_output(_application_default_model(application), source_var) +end + +function _scene_selector_policy(selector::AbstractObjectMultiplicity, applications_by_id, source_application_ids, source_var::Symbol) + if _selector_has_policy(selector) + policy = _selector_policy(selector) + policy isa PreviousTimeStep && return policy + return _as_schedule_policy( + policy; + context="scene input policy for `$(source_var)`", + ) + end + isempty(source_application_ids) && return HoldLast() + length(source_application_ids) == 1 && return _scene_policy_from_source_application( + applications_by_id, + only(source_application_ids), + source_var, + ) + policies = [ + _scene_policy_from_source_application(applications_by_id, application_id, source_var) + for application_id in source_application_ids + ] + first_policy = first(policies) + all(policy -> policy == first_policy, policies) && return first_policy + error( + "Cannot infer default policy for scene input from `$(source_var)` because ", + "selector resolves several source applications `$(source_application_ids)` with ", + "different output policies. Add `policy=...` to `Inputs(...)`." + ) +end + +function _selector_window(selector::AbstractObjectMultiplicity) + return _criteria_get(criteria(selector), :window, nothing) +end + +function _selector_var(selector::AbstractObjectMultiplicity, fallback::Symbol) + return _criteria_get(criteria(selector), :var, fallback) +end + +function _selector_application(selector::AbstractObjectMultiplicity) + return _criteria_get(criteria(selector), :application, nothing) +end + +function _dependency_object_ids(scene::Scene, selector::AbstractObjectMultiplicity, context::ObjectId) + return _resolve_object_ids( + scene, + selector; + context=context, + default_to_context=true, + default_scope=_default_dependency_scope(scene, context), + ) +end + +function _carrier_hint(selector::AbstractObjectMultiplicity, policy, window) + !isnothing(window) && return :temporal_stream + policy isa HoldLast || return :temporal_stream + selector isa Many && return :ref_vector + return :shared_ref +end + +function _status_ref_or_nothing(status, var::Symbol) + status isa Status || return nothing + var in propertynames(status) || return nothing + return refvalue(status, var) +end + +function _input_carrier(scene::Scene, selector::AbstractObjectMultiplicity, source_ids::Vector{ObjectId}, source_var::Symbol) + refs = Base.RefValue[] + for source_id in source_ids + object = _scene_object(scene, source_id) + source_ref = _status_ref_or_nothing(object.status, source_var) + isnothing(source_ref) && return nothing + push!(refs, source_ref) + end + if selector isa Many + isempty(refs) && return RefVector{Any}() + return _ref_vector_carrier(refs) + end + return isempty(refs) ? nothing : only(refs) +end + +function _ref_vector_carrier(refs) + T = typeof(refs[1][]) + typed_refs = Base.RefValue{T}[] + for source_ref in refs + source_ref isa Base.RefValue{T} || return ObjectRefVector(refs) + push!(typed_refs, source_ref) + end + return RefVector(typed_refs) +end + +function _status_with_reference(status::Status, variable::Symbol, reference::Base.RefValue) + names = propertynames(status) + if variable in names + references = ntuple( + index -> names[index] == variable ? reference : refvalue(status, names[index]), + length(names), + ) + return Status(NamedTuple{names}(references)) + end + extended_names = (names..., variable) + references = (ntuple(index -> refvalue(status, names[index]), length(names))..., reference) + return Status(NamedTuple{extended_names}(references)) +end + +function _status_with_default(status::Status, variable::Symbol, value) + variable in propertynames(status) && return status + return _status_with_reference(status, variable, Ref(value)) +end + +function _ensure_scene_object_status!(scene::Scene, object_id::ObjectId) + object = _scene_object(scene, object_id) + isnothing(object.status) && (object.status = Status()) + object.status isa Status || error( + "Scene object `$(object_id.value)` uses model applications but its status has type ", + "`$(typeof(object.status))`. Use `Status(...)` or leave status as `nothing`." + ) + return object.status +end + +function _prepare_scene_output_statuses!(scene::Scene, applications) + for application in applications + defaults = merge(outputs_(application.spec), meteo_outputs_(application.spec)) + for object_id in application.target_ids + status = _ensure_scene_object_status!(scene, object_id) + for (variable, value) in pairs(defaults) + status = _status_with_default(status, Symbol(variable), value) + end + _scene_object(scene, object_id).status = status + end + end + return scene +end + +function _prepare_scene_bound_input_statuses!(scene::Scene, applications, bindings) + applications_by_id = Dict(application.id => application for application in applications) + for binding in bindings + status = _ensure_scene_object_status!(scene, binding.consumer_id) + binding.input in propertynames(status) && continue + application = applications_by_id[binding.application_id] + defaults = inputs_(application.spec) + binding.input in keys(defaults) || error( + "Bound input `$(binding.input)` is not declared by application `$(binding.application_id)`." + ) + _scene_object(scene, binding.consumer_id).status = + _status_with_default(status, binding.input, getproperty(defaults, binding.input)) + end + return scene +end + +function _wire_scene_input_carriers!(scene::Scene, bindings) + for binding in bindings + binding.carrier_hint == :temporal_stream && continue + isnothing(binding.carrier) && continue + object = _scene_object(scene, binding.consumer_id) + status = object.status + status isa Status || continue + reference = binding.carrier isa Base.RefValue ? binding.carrier : Ref(binding.carrier) + object.status = _status_with_reference(status, binding.input, reference) + end + return scene +end + +input_carrier(binding::CompiledSceneInputBinding) = binding.carrier +has_reference_carrier(binding::CompiledSceneInputBinding) = !isnothing(binding.carrier) +input_value(binding::CompiledSceneInputBinding) = _input_value(binding.carrier) +_input_value(::Nothing) = nothing +_input_value(carrier::Base.RefValue) = carrier[] +_input_value(carrier::RefVector) = carrier +_input_value(carrier::ObjectRefVector) = carrier + +function _matching_input_source_applications( + applications_by_object, + source_ids, + source_var::Symbol, + process_filter, + application_filter; + allow_empty::Bool=false, +) + matches = Symbol[] + for source_id in source_ids + for application in get(applications_by_object, source_id, Any[]) + source_var in _scene_output_names(application) || continue + isnothing(process_filter) || application.process == process_filter || continue + isnothing(application_filter) || application.id == application_filter || continue + push!(matches, application.id) + end + end + unique!(matches) + if !allow_empty && + (!isnothing(process_filter) || !isnothing(application_filter)) && + isempty(matches) + error( + "Input selector for source variable `$(source_var)` requested", + isnothing(process_filter) ? "" : " process `$(process_filter)`", + isnothing(application_filter) ? "" : " application `$(application_filter)`", + ", but no matching source application was found." + ) + end + return matches +end + +function _final_canonical_source_application( + applications_by_object, + source_ids, + source_application_ids, + source_var::Symbol, +) + length(source_ids) == 1 || return source_application_ids + matching_ids = Set(source_application_ids) + canonical_ids = Symbol[ + application.id + for application in get(applications_by_object, only(source_ids), Any[]) + if application.id in matching_ids && + _publish_mode_for_output(application.spec, source_var) == :canonical + ] + isempty(canonical_ids) && return source_application_ids + return Symbol[last(canonical_ids)] +end + +function _compile_scene_input_bindings( + scene::Scene, + applications, + manual_application_ids=Set{Symbol}(), +) + bindings = CompiledSceneInputBinding[] + by_object = _applications_by_object(applications) + by_id = Dict(application.id => application for application in applications) + for application in applications + for consumer_id in application.target_ids + declared_inputs = value_inputs(application.spec) + declared_inputs isa NamedTuple || (declared_inputs = NamedTuple()) + for (input_name, selector) in pairs(declared_inputs) + input_sym = Symbol(input_name) + origin = get( + input_origins(application.spec), + input_sym, + :model_spec, + ) + _validate_declared_scene_input_name!(application, input_sym) + selector isa AbstractObjectMultiplicity || error( + "Input binding `$(input_sym)` on application `$(application.id)` must use an object selector." + ) + if origin == :model_spec && !(selector isa Many) && + !isnothing(_criteria_get(criteria(selector), :process, nothing)) && + isnothing(_selector_application(selector)) + Base.depwarn( + "`process=` in scenario `Inputs` is deprecated; name the producer application and use `application=`.", + :Inputs, + ) + end + _push_scene_input_binding!( + bindings, + scene, + application, + consumer_id, + input_sym, + selector, + origin, + by_object, + by_id, + ) + end + application.id in manual_application_ids && continue + _append_inferred_scene_input_bindings!( + bindings, + scene, + application, + consumer_id, + declared_inputs, + by_object, + by_id, + ) + end + end + return bindings +end + +function _push_scene_input_binding!( + bindings, + scene::Scene, + application::CompiledSceneApplication, + consumer_id::ObjectId, + input_sym::Symbol, + selector::AbstractObjectMultiplicity, + origin::Symbol, + applications_by_object, + applications_by_id, + source_ids_override=nothing, +) + source_ids = isnothing(source_ids_override) ? _dependency_object_ids(scene, selector, consumer_id) : source_ids_override + window = _selector_window(selector) + source_var = _selector_var(selector, input_sym) + process_filter = _criteria_get(criteria(selector), :process, nothing) + application_filter = _selector_application(selector) + source_application_ids = _matching_input_source_applications( + applications_by_object, + source_ids, + source_var, + process_filter, + application_filter, + allow_empty=selector isa OptionalOne, + ) + if !(selector isa Many) && + isnothing(process_filter) && + isnothing(application_filter) && + length(source_application_ids) > 1 + source_application_ids = _final_canonical_source_application( + applications_by_object, + source_ids, + source_application_ids, + source_var, + ) + end + if !(selector isa Many) && length(source_application_ids) > 1 + error( + "Input `$(input_sym)` on application `$(application.id)` matched several " * + "source applications `$(source_application_ids)`. Add one of those canonical " * + "identifiers as `application=...` to `Inputs(...)`.", + ) + end + if selector isa OptionalOne && + (!isnothing(process_filter) || !isnothing(application_filter)) && + isempty(source_application_ids) + source_ids = ObjectId[] + end + policy = _scene_selector_policy(selector, applications_by_id, source_application_ids, source_var) + if policy isa PreviousTimeStep + policy.variable == input_sym || error( + "PreviousTimeStep marker for input `$(input_sym)` on application ", + "`$(application.id)` names `$(policy.variable)`. Use ", + "`PreviousTimeStep(:$(input_sym))`." + ) + else + _validate_policy_instance( + _scene_object(scene, consumer_id).scale, + application.process, + input_sym, + policy, + ) + end + carrier = _input_carrier(scene, selector, source_ids, source_var) + carrier_hint = + isempty(source_ids) && selector isa OptionalOne ? + :optional_default : + _carrier_hint(selector, policy, window) + _validate_scene_input_source!( + scene, + application, + consumer_id, + input_sym, + source_var, + source_ids, + carrier, + carrier_hint, + ) + push!( + bindings, + CompiledSceneInputBinding( + application.id, + consumer_id, + input_sym, + selector, + origin, + source_ids, + source_application_ids, + source_var, + process_filter, + application_filter, + multiplicity(selector), + policy, + window, + carrier_hint, + carrier, + ), + ) + return bindings +end + +function _scene_input_names(application::CompiledSceneApplication) + return Symbol[Symbol(var) for var in keys(inputs_(application.spec))] +end + +function _validate_scene_input_source!( + scene::Scene, + application::CompiledSceneApplication, + consumer_id::ObjectId, + input_sym::Symbol, + source_var::Symbol, + source_ids::Vector{ObjectId}, + carrier, + carrier_hint::Symbol, +) + carrier_hint == :temporal_stream && return nothing + !isnothing(carrier) && return nothing + status_source_ids = ObjectId[ + source_id for source_id in source_ids + if _scene_object(scene, source_id).status isa Status + ] + isempty(status_source_ids) && return nothing + error( + "Input binding `$(input_sym)` on application `$(application.id)` for object ", + "`$(consumer_id.value)` reads `$(source_var)` from objects ", + "`$([id.value for id in status_source_ids])`, but no source `Status` reference is available." + ) +end + +function _validate_declared_scene_input_name!(application::CompiledSceneApplication, input_sym::Symbol) + input_names = Set(_scene_input_names(application)) + input_sym in input_names && return nothing + error( + "Input binding `$(input_sym)` on application `$(application.id)` is not declared by ", + "`inputs_` for process `$(application.process)`. Declared model inputs are ", + "`$(sort!(collect(input_names)))`." + ) +end + +function _same_object_output_applications(applications_by_object, application::CompiledSceneApplication, object_id::ObjectId, variable::Symbol) + matches = CompiledSceneApplication[] + for candidate in get(applications_by_object, object_id, Any[]) + candidate.id == application.id && continue + variable in _scene_canonical_output_names(candidate) || continue + push!(matches, candidate) + end + return matches +end + +function _append_inferred_scene_input_bindings!( + bindings, + scene::Scene, + application::CompiledSceneApplication, + consumer_id::ObjectId, + declared_inputs, + applications_by_object, + applications_by_id, +) + declared_names = declared_inputs isa NamedTuple ? Set(Symbol.(keys(declared_inputs))) : Set{Symbol}() + for input_sym in _scene_input_names(application) + input_sym in declared_names && continue + matches = _same_object_output_applications(applications_by_object, application, consumer_id, input_sym) + isempty(matches) && continue + if length(matches) > 1 + error( + "Input `$(input_sym)` on application `$(application.id)` for object `$(consumer_id.value)` ", + "has ambiguous same-object producers: `$([match.id for match in matches])`. ", + "Add `Inputs(:$(input_sym) => One(...))` to disambiguate." + ) + end + producer = only(matches) + selector = One(within=Self(), process=producer.process, application=producer.id, var=input_sym) + _push_scene_input_binding!( + bindings, + scene, + application, + consumer_id, + input_sym, + selector, + :inferred_same_object, + applications_by_object, + applications_by_id, + ObjectId[consumer_id], + ) + end + return bindings +end + +function _bound_scene_inputs(input_bindings) + bound = Set{Tuple{Symbol,ObjectId,Symbol}}() + for binding in input_bindings + push!(bound, (binding.application_id, binding.consumer_id, binding.input)) + end + return bound +end + +function _status_has_variable(scene::Scene, object_id::ObjectId, variable::Symbol) + object = _scene_object(scene, object_id) + object.status isa Status || return false + return variable in propertynames(object.status) +end + +function _validate_scene_required_inputs!(scene::Scene, applications, input_bindings) + bound = _bound_scene_inputs(input_bindings) + missing = NamedTuple[] + for application in applications + for object_id in application.target_ids + for input in _scene_input_names(application) + (application.id, object_id, input) in bound && continue + _status_has_variable(scene, object_id, input) && continue + push!( + missing, + ( + application_id=application.id, + object_id=object_id.value, + input=input, + process=application.process, + ), + ) + end + end + end + isempty(missing) && return nothing + details = join( + [ + "`$(row.application_id)` on object `$(row.object_id)` requires `$(row.input)`" + for row in missing + ], + "; ", + ) + error( + "Missing required scene/object input(s): ", + details, + ". Provide the variable on object `Status`, add an `Inputs(...)` binding, ", + "or add an unambiguous same-object producer." + ) +end + +function _applications_by_object(applications) + by_object = Dict{ObjectId,Vector{Any}}() + for application in applications + for object_id in application.target_ids + push!(get!(by_object, object_id, Any[]), application) + end + end + return by_object +end + +function _matching_callee_applications(applications, object_id::ObjectId, proc, application_name_filter) + matches = Symbol[] + for application in get(applications, object_id, Any[]) + isnothing(proc) || application.process == proc || continue + isnothing(application_name_filter) || application.id == application_name_filter || continue + push!(matches, application.id) + end + return matches +end + +function _compile_scene_call_bindings(scene::Scene, applications) + by_object = _applications_by_object(applications) + bindings = CompiledSceneCallBinding[] + for application in applications + calls = model_calls(application.spec) + calls isa NamedTuple || continue + for consumer_id in application.target_ids + for (call_name, selector) in pairs(calls) + call_sym = Symbol(call_name) + origin = get( + call_origins(application.spec), + call_sym, + :model_spec, + ) + selector isa AbstractObjectMultiplicity || error( + "Call binding `$(call_sym)` on application `$(application.id)` must use an object selector." + ) + if origin == :model_spec && !(selector isa Many) && + !isnothing(_criteria_get(criteria(selector), :process, nothing)) && + isnothing(_selector_application(selector)) + Base.depwarn( + "`process=` in scenario `Calls` is deprecated; name the callee application and use `application=`.", + :Calls, + ) + end + callee_object_ids = _dependency_object_ids(scene, selector, consumer_id) + proc = _criteria_get(criteria(selector), :process, nothing) + app_name = _selector_application(selector) + callee_application_ids = Symbol[] + for object_id in callee_object_ids + append!( + callee_application_ids, + _matching_callee_applications(by_object, object_id, proc, app_name), + ) + end + unique!(callee_application_ids) + if isempty(callee_application_ids) && !(selector isa OptionalOne) + error( + "Call `$(call_sym)` on application `$(application.id)` matched objects ", + "$([id.value for id in callee_object_ids]) but no model application", + isnothing(proc) ? "." : " with process `$(proc)`.", + ) + end + if selector isa One && length(callee_application_ids) != 1 + error( + "Call `$(call_sym)` on application `$(application.id)` expected one callee application, ", + "got `$(callee_application_ids)`. Add `application=:name` to disambiguate." + ) + elseif selector isa OptionalOne && length(callee_application_ids) > 1 + error( + "Call `$(call_sym)` on application `$(application.id)` expected zero or one callee application, ", + "got `$(callee_application_ids)`. Add `application=:name` to disambiguate." + ) + end + push!( + bindings, + CompiledSceneCallBinding( + application.id, + consumer_id, + call_sym, + selector, + origin, + callee_object_ids, + callee_application_ids, + proc, + app_name, + multiplicity(selector), + ), + ) + end + end + end + return bindings +end + +function _scene_call_owners(call_bindings) + owners = Dict{Symbol,Set{Symbol}}() + for binding in call_bindings + for callee_id in binding.callee_application_ids + push!(get!(owners, callee_id, Set{Symbol}()), binding.application_id) + end + end + return owners +end + +function _add_scene_application_edge!(children, parent::Symbol, child::Symbol) + parent == child && return nothing + push!(get!(children, parent, Set{Symbol}()), child) + return nothing +end + +function _scene_input_order_edges!(children, input_bindings, call_owners) + for binding in input_bindings + binding.policy isa PreviousTimeStep && continue + for source_id in binding.source_application_ids + owners = get(call_owners, source_id, nothing) + if isnothing(owners) + _add_scene_application_edge!(children, source_id, binding.application_id) + else + for owner_id in owners + _add_scene_application_edge!(children, owner_id, binding.application_id) + end + end + end + end + return children +end + +function _scene_update_order_edges!(children, applications) + for indexed_writers in values(_scene_writer_groups(applications)) + length(indexed_writers) > 1 || continue + sort!(indexed_writers; by=first) + for index in 2:length(indexed_writers) + previous_application = indexed_writers[index - 1][2] + application = indexed_writers[index][2] + _add_scene_application_edge!(children, previous_application.id, application.id) + end + end + return children +end + +function _stable_topological_application_order(applications, children) + application_ids = Symbol[application.id for application in applications] + positions = Dict(application_id => index for (index, application_id) in pairs(application_ids)) + indegree = Dict(application_id => 0 for application_id in application_ids) + for child_ids in values(children) + for child_id in child_ids + indegree[child_id] = get(indegree, child_id, 0) + 1 + end + end + ready = Symbol[application_id for application_id in application_ids if indegree[application_id] == 0] + order = Symbol[] + while !isempty(ready) + sort!(ready; by=application_id -> positions[application_id]) + application_id = popfirst!(ready) + push!(order, application_id) + child_ids = sort!(collect(get(children, application_id, Set{Symbol}())); by=child_id -> positions[child_id]) + for child_id in child_ids + indegree[child_id] -= 1 + indegree[child_id] == 0 && push!(ready, child_id) + end + end + if length(order) != length(application_ids) + remaining = Symbol[application_id for application_id in application_ids if indegree[application_id] > 0] + error( + "Scene application dependency cycle detected among applications `$(remaining)`. ", + "Break the same-timestep cycle with a temporal policy or revise `Inputs(...)`/`Updates(...)`." + ) + end + return order +end + +function _compile_scene_application_order(applications, input_bindings, call_bindings) + children = Dict{Symbol,Set{Symbol}}() + call_owners = _scene_call_owners(call_bindings) + _scene_input_order_edges!(children, input_bindings, call_owners) + _scene_update_order_edges!(children, applications) + return _stable_topological_application_order(applications, children) +end + +function _ordered_scene_applications(compiled::CompiledScene) + return [compiled.applications_by_id[application_id] for application_id in compiled.application_order] +end + +function explain_scene_applications(compiled::CompiledScene) + return [ + ( + application_id=application.id, + process=application.process, + name=application.name, + target_ids=[id.value for id in application.target_ids], + target_scales=sort!(unique!(Symbol[ + _scene_object(compiled.scene, id).scale + for id in application.target_ids + if !isnothing(_scene_object(compiled.scene, id).scale) + ]); by=string), + applies_to=application.applies_to, + inputs=Tuple(Symbol.(keys(inputs_(application.spec)))), + outputs=Tuple(Symbol.(keys(outputs_(application.spec)))), + environment_inputs=Tuple(Symbol.(keys(meteo_inputs_(application.spec)))), + environment_outputs=Tuple(Symbol.(keys(meteo_outputs_(application.spec)))), + timestep=application.timestep, + clock=application.clock, + model_type=typeof(_application_default_model(application)), + model_storage=isnothing(application.model_overrides) ? :shared_application : :per_object_override, + model_dispatch=_application_model_dispatch(application), + object_overrides=isnothing(application.model_overrides) ? + NamedTuple[] : + [ + ( + object_id=object_id.value, + model_type=typeof(model), + ) + for (object_id, model) in sort!( + collect(application.model_overrides); + by=pair -> string(first(pair).value), + ) + ], + ) + for application in compiled.applications + ] +end + +explain_scene_applications(scene::Scene) = + explain_scene_applications(refresh_bindings!(scene)) + +function _application_model_dispatch(application::CompiledSceneApplication) + isnothing(application.model_overrides) && return :concrete_shared + override_type = valtype(typeof(application.model_overrides)) + default_type = typeof(_application_default_model(application)) + return isconcretetype(override_type) && default_type == override_type ? + :concrete_per_object : + :heterogeneous_per_object +end + +function explain_schedule(compiled::CompiledScene) + timeline = _scene_timeline(compiled.scene) + manual_application_ids = _manual_call_application_ids(compiled) + execution_positions = Dict(application_id => index for (index, application_id) in pairs(compiled.application_order)) + return [ + ( + application_id=application.id, + process=application.process, + execution_index=execution_positions[application.id], + timestep=application.timestep, + clock=application.clock, + dt_steps=application.clock.dt, + phase=application.clock.phase, + dt_seconds=float(application.clock.dt) * timeline.base_step_seconds, + target_ids=[id.value for id in application.target_ids], + root_scheduled=!(application.id in manual_application_ids), + manual_call_only=application.id in manual_application_ids, + ) + for application in _ordered_scene_applications(compiled) + ] +end + +explain_schedule(scene::Scene) = explain_schedule(refresh_bindings!(scene)) + +function _scene_binding_carrier_kind(binding::CompiledSceneInputBinding) + binding.carrier_hint == :temporal_stream && return :temporal_stream + binding.carrier_hint == :optional_default && return :optional_default + carrier = binding.carrier + isnothing(carrier) && return :unresolved + carrier isa Base.RefValue && return :ref + carrier isa RefVector && return :ref_vector + carrier isa ObjectRefVector && return :object_ref_vector + return :custom +end + +function _scene_binding_copy_semantics(binding::CompiledSceneInputBinding) + kind = _scene_binding_carrier_kind(binding) + kind in (:ref, :ref_vector, :object_ref_vector) && return :live_references + kind == :temporal_stream && return :materialized_temporal_value + kind == :optional_default && return :consumer_default + kind == :unresolved && return :not_materialized + return :backend_defined +end + +function explain_bindings(compiled::CompiledScene) + return [ + ( + application_id=binding.application_id, + consumer_id=binding.consumer_id.value, + input=binding.input, + origin=binding.origin, + source_ids=[id.value for id in binding.source_ids], + source_application_ids=binding.source_application_ids, + source_var=binding.source_var, + process=binding.process, + application=binding.application, + multiplicity=binding.multiplicity, + policy=binding.policy, + window=binding.window, + carrier_hint=binding.carrier_hint, + carrier_kind=_scene_binding_carrier_kind(binding), + copy_semantics=_scene_binding_copy_semantics(binding), + has_reference_carrier=has_reference_carrier(binding), + carrier_type=isnothing(binding.carrier) ? nothing : typeof(binding.carrier), + selector=binding.selector, + ) + for binding in compiled.input_bindings + ] +end + +explain_bindings(scene::Scene) = explain_bindings(refresh_bindings!(scene)) + +function explain_calls(compiled::CompiledScene) + return [ + ( + application_id=binding.application_id, + consumer_id=binding.consumer_id.value, + call=binding.call, + origin=binding.origin, + callee_object_ids=[id.value for id in binding.callee_object_ids], + callee_application_ids=binding.callee_application_ids, + process=binding.process, + application=binding.application, + multiplicity=binding.multiplicity, + publication_policy=:explicit_accept, + default_publish=false, + accepted_publish=true, + resolved=!isempty(binding.callee_application_ids), + selector=binding.selector, + ) + for binding in compiled.call_bindings + ] +end + +explain_calls(scene::Scene) = explain_calls(refresh_bindings!(scene)) + +function explain_model_bundles(compiled::CompiledScene) + return [ + ( + application_id=application_id, + object_id=object_id.value, + processes=collect(keys(models)), + model_types=[typeof(model) for model in values(models)], + ) + for ((application_id, object_id), models) in sort!( + collect(compiled.model_bundles_by_target); + by=pair -> (string(first(pair)[1]), string(first(pair)[2].value)), + ) + ] +end + +explain_model_bundles(scene::Scene) = + explain_model_bundles(refresh_bindings!(scene)) + +function explain_writers(compiled::CompiledScene) + groups = _scene_writer_groups(compiled.applications, _manual_call_application_ids(compiled)) + rows = NamedTuple[] + for ((object_id, variable), indexed_writers) in groups + sort!(indexed_writers; by=first) + applications = [application for (_, application) in indexed_writers] + push!( + rows, + ( + object_id=object_id.value, + variable=variable, + application_ids=[application.id for application in applications], + processes=[application.process for application in applications], + update_application_ids=[ + application.id for application in applications + if !isempty(_matching_updates(application.spec, variable)) + ], + update_after=[ + application.id => _update_after_labels(application.spec, variable) + for application in applications + if !isempty(_matching_updates(application.spec, variable)) + ], + duplicate=length(applications) > 1, + ), + ) + end + sort!(rows; by=row -> (string(row.object_id), string(row.variable))) + return rows +end + +explain_writers(scene::Scene) = explain_writers(refresh_bindings!(scene)) diff --git a/src/scene_object/environment_bindings.jl b/src/scene_object/environment_bindings.jl new file mode 100644 index 000000000..7fe95a321 --- /dev/null +++ b/src/scene_object/environment_bindings.jl @@ -0,0 +1,398 @@ +function _environment_config_payload(config) + config isa EnvironmentConfig && return config.config + return config +end + +function _environment_backend_from_config(scene::Scene, config) + payload = _environment_config_payload(config) + isnothing(payload) && return environment_backend(scene.environment) + payload isa NamedTuple && haskey(payload, :backend) && return environment_backend(payload.backend) + payload isa AbstractEnvironmentBackend && return payload + return environment_backend(scene.environment) +end + +function _environment_provider_from_config(config, backend) + payload = _environment_config_payload(config) + payload isa NamedTuple && haskey(payload, :provider) && return Symbol(payload.provider) + payload isa Symbol && return payload + isnothing(backend) && return :none + return :scene +end + +function _object_environment_support(application::CompiledSceneApplication, object::Object) + scale = isnothing(object.scale) ? :Default : object.scale + return EnvironmentSupport(application.id, scale, application.process, object.status) +end + +bind_environment(backend, object::Object, support, config=nothing) = :global + +function _environment_binding_object(scene::Scene, object::Object) + !isnothing(geometry(object)) && return object, object.id, :self + ancestor_id = object.parent + while !isnothing(ancestor_id) + ancestor = _scene_object(scene, ancestor_id) + if !isnothing(geometry(ancestor)) + proxy = Object( + object.id; + scale=object.scale, + kind=object.kind, + species=object.species, + name=object.name, + parent=object.parent, + children=object.children, + geometry=geometry(ancestor), + status=object.status, + applications=object.applications, + ) + return proxy, ancestor.id, :ancestor + end + ancestor_id = ancestor.parent + end + return object, nothing, :global +end + +function _scene_environment_entities(scene::Scene) + return [ + ( + id=object.id.value, + object=object, + scale=object.scale, + kind=object.kind, + species=object.species, + name=object.name, + parent=isnothing(object.parent) ? nothing : object.parent.value, + geometry=geometry(object), + position=position(object), + bounds=bounds(object), + status=object.status, + ) + for object in scene_objects(scene) + ] +end + +function _scene_environment_backends(scene::Scene, compiled::CompiledScene) + backends = Any[] + seen = Set{UInt}() + for application in compiled.applications + backend = _environment_backend_from_config(scene, environment_config(application.spec)) + isnothing(backend) && continue + id = objectid(backend) + id in seen && continue + push!(seen, id) + push!(backends, backend) + end + return backends +end + +function _update_scene_environment_indices!(scene::Scene, compiled::CompiledScene) + entities = _scene_environment_entities(scene) + for backend in _scene_environment_backends(scene, compiled) + update_index!(backend, entities) + end + return nothing +end + +function _environment_variable_names(vars) + return Symbol[Symbol(var) for var in keys(vars)] +end + +function _environment_source_variable_names(model_spec) + return Symbol[Symbol(source) for (_, source) in _environment_sampling_rules(model_spec)] +end + +function _compile_environment_bindings_for_applications(scene::Scene, applications) + bindings = CompiledEnvironmentBinding[] + for application in applications + config = environment_config(application.spec) + backend = _environment_backend_from_config(scene, config) + provider = _environment_provider_from_config(config, backend) + required_inputs = _environment_variable_names(meteo_inputs_(application.spec)) + source_inputs = _environment_source_variable_names(application.spec) + produced_outputs = _environment_variable_names(meteo_outputs_(application.spec)) + for object_id in application.target_ids + object = _scene_object(scene, object_id) + support = _object_environment_support(application, object) + binding_object, geometry_source_object_id, geometry_source = + _environment_binding_object(scene, object) + cell = bind_environment( + backend, + binding_object, + support, + _environment_config_payload(config), + ) + push!( + bindings, + CompiledEnvironmentBinding( + application.id, + object_id, + provider, + backend, + cell, + required_inputs, + source_inputs, + produced_outputs, + support, + geometry_source_object_id, + geometry_source, + config, + ), + ) + end + end + return bindings +end + +_compile_environment_bindings(scene::Scene, compiled::CompiledScene) = + _compile_environment_bindings_for_applications(scene, compiled.applications) + +function _validate_scene_environment_inputs!(bindings, applications_by_id) + missing_rows = NamedTuple[] + for binding in bindings + available = environment_variables(binding.backend) + isnothing(available) && continue + application = applications_by_id[binding.application_id] + for (target, source) in _environment_sampling_rules(application.spec) + source in available && continue + push!( + missing_rows, + ( + application_id=binding.application_id, + object_id=binding.object_id.value, + process=application.process, + target=target, + source=source, + available=Tuple(sort!(collect(available); by=string)), + ), + ) + end + end + isempty(missing_rows) && return nothing + details = join( + [ + string( + row.application_id, + "/", + row.object_id, + " (", + row.process, + ") needs `", + row.target, + "` from source `", + row.source, + "`; available=", + row.available, + ) + for row in missing_rows + ], + "; ", + ) + error("Scene environment is missing required meteo inputs: ", details) +end + +function _scene_environment_samplers(bindings) + samplers_by_application = Dict{Symbol,Any}() + prepared_sources = Tuple{Any,Any}[] + for binding in bindings + haskey(samplers_by_application, binding.application_id) && continue + binding.backend isa GlobalConstant || continue + meteo = environment_meteo(binding.backend) + source_index = findfirst(entry -> entry[1] === meteo, prepared_sources) + sampler = if isnothing(source_index) + prepared = _prepare_meteo_sampler(meteo) + push!(prepared_sources, (meteo, prepared)) + prepared + else + prepared_sources[source_index][2] + end + samplers_by_application[binding.application_id] = sampler + end + return samplers_by_application +end + +function _compiled_environment_bindings( + scene::Scene, + bindings, + by_target, + samplers_by_application=_scene_environment_samplers(bindings), + sample_cache=Dict{Tuple{Symbol,Int},Any}(), +) + return CompiledEnvironmentBindings( + scene, + bindings, + by_target, + samplers_by_application, + sample_cache, + scene.revision, + scene.environment_revision, + ) +end + +function compile_environment_bindings(scene::Scene, compiled::CompiledScene=refresh_bindings!(scene)) + _update_scene_environment_indices!(scene, compiled) + bindings = _compile_environment_bindings(scene, compiled) + _validate_scene_environment_inputs!(bindings, compiled.applications_by_id) + by_target = Dict( + (binding.application_id, binding.object_id) => binding + for binding in bindings + ) + length(by_target) == length(bindings) || error( + "Environment binding compilation produced duplicate `(application_id, object_id)` targets." + ) + return _compiled_environment_bindings(scene, bindings, by_target) +end + +function _same_environment_backend(a, b) + a === b && return true + if a isa GlobalConstant && b isa GlobalConstant + return environment_meteo(a) === environment_meteo(b) + end + return false +end + +function _same_environment_support(a, b) + return a.application == b.application && + a.scale == b.scale && + a.process == b.process && + a.status === b.status +end + +function _reconcile_environment_binding_metadata( + scene::Scene, + compiled::CompiledScene, + cached::CompiledEnvironmentBindings, +) + expected_count = sum(length(application.target_ids) for application in compiled.applications) + expected_count == length(cached.bindings) || return nothing + + bindings = CompiledEnvironmentBinding[] + changed = false + for application in compiled.applications + config = environment_config(application.spec) + backend = _environment_backend_from_config(scene, config) + provider = _environment_provider_from_config(config, backend) + required_inputs = _environment_variable_names(meteo_inputs_(application.spec)) + source_inputs = _environment_source_variable_names(application.spec) + produced_outputs = _environment_variable_names(meteo_outputs_(application.spec)) + for object_id in application.target_ids + key = (application.id, object_id) + old = get(cached.by_target, key, nothing) + isnothing(old) && return nothing + object = _scene_object(scene, object_id) + support = _object_environment_support(application, object) + _, geometry_source_object_id, geometry_source = + _environment_binding_object(scene, object) + _same_environment_backend(old.backend, backend) || return nothing + old.provider == provider || return nothing + isequal(old.config, config) || return nothing + old.geometry_source_object_id == geometry_source_object_id || + return nothing + old.geometry_source == geometry_source || return nothing + _same_environment_support(old.support, support) || return nothing + + if old.required_inputs == required_inputs && + old.source_inputs == source_inputs && + old.produced_outputs == produced_outputs + push!(bindings, old) + else + changed = true + push!( + bindings, + CompiledEnvironmentBinding( + application.id, + object_id, + provider, + backend, + old.cell, + required_inputs, + source_inputs, + produced_outputs, + support, + geometry_source_object_id, + geometry_source, + config, + ), + ) + end + end + end + changed || return cached + _validate_scene_environment_inputs!(bindings, compiled.applications_by_id) + by_target = Dict( + (binding.application_id, binding.object_id) => binding + for binding in bindings + ) + return _compiled_environment_bindings(scene, bindings, by_target) +end + +function _refresh_environment_bindings_for_objects( + scene::Scene, + compiled::CompiledScene, + cached::CompiledEnvironmentBindings, + dirty_object_ids, +) + _update_scene_environment_indices!(scene, compiled) + dirty = Set(dirty_object_ids) + replacements = Dict{Tuple{Symbol,ObjectId},CompiledEnvironmentBinding}() + for application in compiled.applications + selected_ids = ObjectId[id for id in application.target_ids if id in dirty] + isempty(selected_ids) && continue + partial_application = CompiledSceneApplication( + application.id, + application.spec, + application.process, + application.name, + selected_ids, + application.applies_to, + application.timestep, + application.clock, + application.model_overrides, + ) + for binding in _compile_environment_bindings_for_applications(scene, (partial_application,)) + replacements[(binding.application_id, binding.object_id)] = binding + end + end + bindings = CompiledEnvironmentBinding[ + get(replacements, (binding.application_id, binding.object_id), binding) + for binding in cached.bindings + ] + _validate_scene_environment_inputs!(bindings, compiled.applications_by_id) + by_target = Dict( + (binding.application_id, binding.object_id) => binding for binding in bindings + ) + return _compiled_environment_bindings( + scene, + bindings, + by_target, + cached.samplers_by_application, + cached.sample_cache, + ) +end + +function explain_environment_bindings(compiled::CompiledEnvironmentBindings) + return [ + ( + application_id=binding.application_id, + object_id=binding.object_id.value, + provider=binding.provider, + backend_type=isnothing(binding.backend) ? nothing : typeof(binding.backend), + cell=binding.cell, + required_inputs=binding.required_inputs, + source_inputs=binding.source_inputs, + produced_outputs=binding.produced_outputs, + temporal_sampler=!isnothing( + get(compiled.samplers_by_application, binding.application_id, nothing), + ), + geometry_source_object_id=isnothing(binding.geometry_source_object_id) ? + nothing : binding.geometry_source_object_id.value, + geometry_source=binding.geometry_source, + support=binding.support, + config=binding.config, + ) + for binding in compiled.bindings + ] +end + +function explain_environment_bindings(scene::Scene) + return explain_environment_bindings(refresh_environment_bindings!(scene)) +end + diff --git a/src/scene_object/registry_topology.jl b/src/scene_object/registry_topology.jl new file mode 100644 index 000000000..e84f71dc9 --- /dev/null +++ b/src/scene_object/registry_topology.jl @@ -0,0 +1,1065 @@ +abstract type AbstractObjectSelector end +abstract type AbstractObjectMultiplicity end + +struct ObjectRefVector{R} <: AbstractVector{Any} + refs::R +end + +Base.size(v::ObjectRefVector) = size(v.refs) +Base.length(v::ObjectRefVector) = length(v.refs) +Base.getindex(v::ObjectRefVector, i::Int) = v.refs[i][] +Base.setindex!(v::ObjectRefVector, value, i::Int) = (v.refs[i][] = value) +Base.parent(v::ObjectRefVector) = v.refs + +struct ObjectId{T} + value::T +end +ObjectId(id::ObjectId) = id +ObjectId(id::AbstractString) = ObjectId(Symbol(id)) + +mutable struct Object + id::ObjectId + scale::Union{Nothing,Symbol} + kind::Union{Nothing,Symbol} + species::Union{Nothing,Symbol} + name::Union{Nothing,Symbol} + parent::Union{Nothing,ObjectId} + children::Vector{ObjectId} + geometry::Any + status::Any + applications::Any +end + +""" + ObjectTemplate(applications=(); kind=nothing, species=nothing, parameters=NamedTuple()) + +Reusable model-application bundle for one kind of scene object, such as a plant +species. Each mounted `ObjectInstance` scopes unqualified `AppliesTo(...)` +selectors to its own object subtree. Model objects are shared between instances +unless an instance supplies an override. + +`parameters` stores template-level metadata. Parameter-field merging is not +implicit: use an instance model override when model parameters differ. +""" +struct ObjectTemplate{A,P} + kind::Union{Nothing,Symbol} + species::Union{Nothing,Symbol} + applications::A + parameters::P +end + +_as_tuple(value::Tuple) = value +_as_tuple(value::AbstractVector) = Tuple(value) +_as_tuple(value) = (value,) + +function ObjectTemplate( + applications=(); + kind=nothing, + species=nothing, + parameters=NamedTuple(), +) + normalized_applications = _as_tuple(applications) + return ObjectTemplate( + _maybe_symbol(kind), + _maybe_symbol(species), + normalized_applications, + parameters, + ) +end + +""" + Override(; object, model, process=nothing, application=nothing) + +Replace one template model application on one exceptional object. Select the +template application by its process, explicit application name, or both. +The replacement must implement the same process and variable contract. +""" +struct Override{M<:AbstractModel} + object::ObjectId + process::Union{Nothing,Symbol} + application::Union{Nothing,Symbol} + model::M +end + +function Override(; object, model::AbstractModel, process=nothing, application=nothing) + normalized_process = _maybe_symbol(process) + normalized_application = _maybe_symbol(application) + if isnothing(normalized_process) && isnothing(normalized_application) + error("`Override(...)` requires `process=...`, `application=...`, or both.") + end + if !isnothing(normalized_process) && isnothing(normalized_application) + Base.depwarn( + "Process-only `Override` selection is deprecated; name the template application and use `application=`.", + :Override, + ) + end + return Override( + ObjectId(object), + normalized_process, + normalized_application, + model, + ) +end + +""" + ObjectInstance(name, template; root, objects=(), overrides=NamedTuple(), object_overrides=()) + +Mount an `ObjectTemplate` on one concrete scene-object subtree. + +`root` may be an `Object` owned by the instance or the id of an object supplied +separately to `Scene`. `objects` contains additional owned descendants. +`overrides` maps one template application name or process to a replacement +model implementing the same process. `object_overrides` contains `Override` +entries for exceptional organs. +""" +struct ObjectInstance{T,R,O,OV,OOV} + name::Symbol + template::T + root::R + objects::O + overrides::OV + object_overrides::OOV +end + +function ObjectInstance( + name, + template::ObjectTemplate; + root, + objects=(), + overrides=NamedTuple(), + object_overrides=(), +) + normalized_objects = _as_tuple(objects) + normalized_object_overrides = _as_tuple(object_overrides) + all(object -> object isa Object, normalized_objects) || error( + "`ObjectInstance(...; objects=...)` must contain `Object` values." + ) + overrides isa NamedTuple || error( + "`ObjectInstance(...; overrides=...)` must be a NamedTuple keyed by application name or process." + ) + all(override -> override isa Override, normalized_object_overrides) || error( + "`ObjectInstance(...; object_overrides=...)` must contain `Override` values." + ) + return ObjectInstance( + Symbol(name), + template, + root, + normalized_objects, + overrides, + normalized_object_overrides, + ) +end + +struct ObjectModelOverrides{M,O} <: AbstractModel + base::M + overrides::O +end + +process(model::ObjectModelOverrides) = process(model.base) +inputs_(model::ObjectModelOverrides) = inputs_(model.base) +outputs_(model::ObjectModelOverrides) = outputs_(model.base) +dep(model::ObjectModelOverrides) = dep(model.base) +timespec(model::ObjectModelOverrides) = timespec(model.base) +output_policy(model::ObjectModelOverrides) = output_policy(model.base) +meteo_inputs_(model::ObjectModelOverrides) = meteo_inputs_(model.base) +meteo_outputs_(model::ObjectModelOverrides) = meteo_outputs_(model.base) + +function Object( + id; + scale=nothing, + kind=nothing, + species=nothing, + name=nothing, + parent=nothing, + children=ObjectId[], + geometry=nothing, + status=nothing, + applications=(), +) + return Object( + ObjectId(id), + _maybe_symbol(scale), + _maybe_symbol(kind), + _maybe_symbol(species), + _maybe_symbol(name), + isnothing(parent) ? nothing : ObjectId(parent), + ObjectId[ObjectId(child) for child in children], + geometry, + status, + applications, + ) +end + +mutable struct SceneRegistry + objects::Dict{ObjectId,Any} + by_scale::Dict{Symbol,Set{ObjectId}} + by_kind::Dict{Symbol,Set{ObjectId}} + by_species::Dict{Symbol,Set{ObjectId}} + by_name::Dict{Symbol,ObjectId} +end + +SceneRegistry() = SceneRegistry( + Dict{ObjectId,Any}(), + Dict{Symbol,Set{ObjectId}}(), + Dict{Symbol,Set{ObjectId}}(), + Dict{Symbol,Set{ObjectId}}(), + Dict{Symbol,ObjectId}(), +) + +struct MTGObjectAdapter{I,S,K,SP,N,G,ST} + id::I + scale::S + kind::K + species::SP + name::N + geometry::G + status::ST +end + +mutable struct Scene{R,A,E,I,SA} + registry::R + applications::A + environment::E + instances::I + source_adapter::SA + binding_cache::Any + environment_binding_cache::Any + bindings_dirty::Bool + environment_bindings_dirty::Bool + environment_dirty_objects::Union{Nothing,Set{ObjectId}} + revision::Int + environment_revision::Int +end + +function _normalize_object_instances(instances) + instances isa ObjectInstance && return (instances,) + normalized = _as_tuple(instances) + all(instance -> instance isa ObjectInstance, normalized) || error( + "Scene instances must be `ObjectInstance` values." + ) + return normalized +end + +function _instance_root_id(instance::ObjectInstance) + return instance.root isa Object ? instance.root.id : ObjectId(instance.root) +end + +function _collect_scene_items(items, instances) + objects = Object[] + mounted_instances = ObjectInstance[] + for item in items + if item isa Object + push!(objects, item) + elseif item isa ObjectInstance + push!(mounted_instances, item) + else + error("A `Scene` can contain only `Object` and `ObjectInstance` values, got `$(typeof(item))`.") + end + end + append!(mounted_instances, _normalize_object_instances(instances)) + for instance in mounted_instances + instance.root isa Object && push!(objects, instance.root) + append!(objects, instance.objects) + end + ids = Set{ObjectId}() + for object in objects + object.id in ids && error("Scene contains object id `$(object.id.value)` more than once.") + push!(ids, object.id) + end + return objects, mounted_instances +end + +function _object_descendant_ids(objects_by_id, root_id::ObjectId) + ids = ObjectId[root_id] + frontier = ObjectId[root_id] + while !isempty(frontier) + parent_id = popfirst!(frontier) + for object in values(objects_by_id) + object.parent == parent_id || continue + object.id in ids && continue + push!(ids, object.id) + push!(frontier, object.id) + end + end + return ids +end + +function _prepare_object_instances!(objects, instances) + objects_by_id = Dict(object.id => object for object in objects) + claimed_ids = Dict{ObjectId,Symbol}() + instance_ids = Dict{Symbol,Vector{ObjectId}}() + for instance in instances + root_id = _instance_root_id(instance) + haskey(objects_by_id, root_id) || error( + "Object instance `$(instance.name)` refers to missing root object `$(root_id.value)`." + ) + ids = _object_descendant_ids(objects_by_id, root_id) + for id in ids + if haskey(claimed_ids, id) + error( + "Object `$(id.value)` belongs to both instances `$(claimed_ids[id])` and `$(instance.name)`." + ) + end + claimed_ids[id] = instance.name + object = objects_by_id[id] + isnothing(object.kind) && (object.kind = instance.template.kind) + isnothing(object.species) && (object.species = instance.template.species) + end + root = objects_by_id[root_id] + if !isnothing(root.name) && root.name != instance.name + error( + "Object instance `$(instance.name)` root `$(root_id.value)` already has the conflicting name `$(root.name)`." + ) + end + root.name = instance.name + instance_ids[instance.name] = ids + end + length(instance_ids) == length(instances) || error("Object instance names must be unique within a scene.") + return instance_ids +end + +function _register_scene_objects!(scene::Scene, objects) + pending = copy(objects) + while !isempty(pending) + registered = false + for index in reverse(eachindex(pending)) + object = pending[index] + if isnothing(object.parent) || haskey(scene.registry.objects, object.parent) + register_object!(scene, object) + deleteat!(pending, index) + registered = true + end + end + registered && continue + unresolved = [(object.id.value, isnothing(object.parent) ? nothing : object.parent.value) for object in pending] + error("Cannot register scene objects because parent objects are missing or cyclic: $(unresolved).") + end + return scene +end + +""" + Scene(items...; applications=(), instances=(), environment=nothing) + +Create a scene from `Object` and `ObjectInstance` values. Global applications +and applications mounted from object instances are compiled through the same +scene/object dependency graph. +""" +function Scene( + items::Union{Object,ObjectInstance}...; + applications=(), + instances=(), + environment=nothing, + source_adapter=nothing, +) + objects, mounted_instances = _collect_scene_items(items, instances) + instance_ids = _prepare_object_instances!(objects, mounted_instances) + mounted_applications = _mount_object_instance_applications(mounted_instances, instance_ids) + normalized_applications = collect(Any, _as_tuple(applications)) + append!(normalized_applications, mounted_applications) + scene = Scene( + SceneRegistry(), + normalized_applications, + environment, + mounted_instances, + source_adapter, + nothing, + nothing, + true, + true, + nothing, + 0, + 0, + ) + return _register_scene_objects!(scene, objects) +end + +""" + Scene(model::AbstractModel, models::AbstractModel...; + status=NamedTuple(), id=:scene, scale=:Scene, kind=:scene, + name=id, environment=nothing) + +Construct a concise one-object simulation. This is syntax lowering only: it +creates one ordinary [`Object`](@ref), one normal `ModelSpec` per model, and a +regular [`Scene`](@ref). The returned scene therefore uses the same compiler, +scheduler, diagnostics, lifecycle, and output system as explicitly assembled +scenes. + +Use explicit `Object`, `ModelSpec`, and selector construction when applications +need names, different cadences, explicit coupling, or different target sets. +""" +function Scene( + model::AbstractModel, + models::AbstractModel...; + status=NamedTuple(), + id=:scene, + scale=:Scene, + kind=:scene, + name=id, + environment=nothing, +) + object_name = isnothing(name) ? nothing : Symbol(string(name)) + object_status = if status isa Status || isnothing(status) + status + elseif status isa Union{NamedTuple,AbstractDict,Base.Pairs} + Status((; (Symbol(key) => value for (key, value) in pairs(status))...)) + else + error( + "One-object `Scene(...; status=...)` requires a `Status`, `NamedTuple`, ", + "`AbstractDict`, `Base.Pairs`, or `nothing`, got `$(typeof(status))`." + ) + end + selector = isnothing(object_name) ? One(scale=scale) : One(name=object_name) + applications = map((model, models...)) do application_model + ModelSpec(application_model) |> AppliesTo(selector) + end + return Scene( + Object( + id; + scale=scale, + kind=kind, + name=object_name, + status=object_status, + ); + applications=applications, + environment=environment, + ) +end + +function _mtg_attribute(node, key::Symbol, default=nothing) + try + return node[key] + catch + return default + end +end + +""" + objects_from_mtg(root; id=node_id, scale=symbol, kind=..., species=..., + name=..., geometry=..., status=...) + +Adapt one MTG subtree to scene `Object` values. The MTG is traversed once; +node ids and parent relations become stable scene-object identities and +relations. Accessors may attach labels, geometry, and existing status objects +without prescribing a plant architecture. +""" +function objects_from_mtg( + root::MultiScaleTreeGraph.Node; + id=node_id, + scale=symbol, + kind=node -> _mtg_attribute(node, :kind, nothing), + species=node -> _mtg_attribute(node, :species, nothing), + name=node -> _mtg_attribute(node, :name, nothing), + geometry=node -> _mtg_attribute(node, :geometry, nothing), + status=node -> _mtg_attribute(node, :plantsimengine_status, nothing), +) + adapter = MTGObjectAdapter(id, scale, kind, species, name, geometry, status) + return _objects_from_mtg(root, adapter) +end + +function _objects_from_mtg(root::MultiScaleTreeGraph.Node, adapter::MTGObjectAdapter) + objects = Object[] + MultiScaleTreeGraph.traverse!(root) do node + node_parent = parent(node) + parent_id = node === root || isnothing(node_parent) ? nothing : adapter.id(node_parent) + push!( + objects, + Object( + adapter.id(node); + scale=adapter.scale(node), + kind=adapter.kind(node), + species=adapter.species(node), + name=adapter.name(node), + parent=parent_id, + geometry=adapter.geometry(node), + status=adapter.status(node), + ), + ) + end + return objects +end + +""" + Scene(root::MultiScaleTreeGraph.Node; applications=(), instances=(), + environment=nothing, id=node_id, scale=symbol, status=..., ...) + +Build a unified scene directly from an MTG subtree. The MTG accessors are +retained and reused by [`add_organ!`](@ref) when the topology grows. +""" +function Scene( + root::MultiScaleTreeGraph.Node; + applications=(), + instances=(), + environment=nothing, + id=node_id, + scale=symbol, + kind=node -> _mtg_attribute(node, :kind, nothing), + species=node -> _mtg_attribute(node, :species, nothing), + name=node -> _mtg_attribute(node, :name, nothing), + geometry=node -> _mtg_attribute(node, :geometry, nothing), + status=node -> _mtg_attribute(node, :plantsimengine_status, nothing), +) + adapter = MTGObjectAdapter(id, scale, kind, species, name, geometry, status) + objects = _objects_from_mtg(root, adapter) + return Scene( + objects...; + applications=applications, + instances=instances, + environment=environment, + source_adapter=adapter, + ) +end + +function _mark_environment_bindings_dirty!(scene::Scene, object_id::Union{Nothing,ObjectId}=nothing) + if isnothing(object_id) || isnothing(scene.environment_binding_cache) + scene.environment_binding_cache = nothing + scene.environment_dirty_objects = nothing + elseif !isnothing(scene.environment_dirty_objects) + push!(scene.environment_dirty_objects, object_id) + end + scene.environment_bindings_dirty = true + scene.environment_revision += 1 + return scene +end + +function _mark_bindings_dirty!(scene::Scene) + scene.binding_cache = nothing + scene.bindings_dirty = true + scene.revision += 1 + return _mark_environment_bindings_dirty!(scene) +end + +bindings_dirty(scene::Scene) = scene.bindings_dirty +environment_bindings_dirty(scene::Scene) = scene.environment_bindings_dirty +scene_revision(scene::Scene) = scene.revision +environment_revision(scene::Scene) = scene.environment_revision +compiled_bindings(scene::Scene) = scene.binding_cache +compiled_environment_bindings(scene::Scene) = scene.environment_binding_cache +mark_environment_binding_dirty!(scene::Scene) = _mark_environment_bindings_dirty!(scene) +function mark_environment_binding_dirty!(scene::Scene, id) + object_id = ObjectId(id) + _scene_object(scene, object_id) + return _mark_environment_bindings_dirty!(scene, object_id) +end +function mark_environment_binding_dirty!(scene::Scene, object::Object) + return mark_environment_binding_dirty!(scene, object.id) +end + +function _push_index!(index::Dict{Symbol,Set{ObjectId}}, key, id::ObjectId) + isnothing(key) && return nothing + push!(get!(index, key, Set{ObjectId}()), id) + return nothing +end + +function _delete_index!(index::Dict{Symbol,Set{ObjectId}}, key, id::ObjectId) + isnothing(key) && return nothing + ids = get(index, key, nothing) + isnothing(ids) && return nothing + delete!(ids, id) + isempty(ids) && delete!(index, key) + return nothing +end + +function _index_object!(registry::SceneRegistry, object::Object) + _push_index!(registry.by_scale, object.scale, object.id) + _push_index!(registry.by_kind, object.kind, object.id) + _push_index!(registry.by_species, object.species, object.id) + if !isnothing(object.name) + existing = get(registry.by_name, object.name, nothing) + if !isnothing(existing) && existing != object.id + error( + "Scene object name `$(object.name)` is already used by object `$(existing.value)`." + ) + end + registry.by_name[object.name] = object.id + end + return nothing +end + +function _deindex_object!(registry::SceneRegistry, object::Object) + _delete_index!(registry.by_scale, object.scale, object.id) + _delete_index!(registry.by_kind, object.kind, object.id) + _delete_index!(registry.by_species, object.species, object.id) + if !isnothing(object.name) && get(registry.by_name, object.name, nothing) == object.id + delete!(registry.by_name, object.name) + end + return nothing +end + +function _scene_object(scene::Scene, id) + oid = ObjectId(id) + haskey(scene.registry.objects, oid) || error("No scene object with id `$(oid.value)`.") + return scene.registry.objects[oid] +end + +function _instance_for_object(scene::Scene, id) + current_id = ObjectId(id) + while haskey(scene.registry.objects, current_id) + for instance in scene.instances + _instance_root_id(instance) == current_id && return instance + end + parent = scene.registry.objects[current_id].parent + isnothing(parent) && return nothing + current_id = parent + end + return nothing +end + +function _apply_instance_labels!(object::Object, instance) + isnothing(instance) && return object + isnothing(object.kind) && (object.kind = instance.template.kind) + isnothing(object.species) && (object.species = instance.template.species) + return object +end + +""" + register_object!(scene, object; parent=object.parent) + +Register a fully initialized [`Object`](@ref) in `scene`. Structural bindings +are marked dirty and become visible to execution after the next lifecycle +refresh boundary. Prefer [`add_organ!`](@ref) for MTG-backed growth. +""" +function register_object!(scene::Scene, object::Object; parent=object.parent) + registry = scene.registry + haskey(registry.objects, object.id) && error("Scene already contains object id `$(object.id.value)`.") + parent_id = isnothing(parent) ? nothing : ObjectId(parent) + if !isnothing(parent_id) && !haskey(registry.objects, parent_id) + error("No scene object with id `$(parent_id.value)`.") + end + if !isnothing(object.name) + existing = get(registry.by_name, object.name, nothing) + isnothing(existing) || error( + "Scene object name `$(object.name)` is already used by object `$(existing.value)`." + ) + end + instance = isnothing(parent_id) ? nothing : _instance_for_object(scene, parent_id) + _apply_instance_labels!(object, instance) + object.parent = parent_id + registry.objects[object.id] = object + _index_object!(registry, object) + if !isnothing(object.parent) + parent_object = registry.objects[object.parent] + object.id in parent_object.children || push!(parent_object.children, object.id) + end + _mark_bindings_dirty!(scene) + return object +end + +function _status_data!(data::Dict{Symbol,Any}, values) + values === nothing && return data + source = values isa Status ? NamedTuple(values) : values + source isa Union{NamedTuple,AbstractDict,Base.Pairs} || error( + "Initial organ status must be a `Status`, `NamedTuple`, `AbstractDict`, ", + "`Base.Pairs`, or `nothing`, got `$(typeof(values))`." + ) + for (key, value) in pairs(source) + Symbol(key) == :plantsimengine_status && continue + data[Symbol(key)] = value + end + return data +end + +function _organ_status(adapter::MTGObjectAdapter, node, initial_status) + adapted_status = adapter.status(node) + data = Dict{Symbol,Any}() + _status_data!(data, MultiScaleTreeGraph.node_attributes(node)) + _status_data!(data, adapted_status) + data[:node] = node + _status_data!(data, initial_status) + data[:node] = node + return Status((; data...)) +end + +""" + add_organ!(parent, runtime, link, symbol, scale; index=0, id, attributes=(), + initial_status=(), kind=nothing, species=nothing, name=nothing) + +Create an MTG node and register its corresponding scene object as one operation. +`runtime` may be a [`Scene`](@ref), [`SceneRunContext`](@ref), or +[`SceneSimulation`](@ref). The scene reuses the MTG accessors and status +initializer supplied when it was constructed, then overlays `initial_status`. + +This is the public growth API. [`register_object!`](@ref) remains the low-level +registry operation for callers that already own a fully initialized `Object`. +""" +function add_organ!( + parent_node::MultiScaleTreeGraph.Node, + runtime, + link, + organ_symbol, + mtg_scale::Integer; + index::Integer=0, + id=MultiScaleTreeGraph.new_id(MultiScaleTreeGraph.get_root(parent_node)), + attributes=NamedTuple(), + initial_status=NamedTuple(), + kind=nothing, + species=nothing, + name=nothing, +) + scene = runtime_scene(runtime) + adapter = scene.source_adapter + adapter isa MTGObjectAdapter || error( + "`add_organ!` requires a scene constructed from an MTG. Use ", + "`register_object!` for scenes built directly from `Object` values." + ) + parent_id = ObjectId(adapter.id(parent_node)) + _scene_object(scene, parent_id) + root = MultiScaleTreeGraph.get_root(parent_node) + isnothing(MultiScaleTreeGraph.get_node(root, Int(id))) || error( + "MTG node id `$(id)` already exists." + ) + + node = MultiScaleTreeGraph.Node( + Int(id), + parent_node, + MultiScaleTreeGraph.NodeMTG( + Symbol(link), + Symbol(organ_symbol), + Int(index), + Int(mtg_scale), + ), + attributes, + ) + try + status = _organ_status(adapter, node, initial_status) + node[:plantsimengine_status] = status + object = Object( + adapter.id(node); + scale=adapter.scale(node), + kind=isnothing(kind) ? adapter.kind(node) : kind, + species=isnothing(species) ? adapter.species(node) : species, + name=isnothing(name) ? adapter.name(node) : name, + parent=parent_id, + geometry=adapter.geometry(node), + status=status, + ) + register_object!(scene, object) + return status + catch + MultiScaleTreeGraph.delete_node!(node) + rethrow() + end +end + +function _remove_child_link!(scene::Scene, parent_id, child_id::ObjectId) + isnothing(parent_id) && return nothing + parent_object = _scene_object(scene, parent_id) + filter!(!=(child_id), parent_object.children) + return nothing +end + +function remove_object!(scene::Scene, id; recursive::Bool=true) + object = _scene_object(scene, id) + if !recursive && !isempty(object.children) + error("Cannot remove object `$(object.id.value)` with children unless `recursive=true`.") + end + for child in copy(object.children) + remove_object!(scene, child; recursive=true) + end + _remove_child_link!(scene, object.parent, object.id) + _deindex_object!(scene.registry, object) + delete!(scene.registry.objects, object.id) + _mark_bindings_dirty!(scene) + return object +end + +function reparent_object!(scene::Scene, id, new_parent) + object = _scene_object(scene, id) + new_parent_id = isnothing(new_parent) ? nothing : ObjectId(new_parent) + if !isnothing(new_parent_id) + haskey(scene.registry.objects, new_parent_id) || error("No scene object with id `$(new_parent_id.value)`.") + new_parent_id == object.id && error( + "Cannot reparent object `$(object.id.value)` to itself.", + ) + new_parent_id in _descendant_ids(scene, object.id) && error( + "Cannot reparent object `$(object.id.value)` below its descendant `$(new_parent_id.value)`.", + ) + end + _remove_child_link!(scene, object.parent, object.id) + object.parent = new_parent_id + if !isnothing(new_parent_id) + parent_object = _scene_object(scene, new_parent_id) + object.id in parent_object.children || push!(parent_object.children, object.id) + end + _mark_bindings_dirty!(scene) + return object +end + +function move_object!(scene::Scene, id, geometry_or_position) + return update_geometry!(scene, id, geometry_or_position) +end + +function update_geometry!(scene::Scene, id, geometry_or_position; invalidate_environment::Bool=true) + object = _scene_object(scene, id) + object.geometry = geometry_or_position + if invalidate_environment + _mark_environment_bindings_dirty!(scene, object.id) + for descendant_id in _geometry_inheriting_descendants(scene, object.id) + _mark_environment_bindings_dirty!(scene, descendant_id) + end + end + return object +end + +function update_geometry!(object::Object, geometry_or_position) + object.geometry = geometry_or_position + return object +end + +geometry(object::Object) = object.geometry +geometry(status::Status) = hasproperty(status, :geometry) ? status.geometry : nothing +geometry(x) = hasproperty(x, :geometry) ? getproperty(x, :geometry) : nothing + +function _geometry_position(g::NamedTuple) + haskey(g, :position) && return getproperty(g, :position) + if haskey(g, :x) && haskey(g, :y) && haskey(g, :z) + return (x=g.x, y=g.y, z=g.z) + elseif haskey(g, :x) && haskey(g, :y) + return (x=g.x, y=g.y) + end + return nothing +end +_geometry_position(_) = nothing + +position(object::Object) = _geometry_position(geometry(object)) +position(status::Status) = _geometry_position(geometry(status)) + +_geometry_bounds(g::NamedTuple) = haskey(g, :bounds) ? getproperty(g, :bounds) : nothing +_geometry_bounds(_) = nothing +bounds(object::Object) = _geometry_bounds(geometry(object)) +bounds(status::Status) = _geometry_bounds(geometry(status)) + +function _geometry_inheriting_descendants(scene::Scene, root_id::ObjectId) + ids = ObjectId[] + for child_id in _scene_object(scene, root_id).children + child = _scene_object(scene, child_id) + isnothing(geometry(child)) || continue + push!(ids, child_id) + append!(ids, _geometry_inheriting_descendants(scene, child_id)) + end + return ids +end + +function refresh_bindings!(scene::Scene, specs=scene.applications; force::Bool=false) + uses_scene_applications = specs === scene.applications + if !uses_scene_applications + return compile_scene(scene, specs) + end + if force || scene.bindings_dirty || isnothing(scene.binding_cache) + scene.binding_cache = compile_scene(scene, scene.applications) + scene.bindings_dirty = false + end + return scene.binding_cache +end + +function refresh_environment_bindings!(scene::Scene, compiled=refresh_bindings!(scene); force::Bool=false) + if force || scene.environment_bindings_dirty || isnothing(scene.environment_binding_cache) + if !force && + !isnothing(scene.environment_binding_cache) && + !isnothing(scene.environment_dirty_objects) + scene.environment_binding_cache = _refresh_environment_bindings_for_objects( + scene, + compiled, + scene.environment_binding_cache, + scene.environment_dirty_objects, + ) + else + scene.environment_binding_cache = compile_environment_bindings(scene, compiled) + end + scene.environment_bindings_dirty = false + scene.environment_dirty_objects = Set{ObjectId}() + else + reconciled = _reconcile_environment_binding_metadata( + scene, + compiled, + scene.environment_binding_cache, + ) + scene.environment_binding_cache = isnothing(reconciled) ? + compile_environment_bindings(scene, compiled) : + reconciled + end + return scene.environment_binding_cache +end + +function object_ids(scene::Scene; scale=nothing, kind=nothing, species=nothing, name=nothing) + registry = scene.registry + sets = Set{ObjectId}[] + isnothing(scale) || push!(sets, copy(get(registry.by_scale, Symbol(scale), Set{ObjectId}()))) + isnothing(kind) || push!(sets, copy(get(registry.by_kind, Symbol(kind), Set{ObjectId}()))) + isnothing(species) || push!(sets, copy(get(registry.by_species, Symbol(species), Set{ObjectId}()))) + if !isnothing(name) + id = get(registry.by_name, Symbol(name), nothing) + push!(sets, isnothing(id) ? Set{ObjectId}() : Set([id])) + end + isempty(sets) && return sort!(collect(keys(registry.objects)); by=id -> string(id.value)) + ids = reduce(intersect, sets) + return sort!(collect(ids); by=id -> string(id.value)) +end + +scene_objects(scene::Scene; kwargs...) = [_scene_object(scene, id) for id in object_ids(scene; kwargs...)] + +function _instance_object_ids(scene::Scene, instance::ObjectInstance) + root_id = _instance_root_id(instance) + haskey(scene.registry.objects, root_id) || return ObjectId[] + return _sort_object_ids!(_descendant_ids(scene, root_id)) +end + +function _object_instance_name(scene::Scene, object_id::ObjectId) + instance = _instance_for_object(scene, object_id) + return isnothing(instance) ? nothing : instance.name +end + +function explain_objects(scene::Scene) + return [ + ( + id=object.id.value, + scale=object.scale, + kind=object.kind, + species=object.species, + name=object.name, + instance=_object_instance_name(scene, object.id), + parent=isnothing(object.parent) ? nothing : object.parent.value, + children=[child.value for child in object.children], + has_geometry=!isnothing(object.geometry), + has_status=!isnothing(object.status), + n_applications=length(object.applications), + ) + for object in scene_objects(scene) + ] +end + +function _instance_application_ids(scene::Scene, instance::ObjectInstance) + prefix = string(instance.name, "__") + ids = Symbol[] + for application in scene.applications + name = application_name(as_model_spec(application)) + isnothing(name) && continue + startswith(string(name), prefix) && push!(ids, name) + end + return sort!(ids; by=string) +end + +function explain_instances(scene::Scene) + return [ + ( + name=instance.name, + root_id=_instance_root_id(instance).value, + kind=instance.template.kind, + species=instance.template.species, + object_ids=[id.value for id in _instance_object_ids(scene, instance)], + application_ids=_instance_application_ids(scene, instance), + instance_overrides=sort!(Symbol[Symbol(name) for name in keys(instance.overrides)]; by=string), + object_overrides=[ + ( + object_id=override.object.value, + process=override.process, + application=override.application, + model_type=typeof(override.model), + ) + for override in instance.object_overrides + ], + parameters_type=typeof(instance.template.parameters), + parameters_shared_by_reference=true, + ) + for instance in scene.instances + ] +end + +function _object_id_values(ids) + return [id.value for id in _sort_object_ids!(collect(ids))] +end + +function _label_scope_rows(scene::Scene, scope_type::Symbol, label::Symbol, index) + return [ + ( + scope_type=scope_type, + selector=label => key, + context=nothing, + root_id=nothing, + scale=label == :scale ? key : nothing, + kind=label == :kind ? key : nothing, + species=label == :species ? key : nothing, + name=nothing, + object_ids=_object_id_values(ids), + n_objects=length(ids), + ) + for (key, ids) in sort!(collect(index); by=pair -> string(first(pair))) + ] +end + +function explain_scopes(scene::Scene) + rows = NamedTuple[] + all_ids = object_ids(scene) + push!( + rows, + ( + scope_type=:scene, + selector=SceneScope(), + context=nothing, + root_id=nothing, + scale=nothing, + kind=nothing, + species=nothing, + name=nothing, + object_ids=[id.value for id in all_ids], + n_objects=length(all_ids), + ), + ) + for object in scene_objects(scene) + push!( + rows, + ( + scope_type=:object_self, + selector=Self(), + context=object.id.value, + root_id=object.id.value, + scale=object.scale, + kind=object.kind, + species=object.species, + name=object.name, + object_ids=[object.id.value], + n_objects=1, + ), + ) + descendant_ids = _object_id_values(_descendant_ids(scene, object.id)) + push!( + rows, + ( + scope_type=:object_subtree, + selector=Subtree(), + context=object.id.value, + root_id=object.id.value, + scale=object.scale, + kind=object.kind, + species=object.species, + name=object.name, + object_ids=descendant_ids, + n_objects=length(descendant_ids), + ), + ) + object_scope_name = object.id.value isa Symbol ? object.id.value : Symbol(string(object.id.value)) + scope_names = Symbol[object_scope_name] + isnothing(object.name) || push!(scope_names, object.name) + unique!(scope_names) + for scope_name in scope_names + push!( + rows, + ( + scope_type=:named_scope, + selector=Scope(scope_name), + context=nothing, + root_id=object.id.value, + scale=object.scale, + kind=object.kind, + species=object.species, + name=scope_name, + object_ids=descendant_ids, + n_objects=length(descendant_ids), + ), + ) + end + end + append!(rows, _label_scope_rows(scene, :scale, :scale, scene.registry.by_scale)) + append!(rows, _label_scope_rows(scene, :kind, :kind, scene.registry.by_kind)) + append!(rows, _label_scope_rows(scene, :species, :species, scene.registry.by_species)) + return rows +end diff --git a/src/scene_object/runtime_outputs.jl b/src/scene_object/runtime_outputs.jl new file mode 100644 index 000000000..1595b7630 --- /dev/null +++ b/src/scene_object/runtime_outputs.jl @@ -0,0 +1,1611 @@ +struct SceneOutputRetentionPlan + retain_all::Bool + temporal_dependencies::Set{Tuple{Symbol,Symbol}} + requested_outputs::Set{Tuple{Symbol,Symbol}} + dependency_horizons::Dict{Tuple{Symbol,Symbol},Float64} +end + +""" + SceneRunContext + +Runtime context passed as the final argument to scene model kernels. Use +`runtime_scene`, `call_target`, and `call_targets` instead of inspecting its +fields. +""" +struct SceneRunContext{CS,EB,A,TS,OR,C} + compiled::CS + environment_bindings::EB + application::A + object_id::ObjectId + temporal_streams::TS + output_retention::OR + time::Float64 + constants::C + publication_allowed::Bool +end + +struct SceneCallTarget{CS,EB,A,S,TS,OR,C} + compiled::CS + environment_bindings::EB + application::A + object_id::ObjectId + model + status::S + temporal_streams::TS + output_retention::OR + time::Float64 + constants::C + publication_allowed::Bool +end + +abstract type AbstractSceneExecutionBatch end + +struct CompiledSceneExecutionTarget{M,S,MB,IB,EB} + object_id::ObjectId + model::M + status::S + models::MB + input_bindings::IB + environment_binding::EB +end + +struct CompiledSceneExecutionBatch{A,T<:AbstractVector} <: AbstractSceneExecutionBatch + application::A + targets::T +end + +struct CompiledSceneExecutionPlan{B} + batches::B + scene_revision::Int + environment_revision::Int +end + +""" + SceneSimulation + +Result of running a [`Scene`](@ref). Use `scene_outputs`, `collect_outputs`, +and the explanation helpers to inspect it. +""" +mutable struct SceneSimulation{S,CS,EB,EP,OR,TS,R,RT,C} + scene::S + compiled::CS + environment_bindings::EB + execution_plan::EP + output_retention::OR + temporal_streams::TS + output_requests::R + output_request_targets::RT + current_step::Int + constants::C +end + +""" + runtime_scene(runtime) + +Return the live [`Scene`](@ref) owned by a `Scene`, [`SceneRunContext`](@ref), +or [`SceneSimulation`](@ref). Lifecycle-capable models should call this +accessor instead of reaching through runtime implementation fields. +""" +runtime_scene(scene::Scene) = scene +runtime_scene(context::SceneRunContext) = context.compiled.scene +runtime_scene(simulation::SceneSimulation) = simulation.scene +current_step(simulation::SceneSimulation) = simulation.current_step + +scene_outputs(sim::SceneSimulation) = sim.temporal_streams + +# Diagnostics accept the live simulation handle without exposing its compiled +# representation. Views concerning topology and initialization use the current +# scene; compiled views use the simulation's current compiled state. +explain_objects(sim::SceneSimulation) = explain_objects(sim.scene) +explain_instances(sim::SceneSimulation) = explain_instances(sim.scene) +explain_scopes(sim::SceneSimulation) = explain_scopes(sim.scene) +explain_initialization(sim::SceneSimulation) = explain_initialization(sim.scene) +explain_scene_applications(sim::SceneSimulation) = explain_scene_applications(sim.compiled) +explain_schedule(sim::SceneSimulation) = explain_schedule(sim.compiled) +explain_bindings(sim::SceneSimulation) = explain_bindings(sim.compiled) +explain_calls(sim::SceneSimulation) = explain_calls(sim.compiled) +explain_model_bundles(sim::SceneSimulation) = explain_model_bundles(sim.compiled) +explain_writers(sim::SceneSimulation) = explain_writers(sim.compiled) +explain_environment_bindings(sim::SceneSimulation) = + explain_environment_bindings(sim.environment_bindings) + +function _compiled_application_by_id(compiled::CompiledScene, id::Symbol) + application = get(compiled.applications_by_id, id, nothing) + isnothing(application) && error("No compiled scene application with id `$(id)`.") + return application +end + +function _environment_binding_for(env_bindings::CompiledEnvironmentBindings, application_id::Symbol, object_id::ObjectId) + return get(env_bindings.by_target, (application_id, object_id), nothing) +end + +function _scene_object_status(scene::Scene, object_id::ObjectId) + object = _scene_object(scene, object_id) + object.status isa Status || error( + "Scene object `$(object_id.value)` has no `Status`; scene runtime requires status-backed objects." + ) + return object.status +end + +function _set_status_if_present!(status::Status, variable::Symbol, value) + variable in propertynames(status) || error( + "Cannot materialize input `$(variable)` because consumer status has no such variable." + ) + status[variable] = value + return status +end + +_scene_stream_key(application_id::Symbol, object_id::ObjectId, variable::Symbol) = + (application_id, object_id, variable) + +function _scene_retain_output( + retention::SceneOutputRetentionPlan, + application_id::Symbol, + variable::Symbol, +) + retention.retain_all && return true + key = (application_id, variable) + return key in retention.temporal_dependencies || + key in retention.requested_outputs +end + +_scene_retain_output(::Nothing, application_id::Symbol, variable::Symbol) = true + +function _scene_prune_dependency_stream!( + samples, + retention::SceneOutputRetentionPlan, + application_id::Symbol, + variable::Symbol, + time::Real, +) + retention.retain_all && return samples + key = (application_id, variable) + key in retention.requested_outputs && return samples + key in retention.temporal_dependencies || return samples + horizon = get(retention.dependency_horizons, key, 0.0) + if horizon <= 0.0 + length(samples) > 1 && deleteat!(samples, 1:(length(samples) - 1)) + return samples + end + cutoff = float(time) - horizon + 1.0 + filter!(sample -> sample[1] >= cutoff - 1.0e-8, samples) + return samples +end + +_scene_prune_dependency_stream!(samples, ::Nothing, application_id, variable, time) = + samples + +function _scene_publish_outputs!( + streams, + application::CompiledSceneApplication, + object_id::ObjectId, + status, + time::Real, + retention=nothing, +) + isnothing(streams) && return nothing + for variable in keys(outputs_(application.spec)) + var = Symbol(variable) + _scene_retain_output(retention, application.id, var) || continue + hasproperty(status, var) || error( + "Application `$(application.id)` declares output `$(var)`, but object ", + "`$(object_id.value)` status has no such variable." + ) + key = _scene_stream_key(application.id, object_id, var) + value = getproperty(status, var) + samples = get(streams, key, nothing) + if isnothing(samples) + samples = Tuple{Float64,typeof(value)}[] + streams[key] = samples + elseif !(value isa fieldtype(eltype(samples), 2)) + error( + "Output `$(application.id).$(var)` on object `$(object_id.value)` changed ", + "value type from `$(fieldtype(eltype(samples), 2))` to `$(typeof(value))`. ", + "Scene temporal streams require a stable output type." + ) + end + filter!(sample -> !isapprox(sample[1], float(time); atol=1.0e-8, rtol=0.0), samples) + push!(samples, (float(time), value)) + _scene_prune_dependency_stream!( + samples, + retention, + application.id, + var, + time, + ) + end + return nothing +end + +function _scene_latest_sample(samples, time::Real) + latest = nothing + latest_t = -Inf + for (sample_t, value) in samples + sample_t <= float(time) || continue + sample_t >= latest_t || continue + latest = value + latest_t = sample_t + end + return latest +end + +function _scene_linear_value(v_left, v_right, α) + applicable(-, v_right, v_left) || return nothing + delta = v_right - v_left + increment = if applicable(*, α, delta) + α * delta + elseif applicable(*, delta, α) + delta * α + else + return nothing + end + applicable(+, v_left, increment) || return nothing + return v_left + increment +end + +function _scene_interpolated_sample(samples, time::Real, policy::Interpolate) + isempty(samples) && return nothing + t = float(time) + prev_idx = findlast(sample -> sample[1] <= t + 1.0e-8, samples) + next_idx = findfirst(sample -> sample[1] >= t - 1.0e-8, samples) + + if !isnothing(prev_idx) && !isnothing(next_idx) + t_prev, v_prev = samples[prev_idx] + t_next, v_next = samples[next_idx] + isapprox(t_prev, t_next; atol=1.0e-8, rtol=0.0) && return v_prev + policy.mode == :hold && return v_prev + α = (t - t_prev) / (t_next - t_prev) + interpolated = _scene_linear_value(v_prev, v_next, α) + return isnothing(interpolated) ? v_prev : interpolated + end + + if !isnothing(prev_idx) + t_last, v_last = samples[prev_idx] + if policy.extrapolation == :linear && prev_idx >= 2 + t_prev, v_prev = samples[prev_idx - 1] + if !isapprox(t_last, t_prev; atol=1.0e-8, rtol=0.0) + α = (t - t_last) / (t_last - t_prev) + extrapolated = _scene_linear_value(v_prev, v_last, one(α) + α) + !isnothing(extrapolated) && return extrapolated + end + end + return v_last + end + + return first(samples)[2] +end + +function _scene_window_samples(samples, t_start::Real, t_end::Real) + return [value for (sample_t, value) in samples if float(t_start) <= sample_t <= float(t_end)] +end + +function _scene_window_reduce(values, durations, policy) + isempty(values) && return 0.0 + reducer = policy isa Integrate ? policy.reducer : (policy isa Aggregate ? policy.reducer : PlantMeteo.SumReducer()) + f = _normalize_policy_reducer(reducer) + applicable(f, values, durations) && return f(values, durations) + applicable(f, values) && return f(values) + reducer isa PlantMeteo.SumReducer && return sum(values) + reducer isa PlantMeteo.MeanReducer && return Statistics.mean(values) + reducer isa PlantMeteo.MinReducer && return minimum(values) + reducer isa PlantMeteo.MaxReducer && return maximum(values) + reducer isa PlantMeteo.FirstReducer && return first(values) + reducer isa PlantMeteo.LastReducer && return last(values) + error( + "Reducer `$(reducer)` is not callable on scene temporal input values for policy ", + "`$(typeof(policy))`. Expected `(values)` or `(values, durations_seconds)`." + ) +end + +function _scene_duration_steps(duration, timeline) + if duration isa Dates.Period || duration isa Dates.CompoundPeriod || duration isa Real + seconds = _duration_to_seconds(duration) + isnothing(seconds) && return nothing + steps = seconds / timeline.base_step_seconds + steps >= 1.0 || error( + "Input window `$(duration)` is shorter than the scene base step ", + "($(timeline.base_step_seconds) seconds)." + ) + return steps + end + return nothing +end + +function _scene_input_window_steps(binding::CompiledSceneInputBinding, application::CompiledSceneApplication, timeline) + explicit = _scene_duration_steps(binding.window, timeline) + !isnothing(explicit) && return explicit + binding.policy isa Union{Integrate,Aggregate} && return float(application.clock.dt) + return 1.0 +end + +function _scene_temporal_source_value( + streams, + application_id::Symbol, + source_id::ObjectId, + source_var::Symbol, + time::Real, + policy, + t_start::Real, + timeline, +) + samples = get(streams, _scene_stream_key(application_id, source_id, source_var), nothing) + isnothing(samples) && return policy isa Union{Integrate,Aggregate} ? 0.0 : nothing + if policy isa HoldLast + return _scene_latest_sample(samples, time) + elseif policy isa PreviousTimeStep + return _scene_latest_sample(samples, float(time) - 1.0) + elseif policy isa Union{Integrate,Aggregate} + values = _scene_window_samples(samples, t_start, time) + durations = fill(timeline.base_step_seconds, length(values)) + return _scene_window_reduce(values, durations, policy) + elseif policy isa Interpolate + return _scene_interpolated_sample(samples, time, policy) + end + error("Unsupported scene temporal input policy `$(typeof(policy))`.") +end + +function _scene_temporal_source_value( + streams, + application_id::Nothing, + source_id::ObjectId, + source_var::Symbol, + time::Real, + policy, + t_start::Real, + timeline, +) + policy isa PreviousTimeStep && return nothing + error( + "Temporal scene input from `$(source_id.value).$(source_var)` has no ", + "source application for policy `$(typeof(policy))`." + ) +end + +function _scene_temporal_source_application(compiled::CompiledScene, binding::CompiledSceneInputBinding, source_id::ObjectId) + if binding.policy isa PreviousTimeStep && isempty(binding.source_application_ids) + return nothing + end + isempty(binding.source_application_ids) && error( + "Temporal scene input `$(binding.input)` from `$(source_id.value).$(binding.source_var)` ", + "has no resolved source application. Name the producer and add " * + "`application=...` to `Inputs(...)`." + ) + matches = Symbol[ + application_id for application_id in binding.source_application_ids + if source_id in _compiled_application_by_id(compiled, application_id).target_ids + ] + if length(matches) == 1 + return only(matches) + elseif isempty(matches) + binding.policy isa PreviousTimeStep && return nothing + error( + "Temporal scene input `$(binding.input)` from `$(source_id.value).$(binding.source_var)` ", + "has no source application matching object `$(source_id.value)`." + ) + end + if binding.policy isa PreviousTimeStep + positions = Dict(application_id => index for (index, application_id) in pairs(compiled.application_order)) + return last(sort(matches; by=application_id -> get(positions, application_id, 0))) + end + error( + "Temporal scene input `$(binding.input)` from `$(source_id.value).$(binding.source_var)` ", + "has ambiguous source applications `$(matches)`. Add `application=...` to `Inputs(...)`." + ) +end + +function _scene_temporal_input_value( + compiled::CompiledScene, + binding::CompiledSceneInputBinding, + application::CompiledSceneApplication, + status::Status, + streams, + time::Real, + timeline, +) + window_steps = _scene_input_window_steps(binding, application, timeline) + t_start = float(time) - float(window_steps) + 1.0 + initial = _input_value(binding.carrier) + isnothing(initial) && (initial = status[binding.input]) + if binding.multiplicity == :many + values = [ + _scene_temporal_source_value( + streams, + _scene_temporal_source_application(compiled, binding, source_id), + source_id, + binding.source_var, + time, + binding.policy, + t_start, + timeline, + ) + for source_id in binding.source_ids + ] + if binding.policy isa PreviousTimeStep + initial_values = initial isa AbstractVector ? initial : () + return [ + isnothing(value) && index <= length(initial_values) ? initial_values[index] : value + for (index, value) in pairs(values) + ] + end + return values + end + source_id = only(binding.source_ids) + value = _scene_temporal_source_value( + streams, + _scene_temporal_source_application(compiled, binding, source_id), + source_id, + binding.source_var, + time, + binding.policy, + t_start, + timeline, + ) + isnothing(value) && binding.policy isa PreviousTimeStep && return initial + isnothing(value) && error( + "No temporal scene value available for input `$(binding.input)` from ", + "`$(source_id.value).$(binding.source_var)` at t=$(time)." + ) + return value +end + +function _scene_temporal_input_value( + compiled::CompiledScene, + binding::CompiledSceneInputBinding, + application::CompiledSceneApplication, + streams, + time::Real, + timeline, +) + status = _scene_object_status(compiled.scene, binding.consumer_id) + return _scene_temporal_input_value( + compiled, + binding, + application, + status, + streams, + time, + timeline, + ) +end + +function _scene_assign_input_value!(status::Status, variable::Symbol, value) + variable in propertynames(status) || error( + "Cannot materialize input `$(variable)` because consumer status has no such variable." + ) + current = status[variable] + if current isa RefVector && value isa AbstractVector + length(current) != length(value) && resize!(current, length(value)) + for i in eachindex(value) + current[i] = value[i] + end + return status + end + status[variable] = value + return status +end + +function _materialize_scene_inputs!( + compiled::CompiledScene, + application::CompiledSceneApplication, + object_id::ObjectId, + streams=nothing, + time::Real=1, +) + status = _scene_object_status(compiled.scene, object_id) + bindings = get(compiled.input_bindings_by_target, (application.id, object_id), ()) + timeline = nothing + for binding in bindings + if binding.carrier_hint == :temporal_stream + isnothing(streams) && continue + isnothing(timeline) && (timeline = _scene_timeline(compiled.scene)) + value = _scene_temporal_input_value( + compiled, + binding, + application, + status, + streams, + time, + timeline, + ) + _scene_assign_input_value!(status, binding.input, value) + end + end + return status +end + +function _materialize_scene_inputs!( + status::Status, + bindings::Tuple, + compiled::CompiledScene, + application::CompiledSceneApplication, + streams=nothing, + time::Real=1, +) + timeline = nothing + for binding in bindings + if binding.carrier_hint == :temporal_stream + isnothing(streams) && continue + isnothing(timeline) && (timeline = _scene_timeline(compiled.scene)) + value = _scene_temporal_input_value( + compiled, + binding, + application, + status, + streams, + time, + timeline, + ) + _scene_assign_input_value!(status, binding.input, value) + end + end + return status +end + +function _scene_meteo_for_model( + env_bindings::CompiledEnvironmentBindings, + application::CompiledSceneApplication, + object_id::ObjectId, + time::Real, +) + binding = _environment_binding_for(env_bindings, application.id, object_id) + return _scene_meteo_for_binding(env_bindings, application, binding, time) +end + +function _scene_meteo_for_binding( + env_bindings::CompiledEnvironmentBindings, + application::CompiledSceneApplication, + binding, + time::Real, +) + isnothing(binding) && return nothing + isnothing(binding.backend) && return nothing + if binding.backend isa GlobalConstant + sampler = get(env_bindings.samplers_by_application, application.id, nothing) + if !isnothing(sampler) + step = Int(round(time)) + key = (application.id, step) + haskey(env_bindings.sample_cache, key) && + return env_bindings.sample_cache[key] + meteo = environment_meteo(binding.backend) + raw_row = _meteo_row_at_step(meteo, step) + sampled = _sample_meteo_for_model( + sampler, + raw_row, + step, + application.clock, + application.spec, + ) + env_bindings.sample_cache[key] = sampled + return sampled + end + end + return sample_environment(binding.backend, binding.support, time, application.spec) +end + +function _scatter_scene_environment_outputs!( + env_bindings::CompiledEnvironmentBindings, + application::CompiledSceneApplication, + object_id::ObjectId, + status, + time::Real, +) + isempty(keys(meteo_outputs_(application.spec))) && return nothing + binding = _environment_binding_for(env_bindings, application.id, object_id) + return _scatter_scene_environment_outputs!( + application, + binding, + status, + time, + ) +end + +function _scatter_scene_environment_outputs!( + application::CompiledSceneApplication, + binding, + status, + time::Real, +) + isempty(keys(meteo_outputs_(application.spec))) && return nothing + isnothing(binding) && return nothing + isnothing(binding.backend) && return nothing + return scatter_environment_outputs!(binding.backend, binding.support, time, application.spec, status) +end + +function _push_scene_model_entry!(pairs, names::Set{Symbol}, name::Symbol, model) + name in names && return nothing + push!(pairs, name => model) + push!(names, name) + return nothing +end + +function _append_scene_model_dependencies!( + pairs, + names::Set{Symbol}, + applications_by_id, + call_bindings_by_target, + application::CompiledSceneApplication, + object_id::ObjectId, + seen::Set{Tuple{Symbol,ObjectId}}, +) + key = (application.id, object_id) + key in seen && return nothing + push!(seen, key) + _push_scene_model_entry!( + pairs, + names, + application.process, + _application_model(application, object_id), + ) + bindings = get(call_bindings_by_target, key, ()) + for binding in bindings + for application_id in binding.callee_application_ids + callee_application = get(applications_by_id, application_id, nothing) + isnothing(callee_application) && error( + "No compiled scene application with id `$(application_id)`." + ) + matching_object_ids = ObjectId[ + callee_object_id for callee_object_id in binding.callee_object_ids + if callee_object_id in callee_application.target_ids + ] + # Old-style hard-dependency kernels expect one model object per + # process field. Multi-object scene calls are exposed through + # `call_targets(extra, name)` instead. + length(matching_object_ids) == 1 || continue + _append_scene_model_dependencies!( + pairs, + names, + applications_by_id, + call_bindings_by_target, + callee_application, + only(matching_object_ids), + seen, + ) + end + end + return nothing +end + +function _compile_scene_model_bundle( + applications_by_id, + call_bindings_by_target, + application::CompiledSceneApplication, + object_id::ObjectId, +) + pairs = Pair{Symbol,Any}[] + names = Set{Symbol}() + seen = Set{Tuple{Symbol,ObjectId}}() + _append_scene_model_dependencies!( + pairs, + names, + applications_by_id, + call_bindings_by_target, + application, + object_id, + seen, + ) + return NamedTuple{Tuple(first(pair) for pair in pairs)}(Tuple(last(pair) for pair in pairs)) +end + +function _compile_scene_model_bundles(applications, applications_by_id, call_bindings_by_target) + bundles = Dict{Tuple{Symbol,ObjectId},Any}() + for application in applications + for object_id in application.target_ids + bundles[(application.id, object_id)] = _compile_scene_model_bundle( + applications_by_id, + call_bindings_by_target, + application, + object_id, + ) + end + end + return bundles +end + +function _scene_models_for_application( + compiled::CompiledScene, + application::CompiledSceneApplication, + object_id::ObjectId, +) + key = (application.id, object_id) + models = get(compiled.model_bundles_by_target, key, nothing) + isnothing(models) && error( + "No compiled model bundle for application `$(application.id)` on object `$(object_id.value)`." + ) + return models +end + +function _run_scene_application!( + compiled::CompiledScene, + env_bindings::CompiledEnvironmentBindings, + application::CompiledSceneApplication, + object_id::ObjectId; + time::Real=1, + constants=nothing, + temporal_streams=nothing, + output_retention=nothing, + publish::Bool=true, + meteo=nothing, +) + status = _materialize_scene_inputs!(compiled, application, object_id, temporal_streams, time) + meteo_value = isnothing(meteo) ? _scene_meteo_for_model(env_bindings, application, object_id, time) : meteo + context = SceneRunContext( + compiled, + env_bindings, + application, + object_id, + temporal_streams, + output_retention, + float(time), + constants, + publish, + ) + model = _application_model(application, object_id) + models = _scene_models_for_application(compiled, application, object_id) + run!(model, models, status, meteo_value, constants, context) + if publish + _scatter_scene_environment_outputs!(env_bindings, application, object_id, status, time) + _scene_publish_outputs!( + temporal_streams, + application, + object_id, + status, + time, + output_retention, + ) + end + return status +end + +function _run_scene_execution_target!( + compiled::CompiledScene, + env_bindings::CompiledEnvironmentBindings, + application::CompiledSceneApplication, + target::CompiledSceneExecutionTarget; + time::Real=1, + constants=nothing, + temporal_streams=nothing, + output_retention=nothing, +) + status = _materialize_scene_inputs!( + target.status, + target.input_bindings, + compiled, + application, + temporal_streams, + time, + ) + meteo = _scene_meteo_for_binding( + env_bindings, + application, + target.environment_binding, + time, + ) + context = SceneRunContext( + compiled, + env_bindings, + application, + target.object_id, + temporal_streams, + output_retention, + float(time), + constants, + true, + ) + run!(target.model, target.models, status, meteo, constants, context) + _scatter_scene_environment_outputs!( + application, + target.environment_binding, + status, + time, + ) + _scene_publish_outputs!( + temporal_streams, + application, + target.object_id, + status, + time, + output_retention, + ) + return status +end + +function _run_scene_execution_batch!( + batch::CompiledSceneExecutionBatch, + compiled::CompiledScene, + env_bindings::CompiledEnvironmentBindings; + time::Real=1, + constants=nothing, + temporal_streams=nothing, + output_retention=nothing, +) + _scene_application_should_run(batch.application, time) || return nothing + for target in batch.targets + _run_scene_execution_target!( + compiled, + env_bindings, + batch.application, + target; + time=time, + constants=constants, + temporal_streams=temporal_streams, + output_retention=output_retention, + ) + end + return nothing +end + +_scene_application_should_run(application::CompiledSceneApplication, t::Real) = + _should_run_at_time(application.clock, float(t)) + +function _manual_call_application_ids(compiled::CompiledScene) + ids = Set{Symbol}() + for binding in compiled.call_bindings + union!(ids, binding.callee_application_ids) + end + return ids +end + +function _compiled_scene_execution_target( + compiled::CompiledScene, + env_bindings::CompiledEnvironmentBindings, + application::CompiledSceneApplication, + object_id::ObjectId, +) + status = _scene_object_status(compiled.scene, object_id) + model = _application_model(application, object_id) + models = _scene_models_for_application(compiled, application, object_id) + input_bindings = get( + compiled.input_bindings_by_target, + (application.id, object_id), + (), + ) + environment_binding = _environment_binding_for( + env_bindings, + application.id, + object_id, + ) + return CompiledSceneExecutionTarget( + object_id, + model, + status, + models, + input_bindings, + environment_binding, + ) +end + +function _typed_scene_execution_targets(targets, first_index::Int, last_index::Int) + target_type = typeof(targets[first_index]) + typed = Vector{target_type}(undef, last_index - first_index + 1) + for (destination, source) in enumerate(first_index:last_index) + typed[destination] = targets[source] + end + return typed +end + +function _append_scene_execution_batches!( + batches, + application::CompiledSceneApplication, + targets, +) + isempty(targets) && return batches + first_index = firstindex(targets) + target_type = typeof(targets[first_index]) + for index in (first_index + 1):lastindex(targets) + typeof(targets[index]) == target_type && continue + push!( + batches, + CompiledSceneExecutionBatch( + application, + _typed_scene_execution_targets(targets, first_index, index - 1), + ), + ) + first_index = index + target_type = typeof(targets[index]) + end + push!( + batches, + CompiledSceneExecutionBatch( + application, + _typed_scene_execution_targets(targets, first_index, lastindex(targets)), + ), + ) + return batches +end + +function compile_scene_execution_plan( + compiled::CompiledScene, + env_bindings::CompiledEnvironmentBindings, +) + manual_application_ids = _manual_call_application_ids(compiled) + batches = AbstractSceneExecutionBatch[] + for application in _ordered_scene_applications(compiled) + application.id in manual_application_ids && continue + targets = Any[ + _compiled_scene_execution_target( + compiled, + env_bindings, + application, + object_id, + ) + for object_id in application.target_ids + ] + _append_scene_execution_batches!(batches, application, targets) + end + return CompiledSceneExecutionPlan( + batches, + compiled.revision, + env_bindings.environment_revision, + ) +end + +function explain_execution_plan(plan::CompiledSceneExecutionPlan) + return [ + ( + batch_index=index, + application_id=batch.application.id, + process=batch.application.process, + object_ids=[target.object_id.value for target in batch.targets], + batch_size=length(batch.targets), + target_type=eltype(batch.targets), + model_type=fieldtype(eltype(batch.targets), :model), + status_type=fieldtype(eltype(batch.targets), :status), + model_bundle_type=fieldtype(eltype(batch.targets), :models), + input_bindings_type=fieldtype(eltype(batch.targets), :input_bindings), + environment_binding_type=fieldtype( + eltype(batch.targets), + :environment_binding, + ), + inner_loop_dispatch=:concrete_homogeneous_batch, + ) + for (index, batch) in pairs(plan.batches) + ] +end + +function explain_execution_plan(sim::SceneSimulation) + return explain_execution_plan(sim.execution_plan) +end + +function explain_execution_plan(scene::Scene) + compiled = refresh_bindings!(scene) + environment_bindings = refresh_environment_bindings!(scene, compiled) + return explain_execution_plan( + compile_scene_execution_plan(compiled, environment_bindings), + ) +end + +function compile_scene_output_retention( + compiled::CompiledScene, + output_requests; + retain_all::Bool=false, +) + temporal_dependencies = Set{Tuple{Symbol,Symbol}}() + dependency_horizons = Dict{Tuple{Symbol,Symbol},Float64}() + timeline = _scene_timeline(compiled.scene) + for binding in compiled.input_bindings + binding.carrier_hint == :temporal_stream || continue + consumer = _compiled_application_by_id(compiled, binding.application_id) + window_steps = _scene_input_window_steps(binding, consumer, timeline) + for application_id in binding.source_application_ids + source = _compiled_application_by_id(compiled, application_id) + required = if binding.policy isa Union{Integrate,Aggregate} + float(window_steps) + elseif binding.policy isa Union{Interpolate,PreviousTimeStep} + max(2.0, float(source.clock.dt) + 1.0) + else + 0.0 + end + key = (application_id, binding.source_var) + push!( + temporal_dependencies, + key, + ) + dependency_horizons[key] = max( + get(dependency_horizons, key, 0.0), + required, + ) + end + end + + requested_outputs = Set{Tuple{Symbol,Symbol}}() + names = Set{Symbol}() + for request in output_requests + request.name in names && error( + "Duplicate output request name `$(request.name)`. Request names must be unique." + ) + push!(names, request.name) + application = _scene_request_application( + compiled.scene, + compiled, + request, + ) + push!(requested_outputs, (application.id, request.var)) + end + return SceneOutputRetentionPlan( + retain_all, + temporal_dependencies, + requested_outputs, + dependency_horizons, + ) +end + +function _explain_output_retention(compiled::CompiledScene, plan) + keys_to_explain = if plan.retain_all + Set( + (application.id, Symbol(variable)) + for application in compiled.applications + for variable in keys(outputs_(application.spec)) + ) + else + union(plan.temporal_dependencies, plan.requested_outputs) + end + return [ + ( + application_id=application_id, + variable=variable, + reasons=plan.retain_all ? + (:all_outputs,) : + Tuple( + reason for (reason, keys) in ( + :temporal_dependency => plan.temporal_dependencies, + :output_request => plan.requested_outputs, + ) + if (application_id, variable) in keys + ), + retention_steps=plan.retain_all || + (application_id, variable) in plan.requested_outputs ? + nothing : + get( + plan.dependency_horizons, + (application_id, variable), + 0.0, + ), + current_target_count=length( + _compiled_application_by_id( + compiled, + application_id, + ).target_ids, + ), + ) + for (application_id, variable) in sort!( + collect(keys_to_explain); + by=key -> (string(first(key)), string(last(key))), + ) + ] +end + + +function explain_output_retention(sim::SceneSimulation) + return _explain_output_retention(sim.compiled, sim.output_retention) +end + +function explain_output_retention(scene::Scene; outputs=:none) + compiled = refresh_bindings!(scene) + output_requests, retain_all = _scene_output_selection( + outputs, + _UNSPECIFIED_SCENE_OUTPUTS, + ) + plan = compile_scene_output_retention( + compiled, + output_requests; + retain_all=retain_all, + ) + return _explain_output_retention(compiled, plan) +end + +function _scene_call_targets(context::SceneRunContext, name::Symbol) + targets = SceneCallTarget[] + bindings = get( + context.compiled.call_bindings_by_target, + (context.application.id, context.object_id), + (), + ) + for binding in bindings + binding.call == name || continue + for application_id in binding.callee_application_ids + callee_application = _compiled_application_by_id(context.compiled, application_id) + for object_id in binding.callee_object_ids + object_id in callee_application.target_ids || continue + status = _scene_object_status(context.compiled.scene, object_id) + push!( + targets, + SceneCallTarget( + context.compiled, + context.environment_bindings, + callee_application, + object_id, + _application_model(callee_application, object_id), + status, + context.temporal_streams, + context.output_retention, + context.time, + context.constants, + context.publication_allowed, + ), + ) + end + end + end + return targets +end + +""" + call_targets(context::SceneRunContext, name) + call_target(context::SceneRunContext, name) + +Return manually executable targets declared with `Calls(...)` for the current +scene/object model call. `call_target` requires exactly one matching target. +Execute returned targets with [`run_call!`](@ref). +""" +call_targets(context::SceneRunContext, name::Symbol) = _scene_call_targets(context, name) +call_targets(context::SceneRunContext, name::AbstractString) = + call_targets(context, Symbol(name)) +call_target(context::SceneRunContext, name::Symbol) = only(call_targets(context, name)) +call_target(context::SceneRunContext, name::AbstractString) = + call_target(context, Symbol(name)) + +""" + run_call!(target::SceneCallTarget; publish=false, meteo=nothing) + +Run a manually selected model call. By default, the call mutates its target +status without publishing outputs or environment updates, which is suitable +for trial iterations. Pass `publish=true` once for the accepted state. + +Publication permission is inherited through the call stack. A descendant +cannot publish outputs or environment writes while any ancestor is running as +a trial. +""" +function run_call!(target::SceneCallTarget; publish::Bool=false, meteo=nothing) + _run_scene_application!( + target.compiled, + target.environment_bindings, + target.application, + target.object_id; + time=target.time, + constants=target.constants, + temporal_streams=target.temporal_streams, + output_retention=target.output_retention, + publish=publish && target.publication_allowed, + meteo=meteo, + ) + return target +end + +struct _UnspecifiedSceneOutputs end +const _UNSPECIFIED_SCENE_OUTPUTS = _UnspecifiedSceneOutputs() + +function _scene_output_selection(outputs, tracked_outputs) + outputs_specified = !(outputs isa _UnspecifiedSceneOutputs) + tracked_specified = !(tracked_outputs isa _UnspecifiedSceneOutputs) + outputs_specified && tracked_specified && error( + "Use `outputs=...`; do not pass both `outputs` and deprecated `tracked_outputs`.", + ) + + selection = if tracked_specified + Base.depwarn( + "`tracked_outputs` is deprecated; use `outputs=:all`, `outputs=:none`, or `outputs=requests`.", + :run!, + ) + isnothing(tracked_outputs) ? :all : tracked_outputs + elseif outputs_specified + outputs + else + :none + end + + selection === :all && return (OutputRequest[], true) + selection === :none && return (OutputRequest[], false) + selection isa Symbol && error( + "Unsupported output selection `$(selection)`. Use `:all`, `:none`, an `OutputRequest`, or a vector of requests.", + ) + requests = _normalize_output_requests(selection) + return requests, false +end + +function _refresh_simulation_runtime!(simulation::SceneSimulation) + scene = simulation.scene + if bindings_dirty(scene) + simulation.compiled = refresh_bindings!(scene) + simulation.environment_bindings = refresh_environment_bindings!( + scene, + simulation.compiled, + ) + simulation.execution_plan = compile_scene_execution_plan( + simulation.compiled, + simulation.environment_bindings, + ) + elseif environment_bindings_dirty(scene) + simulation.environment_bindings = refresh_environment_bindings!( + scene, + simulation.compiled, + ) + simulation.execution_plan = compile_scene_execution_plan( + simulation.compiled, + simulation.environment_bindings, + ) + end + return simulation +end + +function _continue_scene!(simulation::SceneSimulation, steps::Integer) + steps >= 0 || error("`steps` must be non-negative, got $(steps).") + start_step = simulation.current_step + 1 + final_step = simulation.current_step + steps + for step in start_step:final_step + _refresh_simulation_runtime!(simulation) + _refresh_output_request_targets!(simulation) + for batch in simulation.execution_plan.batches + _run_scene_execution_batch!( + batch, + simulation.compiled, + simulation.environment_bindings; + time=step, + constants=simulation.constants, + temporal_streams=simulation.temporal_streams, + output_retention=simulation.output_retention, + ) + end + simulation.current_step = step + end + _refresh_simulation_runtime!(simulation) + _refresh_output_request_targets!(simulation) + return simulation +end + +function _initial_output_request_targets(scene, compiled, output_requests) + targets = Dict{Symbol,Tuple{Symbol,Dict{ObjectId,Any}}}() + for request in output_requests + application = _scene_request_application(scene, compiled, request) + object_ids = resolve_object_ids( + scene, + request.selector; + context=request.context, + ) + targets[request.name] = ( + application.id, + Dict(id => _scene_object(scene, id).scale for id in object_ids), + ) + end + return targets +end + +function _refresh_output_request_targets!(simulation::SceneSimulation) + for request in simulation.output_requests + _, object_scales = simulation.output_request_targets[request.name] + matched_ids = resolve_object_ids( + simulation.scene, + _selector_as_many(request.selector); + context=request.context, + ) + if request.selector isa Union{One,OptionalOne} && length(matched_ids) > 1 + error( + "Output request `$(request.name)` expected at most one current object for selector ", + "`$(request.selector)`, got $([id.value for id in matched_ids]).", + ) + end + for object_id in matched_ids + object_scales[object_id] = _scene_object( + simulation.scene, + object_id, + ).scale + end + end + return simulation +end + +""" + run!(scene; steps=1, constants=Constants(), outputs=:none) + +Run a fresh simulation timeline while mutating object status in `scene`. +Choose `outputs=:none`, `outputs=:all`, one [`OutputRequest`](@ref), or a +vector of requests. Use [`continue!`](@ref) on the returned +[`SceneSimulation`](@ref) to advance without resetting time. +""" +function run!( + scene::Scene; + steps::Integer=1, + constants=PlantMeteo.Constants(), + outputs=_UNSPECIFIED_SCENE_OUTPUTS, + tracked_outputs=_UNSPECIFIED_SCENE_OUTPUTS, +) + compiled = refresh_bindings!(scene) + env_bindings = refresh_environment_bindings!(scene, compiled) + empty!(env_bindings.sample_cache) + execution_plan = compile_scene_execution_plan(compiled, env_bindings) + output_requests, retain_all = _scene_output_selection(outputs, tracked_outputs) + output_retention = compile_scene_output_retention( + compiled, + output_requests; + retain_all=retain_all, + ) + temporal_streams = Dict{Tuple{Symbol,ObjectId,Symbol},Any}() + output_request_targets = _initial_output_request_targets( + scene, + compiled, + output_requests, + ) + simulation = SceneSimulation( + scene, + compiled, + env_bindings, + execution_plan, + output_retention, + temporal_streams, + output_requests, + output_request_targets, + 0, + constants, + ) + return _continue_scene!(simulation, steps) +end + +""" + continue!(simulation; steps=1) + +Advance an existing [`SceneSimulation`](@ref) without resetting its timeline, +retained streams, temporal dependency history, or environment position. +""" +continue!(simulation::SceneSimulation; steps::Integer=1) = + _continue_scene!(simulation, steps) + +""" + step!(simulation) + +Advance an existing [`SceneSimulation`](@ref) by one timestep. +""" +step!(simulation::SceneSimulation) = continue!(simulation; steps=1) + +function _scene_output_rows(sim::SceneSimulation, filter_object=nothing, filter_var=nothing) + rows = NamedTuple[] + for ((application_id, object_id, variable), samples) in sort!( + collect(sim.temporal_streams); + by=pair -> (string(first(pair)[1]), string(first(pair)[2].value), string(first(pair)[3])), + ) + isnothing(filter_object) || object_id == ObjectId(filter_object) || continue + isnothing(filter_var) || variable == Symbol(filter_var) || continue + for (time, value) in samples + push!( + rows, + ( + timestep=Int(round(time)), + time=time, + application_id=application_id, + object_id=object_id.value, + variable=variable, + value=value, + ), + ) + end + end + sort!(rows; by=row -> (row.timestep, string(row.object_id), string(row.variable))) + return rows +end + +function _materialize_scene_output_rows(rows, sink) + isnothing(sink) && return rows + sink === DataFrames.DataFrame && return DataFrames.DataFrame(rows) + return sink(rows) +end + +function _scene_request_application(scene::Scene, compiled::CompiledScene, request) + requested_ids = Set(resolve_object_ids( + scene, + request.selector; + context=request.context, + )) + declared_scale = _selector_declared_scale(request.selector) + candidates = CompiledSceneApplication[] + for application in compiled.applications + request.var in keys(outputs_(application.spec)) || continue + isnothing(request.process) || application.process == request.process || continue + isnothing(request.application) || + application.id == request.application || + application.name == request.application || + continue + target_match = any(id -> id in requested_ids, application.target_ids) + scale_match = !isnothing(declared_scale) && + _scene_application_matches_scale(scene, application, declared_scale) + (target_match || scale_match) || continue + if isnothing(request.process) && + isnothing(request.application) && + _publish_mode_for_output(application.spec, request.var) == :stream_only + continue + end + push!(candidates, application) + end + if isempty(candidates) + error( + "No scene output publisher found for selector `$(request.selector)` and variable `$(request.var)`", + isnothing(request.application) ? + (isnothing(request.process) ? "." : " from process `$(request.process)`.") : + " from application `$(request.application)`.", + ) + elseif length(candidates) > 1 + error( + "Ambiguous scene output publishers for selector `$(request.selector)` and variable `$(request.var)`: ", + join((application.id for application in candidates), ", "), + ". Provide `application=` or make one publisher canonical.", + ) + end + return only(candidates) +end + +function _scene_request_application(sim::SceneSimulation, request) + return _scene_request_application(sim.scene, sim.compiled, request) +end + +function _selector_declared_scale(selector::AbstractObjectMultiplicity) + selector_criteria = criteria(selector) + scale = _criteria_get(selector_criteria, :scale, nothing) + !isnothing(scale) && return scale + for positional_selector in _criteria_get(selector_criteria, :selectors, ()) + positional_selector isa Scale && return positional_selector.scale + end + return nothing +end + +function _scene_application_matches_scale( + scene::Scene, + application::CompiledSceneApplication, + scale::Symbol, +) + declared_scale = _selector_declared_scale(application.applies_to) + declared_scale == scale && return true + for object_id in application.target_ids + object = get(scene.registry.objects, object_id, nothing) + !isnothing(object) && object.scale == scale && return true + end + return false +end + +function _scene_stream_object_matches_scale( + scene::Scene, + application::CompiledSceneApplication, + object_id::ObjectId, + scale::Symbol, +) + object = get(scene.registry.objects, object_id, nothing) + !isnothing(object) && return object.scale == scale + declared_scale = _selector_declared_scale(application.applies_to) + return isnothing(declared_scale) || declared_scale == scale +end + +function _scene_request_clock(request, timeline) + isnothing(request.clock) && return ClockSpec(1.0, 0.0) + clock = _clock_from_spec_timestep(request.clock, timeline) + isnothing(clock) && error( + "Unsupported clock specification `$(typeof(request.clock))` in ", + "OutputRequest `$(request.name)`.", + ) + return clock +end + +function _scene_requested_value(samples, time, t_start, policy, timeline) + if policy isa HoldLast + value = _scene_latest_sample(samples, time) + return isnothing(value) ? missing : value + elseif policy isa Interpolate + value = _scene_interpolated_sample(samples, time, policy) + return isnothing(value) ? missing : value + elseif policy isa Union{Integrate,Aggregate} + values = _scene_window_samples(samples, t_start, time) + isempty(values) && return missing + durations = fill(timeline.base_step_seconds, length(values)) + return _scene_window_reduce(values, durations, policy) + end + error("Unsupported scene output request policy `$(typeof(policy))`.") +end + +function _scene_requested_output_rows( + sim::SceneSimulation, + request, +) + application_id, requested_objects = sim.output_request_targets[request.name] + requested_ids = keys(requested_objects) + application = _compiled_application_by_id(sim.compiled, application_id) + declared_scale = _selector_declared_scale(request.selector) + timeline = _scene_timeline(sim.scene) + clock = _scene_request_clock(request, timeline) + source_rows = [ + (object_id=object_id, samples=samples) + for ((application_id, object_id, variable), samples) in sim.temporal_streams + if application_id == application.id && + variable == request.var && + (object_id in requested_ids || + (!isnothing(declared_scale) && + _scene_stream_object_matches_scale(sim.scene, application, object_id, declared_scale))) + ] + isempty(source_rows) && return NamedTuple[] + nonempty_source_rows = [row for row in source_rows if !isempty(row.samples)] + isempty(nonempty_source_rows) && return NamedTuple[] + max_time = maximum(last(row.samples)[1] for row in nonempty_source_rows) + rows = NamedTuple[] + for time in 1:Int(floor(max_time)) + _should_run_at_time(clock, float(time)) || continue + t_start = float(time) - float(clock.dt) + 1.0 + for row in sort!(nonempty_source_rows; by=row -> string(row.object_id.value)) + first_sample_time = first(row.samples)[1] + last_sample_time = last(row.samples)[1] + first_sample_time <= float(time) <= last_sample_time || continue + push!( + rows, + ( + timestep=time, + time=float(time), + scale=isnothing(declared_scale) ? + get(requested_objects, row.object_id, missing) : + declared_scale, + process=application.process, + application_id=application.id, + variable=request.var, + object_id=row.object_id.value, + value=_scene_requested_value( + row.samples, + float(time), + t_start, + request.policy, + timeline, + ), + ), + ) + end + end + return rows +end + +function _collect_scene_requested_outputs(sim::SceneSimulation, sink) + outputs = Dict{Symbol,Any}() + for request in sim.output_requests + haskey(outputs, request.name) && error( + "Duplicate output request name `$(request.name)`. Request names must be unique." + ) + outputs[request.name] = _materialize_scene_output_rows( + _scene_requested_output_rows(sim, request), + sink, + ) + end + return outputs +end + +function collect_outputs(sim::SceneSimulation; sink=DataFrames.DataFrame) + isempty(sim.output_requests) || return _collect_scene_requested_outputs(sim, sink) + return _materialize_scene_output_rows(_scene_output_rows(sim), sink) +end + +function collect_outputs(sim::SceneSimulation, name::Symbol; sink=DataFrames.DataFrame) + matches = [request for request in sim.output_requests if request.name == name] + isempty(matches) && error( + "No scene output request named `$(name)`. Available request names are ", + isempty(sim.output_requests) ? "none." : join((request.name for request in sim.output_requests), ", "), + ) + length(matches) == 1 || error( + "Duplicate scene output request name `$(name)`. Request names must be unique." + ) + request = only(matches) + return _materialize_scene_output_rows( + _scene_requested_output_rows(sim, request), + sink, + ) +end + +function collect_outputs(sim::SceneSimulation, object_id, variable::Symbol; sink=DataFrames.DataFrame) + return _materialize_scene_output_rows(_scene_output_rows(sim, object_id, variable), sink) +end + +function explain_outputs(sim::SceneSimulation) + return [ + ( + application_id=application_id, + object_id=object_id.value, + variable=variable, + nsamples=length(samples), + first_time=isempty(samples) ? nothing : first(samples)[1], + last_time=isempty(samples) ? nothing : last(samples)[1], + value_type=isempty(samples) ? Union{} : typeof(last(samples)[2]), + ) + for ((application_id, object_id, variable), samples) in sort!( + collect(sim.temporal_streams); + by=pair -> (string(first(pair)[1]), string(first(pair)[2].value), string(first(pair)[3])), + ) + ] +end diff --git a/src/scene_object/scenario_dsl.jl b/src/scene_object/scenario_dsl.jl new file mode 100644 index 000000000..90a810957 --- /dev/null +++ b/src/scene_object/scenario_dsl.jl @@ -0,0 +1,130 @@ +struct ObjectAddress{SC,K,SP,S,N,P,V,R,M} + scope::SC + kind::K + species::SP + scale::S + name::N + process::P + var::V + relation::R + multiplicity::M +end + +function ObjectAddress(selector::AbstractObjectMultiplicity) + c = criteria(selector) + scope = _criteria_scope(c) + kind = _criteria_value(c, :kind, Kind) + species = _criteria_value(c, :species, Species) + scale = _criteria_value(c, :scale, Scale) + name = haskey(c, :name) ? c.name : nothing + process = haskey(c, :process) ? c.process : nothing + var = haskey(c, :var) ? c.var : nothing + relation = _criteria_value(c, :relation, Relation) + return ObjectAddress(scope, kind, species, scale, name, process, var, relation, multiplicity(selector)) +end + +object_address(selector::AbstractObjectMultiplicity) = ObjectAddress(selector) + +struct Input{S} + selector::S +end +Input(; kwargs...) = Input(One(; kwargs...)) + +struct Call{S} + selector::S +end +Call(; kwargs...) = Call(One(; kwargs...)) + +struct EnvironmentConfig{C} + config::C +end + +_normalize_application_name(name) = isnothing(name) ? nothing : Symbol(name) + +function _normalize_application_bindings(bindings::NamedTuple) + return bindings +end + +function _normalize_application_bindings(bindings::Tuple) + pairs = Pair{Symbol,Any}[] + for binding in bindings + binding isa Pair || error( + "Expected `var => selector` pairs in `Inputs(...)` or `Calls(...)`, got `$(typeof(binding))`." + ) + key = first(binding) + selector = last(binding) + if key isa PreviousTimeStep + selector isa AbstractObjectMultiplicity || error( + "A `PreviousTimeStep(...)` input must map to `One(...)`, ", + "`OptionalOne(...)`, or `Many(...)`." + ) + push!( + pairs, + key.variable => _selector_with_previous_timestep(selector, key), + ) + else + key isa Union{Symbol,AbstractString} || error( + "Binding names in `Inputs(...)` and `Calls(...)` must be symbols, ", + "strings, or `PreviousTimeStep(:input)` markers." + ) + push!(pairs, Symbol(key) => selector) + end + end + return (; pairs...) +end + +function _normalize_application_bindings(binding::Pair) + return _normalize_application_bindings((binding,)) +end + +function _normalize_application_bindings(bindings) + error( + "Unsupported binding declaration `$(bindings)` of type `$(typeof(bindings))`. ", + "Use pairs such as `:x => Many(...)` or keyword arguments." + ) +end + +function _model_default_value_inputs(model) + defaults = Pair{Symbol,Any}[] + for (dep_name, selector) in pairs(dep(model)) + selector isa Input || continue + push!(defaults, Symbol(dep_name) => selector.selector) + end + return (; defaults...) +end + +function _model_default_model_calls(model) + defaults = Pair{Symbol,Any}[] + for (dep_name, selector) in pairs(dep(model)) + selector isa Call || continue + push!(defaults, Symbol(dep_name) => selector.selector) + end + return (; defaults...) +end + +function _merge_value_inputs(defaults::NamedTuple, explicit::NamedTuple) + return (; pairs(defaults)..., pairs(explicit)...) +end + +function _binding_origins(defaults::NamedTuple, explicit::NamedTuple) + origins = Pair{Symbol,Symbol}[] + for name in keys(defaults) + push!(origins, Symbol(name) => :model_default) + end + for name in keys(explicit) + push!(origins, Symbol(name) => :model_spec) + end + return (; origins...) +end + +function _normalize_binding_origins(origins::NamedTuple, bindings::NamedTuple) + normalized = Pair{Symbol,Symbol}[] + for name in keys(bindings) + origin = haskey(origins, name) ? Symbol(getproperty(origins, name)) : :model_spec + origin in (:model_default, :model_spec) || error( + "Unsupported binding origin `$(origin)` for `$(name)`." + ) + push!(normalized, Symbol(name) => origin) + end + return (; normalized...) +end diff --git a/src/scene_object/selectors.jl b/src/scene_object/selectors.jl new file mode 100644 index 000000000..cd4c6ce22 --- /dev/null +++ b/src/scene_object/selectors.jl @@ -0,0 +1,672 @@ +struct SceneScope <: AbstractObjectSelector end +struct Self <: AbstractObjectSelector end +struct Subtree <: AbstractObjectSelector end +struct SelfPlant <: AbstractObjectSelector end + +struct Ancestor <: AbstractObjectSelector + scale::Union{Nothing,Symbol} +end +Ancestor(; scale=nothing) = Ancestor(_maybe_symbol(scale)) + +struct Scope <: AbstractObjectSelector + name::Symbol +end +Scope(name::Union{Symbol,AbstractString}) = Scope(Symbol(name)) + +struct Kind <: AbstractObjectSelector + kind::Symbol +end +Kind(kind::Union{Symbol,AbstractString}) = Kind(Symbol(kind)) + +struct Species <: AbstractObjectSelector + species::Symbol +end +Species(species::Union{Symbol,AbstractString}) = Species(Symbol(species)) + +struct Scale <: AbstractObjectSelector + scale::Symbol +end +Scale(scale::Union{Symbol,AbstractString}) = Scale(Symbol(scale)) + +struct Relation <: AbstractObjectSelector + relation::Symbol + function Relation(relation::Symbol) + relation in _OBJECT_RELATIONS || error( + "Unsupported object relation `$(relation)`. Supported relations are $(_OBJECT_RELATIONS)." + ) + return new(relation) + end +end +const _OBJECT_RELATIONS = (:self, :parent, :children, :ancestors, :descendants, :siblings) +Relation(relation::AbstractString) = Relation(Symbol(relation)) + +_maybe_symbol(x) = isnothing(x) ? nothing : Symbol(x) + +const _OBJECT_ADDRESS_SYMBOL_FIELDS = (:kind, :species, :scale, :name, :process, :var, :relation, :application) + +function _maybe_symbol_collection(value) + value isa Tuple && return Tuple(_maybe_symbol(item) for item in value) + value isa AbstractVector && return Tuple(_maybe_symbol(item) for item in value) + return _maybe_symbol(value) +end + +function _normalize_object_selector_value(key::Symbol, value) + key in _OBJECT_ADDRESS_SYMBOL_FIELDS && return _maybe_symbol_collection(value) + return value +end + +function _object_matches_selector_value(object_value, requested) + isnothing(requested) && return true + requested isa Tuple && return object_value in requested + return object_value == requested +end + +function _normalize_selector_kwargs(kwargs) + return NamedTuple{keys(kwargs)}( + Tuple(_normalize_object_selector_value(k, v) for (k, v) in pairs(kwargs)) + ) +end + +function _normalize_selector_criteria(args::Tuple; kwargs...) + selectors = Tuple(args) + all(selector -> selector isa AbstractObjectSelector, selectors) || error( + "Object selector positional arguments must be selector objects such as `Kind(:plant)` or `Scale(:Leaf)`." + ) + normalized_kwargs = _normalize_selector_kwargs((; kwargs...)) + return (; selectors=selectors, normalized_kwargs...) +end + +struct One{C<:NamedTuple} <: AbstractObjectMultiplicity + criteria::C +end +struct OptionalOne{C<:NamedTuple} <: AbstractObjectMultiplicity + criteria::C +end +struct Many{C<:NamedTuple} <: AbstractObjectMultiplicity + criteria::C +end + +One(args...; kwargs...) = One(_normalize_selector_criteria(args; kwargs...)) +OptionalOne(args...; kwargs...) = OptionalOne(_normalize_selector_criteria(args; kwargs...)) +Many(args...; kwargs...) = Many(_normalize_selector_criteria(args; kwargs...)) + +criteria(selector::AbstractObjectMultiplicity) = selector.criteria +multiplicity(::One) = :one +multiplicity(::OptionalOne) = :optional_one +multiplicity(::Many) = :many + +_rebuild_selector(::One, criteria) = One{typeof(criteria)}(criteria) +_rebuild_selector(::OptionalOne, criteria) = OptionalOne{typeof(criteria)}(criteria) +_rebuild_selector(::Many, criteria) = Many{typeof(criteria)}(criteria) +_selector_as_many(selector::AbstractObjectMultiplicity) = + Many{typeof(criteria(selector))}(criteria(selector)) + +function _selector_with_scope(selector::AbstractObjectMultiplicity, scope) + selector_criteria = criteria(selector) + haskey(selector_criteria, :within) && return selector + return _rebuild_selector(selector, merge(selector_criteria, (; within=scope))) +end + +function _selector_with_application_prefix( + selector::AbstractObjectMultiplicity, + instance_name::Symbol, + template_application_names::Set{Symbol}, +) + selector_criteria = criteria(selector) + haskey(selector_criteria, :application) || return selector + application = selector_criteria.application + isnothing(application) && return selector + application in template_application_names || return selector + mounted_name = Symbol(instance_name, "__", application) + return _rebuild_selector(selector, merge(selector_criteria, (; application=mounted_name))) +end + +function _selector_with_previous_timestep( + selector::AbstractObjectMultiplicity, + previous::PreviousTimeStep, +) + return _rebuild_selector( + selector, + merge(criteria(selector), (; policy=previous)), + ) +end + +function _mounted_application_name(spec, index::Int) + name = application_name(spec) + return isnothing(name) ? process(spec) : name +end + +function _instance_override_matches(spec, key::Symbol) + name = application_name(spec) + return key == process(spec) || (!isnothing(name) && key == name) +end + +function _instance_override_models(instance::ObjectInstance, specs) + selected = Dict{Int,AbstractModel}() + for (key, replacement) in pairs(instance.overrides) + replacement isa AbstractModel || error( + "Override `$(key)` for instance `$(instance.name)` must be an `AbstractModel`, got `$(typeof(replacement))`." + ) + matches = findall(spec -> _instance_override_matches(spec, Symbol(key)), specs) + isempty(matches) && error( + "Override `$(key)` for instance `$(instance.name)` does not match a template application name or process." + ) + length(matches) == 1 || error( + "Override `$(key)` for instance `$(instance.name)` matches several template applications; use a unique application name." + ) + index = only(matches) + haskey(selected, index) && error( + "Several overrides target template application `$(_mounted_application_name(specs[index], index))` in instance `$(instance.name)`." + ) + _validate_model_override_contract!( + model_(specs[index]), + replacement; + description="Override `$(key)` for instance `$(instance.name)`", + ) + selected[index] = replacement + end + return selected +end + +function _model_contract(model) + return ( + process=process(model), + inputs=Tuple(Symbol.(keys(inputs_(model)))), + outputs=Tuple(Symbol.(keys(outputs_(model)))), + meteo_inputs=Tuple(Symbol.(keys(meteo_inputs_(model)))), + meteo_outputs=Tuple(Symbol.(keys(meteo_outputs_(model)))), + ) +end + +function _validate_model_override_contract!(base, replacement; description) + base_contract = _model_contract(base) + replacement_contract = _model_contract(replacement) + base_contract == replacement_contract && return nothing + error( + "$(description) has an incompatible model contract. Expected ", + "`$(base_contract)`, got `$(replacement_contract)`. Object and instance ", + "overrides may change parameters or implementation, but not process or declared variables." + ) +end + +function _object_override_matches(spec, override::Override) + process_match = isnothing(override.process) || process(spec) == override.process + name = application_name(spec) + application_match = isnothing(override.application) || + (!isnothing(name) && name == override.application) + return process_match && application_match +end + +function _object_override_models(instance::ObjectInstance, specs, instance_ids) + entries = Dict{Int,Vector{Pair{ObjectId,AbstractModel}}}() + valid_ids = Set(instance_ids) + for override in instance.object_overrides + override.object in valid_ids || error( + "Object override for `$(override.object.value)` does not belong to instance `$(instance.name)`." + ) + matches = findall(spec -> _object_override_matches(spec, override), specs) + isempty(matches) && error( + "Object override for `$(override.object.value)` in instance `$(instance.name)` ", + "does not match a template application." + ) + length(matches) == 1 || error( + "Object override for `$(override.object.value)` in instance `$(instance.name)` ", + "matches several template applications; add `application=...`." + ) + index = only(matches) + object_models = get!(entries, index, Pair{ObjectId,AbstractModel}[]) + any(entry -> first(entry) == override.object, object_models) && error( + "Several object overrides target application `$(_mounted_application_name(specs[index], index))` ", + "on object `$(override.object.value)` in instance `$(instance.name)`." + ) + _validate_model_override_contract!( + model_(specs[index]), + override.model; + description="Object override for `$(override.object.value)` in instance `$(instance.name)`", + ) + push!(object_models, override.object => override.model) + end + selected = Dict{Int,Any}() + for (index, object_models) in entries + selected[index] = _typed_object_model_dict(object_models) + end + return selected +end + +function _typed_object_model_dict(entries) + isempty(entries) && return Dict{ObjectId,AbstractModel}() + model_type = typeof(last(first(entries))) + if all(entry -> typeof(last(entry)) == model_type, entries) + models = Dict{ObjectId,model_type}() + for (object_id, model) in entries + models[object_id] = model + end + return models + end + return Dict{ObjectId,AbstractModel}(entries) +end + +function _map_selector_bindings(bindings::NamedTuple, f) + mapped = Pair{Symbol,Any}[] + for (name, selector) in pairs(bindings) + push!(mapped, Symbol(name) => (selector isa AbstractObjectMultiplicity ? f(selector) : selector)) + end + return (; mapped...) +end + +function _mount_object_instance_applications(instance::ObjectInstance, instance_ids) + specs = Tuple(as_model_spec(application) for application in instance.template.applications) + base_names = Set(_mounted_application_name(spec, index) for (index, spec) in pairs(specs)) + instance_overrides = _instance_override_models(instance, specs) + object_overrides = _object_override_models(instance, specs, instance_ids) + mounted = Any[] + for (index, spec) in pairs(specs) + base_name = _mounted_application_name(spec, index) + mounted_name = Symbol(instance.name, "__", base_name) + target = applies_to(spec) + isnothing(target) && error( + "Template application `$(base_name)` has no `AppliesTo(...)` selector." + ) + mounted_target = _selector_with_scope(target, Scope(instance.name)) + prefix_application = selector -> _selector_with_application_prefix( + selector, + instance.name, + base_names, + ) + mounted_inputs = _map_selector_bindings(value_inputs(spec), prefix_application) + mounted_calls = _map_selector_bindings(model_calls(spec), prefix_application) + mounted_model = get(instance_overrides, index, model_(spec)) + if haskey(object_overrides, index) + object_models = object_overrides[index] + for replacement in values(object_models) + _validate_model_override_contract!( + mounted_model, + replacement; + description="Object override for template application `$(base_name)`", + ) + end + mounted_model = ObjectModelOverrides(mounted_model, object_models) + end + push!( + mounted, + ModelSpec( + spec; + model=mounted_model, + name=mounted_name, + applies_to=mounted_target, + inputs=mounted_inputs, + calls=mounted_calls, + ), + ) + end + return Tuple(mounted) +end + +function _mount_object_instance_applications(instances, instance_ids) + mounted = Any[] + for instance in instances + append!( + mounted, + _mount_object_instance_applications(instance, instance_ids[instance.name]), + ) + end + return Tuple(mounted) +end + +_sort_object_ids!(ids) = sort!(ids; by=id -> string(id.value)) + +function _object_id_from_context(context) + isnothing(context) && return nothing + context isa Object && return context.id + return ObjectId(context) +end + +function _descendant_ids(scene::Scene, root_id::ObjectId) + ids = ObjectId[root_id] + object = _scene_object(scene, root_id) + for child_id in object.children + append!(ids, _descendant_ids(scene, child_id)) + end + return ids +end + +function _ancestor_id( + scene::Scene, + current_id::ObjectId; + scale=nothing, + kind=nothing, + include_self::Bool=true, +) + id = if include_self + current_id + else + parent = _scene_object(scene, current_id).parent + isnothing(parent) && return nothing + parent + end + while true + object = _scene_object(scene, id) + scale_match = isnothing(scale) || object.scale == Symbol(scale) + kind_match = isnothing(kind) || object.kind == Symbol(kind) + scale_match && kind_match && return id + isnothing(object.parent) && return nothing + id = object.parent + end +end + +function _ancestor_ids(scene::Scene, current_id::ObjectId) + ids = ObjectId[] + parent_id = _scene_object(scene, current_id).parent + while !isnothing(parent_id) + push!(ids, parent_id) + parent_id = _scene_object(scene, parent_id).parent + end + return ids +end + +function _relation_object_ids(scene::Scene, relation::Symbol, context) + current_id = _object_id_from_context(context) + isnothing(current_id) && error( + "`Relation(:$(relation))` selectors require a current object context." + ) + object = _scene_object(scene, current_id) + relation == :self && return ObjectId[current_id] + relation == :parent && return isnothing(object.parent) ? ObjectId[] : ObjectId[object.parent] + relation == :children && return copy(object.children) + relation == :ancestors && return _ancestor_ids(scene, current_id) + relation == :descendants && return _descendant_ids(scene, current_id)[2:end] + if relation == :siblings + isnothing(object.parent) && return ObjectId[] + return ObjectId[ + sibling_id for sibling_id in _scene_object(scene, object.parent).children + if sibling_id != current_id + ] + end + error("Unsupported object relation `$(relation)`.") +end + +function _selector_scope_from_positional(selectors) + scopes = filter( + selector -> selector isa Union{SceneScope,Self,Subtree,SelfPlant,Ancestor,Scope}, + selectors, + ) + isempty(scopes) && return nothing + length(scopes) == 1 || error("Only one scope selector can be used in one object selector.") + return only(scopes) +end + +function _selector_value_from_positional(selectors, ::Type{Kind}) + values = [selector.kind for selector in selectors if selector isa Kind] + isempty(values) && return nothing + length(unique(values)) == 1 || error("Conflicting `Kind(...)` selector values: $(values).") + return only(unique(values)) +end + +function _selector_value_from_positional(selectors, ::Type{Species}) + values = [selector.species for selector in selectors if selector isa Species] + isempty(values) && return nothing + length(unique(values)) == 1 || error("Conflicting `Species(...)` selector values: $(values).") + return only(unique(values)) +end + +function _selector_value_from_positional(selectors, ::Type{Scale}) + values = [selector.scale for selector in selectors if selector isa Scale] + isempty(values) && return nothing + length(unique(values)) == 1 || error("Conflicting `Scale(...)` selector values: $(values).") + return only(unique(values)) +end + +function _selector_value_from_positional(selectors, ::Type{Relation}) + values = [selector.relation for selector in selectors if selector isa Relation] + isempty(values) && return nothing + length(unique(values)) == 1 || error("Conflicting `Relation(...)` selector values: $(values).") + return only(unique(values)) +end + +function _criteria_value(criteria, key::Symbol, selector_type) + positional = _selector_value_from_positional(criteria.selectors, selector_type) + keyword = haskey(criteria, key) ? getproperty(criteria, key) : nothing + if !isnothing(positional) && !isnothing(keyword) && positional != keyword + error("Conflicting selector values for `$(key)`: `$(positional)` and `$(keyword)`.") + end + return isnothing(keyword) ? positional : keyword +end + +function _criteria_scope(criteria) + positional = _selector_scope_from_positional(criteria.selectors) + keyword = haskey(criteria, :within) ? criteria.within : nothing + if !isnothing(positional) && !isnothing(keyword) && typeof(positional) != typeof(keyword) + error("Conflicting scope selectors: `$(positional)` and `$(keyword)`.") + end + return isnothing(keyword) ? positional : keyword +end + +function _scope_object_ids(scene::Scene, scope, context) + if isnothing(scope) || scope isa SceneScope + return ObjectId[keys(scene.registry.objects)...] + end + + current_id = _object_id_from_context(context) + if scope isa Self + isnothing(current_id) && error("`Self()` selectors require a current object context.") + return ObjectId[current_id] + elseif scope isa Subtree + isnothing(current_id) && error("`Subtree()` selectors require a current object context.") + return _descendant_ids(scene, current_id) + elseif scope isa SelfPlant + isnothing(current_id) && error("`SelfPlant()` selectors require a current object context.") + plant_id = _ancestor_id(scene, current_id; scale=:Plant) + isnothing(plant_id) && error("No `scale=:Plant` ancestor found for object `$(current_id.value)`.") + return _descendant_ids(scene, plant_id) + elseif scope isa Ancestor + isnothing(current_id) && error("`Ancestor(...)` selectors require a current object context.") + ancestor_id = _ancestor_id( + scene, + current_id; + scale=scope.scale, + include_self=false, + ) + if isnothing(ancestor_id) + error("No matching ancestor found for object `$(current_id.value)` and selector `$(scope)`.") + end + return _descendant_ids(scene, ancestor_id) + elseif scope isa Scope + root_id = get(scene.registry.by_name, scope.name, nothing) + if isnothing(root_id) + candidate = ObjectId(scope.name) + root_id = haskey(scene.registry.objects, candidate) ? candidate : nothing + end + if isnothing(root_id) + available = sort!(unique(Symbol[ + keys(scene.registry.by_name)..., + (id.value for id in keys(scene.registry.objects))..., + ]); by=string) + suggestions = _near_symbol_matches(scope.name, available) + error( + "No named scope or object `$(scope.name)` found in the scene registry. ", + "available=$(available), suggestions=$(suggestions)." + ) + end + return _descendant_ids(scene, root_id) + end + + error("Unsupported object scope selector `$(scope)` of type `$(typeof(scope))`.") +end + +function _symbol_edit_distance(left::Symbol, right::Symbol) + a = collect(string(left)) + b = collect(string(right)) + previous = collect(0:length(b)) + current = similar(previous) + for i in eachindex(a) + current[1] = i + for j in eachindex(b) + substitution = previous[j] + (a[i] == b[j] ? 0 : 1) + current[j + 1] = min(current[j] + 1, previous[j + 1] + 1, substitution) + end + previous, current = current, previous + end + return previous[end] +end + +function _near_symbol_matches(requested, available) + isnothing(requested) && return Symbol[] + requested_symbol = Symbol(requested) + threshold = max(2, cld(length(string(requested_symbol)), 3)) + ranked = Tuple{Int,Symbol}[ + (_symbol_edit_distance(requested_symbol, candidate), candidate) + for candidate in available + ] + filter!(pair -> first(pair) <= threshold, ranked) + sort!(ranked; by=pair -> (first(pair), string(last(pair)))) + return Symbol[last(pair) for pair in Iterators.take(ranked, 3)] +end + +function _available_selector_labels(scene::Scene, candidate_ids) + objects = (_scene_object(scene, id) for id in candidate_ids) + scales = Symbol[] + kinds = Symbol[] + species = Symbol[] + names = Symbol[] + for object in objects + isnothing(object.scale) || push!(scales, object.scale) + isnothing(object.kind) || push!(kinds, object.kind) + isnothing(object.species) || push!(species, object.species) + isnothing(object.name) || push!(names, object.name) + end + return ( + scales=sort!(unique!(scales); by=string), + kinds=sort!(unique!(kinds); by=string), + species=sort!(unique!(species); by=string), + names=sort!(unique!(names); by=string), + ) +end + +function _selector_resolution_error( + scene::Scene, + selector, + candidate_ids, + matched_ids; + context=nothing, + scale=nothing, + kind=nothing, + species=nothing, + name=nothing, +) + available = _available_selector_labels(scene, candidate_ids) + suggestions = ( + scale=_near_symbol_matches(scale, available.scales), + kind=_near_symbol_matches(kind, available.kinds), + species=_near_symbol_matches(species, available.species), + name=_near_symbol_matches(name, available.names), + ) + expected = selector isa One ? "exactly one" : "zero or one" + context_id = _object_id_from_context(context) + error( + "Expected $(expected) object for selector `$(selector)`, got $(length(matched_ids)). ", + "context=$(isnothing(context_id) ? nothing : context_id.value), ", + "matched_ids=$([id.value for id in matched_ids]), ", + "requested=(scale=$(scale), kind=$(kind), species=$(species), name=$(name)), ", + "available=$(available), suggestions=$(suggestions)." + ) +end + +function _matches_object_criteria(object::Object; scale=nothing, kind=nothing, species=nothing, name=nothing) + _object_matches_selector_value(object.scale, scale) || return false + _object_matches_selector_value(object.kind, kind) || return false + _object_matches_selector_value(object.species, species) || return false + _object_matches_selector_value(object.name, name) || return false + return true +end + +function resolve_object_ids(scene::Scene, selector::AbstractObjectMultiplicity; context=nothing) + return _resolve_object_ids(scene, selector; context=context) +end + +function _resolve_object_ids( + scene::Scene, + selector::AbstractObjectMultiplicity; + context=nothing, + default_to_context::Bool=false, + default_scope=nothing, +) + criteria_ = criteria(selector) + relation = _criteria_value(criteria_, :relation, Relation) + + explicit_scope = _criteria_scope(criteria_) + scope = if !isnothing(explicit_scope) + explicit_scope + elseif isnothing(relation) + default_scope + else + nothing + end + scale = _criteria_value(criteria_, :scale, Scale) + kind = _criteria_value(criteria_, :kind, Kind) + species = _criteria_value(criteria_, :species, Species) + name = haskey(criteria_, :name) ? criteria_.name : nothing + + if default_to_context && + isnothing(explicit_scope) && + isnothing(relation) && + isnothing(scale) && + isnothing(kind) && + isnothing(species) && + isnothing(name) && + !isnothing(context) + return ObjectId[_object_id_from_context(context)] + end + + candidate_ids = if isnothing(relation) + _scope_object_ids(scene, scope, context) + else + related_ids = _relation_object_ids(scene, relation, context) + if isnothing(explicit_scope) + related_ids + else + scoped_ids = Set(_scope_object_ids(scene, explicit_scope, context)) + ObjectId[id for id in related_ids if id in scoped_ids] + end + end + ids = ObjectId[ + id for id in candidate_ids + if _matches_object_criteria(_scene_object(scene, id); scale=scale, kind=kind, species=species, name=name) + ] + _sort_object_ids!(ids) + + if selector isa One && length(ids) != 1 + _selector_resolution_error( + scene, + selector, + candidate_ids, + ids; + context=context, + scale=scale, + kind=kind, + species=species, + name=name, + ) + elseif selector isa OptionalOne && length(ids) > 1 + _selector_resolution_error( + scene, + selector, + candidate_ids, + ids; + context=context, + scale=scale, + kind=kind, + species=species, + name=name, + ) + end + return ids +end + +resolve_objects(scene::Scene, selector::AbstractObjectMultiplicity; context=nothing) = + [_scene_object(scene, id) for id in resolve_object_ids(scene, selector; context=context)] + +function _default_dependency_scope(scene::Scene, context::ObjectId) + object = _scene_object(scene, context) + (object.scale == :Scene || object.kind == :scene) && return SceneScope() + return Self() +end diff --git a/src/scene_object_api.jl b/src/scene_object_api.jl index 30dadb163..71c3baf88 100644 --- a/src/scene_object_api.jl +++ b/src/scene_object_api.jl @@ -1,4791 +1,9 @@ -abstract type AbstractObjectSelector end -abstract type AbstractObjectMultiplicity end - -struct ObjectRefVector{R} <: AbstractVector{Any} - refs::R -end - -Base.size(v::ObjectRefVector) = size(v.refs) -Base.length(v::ObjectRefVector) = length(v.refs) -Base.getindex(v::ObjectRefVector, i::Int) = v.refs[i][] -Base.setindex!(v::ObjectRefVector, value, i::Int) = (v.refs[i][] = value) -Base.parent(v::ObjectRefVector) = v.refs - -struct ObjectId{T} - value::T -end -ObjectId(id::ObjectId) = id -ObjectId(id::AbstractString) = ObjectId(Symbol(id)) - -mutable struct Object - id::ObjectId - scale::Union{Nothing,Symbol} - kind::Union{Nothing,Symbol} - species::Union{Nothing,Symbol} - name::Union{Nothing,Symbol} - parent::Union{Nothing,ObjectId} - children::Vector{ObjectId} - geometry::Any - status::Any - applications::Any -end - -""" - ObjectTemplate(applications=(); kind=nothing, species=nothing, parameters=NamedTuple()) - -Reusable model-application bundle for one kind of scene object, such as a plant -species. Each mounted `ObjectInstance` scopes unqualified `AppliesTo(...)` -selectors to its own object subtree. Model objects are shared between instances -unless an instance supplies an override. - -`parameters` stores template-level metadata. Parameter-field merging is not -implicit: use an instance model override when model parameters differ. -""" -struct ObjectTemplate{A,P} - kind::Union{Nothing,Symbol} - species::Union{Nothing,Symbol} - applications::A - parameters::P -end - -_as_tuple(value::Tuple) = value -_as_tuple(value::AbstractVector) = Tuple(value) -_as_tuple(value) = (value,) - -function ObjectTemplate( - applications=(); - kind=nothing, - species=nothing, - parameters=NamedTuple(), -) - normalized_applications = _as_tuple(applications) - return ObjectTemplate( - _maybe_symbol(kind), - _maybe_symbol(species), - normalized_applications, - parameters, - ) -end - -""" - Override(; object, model, process=nothing, application=nothing) - -Replace one template model application on one exceptional object. Select the -template application by its process, explicit application name, or both. -The replacement must implement the same process and variable contract. -""" -struct Override{M<:AbstractModel} - object::ObjectId - process::Union{Nothing,Symbol} - application::Union{Nothing,Symbol} - model::M -end - -function Override(; object, model::AbstractModel, process=nothing, application=nothing) - normalized_process = _maybe_symbol(process) - normalized_application = _maybe_symbol(application) - if isnothing(normalized_process) && isnothing(normalized_application) - error("`Override(...)` requires `process=...`, `application=...`, or both.") - end - return Override( - ObjectId(object), - normalized_process, - normalized_application, - model, - ) -end - -""" - ObjectInstance(name, template; root, objects=(), overrides=NamedTuple(), object_overrides=()) - -Mount an `ObjectTemplate` on one concrete scene-object subtree. - -`root` may be an `Object` owned by the instance or the id of an object supplied -separately to `Scene`. `objects` contains additional owned descendants. -`overrides` maps one template application name or process to a replacement -model implementing the same process. `object_overrides` contains `Override` -entries for exceptional organs. -""" -struct ObjectInstance{T,R,O,OV,OOV} - name::Symbol - template::T - root::R - objects::O - overrides::OV - object_overrides::OOV -end - -function ObjectInstance( - name, - template::ObjectTemplate; - root, - objects=(), - overrides=NamedTuple(), - object_overrides=(), -) - normalized_objects = _as_tuple(objects) - normalized_object_overrides = _as_tuple(object_overrides) - all(object -> object isa Object, normalized_objects) || error( - "`ObjectInstance(...; objects=...)` must contain `Object` values." - ) - overrides isa NamedTuple || error( - "`ObjectInstance(...; overrides=...)` must be a NamedTuple keyed by application name or process." - ) - all(override -> override isa Override, normalized_object_overrides) || error( - "`ObjectInstance(...; object_overrides=...)` must contain `Override` values." - ) - return ObjectInstance( - Symbol(name), - template, - root, - normalized_objects, - overrides, - normalized_object_overrides, - ) -end - -struct ObjectModelOverrides{M,O} <: AbstractModel - base::M - overrides::O -end - -process(model::ObjectModelOverrides) = process(model.base) -inputs_(model::ObjectModelOverrides) = inputs_(model.base) -outputs_(model::ObjectModelOverrides) = outputs_(model.base) -dep(model::ObjectModelOverrides) = dep(model.base) -timespec(model::ObjectModelOverrides) = timespec(model.base) -output_policy(model::ObjectModelOverrides) = output_policy(model.base) -meteo_inputs_(model::ObjectModelOverrides) = meteo_inputs_(model.base) -meteo_outputs_(model::ObjectModelOverrides) = meteo_outputs_(model.base) - -function Object( - id; - scale=nothing, - kind=nothing, - species=nothing, - name=nothing, - parent=nothing, - children=ObjectId[], - geometry=nothing, - status=nothing, - applications=(), -) - return Object( - ObjectId(id), - _maybe_symbol(scale), - _maybe_symbol(kind), - _maybe_symbol(species), - _maybe_symbol(name), - isnothing(parent) ? nothing : ObjectId(parent), - ObjectId[ObjectId(child) for child in children], - geometry, - status, - applications, - ) -end - -mutable struct SceneRegistry - objects::Dict{ObjectId,Any} - by_scale::Dict{Symbol,Set{ObjectId}} - by_kind::Dict{Symbol,Set{ObjectId}} - by_species::Dict{Symbol,Set{ObjectId}} - by_name::Dict{Symbol,ObjectId} -end - -SceneRegistry() = SceneRegistry( - Dict{ObjectId,Any}(), - Dict{Symbol,Set{ObjectId}}(), - Dict{Symbol,Set{ObjectId}}(), - Dict{Symbol,Set{ObjectId}}(), - Dict{Symbol,ObjectId}(), -) - -struct MTGObjectAdapter{I,S,K,SP,N,G,ST} - id::I - scale::S - kind::K - species::SP - name::N - geometry::G - status::ST -end - -mutable struct Scene{R,A,E,I,SA} - registry::R - applications::A - environment::E - instances::I - source_adapter::SA - binding_cache::Any - environment_binding_cache::Any - bindings_dirty::Bool - environment_bindings_dirty::Bool - environment_dirty_objects::Union{Nothing,Set{ObjectId}} - revision::Int - environment_revision::Int -end - -function _normalize_object_instances(instances) - instances isa ObjectInstance && return (instances,) - normalized = _as_tuple(instances) - all(instance -> instance isa ObjectInstance, normalized) || error( - "Scene instances must be `ObjectInstance` values." - ) - return normalized -end - -function _instance_root_id(instance::ObjectInstance) - return instance.root isa Object ? instance.root.id : ObjectId(instance.root) -end - -function _collect_scene_items(items, instances) - objects = Object[] - mounted_instances = ObjectInstance[] - for item in items - if item isa Object - push!(objects, item) - elseif item isa ObjectInstance - push!(mounted_instances, item) - else - error("A `Scene` can contain only `Object` and `ObjectInstance` values, got `$(typeof(item))`.") - end - end - append!(mounted_instances, _normalize_object_instances(instances)) - for instance in mounted_instances - instance.root isa Object && push!(objects, instance.root) - append!(objects, instance.objects) - end - ids = Set{ObjectId}() - for object in objects - object.id in ids && error("Scene contains object id `$(object.id.value)` more than once.") - push!(ids, object.id) - end - return objects, mounted_instances -end - -function _object_descendant_ids(objects_by_id, root_id::ObjectId) - ids = ObjectId[root_id] - frontier = ObjectId[root_id] - while !isempty(frontier) - parent_id = popfirst!(frontier) - for object in values(objects_by_id) - object.parent == parent_id || continue - object.id in ids && continue - push!(ids, object.id) - push!(frontier, object.id) - end - end - return ids -end - -function _prepare_object_instances!(objects, instances) - objects_by_id = Dict(object.id => object for object in objects) - claimed_ids = Dict{ObjectId,Symbol}() - instance_ids = Dict{Symbol,Vector{ObjectId}}() - for instance in instances - root_id = _instance_root_id(instance) - haskey(objects_by_id, root_id) || error( - "Object instance `$(instance.name)` refers to missing root object `$(root_id.value)`." - ) - ids = _object_descendant_ids(objects_by_id, root_id) - for id in ids - if haskey(claimed_ids, id) - error( - "Object `$(id.value)` belongs to both instances `$(claimed_ids[id])` and `$(instance.name)`." - ) - end - claimed_ids[id] = instance.name - object = objects_by_id[id] - isnothing(object.kind) && (object.kind = instance.template.kind) - isnothing(object.species) && (object.species = instance.template.species) - end - root = objects_by_id[root_id] - if !isnothing(root.name) && root.name != instance.name - error( - "Object instance `$(instance.name)` root `$(root_id.value)` already has the conflicting name `$(root.name)`." - ) - end - root.name = instance.name - instance_ids[instance.name] = ids - end - length(instance_ids) == length(instances) || error("Object instance names must be unique within a scene.") - return instance_ids -end - -function _register_scene_objects!(scene::Scene, objects) - pending = copy(objects) - while !isempty(pending) - registered = false - for index in reverse(eachindex(pending)) - object = pending[index] - if isnothing(object.parent) || haskey(scene.registry.objects, object.parent) - register_object!(scene, object) - deleteat!(pending, index) - registered = true - end - end - registered && continue - unresolved = [(object.id.value, isnothing(object.parent) ? nothing : object.parent.value) for object in pending] - error("Cannot register scene objects because parent objects are missing or cyclic: $(unresolved).") - end - return scene -end - -""" - Scene(items...; applications=(), instances=(), environment=nothing) - -Create a scene from `Object` and `ObjectInstance` values. Global applications -and applications mounted from object instances are compiled through the same -scene/object dependency graph. -""" -function Scene( - items::Union{Object,ObjectInstance}...; - applications=(), - instances=(), - environment=nothing, - source_adapter=nothing, -) - objects, mounted_instances = _collect_scene_items(items, instances) - instance_ids = _prepare_object_instances!(objects, mounted_instances) - mounted_applications = _mount_object_instance_applications(mounted_instances, instance_ids) - normalized_applications = collect(Any, _as_tuple(applications)) - append!(normalized_applications, mounted_applications) - scene = Scene( - SceneRegistry(), - normalized_applications, - environment, - mounted_instances, - source_adapter, - nothing, - nothing, - true, - true, - nothing, - 0, - 0, - ) - return _register_scene_objects!(scene, objects) -end - -function _mtg_attribute(node, key::Symbol, default=nothing) - try - return node[key] - catch - return default - end -end - -""" - objects_from_mtg(root; id=node_id, scale=symbol, kind=..., species=..., - name=..., geometry=..., status=...) - -Adapt one MTG subtree to scene `Object` values. The MTG is traversed once; -node ids and parent relations become stable scene-object identities and -relations. Accessors may attach labels, geometry, and existing status objects -without prescribing a plant architecture. -""" -function objects_from_mtg( - root::MultiScaleTreeGraph.Node; - id=node_id, - scale=symbol, - kind=node -> _mtg_attribute(node, :kind, nothing), - species=node -> _mtg_attribute(node, :species, nothing), - name=node -> _mtg_attribute(node, :name, nothing), - geometry=node -> _mtg_attribute(node, :geometry, nothing), - status=node -> _mtg_attribute(node, :plantsimengine_status, nothing), -) - adapter = MTGObjectAdapter(id, scale, kind, species, name, geometry, status) - return _objects_from_mtg(root, adapter) -end - -function _objects_from_mtg(root::MultiScaleTreeGraph.Node, adapter::MTGObjectAdapter) - objects = Object[] - MultiScaleTreeGraph.traverse!(root) do node - node_parent = parent(node) - parent_id = node === root || isnothing(node_parent) ? nothing : adapter.id(node_parent) - push!( - objects, - Object( - adapter.id(node); - scale=adapter.scale(node), - kind=adapter.kind(node), - species=adapter.species(node), - name=adapter.name(node), - parent=parent_id, - geometry=adapter.geometry(node), - status=adapter.status(node), - ), - ) - end - return objects -end - -""" - Scene(root::MultiScaleTreeGraph.Node; applications=(), instances=(), - environment=nothing, id=node_id, scale=symbol, status=..., ...) - -Build a unified scene directly from an MTG subtree. The MTG accessors are -retained and reused by [`add_organ!`](@ref) when the topology grows. -""" -function Scene( - root::MultiScaleTreeGraph.Node; - applications=(), - instances=(), - environment=nothing, - id=node_id, - scale=symbol, - kind=node -> _mtg_attribute(node, :kind, nothing), - species=node -> _mtg_attribute(node, :species, nothing), - name=node -> _mtg_attribute(node, :name, nothing), - geometry=node -> _mtg_attribute(node, :geometry, nothing), - status=node -> _mtg_attribute(node, :plantsimengine_status, nothing), -) - adapter = MTGObjectAdapter(id, scale, kind, species, name, geometry, status) - objects = _objects_from_mtg(root, adapter) - return Scene( - objects...; - applications=applications, - instances=instances, - environment=environment, - source_adapter=adapter, - ) -end - -function _mark_environment_bindings_dirty!(scene::Scene, object_id::Union{Nothing,ObjectId}=nothing) - if isnothing(object_id) || isnothing(scene.environment_binding_cache) - scene.environment_binding_cache = nothing - scene.environment_dirty_objects = nothing - elseif !isnothing(scene.environment_dirty_objects) - push!(scene.environment_dirty_objects, object_id) - end - scene.environment_bindings_dirty = true - scene.environment_revision += 1 - return scene -end - -function _mark_bindings_dirty!(scene::Scene) - scene.binding_cache = nothing - scene.bindings_dirty = true - scene.revision += 1 - return _mark_environment_bindings_dirty!(scene) -end - -bindings_dirty(scene::Scene) = scene.bindings_dirty -environment_bindings_dirty(scene::Scene) = scene.environment_bindings_dirty -scene_revision(scene::Scene) = scene.revision -environment_revision(scene::Scene) = scene.environment_revision -compiled_bindings(scene::Scene) = scene.binding_cache -compiled_environment_bindings(scene::Scene) = scene.environment_binding_cache -mark_environment_binding_dirty!(scene::Scene) = _mark_environment_bindings_dirty!(scene) -function mark_environment_binding_dirty!(scene::Scene, id) - object_id = ObjectId(id) - _scene_object(scene, object_id) - return _mark_environment_bindings_dirty!(scene, object_id) -end -function mark_environment_binding_dirty!(scene::Scene, object::Object) - return mark_environment_binding_dirty!(scene, object.id) -end - -function _push_index!(index::Dict{Symbol,Set{ObjectId}}, key, id::ObjectId) - isnothing(key) && return nothing - push!(get!(index, key, Set{ObjectId}()), id) - return nothing -end - -function _delete_index!(index::Dict{Symbol,Set{ObjectId}}, key, id::ObjectId) - isnothing(key) && return nothing - ids = get(index, key, nothing) - isnothing(ids) && return nothing - delete!(ids, id) - isempty(ids) && delete!(index, key) - return nothing -end - -function _index_object!(registry::SceneRegistry, object::Object) - _push_index!(registry.by_scale, object.scale, object.id) - _push_index!(registry.by_kind, object.kind, object.id) - _push_index!(registry.by_species, object.species, object.id) - if !isnothing(object.name) - existing = get(registry.by_name, object.name, nothing) - if !isnothing(existing) && existing != object.id - error( - "Scene object name `$(object.name)` is already used by object `$(existing.value)`." - ) - end - registry.by_name[object.name] = object.id - end - return nothing -end - -function _deindex_object!(registry::SceneRegistry, object::Object) - _delete_index!(registry.by_scale, object.scale, object.id) - _delete_index!(registry.by_kind, object.kind, object.id) - _delete_index!(registry.by_species, object.species, object.id) - if !isnothing(object.name) && get(registry.by_name, object.name, nothing) == object.id - delete!(registry.by_name, object.name) - end - return nothing -end - -function _scene_object(scene::Scene, id) - oid = ObjectId(id) - haskey(scene.registry.objects, oid) || error("No scene object with id `$(oid.value)`.") - return scene.registry.objects[oid] -end - -function _instance_for_object(scene::Scene, id) - current_id = ObjectId(id) - while haskey(scene.registry.objects, current_id) - for instance in scene.instances - _instance_root_id(instance) == current_id && return instance - end - parent = scene.registry.objects[current_id].parent - isnothing(parent) && return nothing - current_id = parent - end - return nothing -end - -function _apply_instance_labels!(object::Object, instance) - isnothing(instance) && return object - isnothing(object.kind) && (object.kind = instance.template.kind) - isnothing(object.species) && (object.species = instance.template.species) - return object -end - -function register_object!(scene::Scene, object::Object; parent=object.parent) - registry = scene.registry - haskey(registry.objects, object.id) && error("Scene already contains object id `$(object.id.value)`.") - parent_id = isnothing(parent) ? nothing : ObjectId(parent) - if !isnothing(parent_id) && !haskey(registry.objects, parent_id) - error("No scene object with id `$(parent_id.value)`.") - end - if !isnothing(object.name) - existing = get(registry.by_name, object.name, nothing) - isnothing(existing) || error( - "Scene object name `$(object.name)` is already used by object `$(existing.value)`." - ) - end - instance = isnothing(parent_id) ? nothing : _instance_for_object(scene, parent_id) - _apply_instance_labels!(object, instance) - object.parent = parent_id - registry.objects[object.id] = object - _index_object!(registry, object) - if !isnothing(object.parent) - parent_object = registry.objects[object.parent] - object.id in parent_object.children || push!(parent_object.children, object.id) - end - _mark_bindings_dirty!(scene) - return object -end - -_runtime_scene(scene::Scene) = scene - -function _status_data!(data::Dict{Symbol,Any}, values) - values === nothing && return data - source = values isa Status ? NamedTuple(values) : values - source isa Union{NamedTuple,AbstractDict,Base.Pairs} || error( - "Initial organ status must be a `Status`, `NamedTuple`, `AbstractDict`, ", - "`Base.Pairs`, or `nothing`, got `$(typeof(values))`." - ) - for (key, value) in pairs(source) - Symbol(key) == :plantsimengine_status && continue - data[Symbol(key)] = value - end - return data -end - -function _organ_status(adapter::MTGObjectAdapter, node, initial_status) - adapted_status = adapter.status(node) - data = Dict{Symbol,Any}() - _status_data!(data, MultiScaleTreeGraph.node_attributes(node)) - _status_data!(data, adapted_status) - data[:node] = node - _status_data!(data, initial_status) - data[:node] = node - return Status((; data...)) -end - -""" - add_organ!(parent, runtime, link, symbol, scale; index=0, id, attributes=(), - initial_status=(), kind=nothing, species=nothing, name=nothing) - -Create an MTG node and register its corresponding scene object as one operation. -`runtime` may be a [`Scene`](@ref), [`SceneRunContext`](@ref), or -[`SceneSimulation`](@ref). The scene reuses the MTG accessors and status -initializer supplied when it was constructed, then overlays `initial_status`. - -This is the public growth API. [`register_object!`](@ref) remains the low-level -registry operation for callers that already own a fully initialized `Object`. -""" -function add_organ!( - parent_node::MultiScaleTreeGraph.Node, - runtime, - link, - organ_symbol, - mtg_scale::Integer; - index::Integer=0, - id=MultiScaleTreeGraph.new_id(MultiScaleTreeGraph.get_root(parent_node)), - attributes=NamedTuple(), - initial_status=NamedTuple(), - kind=nothing, - species=nothing, - name=nothing, -) - scene = _runtime_scene(runtime) - adapter = scene.source_adapter - adapter isa MTGObjectAdapter || error( - "`add_organ!` requires a scene constructed from an MTG. Use ", - "`register_object!` for scenes built directly from `Object` values." - ) - parent_id = ObjectId(adapter.id(parent_node)) - _scene_object(scene, parent_id) - root = MultiScaleTreeGraph.get_root(parent_node) - isnothing(MultiScaleTreeGraph.get_node(root, Int(id))) || error( - "MTG node id `$(id)` already exists." - ) - - node = MultiScaleTreeGraph.Node( - Int(id), - parent_node, - MultiScaleTreeGraph.NodeMTG( - Symbol(link), - Symbol(organ_symbol), - Int(index), - Int(mtg_scale), - ), - attributes, - ) - try - status = _organ_status(adapter, node, initial_status) - node[:plantsimengine_status] = status - object = Object( - adapter.id(node); - scale=adapter.scale(node), - kind=isnothing(kind) ? adapter.kind(node) : kind, - species=isnothing(species) ? adapter.species(node) : species, - name=isnothing(name) ? adapter.name(node) : name, - parent=parent_id, - geometry=adapter.geometry(node), - status=status, - ) - register_object!(scene, object) - return status - catch - MultiScaleTreeGraph.delete_node!(node) - rethrow() - end -end - -function _remove_child_link!(scene::Scene, parent_id, child_id::ObjectId) - isnothing(parent_id) && return nothing - parent_object = _scene_object(scene, parent_id) - filter!(!=(child_id), parent_object.children) - return nothing -end - -function remove_object!(scene::Scene, id; recursive::Bool=true) - object = _scene_object(scene, id) - if !recursive && !isempty(object.children) - error("Cannot remove object `$(object.id.value)` with children unless `recursive=true`.") - end - for child in copy(object.children) - remove_object!(scene, child; recursive=true) - end - _remove_child_link!(scene, object.parent, object.id) - _deindex_object!(scene.registry, object) - delete!(scene.registry.objects, object.id) - _mark_bindings_dirty!(scene) - return object -end - -function reparent_object!(scene::Scene, id, new_parent) - object = _scene_object(scene, id) - new_parent_id = isnothing(new_parent) ? nothing : ObjectId(new_parent) - if !isnothing(new_parent_id) - haskey(scene.registry.objects, new_parent_id) || error("No scene object with id `$(new_parent_id.value)`.") - end - _remove_child_link!(scene, object.parent, object.id) - object.parent = new_parent_id - if !isnothing(new_parent_id) - parent_object = _scene_object(scene, new_parent_id) - object.id in parent_object.children || push!(parent_object.children, object.id) - end - _mark_bindings_dirty!(scene) - return object -end - -function move_object!(scene::Scene, id, geometry_or_position) - return update_geometry!(scene, id, geometry_or_position) -end - -function update_geometry!(scene::Scene, id, geometry_or_position; invalidate_environment::Bool=true) - object = _scene_object(scene, id) - object.geometry = geometry_or_position - if invalidate_environment - _mark_environment_bindings_dirty!(scene, object.id) - for descendant_id in _geometry_inheriting_descendants(scene, object.id) - _mark_environment_bindings_dirty!(scene, descendant_id) - end - end - return object -end - -function update_geometry!(object::Object, geometry_or_position) - object.geometry = geometry_or_position - return object -end - -geometry(object::Object) = object.geometry -geometry(status::Status) = hasproperty(status, :geometry) ? status.geometry : nothing -geometry(x) = hasproperty(x, :geometry) ? getproperty(x, :geometry) : nothing - -function _geometry_position(g::NamedTuple) - haskey(g, :position) && return getproperty(g, :position) - if haskey(g, :x) && haskey(g, :y) && haskey(g, :z) - return (x=g.x, y=g.y, z=g.z) - elseif haskey(g, :x) && haskey(g, :y) - return (x=g.x, y=g.y) - end - return nothing -end -_geometry_position(_) = nothing - -position(object::Object) = _geometry_position(geometry(object)) -position(status::Status) = _geometry_position(geometry(status)) - -_geometry_bounds(g::NamedTuple) = haskey(g, :bounds) ? getproperty(g, :bounds) : nothing -_geometry_bounds(_) = nothing -bounds(object::Object) = _geometry_bounds(geometry(object)) -bounds(status::Status) = _geometry_bounds(geometry(status)) - -function _geometry_inheriting_descendants(scene::Scene, root_id::ObjectId) - ids = ObjectId[] - for child_id in _scene_object(scene, root_id).children - child = _scene_object(scene, child_id) - isnothing(geometry(child)) || continue - push!(ids, child_id) - append!(ids, _geometry_inheriting_descendants(scene, child_id)) - end - return ids -end - -function refresh_bindings!(scene::Scene, specs=scene.applications; force::Bool=false) - uses_scene_applications = specs === scene.applications - if !uses_scene_applications - return compile_scene(scene, specs) - end - if force || scene.bindings_dirty || isnothing(scene.binding_cache) - scene.binding_cache = compile_scene(scene, scene.applications) - scene.bindings_dirty = false - end - return scene.binding_cache -end - -function refresh_environment_bindings!(scene::Scene, compiled=refresh_bindings!(scene); force::Bool=false) - if force || scene.environment_bindings_dirty || isnothing(scene.environment_binding_cache) - if !force && - !isnothing(scene.environment_binding_cache) && - !isnothing(scene.environment_dirty_objects) - scene.environment_binding_cache = _refresh_environment_bindings_for_objects( - scene, - compiled, - scene.environment_binding_cache, - scene.environment_dirty_objects, - ) - else - scene.environment_binding_cache = compile_environment_bindings(scene, compiled) - end - scene.environment_bindings_dirty = false - scene.environment_dirty_objects = Set{ObjectId}() - else - reconciled = _reconcile_environment_binding_metadata( - scene, - compiled, - scene.environment_binding_cache, - ) - scene.environment_binding_cache = isnothing(reconciled) ? - compile_environment_bindings(scene, compiled) : - reconciled - end - return scene.environment_binding_cache -end - -function object_ids(scene::Scene; scale=nothing, kind=nothing, species=nothing, name=nothing) - registry = scene.registry - sets = Set{ObjectId}[] - isnothing(scale) || push!(sets, copy(get(registry.by_scale, Symbol(scale), Set{ObjectId}()))) - isnothing(kind) || push!(sets, copy(get(registry.by_kind, Symbol(kind), Set{ObjectId}()))) - isnothing(species) || push!(sets, copy(get(registry.by_species, Symbol(species), Set{ObjectId}()))) - if !isnothing(name) - id = get(registry.by_name, Symbol(name), nothing) - push!(sets, isnothing(id) ? Set{ObjectId}() : Set([id])) - end - isempty(sets) && return sort!(collect(keys(registry.objects)); by=id -> string(id.value)) - ids = reduce(intersect, sets) - return sort!(collect(ids); by=id -> string(id.value)) -end - -scene_objects(scene::Scene; kwargs...) = [_scene_object(scene, id) for id in object_ids(scene; kwargs...)] - -function _instance_object_ids(scene::Scene, instance::ObjectInstance) - root_id = _instance_root_id(instance) - haskey(scene.registry.objects, root_id) || return ObjectId[] - return _sort_object_ids!(_descendant_ids(scene, root_id)) -end - -function _object_instance_name(scene::Scene, object_id::ObjectId) - instance = _instance_for_object(scene, object_id) - return isnothing(instance) ? nothing : instance.name -end - -function explain_objects(scene::Scene) - return [ - ( - id=object.id.value, - scale=object.scale, - kind=object.kind, - species=object.species, - name=object.name, - instance=_object_instance_name(scene, object.id), - parent=isnothing(object.parent) ? nothing : object.parent.value, - children=[child.value for child in object.children], - has_geometry=!isnothing(object.geometry), - has_status=!isnothing(object.status), - n_applications=length(object.applications), - ) - for object in scene_objects(scene) - ] -end - -function _instance_application_ids(scene::Scene, instance::ObjectInstance) - prefix = string(instance.name, "__") - ids = Symbol[] - for application in scene.applications - name = application_name(as_model_spec(application)) - isnothing(name) && continue - startswith(string(name), prefix) && push!(ids, name) - end - return sort!(ids; by=string) -end - -function explain_instances(scene::Scene) - return [ - ( - name=instance.name, - root_id=_instance_root_id(instance).value, - kind=instance.template.kind, - species=instance.template.species, - object_ids=[id.value for id in _instance_object_ids(scene, instance)], - application_ids=_instance_application_ids(scene, instance), - instance_overrides=sort!(Symbol[Symbol(name) for name in keys(instance.overrides)]; by=string), - object_overrides=[ - ( - object_id=override.object.value, - process=override.process, - application=override.application, - model_type=typeof(override.model), - ) - for override in instance.object_overrides - ], - parameters_type=typeof(instance.template.parameters), - parameters_shared_by_reference=true, - ) - for instance in scene.instances - ] -end - -function _object_id_values(ids) - return [id.value for id in _sort_object_ids!(collect(ids))] -end - -function _label_scope_rows(scene::Scene, scope_type::Symbol, label::Symbol, index) - return [ - ( - scope_type=scope_type, - selector=label => key, - context=nothing, - root_id=nothing, - scale=label == :scale ? key : nothing, - kind=label == :kind ? key : nothing, - species=label == :species ? key : nothing, - name=nothing, - object_ids=_object_id_values(ids), - n_objects=length(ids), - ) - for (key, ids) in sort!(collect(index); by=pair -> string(first(pair))) - ] -end - -function explain_scopes(scene::Scene) - rows = NamedTuple[] - all_ids = object_ids(scene) - push!( - rows, - ( - scope_type=:scene, - selector=SceneScope(), - context=nothing, - root_id=nothing, - scale=nothing, - kind=nothing, - species=nothing, - name=nothing, - object_ids=[id.value for id in all_ids], - n_objects=length(all_ids), - ), - ) - for object in scene_objects(scene) - descendant_ids = _object_id_values(_descendant_ids(scene, object.id)) - push!( - rows, - ( - scope_type=:object_subtree, - selector=Self(), - context=object.id.value, - root_id=object.id.value, - scale=object.scale, - kind=object.kind, - species=object.species, - name=object.name, - object_ids=descendant_ids, - n_objects=length(descendant_ids), - ), - ) - object_scope_name = object.id.value isa Symbol ? object.id.value : Symbol(string(object.id.value)) - scope_names = Symbol[object_scope_name] - isnothing(object.name) || push!(scope_names, object.name) - unique!(scope_names) - for scope_name in scope_names - push!( - rows, - ( - scope_type=:named_scope, - selector=Scope(scope_name), - context=nothing, - root_id=object.id.value, - scale=object.scale, - kind=object.kind, - species=object.species, - name=scope_name, - object_ids=descendant_ids, - n_objects=length(descendant_ids), - ), - ) - end - end - append!(rows, _label_scope_rows(scene, :scale, :scale, scene.registry.by_scale)) - append!(rows, _label_scope_rows(scene, :kind, :kind, scene.registry.by_kind)) - append!(rows, _label_scope_rows(scene, :species, :species, scene.registry.by_species)) - return rows -end - -struct SceneScope <: AbstractObjectSelector end -struct Self <: AbstractObjectSelector end -struct SelfPlant <: AbstractObjectSelector end - -struct Ancestor <: AbstractObjectSelector - scale::Union{Nothing,Symbol} -end -Ancestor(; scale=nothing) = Ancestor(_maybe_symbol(scale)) - -struct Scope <: AbstractObjectSelector - name::Symbol -end -Scope(name::Union{Symbol,AbstractString}) = Scope(Symbol(name)) - -struct Kind <: AbstractObjectSelector - kind::Symbol -end -Kind(kind::Union{Symbol,AbstractString}) = Kind(Symbol(kind)) - -struct Species <: AbstractObjectSelector - species::Symbol -end -Species(species::Union{Symbol,AbstractString}) = Species(Symbol(species)) - -struct Scale <: AbstractObjectSelector - scale::Symbol -end -Scale(scale::Union{Symbol,AbstractString}) = Scale(Symbol(scale)) - -struct Relation <: AbstractObjectSelector - relation::Symbol - function Relation(relation::Symbol) - relation in _OBJECT_RELATIONS || error( - "Unsupported object relation `$(relation)`. Supported relations are $(_OBJECT_RELATIONS)." - ) - return new(relation) - end -end -const _OBJECT_RELATIONS = (:self, :parent, :children, :ancestors, :descendants, :siblings) -Relation(relation::AbstractString) = Relation(Symbol(relation)) - -_maybe_symbol(x) = isnothing(x) ? nothing : Symbol(x) - -const _OBJECT_ADDRESS_SYMBOL_FIELDS = (:kind, :species, :scale, :name, :process, :var, :relation, :application) - -function _maybe_symbol_collection(value) - value isa Tuple && return Tuple(_maybe_symbol(item) for item in value) - value isa AbstractVector && return Tuple(_maybe_symbol(item) for item in value) - return _maybe_symbol(value) -end - -function _normalize_object_selector_value(key::Symbol, value) - key in _OBJECT_ADDRESS_SYMBOL_FIELDS && return _maybe_symbol_collection(value) - return value -end - -function _object_matches_selector_value(object_value, requested) - isnothing(requested) && return true - requested isa Tuple && return object_value in requested - return object_value == requested -end - -function _normalize_selector_kwargs(kwargs) - return NamedTuple{keys(kwargs)}( - Tuple(_normalize_object_selector_value(k, v) for (k, v) in pairs(kwargs)) - ) -end - -function _normalize_selector_criteria(args::Tuple; kwargs...) - selectors = Tuple(args) - all(selector -> selector isa AbstractObjectSelector, selectors) || error( - "Object selector positional arguments must be selector objects such as `Kind(:plant)` or `Scale(:Leaf)`." - ) - normalized_kwargs = _normalize_selector_kwargs((; kwargs...)) - return (; selectors=selectors, normalized_kwargs...) -end - -struct One{C<:NamedTuple} <: AbstractObjectMultiplicity - criteria::C -end -struct OptionalOne{C<:NamedTuple} <: AbstractObjectMultiplicity - criteria::C -end -struct Many{C<:NamedTuple} <: AbstractObjectMultiplicity - criteria::C -end - -One(args...; kwargs...) = One(_normalize_selector_criteria(args; kwargs...)) -OptionalOne(args...; kwargs...) = OptionalOne(_normalize_selector_criteria(args; kwargs...)) -Many(args...; kwargs...) = Many(_normalize_selector_criteria(args; kwargs...)) - -criteria(selector::AbstractObjectMultiplicity) = selector.criteria -multiplicity(::One) = :one -multiplicity(::OptionalOne) = :optional_one -multiplicity(::Many) = :many - -_rebuild_selector(::One, criteria) = One{typeof(criteria)}(criteria) -_rebuild_selector(::OptionalOne, criteria) = OptionalOne{typeof(criteria)}(criteria) -_rebuild_selector(::Many, criteria) = Many{typeof(criteria)}(criteria) - -function _selector_with_scope(selector::AbstractObjectMultiplicity, scope) - selector_criteria = criteria(selector) - haskey(selector_criteria, :within) && return selector - return _rebuild_selector(selector, merge(selector_criteria, (; within=scope))) -end - -function _selector_with_application_prefix( - selector::AbstractObjectMultiplicity, - instance_name::Symbol, - template_application_names::Set{Symbol}, -) - selector_criteria = criteria(selector) - haskey(selector_criteria, :application) || return selector - application = selector_criteria.application - isnothing(application) && return selector - application in template_application_names || return selector - mounted_name = Symbol(instance_name, "__", application) - return _rebuild_selector(selector, merge(selector_criteria, (; application=mounted_name))) -end - -function _selector_with_previous_timestep( - selector::AbstractObjectMultiplicity, - previous::PreviousTimeStep, -) - return _rebuild_selector( - selector, - merge(criteria(selector), (; policy=previous)), - ) -end - -function _mounted_application_name(spec, index::Int) - name = application_name(spec) - return isnothing(name) ? process(spec) : name -end - -function _instance_override_matches(spec, key::Symbol) - name = application_name(spec) - return key == process(spec) || (!isnothing(name) && key == name) -end - -function _instance_override_models(instance::ObjectInstance, specs) - selected = Dict{Int,AbstractModel}() - for (key, replacement) in pairs(instance.overrides) - replacement isa AbstractModel || error( - "Override `$(key)` for instance `$(instance.name)` must be an `AbstractModel`, got `$(typeof(replacement))`." - ) - matches = findall(spec -> _instance_override_matches(spec, Symbol(key)), specs) - isempty(matches) && error( - "Override `$(key)` for instance `$(instance.name)` does not match a template application name or process." - ) - length(matches) == 1 || error( - "Override `$(key)` for instance `$(instance.name)` matches several template applications; use a unique application name." - ) - index = only(matches) - haskey(selected, index) && error( - "Several overrides target template application `$(_mounted_application_name(specs[index], index))` in instance `$(instance.name)`." - ) - _validate_model_override_contract!( - model_(specs[index]), - replacement; - description="Override `$(key)` for instance `$(instance.name)`", - ) - selected[index] = replacement - end - return selected -end - -function _model_contract(model) - return ( - process=process(model), - inputs=Tuple(Symbol.(keys(inputs_(model)))), - outputs=Tuple(Symbol.(keys(outputs_(model)))), - meteo_inputs=Tuple(Symbol.(keys(meteo_inputs_(model)))), - meteo_outputs=Tuple(Symbol.(keys(meteo_outputs_(model)))), - ) -end - -function _validate_model_override_contract!(base, replacement; description) - base_contract = _model_contract(base) - replacement_contract = _model_contract(replacement) - base_contract == replacement_contract && return nothing - error( - "$(description) has an incompatible model contract. Expected ", - "`$(base_contract)`, got `$(replacement_contract)`. Object and instance ", - "overrides may change parameters or implementation, but not process or declared variables." - ) -end - -function _object_override_matches(spec, override::Override) - process_match = isnothing(override.process) || process(spec) == override.process - name = application_name(spec) - application_match = isnothing(override.application) || - (!isnothing(name) && name == override.application) - return process_match && application_match -end - -function _object_override_models(instance::ObjectInstance, specs, instance_ids) - entries = Dict{Int,Vector{Pair{ObjectId,AbstractModel}}}() - valid_ids = Set(instance_ids) - for override in instance.object_overrides - override.object in valid_ids || error( - "Object override for `$(override.object.value)` does not belong to instance `$(instance.name)`." - ) - matches = findall(spec -> _object_override_matches(spec, override), specs) - isempty(matches) && error( - "Object override for `$(override.object.value)` in instance `$(instance.name)` ", - "does not match a template application." - ) - length(matches) == 1 || error( - "Object override for `$(override.object.value)` in instance `$(instance.name)` ", - "matches several template applications; add `application=...`." - ) - index = only(matches) - object_models = get!(entries, index, Pair{ObjectId,AbstractModel}[]) - any(entry -> first(entry) == override.object, object_models) && error( - "Several object overrides target application `$(_mounted_application_name(specs[index], index))` ", - "on object `$(override.object.value)` in instance `$(instance.name)`." - ) - _validate_model_override_contract!( - model_(specs[index]), - override.model; - description="Object override for `$(override.object.value)` in instance `$(instance.name)`", - ) - push!(object_models, override.object => override.model) - end - selected = Dict{Int,Any}() - for (index, object_models) in entries - selected[index] = _typed_object_model_dict(object_models) - end - return selected -end - -function _typed_object_model_dict(entries) - isempty(entries) && return Dict{ObjectId,AbstractModel}() - model_type = typeof(last(first(entries))) - if all(entry -> typeof(last(entry)) == model_type, entries) - models = Dict{ObjectId,model_type}() - for (object_id, model) in entries - models[object_id] = model - end - return models - end - return Dict{ObjectId,AbstractModel}(entries) -end - -function _map_selector_bindings(bindings::NamedTuple, f) - mapped = Pair{Symbol,Any}[] - for (name, selector) in pairs(bindings) - push!(mapped, Symbol(name) => (selector isa AbstractObjectMultiplicity ? f(selector) : selector)) - end - return (; mapped...) -end - -function _mount_object_instance_applications(instance::ObjectInstance, instance_ids) - specs = Tuple(as_model_spec(application) for application in instance.template.applications) - base_names = Set(_mounted_application_name(spec, index) for (index, spec) in pairs(specs)) - instance_overrides = _instance_override_models(instance, specs) - object_overrides = _object_override_models(instance, specs, instance_ids) - mounted = Any[] - for (index, spec) in pairs(specs) - base_name = _mounted_application_name(spec, index) - mounted_name = Symbol(instance.name, "__", base_name) - target = applies_to(spec) - isnothing(target) && error( - "Template application `$(base_name)` has no `AppliesTo(...)` selector." - ) - mounted_target = _selector_with_scope(target, Scope(instance.name)) - prefix_application = selector -> _selector_with_application_prefix( - selector, - instance.name, - base_names, - ) - mounted_inputs = _map_selector_bindings(value_inputs(spec), prefix_application) - mounted_calls = _map_selector_bindings(model_calls(spec), prefix_application) - mounted_model = get(instance_overrides, index, model_(spec)) - if haskey(object_overrides, index) - object_models = object_overrides[index] - for replacement in values(object_models) - _validate_model_override_contract!( - mounted_model, - replacement; - description="Object override for template application `$(base_name)`", - ) - end - mounted_model = ObjectModelOverrides(mounted_model, object_models) - end - push!( - mounted, - ModelSpec( - spec; - model=mounted_model, - name=mounted_name, - applies_to=mounted_target, - inputs=mounted_inputs, - calls=mounted_calls, - ), - ) - end - return Tuple(mounted) -end - -function _mount_object_instance_applications(instances, instance_ids) - mounted = Any[] - for instance in instances - append!( - mounted, - _mount_object_instance_applications(instance, instance_ids[instance.name]), - ) - end - return Tuple(mounted) -end - -_sort_object_ids!(ids) = sort!(ids; by=id -> string(id.value)) - -function _object_id_from_context(context) - isnothing(context) && return nothing - context isa Object && return context.id - return ObjectId(context) -end - -function _descendant_ids(scene::Scene, root_id::ObjectId) - ids = ObjectId[root_id] - object = _scene_object(scene, root_id) - for child_id in object.children - append!(ids, _descendant_ids(scene, child_id)) - end - return ids -end - -function _ancestor_id(scene::Scene, current_id::ObjectId; scale=nothing, kind=nothing) - id = current_id - while true - object = _scene_object(scene, id) - scale_match = isnothing(scale) || object.scale == Symbol(scale) - kind_match = isnothing(kind) || object.kind == Symbol(kind) - scale_match && kind_match && return id - isnothing(object.parent) && return nothing - id = object.parent - end -end - -function _ancestor_ids(scene::Scene, current_id::ObjectId) - ids = ObjectId[] - parent_id = _scene_object(scene, current_id).parent - while !isnothing(parent_id) - push!(ids, parent_id) - parent_id = _scene_object(scene, parent_id).parent - end - return ids -end - -function _relation_object_ids(scene::Scene, relation::Symbol, context) - current_id = _object_id_from_context(context) - isnothing(current_id) && error( - "`Relation(:$(relation))` selectors require a current object context." - ) - object = _scene_object(scene, current_id) - relation == :self && return ObjectId[current_id] - relation == :parent && return isnothing(object.parent) ? ObjectId[] : ObjectId[object.parent] - relation == :children && return copy(object.children) - relation == :ancestors && return _ancestor_ids(scene, current_id) - relation == :descendants && return _descendant_ids(scene, current_id)[2:end] - if relation == :siblings - isnothing(object.parent) && return ObjectId[] - return ObjectId[ - sibling_id for sibling_id in _scene_object(scene, object.parent).children - if sibling_id != current_id - ] - end - error("Unsupported object relation `$(relation)`.") -end - -function _selector_scope_from_positional(selectors) - scopes = filter( - selector -> selector isa Union{SceneScope,Self,SelfPlant,Ancestor,Scope}, - selectors, - ) - isempty(scopes) && return nothing - length(scopes) == 1 || error("Only one scope selector can be used in one object selector.") - return only(scopes) -end - -function _selector_value_from_positional(selectors, ::Type{Kind}) - values = [selector.kind for selector in selectors if selector isa Kind] - isempty(values) && return nothing - length(unique(values)) == 1 || error("Conflicting `Kind(...)` selector values: $(values).") - return only(unique(values)) -end - -function _selector_value_from_positional(selectors, ::Type{Species}) - values = [selector.species for selector in selectors if selector isa Species] - isempty(values) && return nothing - length(unique(values)) == 1 || error("Conflicting `Species(...)` selector values: $(values).") - return only(unique(values)) -end - -function _selector_value_from_positional(selectors, ::Type{Scale}) - values = [selector.scale for selector in selectors if selector isa Scale] - isempty(values) && return nothing - length(unique(values)) == 1 || error("Conflicting `Scale(...)` selector values: $(values).") - return only(unique(values)) -end - -function _selector_value_from_positional(selectors, ::Type{Relation}) - values = [selector.relation for selector in selectors if selector isa Relation] - isempty(values) && return nothing - length(unique(values)) == 1 || error("Conflicting `Relation(...)` selector values: $(values).") - return only(unique(values)) -end - -function _criteria_value(criteria, key::Symbol, selector_type) - positional = _selector_value_from_positional(criteria.selectors, selector_type) - keyword = haskey(criteria, key) ? getproperty(criteria, key) : nothing - if !isnothing(positional) && !isnothing(keyword) && positional != keyword - error("Conflicting selector values for `$(key)`: `$(positional)` and `$(keyword)`.") - end - return isnothing(keyword) ? positional : keyword -end - -function _criteria_scope(criteria) - positional = _selector_scope_from_positional(criteria.selectors) - keyword = haskey(criteria, :within) ? criteria.within : nothing - if !isnothing(positional) && !isnothing(keyword) && typeof(positional) != typeof(keyword) - error("Conflicting scope selectors: `$(positional)` and `$(keyword)`.") - end - return isnothing(keyword) ? positional : keyword -end - -function _scope_object_ids(scene::Scene, scope, context) - if isnothing(scope) || scope isa SceneScope - return ObjectId[keys(scene.registry.objects)...] - end - - current_id = _object_id_from_context(context) - if scope isa Self - isnothing(current_id) && error("`Self()` selectors require a current object context.") - return _descendant_ids(scene, current_id) - elseif scope isa SelfPlant - isnothing(current_id) && error("`SelfPlant()` selectors require a current object context.") - plant_id = _ancestor_id(scene, current_id; scale=:Plant) - isnothing(plant_id) && error("No `scale=:Plant` ancestor found for object `$(current_id.value)`.") - return _descendant_ids(scene, plant_id) - elseif scope isa Ancestor - isnothing(current_id) && error("`Ancestor(...)` selectors require a current object context.") - ancestor_id = _ancestor_id(scene, current_id; scale=scope.scale) - if isnothing(ancestor_id) - error("No matching ancestor found for object `$(current_id.value)` and selector `$(scope)`.") - end - return _descendant_ids(scene, ancestor_id) - elseif scope isa Scope - root_id = get(scene.registry.by_name, scope.name, nothing) - if isnothing(root_id) - candidate = ObjectId(scope.name) - root_id = haskey(scene.registry.objects, candidate) ? candidate : nothing - end - if isnothing(root_id) - available = sort!(unique(Symbol[ - keys(scene.registry.by_name)..., - (id.value for id in keys(scene.registry.objects))..., - ]); by=string) - suggestions = _near_symbol_matches(scope.name, available) - error( - "No named scope or object `$(scope.name)` found in the scene registry. ", - "available=$(available), suggestions=$(suggestions)." - ) - end - return _descendant_ids(scene, root_id) - end - - error("Unsupported object scope selector `$(scope)` of type `$(typeof(scope))`.") -end - -function _symbol_edit_distance(left::Symbol, right::Symbol) - a = collect(string(left)) - b = collect(string(right)) - previous = collect(0:length(b)) - current = similar(previous) - for i in eachindex(a) - current[1] = i - for j in eachindex(b) - substitution = previous[j] + (a[i] == b[j] ? 0 : 1) - current[j + 1] = min(current[j] + 1, previous[j + 1] + 1, substitution) - end - previous, current = current, previous - end - return previous[end] -end - -function _near_symbol_matches(requested, available) - isnothing(requested) && return Symbol[] - requested_symbol = Symbol(requested) - threshold = max(2, cld(length(string(requested_symbol)), 3)) - ranked = Tuple{Int,Symbol}[ - (_symbol_edit_distance(requested_symbol, candidate), candidate) - for candidate in available - ] - filter!(pair -> first(pair) <= threshold, ranked) - sort!(ranked; by=pair -> (first(pair), string(last(pair)))) - return Symbol[last(pair) for pair in Iterators.take(ranked, 3)] -end - -function _available_selector_labels(scene::Scene, candidate_ids) - objects = (_scene_object(scene, id) for id in candidate_ids) - scales = Symbol[] - kinds = Symbol[] - species = Symbol[] - names = Symbol[] - for object in objects - isnothing(object.scale) || push!(scales, object.scale) - isnothing(object.kind) || push!(kinds, object.kind) - isnothing(object.species) || push!(species, object.species) - isnothing(object.name) || push!(names, object.name) - end - return ( - scales=sort!(unique!(scales); by=string), - kinds=sort!(unique!(kinds); by=string), - species=sort!(unique!(species); by=string), - names=sort!(unique!(names); by=string), - ) -end - -function _selector_resolution_error( - scene::Scene, - selector, - candidate_ids, - matched_ids; - context=nothing, - scale=nothing, - kind=nothing, - species=nothing, - name=nothing, -) - available = _available_selector_labels(scene, candidate_ids) - suggestions = ( - scale=_near_symbol_matches(scale, available.scales), - kind=_near_symbol_matches(kind, available.kinds), - species=_near_symbol_matches(species, available.species), - name=_near_symbol_matches(name, available.names), - ) - expected = selector isa One ? "exactly one" : "zero or one" - context_id = _object_id_from_context(context) - error( - "Expected $(expected) object for selector `$(selector)`, got $(length(matched_ids)). ", - "context=$(isnothing(context_id) ? nothing : context_id.value), ", - "matched_ids=$([id.value for id in matched_ids]), ", - "requested=(scale=$(scale), kind=$(kind), species=$(species), name=$(name)), ", - "available=$(available), suggestions=$(suggestions)." - ) -end - -function _matches_object_criteria(object::Object; scale=nothing, kind=nothing, species=nothing, name=nothing) - _object_matches_selector_value(object.scale, scale) || return false - _object_matches_selector_value(object.kind, kind) || return false - _object_matches_selector_value(object.species, species) || return false - _object_matches_selector_value(object.name, name) || return false - return true -end - -function resolve_object_ids(scene::Scene, selector::AbstractObjectMultiplicity; context=nothing) - return _resolve_object_ids(scene, selector; context=context) -end - -function _resolve_object_ids( - scene::Scene, - selector::AbstractObjectMultiplicity; - context=nothing, - default_to_context::Bool=false, - default_scope=nothing, -) - criteria_ = criteria(selector) - relation = _criteria_value(criteria_, :relation, Relation) - - explicit_scope = _criteria_scope(criteria_) - scope = if !isnothing(explicit_scope) - explicit_scope - elseif isnothing(relation) - default_scope - else - nothing - end - scale = _criteria_value(criteria_, :scale, Scale) - kind = _criteria_value(criteria_, :kind, Kind) - species = _criteria_value(criteria_, :species, Species) - name = haskey(criteria_, :name) ? criteria_.name : nothing - - if default_to_context && - isnothing(explicit_scope) && - isnothing(relation) && - isnothing(scale) && - isnothing(kind) && - isnothing(species) && - isnothing(name) && - !isnothing(context) - return ObjectId[_object_id_from_context(context)] - end - - candidate_ids = if isnothing(relation) - _scope_object_ids(scene, scope, context) - else - related_ids = _relation_object_ids(scene, relation, context) - if isnothing(explicit_scope) - related_ids - else - scoped_ids = Set(_scope_object_ids(scene, explicit_scope, context)) - ObjectId[id for id in related_ids if id in scoped_ids] - end - end - ids = ObjectId[ - id for id in candidate_ids - if _matches_object_criteria(_scene_object(scene, id); scale=scale, kind=kind, species=species, name=name) - ] - _sort_object_ids!(ids) - - if selector isa One && length(ids) != 1 - _selector_resolution_error( - scene, - selector, - candidate_ids, - ids; - context=context, - scale=scale, - kind=kind, - species=species, - name=name, - ) - elseif selector isa OptionalOne && length(ids) > 1 - _selector_resolution_error( - scene, - selector, - candidate_ids, - ids; - context=context, - scale=scale, - kind=kind, - species=species, - name=name, - ) - end - return ids -end - -resolve_objects(scene::Scene, selector::AbstractObjectMultiplicity; context=nothing) = - [_scene_object(scene, id) for id in resolve_object_ids(scene, selector; context=context)] - -function _default_dependency_scope(scene::Scene, context::ObjectId) - object = _scene_object(scene, context) - (object.scale == :Scene || object.kind == :scene) && return SceneScope() - return Self() -end - -struct CompiledSceneApplication{S,AT,TS,CL,MO} - id::Symbol - spec::S - process::Symbol - name::Union{Nothing,Symbol} - target_ids::Vector{ObjectId} - applies_to::AT - timestep::TS - clock::CL - model_overrides::MO -end - -struct CompiledSceneInputBinding{SEL,P,W,C} - application_id::Symbol - consumer_id::ObjectId - input::Symbol - selector::SEL - origin::Symbol - source_ids::Vector{ObjectId} - source_application_ids::Vector{Symbol} - source_var::Symbol - process::Union{Nothing,Symbol} - application::Union{Nothing,Symbol} - multiplicity::Symbol - policy::P - window::W - carrier_hint::Symbol - carrier::C -end - -struct CompiledSceneCallBinding{SEL} - application_id::Symbol - consumer_id::ObjectId - call::Symbol - selector::SEL - origin::Symbol - callee_object_ids::Vector{ObjectId} - callee_application_ids::Vector{Symbol} - process::Union{Nothing,Symbol} - application::Union{Nothing,Symbol} - multiplicity::Symbol -end - -struct CompiledEnvironmentBinding{B,C,S} - application_id::Symbol - object_id::ObjectId - provider::Symbol - backend::B - cell::C - required_inputs::Vector{Symbol} - source_inputs::Vector{Symbol} - produced_outputs::Vector{Symbol} - support::S - geometry_source_object_id::Union{Nothing,ObjectId} - geometry_source::Symbol - config::Any -end - -struct CompiledEnvironmentBindings{SC,B,I,S,C} - scene::SC - bindings::B - by_target::I - samplers_by_application::S - sample_cache::C - scene_revision::Int - environment_revision::Int -end - -struct CompiledScene{SC,AP,AI,IB,CB,IBI,CBI,MBI,AO} - scene::SC - applications::AP - applications_by_id::AI - input_bindings::IB - call_bindings::CB - input_bindings_by_target::IBI - call_bindings_by_target::CBI - model_bundles_by_target::MBI - application_order::AO - revision::Int -end - -function _index_scene_bindings(bindings, application_field::Symbol, object_field::Symbol) - grouped = Dict{Tuple{Symbol,ObjectId},Vector{Any}}() - for binding in bindings - key = ( - getproperty(binding, application_field), - getproperty(binding, object_field), - ) - push!(get!(grouped, key, Any[]), binding) - end - return Dict(key => Tuple(values) for (key, values) in grouped) -end - -function compile_scene(scene::Scene) - return compile_scene(scene, scene.applications) -end - -function compile_scene(scene::Scene, specs::Tuple) - return _compile_scene(scene, specs) -end - -function compile_scene(scene::Scene, specs::AbstractVector) - return _compile_scene(scene, Tuple(specs)) -end - -function compile_scene(scene::Scene, specs...) - return _compile_scene(scene, specs) -end - -function _scene_timeline(scene::Scene) - backend = environment_backend(scene.environment) - try - return _timeline_context(backend) - catch - return TimelineContext(3600.0) - end -end - -function _compile_scene(scene::Scene, raw_specs) - timeline = _scene_timeline(scene) - applications = _compile_scene_applications(scene, raw_specs, timeline) - call_bindings = _compile_scene_call_bindings(scene, applications) - _validate_scene_writers!(applications, call_bindings) - _prepare_scene_output_statuses!(scene, applications) - input_bindings = _compile_scene_input_bindings( - scene, - applications, - _manual_call_application_ids(call_bindings), - ) - _prepare_scene_bound_input_statuses!(scene, applications, input_bindings) - _wire_scene_input_carriers!(scene, input_bindings) - _validate_scene_required_inputs!(scene, applications, input_bindings) - input_bindings_by_target = _index_scene_bindings(input_bindings, :application_id, :consumer_id) - call_bindings_by_target = _index_scene_bindings(call_bindings, :application_id, :consumer_id) - application_order = _compile_scene_application_order(applications, input_bindings, call_bindings) - applications_by_id = Dict(application.id => application for application in applications) - model_bundles_by_target = _compile_scene_model_bundles( - applications, - applications_by_id, - call_bindings_by_target, - ) - return CompiledScene( - scene, - applications, - applications_by_id, - input_bindings, - call_bindings, - input_bindings_by_target, - call_bindings_by_target, - model_bundles_by_target, - application_order, - scene.revision, - ) -end - -function _compile_scene_applications(scene::Scene, raw_specs, timeline) - process_counts = Dict{Symbol,Int}() - ids = Set{Symbol}() - applications = CompiledSceneApplication[] - for raw_spec in raw_specs - spec = as_model_spec(raw_spec) - selector = applies_to(spec) - isnothing(selector) && error( - "Model application for process `$(process(spec))` has no `AppliesTo(...)` selector." - ) - selector isa AbstractObjectMultiplicity || error( - "`AppliesTo(...)` for process `$(process(spec))` must be an object selector such as `Many(scale=:Leaf)`." - ) - proc = process(spec) - occurrence = get(process_counts, proc, 0) + 1 - process_counts[proc] = occurrence - name = application_name(spec) - app_id = isnothing(name) ? (occurrence == 1 ? proc : Symbol(string(proc), "_", occurrence)) : name - app_id in ids && error("Duplicate compiled scene application id `$(app_id)`.") - push!(ids, app_id) - target_ids = resolve_object_ids(scene, selector) - spec = _scene_spec_with_meteo_hints( - scene, - spec, - _scene_application_hint_scale(scene, target_ids), - ) - model_overrides = _compiled_object_model_overrides(spec, target_ids, app_id) - push!( - applications, - CompiledSceneApplication( - app_id, - spec, - proc, - name, - target_ids, - selector, - timestep(spec), - _scene_application_clock(scene, spec, target_ids, timeline), - model_overrides, - ), - ) - end - return applications -end - -function _scene_spec_with_meteo_hints(scene::Scene, spec, scale::Symbol) - hint = _normalize_meteo_hint(scale, process(spec), meteo_hint(model_(spec))) - - current_bindings = meteo_bindings(spec) - has_explicit_bindings = !(current_bindings isa NamedTuple && isempty(keys(current_bindings))) - new_bindings = has_explicit_bindings || isnothing(hint.bindings) ? current_bindings : hint.bindings - new_bindings = _scene_meteo_bindings_with_environment_sources(spec, new_bindings) - - current_window = meteo_window(spec) - new_window = isnothing(current_window) && !isnothing(hint.window) ? hint.window : current_window - - (new_bindings === current_bindings && new_window === current_window) && return spec - return ModelSpec(spec; meteo_bindings=new_bindings, meteo_window=new_window) -end - -function _scene_meteo_bindings_with_environment_sources(spec, bindings) - sources = _environment_source_overrides(spec) - isempty(keys(sources)) && return bindings - - bindings = bindings isa NamedTuple ? bindings : NamedTuple() - model_inputs = Set(Symbol.(keys(meteo_inputs_(spec)))) - unknown = Symbol[target for target in keys(sources) if !(Symbol(target) in model_inputs)] - isempty(unknown) || error( - "`Environment(; sources=...)` for process `$(process(spec))` contains ", - "unknown model-facing meteo input(s) `$(Tuple(unknown))`. Declared ", - "`meteo_inputs_` are `$(Tuple(sort!(collect(model_inputs); by=string)))`." - ) - - targets = Symbol[Symbol(target) for target in keys(bindings)] - for target in keys(sources) - target = Symbol(target) - target in targets || push!(targets, target) - end - - resolved = Pair{Symbol,Any}[] - for target in targets - rule = haskey(bindings, target) ? - _normalize_meteo_binding_rule(target, bindings[target]) : - (source=target, reducer=PlantMeteo.MeanWeighted()) - source = haskey(sources, target) ? Symbol(sources[target]) : rule.source - push!(resolved, target => (source=source, reducer=rule.reducer)) - end - return (; resolved...) -end - -function _compiled_object_model_overrides(spec, target_ids, application_id::Symbol) - model = model_(spec) - model isa ObjectModelOverrides || return nothing - target_set = Set(target_ids) - unmatched = ObjectId[id for id in keys(model.overrides) if !(id in target_set)] - isempty(unmatched) || error( - "Object override(s) `$([id.value for id in unmatched])` for application ", - "`$(application_id)` do not match its `AppliesTo(...)` target set." - ) - return model.overrides -end - -_application_default_model(application::CompiledSceneApplication) = - model_(application.spec) isa ObjectModelOverrides ? - model_(application.spec).base : - model_(application.spec) - -function _application_model(application::CompiledSceneApplication, object_id::ObjectId) - isnothing(application.model_overrides) && return _application_default_model(application) - return get( - application.model_overrides, - object_id, - _application_default_model(application), - ) -end - -function _scene_output_names(application::CompiledSceneApplication) - return Symbol[Symbol(var) for var in keys(outputs_(application.spec))] -end - -function _scene_canonical_output_names(application::CompiledSceneApplication) - return Symbol[ - variable for variable in _scene_output_names(application) - if _publish_mode_for_output(application.spec, variable) == :canonical - ] -end - -function _scene_writer_groups(applications, skipped_application_ids=Set{Symbol}()) - groups = Dict{Tuple{ObjectId,Symbol},Vector{Tuple{Int,Any}}}() - for (index, application) in pairs(applications) - application.id in skipped_application_ids && continue - for object_id in application.target_ids - for variable in _scene_canonical_output_names(application) - push!(get!(groups, (object_id, variable), Tuple{Int,Any}[]), (index, application)) - end - end - end - return groups -end - -function _application_match_labels(application::CompiledSceneApplication) - labels = Set{Symbol}([application.id, application.process]) - isnothing(application.name) || push!(labels, application.name) - return labels -end - -function _update_variables(update) - return Tuple(Symbol(variable) for variable in getproperty(update, :variables)) -end - -function _update_after(update) - return Tuple(Symbol(label) for label in getproperty(update, :after)) -end - -function _matching_updates(spec, variable::Symbol) - return [update for update in updates(spec) if variable in _update_variables(update)] -end - -function _update_after_labels(spec, variable::Symbol) - labels = Symbol[] - for update in _matching_updates(spec, variable) - append!(labels, _update_after(update)) - end - unique!(labels) - return labels -end - -function _updates_after_previous_writer(spec, variable::Symbol, previous_applications) - matching = _matching_updates(spec, variable) - isempty(matching) && return false - previous_labels = Set{Symbol}() - for application in previous_applications - union!(previous_labels, _application_match_labels(application)) - end - for update in matching - after = _update_after(update) - isempty(after) && continue - any(label -> label in previous_labels, after) && return true - end - return false -end - -function _declares_update_without_previous_writer(spec, variable::Symbol, previous_applications) - isempty(previous_applications) || return false - for update in _matching_updates(spec, variable) - isempty(_update_after(update)) || return true - end - return false -end - -function _manual_call_application_ids(call_bindings) - ids = Set{Symbol}() - for binding in call_bindings - union!(ids, binding.callee_application_ids) - end - return ids -end - -function _validate_scene_writers!(applications, call_bindings=()) - manual_application_ids = _manual_call_application_ids(call_bindings) - for ((object_id, variable), indexed_writers) in _scene_writer_groups(applications, manual_application_ids) - length(indexed_writers) <= 1 && continue - sort!(indexed_writers; by=first) - previous = CompiledSceneApplication[] - for (_, application) in indexed_writers - if _declares_update_without_previous_writer(application.spec, variable, previous) - error( - "Application `$(application.id)` declares `Updates($(variable))` for object ", - "`$(object_id.value)`, but no previous writer for `$(variable)` exists. ", - "Move it after the producer named in `after=...`." - ) - end - if !isempty(previous) && !_updates_after_previous_writer(application.spec, variable, previous) - previous_labels = sort!(collect(reduce(union!, (_application_match_labels(app) for app in previous); init=Set{Symbol}()))) - error( - "Variable `$(variable)` on object `$(object_id.value)` is written by multiple ", - "applications. Application `$(application.id)` must declare ", - "`Updates(:$(variable); after=...)` matching one of the previous writers ", - "`$(previous_labels)`." - ) - end - push!(previous, application) - end - end - return nothing -end - -function _scene_application_hint_scale(scene::Scene, target_ids::Vector{ObjectId}) - isempty(target_ids) && return :Scene - scales = unique!([_scene_object(scene, object_id).scale for object_id in target_ids]) - length(scales) == 1 && return only(scales) - return :Mixed -end - -function _scene_application_clock(scene::Scene, spec, target_ids::Vector{ObjectId}, timeline) - model = model_(spec) - source = _runtime_clock_source_for_spec(spec) - source == :meteo_base_step || return _model_clock(spec, model, timeline) - scale = _scene_application_hint_scale(scene, target_ids) - clock, hint_reason = _resolve_meteo_hint_clock(scale, process(spec), model, timeline) - isnothing(hint_reason) || error(hint_reason) - return clock -end - -function _criteria_get(criteria, key::Symbol, default=nothing) - return haskey(criteria, key) ? getproperty(criteria, key) : default -end - -function _selector_policy(selector::AbstractObjectMultiplicity) - return _criteria_get(criteria(selector), :policy, HoldLast()) -end - -_selector_has_policy(selector::AbstractObjectMultiplicity) = - haskey(criteria(selector), :policy) - -function _scene_policy_from_source_application(applications_by_id, application_id::Symbol, source_var::Symbol) - application = get(applications_by_id, application_id, nothing) - isnothing(application) && error( - "No compiled scene application with id `$(application_id)` while resolving ", - "default output policy for `$(source_var)`." - ) - return _policy_for_output(_application_default_model(application), source_var) -end - -function _scene_selector_policy(selector::AbstractObjectMultiplicity, applications_by_id, source_application_ids, source_var::Symbol) - _selector_has_policy(selector) && return _selector_policy(selector) - isempty(source_application_ids) && return HoldLast() - length(source_application_ids) == 1 && return _scene_policy_from_source_application( - applications_by_id, - only(source_application_ids), - source_var, - ) - policies = [ - _scene_policy_from_source_application(applications_by_id, application_id, source_var) - for application_id in source_application_ids - ] - first_policy = first(policies) - all(policy -> policy == first_policy, policies) && return first_policy - error( - "Cannot infer default policy for scene input from `$(source_var)` because ", - "selector resolves several source applications `$(source_application_ids)` with ", - "different output policies. Add `policy=...` to `Inputs(...)`." - ) -end - -function _selector_window(selector::AbstractObjectMultiplicity) - return _criteria_get(criteria(selector), :window, nothing) -end - -function _selector_var(selector::AbstractObjectMultiplicity, fallback::Symbol) - return _criteria_get(criteria(selector), :var, fallback) -end - -function _selector_application(selector::AbstractObjectMultiplicity) - return _criteria_get(criteria(selector), :application, nothing) -end - -function _dependency_object_ids(scene::Scene, selector::AbstractObjectMultiplicity, context::ObjectId) - return _resolve_object_ids( - scene, - selector; - context=context, - default_to_context=true, - default_scope=_default_dependency_scope(scene, context), - ) -end - -function _carrier_hint(selector::AbstractObjectMultiplicity, policy, window) - !isnothing(window) && return :temporal_stream - policy isa HoldLast || return :temporal_stream - selector isa Many && return :ref_vector - return :shared_ref -end - -function _status_ref_or_nothing(status, var::Symbol) - status isa Status || return nothing - var in propertynames(status) || return nothing - return refvalue(status, var) -end - -function _input_carrier(scene::Scene, selector::AbstractObjectMultiplicity, source_ids::Vector{ObjectId}, source_var::Symbol) - refs = Base.RefValue[] - for source_id in source_ids - object = _scene_object(scene, source_id) - source_ref = _status_ref_or_nothing(object.status, source_var) - isnothing(source_ref) && return nothing - push!(refs, source_ref) - end - if selector isa Many - isempty(refs) && return RefVector{Any}() - return _ref_vector_carrier(refs) - end - return isempty(refs) ? nothing : only(refs) -end - -function _ref_vector_carrier(refs) - T = typeof(refs[1][]) - typed_refs = Base.RefValue{T}[] - for source_ref in refs - source_ref isa Base.RefValue{T} || return ObjectRefVector(refs) - push!(typed_refs, source_ref) - end - return RefVector(typed_refs) -end - -function _status_with_reference(status::Status, variable::Symbol, reference::Base.RefValue) - names = propertynames(status) - if variable in names - references = ntuple( - index -> names[index] == variable ? reference : refvalue(status, names[index]), - length(names), - ) - return Status(NamedTuple{names}(references)) - end - extended_names = (names..., variable) - references = (ntuple(index -> refvalue(status, names[index]), length(names))..., reference) - return Status(NamedTuple{extended_names}(references)) -end - -function _status_with_default(status::Status, variable::Symbol, value) - variable in propertynames(status) && return status - return _status_with_reference(status, variable, Ref(value)) -end - -function _ensure_scene_object_status!(scene::Scene, object_id::ObjectId) - object = _scene_object(scene, object_id) - isnothing(object.status) && (object.status = Status()) - object.status isa Status || error( - "Scene object `$(object_id.value)` uses model applications but its status has type ", - "`$(typeof(object.status))`. Use `Status(...)` or leave status as `nothing`." - ) - return object.status -end - -function _prepare_scene_output_statuses!(scene::Scene, applications) - for application in applications - defaults = merge(outputs_(application.spec), meteo_outputs_(application.spec)) - for object_id in application.target_ids - status = _ensure_scene_object_status!(scene, object_id) - for (variable, value) in pairs(defaults) - status = _status_with_default(status, Symbol(variable), value) - end - _scene_object(scene, object_id).status = status - end - end - return scene -end - -function _prepare_scene_bound_input_statuses!(scene::Scene, applications, bindings) - applications_by_id = Dict(application.id => application for application in applications) - for binding in bindings - status = _ensure_scene_object_status!(scene, binding.consumer_id) - binding.input in propertynames(status) && continue - application = applications_by_id[binding.application_id] - defaults = inputs_(application.spec) - binding.input in keys(defaults) || error( - "Bound input `$(binding.input)` is not declared by application `$(binding.application_id)`." - ) - _scene_object(scene, binding.consumer_id).status = - _status_with_default(status, binding.input, getproperty(defaults, binding.input)) - end - return scene -end - -function _wire_scene_input_carriers!(scene::Scene, bindings) - for binding in bindings - binding.carrier_hint == :temporal_stream && continue - isnothing(binding.carrier) && continue - object = _scene_object(scene, binding.consumer_id) - status = object.status - status isa Status || continue - reference = binding.carrier isa Base.RefValue ? binding.carrier : Ref(binding.carrier) - object.status = _status_with_reference(status, binding.input, reference) - end - return scene -end - -input_carrier(binding::CompiledSceneInputBinding) = binding.carrier -has_reference_carrier(binding::CompiledSceneInputBinding) = !isnothing(binding.carrier) -input_value(binding::CompiledSceneInputBinding) = _input_value(binding.carrier) -_input_value(::Nothing) = nothing -_input_value(carrier::Base.RefValue) = carrier[] -_input_value(carrier::RefVector) = carrier -_input_value(carrier::ObjectRefVector) = carrier - -function _matching_input_source_applications( - applications_by_object, - source_ids, - source_var::Symbol, - process_filter, - application_filter; - allow_empty::Bool=false, -) - matches = Symbol[] - for source_id in source_ids - for application in get(applications_by_object, source_id, Any[]) - source_var in _scene_output_names(application) || continue - isnothing(process_filter) || application.process == process_filter || continue - isnothing(application_filter) || application.id == application_filter || continue - push!(matches, application.id) - end - end - unique!(matches) - if !allow_empty && - (!isnothing(process_filter) || !isnothing(application_filter)) && - isempty(matches) - error( - "Input selector for source variable `$(source_var)` requested", - isnothing(process_filter) ? "" : " process `$(process_filter)`", - isnothing(application_filter) ? "" : " application `$(application_filter)`", - ", but no matching source application was found." - ) - end - return matches -end - -function _compile_scene_input_bindings( - scene::Scene, - applications, - manual_application_ids=Set{Symbol}(), -) - bindings = CompiledSceneInputBinding[] - by_object = _applications_by_object(applications) - by_id = Dict(application.id => application for application in applications) - for application in applications - for consumer_id in application.target_ids - declared_inputs = value_inputs(application.spec) - declared_inputs isa NamedTuple || (declared_inputs = NamedTuple()) - for (input_name, selector) in pairs(declared_inputs) - input_sym = Symbol(input_name) - _validate_declared_scene_input_name!(application, input_sym) - selector isa AbstractObjectMultiplicity || error( - "Input binding `$(input_sym)` on application `$(application.id)` must use an object selector." - ) - _push_scene_input_binding!( - bindings, - scene, - application, - consumer_id, - input_sym, - selector, - get(input_origins(application.spec), input_sym, :model_spec), - by_object, - by_id, - ) - end - application.id in manual_application_ids && continue - _append_inferred_scene_input_bindings!( - bindings, - scene, - application, - consumer_id, - declared_inputs, - by_object, - by_id, - ) - end - end - return bindings -end - -function _push_scene_input_binding!( - bindings, - scene::Scene, - application::CompiledSceneApplication, - consumer_id::ObjectId, - input_sym::Symbol, - selector::AbstractObjectMultiplicity, - origin::Symbol, - applications_by_object, - applications_by_id, - source_ids_override=nothing, -) - source_ids = isnothing(source_ids_override) ? _dependency_object_ids(scene, selector, consumer_id) : source_ids_override - window = _selector_window(selector) - source_var = _selector_var(selector, input_sym) - process_filter = _criteria_get(criteria(selector), :process, nothing) - application_filter = _selector_application(selector) - source_application_ids = _matching_input_source_applications( - applications_by_object, - source_ids, - source_var, - process_filter, - application_filter, - allow_empty=selector isa OptionalOne, - ) - if selector isa OptionalOne && - (!isnothing(process_filter) || !isnothing(application_filter)) && - isempty(source_application_ids) - source_ids = ObjectId[] - end - policy = _scene_selector_policy(selector, applications_by_id, source_application_ids, source_var) - if policy isa PreviousTimeStep - policy.variable == input_sym || error( - "PreviousTimeStep marker for input `$(input_sym)` on application ", - "`$(application.id)` names `$(policy.variable)`. Use ", - "`PreviousTimeStep(:$(input_sym))`." - ) - else - _validate_policy_instance( - _scene_object(scene, consumer_id).scale, - application.process, - input_sym, - policy, - ) - end - carrier = _input_carrier(scene, selector, source_ids, source_var) - carrier_hint = - isempty(source_ids) && selector isa OptionalOne ? - :optional_default : - _carrier_hint(selector, policy, window) - _validate_scene_input_source!( - scene, - application, - consumer_id, - input_sym, - source_var, - source_ids, - carrier, - carrier_hint, - ) - push!( - bindings, - CompiledSceneInputBinding( - application.id, - consumer_id, - input_sym, - selector, - origin, - source_ids, - source_application_ids, - source_var, - process_filter, - application_filter, - multiplicity(selector), - policy, - window, - carrier_hint, - carrier, - ), - ) - return bindings -end - -function _scene_input_names(application::CompiledSceneApplication) - return Symbol[Symbol(var) for var in keys(inputs_(application.spec))] -end - -function _validate_scene_input_source!( - scene::Scene, - application::CompiledSceneApplication, - consumer_id::ObjectId, - input_sym::Symbol, - source_var::Symbol, - source_ids::Vector{ObjectId}, - carrier, - carrier_hint::Symbol, -) - carrier_hint == :temporal_stream && return nothing - !isnothing(carrier) && return nothing - status_source_ids = ObjectId[ - source_id for source_id in source_ids - if _scene_object(scene, source_id).status isa Status - ] - isempty(status_source_ids) && return nothing - error( - "Input binding `$(input_sym)` on application `$(application.id)` for object ", - "`$(consumer_id.value)` reads `$(source_var)` from objects ", - "`$([id.value for id in status_source_ids])`, but no source `Status` reference is available." - ) -end - -function _validate_declared_scene_input_name!(application::CompiledSceneApplication, input_sym::Symbol) - input_names = Set(_scene_input_names(application)) - input_sym in input_names && return nothing - error( - "Input binding `$(input_sym)` on application `$(application.id)` is not declared by ", - "`inputs_` for process `$(application.process)`. Declared model inputs are ", - "`$(sort!(collect(input_names)))`." - ) -end - -function _same_object_output_applications(applications_by_object, application::CompiledSceneApplication, object_id::ObjectId, variable::Symbol) - matches = CompiledSceneApplication[] - for candidate in get(applications_by_object, object_id, Any[]) - candidate.id == application.id && continue - variable in _scene_canonical_output_names(candidate) || continue - push!(matches, candidate) - end - return matches -end - -function _append_inferred_scene_input_bindings!( - bindings, - scene::Scene, - application::CompiledSceneApplication, - consumer_id::ObjectId, - declared_inputs, - applications_by_object, - applications_by_id, -) - declared_names = declared_inputs isa NamedTuple ? Set(Symbol.(keys(declared_inputs))) : Set{Symbol}() - for input_sym in _scene_input_names(application) - input_sym in declared_names && continue - matches = _same_object_output_applications(applications_by_object, application, consumer_id, input_sym) - isempty(matches) && continue - if length(matches) > 1 - error( - "Input `$(input_sym)` on application `$(application.id)` for object `$(consumer_id.value)` ", - "has ambiguous same-object producers: `$([match.id for match in matches])`. ", - "Add `Inputs(:$(input_sym) => One(...))` to disambiguate." - ) - end - producer = only(matches) - selector = One(within=Self(), process=producer.process, application=producer.id, var=input_sym) - _push_scene_input_binding!( - bindings, - scene, - application, - consumer_id, - input_sym, - selector, - :inferred_same_object, - applications_by_object, - applications_by_id, - ObjectId[consumer_id], - ) - end - return bindings -end - -function _bound_scene_inputs(input_bindings) - bound = Set{Tuple{Symbol,ObjectId,Symbol}}() - for binding in input_bindings - push!(bound, (binding.application_id, binding.consumer_id, binding.input)) - end - return bound -end - -function _status_has_variable(scene::Scene, object_id::ObjectId, variable::Symbol) - object = _scene_object(scene, object_id) - object.status isa Status || return false - return variable in propertynames(object.status) -end - -function _validate_scene_required_inputs!(scene::Scene, applications, input_bindings) - bound = _bound_scene_inputs(input_bindings) - missing = NamedTuple[] - for application in applications - for object_id in application.target_ids - for input in _scene_input_names(application) - (application.id, object_id, input) in bound && continue - _status_has_variable(scene, object_id, input) && continue - push!( - missing, - ( - application_id=application.id, - object_id=object_id.value, - input=input, - process=application.process, - ), - ) - end - end - end - isempty(missing) && return nothing - details = join( - [ - "`$(row.application_id)` on object `$(row.object_id)` requires `$(row.input)`" - for row in missing - ], - "; ", - ) - error( - "Missing required scene/object input(s): ", - details, - ". Provide the variable on object `Status`, add an `Inputs(...)` binding, ", - "or add an unambiguous same-object producer." - ) -end - -function _applications_by_object(applications) - by_object = Dict{ObjectId,Vector{Any}}() - for application in applications - for object_id in application.target_ids - push!(get!(by_object, object_id, Any[]), application) - end - end - return by_object -end - -function _matching_callee_applications(applications, object_id::ObjectId, proc, application_name_filter) - matches = Symbol[] - for application in get(applications, object_id, Any[]) - isnothing(proc) || application.process == proc || continue - isnothing(application_name_filter) || application.id == application_name_filter || continue - push!(matches, application.id) - end - return matches -end - -function _compile_scene_call_bindings(scene::Scene, applications) - by_object = _applications_by_object(applications) - bindings = CompiledSceneCallBinding[] - for application in applications - calls = model_calls(application.spec) - calls isa NamedTuple || continue - for consumer_id in application.target_ids - for (call_name, selector) in pairs(calls) - call_sym = Symbol(call_name) - selector isa AbstractObjectMultiplicity || error( - "Call binding `$(call_sym)` on application `$(application.id)` must use an object selector." - ) - callee_object_ids = _dependency_object_ids(scene, selector, consumer_id) - proc = _criteria_get(criteria(selector), :process, nothing) - app_name = _selector_application(selector) - callee_application_ids = Symbol[] - for object_id in callee_object_ids - append!( - callee_application_ids, - _matching_callee_applications(by_object, object_id, proc, app_name), - ) - end - unique!(callee_application_ids) - if isempty(callee_application_ids) && !(selector isa OptionalOne) - error( - "Call `$(call_sym)` on application `$(application.id)` matched objects ", - "$([id.value for id in callee_object_ids]) but no model application", - isnothing(proc) ? "." : " with process `$(proc)`.", - ) - end - if selector isa One && length(callee_application_ids) != 1 - error( - "Call `$(call_sym)` on application `$(application.id)` expected one callee application, ", - "got `$(callee_application_ids)`. Add `application=:name` to disambiguate." - ) - elseif selector isa OptionalOne && length(callee_application_ids) > 1 - error( - "Call `$(call_sym)` on application `$(application.id)` expected zero or one callee application, ", - "got `$(callee_application_ids)`. Add `application=:name` to disambiguate." - ) - end - push!( - bindings, - CompiledSceneCallBinding( - application.id, - consumer_id, - call_sym, - selector, - get(call_origins(application.spec), call_sym, :model_spec), - callee_object_ids, - callee_application_ids, - proc, - app_name, - multiplicity(selector), - ), - ) - end - end - end - return bindings -end - -function _scene_call_owners(call_bindings) - owners = Dict{Symbol,Set{Symbol}}() - for binding in call_bindings - for callee_id in binding.callee_application_ids - push!(get!(owners, callee_id, Set{Symbol}()), binding.application_id) - end - end - return owners -end - -function _add_scene_application_edge!(children, parent::Symbol, child::Symbol) - parent == child && return nothing - push!(get!(children, parent, Set{Symbol}()), child) - return nothing -end - -function _scene_input_order_edges!(children, input_bindings, call_owners) - for binding in input_bindings - binding.policy isa PreviousTimeStep && continue - for source_id in binding.source_application_ids - owners = get(call_owners, source_id, nothing) - if isnothing(owners) - _add_scene_application_edge!(children, source_id, binding.application_id) - else - for owner_id in owners - _add_scene_application_edge!(children, owner_id, binding.application_id) - end - end - end - end - return children -end - -function _scene_update_order_edges!(children, applications) - for indexed_writers in values(_scene_writer_groups(applications)) - length(indexed_writers) > 1 || continue - sort!(indexed_writers; by=first) - for index in 2:length(indexed_writers) - previous_application = indexed_writers[index - 1][2] - application = indexed_writers[index][2] - _add_scene_application_edge!(children, previous_application.id, application.id) - end - end - return children -end - -function _stable_topological_application_order(applications, children) - application_ids = Symbol[application.id for application in applications] - positions = Dict(application_id => index for (index, application_id) in pairs(application_ids)) - indegree = Dict(application_id => 0 for application_id in application_ids) - for child_ids in values(children) - for child_id in child_ids - indegree[child_id] = get(indegree, child_id, 0) + 1 - end - end - ready = Symbol[application_id for application_id in application_ids if indegree[application_id] == 0] - order = Symbol[] - while !isempty(ready) - sort!(ready; by=application_id -> positions[application_id]) - application_id = popfirst!(ready) - push!(order, application_id) - child_ids = sort!(collect(get(children, application_id, Set{Symbol}())); by=child_id -> positions[child_id]) - for child_id in child_ids - indegree[child_id] -= 1 - indegree[child_id] == 0 && push!(ready, child_id) - end - end - if length(order) != length(application_ids) - remaining = Symbol[application_id for application_id in application_ids if indegree[application_id] > 0] - error( - "Scene application dependency cycle detected among applications `$(remaining)`. ", - "Break the same-timestep cycle with a temporal policy or revise `Inputs(...)`/`Updates(...)`." - ) - end - return order -end - -function _compile_scene_application_order(applications, input_bindings, call_bindings) - children = Dict{Symbol,Set{Symbol}}() - call_owners = _scene_call_owners(call_bindings) - _scene_input_order_edges!(children, input_bindings, call_owners) - _scene_update_order_edges!(children, applications) - return _stable_topological_application_order(applications, children) -end - -function _ordered_scene_applications(compiled::CompiledScene) - return [compiled.applications_by_id[application_id] for application_id in compiled.application_order] -end - -function explain_scene_applications(compiled::CompiledScene) - return [ - ( - application_id=application.id, - process=application.process, - name=application.name, - target_ids=[id.value for id in application.target_ids], - applies_to=application.applies_to, - timestep=application.timestep, - clock=application.clock, - model_type=typeof(_application_default_model(application)), - model_storage=isnothing(application.model_overrides) ? :shared_application : :per_object_override, - model_dispatch=_application_model_dispatch(application), - object_overrides=isnothing(application.model_overrides) ? - NamedTuple[] : - [ - ( - object_id=object_id.value, - model_type=typeof(model), - ) - for (object_id, model) in sort!( - collect(application.model_overrides); - by=pair -> string(first(pair).value), - ) - ], - ) - for application in compiled.applications - ] -end - -function _application_model_dispatch(application::CompiledSceneApplication) - isnothing(application.model_overrides) && return :concrete_shared - override_type = valtype(typeof(application.model_overrides)) - default_type = typeof(_application_default_model(application)) - return isconcretetype(override_type) && default_type == override_type ? - :concrete_per_object : - :heterogeneous_per_object -end - -function explain_schedule(compiled::CompiledScene) - timeline = _scene_timeline(compiled.scene) - manual_application_ids = _manual_call_application_ids(compiled) - execution_positions = Dict(application_id => index for (index, application_id) in pairs(compiled.application_order)) - return [ - ( - application_id=application.id, - process=application.process, - execution_index=execution_positions[application.id], - timestep=application.timestep, - clock=application.clock, - dt_steps=application.clock.dt, - phase=application.clock.phase, - dt_seconds=float(application.clock.dt) * timeline.base_step_seconds, - target_ids=[id.value for id in application.target_ids], - root_scheduled=!(application.id in manual_application_ids), - manual_call_only=application.id in manual_application_ids, - ) - for application in _ordered_scene_applications(compiled) - ] -end - -function _scene_binding_carrier_kind(binding::CompiledSceneInputBinding) - binding.carrier_hint == :temporal_stream && return :temporal_stream - binding.carrier_hint == :optional_default && return :optional_default - carrier = binding.carrier - isnothing(carrier) && return :unresolved - carrier isa Base.RefValue && return :ref - carrier isa RefVector && return :ref_vector - carrier isa ObjectRefVector && return :object_ref_vector - return :custom -end - -function _scene_binding_copy_semantics(binding::CompiledSceneInputBinding) - kind = _scene_binding_carrier_kind(binding) - kind in (:ref, :ref_vector, :object_ref_vector) && return :live_references - kind == :temporal_stream && return :materialized_temporal_value - kind == :optional_default && return :consumer_default - kind == :unresolved && return :not_materialized - return :backend_defined -end - -function explain_bindings(compiled::CompiledScene) - return [ - ( - application_id=binding.application_id, - consumer_id=binding.consumer_id.value, - input=binding.input, - origin=binding.origin, - source_ids=[id.value for id in binding.source_ids], - source_application_ids=binding.source_application_ids, - source_var=binding.source_var, - process=binding.process, - application=binding.application, - multiplicity=binding.multiplicity, - policy=binding.policy, - window=binding.window, - carrier_hint=binding.carrier_hint, - carrier_kind=_scene_binding_carrier_kind(binding), - copy_semantics=_scene_binding_copy_semantics(binding), - has_reference_carrier=has_reference_carrier(binding), - carrier_type=isnothing(binding.carrier) ? nothing : typeof(binding.carrier), - selector=binding.selector, - ) - for binding in compiled.input_bindings - ] -end - -function explain_calls(compiled::CompiledScene) - return [ - ( - application_id=binding.application_id, - consumer_id=binding.consumer_id.value, - call=binding.call, - origin=binding.origin, - callee_object_ids=[id.value for id in binding.callee_object_ids], - callee_application_ids=binding.callee_application_ids, - process=binding.process, - application=binding.application, - multiplicity=binding.multiplicity, - publication_policy=:explicit_accept, - default_publish=false, - accepted_publish=true, - resolved=!isempty(binding.callee_application_ids), - selector=binding.selector, - ) - for binding in compiled.call_bindings - ] -end - -function explain_model_bundles(compiled::CompiledScene) - return [ - ( - application_id=application_id, - object_id=object_id.value, - processes=collect(keys(models)), - model_types=[typeof(model) for model in values(models)], - ) - for ((application_id, object_id), models) in sort!( - collect(compiled.model_bundles_by_target); - by=pair -> (string(first(pair)[1]), string(first(pair)[2].value)), - ) - ] -end - -function explain_writers(compiled::CompiledScene) - groups = _scene_writer_groups(compiled.applications, _manual_call_application_ids(compiled)) - rows = NamedTuple[] - for ((object_id, variable), indexed_writers) in groups - sort!(indexed_writers; by=first) - applications = [application for (_, application) in indexed_writers] - push!( - rows, - ( - object_id=object_id.value, - variable=variable, - application_ids=[application.id for application in applications], - processes=[application.process for application in applications], - update_application_ids=[ - application.id for application in applications - if !isempty(_matching_updates(application.spec, variable)) - ], - update_after=[ - application.id => _update_after_labels(application.spec, variable) - for application in applications - if !isempty(_matching_updates(application.spec, variable)) - ], - duplicate=length(applications) > 1, - ), - ) - end - sort!(rows; by=row -> (string(row.object_id), string(row.variable))) - return rows -end - -function _environment_config_payload(config) - config isa EnvironmentConfig && return config.config - return config -end - -function _environment_backend_from_config(scene::Scene, config) - payload = _environment_config_payload(config) - isnothing(payload) && return environment_backend(scene.environment) - payload isa NamedTuple && haskey(payload, :backend) && return environment_backend(payload.backend) - payload isa AbstractEnvironmentBackend && return payload - return environment_backend(scene.environment) -end - -function _environment_provider_from_config(config, backend) - payload = _environment_config_payload(config) - payload isa NamedTuple && haskey(payload, :provider) && return Symbol(payload.provider) - payload isa Symbol && return payload - isnothing(backend) && return :none - return :scene -end - -function _object_environment_support(application::CompiledSceneApplication, object::Object) - scale = isnothing(object.scale) ? :Default : object.scale - return EnvironmentSupport(application.id, scale, application.process, object.status) -end - -bind_environment(backend, object::Object, support, config=nothing) = :global - -function _environment_binding_object(scene::Scene, object::Object) - !isnothing(geometry(object)) && return object, object.id, :self - ancestor_id = object.parent - while !isnothing(ancestor_id) - ancestor = _scene_object(scene, ancestor_id) - if !isnothing(geometry(ancestor)) - proxy = Object( - object.id; - scale=object.scale, - kind=object.kind, - species=object.species, - name=object.name, - parent=object.parent, - children=object.children, - geometry=geometry(ancestor), - status=object.status, - applications=object.applications, - ) - return proxy, ancestor.id, :ancestor - end - ancestor_id = ancestor.parent - end - return object, nothing, :global -end - -function _scene_environment_entities(scene::Scene) - return [ - ( - id=object.id.value, - object=object, - scale=object.scale, - kind=object.kind, - species=object.species, - name=object.name, - parent=isnothing(object.parent) ? nothing : object.parent.value, - geometry=geometry(object), - position=position(object), - bounds=bounds(object), - status=object.status, - ) - for object in scene_objects(scene) - ] -end - -function _scene_environment_backends(scene::Scene, compiled::CompiledScene) - backends = Any[] - seen = Set{UInt}() - for application in compiled.applications - backend = _environment_backend_from_config(scene, environment_config(application.spec)) - isnothing(backend) && continue - id = objectid(backend) - id in seen && continue - push!(seen, id) - push!(backends, backend) - end - return backends -end - -function _update_scene_environment_indices!(scene::Scene, compiled::CompiledScene) - entities = _scene_environment_entities(scene) - for backend in _scene_environment_backends(scene, compiled) - update_index!(backend, entities) - end - return nothing -end - -function _environment_variable_names(vars) - return Symbol[Symbol(var) for var in keys(vars)] -end - -function _environment_source_variable_names(model_spec) - return Symbol[Symbol(source) for (_, source) in _environment_sampling_rules(model_spec)] -end - -function _compile_environment_bindings_for_applications(scene::Scene, applications) - bindings = CompiledEnvironmentBinding[] - for application in applications - config = environment_config(application.spec) - backend = _environment_backend_from_config(scene, config) - provider = _environment_provider_from_config(config, backend) - required_inputs = _environment_variable_names(meteo_inputs_(application.spec)) - source_inputs = _environment_source_variable_names(application.spec) - produced_outputs = _environment_variable_names(meteo_outputs_(application.spec)) - for object_id in application.target_ids - object = _scene_object(scene, object_id) - support = _object_environment_support(application, object) - binding_object, geometry_source_object_id, geometry_source = - _environment_binding_object(scene, object) - cell = bind_environment( - backend, - binding_object, - support, - _environment_config_payload(config), - ) - push!( - bindings, - CompiledEnvironmentBinding( - application.id, - object_id, - provider, - backend, - cell, - required_inputs, - source_inputs, - produced_outputs, - support, - geometry_source_object_id, - geometry_source, - config, - ), - ) - end - end - return bindings -end - -_compile_environment_bindings(scene::Scene, compiled::CompiledScene) = - _compile_environment_bindings_for_applications(scene, compiled.applications) - -function _validate_scene_environment_inputs!(bindings, applications_by_id) - missing_rows = NamedTuple[] - for binding in bindings - available = environment_variables(binding.backend) - isnothing(available) && continue - application = applications_by_id[binding.application_id] - for (target, source) in _environment_sampling_rules(application.spec) - source in available && continue - push!( - missing_rows, - ( - application_id=binding.application_id, - object_id=binding.object_id.value, - process=application.process, - target=target, - source=source, - available=Tuple(sort!(collect(available); by=string)), - ), - ) - end - end - isempty(missing_rows) && return nothing - details = join( - [ - string( - row.application_id, - "/", - row.object_id, - " (", - row.process, - ") needs `", - row.target, - "` from source `", - row.source, - "`; available=", - row.available, - ) - for row in missing_rows - ], - "; ", - ) - error("Scene environment is missing required meteo inputs: ", details) -end - -function _scene_environment_samplers(bindings) - samplers_by_application = Dict{Symbol,Any}() - prepared_sources = Tuple{Any,Any}[] - for binding in bindings - haskey(samplers_by_application, binding.application_id) && continue - binding.backend isa GlobalConstant || continue - meteo = environment_meteo(binding.backend) - source_index = findfirst(entry -> entry[1] === meteo, prepared_sources) - sampler = if isnothing(source_index) - prepared = _prepare_meteo_sampler(meteo) - push!(prepared_sources, (meteo, prepared)) - prepared - else - prepared_sources[source_index][2] - end - samplers_by_application[binding.application_id] = sampler - end - return samplers_by_application -end - -function _compiled_environment_bindings( - scene::Scene, - bindings, - by_target, - samplers_by_application=_scene_environment_samplers(bindings), - sample_cache=Dict{Tuple{Symbol,Int},Any}(), -) - return CompiledEnvironmentBindings( - scene, - bindings, - by_target, - samplers_by_application, - sample_cache, - scene.revision, - scene.environment_revision, - ) -end - -function compile_environment_bindings(scene::Scene, compiled::CompiledScene=refresh_bindings!(scene)) - _update_scene_environment_indices!(scene, compiled) - bindings = _compile_environment_bindings(scene, compiled) - _validate_scene_environment_inputs!(bindings, compiled.applications_by_id) - by_target = Dict( - (binding.application_id, binding.object_id) => binding - for binding in bindings - ) - length(by_target) == length(bindings) || error( - "Environment binding compilation produced duplicate `(application_id, object_id)` targets." - ) - return _compiled_environment_bindings(scene, bindings, by_target) -end - -function _same_environment_backend(a, b) - a === b && return true - if a isa GlobalConstant && b isa GlobalConstant - return environment_meteo(a) === environment_meteo(b) - end - return false -end - -function _same_environment_support(a, b) - return a.application == b.application && - a.scale == b.scale && - a.process == b.process && - a.status === b.status -end - -function _reconcile_environment_binding_metadata( - scene::Scene, - compiled::CompiledScene, - cached::CompiledEnvironmentBindings, -) - expected_count = sum(length(application.target_ids) for application in compiled.applications) - expected_count == length(cached.bindings) || return nothing - - bindings = CompiledEnvironmentBinding[] - changed = false - for application in compiled.applications - config = environment_config(application.spec) - backend = _environment_backend_from_config(scene, config) - provider = _environment_provider_from_config(config, backend) - required_inputs = _environment_variable_names(meteo_inputs_(application.spec)) - source_inputs = _environment_source_variable_names(application.spec) - produced_outputs = _environment_variable_names(meteo_outputs_(application.spec)) - for object_id in application.target_ids - key = (application.id, object_id) - old = get(cached.by_target, key, nothing) - isnothing(old) && return nothing - object = _scene_object(scene, object_id) - support = _object_environment_support(application, object) - _, geometry_source_object_id, geometry_source = - _environment_binding_object(scene, object) - _same_environment_backend(old.backend, backend) || return nothing - old.provider == provider || return nothing - isequal(old.config, config) || return nothing - old.geometry_source_object_id == geometry_source_object_id || - return nothing - old.geometry_source == geometry_source || return nothing - _same_environment_support(old.support, support) || return nothing - - if old.required_inputs == required_inputs && - old.source_inputs == source_inputs && - old.produced_outputs == produced_outputs - push!(bindings, old) - else - changed = true - push!( - bindings, - CompiledEnvironmentBinding( - application.id, - object_id, - provider, - backend, - old.cell, - required_inputs, - source_inputs, - produced_outputs, - support, - geometry_source_object_id, - geometry_source, - config, - ), - ) - end - end - end - changed || return cached - _validate_scene_environment_inputs!(bindings, compiled.applications_by_id) - by_target = Dict( - (binding.application_id, binding.object_id) => binding - for binding in bindings - ) - return _compiled_environment_bindings(scene, bindings, by_target) -end - -function _refresh_environment_bindings_for_objects( - scene::Scene, - compiled::CompiledScene, - cached::CompiledEnvironmentBindings, - dirty_object_ids, -) - _update_scene_environment_indices!(scene, compiled) - dirty = Set(dirty_object_ids) - replacements = Dict{Tuple{Symbol,ObjectId},CompiledEnvironmentBinding}() - for application in compiled.applications - selected_ids = ObjectId[id for id in application.target_ids if id in dirty] - isempty(selected_ids) && continue - partial_application = CompiledSceneApplication( - application.id, - application.spec, - application.process, - application.name, - selected_ids, - application.applies_to, - application.timestep, - application.clock, - application.model_overrides, - ) - for binding in _compile_environment_bindings_for_applications(scene, (partial_application,)) - replacements[(binding.application_id, binding.object_id)] = binding - end - end - bindings = CompiledEnvironmentBinding[ - get(replacements, (binding.application_id, binding.object_id), binding) - for binding in cached.bindings - ] - _validate_scene_environment_inputs!(bindings, compiled.applications_by_id) - by_target = Dict( - (binding.application_id, binding.object_id) => binding for binding in bindings - ) - return _compiled_environment_bindings( - scene, - bindings, - by_target, - cached.samplers_by_application, - cached.sample_cache, - ) -end - -function explain_environment_bindings(compiled::CompiledEnvironmentBindings) - return [ - ( - application_id=binding.application_id, - object_id=binding.object_id.value, - provider=binding.provider, - backend_type=isnothing(binding.backend) ? nothing : typeof(binding.backend), - cell=binding.cell, - required_inputs=binding.required_inputs, - source_inputs=binding.source_inputs, - produced_outputs=binding.produced_outputs, - temporal_sampler=!isnothing( - get(compiled.samplers_by_application, binding.application_id, nothing), - ), - geometry_source_object_id=isnothing(binding.geometry_source_object_id) ? - nothing : binding.geometry_source_object_id.value, - geometry_source=binding.geometry_source, - support=binding.support, - config=binding.config, - ) - for binding in compiled.bindings - ] -end - -function explain_environment_bindings(scene::Scene) - return explain_environment_bindings(refresh_environment_bindings!(scene)) -end - -struct SceneOutputRetentionPlan - retain_all::Bool - temporal_dependencies::Set{Tuple{Symbol,Symbol}} - requested_outputs::Set{Tuple{Symbol,Symbol}} - dependency_horizons::Dict{Tuple{Symbol,Symbol},Float64} -end - -struct SceneRunContext{CS,EB,A,TS,OR,C} - compiled::CS - environment_bindings::EB - application::A - object_id::ObjectId - temporal_streams::TS - output_retention::OR - time::Float64 - constants::C -end - -struct SceneCallTarget{CS,EB,A,S,TS,OR,C} - compiled::CS - environment_bindings::EB - application::A - object_id::ObjectId - model - status::S - temporal_streams::TS - output_retention::OR - time::Float64 - constants::C -end - -abstract type AbstractSceneExecutionBatch end - -struct CompiledSceneExecutionTarget{M,S,MB,IB,EB} - object_id::ObjectId - model::M - status::S - models::MB - input_bindings::IB - environment_binding::EB -end - -struct CompiledSceneExecutionBatch{A,T<:AbstractVector} <: AbstractSceneExecutionBatch - application::A - targets::T -end - -struct CompiledSceneExecutionPlan{B} - batches::B - scene_revision::Int - environment_revision::Int -end - -struct SceneSimulation{S,CS,EB,EP,OR,TS,R} - scene::S - compiled::CS - environment_bindings::EB - execution_plan::EP - output_retention::OR - temporal_streams::TS - output_requests::R -end - -_runtime_scene(context::SceneRunContext) = context.compiled.scene -_runtime_scene(simulation::SceneSimulation) = simulation.scene - -scene_outputs(sim::SceneSimulation) = sim.temporal_streams - -function _compiled_application_by_id(compiled::CompiledScene, id::Symbol) - application = get(compiled.applications_by_id, id, nothing) - isnothing(application) && error("No compiled scene application with id `$(id)`.") - return application -end - -function _environment_binding_for(env_bindings::CompiledEnvironmentBindings, application_id::Symbol, object_id::ObjectId) - return get(env_bindings.by_target, (application_id, object_id), nothing) -end - -function _scene_object_status(scene::Scene, object_id::ObjectId) - object = _scene_object(scene, object_id) - object.status isa Status || error( - "Scene object `$(object_id.value)` has no `Status`; scene runtime requires status-backed objects." - ) - return object.status -end - -function _set_status_if_present!(status::Status, variable::Symbol, value) - variable in propertynames(status) || error( - "Cannot materialize input `$(variable)` because consumer status has no such variable." - ) - status[variable] = value - return status -end - -_scene_stream_key(application_id::Symbol, object_id::ObjectId, variable::Symbol) = - (application_id, object_id, variable) - -function _scene_retain_output( - retention::SceneOutputRetentionPlan, - application_id::Symbol, - variable::Symbol, -) - retention.retain_all && return true - key = (application_id, variable) - return key in retention.temporal_dependencies || - key in retention.requested_outputs -end - -_scene_retain_output(::Nothing, application_id::Symbol, variable::Symbol) = true - -function _scene_prune_dependency_stream!( - samples, - retention::SceneOutputRetentionPlan, - application_id::Symbol, - variable::Symbol, - time::Real, -) - retention.retain_all && return samples - key = (application_id, variable) - key in retention.requested_outputs && return samples - key in retention.temporal_dependencies || return samples - horizon = get(retention.dependency_horizons, key, 0.0) - if horizon <= 0.0 - length(samples) > 1 && deleteat!(samples, 1:(length(samples) - 1)) - return samples - end - cutoff = float(time) - horizon + 1.0 - filter!(sample -> sample[1] >= cutoff - 1.0e-8, samples) - return samples -end - -_scene_prune_dependency_stream!(samples, ::Nothing, application_id, variable, time) = - samples - -function _scene_publish_outputs!( - streams, - application::CompiledSceneApplication, - object_id::ObjectId, - status, - time::Real, - retention=nothing, -) - isnothing(streams) && return nothing - for variable in keys(outputs_(application.spec)) - var = Symbol(variable) - _scene_retain_output(retention, application.id, var) || continue - hasproperty(status, var) || error( - "Application `$(application.id)` declares output `$(var)`, but object ", - "`$(object_id.value)` status has no such variable." - ) - key = _scene_stream_key(application.id, object_id, var) - value = getproperty(status, var) - samples = get(streams, key, nothing) - if isnothing(samples) - samples = Tuple{Float64,typeof(value)}[] - streams[key] = samples - elseif !(value isa fieldtype(eltype(samples), 2)) - error( - "Output `$(application.id).$(var)` on object `$(object_id.value)` changed ", - "value type from `$(fieldtype(eltype(samples), 2))` to `$(typeof(value))`. ", - "Scene temporal streams require a stable output type." - ) - end - filter!(sample -> !isapprox(sample[1], float(time); atol=1.0e-8, rtol=0.0), samples) - push!(samples, (float(time), value)) - _scene_prune_dependency_stream!( - samples, - retention, - application.id, - var, - time, - ) - end - return nothing -end - -function _scene_latest_sample(samples, time::Real) - latest = nothing - latest_t = -Inf - for (sample_t, value) in samples - sample_t <= float(time) || continue - sample_t >= latest_t || continue - latest = value - latest_t = sample_t - end - return latest -end - -function _scene_linear_value(v_left, v_right, α) - applicable(-, v_right, v_left) || return nothing - delta = v_right - v_left - increment = if applicable(*, α, delta) - α * delta - elseif applicable(*, delta, α) - delta * α - else - return nothing - end - applicable(+, v_left, increment) || return nothing - return v_left + increment -end - -function _scene_interpolated_sample(samples, time::Real, policy::Interpolate) - isempty(samples) && return nothing - t = float(time) - prev_idx = findlast(sample -> sample[1] <= t + 1.0e-8, samples) - next_idx = findfirst(sample -> sample[1] >= t - 1.0e-8, samples) - - if !isnothing(prev_idx) && !isnothing(next_idx) - t_prev, v_prev = samples[prev_idx] - t_next, v_next = samples[next_idx] - isapprox(t_prev, t_next; atol=1.0e-8, rtol=0.0) && return v_prev - policy.mode == :hold && return v_prev - α = (t - t_prev) / (t_next - t_prev) - interpolated = _scene_linear_value(v_prev, v_next, α) - return isnothing(interpolated) ? v_prev : interpolated - end - - if !isnothing(prev_idx) - t_last, v_last = samples[prev_idx] - if policy.extrapolation == :linear && prev_idx >= 2 - t_prev, v_prev = samples[prev_idx - 1] - if !isapprox(t_last, t_prev; atol=1.0e-8, rtol=0.0) - α = (t - t_last) / (t_last - t_prev) - extrapolated = _scene_linear_value(v_prev, v_last, one(α) + α) - !isnothing(extrapolated) && return extrapolated - end - end - return v_last - end - - return first(samples)[2] -end - -function _scene_window_samples(samples, t_start::Real, t_end::Real) - return [value for (sample_t, value) in samples if float(t_start) <= sample_t <= float(t_end)] -end - -function _scene_window_reduce(values, durations, policy) - isempty(values) && return 0.0 - reducer = policy isa Integrate ? policy.reducer : (policy isa Aggregate ? policy.reducer : PlantMeteo.SumReducer()) - f = _normalize_policy_reducer(reducer) - applicable(f, values, durations) && return f(values, durations) - applicable(f, values) && return f(values) - reducer isa PlantMeteo.SumReducer && return sum(values) - reducer isa PlantMeteo.MeanReducer && return Statistics.mean(values) - reducer isa PlantMeteo.MinReducer && return minimum(values) - reducer isa PlantMeteo.MaxReducer && return maximum(values) - reducer isa PlantMeteo.FirstReducer && return first(values) - reducer isa PlantMeteo.LastReducer && return last(values) - error( - "Reducer `$(reducer)` is not callable on scene temporal input values for policy ", - "`$(typeof(policy))`. Expected `(values)` or `(values, durations_seconds)`." - ) -end - -function _scene_duration_steps(duration, timeline) - if duration isa Dates.Period || duration isa Dates.CompoundPeriod || duration isa Real - seconds = _duration_to_seconds(duration) - isnothing(seconds) && return nothing - steps = seconds / timeline.base_step_seconds - steps >= 1.0 || error( - "Input window `$(duration)` is shorter than the scene base step ", - "($(timeline.base_step_seconds) seconds)." - ) - return steps - end - return nothing -end - -function _scene_input_window_steps(binding::CompiledSceneInputBinding, application::CompiledSceneApplication, timeline) - explicit = _scene_duration_steps(binding.window, timeline) - !isnothing(explicit) && return explicit - binding.policy isa Union{Integrate,Aggregate} && return float(application.clock.dt) - return 1.0 -end - -function _scene_temporal_source_value( - streams, - application_id::Symbol, - source_id::ObjectId, - source_var::Symbol, - time::Real, - policy, - t_start::Real, - timeline, -) - samples = get(streams, _scene_stream_key(application_id, source_id, source_var), nothing) - isnothing(samples) && return policy isa Union{Integrate,Aggregate} ? 0.0 : nothing - if policy isa HoldLast - return _scene_latest_sample(samples, time) - elseif policy isa PreviousTimeStep - return _scene_latest_sample(samples, float(time) - 1.0) - elseif policy isa Union{Integrate,Aggregate} - values = _scene_window_samples(samples, t_start, time) - durations = fill(timeline.base_step_seconds, length(values)) - return _scene_window_reduce(values, durations, policy) - elseif policy isa Interpolate - return _scene_interpolated_sample(samples, time, policy) - end - error("Unsupported scene temporal input policy `$(typeof(policy))`.") -end - -function _scene_temporal_source_value( - streams, - application_id::Nothing, - source_id::ObjectId, - source_var::Symbol, - time::Real, - policy, - t_start::Real, - timeline, -) - policy isa PreviousTimeStep && return nothing - error( - "Temporal scene input from `$(source_id.value).$(source_var)` has no ", - "source application for policy `$(typeof(policy))`." - ) -end - -function _scene_temporal_source_application(compiled::CompiledScene, binding::CompiledSceneInputBinding, source_id::ObjectId) - if binding.policy isa PreviousTimeStep && isempty(binding.source_application_ids) - return nothing - end - isempty(binding.source_application_ids) && error( - "Temporal scene input `$(binding.input)` from `$(source_id.value).$(binding.source_var)` ", - "has no resolved source application. Add `process=` or `application=` to `Inputs(...)`." - ) - matches = Symbol[ - application_id for application_id in binding.source_application_ids - if source_id in _compiled_application_by_id(compiled, application_id).target_ids - ] - if length(matches) == 1 - return only(matches) - elseif isempty(matches) - binding.policy isa PreviousTimeStep && return nothing - error( - "Temporal scene input `$(binding.input)` from `$(source_id.value).$(binding.source_var)` ", - "has no source application matching object `$(source_id.value)`." - ) - end - if binding.policy isa PreviousTimeStep - positions = Dict(application_id => index for (index, application_id) in pairs(compiled.application_order)) - return last(sort(matches; by=application_id -> get(positions, application_id, 0))) - end - error( - "Temporal scene input `$(binding.input)` from `$(source_id.value).$(binding.source_var)` ", - "has ambiguous source applications `$(matches)`. Add `application=...` to `Inputs(...)`." - ) -end - -function _scene_temporal_input_value( - compiled::CompiledScene, - binding::CompiledSceneInputBinding, - application::CompiledSceneApplication, - status::Status, - streams, - time::Real, - timeline, -) - window_steps = _scene_input_window_steps(binding, application, timeline) - t_start = float(time) - float(window_steps) + 1.0 - initial = _input_value(binding.carrier) - isnothing(initial) && (initial = status[binding.input]) - if binding.multiplicity == :many - values = [ - _scene_temporal_source_value( - streams, - _scene_temporal_source_application(compiled, binding, source_id), - source_id, - binding.source_var, - time, - binding.policy, - t_start, - timeline, - ) - for source_id in binding.source_ids - ] - if binding.policy isa PreviousTimeStep - initial_values = initial isa AbstractVector ? initial : () - return [ - isnothing(value) && index <= length(initial_values) ? initial_values[index] : value - for (index, value) in pairs(values) - ] - end - return values - end - source_id = only(binding.source_ids) - value = _scene_temporal_source_value( - streams, - _scene_temporal_source_application(compiled, binding, source_id), - source_id, - binding.source_var, - time, - binding.policy, - t_start, - timeline, - ) - isnothing(value) && binding.policy isa PreviousTimeStep && return initial - isnothing(value) && error( - "No temporal scene value available for input `$(binding.input)` from ", - "`$(source_id.value).$(binding.source_var)` at t=$(time)." - ) - return value -end - -function _scene_temporal_input_value( - compiled::CompiledScene, - binding::CompiledSceneInputBinding, - application::CompiledSceneApplication, - streams, - time::Real, - timeline, -) - status = _scene_object_status(compiled.scene, binding.consumer_id) - return _scene_temporal_input_value( - compiled, - binding, - application, - status, - streams, - time, - timeline, - ) -end - -function _scene_assign_input_value!(status::Status, variable::Symbol, value) - variable in propertynames(status) || error( - "Cannot materialize input `$(variable)` because consumer status has no such variable." - ) - current = status[variable] - if current isa RefVector && value isa AbstractVector - length(current) != length(value) && resize!(current, length(value)) - for i in eachindex(value) - current[i] = value[i] - end - return status - end - status[variable] = value - return status -end - -function _materialize_scene_inputs!( - compiled::CompiledScene, - application::CompiledSceneApplication, - object_id::ObjectId, - streams=nothing, - time::Real=1, -) - status = _scene_object_status(compiled.scene, object_id) - bindings = get(compiled.input_bindings_by_target, (application.id, object_id), ()) - timeline = nothing - for binding in bindings - if binding.carrier_hint == :temporal_stream - isnothing(streams) && continue - isnothing(timeline) && (timeline = _scene_timeline(compiled.scene)) - value = _scene_temporal_input_value( - compiled, - binding, - application, - status, - streams, - time, - timeline, - ) - _scene_assign_input_value!(status, binding.input, value) - end - end - return status -end - -function _materialize_scene_inputs!( - status::Status, - bindings::Tuple, - compiled::CompiledScene, - application::CompiledSceneApplication, - streams=nothing, - time::Real=1, -) - timeline = nothing - for binding in bindings - if binding.carrier_hint == :temporal_stream - isnothing(streams) && continue - isnothing(timeline) && (timeline = _scene_timeline(compiled.scene)) - value = _scene_temporal_input_value( - compiled, - binding, - application, - status, - streams, - time, - timeline, - ) - _scene_assign_input_value!(status, binding.input, value) - end - end - return status -end - -function _scene_meteo_for_model( - env_bindings::CompiledEnvironmentBindings, - application::CompiledSceneApplication, - object_id::ObjectId, - time::Real, -) - binding = _environment_binding_for(env_bindings, application.id, object_id) - return _scene_meteo_for_binding(env_bindings, application, binding, time) -end - -function _scene_meteo_for_binding( - env_bindings::CompiledEnvironmentBindings, - application::CompiledSceneApplication, - binding, - time::Real, -) - isnothing(binding) && return nothing - isnothing(binding.backend) && return nothing - if binding.backend isa GlobalConstant - sampler = get(env_bindings.samplers_by_application, application.id, nothing) - if !isnothing(sampler) - step = Int(round(time)) - key = (application.id, step) - haskey(env_bindings.sample_cache, key) && - return env_bindings.sample_cache[key] - meteo = environment_meteo(binding.backend) - raw_row = _meteo_row_at_step(meteo, step) - sampled = _sample_meteo_for_model( - sampler, - raw_row, - step, - application.clock, - application.spec, - ) - env_bindings.sample_cache[key] = sampled - return sampled - end - end - return sample_environment(binding.backend, binding.support, time, application.spec) -end - -function _scatter_scene_environment_outputs!( - env_bindings::CompiledEnvironmentBindings, - application::CompiledSceneApplication, - object_id::ObjectId, - status, - time::Real, -) - isempty(keys(meteo_outputs_(application.spec))) && return nothing - binding = _environment_binding_for(env_bindings, application.id, object_id) - return _scatter_scene_environment_outputs!( - application, - binding, - status, - time, - ) -end - -function _scatter_scene_environment_outputs!( - application::CompiledSceneApplication, - binding, - status, - time::Real, -) - isempty(keys(meteo_outputs_(application.spec))) && return nothing - isnothing(binding) && return nothing - isnothing(binding.backend) && return nothing - return scatter_environment_outputs!(binding.backend, binding.support, time, application.spec, status) -end - -function _push_scene_model_entry!(pairs, names::Set{Symbol}, name::Symbol, model) - name in names && return nothing - push!(pairs, name => model) - push!(names, name) - return nothing -end - -function _append_scene_model_dependencies!( - pairs, - names::Set{Symbol}, - applications_by_id, - call_bindings_by_target, - application::CompiledSceneApplication, - object_id::ObjectId, - seen::Set{Tuple{Symbol,ObjectId}}, -) - key = (application.id, object_id) - key in seen && return nothing - push!(seen, key) - _push_scene_model_entry!( - pairs, - names, - application.process, - _application_model(application, object_id), - ) - bindings = get(call_bindings_by_target, key, ()) - for binding in bindings - for application_id in binding.callee_application_ids - callee_application = get(applications_by_id, application_id, nothing) - isnothing(callee_application) && error( - "No compiled scene application with id `$(application_id)`." - ) - matching_object_ids = ObjectId[ - callee_object_id for callee_object_id in binding.callee_object_ids - if callee_object_id in callee_application.target_ids - ] - # Old-style hard-dependency kernels expect one model object per - # process field. Multi-object scene calls are exposed through - # `call_targets(extra, name)` instead. - length(matching_object_ids) == 1 || continue - _append_scene_model_dependencies!( - pairs, - names, - applications_by_id, - call_bindings_by_target, - callee_application, - only(matching_object_ids), - seen, - ) - end - end - return nothing -end - -function _compile_scene_model_bundle( - applications_by_id, - call_bindings_by_target, - application::CompiledSceneApplication, - object_id::ObjectId, -) - pairs = Pair{Symbol,Any}[] - names = Set{Symbol}() - seen = Set{Tuple{Symbol,ObjectId}}() - _append_scene_model_dependencies!( - pairs, - names, - applications_by_id, - call_bindings_by_target, - application, - object_id, - seen, - ) - return NamedTuple{Tuple(first(pair) for pair in pairs)}(Tuple(last(pair) for pair in pairs)) -end - -function _compile_scene_model_bundles(applications, applications_by_id, call_bindings_by_target) - bundles = Dict{Tuple{Symbol,ObjectId},Any}() - for application in applications - for object_id in application.target_ids - bundles[(application.id, object_id)] = _compile_scene_model_bundle( - applications_by_id, - call_bindings_by_target, - application, - object_id, - ) - end - end - return bundles -end - -function _scene_models_for_application( - compiled::CompiledScene, - application::CompiledSceneApplication, - object_id::ObjectId, -) - key = (application.id, object_id) - models = get(compiled.model_bundles_by_target, key, nothing) - isnothing(models) && error( - "No compiled model bundle for application `$(application.id)` on object `$(object_id.value)`." - ) - return models -end - -function _run_scene_application!( - compiled::CompiledScene, - env_bindings::CompiledEnvironmentBindings, - application::CompiledSceneApplication, - object_id::ObjectId; - time::Real=1, - constants=nothing, - temporal_streams=nothing, - output_retention=nothing, - publish::Bool=true, - meteo=nothing, -) - status = _materialize_scene_inputs!(compiled, application, object_id, temporal_streams, time) - meteo_value = isnothing(meteo) ? _scene_meteo_for_model(env_bindings, application, object_id, time) : meteo - context = SceneRunContext( - compiled, - env_bindings, - application, - object_id, - temporal_streams, - output_retention, - float(time), - constants, - ) - model = _application_model(application, object_id) - models = _scene_models_for_application(compiled, application, object_id) - run!(model, models, status, meteo_value, constants, context) - if publish - _scatter_scene_environment_outputs!(env_bindings, application, object_id, status, time) - _scene_publish_outputs!( - temporal_streams, - application, - object_id, - status, - time, - output_retention, - ) - end - return status -end - -function _run_scene_execution_target!( - compiled::CompiledScene, - env_bindings::CompiledEnvironmentBindings, - application::CompiledSceneApplication, - target::CompiledSceneExecutionTarget; - time::Real=1, - constants=nothing, - temporal_streams=nothing, - output_retention=nothing, -) - status = _materialize_scene_inputs!( - target.status, - target.input_bindings, - compiled, - application, - temporal_streams, - time, - ) - meteo = _scene_meteo_for_binding( - env_bindings, - application, - target.environment_binding, - time, - ) - context = SceneRunContext( - compiled, - env_bindings, - application, - target.object_id, - temporal_streams, - output_retention, - float(time), - constants, - ) - run!(target.model, target.models, status, meteo, constants, context) - _scatter_scene_environment_outputs!( - application, - target.environment_binding, - status, - time, - ) - _scene_publish_outputs!( - temporal_streams, - application, - target.object_id, - status, - time, - output_retention, - ) - return status -end - -function _run_scene_execution_batch!( - batch::CompiledSceneExecutionBatch, - compiled::CompiledScene, - env_bindings::CompiledEnvironmentBindings; - time::Real=1, - constants=nothing, - temporal_streams=nothing, - output_retention=nothing, -) - _scene_application_should_run(batch.application, time) || return nothing - for target in batch.targets - _run_scene_execution_target!( - compiled, - env_bindings, - batch.application, - target; - time=time, - constants=constants, - temporal_streams=temporal_streams, - output_retention=output_retention, - ) - end - return nothing -end - -_scene_application_should_run(application::CompiledSceneApplication, t::Real) = - _should_run_at_time(application.clock, float(t)) - -function _manual_call_application_ids(compiled::CompiledScene) - ids = Set{Symbol}() - for binding in compiled.call_bindings - union!(ids, binding.callee_application_ids) - end - return ids -end - -function _compiled_scene_execution_target( - compiled::CompiledScene, - env_bindings::CompiledEnvironmentBindings, - application::CompiledSceneApplication, - object_id::ObjectId, -) - status = _scene_object_status(compiled.scene, object_id) - model = _application_model(application, object_id) - models = _scene_models_for_application(compiled, application, object_id) - input_bindings = get( - compiled.input_bindings_by_target, - (application.id, object_id), - (), - ) - environment_binding = _environment_binding_for( - env_bindings, - application.id, - object_id, - ) - return CompiledSceneExecutionTarget( - object_id, - model, - status, - models, - input_bindings, - environment_binding, - ) -end - -function _typed_scene_execution_targets(targets, first_index::Int, last_index::Int) - target_type = typeof(targets[first_index]) - typed = Vector{target_type}(undef, last_index - first_index + 1) - for (destination, source) in enumerate(first_index:last_index) - typed[destination] = targets[source] - end - return typed -end - -function _append_scene_execution_batches!( - batches, - application::CompiledSceneApplication, - targets, -) - isempty(targets) && return batches - first_index = firstindex(targets) - target_type = typeof(targets[first_index]) - for index in (first_index + 1):lastindex(targets) - typeof(targets[index]) == target_type && continue - push!( - batches, - CompiledSceneExecutionBatch( - application, - _typed_scene_execution_targets(targets, first_index, index - 1), - ), - ) - first_index = index - target_type = typeof(targets[index]) - end - push!( - batches, - CompiledSceneExecutionBatch( - application, - _typed_scene_execution_targets(targets, first_index, lastindex(targets)), - ), - ) - return batches -end - -function compile_scene_execution_plan( - compiled::CompiledScene, - env_bindings::CompiledEnvironmentBindings, -) - manual_application_ids = _manual_call_application_ids(compiled) - batches = AbstractSceneExecutionBatch[] - for application in _ordered_scene_applications(compiled) - application.id in manual_application_ids && continue - targets = Any[ - _compiled_scene_execution_target( - compiled, - env_bindings, - application, - object_id, - ) - for object_id in application.target_ids - ] - _append_scene_execution_batches!(batches, application, targets) - end - return CompiledSceneExecutionPlan( - batches, - compiled.revision, - env_bindings.environment_revision, - ) -end - -function explain_execution_plan(plan::CompiledSceneExecutionPlan) - return [ - ( - batch_index=index, - application_id=batch.application.id, - process=batch.application.process, - object_ids=[target.object_id.value for target in batch.targets], - batch_size=length(batch.targets), - target_type=eltype(batch.targets), - model_type=fieldtype(eltype(batch.targets), :model), - status_type=fieldtype(eltype(batch.targets), :status), - model_bundle_type=fieldtype(eltype(batch.targets), :models), - input_bindings_type=fieldtype(eltype(batch.targets), :input_bindings), - environment_binding_type=fieldtype( - eltype(batch.targets), - :environment_binding, - ), - inner_loop_dispatch=:concrete_homogeneous_batch, - ) - for (index, batch) in pairs(plan.batches) - ] -end - -function explain_execution_plan(sim::SceneSimulation) - return explain_execution_plan(sim.execution_plan) -end - -function explain_execution_plan(scene::Scene) - compiled = refresh_bindings!(scene) - environment_bindings = refresh_environment_bindings!(scene, compiled) - return explain_execution_plan( - compile_scene_execution_plan(compiled, environment_bindings), - ) -end - -function compile_scene_output_retention( - compiled::CompiledScene, - output_requests; - retain_all::Bool=false, -) - temporal_dependencies = Set{Tuple{Symbol,Symbol}}() - dependency_horizons = Dict{Tuple{Symbol,Symbol},Float64}() - timeline = _scene_timeline(compiled.scene) - for binding in compiled.input_bindings - binding.carrier_hint == :temporal_stream || continue - consumer = _compiled_application_by_id(compiled, binding.application_id) - window_steps = _scene_input_window_steps(binding, consumer, timeline) - for application_id in binding.source_application_ids - source = _compiled_application_by_id(compiled, application_id) - required = if binding.policy isa Union{Integrate,Aggregate} - float(window_steps) - elseif binding.policy isa Union{Interpolate,PreviousTimeStep} - max(2.0, float(source.clock.dt) + 1.0) - else - 0.0 - end - key = (application_id, binding.source_var) - push!( - temporal_dependencies, - key, - ) - dependency_horizons[key] = max( - get(dependency_horizons, key, 0.0), - required, - ) - end - end - - requested_outputs = Set{Tuple{Symbol,Symbol}}() - names = Set{Symbol}() - for request in output_requests - request.name in names && error( - "Duplicate output request name `$(request.name)`. Request names must be unique." - ) - push!(names, request.name) - application = _scene_request_application( - compiled.scene, - compiled, - request, - ) - push!(requested_outputs, (application.id, request.var)) - end - return SceneOutputRetentionPlan( - retain_all, - temporal_dependencies, - requested_outputs, - dependency_horizons, - ) -end - -function explain_output_retention(sim::SceneSimulation) - plan = sim.output_retention - keys_to_explain = if plan.retain_all - Set( - (application.id, Symbol(variable)) - for application in sim.compiled.applications - for variable in keys(outputs_(application.spec)) - ) - else - union(plan.temporal_dependencies, plan.requested_outputs) - end - return [ - ( - application_id=application_id, - variable=variable, - reasons=plan.retain_all ? - (:default_all,) : - Tuple( - reason for (reason, keys) in ( - :temporal_dependency => plan.temporal_dependencies, - :output_request => plan.requested_outputs, - ) - if (application_id, variable) in keys - ), - retention_steps=plan.retain_all || - (application_id, variable) in plan.requested_outputs ? - nothing : - get( - plan.dependency_horizons, - (application_id, variable), - 0.0, - ), - current_target_count=length( - _compiled_application_by_id( - sim.compiled, - application_id, - ).target_ids, - ), - ) - for (application_id, variable) in sort!( - collect(keys_to_explain); - by=key -> (string(first(key)), string(last(key))), - ) - ] -end - -function _scene_call_targets(context::SceneRunContext, name::Symbol) - targets = SceneCallTarget[] - bindings = get( - context.compiled.call_bindings_by_target, - (context.application.id, context.object_id), - (), - ) - for binding in bindings - binding.call == name || continue - for application_id in binding.callee_application_ids - callee_application = _compiled_application_by_id(context.compiled, application_id) - for object_id in binding.callee_object_ids - object_id in callee_application.target_ids || continue - status = _scene_object_status(context.compiled.scene, object_id) - push!( - targets, - SceneCallTarget( - context.compiled, - context.environment_bindings, - callee_application, - object_id, - _application_model(callee_application, object_id), - status, - context.temporal_streams, - context.output_retention, - context.time, - context.constants, - ), - ) - end - end - end - return targets -end - -""" - call_targets(context::SceneRunContext, name) - call_target(context::SceneRunContext, name) - -Return manually executable targets declared with `Calls(...)` for the current -scene/object model call. `call_target` requires exactly one matching target. -Execute returned targets with [`run_call!`](@ref). -""" -call_targets(context::SceneRunContext, name::Symbol) = _scene_call_targets(context, name) -call_targets(context::SceneRunContext, name::AbstractString) = - call_targets(context, Symbol(name)) -call_target(context::SceneRunContext, name::Symbol) = only(call_targets(context, name)) -call_target(context::SceneRunContext, name::AbstractString) = - call_target(context, Symbol(name)) - -""" - run_call!(target::SceneCallTarget; publish=false, meteo=nothing) - -Run a manually selected model call. By default, the call mutates its target -status without publishing outputs or environment updates, which is suitable -for trial iterations. Pass `publish=true` once for the accepted state. -""" -function run_call!(target::SceneCallTarget; publish::Bool=false, meteo=nothing) - _run_scene_application!( - target.compiled, - target.environment_bindings, - target.application, - target.object_id; - time=target.time, - constants=target.constants, - temporal_streams=target.temporal_streams, - output_retention=target.output_retention, - publish=publish, - meteo=meteo, - ) - return target -end - -function run!( - scene::Scene; - steps::Integer=1, - constants=PlantMeteo.Constants(), - tracked_outputs=nothing, -) - compiled = refresh_bindings!(scene) - env_bindings = refresh_environment_bindings!(scene, compiled) - empty!(env_bindings.sample_cache) - execution_plan = compile_scene_execution_plan(compiled, env_bindings) - output_requests = _normalize_output_requests(tracked_outputs) - output_retention = compile_scene_output_retention( - compiled, - output_requests; - retain_all=isnothing(tracked_outputs), - ) - temporal_streams = Dict{Tuple{Symbol,ObjectId,Symbol},Any}() - for step in 1:steps - if bindings_dirty(scene) - compiled = refresh_bindings!(scene) - env_bindings = refresh_environment_bindings!(scene, compiled) - execution_plan = compile_scene_execution_plan(compiled, env_bindings) - output_retention = compile_scene_output_retention( - compiled, - output_requests; - retain_all=isnothing(tracked_outputs), - ) - elseif environment_bindings_dirty(scene) - env_bindings = refresh_environment_bindings!(scene, compiled) - execution_plan = compile_scene_execution_plan(compiled, env_bindings) - end - for batch in execution_plan.batches - _run_scene_execution_batch!( - batch, - compiled, - env_bindings; - time=step, - constants=constants, - temporal_streams=temporal_streams, - output_retention=output_retention, - ) - end - end - if bindings_dirty(scene) - compiled = refresh_bindings!(scene) - env_bindings = refresh_environment_bindings!(scene, compiled) - execution_plan = compile_scene_execution_plan(compiled, env_bindings) - output_retention = compile_scene_output_retention( - compiled, - output_requests; - retain_all=isnothing(tracked_outputs), - ) - elseif environment_bindings_dirty(scene) - env_bindings = refresh_environment_bindings!(scene, compiled) - execution_plan = compile_scene_execution_plan(compiled, env_bindings) - end - return SceneSimulation( - scene, - compiled, - env_bindings, - execution_plan, - output_retention, - temporal_streams, - output_requests, - ) -end - -function _scene_output_rows(sim::SceneSimulation, filter_object=nothing, filter_var=nothing) - rows = NamedTuple[] - for ((application_id, object_id, variable), samples) in sort!( - collect(sim.temporal_streams); - by=pair -> (string(first(pair)[1]), string(first(pair)[2].value), string(first(pair)[3])), - ) - isnothing(filter_object) || object_id == ObjectId(filter_object) || continue - isnothing(filter_var) || variable == Symbol(filter_var) || continue - for (time, value) in samples - push!( - rows, - ( - timestep=Int(round(time)), - time=time, - application_id=application_id, - object_id=object_id.value, - variable=variable, - value=value, - ), - ) - end - end - sort!(rows; by=row -> (row.timestep, string(row.object_id), string(row.variable))) - return rows -end - -function _materialize_scene_output_rows(rows, sink) - isnothing(sink) && return rows - sink === DataFrames.DataFrame && return DataFrames.DataFrame(rows) - return sink(rows) -end - -function _scene_request_application(scene::Scene, compiled::CompiledScene, request) - candidates = CompiledSceneApplication[] - for application in compiled.applications - request.var in keys(outputs_(application.spec)) || continue - isnothing(request.process) || application.process == request.process || continue - isnothing(request.application) || - application.id == request.application || - application.name == request.application || - continue - _scene_application_matches_scale(scene, application, request.scale) || continue - if isnothing(request.process) && - isnothing(request.application) && - _publish_mode_for_output(application.spec, request.var) == :stream_only - continue - end - push!(candidates, application) - end - if isempty(candidates) - error( - "No scene output publisher found for `$(request.scale).$(request.var)`", - isnothing(request.application) ? - (isnothing(request.process) ? "." : " from process `$(request.process)`.") : - " from application `$(request.application)`.", - ) - elseif length(candidates) > 1 - error( - "Ambiguous scene output publishers for `$(request.scale).$(request.var)`: ", - join((application.id for application in candidates), ", "), - ". Provide `application=` or make one publisher canonical.", - ) - end - return only(candidates) -end - -function _scene_request_application(sim::SceneSimulation, request) - return _scene_request_application(sim.scene, sim.compiled, request) -end - -function _selector_declared_scale(selector::AbstractObjectMultiplicity) - selector_criteria = criteria(selector) - scale = _criteria_get(selector_criteria, :scale, nothing) - !isnothing(scale) && return scale - for positional_selector in _criteria_get(selector_criteria, :selectors, ()) - positional_selector isa Scale && return positional_selector.scale - end - return nothing -end - -function _scene_application_matches_scale( - scene::Scene, - application::CompiledSceneApplication, - scale::Symbol, -) - declared_scale = _selector_declared_scale(application.applies_to) - declared_scale == scale && return true - for object_id in application.target_ids - object = get(scene.registry.objects, object_id, nothing) - !isnothing(object) && object.scale == scale && return true - end - return false -end - -function _scene_stream_object_matches_scale( - scene::Scene, - application::CompiledSceneApplication, - object_id::ObjectId, - scale::Symbol, -) - object = get(scene.registry.objects, object_id, nothing) - !isnothing(object) && return object.scale == scale - declared_scale = _selector_declared_scale(application.applies_to) - return isnothing(declared_scale) || declared_scale == scale -end - -function _scene_request_clock(request, timeline) - isnothing(request.clock) && return ClockSpec(1.0, 0.0) - clock = _clock_from_spec_timestep(request.clock, timeline) - isnothing(clock) && error( - "Unsupported clock specification `$(typeof(request.clock))` in ", - "OutputRequest `$(request.name)`.", - ) - return clock -end - -function _scene_requested_value(samples, time, t_start, policy, timeline) - if policy isa HoldLast - value = _scene_latest_sample(samples, time) - return isnothing(value) ? missing : value - elseif policy isa Interpolate - value = _scene_interpolated_sample(samples, time, policy) - return isnothing(value) ? missing : value - elseif policy isa Union{Integrate,Aggregate} - values = _scene_window_samples(samples, t_start, time) - isempty(values) && return missing - durations = fill(timeline.base_step_seconds, length(values)) - return _scene_window_reduce(values, durations, policy) - end - error("Unsupported scene output request policy `$(typeof(policy))`.") -end - -function _scene_requested_output_rows( - sim::SceneSimulation, - request, -) - application = _scene_request_application(sim, request) - timeline = _scene_timeline(sim.scene) - clock = _scene_request_clock(request, timeline) - source_rows = [ - (object_id=object_id, samples=samples) - for ((application_id, object_id, variable), samples) in sim.temporal_streams - if application_id == application.id && - variable == request.var && - _scene_stream_object_matches_scale(sim.scene, application, object_id, request.scale) - ] - isempty(source_rows) && return NamedTuple[] - nonempty_source_rows = [row for row in source_rows if !isempty(row.samples)] - isempty(nonempty_source_rows) && return NamedTuple[] - max_time = maximum(last(row.samples)[1] for row in nonempty_source_rows) - rows = NamedTuple[] - for time in 1:Int(floor(max_time)) - _should_run_at_time(clock, float(time)) || continue - t_start = float(time) - float(clock.dt) + 1.0 - for row in sort!(nonempty_source_rows; by=row -> string(row.object_id.value)) - first_sample_time = first(row.samples)[1] - last_sample_time = last(row.samples)[1] - first_sample_time <= float(time) <= last_sample_time || continue - push!( - rows, - ( - timestep=time, - time=float(time), - scale=request.scale, - process=application.process, - application_id=application.id, - variable=request.var, - object_id=row.object_id.value, - value=_scene_requested_value( - row.samples, - float(time), - t_start, - request.policy, - timeline, - ), - ), - ) - end - end - return rows -end - -function _collect_scene_requested_outputs(sim::SceneSimulation, sink) - outputs = Dict{Symbol,Any}() - for request in sim.output_requests - haskey(outputs, request.name) && error( - "Duplicate output request name `$(request.name)`. Request names must be unique." - ) - outputs[request.name] = _materialize_scene_output_rows( - _scene_requested_output_rows(sim, request), - sink, - ) - end - return outputs -end - -function collect_outputs(sim::SceneSimulation; sink=DataFrames.DataFrame) - isempty(sim.output_requests) || return _collect_scene_requested_outputs(sim, sink) - return _materialize_scene_output_rows(_scene_output_rows(sim), sink) -end - -function collect_outputs(sim::SceneSimulation, name::Symbol; sink=DataFrames.DataFrame) - matches = [request for request in sim.output_requests if request.name == name] - isempty(matches) && error( - "No scene output request named `$(name)`. Available request names are ", - isempty(sim.output_requests) ? "none." : join((request.name for request in sim.output_requests), ", "), - ) - length(matches) == 1 || error( - "Duplicate scene output request name `$(name)`. Request names must be unique." - ) - request = only(matches) - return _materialize_scene_output_rows( - _scene_requested_output_rows(sim, request), - sink, - ) -end - -function collect_outputs(sim::SceneSimulation, object_id, variable::Symbol; sink=DataFrames.DataFrame) - return _materialize_scene_output_rows(_scene_output_rows(sim, object_id, variable), sink) -end - -function explain_outputs(sim::SceneSimulation) - return [ - ( - application_id=application_id, - object_id=object_id.value, - variable=variable, - nsamples=length(samples), - first_time=isempty(samples) ? nothing : first(samples)[1], - last_time=isempty(samples) ? nothing : last(samples)[1], - value_type=isempty(samples) ? Union{} : typeof(last(samples)[2]), - ) - for ((application_id, object_id, variable), samples) in sort!( - collect(sim.temporal_streams); - by=pair -> (string(first(pair)[1]), string(first(pair)[2].value), string(first(pair)[3])), - ) - ] -end - -struct ObjectAddress{SC,K,SP,S,N,P,V,R,M} - scope::SC - kind::K - species::SP - scale::S - name::N - process::P - var::V - relation::R - multiplicity::M -end - -function ObjectAddress(selector::AbstractObjectMultiplicity) - c = criteria(selector) - scope = _criteria_scope(c) - kind = _criteria_value(c, :kind, Kind) - species = _criteria_value(c, :species, Species) - scale = _criteria_value(c, :scale, Scale) - name = haskey(c, :name) ? c.name : nothing - process = haskey(c, :process) ? c.process : nothing - var = haskey(c, :var) ? c.var : nothing - relation = _criteria_value(c, :relation, Relation) - return ObjectAddress(scope, kind, species, scale, name, process, var, relation, multiplicity(selector)) -end - -object_address(selector::AbstractObjectMultiplicity) = ObjectAddress(selector) - -struct Input{S} - selector::S -end -Input(; kwargs...) = Input(One(; kwargs...)) - -struct Call{S} - selector::S -end -Call(; kwargs...) = Call(One(; kwargs...)) - -struct EnvironmentConfig{C} - config::C -end - -_normalize_application_name(name) = isnothing(name) ? nothing : Symbol(name) - -function _normalize_application_bindings(bindings::NamedTuple) - return bindings -end - -function _normalize_application_bindings(bindings::Tuple) - pairs = Pair{Symbol,Any}[] - for binding in bindings - binding isa Pair || error( - "Expected `var => selector` pairs in `Inputs(...)` or `Calls(...)`, got `$(typeof(binding))`." - ) - key = first(binding) - selector = last(binding) - if key isa PreviousTimeStep - selector isa AbstractObjectMultiplicity || error( - "A `PreviousTimeStep(...)` input must map to `One(...)`, ", - "`OptionalOne(...)`, or `Many(...)`." - ) - push!( - pairs, - key.variable => _selector_with_previous_timestep(selector, key), - ) - else - key isa Union{Symbol,AbstractString} || error( - "Binding names in `Inputs(...)` and `Calls(...)` must be symbols, ", - "strings, or `PreviousTimeStep(:input)` markers." - ) - push!(pairs, Symbol(key) => selector) - end - end - return (; pairs...) -end - -function _normalize_application_bindings(binding::Pair) - return _normalize_application_bindings((binding,)) -end - -function _normalize_application_bindings(bindings) - error( - "Unsupported binding declaration `$(bindings)` of type `$(typeof(bindings))`. ", - "Use pairs such as `:x => Many(...)` or keyword arguments." - ) -end - -function _model_default_value_inputs(model) - defaults = Pair{Symbol,Any}[] - for (dep_name, selector) in pairs(dep(model)) - selector isa Input || continue - push!(defaults, Symbol(dep_name) => selector.selector) - end - return (; defaults...) -end - -function _model_default_model_calls(model) - defaults = Pair{Symbol,Any}[] - for (dep_name, selector) in pairs(dep(model)) - selector isa Call || continue - push!(defaults, Symbol(dep_name) => selector.selector) - end - return (; defaults...) -end - -function _merge_value_inputs(defaults::NamedTuple, explicit::NamedTuple) - return (; pairs(defaults)..., pairs(explicit)...) -end - -function _binding_origins(defaults::NamedTuple, explicit::NamedTuple) - origins = Pair{Symbol,Symbol}[] - for name in keys(defaults) - push!(origins, Symbol(name) => :model_default) - end - for name in keys(explicit) - push!(origins, Symbol(name) => :model_spec) - end - return (; origins...) -end - -function _normalize_binding_origins(origins::NamedTuple, bindings::NamedTuple) - normalized = Pair{Symbol,Symbol}[] - for name in keys(bindings) - origin = haskey(origins, name) ? Symbol(getproperty(origins, name)) : :model_spec - origin in (:model_default, :model_spec) || error( - "Unsupported binding origin `$(origin)` for `$(name)`." - ) - push!(normalized, Symbol(name) => origin) - end - return (; normalized...) -end +# The Scene/Object API is one public compiler and runtime. Internal ownership is +# split by dependency direction; these files are not modules and add no public +# abstraction boundary. +include("scene_object/registry_topology.jl") +include("scene_object/selectors.jl") +include("scene_object/compilation.jl") +include("scene_object/environment_bindings.jl") +include("scene_object/runtime_outputs.jl") +include("scene_object/scenario_dsl.jl") diff --git a/src/time/runtime/clocks.jl b/src/time/runtime/clocks.jl index dfe57d6b4..0e43c3f8d 100644 --- a/src/time/runtime/clocks.jl +++ b/src/time/runtime/clocks.jl @@ -149,12 +149,23 @@ function _validate_meteo_duration(meteo) if meteo isa Atmosphere _base_step_seconds_from_meteo_row(meteo; require_duration=true) elseif meteo isa TimeStepTable || DataFormat(meteo) == TableAlike() + base_seconds = nothing for (i, row) in enumerate(Tables.rows(meteo)) - _base_step_seconds_from_meteo_row( + seconds = _base_step_seconds_from_meteo_row( row; require_duration=true, context="meteorology row $(i)", ) + if isnothing(base_seconds) + base_seconds = seconds + elseif !isapprox(seconds, base_seconds; atol=1.0e-9, rtol=0.0) + error( + "Inconsistent `duration` in meteorology row $(i): ", + "$(seconds) seconds does not match the base step ", + "$(base_seconds) seconds from row 1. Scene scheduling ", + "requires a fixed environment base step." + ) + end end elseif DataFormat(meteo) == SingletonAlike() && hasproperty(meteo, :duration) _base_step_seconds_from_meteo_row(meteo; require_duration=true) diff --git a/src/time/runtime/output_export.jl b/src/time/runtime/output_export.jl index fb1f4e8a7..f625cf209 100644 --- a/src/time/runtime/output_export.jl +++ b/src/time/runtime/output_export.jl @@ -1,33 +1,49 @@ """ - OutputRequest(scale, var; name=var, process=nothing, application=nothing, + OutputRequest(selector, var; name=var, application=nothing, context=nothing, policy=HoldLast(), clock=nothing) + OutputRequest(scale, var; kwargs...) Request retention and optional resampling of one scene output stream. + +The first form accepts the same `One`, `OptionalOne`, or `Many` selector used +by `AppliesTo`, `Inputs`, `Calls`, and object queries. Passing a scale is a +convenience for `Many(scale=scale)`. """ -struct OutputRequest{P<:Union{Nothing,Symbol},A<:Union{Nothing,Symbol},POL<:SchedulePolicy,C} - scale::Symbol +struct OutputRequest{S<:AbstractObjectMultiplicity,P<:Union{Nothing,Symbol},A<:Union{Nothing,Symbol},CT,POL<:SchedulePolicy,C} + selector::S var::Symbol name::Symbol process::P application::A + context::CT policy::POL clock::C end function OutputRequest( - scale::Symbol, + selector::AbstractObjectMultiplicity, var::Symbol; name::Symbol=var, process=nothing, application=nothing, + context=nothing, policy::SchedulePolicy=HoldLast(), clock=nothing, ) + if !isnothing(process) + Base.depwarn( + "`process=` in `OutputRequest` is deprecated; name the model application and use `application=`.", + :OutputRequest, + ) + end proc = isnothing(process) ? nothing : Symbol(process) app = isnothing(application) ? nothing : Symbol(application) - return OutputRequest(scale, var, name, proc, app, policy, clock) + return OutputRequest(selector, var, name, proc, app, context, policy, clock) end +OutputRequest(scale::Union{Symbol,AbstractString}, var::Symbol; kwargs...) = + OutputRequest(Many(scale=Symbol(scale)), var; kwargs...) + function _export_clock(request::OutputRequest, timeline::TimelineContext) isnothing(request.clock) && return ClockSpec(1.0, 0.0) clock = _clock_from_spec_timestep(request.clock, timeline) @@ -41,7 +57,7 @@ function _normalize_output_requests(requests) isnothing(requests) && return OutputRequest[] requests isa OutputRequest && return OutputRequest[requests] requests isa AbstractVector{<:OutputRequest} || error( - "`tracked_outputs` must be `nothing`, an `OutputRequest`, or a vector of `OutputRequest`." + "`outputs` must be `:all`, `:none`, an `OutputRequest`, or a vector of `OutputRequest`." ) return collect(requests) end diff --git a/src/visualization/scene_graph_editor_api.jl b/src/visualization/scene_graph_editor_api.jl new file mode 100644 index 000000000..17e2f4534 --- /dev/null +++ b/src/visualization/scene_graph_editor_api.jl @@ -0,0 +1,857 @@ +abstract type AbstractSceneGraphEdit end + +struct AddSceneApplication{S} <: AbstractSceneGraphEdit + spec::S +end + +struct RemoveSceneApplication <: AbstractSceneGraphEdit + application_id::Symbol +end + +struct RemoveSceneTemplateApplication <: AbstractSceneGraphEdit + instance::Symbol + application_id::Symbol +end + +RemoveSceneTemplateApplication(instance, application_id) = + RemoveSceneTemplateApplication(Symbol(instance), Symbol(application_id)) + +struct ReplaceSceneApplicationModel{M<:AbstractModel} <: AbstractSceneGraphEdit + application_id::Symbol + model::M +end + +struct UpdateSceneApplication{M<:AbstractModel,S,T} <: AbstractSceneGraphEdit + application_id::Symbol + model::M + name::Symbol + selector::S + timestep::T +end + +struct UpdateSceneTemplateApplication{M<:AbstractModel,S,T} <: AbstractSceneGraphEdit + instance::Symbol + application_id::Symbol + model::M + selector::S + timestep::T +end + +struct RenameSceneApplication <: AbstractSceneGraphEdit + application_id::Symbol + name::Symbol +end + +struct SetSceneApplicationTargets{S} <: AbstractSceneGraphEdit + application_id::Symbol + selector::S +end + +struct SetSceneInputBinding{S} <: AbstractSceneGraphEdit + application_id::Symbol + input::Symbol + selector::S +end + +struct RemoveSceneInputBinding <: AbstractSceneGraphEdit + application_id::Symbol + input::Symbol +end + +struct SetSceneCallBinding{S} <: AbstractSceneGraphEdit + application_id::Symbol + call::Symbol + selector::S +end + +struct RemoveSceneCallBinding <: AbstractSceneGraphEdit + application_id::Symbol + call::Symbol +end + +struct SetSceneApplicationTimeStep{T} <: AbstractSceneGraphEdit + application_id::Symbol + timestep::T +end + +struct SetSceneApplicationEnvironment{C} <: AbstractSceneGraphEdit + application_id::Symbol + configuration::C +end + +struct SetSceneOutputRouting <: AbstractSceneGraphEdit + application_id::Symbol + output::Symbol + route::Symbol +end + +struct SetSceneUpdateOrdering{U} <: AbstractSceneGraphEdit + application_id::Symbol + updates::U +end + +struct MarkScenePreviousTimeStep <: AbstractSceneGraphEdit + application_id::Symbol + input::Symbol +end + +struct UnmarkScenePreviousTimeStep <: AbstractSceneGraphEdit + application_id::Symbol + input::Symbol +end + +struct BreakSceneCycle{V} <: AbstractSceneGraphEdit + application_id::Symbol + input::Symbol + initialize_missing::Bool + initial_value::V +end + +struct AddSceneObject <: AbstractSceneGraphEdit + object::Object +end + +struct RemoveSceneObject <: AbstractSceneGraphEdit + object_id::ObjectId + recursive::Bool +end + +RemoveSceneObject(object_id; recursive::Bool=true) = + RemoveSceneObject(ObjectId(object_id), recursive) + +struct ReparentSceneObject <: AbstractSceneGraphEdit + object_id::ObjectId + parent_id::Union{Nothing,ObjectId} +end + +ReparentSceneObject(object_id, parent_id) = ReparentSceneObject( + ObjectId(object_id), + isnothing(parent_id) ? nothing : ObjectId(parent_id), +) + +struct SetSceneObjectStatus{V} <: AbstractSceneGraphEdit + object_id::ObjectId + variable::Symbol + value::V +end + +SetSceneObjectStatus(object_id, variable, value) = + SetSceneObjectStatus(ObjectId(object_id), Symbol(variable), value) + +struct SetSceneObjectStatuses{V} <: AbstractSceneGraphEdit + object_ids::Vector{ObjectId} + variable::Symbol + value::V +end + + +SetSceneObjectStatuses(object_ids, variable, value) = SetSceneObjectStatuses( + ObjectId[ObjectId(object_id) for object_id in object_ids], + Symbol(variable), + value, +) + +struct RemoveSceneObjectStatus <: AbstractSceneGraphEdit + object_id::ObjectId + variable::Symbol +end + +RemoveSceneObjectStatus(object_id, variable) = + RemoveSceneObjectStatus(ObjectId(object_id), Symbol(variable)) + +struct SetSceneObjectMetadata{C} <: AbstractSceneGraphEdit + object_id::ObjectId + configuration::C +end + +SetSceneObjectMetadata(object_id; kwargs...) = + SetSceneObjectMetadata(ObjectId(object_id), (; kwargs...)) + +struct SetSceneInstanceOverride{M<:AbstractModel} <: AbstractSceneGraphEdit + instance::Symbol + application_id::Symbol + model::M +end + +SetSceneInstanceOverride(instance, application_id, model::AbstractModel) = + SetSceneInstanceOverride(Symbol(instance), Symbol(application_id), model) + +struct RemoveSceneInstanceOverride <: AbstractSceneGraphEdit + instance::Symbol + application_id::Symbol +end + +RemoveSceneInstanceOverride(instance, application_id) = + RemoveSceneInstanceOverride(Symbol(instance), Symbol(application_id)) + +struct SetSceneObjectOverride{M<:AbstractModel} <: AbstractSceneGraphEdit + instance::Symbol + object_id::ObjectId + application_id::Symbol + model::M +end + +SetSceneObjectOverride(instance, object_id, application_id, model::AbstractModel) = + SetSceneObjectOverride(Symbol(instance), ObjectId(object_id), Symbol(application_id), model) + +struct RemoveSceneObjectOverride <: AbstractSceneGraphEdit + instance::Symbol + object_id::ObjectId + application_id::Symbol +end + +RemoveSceneObjectOverride(instance, object_id, application_id) = + RemoveSceneObjectOverride(Symbol(instance), ObjectId(object_id), Symbol(application_id)) + +""" + apply_scene_graph_edit(scene, edit) + +Apply one declarative graph edit transactionally. The input scene is not +modified; a deep-copied, cache-invalidated Scene is returned. Configuration +that is temporarily incomplete or cyclic is retained so the editor can display +and repair it. Structural edit errors leave the original scene unchanged. +""" +function apply_scene_graph_edit(scene::Scene, edit::AbstractSceneGraphEdit) + candidate = deepcopy(scene) + candidate = _apply_scene_graph_edit!(candidate, edit) + _mark_bindings_dirty!(candidate) + return candidate +end + +function _scene_edit_application_id(spec) + normalized = as_model_spec(spec) + name = application_name(normalized) + return isnothing(name) ? process(normalized) : name +end + +function _scene_edit_application_index(scene::Scene, application_id::Symbol) + matches = Int[ + index for (index, spec) in pairs(scene.applications) + if _scene_edit_application_id(spec) == application_id + ] + isempty(matches) && error("Scene has no application `$(application_id)`.") + length(matches) == 1 || error( + "Scene application id `$(application_id)` is ambiguous. Name repeated applications explicitly.", + ) + return only(matches) +end + +function _scene_edit_spec(scene::Scene, application_id::Symbol) + return as_model_spec(scene.applications[_scene_edit_application_index(scene, application_id)]) +end + +function _replace_scene_edit_spec!(scene::Scene, application_id::Symbol, spec) + index = _scene_edit_application_index(scene, application_id) + scene.applications[index] = spec + return scene +end + +function _apply_scene_graph_edit!(scene::Scene, edit::AddSceneApplication) + spec = as_model_spec(edit.spec) + application_id = _scene_edit_application_id(spec) + any(item -> _scene_edit_application_id(item) == application_id, scene.applications) && error( + "Scene application `$(application_id)` already exists.", + ) + push!(scene.applications, spec) + return scene +end + +function _apply_scene_graph_edit!(scene::Scene, edit::RemoveSceneApplication) + deleteat!(scene.applications, _scene_edit_application_index(scene, edit.application_id)) + return scene +end + +function _apply_scene_graph_edit!(scene::Scene, edit::RemoveSceneTemplateApplication) + _, selected_instance = _scene_edit_instance(scene, edit.instance) + base_name = _scene_edit_template_application_id(selected_instance, edit.application_id) + template = selected_instance.template + template_specs = Any[as_model_spec(item) for item in template.applications] + indexes = Int[ + index for (index, spec) in pairs(template_specs) + if _mounted_application_name(spec, index) == base_name + ] + index = only(indexes) + deleteat!(template_specs, index) + replacement_template = ObjectTemplate( + Tuple(template_specs); + kind=template.kind, + species=template.species, + parameters=template.parameters, + ) + instances = Any[] + for instance in scene.instances + if instance.template === template + overrides = _scene_edit_namedtuple_remove(instance.overrides, base_name) + object_overrides = Tuple( + override for override in instance.object_overrides + if override.application != base_name + ) + push!(instances, _scene_edit_normalize_instance( + instance; + template=replacement_template, + overrides=overrides, + object_overrides=object_overrides, + )) + else + push!(instances, _scene_edit_normalize_instance(instance)) + end + end + return _scene_edit_rebuild_instances(scene, Tuple(instances)) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::ReplaceSceneApplicationModel) + spec = _scene_edit_spec(scene, edit.application_id) + _validate_model_override_contract!( + model_(spec), + edit.model; + description="Replacement model for application `$(edit.application_id)`", + ) + return _replace_scene_edit_spec!( + scene, + edit.application_id, + ModelSpec(spec; model=edit.model), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::UpdateSceneApplication) + spec = _scene_edit_spec(scene, edit.application_id) + _validate_model_override_contract!( + model_(spec), + edit.model; + description="Updated model for application `$(edit.application_id)`", + ) + edit.selector isa AbstractObjectMultiplicity || error( + "Application targets must use One(...), OptionalOne(...), or Many(...).", + ) + if edit.name != edit.application_id + any(item -> _scene_edit_application_id(item) == edit.name, scene.applications) && error( + "Scene application `$(edit.name)` already exists.", + ) + end + return _replace_scene_edit_spec!( + scene, + edit.application_id, + ModelSpec( + spec; + model=edit.model, + name=edit.name, + applies_to=edit.selector, + timestep=edit.timestep, + ), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::RenameSceneApplication) + any(item -> _scene_edit_application_id(item) == edit.name, scene.applications) && error( + "Scene application `$(edit.name)` already exists.", + ) + spec = _scene_edit_spec(scene, edit.application_id) + return _replace_scene_edit_spec!( + scene, + edit.application_id, + ModelSpec(spec; name=edit.name), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneApplicationTargets) + edit.selector isa AbstractObjectMultiplicity || error( + "Application targets must use One(...), OptionalOne(...), or Many(...).", + ) + spec = _scene_edit_spec(scene, edit.application_id) + return _replace_scene_edit_spec!( + scene, + edit.application_id, + ModelSpec(spec; applies_to=edit.selector), + ) +end + +function _scene_edit_namedtuple_set(values::NamedTuple, name::Symbol, value) + pairs_ = Pair{Symbol,Any}[ + Symbol(key) => (Symbol(key) == name ? value : item) + for (key, item) in pairs(values) + ] + name in Symbol.(keys(values)) || push!(pairs_, name => value) + return (; pairs_...) +end + +function _scene_edit_namedtuple_remove(values::NamedTuple, name::Symbol) + return (; ( + Symbol(key) => item + for (key, item) in pairs(values) + if Symbol(key) != name + )...) +end + +function _scene_edit_origins_set(origins::NamedTuple, name::Symbol, origin::Symbol) + return _scene_edit_namedtuple_set(origins, name, origin) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneInputBinding) + edit.selector isa AbstractObjectMultiplicity || error("An input binding requires an object selector.") + spec = _scene_edit_spec(scene, edit.application_id) + edit.input in Symbol.(keys(inputs_(spec))) || error( + "Application `$(edit.application_id)` model has no input `$(edit.input)`.", + ) + inputs = _scene_edit_namedtuple_set(spec.inputs, edit.input, edit.selector) + origins = _scene_edit_origins_set(spec.input_origins, edit.input, :model_spec) + return _replace_scene_edit_spec!( + scene, + edit.application_id, + ModelSpec(spec; inputs=inputs, input_origins=origins), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::RemoveSceneInputBinding) + spec = _scene_edit_spec(scene, edit.application_id) + inputs = _scene_edit_namedtuple_remove(spec.inputs, edit.input) + origins = _scene_edit_namedtuple_remove(spec.input_origins, edit.input) + return _replace_scene_edit_spec!( + scene, + edit.application_id, + ModelSpec(spec; inputs=inputs, input_origins=origins), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneCallBinding) + edit.selector isa AbstractObjectMultiplicity || error("A call binding requires an object selector.") + spec = _scene_edit_spec(scene, edit.application_id) + calls = _scene_edit_namedtuple_set(spec.calls, edit.call, edit.selector) + origins = _scene_edit_origins_set(spec.call_origins, edit.call, :model_spec) + return _replace_scene_edit_spec!( + scene, + edit.application_id, + ModelSpec(spec; calls=calls, call_origins=origins), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::RemoveSceneCallBinding) + spec = _scene_edit_spec(scene, edit.application_id) + calls = _scene_edit_namedtuple_remove(spec.calls, edit.call) + origins = _scene_edit_namedtuple_remove(spec.call_origins, edit.call) + return _replace_scene_edit_spec!( + scene, + edit.application_id, + ModelSpec(spec; calls=calls, call_origins=origins), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneApplicationTimeStep) + spec = _scene_edit_spec(scene, edit.application_id) + return _replace_scene_edit_spec!( + scene, + edit.application_id, + ModelSpec(spec; timestep=edit.timestep), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneApplicationEnvironment) + spec = _scene_edit_spec(scene, edit.application_id) + return _replace_scene_edit_spec!( + scene, + edit.application_id, + ModelSpec(spec; environment=edit.configuration), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneOutputRouting) + edit.route in (:canonical, :stream_only) || error( + "Output route must be `:canonical` or `:stream_only`.", + ) + spec = _scene_edit_spec(scene, edit.application_id) + edit.output in Symbol.(keys(outputs_(spec))) || error( + "Application `$(edit.application_id)` model has no output `$(edit.output)`.", + ) + routing = _scene_edit_namedtuple_set(spec.output_routing, edit.output, edit.route) + return _replace_scene_edit_spec!( + scene, + edit.application_id, + ModelSpec(spec; output_routing=routing), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneUpdateOrdering) + spec = _scene_edit_spec(scene, edit.application_id) + return _replace_scene_edit_spec!( + scene, + edit.application_id, + ModelSpec(spec; updates=edit.updates), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::MarkScenePreviousTimeStep) + spec = _scene_edit_spec(scene, edit.application_id) + selector = if haskey(spec.inputs, edit.input) + getproperty(spec.inputs, edit.input) + else + report = compile_scene_report(scene) + selectors = Any[ + binding.selector for binding in report.input_bindings + if binding.application_id == edit.application_id && binding.input == edit.input + ] + isempty(selectors) && error( + "Application `$(edit.application_id)` has no resolved selector for input `$(edit.input)`. Add an input binding first.", + ) + first(selectors) + end + selector isa AbstractObjectMultiplicity || error( + "Input `$(edit.input)` on application `$(edit.application_id)` does not use an object selector.", + ) + previous_selector = _selector_with_previous_timestep( + selector, + PreviousTimeStep(edit.input), + ) + return _apply_scene_graph_edit!( + scene, + SetSceneInputBinding(edit.application_id, edit.input, previous_selector), + ) +end + +function _scene_selector_without_previous_timestep(selector::AbstractObjectMultiplicity) + values = (; ( + Symbol(key) => value + for (key, value) in pairs(criteria(selector)) + if Symbol(key) != :policy + )...) + return _rebuild_selector(selector, values) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::UnmarkScenePreviousTimeStep) + spec = _scene_edit_spec(scene, edit.application_id) + haskey(spec.inputs, edit.input) || error( + "Application `$(edit.application_id)` has no input binding `$(edit.input)`.", + ) + selector = getproperty(spec.inputs, edit.input) + _selector_policy(selector) isa PreviousTimeStep || error( + "Input `$(edit.input)` on application `$(edit.application_id)` is not marked PreviousTimeStep.", + ) + return _apply_scene_graph_edit!( + scene, + SetSceneInputBinding( + edit.application_id, + edit.input, + _scene_selector_without_previous_timestep(selector), + ), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::BreakSceneCycle) + _apply_scene_graph_edit!( + scene, + MarkScenePreviousTimeStep(edit.application_id, edit.input), + ) + edit.initialize_missing || return scene + report = compile_scene_report(scene) + applications = [ + application for application in report.applications + if application.id == edit.application_id + ] + isempty(applications) && error( + "Application `$(edit.application_id)` could not be resolved after breaking its cycle.", + ) + application = only(applications) + for object_id in application.target_ids + object = _scene_object(scene, object_id) + supplied = object.status isa Status && edit.input in Symbol.(propertynames(object.status)) + supplied || _set_scene_object_status!(scene, object_id, edit.input, edit.initial_value) + end + return scene +end + +function _apply_scene_graph_edit!(scene::Scene, edit::AddSceneObject) + register_object!(scene, edit.object) + return scene +end + +function _apply_scene_graph_edit!(scene::Scene, edit::RemoveSceneObject) + remove_object!(scene, edit.object_id; recursive=edit.recursive) + return scene +end + +function _apply_scene_graph_edit!(scene::Scene, edit::ReparentSceneObject) + reparent_object!(scene, edit.object_id, edit.parent_id) + return scene +end + +function _scene_edit_status_values(status) + status isa Status || return Pair{Symbol,Any}[] + return Pair{Symbol,Any}[ + Symbol(name) => status[name] + for name in propertynames(status) + ] +end + +function _set_scene_object_status!(scene, object_id, variable, value) + object = _scene_object(scene, object_id) + values = _scene_edit_status_values(object.status) + index = findfirst(pair -> first(pair) == variable, values) + if isnothing(index) + push!(values, variable => value) + else + values[index] = variable => value + end + object.status = Status((; values...)) + return scene +end + +function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneObjectStatus) + return _set_scene_object_status!(scene, edit.object_id, edit.variable, edit.value) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneObjectStatuses) + isempty(edit.object_ids) && error("SetSceneObjectStatuses requires at least one object id.") + for object_id in edit.object_ids + _set_scene_object_status!(scene, object_id, edit.variable, edit.value) + end + return scene +end + +function _apply_scene_graph_edit!(scene::Scene, edit::RemoveSceneObjectStatus) + object = _scene_object(scene, edit.object_id) + values = Pair{Symbol,Any}[ + pair for pair in _scene_edit_status_values(object.status) + if first(pair) != edit.variable + ] + object.status = isempty(values) ? nothing : Status((; values...)) + return scene +end + +function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneObjectMetadata) + object = _scene_object(scene, edit.object_id) + allowed = Set((:scale, :kind, :species, :name, :geometry, :parent)) + unknown = setdiff(Set(Symbol.(keys(edit.configuration))), allowed) + isempty(unknown) || error("Unsupported object metadata fields: $(sort!(collect(unknown); by=string)).") + _deindex_object!(scene.registry, object) + for (key_, value) in pairs(edit.configuration) + key = Symbol(key_) + if key == :parent + continue + elseif key == :geometry + object.geometry = value + else + setfield!(object, key, isnothing(value) ? nothing : Symbol(value)) + end + end + _index_object!(scene.registry, object) + haskey(edit.configuration, :parent) && reparent_object!(scene, object.id, edit.configuration.parent) + _mark_environment_bindings_dirty!(scene, object.id) + return scene +end + +function _scene_edit_instance(scene::Scene, name::Symbol) + matches = findall(instance -> instance.name == name, scene.instances) + isempty(matches) && error("Scene has no object instance `$(name)`.") + length(matches) == 1 || error("Scene object instance name `$(name)` is ambiguous.") + return only(matches), scene.instances[only(matches)] +end + +function _scene_edit_template_application_id(instance::ObjectInstance, application_id::Symbol) + prefix = string(instance.name, "__") + candidate = startswith(string(application_id), prefix) ? + Symbol(chopprefix(string(application_id), prefix)) : application_id + specs = Tuple(as_model_spec(item) for item in instance.template.applications) + matches = Symbol[ + _mounted_application_name(spec, index) + for (index, spec) in pairs(specs) + if candidate in (_mounted_application_name(spec, index), process(spec)) + ] + isempty(matches) && error( + "Instance `$(instance.name)` template has no application matching `$(application_id)`.", + ) + length(matches) == 1 || error( + "Instance `$(instance.name)` application `$(application_id)` is ambiguous; use its template application name.", + ) + return only(matches) +end + +function _scene_edit_normalize_instance( + instance::ObjectInstance; + template=instance.template, + overrides=instance.overrides, + object_overrides=instance.object_overrides, +) + return ObjectInstance( + instance.name, + template; + root=_instance_root_id(instance), + overrides=overrides, + object_overrides=object_overrides, + ) +end + +function _scene_edit_rebuild_instances( + scene::Scene, + instances; + replace_mounted_ids=Set{Symbol}(), +) + instances = Tuple(_scene_edit_normalize_instance(instance) for instance in instances) + mounted_ids = Set{Symbol}() + for instance in scene.instances + union!(mounted_ids, _instance_application_ids(scene, instance)) + end + global_applications = Any[ + application for application in scene.applications + if _scene_edit_application_id(application) ∉ mounted_ids + ] + old_mounted = Dict( + _scene_edit_application_id(application) => as_model_spec(application) + for application in scene.applications + if _scene_edit_application_id(application) in mounted_ids + ) + rebuilt = Scene( + (deepcopy(object) for object in scene_objects(scene))...; + applications=global_applications, + instances=instances, + environment=scene.environment, + source_adapter=scene.source_adapter, + ) + for (index, application) in pairs(rebuilt.applications) + application_id = _scene_edit_application_id(application) + haskey(old_mounted, application_id) || continue + application_id in replace_mounted_ids && continue + old_spec = old_mounted[application_id] + new_spec = as_model_spec(application) + rebuilt.applications[index] = ModelSpec(old_spec; model=model_(new_spec)) + end + return rebuilt +end + + +function _apply_scene_graph_edit!(scene::Scene, edit::UpdateSceneTemplateApplication) + _, selected_instance = _scene_edit_instance(scene, edit.instance) + base_name = _scene_edit_template_application_id(selected_instance, edit.application_id) + template = selected_instance.template + template_specs = Any[as_model_spec(item) for item in template.applications] + matches = Int[ + index for (index, spec) in pairs(template_specs) + if _mounted_application_name(spec, index) == base_name + ] + isempty(matches) && error("Template application `$(base_name)` was not found.") + index = only(matches) + original = template_specs[index] + _validate_model_override_contract!( + model_(original), + edit.model; + description="Updated shared template application `$(base_name)`", + ) + edit.selector isa AbstractObjectMultiplicity || error( + "Template application targets must use One(...), OptionalOne(...), or Many(...).", + ) + template_specs[index] = ModelSpec( + original; + model=edit.model, + applies_to=edit.selector, + timestep=edit.timestep, + ) + replacement_template = ObjectTemplate( + Tuple(template_specs); + kind=template.kind, + species=template.species, + parameters=template.parameters, + ) + affected = Set{Symbol}() + instances = Any[] + for instance in scene.instances + if instance.template === template + push!(affected, Symbol(instance.name, "__", base_name)) + push!(instances, _scene_edit_normalize_instance(instance; template=replacement_template)) + else + push!(instances, _scene_edit_normalize_instance(instance)) + end + end + return _scene_edit_rebuild_instances( + scene, + Tuple(instances); + replace_mounted_ids=affected, + ) +end + +function _scene_edit_replace_instance(scene::Scene, instance_name::Symbol, replacement) + index, _ = _scene_edit_instance(scene, instance_name) + instances = Any[scene.instances...] + instances[index] = replacement + return _scene_edit_rebuild_instances(scene, Tuple(instances)) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneInstanceOverride) + _, instance = _scene_edit_instance(scene, edit.instance) + application = _scene_edit_template_application_id(instance, edit.application_id) + overrides = _scene_edit_namedtuple_set(instance.overrides, application, edit.model) + return _scene_edit_replace_instance( + scene, + edit.instance, + _scene_edit_normalize_instance(instance; overrides=overrides), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::RemoveSceneInstanceOverride) + _, instance = _scene_edit_instance(scene, edit.instance) + application = _scene_edit_template_application_id(instance, edit.application_id) + haskey(instance.overrides, application) || error( + "Instance `$(edit.instance)` has no override for application `$(application)`.", + ) + overrides = _scene_edit_namedtuple_remove(instance.overrides, application) + return _scene_edit_replace_instance( + scene, + edit.instance, + _scene_edit_normalize_instance(instance; overrides=overrides), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneObjectOverride) + _, instance = _scene_edit_instance(scene, edit.instance) + application = _scene_edit_template_application_id(instance, edit.application_id) + object_ids = Set(_instance_object_ids(scene, instance)) + edit.object_id in object_ids || error( + "Object `$(edit.object_id.value)` does not belong to instance `$(edit.instance)`.", + ) + overrides = Override[ + override for override in instance.object_overrides + if !(override.object == edit.object_id && override.application == application) + ] + push!(overrides, Override(object=edit.object_id, application=application, model=edit.model)) + return _scene_edit_replace_instance( + scene, + edit.instance, + _scene_edit_normalize_instance(instance; object_overrides=Tuple(overrides)), + ) +end + +function _apply_scene_graph_edit!(scene::Scene, edit::RemoveSceneObjectOverride) + _, instance = _scene_edit_instance(scene, edit.instance) + application = _scene_edit_template_application_id(instance, edit.application_id) + overrides = Override[ + override for override in instance.object_overrides + if !(override.object == edit.object_id && override.application == application) + ] + length(overrides) < length(instance.object_overrides) || error( + "Instance `$(edit.instance)` has no object override for `$(edit.object_id.value)` and application `$(application)`.", + ) + return _scene_edit_replace_instance( + scene, + edit.instance, + _scene_edit_normalize_instance(instance; object_overrides=Tuple(overrides)), + ) +end + +abstract type AbstractSceneGraphEditorSession end + +function _scene_graph_editor_missing_http() + throw(ArgumentError( + "Interactive Scene graph editing requires HTTP.jl. Load it with `using HTTP` before calling `edit_graph`.", + )) +end + +""" + edit_graph([scene]; kwargs...) + +Start the HTTP-backed interactive Scene editor. Call `edit_graph()` to begin +with an empty Scene. The implementation is provided by the HTTP package +extension. +""" +edit_graph(args...; kwargs...) = _scene_graph_editor_missing_http() + +current_scene(::AbstractSceneGraphEditorSession) = _scene_graph_editor_missing_http() +apply_edit!(::AbstractSceneGraphEditorSession, ::AbstractSceneGraphEdit) = + _scene_graph_editor_missing_http() +undo!(::AbstractSceneGraphEditorSession) = _scene_graph_editor_missing_http() +redo!(::AbstractSceneGraphEditorSession) = _scene_graph_editor_missing_http() diff --git a/src/visualization/scene_graph_view.jl b/src/visualization/scene_graph_view.jl new file mode 100644 index 000000000..d8b86891d --- /dev/null +++ b/src/visualization/scene_graph_view.jl @@ -0,0 +1,1089 @@ +""" + SceneGraphDiagnostic + +Structured diagnostic emitted while compiling a scene for visualization. A +graph report keeps diagnostics attached to stable application, object, and +variable identities so an editor can present actionable controls. +""" +struct SceneGraphDiagnostic + code::Symbol + severity::Symbol + message::String + phase::Symbol + application_ids::Vector{Symbol} + object_ids::Vector{Any} + variable::Union{Nothing,Symbol} + suggestions::Vector{String} +end + +""" + SceneCompilationReport + +Best-effort result of compiling a `Scene` for inspection. Unlike +[`compile_scene`](@ref), this representation preserves successful earlier +phases when a later phase reports an invalid selector, binding, writer, or +cycle. +""" +struct SceneCompilationReport + scene::Scene + applications::Any + input_bindings::Any + call_bindings::Any + application_order::Vector{Symbol} + dependency_children::Dict{Symbol,Set{Symbol}} + cycles::Vector{Vector{Symbol}} + diagnostics::Vector{SceneGraphDiagnostic} + compiled::Any +end + +""" + SceneGraphView + +JSON-oriented, renderer-independent representation of a Scene/Object graph. +Applications are the default visual unit; `executions` optionally expands them +into concrete `(application, object)` targets. +""" +struct SceneGraphView + level::Symbol + metadata::Dict{String,Any} + objects::Vector{Dict{String,Any}} + instances::Vector{Dict{String,Any}} + applications::Vector{Dict{String,Any}} + executions::Vector{Dict{String,Any}} + edges::Vector{Dict{String,Any}} + model_library::Vector{Dict{String,Any}} + initialization::Vector{Dict{String,Any}} + diagnostics::Vector{Dict{String,Any}} + cycles::Vector{Dict{String,Any}} + available_actions::Vector{String} +end + +function _scene_graph_diagnostic( + err, + phase::Symbol; + code=Symbol(phase, :_error), + severity=:error, + application_ids=Symbol[], + object_ids=Any[], + variable=nothing, + suggestions=String[], +) + return SceneGraphDiagnostic( + Symbol(code), + Symbol(severity), + sprint(showerror, err), + phase, + Symbol[Symbol(id) for id in application_ids], + Any[object_ids...], + isnothing(variable) ? nothing : Symbol(variable), + String[String(item) for item in suggestions], + ) +end + +function _scene_graph_phase!(operation, diagnostics, phase::Symbol, fallback) + try + return operation() + catch err + push!(diagnostics, _scene_graph_diagnostic(err, phase)) + return fallback + end +end + +function _scene_graph_dependency_children(applications, input_bindings, call_bindings) + children = Dict{Symbol,Set{Symbol}}() + call_owners = _scene_call_owners(call_bindings) + _scene_input_order_edges!(children, input_bindings, call_owners) + _scene_update_order_edges!(children, applications) + return children +end + +function _scene_graph_cycle_components(applications, children) + application_ids = Symbol[application.id for application in applications] + index = Ref(0) + indexes = Dict{Symbol,Int}() + lowlinks = Dict{Symbol,Int}() + stack = Symbol[] + on_stack = Set{Symbol}() + components = Vector{Vector{Symbol}}() + + function visit(application_id::Symbol) + index[] += 1 + indexes[application_id] = index[] + lowlinks[application_id] = index[] + push!(stack, application_id) + push!(on_stack, application_id) + + for child in sort!(collect(get(children, application_id, Set{Symbol}())); by=string) + if !haskey(indexes, child) + visit(child) + lowlinks[application_id] = min(lowlinks[application_id], lowlinks[child]) + elseif child in on_stack + lowlinks[application_id] = min(lowlinks[application_id], indexes[child]) + end + end + + lowlinks[application_id] == indexes[application_id] || return + component = Symbol[] + while !isempty(stack) + member = pop!(stack) + delete!(on_stack, member) + push!(component, member) + member == application_id && break + end + if length(component) > 1 || application_id in get(children, application_id, Set{Symbol}()) + sort!(component; by=string) + push!(components, component) + end + end + + for application_id in application_ids + haskey(indexes, application_id) || visit(application_id) + end + sort!(components; by=component -> join(string.(component), ":")) + return components +end + +function _scene_graph_compiled( + scene, + applications, + input_bindings, + call_bindings, + application_order, + diagnostics, +) + isempty(diagnostics) || return nothing + applications_by_id = Dict(application.id => application for application in applications) + input_by_target = _index_scene_bindings(input_bindings, :application_id, :consumer_id) + call_by_target = _index_scene_bindings(call_bindings, :application_id, :consumer_id) + model_bundles = _compile_scene_model_bundles( + applications, + applications_by_id, + call_by_target, + ) + return CompiledScene( + scene, + applications, + applications_by_id, + input_bindings, + call_bindings, + input_by_target, + call_by_target, + model_bundles, + application_order, + scene.revision, + ) +end + +""" + compile_scene_report(scene; strict=false) + +Compile a Scene for visualization while retaining partial graph information +and structured diagnostics. With `strict=true`, call the simulation compiler +and propagate its errors unchanged. +""" +function compile_scene_report(scene::Scene; strict::Bool=false) + # Compilation wires reference carriers and prepares generated status fields. + # A viewer must not mutate the user's editable or pre-run Scene merely by + # inspecting it, so all diagnostic compilation happens on a structural copy. + scene = deepcopy(scene) + if strict + compiled = compile_scene(scene) + children = _scene_graph_dependency_children( + compiled.applications, + compiled.input_bindings, + compiled.call_bindings, + ) + return SceneCompilationReport( + scene, + compiled.applications, + compiled.input_bindings, + compiled.call_bindings, + Symbol[compiled.application_order...], + children, + Vector{Vector{Symbol}}(), + SceneGraphDiagnostic[], + compiled, + ) + end + + diagnostics = SceneGraphDiagnostic[] + timeline = _scene_graph_phase!(diagnostics, :timeline, nothing) do + _scene_timeline(scene) + end + isnothing(timeline) && return SceneCompilationReport( + scene, + CompiledSceneApplication[], + CompiledSceneInputBinding[], + CompiledSceneCallBinding[], + Symbol[], + Dict{Symbol,Set{Symbol}}(), + Vector{Vector{Symbol}}(), + diagnostics, + nothing, + ) + + applications = _scene_graph_phase!(diagnostics, :applications, CompiledSceneApplication[]) do + _compile_scene_applications(scene, Tuple(scene.applications), timeline) + end + isempty(applications) && !isempty(scene.applications) && return SceneCompilationReport( + scene, + applications, + CompiledSceneInputBinding[], + CompiledSceneCallBinding[], + Symbol[], + Dict{Symbol,Set{Symbol}}(), + Vector{Vector{Symbol}}(), + diagnostics, + nothing, + ) + + call_bindings = _scene_graph_phase!(diagnostics, :calls, CompiledSceneCallBinding[]) do + _compile_scene_call_bindings(scene, applications) + end + _scene_graph_phase!(diagnostics, :call_cadence, nothing) do + _validate_scene_call_cadences!(applications, call_bindings, timeline) + end + _scene_graph_phase!(diagnostics, :writers, nothing) do + _validate_scene_writers!(applications, call_bindings) + end + _scene_graph_phase!(diagnostics, :output_status, nothing) do + _prepare_scene_output_statuses!(scene, applications) + end + + input_bindings = _scene_graph_phase!(diagnostics, :inputs, CompiledSceneInputBinding[]) do + _compile_scene_input_bindings( + scene, + applications, + _manual_call_application_ids(call_bindings), + ) + end + _scene_graph_phase!(diagnostics, :input_status, nothing) do + _prepare_scene_bound_input_statuses!(scene, applications, input_bindings) + _wire_scene_input_carriers!(scene, input_bindings) + end + + children = _scene_graph_dependency_children(applications, input_bindings, call_bindings) + cycles = _scene_graph_cycle_components(applications, children) + application_order = Symbol[] + if isempty(cycles) + application_order = _scene_graph_phase!(diagnostics, :schedule, Symbol[]) do + _stable_topological_application_order(applications, children) + end + else + for cycle in cycles + message = "Scene application dependency cycle detected among applications `$(cycle)`." + push!(diagnostics, SceneGraphDiagnostic( + :application_cycle, + :error, + message, + :schedule, + copy(cycle), + Any[], + nothing, + [ + "Mark an eligible input as PreviousTimeStep to use its previous accepted value.", + "Revise Inputs(...) or Updates(...) to remove the same-step dependency.", + ], + )) + end + end + + compiled = try + _scene_graph_compiled( + scene, + applications, + input_bindings, + call_bindings, + application_order, + diagnostics, + ) + catch err + push!(diagnostics, _scene_graph_diagnostic(err, :model_bundles)) + nothing + end + + return SceneCompilationReport( + scene, + applications, + input_bindings, + call_bindings, + application_order, + children, + cycles, + diagnostics, + compiled, + ) +end + +_scene_graph_object_id(value) = value isa ObjectId ? value.value : value +_scene_graph_application_node_id(id) = string("application:", id) +_scene_graph_object_node_id(id) = string("object:", _scene_graph_object_id(id)) +_scene_graph_instance_node_id(name) = string("instance:", name) +_scene_graph_execution_node_id(application_id, object_id) = + string("execution:", application_id, ":", _scene_graph_object_id(object_id)) +_scene_graph_port_id(application_id, role, variable) = + string(_scene_graph_application_node_id(application_id), ":", role, ":", variable) + +function _scene_graph_json_value(value) + value === nothing && return nothing + value === missing && return nothing + value isa Bool && return value + if value isa Real + return isfinite(value) ? value : string(value) + end + value isa AbstractString && return String(value) + value isa Symbol && return string(value) + value isa ObjectId && return _scene_graph_json_value(value.value) + value isa Type && return string(value) + value isa Module && return string(value) + value isa Pair && return Dict( + "first" => _scene_graph_json_value(first(value)), + "second" => _scene_graph_json_value(last(value)), + ) + if value isa NamedTuple + return Dict(string(key) => _scene_graph_json_value(item) for (key, item) in pairs(value)) + end + if value isa AbstractDict + return Dict(string(key) => _scene_graph_json_value(item) for (key, item) in pairs(value)) + end + if value isa Tuple || value isa AbstractArray || value isa AbstractSet + return [_scene_graph_json_value(item) for item in value] + end + value isa AbstractObjectMultiplicity && return _scene_graph_selector_dict(value) + return string(value) +end + +function _scene_graph_selector_atom(selector::AbstractObjectSelector) + descriptor = Dict{String,Any}("type" => string(nameof(typeof(selector)))) + selector isa Ancestor && (descriptor["scale"] = _scene_graph_json_value(selector.scale)) + selector isa Scope && (descriptor["name"] = string(selector.name)) + selector isa Kind && (descriptor["kind"] = string(selector.kind)) + selector isa Species && (descriptor["species"] = string(selector.species)) + selector isa Scale && (descriptor["scale"] = string(selector.scale)) + selector isa Relation && (descriptor["relation"] = string(selector.relation)) + return descriptor +end + +function _scene_graph_policy_dict(policy) + descriptor = Dict{String,Any}( + "type" => string(nameof(typeof(policy))), + "julia" => repr(policy), + ) + policy isa PreviousTimeStep && (descriptor["variable"] = string(policy.variable)) + policy isa PreviousTimeStep && (descriptor["process"] = string(policy.process)) + policy isa Interpolate && (descriptor["mode"] = string(policy.mode)) + policy isa Interpolate && (descriptor["extrapolation"] = string(policy.extrapolation)) + policy isa Union{Integrate,Aggregate} && (descriptor["reducer"] = repr(policy.reducer)) + return descriptor +end + +function _scene_graph_selector_criteria(selector::AbstractObjectMultiplicity) + result = Dict{String,Any}() + for (key_, value) in pairs(criteria(selector)) + key = Symbol(key_) + result[string(key)] = if key == :selectors + [_scene_graph_selector_atom(item) for item in value] + elseif key == :within && value isa AbstractObjectSelector + _scene_graph_selector_atom(value) + elseif key == :policy + _scene_graph_policy_dict(value) + else + _scene_graph_json_value(value) + end + end + return result +end + +function _scene_graph_selector_dict(selector::AbstractObjectMultiplicity) + return Dict{String,Any}( + "type" => string(nameof(typeof(selector))), + "multiplicity" => string(multiplicity(selector)), + "criteria" => _scene_graph_selector_criteria(selector), + "julia" => repr(selector), + ) +end + +function _scene_graph_model_parameters(model) + parameters = Dict{String,Any}() + for field in fieldnames(Base.unwrap_unionall(typeof(model))) + value = getfield(model, field) + parameters[string(field)] = Dict{String,Any}( + "value" => _scene_graph_json_value(value), + "julia" => repr(value), + "type" => string(_parameter_choice_from_type(typeof(value))), + "juliaType" => string(typeof(value)), + ) + end + return parameters +end + +function _scene_graph_port(application, role::Symbol, variable, default) + name = Symbol(variable) + return Dict{String,Any}( + "id" => _scene_graph_port_id(application.id, role, name), + "name" => string(name), + "role" => string(role), + "default" => _scene_graph_json_value(default), + "defaultJulia" => repr(default), + "expectedType" => string(typeof(default)), + ) +end + +function _scene_graph_application_dict(scene, application) + model = _application_default_model(application) + inputs = inputs_(application.spec) + outputs = outputs_(application.spec) + environment_inputs = meteo_inputs_(application.spec) + environment_outputs = meteo_outputs_(application.spec) + target_objects = [_scene_object(scene, id) for id in application.target_ids] + return Dict{String,Any}( + "id" => _scene_graph_application_node_id(application.id), + "applicationId" => string(application.id), + "name" => isnothing(application.name) ? nothing : string(application.name), + "process" => string(application.process), + "modelType" => string(typeof(model)), + "modelName" => string(nameof(typeof(model))), + "module" => string(parentmodule(typeof(model))), + "package" => _model_package_name(parentmodule(typeof(model))), + "modelParameters" => _scene_graph_model_parameters(model), + "selector" => _scene_graph_selector_dict(application.applies_to), + "targetIds" => [_scene_graph_json_value(id.value) for id in application.target_ids], + "targetCount" => length(application.target_ids), + "targetScales" => sort!(unique!(String[string(object.scale) for object in target_objects if !isnothing(object.scale)])), + "targetKinds" => sort!(unique!(String[string(object.kind) for object in target_objects if !isnothing(object.kind)])), + "targetSpecies" => sort!(unique!(String[string(object.species) for object in target_objects if !isnothing(object.species)])), + "targetInstances" => sort!(unique!(String[ + string(instance) for id in application.target_ids + for instance in (_object_instance_name(scene, id),) + if !isnothing(instance) + ])), + "timestep" => _scene_graph_json_value(application.timestep), + "clock" => _scene_graph_json_value(application.clock), + "inputs" => [_scene_graph_port(application, :input, name, value) for (name, value) in pairs(inputs)], + "outputs" => [_scene_graph_port(application, :output, name, value) for (name, value) in pairs(outputs)], + "environmentInputs" => [_scene_graph_port(application, :environment_input, name, value) for (name, value) in pairs(environment_inputs)], + "environmentOutputs" => [_scene_graph_port(application, :environment_output, name, value) for (name, value) in pairs(environment_outputs)], + "modelStorage" => isnothing(application.model_overrides) ? "shared_application" : "per_object_override", + "objectOverrides" => isnothing(application.model_overrides) ? Any[] : [ + Dict( + "objectId" => _scene_graph_json_value(object_id.value), + "modelType" => string(typeof(override)), + "parameters" => _scene_graph_model_parameters(override), + ) + for (object_id, override) in application.model_overrides + ], + ) +end + +function _scene_graph_object_dict(row) + id = row.id + return Dict{String,Any}( + "id" => _scene_graph_object_node_id(id), + "objectId" => _scene_graph_json_value(id), + "scale" => _scene_graph_json_value(row.scale), + "kind" => _scene_graph_json_value(row.kind), + "species" => _scene_graph_json_value(row.species), + "name" => _scene_graph_json_value(row.name), + "instance" => _scene_graph_json_value(row.instance), + "parent" => isnothing(row.parent) ? nothing : _scene_graph_object_node_id(row.parent), + "children" => [_scene_graph_object_node_id(child) for child in row.children], + "hasGeometry" => row.has_geometry, + "hasStatus" => row.has_status, + ) +end + +function _scene_graph_instance_dict(row) + return Dict{String,Any}( + "id" => _scene_graph_instance_node_id(row.name), + "name" => string(row.name), + "rootId" => _scene_graph_json_value(row.root_id), + "kind" => _scene_graph_json_value(row.kind), + "species" => _scene_graph_json_value(row.species), + "objectIds" => [_scene_graph_json_value(id) for id in row.object_ids], + "applicationIds" => string.(row.application_ids), + "instanceOverrides" => string.(row.instance_overrides), + "objectOverrides" => _scene_graph_json_value(row.object_overrides), + "parametersType" => string(row.parameters_type), + ) +end + +function _scene_graph_execution_dict(application, object_id) + model = _application_model(application, object_id) + return Dict{String,Any}( + "id" => _scene_graph_execution_node_id(application.id, object_id), + "applicationId" => string(application.id), + "applicationNodeId" => _scene_graph_application_node_id(application.id), + "objectId" => _scene_graph_json_value(object_id.value), + "objectNodeId" => _scene_graph_object_node_id(object_id), + "modelType" => string(typeof(model)), + "modelParameters" => _scene_graph_model_parameters(model), + "overridden" => typeof(model) != typeof(_application_default_model(application)) || model != _application_default_model(application), + ) +end + +function _scene_graph_application_for_object(applications_by_id, application_id, object_id) + application = get(applications_by_id, application_id, nothing) + isnothing(application) && return false + return object_id in application.target_ids +end + +function _scene_graph_binding_edges(report, level) + edges = Dict{String,Dict{String,Any}}() + applications_by_id = Dict(application.id => application for application in report.applications) + cycle_memberships = Dict{Symbol,Int}() + for (index, component) in pairs(report.cycles) + for application_id in component + cycle_memberships[application_id] = index + end + end + + for binding in report.input_bindings + previous = binding.policy isa PreviousTimeStep + for source_application_id in binding.source_application_ids + source_ids = ObjectId[ + source_id for source_id in binding.source_ids + if _scene_graph_application_for_object( + applications_by_id, + source_application_id, + source_id, + ) + ] + isempty(source_ids) && (source_ids = copy(binding.source_ids)) + if level == :resolved + for source_id in source_ids + edge_id = string( + "binding:", source_application_id, ":", source_id.value, + ":", binding.source_var, ":", binding.application_id, + ":", binding.consumer_id.value, ":", binding.input, + ) + edges[edge_id] = Dict{String,Any}( + "id" => edge_id, + "source" => _scene_graph_execution_node_id(source_application_id, source_id), + "target" => _scene_graph_execution_node_id(binding.application_id, binding.consumer_id), + "sourcePort" => _scene_graph_port_id(source_application_id, :output, binding.source_var), + "targetPort" => _scene_graph_port_id(binding.application_id, :input, binding.input), + "sourceVariable" => string(binding.source_var), + "targetVariable" => string(binding.input), + "sourceApplicationId" => string(source_application_id), + "targetApplicationId" => string(binding.application_id), + "sourceObjectIds" => [_scene_graph_json_value(source_id.value)], + "targetObjectIds" => [_scene_graph_json_value(binding.consumer_id.value)], + "kind" => previous ? "previous_timestep" : string(binding.origin == :inferred ? :inferred_same_object : :value_binding), + "projection" => "resolved", + "origin" => string(binding.origin), + "multiplicity" => string(binding.multiplicity), + "policy" => string(typeof(binding.policy)), + "selector" => _scene_graph_selector_dict(binding.selector), + "cycle" => !previous && get(cycle_memberships, source_application_id, 0) == get(cycle_memberships, binding.application_id, -1), + ) + end + else + edge_id = string( + "binding:", source_application_id, ":", binding.source_var, + ":", binding.application_id, ":", binding.input, + ) + edge = get!(edges, edge_id) do + Dict{String,Any}( + "id" => edge_id, + "source" => _scene_graph_application_node_id(source_application_id), + "target" => _scene_graph_application_node_id(binding.application_id), + "sourcePort" => _scene_graph_port_id(source_application_id, :output, binding.source_var), + "targetPort" => _scene_graph_port_id(binding.application_id, :input, binding.input), + "sourceVariable" => string(binding.source_var), + "targetVariable" => string(binding.input), + "sourceApplicationId" => string(source_application_id), + "targetApplicationId" => string(binding.application_id), + "sourceObjectIds" => Any[], + "targetObjectIds" => Any[], + "kind" => previous ? "previous_timestep" : string(binding.origin == :inferred ? :inferred_same_object : :value_binding), + "projection" => "applications", + "origin" => string(binding.origin), + "multiplicity" => string(binding.multiplicity), + "policy" => string(typeof(binding.policy)), + "selector" => _scene_graph_selector_dict(binding.selector), + "cycle" => !previous && get(cycle_memberships, source_application_id, 0) == get(cycle_memberships, binding.application_id, -1), + ) + end + append!(edge["sourceObjectIds"], [_scene_graph_json_value(id.value) for id in source_ids]) + push!(edge["targetObjectIds"], _scene_graph_json_value(binding.consumer_id.value)) + unique!(edge["sourceObjectIds"]) + unique!(edge["targetObjectIds"]) + end + end + end + return collect(values(edges)) +end + +function _scene_graph_call_edges(report, level) + edges = Dict{String,Dict{String,Any}}() + for binding in report.call_bindings + for callee_application_id in binding.callee_application_ids + if level == :resolved + for callee_object_id in binding.callee_object_ids + edge_id = string( + "call:", binding.application_id, ":", binding.consumer_id.value, + ":", binding.call, ":", callee_application_id, ":", callee_object_id.value, + ) + edges[edge_id] = Dict{String,Any}( + "id" => edge_id, + "source" => _scene_graph_execution_node_id(binding.application_id, binding.consumer_id), + "target" => _scene_graph_execution_node_id(callee_application_id, callee_object_id), + "sourcePort" => nothing, + "targetPort" => nothing, + "kind" => "manual_call", + "projection" => "resolved", + "call" => string(binding.call), + "origin" => string(binding.origin), + "multiplicity" => string(binding.multiplicity), + "selector" => _scene_graph_selector_dict(binding.selector), + "cycle" => false, + ) + end + else + edge_id = string("call:", binding.application_id, ":", binding.call, ":", callee_application_id) + edges[edge_id] = Dict{String,Any}( + "id" => edge_id, + "source" => _scene_graph_application_node_id(binding.application_id), + "target" => _scene_graph_application_node_id(callee_application_id), + "sourcePort" => nothing, + "targetPort" => nothing, + "kind" => "manual_call", + "projection" => "applications", + "call" => string(binding.call), + "origin" => string(binding.origin), + "multiplicity" => string(binding.multiplicity), + "selector" => _scene_graph_selector_dict(binding.selector), + "cycle" => false, + ) + end + end + end + return collect(values(edges)) +end + +function _scene_graph_structure_edges(scene, applications) + edges = Dict{String,Any}[] + for object in values(scene.registry.objects) + if !isnothing(object.parent) + push!(edges, Dict{String,Any}( + "id" => string("topology:", object.parent.value, ":", object.id.value), + "source" => _scene_graph_object_node_id(object.parent), + "target" => _scene_graph_object_node_id(object.id), + "kind" => "object_topology", + "projection" => "topology", + "cycle" => false, + )) + end + end + for application in applications + for object_id in application.target_ids + push!(edges, Dict{String,Any}( + "id" => string("target:", application.id, ":", object_id.value), + "source" => _scene_graph_application_node_id(application.id), + "target" => _scene_graph_object_node_id(object_id), + "kind" => "application_target", + "projection" => "targets", + "cycle" => false, + )) + end + end + return edges +end + +function _scene_graph_diagnostic_dict(diagnostic::SceneGraphDiagnostic) + return Dict{String,Any}( + "code" => string(diagnostic.code), + "severity" => string(diagnostic.severity), + "message" => diagnostic.message, + "phase" => string(diagnostic.phase), + "applicationIds" => string.(diagnostic.application_ids), + "objectIds" => _scene_graph_json_value(diagnostic.object_ids), + "variable" => isnothing(diagnostic.variable) ? nothing : string(diagnostic.variable), + "suggestions" => diagnostic.suggestions, + ) +end + +function _scene_graph_initialization(report) + supplied = Dict( + object.id => Set{Symbol}( + object.status isa Status ? Symbol.(propertynames(object.status)) : Symbol[], + ) + for object in values(report.scene.registry.objects) + ) + bindings = Dict( + (binding.application_id, binding.consumer_id, binding.input) => binding + for binding in report.input_bindings + ) + environment_variables_ = try + environment_variables(environment_backend(report.scene.environment)) + catch + Set{Symbol}() + end + isnothing(environment_variables_) && (environment_variables_ = nothing) + + rows = Dict{String,Any}[] + for application in report.applications + model_inputs = inputs_(application.spec) + model_outputs = outputs_(application.spec) + model_environment_inputs = meteo_inputs_(application.spec) + model_environment_outputs = meteo_outputs_(application.spec) + source_overrides = _environment_source_overrides(application.spec) + for object_id in application.target_ids + object = _scene_object(report.scene, object_id) + for (variable, default) in pairs(model_outputs) + push!(rows, _scene_graph_initialization_row( + application.id, + object_id, + variable, + :output, + :generated, + default, + )) + end + for (variable, default) in pairs(model_environment_outputs) + push!(rows, _scene_graph_initialization_row( + application.id, + object_id, + variable, + :environment_output, + :generated, + default, + )) + end + for (variable_, default) in pairs(model_inputs) + variable = Symbol(variable_) + binding = get(bindings, (application.id, object_id, variable), nothing) + status_supplied = variable in get(supplied, object_id, Set{Symbol}()) + disposition = if !isnothing(binding) && binding.policy isa PreviousTimeStep + status_supplied ? :supplied : :unresolved + elseif !isnothing(binding) + :producer_bound + elseif status_supplied + :supplied + else + :unresolved + end + value = disposition == :supplied ? object.status[variable] : default + row = _scene_graph_initialization_row( + application.id, + object_id, + variable, + :input, + disposition, + value; + binding=binding, + ) + push!(rows, row) + end + for (variable_, default) in pairs(model_environment_inputs) + variable = Symbol(variable_) + source = Symbol(get(source_overrides, variable, variable)) + bound = isnothing(environment_variables_) || source in environment_variables_ + row = _scene_graph_initialization_row( + application.id, + object_id, + variable, + :environment_input, + bound ? :environment_bound : :unresolved, + default, + ) + row["sourceVariable"] = string(source) + push!(rows, row) + end + end + end + sort!(rows; by=row -> ( + row["applicationId"], + string(row["objectId"]), + row["role"], + row["variable"], + )) + return rows +end + +function _scene_graph_initialization_row( + application_id, + object_id, + variable, + role, + disposition, + value; + binding=nothing, +) + return Dict{String,Any}( + "applicationId" => string(application_id), + "objectId" => _scene_graph_json_value(object_id.value), + "variable" => string(variable), + "role" => string(role), + "disposition" => string(disposition), + "value" => _scene_graph_json_value(value), + "valueJulia" => repr(value), + "expectedType" => string(typeof(value)), + "sourceApplicationIds" => isnothing(binding) ? String[] : string.(binding.source_application_ids), + "sourceObjectIds" => isnothing(binding) ? Any[] : [_scene_graph_json_value(id.value) for id in binding.source_ids], + "sourceVariable" => isnothing(binding) ? nothing : string(binding.source_var), + "origin" => isnothing(binding) ? string(disposition == :supplied ? :status : :missing) : string(binding.origin), + "previousTimeStep" => !isnothing(binding) && binding.policy isa PreviousTimeStep, + ) +end + +function _scene_graph_cycle_dict(report, component, index) + members = Set(component) + dependency_edges = Dict{String,Any}[] + for source in component + for target in get(report.dependency_children, source, Set{Symbol}()) + target in members || continue + push!(dependency_edges, Dict{String,Any}( + "sourceApplicationId" => string(source), + "targetApplicationId" => string(target), + )) + end + end + break_candidates = Dict{String,Any}[] + for binding in report.input_bindings + binding.policy isa PreviousTimeStep && continue + binding.application_id in members || continue + any(source -> source in members, binding.source_application_ids) || continue + push!(break_candidates, Dict{String,Any}( + "applicationId" => string(binding.application_id), + "objectId" => _scene_graph_json_value(binding.consumer_id.value), + "input" => string(binding.input), + "sourceApplicationIds" => string.(binding.source_application_ids), + "sourceObjectIds" => [_scene_graph_json_value(id.value) for id in binding.source_ids], + "sourceVariable" => string(binding.source_var), + "selector" => _scene_graph_selector_dict(binding.selector), + )) + end + return Dict{String,Any}( + "id" => string("cycle:", index), + "applicationIds" => string.(component), + "edges" => dependency_edges, + "breakCandidates" => break_candidates, + ) +end + +function _scene_graph_model_library() + return [model_descriptor(type) for type in available_models()] +end + +function _normalize_scene_graph_level(level) + normalized = Symbol(level) + normalized in (:applications, :topology, :resolved) || error( + "Unsupported scene graph level `$(level)`. Use `:applications`, `:topology`, or `:resolved`.", + ) + return normalized +end + +""" + compile_scene_graph(scene; level=:applications, strict=false) + compile_scene_graph(compiled::CompiledScene; level=:applications) + +Build a renderer-independent graph view from a Scene or an existing compiled +scene. +""" +function compile_scene_graph(scene::Scene; level=:applications, strict::Bool=false) + return _scene_graph_view(compile_scene_report(scene; strict=strict), level) +end + +function compile_scene_graph(compiled::CompiledScene; level=:applications) + children = _scene_graph_dependency_children( + compiled.applications, + compiled.input_bindings, + compiled.call_bindings, + ) + report = SceneCompilationReport( + compiled.scene, + compiled.applications, + compiled.input_bindings, + compiled.call_bindings, + Symbol[compiled.application_order...], + children, + _scene_graph_cycle_components(compiled.applications, children), + SceneGraphDiagnostic[], + compiled, + ) + return _scene_graph_view(report, level) +end + +function _scene_graph_view(report::SceneCompilationReport, level) + level = _normalize_scene_graph_level(level) + objects = [_scene_graph_object_dict(row) for row in explain_objects(report.scene)] + instances = [_scene_graph_instance_dict(row) for row in explain_instances(report.scene)] + applications = [_scene_graph_application_dict(report.scene, application) for application in report.applications] + executions = [ + _scene_graph_execution_dict(application, object_id) + for application in report.applications + for object_id in application.target_ids + ] + edges = vcat( + _scene_graph_binding_edges(report, :applications), + _scene_graph_binding_edges(report, :resolved), + _scene_graph_call_edges(report, :applications), + _scene_graph_call_edges(report, :resolved), + _scene_graph_structure_edges(report.scene, report.applications), + ) + sort!(edges; by=edge -> edge["id"]) + initialization = _scene_graph_initialization(report) + diagnostics = [_scene_graph_diagnostic_dict(diagnostic) for diagnostic in report.diagnostics] + cycles = [ + _scene_graph_cycle_dict(report, component, index) + for (index, component) in pairs(report.cycles) + ] + unresolved = count(row -> row["disposition"] == "unresolved", initialization) + metadata = Dict{String,Any}( + "title" => "PlantSimEngine Scene Graph", + "sceneRevision" => report.scene.revision, + "objectCount" => length(objects), + "instanceCount" => length(instances), + "applicationCount" => length(applications), + "executionCount" => sum(application["targetCount"] for application in applications; init=0), + "bindingCount" => length(report.input_bindings), + "callCount" => length(report.call_bindings), + "unresolvedInitializationCount" => unresolved, + "cyclic" => !isempty(cycles), + "strictlyCompiled" => !isnothing(report.compiled), + ) + return SceneGraphView( + level, + metadata, + objects, + instances, + applications, + executions, + edges, + _scene_graph_model_library(), + initialization, + diagnostics, + cycles, + [ + "inspect", + "filter", + "expand_executions", + "add_application", + "connect_binding", + "break_cycle", + ], + ) +end + +scene_graph_view(scene_or_compiled; kwargs...) = compile_scene_graph(scene_or_compiled; kwargs...) + +function _scene_graph_view_dict(view::SceneGraphView) + return Dict{String,Any}( + "schemaVersion" => 1, + "level" => string(view.level), + "metadata" => view.metadata, + "objects" => view.objects, + "instances" => view.instances, + "applications" => view.applications, + "executions" => view.executions, + "edges" => view.edges, + "modelLibrary" => view.model_library, + "initialization" => view.initialization, + "diagnostics" => view.diagnostics, + "cycles" => view.cycles, + "availableActions" => view.available_actions, + ) +end + +scene_graph_view_json(view::SceneGraphView) = + replace(JSON.json(_scene_graph_view_dict(view)), " "<\\/") + +scene_graph_view_json(scene_or_compiled; kwargs...) = + scene_graph_view_json(scene_graph_view(scene_or_compiled; kwargs...)) + +function _scene_graph_assets_dir() + return normpath(joinpath(dirname(dirname(dirname(@__FILE__))), "frontend", "dist")) +end + +function _scene_graph_vite_entry(assets_dir) + manifest_path = joinpath(assets_dir, ".vite", "manifest.json") + isfile(manifest_path) || return nothing + manifest = JSON.parse(read(manifest_path, String)) + for entry in values(manifest) + get(entry, "isEntry", false) == true && return entry + end + return get(manifest, "index.html", nothing) +end + +function _scene_graph_react_html(view::SceneGraphView) + assets_dir = _scene_graph_assets_dir() + entry = _scene_graph_vite_entry(assets_dir) + isnothing(entry) && return nothing + js_file = get(entry, "file", nothing) + isnothing(js_file) && return nothing + css_files = get(entry, "css", Any[]) + js = read(joinpath(assets_dir, js_file), String) + css = join([read(joinpath(assets_dir, css_file), String) for css_file in css_files], "\n") + return _scene_graph_html_document(view, css, js) +end + +function _scene_graph_html_document(view, css, js) + json = scene_graph_view_json(view) + html = raw""" + + + + + +PlantSimEngine Scene Graph + + + + +
+ + + +""" + return replace( + html, + "__PSE_SCENE_GRAPH_JSON__" => json, + "__PSE_SCENE_GRAPH_CSS__" => css, + "__PSE_SCENE_GRAPH_JS__" => js, + ) +end + +function _scene_graph_standalone_html(view::SceneGraphView) + css = raw""" +:root{font-family:Inter,ui-sans-serif,system-ui,sans-serif;color:#2d2722;background:#f4efe6}*{box-sizing:border-box}body{margin:0}.shell{height:100vh;display:grid;grid-template-rows:auto 1fr}.toolbar{display:flex;gap:10px;align-items:center;flex-wrap:wrap;padding:14px 18px;background:#fffaf2;border-bottom:1px solid #dccfbd}.brand{font-weight:800;font-size:19px;margin-right:auto}.toolbar button,.toolbar input{border:1px solid #cdbfaa;background:#fffaf2;border-radius:6px;padding:8px 10px;color:inherit}.toolbar button.active{border-color:#1f7a5a;color:#155b43;background:#e9f4ed}.content{display:grid;grid-template-columns:1fr 300px;min-height:0}.canvas{position:relative;overflow:auto;padding:24px}.grid{position:relative;display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:18px;z-index:2}.card{position:relative;background:#fffaf2;border:1px solid #ddcfbc;border-radius:7px;box-shadow:0 5px 18px #4a3e3020;padding:14px;min-height:150px;cursor:pointer}.card.cycle{border:2px solid #c94c3d}.card h3{margin:0 0 3px;font-size:16px}.muted{color:#776d64;font-size:12px}.badges{display:flex;gap:5px;flex-wrap:wrap;margin:10px 0}.badge{border:1px solid #d6c8b5;border-radius:999px;padding:2px 7px;font-size:11px}.ports{display:grid;grid-template-columns:1fr 1fr;gap:12px}.ports strong{font-size:10px;color:#776d64;text-transform:uppercase}.port{font-family:ui-monospace,monospace;font-size:11px;padding:3px 0}.inspector{overflow:auto;border-left:1px solid #dccfbd;background:#fffaf2;padding:18px}.inspector pre{white-space:pre-wrap;font-size:11px}.diagnostic{border-left:3px solid #c94c3d;padding:8px;margin:8px 0;background:#fff1eb}.empty{padding:60px;text-align:center;color:#776d64}@media(max-width:760px){.content{grid-template-columns:1fr}.inspector{display:none}.toolbar input{width:100%}} +""" + js = raw""" +const data=JSON.parse(document.getElementById('pse-scene-graph-data').textContent);const root=document.getElementById('root');let mode=data.level||'applications';let query='';let selected=null;const esc=v=>String(v??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));function cards(){if(mode==='topology')return data.objects.map(o=>({...o,title:o.name||String(o.objectId),subtitle:[o.kind,o.scale,o.instance].filter(Boolean).join(' · '),inputs:[],outputs:[]}));if(mode==='resolved')return data.executions.map(e=>({...e,title:e.applicationId+' @ '+e.objectId,subtitle:e.modelType,inputs:[],outputs:[]}));return data.applications.map(a=>({...a,title:a.name||a.applicationId,subtitle:a.modelType}));}function render(){const items=cards().filter(item=>JSON.stringify(item).toLowerCase().includes(query.toLowerCase()));root.innerHTML=`
PlantSimEngine Scene Graph
${items.length?`
${items.map(item=>{const appId=item.applicationId;const cyclic=data.cycles.some(c=>c.applicationIds.includes(appId));return `

${esc(item.title)}

${esc(item.subtitle)}
${item.targetCount!==undefined?`
${item.targetCount} targets${(item.targetScales||[]).map(v=>`${esc(v)}`).join('')}
Inputs${(item.inputs||[]).map(p=>`
${esc(p.name)}
`).join('')}
Outputs${(item.outputs||[]).map(p=>`
${esc(p.name)}
`).join('')}
`:''}
`}).join('')}
`:'
No matching graph items.
'}
`;root.querySelectorAll('[data-mode]').forEach(button=>button.onclick=()=>{mode=button.dataset.mode;selected=null;render()});root.querySelector('#search').oninput=event=>{query=event.target.value;render()};root.querySelectorAll('.card').forEach(card=>card.onclick=()=>{selected=cards().find(item=>item.id===card.dataset.id);render()});}render(); +""" + return _scene_graph_html_document(view, css, js) +end + +function scene_graph_view_html(view::SceneGraphView; renderer::Symbol=:react) + renderer == :standalone && return _scene_graph_standalone_html(view) + renderer == :react || error("Unsupported renderer `$(renderer)`. Use `:react` or `:standalone`.") + html = _scene_graph_react_html(view) + return isnothing(html) ? _scene_graph_standalone_html(view) : html +end + +scene_graph_view_html(scene_or_compiled; kwargs...) = + scene_graph_view_html(scene_graph_view(scene_or_compiled); kwargs...) + +""" + write_scene_graph_view(path, scene_or_view; level=:applications, + strict=false, renderer=:react) + +Write a self-contained static Scene graph viewer. The default renderer uses +the bundled frontend when available and otherwise falls back to the standalone +viewer. +""" +function write_scene_graph_view( + path::AbstractString, + scene_or_view; + level=:applications, + strict::Bool=false, + renderer::Symbol=:react, +) + view = scene_or_view isa SceneGraphView ? + scene_or_view : + scene_graph_view(scene_or_view; level=level, strict=strict) + full_path = abspath(path) + mkpath(dirname(full_path)) + write(full_path, scene_graph_view_html(view; renderer=renderer)) + return full_path +end diff --git a/test/Project.toml b/test/Project.toml index 18fc22273..52fe627ff 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -4,6 +4,8 @@ CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" +JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" MultiScaleTreeGraph = "dd4a991b-8a45-4075-bede-262ee62d5583" PlantMeteo = "4630fe09-e0fb-4da5-a846-781cb73437b6" PlantSimEngine = "9a576370-710b-4269-adf9-4f603a9c6423" diff --git a/test/fixtures/graph_editor_e2e_server.jl b/test/fixtures/graph_editor_e2e_server.jl new file mode 100644 index 000000000..d1e00f679 --- /dev/null +++ b/test/fixtures/graph_editor_e2e_server.jl @@ -0,0 +1,31 @@ +using PlantSimEngine +using PlantSimEngine.Examples +using HTTP + +abstract type AbstractReebE2EModel <: PlantSimEngine.AbstractModel end +PlantSimEngine.process_(::Type{AbstractReebE2EModel}) = :reeb_e2e + +struct ReebE2E{T} <: AbstractReebE2EModel + k::T +end + +ReebE2E() = ReebE2E(0.5) +PlantSimEngine.inputs_(::ReebE2E) = (aPPFD=-Inf,) +PlantSimEngine.outputs_(::ReebE2E) = (LAI=-Inf,) + +abstract type AbstractE2EConsumerModel <: PlantSimEngine.AbstractModel end +PlantSimEngine.process_(::Type{AbstractE2EConsumerModel}) = :e2e_consumer + +struct E2EConsumer <: AbstractE2EConsumerModel end +PlantSimEngine.inputs_(::E2EConsumer) = (aPPFD=-Inf,) +PlantSimEngine.outputs_(::E2EConsumer) = (consumed=-Inf,) + +session = edit_graph(; port=0, open_browser=false, autosave=false) +atexit(() -> try + close(session) +catch +end) + +println("PSE_GRAPH_EDITOR_URL=$(session.url)") +flush(stdout) +wait(Condition()) diff --git a/test/runtests.jl b/test/runtests.jl index 70a1e7bdd..d2800b10d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -5,6 +5,8 @@ using Test, Aqua using Tables, DataFrames, CSV using MultiScaleTreeGraph using PlantMeteo, Statistics +using HTTP +using JSON using Documenter # for doctests # There are 3 kinds of tests : @@ -12,7 +14,15 @@ using Documenter # for doctests # Integration tests (launched in Github Actions, they run PBP and XPalm tests) # Benchmarks both internal and downstream, located in the downstream folder, and run in another Github Action -@testset "Testing PlantSimEngine" begin +if length(ARGS) == 1 && endswith(only(ARGS), ".jl") + focused_file = basename(only(ARGS)) + focused_path = joinpath(@__DIR__, focused_file) + isfile(focused_path) || error("Unknown focused test file `$(focused_file)`.") + @testset "Focused: $(focused_file)" begin + include(focused_path) + end +else + @testset "Testing PlantSimEngine" begin Aqua.test_all(PlantSimEngine, ambiguities=false) Aqua.test_ambiguities([PlantSimEngine]) @@ -20,6 +30,66 @@ using Documenter # for doctests include("test-unified-scene-object-api.jl") end + @testset "Scene/Object API stabilization" begin + include("test-scene-api-stabilization.jl") + end + + @testset "Scene hard calls" begin + include("test-scene-hard-calls.jl") + end + + @testset "Scene numerical parity" begin + include("test-scene-numerical-parity.jl") + end + + @testset "Scene status initialization" begin + include("test-scene-status-initialization.jl") + end + + @testset "Scene output boundaries" begin + include("test-scene-output-boundaries.jl") + end + + @testset "Scene time validation" begin + include("test-scene-time-validation.jl") + end + + @testset "Scene runtime matrix" begin + include("test-scene-runtime-matrix.jl") + end + + @testset "Scene meteorological sampling" begin + include("test-scene-meteo-sampling.jl") + end + + @testset "Scene temporal reducers" begin + include("test-scene-temporal-reducers.jl") + end + + @testset "Scene binding inference" begin + include("test-scene-binding-inference.jl") + end + + @testset "Scene multirate integration" begin + include("test-scene-multirate-integration.jl") + end + + @testset "Scene configuration errors" begin + include("test-scene-configuration-errors.jl") + end + + @testset "Scene graph viewer" begin + include("test-scene-graph-view.jl") + end + + @testset "Scene graph editor extension" begin + include("test-scene-graph-editor-extension.jl") + end + + @testset "Model contract" begin + include("test-model-contract.jl") + end + @testset "ModelSpec Updates" begin include("test-updates.jl") end @@ -65,4 +135,5 @@ using Documenter # for doctests doctest(PlantSimEngine; manual=false) end end + end end diff --git a/test/test-TimeStepTable.jl b/test/test-TimeStepTable.jl index 02c601bff..0122c04bf 100644 --- a/test/test-TimeStepTable.jl +++ b/test/test-TimeStepTable.jl @@ -1,6 +1,6 @@ -@testset "Testing TimeStepTable{Status}" begin +@testset "Testing Advanced.TimeStepTable{Status}" begin vars = Status(Ra_SW_f=13.747, sky_fraction=1.0, d=0.03, aPPFD=1500) - ts = TimeStepTable([vars, vars]) + ts = Advanced.TimeStepTable([vars, vars]) @test Tables.istable(typeof(ts)) diff --git a/test/test-model-contract.jl b/test/test-model-contract.jl new file mode 100644 index 000000000..f1fe753bb --- /dev/null +++ b/test/test-model-contract.jl @@ -0,0 +1,42 @@ +using Dates +using PlantSimEngine +using Test + +PlantSimEngine.@process "contract_defaults" verbose = false + +struct ContractDefaultsModel <: AbstractContract_DefaultsModel end +struct ContractExplicitModel <: AbstractContract_DefaultsModel end + +PlantSimEngine.inputs_(::ContractDefaultsModel) = NamedTuple() +PlantSimEngine.outputs_(::ContractDefaultsModel) = (value=0,) +PlantSimEngine.inputs_(::ContractExplicitModel) = NamedTuple() +PlantSimEngine.outputs_(::ContractExplicitModel) = (value=0,) +PlantSimEngine.timespec(::Type{<:ContractExplicitModel}) = ClockSpec(2, 1) +PlantSimEngine.output_policy(::Type{<:ContractExplicitModel}) = (value=Aggregate(),) +PlantSimEngine.timestep_hint(::Type{<:ContractExplicitModel}) = (preferred=Dates.Hour(2),) +PlantSimEngine.meteo_hint(::Type{<:ContractExplicitModel}) = (window=Dates.Hour(2),) +PlantSimEngine.meteo_inputs_(::ContractExplicitModel) = (T=0,) +PlantSimEngine.meteo_outputs_(::ContractExplicitModel) = (T=0,) + +@testset "direct model trait defaults" begin + model = ContractDefaultsModel() + @test process(model) == :contract_defaults + @test application_name(ModelSpec(model)) === nothing + default_scene = Scene(model) + @test only(explain_scene_applications(Advanced.refresh_bindings!(default_scene))).application_id == + :contract_defaults + @test timespec(model) == ClockSpec(1.0, 0.0) + @test output_policy(model) == NamedTuple() + @test timestep_hint(model) === nothing + @test meteo_hint(model) === nothing + @test meteo_inputs_(model) == NamedTuple() + @test meteo_outputs_(model) == NamedTuple() + + explicit = ContractExplicitModel() + @test timespec(explicit) == ClockSpec(2, 1) + @test output_policy(explicit).value isa Aggregate + @test timestep_hint(explicit) == (preferred=Dates.Hour(2),) + @test meteo_hint(explicit) == (window=Dates.Hour(2),) + @test meteo_inputs_(explicit) == (T=0,) + @test meteo_outputs_(explicit) == (T=0,) +end diff --git a/test/test-scene-api-stabilization.jl b/test/test-scene-api-stabilization.jl new file mode 100644 index 000000000..b69d815dd --- /dev/null +++ b/test/test-scene-api-stabilization.jl @@ -0,0 +1,365 @@ +using Dates +using PlantSimEngine +using Test + +PlantSimEngine.@process "stabilization_source" verbose = false + +struct StabilizationSourceModel <: AbstractStabilization_SourceModel end + +PlantSimEngine.inputs_(::StabilizationSourceModel) = NamedTuple() +PlantSimEngine.outputs_(::StabilizationSourceModel) = (signal=0.0,) + +function PlantSimEngine.run!( + ::StabilizationSourceModel, + models, + status, + meteo, + constants, + extra, +) + status.signal += 1 + return nothing +end + +PlantSimEngine.@process "stabilization_consumer" verbose = false + +struct StabilizationConsumerModel <: AbstractStabilization_ConsumerModel end + +PlantSimEngine.inputs_(::StabilizationConsumerModel) = (signal=0.0, supplied=0.0) +PlantSimEngine.outputs_(::StabilizationConsumerModel) = (observed=0.0,) + +function PlantSimEngine.run!( + ::StabilizationConsumerModel, + models, + status, + meteo, + constants, + extra, +) + status.observed = status.signal + status.supplied + return nothing +end + +PlantSimEngine.@process "stabilization_context" verbose = false + +struct StabilizationContextModel <: AbstractStabilization_ContextModel end + +PlantSimEngine.inputs_(::StabilizationContextModel) = NamedTuple() +PlantSimEngine.outputs_(::StabilizationContextModel) = (seen_revision=0,) + +function PlantSimEngine.run!( + ::StabilizationContextModel, + models, + status, + meteo, + constants, + extra, +) + status.seen_revision = Advanced.scene_revision(runtime_scene(extra)) + return nothing +end + +PlantSimEngine.@process "stabilization_environment" verbose = false + +struct StabilizationEnvironmentModel <: AbstractStabilization_EnvironmentModel end + +PlantSimEngine.inputs_(::StabilizationEnvironmentModel) = NamedTuple() +PlantSimEngine.outputs_(::StabilizationEnvironmentModel) = (temperature_seen=0.0,) +PlantSimEngine.meteo_inputs_(::StabilizationEnvironmentModel) = (T=0.0,) + +function PlantSimEngine.run!( + ::StabilizationEnvironmentModel, + models, + status, + meteo, + constants, + extra, +) + status.temperature_seen = meteo.T + return nothing +end + +@testset "one-object lowering and initialization report" begin + scene = Scene( + StabilizationSourceModel(), + StabilizationConsumerModel(); + status=(supplied=2.0,), + ) + + @test length(scene_objects(scene)) == 1 + @test length(scene.applications) == 2 + @test only(scene_objects(scene)).id == ObjectId(:scene) + + explicit_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene, name=:scene, status=Status(supplied=2.0)); + applications=( + ModelSpec(StabilizationSourceModel()) |> AppliesTo(One(name=:scene)), + ModelSpec(StabilizationConsumerModel()) |> AppliesTo(One(name=:scene)), + ), + ) + concise_applications = explain_scene_applications(scene) + explicit_applications = explain_scene_applications(explicit_scene) + @test getproperty.(concise_applications, :application_id) == + getproperty.(explicit_applications, :application_id) + @test getproperty.(concise_applications, :target_ids) == + getproperty.(explicit_applications, :target_ids) + + report = explain_initialization(scene) + dispositions = Dict( + (row.application_id, row.variable, row.role) => row.disposition + for row in report + ) + @test dispositions[(:stabilization_source, :signal, :output)] == :generated + @test dispositions[(:stabilization_consumer, :signal, :input)] == + :producer_bound + @test dispositions[(:stabilization_consumer, :supplied, :input)] == :supplied + @test dispositions[(:stabilization_consumer, :observed, :output)] == :generated + supplied_row = only( + row for row in report + if row.application_id == :stabilization_consumer && + row.variable == :supplied + ) + @test supplied_row.origin == :status + @test supplied_row.expected_type == Float64 + @test supplied_row.provided_type == Float64 + + simulation = run!(scene) + @test only(scene_objects(scene)).status.observed == 3.0 + @test runtime_scene(scene) === scene + @test runtime_scene(simulation) === scene + @test length(explain_scene_applications(simulation)) == 2 + @test length(explain_initialization(simulation)) == length(report) + @test !isempty(explain_execution_plan(simulation)) + + unresolved_scene = Scene(StabilizationConsumerModel()) + unresolved = filter( + row -> row.role == :input && row.disposition == :unresolved, + explain_initialization(unresolved_scene), + ) + @test Set(row.variable for row in unresolved) == Set((:signal, :supplied)) + @test all(row -> row.origin == :missing, unresolved) + @test all(row -> occursin("add `Inputs", row.detail), unresolved) + @test_throws "Missing required scene/object input" run!(unresolved_scene) + + environment_scene = Scene( + StabilizationEnvironmentModel(); + environment=(T=20.0, duration=3600.0), + ) + temperature_row = only( + row for row in explain_initialization(environment_scene) + if row.role == :environment_input && row.variable == :T + ) + @test temperature_row.disposition == :environment_bound +end + +@testset "public namespace boundary" begin + public_names = names(PlantSimEngine) + advanced_names = names(PlantSimEngine.Advanced) + for internal_name in ( + :SceneRegistry, + :CompiledScene, + :CompiledSceneApplication, + :compile_scene, + :refresh_bindings!, + :bindings_dirty, + ) + @test internal_name ∉ public_names + @test internal_name ∈ advanced_names + end +end + +@testset "sanctioned runtime scene access" begin + scene = Scene(StabilizationContextModel()) + run!(scene) + @test only(scene_objects(scene)).status.seen_revision == Advanced.scene_revision(scene) +end + +@testset "explicit output retention and continuation" begin + default_scene = Scene(StabilizationSourceModel()) + @test isempty(explain_output_retention(default_scene)) + @test only(explain_output_retention(default_scene; outputs=:all)).reasons == + (:all_outputs,) + default_simulation = run!(default_scene; steps=2) + @test isempty(scene_outputs(default_simulation)) + @test current_step(default_simulation) == 2 + + retained_scene = Scene(StabilizationSourceModel()) + simulation = run!(retained_scene; steps=2, outputs=:all) + @test current_step(simulation) == 2 + @test last.(scene_outputs(simulation)[ + (:stabilization_source, ObjectId(:scene), :signal) + ]) == [1.0, 2.0] + + @test continue!(simulation; steps=2) === simulation + @test current_step(simulation) == 4 + @test last.(scene_outputs(simulation)[ + (:stabilization_source, ObjectId(:scene), :signal) + ]) == [1.0, 2.0, 3.0, 4.0] + + @test step!(simulation) === simulation + @test current_step(simulation) == 5 + @test last(scene_outputs(simulation)[ + (:stabilization_source, ObjectId(:scene), :signal) + ]) == (5.0, 5.0) + + fresh_result = run!(retained_scene; outputs=:all) + @test current_step(fresh_result) == 1 + @test only(scene_outputs(fresh_result)[ + (:stabilization_source, ObjectId(:scene), :signal) + ]) == (1.0, 6.0) +end + +@testset "self and subtree have distinct scope semantics" begin + scene = Scene( + Object(:plant; scale=:Plant), + Object(:leaf_1; scale=:Leaf, parent=:plant), + Object(:leaf_2; scale=:Leaf, parent=:plant), + ) + + @test resolve_object_ids(scene, One(Self()); context=:plant) == + [ObjectId(:plant)] + @test resolve_object_ids(scene, Many(Subtree()); context=:plant) == + ObjectId[ObjectId(:leaf_1), ObjectId(:leaf_2), ObjectId(:plant)] + @test resolve_object_ids(scene, Many(scale=:Leaf, within=Self()); context=:plant) == + ObjectId[] + @test resolve_object_ids(scene, Many(scale=:Leaf, within=Subtree()); context=:plant) == + ObjectId[ObjectId(:leaf_1), ObjectId(:leaf_2)] + @test_throws "No matching ancestor" resolve_object_ids( + scene, + Many(within=Ancestor(scale=:Plant)); + context=:plant, + ) +end + +@testset "repeated applications require explicit identity" begin + scene = Scene( + Object(:leaf; scale=:Leaf); + applications=( + ModelSpec(StabilizationSourceModel()) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(StabilizationSourceModel()) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + + @test_throws "unnamed applications for process `stabilization_source`" Advanced.compile_scene(scene) + + named_scene = Scene( + Object(:leaf; scale=:Leaf); + applications=( + ModelSpec(StabilizationSourceModel(); name=:source_a) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(StabilizationSourceModel(); name=:source_b) |> + AppliesTo(One(scale=:Leaf)) |> + OutputRouting(; signal=:stream_only), + ), + ) + @test getproperty.(explain_scene_applications(named_scene), :application_id) == + [:source_a, :source_b] + @test explain_schedule(named_scene) isa Vector + @test explain_bindings(named_scene) isa Vector + @test explain_calls(named_scene) isa Vector + @test explain_model_bundles(named_scene) isa Vector + @test explain_writers(named_scene) isa Vector + + ambiguous_process_scene = Scene( + Object(:leaf; scale=:Leaf, status=Status(supplied=0.0)); + applications=( + ModelSpec(StabilizationSourceModel(); name=:source_a) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(StabilizationSourceModel(); name=:source_b) |> + AppliesTo(One(scale=:Leaf)) |> + OutputRouting(; signal=:stream_only), + ModelSpec(StabilizationConsumerModel(); name=:consumer) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs(:signal => One(within=Self(), process=:stabilization_source)), + ), + ) + @test_throws "matched several source applications `[:source_a, :source_b]`" explain_bindings( + ambiguous_process_scene, + ) + + ordered_writer_scene = Scene( + Object(:leaf; scale=:Leaf, status=Status(supplied=0.0)); + applications=( + ModelSpec(StabilizationSourceModel(); name=:source_a) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(StabilizationSourceModel(); name=:source_b) |> + AppliesTo(One(scale=:Leaf)) |> + Updates(:signal; after=:source_a), + ModelSpec(StabilizationConsumerModel(); name=:consumer) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs( + PreviousTimeStep(:signal) => One(within=Self(), var=:signal), + ), + ), + ) + ordered_binding = only( + row for row in explain_bindings(ordered_writer_scene) + if row.application_id == :consumer && row.input == :signal + ) + @test ordered_binding.source_application_ids == [:source_b] +end + +@testset "invalid reparenting is atomic" begin + scene = Scene( + Object(:plant; scale=:Plant), + Object(:leaf; scale=:Leaf, parent=:plant), + ) + + @test_throws "below its descendant" reparent_object!(scene, :plant, :leaf) + @test only(resolve_object_ids(scene, One(Relation(:parent)); context=:leaf)) == + ObjectId(:plant) + @test resolve_object_ids(scene, Many(Relation(:children)); context=:plant) == + [ObjectId(:leaf)] + + @test_throws "to itself" reparent_object!(scene, :plant, :plant) + @test isnothing(only(scene_objects(scene; scale=:Plant)).parent) +end + +@testset "continuation refreshes lifecycle targets and preserves history" begin + scene = Scene( + Object(:leaf_1; scale=:Leaf); + applications=( + ModelSpec(StabilizationSourceModel(); name=:source) |> + AppliesTo(Many(scale=:Leaf)), + ), + ) + request = OutputRequest( + Many(scale=:Leaf), + :signal; + name=:leaf_signal, + application=:source, + ) + simulation = run!(scene; outputs=request) + + register_object!(scene, Object(:leaf_2; scale=:Leaf)) + continue!(simulation) + remove_object!(scene, :leaf_1) + continue!(simulation) + + rows = collect_outputs(simulation, :leaf_signal; sink=nothing) + leaf_1_rows = filter(row -> row.object_id == :leaf_1, rows) + leaf_2_rows = filter(row -> row.object_id == :leaf_2, rows) + @test getproperty.(leaf_1_rows, :timestep) == [1, 2] + @test getproperty.(leaf_1_rows, :value) == [1.0, 2.0] + @test getproperty.(leaf_2_rows, :timestep) == [2, 3] + @test getproperty.(leaf_2_rows, :value) == [1.0, 2.0] + @test all(row -> row.scale == :Leaf, rows) +end + +@testset "output retention allocation boundary" begin + scene = Scene( + (Object(Symbol(:leaf_, i); scale=:Leaf) for i in 1:100)...; + applications=( + ModelSpec(StabilizationSourceModel(); name=:source) |> + AppliesTo(Many(scale=:Leaf)), + ), + ) + + run!(scene; steps=2, outputs=:none) + run!(scene; steps=2, outputs=:all) + none_allocations = @allocated run!(scene; steps=10, outputs=:none) + all_allocations = @allocated run!(scene; steps=10, outputs=:all) + @test none_allocations < all_allocations +end diff --git a/test/test-scene-binding-inference.jl b/test/test-scene-binding-inference.jl new file mode 100644 index 000000000..922684b13 --- /dev/null +++ b/test/test-scene-binding-inference.jl @@ -0,0 +1,102 @@ +using PlantSimEngine +using Test + +PlantSimEngine.@process "lineage_source" verbose = false +PlantSimEngine.@process "lineage_consumer" verbose = false + +struct LineageSourceModel{T} <: AbstractLineage_SourceModel + value::T +end +struct LineageConsumerModel <: AbstractLineage_ConsumerModel end +PlantSimEngine.inputs_(::LineageSourceModel) = NamedTuple() +PlantSimEngine.outputs_(::LineageSourceModel) = (signal=0.0,) +function PlantSimEngine.run!(model::LineageSourceModel, models, status, meteo, constants, extra) + status.signal = model.value +end +PlantSimEngine.inputs_(::LineageConsumerModel) = (signal=0.0,) +PlantSimEngine.outputs_(::LineageConsumerModel) = (observed=0.0,) +function PlantSimEngine.run!(::LineageConsumerModel, models, status, meteo, constants, extra) + status.observed = status.signal +end + +@testset "producer lineage, scope, and ambiguity" begin + same_object = Scene( + Object(:scene; scale=:Scene), + Object(:plant; scale=:Plant, parent=:scene), + Object(:soil; scale=:Soil, parent=:scene), + Object(:leaf; scale=:Leaf, parent=:plant); + applications=( + ModelSpec(LineageSourceModel(1.0); name=:plant_source) |> + AppliesTo(One(scale=:Plant)), + ModelSpec(LineageSourceModel(2.0); name=:soil_source) |> + AppliesTo(One(scale=:Soil)), + ModelSpec(LineageSourceModel(3.0); name=:leaf_source) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(LineageConsumerModel(); name=:leaf_consumer) |> + AppliesTo(One(scale=:Leaf)), + ), + ) + binding = only( + row for row in explain_bindings(Advanced.refresh_bindings!(same_object)) + if row.application_id == :leaf_consumer + ) + @test binding.origin == :inferred_same_object + @test binding.source_ids == [:leaf] + @test binding.source_application_ids == [:leaf_source] + run!(same_object) + @test only(scene_objects(same_object; scale=:Leaf)).status.observed == 3.0 + + ambiguous = Scene( + Object(:scene; scale=:Scene), + Object(:plant; scale=:Plant, parent=:scene), + Object(:soil; scale=:Soil, parent=:scene), + Object(:leaf; scale=:Leaf, parent=:plant); + applications=( + ModelSpec(LineageSourceModel(1.0); name=:plant_source) |> + AppliesTo(One(scale=:Plant)), + ModelSpec(LineageSourceModel(2.0); name=:soil_source) |> + AppliesTo(One(scale=:Soil)), + ModelSpec(LineageConsumerModel(); name=:leaf_consumer) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs(:signal => One( + within=SceneScope(), + process=:lineage_source, + var=:signal, + )), + ), + ) + error = try + Advanced.refresh_bindings!(ambiguous) + nothing + catch exception + sprint(showerror, exception) + end + @test occursin("Expected exactly one object", error) + @test occursin("plant", error) + @test occursin("soil", error) + + explicit = Scene( + Object(:scene; scale=:Scene), + Object(:plant; scale=:Plant, parent=:scene), + Object(:soil; scale=:Soil, parent=:scene), + Object(:leaf; scale=:Leaf, parent=:plant); + applications=( + ModelSpec(LineageSourceModel(1.0); name=:plant_source) |> + AppliesTo(One(scale=:Plant)), + ModelSpec(LineageSourceModel(2.0); name=:soil_source) |> + AppliesTo(One(scale=:Soil)), + ModelSpec(LineageConsumerModel(); name=:leaf_consumer) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs(:signal => One( + scale=:Plant, + within=Ancestor(scale=:Plant), + application=:plant_source, + var=:signal, + )), + ), + ) + explicit_binding = only(explain_bindings(Advanced.refresh_bindings!(explicit))) + @test explicit_binding.source_ids == [:plant] + run!(explicit) + @test only(scene_objects(explicit; scale=:Leaf)).status.observed == 1.0 +end diff --git a/test/test-scene-configuration-errors.jl b/test/test-scene-configuration-errors.jl new file mode 100644 index 000000000..dcabfd12d --- /dev/null +++ b/test/test-scene-configuration-errors.jl @@ -0,0 +1,78 @@ +using Dates +using PlantSimEngine +using Test + +PlantSimEngine.@process "invalid_hint_probe" verbose = false +PlantSimEngine.@process "configuration_probe" verbose = false +PlantSimEngine.@process "configuration_consumer" verbose = false +struct InvalidMeteoHintProbeModel <: AbstractInvalid_Hint_ProbeModel end +struct InvalidTimestepHintProbeModel <: AbstractInvalid_Hint_ProbeModel end +struct ConfigurationProbeModel <: AbstractConfiguration_ProbeModel end +struct ConfigurationConsumerModel <: AbstractConfiguration_ConsumerModel end +PlantSimEngine.inputs_(::Union{InvalidMeteoHintProbeModel,InvalidTimestepHintProbeModel}) = NamedTuple() +PlantSimEngine.outputs_(::Union{InvalidMeteoHintProbeModel,InvalidTimestepHintProbeModel}) = (value=0.0,) +PlantSimEngine.meteo_hint(::Type{<:InvalidMeteoHintProbeModel}) = 42 +PlantSimEngine.timestep_hint(::Type{<:InvalidTimestepHintProbeModel}) = "hourly" +PlantSimEngine.inputs_(::ConfigurationProbeModel) = NamedTuple() +PlantSimEngine.outputs_(::ConfigurationProbeModel) = (value=0.0,) +PlantSimEngine.meteo_inputs_(::ConfigurationProbeModel) = (T=0.0,) +PlantSimEngine.inputs_(::ConfigurationConsumerModel) = (value=0.0,) +PlantSimEngine.outputs_(::ConfigurationConsumerModel) = (observed=0.0,) + +function invalid_hint_scene(model) + return Scene( + Object(:leaf; scale=:Leaf); + applications=( + ModelSpec(model; name=:invalid_hint) |> AppliesTo(One(scale=:Leaf)), + ), + environment=(duration=Hour(1),), + ) +end + +@testset "current configuration errors" begin + @test_throws "Unsupported reducer value" Aggregate(42) + monthly_scene = Scene( + Object(:leaf; scale=:Leaf); + applications=( + ModelSpec(InvalidTimestepHintProbeModel(); name=:monthly) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Month(1)), + ), + environment=(duration=Hour(1),), + ) + @test_throws "Unsupported non-fixed period" Advanced.refresh_bindings!(monthly_scene) + @test_throws "Invalid meteo_hint" Advanced.refresh_bindings!( + invalid_hint_scene(InvalidMeteoHintProbeModel()) + ) + @test_throws "Invalid timestep_hint" Advanced.refresh_bindings!( + invalid_hint_scene(InvalidTimestepHintProbeModel()) + ) + invalid_environment = Scene( + Object(:leaf; scale=:Leaf); + applications=( + ModelSpec(ConfigurationProbeModel(); name=:probe) |> + AppliesTo(One(scale=:Leaf)) |> + Environment(provider=:global, sources=42), + ), + environment=(T=20.0, duration=Hour(1)), + ) + @test_throws Exception Advanced.refresh_bindings!(invalid_environment) + + invalid_policy = Scene( + Object(:source; scale=:Leaf, name=:source), + Object(:consumer; scale=:Leaf, name=:consumer); + applications=( + ModelSpec(ConfigurationProbeModel(); name=:source) |> + AppliesTo(One(name=:source)), + ModelSpec(ConfigurationConsumerModel(); name=:consumer) |> + AppliesTo(One(name=:consumer)) |> + Inputs(:value => One( + name=:source, + within=SceneScope(), + policy=:latest, + )), + ), + environment=(T=20.0, duration=Hour(1)), + ) + @test_throws "Unsupported scene input policy" Advanced.refresh_bindings!(invalid_policy) +end diff --git a/test/test-scene-graph-editor-extension.jl b/test/test-scene-graph-editor-extension.jl new file mode 100644 index 000000000..67409446c --- /dev/null +++ b/test/test-scene-graph-editor-extension.jl @@ -0,0 +1,223 @@ +abstract type AbstractEditorSourceModel <: PlantSimEngine.AbstractModel end +abstract type AbstractEditorConsumerModel <: PlantSimEngine.AbstractModel end +PlantSimEngine.process_(::Type{AbstractEditorSourceModel}) = :editor_source +PlantSimEngine.process_(::Type{AbstractEditorConsumerModel}) = :editor_consumer + +struct EditorSourceModel <: AbstractEditorSourceModel end +PlantSimEngine.inputs_(::EditorSourceModel) = (driver=-Inf,) +PlantSimEngine.outputs_(::EditorSourceModel) = (signal=-Inf,) + +struct EditorConsumerModel <: AbstractEditorConsumerModel end +PlantSimEngine.inputs_(::EditorConsumerModel) = (signal=-Inf,) +PlantSimEngine.outputs_(::EditorConsumerModel) = (result=-Inf,) + +@testset "session lifecycle and edits" begin + scene = Scene( + Object(:leaf; name=:leaf, scale=:Leaf, status=Status(driver=1.0)); + applications=( + ModelSpec(EditorSourceModel(); name=:source) |> + AppliesTo(One(name=:leaf)), + ), + ) + session = edit_graph(scene; port=0, open_browser=false, autosave=false) + try + @test current_scene(session) !== scene + @test occursin("Open in browser:", sprint(show, MIME"text/plain"(), session)) + @test occursin("Quit session: close(session)", sprint(show, MIME"text/plain"(), session)) + + health = HTTP.get("http://$(session.host):$(session.port)/health") + @test health.status == 200 + @test JSON.parse(String(health.body))["ok"] + + state = HTTP.get("http://$(session.host):$(session.port)/state?token=$(session.token)") + payload = JSON.parse(String(state.body)) + @test payload["ok"] + @test payload["graph"]["metadata"]["applicationCount"] == 1 + @test occursin("scene = Scene", payload["sceneCode"]) + + consumer_spec = ModelSpec(EditorConsumerModel(); name=:consumer) |> + AppliesTo(One(name=:leaf)) + apply_edit!(session, AddSceneApplication(consumer_spec)) + @test length(current_scene(session).applications) == 2 + @test !isempty(session.history) + + undo!(session) + @test length(current_scene(session).applications) == 1 + redo!(session) + @test length(current_scene(session).applications) == 2 + finally + close(session) + end +end + +@testset "empty session and automatic save" begin + output_path = joinpath(mktempdir(), "scene.generated.jl") + session = edit_graph( + ; + port=0, + open_browser=false, + autosave=true, + save_path=output_path, + ) + try + @test isempty(current_scene(session).applications) + @test isfile(output_path) + before = read(output_path, String) + @test occursin("scene = Scene", before) + + apply_edit!(session, AddSceneObject(Object(:plant; name=:plant, scale=:Plant))) + after = read(output_path, String) + @test after != before + @test occursin("Object(:plant", after) + @test !isnothing(session.autosave_path) + @test isfile(session.autosave_path) + @test output_path in session.recent_paths + + snapshot_path = joinpath(mktempdir(), "saved-scene.jl") + write(snapshot_path, Base.get_extension(PlantSimEngine, :PlantSimEngineGraphEditorExt)._scene_to_julia(current_scene(session))) + apply_edit!(session, AddSceneObject(Object(:soil; name=:soil, scale=:Soil))) + @test length(scene_objects(current_scene(session))) == 2 + response = Base.get_extension(PlantSimEngine, :PlantSimEngineGraphEditorExt)._handle_command!(session, Dict( + "action" => "open_scene_code", + "path" => snapshot_path, + )) + @test response["ok"] + @test length(scene_objects(current_scene(session))) == 1 + @test session.save_path == snapshot_path + @test first(session.recent_paths) == snapshot_path + finally + close(session) + end +end + + +@testset "JSON editor commands round-trip through Julia" begin + editor_extension = Base.get_extension(PlantSimEngine, :PlantSimEngineGraphEditorExt) + @test !isnothing(editor_extension) + session = edit_graph(; port=0, open_browser=false, autosave=false) + try + object_response = editor_extension._handle_command!(session, Dict( + "action" => "edit", + "kind" => "add_object", + "objectId" => "leaf", + "configuration" => Dict( + "scale" => "Leaf", + "kind" => "organ", + "species" => nothing, + "name" => "leaf", + "parent" => nothing, + ), + )) + @test object_response["ok"] + @test length(scene_objects(current_scene(session))) == 1 + + source_response = editor_extension._handle_command!(session, Dict( + "action" => "edit", + "kind" => "add_application", + "name" => "source", + "modelType" => string(EditorSourceModel), + "parameters" => Dict(), + "selector" => Dict( + "multiplicity" => "one", + "criteria" => Dict( + "selectors" => [Dict("type" => "Scale", "scale" => "Leaf")], + ), + ), + "timestep" => Dict("mode" => "default"), + )) + @test source_response["ok"] + + consumer_response = editor_extension._handle_command!(session, Dict( + "action" => "edit", + "kind" => "add_application", + "name" => "consumer", + "modelType" => string(EditorConsumerModel), + "parameters" => Dict(), + "selector" => Dict( + "multiplicity" => "one", + "criteria" => Dict("selectors" => Any[], "name" => "leaf"), + ), + "timestep" => Dict("mode" => "clock", "dt" => "2.0", "phase" => "0.0"), + )) + @test consumer_response["ok"] + + binding_response = editor_extension._handle_command!(session, Dict( + "action" => "edit", + "kind" => "set_input_binding", + "applicationId" => "consumer", + "input" => "signal", + "selector" => Dict( + "multiplicity" => "one", + "criteria" => Dict( + "selectors" => Any[], + "within" => Dict("type" => "Self"), + "application" => "source", + "var" => "signal", + "policy" => Dict("type" => "HoldLast"), + ), + ), + )) + @test binding_response["ok"] + @test binding_response["graph"]["metadata"]["bindingCount"] == 1 + @test binding_response["graph"]["metadata"]["applicationCount"] == 2 + + status_response = editor_extension._handle_command!(session, Dict( + "action" => "edit", + "kind" => "set_object_statuses", + "objectIds" => ["leaf"], + "variable" => "driver", + "value" => Dict("type" => "float", "value" => "3.5"), + )) + @test status_response["ok"] + @test only(scene_objects(current_scene(session))).status.driver == 3.5 + + undo_response = editor_extension._handle_command!(session, Dict("action" => "undo")) + @test undo_response["ok"] + restored_status = only(scene_objects(current_scene(session))).status + @test isnothing(restored_status) || !(:driver in propertynames(restored_status)) + finally + close(session) + end +end + + +@testset "generated Julia code reconstructs templates and overrides" begin + editor_extension = Base.get_extension(PlantSimEngine, :PlantSimEngineGraphEditorExt) + template = ObjectTemplate( + ( + ModelSpec(EditorSourceModel(); name=:source) |> + AppliesTo(Many(scale=:Leaf)), + ); + kind=:plant, + species=:test_species, + ) + original = Scene( + ObjectInstance( + :plant_a, + template; + root=Object(:plant_a; scale=:Plant), + objects=(Object(:leaf_a; scale=:Leaf, parent=:plant_a, status=Status(driver=1.0)),), + ), + ObjectInstance( + :plant_b, + template; + root=Object(:plant_b; scale=:Plant), + objects=(Object(:leaf_b; scale=:Leaf, parent=:plant_b, status=Status(driver=1.0)),), + overrides=(source=EditorSourceModel(),), + object_overrides=(Override(object=:leaf_b, application=:source, model=EditorSourceModel()),), + ), + ) + code = editor_extension._scene_to_julia(original) + @test occursin("template_1 = ObjectTemplate", code) + @test occursin("instances = (", code) + @test occursin("Override(", code) + @test !occursin("ObjectModelOverrides", code) + + restored = Base.include_string(Main, code, "generated_scene_editor_test.jl") + original_view = scene_graph_view(original) + restored_view = scene_graph_view(restored) + @test restored_view.metadata["objectCount"] == original_view.metadata["objectCount"] + @test restored_view.metadata["instanceCount"] == original_view.metadata["instanceCount"] + @test Set(application["applicationId"] for application in restored_view.applications) == + Set(application["applicationId"] for application in original_view.applications) +end diff --git a/test/test-scene-graph-view.jl b/test/test-scene-graph-view.jl new file mode 100644 index 000000000..ea3aeac0b --- /dev/null +++ b/test/test-scene-graph-view.jl @@ -0,0 +1,431 @@ +abstract type AbstractSceneGraphSourceModel <: PlantSimEngine.AbstractModel end +abstract type AbstractSceneGraphConsumerModel <: PlantSimEngine.AbstractModel end +abstract type AbstractSceneGraphCycleAModel <: PlantSimEngine.AbstractModel end +abstract type AbstractSceneGraphCycleBModel <: PlantSimEngine.AbstractModel end + +PlantSimEngine.process_(::Type{AbstractSceneGraphSourceModel}) = :scene_graph_source +PlantSimEngine.process_(::Type{AbstractSceneGraphConsumerModel}) = :scene_graph_consumer +PlantSimEngine.process_(::Type{AbstractSceneGraphCycleAModel}) = :scene_graph_cycle_a +PlantSimEngine.process_(::Type{AbstractSceneGraphCycleBModel}) = :scene_graph_cycle_b + +struct SceneGraphSourceModel{T} <: AbstractSceneGraphSourceModel + coefficient::T +end + +SceneGraphSourceModel() = SceneGraphSourceModel(2.0) +PlantSimEngine.inputs_(::SceneGraphSourceModel) = (driver=-Inf,) +PlantSimEngine.outputs_(::SceneGraphSourceModel) = (signal=-Inf,) + +struct SceneGraphConsumerModel <: AbstractSceneGraphConsumerModel end +PlantSimEngine.inputs_(::SceneGraphConsumerModel) = (signal=-Inf,) +PlantSimEngine.outputs_(::SceneGraphConsumerModel) = (result=-Inf,) + +struct SceneGraphCycleAModel <: AbstractSceneGraphCycleAModel end +PlantSimEngine.inputs_(::SceneGraphCycleAModel) = (y=-Inf,) +PlantSimEngine.outputs_(::SceneGraphCycleAModel) = (x=-Inf,) + +struct SceneGraphCycleBModel <: AbstractSceneGraphCycleBModel end +PlantSimEngine.inputs_(::SceneGraphCycleBModel) = (x=-Inf,) +PlantSimEngine.outputs_(::SceneGraphCycleBModel) = (y=-Inf,) + +@testset "Scene graph discovery" begin + @test AbstractSceneGraphSourceModel in available_processes() + @test SceneGraphSourceModel in available_models(:scene_graph_source) + + descriptor = model_descriptor(SceneGraphSourceModel) + @test descriptor["process"] == "scene_graph_source" + @test descriptor["inputs"]["driver"] == "-Inf" + @test descriptor["outputs"]["signal"] == "-Inf" + @test descriptor["constructor"]["hasZeroArgConstructor"] + @test descriptor["constructor"]["fields"][1]["name"] == "coefficient" +end + +@testset "Scene graph application and resolved views" begin + scene = Scene( + Object(:leaf; name=:leaf, scale=:Leaf, kind=:organ, status=Status(driver=1.0)); + applications=( + ModelSpec(SceneGraphSourceModel(); name=:source) |> + AppliesTo(One(name=:leaf)), + ModelSpec(SceneGraphConsumerModel(); name=:consumer) |> + AppliesTo(One(name=:leaf)), + ), + ) + + report = compile_scene_report(scene) + @test isempty(report.diagnostics) + @test !isnothing(report.compiled) + @test report.application_order == [:source, :consumer] + + view = scene_graph_view(scene) + @test view isa SceneGraphView + @test view.metadata["objectCount"] == 1 + @test view.metadata["applicationCount"] == 2 + @test view.metadata["executionCount"] == 2 + @test !view.metadata["cyclic"] + @test any(application -> application["applicationId"] == "source", view.applications) + @test any( + edge -> edge["kind"] in ("value_binding", "inferred_same_object") && + edge["sourceVariable"] == "signal" && + edge["targetVariable"] == "signal", + view.edges, + ) + + resolved = scene_graph_view(scene; level=:resolved) + @test length(resolved.executions) == 2 + @test any(edge -> edge["kind"] in ("value_binding", "inferred_same_object"), resolved.edges) + + json = scene_graph_view_json(view) + @test occursin("\"applications\"", json) + @test occursin("SceneGraphSourceModel", json) + + path = write_scene_graph_view( + joinpath(mktempdir(), "scene-graph.html"), + view; + renderer=:standalone, + ) + html = read(path, String) + @test occursin("PlantSimEngine Scene Graph", html) + @test occursin("pse-scene-graph-data", html) + @test occursin("Applications", html) +end + +@testset "Scene graph instances and overrides" begin + template = ObjectTemplate( + ( + ModelSpec(SceneGraphSourceModel(); name=:source) |> + AppliesTo(Many(scale=:Leaf)), + ); + kind=:plant, + species=:test_species, + ) + plant_a = ObjectInstance( + :plant_a, + template; + root=Object(:plant_a; scale=:Plant, kind=:plant), + objects=(Object(:leaf_a; scale=:Leaf, kind=:organ, parent=:plant_a, status=Status(driver=1.0)),), + ) + plant_b = ObjectInstance( + :plant_b, + template; + root=Object(:plant_b; scale=:Plant, kind=:plant), + objects=(Object(:leaf_b; scale=:Leaf, kind=:organ, parent=:plant_b, status=Status(driver=1.0)),), + overrides=(source=SceneGraphSourceModel(3.0),), + ) + scene = Scene(plant_a, plant_b) + view = scene_graph_view(scene) + + @test view.metadata["instanceCount"] == 2 + @test Set(instance["name"] for instance in view.instances) == Set(["plant_a", "plant_b"]) + @test Set(application["applicationId"] for application in view.applications) == + Set(["plant_a__source", "plant_b__source"]) + plant_b_application = only( + application for application in view.applications + if application["applicationId"] == "plant_b__source" + ) + @test plant_b_application["modelParameters"]["coefficient"]["value"] == 3.0 + @test plant_b_application["targetIds"] == ["leaf_b"] +end + +@testset "Scene graph invalid and cyclic reports" begin + invalid_scene = Scene( + Object(:leaf; name=:leaf, scale=:Leaf); + applications=(ModelSpec(SceneGraphSourceModel(); name=:source),), + ) + invalid_report = compile_scene_report(invalid_scene) + @test any(diagnostic -> diagnostic.phase == :applications, invalid_report.diagnostics) + @test_throws Exception compile_scene_report(invalid_scene; strict=true) + + cyclic_scene = Scene( + Object(:leaf; name=:leaf, scale=:Leaf, status=Status()); + applications=( + ModelSpec(SceneGraphCycleAModel(); name=:cycle_a) |> + AppliesTo(One(name=:leaf)), + ModelSpec(SceneGraphCycleBModel(); name=:cycle_b) |> + AppliesTo(One(name=:leaf)), + ), + ) + report = compile_scene_report(cyclic_scene) + @test report.cycles == [[:cycle_a, :cycle_b]] + @test any(diagnostic -> diagnostic.code == :application_cycle, report.diagnostics) + @test isnothing(report.compiled) + + view = scene_graph_view(cyclic_scene) + @test view.metadata["cyclic"] + @test length(view.cycles) == 1 + @test Set(view.cycles[1]["applicationIds"]) == Set(["cycle_a", "cycle_b"]) + @test length(view.cycles[1]["breakCandidates"]) == 2 + @test any(edge -> edge["cycle"] == true, view.edges) + @test_throws Exception compile_scene(cyclic_scene) + + broken_scene = Scene( + Object(:leaf; name=:leaf, scale=:Leaf, status=Status(y=0.0)); + applications=( + ModelSpec(SceneGraphCycleAModel(); name=:cycle_a) |> + AppliesTo(One(name=:leaf)) |> + Inputs(PreviousTimeStep(:y) => One(within=Self(), var=:y)), + ModelSpec(SceneGraphCycleBModel(); name=:cycle_b) |> + AppliesTo(One(name=:leaf)), + ), + ) + broken_view = scene_graph_view(broken_scene) + @test !broken_view.metadata["cyclic"] + @test any(edge -> edge["kind"] == "previous_timestep", broken_view.edges) +end + +@testset "Scene graph initialization comes from Julia" begin + scene = Scene( + Object(:leaf; name=:leaf, scale=:Leaf, status=Status()); + applications=( + ModelSpec(SceneGraphConsumerModel(); name=:consumer) |> + AppliesTo(One(name=:leaf)), + ), + ) + view = scene_graph_view(scene) + unresolved = only( + row for row in view.initialization + if row["applicationId"] == "consumer" && row["variable"] == "signal" + ) + @test unresolved["disposition"] == "unresolved" + @test view.metadata["unresolvedInitializationCount"] == 1 +end + +@testset "Scene graph edits are transactional" begin + scene = Scene(Object(:leaf; name=:leaf, scale=:Leaf, status=Status(driver=1.0))) + source_spec = ModelSpec(SceneGraphSourceModel(); name=:source) |> + AppliesTo(One(name=:leaf)) + with_source = apply_scene_graph_edit(scene, AddSceneApplication(source_spec)) + @test isempty(scene.applications) + @test length(with_source.applications) == 1 + @test_throws "already exists" apply_scene_graph_edit( + with_source, + AddSceneApplication(source_spec), + ) + @test length(with_source.applications) == 1 + + changed_model = apply_scene_graph_edit( + with_source, + ReplaceSceneApplicationModel(:source, SceneGraphSourceModel(4.0)), + ) + changed_view = scene_graph_view(changed_model) + changed_application = only(changed_view.applications) + @test changed_application["modelParameters"]["coefficient"]["value"] == 4.0 + + changed_status = apply_scene_graph_edit( + changed_model, + SetSceneObjectStatus(:leaf, :driver, 3.0), + ) + @test only(scene_objects(changed_status)).status.driver == 3.0 + @test only(scene_objects(changed_model)).status.driver == 1.0 + + removed = apply_scene_graph_edit( + changed_status, + RemoveSceneApplication(:source), + ) + @test isempty(removed.applications) +end + +@testset "Scene graph edit breaks inferred cycles" begin + scene = Scene( + Object(:leaf; name=:leaf, scale=:Leaf, status=Status(y=0.0)); + applications=( + ModelSpec(SceneGraphCycleAModel(); name=:cycle_a) |> + AppliesTo(One(name=:leaf)), + ModelSpec(SceneGraphCycleBModel(); name=:cycle_b) |> + AppliesTo(One(name=:leaf)), + ), + ) + @test scene_graph_view(scene).metadata["cyclic"] + + broken = apply_scene_graph_edit( + scene, + MarkScenePreviousTimeStep(:cycle_a, :y), + ) + broken_view = scene_graph_view(broken) + @test !broken_view.metadata["cyclic"] + @test any(edge -> edge["kind"] == "previous_timestep", broken_view.edges) + + restored = apply_scene_graph_edit( + broken, + UnmarkScenePreviousTimeStep(:cycle_a, :y), + ) + @test scene_graph_view(restored).metadata["cyclic"] + + initialized_scene = Scene( + Object(:leaf; name=:leaf, scale=:Leaf, status=Status()); + applications=( + ModelSpec(SceneGraphCycleAModel(); name=:cycle_a) |> + AppliesTo(One(name=:leaf)), + ModelSpec(SceneGraphCycleBModel(); name=:cycle_b) |> + AppliesTo(One(name=:leaf)), + ), + ) + lagged_without_initial_value = apply_scene_graph_edit( + initialized_scene, + MarkScenePreviousTimeStep(:cycle_a, :y), + ) + lagged_row = only( + row for row in scene_graph_view(lagged_without_initial_value).initialization + if row["applicationId"] == "cycle_a" && row["variable"] == "y" + ) + @test lagged_row["previousTimeStep"] + @test lagged_row["disposition"] == "unresolved" + initialized_break = apply_scene_graph_edit( + initialized_scene, + BreakSceneCycle(:cycle_a, :y, true, 0.25), + ) + @test !scene_graph_view(initialized_break).metadata["cyclic"] + @test only(scene_objects(initialized_break)).status.y == 0.25 +end + +@testset "Scene graph edits preserve application configuration" begin + scene = Scene( + Object(:leaf; name=:leaf, scale=:Leaf, kind=:organ, status=Status(driver=1.0)); + applications=( + ModelSpec(SceneGraphSourceModel(); name=:source) |> + AppliesTo(One(name=:leaf)), + ), + ) + + configured = apply_scene_graph_edit( + scene, + SetSceneApplicationEnvironment(:source, (provider=:scene, sources=(T=:temperature,))), + ) + configured = apply_scene_graph_edit( + configured, + SetSceneOutputRouting(:source, :signal, :stream_only), + ) + configured = apply_scene_graph_edit( + configured, + SetSceneUpdateOrdering(:source, (Updates(:signal; after=:driver),)), + ) + spec = only(configured.applications) + @test environment_config(spec) == (provider=:scene, sources=(T=:temperature,)) + @test output_routing(spec) == (signal=:stream_only,) + @test only(updates(spec)).variables == (:signal,) + + metadata = apply_scene_graph_edit( + configured, + SetSceneObjectMetadata(:leaf; scale=:Organ, kind=:leaf, species=:test, name=:leaf_1), + ) + object = only(scene_objects(metadata)) + @test (object.scale, object.kind, object.species, object.name) == (:Organ, :leaf, :test, :leaf_1) + @test object_ids(metadata; scale=:Organ) == [ObjectId(:leaf)] + @test isempty(object_ids(metadata; scale=:Leaf)) + + without_status = apply_scene_graph_edit(metadata, RemoveSceneObjectStatus(:leaf, :driver)) + @test isnothing(only(scene_objects(without_status)).status) + @test only(scene_objects(metadata)).status.driver == 1.0 +end + +function scene_graph_override_fixture() + template = ObjectTemplate( + ( + ModelSpec(SceneGraphSourceModel(1.0); name=:source) |> + AppliesTo(Many(scale=:Leaf)), + ); + kind=:plant, + species=:test_species, + ) + return Scene( + ObjectInstance( + :plant_a, + template; + root=Object(:plant_a; scale=:Plant), + objects=(Object(:leaf_a; scale=:Leaf, parent=:plant_a, status=Status(driver=1.0)),), + ), + ObjectInstance( + :plant_b, + template; + root=Object(:plant_b; scale=:Plant), + objects=(Object(:leaf_b; scale=:Leaf, parent=:plant_b, status=Status(driver=1.0)),), + ), + ) +end + +@testset "Scene graph instance override edit" begin + scene = scene_graph_override_fixture() + @test length(scene.instances) == 2 + _, instance = PlantSimEngine._scene_edit_instance(scene, :plant_b) + @test PlantSimEngine._scene_edit_template_application_id(instance, :source) == :source + overrides = PlantSimEngine._scene_edit_namedtuple_set( + instance.overrides, + :source, + SceneGraphSourceModel(2.0), + ) + @test haskey(overrides, :source) + replacement = PlantSimEngine._scene_edit_normalize_instance(instance; overrides=overrides) + @test replacement.name == :plant_b + instance_override = apply_scene_graph_edit( + scene, + SetSceneInstanceOverride(:plant_b, :source, SceneGraphSourceModel(2.0)), + ) + view = scene_graph_view(instance_override) + plant_a = only(application for application in view.applications if application["applicationId"] == "plant_a__source") + plant_b = only(application for application in view.applications if application["applicationId"] == "plant_b__source") + @test plant_a["modelParameters"]["coefficient"]["value"] == 1.0 + @test plant_b["modelParameters"]["coefficient"]["value"] == 2.0 + + restored_instance = apply_scene_graph_edit( + instance_override, + RemoveSceneInstanceOverride(:plant_b, :source), + ) + restored_b = only( + application for application in scene_graph_view(restored_instance).applications + if application["applicationId"] == "plant_b__source" + ) + @test restored_b["modelParameters"]["coefficient"]["value"] == 1.0 + @test scene_graph_view(scene).metadata["applicationCount"] == 2 +end + +@testset "Scene graph object override edit" begin + scene = scene_graph_override_fixture() + @test length(scene.instances) == 2 + object_override = apply_scene_graph_edit( + scene, + SetSceneObjectOverride(:plant_a, :leaf_a, :source, SceneGraphSourceModel(3.0)), + ) + application = only( + application for application in scene_graph_view(object_override).applications + if application["applicationId"] == "plant_a__source" + ) + @test application["modelStorage"] == "per_object_override" + @test only(application["objectOverrides"])["parameters"]["coefficient"]["value"] == 3.0 + + restored_object = apply_scene_graph_edit( + object_override, + RemoveSceneObjectOverride(:plant_a, :leaf_a, :source), + ) + restored_application = only( + application for application in scene_graph_view(restored_object).applications + if application["applicationId"] == "plant_a__source" + ) + @test restored_application["modelStorage"] == "shared_application" +end + + +@testset "Scene graph shared template application edit" begin + scene = scene_graph_override_fixture() + updated = apply_scene_graph_edit( + scene, + UpdateSceneTemplateApplication( + :plant_a, + :plant_a__source, + SceneGraphSourceModel(4.0), + Many(scale=:Leaf), + ClockSpec(2.0), + ), + ) + applications = scene_graph_view(updated).applications + @test Set(application["applicationId"] for application in applications) == + Set(["plant_a__source", "plant_b__source"]) + @test all( + application["modelParameters"]["coefficient"]["value"] == 4.0 + for application in applications + ) + @test all(application["targetCount"] == 1 for application in applications) + @test all(!isnothing(application["timestep"]) for application in applications) + @test all( + application["modelParameters"]["coefficient"]["value"] == 1.0 + for application in scene_graph_view(scene).applications + ) +end diff --git a/test/test-scene-hard-calls.jl b/test/test-scene-hard-calls.jl new file mode 100644 index 000000000..2ccad372d --- /dev/null +++ b/test/test-scene-hard-calls.jl @@ -0,0 +1,200 @@ +using Dates +using PlantSimEngine +using Test + +PlantSimEngine.@process "nested_call_leaf" verbose = false +PlantSimEngine.@process "nested_call_middle" verbose = false +PlantSimEngine.@process "nested_call_root" verbose = false +PlantSimEngine.@process "many_call_controller" verbose = false + +struct NestedCallLeafModel <: AbstractNested_Call_LeafModel end +struct NestedCallMiddleModel <: AbstractNested_Call_MiddleModel end +struct NestedCallRootModel <: AbstractNested_Call_RootModel end +struct ManyCallControllerModel <: AbstractMany_Call_ControllerModel end + +PlantSimEngine.inputs_(::NestedCallLeafModel) = NamedTuple() +PlantSimEngine.outputs_(::NestedCallLeafModel) = (value=0.0, calls=0) + +function PlantSimEngine.run!( + ::NestedCallLeafModel, + models, + status, + meteo, + constants, + extra, +) + status.calls += 1 + status.value += 1.0 + return nothing +end + +PlantSimEngine.inputs_(::NestedCallMiddleModel) = NamedTuple() +PlantSimEngine.outputs_(::NestedCallMiddleModel) = (value=0.0, calls=0) + +function PlantSimEngine.run!( + ::NestedCallMiddleModel, + models, + status, + meteo, + constants, + extra, +) + status.calls += 1 + leaf = call_target(extra, :leaf) + run_call!(leaf; publish=true) + status.value = leaf.status.value + return nothing +end + +PlantSimEngine.inputs_(::NestedCallRootModel) = NamedTuple() +PlantSimEngine.outputs_(::NestedCallRootModel) = + (trial_value=0.0, accepted_value=0.0, calls=0) + +function PlantSimEngine.run!( + ::NestedCallRootModel, + models, + status, + meteo, + constants, + extra, +) + status.calls += 1 + middle = call_target(extra, :middle) + run_call!(middle; publish=false) + status.trial_value = middle.status.value + run_call!(middle; publish=true) + status.accepted_value = middle.status.value + return nothing +end + +PlantSimEngine.inputs_(::ManyCallControllerModel) = NamedTuple() +PlantSimEngine.outputs_(::ManyCallControllerModel) = (total=0.0, ncalls=0) + +function PlantSimEngine.run!( + ::ManyCallControllerModel, + models, + status, + meteo, + constants, + extra, +) + targets = call_targets(extra, :children) + status.ncalls = length(targets) + for target in targets + run_call!(target; publish=true) + end + status.total = sum(target.status.value for target in targets) + return nothing +end + +@testset "nested trial publication is transactional" begin + scene = Scene( + Object(:scene; scale=:Scene, name=:scene), + Object(:middle; scale=:Plant, name=:middle, parent=:scene), + Object(:leaf; scale=:Leaf, name=:leaf, parent=:middle); + applications=( + ModelSpec(NestedCallRootModel(); name=:root) |> + AppliesTo(One(name=:scene)) |> + Calls(:middle => One(name=:middle, within=Subtree(), application=:middle)) |> + TimeStep(Hour(1)), + ModelSpec(NestedCallMiddleModel(); name=:middle) |> + AppliesTo(One(name=:middle)) |> + Calls(:leaf => One(name=:leaf, within=Subtree(), application=:leaf)) |> + TimeStep(Hour(1)), + ModelSpec(NestedCallLeafModel(); name=:leaf) |> + AppliesTo(One(name=:leaf)) |> + TimeStep(Hour(1)), + ), + environment=(duration=Hour(1),), + ) + + simulation = run!(scene; outputs=:all) + statuses = Dict(object.id.value => object.status for object in scene_objects(scene)) + @test statuses[:leaf].calls == 2 + @test statuses[:leaf].value == 2.0 + @test statuses[:middle].calls == 2 + @test statuses[:middle].value == 2.0 + @test statuses[:scene].trial_value == 1.0 + @test statuses[:scene].accepted_value == 2.0 + + @test length(scene_outputs(simulation)[(:leaf, ObjectId(:leaf), :value)]) == 1 + @test length(scene_outputs(simulation)[(:middle, ObjectId(:middle), :value)]) == 1 + @test only(scene_outputs(simulation)[(:leaf, ObjectId(:leaf), :value)])[2] == 2.0 + @test only(scene_outputs(simulation)[(:middle, ObjectId(:middle), :value)])[2] == 2.0 + + schedule = Dict(row.application_id => row for row in explain_schedule(simulation.compiled)) + @test schedule[:middle].manual_call_only + @test schedule[:leaf].manual_call_only + @test !schedule[:root].manual_call_only +end + +@testset "Many call targets preserve object identity" begin + scene = Scene( + Object(:scene; scale=:Scene, name=:scene), + Object(:leaf_b; scale=:Leaf, parent=:scene), + Object(:leaf_a; scale=:Leaf, parent=:scene); + applications=( + ModelSpec(ManyCallControllerModel(); name=:controller) |> + AppliesTo(One(name=:scene)) |> + Calls( + :children => Many( + scale=:Leaf, + within=SceneScope(), + application=:leaf_calls, + ), + ), + ModelSpec(NestedCallLeafModel(); name=:leaf_calls) |> + AppliesTo(Many(scale=:Leaf)), + ), + environment=(duration=Hour(1),), + ) + + compiled = Advanced.refresh_bindings!(scene) + call = only(explain_calls(compiled)) + @test call.callee_object_ids == [:leaf_a, :leaf_b] + @test call.callee_application_ids == [:leaf_calls] + + simulation = run!(scene; outputs=:all) + controller = only(scene_objects(scene; scale=:Scene)).status + @test controller.ncalls == 2 + @test controller.total == 2.0 + rows = filter( + row -> row.application_id == :leaf_calls && row.variable == :value, + collect_outputs(simulation; sink=nothing), + ) + @test getproperty.(rows, :object_id) == [:leaf_a, :leaf_b] + @test getproperty.(rows, :value) == [1.0, 1.0] +end + +@testset "manual target cadence contract" begin + incompatible = Scene( + Object(:scene; scale=:Scene, name=:scene), + Object(:leaf; scale=:Leaf, name=:leaf, parent=:scene); + applications=( + ModelSpec(ManyCallControllerModel(); name=:controller) |> + AppliesTo(One(name=:scene)) |> + Calls(:children => One(name=:leaf, application=:leaf_calls)) |> + TimeStep(Day(1)), + ModelSpec(NestedCallLeafModel(); name=:leaf_calls) |> + AppliesTo(One(name=:leaf)) |> + TimeStep(Hour(1)), + ), + environment=(duration=Hour(1),), + ) + @test_throws "incompatible cadence" Advanced.refresh_bindings!(incompatible) + + inherited = Scene( + Object(:scene; scale=:Scene, name=:scene), + Object(:leaf; scale=:Leaf, name=:leaf, parent=:scene); + applications=( + ModelSpec(ManyCallControllerModel(); name=:controller) |> + AppliesTo(One(name=:scene)) |> + Calls(:children => One(name=:leaf, application=:leaf_calls)) |> + TimeStep(Day(1)), + ModelSpec(NestedCallLeafModel(); name=:leaf_calls) |> + AppliesTo(One(name=:leaf)), + ), + environment=(duration=Hour(1),), + ) + @test_nowarn Advanced.refresh_bindings!(inherited) +end diff --git a/test/test-scene-meteo-sampling.jl b/test/test-scene-meteo-sampling.jl new file mode 100644 index 000000000..60a97466a --- /dev/null +++ b/test/test-scene-meteo-sampling.jl @@ -0,0 +1,82 @@ +using Dates +using PlantMeteo +using PlantSimEngine +using Test + +PlantSimEngine.@process "meteo_sampling_probe" verbose = false +struct MeteoSamplingProbeModel <: AbstractMeteo_Sampling_ProbeModel end +PlantSimEngine.inputs_(::MeteoSamplingProbeModel) = NamedTuple() +PlantSimEngine.outputs_(::MeteoSamplingProbeModel) = ( + mean_T=0.0, + min_T=0.0, + max_T=0.0, + mean_Rh=0.0, + mean_radiation=0.0, + radiation_energy=0.0, +) +PlantSimEngine.meteo_inputs_(::MeteoSamplingProbeModel) = ( + T=0.0, + Tmin=0.0, + Tmax=0.0, + Rh=0.0, + Ri_SW_f=0.0, + Ri_SW_q=0.0, +) +PlantSimEngine.meteo_hint(::Type{<:MeteoSamplingProbeModel}) = ( + window=PlantMeteo.RollingWindow(2.0), + bindings=( + T=(source=:T, reducer=MeanWeighted()), + Tmin=(source=:T, reducer=MinReducer()), + Tmax=(source=:T, reducer=MaxReducer()), + Rh=(source=:Rh, reducer=MeanWeighted()), + Ri_SW_f=(source=:Ri_SW_f, reducer=MeanWeighted()), + Ri_SW_q=(source=:Ri_SW_f, reducer=RadiationEnergy()), + ), +) +function PlantSimEngine.run!(::MeteoSamplingProbeModel, models, status, meteo, constants, extra) + status.mean_T = meteo.T + status.min_T = meteo.Tmin + status.max_T = meteo.Tmax + status.mean_Rh = meteo.Rh + status.mean_radiation = meteo.Ri_SW_f + status.radiation_energy = meteo.Ri_SW_q +end + +@testset "meteorological aggregation and model hint" begin + if PlantSimEngine._has_meteo_sampler_api() + base_date = DateTime(2025, 1, 1) + weather = Weather([ + Atmosphere(date=base_date, T=10.0, Wind=1.0, Rh=0.5, P=100.0, Ri_SW_f=100.0, duration=Hour(1)), + Atmosphere(date=base_date + Hour(1), T=20.0, Wind=1.0, Rh=0.6, P=100.0, Ri_SW_f=200.0, duration=Hour(1)), + Atmosphere(date=base_date + Hour(2), T=30.0, Wind=1.0, Rh=0.7, P=100.0, Ri_SW_f=300.0, duration=Hour(1)), + Atmosphere(date=base_date + Hour(3), T=40.0, Wind=1.0, Rh=0.8, P=100.0, Ri_SW_f=400.0, duration=Hour(1)), + ]) + scene = Scene( + Object(:leaf; scale=:Leaf); + applications=( + ModelSpec(MeteoSamplingProbeModel(); name=:probe) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(2)) |> + Environment(provider=:global), + ), + environment=weather, + ) + bindings = only(explain_environment_bindings(Advanced.refresh_environment_bindings!(scene))) + @test bindings.temporal_sampler + spec = Advanced.refresh_bindings!(scene).applications_by_id[:probe].spec + @test meteo_bindings(spec).Ri_SW_q.reducer isa RadiationEnergy + simulation = run!(scene; steps=4, outputs=:all) + values(variable) = getproperty.( + collect_outputs(simulation, :leaf, variable; sink=nothing), + :value, + ) + @test values(:mean_T) == [10.0, 25.0] + @test values(:min_T) == [10.0, 20.0] + @test values(:max_T) == [10.0, 30.0] + @test values(:mean_Rh) == [0.5, 0.65] + @test values(:mean_radiation) == [100.0, 250.0] + @test values(:radiation_energy) ≈ [0.36, 1.8] + else + @test_skip "PlantMeteo weather sampler API unavailable" + end +end diff --git a/test/test-scene-multirate-integration.jl b/test/test-scene-multirate-integration.jl new file mode 100644 index 000000000..9d641461d --- /dev/null +++ b/test/test-scene-multirate-integration.jl @@ -0,0 +1,88 @@ +using Dates +using PlantSimEngine +using Test + +PlantSimEngine.@process "hourly_leaf_flux" verbose = false +PlantSimEngine.@process "daily_plant_flux" verbose = false +PlantSimEngine.@process "daily_soil_state" verbose = false + +struct HourlyLeafFluxModel <: AbstractHourly_Leaf_FluxModel end +struct DailyPlantFluxModel <: AbstractDaily_Plant_FluxModel end +struct DailySoilStateModel <: AbstractDaily_Soil_StateModel end +PlantSimEngine.inputs_(::HourlyLeafFluxModel) = (rate=0.0,) +PlantSimEngine.outputs_(::HourlyLeafFluxModel) = (flux=0.0, hourly_runs=0) +function PlantSimEngine.run!(::HourlyLeafFluxModel, models, status, meteo, constants, extra) + status.flux = status.rate + status.hourly_runs += 1 +end +PlantSimEngine.inputs_(::DailyPlantFluxModel) = (leaf_fluxes=[0.0],) +PlantSimEngine.outputs_(::DailyPlantFluxModel) = (daily_total=0.0, daily_runs=0) +function PlantSimEngine.run!(::DailyPlantFluxModel, models, status, meteo, constants, extra) + status.daily_total = sum(status.leaf_fluxes) + status.daily_runs += 1 +end +PlantSimEngine.inputs_(::DailySoilStateModel) = NamedTuple() +PlantSimEngine.outputs_(::DailySoilStateModel) = (soil_runs=0,) +function PlantSimEngine.run!(::DailySoilStateModel, models, status, meteo, constants, extra) + status.soil_runs += 1 +end + +@testset "48-hour hourly/daily stack and plant isolation" begin + scene = Scene( + Object(:scene; scale=:Scene), + Object(:soil; scale=:Soil, parent=:scene), + Object(:plant_1; scale=:Plant, parent=:scene), + Object(:plant_1_leaf_1; scale=:Leaf, parent=:plant_1, status=Status(rate=1.0)), + Object(:plant_1_leaf_2; scale=:Leaf, parent=:plant_1, status=Status(rate=2.0)), + Object(:plant_2; scale=:Plant, parent=:scene), + Object(:plant_2_leaf_1; scale=:Leaf, parent=:plant_2, status=Status(rate=10.0)), + Object(:plant_2_leaf_2; scale=:Leaf, parent=:plant_2, status=Status(rate=20.0)); + applications=( + ModelSpec(HourlyLeafFluxModel(); name=:hourly_flux) |> + AppliesTo(Many(scale=:Leaf)) |> + TimeStep(Hour(1)), + ModelSpec(DailyPlantFluxModel(); name=:daily_plant) |> + AppliesTo(Many(scale=:Plant)) |> + Inputs(:leaf_fluxes => Many( + scale=:Leaf, + within=Subtree(), + application=:hourly_flux, + var=:flux, + policy=Integrate(), + window=Day(1), + )) |> + TimeStep(Day(1)), + ModelSpec(DailySoilStateModel(); name=:daily_soil) |> + AppliesTo(One(scale=:Soil)) |> + TimeStep(Day(1)), + ), + environment=[(duration=Hour(1),) for _ in 1:48], + ) + compiled = Advanced.refresh_bindings!(scene) + schedule = Dict(row.application_id => row for row in explain_schedule(compiled)) + @test schedule[:hourly_flux].dt_seconds == 3600.0 + @test schedule[:daily_plant].dt_seconds == 86_400.0 + @test schedule[:daily_soil].dt_steps == 24.0 + + simulation = run!(scene; steps=23, outputs=:all) + continue!(simulation; steps=25) + @test current_step(simulation) == 48 + statuses = Dict(object.id.value => object.status for object in scene_objects(scene)) + @test all(statuses[id].hourly_runs == 48 for id in + (:plant_1_leaf_1, :plant_1_leaf_2, :plant_2_leaf_1, :plant_2_leaf_2)) + @test statuses[:plant_1].daily_runs == 2 + @test statuses[:plant_2].daily_runs == 2 + @test statuses[:soil].soil_runs == 2 + @test statuses[:plant_1].daily_total == 72.0 + @test statuses[:plant_2].daily_total == 720.0 + + plant_1_values = last.(scene_outputs(simulation)[ + (:daily_plant, ObjectId(:plant_1), :daily_total) + ]) + plant_2_values = last.(scene_outputs(simulation)[ + (:daily_plant, ObjectId(:plant_2), :daily_total) + ]) + @test plant_1_values == [3.0, 72.0] + @test plant_2_values == [30.0, 720.0] + @test all(isfinite, vcat(plant_1_values, plant_2_values)) +end diff --git a/test/test-scene-numerical-parity.jl b/test/test-scene-numerical-parity.jl new file mode 100644 index 000000000..b2882df23 --- /dev/null +++ b/test/test-scene-numerical-parity.jl @@ -0,0 +1,240 @@ +using Dates +using PlantSimEngine +using Test + +PlantSimEngine.@process "parity_forcing" verbose = false +PlantSimEngine.@process "parity_stage_one" verbose = false +PlantSimEngine.@process "parity_stage_two" verbose = false +PlantSimEngine.@process "parity_stage_three" verbose = false + +struct ParityForcingModel <: AbstractParity_ForcingModel end +struct ParityStageOneModel{T} <: AbstractParity_Stage_OneModel + a::T +end +struct ParityStageTwoModel <: AbstractParity_Stage_TwoModel end +struct ParityStageThreeModel <: AbstractParity_Stage_ThreeModel end + +PlantSimEngine.@process "parity_stage_five" verbose = false +PlantSimEngine.@process "parity_stage_six" verbose = false +PlantSimEngine.@process "parity_soil" verbose = false +PlantSimEngine.@process "parity_gather" verbose = false +PlantSimEngine.@process "parity_receiver" verbose = false + +struct ParityStageFiveModel <: AbstractParity_Stage_FiveModel end +struct ParityStageSixModel <: AbstractParity_Stage_SixModel end +struct ParitySoilModel <: AbstractParity_SoilModel end +struct ParityGatherModel <: AbstractParity_GatherModel end +struct ParityReceiverModel <: AbstractParity_ReceiverModel end + +PlantSimEngine.inputs_(::ParityForcingModel) = NamedTuple() +PlantSimEngine.outputs_(::ParityForcingModel) = (var1=0.0,) +PlantSimEngine.meteo_inputs_(::ParityForcingModel) = (forcing=0.0,) +function PlantSimEngine.run!(::ParityForcingModel, models, status, meteo, constants, extra) + status.var1 = meteo.forcing + return nothing +end + +PlantSimEngine.inputs_(::ParityStageOneModel) = (var1=0.0, var2=0.0) +PlantSimEngine.outputs_(::ParityStageOneModel) = (var3=0.0,) +function PlantSimEngine.run!(model::ParityStageOneModel, models, status, meteo, constants, extra) + status.var3 = model.a + status.var1 * status.var2 + return nothing +end + +PlantSimEngine.inputs_(::ParityStageTwoModel) = (var3=0.0,) +PlantSimEngine.outputs_(::ParityStageTwoModel) = (raw_var4=0.0, var5=0.0) +PlantSimEngine.meteo_inputs_(::ParityStageTwoModel) = (T=0.0, Wind=0.0, Rh=0.0) +function PlantSimEngine.run!(::ParityStageTwoModel, models, status, meteo, constants, extra) + status.raw_var4 = status.var3 * 2 + status.var5 = status.raw_var4 + meteo.T + 2 * meteo.Wind + 3 * meteo.Rh + return nothing +end + +PlantSimEngine.inputs_(::ParityStageThreeModel) = (raw_var4=0.0, var5=0.0) +PlantSimEngine.outputs_(::ParityStageThreeModel) = (var4=0.0, var6=0.0) +function PlantSimEngine.run!(::ParityStageThreeModel, models, status, meteo, constants, extra) + status.var4 = status.raw_var4 * 2 + status.var6 = status.var5 + status.var4 + return nothing +end + +PlantSimEngine.inputs_(::ParityStageFiveModel) = (var5=0.0, var6=0.0) +PlantSimEngine.outputs_(::ParityStageFiveModel) = (var7=0.0,) +function PlantSimEngine.run!(::ParityStageFiveModel, models, status, meteo, constants, extra) + status.var7 = status.var5 * status.var6 +end +PlantSimEngine.inputs_(::ParityStageSixModel) = (var7=0.0,) +PlantSimEngine.outputs_(::ParityStageSixModel) = (var8=0.0,) +function PlantSimEngine.run!(::ParityStageSixModel, models, status, meteo, constants, extra) + status.var8 = status.var7 + 1 +end +PlantSimEngine.inputs_(::ParitySoilModel) = NamedTuple() +PlantSimEngine.outputs_(::ParitySoilModel) = (soil_value=1.0,) +PlantSimEngine.run!(::ParitySoilModel, models, status, meteo, constants, extra) = nothing +PlantSimEngine.inputs_(::ParityGatherModel) = (leaf_values=[0.0], soil_value=0.0) +PlantSimEngine.outputs_(::ParityGatherModel) = (gathered=0.0, scattered=0.0) +function PlantSimEngine.run!(::ParityGatherModel, models, status, meteo, constants, extra) + status.gathered = sum(status.leaf_values) + status.soil_value + status.scattered = first(status.leaf_values) +end +PlantSimEngine.inputs_(::ParityReceiverModel) = (shared=0.0,) +PlantSimEngine.outputs_(::ParityReceiverModel) = (received=0.0,) +function PlantSimEngine.run!(::ParityReceiverModel, models, status, meteo, constants, extra) + status.received = status.shared +end + +parity_weather = [ + (forcing=15.0, T=20.0, Wind=1.0, Rh=0.65, duration=Hour(1)), + (forcing=16.0, T=25.0, Wind=0.5, Rh=0.8, duration=Hour(1)), +] + +function parity_applications(selector) + return ( + ModelSpec(ParityForcingModel(); name=:forcing) |> AppliesTo(selector), + ModelSpec(ParityStageOneModel(1.0); name=:stage_one) |> AppliesTo(selector), + ModelSpec(ParityStageTwoModel(); name=:stage_two) |> AppliesTo(selector), + ModelSpec(ParityStageThreeModel(); name=:stage_three) |> AppliesTo(selector), + ) +end + +function parity_state(status) + return [status.var5, status.var4, status.var6, status.var1, status.var3, status.var2] +end + +@testset "exact one-object process chain" begin + one_step = Scene( + Object(:leaf; scale=:Leaf, status=Status(var2=0.3)); + applications=parity_applications(One(scale=:Leaf)), + environment=parity_weather[1:1], + ) + run!(one_step) + @test parity_state(only(scene_objects(one_step)).status) == [34.95, 22.0, 56.95, 15.0, 5.5, 0.3] + + two_steps = Scene( + Object(:leaf; scale=:Leaf, status=Status(var2=0.3)); + applications=parity_applications(One(scale=:Leaf)), + environment=parity_weather, + ) + simulation = run!(two_steps; steps=2, outputs=:all) + @test parity_state(only(scene_objects(two_steps)).status) == [40.0, 23.2, 63.2, 16.0, 5.8, 0.3] + @test last.(scene_outputs(simulation)[(:stage_three, ObjectId(:leaf), :var6)]) == [56.95, 63.2] +end + +@testset "one application with an object-specific Override" begin + template = ObjectTemplate( + parity_applications(Many(scale=:Leaf)); + kind=:plant, + ) + instance = ObjectInstance( + :plant, + template; + root=Object(:plant; scale=:Plant), + objects=( + Object(:leaf_default; scale=:Leaf, parent=:plant, status=Status(var2=0.3)), + Object(:leaf_override; scale=:Leaf, parent=:plant, status=Status(var2=0.3)), + ), + object_overrides=( + Override( + object=:leaf_override, + application=:stage_one, + model=ParityStageOneModel(2.0), + ), + ), + ) + scene = Scene(instance; environment=parity_weather) + simulation = run!(scene; steps=2, outputs=:all) + statuses = Dict(object.id.value => object.status for object in scene_objects(scene)) + @test parity_state(statuses[:leaf_default]) == [40.0, 23.2, 63.2, 16.0, 5.8, 0.3] + @test parity_state(statuses[:leaf_override]) == [42.0, 27.2, 69.2, 16.0, 6.8, 0.3] + + override_rows = filter( + row -> row.object_id == :leaf_override && row.variable in (:var5, :var4, :var6), + collect_outputs(simulation; sink=nothing), + ) + @test length(override_rows) == 6 +end + + +@testset "exact multiscale gather and scatter chain" begin + scene = Scene( + Object(:scene; scale=:Scene), + Object(:soil; scale=:Soil, parent=:scene), + Object(:plant; scale=:Plant, parent=:scene), + Object(:internode_1; scale=:Internode, parent=:plant), + Object(:leaf_1; scale=:Leaf, parent=:internode_1, status=Status(var2=1.03)), + Object(:internode_2; scale=:Internode, parent=:internode_1), + Object(:leaf_2; scale=:Leaf, parent=:internode_2, status=Status(var2=1.03)); + applications=( + ModelSpec(ParitySoilModel(); name=:soil_source) |> + AppliesTo(One(scale=:Soil)), + ModelSpec(ParityForcingModel(); name=:forcing) |> + AppliesTo(Many(scale=:Leaf)), + ModelSpec(ParityStageOneModel(1.0); name=:stage_one) |> + AppliesTo(Many(scale=:Leaf)), + ModelSpec(ParityStageTwoModel(); name=:stage_two) |> + AppliesTo(Many(scale=:Leaf)), + ModelSpec(ParityStageThreeModel(); name=:stage_three) |> + AppliesTo(Many(scale=:Leaf)), + ModelSpec(ParityStageFiveModel(); name=:stage_five) |> + AppliesTo(Many(scale=:Leaf)), + ModelSpec(ParityStageSixModel(); name=:stage_six) |> + AppliesTo(Many(scale=:Leaf)), + ModelSpec(ParityGatherModel(); name=:plant_gather) |> + AppliesTo(One(scale=:Plant)) |> + Inputs( + :leaf_values => Many( + scale=:Leaf, + within=Subtree(), + application=:stage_six, + var=:var8, + ), + :soil_value => One( + scale=:Soil, + within=SceneScope(), + application=:soil_source, + var=:soil_value, + ), + ), + ModelSpec(ParityReceiverModel(); name=:leaf_receiver) |> + AppliesTo(Many(scale=:Leaf)) |> + Inputs(:shared => One( + scale=:Plant, + within=Ancestor(scale=:Plant), + application=:plant_gather, + var=:scattered, + )), + ModelSpec(ParityReceiverModel(); name=:internode_receiver) |> + AppliesTo(Many(scale=:Internode)) |> + Inputs(:shared => One( + scale=:Plant, + within=Ancestor(scale=:Plant), + application=:plant_gather, + var=:scattered, + )), + ), + environment=[ + (forcing=1.01, T=20.0, Wind=1.0, Rh=0.65, duration=Hour(1)), + (forcing=1.01, T=25.0, Wind=0.5, Rh=0.8, duration=Hour(1)), + ], + ) + simulation = run!(scene; steps=2, outputs=:all) + leaves = scene_objects(scene; scale=:Leaf) + @test getproperty.(leaves, :id) == [ObjectId(:leaf_1), ObjectId(:leaf_2)] + for leaf in leaves + @test leaf.status.var1 === 1.01 + @test leaf.status.var2 === 1.03 + @test leaf.status.var4 ≈ 8.1612000000000013 atol=1e-6 + @test leaf.status.var5 == 32.4806 + @test leaf.status.var8 ≈ 1321.0700490800002 atol=1e-6 + @test leaf.status.received == leaf.status.var8 + end + plant = only(scene_objects(scene; scale=:Plant)).status + @test plant.gathered ≈ 2 * 1321.0700490800002 + 1 atol=1e-6 + @test all( + object -> object.status.received ≈ 1321.0700490800002, + scene_objects(scene; scale=:Internode), + ) + rows = filter(row -> row.variable == :var8, collect_outputs(simulation; sink=nothing)) + @test length(rows) == 4 + @test Set(getproperty.(rows, :object_id)) == Set((:leaf_1, :leaf_2)) +end diff --git a/test/test-scene-output-boundaries.jl b/test/test-scene-output-boundaries.jl new file mode 100644 index 000000000..22c8aaa0b --- /dev/null +++ b/test/test-scene-output-boundaries.jl @@ -0,0 +1,66 @@ +using Dates +using PlantSimEngine +using Test + +PlantSimEngine.@process "boundary_counter" verbose = false +struct BoundaryCounterModel <: AbstractBoundary_CounterModel end +PlantSimEngine.inputs_(::BoundaryCounterModel) = NamedTuple() +PlantSimEngine.outputs_(::BoundaryCounterModel) = (count=0, ignored=0) +function PlantSimEngine.run!(::BoundaryCounterModel, models, status, meteo, constants, extra) + status.count += 1 + status.ignored += 10 +end + +@testset "one step over several objects" begin + scene = Scene( + Object(:leaf_b; scale=:Leaf), + Object(:leaf_a; scale=:Leaf); + applications=( + ModelSpec(BoundaryCounterModel(); name=:leaf_counter) |> + AppliesTo(Many(scale=:Leaf)), + ), + environment=(duration=Hour(1),), + ) + simulation = run!( + scene; + outputs=OutputRequest(:Leaf, :count; name=:leaf_counts), + ) + requested = collect_outputs(simulation, :leaf_counts; sink=nothing) + @test length(requested) == 2 + @test getproperty.(requested, :object_id) == [:leaf_a, :leaf_b] + @test unique(getproperty.(requested, :timestep)) == [1] + @test unique(getproperty.(requested, :application_id)) == [:leaf_counter] + + selected = collect_outputs(simulation, :leaf_counts; sink=nothing) + @test length(selected) == 2 + @test all(row -> row.variable == :count, selected) + @test Set(keys(scene_outputs(simulation))) == Set([ + (:leaf_counter, ObjectId(:leaf_a), :count), + (:leaf_counter, ObjectId(:leaf_b), :count), + ]) +end + +@testset "object count multiplied by cadence" begin + scene = Scene( + Object(:leaf_2; scale=:Leaf), + Object(:leaf_1; scale=:Leaf), + Object(:plant; scale=:Plant); + applications=( + ModelSpec(BoundaryCounterModel(); name=:hourly_leaves) |> + AppliesTo(Many(scale=:Leaf)) |> + TimeStep(Hour(1)), + ModelSpec(BoundaryCounterModel(); name=:daily_plant) |> + AppliesTo(One(scale=:Plant)) |> + TimeStep(Day(1)), + ), + environment=[(duration=Hour(1),) for _ in 1:48], + ) + simulation = run!(scene; steps=48, outputs=:all) + rows = collect_outputs(simulation; sink=nothing) + leaf_rows = filter(row -> row.application_id == :hourly_leaves && row.variable == :count, rows) + plant_rows = filter(row -> row.application_id == :daily_plant && row.variable == :count, rows) + @test length(leaf_rows) == 2 * 48 + @test length(plant_rows) == 2 + @test Set(getproperty.(leaf_rows, :object_id)) == Set((:leaf_1, :leaf_2)) + @test all(row -> row.object_id == :plant, plant_rows) +end diff --git a/test/test-scene-runtime-matrix.jl b/test/test-scene-runtime-matrix.jl new file mode 100644 index 000000000..51ffab59a --- /dev/null +++ b/test/test-scene-runtime-matrix.jl @@ -0,0 +1,99 @@ +using Dates +using PlantSimEngine +using Test + +PlantSimEngine.@process "runtime_matrix_probe" verbose = false +struct RuntimeMatrixProbeModel <: AbstractRuntime_Matrix_ProbeModel end +PlantSimEngine.inputs_(::RuntimeMatrixProbeModel) = NamedTuple() +PlantSimEngine.outputs_(::RuntimeMatrixProbeModel) = (seen=0.0,) +PlantSimEngine.meteo_inputs_(::RuntimeMatrixProbeModel) = (T=0.0,) +function PlantSimEngine.run!(::RuntimeMatrixProbeModel, models, status, meteo, constants, extra) + status.seen = meteo.T +end + +function runtime_matrix_scene(environment) + return Scene( + Object(:probe; scale=:Leaf); + applications=( + ModelSpec(RuntimeMatrixProbeModel(); name=:probe) |> + AppliesTo(One(scale=:Leaf)), + ), + environment=environment, + ) +end + +@testset "environment and output request matrix" begin + constant_scene = runtime_matrix_scene((T=12.0, duration=Hour(1))) + constant_sim = run!(constant_scene; steps=2, outputs=:all) + @test last.(scene_outputs(constant_sim)[(:probe, ObjectId(:probe), :seen)]) == [12.0, 12.0] + + one_row_scene = runtime_matrix_scene([(T=13.0, duration=Hour(1))]) + one_row_sim = run!(one_row_scene) + @test only(scene_objects(one_row_scene)).status.seen == 13.0 + @test_throws Exception run!(runtime_matrix_scene([(T=13.0, duration=Hour(1))]); steps=2) + + rows = [(T=14.0, duration=Hour(1)), (T=15.0, duration=Hour(1))] + selected_scene = runtime_matrix_scene(rows) + selected_sim = run!( + selected_scene; + steps=2, + outputs=OutputRequest(:Leaf, :seen; name=:selected_seen), + ) + selected = collect_outputs(selected_sim, :selected_seen; sink=nothing) + canonical = scene_outputs(selected_sim)[(:probe, ObjectId(:probe), :seen)] + @test getproperty.(selected, :value) == last.(canonical) == [14.0, 15.0] + + no_outputs = run!( + runtime_matrix_scene(rows); + steps=2, + outputs=:none, + ) + @test isempty(scene_outputs(no_outputs)) + @test isempty(collect_outputs(no_outputs; sink=nothing)) + + selector_scene = Scene( + Object(:sun_leaf; scale=:Leaf, kind=:sun), + Object(:shade_leaf; scale=:Leaf, kind=:shade); + applications=( + ModelSpec(RuntimeMatrixProbeModel(); name=:probe) |> + AppliesTo(Many(scale=:Leaf)), + ), + environment=(T=16.0, duration=Hour(1)), + ) + selector_sim = run!( + selector_scene; + outputs=OutputRequest( + One(kind=:sun), + :seen; + name=:sun_seen, + application=:probe, + ), + ) + selector_rows = collect_outputs(selector_sim, :sun_seen; sink=nothing) + @test getproperty.(selector_rows, :object_id) == [:sun_leaf] + @test getproperty.(selector_rows, :value) == [16.0] + + contextual_sim = run!( + selector_scene; + outputs=OutputRequest( + One(Self()), + :seen; + name=:self_seen, + application=:probe, + context=:shade_leaf, + ), + ) + contextual_rows = collect_outputs(contextual_sim, :self_seen; sink=nothing) + @test getproperty.(contextual_rows, :object_id) == [:shade_leaf] + + continued_scene = runtime_matrix_scene([ + (T=17.0, duration=Hour(1)), + (T=18.0, duration=Hour(1)), + (T=19.0, duration=Hour(1)), + ]) + continued_sim = run!(continued_scene; outputs=:all) + continue!(continued_sim; steps=2) + @test last.(scene_outputs(continued_sim)[ + (:probe, ObjectId(:probe), :seen) + ]) == [17.0, 18.0, 19.0] +end diff --git a/test/test-scene-status-initialization.jl b/test/test-scene-status-initialization.jl new file mode 100644 index 000000000..6cdf8b671 --- /dev/null +++ b/test/test-scene-status-initialization.jl @@ -0,0 +1,46 @@ +using PlantSimEngine +using Test + +PlantSimEngine.@process "initialization_source" verbose = false +PlantSimEngine.@process "initialization_consumer" verbose = false + +struct InitializationSourceModel <: AbstractInitialization_SourceModel end +struct InitializationConsumerModel <: AbstractInitialization_ConsumerModel end + +PlantSimEngine.inputs_(::InitializationSourceModel) = NamedTuple() +PlantSimEngine.outputs_(::InitializationSourceModel) = (signal=1,) +function PlantSimEngine.run!(::InitializationSourceModel, models, status, meteo, constants, extra) + status.signal += 1 +end + +PlantSimEngine.inputs_(::InitializationConsumerModel) = (signal=0, supplied=0) +PlantSimEngine.outputs_(::InitializationConsumerModel) = (observed=0,) +function PlantSimEngine.run!(::InitializationConsumerModel, models, status, meteo, constants, extra) + status.observed = status.signal + status.supplied +end + +@testset "generated and partial status" begin + scene = Scene( + Object(:object; scale=:Leaf, status=Status(supplied=40)); + applications=( + ModelSpec(InitializationSourceModel(); name=:source) |> AppliesTo(One(scale=:Leaf)), + ModelSpec(InitializationConsumerModel(); name=:consumer) |> AppliesTo(One(scale=:Leaf)), + ), + ) + compiled = Advanced.refresh_bindings!(scene) + status = only(scene_objects(scene)).status + @test status.supplied == 40 + @test Set(propertynames(status)) == Set((:supplied, :signal, :observed)) + binding = only(row for row in compiled.input_bindings if row.input == :signal) + @test PlantSimEngine.refvalue(status, :signal) === input_carrier(binding) + report = explain_initialization(scene) + @test only(row for row in report if row.variable == :supplied && row.role == :input).disposition == :supplied + @test only(row for row in report if row.variable == :signal && row.role == :input).disposition == :producer_bound + run!(scene) + @test status.observed == 42 + + unresolved = Scene(InitializationConsumerModel(); status=(supplied=1,)) + @test any(row -> row.variable == :signal && row.disposition == :unresolved, + explain_initialization(unresolved)) + @test_throws "Missing required scene/object input" run!(unresolved) +end diff --git a/test/test-scene-temporal-reducers.jl b/test/test-scene-temporal-reducers.jl new file mode 100644 index 000000000..0a172bd32 --- /dev/null +++ b/test/test-scene-temporal-reducers.jl @@ -0,0 +1,82 @@ +using Dates +using PlantSimEngine +using Test + +PlantSimEngine.@process "temporal_reducer_source" verbose = false +PlantSimEngine.@process "temporal_reducer_one_arg" verbose = false +PlantSimEngine.@process "temporal_reducer_two_arg" verbose = false + +struct TemporalReducerSourceModel <: AbstractTemporal_Reducer_SourceModel end +struct TemporalReducerOneArgModel <: AbstractTemporal_Reducer_One_ArgModel end +struct TemporalReducerTwoArgModel <: AbstractTemporal_Reducer_Two_ArgModel end +PlantSimEngine.inputs_(::TemporalReducerSourceModel) = NamedTuple() +PlantSimEngine.outputs_(::TemporalReducerSourceModel) = (signal=0.0,) +function PlantSimEngine.run!(::TemporalReducerSourceModel, models, status, meteo, constants, extra) + status.signal += 1 +end +PlantSimEngine.inputs_(::Union{TemporalReducerOneArgModel,TemporalReducerTwoArgModel}) = (reduced=0.0,) +PlantSimEngine.outputs_(::TemporalReducerOneArgModel) = (one_arg=0.0,) +PlantSimEngine.outputs_(::TemporalReducerTwoArgModel) = (two_arg=0.0,) +PlantSimEngine.run!(::TemporalReducerOneArgModel, models, status, meteo, constants, extra) = + (status.one_arg = status.reduced) +PlantSimEngine.run!(::TemporalReducerTwoArgModel, models, status, meteo, constants, extra) = + (status.two_arg = status.reduced) + +@testset "duration-aware and callable reducers" begin + @test RadiationEnergy()([100.0, 200.0], [1800.0, 3600.0]) ≈ 0.9 + @test RadiationEnergy()([big"100", big"200"], [1800.0, 3600.0]) isa BigFloat + + one_arg = values -> maximum(values) - minimum(values) + two_arg = (values, durations) -> sum(values .* durations) + scene = Scene( + Object(:leaf; scale=:Leaf); + applications=( + ModelSpec(TemporalReducerSourceModel(); name=:source) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(1)), + ModelSpec(TemporalReducerOneArgModel(); name=:one_arg) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs(:reduced => One( + scale=:Leaf, + application=:source, + var=:signal, + policy=Aggregate(one_arg), + window=Hour(3), + )) |> + TimeStep(Hour(3)), + ModelSpec(TemporalReducerTwoArgModel(); name=:two_arg) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs(:reduced => One( + scale=:Leaf, + application=:source, + var=:signal, + policy=Aggregate(two_arg), + window=Hour(3), + )) |> + TimeStep(Hour(3)), + ), + environment=(duration=Hour(1),), + ) + run!(scene; steps=4) + status = only(scene_objects(scene)).status + @test status.one_arg == 2.0 + @test status.two_arg == 9 * 3600.0 + + invalid = (a, b, c) -> 0 + invalid_scene = Scene( + Object(:leaf; scale=:Leaf); + applications=( + ModelSpec(TemporalReducerSourceModel(); name=:source) |> + AppliesTo(One(scale=:Leaf)), + ModelSpec(TemporalReducerOneArgModel(); name=:consumer) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs(:reduced => One( + scale=:Leaf, + application=:source, + var=:signal, + policy=Aggregate(invalid), + )), + ), + ) + @test_throws "must accept values or values and durations" Advanced.refresh_bindings!(invalid_scene) +end diff --git a/test/test-scene-time-validation.jl b/test/test-scene-time-validation.jl new file mode 100644 index 000000000..8b98d3734 --- /dev/null +++ b/test/test-scene-time-validation.jl @@ -0,0 +1,61 @@ +using Dates +using PlantSimEngine +using Test + +PlantSimEngine.@process "time_validation_counter" verbose = false +struct TimeValidationCounterModel <: AbstractTime_Validation_CounterModel end +PlantSimEngine.inputs_(::TimeValidationCounterModel) = NamedTuple() +PlantSimEngine.outputs_(::TimeValidationCounterModel) = (count=0,) +function PlantSimEngine.run!(::TimeValidationCounterModel, models, status, meteo, constants, extra) + status.count += 1 +end + +function time_validation_scene(environment; cadence=nothing) + spec = ModelSpec(TimeValidationCounterModel(); name=:counter) |> + AppliesTo(One(scale=:Scene)) + isnothing(cadence) || (spec = spec |> TimeStep(cadence)) + return Scene( + Object(:scene; scale=:Scene); + applications=(spec,), + environment=environment, + ) +end + +@testset "environment duration validation" begin + @test_throws "Missing required `duration` in meteorology row 1" Advanced.refresh_bindings!( + time_validation_scene([(T=20.0,)]) + ) + @test_throws "meteorology row 2" Advanced.refresh_bindings!( + time_validation_scene([(T=20.0, duration=Hour(1)), (T=21.0,)]) + ) + @test_throws "Invalid duration" Advanced.refresh_bindings!( + time_validation_scene([(duration="one hour",)]) + ) + @test_throws "positive period" Advanced.refresh_bindings!( + time_validation_scene([(duration=Hour(0),)]) + ) + @test_throws "positive period" Advanced.refresh_bindings!( + time_validation_scene([(duration=Hour(-1),)]) + ) + @test_throws "Inconsistent `duration` in meteorology row 2" Advanced.refresh_bindings!( + time_validation_scene([(duration=Hour(1),), (duration=Minute(30),)]) + ) + @test_throws "shorter than the simulation base step" Advanced.refresh_bindings!( + time_validation_scene([(duration=Hour(1),)]; cadence=Minute(30)) + ) + @test_throws "Unsupported non-fixed period" Advanced.refresh_bindings!( + time_validation_scene([(duration=Hour(1),)]; cadence=Month(1)) + ) +end + +@testset "CompoundPeriod base step" begin + base = Dates.CompoundPeriod(Minute(30)) + scene = time_validation_scene([(duration=base,) for _ in 1:48]; cadence=Day(1)) + compiled = Advanced.refresh_bindings!(scene) + schedule = only(explain_schedule(compiled)) + @test schedule.dt_steps == 48.0 + @test schedule.dt_seconds == 86_400.0 + simulation = run!(scene; steps=1, outputs=:all) + @test only(scene_objects(scene)).status.count == 1 + @test length(scene_outputs(simulation)[(:counter, ObjectId(:scene), :count)]) == 1 +end diff --git a/test/test-unified-scene-object-api.jl b/test/test-unified-scene-object-api.jl index f055175e8..ad14a1160 100644 --- a/test/test-unified-scene-object-api.jl +++ b/test/test-unified-scene-object-api.jl @@ -11,7 +11,7 @@ struct SceneObjectDefaultInputConsumerModel <: AbstractScene_Object_Default_Inpu PlantSimEngine.inputs_(::SceneObjectDefaultInputConsumerModel) = (leaf_carbon=[0.0],) PlantSimEngine.outputs_(::SceneObjectDefaultInputConsumerModel) = (plant_carbon=0.0,) PlantSimEngine.dep(::SceneObjectDefaultInputConsumerModel) = ( - leaf_carbon=Input(Many(scale=:Leaf, within=Self(), var=:leaf_carbon)), + leaf_carbon=Input(Many(scale=:Leaf, within=Subtree(), var=:leaf_carbon)), ) PlantSimEngine.@process "scene_object_default_call_consumer" verbose = false @@ -551,7 +551,7 @@ PlantSimEngine.inputs_(::SceneObjectGrowthModel) = NamedTuple() PlantSimEngine.outputs_(::SceneObjectGrowthModel) = (created_count=0,) function PlantSimEngine.run!(::SceneObjectGrowthModel, models, status, meteo, constants=nothing, extra=nothing) - scene = extra.compiled.scene + scene = runtime_scene(extra) if isapprox(extra.time, 1.0) && !(ObjectId(:grown_leaf) in object_ids(scene; scale=:Leaf)) register_object!( scene, @@ -570,7 +570,7 @@ PlantSimEngine.inputs_(::SceneObjectPruningModel) = NamedTuple() PlantSimEngine.outputs_(::SceneObjectPruningModel) = (removed_count=0,) function PlantSimEngine.run!(::SceneObjectPruningModel, models, status, meteo, constants=nothing, extra=nothing) - scene = extra.compiled.scene + scene = runtime_scene(extra) if isapprox(extra.time, 2.0) && ObjectId(:leaf_2) in object_ids(scene; scale=:Leaf) remove_object!(scene, :leaf_2) status.removed_count += 1 @@ -587,7 +587,7 @@ PlantSimEngine.outputs_(::SceneObjectGeometryMoverModel) = (move_count=0,) function PlantSimEngine.run!(::SceneObjectGeometryMoverModel, models, status, meteo, constants=nothing, extra=nothing) if isapprox(extra.time, 1.0) - update_geometry!(extra.compiled.scene, :leaf_1, (cell=:cell_b,)) + update_geometry!(runtime_scene(extra), :leaf_1, (cell=:cell_b,)) status.move_count += 1 end return nothing @@ -796,7 +796,7 @@ end ) @test new_leaf_object.status === new_leaf_status @test new_leaf_object.parent == ObjectId(:plant_2) - @test bindings_dirty(mtg_scene) + @test Advanced.bindings_dirty(mtg_scene) child_count = length(MultiScaleTreeGraph.children(mtg_plant)) @test_throws ErrorException add_organ!( @@ -864,7 +864,7 @@ end @test scene_scope.selector isa SceneScope @test scene_scope.object_ids == [:axis_1, :leaf_1, :leaf_2, :leaf_3, :plant_1, :plant_2, :scene, :soil] plant_1_scope = only(row for row in scope_rows if row.scope_type == :object_subtree && row.root_id == :plant_1) - @test plant_1_scope.selector isa Self + @test plant_1_scope.selector isa Subtree @test plant_1_scope.object_ids == [:axis_1, :leaf_1, :leaf_2, :plant_1] palm_2_scope = only(row for row in scope_rows if row.scope_type == :named_scope && row.name == :palm_2) @test palm_2_scope.selector isa Scope @@ -881,9 +881,9 @@ end @test only(resolve_objects(selector_scene, One(scale=:Scene))).id == ObjectId(:scene) @test resolve_object_ids(selector_scene, Many(Kind(:plant), Scale(:Leaf))) == [ObjectId(:leaf_1), ObjectId(:leaf_2), ObjectId(:leaf_3)] - @test resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Self()); context=:plant_1) == + @test resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Subtree()); context=:plant_1) == [ObjectId(:leaf_1), ObjectId(:leaf_2)] - @test resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Self()); context=:leaf_2) == + @test resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Subtree()); context=:leaf_2) == [ObjectId(:leaf_2)] @test resolve_object_ids(selector_scene, Many(scale=:Leaf, within=SelfPlant()); context=:leaf_2) == [ObjectId(:leaf_1), ObjectId(:leaf_2)] @@ -938,7 +938,7 @@ end end @test contains(scope_selector_error, "available=[:axis_1") @test contains(scope_selector_error, "suggestions=[:palm_1, :palm_2]") - @test_throws ErrorException resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Self())) + @test_throws ErrorException resolve_object_ids(selector_scene, Many(scale=:Leaf, within=Subtree())) @test resolve_object_ids(selector_scene, Many(scale=:Leaf); context=:plant_1) == [ObjectId(:leaf_1), ObjectId(:leaf_2), ObjectId(:leaf_3)] @@ -972,7 +972,7 @@ end ), ), ) - relation_input_compiled = refresh_bindings!(relation_input_scene) + relation_input_compiled = Advanced.refresh_bindings!(relation_input_scene) relation_binding = only( row for row in explain_bindings(relation_input_compiled) if row.application_id == :leaf_consumer @@ -994,7 +994,7 @@ end Inputs( :signals => Many( scale=:Leaf, - within=Self(), + within=Subtree(), process=:scene_object_signal_source, var=:signal, ), @@ -1071,7 +1071,7 @@ end ObjectId(:templated_plant_3), ObjectId(:templated_plant_4), ] - template_compiled = compile_scene(template_scene) + template_compiled = Advanced.compile_scene(template_scene) template_application_rows = explain_scene_applications(template_compiled) @test only(row for row in template_application_rows if row.application_id == :palm_1__signal_source).target_ids == [:templated_leaf_1, :templated_leaf_1_exception] @@ -1137,7 +1137,7 @@ end row.object_ids for row in explain_instances(template_scene) if row.name == :palm_2 ) - refreshed_template = refresh_bindings!(template_scene) + refreshed_template = Advanced.refresh_bindings!(template_scene) @test ObjectId(:templated_leaf_new) in refreshed_template.applications_by_id[:palm_2__signal_source].target_ids remove_object!(template_scene, :templated_leaf_new) @@ -1212,7 +1212,7 @@ end ), ), ) - @test_throws ErrorException compile_scene(unmatched_override_scene) + @test_throws ErrorException Advanced.compile_scene(unmatched_override_scene) call_template = ObjectTemplate( ( @@ -1253,7 +1253,7 @@ end leaf_selector = Many( kind="plant", scale=:Leaf, - within=Self(), + within=Subtree(), process="leaf_state", var="leaf_area", policy=Integrate(), @@ -1262,14 +1262,14 @@ end @test leaf_selector.criteria.kind == :plant @test leaf_selector.criteria.scale == :Leaf - @test leaf_selector.criteria.within isa Self + @test leaf_selector.criteria.within isa Subtree @test leaf_selector.criteria.process == :leaf_state @test leaf_selector.criteria.var == :leaf_area @test leaf_selector.criteria.policy isa Integrate @test leaf_selector.criteria.window == Day(1) address = object_address(leaf_selector) - @test address.scope isa Self + @test address.scope isa Subtree @test address.kind == :plant @test address.scale == :Leaf @test address.process == :leaf_state @@ -1279,9 +1279,9 @@ end @test One(Kind(:plant), Scale(:Leaf)).criteria.selectors == (Kind(:plant), Scale(:Leaf)) @test object_address(OptionalOne(scale=:Scene)).multiplicity == :optional_one - default_input = Input(Many(scale=:Leaf, within=Self(), var=:leaf_carbon)) + default_input = Input(Many(scale=:Leaf, within=Subtree(), var=:leaf_carbon)) @test default_input.selector isa Many - @test default_input.selector.criteria.within isa Self + @test default_input.selector.criteria.within isa Subtree default_call = Call(process=:stomatal_conductance) @test default_call.selector isa One @@ -1292,7 +1292,7 @@ end AppliesTo(Many(kind=:plant, scale=:Leaf)) |> Inputs( :leaf_areas => Many(kind=:plant, scale=:Leaf, within=SceneScope(), var=:leaf_area), - :leaf_carbon => Many(scale=:Leaf, within=Self(), var=:leaf_carbon, policy=Integrate(), window=Day(1)), + :leaf_carbon => Many(scale=:Leaf, within=Subtree(), var=:leaf_carbon, policy=Integrate(), window=Day(1)), ) |> Calls(:stomata => One(scale=:Leaf, process=:stomatal_conductance)) |> TimeStep(Hour(1)) |> @@ -1330,7 +1330,7 @@ end @test value_inputs(default_input_spec).leaf_carbon isa Many @test PlantSimEngine.input_origins(default_input_spec).leaf_carbon == :model_default - @test value_inputs(default_input_spec).leaf_carbon.criteria.within isa Self + @test value_inputs(default_input_spec).leaf_carbon.criteria.within isa Subtree @test !haskey(dep(default_input_spec), :leaf_carbon) override_input_spec = ModelSpec(SceneObjectDefaultInputConsumerModel()) |> @@ -1350,7 +1350,7 @@ end status=Status(leaf_carbon=2.0, carbon_override=3.0), ), ) - default_input_origin_compiled = compile_scene( + default_input_origin_compiled = Advanced.compile_scene( default_input_origin_scene, ( default_input_spec |> @@ -1359,7 +1359,7 @@ end ) @test only(explain_bindings(default_input_origin_compiled)).origin == :model_default - override_input_origin_compiled = compile_scene( + override_input_origin_compiled = Advanced.compile_scene( default_input_origin_scene, ( override_input_spec |> @@ -1406,7 +1406,7 @@ end AppliesTo(One(scale=:Leaf)), ), ) - manual_child_compiled = compile_scene(manual_child_scene) + manual_child_compiled = Advanced.compile_scene(manual_child_scene) @test isempty( filter( binding -> binding.application_id == :manual_consumer, @@ -1421,7 +1421,7 @@ end AppliesTo(Many(scale=:Leaf)) |> Inputs(:leaf_areas => Many(scale=:Leaf, within=SelfPlant(), var=:leaf_area, policy=Integrate(), window=Day(1))), ) - compiled = compile_scene(selector_scene, compiled_specs) + compiled = Advanced.compile_scene(selector_scene, compiled_specs) application_rows = explain_scene_applications(compiled) @test length(application_rows) == 2 @test only(row for row in application_rows if row.application_id == :stomata).target_ids == @@ -1481,7 +1481,7 @@ end @test bundle_row.processes == [:scene_object_leaf_energy, :scene_object_stomata] @test bundle_row.model_types == [SceneObjectLeafEnergyModel, SceneObjectStomataModel] - compiled_environment = compile_environment_bindings(selector_scene, compiled) + compiled_environment = Advanced.compile_environment_bindings(selector_scene, compiled) execution_plan = PlantSimEngine.compile_scene_execution_plan(compiled, compiled_environment) execution_rows = explain_execution_plan(execution_plan) @@ -1513,8 +1513,8 @@ end AppliesTo(Many(scale=:Leaf)), ), ) - batch_compiled = refresh_bindings!(batch_scene) - batch_environment = refresh_environment_bindings!(batch_scene, batch_compiled) + batch_compiled = Advanced.refresh_bindings!(batch_scene) + batch_environment = Advanced.refresh_environment_bindings!(batch_scene, batch_compiled) batch_plan = PlantSimEngine.compile_scene_execution_plan(batch_compiled, batch_environment) @test length(batch_plan.batches) == 1 @@ -1584,9 +1584,9 @@ end ), ), ) - heterogeneous_compiled = refresh_bindings!(heterogeneous_scene) + heterogeneous_compiled = Advanced.refresh_bindings!(heterogeneous_scene) heterogeneous_environment = - refresh_environment_bindings!(heterogeneous_scene, heterogeneous_compiled) + Advanced.refresh_environment_bindings!(heterogeneous_scene, heterogeneous_compiled) heterogeneous_plan = PlantSimEngine.compile_scene_execution_plan( heterogeneous_compiled, heterogeneous_environment, @@ -1621,7 +1621,7 @@ end ModelSpec(SceneObjectLeafEnergyModel(); name=:leaf_energy) |> AppliesTo(Many(scale=:Leaf)), ) - @test_throws ErrorException compile_scene(selector_scene, ambiguous_call_specs) + @test_throws ErrorException Advanced.compile_scene(selector_scene, ambiguous_call_specs) disambiguated_call_specs = ( ModelSpec(SceneObjectStomataModel(); name=:sunlit_stomata) |> @@ -1634,7 +1634,7 @@ end Inputs(:leaf_areas => Many(scale=:Leaf, within=SelfPlant(), var=:leaf_area)) |> Calls(:stomata => One(process=:scene_object_stomata, application=:sunlit_stomata)), ) - disambiguated = compile_scene(selector_scene, disambiguated_call_specs) + disambiguated = Advanced.compile_scene(selector_scene, disambiguated_call_specs) disambiguated_call = only(row for row in explain_calls(disambiguated) if row.consumer_id == :leaf_2) @test disambiguated_call.origin == :model_spec @test disambiguated_call.callee_application_ids == [:sunlit_stomata] @@ -1680,7 +1680,7 @@ end ), ), ) - optional_compiled = refresh_bindings!(optional_dependency_scene) + optional_compiled = Advanced.refresh_bindings!(optional_dependency_scene) optional_binding = only(explain_bindings(optional_compiled)) @test optional_binding.multiplicity == :optional_one @test isempty(optional_binding.source_ids) @@ -1712,7 +1712,7 @@ end Inputs( :renamed_signal => One( scale=:Leaf, - within=Self(), + within=Subtree(), application=:signal_source, var=:signal, ), @@ -1721,7 +1721,7 @@ end AppliesTo(One(scale=:Leaf)), ), ) - renamed_compiled = refresh_bindings!(renamed_input_scene) + renamed_compiled = Advanced.refresh_bindings!(renamed_input_scene) renamed_binding = only(explain_bindings(renamed_compiled)) @test renamed_binding.input == :renamed_signal @test renamed_binding.source_var == :signal @@ -1744,19 +1744,19 @@ end Object(:plant_2; scale=:Plant, kind=:plant, parent=:scene, status=Status(signal_sum=0.0, temporal_total=0.0)), Object(:leaf_3; scale=:Leaf, kind=:plant, parent=:plant_2, status=Status(leaf_area=3.0)), ) - plant_default_scope = compile_scene( + plant_default_scope = Advanced.compile_scene( default_scope_scene, ( ModelSpec(SceneObjectTemporalSumModel(); name=:plant_leaf_sum) |> AppliesTo(Many(scale=:Plant)) |> - Inputs(:signal_sum => Many(scale=:Leaf, var=:leaf_area)), + Inputs(:signal_sum => Many(scale=:Leaf, within=Subtree(), var=:leaf_area)), ), ) @test only(row for row in explain_bindings(plant_default_scope) if row.consumer_id == :plant_1).source_ids == [:leaf_1, :leaf_2] @test only(row for row in explain_bindings(plant_default_scope) if row.consumer_id == :plant_2).source_ids == [:leaf_3] - scene_default_scope = compile_scene( + scene_default_scope = Advanced.compile_scene( default_scope_scene, ( ModelSpec(SceneObjectTemporalSumModel(); name=:scene_leaf_sum) |> @@ -1776,7 +1776,7 @@ end ModelSpec(SceneObjectSignalConsumerModel(); name=:signal_consumer) |> AppliesTo(One(scale=:Leaf)), ) - inferred_compiled = compile_scene(inferred_input_scene, inferred_input_specs) + inferred_compiled = Advanced.compile_scene(inferred_input_scene, inferred_input_specs) inferred_binding = only(explain_bindings(inferred_compiled)) @test inferred_binding.application_id == :signal_consumer @test inferred_binding.input == :signal @@ -1812,7 +1812,7 @@ end AppliesTo(One(scale=:Leaf)), ), ) - generated_status_compiled = refresh_bindings!(generated_status_scene) + generated_status_compiled = Advanced.refresh_bindings!(generated_status_scene) generated_status = only(scene_objects(generated_status_scene; scale=:Leaf)).status @test generated_status isa Status @test Set(propertynames(generated_status)) == @@ -1830,7 +1830,7 @@ end Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(signal=0.0, observed_signal=0.0)); applications=reverse(inferred_input_specs), ) - reversed_compiled = refresh_bindings!(reversed_dependency_scene) + reversed_compiled = Advanced.refresh_bindings!(reversed_dependency_scene) @test length(reversed_compiled.applications_by_id) == length(reversed_compiled.applications) @test reversed_compiled.applications_by_id[:signal_source].process == :scene_object_signal_source @test reversed_compiled.applications_by_id[:signal_consumer].process == :scene_object_signal_consumer @@ -1841,7 +1841,7 @@ end run!(reversed_dependency_scene) @test only(scene_objects(reversed_dependency_scene; scale=:Leaf)).status.observed_signal == 1.0 - @test_throws ErrorException compile_scene( + @test_throws ErrorException Advanced.compile_scene( Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene, status=Status(cycle_a=0.0, cycle_b=0.0)), @@ -1877,7 +1877,7 @@ end AppliesTo(One(scale=:Leaf)), ), ) - lagged_cycle_compiled = refresh_bindings!(lagged_cycle_scene) + lagged_cycle_compiled = Advanced.refresh_bindings!(lagged_cycle_scene) @test lagged_cycle_compiled.application_order == [:cycle_a, :cycle_b] lagged_binding = only( row for row in explain_bindings(lagged_cycle_compiled) @@ -1889,7 +1889,7 @@ end lagged_cycle_simulation = run!( lagged_cycle_scene; steps=3, - tracked_outputs=OutputRequest( + outputs=OutputRequest( :Leaf, :cycle_a; name=:lagged_cycle_a, @@ -1929,14 +1929,14 @@ end Inputs( PreviousTimeStep(:signals) => Many( scale=:Leaf, - within=Self(), + within=Subtree(), var=:signal, ), ), ), ) lagged_external_binding = only( - row for row in explain_bindings(refresh_bindings!(lagged_external_state_scene)) + row for row in explain_bindings(Advanced.refresh_bindings!(lagged_external_state_scene)) if row.application_id == :plant_signal_sum && row.input == :signals ) @test lagged_external_binding.carrier_kind == :temporal_stream @@ -1968,9 +1968,9 @@ end AppliesTo(One(scale=:Leaf)), ), ) - @test_throws "PreviousTimeStep marker for input `cycle_b`" refresh_bindings!(mismatched_lag_scene) + @test_throws "PreviousTimeStep marker for input `cycle_b`" Advanced.refresh_bindings!(mismatched_lag_scene) - @test_throws ErrorException compile_scene( + @test_throws ErrorException Advanced.compile_scene( Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:scene), @@ -1980,7 +1980,7 @@ end AppliesTo(One(scale=:Leaf)), ), ) - @test_throws ErrorException compile_scene( + @test_throws ErrorException Advanced.compile_scene( inferred_input_scene, ( ModelSpec(SceneObjectSignalSourceModel(); name=:sunlit_signal) |> @@ -2000,12 +2000,12 @@ end AppliesTo(One(scale=:Leaf)) |> Inputs(:signal => One(scale=:Leaf, var=:signal, process=:scene_object_signal_source, application=:signal_source)), ) - filtered_binding = only(explain_bindings(compile_scene(inferred_input_scene, filtered_input_specs))) + filtered_binding = only(explain_bindings(Advanced.compile_scene(inferred_input_scene, filtered_input_specs))) @test filtered_binding.origin == :model_spec @test filtered_binding.source_application_ids == [:signal_source] @test filtered_binding.process == :scene_object_signal_source @test filtered_binding.application == :signal_source - @test_throws ErrorException compile_scene( + @test_throws ErrorException Advanced.compile_scene( inferred_input_scene, ( filtered_input_specs[1], @@ -2014,7 +2014,7 @@ end Inputs(:signal => One(scale=:Leaf, var=:signal, application=:missing_source)), ), ) - @test_throws ErrorException compile_scene( + @test_throws ErrorException Advanced.compile_scene( inferred_input_scene, ( filtered_input_specs[1], @@ -2023,7 +2023,7 @@ end Inputs(:siggnal => One(scale=:Leaf, var=:signal, application=:signal_source)), ), ) - @test_throws ErrorException compile_scene( + @test_throws ErrorException Advanced.compile_scene( inferred_input_scene, ( filtered_input_specs[1], @@ -2051,7 +2051,7 @@ end AppliesTo(Many(scale=:Leaf)) |> Inputs(:soil_water_content => One(scale=:Soil, within=SceneScope(), var=:soil_water_content)), ) - carrier_compiled = compile_scene(carrier_scene, carrier_specs) + carrier_compiled = Advanced.compile_scene(carrier_scene, carrier_specs) carrier_rows = explain_bindings(carrier_compiled) leaf_1_carrier_bindings = carrier_compiled.input_bindings_by_target[(:carrier_consumer, ObjectId(:leaf_1))] @test length(leaf_1_carrier_bindings) == 2 @@ -2127,13 +2127,13 @@ end ), ), ) - generic_carrier_compiled = refresh_bindings!(generic_carrier_scene) + generic_carrier_compiled = Advanced.refresh_bindings!(generic_carrier_scene) generic_carrier_binding = only(generic_carrier_compiled.input_bindings) @test input_carrier(generic_carrier_binding) isa PlantSimEngine.RefVector{SceneObjectDualLike{BigFloat}} @test eltype(input_carrier(generic_carrier_binding)) == SceneObjectDualLike{BigFloat} - generic_carrier_sim = run!(generic_carrier_scene) + generic_carrier_sim = run!(generic_carrier_scene; outputs=:all) generic_scene_status = only(scene_objects(generic_carrier_scene; scale=:Scene)).status @test generic_scene_status.values === input_carrier(generic_carrier_binding) @test generic_scene_status.total == SceneObjectDualLike(big"4.0", big"2.0") @@ -2160,39 +2160,39 @@ end Object(:soil; scale=:Soil, kind=:soil, parent=:scene); applications=compiled_specs, ) - @test bindings_dirty(cache_scene) - cached_a = refresh_bindings!(cache_scene) - @test cached_a isa CompiledScene - @test !bindings_dirty(cache_scene) - @test compiled_bindings(cache_scene) === cached_a - @test cached_a.revision == scene_revision(cache_scene) - @test refresh_bindings!(cache_scene) === cached_a + @test Advanced.bindings_dirty(cache_scene) + cached_a = Advanced.refresh_bindings!(cache_scene) + @test cached_a isa Advanced.CompiledScene + @test !Advanced.bindings_dirty(cache_scene) + @test Advanced.compiled_bindings(cache_scene) === cached_a + @test cached_a.revision == Advanced.scene_revision(cache_scene) + @test Advanced.refresh_bindings!(cache_scene) === cached_a register_object!(cache_scene, Object(:leaf_4; scale=:Leaf, kind=:plant, species=:oil_palm); parent=:plant_2) - @test bindings_dirty(cache_scene) - @test isnothing(compiled_bindings(cache_scene)) - cached_b = refresh_bindings!(cache_scene) + @test Advanced.bindings_dirty(cache_scene) + @test isnothing(Advanced.compiled_bindings(cache_scene)) + cached_b = Advanced.refresh_bindings!(cache_scene) @test cached_b !== cached_a - @test cached_b.revision == scene_revision(cache_scene) + @test cached_b.revision == Advanced.scene_revision(cache_scene) @test only(row for row in explain_scene_applications(cached_b) if row.application_id == :leaf_energy).target_ids == [:leaf_1, :leaf_2, :leaf_3, :leaf_4] @test only(row for row in explain_bindings(cached_b) if row.consumer_id == :leaf_3).source_ids == [:leaf_3, :leaf_4] move_object!(cache_scene, :leaf_4, (x=3.0, y=0.0)) - @test !bindings_dirty(cache_scene) - @test environment_bindings_dirty(cache_scene) - @test refresh_bindings!(cache_scene) === cached_b + @test !Advanced.bindings_dirty(cache_scene) + @test Advanced.environment_bindings_dirty(cache_scene) + @test Advanced.refresh_bindings!(cache_scene) === cached_b mark_environment_binding_dirty!(cache_scene) - @test !bindings_dirty(cache_scene) + @test !Advanced.bindings_dirty(cache_scene) reparent_object!(cache_scene, :leaf_4, :plant_1) - cached_c = refresh_bindings!(cache_scene) + cached_c = Advanced.refresh_bindings!(cache_scene) @test only(row for row in explain_bindings(cached_c) if row.consumer_id == :leaf_4).source_ids == [:leaf_1, :leaf_2, :leaf_4] remove_object!(cache_scene, :leaf_4) - cached_d = refresh_bindings!(cache_scene) + cached_d = Advanced.refresh_bindings!(cache_scene) @test only(row for row in explain_scene_applications(cached_d) if row.application_id == :leaf_energy).target_ids == [:leaf_1, :leaf_2, :leaf_3] @@ -2213,10 +2213,10 @@ end applications=environment_specs, environment=grid_backend, ) - compiled_environment = refresh_environment_bindings!(environment_scene) - @test compiled_environment isa CompiledEnvironmentBindings - @test !environment_bindings_dirty(environment_scene) - @test compiled_environment_bindings(environment_scene) === compiled_environment + compiled_environment = Advanced.refresh_environment_bindings!(environment_scene) + @test compiled_environment isa Advanced.CompiledEnvironmentBindings + @test !Advanced.environment_bindings_dirty(environment_scene) + @test Advanced.compiled_environment_bindings(environment_scene) === compiled_environment @test length(compiled_environment.by_target) == length(compiled_environment.bindings) @test compiled_environment.by_target[(:probe, ObjectId(:leaf_1))].cell == :cell_a @test compiled_environment.by_target[(:temperature_update, ObjectId(:leaf_2))].cell == :cell_b @@ -2248,8 +2248,8 @@ end environment=(T=20.0,), ) @test_throws "co2_probe" validate_meteo_inputs(missing_global_meteo_scene) - @test_throws "Scene environment is missing required meteo inputs" refresh_environment_bindings!(missing_global_meteo_scene) - @test_throws "source `CO2`" refresh_environment_bindings!(missing_global_meteo_scene) + @test_throws "Scene environment is missing required meteo inputs" Advanced.refresh_environment_bindings!(missing_global_meteo_scene) + @test_throws "source `CO2`" Advanced.refresh_environment_bindings!(missing_global_meteo_scene) application_environment_backend = SceneObjectMutableEnvironmentBackend(:cell_a => 23.0) @@ -2270,10 +2270,10 @@ end environment=(T=20.0,), ) @test validate_meteo_inputs(application_environment_scene) === nothing - @test validate_meteo_inputs(refresh_bindings!(application_environment_scene)) === + @test validate_meteo_inputs(Advanced.refresh_bindings!(application_environment_scene)) === nothing @test_throws "CO2" validate_meteo_inputs( - refresh_bindings!(application_environment_scene), + Advanced.refresh_bindings!(application_environment_scene), (T=20.0,), ) @@ -2289,14 +2289,14 @@ end ) @test validate_meteo_inputs(remapped_global_meteo_scene) === nothing @test_throws "Ca" validate_meteo_inputs( - refresh_bindings!(remapped_global_meteo_scene), + Advanced.refresh_bindings!(remapped_global_meteo_scene), (T=20.0, CO2=415.0), ) @test validate_meteo_inputs( - refresh_bindings!(remapped_global_meteo_scene), + Advanced.refresh_bindings!(remapped_global_meteo_scene), (T=20.0, Ca=415.0), ) === nothing - remapped_environment = refresh_environment_bindings!(remapped_global_meteo_scene) + remapped_environment = Advanced.refresh_environment_bindings!(remapped_global_meteo_scene) remapped_row = only(explain_environment_bindings(remapped_environment)) @test remapped_row.required_inputs == [:T, :CO2] @test remapped_row.source_inputs == [:T, :Ca] @@ -2316,11 +2316,11 @@ end environment=(T=21.0, Ca=420.0), ) @test validate_meteo_inputs(hinted_global_meteo_scene) === nothing - hinted_environment = refresh_environment_bindings!(hinted_global_meteo_scene) + hinted_environment = Advanced.refresh_environment_bindings!(hinted_global_meteo_scene) hinted_row = only(explain_environment_bindings(hinted_environment)) @test hinted_row.required_inputs == [:T, :CO2] @test hinted_row.source_inputs == [:T, :Ca] - hinted_application = refresh_bindings!(hinted_global_meteo_scene).applications_by_id[:co2_probe] + hinted_application = Advanced.refresh_bindings!(hinted_global_meteo_scene).applications_by_id[:co2_probe] @test meteo_bindings(hinted_application.spec).CO2.source == :Ca run!(hinted_global_meteo_scene) hinted_status = only(scene_objects(hinted_global_meteo_scene; scale=:Leaf)).status @@ -2338,11 +2338,11 @@ end environment=(T=22.0, Ca=420.0, Cb=430.0), ) hinted_override_row = only( - explain_environment_bindings(refresh_environment_bindings!(hinted_override_global_meteo_scene)) + explain_environment_bindings(Advanced.refresh_environment_bindings!(hinted_override_global_meteo_scene)) ) @test hinted_override_row.source_inputs == [:T, :Cb] hinted_override_application = - refresh_bindings!(hinted_override_global_meteo_scene).applications_by_id[:co2_probe] + Advanced.refresh_bindings!(hinted_override_global_meteo_scene).applications_by_id[:co2_probe] @test meteo_bindings(hinted_override_application.spec).CO2.source == :Cb @test meteo_bindings(hinted_override_application.spec).CO2.reducer isa MeanReducer run!(hinted_override_global_meteo_scene) @@ -2401,7 +2401,7 @@ end ), environment=windowed_weather, ) - windowed_default_sim = run!(windowed_default_scene; steps=4) + windowed_default_sim = run!(windowed_default_scene; steps=4, outputs=:all) for leaf in scene_objects(windowed_default_scene; scale=:Leaf) @test leaf.status.temperature_seen == 25.0 values = getproperty.( @@ -2436,10 +2436,10 @@ end environment=windowed_weather, ) windowed_override_application = - refresh_bindings!(windowed_override_scene).applications_by_id[:aggregated_probe] + Advanced.refresh_bindings!(windowed_override_scene).applications_by_id[:aggregated_probe] @test meteo_bindings(windowed_override_application.spec).CO2.source == :Cb @test meteo_bindings(windowed_override_application.spec).CO2.reducer isa MeanReducer - windowed_override_sim = run!(windowed_override_scene; steps=4) + windowed_override_sim = run!(windowed_override_scene; steps=4, outputs=:all) temperature_values = getproperty.( collect_outputs( windowed_override_sim, @@ -2479,16 +2479,16 @@ end ), environment=contract_backend, ) - contract_compiled = refresh_bindings!(contract_scene) + contract_compiled = Advanced.refresh_bindings!(contract_scene) original_contract_bindings = - refresh_environment_bindings!(contract_scene, contract_compiled) + Advanced.refresh_environment_bindings!(contract_scene, contract_compiled) original_contract_binding = original_contract_bindings.by_target[(:probe, ObjectId(:leaf_1))] @test original_contract_binding.required_inputs == [:T, :CO2] @test length(contract_backend.binds) == 1 @test length(contract_backend.index_updates) == 1 - revised_contract_compiled = compile_scene( + revised_contract_compiled = Advanced.compile_scene( contract_scene, ( ModelSpec(SceneObjectTemperatureOnlyProbeModel(); name=:probe) |> @@ -2497,7 +2497,7 @@ end ), ) revised_contract_bindings = - refresh_environment_bindings!(contract_scene, revised_contract_compiled) + Advanced.refresh_environment_bindings!(contract_scene, revised_contract_compiled) revised_contract_binding = revised_contract_bindings.by_target[(:probe, ObjectId(:leaf_1))] @test revised_contract_binding.required_inputs == [:T] @@ -2505,18 +2505,18 @@ end @test revised_contract_binding !== original_contract_binding @test length(contract_backend.binds) == 1 @test length(contract_backend.index_updates) == 1 - @test refresh_environment_bindings!( + @test Advanced.refresh_environment_bindings!( contract_scene, revised_contract_compiled, ) === revised_contract_bindings - structural_environment_cache = refresh_bindings!(environment_scene) + structural_environment_cache = Advanced.refresh_bindings!(environment_scene) move_object!(environment_scene, :leaf_2, (cell=:cell_c,)) - @test !bindings_dirty(environment_scene) - @test environment_bindings_dirty(environment_scene) - @test refresh_bindings!(environment_scene) === structural_environment_cache - refreshed_environment = refresh_environment_bindings!(environment_scene) - @test !environment_bindings_dirty(environment_scene) + @test !Advanced.bindings_dirty(environment_scene) + @test Advanced.environment_bindings_dirty(environment_scene) + @test Advanced.refresh_bindings!(environment_scene) === structural_environment_cache + refreshed_environment = Advanced.refresh_environment_bindings!(environment_scene) + @test !Advanced.environment_bindings_dirty(environment_scene) @test length(grid_backend.binds) == 6 @test length(grid_backend.index_updates) == 2 @test any(entity -> entity.id == :leaf_2 && entity.geometry == (cell=:cell_c,), grid_backend.index_updates[2]) @@ -2524,24 +2524,24 @@ end update_geometry!(environment_scene, :leaf_1, (cell=:cell_e,); invalidate_environment=false) @test geometry(only(object for object in scene_objects(environment_scene; scale=:Leaf) if object.id == ObjectId(:leaf_1))) == (cell=:cell_e,) - @test !environment_bindings_dirty(environment_scene) + @test !Advanced.environment_bindings_dirty(environment_scene) mark_environment_binding_dirty!(environment_scene, :leaf_1) - @test environment_bindings_dirty(environment_scene) - refreshed_after_mark = refresh_environment_bindings!(environment_scene) - @test !environment_bindings_dirty(environment_scene) + @test Advanced.environment_bindings_dirty(environment_scene) + refreshed_after_mark = Advanced.refresh_environment_bindings!(environment_scene) + @test !Advanced.environment_bindings_dirty(environment_scene) @test length(grid_backend.binds) == 8 @test length(grid_backend.index_updates) == 3 @test any(entity -> entity.id == :leaf_1 && entity.geometry == (cell=:cell_e,), grid_backend.index_updates[3]) @test only(row for row in explain_environment_bindings(refreshed_after_mark) if row.application_id == :probe && row.object_id == :leaf_1).cell == :cell_e register_object!(environment_scene, Object(:leaf_3; scale=:Leaf, kind=:plant, species=:oil_palm, geometry=(cell=:cell_d,)); parent=:plant_1) - @test bindings_dirty(environment_scene) - @test environment_bindings_dirty(environment_scene) - refreshed_with_new_leaf = refresh_environment_bindings!(environment_scene) + @test Advanced.bindings_dirty(environment_scene) + @test Advanced.environment_bindings_dirty(environment_scene) + refreshed_with_new_leaf = Advanced.refresh_environment_bindings!(environment_scene) @test length(grid_backend.binds) == 14 @test length(grid_backend.index_updates) == 4 @test any(entity -> entity.id == :leaf_3 && entity.geometry == (cell=:cell_d,), grid_backend.index_updates[4]) - @test only(row for row in explain_scene_applications(refresh_bindings!(environment_scene)) if row.application_id == :probe).target_ids == + @test only(row for row in explain_scene_applications(Advanced.refresh_bindings!(environment_scene)) if row.application_id == :probe).target_ids == [:leaf_1, :leaf_2, :leaf_3] @test only(row for row in explain_environment_bindings(refreshed_with_new_leaf) if row.application_id == :probe && row.object_id == :leaf_3).cell == :cell_d @@ -2570,7 +2570,7 @@ end ), environment=inherited_grid_backend, ) - inherited_bindings = refresh_environment_bindings!(inherited_environment_scene) + inherited_bindings = Advanced.refresh_environment_bindings!(inherited_environment_scene) inherited_rows = explain_environment_bindings(inherited_bindings) inherited_row = only(row for row in inherited_rows if row.object_id == :inherited_leaf) positioned_row = only(row for row in inherited_rows if row.object_id == :positioned_leaf) @@ -2584,9 +2584,9 @@ end ] update_geometry!(inherited_environment_scene, :plant_1, (cell=:cell_b,)) - @test environment_bindings_dirty(inherited_environment_scene) + @test Advanced.environment_bindings_dirty(inherited_environment_scene) refreshed_inherited_bindings = - refresh_environment_bindings!(inherited_environment_scene) + Advanced.refresh_environment_bindings!(inherited_environment_scene) refreshed_inherited_rows = explain_environment_bindings(refreshed_inherited_bindings) @test only( row for row in refreshed_inherited_rows if row.object_id == :inherited_leaf @@ -2638,7 +2638,7 @@ end run!(runtime_scene) @test all(object.status.carrier_total == 4.0 for object in scene_objects(runtime_scene; scale=:Leaf)) @test all(object.status.temperature_seen == 27.5 for object in scene_objects(runtime_scene; scale=:Leaf)) - runtime_compiled = refresh_bindings!(runtime_scene) + runtime_compiled = Advanced.refresh_bindings!(runtime_scene) runtime_application = runtime_compiled.applications_by_id[:carrier_runtime] runtime_object_id = ObjectId(:leaf_1) PlantSimEngine._materialize_scene_inputs!( @@ -2684,7 +2684,7 @@ end call_status = only(scene_objects(call_runtime_scene; scale=:Leaf)).status @test call_status.signal == 1.0 @test call_status.called_signal == 1.0 - call_schedule = explain_schedule(refresh_bindings!(call_runtime_scene)) + call_schedule = explain_schedule(Advanced.refresh_bindings!(call_runtime_scene)) @test only(row for row in call_schedule if row.application_id == :signal_source).manual_call_only @test !only(row for row in call_schedule if row.application_id == :signal_source).root_scheduled @test only(row for row in call_schedule if row.application_id == :signal_caller).root_scheduled @@ -2771,7 +2771,7 @@ end ), environment=iterative_hard_call_backend, ) - iterative_hard_call_sim = run!(iterative_hard_call_scene) + iterative_hard_call_sim = run!(iterative_hard_call_scene; outputs=:all) iterative_hard_call_status = only(scene_objects(iterative_hard_call_scene; scale=:Leaf)).status @test iterative_hard_call_status.temperature_seen == 32.0 @@ -2805,7 +2805,7 @@ end AppliesTo(One(scale=:Leaf)), ), ) - hard_call_order = refresh_bindings!(hard_call_order_scene) + hard_call_order = Advanced.refresh_bindings!(hard_call_order_scene) @test hard_call_order.applications_by_id[:signal_caller].process == :scene_object_signal_caller @test hard_call_order.application_order == [:signal_source, :signal_caller, :signal_consumer] run!(hard_call_order_scene) @@ -2828,14 +2828,14 @@ end environment=(duration=Hour(1),), ) temporal_binding = only( - row for row in explain_bindings(refresh_bindings!(temporal_input_scene)) + row for row in explain_bindings(Advanced.refresh_bindings!(temporal_input_scene)) if row.application_id == :scene_temporal_sum && row.input == :signal_sum ) @test temporal_binding.carrier_hint == :temporal_stream - temporal_input_simulation = run!(temporal_input_scene; steps=3) + temporal_input_simulation = run!(temporal_input_scene; steps=3, outputs=:all) @test temporal_input_simulation isa SceneSimulation @test temporal_input_simulation.scene === temporal_input_scene - @test temporal_input_simulation.compiled isa CompiledScene + @test temporal_input_simulation.compiled isa Advanced.CompiledScene @test only(scene_objects(temporal_input_scene; scale=:Leaf)).status.signal == 3.0 @test only(scene_objects(temporal_input_scene; scale=:Scene)).status.temporal_total == 5.0 temporal_output_rows = collect_outputs(temporal_input_simulation; sink=nothing) @@ -2877,7 +2877,7 @@ end tracked_output_simulation = run!( tracked_output_scene; steps=3, - tracked_outputs=tracked_output_request, + outputs=tracked_output_request, ) tracked_output_rows = collect_outputs( tracked_output_simulation, @@ -2926,7 +2926,7 @@ end auto_tracked_output_simulation = run!( auto_tracked_output_scene; steps=3, - tracked_outputs=OutputRequest(:Leaf, :signal; name=:signal_auto), + outputs=OutputRequest(:Leaf, :signal; name=:signal_auto), ) auto_tracked_rows = collect_outputs( auto_tracked_output_simulation, @@ -2954,7 +2954,7 @@ end ) empty_retention_simulation = run!( empty_retention_scene; - tracked_outputs=OutputRequest[], + outputs=:none, ) @test isempty(scene_outputs(empty_retention_simulation)) @test isempty(explain_output_retention(empty_retention_simulation)) @@ -3004,7 +3004,7 @@ end selective_temporal_simulation = run!( selective_temporal_scene; steps=3, - tracked_outputs=selective_temporal_request, + outputs=selective_temporal_request, ) @test Set(keys(scene_outputs(selective_temporal_simulation))) == Set([ (:hourly_signal, ObjectId(:leaf_1), :signal), @@ -3083,7 +3083,7 @@ end bounded_temporal_simulation = run!( bounded_temporal_scene; steps=19, - tracked_outputs=bounded_temporal_request, + outputs=bounded_temporal_request, ) bounded_source_samples = scene_outputs(bounded_temporal_simulation)[ (:hourly_signal, ObjectId(:leaf_1), :signal) @@ -3118,7 +3118,7 @@ end temporal_holdlast_simulation = run!( temporal_holdlast_scene; steps=9, - tracked_outputs=OutputRequest( + outputs=OutputRequest( :Scene, :temporal_total; name=:holdlast_total, @@ -3147,12 +3147,12 @@ end environment=(duration=Hour(1),), ) trait_policy_binding = only( - row for row in explain_bindings(refresh_bindings!(trait_policy_scene)) + row for row in explain_bindings(Advanced.refresh_bindings!(trait_policy_scene)) if row.application_id == :trait_policy_consumer && row.input == :signal_sum ) @test trait_policy_binding.policy isa Aggregate @test trait_policy_binding.carrier_hint == :temporal_stream - trait_policy_simulation = run!(trait_policy_scene; steps=3) + trait_policy_simulation = run!(trait_policy_scene; steps=3, outputs=:all) @test only(scene_objects(trait_policy_scene; scale=:Scene)).status.temporal_total == 2.5 @test getproperty.( collect_outputs( @@ -3179,7 +3179,7 @@ end environment=(duration=Hour(1),), ) explicit_policy_binding = only( - row for row in explain_bindings(refresh_bindings!(explicit_policy_scene)) + row for row in explain_bindings(Advanced.refresh_bindings!(explicit_policy_scene)) if row.application_id == :explicit_policy_consumer && row.input == :signal_sum ) @test explicit_policy_binding.policy isa Integrate @@ -3219,7 +3219,7 @@ end ), environment=(duration=Hour(1),), ) - generic_integrate_simulation = run!(generic_integrate_scene; steps=3) + generic_integrate_simulation = run!(generic_integrate_scene; steps=3, outputs=:all) generic_integrate_values = getproperty.( collect_outputs( generic_integrate_simulation, @@ -3262,7 +3262,7 @@ end interpolation_simulation = run!( interpolation_scene; steps=5, - tracked_outputs=OutputRequest( + outputs=OutputRequest( :Leaf, :observed_signal; name=:interpolated_signal, @@ -3313,7 +3313,7 @@ end ), environment=(duration=Hour(1),), ) - interpolation_hold_simulation = run!(interpolation_hold_scene; steps=6) + interpolation_hold_simulation = run!(interpolation_hold_scene; steps=6, outputs=:all) @test getproperty.( collect_outputs( interpolation_hold_simulation, @@ -3348,7 +3348,7 @@ end ), ), ) - @test_throws "Invalid interpolation mode `spline`" refresh_bindings!(invalid_interpolation_scene) + @test_throws "Invalid interpolation mode `spline`" Advanced.refresh_bindings!(invalid_interpolation_scene) stream_only_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), @@ -3363,12 +3363,12 @@ end AppliesTo(One(scale=:Leaf)), ), ) - stream_only_compiled = refresh_bindings!(stream_only_scene) + stream_only_compiled = Advanced.refresh_bindings!(stream_only_scene) stream_only_writer = only(row for row in explain_writers(stream_only_compiled) if row.variable == :signal) @test stream_only_writer.application_ids == [:canonical_signal] stream_only_binding = only(row for row in explain_bindings(stream_only_compiled) if row.application_id == :signal_consumer) @test stream_only_binding.source_application_ids == [:canonical_signal] - stream_only_simulation = run!(stream_only_scene) + stream_only_simulation = run!(stream_only_scene; outputs=:all) stream_only_status = only(scene_objects(stream_only_scene; scale=:Leaf)).status @test stream_only_status.observed_signal == 1.0 signal_rows = collect_outputs(stream_only_simulation, :leaf_1, :signal; sink=nothing) @@ -3387,11 +3387,11 @@ end ) @test_throws "No scene output publisher found" run!( stream_only_only_scene; - tracked_outputs=OutputRequest(:Leaf, :signal; name=:stream_signal_auto_fail), + outputs=OutputRequest(:Leaf, :signal; name=:stream_signal_auto_fail), ) stream_only_requested = run!( stream_only_scene; - tracked_outputs=OutputRequest(:Leaf, :signal; name=:canonical_signal_request), + outputs=OutputRequest(:Leaf, :signal; name=:canonical_signal_request), ) stream_only_requested_rows = collect_outputs( stream_only_requested, @@ -3402,7 +3402,7 @@ end @test getproperty.(stream_only_requested_rows, :value) == [1.0] explicit_stream_application = run!( stream_only_scene; - tracked_outputs=OutputRequest( + outputs=OutputRequest( :Leaf, :signal; name=:stream_signal_by_application, @@ -3418,7 +3418,7 @@ end @test getproperty.(explicit_stream_rows, :value) == [10.0] explicit_canonical_application = run!( stream_only_scene; - tracked_outputs=OutputRequest( + outputs=OutputRequest( :Leaf, :signal; name=:canonical_signal_by_application, @@ -3435,7 +3435,7 @@ end @test getproperty.(explicit_canonical_rows, :value) == [1.0] @test_throws "application `missing_signal`" run!( stream_only_scene; - tracked_outputs=OutputRequest( + outputs=OutputRequest( :Leaf, :signal; name=:missing_signal_application, @@ -3444,7 +3444,7 @@ end ) @test_throws "Ambiguous scene output publishers" run!( stream_only_scene; - tracked_outputs=OutputRequest( + outputs=OutputRequest( :Leaf, :signal; name=:ambiguous_signal_request, @@ -3463,18 +3463,18 @@ end ModelSpec(SceneObjectBiomassPrunerModel(); name=:leaf_pruning) |> AppliesTo(One(scale=:Leaf)) - @test_throws ErrorException compile_scene(writer_scene, (biomass_source, biomass_pruner)) - @test_throws ErrorException compile_scene( + @test_throws ErrorException Advanced.compile_scene(writer_scene, (biomass_source, biomass_pruner)) + @test_throws ErrorException Advanced.compile_scene( writer_scene, (biomass_source, biomass_pruner |> Updates(:biomass; after=:water_status)), ) - @test_throws ErrorException compile_scene( + @test_throws ErrorException Advanced.compile_scene( writer_scene, (biomass_pruner |> Updates(:biomass; after=:carbon_allocation), biomass_source), ) ordered_pruner = biomass_pruner |> Updates(:biomass; after=:carbon_allocation) - writer_compiled = compile_scene(writer_scene, (biomass_source, ordered_pruner)) + writer_compiled = Advanced.compile_scene(writer_scene, (biomass_source, ordered_pruner)) writer_row = only(row for row in explain_writers(writer_compiled) if row.variable == :biomass) @test writer_row.object_id == :leaf_1 @test writer_row.duplicate @@ -3513,7 +3513,7 @@ end AppliesTo(Many(scale=:Leaf)), ModelSpec(SceneObjectPlantSignalSumModel(); name=:plant_signal_total) |> AppliesTo(One(scale=:Plant)) |> - Inputs(:signals => Many(scale=:Leaf, within=Self(), var=:signal)), + Inputs(:signals => Many(scale=:Leaf, within=Subtree(), var=:signal)), ModelSpec(SceneObjectPruningModel(); name=:pruning) |> AppliesTo(One(scale=:Scene)), ), @@ -3530,11 +3530,11 @@ end lifecycle_simulation = run!( lifecycle_scene; steps=3, - tracked_outputs=lifecycle_output_request, + outputs=lifecycle_output_request, ) - @test !bindings_dirty(lifecycle_scene) - @test !environment_bindings_dirty(lifecycle_scene) - @test lifecycle_simulation.compiled.revision == scene_revision(lifecycle_scene) + @test !Advanced.bindings_dirty(lifecycle_scene) + @test !Advanced.environment_bindings_dirty(lifecycle_scene) + @test lifecycle_simulation.compiled.revision == Advanced.scene_revision(lifecycle_scene) @test Set(object_ids(lifecycle_scene; scale=:Leaf)) == Set([ObjectId(:leaf_1), ObjectId(:grown_leaf)]) lifecycle_status = only(scene_objects(lifecycle_scene; scale=:Scene)).status @@ -3552,7 +3552,7 @@ end ) @test lifecycle_execution_row.object_ids == [:grown_leaf, :leaf_1] @test lifecycle_simulation.execution_plan.scene_revision == - scene_revision(lifecycle_scene) + Advanced.scene_revision(lifecycle_scene) @test haskey( lifecycle_simulation.compiled.model_bundles_by_target, (:leaf_signal, ObjectId(:grown_leaf)), @@ -3614,9 +3614,9 @@ end ), environment=moving_environment_backend, ) - moving_environment_simulation = run!(moving_environment_scene; steps=2) - @test !bindings_dirty(moving_environment_scene) - @test !environment_bindings_dirty(moving_environment_scene) + moving_environment_simulation = run!(moving_environment_scene; steps=2, outputs=:all) + @test !Advanced.bindings_dirty(moving_environment_scene) + @test !Advanced.environment_bindings_dirty(moving_environment_scene) @test only(scene_objects(moving_environment_scene; scale=:Scene)).status.move_count == 1 @test only(scene_objects(moving_environment_scene; scale=:Leaf)).status.temperature_seen == 30.0 moving_probe_rows = collect_outputs( @@ -3641,7 +3641,7 @@ end ), environment=(duration=Hour(1),), ) - multirate_compiled = refresh_bindings!(multirate_scene) + multirate_compiled = Advanced.refresh_bindings!(multirate_scene) schedule_rows = explain_schedule(multirate_compiled) @test only(schedule_rows).application_id == :hourly_signal @test only(schedule_rows).dt_steps == 2.0 @@ -3658,7 +3658,7 @@ end ), environment=(duration=Hour(1),), ) - trait_clock_compiled = refresh_bindings!(trait_clock_scene) + trait_clock_compiled = Advanced.refresh_bindings!(trait_clock_scene) trait_clock_schedule = only(explain_schedule(trait_clock_compiled)) @test trait_clock_schedule.application_id == :trait_clock_signal @test trait_clock_schedule.dt_steps == 2.0 @@ -3676,7 +3676,7 @@ end ), environment=(duration=Hour(1),), ) - override_schedule = only(explain_schedule(refresh_bindings!(trait_clock_override_scene))) + override_schedule = only(explain_schedule(Advanced.refresh_bindings!(trait_clock_override_scene))) @test override_schedule.dt_steps == 1.0 run!(trait_clock_override_scene; steps=5) @test only(scene_objects(trait_clock_override_scene; scale=:Leaf)).status.signal == 5.0 @@ -3690,7 +3690,7 @@ end ), environment=(duration=Hour(1),), ) - @test_throws "outside `timestep_hint.required=1 day`" refresh_bindings!(strict_hint_scene) + @test_throws "outside `timestep_hint.required=1 day`" Advanced.refresh_bindings!(strict_hint_scene) strict_hint_override_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), @@ -3702,7 +3702,7 @@ end ), environment=(duration=Hour(1),), ) - @test only(explain_schedule(refresh_bindings!(strict_hint_override_scene))).dt_steps == 1.0 + @test only(explain_schedule(Advanced.refresh_bindings!(strict_hint_override_scene))).dt_steps == 1.0 run!(strict_hint_override_scene; steps=2) @test only(scene_objects(strict_hint_override_scene; scale=:Leaf)).status.signal == 2.0 end diff --git a/test/test-updates.jl b/test/test-updates.jl index eae12d54c..3a56f55c7 100644 --- a/test/test-updates.jl +++ b/test/test-updates.jl @@ -55,7 +55,7 @@ function update_scene(applications...) end @testset "ModelSpec Updates" begin - @test_throws "Ambiguous canonical writers" compile_scene( + @test_throws "Ambiguous canonical writers" Advanced.compile_scene( update_scene( ModelSpec(UpdateCarbonAllocationModel()) |> AppliesTo(One(scale=:Leaf)), ModelSpec(UpdateLeafPruningModel()) |> AppliesTo(One(scale=:Leaf)), @@ -67,14 +67,22 @@ end ModelSpec(UpdateLeafPruningModel()) |> AppliesTo(One(scale=:Leaf)) |> Updates(:leaf_biomass; after=:update_carbon_allocation), - ModelSpec(UpdateBiomassObserverModel()) |> AppliesTo(One(scale=:Leaf)), + ModelSpec(UpdateBiomassObserverModel()) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs( + :leaf_biomass => One( + scale=:Leaf, + application=:update_leaf_pruning, + var=:leaf_biomass, + ), + ), ) run!(scene) leaf = only(scene_objects(scene; scale=:Leaf)) @test leaf.status.leaf_biomass == 0.0 @test leaf.status.observed_biomass == 0.0 - @test_throws "without an ordering relation" compile_scene( + @test_throws "without an ordering relation" Advanced.compile_scene( update_scene( ModelSpec(UpdateCarbonAllocationModel()) |> AppliesTo(One(scale=:Leaf)), ModelSpec(UpdateLeafPruningModel()) |> @@ -97,7 +105,15 @@ end :leaf_biomass; after=(:update_carbon_allocation, :update_leaf_senescence), ), - ModelSpec(UpdateBiomassObserverModel()) |> AppliesTo(One(scale=:Leaf)), + ModelSpec(UpdateBiomassObserverModel()) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs( + :leaf_biomass => One( + scale=:Leaf, + application=:update_leaf_pruning, + var=:leaf_biomass, + ), + ), ) run!(ordered) ordered_leaf = only(scene_objects(ordered; scale=:Leaf)) From f168992862265b49c8c60c9666d57af3e8039e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Sat, 20 Jun 2026 10:41:02 +0200 Subject: [PATCH 24/33] Adapt the graph editor to the new API --- .github/workflows/CI.yml | 48 ++++ Project.toml | 2 +- docs/make.jl | 24 ++ docs/src/API/API_public.md | 15 ++ docs/src/developers.md | 52 ++++ docs/src/guides/graph_visualizer_editor.md | 185 +++++++++++++ ext/PlantSimEngineGraphEditorExt.jl | 181 +++++++++++-- frontend/dist/.vite/manifest.json | 4 +- frontend/dist/assets/index-CRLmU9Qw.css | 1 - frontend/dist/assets/index-D625qdlf.js | 38 +++ frontend/dist/assets/index-Do5n-jvW.css | 1 + frontend/dist/assets/index-DryeyTC8.js | 38 --- frontend/dist/index.html | 4 +- frontend/e2e/graph-editor.spec.ts | 41 ++- frontend/src/App.test.ts | 22 +- frontend/src/App.tsx | 248 ++++++++++++++++-- frontend/src/ApplicationConfigurationForm.tsx | 88 +++++++ frontend/src/ApplicationForm.tsx | 25 +- frontend/src/BindingForm.tsx | 66 ++++- frontend/src/ModelNode.tsx | 26 ++ frontend/src/ObjectForm.tsx | 7 +- frontend/src/OverrideForm.tsx | 19 +- frontend/src/sampleGraph.ts | 7 + frontend/src/styles.css | 80 ++++++ frontend/src/types.ts | 45 +++- src/visualization/scene_graph_editor_api.jl | 103 +++++++- src/visualization/scene_graph_view.jl | 247 ++++++++++++++++- test/test-scene-graph-editor-extension.jl | 144 +++++++++- test/test-scene-graph-view.jl | 153 +++++++++++ 29 files changed, 1775 insertions(+), 139 deletions(-) create mode 100644 docs/src/guides/graph_visualizer_editor.md delete mode 100644 frontend/dist/assets/index-CRLmU9Qw.css create mode 100644 frontend/dist/assets/index-D625qdlf.js create mode 100644 frontend/dist/assets/index-Do5n-jvW.css delete mode 100644 frontend/dist/assets/index-DryeyTC8.js create mode 100644 frontend/src/ApplicationConfigurationForm.tsx diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index fdfd8b9b4..a20112922 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -75,3 +75,51 @@ jobs: using PlantSimEngine DocMeta.setdocmeta!(PlantSimEngine, :DocTestSetup, :(using PlantSimEngine); recursive=true) doctest(PlantSimEngine) + graph-editor-e2e: + name: Graph editor frontend and E2E + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + actions: write + contents: read + steps: + - uses: actions/checkout@v6 + - uses: julia-actions/setup-julia@v3 + with: + version: "1" + - uses: julia-actions/cache@v3 + - name: Configure graph editor test environment + shell: julia --project=test --color=yes {0} + run: | + using Pkg + Pkg.develop(PackageSpec(path=pwd())) + Pkg.instantiate() + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: frontend/package-lock.json + - name: Install frontend dependencies + working-directory: frontend + run: npm ci + - name: Check and build frontend + working-directory: frontend + run: | + npm run typecheck + npm test + npm run build + - name: Install Chromium + working-directory: frontend + run: npx playwright install --with-deps chromium + - name: Run graph editor end-to-end tests + working-directory: frontend + run: npm run test:e2e + - name: Upload Playwright report on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: graph-editor-playwright-report + path: | + frontend/playwright-report + frontend/test-results + if-no-files-found: ignore diff --git a/Project.toml b/Project.toml index 2cb6982f1..ffdbd85cc 100644 --- a/Project.toml +++ b/Project.toml @@ -26,7 +26,7 @@ PlantSimEngineGraphEditorExt = "HTTP" CSV = "0.10" DataFrames = "1" Dates = "1.10" -InteractiveUtils = "1.11.0" +InteractiveUtils = "1.10" JSON = "1.6.1" HTTP = "1" Markdown = "1.10" diff --git a/docs/make.jl b/docs/make.jl index b6aa0b46c..3eeff4e8d 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -5,6 +5,29 @@ using PlantMeteo using DataFrames, CSV using Documenter using CairoMakie +using PlantSimEngine.Examples + +function build_scene_graph_example() + output_dir = joinpath(@__DIR__, "src", "assets") + mkpath(output_dir) + scene = Scene( + Object(:plant; name=:plant, scale=:Plant, kind=:plant, status=Status(TT=12.0)); + applications=( + ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> + AppliesTo(One(name=:plant)), + ModelSpec(ToyLAIModel(); name=:lai) |> + AppliesTo(One(name=:plant)), + ModelSpec(Beer(0.6); name=:light_interception) |> + AppliesTo(One(name=:plant)), + ), + ) + write_scene_graph_view( + joinpath(output_dir, "scene_graph_example.html"), + scene, + ) +end + +build_scene_graph_example() DocMeta.setdocmeta!(PlantSimEngine, :DocTestSetup, :(using PlantSimEngine, PlantMeteo, DataFrames, CSV, CairoMakie); recursive=true) @@ -38,6 +61,7 @@ makedocs(; ], "Building Scenarios" => [ "Coupling models" => "./guides/coupling.md", + "Visualize and edit a Scene" => "./guides/graph_visualizer_editor.md", "Model Switching" => "./step_by_step/model_switching.md", "Implementing a process" => "./step_by_step/implement_a_process.md", "Implementing a model" => "./step_by_step/implement_a_model.md", diff --git a/docs/src/API/API_public.md b/docs/src/API/API_public.md index 8966b1e72..dd3a54dd5 100644 --- a/docs/src/API/API_public.md +++ b/docs/src/API/API_public.md @@ -80,6 +80,21 @@ Use structured explanation helpers instead of inspecting internals: See [Migrating To The Scene/Object API](../migration_scene_object.md) for translations from removed APIs. +### Scene graph visualization and editing + +- `compile_scene_report(scene; strict=false)` preserves partial graph state and + structured diagnostics for incomplete or cyclic scenes. +- `scene_graph_view(scene; level=:applications)` returns the typed graph view. +- `scene_graph_view_json(scene)` serializes the same DTO used by the browser. +- `write_scene_graph_view(path, scene)` writes a self-contained static viewer. +- `edit_graph(scene)` starts the optional HTTP editor after `using HTTP`. +- `current_scene(session)`, `undo!(session)`, `redo!(session)`, and + `close(session)` control an interactive session from Julia. + +See [Visualize And Edit A Scene](../guides/graph_visualizer_editor.md) for the +runnable workflow, model discovery, selector previews, cycle breaking, and +Documenter embedding. + ## Advanced compiler API ```@docs diff --git a/docs/src/developers.md b/docs/src/developers.md index 947e8d08f..4df15d38b 100644 --- a/docs/src/developers.md +++ b/docs/src/developers.md @@ -72,6 +72,58 @@ If a change affects public APIs or execution behavior, check both `CI` and `Integration` before merging. Benchmark results are useful for regressions, but should be interpreted alongside the test results. +## Graph Viewer Frontend + +The static viewer and HTTP editor share the React application under +`frontend/`. PlantSimEngine releases include the production bundle in +`frontend/dist`, because Julia package installations do not run Node or Vite. +The content hash in asset filenames is intentional: it prevents browsers and +documentation hosts from reusing stale JavaScript after a release. + +Install the frontend development dependencies from the repository root: + +```sh +cd frontend +npm ci +``` + +Run the fast checks while developing: + +```sh +npm run typecheck +npm test +``` + +Build the production assets after changing TypeScript, CSS, or frontend +dependencies: + +```sh +npm run build +``` + +Commit the resulting `frontend/dist` changes together with the source changes. +Do not commit `frontend/node_modules`, Playwright reports, screenshots, videos, +or local test output. + +The end-to-end suite starts a real Julia `edit_graph` session and controls it +with Chromium: + +```sh +npx playwright install chromium +npm run test:e2e +``` + +Use `npm run test:e2e:ui` for a headed local debugging session. The tests use +stable `data-testid` attributes for commands and confirm mutations through the +Julia `/state` endpoint. Avoid assertions against generated CSS classes or +implementing PlantSimEngine selector semantics in TypeScript. + +Core graph DTO and edit tests live in `test/test-scene-graph-view.jl`. +HTTP-extension tests live in `test/test-scene-graph-editor-extension.jl`. +When changing the graph schema, update those Julia tests, frontend types, unit +tests, Playwright scenarios, and the committed production bundle in the same +change. + ## Documentation impact Changes in PlantSimEngine often require documentation updates beyond the page you diff --git a/docs/src/guides/graph_visualizer_editor.md b/docs/src/guides/graph_visualizer_editor.md new file mode 100644 index 000000000..c822fd54d --- /dev/null +++ b/docs/src/guides/graph_visualizer_editor.md @@ -0,0 +1,185 @@ +```@meta +CurrentModule = PlantSimEngine +``` + +# Visualize And Edit A Scene + +The Scene graph shows how model applications, objects, and compiled value +bindings fit together before a simulation runs. Use the static visualizer when +you want an inspectable HTML artifact, and the interactive editor when you want +browser actions to update a Julia [`Scene`](@ref). + +## A Small Scene + +This example applies three toy models to one plant object. The compiler infers +the same-object `TT_cu` and `LAI` bindings from the declared input and output +names. + +```@example graph_viewer +using PlantSimEngine +using PlantSimEngine.Examples + +scene = Scene( + Object(:plant; name=:plant, scale=:Plant, kind=:plant, + status=Status(TT=12.0)); + applications=( + ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> + AppliesTo(One(name=:plant)), + ModelSpec(ToyLAIModel(); name=:lai) |> + AppliesTo(One(name=:plant)), + ModelSpec(Beer(0.6); name=:light_interception) |> + AppliesTo(One(name=:plant)), + ), +) + +view = scene_graph_view(scene) +view.metadata +``` + +The documentation build writes that graph as a self-contained HTML page and +embeds it below. + +```@raw html + + +``` + +The default **Applications** projection groups all concrete executions of one +application into one card. Use **Objects** to inspect topology and **Executions** +to inspect concrete `(application, object)` pairs. Search, diagnostics, +initialization, selectors, parameters, and resolved edge details remain +available in the static viewer. The topology projection includes scene and +instance containers; selecting an instance or object subtree scopes the +application and execution projections until the filter is cleared. + +## Write A Static Viewer + +The static visualizer is part of PlantSimEngine core and does not load a web +server: + +```julia +path = write_scene_graph_view("scene-graph.html", scene) +``` + +The output bundles the graph payload, JavaScript, and CSS in one HTML file. It +can be opened locally or embedded in Documenter documentation. A downstream +package can generate the file from `docs/make.jl` and place it under +`docs/src/assets`: + +```julia +mkpath(joinpath(@__DIR__, "src", "assets")) +write_scene_graph_view( + joinpath(@__DIR__, "src", "assets", "default_scene.html"), + default_scene(), +) +``` + +Then embed it from a Markdown page with an HTML `iframe`. The graph is +read-only, but its projection controls, search, inspector, and diagnostics are +interactive in the browser. + +## Start The Editor + +The mutable editor is an optional package extension activated by HTTP.jl. Add +HTTP once to the environment that will launch the editor: + +```julia +using Pkg +Pkg.add("HTTP") +``` + +Then start a session: + +```julia +using PlantSimEngine +using HTTP + +session = edit_graph(scene) +``` + +The default browser opens automatically. The returned session also prints its +URL and shutdown command. Julia remains authoritative: browser edits are sent +as semantic commands, applied transactionally to a candidate Scene, compiled, +and returned as a fresh graph state. + +Inspect the current result or stop the server with: + +```julia +edited_scene = current_scene(session) +close(session) +``` + +Call `edit_graph()` without a Scene to start from an empty scenario. Use +`open_browser=false` on remote machines or when a test controls the browser. + +## What Can Be Edited + +The editor supports: + +- scene objects, metadata, status initialization, and parent topology; +- model applications, constructor parameters, target selectors, and cadence; +- explicit value bindings, hard calls, output routing, and update ordering; +- shared template applications plus instance-level and object-level overrides; +- dependency cycles through an explicit `PreviousTimeStep` break action; +- undo, redo, temporary recovery autosaves, and readable Julia Scene scripts. + +Application target and binding dialogs can ask Julia to preview the concrete +objects selected by a declaration. This is important for `Many`, relative +scopes such as `SelfPlant`, and scenes containing several plant instances. + +## Models From Other Packages + +The model browser reflects concrete `AbstractModel` subtypes currently loaded +in Julia. There is no separate registration API. Loading a model package before +starting the editor makes its models available automatically: + +```julia +using PlantSimEngine +using PlantBiophysics +using HTTP + +session = edit_graph(scene) +``` + +The `+` buttons next to ports use exact declared variable names only. For an +input named `LAI`, the editor lists loaded models whose `outputs_` contains +`LAI`. For an output named `LAI`, it lists models whose `inputs_` contains +`LAI`, as well as compatible applications already present in the Scene. This is +a composition aid, not a scientific compatibility inference. + +When the Scene is saved as Julia code, required package imports are emitted for +the model types used by the Scene. + +## Invalid And Cyclic Scenes + +Simulation compilation remains strict, but graph compilation preserves as much +structure as possible and attaches diagnostics. This lets the editor display +incomplete selectors, missing initialization, ambiguous writers, and cycles. + +Cycle edges are shown in red. The break workflow asks which consumer input +should read its previous accepted timestep value and asks for initial values +when the affected target objects do not already provide one. The action changes +the application input policy for every target selected by that application. + +!!! warning + `PreviousTimeStep` changes model semantics. It disconnects the selected + input from current-step producers during one run step. Use it only when that + lag and its initialization value are scientifically intentional. + +## Saving And Recovery + +The **Save** action writes readable Julia code whose final binding is +`scene = Scene(...)`. Once a path is selected, every successful edit rewrites +that file. The editor also keeps a temporary recovery file and lists recent +Scene scripts in **Open**. + +Generated code is best effort for arbitrary Julia values and external runtime +resources. Review the code and keep important scenario scripts under Git. diff --git a/ext/PlantSimEngineGraphEditorExt.jl b/ext/PlantSimEngineGraphEditorExt.jl index 62e4115d4..d9bcf9cc0 100644 --- a/ext/PlantSimEngineGraphEditorExt.jl +++ b/ext/PlantSimEngineGraphEditorExt.jl @@ -64,7 +64,7 @@ function edit_graph( allow_remote::Bool=false, allow_julia_eval::Union{Nothing,Bool}=nothing, recover_path::Union{Nothing,AbstractString}=nothing, - recent_paths=String[], + recent_paths=nothing, ) _is_loopback_host(host) || allow_remote || error( "Graph editor sessions are limited to localhost by default. Pass `allow_remote=true` only for a trusted network.", @@ -82,6 +82,7 @@ function edit_graph( autosave_file = autosave ? _normalized_path( isnothing(autosave_path) ? _default_autosave_path() : autosave_path, ) : nothing + remembered_paths = isnothing(recent_paths) ? _load_recent_paths() : String.(recent_paths) session = GraphEditorSession( initial_scene, Any[], @@ -94,7 +95,7 @@ function edit_graph( autosave_file, isnothing(save_path) ? nothing : _normalized_path(save_path), effective_allow_julia_eval, - String[_normalized_path(path) for path in recent_paths], + String[_normalized_path(path) for path in remembered_paths], ) session_ref[] = session isnothing(session.save_path) || _remember_path!(session, session.save_path) @@ -164,6 +165,14 @@ function _handle_http(session::GraphEditorSession, stream::HTTP.Stream) _authorized_request(session, request) || return _write_response(stream, 403, "text/plain", "Forbidden session token.") if path == "/" || path == "/index.html" return _write_response(stream, 200, "text/html; charset=utf-8", _editor_html(session)) + elseif path == "/static" + view = PlantSimEngine.scene_graph_view(session.scene) + return _write_response( + stream, + 200, + "text/html; charset=utf-8", + PlantSimEngine.scene_graph_view_html(view), + ) elseif path == "/state" return _write_response(stream, 200, "application/json", _state_json(session)) end @@ -253,6 +262,10 @@ function _handle_command!(session, command) session.save_path = path _remember_path!(session, path) _persist_scene!(session) + elseif action == "preview_input_binding" + return _preview_input_binding_payload(session, command) + elseif action == "preview_application_targets" + return _preview_application_targets_payload(session, command) elseif action in ("open_add_application", "begin_add_application") # This command only focuses/prefills frontend state. The Scene is # changed by a subsequent add_application edit. @@ -265,6 +278,55 @@ function _handle_command!(session, command) end end +function _preview_application_targets_payload(session, command) + selector = _selector_from_payload(command["selector"]) + target_ids = PlantSimEngine.resolve_object_ids(session.scene, selector) + payload = _state_payload(session) + payload["targetPreview"] = Dict{String,Any}( + "objectIds" => [PlantSimEngine._scene_graph_json_value(id.value) for id in target_ids], + "count" => length(target_ids), + ) + return payload +end + +function _preview_input_binding_payload(session, command) + application_id = Symbol(command["applicationId"]) + input = Symbol(command["input"]) + candidate = PlantSimEngine.apply_scene_graph_edit( + session.scene, + PlantSimEngine.SetSceneInputBinding( + application_id, + input, + _selector_from_payload(command["selector"]), + ), + ) + report = PlantSimEngine.compile_scene_report(candidate) + bindings = [ + binding for binding in report.input_bindings + if binding.application_id == application_id && binding.input == input + ] + payload = _state_payload(session) + payload["selectorPreview"] = Dict{String,Any}( + "applicationId" => string(application_id), + "input" => string(input), + "consumerObjectIds" => unique([ + PlantSimEngine._scene_graph_json_value(binding.consumer_id.value) + for binding in bindings + ]), + "sourceObjectIds" => unique([ + PlantSimEngine._scene_graph_json_value(source_id.value) + for binding in bindings for source_id in binding.source_ids + ]), + "sourceApplicationIds" => unique([ + string(source_id) for binding in bindings + for source_id in binding.source_application_ids + ]), + "bindingCount" => length(bindings), + "diagnostics" => [diagnostic.message for diagnostic in report.diagnostics], + ) + return payload +end + function _edit_from_command(session, command) kind = String(get(command, "kind", "")) application_id = Symbol(get(command, "applicationId", "")) @@ -317,6 +379,10 @@ function _edit_from_command(session, command) application_id, _configuration_from_payload(session, get(command, "configuration", nothing)), ) + kind == "set_environment_provider" && return PlantSimEngine.SetSceneApplicationEnvironment( + application_id, + _environment_with_provider(session.scene, application_id, command["provider"]), + ) kind == "set_output_routing" && return PlantSimEngine.SetSceneOutputRouting( application_id, Symbol(command["output"]), @@ -391,6 +457,14 @@ function _edit_from_command(session, command) error("Unsupported Scene graph edit kind `$(kind)`.") end +function _environment_with_provider(scene, application_id, provider) + spec = PlantSimEngine._scene_edit_spec(scene, application_id) + current = PlantSimEngine.environment_config(spec) + current isa PlantSimEngine.EnvironmentConfig && (current = current.config) + current_values = current isa NamedTuple ? current : NamedTuple() + return merge(current_values, (provider=Symbol(provider),)) +end + function _symbol_keyed_namedtuple(payload) payload isa AbstractDict || error("Expected an object-valued configuration payload.") return (; (Symbol(key) => value for (key, value) in payload)...) @@ -627,22 +701,43 @@ function _remember_path!(session, path) filter!(!=(normalized), session.recent_paths) pushfirst!(session.recent_paths, normalized) length(session.recent_paths) > 12 && resize!(session.recent_paths, 12) + _persist_recent_paths!(session.recent_paths) return normalized end +function _recent_paths_file() + return joinpath(tempdir(), "PlantSimEngineGraphEditor", "recent-scenes.json") +end + +function _load_recent_paths() + path = _recent_paths_file() + isfile(path) || return String[] + try + values = JSON.parse(read(path, String)) + values isa AbstractVector || return String[] + return String[_normalized_path(value) for value in values if value isa AbstractString] + catch + return String[] + end +end + +function _persist_recent_paths!(paths) + path = _recent_paths_file() + mkpath(dirname(path)) + _atomic_write(path, JSON.json(collect(paths))) + return path +end + function _load_scene_file(path; allow_julia_eval::Bool) allow_julia_eval || error("Opening Julia Scene files is disabled for this editor session.") isfile(path) || error("Scene file `$(path)` does not exist.") module_name = Symbol("PlantSimEngineGraphRecovery_", string(time_ns(); base=16)) workspace = Module(module_name) Core.eval(workspace, :(using PlantSimEngine)) - Base.include(workspace, path) - isdefined(workspace, :scene) || error( - "Scene file `$(path)` must assign its final Scene to `scene`.", - ) - scene = getfield(workspace, :scene) + included = Base.include(workspace, path) + scene = isdefined(workspace, :scene) ? getfield(workspace, :scene) : included scene isa PlantSimEngine.Scene || error( - "Scene file `$(path)` assigned `scene` to `$(typeof(scene))`, expected `PlantSimEngine.Scene`.", + "Scene file `$(path)` must assign its final PlantSimEngine.Scene to `scene`.", ) return scene end @@ -668,10 +763,11 @@ function _scene_to_julia(scene) if !isnothing(scene.source_adapter) push!(diagnostics, "The Scene source_adapter is runtime-specific and is not reconstructed by generated code.") end - for object in PlantSimEngine.scene_objects(scene) - isnothing(object.applications) || object.applications == () || push!( + for model in _scene_code_models(scene) + Base.moduleroot(parentmodule(typeof(model))) === Main || continue + push!( diagnostics, - "Object $(repr(object.id.value)) has object-local applications that are not represented separately.", + "Model $(typeof(model)) is defined in Main. Define or include that model before evaluating this generated Scene script.", ) end for diagnostic in unique(diagnostics) @@ -724,28 +820,37 @@ function _scene_to_julia(scene) return String(take!(io)) end -function _scene_code_modules(scene) - modules = Set{String}(["PlantSimEngine"]) - add_model = function (model) - module_ = Base.moduleroot(parentmodule(typeof(model))) - module_ in (Base, Core, Main) || push!(modules, string(nameof(module_))) - end - for application in scene.applications +function _scene_code_models(scene) + models = Any[] + add_application = function (application) model = PlantSimEngine.model_(PlantSimEngine.as_model_spec(application)) if model isa PlantSimEngine.ObjectModelOverrides - add_model(model.base) - foreach(add_model, values(model.overrides)) + push!(models, model.base) + append!(models, values(model.overrides)) else - add_model(model) + push!(models, model) end end + foreach(add_application, scene.applications) + for object in PlantSimEngine.scene_objects(scene) + isnothing(object.applications) && continue + foreach(add_application, object.applications) + end for instance in scene.instances - for application in instance.template.applications - add_model(PlantSimEngine.model_(PlantSimEngine.as_model_spec(application))) - end - foreach(add_model, values(instance.overrides)) - foreach(override -> add_model(override.model), instance.object_overrides) + foreach(add_application, instance.template.applications) + append!(models, values(instance.overrides)) + append!(models, (override.model for override in instance.object_overrides)) + end + return models +end + +function _scene_code_modules(scene) + modules = Set{String}(["PlantSimEngine"]) + add_model = function (model) + module_ = Base.moduleroot(parentmodule(typeof(model))) + module_ in (Base, Core, Main) || push!(modules, string(nameof(module_))) end + foreach(add_model, _scene_code_models(scene)) return modules end @@ -762,6 +867,13 @@ function _object_code(object) push!(keywords, "status=Status(; $(values))") end isnothing(object.geometry) || push!(keywords, "geometry=$(repr(object.geometry))") + if !isnothing(object.applications) && object.applications != () + applications = join( + (_application_code(PlantSimEngine.as_model_spec(application)) for application in object.applications), + ", ", + ) + push!(keywords, "applications=($(applications),)") + end return "Object($(repr(object.id.value)); $(join(keywords, ", ")))" end @@ -805,7 +917,24 @@ function _application_code(spec) (code *= " |> Inputs($(repr(PlantSimEngine.value_inputs(spec))))") isempty(keys(PlantSimEngine.model_calls(spec))) || (code *= " |> Calls($(repr(PlantSimEngine.model_calls(spec))))") + environment = PlantSimEngine.environment_config(spec) + if !isnothing(environment) + payload = environment isa PlantSimEngine.EnvironmentConfig ? environment.config : environment + code *= " |> Environment($(repr(payload)))" + end isnothing(spec.timestep) || (code *= " |> TimeStep($(repr(spec.timestep)))") + if !isempty(keys(PlantSimEngine.meteo_bindings(spec))) + code = "PlantSimEngine.with_meteo_bindings($(code), $(repr(PlantSimEngine.meteo_bindings(spec))))" + end + if !isnothing(PlantSimEngine.meteo_window(spec)) + code = "PlantSimEngine.with_meteo_window($(code), $(repr(PlantSimEngine.meteo_window(spec))))" + end + isempty(keys(PlantSimEngine.output_routing(spec))) || + (code *= " |> OutputRouting($(repr(PlantSimEngine.output_routing(spec))))") + for update in PlantSimEngine.updates(spec) + variables = join(repr.(collect(update.variables)), ", ") + code *= " |> Updates($(variables); after=$(repr(update.after)))" + end return code end diff --git a/frontend/dist/.vite/manifest.json b/frontend/dist/.vite/manifest.json index 1e4035bac..83b5405a5 100644 --- a/frontend/dist/.vite/manifest.json +++ b/frontend/dist/.vite/manifest.json @@ -1,11 +1,11 @@ { "index.html": { - "file": "assets/index-DryeyTC8.js", + "file": "assets/index-D625qdlf.js", "name": "index", "src": "index.html", "isEntry": true, "css": [ - "assets/index-CRLmU9Qw.css" + "assets/index-Do5n-jvW.css" ] } } \ No newline at end of file diff --git a/frontend/dist/assets/index-CRLmU9Qw.css b/frontend/dist/assets/index-CRLmU9Qw.css deleted file mode 100644 index 3f2f2d68c..000000000 --- a/frontend/dist/assets/index-CRLmU9Qw.css +++ /dev/null @@ -1 +0,0 @@ -.react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #777;--xy-background-pattern-lines-color-default: #777;--xy-background-pattern-cross-color-default: #777;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}:root{--bg: #f3eee6;--paper: #fffaf2;--paper-strong: #fbf2e6;--ink: #312721;--muted: #80756c;--line: #ded2c3;--line-strong: #b7a696;--accent: #1f7a53;--accent-soft: rgba(31, 122, 83, .12);--sage: #7f8f73;--sage-dark: #596851;--ochre: #c99035;--clay: #bf6a54;--shadow: rgba(56, 43, 35, .12)}.scene-editor-shell{height:100vh;min-height:560px;display:grid;grid-template-rows:auto auto auto minmax(0,1fr);color:#302923;background:#f3eee5}.scene-toolbar{z-index:20;display:grid;grid-template-columns:auto minmax(260px,1fr) auto;gap:12px 18px;align-items:center;padding:14px 18px;background:#fffaf2;border-bottom:1px solid #d9cdbd;box-shadow:0 8px 24px #3c302514}.scene-brand{display:flex;align-items:center;gap:11px}.scene-brand .brand-mark{width:5px;height:42px;border-radius:3px;background:#1f7a58}.scene-brand div{display:grid}.scene-brand small{color:#81766c;font:10px/1.2 ui-monospace,monospace;letter-spacing:0}.scene-brand strong{font-size:20px;letter-spacing:0}.scene-search{display:flex;align-items:center;gap:8px;min-width:0;padding:8px 11px;border:1px solid #d9cdbd;border-radius:6px;background:#fffdf8}.scene-search input{width:100%;min-width:0;border:0;outline:0;background:transparent;font:inherit}.scene-search button,.scene-toolbar button,.overlay-panel button,.candidate-popover button,.editor-feedback button{border:0;background:transparent;color:inherit;cursor:pointer}.scene-counts{display:flex;align-items:center;justify-content:flex-end;gap:7px;flex-wrap:wrap}.scene-counts>span,.scene-counts>button{display:inline-flex;align-items:center;gap:5px;padding:5px 8px;border:1px solid #d9cdbd;border-radius:5px;background:#fffaf2;font:12px ui-monospace,monospace}.scene-counts .count-warning{color:#a96a13;border-color:#dfbd83}.scene-counts .count-error{color:#b44e3c;border-color:#dfa496}.view-tabs,.scene-actions{display:flex;align-items:center;gap:7px;flex-wrap:wrap}.view-tabs{grid-column:1 / 3}.scene-actions{justify-content:flex-end}.view-tabs button,.scene-actions button,.cycle-callout button{display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:6px 10px;border:1px solid #d4c7b6;border-radius:5px;background:#fffaf2;color:#4c433b}.view-tabs button.active{color:#176047;border-color:#83b59e;background:#e9f4ed}.scene-actions button:disabled{opacity:.4;cursor:default}.scene-actions .overview-cta{color:#176047;border-color:#86bba4;background:#e9f4ed;font-weight:700}.cycle-callout{display:flex;align-items:center;gap:12px;padding:10px 18px;color:#8f2f24;background:#fff0eb;border-bottom:1px solid #df9a8e}.cycle-callout div{display:grid;margin-right:auto}.cycle-callout span{font-size:12px}.cycle-callout button{border-color:#cf7668;color:#8f2f24}.editor-feedback{display:flex;align-items:center;justify-content:center;gap:12px;padding:7px 16px;background:#fff5d9;border-bottom:1px solid #dfc278;color:#6e5315;font-size:12px}.scene-workspace{min-height:0;display:grid;grid-template-columns:minmax(0,1fr) 310px}.flow-wrap{min-width:0;min-height:0;position:relative}.scene-inspector{overflow:auto;padding:16px;background:#fffaf2;border-left:1px solid #d9cdbd}.scene-inspector>header{display:grid;gap:2px;margin-bottom:14px}.scene-inspector>header span{color:#776c63;font:11px ui-monospace,monospace}.scene-inspector pre{overflow-wrap:anywhere;white-space:pre-wrap;font-size:10px;line-height:1.45}.empty-inspector{display:grid;place-items:center;padding:48px 20px;color:#8a7e74;text-align:center}.inspector-initialization>div{display:flex;justify-content:space-between;gap:8px;padding:6px 0;border-bottom:1px solid #eee4d6;font-size:11px}.inspector-initialization .unresolved{color:#b74635;font-weight:700}.application-node .target-summary{margin:-2px 0 10px;color:#766c63;font-size:11px}.application-node .target-summary strong{color:#2d6652}.application-node .previous-label{margin-left:auto;color:#1f7a58;font:10px ui-monospace,monospace}.entity-node{position:relative;width:240px;min-height:105px;padding:13px;border:1px solid #d8cbbb;border-radius:6px;background:#fffaf2;box-shadow:0 7px 18px #392e241f}.entity-node.selected{border-color:#1f7a58;box-shadow:0 0 0 2px #1f7a5829}.entity-node header{display:grid;gap:3px}.entity-node header span{color:#766c63;font-size:11px}.entity-node .badges{display:flex;gap:5px;flex-wrap:wrap;margin-top:12px}.candidate-popover{position:fixed;z-index:80;width:370px;max-height:460px;display:grid;grid-template-rows:auto minmax(0,1fr);border:1px solid #cfbfaa;border-radius:7px;background:#fffaf2;box-shadow:0 20px 54px #342a203d}.candidate-popover>header{display:flex;gap:10px;padding:12px;border-bottom:1px solid #e1d5c5}.candidate-popover>header div{display:grid;margin-right:auto}.candidate-popover>header span{color:#7a7067;font-size:10px}.candidate-list{overflow:auto;display:grid;gap:7px;padding:9px}.candidate-card{display:grid;grid-template-columns:1fr auto;gap:2px 8px;padding:10px;border:1px solid #ded2c2!important;border-radius:5px;background:#fffdf8!important;text-align:left}.candidate-card:hover{border-color:#76a990!important;background:#edf6f0!important}.candidate-card span{color:#1f7053;font:11px ui-monospace,monospace}.candidate-card small{color:#7a7067}.candidate-card div{grid-column:1 / -1;color:#7a7067;font-size:10px}.candidate-card.existing{border-left:3px solid #1f7a58!important}.candidate-section-label{padding:5px 3px 2px;color:#71675e;font:700 10px ui-monospace,monospace;text-transform:uppercase}.overlay-backdrop{position:fixed;z-index:100;inset:0;display:grid;place-items:center;padding:24px;background:#2d251e61}.overlay-panel{width:min(720px,100%);max-height:min(760px,92vh);display:grid;grid-template-rows:auto minmax(0,1fr);border:1px solid #cdbda8;border-radius:7px;background:#fffaf2;box-shadow:0 24px 70px #271f194d}.overlay-panel>header{display:flex;justify-content:space-between;align-items:center;padding:14px 16px;border-bottom:1px solid #ded2c2}.overlay-content{overflow:auto;padding:15px}.diagnostic-card,.cycle-card,.initialization-card{display:grid;gap:5px;margin-bottom:10px;padding:11px;border:1px solid #ded2c2;border-radius:5px}.diagnostic-card{border-left:3px solid #c85240}.diagnostic-card p,.cycle-card p{margin:0}.diagnostic-card small{color:#766c63}.cycle-card button{width:fit-content;padding:6px 8px;border:1px solid #ce7768;border-radius:4px;color:#963529}.scene-code{min-height:320px;margin:0;padding:14px;overflow:auto;border-radius:5px;background:#292622;color:#f5eee5}@media(max-width:980px){.scene-toolbar{grid-template-columns:1fr}.view-tabs{grid-column:auto}.scene-counts,.scene-actions{justify-content:flex-start}.scene-workspace{grid-template-columns:1fr}.scene-inspector{display:none}}.application-form{width:min(780px,100%)}.application-form>header div{display:grid;gap:2px}.application-form>header span{color:#786d64;font-size:11px}.application-form-content{display:grid;gap:14px}.application-form-content>label,.application-form fieldset label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.application-form input,.application-form select{width:100%;min-width:0;padding:8px 9px;border:1px solid #d3c5b4;border-radius:4px;background:#fffdf8;color:#332c26;font:12px ui-monospace,monospace}.application-form fieldset{margin:0;padding:12px;border:1px solid #ded2c2;border-radius:5px}.application-form legend{padding:0 6px;color:#2d6652;font-weight:800}.form-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.parameter-list{display:grid;gap:9px}.parameter-row{display:grid;grid-template-columns:minmax(0,1fr) 150px;gap:9px;align-items:end}.parameter-row>label>span{color:#3c342e}.parameter-row small{color:#8a7e74;font-weight:400}.selector-summary{margin:10px 0 0;color:#796e64;font-size:11px}.application-form>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.application-form>footer button,.inspector-actions button{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border:1px solid #d2c4b3;border-radius:4px;background:#fffdf8}.application-form>footer .primary{color:#fff;border-color:#1d7052;background:#1f7a58}.application-form>footer button:disabled{opacity:.45}.inspector-actions{display:flex;gap:7px;margin:10px 0 16px}.inspector-actions .danger{color:#a33d31;border-color:#d8a296}.binding-form{width:min(680px,100%)}.binding-form>header div{display:grid;gap:2px}.binding-form>header span{color:#786d64;font-size:11px}.binding-form .overlay-content{display:grid;gap:14px}.binding-route{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:14px;padding:12px;border:1px solid #d8cbbb;border-radius:5px;background:#fffdf8}.binding-route>div{display:grid;gap:3px;min-width:0}.binding-route>div:last-child{text-align:right}.binding-route small{color:#7c7168;text-transform:uppercase;font-size:9px}.binding-route strong,.binding-route code{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.binding-route code{color:#1f7053}.binding-form fieldset{margin:0;padding:12px;border:1px solid #ded2c2;border-radius:5px}.binding-form legend{padding:0 6px;color:#2d6652;font-weight:800}.binding-form fieldset label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.binding-form select{width:100%;min-width:0;padding:8px 9px;border:1px solid #d3c5b4;border-radius:4px;background:#fffdf8;color:#332c26;font:12px ui-monospace,monospace}.binding-form>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.binding-form>footer button{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border:1px solid #d2c4b3;border-radius:4px;background:#fffdf8}.binding-form>footer .primary{color:#fff;border-color:#1d7052;background:#1f7a58}@media(max-width:700px){.form-grid,.parameter-row,.binding-route{grid-template-columns:1fr}.binding-route>div:last-child{text-align:left}}*{box-sizing:border-box}body{margin:0;color:var(--ink);background:radial-gradient(circle at 20% 24%,rgba(31,122,83,.045),transparent 30%),radial-gradient(circle at 78% 68%,rgba(201,144,53,.055),transparent 34%),linear-gradient(180deg,rgba(255,250,242,.26),transparent 42%),var(--bg);font-family:Avenir Next,Trebuchet MS,Segoe UI,sans-serif}.app-shell{display:grid;grid-template-columns:minmax(0,1fr);height:100vh;position:relative}.app-shell.has-side-panel{grid-template-columns:minmax(0,1fr) 340px}.graph-panel{position:relative;min-width:0}.graph-panel:after{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;opacity:.2;background-image:radial-gradient(rgba(49,39,33,.11) .55px,transparent .55px);background-size:16px 16px;-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 22%,black 82%,transparent 100%);mask-image:linear-gradient(to bottom,transparent 0%,black 22%,black 82%,transparent 100%)}.topbar{position:absolute;z-index:10;top:18px;left:18px;right:18px;display:flex;align-items:center;gap:12px;padding:11px 14px;background:#fffaf2eb;border:1px solid var(--line);border-radius:14px;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.topbar:before{content:"";align-self:stretch;width:5px;border-radius:999px;background:var(--accent)}.editor-feedback{position:absolute;z-index:12;top:96px;left:18px;right:18px;padding:9px 12px;border:1px solid var(--line);border-radius:10px;background:#fffaf2f2;box-shadow:0 12px 30px var(--shadow);max-height:132px;overflow:auto;font-size:12px;line-height:1.45;white-space:normal;overflow-wrap:anywhere}.editor-feedback.error{color:var(--clay);border-color:#bf6a5466;background:#fff4eef7}.editor-feedback.info{color:var(--accent);border-color:#1f7a5359;background:#f5fcf8f7}.cycle-break-prompt{position:absolute;z-index:14;top:98px;left:18px;right:18px;display:flex;align-items:center;justify-content:space-between;gap:14px;padding:11px 12px;border:1px solid rgba(211,66,47,.32);border-radius:12px;background:linear-gradient(135deg,rgba(211,66,47,.12),transparent 62%),#fffaf2f5;box-shadow:0 16px 36px #382b2329}.cycle-break-prompt div{display:grid;gap:3px;min-width:0}.cycle-break-prompt strong{color:#7a2018;font-size:13px}.cycle-break-prompt span{color:var(--muted);font-size:12px;line-height:1.35}.cycle-break-prompt code{color:#7a2018;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.cycle-break-prompt.active{border-color:#d3422f85;box-shadow:0 16px 36px #382b2329,0 0 0 4px #d3422f14}.cycle-break-cta{flex:0 0 auto;justify-content:center;font-weight:780}.graph-workbench{flex-wrap:wrap;align-items:flex-start;max-height:min(36vh,250px);overflow:auto;scrollbar-width:thin}.brand-block{min-width:150px}.brand-block h1{font-size:clamp(16px,2.2vw,20px)}.search-box{position:relative;display:flex;align-items:center;gap:8px;flex:1 1 280px;max-width:460px;min-width:220px;padding:7px 9px;border:1px solid var(--line);border-radius:11px;background:#fffdf7db}.search-box input{width:100%;min-width:0;border:0;outline:0;color:var(--ink);background:transparent;font:inherit;font-size:13px}.clear-search{display:grid;place-items:center;width:20px;height:20px;border:0;border-radius:999px;background:#b7a6962e;color:var(--muted);cursor:pointer}.search-results{position:absolute;z-index:80;top:calc(100% + 8px);left:0;right:0;display:grid;gap:6px;max-height:360px;overflow:auto;padding:8px;border:1px solid var(--line);border-radius:12px;background:#fffaf2fa;box-shadow:0 18px 45px var(--shadow)}.search-result{display:grid;gap:2px;padding:8px 9px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--ink);text-align:left;cursor:pointer}.search-result:hover,.search-result:focus-visible{border-color:#1f7a533d;background:var(--accent-soft);outline:none}.search-result strong{overflow-wrap:anywhere;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.search-result span{color:var(--muted);font-size:11px}.eyebrow{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.08em;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}h1,h2,h3{margin:0;letter-spacing:0}h1{font-size:20px;font-weight:800}.metrics{display:flex;flex-wrap:wrap;gap:8px;margin-left:auto}.metrics span,.metric-button,.node-meta span{display:inline-flex;align-items:center;gap:5px;padding:5px 8px;border:1px solid var(--line);border-radius:9px;background:#fffaf2e6;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.metric-button{color:var(--ink);cursor:pointer}.metric-button.active,.metric-button:hover,.metric-button:focus-visible{outline:none;background:#fff6f0}.view-mode-button.overview-cta{border-color:#1f7a5361;color:var(--accent);background:#1f7a531a;box-shadow:0 8px 18px #1f7a531a;font-weight:800}.meta-chip{position:relative}.meta-chip:after{content:attr(data-tooltip);position:absolute;z-index:20;left:0;bottom:calc(100% + 10px);width:max-content;max-width:280px;padding:8px 10px;color:var(--paper);background:var(--ink);border-radius:10px;box-shadow:0 12px 28px #382b232e;font-family:Avenir Next,Trebuchet MS,Segoe UI,sans-serif;font-size:12px;line-height:1.3;white-space:normal;opacity:0;pointer-events:none;transform:translateY(4px);transition:opacity .12s ease,transform .12s ease}.meta-chip:before{content:"";position:absolute;z-index:21;left:16px;bottom:calc(100% + 4px);width:10px;height:10px;background:var(--ink);opacity:0;pointer-events:none;transform:rotate(45deg) translateY(4px);transition:opacity .12s ease,transform .12s ease}.meta-chip:hover:after,.meta-chip:hover:before,.meta-chip:focus-visible:after,.meta-chip:focus-visible:before{opacity:1;transform:translateY(0)}.meta-chip:hover:before,.meta-chip:focus-visible:before{transform:rotate(45deg) translateY(0)}.metrics .warn{color:var(--clay);border-color:#c9614a73}.metrics .caution{color:var(--ochre);border-color:#c9903573}.metrics .warn svg{flex:0 0 auto}.icon-button{display:grid;place-items:center;width:34px;height:34px;border:1px solid var(--line);border-radius:10px;background:var(--paper);color:var(--ink);cursor:pointer;box-shadow:0 8px 18px #382b2314}.icon-button.compact{width:28px;height:28px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:14px}.toolbar-group{display:flex;align-items:center;gap:8px}.open-button{flex:0 0 auto}.panel-switch{flex-wrap:wrap}.select-control{display:inline-flex;align-items:center;gap:6px;padding:5px 8px;border:1px solid var(--line);border-radius:10px;background:#fffaf2e6;color:var(--muted)}.select-control select{max-width:132px;border:0;outline:0;background:transparent;color:var(--ink);font:inherit;font-size:12px}.relationship-legend{position:absolute;z-index:35;top:116px;left:18px;display:grid;gap:7px;width:190px;padding:10px;border:1px solid var(--line);border-radius:13px;background:#fffaf2eb;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);max-height:min(480px,calc(100vh - 142px));overflow:auto}.legend-title,.legend-note{display:flex;align-items:center;gap:6px;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.relationship-legend button{display:flex;align-items:center;gap:8px;padding:6px 7px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--muted);font:inherit;font-size:12px;cursor:pointer}.relationship-legend button.active{color:var(--ink);border-color:#1f7a5338;background:var(--accent-soft)}.legend-line{width:28px;height:0;border-top:2px solid var(--line-strong)}.legend-line.mapped{border-color:var(--accent);border-style:dashed}.legend-line.call{border-color:var(--clay);border-style:dashed}.scale-controls{position:absolute;z-index:35;top:116px;left:220px;display:grid;gap:8px;width:190px;padding:10px;border:1px solid var(--line);border-radius:13px;background:#fffaf2eb;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);max-height:min(560px,calc(100vh - 142px));overflow:auto}.scale-list{display:grid;gap:6px}.scale-list button,.scale-reset{display:grid;gap:2px;width:100%;padding:7px 8px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--muted);font:inherit;text-align:left;cursor:pointer}.scale-list button.active{color:var(--ink);border-color:#1f7a5338;background:var(--accent-soft)}.scale-list button.collapsed{border-color:#b7a69657;border-style:dashed;background:#b7a69614}.scale-list span{overflow-wrap:anywhere;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;font-weight:700}.scale-list small{color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.06em}.scale-reset{color:var(--accent);border-color:#1f7a5333;background:#1f7a5314;text-align:center}.floating-panel{position:absolute;z-index:40;top:116px;right:18px;width:min(360px,calc(100vw - 36px));max-height:min(560px,calc(100vh - 142px));overflow:auto;padding:14px;background:#fffaf2f5;border:1px solid rgba(191,106,84,.32);border-radius:14px;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}.warnings-panel{border-color:#c9903559}.open-panel{left:18px;right:auto;border-color:#1f7a5352}.floating-panel-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}.floating-panel h2{font-size:18px}.react-flow{background:transparent;z-index:1}.react-flow__background{display:none}.react-flow__edges{z-index:8}.react-flow__edges:has(.react-flow__edge.highlighted){z-index:120}.react-flow__edgelabel-renderer{z-index:28}.react-flow__edgelabel-renderer:has(.edge-chip.highlighted){z-index:121}.react-flow__nodes{z-index:20}.model-node{position:relative;overflow:visible;background:#fffaf2f5;border:1px solid var(--line);border-radius:16px;box-shadow:0 18px 42px var(--shadow)}.model-remove-button{position:absolute;z-index:45;top:-11px;right:-11px;display:grid;place-items:center;width:30px;height:30px;border:1px solid rgba(191,106,84,.4);border-radius:999px;color:#fff;background:var(--clay);box-shadow:0 10px 24px #bf6a543d;cursor:pointer}.model-remove-button:hover,.model-remove-button:focus-visible{outline:none;background:#9b3f2e;transform:translateY(-1px)}.node-header{border-radius:16px 16px 0 0}.model-node.selected{border-color:#1f7a53b8;box-shadow:0 20px 48px #1f7a5324}.model-node.focused{border-color:#1f7a539e;box-shadow:0 20px 48px #1f7a5324,0 0 0 4px #1f7a5314}.model-node.dimmed{opacity:.2;filter:grayscale(.2)}.model-node.cyclic{border-color:#bf6a549e;box-shadow:0 18px 42px var(--shadow),0 0 0 3px #bf6a5414}.model-node.cyclic .node-header>svg{color:var(--clay)}.model-node.hard_dependency{border-color:#bf6a548c;border-style:dashed;background:#fff6f0f0;box-shadow:0 14px 30px #bf6a541f}.model-node.hard_dependency .node-header{background:linear-gradient(90deg,rgba(191,106,84,.08),transparent 60%),var(--paper-strong)}.model-node.hard_dependency .node-header>svg{color:var(--clay)}.model-node.hard_dependency .process:before{content:"↳ ";color:var(--clay)}.model-node.overview-node{border-radius:12px;box-shadow:0 10px 24px #382b231a}.model-node.overview-node .node-header{min-height:64px;padding:9px 10px;border-radius:12px 12px 0 0}.model-node.overview-node .process{font-size:12px;line-height:1.18;overflow-wrap:anywhere}.model-node.overview-node .model-type{font-size:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.overview-node-summary{display:flex;flex-wrap:wrap;gap:5px;padding:8px 10px 10px}.overview-node-summary span{min-width:0;max-width:100%;padding:3px 6px;border:1px solid rgba(183,166,150,.35);border-radius:999px;color:var(--muted);background:#fffdf7c2;font-size:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.overview-port-handles .react-flow__handle{width:7px;height:7px;border-color:#fffaf2e6;background:var(--line-strong)}.overview-port-handles .react-flow__handle-right{background:var(--accent)}.hard-chip{color:var(--clay);border-color:#bf6a5447!important;background:#fff6f0e6!important}.node-header{display:grid;grid-template-columns:1fr auto;align-items:flex-start;gap:11px;padding:11px 12px 12px;color:var(--ink);background:var(--paper-strong);border-bottom:1px solid var(--line)}.node-header>svg{color:var(--accent)}.process{font-weight:820;font-size:15px;letter-spacing:0}.model-type{margin-top:2px;font-size:12px;color:var(--muted);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.node-meta{display:flex;flex-wrap:wrap;gap:6px;padding:10px 12px 0}.ports-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;padding:12px}.port-title{margin-bottom:6px;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.08em;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.port{position:relative;display:flex;align-items:center;gap:5px;min-height:24px;margin:4px 0;padding:4px 7px;border:1px solid var(--line);border-radius:8px;background:#fffdf7e6;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.port:after{content:attr(data-default);position:absolute;z-index:60;left:50%;bottom:calc(100% + 8px);width:max-content;max-width:240px;padding:7px 9px;color:var(--paper);background:var(--ink);border-radius:9px;box-shadow:0 12px 28px #382b232e;font-family:Avenir Next,Trebuchet MS,Segoe UI,sans-serif;font-size:11px;line-height:1.3;white-space:normal;opacity:0;pointer-events:none;transform:translate(-50%,4px);transition:opacity .12s ease,transform .12s ease}.port:hover:after,.port.active:after{opacity:1;transform:translate(-50%)}.app-shell.has-candidate-popover .port.active:after{opacity:0}.port.output{justify-content:flex-end}.port.previous{color:var(--clay)}.port.mapped{border-color:#1f7a5361}.port-candidate-button{position:relative;z-index:8;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:16px;height:16px;padding:0;color:var(--accent);background:#fffdf7f0;border:1px solid rgba(31,122,83,.34);border-radius:999px;cursor:pointer}.port-candidate-button:hover,.port-candidate-button:focus-visible{color:#fffdfa;background:var(--accent);outline:none}.port-cycle-break-button{position:relative;z-index:9;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:18px;height:18px;padding:0;color:#9b2d22;background:#fff1ecf5;border:1px solid rgba(211,66,47,.45);border-radius:999px;cursor:pointer;box-shadow:0 5px 12px #d3422f29}.port-cycle-break-button:hover,.port-cycle-break-button:focus-visible{color:#fffdfa;background:#d3422f;outline:none}.port.active .port-candidate-button{color:var(--accent);background:#fffdfa}@keyframes cycleTargetPulse{0%,to{box-shadow:inset 3px 0 #d3422fc7,0 0 0 3px #d3422f14}50%{box-shadow:inset 3px 0 #d3422feb,0 0 0 6px #d3422f26}}.candidate-popover{position:fixed;z-index:1200;display:grid;grid-template-rows:auto minmax(0,1fr);overflow:hidden;color:var(--ink);border:1px solid rgba(31,122,83,.28);border-radius:8px;background:linear-gradient(135deg,rgba(31,122,83,.08),transparent 42%),#fffdf7fa;box-shadow:0 18px 42px #382b232e}.candidate-popover-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:12px 12px 9px;border-bottom:1px solid rgba(183,166,150,.32)}.candidate-popover-header h3{margin:3px 0 0;overflow-wrap:anywhere;font-size:15px}.candidate-popover-list{display:grid;gap:8px;overflow:auto;padding:10px}.candidate-model-card{display:grid;gap:5px;width:100%;padding:9px 10px;color:var(--ink);border:1px solid rgba(183,166,150,.38);border-left:4px solid var(--accent);border-radius:8px;background:#ffffffb3;font:inherit;text-align:left;cursor:pointer}.candidate-model-card:hover,.candidate-model-card:focus-visible{background:#fffdf7f5;border-color:#1f7a536b;outline:none}.candidate-model-card strong{overflow-wrap:anywhere;font-size:12px}.candidate-model-card span{color:var(--muted);font-size:11px}.candidate-model-card small{overflow-wrap:anywhere;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.04em}.port.required-input{border-color:#bf6a54b8;background:linear-gradient(90deg,#bf6a5429,#fffdf7eb 62%),#fffdf7e6;box-shadow:inset 3px 0 #bf6a54bd}.port.required-input .react-flow__handle{border-color:var(--clay);background:#fff6f0;box-shadow:0 0 0 3px #fffaf2eb,0 0 0 6px #bf6a5421}.port.cycle-break-target{border-color:#d3422fb8;background:linear-gradient(90deg,#d3422f2e,#fffdf7eb 62%),#fffdf7e6;box-shadow:inset 3px 0 #d3422fc7,0 0 0 3px #d3422f14}.cycle-break-mode .port.cycle-break-target{animation:cycleTargetPulse 1.35s ease-in-out infinite}.port.cycle-break-target .react-flow__handle{border-color:#d3422f;background:#fff1ec;box-shadow:0 0 0 3px #fffaf2eb,0 0 0 7px #d3422f26}.port.highlighted{border-color:var(--line);background:#fffdfa}.port.focused{border-color:#1f7a5394;background:var(--accent-soft)}.port.active{color:#fffdfa;border-color:var(--accent);background:var(--accent)}.port.cycle-break-target.active,.port.cycle-break-target.focused,.port.cycle-break-target.highlighted{color:#7a2018;border-color:#d3422fc7;background:linear-gradient(90deg,#d3422f33,#fffdf7f0 64%),#fffdf7f0;box-shadow:inset 3px 0 #d3422fdb,0 0 0 4px #d3422f1f}.port.cycle-break-target.active:after{background:#7a2018}.port.cycle-break-target.active .port-cycle-break-button,.port.cycle-break-target.focused .port-cycle-break-button,.port.cycle-break-target.highlighted .port-cycle-break-button{color:#9b2d22;background:#fff1ecfa}.react-flow__handle{width:9px;height:9px;border:1px solid var(--line-strong);background:var(--paper);z-index:25;box-shadow:0 0 0 3px #fffaf2eb}.react-flow__handle.call-handle{width:12px;height:36px;opacity:0;pointer-events:none}.react-flow__edge.multiscale path{stroke-dasharray:7 5}.react-flow__edge.mapped_variable path{stroke:var(--accent)}.react-flow__edge.hard_dependency path{stroke:var(--clay)}.react-flow__edge.cycle_dependency path,.react-flow__edge.cycle_edge path{stroke:#d3422f;filter:drop-shadow(0 0 5px rgba(211,66,47,.26))}.react-flow__edge.call_edge path{stroke-width:1.7;stroke-dasharray:3 6;opacity:.7}.react-flow__edge.highlighted path{stroke-width:3;stroke:var(--accent)}.react-flow__edge.focused path{stroke-width:2.8}.react-flow__edge.highlighted{z-index:80!important}.react-flow__edge.focused{z-index:70!important}.react-flow__edge.dimmed{opacity:.04}.overview-mode .react-flow__edge.variable_edge path{stroke-width:1.45!important;opacity:.74}.overview-mode .react-flow__edge.call_edge path{stroke-width:1.15!important;opacity:.4}.overview-mode .react-flow__edge.highlighted path,.overview-mode .react-flow__edge.focused path{stroke-width:2.8!important;opacity:1}.edge-chip{position:absolute;z-index:-1;display:inline-flex;align-items:center;gap:6px;padding:3px 7px;color:var(--ink);background:#fffaf2f0;border:1px solid var(--line);border-radius:999px;box-shadow:0 8px 20px #382b231a;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:1;pointer-events:none;white-space:nowrap}.edge-chip.mapped_variable,.edge-chip.multiscale{border-color:#1f7a534d;background:#f8faf1f5}.edge-chip.hard_dependency{border-color:#bf6a5452;background:#fff6f0f5}.edge-chip.cycle_dependency,.edge-chip.cycle_edge{color:#7a2018;border-color:#d3422f70;background:#ffeee9fa}.edge-chip small{color:var(--muted);font-size:9px;letter-spacing:.02em;text-transform:uppercase}.edge-chip.highlighted{z-index:80;color:#fffdfa;border-color:var(--accent);background:var(--accent);box-shadow:0 12px 24px #1f7a5338}.edge-chip.highlighted small{color:#fffdf7b8}.edge-chip.dimmed{opacity:.04}.overview-mode .edge-chip:not(.highlighted):not(.focused),.overview-mode .edge-terminal:not(.highlighted):not(.focused){display:none}.edge-chip.focused{z-index:70;border-color:#1f7a535c;box-shadow:0 10px 22px #1f7a5324}.edge-terminal{position:absolute;z-index:32;--terminal-color: var(--line-strong);width:18px;height:10px;pointer-events:none;opacity:.95}.edge-terminal:before{content:"";position:absolute;top:4px;left:2px;right:2px;height:2px;border-radius:999px;background:var(--terminal-color)}.edge-terminal.target:after{content:"";position:absolute;top:1px;width:0;height:0;border-top:4px solid transparent;border-bottom:4px solid transparent}.edge-terminal[data-side=left]:before{left:7px}.edge-terminal[data-side=right]:before{right:7px}.edge-terminal.target[data-side=left]:after{right:0;border-left:7px solid var(--terminal-color)}.edge-terminal.target[data-side=right]:after{left:0;border-right:7px solid var(--terminal-color)}.edge-terminal.highlighted:before{height:3px}.edge-terminal.dimmed{opacity:.12}.cycle-edge-card{border-color:#d3422f57;background:linear-gradient(135deg,rgba(211,66,47,.1),transparent 58%),#fffaf2e0}.cycle-break-button{justify-content:center;margin:8px 0 4px}.inspector{border-left:1px solid var(--line);background:#fffaf2d1;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);padding:18px;overflow:auto}.inspector:focus{outline:none}.inspector.guided-focus{animation:guided-panel-focus 1.8s ease-out}@keyframes guided-panel-focus{0%{box-shadow:inset 0 0 0 3px #1f7a537a,-18px 0 44px #1f7a5329;background:#fafff7f0}60%{box-shadow:inset 0 0 0 3px #1f7a535c,-12px 0 34px #1f7a531f}to{box-shadow:none;background:#fffaf2d1}}.inspector header{display:flex;align-items:center;gap:8px;margin-bottom:14px}.mapping-code-panel{display:grid;gap:10px}.row-with-actions{display:flex;align-items:center;justify-content:space-between;gap:8px}.mapping-code{width:100%;min-height:260px;resize:vertical;border:1px solid var(--line);border-radius:10px;background:#fffdf7e6;color:var(--ink);padding:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.45}.storage-grid{display:grid;gap:8px}.path-status{display:grid;gap:3px;border:1px solid var(--line);border-radius:10px;background:#fffaf2b8;padding:9px}.path-status span{color:var(--muted);font-size:11px;font-weight:720}.path-status strong{color:var(--ink);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow-wrap:anywhere}.recent-mappings{display:grid;gap:8px;margin-top:4px}.open-mapping-panel{display:grid;gap:12px}.recent-mapping-list{display:grid;gap:7px}.recent-mapping-item{display:grid;gap:3px;text-align:left;border:1px solid var(--line);border-radius:10px;background:#fffdf7d1;color:var(--ink);padding:9px;cursor:pointer}.recent-mapping-item:hover,.recent-mapping-item:focus-visible{border-color:#1f7a5352;background:#1f7a5314}.recent-mapping-item span{font-weight:720}.recent-mapping-item small{color:var(--muted);overflow-wrap:anywhere}.inline-field{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px}.inspector h2{font-size:17px}.inspector h3{margin-top:22px;margin-bottom:8px;color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.08em}.row{display:grid;grid-template-columns:84px minmax(0,1fr);gap:8px;padding:8px 0;border-top:1px solid rgba(183,166,150,.35)}.row span{color:var(--muted)}.row strong{overflow-wrap:anywhere;font-weight:620}.variable-card{border:1px solid var(--line);border-radius:12px;background:#fffaf2bf;padding:10px}.edge-detail-card{display:grid;gap:4px;margin-bottom:14px;padding:10px;border:1px solid rgba(31,122,83,.24);border-radius:12px;background:linear-gradient(135deg,rgba(31,122,83,.08),transparent 58%),#fffaf2d1}.edge-detail-card .diagnostic,.edge-detail-card .empty-state{margin-top:6px}.variable-card .row:first-of-type{margin-top:8px}.variable-card-title{display:flex;align-items:center;justify-content:space-between;gap:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.variable-card-title span{overflow-wrap:anywhere;font-weight:720}.variable-card-title small{color:var(--muted);text-transform:uppercase;letter-spacing:.08em}.diagnostic,.edit-suggestion,.initialization-note,.empty-state{border:1px solid var(--line);border-radius:12px;background:#fffaf2bf;padding:10px;color:var(--muted)}.diagnostic,.edit-suggestion,.initialization-note{display:flex;align-items:center;gap:7px}.diagnostic,.edit-suggestion{color:var(--clay);border-color:#c9614a47;background:#c9614a17}.initialization-note{color:#87533b;border-color:#bf6a5452;background:#bf6a541a}.initialization-list{display:grid;gap:8px}.initialization-list.compact{gap:6px}.initialization-group{display:grid;gap:7px}.initialization-group h4,.provenance-block h4{margin:6px 0 0;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700}.initialization-item{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:10px;width:100%;padding:9px 10px;color:var(--ink);background:#fffaf2c7;border:1px solid rgba(191,106,84,.28);border-left:4px solid var(--clay);border-radius:10px;font:inherit;text-align:left;cursor:pointer}.initialization-item:hover,.initialization-item:focus-visible{background:#fff6f0f5;outline:none}.initialization-item span{overflow:hidden;color:var(--muted);font-size:12px;text-overflow:ellipsis;white-space:nowrap}.initialization-item strong{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.initialization-item small{grid-column:1 / -1;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.06em}.initialization-item.previous_time_step{border-left-color:var(--ochre)}.initialization-item.mapped_unresolved{border-left-color:var(--accent)}.warning-list,.provenance-block{display:grid;gap:8px}.warning-group{display:grid;gap:7px}.warning-group h4{margin:6px 0 0;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700}.warning-item,.provenance-edge{display:grid;gap:3px;width:100%;padding:9px 10px;border:1px solid rgba(201,144,53,.28);border-left:4px solid var(--ochre);border-radius:10px;background:#fffaf2c7;color:var(--ink);font:inherit;text-align:left;cursor:pointer}.warning-item.error{border-color:#bf6a5457;border-left-color:var(--clay);background:#bf6a5414}.warning-item.info{border-color:#7f8f7342;border-left-color:var(--sage);background:#7f8f7314}.provenance-edge{border-color:#b7a6966b;border-left-color:var(--line-strong)}.provenance-edge.mapped_variable{border-left-color:var(--accent)}.provenance-edge.hard_dependency{border-left-color:var(--clay)}.warning-item:hover,.warning-item:focus-visible,.provenance-edge:hover,.provenance-edge:focus-visible{background:#fff6f0f5;outline:none}.warning-item strong,.provenance-edge strong{overflow-wrap:anywhere;font-size:12px}.warning-item span,.provenance-edge span,.provenance-edge small{color:var(--muted);font-size:11px;line-height:1.25}.provenance-block{margin-top:10px}.empty-state.compact{padding:7px 8px;font-size:11px}.live-session{border-left:1px solid rgba(183,166,150,.36);padding-left:10px}.live-pill{display:inline-flex;align-items:center;height:28px;padding:0 9px;border:1px solid rgba(191,106,84,.38);border-radius:999px;color:var(--clay);background:#bf6a5414;font-size:11px;font-weight:800;text-transform:uppercase}.live-pill.connected{border-color:#1f7a5357;color:var(--accent);background:#1f7a5317}.metric-button:disabled{cursor:not-allowed;opacity:.42}.model-browser{display:grid;gap:7px}.model-browser-control{display:grid;gap:4px}.model-browser-control span,.parameter-row label{color:var(--muted);font-size:11px;font-weight:700}.model-browser-control select,.model-browser-control input,.parameter-row input,.parameter-row select{min-width:0;border:1px solid rgba(183,166,150,.44);border-radius:8px;background:#ffffffc7;color:var(--ink);font:inherit}.model-browser-control select,.model-browser-control input{height:32px;padding:0 8px}.model-browser-item{display:grid;gap:7px;padding:8px 9px;border:1px solid rgba(183,166,150,.34);border-radius:10px;background:#ffffff9e}.model-browser-item.add-model-config{gap:10px;padding-bottom:0}.model-browser-title{display:grid;gap:2px}.model-browser-item strong{overflow-wrap:anywhere;font-size:12px}.model-browser-item span{color:var(--muted);font-size:11px}.existing-model-editor{display:grid;gap:8px;margin-top:12px;padding-top:10px;border-top:1px solid rgba(183,166,150,.32)}.variable-mapping-editor{display:grid;gap:8px;margin-top:10px;padding-top:10px;border-top:1px solid rgba(183,166,150,.32)}.variable-mapping-editor h4{margin:0;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700}.initialization-editor{display:grid;gap:12px}.initialization-editor-group{display:grid;gap:8px}.initialization-editor-group h3{margin:0}.initialization-editor-row{display:grid;grid-template-columns:minmax(0,.85fr) minmax(0,1fr) minmax(82px,.72fr) auto;gap:7px;align-items:center;padding:9px;border:1px solid rgba(191,106,84,.26);border-left:4px solid var(--clay);border-radius:8px;background:#fffaf2c7}.initialization-editor-row.provided{border-color:#1f7a533d;border-left-color:var(--accent)}.initialization-editor-row label{overflow-wrap:anywhere;font-weight:700}.initialization-editor-row input,.initialization-editor-row select{min-width:0;height:30px;border:1px solid rgba(183,166,150,.44);border-radius:8px;background:#ffffffc7;color:var(--ink);font:inherit}.initialization-editor-row input{padding:0 8px}.initialization-editor-row small{grid-column:1 / -1;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.05em}.parameter-row{display:grid;grid-template-columns:minmax(0,.75fr) minmax(0,1fr) minmax(78px,.8fr);gap:6px;align-items:center}.parameter-row input,.parameter-row select{height:28px;padding:0 7px;font-size:12px}.rate-editor{display:grid;gap:6px;padding:7px;border:1px solid rgba(183,166,150,.28);border-radius:8px;background:#fcf9f4b8}.add-model-footer{position:sticky;bottom:-18px;display:flex;justify-content:flex-end;margin:2px -9px 0;padding:10px 9px;border-top:1px solid rgba(183,166,150,.34);border-radius:0 0 10px 10px;background:#fffaf2f0;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.rate-summary{color:var(--muted);font-size:11px;line-height:1.35}.rate-clock-row{display:grid;grid-template-columns:1fr 1fr;gap:6px}.rate-clock-row label{display:grid;gap:4px;color:var(--muted);font-size:11px;font-weight:700}.rate-clock-row input{min-width:0;height:28px;border:1px solid rgba(183,166,150,.44);border-radius:8px;background:#ffffffc7;color:var(--ink);font:inherit;padding:0 7px}.metric-button.danger{border-color:#bf6a546b;color:#9b3f2e;background:#fff2ecdb}@media(max-width:900px){.app-shell{grid-template-columns:1fr}.inspector{position:absolute;z-index:28;top:150px;right:16px;bottom:16px;width:min(360px,calc(100% - 32px));border:1px solid var(--line);border-radius:14px;box-shadow:0 18px 45px var(--shadow)}.relationship-legend,.scale-controls,.floating-panel{top:150px}.scale-controls{left:220px}}@media(max-width:720px){.topbar{top:10px;left:10px;right:10px;gap:8px;padding:9px 10px}.topbar:before{display:none}.brand-block{min-width:128px}.search-box{flex-basis:100%;max-width:none;min-width:0}.metrics{margin-left:0}.relationship-legend,.scale-controls,.floating-panel{top:132px;left:10px;right:10px;width:auto}.scale-controls{top:326px}.react-flow__minimap{display:none}}.mapping-dialog-overlay{position:fixed;inset:0;z-index:1000;background:#31272161;display:flex;align-items:center;justify-content:center}.mapping-dialog{background:var(--paper);border:1px solid var(--line-strong);border-radius:10px;box-shadow:0 8px 32px var(--shadow);width:360px;max-width:calc(100vw - 32px);display:flex;flex-direction:column;gap:0;overflow:hidden}.mapping-dialog-header{display:flex;align-items:center;justify-content:space-between;padding:14px 16px 10px;border-bottom:1px solid var(--line)}.mapping-dialog-header .eyebrow{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.07em;color:var(--muted)}.mapping-dialog-body{padding:14px 16px;display:flex;flex-direction:column;gap:14px}.mapping-port-summary{display:flex;align-items:center;gap:8px;background:var(--paper-strong);border:1px solid var(--line);border-radius:6px;padding:10px 12px}.mapping-port{display:flex;flex-direction:column;gap:1px;flex:1;min-width:0}.mapping-port small{font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}.mapping-port strong{font-size:12px;font-weight:700;color:var(--accent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mapping-port span{font-size:11px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mapping-port.target strong{color:var(--clay)}.mapping-arrow{font-size:18px;color:var(--muted);flex-shrink:0}.mapping-mode-section{display:flex;flex-direction:column;gap:6px}.mapping-mode-label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);margin-bottom:2px}.mapping-radio{display:flex;align-items:center;gap:8px;font-size:13px;cursor:pointer;padding:4px 0}.mapping-radio input[type=radio]{accent-color:var(--accent);width:14px;height:14px;flex-shrink:0}.mapping-scale-picker{display:flex;flex-direction:column;gap:4px}.mapping-checkbox{display:flex;align-items:center;gap:8px;font-size:13px;cursor:pointer;padding:2px 0}.mapping-checkbox input[type=checkbox]{accent-color:var(--accent);width:14px;height:14px;flex-shrink:0}.mapping-dialog-footer{display:flex;justify-content:flex-end;gap:8px;padding:10px 16px 14px;border-top:1px solid var(--line)}.accent-button{background:var(--accent);color:#fff;border-color:transparent}.accent-button:hover:not(:disabled){background:var(--sage-dark);border-color:transparent} diff --git a/frontend/dist/assets/index-D625qdlf.js b/frontend/dist/assets/index-D625qdlf.js new file mode 100644 index 000000000..7485bab30 --- /dev/null +++ b/frontend/dist/assets/index-D625qdlf.js @@ -0,0 +1,38 @@ +(function(){const E=document.createElement("link").relList;if(E&&E.supports&&E.supports("modulepreload"))return;for(const N of document.querySelectorAll('link[rel="modulepreload"]'))M(N);new MutationObserver(N=>{for(const P of N)if(P.type==="childList")for(const k of P.addedNodes)k.tagName==="LINK"&&k.rel==="modulepreload"&&M(k)}).observe(document,{childList:!0,subtree:!0});function x(N){const P={};return N.integrity&&(P.integrity=N.integrity),N.referrerPolicy&&(P.referrerPolicy=N.referrerPolicy),N.crossOrigin==="use-credentials"?P.credentials="include":N.crossOrigin==="anonymous"?P.credentials="omit":P.credentials="same-origin",P}function M(N){if(N.ep)return;N.ep=!0;const P=x(N);fetch(N.href,P)}})();var _hn=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function bke(g){return g&&g.__esModule&&Object.prototype.hasOwnProperty.call(g,"default")?g.default:g}var T7e={exports:{}},OG={};var Lhn;function ezn(){if(Lhn)return OG;Lhn=1;var g=Symbol.for("react.transitional.element"),E=Symbol.for("react.fragment");function x(M,N,P){var k=null;if(P!==void 0&&(k=""+P),N.key!==void 0&&(k=""+N.key),"key"in N){P={};for(var H in N)H!=="key"&&(P[H]=N[H])}else P=N;return N=P.ref,{$$typeof:g,type:M,key:k,ref:N!==void 0?N:null,props:P}}return OG.Fragment=E,OG.jsx=x,OG.jsxs=x,OG}var Phn;function nzn(){return Phn||(Phn=1,T7e.exports=ezn()),T7e.exports}var F=nzn(),C7e={exports:{}},Mc={};var $hn;function tzn(){if($hn)return Mc;$hn=1;var g=Symbol.for("react.transitional.element"),E=Symbol.for("react.portal"),x=Symbol.for("react.fragment"),M=Symbol.for("react.strict_mode"),N=Symbol.for("react.profiler"),P=Symbol.for("react.consumer"),k=Symbol.for("react.context"),H=Symbol.for("react.forward_ref"),U=Symbol.for("react.suspense"),G=Symbol.for("react.memo"),ie=Symbol.for("react.lazy"),Z=Symbol.for("react.activity"),W=Symbol.iterator;function se(xe){return xe===null||typeof xe!="object"?null:(xe=W&&xe[W]||xe["@@iterator"],typeof xe=="function"?xe:null)}var le={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},ee=Object.assign,Oe={};function ge(xe,un,rt){this.props=xe,this.context=un,this.refs=Oe,this.updater=rt||le}ge.prototype.isReactComponent={},ge.prototype.setState=function(xe,un){if(typeof xe!="object"&&typeof xe!="function"&&xe!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,xe,un,"setState")},ge.prototype.forceUpdate=function(xe){this.updater.enqueueForceUpdate(this,xe,"forceUpdate")};function Pe(){}Pe.prototype=ge.prototype;function ae(xe,un,rt){this.props=xe,this.context=un,this.refs=Oe,this.updater=rt||le}var Me=ae.prototype=new Pe;Me.constructor=ae,ee(Me,ge.prototype),Me.isPureReactComponent=!0;var Be=Array.isArray;function rn(){}var ln={H:null,A:null,T:null,S:null},xn=Object.prototype.hasOwnProperty;function hn(xe,un,rt){var lt=rt.ref;return{$$typeof:g,type:xe,key:un,ref:lt!==void 0?lt:null,props:rt}}function wt(xe,un){return hn(xe.type,un,xe.props)}function Cn(xe){return typeof xe=="object"&&xe!==null&&xe.$$typeof===g}function Qn(xe){var un={"=":"=0",":":"=2"};return"$"+xe.replace(/[=:]/g,function(rt){return un[rt]})}var Y=/\/+/g;function Fe(xe,un){return typeof xe=="object"&&xe!==null&&xe.key!=null?Qn(""+xe.key):un.toString(36)}function mn(xe){switch(xe.status){case"fulfilled":return xe.value;case"rejected":throw xe.reason;default:switch(typeof xe.status=="string"?xe.then(rn,rn):(xe.status="pending",xe.then(function(un){xe.status==="pending"&&(xe.status="fulfilled",xe.value=un)},function(un){xe.status==="pending"&&(xe.status="rejected",xe.reason=un)})),xe.status){case"fulfilled":return xe.value;case"rejected":throw xe.reason}}throw xe}function Ae(xe,un,rt,lt,Bt){var hi=typeof xe;(hi==="undefined"||hi==="boolean")&&(xe=null);var Gt=!1;if(xe===null)Gt=!0;else switch(hi){case"bigint":case"string":case"number":Gt=!0;break;case"object":switch(xe.$$typeof){case g:case E:Gt=!0;break;case ie:return Gt=xe._init,Ae(Gt(xe._payload),un,rt,lt,Bt)}}if(Gt)return Bt=Bt(xe),Gt=lt===""?"."+Fe(xe,0):lt,Be(Bt)?(rt="",Gt!=null&&(rt=Gt.replace(Y,"$&/")+"/"),Ae(Bt,un,rt,"",function(Wr){return Wr})):Bt!=null&&(Cn(Bt)&&(Bt=wt(Bt,rt+(Bt.key==null||xe&&xe.key===Bt.key?"":(""+Bt.key).replace(Y,"$&/")+"/")+Gt)),un.push(Bt)),1;Gt=0;var At=lt===""?".":lt+":";if(Be(xe))for(var st=0;st>>1,kn=Ae[Sn];if(0>>1;SnN(rt,sn))ltN(Bt,rt)?(Ae[Sn]=Bt,Ae[lt]=sn,Sn=lt):(Ae[Sn]=rt,Ae[un]=sn,Sn=un);else if(ltN(Bt,sn))Ae[Sn]=Bt,Ae[lt]=sn,Sn=lt;else break e}}return Ze}function N(Ae,Ze){var sn=Ae.sortIndex-Ze.sortIndex;return sn!==0?sn:Ae.id-Ze.id}if(g.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var P=performance;g.unstable_now=function(){return P.now()}}else{var k=Date,H=k.now();g.unstable_now=function(){return k.now()-H}}var U=[],G=[],ie=1,Z=null,W=3,se=!1,le=!1,ee=!1,Oe=!1,ge=typeof setTimeout=="function"?setTimeout:null,Pe=typeof clearTimeout=="function"?clearTimeout:null,ae=typeof setImmediate<"u"?setImmediate:null;function Me(Ae){for(var Ze=x(G);Ze!==null;){if(Ze.callback===null)M(G);else if(Ze.startTime<=Ae)M(G),Ze.sortIndex=Ze.expirationTime,E(U,Ze);else break;Ze=x(G)}}function Be(Ae){if(ee=!1,Me(Ae),!le)if(x(U)!==null)le=!0,rn||(rn=!0,Qn());else{var Ze=x(G);Ze!==null&&mn(Be,Ze.startTime-Ae)}}var rn=!1,ln=-1,xn=5,hn=-1;function wt(){return Oe?!0:!(g.unstable_now()-hnAe&&wt());){var Sn=Z.callback;if(typeof Sn=="function"){Z.callback=null,W=Z.priorityLevel;var kn=Sn(Z.expirationTime<=Ae);if(Ae=g.unstable_now(),typeof kn=="function"){Z.callback=kn,Me(Ae),Ze=!0;break n}Z===x(U)&&M(U),Me(Ae)}else M(U);Z=x(U)}if(Z!==null)Ze=!0;else{var xe=x(G);xe!==null&&mn(Be,xe.startTime-Ae),Ze=!1}}break e}finally{Z=null,W=sn,se=!1}Ze=void 0}}finally{Ze?Qn():rn=!1}}}var Qn;if(typeof ae=="function")Qn=function(){ae(Cn)};else if(typeof MessageChannel<"u"){var Y=new MessageChannel,Fe=Y.port2;Y.port1.onmessage=Cn,Qn=function(){Fe.postMessage(null)}}else Qn=function(){ge(Cn,0)};function mn(Ae,Ze){ln=ge(function(){Ae(g.unstable_now())},Ze)}g.unstable_IdlePriority=5,g.unstable_ImmediatePriority=1,g.unstable_LowPriority=4,g.unstable_NormalPriority=3,g.unstable_Profiling=null,g.unstable_UserBlockingPriority=2,g.unstable_cancelCallback=function(Ae){Ae.callback=null},g.unstable_forceFrameRate=function(Ae){0>Ae||125Sn?(Ae.sortIndex=sn,E(G,Ae),x(U)===null&&Ae===x(G)&&(ee?(Pe(ln),ln=-1):ee=!0,mn(Be,sn-Sn))):(Ae.sortIndex=kn,E(U,Ae),le||se||(le=!0,rn||(rn=!0,Qn()))),Ae},g.unstable_shouldYield=wt,g.unstable_wrapCallback=function(Ae){var Ze=W;return function(){var sn=W;W=Ze;try{return Ae.apply(this,arguments)}finally{W=sn}}}})(D7e)),D7e}var zhn;function czn(){return zhn||(zhn=1,N7e.exports=rzn()),N7e.exports}var I7e={exports:{}},rd={};var Fhn;function uzn(){if(Fhn)return rd;Fhn=1;var g=WG();function E(U){var G="https://react.dev/errors/"+U;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(g)}catch(E){console.error(E)}}return g(),I7e.exports=uzn(),I7e.exports}var Hhn;function ozn(){if(Hhn)return NG;Hhn=1;var g=czn(),E=WG(),x=odn();function M(a){var d="https://react.dev/errors/"+a;if(1kn||(a.current=Sn[kn],Sn[kn]=null,kn--)}function rt(a,d){kn++,Sn[kn]=a.current,a.current=d}var lt=xe(null),Bt=xe(null),hi=xe(null),Gt=xe(null);function At(a,d){switch(rt(hi,d),rt(Bt,a),rt(lt,null),d.nodeType){case 9:case 11:a=(a=d.documentElement)&&(a=a.namespaceURI)?sP(a):0;break;default:if(a=d.tagName,d=d.namespaceURI)d=sP(d),a=lP(d,a);else switch(a){case"svg":a=1;break;case"math":a=2;break;default:a=0}}un(lt),rt(lt,a)}function st(){un(lt),un(Bt),un(hi)}function Wr(a){a.memoizedState!==null&&rt(Gt,a);var d=lt.current,w=lP(d,a.type);d!==w&&(rt(Bt,a),rt(lt,w))}function Mr(a){Bt.current===a&&(un(lt),un(Bt)),Gt.current===a&&(un(Gt),Y5._currentValue=sn)}var ur,mi;function Fi(a){if(ur===void 0)try{throw Error()}catch(w){var d=w.stack.trim().match(/\n( *(at )?)/);ur=d&&d[1]||"",mi=-1)":-1C||en[j]!==Ln[C]){var nt=` +`+en[j].replace(" at new "," at ");return a.displayName&&nt.includes("")&&(nt=nt.replace("",a.displayName)),nt}while(1<=j&&0<=C);break}}}finally{fu=!1,Error.prepareStackTrace=w}return(w=a?a.displayName||a.name:"")?Fi(w):""}function Ws(a,d){switch(a.tag){case 26:case 27:case 5:return Fi(a.type);case 16:return Fi("Lazy");case 13:return a.child!==d&&d!==null?Fi("Suspense Fallback"):Fi("Suspense");case 19:return Fi("SuspenseList");case 0:case 15:return Eu(a.type,!1);case 11:return Eu(a.type.render,!1);case 1:return Eu(a.type,!0);case 31:return Fi("Activity");default:return""}}function Dh(a){try{var d="",w=null;do d+=Ws(a,w),w=a,a=a.return;while(a);return d}catch(j){return` +Error generating stack: `+j.message+` +`+j.stack}}var ef=Object.prototype.hasOwnProperty,ch=g.unstable_scheduleCallback,kc=g.unstable_cancelCallback,cd=g.unstable_shouldYield,jb=g.unstable_requestPaint,Rs=g.unstable_now,c0=g.unstable_getCurrentPriorityLevel,u0=g.unstable_ImmediatePriority,o0=g.unstable_UserBlockingPriority,Bg=g.unstable_NormalPriority,r6=g.unstable_LowPriority,zg=g.unstable_IdlePriority,c6=g.log,d1=g.unstable_setDisableYieldValue,ud=null,Sf=null;function b1(a){if(typeof c6=="function"&&d1(a),Sf&&typeof Sf.setStrictMode=="function")try{Sf.setStrictMode(ud,a)}catch{}}var xf=Math.clz32?Math.clz32:a5,l5=Math.log,f5=Math.LN2;function a5(a){return a>>>=0,a===0?32:31-(l5(a)/f5|0)|0}var Fg=256,Eb=262144,Jg=4194304;function _u(a){var d=a&42;if(d!==0)return d;switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return a&261888;case 262144:case 524288:case 1048576:case 2097152:return a&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return a&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return a}}function Hg(a,d,w){var j=a.pendingLanes;if(j===0)return 0;var C=0,D=a.suspendedLanes,Q=a.pingedLanes;a=a.warmLanes;var de=j&134217727;return de!==0?(j=de&~D,j!==0?C=_u(j):(Q&=de,Q!==0?C=_u(Q):w||(w=de&~a,w!==0&&(C=_u(w))))):(de=j&~D,de!==0?C=_u(de):Q!==0?C=_u(Q):w||(w=j&~a,w!==0&&(C=_u(w)))),C===0?0:d!==0&&d!==C&&(d&D)===0&&(D=C&-C,w=d&-d,D>=w||D===32&&(w&4194048)!==0)?d:C}function Gg(a,d){return(a.pendingLanes&~(a.suspendedLanes&~a.pingedLanes)&d)===0}function u6(a,d){switch(a){case 1:case 2:case 4:case 8:case 64:return d+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return d+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function h5(){var a=Jg;return Jg<<=1,(Jg&62914560)===0&&(Jg=4194304),a}function Wm(a){for(var d=[],w=0;31>w;w++)d.push(a);return d}function qg(a,d){a.pendingLanes|=d,d!==268435456&&(a.suspendedLanes=0,a.pingedLanes=0,a.warmLanes=0)}function o6(a,d,w,j,C,D){var Q=a.pendingLanes;a.pendingLanes=w,a.suspendedLanes=0,a.pingedLanes=0,a.warmLanes=0,a.expiredLanes&=w,a.entangledLanes&=w,a.errorRecoveryDisabledLanes&=w,a.shellSuspendCounter=0;var de=a.entanglements,en=a.expirationTimes,Ln=a.hiddenUpdates;for(w=Q&~w;0"u")return null;try{return a.activeElement||a.body}catch{return a.body}}var OA=/[\n"\\]/g;function _h(a){return a.replace(OA,function(d){return"\\"+d.charCodeAt(0).toString(16)+" "})}function i3(a,d,w,j,C,D,Q,de){a.name="",Q!=null&&typeof Q!="function"&&typeof Q!="symbol"&&typeof Q!="boolean"?a.type=Q:a.removeAttribute("type"),d!=null?Q==="number"?(d===0&&a.value===""||a.value!=d)&&(a.value=""+Ih(d)):a.value!==""+Ih(d)&&(a.value=""+Ih(d)):Q!=="submit"&&Q!=="reset"||a.removeAttribute("value"),d!=null?a6(a,Q,Ih(d)):w!=null?a6(a,Q,Ih(w)):j!=null&&a.removeAttribute("value"),C==null&&D!=null&&(a.defaultChecked=!!D),C!=null&&(a.checked=C&&typeof C!="function"&&typeof C!="symbol"),de!=null&&typeof de!="function"&&typeof de!="symbol"&&typeof de!="boolean"?a.name=""+Ih(de):a.removeAttribute("name")}function ok(a,d,w,j,C,D,Q,de){if(D!=null&&typeof D!="function"&&typeof D!="symbol"&&typeof D!="boolean"&&(a.type=D),d!=null||w!=null){if(!(D!=="submit"&&D!=="reset"||d!=null)){p5(a);return}w=w!=null?""+Ih(w):"",d=d!=null?""+Ih(d):w,de||d===a.value||(a.value=d),a.defaultValue=d}j=j??C,j=typeof j!="function"&&typeof j!="symbol"&&!!j,a.checked=de?a.checked:!!j,a.defaultChecked=!!j,Q!=null&&typeof Q!="function"&&typeof Q!="symbol"&&typeof Q!="boolean"&&(a.name=Q),p5(a)}function a6(a,d,w){d==="number"&&t3(a.ownerDocument)===a||a.defaultValue===""+w||(a.defaultValue=""+w)}function Vg(a,d,w,j){if(a=a.options,d){d={};for(var C=0;C"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),LA=!1;if(Qg)try{var d6={};Object.defineProperty(d6,"passive",{get:function(){LA=!0}}),window.addEventListener("test",d6,d6),window.removeEventListener("test",d6,d6)}catch{LA=!1}var Op=null,PA=null,lk=null;function xI(){if(lk)return lk;var a,d=PA,w=d.length,j,C="value"in Op?Op.value:Op.textContent,D=C.length;for(a=0;a=w6),NI=" ",DI=!1;function II(a,d){switch(a){case"keyup":return Iq.indexOf(d.keyCode)!==-1;case"keydown":return d.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function _I(a){return a=a.detail,typeof a=="object"&&"data"in a?a.data:null}var k5=!1;function Lq(a,d){switch(a){case"compositionend":return _I(d);case"keypress":return d.which!==32?null:(DI=!0,NI);case"textInput":return a=d.data,a===NI&&DI?null:a;default:return null}}function Pq(a,d){if(k5)return a==="compositionend"||!FA&&II(a,d)?(a=xI(),lk=PA=Op=null,k5=!1,a):null;switch(a){case"paste":return null;case"keypress":if(!(d.ctrlKey||d.altKey||d.metaKey)||d.ctrlKey&&d.altKey){if(d.char&&1=d)return{node:w,offset:d-a};a=j}e:{for(;w;){if(w.nextSibling){w=w.nextSibling;break e}w=w.parentNode}w=void 0}w=FI(w)}}function HI(a,d){return a&&d?a===d?!0:a&&a.nodeType===3?!1:d&&d.nodeType===3?HI(a,d.parentNode):"contains"in a?a.contains(d):a.compareDocumentPosition?!!(a.compareDocumentPosition(d)&16):!1:!1}function GI(a){a=a!=null&&a.ownerDocument!=null&&a.ownerDocument.defaultView!=null?a.ownerDocument.defaultView:window;for(var d=t3(a.document);d instanceof a.HTMLIFrameElement;){try{var w=typeof d.contentWindow.location.href=="string"}catch{w=!1}if(w)a=d.contentWindow;else break;d=t3(a.document)}return d}function qA(a){var d=a&&a.nodeName&&a.nodeName.toLowerCase();return d&&(d==="input"&&(a.type==="text"||a.type==="search"||a.type==="tel"||a.type==="url"||a.type==="password")||d==="textarea"||a.contentEditable==="true")}var Gq=Qg&&"documentMode"in document&&11>=document.documentMode,j5=null,UA=null,y6=null,XA=!1;function qI(a,d,w){var j=w.window===w?w.document:w.nodeType===9?w:w.ownerDocument;XA||j5==null||j5!==t3(j)||(j=j5,"selectionStart"in j&&qA(j)?j={start:j.selectionStart,end:j.selectionEnd}:(j=(j.ownerDocument&&j.ownerDocument.defaultView||window).getSelection(),j={anchorNode:j.anchorNode,anchorOffset:j.anchorOffset,focusNode:j.focusNode,focusOffset:j.focusOffset}),y6&&v6(y6,j)||(y6=j,j=nj(UA,"onSelect"),0>=Q,C-=Q,Sb=1<<32-xf(d)+C|w<Hr?(tc=Ui,Ui=null):tc=Ui.sibling;var qc=Jn(En,Ui,In[Hr],ct);if(qc===null){Ui===null&&(Ui=tc);break}a&&Ui&&qc.alternate===null&&d(En,Ui),on=D(qc,on,Hr),Lu===null?Ji=qc:Lu.sibling=qc,Lu=qc,Ui=tc}if(Hr===In.length)return w(En,Ui),cu&&Zg(En,Hr),Ji;if(Ui===null){for(;HrHr?(tc=Ui,Ui=null):tc=Ui.sibling;var xt=Jn(En,Ui,qc.value,ct);if(xt===null){Ui===null&&(Ui=tc);break}a&&Ui&&xt.alternate===null&&d(En,Ui),on=D(xt,on,Hr),Lu===null?Ji=xt:Lu.sibling=xt,Lu=xt,Ui=tc}if(qc.done)return w(En,Ui),cu&&Zg(En,Hr),Ji;if(Ui===null){for(;!qc.done;Hr++,qc=In.next())qc=gt(En,qc.value,ct),qc!==null&&(on=D(qc,on,Hr),Lu===null?Ji=qc:Lu.sibling=qc,Lu=qc);return cu&&Zg(En,Hr),Ji}for(Ui=j(Ui);!qc.done;Hr++,qc=In.next())qc=Zn(Ui,En,Hr,qc.value,ct),qc!==null&&(a&&qc.alternate!==null&&Ui.delete(qc.key===null?Hr:qc.key),on=D(qc,on,Hr),Lu===null?Ji=qc:Lu.sibling=qc,Lu=qc);return a&&Ui.forEach(function(lX){return d(En,lX)}),cu&&Zg(En,Hr),Ji}function co(En,on,In,ct){if(typeof In=="object"&&In!==null&&In.type===ee&&In.key===null&&(In=In.props.children),typeof In=="object"&&In!==null){switch(In.$$typeof){case se:e:{for(var Ji=In.key;on!==null;){if(on.key===Ji){if(Ji=In.type,Ji===ee){if(on.tag===7){w(En,on.sibling),ct=C(on,In.props.children),ct.return=En,En=ct;break e}}else if(on.elementType===Ji||typeof Ji=="object"&&Ji!==null&&Ji.$$typeof===xn&&a3(Ji)===on.type){w(En,on.sibling),ct=C(on,In.props),T6(ct,In),ct.return=En,En=ct;break e}w(En,on);break}else d(En,on);on=on.sibling}In.type===ee?(ct=s3(In.props.children,En.mode,ct,In.key),ct.return=En,En=ct):(ct=vk(In.type,In.key,In.props,null,En.mode,ct),T6(ct,In),ct.return=En,En=ct)}return Q(En);case le:e:{for(Ji=In.key;on!==null;){if(on.key===Ji)if(on.tag===4&&on.stateNode.containerInfo===In.containerInfo&&on.stateNode.implementation===In.implementation){w(En,on.sibling),ct=C(on,In.children||[]),ct.return=En,En=ct;break e}else{w(En,on);break}else d(En,on);on=on.sibling}ct=eM(In,En.mode,ct),ct.return=En,En=ct}return Q(En);case xn:return In=a3(In),co(En,on,In,ct)}if(mn(In))return Di(En,on,In,ct);if(Qn(In)){if(Ji=Qn(In),typeof Ji!="function")throw Error(M(150));return In=Ji.call(In),Tr(En,on,In,ct)}if(typeof In.then=="function")return co(En,on,Sk(In),ct);if(In.$$typeof===ae)return co(En,on,E6(En,In),ct);xk(En,In)}return typeof In=="string"&&In!==""||typeof In=="number"||typeof In=="bigint"?(In=""+In,on!==null&&on.tag===6?(w(En,on.sibling),ct=C(on,In),ct.return=En,En=ct):(w(En,on),ct=ZA(In,En.mode,ct),ct.return=En,En=ct),Q(En)):w(En,on)}return function(En,on,In,ct){try{M6=0;var Ji=co(En,on,In,ct);return _5=null,Ji}catch(Ui){if(Ui===I5||Ui===jk)throw Ui;var Lu=w1(29,Ui,null,En.mode);return Lu.lanes=ct,Lu.return=En,Lu}}}var d3=h_(!0),d_=h_(!1),Rp=!1;function dM(a){a.updateQueue={baseState:a.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function bM(a,d){a=a.updateQueue,d.updateQueue===a&&(d.updateQueue={baseState:a.baseState,firstBaseUpdate:a.firstBaseUpdate,lastBaseUpdate:a.lastBaseUpdate,shared:a.shared,callbacks:null})}function Bp(a){return{lane:a,tag:0,payload:null,callback:null,next:null}}function zp(a,d,w){var j=a.updateQueue;if(j===null)return null;if(j=j.shared,(Ju&2)!==0){var C=j.pending;return C===null?d.next=d:(d.next=C.next,C.next=d),j.pending=d,d=mk(a),WI(a,null,w),d}return pk(a,j,d,w),mk(a)}function C6(a,d,w){if(d=d.updateQueue,d!==null&&(d=d.shared,(w&4194048)!==0)){var j=d.lanes;j&=a.pendingLanes,w|=j,d.lanes=w,Zm(a,w)}}function gM(a,d){var w=a.updateQueue,j=a.alternate;if(j!==null&&(j=j.updateQueue,w===j)){var C=null,D=null;if(w=w.firstBaseUpdate,w!==null){do{var Q={lane:w.lane,tag:w.tag,payload:w.payload,callback:null,next:null};D===null?C=D=Q:D=D.next=Q,w=w.next}while(w!==null);D===null?C=D=d:D=D.next=d}else C=D=d;w={baseState:j.baseState,firstBaseUpdate:C,lastBaseUpdate:D,shared:j.shared,callbacks:j.callbacks},a.updateQueue=w;return}a=w.lastBaseUpdate,a===null?w.firstBaseUpdate=d:a.next=d,w.lastBaseUpdate=d}var wM=!1;function O6(){if(wM){var a=D5;if(a!==null)throw a}}function N6(a,d,w,j){wM=!1;var C=a.updateQueue;Rp=!1;var D=C.firstBaseUpdate,Q=C.lastBaseUpdate,de=C.shared.pending;if(de!==null){C.shared.pending=null;var en=de,Ln=en.next;en.next=null,Q===null?D=Ln:Q.next=Ln,Q=en;var nt=a.alternate;nt!==null&&(nt=nt.updateQueue,de=nt.lastBaseUpdate,de!==Q&&(de===null?nt.firstBaseUpdate=Ln:de.next=Ln,nt.lastBaseUpdate=en))}if(D!==null){var gt=C.baseState;Q=0,nt=Ln=en=null,de=D;do{var Jn=de.lane&-536870913,Zn=Jn!==de.lane;if(Zn?(nu&Jn)===Jn:(j&Jn)===Jn){Jn!==0&&Jn===N5&&(wM=!0),nt!==null&&(nt=nt.next={lane:0,tag:de.tag,payload:de.payload,callback:null,next:null});e:{var Di=a,Tr=de;Jn=d;var co=w;switch(Tr.tag){case 1:if(Di=Tr.payload,typeof Di=="function"){gt=Di.call(co,gt,Jn);break e}gt=Di;break e;case 3:Di.flags=Di.flags&-65537|128;case 0:if(Di=Tr.payload,Jn=typeof Di=="function"?Di.call(co,gt,Jn):Di,Jn==null)break e;gt=Z({},gt,Jn);break e;case 2:Rp=!0}}Jn=de.callback,Jn!==null&&(a.flags|=64,Zn&&(a.flags|=8192),Zn=C.callbacks,Zn===null?C.callbacks=[Jn]:Zn.push(Jn))}else Zn={lane:Jn,tag:de.tag,payload:de.payload,callback:de.callback,next:null},nt===null?(Ln=nt=Zn,en=gt):nt=nt.next=Zn,Q|=Jn;if(de=de.next,de===null){if(de=C.shared.pending,de===null)break;Zn=de,de=Zn.next,Zn.next=null,C.lastBaseUpdate=Zn,C.shared.pending=null}}while(!0);nt===null&&(en=gt),C.baseState=en,C.firstBaseUpdate=Ln,C.lastBaseUpdate=nt,D===null&&(C.shared.lanes=0),qp|=Q,a.lanes=Q,a.memoizedState=gt}}function b_(a,d){if(typeof a!="function")throw Error(M(191,a));a.call(d)}function g_(a,d){var w=a.callbacks;if(w!==null)for(a.callbacks=null,a=0;aD?D:8;var Q=Ae.T,de={};Ae.T=de,LM(a,!1,d,w);try{var en=C(),Ln=Ae.S;if(Ln!==null&&Ln(de,en),en!==null&&typeof en=="object"&&typeof en.then=="function"){var nt=Wq(en,j);L6(a,d,nt,k1(a))}else L6(a,d,j,k1(a))}catch(gt){L6(a,d,{then:function(){},status:"rejected",reason:gt},k1())}finally{Ze.p=D,Q!==null&&de.types!==null&&(Q.types=de.types),Ae.T=Q}}function IM(){}function _6(a,d,w,j){if(a.tag!==5)throw Error(M(476));var C=U_(a).queue;q_(a,C,d,sn,w===null?IM:function(){return Lk(a),w(j)})}function U_(a){var d=a.memoizedState;if(d!==null)return d;d={memoizedState:sn,baseState:sn,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:iw,lastRenderedState:sn},next:null};var w={};return d.next={memoizedState:w,baseState:w,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:iw,lastRenderedState:w},next:null},a.memoizedState=d,a=a.alternate,a!==null&&(a.memoizedState=d),d}function Lk(a){var d=U_(a);d.next===null&&(d=a.alternate.memoizedState),L6(a,d.next.queue,{},k1())}function _M(){return ca(Y5)}function X_(){return nl().memoizedState}function K_(){return nl().memoizedState}function cU(a){for(var d=a.return;d!==null;){switch(d.tag){case 24:case 3:var w=k1();a=Bp(w);var j=zp(d,a,w);j!==null&&(Bh(j,d,w),C6(j,d,w)),d={cache:sM()},a.payload=d;return}d=d.return}}function uU(a,d,w){var j=k1();w={lane:j,revertLane:0,gesture:null,action:w,hasEagerState:!1,eagerState:null,next:null},Pk(a)?Y_(d,w):(w=QA(a,d,w,j),w!==null&&(Bh(w,a,j),PM(w,d,j)))}function V_(a,d,w){var j=k1();L6(a,d,w,j)}function L6(a,d,w,j){var C={lane:j,revertLane:0,gesture:null,action:w,hasEagerState:!1,eagerState:null,next:null};if(Pk(a))Y_(d,C);else{var D=a.alternate;if(a.lanes===0&&(D===null||D.lanes===0)&&(D=d.lastRenderedReducer,D!==null))try{var Q=d.lastRenderedState,de=D(Q,w);if(C.hasEagerState=!0,C.eagerState=de,g1(de,Q))return pk(a,d,C,0),Io===null&&wk(),!1}catch{}if(w=QA(a,d,C,j),w!==null)return Bh(w,a,j),PM(w,d,j),!0}return!1}function LM(a,d,w,j){if(j={lane:2,revertLane:pT(),gesture:null,action:j,hasEagerState:!1,eagerState:null,next:null},Pk(a)){if(d)throw Error(M(479))}else d=QA(a,w,j,2),d!==null&&Bh(d,a,2)}function Pk(a){var d=a.alternate;return a===bc||d!==null&&d===bc}function Y_(a,d){P5=Tk=!0;var w=a.pending;w===null?d.next=d:(d.next=w.next,w.next=d),a.pending=d}function PM(a,d,w){if((w&4194048)!==0){var j=d.lanes;j&=a.pendingLanes,w|=j,d.lanes=w,Zm(a,w)}}var P6={readContext:ca,use:Nk,useCallback:Bs,useContext:Bs,useEffect:Bs,useImperativeHandle:Bs,useLayoutEffect:Bs,useInsertionEffect:Bs,useMemo:Bs,useReducer:Bs,useRef:Bs,useState:Bs,useDebugValue:Bs,useDeferredValue:Bs,useTransition:Bs,useSyncExternalStore:Bs,useId:Bs,useHostTransitionStatus:Bs,useFormState:Bs,useActionState:Bs,useOptimistic:Bs,useMemoCache:Bs,useCacheRefresh:Bs};P6.useEffectEvent=Bs;var oU={readContext:ca,use:Nk,useCallback:function(a,d){return uh().memoizedState=[a,d===void 0?null:d],a},useContext:ca,useEffect:P_,useImperativeHandle:function(a,d,w){w=w!=null?w.concat([a]):null,Ik(4194308,4,z_.bind(null,d,a),w)},useLayoutEffect:function(a,d){return Ik(4194308,4,a,d)},useInsertionEffect:function(a,d){Ik(4,2,a,d)},useMemo:function(a,d){var w=uh();d=d===void 0?null:d;var j=a();if(b3){b1(!0);try{a()}finally{b1(!1)}}return w.memoizedState=[j,d],j},useReducer:function(a,d,w){var j=uh();if(w!==void 0){var C=w(d);if(b3){b1(!0);try{w(d)}finally{b1(!1)}}}else C=d;return j.memoizedState=j.baseState=C,a={pending:null,lanes:0,dispatch:null,lastRenderedReducer:a,lastRenderedState:C},j.queue=a,a=a.dispatch=uU.bind(null,bc,a),[j.memoizedState,a]},useRef:function(a){var d=uh();return a={current:a},d.memoizedState=a},useState:function(a){a=TM(a);var d=a.queue,w=V_.bind(null,bc,d);return d.dispatch=w,[a.memoizedState,w]},useDebugValue:NM,useDeferredValue:function(a,d){var w=uh();return DM(w,a,d)},useTransition:function(){var a=TM(!1);return a=q_.bind(null,bc,a.queue,!0,!1),uh().memoizedState=a,[!1,a]},useSyncExternalStore:function(a,d,w){var j=bc,C=uh();if(cu){if(w===void 0)throw Error(M(407));w=w()}else{if(w=d(),Io===null)throw Error(M(349));(nu&127)!==0||k_(j,d,w)}C.memoizedState=w;var D={value:w,getSnapshot:d};return C.queue=D,P_(nU.bind(null,j,D,a),[a]),j.flags|=2048,R5(9,{destroy:void 0},j_.bind(null,j,D,w,d),null),w},useId:function(){var a=uh(),d=Io.identifierPrefix;if(cu){var w=xb,j=Sb;w=(j&~(1<<32-xf(j)-1)).toString(32)+w,d="_"+d+"R_"+w,w=Ck++,0<\/script>",D=D.removeChild(D.firstChild);break;case"select":D=typeof j.is=="string"?Q.createElement("select",{is:j.is}):Q.createElement("select"),j.multiple?D.multiple=!0:j.size&&(D.size=j.size);break;default:D=typeof j.is=="string"?Q.createElement(C,{is:j.is}):Q.createElement(C)}}D[Zs]=d,D[Ca]=j;e:for(Q=d.child;Q!==null;){if(Q.tag===5||Q.tag===6)D.appendChild(Q.stateNode);else if(Q.tag!==4&&Q.tag!==27&&Q.child!==null){Q.child.return=Q,Q=Q.child;continue}if(Q===d)break e;for(;Q.sibling===null;){if(Q.return===null||Q.return===d)break e;Q=Q.return}Q.sibling.return=Q.return,Q=Q.sibling}d.stateNode=D;e:switch(oa(D,C,j),C){case"button":case"input":case"select":case"textarea":j=!!j.autoFocus;break e;case"img":j=!0;break e;default:j=!1}j&&a0(d)}}return yo(d),YM(d,d.type,a===null?null:a.memoizedProps,d.pendingProps,w),null;case 6:if(a&&d.stateNode!=null)a.memoizedProps!==j&&a0(d);else{if(typeof j!="string"&&d.stateNode===null)throw Error(M(166));if(a=hi.current,T5(d)){if(a=d.stateNode,w=d.memoizedProps,j=null,C=ra,C!==null)switch(C.tag){case 27:case 5:j=C.memoizedProps}a[Zs]=d,a=!!(a.nodeValue===w||j!==null&&j.suppressHydrationWarning===!0||uP(a.nodeValue,w)),a||Ip(d,!0)}else a=tj(a).createTextNode(j),a[Zs]=d,d.stateNode=a}return yo(d),null;case 31:if(w=d.memoizedState,a===null||a.memoizedState!==null){if(j=T5(d),w!==null){if(a===null){if(!j)throw Error(M(318));if(a=d.memoizedState,a=a!==null?a.dehydrated:null,!a)throw Error(M(557));a[Zs]=d}else l3(),(d.flags&128)===0&&(d.memoizedState=null),d.flags|=4;yo(d),a=!1}else w=C5(),a!==null&&a.memoizedState!==null&&(a.memoizedState.hydrationErrors=w),a=!0;if(!a)return d.flags&256?(m1(d),d):(m1(d),null);if((d.flags&128)!==0)throw Error(M(558))}return yo(d),null;case 13:if(j=d.memoizedState,a===null||a.memoizedState!==null&&a.memoizedState.dehydrated!==null){if(C=T5(d),j!==null&&j.dehydrated!==null){if(a===null){if(!C)throw Error(M(318));if(C=d.memoizedState,C=C!==null?C.dehydrated:null,!C)throw Error(M(317));C[Zs]=d}else l3(),(d.flags&128)===0&&(d.memoizedState=null),d.flags|=4;yo(d),C=!1}else C=C5(),a!==null&&a.memoizedState!==null&&(a.memoizedState.hydrationErrors=C),C=!0;if(!C)return d.flags&256?(m1(d),d):(m1(d),null)}return m1(d),(d.flags&128)!==0?(d.lanes=w,d):(w=j!==null,a=a!==null&&a.memoizedState!==null,w&&(j=d.child,C=null,j.alternate!==null&&j.alternate.memoizedState!==null&&j.alternate.memoizedState.cachePool!==null&&(C=j.alternate.memoizedState.cachePool.pool),D=null,j.memoizedState!==null&&j.memoizedState.cachePool!==null&&(D=j.memoizedState.cachePool.pool),D!==C&&(j.flags|=2048)),w!==a&&w&&(d.child.flags|=8192),zk(d,d.updateQueue),yo(d),null);case 4:return st(),a===null&&kT(d.stateNode.containerInfo),yo(d),null;case 10:return nw(d.type),yo(d),null;case 19:if(un(el),j=d.memoizedState,j===null)return yo(d),null;if(C=(d.flags&128)!==0,D=j.rendering,D===null)if(C)w3(j,!1);else{if(zs!==0||a!==null&&(a.flags&128)!==0)for(a=d.child;a!==null;){if(D=Mk(a),D!==null){for(d.flags|=128,w3(j,!1),a=D.updateQueue,d.updateQueue=a,zk(d,a),d.subtreeFlags=0,a=w,w=d.child;w!==null;)ZI(w,a),w=w.sibling;return rt(el,el.current&1|2),cu&&Zg(d,j.treeForkCount),d.child}a=a.sibling}j.tail!==null&&Rs()>Uk&&(d.flags|=128,C=!0,w3(j,!1),d.lanes=4194304)}else{if(!C)if(a=Mk(D),a!==null){if(d.flags|=128,C=!0,a=a.updateQueue,d.updateQueue=a,zk(d,a),w3(j,!0),j.tail===null&&j.tailMode==="hidden"&&!D.alternate&&!cu)return yo(d),null}else 2*Rs()-j.renderingStartTime>Uk&&w!==536870912&&(d.flags|=128,C=!0,w3(j,!1),d.lanes=4194304);j.isBackwards?(D.sibling=d.child,d.child=D):(a=j.last,a!==null?a.sibling=D:d.child=D,j.last=D)}return j.tail!==null?(a=j.tail,j.rendering=a,j.tail=a.sibling,j.renderingStartTime=Rs(),a.sibling=null,w=el.current,rt(el,C?w&1|2:w&1),cu&&Zg(d,j.treeForkCount),a):(yo(d),null);case 22:case 23:return m1(d),mM(),j=d.memoizedState!==null,a!==null?a.memoizedState!==null!==j&&(d.flags|=8192):j&&(d.flags|=8192),j?(w&536870912)!==0&&(d.flags&128)===0&&(yo(d),d.subtreeFlags&6&&(d.flags|=8192)):yo(d),w=d.updateQueue,w!==null&&zk(d,w.retryQueue),w=null,a!==null&&a.memoizedState!==null&&a.memoizedState.cachePool!==null&&(w=a.memoizedState.cachePool.pool),j=null,d.memoizedState!==null&&d.memoizedState.cachePool!==null&&(j=d.memoizedState.cachePool.pool),j!==w&&(d.flags|=2048),a!==null&&un(f3),null;case 24:return w=null,a!==null&&(w=a.memoizedState.cache),d.memoizedState.cache!==w&&(d.flags|=2048),nw(Al),yo(d),null;case 25:return null;case 30:return null}throw Error(M(156,d.tag))}function aU(a,d){switch(tM(d),d.tag){case 1:return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 3:return nw(Al),st(),a=d.flags,(a&65536)!==0&&(a&128)===0?(d.flags=a&-65537|128,d):null;case 26:case 27:case 5:return Mr(d),null;case 31:if(d.memoizedState!==null){if(m1(d),d.alternate===null)throw Error(M(340));l3()}return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 13:if(m1(d),a=d.memoizedState,a!==null&&a.dehydrated!==null){if(d.alternate===null)throw Error(M(340));l3()}return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 19:return un(el),null;case 4:return st(),null;case 10:return nw(d.type),null;case 22:case 23:return m1(d),mM(),a!==null&&un(f3),a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 24:return nw(Al),null;case 25:return null;default:return null}}function QM(a,d){switch(tM(d),d.tag){case 3:nw(Al),st();break;case 26:case 27:case 5:Mr(d);break;case 4:st();break;case 31:d.memoizedState!==null&&m1(d);break;case 13:m1(d);break;case 19:un(el);break;case 10:nw(d.type);break;case 22:case 23:m1(d),mM(),a!==null&&un(f3);break;case 24:nw(Al)}}function B6(a,d){try{var w=d.updateQueue,j=w!==null?w.lastEffect:null;if(j!==null){var C=j.next;w=C;do{if((w.tag&a)===a){j=void 0;var D=w.create,Q=w.inst;j=D(),Q.destroy=j}w=w.next}while(w!==C)}}catch(de){ro(d,d.return,de)}}function Hp(a,d,w){try{var j=d.updateQueue,C=j!==null?j.lastEffect:null;if(C!==null){var D=C.next;j=D;do{if((j.tag&a)===a){var Q=j.inst,de=Q.destroy;if(de!==void 0){Q.destroy=void 0,C=d;var en=w,Ln=de;try{Ln()}catch(nt){ro(C,en,nt)}}}j=j.next}while(j!==D)}}catch(nt){ro(d,d.return,nt)}}function z6(a){var d=a.updateQueue;if(d!==null){var w=a.stateNode;try{g_(d,w)}catch(j){ro(a,a.return,j)}}}function pL(a,d,w){w.props=g3(a.type,a.memoizedProps),w.state=a.memoizedState;try{w.componentWillUnmount()}catch(j){ro(a,d,j)}}function F6(a,d){try{var w=a.ref;if(w!==null){switch(a.tag){case 26:case 27:case 5:var j=a.stateNode;break;case 30:j=a.stateNode;break;default:j=a.stateNode}typeof w=="function"?a.refCleanup=w(j):w.current=j}}catch(C){ro(a,d,C)}}function Ab(a,d){var w=a.ref,j=a.refCleanup;if(w!==null)if(typeof j=="function")try{j()}catch(C){ro(a,d,C)}finally{a.refCleanup=null,a=a.alternate,a!=null&&(a.refCleanup=null)}else if(typeof w=="function")try{w(null)}catch(C){ro(a,d,C)}else w.current=null}function J6(a){var d=a.type,w=a.memoizedProps,j=a.stateNode;try{e:switch(d){case"button":case"input":case"select":case"textarea":w.autoFocus&&j.focus();break e;case"img":w.src?j.src=w.src:w.srcSet&&(j.srcset=w.srcSet)}}catch(C){ro(a,a.return,C)}}function WM(a,d,w){try{var j=a.stateNode;DU(j,a.type,w,d),j[Ca]=d}catch(C){ro(a,a.return,C)}}function mL(a){return a.tag===5||a.tag===3||a.tag===26||a.tag===27&&Yp(a.type)||a.tag===4}function ZM(a){e:for(;;){for(;a.sibling===null;){if(a.return===null||mL(a.return))return null;a=a.return}for(a.sibling.return=a.return,a=a.sibling;a.tag!==5&&a.tag!==6&&a.tag!==18;){if(a.tag===27&&Yp(a.type)||a.flags&2||a.child===null||a.tag===4)continue e;a.child.return=a,a=a.child}if(!(a.flags&2))return a.stateNode}}function eT(a,d,w){var j=a.tag;if(j===5||j===6)a=a.stateNode,d?(w.nodeType===9?w.body:w.nodeName==="HTML"?w.ownerDocument.body:w).insertBefore(a,d):(d=w.nodeType===9?w.body:w.nodeName==="HTML"?w.ownerDocument.body:w,d.appendChild(a),w=w._reactRootContainer,w!=null||d.onclick!==null||(d.onclick=Yg));else if(j!==4&&(j===27&&Yp(a.type)&&(w=a.stateNode,d=null),a=a.child,a!==null))for(eT(a,d,w),a=a.sibling;a!==null;)eT(a,d,w),a=a.sibling}function p3(a,d,w){var j=a.tag;if(j===5||j===6)a=a.stateNode,d?w.insertBefore(a,d):w.appendChild(a);else if(j!==4&&(j===27&&Yp(a.type)&&(w=a.stateNode),a=a.child,a!==null))for(p3(a,d,w),a=a.sibling;a!==null;)p3(a,d,w),a=a.sibling}function vL(a){var d=a.stateNode,w=a.memoizedProps;try{for(var j=a.type,C=d.attributes;C.length;)d.removeAttributeNode(C[0]);oa(d,j,w),d[Zs]=a,d[Ca]=w}catch(D){ro(a,a.return,D)}}var Mb=!1,Cl=!1,H6=!1,nT=typeof WeakSet=="function"?WeakSet:Set,Mf=null;function hU(a,d){if(a=a.containerInfo,ST=Tf,a=GI(a),qA(a)){if("selectionStart"in a)var w={start:a.selectionStart,end:a.selectionEnd};else e:{w=(w=a.ownerDocument)&&w.defaultView||window;var j=w.getSelection&&w.getSelection();if(j&&j.rangeCount!==0){w=j.anchorNode;var C=j.anchorOffset,D=j.focusNode;j=j.focusOffset;try{w.nodeType,D.nodeType}catch{w=null;break e}var Q=0,de=-1,en=-1,Ln=0,nt=0,gt=a,Jn=null;n:for(;;){for(var Zn;gt!==w||C!==0&>.nodeType!==3||(de=Q+C),gt!==D||j!==0&>.nodeType!==3||(en=Q+j),gt.nodeType===3&&(Q+=gt.nodeValue.length),(Zn=gt.firstChild)!==null;)Jn=gt,gt=Zn;for(;;){if(gt===a)break n;if(Jn===w&&++Ln===C&&(de=Q),Jn===D&&++nt===j&&(en=Q),(Zn=gt.nextSibling)!==null)break;gt=Jn,Jn=gt.parentNode}gt=Zn}w=de===-1||en===-1?null:{start:de,end:en}}else w=null}w=w||{start:0,end:0}}else w=null;for(xT={focusedElem:a,selectionRange:w},Tf=!1,Mf=d;Mf!==null;)if(d=Mf,a=d.child,(d.subtreeFlags&1028)!==0&&a!==null)a.return=d,Mf=a;else for(;Mf!==null;){switch(d=Mf,D=d.alternate,a=d.flags,d.tag){case 0:if((a&4)!==0&&(a=d.updateQueue,a=a!==null?a.events:null,a!==null))for(w=0;w title"))),oa(D,j,w),D[Zs]=a,xl(D),j=D;break e;case"link":var Q=vP("link","href",C).get(j+(w.href||""));if(Q){for(var de=0;deco&&(Q=co,co=Tr,Tr=Q);var En=JI(de,Tr),on=JI(de,co);if(En&&on&&(Zn.rangeCount!==1||Zn.anchorNode!==En.node||Zn.anchorOffset!==En.offset||Zn.focusNode!==on.node||Zn.focusOffset!==on.offset)){var In=gt.createRange();In.setStart(En.node,En.offset),Zn.removeAllRanges(),Tr>co?(Zn.addRange(In),Zn.extend(on.node,on.offset)):(In.setEnd(on.node,on.offset),Zn.addRange(In))}}}}for(gt=[],Zn=de;Zn=Zn.parentNode;)Zn.nodeType===1&>.push({element:Zn,left:Zn.scrollLeft,top:Zn.scrollTop});for(typeof de.focus=="function"&&de.focus(),de=0;dew?32:w,Ae.T=null,w=sT,sT=null;var D=Xp,Q=lw;if(nf=0,H5=Xp=null,lw=0,(Ju&6)!==0)throw Error(M(331));var de=Ju;if(Ju|=4,CL(D.current),AL(D,D.current,Q,w),Ju=de,V6(0,!1),Sf&&typeof Sf.onPostCommitFiberRoot=="function")try{Sf.onPostCommitFiberRoot(ud,D)}catch{}return!0}finally{Ze.p=C,Ae.T=j,XL(a,d)}}function VL(a,d,w){d=sd(w,d),d=FM(a.stateNode,d,2),a=zp(a,d,2),a!==null&&(qg(a,2),Tb(a))}function ro(a,d,w){if(a.tag===3)VL(a,a,w);else for(;d!==null;){if(d.tag===3){VL(d,a,w);break}else if(d.tag===1){var j=d.stateNode;if(typeof d.type.getDerivedStateFromError=="function"||typeof j.componentDidCatch=="function"&&(Up===null||!Up.has(j))){a=sd(w,a),w=tL(2),j=zp(d,w,2),j!==null&&(iL(w,j,d,a),qg(j,2),Tb(j));break}}d=d.return}}function hT(a,d,w){var j=a.pingCache;if(j===null){j=a.pingCache=new gU;var C=new Set;j.set(d,C)}else C=j.get(d),C===void 0&&(C=new Set,j.set(d,C));C.has(w)||(rT=!0,C.add(w),a=yU.bind(null,a,d,w),d.then(a,a))}function yU(a,d,w){var j=a.pingCache;j!==null&&j.delete(d),a.pingedLanes|=a.suspendedLanes&w,a.warmLanes&=~w,Io===a&&(nu&w)===w&&(zs===4||zs===3&&(nu&62914560)===nu&&300>Rs()-qk?(Ju&2)===0&&G5(a,0):cT|=w,J5===nu&&(J5=0)),Tb(a)}function YL(a,d){d===0&&(d=h5()),a=o3(a,d),a!==null&&(qg(a,d),Tb(a))}function kU(a){var d=a.memoizedState,w=0;d!==null&&(w=d.retryLane),YL(a,w)}function jU(a,d){var w=0;switch(a.tag){case 31:case 13:var j=a.stateNode,C=a.memoizedState;C!==null&&(w=C.retryLane);break;case 19:j=a.stateNode;break;case 22:j=a.stateNode._retryCache;break;default:throw Error(M(314))}j!==null&&j.delete(d),YL(a,w)}function EU(a,d){return ch(a,d)}var Wk=null,U5=null,dT=!1,Zk=!1,bT=!1,Vp=0;function Tb(a){a!==U5&&a.next===null&&(U5===null?Wk=U5=a:U5=U5.next=a),Zk=!0,dT||(dT=!0,wT())}function V6(a,d){if(!bT&&Zk){bT=!0;do for(var w=!1,j=Wk;j!==null;){if(a!==0){var C=j.pendingLanes;if(C===0)var D=0;else{var Q=j.suspendedLanes,de=j.pingedLanes;D=(1<<31-xf(42|a)+1)-1,D&=C&~(Q&~de),D=D&201326741?D&201326741|1:D?D|2:0}D!==0&&(w=!0,ZL(j,D))}else D=nu,D=Hg(j,j===Io?D:0,j.cancelPendingCommit!==null||j.timeoutHandle!==-1),(D&3)===0||Gg(j,D)||(w=!0,ZL(j,D));j=j.next}while(w);bT=!1}}function SU(){QL()}function QL(){Zk=dT=!1;var a=0;Vp!==0&&_U()&&(a=Vp);for(var d=Rs(),w=null,j=Wk;j!==null;){var C=j.next,D=gT(j,d);D===0?(j.next=null,w===null?Wk=C:w.next=C,C===null&&(U5=w)):(w=j,(a!==0||(D&3)!==0)&&(Zk=!0)),j=C}nf!==0&&nf!==5||V6(a),Vp!==0&&(Vp=0)}function gT(a,d){for(var w=a.suspendedLanes,j=a.pingedLanes,C=a.expirationTimes,D=a.pendingLanes&-62914561;0de)break;var nt=en.transferSize,gt=en.initiatorType;nt&&oP(gt)&&(en=en.responseEnd,Q+=nt*(en"u"?null:document;function gP(a,d,w){var j=X5;if(j&&typeof d=="string"&&d){var C=_h(d);C='link[rel="'+a+'"][href="'+C+'"]',typeof w=="string"&&(C+='[crossorigin="'+w+'"]'),DT.has(C)||(DT.add(C),a={rel:a,crossOrigin:w,href:d},j.querySelector(C)===null&&(d=j.createElement("link"),oa(d,"link",a),xl(d),j.head.appendChild(d)))}}function HU(a){b0.D(a),gP("dns-prefetch",a,null)}function GU(a,d){b0.C(a,d),gP("preconnect",a,d)}function IT(a,d,w){b0.L(a,d,w);var j=X5;if(j&&a&&d){var C='link[rel="preload"][as="'+_h(d)+'"]';d==="image"&&w&&w.imageSrcSet?(C+='[imagesrcset="'+_h(w.imageSrcSet)+'"]',typeof w.imageSizes=="string"&&(C+='[imagesizes="'+_h(w.imageSizes)+'"]')):C+='[href="'+_h(a)+'"]';var D=C;switch(d){case"style":D=K5(a);break;case"script":D=V5(a)}E1.has(D)||(a=Z({rel:"preload",href:d==="image"&&w&&w.imageSrcSet?void 0:a,as:d},w),E1.set(D,a),j.querySelector(C)!==null||d==="style"&&j.querySelector(y3(D))||d==="script"&&j.querySelector(e9(D))||(d=j.createElement("link"),oa(d,"link",a),xl(d),j.head.appendChild(d)))}}function qU(a,d){b0.m(a,d);var w=X5;if(w&&a){var j=d&&typeof d.as=="string"?d.as:"script",C='link[rel="modulepreload"][as="'+_h(j)+'"][href="'+_h(a)+'"]',D=C;switch(j){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":D=V5(a)}if(!E1.has(D)&&(a=Z({rel:"modulepreload",href:a},d),E1.set(D,a),w.querySelector(C)===null)){switch(j){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(w.querySelector(e9(D)))return}j=w.createElement("link"),oa(j,"link",a),xl(j),w.head.appendChild(j)}}}function UU(a,d,w){b0.S(a,d,w);var j=X5;if(j&&a){var C=Tp(j).hoistableStyles,D=K5(a);d=d||"default";var Q=C.get(D);if(!Q){var de={loading:0,preload:null};if(Q=j.querySelector(y3(D)))de.loading=5;else{a=Z({rel:"stylesheet",href:a,"data-precedence":d},w),(w=E1.get(D))&&PT(a,w);var en=Q=j.createElement("link");xl(en),oa(en,"link",a),en._p=new Promise(function(Ln,nt){en.onload=Ln,en.onerror=nt}),en.addEventListener("load",function(){de.loading|=1}),en.addEventListener("error",function(){de.loading|=2}),de.loading|=4,cj(Q,d,j)}Q={type:"stylesheet",instance:Q,count:1,state:de},C.set(D,Q)}}}function XU(a,d){b0.X(a,d);var w=X5;if(w&&a){var j=Tp(w).hoistableScripts,C=V5(a),D=j.get(C);D||(D=w.querySelector(e9(C)),D||(a=Z({src:a,async:!0},d),(d=E1.get(C))&&$T(a,d),D=w.createElement("script"),xl(D),oa(D,"link",a),w.head.appendChild(D)),D={type:"script",instance:D,count:1,state:null},j.set(C,D))}}function _T(a,d){b0.M(a,d);var w=X5;if(w&&a){var j=Tp(w).hoistableScripts,C=V5(a),D=j.get(C);D||(D=w.querySelector(e9(C)),D||(a=Z({src:a,async:!0,type:"module"},d),(d=E1.get(C))&&$T(a,d),D=w.createElement("script"),xl(D),oa(D,"link",a),w.head.appendChild(D)),D={type:"script",instance:D,count:1,state:null},j.set(C,D))}}function wP(a,d,w,j){var C=(C=hi.current)?rj(C):null;if(!C)throw Error(M(446));switch(a){case"meta":case"title":return null;case"style":return typeof w.precedence=="string"&&typeof w.href=="string"?(d=K5(w.href),w=Tp(C).hoistableStyles,j=w.get(d),j||(j={type:"style",instance:null,count:0,state:null},w.set(d,j)),j):{type:"void",instance:null,count:0,state:null};case"link":if(w.rel==="stylesheet"&&typeof w.href=="string"&&typeof w.precedence=="string"){a=K5(w.href);var D=Tp(C).hoistableStyles,Q=D.get(a);if(Q||(C=C.ownerDocument||C,Q={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},D.set(a,Q),(D=C.querySelector(y3(a)))&&!D._p&&(Q.instance=D,Q.state.loading=5),E1.has(a)||(w={rel:"preload",as:"style",href:w.href,crossOrigin:w.crossOrigin,integrity:w.integrity,media:w.media,hrefLang:w.hrefLang,referrerPolicy:w.referrerPolicy},E1.set(a,w),D||LT(C,a,w,Q.state))),d&&j===null)throw Error(M(528,""));return Q}if(d&&j!==null)throw Error(M(529,""));return null;case"script":return d=w.async,w=w.src,typeof w=="string"&&d&&typeof d!="function"&&typeof d!="symbol"?(d=V5(w),w=Tp(C).hoistableScripts,j=w.get(d),j||(j={type:"script",instance:null,count:0,state:null},w.set(d,j)),j):{type:"void",instance:null,count:0,state:null};default:throw Error(M(444,a))}}function K5(a){return'href="'+_h(a)+'"'}function y3(a){return'link[rel="stylesheet"]['+a+"]"}function pP(a){return Z({},a,{"data-precedence":a.precedence,precedence:null})}function LT(a,d,w,j){a.querySelector('link[rel="preload"][as="style"]['+d+"]")?j.loading=1:(d=a.createElement("link"),j.preload=d,d.addEventListener("load",function(){return j.loading|=1}),d.addEventListener("error",function(){return j.loading|=2}),oa(d,"link",w),xl(d),a.head.appendChild(d))}function V5(a){return'[src="'+_h(a)+'"]'}function e9(a){return"script[async]"+a}function mP(a,d,w){if(d.count++,d.instance===null)switch(d.type){case"style":var j=a.querySelector('style[data-href~="'+_h(w.href)+'"]');if(j)return d.instance=j,xl(j),j;var C=Z({},w,{"data-href":w.href,"data-precedence":w.precedence,href:null,precedence:null});return j=(a.ownerDocument||a).createElement("style"),xl(j),oa(j,"style",C),cj(j,w.precedence,a),d.instance=j;case"stylesheet":C=K5(w.href);var D=a.querySelector(y3(C));if(D)return d.state.loading|=4,d.instance=D,xl(D),D;j=pP(w),(C=E1.get(C))&&PT(j,C),D=(a.ownerDocument||a).createElement("link"),xl(D);var Q=D;return Q._p=new Promise(function(de,en){Q.onload=de,Q.onerror=en}),oa(D,"link",j),d.state.loading|=4,cj(D,w.precedence,a),d.instance=D;case"script":return D=V5(w.src),(C=a.querySelector(e9(D)))?(d.instance=C,xl(C),C):(j=w,(C=E1.get(D))&&(j=Z({},w),$T(j,C)),a=a.ownerDocument||a,C=a.createElement("script"),xl(C),oa(C,"link",j),a.head.appendChild(C),d.instance=C);case"void":return null;default:throw Error(M(443,d.type))}else d.type==="stylesheet"&&(d.state.loading&4)===0&&(j=d.instance,d.state.loading|=4,cj(j,w.precedence,a));return d.instance}function cj(a,d,w){for(var j=w.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),C=j.length?j[j.length-1]:null,D=C,Q=0;Q title"):null)}function KU(a,d,w){if(w===1||d.itemProp!=null)return!1;switch(a){case"meta":case"title":return!0;case"style":if(typeof d.precedence!="string"||typeof d.href!="string"||d.href==="")break;return!0;case"link":if(typeof d.rel!="string"||typeof d.href!="string"||d.href===""||d.onLoad||d.onError)break;return d.rel==="stylesheet"?(a=d.disabled,typeof d.precedence=="string"&&a==null):!0;case"script":if(d.async&&typeof d.async!="function"&&typeof d.async!="symbol"&&!d.onLoad&&!d.onError&&d.src&&typeof d.src=="string")return!0}return!1}function kP(a){return!(a.type==="stylesheet"&&(a.state.loading&3)===0)}function VU(a,d,w,j){if(w.type==="stylesheet"&&(typeof j.media!="string"||matchMedia(j.media).matches!==!1)&&(w.state.loading&4)===0){if(w.instance===null){var C=K5(j.href),D=d.querySelector(y3(C));if(D){d=D._p,d!==null&&typeof d=="object"&&typeof d.then=="function"&&(a.count++,a=n9.bind(a),d.then(a,a)),w.state.loading|=4,w.instance=D,xl(D);return}D=d.ownerDocument||d,j=pP(j),(C=E1.get(C))&&PT(j,C),D=D.createElement("link"),xl(D);var Q=D;Q._p=new Promise(function(de,en){Q.onload=de,Q.onerror=en}),oa(D,"link",j),w.instance=D}a.stylesheets===null&&(a.stylesheets=new Map),a.stylesheets.set(w,d),(d=w.state.preload)&&(w.state.loading&3)===0&&(a.count++,w=n9.bind(a),d.addEventListener("load",w),d.addEventListener("error",w))}}var RT=0;function YU(a,d){return a.stylesheets&&a.count===0&&k3(a,a.stylesheets),0RT?50:800)+d);return a.unsuspend=w,function(){a.unsuspend=null,clearTimeout(j),clearTimeout(C)}}:null}function n9(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)k3(this,this.stylesheets);else if(this.unsuspend){var a=this.unsuspend;this.unsuspend=null,a()}}}var t9=null;function k3(a,d){a.stylesheets=null,a.unsuspend!==null&&(a.count++,t9=new Map,d.forEach(i9,a),t9=null,n9.call(a))}function i9(a,d){if(!(d.state.loading&4)){var w=t9.get(a);if(w)var j=w.get(null);else{w=new Map,t9.set(a,w);for(var C=a.querySelectorAll("link[data-precedence],style[data-precedence]"),D=0;D"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(g)}catch(E){console.error(E)}}return g(),O7e.exports=ozn(),O7e.exports}var lzn=szn();function Ta(g){if(typeof g=="string"||typeof g=="number")return""+g;let E="";if(Array.isArray(g))for(let x=0,M;x{}};function Oue(){for(var g=0,E=arguments.length,x={},M;g=0&&(M=x.slice(N+1),x=x.slice(0,N)),x&&!E.hasOwnProperty(x))throw new Error("unknown type: "+x);return{type:x,name:M}})}aue.prototype=Oue.prototype={constructor:aue,on:function(g,E){var x=this._,M=azn(g+"",x),N,P=-1,k=M.length;if(arguments.length<2){for(;++P0)for(var x=new Array(N),M=0,N,P;M=0&&(E=g.slice(0,x))!=="xmlns"&&(g=g.slice(x+1)),Uhn.hasOwnProperty(E)?{space:Uhn[E],local:g}:g}function dzn(g){return function(){var E=this.ownerDocument,x=this.namespaceURI;return x===Z7e&&E.documentElement.namespaceURI===Z7e?E.createElement(g):E.createElementNS(x,g)}}function bzn(g){return function(){return this.ownerDocument.createElementNS(g.space,g.local)}}function sdn(g){var E=Nue(g);return(E.local?bzn:dzn)(E)}function gzn(){}function gke(g){return g==null?gzn:function(){return this.querySelector(g)}}function wzn(g){typeof g!="function"&&(g=gke(g));for(var E=this._groups,x=E.length,M=new Array(x),N=0;N=ae&&(ae=Pe+1);!(Be=Oe[ae])&&++ae=0;)(k=M[N])&&(P&&k.compareDocumentPosition(P)^4&&P.parentNode.insertBefore(k,P),P=k);return this}function Fzn(g){g||(g=Jzn);function E(Z,W){return Z&&W?g(Z.__data__,W.__data__):!Z-!W}for(var x=this._groups,M=x.length,N=new Array(M),P=0;PE?1:g>=E?0:NaN}function Hzn(){var g=arguments[0];return arguments[0]=this,g.apply(null,arguments),this}function Gzn(){return Array.from(this)}function qzn(){for(var g=this._groups,E=0,x=g.length;E1?this.each((E==null?tFn:typeof E=="function"?rFn:iFn)(g,E,x??"")):hI(this.node(),g)}function hI(g,E){return g.style.getPropertyValue(E)||ddn(g).getComputedStyle(g,null).getPropertyValue(E)}function uFn(g){return function(){delete this[g]}}function oFn(g,E){return function(){this[g]=E}}function sFn(g,E){return function(){var x=E.apply(this,arguments);x==null?delete this[g]:this[g]=x}}function lFn(g,E){return arguments.length>1?this.each((E==null?uFn:typeof E=="function"?sFn:oFn)(g,E)):this.node()[g]}function bdn(g){return g.trim().split(/^|\s+/)}function wke(g){return g.classList||new gdn(g)}function gdn(g){this._node=g,this._names=bdn(g.getAttribute("class")||"")}gdn.prototype={add:function(g){var E=this._names.indexOf(g);E<0&&(this._names.push(g),this._node.setAttribute("class",this._names.join(" ")))},remove:function(g){var E=this._names.indexOf(g);E>=0&&(this._names.splice(E,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(g){return this._names.indexOf(g)>=0}};function wdn(g,E){for(var x=wke(g),M=-1,N=E.length;++M=0&&(x=E.slice(M+1),E=E.slice(0,M)),{type:E,name:x}})}function $Fn(g){return function(){var E=this.__on;if(E){for(var x=0,M=-1,N=E.length,P;x()=>g;function eke(g,{sourceEvent:E,subject:x,target:M,identifier:N,active:P,x:k,y:H,dx:U,dy:G,dispatch:ie}){Object.defineProperties(this,{type:{value:g,enumerable:!0,configurable:!0},sourceEvent:{value:E,enumerable:!0,configurable:!0},subject:{value:x,enumerable:!0,configurable:!0},target:{value:M,enumerable:!0,configurable:!0},identifier:{value:N,enumerable:!0,configurable:!0},active:{value:P,enumerable:!0,configurable:!0},x:{value:k,enumerable:!0,configurable:!0},y:{value:H,enumerable:!0,configurable:!0},dx:{value:U,enumerable:!0,configurable:!0},dy:{value:G,enumerable:!0,configurable:!0},_:{value:ie}})}eke.prototype.on=function(){var g=this._.on.apply(this._,arguments);return g===this._?this:g};function XFn(g){return!g.ctrlKey&&!g.button}function KFn(){return this.parentNode}function VFn(g,E){return E??{x:g.x,y:g.y}}function YFn(){return navigator.maxTouchPoints||"ontouchstart"in this}function jdn(){var g=XFn,E=KFn,x=VFn,M=YFn,N={},P=Oue("start","drag","end"),k=0,H,U,G,ie,Z=0;function W(Me){Me.on("mousedown.drag",se).filter(M).on("touchstart.drag",Oe).on("touchmove.drag",ge,UFn).on("touchend.drag touchcancel.drag",Pe).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function se(Me,Be){if(!(ie||!g.call(this,Me,Be))){var rn=ae(this,E.call(this,Me,Be),Me,Be,"mouse");rn&&($g(Me.view).on("mousemove.drag",le,zG).on("mouseup.drag",ee,zG),ydn(Me.view),_7e(Me),G=!1,H=Me.clientX,U=Me.clientY,rn("start",Me))}}function le(Me){if(fI(Me),!G){var Be=Me.clientX-H,rn=Me.clientY-U;G=Be*Be+rn*rn>Z}N.mouse("drag",Me)}function ee(Me){$g(Me.view).on("mousemove.drag mouseup.drag",null),kdn(Me.view,G),fI(Me),N.mouse("end",Me)}function Oe(Me,Be){if(g.call(this,Me,Be)){var rn=Me.changedTouches,ln=E.call(this,Me,Be),xn=rn.length,hn,wt;for(hn=0;hn>8&15|E>>4&240,E>>4&15|E&240,(E&15)<<4|E&15,1):x===8?Qce(E>>24&255,E>>16&255,E>>8&255,(E&255)/255):x===4?Qce(E>>12&15|E>>8&240,E>>8&15|E>>4&240,E>>4&15|E&240,((E&15)<<4|E&15)/255):null):(E=WFn.exec(g))?new kb(E[1],E[2],E[3],1):(E=ZFn.exec(g))?new kb(E[1]*255/100,E[2]*255/100,E[3]*255/100,1):(E=eJn.exec(g))?Qce(E[1],E[2],E[3],E[4]):(E=nJn.exec(g))?Qce(E[1]*255/100,E[2]*255/100,E[3]*255/100,E[4]):(E=tJn.exec(g))?Zhn(E[1],E[2]/100,E[3]/100,1):(E=iJn.exec(g))?Zhn(E[1],E[2]/100,E[3]/100,E[4]):Xhn.hasOwnProperty(g)?Yhn(Xhn[g]):g==="transparent"?new kb(NaN,NaN,NaN,0):null}function Yhn(g){return new kb(g>>16&255,g>>8&255,g&255,1)}function Qce(g,E,x,M){return M<=0&&(g=E=x=NaN),new kb(g,E,x,M)}function uJn(g){return g instanceof eq||(g=EA(g)),g?(g=g.rgb(),new kb(g.r,g.g,g.b,g.opacity)):new kb}function nke(g,E,x,M){return arguments.length===1?uJn(g):new kb(g,E,x,M??1)}function kb(g,E,x,M){this.r=+g,this.g=+E,this.b=+x,this.opacity=+M}pke(kb,nke,Edn(eq,{brighter(g){return g=g==null?mue:Math.pow(mue,g),new kb(this.r*g,this.g*g,this.b*g,this.opacity)},darker(g){return g=g==null?FG:Math.pow(FG,g),new kb(this.r*g,this.g*g,this.b*g,this.opacity)},rgb(){return this},clamp(){return new kb(kA(this.r),kA(this.g),kA(this.b),vue(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Qhn,formatHex:Qhn,formatHex8:oJn,formatRgb:Whn,toString:Whn}));function Qhn(){return`#${yA(this.r)}${yA(this.g)}${yA(this.b)}`}function oJn(){return`#${yA(this.r)}${yA(this.g)}${yA(this.b)}${yA((isNaN(this.opacity)?1:this.opacity)*255)}`}function Whn(){const g=vue(this.opacity);return`${g===1?"rgb(":"rgba("}${kA(this.r)}, ${kA(this.g)}, ${kA(this.b)}${g===1?")":`, ${g})`}`}function vue(g){return isNaN(g)?1:Math.max(0,Math.min(1,g))}function kA(g){return Math.max(0,Math.min(255,Math.round(g)||0))}function yA(g){return g=kA(g),(g<16?"0":"")+g.toString(16)}function Zhn(g,E,x,M){return M<=0?g=E=x=NaN:x<=0||x>=1?g=E=NaN:E<=0&&(g=NaN),new Xm(g,E,x,M)}function Sdn(g){if(g instanceof Xm)return new Xm(g.h,g.s,g.l,g.opacity);if(g instanceof eq||(g=EA(g)),!g)return new Xm;if(g instanceof Xm)return g;g=g.rgb();var E=g.r/255,x=g.g/255,M=g.b/255,N=Math.min(E,x,M),P=Math.max(E,x,M),k=NaN,H=P-N,U=(P+N)/2;return H?(E===P?k=(x-M)/H+(x0&&U<1?0:k,new Xm(k,H,U,g.opacity)}function sJn(g,E,x,M){return arguments.length===1?Sdn(g):new Xm(g,E,x,M??1)}function Xm(g,E,x,M){this.h=+g,this.s=+E,this.l=+x,this.opacity=+M}pke(Xm,sJn,Edn(eq,{brighter(g){return g=g==null?mue:Math.pow(mue,g),new Xm(this.h,this.s,this.l*g,this.opacity)},darker(g){return g=g==null?FG:Math.pow(FG,g),new Xm(this.h,this.s,this.l*g,this.opacity)},rgb(){var g=this.h%360+(this.h<0)*360,E=isNaN(g)||isNaN(this.s)?0:this.s,x=this.l,M=x+(x<.5?x:1-x)*E,N=2*x-M;return new kb(L7e(g>=240?g-240:g+120,N,M),L7e(g,N,M),L7e(g<120?g+240:g-120,N,M),this.opacity)},clamp(){return new Xm(e1n(this.h),Wce(this.s),Wce(this.l),vue(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const g=vue(this.opacity);return`${g===1?"hsl(":"hsla("}${e1n(this.h)}, ${Wce(this.s)*100}%, ${Wce(this.l)*100}%${g===1?")":`, ${g})`}`}}));function e1n(g){return g=(g||0)%360,g<0?g+360:g}function Wce(g){return Math.max(0,Math.min(1,g||0))}function L7e(g,E,x){return(g<60?E+(x-E)*g/60:g<180?x:g<240?E+(x-E)*(240-g)/60:E)*255}const mke=g=>()=>g;function lJn(g,E){return function(x){return g+x*E}}function fJn(g,E,x){return g=Math.pow(g,x),E=Math.pow(E,x)-g,x=1/x,function(M){return Math.pow(g+M*E,x)}}function aJn(g){return(g=+g)==1?xdn:function(E,x){return x-E?fJn(E,x,g):mke(isNaN(E)?x:E)}}function xdn(g,E){var x=E-g;return x?lJn(g,x):mke(isNaN(g)?E:g)}const yue=(function g(E){var x=aJn(E);function M(N,P){var k=x((N=nke(N)).r,(P=nke(P)).r),H=x(N.g,P.g),U=x(N.b,P.b),G=xdn(N.opacity,P.opacity);return function(ie){return N.r=k(ie),N.g=H(ie),N.b=U(ie),N.opacity=G(ie),N+""}}return M.gamma=g,M})(1);function hJn(g,E){E||(E=[]);var x=g?Math.min(E.length,g.length):0,M=E.slice(),N;return function(P){for(N=0;Nx&&(P=E.slice(x,P),H[k]?H[k]+=P:H[++k]=P),(M=M[0])===(N=N[0])?H[k]?H[k]+=N:H[++k]=N:(H[++k]=null,U.push({i:k,x:r5(M,N)})),x=P7e.lastIndex;return x180?ie+=360:ie-G>180&&(G+=360),W.push({i:Z.push(N(Z)+"rotate(",null,M)-2,x:r5(G,ie)})):ie&&Z.push(N(Z)+"rotate("+ie+M)}function H(G,ie,Z,W){G!==ie?W.push({i:Z.push(N(Z)+"skewX(",null,M)-2,x:r5(G,ie)}):ie&&Z.push(N(Z)+"skewX("+ie+M)}function U(G,ie,Z,W,se,le){if(G!==Z||ie!==W){var ee=se.push(N(se)+"scale(",null,",",null,")");le.push({i:ee-4,x:r5(G,Z)},{i:ee-2,x:r5(ie,W)})}else(Z!==1||W!==1)&&se.push(N(se)+"scale("+Z+","+W+")")}return function(G,ie){var Z=[],W=[];return G=g(G),ie=g(ie),P(G.translateX,G.translateY,ie.translateX,ie.translateY,Z,W),k(G.rotate,ie.rotate,Z,W),H(G.skewX,ie.skewX,Z,W),U(G.scaleX,G.scaleY,ie.scaleX,ie.scaleY,Z,W),G=ie=null,function(se){for(var le=-1,ee=W.length,Oe;++le=0&&g._call.call(void 0,E),g=g._next;--dI}function i1n(){SA=(jue=HG.now())+Due,dI=LG=0;try{MJn()}finally{dI=0,CJn(),SA=0}}function TJn(){var g=HG.now(),E=g-jue;E>Cdn&&(Due-=E,jue=g)}function CJn(){for(var g,E=kue,x,M=1/0;E;)E._call?(M>E._time&&(M=E._time),g=E,E=E._next):(x=E._next,E._next=null,E=g?g._next=x:kue=x);PG=g,rke(M)}function rke(g){if(!dI){LG&&(LG=clearTimeout(LG));var E=g-SA;E>24?(g<1/0&&(LG=setTimeout(i1n,g-HG.now()-Due)),DG&&(DG=clearInterval(DG))):(DG||(jue=HG.now(),DG=setInterval(TJn,Cdn)),dI=1,Odn(i1n))}}function r1n(g,E,x){var M=new Eue;return E=E==null?0:+E,M.restart(N=>{M.stop(),g(N+E)},E,x),M}var OJn=Oue("start","end","cancel","interrupt"),NJn=[],Ddn=0,c1n=1,cke=2,due=3,u1n=4,uke=5,bue=6;function Iue(g,E,x,M,N,P){var k=g.__transition;if(!k)g.__transition={};else if(x in k)return;DJn(g,x,{name:E,index:M,group:N,on:OJn,tween:NJn,time:P.time,delay:P.delay,duration:P.duration,ease:P.ease,timer:null,state:Ddn})}function yke(g,E){var x=Qm(g,E);if(x.state>Ddn)throw new Error("too late; already scheduled");return x}function s5(g,E){var x=Qm(g,E);if(x.state>due)throw new Error("too late; already running");return x}function Qm(g,E){var x=g.__transition;if(!x||!(x=x[E]))throw new Error("transition not found");return x}function DJn(g,E,x){var M=g.__transition,N;M[E]=x,x.timer=Ndn(P,0,x.time);function P(G){x.state=c1n,x.timer.restart(k,x.delay,x.time),x.delay<=G&&k(G-x.delay)}function k(G){var ie,Z,W,se;if(x.state!==c1n)return U();for(ie in M)if(se=M[ie],se.name===x.name){if(se.state===due)return r1n(k);se.state===u1n?(se.state=bue,se.timer.stop(),se.on.call("interrupt",g,g.__data__,se.index,se.group),delete M[ie]):+iecke&&M.state=0&&(E=E.slice(0,x)),!E||E==="start"})}function sHn(g,E,x){var M,N,P=oHn(E)?yke:s5;return function(){var k=P(this,g),H=k.on;H!==M&&(N=(M=H).copy()).on(E,x),k.on=N}}function lHn(g,E){var x=this._id;return arguments.length<2?Qm(this.node(),x).on.on(g):this.each(sHn(x,g,E))}function fHn(g){return function(){var E=this.parentNode;for(var x in this.__transition)if(+x!==g)return;E&&E.removeChild(this)}}function aHn(){return this.on("end.remove",fHn(this._id))}function hHn(g){var E=this._name,x=this._id;typeof g!="function"&&(g=gke(g));for(var M=this._groups,N=M.length,P=new Array(N),k=0;k()=>g;function $Hn(g,{sourceEvent:E,target:x,transform:M,dispatch:N}){Object.defineProperties(this,{type:{value:g,enumerable:!0,configurable:!0},sourceEvent:{value:E,enumerable:!0,configurable:!0},target:{value:x,enumerable:!0,configurable:!0},transform:{value:M,enumerable:!0,configurable:!0},_:{value:N}})}function e6(g,E,x){this.k=g,this.x=E,this.y=x}e6.prototype={constructor:e6,scale:function(g){return g===1?this:new e6(this.k*g,this.x,this.y)},translate:function(g,E){return g===0&E===0?this:new e6(this.k,this.x+this.k*g,this.y+this.k*E)},apply:function(g){return[g[0]*this.k+this.x,g[1]*this.k+this.y]},applyX:function(g){return g*this.k+this.x},applyY:function(g){return g*this.k+this.y},invert:function(g){return[(g[0]-this.x)/this.k,(g[1]-this.y)/this.k]},invertX:function(g){return(g-this.x)/this.k},invertY:function(g){return(g-this.y)/this.k},rescaleX:function(g){return g.copy().domain(g.range().map(this.invertX,this).map(g.invert,g))},rescaleY:function(g){return g.copy().domain(g.range().map(this.invertY,this).map(g.invert,g))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var _ue=new e6(1,0,0);Pdn.prototype=e6.prototype;function Pdn(g){for(;!g.__zoom;)if(!(g=g.parentNode))return _ue;return g.__zoom}function $7e(g){g.stopImmediatePropagation()}function IG(g){g.preventDefault(),g.stopImmediatePropagation()}function RHn(g){return(!g.ctrlKey||g.type==="wheel")&&!g.button}function BHn(){var g=this;return g instanceof SVGElement?(g=g.ownerSVGElement||g,g.hasAttribute("viewBox")?(g=g.viewBox.baseVal,[[g.x,g.y],[g.x+g.width,g.y+g.height]]):[[0,0],[g.width.baseVal.value,g.height.baseVal.value]]):[[0,0],[g.clientWidth,g.clientHeight]]}function o1n(){return this.__zoom||_ue}function zHn(g){return-g.deltaY*(g.deltaMode===1?.05:g.deltaMode?1:.002)*(g.ctrlKey?10:1)}function FHn(){return navigator.maxTouchPoints||"ontouchstart"in this}function JHn(g,E,x){var M=g.invertX(E[0][0])-x[0][0],N=g.invertX(E[1][0])-x[1][0],P=g.invertY(E[0][1])-x[0][1],k=g.invertY(E[1][1])-x[1][1];return g.translate(N>M?(M+N)/2:Math.min(0,M)||Math.max(0,N),k>P?(P+k)/2:Math.min(0,P)||Math.max(0,k))}function $dn(){var g=RHn,E=BHn,x=JHn,M=zHn,N=FHn,P=[0,1/0],k=[[-1/0,-1/0],[1/0,1/0]],H=250,U=hue,G=Oue("start","zoom","end"),ie,Z,W,se=500,le=150,ee=0,Oe=10;function ge(Fe){Fe.property("__zoom",o1n).on("wheel.zoom",xn,{passive:!1}).on("mousedown.zoom",hn).on("dblclick.zoom",wt).filter(N).on("touchstart.zoom",Cn).on("touchmove.zoom",Qn).on("touchend.zoom touchcancel.zoom",Y).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}ge.transform=function(Fe,mn,Ae,Ze){var sn=Fe.selection?Fe.selection():Fe;sn.property("__zoom",o1n),Fe!==sn?Be(Fe,mn,Ae,Ze):sn.interrupt().each(function(){rn(this,arguments).event(Ze).start().zoom(null,typeof mn=="function"?mn.apply(this,arguments):mn).end()})},ge.scaleBy=function(Fe,mn,Ae,Ze){ge.scaleTo(Fe,function(){var sn=this.__zoom.k,Sn=typeof mn=="function"?mn.apply(this,arguments):mn;return sn*Sn},Ae,Ze)},ge.scaleTo=function(Fe,mn,Ae,Ze){ge.transform(Fe,function(){var sn=E.apply(this,arguments),Sn=this.__zoom,kn=Ae==null?Me(sn):typeof Ae=="function"?Ae.apply(this,arguments):Ae,xe=Sn.invert(kn),un=typeof mn=="function"?mn.apply(this,arguments):mn;return x(ae(Pe(Sn,un),kn,xe),sn,k)},Ae,Ze)},ge.translateBy=function(Fe,mn,Ae,Ze){ge.transform(Fe,function(){return x(this.__zoom.translate(typeof mn=="function"?mn.apply(this,arguments):mn,typeof Ae=="function"?Ae.apply(this,arguments):Ae),E.apply(this,arguments),k)},null,Ze)},ge.translateTo=function(Fe,mn,Ae,Ze,sn){ge.transform(Fe,function(){var Sn=E.apply(this,arguments),kn=this.__zoom,xe=Ze==null?Me(Sn):typeof Ze=="function"?Ze.apply(this,arguments):Ze;return x(_ue.translate(xe[0],xe[1]).scale(kn.k).translate(typeof mn=="function"?-mn.apply(this,arguments):-mn,typeof Ae=="function"?-Ae.apply(this,arguments):-Ae),Sn,k)},Ze,sn)};function Pe(Fe,mn){return mn=Math.max(P[0],Math.min(P[1],mn)),mn===Fe.k?Fe:new e6(mn,Fe.x,Fe.y)}function ae(Fe,mn,Ae){var Ze=mn[0]-Ae[0]*Fe.k,sn=mn[1]-Ae[1]*Fe.k;return Ze===Fe.x&&sn===Fe.y?Fe:new e6(Fe.k,Ze,sn)}function Me(Fe){return[(+Fe[0][0]+ +Fe[1][0])/2,(+Fe[0][1]+ +Fe[1][1])/2]}function Be(Fe,mn,Ae,Ze){Fe.on("start.zoom",function(){rn(this,arguments).event(Ze).start()}).on("interrupt.zoom end.zoom",function(){rn(this,arguments).event(Ze).end()}).tween("zoom",function(){var sn=this,Sn=arguments,kn=rn(sn,Sn).event(Ze),xe=E.apply(sn,Sn),un=Ae==null?Me(xe):typeof Ae=="function"?Ae.apply(sn,Sn):Ae,rt=Math.max(xe[1][0]-xe[0][0],xe[1][1]-xe[0][1]),lt=sn.__zoom,Bt=typeof mn=="function"?mn.apply(sn,Sn):mn,hi=U(lt.invert(un).concat(rt/lt.k),Bt.invert(un).concat(rt/Bt.k));return function(Gt){if(Gt===1)Gt=Bt;else{var At=hi(Gt),st=rt/At[2];Gt=new e6(st,un[0]-At[0]*st,un[1]-At[1]*st)}kn.zoom(null,Gt)}})}function rn(Fe,mn,Ae){return!Ae&&Fe.__zooming||new ln(Fe,mn)}function ln(Fe,mn){this.that=Fe,this.args=mn,this.active=0,this.sourceEvent=null,this.extent=E.apply(Fe,mn),this.taps=0}ln.prototype={event:function(Fe){return Fe&&(this.sourceEvent=Fe),this},start:function(){return++this.active===1&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(Fe,mn){return this.mouse&&Fe!=="mouse"&&(this.mouse[1]=mn.invert(this.mouse[0])),this.touch0&&Fe!=="touch"&&(this.touch0[1]=mn.invert(this.touch0[0])),this.touch1&&Fe!=="touch"&&(this.touch1[1]=mn.invert(this.touch1[0])),this.that.__zoom=mn,this.emit("zoom"),this},end:function(){return--this.active===0&&(delete this.that.__zooming,this.emit("end")),this},emit:function(Fe){var mn=$g(this.that).datum();G.call(Fe,this.that,new $Hn(Fe,{sourceEvent:this.sourceEvent,target:ge,transform:this.that.__zoom,dispatch:G}),mn)}};function xn(Fe,...mn){if(!g.apply(this,arguments))return;var Ae=rn(this,mn).event(Fe),Ze=this.__zoom,sn=Math.max(P[0],Math.min(P[1],Ze.k*Math.pow(2,M.apply(this,arguments)))),Sn=Um(Fe);if(Ae.wheel)(Ae.mouse[0][0]!==Sn[0]||Ae.mouse[0][1]!==Sn[1])&&(Ae.mouse[1]=Ze.invert(Ae.mouse[0]=Sn)),clearTimeout(Ae.wheel);else{if(Ze.k===sn)return;Ae.mouse=[Sn,Ze.invert(Sn)],gue(this),Ae.start()}IG(Fe),Ae.wheel=setTimeout(kn,le),Ae.zoom("mouse",x(ae(Pe(Ze,sn),Ae.mouse[0],Ae.mouse[1]),Ae.extent,k));function kn(){Ae.wheel=null,Ae.end()}}function hn(Fe,...mn){if(W||!g.apply(this,arguments))return;var Ae=Fe.currentTarget,Ze=rn(this,mn,!0).event(Fe),sn=$g(Fe.view).on("mousemove.zoom",un,!0).on("mouseup.zoom",rt,!0),Sn=Um(Fe,Ae),kn=Fe.clientX,xe=Fe.clientY;ydn(Fe.view),$7e(Fe),Ze.mouse=[Sn,this.__zoom.invert(Sn)],gue(this),Ze.start();function un(lt){if(IG(lt),!Ze.moved){var Bt=lt.clientX-kn,hi=lt.clientY-xe;Ze.moved=Bt*Bt+hi*hi>ee}Ze.event(lt).zoom("mouse",x(ae(Ze.that.__zoom,Ze.mouse[0]=Um(lt,Ae),Ze.mouse[1]),Ze.extent,k))}function rt(lt){sn.on("mousemove.zoom mouseup.zoom",null),kdn(lt.view,Ze.moved),IG(lt),Ze.event(lt).end()}}function wt(Fe,...mn){if(g.apply(this,arguments)){var Ae=this.__zoom,Ze=Um(Fe.changedTouches?Fe.changedTouches[0]:Fe,this),sn=Ae.invert(Ze),Sn=Ae.k*(Fe.shiftKey?.5:2),kn=x(ae(Pe(Ae,Sn),Ze,sn),E.apply(this,mn),k);IG(Fe),H>0?$g(this).transition().duration(H).call(Be,kn,Ze,Fe):$g(this).call(ge.transform,kn,Ze,Fe)}}function Cn(Fe,...mn){if(g.apply(this,arguments)){var Ae=Fe.touches,Ze=Ae.length,sn=rn(this,mn,Fe.changedTouches.length===Ze).event(Fe),Sn,kn,xe,un;for($7e(Fe),kn=0;kn"[React Flow]: Seems like you have not used zustand provider as an ancestor. Help: https://reactflow.dev/error#001",error002:()=>"It looks like you've created a new nodeTypes or edgeTypes object. If this wasn't on purpose please define the nodeTypes/edgeTypes outside of the component or memoize them.",error003:g=>`Node type "${g}" not found. Using fallback type "default".`,error004:()=>"The React Flow parent container needs a width and a height to render the graph.",error005:()=>"Only child nodes can use a parent extent.",error006:()=>"Can't create edge. An edge needs a source and a target.",error007:g=>`The old edge with id=${g} does not exist.`,error009:g=>`Marker type "${g}" doesn't exist.`,error008:(g,{id:E,sourceHandle:x,targetHandle:M})=>`Couldn't create edge for ${g} handle id: "${g==="source"?x:M}", edge id: ${E}.`,error010:()=>"Handle: No node id found. Make sure to only use a Handle inside a custom Node.",error011:g=>`Edge type "${g}" not found. Using fallback type "default".`,error012:g=>`Node with id "${g}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`,error013:(g="react")=>`It seems that you haven't loaded the styles. Please import '@xyflow/${g}/dist/style.css' or base.css to make sure everything is working properly.`,error014:()=>"useNodeConnections: No node ID found. Call useNodeConnections inside a custom Node or provide a node ID.",error015:()=>"It seems that you are trying to drag a node that is not initialized. Please use onNodesChange as explained in the docs."},GG=[[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY],[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY]],Rdn=["Enter"," ","Escape"],Bdn={"node.a11yDescription.default":"Press enter or space to select a node. Press delete to remove it and escape to cancel.","node.a11yDescription.keyboardDisabled":"Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.","node.a11yDescription.ariaLiveMessage":({direction:g,x:E,y:x})=>`Moved selected node ${g}. New position, x: ${E}, y: ${x}`,"edge.a11yDescription.default":"Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.","controls.ariaLabel":"Control Panel","controls.zoomIn.ariaLabel":"Zoom In","controls.zoomOut.ariaLabel":"Zoom Out","controls.fitView.ariaLabel":"Fit View","controls.interactive.ariaLabel":"Toggle Interactivity","minimap.ariaLabel":"Mini Map","handle.ariaLabel":"Handle"};var bI;(function(g){g.Strict="strict",g.Loose="loose"})(bI||(bI={}));var jA;(function(g){g.Free="free",g.Vertical="vertical",g.Horizontal="horizontal"})(jA||(jA={}));var qG;(function(g){g.Partial="partial",g.Full="full"})(qG||(qG={}));const zdn={inProgress:!1,isValid:null,from:null,fromHandle:null,fromPosition:null,fromNode:null,to:null,toHandle:null,toPosition:null,toNode:null,pointer:null};var W7;(function(g){g.Bezier="default",g.Straight="straight",g.Step="step",g.SmoothStep="smoothstep",g.SimpleBezier="simplebezier"})(W7||(W7={}));var UG;(function(g){g.Arrow="arrow",g.ArrowClosed="arrowclosed"})(UG||(UG={}));var cr;(function(g){g.Left="left",g.Top="top",g.Right="right",g.Bottom="bottom"})(cr||(cr={}));const s1n={[cr.Left]:cr.Right,[cr.Right]:cr.Left,[cr.Top]:cr.Bottom,[cr.Bottom]:cr.Top};function Fdn(g){return g===null?null:g?"valid":"invalid"}const Jdn=g=>"id"in g&&"source"in g&&"target"in g,HHn=g=>"id"in g&&"position"in g&&!("source"in g)&&!("target"in g),jke=g=>"id"in g&&"internals"in g&&!("source"in g)&&!("target"in g),nq=(g,E=[0,0])=>{const{width:x,height:M}=i6(g),N=g.origin??E,P=x*N[0],k=M*N[1];return{x:g.position.x-P,y:g.position.y-k}},GHn=(g,E={nodeOrigin:[0,0]})=>{if(g.length===0)return{x:0,y:0,width:0,height:0};const x=g.reduce((M,N)=>{const P=typeof N=="string";let k=!E.nodeLookup&&!P?N:void 0;E.nodeLookup&&(k=P?E.nodeLookup.get(N):jke(N)?N:E.nodeLookup.get(N.id));const H=k?Sue(k,E.nodeOrigin):{x:0,y:0,x2:0,y2:0};return Lue(M,H)},{x:1/0,y:1/0,x2:-1/0,y2:-1/0});return Pue(x)},tq=(g,E={})=>{let x={x:1/0,y:1/0,x2:-1/0,y2:-1/0},M=!1;return g.forEach(N=>{(E.filter===void 0||E.filter(N))&&(x=Lue(x,Sue(N)),M=!0)}),M?Pue(x):{x:0,y:0,width:0,height:0}},Eke=(g,E,[x,M,N]=[0,0,1],P=!1,k=!1)=>{const H={...rq(E,[x,M,N]),width:E.width/N,height:E.height/N},U=[];for(const G of g.values()){const{measured:ie,selectable:Z=!0,hidden:W=!1}=G;if(k&&!Z||W)continue;const se=ie.width??G.width??G.initialWidth??null,le=ie.height??G.height??G.initialHeight??null,ee=XG(H,wI(G)),Oe=(se??0)*(le??0),ge=P&&ee>0;(!G.internals.handleBounds||ge||ee>=Oe||G.dragging)&&U.push(G)}return U},qHn=(g,E)=>{const x=new Set;return g.forEach(M=>{x.add(M.id)}),E.filter(M=>x.has(M.source)||x.has(M.target))};function UHn(g,E){const x=new Map,M=E?.nodes?new Set(E.nodes.map(N=>N.id)):null;return g.forEach(N=>{N.measured.width&&N.measured.height&&(E?.includeHiddenNodes||!N.hidden)&&(!M||M.has(N.id))&&x.set(N.id,N)}),x}async function XHn({nodes:g,width:E,height:x,panZoom:M,minZoom:N,maxZoom:P},k){if(g.size===0)return Promise.resolve(!0);const H=UHn(g,k),U=tq(H),G=Ske(U,E,x,k?.minZoom??N,k?.maxZoom??P,k?.padding??.1);return await M.setViewport(G,{duration:k?.duration,ease:k?.ease,interpolate:k?.interpolate}),Promise.resolve(!0)}function Hdn({nodeId:g,nextPosition:E,nodeLookup:x,nodeOrigin:M=[0,0],nodeExtent:N,onError:P}){const k=x.get(g),H=k.parentId?x.get(k.parentId):void 0,{x:U,y:G}=H?H.internals.positionAbsolute:{x:0,y:0},ie=k.origin??M;let Z=k.extent||N;if(k.extent==="parent"&&!k.expandParent)if(!H)P?.("005",u5.error005());else{const se=H.measured.width,le=H.measured.height;se&&le&&(Z=[[U,G],[U+se,G+le]])}else H&&pI(k.extent)&&(Z=[[k.extent[0][0]+U,k.extent[0][1]+G],[k.extent[1][0]+U,k.extent[1][1]+G]]);const W=pI(Z)?xA(E,Z,k.measured):E;return(k.measured.width===void 0||k.measured.height===void 0)&&P?.("015",u5.error015()),{position:{x:W.x-U+(k.measured.width??0)*ie[0],y:W.y-G+(k.measured.height??0)*ie[1]},positionAbsolute:W}}async function KHn({nodesToRemove:g=[],edgesToRemove:E=[],nodes:x,edges:M,onBeforeDelete:N}){const P=new Set(g.map(W=>W.id)),k=[];for(const W of x){if(W.deletable===!1)continue;const se=P.has(W.id),le=!se&&W.parentId&&k.find(ee=>ee.id===W.parentId);(se||le)&&k.push(W)}const H=new Set(E.map(W=>W.id)),U=M.filter(W=>W.deletable!==!1),ie=qHn(k,U);for(const W of U)H.has(W.id)&&!ie.find(le=>le.id===W.id)&&ie.push(W);if(!N)return{edges:ie,nodes:k};const Z=await N({nodes:k,edges:ie});return typeof Z=="boolean"?Z?{edges:ie,nodes:k}:{edges:[],nodes:[]}:Z}const gI=(g,E=0,x=1)=>Math.min(Math.max(g,E),x),xA=(g={x:0,y:0},E,x)=>({x:gI(g.x,E[0][0],E[1][0]-(x?.width??0)),y:gI(g.y,E[0][1],E[1][1]-(x?.height??0))});function Gdn(g,E,x){const{width:M,height:N}=i6(x),{x:P,y:k}=x.internals.positionAbsolute;return xA(g,[[P,k],[P+M,k+N]],E)}const l1n=(g,E,x)=>gx?-gI(Math.abs(g-x),1,E)/E:0,qdn=(g,E,x=15,M=40)=>{const N=l1n(g.x,M,E.width-M)*x,P=l1n(g.y,M,E.height-M)*x;return[N,P]},Lue=(g,E)=>({x:Math.min(g.x,E.x),y:Math.min(g.y,E.y),x2:Math.max(g.x2,E.x2),y2:Math.max(g.y2,E.y2)}),oke=({x:g,y:E,width:x,height:M})=>({x:g,y:E,x2:g+x,y2:E+M}),Pue=({x:g,y:E,x2:x,y2:M})=>({x:g,y:E,width:x-g,height:M-E}),wI=(g,E=[0,0])=>{const{x,y:M}=jke(g)?g.internals.positionAbsolute:nq(g,E);return{x,y:M,width:g.measured?.width??g.width??g.initialWidth??0,height:g.measured?.height??g.height??g.initialHeight??0}},Sue=(g,E=[0,0])=>{const{x,y:M}=jke(g)?g.internals.positionAbsolute:nq(g,E);return{x,y:M,x2:x+(g.measured?.width??g.width??g.initialWidth??0),y2:M+(g.measured?.height??g.height??g.initialHeight??0)}},Udn=(g,E)=>Pue(Lue(oke(g),oke(E))),XG=(g,E)=>{const x=Math.max(0,Math.min(g.x+g.width,E.x+E.width)-Math.max(g.x,E.x)),M=Math.max(0,Math.min(g.y+g.height,E.y+E.height)-Math.max(g.y,E.y));return Math.ceil(x*M)},f1n=g=>Km(g.width)&&Km(g.height)&&Km(g.x)&&Km(g.y),Km=g=>!isNaN(g)&&isFinite(g),VHn=(g,E)=>{},iq=(g,E=[1,1])=>({x:E[0]*Math.round(g.x/E[0]),y:E[1]*Math.round(g.y/E[1])}),rq=({x:g,y:E},[x,M,N],P=!1,k=[1,1])=>{const H={x:(g-x)/N,y:(E-M)/N};return P?iq(H,k):H},xue=({x:g,y:E},[x,M,N])=>({x:g*N+x,y:E*N+M});function uI(g,E){if(typeof g=="number")return Math.floor((E-E/(1+g))*.5);if(typeof g=="string"&&g.endsWith("px")){const x=parseFloat(g);if(!Number.isNaN(x))return Math.floor(x)}if(typeof g=="string"&&g.endsWith("%")){const x=parseFloat(g);if(!Number.isNaN(x))return Math.floor(E*x*.01)}return console.error(`[React Flow] The padding value "${g}" is invalid. Please provide a number or a string with a valid unit (px or %).`),0}function YHn(g,E,x){if(typeof g=="string"||typeof g=="number"){const M=uI(g,x),N=uI(g,E);return{top:M,right:N,bottom:M,left:N,x:N*2,y:M*2}}if(typeof g=="object"){const M=uI(g.top??g.y??0,x),N=uI(g.bottom??g.y??0,x),P=uI(g.left??g.x??0,E),k=uI(g.right??g.x??0,E);return{top:M,right:k,bottom:N,left:P,x:P+k,y:M+N}}return{top:0,right:0,bottom:0,left:0,x:0,y:0}}function QHn(g,E,x,M,N,P){const{x:k,y:H}=xue(g,[E,x,M]),{x:U,y:G}=xue({x:g.x+g.width,y:g.y+g.height},[E,x,M]),ie=N-U,Z=P-G;return{left:Math.floor(k),top:Math.floor(H),right:Math.floor(ie),bottom:Math.floor(Z)}}const Ske=(g,E,x,M,N,P)=>{const k=YHn(P,E,x),H=(E-k.x)/g.width,U=(x-k.y)/g.height,G=Math.min(H,U),ie=gI(G,M,N),Z=g.x+g.width/2,W=g.y+g.height/2,se=E/2-Z*ie,le=x/2-W*ie,ee=QHn(g,se,le,ie,E,x),Oe={left:Math.min(ee.left-k.left,0),top:Math.min(ee.top-k.top,0),right:Math.min(ee.right-k.right,0),bottom:Math.min(ee.bottom-k.bottom,0)};return{x:se-Oe.left+Oe.right,y:le-Oe.top+Oe.bottom,zoom:ie}},KG=()=>typeof navigator<"u"&&navigator?.userAgent?.indexOf("Mac")>=0;function pI(g){return g!=null&&g!=="parent"}function i6(g){return{width:g.measured?.width??g.width??g.initialWidth??0,height:g.measured?.height??g.height??g.initialHeight??0}}function Xdn(g){return(g.measured?.width??g.width??g.initialWidth)!==void 0&&(g.measured?.height??g.height??g.initialHeight)!==void 0}function Kdn(g,E={width:0,height:0},x,M,N){const P={...g},k=M.get(x);if(k){const H=k.origin||N;P.x+=k.internals.positionAbsolute.x-(E.width??0)*H[0],P.y+=k.internals.positionAbsolute.y-(E.height??0)*H[1]}return P}function a1n(g,E){if(g.size!==E.size)return!1;for(const x of g)if(!E.has(x))return!1;return!0}function WHn(){let g,E;return{promise:new Promise((M,N)=>{g=M,E=N}),resolve:g,reject:E}}function ZHn(g){return{...Bdn,...g||{}}}function BG(g,{snapGrid:E=[0,0],snapToGrid:x=!1,transform:M,containerBounds:N}){const{x:P,y:k}=Vm(g),H=rq({x:P-(N?.left??0),y:k-(N?.top??0)},M),{x:U,y:G}=x?iq(H,E):H;return{xSnapped:U,ySnapped:G,...H}}const xke=g=>({width:g.offsetWidth,height:g.offsetHeight}),Vdn=g=>g?.getRootNode?.()||window?.document,eGn=["INPUT","SELECT","TEXTAREA"];function Ydn(g){const E=g.composedPath?.()?.[0]||g.target;return E?.nodeType!==1?!1:eGn.includes(E.nodeName)||E.hasAttribute("contenteditable")||!!E.closest(".nokey")}const Qdn=g=>"clientX"in g,Vm=(g,E)=>{const x=Qdn(g),M=x?g.clientX:g.touches?.[0].clientX,N=x?g.clientY:g.touches?.[0].clientY;return{x:M-(E?.left??0),y:N-(E?.top??0)}},h1n=(g,E,x,M,N)=>{const P=E.querySelectorAll(`.${g}`);return!P||!P.length?null:Array.from(P).map(k=>{const H=k.getBoundingClientRect();return{id:k.getAttribute("data-handleid"),type:g,nodeId:N,position:k.getAttribute("data-handlepos"),x:(H.left-x.left)/M,y:(H.top-x.top)/M,...xke(k)}})};function Wdn({sourceX:g,sourceY:E,targetX:x,targetY:M,sourceControlX:N,sourceControlY:P,targetControlX:k,targetControlY:H}){const U=g*.125+N*.375+k*.375+x*.125,G=E*.125+P*.375+H*.375+M*.125,ie=Math.abs(U-g),Z=Math.abs(G-E);return[U,G,ie,Z]}function nue(g,E){return g>=0?.5*g:E*25*Math.sqrt(-g)}function d1n({pos:g,x1:E,y1:x,x2:M,y2:N,c:P}){switch(g){case cr.Left:return[E-nue(E-M,P),x];case cr.Right:return[E+nue(M-E,P),x];case cr.Top:return[E,x-nue(x-N,P)];case cr.Bottom:return[E,x+nue(N-x,P)]}}function Zdn({sourceX:g,sourceY:E,sourcePosition:x=cr.Bottom,targetX:M,targetY:N,targetPosition:P=cr.Top,curvature:k=.25}){const[H,U]=d1n({pos:x,x1:g,y1:E,x2:M,y2:N,c:k}),[G,ie]=d1n({pos:P,x1:M,y1:N,x2:g,y2:E,c:k}),[Z,W,se,le]=Wdn({sourceX:g,sourceY:E,targetX:M,targetY:N,sourceControlX:H,sourceControlY:U,targetControlX:G,targetControlY:ie});return[`M${g},${E} C${H},${U} ${G},${ie} ${M},${N}`,Z,W,se,le]}function e0n({sourceX:g,sourceY:E,targetX:x,targetY:M}){const N=Math.abs(x-g)/2,P=x0}const iGn=({source:g,sourceHandle:E,target:x,targetHandle:M})=>`xy-edge__${g}${E||""}-${x}${M||""}`,rGn=(g,E)=>E.some(x=>x.source===g.source&&x.target===g.target&&(x.sourceHandle===g.sourceHandle||!x.sourceHandle&&!g.sourceHandle)&&(x.targetHandle===g.targetHandle||!x.targetHandle&&!g.targetHandle)),cGn=(g,E,x={})=>{if(!g.source||!g.target)return E;const M=x.getEdgeId||iGn;let N;return Jdn(g)?N={...g}:N={...g,id:M(g)},rGn(N,E)?E:(N.sourceHandle===null&&delete N.sourceHandle,N.targetHandle===null&&delete N.targetHandle,E.concat(N))};function n0n({sourceX:g,sourceY:E,targetX:x,targetY:M}){const[N,P,k,H]=e0n({sourceX:g,sourceY:E,targetX:x,targetY:M});return[`M ${g},${E}L ${x},${M}`,N,P,k,H]}const b1n={[cr.Left]:{x:-1,y:0},[cr.Right]:{x:1,y:0},[cr.Top]:{x:0,y:-1},[cr.Bottom]:{x:0,y:1}},uGn=({source:g,sourcePosition:E=cr.Bottom,target:x})=>E===cr.Left||E===cr.Right?g.xMath.sqrt(Math.pow(E.x-g.x,2)+Math.pow(E.y-g.y,2));function oGn({source:g,sourcePosition:E=cr.Bottom,target:x,targetPosition:M=cr.Top,center:N,offset:P,stepPosition:k}){const H=b1n[E],U=b1n[M],G={x:g.x+H.x*P,y:g.y+H.y*P},ie={x:x.x+U.x*P,y:x.y+U.y*P},Z=uGn({source:G,sourcePosition:E,target:ie}),W=Z.x!==0?"x":"y",se=Z[W];let le=[],ee,Oe;const ge={x:0,y:0},Pe={x:0,y:0},[,,ae,Me]=e0n({sourceX:g.x,sourceY:g.y,targetX:x.x,targetY:x.y});if(H[W]*U[W]===-1){W==="x"?(ee=N.x??G.x+(ie.x-G.x)*k,Oe=N.y??(G.y+ie.y)/2):(ee=N.x??(G.x+ie.x)/2,Oe=N.y??G.y+(ie.y-G.y)*k);const xn=[{x:ee,y:G.y},{x:ee,y:ie.y}],hn=[{x:G.x,y:Oe},{x:ie.x,y:Oe}];H[W]===se?le=W==="x"?xn:hn:le=W==="x"?hn:xn}else{const xn=[{x:G.x,y:ie.y}],hn=[{x:ie.x,y:G.y}];if(W==="x"?le=H.x===se?hn:xn:le=H.y===se?xn:hn,E===M){const Fe=Math.abs(g[W]-x[W]);if(Fe<=P){const mn=Math.min(P-1,P-Fe);H[W]===se?ge[W]=(G[W]>g[W]?-1:1)*mn:Pe[W]=(ie[W]>x[W]?-1:1)*mn}}if(E!==M){const Fe=W==="x"?"y":"x",mn=H[W]===U[Fe],Ae=G[Fe]>ie[Fe],Ze=G[Fe]=Y?(ee=(wt.x+Cn.x)/2,Oe=le[0].y):(ee=le[0].x,Oe=(wt.y+Cn.y)/2)}const Be={x:G.x+ge.x,y:G.y+ge.y},rn={x:ie.x+Pe.x,y:ie.y+Pe.y};return[[g,...Be.x!==le[0].x||Be.y!==le[0].y?[Be]:[],...le,...rn.x!==le[le.length-1].x||rn.y!==le[le.length-1].y?[rn]:[],x],ee,Oe,ae,Me]}function sGn(g,E,x,M){const N=Math.min(g1n(g,E)/2,g1n(E,x)/2,M),{x:P,y:k}=E;if(g.x===P&&P===x.x||g.y===k&&k===x.y)return`L${P} ${k}`;if(g.y===k){const G=g.xx.id===E):g[0])||null}function ske(g,E){return g?typeof g=="string"?g:`${E?`${E}__`:""}${Object.keys(g).sort().map(M=>`${M}=${g[M]}`).join("&")}`:""}function fGn(g,{id:E,defaultColor:x,defaultMarkerStart:M,defaultMarkerEnd:N}){const P=new Set;return g.reduce((k,H)=>([H.markerStart||M,H.markerEnd||N].forEach(U=>{if(U&&typeof U=="object"){const G=ske(U,E);P.has(G)||(k.push({id:G,color:U.color||x,...U}),P.add(G))}}),k),[]).sort((k,H)=>k.id.localeCompare(H.id))}const t0n=1e3,aGn=10,Ake={nodeOrigin:[0,0],nodeExtent:GG,elevateNodesOnSelect:!0,zIndexMode:"basic",defaults:{}},hGn={...Ake,checkEquality:!0};function Mke(g,E){const x={...g};for(const M in E)E[M]!==void 0&&(x[M]=E[M]);return x}function dGn(g,E,x){const M=Mke(Ake,x);for(const N of g.values())if(N.parentId)Cke(N,g,E,M);else{const P=nq(N,M.nodeOrigin),k=pI(N.extent)?N.extent:M.nodeExtent,H=xA(P,k,i6(N));N.internals.positionAbsolute=H}}function bGn(g,E){if(!g.handles)return g.measured?E?.internals.handleBounds:void 0;const x=[],M=[];for(const N of g.handles){const P={id:N.id,width:N.width??1,height:N.height??1,nodeId:g.id,x:N.x,y:N.y,position:N.position,type:N.type};N.type==="source"?x.push(P):N.type==="target"&&M.push(P)}return{source:x,target:M}}function Tke(g){return g==="manual"}function lke(g,E,x,M={}){const N=Mke(hGn,M),P={i:0},k=new Map(E),H=N?.elevateNodesOnSelect&&!Tke(N.zIndexMode)?t0n:0;let U=g.length>0,G=!1;E.clear(),x.clear();for(const ie of g){let Z=k.get(ie.id);if(N.checkEquality&&ie===Z?.internals.userNode)E.set(ie.id,Z);else{const W=nq(ie,N.nodeOrigin),se=pI(ie.extent)?ie.extent:N.nodeExtent,le=xA(W,se,i6(ie));Z={...N.defaults,...ie,measured:{width:ie.measured?.width,height:ie.measured?.height},internals:{positionAbsolute:le,handleBounds:bGn(ie,Z),z:i0n(ie,H,N.zIndexMode),userNode:ie}},E.set(ie.id,Z)}(Z.measured===void 0||Z.measured.width===void 0||Z.measured.height===void 0)&&!Z.hidden&&(U=!1),ie.parentId&&Cke(Z,E,x,M,P),G||=ie.selected??!1}return{nodesInitialized:U,hasSelectedNodes:G}}function gGn(g,E){if(!g.parentId)return;const x=E.get(g.parentId);x?x.set(g.id,g):E.set(g.parentId,new Map([[g.id,g]]))}function Cke(g,E,x,M,N){const{elevateNodesOnSelect:P,nodeOrigin:k,nodeExtent:H,zIndexMode:U}=Mke(Ake,M),G=g.parentId,ie=E.get(G);if(!ie){console.warn(`Parent node ${G} not found. Please make sure that parent nodes are in front of their child nodes in the nodes array.`);return}gGn(g,x),N&&!ie.parentId&&ie.internals.rootParentIndex===void 0&&U==="auto"&&(ie.internals.rootParentIndex=++N.i,ie.internals.z=ie.internals.z+N.i*aGn),N&&ie.internals.rootParentIndex!==void 0&&(N.i=ie.internals.rootParentIndex);const Z=P&&!Tke(U)?t0n:0,{x:W,y:se,z:le}=wGn(g,ie,k,H,Z,U),{positionAbsolute:ee}=g.internals,Oe=W!==ee.x||se!==ee.y;(Oe||le!==g.internals.z)&&E.set(g.id,{...g,internals:{...g.internals,positionAbsolute:Oe?{x:W,y:se}:ee,z:le}})}function i0n(g,E,x){const M=Km(g.zIndex)?g.zIndex:0;return Tke(x)?M:M+(g.selected?E:0)}function wGn(g,E,x,M,N,P){const{x:k,y:H}=E.internals.positionAbsolute,U=i6(g),G=nq(g,x),ie=pI(g.extent)?xA(G,g.extent,U):G;let Z=xA({x:k+ie.x,y:H+ie.y},M,U);g.extent==="parent"&&(Z=Gdn(Z,U,E));const W=i0n(g,N,P),se=E.internals.z??0;return{x:Z.x,y:Z.y,z:se>=W?se+1:W}}function Oke(g,E,x,M=[0,0]){const N=[],P=new Map;for(const k of g){const H=E.get(k.parentId);if(!H)continue;const U=P.get(k.parentId)?.expandedRect??wI(H),G=Udn(U,k.rect);P.set(k.parentId,{expandedRect:G,parent:H})}return P.size>0&&P.forEach(({expandedRect:k,parent:H},U)=>{const G=H.internals.positionAbsolute,ie=i6(H),Z=H.origin??M,W=k.x0||se>0||Oe||ge)&&(N.push({id:U,type:"position",position:{x:H.position.x-W+Oe,y:H.position.y-se+ge}}),x.get(U)?.forEach(Pe=>{g.some(ae=>ae.id===Pe.id)||N.push({id:Pe.id,type:"position",position:{x:Pe.position.x+W,y:Pe.position.y+se}})})),(ie.width0){const se=Oke(W,E,x,N);G.push(...se)}return{changes:G,updatedInternals:U}}async function mGn({delta:g,panZoom:E,transform:x,translateExtent:M,width:N,height:P}){if(!E||!g.x&&!g.y)return Promise.resolve(!1);const k=await E.setViewportConstrained({x:x[0]+g.x,y:x[1]+g.y,zoom:x[2]},[[0,0],[N,P]],M),H=!!k&&(k.x!==x[0]||k.y!==x[1]||k.k!==x[2]);return Promise.resolve(H)}function v1n(g,E,x,M,N,P){let k=N;const H=M.get(k)||new Map;M.set(k,H.set(x,E)),k=`${N}-${g}`;const U=M.get(k)||new Map;if(M.set(k,U.set(x,E)),P){k=`${N}-${g}-${P}`;const G=M.get(k)||new Map;M.set(k,G.set(x,E))}}function r0n(g,E,x){g.clear(),E.clear();for(const M of x){const{source:N,target:P,sourceHandle:k=null,targetHandle:H=null}=M,U={edgeId:M.id,source:N,target:P,sourceHandle:k,targetHandle:H},G=`${N}-${k}--${P}-${H}`,ie=`${P}-${H}--${N}-${k}`;v1n("source",U,ie,g,N,k),v1n("target",U,G,g,P,H),E.set(M.id,M)}}function c0n(g,E){if(!g.parentId)return!1;const x=E.get(g.parentId);return x?x.selected?!0:c0n(x,E):!1}function y1n(g,E,x){let M=g;do{if(M?.matches?.(E))return!0;if(M===x)return!1;M=M?.parentElement}while(M);return!1}function vGn(g,E,x,M){const N=new Map;for(const[P,k]of g)if((k.selected||k.id===M)&&(!k.parentId||!c0n(k,g))&&(k.draggable||E&&typeof k.draggable>"u")){const H=g.get(P);H&&N.set(P,{id:P,position:H.position||{x:0,y:0},distance:{x:x.x-H.internals.positionAbsolute.x,y:x.y-H.internals.positionAbsolute.y},extent:H.extent,parentId:H.parentId,origin:H.origin,expandParent:H.expandParent,internals:{positionAbsolute:H.internals.positionAbsolute||{x:0,y:0}},measured:{width:H.measured.width??0,height:H.measured.height??0}})}return N}function R7e({nodeId:g,dragItems:E,nodeLookup:x,dragging:M=!0}){const N=[];for(const[k,H]of E){const U=x.get(k)?.internals.userNode;U&&N.push({...U,position:H.position,dragging:M})}if(!g)return[N[0],N];const P=x.get(g)?.internals.userNode;return[P?{...P,position:E.get(g)?.position||P.position,dragging:M}:N[0],N]}function yGn({dragItems:g,snapGrid:E,x,y:M}){const N=g.values().next().value;if(!N)return null;const P={x:x-N.distance.x,y:M-N.distance.y},k=iq(P,E);return{x:k.x-P.x,y:k.y-P.y}}function kGn({onNodeMouseDown:g,getStoreItems:E,onDragStart:x,onDrag:M,onDragStop:N}){let P={x:null,y:null},k=0,H=new Map,U=!1,G={x:0,y:0},ie=null,Z=!1,W=null,se=!1,le=!1,ee=null;function Oe({noDragClassName:Pe,handleSelector:ae,domNode:Me,isSelectable:Be,nodeId:rn,nodeClickDistance:ln=0}){W=$g(Me);function xn({x:Qn,y:Y}){const{nodeLookup:Fe,nodeExtent:mn,snapGrid:Ae,snapToGrid:Ze,nodeOrigin:sn,onNodeDrag:Sn,onSelectionDrag:kn,onError:xe,updateNodePositions:un}=E();P={x:Qn,y:Y};let rt=!1;const lt=H.size>1,Bt=lt&&mn?oke(tq(H)):null,hi=lt&&Ze?yGn({dragItems:H,snapGrid:Ae,x:Qn,y:Y}):null;for(const[Gt,At]of H){if(!Fe.has(Gt))continue;let st={x:Qn-At.distance.x,y:Y-At.distance.y};Ze&&(st=hi?{x:Math.round(st.x+hi.x),y:Math.round(st.y+hi.y)}:iq(st,Ae));let Wr=null;if(lt&&mn&&!At.extent&&Bt){const{positionAbsolute:mi}=At.internals,Fi=mi.x-Bt.x+mn[0][0],fu=mi.x+At.measured.width-Bt.x2+mn[1][0],Eu=mi.y-Bt.y+mn[0][1],Ws=mi.y+At.measured.height-Bt.y2+mn[1][1];Wr=[[Fi,Eu],[fu,Ws]]}const{position:Mr,positionAbsolute:ur}=Hdn({nodeId:Gt,nextPosition:st,nodeLookup:Fe,nodeExtent:Wr||mn,nodeOrigin:sn,onError:xe});rt=rt||At.position.x!==Mr.x||At.position.y!==Mr.y,At.position=Mr,At.internals.positionAbsolute=ur}if(le=le||rt,!!rt&&(un(H,!0),ee&&(M||Sn||!rn&&kn))){const[Gt,At]=R7e({nodeId:rn,dragItems:H,nodeLookup:Fe});M?.(ee,H,Gt,At),Sn?.(ee,Gt,At),rn||kn?.(ee,At)}}async function hn(){if(!ie)return;const{transform:Qn,panBy:Y,autoPanSpeed:Fe,autoPanOnNodeDrag:mn}=E();if(!mn){U=!1,cancelAnimationFrame(k);return}const[Ae,Ze]=qdn(G,ie,Fe);(Ae!==0||Ze!==0)&&(P.x=(P.x??0)-Ae/Qn[2],P.y=(P.y??0)-Ze/Qn[2],await Y({x:Ae,y:Ze})&&xn(P)),k=requestAnimationFrame(hn)}function wt(Qn){const{nodeLookup:Y,multiSelectionActive:Fe,nodesDraggable:mn,transform:Ae,snapGrid:Ze,snapToGrid:sn,selectNodesOnDrag:Sn,onNodeDragStart:kn,onSelectionDragStart:xe,unselectNodesAndEdges:un}=E();Z=!0,(!Sn||!Be)&&!Fe&&rn&&(Y.get(rn)?.selected||un()),Be&&Sn&&rn&&g?.(rn);const rt=BG(Qn.sourceEvent,{transform:Ae,snapGrid:Ze,snapToGrid:sn,containerBounds:ie});if(P=rt,H=vGn(Y,mn,rt,rn),H.size>0&&(x||kn||!rn&&xe)){const[lt,Bt]=R7e({nodeId:rn,dragItems:H,nodeLookup:Y});x?.(Qn.sourceEvent,H,lt,Bt),kn?.(Qn.sourceEvent,lt,Bt),rn||xe?.(Qn.sourceEvent,Bt)}}const Cn=jdn().clickDistance(ln).on("start",Qn=>{const{domNode:Y,nodeDragThreshold:Fe,transform:mn,snapGrid:Ae,snapToGrid:Ze}=E();ie=Y?.getBoundingClientRect()||null,se=!1,le=!1,ee=Qn.sourceEvent,Fe===0&&wt(Qn),P=BG(Qn.sourceEvent,{transform:mn,snapGrid:Ae,snapToGrid:Ze,containerBounds:ie}),G=Vm(Qn.sourceEvent,ie)}).on("drag",Qn=>{const{autoPanOnNodeDrag:Y,transform:Fe,snapGrid:mn,snapToGrid:Ae,nodeDragThreshold:Ze,nodeLookup:sn}=E(),Sn=BG(Qn.sourceEvent,{transform:Fe,snapGrid:mn,snapToGrid:Ae,containerBounds:ie});if(ee=Qn.sourceEvent,(Qn.sourceEvent.type==="touchmove"&&Qn.sourceEvent.touches.length>1||rn&&!sn.has(rn))&&(se=!0),!se){if(!U&&Y&&Z&&(U=!0,hn()),!Z){const kn=Vm(Qn.sourceEvent,ie),xe=kn.x-G.x,un=kn.y-G.y;Math.sqrt(xe*xe+un*un)>Ze&&wt(Qn)}(P.x!==Sn.xSnapped||P.y!==Sn.ySnapped)&&H&&Z&&(G=Vm(Qn.sourceEvent,ie),xn(Sn))}}).on("end",Qn=>{if(!(!Z||se)&&(U=!1,Z=!1,cancelAnimationFrame(k),H.size>0)){const{nodeLookup:Y,updateNodePositions:Fe,onNodeDragStop:mn,onSelectionDragStop:Ae}=E();if(le&&(Fe(H,!1),le=!1),N||mn||!rn&&Ae){const[Ze,sn]=R7e({nodeId:rn,dragItems:H,nodeLookup:Y,dragging:!1});N?.(Qn.sourceEvent,H,Ze,sn),mn?.(Qn.sourceEvent,Ze,sn),rn||Ae?.(Qn.sourceEvent,sn)}}}).filter(Qn=>{const Y=Qn.target;return!Qn.button&&(!Pe||!y1n(Y,`.${Pe}`,Me))&&(!ae||y1n(Y,ae,Me))});W.call(Cn)}function ge(){W?.on(".drag",null)}return{update:Oe,destroy:ge}}function jGn(g,E,x){const M=[],N={x:g.x-x,y:g.y-x,width:x*2,height:x*2};for(const P of E.values())XG(N,wI(P))>0&&M.push(P);return M}const EGn=250;function SGn(g,E,x,M){let N=[],P=1/0;const k=jGn(g,x,E+EGn);for(const H of k){const U=[...H.internals.handleBounds?.source??[],...H.internals.handleBounds?.target??[]];for(const G of U){if(M.nodeId===G.nodeId&&M.type===G.type&&M.id===G.id)continue;const{x:ie,y:Z}=AA(H,G,G.position,!0),W=Math.sqrt(Math.pow(ie-g.x,2)+Math.pow(Z-g.y,2));W>E||(W1){const H=M.type==="source"?"target":"source";return N.find(U=>U.type===H)??N[0]}return N[0]}function u0n(g,E,x,M,N,P=!1){const k=M.get(g);if(!k)return null;const H=N==="strict"?k.internals.handleBounds?.[E]:[...k.internals.handleBounds?.source??[],...k.internals.handleBounds?.target??[]],U=(x?H?.find(G=>G.id===x):H?.[0])??null;return U&&P?{...U,...AA(k,U,U.position,!0)}:U}function o0n(g,E){return g||(E?.classList.contains("target")?"target":E?.classList.contains("source")?"source":null)}function xGn(g,E){let x=null;return E?x=!0:g&&!E&&(x=!1),x}const s0n=()=>!0;function AGn(g,{connectionMode:E,connectionRadius:x,handleId:M,nodeId:N,edgeUpdaterType:P,isTarget:k,domNode:H,nodeLookup:U,lib:G,autoPanOnConnect:ie,flowId:Z,panBy:W,cancelConnection:se,onConnectStart:le,onConnect:ee,onConnectEnd:Oe,isValidConnection:ge=s0n,onReconnectEnd:Pe,updateConnection:ae,getTransform:Me,getFromHandle:Be,autoPanSpeed:rn,dragThreshold:ln=1,handleDomNode:xn}){const hn=Vdn(g.target);let wt=0,Cn;const{x:Qn,y:Y}=Vm(g),Fe=o0n(P,xn),mn=H?.getBoundingClientRect();let Ae=!1;if(!mn||!Fe)return;const Ze=u0n(N,Fe,M,U,E);if(!Ze)return;let sn=Vm(g,mn),Sn=!1,kn=null,xe=!1,un=null;function rt(){if(!ie||!mn)return;const[Mr,ur]=qdn(sn,mn,rn);W({x:Mr,y:ur}),wt=requestAnimationFrame(rt)}const lt={...Ze,nodeId:N,type:Fe,position:Ze.position},Bt=U.get(N);let Gt={inProgress:!0,isValid:null,from:AA(Bt,lt,cr.Left,!0),fromHandle:lt,fromPosition:lt.position,fromNode:Bt,to:sn,toHandle:null,toPosition:s1n[lt.position],toNode:null,pointer:sn};function At(){Ae=!0,ae(Gt),le?.(g,{nodeId:N,handleId:M,handleType:Fe})}ln===0&&At();function st(Mr){if(!Ae){const{x:Ws,y:Dh}=Vm(Mr),ef=Ws-Qn,ch=Dh-Y;if(!(ef*ef+ch*ch>ln*ln))return;At()}if(!Be()||!lt){Wr(Mr);return}const ur=Me();sn=Vm(Mr,mn),Cn=SGn(rq(sn,ur,!1,[1,1]),x,U,lt),Sn||(rt(),Sn=!0);const mi=l0n(Mr,{handle:Cn,connectionMode:E,fromNodeId:N,fromHandleId:M,fromType:k?"target":"source",isValidConnection:ge,doc:hn,lib:G,flowId:Z,nodeLookup:U});un=mi.handleDomNode,kn=mi.connection,xe=xGn(!!Cn,mi.isValid);const Fi=U.get(N),fu=Fi?AA(Fi,lt,cr.Left,!0):Gt.from,Eu={...Gt,from:fu,isValid:xe,to:mi.toHandle&&xe?xue({x:mi.toHandle.x,y:mi.toHandle.y},ur):sn,toHandle:mi.toHandle,toPosition:xe&&mi.toHandle?mi.toHandle.position:s1n[lt.position],toNode:mi.toHandle?U.get(mi.toHandle.nodeId):null,pointer:sn};ae(Eu),Gt=Eu}function Wr(Mr){if(!("touches"in Mr&&Mr.touches.length>0)){if(Ae){(Cn||un)&&kn&&xe&&ee?.(kn);const{inProgress:ur,...mi}=Gt,Fi={...mi,toPosition:Gt.toHandle?Gt.toPosition:null};Oe?.(Mr,Fi),P&&Pe?.(Mr,Fi)}se(),cancelAnimationFrame(wt),Sn=!1,xe=!1,kn=null,un=null,hn.removeEventListener("mousemove",st),hn.removeEventListener("mouseup",Wr),hn.removeEventListener("touchmove",st),hn.removeEventListener("touchend",Wr)}}hn.addEventListener("mousemove",st),hn.addEventListener("mouseup",Wr),hn.addEventListener("touchmove",st),hn.addEventListener("touchend",Wr)}function l0n(g,{handle:E,connectionMode:x,fromNodeId:M,fromHandleId:N,fromType:P,doc:k,lib:H,flowId:U,isValidConnection:G=s0n,nodeLookup:ie}){const Z=P==="target",W=E?k.querySelector(`.${H}-flow__handle[data-id="${U}-${E?.nodeId}-${E?.id}-${E?.type}"]`):null,{x:se,y:le}=Vm(g),ee=k.elementFromPoint(se,le),Oe=ee?.classList.contains(`${H}-flow__handle`)?ee:W,ge={handleDomNode:Oe,isValid:!1,connection:null,toHandle:null};if(Oe){const Pe=o0n(void 0,Oe),ae=Oe.getAttribute("data-nodeid"),Me=Oe.getAttribute("data-handleid"),Be=Oe.classList.contains("connectable"),rn=Oe.classList.contains("connectableend");if(!ae||!Pe)return ge;const ln={source:Z?ae:M,sourceHandle:Z?Me:N,target:Z?M:ae,targetHandle:Z?N:Me};ge.connection=ln;const hn=Be&&rn&&(x===bI.Strict?Z&&Pe==="source"||!Z&&Pe==="target":ae!==M||Me!==N);ge.isValid=hn&&G(ln),ge.toHandle=u0n(ae,Pe,Me,ie,x,!0)}return ge}const fke={onPointerDown:AGn,isValid:l0n};function MGn({domNode:g,panZoom:E,getTransform:x,getViewScale:M}){const N=$g(g);function P({translateExtent:H,width:U,height:G,zoomStep:ie=1,pannable:Z=!0,zoomable:W=!0,inversePan:se=!1}){const le=ae=>{if(ae.sourceEvent.type!=="wheel"||!E)return;const Me=x(),Be=ae.sourceEvent.ctrlKey&&KG()?10:1,rn=-ae.sourceEvent.deltaY*(ae.sourceEvent.deltaMode===1?.05:ae.sourceEvent.deltaMode?1:.002)*ie,ln=Me[2]*Math.pow(2,rn*Be);E.scaleTo(ln)};let ee=[0,0];const Oe=ae=>{(ae.sourceEvent.type==="mousedown"||ae.sourceEvent.type==="touchstart")&&(ee=[ae.sourceEvent.clientX??ae.sourceEvent.touches[0].clientX,ae.sourceEvent.clientY??ae.sourceEvent.touches[0].clientY])},ge=ae=>{const Me=x();if(ae.sourceEvent.type!=="mousemove"&&ae.sourceEvent.type!=="touchmove"||!E)return;const Be=[ae.sourceEvent.clientX??ae.sourceEvent.touches[0].clientX,ae.sourceEvent.clientY??ae.sourceEvent.touches[0].clientY],rn=[Be[0]-ee[0],Be[1]-ee[1]];ee=Be;const ln=M()*Math.max(Me[2],Math.log(Me[2]))*(se?-1:1),xn={x:Me[0]-rn[0]*ln,y:Me[1]-rn[1]*ln},hn=[[0,0],[U,G]];E.setViewportConstrained({x:xn.x,y:xn.y,zoom:Me[2]},hn,H)},Pe=$dn().on("start",Oe).on("zoom",Z?ge:null).on("zoom.wheel",W?le:null);N.call(Pe,{})}function k(){N.on("zoom",null)}return{update:P,destroy:k,pointer:Um}}const $ue=g=>({x:g.x,y:g.y,zoom:g.k}),B7e=({x:g,y:E,zoom:x})=>_ue.translate(g,E).scale(x),sI=(g,E)=>g.target.closest(`.${E}`),f0n=(g,E)=>E===2&&Array.isArray(g)&&g.includes(2),TGn=g=>((g*=2)<=1?g*g*g:(g-=2)*g*g+2)/2,z7e=(g,E=0,x=TGn,M=()=>{})=>{const N=typeof E=="number"&&E>0;return N||M(),N?g.transition().duration(E).ease(x).on("end",M):g},a0n=g=>{const E=g.ctrlKey&&KG()?10:1;return-g.deltaY*(g.deltaMode===1?.05:g.deltaMode?1:.002)*E};function CGn({zoomPanValues:g,noWheelClassName:E,d3Selection:x,d3Zoom:M,panOnScrollMode:N,panOnScrollSpeed:P,zoomOnPinch:k,onPanZoomStart:H,onPanZoom:U,onPanZoomEnd:G}){return ie=>{if(sI(ie,E))return ie.ctrlKey&&ie.preventDefault(),!1;ie.preventDefault(),ie.stopImmediatePropagation();const Z=x.property("__zoom").k||1;if(ie.ctrlKey&&k){const Oe=Um(ie),ge=a0n(ie),Pe=Z*Math.pow(2,ge);M.scaleTo(x,Pe,Oe,ie);return}const W=ie.deltaMode===1?20:1;let se=N===jA.Vertical?0:ie.deltaX*W,le=N===jA.Horizontal?0:ie.deltaY*W;!KG()&&ie.shiftKey&&N!==jA.Vertical&&(se=ie.deltaY*W,le=0),M.translateBy(x,-(se/Z)*P,-(le/Z)*P,{internal:!0});const ee=$ue(x.property("__zoom"));clearTimeout(g.panScrollTimeout),g.isPanScrolling?(U?.(ie,ee),g.panScrollTimeout=setTimeout(()=>{G?.(ie,ee),g.isPanScrolling=!1},150)):(g.isPanScrolling=!0,H?.(ie,ee))}}function OGn({noWheelClassName:g,preventScrolling:E,d3ZoomHandler:x}){return function(M,N){const P=M.type==="wheel",k=!E&&P&&!M.ctrlKey,H=sI(M,g);if(M.ctrlKey&&P&&H&&M.preventDefault(),k||H)return null;M.preventDefault(),x.call(this,M,N)}}function NGn({zoomPanValues:g,onDraggingChange:E,onPanZoomStart:x}){return M=>{if(M.sourceEvent?.internal)return;const N=$ue(M.transform);g.mouseButton=M.sourceEvent?.button||0,g.isZoomingOrPanning=!0,g.prevViewport=N,M.sourceEvent?.type==="mousedown"&&E(!0),x&&x?.(M.sourceEvent,N)}}function DGn({zoomPanValues:g,panOnDrag:E,onPaneContextMenu:x,onTransformChange:M,onPanZoom:N}){return P=>{g.usedRightMouseButton=!!(x&&f0n(E,g.mouseButton??0)),P.sourceEvent?.sync||M([P.transform.x,P.transform.y,P.transform.k]),N&&!P.sourceEvent?.internal&&N?.(P.sourceEvent,$ue(P.transform))}}function IGn({zoomPanValues:g,panOnDrag:E,panOnScroll:x,onDraggingChange:M,onPanZoomEnd:N,onPaneContextMenu:P}){return k=>{if(!k.sourceEvent?.internal&&(g.isZoomingOrPanning=!1,P&&f0n(E,g.mouseButton??0)&&!g.usedRightMouseButton&&k.sourceEvent&&P(k.sourceEvent),g.usedRightMouseButton=!1,M(!1),N)){const H=$ue(k.transform);g.prevViewport=H,clearTimeout(g.timerId),g.timerId=setTimeout(()=>{N?.(k.sourceEvent,H)},x?150:0)}}}function _Gn({zoomActivationKeyPressed:g,zoomOnScroll:E,zoomOnPinch:x,panOnDrag:M,panOnScroll:N,zoomOnDoubleClick:P,userSelectionActive:k,noWheelClassName:H,noPanClassName:U,lib:G,connectionInProgress:ie}){return Z=>{const W=g||E,se=x&&Z.ctrlKey,le=Z.type==="wheel";if(Z.button===1&&Z.type==="mousedown"&&(sI(Z,`${G}-flow__node`)||sI(Z,`${G}-flow__edge`)))return!0;if(!M&&!W&&!N&&!P&&!x||k||ie&&!le||sI(Z,H)&&le||sI(Z,U)&&(!le||N&&le&&!g)||!x&&Z.ctrlKey&&le)return!1;if(!x&&Z.type==="touchstart"&&Z.touches?.length>1)return Z.preventDefault(),!1;if(!W&&!N&&!se&&le||!M&&(Z.type==="mousedown"||Z.type==="touchstart")||Array.isArray(M)&&!M.includes(Z.button)&&Z.type==="mousedown")return!1;const ee=Array.isArray(M)&&M.includes(Z.button)||!Z.button||Z.button<=1;return(!Z.ctrlKey||le)&&ee}}function LGn({domNode:g,minZoom:E,maxZoom:x,translateExtent:M,viewport:N,onPanZoom:P,onPanZoomStart:k,onPanZoomEnd:H,onDraggingChange:U}){const G={isZoomingOrPanning:!1,usedRightMouseButton:!1,prevViewport:{},mouseButton:0,timerId:void 0,panScrollTimeout:void 0,isPanScrolling:!1},ie=g.getBoundingClientRect(),Z=$dn().scaleExtent([E,x]).translateExtent(M),W=$g(g).call(Z);Pe({x:N.x,y:N.y,zoom:gI(N.zoom,E,x)},[[0,0],[ie.width,ie.height]],M);const se=W.on("wheel.zoom"),le=W.on("dblclick.zoom");Z.wheelDelta(a0n);function ee(Cn,Qn){return W?new Promise(Y=>{Z?.interpolate(Qn?.interpolate==="linear"?RG:hue).transform(z7e(W,Qn?.duration,Qn?.ease,()=>Y(!0)),Cn)}):Promise.resolve(!1)}function Oe({noWheelClassName:Cn,noPanClassName:Qn,onPaneContextMenu:Y,userSelectionActive:Fe,panOnScroll:mn,panOnDrag:Ae,panOnScrollMode:Ze,panOnScrollSpeed:sn,preventScrolling:Sn,zoomOnPinch:kn,zoomOnScroll:xe,zoomOnDoubleClick:un,zoomActivationKeyPressed:rt,lib:lt,onTransformChange:Bt,connectionInProgress:hi,paneClickDistance:Gt,selectionOnDrag:At}){Fe&&!G.isZoomingOrPanning&&ge();const st=mn&&!rt&&!Fe;Z.clickDistance(At?1/0:!Km(Gt)||Gt<0?0:Gt);const Wr=st?CGn({zoomPanValues:G,noWheelClassName:Cn,d3Selection:W,d3Zoom:Z,panOnScrollMode:Ze,panOnScrollSpeed:sn,zoomOnPinch:kn,onPanZoomStart:k,onPanZoom:P,onPanZoomEnd:H}):OGn({noWheelClassName:Cn,preventScrolling:Sn,d3ZoomHandler:se});if(W.on("wheel.zoom",Wr,{passive:!1}),!Fe){const ur=NGn({zoomPanValues:G,onDraggingChange:U,onPanZoomStart:k});Z.on("start",ur);const mi=DGn({zoomPanValues:G,panOnDrag:Ae,onPaneContextMenu:!!Y,onPanZoom:P,onTransformChange:Bt});Z.on("zoom",mi);const Fi=IGn({zoomPanValues:G,panOnDrag:Ae,panOnScroll:mn,onPaneContextMenu:Y,onPanZoomEnd:H,onDraggingChange:U});Z.on("end",Fi)}const Mr=_Gn({zoomActivationKeyPressed:rt,panOnDrag:Ae,zoomOnScroll:xe,panOnScroll:mn,zoomOnDoubleClick:un,zoomOnPinch:kn,userSelectionActive:Fe,noPanClassName:Qn,noWheelClassName:Cn,lib:lt,connectionInProgress:hi});Z.filter(Mr),un?W.on("dblclick.zoom",le):W.on("dblclick.zoom",null)}function ge(){Z.on("zoom",null)}async function Pe(Cn,Qn,Y){const Fe=B7e(Cn),mn=Z?.constrain()(Fe,Qn,Y);return mn&&await ee(mn),new Promise(Ae=>Ae(mn))}async function ae(Cn,Qn){const Y=B7e(Cn);return await ee(Y,Qn),new Promise(Fe=>Fe(Y))}function Me(Cn){if(W){const Qn=B7e(Cn),Y=W.property("__zoom");(Y.k!==Cn.zoom||Y.x!==Cn.x||Y.y!==Cn.y)&&Z?.transform(W,Qn,null,{sync:!0})}}function Be(){const Cn=W?Pdn(W.node()):{x:0,y:0,k:1};return{x:Cn.x,y:Cn.y,zoom:Cn.k}}function rn(Cn,Qn){return W?new Promise(Y=>{Z?.interpolate(Qn?.interpolate==="linear"?RG:hue).scaleTo(z7e(W,Qn?.duration,Qn?.ease,()=>Y(!0)),Cn)}):Promise.resolve(!1)}function ln(Cn,Qn){return W?new Promise(Y=>{Z?.interpolate(Qn?.interpolate==="linear"?RG:hue).scaleBy(z7e(W,Qn?.duration,Qn?.ease,()=>Y(!0)),Cn)}):Promise.resolve(!1)}function xn(Cn){Z?.scaleExtent(Cn)}function hn(Cn){Z?.translateExtent(Cn)}function wt(Cn){const Qn=!Km(Cn)||Cn<0?0:Cn;Z?.clickDistance(Qn)}return{update:Oe,destroy:ge,setViewport:ae,setViewportConstrained:Pe,getViewport:Be,scaleTo:rn,scaleBy:ln,setScaleExtent:xn,setTranslateExtent:hn,syncViewport:Me,setClickDistance:wt}}var mI;(function(g){g.Line="line",g.Handle="handle"})(mI||(mI={}));function PGn({width:g,prevWidth:E,height:x,prevHeight:M,affectsX:N,affectsY:P}){const k=g-E,H=x-M,U=[k>0?1:k<0?-1:0,H>0?1:H<0?-1:0];return k&&N&&(U[0]=U[0]*-1),H&&P&&(U[1]=U[1]*-1),U}function k1n(g){const E=g.includes("right")||g.includes("left"),x=g.includes("bottom")||g.includes("top"),M=g.includes("left"),N=g.includes("top");return{isHorizontal:E,isVertical:x,affectsX:M,affectsY:N}}function Y7(g,E){return Math.max(0,E-g)}function Q7(g,E){return Math.max(0,g-E)}function tue(g,E,x){return Math.max(0,E-g,g-x)}function j1n(g,E){return g?!E:E}function $Gn(g,E,x,M,N,P,k,H){let{affectsX:U,affectsY:G}=E;const{isHorizontal:ie,isVertical:Z}=E,W=ie&&Z,{xSnapped:se,ySnapped:le}=x,{minWidth:ee,maxWidth:Oe,minHeight:ge,maxHeight:Pe}=M,{x:ae,y:Me,width:Be,height:rn,aspectRatio:ln}=g;let xn=Math.floor(ie?se-g.pointerX:0),hn=Math.floor(Z?le-g.pointerY:0);const wt=Be+(U?-xn:xn),Cn=rn+(G?-hn:hn),Qn=-P[0]*Be,Y=-P[1]*rn;let Fe=tue(wt,ee,Oe),mn=tue(Cn,ge,Pe);if(k){let sn=0,Sn=0;U&&xn<0?sn=Y7(ae+xn+Qn,k[0][0]):!U&&xn>0&&(sn=Q7(ae+wt+Qn,k[1][0])),G&&hn<0?Sn=Y7(Me+hn+Y,k[0][1]):!G&&hn>0&&(Sn=Q7(Me+Cn+Y,k[1][1])),Fe=Math.max(Fe,sn),mn=Math.max(mn,Sn)}if(H){let sn=0,Sn=0;U&&xn>0?sn=Q7(ae+xn,H[0][0]):!U&&xn<0&&(sn=Y7(ae+wt,H[1][0])),G&&hn>0?Sn=Q7(Me+hn,H[0][1]):!G&&hn<0&&(Sn=Y7(Me+Cn,H[1][1])),Fe=Math.max(Fe,sn),mn=Math.max(mn,Sn)}if(N){if(ie){const sn=tue(wt/ln,ge,Pe)*ln;if(Fe=Math.max(Fe,sn),k){let Sn=0;!U&&!G||U&&!G&&W?Sn=Q7(Me+Y+wt/ln,k[1][1])*ln:Sn=Y7(Me+Y+(U?xn:-xn)/ln,k[0][1])*ln,Fe=Math.max(Fe,Sn)}if(H){let Sn=0;!U&&!G||U&&!G&&W?Sn=Y7(Me+wt/ln,H[1][1])*ln:Sn=Q7(Me+(U?xn:-xn)/ln,H[0][1])*ln,Fe=Math.max(Fe,Sn)}}if(Z){const sn=tue(Cn*ln,ee,Oe)/ln;if(mn=Math.max(mn,sn),k){let Sn=0;!U&&!G||G&&!U&&W?Sn=Q7(ae+Cn*ln+Qn,k[1][0])/ln:Sn=Y7(ae+(G?hn:-hn)*ln+Qn,k[0][0])/ln,mn=Math.max(mn,Sn)}if(H){let Sn=0;!U&&!G||G&&!U&&W?Sn=Y7(ae+Cn*ln,H[1][0])/ln:Sn=Q7(ae+(G?hn:-hn)*ln,H[0][0])/ln,mn=Math.max(mn,Sn)}}}hn=hn+(hn<0?mn:-mn),xn=xn+(xn<0?Fe:-Fe),N&&(W?wt>Cn*ln?hn=(j1n(U,G)?-xn:xn)/ln:xn=(j1n(U,G)?-hn:hn)*ln:ie?(hn=xn/ln,G=U):(xn=hn*ln,U=G));const Ae=U?ae+xn:ae,Ze=G?Me+hn:Me;return{width:Be+(U?-xn:xn),height:rn+(G?-hn:hn),x:P[0]*xn*(U?-1:1)+Ae,y:P[1]*hn*(G?-1:1)+Ze}}const h0n={width:0,height:0,x:0,y:0},RGn={...h0n,pointerX:0,pointerY:0,aspectRatio:1};function BGn(g){return[[0,0],[g.measured.width,g.measured.height]]}function zGn(g,E,x){const M=E.position.x+g.position.x,N=E.position.y+g.position.y,P=g.measured.width??0,k=g.measured.height??0,H=x[0]*P,U=x[1]*k;return[[M-H,N-U],[M+P-H,N+k-U]]}function FGn({domNode:g,nodeId:E,getStoreItems:x,onChange:M,onEnd:N}){const P=$g(g);let k={controlDirection:k1n("bottom-right"),boundaries:{minWidth:0,minHeight:0,maxWidth:Number.MAX_VALUE,maxHeight:Number.MAX_VALUE},resizeDirection:void 0,keepAspectRatio:!1};function H({controlPosition:G,boundaries:ie,keepAspectRatio:Z,resizeDirection:W,onResizeStart:se,onResize:le,onResizeEnd:ee,shouldResize:Oe}){let ge={...h0n},Pe={...RGn};k={boundaries:ie,resizeDirection:W,keepAspectRatio:Z,controlDirection:k1n(G)};let ae,Me=null,Be=[],rn,ln,xn,hn=!1;const wt=jdn().on("start",Cn=>{const{nodeLookup:Qn,transform:Y,snapGrid:Fe,snapToGrid:mn,nodeOrigin:Ae,paneDomNode:Ze}=x();if(ae=Qn.get(E),!ae)return;Me=Ze?.getBoundingClientRect()??null;const{xSnapped:sn,ySnapped:Sn}=BG(Cn.sourceEvent,{transform:Y,snapGrid:Fe,snapToGrid:mn,containerBounds:Me});ge={width:ae.measured.width??0,height:ae.measured.height??0,x:ae.position.x??0,y:ae.position.y??0},Pe={...ge,pointerX:sn,pointerY:Sn,aspectRatio:ge.width/ge.height},rn=void 0,ae.parentId&&(ae.extent==="parent"||ae.expandParent)&&(rn=Qn.get(ae.parentId),ln=rn&&ae.extent==="parent"?BGn(rn):void 0),Be=[],xn=void 0;for(const[kn,xe]of Qn)if(xe.parentId===E&&(Be.push({id:kn,position:{...xe.position},extent:xe.extent}),xe.extent==="parent"||xe.expandParent)){const un=zGn(xe,ae,xe.origin??Ae);xn?xn=[[Math.min(un[0][0],xn[0][0]),Math.min(un[0][1],xn[0][1])],[Math.max(un[1][0],xn[1][0]),Math.max(un[1][1],xn[1][1])]]:xn=un}se?.(Cn,{...ge})}).on("drag",Cn=>{const{transform:Qn,snapGrid:Y,snapToGrid:Fe,nodeOrigin:mn}=x(),Ae=BG(Cn.sourceEvent,{transform:Qn,snapGrid:Y,snapToGrid:Fe,containerBounds:Me}),Ze=[];if(!ae)return;const{x:sn,y:Sn,width:kn,height:xe}=ge,un={},rt=ae.origin??mn,{width:lt,height:Bt,x:hi,y:Gt}=$Gn(Pe,k.controlDirection,Ae,k.boundaries,k.keepAspectRatio,rt,ln,xn),At=lt!==kn,st=Bt!==xe,Wr=hi!==sn&&At,Mr=Gt!==Sn&&st;if(!Wr&&!Mr&&!At&&!st)return;if((Wr||Mr||rt[0]===1||rt[1]===1)&&(un.x=Wr?hi:ge.x,un.y=Mr?Gt:ge.y,ge.x=un.x,ge.y=un.y,Be.length>0)){const fu=hi-sn,Eu=Gt-Sn;for(const Ws of Be)Ws.position={x:Ws.position.x-fu+rt[0]*(lt-kn),y:Ws.position.y-Eu+rt[1]*(Bt-xe)},Ze.push(Ws)}if((At||st)&&(un.width=At&&(!k.resizeDirection||k.resizeDirection==="horizontal")?lt:ge.width,un.height=st&&(!k.resizeDirection||k.resizeDirection==="vertical")?Bt:ge.height,ge.width=un.width,ge.height=un.height),rn&&ae.expandParent){const fu=rt[0]*(un.width??0);un.x&&un.x{hn&&(ee?.(Cn,{...ge}),N?.({...ge}),hn=!1)});P.call(wt)}function U(){P.on(".drag",null)}return{update:H,destroy:U}}var F7e={exports:{}},J7e={},H7e={exports:{}},G7e={};var E1n;function JGn(){if(E1n)return G7e;E1n=1;var g=WG();function E(Z,W){return Z===W&&(Z!==0||1/Z===1/W)||Z!==Z&&W!==W}var x=typeof Object.is=="function"?Object.is:E,M=g.useState,N=g.useEffect,P=g.useLayoutEffect,k=g.useDebugValue;function H(Z,W){var se=W(),le=M({inst:{value:se,getSnapshot:W}}),ee=le[0].inst,Oe=le[1];return P(function(){ee.value=se,ee.getSnapshot=W,U(ee)&&Oe({inst:ee})},[Z,se,W]),N(function(){return U(ee)&&Oe({inst:ee}),Z(function(){U(ee)&&Oe({inst:ee})})},[Z]),k(se),se}function U(Z){var W=Z.getSnapshot;Z=Z.value;try{var se=W();return!x(Z,se)}catch{return!0}}function G(Z,W){return W()}var ie=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?G:H;return G7e.useSyncExternalStore=g.useSyncExternalStore!==void 0?g.useSyncExternalStore:ie,G7e}var S1n;function HGn(){return S1n||(S1n=1,H7e.exports=JGn()),H7e.exports}var x1n;function GGn(){if(x1n)return J7e;x1n=1;var g=WG(),E=HGn();function x(G,ie){return G===ie&&(G!==0||1/G===1/ie)||G!==G&&ie!==ie}var M=typeof Object.is=="function"?Object.is:x,N=E.useSyncExternalStore,P=g.useRef,k=g.useEffect,H=g.useMemo,U=g.useDebugValue;return J7e.useSyncExternalStoreWithSelector=function(G,ie,Z,W,se){var le=P(null);if(le.current===null){var ee={hasValue:!1,value:null};le.current=ee}else ee=le.current;le=H(function(){function ge(rn){if(!Pe){if(Pe=!0,ae=rn,rn=W(rn),se!==void 0&&ee.hasValue){var ln=ee.value;if(se(ln,rn))return Me=ln}return Me=rn}if(ln=Me,M(ae,rn))return ln;var xn=W(rn);return se!==void 0&&se(ln,xn)?(ae=rn,ln):(ae=rn,Me=xn)}var Pe=!1,ae,Me,Be=Z===void 0?null:Z;return[function(){return ge(ie())},Be===null?void 0:function(){return ge(Be())}]},[ie,Z,W,se]);var Oe=N(G,le[0],le[1]);return k(function(){ee.hasValue=!0,ee.value=Oe},[Oe]),U(Oe),Oe},J7e}var A1n;function qGn(){return A1n||(A1n=1,F7e.exports=GGn()),F7e.exports}var UGn=qGn();const XGn=bke(UGn),KGn={},M1n=g=>{let E;const x=new Set,M=(ie,Z)=>{const W=typeof ie=="function"?ie(E):ie;if(!Object.is(W,E)){const se=E;E=Z??(typeof W!="object"||W===null)?W:Object.assign({},E,W),x.forEach(le=>le(E,se))}},N=()=>E,U={setState:M,getState:N,getInitialState:()=>G,subscribe:ie=>(x.add(ie),()=>x.delete(ie)),destroy:()=>{(KGn?"production":void 0)!=="production"&&console.warn("[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."),x.clear()}},G=E=g(M,N,U);return U},VGn=g=>g?M1n(g):M1n,{useDebugValue:YGn}=izn,{useSyncExternalStoreWithSelector:QGn}=XGn,WGn=g=>g;function d0n(g,E=WGn,x){const M=QGn(g.subscribe,g.getState,g.getServerState||g.getInitialState,E,x);return YGn(M),M}const T1n=(g,E)=>{const x=VGn(g),M=(N,P=E)=>d0n(x,N,P);return Object.assign(M,x),M},ZGn=(g,E)=>g?T1n(g,E):T1n;function El(g,E){if(Object.is(g,E))return!0;if(typeof g!="object"||g===null||typeof E!="object"||E===null)return!1;if(g instanceof Map&&E instanceof Map){if(g.size!==E.size)return!1;for(const[M,N]of g)if(!Object.is(N,E.get(M)))return!1;return!0}if(g instanceof Set&&E instanceof Set){if(g.size!==E.size)return!1;for(const M of g)if(!E.has(M))return!1;return!0}const x=Object.keys(g);if(x.length!==Object.keys(E).length)return!1;for(const M of x)if(!Object.prototype.hasOwnProperty.call(E,M)||!Object.is(g[M],E[M]))return!1;return!0}var eqn=odn();const Rue=He.createContext(null),nqn=Rue.Provider,b0n=u5.error001();function Fu(g,E){const x=He.useContext(Rue);if(x===null)throw new Error(b0n);return d0n(x,g,E)}function Sl(){const g=He.useContext(Rue);if(g===null)throw new Error(b0n);return He.useMemo(()=>({getState:g.getState,setState:g.setState,subscribe:g.subscribe}),[g])}const C1n={display:"none"},tqn={position:"absolute",width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0px, 0px, 0px, 0px)",clipPath:"inset(100%)"},g0n="react-flow__node-desc",w0n="react-flow__edge-desc",iqn="react-flow__aria-live",rqn=g=>g.ariaLiveMessage,cqn=g=>g.ariaLabelConfig;function uqn({rfId:g}){const E=Fu(rqn);return F.jsx("div",{id:`${iqn}-${g}`,"aria-live":"assertive","aria-atomic":"true",style:tqn,children:E})}function oqn({rfId:g,disableKeyboardA11y:E}){const x=Fu(cqn);return F.jsxs(F.Fragment,{children:[F.jsx("div",{id:`${g0n}-${g}`,style:C1n,children:E?x["node.a11yDescription.default"]:x["node.a11yDescription.keyboardDisabled"]}),F.jsx("div",{id:`${w0n}-${g}`,style:C1n,children:x["edge.a11yDescription.default"]}),!E&&F.jsx(uqn,{rfId:g})]})}const Bue=He.forwardRef(({position:g="top-left",children:E,className:x,style:M,...N},P)=>{const k=`${g}`.split("-");return F.jsx("div",{className:Ta(["react-flow__panel",x,...k]),style:M,ref:P,...N,children:E})});Bue.displayName="Panel";function sqn({proOptions:g,position:E="bottom-right"}){return g?.hideAttribution?null:F.jsx(Bue,{position:E,className:"react-flow__attribution","data-message":"Please only hide this attribution when you are subscribed to React Flow Pro: https://pro.reactflow.dev",children:F.jsx("a",{href:"https://reactflow.dev",target:"_blank",rel:"noopener noreferrer","aria-label":"React Flow attribution",children:"React Flow"})})}const lqn=g=>{const E=[],x=[];for(const[,M]of g.nodeLookup)M.selected&&E.push(M.internals.userNode);for(const[,M]of g.edgeLookup)M.selected&&x.push(M);return{selectedNodes:E,selectedEdges:x}},iue=g=>g.id;function fqn(g,E){return El(g.selectedNodes.map(iue),E.selectedNodes.map(iue))&&El(g.selectedEdges.map(iue),E.selectedEdges.map(iue))}function aqn({onSelectionChange:g}){const E=Sl(),{selectedNodes:x,selectedEdges:M}=Fu(lqn,fqn);return He.useEffect(()=>{const N={nodes:x,edges:M};g?.(N),E.getState().onSelectionChangeHandlers.forEach(P=>P(N))},[x,M,g]),null}const hqn=g=>!!g.onSelectionChangeHandlers;function dqn({onSelectionChange:g}){const E=Fu(hqn);return g||E?F.jsx(aqn,{onSelectionChange:g}):null}const ake=typeof window<"u"?He.useLayoutEffect:He.useEffect,p0n=[0,0],bqn={x:0,y:0,zoom:1},gqn=["nodes","edges","defaultNodes","defaultEdges","onConnect","onConnectStart","onConnectEnd","onClickConnectStart","onClickConnectEnd","nodesDraggable","autoPanOnNodeFocus","nodesConnectable","nodesFocusable","edgesFocusable","edgesReconnectable","elevateNodesOnSelect","elevateEdgesOnSelect","minZoom","maxZoom","nodeExtent","onNodesChange","onEdgesChange","elementsSelectable","connectionMode","snapGrid","snapToGrid","translateExtent","connectOnClick","defaultEdgeOptions","fitView","fitViewOptions","onNodesDelete","onEdgesDelete","onDelete","onNodeDrag","onNodeDragStart","onNodeDragStop","onSelectionDrag","onSelectionDragStart","onSelectionDragStop","onMoveStart","onMove","onMoveEnd","noPanClassName","nodeOrigin","autoPanOnConnect","autoPanOnNodeDrag","onError","connectionRadius","isValidConnection","selectNodesOnDrag","nodeDragThreshold","connectionDragThreshold","onBeforeDelete","debug","autoPanSpeed","ariaLabelConfig","zIndexMode"],O1n=[...gqn,"rfId"],wqn=g=>({setNodes:g.setNodes,setEdges:g.setEdges,setMinZoom:g.setMinZoom,setMaxZoom:g.setMaxZoom,setTranslateExtent:g.setTranslateExtent,setNodeExtent:g.setNodeExtent,reset:g.reset,setDefaultNodesAndEdges:g.setDefaultNodesAndEdges}),N1n={translateExtent:GG,nodeOrigin:p0n,minZoom:.5,maxZoom:2,elementsSelectable:!0,noPanClassName:"nopan",rfId:"1"};function pqn(g){const{setNodes:E,setEdges:x,setMinZoom:M,setMaxZoom:N,setTranslateExtent:P,setNodeExtent:k,reset:H,setDefaultNodesAndEdges:U}=Fu(wqn,El),G=Sl();ake(()=>(U(g.defaultNodes,g.defaultEdges),()=>{ie.current=N1n,H()}),[]);const ie=He.useRef(N1n);return ake(()=>{for(const Z of O1n){const W=g[Z],se=ie.current[Z];W!==se&&(typeof g[Z]>"u"||(Z==="nodes"?E(W):Z==="edges"?x(W):Z==="minZoom"?M(W):Z==="maxZoom"?N(W):Z==="translateExtent"?P(W):Z==="nodeExtent"?k(W):Z==="ariaLabelConfig"?G.setState({ariaLabelConfig:ZHn(W)}):Z==="fitView"?G.setState({fitViewQueued:W}):Z==="fitViewOptions"?G.setState({fitViewOptions:W}):G.setState({[Z]:W})))}ie.current=g},O1n.map(Z=>g[Z])),null}function D1n(){return typeof window>"u"||!window.matchMedia?null:window.matchMedia("(prefers-color-scheme: dark)")}function mqn(g){const[E,x]=He.useState(g==="system"?null:g);return He.useEffect(()=>{if(g!=="system"){x(g);return}const M=D1n(),N=()=>x(M?.matches?"dark":"light");return N(),M?.addEventListener("change",N),()=>{M?.removeEventListener("change",N)}},[g]),E!==null?E:D1n()?.matches?"dark":"light"}const I1n=typeof document<"u"?document:null;function VG(g=null,E={target:I1n,actInsideInputWithModifier:!0}){const[x,M]=He.useState(!1),N=He.useRef(!1),P=He.useRef(new Set([])),[k,H]=He.useMemo(()=>{if(g!==null){const G=(Array.isArray(g)?g:[g]).filter(Z=>typeof Z=="string").map(Z=>Z.replace("+",` +`).replace(` + +`,` ++`).split(` +`)),ie=G.reduce((Z,W)=>Z.concat(...W),[]);return[G,ie]}return[[],[]]},[g]);return He.useEffect(()=>{const U=E?.target??I1n,G=E?.actInsideInputWithModifier??!0;if(g!==null){const ie=se=>{if(N.current=se.ctrlKey||se.metaKey||se.shiftKey||se.altKey,(!N.current||N.current&&!G)&&Ydn(se))return!1;const ee=L1n(se.code,H);if(P.current.add(se[ee]),_1n(k,P.current,!1)){const Oe=se.composedPath?.()?.[0]||se.target,ge=Oe?.nodeName==="BUTTON"||Oe?.nodeName==="A";E.preventDefault!==!1&&(N.current||!ge)&&se.preventDefault(),M(!0)}},Z=se=>{const le=L1n(se.code,H);_1n(k,P.current,!0)?(M(!1),P.current.clear()):P.current.delete(se[le]),se.key==="Meta"&&P.current.clear(),N.current=!1},W=()=>{P.current.clear(),M(!1)};return U?.addEventListener("keydown",ie),U?.addEventListener("keyup",Z),window.addEventListener("blur",W),window.addEventListener("contextmenu",W),()=>{U?.removeEventListener("keydown",ie),U?.removeEventListener("keyup",Z),window.removeEventListener("blur",W),window.removeEventListener("contextmenu",W)}}},[g,M]),x}function _1n(g,E,x){return g.filter(M=>x||M.length===E.size).some(M=>M.every(N=>E.has(N)))}function L1n(g,E){return E.includes(g)?"code":"key"}const vqn=()=>{const g=Sl();return He.useMemo(()=>({zoomIn:E=>{const{panZoom:x}=g.getState();return x?x.scaleBy(1.2,E):Promise.resolve(!1)},zoomOut:E=>{const{panZoom:x}=g.getState();return x?x.scaleBy(1/1.2,E):Promise.resolve(!1)},zoomTo:(E,x)=>{const{panZoom:M}=g.getState();return M?M.scaleTo(E,x):Promise.resolve(!1)},getZoom:()=>g.getState().transform[2],setViewport:async(E,x)=>{const{transform:[M,N,P],panZoom:k}=g.getState();return k?(await k.setViewport({x:E.x??M,y:E.y??N,zoom:E.zoom??P},x),Promise.resolve(!0)):Promise.resolve(!1)},getViewport:()=>{const[E,x,M]=g.getState().transform;return{x:E,y:x,zoom:M}},setCenter:async(E,x,M)=>g.getState().setCenter(E,x,M),fitBounds:async(E,x)=>{const{width:M,height:N,minZoom:P,maxZoom:k,panZoom:H}=g.getState(),U=Ske(E,M,N,P,k,x?.padding??.1);return H?(await H.setViewport(U,{duration:x?.duration,ease:x?.ease,interpolate:x?.interpolate}),Promise.resolve(!0)):Promise.resolve(!1)},screenToFlowPosition:(E,x={})=>{const{transform:M,snapGrid:N,snapToGrid:P,domNode:k}=g.getState();if(!k)return E;const{x:H,y:U}=k.getBoundingClientRect(),G={x:E.x-H,y:E.y-U},ie=x.snapGrid??N,Z=x.snapToGrid??P;return rq(G,M,Z,ie)},flowToScreenPosition:E=>{const{transform:x,domNode:M}=g.getState();if(!M)return E;const{x:N,y:P}=M.getBoundingClientRect(),k=xue(E,x);return{x:k.x+N,y:k.y+P}}}),[])};function m0n(g,E){const x=[],M=new Map,N=[];for(const P of g)if(P.type==="add"){N.push(P);continue}else if(P.type==="remove"||P.type==="replace")M.set(P.id,[P]);else{const k=M.get(P.id);k?k.push(P):M.set(P.id,[P])}for(const P of E){const k=M.get(P.id);if(!k){x.push(P);continue}if(k[0].type==="remove")continue;if(k[0].type==="replace"){x.push({...k[0].item});continue}const H={...P};for(const U of k)yqn(U,H);x.push(H)}return N.length&&N.forEach(P=>{P.index!==void 0?x.splice(P.index,0,{...P.item}):x.push({...P.item})}),x}function yqn(g,E){switch(g.type){case"select":{E.selected=g.selected;break}case"position":{typeof g.position<"u"&&(E.position=g.position),typeof g.dragging<"u"&&(E.dragging=g.dragging);break}case"dimensions":{typeof g.dimensions<"u"&&(E.measured={...g.dimensions},g.setAttributes&&((g.setAttributes===!0||g.setAttributes==="width")&&(E.width=g.dimensions.width),(g.setAttributes===!0||g.setAttributes==="height")&&(E.height=g.dimensions.height))),typeof g.resizing=="boolean"&&(E.resizing=g.resizing);break}}}function v0n(g,E){return m0n(g,E)}function y0n(g,E){return m0n(g,E)}function vA(g,E){return{id:g,type:"select",selected:E}}function lI(g,E=new Set,x=!1){const M=[];for(const[N,P]of g){const k=E.has(N);!(P.selected===void 0&&!k)&&P.selected!==k&&(x&&(P.selected=k),M.push(vA(P.id,k)))}return M}function P1n({items:g=[],lookup:E}){const x=[],M=new Map(g.map(N=>[N.id,N]));for(const[N,P]of g.entries()){const k=E.get(P.id),H=k?.internals?.userNode??k;H!==void 0&&H!==P&&x.push({id:P.id,item:P,type:"replace"}),H===void 0&&x.push({item:P,type:"add",index:N})}for(const[N]of E)M.get(N)===void 0&&x.push({id:N,type:"remove"});return x}function $1n(g){return{id:g.id,type:"remove"}}const R1n=g=>HHn(g),kqn=g=>Jdn(g);function k0n(g){return He.forwardRef(g)}function B1n(g){const[E,x]=He.useState(BigInt(0)),[M]=He.useState(()=>jqn(()=>x(N=>N+BigInt(1))));return ake(()=>{const N=M.get();N.length&&(g(N),M.reset())},[E]),M}function jqn(g){let E=[];return{get:()=>E,reset:()=>{E=[]},push:x=>{E.push(x),g()}}}const j0n=He.createContext(null);function Eqn({children:g}){const E=Sl(),x=He.useCallback(H=>{const{nodes:U=[],setNodes:G,hasDefaultNodes:ie,onNodesChange:Z,nodeLookup:W,fitViewQueued:se,onNodesChangeMiddlewareMap:le}=E.getState();let ee=U;for(const ge of H)ee=typeof ge=="function"?ge(ee):ge;let Oe=P1n({items:ee,lookup:W});for(const ge of le.values())Oe=ge(Oe);ie&&G(ee),Oe.length>0?Z?.(Oe):se&&window.requestAnimationFrame(()=>{const{fitViewQueued:ge,nodes:Pe,setNodes:ae}=E.getState();ge&&ae(Pe)})},[]),M=B1n(x),N=He.useCallback(H=>{const{edges:U=[],setEdges:G,hasDefaultEdges:ie,onEdgesChange:Z,edgeLookup:W}=E.getState();let se=U;for(const le of H)se=typeof le=="function"?le(se):le;ie?G(se):Z&&Z(P1n({items:se,lookup:W}))},[]),P=B1n(N),k=He.useMemo(()=>({nodeQueue:M,edgeQueue:P}),[]);return F.jsx(j0n.Provider,{value:k,children:g})}function Sqn(){const g=He.useContext(j0n);if(!g)throw new Error("useBatchContext must be used within a BatchProvider");return g}const xqn=g=>!!g.panZoom;function Nke(){const g=vqn(),E=Sl(),x=Sqn(),M=Fu(xqn),N=He.useMemo(()=>{const P=Z=>E.getState().nodeLookup.get(Z),k=Z=>{x.nodeQueue.push(Z)},H=Z=>{x.edgeQueue.push(Z)},U=Z=>{const{nodeLookup:W,nodeOrigin:se}=E.getState(),le=R1n(Z)?Z:W.get(Z.id),ee=le.parentId?Kdn(le.position,le.measured,le.parentId,W,se):le.position,Oe={...le,position:ee,width:le.measured?.width??le.width,height:le.measured?.height??le.height};return wI(Oe)},G=(Z,W,se={replace:!1})=>{k(le=>le.map(ee=>{if(ee.id===Z){const Oe=typeof W=="function"?W(ee):W;return se.replace&&R1n(Oe)?Oe:{...ee,...Oe}}return ee}))},ie=(Z,W,se={replace:!1})=>{H(le=>le.map(ee=>{if(ee.id===Z){const Oe=typeof W=="function"?W(ee):W;return se.replace&&kqn(Oe)?Oe:{...ee,...Oe}}return ee}))};return{getNodes:()=>E.getState().nodes.map(Z=>({...Z})),getNode:Z=>P(Z)?.internals.userNode,getInternalNode:P,getEdges:()=>{const{edges:Z=[]}=E.getState();return Z.map(W=>({...W}))},getEdge:Z=>E.getState().edgeLookup.get(Z),setNodes:k,setEdges:H,addNodes:Z=>{const W=Array.isArray(Z)?Z:[Z];x.nodeQueue.push(se=>[...se,...W])},addEdges:Z=>{const W=Array.isArray(Z)?Z:[Z];x.edgeQueue.push(se=>[...se,...W])},toObject:()=>{const{nodes:Z=[],edges:W=[],transform:se}=E.getState(),[le,ee,Oe]=se;return{nodes:Z.map(ge=>({...ge})),edges:W.map(ge=>({...ge})),viewport:{x:le,y:ee,zoom:Oe}}},deleteElements:async({nodes:Z=[],edges:W=[]})=>{const{nodes:se,edges:le,onNodesDelete:ee,onEdgesDelete:Oe,triggerNodeChanges:ge,triggerEdgeChanges:Pe,onDelete:ae,onBeforeDelete:Me}=E.getState(),{nodes:Be,edges:rn}=await KHn({nodesToRemove:Z,edgesToRemove:W,nodes:se,edges:le,onBeforeDelete:Me}),ln=rn.length>0,xn=Be.length>0;if(ln){const hn=rn.map($1n);Oe?.(rn),Pe(hn)}if(xn){const hn=Be.map($1n);ee?.(Be),ge(hn)}return(xn||ln)&&ae?.({nodes:Be,edges:rn}),{deletedNodes:Be,deletedEdges:rn}},getIntersectingNodes:(Z,W=!0,se)=>{const le=f1n(Z),ee=le?Z:U(Z),Oe=se!==void 0;return ee?(se||E.getState().nodes).filter(ge=>{const Pe=E.getState().nodeLookup.get(ge.id);if(Pe&&!le&&(ge.id===Z.id||!Pe.internals.positionAbsolute))return!1;const ae=wI(Oe?ge:Pe),Me=XG(ae,ee);return W&&Me>0||Me>=ae.width*ae.height||Me>=ee.width*ee.height}):[]},isNodeIntersecting:(Z,W,se=!0)=>{const ee=f1n(Z)?Z:U(Z);if(!ee)return!1;const Oe=XG(ee,W);return se&&Oe>0||Oe>=W.width*W.height||Oe>=ee.width*ee.height},updateNode:G,updateNodeData:(Z,W,se={replace:!1})=>{G(Z,le=>{const ee=typeof W=="function"?W(le):W;return se.replace?{...le,data:ee}:{...le,data:{...le.data,...ee}}},se)},updateEdge:ie,updateEdgeData:(Z,W,se={replace:!1})=>{ie(Z,le=>{const ee=typeof W=="function"?W(le):W;return se.replace?{...le,data:ee}:{...le,data:{...le.data,...ee}}},se)},getNodesBounds:Z=>{const{nodeLookup:W,nodeOrigin:se}=E.getState();return GHn(Z,{nodeLookup:W,nodeOrigin:se})},getHandleConnections:({type:Z,id:W,nodeId:se})=>Array.from(E.getState().connectionLookup.get(`${se}-${Z}${W?`-${W}`:""}`)?.values()??[]),getNodeConnections:({type:Z,handleId:W,nodeId:se})=>Array.from(E.getState().connectionLookup.get(`${se}${Z?W?`-${Z}-${W}`:`-${Z}`:""}`)?.values()??[]),fitView:async Z=>{const W=E.getState().fitViewResolver??WHn();return E.setState({fitViewQueued:!0,fitViewOptions:Z,fitViewResolver:W}),x.nodeQueue.push(se=>[...se]),W.promise}}},[]);return He.useMemo(()=>({...N,...g,viewportInitialized:M}),[M])}const z1n=g=>g.selected,Aqn=typeof window<"u"?window:void 0;function Mqn({deleteKeyCode:g,multiSelectionKeyCode:E}){const x=Sl(),{deleteElements:M}=Nke(),N=VG(g,{actInsideInputWithModifier:!1}),P=VG(E,{target:Aqn});He.useEffect(()=>{if(N){const{edges:k,nodes:H}=x.getState();M({nodes:H.filter(z1n),edges:k.filter(z1n)}),x.setState({nodesSelectionActive:!1})}},[N]),He.useEffect(()=>{x.setState({multiSelectionActive:P})},[P])}function Tqn(g){const E=Sl();He.useEffect(()=>{const x=()=>{if(!g.current||!(g.current.checkVisibility?.()??!0))return!1;const M=xke(g.current);(M.height===0||M.width===0)&&E.getState().onError?.("004",u5.error004()),E.setState({width:M.width||500,height:M.height||500})};if(g.current){x(),window.addEventListener("resize",x);const M=new ResizeObserver(()=>x());return M.observe(g.current),()=>{window.removeEventListener("resize",x),M&&g.current&&M.unobserve(g.current)}}},[])}const zue={position:"absolute",width:"100%",height:"100%",top:0,left:0},Cqn=g=>({userSelectionActive:g.userSelectionActive,lib:g.lib,connectionInProgress:g.connection.inProgress});function Oqn({onPaneContextMenu:g,zoomOnScroll:E=!0,zoomOnPinch:x=!0,panOnScroll:M=!1,panOnScrollSpeed:N=.5,panOnScrollMode:P=jA.Free,zoomOnDoubleClick:k=!0,panOnDrag:H=!0,defaultViewport:U,translateExtent:G,minZoom:ie,maxZoom:Z,zoomActivationKeyCode:W,preventScrolling:se=!0,children:le,noWheelClassName:ee,noPanClassName:Oe,onViewportChange:ge,isControlledViewport:Pe,paneClickDistance:ae,selectionOnDrag:Me}){const Be=Sl(),rn=He.useRef(null),{userSelectionActive:ln,lib:xn,connectionInProgress:hn}=Fu(Cqn,El),wt=VG(W),Cn=He.useRef();Tqn(rn);const Qn=He.useCallback(Y=>{ge?.({x:Y[0],y:Y[1],zoom:Y[2]}),Pe||Be.setState({transform:Y})},[ge,Pe]);return He.useEffect(()=>{if(rn.current){Cn.current=LGn({domNode:rn.current,minZoom:ie,maxZoom:Z,translateExtent:G,viewport:U,onDraggingChange:Ae=>Be.setState(Ze=>Ze.paneDragging===Ae?Ze:{paneDragging:Ae}),onPanZoomStart:(Ae,Ze)=>{const{onViewportChangeStart:sn,onMoveStart:Sn}=Be.getState();Sn?.(Ae,Ze),sn?.(Ze)},onPanZoom:(Ae,Ze)=>{const{onViewportChange:sn,onMove:Sn}=Be.getState();Sn?.(Ae,Ze),sn?.(Ze)},onPanZoomEnd:(Ae,Ze)=>{const{onViewportChangeEnd:sn,onMoveEnd:Sn}=Be.getState();Sn?.(Ae,Ze),sn?.(Ze)}});const{x:Y,y:Fe,zoom:mn}=Cn.current.getViewport();return Be.setState({panZoom:Cn.current,transform:[Y,Fe,mn],domNode:rn.current.closest(".react-flow")}),()=>{Cn.current?.destroy()}}},[]),He.useEffect(()=>{Cn.current?.update({onPaneContextMenu:g,zoomOnScroll:E,zoomOnPinch:x,panOnScroll:M,panOnScrollSpeed:N,panOnScrollMode:P,zoomOnDoubleClick:k,panOnDrag:H,zoomActivationKeyPressed:wt,preventScrolling:se,noPanClassName:Oe,userSelectionActive:ln,noWheelClassName:ee,lib:xn,onTransformChange:Qn,connectionInProgress:hn,selectionOnDrag:Me,paneClickDistance:ae})},[g,E,x,M,N,P,k,H,wt,se,Oe,ln,ee,xn,Qn,hn,Me,ae]),F.jsx("div",{className:"react-flow__renderer",ref:rn,style:zue,children:le})}const Nqn=g=>({userSelectionActive:g.userSelectionActive,userSelectionRect:g.userSelectionRect});function Dqn(){const{userSelectionActive:g,userSelectionRect:E}=Fu(Nqn,El);return g&&E?F.jsx("div",{className:"react-flow__selection react-flow__container",style:{width:E.width,height:E.height,transform:`translate(${E.x}px, ${E.y}px)`}}):null}const q7e=(g,E)=>x=>{x.target===E.current&&g?.(x)},Iqn=g=>({userSelectionActive:g.userSelectionActive,elementsSelectable:g.elementsSelectable,connectionInProgress:g.connection.inProgress,dragging:g.paneDragging});function _qn({isSelecting:g,selectionKeyPressed:E,selectionMode:x=qG.Full,panOnDrag:M,paneClickDistance:N,selectionOnDrag:P,onSelectionStart:k,onSelectionEnd:H,onPaneClick:U,onPaneContextMenu:G,onPaneScroll:ie,onPaneMouseEnter:Z,onPaneMouseMove:W,onPaneMouseLeave:se,children:le}){const ee=Sl(),{userSelectionActive:Oe,elementsSelectable:ge,dragging:Pe,connectionInProgress:ae}=Fu(Iqn,El),Me=ge&&(g||Oe),Be=He.useRef(null),rn=He.useRef(),ln=He.useRef(new Set),xn=He.useRef(new Set),hn=He.useRef(!1),wt=sn=>{if(hn.current||ae){hn.current=!1;return}U?.(sn),ee.getState().resetSelectedElements(),ee.setState({nodesSelectionActive:!1})},Cn=sn=>{if(Array.isArray(M)&&M?.includes(2)){sn.preventDefault();return}G?.(sn)},Qn=ie?sn=>ie(sn):void 0,Y=sn=>{hn.current&&(sn.stopPropagation(),hn.current=!1)},Fe=sn=>{const{domNode:Sn}=ee.getState();if(rn.current=Sn?.getBoundingClientRect(),!rn.current)return;const kn=sn.target===Be.current;if(!kn&&!!sn.target.closest(".nokey")||!g||!(P&&kn||E)||sn.button!==0||!sn.isPrimary)return;sn.target?.setPointerCapture?.(sn.pointerId),hn.current=!1;const{x:rt,y:lt}=Vm(sn.nativeEvent,rn.current);ee.setState({userSelectionRect:{width:0,height:0,startX:rt,startY:lt,x:rt,y:lt}}),kn||(sn.stopPropagation(),sn.preventDefault())},mn=sn=>{const{userSelectionRect:Sn,transform:kn,nodeLookup:xe,edgeLookup:un,connectionLookup:rt,triggerNodeChanges:lt,triggerEdgeChanges:Bt,defaultEdgeOptions:hi,resetSelectedElements:Gt}=ee.getState();if(!rn.current||!Sn)return;const{x:At,y:st}=Vm(sn.nativeEvent,rn.current),{startX:Wr,startY:Mr}=Sn;if(!hn.current){const Eu=E?0:N;if(Math.hypot(At-Wr,st-Mr)<=Eu)return;Gt(),k?.(sn)}hn.current=!0;const ur={startX:Wr,startY:Mr,x:AtEu.id)),xn.current=new Set;const fu=hi?.selectable??!0;for(const Eu of ln.current){const Ws=rt.get(Eu);if(Ws)for(const{edgeId:Dh}of Ws.values()){const ef=un.get(Dh);ef&&(ef.selectable??fu)&&xn.current.add(Dh)}}if(!a1n(mi,ln.current)){const Eu=lI(xe,ln.current,!0);lt(Eu)}if(!a1n(Fi,xn.current)){const Eu=lI(un,xn.current);Bt(Eu)}ee.setState({userSelectionRect:ur,userSelectionActive:!0,nodesSelectionActive:!1})},Ae=sn=>{sn.button===0&&(sn.target?.releasePointerCapture?.(sn.pointerId),!Oe&&sn.target===Be.current&&ee.getState().userSelectionRect&&wt?.(sn),ee.setState({userSelectionActive:!1,userSelectionRect:null}),hn.current&&(H?.(sn),ee.setState({nodesSelectionActive:ln.current.size>0})))},Ze=M===!0||Array.isArray(M)&&M.includes(0);return F.jsxs("div",{className:Ta(["react-flow__pane",{draggable:Ze,dragging:Pe,selection:g}]),onClick:Me?void 0:q7e(wt,Be),onContextMenu:q7e(Cn,Be),onWheel:q7e(Qn,Be),onPointerEnter:Me?void 0:Z,onPointerMove:Me?mn:W,onPointerUp:Me?Ae:void 0,onPointerDownCapture:Me?Fe:void 0,onClickCapture:Me?Y:void 0,onPointerLeave:se,ref:Be,style:zue,children:[le,F.jsx(Dqn,{})]})}function hke({id:g,store:E,unselect:x=!1,nodeRef:M}){const{addSelectedNodes:N,unselectNodesAndEdges:P,multiSelectionActive:k,nodeLookup:H,onError:U}=E.getState(),G=H.get(g);if(!G){U?.("012",u5.error012(g));return}E.setState({nodesSelectionActive:!1}),G.selected?(x||G.selected&&k)&&(P({nodes:[G],edges:[]}),requestAnimationFrame(()=>M?.current?.blur())):N([g])}function E0n({nodeRef:g,disabled:E=!1,noDragClassName:x,handleSelector:M,nodeId:N,isSelectable:P,nodeClickDistance:k}){const H=Sl(),[U,G]=He.useState(!1),ie=He.useRef();return He.useEffect(()=>{ie.current=kGn({getStoreItems:()=>H.getState(),onNodeMouseDown:Z=>{hke({id:Z,store:H,nodeRef:g})},onDragStart:()=>{G(!0)},onDragStop:()=>{G(!1)}})},[]),He.useEffect(()=>{if(!(E||!g.current||!ie.current))return ie.current.update({noDragClassName:x,handleSelector:M,domNode:g.current,isSelectable:P,nodeId:N,nodeClickDistance:k}),()=>{ie.current?.destroy()}},[x,M,E,P,g,N,k]),U}const Lqn=g=>E=>E.selected&&(E.draggable||g&&typeof E.draggable>"u");function S0n(){const g=Sl();return He.useCallback(x=>{const{nodeExtent:M,snapToGrid:N,snapGrid:P,nodesDraggable:k,onError:H,updateNodePositions:U,nodeLookup:G,nodeOrigin:ie}=g.getState(),Z=new Map,W=Lqn(k),se=N?P[0]:5,le=N?P[1]:5,ee=x.direction.x*se*x.factor,Oe=x.direction.y*le*x.factor;for(const[,ge]of G){if(!W(ge))continue;let Pe={x:ge.internals.positionAbsolute.x+ee,y:ge.internals.positionAbsolute.y+Oe};N&&(Pe=iq(Pe,P));const{position:ae,positionAbsolute:Me}=Hdn({nodeId:ge.id,nextPosition:Pe,nodeLookup:G,nodeExtent:M,nodeOrigin:ie,onError:H});ge.position=ae,ge.internals.positionAbsolute=Me,Z.set(ge.id,ge)}U(Z)},[])}const Dke=He.createContext(null),Pqn=Dke.Provider;Dke.Consumer;const x0n=()=>He.useContext(Dke),$qn=g=>({connectOnClick:g.connectOnClick,noPanClassName:g.noPanClassName,rfId:g.rfId}),Rqn=(g,E,x)=>M=>{const{connectionClickStartHandle:N,connectionMode:P,connection:k}=M,{fromHandle:H,toHandle:U,isValid:G}=k,ie=U?.nodeId===g&&U?.id===E&&U?.type===x;return{connectingFrom:H?.nodeId===g&&H?.id===E&&H?.type===x,connectingTo:ie,clickConnecting:N?.nodeId===g&&N?.id===E&&N?.type===x,isPossibleEndHandle:P===bI.Strict?H?.type!==x:g!==H?.nodeId||E!==H?.id,connectionInProcess:!!H,clickConnectionInProcess:!!N,valid:ie&&G}};function Bqn({type:g="source",position:E=cr.Top,isValidConnection:x,isConnectable:M=!0,isConnectableStart:N=!0,isConnectableEnd:P=!0,id:k,onConnect:H,children:U,className:G,onMouseDown:ie,onTouchStart:Z,...W},se){const le=k||null,ee=g==="target",Oe=Sl(),ge=x0n(),{connectOnClick:Pe,noPanClassName:ae,rfId:Me}=Fu($qn,El),{connectingFrom:Be,connectingTo:rn,clickConnecting:ln,isPossibleEndHandle:xn,connectionInProcess:hn,clickConnectionInProcess:wt,valid:Cn}=Fu(Rqn(ge,le,g),El);ge||Oe.getState().onError?.("010",u5.error010());const Qn=mn=>{const{defaultEdgeOptions:Ae,onConnect:Ze,hasDefaultEdges:sn}=Oe.getState(),Sn={...Ae,...mn};if(sn){const{edges:kn,setEdges:xe}=Oe.getState();xe(cGn(Sn,kn))}Ze?.(Sn),H?.(Sn)},Y=mn=>{if(!ge)return;const Ae=Qdn(mn.nativeEvent);if(N&&(Ae&&mn.button===0||!Ae)){const Ze=Oe.getState();fke.onPointerDown(mn.nativeEvent,{handleDomNode:mn.currentTarget,autoPanOnConnect:Ze.autoPanOnConnect,connectionMode:Ze.connectionMode,connectionRadius:Ze.connectionRadius,domNode:Ze.domNode,nodeLookup:Ze.nodeLookup,lib:Ze.lib,isTarget:ee,handleId:le,nodeId:ge,flowId:Ze.rfId,panBy:Ze.panBy,cancelConnection:Ze.cancelConnection,onConnectStart:Ze.onConnectStart,onConnectEnd:(...sn)=>Oe.getState().onConnectEnd?.(...sn),updateConnection:Ze.updateConnection,onConnect:Qn,isValidConnection:x||((...sn)=>Oe.getState().isValidConnection?.(...sn)??!0),getTransform:()=>Oe.getState().transform,getFromHandle:()=>Oe.getState().connection.fromHandle,autoPanSpeed:Ze.autoPanSpeed,dragThreshold:Ze.connectionDragThreshold})}Ae?ie?.(mn):Z?.(mn)},Fe=mn=>{const{onClickConnectStart:Ae,onClickConnectEnd:Ze,connectionClickStartHandle:sn,connectionMode:Sn,isValidConnection:kn,lib:xe,rfId:un,nodeLookup:rt,connection:lt}=Oe.getState();if(!ge||!sn&&!N)return;if(!sn){Ae?.(mn.nativeEvent,{nodeId:ge,handleId:le,handleType:g}),Oe.setState({connectionClickStartHandle:{nodeId:ge,type:g,id:le}});return}const Bt=Vdn(mn.target),hi=x||kn,{connection:Gt,isValid:At}=fke.isValid(mn.nativeEvent,{handle:{nodeId:ge,id:le,type:g},connectionMode:Sn,fromNodeId:sn.nodeId,fromHandleId:sn.id||null,fromType:sn.type,isValidConnection:hi,flowId:un,doc:Bt,lib:xe,nodeLookup:rt});At&&Gt&&Qn(Gt);const st=structuredClone(lt);delete st.inProgress,st.toPosition=st.toHandle?st.toHandle.position:null,Ze?.(mn,st),Oe.setState({connectionClickStartHandle:null})};return F.jsx("div",{"data-handleid":le,"data-nodeid":ge,"data-handlepos":E,"data-id":`${Me}-${ge}-${le}-${g}`,className:Ta(["react-flow__handle",`react-flow__handle-${E}`,"nodrag",ae,G,{source:!ee,target:ee,connectable:M,connectablestart:N,connectableend:P,clickconnecting:ln,connectingfrom:Be,connectingto:rn,valid:Cn,connectionindicator:M&&(!hn||xn)&&(hn||wt?P:N)}]),onMouseDown:Y,onTouchStart:Y,onClick:Pe?Fe:void 0,ref:se,...W,children:U})}const o5=He.memo(k0n(Bqn));function zqn({data:g,isConnectable:E,sourcePosition:x=cr.Bottom}){return F.jsxs(F.Fragment,{children:[g?.label,F.jsx(o5,{type:"source",position:x,isConnectable:E})]})}function Fqn({data:g,isConnectable:E,targetPosition:x=cr.Top,sourcePosition:M=cr.Bottom}){return F.jsxs(F.Fragment,{children:[F.jsx(o5,{type:"target",position:x,isConnectable:E}),g?.label,F.jsx(o5,{type:"source",position:M,isConnectable:E})]})}function Jqn(){return null}function Hqn({data:g,isConnectable:E,targetPosition:x=cr.Top}){return F.jsxs(F.Fragment,{children:[F.jsx(o5,{type:"target",position:x,isConnectable:E}),g?.label]})}const Mue={ArrowUp:{x:0,y:-1},ArrowDown:{x:0,y:1},ArrowLeft:{x:-1,y:0},ArrowRight:{x:1,y:0}},F1n={input:zqn,default:Fqn,output:Hqn,group:Jqn};function Gqn(g){return g.internals.handleBounds===void 0?{width:g.width??g.initialWidth??g.style?.width,height:g.height??g.initialHeight??g.style?.height}:{width:g.width??g.style?.width,height:g.height??g.style?.height}}const qqn=g=>{const{width:E,height:x,x:M,y:N}=tq(g.nodeLookup,{filter:P=>!!P.selected});return{width:Km(E)?E:null,height:Km(x)?x:null,userSelectionActive:g.userSelectionActive,transformString:`translate(${g.transform[0]}px,${g.transform[1]}px) scale(${g.transform[2]}) translate(${M}px,${N}px)`}};function Uqn({onSelectionContextMenu:g,noPanClassName:E,disableKeyboardA11y:x}){const M=Sl(),{width:N,height:P,transformString:k,userSelectionActive:H}=Fu(qqn,El),U=S0n(),G=He.useRef(null);He.useEffect(()=>{x||G.current?.focus({preventScroll:!0})},[x]);const ie=!H&&N!==null&&P!==null;if(E0n({nodeRef:G,disabled:!ie}),!ie)return null;const Z=g?se=>{const le=M.getState().nodes.filter(ee=>ee.selected);g(se,le)}:void 0,W=se=>{Object.prototype.hasOwnProperty.call(Mue,se.key)&&(se.preventDefault(),U({direction:Mue[se.key],factor:se.shiftKey?4:1}))};return F.jsx("div",{className:Ta(["react-flow__nodesselection","react-flow__container",E]),style:{transform:k},children:F.jsx("div",{ref:G,className:"react-flow__nodesselection-rect",onContextMenu:Z,tabIndex:x?void 0:-1,onKeyDown:x?void 0:W,style:{width:N,height:P}})})}const J1n=typeof window<"u"?window:void 0,Xqn=g=>({nodesSelectionActive:g.nodesSelectionActive,userSelectionActive:g.userSelectionActive});function A0n({children:g,onPaneClick:E,onPaneMouseEnter:x,onPaneMouseMove:M,onPaneMouseLeave:N,onPaneContextMenu:P,onPaneScroll:k,paneClickDistance:H,deleteKeyCode:U,selectionKeyCode:G,selectionOnDrag:ie,selectionMode:Z,onSelectionStart:W,onSelectionEnd:se,multiSelectionKeyCode:le,panActivationKeyCode:ee,zoomActivationKeyCode:Oe,elementsSelectable:ge,zoomOnScroll:Pe,zoomOnPinch:ae,panOnScroll:Me,panOnScrollSpeed:Be,panOnScrollMode:rn,zoomOnDoubleClick:ln,panOnDrag:xn,defaultViewport:hn,translateExtent:wt,minZoom:Cn,maxZoom:Qn,preventScrolling:Y,onSelectionContextMenu:Fe,noWheelClassName:mn,noPanClassName:Ae,disableKeyboardA11y:Ze,onViewportChange:sn,isControlledViewport:Sn}){const{nodesSelectionActive:kn,userSelectionActive:xe}=Fu(Xqn,El),un=VG(G,{target:J1n}),rt=VG(ee,{target:J1n}),lt=rt||xn,Bt=rt||Me,hi=ie&<!==!0,Gt=un||xe||hi;return Mqn({deleteKeyCode:U,multiSelectionKeyCode:le}),F.jsx(Oqn,{onPaneContextMenu:P,elementsSelectable:ge,zoomOnScroll:Pe,zoomOnPinch:ae,panOnScroll:Bt,panOnScrollSpeed:Be,panOnScrollMode:rn,zoomOnDoubleClick:ln,panOnDrag:!un&<,defaultViewport:hn,translateExtent:wt,minZoom:Cn,maxZoom:Qn,zoomActivationKeyCode:Oe,preventScrolling:Y,noWheelClassName:mn,noPanClassName:Ae,onViewportChange:sn,isControlledViewport:Sn,paneClickDistance:H,selectionOnDrag:hi,children:F.jsxs(_qn,{onSelectionStart:W,onSelectionEnd:se,onPaneClick:E,onPaneMouseEnter:x,onPaneMouseMove:M,onPaneMouseLeave:N,onPaneContextMenu:P,onPaneScroll:k,panOnDrag:lt,isSelecting:!!Gt,selectionMode:Z,selectionKeyPressed:un,paneClickDistance:H,selectionOnDrag:hi,children:[g,kn&&F.jsx(Uqn,{onSelectionContextMenu:Fe,noPanClassName:Ae,disableKeyboardA11y:Ze})]})})}A0n.displayName="FlowRenderer";const Kqn=He.memo(A0n),Vqn=g=>E=>g?Eke(E.nodeLookup,{x:0,y:0,width:E.width,height:E.height},E.transform,!0).map(x=>x.id):Array.from(E.nodeLookup.keys());function Yqn(g){return Fu(He.useCallback(Vqn(g),[g]),El)}const Qqn=g=>g.updateNodeInternals;function Wqn(){const g=Fu(Qqn),[E]=He.useState(()=>typeof ResizeObserver>"u"?null:new ResizeObserver(x=>{const M=new Map;x.forEach(N=>{const P=N.target.getAttribute("data-id");M.set(P,{id:P,nodeElement:N.target,force:!0})}),g(M)}));return He.useEffect(()=>()=>{E?.disconnect()},[E]),E}function Zqn({node:g,nodeType:E,hasDimensions:x,resizeObserver:M}){const N=Sl(),P=He.useRef(null),k=He.useRef(null),H=He.useRef(g.sourcePosition),U=He.useRef(g.targetPosition),G=He.useRef(E),ie=x&&!!g.internals.handleBounds;return He.useEffect(()=>{P.current&&!g.hidden&&(!ie||k.current!==P.current)&&(k.current&&M?.unobserve(k.current),M?.observe(P.current),k.current=P.current)},[ie,g.hidden]),He.useEffect(()=>()=>{k.current&&(M?.unobserve(k.current),k.current=null)},[]),He.useEffect(()=>{if(P.current){const Z=G.current!==E,W=H.current!==g.sourcePosition,se=U.current!==g.targetPosition;(Z||W||se)&&(G.current=E,H.current=g.sourcePosition,U.current=g.targetPosition,N.getState().updateNodeInternals(new Map([[g.id,{id:g.id,nodeElement:P.current,force:!0}]])))}},[g.id,E,g.sourcePosition,g.targetPosition]),P}function eUn({id:g,onClick:E,onMouseEnter:x,onMouseMove:M,onMouseLeave:N,onContextMenu:P,onDoubleClick:k,nodesDraggable:H,elementsSelectable:U,nodesConnectable:G,nodesFocusable:ie,resizeObserver:Z,noDragClassName:W,noPanClassName:se,disableKeyboardA11y:le,rfId:ee,nodeTypes:Oe,nodeClickDistance:ge,onError:Pe}){const{node:ae,internals:Me,isParent:Be}=Fu(At=>{const st=At.nodeLookup.get(g),Wr=At.parentLookup.has(g);return{node:st,internals:st.internals,isParent:Wr}},El);let rn=ae.type||"default",ln=Oe?.[rn]||F1n[rn];ln===void 0&&(Pe?.("003",u5.error003(rn)),rn="default",ln=Oe?.default||F1n.default);const xn=!!(ae.draggable||H&&typeof ae.draggable>"u"),hn=!!(ae.selectable||U&&typeof ae.selectable>"u"),wt=!!(ae.connectable||G&&typeof ae.connectable>"u"),Cn=!!(ae.focusable||ie&&typeof ae.focusable>"u"),Qn=Sl(),Y=Xdn(ae),Fe=Zqn({node:ae,nodeType:rn,hasDimensions:Y,resizeObserver:Z}),mn=E0n({nodeRef:Fe,disabled:ae.hidden||!xn,noDragClassName:W,handleSelector:ae.dragHandle,nodeId:g,isSelectable:hn,nodeClickDistance:ge}),Ae=S0n();if(ae.hidden)return null;const Ze=i6(ae),sn=Gqn(ae),Sn=hn||xn||E||x||M||N,kn=x?At=>x(At,{...Me.userNode}):void 0,xe=M?At=>M(At,{...Me.userNode}):void 0,un=N?At=>N(At,{...Me.userNode}):void 0,rt=P?At=>P(At,{...Me.userNode}):void 0,lt=k?At=>k(At,{...Me.userNode}):void 0,Bt=At=>{const{selectNodesOnDrag:st,nodeDragThreshold:Wr}=Qn.getState();hn&&(!st||!xn||Wr>0)&&hke({id:g,store:Qn,nodeRef:Fe}),E&&E(At,{...Me.userNode})},hi=At=>{if(!(Ydn(At.nativeEvent)||le)){if(Rdn.includes(At.key)&&hn){const st=At.key==="Escape";hke({id:g,store:Qn,unselect:st,nodeRef:Fe})}else if(xn&&ae.selected&&Object.prototype.hasOwnProperty.call(Mue,At.key)){At.preventDefault();const{ariaLabelConfig:st}=Qn.getState();Qn.setState({ariaLiveMessage:st["node.a11yDescription.ariaLiveMessage"]({direction:At.key.replace("Arrow","").toLowerCase(),x:~~Me.positionAbsolute.x,y:~~Me.positionAbsolute.y})}),Ae({direction:Mue[At.key],factor:At.shiftKey?4:1})}}},Gt=()=>{if(le||!Fe.current?.matches(":focus-visible"))return;const{transform:At,width:st,height:Wr,autoPanOnNodeFocus:Mr,setCenter:ur}=Qn.getState();if(!Mr)return;Eke(new Map([[g,ae]]),{x:0,y:0,width:st,height:Wr},At,!0).length>0||ur(ae.position.x+Ze.width/2,ae.position.y+Ze.height/2,{zoom:At[2]})};return F.jsx("div",{className:Ta(["react-flow__node",`react-flow__node-${rn}`,{[se]:xn},ae.className,{selected:ae.selected,selectable:hn,parent:Be,draggable:xn,dragging:mn}]),ref:Fe,style:{zIndex:Me.z,transform:`translate(${Me.positionAbsolute.x}px,${Me.positionAbsolute.y}px)`,pointerEvents:Sn?"all":"none",visibility:Y?"visible":"hidden",...ae.style,...sn},"data-id":g,"data-testid":`rf__node-${g}`,onMouseEnter:kn,onMouseMove:xe,onMouseLeave:un,onContextMenu:rt,onClick:Bt,onDoubleClick:lt,onKeyDown:Cn?hi:void 0,tabIndex:Cn?0:void 0,onFocus:Cn?Gt:void 0,role:ae.ariaRole??(Cn?"group":void 0),"aria-roledescription":"node","aria-describedby":le?void 0:`${g0n}-${ee}`,"aria-label":ae.ariaLabel,...ae.domAttributes,children:F.jsx(Pqn,{value:g,children:F.jsx(ln,{id:g,data:ae.data,type:rn,positionAbsoluteX:Me.positionAbsolute.x,positionAbsoluteY:Me.positionAbsolute.y,selected:ae.selected??!1,selectable:hn,draggable:xn,deletable:ae.deletable??!0,isConnectable:wt,sourcePosition:ae.sourcePosition,targetPosition:ae.targetPosition,dragging:mn,dragHandle:ae.dragHandle,zIndex:Me.z,parentId:ae.parentId,...Ze})})})}var nUn=He.memo(eUn);const tUn=g=>({nodesDraggable:g.nodesDraggable,nodesConnectable:g.nodesConnectable,nodesFocusable:g.nodesFocusable,elementsSelectable:g.elementsSelectable,onError:g.onError});function M0n(g){const{nodesDraggable:E,nodesConnectable:x,nodesFocusable:M,elementsSelectable:N,onError:P}=Fu(tUn,El),k=Yqn(g.onlyRenderVisibleElements),H=Wqn();return F.jsx("div",{className:"react-flow__nodes",style:zue,children:k.map(U=>F.jsx(nUn,{id:U,nodeTypes:g.nodeTypes,nodeExtent:g.nodeExtent,onClick:g.onNodeClick,onMouseEnter:g.onNodeMouseEnter,onMouseMove:g.onNodeMouseMove,onMouseLeave:g.onNodeMouseLeave,onContextMenu:g.onNodeContextMenu,onDoubleClick:g.onNodeDoubleClick,noDragClassName:g.noDragClassName,noPanClassName:g.noPanClassName,rfId:g.rfId,disableKeyboardA11y:g.disableKeyboardA11y,resizeObserver:H,nodesDraggable:E,nodesConnectable:x,nodesFocusable:M,elementsSelectable:N,nodeClickDistance:g.nodeClickDistance,onError:P},U))})}M0n.displayName="NodeRenderer";const iUn=He.memo(M0n);function rUn(g){return Fu(He.useCallback(x=>{if(!g)return x.edges.map(N=>N.id);const M=[];if(x.width&&x.height)for(const N of x.edges){const P=x.nodeLookup.get(N.source),k=x.nodeLookup.get(N.target);P&&k&&tGn({sourceNode:P,targetNode:k,width:x.width,height:x.height,transform:x.transform})&&M.push(N.id)}return M},[g]),El)}const cUn=({color:g="none",strokeWidth:E=1})=>{const x={strokeWidth:E,...g&&{stroke:g}};return F.jsx("polyline",{className:"arrow",style:x,strokeLinecap:"round",fill:"none",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4"})},uUn=({color:g="none",strokeWidth:E=1})=>{const x={strokeWidth:E,...g&&{stroke:g,fill:g}};return F.jsx("polyline",{className:"arrowclosed",style:x,strokeLinecap:"round",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4 -5,-4"})},H1n={[UG.Arrow]:cUn,[UG.ArrowClosed]:uUn};function oUn(g){const E=Sl();return He.useMemo(()=>Object.prototype.hasOwnProperty.call(H1n,g)?H1n[g]:(E.getState().onError?.("009",u5.error009(g)),null),[g])}const sUn=({id:g,type:E,color:x,width:M=12.5,height:N=12.5,markerUnits:P="strokeWidth",strokeWidth:k,orient:H="auto-start-reverse"})=>{const U=oUn(E);return U?F.jsx("marker",{className:"react-flow__arrowhead",id:g,markerWidth:`${M}`,markerHeight:`${N}`,viewBox:"-10 -10 20 20",markerUnits:P,orient:H,refX:"0",refY:"0",children:F.jsx(U,{color:x,strokeWidth:k})}):null},T0n=({defaultColor:g,rfId:E})=>{const x=Fu(P=>P.edges),M=Fu(P=>P.defaultEdgeOptions),N=He.useMemo(()=>fGn(x,{id:E,defaultColor:g,defaultMarkerStart:M?.markerStart,defaultMarkerEnd:M?.markerEnd}),[x,M,E,g]);return N.length?F.jsx("svg",{className:"react-flow__marker","aria-hidden":"true",children:F.jsx("defs",{children:N.map(P=>F.jsx(sUn,{id:P.id,type:P.type,color:P.color,width:P.width,height:P.height,markerUnits:P.markerUnits,strokeWidth:P.strokeWidth,orient:P.orient},P.id))})}):null};T0n.displayName="MarkerDefinitions";var lUn=He.memo(T0n);function C0n({x:g,y:E,label:x,labelStyle:M,labelShowBg:N=!0,labelBgStyle:P,labelBgPadding:k=[2,4],labelBgBorderRadius:H=2,children:U,className:G,...ie}){const[Z,W]=He.useState({x:1,y:0,width:0,height:0}),se=Ta(["react-flow__edge-textwrapper",G]),le=He.useRef(null);return He.useEffect(()=>{if(le.current){const ee=le.current.getBBox();W({x:ee.x,y:ee.y,width:ee.width,height:ee.height})}},[x]),x?F.jsxs("g",{transform:`translate(${g-Z.width/2} ${E-Z.height/2})`,className:se,visibility:Z.width?"visible":"hidden",...ie,children:[N&&F.jsx("rect",{width:Z.width+2*k[0],x:-k[0],y:-k[1],height:Z.height+2*k[1],className:"react-flow__edge-textbg",style:P,rx:H,ry:H}),F.jsx("text",{className:"react-flow__edge-text",y:Z.height/2,dy:"0.3em",ref:le,style:M,children:x}),U]}):null}C0n.displayName="EdgeText";const fUn=He.memo(C0n);function cq({path:g,labelX:E,labelY:x,label:M,labelStyle:N,labelShowBg:P,labelBgStyle:k,labelBgPadding:H,labelBgBorderRadius:U,interactionWidth:G=20,...ie}){return F.jsxs(F.Fragment,{children:[F.jsx("path",{...ie,d:g,fill:"none",className:Ta(["react-flow__edge-path",ie.className])}),G?F.jsx("path",{d:g,fill:"none",strokeOpacity:0,strokeWidth:G,className:"react-flow__edge-interaction"}):null,M&&Km(E)&&Km(x)?F.jsx(fUn,{x:E,y:x,label:M,labelStyle:N,labelShowBg:P,labelBgStyle:k,labelBgPadding:H,labelBgBorderRadius:U}):null]})}function G1n({pos:g,x1:E,y1:x,x2:M,y2:N}){return g===cr.Left||g===cr.Right?[.5*(E+M),x]:[E,.5*(x+N)]}function O0n({sourceX:g,sourceY:E,sourcePosition:x=cr.Bottom,targetX:M,targetY:N,targetPosition:P=cr.Top}){const[k,H]=G1n({pos:x,x1:g,y1:E,x2:M,y2:N}),[U,G]=G1n({pos:P,x1:M,y1:N,x2:g,y2:E}),[ie,Z,W,se]=Wdn({sourceX:g,sourceY:E,targetX:M,targetY:N,sourceControlX:k,sourceControlY:H,targetControlX:U,targetControlY:G});return[`M${g},${E} C${k},${H} ${U},${G} ${M},${N}`,ie,Z,W,se]}function N0n(g){return He.memo(({id:E,sourceX:x,sourceY:M,targetX:N,targetY:P,sourcePosition:k,targetPosition:H,label:U,labelStyle:G,labelShowBg:ie,labelBgStyle:Z,labelBgPadding:W,labelBgBorderRadius:se,style:le,markerEnd:ee,markerStart:Oe,interactionWidth:ge})=>{const[Pe,ae,Me]=O0n({sourceX:x,sourceY:M,sourcePosition:k,targetX:N,targetY:P,targetPosition:H}),Be=g.isInternal?void 0:E;return F.jsx(cq,{id:Be,path:Pe,labelX:ae,labelY:Me,label:U,labelStyle:G,labelShowBg:ie,labelBgStyle:Z,labelBgPadding:W,labelBgBorderRadius:se,style:le,markerEnd:ee,markerStart:Oe,interactionWidth:ge})})}const aUn=N0n({isInternal:!1}),D0n=N0n({isInternal:!0});aUn.displayName="SimpleBezierEdge";D0n.displayName="SimpleBezierEdgeInternal";function I0n(g){return He.memo(({id:E,sourceX:x,sourceY:M,targetX:N,targetY:P,label:k,labelStyle:H,labelShowBg:U,labelBgStyle:G,labelBgPadding:ie,labelBgBorderRadius:Z,style:W,sourcePosition:se=cr.Bottom,targetPosition:le=cr.Top,markerEnd:ee,markerStart:Oe,pathOptions:ge,interactionWidth:Pe})=>{const[ae,Me,Be]=Aue({sourceX:x,sourceY:M,sourcePosition:se,targetX:N,targetY:P,targetPosition:le,borderRadius:ge?.borderRadius,offset:ge?.offset,stepPosition:ge?.stepPosition}),rn=g.isInternal?void 0:E;return F.jsx(cq,{id:rn,path:ae,labelX:Me,labelY:Be,label:k,labelStyle:H,labelShowBg:U,labelBgStyle:G,labelBgPadding:ie,labelBgBorderRadius:Z,style:W,markerEnd:ee,markerStart:Oe,interactionWidth:Pe})})}const _0n=I0n({isInternal:!1}),L0n=I0n({isInternal:!0});_0n.displayName="SmoothStepEdge";L0n.displayName="SmoothStepEdgeInternal";function P0n(g){return He.memo(({id:E,...x})=>{const M=g.isInternal?void 0:E;return F.jsx(_0n,{...x,id:M,pathOptions:He.useMemo(()=>({borderRadius:0,offset:x.pathOptions?.offset}),[x.pathOptions?.offset])})})}const hUn=P0n({isInternal:!1}),$0n=P0n({isInternal:!0});hUn.displayName="StepEdge";$0n.displayName="StepEdgeInternal";function R0n(g){return He.memo(({id:E,sourceX:x,sourceY:M,targetX:N,targetY:P,label:k,labelStyle:H,labelShowBg:U,labelBgStyle:G,labelBgPadding:ie,labelBgBorderRadius:Z,style:W,markerEnd:se,markerStart:le,interactionWidth:ee})=>{const[Oe,ge,Pe]=n0n({sourceX:x,sourceY:M,targetX:N,targetY:P}),ae=g.isInternal?void 0:E;return F.jsx(cq,{id:ae,path:Oe,labelX:ge,labelY:Pe,label:k,labelStyle:H,labelShowBg:U,labelBgStyle:G,labelBgPadding:ie,labelBgBorderRadius:Z,style:W,markerEnd:se,markerStart:le,interactionWidth:ee})})}const dUn=R0n({isInternal:!1}),B0n=R0n({isInternal:!0});dUn.displayName="StraightEdge";B0n.displayName="StraightEdgeInternal";function z0n(g){return He.memo(({id:E,sourceX:x,sourceY:M,targetX:N,targetY:P,sourcePosition:k=cr.Bottom,targetPosition:H=cr.Top,label:U,labelStyle:G,labelShowBg:ie,labelBgStyle:Z,labelBgPadding:W,labelBgBorderRadius:se,style:le,markerEnd:ee,markerStart:Oe,pathOptions:ge,interactionWidth:Pe})=>{const[ae,Me,Be]=Zdn({sourceX:x,sourceY:M,sourcePosition:k,targetX:N,targetY:P,targetPosition:H,curvature:ge?.curvature}),rn=g.isInternal?void 0:E;return F.jsx(cq,{id:rn,path:ae,labelX:Me,labelY:Be,label:U,labelStyle:G,labelShowBg:ie,labelBgStyle:Z,labelBgPadding:W,labelBgBorderRadius:se,style:le,markerEnd:ee,markerStart:Oe,interactionWidth:Pe})})}const bUn=z0n({isInternal:!1}),F0n=z0n({isInternal:!0});bUn.displayName="BezierEdge";F0n.displayName="BezierEdgeInternal";const q1n={default:F0n,straight:B0n,step:$0n,smoothstep:L0n,simplebezier:D0n},U1n={sourceX:null,sourceY:null,targetX:null,targetY:null,sourcePosition:null,targetPosition:null},gUn=(g,E,x)=>x===cr.Left?g-E:x===cr.Right?g+E:g,wUn=(g,E,x)=>x===cr.Top?g-E:x===cr.Bottom?g+E:g,X1n="react-flow__edgeupdater";function K1n({position:g,centerX:E,centerY:x,radius:M=10,onMouseDown:N,onMouseEnter:P,onMouseOut:k,type:H}){return F.jsx("circle",{onMouseDown:N,onMouseEnter:P,onMouseOut:k,className:Ta([X1n,`${X1n}-${H}`]),cx:gUn(E,M,g),cy:wUn(x,M,g),r:M,stroke:"transparent",fill:"transparent"})}function pUn({isReconnectable:g,reconnectRadius:E,edge:x,sourceX:M,sourceY:N,targetX:P,targetY:k,sourcePosition:H,targetPosition:U,onReconnect:G,onReconnectStart:ie,onReconnectEnd:Z,setReconnecting:W,setUpdateHover:se}){const le=Sl(),ee=(Me,Be)=>{if(Me.button!==0)return;const{autoPanOnConnect:rn,domNode:ln,connectionMode:xn,connectionRadius:hn,lib:wt,onConnectStart:Cn,cancelConnection:Qn,nodeLookup:Y,rfId:Fe,panBy:mn,updateConnection:Ae}=le.getState(),Ze=Be.type==="target",sn=(xe,un)=>{W(!1),Z?.(xe,x,Be.type,un)},Sn=xe=>G?.(x,xe),kn=(xe,un)=>{W(!0),ie?.(Me,x,Be.type),Cn?.(xe,un)};fke.onPointerDown(Me.nativeEvent,{autoPanOnConnect:rn,connectionMode:xn,connectionRadius:hn,domNode:ln,handleId:Be.id,nodeId:Be.nodeId,nodeLookup:Y,isTarget:Ze,edgeUpdaterType:Be.type,lib:wt,flowId:Fe,cancelConnection:Qn,panBy:mn,isValidConnection:(...xe)=>le.getState().isValidConnection?.(...xe)??!0,onConnect:Sn,onConnectStart:kn,onConnectEnd:(...xe)=>le.getState().onConnectEnd?.(...xe),onReconnectEnd:sn,updateConnection:Ae,getTransform:()=>le.getState().transform,getFromHandle:()=>le.getState().connection.fromHandle,dragThreshold:le.getState().connectionDragThreshold,handleDomNode:Me.currentTarget})},Oe=Me=>ee(Me,{nodeId:x.target,id:x.targetHandle??null,type:"target"}),ge=Me=>ee(Me,{nodeId:x.source,id:x.sourceHandle??null,type:"source"}),Pe=()=>se(!0),ae=()=>se(!1);return F.jsxs(F.Fragment,{children:[(g===!0||g==="source")&&F.jsx(K1n,{position:H,centerX:M,centerY:N,radius:E,onMouseDown:Oe,onMouseEnter:Pe,onMouseOut:ae,type:"source"}),(g===!0||g==="target")&&F.jsx(K1n,{position:U,centerX:P,centerY:k,radius:E,onMouseDown:ge,onMouseEnter:Pe,onMouseOut:ae,type:"target"})]})}function mUn({id:g,edgesFocusable:E,edgesReconnectable:x,elementsSelectable:M,onClick:N,onDoubleClick:P,onContextMenu:k,onMouseEnter:H,onMouseMove:U,onMouseLeave:G,reconnectRadius:ie,onReconnect:Z,onReconnectStart:W,onReconnectEnd:se,rfId:le,edgeTypes:ee,noPanClassName:Oe,onError:ge,disableKeyboardA11y:Pe}){let ae=Fu(ur=>ur.edgeLookup.get(g));const Me=Fu(ur=>ur.defaultEdgeOptions);ae=Me?{...Me,...ae}:ae;let Be=ae.type||"default",rn=ee?.[Be]||q1n[Be];rn===void 0&&(ge?.("011",u5.error011(Be)),Be="default",rn=ee?.default||q1n.default);const ln=!!(ae.focusable||E&&typeof ae.focusable>"u"),xn=typeof Z<"u"&&(ae.reconnectable||x&&typeof ae.reconnectable>"u"),hn=!!(ae.selectable||M&&typeof ae.selectable>"u"),wt=He.useRef(null),[Cn,Qn]=He.useState(!1),[Y,Fe]=He.useState(!1),mn=Sl(),{zIndex:Ae,sourceX:Ze,sourceY:sn,targetX:Sn,targetY:kn,sourcePosition:xe,targetPosition:un}=Fu(He.useCallback(ur=>{const mi=ur.nodeLookup.get(ae.source),Fi=ur.nodeLookup.get(ae.target);if(!mi||!Fi)return{zIndex:ae.zIndex,...U1n};const fu=lGn({id:g,sourceNode:mi,targetNode:Fi,sourceHandle:ae.sourceHandle||null,targetHandle:ae.targetHandle||null,connectionMode:ur.connectionMode,onError:ge});return{zIndex:nGn({selected:ae.selected,zIndex:ae.zIndex,sourceNode:mi,targetNode:Fi,elevateOnSelect:ur.elevateEdgesOnSelect,zIndexMode:ur.zIndexMode}),...fu||U1n}},[ae.source,ae.target,ae.sourceHandle,ae.targetHandle,ae.selected,ae.zIndex]),El),rt=He.useMemo(()=>ae.markerStart?`url('#${ske(ae.markerStart,le)}')`:void 0,[ae.markerStart,le]),lt=He.useMemo(()=>ae.markerEnd?`url('#${ske(ae.markerEnd,le)}')`:void 0,[ae.markerEnd,le]);if(ae.hidden||Ze===null||sn===null||Sn===null||kn===null)return null;const Bt=ur=>{const{addSelectedEdges:mi,unselectNodesAndEdges:Fi,multiSelectionActive:fu}=mn.getState();hn&&(mn.setState({nodesSelectionActive:!1}),ae.selected&&fu?(Fi({nodes:[],edges:[ae]}),wt.current?.blur()):mi([g])),N&&N(ur,ae)},hi=P?ur=>{P(ur,{...ae})}:void 0,Gt=k?ur=>{k(ur,{...ae})}:void 0,At=H?ur=>{H(ur,{...ae})}:void 0,st=U?ur=>{U(ur,{...ae})}:void 0,Wr=G?ur=>{G(ur,{...ae})}:void 0,Mr=ur=>{if(!Pe&&Rdn.includes(ur.key)&&hn){const{unselectNodesAndEdges:mi,addSelectedEdges:Fi}=mn.getState();ur.key==="Escape"?(wt.current?.blur(),mi({edges:[ae]})):Fi([g])}};return F.jsx("svg",{style:{zIndex:Ae},children:F.jsxs("g",{className:Ta(["react-flow__edge",`react-flow__edge-${Be}`,ae.className,Oe,{selected:ae.selected,animated:ae.animated,inactive:!hn&&!N,updating:Cn,selectable:hn}]),onClick:Bt,onDoubleClick:hi,onContextMenu:Gt,onMouseEnter:At,onMouseMove:st,onMouseLeave:Wr,onKeyDown:ln?Mr:void 0,tabIndex:ln?0:void 0,role:ae.ariaRole??(ln?"group":"img"),"aria-roledescription":"edge","data-id":g,"data-testid":`rf__edge-${g}`,"aria-label":ae.ariaLabel===null?void 0:ae.ariaLabel||`Edge from ${ae.source} to ${ae.target}`,"aria-describedby":ln?`${w0n}-${le}`:void 0,ref:wt,...ae.domAttributes,children:[!Y&&F.jsx(rn,{id:g,source:ae.source,target:ae.target,type:ae.type,selected:ae.selected,animated:ae.animated,selectable:hn,deletable:ae.deletable??!0,label:ae.label,labelStyle:ae.labelStyle,labelShowBg:ae.labelShowBg,labelBgStyle:ae.labelBgStyle,labelBgPadding:ae.labelBgPadding,labelBgBorderRadius:ae.labelBgBorderRadius,sourceX:Ze,sourceY:sn,targetX:Sn,targetY:kn,sourcePosition:xe,targetPosition:un,data:ae.data,style:ae.style,sourceHandleId:ae.sourceHandle,targetHandleId:ae.targetHandle,markerStart:rt,markerEnd:lt,pathOptions:"pathOptions"in ae?ae.pathOptions:void 0,interactionWidth:ae.interactionWidth}),xn&&F.jsx(pUn,{edge:ae,isReconnectable:xn,reconnectRadius:ie,onReconnect:Z,onReconnectStart:W,onReconnectEnd:se,sourceX:Ze,sourceY:sn,targetX:Sn,targetY:kn,sourcePosition:xe,targetPosition:un,setUpdateHover:Qn,setReconnecting:Fe})]})})}var vUn=He.memo(mUn);const yUn=g=>({edgesFocusable:g.edgesFocusable,edgesReconnectable:g.edgesReconnectable,elementsSelectable:g.elementsSelectable,connectionMode:g.connectionMode,onError:g.onError});function J0n({defaultMarkerColor:g,onlyRenderVisibleElements:E,rfId:x,edgeTypes:M,noPanClassName:N,onReconnect:P,onEdgeContextMenu:k,onEdgeMouseEnter:H,onEdgeMouseMove:U,onEdgeMouseLeave:G,onEdgeClick:ie,reconnectRadius:Z,onEdgeDoubleClick:W,onReconnectStart:se,onReconnectEnd:le,disableKeyboardA11y:ee}){const{edgesFocusable:Oe,edgesReconnectable:ge,elementsSelectable:Pe,onError:ae}=Fu(yUn,El),Me=rUn(E);return F.jsxs("div",{className:"react-flow__edges",children:[F.jsx(lUn,{defaultColor:g,rfId:x}),Me.map(Be=>F.jsx(vUn,{id:Be,edgesFocusable:Oe,edgesReconnectable:ge,elementsSelectable:Pe,noPanClassName:N,onReconnect:P,onContextMenu:k,onMouseEnter:H,onMouseMove:U,onMouseLeave:G,onClick:ie,reconnectRadius:Z,onDoubleClick:W,onReconnectStart:se,onReconnectEnd:le,rfId:x,onError:ae,edgeTypes:M,disableKeyboardA11y:ee},Be))]})}J0n.displayName="EdgeRenderer";const kUn=He.memo(J0n),jUn=g=>`translate(${g.transform[0]}px,${g.transform[1]}px) scale(${g.transform[2]})`;function EUn({children:g}){const E=Fu(jUn);return F.jsx("div",{className:"react-flow__viewport xyflow__viewport react-flow__container",style:{transform:E},children:g})}function SUn(g){const E=Nke(),x=He.useRef(!1);He.useEffect(()=>{!x.current&&E.viewportInitialized&&g&&(setTimeout(()=>g(E),1),x.current=!0)},[g,E.viewportInitialized])}const xUn=g=>g.panZoom?.syncViewport;function AUn(g){const E=Fu(xUn),x=Sl();return He.useEffect(()=>{g&&(E?.(g),x.setState({transform:[g.x,g.y,g.zoom]}))},[g,E]),null}function MUn(g){return g.connection.inProgress?{...g.connection,to:rq(g.connection.to,g.transform)}:{...g.connection}}function TUn(g){return MUn}function CUn(g){const E=TUn();return Fu(E,El)}const OUn=g=>({nodesConnectable:g.nodesConnectable,isValid:g.connection.isValid,inProgress:g.connection.inProgress,width:g.width,height:g.height});function NUn({containerStyle:g,style:E,type:x,component:M}){const{nodesConnectable:N,width:P,height:k,isValid:H,inProgress:U}=Fu(OUn,El);return!(P&&N&&U)?null:F.jsx("svg",{style:g,width:P,height:k,className:"react-flow__connectionline react-flow__container",children:F.jsx("g",{className:Ta(["react-flow__connection",Fdn(H)]),children:F.jsx(H0n,{style:E,type:x,CustomComponent:M,isValid:H})})})}const H0n=({style:g,type:E=W7.Bezier,CustomComponent:x,isValid:M})=>{const{inProgress:N,from:P,fromNode:k,fromHandle:H,fromPosition:U,to:G,toNode:ie,toHandle:Z,toPosition:W,pointer:se}=CUn();if(!N)return;if(x)return F.jsx(x,{connectionLineType:E,connectionLineStyle:g,fromNode:k,fromHandle:H,fromX:P.x,fromY:P.y,toX:G.x,toY:G.y,fromPosition:U,toPosition:W,connectionStatus:Fdn(M),toNode:ie,toHandle:Z,pointer:se});let le="";const ee={sourceX:P.x,sourceY:P.y,sourcePosition:U,targetX:G.x,targetY:G.y,targetPosition:W};switch(E){case W7.Bezier:[le]=Zdn(ee);break;case W7.SimpleBezier:[le]=O0n(ee);break;case W7.Step:[le]=Aue({...ee,borderRadius:0});break;case W7.SmoothStep:[le]=Aue(ee);break;default:[le]=n0n(ee)}return F.jsx("path",{d:le,fill:"none",className:"react-flow__connection-path",style:g})};H0n.displayName="ConnectionLine";const DUn={};function V1n(g=DUn){He.useRef(g),Sl(),He.useEffect(()=>{},[g])}function IUn(){Sl(),He.useRef(!1),He.useEffect(()=>{},[])}function G0n({nodeTypes:g,edgeTypes:E,onInit:x,onNodeClick:M,onEdgeClick:N,onNodeDoubleClick:P,onEdgeDoubleClick:k,onNodeMouseEnter:H,onNodeMouseMove:U,onNodeMouseLeave:G,onNodeContextMenu:ie,onSelectionContextMenu:Z,onSelectionStart:W,onSelectionEnd:se,connectionLineType:le,connectionLineStyle:ee,connectionLineComponent:Oe,connectionLineContainerStyle:ge,selectionKeyCode:Pe,selectionOnDrag:ae,selectionMode:Me,multiSelectionKeyCode:Be,panActivationKeyCode:rn,zoomActivationKeyCode:ln,deleteKeyCode:xn,onlyRenderVisibleElements:hn,elementsSelectable:wt,defaultViewport:Cn,translateExtent:Qn,minZoom:Y,maxZoom:Fe,preventScrolling:mn,defaultMarkerColor:Ae,zoomOnScroll:Ze,zoomOnPinch:sn,panOnScroll:Sn,panOnScrollSpeed:kn,panOnScrollMode:xe,zoomOnDoubleClick:un,panOnDrag:rt,onPaneClick:lt,onPaneMouseEnter:Bt,onPaneMouseMove:hi,onPaneMouseLeave:Gt,onPaneScroll:At,onPaneContextMenu:st,paneClickDistance:Wr,nodeClickDistance:Mr,onEdgeContextMenu:ur,onEdgeMouseEnter:mi,onEdgeMouseMove:Fi,onEdgeMouseLeave:fu,reconnectRadius:Eu,onReconnect:Ws,onReconnectStart:Dh,onReconnectEnd:ef,noDragClassName:ch,noWheelClassName:kc,noPanClassName:cd,disableKeyboardA11y:jb,nodeExtent:Rs,rfId:c0,viewport:u0,onViewportChange:o0}){return V1n(g),V1n(E),IUn(),SUn(x),AUn(u0),F.jsx(Kqn,{onPaneClick:lt,onPaneMouseEnter:Bt,onPaneMouseMove:hi,onPaneMouseLeave:Gt,onPaneContextMenu:st,onPaneScroll:At,paneClickDistance:Wr,deleteKeyCode:xn,selectionKeyCode:Pe,selectionOnDrag:ae,selectionMode:Me,onSelectionStart:W,onSelectionEnd:se,multiSelectionKeyCode:Be,panActivationKeyCode:rn,zoomActivationKeyCode:ln,elementsSelectable:wt,zoomOnScroll:Ze,zoomOnPinch:sn,zoomOnDoubleClick:un,panOnScroll:Sn,panOnScrollSpeed:kn,panOnScrollMode:xe,panOnDrag:rt,defaultViewport:Cn,translateExtent:Qn,minZoom:Y,maxZoom:Fe,onSelectionContextMenu:Z,preventScrolling:mn,noDragClassName:ch,noWheelClassName:kc,noPanClassName:cd,disableKeyboardA11y:jb,onViewportChange:o0,isControlledViewport:!!u0,children:F.jsxs(EUn,{children:[F.jsx(kUn,{edgeTypes:E,onEdgeClick:N,onEdgeDoubleClick:k,onReconnect:Ws,onReconnectStart:Dh,onReconnectEnd:ef,onlyRenderVisibleElements:hn,onEdgeContextMenu:ur,onEdgeMouseEnter:mi,onEdgeMouseMove:Fi,onEdgeMouseLeave:fu,reconnectRadius:Eu,defaultMarkerColor:Ae,noPanClassName:cd,disableKeyboardA11y:jb,rfId:c0}),F.jsx(NUn,{style:ee,type:le,component:Oe,containerStyle:ge}),F.jsx("div",{className:"react-flow__edgelabel-renderer"}),F.jsx(iUn,{nodeTypes:g,onNodeClick:M,onNodeDoubleClick:P,onNodeMouseEnter:H,onNodeMouseMove:U,onNodeMouseLeave:G,onNodeContextMenu:ie,nodeClickDistance:Mr,onlyRenderVisibleElements:hn,noPanClassName:cd,noDragClassName:ch,disableKeyboardA11y:jb,nodeExtent:Rs,rfId:c0}),F.jsx("div",{className:"react-flow__viewport-portal"})]})})}G0n.displayName="GraphView";const _Un=He.memo(G0n),Y1n=({nodes:g,edges:E,defaultNodes:x,defaultEdges:M,width:N,height:P,fitView:k,fitViewOptions:H,minZoom:U=.5,maxZoom:G=2,nodeOrigin:ie,nodeExtent:Z,zIndexMode:W="basic"}={})=>{const se=new Map,le=new Map,ee=new Map,Oe=new Map,ge=M??E??[],Pe=x??g??[],ae=ie??[0,0],Me=Z??GG;r0n(ee,Oe,ge);const{nodesInitialized:Be}=lke(Pe,se,le,{nodeOrigin:ae,nodeExtent:Me,zIndexMode:W});let rn=[0,0,1];if(k&&N&&P){const ln=tq(se,{filter:Cn=>!!((Cn.width||Cn.initialWidth)&&(Cn.height||Cn.initialHeight))}),{x:xn,y:hn,zoom:wt}=Ske(ln,N,P,U,G,H?.padding??.1);rn=[xn,hn,wt]}return{rfId:"1",width:N??0,height:P??0,transform:rn,nodes:Pe,nodesInitialized:Be,nodeLookup:se,parentLookup:le,edges:ge,edgeLookup:Oe,connectionLookup:ee,onNodesChange:null,onEdgesChange:null,hasDefaultNodes:x!==void 0,hasDefaultEdges:M!==void 0,panZoom:null,minZoom:U,maxZoom:G,translateExtent:GG,nodeExtent:Me,nodesSelectionActive:!1,userSelectionActive:!1,userSelectionRect:null,connectionMode:bI.Strict,domNode:null,paneDragging:!1,noPanClassName:"nopan",nodeOrigin:ae,nodeDragThreshold:1,connectionDragThreshold:1,snapGrid:[15,15],snapToGrid:!1,nodesDraggable:!0,nodesConnectable:!0,nodesFocusable:!0,edgesFocusable:!0,edgesReconnectable:!0,elementsSelectable:!0,elevateNodesOnSelect:!0,elevateEdgesOnSelect:!0,selectNodesOnDrag:!0,multiSelectionActive:!1,fitViewQueued:k??!1,fitViewOptions:H,fitViewResolver:null,connection:{...zdn},connectionClickStartHandle:null,connectOnClick:!0,ariaLiveMessage:"",autoPanOnConnect:!0,autoPanOnNodeDrag:!0,autoPanOnNodeFocus:!0,autoPanSpeed:15,connectionRadius:20,onError:VHn,isValidConnection:void 0,onSelectionChangeHandlers:[],lib:"react",debug:!1,ariaLabelConfig:Bdn,zIndexMode:W,onNodesChangeMiddlewareMap:new Map,onEdgesChangeMiddlewareMap:new Map}},LUn=({nodes:g,edges:E,defaultNodes:x,defaultEdges:M,width:N,height:P,fitView:k,fitViewOptions:H,minZoom:U,maxZoom:G,nodeOrigin:ie,nodeExtent:Z,zIndexMode:W})=>ZGn((se,le)=>{async function ee(){const{nodeLookup:Oe,panZoom:ge,fitViewOptions:Pe,fitViewResolver:ae,width:Me,height:Be,minZoom:rn,maxZoom:ln}=le();ge&&(await XHn({nodes:Oe,width:Me,height:Be,panZoom:ge,minZoom:rn,maxZoom:ln},Pe),ae?.resolve(!0),se({fitViewResolver:null}))}return{...Y1n({nodes:g,edges:E,width:N,height:P,fitView:k,fitViewOptions:H,minZoom:U,maxZoom:G,nodeOrigin:ie,nodeExtent:Z,defaultNodes:x,defaultEdges:M,zIndexMode:W}),setNodes:Oe=>{const{nodeLookup:ge,parentLookup:Pe,nodeOrigin:ae,elevateNodesOnSelect:Me,fitViewQueued:Be,zIndexMode:rn,nodesSelectionActive:ln}=le(),{nodesInitialized:xn,hasSelectedNodes:hn}=lke(Oe,ge,Pe,{nodeOrigin:ae,nodeExtent:Z,elevateNodesOnSelect:Me,checkEquality:!0,zIndexMode:rn}),wt=ln&&hn;Be&&xn?(ee(),se({nodes:Oe,nodesInitialized:xn,fitViewQueued:!1,fitViewOptions:void 0,nodesSelectionActive:wt})):se({nodes:Oe,nodesInitialized:xn,nodesSelectionActive:wt})},setEdges:Oe=>{const{connectionLookup:ge,edgeLookup:Pe}=le();r0n(ge,Pe,Oe),se({edges:Oe})},setDefaultNodesAndEdges:(Oe,ge)=>{if(Oe){const{setNodes:Pe}=le();Pe(Oe),se({hasDefaultNodes:!0})}if(ge){const{setEdges:Pe}=le();Pe(ge),se({hasDefaultEdges:!0})}},updateNodeInternals:Oe=>{const{triggerNodeChanges:ge,nodeLookup:Pe,parentLookup:ae,domNode:Me,nodeOrigin:Be,nodeExtent:rn,debug:ln,fitViewQueued:xn,zIndexMode:hn}=le(),{changes:wt,updatedInternals:Cn}=pGn(Oe,Pe,ae,Me,Be,rn,hn);Cn&&(dGn(Pe,ae,{nodeOrigin:Be,nodeExtent:rn,zIndexMode:hn}),xn?(ee(),se({fitViewQueued:!1,fitViewOptions:void 0})):se({}),wt?.length>0&&(ln&&console.log("React Flow: trigger node changes",wt),ge?.(wt)))},updateNodePositions:(Oe,ge=!1)=>{const Pe=[];let ae=[];const{nodeLookup:Me,triggerNodeChanges:Be,connection:rn,updateConnection:ln,onNodesChangeMiddlewareMap:xn}=le();for(const[hn,wt]of Oe){const Cn=Me.get(hn),Qn=!!(Cn?.expandParent&&Cn?.parentId&&wt?.position),Y={id:hn,type:"position",position:Qn?{x:Math.max(0,wt.position.x),y:Math.max(0,wt.position.y)}:wt.position,dragging:ge};if(Cn&&rn.inProgress&&rn.fromNode.id===Cn.id){const Fe=AA(Cn,rn.fromHandle,cr.Left,!0);ln({...rn,from:Fe})}Qn&&Cn.parentId&&Pe.push({id:hn,parentId:Cn.parentId,rect:{...wt.internals.positionAbsolute,width:wt.measured.width??0,height:wt.measured.height??0}}),ae.push(Y)}if(Pe.length>0){const{parentLookup:hn,nodeOrigin:wt}=le(),Cn=Oke(Pe,Me,hn,wt);ae.push(...Cn)}for(const hn of xn.values())ae=hn(ae);Be(ae)},triggerNodeChanges:Oe=>{const{onNodesChange:ge,setNodes:Pe,nodes:ae,hasDefaultNodes:Me,debug:Be}=le();if(Oe?.length){if(Me){const rn=v0n(Oe,ae);Pe(rn)}Be&&console.log("React Flow: trigger node changes",Oe),ge?.(Oe)}},triggerEdgeChanges:Oe=>{const{onEdgesChange:ge,setEdges:Pe,edges:ae,hasDefaultEdges:Me,debug:Be}=le();if(Oe?.length){if(Me){const rn=y0n(Oe,ae);Pe(rn)}Be&&console.log("React Flow: trigger edge changes",Oe),ge?.(Oe)}},addSelectedNodes:Oe=>{const{multiSelectionActive:ge,edgeLookup:Pe,nodeLookup:ae,triggerNodeChanges:Me,triggerEdgeChanges:Be}=le();if(ge){const rn=Oe.map(ln=>vA(ln,!0));Me(rn);return}Me(lI(ae,new Set([...Oe]),!0)),Be(lI(Pe))},addSelectedEdges:Oe=>{const{multiSelectionActive:ge,edgeLookup:Pe,nodeLookup:ae,triggerNodeChanges:Me,triggerEdgeChanges:Be}=le();if(ge){const rn=Oe.map(ln=>vA(ln,!0));Be(rn);return}Be(lI(Pe,new Set([...Oe]))),Me(lI(ae,new Set,!0))},unselectNodesAndEdges:({nodes:Oe,edges:ge}={})=>{const{edges:Pe,nodes:ae,nodeLookup:Me,triggerNodeChanges:Be,triggerEdgeChanges:rn}=le(),ln=Oe||ae,xn=ge||Pe,hn=[];for(const Cn of ln){if(!Cn.selected)continue;const Qn=Me.get(Cn.id);Qn&&(Qn.selected=!1),hn.push(vA(Cn.id,!1))}const wt=[];for(const Cn of xn)Cn.selected&&wt.push(vA(Cn.id,!1));Be(hn),rn(wt)},setMinZoom:Oe=>{const{panZoom:ge,maxZoom:Pe}=le();ge?.setScaleExtent([Oe,Pe]),se({minZoom:Oe})},setMaxZoom:Oe=>{const{panZoom:ge,minZoom:Pe}=le();ge?.setScaleExtent([Pe,Oe]),se({maxZoom:Oe})},setTranslateExtent:Oe=>{le().panZoom?.setTranslateExtent(Oe),se({translateExtent:Oe})},resetSelectedElements:()=>{const{edges:Oe,nodes:ge,triggerNodeChanges:Pe,triggerEdgeChanges:ae,elementsSelectable:Me}=le();if(!Me)return;const Be=ge.reduce((ln,xn)=>xn.selected?[...ln,vA(xn.id,!1)]:ln,[]),rn=Oe.reduce((ln,xn)=>xn.selected?[...ln,vA(xn.id,!1)]:ln,[]);Pe(Be),ae(rn)},setNodeExtent:Oe=>{const{nodes:ge,nodeLookup:Pe,parentLookup:ae,nodeOrigin:Me,elevateNodesOnSelect:Be,nodeExtent:rn,zIndexMode:ln}=le();Oe[0][0]===rn[0][0]&&Oe[0][1]===rn[0][1]&&Oe[1][0]===rn[1][0]&&Oe[1][1]===rn[1][1]||(lke(ge,Pe,ae,{nodeOrigin:Me,nodeExtent:Oe,elevateNodesOnSelect:Be,checkEquality:!1,zIndexMode:ln}),se({nodeExtent:Oe}))},panBy:Oe=>{const{transform:ge,width:Pe,height:ae,panZoom:Me,translateExtent:Be}=le();return mGn({delta:Oe,panZoom:Me,transform:ge,translateExtent:Be,width:Pe,height:ae})},setCenter:async(Oe,ge,Pe)=>{const{width:ae,height:Me,maxZoom:Be,panZoom:rn}=le();if(!rn)return Promise.resolve(!1);const ln=typeof Pe?.zoom<"u"?Pe.zoom:Be;return await rn.setViewport({x:ae/2-Oe*ln,y:Me/2-ge*ln,zoom:ln},{duration:Pe?.duration,ease:Pe?.ease,interpolate:Pe?.interpolate}),Promise.resolve(!0)},cancelConnection:()=>{se({connection:{...zdn}})},updateConnection:Oe=>{se({connection:Oe})},reset:()=>se({...Y1n()})}},Object.is);function PUn({initialNodes:g,initialEdges:E,defaultNodes:x,defaultEdges:M,initialWidth:N,initialHeight:P,initialMinZoom:k,initialMaxZoom:H,initialFitViewOptions:U,fitView:G,nodeOrigin:ie,nodeExtent:Z,zIndexMode:W,children:se}){const[le]=He.useState(()=>LUn({nodes:g,edges:E,defaultNodes:x,defaultEdges:M,width:N,height:P,fitView:G,minZoom:k,maxZoom:H,fitViewOptions:U,nodeOrigin:ie,nodeExtent:Z,zIndexMode:W}));return F.jsx(nqn,{value:le,children:F.jsx(Eqn,{children:se})})}function $Un({children:g,nodes:E,edges:x,defaultNodes:M,defaultEdges:N,width:P,height:k,fitView:H,fitViewOptions:U,minZoom:G,maxZoom:ie,nodeOrigin:Z,nodeExtent:W,zIndexMode:se}){return He.useContext(Rue)?F.jsx(F.Fragment,{children:g}):F.jsx(PUn,{initialNodes:E,initialEdges:x,defaultNodes:M,defaultEdges:N,initialWidth:P,initialHeight:k,fitView:H,initialFitViewOptions:U,initialMinZoom:G,initialMaxZoom:ie,nodeOrigin:Z,nodeExtent:W,zIndexMode:se,children:g})}const RUn={width:"100%",height:"100%",overflow:"hidden",position:"relative",zIndex:0};function BUn({nodes:g,edges:E,defaultNodes:x,defaultEdges:M,className:N,nodeTypes:P,edgeTypes:k,onNodeClick:H,onEdgeClick:U,onInit:G,onMove:ie,onMoveStart:Z,onMoveEnd:W,onConnect:se,onConnectStart:le,onConnectEnd:ee,onClickConnectStart:Oe,onClickConnectEnd:ge,onNodeMouseEnter:Pe,onNodeMouseMove:ae,onNodeMouseLeave:Me,onNodeContextMenu:Be,onNodeDoubleClick:rn,onNodeDragStart:ln,onNodeDrag:xn,onNodeDragStop:hn,onNodesDelete:wt,onEdgesDelete:Cn,onDelete:Qn,onSelectionChange:Y,onSelectionDragStart:Fe,onSelectionDrag:mn,onSelectionDragStop:Ae,onSelectionContextMenu:Ze,onSelectionStart:sn,onSelectionEnd:Sn,onBeforeDelete:kn,connectionMode:xe,connectionLineType:un=W7.Bezier,connectionLineStyle:rt,connectionLineComponent:lt,connectionLineContainerStyle:Bt,deleteKeyCode:hi="Backspace",selectionKeyCode:Gt="Shift",selectionOnDrag:At=!1,selectionMode:st=qG.Full,panActivationKeyCode:Wr="Space",multiSelectionKeyCode:Mr=KG()?"Meta":"Control",zoomActivationKeyCode:ur=KG()?"Meta":"Control",snapToGrid:mi,snapGrid:Fi,onlyRenderVisibleElements:fu=!1,selectNodesOnDrag:Eu,nodesDraggable:Ws,autoPanOnNodeFocus:Dh,nodesConnectable:ef,nodesFocusable:ch,nodeOrigin:kc=p0n,edgesFocusable:cd,edgesReconnectable:jb,elementsSelectable:Rs=!0,defaultViewport:c0=bqn,minZoom:u0=.5,maxZoom:o0=2,translateExtent:Bg=GG,preventScrolling:r6=!0,nodeExtent:zg,defaultMarkerColor:c6="#b1b1b7",zoomOnScroll:d1=!0,zoomOnPinch:ud=!0,panOnScroll:Sf=!1,panOnScrollSpeed:b1=.5,panOnScrollMode:xf=jA.Free,zoomOnDoubleClick:l5=!0,panOnDrag:f5=!0,onPaneClick:a5,onPaneMouseEnter:Fg,onPaneMouseMove:Eb,onPaneMouseLeave:Jg,onPaneScroll:_u,onPaneContextMenu:Hg,paneClickDistance:Gg=1,nodeClickDistance:u6=0,children:h5,onReconnect:Wm,onReconnectStart:qg,onReconnectEnd:o6,onEdgeContextMenu:Ug,onEdgeDoubleClick:Zm,onEdgeMouseEnter:d5,onEdgeMouseMove:Mt,onEdgeMouseLeave:Tc,reconnectRadius:dc=10,onNodesChange:s6,onEdgesChange:Af,noDragClassName:Zs="nodrag",noWheelClassName:Ca="nowheel",noPanClassName:Xg="nopan",fitView:b5,fitViewOptions:ek,connectOnClick:MA,attributionPosition:nk,proOptions:e3,defaultEdgeOptions:l6,elevateNodesOnSelect:xp=!0,elevateEdgesOnSelect:Ap=!1,disableKeyboardA11y:Mp=!1,autoPanOnConnect:Tp,autoPanOnNodeDrag:xl,autoPanSpeed:g5,connectionRadius:tk,isValidConnection:Kg,onError:Cp,style:TA,id:f6,nodeDragThreshold:ik,connectionDragThreshold:rk,viewport:n3,onViewportChange:w5,width:s0,height:Ih,colorMode:ck="light",debug:CA,onScroll:p5,ariaLabelConfig:uk,zIndexMode:t3="basic",...OA},_h){const i3=f6||"1",ok=mqn(ck),a6=He.useCallback(Vg=>{Vg.currentTarget.scrollTo({top:0,left:0,behavior:"instant"}),p5?.(Vg)},[p5]);return F.jsx("div",{"data-testid":"rf__wrapper",...OA,onScroll:a6,style:{...TA,...RUn},ref:_h,className:Ta(["react-flow",N,ok]),id:f6,role:"application",children:F.jsxs($Un,{nodes:g,edges:E,width:s0,height:Ih,fitView:b5,fitViewOptions:ek,minZoom:u0,maxZoom:o0,nodeOrigin:kc,nodeExtent:zg,zIndexMode:t3,children:[F.jsx(pqn,{nodes:g,edges:E,defaultNodes:x,defaultEdges:M,onConnect:se,onConnectStart:le,onConnectEnd:ee,onClickConnectStart:Oe,onClickConnectEnd:ge,nodesDraggable:Ws,autoPanOnNodeFocus:Dh,nodesConnectable:ef,nodesFocusable:ch,edgesFocusable:cd,edgesReconnectable:jb,elementsSelectable:Rs,elevateNodesOnSelect:xp,elevateEdgesOnSelect:Ap,minZoom:u0,maxZoom:o0,nodeExtent:zg,onNodesChange:s6,onEdgesChange:Af,snapToGrid:mi,snapGrid:Fi,connectionMode:xe,translateExtent:Bg,connectOnClick:MA,defaultEdgeOptions:l6,fitView:b5,fitViewOptions:ek,onNodesDelete:wt,onEdgesDelete:Cn,onDelete:Qn,onNodeDragStart:ln,onNodeDrag:xn,onNodeDragStop:hn,onSelectionDrag:mn,onSelectionDragStart:Fe,onSelectionDragStop:Ae,onMove:ie,onMoveStart:Z,onMoveEnd:W,noPanClassName:Xg,nodeOrigin:kc,rfId:i3,autoPanOnConnect:Tp,autoPanOnNodeDrag:xl,autoPanSpeed:g5,onError:Cp,connectionRadius:tk,isValidConnection:Kg,selectNodesOnDrag:Eu,nodeDragThreshold:ik,connectionDragThreshold:rk,onBeforeDelete:kn,debug:CA,ariaLabelConfig:uk,zIndexMode:t3}),F.jsx(_Un,{onInit:G,onNodeClick:H,onEdgeClick:U,onNodeMouseEnter:Pe,onNodeMouseMove:ae,onNodeMouseLeave:Me,onNodeContextMenu:Be,onNodeDoubleClick:rn,nodeTypes:P,edgeTypes:k,connectionLineType:un,connectionLineStyle:rt,connectionLineComponent:lt,connectionLineContainerStyle:Bt,selectionKeyCode:Gt,selectionOnDrag:At,selectionMode:st,deleteKeyCode:hi,multiSelectionKeyCode:Mr,panActivationKeyCode:Wr,zoomActivationKeyCode:ur,onlyRenderVisibleElements:fu,defaultViewport:c0,translateExtent:Bg,minZoom:u0,maxZoom:o0,preventScrolling:r6,zoomOnScroll:d1,zoomOnPinch:ud,zoomOnDoubleClick:l5,panOnScroll:Sf,panOnScrollSpeed:b1,panOnScrollMode:xf,panOnDrag:f5,onPaneClick:a5,onPaneMouseEnter:Fg,onPaneMouseMove:Eb,onPaneMouseLeave:Jg,onPaneScroll:_u,onPaneContextMenu:Hg,paneClickDistance:Gg,nodeClickDistance:u6,onSelectionContextMenu:Ze,onSelectionStart:sn,onSelectionEnd:Sn,onReconnect:Wm,onReconnectStart:qg,onReconnectEnd:o6,onEdgeContextMenu:Ug,onEdgeDoubleClick:Zm,onEdgeMouseEnter:d5,onEdgeMouseMove:Mt,onEdgeMouseLeave:Tc,reconnectRadius:dc,defaultMarkerColor:c6,noDragClassName:Zs,noWheelClassName:Ca,noPanClassName:Xg,rfId:i3,disableKeyboardA11y:Mp,nodeExtent:zg,viewport:n3,onViewportChange:w5}),F.jsx(dqn,{onSelectionChange:Y}),h5,F.jsx(sqn,{proOptions:e3,position:nk}),F.jsx(oqn,{rfId:i3,disableKeyboardA11y:Mp})]})})}var zUn=k0n(BUn);const FUn=g=>g.domNode?.querySelector(".react-flow__edgelabel-renderer");function JUn({children:g}){const E=Fu(FUn);return E?eqn.createPortal(g,E):null}function HUn(g){const[E,x]=He.useState(g),M=He.useCallback(N=>x(P=>v0n(N,P)),[]);return[E,x,M]}function GUn(g){const[E,x]=He.useState(g),M=He.useCallback(N=>x(P=>y0n(N,P)),[]);return[E,x,M]}function qUn({dimensions:g,lineWidth:E,variant:x,className:M}){return F.jsx("path",{strokeWidth:E,d:`M${g[0]/2} 0 V${g[1]} M0 ${g[1]/2} H${g[0]}`,className:Ta(["react-flow__background-pattern",x,M])})}function UUn({radius:g,className:E}){return F.jsx("circle",{cx:g,cy:g,r:g,className:Ta(["react-flow__background-pattern","dots",E])})}var Z7;(function(g){g.Lines="lines",g.Dots="dots",g.Cross="cross"})(Z7||(Z7={}));const XUn={[Z7.Dots]:1,[Z7.Lines]:1,[Z7.Cross]:6},KUn=g=>({transform:g.transform,patternId:`pattern-${g.rfId}`});function q0n({id:g,variant:E=Z7.Dots,gap:x=20,size:M,lineWidth:N=1,offset:P=0,color:k,bgColor:H,style:U,className:G,patternClassName:ie}){const Z=He.useRef(null),{transform:W,patternId:se}=Fu(KUn,El),le=M||XUn[E],ee=E===Z7.Dots,Oe=E===Z7.Cross,ge=Array.isArray(x)?x:[x,x],Pe=[ge[0]*W[2]||1,ge[1]*W[2]||1],ae=le*W[2],Me=Array.isArray(P)?P:[P,P],Be=Oe?[ae,ae]:Pe,rn=[Me[0]*W[2]||1+Be[0]/2,Me[1]*W[2]||1+Be[1]/2],ln=`${se}${g||""}`;return F.jsxs("svg",{className:Ta(["react-flow__background",G]),style:{...U,...zue,"--xy-background-color-props":H,"--xy-background-pattern-color-props":k},ref:Z,"data-testid":"rf__background",children:[F.jsx("pattern",{id:ln,x:W[0]%Pe[0],y:W[1]%Pe[1],width:Pe[0],height:Pe[1],patternUnits:"userSpaceOnUse",patternTransform:`translate(-${rn[0]},-${rn[1]})`,children:ee?F.jsx(UUn,{radius:ae/2,className:ie}):F.jsx(qUn,{dimensions:Be,lineWidth:N,variant:E,className:ie})}),F.jsx("rect",{x:"0",y:"0",width:"100%",height:"100%",fill:`url(#${ln})`})]})}q0n.displayName="Background";const VUn=He.memo(q0n);function YUn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",children:F.jsx("path",{d:"M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z"})})}function QUn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 5",children:F.jsx("path",{d:"M0 0h32v4.2H0z"})})}function WUn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 30",children:F.jsx("path",{d:"M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z"})})}function ZUn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32",children:F.jsx("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z"})})}function eXn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32",children:F.jsx("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z"})})}function rue({children:g,className:E,...x}){return F.jsx("button",{type:"button",className:Ta(["react-flow__controls-button",E]),...x,children:g})}const nXn=g=>({isInteractive:g.nodesDraggable||g.nodesConnectable||g.elementsSelectable,minZoomReached:g.transform[2]<=g.minZoom,maxZoomReached:g.transform[2]>=g.maxZoom,ariaLabelConfig:g.ariaLabelConfig});function U0n({style:g,showZoom:E=!0,showFitView:x=!0,showInteractive:M=!0,fitViewOptions:N,onZoomIn:P,onZoomOut:k,onFitView:H,onInteractiveChange:U,className:G,children:ie,position:Z="bottom-left",orientation:W="vertical","aria-label":se}){const le=Sl(),{isInteractive:ee,minZoomReached:Oe,maxZoomReached:ge,ariaLabelConfig:Pe}=Fu(nXn,El),{zoomIn:ae,zoomOut:Me,fitView:Be}=Nke(),rn=()=>{ae(),P?.()},ln=()=>{Me(),k?.()},xn=()=>{Be(N),H?.()},hn=()=>{le.setState({nodesDraggable:!ee,nodesConnectable:!ee,elementsSelectable:!ee}),U?.(!ee)},wt=W==="horizontal"?"horizontal":"vertical";return F.jsxs(Bue,{className:Ta(["react-flow__controls",wt,G]),position:Z,style:g,"data-testid":"rf__controls","aria-label":se??Pe["controls.ariaLabel"],children:[E&&F.jsxs(F.Fragment,{children:[F.jsx(rue,{onClick:rn,className:"react-flow__controls-zoomin",title:Pe["controls.zoomIn.ariaLabel"],"aria-label":Pe["controls.zoomIn.ariaLabel"],disabled:ge,children:F.jsx(YUn,{})}),F.jsx(rue,{onClick:ln,className:"react-flow__controls-zoomout",title:Pe["controls.zoomOut.ariaLabel"],"aria-label":Pe["controls.zoomOut.ariaLabel"],disabled:Oe,children:F.jsx(QUn,{})})]}),x&&F.jsx(rue,{className:"react-flow__controls-fitview",onClick:xn,title:Pe["controls.fitView.ariaLabel"],"aria-label":Pe["controls.fitView.ariaLabel"],children:F.jsx(WUn,{})}),M&&F.jsx(rue,{className:"react-flow__controls-interactive",onClick:hn,title:Pe["controls.interactive.ariaLabel"],"aria-label":Pe["controls.interactive.ariaLabel"],children:ee?F.jsx(eXn,{}):F.jsx(ZUn,{})}),ie]})}U0n.displayName="Controls";const tXn=He.memo(U0n);function iXn({id:g,x:E,y:x,width:M,height:N,style:P,color:k,strokeColor:H,strokeWidth:U,className:G,borderRadius:ie,shapeRendering:Z,selected:W,onClick:se}){const{background:le,backgroundColor:ee}=P||{},Oe=k||le||ee;return F.jsx("rect",{className:Ta(["react-flow__minimap-node",{selected:W},G]),x:E,y:x,rx:ie,ry:ie,width:M,height:N,style:{fill:Oe,stroke:H,strokeWidth:U},shapeRendering:Z,onClick:se?ge=>se(ge,g):void 0})}const rXn=He.memo(iXn),cXn=g=>g.nodes.map(E=>E.id),U7e=g=>g instanceof Function?g:()=>g;function uXn({nodeStrokeColor:g,nodeColor:E,nodeClassName:x="",nodeBorderRadius:M=5,nodeStrokeWidth:N,nodeComponent:P=rXn,onClick:k}){const H=Fu(cXn,El),U=U7e(E),G=U7e(g),ie=U7e(x),Z=typeof window>"u"||window.chrome?"crispEdges":"geometricPrecision";return F.jsx(F.Fragment,{children:H.map(W=>F.jsx(sXn,{id:W,nodeColorFunc:U,nodeStrokeColorFunc:G,nodeClassNameFunc:ie,nodeBorderRadius:M,nodeStrokeWidth:N,NodeComponent:P,onClick:k,shapeRendering:Z},W))})}function oXn({id:g,nodeColorFunc:E,nodeStrokeColorFunc:x,nodeClassNameFunc:M,nodeBorderRadius:N,nodeStrokeWidth:P,shapeRendering:k,NodeComponent:H,onClick:U}){const{node:G,x:ie,y:Z,width:W,height:se}=Fu(le=>{const ee=le.nodeLookup.get(g);if(!ee)return{node:void 0,x:0,y:0,width:0,height:0};const Oe=ee.internals.userNode,{x:ge,y:Pe}=ee.internals.positionAbsolute,{width:ae,height:Me}=i6(Oe);return{node:Oe,x:ge,y:Pe,width:ae,height:Me}},El);return!G||G.hidden||!Xdn(G)?null:F.jsx(H,{x:ie,y:Z,width:W,height:se,style:G.style,selected:!!G.selected,className:M(G),color:E(G),borderRadius:N,strokeColor:x(G),strokeWidth:P,shapeRendering:k,onClick:U,id:G.id})}const sXn=He.memo(oXn);var lXn=He.memo(uXn);const fXn=200,aXn=150,hXn=g=>!g.hidden,dXn=g=>{const E={x:-g.transform[0]/g.transform[2],y:-g.transform[1]/g.transform[2],width:g.width/g.transform[2],height:g.height/g.transform[2]};return{viewBB:E,boundingRect:g.nodeLookup.size>0?Udn(tq(g.nodeLookup,{filter:hXn}),E):E,rfId:g.rfId,panZoom:g.panZoom,translateExtent:g.translateExtent,flowWidth:g.width,flowHeight:g.height,ariaLabelConfig:g.ariaLabelConfig}},bXn="react-flow__minimap-desc";function X0n({style:g,className:E,nodeStrokeColor:x,nodeColor:M,nodeClassName:N="",nodeBorderRadius:P=5,nodeStrokeWidth:k,nodeComponent:H,bgColor:U,maskColor:G,maskStrokeColor:ie,maskStrokeWidth:Z,position:W="bottom-right",onClick:se,onNodeClick:le,pannable:ee=!1,zoomable:Oe=!1,ariaLabel:ge,inversePan:Pe,zoomStep:ae=1,offsetScale:Me=5}){const Be=Sl(),rn=He.useRef(null),{boundingRect:ln,viewBB:xn,rfId:hn,panZoom:wt,translateExtent:Cn,flowWidth:Qn,flowHeight:Y,ariaLabelConfig:Fe}=Fu(dXn,El),mn=g?.width??fXn,Ae=g?.height??aXn,Ze=ln.width/mn,sn=ln.height/Ae,Sn=Math.max(Ze,sn),kn=Sn*mn,xe=Sn*Ae,un=Me*Sn,rt=ln.x-(kn-ln.width)/2-un,lt=ln.y-(xe-ln.height)/2-un,Bt=kn+un*2,hi=xe+un*2,Gt=`${bXn}-${hn}`,At=He.useRef(0),st=He.useRef();At.current=Sn,He.useEffect(()=>{if(rn.current&&wt)return st.current=MGn({domNode:rn.current,panZoom:wt,getTransform:()=>Be.getState().transform,getViewScale:()=>At.current}),()=>{st.current?.destroy()}},[wt]),He.useEffect(()=>{st.current?.update({translateExtent:Cn,width:Qn,height:Y,inversePan:Pe,pannable:ee,zoomStep:ae,zoomable:Oe})},[ee,Oe,Pe,ae,Cn,Qn,Y]);const Wr=se?mi=>{const[Fi,fu]=st.current?.pointer(mi)||[0,0];se(mi,{x:Fi,y:fu})}:void 0,Mr=le?He.useCallback((mi,Fi)=>{const fu=Be.getState().nodeLookup.get(Fi).internals.userNode;le(mi,fu)},[]):void 0,ur=ge??Fe["minimap.ariaLabel"];return F.jsx(Bue,{position:W,style:{...g,"--xy-minimap-background-color-props":typeof U=="string"?U:void 0,"--xy-minimap-mask-background-color-props":typeof G=="string"?G:void 0,"--xy-minimap-mask-stroke-color-props":typeof ie=="string"?ie:void 0,"--xy-minimap-mask-stroke-width-props":typeof Z=="number"?Z*Sn:void 0,"--xy-minimap-node-background-color-props":typeof M=="string"?M:void 0,"--xy-minimap-node-stroke-color-props":typeof x=="string"?x:void 0,"--xy-minimap-node-stroke-width-props":typeof k=="number"?k:void 0},className:Ta(["react-flow__minimap",E]),"data-testid":"rf__minimap",children:F.jsxs("svg",{width:mn,height:Ae,viewBox:`${rt} ${lt} ${Bt} ${hi}`,className:"react-flow__minimap-svg",role:"img","aria-labelledby":Gt,ref:rn,onClick:Wr,children:[ur&&F.jsx("title",{id:Gt,children:ur}),F.jsx(lXn,{onClick:Mr,nodeColor:M,nodeStrokeColor:x,nodeBorderRadius:P,nodeClassName:N,nodeStrokeWidth:k,nodeComponent:H}),F.jsx("path",{className:"react-flow__minimap-mask",d:`M${rt-un},${lt-un}h${Bt+un*2}v${hi+un*2}h${-Bt-un*2}z + M${xn.x},${xn.y}h${xn.width}v${xn.height}h${-xn.width}z`,fillRule:"evenodd",pointerEvents:"none"})]})})}X0n.displayName="MiniMap";const gXn=He.memo(X0n),wXn=g=>E=>g?`${Math.max(1/E.transform[2],1)}`:void 0,pXn={[mI.Line]:"right",[mI.Handle]:"bottom-right"};function mXn({nodeId:g,position:E,variant:x=mI.Handle,className:M,style:N=void 0,children:P,color:k,minWidth:H=10,minHeight:U=10,maxWidth:G=Number.MAX_VALUE,maxHeight:ie=Number.MAX_VALUE,keepAspectRatio:Z=!1,resizeDirection:W,autoScale:se=!0,shouldResize:le,onResizeStart:ee,onResize:Oe,onResizeEnd:ge}){const Pe=x0n(),ae=typeof g=="string"?g:Pe,Me=Sl(),Be=He.useRef(null),rn=x===mI.Handle,ln=Fu(He.useCallback(wXn(rn&&se),[rn,se]),El),xn=He.useRef(null),hn=E??pXn[x];He.useEffect(()=>{if(!(!Be.current||!ae))return xn.current||(xn.current=FGn({domNode:Be.current,nodeId:ae,getStoreItems:()=>{const{nodeLookup:Cn,transform:Qn,snapGrid:Y,snapToGrid:Fe,nodeOrigin:mn,domNode:Ae}=Me.getState();return{nodeLookup:Cn,transform:Qn,snapGrid:Y,snapToGrid:Fe,nodeOrigin:mn,paneDomNode:Ae}},onChange:(Cn,Qn)=>{const{triggerNodeChanges:Y,nodeLookup:Fe,parentLookup:mn,nodeOrigin:Ae}=Me.getState(),Ze=[],sn={x:Cn.x,y:Cn.y},Sn=Fe.get(ae);if(Sn&&Sn.expandParent&&Sn.parentId){const kn=Sn.origin??Ae,xe=Cn.width??Sn.measured.width??0,un=Cn.height??Sn.measured.height??0,rt={id:Sn.id,parentId:Sn.parentId,rect:{width:xe,height:un,...Kdn({x:Cn.x??Sn.position.x,y:Cn.y??Sn.position.y},{width:xe,height:un},Sn.parentId,Fe,kn)}},lt=Oke([rt],Fe,mn,Ae);Ze.push(...lt),sn.x=Cn.x?Math.max(kn[0]*xe,Cn.x):void 0,sn.y=Cn.y?Math.max(kn[1]*un,Cn.y):void 0}if(sn.x!==void 0&&sn.y!==void 0){const kn={id:ae,type:"position",position:{...sn}};Ze.push(kn)}if(Cn.width!==void 0&&Cn.height!==void 0){const xe={id:ae,type:"dimensions",resizing:!0,setAttributes:W?W==="horizontal"?"width":"height":!0,dimensions:{width:Cn.width,height:Cn.height}};Ze.push(xe)}for(const kn of Qn){const xe={...kn,type:"position"};Ze.push(xe)}Y(Ze)},onEnd:({width:Cn,height:Qn})=>{const Y={id:ae,type:"dimensions",resizing:!1,dimensions:{width:Cn,height:Qn}};Me.getState().triggerNodeChanges([Y])}})),xn.current.update({controlPosition:hn,boundaries:{minWidth:H,minHeight:U,maxWidth:G,maxHeight:ie},keepAspectRatio:Z,resizeDirection:W,onResizeStart:ee,onResize:Oe,onResizeEnd:ge,shouldResize:le}),()=>{xn.current?.destroy()}},[hn,H,U,G,ie,Z,ee,Oe,ge,le]);const wt=hn.split("-");return F.jsx("div",{className:Ta(["react-flow__resize-control","nodrag",...wt,x,M]),ref:Be,style:{...N,scale:ln,...k&&{[rn?"backgroundColor":"borderColor"]:k}},children:P})}He.memo(mXn);const vXn=g=>g.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),K0n=(...g)=>g.filter((E,x,M)=>!!E&&E.trim()!==""&&M.indexOf(E)===x).join(" ").trim();var yXn={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};const kXn=He.forwardRef(({color:g="currentColor",size:E=24,strokeWidth:x=2,absoluteStrokeWidth:M,className:N="",children:P,iconNode:k,...H},U)=>He.createElement("svg",{ref:U,...yXn,width:E,height:E,stroke:g,strokeWidth:M?Number(x)*24/Number(E):x,className:K0n("lucide",N),...H},[...k.map(([G,ie])=>He.createElement(G,ie)),...Array.isArray(P)?P:[P]]));const Ef=(g,E)=>{const x=He.forwardRef(({className:M,...N},P)=>He.createElement(kXn,{ref:P,iconNode:E,className:K0n(`lucide-${vXn(g)}`,M),...N}));return x.displayName=`${g}`,x};const jXn=Ef("Box",[["path",{d:"M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z",key:"hh9hay"}],["path",{d:"m3.3 7 8.7 5 8.7-5",key:"g66t2b"}],["path",{d:"M12 22V12",key:"d0xqtd"}]]);const EXn=Ef("Boxes",[["path",{d:"M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z",key:"lc1i9w"}],["path",{d:"m7 16.5-4.74-2.85",key:"1o9zyk"}],["path",{d:"m7 16.5 5-3",key:"va8pkn"}],["path",{d:"M7 16.5v5.17",key:"jnp8gn"}],["path",{d:"M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z",key:"8zsnat"}],["path",{d:"m17 16.5-5-3",key:"8arw3v"}],["path",{d:"m17 16.5 4.74-2.85",key:"8rfmw"}],["path",{d:"M17 16.5v5.17",key:"k6z78m"}],["path",{d:"M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z",key:"1xygjf"}],["path",{d:"M12 8 7.26 5.15",key:"1vbdud"}],["path",{d:"m12 8 4.74-2.85",key:"3rx089"}],["path",{d:"M12 13.5V8",key:"1io7kd"}]]);const YG=Ef("Check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);const SXn=Ef("CircleAlert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);const xXn=Ef("Clock3",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["polyline",{points:"12 6 12 12 16.5 12",key:"1aq6pp"}]]);const AXn=Ef("CodeXml",[["path",{d:"m18 16 4-4-4-4",key:"1inbqp"}],["path",{d:"m6 8-4 4 4 4",key:"15zrgr"}],["path",{d:"m14.5 4-5 16",key:"e7oirm"}]]);const V0n=Ef("Eye",[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);const MXn=Ef("FolderOpen",[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2",key:"usdka0"}]]);const TXn=Ef("GitBranch",[["line",{x1:"6",x2:"6",y1:"3",y2:"15",key:"17qcm7"}],["circle",{cx:"18",cy:"6",r:"3",key:"1h7g24"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}],["path",{d:"M18 9a9 9 0 0 1-9 9",key:"n2h4wq"}]]);const Y0n=Ef("Layers",[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",key:"zw3jo"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",key:"1wduqc"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",key:"kqbvx6"}]]);const Q1n=Ef("Link2",[["path",{d:"M9 17H7A5 5 0 0 1 7 7h2",key:"8i5ue5"}],["path",{d:"M15 7h2a5 5 0 1 1 0 10h-2",key:"1b9ql8"}],["line",{x1:"8",x2:"16",y1:"12",y2:"12",key:"1jonct"}]]);const CXn=Ef("Network",[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]]);const QG=Ef("Plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);const OXn=Ef("Redo2",[["path",{d:"m15 14 5-5-5-5",key:"12vg1m"}],["path",{d:"M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13",key:"6uklza"}]]);const NXn=Ef("RotateCcw",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]]);const DXn=Ef("Save",[["path",{d:"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",key:"1c8476"}],["path",{d:"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7",key:"1ydtos"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7",key:"t51u73"}]]);const Q0n=Ef("Scissors",[["circle",{cx:"6",cy:"6",r:"3",key:"1lh9wr"}],["path",{d:"M8.12 8.12 12 12",key:"1alkpv"}],["path",{d:"M20 4 8.12 15.88",key:"xgtan2"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}],["path",{d:"M14.8 14.8 20 20",key:"ptml3r"}]]);const IXn=Ef("Search",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]]);const wue=Ef("Trash2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]]);const dke=Ef("TriangleAlert",[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);const _Xn=Ef("Undo2",[["path",{d:"M9 14 4 9l5-5",key:"102s5s"}],["path",{d:"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11",key:"f3b9sd"}]]);const Ym=Ef("X",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);function LXn({mode:g,models:E,objects:x,application:M,initialModelType:N,suggestedSelector:P,nameReadOnly:k=!1,preview:H,onPreview:U,onSubmit:G,onClose:ie}){const Z=M?.modelType||N||E[0]?.type||"",[W,se]=He.useState(Z),le=E.find(st=>st.type===W)??null,[ee,Oe]=He.useState(M?.name||M?.applicationId||W1n(le)),[ge,Pe]=He.useState(()=>Tue(le,M)),ae=M?.selector||P||$Xn(x),[Me,Be]=He.useState(ae.multiplicity),[rn,ln]=He.useState(cue(ae,"scale")),[xn,hn]=He.useState(cue(ae,"kind")),[wt,Cn]=He.useState(cue(ae,"species")),[Qn,Y]=He.useState(cue(ae,"name")),Fe=RXn(ae,"within"),[mn,Ae]=He.useState(Fe?.type==="Scope"?"named_scope":"scene"),[Ze,sn]=He.useState(Fe?.type==="Scope"?String(Fe.name||""):""),[Sn,kn]=He.useState(M?.timestep?"clock":"default"),[xe,un]=He.useState("1.0"),[rt,lt]=He.useState("0.0");He.useEffect(()=>{const st=E.find(Wr=>Wr.type===W)??null;Pe(Tue(st,g==="update"?M:void 0)),g==="add"&&Oe(W1n(st))},[M,g,W,E]);const Bt=He.useMemo(()=>({scales:$G(x.map(st=>st.scale)),kinds:$G(x.map(st=>st.kind)),species:$G(x.map(st=>st.species)),names:$G(x.map(st=>st.name))}),[x]),hi=He.useMemo(()=>{const st=[rn&&`scale ${rn}`,xn&&`kind ${xn}`,wt&&`species ${wt}`,Qn&&`name ${Qn}`].filter(Boolean);return st.length?st.join(", "):"all scene objects"},[xn,Qn,rn,wt]),Gt=()=>{const st={selectors:[]};return st.within=mn==="named_scope"&&Ze?{type:"Scope",name:Ze}:{type:"SceneScope"},rn&&(st.scale=rn),xn&&(st.kind=xn),wt&&(st.species=wt),Qn&&(st.name=Qn),{type:BXn(Me),multiplicity:Me,criteria:st,julia:""}},At=()=>{G({applicationId:M?.applicationId,modelType:W,name:ee.trim(),parameters:ge,selector:Gt(),timestep:Sn==="clock"?{mode:"clock",dt:xe,phase:rt}:{mode:"default"}})};return F.jsx("div",{className:"overlay-backdrop",onMouseDown:ie,children:F.jsxs("section",{className:"overlay-panel application-form",onMouseDown:st=>st.stopPropagation(),"data-testid":"application-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:g==="add"?"Add application":`Update ${M?.applicationId}`}),F.jsx("span",{children:"A configured use of a model on selected scene objects"})]}),F.jsx("button",{onClick:ie,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content application-form-content",children:[F.jsxs("label",{children:["Model",F.jsx("select",{value:W,onChange:st=>se(st.target.value),"data-testid":"application-model-select",children:E.map(st=>F.jsxs("option",{value:st.type,children:[st.package?`${st.package} · `:"",st.name," (",st.process,")"]},st.type))})]}),F.jsxs("label",{children:["Application name",F.jsx("input",{value:ee,disabled:k,onChange:st=>Oe(st.target.value),"data-testid":"application-name"})]}),le&&le.constructor.fields.length>0&&F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Model parameters"}),F.jsx(W0n,{fields:le.constructor.fields,values:ge,onChange:Pe})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Target selector"}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Multiplicity",F.jsxs("select",{value:Me,onChange:st=>Be(st.target.value),children:[F.jsx("option",{value:"one",children:"One"}),F.jsx("option",{value:"optional_one",children:"Optional one"}),F.jsx("option",{value:"many",children:"Many"})]})]}),F.jsxs("label",{children:["Scope",F.jsxs("select",{value:mn,onChange:st=>Ae(st.target.value),children:[F.jsx("option",{value:"scene",children:"Whole scene"}),F.jsx("option",{value:"named_scope",children:"Named object subtree"})]})]}),mn==="named_scope"&&F.jsx(_G,{label:"Scope root",value:Ze,options:Bt.names,onChange:sn}),F.jsx(_G,{label:"Scale",value:rn,options:Bt.scales,onChange:ln}),F.jsx(_G,{label:"Kind",value:xn,options:Bt.kinds,onChange:hn}),F.jsx(_G,{label:"Species",value:wt,options:Bt.species,onChange:Cn}),F.jsx(_G,{label:"Object name",value:Qn,options:Bt.names,onChange:Y})]}),F.jsxs("p",{className:"selector-summary",children:["Julia will resolve ",F.jsx("strong",{children:Me.replace("_"," ")})," target from ",hi,"."]}),F.jsxs("button",{className:"selector-preview-button",type:"button",onClick:()=>U(Gt()),"data-testid":"application-target-preview",children:[F.jsx(V0n,{size:15})," Preview targets in Julia"]}),H&&F.jsxs("section",{className:"selector-preview","data-testid":"application-target-preview-result",children:[F.jsxs("strong",{children:[H.count," target object",H.count===1?"":"s"]}),F.jsx("code",{children:H.objectIds.map(String).join(", ")||"No targets"})]})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Timestep"}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Mode",F.jsxs("select",{value:Sn,onChange:st=>kn(st.target.value),children:[F.jsx("option",{value:"default",children:"Model or environment default"}),F.jsx("option",{value:"clock",children:"Explicit clock"})]})]}),Sn==="clock"&&F.jsxs(F.Fragment,{children:[F.jsxs("label",{children:["Step",F.jsx("input",{value:xe,onChange:st=>un(st.target.value)})]}),F.jsxs("label",{children:["Phase",F.jsx("input",{value:rt,onChange:st=>lt(st.target.value)})]})]})]})]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:ie,children:"Cancel"}),F.jsxs("button",{className:"primary",disabled:!W||!ee.trim(),onClick:At,"data-testid":"application-submit",children:[F.jsx(YG,{size:15})," ",g==="add"?"Add application":"Apply changes"]})]})]})})}function W0n({fields:g,values:E,onChange:x}){const M=new Map;for(const P of g)P.typeParameter&&!M.has(P.typeParameter)&&M.set(P.typeParameter,P.name);const N=(P,k)=>{const H=P.typeParameter?g.filter(U=>U.typeParameter===P.typeParameter).map(U=>U.name):[P.name];x(Object.fromEntries(Object.entries(E).map(([U,G])=>[U,H.includes(U)?{...G,type:k}:G])))};return F.jsx("div",{className:"parameter-list",children:g.map(P=>{const k=E[P.name]||{type:P.inferredChoice,value:""},H=!P.typeParameter||M.get(P.typeParameter)===P.name;return F.jsxs("div",{className:"parameter-row",children:[F.jsxs("label",{children:[F.jsx("span",{children:P.name}),F.jsx("small",{children:P.declaredType}),F.jsx("input",{"data-testid":`application-param-${P.name}`,value:k.value,onChange:U=>x({...E,[P.name]:{...k,value:U.target.value}})})]}),H&&F.jsxs("label",{className:"parameter-type",children:[F.jsx("span",{children:P.typeParameter?`${P.typeParameter} type`:"Value type"}),F.jsx("select",{"data-testid":`application-param-type-${P.name}`,value:k.type,onChange:U=>N(P,U.target.value),children:P.choices.map(U=>F.jsx("option",{value:U,children:U},U))})]})]},P.name)})})}function _G({label:g,value:E,options:x,onChange:M}){return F.jsxs("label",{children:[g,F.jsxs("select",{value:E,onChange:N=>M(N.target.value),children:[F.jsx("option",{value:"",children:"Any"}),x.map(N=>F.jsx("option",{value:N,children:N},N))]})]})}function Tue(g,E){return g?Object.fromEntries(g.constructor.fields.map(x=>{const M=E?.modelParameters[x.name],N=M?.type||x.inferredChoice,P=M?M.julia:x.hasDefault?N==="julia"?x.defaultJulia||"":PXn(x.default,N):"";return[x.name,{type:N,value:P}]})):{}}function PXn(g,E){const x=g==null?"":String(g);return E==="symbol"?x.replace(/^:/,""):x}function W1n(g){return g?.process||g?.name||"application"}function $Xn(g){const E=$G(g.map(x=>x.scale))[0];return{type:"Many",multiplicity:"many",criteria:E?{selectors:[],scale:E}:{selectors:[]},julia:""}}function cue(g,E){const x=g.criteria[E];return typeof x=="string"?x:""}function RXn(g,E){const x=g.criteria[E];return x&&typeof x=="object"?x:null}function BXn(g){return g==="one"?"One":g==="optional_one"?"OptionalOne":"Many"}function $G(g){return[...new Set(g.filter(E=>!!E))].sort()}function zXn({application:g,applications:E,onCommand:x,onClose:M}){const N=E.filter(Be=>Be.applicationId!==g.applicationId),[P,k]=He.useState(""),[H,U]=He.useState(N[0]?.applicationId||""),[G,ie]=He.useState(String(g.environment?.provider||"scene")),[Z,W]=He.useState(g.updates||[]),[se,le]=He.useState(g.outputs[0]?.name||""),[ee,Oe]=He.useState(N[0]?.applicationId||""),ge=N.find(Be=>Be.applicationId===H),Pe=He.useMemo(()=>({type:ge?.targetCount===1?"One":"Many",multiplicity:ge?.targetCount===1?"one":"many",criteria:{selectors:[],within:{type:"SceneScope"},application:H},julia:""}),[H,ge?.targetCount]),ae=()=>{!P.trim()||!H||(x({action:"edit",kind:"set_call_binding",applicationId:g.applicationId,call:P.trim(),selector:Pe}),k(""))},Me=()=>{!se||!ee||W(Be=>[...Be.filter(rn=>!rn.variables.includes(se)),{variables:[se],after:[ee]}])};return F.jsx("div",{className:"overlay-backdrop",onMouseDown:M,children:F.jsxs("section",{className:"overlay-panel application-configuration-form",onMouseDown:Be=>Be.stopPropagation(),"data-testid":"application-configuration-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsxs("strong",{children:["Configure ",g.applicationId]}),F.jsx("span",{children:"Authored coupling and execution policy, validated by Julia"})]}),F.jsx("button",{onClick:M,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content application-configuration-content",children:[F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Explicit input bindings"}),Object.entries(g.inputBindings).length===0&&F.jsx("p",{children:"No authored input bindings. Unique same-object producers may still be inferred."}),F.jsx("div",{className:"configuration-list",children:Object.entries(g.inputBindings).map(([Be,rn])=>F.jsxs("div",{children:[F.jsx("code",{children:Be}),F.jsx("span",{children:rn.julia||rn.type}),F.jsx("button",{className:"danger icon-button",title:`Remove ${Be} binding`,onClick:()=>x({action:"edit",kind:"remove_input_binding",applicationId:g.applicationId,input:Be}),children:F.jsx(wue,{size:14})})]},Be))})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Manual calls"}),F.jsx("div",{className:"configuration-list",children:Object.entries(g.callBindings).map(([Be,rn])=>F.jsxs("div",{children:[F.jsx("code",{children:Be}),F.jsx("span",{children:rn.julia||rn.type}),F.jsx("button",{className:"danger icon-button",title:`Remove ${Be} call`,onClick:()=>x({action:"edit",kind:"remove_call_binding",applicationId:g.applicationId,call:Be}),children:F.jsx(wue,{size:14})})]},Be))}),F.jsxs("div",{className:"form-grid compact-configuration-row",children:[F.jsxs("label",{children:["Call name",F.jsx("input",{"data-testid":"call-name",value:P,onChange:Be=>k(Be.target.value),placeholder:"child"})]}),F.jsxs("label",{children:["Target application",F.jsxs("select",{"data-testid":"call-target",value:H,onChange:Be=>U(Be.target.value),children:[F.jsx("option",{value:"",children:"Choose application"}),N.map(Be=>F.jsx("option",{value:Be.applicationId,children:Be.applicationId},Be.applicationId))]})]}),F.jsxs("button",{type:"button","data-testid":"add-call-binding",disabled:!P.trim()||!H,onClick:ae,children:[F.jsx(QG,{size:14})," Add call"]})]})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Environment"}),F.jsxs("div",{className:"form-grid compact-configuration-row",children:[F.jsxs("label",{children:["Provider",F.jsx("input",{"data-testid":"environment-provider",value:G,onChange:Be=>ie(Be.target.value),placeholder:"scene"})]}),F.jsxs("button",{type:"button","data-testid":"apply-environment-provider",disabled:!G.trim(),onClick:()=>x({action:"edit",kind:"set_environment_provider",applicationId:g.applicationId,provider:G.trim()}),children:[F.jsx(YG,{size:14})," Apply provider"]})]}),Object.keys(g.meteoBindings||{}).length>0&&F.jsx("code",{children:JSON.stringify(g.meteoBindings)})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Output routing"}),F.jsx("div",{className:"configuration-list",children:g.outputs.map(Be=>F.jsxs("label",{children:[F.jsx("code",{children:Be.name}),F.jsxs("select",{"data-testid":`output-routing-${Be.name}`,value:g.outputRouting[Be.name]||"canonical",onChange:rn=>x({action:"edit",kind:"set_output_routing",applicationId:g.applicationId,output:Be.name,route:rn.target.value}),children:[F.jsx("option",{value:"canonical",children:"Canonical status owner"}),F.jsx("option",{value:"stream_only",children:"Stream only"})]})]},Be.name))})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Duplicate-writer ordering"}),F.jsx("div",{className:"configuration-list",children:Z.map((Be,rn)=>F.jsxs("div",{children:[F.jsx("code",{children:Be.variables.join(", ")}),F.jsxs("span",{children:["after ",Be.after.join(", ")]}),F.jsx("button",{className:"danger icon-button",title:"Remove update ordering",onClick:()=>W(ln=>ln.filter((xn,hn)=>hn!==rn)),children:F.jsx(wue,{size:14})})]},`${Be.variables.join(",")}:${Be.after.join(",")}`))}),F.jsxs("div",{className:"form-grid compact-configuration-row",children:[F.jsxs("label",{children:["Output",F.jsx("select",{value:se,onChange:Be=>le(Be.target.value),children:g.outputs.map(Be=>F.jsx("option",{value:Be.name,children:Be.name},Be.name))})]}),F.jsxs("label",{children:["Run after",F.jsxs("select",{value:ee,onChange:Be=>Oe(Be.target.value),children:[F.jsx("option",{value:"",children:"Choose application"}),N.map(Be=>F.jsx("option",{value:Be.applicationId,children:Be.applicationId},Be.applicationId))]})]}),F.jsxs("button",{type:"button",disabled:!se||!ee,onClick:Me,children:[F.jsx(QG,{size:14})," Add rule"]}),F.jsxs("button",{type:"button",onClick:()=>x({action:"edit",kind:"set_update_ordering",applicationId:g.applicationId,updates:Z}),children:[F.jsx(YG,{size:14})," Apply ordering"]})]})]})]}),F.jsx("footer",{children:F.jsx("button",{className:"primary",onClick:M,children:"Done"})})]})})}function FXn({endpoints:g,objects:E,preview:x,onPreview:M,onSubmit:N,onClose:P}){const k=JXn(g.sourceApplication.targetIds,g.targetApplication.targetIds),[H,U]=He.useState(g.sourceApplication.targetCount>1&&g.targetApplication.targetCount===1?"many":"one"),[G,ie]=He.useState(k?"self":""),[Z,W]=He.useState("scene"),[se,le]=He.useState(""),[ee,Oe]=He.useState(""),[ge,Pe]=He.useState(X7e(g.sourceApplication.targetScales)),[ae,Me]=He.useState(X7e(g.sourceApplication.targetKinds)),[Be,rn]=He.useState(X7e(g.sourceApplication.targetSpecies)),[ln,xn]=He.useState(""),[hn,wt]=He.useState("application"),[Cn,Qn]=He.useState("automatic"),[Y,Fe]=He.useState(""),mn=uue(E.map(kn=>kn.scale)),Ae=uue(E.map(kn=>kn.kind)),Ze=uue(E.map(kn=>kn.species)),sn=uue(E.map(kn=>kn.name)),Sn=()=>{const kn={selectors:[],var:g.sourcePort.name};kn[hn]=hn==="application"?g.sourceApplication.applicationId:g.sourceApplication.process;const xe=qXn(Z,se,ee);if(xe&&(kn.within=xe),G&&(kn.relation=G),ge&&(kn.scale=ge),ae&&(kn.kind=ae),Be&&(kn.species=Be),ln&&(kn.name=ln),Cn!=="automatic"&&(kn.policy={type:GXn(Cn)}),Y.trim()){const un=Number(Y);Number.isFinite(un)&&(kn.window=un)}return{applicationId:g.targetApplication.applicationId,input:g.targetPort.name,selector:{type:HXn(H),multiplicity:H,criteria:kn,julia:""}}};return F.jsx("div",{className:"overlay-backdrop",onMouseDown:P,children:F.jsxs("section",{className:"overlay-panel binding-form",onMouseDown:kn=>kn.stopPropagation(),"data-testid":"binding-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:"Connect applications"}),F.jsx("span",{children:"Julia resolves this declaration into concrete object bindings"})]}),F.jsx("button",{onClick:P,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content",children:[F.jsxs("div",{className:"binding-route",children:[F.jsxs("div",{children:[F.jsx("small",{children:"Producer"}),F.jsx("strong",{children:g.sourceApplication.applicationId}),F.jsx("code",{children:g.sourcePort.name})]}),F.jsx(Q1n,{size:22}),F.jsxs("div",{children:[F.jsx("small",{children:"Consumer"}),F.jsx("strong",{children:g.targetApplication.applicationId}),F.jsx("code",{children:g.targetPort.name})]})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Source object selector"}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Multiplicity",F.jsxs("select",{value:H,onChange:kn=>U(kn.target.value),children:[F.jsx("option",{value:"one",children:"One"}),F.jsx("option",{value:"optional_one",children:"Optional one"}),F.jsx("option",{value:"many",children:"Many"})]})]}),F.jsxs("label",{children:["Scope",F.jsxs("select",{value:Z,onChange:kn=>W(kn.target.value),children:[F.jsx("option",{value:"scene",children:"Whole scene"}),F.jsx("option",{value:"self",children:"Consumer object"}),F.jsx("option",{value:"subtree",children:"Consumer subtree"}),F.jsx("option",{value:"self_plant",children:"Consumer plant"}),F.jsx("option",{value:"ancestor",children:"Ancestor subtree"}),F.jsx("option",{value:"named_scope",children:"Named object subtree"})]})]}),Z==="ancestor"&&F.jsx(oI,{label:"Ancestor scale",value:ee,options:mn,onChange:Oe}),Z==="named_scope"&&F.jsx(oI,{label:"Scope root",value:se,options:sn,onChange:le}),F.jsxs("label",{children:["Relation",F.jsxs("select",{value:G,onChange:kn=>ie(kn.target.value),children:[F.jsx("option",{value:"",children:"Any relation"}),F.jsx("option",{value:"self",children:"Same object"}),F.jsx("option",{value:"parent",children:"Parent"}),F.jsx("option",{value:"children",children:"Children"}),F.jsx("option",{value:"ancestors",children:"Ancestors"}),F.jsx("option",{value:"descendants",children:"Descendants"}),F.jsx("option",{value:"siblings",children:"Siblings"})]})]}),F.jsxs("label",{children:["Producer filter",F.jsxs("select",{value:hn,onChange:kn=>wt(kn.target.value),children:[F.jsx("option",{value:"application",children:"This application"}),F.jsx("option",{value:"process",children:"Any application of this process"})]})]}),F.jsx(oI,{label:"Scale",value:ge,options:mn,onChange:Pe}),F.jsx(oI,{label:"Kind",value:ae,options:Ae,onChange:Me}),F.jsx(oI,{label:"Species",value:Be,options:Ze,onChange:rn}),F.jsx(oI,{label:"Object name",value:ln,options:sn,onChange:xn}),F.jsxs("label",{children:["Temporal policy",F.jsxs("select",{value:Cn,onChange:kn=>Qn(kn.target.value),children:[F.jsx("option",{value:"automatic",children:"Automatic"}),F.jsx("option",{value:"hold_last",children:"Hold last"}),F.jsx("option",{value:"interpolate",children:"Interpolate"}),F.jsx("option",{value:"integrate",children:"Integrate"}),F.jsx("option",{value:"aggregate",children:"Aggregate"})]})]}),F.jsxs("label",{children:["Window (scene steps)",F.jsx("input",{type:"number",min:"0",step:"1",value:Y,onChange:kn=>Fe(kn.target.value),placeholder:"Automatic"})]})]})]}),x&&F.jsxs("section",{className:"selector-preview","data-testid":"binding-preview",children:[F.jsxs("strong",{children:[x.bindingCount," resolved binding",x.bindingCount===1?"":"s"]}),F.jsxs("span",{children:[x.consumerObjectIds.length," consumer object",x.consumerObjectIds.length===1?"":"s"," from ",x.sourceObjectIds.length," source object",x.sourceObjectIds.length===1?"":"s"]}),x.sourceApplicationIds.length>0&&F.jsx("code",{children:x.sourceApplicationIds.join(", ")}),x.diagnostics.map(kn=>F.jsx("p",{children:kn},kn))]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:P,children:"Cancel"}),F.jsxs("button",{onClick:()=>M(Sn()),"data-testid":"binding-preview-button",children:[F.jsx(V0n,{size:15})," Preview resolution"]}),F.jsxs("button",{className:"primary",onClick:()=>N(Sn()),"data-testid":"binding-submit",children:[F.jsx(Q1n,{size:15})," Apply binding"]})]})]})})}function oI({label:g,value:E,options:x,onChange:M}){return F.jsxs("label",{children:[g,F.jsxs("select",{value:E,onChange:N=>M(N.target.value),children:[F.jsx("option",{value:"",children:"Any"}),x.map(N=>F.jsx("option",{value:N,children:N},N))]})]})}function JXn(g,E){return g.length===E.length&&g.every(x=>E.some(M=>String(M)===String(x)))}function X7e(g){return g.length===1?g[0]:""}function HXn(g){return g==="one"?"One":g==="optional_one"?"OptionalOne":"Many"}function uue(g){return[...new Set(g.filter(E=>!!E))].sort()}function GXn(g){return g==="hold_last"?"HoldLast":g==="interpolate"?"Interpolate":g==="integrate"?"Integrate":"Aggregate"}function qXn(g,E,x){return g==="scene"?{type:"SceneScope"}:g==="self"?{type:"Self"}:g==="subtree"?{type:"Subtree"}:g==="self_plant"?{type:"SelfPlant"}:g==="ancestor"?{type:"Ancestor",scale:x||null}:g==="named_scope"&&E?{type:"Scope",name:E}:null}function UXn({mode:g,objects:E,object:x,onSubmit:M,onClose:N}){const[P,k]=He.useState(String(x?.objectId??"")),[H,U]=He.useState(XXn(x?.parent)),[G,ie]=He.useState(x?.scale||""),[Z,W]=He.useState(x?.kind||""),[se,le]=He.useState(x?.species||""),[ee,Oe]=He.useState(x?.name||""),ge=He.useMemo(()=>E.filter(ae=>String(ae.objectId)!==P),[P,E]),Pe=()=>M({objectId:P.trim(),configuration:{parent:H||null,scale:G.trim()||null,kind:Z.trim()||null,species:se.trim()||null,name:ee.trim()||null}});return F.jsx("div",{className:"overlay-backdrop",onMouseDown:N,children:F.jsxs("section",{className:"overlay-panel object-form",onMouseDown:ae=>ae.stopPropagation(),"data-testid":"object-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:g==="add"?"Add scene object":`Update object ${String(x?.objectId)}`}),F.jsx("span",{children:"Objects define the concrete entities and topology targeted by applications"})]}),F.jsx("button",{onClick:N,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content object-form-content",children:[F.jsxs("label",{children:["Stable object ID",F.jsx("input",{value:P,disabled:g==="update",onChange:ae=>k(ae.target.value),"data-testid":"object-id"})]}),F.jsxs("label",{children:["Parent object",F.jsxs("select",{value:H,onChange:ae=>U(ae.target.value),children:[F.jsx("option",{value:"",children:"No parent"}),ge.map(ae=>F.jsxs("option",{value:String(ae.objectId),children:[ae.name||String(ae.objectId)," · ",ae.scale||"unscaled"]},ae.id))]})]}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Scale",F.jsx("input",{value:G,onChange:ae=>ie(ae.target.value)})]}),F.jsxs("label",{children:["Kind",F.jsx("input",{value:Z,onChange:ae=>W(ae.target.value)})]}),F.jsxs("label",{children:["Species",F.jsx("input",{value:se,onChange:ae=>le(ae.target.value)})]}),F.jsxs("label",{children:["Name",F.jsx("input",{value:ee,onChange:ae=>Oe(ae.target.value)})]})]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:N,children:"Cancel"}),F.jsxs("button",{className:"primary",disabled:!P.trim(),onClick:Pe,"data-testid":"object-submit",children:[F.jsx(YG,{size:15})," ",g==="add"?"Add object":"Apply changes"]})]})]})})}function XXn(g){if(g==null||g==="")return"";const E=String(g);return E.startsWith("object:")?E.slice(7):E}function KXn({application:g,models:E,instances:x,onSubmit:M,onRemove:N,onClose:P}){const k=He.useMemo(()=>E.filter(hn=>hn.process===g.process),[g.process,E]),[H,U]=He.useState("instance"),[G,ie]=He.useState(g.targetInstances[0]||x[0]?.name||""),Z=x.find(hn=>hn.name===G),W=(Z?.objectIds||[]).filter(hn=>g.targetIds.some(wt=>String(wt)===String(hn))),[se,le]=He.useState(W[0]??""),[ee,Oe]=He.useState(g.modelType),ge=k.find(hn=>hn.type===ee)||k[0]||null,[Pe,ae]=He.useState(()=>Tue(ge,g)),Me=VXn(g.applicationId,G),Be=!!Z?.instanceOverrides.includes(Me),rn=!!Z?.objectOverrides.some(hn=>{const wt=hn;return String(wt.object??wt.objectId??"")===String(se)&&String(wt.application??wt.applicationId??"")===Me}),ln=H==="instance"?Be:rn,xn=hn=>{Oe(hn),ae(Tue(k.find(wt=>wt.type===hn)||null))};return F.jsx("div",{className:"overlay-backdrop",onMouseDown:P,children:F.jsxs("section",{className:"overlay-panel override-form",onMouseDown:hn=>hn.stopPropagation(),"data-testid":"override-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:"Create a model override"}),F.jsx("span",{children:"The shared template remains unchanged outside the selected scope"})]}),F.jsx("button",{onClick:P,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content override-form-content",children:[F.jsxs("div",{className:"override-scope-choice",children:[F.jsxs("button",{className:H==="instance"?"active":"",onClick:()=>U("instance"),children:[F.jsx("strong",{children:"One instance"}),F.jsx("span",{children:"All targets of this application in one plant or object instance"})]}),F.jsxs("button",{className:H==="object"?"active":"",onClick:()=>U("object"),children:[F.jsx("strong",{children:"One object"}),F.jsx("span",{children:"Only one concrete execution receives the replacement model"})]})]}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Instance",F.jsx("select",{value:G,onChange:hn=>{const wt=hn.target.value,Qn=(x.find(Y=>Y.name===wt)?.objectIds||[]).find(Y=>g.targetIds.some(Fe=>String(Fe)===String(Y)));ie(wt),le(Qn??"")},children:x.filter(hn=>g.targetInstances.includes(hn.name)).map(hn=>F.jsx("option",{value:hn.name,children:hn.name},hn.name))})]}),H==="object"&&F.jsxs("label",{children:["Object",F.jsx("select",{value:String(se),onChange:hn=>le(hn.target.value),children:W.map(hn=>F.jsx("option",{value:String(hn),children:String(hn)},String(hn)))})]}),F.jsxs("label",{children:["Replacement model",F.jsx("select",{value:ee,onChange:hn=>xn(hn.target.value),children:k.map(hn=>F.jsxs("option",{value:hn.type,children:[hn.package?`${hn.package} · `:"",hn.name]},hn.type))})]})]}),ge&&ge.constructor.fields.length>0&&F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Model parameters"}),F.jsx(W0n,{fields:ge.constructor.fields,values:Pe,onChange:ae})]}),F.jsxs("div",{className:"override-warning",children:[F.jsx("strong",{children:H==="instance"?`Override ${G}`:`Override object ${String(se)}`}),F.jsx("span",{children:"Julia validates that the replacement keeps the same process and declared variable contract."})]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:P,children:"Cancel"}),ln&&F.jsxs("button",{className:"danger","data-testid":"remove-override",onClick:()=>N({scope:H,instance:G,objectId:H==="object"?se:void 0,applicationId:g.applicationId,modelType:ee,parameters:Pe}),children:[F.jsx(wue,{size:15})," Remove override"]}),F.jsxs("button",{className:"primary",disabled:!G||!ee||H==="object"&&!String(se),onClick:()=>M({scope:H,instance:G,objectId:H==="object"?se:void 0,applicationId:g.applicationId,modelType:ee,parameters:Pe}),children:[F.jsx(YG,{size:15})," Apply override"]})]})]})})}function VXn(g,E){const x=`${E}__`;return g.startsWith(x)?g.slice(x.length):g}function Z0n(g){if(g.detailMode==="overview")return 190;const E=Math.max(g.applicationId.length,g.modelName.length,...g.inputs.map(x=>x.name.length),...g.outputs.map(x=>x.name.length));return Math.max(310,Math.min(540,235+E*7))}function YXn({data:g,selected:E}){const x=g.detailMode==="overview",M=new Set(g.requiredInputPortIds),N=new Set(g.candidatePortIds),P=new Set(g.previousTimeStepPortIds),k=new Set(g.cycleBreakInputPortIds),H=ZXn(g);return F.jsxs("section",{className:`model-node application-node ${x?"overview-node":""} ${g.cyclic?"cyclic":""} ${E?"selected":""}`,"data-testid":`application-node-${g.applicationId}`,style:{width:Z0n(g)},children:[x&&F.jsx(WXn,{inputs:g.inputs,outputs:g.outputs}),F.jsxs("header",{className:"node-header",children:[F.jsxs("div",{children:[F.jsx("div",{className:"process",children:g.name||g.applicationId}),F.jsx("div",{className:"model-type",children:g.modelName})]}),F.jsx(Y0n,{size:18})]}),x?F.jsxs("div",{className:"overview-node-summary",children:[F.jsxs("span",{children:[g.targetCount," targets"]}),F.jsxs("span",{children:[g.inputs.length," in"]}),F.jsxs("span",{children:[g.outputs.length," out"]})]}):F.jsxs(F.Fragment,{children:[F.jsxs("div",{className:"node-meta",children:[F.jsxs("span",{className:"meta-chip",title:g.selector.julia,children:[F.jsx(jXn,{size:13})," ",H]}),F.jsxs("span",{className:"meta-chip",title:String(g.clock??"Default scene cadence"),children:[F.jsx(xXn,{size:13})," ",eKn(g)]})]}),F.jsxs("div",{className:"target-summary",children:[F.jsx("strong",{children:g.targetCount})," concrete target",g.targetCount===1?"":"s"]}),F.jsxs("div",{className:"ports-grid",children:[F.jsx(oue,{title:"Inputs",side:"input",ports:g.inputs,required:M,candidates:N,previous:P,cycleBreaks:k,cycleBreakMode:g.cycleBreakMode,application:g,onCandidateClick:g.onCandidateClick,onPortClick:g.onPortClick,onCycleBreak:g.onCycleBreak}),F.jsx(oue,{title:"Outputs",side:"output",ports:g.outputs,required:M,candidates:N,previous:P,cycleBreaks:k,cycleBreakMode:g.cycleBreakMode,application:g,onCandidateClick:g.onCandidateClick,onPortClick:g.onPortClick,onCycleBreak:g.onCycleBreak})]}),(g.environmentInputs.length>0||g.environmentOutputs.length>0)&&F.jsxs("div",{className:"ports-grid environment-ports",children:[F.jsx(oue,{title:"Environment inputs",side:"input",ports:g.environmentInputs,required:M,candidates:new Set,previous:new Set,cycleBreaks:new Set,cycleBreakMode:!1,application:g,onPortClick:g.onPortClick}),F.jsx(oue,{title:"Environment outputs",side:"output",ports:g.environmentOutputs,required:M,candidates:new Set,previous:new Set,cycleBreaks:new Set,cycleBreakMode:!1,application:g,onPortClick:g.onPortClick})]})]})]})}function QXn({data:g,selected:E}){return F.jsxs("section",{className:`entity-node ${g.nodeKind} ${E?"selected":""}`,"data-testid":`${g.nodeKind}-node`,children:[(g.inputPortIds?.length?g.inputPortIds:[void 0]).map((x,M)=>F.jsx(o5,{id:x,type:"target",position:cr.Left,style:{top:`${Cue(M,g.inputPortIds?.length??1)}%`}},x??"target")),F.jsxs("header",{children:[F.jsx("strong",{children:g.title}),F.jsx("span",{children:g.subtitle})]}),F.jsx("div",{className:"badges",children:g.badges.map(x=>F.jsx("span",{className:"meta-chip",children:x},x))}),(g.outputPortIds?.length?g.outputPortIds:[void 0]).map((x,M)=>F.jsx(o5,{id:x,type:"source",position:cr.Right,style:{top:`${Cue(M,g.outputPortIds?.length??1)}%`}},x??"source"))]})}function oue({title:g,side:E,ports:x,required:M,candidates:N,previous:P,cycleBreaks:k,cycleBreakMode:H,application:U,onCandidateClick:G,onPortClick:ie,onCycleBreak:Z}){return F.jsxs("div",{className:`port-column ${E}`,children:[F.jsx("div",{className:"port-title",children:g}),x.map(W=>F.jsxs("div",{className:`port ${M.has(W.id)?"required-input":""} ${P.has(W.id)?"previous":""}`,"data-testid":`port-${E}-${W.name}`,title:`${W.name}: ${W.defaultJulia}`,onClick:se=>{se.stopPropagation(),ie?.(W)},children:[E==="input"&&F.jsx(o5,{id:W.id,type:"target",position:cr.Left}),F.jsx("span",{children:W.name}),N.has(W.id)&&F.jsx("button",{className:"port-candidate-button nodrag nopan",type:"button",title:E==="input"?"Models that compute this variable":"Models that consume this variable","aria-label":E==="input"?`Models that compute ${W.name}`:`Models that consume ${W.name}`,onClick:se=>{se.stopPropagation();const le=se.currentTarget.getBoundingClientRect();G?.(W,{x:le.right,y:le.top+le.height/2})},children:F.jsx(QG,{size:11})}),E==="input"&&H&&k.has(W.id)&&F.jsx("button",{className:"cycle-port-break nodrag nopan",type:"button",title:`Read ${W.name} from the previous accepted timestep`,"aria-label":`Break cycle at ${U.applicationId}.${W.name}`,"data-testid":`cycle-break-${U.applicationId}-${W.name}`,onClick:se=>{se.stopPropagation(),Z?.(U,W)},children:F.jsx(Q0n,{size:12})}),P.has(W.id)&&F.jsx("small",{className:"previous-label",children:"t-1"}),E==="output"&&F.jsx(o5,{id:W.id,type:"source",position:cr.Right})]},W.id))]})}function WXn({inputs:g,outputs:E}){return F.jsxs(F.Fragment,{children:[g.map((x,M)=>F.jsx(o5,{id:x.id,type:"target",position:cr.Left,style:{top:`${Cue(M,g.length)}%`}},x.id)),E.map((x,M)=>F.jsx(o5,{id:x.id,type:"source",position:cr.Right,style:{top:`${Cue(M,E.length)}%`}},x.id))]})}function Cue(g,E){return E<=1?52:28+g/(E-1)*48}function ZXn(g){const E=[...g.targetInstances,...g.targetScales,...g.targetKinds];return E.length>0?E.slice(0,2).join(" / "):g.selector.type}function eKn(g){return g.timestep===null||g.timestep===void 0?"default rate":String(g.timestep)}function nKn({id:g,sourceX:E,sourceY:x,targetX:M,targetY:N,sourcePosition:P=cr.Right,targetPosition:k=cr.Left,markerEnd:H,style:U,data:G}){const[ie,Z,W]=Aue({sourceX:E,sourceY:x,targetX:M,targetY:N,sourcePosition:P,targetPosition:k,borderRadius:14,offset:24}),se=tKn(G);return F.jsxs(F.Fragment,{children:[F.jsx(cq,{id:g,path:ie,markerEnd:H,style:U,interactionWidth:18}),se&&F.jsx(JUn,{children:F.jsx("div",{className:`edge-chip ${G?.kind??""} ${G?.cycle?"cycle":""}`,style:{transform:`translate(-50%, -50%) translate(${Z}px, ${W-12}px)`},children:se})})]})}function tKn(g){return g?g.kind==="manual_call"?g.call||"call":g.kind==="object_topology"||g.kind==="application_target"?"":g.sourceVariable&&g.targetVariable?g.sourceVariable===g.targetVariable?g.sourceVariable:`${g.sourceVariable} → ${g.targetVariable}`:g.kind.replaceAll("_"," "):""}function sue(g){throw new Error('Could not dynamically require "'+g+'". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.')}var K7e={exports:{}},Z1n;function iKn(){return Z1n||(Z1n=1,(function(g,E){(function(x){g.exports=x()})(function(){return(function(){function x(M,N,P){function k(G,ie){if(!N[G]){if(!M[G]){var Z=typeof sue=="function"&&sue;if(!ie&&Z)return Z(G,!0);if(H)return H(G,!0);var W=new Error("Cannot find module '"+G+"'");throw W.code="MODULE_NOT_FOUND",W}var se=N[G]={exports:{}};M[G][0].call(se.exports,function(le){var ee=M[G][1][le];return k(ee||le)},se,se.exports,x,M,N,P)}return N[G].exports}for(var H=typeof sue=="function"&&sue,U=0;U0&&arguments[0]!==void 0?arguments[0]:{},ee=le.defaultLayoutOptions,Oe=ee===void 0?{}:ee,ge=le.algorithms,Pe=ge===void 0?["layered","stress","mrtree","radial","force","disco","sporeOverlap","sporeCompaction","rectpacking"]:ge,ae=le.workerFactory,Me=le.workerUrl;if(k(this,W),this.defaultLayoutOptions=Oe,this.initialized=!1,typeof Me>"u"&&typeof ae>"u")throw new Error("Cannot construct an ELK without both 'workerUrl' and 'workerFactory'.");var Be=ae;typeof Me<"u"&&typeof ae>"u"&&(Be=function(xn){return new Worker(xn)});var rn=Be(Me);if(typeof rn.postMessage!="function")throw new TypeError("Created worker does not provide the required 'postMessage' function.");this.worker=new Z(rn),this.worker.postMessage({cmd:"register",algorithms:Pe}).then(function(ln){return se.initialized=!0}).catch(console.err)}return U(W,[{key:"layout",value:function(le){var ee=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},Oe=ee.layoutOptions,ge=Oe===void 0?this.defaultLayoutOptions:Oe,Pe=ee.logging,ae=Pe===void 0?!1:Pe,Me=ee.measureExecutionTime,Be=Me===void 0?!1:Me;return le?this.worker.postMessage({cmd:"layout",graph:le,layoutOptions:ge,options:{logging:ae,measureExecutionTime:Be}}):Promise.reject(new Error("Missing mandatory parameter 'graph'."))}},{key:"knownLayoutAlgorithms",value:function(){return this.worker.postMessage({cmd:"algorithms"})}},{key:"knownLayoutOptions",value:function(){return this.worker.postMessage({cmd:"options"})}},{key:"knownLayoutCategories",value:function(){return this.worker.postMessage({cmd:"categories"})}},{key:"terminateWorker",value:function(){this.worker&&this.worker.terminate()}}])})();var Z=(function(){function W(se){var le=this;if(k(this,W),se===void 0)throw new Error("Missing mandatory parameter 'worker'.");this.resolvers={},this.worker=se,this.worker.onmessage=function(ee){setTimeout(function(){le.receive(le,ee)},0)}}return U(W,[{key:"postMessage",value:function(le){var ee=this.id||0;this.id=ee+1,le.id=ee;var Oe=this;return new Promise(function(ge,Pe){Oe.resolvers[ee]=function(ae,Me){ae?(Oe.convertGwtStyleError(ae),Pe(ae)):ge(Me)},Oe.worker.postMessage(le)})}},{key:"receive",value:function(le,ee){var Oe=ee.data,ge=le.resolvers[Oe.id];ge&&(delete le.resolvers[Oe.id],Oe.error?ge(Oe.error):ge(null,Oe.data))}},{key:"terminate",value:function(){this.worker&&this.worker.terminate()}},{key:"convertGwtStyleError",value:function(le){if(le){var ee=le.__java$exception;ee&&(ee.cause&&ee.cause.backingJsObject&&(le.cause=ee.cause.backingJsObject,this.convertGwtStyleError(le.cause)),delete le.__java$exception)}}}])})()},{}],2:[function(x,M,N){(function(P){(function(){var k;typeof window<"u"?k=window:typeof P<"u"?k=P:typeof self<"u"&&(k=self);var H;function U(){}function G(){}function ie(){}function Z(){}function W(){}function se(){}function le(){}function ee(){}function Oe(){}function ge(){}function Pe(){}function ae(){}function Me(){}function Be(){}function rn(){}function ln(){}function xn(){}function hn(){}function wt(){}function Cn(){}function Qn(){}function Y(){}function Fe(){}function mn(){}function Ae(){}function Ze(){}function sn(){}function Sn(){}function kn(){}function xe(){}function un(){}function rt(){}function lt(){}function Bt(){}function hi(){}function Gt(){}function At(){}function st(){}function Wr(){}function Mr(){}function ur(){}function mi(){}function Fi(){}function fu(){}function Eu(){}function Ws(){}function Dh(){}function ef(){}function ch(){}function kc(){}function cd(){}function jb(){}function Rs(){}function c0(){}function u0(){}function o0(){}function Bg(){}function r6(){}function zg(){}function c6(){}function d1(){}function ud(){}function Sf(){}function b1(){}function xf(){}function l5(){}function f5(){}function a5(){}function Fg(){}function Eb(){}function Jg(){}function _u(){}function Hg(){}function Gg(){}function u6(){}function h5(){}function Wm(){}function qg(){}function o6(){}function Ug(){}function Zm(){}function d5(){}function Mt(){}function Tc(){}function dc(){}function s6(){}function Af(){}function Zs(){}function Ca(){}function Xg(){}function b5(){}function ek(){}function MA(){}function nk(){}function e3(){}function l6(){}function xp(){}function Ap(){}function Mp(){}function Tp(){}function xl(){}function g5(){}function tk(){}function Kg(){}function Cp(){}function TA(){}function f6(){}function ik(){}function rk(){}function n3(){}function w5(){}function s0(){}function Ih(){}function ck(){}function CA(){}function p5(){}function uk(){}function t3(){}function OA(){}function _h(){}function i3(){}function ok(){}function a6(){}function Vg(){}function vI(){}function yI(){}function m5(){}function uq(){}function kI(){}function jI(){}function NA(){}function oq(){}function sq(){}function sk(){}function Yg(){}function DA(){}function IA(){}function v5(){}function y5(){}function EI(){}function _A(){}function SI(){}function h6(){}function Qg(){}function LA(){}function d6(){}function Op(){}function PA(){}function lk(){}function xI(){}function fk(){}function ak(){}function AI(){}function Lh(){}function r3(){}function hk(){}function b6(){}function lq(){}function $A(){}function RA(){}function g6(){}function dk(){}function MI(){}function fq(){}function aq(){}function hq(){}function BA(){}function dq(){}function bq(){}function gq(){}function wq(){}function pq(){}function TI(){}function mq(){}function vq(){}function yq(){}function kq(){}function zA(){}function jq(){}function Eq(){}function Sq(){}function CI(){}function xq(){}function Aq(){}function Mq(){}function Tq(){}function Cq(){}function Oq(){}function Nq(){}function Dq(){}function Iq(){}function FA(){}function w6(){}function _q(){}function OI(){}function NI(){}function DI(){}function II(){}function _I(){}function k5(){}function Lq(){}function Pq(){}function $q(){}function LI(){}function PI(){}function p6(){}function m6(){}function Rq(){}function bk(){}function $I(){}function JA(){}function HA(){}function GA(){}function RI(){}function BI(){}function zI(){}function Bq(){}function zq(){}function Fq(){}function Jq(){}function Hq(){}function g1(){}function v6(){}function FI(){}function JI(){}function HI(){}function GI(){}function qA(){}function Gq(){}function j5(){}function UA(){}function y6(){}function XA(){}function qI(){}function c3(){}function E5(){}function KA(){}function UI(){}function u3(){}function XI(){}function KI(){}function VI(){}function qq(){}function Uq(){}function Xq(){}function YI(){}function QI(){}function VA(){}function l0(){}function gk(){}function od(){}function S5(){}function YA(){}function wk(){}function pk(){}function QA(){}function o3(){}function WI(){}function mk(){}function x5(){}function Kq(){}function w1(){}function WA(){}function Wg(){}function ZI(){}function vk(){}function s3(){}function ZA(){}function e_(){}function eM(){}function n_(){}function sd(){}function A5(){}function M5(){}function yk(){}function k6(){}function ld(){}function fd(){}function Np(){}function Sb(){}function xb(){}function Zg(){}function t_(){}function nM(){}function tM(){}function i_(){}function ra(){}function Jo(){}function cu(){}function Dp(){}function ad(){}function iM(){}function Ip(){}function r_(){}function c_(){}function T5(){}function l3(){}function C5(){}function _p(){}function rM(){}function Lp(){}function ew(){}function Pp(){}function nw(){}function cM(){}function uM(){}function O5(){}function j6(){}function $p(){}function ca(){}function E6(){}function oM(){}function Vq(){}function Yq(){}function S6(){}function Al(){}function sM(){}function x6(){}function A6(){}function lM(){}function N5(){}function D5(){}function Qq(){}function u_(){}function Wq(){}function o_(){}function f3(){}function fM(){}function kk(){}function s_(){}function I5(){}function aM(){}function jk(){}function Ek(){}function l_(){}function f_(){}function a3(){}function h3(){}function a_(){}function hM(){}function _5(){}function M6(){}function Sk(){}function T6(){}function xk(){}function h_(){}function d3(){}function d_(){}function Rp(){}function dM(){}function bM(){}function Bp(){}function zp(){}function C6(){}function gM(){}function wM(){}function O6(){}function N6(){}function b_(){}function g_(){}function L5(){}function Ak(){}function w_(){}function pM(){}function mM(){}function p1(){}function hd(){}function Fp(){}function vM(){}function p_(){}function Jp(){}function m1(){}function el(){}function Mk(){}function tw(){}function bc(){}function vo(){}function Ml(){}function Tk(){}function P5(){}function b3(){}function Ck(){}function D6(){}function $5(){}function Zq(){}function Bs(){}function yM(){}function kM(){}function m_(){}function v_(){}function eU(){}function jM(){}function EM(){}function SM(){}function uh(){}function nl(){}function Ok(){}function I6(){}function Nk(){}function xM(){}function iw(){}function Dk(){}function AM(){}function MM(){}function y_(){}function k_(){}function j_(){}function nU(){}function E_(){}function S_(){}function TM(){}function x_(){}function tU(){}function A_(){}function M_(){}function T_(){}function CM(){}function C_(){}function O_(){}function N_(){}function D_(){}function I_(){}function iU(){}function __(){}function R5(){}function L_(){}function Ik(){}function _k(){}function P_(){}function OM(){}function rU(){}function $_(){}function R_(){}function B_(){}function z_(){}function F_(){}function NM(){}function J_(){}function H_(){}function DM(){}function G_(){}function q_(){}function IM(){}function _6(){}function U_(){}function Lk(){}function _M(){}function X_(){}function K_(){}function cU(){}function uU(){}function V_(){}function L6(){}function LM(){}function Pk(){}function Y_(){}function PM(){}function P6(){}function oU(){}function $M(){}function Q_(){}function RM(){}function BM(){}function W_(){}function Z_(){}function g3(){}function eL(){}function dd(){}function nL(){}function f0(){}function zM(){}function FM(){}function tL(){}function iL(){}function sU(){}function JM(){}function Tl(){}function ua(){}function rL(){}function cL(){}function uL(){}function oL(){}function $6(){}function sL(){}function $k(){}function lL(){}function lU(){}function Rk(){}function HM(){}function fL(){}function aL(){}function Je(){}function GM(){}function qM(){}function UM(){}function hL(){}function XM(){}function Bk(){}function KM(){}function dL(){}function VM(){}function bL(){}function rw(){}function R6(){}function fU(){}function gL(){}function a0(){}function YM(){}function wL(){}function zk(){}function w3(){}function yo(){}function Fk(){}function aU(){}function QM(){}function B6(){}function Hp(){}function z6(){}function pL(){}function F6(){}function Ab(){}function J6(){}function WM(){}function mL(){}function ZM(){}function eT(){}function p3(){}function vL(){}function Mb(){}function Cl(){}function H6(){}function nT(){}function Mf(){}function hU(){}function yL(){}function kL(){}function Ss(){}function Ph(){}function cw(){}function jL(){}function EL(){}function SL(){}function dU(){}function Jk(){}function $h(){}function h0(){}function xL(){}function Ol(){}function Hk(){}function uw(){}function m3(){}function ow(){}function tT(){}function iT(){}function d0(){}function AL(){}function B5(){}function G6(){}function q6(){}function z5(){}function ML(){}function TL(){}function U6(){}function CL(){}function Gk(){}function OL(){}function bU(){}function gU(){}function Ju(){}function Io(){}function Hc(){}function nu(){}function io(){}function v1(){}function Gp(){}function F5(){}function rT(){}function sw(){}function zs(){}function qp(){}function v3(){}function cT(){}function y1(){}function J5(){}function X6(){}function Rh(){}function uT(){}function qk(){}function NL(){}function Uk(){}function Xk(){}function Up(){}function nf(){}function Xp(){}function H5(){}function lw(){}function oT(){}function sT(){}function DL(){}function K6(){}function lT(){}function k1(){}function IL(){}function Bh(){}function _L(){}function LL(){}function wU(){}function Kp(){}function Kk(){}function fT(){}function G5(){}function PL(){}function $L(){}function RL(){}function BL(){}function Vk(){}function aT(){}function pU(){}function mU(){}function vU(){}function zL(){}function FL(){}function q5(){}function Yk(){}function JL(){}function HL(){}function GL(){}function qL(){}function UL(){}function XL(){}function Qk(){}function KL(){}function VL(){}function ro(){}function hT(){}function yU(){}function YL(){}function kU(){}function jU(){}function EU(){}function Wk(){}function U5(){}function dT(){}function Zk(){}function bT(){}function Vp(){}function Tb(){}function V6(){}function SU(){}function QL(){}function gT(){}function WL(){}function ZL(){}function wT(){mj()}function pT(){T0e()}function eP(){Gf()}function nP(){Rde()}function xU(){PO()}function mT(){OC()}function vT(){iC()}function AU(){tC()}function MU(){TMe()}function Y6(){z4()}function TU(){i$e()}function tP(){n8()}function Gc(){F0()}function yT(){Bhe()}function ej(){X_e()}function kT(){Rhe()}function iP(){V_e()}function jT(){K_e()}function Q6(){Y_e()}function nj(){L$e()}function CU(){Q_e()}function rP(){ABe()}function OU(){Ne()}function NU(){QP()}function cP(){SBe()}function uP(){xBe()}function ko(){VLe()}function ET(){Ige()}function oa(){MBe()}function DU(){Z_e()}function oP(){R4()}function IU(){BFe()}function ST(){P1()}function xT(){cbe()}function tj(){FO()}function sP(){VBe()}function lP(){Gbe()}function AT(){THe()}function MT(){W_e()}function _U(){QXe()}function fP(){Mu()}function LU(){Ja()}function aP(){nge()}function PU(){J0()}function $U(){IW()}function Yp(){JQ()}function hP(){tB()}function TT(){Xt()}function CT(){Ez()}function RU(){$B()}function BU(){bde()}function OT(){Gz()}function ij(){FY()}function tl(){INe()}function zU(){rge()}function j1(e){_n(e)}function NT(e){this.a=e}function W6(e){this.a=e}function dP(e){this.a=e}function bP(e){this.a=e}function Z6(e){this.a=e}function E1(e){this.a=e}function DT(e){this.a=e}function rj(e){this.a=e}function b0(e){this.a=e}function FU(e){this.a=e}function JU(e){this.a=e}function X5(e){this.a=e}function gP(e){this.a=e}function HU(e){this.c=e}function GU(e){this.a=e}function IT(e){this.a=e}function qU(e){this.a=e}function UU(e){this.a=e}function XU(e){this.a=e}function _T(e){this.a=e}function wP(e){this.a=e}function K5(e){this.a=e}function y3(e){this.a=e}function pP(e){this.a=e}function LT(e){this.a=e}function V5(e){this.a=e}function e9(e){this.a=e}function mP(e){this.a=e}function cj(e){this.a=e}function PT(e){this.a=e}function $T(e){this.a=e}function uj(e){this.a=e}function vP(e){this.a=e}function yP(e){this.a=e}function KU(e){this.a=e}function kP(e){this.a=e}function VU(e){this.a=e}function RT(e){this.a=e}function YU(e){this.a=e}function n9(e){this.a=e}function t9(e){this.a=e}function k3(e){this.a=e}function i9(e){this.a=e}function Y5(e){this.b=e}function bd(){this.a=[]}function jP(e,n){e.a=n}function QU(e,n){e.a=n}function WU(e,n){e.b=n}function ZU(e,n){e.c=n}function EP(e,n){e.c=n}function eX(e,n){e.d=n}function nX(e,n){e.d=n}function Tf(e,n){e.k=n}function SP(e,n){e.j=n}function Jue(e,n){e.c=n}function oj(e,n){e.c=n}function sj(e,n){e.a=n}function BT(e,n){e.a=n}function xP(e,n){e.f=n}function tX(e,n){e.a=n}function AP(e,n){e.b=n}function Cb(e,n){e.d=n}function g0(e,n){e.i=n}function fw(e,n){e.o=n}function lj(e,n){e.r=n}function fj(e,n){e.a=n}function j3(e,n){e.b=n}function iX(e,n){e.e=n}function rX(e,n){e.f=n}function Q5(e,n){e.g=n}function Hue(e,n){e.e=n}function cX(e,n){e.f=n}function zT(e,n){e.f=n}function aj(e,n){e.a=n}function FT(e,n){e.b=n}function JT(e,n){e.n=n}function HT(e,n){e.a=n}function uX(e,n){e.c=n}function r9(e,n){e.c=n}function oX(e,n){e.c=n}function MP(e,n){e.a=n}function GT(e,n){e.a=n}function sX(e,n){e.d=n}function Gue(e,n){e.d=n}function qT(e,n){e.e=n}function a(e,n){e.e=n}function d(e,n){e.g=n}function w(e,n){e.f=n}function j(e,n){e.j=n}function C(e,n){e.a=n}function D(e,n){e.a=n}function Q(e,n){e.b=n}function de(e){e.b=e.a}function en(e){e.c=e.d.d}function Ln(e){this.a=e}function nt(e){this.a=e}function gt(e){this.a=e}function Jn(e){this.a=e}function Zn(e){this.a=e}function Di(e){this.a=e}function Tr(e){this.a=e}function co(e){this.a=e}function En(e){this.a=e}function on(e){this.a=e}function In(e){this.a=e}function ct(e){this.a=e}function Ji(e){this.a=e}function Lu(e){this.a=e}function Ui(e){this.b=e}function Hr(e){this.b=e}function tc(e){this.b=e}function qc(e){this.d=e}function xt(e){this.a=e}function lX(e){this.a=e}function que(e){this.a=e}function _ke(e){this.a=e}function Lke(e){this.a=e}function Uue(e){this.a=e}function Xue(e){this.a=e}function fX(e){this.c=e}function L(e){this.c=e}function Pke(e){this.c=e}function Kue(e){this.a=e}function Vue(e){this.a=e}function Yue(e){this.a=e}function Que(e){this.a=e}function c9(e){this.a=e}function $ke(e){this.a=e}function Rke(e){this.a=e}function u9(e){this.a=e}function Bke(e){this.a=e}function zke(e){this.a=e}function Fke(e){this.a=e}function Jke(e){this.a=e}function Hke(e){this.a=e}function Gke(e){this.a=e}function qke(e){this.a=e}function Uke(e){this.a=e}function Xke(e){this.a=e}function Kke(e){this.a=e}function Vke(e){this.a=e}function hj(e){this.a=e}function Yke(e){this.a=e}function Qke(e){this.a=e}function TP(e){this.a=e}function Wke(e){this.a=e}function Zke(e){this.a=e}function Wue(e){this.a=e}function eje(e){this.a=e}function nje(e){this.a=e}function tje(e){this.a=e}function Zue(e){this.a=e}function eoe(e){this.a=e}function noe(e){this.a=e}function dj(e){this.a=e}function o9(e){this.a=e}function ije(e){this.a=e}function W5(e){this.a=e}function toe(e){this.a=e}function rje(e){this.a=e}function cje(e){this.a=e}function uje(e){this.a=e}function oje(e){this.a=e}function sje(e){this.a=e}function lje(e){this.a=e}function fje(e){this.a=e}function aje(e){this.a=e}function hje(e){this.a=e}function dje(e){this.a=e}function bje(e){this.a=e}function ioe(e){this.a=e}function gje(e){this.a=e}function wje(e){this.a=e}function pje(e){this.a=e}function mje(e){this.a=e}function vje(e){this.a=e}function yje(e){this.a=e}function kje(e){this.a=e}function jje(e){this.a=e}function Eje(e){this.a=e}function Sje(e){this.a=e}function xje(e){this.a=e}function Aje(e){this.a=e}function Mje(e){this.a=e}function Tje(e){this.a=e}function Cje(e){this.a=e}function Oje(e){this.a=e}function Nje(e){this.a=e}function Dje(e){this.a=e}function Ije(e){this.a=e}function _je(e){this.a=e}function Lje(e){this.a=e}function Pje(e){this.a=e}function $je(e){this.a=e}function Rje(e){this.a=e}function Bje(e){this.a=e}function zje(e){this.a=e}function Fje(e){this.a=e}function Jje(e){this.a=e}function Hje(e){this.a=e}function Gje(e){this.a=e}function qje(e){this.a=e}function Uje(e){this.a=e}function Xje(e){this.a=e}function Kje(e){this.a=e}function Vje(e){this.a=e}function Yje(e){this.a=e}function Qje(e){this.b=e}function Wje(e){this.a=e}function Zje(e){this.a=e}function eEe(e){this.a=e}function nEe(e){this.a=e}function tEe(e){this.a=e}function iEe(e){this.a=e}function rEe(e){this.c=e}function cEe(e){this.a=e}function uEe(e){this.a=e}function oEe(e){this.a=e}function sEe(e){this.a=e}function lEe(e){this.a=e}function fEe(e){this.a=e}function aEe(e){this.a=e}function hEe(e){this.a=e}function dEe(e){this.a=e}function bEe(e){this.a=e}function gEe(e){this.a=e}function wEe(e){this.a=e}function pEe(e){this.a=e}function mEe(e){this.a=e}function vEe(e){this.a=e}function yEe(e){this.a=e}function kEe(e){this.a=e}function jEe(e){this.a=e}function EEe(e){this.a=e}function SEe(e){this.a=e}function xEe(e){this.a=e}function AEe(e){this.a=e}function MEe(e){this.a=e}function TEe(e){this.a=e}function CEe(e){this.a=e}function OEe(e){this.a=e}function NEe(e){this.a=e}function S1(e){this.a=e}function E3(e){this.a=e}function DEe(e){this.a=e}function IEe(e){this.a=e}function _Ee(e){this.a=e}function LEe(e){this.a=e}function PEe(e){this.a=e}function $Ee(e){this.a=e}function REe(e){this.a=e}function BEe(e){this.a=e}function zEe(e){this.a=e}function FEe(e){this.a=e}function JEe(e){this.a=e}function HEe(e){this.a=e}function GEe(e){this.a=e}function qEe(e){this.a=e}function UEe(e){this.a=e}function XEe(e){this.a=e}function KEe(e){this.a=e}function VEe(e){this.a=e}function YEe(e){this.a=e}function QEe(e){this.a=e}function WEe(e){this.a=e}function ZEe(e){this.a=e}function eSe(e){this.a=e}function nSe(e){this.a=e}function tSe(e){this.a=e}function iSe(e){this.a=e}function CP(e){this.a=e}function rSe(e){this.f=e}function cSe(e){this.a=e}function uSe(e){this.a=e}function oSe(e){this.a=e}function sSe(e){this.a=e}function lSe(e){this.a=e}function fSe(e){this.a=e}function aSe(e){this.a=e}function hSe(e){this.a=e}function dSe(e){this.a=e}function bSe(e){this.a=e}function gSe(e){this.a=e}function wSe(e){this.a=e}function pSe(e){this.a=e}function mSe(e){this.a=e}function vSe(e){this.a=e}function ySe(e){this.a=e}function kSe(e){this.a=e}function jSe(e){this.a=e}function ESe(e){this.a=e}function SSe(e){this.a=e}function xSe(e){this.a=e}function ASe(e){this.a=e}function MSe(e){this.a=e}function TSe(e){this.a=e}function CSe(e){this.a=e}function OSe(e){this.a=e}function NSe(e){this.a=e}function aX(e){this.a=e}function roe(e){this.a=e}function yi(e){this.b=e}function DSe(e){this.a=e}function ISe(e){this.a=e}function _Se(e){this.a=e}function LSe(e){this.a=e}function PSe(e){this.a=e}function $Se(e){this.a=e}function RSe(e){this.a=e}function BSe(e){this.b=e}function zSe(e){this.a=e}function UT(e){this.a=e}function FSe(e){this.a=e}function JSe(e){this.a=e}function OP(e){this.a=e}function NP(e){this.a=e}function coe(e){this.c=e}function DP(e){this.e=e}function IP(e){this.e=e}function hX(e){this.a=e}function HSe(e){this.d=e}function GSe(e){this.a=e}function uoe(e){this.a=e}function ooe(e){this.a=e}function aw(e){this.e=e}function ebn(){this.a=0}function Ce(){MK(this)}function pt(){Hu(this)}function dX(){_Ie(this)}function qSe(){}function hw(){this.c=Q8e}function USe(e,n){e.b+=n}function nbn(e,n){n.Wb(e)}function tbn(e){return e.a}function ibn(e){return e.a}function rbn(e){return e.a}function cbn(e){return e.a}function ubn(e){return e.a}function $(e){return e.e}function obn(){return null}function sbn(){return null}function lbn(e){throw $(e)}function Z5(e){this.a=Nt(e)}function XSe(){this.a=this}function Ob(){aOe.call(this)}function fbn(e){e.b.Mf(e.e)}function KSe(e){e.b=new OX}function bj(e,n){e.b=n-e.b}function gj(e,n){e.a=n-e.a}function VSe(e,n){n.gd(e.a)}function abn(e,n){xr(n,e)}function Hn(e,n){e.push(n)}function YSe(e,n){e.sort(n)}function hbn(e,n,t){e.Wd(t,n)}function XT(e,n){e.e=n,n.b=e}function dbn(){zoe(),LRn()}function QSe(e){$9(),tte.je(e)}function soe(){Ob.call(this)}function bX(){Ob.call(this)}function loe(){aOe.call(this)}function WSe(){Ob.call(this)}function Nl(){Ob.call(this)}function ZSe(){Ob.call(this)}function KT(){Ob.call(this)}function is(){Ob.call(this)}function e4(){Ob.call(this)}function _t(){Ob.call(this)}function au(){Ob.call(this)}function exe(){Ob.call(this)}function _P(){this.Bb|=256}function nxe(){this.b=new fCe}function foe(){foe=Y,new pt}function Qp(e,n){e.length=n}function LP(e,n){Te(e.a,n)}function bbn(e,n){O0e(e.c,n)}function gbn(e,n){hr(e.b,n)}function s9(e,n){ai(e.e,n)}function wbn(e,n){lz(e.a,n)}function pbn(e,n){gQ(e.a,n)}function n4(e){Mz(e.c,e.b)}function mbn(e,n){e.kc().Nb(n)}function aoe(e){this.a=Tjn(e)}function ar(){this.a=new pt}function txe(){this.a=new pt}function PP(){this.a=new Ce}function gX(){this.a=new Ce}function hoe(){this.a=new Ce}function Nb(){this.a=new XPe}function wX(){this.a=new kMe}function doe(){this.a=new $_e}function boe(){this.a=new iNe}function tf(){this.a=new d1}function goe(){this.a=new Zm}function ixe(){this.a=new wLe}function rxe(){this.a=new Ce}function cxe(){this.a=new Ce}function woe(){this.a=new Ce}function uxe(){this.a=new Ce}function oxe(){this.d=new Ce}function sxe(){this.a=new ar}function lxe(){this.a=new pt}function fxe(){this.b=new pt}function axe(){this.b=new Ce}function poe(){this.e=new Ce}function hxe(){this.a=new Gc}function dxe(){this.d=new Ce}function wj(){qSe.call(this)}function pX(){wj.call(this)}function t4(){qSe.call(this)}function moe(){t4.call(this)}function bxe(){soe.call(this)}function $P(){PP.call(this)}function gxe(){q$.call(this)}function wxe(){woe.call(this)}function pxe(){Ce.call(this)}function mxe(){b_e.call(this)}function vxe(){b_e.call(this)}function yxe(){joe.call(this)}function kxe(){joe.call(this)}function jxe(){joe.call(this)}function Exe(){Eoe.call(this)}function pj(){zk.call(this)}function voe(){zk.call(this)}function xs(){Si.call(this)}function Sxe(){Rxe.call(this)}function xxe(){Rxe.call(this)}function Axe(){pt.call(this)}function Mxe(){pt.call(this)}function Txe(){pt.call(this)}function mX(){yBe.call(this)}function Cxe(){ar.call(this)}function Oxe(){_P.call(this)}function vX(){cle.call(this)}function yoe(){pt.call(this)}function yX(){cle.call(this)}function kX(){pt.call(this)}function Nxe(){pt.call(this)}function koe(){p3.call(this)}function Dxe(){koe.call(this)}function Ixe(){p3.call(this)}function _xe(){gT.call(this)}function joe(){this.a=new ar}function Lxe(){this.a=new pt}function Pxe(){this.a=new Ce}function $xe(){this.j=new Ce}function Eoe(){this.a=new pt}function i4(){this.a=new Si}function Rxe(){this.a=new WM}function Soe(){this.a=new z_}function Bxe(){this.a=new PAe}function mj(){mj=Y,Kne=new G}function jX(){jX=Y,Vne=new Fxe}function EX(){EX=Y,Yne=new zxe}function zxe(){y3.call(this,"")}function Fxe(){y3.call(this,"")}function Jxe(e){YRe.call(this,e)}function Hxe(e){YRe.call(this,e)}function xoe(e){E1.call(this,e)}function Aoe(e){VAe.call(this,e)}function vbn(e){VAe.call(this,e)}function ybn(e){Aoe.call(this,e)}function kbn(e){Aoe.call(this,e)}function jbn(e){Aoe.call(this,e)}function Gxe(e){cY.call(this,e)}function qxe(e){cY.call(this,e)}function Uxe(e){KCe.call(this,e)}function Xxe(e){Xoe.call(this,e)}function vj(e){KP.call(this,e)}function Moe(e){KP.call(this,e)}function Kxe(e){KP.call(this,e)}function hu(e){JDe.call(this,e)}function Vxe(e){hu.call(this,e)}function r4(){i9.call(this,{})}function SX(e){y9(),this.a=e}function Yxe(e){e.b=null,e.c=0}function Ebn(e,n){e.e=n,YUe(e,n)}function Sbn(e,n){e.a=n,PTn(e)}function xX(e,n,t){e.a[n.g]=t}function xbn(e,n,t){iAn(t,e,n)}function Abn(e,n){i2n(n.i,e.n)}function Qxe(e,n){pkn(e).Ad(n)}function Mbn(e,n){return e*e/n}function Wxe(e,n){return e.g-n.g}function Tbn(e,n){e.a.ec().Kc(n)}function Cbn(e){return new k3(e)}function Obn(e){return new y2(e)}function Zxe(){Zxe=Y,vme=new U}function Toe(){Toe=Y,yme=new Be}function RP(){RP=Y,US=new xn}function BP(){BP=Y,Wne=new XCe}function eAe(){eAe=Y,Wen=new wt}function zP(e){n1e(),this.a=e}function AX(e){lV(),this.f=e}function w0(e){lV(),this.f=e}function nAe(e){DNe(),this.a=e}function FP(e){hu.call(this,e)}function jo(e){hu.call(this,e)}function tAe(e){hu.call(this,e)}function MX(e){JDe.call(this,e)}function l9(e){hu.call(this,e)}function Gn(e){hu.call(this,e)}function Uc(e){hu.call(this,e)}function iAe(e){hu.call(this,e)}function c4(e){hu.call(this,e)}function gd(e){hu.call(this,e)}function Su(e){_n(e),this.a=e}function yj(e){$fe(e,e.length)}function Coe(e){return Zb(e),e}function Wp(e){return!!e&&e.b}function Nbn(e){return!!e&&e.k}function Dbn(e){return!!e&&e.j}function kj(e){return e.b==e.c}function Re(e){return _n(e),e}function ne(e){return _n(e),e}function VT(e){return _n(e),e}function Ooe(e){return _n(e),e}function Ibn(e){return _n(e),e}function oh(e){hu.call(this,e)}function wd(e){hu.call(this,e)}function u4(e){hu.call(this,e)}function TX(e){hu.call(this,e)}function zt(e){hu.call(this,e)}function CX(e){dle.call(this,e,0)}function OX(){Sae.call(this,12,3)}function NX(){this.a=Pt(Nt(Co))}function rAe(){throw $(new _t)}function Noe(){throw $(new _t)}function cAe(){throw $(new _t)}function _bn(){throw $(new _t)}function Lbn(){throw $(new _t)}function Pbn(){throw $(new _t)}function JP(){JP=Y,$9()}function pd(){Di.call(this,"")}function jj(){Di.call(this,"")}function p0(){Di.call(this,"")}function o4(){Di.call(this,"")}function Doe(e){jo.call(this,e)}function Ioe(e){jo.call(this,e)}function sh(e){Gn.call(this,e)}function f9(e){Hr.call(this,e)}function uAe(e){f9.call(this,e)}function DX(e){B$.call(this,e)}function $bn(e,n,t){e.c.Cf(n,t)}function Rbn(e,n,t){n.Ad(e.a[t])}function Bbn(e,n,t){n.Ne(e.a[t])}function zbn(e,n){return e.a-n.a}function Fbn(e,n){return e.a-n.a}function Jbn(e,n){return e.a-n.a}function HP(e,n){return vY(e,n)}function B(e,n){return H_e(e,n)}function Hbn(e,n){return n in e.a}function oAe(e){return e.a?e.b:0}function Gbn(e){return e.a?e.b:0}function sAe(e,n){return e.f=n,e}function qbn(e,n){return e.b=n,e}function lAe(e,n){return e.c=n,e}function Ubn(e,n){return e.g=n,e}function _oe(e,n){return e.a=n,e}function Loe(e,n){return e.f=n,e}function Xbn(e,n){return e.f=n,e}function Poe(e,n){return e.e=n,e}function Kbn(e,n){return e.k=n,e}function $oe(e,n){return e.a=n,e}function Vbn(e,n){return e.e=n,e}function Ybn(e,n){e.b=new wc(n)}function fAe(e,n){e._d(n),n.$d(e)}function Qbn(e,n){rl(),n.n.a+=e}function Wbn(e,n){F0(),gu(n,e)}function Roe(e){UIe.call(this,e)}function aAe(e){UIe.call(this,e)}function hAe(){qse.call(this,"")}function dAe(){this.b=0,this.a=0}function bAe(){bAe=Y,ann=OAn()}function Zp(e,n){return e.b=n,e}function GP(e,n){return e.a=n,e}function e2(e,n){return e.c=n,e}function n2(e,n){return e.d=n,e}function t2(e,n){return e.e=n,e}function Boe(e,n){return e.f=n,e}function Ej(e,n){return e.a=n,e}function a9(e,n){return e.b=n,e}function h9(e,n){return e.c=n,e}function qe(e,n){return e.c=n,e}function an(e,n){return e.b=n,e}function Ue(e,n){return e.d=n,e}function Xe(e,n){return e.e=n,e}function Zbn(e,n){return e.f=n,e}function Ke(e,n){return e.g=n,e}function Ve(e,n){return e.a=n,e}function Ye(e,n){return e.i=n,e}function Qe(e,n){return e.j=n,e}function egn(e,n){return n.pg(e)}function ngn(e,n){return e.b-n.b}function tgn(e,n){return e.g-n.g}function ign(e,n){return e.s-n.s}function rgn(e,n){return e?0:n-1}function gAe(e,n){return e?0:n-1}function cgn(e,n){return e?n-1:0}function wAe(e,n){return e.k=n,e}function ugn(e,n){return e.j=n,e}function Kr(){this.a=0,this.b=0}function qP(e){UK.call(this,e)}function m0(e){Nw.call(this,e)}function pAe(e){PV.call(this,e)}function mAe(e){PV.call(this,e)}function vAe(){vAe=Y,Pr=QAn()}function v0(){v0=Y,yan=Fxn()}function zoe(){zoe=Y,Ng=EE()}function d9(){d9=Y,Y8e=Jxn()}function yAe(){yAe=Y,rhn=Hxn()}function Foe(){Foe=Y,zu=ITn()}function sa(e){return e.e&&e.e()}function kAe(e,n){return e.c._b(n)}function jAe(e,n){return yFe(e.b,n)}function EAe(e,n){return Dgn(e.a,n)}function SAe(e,n){e.b=0,O2(e,n)}function ogn(e,n){e.c=n,e.b=!0}function S3(e,n){return e.a+=n,e}function IX(e,n){return e.a+=n,e}function md(e,n){return e.a+=n,e}function dw(e,n){return e.a+=n,e}function Db(e){return M1(e),e.o}function Joe(e){MVe(),KRn(this,e)}function xAe(){throw $(new _t)}function AAe(){throw $(new _t)}function MAe(){throw $(new _t)}function TAe(){throw $(new _t)}function CAe(){throw $(new _t)}function OAe(){throw $(new _t)}function UP(e){this.a=new l4(e)}function vd(e){this.a=new bV(e)}function x3(e,n){for(;e.Pe(n););}function Hoe(e,n){for(;e.zd(n););}function sgn(e,n,t){avn(e.a,n,t)}function Goe(e,n,t){e.splice(n,t)}function lgn(e,n){return GLn(n,e)}function qoe(e,n){return e.d[n.p]}function YT(e){return e.b!=e.d.c}function NAe(e){return e.l|e.m<<22}function _X(e){return e?e.d:null}function fgn(e){return e?e.g:null}function agn(e){return e?e.i:null}function DAe(e,n){return aDn(e,n)}function b9(e){return A0(e),e.a}function IAe(e){e.c?hXe(e):dXe(e)}function _Ae(){this.b=new tS(iye)}function LAe(){this.b=new tS(Qre)}function PAe(){this.b=new tS(Qre)}function $Ae(){this.a=new tS(Rye)}function RAe(){this.a=new tS(s6e)}function XP(e){this.a=0,this.b=e}function BAe(){throw $(new _t)}function zAe(){throw $(new _t)}function FAe(){throw $(new _t)}function JAe(){throw $(new _t)}function HAe(){throw $(new _t)}function GAe(){throw $(new _t)}function qAe(){throw $(new _t)}function UAe(){throw $(new _t)}function XAe(){throw $(new _t)}function KAe(){throw $(new _t)}function hgn(){throw $(new au)}function dgn(){throw $(new au)}function QT(e){this.a=new wMe(e)}function g9(e,n){this.e=e,this.d=n}function Uoe(e,n){this.b=e,this.c=n}function VAe(e){tle(e.dc()),this.c=e}function WT(e,n){R3.call(this,e,n)}function w9(e,n){WT.call(this,e,n)}function YAe(e,n){this.a=e,this.b=n}function QAe(e,n){this.a=e,this.b=n}function WAe(e,n){this.a=e,this.b=n}function ZAe(e,n){this.a=e,this.b=n}function eMe(e,n){this.a=e,this.b=n}function nMe(e,n){this.a=e,this.b=n}function tMe(e,n){this.a=e,this.b=n}function iMe(e,n){this.b=e,this.a=n}function rMe(e,n){this.b=e,this.a=n}function bw(e,n){this.g=e,this.i=n}function cMe(e,n){this.a=e,this.b=n}function uMe(e,n){this.b=e,this.a=n}function oMe(e,n){this.a=e,this.b=n}function sMe(e,n){this.b=e,this.a=n}function KP(e){this.b=u(Nt(e),50)}function VP(e){this.b=u(Nt(e),92)}function Ot(e,n){this.f=e,this.g=n}function LX(e,n){this.a=e,this.b=n}function lMe(e,n){this.a=e,this.f=n}function fMe(e){this.a=u(Nt(e),16)}function Xoe(e){this.a=u(Nt(e),16)}function aMe(e,n){this.b=e,this.c=n}function hMe(e){this.a=u(Nt(e),92)}function bgn(e,n){this.a=e,this.b=n}function dMe(e,n){this.a=e,this.b=n}function bMe(e,n){return so(e.b,n)}function gMe(e,n){return e>n&&n0}function JX(e,n){return ao(e,n)<0}function LMe(e,n){return oV(e.a,n)}function _gn(e,n){R_e.call(this,e,n)}function ise(e){CV(),VMn.call(this,e)}function rse(e){CV(),ise.call(this,e)}function cse(e){rV(),KCe.call(this,e)}function use(e,n){ODe(e,e.length,n)}function rC(e,n){cIe(e,e.length,n)}function Dj(e,n){return e.a.get(n)}function PMe(e,n){return so(e.e,n)}function ose(e){return _n(e),!1}function $Me(){return bAe(),new ann}function cC(e){return at(e.a),e.b}function RMe(e,n){this.b=e,this.a=n}function r$(e,n){this.d=e,this.e=n}function BMe(e,n){this.a=e,this.b=n}function zMe(e,n){this.a=e,this.b=n}function FMe(e,n){this.a=e,this.b=n}function JMe(e,n){this.a=e,this.b=n}function HMe(e,n){this.b=e,this.a=n}function f4(e,n){this.a=e,this.b=n}function c$(e,n){Ot.call(this,e,n)}function HX(e,n){Ot.call(this,e,n)}function GX(e,n){Ot.call(this,e,n)}function qX(e,n){Ot.call(this,e,n)}function UX(e,n){Ot.call(this,e,n)}function u$(e,n){Ot.call(this,e,n)}function o$(e){pn.call(this,e,21)}function GMe(e,n){this.b=e,this.a=n}function sse(e,n){this.b=e,this.a=n}function lse(e,n){this.b=e,this.a=n}function fse(e,n){Ot.call(this,e,n)}function XX(e,n){Ot.call(this,e,n)}function uC(e,n){Ot.call(this,e,n)}function ase(e,n){this.b=e,this.a=n}function v9(e,n){this.c=e,this.d=n}function s$(e,n){Ot.call(this,e,n)}function l$(e,n){Ot.call(this,e,n)}function qMe(e,n){this.e=e,this.d=n}function a4(e,n){Ot.call(this,e,n)}function UMe(e,n){this.a=e,this.b=n}function hse(e,n){Ot.call(this,e,n)}function br(e,n){Ot.call(this,e,n)}function f$(e,n){Ot.call(this,e,n)}function Ij(e,n,t){e.splice(n,0,t)}function Lgn(e,n,t){e.Mb(t)&&n.Ad(t)}function Pgn(e,n,t){n.Ne(e.a.We(t))}function $gn(e,n,t){n.Bd(e.a.Xe(t))}function Rgn(e,n,t){n.Ad(e.a.Kb(t))}function Bgn(e,n){return cs(e.c,n)}function zgn(e,n){return cs(e.e,n)}function XMe(e,n){this.a=e,this.b=n}function KMe(e,n){this.a=e,this.b=n}function VMe(e,n){this.a=e,this.b=n}function YMe(e,n){this.a=e,this.b=n}function QMe(e,n){this.a=e,this.b=n}function WMe(e,n){this.a=e,this.b=n}function ZMe(e,n){this.a=e,this.b=n}function eTe(e,n){this.a=e,this.b=n}function nTe(e,n){this.b=e,this.a=n}function tTe(e,n){this.b=e,this.a=n}function iTe(e,n){this.b=e,this.a=n}function rTe(e,n){this.b=n,this.c=e}function a$(e,n){Ot.call(this,e,n)}function oC(e,n){Ot.call(this,e,n)}function dse(e,n){Ot.call(this,e,n)}function _j(e,n){Ot.call(this,e,n)}function h$(e,n){Ot.call(this,e,n)}function KX(e,n){Ot.call(this,e,n)}function VX(e,n){Ot.call(this,e,n)}function Lj(e,n){Ot.call(this,e,n)}function Pj(e,n){Ot.call(this,e,n)}function bse(e,n){Ot.call(this,e,n)}function A3(e,n){Ot.call(this,e,n)}function YX(e,n){Ot.call(this,e,n)}function $j(e,n){Ot.call(this,e,n)}function gse(e,n){Ot.call(this,e,n)}function c2(e,n){Ot.call(this,e,n)}function QX(e,n){Ot.call(this,e,n)}function WX(e,n){Ot.call(this,e,n)}function ZX(e,n){Ot.call(this,e,n)}function wse(e,n){Ot.call(this,e,n)}function sC(e,n){Ot.call(this,e,n)}function pse(e,n){Ot.call(this,e,n)}function M3(e,n){Ot.call(this,e,n)}function eK(e,n){Ot.call(this,e,n)}function d$(e,n){Ot.call(this,e,n)}function lC(e,n){Ot.call(this,e,n)}function u2(e,n){Ot.call(this,e,n)}function b$(e,n){Ot.call(this,e,n)}function mse(e,n){Ot.call(this,e,n)}function nK(e,n){Ot.call(this,e,n)}function tK(e,n){Ot.call(this,e,n)}function iK(e,n){Ot.call(this,e,n)}function rK(e,n){Ot.call(this,e,n)}function cK(e,n){Ot.call(this,e,n)}function uK(e,n){Ot.call(this,e,n)}function g$(e,n){Ot.call(this,e,n)}function cTe(e,n){this.b=e,this.a=n}function vse(e,n){Ot.call(this,e,n)}function uTe(e,n){this.a=e,this.b=n}function oTe(e,n){this.a=e,this.b=n}function sTe(e,n){this.a=e,this.b=n}function yse(e,n){Ot.call(this,e,n)}function kse(e,n){Ot.call(this,e,n)}function lTe(e,n){this.a=e,this.b=n}function Fgn(e,n){return A9(),n!=e}function oK(e){return FCn(e,e.c),e}function Jgn(e){k.clearTimeout(e)}function jse(e,n){Ot.call(this,e,n)}function Ese(e,n){Ot.call(this,e,n)}function fTe(e,n){this.a=e,this.b=n}function aTe(e,n){this.a=e,this.b=n}function hTe(e,n){this.b=e,this.d=n}function dTe(e,n){this.a=e,this.b=n}function bTe(e,n){this.b=e,this.a=n}function w$(e,n){Ot.call(this,e,n)}function gw(e,n){Ot.call(this,e,n)}function sK(e,n){Ot.call(this,e,n)}function p$(e,n){Ot.call(this,e,n)}function Sse(e,n){Ot.call(this,e,n)}function gTe(e,n){this.b=e,this.a=n}function wTe(e,n){this.b=e,this.a=n}function pTe(e,n){this.b=e,this.a=n}function mTe(e,n){this.b=e,this.a=n}function xse(e,n){Ot.call(this,e,n)}function fC(e,n){Ot.call(this,e,n)}function Ase(e,n){Ot.call(this,e,n)}function lK(e,n){Ot.call(this,e,n)}function m$(e,n){Ot.call(this,e,n)}function fK(e,n){Ot.call(this,e,n)}function aK(e,n){Ot.call(this,e,n)}function v$(e,n){Ot.call(this,e,n)}function hK(e,n){Ot.call(this,e,n)}function Mse(e,n){Ot.call(this,e,n)}function dK(e,n){Ot.call(this,e,n)}function bK(e,n){Ot.call(this,e,n)}function aC(e,n){Ot.call(this,e,n)}function gK(e,n){Ot.call(this,e,n)}function Tse(e,n){Ot.call(this,e,n)}function hC(e,n){Ot.call(this,e,n)}function Cse(e,n){Ot.call(this,e,n)}function Ose(e,n){this.a=e,this.b=n}function vTe(e,n){this.a=e,this.b=n}function yTe(e,n){this.a=e,this.b=n}function kTe(){K$(),this.a=new Lle}function jTe(){$z(),this.a=new ar}function ETe(){qV(),this.b=new ar}function STe(){jae(),Afe.call(this)}function xTe(){kae(),d_e.call(this)}function ATe(){kae(),d_e.call(this)}function dC(e,n){Ot.call(this,e,n)}function h4(e,n){Ot.call(this,e,n)}function Rj(e,n){Ot.call(this,e,n)}function Bj(e,n){Ot.call(this,e,n)}function bC(e,n){Ot.call(this,e,n)}function y$(e,n){Ot.call(this,e,n)}function wK(e,n){Ot.call(this,e,n)}function k$(e,n){Ot.call(this,e,n)}function zj(e,n){Ot.call(this,e,n)}function pK(e,n){Ot.call(this,e,n)}function j$(e,n){Ot.call(this,e,n)}function T3(e,n){Ot.call(this,e,n)}function gC(e,n){Ot.call(this,e,n)}function Fj(e,n){Ot.call(this,e,n)}function Jj(e,n){Ot.call(this,e,n)}function mK(e,n){Ot.call(this,e,n)}function wC(e,n){Ot.call(this,e,n)}function E$(e,n){Ot.call(this,e,n)}function C3(e,n){Ot.call(this,e,n)}function vK(e,n){Ot.call(this,e,n)}function yK(e,n){Ot.call(this,e,n)}function S$(e,n){Ot.call(this,e,n)}function je(e,n){this.a=e,this.b=n}function MTe(e,n){this.a=e,this.b=n}function TTe(e,n){this.a=e,this.b=n}function CTe(e,n){this.a=e,this.b=n}function OTe(e,n){this.a=e,this.b=n}function NTe(e,n){this.a=e,this.b=n}function DTe(e,n){this.a=e,this.b=n}function jc(e,n){this.a=e,this.b=n}function ITe(e,n){this.a=e,this.b=n}function _Te(e,n){this.a=e,this.b=n}function LTe(e,n){this.a=e,this.b=n}function PTe(e,n){this.a=e,this.b=n}function $Te(e,n){this.a=e,this.b=n}function RTe(e,n){this.a=e,this.b=n}function BTe(e,n){this.b=e,this.a=n}function zTe(e,n){this.b=e,this.a=n}function FTe(e,n){this.b=e,this.a=n}function JTe(e,n){this.b=e,this.a=n}function HTe(e,n){this.a=e,this.b=n}function GTe(e,n){this.a=e,this.b=n}function qTe(e,n){this.a=e,this.b=n}function UTe(e,n){this.a=e,this.b=n}function XTe(e,n){this.f=e,this.c=n}function Nse(e,n){this.i=e,this.g=n}function x$(e,n){Ot.call(this,e,n)}function d4(e,n){Ot.call(this,e,n)}function A$(e,n){this.a=e,this.b=n}function KTe(e,n){this.a=e,this.b=n}function Dse(e,n){this.d=e,this.e=n}function VTe(e,n){this.a=e,this.b=n}function YTe(e,n){this.a=e,this.b=n}function QTe(e,n){this.d=e,this.b=n}function WTe(e,n){this.e=e,this.a=n}function Ise(e,n){e.i=null,EB(e,n)}function Hgn(e,n){e&&ei(WD,e,n)}function ZTe(e,n){return SQ(e.a,n)}function _se(e,n){return cs(e.g,n)}function Ggn(e,n){return cs(n.b,e)}function qgn(e,n){return-e.b.$e(n)}function M$(e){return OO(e.c,e.b)}function Ugn(e,n){u8n(new ut(e),n)}function Xgn(e,n,t){KHe(n,mW(e,t))}function Kgn(e,n,t){KHe(n,mW(e,t))}function eCe(e,n){V9n(e.a,u(n,12))}function nCe(e,n){this.a=e,this.b=n}function pC(e,n){this.b=e,this.c=n}function j0(e,n){return e.Pd().Xb(n)}function T$(e,n){return v7n(e.Jc(),n)}function du(e){return e?e.kd():null}function ue(e){return e??null}function o2(e){return typeof e===ry}function s2(e){return typeof e===Lge}function $r(e){return typeof e===fZ}function Hj(e,n){return ao(e,n)==0}function C$(e,n){return ao(e,n)>=0}function Gj(e,n){return ao(e,n)!=0}function Lse(e,n){return e.a+=""+n,e}function Vgn(e){return""+(_n(e),e)}function tCe(e){return Ds(e),e.d.gc()}function Pse(e){return vn(e,0),null}function O$(e){return nE(e==null),e}function qj(e,n){return e.a+=""+n,e}function Bc(e,n){return e.a+=""+n,e}function Uj(e,n){return e.a+=""+n,e}function uo(e,n){return e.a+=""+n,e}function Kt(e,n){return e.a+=""+n,e}function iCe(e,n){e.q.setTime(Xb(n))}function rCe(e,n){Ife.call(this,e,n)}function cCe(e,n){Ife.call(this,e,n)}function N$(e,n){Ife.call(this,e,n)}function gc(e,n){Xi(e,n,e.c.b,e.c)}function O3(e,n){Xi(e,n,e.a,e.a.a)}function Ygn(e,n){return e.j[n.p]==2}function uCe(e,n){return e.a=n.g+1,e}function la(e){return e.a=0,e.b=0,e}function oCe(e){Hu(this),xE(this,e)}function sCe(){this.b=0,this.a=!1}function lCe(){this.b=0,this.a=!1}function fCe(){this.b=new l4(I2(12))}function aCe(){aCe=Y,ttn=It(DQ())}function hCe(){hCe=Y,fin=It(FUe())}function dCe(){dCe=Y,tsn=It(oze())}function $se(){$se=Y,foe(),kme=new pt}function Qgn(e){return Nt(e),new Xj(e)}function bCe(e,n){return ue(e)===ue(n)}function D$(e){return e<10?"0"+e:""+e}function gCe(e){return _o(e.l,e.m,e.h)}function uu(e){return typeof e===Lge}function kK(e,n){return of(e.a,0,n)}function b4(e){return sc((_n(e),e))}function Wgn(e){return sc((_n(e),e))}function Zgn(e,n){return ki(e.a,n.a)}function Rse(e,n){return oo(e.a,n.a)}function ewn(e,n){return iIe(e.a,n.a)}function lh(e,n){return e.indexOf(n)}function Bse(e,n){J9(e,0,e.length,n)}function ti(e,n){n$(),ei(kG,e,n)}function fn(e,n){Li.call(this,e,n)}function jK(e,n){b2.call(this,e,n)}function N3(e,n){Nse.call(this,e,n)}function wCe(e,n){jC.call(this,e,n)}function EK(e,n){Y9.call(this,e,n)}function zh(){Uue.call(this,new O0)}function pCe(){fR.call(this,0,0,0,0)}function zse(e){return wu(e.b.b,e,0)}function mCe(e,n){return oo(e.g,n.g)}function nwn(e){return e==op||e==sm}function twn(e){return e==op||e==om}function iwn(e,n){return oo(e.g,n.g)}function rwn(e,n){return rl(),n.a+=e}function cwn(e,n){return rl(),n.a+=e}function uwn(e,n){return rl(),n.c+=e}function own(e,n){return Te(e.c,n),e}function vCe(e,n){return Te(e.a,n),n}function Fse(e,n){return fl(e.a,n),e}function yCe(e){this.a=$Me(),this.b=e}function kCe(e){this.a=$Me(),this.b=e}function wc(e){this.a=e.a,this.b=e.b}function Xj(e){this.a=e,wT.call(this)}function jCe(e){this.a=e,wT.call(this)}function Fs(e){return e.sh()&&e.th()}function D3(e){return e!=nh&&e!=bb}function x1(e){return e==Zc||e==ru}function I3(e){return e==Vl||e==Za}function ECe(e){return e==Fv||e==zv}function I$(e){return fl(new or,e)}function SCe(e){return OV(u(e,125))}function swn(e,n){return ki(n.f,e.f)}function xCe(e,n){return new Y9(n,e)}function lwn(e,n){return new Y9(n,e)}function Dl(e,n,t){Os(e,n),Ns(e,t)}function SK(e,n,t){bB(e,n),gB(e,t)}function ww(e,n,t){Iw(e,n),Dw(e,t)}function mC(e,n,t){X3(e,n),K3(e,t)}function vC(e,n,t){V3(e,n),Y3(e,t)}function xK(e,n){i8(e,n),U9(e,e.D)}function AK(e){XTe.call(this,e,!0)}function g4(){Lf.call(this,0,0,0,0)}function ACe(){c$.call(this,"Head",1)}function MCe(){c$.call(this,"Tail",3)}function TCe(e,n,t){Sle.call(this,e,n,t)}function pw(e){fR.call(this,e,e,e,e)}function E0(e){mh(),x7n.call(this,e)}function CCe(e){Ao(e.Qf(),new Qke(e))}function _3(e){return e!=null?Oi(e):0}function fwn(e,n){return C2(n,Ia(e))}function awn(e,n){return C2(n,Ia(e))}function hwn(e,n){return e[e.length]=n}function dwn(e,n){return e[e.length]=n}function bwn(e,n){return yB(xV(e.f),n)}function gwn(e,n){return yB(xV(e.n),n)}function wwn(e,n){return yB(xV(e.p),n)}function Jse(e){return S3n(e.b.Jc(),e.a)}function pwn(e){return e==null?0:Oi(e)}function MK(e){e.c=oe(Ar,On,1,0,5,1)}function OCe(e,n,t){tr(e.c[n.g],n.g,t)}function mwn(e,n,t){u(e.c,72).Ei(n,t)}function vwn(e,n,t){Dl(t,t.i+e,t.j+n)}function Vr(e,n){Li.call(this,e.b,n)}function ywn(e,n){Et(Vu(e.a),oLe(n))}function kwn(e,n){Et(Cs(e.a),sLe(n))}function jwn(e,n){Ka||(e.b=n)}function TK(e,n,t){return tr(e,n,t),t}function Lt(){Lt=Y,new NCe,new Ce}function NCe(){new pt,new pt,new pt}function Ewn(){throw $(new gd(Pen))}function Swn(){throw $(new gd(Pen))}function xwn(){throw $(new gd($en))}function Awn(){throw $(new gd($en))}function DCe(){DCe=Y,fre=new zE(Ace)}function Oa(){Oa=Y,k.Math.log(2)}function Il(){Il=Y,h1=(NMe(),Aan)}function Kj(e){fi(),aw.call(this,e)}function ICe(e){this.a=e,rfe.call(this,e)}function CK(e){this.a=e,VP.call(this,e)}function OK(e){this.a=e,VP.call(this,e)}function Cr(e,n){uV(e.c,e.c.length,n)}function bu(e){return e.an?1:0}function Gse(e,n){return ao(e,n)>0?e:n}function _o(e,n,t){return{l:e,m:n,h:t}}function Mwn(e,n){e.a!=null&&eCe(n,e.a)}function Twn(e){lc(e,null),Gr(e,null)}function Cwn(e,n,t){return ei(e.g,t,n)}function Own(e,n){Nt(n),z3(e).Ic(new Pe)}function LCe(){Vde(),this.a=new tS(p3e)}function _$(e){this.b=e,this.a=new Ce}function PCe(e){this.b=new o6,this.a=e}function qse(e){Ple.call(this),this.a=e}function $Ce(e){hae.call(this),this.b=e}function RCe(){c$.call(this,"Range",2)}function L$(e){e.j=oe(_me,Se,324,0,0,1)}function BCe(e){e.a=new Bt,e.c=new Bt}function zCe(e){e.a=new pt,e.e=new pt}function Use(e){return new je(e.c,e.d)}function Nwn(e){return new je(e.c,e.d)}function pc(e){return new je(e.a,e.b)}function Dwn(e,n){return ei(e.a,n.a,n)}function Iwn(e,n,t){return ei(e.k,t,n)}function L3(e,n,t){return gde(n,t,e.c)}function Xse(e,n){return re(Bn(e.i,n))}function Kse(e,n){return re(Bn(e.j,n))}function FCe(e,n){return d$n(e.a,n,null)}function Vj(e,n){return EPn(e.c,e.b,n)}function X(e,n){return e!=null&&PQ(e,n)}function JCe(e,n){kt(e),e.Fc(u(n,16))}function _wn(e,n,t){e.c._c(n,u(t,136))}function Lwn(e,n,t){e.c.Si(n,u(t,136))}function Pwn(e,n,t){return a$n(e,n,t),t}function $wn(e,n){return cl(),n.n.b+=e}function NK(e,n){return ekn(e.Jc(),n)!=-1}function Rwn(e,n){return new wOe(e.Jc(),n)}function P$(e){return e.Ob()?e.Pb():null}function HCe(e){return gh(e,0,e.length)}function GCe(e){XV(e,null),KV(e,null)}function qCe(){jC.call(this,null,null)}function UCe(){J$.call(this,null,null)}function XCe(){Ot.call(this,"INSTANCE",0)}function P3(){this.a=oe(Ar,On,1,8,5,1)}function Vse(e){this.a=e,pt.call(this)}function KCe(e){this.a=(jn(),new f9(e))}function Bwn(e){this.b=(jn(),new fX(e))}function y9(){y9=Y,Gme=new SX(null)}function Yse(){Yse=Y,Yse(),bnn=new At}function Te(e,n){return Hn(e.c,n),!0}function VCe(e,n){e.c&&(pfe(n),x_e(n))}function zwn(e,n){e.q.setHours(n),oS(e,n)}function Qse(e,n){return e.a.Ac(n)!=null}function DK(e,n){return e.a.Ac(n)!=null}function Na(e,n){return e.a[n.c.p][n.p]}function Fwn(e,n){return e.e[n.c.p][n.p]}function Jwn(e,n){return e.c[n.c.p][n.p]}function IK(e,n,t){return e.a[n.g][t.g]}function Hwn(e,n){return e.j[n.p]=VOn(n)}function w4(e,n){return e.a*n.a+e.b*n.b}function Gwn(e,n){return e.a=e}function Vwn(e,n,t){return t?n!=0:n!=e-1}function YCe(e,n,t){e.a=n^1502,e.b=t^FZ}function Ywn(e,n,t){return e.a=n,e.b=t,e}function A1(e,n){return e.a*=n,e.b*=n,e}function Yj(e,n,t){return tr(e.g,n,t),t}function Qwn(e,n,t,i){tr(e.a[n.g],t.g,i)}function mr(e,n,t){_C.call(this,e,n,t)}function $$(e,n,t){mr.call(this,e,n,t)}function rs(e,n,t){mr.call(this,e,n,t)}function QCe(e,n,t){$$.call(this,e,n,t)}function Wse(e,n,t){_C.call(this,e,n,t)}function $3(e,n,t){_C.call(this,e,n,t)}function WCe(e,n,t){Z$.call(this,e,n,t)}function Zse(e,n,t){Z$.call(this,e,n,t)}function ZCe(e,n,t){Zse.call(this,e,n,t)}function eOe(e,n,t){Wse.call(this,e,n,t)}function S0(e){this.c=e,this.a=this.c.a}function ut(e){this.i=e,this.f=this.i.j}function R3(e,n){this.a=e,VP.call(this,n)}function nOe(e,n){this.a=e,CX.call(this,n)}function tOe(e,n){this.a=e,CX.call(this,n)}function iOe(e,n){this.a=e,CX.call(this,n)}function ele(e){this.a=e,HU.call(this,e.d)}function rOe(e){e.b.Qb(),--e.d.f.d,hR(e.d)}function cOe(e){e.a=u(Un(e.b.a,4),129)}function uOe(e){e.a=u(Un(e.b.a,4),129)}function Wwn(e){FC(e,lZe),Dz(e,sRn(e))}function nle(e,n){return Cjn(e,new p0,n).a}function Zwn(e){return YT(e.a)?uLe(e):null}function oOe(e){y3.call(this,u(Nt(e),35))}function sOe(e){y3.call(this,u(Nt(e),35))}function tle(e){if(!e)throw $(new KT)}function ile(e){if(!e)throw $(new is)}function Vn(e,n){return Nt(n),new gOe(e,n)}function lOe(e,n){return new WGe(e.a,e.b,n)}function epn(e){return e.l+e.m*sy+e.h*sg}function npn(e){return e==null?null:e.name}function rle(e,n,t){return e.indexOf(n,t)}function R$(e,n){return e.lastIndexOf(n)}function Qj(e){return e==null?Vo:su(e)}function Pn(){Pn=Y,eb=!1,a7=!0}function fOe(){fOe=Y,BX(),nhn=new zU}function cle(){this.Bb|=256,this.Bb|=512}function aOe(){L$(this),MR(this),this.he()}function B$(e){Hr.call(this,e),this.a=e}function ule(e){tc.call(this,e),this.a=e}function ole(e){f9.call(this,e),this.a=e}function cf(e){Di.call(this,(_n(e),e))}function il(e){Di.call(this,(_n(e),e))}function _K(e){Uue.call(this,new lhe(e))}function hOe(e){this.a=e,Ui.call(this,e)}function sle(e,n){this.a=n,CX.call(this,e)}function dOe(e,n){this.a=n,cY.call(this,e)}function bOe(e,n){this.a=e,cY.call(this,n)}function gOe(e,n){this.a=n,KP.call(this,e)}function wOe(e,n){this.a=n,KP.call(this,e)}function lle(e){wX.call(this),fc(this,e)}function Js(e){return at(e.a!=null),e.a}function pOe(e,n){return Te(n.a,e.a),e.a}function mOe(e,n){return Te(n.b,e.a),e.a}function mw(e,n){return Te(n.a,e.a),e.a}function yC(e,n,t){return qY(e,n,n,t),e}function z$(e,n){return++e.b,Te(e.a,n)}function fle(e,n){return++e.b,qo(e.a,n)}function tpn(e,n){return ki(e.c.d,n.c.d)}function ipn(e,n){return ki(e.c.c,n.c.c)}function rpn(e,n){return ki(e.n.a,n.n.a)}function Ho(e,n){return u(pi(e.b,n),16)}function cpn(e,n){return e.n.b=(_n(n),n)}function upn(e,n){return e.n.b=(_n(n),n)}function cs(e,n){return!!n&&e.b[n.g]==n}function Wj(e){return bu(e.a)||bu(e.b)}function opn(e,n){return ki(e.e.b,n.e.b)}function spn(e,n){return ki(e.e.a,n.e.a)}function lpn(e,n,t){return iPe(e,n,t,e.b)}function ale(e,n,t){return iPe(e,n,t,e.c)}function fpn(e){return rl(),!!e&&!e.dc()}function vOe(){Mj(),this.b=new Ije(this)}function F$(){F$=Y,wJ=new Li(QYe,0)}function p4(e){this.d=e,ut.call(this,e)}function m4(e){this.c=e,ut.call(this,e)}function kC(e){this.c=e,p4.call(this,e)}function hle(e,n){Sde.call(this,e,n,null)}function v4(e){return e.a!=null?e.a:null}function vw(e){return e.$H||(e.$H=++NBn)}function jd(e){var n;n=e.a,e.a=e.b,e.b=n}function jC(e,n){Nj(),this.a=e,this.b=n}function J$(e,n){kd(),this.b=e,this.c=n}function H$(e,n){lV(),this.f=n,this.d=e}function dle(e,n){Zae(n,e),this.c=e,this.b=n}function apn(e,n){return hV(e.c).Kd().Xb(n)}function LK(e,n){return new yNe(e,e.gc(),n)}function hpn(e){return BP(),Dt((eLe(),qen),e)}function dpn(e){return new A2(3,e)}function Fh(e){return ll(e,Q2),new xo(e)}function yOe(e){return $9(),parseInt(e)||-1}function k9(e,n,t){return rle(e,Xo(n),t)}function ble(e,n,t){u(cO(e,n),22).Ec(t)}function bpn(e,n,t){gQ(e.a,t),lz(e.a,n)}function j9(e,n,t){var i;i=e.dd(n),i.Rb(t)}function kOe(e,n,t,i){Nfe.call(this,e,n,t,i)}function jOe(e){ufe.call(this,e,null,null)}function PK(e){i2(),this.b=e,this.a=!0}function EOe(e){YP(),this.b=e,this.a=!0}function SOe(e){if(!e)throw $(new Nl)}function gle(e){if(!e)throw $(new KT)}function gpn(e){if(!e)throw $(new bX)}function at(e){if(!e)throw $(new au)}function l2(e){if(!e)throw $(new is)}function xOe(e){e.d=new jOe(e),e.e=new pt}function E9(e){return at(e.b!=0),e.a.a.c}function If(e){return at(e.b!=0),e.c.b.c}function wpn(e,n){return qY(e,n,n+1,""),e}function AOe(e){sZ(),KSe(this),this.Df(e)}function MOe(e){this.c=e,this.a=1,this.b=1}function EC(e){X(e,161)&&u(e,161).mi()}function TOe(e){return e.b=u(oae(e.a),45)}function f2(e,n){return u(Pa(e.a,n),35)}function bi(e,n){return!!e.q&&so(e.q,n)}function ppn(e,n){return e>0?n/(e*e):n*100}function mpn(e,n){return e>0?n*n/e:n*n*100}function vpn(e){return e.f!=null?e.f:""+e.g}function $K(e){return e.f!=null?e.f:""+e.g}function ypn(e){return P1(),e.e.a+e.f.a/2}function kpn(e){return P1(),e.e.b+e.f.b/2}function jpn(e,n,t){return P1(),t.e.b-e*n}function Epn(e,n,t){return P1(),t.e.a-e*n}function Spn(e,n,t){return WP(),t.Lg(e,n)}function xpn(e,n){return F0(),gn(e,n.e,n)}function Apn(e,n,t){return Te(n,WFe(e,t))}function Mpn(e,n,t){tB(),e.nf(n)&&t.Ad(e)}function a2(e,n,t){return e.a+=n,e.b+=t,e}function COe(e,n,t){return e.a-=n,e.b-=t,e}function wle(e,n){return e.a=n.a,e.b=n.b,e}function G$(e){return e.a=-e.a,e.b=-e.b,e}function OOe(e){this.c=e,Os(e,0),Ns(e,0)}function NOe(e){Si.call(this),SE(this,e)}function DOe(){Ot.call(this,"GROW_TREE",0)}function Hs(e,n,t){os.call(this,e,n,t,2)}function IOe(e,n){kd(),ple.call(this,e,n)}function ple(e,n){kd(),J$.call(this,e,n)}function _Oe(e,n){kd(),J$.call(this,e,n)}function LOe(e,n){Nj(),jC.call(this,e,n)}function RK(e,n){Il(),sR.call(this,e,n)}function POe(e,n){Il(),RK.call(this,e,n)}function mle(e,n){Il(),RK.call(this,e,n)}function $Oe(e,n){Il(),mle.call(this,e,n)}function vle(e,n){Il(),sR.call(this,e,n)}function ROe(e,n){Il(),vle.call(this,e,n)}function BOe(e,n){Il(),sR.call(this,e,n)}function Tpn(e,n){return e.c.Ec(u(n,136))}function Cpn(e,n){return u(Bn(e.e,n),26)}function Opn(e,n){return u(Bn(e.e,n),26)}function yle(e,n,t){return Kz(oO(e,n),t)}function Npn(e,n,t){return n.xl(e.e,e.c,t)}function Dpn(e,n,t){return n.yl(e.e,e.c,t)}function BK(e,n){return $0(e.e,u(n,52))}function Ipn(e,n,t){$E(Vu(e.a),n,oLe(t))}function _pn(e,n,t){$E(Cs(e.a),n,sLe(t))}function zOe(e,n){return _n(e),e+qK(n)}function Lpn(e){return e==null?null:su(e)}function Ppn(e){return e==null?null:su(e)}function $pn(e){return e==null?null:cTn(e)}function Rpn(e){return e==null?null:Z$n(e)}function M1(e){e.o==null&&SOn(e)}function $e(e){return nE(e==null||o2(e)),e}function re(e){return nE(e==null||s2(e)),e}function Pt(e){return nE(e==null||$r(e)),e}function Bpn(e,n){return GQ(e,n),new NIe(e,n)}function SC(e,n){this.c=e,g9.call(this,e,n)}function Zj(e,n){this.a=e,SC.call(this,e,n)}function zpn(e,n){this.d=e,en(this),this.b=n}function kle(){yBe.call(this),this.Bb|=Ec}function FOe(){this.a=new Tw,this.b=new Tw}function jle(e){this.q=new k.Date(Xb(e))}function B3(){B3=Y,Gv=new yi("root")}function S9(){S9=Y,eI=new Sxe,new xxe}function h2(){h2=Y,Qme=nn((Vs(),Og))}function Fpn(e,n){n.a?qCn(e,n):DK(e.a,n.b)}function JOe(e,n){Ka||Te(e.a,n)}function Jpn(e,n){return iC(),V9(n.d.i,e)}function Hpn(e,n){return z4(),new $Xe(n,e)}function Gpn(e,n,t){return e.Le(n,t)<=0?t:n}function qpn(e,n,t){return e.Le(n,t)<=0?n:t}function Upn(e,n){return u(Pa(e.b,n),144)}function Xpn(e,n){return u(Pa(e.c,n),233)}function zK(e){return u(Le(e.a,e.b),295)}function HOe(e){return new je(e.c,e.d+e.a)}function GOe(e){return _n(e),e?1231:1237}function qOe(e){return cl(),ECe(u(e,203))}function Ele(e,n){return u(Bn(e.b,n),278)}function UOe(e,n,t){++e.j,e.oj(n,e.Xi(n,t))}function xC(e,n,t){++e.j,e.rj(),bY(e,n,t)}function Sle(e,n,t){ZR.call(this,e,n,t,null)}function XOe(e,n,t){ZR.call(this,e,n,t,null)}function xle(e,n){gY.call(this,e),this.a=n}function Ale(e,n){gY.call(this,e),this.a=n}function Li(e,n){yi.call(this,e),this.a=n}function Mle(e,n){coe.call(this,e),this.a=n}function FK(e,n){coe.call(this,e),this.a=n}function KOe(e,n){this.c=e,Nw.call(this,n)}function VOe(e,n){this.a=e,BSe.call(this,n)}function AC(e,n){this.a=e,BSe.call(this,n)}function Tle(e,n,t){return t=dl(e,n,3,t),t}function Cle(e,n,t){return t=dl(e,n,6,t),t}function Ole(e,n,t){return t=dl(e,n,9,t),t}function fh(e,n){return FC(n,ewe),e.f=n,e}function Nle(e,n){return(n&oi)%e.d.length}function YOe(e,n,t){return bge(e.c,e.b,n,t)}function Kpn(e,n,t){return e.apply(n,t)}function QOe(e,n,t){var i;i=e.dd(n),i.Rb(t)}function WOe(e,n,t){return e.a+=gh(n,0,t),e}function MC(e){return!e.a&&(e.a=new Cn),e.a}function Dle(e,n){var t;return t=e.e,e.e=n,t}function Ile(e,n){var t;return t=n,!!e.De(t)}function Lb(e,n){return Pn(),e==n?0:e?1:-1}function d2(e,n){e.a._c(e.b,n),++e.b,e.c=-1}function Vpn(e,n){var t;t=e[zZ],t.call(e,n)}function Ypn(e,n){var t;t=e[zZ],t.call(e,n)}function Qpn(e,n,t){Ib(),jP(e,n.Te(e.a,t))}function _le(e,n,t){return M4(e,u(n,23),t)}function _f(e,n){return HP(new Array(n),e)}function Wpn(e){return Rt(Bb(e,32))^Rt(e)}function JK(e){return String.fromCharCode(e)}function Zpn(e){return e==null?null:e.message}function HK(e){this.a=(jn(),new In(Nt(e)))}function ZOe(e){this.a=(ll(e,Q2),new xo(e))}function eNe(e){this.a=(ll(e,Q2),new xo(e))}function nNe(){this.a=new Ce,this.b=new Ce}function tNe(){this.a=new Zm,this.b=new nxe}function Lle(){this.b=new O0,this.a=new O0}function iNe(){this.b=new Kr,this.c=new Ce}function Ple(){this.n=new Kr,this.o=new Kr}function q$(){this.n=new t4,this.i=new g4}function rNe(){this.b=new ar,this.a=new ar}function cNe(){this.a=new Ce,this.d=new Ce}function uNe(){this.a=new NU,this.b=new c_}function oNe(){this.b=new _Ae,this.a=new vM}function sNe(){this.b=new pt,this.a=new pt}function lNe(){q$.call(this),this.a=new Kr}function $le(e,n,t,i){fR.call(this,e,n,t,i)}function e2n(e,n){return e.n.a=(_n(n),n+10)}function n2n(e,n){return e.n.a=(_n(n),n+10)}function t2n(e,n){return iC(),!V9(n.d.i,e)}function fNe(e){Hu(e.e),e.d.b=e.d,e.d.a=e.d}function TC(e){e.b?TC(e.b):e.f.c.yc(e.e,e.d)}function i2n(e,n){x1(e.f)?wOn(e,n):sMn(e,n)}function aNe(e,n,t){t!=null&&kB(n,XQ(e,t))}function hNe(e,n,t){t!=null&&jB(n,XQ(e,t))}function y4(e,n,t,i){pe.call(this,e,n,t,i)}function Rle(e,n,t,i){pe.call(this,e,n,t,i)}function dNe(e,n,t,i){Rle.call(this,e,n,t,i)}function bNe(e,n,t,i){mR.call(this,e,n,t,i)}function GK(e,n,t,i){mR.call(this,e,n,t,i)}function gNe(e,n,t,i){GK.call(this,e,n,t,i)}function Ble(e,n,t,i){mR.call(this,e,n,t,i)}function Nn(e,n,t,i){Ble.call(this,e,n,t,i)}function zle(e,n,t,i){GK.call(this,e,n,t,i)}function wNe(e,n,t,i){zle.call(this,e,n,t,i)}function pNe(e,n,t,i){Lfe.call(this,e,n,t,i)}function b2(e,n){jo.call(this,$S+e+dg+n)}function r2n(e,n){return n==e||m8(Nz(n),e)}function Fle(e,n){return e.hk().ti().oi(e,n)}function Jle(e,n){return e.hk().ti().qi(e,n)}function c2n(e,n){return e.e=u(e.d.Kb(n),162)}function mNe(e,n){return ei(e.a,n,"")==null}function vNe(e,n){return _n(e),ue(e)===ue(n)}function bn(e,n){return _n(e),ue(e)===ue(n)}function Hle(e,n,t){return e.lastIndexOf(n,t)}function yNe(e,n,t){this.a=e,dle.call(this,n,t)}function kNe(e){this.c=e,N$.call(this,hN,0)}function jNe(e,n,t){this.c=n,this.b=t,this.a=e}function gi(e,n){return e.a+=n.a,e.b+=n.b,e}function Nr(e,n){return e.a-=n.a,e.b-=n.b,e}function u2n(e){return Qp(e.j.c,0),e.a=-1,e}function o2n(e,n){var t;return t=n.ni(e.a),t}function Gle(e,n,t){return t=dl(e,n,11,t),t}function s2n(e,n,t){return ki(e[n.a],e[t.a])}function l2n(e,n){return oo(e.a.d.p,n.a.d.p)}function f2n(e,n){return oo(n.a.d.p,e.a.d.p)}function a2n(e,n){return ki(e.c-e.s,n.c-n.s)}function h2n(e,n){return ki(e.b.e.a,n.b.e.a)}function d2n(e,n){return ki(e.c.e.a,n.c.e.a)}function b2n(e,n){return he(n,(Ne(),fD),e)}function g2n(e,n){return e.b.zd(new zMe(e,n))}function w2n(e,n){return e.b.zd(new FMe(e,n))}function ENe(e,n){return e.b.zd(new JMe(e,n))}function SNe(e,n){return X(n,16)&&pXe(e.c,n)}function xNe(e){return e.c?wu(e.c.a,e,0):-1}function p2n(e){return e<100?null:new m0(e)}function k4(e){return e==Cg||e==f1||e==to}function m2n(e,n,t){return u(e.c,72).Uk(n,t)}function U$(e,n,t){return u(e.c,72).Vk(n,t)}function v2n(e,n,t){return Npn(e,u(n,344),t)}function qle(e,n,t){return Dpn(e,u(n,344),t)}function y2n(e,n,t){return rGe(e,u(n,344),t)}function ANe(e,n,t){return yMn(e,u(n,344),t)}function eE(e,n){return n==null?null:L2(e.b,n)}function k2n(e,n){Ka||n&&(e.d=n)}function Ule(e,n){if(!e)throw $(new Gn(n))}function x9(e){if(!e)throw $(new Uc(Pge))}function qK(e){return s2(e)?(_n(e),e):e.se()}function X$(e){return!isNaN(e)&&!isFinite(e)}function UK(e){BCe(this),qs(this),fc(this,e)}function bs(e){MK(this),sfe(this.c,0,e.Nc())}function CC(e){A9(),this.d=e,this.a=new P3}function MNe(e,n,t){this.d=e,this.b=t,this.a=n}function _l(e,n,t){this.a=e,this.b=n,this.c=t}function TNe(e,n,t){this.a=e,this.b=n,this.c=t}function Xle(e,n){this.c=e,vV.call(this,e,n)}function CNe(e,n){T3n.call(this,e,e.length,n)}function XK(e,n){if(e!=n)throw $(new Nl)}function ONe(e){this.a=e,yd(),Pu(Date.now())}function NNe(e){As(e.a),uhe(e.c,e.b),e.b=null}function KK(){KK=Y,Hme=new hi,hnn=new Gt}function VK(e){var n;return n=new l5,n.e=e,n}function j2n(e,n,t){return Ib(),e.a.Wd(n,t),n}function Kle(e,n,t){this.b=e,this.c=n,this.a=t}function Vle(e){var n;return n=new oxe,n.b=e,n}function E2n(e){return ga(),Dt((M$e(),Cnn),e)}function S2n(e){return H9(),Dt((F$e(),gnn),e)}function x2n(e){return zl(),Dt((A$e(),knn),e)}function A2n(e){return ws(),Dt((T$e(),Nnn),e)}function M2n(e){return Uo(),Dt((C$e(),Inn),e)}function T2n(e){return Zz(),Dt((aCe(),ttn),e)}function C2n(e){return Lw(),Dt((U$e(),rtn),e)}function O2n(e){return Z9(),Dt((X$e(),Ktn),e)}function N2n(e){return lB(),Dt((IPe(),dtn),e)}function D2n(e){return yE(),Dt((x$e(),Btn),e)}function I2n(e){return zr(),Dt((LRe(),Htn),e)}function _2n(e){return X4(),Dt((q$e(),ein),e)}function L2n(e){return zn(),Dt((ize(),rin),e)}function P2n(e){return K9(),Dt((_Pe(),lin),e)}function YK(e){fR.call(this,e.d,e.c,e.a,e.b)}function Yle(e){fR.call(this,e.d,e.c,e.a,e.b)}function $2n(e){return Ur(),Dt((hCe(),fin),e)}function DNe(){DNe=Y,Nan=oe(Ar,On,1,0,5,1)}function INe(){INe=Y,Van=oe(Ar,On,1,0,5,1)}function Qle(){Qle=Y,Yan=oe(Ar,On,1,0,5,1)}function OC(){OC=Y,jJ=new lq,EJ=new $A}function K$(){K$=Y,gin=new Tq,bin=new Cq}function rl(){rl=Y,yin=new gk,kin=new od}function R2n(e){return _w(),Dt((l$e(),Nin),e)}function B2n(e){return Ff(),Dt((Q$e(),Sin),e)}function z2n(e){return z2(),Dt((CRe(),Ain),e)}function F2n(e){return Bz(),Dt((uze(),Din),e)}function J2n(e){return Q4(),Dt((lBe(),Iin),e)}function H2n(e){return nB(),Dt((wPe(),_in),e)}function G2n(e){return BE(),Dt((Z$e(),Lin),e)}function q2n(e){return pB(),Dt((r$e(),Pin),e)}function U2n(e){return KO(),Dt((hze(),$in),e)}function X2n(e){return fO(),Dt((pPe(),Rin),e)}function K2n(e){return Wb(),Dt((c$e(),zin),e)}function V2n(e){return Sz(),Dt((sBe(),Fin),e)}function Y2n(e){return rO(),Dt((mPe(),Jin),e)}function Q2n(e){return zO(),Dt((uBe(),Hin),e)}function W2n(e){return y8(),Dt((oBe(),Gin),e)}function Z2n(e){return Dc(),Dt((Cze(),qin),e)}function emn(e){return W9(),Dt((u$e(),Uin),e)}function nmn(e){return _0(),Dt((o$e(),Xin),e)}function tmn(e){return _1(),Dt((s$e(),Vin),e)}function imn(e){return JR(),Dt((vPe(),Yin),e)}function rmn(e){return Xs(),Dt((NRe(),Win),e)}function cmn(e){return qR(),Dt((yPe(),Zin),e)}function umn(e){return YO(),Dt((dze(),zun),e)}function omn(e){return IE(),Dt((f$e(),Fun),e)}function smn(e){return B2(),Dt((V$e(),Jun),e)}function lmn(e){return HE(),Dt((ORe(),Hun),e)}function fmn(e){return G0(),Dt((Tze(),Gun),e)}function amn(e){return F1(),Dt((Y$e(),qun),e)}function hmn(e){return uO(),Dt((kPe(),Uun),e)}function dmn(e){return Nc(),Dt((a$e(),Kun),e)}function bmn(e){return DB(),Dt((h$e(),Vun),e)}function gmn(e){return DE(),Dt((d$e(),Yun),e)}function wmn(e){return r8(),Dt((b$e(),Qun),e)}function pmn(e){return wB(),Dt((g$e(),Wun),e)}function mmn(e){return IB(),Dt((w$e(),Zun),e)}function vmn(e){return LB(),Dt((G$e(),din),e)}function ymn(e){return eg(),Dt((K$e(),mon),e)}function kmn(e,n){return _n(e),e+(_n(n),n)}function jmn(e){return mE(),Dt((jPe(),Eon),e)}function Emn(e){return ah(),Dt((SPe(),Oon),e)}function Smn(e){return Da(),Dt((EPe(),Don),e)}function xmn(e){return ha(),Dt((xPe(),Xon),e)}function A9(){A9=Y,nye=(De(),Kn),OH=et}function Amn(e){return Cw(),Dt((APe(),esn),e)}function Mmn(e){return Y4(),Dt((tRe(),nsn),e)}function Tmn(e){return cS(),Dt((dCe(),tsn),e)}function Cmn(e){return NE(),Dt((p$e(),isn),e)}function Omn(e){return OE(),Dt((W$e(),Asn),e)}function Nmn(e){return FR(),Dt((MPe(),Msn),e)}function Dmn(e){return SB(),Dt((TPe(),Dsn),e)}function Imn(e){return vz(),Dt((DRe(),_sn),e)}function _mn(e){return iB(),Dt((CPe(),Lsn),e)}function Lmn(e){return EO(),Dt((m$e(),Psn),e)}function Pmn(e){return az(),Dt((nRe(),tln),e)}function $mn(e){return NB(),Dt((v$e(),iln),e)}function Rmn(e){return WB(),Dt((y$e(),rln),e)}function Bmn(e){return kz(),Dt((eRe(),uln),e)}function zmn(e){return XB(),Dt((S$e(),lln),e)}function Fmn(e){return!e.e&&(e.e=new Ce),e.e}function V$(e,n,t){this.e=n,this.b=e,this.d=t}function _Ne(e,n,t){this.a=e,this.b=n,this.c=t}function LNe(e,n,t){this.a=e,this.b=n,this.c=t}function Wle(e,n,t){this.a=e,this.b=n,this.c=t}function PNe(e,n,t){this.a=e,this.b=n,this.c=t}function $Ne(e,n,t){this.a=e,this.c=n,this.b=t}function Y$(e,n,t){this.b=e,this.a=n,this.c=t}function RNe(e,n,t){this.b=e,this.a=n,this.c=t}function QK(e,n){this.c=e,this.a=n,this.b=n-e}function Jmn(e){return JB(),Dt((j$e(),Iln),e)}function Hmn(e){return e$(),Dt((XLe(),Bln),e)}function Gmn(e){return WC(),Dt((NPe(),zln),e)}function qmn(e){return JO(),Dt((_Re(),Fln),e)}function Umn(e){return ZP(),Dt((ULe(),$ln),e)}function Xmn(e){return nS(),Dt((IRe(),Lln),e)}function Kmn(e){return TO(),Dt((E$e(),Pln),e)}function Vmn(e){return VR(),Dt((OPe(),Nln),e)}function Ymn(e){return rB(),Dt((k$e(),Dln),e)}function Qmn(e){return Tj(),Dt((KLe(),ifn),e)}function Wmn(e){return pO(),Dt((DPe(),rfn),e)}function Zmn(e){return ph(),Dt(($Re(),ffn),e)}function e3n(e){return cg(),Dt((rze(),hfn),e)}function n3n(e){return z1(),Dt((rRe(),Kfn),e)}function t3n(e){return vr(),Dt((PRe(),qfn),e)}function i3n(e){return u8(),Dt((iRe(),Ufn),e)}function r3n(e){return $a(),Dt((O$e(),Xfn),e)}function c3n(e){return Vh(),Dt((tBe(),dfn),e)}function u3n(e){return rg(),Dt((iBe(),vfn),e)}function o3n(e){return G2(),Dt((gze(),ean),e)}function s3n(e){return nv(),Dt((RRe(),nan),e)}function l3n(e){return Br(),Dt((cBe(),tan),e)}function f3n(e){return ps(),Dt((rBe(),ian),e)}function a3n(e){return al(),Dt((cRe(),Zfn),e)}function h3n(e){return jz(),Dt((nBe(),Vfn),e)}function d3n(e){return B1(),Dt((D$e(),Qfn),e)}function b3n(e){return UR(),Dt((uRe(),dan),e)}function g3n(e){return _s(),Dt((bze(),aan),e)}function w3n(e){return G4(),Dt((N$e(),han),e)}function p3n(e){return De(),Dt((BRe(),ran),e)}function m3n(e){return jE(),Dt((I$e(),lan),e)}function v3n(e){return Vs(),Dt((oRe(),fan),e)}function y3n(e){return KB(),Dt((sRe(),ban),e)}function k3n(e){return PB(),Dt((lRe(),pan),e)}function j3n(e){return j8(),Dt((cze(),Oan),e)}function BNe(e,n,t){Il(),bae.call(this,e,n,t)}function WK(e,n,t){Il(),Vfe.call(this,e,n,t)}function zNe(e,n,t){Il(),WK.call(this,e,n,t)}function Zle(e,n,t){Il(),WK.call(this,e,n,t)}function FNe(e,n,t){Il(),Zle.call(this,e,n,t)}function JNe(e,n,t){Il(),efe.call(this,e,n,t)}function efe(e,n,t){Il(),Vfe.call(this,e,n,t)}function nfe(e,n,t){Il(),Vfe.call(this,e,n,t)}function HNe(e,n,t){Il(),nfe.call(this,e,n,t)}function GNe(e,n,t){this.a=e,this.c=n,this.b=t}function qNe(e,n,t){this.a=e,this.b=n,this.c=t}function tfe(e,n,t){this.a=e,this.b=n,this.c=t}function ife(e,n,t){this.a=e,this.b=n,this.c=t}function ZK(e,n,t){this.a=e,this.b=n,this.c=t}function UNe(e,n,t){this.a=e,this.b=n,this.c=t}function Ed(e,n,t){this.e=e,this.a=n,this.c=t}function rfe(e){this.d=e,en(this),this.b=gvn(e.d)}function cfe(e,n){bgn.call(this,e,qB(new Su(n)))}function NC(e,n){return Nt(e),Nt(n),new QAe(e,n)}function j4(e,n){return Nt(e),Nt(n),new iDe(e,n)}function E3n(e,n){return Nt(e),Nt(n),new rDe(e,n)}function S3n(e,n){return Nt(e),Nt(n),new sMe(e,n)}function eV(e){return at(e.b!=0),$l(e,e.a.a)}function x3n(e){return at(e.b!=0),$l(e,e.c.b)}function A3n(e){return!e.c&&(e.c=new Ol),e.c}function DC(e){var n;return n=new Si,RY(n,e),n}function XNe(e){var n;return n=new wX,RY(n,e),n}function M3n(e){var n;return n=new ar,AY(n,e),n}function M9(e){var n;return n=new Ce,AY(n,e),n}function u(e,n){return nE(e==null||PQ(e,n)),e}function T3n(e,n,t){UDe.call(this,n,t),this.a=e}function KNe(e,n){this.c=e,this.b=n,this.a=!1}function VNe(){this.a=";,;",this.b="",this.c=""}function YNe(e,n,t){this.b=e,rCe.call(this,n,t)}function ufe(e,n,t){this.c=e,r$.call(this,n,t)}function ofe(e,n,t){v9.call(this,e,n),this.b=t}function sfe(e,n,t){ebe(t,0,e,n,t.length,!1)}function Jh(e,n,t,i,r){e.b=n,e.c=t,e.d=i,e.a=r}function lfe(e,n,t,i,r){e.d=n,e.c=t,e.a=i,e.b=r}function C3n(e,n){n&&(e.b=n,e.a=(A0(n),n.a))}function IC(e,n){if(!e)throw $(new Gn(n))}function E4(e,n){if(!e)throw $(new Uc(n))}function ffe(e,n){if(!e)throw $(new tAe(n))}function O3n(e,n){return QP(),oo(e.d.p,n.d.p)}function N3n(e,n){return P1(),ki(e.e.b,n.e.b)}function D3n(e,n){return P1(),ki(e.e.a,n.e.a)}function I3n(e,n){return oo(lDe(e.d),lDe(n.d))}function Q$(e,n){return n&&ER(e,n.d)?n:null}function _3n(e,n){return n==(De(),Kn)?e.c:e.d}function L3n(e){return new je(e.c+e.b,e.d+e.a)}function QNe(e){return e!=null&&!kQ(e,uA,oA)}function P3n(e,n){return(IFe(e)<<4|IFe(n))&yr}function WNe(e,n,t,i,r){e.c=n,e.d=t,e.b=i,e.a=r}function afe(e){var n,t;n=e.b,t=e.c,e.b=t,e.c=n}function hfe(e){var n,t;t=e.d,n=e.a,e.d=n,e.a=t}function $3n(e,n){var t;return t=e.c,Jhe(e,n),t}function dfe(e,n){return n<0?e.g=-1:e.g=n,e}function W$(e,n){return T8n(e),e.a*=n,e.b*=n,e}function _C(e,n,t){Dse.call(this,e,n),this.c=t}function Z$(e,n,t){Dse.call(this,e,n),this.c=t}function bfe(e){Qle(),p3.call(this),this._h(e)}function ZNe(){z9(),Vvn.call(this,(y0(),kf))}function eDe(e){return fi(),new Hh(0,e)}function nDe(){nDe=Y,Hce=(jn(),new In(Rne))}function eR(){eR=Y,new Mde((EX(),Yne),(jX(),Vne))}function tDe(){this.b=ne(re(_e((Gf(),kte))))}function nV(e){this.b=e,this.a=$b(this.b.a).Md()}function iDe(e,n){this.b=e,this.a=n,wT.call(this)}function rDe(e,n){this.a=e,this.b=n,wT.call(this)}function cDe(e,n,t){this.a=e,N3.call(this,n,t)}function uDe(e,n,t){this.a=e,N3.call(this,n,t)}function T9(e,n,t){var i;i=new y2(t),Rf(e,n,i)}function gfe(e,n,t){var i;return i=e[n],e[n]=t,i}function nR(e){var n;return n=e.slice(),vY(n,e)}function tR(e){var n;return n=e.n,e.a.b+n.d+n.a}function oDe(e){var n;return n=e.n,e.e.b+n.d+n.a}function wfe(e){var n;return n=e.n,e.e.a+n.b+n.c}function pfe(e){e.a.b=e.b,e.b.a=e.a,e.a=e.b=null}function Vt(e,n){return Xi(e,n,e.c.b,e.c),!0}function R3n(e){return e.a?e.a:NV(e)}function nE(e){if(!e)throw $(new l9(null))}function yw(e,n){return XE(e,new v9(n.a,n.b))}function B3n(e){return!cc(e)&&e.c.i.c==e.d.i.c}function z3n(e,n){return e.c=n)throw $(new bxe)}function Hu(e){e.f=new yCe(e),e.i=new kCe(e),++e.g}function wR(e){this.b=new xo(11),this.a=(Aw(),e)}function bV(e){this.b=null,this.a=(Aw(),e||Fme)}function Ife(e,n){this.e=e,this.d=(n&64)!=0?n|yh:n}function UDe(e,n){this.c=0,this.d=e,this.b=n|64|yh}function XDe(e){this.a=XJe(e.a),this.b=new bs(e.b)}function Sd(e,n,t,i){var r;r=e.i,r.i=n,r.a=t,r.b=i}function _fe(e){var n;for(n=e;n.f;)n=n.f;return n}function jvn(e){return e.e?ihe(e.e):null}function cE(e){return ps(),!e.Gc(Z1)&&!e.Gc(gb)}function KDe(e,n,t){return x8(),GY(e,n)&&GY(e,t)}function VDe(e,n,t){return iYe(e,u(n,12),u(t,12))}function gV(e,n){return n.Sh()?$0(e.b,u(n,52)):n}function pR(e){return new je(e.c+e.b/2,e.d+e.a/2)}function Evn(e,n,t){n.of(t,ne(re(Bn(e.b,t)))*e.a)}function Svn(e,n){n.Tg("General 'Rotator",1),B$n(e)}function Ir(e,n,t,i,r){pY.call(this,e,n,t,i,r,-1)}function uE(e,n,t,i,r){tO.call(this,e,n,t,i,r,-1)}function pe(e,n,t,i){mr.call(this,e,n,t),this.b=i}function mR(e,n,t,i){_C.call(this,e,n,t),this.b=i}function YDe(e){XTe.call(this,e,!1),this.a=!1}function QDe(){yK.call(this,"LOOKAHEAD_LAYOUT",1)}function WDe(){yK.call(this,"LAYOUT_NEXT_LEVEL",3)}function ZDe(e){this.b=e,p4.call(this,e),cOe(this)}function eIe(e){this.b=e,kC.call(this,e),uOe(this)}function nIe(e,n){this.b=e,HU.call(this,e.b),this.a=n}function m2(e,n,t){this.a=e,y4.call(this,n,t,5,6)}function Lfe(e,n,t,i){this.b=e,mr.call(this,n,t,i)}function zb(e,n,t){mh(),this.e=e,this.d=n,this.a=t}function Zr(e,n){for(_n(n);e.Ob();)n.Ad(e.Pb())}function vR(e,n){return fi(),new Yfe(e,n,0)}function wV(e,n){return fi(),new Yfe(6,e,n)}function xvn(e,n){return bn(e.substr(0,n.length),n)}function so(e,n){return $r(n)?RV(e,n):!!Xc(e.f,n)}function Avn(e){return _o(~e.l&Ls,~e.m&Ls,~e.h&G1)}function pV(e){return typeof e===sN||typeof e===aZ}function qh(e){return new qn(new sle(e.a.length,e.a))}function mV(e){return new wn(null,_vn(e,e.length))}function tIe(e){if(!e)throw $(new au);return e.d}function A4(e){var n;return n=CE(e),at(n!=null),n}function Mvn(e){var n;return n=bjn(e),at(n!=null),n}function O9(e,n){var t;return t=e.a.gc(),Zae(n,t),t-n}function hr(e,n){var t;return t=e.a.yc(n,e),t==null}function PC(e,n){return e.a.yc(n,(Pn(),eb))==null}function Tvn(e,n){return e>0?k.Math.log(e/n):-100}function Pfe(e,n){return n?fc(e,n):!1}function M4(e,n,t){return zf(e.a,n),gfe(e.b,n.g,t)}function Cvn(e,n,t){C9(t,e.a.c.length),ol(e.a,t,n)}function ce(e,n,t,i){nFe(n,t,e.length),Ovn(e,n,t,i)}function Ovn(e,n,t,i){var r;for(r=n;r0?1:0}function sE(e){return e.e==0?e:new zb(-e.e,e.d,e.a)}function Dvn(e){return e==Ki?HN:e==Dr?"-INF":""+e}function Ivn(e){return e==Ki?HN:e==Dr?"-INF":""+e}function _vn(e,n){return S8n(n,e.length),new dDe(e,n)}function rIe(e,n,t,i,r){for(;n=e.g}function MV(e,n,t){var i;return i=$Y(e,n,t),zbe(e,i)}function wIe(e,n){var t;t=console[e],t.call(console,n)}function T4(e,n){var t;t=e.a.length,T2(e,t),nY(e,t,n)}function pIe(e,n){var t;++e.j,t=e.Cj(),e.pj(e.Xi(t,n))}function TV(e,n){for(_n(n);e.c=e?new Yoe:K8n(e-1)}function uf(e){if(e==null)throw $(new e4);return e}function _n(e){if(e==null)throw $(new e4);return e}function Zvn(e){return!e.a&&(e.a=new mr(wb,e,4)),e.a}function Sw(e){return!e.d&&(e.d=new mr(Rc,e,1)),e.d}function e5n(e){if(e.p!=3)throw $(new is);return e.e}function n5n(e){if(e.p!=4)throw $(new is);return e.e}function t5n(e){if(e.p!=6)throw $(new is);return e.f}function i5n(e){if(e.p!=3)throw $(new is);return e.j}function r5n(e){if(e.p!=4)throw $(new is);return e.j}function c5n(e){if(e.p!=6)throw $(new is);return e.k}function or(){$xe.call(this),Qp(this.j.c,0),this.a=-1}function MIe(){Ot.call(this,"DELAUNAY_TRIANGULATION",0)}function u5n(){return BP(),z(B(Gen,1),ke,537,0,[Wne])}function o5n(e,n,t){return J4(),t.Kg(e,u(n.jd(),147))}function s5n(e,n){Et((!e.a&&(e.a=new AC(e,e)),e.a),n)}function Wfe(e,n){e.c<0||e.b.b=0?e.hi(t):q0e(e,n)}function I9(e,n){var t;return t=AV("",e),t.n=n,t.i=1,t}function xw(e){return e.c==-2&&oX(e,EMn(e.g,e.b)),e.c}function Zfe(e){return!e.b&&(e.b=new OP(new kX)),e.b}function TIe(e,n){return eR(),new Mde(new sOe(e),new oOe(n))}function f5n(e){return ll(e,gZ),fB(mc(mc(5,e),e/10|0))}function CV(){CV=Y,Xen=new rse(z(B(wg,1),eF,45,0,[]))}function CIe(){j0e.call(this,gg,(yAe(),rhn)),TPn(this)}function OIe(){j0e.call(this,hf,(d9(),Y8e)),PLn(this)}function NIe(e,n){Bwn.call(this,V8n(Nt(e),Nt(n))),this.a=n}function eae(e,n,t,i){bw.call(this,e,n),this.d=t,this.a=i}function SR(e,n,t,i){bw.call(this,e,t),this.a=n,this.f=i}function DIe(e,n){this.b=e,vV.call(this,e,n),cOe(this)}function IIe(e,n){this.b=e,Xle.call(this,e,n),uOe(this)}function hE(e){this.d=e,this.a=this.d.b,this.b=this.d.c}function _Ie(e){e.b=!1,e.c=!1,e.d=!1,e.a=!1}function _9(e){return!e.a&&(e.a=new uAe(e.c.vc())),e.a}function LIe(e){return!e.b&&(e.b=new f9(e.c.ec())),e.b}function PIe(e){return!e.d&&(e.d=new Hr(e.c.Bc())),e.d}function Uh(e,n){for(;n-- >0;)e=e<<1|(e<0?1:0);return e}function $Ie(e,n){var t;return t=new Xu(e),Hn(n.c,t),t}function a5n(e,n){aV(u(n.b,68),e),Ao(n.a,new Wue(e))}function RIe(e,n){e.u.Gc((ps(),Z1))&&vCn(e,n),y9n(e,n)}function Ku(e,n){return ue(e)===ue(n)||e!=null&&di(e,n)}function ei(e,n,t){return $r(n)?Kc(e,n,t):Ko(e.f,n,t)}function nae(e){return jn(),e?e.Me():(Aw(),Aw(),Jme)}function h5n(){return ZP(),z(B(P6e,1),ke,477,0,[Wre])}function d5n(){return e$(),z(B(Rln,1),ke,546,0,[Zre])}function b5n(){return Tj(),z(B(i9e,1),ke,527,0,[TD])}function zc(e,n){return oV(e.a,n)?e.b[u(n,23).g]:null}function g5n(e){return String.fromCharCode.apply(null,e)}function ic(e,n){return Yn(n,e.length),e.charCodeAt(n)}function RC(e){return e.j.c.length=0,rae(e.c),u2n(e.a),e}function L9(e){return e.e==s7&&a(e,$En(e.g,e.b)),e.e}function BC(e){return e.f==s7&&w(e,Axn(e.g,e.b)),e.f}function w5n(e){return!e.b&&(e.b=new Nn(vt,e,4,7)),e.b}function tae(e){return!e.c&&(e.c=new Nn(vt,e,5,8)),e.c}function iae(e){return!e.c&&(e.c=new pe($s,e,9,9)),e.c}function OV(e){return!e.n&&(e.n=new pe(ju,e,1,7)),e.n}function z3(e){var n;return n=e.b,!n&&(e.b=n=new rj(e)),n}function rae(e){var n;for(n=e.Jc();n.Ob();)n.Pb(),n.Qb()}function p5n(e,n,t){var i;i=u(e.d.Kb(t),162),i&&i.Nb(n)}function m5n(e,n){return new a_e(u(Nt(e),51),u(Nt(n),51))}function si(e,n){return R0(e),new wn(e,new whe(n,e.a))}function So(e,n){return R0(e),new wn(e,new the(n,e.a))}function k2(e,n){return R0(e),new xle(e,new WPe(n,e.a))}function xR(e,n){return R0(e),new Ale(e,new ZPe(n,e.a))}function BIe(e,n){W1e(e,ne($1(n,"x")),ne($1(n,"y")))}function zIe(e,n){W1e(e,ne($1(n,"x")),ne($1(n,"y")))}function v5n(e,n){return Qoe(),ki((_n(e),e),(_n(n),n))}function y5n(e,n){return ki(e.d.c+e.d.b/2,n.d.c+n.d.b/2)}function k5n(e,n){return ki(e.g.c+e.g.b/2,n.g.c+n.g.b/2)}function j5n(e){return e!=null&&Sj(jG,e.toLowerCase())}function E5n(e){rl();var n;n=u(e.g,9),n.n.a=e.d.c+n.d.b}function NV(e){var n;return n=Q8n(e),n||null}function ri(e,n,t,i){return WBe(e,n,t,!1),FB(e,i),e}function S5n(e,n,t){OLn(e.a,t),V7n(t),tOn(e.b,t),QLn(n,t)}function C4(e,n,t,i){Ot.call(this,e,n),this.a=t,this.b=i}function AR(e,n,t,i){this.a=e,this.c=n,this.b=t,this.d=i}function cae(e,n,t,i){this.c=e,this.b=n,this.a=t,this.d=i}function FIe(e,n,t,i){this.c=e,this.b=n,this.d=t,this.a=i}function DV(e,n,t,i){this.a=e,this.e=n,this.d=t,this.c=i}function JIe(e,n,t,i){this.a=e,this.d=n,this.c=t,this.b=i}function Lf(e,n,t,i){this.c=e,this.d=n,this.b=t,this.a=i}function IV(e,n,t){this.a=Jge,this.d=e,this.b=n,this.c=t}function uae(e,n){this.b=e,this.c=n,this.a=new s4(this.b)}function HIe(e,n){this.d=(_n(e),e),this.a=16449,this.c=n}function GIe(e,n,t,i){Xze.call(this,e,t,i,!1),this.f=n}function _V(e,n,t){var i,r;return i=Cge(e),r=n.qi(t,i),r}function C1(e){var n,t;return t=(n=new hw,n),q9(t,e),t}function LV(e){var n,t;return t=(n=new hw,n),x0e(t,e),t}function qIe(e){return!e.b&&(e.b=new pe(pr,e,12,3)),e.b}function UIe(e){this.a=new Ce,this.e=oe($t,Se,54,e,0,2)}function PV(e){this.f=e,this.c=this.f.e,e.f>0&&JHe(this)}function XIe(e,n,t,i){this.a=e,this.c=n,this.d=t,this.b=i}function KIe(e,n,t,i){this.a=e,this.b=n,this.d=t,this.c=i}function VIe(e,n,t,i){this.a=e,this.b=n,this.c=t,this.d=i}function YIe(e,n,t,i){this.a=e,this.b=n,this.c=t,this.d=i}function Jb(e,n,t,i){this.e=e,this.a=n,this.c=t,this.d=i}function QIe(e,n,t,i){Il(),QPe.call(this,n,t,i),this.a=e}function WIe(e,n,t,i){Il(),QPe.call(this,n,t,i),this.a=e}function ZIe(e,n){this.a=e,zpn.call(this,e,u(e.d,16).dd(n))}function x5n(e,n){return ki(us(e)*Gs(e),us(n)*Gs(n))}function A5n(e,n){return ki(us(e)*Gs(e),us(n)*Gs(n))}function O4(e){var n;return n=e.f,n||(e.f=new g9(e,e.c))}function jn(){jn=Y,Sc=new Ae,i1=new sn,hJ=new Sn}function Aw(){Aw=Y,Fme=new xe,ote=new xe,Jme=new un}function P9(e){if(Ds(e.d),e.d.d!=e.c)throw $(new Nl)}function qs(e){e.a.a=e.c,e.c.b=e.a,e.a.b=e.c.a=null,e.b=0}function oae(e){return at(e.b0?$f(e):new Ce}function MR(e){return e.n&&(e.e!==pYe&&e.he(),e.j=null),e}function sae(e,n){return e.b=n.b,e.c=n.c,e.d=n.d,e.a=n.a,e}function T5n(e,n,t){return Te(e.a,(GQ(n,t),new bw(n,t))),e}function C5n(e,n){return u(T(e,(me(),Ty)),16).Ec(n),n}function O5n(e,n){return gn(e,u(T(n,(Ne(),mm)),15),n)}function N5n(e){return Hw(e)&&Re($e(ye(e,(Ne(),kg))))}function D5n(e,n,t){return Mj(),_jn(u(Bn(e.e,n),516),t)}function I5n(e,n,t){e.i=0,e.e=0,n!=t&&Hze(e,n,t)}function _5n(e,n,t){e.i=0,e.e=0,n!=t&&Gze(e,n,t)}function e_e(e,n,t,i){this.b=e,this.c=i,N$.call(this,n,t)}function n_e(e,n){this.g=e,this.d=z(B(c1,1),Bd,9,0,[n])}function t_e(e,n){e.d&&!e.d.a&&(USe(e.d,n),t_e(e.d,n))}function i_e(e,n){e.e&&!e.e.a&&(USe(e.e,n),i_e(e.e,n))}function r_e(e,n){return ev(e.j,n.s,n.c)+ev(n.e,e.s,e.c)}function L5n(e,n){return-ki(us(e)*Gs(e),us(n)*Gs(n))}function P5n(e){return u(e.jd(),147).Og()+":"+su(e.kd())}function c_e(){dW(this,new OT),this.wb=(x0(),Rn),d9()}function u_e(e){this.b=new u_,this.a=e,k.Math.random()}function o_e(e){this.b=new Ce,Er(this.b,this.b),this.a=e}function lae(e,n){new Si,this.a=new xs,this.b=e,this.c=n}function s_e(){hu.call(this,"There is no more element.")}function $5n(e){JP(),k.setTimeout(function(){throw e},0)}function R5n(e){e.Tg("No crossing minimization",1),e.Ug()}function B5n(e,n){return Us(e),Us(n),Wxe(u(e,23),u(n,23))}function Hb(e,n,t){var i,r;i=qK(t),r=new k3(i),Rf(e,n,r)}function $V(e,n,t,i,r,c){tO.call(this,e,n,t,i,r,c?-2:-1)}function l_e(e,n,t,i){Dse.call(this,n,t),this.b=e,this.a=i}function fae(e){this.b=e,this.c=e,e.e=null,e.c=null,this.a=1}function TR(e){return!e.a&&(e.a=new pe(Jt,e,10,11)),e.a}function vi(e){return!e.q&&(e.q=new pe(yf,e,11,10)),e.q}function we(e){return!e.s&&(e.s=new pe(ns,e,21,17)),e.s}function f_e(e){return nE(e==null||pV(e)&&e.Rm!==Qn),e}function CR(e,n){if(e==null)throw $(new c4(n));return e}function a_e(e,n){ybn.call(this,new bV(e)),this.a=e,this.b=n}function RV(e,n){return n==null?!!Xc(e.f,null):svn(e.i,n)}function BV(e){return X(e,18)?new w2(u(e,18)):M3n(e.Jc())}function OR(e){return jn(),X(e,59)?new DX(e):new B$(e)}function z5n(e){return Nt(e),rHe(new qn(Vn(e.a.Jc(),new ee)))}function F5n(e){return new nOe(e,e.e.Pd().gc()*e.c.Pd().gc())}function J5n(e){return new tOe(e,e.e.Pd().gc()*e.c.Pd().gc())}function aae(e){return e&&e.hashCode?e.hashCode():vw(e)}function H5n(e){e&&DR(e,e.ge())}function G5n(e,n){var t;return t=Qse(e.a,n),t&&(n.d=null),t}function h_e(e,n,t){return e.f?e.f.cf(n,t):!1}function zC(e,n,t,i){tr(e.c[n.g],t.g,i),tr(e.c[t.g],n.g,i)}function zV(e,n,t,i){tr(e.c[n.g],n.g,t),tr(e.b[n.g],n.g,i)}function q5n(e,n,t){return ne(re(t.a))<=e&&ne(re(t.b))>=n}function d_e(){this.d=new Si,this.b=new pt,this.c=new Ce}function b_e(){this.b=new ar,this.d=new Si,this.e=new $P}function hae(){this.c=new Kr,this.d=new Kr,this.e=new Kr}function Mw(){this.a=new xs,this.b=(ll(3,Q2),new xo(3))}function g_e(e){this.c=e,this.b=new vd(u(Nt(new Sf),51))}function w_e(e){this.c=e,this.b=new vd(u(Nt(new nk),51))}function p_e(e){this.b=e,this.a=new vd(u(Nt(new qg),51))}function xd(e,n){this.e=e,this.a=Ar,this.b=IXe(n),this.c=n}function NR(e){this.c=e.c,this.d=e.d,this.b=e.b,this.a=e.a}function m_e(e,n,t,i,r,c){this.a=e,DY.call(this,n,t,i,r,c)}function v_e(e,n,t,i,r,c){this.a=e,DY.call(this,n,t,i,r,c)}function M0(e,n,t,i,r,c,o){return new rY(e.e,n,t,i,r,c,o)}function U5n(e,n,t){return t>=0&&bn(e.substr(t,n.length),n)}function y_e(e,n){return X(n,147)&&bn(e.b,u(n,147).Og())}function X5n(e,n){return e.a?n.Dh().Jc():u(n.Dh(),72).Gi()}function k_e(e,n){var t;return t=e.b.Oc(n),bPe(t,e.b.gc()),t}function FC(e,n){if(e==null)throw $(new c4(n));return e}function tu(e){return e.u||(Ms(e),e.u=new VOe(e,e)),e.u}function Go(e){var n;return n=u(Un(e,16),29),n||e.fi()}function DR(e,n){var t;return t=Db(e.Pm),n==null?t:t+": "+n}function of(e,n,t){return Yr(n,t,e.length),e.substr(n,t-n)}function j_e(e,n){q$.call(this),The(this),this.a=e,this.c=n}function E_e(){yK.call(this,"FIXED_INTEGER_RATIO_BOXES",2)}function K5n(){return nB(),z(B(gve,1),ke,422,0,[bve,Xte])}function V5n(){return fO(),z(B(Sve,1),ke,419,0,[XJ,Eve])}function Y5n(){return rO(),z(B(Mve,1),ke,476,0,[Ave,VJ])}function Q5n(){return JR(),z(B(Fve,1),ke,420,0,[bie,zve])}function W5n(){return qR(),z(B(n5e,1),ke,423,0,[Sie,Eie])}function Z5n(){return uO(),z(B(J4e,1),ke,421,0,[tre,ire])}function e4n(){return mE(),z(B(jon,1),ke,518,0,[Cx,Tx])}function n4n(){return Da(),z(B(Non,1),ke,508,0,[Ag,Ya])}function t4n(){return ah(),z(B(Con,1),ke,509,0,[pp,Ud])}function i4n(){return ha(),z(B(Uon,1),ke,515,0,[Am,sb])}function r4n(){return Cw(),z(B(Zon,1),ke,454,0,[lb,Jv])}function c4n(){return FR(),z(B($ye,1),ke,425,0,[xre,Pye])}function u4n(){return SB(),z(B(Rye,1),ke,487,0,[BH,qv])}function o4n(){return iB(),z(B(zye,1),ke,426,0,[Bye,Nre])}function s4n(){return lB(),z(B(n3e,1),ke,424,0,[vte,pJ])}function l4n(){return K9(),z(B(sin,1),ke,502,0,[QN,Dte])}function f4n(){return VR(),z(B(C6e,1),ke,478,0,[Kre,T6e])}function a4n(){return WC(),z(B($6e,1),ke,428,0,[ece,YH])}function h4n(){return pO(),z(B(c9e,1),ke,427,0,[WH,r9e])}function IR(e,n,t,i){return t>=0?e.Rh(n,t,i):e.zh(null,t,i)}function JC(e){return e.b.b==0?e.a.uf():eV(e.b)}function d4n(e){if(e.p!=5)throw $(new is);return Rt(e.f)}function b4n(e){if(e.p!=5)throw $(new is);return Rt(e.k)}function dae(e){return ue(e.a)===ue((FY(),zce))&&jPn(e),e.a}function S_e(e,n){fj(this,new je(e.a,e.b)),j3(this,DC(n))}function Tw(){kbn.call(this,new l4(I2(12))),tle(!0),this.a=2}function FV(e,n,t){fi(),aw.call(this,e),this.b=n,this.a=t}function bae(e,n,t){Il(),DP.call(this,n),this.a=e,this.b=t}function g4n(e,n){var t=nte[e.charCodeAt(0)];return t??e}function _R(e,n){return CR(e,"set1"),CR(n,"set2"),new dMe(e,n)}function LR(e,n){return hPe(n),P8n(e,oe($t,ni,30,n,15,1),n)}function w4n(e,n){e.b=n,e.c>0&&e.b>0&&(e.g=oR(e.c,e.b,e.a))}function p4n(e,n){e.c=n,e.c>0&&e.b>0&&(e.g=oR(e.c,e.b,e.a))}function x_e(e){var n;n=e.c.d.b,e.b=n,e.a=e.c.d,n.a=e.c.d.b=e}function A_e(e){return e.b==0?null:(at(e.b!=0),$l(e,e.a.a))}function lo(e,n){return n==null?du(Xc(e.f,null)):Dj(e.i,n)}function M_e(e,n,t,i,r){return new gW(e,(H9(),ate),n,t,i,r)}function JV(e,n,t,i){var r;r=new lNe,n.a[t.g]=r,M4(e.b,i,r)}function T_e(e,n){var t,i;return t=n,i=new st,bVe(e,t,i),i.d}function m4n(e,n){var t;return t=O8n(e.f,n),gi(G$(t),e.f.d)}function HC(e){var n;G8n(e.a),CCe(e.a),n=new TP(e.a),ode(n)}function v4n(e,n){jXe(e,!0),Ao(e.e.Pf(),new Kle(e,!0,n))}function y4n(e,n){return P1(),u(T(n,(Mu(),Nh)),15).a==e}function sc(e){return Math.max(Math.min(e,oi),-2147483648)|0}function C_e(e){q$.call(this),The(this),this.a=e,this.c=!0}function gae(e,n,t){this.a=new Ce,this.e=e,this.f=n,this.c=t}function PR(e,n,t){this.c=new Ce,this.e=e,this.f=n,this.b=t}function O_e(e,n,t){this.i=new Ce,this.b=e,this.g=n,this.a=t}function N_e(e){this.a=u(Nt(e),277),this.b=(jn(),new ole(e))}function $9(){$9=Y;var e,n;n=!pEn(),e=new ln,tte=n?new rn:e}function wae(){wae=Y,Snn=new o0,Ann=new xfe,xnn=new ud}function ah(){ah=Y,pp=new yse(fy,0),Ud=new yse(ly,1)}function Da(){Da=Y,Ag=new kse(XZ,0),Ya=new kse("UP",1)}function Cw(){Cw=Y,lb=new Ese(ly,0),Jv=new Ese(fy,1)}function F3(e,n,t){$R(),e&&ei($ce,e,n),e&&ei(WD,e,t)}function pae(e,n,t){var i;i=e.Fh(n),i>=0?e.$h(i,t):ybe(e,n,t)}function D_e(e,n){var t;for(Nt(n),t=e.a;t;t=t.c)n.Wd(t.g,t.i)}function GC(e,n){var t;t=e.q.getHours(),e.q.setDate(n),oS(e,t)}function I_e(e){var n;return n=new UP(I2(e.length)),m1e(n,e),n}function k4n(e){function n(){}return n.prototype=e||{},new n}function j4n(e,n){return mze(e,n)?(mBe(e),!0):!1}function O1(e,n){if(n==null)throw $(new e4);return jEn(e,n)}function E4n(e){if(e.ye())return null;var n=e.n;return uJ[n]}function j2(e){return e.Db>>16!=3?null:u(e.Cb,26)}function Ia(e){return e.Db>>16!=9?null:u(e.Cb,26)}function __e(e){return e.Db>>16!=6?null:u(e.Cb,85)}function L_e(e,n){var t;return t=e.Fh(n),t>=0?e.Th(t):kW(e,n)}function HV(e,n,t){var i;i=Rze(e,n,t),e.b=new AB(i.c.length)}function P_e(e){this.a=e,this.b=oe(von,Se,2005,e.e.length,0,2)}function $_e(){this.a=new zh,this.e=new ar,this.g=0,this.i=0}function R_e(e,n){L$(this),this.f=n,this.g=e,MR(this),this.he()}function B_e(e,n){return e.b+=n.b,e.c+=n.c,e.d+=n.d,e.a+=n.a,e}function mae(e){var n;return n=e.d,n=e._i(e.f),Et(e,n),n.Ob()}function z_e(e,n){var t;return t=new kfe(n),wGe(t,e),new bs(t)}function S4n(e){if(e.p!=0)throw $(new is);return Gj(e.f,0)}function x4n(e){if(e.p!=0)throw $(new is);return Gj(e.k,0)}function F_e(e){return e.Db>>16!=7?null:u(e.Cb,241)}function vae(e){return e.Db>>16!=7?null:u(e.Cb,174)}function J_e(e){return e.Db>>16!=3?null:u(e.Cb,158)}function R9(e){return e.Db>>16!=6?null:u(e.Cb,241)}function Bi(e){return e.Db>>16!=11?null:u(e.Cb,26)}function E2(e){return e.Db>>16!=17?null:u(e.Cb,29)}function dE(e,n,t,i,r,c){return new L1(e.e,n,e.Jj(),t,i,r,c)}function Kc(e,n,t){return n==null?Ko(e.f,null,t):Pw(e.i,n,t)}function GV(e,n){return k.Math.abs(e)0}function yae(e){var n;return R0(e),n=new ar,si(e,new Gke(n))}function H_e(e,n){var t=e.a=e.a||[];return t[n]||(t[n]=e.te(n))}function O4n(e,n){var t;t=e.q.getHours(),e.q.setMonth(n),oS(e,t)}function lc(e,n){e.c&&qo(e.c.g,e),e.c=n,e.c&&Te(e.c.g,e)}function Or(e,n){e.c&&qo(e.c.a,e),e.c=n,e.c&&Te(e.c.a,e)}function Gr(e,n){e.d&&qo(e.d.e,e),e.d=n,e.d&&Te(e.d.e,e)}function gu(e,n){e.i&&qo(e.i.j,e),e.i=n,e.i&&Te(e.i.j,e)}function G_e(e,n,t){this.a=n,this.c=e,this.b=(Nt(t),new bs(t))}function q_e(e,n,t){this.a=n,this.c=e,this.b=(Nt(t),new bs(t))}function U_e(e,n){this.a=e,this.c=pc(this.a),this.b=new NR(n)}function S2(e,n){if(e<0||e>n)throw $(new jo(Qge+e+Wge+n))}function X_e(){X_e=Y,con=Eo(new or,(zr(),Pc),(Ur(),Ey))}function kae(){kae=Y,uon=Eo(new or,(zr(),Pc),(Ur(),Ey))}function K_e(){K_e=Y,eon=Eo(new or,(zr(),Pc),(Ur(),Ey))}function V_e(){V_e=Y,non=Eo(new or,(zr(),Pc),(Ur(),Ey))}function Y_e(){Y_e=Y,ton=Eo(new or,(zr(),Pc),(Ur(),Ey))}function jae(){jae=Y,ion=Eo(new or,(zr(),Pc),(Ur(),Ey))}function Q_e(){Q_e=Y,Son=qt(new or,(zr(),Pc),(Ur(),nx))}function cl(){cl=Y,Mon=qt(new or,(zr(),Pc),(Ur(),nx))}function W_e(){W_e=Y,Ton=qt(new or,(zr(),Pc),(Ur(),nx))}function qV(){qV=Y,Ion=qt(new or,(zr(),Pc),(Ur(),nx))}function Z_e(){Z_e=Y,Tsn=Eo(new or,(Y4(),Nx),(cS(),cye))}function eLe(){eLe=Y,qen=It((BP(),z(B(Gen,1),ke,537,0,[Wne])))}function $R(){$R=Y,$ce=new pt,WD=new pt,Hgn(fnn,new F6)}function N4n(e,n){var t,i;t=n.c,i=t!=null,i&&T4(e,new y2(n.c))}function nLe(e,n){Kvn(e,e.b,e.c),u(e.b.b,68),n&&u(n.b,68).b}function RR(e,n){X(e.Cb,184)&&(u(e.Cb,184).tb=null),Mo(e,n)}function UV(e,n){X(e.Cb,88)&&H2(Ms(u(e.Cb,88)),4),Mo(e,n)}function D4n(e,n){Q1e(e,n),X(e.Cb,88)&&H2(Ms(u(e.Cb,88)),2)}function I4n(e,n){return ki(u(e.c,65).c.e.b,u(n.c,65).c.e.b)}function _4n(e,n){return ki(u(e.c,65).c.e.a,u(n.c,65).c.e.a)}function fo(e,n){return Cc(),xY(n)?new rR(n,e):new pC(n,e)}function XV(e,n){e.a&&qo(e.a.k,e),e.a=n,e.a&&Te(e.a.k,e)}function KV(e,n){e.b&&qo(e.b.f,e),e.b=n,e.b&&Te(e.b.f,e)}function T0(e,n,t){OFe(n,t,e.gc()),this.c=e,this.a=n,this.b=t-n}function N4(e){this.c=new Si,this.b=e.b,this.d=e.c,this.a=e.a}function VV(e){this.a=k.Math.cos(e),this.b=k.Math.sin(e)}function Gb(e,n,t,i){this.c=e,this.d=i,XV(this,n),KV(this,t)}function pn(e,n){this.b=(_n(e),e),this.a=(n&W2)==0?n|64|yh:n}function L4n(e,n){YCe(e,Rt(Rr(kw(n,24),rF)),Rt(Rr(n,rF)))}function qC(e){return mh(),ao(e,0)>=0?B0(e):sE(B0(Td(e)))}function P4n(){return zl(),z(B(Qo,1),ke,130,0,[Kme,Yo,Vme])}function tLe(e,n,t){return new gW(e,(H9(),fte),null,!1,n,t)}function iLe(e,n,t){return new gW(e,(H9(),hte),n,t,null,!1)}function rLe(e,n,t){var i;OFe(n,t,e.c.length),i=t-n,Goe(e.c,n,i)}function cLe(e,n){var t;return t=u(L2(O4(e.a),n),18),t?t.gc():0}function Eae(e){var n;return R0(e),n=(Aw(),Aw(),ote),aB(e,n)}function uLe(e){for(var n;;)if(n=e.Pb(),!e.Ob())return n}function oLe(e){var n,t;return t=(d9(),n=new hw,n),q9(t,e),t}function sLe(e){var n,t;return t=(d9(),n=new hw,n),q9(t,e),t}function J3(e){return Mj(),X(e.g,9)?u(e.g,9):null}function $4n(){return _w(),z(B(Rte,1),ke,368,0,[lp,ib,sp])}function R4n(){return pB(),z(B(yve,1),ke,350,0,[vve,UJ,Kte])}function B4n(){return Wb(),z(B(Bin,1),ke,449,0,[tie,k7,Ov])}function z4n(){return W9(),z(B(hie,1),ke,302,0,[fie,aie,tD])}function F4n(){return _0(),z(B(die,1),ke,329,0,[iD,Bve,dm])}function J4n(){return _1(),z(B(Kin,1),ke,315,0,[rD,Dv,Sy])}function H4n(){return IE(),z(B(D4e,1),ke,352,0,[Vie,N4e,SH])}function G4n(){return Nc(),z(B(Xun,1),ke,452,0,[xx,ys,Do])}function q4n(){return DB(),z(B(q4e,1),ke,381,0,[H4e,rre,G4e])}function U4n(){return DE(),z(B(U4e,1),ke,348,0,[ure,cre,pD])}function X4n(){return r8(),z(B(K4e,1),ke,349,0,[ore,X4e,Ax])}function K4n(){return wB(),z(B(Q4e,1),ke,351,0,[Y4e,sre,V4e])}function V4n(){return IB(),z(B(W4e,1),ke,382,0,[lre,I7,xm])}function Y4n(){return yE(),z(B(w3e,1),ke,384,0,[Ete,jte,Ste])}function Q4n(){return ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])}function W4n(){return ws(),z(B(Onn,1),ke,461,0,[Th,nb,Uf])}function Z4n(){return Uo(),z(B(Dnn,1),ke,462,0,[ka,tb,Xf])}function eyn(){return NE(),z(B(gye,1),ke,385,0,[bye,hre,yD])}function nyn(){return EO(),z(B(Hye,1),ke,386,0,[zH,Fye,Jye])}function tyn(){return XB(),z(B(a6e,1),ke,387,0,[f6e,Gre,l6e])}function iyn(){return NB(),z(B(o6e,1),ke,303,0,[Pre,u6e,c6e])}function ryn(){return WB(),z(B(s6e,1),ke,436,0,[Px,HH,$re])}function cyn(){return JB(),z(B(L6e,1),ke,430,0,[I6e,_6e,Yre])}function uyn(){return TO(),z(B(Qre,1),ke,435,0,[XH,KH,VH])}function oyn(){return rB(),z(B(D6e,1),ke,429,0,[Vre,N6e,O6e])}function syn(){return $a(),z(B(t8e,1),ke,279,0,[F7,_m,J7])}function lyn(){return B1(),z(B(b8e,1),ke,347,0,[oG,Yd,Qx])}function fyn(){return jE(),z(B(y8e,1),ke,300,0,[HD,Tce,v8e])}function ayn(){return G4(),z(B(E8e,1),ke,281,0,[j8e,Pm,dG])}function _a(e){return pu(z(B(Lr,1),Se,8,0,[e.i.n,e.n,e.a]))}function hyn(e,n,t){var i;i=new wc(t.d),gi(i,e),W1e(n,i.a,i.b)}function lLe(e,n,t){var i;i=new dM,i.b=n,i.a=t,++n.b,Te(e.d,i)}function dyn(e,n,t){var i;return i=fS(e,n,!1),i.b<=n&&i.a<=t}function byn(e){if(e.p!=2)throw $(new is);return Rt(e.f)&yr}function gyn(e){if(e.p!=2)throw $(new is);return Rt(e.k)&yr}function vn(e,n){if(e<0||e>=n)throw $(new jo(Qge+e+Wge+n))}function Yn(e,n){if(e<0||e>=n)throw $(new Doe(Qge+e+Wge+n))}function wyn(e){return e.Db>>16!=6?null:u(SW(e),241)}function fLe(e,n){var t,i;return i=O9(e,n),t=e.a.dd(i),new aMe(e,t)}function pyn(e,n){var t;return t=(_n(e),e).g,gle(!!t),_n(n),t(n)}function myn(e){return e.a==(z9(),AG)&>(e,KDn(e.g,e.b)),e.a}function D4(e){return e.d==(z9(),AG)&&Gue(e,U_n(e.g,e.b)),e.d}function Sae(e,n){vbn.call(this,new l4(I2(e))),ll(n,aYe),this.a=n}function aLe(e,n,t){aw.call(this,25),this.b=e,this.a=n,this.c=t}function ul(e){fi(),aw.call(this,e),this.c=!1,this.a=!1}function hLe(e,n){zb.call(this,1,2,z(B($t,1),ni,30,15,[e,n]))}function Rr(e,n){return I0(pvn(uu(e)?sf(e):e,uu(n)?sf(n):n))}function hh(e,n){return I0(mvn(uu(e)?sf(e):e,uu(n)?sf(n):n))}function YV(e,n){return I0(vvn(uu(e)?sf(e):e,uu(n)?sf(n):n))}function xae(e,n){return NDe(e.a,n)?gfe(e.b,u(n,23).g,null):null}function qb(e){return Nt(e),X(e,18)?new bs(u(e,18)):M9(e.Jc())}function QV(e){cR(),this.a=(jn(),X(e,59)?new DX(e):new B$(e))}function vyn(e){var n;return n=u(nR(e.b),10),new _l(e.a,n,e.c)}function yyn(e,n){var t;t=ne(re(e.a.mf((Xt(),iG)))),$Ve(e,n,t)}function kyn(e,n){return kE(),e.c==n.c?ki(n.d,e.d):ki(e.c,n.c)}function jyn(e,n){return kE(),e.c==n.c?ki(e.d,n.d):ki(e.c,n.c)}function Eyn(e,n){return kE(),e.c==n.c?ki(e.d,n.d):ki(n.c,e.c)}function Syn(e,n){return kE(),e.c==n.c?ki(n.d,e.d):ki(n.c,e.c)}function xyn(e,n){e.b=e.b|n.b,e.c=e.c|n.c,e.d=e.d|n.d,e.a=e.a|n.a}function _(e){return at(e.ai?1:0}function bLe(e,n){var t,i;return t=yY(n),i=t,u(Bn(e.c,i),15).a}function WV(e,n,t){var i;i=e.d[n.p],e.d[n.p]=e.d[t.p],e.d[t.p]=i}function Cyn(e,n,t){var i;e.n&&n&&t&&(i=new gL,Te(e.e,i))}function ZV(e,n){if(hr(e.a,n),n.d)throw $(new hu(LYe));n.d=e}function Tae(e,n){this.a=new Ce,this.d=new Ce,this.f=e,this.c=n}function gLe(){J4(),this.b=new pt,this.a=new pt,this.c=new Ce}function wLe(){this.c=new LCe,this.a=new KPe,this.b=new fxe,MMe()}function pLe(e,n,t){this.d=e,this.j=n,this.e=t,this.o=-1,this.p=3}function mLe(e,n,t){this.d=e,this.k=n,this.f=t,this.o=-1,this.p=5}function vLe(e,n,t,i,r,c){Che.call(this,e,n,t,i,r),c&&(this.o=-2)}function yLe(e,n,t,i,r,c){Ohe.call(this,e,n,t,i,r),c&&(this.o=-2)}function kLe(e,n,t,i,r,c){Gae.call(this,e,n,t,i,r),c&&(this.o=-2)}function jLe(e,n,t,i,r,c){Ihe.call(this,e,n,t,i,r),c&&(this.o=-2)}function ELe(e,n,t,i,r,c){qae.call(this,e,n,t,i,r),c&&(this.o=-2)}function SLe(e,n,t,i,r,c){Nhe.call(this,e,n,t,i,r),c&&(this.o=-2)}function xLe(e,n,t,i,r,c){Dhe.call(this,e,n,t,i,r),c&&(this.o=-2)}function ALe(e,n,t,i,r,c){Uae.call(this,e,n,t,i,r),c&&(this.o=-2)}function MLe(e,n,t,i){DP.call(this,t),this.b=e,this.c=n,this.d=i}function TLe(e,n){this.f=e,this.a=(z9(),xG),this.c=xG,this.b=n}function CLe(e,n){this.g=e,this.d=(z9(),AG),this.a=AG,this.b=n}function Cae(e,n){!e.c&&(e.c=new nr(e,0)),Xz(e.c,(Ei(),lA),n)}function Oyn(e,n){return AOn(e,n,X(n,103)&&(u(n,19).Bb&Ec)!=0)}function Nyn(e,n){return iIe(Pu(e.q.getTime()),Pu(n.q.getTime()))}function OLe(e){return iV(e.e.Pd().gc()*e.c.Pd().gc(),16,new X5(e))}function Dyn(e){return!!e.u&&Vu(e.u.a).i!=0&&!(e.n&&zQ(e.n))}function Iyn(e){return!!e.a&&Cs(e.a.a).i!=0&&!(e.b&&FQ(e.b))}function Oae(e,n){return n==0?!!e.o&&e.o.f!=0:_Q(e,n)}function NLe(e){return at(e.b.b!=e.d.a),e.c=e.b=e.b.b,--e.a,e.c.c}function bE(e){for(;e.d>0&&e.a[--e.d]==0;);e.a[e.d++]==0&&(e.e=0)}function DLe(e){return e.a?e.e.length==0?e.a.a:e.a.a+(""+e.e):e.c}function qr(e,n){this.a=e,qc.call(this,e),S2(n,e.gc()),this.b=n}function ILe(e){this.a=oe(Ar,On,1,b1e(k.Math.max(8,e))<<1,5,1)}function _Le(e){zY.call(this,e,(H9(),lte),null,!1,null,!1)}function LLe(e,n){var t;return t=1-n,e.a[t]=xB(e.a[t],t),xB(e,n)}function PLe(e,n){var t,i;return i=Rr(e,Ic),t=Gh(n,32),hh(t,i)}function _yn(e,n,t){var i;return i=u(e.Zb().xc(n),18),!!i&&i.Gc(t)}function $Le(e,n,t){var i;return i=u(e.Zb().xc(n),18),!!i&&i.Kc(t)}function RLe(e,n,t){var i;i=(Nt(e),new bs(e)),bxn(new G_e(i,n,t))}function XC(e,n,t){var i;i=(Nt(e),new bs(e)),gxn(new q_e(i,n,t))}function Lyn(e,n,t){e.a=n,e.c=t,e.b.a.$b(),qs(e.d),Qp(e.e.a.c,0)}function BLe(e,n){var t;e.e=new Soe,t=q2(n),Cr(t,e.c),fXe(e,t,0)}function Pyn(e,n){return new ZK(n,COe(pc(n.e),e,e),(Pn(),!0))}function $yn(e,n){return R4(),u(T(n,(Mu(),Hv)),15).a>=e.gc()}function Ryn(e){return cl(),!cc(e)&&!(!cc(e)&&e.c.i.c==e.d.i.c)}function dh(e){return u(Ra(e,oe(b7,K8,17,e.c.length,0,1)),323)}function Byn(e){UFe((!e.a&&(e.a=new pe(Jt,e,10,11)),e.a),new DM)}function Nae(){var e,n,t;return n=(t=(e=new hw,e),t),Te(u7e,n),n}function xu(e,n,t,i,r,c){return WBe(e,n,t,c),H1e(e,i),G1e(e,r),e}function zLe(e,n,t,i){return e.a+=""+of(n==null?Vo:su(n),t,i),e}function KC(e,n){if(e<0||e>=n)throw $(new jo(WTn(e,n)));return e}function FLe(e,n,t){if(e<0||nt)throw $(new jo(vTn(e,n,t)))}function Ee(e,n,t,i){var r;r=new zM,r.a=n,r.b=t,r.c=i,Vt(e.b,r)}function Gi(e,n,t,i){var r;r=new zM,r.a=n,r.b=t,r.c=i,Vt(e.a,r)}function zyn(e,n,t){var i;i=FEn();try{return Kpn(e,n,t)}finally{i9n(i)}}function Xb(e){var n;return uu(e)?(n=e,n==-0?0:n):e8n(e)}function JLe(e,n){return X(n,45)?UQ(e.a,u(n,45)):!1}function HLe(e,n){return X(n,45)?UQ(e.a,u(n,45)):!1}function GLe(e,n){return X(n,45)?UQ(e.a,u(n,45)):!1}function Fyn(e,n){return e.a<=e.b?(n.Bd(e.a++),!0):!1}function Jyn(e){return z3(e).dc()?!1:(Own(e,new ae),!0)}function Dae(e){var n;return A0(e),n=new rt,x3(e.a,new Fke(n)),n}function BR(e){var n;return A0(e),n=new lt,x3(e.a,new Jke(n)),n}function Hyn(e){if(!("stack"in e))try{throw e}catch{}return e}function zR(e){return new xo((ll(e,gZ),fB(mc(mc(5,e),e/10|0))))}function qLe(e){return u(Ra(e,oe(cin,lQe,12,e.c.length,0,1)),2004)}function Gyn(e){return iV(e.e.Pd().gc()*e.c.Pd().gc(),273,new JU(e))}function ULe(){ULe=Y,$ln=It((ZP(),z(B(P6e,1),ke,477,0,[Wre])))}function XLe(){XLe=Y,Bln=It((e$(),z(B(Rln,1),ke,546,0,[Zre])))}function KLe(){KLe=Y,ifn=It((Tj(),z(B(i9e,1),ke,527,0,[TD])))}function VLe(){VLe=Y,eye=TIe(ve(1),ve(4)),Z4e=TIe(ve(1),ve(2))}function FR(){FR=Y,xre=new Sse("DFS",0),Pye=new Sse("BFS",1)}function JR(){JR=Y,bie=new wse(F8,0),zve=new wse("TOP_LEFT",1)}function Iae(e,n,t){this.d=new tEe(this),this.e=e,this.i=n,this.f=t}function _ae(e,n,t,i){this.d=e,this.n=n,this.g=t,this.o=i,this.p=-1}function qyn(e,n,t){e.d&&qo(e.d.e,e),e.d=n,e.d&&Pb(e.d.e,t,e)}function Uyn(e,n,t){var i;return i=d8(t),Fz(e.n,i,n),Fz(e.o,n,t),n}function B9(e,n){var t,i;return t=T2(e,n),i=null,t&&(i=t.qe()),i}function gE(e,n){var t,i;return t=O1(e,n),i=null,t&&(i=t.qe()),i}function Ow(e,n){var t,i;return t=O1(e,n),i=null,t&&(i=t.ne()),i}function N1(e,n){var t,i;return t=O1(e,n),i=null,t&&(i=D0e(t)),i}function wE(e,n){$Rn(n,e),afe(e.d),afe(u(T(e,(Ne(),pH)),213))}function eY(e,n){RRn(n,e),hfe(e.d),hfe(u(T(e,(Ne(),pH)),213))}function C0(e,n){_n(n),e.b=e.b-1&e.a.length-1,tr(e.a,e.b,n),kHe(e)}function Lae(e,n){_n(n),tr(e.a,e.c,n),e.c=e.c+1&e.a.length-1,kHe(e)}function jt(e){return at(e.b!=e.d.c),e.c=e.b,e.b=e.b.a,++e.a,e.c.c}function YLe(e){if(e.e.g!=e.b)throw $(new Nl);return!!e.c&&e.d>0}function x2(e){return X(e,18)?u(e,18).dc():!e.Jc().Ob()}function Xyn(e){return new pn(N8n(u(e.a.kd(),18).gc(),e.a.jd()),16)}function QLe(e){var n;n=e.Dh(),this.a=X(n,72)?u(n,72).Gi():n.Jc()}function Pae(e,n){var t;return t=u(Pa(e.b,n),66),!t&&(t=new Si),t}function Kyn(e,n){var t;t=n.a,lc(t,n.c.d),Gr(t,n.d.d),N2(t.a,e.n)}function WLe(e,n,t,i){return X(t,59)?new kOe(e,n,t,i):new Nfe(e,n,t,i)}function Vyn(){return Ff(),z(B(Ein,1),ke,413,0,[am,w7,p7,$te])}function Yyn(){return Lw(),z(B(itn,1),ke,409,0,[XN,UN,pte,mte])}function Qyn(){return Z9(),z(B(Xtn,1),ke,408,0,[op,sm,om,xv])}function Wyn(){return H9(),z(B(dJ,1),ke,309,0,[lte,fte,ate,hte])}function Zyn(){return X4(),z(B(y3e,1),ke,383,0,[ZS,v3e,Cte,Ote])}function e6n(){return LB(),z(B(hin,1),ke,367,0,[Pte,zJ,FJ,WN])}function n6n(){return BE(),z(B(mve,1),ke,301,0,[ix,wve,eD,pve])}function t6n(){return B2(),z(B(Qie,1),ke,203,0,[xH,Yie,Fv,zv])}function i6n(){return F1(),z(B(F4e,1),ke,269,0,[ob,z4e,ere,nre])}function r6n(){return eg(),z(B(pon,1),ke,404,0,[mD,Mx,CH,TH])}function c6n(e){var n;return e.j==(De(),bt)&&(n=Wqe(e),cs(n,et))}function u6n(){return Y4(),z(B(iye,1),ke,398,0,[IH,Ox,Nx,Dx])}function ZLe(e,n){return u(Js(p2(u(pi(e.k,n),16).Mc(),Mv)),113)}function ePe(e,n){return u(Js(x4(u(pi(e.k,n),16).Mc(),Mv)),113)}function o6n(e,n){return w4(new je(n.e.a+n.f.a/2,n.e.b+n.f.b/2),e)}function s6n(){return kz(),z(B(cln,1),ke,401,0,[Fre,Rre,zre,Bre])}function l6n(){return az(),z(B(r6e,1),ke,354,0,[Lre,t6e,i6e,n6e])}function f6n(){return OE(),z(B(Lye,1),ke,353,0,[Sre,RH,Ere,jre])}function a6n(){return u8(),z(B(n8e,1),ke,278,0,[$D,uG,Z9e,e8e])}function h6n(){return z1(),z(B(Ace,1),ke,222,0,[xce,RD,H7,Gy])}function d6n(){return al(),z(B(Wfn,1),ke,292,0,[zD,s1,hb,BD])}function b6n(){return UR(),z(B(KD,1),ke,288,0,[S8e,A8e,Oce,x8e])}function g6n(){return Vs(),z(B(tA,1),ke,380,0,[qD,Og,GD,Lm])}function w6n(){return KB(),z(B(O8e,1),ke,326,0,[Nce,M8e,C8e,T8e])}function p6n(){return PB(),z(B(wan,1),ke,407,0,[Dce,D8e,N8e,I8e])}function Ll(e,n,t){return n<0?kW(e,t):u(t,69).uk().zk(e,e.ei(),n)}function m6n(e,n,t){var i;return i=d8(t),Fz(e.f,i,n),ei(e.g,n,t),n}function v6n(e,n,t){var i;return i=d8(t),Fz(e.p,i,n),ei(e.q,n,t),n}function nPe(e){var n,t;return n=(v0(),t=new w3,t),e&&Dz(n,e),n}function $ae(e){var n;return n=e.$i(e.i),e.i>0&&Wu(e.g,0,n,0,e.i),n}function I4(e){return Mj(),X(e.g,156)?u(e.g,156):null}function y6n(e){return $R(),so($ce,e)?u(Bn($ce,e),342).Pg():null}function k6n(e){e.a=null,e.e=null,Qp(e.b.c,0),Qp(e.f.c,0),e.c=null}function tPe(e,n){var t;for(t=e.j.c.length;t>24}function E6n(e){if(e.p!=1)throw $(new is);return Rt(e.k)<<24>>24}function S6n(e){if(e.p!=7)throw $(new is);return Rt(e.k)<<16>>16}function x6n(e){if(e.p!=7)throw $(new is);return Rt(e.f)<<16>>16}function H3(e,n){return n.e==0||e.e==0?KS:(A8(),TW(e,n))}function cPe(e,n){return ue(n)===ue(e)?"(this Map)":n==null?Vo:su(n)}function A6n(e,n,t){return dV(re(du(Xc(e.f,n))),re(du(Xc(e.f,t))))}function M6n(e,n,t){var i;i=u(Bn(e.g,t),60),Te(e.a.c,new jc(n,i))}function uPe(e,n){var t;return t=new o4,e.Ed(t),t.a+="..",n.Fd(t),t.a}function aa(e){var n;for(n=0;e.Ob();)e.Pb(),n=mc(n,1);return fB(n)}function T6n(e,n,t,i,r){var c;c=zOn(r,t,i),Te(n,GTn(r,c)),RMn(e,r,n)}function oPe(e,n,t){e.i=0,e.e=0,n!=t&&(Gze(e,n,t),Hze(e,n,t))}function sPe(e,n,t,i){this.e=null,this.c=e,this.d=n,this.a=t,this.b=i}function Rae(e,n,t,i,r){this.i=e,this.a=n,this.e=t,this.j=i,this.f=r}function lPe(e,n){hae.call(this),this.a=e,this.b=n,Te(this.a.b,this)}function D1(e,n){mh(),zb.call(this,e,1,z(B($t,1),ni,30,15,[n]))}function C6n(e,n,t){return C8(e,n,t,X(n,103)&&(u(n,19).Bb&Ec)!=0)}function HR(e,n,t){return Hz(e,n,t,X(n,103)&&(u(n,19).Bb&Ec)!=0)}function O6n(e,n,t){return DOn(e,n,t,X(n,103)&&(u(n,19).Bb&Ec)!=0)}function Bae(e,n){return e==(zn(),Qi)&&n==Qi?4:e==Qi||n==Qi?8:32}function N6n(e,n){return u(n==null?du(Xc(e.f,null)):Dj(e.i,n),290)}function fPe(e,n){var t;for(t=n;t;)a2(e,t.i,t.j),t=Bi(t);return e}function Vu(e){return e.n||(Ms(e),e.n=new $De(e,Rc,e),tu(e)),e.n}function Xh(e,n){Cc();var t;return t=u(e,69).tk(),QMn(t,n),t.vl(n)}function pE(e){return at(e.a"+Aae(e.d):"e_"+vw(e)}function I6n(e,n){var t;return t=n!=null?lo(e,n):du(Xc(e.f,n)),O$(t)}function _6n(e,n){var t;return t=n!=null?lo(e,n):du(Xc(e.f,n)),O$(t)}function bPe(e,n){var t;for(t=0;t=0&&e.a[t]===n[t];t--);return t<0}function B6n(e,n){var t,i;i=!1;do t=Dze(e,n),i=i|t;while(t);return i}function mE(){mE=Y,Cx=new vse("UPPER",0),Tx=new vse("LOWER",1)}function qR(){qR=Y,Sie=new pse(ma,0),Eie=new pse("ALTERNATING",1)}function UR(){UR=Y,S8e=new gDe,A8e=new QDe,Oce=new E_e,x8e=new WDe}function wPe(){wPe=Y,_in=It((nB(),z(B(gve,1),ke,422,0,[bve,Xte])))}function pPe(){pPe=Y,Rin=It((fO(),z(B(Sve,1),ke,419,0,[XJ,Eve])))}function mPe(){mPe=Y,Jin=It((rO(),z(B(Mve,1),ke,476,0,[Ave,VJ])))}function vPe(){vPe=Y,Yin=It((JR(),z(B(Fve,1),ke,420,0,[bie,zve])))}function yPe(){yPe=Y,Zin=It((qR(),z(B(n5e,1),ke,423,0,[Sie,Eie])))}function kPe(){kPe=Y,Uun=It((uO(),z(B(J4e,1),ke,421,0,[tre,ire])))}function jPe(){jPe=Y,Eon=It((mE(),z(B(jon,1),ke,518,0,[Cx,Tx])))}function EPe(){EPe=Y,Don=It((Da(),z(B(Non,1),ke,508,0,[Ag,Ya])))}function SPe(){SPe=Y,Oon=It((ah(),z(B(Con,1),ke,509,0,[pp,Ud])))}function xPe(){xPe=Y,Xon=It((ha(),z(B(Uon,1),ke,515,0,[Am,sb])))}function APe(){APe=Y,esn=It((Cw(),z(B(Zon,1),ke,454,0,[lb,Jv])))}function MPe(){MPe=Y,Msn=It((FR(),z(B($ye,1),ke,425,0,[xre,Pye])))}function TPe(){TPe=Y,Dsn=It((SB(),z(B(Rye,1),ke,487,0,[BH,qv])))}function CPe(){CPe=Y,Lsn=It((iB(),z(B(zye,1),ke,426,0,[Bye,Nre])))}function OPe(){OPe=Y,Nln=It((VR(),z(B(C6e,1),ke,478,0,[Kre,T6e])))}function NPe(){NPe=Y,zln=It((WC(),z(B($6e,1),ke,428,0,[ece,YH])))}function DPe(){DPe=Y,rfn=It((pO(),z(B(c9e,1),ke,427,0,[WH,r9e])))}function IPe(){IPe=Y,dtn=It((lB(),z(B(n3e,1),ke,424,0,[vte,pJ])))}function _Pe(){_Pe=Y,lin=It((K9(),z(B(sin,1),ke,502,0,[QN,Dte])))}function XR(e){w0e(),YCe(this,Rt(Rr(kw(e,24),rF)),Rt(Rr(e,rF)))}function z6n(e){return(e.k==(zn(),Qi)||e.k==wr)&&bi(e,(me(),ox))}function F6n(e,n,t){return u(n==null?Ko(e.f,null,t):Pw(e.i,n,t),290)}function J6n(){return vr(),z(B(Vx,1),ke,86,0,[eh,ru,Zc,Za,Vl])}function H6n(){return De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])}function G6n(e){return JP(),function(){return zyn(e,this,arguments)}}function LPe(e,n){var t;return t=n.jd(),new bw(t,e.e.pc(t,u(n.kd(),18)))}function PPe(e,n){var t,i;return t=n.jd(),i=e.De(t),!!i&&Ku(i.e,n.kd())}function rc(e,n){var t,i;for(_n(n),i=e.Jc();i.Ob();)t=i.Pb(),n.Ad(t)}function ol(e,n,t){var i;return i=(vn(n,e.c.length),e.c[n]),e.c[n]=t,i}function Hae(e,n){var t,i;for(t=n,i=0;t>0;)i+=e.a[t],t-=t&-t;return i}function $Pe(e,n){var t;for(t=n;t;)a2(e,-t.i,-t.j),t=Bi(t);return e}function q6n(e,n){var t;return t=e.a.get(n),t??oe(Ar,On,1,0,5,1)}function G3(e,n){return(R0(e),b9(new wn(e,new whe(n,e.a)))).zd(vy)}function U6n(){return zr(),z(B(p3e,1),ke,363,0,[Kf,r1,eo,no,Pc])}function RPe(e){eYe(),KSe(this),this.a=new Si,x1e(this,e),Vt(this.a,e)}function BPe(){MK(this),this.b=new je(Ki,Ki),this.a=new je(Dr,Dr)}function uY(e){KR(),!Ka&&(this.c=e,this.e=!0,this.a=new Ce)}function KR(){KR=Y,Ka=!0,pnn=!1,mnn=!1,ynn=!1,vnn=!1}function VR(){VR=Y,Kre=new Mse(bwe,0),T6e=new Mse("TARGET_WIDTH",1)}function X6n(){return vz(),z(B(Isn,1),ke,364,0,[Cre,Are,Ore,Mre,Tre])}function K6n(){return z2(),z(B(xin,1),ke,371,0,[ZN,GJ,qJ,HJ,JJ])}function V6n(){return HE(),z(B(_4e,1),ke,328,0,[I4e,Wie,Zie,jx,Ex])}function Y6n(){return Xs(),z(B(e5e,1),ke,165,0,[sD,fx,V1,ax,yg])}function Q6n(){return nS(),z(B(_ln,1),ke,369,0,[Uv,$y,Jx,Fx,MD])}function W6n(){return JO(),z(B(F6e,1),ke,330,0,[R6e,nce,z6e,tce,B6e])}function Z6n(){return ph(),z(B(Qa,1),ke,160,0,[Mn,fr,Sa,Kd,Q1])}function e9n(){return nv(),z(B(Zx,1),ke,257,0,[db,FD,g8e,Wx,w8e])}function oY(e,n){var t;return t=u(Pa(e.d,n),21),t||u(Pa(e.e,n),21)}function zPe(e){this.b=e,ut.call(this,e),this.a=u(Un(this.b.a,4),129)}function FPe(e){this.b=e,m4.call(this,e),this.a=u(Un(this.b.a,4),129)}function JPe(e,n){this.c=0,this.b=n,cCe.call(this,e,17493),this.a=this.c}function Pf(e,n,t,i,r){VPe.call(this,n,i,r),this.c=e,this.b=t}function Gae(e,n,t,i,r){pLe.call(this,n,i,r),this.c=e,this.a=t}function qae(e,n,t,i,r){mLe.call(this,n,i,r),this.c=e,this.a=t}function Uae(e,n,t,i,r){VPe.call(this,n,i,r),this.c=e,this.a=t}function Xae(e,n,t){e.a.c.length=0,MPn(e,n,t),e.a.c.length==0||ZIn(e,n)}function VC(e){e.i=0,rC(e.b,null),rC(e.c,null),e.a=null,e.e=null,++e.g}function n9n(e){return e.e=3,e.d=e.Yb(),e.e!=2?(e.e=0,!0):!1}function Kae(e,n){return X(n,144)?bn(e.c,u(n,144).c):!1}function HPe(e){var n;return e.c||(n=e.r,X(n,88)&&(e.c=u(n,29))),e.c}function Ms(e){return e.t||(e.t=new $Se(e),$E(new nAe(e),0,e.t)),e.t}function cc(e){return!e.c||!e.d?!1:!!e.c.i&&e.c.i==e.d.i}function _4(e,n){return n==0||e.e==0?e:n>0?fJe(e,n):QUe(e,-n)}function Vae(e,n){return n==0||e.e==0?e:n>0?QUe(e,n):fJe(e,-n)}function tt(e){if(ht(e))return e.c=e.a,e.a.Pb();throw $(new au)}function GPe(e){var n;return n=e.length,bn($n.substr($n.length-n,n),e)}function qPe(e){var n,t;return n=e.c.i,t=e.d.i,n.k==(zn(),wr)&&t.k==wr}function sY(e){var n,t,i;return n=e&Ls,t=e>>22&Ls,i=e<0?G1:0,_o(n,t,i)}function t9n(e,n){var t,i;t=u(Ykn(e.c,n),18),t&&(i=t.gc(),t.$b(),e.d-=i)}function i9n(e){e&&c8n((Toe(),yme)),--oJ,e&&sJ!=-1&&(Jgn(sJ),sJ=-1)}function Yae(e){_gn.call(this,e==null?Vo:su(e),X(e,80)?u(e,80):null)}function lY(e){var n;return n=new Mw,$u(n,e),he(n,(Ne(),Wc),null),n}function fY(e,n,t){var i;return i=e.Fh(n),i>=0?e.Ih(i,t,!0):Gw(e,n,t)}function r9n(e,n,t){return ki(w4(b8(e),pc(n.b)),w4(b8(e),pc(t.b)))}function c9n(e,n,t){return ki(w4(b8(e),pc(n.e)),w4(b8(e),pc(t.e)))}function u9n(e,n){return k.Math.min(N0(n.a,e.d.d.c),N0(n.b,e.d.d.c))}function UPe(e,n,t){var i;i=new Vse(e.a),xE(i,e.a.a),Ko(i.f,n,t),e.a.a=i}function Qae(e,n,t,i){var r;for(r=0;rn)throw $(new jo(F0e(e,n,"index")));return e}function ehe(e){var n;return n=e.e+e.f,isNaN(n)&&X$(e.d)?e.d:n}function s9n(e,n){var t;t=e.q.getHours()+(n/60|0),e.q.setMinutes(n),oS(e,t)}function nhe(e,n){var t,i;return t=(_n(e),e),i=(_n(n),n),t==i?0:tn.p?-1:0}function n$e(e,n){return so(e.a,n)?(L4(e.a,n),!0):!1}function h9n(e){var n,t;return n=e.jd(),t=u(e.kd(),18),NC(t.Lc(),new Z6(n))}function hY(e){var n;return n=e.b,n.b==0?null:u(Yu(n,0),65).b}function WR(e,n){return _n(n),e.c=0,"Initial capacity must not be negative")}function eB(){eB=Y,Hx=new yi("org.eclipse.elk.labels.labelManager")}function i$e(){i$e=Y,lve=new Li("separateLayerConnections",(LB(),Pte))}function ha(){ha=Y,Am=new jse("REGULAR",0),sb=new jse("CRITICAL",1)}function WC(){WC=Y,ece=new Tse("FIXED",0),YH=new Tse("CENTER_NODE",1)}function nB(){nB=Y,bve=new dse("QUADRATIC",0),Xte=new dse("SCANLINE",1)}function r$e(){r$e=Y,Pin=It((pB(),z(B(yve,1),ke,350,0,[vve,UJ,Kte])))}function c$e(){c$e=Y,zin=It((Wb(),z(B(Bin,1),ke,449,0,[tie,k7,Ov])))}function u$e(){u$e=Y,Uin=It((W9(),z(B(hie,1),ke,302,0,[fie,aie,tD])))}function o$e(){o$e=Y,Xin=It((_0(),z(B(die,1),ke,329,0,[iD,Bve,dm])))}function s$e(){s$e=Y,Vin=It((_1(),z(B(Kin,1),ke,315,0,[rD,Dv,Sy])))}function l$e(){l$e=Y,Nin=It((_w(),z(B(Rte,1),ke,368,0,[lp,ib,sp])))}function f$e(){f$e=Y,Fun=It((IE(),z(B(D4e,1),ke,352,0,[Vie,N4e,SH])))}function a$e(){a$e=Y,Kun=It((Nc(),z(B(Xun,1),ke,452,0,[xx,ys,Do])))}function h$e(){h$e=Y,Vun=It((DB(),z(B(q4e,1),ke,381,0,[H4e,rre,G4e])))}function d$e(){d$e=Y,Yun=It((DE(),z(B(U4e,1),ke,348,0,[ure,cre,pD])))}function b$e(){b$e=Y,Qun=It((r8(),z(B(K4e,1),ke,349,0,[ore,X4e,Ax])))}function g$e(){g$e=Y,Wun=It((wB(),z(B(Q4e,1),ke,351,0,[Y4e,sre,V4e])))}function w$e(){w$e=Y,Zun=It((IB(),z(B(W4e,1),ke,382,0,[lre,I7,xm])))}function p$e(){p$e=Y,isn=It((NE(),z(B(gye,1),ke,385,0,[bye,hre,yD])))}function m$e(){m$e=Y,Psn=It((EO(),z(B(Hye,1),ke,386,0,[zH,Fye,Jye])))}function v$e(){v$e=Y,iln=It((NB(),z(B(o6e,1),ke,303,0,[Pre,u6e,c6e])))}function y$e(){y$e=Y,rln=It((WB(),z(B(s6e,1),ke,436,0,[Px,HH,$re])))}function k$e(){k$e=Y,Dln=It((rB(),z(B(D6e,1),ke,429,0,[Vre,N6e,O6e])))}function j$e(){j$e=Y,Iln=It((JB(),z(B(L6e,1),ke,430,0,[I6e,_6e,Yre])))}function E$e(){E$e=Y,Pln=It((TO(),z(B(Qre,1),ke,435,0,[XH,KH,VH])))}function S$e(){S$e=Y,lln=It((XB(),z(B(a6e,1),ke,387,0,[f6e,Gre,l6e])))}function x$e(){x$e=Y,Btn=It((yE(),z(B(w3e,1),ke,384,0,[Ete,jte,Ste])))}function A$e(){A$e=Y,knn=It((zl(),z(B(Qo,1),ke,130,0,[Kme,Yo,Vme])))}function M$e(){M$e=Y,Cnn=It((ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])))}function T$e(){T$e=Y,Nnn=It((ws(),z(B(Onn,1),ke,461,0,[Th,nb,Uf])))}function C$e(){C$e=Y,Inn=It((Uo(),z(B(Dnn,1),ke,462,0,[ka,tb,Xf])))}function O$e(){O$e=Y,Xfn=It(($a(),z(B(t8e,1),ke,279,0,[F7,_m,J7])))}function N$e(){N$e=Y,han=It((G4(),z(B(E8e,1),ke,281,0,[j8e,Pm,dG])))}function D$e(){D$e=Y,Qfn=It((B1(),z(B(b8e,1),ke,347,0,[oG,Yd,Qx])))}function I$e(){I$e=Y,lan=It((jE(),z(B(y8e,1),ke,300,0,[HD,Tce,v8e])))}function da(e,n){return!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),SQ(e.o,n)}function b9n(e){return!e.g&&(e.g=new J6),!e.g.d&&(e.g.d=new _Se(e)),e.g.d}function g9n(e){return!e.g&&(e.g=new J6),!e.g.b&&(e.g.b=new ISe(e)),e.g.b}function ZC(e){return!e.g&&(e.g=new J6),!e.g.c&&(e.g.c=new PSe(e)),e.g.c}function w9n(e){return!e.g&&(e.g=new J6),!e.g.a&&(e.g.a=new LSe(e)),e.g.a}function p9n(e,n,t,i){return t&&(i=t.Oh(n,zi(t.Ah(),e.c.sk()),null,i)),i}function m9n(e,n,t,i){return t&&(i=t.Qh(n,zi(t.Ah(),e.c.sk()),null,i)),i}function dY(e,n,t,i){var r;return r=oe($t,ni,30,n+1,15,1),__n(r,e,n,t,i),r}function oe(e,n,t,i,r,c){var o;return o=hHe(r,i),r!=10&&z(B(e,c),n,t,r,o),o}function v9n(e,n,t){var i,r;for(r=new Y9(n,e),i=0;it||n=0?e.Ih(t,!0,!0):Gw(e,n,!0)}function eO(e,n){var t,i,r;return r=e.r,i=e.d,t=fS(e,n,!0),t.b!=r||t.a!=i}function $$e(e,n){return PMe(e.e,n)||tg(e.e,n,new PJe(n)),u(Pa(e.e,n),113)}function Ts(e,n,t,i){return _n(e),_n(n),_n(t),_n(i),new Bfe(e,n,new Eu)}function nO(e,n,t){var i,r;return r=(i=E8(e.b,n),i),r?Kz(oO(e,r),t):null}function L9n(e,n,t){var i,r,c;i=O1(e,t),r=null,i&&(r=D0e(i)),c=r,IJe(n,t,c)}function P9n(e,n,t){var i,r,c;i=O1(e,t),r=null,i&&(r=D0e(i)),c=r,IJe(n,t,c)}function os(e,n,t,i){this.$j(),this.a=n,this.b=e,this.c=new Lfe(this,n,t,i)}function pY(e,n,t,i,r,c){_ae.call(this,n,i,r,c),this.c=e,this.b=t}function tO(e,n,t,i,r,c){_ae.call(this,n,i,r,c),this.c=e,this.a=t}function ghe(e,n,t,i,r){zCe(this),this.b=e,this.d=n,this.f=t,this.g=i,this.c=r}function whe(e,n){N$.call(this,n.xd(),n.wd()&-16449),_n(e),this.a=e,this.c=n}function $9n(e,n){e.a.Le(n.d,e.b)>0&&(Te(e.c,new ofe(n.c,n.d,e.d)),e.b=n.d)}function mY(e){e.a=oe($t,ni,30,e.b+1,15,1),e.c=oe($t,ni,30,e.b,15,1),e.d=0}function R9n(e,n,t){var i;return i=Rze(e,n,t),e.b=new AB(i.c.length),Dbe(e,i)}function B9n(e){if(e.b<=0)throw $(new au);return--e.b,e.a-=e.c.c,ve(e.a)}function z9n(e){var n;if(!e.a)throw $(new s_e);return n=e.a,e.a=Bi(e.a),n}function R$e(e){var n;if(e.ll())for(n=e.i-1;n>=0;--n)K(e,n);return $ae(e)}function $4(e){var n;return Nt(e),X(e,204)?(n=u(e,204),n):new e9(e)}function F9n(e){for(;!e.a;)if(!ENe(e.c,new Hke(e)))return!1;return!0}function phe(e,n){if(e.g==null||n>=e.i)throw $(new jK(n,e.i));return e.g[n]}function B$e(e,n,t){if(t8(e,t),t!=null&&!e.dk(t))throw $(new bX);return t}function vY(e,n){return lO(n)!=10&&z(Us(n),n.Qm,n.__elementTypeId$,lO(n),e),e}function z$e(e,n){var t,i;return i=n/e.c.Pd().gc()|0,t=n%e.c.Pd().gc(),P4(e,i,t)}function J9(e,n,t,i){var r;i=(Aw(),i||Fme),r=e.slice(n,t),J0e(r,e,n,t,-n,i)}function Pl(e,n,t,i,r){return n<0?Gw(e,t,i):u(t,69).uk().wk(e,e.ei(),n,i,r)}function J9n(e,n){return ki(ne(re(T(e,(me(),hp)))),ne(re(T(n,hp))))}function F$e(){F$e=Y,gnn=It((H9(),z(B(dJ,1),ke,309,0,[lte,fte,ate,hte])))}function H9(){H9=Y,lte=new c$("All",0),fte=new ACe,ate=new RCe,hte=new MCe}function ws(){ws=Y,Th=new qX(ly,0),nb=new qX(F8,1),Uf=new qX(fy,2)}function J$e(){J$e=Y,Gz(),b7e=Ki,vhn=Dr,g7e=new Zn(Ki),yhn=new Zn(Dr)}function tB(){tB=Y,ofn=new g3,lfn=new eL,sfn=tkn((Xt(),jce),ofn,ab,lfn)}function H9n(e){tB(),u(e.mf((Xt(),Nm)),182).Ec((ps(),JD)),e.of(jce,null)}function G9n(e){return X(e,180)?""+u(e,180).a:e==null?null:su(e)}function q9n(e){return X(e,180)?""+u(e,180).a:e==null?null:su(e)}function mhe(e){var n,t;if(!e.b)return null;for(t=e.b;n=t.a[0];)t=n;return t}function H$e(e){var n,t;if(!e.b)return null;for(t=e.b;n=t.a[1];)t=n;return t}function iO(e){var n;for(n=e.p+1;n=0?uz(e,t,!0,!0):Gw(e,n,!0)}function Q9n(e,n){k4(u(u(e.f,26).mf((Xt(),Kx)),102))&&UFe(iae(u(e.f,26)),n)}function pRe(e,n){Os(e,n==null||X$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function mRe(e,n){Ns(e,n==null||X$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function vRe(e,n){Iw(e,n==null||X$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function yRe(e,n){Dw(e,n==null||X$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function kRe(e){(this.q?this.q:(jn(),jn(),i1)).zc(e.q?e.q:(jn(),jn(),i1))}function SY(e,n,t){var i;return i=e.g[n],Yj(e,n,e.Xi(n,t)),e.Pi(n,t,i),e.Li(),i}function sB(e,n){var t;return t=e.bd(n),t>=0?(e.ed(t),!0):!1}function xY(e){var n;return e.d!=e.r&&(n=ff(e),e.e=!!n&&n.jk()==ZZe,e.d=n),e.e}function AY(e,n){var t;for(Nt(e),Nt(n),t=!1;n.Ob();)t=t|e.Ec(n.Pb());return t}function Pa(e,n){var t;return t=u(Bn(e.e,n),393),t?(VCe(e,t),t.e):null}function jRe(e){var n,t;return n=e/60|0,t=e%60,t==0?""+n:""+n+":"+(""+t)}function ou(e,n){var t,i;return R0(e),i=new the(n,e.a),t=new kNe(i),new wn(e,t)}function T2(e,n){var t=e.a[n],i=(QY(),ite)[typeof t];return i?i(t):F1e(typeof t)}function W9n(e,n){var t,i,r;r=n.c.i,t=u(Bn(e.f,r),60),i=t.d.c-t.e.c,Zhe(n.a,i,0)}function Kh(e,n,t){var i,r;for(i=10,r=0;r=0;)++n[0]}function MRe(e,n,t,i){fi(),aw.call(this,26),this.c=e,this.a=n,this.d=t,this.b=i}function L1(e,n,t,i,r,c,o){DY.call(this,n,i,r,c,o),this.c=e,this.b=t}function TRe(e){this.g=e,this.f=new Ce,this.a=k.Math.min(this.g.c.c,this.g.d.c)}function kE(){kE=Y,Qtn=new xp,Wtn=new Ap,Vtn=new Mp,Ytn=new Tp,Ztn=new xl}function lB(){lB=Y,vte=new fse("EADES",0),pJ=new fse("FRUCHTERMAN_REINGOLD",1)}function fO(){fO=Y,XJ=new bse("READING_DIRECTION",0),Eve=new bse("ROTATION",1)}function CRe(){CRe=Y,Ain=It((z2(),z(B(xin,1),ke,371,0,[ZN,GJ,qJ,HJ,JJ])))}function ORe(){ORe=Y,Hun=It((HE(),z(B(_4e,1),ke,328,0,[I4e,Wie,Zie,jx,Ex])))}function NRe(){NRe=Y,Win=It((Xs(),z(B(e5e,1),ke,165,0,[sD,fx,V1,ax,yg])))}function DRe(){DRe=Y,_sn=It((vz(),z(B(Isn,1),ke,364,0,[Cre,Are,Ore,Mre,Tre])))}function IRe(){IRe=Y,Lln=It((nS(),z(B(_ln,1),ke,369,0,[Uv,$y,Jx,Fx,MD])))}function _Re(){_Re=Y,Fln=It((JO(),z(B(F6e,1),ke,330,0,[R6e,nce,z6e,tce,B6e])))}function LRe(){LRe=Y,Htn=It((zr(),z(B(p3e,1),ke,363,0,[Kf,r1,eo,no,Pc])))}function PRe(){PRe=Y,qfn=It((vr(),z(B(Vx,1),ke,86,0,[eh,ru,Zc,Za,Vl])))}function $Re(){$Re=Y,ffn=It((ph(),z(B(Qa,1),ke,160,0,[Mn,fr,Sa,Kd,Q1])))}function RRe(){RRe=Y,nan=It((nv(),z(B(Zx,1),ke,257,0,[db,FD,g8e,Wx,w8e])))}function BRe(){BRe=Y,ran=It((De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])))}function zRe(e){var n;return n=u(T(e,(me(),fp)),317),n?n.a==e:!1}function FRe(e){var n;return n=u(T(e,(me(),fp)),317),n?n.i==e:!1}function JRe(e,n){return _n(n),Dfe(e),e.d.Ob()?(n.Ad(e.d.Pb()),!0):!1}function fB(e){return ao(e,oi)>0?oi:ao(e,Xr)<0?Xr:Rt(e)}function o8n(e,n){var t;return t=$w(e.e.c,n.e.c),t==0?ki(e.e.d,n.e.d):t}function TY(e,n){var t;return t=u(Bn(e.a,n),150),t||(t=new Mt,ei(e.a,n,t)),t}function Rf(e,n,t){var i;if(n==null)throw $(new e4);return i=O1(e,n),D6n(e,n,t),i}function s8n(e,n){var t,i;for(i=n.c,t=i+1;t<=n.f;t++)e.a[t]>e.a[i]&&(i=t);return i}function l8n(e,n,t){var i;return i=e.a.e[u(n.a,9).p]-e.a.e[u(t.a,9).p],sc(LC(i))}function f8n(e,n,t){var i,r;for(r=new L(t);r.a0?n-1:n,wAe(ugn(aBe(dfe(new i4,t),e.n),e.j),e.k)}function p8n(e,n,t,i){var r;e.j=-1,nbe(e,I0e(e,n,t),(Cc(),r=u(n,69).tk(),r.vl(i)))}function XRe(e,n,t,i,r,c){var o;o=lY(i),lc(o,r),Gr(o,c),gn(e.a,i,new Y$(o,n,t.f))}function aB(e,n){var t;return R0(e),t=new e_e(e,e.a.xd(),e.a.wd()|4,n),new wn(e,t)}function m8n(e,n){var t,i;return t=u(L2(e.d,n),18),t?(i=n,e.e.pc(i,t)):null}function An(e,n){var t;return t=(e.i==null&&vh(e),e.i),n>=0&&n=-.01&&e.a<=Ga&&(e.a=0),e.b>=-.01&&e.b<=Ga&&(e.b=0),e}function q3(e){x8();var n,t;for(t=Fpe,n=0;nt&&(t=e[n]);return t}function v8n(e){var n;return n=ne(re(T(e,(Ne(),Gd)))),n<0&&(n=0,he(e,Gd,n)),n}function y8n(e,n){k4(u(T(u(e.e,9),(Ne(),Wi)),102))&&(jn(),Cr(u(e.e,9).j,n))}function hB(e,n){var t,i;for(i=e.Jc();i.Ob();)t=u(i.Pb(),70),he(t,(me(),Cy),n)}function k8n(e,n){var t,i,r;for(i=n.a.jd(),t=u(n.a.kd(),18).gc(),r=0;re||e>n)throw $(new Ioe("fromIndex: 0, toIndex: "+e+Xge+n))}function QRe(e,n){ji(e,(Yh(),Hre),n.f),ji(e,sln,n.e),ji(e,Jre,n.d),ji(e,oln,n.c)}function Ao(e,n){var t,i,r,c;for(_n(n),i=e.c,r=0,c=i.length;r0&&(e.a/=n,e.b/=n),e}function WRe(e,n,t){var i,r;i=n;do r=ne(e.p[i.p])+t,e.p[i.p]=r,i=e.a[i.p];while(i!=n)}function sl(e){var n;return e.w?e.w:(n=wyn(e),n&&!n.Sh()&&(e.w=n),n)}function Ahe(e,n){return Oa(),Bf(X0),k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)}function C8n(e){var n;return e==null?null:(n=u(e,195),pMn(n,n.length))}function K(e,n){if(e.g==null||n>=e.i)throw $(new jK(n,e.i));return e.Ui(n,e.g[n])}function ga(){ga=Y,Ou=new GX("BEGIN",0),No=new GX(F8,1),Nu=new GX("END",2)}function $a(){$a=Y,F7=new wK(F8,0),_m=new wK("HEAD",1),J7=new wK("TAIL",2)}function R4(){R4=Y,Csn=wh(wh(wh(Oj(new or,(Y4(),Ox)),(cS(),are)),oye),aye)}function P1(){P1=Y,Nsn=wh(wh(wh(Oj(new or,(Y4(),Dx)),(cS(),lye)),rye),sye)}function U3(e,n){return agn(AE(e,n,Rt(ac(Zh,Uh(Rt(ac(n==null?0:Oi(n),e1)),15)))))}function Mhe(e,n){return Oa(),Bf(X0),k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)}function q9(e,n){var t,i;i=e.a,t=fjn(e,n,null),i!=n&&!e.e&&(t=D8(e,n,t)),t&&t.mj()}function O8n(e,n){var t;return t=Nr(pc(u(Bn(e.g,n),8)),Use(u(Bn(e.f,n),460).b)),t}function ZRe(e,n,t){var i=function(){return e.apply(i,arguments)};return n.apply(i,t),i}function B4(e){var n;return nE(e==null||Array.isArray(e)&&(n=lO(e),!(n>=14&&n<=16))),e}function The(e){e.b=(ws(),nb),e.f=(Uo(),tb),e.d=(ll(2,Q2),new xo(2)),e.e=new Kr}function dB(e){this.b=(Nt(e),new bs(e)),this.a=new Ce,this.d=new Ce,this.e=new Kr}function eBe(e){return R0(e),E4(!0,"n may not be negative"),new wn(e,new vBe(e.a))}function N8n(e,n){jn();var t,i;for(i=new Ce,t=0;t0?u(Le(t.a,i-1),9):null}function Bf(e){if(!(e>=0))throw $(new Gn("tolerance ("+e+") must be >= 0"));return e}function EE(){return uce||(uce=new DXe,H4(uce,z(B(yy,1),On,148,0,[new TT]))),uce}function wB(){wB=Y,Y4e=new cK("NO",0),sre=new cK(bwe,1),V4e=new cK("LOOK_BACK",2)}function Nc(){Nc=Y,xx=new nK(vS,0),ys=new nK("INPUT",1),Do=new nK("OUTPUT",2)}function pB(){pB=Y,vve=new KX("ARD",0),UJ=new KX("MSD",1),Kte=new KX("MANUAL",2)}function R8n(){return KO(),z(B(jve,1),ke,267,0,[Qte,kve,Zte,eie,Wte,nie,nD,Yte,Vte])}function B8n(){return YO(),z(B(O4e,1),ke,268,0,[Kie,M4e,T4e,Uie,A4e,C4e,EH,qie,Xie])}function z8n(){return _s(),z(B(k8e,1),ke,266,0,[q7,XD,lG,iA,fG,hG,aG,Cce,UD])}function F8n(){yMe();for(var e=Xne,n=0;nt)throw $(new b2(n,t));return new Xle(e,n)}function mB(e){var n,t;for(t=e.c.Bc().Jc();t.Ob();)n=u(t.Pb(),18),n.$b();e.c.$b(),e.d=0}function J8n(e){var n,t,i,r;for(t=e.a,i=0,r=t.length;i=0),xEn(e.d,e.c)<0&&(e.a=e.a-1&e.d.a.length-1,e.b=e.d.c),e.c=-1}function vBe(e){N$.call(this,e.yd(64)?Gse(0,lf(e.xd(),1)):hN,e.wd()),this.b=1,this.a=e}function yBe(){cle.call(this),this.n=-1,this.g=null,this.i=null,this.j=null,this.Bb|=qf}function kBe(e,n,t,i){this.$j(),this.a=n,this.b=e,this.c=null,this.c=new pNe(this,n,t,i)}function DY(e,n,t,i,r){this.d=e,this.n=n,this.g=t,this.o=i,this.p=-1,r||(this.o=-2-i-1)}function jBe(e){Woe(),this.g=new pt,this.f=new pt,this.b=new pt,this.c=new Tw,this.i=e}function $he(){this.f=new Kr,this.d=new moe,this.c=new Kr,this.a=new Ce,this.b=new Ce}function G8n(e){var n,t;for(t=new L(mHe(e));t.a=0}function Rhe(){Rhe=Y,oon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function EBe(){EBe=Y,son=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function Bhe(){Bhe=Y,lon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function SBe(){SBe=Y,fon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function xBe(){xBe=Y,aon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function ABe(){ABe=Y,hon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function MBe(){MBe=Y,gon=Eo(qt(qt(new or,(zr(),eo),(Ur(),DJ)),no,MJ),Pc,NJ)}function TBe(){TBe=Y,enn=z(B($t,1),ni,30,15,[0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15])}function zhe(e,n){var t;t=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,0,t,e.b))}function Fhe(e,n){var t;t=e.c,e.c=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,1,t,e.c))}function IY(e,n){var t;t=e.c,e.c=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,4,t,e.c))}function Jhe(e,n){var t;t=e.c,e.c=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,1,t,e.c))}function Hhe(e,n){var t;t=e.d,e.d=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,1,t,e.d))}function X9(e,n){var t;t=e.k,e.k=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,2,t,e.k))}function _Y(e,n){var t;t=e.D,e.D=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,2,t,e.D))}function kB(e,n){var t;t=e.f,e.f=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,8,t,e.f))}function jB(e,n){var t;t=e.i,e.i=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,7,t,e.i))}function Ghe(e,n){var t;t=e.a,e.a=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,8,t,e.a))}function qhe(e,n){var t;t=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,0,t,e.b))}function X8n(e,n,t){var i;e.b=n,e.a=t,i=(e.a&512)==512?new _xe:new gT,e.c=SDn(i,e.b,e.a)}function CBe(e,n){return J1(e.e,n)?(Cc(),xY(n)?new rR(n,e):new pC(n,e)):new nCe(n,e)}function K8n(e){var n,t;return 0>e?new Yoe:(n=e+1,t=new JPe(n,e),new Ale(null,t))}function V8n(e,n){jn();var t;return t=new l4(1),$r(e)?Kc(t,e,n):Ko(t.f,e,n),new fX(t)}function Y8n(e,n){var t;t=new Ug,u(n.b,68),u(n.b,68),u(n.b,68),Ao(n.a,new ife(e,t,n))}function OBe(e,n){var t;return X(n,8)?(t=u(n,8),e.a==t.a&&e.b==t.b):!1}function Q8n(e){var n;return n=T(e,(me(),wi)),X(n,174)?QFe(u(n,174)):null}function NBe(e){var n;return e=k.Math.max(e,2),n=b1e(e),e>n?(n<<=1,n>0?n:bS):n}function LY(e){switch(ile(e.e!=3),e.e){case 2:return!1;case 0:return!0}return n9n(e)}function Uhe(e){var n;return e.b==null?(kd(),kd(),nI):(n=e.sl()?e.rl():e.ql(),n)}function DBe(e,n){var t,i;for(i=n.vc().Jc();i.Ob();)t=u(i.Pb(),45),RO(e,t.jd(),t.kd())}function Xhe(e,n){var t;t=e.d,e.d=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,11,t,e.d))}function EB(e,n){var t;t=e.j,e.j=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,13,t,e.j))}function Khe(e,n){var t;t=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,21,t,e.b))}function Vhe(e,n){e.r>0&&e.c0&&e.g!=0&&Vhe(e.i,n/e.r*e.i.d))}function IBe(e,n,t){var i,r,c;for(c=e.a.length-1,r=e.b,i=0;i0?1:0:(!e.c&&(e.c=qC(Pu(e.f))),e.c).e}function HBe(e,n){n?e.B==null&&(e.B=e.D,e.D=null):e.B!=null&&(e.D=e.B,e.B=null)}function t7n(e,n){n.Tg(vQe,1),Zi(ou(new wn(null,new pn(e.b,16)),new Vg),new vI),n.Ug()}function zY(e,n,t,i,r,c){var o;this.c=e,o=new Ce,_de(e,o,n,e.b,t,i,r,c),this.a=new qr(o,0)}function ir(e,n,t,i,r,c,o,l,f,h,b,p,y){return cqe(e,n,t,i,r,c,o,l,f,h,b,p,y),pQ(e,!1),e}function i7n(e,n){typeof window===sN&&typeof window.$gwt===sN&&(window.$gwt[e]=n)}function r7n(e,n,t){var i,r,c;for(i=0,r=0;r>>31;i!=0&&(e[t]=i)}function c7n(e,n,t){t.Tg("DFS Treeifying phase",1),gEn(e,n),UNn(e,n),e.a=null,e.b=null,t.Ug()}function u7n(e,n){var t;n.Tg("General Compactor",1),t=Qjn(u(ye(e,(J0(),Ire)),386)),t.Bg(e)}function o7n(e,n){var t,i;return t=u(ye(e,(J0(),FH)),15),i=u(ye(n,FH),15),oo(t.a,i.a)}function Zhe(e,n,t){var i,r;for(r=St(e,0);r.b!=r.d.c;)i=u(jt(r),8),i.a+=n,i.b+=t;return e}function s7n(e,n,t,i){var r;r=new r4,Hb(r,"x",pz(e,n,i.a)),Hb(r,"y",mz(e,n,i.b)),T4(t,r)}function l7n(e,n,t,i){var r;r=new r4,Hb(r,"x",pz(e,n,i.a)),Hb(r,"y",mz(e,n,i.b)),T4(t,r)}function f7n(){return G0(),z(B(B4e,1),ke,243,0,[AH,gD,wD,P4e,$4e,L4e,R4e,MH,D7,Sx])}function a7n(){return Dc(),z(B(lie,1),ke,261,0,[QJ,Kl,cx,WJ,S7,Nv,ux,j7,E7,ZJ])}function FY(){FY=Y,sA=new Cxe,zce=z(B(ns,1),jv,179,0,[]),Qan=z(B(yf,1),ime,62,0,[])}function z4(){z4=Y,Lte=new Li("edgelabelcenterednessanalysis.includelabel",(Pn(),eb))}function GBe(e,n){return ne(re(Js(AO(So(new wn(null,new pn(e.c.b,16)),new Yje(e)),n))))}function e1e(e,n){return ne(re(Js(AO(So(new wn(null,new pn(e.c.b,16)),new Vje(e)),n))))}function Oi(e){return $r(e)?Od(e):s2(e)?b4(e):o2(e)?GOe(e):Cfe(e)?e.Hb():Sfe(e)?vw(e):aae(e)}function qBe(e,n){return Oa(),Bf(Ga),k.Math.abs(0-n)<=Ga||n==0||isNaN(0)&&isNaN(n)?0:e/n}function h7n(e,n){return Z9(),e==op&&n==om||e==op&&n==xv||e==sm&&n==xv||e==sm&&n==om}function d7n(e,n){return Z9(),e==op&&n==sm||e==sm&&n==op||e==xv&&n==om||e==om&&n==xv}function ss(){ss=Y,A3e=new w5,S3e=new s0,x3e=new Ih,E3e=new ck,M3e=new CA,T3e=new p5}function b7n(e){var n;return n=BR(e),Hj(n.a,0)?(YP(),YP(),dnn):(YP(),new EOe(n.b))}function JY(e){var n;return n=Dae(e),Hj(n.a,0)?(i2(),i2(),ste):(i2(),new PK(n.b))}function HY(e){var n;return n=Dae(e),Hj(n.a,0)?(i2(),i2(),ste):(i2(),new PK(n.c))}function g7n(e){return e.b.c.i.k==(zn(),wr)?u(T(e.b.c.i,(me(),wi)),12):e.b.c}function UBe(e){return e.b.d.i.k==(zn(),wr)?u(T(e.b.d.i,(me(),wi)),12):e.b.d}function XBe(e){switch(e.g){case 2:return De(),Kn;case 4:return De(),et;default:return e}}function KBe(e){switch(e.g){case 1:return De(),bt;case 3:return De(),Xn;default:return e}}function w7n(e,n){var t;return t=m0e(e),V0e(new je(t.c,t.d),new je(t.b,t.a),e.Kf(),n,e.$f())}function p7n(e,n){n.Tg(vQe,1),ode(jgn(new TP((Aj(),new DV(e,!1,!1,new rk))))),n.Ug()}function n1e(){n1e=Y,won=wh(uCe(qt(qt(new or,(zr(),eo),(Ur(),DJ)),no,MJ),Pc),NJ)}function VBe(){VBe=Y,yon=wh(uCe(qt(qt(new or,(zr(),eo),(Ur(),DJ)),no,MJ),Pc),NJ)}function YBe(e,n,t){this.g=e,this.d=n,this.e=t,this.a=new Ce,oCn(this),jn(),Cr(this.a,null)}function Bl(e,n,t,i,r,c,o){Ot.call(this,e,n),this.d=t,this.e=i,this.c=r,this.b=c,this.a=$f(o)}function t1e(e){this.i=e.gc(),this.i>0&&(this.g=this.$i(this.i+(this.i/8|0)+1),e.Oc(this.g))}function xE(e,n){var t,i;for(_n(n),i=n.vc().Jc();i.Ob();)t=u(i.Pb(),45),e.yc(t.jd(),t.kd())}function m7n(e,n,t){var i;for(i=t.Jc();i.Ob();)if(!HR(e,n,i.Pb()))return!1;return!0}function AE(e,n,t){var i;for(i=e.b[t&e.f];i;i=i.b)if(t==i.a&&T1(n,i.g))return i;return null}function ME(e,n,t){var i;for(i=e.c[t&e.f];i;i=i.d)if(t==i.f&&T1(n,i.i))return i;return null}function v7n(e,n){var t;for(Nt(n);e.Ob();)if(t=e.Pb(),!u1e(u(t,9)))return!1;return!0}function y7n(e,n,t,i,r){var c;return t&&(c=zi(n.Ah(),e.c),r=t.Oh(n,-1-(c==-1?i:c),null,r)),r}function k7n(e,n,t,i,r){var c;return t&&(c=zi(n.Ah(),e.c),r=t.Qh(n,-1-(c==-1?i:c),null,r)),r}function QBe(e){var n;if(e.b==-2){if(e.e==0)n=-1;else for(n=0;e.a[n]==0;n++);e.b=n}return e.b}function j7n(e){var n,t,i;return e.j==(De(),Xn)&&(n=Wqe(e),t=cs(n,et),i=cs(n,Kn),i||i&&t)}function E7n(e){var n,t,i;for(i=0,t=new L(e.b);t.ar&&n.ac&&n.br?t=r:Yn(n,t+1),e.a=of(e.a,0,n)+(""+i)+qfe(e.a,t)}function WBe(e,n,t,i){X(e.Cb,184)&&(u(e.Cb,184).tb=null),Mo(e,t),n&&ECn(e,n),i&&e.el(!0)}function A7n(e,n){var t,i;for(i=new L(n.b);i.a1||e.Ob())return++e.a,e.g=0,n=e.i,e.Ob(),n;throw $(new au)}function _7n(e,n){var t,i;for(i=new L(n);i.a>22),r=e.h+n.h+(i>>22),_o(t&Ls,i&Ls,r&G1)}function xze(e,n){var t,i,r;return t=e.l-n.l,i=e.m-n.m+(t>>22),r=e.h-n.h+(i>>22),_o(t&Ls,i&Ls,r&G1)}function YY(e){var n,t,i,r;for(r=new Ce,i=e.Jc();i.Ob();)t=u(i.Pb(),26),n=q2(t),Er(r,n);return r}function Z7n(e){var n;$d(e,!0),n=Rd,bi(e,(Ne(),C7))&&(n+=u(T(e,C7),15).a),he(e,C7,ve(n))}function Aze(e,n,t){var i;Hu(e.a),Ao(t.i,new VEe(e)),i=new _$(u(Bn(e.a,n.b),68)),EJe(e,i,n),t.f=i}function l1e(e){var n,t;return t=(v0(),n=new yo,n),e&&Et((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),t),t}function F4(e,n){var t,i;if(i=0,e<64&&e<=n)for(n=n<64?n:63,t=e;t<=n;t++)i=hh(i,Gh(1,t));return i}function ekn(e,n){var t,i;for(CR(n,"predicate"),i=0;e.Ob();i++)if(t=e.Pb(),n.Lb(t))return i;return-1}function f1e(e,n){if(n===0){!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),e.o.c.$b();return}wW(e,n)}function Mze(e){switch(e.g){case 1:return hb;case 2:return s1;case 3:return BD;default:return zD}}function a1e(e){jn();var n,t,i;for(i=0,t=e.Jc();t.Ob();)n=t.Pb(),i=i+(n!=null?Oi(n):0),i=i|0;return i}function nkn(e){var n;return n=new hn,n.a=e,n.b=okn(e),n.c=oe(ze,Se,2,2,6,1),n.c[0]=JBe(e),n.c[1]=JBe(e),n}function LB(){LB=Y,Pte=new f$(ma,0),zJ=new f$(jQe,1),FJ=new f$(EQe,2),WN=new f$("BOTH",3)}function Z9(){Z9=Y,op=new s$("Q1",0),sm=new s$("Q4",1),om=new s$("Q2",2),xv=new s$("Q3",3)}function _0(){_0=Y,iD=new WX("ONLY_WITHIN_GROUP",0),Bve=new WX(ZZ,1),dm=new WX("ENFORCED",2)}function Wb(){Wb=Y,tie=new YX(ma,0),k7=new YX("INCOMING_ONLY",1),Ov=new YX("OUTGOING_ONLY",2)}function J4(){J4=Y,ufn=new $M,cfn=new Q_}function QY(){QY=Y,ite={boolean:pgn,number:Cbn,string:Obn,object:sqe,function:sqe,undefined:sbn}}function Tze(){Tze=Y,Gun=It((G0(),z(B(B4e,1),ke,243,0,[AH,gD,wD,P4e,$4e,L4e,R4e,MH,D7,Sx])))}function Cze(){Cze=Y,qin=It((Dc(),z(B(lie,1),ke,261,0,[QJ,Kl,cx,WJ,S7,Nv,ux,j7,E7,ZJ])))}function tkn(e,n,t,i){return new rse(z(B(wg,1),eF,45,0,[(GQ(e,n),new bw(e,n)),(GQ(t,i),new bw(t,i))]))}function ikn(e,n){var t,i;return t=u(u(Bn(e.g,n.a),49).a,68),i=u(u(Bn(e.g,n.b),49).a,68),mKe(t,i)}function h1e(e,n,t){var i;if(i=e.gc(),n>i)throw $(new b2(n,i));return e.Qi()&&(t=z_e(e,t)),e.Ci(n,t)}function Oze(e){var n,t,i;return t=e.n,i=e.o,n=e.d,new Lf(t.a-n.b,t.b-n.d,i.a+(n.b+n.c),i.b+(n.d+n.a))}function rkn(e,n){return!e||!n||e==n?!1:$w(e.b.c,n.b.c+n.b.b)<0&&$w(n.b.c,e.b.c+e.b.b)<0}function WY(e,n,t){return e>=128?!1:e<64?Gj(Rr(Gh(1,e),t),0):Gj(Rr(Gh(1,e-64),n),0)}function kO(e,n,t){switch(t.g){case 2:e.b=n;break;case 1:e.c=n;break;case 4:e.d=n;break;case 3:e.a=n}}function jO(e,n,t){return t==null?(!e.q&&(e.q=new pt),L4(e.q,n)):(!e.q&&(e.q=new pt),ei(e.q,n,t)),e}function he(e,n,t){return t==null?(!e.q&&(e.q=new pt),L4(e.q,n)):(!e.q&&(e.q=new pt),ei(e.q,n,t)),e}function Nze(e){var n,t;return t=new YR,$u(t,e),he(t,(D0(),jy),e),n=new pt,W_n(e,t,n),C$n(e,t,n),t}function ckn(e){x8();var n,t,i;for(t=oe(Lr,Se,8,2,0,1),i=0,n=0;n<2;n++)i+=.5,t[n]=RSn(i,e);return t}function Dze(e,n){var t,i,r,c;for(t=!1,i=e.a[n].length,c=0;ce.f,t=e.u+e.e[e.o.p]*e.d>e.f*e.s*e.d,n||t}function d1e(e){var n;return(!e.c||(e.Bb&1)==0&&(e.c.Db&64)!=0)&&(n=ff(e),X(n,88)&&(e.c=u(n,29))),e.c}function b1e(e){var n;if(e<0)return Xr;if(e==0)return 0;for(n=bS;(n&e)==0;n>>=1);return n}function okn(e){var n;return e==0?"Etc/GMT":(e<0?(e=-e,n="Etc/GMT-"):n="Etc/GMT+",n+jRe(e))}function _ze(e){var n,t;return t=UO(e.h),t==32?(n=UO(e.m),n==32?UO(e.l)+32:n+20-10):t-12}function ZY(e){var n,t,i;n=~e.l+1&Ls,t=~e.m+(n==0?1:0)&Ls,i=~e.h+(n==0&&t==0?1:0)&G1,e.l=n,e.m=t,e.h=i}function CE(e){var n;return n=e.a[e.b],n==null?null:(tr(e.a,e.b,null),e.b=e.b+1&e.a.length-1,n)}function g1e(){this.o=null,this.k=null,this.j=null,this.d=null,this.b=null,this.n=null,this.a=null}function w1e(e,n){this.c=e,this.d=n,this.b=this.d/this.c.c.Pd().gc()|0,this.a=this.d%this.c.c.Pd().gc()}function Lze(e,n){this.b=e,N3.call(this,(u(K(we((x0(),Rn).o),10),19),n.i),n.g),this.a=(FY(),zce)}function p1e(e,n,t){this.q=new k.Date,this.q.setFullYear(e+K0,n,t),this.q.setHours(0,0,0,0),oS(this,0)}function Pze(e,n,t){var i,r;return i=new wY(n,t),r=new st,e.b=nXe(e,e.b,i,r),r.b||++e.c,e.b.b=!1,r.d}function m1e(e,n){jn();var t,i,r,c,o;for(o=!1,i=n,r=0,c=i.length;ro||i+r>c)throw $(new soe)}function $ze(e,n,t){var i,r,c,o;for(o=LE(n,t),c=0,r=o.Jc();r.Ob();)i=u(r.Pb(),12),ei(e.c,i,ve(c++))}function L0(e){var n,t;for(t=new L(e.a.b);t.a=0,"Negative initial capacity"),IC(n>=0,"Non-positive load factor"),Hu(this)}function Jze(e,n){var t;for(t=0;t1||n>=0&&e.b<3)}function wkn(){fi();var e;return Uce||(e=dpn(q0("M",!0)),e=aR(q0("M",!1),e),Uce=e,Uce)}function qze(e){if(e.g===0)return new P6;throw $(new Gn($F+(e.f!=null?e.f:""+e.g)))}function Uze(e){if(e.g===0)return new Y_;throw $(new Gn($F+(e.f!=null?e.f:""+e.g)))}function E1e(e,n,t){if(n===0){!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),CB(e.o,t);return}vW(e,n,t)}function nQ(e,n,t){this.g=e,this.e=new Kr,this.f=new Kr,this.d=new Si,this.b=new Si,this.a=n,this.c=t}function tQ(e,n,t,i){this.b=new Ce,this.n=new Ce,this.i=i,this.j=t,this.s=e,this.t=n,this.r=0,this.d=0}function Xze(e,n,t,i){this.b=new pt,this.g=new pt,this.d=(IE(),SH),this.c=e,this.e=n,this.d=t,this.a=i}function t8(e,n){if(!e.Ji()&&n==null)throw $(new Gn("The 'no null' constraint is violated"));return n}function S1e(e){switch(e.g){case 1:return GQe;default:case 2:return 0;case 3:return qQe;case 4:return zpe}}function pkn(e){return Te(e.c,(J4(),ufn)),Ahe(e.a,ne(re(_e((EQ(),jH)))))?new VM:new nSe(e)}function mkn(e){for(;!e.d||!e.d.Ob();)if(e.b&&!kj(e.b))e.d=u(A4(e.b),50);else return null;return e.d}function Od(e){var n,t;for(n=0,t=0;ti?1:0}function Kze(e,n){var t,i,r;for(r=e.b;r;){if(t=e.a.Le(n,r.d),t==0)return r;i=t<0?0:1,r=r.a[i]}return null}function iQ(e,n){var t;return n===e?!0:X(n,229)?(t=u(n,229),di(e.Zb(),t.Zb())):!1}function x1e(e,n){return BUe(e,n)?(gn(e.b,u(T(n,(me(),K1)),22),n),Vt(e.a,n),!0):!1}function kkn(e,n){return bi(e,(me(),Ci))&&bi(n,Ci)?u(T(n,Ci),15).a-u(T(e,Ci),15).a:0}function jkn(e,n){return bi(e,(me(),Ci))&&bi(n,Ci)?u(T(e,Ci),15).a-u(T(n,Ci),15).a:0}function Vze(e){return Ka?oe(wnn,NYe,567,0,0,1):u(Ra(e.a,oe(wnn,NYe,567,e.a.c.length,0,1)),840)}function Us(e){return $r(e)?ze:s2(e)?gr:o2(e)?Yi:Cfe(e)||Sfe(e)?e.Pm:e.Pm||Array.isArray(e)&&B(Ken,1)||Ken}function W3(e,n,t){var i,r;return r=(i=new vX,i),Fc(r,n,t),Et((!e.q&&(e.q=new pe(yf,e,11,10)),e.q),r),r}function rQ(e){var n,t,i,r;for(r=Ign(Man,e),t=r.length,i=oe(ze,Se,2,t,6,1),n=0;n=e.b.c.length||(A1e(e,2*n+1),t=2*n+2,t0&&(n.Ad(t),t.i&&YEn(t))}function M1e(e,n,t){var i;for(i=t-1;i>=0&&e[i]===n[i];i--);return i<0?0:JX(Rr(e[i],Ic),Rr(n[i],Ic))?-1:1}function Skn(e,n){var t;return!e||e==n||!bi(n,(me(),ap))?!1:(t=u(T(n,(me(),ap)),9),t!=e)}function cQ(e){switch(e.i){case 2:return!0;case 1:return!1;case-1:++e.c;default:return e.Yl()}}function Yze(e,n,t){return e.d[n.p][t.p]||(mSn(e,n,t),e.d[n.p][t.p]=!0,e.d[t.p][n.p]=!0),e.a[n.p][t.p]}function Qze(e,n,t){var i,r;this.g=e,this.c=n,this.a=this,this.d=this,r=NBe(t),i=oe(Uen,dN,227,r,0,1),this.b=i}function xkn(e,n){var t,i;for(i=e.Zb().Bc().Jc();i.Ob();)if(t=u(i.Pb(),18),t.Gc(n))return!0;return!1}function Wze(e,n,t){var i,r,c,o;for(_n(t),o=!1,c=e.dd(n),r=t.Jc();r.Ob();)i=r.Pb(),c.Rb(i),o=!0;return o}function uQ(e,n){var t,i;return i=u(Un(e.a,4),129),t=oe(Rce,Ine,415,n,0,1),i!=null&&Wu(i,0,t,0,i.length),t}function Zze(e,n){var t;return t=new DW((e.f&256)!=0,e.i,e.a,e.d,(e.f&16)!=0,e.j,e.g,n),e.e!=null||(t.c=e),t}function Akn(e,n){var t;return e===n?!0:X(n,92)?(t=u(n,92),C0e($b(e),t.vc())):!1}function eFe(e,n,t){var i,r;for(r=t.Jc();r.Ob();)if(i=u(r.Pb(),45),e.ze(n,i.kd()))return!0;return!1}function PB(){PB=Y,Dce=new x$("ELK",0),D8e=new x$("JSON",1),N8e=new x$("DOT",2),I8e=new x$("SVG",3)}function OE(){OE=Y,Sre=new p$(ZZ,0),RH=new p$(KQe,1),Ere=new p$("FAN",2),jre=new p$("CONSTRAINT",3)}function NE(){NE=Y,bye=new sK(ma,0),hre=new sK("MIDDLE_TO_MIDDLE",1),yD=new sK("AVOID_OVERLAP",2)}function EO(){EO=Y,zH=new lK(ma,0),Fye=new lK("RADIAL_COMPACTION",1),Jye=new lK("WEDGE_COMPACTION",2)}function DE(){DE=Y,ure=new iK("STACKED",0),cre=new iK("REVERSE_STACKED",1),pD=new iK("SEQUENCED",2)}function zl(){zl=Y,Kme=new HX("CONCURRENT",0),Yo=new HX("IDENTITY_FINISH",1),Vme=new HX("UNORDERED",2)}function B1(){B1=Y,oG=new pK(L2e,0),Yd=new pK("INCLUDE_CHILDREN",1),Qx=new pK("SEPARATE_CHILDREN",2)}function $B(){$B=Y,d8e=new pw(15),Yfn=new Vr((Xt(),o1),d8e),Yx=Fy,l8e=kfn,f8e=Tg,h8e=Yv,a8e=Om}function oQ(){oQ=Y,Ate=I_e(z(B(Vx,1),ke,86,0,[(vr(),Zc),ru])),Mte=I_e(z(B(Vx,1),ke,86,0,[Vl,Za]))}function Mkn(e){var n,t,i;for(n=0,i=oe(Lr,Se,8,e.b,0,1),t=St(e,0);t.b!=t.d.c;)i[n++]=u(jt(t),8);return i}function sQ(e,n,t){var i,r,c;for(i=new Si,c=St(t,0);c.b!=c.d.c;)r=u(jt(c),8),Vt(i,new wc(r));Wze(e,n,i)}function Tkn(e,n){var t;t=_e((EQ(),jH))!=null&&n.Rg()!=null?ne(re(n.Rg()))/ne(re(_e(jH))):1,ei(e.b,n,t)}function Ckn(e,n){var t,i;return t=u(e.d.Ac(n),18),t?(i=e.e.hc(),i.Fc(t),e.e.d-=t.gc(),t.$b(),i):null}function T1e(e,n){var t,i;if(i=e.c[n],i!=0)for(e.c[n]=0,e.d-=i,t=n+1;t0)return C9(n-1,e.a.c.length),Ad(e.a,n-1);throw $(new ZSe)}function Okn(e,n,t){if(n<0)throw $(new jo(dWe+n));nn)throw $(new Gn(cF+e+DYe+n));if(e<0||n>t)throw $(new Ioe(cF+e+Yge+n+Xge+t))}function tFe(e){if(!e.a||(e.a.i&8)==0)throw $(new Uc("Enumeration class expected for layout option "+e.f))}function iFe(e){R_e.call(this,"The given string does not match the expected format for individual spacings.",e)}function rFe(e){switch(e.i){case-2:return!0;case-1:return!1;case 1:--e.c;default:return e.Zl()}}function Nd(e){switch(e.c){case 0:return rV(),mme;case 1:return new Z5(wqe(new s4(e)));default:return new Uxe(e)}}function cFe(e){switch(e.gc()){case 0:return rV(),mme;case 1:return new Z5(e.Jc().Pb());default:return new cse(e)}}function O1e(e){var n;return n=(!e.a&&(e.a=new pe(ed,e,9,5)),e.a),n.i!=0?Ngn(u(K(n,0),684)):null}function Nkn(e,n){var t;return t=mc(e,n),JX(YV(e,n),0)|C$(YV(e,t),0)?t:mc(hN,YV(Bb(t,63),1))}function N1e(e,n,t){var i,r;return S2(n,e.c.length),i=t.Nc(),r=i.length,r==0?!1:(sfe(e.c,n,i),!0)}function Dkn(e,n){var t,i;for(t=e.a.length-1;n!=e.b;)i=n-1&t,tr(e.a,n,e.a[i]),n=i;tr(e.a,e.b,null),e.b=e.b+1&t}function Ikn(e,n){var t,i;for(t=e.a.length-1,e.c=e.c-1&t;n!=e.c;)i=n+1&t,tr(e.a,n,e.a[i]),n=i;tr(e.a,e.c,null)}function i8(e,n){e.D==null&&e.B!=null&&(e.D=e.B,e.B=null),_Y(e,n==null?null:(_n(n),n)),e.C&&e.fl(null)}function Z3(e){return(e.c!=e.b.b||e.i!=e.g.b)&&(Qp(e.a.c,0),Er(e.a,e.b),Er(e.a,e.g),e.c=e.b.b,e.i=e.g.b),e.a}function _2(e){var n;++e.j,e.i==0?e.g=null:e.ir&&(QHe(n.q,r),i=t!=n.q.d)),i}function bFe(e,n){var t,i,r,c,o,l,f,h;return f=n.i,h=n.j,i=e.f,r=i.i,c=i.j,o=f-r,l=h-c,t=k.Math.sqrt(o*o+l*l),t}function _1e(e,n){var t,i;return i=nz(e),i||(t=(ZW(),gUe(n)),i=new HSe(t),Et(i.Cl(),e)),i}function SO(e,n){var t,i;return t=u(e.c.Ac(n),18),t?(i=e.hc(),i.Fc(t),e.d-=t.gc(),t.$b(),e.mc(i)):e.jc()}function Bkn(e){var n;if(!(e.c.c<0?e.a>=e.c.b:e.a<=e.c.b))throw $(new au);return n=e.a,e.a+=e.c.c,++e.b,ve(n)}function zkn(e){var n,t;if(e==null)return!1;for(n=0,t=e.length;n=i||n=0?n:-n;i>0;)i%2==0?(t*=t,i=i/2|0):(r*=t,i-=1);return n<0?1/r:r}function Kkn(e,n){var t,i,r;for(r=1,t=e,i=n>=0?n:-n;i>0;)i%2==0?(t*=t,i=i/2|0):(r*=t,i-=1);return n<0?1/r:r}function $0(e,n){var t,i,r,c;return c=(r=e?nz(e):null,oqe((i=n,r&&r.El(),i))),c==n&&(t=nz(e),t&&t.El()),c}function P1e(e,n,t){var i,r;return r=e.a,e.a=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,1,r,n),t?t.lj(i):t=i),t}function pFe(e,n,t){var i,r;return r=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,3,r,n),t?t.lj(i):t=i),t}function mFe(e,n,t){var i,r;return r=e.f,e.f=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,0,r,n),t?t.lj(i):t=i),t}function vFe(e){var n,t;if(e!=null)for(t=0;t-129&&e<128?(mDe(),n=e+128,t=Ime[n],!t&&(t=Ime[n]=new Ln(e)),t):new Ln(e)}function ve(e){var n,t;return e>-129&&e<128?(aDe(),n=e+128,t=Cme[n],!t&&(t=Cme[n]=new co(e)),t):new co(e)}function ejn(e,n,t,i,r){n==0||i==0||(n==1?r[i]=Tde(r,t,i,e[0]):i==1?r[n]=Tde(r,e,n,t[0]):HCn(e,t,r,n,i))}function SFe(e,n){var t;e.c.length!=0&&(t=u(Ra(e,oe(c1,Bd,9,e.c.length,0,1)),199),Bse(t,new Lh),Oqe(t,n))}function xFe(e,n){var t;e.c.length!=0&&(t=u(Ra(e,oe(c1,Bd,9,e.c.length,0,1)),199),Bse(t,new r3),Oqe(t,n))}function AFe(e,n){var t;e.a.c.length>0&&(t=u(Le(e.a,e.a.c.length-1),565),x1e(t,n))||Te(e.a,new RPe(n))}function njn(e){rl();var n,t;n=e.d.c-e.e.c,t=u(e.g,156),Ao(t.b,new Lje(n)),Ao(t.c,new Pje(n)),rc(t.i,new $je(n))}function MFe(e){var n;return n=new p0,n.a+="VerticalSegment ",uo(n,e.e),n.a+=" ",Kt(n,nle(new NX,new L(e.k))),n.a}function tjn(e,n){var t;e.c=n,e.a=eEn(n),e.a<54&&(e.f=(t=n.d>1?PLe(n.a[0],n.a[1]):PLe(n.a[0],0),Xb(n.e>0?t:Td(t))))}function hQ(e,n){var t,i,r;for(t=0,r=mu(e,n).Jc();r.Ob();)i=u(r.Pb(),12),t+=T(i,(me(),vs))!=null?1:0;return t}function ev(e,n,t){var i,r,c;for(i=0,c=St(e,0);c.b!=c.d.c&&(r=ne(re(jt(c))),!(r>t));)r>=n&&++i;return i}function ijn(e){var n;return n=u(Pa(e.c.c,""),233),n||(n=new N4(h9(a9(new f0,""),"Other")),tg(e.c.c,"",n)),n}function _E(e){var n;return(e.Db&64)!=0?Jf(e):(n=new cf(Jf(e)),n.a+=" (name: ",Bc(n,e.zb),n.a+=")",n.a)}function B1e(e,n,t){var i,r;return r=e.sb,e.sb=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,4,r,n),t?t.lj(i):t=i),t}function xO(e,n,t){var i;e.Zi(e.i+1),i=e.Xi(n,t),n!=e.i&&Wu(e.g,n,e.g,n+1,e.i-n),tr(e.g,n,i),++e.i,e.Ki(n,t),e.Li()}function z1e(e,n,t){var i,r;return r=e.r,e.r=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,8,r,e.r),t?t.lj(i):t=i),t}function rjn(e,n,t){var i,r;return i=new L1(e.e,3,13,null,(r=n.c,r||(yn(),ih)),Ld(e,n),!1),t?t.lj(i):t=i,t}function cjn(e,n,t){var i,r;return i=new L1(e.e,4,13,(r=n.c,r||(yn(),ih)),null,Ld(e,n),!1),t?t.lj(i):t=i,t}function ujn(e,n){var t,i,r,c;if(n.cj(e.a),c=u(Un(e.a,8),1997),c!=null)for(t=c,i=0,r=t.length;i>1&1431655765,e=(e>>2&858993459)+(e&858993459),e=(e>>4)+e&252645135,e+=e>>8,e+=e>>16,e&63}function ojn(e){return e?(e.i&1)!=0?e==ts?Yi:e==$t?jr:e==Hm?h7:e==Jr?gr:e==Ep?cp:e==t5?up:e==ds?py:XS:e:null}function di(e,n){return $r(e)?bn(e,n):s2(e)?vNe(e,n):o2(e)?(_n(e),ue(e)===ue(n)):Cfe(e)?e.Fb(n):Sfe(e)?bCe(e,n):Mae(e,n)}function CFe(e){var n;return ao(e,0)<0&&(e=I0(Avn(uu(e)?sf(e):e))),n=Rt(Bb(e,32)),64-(n!=0?UO(n):UO(Rt(e))+32)}function AO(e,n){var t;return t=new ch,e.a.zd(t)?(y9(),new SX(_n(hRe(e,t.a,n)))):(A0(e),y9(),y9(),Gme)}function LE(e,n){switch(n.g){case 2:case 1:return mu(e,n);case 3:case 4:return Ks(mu(e,n))}return jn(),jn(),Sc}function sjn(e,n){var t;return n.a&&(t=n.a.a.length,e.a?Kt(e.a,e.b):e.a=new il(e.d),zLe(e.a,n.a,n.d.length,t)),e}function ljn(e){Zz();var n,t,i,r;for(t=DQ(),i=0,r=t.length;it)throw $(new jo(cF+e+Yge+n+", size: "+t));if(e>n)throw $(new Gn(cF+e+DYe+n))}function Fl(e,n,t){if(n<0)q0e(e,t);else{if(!t.pk())throw $(new Gn(W0+t.ve()+_S));u(t,69).uk().Ck(e,e.ei(),n)}}function dQ(e,n,t){return k.Math.abs(n-e)NF?e-t>NF:t-e>NF}function J1e(e,n,t,i){switch(n){case 1:return!e.n&&(e.n=new pe(ju,e,1,7)),e.n;case 2:return e.k}return $de(e,n,t,i)}function NFe(e){var n;return(e.Db&64)!=0?Jf(e):(n=new cf(Jf(e)),n.a+=" (source: ",Bc(n,e.d),n.a+=")",n.a)}function Id(e,n){var t;t=(e.Bb&256)!=0,n?e.Bb|=256:e.Bb&=-257,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,2,t,n))}function H1e(e,n){var t;t=(e.Bb&256)!=0,n?e.Bb|=256:e.Bb&=-257,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,8,t,n))}function G1e(e,n){var t;t=(e.Bb&512)!=0,n?e.Bb|=512:e.Bb&=-513,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,9,t,n))}function _d(e,n){var t;t=(e.Bb&512)!=0,n?e.Bb|=512:e.Bb&=-513,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,3,t,n))}function FB(e,n){var t;t=(e.Bb&256)!=0,n?e.Bb|=256:e.Bb&=-257,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,8,t,n))}function fjn(e,n,t){var i,r;return r=e.a,e.a=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,5,r,e.a),t?s0e(t,i):t=i),t}function PE(e,n){var t;return e.b==-1&&e.a&&(t=e.a.nk(),e.b=t?e.c.Eh(e.a.Jj(),t):zi(e.c.Ah(),e.a)),e.c.vh(e.b,n)}function DFe(e,n){var t,i;for(i=new ut(e);i.e!=i.i.gc();)if(t=u(ft(i),29),ue(n)===ue(t))return!0;return!1}function IFe(e){return e>=65&&e<=70?e-65+10:e>=97&&e<=102?e-97+10:e>=48&&e<=57?e-48:0}function q1e(e){var n,t;return n=e.k,n==(zn(),wr)?(t=u(T(e,(me(),Du)),64),t==(De(),Xn)||t==bt):!1}function _Fe(e){var n;return n=Dae(e),Hj(n.a,0)?(i2(),i2(),ste):(i2(),new PK(FX(n.a,0)?ehe(n)/Xb(n.a):0))}function ajn(e,n){var t;if(t=QO(e,n),X(t,335))return u(t,38);throw $(new Gn(W0+n+"' is not a valid attribute"))}function $E(e,n,t){var i;if(i=e.gc(),n>i)throw $(new b2(n,i));if(e.Qi()&&e.Gc(t))throw $(new Gn($N));e.Ei(n,t)}function LFe(e,n){var t,i;for(i=new ut(e);i.e!=i.i.gc();)if(t=u(ft(i),143),ue(n)===ue(t))return!0;return!1}function hjn(e,n,t){var i,r,c;return c=(r=E8(e.b,n),r),c&&(i=u(Kz(oO(e,c),""),29),i)?gbe(e,i,n,t):null}function bQ(e,n,t){var i,r,c;return c=(r=E8(e.b,n),r),c&&(i=u(Kz(oO(e,c),""),29),i)?wbe(e,i,n,t):null}function djn(e){var n,t,i;for(i=0,t=e.length,n=0;n=0?B0(e):sE(B0(Td(e))))}function PFe(e,n,t,i,r,c){this.e=new Ce,this.f=(Nc(),xx),Te(this.e,e),this.d=n,this.a=t,this.b=i,this.f=r,this.c=c}function ki(e,n){return en?1:e==n?e==0?ki(1/e,1/n):0:isNaN(e)?isNaN(n)?0:1:-1}function bjn(e){var n;return n=e.a[e.c-1&e.a.length-1],n==null?null:(e.c=e.c-1&e.a.length-1,tr(e.a,e.c,null),n)}function $Fe(e){var n,t;for(t=e.p.a.ec().Jc();t.Ob();)if(n=u(t.Pb(),217),n.f&&e.b[n.c]<-1e-10)return n;return null}function gjn(e){var n,t,i;for(n=new Ce,i=new L(e.b);i.a=1?ru:Za):t}function kjn(e){var n,t;for(t=wUe(sl(e)).Jc();t.Ob();)if(n=Pt(t.Pb()),uS(e,n))return I6n((DMe(),Ban),n);return null}function jjn(e,n,t){var i,r;for(r=e.a.ec().Jc();r.Ob();)if(i=u(r.Pb(),9),yO(t,u(Le(n,i.p),18)))return i;return null}function Ejn(e,n,t){var i,r;for(r=X(n,103)&&(u(n,19).Bb&Ec)!=0?new EK(n,e):new Y9(n,e),i=0;i>10)+pN&yr,n[1]=(e&1023)+56320&yr,gh(n,0,n.length)}function Y1e(e,n){var t;t=(e.Bb&Ec)!=0,n?e.Bb|=Ec:e.Bb&=-65537,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,20,t,n))}function a8(e,n){var t;t=(e.Bb&yh)!=0,n?e.Bb|=yh:e.Bb&=-16385,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,16,t,n))}function pQ(e,n){var t;t=(e.Bb&Bu)!=0,n?e.Bb|=Bu:e.Bb&=-32769,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,18,t,n))}function Q1e(e,n){var t;t=(e.Bb&Bu)!=0,n?e.Bb|=Bu:e.Bb&=-32769,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,18,t,n))}function mu(e,n){var t;return e.i||G0e(e),t=u(zc(e.g,n),49),t?new T0(e.j,u(t.a,15).a,u(t.b,15).a):(jn(),jn(),Sc)}function Ajn(e,n,t){var i,r;return i=u(n.mf(e.a),35),r=u(t.mf(e.a),35),i!=null&&r!=null?dO(i,r):i!=null?-1:r!=null?1:0}function W1e(e,n,t){var i,r;return i=(v0(),r=new Fk,r),bB(i,n),gB(i,t),e&&Et((!e.a&&(e.a=new mr(kl,e,5)),e.a),i),i}function Z1e(e,n,t){var i;return i=0,n&&(I3(e.a)?i+=n.f.a/2:i+=n.f.b/2),t&&(I3(e.a)?i+=t.f.a/2:i+=t.f.b/2),i}function Pw(e,n,t){var i;return i=e.a.get(n),e.a.set(n,t===void 0?null:t),i===void 0?(++e.c,++e.b.g):++e.d,i}function mQ(e){var n;return(e.Db&64)!=0?Jf(e):(n=new cf(Jf(e)),n.a+=" (identifier: ",Bc(n,e.k),n.a+=")",n.a)}function qB(e){var n;switch(e.gc()){case 0:return cR(),Zne;case 1:return new HK(Nt(e.Xb(0)));default:return n=e,new QV(n)}}function Mjn(e){switch(u(T(e,(Ne(),Y1)),222).g){case 1:return new ad;case 3:return new T5;default:return new Dp}}function Tjn(e){var n;return n=F2(e),n>34028234663852886e22?Ki:n<-34028234663852886e22?Dr:n}function mc(e,n){var t;return uu(e)&&uu(n)&&(t=e+n,wNn){NLe(t);break}}yR(t,n)}function We(e,n){var t,i,r,c,o;if(t=n.f,tg(e.c.d,t,n),n.g!=null)for(r=n.g,c=0,o=r.length;cn&&i.Le(e[c-1],e[c])>0;--c)o=e[c],tr(e,c,e[c-1]),tr(e,c-1,o)}function Jl(e,n,t,i){if(n<0)ybe(e,t,i);else{if(!t.pk())throw $(new Gn(W0+t.ve()+_S));u(t,69).uk().Ak(e,e.ei(),n,i)}}function Rjn(e,n){var t;if(t=QO(e.Ah(),n),X(t,103))return u(t,19);throw $(new Gn(W0+n+"' is not a valid reference"))}function UB(e,n){if(n==e.d)return e.e;if(n==e.e)return e.d;throw $(new Gn("Node "+n+" not part of edge "+e))}function nde(e,n,t,i){switch(n){case 3:return e.f;case 4:return e.g;case 5:return e.i;case 6:return e.j}return J1e(e,n,t,i)}function Bjn(e){return e.k!=(zn(),Qi)?!1:G3(new wn(null,new v2(new qn(Vn(Ni(e).a.Jc(),new ee)))),new ZA)}function Xs(){Xs=Y,sD=new sC(ma,0),fx=new sC("FIRST",1),V1=new sC(jQe,2),ax=new sC("LAST",3),yg=new sC(EQe,4)}function BE(){BE=Y,ix=new h$("LAYER_SWEEP",0),wve=new h$("MEDIAN_LAYER_SWEEP",1),eD=new h$(ree,2),pve=new h$(ma,3)}function XB(){XB=Y,f6e=new hK("ASPECT_RATIO_DRIVEN",0),Gre=new hK("MAX_SCALE_DRIVEN",1),l6e=new hK("AREA_DRIVEN",2)}function KB(){KB=Y,Nce=new S$(Rpe,0),M8e=new S$("GROUP_DEC",1),C8e=new S$("GROUP_MIXED",2),T8e=new S$("GROUP_INC",3)}function zjn(e,n){return bn(n.b&&n.c?Ub(n.b)+"->"+Ub(n.c):"e_"+Oi(n),e.b&&e.c?Ub(e.b)+"->"+Ub(e.c):"e_"+Oi(e))}function Fjn(e,n){return bn(n.b&&n.c?Ub(n.b)+"->"+Ub(n.c):"e_"+Oi(n),e.b&&e.c?Ub(e.b)+"->"+Ub(e.c):"e_"+Oi(e))}function $w(e,n){return Oa(),Bf(X0),k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)?0:en?1:Lb(isNaN(e),isNaN(n))}function tde(e){EQ(),this.c=$f(z(B(UBn,1),On,829,0,[Run])),this.b=new pt,this.a=e,ei(this.b,jH,1),Ao(Bun,new eSe(this))}function zE(e){var n;this.a=(n=u(e.e&&e.e(),10),new _l(n,u(_f(n,n.length),10),0)),this.b=oe(Ar,On,1,this.a.a.length,5,1)}function su(e){var n;return Array.isArray(e)&&e.Rm===Qn?Db(Us(e))+"@"+(n=Oi(e)>>>0,n.toString(16)):e.toString()}function Jjn(e){var n;return e==null?!0:(n=e.length,n>0&&(Yn(n-1,e.length),e.charCodeAt(n-1)==58)&&!kQ(e,uA,oA))}function kQ(e,n,t){var i,r;for(i=0,r=e.length;i=r)return n.c+t;return n.c+n.b.gc()}function UFe(e,n){S9();var t,i,r,c;for(i=R$e(e),r=n,J9(i,0,i.length,r),t=0;t0&&(i+=r,++t);return t>1&&(i+=e.d*(t-1)),i}function rde(e){var n,t,i;for(i=new pd,i.a+="[",n=0,t=e.gc();n=0;--i)for(n=t[i],r=0;r>5,n=e&31,i=oe($t,ni,30,t+1,15,1),i[t]=1<0&&(n.lengthe.i&&tr(n,e.i,null),n}function VB(e){var n;return(e.Db&64)!=0?_E(e):(n=new cf(_E(e)),n.a+=" (instanceClassName: ",Bc(n,e.D),n.a+=")",n.a)}function YB(e){var n,t,i,r;for(r=0,t=0,i=e.length;t0?(e.Zj(),i=n==null?0:Oi(n),r=(i&oi)%e.d.length,t=jUe(e,r,i,n),t!=-1):!1}function To(e,n,t){var i,r,c;return e.Nj()?(i=e.i,c=e.Oj(),xO(e,i,n),r=e.Gj(3,null,n,i,c),t?t.lj(r):t=r):xO(e,e.i,n),t}function wa(e,n){var t,i,r;return e.f>0&&(e.Zj(),i=n==null?0:Oi(n),r=(i&oi)%e.d.length,t=W0e(e,r,i,n),t)?t.kd():null}function fEn(e,n,t){var i,r;return i=new L1(e.e,3,10,null,(r=n.c,X(r,88)?u(r,29):(yn(),jf)),Ld(e,n),!1),t?t.lj(i):t=i,t}function aEn(e,n,t){var i,r;return i=new L1(e.e,4,10,(r=n.c,X(r,88)?u(r,29):(yn(),jf)),null,Ld(e,n),!1),t?t.lj(i):t=i,t}function iJe(e,n){var t,i,r;return X(n,45)?(t=u(n,45),i=t.jd(),r=L2(e.Pc(),i),T1(r,t.kd())&&(r!=null||e.Pc()._b(i))):!1}function dde(e,n){switch(n){case 3:Dw(e,0);return;case 4:Iw(e,0);return;case 5:Os(e,0);return;case 6:Ns(e,0);return}R1e(e,n)}function Rw(e,n){switch(n.g){case 1:return j4(e.j,(ss(),S3e));case 2:return j4(e.j,(ss(),A3e));default:return jn(),jn(),Sc}}function B0(e){mh();var n,t;return t=Rt(e),n=Rt(Bb(e,32)),n!=0?new hLe(t,n):t>10||t<0?new D1(1,t):cnn[t]}function rJe(e){B2();var n;return(e.q?e.q:(jn(),jn(),i1))._b((Ne(),gp))?n=u(T(e,gp),203):n=u(T(_r(e),vx),203),n}function hEn(e,n,t,i){var r,c;if(c=t-n,c<3)for(;c<3;)e*=10,++c;else{for(r=1;c>3;)r*=10,--c;e=(e+(r>>1))/r|0}return i.i=e,!0}function cJe(e,n,t){fBe(),gxe.call(this),this.a=g2(Tnn,[Se,Zge],[592,216],0,[gJ,gte],2),this.c=new g4,this.g=e,this.f=n,this.d=t}function uJe(e){this.e=oe($t,ni,30,e.length,15,1),this.c=oe(ts,pa,30,e.length,16,1),this.b=oe(ts,pa,30,e.length,16,1),this.f=0}function dEn(e){var n,t;for(e.j=oe(Jr,Jc,30,e.p.c.length,15,1),t=new L(e.p);t.a>5,n&=31,r=e.d+t+(n==0?0:1),i=oe($t,ni,30,r,15,1),aMn(i,e.a,t,n),c=new zb(e.e,r,i),bE(c),c}function h8(e,n,t){var i,r,c;for(r=null,c=e.b;c;){if(i=e.a.Le(n,c.d),t&&i==0)return c;i>=0?c=c.a[1]:(r=c,c=c.a[0])}return r}function DO(e,n,t){var i,r,c;for(r=null,c=e.b;c;){if(i=e.a.Le(n,c.d),t&&i==0)return c;i<=0?c=c.a[0]:(r=c,c=c.a[1])}return r}function MQ(e,n){for(var t=0;!n[t]||n[t]=="";)t++;for(var i=n[t++];t0?(k.Error.stackTraceLimit=Error.stackTraceLimit=64,!0):"stack"in new Error}function mEn(e){var n;n=e.a;do n=u(tt(new qn(Vn(Ni(n).a.Jc(),new ee))),17).d.i,n.k==(zn(),dr)&&Te(e.e,n);while(n.k==(zn(),dr))}function vEn(e,n){var t,i,r;for(i=new qn(Vn(Ni(e).a.Jc(),new ee));ht(i);)if(t=u(tt(i),17),r=t.d.i,r.c==n)return!1;return!0}function hJe(e,n,t){var i,r,c,o;for(r=u(Bn(e.b,t),171),i=0,o=new L(n.j);o.an?1:Lb(isNaN(e),isNaN(n)))>0}function pde(e,n){return Oa(),Oa(),Bf(X0),(k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)?0:en?1:Lb(isNaN(e),isNaN(n)))<0}function pJe(e,n){return Oa(),Oa(),Bf(X0),(k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)?0:en?1:Lb(isNaN(e),isNaN(n)))<=0}function mde(e){switch(e.g){case 12:case 13:case 14:case 15:case 16:case 17:case 18:case 19:case 20:return!0;default:return!1}}function vde(e,n,t,i,r,c){this.a=e,this.c=n,this.b=t,this.f=i,this.d=r,this.e=c,this.c>0&&this.b>0&&(this.g=oR(this.c,this.b,this.a))}function jEn(e,n){var t=e.a,i;n=String(n),t.hasOwnProperty(n)&&(i=t[n]);var r=(QY(),ite)[typeof i],c=r?r(i):F1e(typeof i);return c}function d8(e){var n,t,i;if(i=null,n=Ah in e.a,t=!n,t)throw $(new oh("Every element must have an id."));return i=Z4(O1(e,Ah)),i}function Bw(e){var n,t;for(t=GGe(e),n=null;e.c==2;)li(e),n||(n=(fi(),fi(),new Kj(2)),ug(n,t),t=n),t.Hm(GGe(e));return t}function ZB(e,n){var t,i,r;return e.Zj(),i=n==null?0:Oi(n),r=(i&oi)%e.d.length,t=W0e(e,r,i,n),t?(wBe(e,t),t.kd()):null}function gh(e,n,t){var i,r,c,o;for(c=n+t,Yr(n,c,e.length),o="",r=n;rn.e?1:e.en.d?e.e:e.d=48&&e<48+k.Math.min(10,10)?e-48:e>=97&&e<97?e-97+10:e>=65&&e<65?e-65+10:-1}function EEn(e,n){if(n.c==e)return n.d;if(n.d==e)return n.c;throw $(new Gn("Input edge is not connected to the input port."))}function wh(e,n){if(e.a<0)throw $(new Uc("Did not call before(...) or after(...) before calling add(...)."));return ble(e,e.a,n),e}function yde(e){return $R(),X(e,166)?u(Bn(WD,fnn),296).Qg(e):so(WD,Us(e))?u(Bn(WD,Us(e)),296).Qg(e):null}function Lo(e){var n,t;return(e.Db&32)==0&&(t=(n=u(Un(e,16),29),dt(n||e.fi())-dt(e.fi())),t!=0&&U4(e,32,oe(Ar,On,1,t,5,1))),e}function U4(e,n,t){var i;(e.Db&n)!=0?t==null?JCn(e,n):(i=VQ(e,n),i==-1?e.Eb=t:tr(B4(e.Eb),i,t)):t!=null&&sDn(e,n,t)}function SEn(e,n,t,i){var r,c;n.c.length!=0&&(r=tNn(t,i),c=fCn(n),Zi(aB(new wn(null,new pn(c,1)),new g_),new JIe(e,t,r,i)))}function xEn(e,n){var t,i,r,c;return i=e.a.length-1,t=n-e.b&i,c=e.c-n&i,r=e.c-e.b&i,SOe(t=c?(Ikn(e,n),-1):(Dkn(e,n),1)}function AEn(e,n){var t,i;for(t=(Yn(n,e.length),e.charCodeAt(n)),i=n+1;in.e?1:e.fn.f?1:Oi(e)-Oi(n)}function jJe(e,n){var t;return ue(n)===ue(e)?!0:!X(n,22)||(t=u(n,22),t.gc()!=e.gc())?!1:e.Hc(t)}function ez(e,n){return _n(e),n==null?!1:bn(e,n)?!0:e.length==n.length&&bn(e.toLowerCase(),n.toLowerCase())}function R2(e){var n,t;return ao(e,-129)>0&&ao(e,128)<0?(pDe(),n=Rt(e)+128,t=Ome[n],!t&&(t=Ome[n]=new En(e)),t):new En(e)}function X4(){X4=Y,ZS=new l$(ma,0),v3e=new l$("INSIDE_PORT_SIDE_GROUPS",1),Cte=new l$("GROUP_MODEL_ORDER",2),Ote=new l$(ZZ,3)}function nz(e){var n,t,i;if(i=e.Gh(),!i)for(n=0,t=e.Mh();t;t=t.Mh()){if(++n>$Z)return t.Nh();if(i=t.Gh(),i||t==e)break}return i}function CEn(e){var n;return e.b||ogn(e,(n=o2n(e.e,e.a),!n||!bn(ane,wa((!n.b&&(n.b=new Hs((yn(),Ac),Iu,n)),n.b),"qualified")))),e.c}function OEn(e){var n,t;for(t=new L(e.a.b);t.a2e3&&(Ven=e,sJ=k.setTimeout(Egn,10))),oJ++==0?(r8n((Toe(),yme)),!0):!1}function JEn(e,n,t){var i;(pnn?(nEn(e),!0):mnn||ynn?(m9(),!0):vnn&&(m9(),!1))&&(i=new ONe(n),i.b=t,HMn(e,i))}function OQ(e,n){var t;t=!e.A.Gc((Vs(),Og))||e.q==(Br(),to),e.u.Gc((ps(),Z1))?t?fRn(e,n):xVe(e,n):e.u.Gc(gb)&&(t?D$n(e,n):zVe(e,n))}function HEn(e,n,t){var i,r;aW(e.e,n,t,(De(),Kn)),aW(e.i,n,t,et),e.a&&(r=u(T(n,(me(),wi)),12),i=u(T(t,wi),12),WV(e.g,r,i))}function MJe(e){var n;ue(ye(e,(Xt(),Xv)))===ue((B1(),oG))&&(Bi(e)?(n=u(ye(Bi(e),Xv),347),ji(e,Xv,n)):ji(e,Xv,Qx))}function TJe(e,n,t){return new Lf(k.Math.min(e.a,n.a)-t/2,k.Math.min(e.b,n.b)-t/2,k.Math.abs(e.a-n.a)+t,k.Math.abs(e.b-n.b)+t)}function CJe(e){var n;this.d=new Ce,this.j=new Kr,this.g=new Kr,n=e.g.b,this.f=u(T(_r(n),(Ne(),pl)),86),this.e=ne(re(rz(n,Em)))}function OJe(e){this.d=new Ce,this.e=new O0,this.c=oe($t,ni,30,(De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])).length,15,1),this.b=e}function xde(e,n,t){var i;switch(i=t[e.g][n],e.g){case 1:case 3:return new je(0,i);case 2:case 4:return new je(i,0);default:return null}}function GEn(e,n){var t;if(t=U3(e.o,n),t==null)throw $(new oh("Node did not exist in input."));return Sbe(e,n),PW(e,n),bbe(e,n,t),null}function NJe(e,n){var t,i;for(i=e.a.length,n.lengthi&&tr(n,i,null),n}function Ra(e,n){var t,i;for(i=e.c.length,n.lengthi&&tr(n,i,null),n}function NQ(e,n,t,i){var r;if(r=e.length,n>=r)return r;for(n=n>0?n:0;n0&&(Te(e.b,new KNe(n.a,t)),i=n.a.length,0i&&(n.a+=HCe(oe(Wl,kh,30,-i,15,1))))}function _Je(e,n,t){var i,r,c;if(!t[n.d])for(t[n.d]=!0,r=new L(Z3(n));r.a=e.b>>1)for(i=e.c,t=e.b;t>n;--t)i=i.b;else for(i=e.a.a,t=0;t=0?e.Th(r):kW(e,i)):t<0?kW(e,i):u(i,69).uk().zk(e,e.ei(),t)}function RJe(e){var n,t,i;for(i=(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),e.o),t=i.c.Jc();t.e!=t.i.gc();)n=u(t.Wj(),45),n.kd();return ZC(i)}function _e(e){var n;if(X(e.a,4)){if(n=yde(e.a),n==null)throw $(new Uc(gWe+e.b+"'. "+bWe+(M1(ZD),ZD.k)+T2e));return n}else return e.a}function nSn(e){var n;if(e==null)return null;if(n=mRn(bo(e,!0)),n==null)throw $(new TX("Invalid base64Binary value: '"+e+"'"));return n}function ft(e){var n;try{return n=e.i.Xb(e.e),e.Vj(),e.g=e.e++,n}catch(t){throw t=sr(t),X(t,99)?(e.Vj(),$(new au)):$(t)}}function LQ(e){var n;try{return n=e.c.Ti(e.e),e.Vj(),e.g=e.e++,n}catch(t){throw t=sr(t),X(t,99)?(e.Vj(),$(new au)):$(t)}}function iz(e){var n,t,i,r;for(r=0,t=0,i=e.length;t=64&&n<128&&(r=hh(r,Gh(1,n-64)));return r}function rz(e,n){var t,i;return i=null,bi(e,(Xt(),Jy))&&(t=u(T(e,Jy),105),t.nf(n)&&(i=t.mf(n))),i==null&&_r(e)&&(i=T(_r(e),n)),i}function tSn(e,n){var t;return t=u(T(e,(Ne(),Wc)),78),NK(n,nin)?t?qs(t):(t=new xs,he(e,Wc,t)):t&&he(e,Wc,null),t}function iSn(e,n){var t,i,r;for(r=new xo(n.gc()),i=n.Jc();i.Ob();)t=u(i.Pb(),294),t.c==t.f?k8(e,t,t.c):pTn(e,t)||Hn(r.c,t);return r}function BJe(e,n){var t,i,r;for(t=e.o,r=u(u(pi(e.r,n),22),83).Jc();r.Ob();)i=u(r.Pb(),115),i.e.a=cxn(i,t.a),i.e.b=t.b*ne(re(i.b.mf(wJ)))}function rSn(e,n){var t,i,r,c;return r=e.k,t=ne(re(T(e,(me(),hp)))),c=n.k,i=ne(re(T(n,hp))),c!=(zn(),wr)?-1:r!=wr?1:t==i?0:tt.b)return!0}return!1}function JJe(e){var n;return n=new p0,n.a+="n",e.k!=(zn(),Qi)&&Kt(Kt((n.a+="(",n),$K(e.k).toLowerCase()),")"),Kt((n.a+="_",n),LO(e)),n.a}function HE(){HE=Y,I4e=new lC(Rpe,0),Wie=new lC(ree,1),Zie=new lC("LINEAR_SEGMENTS",2),jx=new lC("BRANDES_KOEPF",3),Ex=new lC(BQe,4)}function K4(e,n,t,i){var r;return t>=0?e.Ph(n,t,i):(e.Mh()&&(i=(r=e.Ch(),r>=0?e.xh(i):e.Mh().Qh(e,-1-r,null,i))),e.zh(n,t,i))}function Ade(e,n){switch(n){case 7:!e.e&&(e.e=new Nn(pr,e,7,4)),kt(e.e);return;case 8:!e.d&&(e.d=new Nn(pr,e,8,5)),kt(e.d);return}dde(e,n)}function ji(e,n,t){return t==null?(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),ZB(e.o,n)):(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),RO(e.o,n,t)),e}function Yu(e,n){var t;t=e.dd(n);try{return t.Pb()}catch(i){throw i=sr(i),X(i,112)?$(new jo("Can't get element "+n)):$(i)}}function HJe(e,n){var t;switch(t=u(zc(e.b,n),127).n,n.g){case 1:e.t>=0&&(t.d=e.t);break;case 3:e.t>=0&&(t.a=e.t)}e.C&&(t.b=e.C.b,t.c=e.C.c)}function aSn(e){var n;n=e.a;do n=u(tt(new qn(Vn(rr(n).a.Jc(),new ee))),17).c.i,n.k==(zn(),dr)&&e.b.Ec(n);while(n.k==(zn(),dr));e.b=Ks(e.b)}function GJe(e,n){var t,i,r;for(r=e,i=new qn(Vn(rr(n).a.Jc(),new ee));ht(i);)t=u(tt(i),17),t.c.i.c&&(r=k.Math.max(r,t.c.i.c.p));return r}function hSn(e,n){var t,i,r;for(r=0,i=u(u(pi(e.r,n),22),83).Jc();i.Ob();)t=u(i.Pb(),115),r+=t.d.d+t.b.Kf().b+t.d.a,i.Ob()&&(r+=e.w);return r}function dSn(e,n){var t,i,r;for(r=0,i=u(u(pi(e.r,n),22),83).Jc();i.Ob();)t=u(i.Pb(),115),r+=t.d.b+t.b.Kf().a+t.d.c,i.Ob()&&(r+=e.w);return r}function qJe(e){var n,t,i,r;if(i=0,r=q2(e),r.c.length==0)return 1;for(t=new L(r);t.a=0?e.Ih(o,t,!0):Gw(e,c,t)):u(c,69).uk().wk(e,e.ei(),r,t,i)}function wSn(e,n,t,i){var r,c;c=n.nf((Xt(),Vv))?u(n.mf(Vv),22):e.j,r=ljn(c),r!=(Zz(),wte)&&(t&&!mde(r)||O0e(NOn(e,r,i),n))}function PQ(e,n){return $r(e)?!!Jen[n]:e.Qm?!!e.Qm[n]:s2(e)?!!Fen[n]:o2(e)?!!zen[n]:!1}function pSn(e){switch(e.g){case 1:return Lw(),XN;case 3:return Lw(),UN;case 2:return Lw(),mte;case 4:return Lw(),pte;default:return null}}function mSn(e,n,t){if(e.e)switch(e.b){case 1:I5n(e.c,n,t);break;case 0:_5n(e.c,n,t)}else oPe(e.c,n,t);e.a[n.p][t.p]=e.c.i,e.a[t.p][n.p]=e.c.e}function XJe(e){var n,t;if(e==null)return null;for(t=oe(c1,Se,199,e.length,0,2),n=0;nc?1:0):0}function B2(){B2=Y,xH=new d$(ma,0),Yie=new d$("PORT_POSITION",1),Fv=new d$("NODE_SIZE_WHERE_SPACE_PERMITS",2),zv=new d$("NODE_SIZE",3)}function vSn(e,n){var t,i,r;for(n.Tg("Untreeify",1),t=u(T(e,(Ti(),yye)),16),r=t.Jc();r.Ob();)i=u(r.Pb(),65),Vt(i.b.d,i),Vt(i.c.b,i);n.Ug()}function Vh(){Vh=Y,sce=new Rj("AUTOMATIC",0),CD=new Rj(ly,1),OD=new Rj(fy,2),nG=new Rj("TOP",3),ZH=new Rj(nwe,4),eG=new Rj(F8,5)}function tv(e,n,t){var i,r;if(r=e.gc(),n>=r)throw $(new b2(n,r));if(e.Qi()&&(i=e.bd(t),i>=0&&i!=n))throw $(new Gn($N));return e.Vi(n,t)}function Ld(e,n){var t,i,r;if(r=CHe(e,n),r>=0)return r;if(e.ml()){for(i=0;i0||e==(jX(),Vne)||n==(EX(),Yne))throw $(new Gn("Invalid range: "+uPe(e,n)))}function Tde(e,n,t,i){A8();var r,c;for(r=0,c=0;c0),(n&-n)==n)return sc(n*Is(e,31)*4656612873077393e-25);do t=Is(e,31),i=t%n;while(t-i+(n-1)<0);return sc(i)}function ySn(e,n){var t,i,r;for(t=mw(new Nb,e),r=new L(n);r.a1&&(c=ySn(e,n)),c}function xSn(e){var n,t,i;for(n=0,i=new L(e.c.a);i.a102?-1:e<=57?e-48:e<65?-1:e<=70?e-65+10:e<97?-1:e-97+10}function GQ(e,n){if(e==null)throw $(new c4("null key in entry: null="+n));if(n==null)throw $(new c4("null value in entry: "+e+"=null"))}function nHe(e,n){var t;return t=z(B(Jr,1),Jc,30,15,[lQ(e.a[0],n),lQ(e.a[1],n),lQ(e.a[2],n)]),e.d&&(t[0]=k.Math.max(t[0],t[2]),t[2]=t[0]),t}function tHe(e,n){var t;return t=z(B(Jr,1),Jc,30,15,[BB(e.a[0],n),BB(e.a[1],n),BB(e.a[2],n)]),e.d&&(t[0]=k.Math.max(t[0],t[2]),t[2]=t[0]),t}function Dde(e,n,t){k4(u(T(n,(Ne(),Wi)),102))||(Xae(e,n,Pd(n,t)),Xae(e,n,Pd(n,(De(),bt))),Xae(e,n,Pd(n,Xn)),jn(),Cr(n.j,new nEe(e)))}function iHe(e){var n,t;for(e.c||APn(e),t=new xs,n=new L(e.a),_(n);n.a0&&(Yn(0,n.length),n.charCodeAt(0)==43)?(Yn(1,n.length+1),n.substr(1)):n))}function JSn(e){var n;return e==null?null:new E0((n=bo(e,!0),n.length>0&&(Yn(0,n.length),n.charCodeAt(0)==43)?(Yn(1,n.length+1),n.substr(1)):n))}function _de(e,n,t,i,r,c,o,l){var f,h;i&&(f=i.a[0],f&&_de(e,n,t,f,r,c,o,l),ZQ(e,t,i.d,r,c,o,l)&&n.Ec(i),h=i.a[1],h&&_de(e,n,t,h,r,c,o,l))}function GE(e,n){var t,i,r,c;for(c=e.gc(),n.lengthc&&tr(n,c,null),n}function HSn(e,n){var t,i;if(i=e.gc(),n==null){for(t=0;t0&&(f+=r),h[b]=o,o+=l*(f+i)}function YSn(e){var n;for(n=0;n0?e.c:0),++r;e.b=i,e.d=c}function gHe(e,n){var t;return t=z(B(Jr,1),Jc,30,15,[Cde(e,(ga(),Ou),n),Cde(e,No,n),Cde(e,Nu,n)]),e.f&&(t[0]=k.Math.max(t[0],t[2]),t[2]=t[0]),t}function wHe(e){var n;bi(e,(Ne(),bp))&&(n=u(T(e,bp),22),n.Gc((G2(),Qf))?(n.Kc(Qf),n.Ec(Wf)):n.Gc(Wf)&&(n.Kc(Wf),n.Ec(Qf)))}function pHe(e){var n;bi(e,(Ne(),bp))&&(n=u(T(e,bp),22),n.Gc((G2(),ea))?(n.Kc(ea),n.Ec(pf)):n.Gc(pf)&&(n.Kc(pf),n.Ec(ea)))}function YQ(e,n,t,i){var r,c,o,l;return e.a==null&&XMn(e,n),o=n.b.j.c.length,c=t.d.p,l=i.d.p,r=l-1,r<0&&(r=o-1),c<=r?e.a[r]-e.a[c]:e.a[o-1]-e.a[c]+e.a[r]}function QSn(e){var n;for(n=0;n0&&(r.b+=n),r}function dz(e,n){var t,i,r;for(r=new Kr,i=e.Jc();i.Ob();)t=u(i.Pb(),37),M8(t,0,r.b),r.b+=t.f.b+n,r.a=k.Math.max(r.a,t.f.a);return r.a>0&&(r.a+=n),r}function vHe(e,n){var t,i;if(n.length==0)return 0;for(t=MV(e.a,n[0],(De(),Kn)),t+=MV(e.a,n[n.length-1],et),i=0;i>16==6?e.Cb.Qh(e,5,xa,n):(i=Oc(u(An((t=u(Un(e,16),29),t||e.fi()),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function ixn(e){$9();var n=e.e;if(n&&n.stack){var t=n.stack,i=n+` +`;return t.substring(0,i.length)==i&&(t=t.substring(i.length)),t.split(` +`)}return[]}function rxn(e){var n;return n=(TBe(),enn),n[e>>>28]|n[e>>24&15]<<4|n[e>>20&15]<<8|n[e>>16&15]<<12|n[e>>12&15]<<16|n[e>>8&15]<<20|n[e>>4&15]<<24|n[e&15]<<28}function kHe(e){var n,t,i;e.b==e.c&&(i=e.a.length,t=b1e(k.Math.max(8,i))<<1,e.b!=0?(n=_f(e.a,t),IBe(e,n,i),e.a=n,e.b=0):Qp(e.a,t),e.c=i)}function cxn(e,n){var t;return t=e.b,t.nf((Xt(),Ps))?t.$f()==(De(),Kn)?-t.Kf().a-ne(re(t.mf(Ps))):n+ne(re(t.mf(Ps))):t.$f()==(De(),Kn)?-t.Kf().a:n}function LO(e){var n;return e.b.c.length!=0&&u(Le(e.b,0),70).a?u(Le(e.b,0),70).a:(n=NV(e),n??""+(e.c?wu(e.c.a,e,0):-1))}function bz(e){var n;return e.f.c.length!=0&&u(Le(e.f,0),70).a?u(Le(e.f,0),70).a:(n=NV(e),n??""+(e.i?wu(e.i.j,e,0):-1))}function uxn(e,n){var t,i;if(n<0||n>=e.gc())return null;for(t=n;t0?e.c:0),r=k.Math.max(r,n.d),++i;e.e=c,e.b=r}function oxn(e){var n,t;if(!e.b)for(e.b=zR(u(e.f,125).jh().i),t=new ut(u(e.f,125).jh());t.e!=t.i.gc();)n=u(ft(t),157),Te(e.b,new AX(n));return e.b}function sxn(e,n){var t,i,r;if(n.dc())return S9(),S9(),eI;for(t=new KOe(e,n.gc()),r=new ut(e);r.e!=r.i.gc();)i=ft(r),n.Gc(i)&&Et(t,i);return t}function $de(e,n,t,i){return n==0?i?(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),e.o):(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),ZC(e.o)):uz(e,n,t,i)}function WQ(e){var n,t;if(e.rb)for(n=0,t=e.rb.i;n>22),r+=i>>22,r<0)?!1:(e.l=t&Ls,e.m=i&Ls,e.h=r&G1,!0)}function ZQ(e,n,t,i,r,c,o){var l,f;return!(n.Re()&&(f=e.a.Le(t,i),f<0||!r&&f==0)||n.Se()&&(l=e.a.Le(t,c),l>0||!o&&l==0))}function hxn(e,n){n8();var t;if(t=e.j.g-n.j.g,t!=0)return 0;switch(e.j.g){case 2:return yQ(n,hve)-yQ(e,hve);case 4:return yQ(e,ave)-yQ(n,ave)}return 0}function dxn(e){switch(e.g){case 0:return iie;case 1:return rie;case 2:return cie;case 3:return uie;case 4:return KJ;case 5:return oie;default:return null}}function Qc(e,n,t){var i,r;return i=(r=new yX,ng(r,n),Mo(r,t),Et((!e.c&&(e.c=new pe(vp,e,12,10)),e.c),r),r),Cd(i,0),O2(i,1),_d(i,!0),Id(i,!0),i}function V4(e,n){var t,i;if(n>=e.i)throw $(new jK(n,e.i));return++e.j,t=e.g[n],i=e.i-n-1,i>0&&Wu(e.g,n+1,e.g,n,i),tr(e.g,--e.i,null),e.Oi(n,t),e.Li(),t}function jHe(e,n){var t,i;return e.Db>>16==17?e.Cb.Qh(e,21,vf,n):(i=Oc(u(An((t=u(Un(e,16),29),t||e.fi()),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function bxn(e){var n,t,i,r;for(jn(),Cr(e.c,e.a),r=new L(e.c);r.at.a.c.length))throw $(new Gn("index must be >= 0 and <= layer node count"));e.c&&qo(e.c.a,e),e.c=t,t&&Pb(t.a,n,e)}function OHe(e,n){this.c=new pt,this.a=e,this.b=n,this.d=u(T(e,(me(),Lv)),316),ue(T(e,(Ne(),o4e)))===ue((rO(),VJ))?this.e=new vxe:this.e=new mxe}function yxn(e,n){var t,i,r,c;for(c=0,i=new L(e);i.a0?n:0),++t;return new je(i,r)}function kxn(e,n){var t,i;for(e.b=0,e.d=new $P,i=new L(n.a);i.a>16==6?e.Cb.Qh(e,6,pr,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),bG)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function Hde(e,n){var t,i;return e.Db>>16==7?e.Cb.Qh(e,1,VD,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),L8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function Gde(e,n){var t,i;return e.Db>>16==9?e.Cb.Qh(e,9,Jt,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),$8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function IHe(e,n){var t,i;return e.Db>>16==5?e.Cb.Qh(e,9,EG,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),Zd)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function _He(e,n){var t,i;return e.Db>>16==7?e.Cb.Qh(e,6,xa,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),n0)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function qde(e,n){var t,i;return e.Db>>16==3?e.Cb.Qh(e,0,QD,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),Wd)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function Ude(e,n){var t,i;return e.Db>>16==3?e.Cb.Qh(e,12,Jt,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),_8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function xxn(e,n,t){var i,r,c;for(t<0&&(t=0),c=e.i,r=t;r$Z)return w8(e,i);if(i==e)return!0}}return!1}function Mxn(e){switch(F$(),e.q.g){case 5:kqe(e,(De(),Xn)),kqe(e,bt);break;case 4:MUe(e,(De(),Xn)),MUe(e,bt);break;default:CVe(e,(De(),Xn)),CVe(e,bt)}}function Txn(e){switch(F$(),e.q.g){case 5:zqe(e,(De(),et)),zqe(e,Kn);break;case 4:BJe(e,(De(),et)),BJe(e,Kn);break;default:OVe(e,(De(),et)),OVe(e,Kn)}}function Cxn(e){var n,t;n=u(T(e,(Gf(),jtn)),15),n?(t=n.a,t==0?he(e,(D0(),yJ),new vQ):he(e,(D0(),yJ),new XR(t))):he(e,(D0(),yJ),new XR(1))}function Oxn(e,n){var t;switch(t=e.i,n.g){case 1:return-(e.n.b+e.o.b);case 2:return e.n.a-t.o.a;case 3:return e.n.b-t.o.b;case 4:return-(e.n.a+e.o.a)}return 0}function Nxn(e,n){switch(e.g){case 0:return n==(Xs(),V1)?zJ:FJ;case 1:return n==(Xs(),V1)?zJ:WN;case 2:return n==(Xs(),V1)?WN:FJ;default:return WN}}function $O(e,n){var t,i,r;for(qo(e.a,n),e.e-=n.r+(e.a.c.length==0?0:e.c),r=Gee,i=new L(e.a);i.a>16==11?e.Cb.Qh(e,10,Jt,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),P8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function LHe(e,n){var t,i;return e.Db>>16==10?e.Cb.Qh(e,11,vf,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),e0)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function PHe(e,n){var t,i;return e.Db>>16==10?e.Cb.Qh(e,12,yf,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),Fm)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function $He(e,n){var t,i,r,c,o;if(n)for(r=n.a.length,t=new Rb(r),o=(t.b-t.a)*t.c<0?(k0(),yb):new S0(t);o.Ob();)c=u(o.Pb(),15),i=B9(n,c.a),i&&kUe(e,i)}function Rxn(){nse();var e,n;for(cBn((x0(),Rn)),VRn(Rn),WQ(Rn),Q8e=(yn(),ih),n=new L(u7e);n.a>19,h=n.h>>19,f!=h?h-f:(r=e.h,l=n.h,r!=l?r-l:(i=e.m,o=n.m,i!=o?i-o:(t=e.l,c=n.l,t-c)))}function RHe(e,n,t){var i,r,c,o,l;for(r=e[t.g],l=new L(n.d);l.a0?e.b:0),++t;n.b=i,n.e=r}function BHe(e){var n,t,i;if(i=e.b,gMe(e.i,i.length)){for(t=i.length*2,e.b=oe(Qne,dN,308,t,0,1),e.c=oe(Qne,dN,308,t,0,1),e.f=t-1,e.i=0,n=e.a;n;n=n.c)qO(e,n,n);++e.g}}function XE(e,n){return e.b.a=k.Math.min(e.b.a,n.c),e.b.b=k.Math.min(e.b.b,n.d),e.a.a=k.Math.max(e.a.a,n.c),e.a.b=k.Math.max(e.a.b,n.d),Hn(e.c,n),!0}function zxn(e,n,t){var i;i=n.c.i,i.k==(zn(),dr)?(he(e,(me(),ja),u(T(i,ja),12)),he(e,gf,u(T(i,gf),12))):(he(e,(me(),ja),n.c),he(e,gf,t.d))}function p8(e,n,t){x8();var i,r,c,o,l,f;return o=n/2,c=t/2,i=k.Math.abs(e.a),r=k.Math.abs(e.b),l=1,f=1,i>o&&(l=o/i),r>c&&(f=c/r),A1(e,k.Math.min(l,f)),e}function Fxn(){Gz();var e,n;try{if(n=u(r0e((y0(),kf),c7),2075),n)return n}catch(t){if(t=sr(t),X(t,101))e=t,Ffe((Lt(),e));else throw $(t)}return new aU}function Jxn(){Gz();var e,n;try{if(n=u(r0e((y0(),kf),hf),2002),n)return n}catch(t){if(t=sr(t),X(t,101))e=t,Ffe((Lt(),e));else throw $(t)}return new uw}function Hxn(){J$e();var e,n;try{if(n=u(r0e((y0(),kf),gg),2084),n)return n}catch(t){if(t=sr(t),X(t,101))e=t,Ffe((Lt(),e));else throw $(t)}return new oT}function Gxn(e,n,t){var i,r;return r=e.e,e.e=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,4,r,n),t?t.lj(i):t=i),r!=n&&(n?t=D8(e,Oz(e,n),t):t=D8(e,e.a,t)),t}function zHe(){t$.call(this),this.e=-1,this.a=!1,this.p=Xr,this.k=-1,this.c=-1,this.b=-1,this.g=!1,this.f=-1,this.j=-1,this.n=-1,this.i=-1,this.d=-1,this.o=Xr}function qxn(e,n){var t,i,r;if(i=e.b.d.d,e.a||(i+=e.b.d.a),r=n.b.d.d,n.a||(r+=n.b.d.a),t=ki(i,r),t==0){if(!e.a&&n.a)return-1;if(!n.a&&e.a)return 1}return t}function Uxn(e,n){var t,i,r;if(i=e.b.b.d,e.a||(i+=e.b.b.a),r=n.b.b.d,n.a||(r+=n.b.b.a),t=ki(i,r),t==0){if(!e.a&&n.a)return-1;if(!n.a&&e.a)return 1}return t}function Xxn(e,n){var t,i,r;if(i=e.b.g.d,e.a||(i+=e.b.g.a),r=n.b.g.d,n.a||(r+=n.b.g.a),t=ki(i,r),t==0){if(!e.a&&n.a)return-1;if(!n.a&&e.a)return 1}return t}function Vde(){Vde=Y,ztn=Eo(qt(qt(qt(new or,(zr(),no),(Ur(),Q3e)),no,W3e),Pc,Z3e),Pc,z3e),Jtn=qt(qt(new or,no,I3e),no,F3e),Ftn=Eo(new or,Pc,H3e)}function Kxn(e){var n,t,i,r,c;for(n=u(T(e,(me(),ox)),92),c=e.n,i=n.Bc().Jc();i.Ob();)t=u(i.Pb(),318),r=t.i,r.c+=c.a,r.d+=c.b,t.c?hXe(t):dXe(t);he(e,ox,null)}function Vxn(e,n,t){var i,r;switch(r=e.b,i=r.d,n.g){case 1:return-i.d-t;case 2:return r.o.a+i.c+t;case 3:return r.o.b+i.a+t;case 4:return-i.b-t;default:return-1}}function FHe(e,n){var t,i;for(i=new L(n);i.a0&&(o=(c&oi)%e.d.length,r=W0e(e,o,c,n),r)?(l=r.ld(t),l):(i=e.ak(c,n,t),e.c.Ec(i),null)}function Wde(e,n){var t,i,r,c;switch(Dd(e,n).Il()){case 3:case 2:{for(t=fv(n),r=0,c=t.i;r=0;i--)if(bn(e[i].d,n)||bn(e[i].d,t)){e.length>=i+1&&e.splice(0,i+1);break}return e}function BO(e,n){var t;return uu(e)&&uu(n)&&(t=e/n,wN0&&(e.b+=2,e.a+=i):(e.b+=1,e.a+=k.Math.min(i,r))}function KHe(e,n){var t,i;if(i=!1,$r(n)&&(i=!0,T4(e,new y2(Pt(n)))),i||X(n,242)&&(i=!0,T4(e,(t=qK(u(n,242)),new k3(t)))),!i)throw $(new MX(U2e))}function hAn(e,n,t,i){var r,c,o;return r=new L1(e.e,1,10,(o=n.c,X(o,88)?u(o,29):(yn(),jf)),(c=t.c,X(c,88)?u(c,29):(yn(),jf)),Ld(e,n),!1),i?i.lj(r):i=r,i}function n0e(e){var n,t;switch(u(T(_r(e),(Ne(),Z5e)),420).g){case 0:return n=e.n,t=e.o,new je(n.a+t.a/2,n.b+t.b/2);case 1:return new wc(e.n);default:return null}}function zO(){zO=Y,YJ=new Lj(ma,0),Cve=new Lj("LEFTUP",1),Nve=new Lj("RIGHTUP",2),Tve=new Lj("LEFTDOWN",3),Ove=new Lj("RIGHTDOWN",4),sie=new Lj("BALANCED",5)}function dAn(e,n,t){var i,r,c;if(i=ki(e.a[n.p],e.a[t.p]),i==0){if(r=u(T(n,(me(),Ty)),16),c=u(T(t,Ty),16),r.Gc(t))return-1;if(c.Gc(n))return 1}return i}function bAn(e){switch(e.g){case 1:return new L_;case 2:return new Ik;case 3:return new R5;case 0:return null;default:throw $(new Gn(Qee+(e.f!=null?e.f:""+e.g)))}}function t0e(e,n,t){switch(n){case 1:!e.n&&(e.n=new pe(ju,e,1,7)),kt(e.n),!e.n&&(e.n=new pe(ju,e,1,7)),er(e.n,u(t,18));return;case 2:X9(e,Pt(t));return}E1e(e,n,t)}function i0e(e,n,t){switch(n){case 3:Dw(e,ne(re(t)));return;case 4:Iw(e,ne(re(t)));return;case 5:Os(e,ne(re(t)));return;case 6:Ns(e,ne(re(t)));return}t0e(e,n,t)}function gz(e,n,t){var i,r,c;c=(i=new yX,i),r=za(c,n,null),r&&r.mj(),Mo(c,t),Et((!e.c&&(e.c=new pe(vp,e,12,10)),e.c),c),Cd(c,0),O2(c,1),_d(c,!0),Id(c,!0)}function r0e(e,n){var t,i,r;return t=Dj(e.i,n),X(t,241)?(r=u(t,241),r.wi()==null,r.ti()):X(t,493)?(i=u(t,1999),r=i.b,r):null}function gAn(e,n,t,i){var r,c;return Nt(n),Nt(t),c=u(eE(e.d,n),15),ERe(!!c,"Row %s not in %s",n,e.e),r=u(eE(e.b,t),15),ERe(!!r,"Column %s not in %s",t,e.c),jze(e,c.a,r.a,i)}function wAn(e){var n,t,i,r,c,o;for(t=null,r=e,c=0,o=r.length;c1||l==-1?(c=u(f,16),r.Wb(Zjn(e,c))):r.Wb(zW(e,u(f,57)))))}function EAn(e,n,t,i){yMe();var r=Xne;function c(){for(var o=0;o0)return!1;return!0}function AAn(e){switch(u(T(e.b,(Ne(),U5e)),381).g){case 1:Zi(So(ou(new wn(null,new pn(e.d,16)),new Zg),new t_),new nM);break;case 2:iIn(e);break;case 0:YTn(e)}}function MAn(e,n,t){var i,r,c;for(i=t,!i&&(i=new i4),i.Tg("Layout",e.a.c.length),c=new L(e.a);c.aXee)return t;r>-1e-6&&++t}return t}function pz(e,n,t){if(X(n,271))return eNn(e,u(n,85),t);if(X(n,276))return Dxn(e,u(n,276),t);throw $(new Gn(u7+Fa(new Su(z(B(Ar,1),On,1,5,[n,t])))))}function mz(e,n,t){if(X(n,271))return nNn(e,u(n,85),t);if(X(n,276))return Ixn(e,u(n,276),t);throw $(new Gn(u7+Fa(new Su(z(B(Ar,1),On,1,5,[n,t])))))}function u0e(e,n){var t;n!=e.b?(t=null,e.b&&(t=IR(e.b,e,-4,t)),n&&(t=K4(n,e,-4,t)),t=pFe(e,n,t),t&&t.mj()):(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,3,n,n))}function WHe(e,n){var t;n!=e.f?(t=null,e.f&&(t=IR(e.f,e,-1,t)),n&&(t=K4(n,e,-1,t)),t=mFe(e,n,t),t&&t.mj()):(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,0,n,n))}function DAn(e,n,t,i){var r,c,o,l;return Fs(e.e)&&(r=n.Jk(),l=n.kd(),c=t.kd(),o=M0(e,1,r,l,c,r.Hk()?C8(e,r,c,X(r,103)&&(u(r,19).Bb&Ec)!=0):-1,!0),i?i.lj(o):i=o),i}function ZHe(e){var n,t,i;if(e==null)return null;if(t=u(e,16),t.dc())return"";for(i=new pd,n=t.Jc();n.Ob();)Bc(i,(Ei(),Pt(n.Pb()))),i.a+=" ";return kK(i,i.a.length-1)}function eGe(e){var n,t,i;if(e==null)return null;if(t=u(e,16),t.dc())return"";for(i=new pd,n=t.Jc();n.Ob();)Bc(i,(Ei(),Pt(n.Pb()))),i.a+=" ";return kK(i,i.a.length-1)}function IAn(e,n){var t,i,r,c,o;for(c=new L(n.a);c.a0&&ic(e,e.length-1)==33)try{return n=gUe(of(e,0,e.length-1)),n.e==null}catch(t){if(t=sr(t),!X(t,32))throw $(t)}return!1}function $An(e,n,t){var i,r,c;switch(i=_r(n),r=GB(i),c=new Qu,gu(c,n),t.g){case 1:xr(c,CO(q4(r)));break;case 2:xr(c,q4(r))}return he(c,(Ne(),vm),re(T(e,vm))),c}function o0e(e){var n,t;return n=u(tt(new qn(Vn(rr(e.a).a.Jc(),new ee))),17),t=u(tt(new qn(Vn(Ni(e.a).a.Jc(),new ee))),17),Re($e(T(n,(me(),Hd))))||Re($e(T(t,Hd)))}function z2(){z2=Y,ZN=new oC("ONE_SIDE",0),GJ=new oC("TWO_SIDES_CORNER",1),qJ=new oC("TWO_SIDES_OPPOSING",2),HJ=new oC("THREE_SIDES",3),JJ=new oC("FOUR_SIDES",4)}function iGe(e,n){var t,i,r,c;for(c=new Ce,r=0,i=n.Jc();i.Ob();){for(t=ve(u(i.Pb(),15).a+r);t.a=e.f)break;Hn(c.c,t)}return c}function RAn(e){var n,t;for(t=new L(e.e.b);t.a0&&SHe(this,this.c-1,(De(),et)),this.c0&&e[0].length>0&&(this.c=Re($e(T(_r(e[0][0]),(me(),Xve))))),this.a=oe(don,Se,2079,e.length,0,2),this.b=oe(bon,Se,2080,e.length,0,2),this.d=new aFe}function JAn(e){return e.c.length==0?!1:(vn(0,e.c.length),u(e.c[0],17)).c.i.k==(zn(),dr)?!0:G3(So(new wn(null,new pn(e,16)),new kk),new o_)}function uGe(e,n){var t,i,r,c,o,l,f;for(l=q2(n),c=n.f,f=n.g,o=k.Math.sqrt(c*c+f*f),r=0,i=new L(l);i.a=0?(t=BO(e,tF),i=xQ(e,tF)):(n=Bb(e,1),t=BO(n,5e8),i=xQ(n,5e8),i=mc(Gh(i,1),Rr(e,1))),hh(Gh(i,32),Rr(t,Ic))}function eMn(e,n,t,i){var r,c,o,l,f;for(r=null,c=0,l=new L(n);l.a1;n>>=1)(n&1)!=0&&(i=H3(i,t)),t.d==1?t=H3(t,t):t=new xJe(ZXe(t.a,t.d,oe($t,ni,30,t.d<<1,15,1)));return i=H3(i,t),i}function w0e(){w0e=Y;var e,n,t,i;for(qme=oe(Jr,Jc,30,25,15,1),Ume=oe(Jr,Jc,30,33,15,1),i=152587890625e-16,n=32;n>=0;n--)Ume[n]=i,i*=.5;for(t=1,e=24;e>=0;e--)qme[e]=t,t*=.5}function cMn(e){var n,t;if(Re($e(ye(e,(Ne(),pm))))){for(t=new qn(Vn(H0(e).a.Jc(),new ee));ht(t);)if(n=u(tt(t),85),Hw(n)&&Re($e(ye(n,kg))))return!0}return!1}function lGe(e){var n,t,i,r;for(n=new Si,t=new Si,r=St(e,0);r.b!=r.d.c;)i=u(jt(r),12),i.e.c.length==0?Xi(t,i,t.c.b,t.c):Xi(n,i,n.c.b,n.c);return Ks(n).Fc(t),n}function fGe(e,n){var t,i,r;hr(e.f,n)&&(n.b=e,i=n.c,wu(e.j,i,0)!=-1||Te(e.j,i),r=n.d,wu(e.j,r,0)!=-1||Te(e.j,r),t=n.a.b,t.c.length!=0&&(!e.i&&(e.i=new CJe(e)),_7n(e.i,t)))}function uMn(e){var n,t,i,r,c;return t=e.c.d,i=t.j,r=e.d.d,c=r.j,i==c?t.p=0&&bn(e.substr(n,3),"GMT")||n>=0&&bn(e.substr(n,3),"UTC"))&&(t[0]=n+3),Zbe(e,t,i)}function sMn(e,n){var t,i,r,c,o;for(c=e.g.a,o=e.g.b,i=new L(e.d);i.at;c--)e[c]|=n[c-t-1]>>>o,e[c-1]=n[c-t-1]<0&&Wu(e.g,n,e.g,n+i,l),o=t.Jc(),e.i+=i,r=0;r>4&15,c=e[i]&15,o[r++]=R8e[t],o[r++]=R8e[c];return gh(o,0,o.length)}function Xo(e){var n,t;return e>=Ec?(n=pN+(e-Ec>>10&1023)&yr,t=56320+(e-Ec&1023)&yr,String.fromCharCode(n)+(""+String.fromCharCode(t))):String.fromCharCode(e&yr)}function mMn(e,n){h2();var t,i,r,c;return r=u(u(pi(e.r,n),22),83),r.gc()>=2?(i=u(r.Jc().Pb(),115),t=e.u.Gc((ps(),nA)),c=e.u.Gc(qy),!i.a&&!t&&(r.gc()==2||c)):!1}function bGe(e,n,t,i,r){var c,o,l;for(c=rXe(e,n,t,i,r),l=!1;!c;)Tz(e,r,!0),l=!0,c=rXe(e,n,t,i,r);l&&Tz(e,r,!1),o=YY(r),o.c.length!=0&&(e.d&&e.d.Fg(o),bGe(e,r,t,i,o))}function kz(){kz=Y,Fre=new v$("NODE_SIZE_REORDERER",0),Rre=new v$("INTERACTIVE_NODE_REORDERER",1),zre=new v$("MIN_SIZE_PRE_PROCESSOR",2),Bre=new v$("MIN_SIZE_POST_PROCESSOR",3)}function jz(){jz=Y,Mce=new zj(ma,0),c8e=new zj("DIRECTED",1),o8e=new zj("UNDIRECTED",2),i8e=new zj("ASSOCIATION",3),u8e=new zj("GENERALIZATION",4),r8e=new zj("DEPENDENCY",5)}function vMn(e,n){var t;if(!Ia(e))throw $(new Uc(BWe));switch(t=Ia(e),n.g){case 1:return-(e.j+e.f);case 2:return e.i-t.g;case 3:return e.j-t.f;case 4:return-(e.i+e.g)}return 0}function yMn(e,n,t){var i,r,c;return i=n.Jk(),c=n.kd(),r=i.Hk()?M0(e,4,i,c,null,C8(e,i,c,X(i,103)&&(u(i,19).Bb&Ec)!=0),!0):M0(e,i.rk()?2:1,i,c,i.gk(),-1,!0),t?t.lj(r):t=r,t}function v8(e,n){var t,i;for(_n(n),i=e.b.c.length,Te(e.b,n);i>0;){if(t=i,i=(i-1)/2|0,e.a.Le(Le(e.b,i),n)<=0)return ol(e.b,t,n),!0;ol(e.b,t,Le(e.b,i))}return ol(e.b,i,n),!0}function v0e(e,n,t,i){var r,c;if(r=0,t)r=BB(e.a[t.g][n.g],i);else for(c=0;c=l)}function gGe(e){switch(e.g){case 0:return new U_;case 1:return new _M;default:throw $(new Gn("No implementation is available for the width approximator "+(e.f!=null?e.f:""+e.g)))}}function y0e(e,n,t,i){var r;if(r=!1,$r(i)&&(r=!0,T9(n,t,Pt(i))),r||o2(i)&&(r=!0,y0e(e,n,t,i)),r||X(i,242)&&(r=!0,Hb(n,t,u(i,242))),!r)throw $(new MX(U2e))}function jMn(e,n){var t,i,r;if(t=n.ni(e.a),t&&(r=wa((!t.b&&(t.b=new Hs((yn(),Ac),Iu,t)),t.b),af),r!=null)){for(i=1;i<(ls(),s7e).length;++i)if(bn(s7e[i],r))return i}return 0}function EMn(e,n){var t,i,r;if(t=n.ni(e.a),t&&(r=wa((!t.b&&(t.b=new Hs((yn(),Ac),Iu,t)),t.b),af),r!=null)){for(i=1;i<(ls(),l7e).length;++i)if(bn(l7e[i],r))return i}return 0}function wGe(e,n){var t,i,r,c;if(_n(n),c=e.a.gc(),c0?1:0;c.a[r]!=t;)c=c.a[r],r=e.a.Le(t.d,c.d)>0?1:0;c.a[r]=i,i.b=t.b,i.a[0]=t.a[0],i.a[1]=t.a[1],t.a[0]=null,t.a[1]=null}function AMn(e){var n,t,i,r;for(n=new Ce,t=oe(ts,pa,30,e.a.c.length,16,1),$fe(t,t.length),r=new L(e.a);r.a0&&KXe((vn(0,t.c.length),u(t.c[0],25)),e),t.c.length>1&&KXe(u(Le(t,t.c.length-1),25),e),n.Ug()}function TMn(e){ps();var n,t;return n=Mi(Z1,z(B(sG,1),ke,280,0,[gb])),!(gO(_R(n,e))>1||(t=Mi(nA,z(B(sG,1),ke,280,0,[eA,qy])),gO(_R(t,e))>1))}function j0e(e,n){var t;t=lo((y0(),kf),e),X(t,493)?Kc(kf,e,new VTe(this,n)):Kc(kf,e,this),dW(this,n),n==(d9(),Y8e)?(this.wb=u(this,2e3),u(n,2002)):this.wb=(x0(),Rn)}function CMn(e){var n,t,i;if(e==null)return null;for(n=null,t=0;tc}function yGe(e,n){var t,i,r;if(S0e(e,n))return!0;for(i=new L(n);i.a=r||n<0)throw $(new jo(Mne+n+dg+r));if(t>=r||t<0)throw $(new jo(Tne+t+dg+r));return n!=t?i=(c=e.Aj(t),e.oj(n,c),c):i=e.vj(t),i}function jGe(e){var n,t,i;if(i=e,e)for(n=0,t=e.Bh();t;t=t.Bh()){if(++n>$Z)return jGe(t);if(i=t,t==e)throw $(new Uc("There is a cycle in the containment hierarchy of "+e))}return i}function Fa(e){var n,t,i;for(i=new Qb(Co,"[","]"),t=e.Jc();t.Ob();)n=t.Pb(),I1(i,ue(n)===ue(e)?"(this Collection)":n==null?Vo:su(n));return i.a?i.e.length==0?i.a.a:i.a.a+(""+i.e):i.c}function S0e(e,n){var t,i;if(i=!1,n.gc()<2)return!1;for(t=0;t1&&(e.j.b+=e.e)):(e.j.a+=t.a,e.j.b=k.Math.max(e.j.b,t.b),e.d.c.length>1&&(e.j.a+=e.e))}function F0(){F0=Y,Tin=z(B(xc,1),qu,64,0,[(De(),Xn),et,bt]),Min=z(B(xc,1),qu,64,0,[et,bt,Kn]),Cin=z(B(xc,1),qu,64,0,[bt,Kn,Xn]),Oin=z(B(xc,1),qu,64,0,[Kn,Xn,et])}function SGe(e){var n,t,i,r,c,o,l,f,h;for(this.a=XJe(e),this.b=new Ce,t=e,i=0,r=t.length;izK(e.d).c?(e.i+=e.g.c,TQ(e.d)):zK(e.d).c>zK(e.g).c?(e.e+=e.d.c,TQ(e.g)):(e.i+=EDe(e.g),e.e+=EDe(e.d),TQ(e.g),TQ(e.d))}function RMn(e,n,t){var i,r,c,o;for(c=n.q,o=n.r,new Gb((ha(),sb),n,c,1),new Gb(sb,c,o,1),r=new L(t);r.al&&(f=l/i),r>c&&(h=c/r),o=k.Math.min(f,h),e.a+=o*(n.a-e.a),e.b+=o*(n.b-e.b)}function JMn(e,n,t,i,r){var c,o;for(o=!1,c=u(Le(t.b,0),26);q_n(e,n,c,i,r)&&(o=!0,TAn(t,c),t.b.c.length!=0);)c=u(Le(t.b,0),26);return t.b.c.length==0&&$O(t.j,t),o&&hz(n.q),o}function A0e(e,n,t,i){var r,c;return t==0?(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),U$(e.o,n,i)):(c=u(An((r=u(Un(e,16),29),r||e.fi()),t),69),c.uk().yk(e,Lo(e),t-dt(e.fi()),n,i))}function dW(e,n){var t;n!=e.sb?(t=null,e.sb&&(t=u(e.sb,52).Qh(e,1,rA,t)),n&&(t=u(n,52).Oh(e,1,rA,t)),t=B1e(e,n,t),t&&t.mj()):(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,4,n,n))}function TGe(e,n){var t,i,r,c;if(n)r=$1(n,"x"),t=new aSe(e),X3(t.a,(_n(r),r)),c=$1(n,"y"),i=new hSe(e),K3(i.a,(_n(c),c));else throw $(new oh("All edge sections need an end point."))}function CGe(e,n){var t,i,r,c;if(n)r=$1(n,"x"),t=new sSe(e),V3(t.a,(_n(r),r)),c=$1(n,"y"),i=new lSe(e),Y3(i.a,(_n(c),c));else throw $(new oh("All edge sections need a start point."))}function HMn(e,n){var t,i,r,c,o,l,f;for(i=Vze(e),c=0,l=i.length;c>22-n,r=e.h<>22-n):n<44?(t=0,i=e.l<>44-n):(t=0,i=0,r=e.l<=Rd?"error":i>=900?"warn":i>=800?"info":"log"),wIe(t,e.a),e.b&&Mbe(n,t,e.b,"Exception: ",!0))}function IGe(e,n){var t,i,r,c,o;for(r=n==1?Mte:Ate,i=r.a.ec().Jc();i.Ob();)for(t=u(i.Pb(),86),o=u(pi(e.f.c,t),22).Jc();o.Ob();)c=u(o.Pb(),49),Te(e.b.b,u(c.b,82)),Te(e.b.a,u(c.b,82).d)}function _Ge(e,n,t,i){var r,c,o,l,f;switch(f=e.b,c=n.d,o=c.j,l=xde(o,f.d[o.g],t),r=gi(pc(c.n),c.a),c.j.g){case 3:case 1:l.a+=r.a;break;case 2:l.b+=r.b;break;case 4:l.b+=r.b}Xi(i,l,i.c.b,i.c)}function XMn(e,n){var t,i,r,c;for(c=n.b.j,e.a=oe($t,ni,30,c.c.length,15,1),r=0,i=0;ie)throw $(new Gn("k must be smaller than n"));return n==0||n==e?1:e==0?0:Zde(e)/(Zde(n)*Zde(e-n))}function M0e(e,n){var t,i,r,c;for(t=new AK(e);t.g==null&&!t.c?mae(t):t.g==null||t.i!=0&&u(t.g[t.i-1],50).Ob();)if(c=u(Cz(t),57),X(c,174))for(i=u(c,174),r=0;r>4],n[t*2+1]=TG[c&15];return gh(n,0,n.length)}function uTn(e){var n,t,i;switch(i=e.c.length,i){case 0:return CV(),Xen;case 1:return n=u(wqe(new L(e)),45),Bpn(n.jd(),n.kd());default:return t=u(Ra(e,oe(wg,eF,45,e.c.length,0,1)),175),new ise(t)}}function Pd(e,n){switch(n.g){case 1:return j4(e.j,(ss(),x3e));case 2:return j4(e.j,(ss(),E3e));case 3:return j4(e.j,(ss(),M3e));case 4:return j4(e.j,(ss(),T3e));default:return jn(),jn(),Sc}}function oTn(e,n){var t,i,r;t=_3n(n,e.e),i=u(Bn(e.g.f,t),15).a,r=e.a.c.length-1,e.a.c.length!=0&&u(Le(e.a,r),295).c==i?(++u(Le(e.a,r),295).a,++u(Le(e.a,r),295).b):Te(e.a,new MOe(i))}function J0(){J0=Y,eln=(Xt(),Fy),nln=Vd,Ysn=Tg,Qsn=Yv,Wsn=ab,Vsn=Vv,Vye=LD,Zsn=Nm,Dre=(Gbe(),Rsn),Ire=Bsn,Qye=Hsn,_re=Usn,Wye=Gsn,Zye=qsn,Yye=zsn,FH=Fsn,JH=Jsn,SD=Xsn,e6e=Ksn,Kye=$sn}function PGe(e,n){var t,i,r,c,o;if(e.e<=n||dyn(e,e.g,n))return e.g;for(c=e.r,i=e.g,o=e.r,r=(c-i)/2+i;i+11&&(e.e.b+=e.a)):(e.e.a+=t.a,e.e.b=k.Math.max(e.e.b,t.b),e.d.c.length>1&&(e.e.a+=e.a))}function fTn(e){var n,t,i,r;switch(r=e.i,n=r.b,i=r.j,t=r.g,r.a.g){case 0:t.a=(e.g.b.o.a-i.a)/2;break;case 1:t.a=n.d.n.a+n.d.a.a;break;case 2:t.a=n.d.n.a+n.d.a.a-i.a;break;case 3:t.b=n.d.n.b+n.d.a.b}}function aTn(e,n,t){var i,r,c;for(r=new qn(Vn(bh(t).a.Jc(),new ee));ht(r);)i=u(tt(r),17),!cc(i)&&!(!cc(i)&&i.c.i.c==i.d.i.c)&&(c=OUe(e,i,t,new pxe),c.c.length>1&&Hn(n.c,c))}function BGe(e,n,t,i,r){if(ii&&(e.a=i),e.br&&(e.b=r),e}function hTn(e){if(X(e,144))return INn(u(e,144));if(X(e,233))return Gjn(u(e,233));if(X(e,21))return qMn(u(e,21));throw $(new Gn(u7+Fa(new Su(z(B(Ar,1),On,1,5,[e])))))}function dTn(e,n,t,i,r){var c,o,l;for(c=!0,o=0;o>>r|t[o+i+1]<>>r,++o}return c}function N0e(e,n,t,i){var r,c,o;if(n.k==(zn(),dr)){for(c=new qn(Vn(rr(n).a.Jc(),new ee));ht(c);)if(r=u(tt(c),17),o=r.c.i.k,o==dr&&e.c.a[r.c.i.c.p]==i&&e.c.a[n.c.p]==t)return!0}return!1}function bTn(e,n){var t,i,r,c;return n&=63,t=e.h&G1,n<22?(c=t>>>n,r=e.m>>n|t<<22-n,i=e.l>>n|e.m<<22-n):n<44?(c=0,r=t>>>n-22,i=e.m>>n-22|e.h<<44-n):(c=0,r=0,i=t>>>n-44),_o(i&Ls,r&Ls,c&G1)}function zGe(e,n,t,i){var r;this.b=i,this.e=e==(eg(),Mx),r=n[t],this.d=g2(ts,[Se,pa],[171,30],16,[r.length,r.length],2),this.a=g2($t,[Se,ni],[54,30],15,[r.length,r.length],2),this.c=new h0e(n,t)}function gTn(e){var n,t,i;for(e.k=new Sae((De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])).length,e.j.c.length),i=new L(e.j);i.a=t)return k8(e,n,i.p),!0;return!1}function uv(e,n,t,i){var r,c,o,l,f,h;for(o=t.length,c=0,r=-1,h=qRe((Yn(n,e.length+1),e.substr(n)),(KK(),Hme)),l=0;lc&&xvn(h,qRe(t[l],Hme))&&(r=l,c=f);return r>=0&&(i[0]=n+c),r}function mTn(e,n,t){var i,r,c,o,l,f,h,b;c=e.d.p,l=c.e,f=c.r,e.g=new CC(f),o=e.d.o.c.p,i=o>0?l[o-1]:oe(c1,Bd,9,0,0,1),r=l[o],h=ot?F0e(e,t,"start index"):n<0||n>t?F0e(n,t,"end index"):rS("end index (%s) must not be less than start index (%s)",z(B(Ar,1),On,1,5,[ve(n),ve(e)]))}function qGe(e,n){var t,i,r,c;for(i=0,r=e.length;i0&&UGe(e,c,t));n.p=0}function jTn(e){var n,t,i,r;for(n=Fb(Kt(new il("Predicates."),"and"),40),t=!0,r=new qc(e);r.b=0?e.hi(r):q0e(e,i);else throw $(new Gn(W0+i.ve()+_S));else throw $(new Gn(YWe+n+QWe));else Fl(e,t,i)}function D0e(e){var n,t;if(t=null,n=!1,X(e,210)&&(n=!0,t=u(e,210).a),n||X(e,265)&&(n=!0,t=""+u(e,265).a),n||X(e,479)&&(n=!0,t=""+u(e,479).a),!n)throw $(new MX(U2e));return t}function I0e(e,n,t){var i,r,c,o,l,f;for(f=Po(e.e.Ah(),n),i=0,l=e.i,r=u(e.g,122),o=0;o=e.d.b.c.length&&(n=new Xu(e.d),n.p=i.p-1,Te(e.d.b,n),t=new Xu(e.d),t.p=i.p,Te(e.d.b,t)),Or(i,u(Le(e.d.b,i.p),25))}function xTn(e){var n,t,i,r;for(t=new Si,fc(t,e.o),i=new $P;t.b!=0;)n=u(t.b==0?null:(at(t.b!=0),$l(t,t.a.a)),500),r=PVe(e,n,!0),r&&Te(i.a,n);for(;i.a.c.length!=0;)n=u(C1e(i),500),PVe(e,n,!1)}function Ge(e){var n;this.c=new Si,this.f=e.e,this.e=e.d,this.i=e.g,this.d=e.c,this.b=e.b,this.k=e.j,this.a=e.a,e.i?this.j=e.i:this.j=(n=u(sa(Qa),10),new _l(n,u(_f(n,n.length),10),0)),this.g=e.f}function cg(){cg=Y,o9e=new h4(vS,0),Sr=new h4("BOOLEAN",1),hc=new h4("INT",2),By=new h4("STRING",3),ec=new h4("DOUBLE",4),Ri=new h4("ENUM",5),Ry=new h4("ENUMSET",6),Wa=new h4("OBJECT",7)}function VE(e,n){var t,i,r,c,o;i=k.Math.min(e.c,n.c),c=k.Math.min(e.d,n.d),r=k.Math.max(e.c+e.b,n.c+n.b),o=k.Math.max(e.d+e.a,n.d+n.a),r=(r/2|0))for(this.e=i?i.c:null,this.d=r;t++0;)She(this);this.b=n,this.a=null}function TTn(e,n){var t,i;n.a?QNn(e,n):(t=u(RX(e.b,n.b),60),t&&t==e.a[n.b.f]&&t.a&&t.a!=n.b.a&&t.c.Ec(n.b),i=u($X(e.b,n.b),60),i&&e.a[i.f]==n.b&&i.a&&i.a!=n.b.a&&n.b.c.Ec(i),DK(e.b,n.b))}function ZGe(e,n){var t,i;if(t=u(zc(e.b,n),127),u(u(pi(e.r,n),22),83).dc()){t.n.b=0,t.n.c=0;return}t.n.b=e.C.b,t.n.c=e.C.c,e.A.Gc((Vs(),Og))&&CXe(e,n),i=dSn(e,n),OW(e,n)==(nv(),db)&&(i+=2*e.w),t.a.a=i}function eqe(e,n){var t,i;if(t=u(zc(e.b,n),127),u(u(pi(e.r,n),22),83).dc()){t.n.d=0,t.n.a=0;return}t.n.d=e.C.d,t.n.a=e.C.a,e.A.Gc((Vs(),Og))&&OXe(e,n),i=hSn(e,n),OW(e,n)==(nv(),db)&&(i+=2*e.w),t.a.b=i}function CTn(e,n){var t,i,r,c;for(c=new Ce,i=new L(n);i.ai&&(Yn(n-1,e.length),e.charCodeAt(n-1)<=32);)--n;return i>0||nt.a&&(i.Gc((rg(),Gx))?r=(n.a-t.a)/2:i.Gc(qx)&&(r=n.a-t.a)),n.b>t.b&&(i.Gc((rg(),Xx))?c=(n.b-t.b)/2:i.Gc(Ux)&&(c=n.b-t.b)),k0e(e,r,c)}function cqe(e,n,t,i,r,c,o,l,f,h,b,p,y){X(e.Cb,88)&&H2(Ms(u(e.Cb,88)),4),Mo(e,t),e.f=o,l8(e,l),f8(e,f),o8(e,h),s8(e,b),_d(e,p),a8(e,y),Id(e,!0),Cd(e,r),e.Xk(c),ng(e,n),i!=null&&(e.i=null,EB(e,i))}function F0e(e,n,t){if(e<0)return rS(uYe,z(B(Ar,1),On,1,5,[t,ve(e)]));if(n<0)throw $(new Gn(oYe+n));return rS("%s (%s) must not be greater than size (%s)",z(B(Ar,1),On,1,5,[t,ve(e),ve(n)]))}function J0e(e,n,t,i,r,c){var o,l,f,h;if(o=i-t,o<7){$jn(n,t,i,c);return}if(f=t+r,l=i+r,h=f+(l-f>>1),J0e(n,e,f,h,-r,c),J0e(n,e,h,l,-r,c),c.Le(e[h-1],e[h])<=0){for(;t=0?e.$h(c,t):ybe(e,r,t);else throw $(new Gn(W0+r.ve()+_S));else throw $(new Gn(YWe+n+QWe));else Jl(e,i,r,t)}function uqe(e){var n,t;if(e.f){for(;e.n>0;){if(n=u(e.k.Xb(e.n-1),75),t=n.Jk(),X(t,103)&&(u(t,19).Bb&Bu)!=0&&(!e.e||t.nk()!=U7||t.Jj()!=0)&&n.kd()!=null)return!0;--e.n}return!1}else return e.n>0}function oqe(e){var n,t,i,r;if(t=u(e,52).Yh(),t)try{if(i=null,n=E8((y0(),kf),WXe(qjn(t))),n&&(r=n.Zh(),r&&(i=r.Dl(Ibn(t.e)))),i&&i!=e)return oqe(i)}catch(c){if(c=sr(c),!X(c,63))throw $(c)}return e}function qTn(e,n,t){var i,r,c;t.Tg("Remove overlaps",1),t.bh(n,Ype),i=u(ye(n,(B3(),Gv)),26),e.f=i,e.a=$Q(u(ye(n,(J0(),SD)),303)),r=re(ye(n,(Xt(),Vd))),Q5(e,(_n(r),r)),c=q2(i),vVe(e,n,c,t),t.bh(n,_F)}function UTn(e){var n,t,i;if(Re($e(ye(e,(Xt(),ID))))){for(i=new Ce,t=new qn(Vn(H0(e).a.Jc(),new ee));ht(t);)n=u(tt(t),85),Hw(n)&&Re($e(ye(n,gce)))&&Hn(i.c,n);return i}else return jn(),jn(),Sc}function sqe(e){if(!e)return eAe(),Wen;var n=e.valueOf?e.valueOf():e;if(n!==e){var t=ite[typeof n];return t?t(n):F1e(typeof n)}else return e instanceof Array||e instanceof k.Array?new n9(e):new i9(e)}function lqe(e,n,t){var i,r,c;switch(c=e.o,i=u(zc(e.p,t),253),r=i.i,r.b=QE(i),r.a=YE(i),r.b=k.Math.max(r.b,c.a),r.b>c.a&&!n&&(r.b=c.a),r.c=-(r.b-c.a)/2,t.g){case 1:r.d=-r.a;break;case 3:r.d=c.b}HW(i),GW(i)}function fqe(e,n,t){var i,r,c;switch(c=e.o,i=u(zc(e.p,t),253),r=i.i,r.b=QE(i),r.a=YE(i),r.a=k.Math.max(r.a,c.b),r.a>c.b&&!n&&(r.a=c.b),r.d=-(r.a-c.b)/2,t.g){case 4:r.c=-r.b;break;case 2:r.c=c.a}HW(i),GW(i)}function XTn(e,n){var t,i,r;return X(n.g,9)&&u(n.g,9).k==(zn(),wr)?Ki:(r=I4(n),r?k.Math.max(0,e.b/2-.5):(t=J3(n),t?(i=ne(re($2(t,(Ne(),xg)))),k.Math.max(0,i/2-.5)):Ki))}function KTn(e,n){var t,i,r;return X(n.g,9)&&u(n.g,9).k==(zn(),wr)?Ki:(r=I4(n),r?k.Math.max(0,e.b/2-.5):(t=J3(n),t?(i=ne(re($2(t,(Ne(),xg)))),k.Math.max(0,i/2-.5)):Ki))}function VTn(e,n){var t,i,r,c,o;if(!n.dc()){if(r=u(n.Xb(0),132),n.gc()==1){KUe(e,r,r,1,0,n);return}for(t=1;t0)try{r=hl(n,Xr,oi)}catch(c){throw c=sr(c),X(c,131)?(i=c,$(new uB(i))):$(c)}return t=(!e.a&&(e.a=new hX(e)),e.a),r=0?u(K(t,r),57):null}function WTn(e,n){if(e<0)return rS(uYe,z(B(Ar,1),On,1,5,["index",ve(e)]));if(n<0)throw $(new Gn(oYe+n));return rS("%s (%s) must be less than size (%s)",z(B(Ar,1),On,1,5,["index",ve(e),ve(n)]))}function ZTn(e){var n,t,i,r,c;if(e==null)return Vo;for(c=new Qb(Co,"[","]"),t=e,i=0,r=t.length;i=0?e.Ih(t,!0,!0):Gw(e,r,!0),163)),u(i,219).Xl(n);else throw $(new Gn(W0+n.ve()+_S))}function U0e(e){var n,t;return e>-0x800000000000&&e<0x800000000000?e==0?0:(n=e<0,n&&(e=-e),t=sc(k.Math.floor(k.Math.log(e)/.6931471805599453)),(!n||e!=k.Math.pow(2,t))&&++t,t):CFe(Pu(e))}function fCn(e){var n,t,i,r,c,o,l;for(c=new zh,t=new L(e);t.a2&&l.e.b+l.j.b<=2&&(r=l,i=o),c.a.yc(r,c),r.q=i);return c}function aCn(e,n,t){t.Tg("Eades radial",1),t.bh(n,_F),e.d=u(ye(n,(B3(),Gv)),26),e.c=ne(re(ye(n,(J0(),JH)))),e.e=$Q(u(ye(n,SD),303)),e.a=Yjn(u(ye(n,e6e),426)),e.b=bAn(u(ye(n,Yye),354)),Wxn(e),t.bh(n,_F)}function hCn(e,n){if(n.Tg("Target Width Setter",1),da(e,(Ja(),Xre)))ji(e,(Yh(),Mm),re(ye(e,Xre)));else throw $(new wd("A target width has to be set if the TargetWidthWidthApproximator should be used."));n.Ug()}function pqe(e,n){var t,i,r;return i=new Ba(e),$u(i,n),he(i,(me(),iH),n),he(i,(Ne(),Wi),(Br(),to)),he(i,Ch,(Vh(),eG)),Tf(i,(zn(),wr)),t=new Qu,gu(t,i),xr(t,(De(),Kn)),r=new Qu,gu(r,i),xr(r,et),i}function mqe(e,n){var t,i,r,c,o;for(e.c[n.p]=!0,Te(e.a,n),o=new L(n.j);o.a=c)o.$b();else for(r=o.Jc(),i=0;i0?Noe():o<0&&Eqe(e,n,-o),!0):!1}function YE(e){var n,t,i,r,c,o,l;if(l=0,e.b==0){for(o=nHe(e,!0),n=0,i=o,r=0,c=i.length;r0&&(l+=t,++n);n>1&&(l+=e.c*(n-1))}else l=oAe(JY(k2(si(mV(e.a),new Bg),new c6)));return l>0?l+e.n.d+e.n.a:0}function QE(e){var n,t,i,r,c,o,l;if(l=0,e.b==0)l=oAe(JY(k2(si(mV(e.a),new r6),new zg)));else{for(o=tHe(e,!0),n=0,i=o,r=0,c=i.length;r0&&(l+=t,++n);n>1&&(l+=e.c*(n-1))}return l>0?l+e.n.b+e.n.c:0}function mCn(e){var n,t;if(e.c.length!=2)throw $(new Uc("Order only allowed for two paths."));n=(vn(0,e.c.length),u(e.c[0],17)),t=(vn(1,e.c.length),u(e.c[1],17)),n.d.i!=t.c.i&&(e.c.length=0,Hn(e.c,t),Hn(e.c,n))}function Sqe(e,n,t){var i;for(ww(t,n.g,n.f),Dl(t,n.i,n.j),i=0;i<(!n.a&&(n.a=new pe(Jt,n,10,11)),n.a).i;i++)Sqe(e,u(K((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a),i),26),u(K((!t.a&&(t.a=new pe(Jt,t,10,11)),t.a),i),26))}function vCn(e,n){var t,i,r,c;for(c=u(zc(e.b,n),127),t=c.a,r=u(u(pi(e.r,n),22),83).Jc();r.Ob();)i=u(r.Pb(),115),i.c&&(t.a=k.Math.max(t.a,wfe(i.c)));if(t.a>0)switch(n.g){case 2:c.n.c=e.s;break;case 4:c.n.b=e.s}}function yCn(e,n){var t,i,r;return t=u(T(n,(Gf(),ky)),15).a-u(T(e,ky),15).a,t==0?(i=Nr(pc(u(T(e,(D0(),KN)),8)),u(T(e,WS),8)),r=Nr(pc(u(T(n,KN),8)),u(T(n,WS),8)),ki(i.a*i.b,r.a*r.b)):t}function kCn(e,n){var t,i,r;return t=u(T(n,(Mu(),$H)),15).a-u(T(e,$H),15).a,t==0?(i=Nr(pc(u(T(e,(Ti(),kD)),8)),u(T(e,_7),8)),r=Nr(pc(u(T(n,kD),8)),u(T(n,_7),8)),ki(i.a*i.b,r.a*r.b)):t}function xqe(e){var n,t;return t=new p0,t.a+="e_",n=R7n(e),n!=null&&(t.a+=""+n),e.c&&e.d&&(Kt((t.a+=" ",t),bz(e.c)),Kt(uo((t.a+="[",t),e.c.i),"]"),Kt((t.a+=eee,t),bz(e.d)),Kt(uo((t.a+="[",t),e.d.i),"]")),t.a}function Aqe(e){switch(e.g){case 0:return new DU;case 1:return new oP;case 2:return new IU;case 3:return new ST;default:throw $(new Gn("No implementation is available for the layout phase "+(e.f!=null?e.f:""+e.g)))}}function V0e(e,n,t,i,r){var c;switch(c=0,r.g){case 1:c=k.Math.max(0,n.b+e.b-(t.b+i));break;case 3:c=k.Math.max(0,-e.b-i);break;case 2:c=k.Math.max(0,-e.a-i);break;case 4:c=k.Math.max(0,n.a+e.a-(t.a+i))}return c}function Mqe(e,n,t){var i,r,c,o,l;if(t)for(r=t.a.length,i=new Rb(r),l=(i.b-i.a)*i.c<0?(k0(),yb):new S0(i);l.Ob();)o=u(l.Pb(),15),c=B9(t,o.a),F2e in c.a||xne in c.a?xIn(e,c,n):URn(e,c,n),Wwn(u(Bn(e.c,d8(c)),85))}function Y0e(e){var n,t;switch(e.b){case-1:return!0;case 0:return t=e.t,t>1||t==-1?(e.b=-1,!0):(n=ff(e),n&&(Cc(),n.jk()==ZZe)?(e.b=-1,!0):(e.b=1,!1));default:case 1:return!1}}function Q0e(e,n){var t,i,r,c;if(li(e),e.c!=0||e.a!=123)throw $(new zt(Ht((Lt(),kZe))));if(c=n==112,i=e.d,t=k9(e.i,125,i),t<0)throw $(new zt(Ht((Lt(),jZe))));return r=of(e.i,i,t),e.d=t+1,P$e(r,c,(e.e&512)==512)}function jCn(e){var n,t,i,r,c,o,l;for(l=Fh(e.c.length),r=new L(e);r.a=0&&i=0?e.Ih(t,!0,!0):Gw(e,r,!0),163)),u(i,219).Ul(n);throw $(new Gn(W0+n.ve()+wne))}function SCn(){nse();var e;return Zan?u(E8((y0(),kf),hf),2e3):(ti(wg,new NL),b$n(),e=u(X(lo((y0(),kf),hf),548)?lo(kf,hf):new OIe,548),Zan=!0,dBn(e),vBn(e),ei((ese(),V8e),e,new m3),Kc(kf,hf,e),e)}function xCn(e,n){var t,i,r,c;e.j=-1,Fs(e.e)?(t=e.i,c=e.i!=0,YC(e,n),i=new L1(e.e,3,e.c,null,n,t,c),r=n.xl(e.e,e.c,null),r=rGe(e,n,r),r?(r.lj(i),r.mj()):ai(e.e,i)):(YC(e,n),r=n.xl(e.e,e.c,null),r&&r.mj())}function Az(e,n){var t,i,r;if(r=0,i=n[0],i>=e.length)return-1;for(t=(Yn(i,e.length),e.charCodeAt(i));t>=48&&t<=57&&(r=r*10+(t-48),++i,!(i>=e.length));)t=(Yn(i,e.length),e.charCodeAt(i));return i>n[0]?n[0]=i:r=-1,r}function ACn(e,n,t){var i,r,c,o,l;o=e.c,l=e.d,c=pu(z(B(Lr,1),Se,8,0,[o.i.n,o.n,o.a])).b,r=(c+pu(z(B(Lr,1),Se,8,0,[l.i.n,l.n,l.a])).b)/2,i=null,o.j==(De(),et)?i=new je(n+o.i.c.c.a+t,r):i=new je(n-t,r),j9(e.a,0,i)}function Hw(e){var n,t,i,r;for(n=null,i=qh(Rl(z(B(Xl,1),On,20,0,[(!e.b&&(e.b=new Nn(vt,e,4,7)),e.b),(!e.c&&(e.c=new Nn(vt,e,5,8)),e.c)])));ht(i);)if(t=u(tt(i),84),r=iu(t),!n)n=r;else if(n!=r)return!1;return!0}function jW(e,n,t){var i;if(++e.j,n>=e.i)throw $(new jo(Mne+n+dg+e.i));if(t>=e.i)throw $(new jo(Tne+t+dg+e.i));return i=e.g[t],n!=t&&(n>16),n=i>>16&16,t=16-n,e=e>>n,i=e-256,n=i>>16&8,t+=n,e<<=n,i=e-W2,n=i>>16&4,t+=n,e<<=n,i=e-yh,n=i>>16&2,t+=n,e<<=n,i=e>>14,n=i&~(i>>1),t+2-n)}function MCn(e,n){var t,i,r;for(r=new Ce,i=St(n.a,0);i.b!=i.d.c;)t=u(jt(i),65),t.c.g==e.g&&ue(T(t.b,(Mu(),Nh)))!==ue(T(t.c,Nh))&&!G3(new wn(null,new pn(r,16)),new OEe(t))&&Hn(r.c,t);return Cr(r,new Mk),r}function Cqe(e,n,t){var i,r,c,o;return X(n,155)&&X(t,155)?(c=u(n,155),o=u(t,155),e.a[c.a][o.a]+e.a[o.a][c.a]):X(n,251)&&X(t,251)&&(i=u(n,251),r=u(t,251),i.a==r.a)?u(T(r.a,(Gf(),ky)),15).a:0}function Oqe(e,n){var t,i,r,c,o,l,f,h;for(h=ne(re(T(n,(Ne(),yx)))),f=e[0].n.a+e[0].o.a+e[0].d.c+h,l=1;l=0?t:(l=fE(Nr(new je(o.c+o.b/2,o.d+o.a/2),new je(c.c+c.b/2,c.d+c.a/2))),-(oKe(c,o)-1)*l)}function CCn(e,n,t){var i;Zi(new wn(null,(!t.a&&(t.a=new pe(Pi,t,6,6)),new pn(t.a,16))),new NTe(e,n)),Zi(new wn(null,(!t.n&&(t.n=new pe(ju,t,1,7)),new pn(t.n,16))),new DTe(e,n)),i=u(ye(t,(Xt(),Kv)),78),i&&Zhe(i,e,n)}function Gw(e,n,t){var i,r,c;if(c=av((ls(),nc),e.Ah(),n),c)return Cc(),u(c,69).vk()||(c=D4(Vc(nc,c))),r=(i=e.Fh(c),u(i>=0?e.Ih(i,!0,!0):Gw(e,c,!0),163)),u(r,219).Ql(n,t);throw $(new Gn(W0+n.ve()+wne))}function W0e(e,n,t,i){var r,c,o,l,f;if(r=e.d[n],r){if(c=r.g,f=r.i,i!=null){for(l=0;l=t&&(i=n,h=(f.c+f.a)/2,o=h-t,f.c<=h-t&&(r=new QK(f.c,o),Pb(e,i++,r)),l=h+t,l<=f.a&&(c=new QK(l,f.a),S2(i,e.c.length),Ij(e.c,i,c)))}function _qe(e,n,t){var i,r,c,o,l,f;if(!n.dc()){for(r=new Si,f=n.Jc();f.Ob();)for(l=u(f.Pb(),40),ei(e.a,ve(l.g),ve(t)),o=(i=St(new S1(l).a.d,0),new E3(i));YT(o.a);)c=u(jt(o.a),65).c,Xi(r,c,r.c.b,r.c);_qe(e,r,t+1)}}function Z0e(e){var n;if(!e.c&&e.g==null)e.d=e._i(e.f),Et(e,e.d),n=e.d;else{if(e.g==null)return!0;if(e.i==0)return!1;n=u(e.g[e.i-1],50)}return n==e.b&&null.Tm>=null.Sm()?(Cz(e),Z0e(e)):n.Ob()}function Lqe(e){if(this.a=e,e.c.i.k==(zn(),wr))this.c=e.c,this.d=u(T(e.c.i,(me(),Du)),64);else if(e.d.i.k==wr)this.c=e.d,this.d=u(T(e.d.i,(me(),Du)),64);else throw $(new Gn("Edge "+e+" is not an external edge."))}function Pqe(e,n){var t,i,r;r=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,3,r,e.b)),n?n!=e&&(Mo(e,n.zb),NY(e,n.d),t=(i=n.c,i??n.zb),IY(e,t==null||bn(t,n.zb)?null:t)):(Mo(e,null),NY(e,0),IY(e,null))}function $qe(e){!nte&&(nte=kRn());var n=e.replace(/[\x00-\x1f\xad\u0600-\u0603\u06dd\u070f\u17b4\u17b5\u200b-\u200f\u2028-\u202e\u2060-\u2064\u206a-\u206f\ufeff\ufff9-\ufffb"\\]/g,function(t){return g4n(t)});return'"'+n+'"'}function ebe(e,n,t,i,r,c){var o,l,f,h,b;if(r!=0)for(ue(e)===ue(t)&&(e=e.slice(n,n+r),n=0),f=t,l=n,h=n+r;l=o)throw $(new b2(n,o));return r=t[n],o==1?i=null:(i=oe(Rce,Ine,415,o-1,0,1),Wu(t,0,i,0,n),c=o-n-1,c>0&&Wu(t,n+1,i,n,c)),g8(e,i),rqe(e,n,r),r}function Rqe(e){var n,t;if(e.f){for(;e.n0)for(o=e.c.d,l=e.d.d,r=A1(Nr(new je(l.a,l.b),o),1/(i+1)),c=new je(o.a,o.b),t=new L(e.a);t.a0?c=q4(t):c=CO(q4(t))),ji(n,T7,c)}function Hqe(e,n){var t,i;if(e.c.length!=0){if(e.c.length==2)iy((vn(0,e.c.length),u(e.c[0],9)),(al(),s1)),iy((vn(1,e.c.length),u(e.c[1],9)),hb);else for(i=new L(e);i.a0&&ZO(e,t,n),c):i.a!=null?(ZO(e,n,t),-1):r.a!=null?(ZO(e,t,n),1):0}function Gqe(e){qV();var n,t,i,r,c,o,l;for(t=new O0,r=new L(e.e.b);r.a=0;)i=t[c],o.$l(i.Jk())&&Et(r,i);!FVe(e,r)&&Fs(e.e)&&s9(e,n.Hk()?M0(e,6,n,(jn(),Sc),null,-1,!1):M0(e,n.rk()?2:1,n,null,null,-1,!1))}function BCn(e,n){var t,i,r,c,o;return e.a==(y8(),rx)?!0:(c=n.a.c,t=n.a.c+n.a.b,!(n.j&&(i=n.A,o=i.c.c.a-i.o.a/2,r=c-(i.n.a+i.o.a),r>o)||n.q&&(i=n.C,o=i.c.c.a-i.o.a/2,r=i.n.a-t,r>o)))}function Uqe(e,n,t){var i,r,c,o,l,f;for(i=0,f=t,n||(i=t*(e.c.length-1),f*=-1),c=new L(e);c.a=0?e.xh(null):e.Mh().Qh(e,-1-n,null,null)),e.yh(u(r,52),t),i&&i.mj(),e.sh()&&e.th()&&t>-1&&ai(e,new Ir(e,9,t,c,r)),r):c}function rbe(e,n){var t,i,r,c,o;for(c=e.b.Ae(n),i=(t=e.a.get(c),t??oe(Ar,On,1,0,5,1)),o=0;o>5,r>=e.d)return e.e<0;if(t=e.a[r],n=1<<(n&31),e.e<0){if(i=QBe(e),r>16)),16).bd(c),l0&&(!(x1(e.a.c)&&n.n.d)&&!(I3(e.a.c)&&n.n.b)&&(n.g.d+=k.Math.max(0,i/2-.5)),!(x1(e.a.c)&&n.n.a)&&!(I3(e.a.c)&&n.n.c)&&(n.g.a-=i-1))}function cUe(e,n,t){var i,r,c,o,l,f;c=u(Le(n.e,0),17).c,i=c.i,r=i.k,f=u(Le(t.g,0),17).d,o=f.i,l=o.k,r==(zn(),dr)?he(e,(me(),ja),u(T(i,ja),12)):he(e,(me(),ja),c),l==dr?he(e,(me(),gf),u(T(o,gf),12)):he(e,(me(),gf),f)}function uUe(e,n){var t,i,r,c,o,l;for(c=new L(e.b);c.a>n,c=e.m>>n|t<<22-n,r=e.l>>n|e.m<<22-n):n<44?(o=i?G1:0,c=t>>n-22,r=e.m>>n-22|t<<44-n):(o=i?G1:0,c=i?Ls:0,r=t>>n-44),_o(r&Ls,c&Ls,o&G1)}function oUe(e,n){var t,i,r,c,o,l,f,h,b;if(e.a.f>0&&X(n,45)&&(e.a.Zj(),h=u(n,45),f=h.jd(),c=f==null?0:Oi(f),o=Nle(e.a,c),t=e.a.d[o],t)){for(i=u(t.g,374),b=t.i,l=0;l=2)for(t=r.Jc(),n=re(t.Pb());t.Ob();)c=n,n=re(t.Pb()),i=k.Math.min(i,(_n(n),n-(_n(c),c)));return i}function sOn(e,n){var t,i,r;for(r=new Ce,i=St(n.a,0);i.b!=i.d.c;)t=u(jt(i),65),t.b.g==e.g&&!bn(t.b.c,DF)&&ue(T(t.b,(Mu(),Nh)))!==ue(T(t.c,Nh))&&!G3(new wn(null,new pn(r,16)),new NEe(t))&&Hn(r.c,t);return Cr(r,new tw),r}function lOn(e,n){var t,i,r;if(ue(n)===ue(Nt(e)))return!0;if(!X(n,16)||(i=u(n,16),r=e.gc(),r!=i.gc()))return!1;if(X(i,59)){for(t=0;t0&&(r=t),o=new L(e.f.e);o.a0?r+=n:r+=1;return r}function pOn(e,n){var t,i,r,c,o,l,f,h,b,p;h=e,f=gE(h,"individualSpacings"),f&&(i=da(n,(Xt(),Jy)),o=!i,o&&(r=new R6,ji(n,Jy,r)),l=u(ye(n,Jy),379),p=f,c=null,p&&(c=(b=BY(p,oe(ze,Se,2,0,6,1)),new PX(p,b))),c&&(t=new JTe(p,l),rc(c,t)))}function mOn(e,n){var t,i,r,c,o,l,f,h,b,p,y;return f=null,p=e,b=null,(uZe in p.a||oZe in p.a||FF in p.a)&&(h=null,y=l1e(n),o=gE(p,uZe),t=new gSe(y),VFe(t.a,o),l=gE(p,oZe),i=new SSe(y),YFe(i.a,l),c=Ow(p,FF),r=new MSe(y),h=(tGe(r.a,c),c),b=h),f=b,f}function vOn(e,n){var t,i,r;if(n===e)return!0;if(X(n,540)){if(r=u(n,833),e.a.d!=r.a.d||z3(e).gc()!=z3(r).gc())return!1;for(i=z3(r).Jc();i.Ob();)if(t=u(i.Pb(),416),cLe(e,t.a.jd())!=u(t.a.kd(),18).gc())return!1;return!0}return!1}function yOn(e,n){var t,i,r,c;for(c=new L(n.a);c.an.c?1:e.bn.b?1:e.a!=n.a?Oi(e.a)-Oi(n.a):e.d==(mE(),Cx)&&n.d==Tx?-1:e.d==Tx&&n.d==Cx?1:0}function xW(e){var n,t,i,r,c,o,l,f;for(r=Ki,i=Dr,t=new L(e.e.b);t.a0&&r0):r<0&&-r0):!1}function jOn(e,n,t,i){var r,c,o,l,f,h,b,p;for(r=(n-e.d)/e.c.c.length,c=0,e.a+=t,e.d=n,p=new L(e.c);p.a>24;return o}function SOn(e){if(e.xe()){var n=e.c;n.ye()?e.o="["+n.n:n.xe()?e.o="["+n.ve():e.o="[L"+n.ve()+";",e.b=n.ue()+"[]",e.k=n.we()+"[]";return}var t=e.j,i=e.d;i=i.split("/"),e.o=MQ(".",[t,MQ("$",i)]),e.b=MQ(".",[t,MQ(".",i)]),e.k=i[i.length-1]}function xOn(e,n){var t,i,r,c,o;for(o=null,c=new L(e.e.a);c.a0&&oN(n,(vn(i-1,e.c.length),u(e.c[i-1],9)),r)>0;)ol(e,i,(vn(i-1,e.c.length),u(e.c[i-1],9))),--i;vn(i,e.c.length),e.c[i]=r}n.b=new pt,n.g=new pt}function vUe(e,n,t){var i,r,c;for(i=1;i0&&n.Le((vn(r-1,e.c.length),u(e.c[r-1],9)),c)>0;)ol(e,r,(vn(r-1,e.c.length),u(e.c[r-1],9))),--r;vn(r,e.c.length),e.c[r]=c}t.a=new pt,t.b=new pt}function Tz(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(c=n.Jc();c.Ob();)r=u(c.Pb(),26),b=r.i+r.g/2,y=r.j+r.f/2,f=e.f,o=f.i+f.g/2,l=f.j+f.f/2,h=b-o,p=y-l,i=k.Math.sqrt(h*h+p*p),h*=e.e/i,p*=e.e/i,t?(b-=h,y-=p):(b+=h,y+=p),Os(r,b-r.g/2),Ns(r,y-r.f/2)}function ov(e){var n,t,i;if(!e.c&&e.b!=null){for(n=e.b.length-4;n>=0;n-=2)for(t=0;t<=n;t+=2)(e.b[t]>e.b[t+2]||e.b[t]===e.b[t+2]&&e.b[t+1]>e.b[t+3])&&(i=e.b[t+2],e.b[t+2]=e.b[t],e.b[t]=i,i=e.b[t+3],e.b[t+3]=e.b[t+1],e.b[t+1]=i);e.c=!0}}function Jf(e){var n,t;return t=new il(Db(e.Pm)),t.a+="@",Kt(t,(n=Oi(e)>>>0,n.toString(16))),e.Sh()?(t.a+=" (eProxyURI: ",uo(t,e.Yh()),e.Hh()&&(t.a+=" eClass: ",uo(t,e.Hh())),t.a+=")"):e.Hh()&&(t.a+=" (eClass: ",uo(t,e.Hh()),t.a+=")"),t.a}function eS(e){var n,t,i,r;if(e.e)throw $(new Uc((M1(bte),qZ+bte.k+UZ)));for(e.d==(vr(),eh)&&Vz(e,Zc),t=new L(e.a.a);t.a>24}return t}function NOn(e,n,t){var i,r,c;if(r=u(zc(e.i,n),318),!r)if(r=new HRe(e.d,n,t),M4(e.i,n,r),mde(n))Qwn(e.a,n.c,n.b,r);else switch(c=ATn(n),i=u(zc(e.p,c),253),c.g){case 1:case 3:r.j=!0,xX(i,n.b,r);break;case 4:case 2:r.k=!0,xX(i,n.c,r)}return r}function DOn(e,n,t,i){var r,c,o,l,f,h;if(l=new z6,f=Po(e.e.Ah(),n),r=u(e.g,122),Cc(),u(n,69).vk())for(o=0;o=0)return r;for(c=1,l=new L(n.j);l.a=0)return r;for(c=1,l=new L(n.j);l.a=0?(n||(n=new jj,i>0&&Bc(n,(Yr(0,i,e.length),e.substr(0,i)))),n.a+="\\",D9(n,t&yr)):n&&D9(n,t&yr);return n?n.a:e}function _On(e){var n,t,i;for(t=new L(e.a.a.b);t.a0&&(!(x1(e.a.c)&&n.n.d)&&!(I3(e.a.c)&&n.n.b)&&(n.g.d-=k.Math.max(0,i/2-.5)),!(x1(e.a.c)&&n.n.a)&&!(I3(e.a.c)&&n.n.c)&&(n.g.a+=k.Math.max(0,i-1)))}function xUe(e,n,t){var i,r;if((e.c-e.b&e.a.length-1)==2)n==(De(),Xn)||n==et?(hB(u(CE(e),16),(al(),s1)),hB(u(CE(e),16),hb)):(hB(u(CE(e),16),(al(),hb)),hB(u(CE(e),16),s1));else for(r=new hE(e);r.a!=r.b;)i=u(zB(r),16),hB(i,t)}function LOn(e,n,t){var i,r,c,o,l,f,h,b,p;for(b=-1,p=0,l=n,f=0,h=l.length;f0&&++p;++b}return p}function POn(e,n){var t,i,r,c,o,l,f;for(r=M9(new roe(e)),l=new qr(r,r.c.length),c=M9(new roe(n)),f=new qr(c,c.c.length),o=null;l.b>0&&f.b>0&&(t=(at(l.b>0),u(l.a.Xb(l.c=--l.b),26)),i=(at(f.b>0),u(f.a.Xb(f.c=--f.b),26)),t==i);)o=t;return o}function $On(e,n){var t,i,r,c;for(n.Tg("Self-Loop pre-processing",1),i=new L(e.a);i.abLe(e,t)?(i=mu(t,(De(),et)),e.d=i.dc()?0:tV(u(i.Xb(0),12)),o=mu(n,Kn),e.b=o.dc()?0:tV(u(o.Xb(0),12))):(r=mu(t,(De(),Kn)),e.d=r.dc()?0:tV(u(r.Xb(0),12)),c=mu(n,et),e.b=c.dc()?0:tV(u(c.Xb(0),12)))}function ROn(e){var n,t,i,r,c,o,l,f;n=!0,r=null,c=null;e:for(f=new L(e.a);f.ae.c));o++)r.a>=e.s&&(c<0&&(c=o),l=o);return f=(e.s+e.c)/2,c>=0&&(i=EIn(e,n,c,l),f=Tgn((vn(i,n.c.length),u(n.c[i],340))),ICn(n,i,t)),f}function Tt(e,n,t){var i,r,c,o,l,f,h;for(o=(c=new Mb,c),Hhe(o,(_n(n),n)),h=(!o.b&&(o.b=new Hs((yn(),Ac),Iu,o)),o.b),f=1;f=2}function JOn(e,n,t,i,r){var c,o,l,f,h,b;for(c=e.c.d.j,o=u(Yu(t,0),8),b=1;b1||(n=Mi(Qf,z(B($c,1),ke,96,0,[W1,Wf])),gO(_R(n,e))>1)||(i=Mi(ea,z(B($c,1),ke,96,0,[l1,pf])),gO(_R(i,e))>1))}function TUe(e){var n,t,i,r,c,o,l;for(n=0,i=new L(e.a);i.a0&&(i.b.n-=i.c,i.b.n<=0&&i.b.u>0&&Vt(n,i.b));for(r=new L(e.i);r.a0&&(i.a.u-=i.c,i.a.u<=0&&i.a.n>0&&Vt(t,i.a))}function Cz(e){var n,t,i,r,c;if(e.g==null&&(e.d=e._i(e.f),Et(e,e.d),e.c))return c=e.f,c;if(n=u(e.g[e.i-1],50),r=n.Pb(),e.e=n,t=e._i(r),t.Ob())e.d=t,Et(e,t);else for(e.d=null;!n.Ob()&&(tr(e.g,--e.i,null),e.i!=0);)i=u(e.g[e.i-1],50),n=i;return r}function GOn(e,n){var t,i,r,c,o,l;if(i=n,r=i.Jk(),J1(e.e,r)){if(r.Qi()&&HR(e,r,i.kd()))return!1}else for(l=Po(e.e.Ah(),r),t=u(e.g,122),c=0;c1||t>1)return 2;return n+t==1?2:0}function Is(e,n){var t,i,r,c,o,l;return c=e.a*FZ+e.b*1502,l=e.b*FZ+11,t=k.Math.floor(l*vN),c+=t,l-=t*Uge,c%=Uge,e.a=c,e.b=l,n<=24?k.Math.floor(e.a*qme[n]):(r=e.a*(1<=2147483648&&(i-=4294967296),i)}function NUe(e,n,t){var i,r,c,o,l,f,h;for(c=new Ce,h=new Si,o=new Si,lLn(e,h,o,n),GPn(e,h,o,n,t),f=new L(e);f.ai.b.g&&Hn(c.c,i);return c}function YOn(e,n,t){var i,r,c,o,l,f;for(l=e.c,o=(t.q?t.q:(jn(),jn(),i1)).vc().Jc();o.Ob();)c=u(o.Pb(),45),i=!b9(si(new wn(null,new pn(l,16)),new u9(new vTe(n,c)))).zd((Ib(),vy)),i&&(f=c.kd(),X(f,4)&&(r=yde(f),r!=null&&(f=r)),n.of(u(c.jd(),147),f))}function QOn(e,n){var t,i,r,c;for(n.Tg("Resize child graph to fit parent.",1),i=new L(e.b);i.a1)for(r=new L(e.a);r.a=0?e.Ih(i,!0,!0):Gw(e,c,!0),163)),u(r,219).Vl(n,t)}else throw $(new Gn(W0+n.ve()+_S))}function eNn(e,n,t){var i,r,c,o,l,f;if(f=Ele(e,u(Bn(e.e,n),26)),l=null,f)switch(f.g){case 3:i=Xse(e,j2(n)),l=(_n(t),t+(_n(i),i));break;case 2:r=Xse(e,j2(n)),o=(_n(t),t+(_n(r),r)),c=Xse(e,u(Bn(e.e,n),26)),l=o-(_n(c),c);break;default:l=t}else l=t;return l}function nNn(e,n,t){var i,r,c,o,l,f;if(f=Ele(e,u(Bn(e.e,n),26)),l=null,f)switch(f.g){case 3:i=Kse(e,j2(n)),l=(_n(t),t+(_n(i),i));break;case 2:r=Kse(e,j2(n)),o=(_n(t),t+(_n(r),r)),c=Kse(e,u(Bn(e.e,n),26)),l=o-(_n(c),c);break;default:l=t}else l=t;return l}function Oz(e,n){var t,i,r,c,o;if(n){for(c=X(e.Cb,88)||X(e.Cb,103),o=!c&&X(e.Cb,335),i=new ut((!n.a&&(n.a=new tE(n,Rc,n)),n.a));i.e!=i.i.gc();)if(t=u(ft(i),87),r=Jz(t),c?X(r,88):o?X(r,159):r)return r;return c?(yn(),jf):(yn(),ih)}else return null}function tNn(e,n){var t,i,r,c,o;for(t=new Ce,r=ou(new wn(null,new pn(e,16)),new L5),c=ou(new wn(null,new pn(e,16)),new Ak),o=U9n(d9n(k2(dNn(z(B(OBn,1),On,832,0,[r,c])),new w_))),i=1;i=2*n&&Te(t,new QK(o[i-1]+n,o[i]-n));return t}function DUe(e,n,t){var i,r,c,o,l,f,h,b;if(t)for(c=t.a.length,i=new Rb(c),l=(i.b-i.a)*i.c<0?(k0(),yb):new S0(i);l.Ob();)o=u(l.Pb(),15),r=B9(t,o.a),r&&(f=v6n(e,(h=(v0(),b=new voe,b),n&&kbe(h,n),h),r),X9(f,N1(r,Ah)),yz(r,f),H0e(r,f),eQ(e,r,f))}function Nz(e){var n,t,i,r,c,o;if(!e.j){if(o=new kL,n=sA,c=n.a.yc(e,n),c==null){for(i=new ut(tu(e));i.e!=i.i.gc();)t=u(ft(i),29),r=Nz(t),er(o,r),Et(o,t);n.a.Ac(e)!=null}_2(o),e.j=new N3((u(K(we((x0(),Rn).o),11),19),o.i),o.g),Ms(e).b&=-33}return e.j}function iNn(e){var n,t,i,r;if(e==null)return null;if(i=bo(e,!0),r=HN.length,bn(i.substr(i.length-r,r),HN)){if(t=i.length,t==4){if(n=(Yn(0,i.length),i.charCodeAt(0)),n==43)return g7e;if(n==45)return yhn}else if(t==3)return g7e}return new aoe(i)}function rNn(e){var n,t,i;return t=e.l,(t&t-1)!=0||(i=e.m,(i&i-1)!=0)||(n=e.h,(n&n-1)!=0)||n==0&&i==0&&t==0?-1:n==0&&i==0&&t!=0?Phe(t):n==0&&i!=0&&t==0?Phe(i)+22:n!=0&&i==0&&t==0?Phe(n)+44:-1}function sv(e,n){var t,i,r,c,o;for(r=n.a&e.f,c=null,i=e.b[r];;i=i.b){if(i==n){c?c.b=n.b:e.b[r]=n.b;break}c=i}for(o=n.f&e.f,c=null,t=e.c[o];;t=t.d){if(t==n){c?c.d=n.d:e.c[o]=n.d;break}c=t}n.e?n.e.c=n.c:e.a=n.c,n.c?n.c.e=n.e:e.e=n.e,--e.i,++e.g}function cNn(e,n){var t;n.d?n.d.b=n.b:e.a=n.b,n.b?n.b.d=n.d:e.e=n.d,!n.e&&!n.c?(t=u(uf(u(L4(e.b,n.a),262)),262),t.a=0,++e.c):(t=u(uf(u(Bn(e.b,n.a),262)),262),--t.a,n.e?n.e.c=n.c:t.b=u(uf(n.c),497),n.c?n.c.e=n.e:t.c=u(uf(n.e),497)),--e.d}function MW(e,n){var t,i,r,c;for(c=new qr(e,0),t=(at(c.b0),c.a.Xb(c.c=--c.b),d2(c,r),at(c.b3&&Kh(e,0,n-3))}function oNn(e){var n,t,i,r;return ue(T(e,(Ne(),wm)))===ue((B1(),Yd))?!e.e&&ue(T(e,fD))!==ue((W9(),tD)):(i=u(T(e,Oie),302),r=Re($e(T(e,Nie)))||ue(T(e,wx))===ue((BE(),eD)),n=u(T(e,z5e),15).a,t=e.a.c.length,!r&&i!=(W9(),tD)&&(n==0||n>t))}function sNn(e,n){var t,i,r,c,o,l,f;for(r=e.Jc();r.Ob();)for(i=u(r.Pb(),9),l=new Qu,gu(l,i),xr(l,(De(),et)),he(l,(me(),rH),(Pn(),!0)),o=n.Jc();o.Ob();)c=u(o.Pb(),9),f=new Qu,gu(f,c),xr(f,Kn),he(f,rH,!0),t=new Mw,he(t,rH,!0),lc(t,l),Gr(t,f)}function lNn(e){var n,t;for(t=0;t0);t++);if(t>0&&t0);n++);return n>0&&t>16!=6&&n){if(w8(e,n))throw $(new Gn(LS+Xqe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Jde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=K4(n,e,6,i)),i=Cle(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,6,n,n))}function Dz(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=3&&n){if(w8(e,n))throw $(new Gn(LS+$Ke(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Ude(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=K4(n,e,12,i)),i=Tle(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,3,n,n))}function kbe(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=9&&n){if(w8(e,n))throw $(new Gn(LS+_Xe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Gde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=K4(n,e,9,i)),i=Ole(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,9,n,n))}function S8(e){var n,t,i,r,c;if(i=ff(e),c=e.j,c==null&&i)return e.Hk()?null:i.gk();if(X(i,159)){if(t=i.hk(),t&&(r=t.ti(),r!=e.i)){if(n=u(i,159),n.lk())try{e.g=r.qi(n,c)}catch(o){if(o=sr(o),X(o,80))e.g=null;else throw $(o)}e.i=r}return e.g}return null}function $Ue(e){var n;return n=new Ce,Te(n,new f4(new je(e.c,e.d),new je(e.c+e.b,e.d))),Te(n,new f4(new je(e.c,e.d),new je(e.c,e.d+e.a))),Te(n,new f4(new je(e.c+e.b,e.d+e.a),new je(e.c+e.b,e.d))),Te(n,new f4(new je(e.c+e.b,e.d+e.a),new je(e.c,e.d+e.a))),n}function aNn(e){var n,t,i,r;for(i=e.a.d.j,r=e.c.d.j,t=new L(e.i.d);t.a>>0),t.toString(16)),JEn(F7n(),(m9(),"Exception during lenientFormat for "+i),n),"<"+i+" threw "+Db(n.Pm)+">";throw $(r)}}function dNn(e){var n,t,i,r,c,o,l,f,h;for(i=!1,n=336,t=0,c=new ZOe(e.length),l=e,f=0,h=l.length;f1)for(n=mw((t=new Nb,++e.b,t),e.d),l=St(c,0);l.b!=l.d.c;)o=u(jt(l),124),Hf(Nf(Of(Df(Cf(new tf,1),0),n),o))}function Iz(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=11&&n){if(w8(e,n))throw $(new Gn(LS+Jbe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Xde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=K4(n,e,10,i)),i=Gle(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,11,n,n))}function mNn(e,n,t){var i,r,c,o,l,f;if(c=0,o=0,e.c)for(f=new L(e.d.i.j);f.ac.a?-1:r.af){for(b=e.d,e.d=oe(z8e,nme,67,2*f+4,0,1),c=0;c=9223372036854776e3?(G9(),jme):(r=!1,e<0&&(r=!0,e=-e),i=0,e>=sg&&(i=sc(e/sg),e-=i*sg),t=0,e>=sy&&(t=sc(e/sy),e-=t*sy),n=sc(e),c=_o(n,t,i),r&&ZY(c),c)}function ONn(e){var n,t,i,r,c;if(c=new Ce,Ao(e.b,new Xke(c)),e.b.c.length=0,c.c.length!=0){for(n=(vn(0,c.c.length),u(c.c[0],80)),t=1,i=c.c.length;t>16!=7&&n){if(w8(e,n))throw $(new Gn(LS+JGe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Hde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=u(n,52).Oh(e,1,VD,i)),i=Mfe(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,7,n,n))}function zUe(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=3&&n){if(w8(e,n))throw $(new Gn(LS+NFe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?qde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=u(n,52).Oh(e,0,QD,i)),i=Tfe(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,3,n,n))}function TW(e,n){A8();var t,i,r,c,o,l,f,h,b;return n.d>e.d&&(l=e,e=n,n=l),n.d<63?yDn(e,n):(o=(e.d&-2)<<4,h=Vae(e,o),b=Vae(n,o),i=KW(e,_4(h,o)),r=KW(n,_4(b,o)),f=TW(h,b),t=TW(i,r),c=TW(KW(h,i),KW(r,b)),c=nZ(nZ(c,f),t),c=_4(c,o),f=_4(f,o<<1),nZ(nZ(f,c),t))}function YO(){YO=Y,Kie=new M3(BQe,0),M4e=new M3("LONGEST_PATH",1),T4e=new M3("LONGEST_PATH_SOURCE",2),Uie=new M3("COFFMAN_GRAHAM",3),A4e=new M3(ree,4),C4e=new M3("STRETCH_WIDTH",5),EH=new M3("MIN_WIDTH",6),qie=new M3("BF_MODEL_ORDER",7),Xie=new M3("DF_MODEL_ORDER",8)}function LNn(e,n){var t,i,r,c,o,l;if(!e.tb){for(c=(!e.rb&&(e.rb=new m2(e,Aa,e)),e.rb),l=new l4(c.i),r=new ut(c);r.e!=r.i.gc();)i=u(ft(r),143),o=i.ve(),t=u(o==null?Ko(l.f,null,i):Pw(l.i,o,i),143),t&&(o==null?Ko(l.f,null,t):Pw(l.i,o,t));e.tb=l}return u(lo(e.tb,n),143)}function QO(e,n){var t,i,r,c,o;if((e.i==null&&vh(e),e.i).length,!e.p){for(o=new l4((3*e.g.i/2|0)+1),r=new m4(e.g);r.e!=r.i.gc();)i=u(LQ(r),179),c=i.ve(),t=u(c==null?Ko(o.f,null,i):Pw(o.i,c,i),179),t&&(c==null?Ko(o.f,null,t):Pw(o.i,c,t));e.p=o}return u(lo(e.p,n),179)}function Mbe(e,n,t,i,r){var c,o,l,f,h;for(DEn(i+DR(t,t.ge()),r),wIe(n,Vjn(t)),c=t.f,c&&Mbe(e,n,c,"Caused by: ",!1),l=(t.k==null&&(t.k=oe(ete,Se,80,0,0,1)),t.k),f=0,h=l.length;f=0;c+=t?1:-1)o=o|n.c.jg(f,c,t,i&&!Re($e(T(n.j,(me(),rb))))&&!Re($e(T(n.j,(me(),_v))))),o=o|n.q.tg(f,c,t),o=o|MXe(e,f[c],t,i);return hr(e.c,n),o}function Lz(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(b=qLe(e.j),p=0,y=b.length;p1&&(e.a=!0),lvn(u(t.b,68),gi(pc(u(n.b,68).c),A1(Nr(pc(u(t.b,68).a),u(n.b,68).a),r))),nLe(e,n),JUe(e,t)}function HUe(e){var n,t,i,r,c,o,l;for(c=new L(e.a.a);c.a0&&c>0?o.p=n++:i>0?o.p=t++:c>0?o.p=r++:o.p=t++}jn(),Cr(e.j,new Iq)}function zNn(e){var n,t;t=null,n=u(Le(e.g,0),17);do{if(t=n.d.i,bi(t,(me(),gf)))return u(T(t,gf),12).i;if(t.k!=(zn(),Qi)&&ht(new qn(Vn(Ni(t).a.Jc(),new ee))))n=u(tt(new qn(Vn(Ni(t).a.Jc(),new ee))),17);else if(t.k!=Qi)return null}while(t&&t.k!=(zn(),Qi));return t}function FNn(e,n){var t,i,r,c,o,l,f,h,b;for(l=n.j,o=n.g,f=u(Le(l,l.c.length-1),113),b=(vn(0,l.c.length),u(l.c[0],113)),h=YQ(e,o,f,b),c=1;ch&&(f=t,b=r,h=i);n.a=b,n.c=f}function qw(e,n,t,i){var r,c;if(r=ue(T(t,(Ne(),bx)))===ue((_0(),dm)),c=u(T(t,B5e),16),bi(e,(me(),Ci)))if(r){if(c.Gc(T(e,gx))&&c.Gc(T(n,gx)))return i*u(T(e,gx),15).a+u(T(e,Ci),15).a}else return u(T(e,Ci),15).a;else return-1;return u(T(e,Ci),15).a}function JNn(e,n,t){var i,r,c,o,l,f,h;for(h=new vd(new dEe(e)),o=z(B(cin,1),lQe,12,0,[n,t]),l=0,f=o.length;lf-e.b&&lf-e.a&&lt.p?1:0:c.Ob()?1:-1}function YNn(e,n){var t,i,r,c,o,l;n.Tg(lWe,1),r=u(ye(e,(Ja(),Bx)),104),c=(!e.a&&(e.a=new pe(Jt,e,10,11)),e.a),o=pxn(c),l=k.Math.max(o.a,ne(re(ye(e,(Yh(),Rx))))-(r.b+r.c)),i=k.Math.max(o.b,ne(re(ye(e,GH)))-(r.d+r.a)),t=i-o.b,ji(e,$x,t),ji(e,Py,l),ji(e,P7,i+t),n.Ug()}function Pz(e){var n,t;if((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i==0)return l1e(e);for(n=u(K((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),0),170),kt((!n.a&&(n.a=new mr(kl,n,5)),n.a)),V3(n,0),Y3(n,0),X3(n,0),K3(n,0),t=(!e.a&&(e.a=new pe(Pi,e,6,6)),e.a);t.i>1;)U2(t,t.i-1);return n}function Po(e,n){Cc();var t,i,r,c;return n?n==(Ei(),mhn)||(n==uhn||n==Dg||n==chn)&&e!=d7e?new Age(e,n):(i=u(n,682),t=i.Yk(),t||(L9(Vc((ls(),nc),n)),t=i.Yk()),c=(!t.i&&(t.i=new pt),t.i),r=u(du(Xc(c.f,e)),2003),!r&&ei(c,e,r=new Age(e,n)),r):thn}function QNn(e,n){var t,i;if(i=PC(e.b,n.b),!i)throw $(new Uc("Invalid hitboxes for scanline constraint calculation."));(Eze(n.b,u(vgn(e.b,n.b),60))||Eze(n.b,u(mgn(e.b,n.b),60)))&&yd(),e.a[n.b.f]=u(RX(e.b,n.b),60),t=u($X(e.b,n.b),60),t&&(e.a[t.f]=n.b)}function WNn(e,n){var t,i,r,c,o,l,f,h,b;for(f=u(T(e,(me(),wi)),12),h=pu(z(B(Lr,1),Se,8,0,[f.i.n,f.n,f.a])).a,b=e.i.n.b,t=dh(e.e),r=t,c=0,o=r.length;c0?c.a?(l=c.b.Kf().a,t>l&&(r=(t-l)/2,c.d.b=r,c.d.c=r)):c.d.c=e.s+t:cE(e.u)&&(i=m0e(c.b),i.c<0&&(c.d.b=-i.c),i.c+i.b>c.b.Kf().a&&(c.d.c=i.c+i.b-c.b.Kf().a))}function rDn(e,n){var t,i,r,c,o;o=new Ce,t=n;do c=u(Bn(e.b,t),132),c.B=t.c,c.D=t.d,Hn(o.c,c),t=u(Bn(e.k,t),17);while(t);return i=(vn(0,o.c.length),u(o.c[0],132)),i.j=!0,i.A=u(i.d.a.ec().Jc().Pb(),17).c.i,r=u(Le(o,o.c.length-1),132),r.q=!0,r.C=u(r.d.a.ec().Jc().Pb(),17).d.i,o}function cDn(e){var n,t;t=u(T(e,(Ne(),yu)),165),n=u(T(e,(me(),mg)),315),t==(Xs(),V1)?(he(e,yu,sD),he(e,mg,(_1(),Dv))):t==yg?(he(e,yu,sD),he(e,mg,(_1(),Sy))):n==(_1(),Dv)?(he(e,yu,V1),he(e,mg,rD)):n==Sy&&(he(e,yu,yg),he(e,mg,rD))}function $z(){$z=Y,vD=new zp,Fon=qt(new or,(zr(),eo),(Ur(),AJ)),Gon=Eo(qt(new or,eo,_J),Pc,IJ),qon=wh(wh(Oj(Eo(qt(new or,Kf,RJ),Pc,$J),no),PJ),BJ),Jon=Eo(qt(qt(qt(new or,r1,TJ),no,OJ),no,g7),Pc,CJ),Hon=Eo(qt(qt(new or,no,g7),no,xJ),Pc,SJ)}function iS(){iS=Y,Kon=qt(Eo(new or,(zr(),Pc),(Ur(),J3e)),eo,AJ),Won=wh(wh(Oj(Eo(qt(new or,Kf,RJ),Pc,$J),no),PJ),BJ),Von=Eo(qt(qt(qt(new or,r1,TJ),no,OJ),no,g7),Pc,CJ),Qon=qt(qt(new or,eo,_J),Pc,IJ),Yon=Eo(qt(qt(new or,no,g7),no,xJ),Pc,SJ)}function uDn(e,n,t,i,r){var c,o;(!cc(n)&&n.c.i.c==n.d.i.c||!OBe(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])),t))&&!cc(n)&&(n.c==r?j9(n.a,0,new wc(t)):Vt(n.a,new wc(t)),i&&!rf(e.a,t)&&(o=u(T(n,(Ne(),Wc)),78),o||(o=new xs,he(n,Wc,o)),c=new wc(t),Xi(o,c,o.c.b,o.c),hr(e.a,c)))}function UUe(e,n){var t,i,r,c;for(c=Rt(ac(Zh,Uh(Rt(ac(n==null?0:Oi(n),e1)),15))),t=c&e.b.length-1,r=null,i=e.b[t];i;r=i,i=i.a)if(i.d==c&&T1(i.i,n))return r?r.a=i.a:e.b[t]=i.a,fAe(u(uf(i.c),593),u(uf(i.f),593)),XT(u(uf(i.b),227),u(uf(i.e),227)),--e.f,++e.e,!0;return!1}function oDn(e){var n,t;for(t=new qn(Vn(rr(e).a.Jc(),new ee));ht(t);)if(n=u(tt(t),17),n.c.i.k!=(zn(),Uu))throw $(new wd(iee+LO(e)+"' has its layer constraint set to FIRST, but has at least one incoming edge that does not come from a FIRST_SEPARATE node. That must not happen."))}function XUe(e,n){var t,i,r,c,o,l,f,h,b,p,y;r=n?new nw:new cM,c=!1;do for(c=!1,h=n?Ks(e.b):e.b,f=h.Jc();f.Ob();)for(l=u(f.Pb(),25),y=qb(l.a),n||Ks(y),p=new L(y);p.a=0;o+=r?1:-1){for(l=n[o],f=i==(De(),et)?r?mu(l,i):Ks(mu(l,i)):r?Ks(mu(l,i)):mu(l,i),c&&(e.c[l.p]=f.gc()),p=f.Jc();p.Ob();)b=u(p.Pb(),12),e.d[b.p]=h++;Er(t,f)}}function VUe(e,n,t){var i,r,c,o,l,f,h,b;for(c=ne(re(e.b.Jc().Pb())),h=ne(re(J7n(n.b))),i=A1(pc(e.a),h-t),r=A1(pc(n.a),t-c),b=gi(i,r),A1(b,1/(h-c)),this.a=b,this.b=new Ce,l=!0,o=e.b.Jc(),o.Pb();o.Ob();)f=ne(re(o.Pb())),l&&f-t>Xee&&(this.b.Ec(t),l=!1),this.b.Ec(f);l&&this.b.Ec(t)}function lDn(e){var n,t,i,r;if(AIn(e,e.n),e.d.c.length>0){for(yj(e.c);obe(e,u(_(new L(e.e.a)),124))>5,n&=31,i>=e.d)return e.e<0?(mh(),rnn):(mh(),KS);if(c=e.d-i,r=oe($t,ni,30,c+1,15,1),dTn(r,c,e.a,i,n),e.e<0){for(t=0;t0&&e.a[t]<<32-n!=0){for(t=0;t=0?!1:(t=av((ls(),nc),r,n),t?(i=t.Gk(),(i>1||i==-1)&&xw(Vc(nc,t))!=3):!0)):!1}function gDn(e,n,t,i){var r,c,o,l,f,h,b,p,y,S;if(f=e.c.d,h=e.d.d,f.j!=h.j)for(S=e.b,b=null,l=null,o=OEn(e),o&&S.i&&(b=e.b.i.i,l=S.i.j),r=f.j,p=null;r!=h.j;)p=n==0?HB(r):X1e(r),c=xde(r,S.d[r.g],t),y=xde(p,S.d[p.g],t),o&&b&&l&&(r==b?JFe(c,b,l):p==b&&JFe(y,b,l)),Vt(i,gi(c,y)),r=p}function Obe(e,n,t){var i,r,c,o,l,f;if(i=cgn(t,e.length),o=e[i],c=gAe(t,o.length),o[c].k==(zn(),wr))for(f=n.j,r=0;r0&&(t[0]+=e.d,o-=t[0]),t[2]>0&&(t[2]+=e.d,o-=t[2]),c=k.Math.max(0,o),t[1]=k.Math.max(t[1],o),Qae(e,No,r.c+i.b+t[0]-(t[1]-o)/2,t),n==No&&(e.c.b=c,e.c.c=r.c+i.b+(c-o)/2)}function iXe(){this.c=oe(Jr,Jc,30,(De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])).length,15,1),this.b=oe(Jr,Jc,30,z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn]).length,15,1),this.a=oe(Jr,Jc,30,z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn]).length,15,1),use(this.c,Ki),use(this.b,Dr),use(this.a,Dr)}function kDn(e,n,t,i){var r,c,o,l,f;for(f=n.i,l=t[f.g][e.d[f.g]],r=!1,o=new L(n.d);o.a=r&&(e.c=!1,e.a=!1),e.b[i++]=r,e.b[i]=c,e.c||ov(e)}}function jDn(e,n,t){var i,r,c,o,l,f,h;for(h=n.d,e.a=new xo(h.c.length),e.c=new pt,l=new L(h);l.a=0?e.Ih(h,!1,!0):Gw(e,t,!1),61));e:for(c=p.Jc();c.Ob();){for(r=u(c.Pb(),57),b=0;be.d[o.p]&&(t+=Hae(e.b,c),C0(e.a,ve(c)));for(;!kj(e.a);)Ehe(e.b,u(A4(e.a),15).a)}return t}function uXe(e,n,t){var i,r,c,o;for(c=(!n.a&&(n.a=new pe(Jt,n,10,11)),n.a).i,r=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));r.e!=r.i.gc();)i=u(ft(r),26),(!i.a&&(i.a=new pe(Jt,i,10,11)),i.a).i==0||(c+=uXe(e,i,!1));if(t)for(o=Bi(n);o;)c+=(!o.a&&(o.a=new pe(Jt,o,10,11)),o.a).i,o=Bi(o);return c}function U2(e,n){var t,i,r,c;return e.Nj()?(i=null,r=e.Oj(),e.Rj()&&(i=e.Tj(e.Yi(n),null)),t=e.Gj(4,c=V4(e,n),null,n,r),e.Kj()&&c!=null&&(i=e.Mj(c,i)),i?(i.lj(t),i.mj()):e.Hj(t),c):(c=V4(e,n),e.Kj()&&c!=null&&(i=e.Mj(c,null),i&&i.mj()),c)}function CDn(e){var n,t,i,r,c,o,l,f,h,b;for(h=e.a,n=new ar,f=0,i=new L(e.d);i.al.d&&(b=l.d+l.a+h));t.c.d=b,n.a.yc(t,n),f=k.Math.max(f,t.c.d+t.c.a)}return f}function ODn(e,n,t){var i,r,c,o,l,f;for(o=u(T(e,(me(),wie)),16).Jc();o.Ob();){switch(c=u(o.Pb(),9),u(T(c,(Ne(),yu)),165).g){case 2:Or(c,n);break;case 4:Or(c,t)}for(r=new qn(Vn(bh(c).a.Jc(),new ee));ht(r);)i=u(tt(r),17),!(i.c&&i.d)&&(l=!i.d,f=u(T(i,Qve),12),l?Gr(i,f):lc(i,f))}}function Dc(){Dc=Y,QJ=new c2("COMMENTS",0),Kl=new c2("EXTERNAL_PORTS",1),cx=new c2("HYPEREDGES",2),WJ=new c2("HYPERNODES",3),S7=new c2("NON_FREE_PORTS",4),Nv=new c2("NORTH_SOUTH_PORTS",5),ux=new c2(MQe,6),j7=new c2("CENTER_LABELS",7),E7=new c2("END_LABELS",8),ZJ=new c2("PARTITIONS",9)}function NDn(e,n,t,i,r){return i<0?(i=uv(e,r,z(B(ze,1),Se,2,6,[mZ,vZ,yZ,kZ,uy,jZ,EZ,SZ,xZ,AZ,MZ,TZ]),n),i<0&&(i=uv(e,r,z(B(ze,1),Se,2,6,["Jan","Feb","Mar","Apr",uy,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),n)),i<0?!1:(t.k=i,!0)):i>0?(t.k=i-1,!0):!1}function DDn(e,n,t,i,r){return i<0?(i=uv(e,r,z(B(ze,1),Se,2,6,[mZ,vZ,yZ,kZ,uy,jZ,EZ,SZ,xZ,AZ,MZ,TZ]),n),i<0&&(i=uv(e,r,z(B(ze,1),Se,2,6,["Jan","Feb","Mar","Apr",uy,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),n)),i<0?!1:(t.k=i,!0)):i>0?(t.k=i-1,!0):!1}function IDn(e,n,t,i,r,c){var o,l,f,h;if(l=32,i<0){if(n[0]>=e.length||(l=ic(e,n[0]),l!=43&&l!=45)||(++n[0],i=Az(e,n),i<0))return!1;l==45&&(i=-i)}return l==32&&n[0]-t==2&&r.b==2&&(f=new t$,h=f.q.getFullYear()-K0+K0-80,o=h%100,c.a=i==o,i+=(h/100|0)*100+(i=0?B0(e):sE(B0(Td(e)))),VS[n]=C$(Gh(e,n),0)?B0(Gh(e,n)):sE(B0(Td(Gh(e,n)))),e=ac(e,5);for(;n=h&&(f=i);f&&(b=k.Math.max(b,f.a.o.a)),b>y&&(p=h,y=b)}return p}function RDn(e){var n,t,i,r,c,o,l;for(c=new vd(u(Nt(new h5),51)),l=Dr,t=new L(e.d);t.aiWe?Cr(f,e.b):i<=iWe&&i>rWe?Cr(f,e.d):i<=rWe&&i>cWe?Cr(f,e.c):i<=cWe&&Cr(f,e.a),c=fXe(e,f,c);return r}function aXe(e,n,t,i){var r,c,o,l,f,h;for(r=(i.c+i.a)/2,qs(n.j),Vt(n.j,r),qs(t.e),Vt(t.e,r),h=new dAe,l=new L(e.f);l.a1,l&&(i=new je(r,t.b),Vt(n.a,i)),SE(n.a,z(B(Lr,1),Se,8,0,[y,p]))}function Ibe(e,n,t){var i,r;for(n=48;t--)hA[t]=t-48<<24>>24;for(i=70;i>=65;i--)hA[i]=i-65+10<<24>>24;for(r=102;r>=97;r--)hA[r]=r-97+10<<24>>24;for(c=0;c<10;c++)TG[c]=48+c&yr;for(e=10;e<=15;e++)TG[e]=65+e-10&yr}function gXe(e,n){n.Tg("Process graph bounds",1),he(e,(Ti(),wre),cC(HY(k2(new wn(null,new pn(e.b,16)),new Zq)))),he(e,pre,cC(HY(k2(new wn(null,new pn(e.b,16)),new Bs)))),he(e,mye,cC(JY(k2(new wn(null,new pn(e.b,16)),new yM)))),he(e,vye,cC(JY(k2(new wn(null,new pn(e.b,16)),new kM)))),n.Ug()}function HDn(e){var n,t,i,r,c;r=u(T(e,(Ne(),jg)),22),c=u(T(e,vH),22),t=new je(e.f.a+e.d.b+e.d.c,e.f.b+e.d.d+e.d.a),n=new wc(t),r.Gc((Vs(),Lm))&&(i=u(T(e,M7),8),c.Gc((_s(),q7))&&(i.a<=0&&(i.a=20),i.b<=0&&(i.b=20)),n.a=k.Math.max(t.a,i.a),n.b=k.Math.max(t.b,i.b)),Re($e(T(e,Rie)))||gLn(e,t,n)}function GDn(e){var n,t,i,r,c,o,l;for(n=!1,t=0,r=new L(e.d.b);r.a>19!=0)return"-"+wXe(e8(e));for(t=e,i="";!(t.l==0&&t.m==0&&t.h==0);){if(r=sY(tF),t=mge(t,r,!0),n=""+NAe(Z0),!(t.l==0&&t.m==0&&t.h==0))for(c=9-n.length;c>0;c--)n="0"+n;i=n+i}return i}function qDn(){if(!Object.create||!Object.getOwnPropertyNames)return!1;var e="__proto__",n=Object.create(null);if(n[e]!==void 0)return!1;var t=Object.getOwnPropertyNames(n);return!(t.length!=0||(n[e]=42,n[e]!==42)||Object.getOwnPropertyNames(n).length==0)}function UDn(e,n,t){var i,r,c,o,l,f,h,b,p;for(i=t.c,r=t.d,l=_a(n.c),f=_a(n.d),i==n.c?(l=vbe(e,l,r),f=pGe(n.d)):(l=pGe(n.c),f=vbe(e,f,r)),h=new qP(n.a),Xi(h,l,h.a,h.a.a),Xi(h,f,h.c.b,h.c),o=n.c==i,p=new cxe,c=0;c=e.a||!b0e(n,t))return-1;if(x2(u(i.Kb(n),20)))return 1;for(r=0,o=u(i.Kb(n),20).Jc();o.Ob();)if(c=u(o.Pb(),17),f=c.c.i==n?c.d.i:c.c.i,l=Pbe(e,f,t,i),l==-1||(r=k.Math.max(r,l),r>e.c-1))return-1;return r+1}function Ja(){Ja=Y,UH=new Vr((Xt(),$7),1.3),Mln=new Vr(Om,(Pn(),!1)),k6e=new pw(15),Bx=new Vr(o1,k6e),zx=new Vr(Vd,15),Eln=ND,Aln=Tg,Tln=Yv,Cln=ab,xln=Vv,qre=LD,Oln=Nm,x6e=(nge(),yln),S6e=vln,Xre=jln,A6e=kln,y6e=wln,Ure=gln,v6e=bln,E6e=mln,p6e=_D,Sln=wce,xD=aln,w6e=fln,AD=hln,j6e=pln,m6e=dln}function pXe(e,n){var t,i,r,c,o,l;if(ue(n)===ue(e))return!0;if(!X(n,16)||(i=u(n,16),l=e.gc(),i.gc()!=l))return!1;if(o=i.Jc(),e.Wi()){for(t=0;t0){if(e.Zj(),n!=null){for(c=0;c>24;case 97:case 98:case 99:case 100:case 101:case 102:return e-97+10<<24>>24;case 65:case 66:case 67:case 68:case 69:case 70:return e-65+10<<24>>24;default:throw $(new sh("Invalid hexadecimal"))}}function vXe(e,n,t,i){var r,c,o,l,f,h;for(f=tW(e,t),h=tW(n,t),r=!1;f&&h&&(i||WSn(f,h,t));)o=tW(f,t),l=tW(h,t),iO(n),iO(e),c=f.c,tZ(f,!1),tZ(h,!1),t?(z0(n,h.p,c),n.p=h.p,z0(e,f.p+1,c),e.p=f.p):(z0(e,f.p,c),e.p=f.p,z0(n,h.p+1,c),n.p=h.p),Or(f,null),Or(h,null),f=o,h=l,r=!0;return r}function yXe(e){switch(e.g){case 0:return new rP;case 1:return new cP;case 3:return new CMe;case 4:return new A6;case 5:return new rNe;case 6:return new ko;case 2:return new uP;case 7:return new kT;case 8:return new yT;default:throw $(new Gn("No implementation is available for the layerer "+(e.f!=null?e.f:""+e.g)))}}function YDn(e,n,t,i){var r,c,o,l,f;for(r=!1,c=!1,l=new L(i.j);l.a=n.length)throw $(new jo("Greedy SwitchDecider: Free layer not in graph."));this.c=n[e],this.e=new CC(i),$Y(this.e,this.c,(De(),Kn)),this.i=new CC(i),$Y(this.i,this.c,et),this.f=new CDe(this.c),this.a=!c&&r.i&&!r.s&&this.c[0].k==(zn(),wr),this.a&&mTn(this,e,n.length)}function jXe(e,n){var t,i,r,c,o,l;c=!e.B.Gc((_s(),UD)),o=e.B.Gc(Cce),e.a=new cJe(o,c,e.c),e.n&&sae(e.a.n,e.n),xX(e.g,(ga(),No),e.a),n||(i=new JE(1,c,e.c),i.n.a=e.k,M4(e.p,(De(),Xn),i),r=new JE(1,c,e.c),r.n.d=e.k,M4(e.p,bt,r),l=new JE(0,c,e.c),l.n.c=e.k,M4(e.p,Kn,l),t=new JE(0,c,e.c),t.n.b=e.k,M4(e.p,et,t))}function WDn(e){var n,t,i;switch(n=u(T(e.d,(Ne(),Y1)),222),n.g){case 2:t=zRn(e);break;case 3:t=(i=new Ce,Zi(si(So(ou(ou(new wn(null,new pn(e.d.b,16)),new Wg),new ZI),new vk),new l0),new Hje(i)),i);break;default:throw $(new Uc("Compaction not supported for "+n+" edges."))}fPn(e,t),rc(new nt(e.g),new Bje(e))}function ZDn(e,n){var t,i,r,c,o,l,f;if(n.Tg("Process directions",1),t=u(T(e,(Mu(),mp)),86),t!=(vr(),Za))for(r=St(e.b,0);r.b!=r.d.c;){switch(i=u(jt(r),40),l=u(T(i,(Ti(),jD)),15).a,f=u(T(i,ED),15).a,t.g){case 4:f*=-1;break;case 1:c=l,l=f,f=c;break;case 2:o=l,l=-f,f=o}he(i,jD,ve(l)),he(i,ED,ve(f))}n.Ug()}function eIn(e){var n,t,i,r,c,o,l,f;for(f=new BPe,l=new L(e.a);l.a0&&n=0)return!1;if(n.p=t.b,Te(t.e,n),r==(zn(),dr)||r==wo){for(o=new L(n.j);o.ae.d[l.p]&&(t+=Hae(e.b,c),C0(e.a,ve(c)))):++o;for(t+=e.b.d*o;!kj(e.a);)Ehe(e.b,u(A4(e.a),15).a)}return t}function IXe(e){var n,t,i,r,c,o;return c=0,n=ff(e),n.ik()&&(c|=4),(e.Bb&as)!=0&&(c|=2),X(e,103)?(t=u(e,19),r=Oc(t),(t.Bb&Bu)!=0&&(c|=32),r&&(dt(E2(r)),c|=8,o=r.t,(o>1||o==-1)&&(c|=16),(r.Bb&Bu)!=0&&(c|=64)),(t.Bb&Ec)!=0&&(c|=U0),c|=qf):X(n,459)?c|=512:(i=n.ik(),i&&(i.i&1)!=0&&(c|=256)),(e.Bb&512)!=0&&(c|=128),c}function hIn(e,n){var t;return e.f==Hce?(t=xw(Vc((ls(),nc),n)),e.e?t==4&&n!=(ey(),Ky)&&n!=(ey(),Xy)&&n!=(ey(),Gce)&&n!=(ey(),qce):t==2):e.d&&(e.d.Gc(n)||e.d.Gc(D4(Vc((ls(),nc),n)))||e.d.Gc(av((ls(),nc),e.b,n)))?!0:e.f&&jbe((ls(),e.f),BC(Vc(nc,n)))?(t=xw(Vc(nc,n)),e.e?t==4:t==2):!1}function dIn(e,n){var t,i,r,c,o,l,f,h;for(c=new Ce,n.b.c.length=0,t=u(gs(Eae(new wn(null,new pn(new nt(e.a.b),1))),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),r=t.Jc();r.Ob();)if(i=u(r.Pb(),15),o=Pae(e.a,i),o.b!=0)for(l=new Xu(n),Hn(c.c,l),l.p=i.a,h=St(o,0);h.b!=h.d.c;)f=u(jt(h),9),Or(f,l);Er(n.b,c)}function _W(e){var n,t,i,r,c,o,l;for(l=new pt,i=new L(e.a.b);i.aag&&(r-=ag),l=u(ye(i,Fy),8),h=l.a,p=l.b+e,c=k.Math.atan2(p,h),c<0&&(c+=ag),c+=n,c>ag&&(c-=ag),Oa(),Bf(1e-10),k.Math.abs(r-c)<=1e-10||r==c||isNaN(r)&&isNaN(c)?0:rc?1:Lb(isNaN(r),isNaN(c))}function Fbe(e,n,t,i){var r,c,o;n&&(c=ne(re(T(n,(Ti(),Xd))))+i,o=t+ne(re(T(n,PH)))/2,he(n,jD,ve(Rt(Pu(k.Math.round(c))))),he(n,ED,ve(Rt(Pu(k.Math.round(o))))),n.d.b==0||Fbe(e,u(P$((r=St(new S1(n).a.d,0),new E3(r))),40),t+ne(re(T(n,PH)))+e.b,i+ne(re(T(n,L7)))),T(n,vre)!=null&&Fbe(e,u(T(n,vre),40),t,i))}function pIn(e,n){var t,i,r,c;if(c=u(ye(e,(Xt(),Qv)),64).g-u(ye(n,Qv),64).g,c!=0)return c;if(t=u(ye(e,kce),15),i=u(ye(n,kce),15),t&&i&&(r=t.a-i.a,r!=0))return r;switch(u(ye(e,Qv),64).g){case 1:return ki(e.i,n.i);case 2:return ki(e.j,n.j);case 3:return ki(n.i,e.i);case 4:return ki(n.j,e.j);default:throw $(new Uc(dwe))}}function Jbe(e){var n,t,i;return(e.Db&64)!=0?bW(e):(n=new il(R2e),t=e.k,t?Kt(Kt((n.a+=' "',n),t),'"'):(!e.n&&(e.n=new pe(ju,e,1,7)),e.n.i>0&&(i=(!e.n&&(e.n=new pe(ju,e,1,7)),u(K(e.n,0),157)).a,!i||Kt(Kt((n.a+=' "',n),i),'"'))),Kt(dw(Kt(dw(Kt(dw(Kt(dw((n.a+=" (",n),e.i),","),e.j)," | "),e.g),","),e.f),")"),n.a)}function _Xe(e){var n,t,i;return(e.Db&64)!=0?bW(e):(n=new il(B2e),t=e.k,t?Kt(Kt((n.a+=' "',n),t),'"'):(!e.n&&(e.n=new pe(ju,e,1,7)),e.n.i>0&&(i=(!e.n&&(e.n=new pe(ju,e,1,7)),u(K(e.n,0),157)).a,!i||Kt(Kt((n.a+=' "',n),i),'"'))),Kt(dw(Kt(dw(Kt(dw(Kt(dw((n.a+=" (",n),e.i),","),e.j)," | "),e.g),","),e.f),")"),n.a)}function mIn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;for(S=-1,A=0,b=n,p=0,y=b.length;p0&&++A;++S}return A}function vIn(e,n){var t,i,r,c,o;for(n==(DE(),cre)&&HO(u(pi(e.a,(z2(),ZN)),16)),r=u(pi(e.a,(z2(),ZN)),16).Jc();r.Ob();)switch(i=u(r.Pb(),107),t=u(Le(i.j,0),113).d.j,c=new bs(i.j),Cr(c,new M5),n.g){case 2:oW(e,c,t,(_w(),ib),1);break;case 1:case 0:o=lNn(c),oW(e,new T0(c,0,o),t,(_w(),ib),0),oW(e,new T0(c,o,c.c.length),t,ib,1)}}function yIn(e){var n,t,i,r,c,o,l;for(r=u(T(e,(me(),ap)),9),i=e.j,t=(vn(0,i.c.length),u(i.c[0],12)),o=new L(r.j);o.ar.p?(xr(c,bt),c.d&&(l=c.o.b,n=c.a.b,c.a.b=l-n)):c.j==bt&&r.p>e.p&&(xr(c,Xn),c.d&&(l=c.o.b,n=c.a.b,c.a.b=-(l-n)));break}return r}function Hbe(e,n){var t,i,r,c,o,l,f;if(n==null||n.length==0)return null;if(r=u(lo(e.a,n),144),!r){for(i=(l=new ct(e.b).a.vc().Jc(),new Ji(l));i.a.Ob();)if(t=(c=u(i.a.Pb(),45),u(c.kd(),144)),o=t.c,f=n.length,bn(o.substr(o.length-f,f),n)&&(n.length==o.length||ic(o,o.length-n.length-1)==46)){if(r)return null;r=t}r&&Kc(e.a,n,r)}return r}function M8(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(c=new je(n,t),b=new L(e.a);b.a1,l&&(i=new je(r,t.b),Vt(n.a,i)),SE(n.a,z(B(Lr,1),Se,8,0,[y,p]))}function G0(){G0=Y,AH=new u2(ma,0),gD=new u2("NIKOLOV",1),wD=new u2("NIKOLOV_PIXEL",2),P4e=new u2("NIKOLOV_IMPROVED",3),$4e=new u2("NIKOLOV_IMPROVED_PIXEL",4),L4e=new u2("DUMMYNODE_PERCENTAGE",5),R4e=new u2("NODECOUNT_PERCENTAGE",6),MH=new u2("NO_BOUNDARY",7),D7=new u2("MODEL_ORDER_LEFT_TO_RIGHT",8),Sx=new u2("MODEL_ORDER_RIGHT_TO_LEFT",9)}function PW(e,n){var t,i,r,c,o,l,f,h,b,p,y,S;return b=null,y=hbe(e,n),i=null,l=u(ye(n,(Xt(),Tfn)),300),l?i=l:i=(jE(),HD),S=i,S==(jE(),HD)&&(r=null,h=u(Bn(e.r,y),300),h?r=h:r=Tce,S=r),ei(e.r,n,S),c=null,f=u(ye(n,Mfn),278),f?c=f:c=(u8(),$D),p=c,p==(u8(),$D)&&(o=null,t=u(Bn(e.b,y),278),t?o=t:o=uG,p=o),b=u(ei(e.b,n,p),278),b}function NIn(e){var n,t,i,r,c;for(i=e.length,n=new jj,c=0;c=40,o&&O_n(e),JLn(e),lDn(e),t=$Fe(e),i=0;t&&i0&&Vt(e.g,c)):(e.d[o]-=h+1,e.d[o]<=0&&e.a[o]>0&&Vt(e.f,c))))}function XXe(e,n,t,i){var r,c,o,l,f,h,b;for(f=new je(t,i),Nr(f,u(T(n,(Ti(),_7)),8)),b=St(n.b,0);b.b!=b.d.c;)h=u(jt(b),40),gi(h.e,f),Vt(e.b,h);for(l=u(gs(yae(new wn(null,new pn(n.a,16))),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16).Jc();l.Ob();){for(o=u(l.Pb(),65),c=St(o.a,0);c.b!=c.d.c;)r=u(jt(c),8),r.a+=f.a,r.b+=f.b;Vt(e.a,o)}}function Qbe(e,n){var t,i,r,c;if(0<(X(e,18)?u(e,18).gc():aa(e.Jc()))){if(r=n,1=0&&f1)&&n==1&&u(e.a[e.b],9).k==(zn(),Uu)?iy(u(e.a[e.b],9),(al(),s1)):i&&(!t||(e.c-e.b&e.a.length-1)>1)&&n==1&&u(e.a[e.c-1&e.a.length-1],9).k==(zn(),Uu)?iy(u(e.a[e.c-1&e.a.length-1],9),(al(),hb)):(e.c-e.b&e.a.length-1)==2?(iy(u(CE(e),9),(al(),s1)),iy(u(CE(e),9),hb)):TOn(e,r),zae(e)}function KIn(e){var n,t,i,r,c,o,l,f;for(f=new pt,n=new gX,o=e.Jc();o.Ob();)r=u(o.Pb(),9),l=mw(nC(new Nb,r),n),Ko(f.f,r,l);for(c=e.Jc();c.Ob();)for(r=u(c.Pb(),9),i=new qn(Vn(Ni(r).a.Jc(),new ee));ht(i);)t=u(tt(i),17),!cc(t)&&Hf(Nf(Of(Cf(Df(new tf,k.Math.max(1,u(T(t,(Ne(),g4e)),15).a)),1),u(Bn(f,t.c.i),124)),u(Bn(f,t.d.i),124)));return n}function YXe(e,n,t,i){var r,c,o,l,f,h,b,p,y,S;if(x8n(e,n,t),c=n[t],S=i?(De(),Kn):(De(),et),Vwn(n.length,t,i)){for(r=n[i?t-1:t+1],rhe(e,r,i?(Nc(),Do):(Nc(),ys)),f=c,b=0,y=f.length;bc*2?(b=new dB(p),h=us(o)/Gs(o),f=uZ(b,n,new t4,t,i,r,h),gi(la(b.e),f),p.c.length=0,c=0,Hn(p.c,b),Hn(p.c,o),c=us(b)*Gs(b)+us(o)*Gs(o)):(Hn(p.c,o),c+=us(o)*Gs(o));return p}function YIn(e,n){var t,i,r,c,o,l,f;for(n.Tg("Port order processing",1),f=u(T(e,(Ne(),b4e)),421),i=new L(e.b);i.at?n:t;h<=p;++h)h==t?l=i++:(c=r[h],b=A.$l(c.Jk()),h==n&&(f=h==p&&!b?i-1:i),b&&++i);return y=u(RE(e,n,t),75),l!=f&&s9(e,new tO(e.e,7,o,ve(l),S.kd(),f)),y}}else return u(jW(e,n,t),75);return u(RE(e,n,t),75)}function Wbe(e,n){var t,i,r,c,o,l,f,h,b,p;for(p=0,c=new P3,C0(c,n);c.b!=c.c;)for(f=u(A4(c),218),h=0,b=u(T(n.j,(Ne(),u1)),269),u(T(n.j,bx),329),o=ne(re(T(n.j,lD))),l=ne(re(T(n.j,Aie))),b!=(F1(),ob)&&(h+=o*LOn(n.j,f.e,b),h+=l*mIn(n.j,f.e)),p+=vHe(f.d,f.e)+h,r=new L(f.b);r.a=0&&(l=axn(e,o),!(l&&(h<22?f.l|=1<>>1,o.m=b>>>1|(p&1)<<21,o.l=y>>>1|(b&1)<<21,--h;return t&&ZY(f),c&&(i?(Z0=e8(e),r&&(Z0=xze(Z0,(G9(),Eme)))):Z0=_o(e.l,e.m,e.h)),f}function ZIn(e,n){var t,i,r,c,o,l,f,h,b,p;for(h=e.e[n.c.p][n.p]+1,f=n.c.a.c.length+1,l=new L(e.a);l.a0&&(Yn(0,e.length),e.charCodeAt(0)==45||(Yn(0,e.length),e.charCodeAt(0)==43))?1:0,i=o;it)throw $(new sh(Yw+e+'"'));return l}function e_n(e){var n,t,i,r,c,o,l;for(o=new Si,c=new L(e.a);c.a=e.length)return t.o=0,!0;switch(ic(e,n[0])){case 43:r=1;break;case 45:r=-1;break;default:return t.o=0,!0}if(++n[0],c=n[0],o=Az(e,n),o==0&&n[0]==c)return!1;if(n[0]l&&(l=r,b.c.length=0),r==l&&Te(b,new jc(t.c.i,t)));jn(),Cr(b,e.c),Pb(e.b,f.p,b)}}function u_n(e,n){var t,i,r,c,o,l,f,h,b;for(o=new L(n.b);o.al&&(l=r,b.c.length=0),r==l&&Te(b,new jc(t.d.i,t)));jn(),Cr(b,e.c),Pb(e.f,f.p,b)}}function o_n(e){var n,t,i,r,c,o,l;for(c=Ia(e),r=new ut((!e.e&&(e.e=new Nn(pr,e,7,4)),e.e));r.e!=r.i.gc();)if(i=u(ft(r),85),l=iu(u(K((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c),0),84)),!C2(l,c))return!0;for(t=new ut((!e.d&&(e.d=new Nn(pr,e,8,5)),e.d));t.e!=t.i.gc();)if(n=u(ft(t),85),o=iu(u(K((!n.b&&(n.b=new Nn(vt,n,4,7)),n.b),0),84)),!C2(o,c))return!0;return!1}function s_n(e){var n,t,i,r,c;i=u(T(e,(me(),wi)),26),c=u(ye(i,(Ne(),jg)),182).Gc((Vs(),Og)),e.e||(r=u(T(e,po),22),n=new je(e.f.a+e.d.b+e.d.c,e.f.b+e.d.d+e.d.a),r.Gc((Dc(),Kl))?(ji(i,Wi,(Br(),to)),Xw(i,n.a,n.b,!1,!0)):Re($e(ye(i,Rie)))||Xw(i,n.a,n.b,!0,!0)),c?ji(i,jg,nn(Og)):ji(i,jg,(t=u(sa(tA),10),new _l(t,u(_f(t,t.length),10),0)))}function l_n(e,n){var t,i,r,c,o,l,f,h;if(h=$e(T(n,(Mu(),ksn))),h==null||(_n(h),h)){for(RCn(e,n),r=new Ce,f=St(n.b,0);f.b!=f.d.c;)o=u(jt(f),40),t=P0e(e,o,null),t&&($u(t,n),Hn(r.c,t));if(e.a=null,e.b=null,r.c.length>1)for(i=new L(r);i.a=0&&l!=t&&(c=new Ir(e,1,l,o,null),i?i.lj(c):i=c),t>=0&&(c=new Ir(e,1,t,l==t?o:null,n),i?i.lj(c):i=c)),i}function WXe(e){var n,t,i;if(e.b==null){if(i=new pd,e.i!=null&&(Bc(i,e.i),i.a+=":"),(e.f&256)!=0){for((e.f&256)!=0&&e.a!=null&&(j5n(e.i)||(i.a+="//"),Bc(i,e.a)),e.d!=null&&(i.a+="/",Bc(i,e.d)),(e.f&16)!=0&&(i.a+="/"),n=0,t=e.j.length;ny?!1:(p=(f=fS(i,y,!1),f.a),b+l+p<=n.b&&(eO(t,c-t.s),t.c=!0,eO(i,c-t.s),IO(i,t.s,t.t+t.d+l),i.k=!0,i1e(t.q,i),S=!0,r&&(vB(n,i),i.j=n,e.c.length>o&&($O((vn(o,e.c.length),u(e.c[o],186)),i),(vn(o,e.c.length),u(e.c[o],186)).a.c.length==0&&Ad(e,o)))),S)}function w_n(e,n){var t,i,r,c,o,l;if(n.Tg("Partition midprocessing",1),r=new Tw,Zi(si(new wn(null,new pn(e.a,16)),new w6),new Eje(r)),r.d!=0){for(l=u(gs(Eae((c=r.i,new wn(null,(c||(r.i=new R3(r,r.c))).Lc()))),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),i=l.Jc(),t=u(i.Pb(),15);i.Ob();)o=u(i.Pb(),15),sNn(u(pi(r,t),22),u(pi(r,o),22)),t=o;n.Ug()}}function uS(e,n){var t,i,r,c,o;if(e.Ab){if(e.Ab){if(o=e.Ab.i,o>0){if(r=u(e.Ab.g,1995),n==null){for(c=0;ct.s&&lf+A&&(O=p.g+y.g,y.a=(y.g*y.a+p.g*p.a)/O,y.g=O,p.f=y,t=!0)),c=l,p=y;return t}function v_n(e,n,t){var i,r,c,o,l,f,h,b;for(t.Tg(UQe,1),Hu(e.b),Hu(e.a),l=null,c=St(n.b,0);!l&&c.b!=c.d.c;)h=u(jt(c),40),Re($e(T(h,(Ti(),fb))))&&(l=h);for(f=new Si,Xi(f,l,f.c.b,f.c),NVe(e,f),b=St(n.b,0);b.b!=b.d.c;)h=u(jt(b),40),o=Pt(T(h,(Ti(),Ix))),r=lo(e.b,o)!=null?u(lo(e.b,o),15).a:0,he(h,gre,ve(r)),i=1+(lo(e.a,o)!=null?u(lo(e.a,o),15).a:0),he(h,pye,ve(i));t.Ug()}function rKe(e){r2(e,new Jw(t2(Zp(n2(e2(new dd,tp),"ELK Box"),"Algorithm for packing of unconnected boxes, i.e. graphs without edges."),new GM))),Ee(e,tp,nm,g9e),Ee(e,tp,em,15),Ee(e,tp,EN,ve(0)),Ee(e,tp,C2e,_e(h9e)),Ee(e,tp,wv,_e(gfn)),Ee(e,tp,hy,_e(wfn)),Ee(e,tp,G8,wWe),Ee(e,tp,jS,_e(d9e)),Ee(e,tp,dy,_e(b9e)),Ee(e,tp,O2e,_e(lce)),Ee(e,tp,TF,_e(bfn))}function cKe(e,n){var t,i,r,c,o,l,f,h,b;if(r=e.i,o=r.o.a,c=r.o.b,o<=0&&c<=0)return De(),ku;switch(h=e.n.a,b=e.n.b,l=e.o.a,t=e.o.b,n.g){case 2:case 1:if(h<0)return De(),Kn;if(h+l>o)return De(),et;break;case 4:case 3:if(b<0)return De(),Xn;if(b+t>c)return De(),bt}return f=(h+l/2)/o,i=(b+t/2)/c,f+i<=1&&f-i<=0?(De(),Kn):f+i>=1&&f-i>=0?(De(),et):i<.5?(De(),Xn):(De(),bt)}function uKe(e,n,t,i,r,c,o){var l,f,h,b,p,y;for(y=new g4,h=n.Jc();h.Ob();)for(l=u(h.Pb(),837),p=new L(l.Pf());p.a0?l.a?(h=l.b.Kf().b,r>h&&(e.v||l.c.d.c.length==1?(o=(r-h)/2,l.d.d=o,l.d.a=o):(t=u(Le(l.c.d,0),187).Kf().b,i=(t-h)/2,l.d.d=k.Math.max(0,i),l.d.a=r-i-h))):l.d.a=e.t+r:cE(e.u)&&(c=m0e(l.b),c.d<0&&(l.d.d=-c.d),c.d+c.a>l.b.Kf().b&&(l.d.a=c.d+c.a-l.b.Kf().b))}function Gf(){Gf=Y,ky=new Vr((Xt(),PD),ve(1)),vJ=new Vr(Vd,80),Stn=new Vr(q9e,5),btn=new Vr($7,H8),jtn=new Vr(Ece,ve(1)),Etn=new Vr(Sce,(Pn(),!0)),c3e=new pw(50),ytn=new Vr(o1,c3e),t3e=_D,u3e=Kx,gtn=new Vr(dce,!1),r3e=LD,mtn=Om,vtn=ab,ptn=Tg,wtn=Vv,ktn=Nm,i3e=(T0e(),otn),kte=atn,mJ=utn,yte=stn,o3e=ftn,Mtn=z7,Ttn=rG,Atn=Im,xtn=B7,s3e=(G4(),Pm),new Vr(Hy,s3e)}function j_n(e,n){var t;switch(lO(e)){case 6:return $r(n);case 7:return s2(n);case 8:return o2(n);case 3:return Array.isArray(n)&&(t=lO(n),!(t>=14&&t<=16));case 11:return n!=null&&typeof n===aZ;case 12:return n!=null&&(typeof n===sN||typeof n==aZ);case 0:return PQ(n,e.__elementTypeId$);case 2:return pV(n)&&n.Rm!==Qn;case 1:return pV(n)&&n.Rm!==Qn||PQ(n,e.__elementTypeId$);default:return!0}}function E_n(e){var n,t,i,r;i=e.o,h2(),e.A.dc()||di(e.A,Qme)?r=i.a:(e.D?r=k.Math.max(i.a,QE(e.f)):r=QE(e.f),e.A.Gc((Vs(),GD))&&!e.B.Gc((_s(),iA))&&(r=k.Math.max(r,QE(u(zc(e.p,(De(),Xn)),253))),r=k.Math.max(r,QE(u(zc(e.p,bt),253)))),n=nze(e),n&&(r=k.Math.max(r,n.a))),Re($e(e.e.Rf().mf((Xt(),Om))))?i.a=k.Math.max(i.a,r):i.a=r,t=e.f.i,t.c=0,t.b=r,HW(e.f)}function oKe(e,n){var t,i,r,c;return i=k.Math.min(k.Math.abs(e.c-(n.c+n.b)),k.Math.abs(e.c+e.b-n.c)),c=k.Math.min(k.Math.abs(e.d-(n.d+n.a)),k.Math.abs(e.d+e.a-n.d)),t=k.Math.abs(e.c+e.b/2-(n.c+n.b/2)),t>e.b/2+n.b/2||(r=k.Math.abs(e.d+e.a/2-(n.d+n.a/2)),r>e.a/2+n.a/2)?1:t==0&&r==0?0:t==0?c/r+1:r==0?i/t+1:k.Math.min(i/t,c/r)+1}function S_n(e,n){var t,i,r,c,o,l,f;for(c=0,l=0,f=0,r=new L(e.f.e);r.a0&&e.d!=(yE(),Ste)&&(l+=o*(i.d.a+e.a[n.a][i.a]*(n.d.a-i.d.a)/t)),t>0&&e.d!=(yE(),jte)&&(f+=o*(i.d.b+e.a[n.a][i.a]*(n.d.b-i.d.b)/t)));switch(e.d.g){case 1:return new je(l/c,n.d.b);case 2:return new je(n.d.a,f/c);default:return new je(l/c,f/c)}}function sKe(e){var n,t,i,r,c,o;for(t=(!e.a&&(e.a=new mr(kl,e,5)),e.a).i+2,o=new xo(t),Te(o,new je(e.j,e.k)),Zi(new wn(null,(!e.a&&(e.a=new mr(kl,e,5)),new pn(e.a,16))),new tSe(o)),Te(o,new je(e.b,e.c)),n=1;n0&&(kO(f,!1,(vr(),Zc)),kO(f,!0,ru)),Ao(n.g,new eTe(e,t)),ei(e.g,n,t)}function nge(){nge=Y,pln=new fn(s2e,(Pn(),!1)),ve(-1),fln=new fn(l2e,ve(-1)),ve(-1),aln=new fn(f2e,ve(-1)),hln=new fn(a2e,!1),dln=new fn(h2e,!1),g6e=(VR(),Kre),kln=new fn(d2e,g6e),jln=new fn(b2e,-1),b6e=(XB(),Gre),yln=new fn(g2e,b6e),vln=new fn(w2e,!0),h6e=(rB(),Vre),wln=new fn(p2e,h6e),gln=new fn(m2e,!1),ve(1),bln=new fn(v2e,ve(1)),d6e=(JB(),Yre),mln=new fn(y2e,d6e)}function aKe(){aKe=Y;var e;for(Nme=z(B($t,1),ni,30,15,[-1,-1,30,19,15,13,11,11,10,9,9,8,8,8,8,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5]),rte=oe($t,ni,30,37,15,1),nnn=z(B($t,1),ni,30,15,[-1,-1,63,40,32,28,25,23,21,20,19,19,18,18,17,17,16,16,16,15,15,15,15,14,14,14,14,14,14,13,13,13,13,13,13,13,13]),Dme=oe(Ep,SYe,30,37,14,1),e=2;e<=36;e++)rte[e]=sc(k.Math.pow(e,Nme[e])),Dme[e]=BO(hN,rte[e])}function x_n(e){var n;if((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i!=1)throw $(new Gn(zWe+(!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i));return n=new xs,KY(u(K((!e.b&&(e.b=new Nn(vt,e,4,7)),e.b),0),84))&&fc(n,VVe(e,KY(u(K((!e.b&&(e.b=new Nn(vt,e,4,7)),e.b),0),84)),!1)),KY(u(K((!e.c&&(e.c=new Nn(vt,e,5,8)),e.c),0),84))&&fc(n,VVe(e,KY(u(K((!e.c&&(e.c=new Nn(vt,e,5,8)),e.c),0),84)),!0)),n}function hKe(e,n){var t,i,r,c,o;for(n.d?r=e.a.c==(ah(),pp)?rr(n.b):Ni(n.b):r=e.a.c==(ah(),Ud)?rr(n.b):Ni(n.b),c=!1,i=new qn(Vn(r.a.Jc(),new ee));ht(i);)if(t=u(tt(i),17),o=Re(e.a.f[e.a.g[n.b.p].p]),!(!o&&!cc(t)&&t.c.i.c==t.d.i.c)&&!(Re(e.a.n[e.a.g[n.b.p].p])||Re(e.a.n[e.a.g[n.b.p].p]))&&(c=!0,rf(e.b,e.a.g[KSn(t,n.b).p])))return n.c=!0,n.a=t,n;return n.c=c,n.a=null,n}function tge(e,n,t){var i,r,c,o,l,f,h;if(i=t.gc(),i==0)return!1;if(e.Nj())if(f=e.Oj(),lde(e,n,t),o=i==1?e.Gj(3,null,t.Jc().Pb(),n,f):e.Gj(5,null,t,n,f),e.Kj()){for(l=i<100?null:new m0(i),c=n+i,r=n;r0){for(o=0;o>16==-15&&e.Cb.Vh()&&jY(new pY(e.Cb,9,13,t,e.c,Ld(Cs(u(e.Cb,62)),e))):X(e.Cb,88)&&e.Db>>16==-23&&e.Cb.Vh()&&(n=e.c,X(n,88)||(n=(yn(),jf)),X(t,88)||(t=(yn(),jf)),jY(new pY(e.Cb,9,10,t,n,Ld(Vu(u(e.Cb,29)),e)))))),e.c}function gKe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A;if(n==t)return!0;if(n=ube(e,n),t=ube(e,t),i=HQ(n),i){if(b=HQ(t),b!=i)return b?(f=i.kk(),A=b.kk(),f==A&&f!=null):!1;if(o=(!n.d&&(n.d=new mr(Rc,n,1)),n.d),c=o.i,y=(!t.d&&(t.d=new mr(Rc,t,1)),t.d),c==y.i){for(h=0;h0,l=UB(n,c),fle(t?l.b:l.g,n),Z3(l).c.length==1&&Xi(i,l,i.c.b,i.c),r=new jc(c,n),C0(e.o,r),qo(e.e.a,c))}function mKe(e,n){var t,i,r,c,o,l,f;return i=k.Math.abs(pR(e.b).a-pR(n.b).a),l=k.Math.abs(pR(e.b).b-pR(n.b).b),r=0,f=0,t=1,o=1,i>e.b.b/2+n.b.b/2&&(r=k.Math.min(k.Math.abs(e.b.c-(n.b.c+n.b.b)),k.Math.abs(e.b.c+e.b.b-n.b.c)),t=1-r/i),l>e.b.a/2+n.b.a/2&&(f=k.Math.min(k.Math.abs(e.b.d-(n.b.d+n.b.a)),k.Math.abs(e.b.d+e.b.a-n.b.d)),o=1-f/l),c=k.Math.min(t,o),(1-c)*k.Math.sqrt(i*i+l*l)}function N_n(e){var n,t,i,r;for(cZ(e,e.e,e.f,(Cw(),lb),!0,e.c,e.i),cZ(e,e.e,e.f,lb,!1,e.c,e.i),cZ(e,e.e,e.f,Jv,!0,e.c,e.i),cZ(e,e.e,e.f,Jv,!1,e.c,e.i),T_n(e,e.c,e.e,e.f,e.i),i=new qr(e.i,0);i.b=65;t--)rh[t]=t-65<<24>>24;for(i=122;i>=97;i--)rh[i]=i-97+26<<24>>24;for(r=57;r>=48;r--)rh[r]=r-48+52<<24>>24;for(rh[43]=62,rh[47]=63,c=0;c<=25;c++)t0[c]=65+c&yr;for(o=26,f=0;o<=51;++o,f++)t0[o]=97+f&yr;for(e=52,l=0;e<=61;++e,l++)t0[e]=48+l&yr;t0[62]=43,t0[63]=47}function vKe(e,n){var t,i,r,c,o,l;return r=Whe(e),l=Whe(n),r==l?e.e==n.e&&e.a<54&&n.a<54?e.fn.f?1:0:(i=e.e-n.e,t=(e.d>0?e.d:k.Math.floor((e.a-1)*xYe)+1)-(n.d>0?n.d:k.Math.floor((n.a-1)*xYe)+1),t>i+1?r:t0&&(o=H3(o,NKe(i))),yJe(c,o))):rh&&(y=0,S+=f+n,f=0),M8(o,y,S),t=k.Math.max(t,y+b.a),f=k.Math.max(f,b.b),y+=b.a+n;return new je(t+n,S+f+n)}function uge(e,n){var t,i,r,c,o,l,f;if(!Ia(e))throw $(new Uc(BWe));if(i=Ia(e),c=i.g,r=i.f,c<=0&&r<=0)return De(),ku;switch(l=e.i,f=e.j,n.g){case 2:case 1:if(l<0)return De(),Kn;if(l+e.g>c)return De(),et;break;case 4:case 3:if(f<0)return De(),Xn;if(f+e.f>r)return De(),bt}return o=(l+e.g/2)/c,t=(f+e.f/2)/r,o+t<=1&&o-t<=0?(De(),Kn):o+t>=1&&o-t>=0?(De(),et):t<.5?(De(),Xn):(De(),bt)}function __n(e,n,t,i,r){var c,o;if(c=mc(Rr(n[0],Ic),Rr(i[0],Ic)),e[0]=Rt(c),c=kw(c,32),t>=r){for(o=1;o0&&(r.b[o++]=0,r.b[o++]=c.b[0]-1),n=1;n0&&(Cb(f,f.d-r.d),r.c==(ha(),sb)&&tX(f,f.a-r.d),f.d<=0&&f.i>0&&Xi(n,f,n.c.b,n.c)));for(c=new L(e.f);c.a0&&(g0(l,l.i-r.d),r.c==(ha(),sb)&&AP(l,l.b-r.d),l.i<=0&&l.d>0&&Xi(t,l,t.c.b,t.c)))}function $_n(e,n,t,i,r){var c,o,l,f,h,b,p,y,S;for(jn(),Cr(e,new XM),o=DC(e),S=new Ce,y=new Ce,l=null,f=0;o.b!=0;)c=u(o.b==0?null:(at(o.b!=0),$l(o,o.a.a)),167),!l||us(l)*Gs(l)/21&&(f>us(l)*Gs(l)/2||o.b==0)&&(p=new dB(y),b=us(l)/Gs(l),h=uZ(p,n,new t4,t,i,r,b),gi(la(p.e),h),l=p,Hn(S.c,p),f=0,y.c.length=0));return Er(S,y),S}function Wu(e,n,t,i,r){yd();var c,o,l,f,h,b,p;if(Rfe(e,"src"),Rfe(t,"dest"),p=Us(e),f=Us(t),ffe((p.i&4)!=0,"srcType is not an array"),ffe((f.i&4)!=0,"destType is not an array"),b=p.c,o=f.c,ffe((b.i&1)!=0?b==o:(o.i&1)==0,"Array types don't match"),skn(e,n,t,i,r),(b.i&1)==0&&p!=f)if(h=B4(e),c=B4(t),ue(e)===ue(t)&&ni;)tr(c,l,h[--n]);else for(l=i+r;i0),i.a.Xb(i.c=--i.b),p>y+f&&As(i);for(o=new L(S);o.a0),i.a.Xb(i.c=--i.b)}}function B_n(){fi();var e,n,t,i,r,c;if(Xce)return Xce;for(e=new ul(4),V2(e,q0(qne,!0)),dS(e,q0("M",!0)),dS(e,q0("C",!0)),c=new ul(4),i=0;i<11;i++)ho(c,i,i);return n=new ul(4),V2(n,q0("M",!0)),ho(n,4448,4607),ho(n,65438,65439),r=new Kj(2),ug(r,e),ug(r,bA),t=new Kj(2),t.Hm(aR(c,q0("L",!0))),t.Hm(n),t=new A2(3,t),t=new zfe(r,t),Xce=t,Xce}function K2(e,n){var t,i,r,c,o,l,f,h;for(t=new RegExp(n,"g"),f=oe(ze,Se,2,0,6,1),i=0,h=e,c=null;;)if(l=t.exec(h),l==null||h==""){f[i]=h;break}else o=l.index,f[i]=(Yr(0,o,h.length),h.substr(0,o)),h=of(h,o+l[0].length,h.length),t.lastIndex=0,c==h&&(f[i]=(Yr(0,1,h.length),h.substr(0,1)),h=(Yn(1,h.length+1),h.substr(1))),c=h,++i;if(e.length>0){for(r=f.length;r>0&&f[r-1]=="";)--r;rb&&(b=f);for(h=k.Math.pow(4,n),b>h&&(h=b),y=(k.Math.log(h)-k.Math.log(1))/n,c=k.Math.exp(y),r=c,o=0;o0&&(p-=i[0]+e.c,i[0]+=e.c),i[2]>0&&(p-=i[2]+e.c),i[1]=k.Math.max(i[1],p),dR(e.a[1],t.c+n.b+i[0]-(i[1]-p)/2,i[1]);for(c=e.a,l=0,h=c.length;l0?(e.n.c.length-1)*e.i:0,i=new L(e.n);i.a1)for(i=St(r,0);i.b!=i.d.c;)for(t=u(jt(i),235),c=0,f=new L(t.e);f.a0&&(n[0]+=e.c,p-=n[0]),n[2]>0&&(p-=n[2]+e.c),n[1]=k.Math.max(n[1],p),bR(e.a[1],i.d+t.d+n[0]-(n[1]-p)/2,n[1]);else for(A=i.d+t.d,S=i.a-t.d-t.a,o=e.a,f=0,b=o.length;f=n.o&&t.f<=n.f||n.a*.5<=t.f&&n.a*1.5>=t.f){if(o=u(Le(n.n,n.n.c.length-1),208),o.e+o.d+t.g+r<=i&&(c=u(Le(n.n,n.n.c.length-1),208),c.f-e.f+t.f<=e.b||e.a.c.length==1))return ede(n,t),!0;if(n.s+t.g<=i&&n.t+n.d+t.f+r<=e.f+e.b)return Te(n.b,t),l=u(Le(n.n,n.n.c.length-1),208),Te(n.n,new PR(n.s,l.f+l.a+n.i,n.i)),Ide(u(Le(n.n,n.n.c.length-1),208),t),jKe(n,t),!0}return!1}function Hz(e,n,t,i){var r,c,o,l,f;if(f=Po(e.e.Ah(),n),r=u(e.g,122),Cc(),u(n,69).vk()){for(o=0;o0||$w(r.b.d,e.b.d+e.b.a)==0&&i.b<0||$w(r.b.d+r.b.a,e.b.d)==0&&i.b>0){l=0;break}}else l=k.Math.min(l,hqe(e,r,i));l=k.Math.min(l,SKe(e,c,l,i))}return l}function sge(e,n){var t,i,r,c,o,l,f;if(e.b<2)throw $(new Gn("The vector chain must contain at least a source and a target point."));for(r=(at(e.b!=0),u(e.a.a.c,8)),vC(n,r.a,r.b),f=new p4((!n.a&&(n.a=new mr(kl,n,5)),n.a)),o=St(e,1);o.a=0&&c!=t))throw $(new Gn($N));for(r=0,f=0;fne(Na(o.g,o.d[0]).a)?(at(f.b>0),f.a.Xb(f.c=--f.b),d2(f,o),r=!0):l.e&&l.e.gc()>0&&(c=(!l.e&&(l.e=new Ce),l.e).Kc(n),h=(!l.e&&(l.e=new Ce),l.e).Kc(t),(c||h)&&((!l.e&&(l.e=new Ce),l.e).Ec(o),++o.c));r||Hn(i.c,o)}function K_n(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;return p=e.a.i+e.a.g/2,y=e.a.i+e.a.g/2,A=n.i+n.g/2,I=n.j+n.f/2,l=new je(A,I),h=u(ye(n,(Xt(),Fy)),8),h.a=h.a+p,h.b=h.b+y,c=(l.b-h.b)/(l.a-h.a),i=l.b-c*l.a,O=t.i+t.g/2,R=t.j+t.f/2,f=new je(O,R),b=u(ye(t,Fy),8),b.a=b.a+p,b.b=b.b+y,o=(f.b-b.b)/(f.a-b.a),r=f.b-o*f.a,S=(i-r)/(o-c),h.a>>0,"0"+n.toString(16)),i="\\x"+of(t,t.length-2,t.length)):e>=Ec?(t=(n=e>>>0,"0"+n.toString(16)),i="\\v"+of(t,t.length-6,t.length)):i=""+String.fromCharCode(e&yr)}return i}function CKe(e,n){var t,i,r,c,o,l,f,h,b;for(c=new L(e.b);c.at){n.Ug();return}switch(u(T(e,(Ne(),Hie)),350).g){case 2:c=new E6;break;case 0:c=new Lp;break;default:c=new oM}if(i=c.mg(e,r),!c.ng())switch(u(T(e,kH),351).g){case 2:i=dqe(r,i);break;case 1:i=iGe(r,i)}VLn(e,r,i),n.Ug()}function oS(e,n){var t,i,r,c,o,l,f,h;n%=24,e.q.getHours()!=n&&(i=new k.Date(e.q.getTime()),i.setDate(i.getDate()+1),l=e.q.getTimezoneOffset()-i.getTimezoneOffset(),l>0&&(f=l/60|0,h=l%60,r=e.q.getDate(),t=e.q.getHours(),t+f>=24&&++r,c=new k.Date(e.q.getFullYear(),e.q.getMonth(),r,n+f,e.q.getMinutes()+h,e.q.getSeconds(),e.q.getMilliseconds()),e.q.setTime(c.getTime()))),o=e.q.getTime(),e.q.setTime(o+36e5),e.q.getHours()!=n&&e.q.setTime(o)}function nLn(e,n){var t,i,r,c;if(L4n(e.d,e.e),e.c.a.$b(),ne(re(T(n.j,(Ne(),lD))))!=0||ne(re(T(n.j,lD)))!=0)for(t=mv,ue(T(n.j,u1))!==ue((F1(),ob))&&he(n.j,(me(),rb),(Pn(),!0)),c=u(T(n.j,kx),15).a,r=0;rr&&++h,Te(o,(vn(l+h,n.c.length),u(n.c[l+h],15))),f+=(vn(l+h,n.c.length),u(n.c[l+h],15)).a-i,++t;t=I&&e.e[f.p]>A*e.b||V>=t*I)&&(Hn(y.c,l),l=new Ce,fc(o,c),c.a.$b(),h-=b,S=k.Math.max(S,h*e.b+O),h+=V,q=V,V=0,b=0,O=0);return new jc(S,y)}function qW(e){var n,t,i,r,c,o,l;if(!e.d){if(l=new hU,n=sA,c=n.a.yc(e,n),c==null){for(i=new ut(tu(e));i.e!=i.i.gc();)t=u(ft(i),29),er(l,qW(t));n.a.Ac(e)!=null,n.a.gc()==0}for(o=l.i,r=(!e.q&&(e.q=new pe(yf,e,11,10)),new ut(e.q));r.e!=r.i.gc();++o)u(ft(r),403);er(l,(!e.q&&(e.q=new pe(yf,e,11,10)),e.q)),_2(l),e.d=new N3((u(K(we((x0(),Rn).o),9),19),l.i),l.g),e.e=u(l.g,678),e.e==null&&(e.e=Qan),Ms(e).b&=-17}return e.d}function C8(e,n,t,i){var r,c,o,l,f,h;if(h=Po(e.e.Ah(),n),f=0,r=u(e.g,122),Cc(),u(n,69).vk()){for(o=0;o1||A==-1)if(p=u(O,72),y=u(b,72),p.dc())y.$b();else for(o=!!Oc(n),c=0,l=e.a?p.Jc():p.Gi();l.Ob();)h=u(l.Pb(),57),r=u(Pa(e,h),57),r?(o?(f=y.bd(r),f==-1?y.Ei(c,r):c!=f&&y.Si(c,r)):y.Ei(c,r),++c):e.b&&!o&&(y.Ei(c,h),++c);else O==null?b.Wb(null):(r=Pa(e,O),r==null?e.b&&!Oc(n)&&b.Wb(O):b.Wb(r))}function uLn(e,n){var t,i,r,c,o,l,f,h;for(t=new b6,r=new qn(Vn(rr(n).a.Jc(),new ee));ht(r);)if(i=u(tt(r),17),!cc(i)&&(l=i.c.i,b0e(l,EJ))){if(h=Pbe(e,l,EJ,jJ),h==-1)continue;t.b=k.Math.max(t.b,h),!t.a&&(t.a=new Ce),Te(t.a,l)}for(o=new qn(Vn(Ni(n).a.Jc(),new ee));ht(o);)if(c=u(tt(o),17),!cc(c)&&(f=c.d.i,b0e(f,jJ))){if(h=Pbe(e,f,jJ,EJ),h==-1)continue;t.d=k.Math.max(t.d,h),!t.c&&(t.c=new Ce),Te(t.c,f)}return t}function oLn(e,n,t,i){var r,c,o,l,f,h,b;if(t.d.i!=n.i){for(r=new Ba(e),Tf(r,(zn(),dr)),he(r,(me(),wi),t),he(r,(Ne(),Wi),(Br(),to)),Hn(i.c,r),o=new Qu,gu(o,r),xr(o,(De(),Kn)),l=new Qu,gu(l,r),xr(l,et),b=t.d,Gr(t,o),c=new Mw,$u(c,t),he(c,Wc,null),lc(c,l),Gr(c,b),h=new qr(t.b,0);h.b1e6)throw $(new FP("power of ten too big"));if(e<=oi)return _4(XO(my[1],n),n);for(i=XO(my[1],oi),r=i,t=Pu(e-oi),n=sc(e%oi);ao(t,oi)>0;)r=H3(r,i),t=lf(t,oi);for(r=H3(r,XO(my[1],n)),r=_4(r,oi),t=Pu(e-oi);ao(t,oi)>0;)r=_4(r,oi),t=lf(t,oi);return r=_4(r,n),r}function DKe(e){var n,t,i,r,c,o,l,f,h,b;for(f=new L(e.a);f.ah&&i>h)b=l,h=ne(n.p[l.p])+ne(n.d[l.p])+l.o.b+l.d.a;else{r=!1,t.$g()&&t.ah("bk node placement breaks on "+l+" which should have been after "+b);break}if(!r)break}return t.$g()&&t.ah(n+" is feasible: "+r),r}function age(e,n,t,i){var r,c,o,l,f,h,b,p,y;if(c=new Ba(e),Tf(c,(zn(),wo)),he(c,(Ne(),Wi),(Br(),to)),r=0,n){for(o=new Qu,he(o,(me(),wi),n),he(c,wi,n.i),xr(o,(De(),Kn)),gu(o,c),y=dh(n.e),h=y,b=0,p=h.length;b0){if(r<0&&b.a&&(r=f,c=h[0],i=0),r>=0){if(l=b.b,f==r&&(l-=i++,l==0))return 0;if(!_Ve(n,h,b,l,o)){f=r-1,h[0]=c;continue}}else if(r=-1,!_Ve(n,h,b,0,o))return 0}else{if(r=-1,ic(b.c,0)==32){if(p=h[0],ARe(n,h),h[0]>p)continue}else if(U5n(n,b.c,h[0])){h[0]+=b.c.length;continue}return 0}return eRn(o,t)?h[0]:0}function hLn(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(b=new wR(new eje(t)),l=oe(ts,pa,30,e.f.e.c.length,16,1),$fe(l,l.length),t[n.a]=0,h=new L(e.f.e);h.a=l.a?c.b>=l.b?(i.a=l.a+(c.a-l.a)/2+r,i.b=l.b+(c.b-l.b)/2-r-e.e.b):(i.a=l.a+(c.a-l.a)/2+r,i.b=c.b+(l.b-c.b)/2+r):c.b>=l.b?(i.a=c.a+(l.a-c.a)/2+r,i.b=l.b+(c.b-l.b)/2+r):(i.a=c.a+(l.a-c.a)/2+r,i.b=c.b+(l.b-c.b)/2-r-e.e.b))}function lS(e){var n,t,i,r,c,o,l,f;if(!e.f){if(f=new nT,l=new nT,n=sA,o=n.a.yc(e,n),o==null){for(c=new ut(tu(e));c.e!=c.i.gc();)r=u(ft(c),29),er(f,lS(r));n.a.Ac(e)!=null,n.a.gc()==0}for(i=(!e.s&&(e.s=new pe(ns,e,21,17)),new ut(e.s));i.e!=i.i.gc();)t=u(ft(i),179),X(t,103)&&Et(l,u(t,19));_2(l),e.r=new uDe(e,(u(K(we((x0(),Rn).o),6),19),l.i),l.g),er(f,e.r),_2(f),e.f=new N3((u(K(we(Rn.o),5),19),f.i),f.g),Ms(e).b&=-3}return e.f}function Gz(){Gz=Y,R8e=z(B(Wl,1),kh,30,15,[48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70]),Man=new RegExp(`[ +\r\f]+`);try{cA=z(B(QBn,1),On,2076,0,[new UT(($se(),QB("yyyy-MM-dd'T'HH:mm:ss'.'SSSZ",MC((RP(),RP(),US))))),new UT(QB("yyyy-MM-dd'T'HH:mm:ss'.'SSS",MC(US))),new UT(QB("yyyy-MM-dd'T'HH:mm:ss",MC(US))),new UT(QB("yyyy-MM-dd'T'HH:mm",MC(US))),new UT(QB("yyyy-MM-dd",MC(US)))])}catch(e){if(e=sr(e),!X(e,80))throw $(e)}}function dLn(e){var n,t,i,r,c,o,l;for(t=null,l=null,i=u(T(e.b,(Ne(),Die)),348),i==(DE(),pD)&&(t=new Ce,l=new Ce),o=new L(e.d);o.at);return c}function _Ke(e,n){var t,i,r,c;if(r=Is(e.d,1)!=0,i=xz(e,n),i==0&&Re($e(T(n.j,(me(),rb)))))return 0;!Re($e(T(n.j,(me(),rb))))&&!Re($e(T(n.j,_v)))||ue(T(n.j,(Ne(),u1)))===ue((F1(),ob))?n.c.kg(n.e,r):r=Re($e(T(n.j,rb))),WO(e,n,r,!0),Re($e(T(n.j,_v)))&&he(n.j,_v,(Pn(),!1)),Re($e(T(n.j,rb)))&&(he(n.j,rb,(Pn(),!1)),he(n.j,_v,!0)),t=xz(e,n);do{if(Qhe(e),t==0)return 0;r=!r,c=t,WO(e,n,r,!1),t=xz(e,n)}while(c>t);return c}function gLn(e,n,t){var i,r,c,o,l;if(i=u(T(e,(Ne(),Cie)),22),t.a>n.a&&(i.Gc((rg(),Gx))?e.c.a+=(t.a-n.a)/2:i.Gc(qx)&&(e.c.a+=t.a-n.a)),t.b>n.b&&(i.Gc((rg(),Xx))?e.c.b+=(t.b-n.b)/2:i.Gc(Ux)&&(e.c.b+=t.b-n.b)),u(T(e,(me(),po)),22).Gc((Dc(),Kl))&&(t.a>n.a||t.b>n.b))for(l=new L(e.a);l.an.a&&(i.Gc((rg(),Gx))?e.c.a+=(t.a-n.a)/2:i.Gc(qx)&&(e.c.a+=t.a-n.a)),t.b>n.b&&(i.Gc((rg(),Xx))?e.c.b+=(t.b-n.b)/2:i.Gc(Ux)&&(e.c.b+=t.b-n.b)),u(T(e,(me(),po)),22).Gc((Dc(),Kl))&&(t.a>n.a||t.b>n.b))for(o=new L(e.a);o.a=0&&p<=1&&y>=0&&y<=1?gi(new je(e.a,e.b),A1(new je(n.a,n.b),p)):null}function fS(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(c=0,o=e.t,r=0,i=0,f=0,y=0,p=0,t&&(e.n.c.length=0,Te(e.n,new PR(e.s,e.t,e.i))),l=0,b=new L(e.b);b.a0?e.i:0)>n&&f>0&&(c=0,o+=f+e.i,r=k.Math.max(r,y),i+=f+e.i,f=0,y=0,t&&(++p,Te(e.n,new PR(e.s,o,e.i))),l=0),y+=h.g+(l>0?e.i:0),f=k.Math.max(f,h.f),t&&Ide(u(Le(e.n,p),208),h),c+=h.g+(l>0?e.i:0),++l;return r=k.Math.max(r,y),i+=f,t&&(e.r=r,e.d=i,Pde(e.j)),new Lf(e.s,e.t,r,i)}function qz(e){var n,t,i;return t=ue(ye(e,(Ne(),_y)))===ue((KO(),eie))||ue(ye(e,_y))===ue(Vte)||ue(ye(e,_y))===ue(Yte)||ue(ye(e,_y))===ue(Wte)||ue(ye(e,_y))===ue(nie)||ue(ye(e,_y))===ue(nD),i=ue(ye(e,bH))===ue((YO(),qie))||ue(ye(e,bH))===ue(Xie)||ue(ye(e,aD))===ue((G0(),D7))||ue(ye(e,aD))===ue((G0(),Sx)),n=ue(ye(e,u1))!==ue((F1(),ob))||Re($e(ye(e,A7)))||ue(ye(e,dx))!==ue((X4(),ZS))||ne(re(ye(e,lD)))!=0||ne(re(ye(e,Aie)))!=0,t||i||n}function fv(e){var n,t,i,r,c,o,l,f;if(!e.a){if(e.o=null,f=new RSe(e),n=new Mf,t=sA,l=t.a.yc(e,t),l==null){for(o=new ut(tu(e));o.e!=o.i.gc();)c=u(ft(o),29),er(f,fv(c));t.a.Ac(e)!=null,t.a.gc()==0}for(r=(!e.s&&(e.s=new pe(ns,e,21,17)),new ut(e.s));r.e!=r.i.gc();)i=u(ft(r),179),X(i,335)&&Et(n,u(i,38));_2(n),e.k=new cDe(e,(u(K(we((x0(),Rn).o),7),19),n.i),n.g),er(f,e.k),_2(f),e.a=new N3((u(K(we(Rn.o),4),19),f.i),f.g),Ms(e).b&=-2}return e.a}function mLn(e){var n,t,i,r,c,o,l,f,h,b,p,y;if(l=e.d,p=u(T(e,(me(),Dy)),16),n=u(T(e,xy),16),!(!p&&!n)){if(c=ne(re($2(e,(Ne(),Bie)))),o=ne(re($2(e,w4e))),y=0,p){for(h=0,r=p.Jc();r.Ob();)i=u(r.Pb(),9),h=k.Math.max(h,i.o.b),y+=i.o.a;y+=c*(p.gc()-1),l.d+=h+o}if(t=0,n){for(h=0,r=n.Jc();r.Ob();)i=u(r.Pb(),9),h=k.Math.max(h,i.o.b),t+=i.o.a;t+=c*(n.gc()-1),l.a+=h+o}f=k.Math.max(y,t),f>e.o.a&&(b=(f-e.o.a)/2,l.b=k.Math.max(l.b,b),l.c=k.Math.max(l.c,b))}}function bge(e,n,t,i){var r,c,o,l,f,h,b;if(b=Po(e.e.Ah(),n),r=0,c=u(e.g,122),f=null,Cc(),u(n,69).vk()){for(l=0;ll?1:-1:M1e(e.a,n.a,c),r==-1)p=-f,b=o==f?aY(n.a,l,e.a,c):dY(n.a,l,e.a,c);else if(p=o,o==f){if(r==0)return mh(),KS;b=aY(e.a,c,n.a,l)}else b=dY(e.a,c,n.a,l);return h=new zb(p,b.length,b),bE(h),h}function kLn(e,n){var t,i,r,c;if(c=yKe(n),!n.c&&(n.c=new pe($s,n,9,9)),Zi(new wn(null,(!n.c&&(n.c=new pe($s,n,9,9)),new pn(n.c,16))),new rje(c)),r=u(T(c,(me(),po)),22),p$n(n,r),r.Gc((Dc(),Kl)))for(i=new ut((!n.c&&(n.c=new pe($s,n,9,9)),n.c));i.e!=i.i.gc();)t=u(ft(i),125),J$n(e,n,c,t);return u(ye(n,(Ne(),jg)),182).gc()!=0&&oXe(n,c),Re($e(T(c,a4e)))&&r.Ec(ZJ),bi(c,hD)&&Qxe(new tde(ne(re(T(c,hD)))),c),ue(ye(n,wm))===ue((B1(),Yd))?fBn(e,n,c):K$n(e,n,c),c}function bo(e,n){var t,i,r,c,o,l,f;if(e==null)return null;if(c=e.length,c==0)return"";for(f=oe(Wl,kh,30,c,15,1),Yr(0,c,e.length),Yr(0,c,f.length),rIe(e,0,c,f,0),t=null,l=n,r=0,o=0;r0?of(t.a,0,c-1):""):(Yr(0,c-1,e.length),e.substr(0,c-1)):t?t.a:e}function jLn(e,n,t){var i,r,c;if(bi(n,(Ne(),yu))&&(ue(T(n,yu))===ue((Xs(),V1))||ue(T(n,yu))===ue(yg))||bi(t,yu)&&(ue(T(t,yu))===ue((Xs(),V1))||ue(T(t,yu))===ue(yg)))return 0;if(i=_r(n),r=fIn(e,n,t),r!=0)return r;if(bi(n,(me(),Ci))&&bi(t,Ci)){if(c=oo(qw(n,t,i,u(T(i,cb),15).a),qw(t,n,i,u(T(i,cb),15).a)),ue(T(i,bx))===ue((_0(),iD))&&ue(T(n,gx))!==ue(T(t,gx))&&(c=0),c<0)return ZO(e,n,t),c;if(c>0)return ZO(e,t,n),c}return $Cn(e,n,t)}function LKe(e,n){var t,i,r,c,o,l,f,h,b,p,y;for(i=new qn(Vn(H0(n).a.Jc(),new ee));ht(i);)t=u(tt(i),85),X(K((!t.b&&(t.b=new Nn(vt,t,4,7)),t.b),0),193)||(f=iu(u(K((!t.c&&(t.c=new Nn(vt,t,5,8)),t.c),0),84)),ZE(t)||(o=n.i+n.g/2,l=n.j+n.f/2,b=f.i+f.g/2,p=f.j+f.f/2,y=new Kr,y.a=b-o,y.b=p-l,c=new je(y.a,y.b),p8(c,n.g,n.f),y.a-=c.a,y.b-=c.b,o=b-y.a,l=p-y.b,h=new je(y.a,y.b),p8(h,f.g,f.f),y.a-=h.a,y.b-=h.b,b=o+y.a,p=l+y.b,r=Pz(t),V3(r,o),Y3(r,l),X3(r,b),K3(r,p),LKe(e,f)))}function V2(e,n){var t,i,r,c,o;if(o=u(n,137),ov(e),ov(o),o.b!=null){if(e.c=!0,e.b==null){e.b=oe($t,ni,30,o.b.length,15,1),Wu(o.b,0,e.b,0,o.b.length);return}for(c=oe($t,ni,30,e.b.length+o.b.length,15,1),t=0,i=0,r=0;t=e.b.length?(c[r++]=o.b[i++],c[r++]=o.b[i++]):i>=o.b.length?(c[r++]=e.b[t++],c[r++]=e.b[t++]):o.b[i]0?e.i:0)),++n;for(K1e(e.n,f),e.d=t,e.r=i,e.g=0,e.f=0,e.e=0,e.o=Ki,e.p=Ki,c=new L(e.b);c.a0&&(r=(!e.n&&(e.n=new pe(ju,e,1,7)),u(K(e.n,0),157)).a,!r||Kt(Kt((n.a+=' "',n),r),'"'))),t=(!e.b&&(e.b=new Nn(vt,e,4,7)),!(e.b.i<=1&&(!e.c&&(e.c=new Nn(vt,e,5,8)),e.c.i<=1))),t?n.a+=" [":n.a+=" ",Kt(n,nle(new NX,new ut(e.b))),t&&(n.a+="]"),n.a+=eee,t&&(n.a+="["),Kt(n,nle(new NX,new ut(e.c))),t&&(n.a+="]"),n.a)}function SLn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn;for(be=e.c,fe=n.c,t=wu(be.a,e,0),i=wu(fe.a,n,0),V=u(Rw(e,(Nc(),ys)).Jc().Pb(),12),tn=u(Rw(e,Do).Jc().Pb(),12),te=u(Rw(n,ys).Jc().Pb(),12),Tn=u(Rw(n,Do).Jc().Pb(),12),R=dh(V.e),Ie=dh(tn.g),q=dh(te.e),cn=dh(Tn.g),z0(e,i,fe),o=q,b=0,A=o.length;b0&&f[i]&&(A=L3(e.b,f[i],r)),O=k.Math.max(O,r.c.c.b+A);for(c=new L(b.e);c.ab?new Gb((ha(),Am),t,n,h-b):h>0&&b>0&&(new Gb((ha(),Am),n,t,0),new Gb(Am,t,n,0))),o)}function TLn(e,n,t){var i,r,c;for(e.a=new Ce,c=St(n.b,0);c.b!=c.d.c;){for(r=u(jt(c),40);u(T(r,(Mu(),Nh)),15).a>e.a.c.length-1;)Te(e.a,new jc(mv,Fpe));i=u(T(r,Nh),15).a,t==(vr(),Zc)||t==ru?(r.e.ane(re(u(Le(e.a,i),49).b))&&FT(u(Le(e.a,i),49),r.e.a+r.f.a)):(r.e.bne(re(u(Le(e.a,i),49).b))&&FT(u(Le(e.a,i),49),r.e.b+r.f.b))}}function RKe(e,n,t,i){var r,c,o,l,f,h,b;if(c=GB(i),l=Re($e(T(i,(Ne(),c4e)))),(l||Re($e(T(e,dH))))&&!D3(u(T(e,Wi),102)))r=q4(c),f=ege(e,t,t==(Nc(),Do)?r:CO(r));else switch(f=new Qu,gu(f,e),n?(b=f.n,b.a=n.a-e.n.a,b.b=n.b-e.n.b,BGe(b,0,0,e.o.a,e.o.b),xr(f,cKe(f,c))):(r=q4(c),xr(f,t==(Nc(),Do)?r:CO(r))),o=u(T(i,(me(),po)),22),h=f.j,c.g){case 2:case 1:(h==(De(),Xn)||h==bt)&&o.Ec((Dc(),Nv));break;case 4:case 3:(h==(De(),et)||h==Kn)&&o.Ec((Dc(),Nv))}return f}function BKe(e,n){var t,i,r,c,o,l;for(o=new D2(new on(e.f.b).a);o.b;){if(c=Q3(o),r=u(c.jd(),591),n==1){if(r.yf()!=(vr(),Vl)&&r.yf()!=Za)continue}else if(r.yf()!=(vr(),Zc)&&r.yf()!=ru)continue;switch(i=u(u(c.kd(),49).b,82),l=u(u(c.kd(),49).a,194),t=l.c,r.yf().g){case 2:i.g.c=e.e.a,i.g.b=k.Math.max(1,i.g.b+t);break;case 1:i.g.c=i.g.c+t,i.g.b=k.Math.max(1,i.g.b-t);break;case 4:i.g.d=e.e.b,i.g.a=k.Math.max(1,i.g.a+t);break;case 3:i.g.d=i.g.d+t,i.g.a=k.Math.max(1,i.g.a-t)}}}function CLn(e,n){var t,i,r,c,o,l,f,h,b,p;for(n.Tg("Simple node placement",1),p=u(T(e,(me(),Lv)),316),l=0,c=new L(e.b);c.a1)throw $(new Gn(JN));f||(c=Xh(n,i.Jc().Pb()),o.Ec(c))}return h1e(e,I0e(e,n,t),o)}function Xz(e,n,t){var i,r,c,o,l,f,h,b;if(J1(e.e,n))f=(Cc(),u(n,69).vk()?new rR(n,e):new pC(n,e)),Mz(f.c,f.b),Vj(f,u(t,18));else{for(b=Po(e.e.Ah(),n),i=u(e.g,122),o=0;o"}f!=null&&(n.a+=""+f)}else e.e?(l=e.e.zb,l!=null&&(n.a+=""+l)):(n.a+="?",e.b?(n.a+=" super ",YW(e.b,n)):e.f&&(n.a+=" extends ",YW(e.f,n)))}function PLn(e){e.b=null,e.a=null,e.o=null,e.q=null,e.v=null,e.w=null,e.B=null,e.p=null,e.Q=null,e.R=null,e.S=null,e.T=null,e.U=null,e.V=null,e.W=null,e.bb=null,e.eb=null,e.ab=null,e.H=null,e.db=null,e.c=null,e.d=null,e.f=null,e.n=null,e.r=null,e.s=null,e.u=null,e.G=null,e.J=null,e.e=null,e.j=null,e.i=null,e.g=null,e.k=null,e.t=null,e.F=null,e.I=null,e.L=null,e.M=null,e.O=null,e.P=null,e.$=null,e.N=null,e.Z=null,e.cb=null,e.K=null,e.D=null,e.A=null,e.C=null,e._=null,e.fb=null,e.X=null,e.Y=null,e.gb=!1,e.hb=!1}function $Ln(e){var n,t,i,r;if(i=lZ((!e.c&&(e.c=qC(Pu(e.f))),e.c),0),e.e==0||e.a==0&&e.f!=-1&&e.e<0)return i;if(n=Whe(e)<0?1:0,t=e.e,r=(i.length+1+k.Math.abs(sc(e.e)),new o4),n==1&&(r.a+="-"),e.e>0)if(t-=i.length-n,t>=0){for(r.a+="0.";t>pg.length;t-=pg.length)ADe(r,pg);WOe(r,pg,sc(t)),Kt(r,(Yn(n,i.length+1),i.substr(n)))}else t=n-t,Kt(r,of(i,n,sc(t))),r.a+=".",Kt(r,qfe(i,sc(t)));else{for(Kt(r,(Yn(n,i.length+1),i.substr(n)));t<-pg.length;t+=pg.length)ADe(r,pg);WOe(r,pg,sc(-t))}return r.a}function QW(e){var n,t,i,r,c,o,l,f,h;return!(e.k!=(zn(),Qi)||e.j.c.length<=1||(c=u(T(e,(Ne(),Wi)),102),c==(Br(),to))||(r=(B2(),(e.q?e.q:(jn(),jn(),i1))._b(gp)?i=u(T(e,gp),203):i=u(T(_r(e),vx),203),i),r==xH)||!(r==Fv||r==zv)&&(o=ne(re($2(e,yx))),n=u(T(e,bD),140),!n&&(n=new $le(o,o,o,o)),h=mu(e,(De(),Kn)),f=n.d+n.a+(h.gc()-1)*o,f>e.o.b||(t=mu(e,et),l=n.d+n.a+(t.gc()-1)*o,l>e.o.b)))}function RLn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;n.Tg("Orthogonal edge routing",1),h=ne(re(T(e,(Ne(),Sm)))),t=ne(re(T(e,jm))),i=ne(re(T(e,ub))),y=new jV(0,t),I=0,o=new qr(e.b,0),l=null,b=null,f=null,p=null;do b=o.b0?(S=(A-1)*t,l&&(S+=i),b&&(S+=i),S0;for(l=u(T(e.c.i,mm),15).a,c=u(gs(si(n.Mc(),new xje(l)),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),o=new Si,b=new ar,Vt(o,e.c.i),hr(b,e.c.i);o.b!=0;){if(t=u(o.b==0?null:(at(o.b!=0),$l(o,o.a.a)),9),c.Gc(t))return!0;for(r=new qn(Vn(Ni(t).a.Jc(),new ee));ht(r);)i=u(tt(r),17),f=i.d.i,b.a._b(f)||(b.a.yc(f,b),Xi(o,f,o.c.b,o.c))}return!1}function GKe(e,n,t){var i,r,c,o,l,f,h,b,p;for(p=new Ce,b=new Tae(0,t),c=0,vB(b,new tQ(0,0,b,t)),r=0,h=new ut(e);h.e!=h.i.gc();)f=u(ft(h),26),i=u(Le(b.a,b.a.c.length-1),173),l=r+f.g+(u(Le(b.a,0),173).b.c.length==0?0:t),(l>n||Re($e(ye(f,(Ja(),AD)))))&&(r=0,c+=b.b+t,Hn(p.c,b),b=new Tae(c,t),i=new tQ(0,b.f,b,t),vB(b,i),r=0),i.b.c.length==0||!Re($e(ye(Bi(f),(Ja(),Ure))))&&(f.f>=i.o&&f.f<=i.f||i.a*.5<=f.f&&i.a*1.5>=f.f)?ede(i,f):(o=new tQ(i.s+i.r+t,b.f,b,t),vB(b,o),ede(o,f)),r=f.i+f.g;return Hn(p.c,b),p}function aS(e){var n,t,i,r;if(!(e.b==null||e.b.length<=2)&&!e.a){for(n=0,r=0;r=e.b[r+1])r+=2;else if(t0)for(i=new bs(u(pi(e.a,c),22)),jn(),Cr(i,new noe(n)),r=new qr(c.b,0);r.b0&&i>=-6?i>=0?yC(c,t-sc(e.e),"."):(qY(c,n-1,n-1,"0."),yC(c,n+1,gh(pg,0,-sc(i)-1))):(t-n>=1&&(yC(c,n,"."),++t),yC(c,t,"E"),i>0&&yC(c,++t,"+"),yC(c,++t,""+rE(Pu(i)))),e.g=c.a,e.g))}function KLn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie;i=ne(re(T(n,(Ne(),s4e)))),be=u(T(n,kx),15).a,y=4,r=3,fe=20/be,S=!1,f=0,o=oi;do{for(c=f!=1,p=f!=0,Ie=0,I=e.a,q=0,te=I.length;qbe)?(f=2,o=oi):f==0?(f=1,o=Ie):(f=0,o=Ie)):(S=Ie>=o||o-Ie=Ec?Bc(t,V1e(i)):D9(t,i&yr),o=new FV(10,null,0),Cvn(e.a,o,l-1)):(t=(o.Km().length+c,new jj),Bc(t,o.Km())),n.e==0?(i=n.Im(),i>=Ec?Bc(t,V1e(i)):D9(t,i&yr)):Bc(t,n.Km()),u(o,517).b=t.a}}function VLn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I;if(!t.dc()){for(l=0,y=0,i=t.Jc(),A=u(i.Pb(),15).a;l0?1:Lb(isNaN(i),isNaN(0)))>=0^(Bf(xh),(k.Math.abs(l)<=xh||l==0||isNaN(l)&&isNaN(0)?0:l<0?-1:l>0?1:Lb(isNaN(l),isNaN(0)))>=0)?k.Math.max(l,i):(Bf(xh),(k.Math.abs(i)<=xh||i==0||isNaN(i)&&isNaN(0)?0:i<0?-1:i>0?1:Lb(isNaN(i),isNaN(0)))>0?k.Math.sqrt(l*l+i*i):-k.Math.sqrt(l*l+i*i))}function ZLn(e){var n,t,i,r;r=e.o,h2(),e.A.dc()||di(e.A,Qme)?n=r.b:(e.D?n=k.Math.max(r.b,YE(e.f)):n=YE(e.f),e.A.Gc((Vs(),GD))&&!e.B.Gc((_s(),iA))&&(n=k.Math.max(n,YE(u(zc(e.p,(De(),et)),253))),n=k.Math.max(n,YE(u(zc(e.p,Kn),253)))),t=nze(e),t&&(n=k.Math.max(n,t.b)),e.A.Gc(qD)&&(e.q==(Br(),f1)||e.q==to)&&(n=k.Math.max(n,tR(u(zc(e.b,(De(),et)),127))),n=k.Math.max(n,tR(u(zc(e.b,Kn),127))))),Re($e(e.e.Rf().mf((Xt(),Om))))?r.b=k.Math.max(r.b,n):r.b=n,i=e.f.i,i.d=0,i.a=n,GW(e.f)}function ePn(e,n,t,i,r,c,o,l){var f,h,b,p;switch(f=$f(z(B(qBn,1),On,238,0,[n,t,i,r])),p=null,e.b.g){case 1:p=$f(z(B(M6e,1),On,523,0,[new Lk,new IM,new _6]));break;case 0:p=$f(z(B(M6e,1),On,523,0,[new _6,new IM,new Lk]));break;case 2:p=$f(z(B(M6e,1),On,523,0,[new IM,new Lk,new _6]))}for(b=new L(p);b.a1&&(f=h.Gg(f,e.a,l));return f.c.length==1?u(Le(f,f.c.length-1),238):f.c.length==2?zLn((vn(0,f.c.length),u(f.c[0],238)),(vn(1,f.c.length),u(f.c[1],238)),o,c):null}function nPn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A;r=new i9(e),c=new Yqe,i=(VC(c.n),VC(c.p),Hu(c.c),VC(c.f),VC(c.o),Hu(c.q),Hu(c.d),Hu(c.g),Hu(c.k),Hu(c.e),Hu(c.i),Hu(c.j),Hu(c.r),Hu(c.b),y=yqe(c,r,null),kUe(c,r),y),n&&(f=new i9(n),o=vLn(f),M0e(i,z(B(u9e,1),On,524,0,[o]))),p=!1,b=!1,t&&(f=new i9(t),GF in f.a&&(p=O1(f,GF).oe().a),aZe in f.a&&(b=O1(f,aZe).oe().a)),h=wAe(aBe(new i4,p),b),sTn(new RM,i,h),GF in r.a&&Rf(r,GF,null),(p||b)&&(l=new r4,bKe(h,l,p,b),Rf(r,GF,l)),S=new ySe(c),zze(new AK(i),S),A=new kSe(c),zze(new AK(i),A)}function tPn(e,n,t){var i,r,c,o,l,f,h;for(t.Tg("Find roots",1),e.a.c.length=0,r=St(n.b,0);r.b!=r.d.c;)i=u(jt(r),40),i.b.b==0&&(he(i,(Ti(),fb),(Pn(),!0)),Te(e.a,i));switch(e.a.c.length){case 0:c=new nQ(0,n,"DUMMY_ROOT"),he(c,(Ti(),fb),(Pn(),!0)),he(c,bre,!0),Vt(n.b,c);break;case 1:break;default:for(o=new nQ(0,n,DF),f=new L(e.a);f.a=k.Math.abs(i.b)?(i.b=0,c.d+c.a>o.d&&c.do.c&&c.c0){if(n=new Nse(e.i,e.g),t=e.i,c=t<100?null:new m0(t),e.Rj())for(i=0;i0){for(l=e.g,h=e.i,vE(e),c=h<100?null:new m0(h),i=0;i>13|(e.m&15)<<9,r=e.m>>4&8191,c=e.m>>17|(e.h&255)<<5,o=(e.h&1048320)>>8,l=n.l&8191,f=n.l>>13|(n.m&15)<<9,h=n.m>>4&8191,b=n.m>>17|(n.h&255)<<5,p=(n.h&1048320)>>8,cn=t*l,tn=i*l,Tn=r*l,Dn=c*l,ot=o*l,f!=0&&(tn+=t*f,Tn+=i*f,Dn+=r*f,ot+=c*f),h!=0&&(Tn+=t*h,Dn+=i*h,ot+=r*h),b!=0&&(Dn+=t*b,ot+=i*b),p!=0&&(ot+=t*p),S=cn&Ls,A=(tn&511)<<13,y=S+A,I=cn>>22,R=tn>>9,q=(Tn&262143)<<4,V=(Dn&31)<<17,O=I+R+q+V,be=Tn>>18,fe=Dn>>5,Ie=(ot&4095)<<8,te=be+fe+Ie,O+=y>>22,y&=Ls,te+=O>>22,O&=Ls,te&=G1,_o(y,O,te)}function KKe(e){var n,t,i,r,c,o,l;if(l=u(Le(e.j,0),12),l.g.c.length!=0&&l.e.c.length!=0)throw $(new Uc("Interactive layout does not support NORTH/SOUTH ports with incoming _and_ outgoing edges."));if(l.g.c.length!=0){for(c=Ki,t=new L(l.g);t.a0&&UGe(e,l,p);for(r=new L(p);r.a4)if(e.dk(n)){if(e.$k()){if(r=u(n,52),i=r.Bh(),f=i==e.e&&(e.kl()?r.vh(r.Ch(),e.gl())==e.hl():-1-r.Ch()==e.Jj()),e.ll()&&!f&&!i&&r.Gh()){for(c=0;ce.d[o.p]&&(t+=Hae(e.b,c)*u(f.b,15).a,C0(e.a,ve(c)));for(;!kj(e.a);)Ehe(e.b,u(A4(e.a),15).a)}return t}function oPn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;for(n.Tg(FQe,1),S=new Ce,b=k.Math.max(e.a.c.length,u(T(e,(me(),cb)),15).a),t=b*u(T(e,cD),15).a,l=ue(T(e,(Ne(),Iy)))===ue((_0(),dm)),O=new L(e.a);O.a0&&(h=e.n.a/c);break;case 2:case 4:r=e.i.o.b,r>0&&(h=e.n.b/r)}he(e,(me(),hp),h)}if(f=e.o,o=e.a,i)o.a=i.a,o.b=i.b,e.d=!0;else if(n!=nh&&n!=bb&&l!=ku)switch(l.g){case 1:o.a=f.a/2;break;case 2:o.a=f.a,o.b=f.b/2;break;case 3:o.a=f.a/2,o.b=f.b;break;case 4:o.b=f.b/2}else o.a=f.a/2,o.b=f.b/2}function hS(e){var n,t,i,r,c,o,l,f,h,b;if(e.Nj())if(b=e.Cj(),f=e.Oj(),b>0)if(n=new t1e(e.nj()),t=b,c=t<100?null:new m0(t),xC(e,t,n.g),r=t==1?e.Gj(4,K(n,0),null,0,f):e.Gj(6,n,null,-1,f),e.Kj()){for(i=new ut(n);i.e!=i.i.gc();)c=e.Mj(ft(i),c);c?(c.lj(r),c.mj()):e.Hj(r)}else c?(c.lj(r),c.mj()):e.Hj(r);else xC(e,e.Cj(),e.Dj()),e.Hj(e.Gj(6,(jn(),Sc),null,-1,f));else if(e.Kj())if(b=e.Cj(),b>0){for(l=e.Dj(),h=b,xC(e,b,l),c=h<100?null:new m0(h),i=0;i1&&us(o)*Gs(o)/2>l[0]){for(c=0;cl[c];)++c;A=new T0(O,0,c+1),p=new dB(A),b=us(o)/Gs(o),f=uZ(p,n,new t4,t,i,r,b),gi(la(p.e),f),E4(v8(y,p),B8),S=new T0(O,c+1,O.c.length),zde(y,S),O.c.length=0,h=0,ODe(l,l.length,0)}else I=y.b.c.length==0?null:Le(y.b,0),I!=null&&PY(y,0),h>0&&(l[h]=l[h-1]),l[h]+=us(o)*Gs(o),++h,Hn(O.c,o);return O}function wPn(e,n){var t,i,r,c;t=n.b,c=new bs(t.j),r=0,i=t.j,i.c.length=0,jw(u(Yb(e.b,(De(),Xn),(_w(),lp)),16),t),r=_O(c,r,new k6,i),jw(u(Yb(e.b,Xn,ib),16),t),r=_O(c,r,new ld,i),jw(u(Yb(e.b,Xn,sp),16),t),jw(u(Yb(e.b,et,lp),16),t),jw(u(Yb(e.b,et,ib),16),t),r=_O(c,r,new fd,i),jw(u(Yb(e.b,et,sp),16),t),jw(u(Yb(e.b,bt,lp),16),t),r=_O(c,r,new Np,i),jw(u(Yb(e.b,bt,ib),16),t),r=_O(c,r,new Sb,i),jw(u(Yb(e.b,bt,sp),16),t),jw(u(Yb(e.b,Kn,lp),16),t),r=_O(c,r,new sd,i),jw(u(Yb(e.b,Kn,ib),16),t),jw(u(Yb(e.b,Kn,sp),16),t)}function pPn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O;for(n.Tg("Layer size calculation",1),b=Ki,h=Dr,r=!1,l=new L(e.b);l.a.5?R-=o*2*(A-.5):A<.5&&(R+=c*2*(.5-A)),r=l.d.b,RI.a-O-b&&(R=I.a-O-b),l.n.a=n+R}}function vPn(e){var n,t,i,r,c;if(i=u(T(e,(Ne(),yu)),165),i==(Xs(),V1)){for(t=new qn(Vn(rr(e).a.Jc(),new ee));ht(t);)if(n=u(tt(t),17),!qPe(n))throw $(new wd(iee+LO(e)+"' has its layer constraint set to FIRST_SEPARATE, but has at least one incoming edge. FIRST_SEPARATE nodes must not have incoming edges."))}else if(i==yg){for(c=new qn(Vn(Ni(e).a.Jc(),new ee));ht(c);)if(r=u(tt(c),17),!qPe(r))throw $(new wd(iee+LO(e)+"' has its layer constraint set to LAST_SEPARATE, but has at least one outgoing edge. LAST_SEPARATE nodes must not have outgoing edges."))}}function rN(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;if(e.e&&e.c.c>19!=0&&(n=e8(n),f=!f),o=rNn(n),c=!1,r=!1,i=!1,e.h==gN&&e.m==0&&e.l==0)if(r=!0,c=!0,o==-1)e=gCe((G9(),jme)),i=!0,f=!f;else return l=lbe(e,o),f&&ZY(l),t&&(Z0=_o(0,0,0)),l;else e.h>>19!=0&&(c=!0,e=e8(e),i=!0,f=!f);return o!=-1?bkn(e,o,f,c,t):Kde(e,n)<0?(t&&(c?Z0=e8(e):Z0=_o(e.l,e.m,e.h)),_o(0,0,0)):WIn(i?e:_o(e.l,e.m,e.h),n,f,c,r,t)}function nZ(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;if(o=e.e,f=n.e,o==0)return n;if(f==0)return e;if(c=e.d,l=n.d,c+l==2)return t=Rr(e.a[0],Ic),i=Rr(n.a[0],Ic),o==f?(b=mc(t,i),A=Rt(b),S=Rt(Bb(b,32)),S==0?new D1(o,A):new zb(o,2,z(B($t,1),ni,30,15,[A,S]))):(mh(),C$(o<0?lf(i,t):lf(t,i),0)?B0(o<0?lf(i,t):lf(t,i)):sE(B0(Td(o<0?lf(i,t):lf(t,i)))));if(o==f)y=o,p=c>=l?dY(e.a,c,n.a,l):dY(n.a,l,e.a,c);else{if(r=c!=l?c>l?1:-1:M1e(e.a,n.a,c),r==0)return mh(),KS;r==1?(y=o,p=aY(e.a,c,n.a,l)):(y=f,p=aY(n.a,l,e.a,c))}return h=new zb(y,p.length,p),bE(h),h}function kPn(e,n){var t,i,r,c,o,l,f;if(!(e.g>n.f||n.g>e.f)){for(t=0,i=0,o=e.w.a.ec().Jc();o.Ob();)r=u(o.Pb(),12),dQ(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])).b,n.g,n.f)&&++t;for(l=e.r.a.ec().Jc();l.Ob();)r=u(l.Pb(),12),dQ(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])).b,n.g,n.f)&&--t;for(f=n.w.a.ec().Jc();f.Ob();)r=u(f.Pb(),12),dQ(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])).b,e.g,e.f)&&++i;for(c=n.r.a.ec().Jc();c.Ob();)r=u(c.Pb(),12),dQ(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])).b,e.g,e.f)&&--i;t=0)return t;switch(xw(Vc(e,t))){case 2:{if(bn("",Dd(e,t.ok()).ve())){if(f=BC(Vc(e,t)),l=L9(Vc(e,t)),b=gbe(e,n,f,l),b)return b;for(r=qbe(e,n),o=0,p=r.gc();o1)throw $(new Gn(JN));for(b=Po(e.e.Ah(),n),i=u(e.g,122),o=0;o1,h=new La(y.b);bu(h.a)||bu(h.b);)f=u(bu(h.a)?_(h.a):_(h.b),17),p=f.c==y?f.d:f.c,k.Math.abs(pu(z(B(Lr,1),Se,8,0,[p.i.n,p.n,p.a])).b-o.b)>1&&uDn(e,f,o,c,y)}}function APn(e){var n,t,i,r,c,o;if(r=new qr(e.e,0),i=new qr(e.a,0),e.d)for(t=0;tXee;){for(c=n,o=0;k.Math.abs(n-c)0),r.a.Xb(r.c=--r.b),R_n(e,e.b-o,c,i,r),at(r.b0),i.a.Xb(i.c=--i.b)}if(!e.d)for(t=0;t0?(e.f[b.p]=S/(b.e.c.length+b.g.c.length),e.c=k.Math.min(e.c,e.f[b.p]),e.b=k.Math.max(e.b,e.f[b.p])):l&&(e.f[b.p]=S)}}function TPn(e){e.b=null,e.bb=null,e.fb=null,e.qb=null,e.a=null,e.c=null,e.d=null,e.e=null,e.f=null,e.n=null,e.M=null,e.L=null,e.Q=null,e.R=null,e.K=null,e.db=null,e.eb=null,e.g=null,e.i=null,e.j=null,e.k=null,e.gb=null,e.o=null,e.p=null,e.q=null,e.r=null,e.$=null,e.ib=null,e.S=null,e.T=null,e.t=null,e.s=null,e.u=null,e.v=null,e.w=null,e.B=null,e.A=null,e.C=null,e.D=null,e.F=null,e.G=null,e.H=null,e.I=null,e.J=null,e.P=null,e.Z=null,e.U=null,e.V=null,e.W=null,e.X=null,e.Y=null,e._=null,e.ab=null,e.cb=null,e.hb=null,e.nb=null,e.lb=null,e.mb=null,e.ob=null,e.pb=null,e.jb=null,e.kb=null,e.N=!1,e.O=!1}function CPn(e,n,t){var i,r,c,o;for(t.Tg("Graph transformation ("+e.a+")",1),o=qb(n.a),c=new L(n.b);c.a=l.b.c)&&(l.b=n),(!l.c||n.c<=l.c.c)&&(l.d=l.c,l.c=n),(!l.e||n.d>=l.e.d)&&(l.e=n),(!l.f||n.d<=l.f.d)&&(l.f=n);return i=new cz((Z9(),op)),XC(e,Wtn,new Su(z(B(VN,1),On,377,0,[i]))),o=new cz(sm),XC(e,Qtn,new Su(z(B(VN,1),On,377,0,[o]))),r=new cz(om),XC(e,Ytn,new Su(z(B(VN,1),On,377,0,[r]))),c=new cz(xv),XC(e,Vtn,new Su(z(B(VN,1),On,377,0,[c]))),MW(i.c,op),MW(r.c,om),MW(c.c,xv),MW(o.c,sm),l.a.c.length=0,Er(l.a,i.c),Er(l.a,Ks(r.c)),Er(l.a,c.c),Er(l.a,Ks(o.c)),l}function DPn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;for(n.Tg(lWe,1),S=ne(re(ye(e,(Yh(),Mm)))),o=ne(re(ye(e,(Ja(),zx)))),l=u(ye(e,Bx),104),Yhe((!e.a&&(e.a=new pe(Jt,e,10,11)),e.a)),b=GKe((!e.a&&(e.a=new pe(Jt,e,10,11)),e.a),S,o),!e.a&&(e.a=new pe(Jt,e,10,11)),h=new L(b);h.a0&&(e.a=f+(S-1)*c,n.c.b+=e.a,n.f.b+=e.a)),A.a.gc()!=0&&(y=new jV(1,c),S=jge(y,n,A,O,n.f.b+f-n.c.b),S>0&&(n.f.b+=f+(S-1)*c))}function QKe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te;for(b=ne(re(T(e,(Ne(),Sg)))),i=ne(re(T(e,m4e))),y=new R6,he(y,Sg,b+i),h=n,R=h.d,O=h.c.i,q=h.d.i,I=zse(O.c),V=zse(q.c),r=new Ce,p=I;p<=V;p++)l=new Ba(e),Tf(l,(zn(),dr)),he(l,(me(),wi),h),he(l,Wi,(Br(),to)),he(l,yH,y),S=u(Le(e.b,p),25),p==I?z0(l,S.a.c.length-t,S):Or(l,S),te=ne(re(T(h,Gd))),te<0&&(te=0,he(h,Gd,te)),l.o.b=te,A=k.Math.floor(te/2),o=new Qu,xr(o,(De(),Kn)),gu(o,l),o.n.b=A,f=new Qu,xr(f,et),gu(f,l),f.n.b=A,Gr(h,o),c=new Mw,$u(c,h),he(c,Wc,null),lc(c,f),Gr(c,R),zxn(l,h,c),Hn(r.c,c),h=c;return r}function _Pn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;if(O=n.b.c.length,!(O<3)){for(S=oe($t,ni,30,O,15,1),p=0,b=new L(n.b);b.ao)&&hr(e.b,u(I.b,17));++l}c=o}}}function tZ(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;for(f=u(Pd(e,(De(),Kn)).Jc().Pb(),12).e,S=u(Pd(e,et).Jc().Pb(),12).g,l=f.c.length,V=_a(u(Le(e.j,0),12));l-- >0;){for(O=(vn(0,f.c.length),u(f.c[0],17)),r=(vn(0,S.c.length),u(S.c[0],17)),q=r.d.e,c=wu(q,r,0),qyn(O,r.d,c),lc(r,null),Gr(r,null),A=O.a,n&&Vt(A,new wc(V)),i=St(r.a,0);i.b!=i.d.c;)t=u(jt(i),8),Vt(A,new wc(t));for(R=O.b,y=new L(r.b);y.a-2;default:return!1}switch(n=e.Pj(),e.p){case 0:return n!=null&&Re($e(n))!=Gj(e.k,0);case 1:return n!=null&&u(n,221).a!=Rt(e.k)<<24>>24;case 2:return n!=null&&u(n,180).a!=(Rt(e.k)&yr);case 6:return n!=null&&Gj(u(n,190).a,e.k);case 5:return n!=null&&u(n,15).a!=Rt(e.k);case 7:return n!=null&&u(n,191).a!=Rt(e.k)<<16>>16;case 3:return n!=null&&ne(re(n))!=e.j;case 4:return n!=null&&u(n,164).a!=e.j;default:return n==null?e.n!=null:!di(n,e.n)}}function cN(e,n,t){var i,r,c,o;return e.ml()&&e.ll()&&(o=gV(e,u(t,57)),ue(o)!==ue(t))?(e.vj(n),e.Bj(n,B$e(e,n,o)),e.$k()&&(c=(r=u(t,52),e.kl()?e.il()?r.Qh(e.b,Oc(u(An(Go(e.b),e.Jj()),19)).n,u(An(Go(e.b),e.Jj()).Fk(),29).ik(),null):r.Qh(e.b,zi(r.Ah(),Oc(u(An(Go(e.b),e.Jj()),19))),null,null):r.Qh(e.b,-1-e.Jj(),null,null)),!u(o,52).Mh()&&(c=(i=u(o,52),e.kl()?e.il()?i.Oh(e.b,Oc(u(An(Go(e.b),e.Jj()),19)).n,u(An(Go(e.b),e.Jj()).Fk(),29).ik(),c):i.Oh(e.b,zi(i.Ah(),Oc(u(An(Go(e.b),e.Jj()),19))),null,c):i.Oh(e.b,-1-e.Jj(),null,c))),c&&c.mj()),Fs(e.b)&&e.Hj(e.Gj(9,t,o,n,!1)),o):t}function WKe(e){var n,t,i,r,c,o,l,f,h,b;for(i=new Ce,o=new L(e.e.a);o.a0&&(o=k.Math.max(o,qBe(e.C.b+i.d.b,r))),b=i,p=r,y=c;e.C&&e.C.c>0&&(S=y+e.C.c,h&&(S+=b.d.c),o=k.Math.max(o,(Oa(),Bf(Ga),k.Math.abs(p-1)<=Ga||p==1||isNaN(p)&&isNaN(1)?0:S/(1-p)))),t.n.b=0,t.a.a=o}function eVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S;if(t=u(zc(e.b,n),127),f=u(u(pi(e.r,n),22),83),f.dc()){t.n.d=0,t.n.a=0;return}for(h=e.u.Gc((ps(),Z1)),o=0,e.A.Gc((Vs(),Og))&&OXe(e,n),l=f.Jc(),b=null,y=0,p=0;l.Ob();)i=u(l.Pb(),115),c=ne(re(i.b.mf((F$(),wJ)))),r=i.b.Kf().b,b?(S=p+b.d.a+e.w+i.d.d,o=k.Math.max(o,(Oa(),Bf(Ga),k.Math.abs(y-c)<=Ga||y==c||isNaN(y)&&isNaN(c)?0:S/(c-y)))):e.C&&e.C.d>0&&(o=k.Math.max(o,qBe(e.C.d+i.d.d,c))),b=i,y=c,p=r;e.C&&e.C.a>0&&(S=p+e.C.a,h&&(S+=b.d.a),o=k.Math.max(o,(Oa(),Bf(Ga),k.Math.abs(y-1)<=Ga||y==1||isNaN(y)&&isNaN(1)?0:S/(1-y)))),t.n.d=0,t.a.b=o}function nVe(e,n,t){var i,r,c,o,l,f;for(this.g=e,l=n.d.length,f=t.d.length,this.d=oe(c1,Bd,9,l+f,0,1),o=0;o0?OY(this,this.f/this.a):Na(n.g,n.d[0]).a!=null&&Na(t.g,t.d[0]).a!=null?OY(this,(ne(Na(n.g,n.d[0]).a)+ne(Na(t.g,t.d[0]).a))/2):Na(n.g,n.d[0]).a!=null?OY(this,Na(n.g,n.d[0]).a):Na(t.g,t.d[0]).a!=null&&OY(this,Na(t.g,t.d[0]).a)}function PPn(e,n,t,i,r,c,o,l){var f,h,b,p,y,S,A,O,I,R;if(A=!1,h=Ebe(t.q,n.f+n.b-t.q.f),S=i.f>n.b&&l,R=r-(t.q.e+h-o),p=(f=fS(i,R,!1),f.a),S&&p>i.f)return!1;if(S){for(y=0,I=new L(n.d);I.a=(vn(c,e.c.length),u(e.c[c],186)).e,!S&&p>n.b&&!b)?!1:((b||S||p<=n.b)&&(b&&p>n.b?(t.d=p,eO(t,PGe(t,p))):(QHe(t.q,h),t.c=!0),eO(i,r-(t.s+t.r)),IO(i,t.q.e+t.q.d,n.f),vB(n,i),e.c.length>c&&($O((vn(c,e.c.length),u(e.c[c],186)),i),(vn(c,e.c.length),u(e.c[c],186)).a.c.length==0&&Ad(e,c)),A=!0),A)}function $Pn(e,n){var t,i,r,c,o,l,f,h,b,p;for(e.a=new vIe(lkn(Vx)),i=new L(n.a);i.a0&&(Yn(0,t.length),t.charCodeAt(0)!=47)))throw $(new Gn("invalid opaquePart: "+t));if(e&&!(n!=null&&Sj(jG,n.toLowerCase()))&&!(t==null||!kQ(t,uA,oA)))throw $(new Gn(FZe+t));if(e&&n!=null&&Sj(jG,n.toLowerCase())&&!PAn(t))throw $(new Gn(FZe+t));if(!Jjn(i))throw $(new Gn("invalid device: "+i));if(!zkn(r))throw o=r==null?"invalid segments: null":"invalid segment: "+_kn(r),$(new Gn(o));if(!(c==null||lh(c,Xo(35))==-1))throw $(new Gn("invalid query: "+c))}function iVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;if(y=new wc(e.o),R=n.a/y.a,l=n.b/y.b,O=n.a-y.a,c=n.b-y.b,t)for(r=ue(T(e,(Ne(),Wi)))===ue((Br(),to)),A=new L(e.j);A.a=1&&(I-o>0&&p>=0?(f.n.a+=O,f.n.b+=c*o):I-o<0&&b>=0&&(f.n.a+=O*I,f.n.b+=c));e.o.a=n.a,e.o.b=n.b,he(e,(Ne(),jg),(Vs(),i=u(sa(tA),10),new _l(i,u(_f(i,i.length),10),0)))}function FPn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;if(t.Tg("Network simplex layering",1),e.b=n,R=u(T(n,(Ne(),kx)),15).a*4,I=e.b.a,I.c.length<1){t.Ug();return}for(c=_In(e,I),O=null,r=St(c,0);r.b!=r.d.c;){for(i=u(jt(r),16),l=R*sc(k.Math.sqrt(i.gc())),o=KIn(i),BW(_oe(Kbn(Loe(VK(o),l),O),!0),t.dh(1)),y=e.b.b,A=new L(o.a);A.a1)for(O=oe($t,ni,30,e.b.b.c.length,15,1),p=0,h=new L(e.b.b);h.a0){tz(e,t,0),t.a+=String.fromCharCode(i),r=AEn(n,c),tz(e,t,r),c+=r-1;continue}i==39?c+10&&A.a<=0){f.c.length=0,Hn(f.c,A);break}S=A.i-A.d,S>=l&&(S>l&&(f.c.length=0,l=S),Hn(f.c,A))}f.c.length!=0&&(o=u(Le(f,sz(r,f.c.length)),116),V.a.Ac(o)!=null,o.g=b++,oge(o,n,t,i),f.c.length=0)}for(I=e.c.length+1,y=new L(e);y.aDr||n.o==Ag&&b=l&&r<=f)l<=r&&c<=f?(t[b++]=r,t[b++]=c,i+=2):l<=r?(t[b++]=r,t[b++]=f,e.b[i]=f+1,o+=2):c<=f?(t[b++]=l,t[b++]=c,i+=2):(t[b++]=l,t[b++]=f,e.b[i]=f+1);else if(fX0)&&l<10);Poe(e.c,new b5),rVe(e),Pvn(e.c),OPn(e.f)}function ZPn(e,n){var t,i,r,c,o,l,f,h,b,p,y;switch(e.k.g){case 1:if(i=u(T(e,(me(),wi)),17),t=u(T(i,Kve),78),t?Re($e(T(i,Hd)))&&(t=k1e(t)):t=new xs,h=u(T(e,ja),12),h){if(b=pu(z(B(Lr,1),Se,8,0,[h.i.n,h.n,h.a])),n<=b.a)return b.b;Xi(t,b,t.a,t.a.a)}if(p=u(T(e,gf),12),p){if(y=pu(z(B(Lr,1),Se,8,0,[p.i.n,p.n,p.a])),y.a<=n)return y.b;Xi(t,y,t.c.b,t.c)}if(t.b>=2){for(f=St(t,0),o=u(jt(f),8),l=u(jt(f),8);l.a0&&kO(h,!0,(vr(),ru)),l.k==(zn(),wr)&&_Ie(h),ei(e.f,l,n)}}function uVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q;for(h=Ki,b=Ki,l=Dr,f=Dr,y=new L(n.i);y.a=e.j?(++e.j,Te(e.b,ve(1)),Te(e.c,b)):(i=e.d[n.p][1],ol(e.b,h,ve(u(Le(e.b,h),15).a+1-i)),ol(e.c,h,ne(re(Le(e.c,h)))+b-i*e.f)),(e.r==(G0(),gD)&&(u(Le(e.b,h),15).a>e.k||u(Le(e.b,h-1),15).a>e.k)||e.r==wD&&(ne(re(Le(e.c,h)))>e.n||ne(re(Le(e.c,h-1)))>e.n))&&(f=!1),o=new qn(Vn(rr(n).a.Jc(),new ee));ht(o);)c=u(tt(o),17),l=c.c.i,e.g[l.p]==h&&(p=oVe(e,l),r=r+u(p.a,15).a,f=f&&Re($e(p.b)));return e.g[n.p]=h,r=r+e.d[n.p][0],new jc(ve(r),(Pn(),!!f))}function n$n(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;return y=e.c[n],S=e.c[t],A=u(T(y,(me(),Ty)),16),!!A&&A.gc()!=0&&A.Gc(S)||(O=y.k!=(zn(),dr)&&S.k!=dr,I=u(T(y,ap),9),R=u(T(S,ap),9),q=I!=R,V=!!I&&I!=y||!!R&&R!=S,te=qQ(y,(De(),Xn)),be=qQ(S,bt),V=V|(qQ(y,bt)||qQ(S,Xn)),fe=V&&q||te||be,O&&fe)||y.k==(zn(),wo)&&S.k==Qi||S.k==(zn(),wo)&&y.k==Qi?!1:(b=e.c[n],c=e.c[t],r=HHe(e.e,b,c,(De(),Kn)),f=HHe(e.i,b,c,et),TNn(e.f,b,c),h=Yze(e.b,b,c)+u(r.a,15).a+u(f.a,15).a+e.f.d,l=Yze(e.b,c,b)+u(r.b,15).a+u(f.b,15).a+e.f.b,e.a&&(p=u(T(b,wi),12),o=u(T(c,wi),12),i=MHe(e.g,p,o),h+=u(i.a,15).a,l+=u(i.b,15).a),h>l)}function sVe(e,n){var t,i,r,c,o;t=ne(re(T(n,(Ne(),Vf)))),t<2&&he(n,Vf,2),i=u(T(n,pl),86),i==(vr(),eh)&&he(n,pl,GB(n)),r=u(T(n,Dun),15),r.a==0?he(n,(me(),Oy),new vQ):he(n,(me(),Oy),new XR(r.a)),c=$e(T(n,mx)),c==null&&he(n,mx,(Pn(),ue(T(n,Y1))===ue((z1(),H7)))),Zi(new wn(null,new pn(n.a,16)),new Zue(e)),Zi(ou(new wn(null,new pn(n.b,16)),new Af),new eoe(e)),o=new tVe(n),he(n,(me(),Lv),o),RC(e.a),fa(e.a,(zr(),Kf),u(T(n,_y),188)),fa(e.a,r1,u(T(n,bH),188)),fa(e.a,eo,u(T(n,wx),188)),fa(e.a,no,u(T(n,mH),188)),fa(e.a,Pc,D7n(u(T(n,Y1),222))),Fse(e.a,YRn(n)),he(n,yie,rN(e.a,n))}function jge(e,n,t,i,r){var c,o,l,f,h,b,p,y,S,A,O,I,R;for(p=new pt,o=new Ce,tqe(e,t,e.d.zg(),o,p),tqe(e,i,e.d.Ag(),o,p),e.b=.2*(O=lUe(ou(new wn(null,new pn(o,16)),new pM)),I=lUe(ou(new wn(null,new pn(o,16)),new mM)),k.Math.min(O,I)),c=0,l=0;l=2&&(R=NUe(o,!0,y),!e.e&&(e.e=new MEe(e)),SEn(e.e,R,o,e.b)),sGe(o,y),o$n(o),S=-1,b=new L(o);b.a0&&(t+=f.n.a+f.o.a/2,++p),A=new L(f.j);A.a0&&(t/=p),R=oe(Jr,Jc,30,i.a.c.length,15,1),l=0,h=new L(i.a);h.a-1){for(r=St(l,0);r.b!=r.d.c;)i=u(jt(r),132),i.v=o;for(;l.b!=0;)for(i=u(eW(l,0),132),t=new L(i.i);t.a-1){for(c=new L(l);c.a0)&&(fw(f,k.Math.min(f.o,r.o-1)),g0(f,f.i-1),f.i==0&&Hn(l.c,f))}}function aVe(e,n,t,i,r){var c,o,l,f;return f=Ki,o=!1,l=dge(e,Nr(new je(n.a,n.b),e),gi(new je(t.a,t.b),r),Nr(new je(i.a,i.b),t)),c=!!l&&!(k.Math.abs(l.a-e.a)<=np&&k.Math.abs(l.b-e.b)<=np||k.Math.abs(l.a-n.a)<=np&&k.Math.abs(l.b-n.b)<=np),l=dge(e,Nr(new je(n.a,n.b),e),t,r),l&&((k.Math.abs(l.a-e.a)<=np&&k.Math.abs(l.b-e.b)<=np)==(k.Math.abs(l.a-n.a)<=np&&k.Math.abs(l.b-n.b)<=np)||c?f=k.Math.min(f,fE(Nr(l,t))):o=!0),l=dge(e,Nr(new je(n.a,n.b),e),i,r),l&&(o||(k.Math.abs(l.a-e.a)<=np&&k.Math.abs(l.b-e.b)<=np)==(k.Math.abs(l.a-n.a)<=np&&k.Math.abs(l.b-n.b)<=np)||c)&&(f=k.Math.min(f,fE(Nr(l,i)))),f}function hVe(e){r2(e,new Jw(GP(t2(Zp(n2(e2(new dd,V0),cQe),"Minimizes the stress within a layout using stress majorization. Stress exists if the euclidean distance between a pair of nodes doesn't match their graph theoretic distance, that is, the shortest path between the two nodes. The method allows to specify individual edge lengths."),new dc),$o))),Ee(e,V0,jS,_e(d3e)),Ee(e,V0,lF,(Pn(),!0)),Ee(e,V0,wv,_e(Ltn)),Ee(e,V0,dy,_e(Ptn)),Ee(e,V0,hy,_e($tn)),Ee(e,V0,U8,_e(_tn)),Ee(e,V0,ES,_e(g3e)),Ee(e,V0,X8,_e(Rtn)),Ee(e,V0,swe,_e(h3e)),Ee(e,V0,fwe,_e(f3e)),Ee(e,V0,awe,_e(a3e)),Ee(e,V0,hwe,_e(b3e)),Ee(e,V0,lwe,_e(kJ))}function s$n(e){var n,t,i,r,c,o,l,f;for(n=null,i=new L(e);i.a0&&t.c==0&&(!n&&(n=new Ce),Hn(n.c,t));if(n)for(;n.c.length!=0;){if(t=u(Ad(n,0),239),t.b&&t.b.c.length>0){for(c=(!t.b&&(t.b=new Ce),new L(t.b));c.awu(e,t,0))return new jc(r,t)}else if(ne(Na(r.g,r.d[0]).a)>ne(Na(t.g,t.d[0]).a))return new jc(r,t)}for(l=(!t.e&&(t.e=new Ce),t.e).Jc();l.Ob();)o=u(l.Pb(),239),f=(!o.b&&(o.b=new Ce),o.b),S2(0,f.c.length),Ij(f.c,0,t),o.c==f.c.length&&Hn(n.c,o)}return null}function dS(e,n){var t,i,r,c,o,l,f,h,b;if(n.e==5){cVe(e,n);return}if(h=n,!(h.b==null||e.b==null)){for(ov(e),aS(e),ov(h),aS(h),t=oe($t,ni,30,e.b.length+h.b.length,15,1),b=0,i=0,o=0;i=l&&r<=f)l<=r&&c<=f?i+=2:l<=r?(e.b[i]=f+1,o+=2):c<=f?(t[b++]=r,t[b++]=l-1,i+=2):(t[b++]=r,t[b++]=l-1,e.b[i]=f+1,o+=2);else if(f0),u(b.a.Xb(b.c=--b.b),17));c!=i&&b.b>0;)e.a[c.p]=!0,e.a[i.p]=!0,c=(at(b.b>0),u(b.a.Xb(b.c=--b.b),17));b.b>0&&As(b)}}function dVe(e,n,t){var i,r,c,o,l,f,h,b,p,y;if(t)for(i=-1,b=new qr(n,0);b.b0?r-=864e5:r+=864e5,f=new jle(mc(Pu(n.q.getTime()),r))),b=new o4,h=e.a.length,c=0;c=97&&i<=122||i>=65&&i<=90){for(o=c+1;o=h)throw $(new Gn("Missing trailing '"));o+1=14&&b<=16))?n.a._b(i)?(t.a?Kt(t.a,t.b):t.a=new il(t.d),Uj(t.a,"[...]")):(l=B4(i),h=new w2(n),I1(t,gVe(l,h))):X(i,171)?I1(t,tCn(u(i,171))):X(i,195)?I1(t,GAn(u(i,195))):X(i,201)?I1(t,YMn(u(i,201))):X(i,2073)?I1(t,qAn(u(i,2073))):X(i,54)?I1(t,nCn(u(i,54))):X(i,584)?I1(t,gCn(u(i,584))):X(i,830)?I1(t,eCn(u(i,830))):X(i,108)&&I1(t,ZTn(u(i,108))):I1(t,i==null?Vo:su(i));return t.a?t.e.length==0?t.a.a:t.a.a+(""+t.e):t.c}function N8(e,n){var t,i,r,c;c=e.F,n==null?(e.F=null,i8(e,null)):(e.F=(_n(n),n),i=lh(n,Xo(60)),i!=-1?(r=(Yr(0,i,n.length),n.substr(0,i)),lh(n,Xo(46))==-1&&!bn(r,ry)&&!bn(r,RS)&&!bn(r,XF)&&!bn(r,BS)&&!bn(r,zS)&&!bn(r,FS)&&!bn(r,JS)&&!bn(r,HS)&&(r=een),t=R$(n,Xo(62)),t!=-1&&(r+=""+(Yn(t+1,n.length+1),n.substr(t+1))),i8(e,r)):(r=n,lh(n,Xo(46))==-1&&(i=lh(n,Xo(91)),i!=-1&&(r=(Yr(0,i,n.length),n.substr(0,i))),!bn(r,ry)&&!bn(r,RS)&&!bn(r,XF)&&!bn(r,BS)&&!bn(r,zS)&&!bn(r,FS)&&!bn(r,JS)&&!bn(r,HS)?(r=een,i!=-1&&(r+=""+(Yn(i,n.length+1),n.substr(i)))):r=n),i8(e,r),r==n&&(e.F=e.D))),(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,5,c,n))}function g$n(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;if(e.c=e.e,A=$e(T(n,(Ne(),Iun))),S=A==null||(_n(A),A),c=u(T(n,(me(),po)),22).Gc((Dc(),Kl)),r=u(T(n,Wi),102),t=!(r==(Br(),Cg)||r==f1||r==to),S&&(t||!c)){for(p=new L(n.a);p.a=0)return r=Rjn(e,(Yr(1,o,n.length),n.substr(1,o-1))),b=(Yr(o+1,f,n.length),n.substr(o+1,f-(o+1))),FRn(e,b,r)}else{if(t=-1,Mme==null&&(Mme=new RegExp("\\d")),Mme.test(String.fromCharCode(l))&&(t=Hle(n,Xo(46),f-1),t>=0)){i=u(fY(e,VRe(e,(Yr(1,t,n.length),n.substr(1,t-1))),!1),61),h=0;try{h=hl((Yn(t+1,n.length+1),n.substr(t+1)),Xr,oi)}catch(y){throw y=sr(y),X(y,131)?(c=y,$(new uB(c))):$(y)}if(h>16==-10?t=u(e.Cb,293).Wk(n,t):e.Db>>16==-15&&(!n&&(n=(yn(),ih)),!h&&(h=(yn(),ih)),e.Cb.Vh()&&(f=new L1(e.Cb,1,13,h,n,Ld(Cs(u(e.Cb,62)),e),!1),t?t.lj(f):t=f));else if(X(e.Cb,88))e.Db>>16==-23&&(X(n,88)||(n=(yn(),jf)),X(h,88)||(h=(yn(),jf)),e.Cb.Vh()&&(f=new L1(e.Cb,1,10,h,n,Ld(Vu(u(e.Cb,29)),e),!1),t?t.lj(f):t=f));else if(X(e.Cb,446))for(l=u(e.Cb,834),o=(!l.b&&(l.b=new OP(new kX)),l.b),c=(i=new D2(new on(o.a).a),new NP(i));c.a.b;)r=u(Q3(c.a).jd(),87),t=D8(r,Oz(r,l),t)}return t}function p$n(e,n){var t,i,r,c,o,l,f,h,b,p,y;for(o=Re($e(ye(e,(Ne(),pm)))),y=u(ye(e,ym),22),f=!1,h=!1,p=new ut((!e.c&&(e.c=new pe($s,e,9,9)),e.c));p.e!=p.i.gc()&&(!f||!h);){for(c=u(ft(p),125),l=0,r=qh(Rl(z(B(Xl,1),On,20,0,[(!c.d&&(c.d=new Nn(pr,c,8,5)),c.d),(!c.e&&(c.e=new Nn(pr,c,7,4)),c.e)])));ht(r)&&(i=u(tt(r),85),b=o&&Hw(i)&&Re($e(ye(i,kg))),t=VKe((!i.b&&(i.b=new Nn(vt,i,4,7)),i.b),c)?e==Bi(iu(u(K((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c),0),84))):e==Bi(iu(u(K((!i.b&&(i.b=new Nn(vt,i,4,7)),i.b),0),84))),!((b||t)&&(++l,l>1))););(l>0||y.Gc((ps(),Z1))&&(!c.n&&(c.n=new pe(ju,c,1,7)),c.n).i>0)&&(f=!0),l>1&&(h=!0)}f&&n.Ec((Dc(),Kl)),h&&n.Ec((Dc(),cx))}function pVe(e){var n,t,i,r,c,o,l,f,h,b,p,y;if(y=u(ye(e,(Xt(),Tg)),22),y.dc())return null;if(l=0,o=0,y.Gc((Vs(),qD))){for(b=u(ye(e,Kx),102),i=2,t=2,r=2,c=2,n=Bi(e)?u(ye(Bi(e),Mg),86):u(ye(e,Mg),86),h=new ut((!e.c&&(e.c=new pe($s,e,9,9)),e.c));h.e!=h.i.gc();)if(f=u(ft(h),125),p=u(ye(f,Qv),64),p==(De(),ku)&&(p=uge(f,n),ji(f,Qv,p)),b==(Br(),to))switch(p.g){case 1:i=k.Math.max(i,f.i+f.g);break;case 2:t=k.Math.max(t,f.j+f.f);break;case 3:r=k.Math.max(r,f.i+f.g);break;case 4:c=k.Math.max(c,f.j+f.f)}else switch(p.g){case 1:i+=f.g+2;break;case 2:t+=f.f+2;break;case 3:r+=f.g+2;break;case 4:c+=f.f+2}l=k.Math.max(i,r),o=k.Math.max(t,c)}return Xw(e,l,o,!0,!0)}function m$n(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O;for(r=null,i=new L(n.a);i.a1)for(r=e.e.b,Vt(e.e,f),l=f.a.ec().Jc();l.Ob();)o=u(l.Pb(),9),ei(e.c,o,ve(r))}}function v$n(e,n,t,i){var r,c,o,l,f,h,b,p,y,S;for(c=new Lqe(n),p=UDn(e,n,c),S=k.Math.max(ne(re(T(n,(Ne(),Gd)))),1),b=new L(p.a);b.a=0){for(f=null,l=new qr(b.a,h+1);l.b0,h?h&&(y=R.p,o?++y:--y,p=u(Le(R.c.a,y),9),i=Oze(p),S=!(RUe(i,fe,t[0])||KDe(i,fe,t[0]))):S=!0),A=!1,be=n.D.i,be&&be.c&&l.e&&(b=o&&be.p>0||!o&&be.p=0&&Oo?1:Lb(isNaN(0),isNaN(o)))<0&&(Bf(xh),(k.Math.abs(o-1)<=xh||o==1||isNaN(o)&&isNaN(1)?0:o<1?-1:o>1?1:Lb(isNaN(o),isNaN(1)))<0)&&(Bf(xh),(k.Math.abs(0-l)<=xh||l==0||isNaN(0)&&isNaN(l)?0:0l?1:Lb(isNaN(0),isNaN(l)))<0)&&(Bf(xh),(k.Math.abs(l-1)<=xh||l==1||isNaN(l)&&isNaN(1)?0:l<1?-1:l>1?1:Lb(isNaN(l),isNaN(1)))<0)),c)}function M$n(e){var n,t,i,r,c,o,l,f,h,b,p;for(e.j=oe($t,ni,30,e.g,15,1),e.o=new Ce,Zi(ou(new wn(null,new pn(e.e.b,16)),new h3),new jEe(e)),e.a=oe(ts,pa,30,e.b,16,1),AO(new wn(null,new pn(e.e.b,16)),new SEe(e)),i=(p=new Ce,Zi(si(ou(new wn(null,new pn(e.e.b,16)),new _5),new EEe(e)),new sTe(e,p)),p),f=new L(i);f.a=h.c.c.length?b=Bae((zn(),Qi),dr):b=Bae((zn(),dr),dr),b*=2,c=t.a.g,t.a.g=k.Math.max(c,c+(b-c)),o=t.b.g,t.b.g=k.Math.max(o,o+(b-o)),r=n}}function Vz(e,n){var t;if(e.e)throw $(new Uc((M1(bte),qZ+bte.k+UZ)));if(!zgn(e.a,n))throw $(new hu($Ye+n+RYe));if(n==e.d)return e;switch(t=e.d,e.d=n,t.g){case 0:switch(n.g){case 2:zw(e);break;case 1:P0(e),zw(e);break;case 4:rv(e),zw(e);break;case 3:rv(e),P0(e),zw(e)}break;case 2:switch(n.g){case 1:P0(e),_W(e);break;case 4:rv(e),zw(e);break;case 3:rv(e),P0(e),zw(e)}break;case 1:switch(n.g){case 2:P0(e),_W(e);break;case 4:P0(e),rv(e),zw(e);break;case 3:P0(e),rv(e),P0(e),zw(e)}break;case 4:switch(n.g){case 2:rv(e),zw(e);break;case 1:rv(e),P0(e),zw(e);break;case 3:P0(e),_W(e)}break;case 3:switch(n.g){case 2:P0(e),rv(e),zw(e);break;case 1:P0(e),rv(e),P0(e),zw(e);break;case 4:P0(e),_W(e)}}return e}function hv(e,n){var t;if(e.d)throw $(new Uc((M1(Tte),qZ+Tte.k+UZ)));if(!Bgn(e.a,n))throw $(new hu($Ye+n+RYe));if(n==e.c)return e;switch(t=e.c,e.c=n,t.g){case 0:switch(n.g){case 2:Zb(e);break;case 1:L0(e),Zb(e);break;case 4:cv(e),Zb(e);break;case 3:cv(e),L0(e),Zb(e)}break;case 2:switch(n.g){case 1:L0(e),LW(e);break;case 4:cv(e),Zb(e);break;case 3:cv(e),L0(e),Zb(e)}break;case 1:switch(n.g){case 2:L0(e),LW(e);break;case 4:L0(e),cv(e),Zb(e);break;case 3:L0(e),cv(e),L0(e),Zb(e)}break;case 4:switch(n.g){case 2:cv(e),Zb(e);break;case 1:cv(e),L0(e),Zb(e);break;case 3:L0(e),LW(e)}break;case 3:switch(n.g){case 2:L0(e),cv(e),Zb(e);break;case 1:L0(e),cv(e),L0(e),Zb(e);break;case 4:L0(e),LW(e)}}return e}function T$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;for(p=e.b,b=new qr(p,0),d2(b,new Xu(e)),q=!1,o=1;b.b0&&(n.a+=Co),Yz(u(ft(l),174),n);for(n.a+=eee,f=new p4((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c));f.e!=f.i.gc();)f.e>0&&(n.a+=Co),Yz(u(ft(f),174),n);n.a+=")"}}function C$n(e,n,t){var i,r,c,o,l,f,h,b;for(f=new ut((!e.a&&(e.a=new pe(Jt,e,10,11)),e.a));f.e!=f.i.gc();)for(l=u(ft(f),26),r=new qn(Vn(H0(l).a.Jc(),new ee));ht(r);){if(i=u(tt(r),85),!i.b&&(i.b=new Nn(vt,i,4,7)),!(i.b.i<=1&&(!i.c&&(i.c=new Nn(vt,i,5,8)),i.c.i<=1)))throw $(new u4("Graph must not contain hyperedges."));if(!ZE(i)&&l!=iu(u(K((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c),0),84)))for(h=new nNe,$u(h,i),he(h,(D0(),jy),i),EP(h,u(du(Xc(t.f,l)),155)),eX(h,u(Bn(t,iu(u(K((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c),0),84))),155)),Te(n.c,h),o=new ut((!i.n&&(i.n=new pe(ju,i,1,7)),i.n));o.e!=o.i.gc();)c=u(ft(o),157),b=new lPe(h,c.a),$u(b,c),he(b,jy,c),b.e.a=k.Math.max(c.g,1),b.e.b=k.Math.max(c.f,1),hge(b),Te(n.d,b)}}function O$n(e,n,t){var i,r,c,o,l,f,h,b,p,y;switch(t.Tg("Node promotion heuristic",1),e.i=n,e.r=u(T(n,(Ne(),aD)),243),e.r!=(G0(),D7)&&e.r!=Sx?tRn(e):TDn(e),b=u(T(e.i,i4e),15).a,c=new Oq,e.r.g){case 2:case 1:O8(e,c);break;case 3:for(e.r=MH,O8(e,c),f=0,l=new L(e.b);l.ae.k&&(e.r=gD,O8(e,c));break;case 4:for(e.r=MH,O8(e,c),h=0,r=new L(e.c);r.ae.n&&(e.r=wD,O8(e,c));break;case 6:y=sc(k.Math.ceil(e.g.length*b/100)),O8(e,new kje(y));break;case 5:p=sc(k.Math.ceil(e.e*b/100)),O8(e,new jje(p));break;case 8:ZVe(e,!0);break;case 9:ZVe(e,!1);break;default:O8(e,c)}e.r!=D7&&e.r!=Sx?KNn(e,n):dIn(e,n),t.Ug()}function N$n(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;for(p=new Mge(e),v4n(p,!(n==(vr(),Vl)||n==Za)),b=p.a,y=new t4,r=(ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])),o=0,f=r.length;o0&&(y.d+=b.n.d,y.d+=b.d),y.a>0&&(y.a+=b.n.a,y.a+=b.d),y.b>0&&(y.b+=b.n.b,y.b+=b.d),y.c>0&&(y.c+=b.n.c,y.c+=b.d),y}function yVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A;for(y=t.d,p=t.c,c=new je(t.f.a+t.d.b+t.d.c,t.f.b+t.d.d+t.d.a),o=c.b,h=new L(e.a);h.a0&&(e.c[n.c.p][n.p].d+=Is(e.i,24)*vN*.07000000029802322-.03500000014901161,e.c[n.c.p][n.p].a=e.c[n.c.p][n.p].d/e.c[n.c.p][n.p].b)}}function I$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;for(A=new L(e);A.ai.d,i.d=k.Math.max(i.d,n),l&&t&&(i.d=k.Math.max(i.d,i.a),i.a=i.d+r);break;case 3:t=n>i.a,i.a=k.Math.max(i.a,n),l&&t&&(i.a=k.Math.max(i.a,i.d),i.d=i.a+r);break;case 2:t=n>i.c,i.c=k.Math.max(i.c,n),l&&t&&(i.c=k.Math.max(i.b,i.c),i.b=i.c+r);break;case 4:t=n>i.b,i.b=k.Math.max(i.b,n),l&&t&&(i.b=k.Math.max(i.b,i.c),i.c=i.b+r)}}}function jVe(e,n){var t,i,r,c,o,l,f,h,b;return h="",n.length==0?e.le(Jge,wZ,-1,-1):(b=J2(n),bn(b.substr(0,3),"at ")&&(b=(Yn(3,b.length+1),b.substr(3))),b=b.replace(/\[.*?\]/g,""),o=b.indexOf("("),o==-1?(o=b.indexOf("@"),o==-1?(h=b,b=""):(h=J2((Yn(o+1,b.length+1),b.substr(o+1))),b=J2((Yr(0,o,b.length),b.substr(0,o))))):(t=b.indexOf(")",o),h=(Yr(o+1,t,b.length),b.substr(o+1,t-(o+1))),b=J2((Yr(0,o,b.length),b.substr(0,o)))),o=lh(b,Xo(46)),o!=-1&&(b=(Yn(o+1,b.length+1),b.substr(o+1))),(b.length==0||bn(b,"Anonymous function"))&&(b=wZ),l=R$(h,Xo(58)),r=Hle(h,Xo(58),l-1),f=-1,i=-1,c=Jge,l!=-1&&r!=-1&&(c=(Yr(0,r,h.length),h.substr(0,r)),f=yOe((Yr(r+1,l,h.length),h.substr(r+1,l-(r+1)))),i=yOe((Yn(l+1,h.length+1),h.substr(l+1)))),e.le(c,b,f,i))}function L$n(e){var n,t,i,r,c,o,l,f,h,b,p;for(h=new L(e);h.a0||b.j==Kn&&b.e.c.length-b.g.c.length<0)){n=!1;break}for(r=new L(b.g);r.a=h&&be>=I&&(y+=A.n.b+O.n.b+O.a.b-te,++l));if(t)for(o=new L(q.e);o.a=h&&be>=I&&(y+=A.n.b+O.n.b+O.a.b-te,++l))}l>0&&(fe+=y/l,++S)}S>0?(n.a=r*fe/S,n.g=S):(n.a=0,n.g=0)}function xge(e,n,t,i){var r,c,o,l,f;return l=new Mge(n),NNn(l,i),r=!0,e&&e.nf((Xt(),Mg))&&(c=u(e.mf((Xt(),Mg)),86),r=c==(vr(),eh)||c==Zc||c==ru),jXe(l,!1),Ao(l.e.Pf(),new Kle(l,!1,r)),JV(l,l.f,(ga(),Ou),(De(),Xn)),JV(l,l.f,Nu,bt),JV(l,l.g,Ou,Kn),JV(l,l.g,Nu,et),HJe(l,Xn),HJe(l,bt),RIe(l,et),RIe(l,Kn),h2(),o=l.A.Gc((Vs(),Lm))&&l.B.Gc((_s(),XD))?tJe(l):null,o&&Ybn(l.a,o),_$n(l),nxn(l),txn(l),f$n(l),E_n(l),Mxn(l),OQ(l,Xn),OQ(l,bt),aIn(l),ZLn(l),t&&(Xjn(l),Txn(l),OQ(l,et),OQ(l,Kn),f=l.B.Gc((_s(),iA)),lqe(l,f,Xn),lqe(l,f,bt),fqe(l,f,et),fqe(l,f,Kn),Zi(new wn(null,new pn(new ct(l.i),0)),new Fg),Zi(si(new wn(null,Jfe(l.r).a.oc()),new Eb),new Jg),zAn(l),l.e.Nf(l.o),Zi(new wn(null,Jfe(l.r).a.oc()),new _u)),l.o}function $$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O;for(h=Ki,i=new L(e.a.b);i.a1)for(S=new wge(A,V,i),rc(V,new fTe(e,S)),Hn(o.c,S),p=V.a.ec().Jc();p.Ob();)b=u(p.Pb(),49),qo(c,b.b);if(l.a.gc()>1)for(S=new wge(A,l,i),rc(l,new aTe(e,S)),Hn(o.c,S),p=l.a.ec().Jc();p.Ob();)b=u(p.Pb(),49),qo(c,b.b)}}function F$n(e,n){var t,i,r,c,o,l;if(u(T(n,(me(),po)),22).Gc((Dc(),Kl))){for(l=new L(n.a);l.a=0&&o0&&(u(zc(e.b,n),127).a.b=t)}function K$n(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;for(S=0,i=new ar,c=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));c.e!=c.i.gc();)r=u(ft(c),26),Re($e(ye(r,(Ne(),Eg))))||(p=Bi(r),qz(p)&&!Re($e(ye(r,lH)))&&(ji(r,(me(),Ci),ve(S)),++S,da(r,gm)&&hr(i,u(ye(r,gm),15))),SVe(e,r,t));for(he(t,(me(),cb),ve(S)),he(t,cD,ve(i.a.gc())),S=0,b=new ut((!n.b&&(n.b=new pe(pr,n,12,3)),n.b));b.e!=b.i.gc();)f=u(ft(b),85),qz(n)&&(ji(f,Ci,ve(S)),++S),I=hW(f),R=kGe(f),y=Re($e(ye(I,(Ne(),pm)))),O=!Re($e(ye(f,Eg))),A=y&&Hw(f)&&Re($e(ye(f,kg))),o=Bi(I)==n&&Bi(I)==Bi(R),l=(Bi(I)==n&&R==n)^(Bi(R)==n&&I==n),O&&!A&&(l||o)&&Dge(e,f,n,t);if(Bi(n))for(h=new ut(qIe(Bi(n)));h.e!=h.i.gc();)f=u(ft(h),85),I=hW(f),I==n&&Hw(f)&&(A=Re($e(ye(I,(Ne(),pm))))&&Re($e(ye(f,kg))),A&&Dge(e,f,n,t))}function V$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn,Dn;for(fe=new Ce,A=new L(e.b);A.a=n.length)return{done:!0};var r=n[i++];return{value:[r,t.get(r)],done:!1}}}},qDn()||(e.prototype.createObject=function(){return{}},e.prototype.get=function(n){return this.obj[":"+n]},e.prototype.set=function(n,t){this.obj[":"+n]=t},e.prototype[zZ]=function(n){delete this.obj[":"+n]},e.prototype.keys=function(){var n=[];for(var t in this.obj)t.charCodeAt(0)==58&&n.push(t.substring(1));return n}),e}function Ti(){Ti=Y,_x=new yi(owe),new Li("DEPTH",ve(0)),gre=new Li("FAN",ve(0)),pye=new Li(KQe,ve(0)),fb=new Li("ROOT",(Pn(),!1)),mre=new Li("LEFTNEIGHBOR",null),rsn=new Li("RIGHTNEIGHBOR",null),LH=new Li("LEFTSIBLING",null),vre=new Li("RIGHTSIBLING",null),bre=new Li("DUMMY",!1),new Li("LEVEL",ve(0)),yye=new Li("REMOVABLE_EDGES",new Si),jD=new Li("XCOOR",ve(0)),ED=new Li("YCOOR",ve(0)),PH=new Li("LEVELHEIGHT",0),Ea=new Li("LEVELMIN",0),Yf=new Li("LEVELMAX",0),wre=new Li("GRAPH_XMIN",0),pre=new Li("GRAPH_YMIN",0),mye=new Li("GRAPH_XMAX",0),vye=new Li("GRAPH_YMAX",0),wye=new Li("COMPACT_LEVEL_ASCENSION",!1),dre=new Li("COMPACT_CONSTRAINTS",new Ce),Ix=new Li("ID",""),Lx=new Li("POSITION",ve(0)),Xd=new Li("PRELIM",0),L7=new Li("MODIFIER",0),_7=new yi(iQe),kD=new yi(rQe)}function Z$n(e){cge();var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;if(e==null)return null;if(p=e.length*8,p==0)return"";for(l=p%24,S=p/24|0,y=l!=0?S+1:S,c=null,c=oe(Wl,kh,30,y*4,15,1),h=0,b=0,n=0,t=0,i=0,o=0,r=0,f=0;f>24,h=(n&3)<<24>>24,A=(n&-128)==0?n>>2<<24>>24:(n>>2^192)<<24>>24,O=(t&-128)==0?t>>4<<24>>24:(t>>4^240)<<24>>24,I=(i&-128)==0?i>>6<<24>>24:(i>>6^252)<<24>>24,c[o++]=t0[A],c[o++]=t0[O|h<<4],c[o++]=t0[b<<2|I],c[o++]=t0[i&63];return l==8?(n=e[r],h=(n&3)<<24>>24,A=(n&-128)==0?n>>2<<24>>24:(n>>2^192)<<24>>24,c[o++]=t0[A],c[o++]=t0[h<<4],c[o++]=61,c[o++]=61):l==16&&(n=e[r],t=e[r+1],b=(t&15)<<24>>24,h=(n&3)<<24>>24,A=(n&-128)==0?n>>2<<24>>24:(n>>2^192)<<24>>24,O=(t&-128)==0?t>>4<<24>>24:(t>>4^240)<<24>>24,c[o++]=t0[A],c[o++]=t0[O|h<<4],c[o++]=t0[b<<2],c[o++]=61),gh(c,0,c.length)}function eRn(e,n){var t,i,r,c,o,l,f;if(e.e==0&&e.p>0&&(e.p=-(e.p-1)),e.p>Xr&&Fae(n,e.p-K0),o=n.q.getDate(),GC(n,1),e.k>=0&&O4n(n,e.k),e.c>=0?GC(n,e.c):e.k>=0?(f=new p1e(n.q.getFullYear()-K0,n.q.getMonth(),35),i=35-f.q.getDate(),GC(n,k.Math.min(i,o))):GC(n,o),e.f<0&&(e.f=n.q.getHours()),e.b>0&&e.f<12&&(e.f+=12),zwn(n,e.f==24&&e.g?0:e.f),e.j>=0&&s9n(n,e.j),e.n>=0&&k9n(n,e.n),e.i>=0&&iCe(n,mc(ac(BO(Pu(n.q.getTime()),Rd),Rd),e.i)),e.a&&(r=new t$,Fae(r,r.q.getFullYear()-K0-80),JX(Pu(n.q.getTime()),Pu(r.q.getTime()))&&Fae(n,r.q.getFullYear()-K0+100)),e.d>=0){if(e.c==-1)t=(7+e.d-n.q.getDay())%7,t>3&&(t-=7),l=n.q.getMonth(),GC(n,n.q.getDate()+t),n.q.getMonth()!=l&&GC(n,n.q.getDate()+(t>0?-7:7));else if(n.q.getDay()!=e.d)return!1}return e.o>Xr&&(c=n.q.getTimezoneOffset(),iCe(n,mc(Pu(n.q.getTime()),(e.o-c)*60*Rd))),!0}function TVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te;if(r=T(n,(me(),wi)),!!X(r,206)){for(A=u(r,26),O=n.e,y=new wc(n.c),c=n.d,y.a+=c.b,y.b+=c.d,te=u(ye(A,(Ne(),vH)),182),cs(te,(_s(),lG))&&(S=u(ye(A,l4e),104),QU(S,c.a),nX(S,c.d),WU(S,c.b),ZU(S,c.c)),t=new Ce,b=new L(n.a);b.ai.c.length-1;)Te(i,new jc(mv,Fpe));t=u(T(r,Nh),15).a,x1(u(T(e,mp),86))?(r.e.ane(re((vn(t,i.c.length),u(i.c[t],49)).b))&&FT((vn(t,i.c.length),u(i.c[t],49)),r.e.a+r.f.a)):(r.e.bne(re((vn(t,i.c.length),u(i.c[t],49)).b))&&FT((vn(t,i.c.length),u(i.c[t],49)),r.e.b+r.f.b))}for(c=St(e.b,0);c.b!=c.d.c;)r=u(jt(c),40),t=u(T(r,(Mu(),Nh)),15).a,he(r,(Ti(),Ea),re((vn(t,i.c.length),u(i.c[t],49)).a)),he(r,Yf,re((vn(t,i.c.length),u(i.c[t],49)).b));n.Ug()}function tRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O;for(e.o=ne(re(T(e.i,(Ne(),xg)))),e.f=ne(re(T(e.i,ub))),e.j=e.i.b.c.length,l=e.j-1,y=0,e.k=0,e.n=0,e.b=$f(oe(jr,Se,15,e.j,0,1)),e.c=$f(oe(gr,Se,346,e.j,7,1)),o=new L(e.i.b);o.a0&&Te(e.q,b),Te(e.p,b);n-=i,S=f+n,h+=n*e.f,ol(e.b,l,ve(S)),ol(e.c,l,h),e.k=k.Math.max(e.k,S),e.n=k.Math.max(e.n,h),e.e+=n,n+=O}}function NVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;if(n.b!=0){for(S=new Si,l=null,A=null,i=sc(k.Math.floor(k.Math.log(n.b)*k.Math.LOG10E)+1),f=0,V=St(n,0);V.b!=V.d.c;)for(R=u(jt(V),40),ue(A)!==ue(T(R,(Ti(),Ix)))&&(A=Pt(T(R,Ix)),f=0),A!=null?l=A+dLe(f++,i):l=dLe(f++,i),he(R,Ix,l),I=(r=St(new S1(R).a.d,0),new E3(r));YT(I.a);)O=u(jt(I.a),65).c,Xi(S,O,S.c.b,S.c),he(O,Ix,l);for(y=new pt,o=0;o0&&(V-=S),pge(o,V),b=0,y=new L(o.a);y.a0),l.a.Xb(l.c=--l.b)),f=.4*i*b,!c&&l.b0&&(f=(Yn(0,n.length),n.charCodeAt(0)),f!=64)){if(f==37&&(p=n.lastIndexOf("%"),h=!1,p!=0&&(p==y-1||(h=(Yn(p+1,n.length),n.charCodeAt(p+1)==46))))){if(o=(Yr(1,p,n.length),n.substr(1,p-1)),V=bn("%",o)?null:Cge(o),i=0,h)try{i=hl((Yn(p+2,n.length+1),n.substr(p+2)),Xr,oi)}catch(te){throw te=sr(te),X(te,131)?(l=te,$(new uB(l))):$(te)}for(I=Uhe(e.Dh());I.Ob();)if(A=OB(I),X(A,504)&&(r=u(A,587),q=r.d,(V==null?q==null:bn(V,q))&&i--==0))return r;return null}if(b=n.lastIndexOf("."),S=b==-1?n:(Yr(0,b,n.length),n.substr(0,b)),t=0,b!=-1)try{t=hl((Yn(b+1,n.length+1),n.substr(b+1)),Xr,oi)}catch(te){if(te=sr(te),X(te,131))S=n;else throw $(te)}for(S=bn("%",S)?null:Cge(S),O=Uhe(e.Dh());O.Ob();)if(A=OB(O),X(A,197)&&(c=u(A,197),R=c.ve(),(S==null?R==null:bn(S,R))&&t--==0))return c;return null}return wVe(e,n)}function lRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q;for(b=new pt,f=new Tw,i=new L(e.a.a.b);i.an.d.c){if(S=e.c[n.a.d],I=e.c[p.a.d],S==I)continue;Hf(Nf(Of(Df(Cf(new tf,1),100),S),I))}}}}}function fRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;if(y=u(u(pi(e.r,n),22),83),n==(De(),et)||n==Kn){xVe(e,n);return}for(c=n==Xn?(Lw(),UN):(Lw(),XN),te=n==Xn?(Uo(),ka):(Uo(),Xf),t=u(zc(e.b,n),127),i=t.i,r=i.c+q3(z(B(Jr,1),Jc,30,15,[t.n.b,e.C.b,e.k])),R=i.c+i.b-q3(z(B(Jr,1),Jc,30,15,[t.n.c,e.C.c,e.k])),o=$oe(Vle(c),e.t),q=n==Xn?Dr:Ki,p=y.Jc();p.Ob();)h=u(p.Pb(),115),!(!h.c||h.c.d.c.length<=0)&&(I=h.b.Kf(),O=h.e,S=h.c,A=S.i,A.b=(f=S.n,S.e.a+f.b+f.c),A.a=(l=S.n,S.e.b+l.d+l.a),FC(te,ewe),S.f=te,ba(S,(ws(),Uf)),A.c=O.a-(A.b-I.a)/2,be=k.Math.min(r,O.a),fe=k.Math.max(R,O.a+I.a),A.cfe&&(A.c=fe-A.b),Te(o.d,new fV(A,U1e(o,A))),q=n==Xn?k.Math.max(q,O.b+h.b.Kf().b):k.Math.min(q,O.b));for(q+=n==Xn?e.t:-e.t,V=ade((o.e=q,o)),V>0&&(u(zc(e.b,n),127).a.b=V),b=y.Jc();b.Ob();)h=u(b.Pb(),115),!(!h.c||h.c.d.c.length<=0)&&(A=h.c.i,A.c-=h.e.a,A.d-=h.e.b)}function aRn(e,n){XW();var t,i,r,c,o,l,f,h,b,p,y,S,A,O;if(f=ao(e,0)<0,f&&(e=Td(e)),ao(e,0)==0)switch(n){case 0:return"0";case 1:return R8;case 2:return"0.00";case 3:return"0.000";case 4:return"0.0000";case 5:return"0.00000";case 6:return"0.000000";default:return S=new p0,n<0?S.a+="0E+":S.a+="0E",S.a+=n==Xr?"2147483648":""+-n,S.a}b=18,p=oe(Wl,kh,30,b+1,15,1),t=b,O=e;do h=O,O=BO(O,10),p[--t]=Rt(mc(48,lf(h,ac(O,10))))&yr;while(ao(O,0)!=0);if(r=lf(lf(lf(b,t),n),1),n==0)return f&&(p[--t]=45),gh(p,t,b-t);if(n>0&&ao(r,-6)>=0){if(ao(r,0)>=0){for(c=t+Rt(r),l=b-1;l>=c;l--)p[l+1]=p[l];return p[++c]=46,f&&(p[--t]=45),gh(p,t,b-t+1)}for(o=2;JX(o,mc(Td(r),1));o++)p[--t]=48;return p[--t]=46,p[--t]=48,f&&(p[--t]=45),gh(p,t,b-t)}return A=t+1,i=b,y=new o4,f&&(y.a+="-"),i-A>=1?(Fb(y,p[t]),y.a+=".",y.a+=gh(p,t+1,b-t-1)):y.a+=gh(p,t,b-t),y.a+="E",ao(r,0)>0&&(y.a+="+"),y.a+=""+rE(r),y.a}function DVe(e){r2(e,new Jw(GP(t2(Zp(n2(e2(new dd,Gl),"ELK Radial"),'A radial layout provider which is based on the algorithm of Peter Eades published in "Drawing free trees.", published by International Institute for Advanced Study of Social Information Science, Fujitsu Limited in 1991. The radial layouter takes a tree and places the nodes in radial order around the root. The nodes of the same tree level are placed on the same radius.'),new OM),Gl))),Ee(e,Gl,CF,_e(eln)),Ee(e,Gl,em,_e(nln)),Ee(e,Gl,wv,_e(Ysn)),Ee(e,Gl,dy,_e(Qsn)),Ee(e,Gl,hy,_e(Wsn)),Ee(e,Gl,U8,_e(Vsn)),Ee(e,Gl,ES,_e(Vye)),Ee(e,Gl,X8,_e(Zsn)),Ee(e,Gl,Zee,_e(Dre)),Ee(e,Gl,Wee,_e(Ire)),Ee(e,Gl,LF,_e(Qye)),Ee(e,Gl,ene,_e(_re)),Ee(e,Gl,nne,_e(Wye)),Ee(e,Gl,c2e,_e(Zye)),Ee(e,Gl,r2e,_e(Yye)),Ee(e,Gl,e2e,_e(FH)),Ee(e,Gl,n2e,_e(JH)),Ee(e,Gl,t2e,_e(SD)),Ee(e,Gl,i2e,_e(e6e)),Ee(e,Gl,Zpe,_e(Kye))}function Xw(e,n,t,i,r){var c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;if(I=new je(e.g,e.f),O=R0e(e),O.a=k.Math.max(O.a,n),O.b=k.Math.max(O.b,t),fe=O.a/I.a,b=O.b/I.b,te=O.a-I.a,f=O.b-I.b,i)for(o=Bi(e)?u(ye(Bi(e),(Xt(),Mg)),86):u(ye(e,(Xt(),Mg)),86),l=ue(ye(e,(Xt(),Kx)))===ue((Br(),to)),q=new ut((!e.c&&(e.c=new pe($s,e,9,9)),e.c));q.e!=q.i.gc();)switch(R=u(ft(q),125),V=u(ye(R,Qv),64),V==(De(),ku)&&(V=uge(R,o),ji(R,Qv,V)),V.g){case 1:l||Os(R,R.i*fe);break;case 2:Os(R,R.i+te),l||Ns(R,R.j*b);break;case 3:l||Os(R,R.i*fe),Ns(R,R.j+f);break;case 4:l||Ns(R,R.j*b)}if(ww(e,O.a,O.b),r)for(y=new ut((!e.n&&(e.n=new pe(ju,e,1,7)),e.n));y.e!=y.i.gc();)p=u(ft(y),157),S=p.i+p.g/2,A=p.j+p.f/2,be=S/I.a,h=A/I.b,be+h>=1&&(be-h>0&&A>=0?(Os(p,p.i+te),Ns(p,p.j+f*h)):be-h<0&&S>=0&&(Os(p,p.i+te*be),Ns(p,p.j+f)));return ji(e,(Xt(),Tg),(Vs(),c=u(sa(tA),10),new _l(c,u(_f(c,c.length),10),0))),new je(fe,b)}function Qz(e){var n,t,i,r,c,o,l,f,h,b,p;if(e==null)throw $(new sh(Vo));if(h=e,c=e.length,f=!1,c>0&&(n=(Yn(0,e.length),e.charCodeAt(0)),(n==45||n==43)&&(e=(Yn(1,e.length+1),e.substr(1)),--c,f=n==45)),c==0)throw $(new sh(Yw+h+'"'));for(;e.length>0&&(Yn(0,e.length),e.charCodeAt(0)==48);)e=(Yn(1,e.length+1),e.substr(1)),--c;if(c>(aKe(),nnn)[10])throw $(new sh(Yw+h+'"'));for(r=0;r0&&(p=-parseInt((Yr(0,i,e.length),e.substr(0,i)),10),e=(Yn(i,e.length+1),e.substr(i)),c-=i,t=!1);c>=o;){if(i=parseInt((Yr(0,o,e.length),e.substr(0,o)),10),e=(Yn(o,e.length+1),e.substr(o)),c-=o,t)t=!1;else{if(ao(p,l)<0)throw $(new sh(Yw+h+'"'));p=ac(p,b)}p=lf(p,i)}if(ao(p,0)>0)throw $(new sh(Yw+h+'"'));if(!f&&(p=Td(p),ao(p,0)<0))throw $(new sh(Yw+h+'"'));return p}function Cge(e){ZW();var n,t,i,r,c,o,l,f;if(e==null)return null;if(r=lh(e,Xo(37)),r<0)return e;for(f=new il((Yr(0,r,e.length),e.substr(0,r))),n=oe(ds,kv,30,4,15,1),l=0,i=0,o=e.length;rr+2&&WY((Yn(r+1,e.length),e.charCodeAt(r+1)),G8e,q8e)&&WY((Yn(r+2,e.length),e.charCodeAt(r+2)),G8e,q8e))if(t=P3n((Yn(r+1,e.length),e.charCodeAt(r+1)),(Yn(r+2,e.length),e.charCodeAt(r+2))),r+=2,i>0?(t&192)==128?n[l++]=t<<24>>24:i=0:t>=128&&((t&224)==192?(n[l++]=t<<24>>24,i=2):(t&240)==224?(n[l++]=t<<24>>24,i=3):(t&248)==240&&(n[l++]=t<<24>>24,i=4)),i>0){if(l==i){switch(l){case 2:{Fb(f,((n[0]&31)<<6|n[1]&63)&yr);break}case 3:{Fb(f,((n[0]&15)<<12|(n[1]&63)<<6|n[2]&63)&yr);break}}l=0,i=0}}else{for(c=0;c=2){if((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i==0)t=(v0(),r=new yo,r),Et((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),t);else if((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i>1)for(y=new p4((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a));y.e!=y.i.gc();)KE(y);sge(n,u(K((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),0),170))}if(p)for(i=new ut((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a));i.e!=i.i.gc();)for(t=u(ft(i),170),h=new ut((!t.a&&(t.a=new mr(kl,t,5)),t.a));h.e!=h.i.gc();)f=u(ft(h),372),l.a=k.Math.max(l.a,f.a),l.b=k.Math.max(l.b,f.b);for(o=new ut((!e.n&&(e.n=new pe(ju,e,1,7)),e.n));o.e!=o.i.gc();)c=u(ft(o),157),b=u(ye(c,Yx),8),b&&Dl(c,b.a,b.b),p&&(l.a=k.Math.max(l.a,c.i+c.g),l.b=k.Math.max(l.b,c.j+c.f));return l}function _Ve(e,n,t,i,r){var c,o,l;if(ARe(e,n),o=n[0],c=ic(t.c,0),l=-1,j1e(t))if(i>0){if(o+i>e.length)return!1;l=Az((Yr(0,o+i,e.length),e.substr(0,o+i)),n)}else l=Az(e,n);switch(c){case 71:return l=uv(e,o,z(B(ze,1),Se,2,6,[mYe,vYe]),n),r.e=l,!0;case 77:return NDn(e,n,r,l,o);case 76:return DDn(e,n,r,l,o);case 69:return _Tn(e,n,o,r);case 99:return LTn(e,n,o,r);case 97:return l=uv(e,o,z(B(ze,1),Se,2,6,["AM","PM"]),n),r.b=l,!0;case 121:return IDn(e,n,o,l,t,r);case 100:return l<=0?!1:(r.c=l,!0);case 83:return l<0?!1:hEn(l,o,n[0],r);case 104:l==12&&(l=0);case 75:case 72:return l<0?!1:(r.f=l,r.g=!1,!0);case 107:return l<0?!1:(r.f=l,r.g=!0,!0);case 109:return l<0?!1:(r.j=l,!0);case 115:return l<0?!1:(r.n=l,!0);case 90:if(ocn[f]&&(I=f),p=new L(e.a.b);p.a=l){at(q.b>0),q.a.Xb(q.c=--q.b);break}else I.a>f&&(i?(Er(i.b,I.b),i.a=k.Math.max(i.a,I.a),As(q)):(Te(I.b,b),I.c=k.Math.min(I.c,f),I.a=k.Math.max(I.a,l),i=I));i||(i=new axe,i.c=f,i.a=l,d2(q,i),Te(i.b,b))}for(o=e.b,h=0,R=new L(t);R.a1;){if(r=SNn(n),p=c.g,A=u(ye(n,Bx),104),O=ne(re(ye(n,UH))),(!n.a&&(n.a=new pe(Jt,n,10,11)),n.a).i>1&&ne(re(ye(n,(Yh(),Hre))))!=Ki&&(c.c+(A.b+A.c))/(c.b+(A.d+A.a))1&&ne(re(ye(n,(Yh(),Jre))))!=Ki&&(c.c+(A.b+A.c))/(c.b+(A.d+A.a))>O&&ji(r,(Yh(),Mm),k.Math.max(ne(re(ye(n,Rx))),ne(re(ye(r,Mm)))-ne(re(ye(n,Jre))))),S=new Ose(i,b),f=QVe(S,r,y),h=f.g,h>=p&&h==h){for(o=0;o<(!r.a&&(r.a=new pe(Jt,r,10,11)),r.a).i;o++)Sqe(e,u(K((!r.a&&(r.a=new pe(Jt,r,10,11)),r.a),o),26),u(K((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a),o),26));QRe(n,S),p4n(c,f.c),w4n(c,f.b)}--l}ji(n,(Yh(),P7),c.b),ji(n,Py,c.c),t.Ug()}function wRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn;for(n.Tg("Compound graph postprocessor",1),t=Re($e(T(e,(Ne(),Jie)))),l=u(T(e,(me(),qve)),229),b=new ar,R=l.ec().Jc();R.Ob();){for(I=u(R.Pb(),17),o=new bs(l.cc(I)),jn(),Cr(o,new noe(e)),be=g7n((vn(0,o.c.length),u(o.c[0],250))),Ie=UBe(u(Le(o,o.c.length-1),250)),V=be.i,V9(Ie.i,V)?q=V.e:q=_r(V),p=tSn(I,o),qs(I.a),y=null,c=new L(o);c.aEh,tn=k.Math.abs(y.b-A.b)>Eh,(!t&&cn&&tn||t&&(cn||tn))&&Vt(I.a,te)),fc(I.a,i),i.b==0?y=te:y=(at(i.b!=0),u(i.c.b.c,8)),U7n(S,p,O),UBe(r)==Ie&&(_r(Ie.i)!=r.a&&(O=new Kr,L0e(O,_r(Ie.i),q)),he(I,jie,O)),WMn(S,I,q),b.a.yc(S,b);lc(I,be),Gr(I,Ie)}for(h=b.a.ec().Jc();h.Ob();)f=u(h.Pb(),17),lc(f,null),Gr(f,null);n.Ug()}function pRn(e,n){var t,i,r,c,o,l,f,h,b,p,y;for(r=u(T(e,(Mu(),mp)),86),b=r==(vr(),Zc)||r==ru?Za:ru,t=u(gs(si(new wn(null,new pn(e.b,16)),new b3),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),f=u(gs(So(t.Mc(),new LEe(n)),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[Yo]))),16),f.Fc(u(gs(So(t.Mc(),new PEe(n)),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[Yo]))),18)),f.gd(new $Ee(b)),y=new vd(new REe(r)),i=new pt,l=f.Jc();l.Ob();)o=u(l.Pb(),240),h=u(o.a,40),Re($e(o.c))?(y.a.yc(h,(Pn(),eb))==null,new c9(y.a.Xc(h,!1)).a.gc()>0&&ei(i,h,u(new c9(y.a.Xc(h,!1)).a.Tc(),40)),new c9(y.a.$c(h,!0)).a.gc()>1&&ei(i,nJe(y,h),h)):(new c9(y.a.Xc(h,!1)).a.gc()>0&&(c=u(new c9(y.a.Xc(h,!1)).a.Tc(),40),ue(c)===ue(du(Xc(i.f,h)))&&u(T(h,(Ti(),dre)),16).Ec(c)),new c9(y.a.$c(h,!0)).a.gc()>1&&(p=nJe(y,h),ue(du(Xc(i.f,p)))===ue(h)&&u(T(p,(Ti(),dre)),16).Ec(h)),y.a.Ac(h)!=null)}function LVe(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te;if(e.gc()==1)return u(e.Xb(0),235);if(e.gc()<=0)return new YR;for(r=e.Jc();r.Ob();){for(t=u(r.Pb(),235),A=0,b=oi,p=oi,f=Xr,h=Xr,S=new L(t.e);S.al&&(V=0,te+=o+R,o=0),qIn(O,t,V,te),n=k.Math.max(n,V+I.a),o=k.Math.max(o,I.b),V+=I.a+R;return O}function mRn(e){cge();var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;if(e==null||(c=oB(e),A=djn(c),A%4!=0))return null;if(O=A/4|0,O==0)return oe(ds,kv,30,0,15,1);for(p=null,n=0,t=0,i=0,r=0,o=0,l=0,f=0,h=0,S=0,y=0,b=0,p=oe(ds,kv,30,O*3,15,1);S>4)<<24>>24,p[y++]=((t&15)<<4|i>>2&15)<<24>>24,p[y++]=(i<<6|r)<<24>>24}return!ZT(o=c[b++])||!ZT(l=c[b++])?null:(n=rh[o],t=rh[l],f=c[b++],h=c[b++],rh[f]==-1||rh[h]==-1?f==61&&h==61?(t&15)!=0?null:(I=oe(ds,kv,30,S*3+1,15,1),Wu(p,0,I,0,S*3),I[y]=(n<<2|t>>4)<<24>>24,I):f!=61&&h==61?(i=rh[f],(i&3)!=0?null:(I=oe(ds,kv,30,S*3+2,15,1),Wu(p,0,I,0,S*3),I[y++]=(n<<2|t>>4)<<24>>24,I[y]=((t&15)<<4|i>>2&15)<<24>>24,I)):null:(i=rh[f],r=rh[h],p[y++]=(n<<2|t>>4)<<24>>24,p[y++]=((t&15)<<4|i>>2&15)<<24>>24,p[y++]=(i<<6|r)<<24>>24,p))}function vRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be;for(n.Tg(SQe,1),A=u(T(e,(Ne(),Y1)),222),r=new L(e.b);r.a=2){for(O=!0,y=new L(c.j),t=u(_(y),12),S=null;y.a0)if(i=p.gc(),h=sc(k.Math.floor((i+1)/2))-1,r=sc(k.Math.ceil((i+1)/2))-1,n.o==Ya)for(b=r;b>=h;b--)n.a[te.p]==te&&(O=u(p.Xb(b),49),A=u(O.a,9),!rf(t,O.b)&&S>e.b.e[A.p]&&(n.a[A.p]=te,n.g[te.p]=n.g[A.p],n.a[te.p]=n.g[te.p],n.f[n.g[te.p].p]=(Pn(),!!(Re(n.f[n.g[te.p].p])&te.k==(zn(),dr))),S=e.b.e[A.p]));else for(b=h;b<=r;b++)n.a[te.p]==te&&(R=u(p.Xb(b),49),I=u(R.a,9),!rf(t,R.b)&&S0&&(r=u(Le(I.c.a,fe-1),9),o=e.i[r.p],cn=k.Math.ceil(L3(e.n,r,I)),c=be.a.e-I.d.d-(o.a.e+r.o.b+r.d.a)-cn),h=Ki,fe0&&Ie.a.e.e-Ie.a.a-(Ie.b.e.e-Ie.b.a)<0,A=V.a.e.e-V.a.a-(V.b.e.e-V.b.a)<0&&Ie.a.e.e-Ie.a.a-(Ie.b.e.e-Ie.b.a)>0,S=V.a.e.e+V.b.aIe.b.e.e+Ie.a.a,te=0,!O&&!A&&(y?c+p>0?te=p:h-i>0&&(te=i):S&&(c+l>0?te=l:h-q>0&&(te=q))),be.a.e+=te,be.b&&(be.d.e+=te),!1))}function $Ve(e,n,t){var i,r,c,o,l,f,h,b,p,y;if(i=new Lf(n.Jf().a,n.Jf().b,n.Kf().a,n.Kf().b),r=new g4,e.c)for(o=new L(n.Pf());o.a0&&Or(S,(vn(t,n.c.length),u(n.c[t],25))),c=0,y=!0,R=Ks(qb(rr(S))),f=R.Jc();f.Ob();){for(l=u(f.Pb(),17),y=!1,p=l,h=0;h(vn(h,n.c.length),u(n.c[h],25)).a.c.length?Or(r,(vn(h,n.c.length),u(n.c[h],25))):z0(r,i+c,(vn(h,n.c.length),u(n.c[h],25))),p=CW(p,r);t>0&&(c+=1)}if(y){for(h=0;h(vn(h,n.c.length),u(n.c[h],25)).a.c.length?Or(r,(vn(h,n.c.length),u(n.c[h],25))):z0(r,i+c,(vn(h,n.c.length),u(n.c[h],25)));t>0&&(c+=1)}for(o=!1,O=new qn(Vn(Ni(S).a.Jc(),new ee));ht(O);){for(A=u(tt(O),17),p=A,b=t+1;b(vn(h,n.c.length),u(n.c[h],25)).a.c.length?Or(I,(vn(h,n.c.length),u(n.c[h],25))):z0(I,i+1,(vn(h,n.c.length),u(n.c[h],25))));o&&(c+=1),o=!0}return c>0?c-1:0}function q0(e,n){fi();var t,i,r,c,o,l,f,h,b,p,y,S,A;if(xj(K7)==0){for(p=oe(WBn,Se,121,jhn.length,0,1),o=0;oh&&(i.a+=HCe(oe(Wl,kh,30,-h,15,1))),i.a+="Is",lh(f,Xo(32))>=0)for(r=0;r=i.o.b/2}else q=!p;q?(R=u(T(i,(me(),Dy)),16),R?y?c=R:(r=u(T(i,xy),16),r?R.gc()<=r.gc()?c=R:c=r:(c=new Ce,he(i,xy,c))):(c=new Ce,he(i,Dy,c))):(r=u(T(i,(me(),xy)),16),r?p?c=r:(R=u(T(i,Dy),16),R?r.gc()<=R.gc()?c=r:c=R:(c=new Ce,he(i,Dy,c))):(c=new Ce,he(i,xy,c))),c.Ec(e),he(e,(me(),eH),t),n.d==t?(Gr(n,null),t.e.c.length+t.g.c.length==0&&gu(t,null),vkn(t)):(lc(n,null),t.e.c.length+t.g.c.length==0&&gu(t,null)),qs(n.a)}function SRn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn,Dn,ot,Qt,qi;for(t.Tg("MinWidth layering",1),S=n.b,Ie=n.a,qi=u(T(n,(Ne(),n4e)),15).a,l=u(T(n,t4e),15).a,e.b=ne(re(T(n,Vf))),e.d=Ki,te=new L(Ie);te.aS&&(c&&(gc(fe,y),gc(cn,ve(h.b-1))),Qt=t.b,qi+=y+n,y=0,b=k.Math.max(b,t.b+t.c+ot)),Os(l,Qt),Ns(l,qi),b=k.Math.max(b,Qt+ot+t.c),y=k.Math.max(y,p),Qt+=ot+n;if(b=k.Math.max(b,i),Dn=qi+y+t.a,Dn0?(h=0,I&&(h+=l),h+=(tn-1)*o,V&&(h+=l),cn&&V&&(h=k.Math.max(h,GNn(V,o,q,Ie))),h=e.a&&(i=uLn(e,q),b=k.Math.max(b,i.b),te=k.Math.max(te,i.d),Te(l,new jc(q,i)));for(cn=new Ce,h=0;h0),I.a.Xb(I.c=--I.b),tn=new Xu(e.b),d2(I,tn),at(I.b0){for(y=b<100?null:new m0(b),h=new t1e(n),A=h.g,R=oe($t,ni,30,b,15,1),i=0,te=new Nw(b),r=0;r=0;)if(S!=null?di(S,A[f]):ue(S)===ue(A[f])){R.length<=i&&(I=R,R=oe($t,ni,30,2*R.length,15,1),Wu(I,0,R,0,i)),R[i++]=r,Et(te,A[f]);break e}if(S=S,ue(S)===ue(l))break}}if(h=te,A=te.g,b=i,i>R.length&&(I=R,R=oe($t,ni,30,i,15,1),Wu(I,0,R,0,i)),i>0){for(V=!0,c=0;c=0;)V4(e,R[o]);if(i!=b){for(r=b;--r>=i;)V4(h,r);I=R,R=oe($t,ni,30,i,15,1),Wu(I,0,R,0,i)}n=h}}}else for(n=sxn(e,n),r=e.i;--r>=0;)n.Gc(e.g[r])&&(V4(e,r),V=!0);if(V){if(R!=null){for(t=n.gc(),p=t==1?dE(e,4,n.Jc().Pb(),null,R[0],O):dE(e,6,n,R,R[0],O),y=t<100?null:new m0(t),r=n.Jc();r.Ob();)S=r.Pb(),y=qle(e,u(S,75),y);y?(y.lj(p),y.mj()):ai(e.e,p)}else{for(y=p2n(n.gc()),r=n.Jc();r.Ob();)S=r.Pb(),y=qle(e,u(S,75),y);y&&y.mj()}return!0}else return!1}function CRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;for(t=new UJe(n),t.a||t_n(n),h=eIn(n),f=new Tw,I=new iXe,O=new L(n.a);O.a0||t.o==Ya&&r=t}function NRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn;for(V=e.a,te=0,be=V.length;te0?(p=u(Le(y.c.a,o-1),9),cn=L3(e.b,y,p),I=y.n.b-y.d.d-(p.n.b+p.o.b+p.d.a+cn)):I=y.n.b-y.d.d,h=k.Math.min(I,h),o1&&(o=k.Math.min(o,k.Math.abs(u(Yu(l.a,1),8).b-b.b)))));else for(O=new L(n.j);O.ar&&(c=y.a-r,o=oi,i.c.length=0,r=y.a),y.a>=r&&(Hn(i.c,l),l.a.b>1&&(o=k.Math.min(o,k.Math.abs(u(Yu(l.a,l.a.b-2),8).b-y.b)))));if(i.c.length!=0&&c>n.o.a/2&&o>n.o.b/2){for(S=new Qu,gu(S,n),xr(S,(De(),Xn)),S.n.a=n.o.a/2,R=new Qu,gu(R,n),xr(R,bt),R.n.a=n.o.a/2,R.n.b=n.o.b,f=new L(i);f.a=h.b?lc(l,R):lc(l,S)):(h=u(x3n(l.a),8),I=l.a.b==0?_a(l.c):u(If(l.a),8),I.b>=h.b?Gr(l,R):Gr(l,S)),p=u(T(l,(Ne(),Wc)),78),p&&P2(p,h,!0);n.n.a=r-n.o.a/2}}function _Rn(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(l=St(e.b,0);l.b!=l.d.c;)if(o=u(jt(l),40),!bn(o.c,DF))for(h=sOn(o,e),n==(vr(),Zc)||n==ru?Cr(h,new j_):Cr(h,new tU),f=h.c.length,i=0;i=0?S=q4(l):S=CO(q4(l)),e.of(T7,S)),h=new Kr,y=!1,e.nf(wp)?(wle(h,u(e.mf(wp),8)),y=!0):Ywn(h,o.a/2,o.b/2),S.g){case 4:he(b,yu,(Xs(),V1)),he(b,tH,(Wb(),Ov)),b.o.b=o.b,O<0&&(b.o.a=-O),xr(p,(De(),et)),y||(h.a=o.a),h.a-=o.a;break;case 2:he(b,yu,(Xs(),yg)),he(b,tH,(Wb(),k7)),b.o.b=o.b,O<0&&(b.o.a=-O),xr(p,(De(),Kn)),y||(h.a=0);break;case 1:he(b,mg,(_1(),Dv)),b.o.a=o.a,O<0&&(b.o.b=-O),xr(p,(De(),bt)),y||(h.b=o.b),h.b-=o.b;break;case 3:he(b,mg,(_1(),Sy)),b.o.a=o.a,O<0&&(b.o.b=-O),xr(p,(De(),Xn)),y||(h.b=0)}if(wle(p.n,h),he(b,wp,h),n==Cg||n==f1||n==to){if(A=0,n==Cg&&e.nf(qd))switch(S.g){case 1:case 2:A=u(e.mf(qd),15).a;break;case 3:case 4:A=-u(e.mf(qd),15).a}else switch(S.g){case 4:case 2:A=c.b,n==f1&&(A/=r.b);break;case 1:case 3:A=c.a,n==f1&&(A/=r.a)}he(b,hp,A)}return he(b,Du,S),b}function LRn(){zoe();function e(i){var r=this;this.dispatch=function(c){var o=c.data;switch(o.cmd){case"algorithms":var l=fde((jn(),new Hr(new ct(Ng.b))));i.postMessage({id:o.id,data:l});break;case"categories":var f=fde((jn(),new Hr(new ct(Ng.c))));i.postMessage({id:o.id,data:f});break;case"options":var h=fde((jn(),new Hr(new ct(Ng.d))));i.postMessage({id:o.id,data:h});break;case"register":sPn(o.algorithms),i.postMessage({id:o.id});break;case"layout":nPn(o.graph,o.layoutOptions||{},o.options||{}),i.postMessage({id:o.id,data:o.graph});break}},this.saveDispatch=function(c){try{r.dispatch(c)}catch(o){i.postMessage({id:c.data.id,error:o})}}}function n(i){var r=this;this.dispatcher=new e({postMessage:function(c){r.onmessage({data:c})}}),this.postMessage=function(c){setTimeout(function(){r.dispatcher.saveDispatch({data:c})},0)}}if(typeof document===GZ&&typeof self!==GZ){var t=new e(self);self.onmessage=t.saveDispatch}else typeof M!==GZ&&M.exports&&(Object.defineProperty(N,"__esModule",{value:!0}),M.exports={default:n,Worker:n})}function uZ(e,n,t,i,r,c,o){var l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn,Dn,ot,Qt,qi;for(O=0,Tn=0,h=new L(e.b);h.aO&&(c&&(gc(fe,S),gc(cn,ve(b.b-1)),Te(e.d,A),l.c.length=0),Qt=t.b,qi+=S+n,S=0,p=k.Math.max(p,t.b+t.c+ot)),Hn(l.c,f),zJe(f,Qt,qi),p=k.Math.max(p,Qt+ot+t.c),S=k.Math.max(S,y),Qt+=ot+n,A=f;if(Er(e.a,l),Te(e.d,u(Le(l,l.c.length-1),167)),p=k.Math.max(p,i),Dn=qi+S+t.a,Dnr.d.d+r.d.a?b.f.d=!0:(b.f.d=!0,b.f.a=!0))),i.b!=i.d.c&&(n=t);b&&(c=u(Bn(e.f,o.d.i),60),n.bc.d.d+c.d.a?b.f.d=!0:(b.f.d=!0,b.f.a=!0))}for(l=new qn(Vn(rr(S).a.Jc(),new ee));ht(l);)o=u(tt(l),17),o.a.b!=0&&(n=u(If(o.a),8),o.d.j==(De(),Xn)&&(I=new sS(n,new je(n.a,r.d.d),r,o),I.f.a=!0,I.a=o.d,Hn(O.c,I)),o.d.j==bt&&(I=new sS(n,new je(n.a,r.d.d+r.d.a),r,o),I.f.d=!0,I.a=o.d,Hn(O.c,I)))}return O}function FRn(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(f=new Ce,p=n.length,o=d1e(t),h=0;h=A&&(q>A&&(S.c.length=0,A=q),Hn(S.c,o));S.c.length!=0&&(y=u(Le(S,sz(n,S.c.length)),132),Dn.a.Ac(y)!=null,y.s=O++,mbe(y,tn,fe),S.c.length=0)}for(te=e.c.length+1,l=new L(e);l.aTn.s&&(As(t),qo(Tn.i,i),i.c>0&&(i.a=Tn,Te(Tn.t,i),i.b=Ie,Te(Ie.i,i)))}function HVe(e,n,t,i,r){var c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn,Dn;for(O=new xo(n.b),te=new xo(n.b),y=new xo(n.b),cn=new xo(n.b),I=new xo(n.b),Ie=St(n,0);Ie.b!=Ie.d.c;)for(be=u(jt(Ie),12),l=new L(be.g);l.a0,R=be.g.c.length>0,h&&R?Hn(y.c,be):h?Hn(O.c,be):R&&Hn(te.c,be);for(A=new L(O);A.aq.mh()-h.b&&(y=q.mh()-h.b),S>q.nh()-h.d&&(S=q.nh()-h.d),b0){for(V=St(e.f,0);V.b!=V.d.c;)q=u(jt(V),9),q.p+=y-e.e;_0e(e),qs(e.f),Ibe(e,i,S)}else{for(Vt(e.f,S),S.p=i,e.e=k.Math.max(e.e,i),c=new qn(Vn(rr(S).a.Jc(),new ee));ht(c);)r=u(tt(c),17),!r.c.i.c&&r.c.i.k==(zn(),Uu)&&(Vt(e.f,r.c.i),r.c.i.p=i-1);e.c=i}else _0e(e),qs(e.f),i=0,ht(new qn(Vn(rr(S).a.Jc(),new ee)))?(y=0,y=GJe(y,S),i=y+2,Ibe(e,i,S)):(Vt(e.f,S),S.p=0,e.e=k.Math.max(e.e,0),e.b=u(Le(e.d.b,0),25),e.c=0);for(e.f.b==0||_0e(e),e.d.a.c.length=0,R=new Ce,h=new L(e.d.b);h.a=48&&n<=57){for(i=n-48;r=48&&n<=57;)if(i=i*10+n-48,i<0)throw $(new zt(Ht((Lt(),Z2e))))}else throw $(new zt(Ht((Lt(),DZe))));if(t=i,n==44){if(r>=e.j)throw $(new zt(Ht((Lt(),_Ze))));if((n=ic(e.i,r++))>=48&&n<=57){for(t=n-48;r=48&&n<=57;)if(t=t*10+n-48,t<0)throw $(new zt(Ht((Lt(),Z2e))));if(i>t)throw $(new zt(Ht((Lt(),LZe))))}else t=-1}if(n!=125)throw $(new zt(Ht((Lt(),IZe))));e._l(r)?(c=(fi(),fi(),new A2(9,c)),e.d=r+1):(c=(fi(),fi(),new A2(3,c)),e.d=r),c.Mm(i),c.Lm(t),li(e)}}return c}function XRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be;for(r=1,S=new Ce,i=0;i=u(Le(e.b,i),25).a.c.length/4)continue}if(u(Le(e.b,i),25).a.c.length>n){for(te=new Ce,Te(te,u(Le(e.b,i),25)),o=0;o1)for(A=new p4((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a));A.e!=A.i.gc();)KE(A);for(o=u(K((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),0),170),I=Qt,Qt>be+te?I=be+te:Qtfe+O?R=fe+O:qibe-te&&Ife-O&&RQt+ot?cn=Qt+ot:beqi+Ie?tn=qi+Ie:feQt-ot&&cnqi-Ie&&tnt&&(y=t-1),S=i0+Is(n,24)*vN*p-p/2,S<0?S=1:S>i&&(S=i-1),r=(v0(),f=new Fk,f),bB(r,y),gB(r,S),Et((!o.a&&(o.a=new mr(kl,o,5)),o.a),r)}function lZ(e,n){XW();var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie;if(V=e.e,b=e.d,r=e.a,V==0)switch(n){case 0:return"0";case 1:return R8;case 2:return"0.00";case 3:return"0.000";case 4:return"0.0000";case 5:return"0.00000";case 6:return"0.000000";default:return R=new p0,R.a+="0E",R.a+=-n,R.a}if(O=b*10+1+7,I=oe(Wl,kh,30,O+1,15,1),t=O,b==1)if(c=r[0],c<0){Ie=Rr(c,Ic);do p=Ie,Ie=BO(Ie,10),I[--t]=48+Rt(lf(p,ac(Ie,10)))&yr;while(ao(Ie,0)!=0)}else{Ie=c;do p=Ie,Ie=Ie/10|0,I[--t]=48+(p-Ie*10)&yr;while(Ie!=0)}else{te=oe($t,ni,30,b,15,1),fe=b,Wu(r,0,te,0,fe);e:for(;;){for(q=0,l=fe-1;l>=0;l--)be=mc(Gh(q,32),Rr(te[l],Ic)),S=ZAn(be),te[l]=Rt(S),q=Rt(kw(S,32));A=Rt(q),y=t;do I[--t]=48+A%10&yr;while((A=A/10|0)!=0&&t!=0);for(i=9-y+t,o=0;o0;o++)I[--t]=48;for(f=fe-1;te[f]==0;f--)if(f==0)break e;fe=f+1}for(;I[t]==48;)++t}return h=V<0,h&&(I[--t]=45),gh(I,t,O-t)}function XVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;switch(e.c=n,e.g=new pt,t=(_b(),new w0(e.c)),i=new TP(t),ode(i),V=Pt(ye(e.c,(FO(),q6e))),f=u(ye(e.c,rce),330),be=u(ye(e.c,cce),427),o=u(ye(e.c,J6e),477),te=u(ye(e.c,ice),428),e.j=ne(re(ye(e.c,Gln))),l=e.a,f.g){case 0:l=e.a;break;case 1:l=e.b;break;case 2:l=e.i;break;case 3:l=e.e;break;case 4:l=e.f;break;default:throw $(new Gn($F+(f.f!=null?f.f:""+f.g)))}if(e.d=new O_e(l,be,o),he(e.d,(Q9(),QS),$e(ye(e.c,Jln))),e.d.c=Re($e(ye(e.c,H6e))),TR(e.c).i==0)return e.d;for(p=new ut(TR(e.c));p.e!=p.i.gc();){for(b=u(ft(p),26),S=b.g/2,y=b.f/2,fe=new je(b.i+S,b.j+y);so(e.g,fe);)a2(fe,(k.Math.random()-.5)*Eh,(k.Math.random()-.5)*Eh);O=u(ye(b,(Xt(),R7)),140),I=new U_e(fe,new Lf(fe.a-S-e.j/2-O.b,fe.b-y-e.j/2-O.d,b.g+e.j+(O.b+O.c),b.f+e.j+(O.d+O.a))),Te(e.d.i,I),ei(e.g,fe,new jc(I,b))}switch(te.g){case 0:if(V==null)e.d.d=u(Le(e.d.i,0),68);else for(q=new L(e.d.i);q.a0?ot+1:1);for(o=new L(fe.g);o.a0?ot+1:1)}e.d[h]==0?Vt(e.f,O):e.a[h]==0&&Vt(e.g,O),++h}for(A=-1,S=1,p=new Ce,e.e=u(T(n,(me(),Oy)),234);jl>0;){for(;e.f.b!=0;)qi=u(eV(e.f),9),e.c[qi.p]=A--,Ybe(e,qi),--jl;for(;e.g.b!=0;)Es=u(eV(e.g),9),e.c[Es.p]=S++,Ybe(e,Es),--jl;if(jl>0){for(y=Xr,q=new L(V);q.a=y&&(te>y&&(p.c.length=0,y=te),Hn(p.c,O)));b=e.qg(p),e.c[b.p]=S++,Ybe(e,b),--jl}}for(Qt=V.c.length+1,h=0;he.c[eu]&&($d(i,!0),he(n,Ay,(Pn(),!0)));e.a=null,e.d=null,e.c=null,qs(e.g),qs(e.f),t.Ug()}function VVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;for(be=u(K((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),0),170),b=new xs,te=new pt,fe=sKe(be),Ko(te.f,be,fe),y=new pt,i=new Si,A=qh(Rl(z(B(Xl,1),On,20,0,[(!n.d&&(n.d=new Nn(pr,n,8,5)),n.d),(!n.e&&(n.e=new Nn(pr,n,7,4)),n.e)])));ht(A);){if(S=u(tt(A),85),(!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i!=1)throw $(new Gn(zWe+(!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i));S!=e&&(I=u(K((!S.a&&(S.a=new pe(Pi,S,6,6)),S.a),0),170),Xi(i,I,i.c.b,i.c),O=u(du(Xc(te.f,I)),13),O||(O=sKe(I),Ko(te.f,I,O)),p=t?Nr(new wc(u(Le(fe,fe.c.length-1),8)),u(Le(O,O.c.length-1),8)):Nr(new wc((vn(0,fe.c.length),u(fe.c[0],8))),(vn(0,O.c.length),u(O.c[0],8))),Ko(y.f,I,p))}if(i.b!=0)for(R=u(Le(fe,t?fe.c.length-1:0),8),h=1;h1&&Xi(b,R,b.c.b,b.c),CY(r)));R=q}return b}function YVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn;for(t.Tg(QQe,1),Tn=u(gs(si(new wn(null,new pn(n,16)),new S_),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),b=u(gs(si(new wn(null,new pn(n,16)),new zEe(n)),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[Yo]))),16),A=u(gs(si(new wn(null,new pn(n,16)),new BEe(n)),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[Yo]))),16),O=oe(_H,IF,40,n.gc(),0,1),o=0;o=0&&tn=0&&!O[S]){O[S]=r,b.ed(l),--l;break}if(S=tn-y,S=0&&!O[S]){O[S]=r,b.ed(l),--l;break}}for(A.gd(new TM),f=O.length-1;f>=0;f--)!O[f]&&!A.dc()&&(O[f]=u(A.Xb(0),40),A.ed(0));for(h=0;hy&&$O((vn(y,n.c.length),u(n.c[y],186)),b),b=null;n.c.length>y&&(vn(y,n.c.length),u(n.c[y],186)).a.c.length==0;)qo(n,(vn(y,n.c.length),n.c[y]));if(!b){--o;continue}if(!Re($e(u(Le(b.b,0),26).mf((Ja(),AD))))&&g_n(n,A,c,b,I,t,y,i)){O=!0;continue}if(I){if(S=A.b,p=b.f,!Re($e(u(Le(b.b,0),26).mf(AD)))&&PPn(n,A,c,b,t,y,i,r)){if(O=!0,S=e.j){e.a=-1,e.c=1;return}if(n=ic(e.i,e.d++),e.a=n,e.b==1){switch(n){case 92:if(i=10,e.d>=e.j)throw $(new zt(Ht((Lt(),qF))));e.a=ic(e.i,e.d++);break;case 45:(e.e&512)==512&&e.d=e.j||ic(e.i,e.d)!=63)break;if(++e.d>=e.j)throw $(new zt(Ht((Lt(),One))));switch(n=ic(e.i,e.d++),n){case 58:i=13;break;case 61:i=14;break;case 33:i=15;break;case 91:i=19;break;case 62:i=18;break;case 60:if(e.d>=e.j)throw $(new zt(Ht((Lt(),One))));if(n=ic(e.i,e.d++),n==61)i=16;else if(n==33)i=17;else throw $(new zt(Ht((Lt(),bZe))));break;case 35:for(;e.d=e.j)throw $(new zt(Ht((Lt(),qF))));e.a=ic(e.i,e.d++);break;default:i=0}e.c=i}function iBn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I;if(t.Tg("Process compaction",1),!!Re($e(T(n,(Mu(),Sye))))){for(r=u(T(n,mp),86),S=ne(re(T(n,kre))),TLn(e,n,r),pRn(n,S/2/2),A=n.b,Vb(A,new DEe(r)),h=St(A,0);h.b!=h.d.c;)if(f=u(jt(h),40),!Re($e(T(f,(Ti(),fb))))){if(i=nIn(f,r),O=Y_n(f,n),p=0,y=0,i)switch(I=i.e,r.g){case 2:p=I.a-S-f.f.a,O.e.a-S-f.f.ap&&(p=O.e.a+O.f.a+S),y=p+f.f.a;break;case 4:p=I.b-S-f.f.b,O.e.b-S-f.f.bp&&(p=O.e.b+O.f.b+S),y=p+f.f.b}else if(O)switch(r.g){case 2:p=O.e.a-S-f.f.a,y=p+f.f.a;break;case 1:p=O.e.a+O.f.a+S,y=p+f.f.a;break;case 4:p=O.e.b-S-f.f.b,y=p+f.f.b;break;case 3:p=O.e.b+O.f.b+S,y=p+f.f.b}ue(T(n,yre))===ue((NE(),yD))?(c=p,o=y,l=R1(si(new wn(null,new pn(e.a,16)),new bTe(c,o))),l.a!=null?r==(vr(),Zc)||r==ru?f.e.a=p:f.e.b=p:(r==(vr(),Zc)||r==Vl?l=R1(si(eBe(new wn(null,new pn(e.a,16))),new IEe(c))):l=R1(si(eBe(new wn(null,new pn(e.a,16))),new _Ee(c))),l.a!=null&&(r==Zc||r==ru?f.e.a=ne(re((at(l.a!=null),u(l.a,49)).a)):f.e.b=ne(re((at(l.a!=null),u(l.a,49)).a)))),l.a!=null&&(b=wu(e.a,(at(l.a!=null),l.a),0),b>0&&b!=u(T(f,Nh),15).a&&(he(f,wye,(Pn(),!0)),he(f,Nh,ve(b))))):r==(vr(),Zc)||r==ru?f.e.a=p:f.e.b=p}t.Ug()}}function rBn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be;if(t.Tg("Coffman-Graham Layering",1),n.a.c.length==0){t.Ug();return}for(be=u(T(n,(Ne(),e4e)),15).a,f=0,o=0,y=new L(n.a);y.a=be||!vEn(R,i))&&(i=$Ie(n,b)),Or(R,i),c=new qn(Vn(rr(R).a.Jc(),new ee));ht(c);)r=u(tt(c),17),!e.a[r.p]&&(O=r.c.i,--e.e[O.p],e.e[O.p]==0&&E4(v8(S,O),B8));for(h=b.c.length-1;h>=0;--h)Te(n.b,(vn(h,b.c.length),u(b.c[h],25)));n.a.c.length=0,t.Ug()}function WVe(e){var n,t,i,r,c,o,l,f,h;for(e.b=1,li(e),n=null,e.c==0&&e.a==94?(li(e),n=(fi(),fi(),new ul(4)),ho(n,0,l7),l=new ul(4)):l=(fi(),fi(),new ul(4)),r=!0;(h=e.c)!=1;){if(h==0&&e.a==93&&!r){n&&(dS(n,l),l=n);break}if(t=e.a,i=!1,h==10)switch(t){case 100:case 68:case 119:case 87:case 115:case 83:V2(l,T8(t)),i=!0;break;case 105:case 73:case 99:case 67:t=(V2(l,T8(t)),-1),t<0&&(i=!0);break;case 112:case 80:if(f=Q0e(e,t),!f)throw $(new zt(Ht((Lt(),Nne))));V2(l,f),i=!0;break;default:t=Lbe(e)}else if(h==24&&!r){if(n&&(dS(n,l),l=n),c=WVe(e),dS(l,c),e.c!=0||e.a!=93)throw $(new zt(Ht((Lt(),SZe))));break}if(li(e),!i){if(h==0){if(t==91)throw $(new zt(Ht((Lt(),Q2e))));if(t==93)throw $(new zt(Ht((Lt(),W2e))));if(t==45&&!r&&e.a!=93)throw $(new zt(Ht((Lt(),Dne))))}if(e.c!=0||e.a!=45||t==45&&r)ho(l,t,t);else{if(li(e),(h=e.c)==1)throw $(new zt(Ht((Lt(),UF))));if(h==0&&e.a==93)ho(l,t,t),ho(l,45,45);else{if(h==0&&e.a==93||h==24)throw $(new zt(Ht((Lt(),Dne))));if(o=e.a,h==0){if(o==91)throw $(new zt(Ht((Lt(),Q2e))));if(o==93)throw $(new zt(Ht((Lt(),W2e))));if(o==45)throw $(new zt(Ht((Lt(),Dne))))}else h==10&&(o=Lbe(e));if(li(e),t>o)throw $(new zt(Ht((Lt(),MZe))));ho(l,t,o)}}}r=!1}if(e.c==1)throw $(new zt(Ht((Lt(),UF))));return ov(l),aS(l),e.b=0,li(e),l}function ZVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te;te=!1;do for(te=!1,c=n?new nt(e.a.b).a.gc()-2:1;n?c>=0:cu(T(I,Ci),15).a)&&(V=!1);if(V){for(f=n?c+1:c-1,l=Pae(e.a,ve(f)),o=!1,q=!0,i=!1,b=St(l,0);b.b!=b.d.c;)h=u(jt(b),9),bi(h,Ci)?h.p!=p.p&&(o=o|(n?u(T(h,Ci),15).au(T(p,Ci),15).a),q=!1):!o&&q&&h.k==(zn(),Uu)&&(i=!0,n?y=u(tt(new qn(Vn(rr(h).a.Jc(),new ee))),17).c.i:y=u(tt(new qn(Vn(Ni(h).a.Jc(),new ee))),17).d.i,y==p&&(n?t=u(tt(new qn(Vn(Ni(h).a.Jc(),new ee))),17).d.i:t=u(tt(new qn(Vn(rr(h).a.Jc(),new ee))),17).c.i,(n?u(f2(e.a,t),15).a-u(f2(e.a,y),15).a:u(f2(e.a,y),15).a-u(f2(e.a,t),15).a)<=2&&(q=!1)));if(i&&q&&(n?t=u(tt(new qn(Vn(Ni(p).a.Jc(),new ee))),17).d.i:t=u(tt(new qn(Vn(rr(p).a.Jc(),new ee))),17).c.i,(n?u(f2(e.a,t),15).a-u(f2(e.a,p),15).a:u(f2(e.a,p),15).a-u(f2(e.a,t),15).a)<=2&&t.k==(zn(),Qi)&&(q=!1)),o||q){for(O=_Ue(e,p,n);O.a.gc()!=0;)A=u(O.a.ec().Jc().Pb(),9),O.a.Ac(A)!=null,fc(O,_Ue(e,A,n));--S,te=!0}}}while(te)}function cBn(e){Tt(e.c,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#decimal"])),Tt(e.d,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#integer"])),Tt(e.e,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#boolean"])),Tt(e.f,Ut,z(B(ze,1),Se,2,6,[oc,"EBoolean",ui,"EBoolean:Object"])),Tt(e.i,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#byte"])),Tt(e.g,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#hexBinary"])),Tt(e.j,Ut,z(B(ze,1),Se,2,6,[oc,"EByte",ui,"EByte:Object"])),Tt(e.n,Ut,z(B(ze,1),Se,2,6,[oc,"EChar",ui,"EChar:Object"])),Tt(e.t,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#double"])),Tt(e.u,Ut,z(B(ze,1),Se,2,6,[oc,"EDouble",ui,"EDouble:Object"])),Tt(e.F,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#float"])),Tt(e.G,Ut,z(B(ze,1),Se,2,6,[oc,"EFloat",ui,"EFloat:Object"])),Tt(e.I,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#int"])),Tt(e.J,Ut,z(B(ze,1),Se,2,6,[oc,"EInt",ui,"EInt:Object"])),Tt(e.N,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#long"])),Tt(e.O,Ut,z(B(ze,1),Se,2,6,[oc,"ELong",ui,"ELong:Object"])),Tt(e.Z,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#short"])),Tt(e.$,Ut,z(B(ze,1),Se,2,6,[oc,"EShort",ui,"EShort:Object"])),Tt(e._,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#string"]))}function Ne(){Ne=Y,Bie=(Xt(),Ifn),w4e=_fn,dD=Lfn,Vf=Pfn,Bv=q9e,Sg=U9e,Em=X9e,O7=K9e,N7=V9e,zie=iG,xg=Vd,Fie=$fn,yx=W9e,yH=Jy,hD=(Ige(),Jcn),jm=Hcn,ub=Gcn,Sm=qcn,Nun=new Vr(PD,ve(0)),C7=Bcn,g4e=zcn,Ly=Fcn,x4e=dun,m4e=Kcn,v4e=Qcn,Hie=run,y4e=eun,k4e=tun,kH=pun,Gie=bun,E4e=lun,j4e=oun,S4e=aun,r4e=kcn,Lie=pcn,gH=wcn,Pie=vcn,gp=Icn,vx=_cn,Iie=qrn,X5e=Xrn,Pun=z7,$un=rG,Lun=Im,_un=B7,p4e=(G4(),Pm),new Vr(Hy,p4e),f4e=new pw(12),l4e=new Vr(o1,f4e),G5e=(z1(),H7),Y1=new Vr(j9e,G5e),vm=new Vr(Ps,0),Dun=new Vr(Ece,ve(1)),uH=new Vr($7,H8),Eg=tG,Wi=Kx,T7=Qv,Sun=DD,Ch=yfn,wm=Xv,Iun=new Vr(Sce,(Pn(),!0)),pm=ID,kg=gce,jg=Tg,vH=ab,Rie=Om,H5e=(vr(),eh),pl=new Vr(Mg,H5e),bp=Vv,pH=O9e,ym=Nm,Oun=jce,d4e=H9e,h4e=(nv(),FD),new Vr(R9e,h4e),Mun=mce,Tun=vce,Cun=yce,Aun=pce,Jie=Xcn,bH=gcn,aD=bcn,kx=Ucn,yu=ocn,_y=$rn,wx=Prn,A7=krn,z5e=jrn,Oie=Arn,fD=Ern,Nie=_rn,c4e=jcn,u4e=Ecn,Z5e=ncn,mH=$cn,$ie=Acn,_ie=Yrn,s4e=Ncn,U5e=Hrn,Die=Grn,Cie=ND,o4e=Scn,sH=irn,P5e=trn,oH=nrn,Y5e=Zrn,V5e=Wrn,Q5e=ecn,M7=Yv,Wc=Kv,Gd=Sfn,Oh=bce,Rv=dce,F5e=Trn,qd=kce,hx=Efn,dH=Afn,wp=z9e,a4e=Cfn,mm=Ofn,n4e=lcn,t4e=acn,km=Fy,xie=ern,i4e=dcn,hH=zrn,aH=Brn,wH=R7,e4e=rcn,mx=Tcn,bD=Y9e,J5e=Rrn,b4e=Rcn,q5e=Frn,kun=Orn,jun=Nrn,xun=ucn,Eun=Drn,W5e=wce,px=scn,fH=Irn,u1=yrn,Mie=prn,lD=crn,Aie=urn,lH=mrn,dx=rrn,Tie=vrn,gm=wrn,gx=grn,yun=brn,Iy=orn,bx=drn,B5e=hrn,$5e=srn,R5e=frn,K5e=Qrn}function uBn(e,n,t,i,r,c,o){var l,f,h,b,p,y,S,A;return y=u(i.a,15).a,S=u(i.b,15).a,p=e.b,A=e.c,l=0,b=0,n==(vr(),Zc)||n==ru?(b=cC(_Fe(k2(So(new wn(null,new pn(t.b,16)),new C_),new xM))),p.e.b+p.f.b/2>b?(h=++S,l=ne(re(Js(p2(So(new wn(null,new pn(t.b,16)),new pTe(r,h)),new iw))))):(f=++y,l=ne(re(Js(x4(So(new wn(null,new pn(t.b,16)),new mTe(r,f)),new Dk)))))):(b=cC(_Fe(k2(So(new wn(null,new pn(t.b,16)),new k_),new I6))),p.e.a+p.f.a/2>b?(h=++S,l=ne(re(Js(p2(So(new wn(null,new pn(t.b,16)),new wTe(r,h)),new AM))))):(f=++y,l=ne(re(Js(x4(So(new wn(null,new pn(t.b,16)),new gTe(r,f)),new MM)))))),n==Zc?(gc(e.a,new je(ne(re(T(p,(Ti(),Ea))))-r,l)),gc(e.a,new je(A.e.a+A.f.a+r+c,l)),gc(e.a,new je(A.e.a+A.f.a+r+c,A.e.b+A.f.b/2)),gc(e.a,new je(A.e.a+A.f.a,A.e.b+A.f.b/2))):n==ru?(gc(e.a,new je(ne(re(T(p,(Ti(),Yf))))+r,p.e.b+p.f.b/2)),gc(e.a,new je(p.e.a+p.f.a+r,l)),gc(e.a,new je(A.e.a-r-c,l)),gc(e.a,new je(A.e.a-r-c,A.e.b+A.f.b/2)),gc(e.a,new je(A.e.a,A.e.b+A.f.b/2))):n==Vl?(gc(e.a,new je(l,ne(re(T(p,(Ti(),Ea))))-r)),gc(e.a,new je(l,A.e.b+A.f.b+r+c)),gc(e.a,new je(A.e.a+A.f.a/2,A.e.b+A.f.b+r+c)),gc(e.a,new je(A.e.a+A.f.a/2,A.e.b+A.f.b+r))):(e.a.b==0||(u(If(e.a),8).b=ne(re(T(p,(Ti(),Yf))))+r*u(o.b,15).a),gc(e.a,new je(l,ne(re(T(p,(Ti(),Yf))))+r*u(o.b,15).a)),gc(e.a,new je(l,A.e.b-r*u(o.a,15).a-c))),new jc(ve(y),ve(S))}function oBn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S;if(o=!0,p=null,i=null,r=null,n=!1,S=Pan,h=null,c=null,l=0,f=NQ(e,l,U8e,X8e),f=0&&bn(e.substr(l,2),"//")?(l+=2,f=NQ(e,l,uA,oA),i=(Yr(l,f,e.length),e.substr(l,f-l)),l=f):p!=null&&(l==e.length||(Yn(l,e.length),e.charCodeAt(l)!=47))&&(o=!1,f=rle(e,Xo(35),l),f==-1&&(f=e.length),i=(Yr(l,f,e.length),e.substr(l,f-l)),l=f);if(!t&&l0&&ic(b,b.length-1)==58&&(r=b,l=f)),lo?(Ys(e,n,t),1):(Ys(e,t,n),-1)}for(q=e.f,V=0,te=q.length;V0?Ys(e,n,t):Ys(e,t,n),i;if(!bi(n,(me(),Ci))||!bi(t,Ci))return c=rW(e,n),l=rW(e,t),c>l?(Ys(e,n,t),1):(Ys(e,t,n),-1)}if(!y&&!A&&(i=nYe(e,n,t),i!=0))return i>0?Ys(e,n,t):Ys(e,t,n),i}return bi(n,(me(),Ci))&&bi(t,Ci)?(c=qw(n,t,e.c,u(T(e.c,cb),15).a),l=qw(t,n,e.c,u(T(e.c,cb),15).a),c>l?(Ys(e,n,t),1):(Ys(e,t,n),-1)):(Ys(e,t,n),-1)}function eYe(){eYe=Y,sZ(),Wt=new Tw,gn(Wt,(De(),na),th),gn(Wt,mf,th),gn(Wt,ks,th),gn(Wt,ta,th),gn(Wt,es,th),gn(Wt,js,th),gn(Wt,ta,na),gn(Wt,th,Yl),gn(Wt,na,Yl),gn(Wt,mf,Yl),gn(Wt,ks,Yl),gn(Wt,Zo,Yl),gn(Wt,ta,Yl),gn(Wt,es,Yl),gn(Wt,js,Yl),gn(Wt,zo,Yl),gn(Wt,th,vl),gn(Wt,na,vl),gn(Wt,Yl,vl),gn(Wt,mf,vl),gn(Wt,ks,vl),gn(Wt,Zo,vl),gn(Wt,ta,vl),gn(Wt,zo,vl),gn(Wt,yl,vl),gn(Wt,es,vl),gn(Wt,hs,vl),gn(Wt,js,vl),gn(Wt,na,mf),gn(Wt,ks,mf),gn(Wt,ta,mf),gn(Wt,js,mf),gn(Wt,na,ks),gn(Wt,mf,ks),gn(Wt,ta,ks),gn(Wt,ks,ks),gn(Wt,es,ks),gn(Wt,th,Ql),gn(Wt,na,Ql),gn(Wt,Yl,Ql),gn(Wt,vl,Ql),gn(Wt,mf,Ql),gn(Wt,ks,Ql),gn(Wt,Zo,Ql),gn(Wt,ta,Ql),gn(Wt,yl,Ql),gn(Wt,zo,Ql),gn(Wt,js,Ql),gn(Wt,es,Ql),gn(Wt,mo,Ql),gn(Wt,th,yl),gn(Wt,na,yl),gn(Wt,Yl,yl),gn(Wt,mf,yl),gn(Wt,ks,yl),gn(Wt,Zo,yl),gn(Wt,ta,yl),gn(Wt,zo,yl),gn(Wt,js,yl),gn(Wt,hs,yl),gn(Wt,mo,yl),gn(Wt,na,zo),gn(Wt,mf,zo),gn(Wt,ks,zo),gn(Wt,ta,zo),gn(Wt,yl,zo),gn(Wt,js,zo),gn(Wt,es,zo),gn(Wt,th,Wo),gn(Wt,na,Wo),gn(Wt,Yl,Wo),gn(Wt,mf,Wo),gn(Wt,ks,Wo),gn(Wt,Zo,Wo),gn(Wt,ta,Wo),gn(Wt,zo,Wo),gn(Wt,js,Wo),gn(Wt,na,es),gn(Wt,Yl,es),gn(Wt,vl,es),gn(Wt,ks,es),gn(Wt,th,hs),gn(Wt,na,hs),gn(Wt,vl,hs),gn(Wt,mf,hs),gn(Wt,ks,hs),gn(Wt,Zo,hs),gn(Wt,ta,hs),gn(Wt,ta,mo),gn(Wt,ks,mo),gn(Wt,zo,th),gn(Wt,zo,mf),gn(Wt,zo,Yl),gn(Wt,Zo,th),gn(Wt,Zo,na),gn(Wt,Zo,vl)}function sBn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;switch(t.Tg("Brandes & Koepf node placement",1),e.a=n,e.c=F_n(n),i=u(T(n,(Ne(),$ie)),282),S=Re($e(T(n,mx))),e.d=i==(zO(),YJ)&&!S||i==sie,_Pn(e,n),be=null,fe=null,R=null,q=null,I=(ll(4,Q2),new xo(4)),u(T(n,$ie),282).g){case 3:R=new lv(n,e.c.d,(Da(),Ag),(ah(),Ud)),Hn(I.c,R);break;case 1:q=new lv(n,e.c.d,(Da(),Ya),(ah(),Ud)),Hn(I.c,q);break;case 4:be=new lv(n,e.c.d,(Da(),Ag),(ah(),pp)),Hn(I.c,be);break;case 2:fe=new lv(n,e.c.d,(Da(),Ya),(ah(),pp)),Hn(I.c,fe);break;default:R=new lv(n,e.c.d,(Da(),Ag),(ah(),Ud)),q=new lv(n,e.c.d,Ya,Ud),be=new lv(n,e.c.d,Ag,pp),fe=new lv(n,e.c.d,Ya,pp),Hn(I.c,be),Hn(I.c,fe),Hn(I.c,R),Hn(I.c,q)}for(r=new lTe(n,e.c),l=new L(I);l.axW(c))&&(p=c);for(!p&&(p=(vn(0,I.c.length),u(I.c[0],185))),O=new L(n.b);O.a0?(Ys(e,t,n),1):(Ys(e,n,t),-1);if(b&&V)return Ys(e,t,n),1;if(p&&q)return Ys(e,n,t),-1;if(p&&V)return 0}else for(tn=new L(h.j);tn.ap&&(Dn=0,ot+=b+Ie,b=0),XXe(be,o,Dn,ot),n=k.Math.max(n,Dn+fe.a),b=k.Math.max(b,fe.b),Dn+=fe.a+Ie;for(te=new pt,t=new pt,tn=new L(e);tn.a=-1900?1:0,t>=4?Kt(e,z(B(ze,1),Se,2,6,[mYe,vYe])[l]):Kt(e,z(B(ze,1),Se,2,6,["BC","AD"])[l]);break;case 121:VEn(e,t,i);break;case 77:GIn(e,t,i);break;case 107:f=r.q.getHours(),f==0?Kh(e,24,t):Kh(e,f,t);break;case 83:uNn(e,t,r);break;case 69:b=i.q.getDay(),t==5?Kt(e,z(B(ze,1),Se,2,6,["S","M","T","W","T","F","S"])[b]):t==4?Kt(e,z(B(ze,1),Se,2,6,[CZ,OZ,NZ,DZ,IZ,_Z,LZ])[b]):Kt(e,z(B(ze,1),Se,2,6,["Sun","Mon","Tue","Wed","Thu","Fri","Sat"])[b]);break;case 97:r.q.getHours()>=12&&r.q.getHours()<24?Kt(e,z(B(ze,1),Se,2,6,["AM","PM"])[1]):Kt(e,z(B(ze,1),Se,2,6,["AM","PM"])[0]);break;case 104:p=r.q.getHours()%12,p==0?Kh(e,12,t):Kh(e,p,t);break;case 75:y=r.q.getHours()%12,Kh(e,y,t);break;case 72:S=r.q.getHours(),Kh(e,S,t);break;case 99:A=i.q.getDay(),t==5?Kt(e,z(B(ze,1),Se,2,6,["S","M","T","W","T","F","S"])[A]):t==4?Kt(e,z(B(ze,1),Se,2,6,[CZ,OZ,NZ,DZ,IZ,_Z,LZ])[A]):t==3?Kt(e,z(B(ze,1),Se,2,6,["Sun","Mon","Tue","Wed","Thu","Fri","Sat"])[A]):Kh(e,A,1);break;case 76:O=i.q.getMonth(),t==5?Kt(e,z(B(ze,1),Se,2,6,["J","F","M","A","M","J","J","A","S","O","N","D"])[O]):t==4?Kt(e,z(B(ze,1),Se,2,6,[mZ,vZ,yZ,kZ,uy,jZ,EZ,SZ,xZ,AZ,MZ,TZ])[O]):t==3?Kt(e,z(B(ze,1),Se,2,6,["Jan","Feb","Mar","Apr",uy,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"])[O]):Kh(e,O+1,t);break;case 81:I=i.q.getMonth()/3|0,t<4?Kt(e,z(B(ze,1),Se,2,6,["Q1","Q2","Q3","Q4"])[I]):Kt(e,z(B(ze,1),Se,2,6,["1st quarter","2nd quarter","3rd quarter","4th quarter"])[I]);break;case 100:R=i.q.getDate(),Kh(e,R,t);break;case 109:h=r.q.getMinutes(),Kh(e,h,t);break;case 115:o=r.q.getSeconds(),Kh(e,o,t);break;case 122:t<4?Kt(e,c.c[0]):Kt(e,c.c[1]);break;case 118:Kt(e,c.b);break;case 90:t<3?Kt(e,lCn(c)):t==3?Kt(e,dCn(c)):Kt(e,bCn(c.a));break;default:return!1}return!0}function Dge(e,n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn,Dn,ot,Qt;if(LXe(n),f=u(K((!n.b&&(n.b=new Nn(vt,n,4,7)),n.b),0),84),b=u(K((!n.c&&(n.c=new Nn(vt,n,5,8)),n.c),0),84),l=iu(f),h=iu(b),o=(!n.a&&(n.a=new pe(Pi,n,6,6)),n.a).i==0?null:u(K((!n.a&&(n.a=new pe(Pi,n,6,6)),n.a),0),170),Ie=u(Bn(e.a,l),9),Dn=u(Bn(e.a,h),9),cn=null,ot=null,X(f,193)&&(fe=u(Bn(e.a,f),246),X(fe,12)?cn=u(fe,12):X(fe,9)&&(Ie=u(fe,9),cn=u(Le(Ie.j,0),12))),X(b,193)&&(Tn=u(Bn(e.a,b),246),X(Tn,12)?ot=u(Tn,12):X(Tn,9)&&(Dn=u(Tn,9),ot=u(Le(Dn.j,0),12))),!Ie||!Dn)throw $(new u4("The source or the target of edge "+n+" could not be found. This usually happens when an edge connects a node laid out by ELK Layered to a node in another level of hierarchy laid out by either another instance of ELK Layered or another layout algorithm alltogether. The former can be solved by setting the hierarchyHandling option to INCLUDE_CHILDREN."));for(O=new Mw,$u(O,n),he(O,(me(),wi),n),he(O,(Ne(),Wc),null),S=u(T(i,po),22),Ie==Dn&&S.Ec((Dc(),ux)),cn||(be=(Nc(),Do),tn=null,o&&D3(u(T(Ie,Wi),102))&&(tn=new je(o.j,o.k),fPe(tn,j2(n)),$Pe(tn,t),C2(h,l)&&(be=ys,gi(tn,Ie.n))),cn=RKe(Ie,tn,be,i)),ot||(be=(Nc(),ys),Qt=null,o&&D3(u(T(Dn,Wi),102))&&(Qt=new je(o.b,o.c),fPe(Qt,j2(n)),$Pe(Qt,t)),ot=RKe(Dn,Qt,be,_r(Dn))),lc(O,cn),Gr(O,ot),(cn.e.c.length>1||cn.g.c.length>1||ot.e.c.length>1||ot.g.c.length>1)&&S.Ec((Dc(),cx)),y=new ut((!n.n&&(n.n=new pe(ju,n,1,7)),n.n));y.e!=y.i.gc();)if(p=u(ft(y),157),!Re($e(ye(p,Eg)))&&p.a)switch(I=fQ(p),Te(O.b,I),u(T(I,Oh),279).g){case 1:case 2:S.Ec((Dc(),E7));break;case 0:S.Ec((Dc(),j7)),he(I,Oh,($a(),F7))}if(c=u(T(i,wx),301),R=u(T(i,mH),328),r=c==(BE(),eD)||R==(HE(),Wie),o&&(!o.a&&(o.a=new mr(kl,o,5)),o.a).i!=0&&r){for(q=lTn(o),A=new xs,te=St(q,0);te.b!=te.d.c;)V=u(jt(te),8),Vt(A,new wc(V));he(O,Kve,A)}return O}function hBn(e,n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn,Dn,ot,Qt,qi;for(tn=0,Tn=0,Ie=new pt,be=u(Js(p2(So(new wn(null,new pn(e.b,16)),new y_),new Nk)),15).a+1,cn=oe($t,ni,30,be,15,1),I=oe($t,ni,30,be,15,1),O=0;O1)for(l=ot+1;lh.b.e.b*(1-R)+h.c.e.b*R));A++);if(fe.gc()>0&&(Qt=h.a.b==0?pc(h.b.e):u(If(h.a),8),V=gi(pc(u(fe.Xb(fe.gc()-1),40).e),u(fe.Xb(fe.gc()-1),40).f),y=gi(pc(u(fe.Xb(0),40).e),u(fe.Xb(0),40).f),A>=fe.gc()-1&&Qt.b>V.b&&h.c.e.b>V.b||A<=0&&Qt.bh.b.e.a*(1-R)+h.c.e.a*R));A++);if(fe.gc()>0&&(Qt=h.a.b==0?pc(h.b.e):u(If(h.a),8),V=gi(pc(u(fe.Xb(fe.gc()-1),40).e),u(fe.Xb(fe.gc()-1),40).f),y=gi(pc(u(fe.Xb(0),40).e),u(fe.Xb(0),40).f),A>=fe.gc()-1&&Qt.a>V.a&&h.c.e.a>V.a||A<=0&&Qt.a=ne(re(T(e,(Ti(),vye))))&&++Tn):(S.f&&S.d.e.a<=ne(re(T(e,(Ti(),wre))))&&++tn,S.g&&S.c.e.a+S.c.f.a>=ne(re(T(e,(Ti(),mye))))&&++Tn)}else te==0?K0e(h):te<0&&(++cn[ot],++I[qi],Dn=uBn(h,n,e,new jc(ve(tn),ve(Tn)),t,i,new jc(ve(I[qi]),ve(cn[ot]))),tn=u(Dn.a,15).a,Tn=u(Dn.b,15).a)}function dBn(e){e.gb||(e.gb=!0,e.b=Au(e,0),Vi(e.b,18),Ii(e.b,19),e.a=Au(e,1),Vi(e.a,1),Ii(e.a,2),Ii(e.a,3),Ii(e.a,4),Ii(e.a,5),e.o=Au(e,2),Vi(e.o,8),Vi(e.o,9),Ii(e.o,10),Ii(e.o,11),Ii(e.o,12),Ii(e.o,13),Ii(e.o,14),Ii(e.o,15),Ii(e.o,16),Ii(e.o,17),Ii(e.o,18),Ii(e.o,19),Ii(e.o,20),Ii(e.o,21),Ii(e.o,22),Ii(e.o,23),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),e.p=Au(e,3),Vi(e.p,2),Vi(e.p,3),Vi(e.p,4),Vi(e.p,5),Ii(e.p,6),Ii(e.p,7),Yc(e.p),Yc(e.p),e.q=Au(e,4),Vi(e.q,8),e.v=Au(e,5),Ii(e.v,9),Yc(e.v),Yc(e.v),Yc(e.v),e.w=Au(e,6),Vi(e.w,2),Vi(e.w,3),Vi(e.w,4),Ii(e.w,5),e.B=Au(e,7),Ii(e.B,1),Yc(e.B),Yc(e.B),Yc(e.B),e.Q=Au(e,8),Ii(e.Q,0),Yc(e.Q),e.R=Au(e,9),Vi(e.R,1),e.S=Au(e,10),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),e.T=Au(e,11),Ii(e.T,10),Ii(e.T,11),Ii(e.T,12),Ii(e.T,13),Ii(e.T,14),Yc(e.T),Yc(e.T),e.U=Au(e,12),Vi(e.U,2),Vi(e.U,3),Ii(e.U,4),Ii(e.U,5),Ii(e.U,6),Ii(e.U,7),Yc(e.U),e.V=Au(e,13),Ii(e.V,10),e.W=Au(e,14),Vi(e.W,18),Vi(e.W,19),Vi(e.W,20),Ii(e.W,21),Ii(e.W,22),Ii(e.W,23),e.bb=Au(e,15),Vi(e.bb,10),Vi(e.bb,11),Vi(e.bb,12),Vi(e.bb,13),Vi(e.bb,14),Vi(e.bb,15),Vi(e.bb,16),Ii(e.bb,17),Yc(e.bb),Yc(e.bb),e.eb=Au(e,16),Vi(e.eb,2),Vi(e.eb,3),Vi(e.eb,4),Vi(e.eb,5),Vi(e.eb,6),Vi(e.eb,7),Ii(e.eb,8),Ii(e.eb,9),e.ab=Au(e,17),Vi(e.ab,0),Vi(e.ab,1),e.H=Au(e,18),Ii(e.H,0),Ii(e.H,1),Ii(e.H,2),Ii(e.H,3),Ii(e.H,4),Ii(e.H,5),Yc(e.H),e.db=Au(e,19),Ii(e.db,2),e.c=ci(e,20),e.d=ci(e,21),e.e=ci(e,22),e.f=ci(e,23),e.i=ci(e,24),e.g=ci(e,25),e.j=ci(e,26),e.k=ci(e,27),e.n=ci(e,28),e.r=ci(e,29),e.s=ci(e,30),e.t=ci(e,31),e.u=ci(e,32),e.fb=ci(e,33),e.A=ci(e,34),e.C=ci(e,35),e.D=ci(e,36),e.F=ci(e,37),e.G=ci(e,38),e.I=ci(e,39),e.J=ci(e,40),e.L=ci(e,41),e.M=ci(e,42),e.N=ci(e,43),e.O=ci(e,44),e.P=ci(e,45),e.X=ci(e,46),e.Y=ci(e,47),e.Z=ci(e,48),e.$=ci(e,49),e._=ci(e,50),e.cb=ci(e,51),e.K=ci(e,52))}function bBn(e,n,t,i){var r,c,o,l,f,h,b,p,y,S,A;for(p=St(e.b,0);p.b!=p.d.c;)if(b=u(jt(p),40),!bn(b.c,DF))for(c=u(gs(new wn(null,new pn(MCn(b,e),16)),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),n==(vr(),Zc)||n==ru?c.gd(new A_):c.gd(new M_),A=c.gc(),r=0;r0&&(l=u(If(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(If(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&k.Math.abs(f-S)/(k.Math.abs(l-y)/40)>50&&(S>f?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a+i/5.3,b.e.b+b.f.b*o-i/2)):gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a+i/5.3,b.e.b+b.f.b*o+i/2)))),gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a,b.e.b+b.f.b*o))):n==ru?(h=ne(re(T(b,(Ti(),Ea)))),b.e.a-i>h?gc(u(c.Xb(r),65).a,new je(h-t,b.e.b+b.f.b*o)):u(c.Xb(r),65).a.b>0&&(l=u(If(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(If(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&k.Math.abs(f-S)/(k.Math.abs(l-y)/40)>50&&(S>f?gc(u(c.Xb(r),65).a,new je(b.e.a-i/5.3,b.e.b+b.f.b*o-i/2)):gc(u(c.Xb(r),65).a,new je(b.e.a-i/5.3,b.e.b+b.f.b*o+i/2)))),gc(u(c.Xb(r),65).a,new je(b.e.a,b.e.b+b.f.b*o))):n==Vl?(h=ne(re(T(b,(Ti(),Yf)))),b.e.b+b.f.b+i0&&(l=u(If(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(If(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&k.Math.abs(l-y)/(k.Math.abs(f-S)/40)>50&&(y>l?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o-i/2,b.e.b+i/5.3+b.f.b)):gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o+i/2,b.e.b+i/5.3+b.f.b)))),gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o,b.e.b+b.f.b))):(h=ne(re(T(b,(Ti(),Ea)))),Bze(u(c.Xb(r),65),e)?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o,u(If(u(c.Xb(r),65).a),8).b)):b.e.b-i>h?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o,h-t)):u(c.Xb(r),65).a.b>0&&(l=u(If(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(If(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&k.Math.abs(l-y)/(k.Math.abs(f-S)/40)>50&&(y>l?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o-i/2,b.e.b-i/5.3)):gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o+i/2,b.e.b-i/5.3)))),gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o,b.e.b)))}function iYe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;if(o=n,y=t,so(e.a,o)){if(rf(u(Bn(e.a,o),47),y))return 1}else ei(e.a,o,new ar);if(so(e.a,y)){if(rf(u(Bn(e.a,y),47),o))return-1}else ei(e.a,y,new ar);if(so(e.e,o)){if(rf(u(Bn(e.e,o),47),y))return-1}else ei(e.e,o,new ar);if(so(e.e,y)){if(rf(u(Bn(e.a,y),47),o))return 1}else ei(e.e,y,new ar);if(o.j!=y.j)return be=iwn(o.j,y.j),be>0?Hl(e,o,y,1):Hl(e,y,o,1),be;if(fe=1,o.e.c.length!=0&&y.e.c.length!=0){if((o.j==(De(),Kn)&&y.j==Kn||o.j==Xn&&y.j==Xn||o.j==bt&&y.j==bt)&&(fe=-fe),b=u(Le(o.e,0),17).c,I=u(Le(y.e,0),17).c,f=b.i,A=I.i,f==A)for(V=new L(f.j);V.a0?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe);if(i=kFe(u(gs(mV(e.d),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),20),f,A),i!=0)return i>0?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe);if(e.c&&(be=QJe(e,o,y),be!=0))return be>0?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe)}return o.g.c.length!=0&&y.g.c.length!=0?((o.j==(De(),Kn)&&y.j==Kn||o.j==bt&&y.j==bt)&&(fe=-fe),p=u(T(o,(me(),mie)),9),R=u(T(y,mie),9),e.f==(F1(),nre)&&p&&R&&bi(p,Ci)&&bi(R,Ci)?(l=qw(p,R,e.b,u(T(e.b,cb),15).a),S=qw(R,p,e.b,u(T(e.b,cb),15).a),l>S?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe)):e.c&&(be=QJe(e,o,y),be!=0)?be>0?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe):(h=0,O=0,bi(u(Le(o.g,0),17),Ci)&&(h=qw(u(Le(o.g,0),246),u(Le(y.g,0),246),e.b,o.g.c.length+o.e.c.length)),bi(u(Le(y.g,0),17),Ci)&&(O=qw(u(Le(y.g,0),246),u(Le(o.g,0),246),e.b,y.g.c.length+y.e.c.length)),p&&p==R||e.g&&(e.g._b(p)&&(h=u(e.g.xc(p),15).a),e.g._b(R)&&(O=u(e.g.xc(R),15).a)),h>O?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe))):o.e.c.length!=0&&y.g.c.length!=0?(Hl(e,o,y,fe),1):o.g.c.length!=0&&y.e.c.length!=0?(Hl(e,y,o,fe),-1):bi(o,(me(),Ci))&&bi(y,Ci)?(c=o.i.j.c.length,l=qw(o,y,e.b,c),S=qw(y,o,e.b,c),(o.j==(De(),Kn)&&y.j==Kn||o.j==bt&&y.j==bt)&&(fe=-fe),l>S?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe)):(Hl(e,y,o,fe),-fe)}function me(){me=Y;var e,n;wi=new yi(owe),Gve=new yi("coordinateOrigin"),yie=new yi("processors"),Hve=new Li("compoundNode",(Pn(),!1)),uD=new Li("insideConnections",!1),Kve=new yi("originalBendpoints"),Vve=new yi("originalDummyNodePosition"),Yve=new yi("originalLabelEdge"),sx=new yi("representedLabels"),ox=new yi("endLabels"),My=new yi("endLabel.origin"),Cy=new Li("labelSide",(al(),zD)),Iv=new Li("maxEdgeThickness",0),Hd=new Li("reversed",!1),Oy=new yi(tQe),ja=new Li("longEdgeSource",null),gf=new Li("longEdgeTarget",null),bm=new Li("longEdgeHasLabelDummies",!1),oD=new Li("longEdgeBeforeLabelDummy",!1),tH=new Li("edgeConstraint",(Wb(),tie)),ap=new yi("inLayerLayoutUnit"),mg=new Li("inLayerConstraint",(_1(),rD)),Ty=new Li("inLayerSuccessorConstraint",new Ce),Xve=new Li("inLayerSuccessorConstraintBetweenNonDummies",!1),vs=new yi("portDummy"),nH=new Li("crossingHint",ve(0)),po=new Li("graphProperties",(n=u(sa(lie),10),new _l(n,u(_f(n,n.length),10),0))),Du=new Li("externalPortSide",(De(),ku)),Uve=new Li("externalPortSize",new Kr),gie=new yi("externalPortReplacedDummies"),iH=new yi("externalPortReplacedDummy"),K1=new Li("externalPortConnections",(e=u(sa(xc),10),new _l(e,u(_f(e,e.length),10),0))),hp=new Li(QYe,0),Jve=new yi("barycenterAssociates"),Dy=new yi("TopSideComments"),xy=new yi("BottomSideComments"),eH=new yi("CommentConnectionPort"),pie=new Li("inputCollect",!1),vie=new Li("outputCollect",!1),Ay=new Li("cyclic",!1),qve=new yi("crossHierarchyMap"),jie=new yi("targetOffset"),new Li("splineLabelSize",new Kr),Lv=new yi("spacings"),rH=new Li("partitionConstraint",!1),fp=new yi("breakingPoint.info"),Zve=new yi("splines.survivingEdge"),vg=new yi("splines.route.start"),Pv=new yi("splines.edgeChain"),Wve=new yi("originalPortConstraints"),dp=new yi("selfLoopHolder"),x7=new yi("splines.nsPortY"),Ci=new yi("modelOrder"),cb=new yi("modelOrder.maximum"),cD=new yi("modelOrderGroups.cb.number"),mie=new yi("longEdgeTargetNode"),rb=new Li(TQe,!1),_v=new Li(TQe,!1),wie=new yi("layerConstraints.hiddenNodes"),Qve=new yi("layerConstraints.opposidePort"),kie=new yi("targetNode.modelOrder"),Ny=new Li("tarjan.lowlink",ve(oi)),lx=new Li("tarjan.id",ve(-1)),cH=new Li("tarjan.onstack",!1),Qin=new Li("partOfCycle",!1),$v=new yi("medianHeuristic.weight")}function Xt(){Xt=Y;var e,n;zy=new yi(pWe),Dm=new yi(mWe),p9e=(Vh(),sce),yfn=new fn(ppe,p9e),$7=new fn(G8,null),kfn=new yi(N2e),v9e=(rg(),Mi(ace,z(B(hce,1),ke,299,0,[fce]))),ND=new fn(TF,v9e),DD=new fn(_N,(Pn(),!1)),y9e=(vr(),eh),Mg=new fn(Fee,y9e),E9e=(z1(),xce),j9e=new fn(IN,E9e),xfn=new fn(C2e,!1),x9e=(B1(),oG),Xv=new fn(MF,x9e),P9e=new pw(12),o1=new fn(nm,P9e),_D=new fn(jS,!1),wce=new fn(OF,!1),LD=new fn(ES,!1),F9e=(Br(),bb),Kx=new fn(WZ,F9e),Fy=new yi(CF),PD=new yi(EN),Ece=new yi(sF),Sce=new yi(kS),T9e=new xs,Kv=new fn(Tpe,T9e),Efn=new fn(Dpe,!1),Afn=new fn(Ipe,!1),new fn(vWe,0),C9e=new wj,R7=new fn(Lpe,C9e),tG=new fn(gpe,!1),Dfn=new fn(yWe,1),Cm=new yi(kWe),Tm=new yi(jWe),z7=new fn(SN,!1),new fn(EWe,!0),ve(0),new fn(SWe,ve(100)),new fn(xWe,!1),ve(0),new fn(AWe,ve(4e3)),ve(0),new fn(MWe,ve(400)),new fn(TWe,!1),new fn(CWe,!1),new fn(OWe,!0),new fn(NWe,!1),m9e=(KB(),Nce),jfn=new fn(O2e,m9e),M9e=(jE(),HD),Tfn=new fn(DWe,M9e),A9e=(u8(),$D),Mfn=new fn(IWe,A9e),Ifn=new fn(ipe,10),_fn=new fn(rpe,10),Lfn=new fn(cpe,20),Pfn=new fn(upe,10),q9e=new fn(QZ,2),U9e=new fn(zee,10),X9e=new fn(ope,0),iG=new fn(fpe,5),K9e=new fn(spe,1),V9e=new fn(lpe,1),Vd=new fn(em,20),$fn=new fn(ape,10),W9e=new fn(hpe,10),Jy=new yi(dpe),Q9e=new pCe,Y9e=new fn(Ppe,Q9e),Ofn=new yi(Hee),$9e=!1,Cfn=new fn(Jee,$9e),N9e=new pw(5),O9e=new fn(ype,N9e),D9e=(G2(),n=u(sa($c),10),new _l(n,u(_f(n,n.length),10),0)),Vv=new fn(U8,D9e),B9e=(nv(),db),R9e=new fn(Epe,B9e),mce=new yi(Spe),vce=new yi(xpe),yce=new yi(Ape),pce=new yi(Mpe),I9e=(e=u(sa(tA),10),new _l(e,u(_f(e,e.length),10),0)),Tg=new fn(wv,I9e),L9e=nn((_s(),q7)),ab=new fn(hy,L9e),_9e=new je(0,0),Yv=new fn(dy,_9e),Om=new fn(q8,!1),k9e=($a(),F7),bce=new fn(Ope,k9e),dce=new fn(lF,!1),ve(1),new fn(_We,null),z9e=new yi(_pe),kce=new yi(Npe),G9e=(De(),ku),Qv=new fn(wpe,G9e),Ps=new yi(bpe),J9e=(ps(),nn(gb)),Nm=new fn(X8,J9e),jce=new fn(kpe,!1),H9e=new fn(jpe,!0),ve(1),Jfn=new fn(hne,ve(3)),ve(1),Gfn=new fn(D2e,ve(4)),rG=new fn(xN,1),cG=new fn(dne,null),Im=new fn(AN,150),B7=new fn(MN,1.414),Hy=new fn(Ww,null),Rfn=new fn(I2e,1),ID=new fn(mpe,!1),gce=new fn(vpe,!1),Sfn=new fn(Cpe,1),S9e=(jz(),Mce),new fn(LWe,S9e),Nfn=!0,Hfn=(UR(),Oce),zfn=(G4(),Pm),Ffn=Pm,Bfn=Pm}function Ur(){Ur=Y,B3e=new br("DIRECTION_PREPROCESSOR",0),P3e=new br("COMMENT_PREPROCESSOR",1),Av=new br("EDGE_AND_LAYER_CONSTRAINT_EDGE_REVERSER",2),Ite=new br("INTERACTIVE_EXTERNAL_PORT_POSITIONER",3),rve=new br("PARTITION_PREPROCESSOR",4),TJ=new br("LABEL_DUMMY_INSERTER",5),RJ=new br("SELF_LOOP_PREPROCESSOR",6),fm=new br("LAYER_CONSTRAINT_PREPROCESSOR",7),tve=new br("PARTITION_MIDPROCESSOR",8),X3e=new br("HIGH_DEGREE_NODE_LAYER_PROCESSOR",9),eve=new br("NODE_PROMOTION",10),lm=new br("LAYER_CONSTRAINT_POSTPROCESSOR",11),ive=new br("PARTITION_POSTPROCESSOR",12),G3e=new br("HIERARCHICAL_PORT_CONSTRAINT_PROCESSOR",13),cve=new br("SEMI_INTERACTIVE_CROSSMIN_PROCESSOR",14),O3e=new br("BREAKING_POINT_INSERTER",15),DJ=new br("LONG_EDGE_SPLITTER",16),_te=new br("PORT_SIDE_PROCESSOR",17),AJ=new br("INVERTED_PORT_PROCESSOR",18),LJ=new br("PORT_LIST_SORTER",19),ove=new br("SORT_BY_INPUT_ORDER_OF_MODEL",20),_J=new br("NORTH_SOUTH_PORT_PREPROCESSOR",21),N3e=new br("BREAKING_POINT_PROCESSOR",22),nve=new br(yQe,23),sve=new br(kQe,24),PJ=new br("SELF_LOOP_PORT_RESTORER",25),C3e=new br("ALTERNATING_LAYER_UNZIPPER",26),uve=new br("SINGLE_EDGE_GRAPH_WRAPPER",27),MJ=new br("IN_LAYER_CONSTRAINT_PROCESSOR",28),F3e=new br("END_NODE_PORT_LABEL_MANAGEMENT_PROCESSOR",29),W3e=new br("LABEL_AND_NODE_SIZE_PROCESSOR",30),Q3e=new br("INNERMOST_NODE_MARGIN_CALCULATOR",31),BJ=new br("SELF_LOOP_ROUTER",32),_3e=new br("COMMENT_NODE_MARGIN_CALCULATOR",33),xJ=new br("END_LABEL_PREPROCESSOR",34),OJ=new br("LABEL_DUMMY_SWITCHER",35),I3e=new br("CENTER_LABEL_MANAGEMENT_PROCESSOR",36),g7=new br("LABEL_SIDE_SELECTOR",37),V3e=new br("HYPEREDGE_DUMMY_MERGER",38),q3e=new br("HIERARCHICAL_PORT_DUMMY_SIZE_PROCESSOR",39),Z3e=new br("LAYER_SIZE_AND_GRAPH_HEIGHT_CALCULATOR",40),nx=new br("HIERARCHICAL_PORT_POSITION_PROCESSOR",41),$3e=new br("CONSTRAINTS_POSTPROCESSOR",42),L3e=new br("COMMENT_POSTPROCESSOR",43),Y3e=new br("HYPERNODE_PROCESSOR",44),U3e=new br("HIERARCHICAL_PORT_ORTHOGONAL_EDGE_ROUTER",45),NJ=new br("LONG_EDGE_JOINER",46),$J=new br("SELF_LOOP_POSTPROCESSOR",47),D3e=new br("BREAKING_POINT_REMOVER",48),IJ=new br("NORTH_SOUTH_PORT_POSTPROCESSOR",49),K3e=new br("HORIZONTAL_COMPACTOR",50),CJ=new br("LABEL_DUMMY_REMOVER",51),J3e=new br("FINAL_SPLINE_BENDPOINTS_CALCULATOR",52),z3e=new br("END_LABEL_SORTER",53),Ey=new br("REVERSED_EDGE_RESTORER",54),SJ=new br("END_LABEL_POSTPROCESSOR",55),H3e=new br("HIERARCHICAL_NODE_RESIZER",56),R3e=new br("DIRECTION_POSTPROCESSOR",57)}function gBn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn,Dn,ot,Qt,qi,Es,eu,jl,i5,i0,ia,nd,Zl,Yy,gA,td,Ma,r0,Ig,_g,Qy,Lg,Pg,id,Gm,M7e,Sp,wA,Kce,Wy,pA,qm,mA,Vce,Ihn;for(M7e=0,Qt=n,eu=0,i0=Qt.length;eu0&&(e.a[Ma.p]=M7e++)}for(pA=0,qi=t,jl=0,ia=qi.length;jl0;){for(Ma=(at(Qy.b>0),u(Qy.a.Xb(Qy.c=--Qy.b),12)),_g=0,l=new L(Ma.e);l.a0&&(Ma.j==(De(),Xn)?(e.a[Ma.p]=pA,++pA):(e.a[Ma.p]=pA+nd+Yy,++Yy))}pA+=Yy}for(Ig=new pt,A=new zh,ot=n,Es=0,i5=ot.length;Esh.b&&(h.b=Lg)):Ma.i.c==Gm&&(Lgh.c&&(h.c=Lg));for(J9(O,0,O.length,null),Wy=oe($t,ni,30,O.length,15,1),i=oe($t,ni,30,pA+1,15,1),R=0;R0;)Ie%2>0&&(r+=Vce[Ie+1]),Ie=(Ie-1)/2|0,++Vce[Ie];for(tn=oe(kon,On,370,O.length*2,0,1),te=0;te0&&JC(Es.f),ye(R,cG)!=null&&(!R.a&&(R.a=new pe(Jt,R,10,11)),!!R.a)&&(!R.a&&(R.a=new pe(Jt,R,10,11)),R.a).i>0?(l=u(ye(R,cG),521),_g=l.Sg(R),ww(R,k.Math.max(R.g,_g.a+nd.b+nd.c),k.Math.max(R.f,_g.b+nd.d+nd.a))):(!R.a&&(R.a=new pe(Jt,R,10,11)),R.a).i!=0&&(_g=new je(ne(re(ye(R,Im))),ne(re(ye(R,Im)))/ne(re(ye(R,B7)))),ww(R,k.Math.max(R.g,_g.a+nd.b+nd.c),k.Math.max(R.f,_g.b+nd.d+nd.a)));if(ia=u(ye(n,o1),104),S=n.g-(ia.b+ia.c),y=n.f-(ia.d+ia.a),Pg.ah("Available Child Area: ("+S+"|"+y+")"),ji(n,$7,S/y),DJe(n,r,i.dh(i5)),u(ye(n,Hy),281)==dG&&(oZ(n),ww(n,ia.b+ne(re(ye(n,Cm)))+ia.c,ia.d+ne(re(ye(n,Tm)))+ia.a)),Pg.ah("Executed layout algorithm: "+Pt(ye(n,zy))+" on node "+n.k),u(ye(n,Hy),281)==Pm){if(S<0||y<0)throw $(new wd("The size defined by the parent parallel node is too small for the space provided by the paddings of the child hierarchical node. "+n.k));for(da(n,Cm)||da(n,Tm)||oZ(n),O=ne(re(ye(n,Cm))),A=ne(re(ye(n,Tm))),Pg.ah("Desired Child Area: ("+O+"|"+A+")"),Yy=S/O,gA=y/A,Zl=k.Math.min(Yy,k.Math.min(gA,ne(re(ye(n,Rfn))))),ji(n,rG,Zl),Pg.ah(n.k+" -- Local Scale Factor (X|Y): ("+Yy+"|"+gA+")"),te=u(ye(n,ND),22),c=0,o=0,Zl'?":bn(bZe,e)?"'(?<' or '(? toIndex: ",Yge=", toIndex: ",Qge="Index: ",Wge=", Size: ",z8="org.eclipse.elk.alg.common",Yt={51:1},IYe="org.eclipse.elk.alg.common.compaction",_Ye="Scanline/EventHandler",n1="org.eclipse.elk.alg.common.compaction.oned",LYe="CNode belongs to another CGroup.",PYe="ISpacingsHandler/1",qZ="The ",UZ=" instance has been finished already.",$Ye="The direction ",RYe=" is not supported by the CGraph instance.",BYe="OneDimensionalCompactor",zYe="OneDimensionalCompactor/lambda$0$Type",FYe="Quadruplet",JYe="ScanlineConstraintCalculator",HYe="ScanlineConstraintCalculator/ConstraintsScanlineHandler",GYe="ScanlineConstraintCalculator/ConstraintsScanlineHandler/lambda$0$Type",qYe="ScanlineConstraintCalculator/Timestamp",UYe="ScanlineConstraintCalculator/lambda$0$Type",jh={178:1,48:1},mS="org.eclipse.elk.alg.common.networksimplex",pa={171:1,3:1,4:1},XYe="org.eclipse.elk.alg.common.nodespacing",lg="org.eclipse.elk.alg.common.nodespacing.cellsystem",F8="CENTER",KYe={216:1,337:1},Zge={3:1,4:1,5:1,592:1},ly="LEFT",fy="RIGHT",ewe="Vertical alignment cannot be null",nwe="BOTTOM",uF="org.eclipse.elk.alg.common.nodespacing.internal",vS="UNDEFINED",Ga=.01,yN="org.eclipse.elk.alg.common.nodespacing.internal.algorithm",VYe="LabelPlacer/lambda$0$Type",YYe="LabelPlacer/lambda$1$Type",QYe="portRatioOrPosition",J8="org.eclipse.elk.alg.common.overlaps",XZ="DOWN",ay="org.eclipse.elk.alg.common.spore",Z2={3:1,4:1,5:1,198:1},WYe={3:1,6:1,4:1,5:1,90:1,110:1},KZ="org.eclipse.elk.alg.force",twe="ComponentsProcessor",ZYe="ComponentsProcessor/1",iwe="ElkGraphImporter/lambda$0$Type",Qw={214:1},gv="org.eclipse.elk.core",kN="org.eclipse.elk.graph.properties",eQe="IPropertyHolder",jN="org.eclipse.elk.alg.force.graph",nQe="Component Layout",rwe="org.eclipse.elk.alg.force.model",vu="org.eclipse.elk.core.data",oF="org.eclipse.elk.force.model",cwe="org.eclipse.elk.force.iterations",uwe="org.eclipse.elk.force.repulsivePower",VZ="org.eclipse.elk.force.temperature",Eh=.001,YZ="org.eclipse.elk.force.repulsion",qa={148:1},yS="org.eclipse.elk.alg.force.options",H8=1.600000023841858,$o="org.eclipse.elk.force",EN="org.eclipse.elk.priority",em="org.eclipse.elk.spacing.nodeNode",QZ="org.eclipse.elk.spacing.edgeLabel",G8="org.eclipse.elk.aspectRatio",sF="org.eclipse.elk.randomSeed",kS="org.eclipse.elk.separateConnectedComponents",nm="org.eclipse.elk.padding",jS="org.eclipse.elk.interactive",WZ="org.eclipse.elk.portConstraints",lF="org.eclipse.elk.edgeLabels.inline",ES="org.eclipse.elk.omitNodeMicroLayout",q8="org.eclipse.elk.nodeSize.fixedGraphSize",hy="org.eclipse.elk.nodeSize.options",wv="org.eclipse.elk.nodeSize.constraints",U8="org.eclipse.elk.nodeLabels.placement",X8="org.eclipse.elk.portLabels.placement",SN="org.eclipse.elk.topdownLayout",xN="org.eclipse.elk.topdown.scaleFactor",AN="org.eclipse.elk.topdown.hierarchicalNodeWidth",MN="org.eclipse.elk.topdown.hierarchicalNodeAspectRatio",Ww="org.eclipse.elk.topdown.nodeType",owe="origin",tQe="random",iQe="boundingBox.upLeft",rQe="boundingBox.lowRight",swe="org.eclipse.elk.stress.fixed",lwe="org.eclipse.elk.stress.desiredEdgeLength",fwe="org.eclipse.elk.stress.dimension",awe="org.eclipse.elk.stress.epsilon",hwe="org.eclipse.elk.stress.iterationLimit",V0="org.eclipse.elk.stress",cQe="ELK Stress",dy="org.eclipse.elk.nodeSize.minimum",fF="org.eclipse.elk.alg.force.stress",uQe="Layered layout",by="org.eclipse.elk.alg.layered",TN="org.eclipse.elk.alg.layered.compaction.components",SS="org.eclipse.elk.alg.layered.compaction.oned",aF="org.eclipse.elk.alg.layered.compaction.oned.algs",fg="org.eclipse.elk.alg.layered.compaction.recthull",Ua="org.eclipse.elk.alg.layered.components",ma="NONE",ZZ="MODEL_ORDER",qu={3:1,6:1,4:1,10:1,5:1,126:1},oQe={3:1,6:1,4:1,5:1,135:1,90:1,110:1},hF="org.eclipse.elk.alg.layered.compound",Ai={43:1},Zu="org.eclipse.elk.alg.layered.graph",eee=" -> ",sQe="Not supported by LGraph",dwe="Port side is undefined",K8={3:1,6:1,4:1,5:1,323:1,135:1,90:1,110:1},Bd={3:1,6:1,4:1,5:1,135:1,199:1,209:1,90:1,110:1},lQe={3:1,6:1,4:1,5:1,135:1,2004:1,209:1,90:1,110:1},fQe=`([{"' \r +`,aQe=`)]}"' \r +`,hQe="The given string contains parts that cannot be parsed as numbers.",CN="org.eclipse.elk.core.math",dQe={3:1,4:1,140:1,213:1,414:1},bQe={3:1,4:1,104:1,213:1,414:1},zd="org.eclipse.elk.alg.layered.graph.transform",gQe="ElkGraphImporter",wQe="ElkGraphImporter/lambda$1$Type",pQe="ElkGraphImporter/lambda$2$Type",mQe="ElkGraphImporter/lambda$4$Type",Wn="org.eclipse.elk.alg.layered.intermediate",vQe="Node margin calculation",yQe="ONE_SIDED_GREEDY_SWITCH",kQe="TWO_SIDED_GREEDY_SWITCH",nee="No implementation is available for the layout processor ",tee="IntermediateProcessorStrategy",iee="Node '",jQe="FIRST_SEPARATE",EQe="LAST_SEPARATE",SQe="Odd port side processing",lr="org.eclipse.elk.alg.layered.intermediate.compaction",xS="org.eclipse.elk.alg.layered.intermediate.greedyswitch",t1="org.eclipse.elk.alg.layered.p3order.counting",AS={220:1},gy="org.eclipse.elk.alg.layered.intermediate.loops",gl="org.eclipse.elk.alg.layered.intermediate.loops.ordering",Y0="org.eclipse.elk.alg.layered.intermediate.loops.routing",dF="org.eclipse.elk.alg.layered.intermediate.preserveorder",Sh="org.eclipse.elk.alg.layered.intermediate.wrapping",Cu="org.eclipse.elk.alg.layered.options",ree="INTERACTIVE",bwe="GREEDY",xQe="DEPTH_FIRST",AQe="EDGE_LENGTH",MQe="SELF_LOOPS",TQe="firstTryWithInitialOrder",gwe="org.eclipse.elk.layered.directionCongruency",wwe="org.eclipse.elk.layered.feedbackEdges",bF="org.eclipse.elk.layered.interactiveReferencePoint",pwe="org.eclipse.elk.layered.mergeEdges",mwe="org.eclipse.elk.layered.mergeHierarchyEdges",vwe="org.eclipse.elk.layered.allowNonFlowPortsToSwitchSides",ywe="org.eclipse.elk.layered.portSortingStrategy",kwe="org.eclipse.elk.layered.thoroughness",jwe="org.eclipse.elk.layered.unnecessaryBendpoints",Ewe="org.eclipse.elk.layered.generatePositionAndLayerIds",ON="org.eclipse.elk.layered.cycleBreaking.strategy",NN="org.eclipse.elk.layered.layering.strategy",Swe="org.eclipse.elk.layered.layering.layerConstraint",xwe="org.eclipse.elk.layered.layering.layerChoiceConstraint",Awe="org.eclipse.elk.layered.layering.layerId",cee="org.eclipse.elk.layered.layering.minWidth.upperBoundOnWidth",uee="org.eclipse.elk.layered.layering.minWidth.upperLayerEstimationScalingFactor",oee="org.eclipse.elk.layered.layering.nodePromotion.strategy",see="org.eclipse.elk.layered.layering.nodePromotion.maxIterations",lee="org.eclipse.elk.layered.layering.coffmanGraham.layerBound",MS="org.eclipse.elk.layered.crossingMinimization.strategy",Mwe="org.eclipse.elk.layered.crossingMinimization.forceNodeModelOrder",fee="org.eclipse.elk.layered.crossingMinimization.hierarchicalSweepiness",aee="org.eclipse.elk.layered.crossingMinimization.semiInteractive",Twe="org.eclipse.elk.layered.crossingMinimization.inLayerPredOf",Cwe="org.eclipse.elk.layered.crossingMinimization.inLayerSuccOf",Owe="org.eclipse.elk.layered.crossingMinimization.positionChoiceConstraint",Nwe="org.eclipse.elk.layered.crossingMinimization.positionId",Dwe="org.eclipse.elk.layered.crossingMinimization.greedySwitch.activationThreshold",hee="org.eclipse.elk.layered.crossingMinimization.greedySwitch.type",gF="org.eclipse.elk.layered.crossingMinimization.greedySwitchHierarchical.type",pv="org.eclipse.elk.layered.nodePlacement.strategy",wF="org.eclipse.elk.layered.nodePlacement.favorStraightEdges",dee="org.eclipse.elk.layered.nodePlacement.bk.edgeStraightening",bee="org.eclipse.elk.layered.nodePlacement.bk.fixedAlignment",gee="org.eclipse.elk.layered.nodePlacement.linearSegments.deflectionDampening",wee="org.eclipse.elk.layered.nodePlacement.networkSimplex.nodeFlexibility",pee="org.eclipse.elk.layered.nodePlacement.networkSimplex.nodeFlexibility.default",Iwe="org.eclipse.elk.layered.edgeRouting.selfLoopDistribution",_we="org.eclipse.elk.layered.edgeRouting.selfLoopOrdering",pF="org.eclipse.elk.layered.edgeRouting.splines.mode",mF="org.eclipse.elk.layered.edgeRouting.splines.sloppy.layerSpacingFactor",mee="org.eclipse.elk.layered.edgeRouting.polyline.slopedEdgeZoneWidth",Lwe="org.eclipse.elk.layered.spacing.baseValue",Pwe="org.eclipse.elk.layered.spacing.edgeNodeBetweenLayers",$we="org.eclipse.elk.layered.spacing.edgeEdgeBetweenLayers",Rwe="org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers",Bwe="org.eclipse.elk.layered.priority.direction",zwe="org.eclipse.elk.layered.priority.shortness",Fwe="org.eclipse.elk.layered.priority.straightness",vee="org.eclipse.elk.layered.compaction.connectedComponents",Jwe="org.eclipse.elk.layered.compaction.postCompaction.strategy",Hwe="org.eclipse.elk.layered.compaction.postCompaction.constraints",vF="org.eclipse.elk.layered.highDegreeNodes.treatment",yee="org.eclipse.elk.layered.highDegreeNodes.threshold",kee="org.eclipse.elk.layered.highDegreeNodes.treeHeight",q1="org.eclipse.elk.layered.wrapping.strategy",yF="org.eclipse.elk.layered.wrapping.additionalEdgeSpacing",kF="org.eclipse.elk.layered.wrapping.correctionFactor",TS="org.eclipse.elk.layered.wrapping.cutting.strategy",jee="org.eclipse.elk.layered.wrapping.cutting.cuts",Eee="org.eclipse.elk.layered.wrapping.cutting.msd.freedom",jF="org.eclipse.elk.layered.wrapping.validify.strategy",EF="org.eclipse.elk.layered.wrapping.validify.forbiddenIndices",SF="org.eclipse.elk.layered.wrapping.multiEdge.improveCuts",xF="org.eclipse.elk.layered.wrapping.multiEdge.distancePenalty",See="org.eclipse.elk.layered.wrapping.multiEdge.improveWrappedEdges",xee="org.eclipse.elk.layered.layerUnzipping.strategy",Aee="org.eclipse.elk.layered.layerUnzipping.minimizeEdgeLength",Mee="org.eclipse.elk.layered.layerUnzipping.layerSplit",Tee="org.eclipse.elk.layered.layerUnzipping.resetOnLongEdges",Gwe="org.eclipse.elk.layered.edgeLabels.sideSelection",qwe="org.eclipse.elk.layered.edgeLabels.centerLabelPlacementStrategy",AF="org.eclipse.elk.layered.considerModelOrder.strategy",Uwe="org.eclipse.elk.layered.considerModelOrder.portModelOrder",DN="org.eclipse.elk.layered.considerModelOrder.noModelOrder",Cee="org.eclipse.elk.layered.considerModelOrder.components",Xwe="org.eclipse.elk.layered.considerModelOrder.longEdgeStrategy",Oee="org.eclipse.elk.layered.considerModelOrder.crossingCounterNodeInfluence",Nee="org.eclipse.elk.layered.considerModelOrder.crossingCounterPortInfluence",Dee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cycleBreakingId",Iee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.crossingMinimizationId",_ee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.componentGroupId",Kwe="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cbGroupOrderStrategy",Lee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cbPreferredSourceId",Pee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cbPreferredTargetId",Vwe="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cmGroupOrderStrategy",Ywe="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cmEnforcedGroupOrders",$ee="layering",CQe="layering.minWidth",OQe="layering.nodePromotion",V8="crossingMinimization",MF="org.eclipse.elk.hierarchyHandling",NQe="crossingMinimization.greedySwitch",DQe="nodePlacement",IQe="nodePlacement.bk",_Qe="edgeRouting",IN="org.eclipse.elk.edgeRouting",Xa="spacing",Qwe="priority",Wwe="compaction",LQe="compaction.postCompaction",PQe="Specifies whether and how post-process compaction is applied.",Zwe="highDegreeNodes",epe="wrapping",$Qe="wrapping.cutting",RQe="wrapping.validify",npe="wrapping.multiEdge",Ree="layerUnzipping",Bee="edgeLabels",CS="considerModelOrder",Y8="considerModelOrder.groupModelOrder",tpe="Group ID of the Node Type",ipe="org.eclipse.elk.spacing.commentComment",rpe="org.eclipse.elk.spacing.commentNode",cpe="org.eclipse.elk.spacing.componentComponent",upe="org.eclipse.elk.spacing.edgeEdge",zee="org.eclipse.elk.spacing.edgeNode",ope="org.eclipse.elk.spacing.labelLabel",spe="org.eclipse.elk.spacing.labelPortHorizontal",lpe="org.eclipse.elk.spacing.labelPortVertical",fpe="org.eclipse.elk.spacing.labelNode",ape="org.eclipse.elk.spacing.nodeSelfLoop",hpe="org.eclipse.elk.spacing.portPort",dpe="org.eclipse.elk.spacing.individual",bpe="org.eclipse.elk.port.borderOffset",gpe="org.eclipse.elk.noLayout",wpe="org.eclipse.elk.port.side",_N="org.eclipse.elk.debugMode",ppe="org.eclipse.elk.alignment",mpe="org.eclipse.elk.insideSelfLoops.activate",vpe="org.eclipse.elk.insideSelfLoops.yo",Fee="org.eclipse.elk.direction",ype="org.eclipse.elk.nodeLabels.padding",kpe="org.eclipse.elk.portLabels.nextToPortIfPossible",jpe="org.eclipse.elk.portLabels.treatAsGroup",Epe="org.eclipse.elk.portAlignment.default",Spe="org.eclipse.elk.portAlignment.north",xpe="org.eclipse.elk.portAlignment.south",Ape="org.eclipse.elk.portAlignment.west",Mpe="org.eclipse.elk.portAlignment.east",TF="org.eclipse.elk.contentAlignment",Tpe="org.eclipse.elk.junctionPoints",Cpe="org.eclipse.elk.edge.thickness",Ope="org.eclipse.elk.edgeLabels.placement",Npe="org.eclipse.elk.port.index",Dpe="org.eclipse.elk.commentBox",Ipe="org.eclipse.elk.hypernode",_pe="org.eclipse.elk.port.anchor",Jee="org.eclipse.elk.partitioning.activate",Hee="org.eclipse.elk.partitioning.partition",CF="org.eclipse.elk.position",Lpe="org.eclipse.elk.margins",Ppe="org.eclipse.elk.spacing.portsSurrounding",OF="org.eclipse.elk.interactiveLayout",Ru="org.eclipse.elk.core.util",$pe={3:1,4:1,5:1,590:1},BQe="NETWORK_SIMPLEX",Rpe="SIMPLE",uc={95:1,43:1},Zw="org.eclipse.elk.alg.layered.p1cycles",zQe="Depth-first cycle removal",FQe="Model order cycle breaking",U1="org.eclipse.elk.alg.layered.p2layers",Bpe={406:1,220:1},JQe={830:1,3:1,4:1},Ro="org.eclipse.elk.alg.layered.p3order",mv=17976931348623157e292,Gee=5e-324,Lc="org.eclipse.elk.alg.layered.p4nodes",HQe={3:1,4:1,5:1,838:1},xh=1e-5,Q0="org.eclipse.elk.alg.layered.p4nodes.bk",qee="org.eclipse.elk.alg.layered.p5edges",va="org.eclipse.elk.alg.layered.p5edges.orthogonal",Uee="org.eclipse.elk.alg.layered.p5edges.orthogonal.direction",Xee=1e-6,tm="org.eclipse.elk.alg.layered.p5edges.splines",Kee=.09999999999999998,NF=1e-8,GQe=4.71238898038469,qQe=1.5707963267948966,zpe=3.141592653589793,X1="org.eclipse.elk.alg.mrtree",Vee=.10000000149011612,DF="SUPER_ROOT",OS="org.eclipse.elk.alg.mrtree.graph",Fpe=-17976931348623157e292,go="org.eclipse.elk.alg.mrtree.intermediate",UQe="Processor compute fanout",IF={3:1,6:1,4:1,5:1,522:1,90:1,110:1},XQe="Set neighbors in level",LN="org.eclipse.elk.alg.mrtree.options",KQe="DESCENDANTS",Jpe="org.eclipse.elk.mrtree.compaction",Hpe="org.eclipse.elk.mrtree.edgeEndTextureLength",Gpe="org.eclipse.elk.mrtree.treeLevel",qpe="org.eclipse.elk.mrtree.positionConstraint",Upe="org.eclipse.elk.mrtree.weighting",Xpe="org.eclipse.elk.mrtree.edgeRoutingMode",Kpe="org.eclipse.elk.mrtree.searchOrder",VQe="Position Constraint",Bo="org.eclipse.elk.mrtree",YQe="org.eclipse.elk.tree",QQe="Processor arrange level",Q8="org.eclipse.elk.alg.mrtree.p2order",Qs="org.eclipse.elk.alg.mrtree.p4route",Vpe="org.eclipse.elk.alg.radial",ag=6.283185307179586,Ype="Before",_F="After",Qpe="org.eclipse.elk.alg.radial.intermediate",WQe="COMPACTION",Yee="org.eclipse.elk.alg.radial.intermediate.compaction",ZQe={3:1,4:1,5:1,90:1},Wpe="org.eclipse.elk.alg.radial.intermediate.optimization",Qee="No implementation is available for the layout option ",NS="org.eclipse.elk.alg.radial.options",eWe="CompactionStrategy",Zpe="org.eclipse.elk.radial.centerOnRoot",e2e="org.eclipse.elk.radial.orderId",n2e="org.eclipse.elk.radial.radius",LF="org.eclipse.elk.radial.rotate",Wee="org.eclipse.elk.radial.compactor",Zee="org.eclipse.elk.radial.compactionStepSize",t2e="org.eclipse.elk.radial.sorter",i2e="org.eclipse.elk.radial.wedgeCriteria",r2e="org.eclipse.elk.radial.optimizationCriteria",ene="org.eclipse.elk.radial.rotation.targetAngle",nne="org.eclipse.elk.radial.rotation.computeAdditionalWedgeSpace",c2e="org.eclipse.elk.radial.rotation.outgoingEdgeAngles",nWe="Compaction",u2e="rotation",Gl="org.eclipse.elk.radial",tWe="org.eclipse.elk.alg.radial.p1position.wedge",o2e="org.eclipse.elk.alg.radial.sorting",iWe=5.497787143782138,rWe=3.9269908169872414,cWe=2.356194490192345,uWe="org.eclipse.elk.alg.rectpacking",DS="org.eclipse.elk.alg.rectpacking.intermediate",tne="org.eclipse.elk.alg.rectpacking.options",s2e="org.eclipse.elk.rectpacking.trybox",l2e="org.eclipse.elk.rectpacking.currentPosition",f2e="org.eclipse.elk.rectpacking.desiredPosition",a2e="org.eclipse.elk.rectpacking.inNewRow",h2e="org.eclipse.elk.rectpacking.orderBySize",d2e="org.eclipse.elk.rectpacking.widthApproximation.strategy",b2e="org.eclipse.elk.rectpacking.widthApproximation.targetWidth",g2e="org.eclipse.elk.rectpacking.widthApproximation.optimizationGoal",w2e="org.eclipse.elk.rectpacking.widthApproximation.lastPlaceShift",p2e="org.eclipse.elk.rectpacking.packing.strategy",m2e="org.eclipse.elk.rectpacking.packing.compaction.rowHeightReevaluation",v2e="org.eclipse.elk.rectpacking.packing.compaction.iterations",y2e="org.eclipse.elk.rectpacking.whiteSpaceElimination.strategy",ine="widthApproximation",oWe="Compaction Strategy",sWe="packing.compaction",ms="org.eclipse.elk.rectpacking",W8="org.eclipse.elk.alg.rectpacking.p1widthapproximation",PF="org.eclipse.elk.alg.rectpacking.p2packing",lWe="No Compaction",k2e="org.eclipse.elk.alg.rectpacking.p3whitespaceelimination",PN="org.eclipse.elk.alg.rectpacking.util",$F="No implementation available for ",im="org.eclipse.elk.alg.spore",rm="org.eclipse.elk.alg.spore.options",ep="org.eclipse.elk.sporeCompaction",rne="org.eclipse.elk.underlyingLayoutAlgorithm",j2e="org.eclipse.elk.processingOrder.treeConstruction",E2e="org.eclipse.elk.processingOrder.spanningTreeCostFunction",cne="org.eclipse.elk.processingOrder.preferredRoot",une="org.eclipse.elk.processingOrder.rootSelection",one="org.eclipse.elk.structure.structureExtractionStrategy",S2e="org.eclipse.elk.compaction.compactionStrategy",x2e="org.eclipse.elk.compaction.orthogonal",A2e="org.eclipse.elk.overlapRemoval.maxIterations",M2e="org.eclipse.elk.overlapRemoval.runScanline",sne="processingOrder",fWe="overlapRemoval",Z8="org.eclipse.elk.sporeOverlap",aWe="org.eclipse.elk.alg.spore.p1structure",lne="org.eclipse.elk.alg.spore.p2processingorder",fne="org.eclipse.elk.alg.spore.p3execution",hWe="Topdown Layout",dWe="Invalid index: ",e7="org.eclipse.elk.core.alg",vv={342:1},cm={296:1},bWe="Make sure its type is registered with the ",T2e=" utility class.",n7="true",ane="false",gWe="Couldn't clone property '",np=.05,Oo="org.eclipse.elk.core.options",wWe=1.2999999523162842,tp="org.eclipse.elk.box",C2e="org.eclipse.elk.expandNodes",O2e="org.eclipse.elk.box.packingMode",pWe="org.eclipse.elk.algorithm",mWe="org.eclipse.elk.resolvedAlgorithm",N2e="org.eclipse.elk.bendPoints",yBn="org.eclipse.elk.labelManager",vWe="org.eclipse.elk.softwrappingFuzziness",yWe="org.eclipse.elk.scaleFactor",kWe="org.eclipse.elk.childAreaWidth",jWe="org.eclipse.elk.childAreaHeight",EWe="org.eclipse.elk.animate",SWe="org.eclipse.elk.animTimeFactor",xWe="org.eclipse.elk.layoutAncestors",AWe="org.eclipse.elk.maxAnimTime",MWe="org.eclipse.elk.minAnimTime",TWe="org.eclipse.elk.progressBar",CWe="org.eclipse.elk.validateGraph",OWe="org.eclipse.elk.validateOptions",NWe="org.eclipse.elk.zoomToFit",DWe="org.eclipse.elk.json.shapeCoords",IWe="org.eclipse.elk.json.edgeCoords",kBn="org.eclipse.elk.font.name",_We="org.eclipse.elk.font.size",hne="org.eclipse.elk.topdown.sizeCategories",D2e="org.eclipse.elk.topdown.sizeCategoriesHierarchicalNodeWeight",dne="org.eclipse.elk.topdown.sizeApproximator",I2e="org.eclipse.elk.topdown.scaleCap",LWe="org.eclipse.elk.edge.type",PWe="partitioning",$We="nodeLabels",RF="portAlignment",bne="nodeSize",gne="port",_2e="portLabels",t7="topdown",RWe="insideSelfLoops",L2e="INHERIT",i7="org.eclipse.elk.fixed",BF="org.eclipse.elk.random",zF={3:1,35:1,23:1,521:1,288:1},BWe="port must have a parent node to calculate the port side",zWe="The edge needs to have exactly one edge section. Found: ",IS="org.eclipse.elk.core.util.adapters",ql="org.eclipse.emf.ecore",yv="org.eclipse.elk.graph",FWe="EMapPropertyHolder",JWe="ElkBendPoint",HWe="ElkGraphElement",GWe="ElkConnectableShape",P2e="ElkEdge",qWe="ElkEdgeSection",UWe="EModelElement",XWe="ENamedElement",$2e="ElkLabel",R2e="ElkNode",B2e="ElkPort",KWe={94:1,93:1},wy="org.eclipse.emf.common.notify.impl",W0="The feature '",_S="' is not a valid changeable feature",VWe="Expecting null",wne="' is not a valid feature",YWe="The feature ID",QWe=" is not a valid feature ID",Bu=32768,WWe={109:1,94:1,93:1,57:1,52:1,100:1},Fn="org.eclipse.emf.ecore.impl",hg="org.eclipse.elk.graph.impl",LS="Recursive containment not allowed for ",r7="The datatype '",ip="' is not a valid classifier",pne="The value '",kv={195:1,3:1,4:1},mne="The class '",c7="http://www.eclipse.org/elk/ElkGraph",z2e="property",PS="value",vne="source",ZWe="properties",eZe="identifier",yne="height",kne="width",jne="parent",Ene="text",Sne="children",nZe="hierarchical",F2e="sources",xne="targets",Ane="sections",FF="bendPoints",J2e="outgoingShape",H2e="incomingShape",G2e="outgoingSections",q2e="incomingSections",yc="org.eclipse.emf.common.util",U2e="Severe implementation error in the Json to ElkGraph importer.",Ah="id",Qr="org.eclipse.elk.graph.json",u7="Unhandled parameter types: ",tZe="startPoint",iZe="An edge must have at least one source and one target (edge id: '",o7="').",rZe="Referenced edge section does not exist: ",cZe=" (edge id: '",X2e="target",uZe="sourcePoint",oZe="targetPoint",JF="group",ui="name",sZe="connectableShape cannot be null",lZe="edge cannot be null",fZe="Passed edge is not 'simple'.",HF="org.eclipse.elk.graph.util",$N="The 'no duplicates' constraint is violated",Mne="targetIndex=",dg=", size=",Tne="sourceIndex=",Mh={3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,67:1,61:1},Cne={3:1,4:1,20:1,31:1,56:1,18:1,50:1,16:1,59:1,71:1,67:1,61:1,585:1},GF="logging",aZe="measureExecutionTime",hZe="parser.parse.1",dZe="parser.parse.2",qF="parser.next.1",One="parser.next.2",bZe="parser.next.3",gZe="parser.next.4",bg="parser.factor.1",K2e="parser.factor.2",wZe="parser.factor.3",pZe="parser.factor.4",mZe="parser.factor.5",vZe="parser.factor.6",yZe="parser.atom.1",kZe="parser.atom.2",jZe="parser.atom.3",V2e="parser.atom.4",Nne="parser.atom.5",Y2e="parser.cc.1",UF="parser.cc.2",EZe="parser.cc.3",SZe="parser.cc.5",Q2e="parser.cc.6",W2e="parser.cc.7",Dne="parser.cc.8",xZe="parser.ope.1",AZe="parser.ope.2",MZe="parser.ope.3",Fd="parser.descape.1",TZe="parser.descape.2",CZe="parser.descape.3",OZe="parser.descape.4",NZe="parser.descape.5",Ul="parser.process.1",DZe="parser.quantifier.1",IZe="parser.quantifier.2",_Ze="parser.quantifier.3",LZe="parser.quantifier.4",Z2e="parser.quantifier.5",PZe="org.eclipse.emf.common.notify",eme={415:1,676:1},$Ze={3:1,4:1,20:1,31:1,56:1,18:1,16:1,71:1,61:1},RN={373:1,151:1},$S="index=",Ine={3:1,4:1,5:1,129:1},RZe={3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,61:1},nme={3:1,6:1,4:1,5:1,198:1},BZe={3:1,4:1,5:1,175:1,374:1},qf=1024,zZe=";/?:@&=+$,",FZe="invalid authority: ",JZe="EAnnotation",HZe="ETypedElement",GZe="EStructuralFeature",qZe="EAttribute",UZe="EClassifier",XZe="EEnumLiteral",KZe="EGenericType",VZe="EOperation",YZe="EParameter",QZe="EReference",WZe="ETypeParameter",$i="org.eclipse.emf.ecore.util",_ne={77:1},tme={3:1,20:1,18:1,16:1,61:1,586:1,77:1,72:1,98:1},ZZe="org.eclipse.emf.ecore.util.FeatureMap$Entry",as=8192,RS="byte",XF="char",BS="double",zS="float",FS="int",JS="long",HS="short",een="java.lang.Object",jv={3:1,4:1,5:1,255:1},ime={3:1,4:1,5:1,678:1},nen={3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,67:1,61:1,72:1},lu={3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,67:1,61:1,77:1,72:1,98:1},BN="mixed",Ut="http:///org/eclipse/emf/ecore/util/ExtendedMetaData",af="kind",ten={3:1,4:1,5:1,679:1},rme={3:1,4:1,20:1,31:1,56:1,18:1,16:1,71:1,61:1,77:1,72:1,98:1},KF={20:1,31:1,56:1,18:1,16:1,61:1,72:1},VF={50:1,128:1,287:1},YF={75:1,344:1},QF="The value of type '",WF="' must be of type '",Ev=1306,hf="http://www.eclipse.org/emf/2002/Ecore",ZF=-32768,rp="constraints",oc="baseType",ien="getEStructuralFeature",ren="getFeatureID",GS="feature",cen="getOperationID",cme="operation",uen="defaultValue",oen="eTypeParameters",sen="isInstance",len="getEEnumLiteral",fen="eContainingClass",ii={58:1},aen={3:1,4:1,5:1,122:1},hen="org.eclipse.emf.ecore.resource",den={94:1,93:1,588:1,1996:1},Lne="org.eclipse.emf.ecore.resource.impl",ume="unspecified",zN="simple",eJ="attribute",ben="attributeWildcard",nJ="element",Pne="elementWildcard",ya="collapse",$ne="itemType",tJ="namespace",FN="##targetNamespace",df="whiteSpace",ome="wildcards",gg="http://www.eclipse.org/emf/2003/XMLType",Rne="##any",s7="uninitialized",JN="The multiplicity constraint is violated",iJ="org.eclipse.emf.ecore.xml.type",gen="ProcessingInstruction",wen="SimpleAnyType",pen="XMLTypeDocumentRoot",kr="org.eclipse.emf.ecore.xml.type.impl",HN="INF",men="processing",ven="ENTITIES_._base",sme="minLength",lme="ENTITY",rJ="NCName",yen="IDREFS_._base",fme="integer",Bne="token",zne="pattern",ken="[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*",ame="\\i\\c*",jen="[\\i-[:]][\\c-[:]]*",Een="nonPositiveInteger",GN="maxInclusive",hme="NMTOKEN",Sen="NMTOKENS_._base",dme="nonNegativeInteger",qN="minInclusive",xen="normalizedString",Aen="unsignedByte",Men="unsignedInt",Ten="18446744073709551615",Cen="unsignedShort",Oen="processingInstruction",Jd="org.eclipse.emf.ecore.xml.type.internal",l7=1114111,Nen="Internal Error: shorthands: \\u",qS="xml:isDigit",Fne="xml:isWord",Jne="xml:isSpace",Hne="xml:isNameChar",Gne="xml:isInitialNameChar",Den="09٠٩۰۹०९০৯੦੯૦૯୦୯௧௯౦౯೦೯൦൯๐๙໐໙༠༩",Ien="AZazÀÖØöøıĴľŁňŊžƀǃǍǰǴǵǺȗɐʨʻˁΆΆΈΊΌΌΎΡΣώϐϖϚϚϜϜϞϞϠϠϢϳЁЌЎяёќўҁҐӄӇӈӋӌӐӫӮӵӸӹԱՖՙՙաֆאתװײءغفيٱڷںھۀێېۓەەۥۦअहऽऽक़ॡঅঌএঐওনপরললশহড়ঢ়য়ৡৰৱਅਊਏਐਓਨਪਰਲਲ਼ਵਸ਼ਸਹਖ਼ੜਫ਼ਫ਼ੲੴઅઋઍઍએઑઓનપરલળવહઽઽૠૠଅଌଏଐଓନପରଲଳଶହଽଽଡ଼ଢ଼ୟୡஅஊஎஐஒகஙசஜஜஞடணதநபமவஷஹఅఌఎఐఒనపళవహౠౡಅಌಎಐಒನಪಳವಹೞೞೠೡഅഌഎഐഒനപഹൠൡกฮะะาำเๅກຂຄຄງຈຊຊຍຍດທນຟມຣລລວວສຫອຮະະາຳຽຽເໄཀཇཉཀྵႠჅაჶᄀᄀᄂᄃᄅᄇᄉᄉᄋᄌᄎᄒᄼᄼᄾᄾᅀᅀᅌᅌᅎᅎᅐᅐᅔᅕᅙᅙᅟᅡᅣᅣᅥᅥᅧᅧᅩᅩᅭᅮᅲᅳᅵᅵᆞᆞᆨᆨᆫᆫᆮᆯᆷᆸᆺᆺᆼᇂᇫᇫᇰᇰᇹᇹḀẛẠỹἀἕἘἝἠὅὈὍὐὗὙὙὛὛὝὝὟώᾀᾴᾶᾼιιῂῄῆῌῐΐῖΊῠῬῲῴῶῼΩΩKÅ℮℮ↀↂ〇〇〡〩ぁゔァヺㄅㄬ一龥가힣",_en="Private Use",qne="ASSIGNED",Une="\0€ÿĀſƀɏɐʯʰ˿̀ͯͰϿЀӿ԰֏֐׿؀ۿ܀ݏހ޿ऀॿঀ৿਀੿઀૿଀୿஀௿ఀ౿ಀ೿ഀൿ඀෿฀๿຀໿ༀ࿿က႟Ⴀჿᄀᇿሀ፿Ꭰ᏿᐀ᙿ ᚟ᚠ᛿ក៿᠀᢯Ḁỿἀ῿ ⁰₟₠⃏⃐⃿℀⅏⅐↏←⇿∀⋿⌀⏿␀␿⑀⑟①⓿─╿▀▟■◿☀⛿✀➿⠀⣿⺀⻿⼀⿟⿰⿿ 〿぀ゟ゠ヿ㄀ㄯ㄰㆏㆐㆟ㆠㆿ㈀㋿㌀㏿㐀䶵一鿿ꀀ꒏꒐꓏가힣豈﫿ffﭏﭐ﷿︠︯︰﹏﹐﹯ﹰ﻾\uFEFF\uFEFF＀￯",bme="UNASSIGNED",f7={3:1,121:1},Len="org.eclipse.emf.ecore.xml.type.util",cJ={3:1,4:1,5:1,376:1},gme="org.eclipse.xtext.xbase.lib",Pen="Cannot add elements to a Range",$en="Cannot set elements in a Range",Ren="Cannot remove elements from a Range",Ben="user.agent",s,uJ,Xne;k.goog=k.goog||{},k.goog.global=k.goog.global||k,uJ={},m(1,null,{},U),s.Fb=function(n){return bCe(this,n)},s.Gb=function(){return this.Pm},s.Hb=function(){return vw(this)},s.Ib=function(){var n;return Db(Us(this))+"@"+(n=Oi(this)>>>0,n.toString(16))},s.equals=function(e){return this.Fb(e)},s.hashCode=function(){return this.Hb()},s.toString=function(){return this.Ib()};var zen,Fen,Jen;m(298,1,{298:1,2086:1},g1e),s.te=function(n){var t;return t=new g1e,t.i=4,n>1?t.c=H_e(this,n-1):t.c=this,t},s.ue=function(){return M1(this),this.b},s.ve=function(){return Db(this)},s.we=function(){return M1(this),this.k},s.xe=function(){return(this.i&4)!=0},s.ye=function(){return(this.i&1)!=0},s.Ib=function(){return xhe(this)},s.i=0;var Ar=v(Tu,"Object",1),wme=v(Tu,"Class",298);m(2058,1,lN),v(fN,"Optional",2058),m(1160,2058,lN,G),s.Fb=function(n){return n===this},s.Hb=function(){return 2040732332},s.Ib=function(){return"Optional.absent()"},s.Jb=function(n){return Nt(n),mj(),Kne};var Kne;v(fN,"Absent",1160),m(627,1,{},NX),v(fN,"Joiner",627);var jBn=Hi(fN,"Predicate");m(577,1,{178:1,577:1,3:1,48:1},NT),s.Mb=function(n){return Jze(this,n)},s.Lb=function(n){return Jze(this,n)},s.Fb=function(n){var t;return X(n,577)?(t=u(n,577),abe(this.a,t.a)):!1},s.Hb=function(){return y1e(this.a)+306654252},s.Ib=function(){return jTn(this.a)},v(fN,"Predicates/AndPredicate",577),m(411,2058,{411:1,3:1},W6),s.Fb=function(n){var t;return X(n,411)?(t=u(n,411),di(this.a,t.a)):!1},s.Hb=function(){return 1502476572+Oi(this.a)},s.Ib=function(){return sYe+this.a+")"},s.Jb=function(n){return new W6(CR(n.Kb(this.a),"the Function passed to Optional.transform() must not return null."))},v(fN,"Present",411),m(204,1,I8),s.Nb=function(n){Zr(this,n)},s.Qb=function(){rAe()},v(dn,"UnmodifiableIterator",204),m(2038,204,_8),s.Qb=function(){rAe()},s.Rb=function(n){throw $(new _t)},s.Wb=function(n){throw $(new _t)},v(dn,"UnmodifiableListIterator",2038),m(392,2038,_8),s.Ob=function(){return this.b0},s.Pb=function(){if(this.b>=this.c)throw $(new au);return this.Xb(this.b++)},s.Tb=function(){return this.b},s.Ub=function(){if(this.b<=0)throw $(new au);return this.Xb(--this.b)},s.Vb=function(){return this.b-1},s.b=0,s.c=0,v(dn,"AbstractIndexedListIterator",392),m(702,204,I8),s.Ob=function(){return LY(this)},s.Pb=function(){return vhe(this)},s.e=1,v(dn,"AbstractIterator",702),m(2046,1,{229:1}),s.Zb=function(){var n;return n=this.f,n||(this.f=this.ac())},s.Fb=function(n){return iQ(this,n)},s.Hb=function(){return Oi(this.Zb())},s.dc=function(){return this.gc()==0},s.ec=function(){return S4(this)},s.Ib=function(){return su(this.Zb())},v(dn,"AbstractMultimap",2046),m(730,2046,og),s.$b=function(){mB(this)},s._b=function(n){return kAe(this,n)},s.ac=function(){return new g9(this,this.c)},s.ic=function(n){return this.hc()},s.bc=function(){return new R3(this,this.c)},s.jc=function(){return this.mc(this.hc())},s.kc=function(){return new Hxe(this)},s.lc=function(){return fW(this.c.vc().Lc(),new W,64,this.d)},s.cc=function(n){return pi(this,n)},s.fc=function(n){return SO(this,n)},s.gc=function(){return this.d},s.mc=function(n){return jn(),new Hr(n)},s.nc=function(){return new Jxe(this)},s.oc=function(){return fW(this.c.Bc().Lc(),new ie,64,this.d)},s.pc=function(n,t){return new ZR(this,n,t,null)},s.d=0,v(dn,"AbstractMapBasedMultimap",730),m(1661,730,og),s.hc=function(){return new xo(this.a)},s.jc=function(){return jn(),jn(),Sc},s.cc=function(n){return u(pi(this,n),16)},s.fc=function(n){return u(SO(this,n),16)},s.Zb=function(){return O4(this)},s.Fb=function(n){return iQ(this,n)},s.qc=function(n){return u(pi(this,n),16)},s.rc=function(n){return u(SO(this,n),16)},s.mc=function(n){return OR(u(n,16))},s.pc=function(n,t){return WLe(this,n,u(t,16),null)},v(dn,"AbstractListMultimap",1661),m(736,1,Fr),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.c.Ob()||this.e.Ob()},s.Pb=function(){var n;return this.e.Ob()||(n=u(this.c.Pb(),45),this.b=n.jd(),this.a=u(n.kd(),18),this.e=this.a.Jc()),this.sc(this.b,this.e.Pb())},s.Qb=function(){this.e.Qb(),u(uf(this.a),18).dc()&&this.c.Qb(),--this.d.d},v(dn,"AbstractMapBasedMultimap/Itr",736),m(1098,736,Fr,Jxe),s.sc=function(n,t){return t},v(dn,"AbstractMapBasedMultimap/1",1098),m(1099,1,{},ie),s.Kb=function(n){return u(n,18).Lc()},v(dn,"AbstractMapBasedMultimap/1methodref$spliterator$Type",1099),m(1100,736,Fr,Hxe),s.sc=function(n,t){return new bw(n,t)},v(dn,"AbstractMapBasedMultimap/2",1100);var pme=Hi(mt,"Map");m(2027,1,Vw),s.wc=function(n){bO(this,n)},s.$b=function(){this.vc().$b()},s.tc=function(n){return UQ(this,n)},s._b=function(n){return!!l0e(this,n,!1)},s.uc=function(n){var t,i,r;for(i=this.vc().Jc();i.Ob();)if(t=u(i.Pb(),45),r=t.kd(),ue(n)===ue(r)||n!=null&&di(n,r))return!0;return!1},s.Fb=function(n){var t,i,r;if(n===this)return!0;if(!X(n,92)||(r=u(n,92),this.gc()!=r.gc()))return!1;for(i=r.vc().Jc();i.Ob();)if(t=u(i.Pb(),45),!this.tc(t))return!1;return!0},s.xc=function(n){return du(l0e(this,n,!1))},s.Hb=function(){return a1e(this.vc())},s.dc=function(){return this.gc()==0},s.ec=function(){return new nt(this)},s.yc=function(n,t){throw $(new gd("Put not supported on this map"))},s.zc=function(n){xE(this,n)},s.Ac=function(n){return du(l0e(this,n,!0))},s.gc=function(){return this.vc().gc()},s.Ib=function(){return oGe(this)},s.Bc=function(){return new ct(this)},v(mt,"AbstractMap",2027),m(2047,2027,Vw),s.bc=function(){return new VP(this)},s.vc=function(){return zDe(this)},s.ec=function(){var n;return n=this.g,n||(this.g=this.bc())},s.Bc=function(){var n;return n=this.i,n||(this.i=new hMe(this))},v(dn,"Maps/ViewCachingAbstractMap",2047),m(395,2047,Vw,g9),s.xc=function(n){return m8n(this,n)},s.Ac=function(n){return Ckn(this,n)},s.$b=function(){this.d==this.e.c?this.e.$b():uR(new yfe(this))},s._b=function(n){return yFe(this.d,n)},s.Dc=function(){return new dP(this)},s.Cc=function(){return this.Dc()},s.Fb=function(n){return this===n||di(this.d,n)},s.Hb=function(){return Oi(this.d)},s.ec=function(){return this.e.ec()},s.gc=function(){return this.d.gc()},s.Ib=function(){return su(this.d)},v(dn,"AbstractMapBasedMultimap/AsMap",395);var Xl=Hi(Tu,"Iterable");m(31,1,Y2),s.Ic=function(n){rc(this,n)},s.Lc=function(){return new pn(this,0)},s.Mc=function(){return new wn(null,this.Lc())},s.Ec=function(n){throw $(new gd("Add not supported on this collection"))},s.Fc=function(n){return fc(this,n)},s.$b=function(){rae(this)},s.Gc=function(n){return P2(this,n,!1)},s.Hc=function(n){return yO(this,n)},s.dc=function(){return this.gc()==0},s.Kc=function(n){return P2(this,n,!0)},s.Nc=function(){return Ofe(this)},s.Oc=function(n){return GE(this,n)},s.Ib=function(){return Fa(this)},v(mt,"AbstractCollection",31);var bf=Hi(mt,"Set");m(Ha,31,fs),s.Lc=function(){return new pn(this,1)},s.Fb=function(n){return jJe(this,n)},s.Hb=function(){return a1e(this)},v(mt,"AbstractSet",Ha),m(2030,Ha,fs),v(dn,"Sets/ImprovedAbstractSet",2030),m(2031,2030,fs),s.$b=function(){this.Pc().$b()},s.Gc=function(n){return iJe(this,n)},s.dc=function(){return this.Pc().dc()},s.Kc=function(n){var t;return this.Gc(n)&&X(n,45)?(t=u(n,45),this.Pc().ec().Kc(t.jd())):!1},s.gc=function(){return this.Pc().gc()},v(dn,"Maps/EntrySet",2031),m(1096,2031,fs,dP),s.Gc=function(n){return $1e(this.a.d.vc(),n)},s.Jc=function(){return new yfe(this.a)},s.Pc=function(){return this.a},s.Kc=function(n){var t;return $1e(this.a.d.vc(),n)?(t=u(uf(u(n,45)),45),t9n(this.a.e,t.jd()),!0):!1},s.Lc=function(){return NC(this.a.d.vc().Lc(),new bP(this.a))},v(dn,"AbstractMapBasedMultimap/AsMap/AsMapEntries",1096),m(1097,1,{},bP),s.Kb=function(n){return LPe(this.a,u(n,45))},v(dn,"AbstractMapBasedMultimap/AsMap/AsMapEntries/0methodref$wrapEntry$Type",1097),m(734,1,Fr,yfe),s.Nb=function(n){Zr(this,n)},s.Pb=function(){var n;return n=u(this.b.Pb(),45),this.a=u(n.kd(),18),LPe(this.c,n)},s.Ob=function(){return this.b.Ob()},s.Qb=function(){x9(!!this.a),this.b.Qb(),this.c.e.d-=this.a.gc(),this.a.$b(),this.a=null},v(dn,"AbstractMapBasedMultimap/AsMap/AsMapIterator",734),m(530,2030,fs,VP),s.$b=function(){this.b.$b()},s.Gc=function(n){return this.b._b(n)},s.Ic=function(n){Nt(n),this.b.wc(new cj(n))},s.dc=function(){return this.b.dc()},s.Jc=function(){return new vj(this.b.vc().Jc())},s.Kc=function(n){return this.b._b(n)?(this.b.Ac(n),!0):!1},s.gc=function(){return this.b.gc()},v(dn,"Maps/KeySet",530),m(332,530,fs,R3),s.$b=function(){var n;uR((n=this.b.vc().Jc(),new Uoe(this,n)))},s.Hc=function(n){return this.b.ec().Hc(n)},s.Fb=function(n){return this===n||di(this.b.ec(),n)},s.Hb=function(){return Oi(this.b.ec())},s.Jc=function(){var n;return n=this.b.vc().Jc(),new Uoe(this,n)},s.Kc=function(n){var t,i;return i=0,t=u(this.b.Ac(n),18),t&&(i=t.gc(),t.$b(),this.a.d-=i),i>0},s.Lc=function(){return this.b.ec().Lc()},v(dn,"AbstractMapBasedMultimap/KeySet",332),m(735,1,Fr,Uoe),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.c.Ob()},s.Pb=function(){return this.a=u(this.c.Pb(),45),this.a.jd()},s.Qb=function(){var n;x9(!!this.a),n=u(this.a.kd(),18),this.c.Qb(),this.b.a.d-=n.gc(),n.$b(),this.a=null},v(dn,"AbstractMapBasedMultimap/KeySet/1",735),m(489,395,{92:1,134:1},SC),s.bc=function(){return this.Qc()},s.ec=function(){return this.Sc()},s.Qc=function(){return new WT(this.c,this.Uc())},s.Rc=function(){return this.Uc().Rc()},s.Sc=function(){var n;return n=this.b,n||(this.b=this.Qc())},s.Tc=function(){return this.Uc().Tc()},s.Uc=function(){return u(this.d,134)},v(dn,"AbstractMapBasedMultimap/SortedAsMap",489),m(437,489,$ge,Zj),s.bc=function(){return new w9(this.a,u(u(this.d,134),138))},s.Qc=function(){return new w9(this.a,u(u(this.d,134),138))},s.ec=function(){var n;return n=this.b,u(n||(this.b=new w9(this.a,u(u(this.d,134),138))),277)},s.Sc=function(){var n;return n=this.b,u(n||(this.b=new w9(this.a,u(u(this.d,134),138))),277)},s.Uc=function(){return u(u(this.d,134),138)},s.Vc=function(n){return u(u(this.d,134),138).Vc(n)},s.Wc=function(n){return u(u(this.d,134),138).Wc(n)},s.Xc=function(n,t){return new Zj(this.a,u(u(this.d,134),138).Xc(n,t))},s.Yc=function(n){return u(u(this.d,134),138).Yc(n)},s.Zc=function(n){return u(u(this.d,134),138).Zc(n)},s.$c=function(n,t){return new Zj(this.a,u(u(this.d,134),138).$c(n,t))},v(dn,"AbstractMapBasedMultimap/NavigableAsMap",437),m(488,332,lYe,WT),s.Lc=function(){return this.b.ec().Lc()},v(dn,"AbstractMapBasedMultimap/SortedKeySet",488),m(394,488,Rge,w9),v(dn,"AbstractMapBasedMultimap/NavigableKeySet",394),m(539,31,Y2,ZR),s.Ec=function(n){var t,i;return Ds(this),i=this.d.dc(),t=this.d.Ec(n),t&&(++this.f.d,i&&TC(this)),t},s.Fc=function(n){var t,i,r;return n.dc()?!1:(r=(Ds(this),this.d.gc()),t=this.d.Fc(n),t&&(i=this.d.gc(),this.f.d+=i-r,r==0&&TC(this)),t)},s.$b=function(){var n;n=(Ds(this),this.d.gc()),n!=0&&(this.d.$b(),this.f.d-=n,hR(this))},s.Gc=function(n){return Ds(this),this.d.Gc(n)},s.Hc=function(n){return Ds(this),this.d.Hc(n)},s.Fb=function(n){return n===this?!0:(Ds(this),di(this.d,n))},s.Hb=function(){return Ds(this),Oi(this.d)},s.Jc=function(){return Ds(this),new rfe(this)},s.Kc=function(n){var t;return Ds(this),t=this.d.Kc(n),t&&(--this.f.d,hR(this)),t},s.gc=function(){return tCe(this)},s.Lc=function(){return Ds(this),this.d.Lc()},s.Ib=function(){return Ds(this),su(this.d)},v(dn,"AbstractMapBasedMultimap/WrappedCollection",539);var wl=Hi(mt,"List");m(732,539,{20:1,31:1,18:1,16:1},Nfe),s.gd=function(n){Vb(this,n)},s.Lc=function(){return Ds(this),this.d.Lc()},s._c=function(n,t){var i;Ds(this),i=this.d.dc(),u(this.d,16)._c(n,t),++this.a.d,i&&TC(this)},s.ad=function(n,t){var i,r,c;return t.dc()?!1:(c=(Ds(this),this.d.gc()),i=u(this.d,16).ad(n,t),i&&(r=this.d.gc(),this.a.d+=r-c,c==0&&TC(this)),i)},s.Xb=function(n){return Ds(this),u(this.d,16).Xb(n)},s.bd=function(n){return Ds(this),u(this.d,16).bd(n)},s.cd=function(){return Ds(this),new ICe(this)},s.dd=function(n){return Ds(this),new ZIe(this,n)},s.ed=function(n){var t;return Ds(this),t=u(this.d,16).ed(n),--this.a.d,hR(this),t},s.fd=function(n,t){return Ds(this),u(this.d,16).fd(n,t)},s.hd=function(n,t){return Ds(this),WLe(this.a,this.e,u(this.d,16).hd(n,t),this.b?this.b:this)},v(dn,"AbstractMapBasedMultimap/WrappedList",732),m(1095,732,{20:1,31:1,18:1,16:1,59:1},kOe),v(dn,"AbstractMapBasedMultimap/RandomAccessWrappedList",1095),m(619,1,Fr,rfe),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return P9(this),this.b.Ob()},s.Pb=function(){return P9(this),this.b.Pb()},s.Qb=function(){rOe(this)},v(dn,"AbstractMapBasedMultimap/WrappedCollection/WrappedIterator",619),m(733,619,Qh,ICe,ZIe),s.Qb=function(){rOe(this)},s.Rb=function(n){var t;t=tCe(this.a)==0,(P9(this),u(this.b,128)).Rb(n),++this.a.a.d,t&&TC(this.a)},s.Sb=function(){return(P9(this),u(this.b,128)).Sb()},s.Tb=function(){return(P9(this),u(this.b,128)).Tb()},s.Ub=function(){return(P9(this),u(this.b,128)).Ub()},s.Vb=function(){return(P9(this),u(this.b,128)).Vb()},s.Wb=function(n){(P9(this),u(this.b,128)).Wb(n)},v(dn,"AbstractMapBasedMultimap/WrappedList/WrappedListIterator",733),m(731,539,lYe,Sle),s.Lc=function(){return Ds(this),this.d.Lc()},v(dn,"AbstractMapBasedMultimap/WrappedSortedSet",731),m(1094,731,Rge,TCe),v(dn,"AbstractMapBasedMultimap/WrappedNavigableSet",1094),m(1093,539,fs,XOe),s.Lc=function(){return Ds(this),this.d.Lc()},v(dn,"AbstractMapBasedMultimap/WrappedSet",1093),m(1102,1,{},W),s.Kb=function(n){return h9n(u(n,45))},v(dn,"AbstractMapBasedMultimap/lambda$1$Type",1102),m(1101,1,{},Z6),s.Kb=function(n){return new bw(this.a,n)},v(dn,"AbstractMapBasedMultimap/lambda$2$Type",1101);var wg=Hi(mt,"Map/Entry");m(358,1,hZ),s.Fb=function(n){var t;return X(n,45)?(t=u(n,45),T1(this.jd(),t.jd())&&T1(this.kd(),t.kd())):!1},s.Hb=function(){var n,t;return n=this.jd(),t=this.kd(),(n==null?0:Oi(n))^(t==null?0:Oi(t))},s.ld=function(n){throw $(new _t)},s.Ib=function(){return this.jd()+"="+this.kd()},v(dn,fYe,358),m(U0,31,Y2),s.$b=function(){this.md().$b()},s.Gc=function(n){var t;return X(n,45)?(t=u(n,45),_yn(this.md(),t.jd(),t.kd())):!1},s.Kc=function(n){var t;return X(n,45)?(t=u(n,45),$Le(this.md(),t.jd(),t.kd())):!1},s.gc=function(){return this.md().d},v(dn,"Multimaps/Entries",U0),m(737,U0,Y2,E1),s.Jc=function(){return this.a.kc()},s.md=function(){return this.a},s.Lc=function(){return this.a.lc()},v(dn,"AbstractMultimap/Entries",737),m(738,737,fs,xoe),s.Lc=function(){return this.a.lc()},s.Fb=function(n){return C0e(this,n)},s.Hb=function(){return FBe(this)},v(dn,"AbstractMultimap/EntrySet",738),m(739,31,Y2,DT),s.$b=function(){this.a.$b()},s.Gc=function(n){return xkn(this.a,n)},s.Jc=function(){return this.a.nc()},s.gc=function(){return this.a.d},s.Lc=function(){return this.a.oc()},v(dn,"AbstractMultimap/Values",739),m(2049,31,{833:1,20:1,31:1,18:1}),s.Ic=function(n){Nt(n),z3(this).Ic(new VU(n))},s.Lc=function(){var n;return n=z3(this).Lc(),fW(n,new Me,64|n.wd()&1296,this.a.d)},s.Ec=function(n){return Noe(),!0},s.Fc=function(n){return Nt(this),Nt(n),X(n,540)?Jyn(u(n,833)):!n.dc()&&AY(this,n.Jc())},s.Gc=function(n){var t;return t=u(L2(O4(this.a),n),18),(t?t.gc():0)>0},s.Fb=function(n){return vOn(this,n)},s.Hb=function(){return Oi(z3(this))},s.dc=function(){return z3(this).dc()},s.Kc=function(n){return Eqe(this,n,1)>0},s.Ib=function(){return su(z3(this))},v(dn,"AbstractMultiset",2049),m(2051,2030,fs),s.$b=function(){mB(this.a.a)},s.Gc=function(n){var t,i;return X(n,490)?(i=u(n,416),u(i.a.kd(),18).gc()<=0?!1:(t=cLe(this.a,i.a.jd()),t==u(i.a.kd(),18).gc())):!1},s.Kc=function(n){var t,i,r,c;return X(n,490)&&(i=u(n,416),t=i.a.jd(),r=u(i.a.kd(),18).gc(),r!=0)?(c=this.a,pCn(c,t,r)):!1},v(dn,"Multisets/EntrySet",2051),m(1108,2051,fs,rj),s.Jc=function(){return new Kxe(zDe(O4(this.a.a)).Jc())},s.gc=function(){return O4(this.a.a).gc()},v(dn,"AbstractMultiset/EntrySet",1108),m(618,730,og),s.hc=function(){return this.nd()},s.jc=function(){return this.od()},s.cc=function(n){return this.pd(n)},s.fc=function(n){return this.qd(n)},s.Zb=function(){var n;return n=this.f,n||(this.f=this.ac())},s.od=function(){return jn(),jn(),hJ},s.Fb=function(n){return iQ(this,n)},s.pd=function(n){return u(pi(this,n),22)},s.qd=function(n){return u(SO(this,n),22)},s.mc=function(n){return jn(),new f9(u(n,22))},s.pc=function(n,t){return new XOe(this,n,u(t,22))},v(dn,"AbstractSetMultimap",618),m(1689,618,og),s.hc=function(){return new vd(this.b)},s.nd=function(){return new vd(this.b)},s.jc=function(){return Ufe(new vd(this.b))},s.od=function(){return Ufe(new vd(this.b))},s.cc=function(n){return u(u(pi(this,n),22),83)},s.pd=function(n){return u(u(pi(this,n),22),83)},s.fc=function(n){return u(u(SO(this,n),22),83)},s.qd=function(n){return u(u(SO(this,n),22),83)},s.mc=function(n){return X(n,277)?Ufe(u(n,277)):(jn(),new ole(u(n,83)))},s.Zb=function(){var n;return n=this.f,n||(this.f=X(this.c,138)?new Zj(this,u(this.c,138)):X(this.c,134)?new SC(this,u(this.c,134)):new g9(this,this.c))},s.pc=function(n,t){return X(t,277)?new TCe(this,n,u(t,277)):new Sle(this,n,u(t,83))},v(dn,"AbstractSortedSetMultimap",1689),m(1690,1689,og),s.Zb=function(){var n;return n=this.f,u(u(n||(this.f=X(this.c,138)?new Zj(this,u(this.c,138)):X(this.c,134)?new SC(this,u(this.c,134)):new g9(this,this.c)),134),138)},s.ec=function(){var n;return n=this.i,u(u(n||(this.i=X(this.c,138)?new w9(this,u(this.c,138)):X(this.c,134)?new WT(this,u(this.c,134)):new R3(this,this.c)),83),277)},s.bc=function(){return X(this.c,138)?new w9(this,u(this.c,138)):X(this.c,134)?new WT(this,u(this.c,134)):new R3(this,this.c)},v(dn,"AbstractSortedKeySortedSetMultimap",1690),m(2071,1,{2008:1}),s.Fb=function(n){return sAn(this,n)},s.Hb=function(){var n;return a1e((n=this.g,n||(this.g=new b0(this))))},s.Ib=function(){var n;return oGe((n=this.f,n||(this.f=new ele(this))))},v(dn,"AbstractTable",2071),m(669,Ha,fs,b0),s.$b=function(){cAe()},s.Gc=function(n){var t,i;return X(n,468)?(t=u(n,687),i=u(L2(fIe(this.a),j0(t.c.e,t.b)),92),!!i&&$1e(i.vc(),new bw(j0(t.c.c,t.a),P4(t.c,t.b,t.a)))):!1},s.Jc=function(){return F5n(this.a)},s.Kc=function(n){var t,i;return X(n,468)?(t=u(n,687),i=u(L2(fIe(this.a),j0(t.c.e,t.b)),92),!!i&&Vkn(i.vc(),new bw(j0(t.c.c,t.a),P4(t.c,t.b,t.a)))):!1},s.gc=function(){return wDe(this.a)},s.Lc=function(){return Gyn(this.a)},v(dn,"AbstractTable/CellSet",669),m(1987,31,Y2,FU),s.$b=function(){cAe()},s.Gc=function(n){return WAn(this.a,n)},s.Jc=function(){return J5n(this.a)},s.gc=function(){return wDe(this.a)},s.Lc=function(){return OLe(this.a)},v(dn,"AbstractTable/Values",1987),m(1662,1661,og),v(dn,"ArrayListMultimapGwtSerializationDependencies",1662),m(506,1662,og,OX,Sae),s.hc=function(){return new xo(this.a)},s.a=0,v(dn,"ArrayListMultimap",506),m(668,2071,{668:1,2008:1,3:1},jqe),v(dn,"ArrayTable",668),m(1983,392,_8,nOe),s.Xb=function(n){return new w1e(this.a,n)},v(dn,"ArrayTable/1",1983),m(1984,1,{},JU),s.rd=function(n){return new w1e(this.a,n)},v(dn,"ArrayTable/1methodref$getCell$Type",1984),m(2072,1,{687:1}),s.Fb=function(n){var t;return n===this?!0:X(n,468)?(t=u(n,687),T1(j0(this.c.e,this.b),j0(t.c.e,t.b))&&T1(j0(this.c.c,this.a),j0(t.c.c,t.a))&&T1(P4(this.c,this.b,this.a),P4(t.c,t.b,t.a))):!1},s.Hb=function(){return RB(z(B(Ar,1),On,1,5,[j0(this.c.e,this.b),j0(this.c.c,this.a),P4(this.c,this.b,this.a)]))},s.Ib=function(){return"("+j0(this.c.e,this.b)+","+j0(this.c.c,this.a)+")="+P4(this.c,this.b,this.a)},v(dn,"Tables/AbstractCell",2072),m(468,2072,{468:1,687:1},w1e),s.a=0,s.b=0,s.d=0,v(dn,"ArrayTable/2",468),m(1986,1,{},X5),s.rd=function(n){return z$e(this.a,n)},v(dn,"ArrayTable/2methodref$getValue$Type",1986),m(1985,392,_8,tOe),s.Xb=function(n){return z$e(this.a,n)},v(dn,"ArrayTable/3",1985),m(2039,2027,Vw),s.$b=function(){uR(this.kc())},s.vc=function(){return new uj(this)},s.lc=function(){return new HIe(this.kc(),this.gc())},v(dn,"Maps/IteratorBasedAbstractMap",2039),m(826,2039,Vw),s.$b=function(){throw $(new _t)},s._b=function(n){return jAe(this.c,n)},s.kc=function(){return new iOe(this,this.c.b.c.gc())},s.lc=function(){return iV(this.c.b.c.gc(),16,new gP(this))},s.xc=function(n){var t;return t=u(eE(this.c,n),15),t?this.td(t.a):null},s.dc=function(){return this.c.b.c.dc()},s.ec=function(){return hV(this.c)},s.yc=function(n,t){var i;if(i=u(eE(this.c,n),15),!i)throw $(new Gn(this.sd()+" "+n+" not in "+hV(this.c)));return this.ud(i.a,t)},s.Ac=function(n){throw $(new _t)},s.gc=function(){return this.c.b.c.gc()},v(dn,"ArrayTable/ArrayMap",826),m(1982,1,{},gP),s.rd=function(n){return dIe(this.a,n)},v(dn,"ArrayTable/ArrayMap/0methodref$getEntry$Type",1982),m(1980,358,hZ,YAe),s.jd=function(){return apn(this.a,this.b)},s.kd=function(){return this.a.td(this.b)},s.ld=function(n){return this.a.ud(this.b,n)},s.b=0,v(dn,"ArrayTable/ArrayMap/1",1980),m(1981,392,_8,iOe),s.Xb=function(n){return dIe(this.a,n)},v(dn,"ArrayTable/ArrayMap/2",1981),m(1979,826,Vw,nIe),s.sd=function(){return"Column"},s.td=function(n){return P4(this.b,this.a,n)},s.ud=function(n,t){return jze(this.b,this.a,n,t)},s.a=0,v(dn,"ArrayTable/Row",1979),m(827,826,Vw,ele),s.td=function(n){return new nIe(this.a,n)},s.yc=function(n,t){return u(t,92),_bn()},s.ud=function(n,t){return u(t,92),Lbn()},s.sd=function(){return"Row"},v(dn,"ArrayTable/RowMap",827),m(1126,1,bl,QAe),s.yd=function(n){return(this.a.wd()&-262&n)!=0},s.wd=function(){return this.a.wd()&-262},s.xd=function(){return this.a.xd()},s.Nb=function(n){this.a.Nb(new ZAe(n,this.b))},s.zd=function(n){return this.a.zd(new WAe(n,this.b))},v(dn,"CollectSpliterators/1",1126),m(1127,1,it,WAe),s.Ad=function(n){this.a.Ad(this.b.Kb(n))},v(dn,"CollectSpliterators/1/lambda$0$Type",1127),m(1128,1,it,ZAe),s.Ad=function(n){this.a.Ad(this.b.Kb(n))},v(dn,"CollectSpliterators/1/lambda$1$Type",1128),m(1123,1,bl,jNe),s.yd=function(n){return((16464|this.b)&n)!=0},s.wd=function(){return 16464|this.b},s.xd=function(){return this.a.xd()},s.Nb=function(n){this.a.Oe(new nMe(n,this.c))},s.zd=function(n){return this.a.Pe(new eMe(n,this.c))},s.b=0,v(dn,"CollectSpliterators/1WithCharacteristics",1123),m(1124,1,aN,eMe),s.Bd=function(n){this.a.Ad(this.b.rd(n))},v(dn,"CollectSpliterators/1WithCharacteristics/lambda$0$Type",1124),m(1125,1,aN,nMe),s.Bd=function(n){this.a.Ad(this.b.rd(n))},v(dn,"CollectSpliterators/1WithCharacteristics/lambda$1$Type",1125),m(1119,1,bl),s.yd=function(n){return(this.a&n)!=0},s.wd=function(){return this.a},s.xd=function(){return this.e&&(this.b=Gse(this.b,this.e.xd())),Gse(this.b,0)},s.Nb=function(n){this.e&&(this.e.Nb(n),this.e=null),this.c.Nb(new tMe(this,n)),this.b=0},s.zd=function(n){for(;;){if(this.e&&this.e.zd(n))return Gj(this.b,hN)&&(this.b=lf(this.b,1)),!0;if(this.e=null,!this.c.zd(new K5(this)))return!1}},s.a=0,s.b=0,v(dn,"CollectSpliterators/FlatMapSpliterator",1119),m(1121,1,it,K5),s.Ad=function(n){c2n(this.a,n)},v(dn,"CollectSpliterators/FlatMapSpliterator/lambda$0$Type",1121),m(1122,1,it,tMe),s.Ad=function(n){p5n(this.a,this.b,n)},v(dn,"CollectSpliterators/FlatMapSpliterator/lambda$1$Type",1122),m(1120,1119,bl,sPe),v(dn,"CollectSpliterators/FlatMapSpliteratorOfObject",1120),m(254,1,dZ),s.Dd=function(n){return this.Cd(u(n,254))},s.Cd=function(n){var t;return n==(EX(),Yne)?1:n==(jX(),Vne)?-1:(t=(eR(),dO(this.a,n.a)),t!=0?t:(Pn(),X(this,513)==X(n,513)?0:X(this,513)?1:-1))},s.Gd=function(){return this.a},s.Fb=function(n){return Lde(this,n)},v(dn,"Cut",254),m(1793,254,dZ,Fxe),s.Cd=function(n){return n==this?0:1},s.Ed=function(n){throw $(new loe)},s.Fd=function(n){n.a+="+∞)"},s.Gd=function(){throw $(new Uc(hYe))},s.Hb=function(){return yd(),jde(this)},s.Hd=function(n){return!1},s.Ib=function(){return"+∞"};var Vne;v(dn,"Cut/AboveAll",1793),m(513,254,{254:1,513:1,3:1,35:1},oOe),s.Ed=function(n){uo((n.a+="(",n),this.a)},s.Fd=function(n){Fb(uo(n,this.a),93)},s.Hb=function(){return~Oi(this.a)},s.Hd=function(n){return eR(),dO(this.a,n)<0},s.Ib=function(){return"/"+this.a+"\\"},v(dn,"Cut/AboveValue",513),m(1792,254,dZ,zxe),s.Cd=function(n){return n==this?0:-1},s.Ed=function(n){n.a+="(-∞"},s.Fd=function(n){throw $(new loe)},s.Gd=function(){throw $(new Uc(hYe))},s.Hb=function(){return yd(),jde(this)},s.Hd=function(n){return!0},s.Ib=function(){return"-∞"};var Yne;v(dn,"Cut/BelowAll",1792),m(1794,254,dZ,sOe),s.Ed=function(n){uo((n.a+="[",n),this.a)},s.Fd=function(n){Fb(uo(n,this.a),41)},s.Hb=function(){return Oi(this.a)},s.Hd=function(n){return eR(),dO(this.a,n)<=0},s.Ib=function(){return"\\"+this.a+"/"},v(dn,"Cut/BelowValue",1794),m(535,1,Wh),s.Ic=function(n){rc(this,n)},s.Ib=function(){return xjn(u(CR(this,"use Optional.orNull() instead of Optional.or(null)"),20).Jc())},v(dn,"FluentIterable",535),m(433,535,Wh,Xj),s.Jc=function(){return new qn(Vn(this.a.Jc(),new ee))},v(dn,"FluentIterable/2",433),m(36,1,{},ee),s.Kb=function(n){return u(n,20).Jc()},s.Fb=function(n){return this===n},v(dn,"FluentIterable/2/0methodref$iterator$Type",36),m(1040,535,Wh,jCe),s.Jc=function(){return qh(this)},v(dn,"FluentIterable/3",1040),m(714,392,_8,sle),s.Xb=function(n){return this.a[n].Jc()},v(dn,"FluentIterable/3/1",714),m(2032,1,{}),s.Ib=function(){return su(this.Id().b)},v(dn,"ForwardingObject",2032),m(2033,2032,dYe),s.Id=function(){return this.Jd()},s.Ic=function(n){rc(this,n)},s.Lc=function(){return new pn(this,0)},s.Mc=function(){return new wn(null,this.Lc())},s.Ec=function(n){return this.Jd(),AAe()},s.Fc=function(n){return this.Jd(),MAe()},s.$b=function(){this.Jd(),TAe()},s.Gc=function(n){return this.Jd().Gc(n)},s.Hc=function(n){return this.Jd().Hc(n)},s.dc=function(){return this.Jd().b.dc()},s.Jc=function(){return this.Jd().Jc()},s.Kc=function(n){return this.Jd(),CAe()},s.gc=function(){return this.Jd().b.gc()},s.Nc=function(){return this.Jd().Nc()},s.Oc=function(n){return this.Jd().Oc(n)},v(dn,"ForwardingCollection",2033),m(2040,31,Bge),s.Jc=function(){return this.Md()},s.Ec=function(n){throw $(new _t)},s.Fc=function(n){throw $(new _t)},s.Kd=function(){var n;return n=this.c,n||(this.c=this.Ld())},s.$b=function(){throw $(new _t)},s.Gc=function(n){return n!=null&&P2(this,n,!1)},s.Ld=function(){switch(this.gc()){case 0:return cR(),Zne;case 1:return new HK(Nt(this.Md().Pb()));default:return new cfe(this,this.Nc())}},s.Kc=function(n){throw $(new _t)},v(dn,"ImmutableCollection",2040),m(1259,2040,Bge,pP),s.Jc=function(){return $4(new tc(this.a.b.Jc()))},s.Gc=function(n){return n!=null&&Sj(this.a,n)},s.Hc=function(n){return Koe(this.a,n)},s.dc=function(){return this.a.b.dc()},s.Md=function(){return $4(new tc(this.a.b.Jc()))},s.gc=function(){return this.a.b.gc()},s.Nc=function(){return this.a.b.Nc()},s.Oc=function(n){return Voe(this.a,n)},s.Ib=function(){return su(this.a.b)},v(dn,"ForwardingImmutableCollection",1259),m(311,2040,L8),s.Jc=function(){return this.Md()},s.cd=function(){return this.Nd(0)},s.dd=function(n){return this.Nd(n)},s.gd=function(n){Vb(this,n)},s.Lc=function(){return new pn(this,16)},s.hd=function(n,t){return this.Od(n,t)},s._c=function(n,t){throw $(new _t)},s.ad=function(n,t){throw $(new _t)},s.Kd=function(){return this},s.Fb=function(n){return lOn(this,n)},s.Hb=function(){return P7n(this)},s.bd=function(n){return n==null?-1:HSn(this,n)},s.Md=function(){return this.Nd(0)},s.Nd=function(n){return LK(this,n)},s.ed=function(n){throw $(new _t)},s.fd=function(n,t){throw $(new _t)},s.Od=function(n,t){var i;return qB((i=new fMe(this),new T0(i,n,t)))},v(dn,"ImmutableList",311),m(2067,311,L8),s.Jc=function(){return $4(this.Pd().Jc())},s.hd=function(n,t){return qB(this.Pd().hd(n,t))},s.Gc=function(n){return n!=null&&this.Pd().Gc(n)},s.Hc=function(n){return this.Pd().Hc(n)},s.Fb=function(n){return di(this.Pd(),n)},s.Xb=function(n){return j0(this,n)},s.Hb=function(){return Oi(this.Pd())},s.bd=function(n){return this.Pd().bd(n)},s.dc=function(){return this.Pd().dc()},s.Md=function(){return $4(this.Pd().Jc())},s.gc=function(){return this.Pd().gc()},s.Od=function(n,t){return qB(this.Pd().hd(n,t))},s.Nc=function(){return this.Pd().Oc(oe(Ar,On,1,this.Pd().gc(),5,1))},s.Oc=function(n){return this.Pd().Oc(n)},s.Ib=function(){return su(this.Pd())},v(dn,"ForwardingImmutableList",2067),m(717,1,P8),s.vc=function(){return $b(this)},s.wc=function(n){bO(this,n)},s.ec=function(){return hV(this)},s.Bc=function(){return this.Td()},s.$b=function(){throw $(new _t)},s._b=function(n){return this.xc(n)!=null},s.uc=function(n){return this.Td().Gc(n)},s.Rd=function(){return new qU(this)},s.Sd=function(){return new UU(this)},s.Fb=function(n){return Akn(this,n)},s.Hb=function(){return $b(this).Hb()},s.dc=function(){return this.gc()==0},s.yc=function(n,t){return Pbn()},s.Ac=function(n){throw $(new _t)},s.Ib=function(){return UMn(this)},s.Td=function(){return this.e?this.e:this.e=this.Sd()},s.c=null,s.d=null,s.e=null,v(dn,"ImmutableMap",717),m(718,717,P8),s._b=function(n){return jAe(this,n)},s.uc=function(n){return pMe(this.b,n)},s.Qd=function(){return cFe(new wP(this))},s.Rd=function(){return cFe(LIe(this.b))},s.Sd=function(){return new pP(PIe(this.b))},s.Fb=function(n){return vMe(this.b,n)},s.xc=function(n){return eE(this,n)},s.Hb=function(){return Oi(this.b.c)},s.dc=function(){return this.b.c.dc()},s.gc=function(){return this.b.c.gc()},s.Ib=function(){return su(this.b.c)},v(dn,"ForwardingImmutableMap",718),m(2034,2033,bZ),s.Id=function(){return this.Ud()},s.Jd=function(){return this.Ud()},s.Lc=function(){return new pn(this,1)},s.Fb=function(n){return n===this||this.Ud().Fb(n)},s.Hb=function(){return this.Ud().Hb()},v(dn,"ForwardingSet",2034),m(1055,2034,bZ,wP),s.Id=function(){return _9(this.a.b)},s.Jd=function(){return _9(this.a.b)},s.Gc=function(n){if(X(n,45)&&u(n,45).jd()==null)return!1;try{return mMe(_9(this.a.b),n)}catch(t){if(t=sr(t),X(t,211))return!1;throw $(t)}},s.Ud=function(){return _9(this.a.b)},s.Oc=function(n){var t,i;return t=k_e(_9(this.a.b),n),_9(this.a.b).b.gc()=0?"+":"")+(i/60|0),t=D$(k.Math.abs(i)%60),(vGe(),snn)[this.q.getDay()]+" "+lnn[this.q.getMonth()]+" "+D$(this.q.getDate())+" "+D$(this.q.getHours())+":"+D$(this.q.getMinutes())+":"+D$(this.q.getSeconds())+" GMT"+n+t+" "+this.q.getFullYear()};var lJ=v(mt,"Date",205);m(1977,205,jYe,zHe),s.a=!1,s.b=0,s.c=0,s.d=0,s.e=0,s.f=0,s.g=!1,s.i=0,s.j=0,s.k=0,s.n=0,s.o=0,s.p=0,v("com.google.gwt.i18n.shared.impl","DateRecord",1977),m(2026,1,{}),s.ne=function(){return null},s.oe=function(){return null},s.pe=function(){return null},s.qe=function(){return null},s.re=function(){return null},v(oy,"JSONValue",2026),m(139,2026,{139:1},bd,n9),s.Fb=function(n){return X(n,139)?Mae(this.a,u(n,139).a):!1},s.me=function(){return tbn},s.Hb=function(){return aae(this.a)},s.ne=function(){return this},s.Ib=function(){var n,t,i;for(i=new il("["),t=0,n=this.a.length;t0&&(i.a+=","),uo(i,T2(this,t));return i.a+="]",i.a},v(oy,"JSONArray",139),m(479,2026,{479:1},t9),s.me=function(){return ibn},s.oe=function(){return this},s.Ib=function(){return Pn(),""+this.a},s.a=!1;var Yen,Qen;v(oy,"JSONBoolean",479),m(981,63,H1,Vxe),v(oy,"JSONException",981),m(1017,2026,{},wt),s.me=function(){return obn},s.Ib=function(){return Vo};var Wen;v(oy,"JSONNull",1017),m(265,2026,{265:1},k3),s.Fb=function(n){return X(n,265)?this.a==u(n,265).a:!1},s.me=function(){return rbn},s.Hb=function(){return b4(this.a)},s.pe=function(){return this},s.Ib=function(){return this.a+""},s.a=0,v(oy,"JSONNumber",265),m(149,2026,{149:1},r4,i9),s.Fb=function(n){return X(n,149)?Mae(this.a,u(n,149).a):!1},s.me=function(){return cbn},s.Hb=function(){return aae(this.a)},s.qe=function(){return this},s.Ib=function(){var n,t,i,r,c,o,l;for(l=new il("{"),n=!0,o=BY(this,oe(ze,Se,2,0,6,1)),i=o,r=0,c=i.length;r=0?":"+this.c:"")+")"},s.c=0;var _me=v(Tu,"StackTraceElement",324);Jen={3:1,472:1,35:1,2:1};var ze=v(Tu,zge,2);m(111,418,{472:1},pd,jj,cf),v(Tu,"StringBuffer",111),m(106,418,{472:1},p0,o4,il),v(Tu,"StringBuilder",106),m(691,99,iF,Doe),v(Tu,"StringIndexOutOfBoundsException",691),m(2107,1,{});var tnn;m(46,63,{3:1,101:1,63:1,80:1,46:1},_t,gd),v(Tu,"UnsupportedOperationException",46),m(247,242,{3:1,35:1,242:1,247:1},MO,Joe),s.Dd=function(n){return vKe(this,u(n,247))},s.se=function(){return F2(UKe(this))},s.Fb=function(n){var t;return this===n?!0:X(n,247)?(t=u(n,247),this.e==t.e&&vKe(this,t)==0):!1},s.Hb=function(){var n;return this.b!=0?this.b:this.a<54?(n=Pu(this.f),this.b=Rt(Rr(n,-1)),this.b=33*this.b+Rt(Rr(kw(n,32),-1)),this.b=17*this.b+sc(this.e),this.b):(this.b=17*wFe(this.c)+sc(this.e),this.b)},s.Ib=function(){return UKe(this)},s.a=0,s.b=0,s.d=0,s.e=0,s.f=0;var inn,pg,Lme,Pme,$me,Rme,Bme,zme,cte=v("java.math","BigDecimal",247);m(91,242,{3:1,35:1,242:1,91:1},D1,hLe,zb,xJe,E0),s.Dd=function(n){return yJe(this,u(n,91))},s.se=function(){return F2(lZ(this,0))},s.Fb=function(n){return ude(this,n)},s.Hb=function(){return wFe(this)},s.Ib=function(){return lZ(this,0)},s.b=-2,s.c=0,s.d=0,s.e=0;var rnn,fJ,cnn,ute,aJ,KS,Sv=v("java.math","BigInteger",91),unn,onn,my,VS;m(484,2027,Vw),s.$b=function(){Hu(this)},s._b=function(n){return so(this,n)},s.uc=function(n){return eFe(this,n,this.i)||eFe(this,n,this.f)},s.vc=function(){return new on(this)},s.xc=function(n){return Bn(this,n)},s.yc=function(n,t){return ei(this,n,t)},s.Ac=function(n){return L4(this,n)},s.gc=function(){return xj(this)},s.g=0,v(mt,"AbstractHashMap",484),m(306,Ha,fs,on),s.$b=function(){this.a.$b()},s.Gc=function(n){return JLe(this,n)},s.Jc=function(){return new D2(this.a)},s.Kc=function(n){var t;return JLe(this,n)?(t=u(n,45).jd(),this.a.Ac(t),!0):!1},s.gc=function(){return this.a.gc()},v(mt,"AbstractHashMap/EntrySet",306),m(307,1,Fr,D2),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return Q3(this)},s.Ob=function(){return this.b},s.Qb=function(){gRe(this)},s.b=!1,s.d=0,v(mt,"AbstractHashMap/EntrySetIterator",307),m(417,1,Fr,qc),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return zX(this)},s.Pb=function(){return oae(this)},s.Qb=function(){As(this)},s.b=0,s.c=-1,v(mt,"AbstractList/IteratorImpl",417),m(97,417,Qh,qr),s.Qb=function(){As(this)},s.Rb=function(n){d2(this,n)},s.Sb=function(){return this.b>0},s.Tb=function(){return this.b},s.Ub=function(){return at(this.b>0),this.a.Xb(this.c=--this.b)},s.Vb=function(){return this.b-1},s.Wb=function(n){l2(this.c!=-1),this.a.fd(this.c,n)},v(mt,"AbstractList/ListIteratorImpl",97),m(258,56,$8,T0),s._c=function(n,t){S2(n,this.b),this.c._c(this.a+n,t),++this.b},s.Xb=function(n){return vn(n,this.b),this.c.Xb(this.a+n)},s.ed=function(n){var t;return vn(n,this.b),t=this.c.ed(this.a+n),--this.b,t},s.fd=function(n,t){return vn(n,this.b),this.c.fd(this.a+n,t)},s.gc=function(){return this.b},s.a=0,s.b=0,v(mt,"AbstractList/SubList",258),m(232,Ha,fs,nt),s.$b=function(){this.a.$b()},s.Gc=function(n){return this.a._b(n)},s.Jc=function(){var n;return n=this.a.vc().Jc(),new gt(n)},s.Kc=function(n){return this.a._b(n)?(this.a.Ac(n),!0):!1},s.gc=function(){return this.a.gc()},v(mt,"AbstractMap/1",232),m(529,1,Fr,gt),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.a.Ob()},s.Pb=function(){var n;return n=u(this.a.Pb(),45),n.jd()},s.Qb=function(){this.a.Qb()},v(mt,"AbstractMap/1/1",529),m(230,31,Y2,ct),s.$b=function(){this.a.$b()},s.Gc=function(n){return this.a.uc(n)},s.Jc=function(){var n;return n=this.a.vc().Jc(),new Ji(n)},s.gc=function(){return this.a.gc()},v(mt,"AbstractMap/2",230),m(304,1,Fr,Ji),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.a.Ob()},s.Pb=function(){var n;return n=u(this.a.Pb(),45),n.kd()},s.Qb=function(){this.a.Qb()},v(mt,"AbstractMap/2/1",304),m(480,1,{480:1,45:1}),s.Fb=function(n){var t;return X(n,45)?(t=u(n,45),Ku(this.d,t.jd())&&Ku(this.e,t.kd())):!1},s.jd=function(){return this.d},s.kd=function(){return this.e},s.Hb=function(){return _3(this.d)^_3(this.e)},s.ld=function(n){return Dle(this,n)},s.Ib=function(){return this.d+"="+this.e},v(mt,"AbstractMap/AbstractEntry",480),m(390,480,{480:1,390:1,45:1},r$),v(mt,"AbstractMap/SimpleEntry",390),m(2044,1,RZ),s.Fb=function(n){var t;return X(n,45)?(t=u(n,45),Ku(this.jd(),t.jd())&&Ku(this.kd(),t.kd())):!1},s.Hb=function(){return _3(this.jd())^_3(this.kd())},s.Ib=function(){return this.jd()+"="+this.kd()},v(mt,fYe,2044),m(2052,2027,$ge),s.Vc=function(n){return _X(this.Ce(n))},s.tc=function(n){return PPe(this,n)},s._b=function(n){return Ile(this,n)},s.vc=function(){return new Ui(this)},s.Rc=function(){return tIe(this.Ee())},s.Wc=function(n){return _X(this.Fe(n))},s.xc=function(n){var t;return t=n,du(this.De(t))},s.Yc=function(n){return _X(this.Ge(n))},s.ec=function(){return new Lu(this)},s.Tc=function(){return tIe(this.He())},s.Zc=function(n){return _X(this.Ie(n))},v(mt,"AbstractNavigableMap",2052),m(620,Ha,fs,Ui),s.Gc=function(n){return X(n,45)&&PPe(this.b,u(n,45))},s.Jc=function(){return this.b.Be()},s.Kc=function(n){var t;return X(n,45)?(t=u(n,45),this.b.Je(t)):!1},s.gc=function(){return this.b.gc()},v(mt,"AbstractNavigableMap/EntrySet",620),m(1115,Ha,Rge,Lu),s.Lc=function(){return new o$(this)},s.$b=function(){this.a.$b()},s.Gc=function(n){return Ile(this.a,n)},s.Jc=function(){var n;return n=this.a.vc().b.Be(),new _ke(n)},s.Kc=function(n){return Ile(this.a,n)?(this.a.Ac(n),!0):!1},s.gc=function(){return this.a.gc()},v(mt,"AbstractNavigableMap/NavigableKeySet",1115),m(1116,1,Fr,_ke),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return zX(this.a.a)},s.Pb=function(){var n;return n=TOe(this.a),n.jd()},s.Qb=function(){NNe(this.a)},v(mt,"AbstractNavigableMap/NavigableKeySet/1",1116),m(2065,31,Y2),s.Ec=function(n){return E4(v8(this,n),B8),!0},s.Fc=function(n){return _n(n),IC(n!=this,"Can't add a queue to itself"),fc(this,n)},s.$b=function(){for(;MY(this)!=null;);},v(mt,"AbstractQueue",2065),m(314,31,{4:1,20:1,31:1,18:1},P3,ILe),s.Ec=function(n){return Lae(this,n),!0},s.$b=function(){zae(this)},s.Gc=function(n){return mze(new hE(this),n)},s.dc=function(){return kj(this)},s.Jc=function(){return new hE(this)},s.Kc=function(n){return j4n(new hE(this),n)},s.gc=function(){return this.c-this.b&this.a.length-1},s.Lc=function(){return new pn(this,272)},s.Oc=function(n){var t;return t=this.c-this.b&this.a.length-1,n.lengtht&&tr(n,t,null),n},s.b=0,s.c=0,v(mt,"ArrayDeque",314),m(448,1,Fr,hE),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.a!=this.b},s.Pb=function(){return zB(this)},s.Qb=function(){mBe(this)},s.a=0,s.b=0,s.c=-1,v(mt,"ArrayDeque/IteratorImpl",448),m(13,56,AYe,Ce,xo,bs),s._c=function(n,t){Pb(this,n,t)},s.Ec=function(n){return Te(this,n)},s.ad=function(n,t){return N1e(this,n,t)},s.Fc=function(n){return Er(this,n)},s.$b=function(){Qp(this.c,0)},s.Gc=function(n){return wu(this,n,0)!=-1},s.Ic=function(n){Ao(this,n)},s.Xb=function(n){return Le(this,n)},s.bd=function(n){return wu(this,n,0)},s.dc=function(){return this.c.length==0},s.Jc=function(){return new L(this)},s.ed=function(n){return Ad(this,n)},s.Kc=function(n){return qo(this,n)},s.ae=function(n,t){rLe(this,n,t)},s.fd=function(n,t){return ol(this,n,t)},s.gc=function(){return this.c.length},s.gd=function(n){Cr(this,n)},s.Nc=function(){return nR(this.c)},s.Oc=function(n){return Ra(this,n)};var EBn=v(mt,"ArrayList",13);m(7,1,Fr,L),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return bu(this)},s.Pb=function(){return _(this)},s.Qb=function(){oE(this)},s.a=0,s.b=-1,v(mt,"ArrayList/1",7),m(2074,k.Function,{},mn),s.Ke=function(n,t){return ki(n,t)},m(123,56,MYe,Su),s.Gc=function(n){return pBe(this,n)!=-1},s.Ic=function(n){var t,i,r,c;for(_n(n),i=this.a,r=0,c=i.length;r0)throw $(new Gn(Kge+n+" greater than "+this.e));return this.f.Re()?M_e(this.c,this.b,this.a,n,t):tLe(this.c,n,t)},s.yc=function(n,t){if(!ZQ(this.c,this.f,n,this.b,this.a,this.e,this.d))throw $(new Gn(n+" outside the range "+this.b+" to "+this.e));return Pze(this.c,n,t)},s.Ac=function(n){var t;return t=n,ZQ(this.c,this.f,t,this.b,this.a,this.e,this.d)?T_e(this.c,t):null},s.Je=function(n){return ER(this,n.jd())&&uhe(this.c,n)},s.gc=function(){var n,t,i;if(this.f.Re()?this.a?t=h8(this.c,this.b,!0):t=h8(this.c,this.b,!1):t=mhe(this.c),!(t&&ER(this,t.d)&&t))return 0;for(n=0,i=new zY(this.c,this.f,this.b,this.a,this.e,this.d);zX(i.a);i.b=u(oae(i.a),45))++n;return n},s.$c=function(n,t){if(this.f.Re()&&this.c.a.Le(n,this.b)<0)throw $(new Gn(Kge+n+OYe+this.b));return this.f.Se()?M_e(this.c,n,t,this.e,this.d):iLe(this.c,n,t)},s.a=!1,s.d=!1,v(mt,"TreeMap/SubMap",622),m(309,23,JZ,c$),s.Re=function(){return!1},s.Se=function(){return!1};var lte,fte,ate,hte,dJ=yt(mt,"TreeMap/SubMapType",309,Ct,Wyn,S2n);m(1112,309,JZ,ACe),s.Se=function(){return!0},yt(mt,"TreeMap/SubMapType/1",1112,dJ,null,null),m(1113,309,JZ,RCe),s.Re=function(){return!0},s.Se=function(){return!0},yt(mt,"TreeMap/SubMapType/2",1113,dJ,null,null),m(1114,309,JZ,MCe),s.Re=function(){return!0},yt(mt,"TreeMap/SubMapType/3",1114,dJ,null,null);var gnn;m(141,Ha,{3:1,20:1,31:1,18:1,277:1,22:1,83:1,141:1},wX,lle,vd,c9),s.Lc=function(){return new o$(this)},s.Ec=function(n){return PC(this,n)},s.$b=function(){this.a.$b()},s.Gc=function(n){return this.a._b(n)},s.Jc=function(){return this.a.ec().Jc()},s.Kc=function(n){return DK(this,n)},s.gc=function(){return this.a.gc()};var CBn=v(mt,"TreeSet",141);m(1052,1,{},$ke),s.Te=function(n,t){return Gpn(this.a,n,t)},v(HZ,"BinaryOperator/lambda$0$Type",1052),m(1053,1,{},Rke),s.Te=function(n,t){return qpn(this.a,n,t)},v(HZ,"BinaryOperator/lambda$1$Type",1053),m(935,1,{},Eu),s.Kb=function(n){return n},v(HZ,"Function/lambda$0$Type",935),m(388,1,Ft,u9),s.Mb=function(n){return!this.a.Mb(n)},v(HZ,"Predicate/lambda$2$Type",388),m(567,1,{567:1});var wnn=v(pS,"Handler",567);m(2069,1,lN),s.ve=function(){return"DUMMY"},s.Ib=function(){return this.ve()};var Xme;v(pS,"Level",2069),m(1672,2069,lN,Ws),s.ve=function(){return"INFO"},v(pS,"Level/LevelInfo",1672),m(1824,1,{},txe);var dte;v(pS,"LogManager",1824),m(1866,1,lN,ONe),s.b=null,v(pS,"LogRecord",1866),m(511,1,{511:1},uY),s.e=!1;var pnn=!1,mnn=!1,Ka=!1,vnn=!1,ynn=!1;v(pS,"Logger",511),m(819,567,{567:1},Mr),v(pS,"SimpleConsoleLogHandler",819),m(130,23,{3:1,35:1,23:1,130:1},HX);var Kme,Yo,Vme,Qo=yt(_c,"Collector/Characteristics",130,Ct,P4n,x2n),knn;m(746,1,{},Bfe),v(_c,"CollectorImpl",746),m(1050,1,{},Wr),s.Te=function(n,t){return sjn(u(n,212),u(t,212))},v(_c,"Collectors/10methodref$merge$Type",1050),m(1051,1,{},ur),s.Kb=function(n){return DLe(u(n,212))},v(_c,"Collectors/11methodref$toString$Type",1051),m(152,1,{},mi),s.Wd=function(n,t){u(n,18).Ec(t)},v(_c,"Collectors/20methodref$add$Type",152),m(154,1,{},Fi),s.Ve=function(){return new Ce},v(_c,"Collectors/21methodref$ctor$Type",154),m(1049,1,{},fu),s.Wd=function(n,t){I1(u(n,212),u(t,472))},v(_c,"Collectors/9methodref$add$Type",1049),m(1048,1,{},VNe),s.Ve=function(){return new Qb(this.a,this.b,this.c)},v(_c,"Collectors/lambda$15$Type",1048),m(153,1,{},kc),s.Te=function(n,t){return ygn(u(n,18),u(t,18))},v(_c,"Collectors/lambda$45$Type",153),m(538,1,{}),s.Ye=function(){aE(this)},s.d=!1,v(_c,"TerminatableStream",538),m(768,538,Vge,xle),s.Ye=function(){aE(this)},v(_c,"DoubleStreamImpl",768),m(1297,724,bl,YNe),s.Pe=function(n){return LSn(this,u(n,189))},s.a=null,v(_c,"DoubleStreamImpl/2",1297),m(1298,1,mN,Bke),s.Ne=function(n){dwn(this.a,n)},v(_c,"DoubleStreamImpl/2/lambda$0$Type",1298),m(1295,1,mN,zke),s.Ne=function(n){hwn(this.a,n)},v(_c,"DoubleStreamImpl/lambda$0$Type",1295),m(1296,1,mN,Fke),s.Ne=function(n){sJe(this.a,n)},v(_c,"DoubleStreamImpl/lambda$2$Type",1296),m(1351,723,bl,JPe),s.Pe=function(n){return Fyn(this,u(n,202))},s.a=0,s.b=0,s.c=0,v(_c,"IntStream/5",1351),m(793,538,Vge,Ale),s.Ye=function(){aE(this)},s.Ze=function(){return A0(this),this.a},v(_c,"IntStreamImpl",793),m(794,538,Vge,Yoe),s.Ye=function(){aE(this)},s.Ze=function(){return A0(this),Yse(),bnn},v(_c,"IntStreamImpl/Empty",794),m(1651,1,aN,Jke),s.Bd=function(n){eze(this.a,n)},v(_c,"IntStreamImpl/lambda$4$Type",1651);var OBn=Hi(_c,"Stream");m(28,538,{520:1,677:1,832:1},wn),s.Ye=function(){aE(this)};var vy;v(_c,"StreamImpl",28),m(1072,486,bl,kNe),s.zd=function(n){for(;F9n(this);){if(this.a.zd(n))return!0;aE(this.b),this.b=null,this.a=null}return!1},v(_c,"StreamImpl/1",1072),m(1073,1,it,Hke),s.Ad=function(n){C3n(this.a,u(n,832))},v(_c,"StreamImpl/1/lambda$0$Type",1073),m(1074,1,Ft,Gke),s.Mb=function(n){return hr(this.a,n)},v(_c,"StreamImpl/1methodref$add$Type",1074),m(1075,486,bl,e_e),s.zd=function(n){var t;return this.a||(t=new Ce,this.b.a.Nb(new qke(t)),jn(),Cr(t,this.c),this.a=new pn(t,16)),JRe(this.a,n)},s.a=null,v(_c,"StreamImpl/5",1075),m(1076,1,it,qke),s.Ad=function(n){Te(this.a,n)},v(_c,"StreamImpl/5/2methodref$add$Type",1076),m(725,486,bl,whe),s.zd=function(n){for(this.b=!1;!this.b&&this.c.zd(new BMe(this,n)););return this.b},s.b=!1,v(_c,"StreamImpl/FilterSpliterator",725),m(1066,1,it,BMe),s.Ad=function(n){kvn(this.a,this.b,n)},v(_c,"StreamImpl/FilterSpliterator/lambda$0$Type",1066),m(1061,724,bl,WPe),s.Pe=function(n){return g2n(this,u(n,189))},v(_c,"StreamImpl/MapToDoubleSpliterator",1061),m(1065,1,it,zMe),s.Ad=function(n){Pgn(this.a,this.b,n)},v(_c,"StreamImpl/MapToDoubleSpliterator/lambda$0$Type",1065),m(1060,723,bl,ZPe),s.Pe=function(n){return w2n(this,u(n,202))},v(_c,"StreamImpl/MapToIntSpliterator",1060),m(1064,1,it,FMe),s.Ad=function(n){$gn(this.a,this.b,n)},v(_c,"StreamImpl/MapToIntSpliterator/lambda$0$Type",1064),m(722,486,bl,the),s.zd=function(n){return ENe(this,n)},v(_c,"StreamImpl/MapToObjSpliterator",722),m(1063,1,it,JMe),s.Ad=function(n){Rgn(this.a,this.b,n)},v(_c,"StreamImpl/MapToObjSpliterator/lambda$0$Type",1063),m(1062,486,bl,vBe),s.zd=function(n){for(;FX(this.b,0);){if(!this.a.zd(new ef))return!1;this.b=lf(this.b,1)}return this.a.zd(n)},s.b=0,v(_c,"StreamImpl/SkipSpliterator",1062),m(1067,1,it,ef),s.Ad=function(n){},v(_c,"StreamImpl/SkipSpliterator/lambda$0$Type",1067),m(617,1,it,ch),s.Ad=function(n){jP(this,n)},v(_c,"StreamImpl/ValueConsumer",617),m(1068,1,it,Dh),s.Ad=function(n){Ib()},v(_c,"StreamImpl/lambda$0$Type",1068),m(1069,1,it,cd),s.Ad=function(n){Ib()},v(_c,"StreamImpl/lambda$1$Type",1069),m(1070,1,{},Uke),s.Te=function(n,t){return j2n(this.a,n,t)},v(_c,"StreamImpl/lambda$4$Type",1070),m(1071,1,it,HMe),s.Ad=function(n){Qpn(this.b,this.a,n)},v(_c,"StreamImpl/lambda$5$Type",1071),m(1077,1,it,Xke),s.Ad=function(n){B7n(this.a,u(n,375))},v(_c,"TerminatableStream/lambda$0$Type",1077),m(2104,1,{}),m(1976,1,{},jb),v("javaemul.internal","ConsoleLogger",1976);var NBn=0;m(2096,1,{}),m(1800,1,it,Rs),s.Ad=function(n){u(n,321)},v(z8,"BowyerWatsonTriangulation/lambda$0$Type",1800),m(1801,1,it,Kke),s.Ad=function(n){fc(this.a,u(n,321).e)},v(z8,"BowyerWatsonTriangulation/lambda$1$Type",1801),m(1802,1,it,c0),s.Ad=function(n){u(n,177)},v(z8,"BowyerWatsonTriangulation/lambda$2$Type",1802),m(1797,1,Yt,Vke),s.Le=function(n,t){return A6n(this.a,u(n,177),u(t,177))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(z8,"NaiveMinST/lambda$0$Type",1797),m(440,1,{},hj),v(z8,"NodeMicroLayout",440),m(177,1,{177:1},f4),s.Fb=function(n){var t;return X(n,177)?(t=u(n,177),Ku(this.a,t.a)&&Ku(this.b,t.b)||Ku(this.a,t.b)&&Ku(this.b,t.a)):!1},s.Hb=function(){return _3(this.a)+_3(this.b)};var DBn=v(z8,"TEdge",177);m(321,1,{321:1},lge),s.Fb=function(n){var t;return X(n,321)?(t=u(n,321),cB(this,t.a)&&cB(this,t.b)&&cB(this,t.c)):!1},s.Hb=function(){return _3(this.a)+_3(this.b)+_3(this.c)},v(z8,"TTriangle",321),m(225,1,{225:1},_$),v(z8,"Tree",225),m(1183,1,{},G_e),v(IYe,"Scanline",1183);var jnn=Hi(IYe,_Ye);m(1728,1,{},GRe),v(n1,"CGraph",1728),m(320,1,{320:1},$_e),s.b=0,s.c=0,s.d=0,s.g=0,s.i=0,s.k=Dr,v(n1,"CGroup",320),m(814,1,{},doe),v(n1,"CGroup/CGroupBuilder",814),m(60,1,{60:1},iNe),s.Ib=function(){var n;return this.j?Pt(this.j.Kb(this)):(M1(bJ),bJ.o+"@"+(n=vw(this)>>>0,n.toString(16)))},s.f=0,s.i=Dr;var bJ=v(n1,"CNode",60);m(813,1,{},boe),v(n1,"CNode/CNodeBuilder",813);var Enn;m(1551,1,{},u0),s.df=function(n,t){return 0},s.ef=function(n,t){return 0},v(n1,PYe,1551),m(1830,1,{},o0),s.af=function(n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;for(b=Ki,r=new L(n.a.b);r.ar.d.c||r.d.c==o.d.c&&r.d.b0?n+this.n.d+this.n.a:0},s.gf=function(){var n,t,i,r,c;if(c=0,this.e)this.b?c=this.b.a:this.a[1][1]&&(c=this.a[1][1].gf());else if(this.g)c=ide(this,nW(this,null,!0));else for(t=(ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])),i=0,r=t.length;i0?c+this.n.b+this.n.c:0},s.hf=function(){var n,t,i,r,c;if(this.g)for(n=nW(this,null,!1),i=(ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])),r=0,c=i.length;r0&&(r[0]+=this.d,i-=r[0]),r[2]>0&&(r[2]+=this.d,i-=r[2]),this.c.a=k.Math.max(0,i),this.c.d=t.d+n.d+(this.c.a-i)/2,r[1]=k.Math.max(r[1],i),Wae(this,No,t.d+n.d+r[0]-(r[1]-i)/2,r)},s.b=null,s.d=0,s.e=!1,s.f=!1,s.g=!1;var gte=0,gJ=0;v(lg,"GridContainerCell",1499),m(461,23,{3:1,35:1,23:1,461:1},qX);var nb,Th,Uf,Onn=yt(lg,"HorizontalLabelAlignment",461,Ct,W4n,A2n),Nnn;m(318,216,{216:1,318:1},C_e,HRe,j_e),s.ff=function(){return oDe(this)},s.gf=function(){return wfe(this)},s.a=0,s.c=!1;var IBn=v(lg,"LabelCell",318);m(253,337,{216:1,337:1,253:1},JE),s.ff=function(){return YE(this)},s.gf=function(){return QE(this)},s.hf=function(){HW(this)},s.jf=function(){GW(this)},s.b=0,s.c=0,s.d=!1,v(lg,"StripContainerCell",253),m(1655,1,Ft,r6),s.Mb=function(n){return Nbn(u(n,216))},v(lg,"StripContainerCell/lambda$0$Type",1655),m(1656,1,{},zg),s.We=function(n){return u(n,216).gf()},v(lg,"StripContainerCell/lambda$1$Type",1656),m(1657,1,Ft,Bg),s.Mb=function(n){return Dbn(u(n,216))},v(lg,"StripContainerCell/lambda$2$Type",1657),m(1658,1,{},c6),s.We=function(n){return u(n,216).ff()},v(lg,"StripContainerCell/lambda$3$Type",1658),m(462,23,{3:1,35:1,23:1,462:1},UX);var Xf,tb,ka,Dnn=yt(lg,"VerticalLabelAlignment",462,Ct,Z4n,M2n),Inn;m(787,1,{},Mge),s.c=0,s.d=0,s.k=0,s.s=0,s.t=0,s.v=!1,s.w=0,s.D=!1,s.F=!1,v(uF,"NodeContext",787),m(1497,1,Yt,f5),s.Le=function(n,t){return mCe(u(n,64),u(t,64))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(uF,"NodeContext/0methodref$comparePortSides$Type",1497),m(1498,1,Yt,a5),s.Le=function(n,t){return gMn(u(n,115),u(t,115))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(uF,"NodeContext/1methodref$comparePortContexts$Type",1498),m(168,23,{3:1,35:1,23:1,168:1},Bl);var _nn,Lnn,Pnn,$nn,Rnn,Bnn,znn,Fnn,Jnn,Hnn,Gnn,qnn,Unn,Xnn,Knn,Vnn,Ynn,Qnn,Wnn,Znn,etn,wte,ntn=yt(uF,"NodeLabelLocation",168,Ct,DQ,T2n),ttn;m(115,1,{115:1},Bqe),s.a=!1,v(uF,"PortContext",115),m(1502,1,it,Fg),s.Ad=function(n){IAe(u(n,318))},v(yN,VYe,1502),m(1503,1,Ft,Eb),s.Mb=function(n){return!!u(n,115).c},v(yN,YYe,1503),m(1504,1,it,Jg),s.Ad=function(n){IAe(u(n,115).c)},v(yN,"LabelPlacer/lambda$2$Type",1504);var Qme;m(1501,1,it,_u),s.Ad=function(n){h2(),fbn(u(n,115))},v(yN,"NodeLabelAndSizeUtilities/lambda$0$Type",1501),m(788,1,it,Kle),s.Ad=function(n){Sgn(this.b,this.c,this.a,u(n,187))},s.a=!1,s.c=!1,v(yN,"NodeLabelCellCreator/lambda$0$Type",788),m(1500,1,it,Wke),s.Ad=function(n){bbn(this.a,u(n,187))},v(yN,"PortContextCreator/lambda$0$Type",1500);var wJ;m(1872,1,{},Hg),v(J8,"GreedyRectangleStripOverlapRemover",1872),m(1873,1,Yt,Gg),s.Le=function(n,t){return tpn(u(n,226),u(t,226))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(J8,"GreedyRectangleStripOverlapRemover/0methodref$compareByYCoordinate$Type",1873),m(1826,1,{},oxe),s.a=5,s.e=0,v(J8,"RectangleStripOverlapRemover",1826),m(1827,1,Yt,u6),s.Le=function(n,t){return ipn(u(n,226),u(t,226))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(J8,"RectangleStripOverlapRemover/0methodref$compareLeftRectangleBorders$Type",1827),m(1829,1,Yt,h5),s.Le=function(n,t){return $vn(u(n,226),u(t,226))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(J8,"RectangleStripOverlapRemover/1methodref$compareRightRectangleBorders$Type",1829),m(409,23,{3:1,35:1,23:1,409:1},u$);var UN,pte,mte,XN,itn=yt(J8,"RectangleStripOverlapRemover/OverlapRemovalDirection",409,Ct,Yyn,C2n),rtn;m(226,1,{226:1},fV),v(J8,"RectangleStripOverlapRemover/RectangleNode",226),m(1828,1,it,Zke),s.Ad=function(n){USn(this.a,u(n,226))},v(J8,"RectangleStripOverlapRemover/lambda$1$Type",1828);var ctn=!1,YS,Wme;m(1798,1,it,Wm),s.Ad=function(n){XKe(u(n,225))},v(ay,"DepthFirstCompaction/0methodref$compactTree$Type",1798),m(810,1,it,Wue),s.Ad=function(n){a5n(this.a,u(n,225))},v(ay,"DepthFirstCompaction/lambda$1$Type",810),m(1799,1,it,_Ne),s.Ad=function(n){IEn(this.a,this.b,this.c,u(n,225))},v(ay,"DepthFirstCompaction/lambda$2$Type",1799);var QS,Zme;m(68,1,{68:1},U_e),v(ay,"Node",68),m(1179,1,{},PCe),v(ay,"ScanlineOverlapCheck",1179),m(1180,1,{683:1},p_e),s._e=function(n){Fpn(this,u(n,442))},v(ay,"ScanlineOverlapCheck/OverlapsScanlineHandler",1180),m(1181,1,Yt,qg),s.Le=function(n,t){return yjn(u(n,68),u(t,68))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(ay,"ScanlineOverlapCheck/OverlapsScanlineHandler/lambda$0$Type",1181),m(442,1,{442:1},lse),s.a=!1,v(ay,"ScanlineOverlapCheck/Timestamp",442),m(1182,1,Yt,o6),s.Le=function(n,t){return Uxn(u(n,442),u(t,442))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(ay,"ScanlineOverlapCheck/lambda$0$Type",1182),m(545,1,{},Ug),v("org.eclipse.elk.alg.common.utils","SVGImage",545),m(748,1,{},Zm),v(KZ,twe,748),m(1164,1,Yt,d5),s.Le=function(n,t){return yCn(u(n,235),u(t,235))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(KZ,ZYe,1164),m(1165,1,it,GMe),s.Ad=function(n){hyn(this.b,this.a,u(n,251))},v(KZ,iwe,1165),m(214,1,Qw),v(gv,"AbstractLayoutProvider",214),m(726,214,Qw,goe),s.kf=function(n,t){CUe(this,n,t)},v(KZ,"ForceLayoutProvider",726);var _Bn=Hi(kN,eQe);m(150,1,{3:1,105:1,150:1},Mt),s.of=function(n,t){return jO(this,n,t)},s.lf=function(){return jDe(this)},s.mf=function(n){return T(this,n)},s.nf=function(n){return bi(this,n)},v(kN,"MapPropertyHolder",150),m(313,150,{3:1,313:1,105:1,150:1}),v(jN,"FParticle",313),m(251,313,{3:1,251:1,313:1,105:1,150:1},oIe),s.Ib=function(){var n;return this.a?(n=wu(this.a.a,this,0),n>=0?"b"+n+"["+tY(this.a)+"]":"b["+tY(this.a)+"]"):"b_"+vw(this)},v(jN,"FBendpoint",251),m(291,150,{3:1,291:1,105:1,150:1},nNe),s.Ib=function(){return tY(this)},v(jN,"FEdge",291),m(235,150,{3:1,235:1,105:1,150:1},YR);var LBn=v(jN,"FGraph",235);m(445,313,{3:1,445:1,313:1,105:1,150:1},lPe),s.Ib=function(){return this.b==null||this.b.length==0?"l["+tY(this.a)+"]":"l_"+this.b},v(jN,"FLabel",445),m(155,313,{3:1,155:1,313:1,105:1,150:1},$Ce),s.Ib=function(){return Aae(this)},s.a=0,v(jN,"FNode",155),m(2062,1,{}),s.qf=function(n){ige(this,n)},s.rf=function(){dHe(this)},s.d=0,v(rwe,"AbstractForceModel",2062),m(631,2062,{631:1},tze),s.pf=function(n,t){var i,r,c,o,l;return YKe(this.f,n,t),c=Nr(pc(t.d),n.d),l=k.Math.sqrt(c.a*c.a+c.b*c.b),r=k.Math.max(0,l-fE(n.e)/2-fE(t.e)/2),i=Cqe(this.e,n,t),i>0?o=-Tvn(r,this.c)*i:o=ppn(r,this.b)*u(T(n,(Gf(),ky)),15).a,A1(c,o/l),c},s.qf=function(n){ige(this,n),this.a=u(T(n,(Gf(),mJ)),15).a,this.c=ne(re(T(n,vJ))),this.b=ne(re(T(n,yte)))},s.sf=function(n){return n0&&(o-=Mbn(r,this.a)*i),A1(c,o*this.b/l),c},s.qf=function(n){var t,i,r,c,o,l,f;for(ige(this,n),this.b=ne(re(T(n,(Gf(),kte)))),this.c=this.b/u(T(n,mJ),15).a,r=n.e.c.length,o=0,c=0,f=new L(n.e);f.a0},s.a=0,s.b=0,s.c=0,v(rwe,"FruchtermanReingoldModel",632);var yy=Hi(vu,"ILayoutMetaDataProvider");m(844,1,qa,pT),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,oF),""),"Force Model"),"Determines the model for force calculation."),e3e),(cg(),Ri)),n3e),nn((ph(),Mn))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,cwe),""),"Iterations"),"The number of iterations on the force model."),ve(300)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,uwe),""),"Repulsive Power"),"Determines how many bend points are added to the edge; such bend points are regarded as repelling particles in the force model"),ve(0)),hc),jr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,VZ),""),"FR Temperature"),"The temperature is used as a scaling factor for particle displacements."),Eh),ec),gr),nn(Mn)))),Gi(n,VZ,oF,htn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,YZ),""),"Eades Repulsion"),"Factor for repulsive forces in Eades' model."),5),ec),gr),nn(Mn)))),Gi(n,YZ,oF,ltn),RVe((new eP,n))};var utn,otn,e3e,stn,ltn,ftn,atn,htn;v(yS,"ForceMetaDataProvider",844),m(424,23,{3:1,35:1,23:1,424:1},fse);var vte,pJ,n3e=yt(yS,"ForceModelStrategy",424,Ct,s4n,N2n),dtn;m(984,1,qa,eP),s.tf=function(n){RVe(n)};var btn,gtn,t3e,mJ,i3e,wtn,ptn,mtn,vtn,r3e,ytn,c3e,u3e,ktn,ky,jtn,yte,o3e,Etn,Stn,vJ,kte,xtn,Atn,Mtn,s3e,Ttn;v(yS,"ForceOptions",984),m(985,1,{},Tc),s.uf=function(){var n;return n=new goe,n},s.vf=function(n){},v(yS,"ForceOptions/ForceFactory",985);var KN,WS,jy,yJ;m(845,1,qa,nP),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,swe),""),"Fixed Position"),"Prevent that the node is moved by the layout algorithm."),(Pn(),!1)),(cg(),Sr)),Yi),nn((ph(),fr))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,lwe),""),"Desired Edge Length"),"Either specified for parent nodes or for individual edges, where the latter takes higher precedence."),100),ec),gr),Mi(Mn,z(B(Qa,1),ke,160,0,[Sa]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,fwe),""),"Layout Dimension"),"Dimensions that are permitted to be altered during layout."),l3e),Ri),w3e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,awe),""),"Stress Epsilon"),"Termination criterion for the iterative process."),Eh),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hwe),""),"Iteration Limit"),"Maximum number of performed iterations. Takes higher precedence than 'epsilon'."),ve(oi)),hc),jr),nn(Mn)))),hVe((new xU,n))};var Ctn,Otn,l3e,Ntn,Dtn,Itn;v(yS,"StressMetaDataProvider",845),m(988,1,qa,xU),s.tf=function(n){hVe(n)};var kJ,f3e,a3e,h3e,d3e,b3e,_tn,Ltn,Ptn,$tn,g3e,Rtn;v(yS,"StressOptions",988),m(989,1,{},dc),s.uf=function(){var n;return n=new tNe,n},s.vf=function(n){},v(yS,"StressOptions/StressFactory",989),m(1080,214,Qw,tNe),s.kf=function(n,t){var i,r,c,o,l;for(t.Tg(cQe,1),Re($e(ye(n,(PO(),d3e))))?Re($e(ye(n,g3e)))||HC((i=new hj((_b(),new w0(n))),i)):CUe(new goe,n,t.dh(1)),c=Nze(n),r=EKe(this.a,c),l=r.Jc();l.Ob();)o=u(l.Pb(),235),!(o.e.c.length<=1)&&(HLn(this.b,o),gOn(this.b),Ao(o.d,new s6));c=LVe(r),GVe(c),t.Ug()},v(fF,"StressLayoutProvider",1080),m(1081,1,it,s6),s.Ad=function(n){hge(u(n,445))},v(fF,"StressLayoutProvider/lambda$0$Type",1081),m(986,1,{},nxe),s.c=0,s.e=0,s.g=0,v(fF,"StressMajorization",986),m(384,23,{3:1,35:1,23:1,384:1},XX);var jte,Ete,Ste,w3e=yt(fF,"StressMajorization/Dimension",384,Ct,Y4n,D2n),Btn;m(987,1,Yt,eje),s.Le=function(n,t){return s2n(this.a,u(n,155),u(t,155))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fF,"StressMajorization/lambda$0$Type",987),m(1161,1,{},wLe),v(by,"ElkLayered",1161),m(1162,1,it,nje),s.Ad=function(n){iCn(this.a,u(n,37))},v(by,"ElkLayered/lambda$0$Type",1162),m(1163,1,it,tje),s.Ad=function(n){b2n(this.a,u(n,37))},v(by,"ElkLayered/lambda$1$Type",1163),m(1246,1,{},LCe);var ztn,Ftn,Jtn;v(by,"GraphConfigurator",1246),m(757,1,it,Zue),s.Ad=function(n){OGe(this.a,u(n,9))},v(by,"GraphConfigurator/lambda$0$Type",757),m(758,1,{},Af),s.Kb=function(n){return Vde(),new wn(null,new pn(u(n,25).a,16))},v(by,"GraphConfigurator/lambda$1$Type",758),m(759,1,it,eoe),s.Ad=function(n){OGe(this.a,u(n,9))},v(by,"GraphConfigurator/lambda$2$Type",759),m(1079,214,Qw,ixe),s.kf=function(n,t){var i;i=kLn(new lxe,n),ue(ye(n,(Ne(),wm)))===ue((B1(),Yd))?Ojn(this.a,i,t):aOn(this.a,i,t),t.Zg()||TVe(new vT,i)},v(by,"LayeredLayoutProvider",1079),m(363,23,{3:1,35:1,23:1,363:1},uC);var Kf,r1,eo,no,Pc,p3e=yt(by,"LayeredPhases",363,Ct,U6n,I2n),Htn;m(1683,1,{},jBe),s.i=0;var Gtn;v(TN,"ComponentsToCGraphTransformer",1683);var qtn;m(1684,1,{},Zs),s.wf=function(n,t){return k.Math.min(n.a!=null?ne(n.a):n.c.i,t.a!=null?ne(t.a):t.c.i)},s.xf=function(n,t){return k.Math.min(n.a!=null?ne(n.a):n.c.i,t.a!=null?ne(t.a):t.c.i)},v(TN,"ComponentsToCGraphTransformer/1",1684),m(82,1,{82:1}),s.i=0,s.k=!0,s.o=Dr;var xte=v(SS,"CNode",82);m(460,82,{460:1,82:1},hle,Sde),s.Ib=function(){return""},v(TN,"ComponentsToCGraphTransformer/CRectNode",460),m(1652,1,{},Ca);var Ate,Mte;v(TN,"OneDimensionalComponentsCompaction",1652),m(1653,1,{},Xg),s.Kb=function(n){return T4n(u(n,49))},s.Fb=function(n){return this===n},v(TN,"OneDimensionalComponentsCompaction/lambda$0$Type",1653),m(1654,1,{},b5),s.Kb=function(n){return Ljn(u(n,49))},s.Fb=function(n){return this===n},v(TN,"OneDimensionalComponentsCompaction/lambda$1$Type",1654),m(1686,1,{},vIe),v(SS,"CGraph",1686),m(194,1,{194:1},CQ),s.b=0,s.c=0,s.e=0,s.g=!0,s.i=Dr,v(SS,"CGroup",194),m(1685,1,{},ek),s.wf=function(n,t){return k.Math.max(n.a!=null?ne(n.a):n.c.i,t.a!=null?ne(t.a):t.c.i)},s.xf=function(n,t){return k.Math.max(n.a!=null?ne(n.a):n.c.i,t.a!=null?ne(t.a):t.c.i)},v(SS,PYe,1685),m(1687,1,{},Nqe),s.d=!1;var Utn,Tte=v(SS,BYe,1687);m(1688,1,{},MA),s.Kb=function(n){return Zoe(),Pn(),u(u(n,49).a,82).d.e!=0},s.Fb=function(n){return this===n},v(SS,zYe,1688),m(817,1,{},mfe),s.a=!1,s.b=!1,s.c=!1,s.d=!1,v(SS,FYe,817),m(1868,1,{},IDe),v(aF,JYe,1868);var VN=Hi(fg,_Ye);m(1869,1,{377:1},w_e),s._e=function(n){mDn(this,u(n,465))},v(aF,HYe,1869),m(1870,1,Yt,nk),s.Le=function(n,t){return k5n(u(n,82),u(t,82))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(aF,GYe,1870),m(465,1,{465:1},ase),s.a=!1,v(aF,qYe,465),m(1871,1,Yt,e3),s.Le=function(n,t){return Xxn(u(n,465),u(t,465))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(aF,UYe,1871),m(146,1,{146:1},v9,ofe),s.Fb=function(n){var t;return n==null||PBn!=Us(n)?!1:(t=u(n,146),Ku(this.c,t.c)&&Ku(this.d,t.d))},s.Hb=function(){return RB(z(B(Ar,1),On,1,5,[this.c,this.d]))},s.Ib=function(){return"("+this.c+Co+this.d+(this.a?"cx":"")+this.b+")"},s.a=!0,s.c=0,s.d=0;var PBn=v(fg,"Point",146);m(408,23,{3:1,35:1,23:1,408:1},s$);var op,om,xv,sm,Xtn=yt(fg,"Point/Quadrant",408,Ct,Qyn,O2n),Ktn;m(1674,1,{},rxe),s.b=null,s.c=null,s.d=null,s.e=null,s.f=null;var Vtn,Ytn,Qtn,Wtn,Ztn;v(fg,"RectilinearConvexHull",1674),m(569,1,{377:1},cz),s._e=function(n){$9n(this,u(n,146))},s.b=0;var m3e;v(fg,"RectilinearConvexHull/MaximalElementsEventHandler",569),m(1676,1,Yt,l6),s.Le=function(n,t){return v5n(re(n),re(t))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/MaximalElementsEventHandler/lambda$0$Type",1676),m(1675,1,{377:1},TRe),s._e=function(n){_Nn(this,u(n,146))},s.a=0,s.b=null,s.c=null,s.d=null,s.e=null,v(fg,"RectilinearConvexHull/RectangleEventHandler",1675),m(1677,1,Yt,xp),s.Le=function(n,t){return kyn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$0$Type",1677),m(1678,1,Yt,Ap),s.Le=function(n,t){return jyn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$1$Type",1678),m(1679,1,Yt,Mp),s.Le=function(n,t){return Syn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$2$Type",1679),m(1680,1,Yt,Tp),s.Le=function(n,t){return Eyn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$3$Type",1680),m(1681,1,Yt,xl),s.Le=function(n,t){return OMn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$4$Type",1681),m(1682,1,{},q_e),v(fg,"Scanline",1682),m(2066,1,{}),v(Ua,"AbstractGraphPlacer",2066),m(336,1,{336:1},AOe),s.Df=function(n){return this.Ef(n)?(gn(this.b,u(T(n,(me(),K1)),22),n),!0):!1},s.Ef=function(n){var t,i,r,c;for(t=u(T(n,(me(),K1)),22),c=u(pi(xi,t),22),r=c.Jc();r.Ob();)if(i=u(r.Pb(),22),!u(pi(this.b,i),16).dc())return!1;return!0};var xi;v(Ua,"ComponentGroup",336),m(766,2066,{},woe),s.Ff=function(n){var t,i;for(i=new L(this.a);i.ai&&(p=0,y+=f+r,f=0),h=o.c,M8(o,p+h.a,y+h.b),la(h),c=k.Math.max(c,p+b.a),f=k.Math.max(f,b.b),p+=b.a+r;t.f.a=c,t.f.b=y+f},s.Hf=function(n,t){var i,r,c,o,l;if(ue(T(t,(Ne(),dx)))===ue((X4(),ZS))){for(r=n.Jc();r.Ob();){for(i=u(r.Pb(),37),l=0,o=new L(i.a);o.ai&&!u(T(o,(me(),K1)),22).Gc((De(),Xn))||h&&u(T(h,(me(),K1)),22).Gc((De(),et))||u(T(o,(me(),K1)),22).Gc((De(),Kn)))&&(S=y,A+=f+r,f=0),b=o.c,u(T(o,(me(),K1)),22).Gc((De(),Xn))&&(S=c+r),M8(o,S+b.a,A+b.b),c=k.Math.max(c,S+p.a),u(T(o,K1),22).Gc(bt)&&(y=k.Math.max(y,S+p.a+r)),la(b),f=k.Math.max(f,p.b),S+=p.a+r,h=o;t.f.a=c,t.f.b=A+f},s.Hf=function(n,t){},v(Ua,"ModelOrderRowGraphPlacer",1277),m(1275,1,Yt,TA),s.Le=function(n,t){return $7n(u(n,37),u(t,37))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ua,"SimpleRowGraphPlacer/1",1275);var nin;m(1245,1,jh,f6),s.Lb=function(n){var t;return t=u(T(u(n,250).b,(Ne(),Wc)),78),!!t&&t.b!=0},s.Fb=function(n){return this===n},s.Mb=function(n){var t;return t=u(T(u(n,250).b,(Ne(),Wc)),78),!!t&&t.b!=0},v(hF,"CompoundGraphPostprocessor/1",1245),m(1244,1,Ai,fxe),s.If=function(n,t){VJe(this,u(n,37),t)},v(hF,"CompoundGraphPreprocessor",1244),m(444,1,{444:1},PFe),s.c=!1,v(hF,"CompoundGraphPreprocessor/ExternalPort",444),m(250,1,{250:1},Y$),s.Ib=function(){return $K(this.c)+":"+xqe(this.b)},v(hF,"CrossHierarchyEdge",250),m(764,1,Yt,noe),s.Le=function(n,t){return vxn(this,u(n,250),u(t,250))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(hF,"CrossHierarchyEdgeComparator",764),m(246,150,{3:1,246:1,105:1,150:1}),s.p=0,v(Zu,"LGraphElement",246),m(17,246,{3:1,17:1,246:1,105:1,150:1},Mw),s.Ib=function(){return xqe(this)};var b7=v(Zu,"LEdge",17);m(37,246,{3:1,20:1,37:1,246:1,105:1,150:1},$he),s.Ic=function(n){rc(this,n)},s.Jc=function(){return new L(this.b)},s.Ib=function(){return this.b.c.length==0?"G-unlayered"+Fa(this.a):this.a.c.length==0?"G-layered"+Fa(this.b):"G[layerless"+Fa(this.a)+", layers"+Fa(this.b)+"]"};var tin=v(Zu,"LGraph",37),iin;m(655,1,{}),s.Jf=function(){return this.e.n},s.mf=function(n){return T(this.e,n)},s.Kf=function(){return this.e.o},s.Lf=function(){return this.e.p},s.nf=function(n){return bi(this.e,n)},s.Mf=function(n){this.e.n.a=n.a,this.e.n.b=n.b},s.Nf=function(n){this.e.o.a=n.a,this.e.o.b=n.b},s.Of=function(n){this.e.p=n},v(Zu,"LGraphAdapters/AbstractLShapeAdapter",655),m(464,1,{837:1},dj),s.Pf=function(){var n,t;if(!this.b)for(this.b=Fh(this.a.b.c.length),t=new L(this.a.b);t.a0&&hFe((Yn(t-1,n.length),n.charCodeAt(t-1)),aQe);)--t;if(o> ",n),bz(i)),Kt(uo((n.a+="[",n),i.i),"]")),n.a},s.c=!0,s.d=!1;var E3e,S3e,x3e,A3e,M3e,T3e,cin=v(Zu,"LPort",12);m(399,1,Wh,o9),s.Ic=function(n){rc(this,n)},s.Jc=function(){var n;return n=new L(this.a.e),new ije(n)},v(Zu,"LPort/1",399),m(1273,1,Fr,ije),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(_(this.a),17).c},s.Ob=function(){return bu(this.a)},s.Qb=function(){oE(this.a)},v(Zu,"LPort/1/1",1273),m(365,1,Wh,W5),s.Ic=function(n){rc(this,n)},s.Jc=function(){var n;return n=new L(this.a.g),new toe(n)},v(Zu,"LPort/2",365),m(763,1,Fr,toe),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(_(this.a),17).d},s.Ob=function(){return bu(this.a)},s.Qb=function(){oE(this.a)},v(Zu,"LPort/2/1",763),m(1266,1,Wh,UMe),s.Ic=function(n){rc(this,n)},s.Jc=function(){return new La(this)},v(Zu,"LPort/CombineIter",1266),m(207,1,Fr,La),s.Nb=function(n){Zr(this,n)},s.Qb=function(){xAe()},s.Ob=function(){return Wj(this)},s.Pb=function(){return bu(this.a)?_(this.a):_(this.b)},v(Zu,"LPort/CombineIter/1",207),m(1267,1,jh,w5),s.Lb=function(n){return GDe(n)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).g.c.length!=0},v(Zu,"LPort/lambda$0$Type",1267),m(1268,1,jh,s0),s.Lb=function(n){return qDe(n)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).e.c.length!=0},v(Zu,"LPort/lambda$1$Type",1268),m(1269,1,jh,Ih),s.Lb=function(n){return ss(),u(n,12).j==(De(),Xn)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).j==(De(),Xn)},v(Zu,"LPort/lambda$2$Type",1269),m(1270,1,jh,ck),s.Lb=function(n){return ss(),u(n,12).j==(De(),et)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).j==(De(),et)},v(Zu,"LPort/lambda$3$Type",1270),m(1271,1,jh,CA),s.Lb=function(n){return ss(),u(n,12).j==(De(),bt)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).j==(De(),bt)},v(Zu,"LPort/lambda$4$Type",1271),m(1272,1,jh,p5),s.Lb=function(n){return ss(),u(n,12).j==(De(),Kn)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).j==(De(),Kn)},v(Zu,"LPort/lambda$5$Type",1272),m(25,246,{3:1,20:1,246:1,25:1,105:1,150:1},Xu),s.Ic=function(n){rc(this,n)},s.Jc=function(){return new L(this.a)},s.Ib=function(){return"L_"+wu(this.b.b,this,0)+Fa(this.a)},v(Zu,"Layer",25),m(1659,1,{},_$e),s.b=0,v(Zu,"Tarjan",1659),m(1282,1,{},lxe),v(zd,gQe,1282),m(1286,1,{},uk),s.Kb=function(n){return iu(u(n,84))},v(zd,"ElkGraphImporter/0methodref$connectableShapeToNode$Type",1286),m(1289,1,{},t3),s.Kb=function(n){return iu(u(n,84))},v(zd,"ElkGraphImporter/1methodref$connectableShapeToNode$Type",1289),m(1283,1,it,rje),s.Ad=function(n){Jqe(this.a,u(n,125))},v(zd,iwe,1283),m(1284,1,it,cje),s.Ad=function(n){Jqe(this.a,u(n,125))},v(zd,wQe,1284),m(1285,1,{},_h),s.Kb=function(n){return new wn(null,new pn(tae(u(n,85)),16))},v(zd,pQe,1285),m(1287,1,Ft,uje),s.Mb=function(n){return fwn(this.a,u(n,26))},v(zd,mQe,1287),m(1288,1,{},i3),s.Kb=function(n){return new wn(null,new pn(w5n(u(n,85)),16))},v(zd,"ElkGraphImporter/lambda$5$Type",1288),m(1290,1,Ft,oje),s.Mb=function(n){return awn(this.a,u(n,26))},v(zd,"ElkGraphImporter/lambda$7$Type",1290),m(1291,1,Ft,ok),s.Mb=function(n){return N5n(u(n,85))},v(zd,"ElkGraphImporter/lambda$8$Type",1291),m(1261,1,{},vT);var uin;v(zd,"ElkGraphLayoutTransferrer",1261),m(1262,1,Ft,sje),s.Mb=function(n){return t2n(this.a,u(n,17))},v(zd,"ElkGraphLayoutTransferrer/lambda$0$Type",1262),m(1263,1,it,lje),s.Ad=function(n){iC(),Te(this.a,u(n,17))},v(zd,"ElkGraphLayoutTransferrer/lambda$1$Type",1263),m(1264,1,Ft,fje),s.Mb=function(n){return Jpn(this.a,u(n,17))},v(zd,"ElkGraphLayoutTransferrer/lambda$2$Type",1264),m(1265,1,it,aje),s.Ad=function(n){iC(),Te(this.a,u(n,17))},v(zd,"ElkGraphLayoutTransferrer/lambda$3$Type",1265),m(806,1,{},Lle),v(Wn,"BiLinkedHashMultiMap",806),m(1511,1,Ai,a6),s.If=function(n,t){t7n(u(n,37),t)},v(Wn,"CommentNodeMarginCalculator",1511),m(1512,1,{},Vg),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"CommentNodeMarginCalculator/lambda$0$Type",1512),m(1513,1,it,vI),s.Ad=function(n){mLn(u(n,9))},v(Wn,"CommentNodeMarginCalculator/lambda$1$Type",1513),m(1514,1,Ai,OA),s.If=function(n,t){xDn(u(n,37),t)},v(Wn,"CommentPostprocessor",1514),m(1515,1,Ai,yI),s.If=function(n,t){q$n(u(n,37),t)},v(Wn,"CommentPreprocessor",1515),m(1516,1,Ai,m5),s.If=function(n,t){RNn(u(n,37),t)},v(Wn,"ConstraintsPostprocessor",1516),m(1517,1,Ai,uq),s.If=function(n,t){M7n(u(n,37),t)},v(Wn,"EdgeAndLayerConstraintEdgeReverser",1517),m(1518,1,Ai,kI),s.If=function(n,t){tEn(u(n,37),t)},v(Wn,"EndLabelPostprocessor",1518),m(1519,1,{},jI),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"EndLabelPostprocessor/lambda$0$Type",1519),m(1520,1,Ft,NA),s.Mb=function(n){return z6n(u(n,9))},v(Wn,"EndLabelPostprocessor/lambda$1$Type",1520),m(1521,1,it,oq),s.Ad=function(n){Kxn(u(n,9))},v(Wn,"EndLabelPostprocessor/lambda$2$Type",1521),m(1522,1,Ai,sq),s.If=function(n,t){OTn(u(n,37),t)},v(Wn,"EndLabelPreprocessor",1522),m(1523,1,{},sk),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"EndLabelPreprocessor/lambda$0$Type",1523),m(1524,1,it,LNe),s.Ad=function(n){xgn(this.a,this.b,this.c,u(n,9))},s.a=0,s.b=0,s.c=!1,v(Wn,"EndLabelPreprocessor/lambda$1$Type",1524),m(1525,1,Ft,Yg),s.Mb=function(n){return ue(T(u(n,70),(Ne(),Oh)))===ue(($a(),J7))},v(Wn,"EndLabelPreprocessor/lambda$2$Type",1525),m(1526,1,it,hje),s.Ad=function(n){Vt(this.a,u(n,70))},v(Wn,"EndLabelPreprocessor/lambda$3$Type",1526),m(1527,1,Ft,DA),s.Mb=function(n){return ue(T(u(n,70),(Ne(),Oh)))===ue(($a(),_m))},v(Wn,"EndLabelPreprocessor/lambda$4$Type",1527),m(1528,1,it,dje),s.Ad=function(n){Vt(this.a,u(n,70))},v(Wn,"EndLabelPreprocessor/lambda$5$Type",1528),m(1576,1,Ai,AU),s.If=function(n,t){pjn(u(n,37),t)};var oin;v(Wn,"EndLabelSorter",1576),m(1577,1,Yt,IA),s.Le=function(n,t){return REn(u(n,455),u(t,455))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"EndLabelSorter/1",1577),m(455,1,{455:1},o_e),v(Wn,"EndLabelSorter/LabelGroup",455),m(1578,1,{},v5),s.Kb=function(n){return tC(),new wn(null,new pn(u(n,25).a,16))},v(Wn,"EndLabelSorter/lambda$0$Type",1578),m(1579,1,Ft,y5),s.Mb=function(n){return tC(),u(n,9).k==(zn(),Qi)},v(Wn,"EndLabelSorter/lambda$1$Type",1579),m(1580,1,it,EI),s.Ad=function(n){GMn(u(n,9))},v(Wn,"EndLabelSorter/lambda$2$Type",1580),m(1581,1,Ft,_A),s.Mb=function(n){return tC(),ue(T(u(n,70),(Ne(),Oh)))===ue(($a(),_m))},v(Wn,"EndLabelSorter/lambda$3$Type",1581),m(1582,1,Ft,SI),s.Mb=function(n){return tC(),ue(T(u(n,70),(Ne(),Oh)))===ue(($a(),J7))},v(Wn,"EndLabelSorter/lambda$4$Type",1582),m(1529,1,Ai,h6),s.If=function(n,t){LLn(this,u(n,37))},s.b=0,s.c=0,v(Wn,"FinalSplineBendpointsCalculator",1529),m(1530,1,{},Qg),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"FinalSplineBendpointsCalculator/lambda$0$Type",1530),m(1531,1,{},LA),s.Kb=function(n){return new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(Wn,"FinalSplineBendpointsCalculator/lambda$1$Type",1531),m(1532,1,Ft,d6),s.Mb=function(n){return!cc(u(n,17))},v(Wn,"FinalSplineBendpointsCalculator/lambda$2$Type",1532),m(1533,1,Ft,Op),s.Mb=function(n){return bi(u(n,17),(me(),vg))},v(Wn,"FinalSplineBendpointsCalculator/lambda$3$Type",1533),m(1534,1,it,bje),s.Ad=function(n){HIn(this.a,u(n,132))},v(Wn,"FinalSplineBendpointsCalculator/lambda$4$Type",1534),m(1535,1,it,PA),s.Ad=function(n){HO(u(n,17).a)},v(Wn,"FinalSplineBendpointsCalculator/lambda$5$Type",1535),m(790,1,Ai,ioe),s.If=function(n,t){CPn(this,u(n,37),t)},v(Wn,"GraphTransformer",790),m(502,23,{3:1,35:1,23:1,502:1},hse);var Dte,QN,sin=yt(Wn,"GraphTransformer/Mode",502,Ct,l4n,P2n),lin;m(1536,1,Ai,lk),s.If=function(n,t){QOn(u(n,37),t)},v(Wn,"HierarchicalNodeResizingProcessor",1536),m(1537,1,Ai,xI),s.If=function(n,t){H8n(u(n,37),t)},v(Wn,"HierarchicalPortConstraintProcessor",1537),m(1538,1,Yt,fk),s.Le=function(n,t){return rSn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"HierarchicalPortConstraintProcessor/NodeComparator",1538),m(1539,1,Ai,ak),s.If=function(n,t){P_n(u(n,37),t)},v(Wn,"HierarchicalPortDummySizeProcessor",1539),m(1540,1,Ai,AI),s.If=function(n,t){VDn(this,u(n,37),t)},s.a=0,v(Wn,"HierarchicalPortOrthogonalEdgeRouter",1540),m(1541,1,Yt,Lh),s.Le=function(n,t){return rpn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"HierarchicalPortOrthogonalEdgeRouter/1",1541),m(1542,1,Yt,r3),s.Le=function(n,t){return J9n(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"HierarchicalPortOrthogonalEdgeRouter/2",1542),m(1543,1,Ai,hk),s.If=function(n,t){MMn(u(n,37),t)},v(Wn,"HierarchicalPortPositionProcessor",1543),m(1544,1,Ai,mT),s.If=function(n,t){TRn(this,u(n,37))},s.a=0,s.c=0;var jJ,EJ;v(Wn,"HighDegreeNodeLayeringProcessor",1544),m(566,1,{566:1},b6),s.b=-1,s.d=-1,v(Wn,"HighDegreeNodeLayeringProcessor/HighDegreeNodeInformation",566),m(1545,1,{},lq),s.Kb=function(n){return OC(),rr(u(n,9))},s.Fb=function(n){return this===n},v(Wn,"HighDegreeNodeLayeringProcessor/lambda$0$Type",1545),m(1546,1,{},$A),s.Kb=function(n){return OC(),Ni(u(n,9))},s.Fb=function(n){return this===n},v(Wn,"HighDegreeNodeLayeringProcessor/lambda$1$Type",1546),m(1552,1,Ai,RA),s.If=function(n,t){M_n(this,u(n,37),t)},v(Wn,"HyperedgeDummyMerger",1552),m(791,1,{},Wle),s.a=!1,s.b=!1,s.c=!1,v(Wn,"HyperedgeDummyMerger/MergeState",791),m(1553,1,{},g6),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"HyperedgeDummyMerger/lambda$0$Type",1553),m(1554,1,{},dk),s.Kb=function(n){return new wn(null,new pn(u(n,9).j,16))},v(Wn,"HyperedgeDummyMerger/lambda$1$Type",1554),m(1555,1,it,MI),s.Ad=function(n){u(n,12).p=-1},v(Wn,"HyperedgeDummyMerger/lambda$2$Type",1555),m(1556,1,Ai,aq),s.If=function(n,t){A_n(u(n,37),t)},v(Wn,"HypernodesProcessor",1556),m(1557,1,Ai,hq),s.If=function(n,t){L_n(u(n,37),t)},v(Wn,"InLayerConstraintProcessor",1557),m(1558,1,Ai,BA),s.If=function(n,t){p7n(u(n,37),t)},v(Wn,"InnermostNodeMarginCalculator",1558),m(1559,1,Ai,dq),s.If=function(n,t){F$n(this,u(n,37))},s.a=Dr,s.b=Dr,s.c=Ki,s.d=Ki;var $Bn=v(Wn,"InteractiveExternalPortPositioner",1559);m(1560,1,{},bq),s.Kb=function(n){return u(n,17).d.i},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$0$Type",1560),m(1561,1,{},gje),s.Kb=function(n){return cpn(this.a,re(n))},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$1$Type",1561),m(1562,1,{},gq),s.Kb=function(n){return u(n,17).c.i},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$2$Type",1562),m(1563,1,{},wje),s.Kb=function(n){return upn(this.a,re(n))},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$3$Type",1563),m(1564,1,{},pje),s.Kb=function(n){return e2n(this.a,re(n))},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$4$Type",1564),m(1565,1,{},mje),s.Kb=function(n){return n2n(this.a,re(n))},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$5$Type",1565),m(79,23,{3:1,35:1,23:1,79:1,196:1},br),s.bg=function(){switch(this.g){case 15:return new ew;case 22:return new Pp;case 48:return new uM;case 29:case 36:return new Eq;case 33:return new a6;case 43:return new OA;case 1:return new yI;case 42:return new m5;case 57:return new ioe((K9(),QN));case 0:return new ioe((K9(),Dte));case 2:return new uq;case 55:return new kI;case 34:return new sq;case 52:return new h6;case 56:return new lk;case 13:return new xI;case 39:return new ak;case 45:return new AI;case 41:return new hk;case 9:return new mT;case 50:return new vOe;case 38:return new RA;case 44:return new aq;case 28:return new hq;case 31:return new BA;case 3:return new dq;case 18:return new fq;case 30:return new wq;case 5:return new MU;case 51:return new vq;case 35:return new Y6;case 37:return new Sq;case 53:return new AU;case 11:return new CI;case 7:return new TU;case 40:return new xq;case 46:return new Aq;case 16:return new Mq;case 10:return new kTe;case 49:return new Nq;case 21:return new Dq;case 23:return new zP((eg(),Mx));case 8:return new FA;case 12:return new _q;case 4:return new OI;case 19:return new tP;case 17:return new PI;case 54:return new p6;case 6:return new Fq;case 25:return new hxe;case 26:return new rM;case 47:return new HA;case 32:return new uNe;case 14:return new GI;case 27:return new Vq;case 20:return new y6;case 24:return new zP((eg(),CH));default:throw $(new Gn(nee+(this.f!=null?this.f:""+this.g)))}};var C3e,O3e,N3e,D3e,I3e,_3e,L3e,P3e,$3e,R3e,B3e,Av,SJ,xJ,z3e,F3e,J3e,H3e,G3e,q3e,U3e,nx,X3e,K3e,V3e,Y3e,Q3e,Ite,AJ,MJ,W3e,TJ,CJ,OJ,g7,lm,fm,Z3e,NJ,DJ,eve,IJ,_J,nve,tve,ive,rve,LJ,_te,Ey,PJ,$J,RJ,BJ,cve,uve,ove,sve,RBn=yt(Wn,tee,79,Ct,FUe,$2n),fin;m(1566,1,Ai,fq),s.If=function(n,t){R$n(u(n,37),t)},v(Wn,"InvertedPortProcessor",1566),m(1567,1,Ai,wq),s.If=function(n,t){$In(u(n,37),t)},v(Wn,"LabelAndNodeSizeProcessor",1567),m(1568,1,Ft,pq),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"LabelAndNodeSizeProcessor/lambda$0$Type",1568),m(1569,1,Ft,TI),s.Mb=function(n){return u(n,9).k==(zn(),wr)},v(Wn,"LabelAndNodeSizeProcessor/lambda$1$Type",1569),m(1570,1,it,RNe),s.Ad=function(n){Agn(this.b,this.a,this.c,u(n,9))},s.a=!1,s.c=!1,v(Wn,"LabelAndNodeSizeProcessor/lambda$2$Type",1570),m(1571,1,Ai,MU),s.If=function(n,t){w$n(u(n,37),t)};var ain;v(Wn,"LabelDummyInserter",1571),m(1572,1,jh,mq),s.Lb=function(n){return ue(T(u(n,70),(Ne(),Oh)))===ue(($a(),F7))},s.Fb=function(n){return this===n},s.Mb=function(n){return ue(T(u(n,70),(Ne(),Oh)))===ue(($a(),F7))},v(Wn,"LabelDummyInserter/1",1572),m(1573,1,Ai,vq),s.If=function(n,t){i$n(u(n,37),t)},v(Wn,"LabelDummyRemover",1573),m(1574,1,Ft,yq),s.Mb=function(n){return Re($e(T(u(n,70),(Ne(),Rv))))},v(Wn,"LabelDummyRemover/lambda$0$Type",1574),m(1332,1,Ai,Y6),s.If=function(n,t){QPn(this,u(n,37),t)},s.a=null;var Lte;v(Wn,"LabelDummySwitcher",1332),m(294,1,{294:1},$Xe),s.c=0,s.d=null,s.f=0,v(Wn,"LabelDummySwitcher/LabelDummyInfo",294),m(1333,1,{},kq),s.Kb=function(n){return z4(),new wn(null,new pn(u(n,25).a,16))},v(Wn,"LabelDummySwitcher/lambda$0$Type",1333),m(1334,1,Ft,zA),s.Mb=function(n){return z4(),u(n,9).k==(zn(),Uu)},v(Wn,"LabelDummySwitcher/lambda$1$Type",1334),m(1335,1,{},vje),s.Kb=function(n){return Hpn(this.a,u(n,9))},v(Wn,"LabelDummySwitcher/lambda$2$Type",1335),m(1336,1,it,yje),s.Ad=function(n){qvn(this.a,u(n,294))},v(Wn,"LabelDummySwitcher/lambda$3$Type",1336),m(1337,1,Yt,jq),s.Le=function(n,t){return yvn(u(n,294),u(t,294))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"LabelDummySwitcher/lambda$4$Type",1337),m(789,1,Ai,Eq),s.If=function(n,t){j9n(u(n,37),t)},v(Wn,"LabelManagementProcessor",789),m(1575,1,Ai,Sq),s.If=function(n,t){bDn(u(n,37),t)},v(Wn,"LabelSideSelector",1575),m(1583,1,Ai,CI),s.If=function(n,t){Z_n(u(n,37),t)},v(Wn,"LayerConstraintPostprocessor",1583),m(1584,1,Ai,TU),s.If=function(n,t){QCn(u(n,37),t)};var lve;v(Wn,"LayerConstraintPreprocessor",1584),m(367,23,{3:1,35:1,23:1,367:1},f$);var WN,zJ,FJ,Pte,hin=yt(Wn,"LayerConstraintPreprocessor/HiddenNodeConnections",367,Ct,e6n,vmn),din;m(1585,1,Ai,xq),s.If=function(n,t){pPn(u(n,37),t)},v(Wn,"LayerSizeAndGraphHeightCalculator",1585),m(1586,1,Ai,Aq),s.If=function(n,t){WOn(u(n,37),t)},v(Wn,"LongEdgeJoiner",1586),m(1587,1,Ai,Mq),s.If=function(n,t){XLn(u(n,37),t)},v(Wn,"LongEdgeSplitter",1587),m(1588,1,Ai,kTe),s.If=function(n,t){O$n(this,u(n,37),t)},s.e=0,s.f=0,s.j=0,s.k=0,s.n=0,s.o=0;var bin,gin;v(Wn,"NodePromotion",1588),m(1589,1,Yt,Tq),s.Le=function(n,t){return kkn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"NodePromotion/1",1589),m(1590,1,Yt,Cq),s.Le=function(n,t){return jkn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"NodePromotion/2",1590),m(1591,1,{},Oq),s.Kb=function(n){return u(n,49),K$(),Pn(),!0},s.Fb=function(n){return this===n},v(Wn,"NodePromotion/lambda$0$Type",1591),m(1592,1,{},kje),s.Kb=function(n){return M4n(this.a,u(n,49))},s.Fb=function(n){return this===n},s.a=0,v(Wn,"NodePromotion/lambda$1$Type",1592),m(1593,1,{},jje),s.Kb=function(n){return A4n(this.a,u(n,49))},s.Fb=function(n){return this===n},s.a=0,v(Wn,"NodePromotion/lambda$2$Type",1593),m(1594,1,Ai,Nq),s.If=function(n,t){vRn(u(n,37),t)},v(Wn,"NorthSouthPortPostprocessor",1594),m(1595,1,Ai,Dq),s.If=function(n,t){xRn(u(n,37),t)},v(Wn,"NorthSouthPortPreprocessor",1595),m(1596,1,Yt,Iq),s.Le=function(n,t){return z7n(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"NorthSouthPortPreprocessor/lambda$0$Type",1596),m(1597,1,Ai,FA),s.If=function(n,t){w_n(u(n,37),t)},v(Wn,"PartitionMidprocessor",1597),m(1598,1,Ft,w6),s.Mb=function(n){return bi(u(n,9),(Ne(),mm))},v(Wn,"PartitionMidprocessor/lambda$0$Type",1598),m(1599,1,it,Eje),s.Ad=function(n){O5n(this.a,u(n,9))},v(Wn,"PartitionMidprocessor/lambda$1$Type",1599),m(1600,1,Ai,_q),s.If=function(n,t){vNn(u(n,37),t)},v(Wn,"PartitionPostprocessor",1600),m(1601,1,Ai,OI),s.If=function(n,t){jIn(u(n,37),t)},v(Wn,"PartitionPreprocessor",1601),m(1602,1,Ft,NI),s.Mb=function(n){return bi(u(n,9),(Ne(),mm))},v(Wn,"PartitionPreprocessor/lambda$0$Type",1602),m(1603,1,Ft,DI),s.Mb=function(n){return bi(u(n,9),(Ne(),mm))},v(Wn,"PartitionPreprocessor/lambda$1$Type",1603),m(1604,1,{},II),s.Kb=function(n){return new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(Wn,"PartitionPreprocessor/lambda$2$Type",1604),m(1605,1,Ft,Sje),s.Mb=function(n){return lgn(this.a,u(n,17))},v(Wn,"PartitionPreprocessor/lambda$3$Type",1605),m(1606,1,it,_I),s.Ad=function(n){Z7n(u(n,17))},v(Wn,"PartitionPreprocessor/lambda$4$Type",1606),m(1607,1,Ft,xje),s.Mb=function(n){return Uvn(this.a,u(n,9))},s.a=0,v(Wn,"PartitionPreprocessor/lambda$5$Type",1607),m(1608,1,Ai,tP),s.If=function(n,t){YIn(u(n,37),t)};var fve,win,pin,min,ave,hve;v(Wn,"PortListSorter",1608),m(1609,1,{},k5),s.Kb=function(n){return n8(),u(n,12).e},v(Wn,"PortListSorter/lambda$0$Type",1609),m(1610,1,{},Lq),s.Kb=function(n){return n8(),u(n,12).g},v(Wn,"PortListSorter/lambda$1$Type",1610),m(1611,1,Yt,Pq),s.Le=function(n,t){return aPe(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"PortListSorter/lambda$2$Type",1611),m(1612,1,Yt,$q),s.Le=function(n,t){return hxn(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"PortListSorter/lambda$3$Type",1612),m(1613,1,Yt,LI),s.Le=function(n,t){return lKe(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"PortListSorter/lambda$4$Type",1613),m(1614,1,Ai,PI),s.If=function(n,t){rOn(u(n,37),t)},v(Wn,"PortSideProcessor",1614),m(1615,1,Ai,p6),s.If=function(n,t){sIn(u(n,37),t)},v(Wn,"ReversedEdgeRestorer",1615),m(1620,1,Ai,hxe),s.If=function(n,t){VSn(this,u(n,37),t)},v(Wn,"SelfLoopPortRestorer",1620),m(1621,1,{},m6),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"SelfLoopPortRestorer/lambda$0$Type",1621),m(1622,1,Ft,Rq),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"SelfLoopPortRestorer/lambda$1$Type",1622),m(1623,1,Ft,bk),s.Mb=function(n){return bi(u(n,9),(me(),dp))},v(Wn,"SelfLoopPortRestorer/lambda$2$Type",1623),m(1624,1,{},$I),s.Kb=function(n){return u(T(u(n,9),(me(),dp)),338)},v(Wn,"SelfLoopPortRestorer/lambda$3$Type",1624),m(1625,1,it,Aje),s.Ad=function(n){rTn(this.a,u(n,338))},v(Wn,"SelfLoopPortRestorer/lambda$4$Type",1625),m(792,1,it,JA),s.Ad=function(n){gTn(u(n,107))},v(Wn,"SelfLoopPortRestorer/lambda$5$Type",792),m(1627,1,Ai,HA),s.If=function(n,t){oSn(u(n,37),t)},v(Wn,"SelfLoopPostProcessor",1627),m(1628,1,{},GA),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"SelfLoopPostProcessor/lambda$0$Type",1628),m(1629,1,Ft,RI),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"SelfLoopPostProcessor/lambda$1$Type",1629),m(1630,1,Ft,BI),s.Mb=function(n){return bi(u(n,9),(me(),dp))},v(Wn,"SelfLoopPostProcessor/lambda$2$Type",1630),m(1631,1,it,zI),s.Ad=function(n){aAn(u(n,9))},v(Wn,"SelfLoopPostProcessor/lambda$3$Type",1631),m(1632,1,{},Bq),s.Kb=function(n){return new wn(null,new pn(u(n,107).f,1))},v(Wn,"SelfLoopPostProcessor/lambda$4$Type",1632),m(1633,1,it,Mje),s.Ad=function(n){Kyn(this.a,u(n,341))},v(Wn,"SelfLoopPostProcessor/lambda$5$Type",1633),m(1634,1,Ft,zq),s.Mb=function(n){return!!u(n,107).i},v(Wn,"SelfLoopPostProcessor/lambda$6$Type",1634),m(1635,1,it,Tje),s.Ad=function(n){Abn(this.a,u(n,107))},v(Wn,"SelfLoopPostProcessor/lambda$7$Type",1635),m(1616,1,Ai,Fq),s.If=function(n,t){$On(u(n,37),t)},v(Wn,"SelfLoopPreProcessor",1616),m(1617,1,{},Jq),s.Kb=function(n){return new wn(null,new pn(u(n,107).f,1))},v(Wn,"SelfLoopPreProcessor/lambda$0$Type",1617),m(1618,1,{},Hq),s.Kb=function(n){return u(n,341).a},v(Wn,"SelfLoopPreProcessor/lambda$1$Type",1618),m(1619,1,it,g1),s.Ad=function(n){Twn(u(n,17))},v(Wn,"SelfLoopPreProcessor/lambda$2$Type",1619),m(1636,1,Ai,uNe),s.If=function(n,t){FMn(this,u(n,37),t)},v(Wn,"SelfLoopRouter",1636),m(1637,1,{},v6),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"SelfLoopRouter/lambda$0$Type",1637),m(1638,1,Ft,FI),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"SelfLoopRouter/lambda$1$Type",1638),m(1639,1,Ft,JI),s.Mb=function(n){return bi(u(n,9),(me(),dp))},v(Wn,"SelfLoopRouter/lambda$2$Type",1639),m(1640,1,{},HI),s.Kb=function(n){return u(T(u(n,9),(me(),dp)),338)},v(Wn,"SelfLoopRouter/lambda$3$Type",1640),m(1641,1,it,XMe),s.Ad=function(n){S5n(this.a,this.b,u(n,338))},v(Wn,"SelfLoopRouter/lambda$4$Type",1641),m(1642,1,Ai,GI),s.If=function(n,t){nDn(u(n,37),t)},v(Wn,"SemiInteractiveCrossMinProcessor",1642),m(1643,1,Ft,qA),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"SemiInteractiveCrossMinProcessor/lambda$0$Type",1643),m(1644,1,Ft,Gq),s.Mb=function(n){return jDe(u(n,9))._b((Ne(),km))},v(Wn,"SemiInteractiveCrossMinProcessor/lambda$1$Type",1644),m(1645,1,Yt,j5),s.Le=function(n,t){return Z8n(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"SemiInteractiveCrossMinProcessor/lambda$2$Type",1645),m(1646,1,{},UA),s.Te=function(n,t){return C5n(u(n,9),u(t,9))},v(Wn,"SemiInteractiveCrossMinProcessor/lambda$3$Type",1646),m(1648,1,Ai,y6),s.If=function(n,t){LPn(u(n,37),t)},v(Wn,"SortByInputModelProcessor",1648),m(1649,1,Ft,XA),s.Mb=function(n){return u(n,12).g.c.length!=0},v(Wn,"SortByInputModelProcessor/lambda$0$Type",1649),m(1650,1,it,Cje),s.Ad=function(n){yTn(this.a,u(n,12))},v(Wn,"SortByInputModelProcessor/lambda$1$Type",1650),m(1729,804,{},LBe),s.bf=function(n){var t,i,r,c;switch(this.c=n,this.a.g){case 2:t=new Ce,Zi(si(new wn(null,new pn(this.c.a.b,16)),new QI),new WMe(this,t)),GO(this,new c3),Ao(t,new E5),t.c.length=0,Zi(si(new wn(null,new pn(this.c.a.b,16)),new KA),new Nje(t)),GO(this,new UI),Ao(t,new u3),t.c.length=0,i=_Ce(HY(k2(new wn(null,new pn(this.c.a.b,16)),new Dje(this))),new XI),Zi(new wn(null,new pn(this.c.a.a,16)),new VMe(i,t)),GO(this,new VI),Ao(t,new qq),t.c.length=0;break;case 3:r=new Ce,GO(this,new qI),c=_Ce(HY(k2(new wn(null,new pn(this.c.a.b,16)),new Oje(this))),new KI),Zi(si(new wn(null,new pn(this.c.a.b,16)),new Uq),new QMe(c,r)),GO(this,new Xq),Ao(r,new YI),r.c.length=0;break;default:throw $(new exe)}},s.b=0,v(lr,"EdgeAwareScanlineConstraintCalculation",1729),m(1730,1,jh,qI),s.Lb=function(n){return X(u(n,60).g,156)},s.Fb=function(n){return this===n},s.Mb=function(n){return X(u(n,60).g,156)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$0$Type",1730),m(1731,1,{},Oje),s.We=function(n){return XTn(this.a,u(n,60))},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$1$Type",1731),m(1739,1,nF,KMe),s.be=function(){UE(this.a,this.b,-1)},s.b=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$10$Type",1739),m(1741,1,jh,c3),s.Lb=function(n){return X(u(n,60).g,156)},s.Fb=function(n){return this===n},s.Mb=function(n){return X(u(n,60).g,156)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$11$Type",1741),m(1742,1,it,E5),s.Ad=function(n){u(n,375).be()},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$12$Type",1742),m(1743,1,Ft,KA),s.Mb=function(n){return X(u(n,60).g,9)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$13$Type",1743),m(1745,1,it,Nje),s.Ad=function(n){Pjn(this.a,u(n,60))},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$14$Type",1745),m(1744,1,nF,nTe),s.be=function(){UE(this.b,this.a,-1)},s.a=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$15$Type",1744),m(1746,1,jh,UI),s.Lb=function(n){return X(u(n,60).g,9)},s.Fb=function(n){return this===n},s.Mb=function(n){return X(u(n,60).g,9)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$16$Type",1746),m(1747,1,it,u3),s.Ad=function(n){u(n,375).be()},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$17$Type",1747),m(1748,1,{},Dje),s.We=function(n){return KTn(this.a,u(n,60))},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$18$Type",1748),m(1749,1,{},XI),s.Ue=function(){return 0},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$19$Type",1749),m(1732,1,{},KI),s.Ue=function(){return 0},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$2$Type",1732),m(1751,1,it,VMe),s.Ad=function(n){hvn(this.a,this.b,u(n,320))},s.a=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$20$Type",1751),m(1750,1,nF,YMe),s.be=function(){aUe(this.a,this.b,-1)},s.b=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$21$Type",1750),m(1752,1,jh,VI),s.Lb=function(n){return u(n,60),!0},s.Fb=function(n){return this===n},s.Mb=function(n){return u(n,60),!0},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$22$Type",1752),m(1753,1,it,qq),s.Ad=function(n){u(n,375).be()},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$23$Type",1753),m(1733,1,Ft,Uq),s.Mb=function(n){return X(u(n,60).g,9)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$3$Type",1733),m(1735,1,it,QMe),s.Ad=function(n){dvn(this.a,this.b,u(n,60))},s.a=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$4$Type",1735),m(1734,1,nF,tTe),s.be=function(){UE(this.b,this.a,-1)},s.a=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$5$Type",1734),m(1736,1,jh,Xq),s.Lb=function(n){return u(n,60),!0},s.Fb=function(n){return this===n},s.Mb=function(n){return u(n,60),!0},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$6$Type",1736),m(1737,1,it,YI),s.Ad=function(n){u(n,375).be()},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$7$Type",1737),m(1738,1,Ft,QI),s.Mb=function(n){return X(u(n,60).g,156)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$8$Type",1738),m(1740,1,it,WMe),s.Ad=function(n){j8n(this.a,this.b,u(n,60))},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$9$Type",1740),m(1547,1,Ai,vOe),s.If=function(n,t){YLn(this,u(n,37),t)};var vin;v(lr,"HorizontalGraphCompactor",1547),m(1548,1,{},Ije),s.df=function(n,t){var i,r,c;return yhe(n,t)||(i=J3(n),r=J3(t),i&&i.k==(zn(),wr)||r&&r.k==(zn(),wr))?0:(c=u(T(this.a.a,(me(),Lv)),316),lpn(c,i?i.k:(zn(),dr),r?r.k:(zn(),dr)))},s.ef=function(n,t){var i,r,c;return yhe(n,t)?1:(i=J3(n),r=J3(t),c=u(T(this.a.a,(me(),Lv)),316),ale(c,i?i.k:(zn(),dr),r?r.k:(zn(),dr)))},v(lr,"HorizontalGraphCompactor/1",1548),m(1549,1,{},VA),s.cf=function(n,t){return Mj(),n.a.i==0},v(lr,"HorizontalGraphCompactor/lambda$0$Type",1549),m(1550,1,{},_je),s.cf=function(n,t){return D5n(this.a,n,t)},v(lr,"HorizontalGraphCompactor/lambda$1$Type",1550),m(1696,1,{},dRe);var yin,kin;v(lr,"LGraphToCGraphTransformer",1696),m(1704,1,Ft,l0),s.Mb=function(n){return n!=null},v(lr,"LGraphToCGraphTransformer/0methodref$nonNull$Type",1704),m(1697,1,{},gk),s.Kb=function(n){return rl(),su(T(u(u(n,60).g,9),(me(),wi)))},v(lr,"LGraphToCGraphTransformer/lambda$0$Type",1697),m(1698,1,{},od),s.Kb=function(n){return rl(),MFe(u(u(n,60).g,156))},v(lr,"LGraphToCGraphTransformer/lambda$1$Type",1698),m(1707,1,Ft,S5),s.Mb=function(n){return rl(),X(u(n,60).g,9)},v(lr,"LGraphToCGraphTransformer/lambda$10$Type",1707),m(1708,1,it,YA),s.Ad=function(n){E5n(u(n,60))},v(lr,"LGraphToCGraphTransformer/lambda$11$Type",1708),m(1709,1,Ft,wk),s.Mb=function(n){return rl(),X(u(n,60).g,156)},v(lr,"LGraphToCGraphTransformer/lambda$12$Type",1709),m(1713,1,it,pk),s.Ad=function(n){njn(u(n,60))},v(lr,"LGraphToCGraphTransformer/lambda$13$Type",1713),m(1710,1,it,Lje),s.Ad=function(n){rwn(this.a,u(n,8))},s.a=0,v(lr,"LGraphToCGraphTransformer/lambda$14$Type",1710),m(1711,1,it,Pje),s.Ad=function(n){uwn(this.a,u(n,119))},s.a=0,v(lr,"LGraphToCGraphTransformer/lambda$15$Type",1711),m(1712,1,it,$je),s.Ad=function(n){cwn(this.a,u(n,8))},s.a=0,v(lr,"LGraphToCGraphTransformer/lambda$16$Type",1712),m(1714,1,{},QA),s.Kb=function(n){return rl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(lr,"LGraphToCGraphTransformer/lambda$17$Type",1714),m(1715,1,Ft,o3),s.Mb=function(n){return rl(),cc(u(n,17))},v(lr,"LGraphToCGraphTransformer/lambda$18$Type",1715),m(1716,1,it,Rje),s.Ad=function(n){W9n(this.a,u(n,17))},v(lr,"LGraphToCGraphTransformer/lambda$19$Type",1716),m(1700,1,it,Bje),s.Ad=function(n){Myn(this.a,u(n,156))},v(lr,"LGraphToCGraphTransformer/lambda$2$Type",1700),m(1717,1,{},WI),s.Kb=function(n){return rl(),new wn(null,new pn(u(n,25).a,16))},v(lr,"LGraphToCGraphTransformer/lambda$20$Type",1717),m(1718,1,{},mk),s.Kb=function(n){return rl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(lr,"LGraphToCGraphTransformer/lambda$21$Type",1718),m(1719,1,{},x5),s.Kb=function(n){return rl(),u(T(u(n,17),(me(),vg)),16)},v(lr,"LGraphToCGraphTransformer/lambda$22$Type",1719),m(1720,1,Ft,Kq),s.Mb=function(n){return fpn(u(n,16))},v(lr,"LGraphToCGraphTransformer/lambda$23$Type",1720),m(1721,1,it,zje),s.Ad=function(n){VTn(this.a,u(n,16))},v(lr,"LGraphToCGraphTransformer/lambda$24$Type",1721),m(1722,1,{},w1),s.Kb=function(n){return rl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(lr,"LGraphToCGraphTransformer/lambda$25$Type",1722),m(1723,1,Ft,WA),s.Mb=function(n){return rl(),cc(u(n,17))},v(lr,"LGraphToCGraphTransformer/lambda$26$Type",1723),m(1725,1,it,Fje),s.Ad=function(n){q8n(this.a,u(n,17))},v(lr,"LGraphToCGraphTransformer/lambda$27$Type",1725),m(1724,1,it,Jje),s.Ad=function(n){Qbn(this.a,u(n,70))},s.a=0,v(lr,"LGraphToCGraphTransformer/lambda$28$Type",1724),m(1699,1,it,ZMe),s.Ad=function(n){M6n(this.a,this.b,u(n,156))},v(lr,"LGraphToCGraphTransformer/lambda$3$Type",1699),m(1701,1,{},Wg),s.Kb=function(n){return rl(),new wn(null,new pn(u(n,25).a,16))},v(lr,"LGraphToCGraphTransformer/lambda$4$Type",1701),m(1702,1,{},ZI),s.Kb=function(n){return rl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(lr,"LGraphToCGraphTransformer/lambda$5$Type",1702),m(1703,1,{},vk),s.Kb=function(n){return rl(),u(T(u(n,17),(me(),vg)),16)},v(lr,"LGraphToCGraphTransformer/lambda$6$Type",1703),m(1705,1,it,Hje),s.Ad=function(n){uCn(this.a,u(n,16))},v(lr,"LGraphToCGraphTransformer/lambda$8$Type",1705),m(1706,1,it,eTe),s.Ad=function(n){Cwn(this.a,this.b,u(n,156))},v(lr,"LGraphToCGraphTransformer/lambda$9$Type",1706),m(1695,1,{},s3),s.af=function(n){var t,i,r,c,o;for(this.a=n,this.d=new gX,this.c=oe(Yme,On,124,this.a.a.a.c.length,0,1),this.b=0,i=new L(this.a.a.a);i.a=I&&(Te(o,ve(p)),V=k.Math.max(V,te[p-1]-y),f+=O,R+=te[p-1]-R,y=te[p-1],O=h[p]),O=k.Math.max(O,h[p]),++p;f+=O}A=k.Math.min(1/V,1/t.b/f),A>r&&(r=A,i=o)}return i},s.ng=function(){return!1},v(Sh,"MSDCutIndexHeuristic",803),m(1647,1,Ai,Vq),s.If=function(n,t){eLn(u(n,37),t)},v(Sh,"SingleEdgeGraphWrapper",1647),m(231,23,{3:1,35:1,23:1,231:1},_j);var Tv,m7,v7,hm,tx,Cv,y7=yt(Cu,"CenterEdgeLabelPlacementStrategy",231,Ct,S9n,J2n),Iin;m(422,23,{3:1,35:1,23:1,422:1},dse);var bve,Xte,gve=yt(Cu,"ConstraintCalculationStrategy",422,Ct,K5n,H2n),_in;m(301,23,{3:1,35:1,23:1,301:1,188:1,196:1},h$),s.bg=function(){return yUe(this)},s.og=function(){return yUe(this)};var eD,ix,wve,pve,mve=yt(Cu,"CrossingMinimizationStrategy",301,Ct,n6n,G2n),Lin;m(350,23,{3:1,35:1,23:1,350:1},KX);var vve,Kte,UJ,yve=yt(Cu,"CuttingStrategy",350,Ct,R4n,q2n),Pin;m(267,23,{3:1,35:1,23:1,267:1,188:1,196:1},A3),s.bg=function(){return SXe(this)},s.og=function(){return SXe(this)};var Vte,kve,Yte,Qte,Wte,Zte,eie,nie,nD,jve=yt(Cu,"CycleBreakingStrategy",267,Ct,R8n,U2n),$in;m(419,23,{3:1,35:1,23:1,419:1},bse);var XJ,Eve,Sve=yt(Cu,"DirectionCongruency",419,Ct,V5n,X2n),Rin;m(449,23,{3:1,35:1,23:1,449:1},YX);var k7,tie,Ov,Bin=yt(Cu,"EdgeConstraint",449,Ct,B4n,K2n),zin;m(284,23,{3:1,35:1,23:1,284:1},$j);var iie,rie,cie,uie,KJ,oie,xve=yt(Cu,"EdgeLabelSideSelection",284,Ct,x9n,V2n),Fin;m(476,23,{3:1,35:1,23:1,476:1},gse);var VJ,Ave,Mve=yt(Cu,"EdgeStraighteningStrategy",476,Ct,Y5n,Y2n),Jin;m(282,23,{3:1,35:1,23:1,282:1},Lj);var sie,Tve,Cve,YJ,Ove,Nve,Dve=yt(Cu,"FixedAlignment",282,Ct,A9n,Q2n),Hin;m(283,23,{3:1,35:1,23:1,283:1},Pj);var Ive,_ve,Lve,Pve,rx,$ve,Rve=yt(Cu,"GraphCompactionStrategy",283,Ct,M9n,W2n),Gin;m(261,23,{3:1,35:1,23:1,261:1},c2);var j7,QJ,E7,Kl,cx,WJ,S7,Nv,ZJ,ux,lie=yt(Cu,"GraphProperties",261,Ct,a7n,Z2n),qin;m(302,23,{3:1,35:1,23:1,302:1},QX);var tD,fie,aie,hie=yt(Cu,"GreedySwitchType",302,Ct,z4n,emn),Uin;m(329,23,{3:1,35:1,23:1,329:1},WX);var dm,Bve,iD,die=yt(Cu,"GroupOrderStrategy",329,Ct,F4n,nmn),Xin;m(315,23,{3:1,35:1,23:1,315:1},ZX);var Sy,rD,Dv,Kin=yt(Cu,"InLayerConstraint",315,Ct,J4n,tmn),Vin;m(420,23,{3:1,35:1,23:1,420:1},wse);var bie,zve,Fve=yt(Cu,"InteractiveReferencePoint",420,Ct,Q5n,imn),Yin,Jve,xy,fp,cD,eH,Hve,Gve,nH,qve,Ay,tH,ox,My,K1,gie,iH,Du,Uve,rb,po,wie,pie,uD,mg,ap,Ty,Xve,Qin,Cy,oD,bm,ja,gf,mie,Iv,cb,Ci,wi,Kve,Vve,Yve,Qve,Wve,vie,rH,vs,hp,yie,Oy,sx,Hd,_v,dp,Lv,Pv,x7,vg,Zve,kie,jie,lx,Ny,cH,Dy,$v;m(165,23,{3:1,35:1,23:1,165:1},sC);var fx,V1,ax,yg,sD,e5e=yt(Cu,"LayerConstraint",165,Ct,Y6n,rmn),Win;m(423,23,{3:1,35:1,23:1,423:1},pse);var Eie,Sie,n5e=yt(Cu,"LayerUnzippingStrategy",423,Ct,W5n,cmn),Zin;m(843,1,qa,ET),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,gwe),""),"Direction Congruency"),"Specifies how drawings of the same graph with different layout directions compare to each other: either a natural reading direction is preserved or the drawings are rotated versions of each other."),b5e),(cg(),Ri)),Sve),nn((ph(),Mn))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,wwe),""),"Feedback Edges"),"Whether feedback edges should be highlighted by routing around the nodes."),(Pn(),!1)),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,bF),""),"Interactive Reference Point"),"Determines which point of a node is considered by interactive layout phases."),y5e),Ri),Fve),nn(Mn)))),Gi(n,bF,ON,icn),Gi(n,bF,MS,tcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,pwe),""),"Merge Edges"),"Edges that have no ports are merged so they touch the connected nodes at the same points. When this option is disabled, one port is created for each edge directly connected to a node. When it is enabled, all such incoming edges share an input port, and all outgoing edges share an output port."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,mwe),""),"Merge Hierarchy-Crossing Edges"),"If hierarchical layout is active, hierarchy-crossing edges use as few hierarchical ports as possible. They are broken by the algorithm, with hierarchical ports inserted as required. Usually, one such port is created for each edge at each hierarchy crossing point. With this option set to true, we try to create as few hierarchical ports as possible in the process. In particular, all edges that form a hyperedge can share a port."),!0),Sr),Yi),nn(Mn)))),We(n,new Ge(Zbn(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vwe),""),"Allow Non-Flow Ports To Switch Sides"),"Specifies whether non-flow ports may switch sides if their node's port constraints are either FIXED_SIDE or FIXED_ORDER. A non-flow port is a port on a side that is not part of the currently configured layout flow. For instance, given a left-to-right layout direction, north and south ports would be considered non-flow ports. Further note that the underlying criterium whether to switch sides or not solely relies on the minimization of edge crossings. Hence, edge length and other aesthetics criteria are not addressed."),!1),Sr),Yi),nn(Kd)),z(B(ze,1),Se,2,6,["org.eclipse.elk.layered.northOrSouthPort"])))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ywe),""),"Port Sorting Strategy"),"Only relevant for nodes with FIXED_SIDE port constraints. Determines the way a node's ports are distributed on the sides of a node if their order is not prescribed. The option is set on parent nodes."),N5e),Ri),J4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,kwe),""),"Thoroughness"),"How much effort should be spent to produce a nice layout."),ve(7)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,jwe),""),"Add Unnecessary Bendpoints"),"Adds bend points even if an edge does not change direction. If true, each long edge dummy will contribute a bend point to its edges and hierarchy-crossing edges will always get a bend point where they cross hierarchy boundaries. By default, bend points are only added where an edge changes direction."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ewe),""),"Generate Position and Layer IDs"),"If enabled position id and layer id are generated, which are usually only used internally when setting the interactiveLayout option. This option should be specified on the root node."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ON),"cycleBreaking"),"Cycle Breaking Strategy"),"Strategy for cycle breaking. Cycle breaking looks for cycles in the graph and determines which edges to reverse to break the cycles. Reversed edges will end up pointing to the opposite direction of regular edges (that is, reversed edges will point left if edges usually point right)."),d5e),Ri),jve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,NN),$ee),"Node Layering Strategy"),"Strategy for node layering."),E5e),Ri),O4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Swe),$ee),"Layer Constraint"),"Determines a constraint on the placement of the node regarding the layering."),k5e),Ri),e5e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xwe),$ee),"Layer Choice Constraint"),"Allows to set a constraint regarding the layer placement of a node. Let i be the value of teh constraint. Assumed the drawing has n layers and i < n. If set to i, it expresses that the node should be placed in i-th layer. Should i>=n be true then the node is placed in the last layer of the drawing. Note that this option is not part of any of ELK Layered's default configurations but is only evaluated as part of the `InteractiveLayeredGraphVisitor`, which must be applied manually or used via the `DiagramLayoutEngine."),null),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Awe),$ee),"Layer ID"),"Layer identifier that was calculated by ELK Layered for a node. This is only generated if interactiveLayot or generatePositionAndLayerIds is set."),ve(-1)),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,cee),CQe),"Upper Bound On Width [MinWidth Layerer]"),"Defines a loose upper bound on the width of the MinWidth layerer. If set to '-1' multiple values are tested and the best result is selected."),ve(4)),hc),jr),nn(Mn)))),Gi(n,cee,NN,fcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,uee),CQe),"Upper Layer Estimation Scaling Factor [MinWidth Layerer]"),"Multiplied with Upper Bound On Width for defining an upper bound on the width of layers which haven't been determined yet, but whose maximum width had been (roughly) estimated by the MinWidth algorithm. Compensates for too high estimations. If set to '-1' multiple values are tested and the best result is selected."),ve(2)),hc),jr),nn(Mn)))),Gi(n,uee,NN,hcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,oee),OQe),"Node Promotion Strategy"),"Reduces number of dummy nodes after layering phase (if possible)."),j5e),Ri),B4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,see),OQe),"Max Node Promotion Iterations"),"Limits the number of iterations for node promotion."),ve(0)),hc),jr),nn(Mn)))),Gi(n,see,oee,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,lee),"layering.coffmanGraham"),"Layer Bound"),"The maximum number of nodes allowed per layer."),ve(oi)),hc),jr),nn(Mn)))),Gi(n,lee,NN,ccn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,MS),V8),"Crossing Minimization Strategy"),"Strategy for crossing minimization."),h5e),Ri),mve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Mwe),V8),"Force Node Model Order"),"The node order given by the model does not change to produce a better layout. E.g. if node A is before node B in the model this is not changed during crossing minimization. This assumes that the node model order is already respected before crossing minimization. This can be achieved by setting considerModelOrder.strategy to NODES_AND_EDGES."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,fee),V8),"Hierarchical Sweepiness"),"How likely it is to use cross-hierarchy (1) vs bottom-up (-1)."),.1),ec),gr),nn(Mn)))),Gi(n,fee,MF,Crn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,aee),V8),"Semi-Interactive Crossing Minimization"),"Preserves the order of nodes within a layer but still minimizes crossings between edges connecting long edge dummies. Derives the desired order from positions specified by the 'org.eclipse.elk.position' layout option. Requires a crossing minimization strategy that is able to process 'in-layer' constraints."),!1),Sr),Yi),nn(Mn)))),Gi(n,aee,MS,Lrn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Twe),V8),"In Layer Predecessor of"),"Allows to set a constraint which specifies of which node the current node is the predecessor. If set to 's' then the node is the predecessor of 's' and is in the same layer"),null),By),ze),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Cwe),V8),"In Layer Successor of"),"Allows to set a constraint which specifies of which node the current node is the successor. If set to 's' then the node is the successor of 's' and is in the same layer"),null),By),ze),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Owe),V8),"Position Choice Constraint"),"Allows to set a constraint regarding the position placement of a node in a layer. Assumed the layer in which the node placed includes n other nodes and i < n. If set to i, it expresses that the node should be placed at the i-th position. Should i>=n be true then the node is placed at the last position in the layer. Note that this option is not part of any of ELK Layered's default configurations but is only evaluated as part of the `InteractiveLayeredGraphVisitor`, which must be applied manually or used via the `DiagramLayoutEngine."),null),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Nwe),V8),"Position ID"),"Position within a layer that was determined by ELK Layered for a node. This is only generated if interactiveLayot or generatePositionAndLayerIds is set."),ve(-1)),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Dwe),NQe),"Greedy Switch Activation Threshold"),"By default it is decided automatically if the greedy switch is activated or not. The decision is based on whether the size of the input graph (without dummy nodes) is smaller than the value of this option. A '0' enforces the activation."),ve(40)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hee),NQe),"Greedy Switch Crossing Minimization"),"Greedy Switch strategy for crossing minimization. The greedy switch heuristic is executed after the regular crossing minimization as a post-processor. Note that if 'hierarchyHandling' is set to 'INCLUDE_CHILDREN', the 'greedySwitchHierarchical.type' option must be used."),a5e),Ri),hie),nn(Mn)))),Gi(n,hee,MS,Mrn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,gF),"crossingMinimization.greedySwitchHierarchical"),"Greedy Switch Crossing Minimization (hierarchical)"),"Activates the greedy switch heuristic in case hierarchical layout is used. The differences to the non-hierarchical case (see 'greedySwitch.type') are: 1) greedy switch is inactive by default, 3) only the option value set on the node at which hierarchical layout starts is relevant, and 2) if it's activated by the user, it properly addresses hierarchy-crossing edges."),f5e),Ri),hie),nn(Mn)))),Gi(n,gF,MS,Srn),Gi(n,gF,MF,xrn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,pv),DQe),"Node Placement Strategy"),"Strategy for node placement."),O5e),Ri),_4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,wF),DQe),"Favor Straight Edges Over Balancing"),"Favor straight edges over a balanced node placement. The default behavior is determined automatically based on the used 'edgeRouting'. For an orthogonal style it is set to true, for all other styles to false."),Sr),Yi),nn(Mn)))),Gi(n,wF,pv,Ccn),Gi(n,wF,pv,Ocn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,dee),IQe),"BK Edge Straightening"),"Specifies whether the Brandes Koepf node placer tries to increase the number of straight edges at the expense of diagram size. There is a subtle difference to the 'favorStraightEdges' option, which decides whether a balanced placement of the nodes is desired, or not. In bk terms this means combining the four alignments into a single balanced one, or not. This option on the other hand tries to straighten additional edges during the creation of each of the four alignments."),M5e),Ri),Mve),nn(Mn)))),Gi(n,dee,pv,xcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,bee),IQe),"BK Fixed Alignment"),"Tells the BK node placer to use a certain alignment (out of its four) instead of the one producing the smallest height, or the combination of all four."),T5e),Ri),Dve),nn(Mn)))),Gi(n,bee,pv,Mcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,gee),"nodePlacement.linearSegments"),"Linear Segments Deflection Dampening"),"Dampens the movement of nodes to keep the diagram from getting too large."),.3),ec),gr),nn(Mn)))),Gi(n,gee,pv,Dcn),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,wee),"nodePlacement.networkSimplex"),"Node Flexibility"),"Aims at shorter and straighter edges. Two configurations are possible: (a) allow ports to move freely on the side they are assigned to (the order is always defined beforehand), (b) additionally allow to enlarge a node wherever it helps. If this option is not configured for a node, the 'nodeFlexibility.default' value is used, which is specified for the node's parent."),Ri),Qie),nn(fr)))),Gi(n,wee,pv,Pcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,pee),"nodePlacement.networkSimplex.nodeFlexibility"),"Node Flexibility Default"),"Default value of the 'nodeFlexibility' option for the children of a hierarchical node."),C5e),Ri),Qie),nn(Mn)))),Gi(n,pee,pv,Lcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Iwe),_Qe),"Self-Loop Distribution"),"Alter the distribution of the loops around the node. It only takes effect for PortConstraints.FREE."),p5e),Ri),q4e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,_we),_Qe),"Self-Loop Ordering"),"Alter the ordering of the loops they can either be stacked or sequenced. It only takes effect for PortConstraints.FREE."),m5e),Ri),U4e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,pF),"edgeRouting.splines"),"Spline Routing Mode"),"Specifies the way control points are assembled for each individual edge. CONSERVATIVE ensures that edges are properly routed around the nodes but feels rather orthogonal at times. SLOPPY uses fewer control points to obtain curvier edge routes but may result in edges overlapping nodes."),v5e),Ri),K4e),nn(Mn)))),Gi(n,pF,IN,Urn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,mF),"edgeRouting.splines.sloppy"),"Sloppy Spline Layer Spacing Factor"),"Spacing factor for routing area between layers when using sloppy spline routing."),.2),ec),gr),nn(Mn)))),Gi(n,mF,IN,Krn),Gi(n,mF,pF,Vrn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,mee),"edgeRouting.polyline"),"Sloped Edge Zone Width"),"Width of the strip to the left and to the right of each layer where the polyline edge router is allowed to refrain from ensuring that edges are routed horizontally. This prevents awkward bend points for nodes that extent almost to the edge of their layer."),2),ec),gr),nn(Mn)))),Gi(n,mee,IN,Jrn),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Lwe),Xa),"Spacing Base Value"),"An optional base value for all other layout options of the 'spacing' group. It can be used to conveniently alter the overall 'spaciousness' of the drawing. Whenever an explicit value is set for the other layout options, this base value will have no effect. The base value is not inherited, i.e. it must be set for each hierarchical node."),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Pwe),Xa),"Edge Node Between Layers Spacing"),"The spacing to be preserved between nodes and edges that are routed next to the node's layer. For the spacing between nodes and edges that cross the node's layer 'spacing.edgeNode' is used."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,$we),Xa),"Edge Edge Between Layer Spacing"),"Spacing to be preserved between pairs of edges that are routed between the same pair of layers. Note that 'spacing.edgeEdge' is used for the spacing between pairs of edges crossing the same layer."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Rwe),Xa),"Node Node Between Layers Spacing"),"The spacing to be preserved between any pair of nodes of two adjacent layers. Note that 'spacing.nodeNode' is used for the spacing between nodes within the layer itself."),20),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Bwe),Qwe),"Direction Priority"),"Defines how important it is to have a certain edge point into the direction of the overall layout. This option is evaluated during the cycle breaking phase."),ve(0)),hc),jr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,zwe),Qwe),"Shortness Priority"),"Defines how important it is to keep an edge as short as possible. This option is evaluated during the layering phase."),ve(0)),hc),jr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Fwe),Qwe),"Straightness Priority"),"Defines how important it is to keep an edge straight, i.e. aligned with one of the two axes. This option is evaluated during node placement."),ve(0)),hc),jr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vee),Wwe),"Connected Components Compaction"),"Tries to further compact components (disconnected sub-graphs)."),!1),Sr),Yi),nn(Mn)))),Gi(n,vee,kS,!0),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Jwe),LQe),"Post Compaction Strategy"),PQe),i5e),Ri),Rve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Hwe),LQe),"Post Compaction Constraint Calculation"),PQe),t5e),Ri),gve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vF),Zwe),"High Degree Node Treatment"),"Makes room around high degree nodes to place leafs and trees."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,yee),Zwe),"High Degree Node Threshold"),"Whether a node is considered to have a high degree."),ve(16)),hc),jr),nn(Mn)))),Gi(n,yee,vF,!0),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,kee),Zwe),"High Degree Node Maximum Tree Height"),"Maximum height of a subtree connected to a high degree node to be moved to separate layers."),ve(5)),hc),jr),nn(Mn)))),Gi(n,kee,vF,!0),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,q1),epe),"Graph Wrapping Strategy"),"For certain graphs and certain prescribed drawing areas it may be desirable to split the laid out graph into chunks that are placed side by side. The edges that connect different chunks are 'wrapped' around from the end of one chunk to the start of the other chunk. The points between the chunks are referred to as 'cuts'."),_5e),Ri),W4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,yF),epe),"Additional Wrapped Edges Spacing"),"To visually separate edges that are wrapped from regularly routed edges an additional spacing value can be specified in form of this layout option. The spacing is added to the regular edgeNode spacing."),10),ec),gr),nn(Mn)))),Gi(n,yF,q1,Vcn),Gi(n,yF,q1,Ycn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,kF),epe),"Correction Factor for Wrapping"),"At times and for certain types of graphs the executed wrapping may produce results that are consistently biased in the same fashion: either wrapping to often or to rarely. This factor can be used to correct the bias. Internally, it is simply multiplied with the 'aspect ratio' layout option."),1),ec),gr),nn(Mn)))),Gi(n,kF,q1,Wcn),Gi(n,kF,q1,Zcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,TS),$Qe),"Cutting Strategy"),"The strategy by which the layer indexes are determined at which the layering crumbles into chunks."),I5e),Ri),yve),nn(Mn)))),Gi(n,TS,q1,cun),Gi(n,TS,q1,uun),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,jee),$Qe),"Manually Specified Cuts"),"Allows the user to specify her own cuts for a certain graph."),Wa),wl),nn(Mn)))),Gi(n,jee,TS,nun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Eee),"wrapping.cutting.msd"),"MSD Freedom"),"The MSD cutting strategy starts with an initial guess on the number of chunks the graph should be split into. The freedom specifies how much the strategy may deviate from this guess. E.g. if an initial number of 3 is computed, a freedom of 1 allows 2, 3, and 4 cuts."),D5e),hc),jr),nn(Mn)))),Gi(n,Eee,TS,iun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,jF),RQe),"Validification Strategy"),"When wrapping graphs, one can specify indices that are not allowed as split points. The validification strategy makes sure every computed split point is allowed."),L5e),Ri),Q4e),nn(Mn)))),Gi(n,jF,q1,mun),Gi(n,jF,q1,vun),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,EF),RQe),"Valid Indices for Wrapping"),null),Wa),wl),nn(Mn)))),Gi(n,EF,q1,gun),Gi(n,EF,q1,wun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,SF),npe),"Improve Cuts"),"For general graphs it is important that not too many edges wrap backwards. Thus a compromise between evenly-distributed cuts and the total number of cut edges is sought."),!0),Sr),Yi),nn(Mn)))),Gi(n,SF,q1,fun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xF),npe),"Distance Penalty When Improving Cuts"),null),2),ec),gr),nn(Mn)))),Gi(n,xF,q1,sun),Gi(n,xF,SF,!0),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,See),npe),"Improve Wrapped Edges"),"The initial wrapping is performed in a very simple way. As a consequence, edges that wrap from one chunk to another may be unnecessarily long. Activating this option tries to shorten such edges."),!0),Sr),Yi),nn(Mn)))),Gi(n,See,q1,hun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xee),Ree),"Layer Unzipping Strategy"),"The strategy to use for unzipping a layer into multiple sublayers while maintaining the existing ordering of nodes and edges after crossing minimization. The default value is 'NONE'."),A5e),Ri),n5e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Aee),Ree),"Minimize Edge Length Heuristic"),"Use a heuristic to decide whether or not to actually perform the layer split with the goal of minimizing the total edge length. This option only works when layerSplit is set to 2. The property can be set to the nodes in a layer, which then applies the property for the layer. If any node sets the value to true, then the value is set to true for the entire layer."),!1),Sr),Yi),nn(fr)))),Gi(n,Aee,Mee,mcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Mee),Ree),"Unzipping Layer Split"),"Defines the number of sublayers to split a layer into. The property can be set to the nodes in a layer, which then applies the property for the layer. If multiple nodes set the value to different values, then the lowest value is chosen."),S5e),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Tee),Ree),"Reset Alternation on Long Edges"),"If set to true, nodes will always be placed in the first sublayer after a long edge when using the ALTERNATING strategy. Otherwise long edge dummies are treated the same as regular nodes. The default value is true. The property can be set to the nodes in a layer, which then applies the property for the layer. If any node sets the value to false, then the value is set to false for the entire layer."),x5e),Sr),Yi),nn(fr)))),Gi(n,Tee,xee,ycn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Gwe),Bee),"Edge Label Side Selection"),"Method to decide on edge label sides."),w5e),Ri),xve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,qwe),Bee),"Edge Center Label Placement Strategy"),"Determines in which layer center labels of long edges should be placed."),g5e),Ri),y7),Mi(Mn,z(B(Qa,1),ke,160,0,[Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,AF),CS),"Consider Model Order"),"Preserves the order of nodes and edges in the model file if this does not lead to additional edge crossings. Depending on the strategy this is not always possible since the node and edge order might be conflicting."),l5e),Ri),F4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Uwe),CS),"Consider Port Order"),"If disabled the port order of output ports is derived from the edge order and input ports are ordered by their incoming connections. If enabled all ports are ordered by the port model order."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,DN),CS),"No Model Order"),"Set on a node to not set a model order for this node even though it is a real node."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Cee),CS),"Consider Model Order for Components"),"If set to NONE the usual ordering strategy (by cumulative node priority and size of nodes) is used. INSIDE_PORT_SIDES orders the components with external ports only inside the groups with the same port side. FORCE_MODEL_ORDER enforces the mode order on components. This option might produce bad alignments and sub optimal drawings in terms of used area since the ordering should be respected."),r5e),Ri),y3e),nn(Mn)))),Gi(n,Cee,kS,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Xwe),CS),"Long Edge Ordering Strategy"),"Indicates whether long edges are sorted under, over, or equal to nodes that have no connection to a previous layer in a left-to-right or right-to-left layout. Under and over changes to right and left in a vertical layout."),s5e),Ri),D4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Oee),CS),"Crossing Counter Node Order Influence"),"Indicates with what percentage (1 for 100%) violations of the node model order are weighted against the crossings e.g. a value of 0.5 means two model order violations are as important as on edge crossing. This allows some edge crossings in favor of preserving the model order. It is advised to set this value to a very small positive value (e.g. 0.001) to have minimal crossing and a optimal node order. Defaults to no influence (0)."),0),ec),gr),nn(Mn)))),Gi(n,Oee,AF,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Nee),CS),"Crossing Counter Port Order Influence"),"Indicates with what percentage (1 for 100%) violations of the port model order are weighted against the crossings e.g. a value of 0.5 means two model order violations are as important as on edge crossing. This allows some edge crossings in favor of preserving the model order. It is advised to set this value to a very small positive value (e.g. 0.001) to have minimal crossing and a optimal port order. Defaults to no influence (0)."),0),ec),gr),nn(Mn)))),Gi(n,Nee,AF,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Dee),Y8),tpe),"Used to define partial ordering groups during cycle breaking. A lower group id means that the group is sorted before other groups. A group model order of 0 is the default group."),ve(0)),hc),jr),nn(fr)))),Gi(n,Dee,DN,!1),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Iee),Y8),tpe),"Used to define partial ordering groups during crossing minimization. A lower group id means that the group is sorted before other groups. A group model order of 0 is the default group."),ve(0)),hc),jr),Mi(fr,z(B(Qa,1),ke,160,0,[Sa,Kd]))))),Gi(n,Iee,DN,!1),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,_ee),Y8),tpe),"Used to define partial ordering groups during component packing. A lower group id means that the group is sorted before other groups. A group model order of 0 is the default group."),ve(0)),hc),jr),Mi(fr,z(B(Qa,1),ke,160,0,[Sa,Kd]))))),Gi(n,_ee,DN,!1),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Kwe),Y8),"Cycle Breaking Group Ordering Strategy"),"Determines how to count ordering violations during cycle breaking. NONE: They do not count. ENFORCED: A group with a higher model order is before a node with a smaller. MODEL_ORDER: The model order counts instead of the model order group id ordering."),c5e),Ri),die),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Lee),Y8),"Cycle Breaking Preferred Source Id"),"The model order group id for which should be preferred as a source if possible."),hc),jr),nn(Mn)))),Gi(n,Lee,ON,lrn),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Pee),Y8),"Cycle Breaking Preferred Target Id"),"The model order group id for which should be preferred as a target if possible."),hc),jr),nn(Mn)))),Gi(n,Pee,ON,arn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Vwe),Y8),"Crossing Minimization Group Ordering Strategy"),"Determines how to count ordering violations during crossing minimization. NONE: They do not count. ENFORCED: A group with a lower id is before a group with a higher id. MODEL_ORDER: The model order counts instead of the model order group id ordering."),o5e),Ri),die),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ywe),Y8),"Crossing Minimization Enforced Group Orders"),"Holds all group ids which are enforcing their order during crossing minimization strategies. E.g. if only groups 2 and -1 (default) enforce their ordering. Other groups e.g. the group of timer nodes can be ordered arbitrarily if it helps and the mentioned groups may not change their order."),u5e),Wa),wl),nn(Mn)))),rYe((new OU,n))};var ern,nrn,trn,t5e,irn,i5e,rrn,r5e,crn,urn,orn,c5e,srn,lrn,frn,arn,hrn,u5e,drn,o5e,brn,grn,wrn,prn,s5e,mrn,vrn,yrn,l5e,krn,jrn,Ern,f5e,Srn,xrn,Arn,a5e,Mrn,Trn,Crn,Orn,Nrn,Drn,Irn,_rn,Lrn,Prn,h5e,$rn,d5e,Rrn,b5e,Brn,g5e,zrn,w5e,Frn,Jrn,Hrn,p5e,Grn,m5e,qrn,v5e,Urn,Xrn,Krn,Vrn,Yrn,Qrn,Wrn,Zrn,ecn,ncn,y5e,tcn,icn,rcn,ccn,ucn,ocn,k5e,scn,lcn,fcn,acn,hcn,dcn,bcn,j5e,gcn,E5e,wcn,S5e,pcn,mcn,vcn,x5e,ycn,kcn,A5e,jcn,Ecn,Scn,M5e,xcn,Acn,T5e,Mcn,Tcn,Ccn,Ocn,Ncn,Dcn,Icn,_cn,C5e,Lcn,Pcn,$cn,O5e,Rcn,N5e,Bcn,zcn,Fcn,Jcn,Hcn,Gcn,qcn,Ucn,Xcn,Kcn,Vcn,Ycn,Qcn,Wcn,Zcn,eun,nun,tun,D5e,iun,run,I5e,cun,uun,oun,sun,lun,fun,aun,hun,dun,_5e,bun,gun,wun,pun,L5e,mun,vun;v(Cu,"LayeredMetaDataProvider",843),m(982,1,qa,OU),s.tf=function(n){rYe(n)};var Ch,xie,uH,hx,oH,P5e,sH,dx,lD,Aie,Iy,$5e,R5e,B5e,bx,yun,gx,gm,Mie,lH,Tie,u1,Cie,A7,z5e,fD,Oie,F5e,kun,jun,Eun,fH,Nie,wx,_y,Sun,pl,J5e,H5e,aH,Rv,Oh,hH,Y1,G5e,q5e,U5e,Die,Iie,X5e,Gd,_ie,K5e,wm,V5e,Y5e,Q5e,dH,pm,kg,W5e,Z5e,Wc,e4e,xun,yu,px,n4e,t4e,i4e,aD,bH,gH,Lie,Pie,r4e,wH,c4e,u4e,pH,bp,o4e,$ie,mx,s4e,gp,vx,mH,jg,Rie,M7,vH,Eg,l4e,f4e,a4e,mm,h4e,Aun,Mun,Tun,Cun,wp,vm,Wi,qd,Oun,ym,d4e,T7,b4e,km,Nun,C7,g4e,Ly,Dun,Iun,hD,Bie,w4e,dD,Vf,jm,Bv,Sg,ub,yH,Em,zie,O7,N7,xg,Sm,Fie,bD,yx,kx,_un,Lun,Pun,p4e,$un,Jie,m4e,v4e,y4e,k4e,Hie,j4e,E4e,S4e,x4e,Gie,kH;v(Cu,"LayeredOptions",982),m(983,1,{},Yq),s.uf=function(){var n;return n=new ixe,n},s.vf=function(n){},v(Cu,"LayeredOptions/LayeredFactory",983),m(1345,1,{}),s.a=0;var Run;v(Ru,"ElkSpacings/AbstractSpacingsBuilder",1345),m(778,1345,{},tde);var jH,Bun;v(Cu,"LayeredSpacings/LayeredSpacingsBuilder",778),m(268,23,{3:1,35:1,23:1,268:1,188:1,196:1},M3),s.bg=function(){return yXe(this)},s.og=function(){return yXe(this)};var qie,Uie,Xie,A4e,M4e,T4e,EH,Kie,C4e,O4e=yt(Cu,"LayeringStrategy",268,Ct,B8n,umn),zun;m(352,23,{3:1,35:1,23:1,352:1},eK);var Vie,N4e,SH,D4e=yt(Cu,"LongEdgeOrderingStrategy",352,Ct,H4n,omn),Fun;m(203,23,{3:1,35:1,23:1,203:1},d$);var zv,Fv,xH,Yie,Qie=yt(Cu,"NodeFlexibility",203,Ct,t6n,smn),Jun;m(328,23,{3:1,35:1,23:1,328:1,188:1,196:1},lC),s.bg=function(){return sUe(this)},s.og=function(){return sUe(this)};var jx,Wie,Zie,Ex,I4e,_4e=yt(Cu,"NodePlacementStrategy",328,Ct,V6n,lmn),Hun;m(243,23,{3:1,35:1,23:1,243:1},u2);var L4e,D7,Sx,gD,P4e,$4e,wD,R4e,AH,MH,B4e=yt(Cu,"NodePromotionStrategy",243,Ct,f7n,fmn),Gun;m(269,23,{3:1,35:1,23:1,269:1},b$);var z4e,ob,ere,nre,F4e=yt(Cu,"OrderingStrategy",269,Ct,i6n,amn),qun;m(421,23,{3:1,35:1,23:1,421:1},mse);var tre,ire,J4e=yt(Cu,"PortSortingStrategy",421,Ct,Z5n,hmn),Uun;m(452,23,{3:1,35:1,23:1,452:1},nK);var ys,Do,xx,Xun=yt(Cu,"PortType",452,Ct,G4n,dmn),Kun;m(381,23,{3:1,35:1,23:1,381:1},tK);var H4e,rre,G4e,q4e=yt(Cu,"SelfLoopDistributionStrategy",381,Ct,q4n,bmn),Vun;m(348,23,{3:1,35:1,23:1,348:1},iK);var cre,pD,ure,U4e=yt(Cu,"SelfLoopOrderingStrategy",348,Ct,U4n,gmn),Yun;m(316,1,{316:1},tVe),v(Cu,"Spacings",316),m(349,23,{3:1,35:1,23:1,349:1},rK);var ore,X4e,Ax,K4e=yt(Cu,"SplineRoutingMode",349,Ct,X4n,wmn),Qun;m(351,23,{3:1,35:1,23:1,351:1},cK);var sre,V4e,Y4e,Q4e=yt(Cu,"ValidifyStrategy",351,Ct,K4n,pmn),Wun;m(382,23,{3:1,35:1,23:1,382:1},uK);var xm,lre,I7,W4e=yt(Cu,"WrappingStrategy",382,Ct,V4n,mmn),Zun;m(1361,1,uc,jT),s.pg=function(n){return u(n,37),eon},s.If=function(n,t){RPn(this,u(n,37),t)};var eon;v(Zw,"BFSNodeOrderCycleBreaker",1361),m(1359,1,uc,iP),s.pg=function(n){return u(n,37),non},s.If=function(n,t){ILn(this,u(n,37),t)};var non;v(Zw,"DFSNodeOrderCycleBreaker",1359),m(1360,1,it,$Ne),s.Ad=function(n){LIn(this.a,this.c,this.b,u(n,17))},s.b=!1,v(Zw,"DFSNodeOrderCycleBreaker/lambda$0$Type",1360),m(1353,1,uc,Q6),s.pg=function(n){return u(n,37),ton},s.If=function(n,t){DLn(this,u(n,37),t)};var ton;v(Zw,"DepthFirstCycleBreaker",1353),m(779,1,uc,Afe),s.pg=function(n){return u(n,37),ion},s.If=function(n,t){ZRn(this,u(n,37),t)},s.qg=function(n){return u(Le(n,sz(this.e,n.c.length)),9)};var ion;v(Zw,"GreedyCycleBreaker",779),m(1356,779,uc,STe),s.qg=function(n){var t,i,r,c,o,l,f,h,b;for(b=null,r=oi,h=k.Math.max(this.b.a.c.length,u(T(this.b,(me(),cb)),15).a),t=h*u(T(this.b,cD),15).a,c=new S6,i=ue(T(this.b,(Ne(),Iy)))===ue((_0(),dm)),f=new L(n);f.ao&&(r=o,b=l));return b||u(Le(n,sz(this.e,n.c.length)),9)},v(Zw,"GreedyModelOrderCycleBreaker",1356),m(505,1,{},S6),s.a=0,s.b=0,v(Zw,"GroupModelOrderCalculator",505),m(1354,1,uc,nj),s.pg=function(n){return u(n,37),ron},s.If=function(n,t){cPn(this,u(n,37),t)};var ron;v(Zw,"InteractiveCycleBreaker",1354),m(1355,1,uc,ej),s.pg=function(n){return u(n,37),con},s.If=function(n,t){oPn(u(n,37),t)};var con;v(Zw,"ModelOrderCycleBreaker",1355),m(780,1,uc),s.pg=function(n){return u(n,37),uon},s.If=function(n,t){V_n(this,u(n,37),t)},s.rg=function(n,t){var i,r,c,o,l,f,h,b,p,y;for(l=0;lb&&(h=S,y=b),paa(new qn(Vn(Ni(f).a.Jc(),new ee))))for(c=new qn(Vn(rr(h).a.Jc(),new ee));ht(c);)r=u(tt(c),17),u(Yu(this.d,l),22).Gc(r.c.i)&&Te(this.c,r);else for(c=new qn(Vn(Ni(f).a.Jc(),new ee));ht(c);)r=u(tt(c),17),u(Yu(this.d,l),22).Gc(r.d.i)&&Te(this.c,r)}},v(Zw,"SCCNodeTypeCycleBreaker",1358),m(1357,780,uc,ATe),s.rg=function(n,t){var i,r,c,o,l,f,h,b,p,y,S,A;for(l=0;lb&&(h=S,y=b),paa(new qn(Vn(Ni(f).a.Jc(),new ee))))for(c=new qn(Vn(rr(h).a.Jc(),new ee));ht(c);)r=u(tt(c),17),u(Yu(this.d,l),22).Gc(r.c.i)&&Te(this.c,r);else for(c=new qn(Vn(Ni(f).a.Jc(),new ee));ht(c);)r=u(tt(c),17),u(Yu(this.d,l),22).Gc(r.d.i)&&Te(this.c,r)}},v(Zw,"SCConnectivity",1357),m(1373,1,uc,kT),s.pg=function(n){return u(n,37),oon},s.If=function(n,t){iRn(this,u(n,37),t)};var oon;v(U1,"BreadthFirstModelOrderLayerer",1373),m(1374,1,Yt,sM),s.Le=function(n,t){return JTn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"BreadthFirstModelOrderLayerer/lambda$0$Type",1374),m(1364,1,uc,CMe),s.pg=function(n){return u(n,37),son},s.If=function(n,t){rBn(this,u(n,37),t)};var son;v(U1,"CoffmanGrahamLayerer",1364),m(1365,1,Yt,Wje),s.Le=function(n,t){return VNn(this.a,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"CoffmanGrahamLayerer/0methodref$compareNodesInTopo$Type",1365),m(1366,1,Yt,Zje),s.Le=function(n,t){return fvn(this.a,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"CoffmanGrahamLayerer/lambda$1$Type",1366),m(1375,1,uc,yT),s.pg=function(n){return u(n,37),lon},s.If=function(n,t){GRn(this,u(n,37),t)},s.c=0,s.e=0;var lon;v(U1,"DepthFirstModelOrderLayerer",1375),m(1376,1,Yt,x6),s.Le=function(n,t){return HTn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"DepthFirstModelOrderLayerer/lambda$0$Type",1376),m(1367,1,uc,A6),s.pg=function(n){return u(n,37),qt(qt(qt(new or,(zr(),Kf),(Ur(),Ite)),r1,fm),eo,lm)},s.If=function(n,t){bRn(u(n,37),t)},v(U1,"InteractiveLayerer",1367),m(564,1,{564:1},axe),s.a=0,s.c=0,v(U1,"InteractiveLayerer/LayerSpan",564),m(1363,1,uc,cP),s.pg=function(n){return u(n,37),fon},s.If=function(n,t){HNn(this,u(n,37),t)};var fon;v(U1,"LongestPathLayerer",1363),m(1372,1,uc,uP),s.pg=function(n){return u(n,37),aon},s.If=function(n,t){fDn(this,u(n,37),t)};var aon;v(U1,"LongestPathSourceLayerer",1372),m(1370,1,uc,ko),s.pg=function(n){return u(n,37),qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)},s.If=function(n,t){SRn(this,u(n,37),t)},s.a=0,s.b=0,s.d=0;var Z4e,eye;v(U1,"MinWidthLayerer",1370),m(1371,1,Yt,eEe),s.Le=function(n,t){return N7n(this,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"MinWidthLayerer/MinOutgoingEdgesComparator",1371),m(1362,1,uc,rP),s.pg=function(n){return u(n,37),hon},s.If=function(n,t){FPn(this,u(n,37),t)};var hon;v(U1,"NetworkSimplexLayerer",1362),m(1368,1,uc,rNe),s.pg=function(n){return u(n,37),qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)},s.If=function(n,t){A$n(this,u(n,37),t)},s.d=0,s.f=0,s.g=0,s.i=0,s.s=0,s.t=0,s.u=0,v(U1,"StretchWidthLayerer",1368),m(1369,1,Yt,Wq),s.Le=function(n,t){return a9n(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"StretchWidthLayerer/1",1369),m(406,1,Bpe),s.eg=function(n,t,i,r,c,o){},s.tg=function(n,t,i){return YXe(this,n,t,i)},s.dg=function(){this.g=oe(Hm,JQe,30,this.d,15,1),this.f=oe(Hm,JQe,30,this.d,15,1)},s.fg=function(n,t){this.e[n]=oe($t,ni,30,t[n].length,15,1)},s.gg=function(n,t,i){var r;r=i[n][t],r.p=t,this.e[n][t]=t},s.hg=function(n,t,i,r){u(Le(r[n][t].j,i),12).p=this.d++},s.b=0,s.c=0,s.d=0,v(Ro,"AbstractBarycenterPortDistributor",406),m(1663,1,Yt,nEe),s.Le=function(n,t){return BEn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ro,"AbstractBarycenterPortDistributor/lambda$0$Type",1663),m(816,1,AS,Iae),s.eg=function(n,t,i,r,c,o){},s.gg=function(n,t,i){},s.hg=function(n,t,i,r){},s.cg=function(){return!1},s.dg=function(){this.c=this.e.a,this.g=this.f.g},s.fg=function(n,t){t[n][0].c.p=n},s.ig=function(){return!1},s.ug=function(n,t,i,r){i?FHe(this,n):(XHe(this,n,r),dVe(this,n,t)),n.c.length>1&&(Re($e(T(_r((vn(0,n.c.length),u(n.c[0],9))),(Ne(),A7))))?vUe(n,this.d,u(this,660)):(jn(),Cr(n,this.d)),lze(this.e,n))},s.jg=function(n,t,i,r){var c,o,l,f,h,b,p;for(t!=SDe(i,n.length)&&(o=n[t-(i?1:-1)],rhe(this.f,o,i?(Nc(),Do):(Nc(),ys))),c=n[t][0],p=!r||c.k==(zn(),wr),b=$f(n[t]),this.ug(b,p,!1,i),l=0,h=new L(b);h.a"),n0?HV(this.a,n[t-1],n[t]):!i&&t1&&(Re($e(T(_r((vn(0,n.c.length),u(n.c[0],9))),(Ne(),A7))))?vUe(n,this.d,this):(jn(),Cr(n,this.d)),Re($e(T(_r((vn(0,n.c.length),u(n.c[0],9))),A7)))||lze(this.e,n))},v(Ro,"ModelOrderBarycenterHeuristic",660),m(1843,1,Yt,lEe),s.Le=function(n,t){return jLn(this.a,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ro,"ModelOrderBarycenterHeuristic/lambda$0$Type",1843),m(1383,1,uc,sP),s.pg=function(n){var t;return u(n,37),t=I$(yon),qt(t,(zr(),eo),(Ur(),LJ)),t},s.If=function(n,t){R5n((u(n,37),t))};var yon;v(Ro,"NoCrossingMinimizer",1383),m(796,406,Bpe,Roe),s.sg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,A;switch(y=this.g,i.g){case 1:{for(c=0,o=0,p=new L(n.j);p.a1&&(c.j==(De(),et)?this.b[n]=!0:c.j==Kn&&n>0&&(this.b[n-1]=!0))},s.f=0,v(t1,"AllCrossingsCounter",1838),m(583,1,{},AB),s.b=0,s.d=0,v(t1,"BinaryIndexedTree",583),m(519,1,{},CC);var nye,OH;v(t1,"CrossingsCounter",519),m(1912,1,Yt,fEe),s.Le=function(n,t){return evn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(t1,"CrossingsCounter/lambda$0$Type",1912),m(1913,1,Yt,aEe),s.Le=function(n,t){return nvn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(t1,"CrossingsCounter/lambda$1$Type",1913),m(1914,1,Yt,hEe),s.Le=function(n,t){return tvn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(t1,"CrossingsCounter/lambda$2$Type",1914),m(1915,1,Yt,dEe),s.Le=function(n,t){return ivn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(t1,"CrossingsCounter/lambda$3$Type",1915),m(1916,1,it,bEe),s.Ad=function(n){K9n(this.a,u(n,12))},v(t1,"CrossingsCounter/lambda$4$Type",1916),m(1917,1,Ft,gEe),s.Mb=function(n){return Fgn(this.a,u(n,12))},v(t1,"CrossingsCounter/lambda$5$Type",1917),m(1918,1,it,wEe),s.Ad=function(n){eCe(this,n)},v(t1,"CrossingsCounter/lambda$6$Type",1918),m(1919,1,it,cTe),s.Ad=function(n){var t;A9(),C0(this.b,(t=this.a,u(n,12),t))},v(t1,"CrossingsCounter/lambda$7$Type",1919),m(823,1,jh,hM),s.Lb=function(n){return A9(),bi(u(n,12),(me(),vs))},s.Fb=function(n){return this===n},s.Mb=function(n){return A9(),bi(u(n,12),(me(),vs))},v(t1,"CrossingsCounter/lambda$8$Type",823),m(1911,1,{},pEe),v(t1,"HyperedgeCrossingsCounter",1911),m(467,1,{35:1,467:1},cNe),s.Dd=function(n){return TEn(this,u(n,467))},s.b=0,s.c=0,s.e=0,s.f=0;var BBn=v(t1,"HyperedgeCrossingsCounter/Hyperedge",467);m(370,1,{35:1,370:1},AR),s.Dd=function(n){return kOn(this,u(n,370))},s.b=0,s.c=0;var kon=v(t1,"HyperedgeCrossingsCounter/HyperedgeCorner",370);m(518,23,{3:1,35:1,23:1,518:1},vse);var Tx,Cx,jon=yt(t1,"HyperedgeCrossingsCounter/HyperedgeCorner/Type",518,Ct,e4n,jmn),Eon;m(1385,1,uc,CU),s.pg=function(n){return u(T(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?Son:null},s.If=function(n,t){Qxn(this,u(n,37),t)};var Son;v(Lc,"InteractiveNodePlacer",1385),m(1386,1,uc,AT),s.pg=function(n){return u(T(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?xon:null},s.If=function(n,t){$Sn(this,u(n,37),t)};var xon,NH,DH;v(Lc,"LinearSegmentsNodePlacer",1386),m(263,1,{35:1,263:1},poe),s.Dd=function(n){return ngn(this,u(n,263))},s.Fb=function(n){var t;return X(n,263)?(t=u(n,263),this.b==t.b):!1},s.Hb=function(){return this.b},s.Ib=function(){return"ls"+Fa(this.e)},s.a=0,s.b=0,s.c=-1,s.d=-1,s.g=0;var Aon=v(Lc,"LinearSegmentsNodePlacer/LinearSegment",263);m(1388,1,uc,_De),s.pg=function(n){return u(T(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?Mon:null},s.If=function(n,t){qRn(this,u(n,37),t)},s.b=0,s.g=0;var Mon;v(Lc,"NetworkSimplexPlacer",1388),m(1407,1,Yt,f3),s.Le=function(n,t){return oo(u(n,15).a,u(t,15).a)},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Lc,"NetworkSimplexPlacer/0methodref$compare$Type",1407),m(1409,1,Yt,fM),s.Le=function(n,t){return oo(u(n,15).a,u(t,15).a)},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Lc,"NetworkSimplexPlacer/1methodref$compare$Type",1409),m(644,1,{644:1},uTe);var zBn=v(Lc,"NetworkSimplexPlacer/EdgeRep",644);m(405,1,{405:1},cae),s.b=!1;var FBn=v(Lc,"NetworkSimplexPlacer/NodeRep",405);m(500,13,{3:1,4:1,20:1,31:1,56:1,13:1,18:1,16:1,59:1,500:1},pxe),v(Lc,"NetworkSimplexPlacer/Path",500),m(1389,1,{},kk),s.Kb=function(n){return u(n,17).d.i.k},v(Lc,"NetworkSimplexPlacer/Path/lambda$0$Type",1389),m(1390,1,Ft,o_),s.Mb=function(n){return u(n,249)==(zn(),dr)},v(Lc,"NetworkSimplexPlacer/Path/lambda$1$Type",1390),m(1391,1,{},s_),s.Kb=function(n){return u(n,17).d.i},v(Lc,"NetworkSimplexPlacer/Path/lambda$2$Type",1391),m(1392,1,Ft,mEe),s.Mb=function(n){return qOe(rJe(u(n,9)))},v(Lc,"NetworkSimplexPlacer/Path/lambda$3$Type",1392),m(1393,1,Ft,I5),s.Mb=function(n){return F3n(u(n,12))},v(Lc,"NetworkSimplexPlacer/lambda$0$Type",1393),m(1394,1,it,oTe),s.Ad=function(n){Iwn(this.a,this.b,u(n,12))},v(Lc,"NetworkSimplexPlacer/lambda$1$Type",1394),m(1403,1,it,vEe),s.Ad=function(n){sCn(this.a,u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$10$Type",1403),m(1404,1,{},aM),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$11$Type",1404),m(1405,1,it,yEe),s.Ad=function(n){JDn(this.a,u(n,9))},v(Lc,"NetworkSimplexPlacer/lambda$12$Type",1405),m(1406,1,{},jk),s.Kb=function(n){return cl(),ve(u(n,124).e)},v(Lc,"NetworkSimplexPlacer/lambda$13$Type",1406),m(1408,1,{},Ek),s.Kb=function(n){return cl(),ve(u(n,124).e)},v(Lc,"NetworkSimplexPlacer/lambda$15$Type",1408),m(1410,1,Ft,l_),s.Mb=function(n){return cl(),u(n,405).c.k==(zn(),Qi)},v(Lc,"NetworkSimplexPlacer/lambda$17$Type",1410),m(1411,1,Ft,f_),s.Mb=function(n){return cl(),u(n,405).c.j.c.length>1},v(Lc,"NetworkSimplexPlacer/lambda$18$Type",1411),m(1412,1,it,FIe),s.Ad=function(n){Wjn(this.c,this.b,this.d,this.a,u(n,405))},s.c=0,s.d=0,v(Lc,"NetworkSimplexPlacer/lambda$19$Type",1412),m(1395,1,{},a3),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$2$Type",1395),m(1413,1,it,kEe),s.Ad=function(n){$wn(this.a,u(n,12))},s.a=0,v(Lc,"NetworkSimplexPlacer/lambda$20$Type",1413),m(1414,1,{},h3),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$21$Type",1414),m(1415,1,it,jEe),s.Ad=function(n){Hwn(this.a,u(n,9))},v(Lc,"NetworkSimplexPlacer/lambda$22$Type",1415),m(1416,1,Ft,a_),s.Mb=function(n){return qOe(n)},v(Lc,"NetworkSimplexPlacer/lambda$23$Type",1416),m(1417,1,{},_5),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$24$Type",1417),m(1418,1,Ft,EEe),s.Mb=function(n){return Ygn(this.a,u(n,9))},v(Lc,"NetworkSimplexPlacer/lambda$25$Type",1418),m(1419,1,it,sTe),s.Ad=function(n){aTn(this.a,this.b,u(n,9))},v(Lc,"NetworkSimplexPlacer/lambda$26$Type",1419),m(1420,1,Ft,M6),s.Mb=function(n){return cl(),!cc(u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$27$Type",1420),m(1421,1,Ft,Sk),s.Mb=function(n){return cl(),!cc(u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$28$Type",1421),m(1422,1,{},SEe),s.Te=function(n,t){return Pwn(this.a,u(n,25),u(t,25))},v(Lc,"NetworkSimplexPlacer/lambda$29$Type",1422),m(1396,1,{},T6),s.Kb=function(n){return cl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(Lc,"NetworkSimplexPlacer/lambda$3$Type",1396),m(1397,1,Ft,xk),s.Mb=function(n){return cl(),Ryn(u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$4$Type",1397),m(1398,1,it,xEe),s.Ad=function(n){Q_n(this.a,u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$5$Type",1398),m(1399,1,{},h_),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$6$Type",1399),m(1400,1,Ft,d3),s.Mb=function(n){return cl(),u(n,9).k==(zn(),Qi)},v(Lc,"NetworkSimplexPlacer/lambda$7$Type",1400),m(1401,1,{},d_),s.Kb=function(n){return cl(),new wn(null,new v2(new qn(Vn(bh(u(n,9)).a.Jc(),new ee))))},v(Lc,"NetworkSimplexPlacer/lambda$8$Type",1401),m(1402,1,Ft,Rp),s.Mb=function(n){return cl(),B3n(u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$9$Type",1402),m(1384,1,uc,MT),s.pg=function(n){return u(T(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?Ton:null},s.If=function(n,t){CLn(u(n,37),t)};var Ton;v(Lc,"SimpleNodePlacer",1384),m(185,1,{185:1},lv),s.Ib=function(){var n;return n="",this.c==(ah(),pp)?n+=fy:this.c==Ud&&(n+=ly),this.o==(Da(),Ag)?n+=XZ:this.o==Ya?n+="UP":n+="BALANCED",n},v(Q0,"BKAlignedLayout",185),m(509,23,{3:1,35:1,23:1,509:1},yse);var Ud,pp,Con=yt(Q0,"BKAlignedLayout/HDirection",509,Ct,t4n,Emn),Oon;m(508,23,{3:1,35:1,23:1,508:1},kse);var Ag,Ya,Non=yt(Q0,"BKAlignedLayout/VDirection",508,Ct,n4n,Smn),Don;m(1664,1,{},lTe),v(Q0,"BKAligner",1664),m(1667,1,{},OHe),v(Q0,"BKCompactor",1667),m(652,1,{652:1},dM),s.a=0,v(Q0,"BKCompactor/ClassEdge",652),m(456,1,{456:1},dxe),s.a=null,s.b=0,v(Q0,"BKCompactor/ClassNode",456),m(1387,1,uc,ETe),s.pg=function(n){return u(T(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?Ion:null},s.If=function(n,t){sBn(this,u(n,37),t)},s.d=!1;var Ion;v(Q0,"BKNodePlacer",1387),m(1665,1,{},bM),s.d=0,v(Q0,"NeighborhoodInformation",1665),m(1666,1,Yt,AEe),s.Le=function(n,t){return l8n(this,u(n,49),u(t,49))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Q0,"NeighborhoodInformation/NeighborComparator",1666),m(809,1,{}),v(Q0,"ThresholdStrategy",809),m(1795,809,{},mxe),s.vg=function(n,t,i){return this.a.o==(Da(),Ya)?Ki:Dr},s.wg=function(){},v(Q0,"ThresholdStrategy/NullThresholdStrategy",1795),m(576,1,{576:1},hTe),s.c=!1,s.d=!1,v(Q0,"ThresholdStrategy/Postprocessable",576),m(1796,809,{},vxe),s.vg=function(n,t,i){var r,c,o;return c=t==i,r=this.a.a[i.p]==t,c||r?(o=n,this.a.c==(ah(),pp)?(c&&(o=VW(this,t,!0)),!isNaN(o)&&!isFinite(o)&&r&&(o=VW(this,i,!1))):(c&&(o=VW(this,t,!0)),!isNaN(o)&&!isFinite(o)&&r&&(o=VW(this,i,!1))),o):n},s.wg=function(){for(var n,t,i,r,c;this.d.b!=0;)c=u(A_e(this.d),576),r=hKe(this,c),r.a&&(n=r.a,i=Re(this.a.f[this.a.g[c.b.p].p]),!(!i&&!cc(n)&&n.c.i.c==n.d.i.c)&&(t=bUe(this,c),t||vCe(this.e,c)));for(;this.e.a.c.length!=0;)bUe(this,u(C1e(this.e),576))},v(Q0,"ThresholdStrategy/SimpleThresholdStrategy",1796),m(635,1,{635:1,188:1,196:1},Bp),s.bg=function(){return sze(this)},s.og=function(){return sze(this)};var fre;v(qee,"EdgeRouterFactory",635),m(1445,1,uc,_U),s.pg=function(n){return vDn(u(n,37))},s.If=function(n,t){RLn(u(n,37),t)};var _on,Lon,Pon,$on,Ron,tye,Bon,zon;v(qee,"OrthogonalEdgeRouter",1445),m(1438,1,uc,jTe),s.pg=function(n){return uAn(u(n,37))},s.If=function(n,t){oRn(this,u(n,37),t)};var Fon,Jon,Hon,Gon,vD,qon;v(qee,"PolylineEdgeRouter",1438),m(1439,1,jh,zp),s.Lb=function(n){return u1e(u(n,9))},s.Fb=function(n){return this===n},s.Mb=function(n){return u1e(u(n,9))},v(qee,"PolylineEdgeRouter/1",1439),m(1851,1,Ft,C6),s.Mb=function(n){return u(n,133).c==(ha(),sb)},v(va,"HyperEdgeCycleDetector/lambda$0$Type",1851),m(1852,1,{},gM),s.Xe=function(n){return u(n,133).d},v(va,"HyperEdgeCycleDetector/lambda$1$Type",1852),m(1853,1,Ft,wM),s.Mb=function(n){return u(n,133).c==(ha(),sb)},v(va,"HyperEdgeCycleDetector/lambda$2$Type",1853),m(1854,1,{},O6),s.Xe=function(n){return u(n,133).d},v(va,"HyperEdgeCycleDetector/lambda$3$Type",1854),m(1855,1,{},N6),s.Xe=function(n){return u(n,133).d},v(va,"HyperEdgeCycleDetector/lambda$4$Type",1855),m(1856,1,{},b_),s.Xe=function(n){return u(n,133).d},v(va,"HyperEdgeCycleDetector/lambda$5$Type",1856),m(116,1,{35:1,116:1},mO),s.Dd=function(n){return tgn(this,u(n,116))},s.Fb=function(n){var t;return X(n,116)?(t=u(n,116),this.g==t.g):!1},s.Hb=function(){return this.g},s.Ib=function(){var n,t,i,r;for(n=new il("{"),r=new L(this.n);r.a"+this.b+" ("+vpn(this.c)+")"},s.d=0,v(va,"HyperEdgeSegmentDependency",133),m(515,23,{3:1,35:1,23:1,515:1},jse);var sb,Am,Uon=yt(va,"HyperEdgeSegmentDependency/DependencyType",515,Ct,i4n,xmn),Xon;m(1857,1,{},MEe),v(va,"HyperEdgeSegmentSplitter",1857),m(1858,1,{},dAe),s.a=0,s.b=0,v(va,"HyperEdgeSegmentSplitter/AreaRating",1858),m(340,1,{340:1},QK),s.a=0,s.b=0,s.c=0,v(va,"HyperEdgeSegmentSplitter/FreeArea",340),m(1859,1,Yt,g_),s.Le=function(n,t){return a2n(u(n,116),u(t,116))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(va,"HyperEdgeSegmentSplitter/lambda$0$Type",1859),m(1860,1,it,JIe),s.Ad=function(n){T6n(this.a,this.d,this.c,this.b,u(n,116))},s.b=0,v(va,"HyperEdgeSegmentSplitter/lambda$1$Type",1860),m(1861,1,{},L5),s.Kb=function(n){return new wn(null,new pn(u(n,116).e,16))},v(va,"HyperEdgeSegmentSplitter/lambda$2$Type",1861),m(1862,1,{},Ak),s.Kb=function(n){return new wn(null,new pn(u(n,116).j,16))},v(va,"HyperEdgeSegmentSplitter/lambda$3$Type",1862),m(1863,1,{},w_),s.We=function(n){return ne(re(n))},v(va,"HyperEdgeSegmentSplitter/lambda$4$Type",1863),m(653,1,{},jV),s.a=0,s.b=0,s.c=0,v(va,"OrthogonalRoutingGenerator",653),m(1668,1,{},pM),s.Kb=function(n){return new wn(null,new pn(u(n,116).e,16))},v(va,"OrthogonalRoutingGenerator/lambda$0$Type",1668),m(1669,1,{},mM),s.Kb=function(n){return new wn(null,new pn(u(n,116).j,16))},v(va,"OrthogonalRoutingGenerator/lambda$1$Type",1669),m(661,1,{}),v(Uee,"BaseRoutingDirectionStrategy",661),m(1849,661,{},yxe),s.xg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I;if(!(n.r&&!n.q))for(p=t+n.o*i,b=new L(n.n);b.aEh&&(o=p,c=n,r=new je(y,o),Vt(l.a,r),Uw(this,l,c,r,!1),S=n.r,S&&(A=ne(re(Yu(S.e,0))),r=new je(A,o),Vt(l.a,r),Uw(this,l,c,r,!1),o=t+S.o*i,c=S,r=new je(A,o),Vt(l.a,r),Uw(this,l,c,r,!1)),r=new je(I,o),Vt(l.a,r),Uw(this,l,c,r,!1)))},s.yg=function(n){return n.i.n.a+n.n.a+n.a.a},s.zg=function(){return De(),bt},s.Ag=function(){return De(),Xn},v(Uee,"NorthToSouthRoutingStrategy",1849),m(1850,661,{},kxe),s.xg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I;if(!(n.r&&!n.q))for(p=t-n.o*i,b=new L(n.n);b.aEh&&(o=p,c=n,r=new je(y,o),Vt(l.a,r),Uw(this,l,c,r,!1),S=n.r,S&&(A=ne(re(Yu(S.e,0))),r=new je(A,o),Vt(l.a,r),Uw(this,l,c,r,!1),o=t-S.o*i,c=S,r=new je(A,o),Vt(l.a,r),Uw(this,l,c,r,!1)),r=new je(I,o),Vt(l.a,r),Uw(this,l,c,r,!1)))},s.yg=function(n){return n.i.n.a+n.n.a+n.a.a},s.zg=function(){return De(),Xn},s.Ag=function(){return De(),bt},v(Uee,"SouthToNorthRoutingStrategy",1850),m(1848,661,{},jxe),s.xg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I;if(!(n.r&&!n.q))for(p=t+n.o*i,b=new L(n.n);b.aEh&&(o=p,c=n,r=new je(o,y),Vt(l.a,r),Uw(this,l,c,r,!0),S=n.r,S&&(A=ne(re(Yu(S.e,0))),r=new je(o,A),Vt(l.a,r),Uw(this,l,c,r,!0),o=t+S.o*i,c=S,r=new je(o,A),Vt(l.a,r),Uw(this,l,c,r,!0)),r=new je(o,I),Vt(l.a,r),Uw(this,l,c,r,!0)))},s.yg=function(n){return n.i.n.b+n.n.b+n.a.b},s.zg=function(){return De(),et},s.Ag=function(){return De(),Kn},v(Uee,"WestToEastRoutingStrategy",1848),m(812,1,{},fge),s.Ib=function(){return Fa(this.a)},s.b=0,s.c=!1,s.d=!1,s.f=0,v(tm,"NubSpline",812),m(410,1,{410:1},VUe,S_e),v(tm,"NubSpline/PolarCP",410),m(1440,1,uc,yHe),s.pg=function(n){return XAn(u(n,37))},s.If=function(n,t){MRn(this,u(n,37),t)};var Kon,Von,Yon,Qon,Won;v(tm,"SplineEdgeRouter",1440),m(273,1,{273:1},QR),s.Ib=function(){return this.a+" ->("+this.c+") "+this.b},s.c=0,v(tm,"SplineEdgeRouter/Dependency",273),m(454,23,{3:1,35:1,23:1,454:1},Ese);var lb,Jv,Zon=yt(tm,"SplineEdgeRouter/SideToProcess",454,Ct,r4n,Amn),esn;m(1441,1,Ft,p1),s.Mb=function(n){return iS(),!u(n,132).o},v(tm,"SplineEdgeRouter/lambda$0$Type",1441),m(1442,1,{},hd),s.Xe=function(n){return iS(),u(n,132).v+1},v(tm,"SplineEdgeRouter/lambda$1$Type",1442),m(1443,1,it,fTe),s.Ad=function(n){G3n(this.a,this.b,u(n,49))},v(tm,"SplineEdgeRouter/lambda$2$Type",1443),m(1444,1,it,aTe),s.Ad=function(n){q3n(this.a,this.b,u(n,49))},v(tm,"SplineEdgeRouter/lambda$3$Type",1444),m(132,1,{35:1,132:1},iqe,wge),s.Dd=function(n){return ign(this,u(n,132))},s.b=0,s.e=!1,s.f=0,s.g=0,s.j=!1,s.k=!1,s.n=0,s.o=!1,s.p=!1,s.q=!1,s.s=0,s.u=0,s.v=0,s.F=0,v(tm,"SplineSegment",132),m(457,1,{457:1},Fp),s.a=0,s.b=!1,s.c=!1,s.d=!1,s.e=!1,s.f=0,v(tm,"SplineSegment/EdgeInformation",457),m(1167,1,{},vM),v(X1,twe,1167),m(1168,1,Yt,p_),s.Le=function(n,t){return kCn(u(n,120),u(t,120))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(X1,ZYe,1168),m(1166,1,{},_Ae),v(X1,"MrTree",1166),m(398,23,{3:1,35:1,23:1,398:1,188:1,196:1},w$),s.bg=function(){return Aqe(this)},s.og=function(){return Aqe(this)};var IH,Ox,Nx,Dx,iye=yt(X1,"TreeLayoutPhases",398,Ct,u6n,Mmn),nsn;m(1082,214,Qw,oNe),s.kf=function(n,t){var i,r,c,o,l,f,h,b;for(Re($e(ye(n,(Mu(),Tye))))||HC((i=new hj((_b(),new w0(n))),i)),l=t.dh(Vee),l.Tg("build tGraph",1),f=(h=new QC,$u(h,n),he(h,(Ti(),_x),n),b=new pt,i_n(n,h,b),y_n(n,h,b),h),l.Ug(),l=t.dh(Vee),l.Tg("Split graph",1),o=l_n(this.a,f),l.Ug(),c=new L(o);c.a"+Ub(this.c):"e_"+Oi(this)},v(OS,"TEdge",65),m(120,150,{3:1,120:1,105:1,150:1},QC),s.Ib=function(){var n,t,i,r,c;for(c=null,r=St(this.b,0);r.b!=r.d.c;)i=u(jt(r),40),c+=(i.c==null||i.c.length==0?"n_"+i.g:"n_"+i.c)+` +`;for(t=St(this.a,0);t.b!=t.d.c;)n=u(jt(t),65),c+=(n.b&&n.c?Ub(n.b)+"->"+Ub(n.c):"e_"+Oi(n))+` +`;return c};var JBn=v(OS,"TGraph",120);m(633,494,{3:1,494:1,633:1,105:1,150:1}),v(OS,"TShape",633),m(40,633,{3:1,494:1,40:1,633:1,105:1,150:1},nQ),s.Ib=function(){return Ub(this)};var _H=v(OS,"TNode",40);m(236,1,Wh,S1),s.Ic=function(n){rc(this,n)},s.Jc=function(){var n;return n=St(this.a.d,0),new E3(n)},v(OS,"TNode/2",236),m(334,1,Fr,E3),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(jt(this.a),65).c},s.Ob=function(){return YT(this.a)},s.Qb=function(){CY(this.a)},v(OS,"TNode/2/1",334),m(1893,1,Ai,vo),s.If=function(n,t){iBn(this,u(n,120),t)},v(go,"CompactionProcessor",1893),m(1894,1,Yt,DEe),s.Le=function(n,t){return C7n(this.a,u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$0$Type",1894),m(1895,1,Ft,bTe),s.Mb=function(n){return q5n(this.b,this.a,u(n,49))},s.a=0,s.b=0,v(go,"CompactionProcessor/lambda$1$Type",1895),m(1904,1,Yt,Ml),s.Le=function(n,t){return Rvn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$10$Type",1904),m(1905,1,Yt,Tk),s.Le=function(n,t){return opn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$11$Type",1905),m(1906,1,Yt,P5),s.Le=function(n,t){return Bvn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$12$Type",1906),m(1896,1,Ft,IEe),s.Mb=function(n){return Xwn(this.a,u(n,49))},s.a=0,v(go,"CompactionProcessor/lambda$2$Type",1896),m(1897,1,Ft,_Ee),s.Mb=function(n){return Kwn(this.a,u(n,49))},s.a=0,v(go,"CompactionProcessor/lambda$3$Type",1897),m(1898,1,Ft,b3),s.Mb=function(n){return u(n,40).c.indexOf(DF)==-1},v(go,"CompactionProcessor/lambda$4$Type",1898),m(1899,1,{},LEe),s.Kb=function(n){return Pyn(this.a,u(n,40))},s.a=0,v(go,"CompactionProcessor/lambda$5$Type",1899),m(K0,1,{},PEe),s.Kb=function(n){return Y9n(this.a,u(n,40))},s.a=0,v(go,"CompactionProcessor/lambda$6$Type",K0),m(1901,1,Yt,$Ee),s.Le=function(n,t){return r9n(this.a,u(n,240),u(t,240))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$7$Type",1901),m(1902,1,Yt,REe),s.Le=function(n,t){return c9n(this.a,u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$8$Type",1902),m(1903,1,Yt,Ck),s.Le=function(n,t){return spn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$9$Type",1903),m(1891,1,Ai,D6),s.If=function(n,t){ZDn(u(n,120),t)},v(go,"DirectionProcessor",1891),m(1883,1,Ai,sNe),s.If=function(n,t){v_n(this,u(n,120),t)},v(go,"FanProcessor",1883),m(1251,1,Ai,$5),s.If=function(n,t){gXe(u(n,120),t)},v(go,"GraphBoundsProcessor",1251),m(1252,1,{},Zq),s.We=function(n){return u(n,40).e.a},v(go,"GraphBoundsProcessor/lambda$0$Type",1252),m(1253,1,{},Bs),s.We=function(n){return u(n,40).e.b},v(go,"GraphBoundsProcessor/lambda$1$Type",1253),m(1254,1,{},yM),s.We=function(n){return Cgn(u(n,40))},v(go,"GraphBoundsProcessor/lambda$2$Type",1254),m(1255,1,{},kM),s.We=function(n){return Ogn(u(n,40))},v(go,"GraphBoundsProcessor/lambda$3$Type",1255),m(264,23,{3:1,35:1,23:1,264:1,196:1},gw),s.bg=function(){switch(this.g){case 0:return new Pxe;case 1:return new sNe;case 2:return new Lxe;case 3:return new EM;case 4:return new v_;case 8:return new m_;case 5:return new D6;case 6:return new uh;case 7:return new vo;case 9:return new $5;case 10:return new nl;default:throw $(new Gn(nee+(this.f!=null?this.f:""+this.g)))}};var rye,cye,uye,oye,sye,lye,fye,aye,hye,dye,are,HBn=yt(go,tee,264,Ct,oze,Tmn),tsn;m(1890,1,Ai,m_),s.If=function(n,t){nRn(u(n,120),t)},v(go,"LevelCoordinatesProcessor",1890),m(1888,1,Ai,v_),s.If=function(n,t){ENn(this,u(n,120),t)},s.a=0,v(go,"LevelHeightProcessor",1888),m(1889,1,Wh,eU),s.Ic=function(n){rc(this,n)},s.Jc=function(){return jn(),p9(),d7},v(go,"LevelHeightProcessor/1",1889),m(1884,1,Ai,Lxe),s.If=function(n,t){PDn(this,u(n,120),t)},v(go,"LevelProcessor",1884),m(1885,1,Ft,jM),s.Mb=function(n){return Re($e(T(u(n,40),(Ti(),fb))))},v(go,"LevelProcessor/lambda$0$Type",1885),m(1886,1,Ai,EM),s.If=function(n,t){NTn(this,u(n,120),t)},s.a=0,v(go,"NeighborsProcessor",1886),m(1887,1,Wh,SM),s.Ic=function(n){rc(this,n)},s.Jc=function(){return jn(),p9(),d7},v(go,"NeighborsProcessor/1",1887),m(1892,1,Ai,uh),s.If=function(n,t){p_n(this,u(n,120),t)},s.a=0,v(go,"NodePositionProcessor",1892),m(1882,1,Ai,Pxe),s.If=function(n,t){tPn(this,u(n,120),t)},v(go,"RootProcessor",1882),m(1907,1,Ai,nl),s.If=function(n,t){vSn(u(n,120),t)},v(go,"Untreeifyer",1907),m(385,23,{3:1,35:1,23:1,385:1},sK);var yD,hre,bye,gye=yt(LN,"EdgeRoutingMode",385,Ct,eyn,Cmn),isn,kD,_7,dre,wye,pye,bre,gre,mye,wre,vye,pre,Ix,mre,LH,PH,Yf,Ea,L7,_x,Lx,Xd,yye,rsn,vre,fb,jD,ED;m(846,1,qa,xT),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Jpe),""),VQe),"Turns on Tree compaction which decreases the size of the whole tree by placing nodes of multiple levels in one large level"),(Pn(),!1)),(cg(),Sr)),Yi),nn((ph(),Mn))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Hpe),""),"Edge End Texture Length"),"Should be set to the length of the texture at the end of an edge. This value can be used to improve the Edge Routing."),7),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Gpe),""),"Tree Level"),"The index for the tree level the node is in"),ve(0)),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,qpe),""),VQe),"When set to a positive number this option will force the algorithm to place the node to the specified position within the trees layer if weighting is set to constraint"),ve(-1)),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Upe),""),"Weighting of Nodes"),"Which weighting to use when computing a node order."),Eye),Ri),Lye),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Xpe),""),"Edge Routing Mode"),"Chooses an Edge Routing algorithm."),kye),Ri),gye),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Kpe),""),"Search Order"),"Which search order to use when computing a spanning tree."),jye),Ri),$ye),nn(Mn)))),BVe((new fP,n))};var csn,usn,osn,kye,ssn,lsn,jye,fsn,asn,Eye;v(LN,"MrTreeMetaDataProvider",846),m(990,1,qa,fP),s.tf=function(n){BVe(n)};var hsn,Sye,xye,mp,Aye,Mye,yre,dsn,bsn,gsn,wsn,psn,msn,vsn,Tye,Cye,Oye,ysn,Hv,$H,Nye,ksn,Dye,kre,jsn,Esn,Ssn,Iye,xsn,Nh,_ye;v(LN,"MrTreeOptions",990),m(991,1,{},Ok),s.uf=function(){var n;return n=new oNe,n},s.vf=function(n){},v(LN,"MrTreeOptions/MrtreeFactory",991),m(353,23,{3:1,35:1,23:1,353:1},p$);var jre,RH,Ere,Sre,Lye=yt(LN,"OrderWeighting",353,Ct,f6n,Omn),Asn;m(425,23,{3:1,35:1,23:1,425:1},Sse);var Pye,xre,$ye=yt(LN,"TreeifyingOrder",425,Ct,c4n,Nmn),Msn;m(1446,1,uc,DU),s.pg=function(n){return u(n,120),Tsn},s.If=function(n,t){c7n(this,u(n,120),t)};var Tsn;v("org.eclipse.elk.alg.mrtree.p1treeify","DFSTreeifyer",1446),m(1447,1,uc,oP),s.pg=function(n){return u(n,120),Csn},s.If=function(n,t){zDn(this,u(n,120),t)};var Csn;v(Q8,"NodeOrderer",1447),m(1454,1,{},nU),s.rd=function(n){return fDe(n)},v(Q8,"NodeOrderer/0methodref$lambda$6$Type",1454),m(1448,1,Ft,E_),s.Mb=function(n){return R4(),Re($e(T(u(n,40),(Ti(),fb))))},v(Q8,"NodeOrderer/lambda$0$Type",1448),m(1449,1,Ft,S_),s.Mb=function(n){return R4(),u(T(u(n,40),(Mu(),Hv)),15).a<0},v(Q8,"NodeOrderer/lambda$1$Type",1449),m(1450,1,Ft,zEe),s.Mb=function(n){return U8n(this.a,u(n,40))},v(Q8,"NodeOrderer/lambda$2$Type",1450),m(1451,1,Ft,BEe),s.Mb=function(n){return $yn(this.a,u(n,40))},v(Q8,"NodeOrderer/lambda$3$Type",1451),m(1452,1,Yt,TM),s.Le=function(n,t){return a8n(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Q8,"NodeOrderer/lambda$4$Type",1452),m(1453,1,Ft,x_),s.Mb=function(n){return R4(),u(T(u(n,40),(Ti(),gre)),15).a!=0},v(Q8,"NodeOrderer/lambda$5$Type",1453),m(1455,1,uc,IU),s.pg=function(n){return u(n,120),Osn},s.If=function(n,t){UIn(this,u(n,120),t)},s.b=0;var Osn;v("org.eclipse.elk.alg.mrtree.p3place","NodePlacer",1455),m(1456,1,uc,ST),s.pg=function(n){return u(n,120),Nsn},s.If=function(n,t){MIn(u(n,120),t)};var Nsn,GBn=v(Qs,"EdgeRouter",1456);m(1458,1,Yt,Nk),s.Le=function(n,t){return oo(u(n,15).a,u(t,15).a)},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/0methodref$compare$Type",1458),m(1463,1,{},xM),s.We=function(n){return ne(re(n))},v(Qs,"EdgeRouter/1methodref$doubleValue$Type",1463),m(1465,1,Yt,iw),s.Le=function(n,t){return ki(ne(re(n)),ne(re(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/2methodref$compare$Type",1465),m(1467,1,Yt,Dk),s.Le=function(n,t){return ki(ne(re(n)),ne(re(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/3methodref$compare$Type",1467),m(1469,1,{},I6),s.We=function(n){return ne(re(n))},v(Qs,"EdgeRouter/4methodref$doubleValue$Type",1469),m(1471,1,Yt,AM),s.Le=function(n,t){return ki(ne(re(n)),ne(re(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/5methodref$compare$Type",1471),m(1473,1,Yt,MM),s.Le=function(n,t){return ki(ne(re(n)),ne(re(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/6methodref$compare$Type",1473),m(1457,1,{},y_),s.Kb=function(n){return P1(),u(T(u(n,40),(Mu(),Nh)),15)},v(Qs,"EdgeRouter/lambda$0$Type",1457),m(1468,1,{},k_),s.Kb=function(n){return ypn(u(n,40))},v(Qs,"EdgeRouter/lambda$11$Type",1468),m(1470,1,{},wTe),s.Kb=function(n){return J3n(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Qs,"EdgeRouter/lambda$13$Type",1470),m(1472,1,{},gTe),s.Kb=function(n){return Epn(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Qs,"EdgeRouter/lambda$15$Type",1472),m(1474,1,Yt,j_),s.Le=function(n,t){return QEn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$17$Type",1474),m(1475,1,Yt,tU),s.Le=function(n,t){return WEn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$18$Type",1475),m(1476,1,Yt,A_),s.Le=function(n,t){return eSn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$19$Type",1476),m(1459,1,Ft,FEe),s.Mb=function(n){return y4n(this.a,u(n,40))},s.a=0,v(Qs,"EdgeRouter/lambda$2$Type",1459),m(1477,1,Yt,M_),s.Le=function(n,t){return ZEn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$20$Type",1477),m(1460,1,Yt,T_),s.Le=function(n,t){return N3n(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$3$Type",1460),m(1461,1,Yt,CM),s.Le=function(n,t){return D3n(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$4$Type",1461),m(1462,1,{},C_),s.Kb=function(n){return kpn(u(n,40))},v(Qs,"EdgeRouter/lambda$5$Type",1462),m(1464,1,{},pTe),s.Kb=function(n){return H3n(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Qs,"EdgeRouter/lambda$7$Type",1464),m(1466,1,{},mTe),s.Kb=function(n){return jpn(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Qs,"EdgeRouter/lambda$9$Type",1466),m(662,1,{662:1},lHe),s.e=0,s.f=!1,s.g=!1,v(Qs,"MultiLevelEdgeNodeNodeGap",662),m(1864,1,Yt,O_),s.Le=function(n,t){return I4n(u(n,240),u(t,240))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"MultiLevelEdgeNodeNodeGap/lambda$0$Type",1864),m(1865,1,Yt,N_),s.Le=function(n,t){return _4n(u(n,240),u(t,240))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"MultiLevelEdgeNodeNodeGap/lambda$1$Type",1865);var Gv;m(487,23,{3:1,35:1,23:1,487:1,188:1,196:1},xse),s.bg=function(){return XFe(this)},s.og=function(){return XFe(this)};var BH,qv,Rye=yt(Vpe,"RadialLayoutPhases",487,Ct,u4n,Dmn),Dsn;m(1083,214,Qw,$Ae),s.kf=function(n,t){var i,r,c,o,l,f;if(i=qUe(this,n),t.Tg("Radial layout",i.c.length),Re($e(ye(n,(J0(),Vye))))||HC((r=new hj((_b(),new w0(n))),r)),f=YAn(n),ji(n,(B3(),Gv),f),!f)throw $(new Gn("The given graph is not a tree!"));for(c=ne(re(ye(n,JH))),c==0&&(c=vqe(n)),ji(n,JH,c),l=new L(qUe(this,n));l.a=3)for(fe=u(K(te,0),26),Ie=u(K(te,1),26),o=0;o+2=fe.f+Ie.f+p||Ie.f>=be.f+fe.f+p){cn=!0;break}else++o;else cn=!0;if(!cn){for(S=te.i,f=new ut(te);f.e!=f.i.gc();)l=u(ft(f),26),ji(l,(Xt(),PD),ve(S)),--S;kKe(n,new i4),t.Ug();return}for(i=(RC(this.a),fa(this.a,(WB(),Px),u(ye(n,A6e),188)),fa(this.a,HH,u(ye(n,y6e),188)),fa(this.a,$re,u(ye(n,E6e),188)),Fse(this.a,(Tn=new or,qt(Tn,Px,(kz(),zre)),qt(Tn,HH,Bre),Re($e(ye(n,m6e)))&&qt(Tn,Px,Fre),Re($e(ye(n,p6e)))&&qt(Tn,Px,Rre),Tn)),rN(this.a,n)),b=1/i.c.length,O=new L(i);O.a0&&gFe((Yn(t-1,n.length),n.charCodeAt(t-1)),aQe);)--t;if(r>=t)throw $(new Gn("The given string does not contain any numbers."));if(c=K2((Yr(r,t,n.length),n.substr(r,t-r)),`,|;|\r| +`),c.length!=2)throw $(new Gn("Exactly two numbers are expected, "+c.length+" were found."));try{this.a=F2(J2(c[0])),this.b=F2(J2(c[1]))}catch(o){throw o=sr(o),X(o,131)?(i=o,$(new Gn(hQe+i))):$(o)}},s.Ib=function(){return"("+this.a+","+this.b+")"},s.a=0,s.b=0;var Lr=v(CN,"KVector",8);m(78,66,{3:1,4:1,20:1,31:1,56:1,18:1,66:1,16:1,78:1,414:1},xs,qP,NOe),s.Nc=function(){return Mkn(this)},s.ag=function(n){var t,i,r,c,o,l;r=K2(n,`,|;|\\(|\\)|\\[|\\]|\\{|\\}| | | +`),qs(this);try{for(i=0,o=0,c=0,l=0;i0&&(o%2==0?c=F2(r[i]):l=F2(r[i]),o>0&&o%2!=0&&Vt(this,new je(c,l)),++o),++i}catch(f){throw f=sr(f),X(f,131)?(t=f,$(new Gn("The given string does not match the expected format for vectors."+t))):$(f)}},s.Ib=function(){var n,t,i;for(n=new il("("),t=St(this,0);t.b!=t.d.c;)i=u(jt(t),8),Kt(n,i.a+","+i.b),t.b!=t.d.c&&(n.a+="; ");return(n.a+=")",n).a};var l9e=v(CN,"KVectorChain",78);m(256,23,{3:1,35:1,23:1,256:1},Rj);var sce,ZH,eG,CD,OD,nG,f9e=yt(Oo,"Alignment",256,Ct,T9n,c3n),dfn;m(975,1,qa,CT),s.tf=function(n){rKe(n)};var a9e,lce,bfn,h9e,d9e,gfn,b9e,wfn,pfn,g9e,w9e,mfn;v(Oo,"BoxLayouterOptions",975),m(976,1,{},GM),s.uf=function(){var n;return n=new hL,n},s.vf=function(n){},v(Oo,"BoxLayouterOptions/BoxFactory",976),m(299,23,{3:1,35:1,23:1,299:1},Bj);var Gx,fce,qx,Ux,Xx,ace,hce=yt(Oo,"ContentAlignment",299,Ct,C9n,u3n),vfn;m(689,1,qa,TT),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,pWe),""),"Layout Algorithm"),"Select a specific layout algorithm."),(cg(),By)),ze),nn((ph(),Mn))))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,mWe),""),"Resolved Layout Algorithm"),"Meta data associated with the selected algorithm."),Wa),XBn),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ppe),""),"Alignment"),"Alignment of the selected node relative to other nodes; the exact meaning depends on the used algorithm."),p9e),Ri),f9e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,G8),""),"Aspect Ratio"),"The desired aspect ratio of the drawing, that is the quotient of width by height."),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,N2e),""),"Bend Points"),"A fixed list of bend points for the edge. This is used by the 'Fixed Layout' algorithm to specify a pre-defined routing for an edge. The vector chain must include the source point, any bend points, and the target point, so it must have at least two points."),Wa),l9e),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,TF),""),"Content Alignment"),"Specifies how the content of a node are aligned. Each node can individually control the alignment of its contents. I.e. if a node should be aligned top left in its parent node, the parent node should specify that option."),v9e),Ry),hce),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,_N),""),"Debug Mode"),"Whether additional debug information shall be generated."),(Pn(),!1)),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Fee),""),"Direction"),"Overall direction of edges: horizontal (right / left) or vertical (down / up)."),y9e),Ri),Vx),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,IN),""),"Edge Routing"),"What kind of edge routing style should be applied for the content of a parent node. Algorithms may also set this option to single edges in order to mark them as splines. The bend point list of edges with this option set to SPLINES must be interpreted as control points for a piecewise cubic spline."),E9e),Ri),Ace),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,C2e),""),"Expand Nodes"),"If active, nodes are expanded to fill the area of their parent."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,MF),""),"Hierarchy Handling"),"Determines whether separate layout runs are triggered for different compound nodes in a hierarchical graph. Setting a node's hierarchy handling to `INCLUDE_CHILDREN` will lay out that node and all of its descendants in a single layout run, until a descendant is encountered which has its hierarchy handling set to `SEPARATE_CHILDREN`. In general, `SEPARATE_CHILDREN` will ensure that a new layout run is triggered for a node with that setting. Including multiple levels of hierarchy in a single layout run may allow cross-hierarchical edges to be laid out properly. If the root node is set to `INHERIT` (or not set at all), the default behavior is `SEPARATE_CHILDREN`."),x9e),Ri),b8e),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,nm),""),"Padding"),"The padding to be left to a parent element's border when placing child elements. This can also serve as an output option of a layout algorithm if node size calculation is setup appropriately."),P9e),Wa),j3e),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,jS),""),"Interactive"),"Whether the algorithm should be run in interactive mode for the content of a parent node. What this means exactly depends on how the specific algorithm interprets this option. Usually in the interactive mode algorithms try to modify the current layout as little as possible."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,OF),""),"interactive Layout"),"Whether the graph should be changeable interactively and by setting constraints"),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ES),""),"Omit Node Micro Layout"),"Node micro layout comprises the computation of node dimensions (if requested), the placement of ports and their labels, and the placement of node labels. The functionality is implemented independent of any specific layout algorithm and shouldn't have any negative impact on the layout algorithm's performance itself. Yet, if any unforeseen behavior occurs, this option allows to deactivate the micro layout."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,WZ),""),"Port Constraints"),"Defines constraints of the position of the ports of a node."),F9e),Ri),p8e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,CF),""),"Position"),"The position of a node, port, or label. This is used by the 'Fixed Layout' algorithm to specify a pre-defined position."),Wa),Lr),Mi(fr,z(B(Qa,1),ke,160,0,[Kd,Q1]))))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,EN),""),"Priority"),"Defines the priority of an object; its meaning depends on the specific layout algorithm and the context where it is used."),hc),jr),Mi(fr,z(B(Qa,1),ke,160,0,[Sa]))))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,sF),""),"Randomization Seed"),"Seed used for pseudo-random number generators to control the layout algorithm. If the value is 0, the seed shall be determined pseudo-randomly (e.g. from the system time)."),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,kS),""),"Separate Connected Components"),"Whether each connected component should be processed separately."),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Tpe),""),"Junction Points"),"This option is not used as option, but as output of the layout algorithms. It is attached to edges and determines the points where junction symbols should be drawn in order to represent hyperedges with orthogonal routing. Whether such points are computed depends on the chosen layout algorithm and edge routing style. The points are put into the vector chain with no specific order."),T9e),Wa),l9e),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Dpe),""),"Comment Box"),"Whether the node should be regarded as a comment box instead of a regular node. In that case its placement should be similar to how labels are handled. Any edges incident to a comment box specify to which graph elements the comment is related."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ipe),""),"Hypernode"),"Whether the node should be handled as a hypernode."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,yBn),""),"Label Manager"),"Label managers can shorten labels upon a layout algorithm's request."),Wa),ZBn),Mi(Mn,z(B(Qa,1),ke,160,0,[Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vWe),""),"Softwrapping Fuzziness"),"Determines the amount of fuzziness to be used when performing softwrapping on labels. The value expresses the percent of overhang that is permitted for each line. If the next line would take up less space than this threshold, it is appended to the current line instead of being placed in a new line."),0),ec),gr),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Lpe),""),"Margins"),"Margins define additional space around the actual bounds of a graph element. For instance, ports or labels being placed on the outside of a node's border might introduce such a margin. The margin is used to guarantee non-overlap of other graph elements with those ports or labels."),C9e),Wa),k3e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,gpe),""),"No Layout"),"No layout is done for the associated element. This is used to mark parts of a diagram to avoid their inclusion in the layout graph, or to mark parts of the layout graph to prevent layout engines from processing them. If you wish to exclude the contents of a compound node from automatic layout, while the node itself is still considered on its own layer, use the 'Fixed Layout' algorithm for that node."),!1),Sr),Yi),Mi(fr,z(B(Qa,1),ke,160,0,[Sa,Kd,Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,yWe),""),"Scale Factor"),"The scaling factor to be applied to the corresponding node in recursive layout. It causes the corresponding node's size to be adjusted, and its ports and labels to be sized and placed accordingly after the layout of that node has been determined (and before the node itself and its siblings are arranged). The scaling is not reverted afterwards, so the resulting layout graph contains the adjusted size and position data. This option is currently not supported if 'Layout Hierarchy' is set."),1),ec),gr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,kWe),""),"Child Area Width"),"The width of the area occupied by the laid out children of a node."),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,jWe),""),"Child Area Height"),"The height of the area occupied by the laid out children of a node."),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,SN),""),hWe),"Turns topdown layout on and off. If this option is enabled, hierarchical layout will be computed first for the root node and then for its children recursively. Layouts are then scaled down to fit the area provided by their parents. Graphs must follow a certain structure for topdown layout to work properly. {@link TopdownNodeTypes.PARALLEL_NODE} nodes must have children of type {@link TopdownNodeTypes.HIERARCHICAL_NODE} and must define {@link topdown.hierarchicalNodeWidth} and {@link topdown.hierarchicalNodeAspectRatio} for their children. Furthermore they need to be laid out using an algorithm that is a {@link TopdownLayoutProvider}. Hierarchical nodes can also be parents of other hierarchical nodes and can optionally use a {@link TopdownSizeApproximator} to dynamically set sizes during topdown layout. In this case {@link topdown.hierarchicalNodeWidth} and {@link topdown.hierarchicalNodeAspectRatio} should be set on the node itself rather than the parent. The values are then used by the size approximator as base values. Hierarchical nodes require the layout option {@link nodeSize.fixedGraphSize} to be true to prevent the algorithm used there from resizing the hierarchical node. This option is not supported if 'Hierarchy Handling' is set to 'INCLUDE_CHILDREN'"),!1),Sr),Yi),nn(Mn)))),Gi(n,SN,Ww,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,EWe),""),"Animate"),"Whether the shift from the old layout to the new computed layout shall be animated."),!0),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,SWe),""),"Animation Time Factor"),"Factor for computation of animation time. The higher the value, the longer the animation time. If the value is 0, the resulting time is always equal to the minimum defined by 'Minimal Animation Time'."),ve(100)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xWe),""),"Layout Ancestors"),"Whether the hierarchy levels on the path from the selected element to the root of the diagram shall be included in the layout process."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,AWe),""),"Maximal Animation Time"),"The maximal time for animations, in milliseconds."),ve(4e3)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,MWe),""),"Minimal Animation Time"),"The minimal time for animations, in milliseconds."),ve(400)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,TWe),""),"Progress Bar"),"Whether a progress bar shall be displayed during layout computations."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,CWe),""),"Validate Graph"),"Whether the graph shall be validated before any layout algorithm is applied. If this option is enabled and at least one error is found, the layout process is aborted and a message is shown to the user."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,OWe),""),"Validate Options"),"Whether layout options shall be validated before any layout algorithm is applied. If this option is enabled and at least one error is found, the layout process is aborted and a message is shown to the user."),!0),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,NWe),""),"Zoom to Fit"),"Whether the zoom level shall be set to view the whole diagram after layout."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,O2e),"box"),"Box Layout Mode"),"Configures the packing mode used by the {@link BoxLayoutProvider}. If SIMPLE is not required (neither priorities are used nor the interactive mode), GROUP_DEC can improve the packing and decrease the area. GROUP_MIXED and GROUP_INC may, in very specific scenarios, work better."),m9e),Ri),O8e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,DWe),"json"),"Shape Coords"),"For layouts transferred into JSON graphs, specify the coordinate system to be used for nodes, ports, and labels of nodes and ports."),M9e),Ri),y8e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,IWe),"json"),"Edge Coords"),"For layouts transferred into JSON graphs, specify the coordinate system to be used for edge route points and edge labels."),A9e),Ri),n8e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ipe),Xa),"Comment Comment Spacing"),"Spacing to be preserved between a comment box and other comment boxes connected to the same node. The space left between comment boxes of different nodes is controlled by the node-node spacing."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,rpe),Xa),"Comment Node Spacing"),"Spacing to be preserved between a node and its connected comment boxes. The space left between a node and the comments of another node is controlled by the node-node spacing."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,cpe),Xa),"Components Spacing"),"Spacing to be preserved between pairs of connected components. This option is only relevant if 'separateConnectedComponents' is activated."),20),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,upe),Xa),"Edge Spacing"),"Spacing to be preserved between any two edges. Note that while this can somewhat easily be satisfied for the segments of orthogonally drawn edges, it is harder for general polylines or splines."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,QZ),Xa),"Edge Label Spacing"),"The minimal distance to be preserved between a label and the edge it is associated with. Note that the placement of a label is influenced by the 'edgelabels.placement' option."),2),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,zee),Xa),"Edge Node Spacing"),"Spacing to be preserved between nodes and edges."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ope),Xa),"Label Spacing"),"Determines the amount of space to be left between two labels of the same graph element."),0),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,fpe),Xa),"Label Node Spacing"),"Spacing to be preserved between labels and the border of node they are associated with. Note that the placement of a label is influenced by the 'nodelabels.placement' option."),5),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,spe),Xa),"Horizontal spacing between Label and Port"),"Horizontal spacing to be preserved between labels and the ports they are associated with. Note that the placement of a label is influenced by the 'portlabels.placement' option."),1),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,lpe),Xa),"Vertical spacing between Label and Port"),"Vertical spacing to be preserved between labels and the ports they are associated with. Note that the placement of a label is influenced by the 'portlabels.placement' option."),1),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,em),Xa),"Node Spacing"),"The minimal distance to be preserved between each two nodes."),20),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ape),Xa),"Node Self Loop Spacing"),"Spacing to be preserved between a node and its self loops."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hpe),Xa),"Port Spacing"),"Spacing between pairs of ports of the same node."),10),ec),gr),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,dpe),Xa),"Individual Spacing"),"Allows to specify individual spacing values for graph elements that shall be different from the value specified for the element's parent."),Wa),gan),Mi(fr,z(B(Qa,1),ke,160,0,[Sa,Kd,Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ppe),Xa),"Additional Port Space"),"Additional space around the sets of ports on each node side. For each side of a node, this option can reserve additional space before and after the ports on each side. For example, a top spacing of 20 makes sure that the first port on the western and eastern side is 20 units away from the northern border."),Q9e),Wa),k3e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Hee),PWe),"Layout Partition"),"Partition to which the node belongs. This requires Layout Partitioning to be active. Nodes with lower partition IDs will appear to the left of nodes with higher partition IDs (assuming a left-to-right layout direction)."),hc),jr),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),Gi(n,Hee,Jee,Nfn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Jee),PWe),"Layout Partitioning"),"Whether to activate partitioned layout. This will allow to group nodes through the Layout Partition option. a pair of nodes with different partition indices is then placed such that the node with lower index is placed to the left of the other node (with left-to-right layout direction). Depending on the layout algorithm, this may only be guaranteed to work if all nodes have a layout partition configured, or at least if edges that cross partitions are not part of a partition-crossing cycle."),$9e),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ype),$We),"Node Label Padding"),"Define padding for node labels that are placed inside of a node."),N9e),Wa),j3e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,U8),$We),"Node Label Placement"),"Hints for where node labels are to be placed; if empty, the node label's position is not modified."),D9e),Ry),$c),Mi(fr,z(B(Qa,1),ke,160,0,[Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Epe),RF),"Port Alignment"),"Defines the default port distribution for a node. May be overridden for each side individually."),B9e),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Spe),RF),"Port Alignment (North)"),"Defines how ports on the northern side are placed, overriding the node's general port alignment."),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,xpe),RF),"Port Alignment (South)"),"Defines how ports on the southern side are placed, overriding the node's general port alignment."),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Ape),RF),"Port Alignment (West)"),"Defines how ports on the western side are placed, overriding the node's general port alignment."),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Mpe),RF),"Port Alignment (East)"),"Defines how ports on the eastern side are placed, overriding the node's general port alignment."),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,wv),bne),"Node Size Constraints"),"What should be taken into account when calculating a node's size. Empty size constraints specify that a node's size is already fixed and should not be changed."),I9e),Ry),tA),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hy),bne),"Node Size Options"),"Options modifying the behavior of the size constraints set on a node. Each member of the set specifies something that should be taken into account when calculating node sizes. The empty set corresponds to no further modifications."),L9e),Ry),k8e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,dy),bne),"Node Size Minimum"),"The minimal size to which a node can be reduced."),_9e),Wa),Lr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,q8),bne),"Fixed Graph Size"),"By default, the fixed layout provider will enlarge a graph until it is large enough to contain its children. If this option is set, it won't do so."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ope),Bee),"Edge Label Placement"),"Gives a hint on where to put edge labels."),k9e),Ri),t8e),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,lF),Bee),"Inline Edge Labels"),"If true, an edge label is placed directly on its edge. May only apply to center edge labels. This kind of label placement is only advisable if the label's rendering is such that it is not crossed by its edge and thus stays legible."),!1),Sr),Yi),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,kBn),"font"),"Font Name"),"Font name used for a label."),By),ze),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,_We),"font"),"Font Size"),"Font size used for a label."),hc),jr),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,_pe),gne),"Port Anchor Offset"),"The offset to the port position where connections shall be attached."),Wa),Lr),nn(Kd)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Npe),gne),"Port Index"),"The index of a port in the fixed order around a node. The order is assumed as clockwise, starting with the leftmost port on the top side. This option must be set if 'Port Constraints' is set to FIXED_ORDER and no specific positions are given for the ports. Additionally, the option 'Port Side' must be defined in this case."),hc),jr),nn(Kd)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,wpe),gne),"Port Side"),"The side of a node on which a port is situated. This option must be set if 'Port Constraints' is set to FIXED_SIDE or FIXED_ORDER and no specific positions are given for the ports."),G9e),Ri),xc),nn(Kd)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,bpe),gne),"Port Border Offset"),"The offset of ports on the node border. With a positive offset the port is moved outside of the node, while with a negative offset the port is moved towards the inside. An offset of 0 means that the port is placed directly on the node border, i.e. if the port side is north, the port's south border touches the nodes's north border; if the port side is east, the port's west border touches the nodes's east border; if the port side is south, the port's north border touches the node's south border; if the port side is west, the port's east border touches the node's west border."),ec),gr),nn(Kd)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,X8),_2e),"Port Label Placement"),"Decides on a placement method for port labels; if empty, the node label's position is not modified."),J9e),Ry),sG),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,kpe),_2e),"Port Labels Next to Port"),"Use 'portLabels.placement': NEXT_TO_PORT_OF_POSSIBLE."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,jpe),_2e),"Treat Port Labels as Group"),"If this option is true (default), the labels of a port will be treated as a group when it comes to centering them next to their port. If this option is false, only the first label will be centered next to the port, with the others being placed below. This only applies to labels of eastern and western ports and will have no effect if labels are not placed next to their port."),!0),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hne),t7),"Number of size categories"),"Defines the number of categories to use for the FIXED_INTEGER_RATIO_BOXES size approximator."),ve(3)),hc),jr),nn(Mn)))),Gi(n,hne,dne,Hfn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,D2e),t7),"Weight of a node containing children for determining the graph size"),"When determining the graph size for the size categorisation, this value determines how many times a node containing children is weighted more than a simple node. For example setting this value to four would result in a graph containing a simple node and a hierarchical node to be counted as having a size of five."),ve(4)),hc),jr),nn(Mn)))),Gi(n,D2e,hne,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xN),t7),"Topdown Scale Factor"),"The scaling factor to be applied to the nodes laid out within the node in recursive topdown layout. The difference to 'Scale Factor' is that the node itself is not scaled. This value has to be set on hierarchical nodes."),1),ec),gr),nn(Mn)))),Gi(n,xN,Ww,zfn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,dne),t7),"Topdown Size Approximator"),"The size approximator to be used to set sizes of hierarchical nodes during topdown layout. The default value is null, which results in nodes keeping whatever size is defined for them e.g. through parent parallel node or by manually setting the size."),null),Wa),KBn),nn(fr)))),Gi(n,dne,Ww,Ffn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,AN),t7),"Topdown Hierarchical Node Width"),"The fixed size of a hierarchical node when using topdown layout. If this value is set on a parallel node it applies to its children, when set on a hierarchical node it applies to the node itself."),150),ec),gr),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),Gi(n,AN,Ww,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,MN),t7),"Topdown Hierarchical Node Aspect Ratio"),"The fixed aspect ratio of a hierarchical node when using topdown layout. Default is 1/sqrt(2). If this value is set on a parallel node it applies to its children, when set on a hierarchical node it applies to the node itself."),1.414),ec),gr),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),Gi(n,MN,Ww,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ww),t7),"Topdown Node Type"),"The different node types used for topdown layout. If the node type is set to {@link TopdownNodeTypes.PARALLEL_NODE} the algorithm must be set to a {@link TopdownLayoutProvider} such as {@link TopdownPacking}. The {@link nodeSize.fixedGraphSize} option is technically only required for hierarchical nodes."),null),Ri),E8e),nn(fr)))),Gi(n,Ww,q8,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,I2e),t7),"Topdown Scale Cap"),"Determines the upper limit for the topdown scale factor. The default value is 1.0 which ensures that nested children never end up appearing larger than their parents in terms of unit sizes such as the font size. If the limit is larger, nodes will fully utilize the available space, but it is counteriniuitive for inner nodes to have a larger scale than outer nodes."),1),ec),gr),nn(Mn)))),Gi(n,I2e,Ww,Bfn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,mpe),RWe),"Activate Inside Self Loops"),"Whether this node allows to route self loops inside of it instead of around it. If set to true, this will make the node a compound node if it isn't already, and will require the layout algorithm to support compound nodes with hierarchical ports."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vpe),RWe),"Inside Self Loop"),"Whether a self loop should be routed inside a node instead of around that node."),!1),Sr),Yi),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Cpe),"edge"),"Edge Thickness"),"The thickness of an edge. This is a hint on the line width used to draw an edge, possibly requiring more space to be reserved for it."),1),ec),gr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,LWe),"edge"),"Edge Type"),"The type of an edge. This is usually used for UML class diagrams, where associations must be handled differently from generalizations."),S9e),Ri),s8e),nn(Sa)))),Cj(n,new N4(Ej(h9(a9(new f0,$n),"Layered"),'The layer-based method was introduced by Sugiyama, Tagawa and Toda in 1981. It emphasizes the direction of edges by pointing as many edges as possible into the same direction. The nodes are arranged in layers, which are sometimes called "hierarchies", and then reordered such that the number of edge crossings is minimized. Afterwards, concrete coordinates are computed for the nodes and edge bend points.'))),Cj(n,new N4(Ej(h9(a9(new f0,"org.eclipse.elk.orthogonal"),"Orthogonal"),`Orthogonal methods that follow the "topology-shape-metrics" approach by Batini, Nardelli and Tamassia '86. The first phase determines the topology of the drawing by applying a planarization technique, which results in a planar representation of the graph. The orthogonal shape is computed in the second phase, which aims at minimizing the number of edge bends, and is called orthogonalization. The third phase leads to concrete coordinates for nodes and edge bend points by applying a compaction method, thus defining the metrics.`))),Cj(n,new N4(Ej(h9(a9(new f0,$o),"Force"),"Layout algorithms that follow physical analogies by simulating a system of attractive and repulsive forces. The first successful method of this kind was proposed by Eades in 1984."))),Cj(n,new N4(Ej(h9(a9(new f0,"org.eclipse.elk.circle"),"Circle"),"Circular layout algorithms emphasize cycles or biconnected components of a graph by arranging them in circles. This is useful if a drawing is desired where such components are clearly grouped, or where cycles are shown as prominent OPTIONS of the graph."))),Cj(n,new N4(Ej(h9(a9(new f0,YQe),"Tree"),"Specialized layout methods for trees, i.e. acyclic graphs. The regular structure of graphs that have no undirected cycles can be emphasized using an algorithm of this type."))),Cj(n,new N4(Ej(h9(a9(new f0,"org.eclipse.elk.planar"),"Planar"),"Algorithms that require a planar or upward planar graph. Most of these algorithms are theoretically interesting, but not practically usable."))),Cj(n,new N4(Ej(h9(a9(new f0,Gl),"Radial"),"Radial layout algorithms usually position the nodes of the graph on concentric circles."))),JXe((new RU,n)),rKe((new CT,n)),bXe((new BU,n))};var zy,yfn,p9e,$7,kfn,jfn,m9e,Tm,Cm,Efn,ND,v9e,DD,Mg,y9e,dce,bce,k9e,j9e,E9e,Sfn,S9e,xfn,Xv,x9e,Afn,ID,gce,_D,wce,Mfn,A9e,Tfn,M9e,Kv,T9e,R7,C9e,O9e,N9e,Vv,D9e,Tg,I9e,Om,Yv,_9e,ab,L9e,tG,LD,o1,P9e,Cfn,$9e,Ofn,Nfn,R9e,B9e,pce,mce,vce,yce,z9e,Ps,Kx,F9e,kce,jce,Nm,J9e,H9e,Qv,G9e,Fy,PD,Ece,Dm,Dfn,Sce,Ifn,_fn,Lfn,Pfn,q9e,U9e,Jy,X9e,iG,K9e,V9e,Vd,$fn,Y9e,Q9e,W9e,B7,Im,z7,Hy,Rfn,Bfn,rG,zfn,cG,Ffn,Jfn,Hfn,Gfn;v(Oo,"CoreOptions",689),m(86,23,{3:1,35:1,23:1,86:1},bC);var Za,Zc,ru,eh,Vl,Vx=yt(Oo,"Direction",86,Ct,J6n,t3n),qfn;m(278,23,{3:1,35:1,23:1,278:1},y$);var uG,$D,Z9e,e8e,n8e=yt(Oo,"EdgeCoords",278,Ct,a6n,i3n),Ufn;m(279,23,{3:1,35:1,23:1,279:1},wK);var F7,_m,J7,t8e=yt(Oo,"EdgeLabelPlacement",279,Ct,syn,r3n),Xfn;m(222,23,{3:1,35:1,23:1,222:1},k$);var H7,RD,Gy,xce,Ace=yt(Oo,"EdgeRouting",222,Ct,h6n,n3n),Kfn;m(327,23,{3:1,35:1,23:1,327:1},zj);var i8e,r8e,c8e,u8e,Mce,o8e,s8e=yt(Oo,"EdgeType",327,Ct,D9n,h3n),Vfn;m(973,1,qa,RU),s.tf=function(n){JXe(n)};var l8e,f8e,a8e,h8e,Yfn,d8e,Yx;v(Oo,"FixedLayouterOptions",973),m(974,1,{},qM),s.uf=function(){var n;return n=new rw,n},s.vf=function(n){},v(Oo,"FixedLayouterOptions/FixedFactory",974),m(347,23,{3:1,35:1,23:1,347:1},pK);var Yd,oG,Qx,b8e=yt(Oo,"HierarchyHandling",347,Ct,lyn,d3n),Qfn,KBn=Hi(Oo,"ITopdownSizeApproximator");m(292,23,{3:1,35:1,23:1,292:1},j$);var s1,hb,BD,zD,Wfn=yt(Oo,"LabelSide",292,Ct,d6n,a3n),Zfn;m(96,23,{3:1,35:1,23:1,96:1},T3);var W1,Qf,wf,Wf,ml,Zf,pf,l1,ea,$c=yt(Oo,"NodeLabelPlacement",96,Ct,I8n,o3n),ean;m(257,23,{3:1,35:1,23:1,257:1},gC);var g8e,Wx,db,w8e,FD,Zx=yt(Oo,"PortAlignment",257,Ct,e9n,s3n),nan;m(102,23,{3:1,35:1,23:1,102:1},Fj);var Cg,to,f1,G7,nh,bb,p8e=yt(Oo,"PortConstraints",102,Ct,N9n,l3n),tan;m(280,23,{3:1,35:1,23:1,280:1},Jj);var eA,nA,Z1,JD,gb,qy,sG=yt(Oo,"PortLabelPlacement",280,Ct,O9n,f3n),ian;m(64,23,{3:1,35:1,23:1,64:1},wC);var et,Xn,Yl,Ql,Wo,zo,th,na,ks,hs,mo,js,Zo,es,ta,vl,yl,mf,bt,ku,Kn,xc=yt(Oo,"PortSide",64,Ct,H6n,p3n),ran;m(977,1,qa,BU),s.tf=function(n){bXe(n)};var can,uan,m8e,oan,san;v(Oo,"RandomLayouterOptions",977),m(978,1,{},UM),s.uf=function(){var n;return n=new YM,n},s.vf=function(n){},v(Oo,"RandomLayouterOptions/RandomFactory",978),m(300,23,{3:1,35:1,23:1,300:1},mK);var HD,Tce,v8e,y8e=yt(Oo,"ShapeCoords",300,Ct,fyn,m3n),lan;m(380,23,{3:1,35:1,23:1,380:1},E$);var Lm,GD,qD,Og,tA=yt(Oo,"SizeConstraint",380,Ct,g6n,v3n),fan;m(266,23,{3:1,35:1,23:1,266:1},C3);var UD,lG,q7,Cce,XD,iA,fG,aG,hG,k8e=yt(Oo,"SizeOptions",266,Ct,z8n,g3n),aan;m(281,23,{3:1,35:1,23:1,281:1},vK);var Pm,j8e,dG,E8e=yt(Oo,"TopdownNodeTypes",281,Ct,ayn,w3n),han;m(288,23,zF);var S8e,Oce,x8e,A8e,KD=yt(Oo,"TopdownSizeApproximator",288,Ct,b6n,b3n);m(969,288,zF,gDe),s.Sg=function(n){return WJe(n)},yt(Oo,"TopdownSizeApproximator/1",969,KD,null,null),m(970,288,zF,QDe),s.Sg=function(n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn;for(t=u(ye(n,(Xt(),Dm)),144),Ie=(v0(),A=new pj,A),VO(Ie,n),cn=new pt,o=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));o.e!=o.i.gc();)r=u(ft(o),26),V=(S=new pj,S),Iz(V,Ie),VO(V,r),Tn=WJe(r),ww(V,k.Math.max(r.g,Tn.a),k.Math.max(r.f,Tn.b)),Ko(cn.f,r,V);for(c=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));c.e!=c.i.gc();)for(r=u(ft(c),26),p=new ut((!r.e&&(r.e=new Nn(pr,r,7,4)),r.e));p.e!=p.i.gc();)b=u(ft(p),85),be=u(du(Xc(cn.f,r)),26),fe=u(Bn(cn,K((!b.c&&(b.c=new Nn(vt,b,5,8)),b.c),0)),26),te=(y=new w3,y),Et((!te.b&&(te.b=new Nn(vt,te,4,7)),te.b),be),Et((!te.c&&(te.c=new Nn(vt,te,5,8)),te.c),fe),Dz(te,Bi(be)),VO(te,b);I=u(JC(t.f),214);try{I.kf(Ie,new a0),Wfe(t.f,I)}catch(Dn){throw Dn=sr(Dn),X(Dn,101)?(O=Dn,$(O)):$(Dn)}return da(Ie,Cm)||da(Ie,Tm)||oZ(Ie),h=ne(re(ye(Ie,Cm))),f=ne(re(ye(Ie,Tm))),l=h/f,i=ne(re(ye(Ie,Im)))*k.Math.sqrt((!Ie.a&&(Ie.a=new pe(Jt,Ie,10,11)),Ie.a).i),tn=u(ye(Ie,o1),104),q=tn.b+tn.c+1,R=tn.d+tn.a+1,new je(k.Math.max(q,i),k.Math.max(R,i/l))},yt(Oo,"TopdownSizeApproximator/2",970,KD,null,null),m(971,288,zF,E_e),s.Sg=function(n){var t,i,r,c,o,l;return i=ne(re(ye(n,(Xt(),Im)))),t=i/ne(re(ye(n,B7))),r=z_n(n),o=u(ye(n,o1),104),c=ne(re(_e(Vd))),Bi(n)&&(c=ne(re(ye(Bi(n),Vd)))),l=A1(new je(i,t),r),gi(l,new je(-(o.b+o.c)-c,-(o.d+o.a)-c))},yt(Oo,"TopdownSizeApproximator/3",971,KD,null,null),m(972,288,zF,WDe),s.Sg=function(n){var t,i,r,c,o,l,f,h,b,p;for(l=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));l.e!=l.i.gc();)o=u(ft(l),26),ye(o,(Xt(),cG))!=null&&(!o.a&&(o.a=new pe(Jt,o,10,11)),!!o.a)&&(!o.a&&(o.a=new pe(Jt,o,10,11)),o.a).i>0?(i=u(ye(o,cG),521),p=i.Sg(o),b=u(ye(o,o1),104),ww(o,k.Math.max(o.g,p.a+b.b+b.c),k.Math.max(o.f,p.b+b.d+b.a))):(!o.a&&(o.a=new pe(Jt,o,10,11)),o.a).i!=0&&ww(o,ne(re(ye(o,Im))),ne(re(ye(o,Im)))/ne(re(ye(o,B7))));t=u(ye(n,(Xt(),Dm)),144),h=u(JC(t.f),214);try{h.kf(n,new a0),Wfe(t.f,h)}catch(y){throw y=sr(y),X(y,101)?(f=y,$(f)):$(y)}return ji(n,zy,i7),dPe(n),oZ(n),c=ne(re(ye(n,Cm))),r=ne(re(ye(n,Tm))),new je(c,r)},yt(Oo,"TopdownSizeApproximator/4",972,KD,null,null);var dan;m(345,1,{852:1},i4),s.Tg=function(n,t){return aGe(this,n,t)},s.Ug=function(){$Ge(this)},s.Vg=function(){return this.q},s.Wg=function(){return this.f?OR(this.f):null},s.Xg=function(){return OR(this.a)},s.Yg=function(){return this.p},s.Zg=function(){return!1},s.$g=function(){return this.n},s._g=function(){return this.p!=null&&!this.b},s.ah=function(n){var t;this.n&&(t=n,Te(this.f,t))},s.bh=function(n,t){var i,r;this.n&&n&&Cyn(this,(i=new hIe,r=zW(i,n),x$n(i),r),(PB(),Dce))},s.dh=function(n){var t;return this.b?null:(t=w8n(this,this.g),Vt(this.a,t),t.i=this,this.d=n,t)},s.eh=function(n){n>0&&!this.b&&Vhe(this,n)},s.b=!1,s.c=0,s.d=-1,s.e=null,s.f=null,s.g=-1,s.j=!1,s.k=!1,s.n=!1,s.o=0,s.q=0,s.r=0,v(Ru,"BasicProgressMonitor",345),m(706,214,Qw,hL),s.kf=function(n,t){kKe(n,t)},v(Ru,"BoxLayoutProvider",706),m(965,1,Yt,ZEe),s.Le=function(n,t){return xNn(this,u(n,26),u(t,26))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},s.a=!1,v(Ru,"BoxLayoutProvider/1",965),m(167,1,{167:1},dB,OOe),s.Ib=function(){return this.c?Jbe(this.c):Fa(this.b)},v(Ru,"BoxLayoutProvider/Group",167),m(326,23,{3:1,35:1,23:1,326:1},S$);var M8e,T8e,C8e,Nce,O8e=yt(Ru,"BoxLayoutProvider/PackingMode",326,Ct,w6n,y3n),ban;m(966,1,Yt,XM),s.Le=function(n,t){return L5n(u(n,167),u(t,167))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ru,"BoxLayoutProvider/lambda$0$Type",966),m(967,1,Yt,Bk),s.Le=function(n,t){return x5n(u(n,167),u(t,167))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ru,"BoxLayoutProvider/lambda$1$Type",967),m(968,1,Yt,KM),s.Le=function(n,t){return A5n(u(n,167),u(t,167))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ru,"BoxLayoutProvider/lambda$2$Type",968),m(1338,1,{829:1},dL),s.Lg=function(n,t){return WP(),!X(t,174)||DAe((J4(),u(n,174)),t)},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$0$Type",1338),m(1339,1,it,eSe),s.Ad=function(n){Tkn(this.a,u(n,147))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$1$Type",1339),m(1340,1,it,VM),s.Ad=function(n){u(n,105),WP()},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$2$Type",1340),m(1344,1,it,nSe),s.Ad=function(n){e7n(this.a,u(n,105))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$3$Type",1344),m(1342,1,Ft,MTe),s.Mb=function(n){return fkn(this.a,this.b,u(n,147))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$4$Type",1342),m(1341,1,Ft,TTe),s.Mb=function(n){return Spn(this.a,this.b,u(n,829))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$5$Type",1341),m(1343,1,it,CTe),s.Ad=function(n){Evn(this.a,this.b,u(n,147))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$6$Type",1343),m(930,1,{},bL),s.Kb=function(n){return SCe(n)},s.Fb=function(n){return this===n},v(Ru,"ElkUtil/lambda$0$Type",930),m(931,1,it,OTe),s.Ad=function(n){CCn(this.a,this.b,u(n,85))},s.a=0,s.b=0,v(Ru,"ElkUtil/lambda$1$Type",931),m(932,1,it,NTe),s.Ad=function(n){xbn(this.a,this.b,u(n,170))},s.a=0,s.b=0,v(Ru,"ElkUtil/lambda$2$Type",932),m(933,1,it,DTe),s.Ad=function(n){vwn(this.a,this.b,u(n,157))},s.a=0,s.b=0,v(Ru,"ElkUtil/lambda$3$Type",933),m(934,1,it,tSe),s.Ad=function(n){U3n(this.a,u(n,372))},v(Ru,"ElkUtil/lambda$4$Type",934),m(331,1,{35:1,331:1},ebn),s.Dd=function(n){return Gwn(this,u(n,242))},s.Fb=function(n){var t;return X(n,331)?(t=u(n,331),this.a==t.a):!1},s.Hb=function(){return sc(this.a)},s.Ib=function(){return this.a+" (exclusive)"},s.a=0,v(Ru,"ExclusiveBounds/ExclusiveLowerBound",331),m(1088,214,Qw,rw),s.kf=function(n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn;for(t.Tg("Fixed Layout",1),o=u(ye(n,(Xt(),j9e)),222),y=0,S=0,V=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));V.e!=V.i.gc();){for(R=u(ft(V),26),tn=u(ye(R,($B(),Yx)),8),tn&&(Dl(R,tn.a,tn.b),u(ye(R,f8e),182).Gc((Vs(),Lm))&&(A=u(ye(R,h8e),8),A.a>0&&A.b>0&&Xw(R,A.a,A.b,!0,!0))),y=k.Math.max(y,R.i+R.g),S=k.Math.max(S,R.j+R.f),b=new ut((!R.n&&(R.n=new pe(ju,R,1,7)),R.n));b.e!=b.i.gc();)f=u(ft(b),157),tn=u(ye(f,Yx),8),tn&&Dl(f,tn.a,tn.b),y=k.Math.max(y,R.i+f.i+f.g),S=k.Math.max(S,R.j+f.j+f.f);for(fe=new ut((!R.c&&(R.c=new pe($s,R,9,9)),R.c));fe.e!=fe.i.gc();)for(be=u(ft(fe),125),tn=u(ye(be,Yx),8),tn&&Dl(be,tn.a,tn.b),Ie=R.i+be.i,cn=R.j+be.j,y=k.Math.max(y,Ie+be.g),S=k.Math.max(S,cn+be.f),h=new ut((!be.n&&(be.n=new pe(ju,be,1,7)),be.n));h.e!=h.i.gc();)f=u(ft(h),157),tn=u(ye(f,Yx),8),tn&&Dl(f,tn.a,tn.b),y=k.Math.max(y,Ie+f.i+f.g),S=k.Math.max(S,cn+f.j+f.f);for(c=new qn(Vn(H0(R).a.Jc(),new ee));ht(c);)i=u(tt(c),85),p=IVe(i),y=k.Math.max(y,p.a),S=k.Math.max(S,p.b);for(r=new qn(Vn(AW(R).a.Jc(),new ee));ht(r);)i=u(tt(r),85),Bi(hW(i))!=n&&(p=IVe(i),y=k.Math.max(y,p.a),S=k.Math.max(S,p.b))}if(o==(z1(),H7))for(q=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));q.e!=q.i.gc();)for(R=u(ft(q),26),r=new qn(Vn(H0(R).a.Jc(),new ee));ht(r);)i=u(tt(r),85),l=x_n(i),l.b==0?ji(i,Kv,null):ji(i,Kv,l);Re($e(ye(n,($B(),a8e))))||(te=u(ye(n,Yfn),104),I=y+te.b+te.c,O=S+te.d+te.a,Xw(n,I,O,!0,!0)),t.Ug()},v(Ru,"FixedLayoutProvider",1088),m(379,150,{3:1,414:1,379:1,105:1,150:1},R6,kRe),s.ag=function(n){var t,i,r,c,o,l,f,h,b;if(n)try{for(h=K2(n,";,;"),o=h,l=0,f=o.length;l>16&yr|t^r<<16},s.Jc=function(){return new iSe(this)},s.Ib=function(){return this.a==null&&this.b==null?"pair(null,null)":this.a==null?"pair(null,"+su(this.b)+")":this.b==null?"pair("+su(this.a)+",null)":"pair("+su(this.a)+","+su(this.b)+")"},v(Ru,"Pair",49),m(979,1,Fr,iSe),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return!this.c&&(!this.b&&this.a.a!=null||this.a.b!=null)},s.Pb=function(){if(!this.c&&!this.b&&this.a.a!=null)return this.b=!0,this.a.a;if(!this.c&&this.a.b!=null)return this.c=!0,this.a.b;throw $(new au)},s.Qb=function(){throw this.c&&this.a.b!=null?this.a.b=null:this.b&&this.a.a!=null&&(this.a.a=null),$(new is)},s.b=!1,s.c=!1,v(Ru,"Pair/1",979),m(1078,214,Qw,YM),s.kf=function(n,t){var i,r,c,o,l;if(t.Tg("Random Layout",1),(!n.a&&(n.a=new pe(Jt,n,10,11)),n.a).i==0){t.Ug();return}o=u(ye(n,(bde(),oan)),15),o&&o.a!=0?c=new XR(o.a):c=new vQ,i=VT(re(ye(n,can))),l=VT(re(ye(n,san))),r=u(ye(n,uan),104),U$n(n,c,i,l,r),t.Ug()},v(Ru,"RandomLayoutProvider",1078),m(240,1,{240:1},ZK),s.Fb=function(n){return Ku(this.a,u(n,240).a)&&Ku(this.b,u(n,240).b)&&Ku(this.c,u(n,240).c)},s.Hb=function(){return RB(z(B(Ar,1),On,1,5,[this.a,this.b,this.c]))},s.Ib=function(){return"("+this.a+Co+this.b+Co+this.c+")"},v(Ru,"Triple",240);var man;m(550,1,{}),s.Jf=function(){return new je(this.f.i,this.f.j)},s.mf=function(n){return y_e(n,(Xt(),Ps))?ye(this.f,van):ye(this.f,n)},s.Kf=function(){return new je(this.f.g,this.f.f)},s.Lf=function(){return this.g},s.nf=function(n){return da(this.f,n)},s.Mf=function(n){Os(this.f,n.a),Ns(this.f,n.b)},s.Nf=function(n){Iw(this.f,n.a),Dw(this.f,n.b)},s.Of=function(n){this.g=n},s.g=0;var van;v(IS,"ElkGraphAdapters/AbstractElkGraphElementAdapter",550),m(552,1,{837:1},CP),s.Pf=function(){var n,t;if(!this.b)for(this.b=zR(OV(this.a).i),t=new ut(OV(this.a));t.e!=t.i.gc();)n=u(ft(t),157),Te(this.b,new AX(n));return this.b},s.b=null,v(IS,"ElkGraphAdapters/ElkEdgeAdapter",552),m(260,550,{},w0),s.Qf=function(){return mHe(this)},s.a=null,v(IS,"ElkGraphAdapters/ElkGraphAdapter",260),m(630,550,{187:1},AX),v(IS,"ElkGraphAdapters/ElkLabelAdapter",630),m(551,550,{685:1},H$),s.Pf=function(){return ZSn(this)},s.Tf=function(){var n;return n=u(ye(this.f,(Xt(),R7)),140),!n&&(n=new wj),n},s.Vf=function(){return exn(this)},s.Xf=function(n){var t;t=new YK(n),ji(this.f,(Xt(),R7),t)},s.Yf=function(n){ji(this.f,(Xt(),o1),new Yle(n))},s.Rf=function(){return this.d},s.Sf=function(){var n,t;if(!this.a)for(this.a=new Ce,t=new qn(Vn(AW(u(this.f,26)).a.Jc(),new ee));ht(t);)n=u(tt(t),85),Te(this.a,new CP(n));return this.a},s.Uf=function(){var n,t;if(!this.c)for(this.c=new Ce,t=new qn(Vn(H0(u(this.f,26)).a.Jc(),new ee));ht(t);)n=u(tt(t),85),Te(this.c,new CP(n));return this.c},s.Wf=function(){return TR(u(this.f,26)).i!=0||Re($e(u(this.f,26).mf((Xt(),ID))))},s.Zf=function(){Q9n(this,(_b(),man))},s.a=null,s.b=null,s.c=null,s.d=null,s.e=null,v(IS,"ElkGraphAdapters/ElkNodeAdapter",551),m(1249,550,{836:1},rSe),s.Pf=function(){return oxn(this)},s.Sf=function(){var n,t;if(!this.a)for(this.a=Fh(u(this.f,125).gh().i),t=new ut(u(this.f,125).gh());t.e!=t.i.gc();)n=u(ft(t),85),Te(this.a,new CP(n));return this.a},s.Uf=function(){var n,t;if(!this.c)for(this.c=Fh(u(this.f,125).hh().i),t=new ut(u(this.f,125).hh());t.e!=t.i.gc();)n=u(ft(t),85),Te(this.c,new CP(n));return this.c},s.$f=function(){return u(u(this.f,125).mf((Xt(),Qv)),64)},s._f=function(){var n,t,i,r,c,o,l,f;for(r=Ia(u(this.f,125)),i=new ut(u(this.f,125).hh());i.e!=i.i.gc();)for(n=u(ft(i),85),f=new ut((!n.c&&(n.c=new Nn(vt,n,5,8)),n.c));f.e!=f.i.gc();){if(l=u(ft(f),84),C2(iu(l),r))return!0;if(iu(l)==r&&Re($e(ye(n,(Xt(),gce)))))return!0}for(t=new ut(u(this.f,125).gh());t.e!=t.i.gc();)for(n=u(ft(t),85),o=new ut((!n.b&&(n.b=new Nn(vt,n,4,7)),n.b));o.e!=o.i.gc();)if(c=u(ft(o),84),C2(iu(c),r))return!0;return!1},s.a=null,s.b=null,s.c=null,v(IS,"ElkGraphAdapters/ElkPortAdapter",1249),m(1250,1,Yt,wL),s.Le=function(n,t){return pIn(u(n,125),u(t,125))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(IS,"ElkGraphAdapters/PortComparator",1250);var wb=Hi(ql,"EObject"),U7=Hi(yv,FWe),kl=Hi(yv,JWe),VD=Hi(yv,HWe),YD=Hi(yv,"ElkShape"),vt=Hi(yv,GWe),pr=Hi(yv,P2e),Pi=Hi(yv,qWe),QD=Hi(ql,UWe),rA=Hi(ql,"EFactory"),yan,Ice=Hi(ql,XWe),xa=Hi(ql,"EPackage"),Pr,kan,jan,_8e,bG,Ean,L8e,P8e,$8e,a1,San,xan,ju=Hi(yv,$2e),Jt=Hi(yv,R2e),$s=Hi(yv,B2e);m(93,1,KWe),s.qh=function(){return this.rh(),null},s.rh=function(){return null},s.sh=function(){return this.rh(),!1},s.th=function(){return!1},s.uh=function(n){ai(this,n)},v(wy,"BasicNotifierImpl",93),m(100,93,WWe),s.Vh=function(){return Fs(this)},s.vh=function(n,t){return n},s.wh=function(){throw $(new _t)},s.xh=function(n){var t;return t=Oc(u(An(this.Ah(),this.Ch()),19)),this.Mh().Qh(this,t.n,t.f,n)},s.yh=function(n,t){throw $(new _t)},s.zh=function(n,t,i){return dl(this,n,t,i)},s.Ah=function(){var n;return this.wh()&&(n=this.wh().Lk(),n)?n:this.fi()},s.Bh=function(){return SW(this)},s.Ch=function(){throw $(new _t)},s.Dh=function(){var n,t;return t=this.Xh().Mk(),!t&&this.wh().Rk(t=(Nj(),n=dae(vh(this.Ah())),n==null?Fce:new jC(this,n))),t},s.Eh=function(n,t){return n},s.Fh=function(n){var t;return t=n.nk(),t?n.Jj():zi(this.Ah(),n)},s.Gh=function(){var n;return n=this.wh(),n?n.Ok():null},s.Hh=function(){return this.wh()?this.wh().Lk():null},s.Ih=function(n,t,i){return uz(this,n,t,i)},s.Jh=function(n){return F9(this,n)},s.Kh=function(n,t){return fY(this,n,t)},s.Lh=function(){var n;return n=this.wh(),!!n&&n.Pk()},s.Mh=function(){throw $(new _t)},s.Nh=function(){return nz(this)},s.Oh=function(n,t,i,r){return K4(this,n,t,r)},s.Ph=function(n,t,i){var r;return r=u(An(this.Ah(),t),69),r.uk().xk(this,this.ei(),t-this.gi(),n,i)},s.Qh=function(n,t,i,r){return IR(this,n,t,r)},s.Rh=function(n,t,i){var r;return r=u(An(this.Ah(),t),69),r.uk().yk(this,this.ei(),t-this.gi(),n,i)},s.Sh=function(){return!!this.wh()&&!!this.wh().Nk()},s.Th=function(n){return _Q(this,n)},s.Uh=function(n){return L_e(this,n)},s.Wh=function(n){return wVe(this,n)},s.Xh=function(){throw $(new _t)},s.Yh=function(){return this.wh()?this.wh().Nk():null},s.Zh=function(){return nz(this)},s.$h=function(n,t){vW(this,n,t)},s._h=function(n){this.Xh().Qk(n)},s.ai=function(n){this.Xh().Tk(n)},s.bi=function(n){this.Xh().Sk(n)},s.ci=function(n,t){var i,r,c,o;return o=this.Gh(),o&&n&&(t=vc(o.Cl(),this,t),o.Gl(this)),r=this.Mh(),r&&(($W(this,this.Mh(),this.Ch()).Bb&Ec)!=0?(c=r.Nh(),c&&(n?!o&&c.Gl(this):c.Fl(this))):(t=(i=this.Ch(),i>=0?this.xh(t):this.Mh().Qh(this,-1-i,null,t)),t=this.zh(null,-1,t))),this.ai(n),t},s.di=function(n){var t,i,r,c,o,l,f,h;if(i=this.Ah(),o=zi(i,n),t=this.gi(),o>=t)return u(n,69).uk().Bk(this,this.ei(),o-t);if(o<=-1)if(l=av((ls(),nc),i,n),l){if(Cc(),u(l,69).vk()||(l=D4(Vc(nc,l))),c=(r=this.Fh(l),u(r>=0?this.Ih(r,!0,!0):Gw(this,l,!0),163)),h=l.Gk(),h>1||h==-1)return u(u(c,219).Ql(n,!1),77)}else throw $(new Gn(W0+n.ve()+wne));else if(n.Hk())return r=this.Fh(n),u(r>=0?this.Ih(r,!1,!0):Gw(this,n,!1),77);return f=new KTe(this,n),f},s.ei=function(){return khe(this)},s.fi=function(){return(x0(),Rn).S},s.gi=function(){return dt(this.fi())},s.hi=function(n){wW(this,n)},s.Ib=function(){return Jf(this)},v(Fn,"BasicEObjectImpl",100);var Aan;m(117,100,{109:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1}),s.ii=function(n){var t;return t=jhe(this),t[n]},s.ji=function(n,t){var i;i=jhe(this),tr(i,n,t)},s.ki=function(n){var t;t=jhe(this),tr(t,n,null)},s.qh=function(){return u(Un(this,4),129)},s.rh=function(){throw $(new _t)},s.sh=function(){return(this.Db&4)!=0},s.wh=function(){throw $(new _t)},s.li=function(n){U4(this,2,n)},s.yh=function(n,t){this.Db=t<<16|this.Db&255,this.li(n)},s.Ah=function(){return Go(this)},s.Ch=function(){return this.Db>>16},s.Dh=function(){var n,t;return Nj(),t=dae(vh((n=u(Un(this,16),29),n||this.fi()))),t==null?Fce:new jC(this,t)},s.th=function(){return(this.Db&1)==0},s.Gh=function(){return u(Un(this,128),1996)},s.Hh=function(){return u(Un(this,16),29)},s.Lh=function(){return(this.Db&32)!=0},s.Mh=function(){return u(Un(this,2),52)},s.Sh=function(){return(this.Db&64)!=0},s.Xh=function(){throw $(new _t)},s.Yh=function(){return u(Un(this,64),290)},s._h=function(n){U4(this,16,n)},s.ai=function(n){U4(this,128,n)},s.bi=function(n){U4(this,64,n)},s.ei=function(){return Lo(this)},s.Db=0,v(Fn,"MinimalEObjectImpl",117),m(118,117,{109:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.li=function(n){this.Cb=n},s.Mh=function(){return this.Cb},v(Fn,"MinimalEObjectImpl/Container",118),m(2045,118,{109:1,343:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return $de(this,n,t,i)},s.Rh=function(n,t,i){return A0e(this,n,t,i)},s.Th=function(n){return Oae(this,n)},s.$h=function(n,t){E1e(this,n,t)},s.fi=function(){return Gu(),xan},s.hi=function(n){f1e(this,n)},s.lf=function(){return RJe(this)},s.fh=function(){return!this.o&&(this.o=new os((Gu(),a1),Qd,this,0)),this.o},s.mf=function(n){return ye(this,n)},s.nf=function(n){return da(this,n)},s.of=function(n,t){return ji(this,n,t)},v(hg,"EMapPropertyHolderImpl",2045),m(559,118,{109:1,372:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},Fk),s.Ih=function(n,t,i){switch(n){case 0:return this.a;case 1:return this.b}return uz(this,n,t,i)},s.Th=function(n){switch(n){case 0:return this.a!=0;case 1:return this.b!=0}return _Q(this,n)},s.$h=function(n,t){switch(n){case 0:bB(this,ne(re(t)));return;case 1:gB(this,ne(re(t)));return}vW(this,n,t)},s.fi=function(){return Gu(),kan},s.hi=function(n){switch(n){case 0:bB(this,0);return;case 1:gB(this,0);return}wW(this,n)},s.Ib=function(){var n;return(this.Db&64)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (x: ",S3(n,this.a),n.a+=", y: ",S3(n,this.b),n.a+=")",n.a)},s.a=0,s.b=0,v(hg,"ElkBendPointImpl",559),m(727,2045,{109:1,343:1,174:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return J1e(this,n,t,i)},s.Ph=function(n,t,i){return sW(this,n,t,i)},s.Rh=function(n,t,i){return XY(this,n,t,i)},s.Th=function(n){return r1e(this,n)},s.$h=function(n,t){t0e(this,n,t)},s.fi=function(){return Gu(),Ean},s.hi=function(n){R1e(this,n)},s.ih=function(){return this.k},s.jh=function(){return OV(this)},s.Ib=function(){return mQ(this)},s.k=null,v(hg,"ElkGraphElementImpl",727),m(728,727,{109:1,343:1,174:1,276:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return nde(this,n,t,i)},s.Th=function(n){return sde(this,n)},s.$h=function(n,t){i0e(this,n,t)},s.fi=function(){return Gu(),San},s.hi=function(n){dde(this,n)},s.kh=function(){return this.f},s.lh=function(){return this.g},s.mh=function(){return this.i},s.nh=function(){return this.j},s.oh=function(n,t){ww(this,n,t)},s.ph=function(n,t){Dl(this,n,t)},s.Ib=function(){return bW(this)},s.f=0,s.g=0,s.i=0,s.j=0,v(hg,"ElkShapeImpl",728),m(729,728,{109:1,343:1,84:1,174:1,276:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return Nde(this,n,t,i)},s.Ph=function(n,t,i){return Yde(this,n,t,i)},s.Rh=function(n,t,i){return Qde(this,n,t,i)},s.Th=function(n){return v1e(this,n)},s.$h=function(n,t){fbe(this,n,t)},s.fi=function(){return Gu(),jan},s.hi=function(n){Ade(this,n)},s.gh=function(){return!this.d&&(this.d=new Nn(pr,this,8,5)),this.d},s.hh=function(){return!this.e&&(this.e=new Nn(pr,this,7,4)),this.e},v(hg,"ElkConnectableShapeImpl",729),m(271,727,{109:1,343:1,85:1,174:1,271:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},w3),s.xh=function(n){return Ude(this,n)},s.Ih=function(n,t,i){switch(n){case 3:return j2(this);case 4:return!this.b&&(this.b=new Nn(vt,this,4,7)),this.b;case 5:return!this.c&&(this.c=new Nn(vt,this,5,8)),this.c;case 6:return!this.a&&(this.a=new pe(Pi,this,6,6)),this.a;case 7:return Pn(),!this.b&&(this.b=new Nn(vt,this,4,7)),!(this.b.i<=1&&(!this.c&&(this.c=new Nn(vt,this,5,8)),this.c.i<=1));case 8:return Pn(),!!ZE(this);case 9:return Pn(),!!Hw(this);case 10:return Pn(),!this.b&&(this.b=new Nn(vt,this,4,7)),this.b.i!=0&&(!this.c&&(this.c=new Nn(vt,this,5,8)),this.c.i!=0)}return J1e(this,n,t,i)},s.Ph=function(n,t,i){var r;switch(t){case 3:return this.Cb&&(i=(r=this.Db>>16,r>=0?Ude(this,i):this.Cb.Qh(this,-1-r,null,i))),Tle(this,u(n,26),i);case 4:return!this.b&&(this.b=new Nn(vt,this,4,7)),To(this.b,n,i);case 5:return!this.c&&(this.c=new Nn(vt,this,5,8)),To(this.c,n,i);case 6:return!this.a&&(this.a=new pe(Pi,this,6,6)),To(this.a,n,i)}return sW(this,n,t,i)},s.Rh=function(n,t,i){switch(t){case 3:return Tle(this,null,i);case 4:return!this.b&&(this.b=new Nn(vt,this,4,7)),vc(this.b,n,i);case 5:return!this.c&&(this.c=new Nn(vt,this,5,8)),vc(this.c,n,i);case 6:return!this.a&&(this.a=new pe(Pi,this,6,6)),vc(this.a,n,i)}return XY(this,n,t,i)},s.Th=function(n){switch(n){case 3:return!!j2(this);case 4:return!!this.b&&this.b.i!=0;case 5:return!!this.c&&this.c.i!=0;case 6:return!!this.a&&this.a.i!=0;case 7:return!this.b&&(this.b=new Nn(vt,this,4,7)),!(this.b.i<=1&&(!this.c&&(this.c=new Nn(vt,this,5,8)),this.c.i<=1));case 8:return ZE(this);case 9:return Hw(this);case 10:return!this.b&&(this.b=new Nn(vt,this,4,7)),this.b.i!=0&&(!this.c&&(this.c=new Nn(vt,this,5,8)),this.c.i!=0)}return r1e(this,n)},s.$h=function(n,t){switch(n){case 3:Dz(this,u(t,26));return;case 4:!this.b&&(this.b=new Nn(vt,this,4,7)),kt(this.b),!this.b&&(this.b=new Nn(vt,this,4,7)),er(this.b,u(t,18));return;case 5:!this.c&&(this.c=new Nn(vt,this,5,8)),kt(this.c),!this.c&&(this.c=new Nn(vt,this,5,8)),er(this.c,u(t,18));return;case 6:!this.a&&(this.a=new pe(Pi,this,6,6)),kt(this.a),!this.a&&(this.a=new pe(Pi,this,6,6)),er(this.a,u(t,18));return}t0e(this,n,t)},s.fi=function(){return Gu(),_8e},s.hi=function(n){switch(n){case 3:Dz(this,null);return;case 4:!this.b&&(this.b=new Nn(vt,this,4,7)),kt(this.b);return;case 5:!this.c&&(this.c=new Nn(vt,this,5,8)),kt(this.c);return;case 6:!this.a&&(this.a=new pe(Pi,this,6,6)),kt(this.a);return}R1e(this,n)},s.Ib=function(){return $Ke(this)},v(hg,"ElkEdgeImpl",271),m(443,2045,{109:1,343:1,170:1,443:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},yo),s.xh=function(n){return Jde(this,n)},s.Ih=function(n,t,i){switch(n){case 1:return this.j;case 2:return this.k;case 3:return this.b;case 4:return this.c;case 5:return!this.a&&(this.a=new mr(kl,this,5)),this.a;case 6:return __e(this);case 7:return t?BQ(this):this.i;case 8:return t?RQ(this):this.f;case 9:return!this.g&&(this.g=new Nn(Pi,this,9,10)),this.g;case 10:return!this.e&&(this.e=new Nn(Pi,this,10,9)),this.e;case 11:return this.d}return $de(this,n,t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 6:return this.Cb&&(i=(c=this.Db>>16,c>=0?Jde(this,i):this.Cb.Qh(this,-1-c,null,i))),Cle(this,u(n,85),i);case 9:return!this.g&&(this.g=new Nn(Pi,this,9,10)),To(this.g,n,i);case 10:return!this.e&&(this.e=new Nn(Pi,this,10,9)),To(this.e,n,i)}return o=u(An((r=u(Un(this,16),29),r||(Gu(),bG)),t),69),o.uk().xk(this,Lo(this),t-dt((Gu(),bG)),n,i)},s.Rh=function(n,t,i){switch(t){case 5:return!this.a&&(this.a=new mr(kl,this,5)),vc(this.a,n,i);case 6:return Cle(this,null,i);case 9:return!this.g&&(this.g=new Nn(Pi,this,9,10)),vc(this.g,n,i);case 10:return!this.e&&(this.e=new Nn(Pi,this,10,9)),vc(this.e,n,i)}return A0e(this,n,t,i)},s.Th=function(n){switch(n){case 1:return this.j!=0;case 2:return this.k!=0;case 3:return this.b!=0;case 4:return this.c!=0;case 5:return!!this.a&&this.a.i!=0;case 6:return!!__e(this);case 7:return!!this.i;case 8:return!!this.f;case 9:return!!this.g&&this.g.i!=0;case 10:return!!this.e&&this.e.i!=0;case 11:return this.d!=null}return Oae(this,n)},s.$h=function(n,t){switch(n){case 1:V3(this,ne(re(t)));return;case 2:Y3(this,ne(re(t)));return;case 3:X3(this,ne(re(t)));return;case 4:K3(this,ne(re(t)));return;case 5:!this.a&&(this.a=new mr(kl,this,5)),kt(this.a),!this.a&&(this.a=new mr(kl,this,5)),er(this.a,u(t,18));return;case 6:PUe(this,u(t,85));return;case 7:jB(this,u(t,84));return;case 8:kB(this,u(t,84));return;case 9:!this.g&&(this.g=new Nn(Pi,this,9,10)),kt(this.g),!this.g&&(this.g=new Nn(Pi,this,9,10)),er(this.g,u(t,18));return;case 10:!this.e&&(this.e=new Nn(Pi,this,10,9)),kt(this.e),!this.e&&(this.e=new Nn(Pi,this,10,9)),er(this.e,u(t,18));return;case 11:Xhe(this,Pt(t));return}E1e(this,n,t)},s.fi=function(){return Gu(),bG},s.hi=function(n){switch(n){case 1:V3(this,0);return;case 2:Y3(this,0);return;case 3:X3(this,0);return;case 4:K3(this,0);return;case 5:!this.a&&(this.a=new mr(kl,this,5)),kt(this.a);return;case 6:PUe(this,null);return;case 7:jB(this,null);return;case 8:kB(this,null);return;case 9:!this.g&&(this.g=new Nn(Pi,this,9,10)),kt(this.g);return;case 10:!this.e&&(this.e=new Nn(Pi,this,10,9)),kt(this.e);return;case 11:Xhe(this,null);return}f1e(this,n)},s.Ib=function(){return Xqe(this)},s.b=0,s.c=0,s.d=null,s.j=0,s.k=0,v(hg,"ElkEdgeSectionImpl",443),m(161,118,{109:1,94:1,93:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1}),s.Ih=function(n,t,i){var r;return n==0?(!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab):Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Ph=function(n,t,i){var r,c;return t==0?(!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i)):(c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().xk(this,Lo(this),t-dt(this.fi()),n,i))},s.Rh=function(n,t,i){var r,c;return t==0?(!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i)):(c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,Lo(this),t-dt(this.fi()),n,i))},s.Th=function(n){var t;return n==0?!!this.Ab&&this.Ab.i!=0:Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.Wh=function(n){return Tge(this,n)},s.$h=function(n,t){var i;if(n===0){!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.ai=function(n){U4(this,128,n)},s.fi=function(){return yn(),Gan},s.hi=function(n){var t;if(n===0){!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.mi=function(){this.Bb|=1},s.ni=function(n){return uS(this,n)},s.Bb=0,v(Fn,"EModelElementImpl",161),m(710,161,{109:1,94:1,93:1,469:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1},OT),s.oi=function(n,t){return lVe(this,n,t)},s.pi=function(n){var t,i,r,c,o;if(this.a!=sl(n)||(n.Bb&256)!=0)throw $(new Gn(mne+n.zb+ip));for(r=tu(n);Vu(r.a).i!=0;){if(i=u(cN(r,0,(t=u(K(Vu(r.a),0),87),o=t.c,X(o,88)?u(o,29):(yn(),jf))),29),Fw(i))return c=sl(i).ti().pi(i),u(c,52)._h(n),c;r=tu(i)}return(n.D!=null?n.D:n.B)=="java.util.Map$Entry"?new bDe(n):new bfe(n)},s.qi=function(n,t){return Kw(this,n,t)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.a}return Pl(this,n-dt((yn(),vb)),An((r=u(Un(this,16),29),r||vb),n),t,i)},s.Ph=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 1:return this.a&&(i=u(this.a,52).Qh(this,4,xa,i)),P1e(this,u(n,241),i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),vb)),t),69),c.uk().xk(this,Lo(this),t-dt((yn(),vb)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 1:return P1e(this,null,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),vb)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),vb)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return!!this.a}return Ll(this,n-dt((yn(),vb)),An((t=u(Un(this,16),29),t||vb),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:EGe(this,u(t,241));return}Jl(this,n-dt((yn(),vb)),An((i=u(Un(this,16),29),i||vb),n),t)},s.fi=function(){return yn(),vb},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:EGe(this,null);return}Fl(this,n-dt((yn(),vb)),An((t=u(Un(this,16),29),t||vb),n))};var cA,R8e,Man;v(Fn,"EFactoryImpl",710),m(1018,710,{109:1,2075:1,94:1,93:1,469:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1},aU),s.oi=function(n,t){switch(n.fk()){case 12:return u(t,147).Og();case 13:return su(t);default:throw $(new Gn(r7+n.ve()+ip))}},s.pi=function(n){var t,i,r,c,o,l,f,h;switch(n.G==-1&&(n.G=(t=sl(n),t?Ld(t.si(),n):-1)),n.G){case 4:return o=new QM,o;case 6:return l=new pj,l;case 7:return f=new voe,f;case 8:return r=new w3,r;case 9:return i=new Fk,i;case 10:return c=new yo,c;case 11:return h=new B6,h;default:throw $(new Gn(mne+n.zb+ip))}},s.qi=function(n,t){switch(n.fk()){case 13:case 12:return null;default:throw $(new Gn(r7+n.ve()+ip))}},v(hg,"ElkGraphFactoryImpl",1018),m(439,161,{109:1,94:1,93:1,158:1,197:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1}),s.Dh=function(){var n,t;return t=(n=u(Un(this,16),29),dae(vh(n||this.fi()))),t==null?(Nj(),Nj(),Fce):new LOe(this,t)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.ve()}return Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null}return Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:this.ri(Pt(t));return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return yn(),qan},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:this.ri(null);return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.ve=function(){return this.zb},s.ri=function(n){Mo(this,n)},s.Ib=function(){return _E(this)},s.zb=null,v(Fn,"ENamedElementImpl",439),m(184,439,{109:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,52:1,100:1,161:1,184:1,117:1,118:1,680:1},c_e),s.xh=function(n){return _He(this,n)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return this.yb;case 3:return this.xb;case 4:return this.sb;case 5:return!this.rb&&(this.rb=new m2(this,Aa,this)),this.rb;case 6:return!this.vb&&(this.vb=new y4(xa,this,6,7)),this.vb;case 7:return t?this.Db>>16==7?u(this.Cb,241):null:F_e(this)}return Pl(this,n-dt((yn(),n0)),An((r=u(Un(this,16),29),r||n0),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 4:return this.sb&&(i=u(this.sb,52).Qh(this,1,rA,i)),B1e(this,u(n,469),i);case 5:return!this.rb&&(this.rb=new m2(this,Aa,this)),To(this.rb,n,i);case 6:return!this.vb&&(this.vb=new y4(xa,this,6,7)),To(this.vb,n,i);case 7:return this.Cb&&(i=(c=this.Db>>16,c>=0?_He(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,7,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),n0)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),n0)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 4:return B1e(this,null,i);case 5:return!this.rb&&(this.rb=new m2(this,Aa,this)),vc(this.rb,n,i);case 6:return!this.vb&&(this.vb=new y4(xa,this,6,7)),vc(this.vb,n,i);case 7:return dl(this,null,7,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),n0)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),n0)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.yb!=null;case 3:return this.xb!=null;case 4:return!!this.sb;case 5:return!!this.rb&&this.rb.i!=0;case 6:return!!this.vb&&this.vb.i!=0;case 7:return!!F_e(this)}return Ll(this,n-dt((yn(),n0)),An((t=u(Un(this,16),29),t||n0),n))},s.Wh=function(n){var t;return t=LNn(this,n),t||Tge(this,n)},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Pt(t));return;case 2:TB(this,Pt(t));return;case 3:MB(this,Pt(t));return;case 4:dW(this,u(t,469));return;case 5:!this.rb&&(this.rb=new m2(this,Aa,this)),kt(this.rb),!this.rb&&(this.rb=new m2(this,Aa,this)),er(this.rb,u(t,18));return;case 6:!this.vb&&(this.vb=new y4(xa,this,6,7)),kt(this.vb),!this.vb&&(this.vb=new y4(xa,this,6,7)),er(this.vb,u(t,18));return}Jl(this,n-dt((yn(),n0)),An((i=u(Un(this,16),29),i||n0),n),t)},s.bi=function(n){var t,i;if(n&&this.rb)for(i=new ut(this.rb);i.e!=i.i.gc();)t=ft(i),X(t,360)&&(u(t,360).w=null);U4(this,64,n)},s.fi=function(){return yn(),n0},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Mo(this,null);return;case 2:TB(this,null);return;case 3:MB(this,null);return;case 4:dW(this,null);return;case 5:!this.rb&&(this.rb=new m2(this,Aa,this)),kt(this.rb);return;case 6:!this.vb&&(this.vb=new y4(xa,this,6,7)),kt(this.vb);return}Fl(this,n-dt((yn(),n0)),An((t=u(Un(this,16),29),t||n0),n))},s.mi=function(){WQ(this)},s.si=function(){return!this.rb&&(this.rb=new m2(this,Aa,this)),this.rb},s.ti=function(){return this.sb},s.ui=function(){return this.ub},s.vi=function(){return this.xb},s.wi=function(){return this.yb},s.xi=function(n){this.ub=n},s.Ib=function(){var n;return(this.Db&64)!=0?_E(this):(n=new cf(_E(this)),n.a+=" (nsURI: ",Bc(n,this.yb),n.a+=", nsPrefix: ",Bc(n,this.xb),n.a+=")",n.a)},s.xb=null,s.yb=null,v(Fn,"EPackageImpl",184),m(556,184,{109:1,2077:1,556:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,52:1,100:1,161:1,184:1,117:1,118:1,680:1},nUe),s.q=!1,s.r=!1;var Tan=!1;v(hg,"ElkGraphPackageImpl",556),m(362,728,{109:1,343:1,174:1,157:1,276:1,362:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},QM),s.xh=function(n){return Hde(this,n)},s.Ih=function(n,t,i){switch(n){case 7:return vae(this);case 8:return this.a}return nde(this,n,t,i)},s.Ph=function(n,t,i){var r;return t===7?(this.Cb&&(i=(r=this.Db>>16,r>=0?Hde(this,i):this.Cb.Qh(this,-1-r,null,i))),Mfe(this,u(n,174),i)):sW(this,n,t,i)},s.Rh=function(n,t,i){return t==7?Mfe(this,null,i):XY(this,n,t,i)},s.Th=function(n){switch(n){case 7:return!!vae(this);case 8:return!bn("",this.a)}return sde(this,n)},s.$h=function(n,t){switch(n){case 7:Abe(this,u(t,174));return;case 8:Ghe(this,Pt(t));return}i0e(this,n,t)},s.fi=function(){return Gu(),L8e},s.hi=function(n){switch(n){case 7:Abe(this,null);return;case 8:Ghe(this,"");return}dde(this,n)},s.Ib=function(){return JGe(this)},s.a="",v(hg,"ElkLabelImpl",362),m(206,729,{109:1,343:1,84:1,174:1,26:1,276:1,206:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},pj),s.xh=function(n){return Xde(this,n)},s.Ih=function(n,t,i){switch(n){case 9:return!this.c&&(this.c=new pe($s,this,9,9)),this.c;case 10:return!this.a&&(this.a=new pe(Jt,this,10,11)),this.a;case 11:return Bi(this);case 12:return!this.b&&(this.b=new pe(pr,this,12,3)),this.b;case 13:return Pn(),!this.a&&(this.a=new pe(Jt,this,10,11)),this.a.i>0}return Nde(this,n,t,i)},s.Ph=function(n,t,i){var r;switch(t){case 9:return!this.c&&(this.c=new pe($s,this,9,9)),To(this.c,n,i);case 10:return!this.a&&(this.a=new pe(Jt,this,10,11)),To(this.a,n,i);case 11:return this.Cb&&(i=(r=this.Db>>16,r>=0?Xde(this,i):this.Cb.Qh(this,-1-r,null,i))),Gle(this,u(n,26),i);case 12:return!this.b&&(this.b=new pe(pr,this,12,3)),To(this.b,n,i)}return Yde(this,n,t,i)},s.Rh=function(n,t,i){switch(t){case 9:return!this.c&&(this.c=new pe($s,this,9,9)),vc(this.c,n,i);case 10:return!this.a&&(this.a=new pe(Jt,this,10,11)),vc(this.a,n,i);case 11:return Gle(this,null,i);case 12:return!this.b&&(this.b=new pe(pr,this,12,3)),vc(this.b,n,i)}return Qde(this,n,t,i)},s.Th=function(n){switch(n){case 9:return!!this.c&&this.c.i!=0;case 10:return!!this.a&&this.a.i!=0;case 11:return!!Bi(this);case 12:return!!this.b&&this.b.i!=0;case 13:return!this.a&&(this.a=new pe(Jt,this,10,11)),this.a.i>0}return v1e(this,n)},s.$h=function(n,t){switch(n){case 9:!this.c&&(this.c=new pe($s,this,9,9)),kt(this.c),!this.c&&(this.c=new pe($s,this,9,9)),er(this.c,u(t,18));return;case 10:!this.a&&(this.a=new pe(Jt,this,10,11)),kt(this.a),!this.a&&(this.a=new pe(Jt,this,10,11)),er(this.a,u(t,18));return;case 11:Iz(this,u(t,26));return;case 12:!this.b&&(this.b=new pe(pr,this,12,3)),kt(this.b),!this.b&&(this.b=new pe(pr,this,12,3)),er(this.b,u(t,18));return}fbe(this,n,t)},s.fi=function(){return Gu(),P8e},s.hi=function(n){switch(n){case 9:!this.c&&(this.c=new pe($s,this,9,9)),kt(this.c);return;case 10:!this.a&&(this.a=new pe(Jt,this,10,11)),kt(this.a);return;case 11:Iz(this,null);return;case 12:!this.b&&(this.b=new pe(pr,this,12,3)),kt(this.b);return}Ade(this,n)},s.Ib=function(){return Jbe(this)},v(hg,"ElkNodeImpl",206),m(193,729,{109:1,343:1,84:1,174:1,125:1,276:1,193:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},voe),s.xh=function(n){return Gde(this,n)},s.Ih=function(n,t,i){return n==9?Ia(this):Nde(this,n,t,i)},s.Ph=function(n,t,i){var r;return t===9?(this.Cb&&(i=(r=this.Db>>16,r>=0?Gde(this,i):this.Cb.Qh(this,-1-r,null,i))),Ole(this,u(n,26),i)):Yde(this,n,t,i)},s.Rh=function(n,t,i){return t==9?Ole(this,null,i):Qde(this,n,t,i)},s.Th=function(n){return n==9?!!Ia(this):v1e(this,n)},s.$h=function(n,t){if(n===9){kbe(this,u(t,26));return}fbe(this,n,t)},s.fi=function(){return Gu(),$8e},s.hi=function(n){if(n===9){kbe(this,null);return}Ade(this,n)},s.Ib=function(){return _Xe(this)},v(hg,"ElkPortImpl",193);var Can=Hi(yc,"BasicEMap/Entry");m(1091,118,{109:1,45:1,94:1,93:1,136:1,57:1,114:1,52:1,100:1,117:1,118:1},B6),s.Fb=function(n){return this===n},s.jd=function(){return this.b},s.Hb=function(){return vw(this)},s.Ai=function(n){zhe(this,u(n,147))},s.Ih=function(n,t,i){switch(n){case 0:return this.b;case 1:return this.c}return uz(this,n,t,i)},s.Th=function(n){switch(n){case 0:return!!this.b;case 1:return this.c!=null}return _Q(this,n)},s.$h=function(n,t){switch(n){case 0:zhe(this,u(t,147));return;case 1:Fhe(this,t);return}vW(this,n,t)},s.fi=function(){return Gu(),a1},s.hi=function(n){switch(n){case 0:zhe(this,null);return;case 1:Fhe(this,null);return}wW(this,n)},s.yi=function(){var n;return this.a==-1&&(n=this.b,this.a=n?Oi(n):0),this.a},s.kd=function(){return this.c},s.zi=function(n){this.a=n},s.ld=function(n){var t;return t=this.c,Fhe(this,n),t},s.Ib=function(){var n;return(this.Db&64)!=0?Jf(this):(n=new p0,Kt(Kt(Kt(n,this.b?this.b.Og():Vo),eee),Qj(this.c)),n.a)},s.a=-1,s.c=null;var Qd=v(hg,"ElkPropertyToValueMapEntryImpl",1091);m(980,1,{},Hp),v(Qr,"JsonAdapter",980),m(215,63,H1,oh),v(Qr,"JsonImportException",215),m(850,1,{},Yqe),v(Qr,"JsonImporter",850),m(884,1,{},ITe),s.Bi=function(n){GHe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$0$Type",884),m(885,1,{},_Te),s.Bi=function(n){Mqe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$1$Type",885),m(893,1,{},cSe),s.Bi=function(n){BIe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$10$Type",893),m(895,1,{},LTe),s.Bi=function(n){bqe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$11$Type",895),m(896,1,{},PTe),s.Bi=function(n){gqe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$12$Type",896),m(902,1,{},VIe),s.Bi=function(n){RGe(this.a,this.b,this.c,this.d,u(n,139))},v(Qr,"JsonImporter/lambda$13$Type",902),m(901,1,{},YIe),s.Bi=function(n){nKe(this.a,this.b,this.c,this.d,u(n,149))},v(Qr,"JsonImporter/lambda$14$Type",901),m(897,1,{},$Te),s.Bi=function(n){aNe(this.a,this.b,Pt(n))},v(Qr,"JsonImporter/lambda$15$Type",897),m(898,1,{},RTe),s.Bi=function(n){hNe(this.a,this.b,Pt(n))},v(Qr,"JsonImporter/lambda$16$Type",898),m(899,1,{},BTe),s.Bi=function(n){xHe(this.b,this.a,u(n,139))},v(Qr,"JsonImporter/lambda$17$Type",899),m(900,1,{},zTe),s.Bi=function(n){AHe(this.b,this.a,u(n,139))},v(Qr,"JsonImporter/lambda$18$Type",900),m(905,1,{},uSe),s.Bi=function(n){CGe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$19$Type",905),m(886,1,{},oSe),s.Bi=function(n){$He(this.a,u(n,139))},v(Qr,"JsonImporter/lambda$2$Type",886),m(903,1,{},sSe),s.Bi=function(n){V3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$20$Type",903),m(904,1,{},lSe),s.Bi=function(n){Y3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$21$Type",904),m(908,1,{},fSe),s.Bi=function(n){TGe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$22$Type",908),m(906,1,{},aSe),s.Bi=function(n){X3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$23$Type",906),m(907,1,{},hSe),s.Bi=function(n){K3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$24$Type",907),m(910,1,{},dSe),s.Bi=function(n){nGe(this.a,u(n,139))},v(Qr,"JsonImporter/lambda$25$Type",910),m(909,1,{},bSe),s.Bi=function(n){zIe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$26$Type",909),m(911,1,it,FTe),s.Ad=function(n){L9n(this.b,this.a,Pt(n))},v(Qr,"JsonImporter/lambda$27$Type",911),m(912,1,it,JTe),s.Ad=function(n){P9n(this.b,this.a,Pt(n))},v(Qr,"JsonImporter/lambda$28$Type",912),m(913,1,{},HTe),s.Bi=function(n){fUe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$29$Type",913),m(889,1,{},gSe),s.Bi=function(n){VFe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$3$Type",889),m(914,1,{},GTe),s.Bi=function(n){DUe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$30$Type",914),m(915,1,{},wSe),s.Bi=function(n){pRe(this.a,re(n))},v(Qr,"JsonImporter/lambda$31$Type",915),m(916,1,{},pSe),s.Bi=function(n){mRe(this.a,re(n))},v(Qr,"JsonImporter/lambda$32$Type",916),m(917,1,{},mSe),s.Bi=function(n){vRe(this.a,re(n))},v(Qr,"JsonImporter/lambda$33$Type",917),m(918,1,{},vSe),s.Bi=function(n){yRe(this.a,re(n))},v(Qr,"JsonImporter/lambda$34$Type",918),m(919,1,{},ySe),s.Bi=function(n){DMn(this.a,u(n,57))},v(Qr,"JsonImporter/lambda$35$Type",919),m(920,1,{},kSe),s.Bi=function(n){IMn(this.a,u(n,57))},v(Qr,"JsonImporter/lambda$36$Type",920),m(924,1,{},KIe),v(Qr,"JsonImporter/lambda$37$Type",924),m(921,1,it,GNe),s.Ad=function(n){s7n(this.a,this.c,this.b,u(n,372))},v(Qr,"JsonImporter/lambda$38$Type",921),m(922,1,it,qTe),s.Ad=function(n){Xgn(this.a,this.b,u(n,170))},v(Qr,"JsonImporter/lambda$39$Type",922),m(887,1,{},jSe),s.Bi=function(n){V3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$4$Type",887),m(923,1,it,UTe),s.Ad=function(n){Kgn(this.a,this.b,u(n,170))},v(Qr,"JsonImporter/lambda$40$Type",923),m(925,1,it,qNe),s.Ad=function(n){l7n(this.a,this.b,this.c,u(n,8))},v(Qr,"JsonImporter/lambda$41$Type",925),m(888,1,{},ESe),s.Bi=function(n){Y3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$5$Type",888),m(892,1,{},SSe),s.Bi=function(n){YFe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$6$Type",892),m(890,1,{},xSe),s.Bi=function(n){X3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$7$Type",890),m(891,1,{},ASe),s.Bi=function(n){K3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$8$Type",891),m(894,1,{},MSe),s.Bi=function(n){tGe(this.a,u(n,139))},v(Qr,"JsonImporter/lambda$9$Type",894),m(944,1,it,TSe),s.Ad=function(n){T4(this.a,new y2(Pt(n)))},v(Qr,"JsonMetaDataConverter/lambda$0$Type",944),m(945,1,it,CSe),s.Ad=function(n){zvn(this.a,u(n,244))},v(Qr,"JsonMetaDataConverter/lambda$1$Type",945),m(946,1,it,OSe),s.Ad=function(n){N4n(this.a,u(n,144))},v(Qr,"JsonMetaDataConverter/lambda$2$Type",946),m(947,1,it,NSe),s.Ad=function(n){Fvn(this.a,u(n,160))},v(Qr,"JsonMetaDataConverter/lambda$3$Type",947),m(244,23,{3:1,35:1,23:1,244:1},d4);var gG,wG,_ce,pG,mG,vG,Lce,Pce,yG=yt(kN,"GraphFeature",244,Ct,b8n,j3n),Oan;m(11,1,{35:1,147:1},yi,Li,fn,Vr),s.Dd=function(n){return qwn(this,u(n,147))},s.Fb=function(n){return y_e(this,n)},s.Rg=function(){return _e(this)},s.Og=function(){return this.b},s.Hb=function(){return Od(this.b)},s.Ib=function(){return this.b},v(kN,"Property",11),m(657,1,Yt,aX),s.Le=function(n,t){return Ajn(this,u(n,105),u(t,105))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(kN,"PropertyHolderComparator",657),m(698,1,Fr,roe),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return z9n(this)},s.Qb=function(){xAe()},s.Ob=function(){return!!this.a},v(HF,"ElkGraphUtil/AncestorIterator",698);var B8e=Hi(yc,"EList");m(71,56,{20:1,31:1,56:1,18:1,16:1,71:1,61:1}),s._c=function(n,t){$E(this,n,t)},s.Ec=function(n){return Et(this,n)},s.ad=function(n,t){return h1e(this,n,t)},s.Fc=function(n){return er(this,n)},s.Gi=function(){return new m4(this)},s.Hi=function(){return new kC(this)},s.Ii=function(n){return hO(this,n)},s.Ji=function(){return!0},s.Ki=function(n,t){},s.Li=function(){},s.Mi=function(n,t){bY(this,n,t)},s.Ni=function(n,t,i){},s.Oi=function(n,t){},s.Pi=function(n,t,i){},s.Fb=function(n){return pXe(this,n)},s.Hb=function(){return s1e(this)},s.Qi=function(){return!1},s.Jc=function(){return new ut(this)},s.cd=function(){return new p4(this)},s.dd=function(n){var t;if(t=this.gc(),n<0||n>t)throw $(new b2(n,t));return new vV(this,n)},s.Si=function(n,t){this.Ri(n,this.bd(t))},s.Kc=function(n){return sB(this,n)},s.Ui=function(n,t){return t},s.fd=function(n,t){return tv(this,n,t)},s.Ib=function(){return rde(this)},s.Wi=function(){return!0},s.Xi=function(n,t){return t8(this,t)},v(yc,"AbstractEList",71),m(67,71,Mh,z6,Nw,t1e),s.Ci=function(n,t){return lW(this,n,t)},s.Di=function(n){return uHe(this,n)},s.Ei=function(n,t){xO(this,n,t)},s.Fi=function(n){YC(this,n)},s.Yi=function(n){return phe(this,n)},s.$b=function(){vE(this)},s.Gc=function(n){return m8(this,n)},s.Xb=function(n){return K(this,n)},s.Zi=function(n){var t,i,r;++this.j,i=this.g==null?0:this.g.length,n>i&&(r=this.g,t=i+(i/2|0)+4,t=0?(this.ed(t),!0):!1},s.Vi=function(n,t){return this.Bj(n,this.Xi(n,t))},s.gc=function(){return this.Cj()},s.Nc=function(){return this.Dj()},s.Oc=function(n){return this.Ej(n)},s.Ib=function(){return this.Fj()},v(yc,"DelegatingEList",2055),m(2056,2055,$Ze),s.Ci=function(n,t){return tge(this,n,t)},s.Di=function(n){return this.Ci(this.Cj(),n)},s.Ei=function(n,t){tUe(this,n,t)},s.Fi=function(n){qqe(this,n)},s.Ji=function(){return!this.Kj()},s.$b=function(){hS(this)},s.Gj=function(n,t,i,r,c){return new m_e(this,n,t,i,r,c)},s.Hj=function(n){ai(this.hj(),n)},s.Ij=function(){return null},s.Jj=function(){return-1},s.hj=function(){return null},s.Kj=function(){return!1},s.Lj=function(n,t){return t},s.Mj=function(n,t){return t},s.Nj=function(){return!1},s.Oj=function(){return!this.yj()},s.Ri=function(n,t){var i,r;return this.Nj()?(r=this.Oj(),i=E0e(this,n,t),this.Hj(this.Gj(7,ve(t),i,n,r)),i):E0e(this,n,t)},s.ed=function(n){var t,i,r,c;return this.Nj()?(i=null,r=this.Oj(),t=this.Gj(4,c=iR(this,n),null,n,r),this.Kj()&&c?(i=this.Mj(c,i),i?(i.lj(t),i.mj()):this.Hj(t)):i?(i.lj(t),i.mj()):this.Hj(t),c):(c=iR(this,n),this.Kj()&&c&&(i=this.Mj(c,null),i&&i.mj()),c)},s.Vi=function(n,t){return dKe(this,n,t)},v(wy,"DelegatingNotifyingListImpl",2056),m(151,1,RN),s.lj=function(n){return s0e(this,n)},s.mj=function(){jY(this)},s.ej=function(){return this.d},s.Ij=function(){return null},s.Pj=function(){return null},s.fj=function(n){return-1},s.gj=function(){return WUe(this)},s.hj=function(){return null},s.ij=function(){return Nbe(this)},s.jj=function(){return this.o<0?this.o<-2?-2-this.o-1:-1:this.o},s.Qj=function(){return!1},s.kj=function(n){var t,i,r,c,o,l,f,h,b,p,y;switch(this.d){case 1:case 2:switch(c=n.ej(),c){case 1:case 2:if(o=n.hj(),ue(o)===ue(this.hj())&&this.fj(null)==n.fj(null))return this.g=n.gj(),n.ej()==1&&(this.d=1),!0}case 4:{switch(c=n.ej(),c){case 4:{if(o=n.hj(),ue(o)===ue(this.hj())&&this.fj(null)==n.fj(null))return b=yge(this),h=this.o<0?this.o<-2?-2-this.o-1:-1:this.o,l=n.jj(),this.d=6,y=new Nw(2),h<=l?(Et(y,this.n),Et(y,n.ij()),this.g=z(B($t,1),ni,30,15,[this.o=h,l+1])):(Et(y,n.ij()),Et(y,this.n),this.g=z(B($t,1),ni,30,15,[this.o=l,h])),this.n=y,b||(this.o=-2-this.o-1),!0;break}}break}case 6:{switch(c=n.ej(),c){case 4:{if(o=n.hj(),ue(o)===ue(this.hj())&&this.fj(null)==n.fj(null)){for(b=yge(this),l=n.jj(),p=u(this.g,54),r=oe($t,ni,30,p.length+1,15,1),t=0;t>>0,t.toString(16))),r.a+=" (eventType: ",this.d){case 1:{r.a+="SET";break}case 2:{r.a+="UNSET";break}case 3:{r.a+="ADD";break}case 5:{r.a+="ADD_MANY";break}case 4:{r.a+="REMOVE";break}case 6:{r.a+="REMOVE_MANY";break}case 7:{r.a+="MOVE";break}case 8:{r.a+="REMOVING_ADAPTER";break}case 9:{r.a+="RESOLVE";break}default:{IX(r,this.d);break}}if(zXe(this)&&(r.a+=", touch: true"),r.a+=", position: ",IX(r,this.o<0?this.o<-2?-2-this.o-1:-1:this.o),r.a+=", notifier: ",qj(r,this.hj()),r.a+=", feature: ",qj(r,this.Ij()),r.a+=", oldValue: ",qj(r,Nbe(this)),r.a+=", newValue: ",this.d==6&&X(this.g,54)){for(i=u(this.g,54),r.a+="[",n=0;n10?((!this.b||this.c.j!=this.a)&&(this.b=new w2(this),this.a=this.j),rf(this.b,n)):m8(this,n)},s.Wi=function(){return!0},s.a=0,v(yc,"AbstractEList/1",949),m(305,99,iF,b2),v(yc,"AbstractEList/BasicIndexOutOfBoundsException",305),m(42,1,Fr,ut),s.Nb=function(n){Zr(this,n)},s.Vj=function(){if(this.i.j!=this.f)throw $(new Nl)},s.Wj=function(){return ft(this)},s.Ob=function(){return this.e!=this.i.gc()},s.Pb=function(){return this.Wj()},s.Qb=function(){KE(this)},s.e=0,s.f=0,s.g=-1,v(yc,"AbstractEList/EIterator",42),m(286,42,Qh,p4,vV),s.Qb=function(){KE(this)},s.Rb=function(n){oJe(this,n)},s.Xj=function(){var n;try{return n=this.d.Xb(--this.e),this.Vj(),this.g=this.e,n}catch(t){throw t=sr(t),X(t,99)?(this.Vj(),$(new au)):$(t)}},s.Yj=function(n){oHe(this,n)},s.Sb=function(){return this.e!=0},s.Tb=function(){return this.e},s.Ub=function(){return this.Xj()},s.Vb=function(){return this.e-1},s.Wb=function(n){this.Yj(n)},v(yc,"AbstractEList/EListIterator",286),m(355,42,Fr,m4),s.Wj=function(){return LQ(this)},s.Qb=function(){throw $(new _t)},v(yc,"AbstractEList/NonResolvingEIterator",355),m(391,286,Qh,kC,Xle),s.Rb=function(n){throw $(new _t)},s.Wj=function(){var n;try{return n=this.c.Ti(this.e),this.Vj(),this.g=this.e++,n}catch(t){throw t=sr(t),X(t,99)?(this.Vj(),$(new au)):$(t)}},s.Xj=function(){var n;try{return n=this.c.Ti(--this.e),this.Vj(),this.g=this.e,n}catch(t){throw t=sr(t),X(t,99)?(this.Vj(),$(new au)):$(t)}},s.Qb=function(){throw $(new _t)},s.Wb=function(n){throw $(new _t)},v(yc,"AbstractEList/NonResolvingEListIterator",391),m(2042,71,RZe),s.Ci=function(n,t){var i,r,c,o,l,f,h,b,p,y,S;if(c=t.gc(),c!=0){for(b=u(Un(this.a,4),129),p=b==null?0:b.length,S=p+c,r=uQ(this,S),y=p-n,y>0&&Wu(b,n,r,n+c,y),h=t.Jc(),l=0;li)throw $(new b2(n,i));return new IIe(this,n)},s.$b=function(){var n,t;++this.j,n=u(Un(this.a,4),129),t=n==null?0:n.length,g8(this,null),bY(this,t,n)},s.Gc=function(n){var t,i,r,c,o;if(t=u(Un(this.a,4),129),t!=null){if(n!=null){for(r=t,c=0,o=r.length;c=i)throw $(new b2(n,i));return t[n]},s.bd=function(n){var t,i,r;if(t=u(Un(this.a,4),129),t!=null){if(n!=null){for(i=0,r=t.length;ii)throw $(new b2(n,i));return new DIe(this,n)},s.Ri=function(n,t){var i,r,c;if(i=wJe(this),c=i==null?0:i.length,n>=c)throw $(new jo(Mne+n+dg+c));if(t>=c)throw $(new jo(Tne+t+dg+c));return r=i[t],n!=t&&(n0&&Wu(n,0,t,0,i),t},s.Oc=function(n){var t,i,r;return t=u(Un(this.a,4),129),r=t==null?0:t.length,r>0&&(n.lengthr&&tr(n,r,null),n};var Nan;v(yc,"ArrayDelegatingEList",2042),m(1032,42,Fr,zPe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw $(new Nl)},s.Qb=function(){KE(this),this.a=u(Un(this.b.a,4),129)},v(yc,"ArrayDelegatingEList/EIterator",1032),m(712,286,Qh,ZDe,DIe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw $(new Nl)},s.Yj=function(n){oHe(this,n),this.a=u(Un(this.b.a,4),129)},s.Qb=function(){KE(this),this.a=u(Un(this.b.a,4),129)},v(yc,"ArrayDelegatingEList/EListIterator",712),m(1033,355,Fr,FPe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw $(new Nl)},v(yc,"ArrayDelegatingEList/NonResolvingEIterator",1033),m(713,391,Qh,eIe,IIe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw $(new Nl)},v(yc,"ArrayDelegatingEList/NonResolvingEListIterator",713),m(605,305,iF,jK),v(yc,"BasicEList/BasicIndexOutOfBoundsException",605),m(699,67,Mh,Nse),s._c=function(n,t){throw $(new _t)},s.Ec=function(n){throw $(new _t)},s.ad=function(n,t){throw $(new _t)},s.Fc=function(n){throw $(new _t)},s.$b=function(){throw $(new _t)},s.Zi=function(n){throw $(new _t)},s.Jc=function(){return this.Gi()},s.cd=function(){return this.Hi()},s.dd=function(n){return this.Ii(n)},s.Ri=function(n,t){throw $(new _t)},s.Si=function(n,t){throw $(new _t)},s.ed=function(n){throw $(new _t)},s.Kc=function(n){throw $(new _t)},s.fd=function(n,t){throw $(new _t)},v(yc,"BasicEList/UnmodifiableEList",699),m(711,1,{3:1,20:1,18:1,16:1,61:1,586:1}),s._c=function(n,t){_wn(this,n,u(t,45))},s.Ec=function(n){return Tpn(this,u(n,45))},s.Ic=function(n){rc(this,n)},s.Xb=function(n){return u(K(this.c,n),136)},s.Ri=function(n,t){return u(this.c.Ri(n,t),45)},s.Si=function(n,t){Lwn(this,n,u(t,45))},s.ed=function(n){return u(this.c.ed(n),45)},s.fd=function(n,t){return Hvn(this,n,u(t,45))},s.gd=function(n){Vb(this,n)},s.Lc=function(){return new pn(this,16)},s.Mc=function(){return new wn(null,new pn(this,16))},s.ad=function(n,t){return this.c.ad(n,t)},s.Fc=function(n){return this.c.Fc(n)},s.$b=function(){this.c.$b()},s.Gc=function(n){return this.c.Gc(n)},s.Hc=function(n){return yO(this.c,n)},s.Zj=function(){var n,t,i;if(this.d==null){for(this.d=oe(z8e,nme,67,2*this.f+1,0,1),i=this.e,this.f=0,t=this.c.Jc();t.e!=t.i.gc();)n=u(t.Wj(),136),lz(this,n);this.e=i}},s.Fb=function(n){return SNe(this,n)},s.Hb=function(){return s1e(this.c)},s.bd=function(n){return this.c.bd(n)},s.$j=function(){this.c=new DSe(this)},s.dc=function(){return this.f==0},s.Jc=function(){return this.c.Jc()},s.cd=function(){return this.c.cd()},s.dd=function(n){return this.c.dd(n)},s._j=function(){return ZC(this)},s.ak=function(n,t,i){return new UNe(n,t,i)},s.bk=function(){return new mL},s.Kc=function(n){return wBe(this,n)},s.gc=function(){return this.f},s.hd=function(n,t){return new T0(this.c,n,t)},s.Nc=function(){return this.c.Nc()},s.Oc=function(n){return this.c.Oc(n)},s.Ib=function(){return rde(this.c)},s.e=0,s.f=0,v(yc,"BasicEMap",711),m(1027,67,Mh,DSe),s.Ki=function(n,t){wbn(this,u(t,136))},s.Ni=function(n,t,i){var r;++(r=this,u(t,136),r).a.e},s.Oi=function(n,t){pbn(this,u(t,136))},s.Pi=function(n,t,i){bpn(this,u(t,136),u(i,136))},s.Mi=function(n,t){fze(this.a)},v(yc,"BasicEMap/1",1027),m(1028,67,Mh,mL),s.$i=function(n){return oe(YBn,BZe,611,n,0,1)},v(yc,"BasicEMap/2",1028),m(1029,Ha,fs,ISe),s.$b=function(){this.a.c.$b()},s.Gc=function(n){return SQ(this.a,n)},s.Jc=function(){return this.a.f==0?(S9(),eI.a):new pAe(this.a)},s.Kc=function(n){var t;return t=this.a.f,ZB(this.a,n),this.a.f!=t},s.gc=function(){return this.a.f},v(yc,"BasicEMap/3",1029),m(1030,31,Y2,_Se),s.$b=function(){this.a.c.$b()},s.Gc=function(n){return mXe(this.a,n)},s.Jc=function(){return this.a.f==0?(S9(),eI.a):new mAe(this.a)},s.gc=function(){return this.a.f},v(yc,"BasicEMap/4",1030),m(1031,Ha,fs,LSe),s.$b=function(){this.a.c.$b()},s.Gc=function(n){var t,i,r,c,o,l,f,h,b;if(this.a.f>0&&X(n,45)&&(this.a.Zj(),h=u(n,45),f=h.jd(),c=f==null?0:Oi(f),o=Nle(this.a,c),t=this.a.d[o],t)){for(i=u(t.g,374),b=t.i,l=0;l"+this.c},s.a=0;var YBn=v(yc,"BasicEMap/EntryImpl",611);m(534,1,{},J6),v(yc,"BasicEMap/View",534);var eI;m(769,1,{}),s.Fb=function(n){return abe((jn(),Sc),n)},s.Hb=function(){return y1e((jn(),Sc))},s.Ib=function(){return Fa((jn(),Sc))},v(yc,"ECollections/BasicEmptyUnmodifiableEList",769),m(1302,1,Qh,WM),s.Nb=function(n){Zr(this,n)},s.Rb=function(n){throw $(new _t)},s.Ob=function(){return!1},s.Sb=function(){return!1},s.Pb=function(){throw $(new au)},s.Tb=function(){return 0},s.Ub=function(){throw $(new au)},s.Vb=function(){return-1},s.Qb=function(){throw $(new _t)},s.Wb=function(n){throw $(new _t)},v(yc,"ECollections/BasicEmptyUnmodifiableEList/1",1302),m(1300,769,{20:1,18:1,16:1,61:1},Sxe),s._c=function(n,t){zAe()},s.Ec=function(n){return BAe()},s.ad=function(n,t){return FAe()},s.Fc=function(n){return JAe()},s.$b=function(){HAe()},s.Gc=function(n){return!1},s.Hc=function(n){return!1},s.Ic=function(n){rc(this,n)},s.Xb=function(n){return Pse((jn(),n)),null},s.bd=function(n){return-1},s.dc=function(){return!0},s.Jc=function(){return this.a},s.cd=function(){return this.a},s.dd=function(n){return this.a},s.Ri=function(n,t){return GAe()},s.Si=function(n,t){qAe()},s.ed=function(n){return UAe()},s.Kc=function(n){return XAe()},s.fd=function(n,t){return KAe()},s.gc=function(){return 0},s.gd=function(n){Vb(this,n)},s.Lc=function(){return new pn(this,16)},s.Mc=function(){return new wn(null,new pn(this,16))},s.hd=function(n,t){return jn(),new T0(Sc,n,t)},s.Nc=function(){return Ofe((jn(),Sc))},s.Oc=function(n){return jn(),GE(Sc,n)},v(yc,"ECollections/EmptyUnmodifiableEList",1300),m(1301,769,{20:1,18:1,16:1,61:1,586:1},xxe),s._c=function(n,t){zAe()},s.Ec=function(n){return BAe()},s.ad=function(n,t){return FAe()},s.Fc=function(n){return JAe()},s.$b=function(){HAe()},s.Gc=function(n){return!1},s.Hc=function(n){return!1},s.Ic=function(n){rc(this,n)},s.Xb=function(n){return Pse((jn(),n)),null},s.bd=function(n){return-1},s.dc=function(){return!0},s.Jc=function(){return this.a},s.cd=function(){return this.a},s.dd=function(n){return this.a},s.Ri=function(n,t){return GAe()},s.Si=function(n,t){qAe()},s.ed=function(n){return UAe()},s.Kc=function(n){return XAe()},s.fd=function(n,t){return KAe()},s.gc=function(){return 0},s.gd=function(n){Vb(this,n)},s.Lc=function(){return new pn(this,16)},s.Mc=function(){return new wn(null,new pn(this,16))},s.hd=function(n,t){return jn(),new T0(Sc,n,t)},s.Nc=function(){return Ofe((jn(),Sc))},s.Oc=function(n){return jn(),GE(Sc,n)},s._j=function(){return jn(),jn(),i1},v(yc,"ECollections/EmptyUnmodifiableEMap",1301);var J8e=Hi(yc,"Enumerator"),kG;m(290,1,{290:1},DW),s.Fb=function(n){var t;return this===n?!0:X(n,290)?(t=u(n,290),this.f==t.f&&uvn(this.i,t.i)&&cV(this.a,(this.f&256)!=0?(t.f&256)!=0?t.a:null:(t.f&256)!=0?null:t.a)&&cV(this.d,t.d)&&cV(this.g,t.g)&&cV(this.e,t.e)&&lSn(this,t)):!1},s.Hb=function(){return this.f},s.Ib=function(){return WXe(this)},s.f=0;var Dan=0,Ian=0,_an=0,Lan=0,H8e=0,G8e=0,q8e=0,U8e=0,X8e=0,Pan,uA=0,oA=0,$an=0,Ran=0,jG,K8e;v(yc,"URI",290),m(1090,44,bv,Axe),s.yc=function(n,t){return u(Kc(this,Pt(n),u(t,290)),290)},v(yc,"URI/URICache",1090),m(492,67,Mh,ZM,lR),s.Qi=function(){return!0},v(yc,"UniqueEList",492),m(578,63,H1,uB),v(yc,"WrappedException",578);var Zt=Hi(ql,JZe),$m=Hi(ql,HZe),ns=Hi(ql,GZe),Rm=Hi(ql,qZe),Aa=Hi(ql,UZe),vf=Hi(ql,"EClass"),Bce=Hi(ql,"EDataType"),Ban;m(1198,44,bv,Mxe),s.xc=function(n){return $r(n)?lo(this,n):du(Xc(this.f,n))},v(ql,"EDataType/Internal/ConversionDelegate/Factory/Registry/Impl",1198);var EG=Hi(ql,"EEnum"),ed=Hi(ql,XZe),Rc=Hi(ql,KZe),yf=Hi(ql,VZe),kf,vp=Hi(ql,YZe),Bm=Hi(ql,QZe);m(1023,1,{},eT),s.Ib=function(){return"NIL"},v(ql,"EStructuralFeature/Internal/DynamicValueHolder/1",1023);var zan;m(1022,44,bv,Txe),s.xc=function(n){return $r(n)?lo(this,n):du(Xc(this.f,n))},v(ql,"EStructuralFeature/Internal/SettingDelegate/Factory/Registry/Impl",1022);var Fo=Hi(ql,WZe),Uy=Hi(ql,"EValidator/PatternMatcher"),V8e,Y8e,Rn,Wd,zm,pb,Fan,Jan,Han,mb,Zd,vb,yp,ih,Gan,qan,jf,e0,Uan,n0,Fm,Wv,Ac,Xan,Kan,kp,SG=Hi($i,"FeatureMap/Entry");m(533,1,{75:1},A$),s.Jk=function(){return this.a},s.kd=function(){return this.b},v(Fn,"BasicEObjectImpl/1",533),m(1021,1,_ne,KTe),s.Dk=function(n){return fY(this.a,this.b,n)},s.Oj=function(){return L_e(this.a,this.b)},s.Wb=function(n){pae(this.a,this.b,n)},s.Ek=function(){l5n(this.a,this.b)},v(Fn,"BasicEObjectImpl/4",1021),m(2043,1,{114:1}),s.Kk=function(n){this.e=n==0?Van:oe(Ar,On,1,n,5,1)},s.ii=function(n){return this.e[n]},s.ji=function(n,t){this.e[n]=t},s.ki=function(n){this.e[n]=null},s.Lk=function(){return this.c},s.Mk=function(){throw $(new _t)},s.Nk=function(){throw $(new _t)},s.Ok=function(){return this.d},s.Pk=function(){return this.e!=null},s.Qk=function(n){this.c=n},s.Rk=function(n){throw $(new _t)},s.Sk=function(n){throw $(new _t)},s.Tk=function(n){this.d=n};var Van;v(Fn,"BasicEObjectImpl/EPropertiesHolderBaseImpl",2043),m(192,2043,{114:1},tl),s.Mk=function(){return this.a},s.Nk=function(){return this.b},s.Rk=function(n){this.a=n},s.Sk=function(n){this.b=n},v(Fn,"BasicEObjectImpl/EPropertiesHolderImpl",192),m(501,100,WWe,p3),s.rh=function(){return this.f},s.wh=function(){return this.k},s.yh=function(n,t){this.g=n,this.i=t},s.Ah=function(){return(this.j&2)==0?this.fi():this.Xh().Lk()},s.Ch=function(){return this.i},s.th=function(){return(this.j&1)!=0},s.Mh=function(){return this.g},s.Sh=function(){return(this.j&4)!=0},s.Xh=function(){return!this.k&&(this.k=new tl),this.k},s._h=function(n){this.Xh().Qk(n),n?this.j|=2:this.j&=-3},s.bi=function(n){this.Xh().Sk(n),n?this.j|=4:this.j&=-5},s.fi=function(){return(x0(),Rn).S},s.i=0,s.j=1,v(Fn,"EObjectImpl",501),m(785,501,{109:1,94:1,93:1,57:1,114:1,52:1,100:1},bfe),s.ii=function(n){return this.e[n]},s.ji=function(n,t){this.e[n]=t},s.ki=function(n){this.e[n]=null},s.Ah=function(){return this.d},s.Fh=function(n){return zi(this.d,n)},s.Hh=function(){return this.d},s.Lh=function(){return this.e!=null},s.Xh=function(){return!this.k&&(this.k=new vL),this.k},s._h=function(n){this.d=n},s.ei=function(){var n;return this.e==null&&(n=dt(this.d),this.e=n==0?Yan:oe(Ar,On,1,n,5,1)),this},s.gi=function(){return 0};var Yan;v(Fn,"DynamicEObjectImpl",785),m(1483,785,{109:1,45:1,94:1,93:1,136:1,57:1,114:1,52:1,100:1},bDe),s.Fb=function(n){return this===n},s.Hb=function(){return vw(this)},s._h=function(n){this.d=n,this.b=QO(n,"key"),this.c=QO(n,PS)},s.yi=function(){var n;return this.a==-1&&(n=EY(this,this.b),this.a=n==null?0:Oi(n)),this.a},s.jd=function(){return EY(this,this.b)},s.kd=function(){return EY(this,this.c)},s.zi=function(n){this.a=n},s.Ai=function(n){pae(this,this.b,n)},s.ld=function(n){var t;return t=EY(this,this.c),pae(this,this.c,n),t},s.a=0,v(Fn,"DynamicEObjectImpl/BasicEMapEntry",1483),m(1484,1,{114:1},vL),s.Kk=function(n){throw $(new _t)},s.ii=function(n){throw $(new _t)},s.ji=function(n,t){throw $(new _t)},s.ki=function(n){throw $(new _t)},s.Lk=function(){throw $(new _t)},s.Mk=function(){return this.a},s.Nk=function(){return this.b},s.Ok=function(){return this.c},s.Pk=function(){throw $(new _t)},s.Qk=function(n){throw $(new _t)},s.Rk=function(n){this.a=n},s.Sk=function(n){this.b=n},s.Tk=function(n){this.c=n},v(Fn,"DynamicEObjectImpl/DynamicEPropertiesHolderImpl",1484),m(504,161,{109:1,94:1,93:1,587:1,158:1,57:1,114:1,52:1,100:1,504:1,161:1,117:1,118:1},Mb),s.xh=function(n){return qde(this,n)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.d;case 2:return i?(!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),this.b):(!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),ZC(this.b));case 3:return J_e(this);case 4:return!this.a&&(this.a=new mr(wb,this,4)),this.a;case 5:return!this.c&&(this.c=new $3(wb,this,5)),this.c}return Pl(this,n-dt((yn(),Wd)),An((r=u(Un(this,16),29),r||Wd),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 3:return this.Cb&&(i=(c=this.Db>>16,c>=0?qde(this,i):this.Cb.Qh(this,-1-c,null,i))),Tfe(this,u(n,158),i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),Wd)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),Wd)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 2:return!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),U$(this.b,n,i);case 3:return Tfe(this,null,i);case 4:return!this.a&&(this.a=new mr(wb,this,4)),vc(this.a,n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),Wd)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),Wd)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.d!=null;case 2:return!!this.b&&this.b.f!=0;case 3:return!!J_e(this);case 4:return!!this.a&&this.a.i!=0;case 5:return!!this.c&&this.c.i!=0}return Ll(this,n-dt((yn(),Wd)),An((t=u(Un(this,16),29),t||Wd),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:X3n(this,Pt(t));return;case 2:!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),CB(this.b,t);return;case 3:zUe(this,u(t,158));return;case 4:!this.a&&(this.a=new mr(wb,this,4)),kt(this.a),!this.a&&(this.a=new mr(wb,this,4)),er(this.a,u(t,18));return;case 5:!this.c&&(this.c=new $3(wb,this,5)),kt(this.c),!this.c&&(this.c=new $3(wb,this,5)),er(this.c,u(t,18));return}Jl(this,n-dt((yn(),Wd)),An((i=u(Un(this,16),29),i||Wd),n),t)},s.fi=function(){return yn(),Wd},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Hhe(this,null);return;case 2:!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),this.b.c.$b();return;case 3:zUe(this,null);return;case 4:!this.a&&(this.a=new mr(wb,this,4)),kt(this.a);return;case 5:!this.c&&(this.c=new $3(wb,this,5)),kt(this.c);return}Fl(this,n-dt((yn(),Wd)),An((t=u(Un(this,16),29),t||Wd),n))},s.Ib=function(){return NFe(this)},s.d=null,v(Fn,"EAnnotationImpl",504),m(142,711,tme,os),s.Ei=function(n,t){mwn(this,n,u(t,45))},s.Uk=function(n,t){return m2n(this,u(n,45),t)},s.Yi=function(n){return u(u(this.c,72).Yi(n),136)},s.Gi=function(){return u(this.c,72).Gi()},s.Hi=function(){return u(this.c,72).Hi()},s.Ii=function(n){return u(this.c,72).Ii(n)},s.Vk=function(n,t){return U$(this,n,t)},s.Dk=function(n){return u(this.c,77).Dk(n)},s.$j=function(){},s.Oj=function(){return u(this.c,77).Oj()},s.ak=function(n,t,i){var r;return r=u(sl(this.b).ti().pi(this.b),136),r.zi(n),r.Ai(t),r.ld(i),r},s.bk=function(){return new uoe(this)},s.Wb=function(n){CB(this,n)},s.Ek=function(){u(this.c,77).Ek()},v($i,"EcoreEMap",142),m(169,142,tme,Hs),s.Zj=function(){var n,t,i,r,c,o;if(this.d==null){for(o=oe(z8e,nme,67,2*this.f+1,0,1),i=this.c.Jc();i.e!=i.i.gc();)t=u(i.Wj(),136),r=t.yi(),c=(r&oi)%o.length,n=o[c],!n&&(n=o[c]=new uoe(this)),n.Ec(t);this.d=o}},v(Fn,"EAnnotationImpl/1",169),m(293,439,{109:1,94:1,93:1,158:1,197:1,57:1,114:1,470:1,52:1,100:1,161:1,293:1,117:1,118:1}),s.Ih=function(n,t,i){var r,c;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),!!this.Hk();case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q}return Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 9:return EV(this,i)}return c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,Lo(this),t-dt(this.fi()),n,i)},s.Th=function(n){var t,i;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return this.Hk();case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0)}return Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:this.ri(Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Cd(this,u(t,15).a);return;case 5:this.Xk(u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return yn(),Kan},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:this.ri(null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Cd(this,0);return;case 5:this.Xk(1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.mi=function(){ff(this),this.Bb|=1},s.Fk=function(){return ff(this)},s.Gk=function(){return this.t},s.Hk=function(){var n;return n=this.t,n>1||n==-1},s.Qi=function(){return(this.Bb&512)!=0},s.Wk=function(n,t){return z1e(this,n,t)},s.Xk=function(n){O2(this,n)},s.Ib=function(){return tbe(this)},s.s=0,s.t=1,v(Fn,"ETypedElementImpl",293),m(451,293,{109:1,94:1,93:1,158:1,197:1,57:1,179:1,69:1,114:1,470:1,52:1,100:1,161:1,451:1,293:1,117:1,118:1,682:1}),s.xh=function(n){return jHe(this,n)},s.Ih=function(n,t,i){var r,c;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),!!this.Hk();case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return Pn(),(this.Bb&qf)!=0;case 11:return Pn(),(this.Bb&U0)!=0;case 12:return Pn(),(this.Bb&W2)!=0;case 13:return this.j;case 14:return S8(this);case 15:return Pn(),(this.Bb&as)!=0;case 16:return Pn(),(this.Bb&yh)!=0;case 17:return E2(this)}return Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 17:return this.Cb&&(i=(c=this.Db>>16,c>=0?jHe(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,17,i)}return o=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),o.uk().xk(this,Lo(this),t-dt(this.fi()),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 9:return EV(this,i);case 17:return dl(this,null,17,i)}return c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,Lo(this),t-dt(this.fi()),n,i)},s.Th=function(n){var t,i;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return this.Hk();case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return(this.Bb&qf)==0;case 11:return(this.Bb&U0)!=0;case 12:return(this.Bb&W2)!=0;case 13:return this.j!=null;case 14:return S8(this)!=null;case 15:return(this.Bb&as)!=0;case 16:return(this.Bb&yh)!=0;case 17:return!!E2(this)}return Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:UV(this,Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Cd(this,u(t,15).a);return;case 5:this.Xk(u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return;case 10:o8(this,Re($e(t)));return;case 11:f8(this,Re($e(t)));return;case 12:l8(this,Re($e(t)));return;case 13:Ise(this,Pt(t));return;case 15:s8(this,Re($e(t)));return;case 16:a8(this,Re($e(t)));return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return yn(),Xan},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,88)&&H2(Ms(u(this.Cb,88)),4),Mo(this,null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Cd(this,0);return;case 5:this.Xk(1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return;case 10:o8(this,!0);return;case 11:f8(this,!1);return;case 12:l8(this,!1);return;case 13:this.i=null,EB(this,null);return;case 15:s8(this,!1);return;case 16:a8(this,!1);return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.mi=function(){L9(Vc((ls(),nc),this)),ff(this),this.Bb|=1},s.nk=function(){return this.f},s.gk=function(){return S8(this)},s.ok=function(){return E2(this)},s.sk=function(){return null},s.Yk=function(){return this.k},s.Jj=function(){return this.n},s.tk=function(){return wz(this)},s.uk=function(){var n,t,i,r,c,o,l,f,h;return this.p||(i=E2(this),(i.i==null&&vh(i),i.i).length,r=this.sk(),r&&dt(E2(r)),c=ff(this),l=c.ik(),n=l?(l.i&1)!=0?l==ts?Yi:l==$t?jr:l==Hm?h7:l==Jr?gr:l==Ep?cp:l==t5?up:l==ds?py:XS:l:null,t=S8(this),f=c.gk(),Djn(this),(this.Bb&yh)!=0&&((o=Wde((ls(),nc),i))&&o!=this||(o=D4(Vc(nc,this))))?this.p=new YTe(this,o):this.Hk()?this.$k()?r?(this.Bb&as)!=0?n?this._k()?this.p=new Jb(47,n,this,r):this.p=new Jb(5,n,this,r):this._k()?this.p=new Kb(46,this,r):this.p=new Kb(4,this,r):n?this._k()?this.p=new Jb(49,n,this,r):this.p=new Jb(7,n,this,r):this._k()?this.p=new Kb(48,this,r):this.p=new Kb(6,this,r):(this.Bb&as)!=0?n?n==wg?this.p=new Ed(50,Can,this):this._k()?this.p=new Ed(43,n,this):this.p=new Ed(1,n,this):this._k()?this.p=new xd(42,this):this.p=new xd(0,this):n?n==wg?this.p=new Ed(41,Can,this):this._k()?this.p=new Ed(45,n,this):this.p=new Ed(3,n,this):this._k()?this.p=new xd(44,this):this.p=new xd(2,this):X(c,159)?n==SG?this.p=new xd(40,this):(this.Bb&512)!=0?(this.Bb&as)!=0?n?this.p=new Ed(9,n,this):this.p=new xd(8,this):n?this.p=new Ed(11,n,this):this.p=new xd(10,this):(this.Bb&as)!=0?n?this.p=new Ed(13,n,this):this.p=new xd(12,this):n?this.p=new Ed(15,n,this):this.p=new xd(14,this):r?(h=r.t,h>1||h==-1?this._k()?(this.Bb&as)!=0?n?this.p=new Jb(25,n,this,r):this.p=new Kb(24,this,r):n?this.p=new Jb(27,n,this,r):this.p=new Kb(26,this,r):(this.Bb&as)!=0?n?this.p=new Jb(29,n,this,r):this.p=new Kb(28,this,r):n?this.p=new Jb(31,n,this,r):this.p=new Kb(30,this,r):this._k()?(this.Bb&as)!=0?n?this.p=new Jb(33,n,this,r):this.p=new Kb(32,this,r):n?this.p=new Jb(35,n,this,r):this.p=new Kb(34,this,r):(this.Bb&as)!=0?n?this.p=new Jb(37,n,this,r):this.p=new Kb(36,this,r):n?this.p=new Jb(39,n,this,r):this.p=new Kb(38,this,r)):this._k()?(this.Bb&as)!=0?n?this.p=new Ed(17,n,this):this.p=new xd(16,this):n?this.p=new Ed(19,n,this):this.p=new xd(18,this):(this.Bb&as)!=0?n?this.p=new Ed(21,n,this):this.p=new xd(20,this):n?this.p=new Ed(23,n,this):this.p=new xd(22,this):this.Zk()?this._k()?this.p=new BNe(u(c,29),this,r):this.p=new bae(u(c,29),this,r):X(c,159)?n==SG?this.p=new xd(40,this):(this.Bb&as)!=0?n?this.p=new PDe(t,f,this,(AQ(),l==$t?i7e:l==ts?W8e:l==Ep?r7e:l==Hm?t7e:l==Jr?n7e:l==t5?c7e:l==ds?Z8e:l==Wl?e7e:Jce)):this.p=new WIe(u(c,159),t,f,this):n?this.p=new LDe(t,f,this,(AQ(),l==$t?i7e:l==ts?W8e:l==Ep?r7e:l==Hm?t7e:l==Jr?n7e:l==t5?c7e:l==ds?Z8e:l==Wl?e7e:Jce)):this.p=new QIe(u(c,159),t,f,this):this.$k()?r?(this.Bb&as)!=0?this._k()?this.p=new FNe(u(c,29),this,r):this.p=new Zle(u(c,29),this,r):this._k()?this.p=new zNe(u(c,29),this,r):this.p=new WK(u(c,29),this,r):(this.Bb&as)!=0?this._k()?this.p=new $Oe(u(c,29),this):this.p=new mle(u(c,29),this):this._k()?this.p=new POe(u(c,29),this):this.p=new RK(u(c,29),this):this._k()?r?(this.Bb&as)!=0?this.p=new JNe(u(c,29),this,r):this.p=new efe(u(c,29),this,r):(this.Bb&as)!=0?this.p=new ROe(u(c,29),this):this.p=new vle(u(c,29),this):r?(this.Bb&as)!=0?this.p=new HNe(u(c,29),this,r):this.p=new nfe(u(c,29),this,r):(this.Bb&as)!=0?this.p=new BOe(u(c,29),this):this.p=new sR(u(c,29),this)),this.p},s.pk=function(){return(this.Bb&qf)!=0},s.Zk=function(){return!1},s.$k=function(){return!1},s.qk=function(){return(this.Bb&yh)!=0},s.vk=function(){return xY(this)},s._k=function(){return!1},s.rk=function(){return(this.Bb&as)!=0},s.al=function(n){this.k=n},s.ri=function(n){UV(this,n)},s.Ib=function(){return zz(this)},s.e=!1,s.n=0,v(Fn,"EStructuralFeatureImpl",451),m(335,451,{109:1,94:1,93:1,38:1,158:1,197:1,57:1,179:1,69:1,114:1,470:1,52:1,100:1,335:1,161:1,451:1,293:1,117:1,118:1,682:1},mX),s.Ih=function(n,t,i){var r,c;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),!!Y0e(this);case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return Pn(),(this.Bb&qf)!=0;case 11:return Pn(),(this.Bb&U0)!=0;case 12:return Pn(),(this.Bb&W2)!=0;case 13:return this.j;case 14:return S8(this);case 15:return Pn(),(this.Bb&as)!=0;case 16:return Pn(),(this.Bb&yh)!=0;case 17:return E2(this);case 18:return Pn(),(this.Bb&Bu)!=0;case 19:return t?UY(this):e$e(this)}return Pl(this,n-dt((yn(),zm)),An((r=u(Un(this,16),29),r||zm),n),t,i)},s.Th=function(n){var t,i;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return Y0e(this);case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return(this.Bb&qf)==0;case 11:return(this.Bb&U0)!=0;case 12:return(this.Bb&W2)!=0;case 13:return this.j!=null;case 14:return S8(this)!=null;case 15:return(this.Bb&as)!=0;case 16:return(this.Bb&yh)!=0;case 17:return!!E2(this);case 18:return(this.Bb&Bu)!=0;case 19:return!!e$e(this)}return Ll(this,n-dt((yn(),zm)),An((t=u(Un(this,16),29),t||zm),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:UV(this,Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Cd(this,u(t,15).a);return;case 5:SAe(this,u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return;case 10:o8(this,Re($e(t)));return;case 11:f8(this,Re($e(t)));return;case 12:l8(this,Re($e(t)));return;case 13:Ise(this,Pt(t));return;case 15:s8(this,Re($e(t)));return;case 16:a8(this,Re($e(t)));return;case 18:pQ(this,Re($e(t)));return}Jl(this,n-dt((yn(),zm)),An((i=u(Un(this,16),29),i||zm),n),t)},s.fi=function(){return yn(),zm},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,88)&&H2(Ms(u(this.Cb,88)),4),Mo(this,null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Cd(this,0);return;case 5:this.b=0,O2(this,1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return;case 10:o8(this,!0);return;case 11:f8(this,!1);return;case 12:l8(this,!1);return;case 13:this.i=null,EB(this,null);return;case 15:s8(this,!1);return;case 16:a8(this,!1);return;case 18:pQ(this,!1);return}Fl(this,n-dt((yn(),zm)),An((t=u(Un(this,16),29),t||zm),n))},s.mi=function(){UY(this),L9(Vc((ls(),nc),this)),ff(this),this.Bb|=1},s.Hk=function(){return Y0e(this)},s.Wk=function(n,t){return this.b=0,this.a=null,z1e(this,n,t)},s.Xk=function(n){SAe(this,n)},s.Ib=function(){var n;return(this.Db&64)!=0?zz(this):(n=new cf(zz(this)),n.a+=" (iD: ",md(n,(this.Bb&Bu)!=0),n.a+=")",n.a)},s.b=0,v(Fn,"EAttributeImpl",335),m(360,439,{109:1,94:1,93:1,143:1,158:1,197:1,57:1,114:1,52:1,100:1,360:1,161:1,117:1,118:1,681:1}),s.bl=function(n){return n.Ah()==this},s.xh=function(n){return QQ(this,n)},s.yh=function(n,t){this.w=null,this.Db=t<<16|this.Db&255,this.Cb=n},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return this.D!=null?this.D:this.B;case 3:return Fw(this);case 4:return this.gk();case 5:return this.F;case 6:return t?sl(this):R9(this);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),this.A}return Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 6:return this.Cb&&(i=(c=this.Db>>16,c>=0?QQ(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,6,i)}return o=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),o.uk().xk(this,Lo(this),t-dt(this.fi()),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 6:return dl(this,null,6,i);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),vc(this.A,n,i)}return c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,Lo(this),t-dt(this.fi()),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.D!=null&&this.D==this.F;case 3:return!!Fw(this);case 4:return this.gk()!=null;case 5:return this.F!=null&&this.F!=this.D&&this.F!=this.B;case 6:return!!R9(this);case 7:return!!this.A&&this.A.i!=0}return Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:RR(this,Pt(t));return;case 2:xK(this,Pt(t));return;case 5:N8(this,Pt(t));return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A),!this.A&&(this.A=new rs(Fo,this,7)),er(this.A,u(t,18));return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return yn(),Fan},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,184)&&(u(this.Cb,184).tb=null),Mo(this,null);return;case 2:i8(this,null),U9(this,this.D);return;case 5:N8(this,null);return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A);return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.fk=function(){var n;return this.G==-1&&(this.G=(n=sl(this),n?Ld(n.si(),this):-1)),this.G},s.gk=function(){return null},s.hk=function(){return sl(this)},s.cl=function(){return this.v},s.ik=function(){return Fw(this)},s.jk=function(){return this.D!=null?this.D:this.B},s.kk=function(){return this.F},s.dk=function(n){return FW(this,n)},s.dl=function(n){this.v=n},s.el=function(n){HBe(this,n)},s.fl=function(n){this.C=n},s.ri=function(n){RR(this,n)},s.Ib=function(){return VB(this)},s.C=null,s.D=null,s.G=-1,v(Fn,"EClassifierImpl",360),m(88,360,{109:1,94:1,93:1,29:1,143:1,158:1,197:1,57:1,114:1,52:1,100:1,88:1,360:1,161:1,471:1,117:1,118:1,681:1},ij),s.bl=function(n){return r2n(this,n.Ah())},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return this.D!=null?this.D:this.B;case 3:return Fw(this);case 4:return null;case 5:return this.F;case 6:return t?sl(this):R9(this);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),this.A;case 8:return Pn(),(this.Bb&256)!=0;case 9:return Pn(),(this.Bb&512)!=0;case 10:return tu(this);case 11:return!this.q&&(this.q=new pe(yf,this,11,10)),this.q;case 12:return fv(this);case 13:return lS(this);case 14:return lS(this),this.r;case 15:return fv(this),this.k;case 16:return B0e(this);case 17:return qW(this);case 18:return vh(this);case 19:return Nz(this);case 20:return fv(this),this.o;case 21:return!this.s&&(this.s=new pe(ns,this,21,17)),this.s;case 22:return Vu(this);case 23:return NW(this)}return Pl(this,n-dt((yn(),pb)),An((r=u(Un(this,16),29),r||pb),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 6:return this.Cb&&(i=(c=this.Db>>16,c>=0?QQ(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,6,i);case 11:return!this.q&&(this.q=new pe(yf,this,11,10)),To(this.q,n,i);case 21:return!this.s&&(this.s=new pe(ns,this,21,17)),To(this.s,n,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),pb)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),pb)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 6:return dl(this,null,6,i);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),vc(this.A,n,i);case 11:return!this.q&&(this.q=new pe(yf,this,11,10)),vc(this.q,n,i);case 21:return!this.s&&(this.s=new pe(ns,this,21,17)),vc(this.s,n,i);case 22:return vc(Vu(this),n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),pb)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),pb)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.D!=null&&this.D==this.F;case 3:return!!Fw(this);case 4:return!1;case 5:return this.F!=null&&this.F!=this.D&&this.F!=this.B;case 6:return!!R9(this);case 7:return!!this.A&&this.A.i!=0;case 8:return(this.Bb&256)!=0;case 9:return(this.Bb&512)!=0;case 10:return!!this.u&&Vu(this.u.a).i!=0&&!(this.n&&zQ(this.n));case 11:return!!this.q&&this.q.i!=0;case 12:return fv(this).i!=0;case 13:return lS(this).i!=0;case 14:return lS(this),this.r.i!=0;case 15:return fv(this),this.k.i!=0;case 16:return B0e(this).i!=0;case 17:return qW(this).i!=0;case 18:return vh(this).i!=0;case 19:return Nz(this).i!=0;case 20:return fv(this),!!this.o;case 21:return!!this.s&&this.s.i!=0;case 22:return!!this.n&&zQ(this.n);case 23:return NW(this).i!=0}return Ll(this,n-dt((yn(),pb)),An((t=u(Un(this,16),29),t||pb),n))},s.Wh=function(n){var t;return t=this.i==null||this.q&&this.q.i!=0?null:QO(this,n),t||Tge(this,n)},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:RR(this,Pt(t));return;case 2:xK(this,Pt(t));return;case 5:N8(this,Pt(t));return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A),!this.A&&(this.A=new rs(Fo,this,7)),er(this.A,u(t,18));return;case 8:H1e(this,Re($e(t)));return;case 9:G1e(this,Re($e(t)));return;case 10:hS(tu(this)),er(tu(this),u(t,18));return;case 11:!this.q&&(this.q=new pe(yf,this,11,10)),kt(this.q),!this.q&&(this.q=new pe(yf,this,11,10)),er(this.q,u(t,18));return;case 21:!this.s&&(this.s=new pe(ns,this,21,17)),kt(this.s),!this.s&&(this.s=new pe(ns,this,21,17)),er(this.s,u(t,18));return;case 22:kt(Vu(this)),er(Vu(this),u(t,18));return}Jl(this,n-dt((yn(),pb)),An((i=u(Un(this,16),29),i||pb),n),t)},s.fi=function(){return yn(),pb},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,184)&&(u(this.Cb,184).tb=null),Mo(this,null);return;case 2:i8(this,null),U9(this,this.D);return;case 5:N8(this,null);return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A);return;case 8:H1e(this,!1);return;case 9:G1e(this,!1);return;case 10:this.u&&hS(this.u);return;case 11:!this.q&&(this.q=new pe(yf,this,11,10)),kt(this.q);return;case 21:!this.s&&(this.s=new pe(ns,this,21,17)),kt(this.s);return;case 22:this.n&&kt(this.n);return}Fl(this,n-dt((yn(),pb)),An((t=u(Un(this,16),29),t||pb),n))},s.mi=function(){var n,t;if(fv(this),lS(this),B0e(this),qW(this),vh(this),Nz(this),NW(this),vE(A3n(Ms(this))),this.s)for(n=0,t=this.s.i;n=0;--t)K(this,t);return hde(this,n)},s.Ek=function(){kt(this)},s.Xi=function(n,t){return gBe(this,n,t)},v($i,"EcoreEList",623),m(491,623,lu,_C),s.Ji=function(){return!1},s.Jj=function(){return this.c},s.Kj=function(){return!1},s.ml=function(){return!0},s.Qi=function(){return!0},s.Ui=function(n,t){return t},s.Wi=function(){return!1},s.c=0,v($i,"EObjectEList",491),m(81,491,lu,mr),s.Kj=function(){return!0},s.kl=function(){return!1},s.$k=function(){return!0},v($i,"EObjectContainmentEList",81),m(543,81,lu,$$),s.Li=function(){this.b=!0},s.Oj=function(){return this.b},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.b,this.b=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.b=!1},s.b=!1,v($i,"EObjectContainmentEList/Unsettable",543),m(1130,543,lu,$De),s.Ri=function(n,t){var i,r;return i=u(RE(this,n,t),87),Fs(this.e)&&s9(this,new tO(this.a,7,(yn(),Jan),ve(t),(r=i.c,X(r,88)?u(r,29):jf),n)),i},s.Sj=function(n,t){return fEn(this,u(n,87),t)},s.Tj=function(n,t){return aEn(this,u(n,87),t)},s.Uj=function(n,t,i){return hAn(this,u(n,87),u(t,87),i)},s.Gj=function(n,t,i,r,c){switch(n){case 3:return dE(this,n,t,i,r,this.i>1);case 5:return dE(this,n,t,i,r,this.i-u(i,16).gc()>0);default:return new L1(this.e,n,this.c,t,i,r,!0)}},s.Rj=function(){return!0},s.Oj=function(){return zQ(this)},s.Ek=function(){kt(this)},v(Fn,"EClassImpl/1",1130),m(1144,1143,eme),s.bj=function(n){var t,i,r,c,o,l,f;if(i=n.ej(),i!=8){if(r=KEn(n),r==0)switch(i){case 1:case 9:{f=n.ij(),f!=null&&(t=Ms(u(f,471)),!t.c&&(t.c=new Ol),sB(t.c,n.hj())),l=n.gj(),l!=null&&(c=u(l,471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),Et(t.c,u(n.hj(),29))));break}case 3:{l=n.gj(),l!=null&&(c=u(l,471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),Et(t.c,u(n.hj(),29))));break}case 5:{if(l=n.gj(),l!=null)for(o=u(l,18).Jc();o.Ob();)c=u(o.Pb(),471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),Et(t.c,u(n.hj(),29)));break}case 4:{f=n.ij(),f!=null&&(c=u(f,471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),sB(t.c,n.hj())));break}case 6:{if(f=n.ij(),f!=null)for(o=u(f,18).Jc();o.Ob();)c=u(o.Pb(),471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),sB(t.c,n.hj()));break}}this.ol(r)}},s.ol=function(n){AXe(this,n)},s.b=63,v(Fn,"ESuperAdapter",1144),m(1145,1144,eme,$Se),s.ol=function(n){H2(this,n)},v(Fn,"EClassImpl/10",1145),m(1134,699,lu),s.Ci=function(n,t){return lW(this,n,t)},s.Di=function(n){return uHe(this,n)},s.Ei=function(n,t){xO(this,n,t)},s.Fi=function(n){YC(this,n)},s.Yi=function(n){return phe(this,n)},s.Vi=function(n,t){return SY(this,n,t)},s.Uk=function(n,t){throw $(new _t)},s.Gi=function(){return new m4(this)},s.Hi=function(){return new kC(this)},s.Ii=function(n){return hO(this,n)},s.Vk=function(n,t){throw $(new _t)},s.Dk=function(n){return this},s.Oj=function(){return this.i!=0},s.Wb=function(n){throw $(new _t)},s.Ek=function(){throw $(new _t)},v($i,"EcoreEList/UnmodifiableEList",1134),m(333,1134,lu,N3),s.Wi=function(){return!1},v($i,"EcoreEList/UnmodifiableEList/FastCompare",333),m(1137,333,lu,Lze),s.bd=function(n){var t,i,r;if(X(n,179)&&(t=u(n,179),i=t.Jj(),i!=-1)){for(r=this.i;i4)if(this.dk(n)){if(this.$k()){if(r=u(n,52),i=r.Bh(),f=i==this.b&&(this.kl()?r.vh(r.Ch(),u(An(Go(this.b),this.Jj()).Fk(),29).ik())==Oc(u(An(Go(this.b),this.Jj()),19)).n:-1-r.Ch()==this.Jj()),this.ll()&&!f&&!i&&r.Gh()){for(c=0;c1||r==-1)):!1},s.kl=function(){var n,t,i;return t=An(Go(this.b),this.Jj()),X(t,103)?(n=u(t,19),i=Oc(n),!!i):!1},s.ll=function(){var n,t;return t=An(Go(this.b),this.Jj()),X(t,103)?(n=u(t,19),(n.Bb&Ec)!=0):!1},s.bd=function(n){var t,i,r,c;if(r=this.xj(n),r>=0)return r;if(this.ml()){for(i=0,c=this.Cj();i=0;--n)cN(this,n,this.vj(n));return this.Dj()},s.Oc=function(n){var t;if(this.ll())for(t=this.Cj()-1;t>=0;--t)cN(this,t,this.vj(t));return this.Ej(n)},s.Ek=function(){hS(this)},s.Xi=function(n,t){return B$e(this,n,t)},v($i,"DelegatingEcoreEList",744),m(1140,744,rme,VOe),s.oj=function(n,t){Ipn(this,n,u(t,29))},s.pj=function(n){ywn(this,u(n,29))},s.vj=function(n){var t,i;return t=u(K(Vu(this.a),n),87),i=t.c,X(i,88)?u(i,29):(yn(),jf)},s.Aj=function(n){var t,i;return t=u(U2(Vu(this.a),n),87),i=t.c,X(i,88)?u(i,29):(yn(),jf)},s.Bj=function(n,t){return zSn(this,n,u(t,29))},s.Ji=function(){return!1},s.Gj=function(n,t,i,r,c){return null},s.qj=function(){return new zSe(this)},s.rj=function(){kt(Vu(this.a))},s.sj=function(n){return DFe(this,n)},s.tj=function(n){var t,i;for(i=n.Jc();i.Ob();)if(t=i.Pb(),!DFe(this,t))return!1;return!0},s.uj=function(n){var t,i,r;if(X(n,16)&&(r=u(n,16),r.gc()==Vu(this.a).i)){for(t=r.Jc(),i=new ut(this);t.Ob();)if(ue(t.Pb())!==ue(ft(i)))return!1;return!0}return!1},s.wj=function(){var n,t,i,r,c;for(i=1,t=new ut(Vu(this.a));t.e!=t.i.gc();)n=u(ft(t),87),r=(c=n.c,X(c,88)?u(c,29):(yn(),jf)),i=31*i+(r?vw(r):0);return i},s.xj=function(n){var t,i,r,c;for(r=0,i=new ut(Vu(this.a));i.e!=i.i.gc();){if(t=u(ft(i),87),ue(n)===ue((c=t.c,X(c,88)?u(c,29):(yn(),jf))))return r;++r}return-1},s.yj=function(){return Vu(this.a).i==0},s.zj=function(){return null},s.Cj=function(){return Vu(this.a).i},s.Dj=function(){var n,t,i,r,c,o;for(o=Vu(this.a).i,c=oe(Ar,On,1,o,5,1),i=0,t=new ut(Vu(this.a));t.e!=t.i.gc();)n=u(ft(t),87),c[i++]=(r=n.c,X(r,88)?u(r,29):(yn(),jf));return c},s.Ej=function(n){var t,i,r,c,o,l,f;for(f=Vu(this.a).i,n.lengthf&&tr(n,f,null),r=0,i=new ut(Vu(this.a));i.e!=i.i.gc();)t=u(ft(i),87),o=(l=t.c,X(l,88)?u(l,29):(yn(),jf)),tr(n,r++,o);return n},s.Fj=function(){var n,t,i,r,c;for(c=new pd,c.a+="[",n=Vu(this.a),t=0,r=Vu(this.a).i;t>16,c>=0?QQ(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,6,i);case 9:return!this.a&&(this.a=new pe(ed,this,9,5)),To(this.a,n,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),mb)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),mb)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 6:return dl(this,null,6,i);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),vc(this.A,n,i);case 9:return!this.a&&(this.a=new pe(ed,this,9,5)),vc(this.a,n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),mb)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),mb)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.D!=null&&this.D==this.F;case 3:return!!Fw(this);case 4:return!!O1e(this);case 5:return this.F!=null&&this.F!=this.D&&this.F!=this.B;case 6:return!!R9(this);case 7:return!!this.A&&this.A.i!=0;case 8:return(this.Bb&256)==0;case 9:return!!this.a&&this.a.i!=0}return Ll(this,n-dt((yn(),mb)),An((t=u(Un(this,16),29),t||mb),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:RR(this,Pt(t));return;case 2:xK(this,Pt(t));return;case 5:N8(this,Pt(t));return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A),!this.A&&(this.A=new rs(Fo,this,7)),er(this.A,u(t,18));return;case 8:FB(this,Re($e(t)));return;case 9:!this.a&&(this.a=new pe(ed,this,9,5)),kt(this.a),!this.a&&(this.a=new pe(ed,this,9,5)),er(this.a,u(t,18));return}Jl(this,n-dt((yn(),mb)),An((i=u(Un(this,16),29),i||mb),n),t)},s.fi=function(){return yn(),mb},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,184)&&(u(this.Cb,184).tb=null),Mo(this,null);return;case 2:i8(this,null),U9(this,this.D);return;case 5:N8(this,null);return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A);return;case 8:FB(this,!0);return;case 9:!this.a&&(this.a=new pe(ed,this,9,5)),kt(this.a);return}Fl(this,n-dt((yn(),mb)),An((t=u(Un(this,16),29),t||mb),n))},s.mi=function(){var n,t;if(this.a)for(n=0,t=this.a.i;n>16==5?u(this.Cb,675):null}return Pl(this,n-dt((yn(),Zd)),An((r=u(Un(this,16),29),r||Zd),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 5:return this.Cb&&(i=(c=this.Db>>16,c>=0?IHe(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,5,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),Zd)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),Zd)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 5:return dl(this,null,5,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),Zd)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),Zd)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.d!=0;case 3:return!!this.b;case 4:return this.c!=null;case 5:return!!(this.Db>>16==5&&u(this.Cb,675))}return Ll(this,n-dt((yn(),Zd)),An((t=u(Un(this,16),29),t||Zd),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Pt(t));return;case 2:NY(this,u(t,15).a);return;case 3:Pqe(this,u(t,2001));return;case 4:IY(this,Pt(t));return}Jl(this,n-dt((yn(),Zd)),An((i=u(Un(this,16),29),i||Zd),n),t)},s.fi=function(){return yn(),Zd},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Mo(this,null);return;case 2:NY(this,0);return;case 3:Pqe(this,null);return;case 4:IY(this,null);return}Fl(this,n-dt((yn(),Zd)),An((t=u(Un(this,16),29),t||Zd),n))},s.Ib=function(){var n;return n=this.c,n??this.zb},s.b=null,s.c=null,s.d=0,v(Fn,"EEnumLiteralImpl",568);var QBn=Hi(Fn,"EFactoryImpl/InternalEDateTimeFormat");m(485,1,{2076:1},UT),v(Fn,"EFactoryImpl/1ClientInternalEDateTimeFormat",485),m(248,118,{109:1,94:1,93:1,87:1,57:1,114:1,52:1,100:1,248:1,117:1,118:1},hw),s.zh=function(n,t,i){var r;return i=dl(this,n,t,i),this.e&&X(n,179)&&(r=Oz(this,this.e),r!=this.c&&(i=D8(this,r,i))),i},s.Ih=function(n,t,i){var r;switch(n){case 0:return this.f;case 1:return!this.d&&(this.d=new mr(Rc,this,1)),this.d;case 2:return t?Jz(this):this.c;case 3:return this.b;case 4:return this.e;case 5:return t?HQ(this):this.a}return Pl(this,n-dt((yn(),yp)),An((r=u(Un(this,16),29),r||yp),n),t,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return mFe(this,null,i);case 1:return!this.d&&(this.d=new mr(Rc,this,1)),vc(this.d,n,i);case 3:return pFe(this,null,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),yp)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),yp)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.f;case 1:return!!this.d&&this.d.i!=0;case 2:return!!this.c;case 3:return!!this.b;case 4:return!!this.e;case 5:return!!this.a}return Ll(this,n-dt((yn(),yp)),An((t=u(Un(this,16),29),t||yp),n))},s.$h=function(n,t){var i;switch(n){case 0:WHe(this,u(t,87));return;case 1:!this.d&&(this.d=new mr(Rc,this,1)),kt(this.d),!this.d&&(this.d=new mr(Rc,this,1)),er(this.d,u(t,18));return;case 3:u0e(this,u(t,87));return;case 4:x0e(this,u(t,834));return;case 5:q9(this,u(t,143));return}Jl(this,n-dt((yn(),yp)),An((i=u(Un(this,16),29),i||yp),n),t)},s.fi=function(){return yn(),yp},s.hi=function(n){var t;switch(n){case 0:WHe(this,null);return;case 1:!this.d&&(this.d=new mr(Rc,this,1)),kt(this.d);return;case 3:u0e(this,null);return;case 4:x0e(this,null);return;case 5:q9(this,null);return}Fl(this,n-dt((yn(),yp)),An((t=u(Un(this,16),29),t||yp),n))},s.Ib=function(){var n;return n=new il(Jf(this)),n.a+=" (expression: ",YW(this,n),n.a+=")",n.a};var Q8e;v(Fn,"EGenericTypeImpl",248),m(2029,2024,KF),s.Ei=function(n,t){QOe(this,n,t)},s.Uk=function(n,t){return QOe(this,this.gc(),n),t},s.Yi=function(n){return Yu(this.nj(),n)},s.Gi=function(){return this.Hi()},s.nj=function(){return new GSe(this)},s.Hi=function(){return this.Ii(0)},s.Ii=function(n){return this.nj().dd(n)},s.Vk=function(n,t){return P2(this,n,!0),t},s.Ri=function(n,t){var i,r;return r=eW(this,t),i=this.dd(n),i.Rb(r),r},s.Si=function(n,t){var i;P2(this,t,!0),i=this.dd(n),i.Rb(t)},v($i,"AbstractSequentialInternalEList",2029),m(482,2029,KF,jC),s.Yi=function(n){return Yu(this.nj(),n)},s.Gi=function(){return this.b==null?(kd(),kd(),nI):this.ql()},s.nj=function(){return new wCe(this.a,this.b)},s.Hi=function(){return this.b==null?(kd(),kd(),nI):this.ql()},s.Ii=function(n){var t,i;if(this.b==null){if(n<0||n>1)throw $(new jo($S+n+", size=0"));return kd(),kd(),nI}for(i=this.ql(),t=0;t0;)if(t=this.c[--this.d],(!this.e||t.nk()!=U7||t.Jj()!=0)&&(!this.tl()||this.b.Uh(t))){if(o=this.b.Kh(t,this.sl()),this.f=(Cc(),u(t,69).vk()),this.f||t.Hk()){if(this.sl()?(r=u(o,16),this.k=r):(r=u(o,72),this.k=this.j=r),X(this.k,59)?(this.o=this.k.gc(),this.n=this.o):this.p=this.j?this.j.Ii(this.k.gc()):this.k.dd(this.k.gc()),this.p?YGe(this,this.p):uqe(this))return c=this.p?this.p.Ub():this.j?this.j.Yi(--this.n):this.k.Xb(--this.n),this.f?(n=u(c,75),n.Jk(),i=n.kd(),this.i=i):(i=c,this.i=i),this.g=-3,!0}else if(o!=null)return this.k=null,this.p=null,i=o,this.i=i,this.g=-2,!0}return this.k=null,this.p=null,this.g=-1,!1}else return c=this.p?this.p.Ub():this.j?this.j.Yi(--this.n):this.k.Xb(--this.n),this.f?(n=u(c,75),n.Jk(),i=n.kd(),this.i=i):(i=c,this.i=i),this.g=-3,!0}},s.Pb=function(){return OB(this)},s.Tb=function(){return this.a},s.Ub=function(){var n;if(this.g<-1||this.Sb())return--this.a,this.g=0,n=this.i,this.Sb(),n;throw $(new au)},s.Vb=function(){return this.a-1},s.Qb=function(){throw $(new _t)},s.sl=function(){return!1},s.Wb=function(n){throw $(new _t)},s.tl=function(){return!0},s.a=0,s.d=0,s.f=!1,s.g=0,s.n=0,s.o=0;var nI;v($i,"EContentsEList/FeatureIteratorImpl",287),m(700,287,VF,ple),s.sl=function(){return!0},v($i,"EContentsEList/ResolvingFeatureIteratorImpl",700),m(1147,700,VF,IOe),s.tl=function(){return!1},v(Fn,"ENamedElementImpl/1/1",1147),m(1148,287,VF,_Oe),s.tl=function(){return!1},v(Fn,"ENamedElementImpl/1/2",1148),m(39,151,RN,M2,iY,Ir,pY,L1,Pf,Che,vLe,Ohe,yLe,Gae,kLe,Ihe,jLe,qae,ELe,Nhe,SLe,uE,tO,$V,Dhe,xLe,Uae,ALe),s.Ij=function(){return ahe(this)},s.Pj=function(){var n;return n=ahe(this),n?n.gk():null},s.fj=function(n){return this.b==-1&&this.a&&(this.b=this.c.Eh(this.a.Jj(),this.a.nk())),this.c.vh(this.b,n)},s.hj=function(){return this.c},s.Qj=function(){var n;return n=ahe(this),n?n.rk():!1},s.b=-1,v(Fn,"ENotificationImpl",39),m(403,293,{109:1,94:1,93:1,158:1,197:1,57:1,62:1,114:1,470:1,52:1,100:1,161:1,403:1,293:1,117:1,118:1},vX),s.xh=function(n){return LHe(this,n)},s.Ih=function(n,t,i){var r,c,o;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),o=this.t,o>1||o==-1;case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return this.Db>>16==10?u(this.Cb,29):null;case 11:return!this.d&&(this.d=new rs(Fo,this,11)),this.d;case 12:return!this.c&&(this.c=new pe(vp,this,12,10)),this.c;case 13:return!this.a&&(this.a=new AC(this,this)),this.a;case 14:return Cs(this)}return Pl(this,n-dt((yn(),e0)),An((r=u(Un(this,16),29),r||e0),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 10:return this.Cb&&(i=(c=this.Db>>16,c>=0?LHe(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,10,i);case 12:return!this.c&&(this.c=new pe(vp,this,12,10)),To(this.c,n,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),e0)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),e0)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 9:return EV(this,i);case 10:return dl(this,null,10,i);case 11:return!this.d&&(this.d=new rs(Fo,this,11)),vc(this.d,n,i);case 12:return!this.c&&(this.c=new pe(vp,this,12,10)),vc(this.c,n,i);case 14:return vc(Cs(this),n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),e0)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),e0)),n,i)},s.Th=function(n){var t,i,r;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return r=this.t,r>1||r==-1;case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return!!(this.Db>>16==10&&u(this.Cb,29));case 11:return!!this.d&&this.d.i!=0;case 12:return!!this.c&&this.c.i!=0;case 13:return!!this.a&&Cs(this.a.a).i!=0&&!(this.b&&FQ(this.b));case 14:return!!this.b&&FQ(this.b)}return Ll(this,n-dt((yn(),e0)),An((t=u(Un(this,16),29),t||e0),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Cd(this,u(t,15).a);return;case 5:O2(this,u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return;case 11:!this.d&&(this.d=new rs(Fo,this,11)),kt(this.d),!this.d&&(this.d=new rs(Fo,this,11)),er(this.d,u(t,18));return;case 12:!this.c&&(this.c=new pe(vp,this,12,10)),kt(this.c),!this.c&&(this.c=new pe(vp,this,12,10)),er(this.c,u(t,18));return;case 13:!this.a&&(this.a=new AC(this,this)),hS(this.a),!this.a&&(this.a=new AC(this,this)),er(this.a,u(t,18));return;case 14:kt(Cs(this)),er(Cs(this),u(t,18));return}Jl(this,n-dt((yn(),e0)),An((i=u(Un(this,16),29),i||e0),n),t)},s.fi=function(){return yn(),e0},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Mo(this,null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Cd(this,0);return;case 5:O2(this,1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return;case 11:!this.d&&(this.d=new rs(Fo,this,11)),kt(this.d);return;case 12:!this.c&&(this.c=new pe(vp,this,12,10)),kt(this.c);return;case 13:this.a&&hS(this.a);return;case 14:this.b&&kt(this.b);return}Fl(this,n-dt((yn(),e0)),An((t=u(Un(this,16),29),t||e0),n))},s.mi=function(){var n,t;if(this.c)for(n=0,t=this.c.i;nf&&tr(n,f,null),r=0,i=new ut(Cs(this.a));i.e!=i.i.gc();)t=u(ft(i),87),o=(l=t.c,l||(yn(),ih)),tr(n,r++,o);return n},s.Fj=function(){var n,t,i,r,c;for(c=new pd,c.a+="[",n=Cs(this.a),t=0,r=Cs(this.a).i;t1);case 5:return dE(this,n,t,i,r,this.i-u(i,16).gc()>0);default:return new L1(this.e,n,this.c,t,i,r,!0)}},s.Rj=function(){return!0},s.Oj=function(){return FQ(this)},s.Ek=function(){kt(this)},v(Fn,"EOperationImpl/2",1331),m(493,1,{1999:1,493:1},VTe),v(Fn,"EPackageImpl/1",493),m(14,81,lu,pe),s.gl=function(){return this.d},s.hl=function(){return this.b},s.kl=function(){return!0},s.b=0,v($i,"EObjectContainmentWithInverseEList",14),m(361,14,lu,y4),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectContainmentWithInverseEList/Resolving",361),m(312,361,lu,m2),s.Li=function(){this.a.tb=null},v(Fn,"EPackageImpl/2",312),m(1243,1,{},Ss),v(Fn,"EPackageImpl/3",1243),m(721,44,bv,yoe),s._b=function(n){return $r(n)?RV(this,n):!!Xc(this.f,n)},v(Fn,"EPackageRegistryImpl",721),m(503,293,{109:1,94:1,93:1,158:1,197:1,57:1,2078:1,114:1,470:1,52:1,100:1,161:1,503:1,293:1,117:1,118:1},yX),s.xh=function(n){return PHe(this,n)},s.Ih=function(n,t,i){var r,c,o;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),o=this.t,o>1||o==-1;case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return this.Db>>16==10?u(this.Cb,62):null}return Pl(this,n-dt((yn(),Fm)),An((r=u(Un(this,16),29),r||Fm),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 10:return this.Cb&&(i=(c=this.Db>>16,c>=0?PHe(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,10,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),Fm)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),Fm)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 9:return EV(this,i);case 10:return dl(this,null,10,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),Fm)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),Fm)),n,i)},s.Th=function(n){var t,i,r;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return r=this.t,r>1||r==-1;case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return!!(this.Db>>16==10&&u(this.Cb,62))}return Ll(this,n-dt((yn(),Fm)),An((t=u(Un(this,16),29),t||Fm),n))},s.fi=function(){return yn(),Fm},v(Fn,"EParameterImpl",503),m(103,451,{109:1,94:1,93:1,158:1,197:1,57:1,19:1,179:1,69:1,114:1,470:1,52:1,100:1,161:1,103:1,451:1,293:1,117:1,118:1,682:1},kle),s.Ih=function(n,t,i){var r,c,o,l;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),l=this.t,l>1||l==-1;case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return Pn(),(this.Bb&qf)!=0;case 11:return Pn(),(this.Bb&U0)!=0;case 12:return Pn(),(this.Bb&W2)!=0;case 13:return this.j;case 14:return S8(this);case 15:return Pn(),(this.Bb&as)!=0;case 16:return Pn(),(this.Bb&yh)!=0;case 17:return E2(this);case 18:return Pn(),(this.Bb&Bu)!=0;case 19:return Pn(),o=Oc(this),!!(o&&(o.Bb&Bu)!=0);case 20:return Pn(),(this.Bb&Ec)!=0;case 21:return t?Oc(this):this.b;case 22:return t?d1e(this):HPe(this);case 23:return!this.a&&(this.a=new $3(Rm,this,23)),this.a}return Pl(this,n-dt((yn(),Wv)),An((r=u(Un(this,16),29),r||Wv),n),t,i)},s.Th=function(n){var t,i,r,c;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return c=this.t,c>1||c==-1;case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return(this.Bb&qf)==0;case 11:return(this.Bb&U0)!=0;case 12:return(this.Bb&W2)!=0;case 13:return this.j!=null;case 14:return S8(this)!=null;case 15:return(this.Bb&as)!=0;case 16:return(this.Bb&yh)!=0;case 17:return!!E2(this);case 18:return(this.Bb&Bu)!=0;case 19:return r=Oc(this),!!r&&(r.Bb&Bu)!=0;case 20:return(this.Bb&Ec)==0;case 21:return!!this.b;case 22:return!!HPe(this);case 23:return!!this.a&&this.a.i!=0}return Ll(this,n-dt((yn(),Wv)),An((t=u(Un(this,16),29),t||Wv),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:UV(this,Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Cd(this,u(t,15).a);return;case 5:O2(this,u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return;case 10:o8(this,Re($e(t)));return;case 11:f8(this,Re($e(t)));return;case 12:l8(this,Re($e(t)));return;case 13:Ise(this,Pt(t));return;case 15:s8(this,Re($e(t)));return;case 16:a8(this,Re($e(t)));return;case 18:D4n(this,Re($e(t)));return;case 20:Y1e(this,Re($e(t)));return;case 21:Khe(this,u(t,19));return;case 23:!this.a&&(this.a=new $3(Rm,this,23)),kt(this.a),!this.a&&(this.a=new $3(Rm,this,23)),er(this.a,u(t,18));return}Jl(this,n-dt((yn(),Wv)),An((i=u(Un(this,16),29),i||Wv),n),t)},s.fi=function(){return yn(),Wv},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,88)&&H2(Ms(u(this.Cb,88)),4),Mo(this,null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Cd(this,0);return;case 5:O2(this,1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return;case 10:o8(this,!0);return;case 11:f8(this,!1);return;case 12:l8(this,!1);return;case 13:this.i=null,EB(this,null);return;case 15:s8(this,!1);return;case 16:a8(this,!1);return;case 18:Q1e(this,!1),X(this.Cb,88)&&H2(Ms(u(this.Cb,88)),2);return;case 20:Y1e(this,!0);return;case 21:Khe(this,null);return;case 23:!this.a&&(this.a=new $3(Rm,this,23)),kt(this.a);return}Fl(this,n-dt((yn(),Wv)),An((t=u(Un(this,16),29),t||Wv),n))},s.mi=function(){d1e(this),L9(Vc((ls(),nc),this)),ff(this),this.Bb|=1},s.sk=function(){return Oc(this)},s.Zk=function(){var n;return n=Oc(this),!!n&&(n.Bb&Bu)!=0},s.$k=function(){return(this.Bb&Bu)!=0},s._k=function(){return(this.Bb&Ec)!=0},s.Wk=function(n,t){return this.c=null,z1e(this,n,t)},s.Ib=function(){var n;return(this.Db&64)!=0?zz(this):(n=new cf(zz(this)),n.a+=" (containment: ",md(n,(this.Bb&Bu)!=0),n.a+=", resolveProxies: ",md(n,(this.Bb&Ec)!=0),n.a+=")",n.a)},v(Fn,"EReferenceImpl",103),m(549,118,{109:1,45:1,94:1,93:1,136:1,57:1,114:1,52:1,100:1,549:1,117:1,118:1},Ph),s.Fb=function(n){return this===n},s.jd=function(){return this.b},s.kd=function(){return this.c},s.Hb=function(){return vw(this)},s.Ai=function(n){K3n(this,Pt(n))},s.ld=function(n){return $3n(this,Pt(n))},s.Ih=function(n,t,i){var r;switch(n){case 0:return this.b;case 1:return this.c}return Pl(this,n-dt((yn(),Ac)),An((r=u(Un(this,16),29),r||Ac),n),t,i)},s.Th=function(n){var t;switch(n){case 0:return this.b!=null;case 1:return this.c!=null}return Ll(this,n-dt((yn(),Ac)),An((t=u(Un(this,16),29),t||Ac),n))},s.$h=function(n,t){var i;switch(n){case 0:V3n(this,Pt(t));return;case 1:Jhe(this,Pt(t));return}Jl(this,n-dt((yn(),Ac)),An((i=u(Un(this,16),29),i||Ac),n),t)},s.fi=function(){return yn(),Ac},s.hi=function(n){var t;switch(n){case 0:qhe(this,null);return;case 1:Jhe(this,null);return}Fl(this,n-dt((yn(),Ac)),An((t=u(Un(this,16),29),t||Ac),n))},s.yi=function(){var n;return this.a==-1&&(n=this.b,this.a=n==null?0:Od(n)),this.a},s.zi=function(n){this.a=n},s.Ib=function(){var n;return(this.Db&64)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (key: ",Bc(n,this.b),n.a+=", value: ",Bc(n,this.c),n.a+=")",n.a)},s.a=-1,s.b=null,s.c=null;var Iu=v(Fn,"EStringToStringMapEntryImpl",549),Wan=Hi($i,"FeatureMap/Entry/Internal");m(562,1,YF),s.vl=function(n){return this.wl(u(n,52))},s.wl=function(n){return this.vl(n)},s.Fb=function(n){var t,i;return this===n?!0:X(n,75)?(t=u(n,75),t.Jk()==this.c?(i=this.kd(),i==null?t.kd()==null:di(i,t.kd())):!1):!1},s.Jk=function(){return this.c},s.Hb=function(){var n;return n=this.kd(),Oi(this.c)^(n==null?0:Oi(n))},s.Ib=function(){var n,t;return n=this.c,t=sl(n.ok()).vi(),n.ve(),(t!=null&&t.length!=0?t+":"+n.ve():n.ve())+"="+this.kd()},v(Fn,"EStructuralFeatureImpl/BasicFeatureMapEntry",562),m(777,562,YF,Mle),s.wl=function(n){return new Mle(this.c,n)},s.kd=function(){return this.a},s.xl=function(n,t,i){return y7n(this,n,this.a,t,i)},s.yl=function(n,t,i){return k7n(this,n,this.a,t,i)},v(Fn,"EStructuralFeatureImpl/ContainmentUpdatingFeatureMapEntry",777),m(1304,1,{},YTe),s.wk=function(n,t,i,r,c){var o;return o=u(F9(n,this.b),219),o.Wl(this.a).Dk(r)},s.xk=function(n,t,i,r,c){var o;return o=u(F9(n,this.b),219),o.Nl(this.a,r,c)},s.yk=function(n,t,i,r,c){var o;return o=u(F9(n,this.b),219),o.Ol(this.a,r,c)},s.zk=function(n,t,i){var r;return r=u(F9(n,this.b),219),r.Wl(this.a).Oj()},s.Ak=function(n,t,i,r){var c;c=u(F9(n,this.b),219),c.Wl(this.a).Wb(r)},s.Bk=function(n,t,i){return u(F9(n,this.b),219).Wl(this.a)},s.Ck=function(n,t,i){var r;r=u(F9(n,this.b),219),r.Wl(this.a).Ek()},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateFeatureMapDelegator",1304),m(89,1,{},Ed,Jb,xd,Kb),s.wk=function(n,t,i,r,c){var o;if(o=t.ii(i),o==null&&t.ji(i,o=Wz(this,n)),!c)switch(this.e){case 50:case 41:return u(o,586)._j();case 40:return u(o,219).Tl()}return o},s.xk=function(n,t,i,r,c){var o,l;return l=t.ii(i),l==null&&t.ji(i,l=Wz(this,n)),o=u(l,72).Uk(r,c),o},s.yk=function(n,t,i,r,c){var o;return o=t.ii(i),o!=null&&(c=u(o,72).Vk(r,c)),c},s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null&&u(r,77).Oj()},s.Ak=function(n,t,i,r){var c;c=u(t.ii(i),77),!c&&t.ji(i,c=Wz(this,n)),c.Wb(r)},s.Bk=function(n,t,i){var r,c;return c=t.ii(i),c==null&&t.ji(i,c=Wz(this,n)),X(c,77)?u(c,77):(r=u(t.ii(i),16),new JSe(r))},s.Ck=function(n,t,i){var r;r=u(t.ii(i),77),!r&&t.ji(i,r=Wz(this,n)),r.Ek()},s.b=0,s.e=0,v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateMany",89),m(498,1,{}),s.xk=function(n,t,i,r,c){throw $(new _t)},s.yk=function(n,t,i,r,c){throw $(new _t)},s.Bk=function(n,t,i){return new XIe(this,n,t,i)};var h1;v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingle",498),m(1321,1,_ne,XIe),s.Dk=function(n){return this.a.wk(this.c,this.d,this.b,n,!0)},s.Oj=function(){return this.a.zk(this.c,this.d,this.b)},s.Wb=function(n){this.a.Ak(this.c,this.d,this.b,n)},s.Ek=function(){this.a.Ck(this.c,this.d,this.b)},s.b=0,v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingle/1",1321),m(770,498,{},bae),s.wk=function(n,t,i,r,c){return $W(n,n.Mh(),n.Ch())==this.b?this._k()&&r?SW(n):n.Mh():null},s.xk=function(n,t,i,r,c){var o,l;return n.Mh()&&(c=(o=n.Ch(),o>=0?n.xh(c):n.Mh().Qh(n,-1-o,null,c))),l=zi(n.Ah(),this.e),n.zh(r,l,c)},s.yk=function(n,t,i,r,c){var o;return o=zi(n.Ah(),this.e),n.zh(null,o,c)},s.zk=function(n,t,i){var r;return r=zi(n.Ah(),this.e),!!n.Mh()&&n.Ch()==r},s.Ak=function(n,t,i,r){var c,o,l,f,h;if(r!=null&&!FW(this.a,r))throw $(new l9(QF+(X(r,57)?c0e(u(r,57).Ah()):xhe(Us(r)))+WF+this.a+"'"));if(c=n.Mh(),l=zi(n.Ah(),this.e),ue(r)!==ue(c)||n.Ch()!=l&&r!=null){if(w8(n,u(r,57)))throw $(new Gn(LS+n.Ib()));h=null,c&&(h=(o=n.Ch(),o>=0?n.xh(h):n.Mh().Qh(n,-1-o,null,h))),f=u(r,52),f&&(h=f.Oh(n,zi(f.Ah(),this.b),null,h)),h=n.zh(f,l,h),h&&h.mj()}else n.sh()&&n.th()&&ai(n,new Ir(n,1,l,r,r))},s.Ck=function(n,t,i){var r,c,o,l;r=n.Mh(),r?(l=(c=n.Ch(),c>=0?n.xh(null):n.Mh().Qh(n,-1-c,null,null)),o=zi(n.Ah(),this.e),l=n.zh(null,o,l),l&&l.mj()):n.sh()&&n.th()&&ai(n,new uE(n,1,this.e,null,null))},s._k=function(){return!1},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleContainer",770),m(1305,770,{},BNe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleContainerResolving",1305),m(560,498,{}),s.wk=function(n,t,i,r,c){var o;return o=t.ii(i),o==null?this.b:ue(o)===ue(h1)?null:o},s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null&&(ue(r)===ue(h1)||!di(r,this.b))},s.Ak=function(n,t,i,r){var c,o;n.sh()&&n.th()?(c=(o=t.ii(i),o==null?this.b:ue(o)===ue(h1)?null:o),r==null?this.c!=null?(t.ji(i,null),r=this.b):this.b!=null?t.ji(i,h1):t.ji(i,null):(this.zl(r),t.ji(i,r)),ai(n,this.d.Al(n,1,this.e,c,r))):r==null?this.c!=null?t.ji(i,null):this.b!=null?t.ji(i,h1):t.ji(i,null):(this.zl(r),t.ji(i,r))},s.Ck=function(n,t,i){var r,c;n.sh()&&n.th()?(r=(c=t.ii(i),c==null?this.b:ue(c)===ue(h1)?null:c),t.ki(i),ai(n,this.d.Al(n,1,this.e,r,this.b))):t.ki(i)},s.zl=function(n){throw $(new WSe)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData",560),m(Ev,1,{},cw),s.Al=function(n,t,i,r,c){return new uE(n,t,i,r,c)},s.Bl=function(n,t,i,r,c,o){return new $V(n,t,i,r,c,o)};var W8e,Z8e,e7e,n7e,t7e,i7e,r7e,Jce,c7e;v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator",Ev),m(1322,Ev,{},jL),s.Al=function(n,t,i,r,c){return new Uae(n,t,i,Re($e(r)),Re($e(c)))},s.Bl=function(n,t,i,r,c,o){return new ALe(n,t,i,Re($e(r)),Re($e(c)),o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/1",1322),m(1323,Ev,{},EL),s.Al=function(n,t,i,r,c){return new Che(n,t,i,u(r,221).a,u(c,221).a)},s.Bl=function(n,t,i,r,c,o){return new vLe(n,t,i,u(r,221).a,u(c,221).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/2",1323),m(1324,Ev,{},SL),s.Al=function(n,t,i,r,c){return new Ohe(n,t,i,u(r,180).a,u(c,180).a)},s.Bl=function(n,t,i,r,c,o){return new yLe(n,t,i,u(r,180).a,u(c,180).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/3",1324),m(1325,Ev,{},dU),s.Al=function(n,t,i,r,c){return new Gae(n,t,i,ne(re(r)),ne(re(c)))},s.Bl=function(n,t,i,r,c,o){return new kLe(n,t,i,ne(re(r)),ne(re(c)),o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/4",1325),m(1326,Ev,{},Jk),s.Al=function(n,t,i,r,c){return new Ihe(n,t,i,u(r,164).a,u(c,164).a)},s.Bl=function(n,t,i,r,c,o){return new jLe(n,t,i,u(r,164).a,u(c,164).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/5",1326),m(1327,Ev,{},$h),s.Al=function(n,t,i,r,c){return new qae(n,t,i,u(r,15).a,u(c,15).a)},s.Bl=function(n,t,i,r,c,o){return new ELe(n,t,i,u(r,15).a,u(c,15).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/6",1327),m(1328,Ev,{},h0),s.Al=function(n,t,i,r,c){return new Nhe(n,t,i,u(r,190).a,u(c,190).a)},s.Bl=function(n,t,i,r,c,o){return new SLe(n,t,i,u(r,190).a,u(c,190).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/7",1328),m(1329,Ev,{},xL),s.Al=function(n,t,i,r,c){return new Dhe(n,t,i,u(r,191).a,u(c,191).a)},s.Bl=function(n,t,i,r,c,o){return new xLe(n,t,i,u(r,191).a,u(c,191).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/8",1329),m(1307,560,{},QIe),s.zl=function(n){if(!this.a.dk(n))throw $(new l9(QF+Us(n)+WF+this.a+"'"))},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataDynamic",1307),m(1308,560,{},LDe),s.zl=function(n){},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataStatic",1308),m(771,560,{}),s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null},s.Ak=function(n,t,i,r){var c,o;n.sh()&&n.th()?(c=!0,o=t.ii(i),o==null?(c=!1,o=this.b):ue(o)===ue(h1)&&(o=null),r==null?this.c!=null?(t.ji(i,null),r=this.b):t.ji(i,h1):(this.zl(r),t.ji(i,r)),ai(n,this.d.Bl(n,1,this.e,o,r,!c))):r==null?this.c!=null?t.ji(i,null):t.ji(i,h1):(this.zl(r),t.ji(i,r))},s.Ck=function(n,t,i){var r,c;n.sh()&&n.th()?(r=!0,c=t.ii(i),c==null?(r=!1,c=this.b):ue(c)===ue(h1)&&(c=null),t.ki(i),ai(n,this.d.Bl(n,2,this.e,c,this.b,r))):t.ki(i)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettable",771),m(1309,771,{},WIe),s.zl=function(n){if(!this.a.dk(n))throw $(new l9(QF+Us(n)+WF+this.a+"'"))},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettableDynamic",1309),m(1310,771,{},PDe),s.zl=function(n){},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettableStatic",1310),m(402,498,{},sR),s.wk=function(n,t,i,r,c){var o,l,f,h,b;if(b=t.ii(i),this.rk()&&ue(b)===ue(h1))return null;if(this._k()&&r&&b!=null){if(f=u(b,52),f.Sh()&&(h=$0(n,f),f!=h)){if(!FW(this.a,h))throw $(new l9(QF+Us(h)+WF+this.a+"'"));t.ji(i,b=h),this.$k()&&(o=u(h,52),l=f.Qh(n,this.b?zi(f.Ah(),this.b):-1-zi(n.Ah(),this.e),null,null),!o.Mh()&&(l=o.Oh(n,this.b?zi(o.Ah(),this.b):-1-zi(n.Ah(),this.e),null,l)),l&&l.mj()),n.sh()&&n.th()&&ai(n,new uE(n,9,this.e,f,h))}return b}else return b},s.xk=function(n,t,i,r,c){var o,l;return l=t.ii(i),ue(l)===ue(h1)&&(l=null),t.ji(i,r),this.Kj()?ue(l)!==ue(r)&&l!=null&&(o=u(l,52),c=o.Qh(n,zi(o.Ah(),this.b),null,c)):this.$k()&&l!=null&&(c=u(l,52).Qh(n,-1-zi(n.Ah(),this.e),null,c)),n.sh()&&n.th()&&(!c&&(c=new m0(4)),c.lj(new uE(n,1,this.e,l,r))),c},s.yk=function(n,t,i,r,c){var o;return o=t.ii(i),ue(o)===ue(h1)&&(o=null),t.ki(i),n.sh()&&n.th()&&(!c&&(c=new m0(4)),this.rk()?c.lj(new uE(n,2,this.e,o,null)):c.lj(new uE(n,1,this.e,o,null))),c},s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null},s.Ak=function(n,t,i,r){var c,o,l,f,h;if(r!=null&&!FW(this.a,r))throw $(new l9(QF+(X(r,57)?c0e(u(r,57).Ah()):xhe(Us(r)))+WF+this.a+"'"));h=t.ii(i),f=h!=null,this.rk()&&ue(h)===ue(h1)&&(h=null),l=null,this.Kj()?ue(h)!==ue(r)&&(h!=null&&(c=u(h,52),l=c.Qh(n,zi(c.Ah(),this.b),null,l)),r!=null&&(c=u(r,52),l=c.Oh(n,zi(c.Ah(),this.b),null,l))):this.$k()&&ue(h)!==ue(r)&&(h!=null&&(l=u(h,52).Qh(n,-1-zi(n.Ah(),this.e),null,l)),r!=null&&(l=u(r,52).Oh(n,-1-zi(n.Ah(),this.e),null,l))),r==null&&this.rk()?t.ji(i,h1):t.ji(i,r),n.sh()&&n.th()?(o=new $V(n,1,this.e,h,r,this.rk()&&!f),l?(l.lj(o),l.mj()):ai(n,o)):l&&l.mj()},s.Ck=function(n,t,i){var r,c,o,l,f;f=t.ii(i),l=f!=null,this.rk()&&ue(f)===ue(h1)&&(f=null),o=null,f!=null&&(this.Kj()?(r=u(f,52),o=r.Qh(n,zi(r.Ah(),this.b),null,o)):this.$k()&&(o=u(f,52).Qh(n,-1-zi(n.Ah(),this.e),null,o))),t.ki(i),n.sh()&&n.th()?(c=new $V(n,this.rk()?2:1,this.e,f,null,l),o?(o.lj(c),o.mj()):ai(n,c)):o&&o.mj()},s.Kj=function(){return!1},s.$k=function(){return!1},s._k=function(){return!1},s.rk=function(){return!1},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObject",402),m(561,402,{},RK),s.$k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainment",561),m(1313,561,{},POe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentResolving",1313),m(773,561,{},mle),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentUnsettable",773),m(1315,773,{},$Oe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentUnsettableResolving",1315),m(638,561,{},WK),s.Kj=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverse",638),m(1314,638,{},zNe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseResolving",1314),m(774,638,{},Zle),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseUnsettable",774),m(1316,774,{},FNe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseUnsettableResolving",1316),m(639,402,{},vle),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolving",639),m(1317,639,{},ROe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingUnsettable",1317),m(775,639,{},efe),s.Kj=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingWithInverse",775),m(1318,775,{},JNe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingWithInverseUnsettable",1318),m(1311,402,{},BOe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectUnsettable",1311),m(772,402,{},nfe),s.Kj=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectWithInverse",772),m(1312,772,{},HNe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectWithInverseUnsettable",1312),m(776,562,YF,Qfe),s.wl=function(n){return new Qfe(this.a,this.c,n)},s.kd=function(){return this.b},s.xl=function(n,t,i){return p9n(this,n,this.b,i)},s.yl=function(n,t,i){return m9n(this,n,this.b,i)},v(Fn,"EStructuralFeatureImpl/InverseUpdatingFeatureMapEntry",776),m(1319,1,_ne,JSe),s.Dk=function(n){return this.a},s.Oj=function(){return X(this.a,98)?u(this.a,98).Oj():!this.a.dc()},s.Wb=function(n){this.a.$b(),this.a.Fc(u(n,16))},s.Ek=function(){X(this.a,98)?u(this.a,98).Ek():this.a.$b()},v(Fn,"EStructuralFeatureImpl/SettingMany",1319),m(1320,562,YF,gPe),s.vl=function(n){return new FK((Ei(),aA),this.b.oi(this.a,n))},s.kd=function(){return null},s.xl=function(n,t,i){return i},s.yl=function(n,t,i){return i},v(Fn,"EStructuralFeatureImpl/SimpleContentFeatureMapEntry",1320),m(640,562,YF,FK),s.vl=function(n){return new FK(this.c,n)},s.kd=function(){return this.a},s.xl=function(n,t,i){return i},s.yl=function(n,t,i){return i},v(Fn,"EStructuralFeatureImpl/SimpleFeatureMapEntry",640),m(396,492,Mh,Ol),s.$i=function(n){return oe(vf,On,29,n,0,1)},s.Wi=function(){return!1},v(Fn,"ESuperAdapter/1",396),m(446,439,{109:1,94:1,93:1,158:1,197:1,57:1,114:1,834:1,52:1,100:1,161:1,446:1,117:1,118:1},Hk),s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return!this.a&&(this.a=new tE(this,Rc,this)),this.a}return Pl(this,n-dt((yn(),kp)),An((r=u(Un(this,16),29),r||kp),n),t,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 2:return!this.a&&(this.a=new tE(this,Rc,this)),vc(this.a,n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),kp)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),kp)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return!!this.a&&this.a.i!=0}return Ll(this,n-dt((yn(),kp)),An((t=u(Un(this,16),29),t||kp),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Pt(t));return;case 2:!this.a&&(this.a=new tE(this,Rc,this)),kt(this.a),!this.a&&(this.a=new tE(this,Rc,this)),er(this.a,u(t,18));return}Jl(this,n-dt((yn(),kp)),An((i=u(Un(this,16),29),i||kp),n),t)},s.fi=function(){return yn(),kp},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Mo(this,null);return;case 2:!this.a&&(this.a=new tE(this,Rc,this)),kt(this.a);return}Fl(this,n-dt((yn(),kp)),An((t=u(Un(this,16),29),t||kp),n))},v(Fn,"ETypeParameterImpl",446),m(447,81,lu,tE),s.Lj=function(n,t){return lMn(this,u(n,87),t)},s.Mj=function(n,t){return fMn(this,u(n,87),t)},v(Fn,"ETypeParameterImpl/1",447),m(637,44,bv,kX),s.ec=function(){return new OP(this)},v(Fn,"ETypeParameterImpl/2",637),m(557,Ha,fs,OP),s.Ec=function(n){return mNe(this,u(n,87))},s.Fc=function(n){var t,i,r;for(r=!1,i=n.Jc();i.Ob();)t=u(i.Pb(),87),ei(this.a,t,"")==null&&(r=!0);return r},s.$b=function(){Hu(this.a)},s.Gc=function(n){return so(this.a,n)},s.Jc=function(){var n;return n=new D2(new on(this.a).a),new NP(n)},s.Kc=function(n){return n$e(this,n)},s.gc=function(){return xj(this.a)},v(Fn,"ETypeParameterImpl/2/1",557),m(558,1,Fr,NP),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(Q3(this.a).jd(),87)},s.Ob=function(){return this.a.b},s.Qb=function(){gRe(this.a)},v(Fn,"ETypeParameterImpl/2/1/1",558),m(1281,44,bv,Nxe),s._b=function(n){return $r(n)?RV(this,n):!!Xc(this.f,n)},s.xc=function(n){var t,i;return t=$r(n)?lo(this,n):du(Xc(this.f,n)),X(t,835)?(i=u(t,835),t=i.Ik(),ei(this,u(n,241),t),t):t??(n==null?(BX(),ehn):null)},v(Fn,"EValidatorRegistryImpl",1281),m(1303,710,{109:1,94:1,93:1,469:1,158:1,57:1,114:1,2002:1,52:1,100:1,161:1,117:1,118:1},uw),s.oi=function(n,t){switch(n.fk()){case 21:case 22:case 23:case 24:case 26:case 31:case 32:case 37:case 38:case 39:case 40:case 43:case 44:case 48:case 49:case 20:return t==null?null:su(t);case 25:return C8n(t);case 27:return G9n(t);case 28:return q9n(t);case 29:return t==null?null:FCe(cA[0],u(t,205));case 41:return t==null?"":Db(u(t,298));case 42:return su(t);case 50:return Pt(t);default:throw $(new Gn(r7+n.ve()+ip))}},s.pi=function(n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;switch(n.G==-1&&(n.G=(S=sl(n),S?Ld(S.si(),n):-1)),n.G){case 0:return i=new mX,i;case 1:return t=new Mb,t;case 2:return r=new ij,r;case 4:return c=new _P,c;case 5:return o=new Oxe,o;case 6:return l=new XSe,l;case 7:return f=new OT,f;case 10:return b=new p3,b;case 11:return p=new vX,p;case 12:return y=new c_e,y;case 13:return A=new yX,A;case 14:return O=new kle,O;case 17:return I=new Ph,I;case 18:return h=new hw,h;case 19:return R=new Hk,R;default:throw $(new Gn(mne+n.zb+ip))}},s.qi=function(n,t){switch(n.fk()){case 20:return t==null?null:new Joe(t);case 21:return t==null?null:new E0(t);case 23:case 22:return t==null?null:MEn(t);case 26:case 24:return t==null?null:sO(hl(t,-128,127)<<24>>24);case 25:return EOn(t);case 27:return lxn(t);case 28:return fxn(t);case 29:return CMn(t);case 32:case 31:return t==null?null:F2(t);case 38:case 37:return t==null?null:new aoe(t);case 40:case 39:return t==null?null:ve(hl(t,Xr,oi));case 41:return null;case 42:return t==null,null;case 44:case 43:return t==null?null:R2(Qz(t));case 49:case 48:return t==null?null:c8(hl(t,ZF,32767)<<16>>16);case 50:return t;default:throw $(new Gn(r7+n.ve()+ip))}},v(Fn,"EcoreFactoryImpl",1303),m(548,184,{109:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,2e3:1,52:1,100:1,161:1,184:1,548:1,117:1,118:1,680:1},OIe),s.gb=!1,s.hb=!1;var u7e,Zan=!1;v(Fn,"EcorePackageImpl",548),m(1199,1,{835:1},m3),s.Ik=function(){return fOe(),nhn},v(Fn,"EcorePackageImpl/1",1199),m(1208,1,ii,ow),s.dk=function(n){return X(n,158)},s.ek=function(n){return oe(QD,On,158,n,0,1)},v(Fn,"EcorePackageImpl/10",1208),m(1209,1,ii,tT),s.dk=function(n){return X(n,197)},s.ek=function(n){return oe(Ice,On,197,n,0,1)},v(Fn,"EcorePackageImpl/11",1209),m(1210,1,ii,iT),s.dk=function(n){return X(n,57)},s.ek=function(n){return oe(wb,On,57,n,0,1)},v(Fn,"EcorePackageImpl/12",1210),m(1211,1,ii,d0),s.dk=function(n){return X(n,403)},s.ek=function(n){return oe(yf,ime,62,n,0,1)},v(Fn,"EcorePackageImpl/13",1211),m(1212,1,ii,AL),s.dk=function(n){return X(n,241)},s.ek=function(n){return oe(xa,On,241,n,0,1)},v(Fn,"EcorePackageImpl/14",1212),m(1213,1,ii,B5),s.dk=function(n){return X(n,503)},s.ek=function(n){return oe(vp,On,2078,n,0,1)},v(Fn,"EcorePackageImpl/15",1213),m(1214,1,ii,G6),s.dk=function(n){return X(n,103)},s.ek=function(n){return oe(Bm,jv,19,n,0,1)},v(Fn,"EcorePackageImpl/16",1214),m(1215,1,ii,q6),s.dk=function(n){return X(n,179)},s.ek=function(n){return oe(ns,jv,179,n,0,1)},v(Fn,"EcorePackageImpl/17",1215),m(1216,1,ii,z5),s.dk=function(n){return X(n,470)},s.ek=function(n){return oe($m,On,470,n,0,1)},v(Fn,"EcorePackageImpl/18",1216),m(1217,1,ii,ML),s.dk=function(n){return X(n,549)},s.ek=function(n){return oe(Iu,BZe,549,n,0,1)},v(Fn,"EcorePackageImpl/19",1217),m(1200,1,ii,TL),s.dk=function(n){return X(n,335)},s.ek=function(n){return oe(Rm,jv,38,n,0,1)},v(Fn,"EcorePackageImpl/2",1200),m(1218,1,ii,U6),s.dk=function(n){return X(n,248)},s.ek=function(n){return oe(Rc,ten,87,n,0,1)},v(Fn,"EcorePackageImpl/20",1218),m(1219,1,ii,CL),s.dk=function(n){return X(n,446)},s.ek=function(n){return oe(Fo,On,834,n,0,1)},v(Fn,"EcorePackageImpl/21",1219),m(1220,1,ii,Gk),s.dk=function(n){return o2(n)},s.ek=function(n){return oe(Yi,Se,473,n,8,1)},v(Fn,"EcorePackageImpl/22",1220),m(1221,1,ii,OL),s.dk=function(n){return X(n,195)},s.ek=function(n){return oe(ds,Se,195,n,0,2)},v(Fn,"EcorePackageImpl/23",1221),m(1222,1,ii,bU),s.dk=function(n){return X(n,221)},s.ek=function(n){return oe(py,Se,221,n,0,1)},v(Fn,"EcorePackageImpl/24",1222),m(1223,1,ii,gU),s.dk=function(n){return X(n,180)},s.ek=function(n){return oe(XS,Se,180,n,0,1)},v(Fn,"EcorePackageImpl/25",1223),m(1224,1,ii,Ju),s.dk=function(n){return X(n,205)},s.ek=function(n){return oe(lJ,Se,205,n,0,1)},v(Fn,"EcorePackageImpl/26",1224),m(1225,1,ii,Io),s.dk=function(n){return!1},s.ek=function(n){return oe(S7e,On,2174,n,0,1)},v(Fn,"EcorePackageImpl/27",1225),m(1226,1,ii,Hc),s.dk=function(n){return s2(n)},s.ek=function(n){return oe(gr,Se,346,n,7,1)},v(Fn,"EcorePackageImpl/28",1226),m(1227,1,ii,nu),s.dk=function(n){return X(n,61)},s.ek=function(n){return oe(B8e,Z2,61,n,0,1)},v(Fn,"EcorePackageImpl/29",1227),m(1201,1,ii,io),s.dk=function(n){return X(n,504)},s.ek=function(n){return oe(Zt,{3:1,4:1,5:1,1995:1},587,n,0,1)},v(Fn,"EcorePackageImpl/3",1201),m(1228,1,ii,v1),s.dk=function(n){return X(n,568)},s.ek=function(n){return oe(J8e,On,2001,n,0,1)},v(Fn,"EcorePackageImpl/30",1228),m(1229,1,ii,Gp),s.dk=function(n){return X(n,163)},s.ek=function(n){return oe(a7e,Z2,163,n,0,1)},v(Fn,"EcorePackageImpl/31",1229),m(1230,1,ii,F5),s.dk=function(n){return X(n,75)},s.ek=function(n){return oe(SG,aen,75,n,0,1)},v(Fn,"EcorePackageImpl/32",1230),m(1231,1,ii,rT),s.dk=function(n){return X(n,164)},s.ek=function(n){return oe(h7,Se,164,n,0,1)},v(Fn,"EcorePackageImpl/33",1231),m(1232,1,ii,sw),s.dk=function(n){return X(n,15)},s.ek=function(n){return oe(jr,Se,15,n,0,1)},v(Fn,"EcorePackageImpl/34",1232),m(1233,1,ii,zs),s.dk=function(n){return X(n,298)},s.ek=function(n){return oe(wme,On,298,n,0,1)},v(Fn,"EcorePackageImpl/35",1233),m(1234,1,ii,qp),s.dk=function(n){return X(n,190)},s.ek=function(n){return oe(cp,Se,190,n,0,1)},v(Fn,"EcorePackageImpl/36",1234),m(1235,1,ii,v3),s.dk=function(n){return X(n,92)},s.ek=function(n){return oe(pme,On,92,n,0,1)},v(Fn,"EcorePackageImpl/37",1235),m(1236,1,ii,cT),s.dk=function(n){return X(n,588)},s.ek=function(n){return oe(o7e,On,588,n,0,1)},v(Fn,"EcorePackageImpl/38",1236),m(1237,1,ii,y1),s.dk=function(n){return!1},s.ek=function(n){return oe(x7e,On,2175,n,0,1)},v(Fn,"EcorePackageImpl/39",1237),m(1202,1,ii,J5),s.dk=function(n){return X(n,88)},s.ek=function(n){return oe(vf,On,29,n,0,1)},v(Fn,"EcorePackageImpl/4",1202),m(1238,1,ii,X6),s.dk=function(n){return X(n,191)},s.ek=function(n){return oe(up,Se,191,n,0,1)},v(Fn,"EcorePackageImpl/40",1238),m(1239,1,ii,Rh),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(Fn,"EcorePackageImpl/41",1239),m(1240,1,ii,uT),s.dk=function(n){return X(n,585)},s.ek=function(n){return oe(F8e,On,585,n,0,1)},v(Fn,"EcorePackageImpl/42",1240),m(1241,1,ii,qk),s.dk=function(n){return!1},s.ek=function(n){return oe(A7e,Se,2176,n,0,1)},v(Fn,"EcorePackageImpl/43",1241),m(1242,1,ii,NL),s.dk=function(n){return X(n,45)},s.ek=function(n){return oe(wg,eF,45,n,0,1)},v(Fn,"EcorePackageImpl/44",1242),m(1203,1,ii,Uk),s.dk=function(n){return X(n,143)},s.ek=function(n){return oe(Aa,On,143,n,0,1)},v(Fn,"EcorePackageImpl/5",1203),m(1204,1,ii,Xk),s.dk=function(n){return X(n,159)},s.ek=function(n){return oe(Bce,On,159,n,0,1)},v(Fn,"EcorePackageImpl/6",1204),m(1205,1,ii,Up),s.dk=function(n){return X(n,459)},s.ek=function(n){return oe(EG,On,675,n,0,1)},v(Fn,"EcorePackageImpl/7",1205),m(1206,1,ii,nf),s.dk=function(n){return X(n,568)},s.ek=function(n){return oe(ed,On,684,n,0,1)},v(Fn,"EcorePackageImpl/8",1206),m(1207,1,ii,Xp),s.dk=function(n){return X(n,469)},s.ek=function(n){return oe(rA,On,469,n,0,1)},v(Fn,"EcorePackageImpl/9",1207),m(1019,2042,RZe,nAe),s.Ki=function(n,t){ujn(this,u(t,415))},s.Oi=function(n,t){rqe(this,n,u(t,415))},v(Fn,"MinimalEObjectImpl/1ArrayDelegatingAdapterList",1019),m(1020,151,RN,yIe),s.hj=function(){return this.a.a},v(Fn,"MinimalEObjectImpl/1ArrayDelegatingAdapterList/1",1020),m(1047,1046,{},NCe),v("org.eclipse.emf.ecore.plugin","EcorePlugin",1047);var o7e=Hi(hen,"Resource");m(786,1485,den),s.Fl=function(n){},s.Gl=function(n){},s.Cl=function(){return!this.a&&(this.a=new hX(this)),this.a},s.Dl=function(n){var t,i,r,c,o;if(r=n.length,r>0)if(Yn(0,n.length),n.charCodeAt(0)==47){for(o=new xo(4),c=1,t=1;t0&&(n=(Yr(0,i,n.length),n.substr(0,i))));return wCn(this,n)},s.El=function(){return this.c},s.Ib=function(){var n;return Db(this.Pm)+"@"+(n=Oi(this)>>>0,n.toString(16))+" uri='"+this.d+"'"},s.b=!1,v(Lne,"ResourceImpl",786),m(1486,786,den,HSe),v(Lne,"BinaryResourceImpl",1486),m(1159,697,Cne),s._i=function(n){return X(n,57)?X5n(this,u(n,57)):X(n,588)?new ut(u(n,588).Cl()):ue(n)===ue(this.f)?u(n,18).Jc():(S9(),eI.a)},s.Ob=function(){return Z0e(this)},s.a=!1,v($i,"EcoreUtil/ContentTreeIterator",1159),m(1487,1159,Cne,YDe),s._i=function(n){return ue(n)===ue(this.f)?u(n,16).Jc():new QLe(u(n,57))},v(Lne,"ResourceImpl/5",1487),m(647,2054,nen,hX),s.Gc=function(n){return this.i<=4?m8(this,n):X(n,52)&&u(n,52).Gh()==this.a},s.Ki=function(n,t){n==this.i-1&&(this.a.b||(this.a.b=!0))},s.Mi=function(n,t){n==0?this.a.b||(this.a.b=!0):bY(this,n,t)},s.Oi=function(n,t){},s.Pi=function(n,t,i){},s.Jj=function(){return 2},s.hj=function(){return this.a},s.Kj=function(){return!0},s.Lj=function(n,t){var i;return i=u(n,52),t=i.ci(this.a,t),t},s.Mj=function(n,t){var i;return i=u(n,52),i.ci(null,t)},s.Nj=function(){return!1},s.Qi=function(){return!0},s.$i=function(n){return oe(wb,On,57,n,0,1)},s.Wi=function(){return!1},v(Lne,"ResourceImpl/ContentsEList",647),m(953,2024,$8,GSe),s.dd=function(n){return this.a.Ii(n)},s.gc=function(){return this.a.gc()},v($i,"AbstractSequentialInternalEList/1",953);var s7e,l7e,nc,f7e;m(625,1,{},ZNe);var xG,AG;v($i,"BasicExtendedMetaData",625),m(1150,1,{},QTe),s.Hl=function(){return null},s.Il=function(){return this.a==-2&&HT(this,jMn(this.d,this.b)),this.a},s.Jl=function(){return null},s.Kl=function(){return jn(),jn(),Sc},s.ve=function(){return this.c==s7&&uX(this,AJe(this.d,this.b)),this.c},s.Ll=function(){return 0},s.a=-2,s.c=s7,v($i,"BasicExtendedMetaData/EClassExtendedMetaDataImpl",1150),m(1151,1,{},TLe),s.Hl=function(){return this.a==(z9(),xG)&&MP(this,uIn(this.f,this.b)),this.a},s.Il=function(){return 0},s.Jl=function(){return this.c==(z9(),xG)&&r9(this,oIn(this.f,this.b)),this.c},s.Kl=function(){return!this.d&&sX(this,G_n(this.f,this.b)),this.d},s.ve=function(){return this.e==s7&&qT(this,AJe(this.f,this.b)),this.e},s.Ll=function(){return this.g==-2&&d(this,HAn(this.f,this.b)),this.g},s.e=s7,s.g=-2,v($i,"BasicExtendedMetaData/EDataTypeExtendedMetaDataImpl",1151),m(1149,1,{},WTe),s.b=!1,s.c=!1,v($i,"BasicExtendedMetaData/EPackageExtendedMetaDataImpl",1149),m(1152,1,{},CLe),s.c=-2,s.e=s7,s.f=s7,v($i,"BasicExtendedMetaData/EStructuralFeatureExtendedMetaDataImpl",1152),m(581,623,lu,Z$),s.Jj=function(){return this.c},s.ml=function(){return!1},s.Ui=function(n,t){return t},s.c=0,v($i,"EDataTypeEList",581);var a7e=Hi($i,"FeatureMap");m(76,581,{3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,67:1,61:1,77:1,163:1,219:1,1998:1,72:1,98:1},nr),s._c=function(n,t){MNn(this,n,u(t,75))},s.Ec=function(n){return GOn(this,u(n,75))},s.Fi=function(n){Jvn(this,u(n,75))},s.Lj=function(n,t){return v2n(this,u(n,75),t)},s.Mj=function(n,t){return qle(this,u(n,75),t)},s.Ri=function(n,t){return QIn(this,n,t)},s.Ui=function(n,t){return BPn(this,n,u(t,75))},s.fd=function(n,t){return hDn(this,n,u(t,75))},s.Sj=function(n,t){return y2n(this,u(n,75),t)},s.Tj=function(n,t){return ANe(this,u(n,75),t)},s.Uj=function(n,t,i){return DAn(this,u(n,75),u(t,75),i)},s.Xi=function(n,t){return uW(this,n,u(t,75))},s.Ml=function(n,t){return Xbe(this,n,t)},s.ad=function(n,t){var i,r,c,o,l,f,h,b,p;for(b=new Nw(t.gc()),c=t.Jc();c.Ob();)if(r=u(c.Pb(),75),o=r.Jk(),J1(this.e,o))(!o.Qi()||!HR(this,o,r.kd())&&!m8(b,r))&&Et(b,r);else{for(p=Po(this.e.Ah(),o),i=u(this.g,122),l=!0,f=0;f=0;)if(t=n[this.c],this.k.$l(t.Jk()))return this.j=this.f?t:t.kd(),this.i=-2,!0;return this.i=-1,this.g=-1,!1},v($i,"BasicFeatureMap/FeatureEIterator",412),m(666,412,Qh,EK),s.sl=function(){return!0},v($i,"BasicFeatureMap/ResolvingFeatureEIterator",666),m(951,482,KF,qCe),s.nj=function(){return this},v($i,"EContentsEList/1",951),m(952,482,KF,wCe),s.sl=function(){return!1},v($i,"EContentsEList/2",952),m(950,287,VF,UCe),s.ul=function(n){},s.Ob=function(){return!1},s.Sb=function(){return!1},v($i,"EContentsEList/FeatureIteratorImpl/1",950),m(824,581,lu,Zse),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EDataTypeEList/Unsettable",824),m(1920,581,lu,WCe),s.Qi=function(){return!0},v($i,"EDataTypeUniqueEList",1920),m(1921,824,lu,ZCe),s.Qi=function(){return!0},v($i,"EDataTypeUniqueEList/Unsettable",1921),m(145,81,lu,rs),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectContainmentEList/Resolving",145),m(1153,543,lu,QCe),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectContainmentEList/Unsettable/Resolving",1153),m(753,14,lu,Rle),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EObjectContainmentWithInverseEList/Unsettable",753),m(1187,753,lu,dNe),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectContainmentWithInverseEList/Unsettable/Resolving",1187),m(745,491,lu,Wse),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EObjectEList/Unsettable",745),m(339,491,lu,$3),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectResolvingEList",339),m(1825,745,lu,eOe),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectResolvingEList/Unsettable",1825),m(1488,1,{},H5);var ehn;v($i,"EObjectValidator",1488),m(547,491,lu,mR),s.gl=function(){return this.d},s.hl=function(){return this.b},s.Kj=function(){return!0},s.kl=function(){return!0},s.b=0,v($i,"EObjectWithInverseEList",547),m(1190,547,lu,bNe),s.jl=function(){return!0},v($i,"EObjectWithInverseEList/ManyInverse",1190),m(626,547,lu,GK),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EObjectWithInverseEList/Unsettable",626),m(1189,626,lu,gNe),s.jl=function(){return!0},v($i,"EObjectWithInverseEList/Unsettable/ManyInverse",1189),m(754,547,lu,Ble),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectWithInverseResolvingEList",754),m(33,754,lu,Nn),s.jl=function(){return!0},v($i,"EObjectWithInverseResolvingEList/ManyInverse",33),m(755,626,lu,zle),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectWithInverseResolvingEList/Unsettable",755),m(1188,755,lu,wNe),s.jl=function(){return!0},v($i,"EObjectWithInverseResolvingEList/Unsettable/ManyInverse",1188),m(1154,623,lu),s.Ji=function(){return(this.b&1792)==0},s.Li=function(){this.b|=1},s.il=function(){return(this.b&4)!=0},s.Kj=function(){return(this.b&40)!=0},s.jl=function(){return(this.b&16)!=0},s.kl=function(){return(this.b&8)!=0},s.ll=function(){return(this.b&U0)!=0},s.$k=function(){return(this.b&32)!=0},s.ml=function(){return(this.b&qf)!=0},s.dk=function(n){return this.d?rPe(this.d,n):this.Jk().Fk().dk(n)},s.Oj=function(){return(this.b&2)!=0?(this.b&1)!=0:this.i!=0},s.Qi=function(){return(this.b&128)!=0},s.Ek=function(){var n;kt(this),(this.b&2)!=0&&(Fs(this.e)?(n=(this.b&1)!=0,this.b&=-2,s9(this,new Pf(this.e,2,zi(this.e.Ah(),this.Jk()),n,!1))):this.b&=-2)},s.Wi=function(){return(this.b&1536)==0},s.b=0,v($i,"EcoreEList/Generic",1154),m(1155,1154,lu,l_e),s.Jk=function(){return this.a},v($i,"EcoreEList/Dynamic",1155),m(752,67,Mh,uoe),s.$i=function(n){return aO(this.a.a,n)},v($i,"EcoreEMap/1",752),m(751,81,lu,Lfe),s.Ki=function(n,t){lz(this.b,u(t,136))},s.Mi=function(n,t){fze(this.b)},s.Ni=function(n,t,i){var r;++(r=this.b,u(t,136),r).e},s.Oi=function(n,t){gQ(this.b,u(t,136))},s.Pi=function(n,t,i){gQ(this.b,u(i,136)),ue(i)===ue(t)&&u(i,136).zi(pwn(u(t,136).jd())),lz(this.b,u(t,136))},v($i,"EcoreEMap/DelegateEObjectContainmentEList",751),m(1185,142,tme,kBe),v($i,"EcoreEMap/Unsettable",1185),m(1186,751,lu,pNe),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EcoreEMap/Unsettable/UnsettableDelegateEObjectContainmentEList",1186),m(1158,223,bv,hIe),s.a=!1,s.b=!1,v($i,"EcoreUtil/Copier",1158),m(747,1,Fr,QLe),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return aJe(this)},s.Pb=function(){var n;return aJe(this),n=this.b,this.b=null,n},s.Qb=function(){this.a.Qb()},v($i,"EcoreUtil/ProperContentIterator",747),m(1489,1488,{},zU);var nhn;v($i,"EcoreValidator",1489);var thn;Hi($i,"FeatureMapUtil/Validator"),m(1258,1,{2003:1},lw),s.$l=function(n){return!0},v($i,"FeatureMapUtil/1",1258),m(760,1,{2003:1},Age),s.$l=function(n){var t;return this.c==n?!0:(t=$e(Bn(this.a,n)),t==null?hIn(this,n)?(UPe(this.a,n,(Pn(),a7)),!0):(UPe(this.a,n,(Pn(),eb)),!1):t==(Pn(),a7))},s.e=!1;var Hce;v($i,"FeatureMapUtil/BasicValidator",760),m(761,44,bv,Vse),v($i,"FeatureMapUtil/BasicValidator/Cache",761),m(495,56,{20:1,31:1,56:1,18:1,16:1,61:1,77:1,72:1,98:1},pC),s._c=function(n,t){ZUe(this.c,this.b,n,t)},s.Ec=function(n){return Xbe(this.c,this.b,n)},s.ad=function(n,t){return NLn(this.c,this.b,n,t)},s.Fc=function(n){return Vj(this,n)},s.Ei=function(n,t){p8n(this.c,this.b,n,t)},s.Uk=function(n,t){return Bbe(this.c,this.b,n,t)},s.Yi=function(n){return Uz(this.c,this.b,n,!1)},s.Gi=function(){return xCe(this.c,this.b)},s.Hi=function(){return lwn(this.c,this.b)},s.Ii=function(n){return v9n(this.c,this.b,n)},s.Vk=function(n,t){return YOe(this,n,t)},s.$b=function(){n4(this)},s.Gc=function(n){return HR(this.c,this.b,n)},s.Hc=function(n){return m7n(this.c,this.b,n)},s.Xb=function(n){return Uz(this.c,this.b,n,!0)},s.Dk=function(n){return this},s.bd=function(n){return C6n(this.c,this.b,n)},s.dc=function(){return M$(this)},s.Oj=function(){return!OO(this.c,this.b)},s.Jc=function(){return n8n(this.c,this.b)},s.cd=function(){return t8n(this.c,this.b)},s.dd=function(n){return Ejn(this.c,this.b,n)},s.Ri=function(n,t){return wKe(this.c,this.b,n,t)},s.Si=function(n,t){E9n(this.c,this.b,n,t)},s.ed=function(n){return HGe(this.c,this.b,n)},s.Kc=function(n){return PIn(this.c,this.b,n)},s.fd=function(n,t){return xKe(this.c,this.b,n,t)},s.Wb=function(n){Mz(this.c,this.b),Vj(this,u(n,16))},s.gc=function(){return Sjn(this.c,this.b)},s.Nc=function(){return Oyn(this.c,this.b)},s.Oc=function(n){return O6n(this.c,this.b,n)},s.Ib=function(){var n,t;for(t=new pd,t.a+="[",n=xCe(this.c,this.b);cQ(n);)Bc(t,Qj(oz(n))),cQ(n)&&(t.a+=Co);return t.a+="]",t.a},s.Ek=function(){Mz(this.c,this.b)},v($i,"FeatureMapUtil/FeatureEList",495),m(634,39,RN,rY),s.fj=function(n){return PE(this,n)},s.kj=function(n){var t,i,r,c,o,l,f;switch(this.d){case 1:case 2:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return this.g=n.gj(),n.ej()==1&&(this.d=1),!0;break}case 3:{switch(c=n.ej(),c){case 3:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return this.d=5,t=new Nw(2),Et(t,this.g),Et(t,n.gj()),this.g=t,!0;break}}break}case 5:{switch(c=n.ej(),c){case 3:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return i=u(this.g,18),i.Ec(n.gj()),!0;break}}break}case 4:{switch(c=n.ej(),c){case 3:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return this.d=1,this.g=n.gj(),!0;break}case 4:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return this.d=6,f=new Nw(2),Et(f,this.n),Et(f,n.ij()),this.n=f,l=z(B($t,1),ni,30,15,[this.o,n.jj()]),this.g=l,!0;break}}break}case 6:{switch(c=n.ej(),c){case 4:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return i=u(this.n,18),i.Ec(n.ij()),l=u(this.g,54),r=oe($t,ni,30,l.length+1,15,1),Wu(l,0,r,0,l.length),r[l.length]=n.jj(),this.g=r,!0;break}}break}}return!1},v($i,"FeatureMapUtil/FeatureENotificationImpl",634),m(553,495,{20:1,31:1,56:1,18:1,16:1,61:1,77:1,163:1,219:1,1998:1,72:1,98:1},rR),s.Ml=function(n,t){return Xbe(this.c,n,t)},s.Nl=function(n,t,i){return Bbe(this.c,n,t,i)},s.Ol=function(n,t,i){return bge(this.c,n,t,i)},s.Pl=function(){return this},s.Ql=function(n,t){return iN(this.c,n,t)},s.Rl=function(n){return u(Uz(this.c,this.b,n,!1),75).Jk()},s.Sl=function(n){return u(Uz(this.c,this.b,n,!1),75).kd()},s.Tl=function(){return this.a},s.Ul=function(n){return!OO(this.c,n)},s.Vl=function(n,t){Xz(this.c,n,t)},s.Wl=function(n){return CBe(this.c,n)},s.Xl=function(n){fHe(this.c,n)},v($i,"FeatureMapUtil/FeatureFeatureMap",553),m(1257,1,_ne,nCe),s.Dk=function(n){return Uz(this.b,this.a,-1,n)},s.Oj=function(){return!OO(this.b,this.a)},s.Wb=function(n){Xz(this.b,this.a,n)},s.Ek=function(){Mz(this.b,this.a)},v($i,"FeatureMapUtil/FeatureValue",1257);var Xy,Gce,qce,Ky,ihn,tI=Hi(iJ,"AnyType");m(670,63,H1,TX),v(iJ,"InvalidDatatypeValueException",670);var MG=Hi(iJ,gen),iI=Hi(iJ,wen),h7e=Hi(iJ,pen),rhn,zu,d7e,Dg,chn,uhn,ohn,shn,lhn,fhn,ahn,hhn,dhn,bhn,ghn,Zv,whn,e5,lA,phn,jp,rI,cI,mhn,fA,aA;m(828,501,{109:1,94:1,93:1,57:1,52:1,100:1,841:1},koe),s.Ih=function(n,t,i){switch(n){case 0:return i?(!this.c&&(this.c=new nr(this,0)),this.c):(!this.c&&(this.c=new nr(this,0)),this.c.b);case 1:return i?(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)):(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),219)).Tl();case 2:return i?(!this.b&&(this.b=new nr(this,2)),this.b):(!this.b&&(this.b=new nr(this,2)),this.b.b)}return Pl(this,n-dt(this.fi()),An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),n),t,i)},s.Rh=function(n,t,i){var r;switch(t){case 0:return!this.c&&(this.c=new nr(this,0)),eN(this.c,n,i);case 1:return(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),72)).Vk(n,i);case 2:return!this.b&&(this.b=new nr(this,2)),eN(this.b,n,i)}return r=u(An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),t),69),r.uk().yk(this,khe(this),t-dt(this.fi()),n,i)},s.Th=function(n){switch(n){case 0:return!!this.c&&this.c.i!=0;case 1:return!(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)).dc();case 2:return!!this.b&&this.b.i!=0}return Ll(this,n-dt(this.fi()),An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),$C(this.c,t);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),219)).Wb(t);return;case 2:!this.b&&(this.b=new nr(this,2)),$C(this.b,t);return}Jl(this,n-dt(this.fi()),An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),n),t)},s.fi=function(){return Ei(),d7e},s.hi=function(n){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),kt(this.c);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)).$b();return;case 2:!this.b&&(this.b=new nr(this,2)),kt(this.b);return}Fl(this,n-dt(this.fi()),An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.Ib=function(){var n;return(this.j&4)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (mixed: ",qj(n,this.c),n.a+=", anyAttribute: ",qj(n,this.b),n.a+=")",n.a)},v(kr,"AnyTypeImpl",828),m(671,501,{109:1,94:1,93:1,57:1,52:1,100:1,2081:1,671:1},wU),s.Ih=function(n,t,i){switch(n){case 0:return this.a;case 1:return this.b}return Pl(this,n-dt((Ei(),Zv)),An((this.j&2)==0?Zv:(!this.k&&(this.k=new tl),this.k).Lk(),n),t,i)},s.Th=function(n){switch(n){case 0:return this.a!=null;case 1:return this.b!=null}return Ll(this,n-dt((Ei(),Zv)),An((this.j&2)==0?Zv:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:C(this,Pt(t));return;case 1:Q(this,Pt(t));return}Jl(this,n-dt((Ei(),Zv)),An((this.j&2)==0?Zv:(!this.k&&(this.k=new tl),this.k).Lk(),n),t)},s.fi=function(){return Ei(),Zv},s.hi=function(n){switch(n){case 0:this.a=null;return;case 1:this.b=null;return}Fl(this,n-dt((Ei(),Zv)),An((this.j&2)==0?Zv:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.Ib=function(){var n;return(this.j&4)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (data: ",Bc(n,this.a),n.a+=", target: ",Bc(n,this.b),n.a+=")",n.a)},s.a=null,s.b=null,v(kr,"ProcessingInstructionImpl",671),m(672,828,{109:1,94:1,93:1,57:1,52:1,100:1,841:1,2082:1,672:1},Dxe),s.Ih=function(n,t,i){switch(n){case 0:return i?(!this.c&&(this.c=new nr(this,0)),this.c):(!this.c&&(this.c=new nr(this,0)),this.c.b);case 1:return i?(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)):(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),219)).Tl();case 2:return i?(!this.b&&(this.b=new nr(this,2)),this.b):(!this.b&&(this.b=new nr(this,2)),this.b.b);case 3:return!this.c&&(this.c=new nr(this,0)),Pt(iN(this.c,(Ei(),lA),!0));case 4:return Jle(this.a,(!this.c&&(this.c=new nr(this,0)),Pt(iN(this.c,(Ei(),lA),!0))));case 5:return this.a}return Pl(this,n-dt((Ei(),e5)),An((this.j&2)==0?e5:(!this.k&&(this.k=new tl),this.k).Lk(),n),t,i)},s.Th=function(n){switch(n){case 0:return!!this.c&&this.c.i!=0;case 1:return!(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)).dc();case 2:return!!this.b&&this.b.i!=0;case 3:return!this.c&&(this.c=new nr(this,0)),Pt(iN(this.c,(Ei(),lA),!0))!=null;case 4:return Jle(this.a,(!this.c&&(this.c=new nr(this,0)),Pt(iN(this.c,(Ei(),lA),!0))))!=null;case 5:return!!this.a}return Ll(this,n-dt((Ei(),e5)),An((this.j&2)==0?e5:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),$C(this.c,t);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),219)).Wb(t);return;case 2:!this.b&&(this.b=new nr(this,2)),$C(this.b,t);return;case 3:Cae(this,Pt(t));return;case 4:Cae(this,Fle(this.a,t));return;case 5:D(this,u(t,159));return}Jl(this,n-dt((Ei(),e5)),An((this.j&2)==0?e5:(!this.k&&(this.k=new tl),this.k).Lk(),n),t)},s.fi=function(){return Ei(),e5},s.hi=function(n){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),kt(this.c);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)).$b();return;case 2:!this.b&&(this.b=new nr(this,2)),kt(this.b);return;case 3:!this.c&&(this.c=new nr(this,0)),Xz(this.c,(Ei(),lA),null);return;case 4:Cae(this,Fle(this.a,null));return;case 5:this.a=null;return}Fl(this,n-dt((Ei(),e5)),An((this.j&2)==0?e5:(!this.k&&(this.k=new tl),this.k).Lk(),n))},v(kr,"SimpleAnyTypeImpl",672),m(673,501,{109:1,94:1,93:1,57:1,52:1,100:1,2083:1,673:1},Ixe),s.Ih=function(n,t,i){switch(n){case 0:return i?(!this.a&&(this.a=new nr(this,0)),this.a):(!this.a&&(this.a=new nr(this,0)),this.a.b);case 1:return i?(!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),this.b):(!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),ZC(this.b));case 2:return i?(!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),this.c):(!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),ZC(this.c));case 3:return!this.a&&(this.a=new nr(this,0)),fo(this.a,(Ei(),rI));case 4:return!this.a&&(this.a=new nr(this,0)),fo(this.a,(Ei(),cI));case 5:return!this.a&&(this.a=new nr(this,0)),fo(this.a,(Ei(),fA));case 6:return!this.a&&(this.a=new nr(this,0)),fo(this.a,(Ei(),aA))}return Pl(this,n-dt((Ei(),jp)),An((this.j&2)==0?jp:(!this.k&&(this.k=new tl),this.k).Lk(),n),t,i)},s.Rh=function(n,t,i){var r;switch(t){case 0:return!this.a&&(this.a=new nr(this,0)),eN(this.a,n,i);case 1:return!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),U$(this.b,n,i);case 2:return!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),U$(this.c,n,i);case 5:return!this.a&&(this.a=new nr(this,0)),YOe(fo(this.a,(Ei(),fA)),n,i)}return r=u(An((this.j&2)==0?(Ei(),jp):(!this.k&&(this.k=new tl),this.k).Lk(),t),69),r.uk().yk(this,khe(this),t-dt((Ei(),jp)),n,i)},s.Th=function(n){switch(n){case 0:return!!this.a&&this.a.i!=0;case 1:return!!this.b&&this.b.f!=0;case 2:return!!this.c&&this.c.f!=0;case 3:return!this.a&&(this.a=new nr(this,0)),!M$(fo(this.a,(Ei(),rI)));case 4:return!this.a&&(this.a=new nr(this,0)),!M$(fo(this.a,(Ei(),cI)));case 5:return!this.a&&(this.a=new nr(this,0)),!M$(fo(this.a,(Ei(),fA)));case 6:return!this.a&&(this.a=new nr(this,0)),!M$(fo(this.a,(Ei(),aA)))}return Ll(this,n-dt((Ei(),jp)),An((this.j&2)==0?jp:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:!this.a&&(this.a=new nr(this,0)),$C(this.a,t);return;case 1:!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),CB(this.b,t);return;case 2:!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),CB(this.c,t);return;case 3:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),rI))),!this.a&&(this.a=new nr(this,0)),Vj(fo(this.a,rI),u(t,18));return;case 4:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),cI))),!this.a&&(this.a=new nr(this,0)),Vj(fo(this.a,cI),u(t,18));return;case 5:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),fA))),!this.a&&(this.a=new nr(this,0)),Vj(fo(this.a,fA),u(t,18));return;case 6:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),aA))),!this.a&&(this.a=new nr(this,0)),Vj(fo(this.a,aA),u(t,18));return}Jl(this,n-dt((Ei(),jp)),An((this.j&2)==0?jp:(!this.k&&(this.k=new tl),this.k).Lk(),n),t)},s.fi=function(){return Ei(),jp},s.hi=function(n){switch(n){case 0:!this.a&&(this.a=new nr(this,0)),kt(this.a);return;case 1:!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),this.b.c.$b();return;case 2:!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),this.c.c.$b();return;case 3:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),rI)));return;case 4:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),cI)));return;case 5:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),fA)));return;case 6:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),aA)));return}Fl(this,n-dt((Ei(),jp)),An((this.j&2)==0?jp:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.Ib=function(){var n;return(this.j&4)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (mixed: ",qj(n,this.a),n.a+=")",n.a)},v(kr,"XMLTypeDocumentRootImpl",673),m(1990,710,{109:1,94:1,93:1,469:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1,2084:1},oT),s.oi=function(n,t){switch(n.fk()){case 7:case 8:case 9:case 10:case 16:case 22:case 23:case 24:case 25:case 26:case 32:case 33:case 34:case 36:case 37:case 44:case 45:case 50:case 51:case 53:case 55:case 56:case 57:case 58:case 60:case 61:case 4:return t==null?null:su(t);case 19:case 28:case 29:case 35:case 38:case 39:case 41:case 46:case 52:case 54:case 5:return Pt(t);case 6:return Rpn(u(t,195));case 12:case 47:case 49:case 11:return lVe(this,n,t);case 13:return t==null?null:$Ln(u(t,247));case 15:case 14:return t==null?null:Dvn(ne(re(t)));case 17:return ZHe((Ei(),t));case 18:return ZHe(t);case 21:case 20:return t==null?null:Ivn(u(t,164).a);case 27:return $pn(u(t,195));case 30:return aHe((Ei(),u(t,16)));case 31:return aHe(u(t,16));case 40:return Ppn((Ei(),t));case 42:return eGe((Ei(),t));case 43:return eGe(t);case 59:case 48:return Lpn((Ei(),t));default:throw $(new Gn(r7+n.ve()+ip))}},s.pi=function(n){var t,i,r,c,o;switch(n.G==-1&&(n.G=(i=sl(n),i?Ld(i.si(),n):-1)),n.G){case 0:return t=new koe,t;case 1:return r=new wU,r;case 2:return c=new Dxe,c;case 3:return o=new Ixe,o;default:throw $(new Gn(mne+n.zb+ip))}},s.qi=function(n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q;switch(n.fk()){case 5:case 52:case 4:return t;case 6:return nSn(t);case 8:case 7:return t==null?null:BAn(t);case 9:return t==null?null:sO(hl((r=bo(t,!0),r.length>0&&(Yn(0,r.length),r.charCodeAt(0)==43)?(Yn(1,r.length+1),r.substr(1)):r),-128,127)<<24>>24);case 10:return t==null?null:sO(hl((c=bo(t,!0),c.length>0&&(Yn(0,c.length),c.charCodeAt(0)==43)?(Yn(1,c.length+1),c.substr(1)):c),-128,127)<<24>>24);case 11:return Pt(Kw(this,(Ei(),ohn),t));case 12:return Pt(Kw(this,(Ei(),shn),t));case 13:return t==null?null:new Joe(bo(t,!0));case 15:case 14:return XOn(t);case 16:return Pt(Kw(this,(Ei(),lhn),t));case 17:return dJe((Ei(),t));case 18:return dJe(t);case 28:case 29:case 35:case 38:case 39:case 41:case 54:case 19:return bo(t,!0);case 21:case 20:return iNn(t);case 22:return Pt(Kw(this,(Ei(),fhn),t));case 23:return Pt(Kw(this,(Ei(),ahn),t));case 24:return Pt(Kw(this,(Ei(),hhn),t));case 25:return Pt(Kw(this,(Ei(),dhn),t));case 26:return Pt(Kw(this,(Ei(),bhn),t));case 27:return UEn(t);case 30:return bJe((Ei(),t));case 31:return bJe(t);case 32:return t==null?null:ve(hl((p=bo(t,!0),p.length>0&&(Yn(0,p.length),p.charCodeAt(0)==43)?(Yn(1,p.length+1),p.substr(1)):p),Xr,oi));case 33:return t==null?null:new E0((y=bo(t,!0),y.length>0&&(Yn(0,y.length),y.charCodeAt(0)==43)?(Yn(1,y.length+1),y.substr(1)):y));case 34:return t==null?null:ve(hl((S=bo(t,!0),S.length>0&&(Yn(0,S.length),S.charCodeAt(0)==43)?(Yn(1,S.length+1),S.substr(1)):S),Xr,oi));case 36:return t==null?null:R2(Qz((A=bo(t,!0),A.length>0&&(Yn(0,A.length),A.charCodeAt(0)==43)?(Yn(1,A.length+1),A.substr(1)):A)));case 37:return t==null?null:R2(Qz((O=bo(t,!0),O.length>0&&(Yn(0,O.length),O.charCodeAt(0)==43)?(Yn(1,O.length+1),O.substr(1)):O)));case 40:return JSn((Ei(),t));case 42:return gJe((Ei(),t));case 43:return gJe(t);case 44:return t==null?null:new E0((I=bo(t,!0),I.length>0&&(Yn(0,I.length),I.charCodeAt(0)==43)?(Yn(1,I.length+1),I.substr(1)):I));case 45:return t==null?null:new E0((R=bo(t,!0),R.length>0&&(Yn(0,R.length),R.charCodeAt(0)==43)?(Yn(1,R.length+1),R.substr(1)):R));case 46:return bo(t,!1);case 47:return Pt(Kw(this,(Ei(),ghn),t));case 59:case 48:return FSn((Ei(),t));case 49:return Pt(Kw(this,(Ei(),whn),t));case 50:return t==null?null:c8(hl((q=bo(t,!0),q.length>0&&(Yn(0,q.length),q.charCodeAt(0)==43)?(Yn(1,q.length+1),q.substr(1)):q),ZF,32767)<<16>>16);case 51:return t==null?null:c8(hl((o=bo(t,!0),o.length>0&&(Yn(0,o.length),o.charCodeAt(0)==43)?(Yn(1,o.length+1),o.substr(1)):o),ZF,32767)<<16>>16);case 53:return Pt(Kw(this,(Ei(),phn),t));case 55:return t==null?null:c8(hl((l=bo(t,!0),l.length>0&&(Yn(0,l.length),l.charCodeAt(0)==43)?(Yn(1,l.length+1),l.substr(1)):l),ZF,32767)<<16>>16);case 56:return t==null?null:c8(hl((f=bo(t,!0),f.length>0&&(Yn(0,f.length),f.charCodeAt(0)==43)?(Yn(1,f.length+1),f.substr(1)):f),ZF,32767)<<16>>16);case 57:return t==null?null:R2(Qz((h=bo(t,!0),h.length>0&&(Yn(0,h.length),h.charCodeAt(0)==43)?(Yn(1,h.length+1),h.substr(1)):h)));case 58:return t==null?null:R2(Qz((b=bo(t,!0),b.length>0&&(Yn(0,b.length),b.charCodeAt(0)==43)?(Yn(1,b.length+1),b.substr(1)):b)));case 60:return t==null?null:ve(hl((i=bo(t,!0),i.length>0&&(Yn(0,i.length),i.charCodeAt(0)==43)?(Yn(1,i.length+1),i.substr(1)):i),Xr,oi));case 61:return t==null?null:ve(hl(bo(t,!0),Xr,oi));default:throw $(new Gn(r7+n.ve()+ip))}};var vhn,b7e,yhn,g7e;v(kr,"XMLTypeFactoryImpl",1990),m(582,184,{109:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,52:1,100:1,161:1,184:1,117:1,118:1,680:1,2006:1,582:1},CIe),s.N=!1,s.O=!1;var khn=!1;v(kr,"XMLTypePackageImpl",582),m(1923,1,{835:1},sT),s.Ik=function(){return rge(),Ohn},v(kr,"XMLTypePackageImpl/1",1923),m(1932,1,ii,DL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/10",1932),m(1933,1,ii,K6),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/11",1933),m(1934,1,ii,lT),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/12",1934),m(1935,1,ii,k1),s.dk=function(n){return s2(n)},s.ek=function(n){return oe(gr,Se,346,n,7,1)},v(kr,"XMLTypePackageImpl/13",1935),m(1936,1,ii,IL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/14",1936),m(1937,1,ii,Bh),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/15",1937),m(1938,1,ii,_L),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/16",1938),m(1939,1,ii,LL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/17",1939),m(1940,1,ii,Kp),s.dk=function(n){return X(n,164)},s.ek=function(n){return oe(h7,Se,164,n,0,1)},v(kr,"XMLTypePackageImpl/18",1940),m(1941,1,ii,Kk),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/19",1941),m(1924,1,ii,fT),s.dk=function(n){return X(n,841)},s.ek=function(n){return oe(tI,On,841,n,0,1)},v(kr,"XMLTypePackageImpl/2",1924),m(1942,1,ii,G5),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/20",1942),m(1943,1,ii,PL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/21",1943),m(1944,1,ii,$L),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/22",1944),m(1945,1,ii,RL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/23",1945),m(1946,1,ii,BL),s.dk=function(n){return X(n,195)},s.ek=function(n){return oe(ds,Se,195,n,0,2)},v(kr,"XMLTypePackageImpl/24",1946),m(1947,1,ii,Vk),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/25",1947),m(1948,1,ii,aT),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/26",1948),m(1949,1,ii,pU),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/27",1949),m(1950,1,ii,mU),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/28",1950),m(1951,1,ii,vU),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/29",1951),m(1925,1,ii,zL),s.dk=function(n){return X(n,671)},s.ek=function(n){return oe(MG,On,2081,n,0,1)},v(kr,"XMLTypePackageImpl/3",1925),m(1952,1,ii,FL),s.dk=function(n){return X(n,15)},s.ek=function(n){return oe(jr,Se,15,n,0,1)},v(kr,"XMLTypePackageImpl/30",1952),m(1953,1,ii,q5),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/31",1953),m(1954,1,ii,Yk),s.dk=function(n){return X(n,190)},s.ek=function(n){return oe(cp,Se,190,n,0,1)},v(kr,"XMLTypePackageImpl/32",1954),m(1955,1,ii,JL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/33",1955),m(1956,1,ii,HL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/34",1956),m(1957,1,ii,GL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/35",1957),m(1958,1,ii,qL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/36",1958),m(1959,1,ii,UL),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/37",1959),m(1960,1,ii,XL),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/38",1960),m(1961,1,ii,Qk),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/39",1961),m(1926,1,ii,KL),s.dk=function(n){return X(n,672)},s.ek=function(n){return oe(iI,On,2082,n,0,1)},v(kr,"XMLTypePackageImpl/4",1926),m(1962,1,ii,VL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/40",1962),m(1963,1,ii,ro),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/41",1963),m(1964,1,ii,hT),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/42",1964),m(1965,1,ii,yU),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/43",1965),m(1966,1,ii,YL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/44",1966),m(1967,1,ii,kU),s.dk=function(n){return X(n,191)},s.ek=function(n){return oe(up,Se,191,n,0,1)},v(kr,"XMLTypePackageImpl/45",1967),m(1968,1,ii,jU),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/46",1968),m(1969,1,ii,EU),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/47",1969),m(1970,1,ii,Wk),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/48",1970),m(1971,1,ii,U5),s.dk=function(n){return X(n,191)},s.ek=function(n){return oe(up,Se,191,n,0,1)},v(kr,"XMLTypePackageImpl/49",1971),m(1927,1,ii,dT),s.dk=function(n){return X(n,673)},s.ek=function(n){return oe(h7e,On,2083,n,0,1)},v(kr,"XMLTypePackageImpl/5",1927),m(1972,1,ii,Zk),s.dk=function(n){return X(n,190)},s.ek=function(n){return oe(cp,Se,190,n,0,1)},v(kr,"XMLTypePackageImpl/50",1972),m(1973,1,ii,bT),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/51",1973),m(1974,1,ii,Vp),s.dk=function(n){return X(n,15)},s.ek=function(n){return oe(jr,Se,15,n,0,1)},v(kr,"XMLTypePackageImpl/52",1974),m(1928,1,ii,Tb),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/6",1928),m(1929,1,ii,V6),s.dk=function(n){return X(n,195)},s.ek=function(n){return oe(ds,Se,195,n,0,2)},v(kr,"XMLTypePackageImpl/7",1929),m(1930,1,ii,SU),s.dk=function(n){return o2(n)},s.ek=function(n){return oe(Yi,Se,473,n,8,1)},v(kr,"XMLTypePackageImpl/8",1930),m(1931,1,ii,QL),s.dk=function(n){return X(n,221)},s.ek=function(n){return oe(py,Se,221,n,0,1)},v(kr,"XMLTypePackageImpl/9",1931);var rh,t0,hA,TG,J;m(53,63,H1,zt),v(Jd,"RegEx/ParseException",53),m(820,1,{},gT),s._l=function(n){return ni*16)throw $(new zt(Ht((Lt(),TZe))));i=i*16+c}while(!0);if(this.a!=125)throw $(new zt(Ht((Lt(),CZe))));if(i>l7)throw $(new zt(Ht((Lt(),OZe))));n=i}else{if(c=0,this.c!=0||(c=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(i=c,li(this),this.c!=0||(c=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));i=i*16+c,n=i}break;case 117:if(r=0,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));t=t*16+r,n=t;break;case 118:if(li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,t>l7)throw $(new zt(Ht((Lt(),"parser.descappe.4"))));n=t;break;case 65:case 90:case 122:throw $(new zt(Ht((Lt(),NZe))))}return n},s.bm=function(n){var t,i;switch(n){case 100:i=(this.e&32)==32?q0("Nd",!0):(fi(),CG);break;case 68:i=(this.e&32)==32?q0("Nd",!1):(fi(),k7e);break;case 119:i=(this.e&32)==32?q0("IsWord",!0):(fi(),V7);break;case 87:i=(this.e&32)==32?q0("IsWord",!1):(fi(),E7e);break;case 115:i=(this.e&32)==32?q0("IsSpace",!0):(fi(),Vy);break;case 83:i=(this.e&32)==32?q0("IsSpace",!1):(fi(),j7e);break;default:throw $(new hu((t=n,Nen+t.toString(16))))}return i},s.cm=function(n){var t,i,r,c,o,l,f,h,b,p,y,S;for(this.b=1,li(this),t=null,this.c==0&&this.a==94?(li(this),n?p=(fi(),fi(),new ul(5)):(t=(fi(),fi(),new ul(4)),ho(t,0,l7),p=new ul(4))):p=(fi(),fi(),new ul(4)),c=!0;(S=this.c)!=1&&!(S==0&&this.a==93&&!c);){if(c=!1,i=this.a,r=!1,S==10)switch(i){case 100:case 68:case 119:case 87:case 115:case 83:V2(p,this.bm(i)),r=!0;break;case 105:case 73:case 99:case 67:i=this.sm(p,i),i<0&&(r=!0);break;case 112:case 80:if(y=Q0e(this,i),!y)throw $(new zt(Ht((Lt(),Nne))));V2(p,y),r=!0;break;default:i=this.am()}else if(S==20){if(l=k9(this.i,58,this.d),l<0)throw $(new zt(Ht((Lt(),Y2e))));if(f=!0,ic(this.i,this.d)==94&&(++this.d,f=!1),o=of(this.i,this.d,l),h=P$e(o,f,(this.e&512)==512),!h)throw $(new zt(Ht((Lt(),EZe))));if(V2(p,h),r=!0,l+1>=this.j||ic(this.i,l+1)!=93)throw $(new zt(Ht((Lt(),Y2e))));this.d=l+2}if(li(this),!r)if(this.c!=0||this.a!=45)ho(p,i,i);else{if(li(this),(S=this.c)==1)throw $(new zt(Ht((Lt(),UF))));S==0&&this.a==93?(ho(p,i,i),ho(p,45,45)):(b=this.a,S==10&&(b=this.am()),li(this),ho(p,i,b))}(this.e&qf)==qf&&this.c==0&&this.a==44&&li(this)}if(this.c==1)throw $(new zt(Ht((Lt(),UF))));return t&&(dS(t,p),p=t),ov(p),aS(p),this.b=0,li(this),p},s.dm=function(){var n,t,i,r;for(i=this.cm(!1);(r=this.c)!=7;)if(n=this.a,r==0&&(n==45||n==38)||r==4){if(li(this),this.c!=9)throw $(new zt(Ht((Lt(),xZe))));if(t=this.cm(!1),r==4)V2(i,t);else if(n==45)dS(i,t);else if(n==38)cVe(i,t);else throw $(new hu("ASSERT"))}else throw $(new zt(Ht((Lt(),AZe))));return li(this),i},s.em=function(){var n,t;return n=this.a-48,t=(fi(),fi(),new FV(12,null,n)),!this.g&&(this.g=new PP),LP(this.g,new ooe(n)),li(this),t},s.fm=function(){return li(this),fi(),Shn},s.gm=function(){return li(this),fi(),Ehn},s.hm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.im=function(){throw $(new zt(Ht((Lt(),Ul))))},s.jm=function(){return li(this),wkn()},s.km=function(){return li(this),fi(),Ahn},s.lm=function(){return li(this),fi(),Thn},s.mm=function(){var n;if(this.d>=this.j||((n=ic(this.i,this.d++))&65504)!=64)throw $(new zt(Ht((Lt(),yZe))));return li(this),fi(),fi(),new Hh(0,n-64)},s.nm=function(){return li(this),B_n()},s.om=function(){return li(this),fi(),Chn},s.pm=function(){var n;return n=(fi(),fi(),new Hh(0,105)),li(this),n},s.qm=function(){return li(this),fi(),Mhn},s.rm=function(){return li(this),fi(),xhn},s.sm=function(n,t){return this.am()},s.tm=function(){return li(this),fi(),v7e},s.um=function(){var n,t,i,r,c;if(this.d+1>=this.j)throw $(new zt(Ht((Lt(),pZe))));if(r=-1,t=null,n=ic(this.i,this.d),49<=n&&n<=57){if(r=n-48,!this.g&&(this.g=new PP),LP(this.g,new ooe(r)),++this.d,ic(this.i,this.d)!=41)throw $(new zt(Ht((Lt(),bg))));++this.d}else switch(n==63&&--this.d,li(this),t=Oge(this),t.e){case 20:case 21:case 22:case 23:break;case 8:if(this.c!=7)throw $(new zt(Ht((Lt(),bg))));break;default:throw $(new zt(Ht((Lt(),mZe))))}if(li(this),c=Bw(this),i=null,c.e==2){if(c.Nm()!=2)throw $(new zt(Ht((Lt(),vZe))));i=c.Jm(1),c=c.Jm(0)}if(this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),fi(),fi(),new MRe(r,t,c,i)},s.vm=function(){return li(this),fi(),y7e},s.wm=function(){var n;if(li(this),n=vR(24,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.xm=function(){var n;if(li(this),n=vR(20,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.ym=function(){var n;if(li(this),n=vR(22,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.zm=function(){var n,t,i,r,c;for(n=0,i=0,t=-1;this.d=this.j)throw $(new zt(Ht((Lt(),K2e))));if(t==45){for(++this.d;this.d=this.j)throw $(new zt(Ht((Lt(),K2e))))}if(t==58){if(++this.d,li(this),r=bIe(Bw(this),n,i),this.c!=7)throw $(new zt(Ht((Lt(),bg))));li(this)}else if(t==41)++this.d,li(this),r=bIe(Bw(this),n,i);else throw $(new zt(Ht((Lt(),wZe))));return r},s.Am=function(){var n;if(li(this),n=vR(21,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.Bm=function(){var n;if(li(this),n=vR(23,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.Cm=function(){var n,t;if(li(this),n=this.f++,t=wV(Bw(this),n),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),t},s.Dm=function(){var n;if(li(this),n=wV(Bw(this),0),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.Em=function(n){return li(this),this.c==5?(li(this),aR(n,(fi(),fi(),new A2(9,n)))):aR(n,(fi(),fi(),new A2(3,n)))},s.Fm=function(n){var t;return li(this),t=(fi(),fi(),new Kj(2)),this.c==5?(li(this),ug(t,bA),ug(t,n)):(ug(t,n),ug(t,bA)),t},s.Gm=function(n){return li(this),this.c==5?(li(this),fi(),fi(),new A2(9,n)):(fi(),fi(),new A2(3,n))},s.a=0,s.b=0,s.c=0,s.d=0,s.e=0,s.f=1,s.g=null,s.j=0,v(Jd,"RegEx/RegexParser",820),m(1910,820,{},_xe),s._l=function(n){return!1},s.am=function(){return Lbe(this)},s.bm=function(n){return T8(n)},s.cm=function(n){return WVe(this)},s.dm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.em=function(){throw $(new zt(Ht((Lt(),Ul))))},s.fm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.gm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.hm=function(){return li(this),T8(67)},s.im=function(){return li(this),T8(73)},s.jm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.km=function(){throw $(new zt(Ht((Lt(),Ul))))},s.lm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.mm=function(){return li(this),T8(99)},s.nm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.om=function(){throw $(new zt(Ht((Lt(),Ul))))},s.pm=function(){return li(this),T8(105)},s.qm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.rm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.sm=function(n,t){return V2(n,T8(t)),-1},s.tm=function(){return li(this),fi(),fi(),new Hh(0,94)},s.um=function(){throw $(new zt(Ht((Lt(),Ul))))},s.vm=function(){return li(this),fi(),fi(),new Hh(0,36)},s.wm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.xm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.ym=function(){throw $(new zt(Ht((Lt(),Ul))))},s.zm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.Am=function(){throw $(new zt(Ht((Lt(),Ul))))},s.Bm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.Cm=function(){var n;if(li(this),n=wV(Bw(this),0),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.Dm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.Em=function(n){return li(this),aR(n,(fi(),fi(),new A2(3,n)))},s.Fm=function(n){var t;return li(this),t=(fi(),fi(),new Kj(2)),ug(t,n),ug(t,bA),t},s.Gm=function(n){return li(this),fi(),fi(),new A2(3,n)};var n5=null,X7=null;v(Jd,"RegEx/ParserForXMLSchema",1910),m(121,1,f7,aw),s.Hm=function(n){throw $(new hu("Not supported."))},s.Im=function(){return-1},s.Jm=function(n){return null},s.Km=function(){return null},s.Lm=function(n){},s.Mm=function(n){},s.Nm=function(){return 0},s.Ib=function(){return this.Om(0)},s.Om=function(n){return this.e==11?".":""},s.e=0;var w7e,K7,dA,jhn,p7e,Jm=null,CG,Uce=null,m7e,bA,Xce=null,v7e,y7e,k7e,j7e,E7e,Ehn,Vy,Shn,xhn,Ahn,Mhn,V7,Thn,Chn,WBn=v(Jd,"RegEx/Token",121);m(137,121,{3:1,137:1,121:1},ul),s.Om=function(n){var t,i,r;if(this.e==4)if(this==m7e)i=".";else if(this==CG)i="\\d";else if(this==V7)i="\\w";else if(this==Vy)i="\\s";else{for(r=new pd,r.a+="[",t=0;t0&&(r.a+=","),this.b[t]===this.b[t+1]?Bc(r,tN(this.b[t])):(Bc(r,tN(this.b[t])),r.a+="-",Bc(r,tN(this.b[t+1])));r.a+="]",i=r.a}else if(this==k7e)i="\\D";else if(this==E7e)i="\\W";else if(this==j7e)i="\\S";else{for(r=new pd,r.a+="[^",t=0;t0&&(r.a+=","),this.b[t]===this.b[t+1]?Bc(r,tN(this.b[t])):(Bc(r,tN(this.b[t])),r.a+="-",Bc(r,tN(this.b[t+1])));r.a+="]",i=r.a}return i},s.a=!1,s.c=!1,v(Jd,"RegEx/RangeToken",137),m(580,1,{580:1},ooe),s.a=0,v(Jd,"RegEx/RegexParser/ReferencePosition",580),m(579,1,{3:1,579:1},wMe),s.Fb=function(n){var t;return n==null||!X(n,579)?!1:(t=u(n,579),bn(this.b,t.b)&&this.a==t.a)},s.Hb=function(){return Od(this.b+"/"+Tbe(this.a))},s.Ib=function(){return this.c.Om(this.a)},s.a=0,v(Jd,"RegEx/RegularExpression",579),m(228,121,f7,Hh),s.Im=function(){return this.a},s.Om=function(n){var t,i,r;switch(this.e){case 0:switch(this.a){case 124:case 42:case 43:case 63:case 40:case 41:case 46:case 91:case 123:case 92:r="\\"+JK(this.a&yr);break;case 12:r="\\f";break;case 10:r="\\n";break;case 13:r="\\r";break;case 9:r="\\t";break;case 27:r="\\e";break;default:this.a>=Ec?(i=(t=this.a>>>0,"0"+t.toString(16)),r="\\v"+of(i,i.length-6,i.length)):r=""+JK(this.a&yr)}break;case 8:this==v7e||this==y7e?r=""+JK(this.a&yr):r="\\"+JK(this.a&yr);break;default:r=null}return r},s.a=0,v(Jd,"RegEx/Token/CharToken",228),m(322,121,f7,A2),s.Jm=function(n){return this.a},s.Lm=function(n){this.b=n},s.Mm=function(n){this.c=n},s.Nm=function(){return 1},s.Om=function(n){var t;if(this.e==3)if(this.c<0&&this.b<0)t=this.a.Om(n)+"*";else if(this.c==this.b)t=this.a.Om(n)+"{"+this.c+"}";else if(this.c>=0&&this.b>=0)t=this.a.Om(n)+"{"+this.c+","+this.b+"}";else if(this.c>=0&&this.b<0)t=this.a.Om(n)+"{"+this.c+",}";else throw $(new hu("Token#toString(): CLOSURE "+this.c+Co+this.b));else if(this.c<0&&this.b<0)t=this.a.Om(n)+"*?";else if(this.c==this.b)t=this.a.Om(n)+"{"+this.c+"}?";else if(this.c>=0&&this.b>=0)t=this.a.Om(n)+"{"+this.c+","+this.b+"}?";else if(this.c>=0&&this.b<0)t=this.a.Om(n)+"{"+this.c+",}?";else throw $(new hu("Token#toString(): NONGREEDYCLOSURE "+this.c+Co+this.b));return t},s.b=0,s.c=0,v(Jd,"RegEx/Token/ClosureToken",322),m(821,121,f7,zfe),s.Jm=function(n){return n==0?this.a:this.b},s.Nm=function(){return 2},s.Om=function(n){var t;return this.b.e==3&&this.b.Jm(0)==this.a?t=this.a.Om(n)+"+":this.b.e==9&&this.b.Jm(0)==this.a?t=this.a.Om(n)+"+?":t=this.a.Om(n)+(""+this.b.Om(n)),t},v(Jd,"RegEx/Token/ConcatToken",821),m(1908,121,f7,MRe),s.Jm=function(n){if(n==0)return this.d;if(n==1)return this.b;throw $(new hu("Internal Error: "+n))},s.Nm=function(){return this.b?2:1},s.Om=function(n){var t;return this.c>0?t="(?("+this.c+")":this.a.e==8?t="(?("+this.a+")":t="(?"+this.a,this.b?t+=this.d+"|"+this.b+")":t+=this.d+")",t},s.c=0,v(Jd,"RegEx/Token/ConditionToken",1908),m(1909,121,f7,aLe),s.Jm=function(n){return this.b},s.Nm=function(){return 1},s.Om=function(n){return"(?"+(this.a==0?"":Tbe(this.a))+(this.c==0?"":Tbe(this.c))+":"+this.b.Om(n)+")"},s.a=0,s.c=0,v(Jd,"RegEx/Token/ModifierToken",1909),m(822,121,f7,Yfe),s.Jm=function(n){return this.a},s.Nm=function(){return 1},s.Om=function(n){var t;switch(t=null,this.e){case 6:this.b==0?t="(?:"+this.a.Om(n)+")":t="("+this.a.Om(n)+")";break;case 20:t="(?="+this.a.Om(n)+")";break;case 21:t="(?!"+this.a.Om(n)+")";break;case 22:t="(?<="+this.a.Om(n)+")";break;case 23:t="(?"+this.a.Om(n)+")"}return t},s.b=0,v(Jd,"RegEx/Token/ParenToken",822),m(517,121,{3:1,121:1,517:1},FV),s.Km=function(){return this.b},s.Om=function(n){return this.e==12?"\\"+this.a:IOn(this.b)},s.a=0,v(Jd,"RegEx/Token/StringToken",517),m(466,121,f7,Kj),s.Hm=function(n){ug(this,n)},s.Jm=function(n){return u(Ew(this.a,n),121)},s.Nm=function(){return this.a?this.a.a.c.length:0},s.Om=function(n){var t,i,r,c,o;if(this.e==1){if(this.a.a.c.length==2)t=u(Ew(this.a,0),121),i=u(Ew(this.a,1),121),i.e==3&&i.Jm(0)==t?c=t.Om(n)+"+":i.e==9&&i.Jm(0)==t?c=t.Om(n)+"+?":c=t.Om(n)+(""+i.Om(n));else{for(o=new pd,r=0;r=this.c.b:this.a<=this.c.b},s.Sb=function(){return this.b>0},s.Tb=function(){return this.b},s.Vb=function(){return this.b-1},s.Qb=function(){throw $(new gd(Ren))},s.a=0,s.b=0,v(gme,"ExclusiveRange/RangeIterator",259);var Wl=I9(XF,"C"),$t=I9(FS,"I"),ts=I9(ry,"Z"),Ep=I9(JS,"J"),ds=I9(RS,"B"),Jr=I9(BS,"D"),Hm=I9(zS,"F"),t5=I9(HS,"S"),ZBn=Hi("org.eclipse.elk.core.labels","ILabelManager"),S7e=Hi(yc,"DiagnosticChain"),x7e=Hi(hen,"ResourceSet"),A7e=v(yc,"InvocationTargetException",null),Nhn=(JP(),G6n),Dhn=Dhn=EAn;F8n(dbn),i7n("permProps",[[["locale","default"],[Ben,"gecko1_8"]],[["locale","default"],[Ben,"safari"]]]),Dhn(null,"elk",null)}).call(this)}).call(this,typeof _hn<"u"?_hn:typeof self<"u"?self:typeof window<"u"?window:{})},{}],3:[function(x,M,N){function P(ge){"@babel/helpers - typeof";return P=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(Pe){return typeof Pe}:function(Pe){return Pe&&typeof Symbol=="function"&&Pe.constructor===Symbol&&Pe!==Symbol.prototype?"symbol":typeof Pe},P(ge)}function k(ge,Pe,ae){return Object.defineProperty(ge,"prototype",{writable:!1}),ge}function H(ge,Pe){if(!(ge instanceof Pe))throw new TypeError("Cannot call a class as a function")}function U(ge,Pe,ae){return Pe=W(Pe),G(ge,Z()?Reflect.construct(Pe,ae||[],W(ge).constructor):Pe.apply(ge,ae))}function G(ge,Pe){if(Pe&&(P(Pe)=="object"||typeof Pe=="function"))return Pe;if(Pe!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return ie(ge)}function ie(ge){if(ge===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return ge}function Z(){try{var ge=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch{}return(Z=function(){return!!ge})()}function W(ge){return W=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(Pe){return Pe.__proto__||Object.getPrototypeOf(Pe)},W(ge)}function se(ge,Pe){if(typeof Pe!="function"&&Pe!==null)throw new TypeError("Super expression must either be null or a function");ge.prototype=Object.create(Pe&&Pe.prototype,{constructor:{value:ge,writable:!0,configurable:!0}}),Object.defineProperty(ge,"prototype",{writable:!1}),Pe&&le(ge,Pe)}function le(ge,Pe){return le=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(ae,Me){return ae.__proto__=Me,ae},le(ge,Pe)}var ee=x("./elk-api.js").default,Oe=(function(ge){function Pe(){var ae=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};H(this,Pe);var Me=Object.assign({},ae),Be=!1;try{x.resolve("web-worker"),Be=!0}catch{}if(ae.workerUrl)if(Be){var rn=x("web-worker");Me.workerFactory=function(hn){return new rn(hn)}}else console.warn(`Web worker requested but 'web-worker' package not installed. +Consider installing the package or pass your own 'workerFactory' to ELK's constructor. +... Falling back to non-web worker version.`);if(!Me.workerFactory){var ln=x("./elk-worker.min.js"),xn=ln.Worker;Me.workerFactory=function(hn){return new xn(hn)}}return U(this,Pe,[Me])}return se(Pe,ge),k(Pe)})(ee);Object.defineProperty(M.exports,"__esModule",{value:!0}),M.exports=Oe,Oe.default=Oe},{"./elk-api.js":1,"./elk-worker.min.js":2,"web-worker":4}],4:[function(x,M,N){var P=typeof Worker<"u"?Worker:void 0;M.exports=P},{}]},{},[3])(3)})})(K7e)),K7e.exports}var rKn=iKn();const cKn=bke(rKn),uKn=new cKn;async function oKn(g,E,x){const M={id:"root",layoutOptions:sKn(x),children:g.map(k=>({id:k.id,width:lKn(k.data),height:fKn(k.data),ports:k.data.nodeKind==="application"?[...k.data.inputs.map((H,U)=>lue(H.id,"WEST",U)),...k.data.outputs.map((H,U)=>lue(H.id,"EAST",U))]:[...(k.data.inputPortIds??[]).map((H,U)=>lue(H,"WEST",U)),...(k.data.outputPortIds??[]).map((H,U)=>lue(H,"EAST",U))],layoutOptions:{"org.eclipse.elk.portConstraints":"FIXED_ORDER"}})),edges:E.map(k=>({id:k.id,sources:[k.sourceHandle??k.source],targets:[k.targetHandle??k.target]}))},N=await uKn.layout(M),P=new Map((N.children??[]).map(k=>[k.id,{x:k.x??0,y:k.y??0}]));return g.map(k=>({...k,position:P.get(k.id)??k.position}))}function sKn(g){return{"elk.algorithm":g==="topology"?"mrtree":"layered","elk.direction":g==="topology"?"DOWN":"RIGHT","elk.spacing.nodeNode":g==="overview"?"24":g==="compact"?"32":"56","elk.layered.spacing.nodeNodeBetweenLayers":g==="overview"?"48":g==="compact"?"60":"110","elk.layered.nodePlacement.strategy":"BRANDES_KOEPF","elk.layered.crossingMinimization.semiInteractive":"true","elk.edgeRouting":"ORTHOGONAL"}}function lue(g,E,x){return{id:g,width:9,height:9,layoutOptions:{"org.eclipse.elk.port.side":E,"org.eclipse.elk.port.index":String(x)}}}function lKn(g){return g.nodeKind==="application"?Z0n(g):240}function fKn(g){return g.nodeKind!=="application"?112:g.detailMode==="overview"?108:Math.max(178,142+Math.max(g.inputs.length,g.outputs.length)*27)}const V7e=Ike("source","degree_days","ToyDegreeDaysCumulModel",[],["TT_cu"]),fue=Ike("lai","lai_dynamic","ToyLAIModel",["TT_cu"],["LAI"]),Y7e=Ike("light","light_interception","Beer",["LAI"],["aPPFD"]),edn={schemaVersion:1,level:"applications",metadata:{title:"PlantSimEngine Scene Graph",sceneRevision:0,objectCount:1,instanceCount:0,applicationCount:3,executionCount:3,bindingCount:2,callCount:0,unresolvedInitializationCount:1,cyclic:!1,strictlyCompiled:!0},objects:[{id:"object:plant",objectId:"plant",scale:"Plant",kind:"plant",species:null,name:"plant",instance:null,parent:null,children:[],hasGeometry:!1,hasStatus:!0}],instances:[],applications:[V7e,fue,Y7e],executions:[V7e,fue,Y7e].map(g=>({id:`execution:${g.applicationId}:plant`,applicationId:g.applicationId,applicationNodeId:g.id,objectId:"plant",objectNodeId:"object:plant",modelType:g.modelType,modelParameters:{},overridden:!1})),edges:[tdn(V7e,"TT_cu",fue,"TT_cu"),tdn(fue,"LAI",Y7e,"LAI")],modelLibrary:[],initialization:[{applicationId:"source",objectId:"plant",variable:"TT",role:"input",disposition:"unresolved",value:"-Inf",valueJulia:"-Inf",expectedType:"Float64",sourceApplicationIds:[],sourceObjectIds:[],sourceVariable:null,origin:"missing",previousTimeStep:!1}],diagnostics:[],cycles:[],availableActions:["inspect"]};function Ike(g,E,x,M,N){return{id:`application:${g}`,applicationId:g,name:g,process:E,modelType:x,modelName:x,module:"PlantSimEngine.Examples",package:"PlantSimEngine",modelParameters:{},selector:{type:"One",multiplicity:"one",criteria:{scale:"Plant"},julia:"One(scale=:Plant)"},targetIds:["plant"],targetCount:1,targetScales:["Plant"],targetKinds:["plant"],targetSpecies:[],targetInstances:[],timestep:null,clock:null,inputs:M.map(P=>ndn(g,"input",P)),outputs:N.map(P=>ndn(g,"output",P)),environmentInputs:[],environmentOutputs:[],inputBindings:{},callBindings:{},environment:null,meteoBindings:{},meteoWindow:null,outputRouting:{},updates:[],modelStorage:"shared_application",objectOverrides:[]}}function ndn(g,E,x){return{id:`application:${g}:${E}:${x}`,name:x,role:E,default:"-Inf",defaultJulia:"-Inf",expectedType:"Float64"}}function tdn(g,E,x,M){return{id:`binding:${g.applicationId}:${E}:${x.applicationId}:${M}`,source:g.id,target:x.id,sourcePort:`application:${g.applicationId}:output:${E}`,targetPort:`application:${x.applicationId}:input:${M}`,sourceVariable:E,targetVariable:M,sourceApplicationId:g.applicationId,targetApplicationId:x.applicationId,kind:"inferred_same_object",cycle:!1,projection:"applications"}}const aKn={application:YXn,entity:QXn},hKn={sceneEdge:nKn};function dKn(){const[g,E]=He.useState(W7e),[x,M]=He.useState(()=>W7e().level),[N,P]=He.useState(()=>W7e().metadata.applicationCount>24?"overview":"detail"),[k,H]=He.useState(""),[U,G]=He.useState(null),[ie,Z]=He.useState(null),[W,se]=He.useState(null),[le,ee]=He.useState(null),[Oe,ge]=He.useState(!1),[Pe,ae]=He.useState(!1),[Me,Be]=He.useState(!1),[rn,ln]=He.useState(!1),[xn,hn]=He.useState(!1),[wt,Cn]=He.useState(""),[Qn,Y]=He.useState(null),[Fe,mn]=He.useState(null),[Ae,Ze]=He.useState([]),[sn,Sn]=He.useState(null),[kn,xe]=He.useState(!1),[un,rt]=He.useState(!1),[lt,Bt]=He.useState(!1),[hi,Gt]=He.useState(null),[At,st]=He.useState(null),[Wr,Mr]=He.useState(null),[ur,mi]=He.useState(null),[Fi,fu]=He.useState(null),[Eu,Ws]=He.useState(null),[Dh,ef]=He.useState(null),[ch,kc]=He.useState(null),[cd,jb]=He.useState(!1),[Rs,c0]=He.useState(null),[u0,o0,Bg]=HUn([]),[r6,zg,c6]=GUn([]),d1=He.useMemo(IKn,[]),ud=He.useMemo(()=>new Map(g.applications.map(Mt=>[Mt.applicationId,Mt])),[g.applications]),Sf=He.useMemo(()=>new Set(g.initialization.filter(Mt=>Mt.role==="input"&&Mt.disposition==="unresolved").map(Mt=>Q7e(Mt.applicationId,"input",Mt.variable))),[g.initialization]),b1=He.useMemo(()=>new Set(g.initialization.filter(Mt=>Mt.role==="input"&&Mt.previousTimeStep).map(Mt=>Q7e(Mt.applicationId,"input",Mt.variable))),[g.initialization]),xf=He.useMemo(()=>new Set(g.cycles.flatMap(Mt=>Mt.applicationIds)),[g.cycles]),l5=He.useMemo(()=>new Set(g.cycles.flatMap(Mt=>Mt.breakCandidates.map(Tc=>Q7e(Tc.applicationId,"input",Tc.input)))),[g.cycles]),f5=He.useMemo(()=>vKn(g),[g]),a5=He.useMemo(()=>le?yKn(g.modelLibrary,le.port):[],[le,g.modelLibrary]),Fg=He.useMemo(()=>le?jKn(g.applications,le):[],[le,g.applications]),Eb=He.useMemo(()=>{const Mt=new Map;for(const Tc of g.applications)for(const dc of[...Tc.inputs,...Tc.outputs])Mt.set(dc.id,{application:Tc,port:dc});return Mt},[g.applications]),Jg=He.useMemo(()=>ie?new Set(ie.objectIds.map(n6)):null,[ie]);He.useEffect(()=>{if(!d1?.websocketUrl)return;const Mt=new WebSocket(d1.websocketUrl);return Sn(Mt),Mt.addEventListener("open",()=>{xe(!0),Gt(null)}),Mt.addEventListener("close",()=>{xe(!1),Gt("Editor connection closed.")}),Mt.addEventListener("message",Tc=>{const dc=JSON.parse(Tc.data);dc.graph&&E(dc.graph),typeof dc.sceneCode=="string"&&Cn(dc.sceneCode),Y(dc.autosavePath??null),mn(dc.savePath??null),Ze(dc.recentPaths??[]),dc.selectorPreview&&kc(dc.selectorPreview),dc.targetPreview&&Mr(dc.targetPreview),dc.ok===!1&&(kc(null),Mr(null)),rt(!!dc.canUndo),Bt(!!dc.canRedo),Gt(dc.ok===!1?dc.diagnostics?.[0]||"The edit failed.":null)}),()=>Mt.close()},[d1?.websocketUrl]),He.useEffect(()=>{g.metadata.cyclic||(jb(!1),c0(null))},[g.metadata.cyclic]);const _u=He.useCallback(Mt=>{if(!sn||sn.readyState!==WebSocket.OPEN){Gt("This action requires an interactive Julia editor session.");return}sn.send(JSON.stringify(Mt))},[sn]),Hg=He.useCallback((Mt,Tc,dc)=>{G(Mt),se(Tc),ee({application:Mt,port:Tc,x:dc.x,y:dc.y})},[]);He.useEffect(()=>{const Mt=bKn({graph:g,view:x,detailMode:N,query:k,scopedObjectIds:Jg,unresolvedPortIds:Sf,previousPortIds:b1,candidatePortIds:f5,cyclicApplications:xf,cycleBreakPortIds:l5,cycleBreakMode:cd,openCandidates:Hg,onPortClick:se,onCycleBreak:(Af,Zs)=>c0({application:Af,port:Zs})}),Tc=new Set(Mt.map(Af=>Af.id)),dc=gKn(g,x).filter(Af=>Tc.has(Af.source)&&Tc.has(Af.target));oKn(Mt,dc,x==="topology"?"topology":N==="overview"?"overview":"data_flow").then(o0),zg(dc)},[f5,cd,l5,xf,N,g,Hg,b1,k,Jg,zg,o0,Sf,x]);const Gg=He.useCallback((Mt,Tc)=>{if(Tc.data.nodeKind==="application")G(ud.get(Tc.data.applicationId)??null);else if(G(Tc.data.detail),Tc.data.nodeKind==="object"){const dc=Tc.data.detail;Z({label:`subtree ${dc.name||String(dc.objectId)}`,objectIds:pKn(g.objects,dc.objectId)})}else if(Tc.data.nodeKind==="instance"){const dc=Tc.data.detail;Z({label:`instance ${dc.name}`,objectIds:dc.objectIds})}else Tc.data.nodeKind==="scene"&&Z(null);se(null)},[ud,g.objects]),u6=He.useCallback(Mt=>{le&&(Mr(null),st({mode:"add",initialModelType:Mt.type,suggestedSelector:DKn(le.application)}),kn||Gt(`${Mt.name} matches ${le.port.name}. Start an interactive Julia editor session to add it to the Scene.`),ee(null))},[le,kn]),h5=He.useCallback(Mt=>{if(!kn){Gt("Adding or updating an application requires an interactive Julia editor session."),st(null);return}_u({action:"edit",kind:Mt.applicationId?At?.scope==="template"?"update_template_application":"update_application":"add_application",instance:At?.instance,...Mt}),st(null)},[At?.instance,At?.scope,kn,_u]),Wm=He.useCallback(Mt=>{if(!kn){Gt("Creating a binding requires an interactive Julia editor session."),ef(null);return}_u({action:"edit",kind:"set_input_binding",...Mt}),ef(null)},[kn,_u]),qg=He.useCallback(Mt=>{if(!kn){Gt("Adding or updating an object requires an interactive Julia editor session."),mi(null);return}_u({action:"edit",kind:ur?.mode==="update"?"update_object":"add_object",objectId:Mt.objectId,configuration:Mt.configuration}),mi(null)},[kn,ur?.mode,_u]),o6=He.useCallback(Mt=>{if(!kn){Gt("Creating an override requires an interactive Julia editor session."),fu(null);return}_u({action:"edit",kind:Mt.scope==="instance"?"set_instance_override":"set_object_override",...Mt}),fu(null)},[kn,_u]),Ug=He.useCallback(Mt=>{if(!kn){Gt("Removing an override requires an interactive Julia editor session.");return}_u({action:"edit",kind:Mt.scope==="instance"?"remove_instance_override":"remove_object_override",...Mt}),fu(null)},[kn,_u]),Zm=He.useCallback(Mt=>{if(!Mt.sourceHandle||!Mt.targetHandle)return;const Tc=Eb.get(Mt.sourceHandle),dc=Eb.get(Mt.targetHandle);if(!Tc||!dc||Tc.port.role!=="output"||dc.port.role!=="input"){Gt("Connect an application output to an application input.");return}ef({sourceApplication:Tc.application,sourcePort:Tc.port,targetApplication:dc.application,targetPort:dc.port}),kc(null)},[Eb]),d5=He.useMemo(()=>{if(!U)return g.initialization;if("applicationId"in U)return g.initialization.filter(Mt=>Mt.applicationId===U.applicationId);if("objectId"in U)return g.initialization.filter(Mt=>String(Mt.objectId)===String(U.objectId));if("objectIds"in U){const Mt=new Set(U.objectIds.map(n6));return g.initialization.filter(Tc=>Mt.has(n6(Tc.objectId)))}return g.initialization},[g.initialization,U]);return F.jsxs("main",{className:"scene-editor-shell","data-testid":"scene-graph-viewer",children:[F.jsxs("header",{className:"scene-toolbar",children:[F.jsxs("div",{className:"scene-brand",children:[F.jsx("span",{className:"brand-mark"}),F.jsxs("div",{children:[F.jsx("small",{children:"PLANTSIMENGINE"}),F.jsx("strong",{children:"Scene Graph"})]})]}),F.jsxs("div",{className:"scene-search",children:[F.jsx(IXn,{size:17}),F.jsx("input",{value:k,onChange:Mt=>H(Mt.target.value),placeholder:"Search application, object, or variable"}),k&&F.jsx("button",{"aria-label":"Clear search",onClick:()=>H(""),children:F.jsx(Ym,{size:15})})]}),F.jsxs("div",{className:"scene-counts",children:[F.jsxs("span",{children:[g.metadata.applicationCount," applications"]}),F.jsxs("span",{children:[g.metadata.objectCount," objects"]}),g.metadata.unresolvedInitializationCount>0&&F.jsxs("button",{className:"count-warning",onClick:()=>ae(!0),children:[F.jsx(SXn,{size:14})," ",g.metadata.unresolvedInitializationCount," init"]}),g.diagnostics.length>0&&F.jsxs("button",{className:"count-error",onClick:()=>ge(!0),children:[F.jsx(dke,{size:14})," ",g.diagnostics.length]})]}),F.jsxs("nav",{className:"view-tabs","aria-label":"Graph projection",children:[F.jsxs("button",{className:x==="applications"?"active":"",onClick:()=>M("applications"),children:[F.jsx(Y0n,{size:15})," Applications"]}),F.jsxs("button",{className:x==="topology"?"active":"",onClick:()=>M("topology"),children:[F.jsx(TXn,{size:15})," Objects"]}),F.jsxs("button",{className:x==="resolved"?"active":"",onClick:()=>M("resolved"),children:[F.jsx(CXn,{size:15})," Executions"]})]}),F.jsxs("div",{className:"scene-actions",children:[d1&&F.jsxs("button",{"data-testid":"open-scene",onClick:()=>ln(!0),children:[F.jsx(MXn,{size:15})," Open"]}),d1&&F.jsxs("button",{"data-testid":"save-scene",onClick:()=>hn(!0),children:[F.jsx(DXn,{size:15})," ",Fe?"Saved":"Save"]}),x!=="topology"&&F.jsx("button",{className:N==="overview"?"overview-cta":"",onClick:()=>P(Mt=>Mt==="overview"?"detail":"overview"),children:N==="overview"?"Overview Mode - Show Detailed View":"Show Overview"}),d1&&F.jsxs("button",{"data-testid":"add-application",onClick:()=>{Mr(null),st({mode:"add"})},children:[F.jsx(QG,{size:15})," Add application"]}),d1&&F.jsxs("button",{"data-testid":"add-object",onClick:()=>mi({mode:"add"}),children:[F.jsx(QG,{size:15})," Add object"]}),d1&&F.jsx("button",{disabled:!un,onClick:()=>_u({action:"undo"}),"aria-label":"Undo",children:F.jsx(_Xn,{size:15})}),d1&&F.jsx("button",{disabled:!lt,onClick:()=>_u({action:"redo"}),"aria-label":"Redo",children:F.jsx(OXn,{size:15})}),F.jsxs("button",{onClick:()=>Be(!0),children:[F.jsx(AXn,{size:15})," Scene code"]})]})]}),g.metadata.cyclic&&F.jsxs("section",{className:"cycle-callout","data-testid":"cycle-callout",children:[F.jsx(dke,{size:19}),F.jsxs("div",{children:[F.jsx("strong",{children:"Current-step dependency cycle"}),F.jsx("span",{children:"Select a cycle input to read its previous accepted timestep value."})]}),F.jsx("button",{className:cd?"active":"",onClick:()=>{M("applications"),P("detail"),jb(Mt=>!Mt)},"data-testid":"choose-cycle-break",children:cd?"Cancel break selection":"Choose a break point in graph"})]}),hi&&F.jsxs("div",{className:"editor-feedback",children:[hi,F.jsx("button",{onClick:()=>Gt(null),children:F.jsx(Ym,{size:14})})]}),ie&&F.jsxs("section",{className:"graph-scope-filter","data-testid":"graph-scope-filter",children:[F.jsxs("span",{children:["Showing ",x==="resolved"?"executions":x==="applications"?"applications":"topology"," for ",F.jsx("strong",{children:ie.label})," (",ie.objectIds.length," objects)"]}),x==="topology"&&F.jsx("button",{onClick:()=>M("applications"),children:"Show related applications"}),F.jsxs("button",{"aria-label":"Clear graph scope",onClick:()=>Z(null),children:[F.jsx(Ym,{size:14})," Clear"]})]}),F.jsxs("section",{className:"scene-workspace",children:[F.jsx("div",{className:"flow-wrap",children:F.jsxs(zUn,{nodes:u0,edges:r6,nodeTypes:aKn,edgeTypes:hKn,onNodesChange:Bg,onEdgesChange:c6,onConnect:Zm,onNodeClick:Gg,onEdgeClick:(Mt,Tc)=>G(Tc.data??null),fitView:!0,minZoom:.05,maxZoom:2,children:[F.jsx(VUn,{color:"#d8cdbc",gap:22,size:1}),F.jsx(tXn,{}),F.jsx(gXn,{pannable:!0,zoomable:!0})]})}),F.jsx(SKn,{selection:U,port:W,initialization:d5,interactive:kn,onEditApplication:Mt=>{Mr(null),st({mode:"update",scope:Mt.targetInstances.length>0?"template":"application",instance:Mt.targetInstances[0],application:Mt})},onRemoveApplication:Mt=>_u({action:"edit",kind:Mt.targetInstances.length>0?"remove_template_application":"remove_application",instance:Mt.targetInstances[0],applicationId:Mt.applicationId}),onConfigureApplication:Mt=>Ws(Mt.applicationId),onOverrideApplication:fu,onEditObject:Mt=>mi({mode:"update",object:Mt}),onRemoveObject:Mt=>_u({action:"edit",kind:"remove_object",objectId:Mt.objectId,recursive:!0})})]}),le&&(a5.length>0||Fg.length>0)&&F.jsx(kKn,{candidate:le,models:a5,applications:Fg,onSelectModel:u6,onSelectApplication:Mt=>{ef(EKn(le,Mt)),kc(null),ee(null)},onClose:()=>ee(null)}),Oe&&F.jsx(xKn,{graph:g,onClose:()=>ge(!1),sendCommand:_u,interactive:kn}),Pe&&F.jsx(AKn,{graph:g,onClose:()=>ae(!1),sendCommand:_u,interactive:kn}),Me&&F.jsx(TKn,{code:wt,onClose:()=>Be(!1)}),rn&&F.jsx(udn,{mode:"open",recentPaths:Ae,currentPath:Fe,autosavePath:Qn,onSubmit:Mt=>{_u({action:"open_scene_code",path:Mt}),ln(!1)},onClose:()=>ln(!1)}),xn&&F.jsx(udn,{mode:"save",recentPaths:Ae,currentPath:Fe,autosavePath:Qn,onSubmit:Mt=>{_u({action:"save_scene_code",path:Mt}),hn(!1)},onClose:()=>hn(!1)}),At&&F.jsx(LXn,{mode:At.mode,models:g.modelLibrary,objects:g.objects,application:At.application,initialModelType:At.initialModelType,suggestedSelector:At.suggestedSelector,nameReadOnly:At.scope==="template",preview:Wr,onPreview:Mt=>{Mr(null),_u({action:"preview_application_targets",selector:Mt})},onSubmit:h5,onClose:()=>{st(null),Mr(null)}}),Dh&&F.jsx(FXn,{endpoints:Dh,objects:g.objects,preview:ch,onPreview:Mt=>{kc(null),_u({action:"preview_input_binding",...Mt})},onSubmit:Wm,onClose:()=>{ef(null),kc(null)}}),ur&&F.jsx(UXn,{mode:ur.mode,objects:g.objects,object:ur.object,onSubmit:qg,onClose:()=>mi(null)}),Fi&&F.jsx(KXn,{application:Fi,models:g.modelLibrary,instances:g.instances,onSubmit:o6,onRemove:Ug,onClose:()=>fu(null)}),Eu&&ud.get(Eu)&&F.jsx(zXn,{application:ud.get(Eu),applications:g.applications,onCommand:_u,onClose:()=>Ws(null)}),Rs&&F.jsx(CKn,{selection:Rs,initialization:g.initialization,onSubmit:(Mt,Tc)=>{_u({action:"edit",kind:"break_cycle",applicationId:Rs.application.applicationId,input:Rs.port.name,initializeMissing:Mt,initialValue:Tc}),c0(null)},onClose:()=>c0(null)})]})}function bKn({graph:g,view:E,detailMode:x,query:M,scopedObjectIds:N,unresolvedPortIds:P,previousPortIds:k,candidatePortIds:H,cyclicApplications:U,cycleBreakPortIds:G,cycleBreakMode:ie,openCandidates:Z,onPortClick:W,onCycleBreak:se}){const le=Oe=>!M||JSON.stringify(Oe).toLowerCase().includes(M.toLowerCase());if(E==="topology"){const Oe={entity:"scene",objectCount:g.metadata.objectCount,instanceCount:g.metadata.instanceCount,applicationCount:g.metadata.applicationCount},ge={id:"scene:root",type:"entity",position:{x:0,y:0},data:{nodeKind:"scene",title:g.metadata.title||"Scene",subtitle:"scene root",badges:[`${g.metadata.instanceCount} instances`,`${g.metadata.objectCount} objects`],detail:Oe}},Pe=g.instances.filter(le).map(Me=>({id:Me.id,type:"entity",position:{x:0,y:0},data:{nodeKind:"instance",title:Me.name,subtitle:[Me.kind,Me.species].filter(Boolean).join(" · ")||"object instance",badges:[`${Me.objectIds.length} objects`,`${Me.applicationIds.length} applications`,`${Me.instanceOverrides.length+Me.objectOverrides.length} overrides`],detail:Me}})),ae=g.objects.filter(le).map(Me=>({id:Me.id,type:"entity",position:{x:0,y:0},data:{nodeKind:"object",title:Me.name||String(Me.objectId),subtitle:[Me.kind,Me.scale,Me.instance].filter(Boolean).join(" · "),badges:[Me.species,Me.hasStatus?"status":null,Me.hasGeometry?"geometry":null].filter(Boolean),detail:Me}}));return[ge,...Pe,...ae]}if(E==="resolved"){const Oe=new Map(g.applications.map(Pe=>[Pe.applicationId,Pe]));return[...g.executions.filter(Pe=>!N||N.has(n6(Pe.objectId))).filter(le).map(Pe=>{const ae=Oe.get(Pe.applicationId);return{id:Pe.id,type:"entity",position:{x:0,y:0},data:{nodeKind:"execution",title:Pe.applicationId,subtitle:`object ${String(Pe.objectId)}`,badges:[NKn(Pe.modelType),Pe.overridden?"override":"shared"],inputPortIds:[...ae?.inputs??[],...ae?.environmentInputs??[]].map(Me=>Me.id),outputPortIds:[...ae?.outputs??[],...ae?.environmentOutputs??[]].map(Me=>Me.id),detail:Pe}}}),...idn(g,"resolved")]}return[...g.applications.filter(Oe=>!N||Oe.targetIds.some(ge=>N.has(n6(ge)))).filter(le).map(Oe=>({id:Oe.id,type:"application",position:{x:0,y:0},data:{...Oe,nodeKind:"application",detailMode:x,cyclic:U.has(Oe.applicationId),requiredInputPortIds:Oe.inputs.filter(ge=>P.has(ge.id)).map(ge=>ge.id),candidatePortIds:[...Oe.inputs,...Oe.outputs].filter(ge=>H.has(ge.id)).map(ge=>ge.id),previousTimeStepPortIds:Oe.inputs.filter(ge=>k.has(ge.id)).map(ge=>ge.id),cycleBreakInputPortIds:Oe.inputs.filter(ge=>G.has(ge.id)).map(ge=>ge.id),cycleBreakMode:ie,onCandidateClick:(ge,Pe)=>Z(Oe,ge,Pe),onPortClick:W,onCycleBreak:se}})),...idn(g,"applications")]}function idn(g,E){const x=g.edges.filter(N=>N.kind==="environment_binding"&&N.projection===E);return[...new Set(x.flatMap(N=>[N.source,N.target]).filter(N=>N.startsWith("environment:")))].map(N=>{const P=N.slice(12),k=rdn(x.filter(U=>U.target===N).map(U=>U.targetPort).filter(Boolean)),H=rdn(x.filter(U=>U.source===N).map(U=>U.sourcePort).filter(Boolean));return{id:N,type:"entity",position:{x:0,y:0},data:{nodeKind:"environment",title:P,subtitle:"environment provider",badges:[`${H.length} inputs`,`${k.length} outputs`],inputPortIds:k,outputPortIds:H,detail:{provider:P}}}})}function rdn(g){return[...new Set(g)]}function gKn(g,E){return(E==="topology"?[...g.edges,...wKn(g)]:g.edges).filter(M=>mKn(M,E)).map(M=>({id:M.id,source:M.source,target:M.target,sourceHandle:M.sourcePort||void 0,targetHandle:M.targetPort||void 0,type:"sceneEdge",data:M,markerEnd:{type:UG.ArrowClosed,color:cdn(M),width:16,height:16},style:{stroke:cdn(M),strokeWidth:M.cycle?4:M.kind==="manual_call"?2.5:1.8,strokeDasharray:M.kind==="previous_timestep"?"7 5":M.kind==="manual_call"?"3 4":void 0}}))}function wKn(g){const E=[],x=new Set(g.instances.flatMap(M=>M.objectIds.map(n6)));for(const M of g.instances)E.push({id:`topology:scene:${M.id}`,source:"scene:root",target:M.id,kind:"object_topology",projection:"topology",cycle:!1}),E.push({id:`topology:${M.id}:object:${String(M.rootId)}`,source:M.id,target:`object:${String(M.rootId)}`,kind:"object_topology",projection:"topology",cycle:!1});for(const M of g.objects)M.parent===null&&!x.has(n6(M.objectId))&&E.push({id:`topology:scene:${M.id}`,source:"scene:root",target:M.id,kind:"object_topology",projection:"topology",cycle:!1});return E}function pKn(g,E){const x=new Map;for(const k of g){if(k.parent===null)continue;const H=n6(k.parent);x.set(H,[...x.get(H)??[],k.objectId])}const M=[],N=[E],P=new Set;for(;N.length>0;){const k=N.pop(),H=n6(k);P.has(H)||(P.add(H),M.push(k),N.push(...x.get(H)??[]))}return M}function n6(g){return String(g)}function mKn(g,E){const x=g.projection;return E==="topology"?g.kind==="object_topology":E==="resolved"?x==="resolved":x==="applications"||!x&&!["object_topology","application_target"].includes(g.kind)}function cdn(g){return g.cycle?"#cf4937":g.kind==="previous_timestep"?"#317b62":g.kind==="manual_call"?"#be6a54":g.kind==="object_topology"?"#7b7167":g.kind==="environment_binding"?"#367b8b":"#a59687"}function vKn(g){const E=new Set;for(const x of g.applications){for(const M of x.inputs)(g.applications.some(P=>P.applicationId!==x.applicationId&&P.outputs.some(k=>k.name===M.name))||g.modelLibrary.some(P=>Object.prototype.hasOwnProperty.call(P.outputs,M.name)))&&E.add(M.id);for(const M of x.outputs)(g.applications.some(P=>P.applicationId!==x.applicationId&&P.inputs.some(k=>k.name===M.name))||g.modelLibrary.some(P=>Object.prototype.hasOwnProperty.call(P.inputs,M.name)))&&E.add(M.id)}return E}function yKn(g,E){const x=E.role==="input"?"outputs":"inputs";return g.filter(M=>Object.prototype.hasOwnProperty.call(M[x],E.name)).sort((M,N)=>`${M.package}.${M.name}`.localeCompare(`${N.package}.${N.name}`))}function kKn({candidate:g,models:E,applications:x,onSelectModel:M,onSelectApplication:N,onClose:P}){const k=g.port.role==="input"?`Models that compute ${g.port.name}`:`Models that consume ${g.port.name}`;return F.jsxs("section",{className:"candidate-popover",style:{left:Math.min(g.x+8,window.innerWidth-390),top:Math.min(g.y-20,window.innerHeight-480)},children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:k}),F.jsx("span",{children:"Exact declared variable-name matches"})]}),F.jsx("button",{onClick:P,children:F.jsx(Ym,{size:15})})]}),F.jsxs("div",{className:"candidate-list",children:[x.length>0&&F.jsx("div",{className:"candidate-section-label",children:"Existing applications"}),x.map(H=>F.jsxs("button",{className:"candidate-card existing",onClick:()=>N(H),children:[F.jsx("strong",{children:H.name||H.applicationId}),F.jsx("span",{children:H.modelName}),F.jsxs("small",{children:[H.targetCount," target",H.targetCount===1?"":"s"]}),F.jsx("div",{children:"Connect without adding another application"})]},H.applicationId)),E.length>0&&F.jsx("div",{className:"candidate-section-label",children:"Available models"}),E.map(H=>F.jsxs("button",{className:"candidate-card",onClick:()=>M(H),children:[F.jsx("strong",{children:H.name}),F.jsx("span",{children:H.process}),F.jsx("small",{children:H.package||H.module}),F.jsxs("div",{children:[Object.keys(H.inputs).length," inputs · ",Object.keys(H.outputs).length," outputs"]})]},H.type))]})]})}function jKn(g,E){return g.filter(x=>x.applicationId!==E.application.applicationId).filter(x=>(E.port.role==="input"?x.outputs:x.inputs).some(N=>N.name===E.port.name)).sort((x,M)=>x.applicationId.localeCompare(M.applicationId))}function EKn(g,E){if(g.port.role==="input"){const M=E.outputs.find(N=>N.name===g.port.name);if(!M)throw new Error(`Application ${E.applicationId} does not output ${g.port.name}.`);return{sourceApplication:E,sourcePort:M,targetApplication:g.application,targetPort:g.port}}const x=E.inputs.find(M=>M.name===g.port.name);if(!x)throw new Error(`Application ${E.applicationId} does not input ${g.port.name}.`);return{sourceApplication:g.application,sourcePort:g.port,targetApplication:E,targetPort:x}}function SKn({selection:g,port:E,initialization:x,interactive:M,onEditApplication:N,onConfigureApplication:P,onRemoveApplication:k,onOverrideApplication:H,onEditObject:U,onRemoveObject:G}){const ie=g&&"applicationId"in g&&"selector"in g?g:null,Z=g&&"objectId"in g&&!("applicationId"in g)?g:null;return F.jsxs("aside",{className:"scene-inspector",children:[F.jsxs("header",{children:[F.jsx("strong",{children:"Inspector"}),g&&F.jsx("span",{children:OKn(g)})]}),!g&&F.jsxs("div",{className:"empty-inspector",children:[F.jsx(EXn,{size:28}),F.jsx("p",{children:"Select an application, object, execution, or relationship."})]}),g&&F.jsx("pre",{children:JSON.stringify(g,null,2)}),ie&&M&&F.jsxs("div",{className:"inspector-actions",children:[F.jsx("button",{onClick:()=>N(ie),children:ie.targetInstances.length>0?"Edit shared template":"Edit application"}),ie.targetInstances.length===0&&F.jsx("button",{"data-testid":"configure-application",onClick:()=>P(ie),children:"Configure coupling"}),ie.targetInstances.length>0&&F.jsx("button",{onClick:()=>H(ie),children:"Create override"}),F.jsx("button",{className:"danger",onClick:()=>k(ie),children:ie.targetInstances.length>0?"Remove from shared template":"Remove application"})]}),Z&&M&&F.jsxs("div",{className:"inspector-actions",children:[F.jsx("button",{onClick:()=>U(Z),children:"Edit object"}),F.jsx("button",{className:"danger",onClick:()=>G(Z),children:"Remove object and descendants"})]}),E&&F.jsxs("section",{children:[F.jsx("h4",{children:"Selected variable"}),F.jsx("code",{children:E.name}),F.jsx("p",{children:E.expectedType})]}),g&&x.length>0&&F.jsxs("section",{className:"inspector-initialization",children:[F.jsx("h4",{children:"Initialization"}),x.slice(0,8).map(W=>F.jsxs("div",{children:[F.jsx("code",{children:W.variable}),F.jsx("span",{className:W.disposition==="unresolved"?"unresolved":"",children:W.disposition})]},`${W.applicationId}:${W.objectId}:${W.variable}`))]})]})}function xKn({graph:g,onClose:E,sendCommand:x,interactive:M}){return F.jsxs(Fue,{title:"Diagnostics and cycles",onClose:E,children:[g.diagnostics.map(N=>F.jsxs("article",{className:"diagnostic-card",children:[F.jsx("strong",{children:N.code}),F.jsx("p",{children:N.message}),N.suggestions.map(P=>F.jsx("small",{children:P},P))]},`${N.code}:${N.message}`)),g.cycles.map(N=>F.jsxs("article",{className:"cycle-card",children:[F.jsx("strong",{children:N.applicationIds.join(" → ")}),F.jsx("p",{children:"Choose an input to read from the previous timestep."}),N.breakCandidates.map(P=>F.jsxs("button",{disabled:!M,onClick:()=>x({action:"edit",kind:"mark_previous_timestep",applicationId:P.applicationId,input:P.input}),children:[P.applicationId,".",P.input]},`${P.applicationId}:${P.objectId}:${P.input}`))]},N.id)),g.diagnostics.length===0&&g.cycles.length===0&&F.jsx("p",{children:"No diagnostics."})]})}function AKn({graph:g,onClose:E,sendCommand:x,interactive:M}){const N=g.initialization.filter(k=>k.disposition==="unresolved"),P=new Map;for(const k of N){const H=`${k.applicationId}:${k.variable}`;P.set(H,[...P.get(H)||[],k])}return F.jsxs(Fue,{title:"Initialization",onClose:E,children:[[...P.entries()].map(([k,H])=>F.jsx(MKn,{rows:H,interactive:M,sendCommand:x},k)),N.length===0&&F.jsx("p",{children:"No unresolved initial values."})]})}function MKn({rows:g,interactive:E,sendCommand:x}){const[M,N]=He.useState("float"),[P,k]=He.useState(""),H=g[0],U={type:M,value:P};return F.jsxs("article",{className:"initialization-group",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:H.variable}),F.jsx("span",{children:H.applicationId})]}),F.jsxs("small",{children:[g.length," object",g.length===1?"":"s"," · expected ",H.expectedType]})]}),F.jsx("p",{children:"Required because the input has no producer, environment source, status value, or usable temporal initialization."}),E&&F.jsxs("div",{className:"initialization-value",children:[F.jsxs("label",{children:["Type",F.jsxs("select",{value:M,onChange:G=>N(G.target.value),children:[F.jsx("option",{value:"float",children:"Float"}),F.jsx("option",{value:"integer",children:"Integer"}),F.jsx("option",{value:"boolean",children:"Boolean"}),F.jsx("option",{value:"symbol",children:"Symbol"}),F.jsx("option",{value:"string",children:"String"}),F.jsx("option",{value:"julia",children:"Julia expression"})]})]}),F.jsxs("label",{children:["Value",F.jsx("input",{value:P,onChange:G=>k(G.target.value)})]}),F.jsx("button",{disabled:!P.trim(),onClick:()=>x({action:"edit",kind:"set_object_statuses",objectIds:g.map(G=>G.objectId),variable:H.variable,value:U}),children:"Set all targets"})]}),F.jsx("div",{className:"initialization-object-list",children:g.map(G=>F.jsxs("div",{children:[F.jsxs("span",{children:["Object ",String(G.objectId)]}),F.jsx("code",{children:G.origin}),E&&F.jsx("button",{disabled:!P.trim(),onClick:()=>x({action:"edit",kind:"set_object_status",objectId:G.objectId,variable:G.variable,value:U}),children:"Set this object"})]},String(G.objectId)))})]})}function TKn({code:g,onClose:E}){return F.jsx(Fue,{title:"Scene code",onClose:E,children:F.jsx("pre",{className:"scene-code",children:g||"Scene code is available from an interactive editor session."})})}function udn({mode:g,recentPaths:E,currentPath:x,autosavePath:M,onSubmit:N,onClose:P}){const[k,H]=He.useState(x||"");return F.jsx(Fue,{title:g==="open"?"Open Scene":"Save Scene",onClose:P,children:F.jsxs("div",{className:"scene-file-dialog",children:[F.jsx("p",{children:g==="open"?"Open a Julia script whose final binding is `scene = Scene(...)`. Future edits will be saved back to that file.":"After the first save, every successful graph edit automatically rewrites this Julia script."}),F.jsxs("label",{children:["Julia file path",F.jsxs("div",{className:"scene-path-input",children:[F.jsx("input",{value:k,onChange:U=>H(U.target.value),placeholder:"/absolute/path/to/scene.jl",autoFocus:!0}),F.jsx("button",{className:"primary",disabled:!k.trim(),onClick:()=>N(k.trim()),children:g==="open"?"Open":"Save"})]})]}),g==="open"&&E.length>0&&F.jsxs("section",{children:[F.jsx("strong",{children:"Recent scenes"}),F.jsx("div",{className:"recent-scene-list",children:E.map(U=>F.jsxs("button",{onClick:()=>N(U),children:[F.jsx("span",{children:U.split("/").at(-1)}),F.jsx("small",{children:U})]},U))})]}),g==="open"&&M&&F.jsxs("section",{children:[F.jsx("strong",{children:"Recovery autosave"}),F.jsx("button",{className:"recovery-path",onClick:()=>N(M),children:M})]}),F.jsx("small",{children:"Use Git to version saved Scene scripts and review scientific configuration changes."})]})})}function CKn({selection:g,initialization:E,onSubmit:x,onClose:M}){const N=E.filter(G=>G.applicationId===g.application.applicationId&&G.variable===g.port.name&&G.disposition!=="supplied"),[P,k]=He.useState("float"),[H,U]=He.useState("");return F.jsx("div",{className:"overlay-backdrop",onMouseDown:M,children:F.jsxs("section",{className:"overlay-panel cycle-break-dialog",onMouseDown:G=>G.stopPropagation(),"data-testid":"cycle-break-dialog",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:"Break the current-step cycle"}),F.jsxs("span",{children:[g.application.applicationId,".",g.port.name]})]}),F.jsx("button",{onClick:M,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content",children:[F.jsx("p",{children:"This changes the application input to read its value from the previous accepted timestep. The model is disconnected from the current value during each run step."}),F.jsxs("div",{className:"cycle-impact",children:[F.jsx("strong",{children:"Application-wide change"}),F.jsxs("span",{children:["It affects all ",g.application.targetCount," targets selected by this application."]})]}),N.length>0&&F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Required initial value"}),F.jsxs("p",{children:[N.length," target",N.length===1?"":"s"," need a value before the first timestep."]}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Value type",F.jsxs("select",{value:P,onChange:G=>k(G.target.value),children:[F.jsx("option",{value:"float",children:"Float"}),F.jsx("option",{value:"integer",children:"Integer"}),F.jsx("option",{value:"boolean",children:"Boolean"}),F.jsx("option",{value:"symbol",children:"Symbol"}),F.jsx("option",{value:"string",children:"String"}),F.jsx("option",{value:"julia",children:"Julia expression"})]})]}),F.jsxs("label",{children:["Initial value",F.jsx("input",{value:H,onChange:G=>U(G.target.value),autoFocus:!0})]})]})]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:M,children:"Cancel"}),F.jsxs("button",{className:"primary",disabled:N.length>0&&!H.trim(),onClick:()=>x(N.length>0,N.length>0?{type:P,value:H}:null),"data-testid":"confirm-cycle-break",children:[F.jsx(Q0n,{size:15})," Use previous timestep"]})]})]})})}function Fue({title:g,onClose:E,children:x}){return F.jsx("div",{className:"overlay-backdrop",onMouseDown:E,children:F.jsxs("section",{className:"overlay-panel",onMouseDown:M=>M.stopPropagation(),children:[F.jsxs("header",{children:[F.jsx("strong",{children:g}),F.jsx("button",{onClick:E,children:F.jsx(Ym,{size:17})})]}),F.jsx("div",{className:"overlay-content",children:x})]})})}function OKn(g){return"applicationId"in g?g.applicationId:"objectId"in g?String(g.objectId):"objectIds"in g?g.name:"entity"in g?"Scene":"provider"in g?g.provider:g.kind.replaceAll("_"," ")}function NKn(g){return g.split(".").at(-1)||g}function DKn(g){const E={selectors:[]};return g.targetInstances.length===0&&g.targetScales.length===1&&(E.scale=g.targetScales[0]),g.targetKinds.length===1&&(E.kind=g.targetKinds[0]),g.targetSpecies.length===1&&(E.species=g.targetSpecies[0]),{type:g.targetCount===1?"One":"Many",multiplicity:g.targetCount===1?"one":"many",criteria:E,julia:""}}function Q7e(g,E,x){return`application:${g}:${E}:${x}`}function W7e(){const g=document.getElementById("pse-scene-graph-data");if(!g?.textContent)return edn;try{return JSON.parse(g.textContent)}catch{return edn}}function IKn(){const g=document.getElementById("pse-editor-config");if(!g?.textContent)return null;try{return JSON.parse(g.textContent)}catch{return null}}class _Kn extends He.Component{state={error:null};static getDerivedStateFromError(E){return{error:E}}componentDidCatch(E,x){console.error("PlantSimEngine Scene graph frontend failed",E,x)}render(){return this.state.error?F.jsxs("main",{className:"frontend-error","data-testid":"frontend-error",children:[F.jsx(dke,{size:28}),F.jsx("h1",{children:"The graph view could not be rendered"}),F.jsx("p",{children:this.state.error.message}),F.jsxs("button",{onClick:()=>window.location.reload(),children:[F.jsx(NXn,{size:15})," Reload graph"]})]}):this.props.children}}lzn.createRoot(document.getElementById("root")).render(F.jsx(He.StrictMode,{children:F.jsx(_Kn,{children:F.jsx(dKn,{})})})); diff --git a/frontend/dist/assets/index-Do5n-jvW.css b/frontend/dist/assets/index-Do5n-jvW.css new file mode 100644 index 000000000..80bcc42f0 --- /dev/null +++ b/frontend/dist/assets/index-Do5n-jvW.css @@ -0,0 +1 @@ +.react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #777;--xy-background-pattern-lines-color-default: #777;--xy-background-pattern-cross-color-default: #777;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}:root{--bg: #f3eee6;--paper: #fffaf2;--paper-strong: #fbf2e6;--ink: #312721;--muted: #80756c;--line: #ded2c3;--line-strong: #b7a696;--accent: #1f7a53;--accent-soft: rgba(31, 122, 83, .12);--sage: #7f8f73;--sage-dark: #596851;--ochre: #c99035;--clay: #bf6a54;--shadow: rgba(56, 43, 35, .12)}.scene-editor-shell{height:100vh;min-height:560px;display:grid;grid-template-rows:auto auto auto minmax(0,1fr);color:#302923;background:#f3eee5}.frontend-error{min-height:100vh;display:grid;place-content:center;justify-items:center;gap:10px;padding:24px;color:#743128;background:#fff5ef;text-align:center}.frontend-error h1,.frontend-error p{margin:0}.frontend-error p{max-width:620px;color:#655850}.frontend-error button{display:inline-flex;align-items:center;gap:6px;margin-top:8px;padding:8px 11px;border:1px solid #b95040;border-radius:5px;color:#fff;background:#b94435}.scene-toolbar{z-index:20;display:grid;grid-template-columns:auto minmax(260px,1fr) auto;gap:12px 18px;align-items:center;padding:14px 18px;background:#fffaf2;border-bottom:1px solid #d9cdbd;box-shadow:0 8px 24px #3c302514}.scene-brand{display:flex;align-items:center;gap:11px}.scene-brand .brand-mark{width:5px;height:42px;border-radius:3px;background:#1f7a58}.scene-brand div{display:grid}.scene-brand small{color:#81766c;font:10px/1.2 ui-monospace,monospace;letter-spacing:0}.scene-brand strong{font-size:20px;letter-spacing:0}.scene-search{display:flex;align-items:center;gap:8px;min-width:0;padding:8px 11px;border:1px solid #d9cdbd;border-radius:6px;background:#fffdf8}.scene-search input{width:100%;min-width:0;border:0;outline:0;background:transparent;font:inherit}.scene-search button,.scene-toolbar button,.overlay-panel button,.candidate-popover button,.editor-feedback button{border:0;background:transparent;color:inherit;cursor:pointer}.scene-counts{display:flex;align-items:center;justify-content:flex-end;gap:7px;flex-wrap:wrap}.scene-counts>span,.scene-counts>button{display:inline-flex;align-items:center;gap:5px;padding:5px 8px;border:1px solid #d9cdbd;border-radius:5px;background:#fffaf2;font:12px ui-monospace,monospace}.scene-counts .count-warning{color:#a96a13;border-color:#dfbd83}.scene-counts .count-error{color:#b44e3c;border-color:#dfa496}.view-tabs,.scene-actions{display:flex;align-items:center;gap:7px;flex-wrap:wrap}.view-tabs{grid-column:1 / 3}.scene-actions{justify-content:flex-end}.view-tabs button,.scene-actions button,.cycle-callout button{display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:6px 10px;border:1px solid #d4c7b6;border-radius:5px;background:#fffaf2;color:#4c433b}.view-tabs button.active{color:#176047;border-color:#83b59e;background:#e9f4ed}.scene-actions button:disabled{opacity:.4;cursor:default}.scene-actions .overview-cta{color:#176047;border-color:#86bba4;background:#e9f4ed;font-weight:700}.cycle-callout{display:flex;align-items:center;gap:12px;padding:10px 18px;color:#8f2f24;background:#fff0eb;border-bottom:1px solid #df9a8e}.cycle-callout div{display:grid;margin-right:auto}.cycle-callout span{font-size:12px}.cycle-callout button{border-color:#cf7668;color:#8f2f24}.cycle-callout button.active{color:#fff;border-color:#9d3428;background:#b94435}.editor-feedback{display:flex;align-items:center;justify-content:center;gap:12px;padding:7px 16px;background:#fff5d9;border-bottom:1px solid #dfc278;color:#6e5315;font-size:12px}.graph-scope-filter{align-items:center;background:#edf5ef;border-bottom:1px solid #b9d4c0;color:#365849;display:flex;font-size:12px;gap:10px;justify-content:center;min-height:38px;padding:6px 14px}.graph-scope-filter button{align-items:center;background:#fff;border:1px solid #b9d4c0;border-radius:5px;color:#365849;cursor:pointer;display:inline-flex;gap:5px;padding:5px 8px}.scene-workspace{min-height:0;display:grid;grid-template-columns:minmax(0,1fr) 310px}.flow-wrap{min-width:0;min-height:0;position:relative}.scene-inspector{overflow:auto;padding:16px;background:#fffaf2;border-left:1px solid #d9cdbd}.scene-inspector>header{display:grid;gap:2px;margin-bottom:14px}.scene-inspector>header span{color:#776c63;font:11px ui-monospace,monospace}.scene-inspector pre{overflow-wrap:anywhere;white-space:pre-wrap;font-size:10px;line-height:1.45}.empty-inspector{display:grid;place-items:center;padding:48px 20px;color:#8a7e74;text-align:center}.inspector-initialization>div{display:flex;justify-content:space-between;gap:8px;padding:6px 0;border-bottom:1px solid #eee4d6;font-size:11px}.inspector-initialization .unresolved{color:#b74635;font-weight:700}.application-node .target-summary{margin:-2px 0 10px;color:#766c63;font-size:11px}.application-node .target-summary strong{color:#2d6652}.application-node .previous-label{margin-left:auto;color:#1f7a58;font:10px ui-monospace,monospace}.cycle-port-break{display:inline-grid;place-items:center;width:24px;height:24px;margin-left:auto;border:1px solid #c94e3e!important;border-radius:50%;color:#a63428!important;background:#fff0eb!important;box-shadow:0 3px 9px #8c2d232e}.cycle-port-break:hover{color:#fff!important;background:#bd4435!important}.entity-node{position:relative;width:240px;min-height:105px;padding:13px;border:1px solid #d8cbbb;border-radius:6px;background:#fffaf2;box-shadow:0 7px 18px #392e241f}.entity-node.selected{border-color:#1f7a58;box-shadow:0 0 0 2px #1f7a5829}.entity-node header{display:grid;gap:3px}.entity-node header span{color:#766c63;font-size:11px}.entity-node .badges{display:flex;gap:5px;flex-wrap:wrap;margin-top:12px}.candidate-popover{position:fixed;z-index:80;width:370px;max-height:460px;display:grid;grid-template-rows:auto minmax(0,1fr);border:1px solid #cfbfaa;border-radius:7px;background:#fffaf2;box-shadow:0 20px 54px #342a203d}.candidate-popover>header{display:flex;gap:10px;padding:12px;border-bottom:1px solid #e1d5c5}.candidate-popover>header div{display:grid;margin-right:auto}.candidate-popover>header span{color:#7a7067;font-size:10px}.candidate-list{overflow:auto;display:grid;gap:7px;padding:9px}.candidate-card{display:grid;grid-template-columns:1fr auto;gap:2px 8px;padding:10px;border:1px solid #ded2c2!important;border-radius:5px;background:#fffdf8!important;text-align:left}.candidate-card:hover{border-color:#76a990!important;background:#edf6f0!important}.candidate-card span{color:#1f7053;font:11px ui-monospace,monospace}.candidate-card small{color:#7a7067}.candidate-card div{grid-column:1 / -1;color:#7a7067;font-size:10px}.candidate-card.existing{border-left:3px solid #1f7a58!important}.candidate-section-label{padding:5px 3px 2px;color:#71675e;font:700 10px ui-monospace,monospace;text-transform:uppercase}.overlay-backdrop{position:fixed;z-index:100;inset:0;display:grid;place-items:center;padding:24px;background:#2d251e61}.overlay-panel{width:min(720px,100%);max-height:min(760px,92vh);display:grid;grid-template-rows:auto minmax(0,1fr);border:1px solid #cdbda8;border-radius:7px;background:#fffaf2;box-shadow:0 24px 70px #271f194d}.overlay-panel>header{display:flex;justify-content:space-between;align-items:center;padding:14px 16px;border-bottom:1px solid #ded2c2}.overlay-content{overflow:auto;padding:15px}.diagnostic-card,.cycle-card,.initialization-card{display:grid;gap:5px;margin-bottom:10px;padding:11px;border:1px solid #ded2c2;border-radius:5px}.diagnostic-card{border-left:3px solid #c85240}.diagnostic-card p,.cycle-card p{margin:0}.diagnostic-card small{color:#766c63}.cycle-card button{width:fit-content;padding:6px 8px;border:1px solid #ce7768;border-radius:4px;color:#963529}.scene-code{min-height:320px;margin:0;padding:14px;overflow:auto;border-radius:5px;background:#292622;color:#f5eee5}.scene-file-dialog{display:grid;gap:14px}.scene-file-dialog>p{margin:0;line-height:1.5}.scene-file-dialog>label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.scene-path-input{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px}.scene-path-input input{width:100%;min-width:0;padding:8px 9px;border:1px solid #d3c5b4;border-radius:4px;background:#fffdf8;font:12px ui-monospace,monospace}.scene-path-input button{padding:8px 13px;border:1px solid #1d7052!important;border-radius:4px;color:#fff!important;background:#1f7a58!important}.scene-path-input button:disabled{opacity:.4;cursor:default}.scene-file-dialog section{display:grid;gap:7px}.recent-scene-list{display:grid;gap:6px}.recent-scene-list button{display:grid;gap:2px;padding:9px;border:1px solid #d8cbbb!important;border-radius:5px;background:#fffdf8!important;text-align:left}.recent-scene-list button:hover{border-color:#73a88e!important;background:#edf6f0!important}.recent-scene-list small,.scene-file-dialog>small{color:#776d64;overflow-wrap:anywhere}.recovery-path{padding:9px;border:1px dashed #c99035!important;border-radius:5px;color:#6d511d!important;background:#fff6e6!important;text-align:left;overflow-wrap:anywhere}.initialization-group{display:grid;gap:10px;margin-bottom:12px;padding:12px;border:1px solid #d8cbbb;border-radius:6px;background:#fffdf8}.initialization-group>header{display:flex;align-items:end;justify-content:space-between;gap:12px}.initialization-group>header div{display:grid}.initialization-group>header span,.initialization-group>header small{color:#786d64;font:10px ui-monospace,monospace}.initialization-group>p{margin:0;color:#6c625a;font-size:11px;line-height:1.45}.initialization-value{display:grid;grid-template-columns:120px minmax(0,1fr) auto;gap:8px;align-items:end}.initialization-value label{display:grid;gap:4px;color:#5f554d;font-size:10px;font-weight:700}.initialization-value input,.initialization-value select{width:100%;min-width:0;padding:7px 8px;border:1px solid #d3c5b4;border-radius:4px;background:#fff}.initialization-value button,.initialization-object-list button{padding:7px 9px;border:1px solid #85ad99!important;border-radius:4px;color:#176047!important;background:#edf6f0!important}.initialization-value button:disabled,.initialization-object-list button:disabled{opacity:.4;cursor:default}.initialization-object-list{display:grid;gap:5px}.initialization-object-list>div{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:8px;align-items:center;padding-top:5px;border-top:1px solid #ece1d2;font-size:11px}.initialization-object-list code{color:#a33d31}@media(max-width:980px){.scene-toolbar{grid-template-columns:1fr}.view-tabs{grid-column:auto}.scene-counts,.scene-actions{justify-content:flex-start}.scene-workspace{grid-template-columns:1fr}.scene-inspector{display:none}}.application-form{width:min(780px,100%)}.object-form{width:min(640px,100%)}.override-form{width:min(760px,100%)}.application-form>header div{display:grid;gap:2px}.object-form>header div{display:grid;gap:2px}.override-form>header div{display:grid;gap:2px}.application-form>header span{color:#786d64;font-size:11px}.object-form>header span{color:#786d64;font-size:11px}.override-form>header span{color:#786d64;font-size:11px}.application-form-content,.object-form-content,.override-form-content{display:grid;gap:14px}.application-form-content>label,.application-form fieldset label,.object-form-content label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.override-form-content label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.application-form input,.application-form select,.object-form input,.object-form select,.override-form input,.override-form select{width:100%;min-width:0;padding:8px 9px;border:1px solid #d3c5b4;border-radius:4px;background:#fffdf8;color:#332c26;font:12px ui-monospace,monospace}.override-form fieldset{margin:0;padding:12px;border:1px solid #ded2c2;border-radius:5px}.override-form legend{padding:0 6px;color:#2d6652;font-weight:800}.override-form fieldset label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.override-scope-choice{display:grid;grid-template-columns:1fr 1fr;gap:9px}.override-scope-choice button{display:grid;gap:3px;padding:11px;border:1px solid #d4c7b6!important;border-radius:5px;background:#fffdf8!important;text-align:left}.override-scope-choice button.active{border-color:#72a88d!important;background:#edf6f0!important}.override-scope-choice span{color:#756a61;font-size:10px;line-height:1.4}.override-warning{display:grid;gap:3px;padding:10px;border-left:3px solid #c99035;background:#fff6e6}.override-warning span{color:#74685e;font-size:11px}.application-form fieldset{margin:0;padding:12px;border:1px solid #ded2c2;border-radius:5px}.application-form legend{padding:0 6px;color:#2d6652;font-weight:800}.form-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.parameter-list{display:grid;gap:9px}.parameter-row{display:grid;grid-template-columns:minmax(0,1fr) 150px;gap:9px;align-items:end}.parameter-row>label>span{color:#3c342e}.parameter-row small{color:#8a7e74;font-weight:400}.selector-summary{margin:10px 0 0;color:#796e64;font-size:11px}.application-form>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.object-form>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.override-form>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.application-form>footer button,.object-form>footer button,.override-form>footer button,.inspector-actions button{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border:1px solid #d2c4b3;border-radius:4px;background:#fffdf8}.application-form>footer .primary,.object-form>footer .primary{color:#fff;border-color:#1d7052;background:#1f7a58}.override-form>footer .primary{color:#fff;border-color:#1d7052;background:#1f7a58}.application-form>footer button:disabled{opacity:.45}.object-form>footer button:disabled{opacity:.45}.override-form>footer button:disabled{opacity:.45}.inspector-actions{display:flex;gap:7px;margin:10px 0 16px}.inspector-actions .danger{color:#a33d31;border-color:#d8a296}.binding-form{width:min(680px,100%)}.binding-form>header div{display:grid;gap:2px}.binding-form>header span{color:#786d64;font-size:11px}.binding-form .overlay-content{display:grid;gap:14px}.binding-route{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:14px;padding:12px;border:1px solid #d8cbbb;border-radius:5px;background:#fffdf8}.binding-route>div{display:grid;gap:3px;min-width:0}.binding-route>div:last-child{text-align:right}.binding-route small{color:#7c7168;text-transform:uppercase;font-size:9px}.binding-route strong,.binding-route code{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.binding-route code{color:#1f7053}.binding-form fieldset{margin:0;padding:12px;border:1px solid #ded2c2;border-radius:5px}.binding-form legend{padding:0 6px;color:#2d6652;font-weight:800}.binding-form fieldset label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.binding-form select{width:100%;min-width:0;padding:8px 9px;border:1px solid #d3c5b4;border-radius:4px;background:#fffdf8;color:#332c26;font:12px ui-monospace,monospace}.binding-form>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.binding-form>footer button{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border:1px solid #d2c4b3;border-radius:4px;background:#fffdf8}.binding-form>footer .primary{color:#fff;border-color:#1d7052;background:#1f7a58}.cycle-break-dialog{width:min(620px,100%)}.cycle-break-dialog>header div{display:grid;gap:2px}.cycle-break-dialog>header span{color:#a33d31;font:11px ui-monospace,monospace}.cycle-break-dialog .overlay-content{display:grid;gap:13px}.cycle-break-dialog p{margin:0;line-height:1.5}.cycle-impact{display:grid;gap:3px;padding:10px;border-left:3px solid #c54c3c;background:#fff0eb}.cycle-impact span{color:#705f57;font-size:11px}.cycle-break-dialog fieldset{margin:0;padding:12px;border:1px solid #d8cbbb;border-radius:5px}.cycle-break-dialog legend{padding:0 6px;color:#9a392e;font-weight:800}.cycle-break-dialog label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.cycle-break-dialog input,.cycle-break-dialog select{width:100%;min-width:0;padding:8px 9px;border:1px solid #d3c5b4;border-radius:4px;background:#fffdf8}.cycle-break-dialog>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.cycle-break-dialog>footer button{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border:1px solid #d2c4b3;border-radius:4px;background:#fffdf8}.cycle-break-dialog>footer .primary{color:#fff;border-color:#a8392d;background:#b94435}.cycle-break-dialog>footer button:disabled{opacity:.45;cursor:default}@media(max-width:700px){.form-grid,.parameter-row,.override-scope-choice,.binding-route{grid-template-columns:1fr}.binding-route>div:last-child{text-align:left}.initialization-value{grid-template-columns:1fr}}*{box-sizing:border-box}body{margin:0;color:var(--ink);background:radial-gradient(circle at 20% 24%,rgba(31,122,83,.045),transparent 30%),radial-gradient(circle at 78% 68%,rgba(201,144,53,.055),transparent 34%),linear-gradient(180deg,rgba(255,250,242,.26),transparent 42%),var(--bg);font-family:Avenir Next,Trebuchet MS,Segoe UI,sans-serif}.app-shell{display:grid;grid-template-columns:minmax(0,1fr);height:100vh;position:relative}.app-shell.has-side-panel{grid-template-columns:minmax(0,1fr) 340px}.graph-panel{position:relative;min-width:0}.graph-panel:after{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;opacity:.2;background-image:radial-gradient(rgba(49,39,33,.11) .55px,transparent .55px);background-size:16px 16px;-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 22%,black 82%,transparent 100%);mask-image:linear-gradient(to bottom,transparent 0%,black 22%,black 82%,transparent 100%)}.topbar{position:absolute;z-index:10;top:18px;left:18px;right:18px;display:flex;align-items:center;gap:12px;padding:11px 14px;background:#fffaf2eb;border:1px solid var(--line);border-radius:14px;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.topbar:before{content:"";align-self:stretch;width:5px;border-radius:999px;background:var(--accent)}.editor-feedback{position:absolute;z-index:12;top:96px;left:18px;right:18px;padding:9px 12px;border:1px solid var(--line);border-radius:10px;background:#fffaf2f2;box-shadow:0 12px 30px var(--shadow);max-height:132px;overflow:auto;font-size:12px;line-height:1.45;white-space:normal;overflow-wrap:anywhere}.editor-feedback.error{color:var(--clay);border-color:#bf6a5466;background:#fff4eef7}.editor-feedback.info{color:var(--accent);border-color:#1f7a5359;background:#f5fcf8f7}.cycle-break-prompt{position:absolute;z-index:14;top:98px;left:18px;right:18px;display:flex;align-items:center;justify-content:space-between;gap:14px;padding:11px 12px;border:1px solid rgba(211,66,47,.32);border-radius:12px;background:linear-gradient(135deg,rgba(211,66,47,.12),transparent 62%),#fffaf2f5;box-shadow:0 16px 36px #382b2329}.cycle-break-prompt div{display:grid;gap:3px;min-width:0}.cycle-break-prompt strong{color:#7a2018;font-size:13px}.cycle-break-prompt span{color:var(--muted);font-size:12px;line-height:1.35}.cycle-break-prompt code{color:#7a2018;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.cycle-break-prompt.active{border-color:#d3422f85;box-shadow:0 16px 36px #382b2329,0 0 0 4px #d3422f14}.cycle-break-cta{flex:0 0 auto;justify-content:center;font-weight:780}.graph-workbench{flex-wrap:wrap;align-items:flex-start;max-height:min(36vh,250px);overflow:auto;scrollbar-width:thin}.brand-block{min-width:150px}.brand-block h1{font-size:clamp(16px,2.2vw,20px)}.search-box{position:relative;display:flex;align-items:center;gap:8px;flex:1 1 280px;max-width:460px;min-width:220px;padding:7px 9px;border:1px solid var(--line);border-radius:11px;background:#fffdf7db}.search-box input{width:100%;min-width:0;border:0;outline:0;color:var(--ink);background:transparent;font:inherit;font-size:13px}.clear-search{display:grid;place-items:center;width:20px;height:20px;border:0;border-radius:999px;background:#b7a6962e;color:var(--muted);cursor:pointer}.search-results{position:absolute;z-index:80;top:calc(100% + 8px);left:0;right:0;display:grid;gap:6px;max-height:360px;overflow:auto;padding:8px;border:1px solid var(--line);border-radius:12px;background:#fffaf2fa;box-shadow:0 18px 45px var(--shadow)}.search-result{display:grid;gap:2px;padding:8px 9px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--ink);text-align:left;cursor:pointer}.search-result:hover,.search-result:focus-visible{border-color:#1f7a533d;background:var(--accent-soft);outline:none}.search-result strong{overflow-wrap:anywhere;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.search-result span{color:var(--muted);font-size:11px}.eyebrow{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.08em;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}h1,h2,h3{margin:0;letter-spacing:0}h1{font-size:20px;font-weight:800}.metrics{display:flex;flex-wrap:wrap;gap:8px;margin-left:auto}.metrics span,.metric-button,.node-meta span{display:inline-flex;align-items:center;gap:5px;padding:5px 8px;border:1px solid var(--line);border-radius:9px;background:#fffaf2e6;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.metric-button{color:var(--ink);cursor:pointer}.metric-button.active,.metric-button:hover,.metric-button:focus-visible{outline:none;background:#fff6f0}.view-mode-button.overview-cta{border-color:#1f7a5361;color:var(--accent);background:#1f7a531a;box-shadow:0 8px 18px #1f7a531a;font-weight:800}.meta-chip{position:relative}.meta-chip:after{content:attr(data-tooltip);position:absolute;z-index:20;left:0;bottom:calc(100% + 10px);width:max-content;max-width:280px;padding:8px 10px;color:var(--paper);background:var(--ink);border-radius:10px;box-shadow:0 12px 28px #382b232e;font-family:Avenir Next,Trebuchet MS,Segoe UI,sans-serif;font-size:12px;line-height:1.3;white-space:normal;opacity:0;pointer-events:none;transform:translateY(4px);transition:opacity .12s ease,transform .12s ease}.meta-chip:before{content:"";position:absolute;z-index:21;left:16px;bottom:calc(100% + 4px);width:10px;height:10px;background:var(--ink);opacity:0;pointer-events:none;transform:rotate(45deg) translateY(4px);transition:opacity .12s ease,transform .12s ease}.meta-chip:hover:after,.meta-chip:hover:before,.meta-chip:focus-visible:after,.meta-chip:focus-visible:before{opacity:1;transform:translateY(0)}.meta-chip:hover:before,.meta-chip:focus-visible:before{transform:rotate(45deg) translateY(0)}.metrics .warn{color:var(--clay);border-color:#c9614a73}.metrics .caution{color:var(--ochre);border-color:#c9903573}.metrics .warn svg{flex:0 0 auto}.icon-button{display:grid;place-items:center;width:34px;height:34px;border:1px solid var(--line);border-radius:10px;background:var(--paper);color:var(--ink);cursor:pointer;box-shadow:0 8px 18px #382b2314}.icon-button.compact{width:28px;height:28px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:14px}.toolbar-group{display:flex;align-items:center;gap:8px}.open-button{flex:0 0 auto}.panel-switch{flex-wrap:wrap}.select-control{display:inline-flex;align-items:center;gap:6px;padding:5px 8px;border:1px solid var(--line);border-radius:10px;background:#fffaf2e6;color:var(--muted)}.select-control select{max-width:132px;border:0;outline:0;background:transparent;color:var(--ink);font:inherit;font-size:12px}.relationship-legend{position:absolute;z-index:35;top:116px;left:18px;display:grid;gap:7px;width:190px;padding:10px;border:1px solid var(--line);border-radius:13px;background:#fffaf2eb;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);max-height:min(480px,calc(100vh - 142px));overflow:auto}.legend-title,.legend-note{display:flex;align-items:center;gap:6px;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.relationship-legend button{display:flex;align-items:center;gap:8px;padding:6px 7px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--muted);font:inherit;font-size:12px;cursor:pointer}.relationship-legend button.active{color:var(--ink);border-color:#1f7a5338;background:var(--accent-soft)}.legend-line{width:28px;height:0;border-top:2px solid var(--line-strong)}.legend-line.mapped{border-color:var(--accent);border-style:dashed}.legend-line.call{border-color:var(--clay);border-style:dashed}.scale-controls{position:absolute;z-index:35;top:116px;left:220px;display:grid;gap:8px;width:190px;padding:10px;border:1px solid var(--line);border-radius:13px;background:#fffaf2eb;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);max-height:min(560px,calc(100vh - 142px));overflow:auto}.scale-list{display:grid;gap:6px}.scale-list button,.scale-reset{display:grid;gap:2px;width:100%;padding:7px 8px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--muted);font:inherit;text-align:left;cursor:pointer}.scale-list button.active{color:var(--ink);border-color:#1f7a5338;background:var(--accent-soft)}.scale-list button.collapsed{border-color:#b7a69657;border-style:dashed;background:#b7a69614}.scale-list span{overflow-wrap:anywhere;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;font-weight:700}.scale-list small{color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.06em}.scale-reset{color:var(--accent);border-color:#1f7a5333;background:#1f7a5314;text-align:center}.floating-panel{position:absolute;z-index:40;top:116px;right:18px;width:min(360px,calc(100vw - 36px));max-height:min(560px,calc(100vh - 142px));overflow:auto;padding:14px;background:#fffaf2f5;border:1px solid rgba(191,106,84,.32);border-radius:14px;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}.warnings-panel{border-color:#c9903559}.open-panel{left:18px;right:auto;border-color:#1f7a5352}.floating-panel-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}.floating-panel h2{font-size:18px}.react-flow{background:transparent;z-index:1}.react-flow__background{display:none}.react-flow__edges{z-index:8}.react-flow__edges:has(.react-flow__edge.highlighted){z-index:120}.react-flow__edgelabel-renderer{z-index:28}.react-flow__edgelabel-renderer:has(.edge-chip.highlighted){z-index:121}.react-flow__nodes{z-index:20}.model-node{position:relative;overflow:visible;background:#fffaf2f5;border:1px solid var(--line);border-radius:16px;box-shadow:0 18px 42px var(--shadow)}.model-remove-button{position:absolute;z-index:45;top:-11px;right:-11px;display:grid;place-items:center;width:30px;height:30px;border:1px solid rgba(191,106,84,.4);border-radius:999px;color:#fff;background:var(--clay);box-shadow:0 10px 24px #bf6a543d;cursor:pointer}.model-remove-button:hover,.model-remove-button:focus-visible{outline:none;background:#9b3f2e;transform:translateY(-1px)}.node-header{border-radius:16px 16px 0 0}.model-node.selected{border-color:#1f7a53b8;box-shadow:0 20px 48px #1f7a5324}.model-node.focused{border-color:#1f7a539e;box-shadow:0 20px 48px #1f7a5324,0 0 0 4px #1f7a5314}.model-node.dimmed{opacity:.2;filter:grayscale(.2)}.model-node.cyclic{border-color:#bf6a549e;box-shadow:0 18px 42px var(--shadow),0 0 0 3px #bf6a5414}.model-node.cyclic .node-header>svg{color:var(--clay)}.model-node.hard_dependency{border-color:#bf6a548c;border-style:dashed;background:#fff6f0f0;box-shadow:0 14px 30px #bf6a541f}.model-node.hard_dependency .node-header{background:linear-gradient(90deg,rgba(191,106,84,.08),transparent 60%),var(--paper-strong)}.model-node.hard_dependency .node-header>svg{color:var(--clay)}.model-node.hard_dependency .process:before{content:"↳ ";color:var(--clay)}.model-node.overview-node{border-radius:12px;box-shadow:0 10px 24px #382b231a}.model-node.overview-node .node-header{min-height:64px;padding:9px 10px;border-radius:12px 12px 0 0}.model-node.overview-node .process{font-size:12px;line-height:1.18;overflow-wrap:anywhere}.model-node.overview-node .model-type{font-size:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.overview-node-summary{display:flex;flex-wrap:wrap;gap:5px;padding:8px 10px 10px}.overview-node-summary span{min-width:0;max-width:100%;padding:3px 6px;border:1px solid rgba(183,166,150,.35);border-radius:999px;color:var(--muted);background:#fffdf7c2;font-size:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.overview-port-handles .react-flow__handle{width:7px;height:7px;border-color:#fffaf2e6;background:var(--line-strong)}.overview-port-handles .react-flow__handle-right{background:var(--accent)}.hard-chip{color:var(--clay);border-color:#bf6a5447!important;background:#fff6f0e6!important}.node-header{display:grid;grid-template-columns:1fr auto;align-items:flex-start;gap:11px;padding:11px 12px 12px;color:var(--ink);background:var(--paper-strong);border-bottom:1px solid var(--line)}.node-header>svg{color:var(--accent)}.process{font-weight:820;font-size:15px;letter-spacing:0}.model-type{margin-top:2px;font-size:12px;color:var(--muted);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.node-meta{display:flex;flex-wrap:wrap;gap:6px;padding:10px 12px 0}.ports-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;padding:12px}.port-title{margin-bottom:6px;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.08em;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.port{position:relative;display:flex;align-items:center;gap:5px;min-height:24px;margin:4px 0;padding:4px 7px;border:1px solid var(--line);border-radius:8px;background:#fffdf7e6;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.port:after{content:attr(data-default);position:absolute;z-index:60;left:50%;bottom:calc(100% + 8px);width:max-content;max-width:240px;padding:7px 9px;color:var(--paper);background:var(--ink);border-radius:9px;box-shadow:0 12px 28px #382b232e;font-family:Avenir Next,Trebuchet MS,Segoe UI,sans-serif;font-size:11px;line-height:1.3;white-space:normal;opacity:0;pointer-events:none;transform:translate(-50%,4px);transition:opacity .12s ease,transform .12s ease}.port:hover:after,.port.active:after{opacity:1;transform:translate(-50%)}.app-shell.has-candidate-popover .port.active:after{opacity:0}.port.output{justify-content:flex-end}.port.previous{color:var(--clay)}.port.mapped{border-color:#1f7a5361}.port-candidate-button{position:relative;z-index:8;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:16px;height:16px;padding:0;color:var(--accent);background:#fffdf7f0;border:1px solid rgba(31,122,83,.34);border-radius:999px;cursor:pointer}.port-candidate-button:hover,.port-candidate-button:focus-visible{color:#fffdfa;background:var(--accent);outline:none}.port-cycle-break-button{position:relative;z-index:9;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:18px;height:18px;padding:0;color:#9b2d22;background:#fff1ecf5;border:1px solid rgba(211,66,47,.45);border-radius:999px;cursor:pointer;box-shadow:0 5px 12px #d3422f29}.port-cycle-break-button:hover,.port-cycle-break-button:focus-visible{color:#fffdfa;background:#d3422f;outline:none}.port.active .port-candidate-button{color:var(--accent);background:#fffdfa}@keyframes cycleTargetPulse{0%,to{box-shadow:inset 3px 0 #d3422fc7,0 0 0 3px #d3422f14}50%{box-shadow:inset 3px 0 #d3422feb,0 0 0 6px #d3422f26}}.candidate-popover{position:fixed;z-index:1200;display:grid;grid-template-rows:auto minmax(0,1fr);overflow:hidden;color:var(--ink);border:1px solid rgba(31,122,83,.28);border-radius:8px;background:linear-gradient(135deg,rgba(31,122,83,.08),transparent 42%),#fffdf7fa;box-shadow:0 18px 42px #382b232e}.candidate-popover-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:12px 12px 9px;border-bottom:1px solid rgba(183,166,150,.32)}.candidate-popover-header h3{margin:3px 0 0;overflow-wrap:anywhere;font-size:15px}.candidate-popover-list{display:grid;gap:8px;overflow:auto;padding:10px}.candidate-model-card{display:grid;gap:5px;width:100%;padding:9px 10px;color:var(--ink);border:1px solid rgba(183,166,150,.38);border-left:4px solid var(--accent);border-radius:8px;background:#ffffffb3;font:inherit;text-align:left;cursor:pointer}.candidate-model-card:hover,.candidate-model-card:focus-visible{background:#fffdf7f5;border-color:#1f7a536b;outline:none}.candidate-model-card strong{overflow-wrap:anywhere;font-size:12px}.candidate-model-card span{color:var(--muted);font-size:11px}.candidate-model-card small{overflow-wrap:anywhere;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.04em}.port.required-input{border-color:#bf6a54b8;background:linear-gradient(90deg,#bf6a5429,#fffdf7eb 62%),#fffdf7e6;box-shadow:inset 3px 0 #bf6a54bd}.port.required-input .react-flow__handle{border-color:var(--clay);background:#fff6f0;box-shadow:0 0 0 3px #fffaf2eb,0 0 0 6px #bf6a5421}.port.cycle-break-target{border-color:#d3422fb8;background:linear-gradient(90deg,#d3422f2e,#fffdf7eb 62%),#fffdf7e6;box-shadow:inset 3px 0 #d3422fc7,0 0 0 3px #d3422f14}.cycle-break-mode .port.cycle-break-target{animation:cycleTargetPulse 1.35s ease-in-out infinite}.port.cycle-break-target .react-flow__handle{border-color:#d3422f;background:#fff1ec;box-shadow:0 0 0 3px #fffaf2eb,0 0 0 7px #d3422f26}.port.highlighted{border-color:var(--line);background:#fffdfa}.port.focused{border-color:#1f7a5394;background:var(--accent-soft)}.port.active{color:#fffdfa;border-color:var(--accent);background:var(--accent)}.port.cycle-break-target.active,.port.cycle-break-target.focused,.port.cycle-break-target.highlighted{color:#7a2018;border-color:#d3422fc7;background:linear-gradient(90deg,#d3422f33,#fffdf7f0 64%),#fffdf7f0;box-shadow:inset 3px 0 #d3422fdb,0 0 0 4px #d3422f1f}.port.cycle-break-target.active:after{background:#7a2018}.port.cycle-break-target.active .port-cycle-break-button,.port.cycle-break-target.focused .port-cycle-break-button,.port.cycle-break-target.highlighted .port-cycle-break-button{color:#9b2d22;background:#fff1ecfa}.react-flow__handle{width:9px;height:9px;border:1px solid var(--line-strong);background:var(--paper);z-index:25;box-shadow:0 0 0 3px #fffaf2eb}.react-flow__handle.call-handle{width:12px;height:36px;opacity:0;pointer-events:none}.react-flow__edge.multiscale path{stroke-dasharray:7 5}.react-flow__edge.mapped_variable path{stroke:var(--accent)}.react-flow__edge.hard_dependency path{stroke:var(--clay)}.react-flow__edge.cycle_dependency path,.react-flow__edge.cycle_edge path{stroke:#d3422f;filter:drop-shadow(0 0 5px rgba(211,66,47,.26))}.react-flow__edge.call_edge path{stroke-width:1.7;stroke-dasharray:3 6;opacity:.7}.react-flow__edge.highlighted path{stroke-width:3;stroke:var(--accent)}.react-flow__edge.focused path{stroke-width:2.8}.react-flow__edge.highlighted{z-index:80!important}.react-flow__edge.focused{z-index:70!important}.react-flow__edge.dimmed{opacity:.04}.overview-mode .react-flow__edge.variable_edge path{stroke-width:1.45!important;opacity:.74}.overview-mode .react-flow__edge.call_edge path{stroke-width:1.15!important;opacity:.4}.overview-mode .react-flow__edge.highlighted path,.overview-mode .react-flow__edge.focused path{stroke-width:2.8!important;opacity:1}.edge-chip{position:absolute;z-index:-1;display:inline-flex;align-items:center;gap:6px;padding:3px 7px;color:var(--ink);background:#fffaf2f0;border:1px solid var(--line);border-radius:999px;box-shadow:0 8px 20px #382b231a;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:1;pointer-events:none;white-space:nowrap}.edge-chip.mapped_variable,.edge-chip.multiscale{border-color:#1f7a534d;background:#f8faf1f5}.edge-chip.hard_dependency{border-color:#bf6a5452;background:#fff6f0f5}.edge-chip.cycle_dependency,.edge-chip.cycle_edge{color:#7a2018;border-color:#d3422f70;background:#ffeee9fa}.edge-chip small{color:var(--muted);font-size:9px;letter-spacing:.02em;text-transform:uppercase}.edge-chip.highlighted{z-index:80;color:#fffdfa;border-color:var(--accent);background:var(--accent);box-shadow:0 12px 24px #1f7a5338}.edge-chip.highlighted small{color:#fffdf7b8}.edge-chip.dimmed{opacity:.04}.overview-mode .edge-chip:not(.highlighted):not(.focused),.overview-mode .edge-terminal:not(.highlighted):not(.focused){display:none}.edge-chip.focused{z-index:70;border-color:#1f7a535c;box-shadow:0 10px 22px #1f7a5324}.edge-terminal{position:absolute;z-index:32;--terminal-color: var(--line-strong);width:18px;height:10px;pointer-events:none;opacity:.95}.edge-terminal:before{content:"";position:absolute;top:4px;left:2px;right:2px;height:2px;border-radius:999px;background:var(--terminal-color)}.edge-terminal.target:after{content:"";position:absolute;top:1px;width:0;height:0;border-top:4px solid transparent;border-bottom:4px solid transparent}.edge-terminal[data-side=left]:before{left:7px}.edge-terminal[data-side=right]:before{right:7px}.edge-terminal.target[data-side=left]:after{right:0;border-left:7px solid var(--terminal-color)}.edge-terminal.target[data-side=right]:after{left:0;border-right:7px solid var(--terminal-color)}.edge-terminal.highlighted:before{height:3px}.edge-terminal.dimmed{opacity:.12}.cycle-edge-card{border-color:#d3422f57;background:linear-gradient(135deg,rgba(211,66,47,.1),transparent 58%),#fffaf2e0}.cycle-break-button{justify-content:center;margin:8px 0 4px}.inspector{border-left:1px solid var(--line);background:#fffaf2d1;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);padding:18px;overflow:auto}.inspector:focus{outline:none}.inspector.guided-focus{animation:guided-panel-focus 1.8s ease-out}@keyframes guided-panel-focus{0%{box-shadow:inset 0 0 0 3px #1f7a537a,-18px 0 44px #1f7a5329;background:#fafff7f0}60%{box-shadow:inset 0 0 0 3px #1f7a535c,-12px 0 34px #1f7a531f}to{box-shadow:none;background:#fffaf2d1}}.inspector header{display:flex;align-items:center;gap:8px;margin-bottom:14px}.mapping-code-panel{display:grid;gap:10px}.row-with-actions{display:flex;align-items:center;justify-content:space-between;gap:8px}.mapping-code{width:100%;min-height:260px;resize:vertical;border:1px solid var(--line);border-radius:10px;background:#fffdf7e6;color:var(--ink);padding:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.45}.storage-grid{display:grid;gap:8px}.path-status{display:grid;gap:3px;border:1px solid var(--line);border-radius:10px;background:#fffaf2b8;padding:9px}.path-status span{color:var(--muted);font-size:11px;font-weight:720}.path-status strong{color:var(--ink);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow-wrap:anywhere}.recent-mappings{display:grid;gap:8px;margin-top:4px}.open-mapping-panel{display:grid;gap:12px}.recent-mapping-list{display:grid;gap:7px}.recent-mapping-item{display:grid;gap:3px;text-align:left;border:1px solid var(--line);border-radius:10px;background:#fffdf7d1;color:var(--ink);padding:9px;cursor:pointer}.recent-mapping-item:hover,.recent-mapping-item:focus-visible{border-color:#1f7a5352;background:#1f7a5314}.recent-mapping-item span{font-weight:720}.recent-mapping-item small{color:var(--muted);overflow-wrap:anywhere}.inline-field{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px}.inspector h2{font-size:17px}.inspector h3{margin-top:22px;margin-bottom:8px;color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.08em}.row{display:grid;grid-template-columns:84px minmax(0,1fr);gap:8px;padding:8px 0;border-top:1px solid rgba(183,166,150,.35)}.row span{color:var(--muted)}.row strong{overflow-wrap:anywhere;font-weight:620}.variable-card{border:1px solid var(--line);border-radius:12px;background:#fffaf2bf;padding:10px}.edge-detail-card{display:grid;gap:4px;margin-bottom:14px;padding:10px;border:1px solid rgba(31,122,83,.24);border-radius:12px;background:linear-gradient(135deg,rgba(31,122,83,.08),transparent 58%),#fffaf2d1}.edge-detail-card .diagnostic,.edge-detail-card .empty-state{margin-top:6px}.variable-card .row:first-of-type{margin-top:8px}.variable-card-title{display:flex;align-items:center;justify-content:space-between;gap:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.variable-card-title span{overflow-wrap:anywhere;font-weight:720}.variable-card-title small{color:var(--muted);text-transform:uppercase;letter-spacing:.08em}.diagnostic,.edit-suggestion,.initialization-note,.empty-state{border:1px solid var(--line);border-radius:12px;background:#fffaf2bf;padding:10px;color:var(--muted)}.diagnostic,.edit-suggestion,.initialization-note{display:flex;align-items:center;gap:7px}.diagnostic,.edit-suggestion{color:var(--clay);border-color:#c9614a47;background:#c9614a17}.initialization-note{color:#87533b;border-color:#bf6a5452;background:#bf6a541a}.initialization-list{display:grid;gap:8px}.initialization-list.compact{gap:6px}.initialization-group{display:grid;gap:7px}.initialization-group h4,.provenance-block h4{margin:6px 0 0;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700}.initialization-item{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:10px;width:100%;padding:9px 10px;color:var(--ink);background:#fffaf2c7;border:1px solid rgba(191,106,84,.28);border-left:4px solid var(--clay);border-radius:10px;font:inherit;text-align:left;cursor:pointer}.initialization-item:hover,.initialization-item:focus-visible{background:#fff6f0f5;outline:none}.initialization-item span{overflow:hidden;color:var(--muted);font-size:12px;text-overflow:ellipsis;white-space:nowrap}.initialization-item strong{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.initialization-item small{grid-column:1 / -1;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.06em}.initialization-item.previous_time_step{border-left-color:var(--ochre)}.initialization-item.mapped_unresolved{border-left-color:var(--accent)}.warning-list,.provenance-block{display:grid;gap:8px}.warning-group{display:grid;gap:7px}.warning-group h4{margin:6px 0 0;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700}.warning-item,.provenance-edge{display:grid;gap:3px;width:100%;padding:9px 10px;border:1px solid rgba(201,144,53,.28);border-left:4px solid var(--ochre);border-radius:10px;background:#fffaf2c7;color:var(--ink);font:inherit;text-align:left;cursor:pointer}.warning-item.error{border-color:#bf6a5457;border-left-color:var(--clay);background:#bf6a5414}.warning-item.info{border-color:#7f8f7342;border-left-color:var(--sage);background:#7f8f7314}.provenance-edge{border-color:#b7a6966b;border-left-color:var(--line-strong)}.provenance-edge.mapped_variable{border-left-color:var(--accent)}.provenance-edge.hard_dependency{border-left-color:var(--clay)}.warning-item:hover,.warning-item:focus-visible,.provenance-edge:hover,.provenance-edge:focus-visible{background:#fff6f0f5;outline:none}.warning-item strong,.provenance-edge strong{overflow-wrap:anywhere;font-size:12px}.warning-item span,.provenance-edge span,.provenance-edge small{color:var(--muted);font-size:11px;line-height:1.25}.provenance-block{margin-top:10px}.empty-state.compact{padding:7px 8px;font-size:11px}.live-session{border-left:1px solid rgba(183,166,150,.36);padding-left:10px}.live-pill{display:inline-flex;align-items:center;height:28px;padding:0 9px;border:1px solid rgba(191,106,84,.38);border-radius:999px;color:var(--clay);background:#bf6a5414;font-size:11px;font-weight:800;text-transform:uppercase}.live-pill.connected{border-color:#1f7a5357;color:var(--accent);background:#1f7a5317}.metric-button:disabled{cursor:not-allowed;opacity:.42}.model-browser{display:grid;gap:7px}.model-browser-control{display:grid;gap:4px}.model-browser-control span,.parameter-row label{color:var(--muted);font-size:11px;font-weight:700}.model-browser-control select,.model-browser-control input,.parameter-row input,.parameter-row select{min-width:0;border:1px solid rgba(183,166,150,.44);border-radius:8px;background:#ffffffc7;color:var(--ink);font:inherit}.model-browser-control select,.model-browser-control input{height:32px;padding:0 8px}.model-browser-item{display:grid;gap:7px;padding:8px 9px;border:1px solid rgba(183,166,150,.34);border-radius:10px;background:#ffffff9e}.model-browser-item.add-model-config{gap:10px;padding-bottom:0}.model-browser-title{display:grid;gap:2px}.model-browser-item strong{overflow-wrap:anywhere;font-size:12px}.model-browser-item span{color:var(--muted);font-size:11px}.existing-model-editor{display:grid;gap:8px;margin-top:12px;padding-top:10px;border-top:1px solid rgba(183,166,150,.32)}.variable-mapping-editor{display:grid;gap:8px;margin-top:10px;padding-top:10px;border-top:1px solid rgba(183,166,150,.32)}.variable-mapping-editor h4{margin:0;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700}.initialization-editor{display:grid;gap:12px}.initialization-editor-group{display:grid;gap:8px}.initialization-editor-group h3{margin:0}.initialization-editor-row{display:grid;grid-template-columns:minmax(0,.85fr) minmax(0,1fr) minmax(82px,.72fr) auto;gap:7px;align-items:center;padding:9px;border:1px solid rgba(191,106,84,.26);border-left:4px solid var(--clay);border-radius:8px;background:#fffaf2c7}.initialization-editor-row.provided{border-color:#1f7a533d;border-left-color:var(--accent)}.initialization-editor-row label{overflow-wrap:anywhere;font-weight:700}.initialization-editor-row input,.initialization-editor-row select{min-width:0;height:30px;border:1px solid rgba(183,166,150,.44);border-radius:8px;background:#ffffffc7;color:var(--ink);font:inherit}.initialization-editor-row input{padding:0 8px}.initialization-editor-row small{grid-column:1 / -1;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.05em}.parameter-row{display:grid;grid-template-columns:minmax(0,.75fr) minmax(0,1fr) minmax(78px,.8fr);gap:6px;align-items:center}.parameter-row input,.parameter-row select{height:28px;padding:0 7px;font-size:12px}.rate-editor{display:grid;gap:6px;padding:7px;border:1px solid rgba(183,166,150,.28);border-radius:8px;background:#fcf9f4b8}.add-model-footer{position:sticky;bottom:-18px;display:flex;justify-content:flex-end;margin:2px -9px 0;padding:10px 9px;border-top:1px solid rgba(183,166,150,.34);border-radius:0 0 10px 10px;background:#fffaf2f0;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.rate-summary{color:var(--muted);font-size:11px;line-height:1.35}.rate-clock-row{display:grid;grid-template-columns:1fr 1fr;gap:6px}.rate-clock-row label{display:grid;gap:4px;color:var(--muted);font-size:11px;font-weight:700}.rate-clock-row input{min-width:0;height:28px;border:1px solid rgba(183,166,150,.44);border-radius:8px;background:#ffffffc7;color:var(--ink);font:inherit;padding:0 7px}.metric-button.danger{border-color:#bf6a546b;color:#9b3f2e;background:#fff2ecdb}@media(max-width:900px){.app-shell{grid-template-columns:1fr}.inspector{position:absolute;z-index:28;top:150px;right:16px;bottom:16px;width:min(360px,calc(100% - 32px));border:1px solid var(--line);border-radius:14px;box-shadow:0 18px 45px var(--shadow)}.relationship-legend,.scale-controls,.floating-panel{top:150px}.scale-controls{left:220px}}@media(max-width:720px){.topbar{top:10px;left:10px;right:10px;gap:8px;padding:9px 10px}.topbar:before{display:none}.brand-block{min-width:128px}.search-box{flex-basis:100%;max-width:none;min-width:0}.metrics{margin-left:0}.relationship-legend,.scale-controls,.floating-panel{top:132px;left:10px;right:10px;width:auto}.scale-controls{top:326px}.react-flow__minimap{display:none}}.mapping-dialog-overlay{position:fixed;inset:0;z-index:1000;background:#31272161;display:flex;align-items:center;justify-content:center}.mapping-dialog{background:var(--paper);border:1px solid var(--line-strong);border-radius:10px;box-shadow:0 8px 32px var(--shadow);width:360px;max-width:calc(100vw - 32px);display:flex;flex-direction:column;gap:0;overflow:hidden}.mapping-dialog-header{display:flex;align-items:center;justify-content:space-between;padding:14px 16px 10px;border-bottom:1px solid var(--line)}.mapping-dialog-header .eyebrow{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.07em;color:var(--muted)}.mapping-dialog-body{padding:14px 16px;display:flex;flex-direction:column;gap:14px}.mapping-port-summary{display:flex;align-items:center;gap:8px;background:var(--paper-strong);border:1px solid var(--line);border-radius:6px;padding:10px 12px}.mapping-port{display:flex;flex-direction:column;gap:1px;flex:1;min-width:0}.mapping-port small{font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}.mapping-port strong{font-size:12px;font-weight:700;color:var(--accent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mapping-port span{font-size:11px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mapping-port.target strong{color:var(--clay)}.mapping-arrow{font-size:18px;color:var(--muted);flex-shrink:0}.mapping-mode-section{display:flex;flex-direction:column;gap:6px}.mapping-mode-label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);margin-bottom:2px}.mapping-radio{display:flex;align-items:center;gap:8px;font-size:13px;cursor:pointer;padding:4px 0}.mapping-radio input[type=radio]{accent-color:var(--accent);width:14px;height:14px;flex-shrink:0}.mapping-scale-picker{display:flex;flex-direction:column;gap:4px}.mapping-checkbox{display:flex;align-items:center;gap:8px;font-size:13px;cursor:pointer;padding:2px 0}.mapping-checkbox input[type=checkbox]{accent-color:var(--accent);width:14px;height:14px;flex-shrink:0}.mapping-dialog-footer{display:flex;justify-content:flex-end;gap:8px;padding:10px 16px 14px;border-top:1px solid var(--line)}.accent-button{background:var(--accent);color:#fff;border-color:transparent}.accent-button:hover:not(:disabled){background:var(--sage-dark);border-color:transparent}.selector-preview{display:grid;gap:6px;padding:12px;border:1px solid #b9d3c4;background:#f2f8f4;border-radius:6px}.selector-preview span,.selector-preview p{margin:0;color:#615c55;font-size:.86rem}.selector-preview p{color:#a44b39}.selector-preview-button{display:inline-flex;align-items:center;gap:6px;width:max-content}.environment-ports{border-top:1px solid #ded5c8;margin-top:8px;padding-top:8px}.entity-node.environment{border-color:#8fb9c2;background:#f3fafb}.application-configuration-form{width:min(820px,100%)}.application-configuration-content{display:grid;gap:14px}.application-configuration-content fieldset{margin:0;padding:12px;border:1px solid #ded2c2;border-radius:5px}.application-configuration-content legend{padding:0 6px;color:#2d6652;font-weight:800}.application-configuration-content p{margin:0;color:#786d64}.configuration-list{display:grid;gap:7px;margin-bottom:10px}.configuration-list>div,.configuration-list>label{min-height:36px;display:grid;grid-template-columns:minmax(100px,.6fr) minmax(0,1fr) auto;align-items:center;gap:10px;padding:7px 9px;border:1px solid #e4d9ca;border-radius:5px;background:#fffdf8}.configuration-list span{overflow:hidden;color:#786d64;text-overflow:ellipsis;white-space:nowrap}.configuration-list select,.compact-configuration-row input,.compact-configuration-row select{width:100%;min-height:34px;border:1px solid #cdbda8;border-radius:5px;background:#fffdf8;color:#302923}.compact-configuration-row{align-items:end}.compact-configuration-row label{display:grid;gap:5px;color:#655b52;font-size:12px;font-weight:700}.compact-configuration-row button{min-height:34px;display:inline-flex;align-items:center;justify-content:center;gap:5px}.icon-button{width:32px;height:32px;display:inline-grid;place-items:center;padding:0}.application-configuration-form>footer{display:flex;justify-content:flex-end;padding:12px 15px;border-top:1px solid #ded2c2} diff --git a/frontend/dist/assets/index-DryeyTC8.js b/frontend/dist/assets/index-DryeyTC8.js deleted file mode 100644 index 98c34d9ba..000000000 --- a/frontend/dist/assets/index-DryeyTC8.js +++ /dev/null @@ -1,38 +0,0 @@ -(function(){const j=document.createElement("link").relList;if(j&&j.supports&&j.supports("modulepreload"))return;for(const D of document.querySelectorAll('link[rel="modulepreload"]'))x(D);new MutationObserver(D=>{for(const $ of D)if($.type==="childList")for(const E of $.addedNodes)E.tagName==="LINK"&&E.rel==="modulepreload"&&x(E)}).observe(document,{childList:!0,subtree:!0});function A(D){const $={};return D.integrity&&($.integrity=D.integrity),D.referrerPolicy&&($.referrerPolicy=D.referrerPolicy),D.crossOrigin==="use-credentials"?$.credentials="include":D.crossOrigin==="anonymous"?$.credentials="omit":$.credentials="same-origin",$}function x(D){if(D.ep)return;D.ep=!0;const $=A(D);fetch(D.href,$)}})();var Ahn=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function rke(g){return g&&g.__esModule&&Object.prototype.hasOwnProperty.call(g,"default")?g.default:g}var v7e={exports:{}},xG={};var Mhn;function qBn(){if(Mhn)return xG;Mhn=1;var g=Symbol.for("react.transitional.element"),j=Symbol.for("react.fragment");function A(x,D,$){var E=null;if($!==void 0&&(E=""+$),D.key!==void 0&&(E=""+D.key),"key"in D){$={};for(var H in D)H!=="key"&&($[H]=D[H])}else $=D;return D=$.ref,{$$typeof:g,type:x,key:E,ref:D!==void 0?D:null,props:$}}return xG.Fragment=j,xG.jsx=A,xG.jsxs=A,xG}var Thn;function UBn(){return Thn||(Thn=1,v7e.exports=qBn()),v7e.exports}var re=UBn(),y7e={exports:{}},Ac={};var xhn;function XBn(){if(xhn)return Ac;xhn=1;var g=Symbol.for("react.transitional.element"),j=Symbol.for("react.portal"),A=Symbol.for("react.fragment"),x=Symbol.for("react.strict_mode"),D=Symbol.for("react.profiler"),$=Symbol.for("react.consumer"),E=Symbol.for("react.context"),H=Symbol.for("react.forward_ref"),U=Symbol.for("react.suspense"),J=Symbol.for("react.memo"),te=Symbol.for("react.lazy"),Q=Symbol.for("react.activity"),Z=Symbol.iterator;function le(Me){return Me===null||typeof Me!="object"?null:(Me=Z&&Me[Z]||Me["@@iterator"],typeof Me=="function"?Me:null)}var be={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},ne=Object.assign,De={};function Se(Me,rn,ut){this.props=Me,this.context=rn,this.refs=De,this.updater=ut||be}Se.prototype.isReactComponent={},Se.prototype.setState=function(Me,rn){if(typeof Me!="object"&&typeof Me!="function"&&Me!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,Me,rn,"setState")},Se.prototype.forceUpdate=function(Me){this.updater.enqueueForceUpdate(this,Me,"forceUpdate")};function ze(){}ze.prototype=Se.prototype;function de(Me,rn,ut){this.props=Me,this.context=rn,this.refs=De,this.updater=ut||be}var Le=de.prototype=new ze;Le.constructor=de,ne(Le,Se.prototype),Le.isPureReactComponent=!0;var an=Array.isArray;function ln(){}var on={H:null,A:null,T:null,S:null},Mn=Object.prototype.hasOwnProperty;function Nn(Me,rn,ut){var st=ut.ref;return{$$typeof:g,type:Me,key:rn,ref:st!==void 0?st:null,props:ut}}function Ft(Me,rn){return Nn(Me.type,rn,Me.props)}function In(Me){return typeof Me=="object"&&Me!==null&&Me.$$typeof===g}function nt(Me){var rn={"=":"=0",":":"=2"};return"$"+Me.replace(/[=:]/g,function(ut){return rn[ut]})}var Y=/\/+/g;function Fe(Me,rn){return typeof Me=="object"&&Me!==null&&Me.key!=null?nt(""+Me.key):rn.toString(36)}function gn(Me){switch(Me.status){case"fulfilled":return Me.value;case"rejected":throw Me.reason;default:switch(typeof Me.status=="string"?Me.then(ln,ln):(Me.status="pending",Me.then(function(rn){Me.status==="pending"&&(Me.status="fulfilled",Me.value=rn)},function(rn){Me.status==="pending"&&(Me.status="rejected",Me.reason=rn)})),Me.status){case"fulfilled":return Me.value;case"rejected":throw Me.reason}}throw Me}function Ae(Me,rn,ut,st,Kt){var li=typeof Me;(li==="undefined"||li==="boolean")&&(Me=null);var oi=!1;if(Me===null)oi=!0;else switch(li){case"bigint":case"string":case"number":oi=!0;break;case"object":switch(Me.$$typeof){case g:case j:oi=!0;break;case te:return oi=Me._init,Ae(oi(Me._payload),rn,ut,st,Kt)}}if(oi)return Kt=Kt(Me),oi=st===""?"."+Fe(Me,0):st,an(Kt)?(ut="",oi!=null&&(ut=oi.replace(Y,"$&/")+"/"),Ae(Kt,rn,ut,"",function(nc){return nc})):Kt!=null&&(In(Kt)&&(Kt=Ft(Kt,ut+(Kt.key==null||Me&&Me.key===Kt.key?"":(""+Kt.key).replace(Y,"$&/")+"/")+oi)),rn.push(Kt)),1;oi=0;var Jt=st===""?".":st+":";if(an(Me))for(var hi=0;hi>>1,En=Ae[jn];if(0>>1;jnD(ut,un))stD(Kt,ut)?(Ae[jn]=Kt,Ae[st]=un,jn=st):(Ae[jn]=ut,Ae[rn]=un,jn=rn);else if(stD(Kt,un))Ae[jn]=Kt,Ae[st]=un,jn=st;else break e}}return Je}function D(Ae,Je){var un=Ae.sortIndex-Je.sortIndex;return un!==0?un:Ae.id-Je.id}if(g.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var $=performance;g.unstable_now=function(){return $.now()}}else{var E=Date,H=E.now();g.unstable_now=function(){return E.now()-H}}var U=[],J=[],te=1,Q=null,Z=3,le=!1,be=!1,ne=!1,De=!1,Se=typeof setTimeout=="function"?setTimeout:null,ze=typeof clearTimeout=="function"?clearTimeout:null,de=typeof setImmediate<"u"?setImmediate:null;function Le(Ae){for(var Je=A(J);Je!==null;){if(Je.callback===null)x(J);else if(Je.startTime<=Ae)x(J),Je.sortIndex=Je.expirationTime,j(U,Je);else break;Je=A(J)}}function an(Ae){if(ne=!1,Le(Ae),!be)if(A(U)!==null)be=!0,ln||(ln=!0,nt());else{var Je=A(J);Je!==null&&gn(an,Je.startTime-Ae)}}var ln=!1,on=-1,Mn=5,Nn=-1;function Ft(){return De?!0:!(g.unstable_now()-NnAe&&Ft());){var jn=Q.callback;if(typeof jn=="function"){Q.callback=null,Z=Q.priorityLevel;var En=jn(Q.expirationTime<=Ae);if(Ae=g.unstable_now(),typeof En=="function"){Q.callback=En,Le(Ae),Je=!0;break n}Q===A(U)&&x(U),Le(Ae)}else x(U);Q=A(U)}if(Q!==null)Je=!0;else{var Me=A(J);Me!==null&&gn(an,Me.startTime-Ae),Je=!1}}break e}finally{Q=null,Z=un,le=!1}Je=void 0}}finally{Je?nt():ln=!1}}}var nt;if(typeof de=="function")nt=function(){de(In)};else if(typeof MessageChannel<"u"){var Y=new MessageChannel,Fe=Y.port2;Y.port1.onmessage=In,nt=function(){Fe.postMessage(null)}}else nt=function(){Se(In,0)};function gn(Ae,Je){on=Se(function(){Ae(g.unstable_now())},Je)}g.unstable_IdlePriority=5,g.unstable_ImmediatePriority=1,g.unstable_LowPriority=4,g.unstable_NormalPriority=3,g.unstable_Profiling=null,g.unstable_UserBlockingPriority=2,g.unstable_cancelCallback=function(Ae){Ae.callback=null},g.unstable_forceFrameRate=function(Ae){0>Ae||125jn?(Ae.sortIndex=un,j(J,Ae),A(U)===null&&Ae===A(J)&&(ne?(ze(on),on=-1):ne=!0,gn(an,un-jn))):(Ae.sortIndex=En,j(U,Ae),be||le||(be=!0,ln||(ln=!0,nt()))),Ae},g.unstable_shouldYield=Ft,g.unstable_wrapCallback=function(Ae){var Je=Z;return function(){var un=Z;Z=Je;try{return Ae.apply(this,arguments)}finally{Z=un}}}})(j7e)),j7e}var Nhn;function YBn(){return Nhn||(Nhn=1,E7e.exports=VBn()),E7e.exports}var S7e={exports:{}},W1={};var Dhn;function QBn(){if(Dhn)return W1;Dhn=1;var g=XG();function j(U){var J="https://react.dev/errors/"+U;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(g)}catch(j){console.error(j)}}return g(),S7e.exports=QBn(),S7e.exports}var _hn;function ZBn(){if(_hn)return CG;_hn=1;var g=YBn(),j=XG(),A=edn();function x(a){var d="https://react.dev/errors/"+a;if(1En||(a.current=jn[En],jn[En]=null,En--)}function ut(a,d){En++,jn[En]=a.current,a.current=d}var st=Me(null),Kt=Me(null),li=Me(null),oi=Me(null);function Jt(a,d){switch(ut(li,d),ut(Kt,a),ut(st,null),d.nodeType){case 9:case 11:a=(a=d.documentElement)&&(a=a.namespaceURI)?uP(a):0;break;default:if(a=d.tagName,d=d.namespaceURI)d=uP(d),a=oP(d,a);else switch(a){case"svg":a=1;break;case"math":a=2;break;default:a=0}}rn(st),ut(st,a)}function hi(){rn(st),rn(Kt),rn(li)}function nc(a){a.memoizedState!==null&&ut(oi,a);var d=st.current,w=oP(d,a.type);d!==w&&(ut(Kt,a),ut(st,w))}function Xr(a){Kt.current===a&&(rn(st),rn(Kt)),oi.current===a&&(rn(oi),B5._currentValue=un)}var pr,Ei;function Bi(a){if(pr===void 0)try{throw Error()}catch(w){var d=w.stack.trim().match(/\n( *(at )?)/);pr=d&&d[1]||"",Ei=-1)":-1C||Ze[k]!==Ln[C]){var et=` -`+Ze[k].replace(" at new "," at ");return a.displayName&&et.includes("")&&(et=et.replace("",a.displayName)),et}while(1<=k&&0<=C);break}}}finally{Fc=!1,Error.prepareStackTrace=w}return(w=a?a.displayName||a.name:"")?Bi(w):""}function kl(a,d){switch(a.tag){case 26:case 27:case 5:return Bi(a.type);case 16:return Bi("Lazy");case 13:return a.child!==d&&d!==null?Bi("Suspense Fallback"):Bi("Suspense");case 19:return Bi("SuspenseList");case 0:case 15:return Du(a.type,!1);case 11:return Du(a.type.render,!1);case 1:return Du(a.type,!0);case 31:return Bi("Activity");default:return""}}function s1(a){try{var d="",w=null;do d+=kl(a,w),w=a,a=a.return;while(a);return d}catch(k){return` -Error generating stack: `+k.message+` -`+k.stack}}var Za=Object.prototype.hasOwnProperty,Wa=g.unstable_scheduleCallback,eu=g.unstable_cancelCallback,Ng=g.unstable_shouldYield,Ai=g.unstable_requestPaint,Mc=g.unstable_now,no=g.unstable_getCurrentPriorityLevel,bb=g.unstable_ImmediatePriority,l1=g.unstable_UserBlockingPriority,$m=g.unstable_NormalPriority,pM=g.unstable_LowPriority,Zv=g.unstable_IdlePriority,mM=g.log,vM=g.unstable_setDisableYieldValue,Rm=null,eh=null;function gb(a){if(typeof mM=="function"&&vM(a),eh&&typeof eh.setStrictMode=="function")try{eh.setStrictMode(Rm,a)}catch{}}var nh=Math.clz32?Math.clz32:EM,yM=Math.log,kM=Math.LN2;function EM(a){return a>>>=0,a===0?32:31-(yM(a)/kM|0)|0}var Wv=256,e5=262144,n5=4194304;function Dg(a){var d=a&42;if(d!==0)return d;switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return a&261888;case 262144:case 524288:case 1048576:case 2097152:return a&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return a&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return a}}function t5(a,d,w){var k=a.pendingLanes;if(k===0)return 0;var C=0,N=a.suspendedLanes,V=a.pingedLanes;a=a.warmLanes;var ae=k&134217727;return ae!==0?(k=ae&~N,k!==0?C=Dg(k):(V&=ae,V!==0?C=Dg(V):w||(w=ae&~a,w!==0&&(C=Dg(w))))):(ae=k&~N,ae!==0?C=Dg(ae):V!==0?C=Dg(V):w||(w=k&~a,w!==0&&(C=Dg(w)))),C===0?0:d!==0&&d!==C&&(d&N)===0&&(N=C&-C,w=d&-d,N>=w||N===32&&(w&4194048)!==0)?d:C}function Bm(a,d){return(a.pendingLanes&~(a.suspendedLanes&~a.pingedLanes)&d)===0}function jM(a,d){switch(a){case 1:case 2:case 4:case 8:case 64:return d+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return d+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function z7(){var a=n5;return n5<<=1,(n5&62914560)===0&&(n5=4194304),a}function U6(a){for(var d=[],w=0;31>w;w++)d.push(a);return d}function zm(a,d){a.pendingLanes|=d,d!==268435456&&(a.suspendedLanes=0,a.pingedLanes=0,a.warmLanes=0)}function SM(a,d,w,k,C,N){var V=a.pendingLanes;a.pendingLanes=w,a.suspendedLanes=0,a.pingedLanes=0,a.warmLanes=0,a.expiredLanes&=w,a.entangledLanes&=w,a.errorRecoveryDisabledLanes&=w,a.shellSuspendCounter=0;var ae=a.entanglements,Ze=a.expirationTimes,Ln=a.hiddenUpdates;for(w=V&~w;0"u")return null;try{return a.activeElement||a.body}catch{return a.body}}var xM=/[\n"\\]/g;function Ch(a){return a.replace(xM,function(d){return"\\"+d.charCodeAt(0).toString(16)+" "})}function Um(a,d,w,k,C,N,V,ae){a.name="",V!=null&&typeof V!="function"&&typeof V!="symbol"&&typeof V!="boolean"?a.type=V:a.removeAttribute("type"),d!=null?V==="number"?(d===0&&a.value===""||a.value!=d)&&(a.value=""+xh(d)):a.value!==""+xh(d)&&(a.value=""+xh(d)):V!=="submit"&&V!=="reset"||a.removeAttribute("value"),d!=null?Q6(a,V,xh(d)):w!=null?Q6(a,V,xh(w)):k!=null&&a.removeAttribute("value"),C==null&&N!=null&&(a.defaultChecked=!!N),C!=null&&(a.checked=C&&typeof C!="function"&&typeof C!="symbol"),ae!=null&&typeof ae!="function"&&typeof ae!="symbol"&&typeof ae!="boolean"?a.name=""+xh(ae):a.removeAttribute("name")}function Q7(a,d,w,k,C,N,V,ae){if(N!=null&&typeof N!="function"&&typeof N!="symbol"&&typeof N!="boolean"&&(a.type=N),d!=null||w!=null){if(!(N!=="submit"&&N!=="reset"||d!=null)){u5(a);return}w=w!=null?""+xh(w):"",d=d!=null?""+xh(d):w,ae||d===a.value||(a.value=d),a.defaultValue=d}k=k??C,k=typeof k!="function"&&typeof k!="symbol"&&!!k,a.checked=ae?a.checked:!!k,a.defaultChecked=!!k,V!=null&&typeof V!="function"&&typeof V!="symbol"&&typeof V!="boolean"&&(a.name=V),u5(a)}function Q6(a,d,w){d==="number"&&qm(a.ownerDocument)===a||a.defaultValue===""+w||(a.defaultValue=""+w)}function Lg(a,d,w,k){if(a=a.options,d){d={};for(var C=0;C"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),IM=!1;if($g)try{var W6={};Object.defineProperty(W6,"passive",{get:function(){IM=!0}}),window.addEventListener("test",W6,W6),window.removeEventListener("test",W6,W6)}catch{IM=!1}var w2=null,_M=null,W7=null;function jI(){if(W7)return W7;var a,d=_M,w=d.length,k,C="value"in w2?w2.value:w2.textContent,N=C.length;for(a=0;a=ty),CI=" ",OI=!1;function NI(a,d){switch(a){case"keyup":return xq.indexOf(d.keyCode)!==-1;case"keydown":return d.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function DI(a){return a=a.detail,typeof a=="object"&&"data"in a?a.data:null}var f5=!1;function Oq(a,d){switch(a){case"compositionend":return DI(d);case"keypress":return d.which!==32?null:(OI=!0,CI);case"textInput":return a=d.data,a===CI&&OI?null:a;default:return null}}function Nq(a,d){if(f5)return a==="compositionend"||!BM&&NI(a,d)?(a=jI(),W7=_M=w2=null,f5=!1,a):null;switch(a){case"paste":return null;case"keypress":if(!(d.ctrlKey||d.altKey||d.metaKey)||d.ctrlKey&&d.altKey){if(d.char&&1=d)return{node:w,offset:d-a};a=k}e:{for(;w;){if(w.nextSibling){w=w.nextSibling;break e}w=w.parentNode}w=void 0}w=BI(w)}}function FI(a,d){return a&&d?a===d?!0:a&&a.nodeType===3?!1:d&&d.nodeType===3?FI(a,d.parentNode):"contains"in a?a.contains(d):a.compareDocumentPosition?!!(a.compareDocumentPosition(d)&16):!1:!1}function HI(a){a=a!=null&&a.ownerDocument!=null&&a.ownerDocument.defaultView!=null?a.ownerDocument.defaultView:window;for(var d=qm(a.document);d instanceof a.HTMLIFrameElement;){try{var w=typeof d.contentWindow.location.href=="string"}catch{w=!1}if(w)a=d.contentWindow;else break;d=qm(a.document)}return d}function JM(a){var d=a&&a.nodeName&&a.nodeName.toLowerCase();return d&&(d==="input"&&(a.type==="text"||a.type==="search"||a.type==="tel"||a.type==="url"||a.type==="password")||d==="textarea"||a.contentEditable==="true")}var Bq=$g&&"documentMode"in document&&11>=document.documentMode,a5=null,GM=null,uy=null,qM=!1;function JI(a,d,w){var k=w.window===w?w.document:w.nodeType===9?w:w.ownerDocument;qM||a5==null||a5!==qm(k)||(k=a5,"selectionStart"in k&&JM(k)?k={start:k.selectionStart,end:k.selectionEnd}:(k=(k.ownerDocument&&k.ownerDocument.defaultView||window).getSelection(),k={anchorNode:k.anchorNode,anchorOffset:k.anchorOffset,focusNode:k.focusNode,focusOffset:k.focusOffset}),uy&&cy(uy,k)||(uy=k,k=qk(GM,"onSelect"),0>=V,C-=V,wb=1<<32-nh(d)+C|w<Hr?(tc=Ui,Ui=null):tc=Ui.sibling;var Gc=Hn(yn,Ui,Dn[Hr],rt);if(Gc===null){Ui===null&&(Ui=tc);break}a&&Ui&&Gc.alternate===null&&d(yn,Ui),tn=N(Gc,tn,Hr),Iu===null?Hi=Gc:Iu.sibling=Gc,Iu=Gc,Ui=tc}if(Hr===Dn.length)return w(yn,Ui),cu&&Bg(yn,Hr),Hi;if(Ui===null){for(;HrHr?(tc=Ui,Ui=null):tc=Ui.sibling;var jt=Hn(yn,Ui,Gc.value,rt);if(jt===null){Ui===null&&(Ui=tc);break}a&&Ui&&jt.alternate===null&&d(yn,Ui),tn=N(jt,tn,Hr),Iu===null?Hi=jt:Iu.sibling=jt,Iu=jt,Ui=tc}if(Gc.done)return w(yn,Ui),cu&&Bg(yn,Hr),Hi;if(Ui===null){for(;!Gc.done;Hr++,Gc=Dn.next())Gc=bt(yn,Gc.value,rt),Gc!==null&&(tn=N(Gc,tn,Hr),Iu===null?Hi=Gc:Iu.sibling=Gc,Iu=Gc);return cu&&Bg(yn,Hr),Hi}for(Ui=k(Ui);!Gc.done;Hr++,Gc=Dn.next())Gc=Zn(Ui,yn,Hr,Gc.value,rt),Gc!==null&&(a&&Gc.alternate!==null&&Ui.delete(Gc.key===null?Hr:Gc.key),tn=N(Gc,tn,Hr),Iu===null?Hi=Gc:Iu.sibling=Gc,Iu=Gc);return a&&Ui.forEach(function(rX){return d(yn,rX)}),cu&&Bg(yn,Hr),Hi}function ro(yn,tn,Dn,rt){if(typeof Dn=="object"&&Dn!==null&&Dn.type===ne&&Dn.key===null&&(Dn=Dn.props.children),typeof Dn=="object"&&Dn!==null){switch(Dn.$$typeof){case le:e:{for(var Hi=Dn.key;tn!==null;){if(tn.key===Hi){if(Hi=Dn.type,Hi===ne){if(tn.tag===7){w(yn,tn.sibling),rt=C(tn,Dn.props.children),rt.return=yn,yn=rt;break e}}else if(tn.elementType===Hi||typeof Hi=="object"&&Hi!==null&&Hi.$$typeof===Mn&&e3(Hi)===tn.type){w(yn,tn.sibling),rt=C(tn,Dn.props),by(rt,Dn),rt.return=yn,yn=rt;break e}w(yn,tn);break}else d(yn,tn);tn=tn.sibling}Dn.type===ne?(rt=Qm(Dn.props.children,yn.mode,rt,Dn.key),rt.return=yn,yn=rt):(rt=lk(Dn.type,Dn.key,Dn.props,null,yn.mode,rt),by(rt,Dn),rt.return=yn,yn=rt)}return V(yn);case be:e:{for(Hi=Dn.key;tn!==null;){if(tn.key===Hi)if(tn.tag===4&&tn.stateNode.containerInfo===Dn.containerInfo&&tn.stateNode.implementation===Dn.implementation){w(yn,tn.sibling),rt=C(tn,Dn.children||[]),rt.return=yn,yn=rt;break e}else{w(yn,tn);break}else d(yn,tn);tn=tn.sibling}rt=ZM(Dn,yn.mode,rt),rt.return=yn,yn=rt}return V(yn);case Mn:return Dn=e3(Dn),ro(yn,tn,Dn,rt)}if(gn(Dn))return Di(yn,tn,Dn,rt);if(nt(Dn)){if(Hi=nt(Dn),typeof Hi!="function")throw Error(x(150));return Dn=Hi.call(Dn),Tr(yn,tn,Dn,rt)}if(typeof Dn.then=="function")return ro(yn,tn,bk(Dn),rt);if(Dn.$$typeof===de)return ro(yn,tn,ly(yn,Dn),rt);gk(yn,Dn)}return typeof Dn=="string"&&Dn!==""||typeof Dn=="number"||typeof Dn=="bigint"?(Dn=""+Dn,tn!==null&&tn.tag===6?(w(yn,tn.sibling),rt=C(tn,Dn),rt.return=yn,yn=rt):(w(yn,tn),rt=QM(Dn,yn.mode,rt),rt.return=yn,yn=rt),V(yn)):w(yn,tn)}return function(yn,tn,Dn,rt){try{dy=0;var Hi=ro(yn,tn,Dn,rt);return j5=null,Hi}catch(Ui){if(Ui===E5||Ui===hk)throw Ui;var Iu=a1(29,Ui,null,yn.mode);return Iu.lanes=rt,Iu.return=yn,Iu}}}var t3=f_(!0),a_=f_(!1),S2=!1;function aT(a){a.updateQueue={baseState:a.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function hT(a,d){a=a.updateQueue,d.updateQueue===a&&(d.updateQueue={baseState:a.baseState,firstBaseUpdate:a.firstBaseUpdate,lastBaseUpdate:a.lastBaseUpdate,shared:a.shared,callbacks:null})}function A2(a){return{lane:a,tag:0,payload:null,callback:null,next:null}}function M2(a,d,w){var k=a.updateQueue;if(k===null)return null;if(k=k.shared,(zu&2)!==0){var C=k.pending;return C===null?d.next=d:(d.next=C.next,C.next=d),k.pending=d,d=sk(a),YI(a,null,w),d}return ok(a,k,d,w),sk(a)}function gy(a,d,w){if(d=d.updateQueue,d!==null&&(d=d.shared,(w&4194048)!==0)){var k=d.lanes;k&=a.pendingLanes,w|=k,d.lanes=w,X6(a,w)}}function dT(a,d){var w=a.updateQueue,k=a.alternate;if(k!==null&&(k=k.updateQueue,w===k)){var C=null,N=null;if(w=w.firstBaseUpdate,w!==null){do{var V={lane:w.lane,tag:w.tag,payload:w.payload,callback:null,next:null};N===null?C=N=V:N=N.next=V,w=w.next}while(w!==null);N===null?C=N=d:N=N.next=d}else C=N=d;w={baseState:k.baseState,firstBaseUpdate:C,lastBaseUpdate:N,shared:k.shared,callbacks:k.callbacks},a.updateQueue=w;return}a=w.lastBaseUpdate,a===null?w.firstBaseUpdate=d:a.next=d,w.lastBaseUpdate=d}var bT=!1;function wy(){if(bT){var a=k5;if(a!==null)throw a}}function py(a,d,w,k){bT=!1;var C=a.updateQueue;S2=!1;var N=C.firstBaseUpdate,V=C.lastBaseUpdate,ae=C.shared.pending;if(ae!==null){C.shared.pending=null;var Ze=ae,Ln=Ze.next;Ze.next=null,V===null?N=Ln:V.next=Ln,V=Ze;var et=a.alternate;et!==null&&(et=et.updateQueue,ae=et.lastBaseUpdate,ae!==V&&(ae===null?et.firstBaseUpdate=Ln:ae.next=Ln,et.lastBaseUpdate=Ze))}if(N!==null){var bt=C.baseState;V=0,et=Ln=Ze=null,ae=N;do{var Hn=ae.lane&-536870913,Zn=Hn!==ae.lane;if(Zn?(nu&Hn)===Hn:(k&Hn)===Hn){Hn!==0&&Hn===y5&&(bT=!0),et!==null&&(et=et.next={lane:0,tag:ae.tag,payload:ae.payload,callback:null,next:null});e:{var Di=a,Tr=ae;Hn=d;var ro=w;switch(Tr.tag){case 1:if(Di=Tr.payload,typeof Di=="function"){bt=Di.call(ro,bt,Hn);break e}bt=Di;break e;case 3:Di.flags=Di.flags&-65537|128;case 0:if(Di=Tr.payload,Hn=typeof Di=="function"?Di.call(ro,bt,Hn):Di,Hn==null)break e;bt=Q({},bt,Hn);break e;case 2:S2=!0}}Hn=ae.callback,Hn!==null&&(a.flags|=64,Zn&&(a.flags|=8192),Zn=C.callbacks,Zn===null?C.callbacks=[Hn]:Zn.push(Hn))}else Zn={lane:Hn,tag:ae.tag,payload:ae.payload,callback:ae.callback,next:null},et===null?(Ln=et=Zn,Ze=bt):et=et.next=Zn,V|=Hn;if(ae=ae.next,ae===null){if(ae=C.shared.pending,ae===null)break;Zn=ae,ae=Zn.next,Zn.next=null,C.lastBaseUpdate=Zn,C.shared.pending=null}}while(!0);et===null&&(Ze=bt),C.baseState=Ze,C.firstBaseUpdate=Ln,C.lastBaseUpdate=et,N===null&&(C.shared.lanes=0),N2|=V,a.lanes=V,a.memoizedState=bt}}function h_(a,d){if(typeof a!="function")throw Error(x(191,a));a.call(d)}function d_(a,d){var w=a.callbacks;if(w!==null)for(a.callbacks=null,a=0;aN?N:8;var V=Ae.T,ae={};Ae.T=ae,IT(a,!1,d,w);try{var Ze=C(),Ln=Ae.S;if(Ln!==null&&Ln(ae,Ze),Ze!==null&&typeof Ze=="object"&&typeof Ze.then=="function"){var et=Xq(Ze,k);ky(a,d,et,w1(a))}else ky(a,d,k,w1(a))}catch(bt){ky(a,d,{then:function(){},status:"rejected",reason:bt},w1())}finally{Je.p=N,V!==null&&ae.types!==null&&(V.types=ae.types),Ae.T=V}}function NT(){}function yy(a,d,w,k){if(a.tag!==5)throw Error(x(476));var C=G_(a).queue;J_(a,C,d,un,w===null?NT:function(){return Ak(a),w(k)})}function G_(a){var d=a.memoizedState;if(d!==null)return d;d={memoizedState:un,baseState:un,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Jg,lastRenderedState:un},next:null};var w={};return d.next={memoizedState:w,baseState:w,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:Jg,lastRenderedState:w},next:null},a.memoizedState=d,a=a.alternate,a!==null&&(a.memoizedState=d),d}function Ak(a){var d=G_(a);d.next===null&&(d=a.alternate.memoizedState),ky(a,d.next.queue,{},w1())}function DT(){return Zf(B5)}function q_(){return Qs().memoizedState}function U_(){return Qs().memoizedState}function eU(a){for(var d=a.return;d!==null;){switch(d.tag){case 24:case 3:var w=w1();a=A2(w);var k=M2(d,a,w);k!==null&&(_h(k,d,w),gy(k,d,w)),d={cache:uT()},a.payload=d;return}d=d.return}}function nU(a,d,w){var k=w1();w={lane:k,revertLane:0,gesture:null,action:w,hasEagerState:!1,eagerState:null,next:null},Mk(a)?K_(d,w):(w=VM(a,d,w,k),w!==null&&(_h(w,a,k),_T(w,d,k)))}function X_(a,d,w){var k=w1();ky(a,d,w,k)}function ky(a,d,w,k){var C={lane:k,revertLane:0,gesture:null,action:w,hasEagerState:!1,eagerState:null,next:null};if(Mk(a))K_(d,C);else{var N=a.alternate;if(a.lanes===0&&(N===null||N.lanes===0)&&(N=d.lastRenderedReducer,N!==null))try{var V=d.lastRenderedState,ae=N(V,w);if(C.hasEagerState=!0,C.eagerState=ae,f1(ae,V))return ok(a,d,C,0),Do===null&&uk(),!1}catch{}if(w=VM(a,d,C,k),w!==null)return _h(w,a,k),_T(w,d,k),!0}return!1}function IT(a,d,w,k){if(k={lane:2,revertLane:gx(),gesture:null,action:k,hasEagerState:!1,eagerState:null,next:null},Mk(a)){if(d)throw Error(x(479))}else d=VM(a,w,k,2),d!==null&&_h(d,a,2)}function Mk(a){var d=a.alternate;return a===dc||d!==null&&d===dc}function K_(a,d){A5=mk=!0;var w=a.pending;w===null?d.next=d:(d.next=w.next,w.next=d),a.pending=d}function _T(a,d,w){if((w&4194048)!==0){var k=d.lanes;k&=a.pendingLanes,w|=k,d.lanes=w,X6(a,w)}}var Ey={readContext:Zf,use:kk,useCallback:$s,useContext:$s,useEffect:$s,useImperativeHandle:$s,useLayoutEffect:$s,useInsertionEffect:$s,useMemo:$s,useReducer:$s,useRef:$s,useState:$s,useDebugValue:$s,useDeferredValue:$s,useTransition:$s,useSyncExternalStore:$s,useId:$s,useHostTransitionStatus:$s,useFormState:$s,useActionState:$s,useOptimistic:$s,useMemoCache:$s,useCacheRefresh:$s};Ey.useEffectEvent=$s;var tU={readContext:Zf,use:kk,useCallback:function(a,d){return th().memoizedState=[a,d===void 0?null:d],a},useContext:Zf,useEffect:__,useImperativeHandle:function(a,d,w){w=w!=null?w.concat([a]):null,jk(4194308,4,R_.bind(null,d,a),w)},useLayoutEffect:function(a,d){return jk(4194308,4,a,d)},useInsertionEffect:function(a,d){jk(4,2,a,d)},useMemo:function(a,d){var w=th();d=d===void 0?null:d;var k=a();if(i3){gb(!0);try{a()}finally{gb(!1)}}return w.memoizedState=[k,d],k},useReducer:function(a,d,w){var k=th();if(w!==void 0){var C=w(d);if(i3){gb(!0);try{w(d)}finally{gb(!1)}}}else C=d;return k.memoizedState=k.baseState=C,a={pending:null,lanes:0,dispatch:null,lastRenderedReducer:a,lastRenderedState:C},k.queue=a,a=a.dispatch=nU.bind(null,dc,a),[k.memoizedState,a]},useRef:function(a){var d=th();return a={current:a},d.memoizedState=a},useState:function(a){a=MT(a);var d=a.queue,w=X_.bind(null,dc,d);return d.dispatch=w,[a.memoizedState,w]},useDebugValue:CT,useDeferredValue:function(a,d){var w=th();return OT(w,a,d)},useTransition:function(){var a=MT(!1);return a=J_.bind(null,dc,a.queue,!0,!1),th().memoizedState=a,[!1,a]},useSyncExternalStore:function(a,d,w){var k=dc,C=th();if(cu){if(w===void 0)throw Error(x(407));w=w()}else{if(w=d(),Do===null)throw Error(x(349));(nu&127)!==0||v_(k,d,w)}C.memoizedState=w;var N={value:w,getSnapshot:d};return C.queue=N,__(Yq.bind(null,k,N,a),[a]),k.flags|=2048,T5(9,{destroy:void 0},y_.bind(null,k,N,w,d),null),w},useId:function(){var a=th(),d=Do.identifierPrefix;if(cu){var w=pb,k=wb;w=(k&~(1<<32-nh(k)-1)).toString(32)+w,d="_"+d+"R_"+w,w=vk++,0<\/script>",N=N.removeChild(N.firstChild);break;case"select":N=typeof k.is=="string"?V.createElement("select",{is:k.is}):V.createElement("select"),k.multiple?N.multiple=!0:k.size&&(N.size=k.size);break;default:N=typeof k.is=="string"?V.createElement(C,{is:k.is}):V.createElement(C)}}N[Ql]=d,N[Ea]=k;e:for(V=d.child;V!==null;){if(V.tag===5||V.tag===6)N.appendChild(V.stateNode);else if(V.tag!==4&&V.tag!==27&&V.child!==null){V.child.return=V,V=V.child;continue}if(V===d)break e;for(;V.sibling===null;){if(V.return===null||V.return===d)break e;V=V.return}V.sibling.return=V.return,V=V.sibling}d.stateNode=N;e:switch(ea(N,C,k),C){case"button":case"input":case"select":case"textarea":k=!!k.autoFocus;break e;case"img":k=!0;break e;default:k=!1}k&&i0(d)}}return vo(d),KT(d,d.type,a===null?null:a.memoizedProps,d.pendingProps,w),null;case 6:if(a&&d.stateNode!=null)a.memoizedProps!==k&&i0(d);else{if(typeof k!="string"&&d.stateNode===null)throw Error(x(166));if(a=li.current,p5(d)){if(a=d.stateNode,w=d.memoizedProps,k=null,C=Qf,C!==null)switch(C.tag){case 27:case 5:k=C.memoizedProps}a[Ql]=d,a=!!(a.nodeValue===w||k!==null&&k.suppressHydrationWarning===!0||rP(a.nodeValue,w)),a||v2(d,!0)}else a=Uk(a).createTextNode(k),a[Ql]=d,d.stateNode=a}return vo(d),null;case 31:if(w=d.memoizedState,a===null||a.memoizedState!==null){if(k=p5(d),w!==null){if(a===null){if(!k)throw Error(x(318));if(a=d.memoizedState,a=a!==null?a.dehydrated:null,!a)throw Error(x(557));a[Ql]=d}else Zm(),(d.flags&128)===0&&(d.memoizedState=null),d.flags|=4;vo(d),a=!1}else w=m5(),a!==null&&a.memoizedState!==null&&(a.memoizedState.hydrationErrors=w),a=!0;if(!a)return d.flags&256?(d1(d),d):(d1(d),null);if((d.flags&128)!==0)throw Error(x(558))}return vo(d),null;case 13:if(k=d.memoizedState,a===null||a.memoizedState!==null&&a.memoizedState.dehydrated!==null){if(C=p5(d),k!==null&&k.dehydrated!==null){if(a===null){if(!C)throw Error(x(318));if(C=d.memoizedState,C=C!==null?C.dehydrated:null,!C)throw Error(x(317));C[Ql]=d}else Zm(),(d.flags&128)===0&&(d.memoizedState=null),d.flags|=4;vo(d),C=!1}else C=m5(),a!==null&&a.memoizedState!==null&&(a.memoizedState.hydrationErrors=C),C=!0;if(!C)return d.flags&256?(d1(d),d):(d1(d),null)}return d1(d),(d.flags&128)!==0?(d.lanes=w,d):(w=k!==null,a=a!==null&&a.memoizedState!==null,w&&(k=d.child,C=null,k.alternate!==null&&k.alternate.memoizedState!==null&&k.alternate.memoizedState.cachePool!==null&&(C=k.alternate.memoizedState.cachePool.pool),N=null,k.memoizedState!==null&&k.memoizedState.cachePool!==null&&(N=k.memoizedState.cachePool.pool),N!==C&&(k.flags|=2048)),w!==a&&w&&(d.child.flags|=8192),Ok(d,d.updateQueue),vo(d),null);case 4:return hi(),a===null&&vx(d.stateNode.containerInfo),vo(d),null;case 10:return Fg(d.type),vo(d),null;case 19:if(rn(Ys),k=d.memoizedState,k===null)return vo(d),null;if(C=(d.flags&128)!==0,N=k.rendering,N===null)if(C)c3(k,!1);else{if(Rs!==0||a!==null&&(a.flags&128)!==0)for(a=d.child;a!==null;){if(N=pk(a),N!==null){for(d.flags|=128,c3(k,!1),a=N.updateQueue,d.updateQueue=a,Ok(d,a),d.subtreeFlags=0,a=w,w=d.child;w!==null;)QI(w,a),w=w.sibling;return ut(Ys,Ys.current&1|2),cu&&Bg(d,k.treeForkCount),d.child}a=a.sibling}k.tail!==null&&Mc()>Pk&&(d.flags|=128,C=!0,c3(k,!1),d.lanes=4194304)}else{if(!C)if(a=pk(N),a!==null){if(d.flags|=128,C=!0,a=a.updateQueue,d.updateQueue=a,Ok(d,a),c3(k,!0),k.tail===null&&k.tailMode==="hidden"&&!N.alternate&&!cu)return vo(d),null}else 2*Mc()-k.renderingStartTime>Pk&&w!==536870912&&(d.flags|=128,C=!0,c3(k,!1),d.lanes=4194304);k.isBackwards?(N.sibling=d.child,d.child=N):(a=k.last,a!==null?a.sibling=N:d.child=N,k.last=N)}return k.tail!==null?(a=k.tail,k.rendering=a,k.tail=a.sibling,k.renderingStartTime=Mc(),a.sibling=null,w=Ys.current,ut(Ys,C?w&1|2:w&1),cu&&Bg(d,k.treeForkCount),a):(vo(d),null);case 22:case 23:return d1(d),wT(),k=d.memoizedState!==null,a!==null?a.memoizedState!==null!==k&&(d.flags|=8192):k&&(d.flags|=8192),k?(w&536870912)!==0&&(d.flags&128)===0&&(vo(d),d.subtreeFlags&6&&(d.flags|=8192)):vo(d),w=d.updateQueue,w!==null&&Ok(d,w.retryQueue),w=null,a!==null&&a.memoizedState!==null&&a.memoizedState.cachePool!==null&&(w=a.memoizedState.cachePool.pool),k=null,d.memoizedState!==null&&d.memoizedState.cachePool!==null&&(k=d.memoizedState.cachePool.pool),k!==w&&(d.flags|=2048),a!==null&&rn(Wm),null;case 24:return w=null,a!==null&&(w=a.memoizedState.cache),d.memoizedState.cache!==w&&(d.flags|=2048),Fg(jl),vo(d),null;case 25:return null;case 30:return null}throw Error(x(156,d.tag))}function uU(a,d){switch(eT(d),d.tag){case 1:return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 3:return Fg(jl),hi(),a=d.flags,(a&65536)!==0&&(a&128)===0?(d.flags=a&-65537|128,d):null;case 26:case 27:case 5:return Xr(d),null;case 31:if(d.memoizedState!==null){if(d1(d),d.alternate===null)throw Error(x(340));Zm()}return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 13:if(d1(d),a=d.memoizedState,a!==null&&a.dehydrated!==null){if(d.alternate===null)throw Error(x(340));Zm()}return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 19:return rn(Ys),null;case 4:return hi(),null;case 10:return Fg(d.type),null;case 22:case 23:return d1(d),wT(),a!==null&&rn(Wm),a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 24:return Fg(jl),null;case 25:return null;default:return null}}function VT(a,d){switch(eT(d),d.tag){case 3:Fg(jl),hi();break;case 26:case 27:case 5:Xr(d);break;case 4:hi();break;case 31:d.memoizedState!==null&&d1(d);break;case 13:d1(d);break;case 19:rn(Ys);break;case 10:Fg(d.type);break;case 22:case 23:d1(d),wT(),a!==null&&rn(Wm);break;case 24:Fg(jl)}}function Ay(a,d){try{var w=d.updateQueue,k=w!==null?w.lastEffect:null;if(k!==null){var C=k.next;w=C;do{if((w.tag&a)===a){k=void 0;var N=w.create,V=w.inst;k=N(),V.destroy=k}w=w.next}while(w!==C)}}catch(ae){io(d,d.return,ae)}}function C2(a,d,w){try{var k=d.updateQueue,C=k!==null?k.lastEffect:null;if(C!==null){var N=C.next;k=N;do{if((k.tag&a)===a){var V=k.inst,ae=V.destroy;if(ae!==void 0){V.destroy=void 0,C=d;var Ze=w,Ln=ae;try{Ln()}catch(et){io(C,Ze,et)}}}k=k.next}while(k!==N)}}catch(et){io(d,d.return,et)}}function My(a){var d=a.updateQueue;if(d!==null){var w=a.stateNode;try{d_(d,w)}catch(k){io(a,a.return,k)}}}function gL(a,d,w){w.props=r3(a.type,a.memoizedProps),w.state=a.memoizedState;try{w.componentWillUnmount()}catch(k){io(a,d,k)}}function Ty(a,d){try{var w=a.ref;if(w!==null){switch(a.tag){case 26:case 27:case 5:var k=a.stateNode;break;case 30:k=a.stateNode;break;default:k=a.stateNode}typeof w=="function"?a.refCleanup=w(k):w.current=k}}catch(C){io(a,d,C)}}function mb(a,d){var w=a.ref,k=a.refCleanup;if(w!==null)if(typeof k=="function")try{k()}catch(C){io(a,d,C)}finally{a.refCleanup=null,a=a.alternate,a!=null&&(a.refCleanup=null)}else if(typeof w=="function")try{w(null)}catch(C){io(a,d,C)}else w.current=null}function xy(a){var d=a.type,w=a.memoizedProps,k=a.stateNode;try{e:switch(d){case"button":case"input":case"select":case"textarea":w.autoFocus&&k.focus();break e;case"img":w.src?k.src=w.src:w.srcSet&&(k.srcset=w.srcSet)}}catch(C){io(a,a.return,C)}}function YT(a,d,w){try{var k=a.stateNode;TU(k,a.type,w,d),k[Ea]=d}catch(C){io(a,a.return,C)}}function wL(a){return a.tag===5||a.tag===3||a.tag===26||a.tag===27&&P2(a.type)||a.tag===4}function QT(a){e:for(;;){for(;a.sibling===null;){if(a.return===null||wL(a.return))return null;a=a.return}for(a.sibling.return=a.return,a=a.sibling;a.tag!==5&&a.tag!==6&&a.tag!==18;){if(a.tag===27&&P2(a.type)||a.flags&2||a.child===null||a.tag===4)continue e;a.child.return=a,a=a.child}if(!(a.flags&2))return a.stateNode}}function ZT(a,d,w){var k=a.tag;if(k===5||k===6)a=a.stateNode,d?(w.nodeType===9?w.body:w.nodeName==="HTML"?w.ownerDocument.body:w).insertBefore(a,d):(d=w.nodeType===9?w.body:w.nodeName==="HTML"?w.ownerDocument.body:w,d.appendChild(a),w=w._reactRootContainer,w!=null||d.onclick!==null||(d.onclick=Pg));else if(k!==4&&(k===27&&P2(a.type)&&(w=a.stateNode,d=null),a=a.child,a!==null))for(ZT(a,d,w),a=a.sibling;a!==null;)ZT(a,d,w),a=a.sibling}function u3(a,d,w){var k=a.tag;if(k===5||k===6)a=a.stateNode,d?w.insertBefore(a,d):w.appendChild(a);else if(k!==4&&(k===27&&P2(a.type)&&(w=a.stateNode),a=a.child,a!==null))for(u3(a,d,w),a=a.sibling;a!==null;)u3(a,d,w),a=a.sibling}function pL(a){var d=a.stateNode,w=a.memoizedProps;try{for(var k=a.type,C=d.attributes;C.length;)d.removeAttributeNode(C[0]);ea(d,k,w),d[Ql]=a,d[Ea]=w}catch(N){io(a,a.return,N)}}var vb=!1,Ml=!1,Cy=!1,WT=typeof WeakSet=="function"?WeakSet:Set,yf=null;function oU(a,d){if(a=a.containerInfo,Ex=kf,a=HI(a),JM(a)){if("selectionStart"in a)var w={start:a.selectionStart,end:a.selectionEnd};else e:{w=(w=a.ownerDocument)&&w.defaultView||window;var k=w.getSelection&&w.getSelection();if(k&&k.rangeCount!==0){w=k.anchorNode;var C=k.anchorOffset,N=k.focusNode;k=k.focusOffset;try{w.nodeType,N.nodeType}catch{w=null;break e}var V=0,ae=-1,Ze=-1,Ln=0,et=0,bt=a,Hn=null;n:for(;;){for(var Zn;bt!==w||C!==0&&bt.nodeType!==3||(ae=V+C),bt!==N||k!==0&&bt.nodeType!==3||(Ze=V+k),bt.nodeType===3&&(V+=bt.nodeValue.length),(Zn=bt.firstChild)!==null;)Hn=bt,bt=Zn;for(;;){if(bt===a)break n;if(Hn===w&&++Ln===C&&(ae=V),Hn===N&&++et===k&&(Ze=V),(Zn=bt.nextSibling)!==null)break;bt=Hn,Hn=bt.parentNode}bt=Zn}w=ae===-1||Ze===-1?null:{start:ae,end:Ze}}else w=null}w=w||{start:0,end:0}}else w=null;for(jx={focusedElem:a,selectionRange:w},kf=!1,yf=d;yf!==null;)if(d=yf,a=d.child,(d.subtreeFlags&1028)!==0&&a!==null)a.return=d,yf=a;else for(;yf!==null;){switch(d=yf,N=d.alternate,a=d.flags,d.tag){case 0:if((a&4)!==0&&(a=d.updateQueue,a=a!==null?a.events:null,a!==null))for(w=0;w title"))),ea(N,k,w),N[Ql]=a,El(N),k=N;break e;case"link":var V=pP("link","href",C).get(k+(w.href||""));if(V){for(var ae=0;aero&&(V=ro,ro=Tr,Tr=V);var yn=zI(ae,Tr),tn=zI(ae,ro);if(yn&&tn&&(Zn.rangeCount!==1||Zn.anchorNode!==yn.node||Zn.anchorOffset!==yn.offset||Zn.focusNode!==tn.node||Zn.focusOffset!==tn.offset)){var Dn=bt.createRange();Dn.setStart(yn.node,yn.offset),Zn.removeAllRanges(),Tr>ro?(Zn.addRange(Dn),Zn.extend(tn.node,tn.offset)):(Dn.setEnd(tn.node,tn.offset),Zn.addRange(Dn))}}}}for(bt=[],Zn=ae;Zn=Zn.parentNode;)Zn.nodeType===1&&bt.push({element:Zn,left:Zn.scrollLeft,top:Zn.scrollTop});for(typeof ae.focus=="function"&&ae.focus(),ae=0;aew?32:w,Ae.T=null,w=ux,ux=null;var N=I2,V=Vg;if(Zl=0,D5=I2=null,Vg=0,(zu&6)!==0)throw Error(x(331));var ae=zu;if(zu|=4,TL(N.current),SL(N,N.current,V,w),zu=ae,Ly(0,!1),eh&&typeof eh.onPostCommitFiberRoot=="function")try{eh.onPostCommitFiberRoot(Rm,N)}catch{}return!0}finally{Je.p=C,Ae.T=k,qL(a,d)}}function XL(a,d,w){d=td(w,d),d=BT(a.stateNode,d,2),a=M2(a,d,2),a!==null&&(zm(a,2),yb(a))}function io(a,d,w){if(a.tag===3)XL(a,a,w);else for(;d!==null;){if(d.tag===3){XL(d,a,w);break}else if(d.tag===1){var k=d.stateNode;if(typeof d.type.getDerivedStateFromError=="function"||typeof k.componentDidCatch=="function"&&(D2===null||!D2.has(k))){a=td(w,a),w=eL(2),k=M2(d,w,2),k!==null&&(nL(w,k,d,a),zm(k,2),yb(k));break}}d=d.return}}function fx(a,d,w){var k=a.pingCache;if(k===null){k=a.pingCache=new fU;var C=new Set;k.set(d,C)}else C=k.get(d),C===void 0&&(C=new Set,k.set(d,C));C.has(w)||(tx=!0,C.add(w),a=gU.bind(null,a,d,w),d.then(a,a))}function gU(a,d,w){var k=a.pingCache;k!==null&&k.delete(d),a.pingedLanes|=a.suspendedLanes&w,a.warmLanes&=~w,Do===a&&(nu&w)===w&&(Rs===4||Rs===3&&(nu&62914560)===nu&&300>Mc()-Lk?(zu&2)===0&&I5(a,0):ix|=w,N5===nu&&(N5=0)),yb(a)}function KL(a,d){d===0&&(d=z7()),a=Ym(a,d),a!==null&&(zm(a,d),yb(a))}function wU(a){var d=a.memoizedState,w=0;d!==null&&(w=d.retryLane),KL(a,w)}function pU(a,d){var w=0;switch(a.tag){case 31:case 13:var k=a.stateNode,C=a.memoizedState;C!==null&&(w=C.retryLane);break;case 19:k=a.stateNode;break;case 22:k=a.stateNode._retryCache;break;default:throw Error(x(314))}k!==null&&k.delete(d),KL(a,w)}function mU(a,d){return Wa(a,d)}var Hk=null,L5=null,ax=!1,Jk=!1,hx=!1,L2=0;function yb(a){a!==L5&&a.next===null&&(L5===null?Hk=L5=a:L5=L5.next=a),Jk=!0,ax||(ax=!0,bx())}function Ly(a,d){if(!hx&&Jk){hx=!0;do for(var w=!1,k=Hk;k!==null;){if(a!==0){var C=k.pendingLanes;if(C===0)var N=0;else{var V=k.suspendedLanes,ae=k.pingedLanes;N=(1<<31-nh(42|a)+1)-1,N&=C&~(V&~ae),N=N&201326741?N&201326741|1:N?N|2:0}N!==0&&(w=!0,QL(k,N))}else N=nu,N=t5(k,k===Do?N:0,k.cancelPendingCommit!==null||k.timeoutHandle!==-1),(N&3)===0||Bm(k,N)||(w=!0,QL(k,N));k=k.next}while(w);hx=!1}}function vU(){VL()}function VL(){Jk=ax=!1;var a=0;L2!==0&&CU()&&(a=L2);for(var d=Mc(),w=null,k=Hk;k!==null;){var C=k.next,N=dx(k,d);N===0?(k.next=null,w===null?Hk=C:w.next=C,C===null&&(L5=w)):(w=k,(a!==0||(N&3)!==0)&&(Jk=!0)),k=C}Zl!==0&&Zl!==5||Ly(a),L2!==0&&(L2=0)}function dx(a,d){for(var w=a.suspendedLanes,k=a.pingedLanes,C=a.expirationTimes,N=a.pendingLanes&-62914561;0ae)break;var et=Ze.transferSize,bt=Ze.initiatorType;et&&cP(bt)&&(Ze=Ze.responseEnd,V+=et*(Ze"u"?null:document;function dP(a,d,w){var k=P5;if(k&&typeof d=="string"&&d){var C=Ch(d);C='link[rel="'+a+'"][href="'+C+'"]',typeof w=="string"&&(C+='[crossorigin="'+w+'"]'),Ox.has(C)||(Ox.add(C),a={rel:a,crossOrigin:w,href:d},k.querySelector(C)===null&&(d=k.createElement("link"),ea(d,"link",a),El(d),k.head.appendChild(d)))}}function RU(a){u0.D(a),dP("dns-prefetch",a,null)}function BU(a,d){u0.C(a,d),dP("preconnect",a,d)}function Nx(a,d,w){u0.L(a,d,w);var k=P5;if(k&&a&&d){var C='link[rel="preload"][as="'+Ch(d)+'"]';d==="image"&&w&&w.imageSrcSet?(C+='[imagesrcset="'+Ch(w.imageSrcSet)+'"]',typeof w.imageSizes=="string"&&(C+='[imagesizes="'+Ch(w.imageSizes)+'"]')):C+='[href="'+Ch(a)+'"]';var N=C;switch(d){case"style":N=$5(a);break;case"script":N=R5(a)}m1.has(N)||(a=Q({rel:"preload",href:d==="image"&&w&&w.imageSrcSet?void 0:a,as:d},w),m1.set(N,a),k.querySelector(C)!==null||d==="style"&&k.querySelector(l3(N))||d==="script"&&k.querySelector(zy(N))||(d=k.createElement("link"),ea(d,"link",a),El(d),k.head.appendChild(d)))}}function zU(a,d){u0.m(a,d);var w=P5;if(w&&a){var k=d&&typeof d.as=="string"?d.as:"script",C='link[rel="modulepreload"][as="'+Ch(k)+'"][href="'+Ch(a)+'"]',N=C;switch(k){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":N=R5(a)}if(!m1.has(N)&&(a=Q({rel:"modulepreload",href:a},d),m1.set(N,a),w.querySelector(C)===null)){switch(k){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(w.querySelector(zy(N)))return}k=w.createElement("link"),ea(k,"link",a),El(k),w.head.appendChild(k)}}}function FU(a,d,w){u0.S(a,d,w);var k=P5;if(k&&a){var C=b2(k).hoistableStyles,N=$5(a);d=d||"default";var V=C.get(N);if(!V){var ae={loading:0,preload:null};if(V=k.querySelector(l3(N)))ae.loading=5;else{a=Q({rel:"stylesheet",href:a,"data-precedence":d},w),(w=m1.get(N))&&_x(a,w);var Ze=V=k.createElement("link");El(Ze),ea(Ze,"link",a),Ze._p=new Promise(function(Ln,et){Ze.onload=Ln,Ze.onerror=et}),Ze.addEventListener("load",function(){ae.loading|=1}),Ze.addEventListener("error",function(){ae.loading|=2}),ae.loading|=4,Vk(V,d,k)}V={type:"stylesheet",instance:V,count:1,state:ae},C.set(N,V)}}}function HU(a,d){u0.X(a,d);var w=P5;if(w&&a){var k=b2(w).hoistableScripts,C=R5(a),N=k.get(C);N||(N=w.querySelector(zy(C)),N||(a=Q({src:a,async:!0},d),(d=m1.get(C))&&Lx(a,d),N=w.createElement("script"),El(N),ea(N,"link",a),w.head.appendChild(N)),N={type:"script",instance:N,count:1,state:null},k.set(C,N))}}function Dx(a,d){u0.M(a,d);var w=P5;if(w&&a){var k=b2(w).hoistableScripts,C=R5(a),N=k.get(C);N||(N=w.querySelector(zy(C)),N||(a=Q({src:a,async:!0,type:"module"},d),(d=m1.get(C))&&Lx(a,d),N=w.createElement("script"),El(N),ea(N,"link",a),w.head.appendChild(N)),N={type:"script",instance:N,count:1,state:null},k.set(C,N))}}function bP(a,d,w,k){var C=(C=li.current)?Kk(C):null;if(!C)throw Error(x(446));switch(a){case"meta":case"title":return null;case"style":return typeof w.precedence=="string"&&typeof w.href=="string"?(d=$5(w.href),w=b2(C).hoistableStyles,k=w.get(d),k||(k={type:"style",instance:null,count:0,state:null},w.set(d,k)),k):{type:"void",instance:null,count:0,state:null};case"link":if(w.rel==="stylesheet"&&typeof w.href=="string"&&typeof w.precedence=="string"){a=$5(w.href);var N=b2(C).hoistableStyles,V=N.get(a);if(V||(C=C.ownerDocument||C,V={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},N.set(a,V),(N=C.querySelector(l3(a)))&&!N._p&&(V.instance=N,V.state.loading=5),m1.has(a)||(w={rel:"preload",as:"style",href:w.href,crossOrigin:w.crossOrigin,integrity:w.integrity,media:w.media,hrefLang:w.hrefLang,referrerPolicy:w.referrerPolicy},m1.set(a,w),N||Ix(C,a,w,V.state))),d&&k===null)throw Error(x(528,""));return V}if(d&&k!==null)throw Error(x(529,""));return null;case"script":return d=w.async,w=w.src,typeof w=="string"&&d&&typeof d!="function"&&typeof d!="symbol"?(d=R5(w),w=b2(C).hoistableScripts,k=w.get(d),k||(k={type:"script",instance:null,count:0,state:null},w.set(d,k)),k):{type:"void",instance:null,count:0,state:null};default:throw Error(x(444,a))}}function $5(a){return'href="'+Ch(a)+'"'}function l3(a){return'link[rel="stylesheet"]['+a+"]"}function gP(a){return Q({},a,{"data-precedence":a.precedence,precedence:null})}function Ix(a,d,w,k){a.querySelector('link[rel="preload"][as="style"]['+d+"]")?k.loading=1:(d=a.createElement("link"),k.preload=d,d.addEventListener("load",function(){return k.loading|=1}),d.addEventListener("error",function(){return k.loading|=2}),ea(d,"link",w),El(d),a.head.appendChild(d))}function R5(a){return'[src="'+Ch(a)+'"]'}function zy(a){return"script[async]"+a}function wP(a,d,w){if(d.count++,d.instance===null)switch(d.type){case"style":var k=a.querySelector('style[data-href~="'+Ch(w.href)+'"]');if(k)return d.instance=k,El(k),k;var C=Q({},w,{"data-href":w.href,"data-precedence":w.precedence,href:null,precedence:null});return k=(a.ownerDocument||a).createElement("style"),El(k),ea(k,"style",C),Vk(k,w.precedence,a),d.instance=k;case"stylesheet":C=$5(w.href);var N=a.querySelector(l3(C));if(N)return d.state.loading|=4,d.instance=N,El(N),N;k=gP(w),(C=m1.get(C))&&_x(k,C),N=(a.ownerDocument||a).createElement("link"),El(N);var V=N;return V._p=new Promise(function(ae,Ze){V.onload=ae,V.onerror=Ze}),ea(N,"link",k),d.state.loading|=4,Vk(N,w.precedence,a),d.instance=N;case"script":return N=R5(w.src),(C=a.querySelector(zy(N)))?(d.instance=C,El(C),C):(k=w,(C=m1.get(N))&&(k=Q({},w),Lx(k,C)),a=a.ownerDocument||a,C=a.createElement("script"),El(C),ea(C,"link",k),a.head.appendChild(C),d.instance=C);case"void":return null;default:throw Error(x(443,d.type))}else d.type==="stylesheet"&&(d.state.loading&4)===0&&(k=d.instance,d.state.loading|=4,Vk(k,w.precedence,a));return d.instance}function Vk(a,d,w){for(var k=w.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),C=k.length?k[k.length-1]:null,N=C,V=0;V title"):null)}function JU(a,d,w){if(w===1||d.itemProp!=null)return!1;switch(a){case"meta":case"title":return!0;case"style":if(typeof d.precedence!="string"||typeof d.href!="string"||d.href==="")break;return!0;case"link":if(typeof d.rel!="string"||typeof d.href!="string"||d.href===""||d.onLoad||d.onError)break;return d.rel==="stylesheet"?(a=d.disabled,typeof d.precedence=="string"&&a==null):!0;case"script":if(d.async&&typeof d.async!="function"&&typeof d.async!="symbol"&&!d.onLoad&&!d.onError&&d.src&&typeof d.src=="string")return!0}return!1}function vP(a){return!(a.type==="stylesheet"&&(a.state.loading&3)===0)}function GU(a,d,w,k){if(w.type==="stylesheet"&&(typeof k.media!="string"||matchMedia(k.media).matches!==!1)&&(w.state.loading&4)===0){if(w.instance===null){var C=$5(k.href),N=d.querySelector(l3(C));if(N){d=N._p,d!==null&&typeof d=="object"&&typeof d.then=="function"&&(a.count++,a=Fy.bind(a),d.then(a,a)),w.state.loading|=4,w.instance=N,El(N);return}N=d.ownerDocument||d,k=gP(k),(C=m1.get(C))&&_x(k,C),N=N.createElement("link"),El(N);var V=N;V._p=new Promise(function(ae,Ze){V.onload=ae,V.onerror=Ze}),ea(N,"link",k),w.instance=N}a.stylesheets===null&&(a.stylesheets=new Map),a.stylesheets.set(w,d),(d=w.state.preload)&&(w.state.loading&3)===0&&(a.count++,w=Fy.bind(a),d.addEventListener("load",w),d.addEventListener("error",w))}}var Px=0;function qU(a,d){return a.stylesheets&&a.count===0&&f3(a,a.stylesheets),0Px?50:800)+d);return a.unsuspend=w,function(){a.unsuspend=null,clearTimeout(k),clearTimeout(C)}}:null}function Fy(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)f3(this,this.stylesheets);else if(this.unsuspend){var a=this.unsuspend;this.unsuspend=null,a()}}}var Hy=null;function f3(a,d){a.stylesheets=null,a.unsuspend!==null&&(a.count++,Hy=new Map,d.forEach(Jy,a),Hy=null,Fy.call(a))}function Jy(a,d){if(!(d.state.loading&4)){var w=Hy.get(a);if(w)var k=w.get(null);else{w=new Map,Hy.set(a,w);for(var C=a.querySelectorAll("link[data-precedence],style[data-precedence]"),N=0;N"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(g)}catch(j){console.error(j)}}return g(),k7e.exports=ZBn(),k7e.exports}var ezn=WBn();function ka(g){if(typeof g=="string"||typeof g=="number")return""+g;let j="";if(Array.isArray(g))for(let A=0,x;A{}};function Eue(){for(var g=0,j=arguments.length,A={},x;g=0&&(x=A.slice(D+1),A=A.slice(0,D)),A&&!j.hasOwnProperty(A))throw new Error("unknown type: "+A);return{type:A,name:x}})}cue.prototype=Eue.prototype={constructor:cue,on:function(g,j){var A=this._,x=tzn(g+"",A),D,$=-1,E=x.length;if(arguments.length<2){for(;++$0)for(var A=new Array(D),x=0,D,$;x=0&&(j=g.slice(0,A))!=="xmlns"&&(g=g.slice(A+1)),$hn.hasOwnProperty(j)?{space:$hn[j],local:g}:g}function rzn(g){return function(){var j=this.ownerDocument,A=this.namespaceURI;return A===G7e&&j.documentElement.namespaceURI===G7e?j.createElement(g):j.createElementNS(A,g)}}function czn(g){return function(){return this.ownerDocument.createElementNS(g.space,g.local)}}function ndn(g){var j=jue(g);return(j.local?czn:rzn)(j)}function uzn(){}function cke(g){return g==null?uzn:function(){return this.querySelector(g)}}function ozn(g){typeof g!="function"&&(g=cke(g));for(var j=this._groups,A=j.length,x=new Array(A),D=0;D=de&&(de=ze+1);!(an=De[de])&&++de=0;)(E=x[D])&&($&&E.compareDocumentPosition($)^4&&$.parentNode.insertBefore(E,$),$=E);return this}function Dzn(g){g||(g=Izn);function j(Q,Z){return Q&&Z?g(Q.__data__,Z.__data__):!Q-!Z}for(var A=this._groups,x=A.length,D=new Array(x),$=0;$j?1:g>=j?0:NaN}function _zn(){var g=arguments[0];return arguments[0]=this,g.apply(null,arguments),this}function Lzn(){return Array.from(this)}function Pzn(){for(var g=this._groups,j=0,A=g.length;j1?this.each((j==null?Xzn:typeof j=="function"?Vzn:Kzn)(g,j,A??"")):lI(this.node(),g)}function lI(g,j){return g.style.getPropertyValue(j)||udn(g).getComputedStyle(g,null).getPropertyValue(j)}function Qzn(g){return function(){delete this[g]}}function Zzn(g,j){return function(){this[g]=j}}function Wzn(g,j){return function(){var A=j.apply(this,arguments);A==null?delete this[g]:this[g]=A}}function eFn(g,j){return arguments.length>1?this.each((j==null?Qzn:typeof j=="function"?Wzn:Zzn)(g,j)):this.node()[g]}function odn(g){return g.trim().split(/^|\s+/)}function uke(g){return g.classList||new sdn(g)}function sdn(g){this._node=g,this._names=odn(g.getAttribute("class")||"")}sdn.prototype={add:function(g){var j=this._names.indexOf(g);j<0&&(this._names.push(g),this._node.setAttribute("class",this._names.join(" ")))},remove:function(g){var j=this._names.indexOf(g);j>=0&&(this._names.splice(j,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(g){return this._names.indexOf(g)>=0}};function ldn(g,j){for(var A=uke(g),x=-1,D=j.length;++x=0&&(A=j.slice(x+1),j=j.slice(0,x)),{type:j,name:A}})}function xFn(g){return function(){var j=this.__on;if(j){for(var A=0,x=-1,D=j.length,$;A()=>g;function q7e(g,{sourceEvent:j,subject:A,target:x,identifier:D,active:$,x:E,y:H,dx:U,dy:J,dispatch:te}){Object.defineProperties(this,{type:{value:g,enumerable:!0,configurable:!0},sourceEvent:{value:j,enumerable:!0,configurable:!0},subject:{value:A,enumerable:!0,configurable:!0},target:{value:x,enumerable:!0,configurable:!0},identifier:{value:D,enumerable:!0,configurable:!0},active:{value:$,enumerable:!0,configurable:!0},x:{value:E,enumerable:!0,configurable:!0},y:{value:H,enumerable:!0,configurable:!0},dx:{value:U,enumerable:!0,configurable:!0},dy:{value:J,enumerable:!0,configurable:!0},_:{value:te}})}q7e.prototype.on=function(){var g=this._.on.apply(this._,arguments);return g===this._?this:g};function RFn(g){return!g.ctrlKey&&!g.button}function BFn(){return this.parentNode}function zFn(g,j){return j??{x:g.x,y:g.y}}function FFn(){return navigator.maxTouchPoints||"ontouchstart"in this}function gdn(){var g=RFn,j=BFn,A=zFn,x=FFn,D={},$=Eue("start","drag","end"),E=0,H,U,J,te,Q=0;function Z(Le){Le.on("mousedown.drag",le).filter(x).on("touchstart.drag",De).on("touchmove.drag",Se,$Fn).on("touchend.drag touchcancel.drag",ze).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function le(Le,an){if(!(te||!g.call(this,Le,an))){var ln=de(this,j.call(this,Le,an),Le,an,"mouse");ln&&(Cg(Le.view).on("mousemove.drag",be,$G).on("mouseup.drag",ne,$G),ddn(Le.view),A7e(Le),J=!1,H=Le.clientX,U=Le.clientY,ln("start",Le))}}function be(Le){if(oI(Le),!J){var an=Le.clientX-H,ln=Le.clientY-U;J=an*an+ln*ln>Q}D.mouse("drag",Le)}function ne(Le){Cg(Le.view).on("mousemove.drag mouseup.drag",null),bdn(Le.view,J),oI(Le),D.mouse("end",Le)}function De(Le,an){if(g.call(this,Le,an)){var ln=Le.changedTouches,on=j.call(this,Le,an),Mn=ln.length,Nn,Ft;for(Nn=0;Nn>8&15|j>>4&240,j>>4&15|j&240,(j&15)<<4|j&15,1):A===8?Uce(j>>24&255,j>>16&255,j>>8&255,(j&255)/255):A===4?Uce(j>>12&15|j>>8&240,j>>8&15|j>>4&240,j>>4&15|j&240,((j&15)<<4|j&15)/255):null):(j=JFn.exec(g))?new db(j[1],j[2],j[3],1):(j=GFn.exec(g))?new db(j[1]*255/100,j[2]*255/100,j[3]*255/100,1):(j=qFn.exec(g))?Uce(j[1],j[2],j[3],j[4]):(j=UFn.exec(g))?Uce(j[1]*255/100,j[2]*255/100,j[3]*255/100,j[4]):(j=XFn.exec(g))?Ghn(j[1],j[2]/100,j[3]/100,1):(j=KFn.exec(g))?Ghn(j[1],j[2]/100,j[3]/100,j[4]):Rhn.hasOwnProperty(g)?Fhn(Rhn[g]):g==="transparent"?new db(NaN,NaN,NaN,0):null}function Fhn(g){return new db(g>>16&255,g>>8&255,g&255,1)}function Uce(g,j,A,x){return x<=0&&(g=j=A=NaN),new db(g,j,A,x)}function QFn(g){return g instanceof VG||(g=dM(g)),g?(g=g.rgb(),new db(g.r,g.g,g.b,g.opacity)):new db}function U7e(g,j,A,x){return arguments.length===1?QFn(g):new db(g,j,A,x??1)}function db(g,j,A,x){this.r=+g,this.g=+j,this.b=+A,this.opacity=+x}oke(db,U7e,wdn(VG,{brighter(g){return g=g==null?aue:Math.pow(aue,g),new db(this.r*g,this.g*g,this.b*g,this.opacity)},darker(g){return g=g==null?RG:Math.pow(RG,g),new db(this.r*g,this.g*g,this.b*g,this.opacity)},rgb(){return this},clamp(){return new db(aM(this.r),aM(this.g),aM(this.b),hue(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Hhn,formatHex:Hhn,formatHex8:ZFn,formatRgb:Jhn,toString:Jhn}));function Hhn(){return`#${fM(this.r)}${fM(this.g)}${fM(this.b)}`}function ZFn(){return`#${fM(this.r)}${fM(this.g)}${fM(this.b)}${fM((isNaN(this.opacity)?1:this.opacity)*255)}`}function Jhn(){const g=hue(this.opacity);return`${g===1?"rgb(":"rgba("}${aM(this.r)}, ${aM(this.g)}, ${aM(this.b)}${g===1?")":`, ${g})`}`}function hue(g){return isNaN(g)?1:Math.max(0,Math.min(1,g))}function aM(g){return Math.max(0,Math.min(255,Math.round(g)||0))}function fM(g){return g=aM(g),(g<16?"0":"")+g.toString(16)}function Ghn(g,j,A,x){return x<=0?g=j=A=NaN:A<=0||A>=1?g=j=NaN:j<=0&&(g=NaN),new Im(g,j,A,x)}function pdn(g){if(g instanceof Im)return new Im(g.h,g.s,g.l,g.opacity);if(g instanceof VG||(g=dM(g)),!g)return new Im;if(g instanceof Im)return g;g=g.rgb();var j=g.r/255,A=g.g/255,x=g.b/255,D=Math.min(j,A,x),$=Math.max(j,A,x),E=NaN,H=$-D,U=($+D)/2;return H?(j===$?E=(A-x)/H+(A0&&U<1?0:E,new Im(E,H,U,g.opacity)}function WFn(g,j,A,x){return arguments.length===1?pdn(g):new Im(g,j,A,x??1)}function Im(g,j,A,x){this.h=+g,this.s=+j,this.l=+A,this.opacity=+x}oke(Im,WFn,wdn(VG,{brighter(g){return g=g==null?aue:Math.pow(aue,g),new Im(this.h,this.s,this.l*g,this.opacity)},darker(g){return g=g==null?RG:Math.pow(RG,g),new Im(this.h,this.s,this.l*g,this.opacity)},rgb(){var g=this.h%360+(this.h<0)*360,j=isNaN(g)||isNaN(this.s)?0:this.s,A=this.l,x=A+(A<.5?A:1-A)*j,D=2*A-x;return new db(M7e(g>=240?g-240:g+120,D,x),M7e(g,D,x),M7e(g<120?g+240:g-120,D,x),this.opacity)},clamp(){return new Im(qhn(this.h),Xce(this.s),Xce(this.l),hue(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const g=hue(this.opacity);return`${g===1?"hsl(":"hsla("}${qhn(this.h)}, ${Xce(this.s)*100}%, ${Xce(this.l)*100}%${g===1?")":`, ${g})`}`}}));function qhn(g){return g=(g||0)%360,g<0?g+360:g}function Xce(g){return Math.max(0,Math.min(1,g||0))}function M7e(g,j,A){return(g<60?j+(A-j)*g/60:g<180?A:g<240?j+(A-j)*(240-g)/60:j)*255}const ske=g=>()=>g;function eHn(g,j){return function(A){return g+A*j}}function nHn(g,j,A){return g=Math.pow(g,A),j=Math.pow(j,A)-g,A=1/A,function(x){return Math.pow(g+x*j,A)}}function tHn(g){return(g=+g)==1?mdn:function(j,A){return A-j?nHn(j,A,g):ske(isNaN(j)?A:j)}}function mdn(g,j){var A=j-g;return A?eHn(g,A):ske(isNaN(g)?j:g)}const due=(function g(j){var A=tHn(j);function x(D,$){var E=A((D=U7e(D)).r,($=U7e($)).r),H=A(D.g,$.g),U=A(D.b,$.b),J=mdn(D.opacity,$.opacity);return function(te){return D.r=E(te),D.g=H(te),D.b=U(te),D.opacity=J(te),D+""}}return x.gamma=g,x})(1);function iHn(g,j){j||(j=[]);var A=g?Math.min(j.length,g.length):0,x=j.slice(),D;return function($){for(D=0;DA&&($=j.slice(A,$),H[E]?H[E]+=$:H[++E]=$),(x=x[0])===(D=D[0])?H[E]?H[E]+=D:H[++E]=D:(H[++E]=null,U.push({i:E,x:Xv(x,D)})),A=T7e.lastIndex;return A180?te+=360:te-J>180&&(J+=360),Z.push({i:Q.push(D(Q)+"rotate(",null,x)-2,x:Xv(J,te)})):te&&Q.push(D(Q)+"rotate("+te+x)}function H(J,te,Q,Z){J!==te?Z.push({i:Q.push(D(Q)+"skewX(",null,x)-2,x:Xv(J,te)}):te&&Q.push(D(Q)+"skewX("+te+x)}function U(J,te,Q,Z,le,be){if(J!==Q||te!==Z){var ne=le.push(D(le)+"scale(",null,",",null,")");be.push({i:ne-4,x:Xv(J,Q)},{i:ne-2,x:Xv(te,Z)})}else(Q!==1||Z!==1)&&le.push(D(le)+"scale("+Q+","+Z+")")}return function(J,te){var Q=[],Z=[];return J=g(J),te=g(te),$(J.translateX,J.translateY,te.translateX,te.translateY,Q,Z),E(J.rotate,te.rotate,Q,Z),H(J.skewX,te.skewX,Q,Z),U(J.scaleX,J.scaleY,te.scaleX,te.scaleY,Q,Z),J=te=null,function(le){for(var be=-1,ne=Z.length,De;++be=0&&g._call.call(void 0,j),g=g._next;--fI}function Khn(){bM=(gue=zG.now())+Sue,fI=DG=0;try{mHn()}finally{fI=0,yHn(),bM=0}}function vHn(){var g=zG.now(),j=g-gue;j>Edn&&(Sue-=j,gue=g)}function yHn(){for(var g,j=bue,A,x=1/0;j;)j._call?(x>j._time&&(x=j._time),g=j,j=j._next):(A=j._next,j._next=null,j=g?g._next=A:bue=A);IG=g,V7e(x)}function V7e(g){if(!fI){DG&&(DG=clearTimeout(DG));var j=g-bM;j>24?(g<1/0&&(DG=setTimeout(Khn,g-zG.now()-Sue)),OG&&(OG=clearInterval(OG))):(OG||(gue=zG.now(),OG=setInterval(vHn,Edn)),fI=1,jdn(Khn))}}function Vhn(g,j,A){var x=new wue;return j=j==null?0:+j,x.restart(D=>{x.stop(),g(D+j)},j,A),x}var kHn=Eue("start","end","cancel","interrupt"),EHn=[],Adn=0,Yhn=1,Y7e=2,oue=3,Qhn=4,Q7e=5,sue=6;function Aue(g,j,A,x,D,$){var E=g.__transition;if(!E)g.__transition={};else if(A in E)return;jHn(g,A,{name:j,index:x,group:D,on:kHn,tween:EHn,time:$.time,delay:$.delay,duration:$.duration,ease:$.ease,timer:null,state:Adn})}function fke(g,j){var A=Pm(g,j);if(A.state>Adn)throw new Error("too late; already scheduled");return A}function Qv(g,j){var A=Pm(g,j);if(A.state>oue)throw new Error("too late; already running");return A}function Pm(g,j){var A=g.__transition;if(!A||!(A=A[j]))throw new Error("transition not found");return A}function jHn(g,j,A){var x=g.__transition,D;x[j]=A,A.timer=Sdn($,0,A.time);function $(J){A.state=Yhn,A.timer.restart(E,A.delay,A.time),A.delay<=J&&E(J-A.delay)}function E(J){var te,Q,Z,le;if(A.state!==Yhn)return U();for(te in x)if(le=x[te],le.name===A.name){if(le.state===oue)return Vhn(E);le.state===Qhn?(le.state=sue,le.timer.stop(),le.on.call("interrupt",g,g.__data__,le.index,le.group),delete x[te]):+teY7e&&x.state=0&&(j=j.slice(0,A)),!j||j==="start"})}function WHn(g,j,A){var x,D,$=ZHn(j)?fke:Qv;return function(){var E=$(this,g),H=E.on;H!==x&&(D=(x=H).copy()).on(j,A),E.on=D}}function eJn(g,j){var A=this._id;return arguments.length<2?Pm(this.node(),A).on.on(g):this.each(WHn(A,g,j))}function nJn(g){return function(){var j=this.parentNode;for(var A in this.__transition)if(+A!==g)return;j&&j.removeChild(this)}}function tJn(){return this.on("end.remove",nJn(this._id))}function iJn(g){var j=this._name,A=this._id;typeof g!="function"&&(g=cke(g));for(var x=this._groups,D=x.length,$=new Array(D),E=0;E()=>g;function xJn(g,{sourceEvent:j,target:A,transform:x,dispatch:D}){Object.defineProperties(this,{type:{value:g,enumerable:!0,configurable:!0},sourceEvent:{value:j,enumerable:!0,configurable:!0},target:{value:A,enumerable:!0,configurable:!0},transform:{value:x,enumerable:!0,configurable:!0},_:{value:D}})}function J6(g,j,A){this.k=g,this.x=j,this.y=A}J6.prototype={constructor:J6,scale:function(g){return g===1?this:new J6(this.k*g,this.x,this.y)},translate:function(g,j){return g===0&j===0?this:new J6(this.k,this.x+this.k*g,this.y+this.k*j)},apply:function(g){return[g[0]*this.k+this.x,g[1]*this.k+this.y]},applyX:function(g){return g*this.k+this.x},applyY:function(g){return g*this.k+this.y},invert:function(g){return[(g[0]-this.x)/this.k,(g[1]-this.y)/this.k]},invertX:function(g){return(g-this.x)/this.k},invertY:function(g){return(g-this.y)/this.k},rescaleX:function(g){return g.copy().domain(g.range().map(this.invertX,this).map(g.invert,g))},rescaleY:function(g){return g.copy().domain(g.range().map(this.invertY,this).map(g.invert,g))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var Mue=new J6(1,0,0);Cdn.prototype=J6.prototype;function Cdn(g){for(;!g.__zoom;)if(!(g=g.parentNode))return Mue;return g.__zoom}function x7e(g){g.stopImmediatePropagation()}function NG(g){g.preventDefault(),g.stopImmediatePropagation()}function CJn(g){return(!g.ctrlKey||g.type==="wheel")&&!g.button}function OJn(){var g=this;return g instanceof SVGElement?(g=g.ownerSVGElement||g,g.hasAttribute("viewBox")?(g=g.viewBox.baseVal,[[g.x,g.y],[g.x+g.width,g.y+g.height]]):[[0,0],[g.width.baseVal.value,g.height.baseVal.value]]):[[0,0],[g.clientWidth,g.clientHeight]]}function Zhn(){return this.__zoom||Mue}function NJn(g){return-g.deltaY*(g.deltaMode===1?.05:g.deltaMode?1:.002)*(g.ctrlKey?10:1)}function DJn(){return navigator.maxTouchPoints||"ontouchstart"in this}function IJn(g,j,A){var x=g.invertX(j[0][0])-A[0][0],D=g.invertX(j[1][0])-A[1][0],$=g.invertY(j[0][1])-A[0][1],E=g.invertY(j[1][1])-A[1][1];return g.translate(D>x?(x+D)/2:Math.min(0,x)||Math.max(0,D),E>$?($+E)/2:Math.min(0,$)||Math.max(0,E))}function Odn(){var g=CJn,j=OJn,A=IJn,x=NJn,D=DJn,$=[0,1/0],E=[[-1/0,-1/0],[1/0,1/0]],H=250,U=uue,J=Eue("start","zoom","end"),te,Q,Z,le=500,be=150,ne=0,De=10;function Se(Fe){Fe.property("__zoom",Zhn).on("wheel.zoom",Mn,{passive:!1}).on("mousedown.zoom",Nn).on("dblclick.zoom",Ft).filter(D).on("touchstart.zoom",In).on("touchmove.zoom",nt).on("touchend.zoom touchcancel.zoom",Y).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}Se.transform=function(Fe,gn,Ae,Je){var un=Fe.selection?Fe.selection():Fe;un.property("__zoom",Zhn),Fe!==un?an(Fe,gn,Ae,Je):un.interrupt().each(function(){ln(this,arguments).event(Je).start().zoom(null,typeof gn=="function"?gn.apply(this,arguments):gn).end()})},Se.scaleBy=function(Fe,gn,Ae,Je){Se.scaleTo(Fe,function(){var un=this.__zoom.k,jn=typeof gn=="function"?gn.apply(this,arguments):gn;return un*jn},Ae,Je)},Se.scaleTo=function(Fe,gn,Ae,Je){Se.transform(Fe,function(){var un=j.apply(this,arguments),jn=this.__zoom,En=Ae==null?Le(un):typeof Ae=="function"?Ae.apply(this,arguments):Ae,Me=jn.invert(En),rn=typeof gn=="function"?gn.apply(this,arguments):gn;return A(de(ze(jn,rn),En,Me),un,E)},Ae,Je)},Se.translateBy=function(Fe,gn,Ae,Je){Se.transform(Fe,function(){return A(this.__zoom.translate(typeof gn=="function"?gn.apply(this,arguments):gn,typeof Ae=="function"?Ae.apply(this,arguments):Ae),j.apply(this,arguments),E)},null,Je)},Se.translateTo=function(Fe,gn,Ae,Je,un){Se.transform(Fe,function(){var jn=j.apply(this,arguments),En=this.__zoom,Me=Je==null?Le(jn):typeof Je=="function"?Je.apply(this,arguments):Je;return A(Mue.translate(Me[0],Me[1]).scale(En.k).translate(typeof gn=="function"?-gn.apply(this,arguments):-gn,typeof Ae=="function"?-Ae.apply(this,arguments):-Ae),jn,E)},Je,un)};function ze(Fe,gn){return gn=Math.max($[0],Math.min($[1],gn)),gn===Fe.k?Fe:new J6(gn,Fe.x,Fe.y)}function de(Fe,gn,Ae){var Je=gn[0]-Ae[0]*Fe.k,un=gn[1]-Ae[1]*Fe.k;return Je===Fe.x&&un===Fe.y?Fe:new J6(Fe.k,Je,un)}function Le(Fe){return[(+Fe[0][0]+ +Fe[1][0])/2,(+Fe[0][1]+ +Fe[1][1])/2]}function an(Fe,gn,Ae,Je){Fe.on("start.zoom",function(){ln(this,arguments).event(Je).start()}).on("interrupt.zoom end.zoom",function(){ln(this,arguments).event(Je).end()}).tween("zoom",function(){var un=this,jn=arguments,En=ln(un,jn).event(Je),Me=j.apply(un,jn),rn=Ae==null?Le(Me):typeof Ae=="function"?Ae.apply(un,jn):Ae,ut=Math.max(Me[1][0]-Me[0][0],Me[1][1]-Me[0][1]),st=un.__zoom,Kt=typeof gn=="function"?gn.apply(un,jn):gn,li=U(st.invert(rn).concat(ut/st.k),Kt.invert(rn).concat(ut/Kt.k));return function(oi){if(oi===1)oi=Kt;else{var Jt=li(oi),hi=ut/Jt[2];oi=new J6(hi,rn[0]-Jt[0]*hi,rn[1]-Jt[1]*hi)}En.zoom(null,oi)}})}function ln(Fe,gn,Ae){return!Ae&&Fe.__zooming||new on(Fe,gn)}function on(Fe,gn){this.that=Fe,this.args=gn,this.active=0,this.sourceEvent=null,this.extent=j.apply(Fe,gn),this.taps=0}on.prototype={event:function(Fe){return Fe&&(this.sourceEvent=Fe),this},start:function(){return++this.active===1&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(Fe,gn){return this.mouse&&Fe!=="mouse"&&(this.mouse[1]=gn.invert(this.mouse[0])),this.touch0&&Fe!=="touch"&&(this.touch0[1]=gn.invert(this.touch0[0])),this.touch1&&Fe!=="touch"&&(this.touch1[1]=gn.invert(this.touch1[0])),this.that.__zoom=gn,this.emit("zoom"),this},end:function(){return--this.active===0&&(delete this.that.__zooming,this.emit("end")),this},emit:function(Fe){var gn=Cg(this.that).datum();J.call(Fe,this.that,new xJn(Fe,{sourceEvent:this.sourceEvent,target:Se,transform:this.that.__zoom,dispatch:J}),gn)}};function Mn(Fe,...gn){if(!g.apply(this,arguments))return;var Ae=ln(this,gn).event(Fe),Je=this.__zoom,un=Math.max($[0],Math.min($[1],Je.k*Math.pow(2,x.apply(this,arguments)))),jn=Dm(Fe);if(Ae.wheel)(Ae.mouse[0][0]!==jn[0]||Ae.mouse[0][1]!==jn[1])&&(Ae.mouse[1]=Je.invert(Ae.mouse[0]=jn)),clearTimeout(Ae.wheel);else{if(Je.k===un)return;Ae.mouse=[jn,Je.invert(jn)],lue(this),Ae.start()}NG(Fe),Ae.wheel=setTimeout(En,be),Ae.zoom("mouse",A(de(ze(Je,un),Ae.mouse[0],Ae.mouse[1]),Ae.extent,E));function En(){Ae.wheel=null,Ae.end()}}function Nn(Fe,...gn){if(Z||!g.apply(this,arguments))return;var Ae=Fe.currentTarget,Je=ln(this,gn,!0).event(Fe),un=Cg(Fe.view).on("mousemove.zoom",rn,!0).on("mouseup.zoom",ut,!0),jn=Dm(Fe,Ae),En=Fe.clientX,Me=Fe.clientY;ddn(Fe.view),x7e(Fe),Je.mouse=[jn,this.__zoom.invert(jn)],lue(this),Je.start();function rn(st){if(NG(st),!Je.moved){var Kt=st.clientX-En,li=st.clientY-Me;Je.moved=Kt*Kt+li*li>ne}Je.event(st).zoom("mouse",A(de(Je.that.__zoom,Je.mouse[0]=Dm(st,Ae),Je.mouse[1]),Je.extent,E))}function ut(st){un.on("mousemove.zoom mouseup.zoom",null),bdn(st.view,Je.moved),NG(st),Je.event(st).end()}}function Ft(Fe,...gn){if(g.apply(this,arguments)){var Ae=this.__zoom,Je=Dm(Fe.changedTouches?Fe.changedTouches[0]:Fe,this),un=Ae.invert(Je),jn=Ae.k*(Fe.shiftKey?.5:2),En=A(de(ze(Ae,jn),Je,un),j.apply(this,gn),E);NG(Fe),H>0?Cg(this).transition().duration(H).call(an,En,Je,Fe):Cg(this).call(Se.transform,En,Je,Fe)}}function In(Fe,...gn){if(g.apply(this,arguments)){var Ae=Fe.touches,Je=Ae.length,un=ln(this,gn,Fe.changedTouches.length===Je).event(Fe),jn,En,Me,rn;for(x7e(Fe),En=0;En"[React Flow]: Seems like you have not used zustand provider as an ancestor. Help: https://reactflow.dev/error#001",error002:()=>"It looks like you've created a new nodeTypes or edgeTypes object. If this wasn't on purpose please define the nodeTypes/edgeTypes outside of the component or memoize them.",error003:g=>`Node type "${g}" not found. Using fallback type "default".`,error004:()=>"The React Flow parent container needs a width and a height to render the graph.",error005:()=>"Only child nodes can use a parent extent.",error006:()=>"Can't create edge. An edge needs a source and a target.",error007:g=>`The old edge with id=${g} does not exist.`,error009:g=>`Marker type "${g}" doesn't exist.`,error008:(g,{id:j,sourceHandle:A,targetHandle:x})=>`Couldn't create edge for ${g} handle id: "${g==="source"?A:x}", edge id: ${j}.`,error010:()=>"Handle: No node id found. Make sure to only use a Handle inside a custom Node.",error011:g=>`Edge type "${g}" not found. Using fallback type "default".`,error012:g=>`Node with id "${g}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`,error013:(g="react")=>`It seems that you haven't loaded the styles. Please import '@xyflow/${g}/dist/style.css' or base.css to make sure everything is working properly.`,error014:()=>"useNodeConnections: No node ID found. Call useNodeConnections inside a custom Node or provide a node ID.",error015:()=>"It seems that you are trying to drag a node that is not initialized. Please use onNodesChange as explained in the docs."},FG=[[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY],[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY]],Ndn=["Enter"," ","Escape"],Ddn={"node.a11yDescription.default":"Press enter or space to select a node. Press delete to remove it and escape to cancel.","node.a11yDescription.keyboardDisabled":"Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.","node.a11yDescription.ariaLiveMessage":({direction:g,x:j,y:A})=>`Moved selected node ${g}. New position, x: ${j}, y: ${A}`,"edge.a11yDescription.default":"Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.","controls.ariaLabel":"Control Panel","controls.zoomIn.ariaLabel":"Zoom In","controls.zoomOut.ariaLabel":"Zoom Out","controls.fitView.ariaLabel":"Fit View","controls.interactive.ariaLabel":"Toggle Interactivity","minimap.ariaLabel":"Mini Map","handle.ariaLabel":"Handle"};var aI;(function(g){g.Strict="strict",g.Loose="loose"})(aI||(aI={}));var hM;(function(g){g.Free="free",g.Vertical="vertical",g.Horizontal="horizontal"})(hM||(hM={}));var HG;(function(g){g.Partial="partial",g.Full="full"})(HG||(HG={}));const Idn={inProgress:!1,isValid:null,from:null,fromHandle:null,fromPosition:null,fromNode:null,to:null,toHandle:null,toPosition:null,toNode:null,pointer:null};var R7;(function(g){g.Bezier="default",g.Straight="straight",g.Step="step",g.SmoothStep="smoothstep",g.SimpleBezier="simplebezier"})(R7||(R7={}));var JG;(function(g){g.Arrow="arrow",g.ArrowClosed="arrowclosed"})(JG||(JG={}));var cr;(function(g){g.Left="left",g.Top="top",g.Right="right",g.Bottom="bottom"})(cr||(cr={}));const Whn={[cr.Left]:cr.Right,[cr.Right]:cr.Left,[cr.Top]:cr.Bottom,[cr.Bottom]:cr.Top};function _dn(g){return g===null?null:g?"valid":"invalid"}const Ldn=g=>"id"in g&&"source"in g&&"target"in g,_Jn=g=>"id"in g&&"position"in g&&!("source"in g)&&!("target"in g),hke=g=>"id"in g&&"internals"in g&&!("source"in g)&&!("target"in g),YG=(g,j=[0,0])=>{const{width:A,height:x}=q6(g),D=g.origin??j,$=A*D[0],E=x*D[1];return{x:g.position.x-$,y:g.position.y-E}},LJn=(g,j={nodeOrigin:[0,0]})=>{if(g.length===0)return{x:0,y:0,width:0,height:0};const A=g.reduce((x,D)=>{const $=typeof D=="string";let E=!j.nodeLookup&&!$?D:void 0;j.nodeLookup&&(E=$?j.nodeLookup.get(D):hke(D)?D:j.nodeLookup.get(D.id));const H=E?pue(E,j.nodeOrigin):{x:0,y:0,x2:0,y2:0};return Tue(x,H)},{x:1/0,y:1/0,x2:-1/0,y2:-1/0});return xue(A)},QG=(g,j={})=>{let A={x:1/0,y:1/0,x2:-1/0,y2:-1/0},x=!1;return g.forEach(D=>{(j.filter===void 0||j.filter(D))&&(A=Tue(A,pue(D)),x=!0)}),x?xue(A):{x:0,y:0,width:0,height:0}},dke=(g,j,[A,x,D]=[0,0,1],$=!1,E=!1)=>{const H={...WG(j,[A,x,D]),width:j.width/D,height:j.height/D},U=[];for(const J of g.values()){const{measured:te,selectable:Q=!0,hidden:Z=!1}=J;if(E&&!Q||Z)continue;const le=te.width??J.width??J.initialWidth??null,be=te.height??J.height??J.initialHeight??null,ne=GG(H,dI(J)),De=(le??0)*(be??0),Se=$&&ne>0;(!J.internals.handleBounds||Se||ne>=De||J.dragging)&&U.push(J)}return U},PJn=(g,j)=>{const A=new Set;return g.forEach(x=>{A.add(x.id)}),j.filter(x=>A.has(x.source)||A.has(x.target))};function $Jn(g,j){const A=new Map,x=j?.nodes?new Set(j.nodes.map(D=>D.id)):null;return g.forEach(D=>{D.measured.width&&D.measured.height&&(j?.includeHiddenNodes||!D.hidden)&&(!x||x.has(D.id))&&A.set(D.id,D)}),A}async function RJn({nodes:g,width:j,height:A,panZoom:x,minZoom:D,maxZoom:$},E){if(g.size===0)return Promise.resolve(!0);const H=$Jn(g,E),U=QG(H),J=bke(U,j,A,E?.minZoom??D,E?.maxZoom??$,E?.padding??.1);return await x.setViewport(J,{duration:E?.duration,ease:E?.ease,interpolate:E?.interpolate}),Promise.resolve(!0)}function Pdn({nodeId:g,nextPosition:j,nodeLookup:A,nodeOrigin:x=[0,0],nodeExtent:D,onError:$}){const E=A.get(g),H=E.parentId?A.get(E.parentId):void 0,{x:U,y:J}=H?H.internals.positionAbsolute:{x:0,y:0},te=E.origin??x;let Q=E.extent||D;if(E.extent==="parent"&&!E.expandParent)if(!H)$?.("005",Vv.error005());else{const le=H.measured.width,be=H.measured.height;le&&be&&(Q=[[U,J],[U+le,J+be]])}else H&&bI(E.extent)&&(Q=[[E.extent[0][0]+U,E.extent[0][1]+J],[E.extent[1][0]+U,E.extent[1][1]+J]]);const Z=bI(Q)?gM(j,Q,E.measured):j;return(E.measured.width===void 0||E.measured.height===void 0)&&$?.("015",Vv.error015()),{position:{x:Z.x-U+(E.measured.width??0)*te[0],y:Z.y-J+(E.measured.height??0)*te[1]},positionAbsolute:Z}}async function BJn({nodesToRemove:g=[],edgesToRemove:j=[],nodes:A,edges:x,onBeforeDelete:D}){const $=new Set(g.map(Z=>Z.id)),E=[];for(const Z of A){if(Z.deletable===!1)continue;const le=$.has(Z.id),be=!le&&Z.parentId&&E.find(ne=>ne.id===Z.parentId);(le||be)&&E.push(Z)}const H=new Set(j.map(Z=>Z.id)),U=x.filter(Z=>Z.deletable!==!1),te=PJn(E,U);for(const Z of U)H.has(Z.id)&&!te.find(be=>be.id===Z.id)&&te.push(Z);if(!D)return{edges:te,nodes:E};const Q=await D({nodes:E,edges:te});return typeof Q=="boolean"?Q?{edges:te,nodes:E}:{edges:[],nodes:[]}:Q}const hI=(g,j=0,A=1)=>Math.min(Math.max(g,j),A),gM=(g={x:0,y:0},j,A)=>({x:hI(g.x,j[0][0],j[1][0]-(A?.width??0)),y:hI(g.y,j[0][1],j[1][1]-(A?.height??0))});function $dn(g,j,A){const{width:x,height:D}=q6(A),{x:$,y:E}=A.internals.positionAbsolute;return gM(g,[[$,E],[$+x,E+D]],j)}const e1n=(g,j,A)=>gA?-hI(Math.abs(g-A),1,j)/j:0,Rdn=(g,j,A=15,x=40)=>{const D=e1n(g.x,x,j.width-x)*A,$=e1n(g.y,x,j.height-x)*A;return[D,$]},Tue=(g,j)=>({x:Math.min(g.x,j.x),y:Math.min(g.y,j.y),x2:Math.max(g.x2,j.x2),y2:Math.max(g.y2,j.y2)}),Z7e=({x:g,y:j,width:A,height:x})=>({x:g,y:j,x2:g+A,y2:j+x}),xue=({x:g,y:j,x2:A,y2:x})=>({x:g,y:j,width:A-g,height:x-j}),dI=(g,j=[0,0])=>{const{x:A,y:x}=hke(g)?g.internals.positionAbsolute:YG(g,j);return{x:A,y:x,width:g.measured?.width??g.width??g.initialWidth??0,height:g.measured?.height??g.height??g.initialHeight??0}},pue=(g,j=[0,0])=>{const{x:A,y:x}=hke(g)?g.internals.positionAbsolute:YG(g,j);return{x:A,y:x,x2:A+(g.measured?.width??g.width??g.initialWidth??0),y2:x+(g.measured?.height??g.height??g.initialHeight??0)}},Bdn=(g,j)=>xue(Tue(Z7e(g),Z7e(j))),GG=(g,j)=>{const A=Math.max(0,Math.min(g.x+g.width,j.x+j.width)-Math.max(g.x,j.x)),x=Math.max(0,Math.min(g.y+g.height,j.y+j.height)-Math.max(g.y,j.y));return Math.ceil(A*x)},n1n=g=>_m(g.width)&&_m(g.height)&&_m(g.x)&&_m(g.y),_m=g=>!isNaN(g)&&isFinite(g),zJn=(g,j)=>{},ZG=(g,j=[1,1])=>({x:j[0]*Math.round(g.x/j[0]),y:j[1]*Math.round(g.y/j[1])}),WG=({x:g,y:j},[A,x,D],$=!1,E=[1,1])=>{const H={x:(g-A)/D,y:(j-x)/D};return $?ZG(H,E):H},mue=({x:g,y:j},[A,x,D])=>({x:g*D+A,y:j*D+x});function rI(g,j){if(typeof g=="number")return Math.floor((j-j/(1+g))*.5);if(typeof g=="string"&&g.endsWith("px")){const A=parseFloat(g);if(!Number.isNaN(A))return Math.floor(A)}if(typeof g=="string"&&g.endsWith("%")){const A=parseFloat(g);if(!Number.isNaN(A))return Math.floor(j*A*.01)}return console.error(`[React Flow] The padding value "${g}" is invalid. Please provide a number or a string with a valid unit (px or %).`),0}function FJn(g,j,A){if(typeof g=="string"||typeof g=="number"){const x=rI(g,A),D=rI(g,j);return{top:x,right:D,bottom:x,left:D,x:D*2,y:x*2}}if(typeof g=="object"){const x=rI(g.top??g.y??0,A),D=rI(g.bottom??g.y??0,A),$=rI(g.left??g.x??0,j),E=rI(g.right??g.x??0,j);return{top:x,right:E,bottom:D,left:$,x:$+E,y:x+D}}return{top:0,right:0,bottom:0,left:0,x:0,y:0}}function HJn(g,j,A,x,D,$){const{x:E,y:H}=mue(g,[j,A,x]),{x:U,y:J}=mue({x:g.x+g.width,y:g.y+g.height},[j,A,x]),te=D-U,Q=$-J;return{left:Math.floor(E),top:Math.floor(H),right:Math.floor(te),bottom:Math.floor(Q)}}const bke=(g,j,A,x,D,$)=>{const E=FJn($,j,A),H=(j-E.x)/g.width,U=(A-E.y)/g.height,J=Math.min(H,U),te=hI(J,x,D),Q=g.x+g.width/2,Z=g.y+g.height/2,le=j/2-Q*te,be=A/2-Z*te,ne=HJn(g,le,be,te,j,A),De={left:Math.min(ne.left-E.left,0),top:Math.min(ne.top-E.top,0),right:Math.min(ne.right-E.right,0),bottom:Math.min(ne.bottom-E.bottom,0)};return{x:le-De.left+De.right,y:be-De.top+De.bottom,zoom:te}},qG=()=>typeof navigator<"u"&&navigator?.userAgent?.indexOf("Mac")>=0;function bI(g){return g!=null&&g!=="parent"}function q6(g){return{width:g.measured?.width??g.width??g.initialWidth??0,height:g.measured?.height??g.height??g.initialHeight??0}}function zdn(g){return(g.measured?.width??g.width??g.initialWidth)!==void 0&&(g.measured?.height??g.height??g.initialHeight)!==void 0}function Fdn(g,j={width:0,height:0},A,x,D){const $={...g},E=x.get(A);if(E){const H=E.origin||D;$.x+=E.internals.positionAbsolute.x-(j.width??0)*H[0],$.y+=E.internals.positionAbsolute.y-(j.height??0)*H[1]}return $}function t1n(g,j){if(g.size!==j.size)return!1;for(const A of g)if(!j.has(A))return!1;return!0}function JJn(){let g,j;return{promise:new Promise((x,D)=>{g=x,j=D}),resolve:g,reject:j}}function GJn(g){return{...Ddn,...g||{}}}function PG(g,{snapGrid:j=[0,0],snapToGrid:A=!1,transform:x,containerBounds:D}){const{x:$,y:E}=Lm(g),H=WG({x:$-(D?.left??0),y:E-(D?.top??0)},x),{x:U,y:J}=A?ZG(H,j):H;return{xSnapped:U,ySnapped:J,...H}}const gke=g=>({width:g.offsetWidth,height:g.offsetHeight}),Hdn=g=>g?.getRootNode?.()||window?.document,qJn=["INPUT","SELECT","TEXTAREA"];function Jdn(g){const j=g.composedPath?.()?.[0]||g.target;return j?.nodeType!==1?!1:qJn.includes(j.nodeName)||j.hasAttribute("contenteditable")||!!j.closest(".nokey")}const Gdn=g=>"clientX"in g,Lm=(g,j)=>{const A=Gdn(g),x=A?g.clientX:g.touches?.[0].clientX,D=A?g.clientY:g.touches?.[0].clientY;return{x:x-(j?.left??0),y:D-(j?.top??0)}},i1n=(g,j,A,x,D)=>{const $=j.querySelectorAll(`.${g}`);return!$||!$.length?null:Array.from($).map(E=>{const H=E.getBoundingClientRect();return{id:E.getAttribute("data-handleid"),type:g,nodeId:D,position:E.getAttribute("data-handlepos"),x:(H.left-A.left)/x,y:(H.top-A.top)/x,...gke(E)}})};function qdn({sourceX:g,sourceY:j,targetX:A,targetY:x,sourceControlX:D,sourceControlY:$,targetControlX:E,targetControlY:H}){const U=g*.125+D*.375+E*.375+A*.125,J=j*.125+$*.375+H*.375+x*.125,te=Math.abs(U-g),Q=Math.abs(J-j);return[U,J,te,Q]}function Yce(g,j){return g>=0?.5*g:j*25*Math.sqrt(-g)}function r1n({pos:g,x1:j,y1:A,x2:x,y2:D,c:$}){switch(g){case cr.Left:return[j-Yce(j-x,$),A];case cr.Right:return[j+Yce(x-j,$),A];case cr.Top:return[j,A-Yce(A-D,$)];case cr.Bottom:return[j,A+Yce(D-A,$)]}}function Udn({sourceX:g,sourceY:j,sourcePosition:A=cr.Bottom,targetX:x,targetY:D,targetPosition:$=cr.Top,curvature:E=.25}){const[H,U]=r1n({pos:A,x1:g,y1:j,x2:x,y2:D,c:E}),[J,te]=r1n({pos:$,x1:x,y1:D,x2:g,y2:j,c:E}),[Q,Z,le,be]=qdn({sourceX:g,sourceY:j,targetX:x,targetY:D,sourceControlX:H,sourceControlY:U,targetControlX:J,targetControlY:te});return[`M${g},${j} C${H},${U} ${J},${te} ${x},${D}`,Q,Z,le,be]}function Xdn({sourceX:g,sourceY:j,targetX:A,targetY:x}){const D=Math.abs(A-g)/2,$=A0}const KJn=({source:g,sourceHandle:j,target:A,targetHandle:x})=>`xy-edge__${g}${j||""}-${A}${x||""}`,VJn=(g,j)=>j.some(A=>A.source===g.source&&A.target===g.target&&(A.sourceHandle===g.sourceHandle||!A.sourceHandle&&!g.sourceHandle)&&(A.targetHandle===g.targetHandle||!A.targetHandle&&!g.targetHandle)),YJn=(g,j,A={})=>{if(!g.source||!g.target)return j;const x=A.getEdgeId||KJn;let D;return Ldn(g)?D={...g}:D={...g,id:x(g)},VJn(D,j)?j:(D.sourceHandle===null&&delete D.sourceHandle,D.targetHandle===null&&delete D.targetHandle,j.concat(D))};function Kdn({sourceX:g,sourceY:j,targetX:A,targetY:x}){const[D,$,E,H]=Xdn({sourceX:g,sourceY:j,targetX:A,targetY:x});return[`M ${g},${j}L ${A},${x}`,D,$,E,H]}const c1n={[cr.Left]:{x:-1,y:0},[cr.Right]:{x:1,y:0},[cr.Top]:{x:0,y:-1},[cr.Bottom]:{x:0,y:1}},QJn=({source:g,sourcePosition:j=cr.Bottom,target:A})=>j===cr.Left||j===cr.Right?g.xMath.sqrt(Math.pow(j.x-g.x,2)+Math.pow(j.y-g.y,2));function ZJn({source:g,sourcePosition:j=cr.Bottom,target:A,targetPosition:x=cr.Top,center:D,offset:$,stepPosition:E}){const H=c1n[j],U=c1n[x],J={x:g.x+H.x*$,y:g.y+H.y*$},te={x:A.x+U.x*$,y:A.y+U.y*$},Q=QJn({source:J,sourcePosition:j,target:te}),Z=Q.x!==0?"x":"y",le=Q[Z];let be=[],ne,De;const Se={x:0,y:0},ze={x:0,y:0},[,,de,Le]=Xdn({sourceX:g.x,sourceY:g.y,targetX:A.x,targetY:A.y});if(H[Z]*U[Z]===-1){Z==="x"?(ne=D.x??J.x+(te.x-J.x)*E,De=D.y??(J.y+te.y)/2):(ne=D.x??(J.x+te.x)/2,De=D.y??J.y+(te.y-J.y)*E);const Mn=[{x:ne,y:J.y},{x:ne,y:te.y}],Nn=[{x:J.x,y:De},{x:te.x,y:De}];H[Z]===le?be=Z==="x"?Mn:Nn:be=Z==="x"?Nn:Mn}else{const Mn=[{x:J.x,y:te.y}],Nn=[{x:te.x,y:J.y}];if(Z==="x"?be=H.x===le?Nn:Mn:be=H.y===le?Mn:Nn,j===x){const Fe=Math.abs(g[Z]-A[Z]);if(Fe<=$){const gn=Math.min($-1,$-Fe);H[Z]===le?Se[Z]=(J[Z]>g[Z]?-1:1)*gn:ze[Z]=(te[Z]>A[Z]?-1:1)*gn}}if(j!==x){const Fe=Z==="x"?"y":"x",gn=H[Z]===U[Fe],Ae=J[Fe]>te[Fe],Je=J[Fe]=Y?(ne=(Ft.x+In.x)/2,De=be[0].y):(ne=be[0].x,De=(Ft.y+In.y)/2)}const an={x:J.x+Se.x,y:J.y+Se.y},ln={x:te.x+ze.x,y:te.y+ze.y};return[[g,...an.x!==be[0].x||an.y!==be[0].y?[an]:[],...be,...ln.x!==be[be.length-1].x||ln.y!==be[be.length-1].y?[ln]:[],A],ne,De,de,Le]}function WJn(g,j,A,x){const D=Math.min(u1n(g,j)/2,u1n(j,A)/2,x),{x:$,y:E}=j;if(g.x===$&&$===A.x||g.y===E&&E===A.y)return`L${$} ${E}`;if(g.y===E){const J=g.xA.id===j):g[0])||null}function W7e(g,j){return g?typeof g=="string"?g:`${j?`${j}__`:""}${Object.keys(g).sort().map(x=>`${x}=${g[x]}`).join("&")}`:""}function nGn(g,{id:j,defaultColor:A,defaultMarkerStart:x,defaultMarkerEnd:D}){const $=new Set;return g.reduce((E,H)=>([H.markerStart||x,H.markerEnd||D].forEach(U=>{if(U&&typeof U=="object"){const J=W7e(U,j);$.has(J)||(E.push({id:J,color:U.color||A,...U}),$.add(J))}}),E),[]).sort((E,H)=>E.id.localeCompare(H.id))}const Vdn=1e3,tGn=10,wke={nodeOrigin:[0,0],nodeExtent:FG,elevateNodesOnSelect:!0,zIndexMode:"basic",defaults:{}},iGn={...wke,checkEquality:!0};function pke(g,j){const A={...g};for(const x in j)j[x]!==void 0&&(A[x]=j[x]);return A}function rGn(g,j,A){const x=pke(wke,A);for(const D of g.values())if(D.parentId)vke(D,g,j,x);else{const $=YG(D,x.nodeOrigin),E=bI(D.extent)?D.extent:x.nodeExtent,H=gM($,E,q6(D));D.internals.positionAbsolute=H}}function cGn(g,j){if(!g.handles)return g.measured?j?.internals.handleBounds:void 0;const A=[],x=[];for(const D of g.handles){const $={id:D.id,width:D.width??1,height:D.height??1,nodeId:g.id,x:D.x,y:D.y,position:D.position,type:D.type};D.type==="source"?A.push($):D.type==="target"&&x.push($)}return{source:A,target:x}}function mke(g){return g==="manual"}function eke(g,j,A,x={}){const D=pke(iGn,x),$={i:0},E=new Map(j),H=D?.elevateNodesOnSelect&&!mke(D.zIndexMode)?Vdn:0;let U=g.length>0,J=!1;j.clear(),A.clear();for(const te of g){let Q=E.get(te.id);if(D.checkEquality&&te===Q?.internals.userNode)j.set(te.id,Q);else{const Z=YG(te,D.nodeOrigin),le=bI(te.extent)?te.extent:D.nodeExtent,be=gM(Z,le,q6(te));Q={...D.defaults,...te,measured:{width:te.measured?.width,height:te.measured?.height},internals:{positionAbsolute:be,handleBounds:cGn(te,Q),z:Ydn(te,H,D.zIndexMode),userNode:te}},j.set(te.id,Q)}(Q.measured===void 0||Q.measured.width===void 0||Q.measured.height===void 0)&&!Q.hidden&&(U=!1),te.parentId&&vke(Q,j,A,x,$),J||=te.selected??!1}return{nodesInitialized:U,hasSelectedNodes:J}}function uGn(g,j){if(!g.parentId)return;const A=j.get(g.parentId);A?A.set(g.id,g):j.set(g.parentId,new Map([[g.id,g]]))}function vke(g,j,A,x,D){const{elevateNodesOnSelect:$,nodeOrigin:E,nodeExtent:H,zIndexMode:U}=pke(wke,x),J=g.parentId,te=j.get(J);if(!te){console.warn(`Parent node ${J} not found. Please make sure that parent nodes are in front of their child nodes in the nodes array.`);return}uGn(g,A),D&&!te.parentId&&te.internals.rootParentIndex===void 0&&U==="auto"&&(te.internals.rootParentIndex=++D.i,te.internals.z=te.internals.z+D.i*tGn),D&&te.internals.rootParentIndex!==void 0&&(D.i=te.internals.rootParentIndex);const Q=$&&!mke(U)?Vdn:0,{x:Z,y:le,z:be}=oGn(g,te,E,H,Q,U),{positionAbsolute:ne}=g.internals,De=Z!==ne.x||le!==ne.y;(De||be!==g.internals.z)&&j.set(g.id,{...g,internals:{...g.internals,positionAbsolute:De?{x:Z,y:le}:ne,z:be}})}function Ydn(g,j,A){const x=_m(g.zIndex)?g.zIndex:0;return mke(A)?x:x+(g.selected?j:0)}function oGn(g,j,A,x,D,$){const{x:E,y:H}=j.internals.positionAbsolute,U=q6(g),J=YG(g,A),te=bI(g.extent)?gM(J,g.extent,U):J;let Q=gM({x:E+te.x,y:H+te.y},x,U);g.extent==="parent"&&(Q=$dn(Q,U,j));const Z=Ydn(g,D,$),le=j.internals.z??0;return{x:Q.x,y:Q.y,z:le>=Z?le+1:Z}}function yke(g,j,A,x=[0,0]){const D=[],$=new Map;for(const E of g){const H=j.get(E.parentId);if(!H)continue;const U=$.get(E.parentId)?.expandedRect??dI(H),J=Bdn(U,E.rect);$.set(E.parentId,{expandedRect:J,parent:H})}return $.size>0&&$.forEach(({expandedRect:E,parent:H},U)=>{const J=H.internals.positionAbsolute,te=q6(H),Q=H.origin??x,Z=E.x0||le>0||De||Se)&&(D.push({id:U,type:"position",position:{x:H.position.x-Z+De,y:H.position.y-le+Se}}),A.get(U)?.forEach(ze=>{g.some(de=>de.id===ze.id)||D.push({id:ze.id,type:"position",position:{x:ze.position.x+Z,y:ze.position.y+le}})})),(te.width0){const le=yke(Z,j,A,D);J.push(...le)}return{changes:J,updatedInternals:U}}async function lGn({delta:g,panZoom:j,transform:A,translateExtent:x,width:D,height:$}){if(!j||!g.x&&!g.y)return Promise.resolve(!1);const E=await j.setViewportConstrained({x:A[0]+g.x,y:A[1]+g.y,zoom:A[2]},[[0,0],[D,$]],x),H=!!E&&(E.x!==A[0]||E.y!==A[1]||E.k!==A[2]);return Promise.resolve(H)}function f1n(g,j,A,x,D,$){let E=D;const H=x.get(E)||new Map;x.set(E,H.set(A,j)),E=`${D}-${g}`;const U=x.get(E)||new Map;if(x.set(E,U.set(A,j)),$){E=`${D}-${g}-${$}`;const J=x.get(E)||new Map;x.set(E,J.set(A,j))}}function Qdn(g,j,A){g.clear(),j.clear();for(const x of A){const{source:D,target:$,sourceHandle:E=null,targetHandle:H=null}=x,U={edgeId:x.id,source:D,target:$,sourceHandle:E,targetHandle:H},J=`${D}-${E}--${$}-${H}`,te=`${$}-${H}--${D}-${E}`;f1n("source",U,te,g,D,E),f1n("target",U,J,g,$,H),j.set(x.id,x)}}function Zdn(g,j){if(!g.parentId)return!1;const A=j.get(g.parentId);return A?A.selected?!0:Zdn(A,j):!1}function a1n(g,j,A){let x=g;do{if(x?.matches?.(j))return!0;if(x===A)return!1;x=x?.parentElement}while(x);return!1}function fGn(g,j,A,x){const D=new Map;for(const[$,E]of g)if((E.selected||E.id===x)&&(!E.parentId||!Zdn(E,g))&&(E.draggable||j&&typeof E.draggable>"u")){const H=g.get($);H&&D.set($,{id:$,position:H.position||{x:0,y:0},distance:{x:A.x-H.internals.positionAbsolute.x,y:A.y-H.internals.positionAbsolute.y},extent:H.extent,parentId:H.parentId,origin:H.origin,expandParent:H.expandParent,internals:{positionAbsolute:H.internals.positionAbsolute||{x:0,y:0}},measured:{width:H.measured.width??0,height:H.measured.height??0}})}return D}function C7e({nodeId:g,dragItems:j,nodeLookup:A,dragging:x=!0}){const D=[];for(const[E,H]of j){const U=A.get(E)?.internals.userNode;U&&D.push({...U,position:H.position,dragging:x})}if(!g)return[D[0],D];const $=A.get(g)?.internals.userNode;return[$?{...$,position:j.get(g)?.position||$.position,dragging:x}:D[0],D]}function aGn({dragItems:g,snapGrid:j,x:A,y:x}){const D=g.values().next().value;if(!D)return null;const $={x:A-D.distance.x,y:x-D.distance.y},E=ZG($,j);return{x:E.x-$.x,y:E.y-$.y}}function hGn({onNodeMouseDown:g,getStoreItems:j,onDragStart:A,onDrag:x,onDragStop:D}){let $={x:null,y:null},E=0,H=new Map,U=!1,J={x:0,y:0},te=null,Q=!1,Z=null,le=!1,be=!1,ne=null;function De({noDragClassName:ze,handleSelector:de,domNode:Le,isSelectable:an,nodeId:ln,nodeClickDistance:on=0}){Z=Cg(Le);function Mn({x:nt,y:Y}){const{nodeLookup:Fe,nodeExtent:gn,snapGrid:Ae,snapToGrid:Je,nodeOrigin:un,onNodeDrag:jn,onSelectionDrag:En,onError:Me,updateNodePositions:rn}=j();$={x:nt,y:Y};let ut=!1;const st=H.size>1,Kt=st&&gn?Z7e(QG(H)):null,li=st&&Je?aGn({dragItems:H,snapGrid:Ae,x:nt,y:Y}):null;for(const[oi,Jt]of H){if(!Fe.has(oi))continue;let hi={x:nt-Jt.distance.x,y:Y-Jt.distance.y};Je&&(hi=li?{x:Math.round(hi.x+li.x),y:Math.round(hi.y+li.y)}:ZG(hi,Ae));let nc=null;if(st&&gn&&!Jt.extent&&Kt){const{positionAbsolute:Ei}=Jt.internals,Bi=Ei.x-Kt.x+gn[0][0],Fc=Ei.x+Jt.measured.width-Kt.x2+gn[1][0],Du=Ei.y-Kt.y+gn[0][1],kl=Ei.y+Jt.measured.height-Kt.y2+gn[1][1];nc=[[Bi,Du],[Fc,kl]]}const{position:Xr,positionAbsolute:pr}=Pdn({nodeId:oi,nextPosition:hi,nodeLookup:Fe,nodeExtent:nc||gn,nodeOrigin:un,onError:Me});ut=ut||Jt.position.x!==Xr.x||Jt.position.y!==Xr.y,Jt.position=Xr,Jt.internals.positionAbsolute=pr}if(be=be||ut,!!ut&&(rn(H,!0),ne&&(x||jn||!ln&&En))){const[oi,Jt]=C7e({nodeId:ln,dragItems:H,nodeLookup:Fe});x?.(ne,H,oi,Jt),jn?.(ne,oi,Jt),ln||En?.(ne,Jt)}}async function Nn(){if(!te)return;const{transform:nt,panBy:Y,autoPanSpeed:Fe,autoPanOnNodeDrag:gn}=j();if(!gn){U=!1,cancelAnimationFrame(E);return}const[Ae,Je]=Rdn(J,te,Fe);(Ae!==0||Je!==0)&&($.x=($.x??0)-Ae/nt[2],$.y=($.y??0)-Je/nt[2],await Y({x:Ae,y:Je})&&Mn($)),E=requestAnimationFrame(Nn)}function Ft(nt){const{nodeLookup:Y,multiSelectionActive:Fe,nodesDraggable:gn,transform:Ae,snapGrid:Je,snapToGrid:un,selectNodesOnDrag:jn,onNodeDragStart:En,onSelectionDragStart:Me,unselectNodesAndEdges:rn}=j();Q=!0,(!jn||!an)&&!Fe&&ln&&(Y.get(ln)?.selected||rn()),an&&jn&&ln&&g?.(ln);const ut=PG(nt.sourceEvent,{transform:Ae,snapGrid:Je,snapToGrid:un,containerBounds:te});if($=ut,H=fGn(Y,gn,ut,ln),H.size>0&&(A||En||!ln&&Me)){const[st,Kt]=C7e({nodeId:ln,dragItems:H,nodeLookup:Y});A?.(nt.sourceEvent,H,st,Kt),En?.(nt.sourceEvent,st,Kt),ln||Me?.(nt.sourceEvent,Kt)}}const In=gdn().clickDistance(on).on("start",nt=>{const{domNode:Y,nodeDragThreshold:Fe,transform:gn,snapGrid:Ae,snapToGrid:Je}=j();te=Y?.getBoundingClientRect()||null,le=!1,be=!1,ne=nt.sourceEvent,Fe===0&&Ft(nt),$=PG(nt.sourceEvent,{transform:gn,snapGrid:Ae,snapToGrid:Je,containerBounds:te}),J=Lm(nt.sourceEvent,te)}).on("drag",nt=>{const{autoPanOnNodeDrag:Y,transform:Fe,snapGrid:gn,snapToGrid:Ae,nodeDragThreshold:Je,nodeLookup:un}=j(),jn=PG(nt.sourceEvent,{transform:Fe,snapGrid:gn,snapToGrid:Ae,containerBounds:te});if(ne=nt.sourceEvent,(nt.sourceEvent.type==="touchmove"&&nt.sourceEvent.touches.length>1||ln&&!un.has(ln))&&(le=!0),!le){if(!U&&Y&&Q&&(U=!0,Nn()),!Q){const En=Lm(nt.sourceEvent,te),Me=En.x-J.x,rn=En.y-J.y;Math.sqrt(Me*Me+rn*rn)>Je&&Ft(nt)}($.x!==jn.xSnapped||$.y!==jn.ySnapped)&&H&&Q&&(J=Lm(nt.sourceEvent,te),Mn(jn))}}).on("end",nt=>{if(!(!Q||le)&&(U=!1,Q=!1,cancelAnimationFrame(E),H.size>0)){const{nodeLookup:Y,updateNodePositions:Fe,onNodeDragStop:gn,onSelectionDragStop:Ae}=j();if(be&&(Fe(H,!1),be=!1),D||gn||!ln&&Ae){const[Je,un]=C7e({nodeId:ln,dragItems:H,nodeLookup:Y,dragging:!1});D?.(nt.sourceEvent,H,Je,un),gn?.(nt.sourceEvent,Je,un),ln||Ae?.(nt.sourceEvent,un)}}}).filter(nt=>{const Y=nt.target;return!nt.button&&(!ze||!a1n(Y,`.${ze}`,Le))&&(!de||a1n(Y,de,Le))});Z.call(In)}function Se(){Z?.on(".drag",null)}return{update:De,destroy:Se}}function dGn(g,j,A){const x=[],D={x:g.x-A,y:g.y-A,width:A*2,height:A*2};for(const $ of j.values())GG(D,dI($))>0&&x.push($);return x}const bGn=250;function gGn(g,j,A,x){let D=[],$=1/0;const E=dGn(g,A,j+bGn);for(const H of E){const U=[...H.internals.handleBounds?.source??[],...H.internals.handleBounds?.target??[]];for(const J of U){if(x.nodeId===J.nodeId&&x.type===J.type&&x.id===J.id)continue;const{x:te,y:Q}=wM(H,J,J.position,!0),Z=Math.sqrt(Math.pow(te-g.x,2)+Math.pow(Q-g.y,2));Z>j||(Z<$?(D=[{...J,x:te,y:Q}],$=Z):Z===$&&D.push({...J,x:te,y:Q}))}}if(!D.length)return null;if(D.length>1){const H=x.type==="source"?"target":"source";return D.find(U=>U.type===H)??D[0]}return D[0]}function Wdn(g,j,A,x,D,$=!1){const E=x.get(g);if(!E)return null;const H=D==="strict"?E.internals.handleBounds?.[j]:[...E.internals.handleBounds?.source??[],...E.internals.handleBounds?.target??[]],U=(A?H?.find(J=>J.id===A):H?.[0])??null;return U&&$?{...U,...wM(E,U,U.position,!0)}:U}function e0n(g,j){return g||(j?.classList.contains("target")?"target":j?.classList.contains("source")?"source":null)}function wGn(g,j){let A=null;return j?A=!0:g&&!j&&(A=!1),A}const n0n=()=>!0;function pGn(g,{connectionMode:j,connectionRadius:A,handleId:x,nodeId:D,edgeUpdaterType:$,isTarget:E,domNode:H,nodeLookup:U,lib:J,autoPanOnConnect:te,flowId:Q,panBy:Z,cancelConnection:le,onConnectStart:be,onConnect:ne,onConnectEnd:De,isValidConnection:Se=n0n,onReconnectEnd:ze,updateConnection:de,getTransform:Le,getFromHandle:an,autoPanSpeed:ln,dragThreshold:on=1,handleDomNode:Mn}){const Nn=Hdn(g.target);let Ft=0,In;const{x:nt,y:Y}=Lm(g),Fe=e0n($,Mn),gn=H?.getBoundingClientRect();let Ae=!1;if(!gn||!Fe)return;const Je=Wdn(D,Fe,x,U,j);if(!Je)return;let un=Lm(g,gn),jn=!1,En=null,Me=!1,rn=null;function ut(){if(!te||!gn)return;const[Xr,pr]=Rdn(un,gn,ln);Z({x:Xr,y:pr}),Ft=requestAnimationFrame(ut)}const st={...Je,nodeId:D,type:Fe,position:Je.position},Kt=U.get(D);let oi={inProgress:!0,isValid:null,from:wM(Kt,st,cr.Left,!0),fromHandle:st,fromPosition:st.position,fromNode:Kt,to:un,toHandle:null,toPosition:Whn[st.position],toNode:null,pointer:un};function Jt(){Ae=!0,de(oi),be?.(g,{nodeId:D,handleId:x,handleType:Fe})}on===0&&Jt();function hi(Xr){if(!Ae){const{x:kl,y:s1}=Lm(Xr),Za=kl-nt,Wa=s1-Y;if(!(Za*Za+Wa*Wa>on*on))return;Jt()}if(!an()||!st){nc(Xr);return}const pr=Le();un=Lm(Xr,gn),In=gGn(WG(un,pr,!1,[1,1]),A,U,st),jn||(ut(),jn=!0);const Ei=t0n(Xr,{handle:In,connectionMode:j,fromNodeId:D,fromHandleId:x,fromType:E?"target":"source",isValidConnection:Se,doc:Nn,lib:J,flowId:Q,nodeLookup:U});rn=Ei.handleDomNode,En=Ei.connection,Me=wGn(!!In,Ei.isValid);const Bi=U.get(D),Fc=Bi?wM(Bi,st,cr.Left,!0):oi.from,Du={...oi,from:Fc,isValid:Me,to:Ei.toHandle&&Me?mue({x:Ei.toHandle.x,y:Ei.toHandle.y},pr):un,toHandle:Ei.toHandle,toPosition:Me&&Ei.toHandle?Ei.toHandle.position:Whn[st.position],toNode:Ei.toHandle?U.get(Ei.toHandle.nodeId):null,pointer:un};de(Du),oi=Du}function nc(Xr){if(!("touches"in Xr&&Xr.touches.length>0)){if(Ae){(In||rn)&&En&&Me&&ne?.(En);const{inProgress:pr,...Ei}=oi,Bi={...Ei,toPosition:oi.toHandle?oi.toPosition:null};De?.(Xr,Bi),$&&ze?.(Xr,Bi)}le(),cancelAnimationFrame(Ft),jn=!1,Me=!1,En=null,rn=null,Nn.removeEventListener("mousemove",hi),Nn.removeEventListener("mouseup",nc),Nn.removeEventListener("touchmove",hi),Nn.removeEventListener("touchend",nc)}}Nn.addEventListener("mousemove",hi),Nn.addEventListener("mouseup",nc),Nn.addEventListener("touchmove",hi),Nn.addEventListener("touchend",nc)}function t0n(g,{handle:j,connectionMode:A,fromNodeId:x,fromHandleId:D,fromType:$,doc:E,lib:H,flowId:U,isValidConnection:J=n0n,nodeLookup:te}){const Q=$==="target",Z=j?E.querySelector(`.${H}-flow__handle[data-id="${U}-${j?.nodeId}-${j?.id}-${j?.type}"]`):null,{x:le,y:be}=Lm(g),ne=E.elementFromPoint(le,be),De=ne?.classList.contains(`${H}-flow__handle`)?ne:Z,Se={handleDomNode:De,isValid:!1,connection:null,toHandle:null};if(De){const ze=e0n(void 0,De),de=De.getAttribute("data-nodeid"),Le=De.getAttribute("data-handleid"),an=De.classList.contains("connectable"),ln=De.classList.contains("connectableend");if(!de||!ze)return Se;const on={source:Q?de:x,sourceHandle:Q?Le:D,target:Q?x:de,targetHandle:Q?D:Le};Se.connection=on;const Nn=an&&ln&&(A===aI.Strict?Q&&ze==="source"||!Q&&ze==="target":de!==x||Le!==D);Se.isValid=Nn&&J(on),Se.toHandle=Wdn(de,ze,Le,te,A,!0)}return Se}const nke={onPointerDown:pGn,isValid:t0n};function mGn({domNode:g,panZoom:j,getTransform:A,getViewScale:x}){const D=Cg(g);function $({translateExtent:H,width:U,height:J,zoomStep:te=1,pannable:Q=!0,zoomable:Z=!0,inversePan:le=!1}){const be=de=>{if(de.sourceEvent.type!=="wheel"||!j)return;const Le=A(),an=de.sourceEvent.ctrlKey&&qG()?10:1,ln=-de.sourceEvent.deltaY*(de.sourceEvent.deltaMode===1?.05:de.sourceEvent.deltaMode?1:.002)*te,on=Le[2]*Math.pow(2,ln*an);j.scaleTo(on)};let ne=[0,0];const De=de=>{(de.sourceEvent.type==="mousedown"||de.sourceEvent.type==="touchstart")&&(ne=[de.sourceEvent.clientX??de.sourceEvent.touches[0].clientX,de.sourceEvent.clientY??de.sourceEvent.touches[0].clientY])},Se=de=>{const Le=A();if(de.sourceEvent.type!=="mousemove"&&de.sourceEvent.type!=="touchmove"||!j)return;const an=[de.sourceEvent.clientX??de.sourceEvent.touches[0].clientX,de.sourceEvent.clientY??de.sourceEvent.touches[0].clientY],ln=[an[0]-ne[0],an[1]-ne[1]];ne=an;const on=x()*Math.max(Le[2],Math.log(Le[2]))*(le?-1:1),Mn={x:Le[0]-ln[0]*on,y:Le[1]-ln[1]*on},Nn=[[0,0],[U,J]];j.setViewportConstrained({x:Mn.x,y:Mn.y,zoom:Le[2]},Nn,H)},ze=Odn().on("start",De).on("zoom",Q?Se:null).on("zoom.wheel",Z?be:null);D.call(ze,{})}function E(){D.on("zoom",null)}return{update:$,destroy:E,pointer:Dm}}const Cue=g=>({x:g.x,y:g.y,zoom:g.k}),O7e=({x:g,y:j,zoom:A})=>Mue.translate(g,j).scale(A),cI=(g,j)=>g.target.closest(`.${j}`),i0n=(g,j)=>j===2&&Array.isArray(g)&&g.includes(2),vGn=g=>((g*=2)<=1?g*g*g:(g-=2)*g*g+2)/2,N7e=(g,j=0,A=vGn,x=()=>{})=>{const D=typeof j=="number"&&j>0;return D||x(),D?g.transition().duration(j).ease(A).on("end",x):g},r0n=g=>{const j=g.ctrlKey&&qG()?10:1;return-g.deltaY*(g.deltaMode===1?.05:g.deltaMode?1:.002)*j};function yGn({zoomPanValues:g,noWheelClassName:j,d3Selection:A,d3Zoom:x,panOnScrollMode:D,panOnScrollSpeed:$,zoomOnPinch:E,onPanZoomStart:H,onPanZoom:U,onPanZoomEnd:J}){return te=>{if(cI(te,j))return te.ctrlKey&&te.preventDefault(),!1;te.preventDefault(),te.stopImmediatePropagation();const Q=A.property("__zoom").k||1;if(te.ctrlKey&&E){const De=Dm(te),Se=r0n(te),ze=Q*Math.pow(2,Se);x.scaleTo(A,ze,De,te);return}const Z=te.deltaMode===1?20:1;let le=D===hM.Vertical?0:te.deltaX*Z,be=D===hM.Horizontal?0:te.deltaY*Z;!qG()&&te.shiftKey&&D!==hM.Vertical&&(le=te.deltaY*Z,be=0),x.translateBy(A,-(le/Q)*$,-(be/Q)*$,{internal:!0});const ne=Cue(A.property("__zoom"));clearTimeout(g.panScrollTimeout),g.isPanScrolling?(U?.(te,ne),g.panScrollTimeout=setTimeout(()=>{J?.(te,ne),g.isPanScrolling=!1},150)):(g.isPanScrolling=!0,H?.(te,ne))}}function kGn({noWheelClassName:g,preventScrolling:j,d3ZoomHandler:A}){return function(x,D){const $=x.type==="wheel",E=!j&&$&&!x.ctrlKey,H=cI(x,g);if(x.ctrlKey&&$&&H&&x.preventDefault(),E||H)return null;x.preventDefault(),A.call(this,x,D)}}function EGn({zoomPanValues:g,onDraggingChange:j,onPanZoomStart:A}){return x=>{if(x.sourceEvent?.internal)return;const D=Cue(x.transform);g.mouseButton=x.sourceEvent?.button||0,g.isZoomingOrPanning=!0,g.prevViewport=D,x.sourceEvent?.type==="mousedown"&&j(!0),A&&A?.(x.sourceEvent,D)}}function jGn({zoomPanValues:g,panOnDrag:j,onPaneContextMenu:A,onTransformChange:x,onPanZoom:D}){return $=>{g.usedRightMouseButton=!!(A&&i0n(j,g.mouseButton??0)),$.sourceEvent?.sync||x([$.transform.x,$.transform.y,$.transform.k]),D&&!$.sourceEvent?.internal&&D?.($.sourceEvent,Cue($.transform))}}function SGn({zoomPanValues:g,panOnDrag:j,panOnScroll:A,onDraggingChange:x,onPanZoomEnd:D,onPaneContextMenu:$}){return E=>{if(!E.sourceEvent?.internal&&(g.isZoomingOrPanning=!1,$&&i0n(j,g.mouseButton??0)&&!g.usedRightMouseButton&&E.sourceEvent&&$(E.sourceEvent),g.usedRightMouseButton=!1,x(!1),D)){const H=Cue(E.transform);g.prevViewport=H,clearTimeout(g.timerId),g.timerId=setTimeout(()=>{D?.(E.sourceEvent,H)},A?150:0)}}}function AGn({zoomActivationKeyPressed:g,zoomOnScroll:j,zoomOnPinch:A,panOnDrag:x,panOnScroll:D,zoomOnDoubleClick:$,userSelectionActive:E,noWheelClassName:H,noPanClassName:U,lib:J,connectionInProgress:te}){return Q=>{const Z=g||j,le=A&&Q.ctrlKey,be=Q.type==="wheel";if(Q.button===1&&Q.type==="mousedown"&&(cI(Q,`${J}-flow__node`)||cI(Q,`${J}-flow__edge`)))return!0;if(!x&&!Z&&!D&&!$&&!A||E||te&&!be||cI(Q,H)&&be||cI(Q,U)&&(!be||D&&be&&!g)||!A&&Q.ctrlKey&&be)return!1;if(!A&&Q.type==="touchstart"&&Q.touches?.length>1)return Q.preventDefault(),!1;if(!Z&&!D&&!le&&be||!x&&(Q.type==="mousedown"||Q.type==="touchstart")||Array.isArray(x)&&!x.includes(Q.button)&&Q.type==="mousedown")return!1;const ne=Array.isArray(x)&&x.includes(Q.button)||!Q.button||Q.button<=1;return(!Q.ctrlKey||be)&&ne}}function MGn({domNode:g,minZoom:j,maxZoom:A,translateExtent:x,viewport:D,onPanZoom:$,onPanZoomStart:E,onPanZoomEnd:H,onDraggingChange:U}){const J={isZoomingOrPanning:!1,usedRightMouseButton:!1,prevViewport:{},mouseButton:0,timerId:void 0,panScrollTimeout:void 0,isPanScrolling:!1},te=g.getBoundingClientRect(),Q=Odn().scaleExtent([j,A]).translateExtent(x),Z=Cg(g).call(Q);ze({x:D.x,y:D.y,zoom:hI(D.zoom,j,A)},[[0,0],[te.width,te.height]],x);const le=Z.on("wheel.zoom"),be=Z.on("dblclick.zoom");Q.wheelDelta(r0n);function ne(In,nt){return Z?new Promise(Y=>{Q?.interpolate(nt?.interpolate==="linear"?LG:uue).transform(N7e(Z,nt?.duration,nt?.ease,()=>Y(!0)),In)}):Promise.resolve(!1)}function De({noWheelClassName:In,noPanClassName:nt,onPaneContextMenu:Y,userSelectionActive:Fe,panOnScroll:gn,panOnDrag:Ae,panOnScrollMode:Je,panOnScrollSpeed:un,preventScrolling:jn,zoomOnPinch:En,zoomOnScroll:Me,zoomOnDoubleClick:rn,zoomActivationKeyPressed:ut,lib:st,onTransformChange:Kt,connectionInProgress:li,paneClickDistance:oi,selectionOnDrag:Jt}){Fe&&!J.isZoomingOrPanning&&Se();const hi=gn&&!ut&&!Fe;Q.clickDistance(Jt?1/0:!_m(oi)||oi<0?0:oi);const nc=hi?yGn({zoomPanValues:J,noWheelClassName:In,d3Selection:Z,d3Zoom:Q,panOnScrollMode:Je,panOnScrollSpeed:un,zoomOnPinch:En,onPanZoomStart:E,onPanZoom:$,onPanZoomEnd:H}):kGn({noWheelClassName:In,preventScrolling:jn,d3ZoomHandler:le});if(Z.on("wheel.zoom",nc,{passive:!1}),!Fe){const pr=EGn({zoomPanValues:J,onDraggingChange:U,onPanZoomStart:E});Q.on("start",pr);const Ei=jGn({zoomPanValues:J,panOnDrag:Ae,onPaneContextMenu:!!Y,onPanZoom:$,onTransformChange:Kt});Q.on("zoom",Ei);const Bi=SGn({zoomPanValues:J,panOnDrag:Ae,panOnScroll:gn,onPaneContextMenu:Y,onPanZoomEnd:H,onDraggingChange:U});Q.on("end",Bi)}const Xr=AGn({zoomActivationKeyPressed:ut,panOnDrag:Ae,zoomOnScroll:Me,panOnScroll:gn,zoomOnDoubleClick:rn,zoomOnPinch:En,userSelectionActive:Fe,noPanClassName:nt,noWheelClassName:In,lib:st,connectionInProgress:li});Q.filter(Xr),rn?Z.on("dblclick.zoom",be):Z.on("dblclick.zoom",null)}function Se(){Q.on("zoom",null)}async function ze(In,nt,Y){const Fe=O7e(In),gn=Q?.constrain()(Fe,nt,Y);return gn&&await ne(gn),new Promise(Ae=>Ae(gn))}async function de(In,nt){const Y=O7e(In);return await ne(Y,nt),new Promise(Fe=>Fe(Y))}function Le(In){if(Z){const nt=O7e(In),Y=Z.property("__zoom");(Y.k!==In.zoom||Y.x!==In.x||Y.y!==In.y)&&Q?.transform(Z,nt,null,{sync:!0})}}function an(){const In=Z?Cdn(Z.node()):{x:0,y:0,k:1};return{x:In.x,y:In.y,zoom:In.k}}function ln(In,nt){return Z?new Promise(Y=>{Q?.interpolate(nt?.interpolate==="linear"?LG:uue).scaleTo(N7e(Z,nt?.duration,nt?.ease,()=>Y(!0)),In)}):Promise.resolve(!1)}function on(In,nt){return Z?new Promise(Y=>{Q?.interpolate(nt?.interpolate==="linear"?LG:uue).scaleBy(N7e(Z,nt?.duration,nt?.ease,()=>Y(!0)),In)}):Promise.resolve(!1)}function Mn(In){Q?.scaleExtent(In)}function Nn(In){Q?.translateExtent(In)}function Ft(In){const nt=!_m(In)||In<0?0:In;Q?.clickDistance(nt)}return{update:De,destroy:Se,setViewport:de,setViewportConstrained:ze,getViewport:an,scaleTo:ln,scaleBy:on,setScaleExtent:Mn,setTranslateExtent:Nn,syncViewport:Le,setClickDistance:Ft}}var gI;(function(g){g.Line="line",g.Handle="handle"})(gI||(gI={}));function TGn({width:g,prevWidth:j,height:A,prevHeight:x,affectsX:D,affectsY:$}){const E=g-j,H=A-x,U=[E>0?1:E<0?-1:0,H>0?1:H<0?-1:0];return E&&D&&(U[0]=U[0]*-1),H&&$&&(U[1]=U[1]*-1),U}function h1n(g){const j=g.includes("right")||g.includes("left"),A=g.includes("bottom")||g.includes("top"),x=g.includes("left"),D=g.includes("top");return{isHorizontal:j,isVertical:A,affectsX:x,affectsY:D}}function P7(g,j){return Math.max(0,j-g)}function $7(g,j){return Math.max(0,g-j)}function Qce(g,j,A){return Math.max(0,j-g,g-A)}function d1n(g,j){return g?!j:j}function xGn(g,j,A,x,D,$,E,H){let{affectsX:U,affectsY:J}=j;const{isHorizontal:te,isVertical:Q}=j,Z=te&&Q,{xSnapped:le,ySnapped:be}=A,{minWidth:ne,maxWidth:De,minHeight:Se,maxHeight:ze}=x,{x:de,y:Le,width:an,height:ln,aspectRatio:on}=g;let Mn=Math.floor(te?le-g.pointerX:0),Nn=Math.floor(Q?be-g.pointerY:0);const Ft=an+(U?-Mn:Mn),In=ln+(J?-Nn:Nn),nt=-$[0]*an,Y=-$[1]*ln;let Fe=Qce(Ft,ne,De),gn=Qce(In,Se,ze);if(E){let un=0,jn=0;U&&Mn<0?un=P7(de+Mn+nt,E[0][0]):!U&&Mn>0&&(un=$7(de+Ft+nt,E[1][0])),J&&Nn<0?jn=P7(Le+Nn+Y,E[0][1]):!J&&Nn>0&&(jn=$7(Le+In+Y,E[1][1])),Fe=Math.max(Fe,un),gn=Math.max(gn,jn)}if(H){let un=0,jn=0;U&&Mn>0?un=$7(de+Mn,H[0][0]):!U&&Mn<0&&(un=P7(de+Ft,H[1][0])),J&&Nn>0?jn=$7(Le+Nn,H[0][1]):!J&&Nn<0&&(jn=P7(Le+In,H[1][1])),Fe=Math.max(Fe,un),gn=Math.max(gn,jn)}if(D){if(te){const un=Qce(Ft/on,Se,ze)*on;if(Fe=Math.max(Fe,un),E){let jn=0;!U&&!J||U&&!J&&Z?jn=$7(Le+Y+Ft/on,E[1][1])*on:jn=P7(Le+Y+(U?Mn:-Mn)/on,E[0][1])*on,Fe=Math.max(Fe,jn)}if(H){let jn=0;!U&&!J||U&&!J&&Z?jn=P7(Le+Ft/on,H[1][1])*on:jn=$7(Le+(U?Mn:-Mn)/on,H[0][1])*on,Fe=Math.max(Fe,jn)}}if(Q){const un=Qce(In*on,ne,De)/on;if(gn=Math.max(gn,un),E){let jn=0;!U&&!J||J&&!U&&Z?jn=$7(de+In*on+nt,E[1][0])/on:jn=P7(de+(J?Nn:-Nn)*on+nt,E[0][0])/on,gn=Math.max(gn,jn)}if(H){let jn=0;!U&&!J||J&&!U&&Z?jn=P7(de+In*on,H[1][0])/on:jn=$7(de+(J?Nn:-Nn)*on,H[0][0])/on,gn=Math.max(gn,jn)}}}Nn=Nn+(Nn<0?gn:-gn),Mn=Mn+(Mn<0?Fe:-Fe),D&&(Z?Ft>In*on?Nn=(d1n(U,J)?-Mn:Mn)/on:Mn=(d1n(U,J)?-Nn:Nn)*on:te?(Nn=Mn/on,J=U):(Mn=Nn*on,U=J));const Ae=U?de+Mn:de,Je=J?Le+Nn:Le;return{width:an+(U?-Mn:Mn),height:ln+(J?-Nn:Nn),x:$[0]*Mn*(U?-1:1)+Ae,y:$[1]*Nn*(J?-1:1)+Je}}const c0n={width:0,height:0,x:0,y:0},CGn={...c0n,pointerX:0,pointerY:0,aspectRatio:1};function OGn(g){return[[0,0],[g.measured.width,g.measured.height]]}function NGn(g,j,A){const x=j.position.x+g.position.x,D=j.position.y+g.position.y,$=g.measured.width??0,E=g.measured.height??0,H=A[0]*$,U=A[1]*E;return[[x-H,D-U],[x+$-H,D+E-U]]}function DGn({domNode:g,nodeId:j,getStoreItems:A,onChange:x,onEnd:D}){const $=Cg(g);let E={controlDirection:h1n("bottom-right"),boundaries:{minWidth:0,minHeight:0,maxWidth:Number.MAX_VALUE,maxHeight:Number.MAX_VALUE},resizeDirection:void 0,keepAspectRatio:!1};function H({controlPosition:J,boundaries:te,keepAspectRatio:Q,resizeDirection:Z,onResizeStart:le,onResize:be,onResizeEnd:ne,shouldResize:De}){let Se={...c0n},ze={...CGn};E={boundaries:te,resizeDirection:Z,keepAspectRatio:Q,controlDirection:h1n(J)};let de,Le=null,an=[],ln,on,Mn,Nn=!1;const Ft=gdn().on("start",In=>{const{nodeLookup:nt,transform:Y,snapGrid:Fe,snapToGrid:gn,nodeOrigin:Ae,paneDomNode:Je}=A();if(de=nt.get(j),!de)return;Le=Je?.getBoundingClientRect()??null;const{xSnapped:un,ySnapped:jn}=PG(In.sourceEvent,{transform:Y,snapGrid:Fe,snapToGrid:gn,containerBounds:Le});Se={width:de.measured.width??0,height:de.measured.height??0,x:de.position.x??0,y:de.position.y??0},ze={...Se,pointerX:un,pointerY:jn,aspectRatio:Se.width/Se.height},ln=void 0,de.parentId&&(de.extent==="parent"||de.expandParent)&&(ln=nt.get(de.parentId),on=ln&&de.extent==="parent"?OGn(ln):void 0),an=[],Mn=void 0;for(const[En,Me]of nt)if(Me.parentId===j&&(an.push({id:En,position:{...Me.position},extent:Me.extent}),Me.extent==="parent"||Me.expandParent)){const rn=NGn(Me,de,Me.origin??Ae);Mn?Mn=[[Math.min(rn[0][0],Mn[0][0]),Math.min(rn[0][1],Mn[0][1])],[Math.max(rn[1][0],Mn[1][0]),Math.max(rn[1][1],Mn[1][1])]]:Mn=rn}le?.(In,{...Se})}).on("drag",In=>{const{transform:nt,snapGrid:Y,snapToGrid:Fe,nodeOrigin:gn}=A(),Ae=PG(In.sourceEvent,{transform:nt,snapGrid:Y,snapToGrid:Fe,containerBounds:Le}),Je=[];if(!de)return;const{x:un,y:jn,width:En,height:Me}=Se,rn={},ut=de.origin??gn,{width:st,height:Kt,x:li,y:oi}=xGn(ze,E.controlDirection,Ae,E.boundaries,E.keepAspectRatio,ut,on,Mn),Jt=st!==En,hi=Kt!==Me,nc=li!==un&&Jt,Xr=oi!==jn&&hi;if(!nc&&!Xr&&!Jt&&!hi)return;if((nc||Xr||ut[0]===1||ut[1]===1)&&(rn.x=nc?li:Se.x,rn.y=Xr?oi:Se.y,Se.x=rn.x,Se.y=rn.y,an.length>0)){const Fc=li-un,Du=oi-jn;for(const kl of an)kl.position={x:kl.position.x-Fc+ut[0]*(st-En),y:kl.position.y-Du+ut[1]*(Kt-Me)},Je.push(kl)}if((Jt||hi)&&(rn.width=Jt&&(!E.resizeDirection||E.resizeDirection==="horizontal")?st:Se.width,rn.height=hi&&(!E.resizeDirection||E.resizeDirection==="vertical")?Kt:Se.height,Se.width=rn.width,Se.height=rn.height),ln&&de.expandParent){const Fc=ut[0]*(rn.width??0);rn.x&&rn.x{Nn&&(ne?.(In,{...Se}),D?.({...Se}),Nn=!1)});$.call(Ft)}function U(){$.on(".drag",null)}return{update:H,destroy:U}}var D7e={exports:{}},I7e={},_7e={exports:{}},L7e={};var b1n;function IGn(){if(b1n)return L7e;b1n=1;var g=XG();function j(Q,Z){return Q===Z&&(Q!==0||1/Q===1/Z)||Q!==Q&&Z!==Z}var A=typeof Object.is=="function"?Object.is:j,x=g.useState,D=g.useEffect,$=g.useLayoutEffect,E=g.useDebugValue;function H(Q,Z){var le=Z(),be=x({inst:{value:le,getSnapshot:Z}}),ne=be[0].inst,De=be[1];return $(function(){ne.value=le,ne.getSnapshot=Z,U(ne)&&De({inst:ne})},[Q,le,Z]),D(function(){return U(ne)&&De({inst:ne}),Q(function(){U(ne)&&De({inst:ne})})},[Q]),E(le),le}function U(Q){var Z=Q.getSnapshot;Q=Q.value;try{var le=Z();return!A(Q,le)}catch{return!0}}function J(Q,Z){return Z()}var te=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?J:H;return L7e.useSyncExternalStore=g.useSyncExternalStore!==void 0?g.useSyncExternalStore:te,L7e}var g1n;function _Gn(){return g1n||(g1n=1,_7e.exports=IGn()),_7e.exports}var w1n;function LGn(){if(w1n)return I7e;w1n=1;var g=XG(),j=_Gn();function A(J,te){return J===te&&(J!==0||1/J===1/te)||J!==J&&te!==te}var x=typeof Object.is=="function"?Object.is:A,D=j.useSyncExternalStore,$=g.useRef,E=g.useEffect,H=g.useMemo,U=g.useDebugValue;return I7e.useSyncExternalStoreWithSelector=function(J,te,Q,Z,le){var be=$(null);if(be.current===null){var ne={hasValue:!1,value:null};be.current=ne}else ne=be.current;be=H(function(){function Se(ln){if(!ze){if(ze=!0,de=ln,ln=Z(ln),le!==void 0&&ne.hasValue){var on=ne.value;if(le(on,ln))return Le=on}return Le=ln}if(on=Le,x(de,ln))return on;var Mn=Z(ln);return le!==void 0&&le(on,Mn)?(de=ln,on):(de=ln,Le=Mn)}var ze=!1,de,Le,an=Q===void 0?null:Q;return[function(){return Se(te())},an===null?void 0:function(){return Se(an())}]},[te,Q,Z,le]);var De=D(J,be[0],be[1]);return E(function(){ne.hasValue=!0,ne.value=De},[De]),U(De),De},I7e}var p1n;function PGn(){return p1n||(p1n=1,D7e.exports=LGn()),D7e.exports}var $Gn=PGn();const RGn=rke($Gn),BGn={},m1n=g=>{let j;const A=new Set,x=(te,Q)=>{const Z=typeof te=="function"?te(j):te;if(!Object.is(Z,j)){const le=j;j=Q??(typeof Z!="object"||Z===null)?Z:Object.assign({},j,Z),A.forEach(be=>be(j,le))}},D=()=>j,U={setState:x,getState:D,getInitialState:()=>J,subscribe:te=>(A.add(te),()=>A.delete(te)),destroy:()=>{(BGn?"production":void 0)!=="production"&&console.warn("[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."),A.clear()}},J=j=g(x,D,U);return U},zGn=g=>g?m1n(g):m1n,{useDebugValue:FGn}=KBn,{useSyncExternalStoreWithSelector:HGn}=RGn,JGn=g=>g;function u0n(g,j=JGn,A){const x=HGn(g.subscribe,g.getState,g.getServerState||g.getInitialState,j,A);return FGn(x),x}const v1n=(g,j)=>{const A=zGn(g),x=(D,$=j)=>u0n(A,D,$);return Object.assign(x,A),x},GGn=(g,j)=>g?v1n(g,j):v1n;function vl(g,j){if(Object.is(g,j))return!0;if(typeof g!="object"||g===null||typeof j!="object"||j===null)return!1;if(g instanceof Map&&j instanceof Map){if(g.size!==j.size)return!1;for(const[x,D]of g)if(!Object.is(D,j.get(x)))return!1;return!0}if(g instanceof Set&&j instanceof Set){if(g.size!==j.size)return!1;for(const x of g)if(!j.has(x))return!1;return!0}const A=Object.keys(g);if(A.length!==Object.keys(j).length)return!1;for(const x of A)if(!Object.prototype.hasOwnProperty.call(j,x)||!Object.is(g[x],j[x]))return!1;return!0}var qGn=edn();const Oue=kn.createContext(null),UGn=Oue.Provider,o0n=Vv.error001();function Bu(g,j){const A=kn.useContext(Oue);if(A===null)throw new Error(o0n);return u0n(A,g,j)}function yl(){const g=kn.useContext(Oue);if(g===null)throw new Error(o0n);return kn.useMemo(()=>({getState:g.getState,setState:g.setState,subscribe:g.subscribe}),[g])}const y1n={display:"none"},XGn={position:"absolute",width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0px, 0px, 0px, 0px)",clipPath:"inset(100%)"},s0n="react-flow__node-desc",l0n="react-flow__edge-desc",KGn="react-flow__aria-live",VGn=g=>g.ariaLiveMessage,YGn=g=>g.ariaLabelConfig;function QGn({rfId:g}){const j=Bu(VGn);return re.jsx("div",{id:`${KGn}-${g}`,"aria-live":"assertive","aria-atomic":"true",style:XGn,children:j})}function ZGn({rfId:g,disableKeyboardA11y:j}){const A=Bu(YGn);return re.jsxs(re.Fragment,{children:[re.jsx("div",{id:`${s0n}-${g}`,style:y1n,children:j?A["node.a11yDescription.default"]:A["node.a11yDescription.keyboardDisabled"]}),re.jsx("div",{id:`${l0n}-${g}`,style:y1n,children:A["edge.a11yDescription.default"]}),!j&&re.jsx(QGn,{rfId:g})]})}const Nue=kn.forwardRef(({position:g="top-left",children:j,className:A,style:x,...D},$)=>{const E=`${g}`.split("-");return re.jsx("div",{className:ka(["react-flow__panel",A,...E]),style:x,ref:$,...D,children:j})});Nue.displayName="Panel";function WGn({proOptions:g,position:j="bottom-right"}){return g?.hideAttribution?null:re.jsx(Nue,{position:j,className:"react-flow__attribution","data-message":"Please only hide this attribution when you are subscribed to React Flow Pro: https://pro.reactflow.dev",children:re.jsx("a",{href:"https://reactflow.dev",target:"_blank",rel:"noopener noreferrer","aria-label":"React Flow attribution",children:"React Flow"})})}const eqn=g=>{const j=[],A=[];for(const[,x]of g.nodeLookup)x.selected&&j.push(x.internals.userNode);for(const[,x]of g.edgeLookup)x.selected&&A.push(x);return{selectedNodes:j,selectedEdges:A}},Zce=g=>g.id;function nqn(g,j){return vl(g.selectedNodes.map(Zce),j.selectedNodes.map(Zce))&&vl(g.selectedEdges.map(Zce),j.selectedEdges.map(Zce))}function tqn({onSelectionChange:g}){const j=yl(),{selectedNodes:A,selectedEdges:x}=Bu(eqn,nqn);return kn.useEffect(()=>{const D={nodes:A,edges:x};g?.(D),j.getState().onSelectionChangeHandlers.forEach($=>$(D))},[A,x,g]),null}const iqn=g=>!!g.onSelectionChangeHandlers;function rqn({onSelectionChange:g}){const j=Bu(iqn);return g||j?re.jsx(tqn,{onSelectionChange:g}):null}const tke=typeof window<"u"?kn.useLayoutEffect:kn.useEffect,f0n=[0,0],cqn={x:0,y:0,zoom:1},uqn=["nodes","edges","defaultNodes","defaultEdges","onConnect","onConnectStart","onConnectEnd","onClickConnectStart","onClickConnectEnd","nodesDraggable","autoPanOnNodeFocus","nodesConnectable","nodesFocusable","edgesFocusable","edgesReconnectable","elevateNodesOnSelect","elevateEdgesOnSelect","minZoom","maxZoom","nodeExtent","onNodesChange","onEdgesChange","elementsSelectable","connectionMode","snapGrid","snapToGrid","translateExtent","connectOnClick","defaultEdgeOptions","fitView","fitViewOptions","onNodesDelete","onEdgesDelete","onDelete","onNodeDrag","onNodeDragStart","onNodeDragStop","onSelectionDrag","onSelectionDragStart","onSelectionDragStop","onMoveStart","onMove","onMoveEnd","noPanClassName","nodeOrigin","autoPanOnConnect","autoPanOnNodeDrag","onError","connectionRadius","isValidConnection","selectNodesOnDrag","nodeDragThreshold","connectionDragThreshold","onBeforeDelete","debug","autoPanSpeed","ariaLabelConfig","zIndexMode"],k1n=[...uqn,"rfId"],oqn=g=>({setNodes:g.setNodes,setEdges:g.setEdges,setMinZoom:g.setMinZoom,setMaxZoom:g.setMaxZoom,setTranslateExtent:g.setTranslateExtent,setNodeExtent:g.setNodeExtent,reset:g.reset,setDefaultNodesAndEdges:g.setDefaultNodesAndEdges}),E1n={translateExtent:FG,nodeOrigin:f0n,minZoom:.5,maxZoom:2,elementsSelectable:!0,noPanClassName:"nopan",rfId:"1"};function sqn(g){const{setNodes:j,setEdges:A,setMinZoom:x,setMaxZoom:D,setTranslateExtent:$,setNodeExtent:E,reset:H,setDefaultNodesAndEdges:U}=Bu(oqn,vl),J=yl();tke(()=>(U(g.defaultNodes,g.defaultEdges),()=>{te.current=E1n,H()}),[]);const te=kn.useRef(E1n);return tke(()=>{for(const Q of k1n){const Z=g[Q],le=te.current[Q];Z!==le&&(typeof g[Q]>"u"||(Q==="nodes"?j(Z):Q==="edges"?A(Z):Q==="minZoom"?x(Z):Q==="maxZoom"?D(Z):Q==="translateExtent"?$(Z):Q==="nodeExtent"?E(Z):Q==="ariaLabelConfig"?J.setState({ariaLabelConfig:GJn(Z)}):Q==="fitView"?J.setState({fitViewQueued:Z}):Q==="fitViewOptions"?J.setState({fitViewOptions:Z}):J.setState({[Q]:Z})))}te.current=g},k1n.map(Q=>g[Q])),null}function j1n(){return typeof window>"u"||!window.matchMedia?null:window.matchMedia("(prefers-color-scheme: dark)")}function lqn(g){const[j,A]=kn.useState(g==="system"?null:g);return kn.useEffect(()=>{if(g!=="system"){A(g);return}const x=j1n(),D=()=>A(x?.matches?"dark":"light");return D(),x?.addEventListener("change",D),()=>{x?.removeEventListener("change",D)}},[g]),j!==null?j:j1n()?.matches?"dark":"light"}const S1n=typeof document<"u"?document:null;function UG(g=null,j={target:S1n,actInsideInputWithModifier:!0}){const[A,x]=kn.useState(!1),D=kn.useRef(!1),$=kn.useRef(new Set([])),[E,H]=kn.useMemo(()=>{if(g!==null){const J=(Array.isArray(g)?g:[g]).filter(Q=>typeof Q=="string").map(Q=>Q.replace("+",` -`).replace(` - -`,` -+`).split(` -`)),te=J.reduce((Q,Z)=>Q.concat(...Z),[]);return[J,te]}return[[],[]]},[g]);return kn.useEffect(()=>{const U=j?.target??S1n,J=j?.actInsideInputWithModifier??!0;if(g!==null){const te=le=>{if(D.current=le.ctrlKey||le.metaKey||le.shiftKey||le.altKey,(!D.current||D.current&&!J)&&Jdn(le))return!1;const ne=M1n(le.code,H);if($.current.add(le[ne]),A1n(E,$.current,!1)){const De=le.composedPath?.()?.[0]||le.target,Se=De?.nodeName==="BUTTON"||De?.nodeName==="A";j.preventDefault!==!1&&(D.current||!Se)&&le.preventDefault(),x(!0)}},Q=le=>{const be=M1n(le.code,H);A1n(E,$.current,!0)?(x(!1),$.current.clear()):$.current.delete(le[be]),le.key==="Meta"&&$.current.clear(),D.current=!1},Z=()=>{$.current.clear(),x(!1)};return U?.addEventListener("keydown",te),U?.addEventListener("keyup",Q),window.addEventListener("blur",Z),window.addEventListener("contextmenu",Z),()=>{U?.removeEventListener("keydown",te),U?.removeEventListener("keyup",Q),window.removeEventListener("blur",Z),window.removeEventListener("contextmenu",Z)}}},[g,x]),A}function A1n(g,j,A){return g.filter(x=>A||x.length===j.size).some(x=>x.every(D=>j.has(D)))}function M1n(g,j){return j.includes(g)?"code":"key"}const fqn=()=>{const g=yl();return kn.useMemo(()=>({zoomIn:j=>{const{panZoom:A}=g.getState();return A?A.scaleBy(1.2,j):Promise.resolve(!1)},zoomOut:j=>{const{panZoom:A}=g.getState();return A?A.scaleBy(1/1.2,j):Promise.resolve(!1)},zoomTo:(j,A)=>{const{panZoom:x}=g.getState();return x?x.scaleTo(j,A):Promise.resolve(!1)},getZoom:()=>g.getState().transform[2],setViewport:async(j,A)=>{const{transform:[x,D,$],panZoom:E}=g.getState();return E?(await E.setViewport({x:j.x??x,y:j.y??D,zoom:j.zoom??$},A),Promise.resolve(!0)):Promise.resolve(!1)},getViewport:()=>{const[j,A,x]=g.getState().transform;return{x:j,y:A,zoom:x}},setCenter:async(j,A,x)=>g.getState().setCenter(j,A,x),fitBounds:async(j,A)=>{const{width:x,height:D,minZoom:$,maxZoom:E,panZoom:H}=g.getState(),U=bke(j,x,D,$,E,A?.padding??.1);return H?(await H.setViewport(U,{duration:A?.duration,ease:A?.ease,interpolate:A?.interpolate}),Promise.resolve(!0)):Promise.resolve(!1)},screenToFlowPosition:(j,A={})=>{const{transform:x,snapGrid:D,snapToGrid:$,domNode:E}=g.getState();if(!E)return j;const{x:H,y:U}=E.getBoundingClientRect(),J={x:j.x-H,y:j.y-U},te=A.snapGrid??D,Q=A.snapToGrid??$;return WG(J,x,Q,te)},flowToScreenPosition:j=>{const{transform:A,domNode:x}=g.getState();if(!x)return j;const{x:D,y:$}=x.getBoundingClientRect(),E=mue(j,A);return{x:E.x+D,y:E.y+$}}}),[])};function a0n(g,j){const A=[],x=new Map,D=[];for(const $ of g)if($.type==="add"){D.push($);continue}else if($.type==="remove"||$.type==="replace")x.set($.id,[$]);else{const E=x.get($.id);E?E.push($):x.set($.id,[$])}for(const $ of j){const E=x.get($.id);if(!E){A.push($);continue}if(E[0].type==="remove")continue;if(E[0].type==="replace"){A.push({...E[0].item});continue}const H={...$};for(const U of E)aqn(U,H);A.push(H)}return D.length&&D.forEach($=>{$.index!==void 0?A.splice($.index,0,{...$.item}):A.push({...$.item})}),A}function aqn(g,j){switch(g.type){case"select":{j.selected=g.selected;break}case"position":{typeof g.position<"u"&&(j.position=g.position),typeof g.dragging<"u"&&(j.dragging=g.dragging);break}case"dimensions":{typeof g.dimensions<"u"&&(j.measured={...g.dimensions},g.setAttributes&&((g.setAttributes===!0||g.setAttributes==="width")&&(j.width=g.dimensions.width),(g.setAttributes===!0||g.setAttributes==="height")&&(j.height=g.dimensions.height))),typeof g.resizing=="boolean"&&(j.resizing=g.resizing);break}}}function h0n(g,j){return a0n(g,j)}function d0n(g,j){return a0n(g,j)}function lM(g,j){return{id:g,type:"select",selected:j}}function uI(g,j=new Set,A=!1){const x=[];for(const[D,$]of g){const E=j.has(D);!($.selected===void 0&&!E)&&$.selected!==E&&(A&&($.selected=E),x.push(lM($.id,E)))}return x}function T1n({items:g=[],lookup:j}){const A=[],x=new Map(g.map(D=>[D.id,D]));for(const[D,$]of g.entries()){const E=j.get($.id),H=E?.internals?.userNode??E;H!==void 0&&H!==$&&A.push({id:$.id,item:$,type:"replace"}),H===void 0&&A.push({item:$,type:"add",index:D})}for(const[D]of j)x.get(D)===void 0&&A.push({id:D,type:"remove"});return A}function x1n(g){return{id:g.id,type:"remove"}}const C1n=g=>_Jn(g),hqn=g=>Ldn(g);function b0n(g){return kn.forwardRef(g)}function O1n(g){const[j,A]=kn.useState(BigInt(0)),[x]=kn.useState(()=>dqn(()=>A(D=>D+BigInt(1))));return tke(()=>{const D=x.get();D.length&&(g(D),x.reset())},[j]),x}function dqn(g){let j=[];return{get:()=>j,reset:()=>{j=[]},push:A=>{j.push(A),g()}}}const g0n=kn.createContext(null);function bqn({children:g}){const j=yl(),A=kn.useCallback(H=>{const{nodes:U=[],setNodes:J,hasDefaultNodes:te,onNodesChange:Q,nodeLookup:Z,fitViewQueued:le,onNodesChangeMiddlewareMap:be}=j.getState();let ne=U;for(const Se of H)ne=typeof Se=="function"?Se(ne):Se;let De=T1n({items:ne,lookup:Z});for(const Se of be.values())De=Se(De);te&&J(ne),De.length>0?Q?.(De):le&&window.requestAnimationFrame(()=>{const{fitViewQueued:Se,nodes:ze,setNodes:de}=j.getState();Se&&de(ze)})},[]),x=O1n(A),D=kn.useCallback(H=>{const{edges:U=[],setEdges:J,hasDefaultEdges:te,onEdgesChange:Q,edgeLookup:Z}=j.getState();let le=U;for(const be of H)le=typeof be=="function"?be(le):be;te?J(le):Q&&Q(T1n({items:le,lookup:Z}))},[]),$=O1n(D),E=kn.useMemo(()=>({nodeQueue:x,edgeQueue:$}),[]);return re.jsx(g0n.Provider,{value:E,children:g})}function gqn(){const g=kn.useContext(g0n);if(!g)throw new Error("useBatchContext must be used within a BatchProvider");return g}const wqn=g=>!!g.panZoom;function kke(){const g=fqn(),j=yl(),A=gqn(),x=Bu(wqn),D=kn.useMemo(()=>{const $=Q=>j.getState().nodeLookup.get(Q),E=Q=>{A.nodeQueue.push(Q)},H=Q=>{A.edgeQueue.push(Q)},U=Q=>{const{nodeLookup:Z,nodeOrigin:le}=j.getState(),be=C1n(Q)?Q:Z.get(Q.id),ne=be.parentId?Fdn(be.position,be.measured,be.parentId,Z,le):be.position,De={...be,position:ne,width:be.measured?.width??be.width,height:be.measured?.height??be.height};return dI(De)},J=(Q,Z,le={replace:!1})=>{E(be=>be.map(ne=>{if(ne.id===Q){const De=typeof Z=="function"?Z(ne):Z;return le.replace&&C1n(De)?De:{...ne,...De}}return ne}))},te=(Q,Z,le={replace:!1})=>{H(be=>be.map(ne=>{if(ne.id===Q){const De=typeof Z=="function"?Z(ne):Z;return le.replace&&hqn(De)?De:{...ne,...De}}return ne}))};return{getNodes:()=>j.getState().nodes.map(Q=>({...Q})),getNode:Q=>$(Q)?.internals.userNode,getInternalNode:$,getEdges:()=>{const{edges:Q=[]}=j.getState();return Q.map(Z=>({...Z}))},getEdge:Q=>j.getState().edgeLookup.get(Q),setNodes:E,setEdges:H,addNodes:Q=>{const Z=Array.isArray(Q)?Q:[Q];A.nodeQueue.push(le=>[...le,...Z])},addEdges:Q=>{const Z=Array.isArray(Q)?Q:[Q];A.edgeQueue.push(le=>[...le,...Z])},toObject:()=>{const{nodes:Q=[],edges:Z=[],transform:le}=j.getState(),[be,ne,De]=le;return{nodes:Q.map(Se=>({...Se})),edges:Z.map(Se=>({...Se})),viewport:{x:be,y:ne,zoom:De}}},deleteElements:async({nodes:Q=[],edges:Z=[]})=>{const{nodes:le,edges:be,onNodesDelete:ne,onEdgesDelete:De,triggerNodeChanges:Se,triggerEdgeChanges:ze,onDelete:de,onBeforeDelete:Le}=j.getState(),{nodes:an,edges:ln}=await BJn({nodesToRemove:Q,edgesToRemove:Z,nodes:le,edges:be,onBeforeDelete:Le}),on=ln.length>0,Mn=an.length>0;if(on){const Nn=ln.map(x1n);De?.(ln),ze(Nn)}if(Mn){const Nn=an.map(x1n);ne?.(an),Se(Nn)}return(Mn||on)&&de?.({nodes:an,edges:ln}),{deletedNodes:an,deletedEdges:ln}},getIntersectingNodes:(Q,Z=!0,le)=>{const be=n1n(Q),ne=be?Q:U(Q),De=le!==void 0;return ne?(le||j.getState().nodes).filter(Se=>{const ze=j.getState().nodeLookup.get(Se.id);if(ze&&!be&&(Se.id===Q.id||!ze.internals.positionAbsolute))return!1;const de=dI(De?Se:ze),Le=GG(de,ne);return Z&&Le>0||Le>=de.width*de.height||Le>=ne.width*ne.height}):[]},isNodeIntersecting:(Q,Z,le=!0)=>{const ne=n1n(Q)?Q:U(Q);if(!ne)return!1;const De=GG(ne,Z);return le&&De>0||De>=Z.width*Z.height||De>=ne.width*ne.height},updateNode:J,updateNodeData:(Q,Z,le={replace:!1})=>{J(Q,be=>{const ne=typeof Z=="function"?Z(be):Z;return le.replace?{...be,data:ne}:{...be,data:{...be.data,...ne}}},le)},updateEdge:te,updateEdgeData:(Q,Z,le={replace:!1})=>{te(Q,be=>{const ne=typeof Z=="function"?Z(be):Z;return le.replace?{...be,data:ne}:{...be,data:{...be.data,...ne}}},le)},getNodesBounds:Q=>{const{nodeLookup:Z,nodeOrigin:le}=j.getState();return LJn(Q,{nodeLookup:Z,nodeOrigin:le})},getHandleConnections:({type:Q,id:Z,nodeId:le})=>Array.from(j.getState().connectionLookup.get(`${le}-${Q}${Z?`-${Z}`:""}`)?.values()??[]),getNodeConnections:({type:Q,handleId:Z,nodeId:le})=>Array.from(j.getState().connectionLookup.get(`${le}${Q?Z?`-${Q}-${Z}`:`-${Q}`:""}`)?.values()??[]),fitView:async Q=>{const Z=j.getState().fitViewResolver??JJn();return j.setState({fitViewQueued:!0,fitViewOptions:Q,fitViewResolver:Z}),A.nodeQueue.push(le=>[...le]),Z.promise}}},[]);return kn.useMemo(()=>({...D,...g,viewportInitialized:x}),[x])}const N1n=g=>g.selected,pqn=typeof window<"u"?window:void 0;function mqn({deleteKeyCode:g,multiSelectionKeyCode:j}){const A=yl(),{deleteElements:x}=kke(),D=UG(g,{actInsideInputWithModifier:!1}),$=UG(j,{target:pqn});kn.useEffect(()=>{if(D){const{edges:E,nodes:H}=A.getState();x({nodes:H.filter(N1n),edges:E.filter(N1n)}),A.setState({nodesSelectionActive:!1})}},[D]),kn.useEffect(()=>{A.setState({multiSelectionActive:$})},[$])}function vqn(g){const j=yl();kn.useEffect(()=>{const A=()=>{if(!g.current||!(g.current.checkVisibility?.()??!0))return!1;const x=gke(g.current);(x.height===0||x.width===0)&&j.getState().onError?.("004",Vv.error004()),j.setState({width:x.width||500,height:x.height||500})};if(g.current){A(),window.addEventListener("resize",A);const x=new ResizeObserver(()=>A());return x.observe(g.current),()=>{window.removeEventListener("resize",A),x&&g.current&&x.unobserve(g.current)}}},[])}const Due={position:"absolute",width:"100%",height:"100%",top:0,left:0},yqn=g=>({userSelectionActive:g.userSelectionActive,lib:g.lib,connectionInProgress:g.connection.inProgress});function kqn({onPaneContextMenu:g,zoomOnScroll:j=!0,zoomOnPinch:A=!0,panOnScroll:x=!1,panOnScrollSpeed:D=.5,panOnScrollMode:$=hM.Free,zoomOnDoubleClick:E=!0,panOnDrag:H=!0,defaultViewport:U,translateExtent:J,minZoom:te,maxZoom:Q,zoomActivationKeyCode:Z,preventScrolling:le=!0,children:be,noWheelClassName:ne,noPanClassName:De,onViewportChange:Se,isControlledViewport:ze,paneClickDistance:de,selectionOnDrag:Le}){const an=yl(),ln=kn.useRef(null),{userSelectionActive:on,lib:Mn,connectionInProgress:Nn}=Bu(yqn,vl),Ft=UG(Z),In=kn.useRef();vqn(ln);const nt=kn.useCallback(Y=>{Se?.({x:Y[0],y:Y[1],zoom:Y[2]}),ze||an.setState({transform:Y})},[Se,ze]);return kn.useEffect(()=>{if(ln.current){In.current=MGn({domNode:ln.current,minZoom:te,maxZoom:Q,translateExtent:J,viewport:U,onDraggingChange:Ae=>an.setState(Je=>Je.paneDragging===Ae?Je:{paneDragging:Ae}),onPanZoomStart:(Ae,Je)=>{const{onViewportChangeStart:un,onMoveStart:jn}=an.getState();jn?.(Ae,Je),un?.(Je)},onPanZoom:(Ae,Je)=>{const{onViewportChange:un,onMove:jn}=an.getState();jn?.(Ae,Je),un?.(Je)},onPanZoomEnd:(Ae,Je)=>{const{onViewportChangeEnd:un,onMoveEnd:jn}=an.getState();jn?.(Ae,Je),un?.(Je)}});const{x:Y,y:Fe,zoom:gn}=In.current.getViewport();return an.setState({panZoom:In.current,transform:[Y,Fe,gn],domNode:ln.current.closest(".react-flow")}),()=>{In.current?.destroy()}}},[]),kn.useEffect(()=>{In.current?.update({onPaneContextMenu:g,zoomOnScroll:j,zoomOnPinch:A,panOnScroll:x,panOnScrollSpeed:D,panOnScrollMode:$,zoomOnDoubleClick:E,panOnDrag:H,zoomActivationKeyPressed:Ft,preventScrolling:le,noPanClassName:De,userSelectionActive:on,noWheelClassName:ne,lib:Mn,onTransformChange:nt,connectionInProgress:Nn,selectionOnDrag:Le,paneClickDistance:de})},[g,j,A,x,D,$,E,H,Ft,le,De,on,ne,Mn,nt,Nn,Le,de]),re.jsx("div",{className:"react-flow__renderer",ref:ln,style:Due,children:be})}const Eqn=g=>({userSelectionActive:g.userSelectionActive,userSelectionRect:g.userSelectionRect});function jqn(){const{userSelectionActive:g,userSelectionRect:j}=Bu(Eqn,vl);return g&&j?re.jsx("div",{className:"react-flow__selection react-flow__container",style:{width:j.width,height:j.height,transform:`translate(${j.x}px, ${j.y}px)`}}):null}const P7e=(g,j)=>A=>{A.target===j.current&&g?.(A)},Sqn=g=>({userSelectionActive:g.userSelectionActive,elementsSelectable:g.elementsSelectable,connectionInProgress:g.connection.inProgress,dragging:g.paneDragging});function Aqn({isSelecting:g,selectionKeyPressed:j,selectionMode:A=HG.Full,panOnDrag:x,paneClickDistance:D,selectionOnDrag:$,onSelectionStart:E,onSelectionEnd:H,onPaneClick:U,onPaneContextMenu:J,onPaneScroll:te,onPaneMouseEnter:Q,onPaneMouseMove:Z,onPaneMouseLeave:le,children:be}){const ne=yl(),{userSelectionActive:De,elementsSelectable:Se,dragging:ze,connectionInProgress:de}=Bu(Sqn,vl),Le=Se&&(g||De),an=kn.useRef(null),ln=kn.useRef(),on=kn.useRef(new Set),Mn=kn.useRef(new Set),Nn=kn.useRef(!1),Ft=un=>{if(Nn.current||de){Nn.current=!1;return}U?.(un),ne.getState().resetSelectedElements(),ne.setState({nodesSelectionActive:!1})},In=un=>{if(Array.isArray(x)&&x?.includes(2)){un.preventDefault();return}J?.(un)},nt=te?un=>te(un):void 0,Y=un=>{Nn.current&&(un.stopPropagation(),Nn.current=!1)},Fe=un=>{const{domNode:jn}=ne.getState();if(ln.current=jn?.getBoundingClientRect(),!ln.current)return;const En=un.target===an.current;if(!En&&!!un.target.closest(".nokey")||!g||!($&&En||j)||un.button!==0||!un.isPrimary)return;un.target?.setPointerCapture?.(un.pointerId),Nn.current=!1;const{x:ut,y:st}=Lm(un.nativeEvent,ln.current);ne.setState({userSelectionRect:{width:0,height:0,startX:ut,startY:st,x:ut,y:st}}),En||(un.stopPropagation(),un.preventDefault())},gn=un=>{const{userSelectionRect:jn,transform:En,nodeLookup:Me,edgeLookup:rn,connectionLookup:ut,triggerNodeChanges:st,triggerEdgeChanges:Kt,defaultEdgeOptions:li,resetSelectedElements:oi}=ne.getState();if(!ln.current||!jn)return;const{x:Jt,y:hi}=Lm(un.nativeEvent,ln.current),{startX:nc,startY:Xr}=jn;if(!Nn.current){const Du=j?0:D;if(Math.hypot(Jt-nc,hi-Xr)<=Du)return;oi(),E?.(un)}Nn.current=!0;const pr={startX:nc,startY:Xr,x:JtDu.id)),Mn.current=new Set;const Fc=li?.selectable??!0;for(const Du of on.current){const kl=ut.get(Du);if(kl)for(const{edgeId:s1}of kl.values()){const Za=rn.get(s1);Za&&(Za.selectable??Fc)&&Mn.current.add(s1)}}if(!t1n(Ei,on.current)){const Du=uI(Me,on.current,!0);st(Du)}if(!t1n(Bi,Mn.current)){const Du=uI(rn,Mn.current);Kt(Du)}ne.setState({userSelectionRect:pr,userSelectionActive:!0,nodesSelectionActive:!1})},Ae=un=>{un.button===0&&(un.target?.releasePointerCapture?.(un.pointerId),!De&&un.target===an.current&&ne.getState().userSelectionRect&&Ft?.(un),ne.setState({userSelectionActive:!1,userSelectionRect:null}),Nn.current&&(H?.(un),ne.setState({nodesSelectionActive:on.current.size>0})))},Je=x===!0||Array.isArray(x)&&x.includes(0);return re.jsxs("div",{className:ka(["react-flow__pane",{draggable:Je,dragging:ze,selection:g}]),onClick:Le?void 0:P7e(Ft,an),onContextMenu:P7e(In,an),onWheel:P7e(nt,an),onPointerEnter:Le?void 0:Q,onPointerMove:Le?gn:Z,onPointerUp:Le?Ae:void 0,onPointerDownCapture:Le?Fe:void 0,onClickCapture:Le?Y:void 0,onPointerLeave:le,ref:an,style:Due,children:[be,re.jsx(jqn,{})]})}function ike({id:g,store:j,unselect:A=!1,nodeRef:x}){const{addSelectedNodes:D,unselectNodesAndEdges:$,multiSelectionActive:E,nodeLookup:H,onError:U}=j.getState(),J=H.get(g);if(!J){U?.("012",Vv.error012(g));return}j.setState({nodesSelectionActive:!1}),J.selected?(A||J.selected&&E)&&($({nodes:[J],edges:[]}),requestAnimationFrame(()=>x?.current?.blur())):D([g])}function w0n({nodeRef:g,disabled:j=!1,noDragClassName:A,handleSelector:x,nodeId:D,isSelectable:$,nodeClickDistance:E}){const H=yl(),[U,J]=kn.useState(!1),te=kn.useRef();return kn.useEffect(()=>{te.current=hGn({getStoreItems:()=>H.getState(),onNodeMouseDown:Q=>{ike({id:Q,store:H,nodeRef:g})},onDragStart:()=>{J(!0)},onDragStop:()=>{J(!1)}})},[]),kn.useEffect(()=>{if(!(j||!g.current||!te.current))return te.current.update({noDragClassName:A,handleSelector:x,domNode:g.current,isSelectable:$,nodeId:D,nodeClickDistance:E}),()=>{te.current?.destroy()}},[A,x,j,$,g,D,E]),U}const Mqn=g=>j=>j.selected&&(j.draggable||g&&typeof j.draggable>"u");function p0n(){const g=yl();return kn.useCallback(A=>{const{nodeExtent:x,snapToGrid:D,snapGrid:$,nodesDraggable:E,onError:H,updateNodePositions:U,nodeLookup:J,nodeOrigin:te}=g.getState(),Q=new Map,Z=Mqn(E),le=D?$[0]:5,be=D?$[1]:5,ne=A.direction.x*le*A.factor,De=A.direction.y*be*A.factor;for(const[,Se]of J){if(!Z(Se))continue;let ze={x:Se.internals.positionAbsolute.x+ne,y:Se.internals.positionAbsolute.y+De};D&&(ze=ZG(ze,$));const{position:de,positionAbsolute:Le}=Pdn({nodeId:Se.id,nextPosition:ze,nodeLookup:J,nodeExtent:x,nodeOrigin:te,onError:H});Se.position=de,Se.internals.positionAbsolute=Le,Q.set(Se.id,Se)}U(Q)},[])}const Eke=kn.createContext(null),Tqn=Eke.Provider;Eke.Consumer;const m0n=()=>kn.useContext(Eke),xqn=g=>({connectOnClick:g.connectOnClick,noPanClassName:g.noPanClassName,rfId:g.rfId}),Cqn=(g,j,A)=>x=>{const{connectionClickStartHandle:D,connectionMode:$,connection:E}=x,{fromHandle:H,toHandle:U,isValid:J}=E,te=U?.nodeId===g&&U?.id===j&&U?.type===A;return{connectingFrom:H?.nodeId===g&&H?.id===j&&H?.type===A,connectingTo:te,clickConnecting:D?.nodeId===g&&D?.id===j&&D?.type===A,isPossibleEndHandle:$===aI.Strict?H?.type!==A:g!==H?.nodeId||j!==H?.id,connectionInProcess:!!H,clickConnectionInProcess:!!D,valid:te&&J}};function Oqn({type:g="source",position:j=cr.Top,isValidConnection:A,isConnectable:x=!0,isConnectableStart:D=!0,isConnectableEnd:$=!0,id:E,onConnect:H,children:U,className:J,onMouseDown:te,onTouchStart:Q,...Z},le){const be=E||null,ne=g==="target",De=yl(),Se=m0n(),{connectOnClick:ze,noPanClassName:de,rfId:Le}=Bu(xqn,vl),{connectingFrom:an,connectingTo:ln,clickConnecting:on,isPossibleEndHandle:Mn,connectionInProcess:Nn,clickConnectionInProcess:Ft,valid:In}=Bu(Cqn(Se,be,g),vl);Se||De.getState().onError?.("010",Vv.error010());const nt=gn=>{const{defaultEdgeOptions:Ae,onConnect:Je,hasDefaultEdges:un}=De.getState(),jn={...Ae,...gn};if(un){const{edges:En,setEdges:Me}=De.getState();Me(YJn(jn,En))}Je?.(jn),H?.(jn)},Y=gn=>{if(!Se)return;const Ae=Gdn(gn.nativeEvent);if(D&&(Ae&&gn.button===0||!Ae)){const Je=De.getState();nke.onPointerDown(gn.nativeEvent,{handleDomNode:gn.currentTarget,autoPanOnConnect:Je.autoPanOnConnect,connectionMode:Je.connectionMode,connectionRadius:Je.connectionRadius,domNode:Je.domNode,nodeLookup:Je.nodeLookup,lib:Je.lib,isTarget:ne,handleId:be,nodeId:Se,flowId:Je.rfId,panBy:Je.panBy,cancelConnection:Je.cancelConnection,onConnectStart:Je.onConnectStart,onConnectEnd:(...un)=>De.getState().onConnectEnd?.(...un),updateConnection:Je.updateConnection,onConnect:nt,isValidConnection:A||((...un)=>De.getState().isValidConnection?.(...un)??!0),getTransform:()=>De.getState().transform,getFromHandle:()=>De.getState().connection.fromHandle,autoPanSpeed:Je.autoPanSpeed,dragThreshold:Je.connectionDragThreshold})}Ae?te?.(gn):Q?.(gn)},Fe=gn=>{const{onClickConnectStart:Ae,onClickConnectEnd:Je,connectionClickStartHandle:un,connectionMode:jn,isValidConnection:En,lib:Me,rfId:rn,nodeLookup:ut,connection:st}=De.getState();if(!Se||!un&&!D)return;if(!un){Ae?.(gn.nativeEvent,{nodeId:Se,handleId:be,handleType:g}),De.setState({connectionClickStartHandle:{nodeId:Se,type:g,id:be}});return}const Kt=Hdn(gn.target),li=A||En,{connection:oi,isValid:Jt}=nke.isValid(gn.nativeEvent,{handle:{nodeId:Se,id:be,type:g},connectionMode:jn,fromNodeId:un.nodeId,fromHandleId:un.id||null,fromType:un.type,isValidConnection:li,flowId:rn,doc:Kt,lib:Me,nodeLookup:ut});Jt&&oi&&nt(oi);const hi=structuredClone(st);delete hi.inProgress,hi.toPosition=hi.toHandle?hi.toHandle.position:null,Je?.(gn,hi),De.setState({connectionClickStartHandle:null})};return re.jsx("div",{"data-handleid":be,"data-nodeid":Se,"data-handlepos":j,"data-id":`${Le}-${Se}-${be}-${g}`,className:ka(["react-flow__handle",`react-flow__handle-${j}`,"nodrag",de,J,{source:!ne,target:ne,connectable:x,connectablestart:D,connectableend:$,clickconnecting:on,connectingfrom:an,connectingto:ln,valid:In,connectionindicator:x&&(!Nn||Mn)&&(Nn||Ft?$:D)}]),onMouseDown:Y,onTouchStart:Y,onClick:ze?Fe:void 0,ref:le,...Z,children:U})}const Yv=kn.memo(b0n(Oqn));function Nqn({data:g,isConnectable:j,sourcePosition:A=cr.Bottom}){return re.jsxs(re.Fragment,{children:[g?.label,re.jsx(Yv,{type:"source",position:A,isConnectable:j})]})}function Dqn({data:g,isConnectable:j,targetPosition:A=cr.Top,sourcePosition:x=cr.Bottom}){return re.jsxs(re.Fragment,{children:[re.jsx(Yv,{type:"target",position:A,isConnectable:j}),g?.label,re.jsx(Yv,{type:"source",position:x,isConnectable:j})]})}function Iqn(){return null}function _qn({data:g,isConnectable:j,targetPosition:A=cr.Top}){return re.jsxs(re.Fragment,{children:[re.jsx(Yv,{type:"target",position:A,isConnectable:j}),g?.label]})}const yue={ArrowUp:{x:0,y:-1},ArrowDown:{x:0,y:1},ArrowLeft:{x:-1,y:0},ArrowRight:{x:1,y:0}},D1n={input:Nqn,default:Dqn,output:_qn,group:Iqn};function Lqn(g){return g.internals.handleBounds===void 0?{width:g.width??g.initialWidth??g.style?.width,height:g.height??g.initialHeight??g.style?.height}:{width:g.width??g.style?.width,height:g.height??g.style?.height}}const Pqn=g=>{const{width:j,height:A,x,y:D}=QG(g.nodeLookup,{filter:$=>!!$.selected});return{width:_m(j)?j:null,height:_m(A)?A:null,userSelectionActive:g.userSelectionActive,transformString:`translate(${g.transform[0]}px,${g.transform[1]}px) scale(${g.transform[2]}) translate(${x}px,${D}px)`}};function $qn({onSelectionContextMenu:g,noPanClassName:j,disableKeyboardA11y:A}){const x=yl(),{width:D,height:$,transformString:E,userSelectionActive:H}=Bu(Pqn,vl),U=p0n(),J=kn.useRef(null);kn.useEffect(()=>{A||J.current?.focus({preventScroll:!0})},[A]);const te=!H&&D!==null&&$!==null;if(w0n({nodeRef:J,disabled:!te}),!te)return null;const Q=g?le=>{const be=x.getState().nodes.filter(ne=>ne.selected);g(le,be)}:void 0,Z=le=>{Object.prototype.hasOwnProperty.call(yue,le.key)&&(le.preventDefault(),U({direction:yue[le.key],factor:le.shiftKey?4:1}))};return re.jsx("div",{className:ka(["react-flow__nodesselection","react-flow__container",j]),style:{transform:E},children:re.jsx("div",{ref:J,className:"react-flow__nodesselection-rect",onContextMenu:Q,tabIndex:A?void 0:-1,onKeyDown:A?void 0:Z,style:{width:D,height:$}})})}const I1n=typeof window<"u"?window:void 0,Rqn=g=>({nodesSelectionActive:g.nodesSelectionActive,userSelectionActive:g.userSelectionActive});function v0n({children:g,onPaneClick:j,onPaneMouseEnter:A,onPaneMouseMove:x,onPaneMouseLeave:D,onPaneContextMenu:$,onPaneScroll:E,paneClickDistance:H,deleteKeyCode:U,selectionKeyCode:J,selectionOnDrag:te,selectionMode:Q,onSelectionStart:Z,onSelectionEnd:le,multiSelectionKeyCode:be,panActivationKeyCode:ne,zoomActivationKeyCode:De,elementsSelectable:Se,zoomOnScroll:ze,zoomOnPinch:de,panOnScroll:Le,panOnScrollSpeed:an,panOnScrollMode:ln,zoomOnDoubleClick:on,panOnDrag:Mn,defaultViewport:Nn,translateExtent:Ft,minZoom:In,maxZoom:nt,preventScrolling:Y,onSelectionContextMenu:Fe,noWheelClassName:gn,noPanClassName:Ae,disableKeyboardA11y:Je,onViewportChange:un,isControlledViewport:jn}){const{nodesSelectionActive:En,userSelectionActive:Me}=Bu(Rqn,vl),rn=UG(J,{target:I1n}),ut=UG(ne,{target:I1n}),st=ut||Mn,Kt=ut||Le,li=te&&st!==!0,oi=rn||Me||li;return mqn({deleteKeyCode:U,multiSelectionKeyCode:be}),re.jsx(kqn,{onPaneContextMenu:$,elementsSelectable:Se,zoomOnScroll:ze,zoomOnPinch:de,panOnScroll:Kt,panOnScrollSpeed:an,panOnScrollMode:ln,zoomOnDoubleClick:on,panOnDrag:!rn&&st,defaultViewport:Nn,translateExtent:Ft,minZoom:In,maxZoom:nt,zoomActivationKeyCode:De,preventScrolling:Y,noWheelClassName:gn,noPanClassName:Ae,onViewportChange:un,isControlledViewport:jn,paneClickDistance:H,selectionOnDrag:li,children:re.jsxs(Aqn,{onSelectionStart:Z,onSelectionEnd:le,onPaneClick:j,onPaneMouseEnter:A,onPaneMouseMove:x,onPaneMouseLeave:D,onPaneContextMenu:$,onPaneScroll:E,panOnDrag:st,isSelecting:!!oi,selectionMode:Q,selectionKeyPressed:rn,paneClickDistance:H,selectionOnDrag:li,children:[g,En&&re.jsx($qn,{onSelectionContextMenu:Fe,noPanClassName:Ae,disableKeyboardA11y:Je})]})})}v0n.displayName="FlowRenderer";const Bqn=kn.memo(v0n),zqn=g=>j=>g?dke(j.nodeLookup,{x:0,y:0,width:j.width,height:j.height},j.transform,!0).map(A=>A.id):Array.from(j.nodeLookup.keys());function Fqn(g){return Bu(kn.useCallback(zqn(g),[g]),vl)}const Hqn=g=>g.updateNodeInternals;function Jqn(){const g=Bu(Hqn),[j]=kn.useState(()=>typeof ResizeObserver>"u"?null:new ResizeObserver(A=>{const x=new Map;A.forEach(D=>{const $=D.target.getAttribute("data-id");x.set($,{id:$,nodeElement:D.target,force:!0})}),g(x)}));return kn.useEffect(()=>()=>{j?.disconnect()},[j]),j}function Gqn({node:g,nodeType:j,hasDimensions:A,resizeObserver:x}){const D=yl(),$=kn.useRef(null),E=kn.useRef(null),H=kn.useRef(g.sourcePosition),U=kn.useRef(g.targetPosition),J=kn.useRef(j),te=A&&!!g.internals.handleBounds;return kn.useEffect(()=>{$.current&&!g.hidden&&(!te||E.current!==$.current)&&(E.current&&x?.unobserve(E.current),x?.observe($.current),E.current=$.current)},[te,g.hidden]),kn.useEffect(()=>()=>{E.current&&(x?.unobserve(E.current),E.current=null)},[]),kn.useEffect(()=>{if($.current){const Q=J.current!==j,Z=H.current!==g.sourcePosition,le=U.current!==g.targetPosition;(Q||Z||le)&&(J.current=j,H.current=g.sourcePosition,U.current=g.targetPosition,D.getState().updateNodeInternals(new Map([[g.id,{id:g.id,nodeElement:$.current,force:!0}]])))}},[g.id,j,g.sourcePosition,g.targetPosition]),$}function qqn({id:g,onClick:j,onMouseEnter:A,onMouseMove:x,onMouseLeave:D,onContextMenu:$,onDoubleClick:E,nodesDraggable:H,elementsSelectable:U,nodesConnectable:J,nodesFocusable:te,resizeObserver:Q,noDragClassName:Z,noPanClassName:le,disableKeyboardA11y:be,rfId:ne,nodeTypes:De,nodeClickDistance:Se,onError:ze}){const{node:de,internals:Le,isParent:an}=Bu(Jt=>{const hi=Jt.nodeLookup.get(g),nc=Jt.parentLookup.has(g);return{node:hi,internals:hi.internals,isParent:nc}},vl);let ln=de.type||"default",on=De?.[ln]||D1n[ln];on===void 0&&(ze?.("003",Vv.error003(ln)),ln="default",on=De?.default||D1n.default);const Mn=!!(de.draggable||H&&typeof de.draggable>"u"),Nn=!!(de.selectable||U&&typeof de.selectable>"u"),Ft=!!(de.connectable||J&&typeof de.connectable>"u"),In=!!(de.focusable||te&&typeof de.focusable>"u"),nt=yl(),Y=zdn(de),Fe=Gqn({node:de,nodeType:ln,hasDimensions:Y,resizeObserver:Q}),gn=w0n({nodeRef:Fe,disabled:de.hidden||!Mn,noDragClassName:Z,handleSelector:de.dragHandle,nodeId:g,isSelectable:Nn,nodeClickDistance:Se}),Ae=p0n();if(de.hidden)return null;const Je=q6(de),un=Lqn(de),jn=Nn||Mn||j||A||x||D,En=A?Jt=>A(Jt,{...Le.userNode}):void 0,Me=x?Jt=>x(Jt,{...Le.userNode}):void 0,rn=D?Jt=>D(Jt,{...Le.userNode}):void 0,ut=$?Jt=>$(Jt,{...Le.userNode}):void 0,st=E?Jt=>E(Jt,{...Le.userNode}):void 0,Kt=Jt=>{const{selectNodesOnDrag:hi,nodeDragThreshold:nc}=nt.getState();Nn&&(!hi||!Mn||nc>0)&&ike({id:g,store:nt,nodeRef:Fe}),j&&j(Jt,{...Le.userNode})},li=Jt=>{if(!(Jdn(Jt.nativeEvent)||be)){if(Ndn.includes(Jt.key)&&Nn){const hi=Jt.key==="Escape";ike({id:g,store:nt,unselect:hi,nodeRef:Fe})}else if(Mn&&de.selected&&Object.prototype.hasOwnProperty.call(yue,Jt.key)){Jt.preventDefault();const{ariaLabelConfig:hi}=nt.getState();nt.setState({ariaLiveMessage:hi["node.a11yDescription.ariaLiveMessage"]({direction:Jt.key.replace("Arrow","").toLowerCase(),x:~~Le.positionAbsolute.x,y:~~Le.positionAbsolute.y})}),Ae({direction:yue[Jt.key],factor:Jt.shiftKey?4:1})}}},oi=()=>{if(be||!Fe.current?.matches(":focus-visible"))return;const{transform:Jt,width:hi,height:nc,autoPanOnNodeFocus:Xr,setCenter:pr}=nt.getState();if(!Xr)return;dke(new Map([[g,de]]),{x:0,y:0,width:hi,height:nc},Jt,!0).length>0||pr(de.position.x+Je.width/2,de.position.y+Je.height/2,{zoom:Jt[2]})};return re.jsx("div",{className:ka(["react-flow__node",`react-flow__node-${ln}`,{[le]:Mn},de.className,{selected:de.selected,selectable:Nn,parent:an,draggable:Mn,dragging:gn}]),ref:Fe,style:{zIndex:Le.z,transform:`translate(${Le.positionAbsolute.x}px,${Le.positionAbsolute.y}px)`,pointerEvents:jn?"all":"none",visibility:Y?"visible":"hidden",...de.style,...un},"data-id":g,"data-testid":`rf__node-${g}`,onMouseEnter:En,onMouseMove:Me,onMouseLeave:rn,onContextMenu:ut,onClick:Kt,onDoubleClick:st,onKeyDown:In?li:void 0,tabIndex:In?0:void 0,onFocus:In?oi:void 0,role:de.ariaRole??(In?"group":void 0),"aria-roledescription":"node","aria-describedby":be?void 0:`${s0n}-${ne}`,"aria-label":de.ariaLabel,...de.domAttributes,children:re.jsx(Tqn,{value:g,children:re.jsx(on,{id:g,data:de.data,type:ln,positionAbsoluteX:Le.positionAbsolute.x,positionAbsoluteY:Le.positionAbsolute.y,selected:de.selected??!1,selectable:Nn,draggable:Mn,deletable:de.deletable??!0,isConnectable:Ft,sourcePosition:de.sourcePosition,targetPosition:de.targetPosition,dragging:gn,dragHandle:de.dragHandle,zIndex:Le.z,parentId:de.parentId,...Je})})})}var Uqn=kn.memo(qqn);const Xqn=g=>({nodesDraggable:g.nodesDraggable,nodesConnectable:g.nodesConnectable,nodesFocusable:g.nodesFocusable,elementsSelectable:g.elementsSelectable,onError:g.onError});function y0n(g){const{nodesDraggable:j,nodesConnectable:A,nodesFocusable:x,elementsSelectable:D,onError:$}=Bu(Xqn,vl),E=Fqn(g.onlyRenderVisibleElements),H=Jqn();return re.jsx("div",{className:"react-flow__nodes",style:Due,children:E.map(U=>re.jsx(Uqn,{id:U,nodeTypes:g.nodeTypes,nodeExtent:g.nodeExtent,onClick:g.onNodeClick,onMouseEnter:g.onNodeMouseEnter,onMouseMove:g.onNodeMouseMove,onMouseLeave:g.onNodeMouseLeave,onContextMenu:g.onNodeContextMenu,onDoubleClick:g.onNodeDoubleClick,noDragClassName:g.noDragClassName,noPanClassName:g.noPanClassName,rfId:g.rfId,disableKeyboardA11y:g.disableKeyboardA11y,resizeObserver:H,nodesDraggable:j,nodesConnectable:A,nodesFocusable:x,elementsSelectable:D,nodeClickDistance:g.nodeClickDistance,onError:$},U))})}y0n.displayName="NodeRenderer";const Kqn=kn.memo(y0n);function Vqn(g){return Bu(kn.useCallback(A=>{if(!g)return A.edges.map(D=>D.id);const x=[];if(A.width&&A.height)for(const D of A.edges){const $=A.nodeLookup.get(D.source),E=A.nodeLookup.get(D.target);$&&E&&XJn({sourceNode:$,targetNode:E,width:A.width,height:A.height,transform:A.transform})&&x.push(D.id)}return x},[g]),vl)}const Yqn=({color:g="none",strokeWidth:j=1})=>{const A={strokeWidth:j,...g&&{stroke:g}};return re.jsx("polyline",{className:"arrow",style:A,strokeLinecap:"round",fill:"none",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4"})},Qqn=({color:g="none",strokeWidth:j=1})=>{const A={strokeWidth:j,...g&&{stroke:g,fill:g}};return re.jsx("polyline",{className:"arrowclosed",style:A,strokeLinecap:"round",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4 -5,-4"})},_1n={[JG.Arrow]:Yqn,[JG.ArrowClosed]:Qqn};function Zqn(g){const j=yl();return kn.useMemo(()=>Object.prototype.hasOwnProperty.call(_1n,g)?_1n[g]:(j.getState().onError?.("009",Vv.error009(g)),null),[g])}const Wqn=({id:g,type:j,color:A,width:x=12.5,height:D=12.5,markerUnits:$="strokeWidth",strokeWidth:E,orient:H="auto-start-reverse"})=>{const U=Zqn(j);return U?re.jsx("marker",{className:"react-flow__arrowhead",id:g,markerWidth:`${x}`,markerHeight:`${D}`,viewBox:"-10 -10 20 20",markerUnits:$,orient:H,refX:"0",refY:"0",children:re.jsx(U,{color:A,strokeWidth:E})}):null},k0n=({defaultColor:g,rfId:j})=>{const A=Bu($=>$.edges),x=Bu($=>$.defaultEdgeOptions),D=kn.useMemo(()=>nGn(A,{id:j,defaultColor:g,defaultMarkerStart:x?.markerStart,defaultMarkerEnd:x?.markerEnd}),[A,x,j,g]);return D.length?re.jsx("svg",{className:"react-flow__marker","aria-hidden":"true",children:re.jsx("defs",{children:D.map($=>re.jsx(Wqn,{id:$.id,type:$.type,color:$.color,width:$.width,height:$.height,markerUnits:$.markerUnits,strokeWidth:$.strokeWidth,orient:$.orient},$.id))})}):null};k0n.displayName="MarkerDefinitions";var eUn=kn.memo(k0n);function E0n({x:g,y:j,label:A,labelStyle:x,labelShowBg:D=!0,labelBgStyle:$,labelBgPadding:E=[2,4],labelBgBorderRadius:H=2,children:U,className:J,...te}){const[Q,Z]=kn.useState({x:1,y:0,width:0,height:0}),le=ka(["react-flow__edge-textwrapper",J]),be=kn.useRef(null);return kn.useEffect(()=>{if(be.current){const ne=be.current.getBBox();Z({x:ne.x,y:ne.y,width:ne.width,height:ne.height})}},[A]),A?re.jsxs("g",{transform:`translate(${g-Q.width/2} ${j-Q.height/2})`,className:le,visibility:Q.width?"visible":"hidden",...te,children:[D&&re.jsx("rect",{width:Q.width+2*E[0],x:-E[0],y:-E[1],height:Q.height+2*E[1],className:"react-flow__edge-textbg",style:$,rx:H,ry:H}),re.jsx("text",{className:"react-flow__edge-text",y:Q.height/2,dy:"0.3em",ref:be,style:x,children:A}),U]}):null}E0n.displayName="EdgeText";const nUn=kn.memo(E0n);function eq({path:g,labelX:j,labelY:A,label:x,labelStyle:D,labelShowBg:$,labelBgStyle:E,labelBgPadding:H,labelBgBorderRadius:U,interactionWidth:J=20,...te}){return re.jsxs(re.Fragment,{children:[re.jsx("path",{...te,d:g,fill:"none",className:ka(["react-flow__edge-path",te.className])}),J?re.jsx("path",{d:g,fill:"none",strokeOpacity:0,strokeWidth:J,className:"react-flow__edge-interaction"}):null,x&&_m(j)&&_m(A)?re.jsx(nUn,{x:j,y:A,label:x,labelStyle:D,labelShowBg:$,labelBgStyle:E,labelBgPadding:H,labelBgBorderRadius:U}):null]})}function L1n({pos:g,x1:j,y1:A,x2:x,y2:D}){return g===cr.Left||g===cr.Right?[.5*(j+x),A]:[j,.5*(A+D)]}function j0n({sourceX:g,sourceY:j,sourcePosition:A=cr.Bottom,targetX:x,targetY:D,targetPosition:$=cr.Top}){const[E,H]=L1n({pos:A,x1:g,y1:j,x2:x,y2:D}),[U,J]=L1n({pos:$,x1:x,y1:D,x2:g,y2:j}),[te,Q,Z,le]=qdn({sourceX:g,sourceY:j,targetX:x,targetY:D,sourceControlX:E,sourceControlY:H,targetControlX:U,targetControlY:J});return[`M${g},${j} C${E},${H} ${U},${J} ${x},${D}`,te,Q,Z,le]}function S0n(g){return kn.memo(({id:j,sourceX:A,sourceY:x,targetX:D,targetY:$,sourcePosition:E,targetPosition:H,label:U,labelStyle:J,labelShowBg:te,labelBgStyle:Q,labelBgPadding:Z,labelBgBorderRadius:le,style:be,markerEnd:ne,markerStart:De,interactionWidth:Se})=>{const[ze,de,Le]=j0n({sourceX:A,sourceY:x,sourcePosition:E,targetX:D,targetY:$,targetPosition:H}),an=g.isInternal?void 0:j;return re.jsx(eq,{id:an,path:ze,labelX:de,labelY:Le,label:U,labelStyle:J,labelShowBg:te,labelBgStyle:Q,labelBgPadding:Z,labelBgBorderRadius:le,style:be,markerEnd:ne,markerStart:De,interactionWidth:Se})})}const tUn=S0n({isInternal:!1}),A0n=S0n({isInternal:!0});tUn.displayName="SimpleBezierEdge";A0n.displayName="SimpleBezierEdgeInternal";function M0n(g){return kn.memo(({id:j,sourceX:A,sourceY:x,targetX:D,targetY:$,label:E,labelStyle:H,labelShowBg:U,labelBgStyle:J,labelBgPadding:te,labelBgBorderRadius:Q,style:Z,sourcePosition:le=cr.Bottom,targetPosition:be=cr.Top,markerEnd:ne,markerStart:De,pathOptions:Se,interactionWidth:ze})=>{const[de,Le,an]=vue({sourceX:A,sourceY:x,sourcePosition:le,targetX:D,targetY:$,targetPosition:be,borderRadius:Se?.borderRadius,offset:Se?.offset,stepPosition:Se?.stepPosition}),ln=g.isInternal?void 0:j;return re.jsx(eq,{id:ln,path:de,labelX:Le,labelY:an,label:E,labelStyle:H,labelShowBg:U,labelBgStyle:J,labelBgPadding:te,labelBgBorderRadius:Q,style:Z,markerEnd:ne,markerStart:De,interactionWidth:ze})})}const T0n=M0n({isInternal:!1}),x0n=M0n({isInternal:!0});T0n.displayName="SmoothStepEdge";x0n.displayName="SmoothStepEdgeInternal";function C0n(g){return kn.memo(({id:j,...A})=>{const x=g.isInternal?void 0:j;return re.jsx(T0n,{...A,id:x,pathOptions:kn.useMemo(()=>({borderRadius:0,offset:A.pathOptions?.offset}),[A.pathOptions?.offset])})})}const iUn=C0n({isInternal:!1}),O0n=C0n({isInternal:!0});iUn.displayName="StepEdge";O0n.displayName="StepEdgeInternal";function N0n(g){return kn.memo(({id:j,sourceX:A,sourceY:x,targetX:D,targetY:$,label:E,labelStyle:H,labelShowBg:U,labelBgStyle:J,labelBgPadding:te,labelBgBorderRadius:Q,style:Z,markerEnd:le,markerStart:be,interactionWidth:ne})=>{const[De,Se,ze]=Kdn({sourceX:A,sourceY:x,targetX:D,targetY:$}),de=g.isInternal?void 0:j;return re.jsx(eq,{id:de,path:De,labelX:Se,labelY:ze,label:E,labelStyle:H,labelShowBg:U,labelBgStyle:J,labelBgPadding:te,labelBgBorderRadius:Q,style:Z,markerEnd:le,markerStart:be,interactionWidth:ne})})}const rUn=N0n({isInternal:!1}),D0n=N0n({isInternal:!0});rUn.displayName="StraightEdge";D0n.displayName="StraightEdgeInternal";function I0n(g){return kn.memo(({id:j,sourceX:A,sourceY:x,targetX:D,targetY:$,sourcePosition:E=cr.Bottom,targetPosition:H=cr.Top,label:U,labelStyle:J,labelShowBg:te,labelBgStyle:Q,labelBgPadding:Z,labelBgBorderRadius:le,style:be,markerEnd:ne,markerStart:De,pathOptions:Se,interactionWidth:ze})=>{const[de,Le,an]=Udn({sourceX:A,sourceY:x,sourcePosition:E,targetX:D,targetY:$,targetPosition:H,curvature:Se?.curvature}),ln=g.isInternal?void 0:j;return re.jsx(eq,{id:ln,path:de,labelX:Le,labelY:an,label:U,labelStyle:J,labelShowBg:te,labelBgStyle:Q,labelBgPadding:Z,labelBgBorderRadius:le,style:be,markerEnd:ne,markerStart:De,interactionWidth:ze})})}const cUn=I0n({isInternal:!1}),_0n=I0n({isInternal:!0});cUn.displayName="BezierEdge";_0n.displayName="BezierEdgeInternal";const P1n={default:_0n,straight:D0n,step:O0n,smoothstep:x0n,simplebezier:A0n},$1n={sourceX:null,sourceY:null,targetX:null,targetY:null,sourcePosition:null,targetPosition:null},uUn=(g,j,A)=>A===cr.Left?g-j:A===cr.Right?g+j:g,oUn=(g,j,A)=>A===cr.Top?g-j:A===cr.Bottom?g+j:g,R1n="react-flow__edgeupdater";function B1n({position:g,centerX:j,centerY:A,radius:x=10,onMouseDown:D,onMouseEnter:$,onMouseOut:E,type:H}){return re.jsx("circle",{onMouseDown:D,onMouseEnter:$,onMouseOut:E,className:ka([R1n,`${R1n}-${H}`]),cx:uUn(j,x,g),cy:oUn(A,x,g),r:x,stroke:"transparent",fill:"transparent"})}function sUn({isReconnectable:g,reconnectRadius:j,edge:A,sourceX:x,sourceY:D,targetX:$,targetY:E,sourcePosition:H,targetPosition:U,onReconnect:J,onReconnectStart:te,onReconnectEnd:Q,setReconnecting:Z,setUpdateHover:le}){const be=yl(),ne=(Le,an)=>{if(Le.button!==0)return;const{autoPanOnConnect:ln,domNode:on,connectionMode:Mn,connectionRadius:Nn,lib:Ft,onConnectStart:In,cancelConnection:nt,nodeLookup:Y,rfId:Fe,panBy:gn,updateConnection:Ae}=be.getState(),Je=an.type==="target",un=(Me,rn)=>{Z(!1),Q?.(Me,A,an.type,rn)},jn=Me=>J?.(A,Me),En=(Me,rn)=>{Z(!0),te?.(Le,A,an.type),In?.(Me,rn)};nke.onPointerDown(Le.nativeEvent,{autoPanOnConnect:ln,connectionMode:Mn,connectionRadius:Nn,domNode:on,handleId:an.id,nodeId:an.nodeId,nodeLookup:Y,isTarget:Je,edgeUpdaterType:an.type,lib:Ft,flowId:Fe,cancelConnection:nt,panBy:gn,isValidConnection:(...Me)=>be.getState().isValidConnection?.(...Me)??!0,onConnect:jn,onConnectStart:En,onConnectEnd:(...Me)=>be.getState().onConnectEnd?.(...Me),onReconnectEnd:un,updateConnection:Ae,getTransform:()=>be.getState().transform,getFromHandle:()=>be.getState().connection.fromHandle,dragThreshold:be.getState().connectionDragThreshold,handleDomNode:Le.currentTarget})},De=Le=>ne(Le,{nodeId:A.target,id:A.targetHandle??null,type:"target"}),Se=Le=>ne(Le,{nodeId:A.source,id:A.sourceHandle??null,type:"source"}),ze=()=>le(!0),de=()=>le(!1);return re.jsxs(re.Fragment,{children:[(g===!0||g==="source")&&re.jsx(B1n,{position:H,centerX:x,centerY:D,radius:j,onMouseDown:De,onMouseEnter:ze,onMouseOut:de,type:"source"}),(g===!0||g==="target")&&re.jsx(B1n,{position:U,centerX:$,centerY:E,radius:j,onMouseDown:Se,onMouseEnter:ze,onMouseOut:de,type:"target"})]})}function lUn({id:g,edgesFocusable:j,edgesReconnectable:A,elementsSelectable:x,onClick:D,onDoubleClick:$,onContextMenu:E,onMouseEnter:H,onMouseMove:U,onMouseLeave:J,reconnectRadius:te,onReconnect:Q,onReconnectStart:Z,onReconnectEnd:le,rfId:be,edgeTypes:ne,noPanClassName:De,onError:Se,disableKeyboardA11y:ze}){let de=Bu(pr=>pr.edgeLookup.get(g));const Le=Bu(pr=>pr.defaultEdgeOptions);de=Le?{...Le,...de}:de;let an=de.type||"default",ln=ne?.[an]||P1n[an];ln===void 0&&(Se?.("011",Vv.error011(an)),an="default",ln=ne?.default||P1n.default);const on=!!(de.focusable||j&&typeof de.focusable>"u"),Mn=typeof Q<"u"&&(de.reconnectable||A&&typeof de.reconnectable>"u"),Nn=!!(de.selectable||x&&typeof de.selectable>"u"),Ft=kn.useRef(null),[In,nt]=kn.useState(!1),[Y,Fe]=kn.useState(!1),gn=yl(),{zIndex:Ae,sourceX:Je,sourceY:un,targetX:jn,targetY:En,sourcePosition:Me,targetPosition:rn}=Bu(kn.useCallback(pr=>{const Ei=pr.nodeLookup.get(de.source),Bi=pr.nodeLookup.get(de.target);if(!Ei||!Bi)return{zIndex:de.zIndex,...$1n};const Fc=eGn({id:g,sourceNode:Ei,targetNode:Bi,sourceHandle:de.sourceHandle||null,targetHandle:de.targetHandle||null,connectionMode:pr.connectionMode,onError:Se});return{zIndex:UJn({selected:de.selected,zIndex:de.zIndex,sourceNode:Ei,targetNode:Bi,elevateOnSelect:pr.elevateEdgesOnSelect,zIndexMode:pr.zIndexMode}),...Fc||$1n}},[de.source,de.target,de.sourceHandle,de.targetHandle,de.selected,de.zIndex]),vl),ut=kn.useMemo(()=>de.markerStart?`url('#${W7e(de.markerStart,be)}')`:void 0,[de.markerStart,be]),st=kn.useMemo(()=>de.markerEnd?`url('#${W7e(de.markerEnd,be)}')`:void 0,[de.markerEnd,be]);if(de.hidden||Je===null||un===null||jn===null||En===null)return null;const Kt=pr=>{const{addSelectedEdges:Ei,unselectNodesAndEdges:Bi,multiSelectionActive:Fc}=gn.getState();Nn&&(gn.setState({nodesSelectionActive:!1}),de.selected&&Fc?(Bi({nodes:[],edges:[de]}),Ft.current?.blur()):Ei([g])),D&&D(pr,de)},li=$?pr=>{$(pr,{...de})}:void 0,oi=E?pr=>{E(pr,{...de})}:void 0,Jt=H?pr=>{H(pr,{...de})}:void 0,hi=U?pr=>{U(pr,{...de})}:void 0,nc=J?pr=>{J(pr,{...de})}:void 0,Xr=pr=>{if(!ze&&Ndn.includes(pr.key)&&Nn){const{unselectNodesAndEdges:Ei,addSelectedEdges:Bi}=gn.getState();pr.key==="Escape"?(Ft.current?.blur(),Ei({edges:[de]})):Bi([g])}};return re.jsx("svg",{style:{zIndex:Ae},children:re.jsxs("g",{className:ka(["react-flow__edge",`react-flow__edge-${an}`,de.className,De,{selected:de.selected,animated:de.animated,inactive:!Nn&&!D,updating:In,selectable:Nn}]),onClick:Kt,onDoubleClick:li,onContextMenu:oi,onMouseEnter:Jt,onMouseMove:hi,onMouseLeave:nc,onKeyDown:on?Xr:void 0,tabIndex:on?0:void 0,role:de.ariaRole??(on?"group":"img"),"aria-roledescription":"edge","data-id":g,"data-testid":`rf__edge-${g}`,"aria-label":de.ariaLabel===null?void 0:de.ariaLabel||`Edge from ${de.source} to ${de.target}`,"aria-describedby":on?`${l0n}-${be}`:void 0,ref:Ft,...de.domAttributes,children:[!Y&&re.jsx(ln,{id:g,source:de.source,target:de.target,type:de.type,selected:de.selected,animated:de.animated,selectable:Nn,deletable:de.deletable??!0,label:de.label,labelStyle:de.labelStyle,labelShowBg:de.labelShowBg,labelBgStyle:de.labelBgStyle,labelBgPadding:de.labelBgPadding,labelBgBorderRadius:de.labelBgBorderRadius,sourceX:Je,sourceY:un,targetX:jn,targetY:En,sourcePosition:Me,targetPosition:rn,data:de.data,style:de.style,sourceHandleId:de.sourceHandle,targetHandleId:de.targetHandle,markerStart:ut,markerEnd:st,pathOptions:"pathOptions"in de?de.pathOptions:void 0,interactionWidth:de.interactionWidth}),Mn&&re.jsx(sUn,{edge:de,isReconnectable:Mn,reconnectRadius:te,onReconnect:Q,onReconnectStart:Z,onReconnectEnd:le,sourceX:Je,sourceY:un,targetX:jn,targetY:En,sourcePosition:Me,targetPosition:rn,setUpdateHover:nt,setReconnecting:Fe})]})})}var fUn=kn.memo(lUn);const aUn=g=>({edgesFocusable:g.edgesFocusable,edgesReconnectable:g.edgesReconnectable,elementsSelectable:g.elementsSelectable,connectionMode:g.connectionMode,onError:g.onError});function L0n({defaultMarkerColor:g,onlyRenderVisibleElements:j,rfId:A,edgeTypes:x,noPanClassName:D,onReconnect:$,onEdgeContextMenu:E,onEdgeMouseEnter:H,onEdgeMouseMove:U,onEdgeMouseLeave:J,onEdgeClick:te,reconnectRadius:Q,onEdgeDoubleClick:Z,onReconnectStart:le,onReconnectEnd:be,disableKeyboardA11y:ne}){const{edgesFocusable:De,edgesReconnectable:Se,elementsSelectable:ze,onError:de}=Bu(aUn,vl),Le=Vqn(j);return re.jsxs("div",{className:"react-flow__edges",children:[re.jsx(eUn,{defaultColor:g,rfId:A}),Le.map(an=>re.jsx(fUn,{id:an,edgesFocusable:De,edgesReconnectable:Se,elementsSelectable:ze,noPanClassName:D,onReconnect:$,onContextMenu:E,onMouseEnter:H,onMouseMove:U,onMouseLeave:J,onClick:te,reconnectRadius:Q,onDoubleClick:Z,onReconnectStart:le,onReconnectEnd:be,rfId:A,onError:de,edgeTypes:x,disableKeyboardA11y:ne},an))]})}L0n.displayName="EdgeRenderer";const hUn=kn.memo(L0n),dUn=g=>`translate(${g.transform[0]}px,${g.transform[1]}px) scale(${g.transform[2]})`;function bUn({children:g}){const j=Bu(dUn);return re.jsx("div",{className:"react-flow__viewport xyflow__viewport react-flow__container",style:{transform:j},children:g})}function gUn(g){const j=kke(),A=kn.useRef(!1);kn.useEffect(()=>{!A.current&&j.viewportInitialized&&g&&(setTimeout(()=>g(j),1),A.current=!0)},[g,j.viewportInitialized])}const wUn=g=>g.panZoom?.syncViewport;function pUn(g){const j=Bu(wUn),A=yl();return kn.useEffect(()=>{g&&(j?.(g),A.setState({transform:[g.x,g.y,g.zoom]}))},[g,j]),null}function mUn(g){return g.connection.inProgress?{...g.connection,to:WG(g.connection.to,g.transform)}:{...g.connection}}function vUn(g){return mUn}function yUn(g){const j=vUn();return Bu(j,vl)}const kUn=g=>({nodesConnectable:g.nodesConnectable,isValid:g.connection.isValid,inProgress:g.connection.inProgress,width:g.width,height:g.height});function EUn({containerStyle:g,style:j,type:A,component:x}){const{nodesConnectable:D,width:$,height:E,isValid:H,inProgress:U}=Bu(kUn,vl);return!($&&D&&U)?null:re.jsx("svg",{style:g,width:$,height:E,className:"react-flow__connectionline react-flow__container",children:re.jsx("g",{className:ka(["react-flow__connection",_dn(H)]),children:re.jsx(P0n,{style:j,type:A,CustomComponent:x,isValid:H})})})}const P0n=({style:g,type:j=R7.Bezier,CustomComponent:A,isValid:x})=>{const{inProgress:D,from:$,fromNode:E,fromHandle:H,fromPosition:U,to:J,toNode:te,toHandle:Q,toPosition:Z,pointer:le}=yUn();if(!D)return;if(A)return re.jsx(A,{connectionLineType:j,connectionLineStyle:g,fromNode:E,fromHandle:H,fromX:$.x,fromY:$.y,toX:J.x,toY:J.y,fromPosition:U,toPosition:Z,connectionStatus:_dn(x),toNode:te,toHandle:Q,pointer:le});let be="";const ne={sourceX:$.x,sourceY:$.y,sourcePosition:U,targetX:J.x,targetY:J.y,targetPosition:Z};switch(j){case R7.Bezier:[be]=Udn(ne);break;case R7.SimpleBezier:[be]=j0n(ne);break;case R7.Step:[be]=vue({...ne,borderRadius:0});break;case R7.SmoothStep:[be]=vue(ne);break;default:[be]=Kdn(ne)}return re.jsx("path",{d:be,fill:"none",className:"react-flow__connection-path",style:g})};P0n.displayName="ConnectionLine";const jUn={};function z1n(g=jUn){kn.useRef(g),yl(),kn.useEffect(()=>{},[g])}function SUn(){yl(),kn.useRef(!1),kn.useEffect(()=>{},[])}function $0n({nodeTypes:g,edgeTypes:j,onInit:A,onNodeClick:x,onEdgeClick:D,onNodeDoubleClick:$,onEdgeDoubleClick:E,onNodeMouseEnter:H,onNodeMouseMove:U,onNodeMouseLeave:J,onNodeContextMenu:te,onSelectionContextMenu:Q,onSelectionStart:Z,onSelectionEnd:le,connectionLineType:be,connectionLineStyle:ne,connectionLineComponent:De,connectionLineContainerStyle:Se,selectionKeyCode:ze,selectionOnDrag:de,selectionMode:Le,multiSelectionKeyCode:an,panActivationKeyCode:ln,zoomActivationKeyCode:on,deleteKeyCode:Mn,onlyRenderVisibleElements:Nn,elementsSelectable:Ft,defaultViewport:In,translateExtent:nt,minZoom:Y,maxZoom:Fe,preventScrolling:gn,defaultMarkerColor:Ae,zoomOnScroll:Je,zoomOnPinch:un,panOnScroll:jn,panOnScrollSpeed:En,panOnScrollMode:Me,zoomOnDoubleClick:rn,panOnDrag:ut,onPaneClick:st,onPaneMouseEnter:Kt,onPaneMouseMove:li,onPaneMouseLeave:oi,onPaneScroll:Jt,onPaneContextMenu:hi,paneClickDistance:nc,nodeClickDistance:Xr,onEdgeContextMenu:pr,onEdgeMouseEnter:Ei,onEdgeMouseMove:Bi,onEdgeMouseLeave:Fc,reconnectRadius:Du,onReconnect:kl,onReconnectStart:s1,onReconnectEnd:Za,noDragClassName:Wa,noWheelClassName:eu,noPanClassName:Ng,disableKeyboardA11y:Ai,nodeExtent:Mc,rfId:no,viewport:bb,onViewportChange:l1}){return z1n(g),z1n(j),SUn(),gUn(A),pUn(bb),re.jsx(Bqn,{onPaneClick:st,onPaneMouseEnter:Kt,onPaneMouseMove:li,onPaneMouseLeave:oi,onPaneContextMenu:hi,onPaneScroll:Jt,paneClickDistance:nc,deleteKeyCode:Mn,selectionKeyCode:ze,selectionOnDrag:de,selectionMode:Le,onSelectionStart:Z,onSelectionEnd:le,multiSelectionKeyCode:an,panActivationKeyCode:ln,zoomActivationKeyCode:on,elementsSelectable:Ft,zoomOnScroll:Je,zoomOnPinch:un,zoomOnDoubleClick:rn,panOnScroll:jn,panOnScrollSpeed:En,panOnScrollMode:Me,panOnDrag:ut,defaultViewport:In,translateExtent:nt,minZoom:Y,maxZoom:Fe,onSelectionContextMenu:Q,preventScrolling:gn,noDragClassName:Wa,noWheelClassName:eu,noPanClassName:Ng,disableKeyboardA11y:Ai,onViewportChange:l1,isControlledViewport:!!bb,children:re.jsxs(bUn,{children:[re.jsx(hUn,{edgeTypes:j,onEdgeClick:D,onEdgeDoubleClick:E,onReconnect:kl,onReconnectStart:s1,onReconnectEnd:Za,onlyRenderVisibleElements:Nn,onEdgeContextMenu:pr,onEdgeMouseEnter:Ei,onEdgeMouseMove:Bi,onEdgeMouseLeave:Fc,reconnectRadius:Du,defaultMarkerColor:Ae,noPanClassName:Ng,disableKeyboardA11y:Ai,rfId:no}),re.jsx(EUn,{style:ne,type:be,component:De,containerStyle:Se}),re.jsx("div",{className:"react-flow__edgelabel-renderer"}),re.jsx(Kqn,{nodeTypes:g,onNodeClick:x,onNodeDoubleClick:$,onNodeMouseEnter:H,onNodeMouseMove:U,onNodeMouseLeave:J,onNodeContextMenu:te,nodeClickDistance:Xr,onlyRenderVisibleElements:Nn,noPanClassName:Ng,noDragClassName:Wa,disableKeyboardA11y:Ai,nodeExtent:Mc,rfId:no}),re.jsx("div",{className:"react-flow__viewport-portal"})]})})}$0n.displayName="GraphView";const AUn=kn.memo($0n),F1n=({nodes:g,edges:j,defaultNodes:A,defaultEdges:x,width:D,height:$,fitView:E,fitViewOptions:H,minZoom:U=.5,maxZoom:J=2,nodeOrigin:te,nodeExtent:Q,zIndexMode:Z="basic"}={})=>{const le=new Map,be=new Map,ne=new Map,De=new Map,Se=x??j??[],ze=A??g??[],de=te??[0,0],Le=Q??FG;Qdn(ne,De,Se);const{nodesInitialized:an}=eke(ze,le,be,{nodeOrigin:de,nodeExtent:Le,zIndexMode:Z});let ln=[0,0,1];if(E&&D&&$){const on=QG(le,{filter:In=>!!((In.width||In.initialWidth)&&(In.height||In.initialHeight))}),{x:Mn,y:Nn,zoom:Ft}=bke(on,D,$,U,J,H?.padding??.1);ln=[Mn,Nn,Ft]}return{rfId:"1",width:D??0,height:$??0,transform:ln,nodes:ze,nodesInitialized:an,nodeLookup:le,parentLookup:be,edges:Se,edgeLookup:De,connectionLookup:ne,onNodesChange:null,onEdgesChange:null,hasDefaultNodes:A!==void 0,hasDefaultEdges:x!==void 0,panZoom:null,minZoom:U,maxZoom:J,translateExtent:FG,nodeExtent:Le,nodesSelectionActive:!1,userSelectionActive:!1,userSelectionRect:null,connectionMode:aI.Strict,domNode:null,paneDragging:!1,noPanClassName:"nopan",nodeOrigin:de,nodeDragThreshold:1,connectionDragThreshold:1,snapGrid:[15,15],snapToGrid:!1,nodesDraggable:!0,nodesConnectable:!0,nodesFocusable:!0,edgesFocusable:!0,edgesReconnectable:!0,elementsSelectable:!0,elevateNodesOnSelect:!0,elevateEdgesOnSelect:!0,selectNodesOnDrag:!0,multiSelectionActive:!1,fitViewQueued:E??!1,fitViewOptions:H,fitViewResolver:null,connection:{...Idn},connectionClickStartHandle:null,connectOnClick:!0,ariaLiveMessage:"",autoPanOnConnect:!0,autoPanOnNodeDrag:!0,autoPanOnNodeFocus:!0,autoPanSpeed:15,connectionRadius:20,onError:zJn,isValidConnection:void 0,onSelectionChangeHandlers:[],lib:"react",debug:!1,ariaLabelConfig:Ddn,zIndexMode:Z,onNodesChangeMiddlewareMap:new Map,onEdgesChangeMiddlewareMap:new Map}},MUn=({nodes:g,edges:j,defaultNodes:A,defaultEdges:x,width:D,height:$,fitView:E,fitViewOptions:H,minZoom:U,maxZoom:J,nodeOrigin:te,nodeExtent:Q,zIndexMode:Z})=>GGn((le,be)=>{async function ne(){const{nodeLookup:De,panZoom:Se,fitViewOptions:ze,fitViewResolver:de,width:Le,height:an,minZoom:ln,maxZoom:on}=be();Se&&(await RJn({nodes:De,width:Le,height:an,panZoom:Se,minZoom:ln,maxZoom:on},ze),de?.resolve(!0),le({fitViewResolver:null}))}return{...F1n({nodes:g,edges:j,width:D,height:$,fitView:E,fitViewOptions:H,minZoom:U,maxZoom:J,nodeOrigin:te,nodeExtent:Q,defaultNodes:A,defaultEdges:x,zIndexMode:Z}),setNodes:De=>{const{nodeLookup:Se,parentLookup:ze,nodeOrigin:de,elevateNodesOnSelect:Le,fitViewQueued:an,zIndexMode:ln,nodesSelectionActive:on}=be(),{nodesInitialized:Mn,hasSelectedNodes:Nn}=eke(De,Se,ze,{nodeOrigin:de,nodeExtent:Q,elevateNodesOnSelect:Le,checkEquality:!0,zIndexMode:ln}),Ft=on&&Nn;an&&Mn?(ne(),le({nodes:De,nodesInitialized:Mn,fitViewQueued:!1,fitViewOptions:void 0,nodesSelectionActive:Ft})):le({nodes:De,nodesInitialized:Mn,nodesSelectionActive:Ft})},setEdges:De=>{const{connectionLookup:Se,edgeLookup:ze}=be();Qdn(Se,ze,De),le({edges:De})},setDefaultNodesAndEdges:(De,Se)=>{if(De){const{setNodes:ze}=be();ze(De),le({hasDefaultNodes:!0})}if(Se){const{setEdges:ze}=be();ze(Se),le({hasDefaultEdges:!0})}},updateNodeInternals:De=>{const{triggerNodeChanges:Se,nodeLookup:ze,parentLookup:de,domNode:Le,nodeOrigin:an,nodeExtent:ln,debug:on,fitViewQueued:Mn,zIndexMode:Nn}=be(),{changes:Ft,updatedInternals:In}=sGn(De,ze,de,Le,an,ln,Nn);In&&(rGn(ze,de,{nodeOrigin:an,nodeExtent:ln,zIndexMode:Nn}),Mn?(ne(),le({fitViewQueued:!1,fitViewOptions:void 0})):le({}),Ft?.length>0&&(on&&console.log("React Flow: trigger node changes",Ft),Se?.(Ft)))},updateNodePositions:(De,Se=!1)=>{const ze=[];let de=[];const{nodeLookup:Le,triggerNodeChanges:an,connection:ln,updateConnection:on,onNodesChangeMiddlewareMap:Mn}=be();for(const[Nn,Ft]of De){const In=Le.get(Nn),nt=!!(In?.expandParent&&In?.parentId&&Ft?.position),Y={id:Nn,type:"position",position:nt?{x:Math.max(0,Ft.position.x),y:Math.max(0,Ft.position.y)}:Ft.position,dragging:Se};if(In&&ln.inProgress&&ln.fromNode.id===In.id){const Fe=wM(In,ln.fromHandle,cr.Left,!0);on({...ln,from:Fe})}nt&&In.parentId&&ze.push({id:Nn,parentId:In.parentId,rect:{...Ft.internals.positionAbsolute,width:Ft.measured.width??0,height:Ft.measured.height??0}}),de.push(Y)}if(ze.length>0){const{parentLookup:Nn,nodeOrigin:Ft}=be(),In=yke(ze,Le,Nn,Ft);de.push(...In)}for(const Nn of Mn.values())de=Nn(de);an(de)},triggerNodeChanges:De=>{const{onNodesChange:Se,setNodes:ze,nodes:de,hasDefaultNodes:Le,debug:an}=be();if(De?.length){if(Le){const ln=h0n(De,de);ze(ln)}an&&console.log("React Flow: trigger node changes",De),Se?.(De)}},triggerEdgeChanges:De=>{const{onEdgesChange:Se,setEdges:ze,edges:de,hasDefaultEdges:Le,debug:an}=be();if(De?.length){if(Le){const ln=d0n(De,de);ze(ln)}an&&console.log("React Flow: trigger edge changes",De),Se?.(De)}},addSelectedNodes:De=>{const{multiSelectionActive:Se,edgeLookup:ze,nodeLookup:de,triggerNodeChanges:Le,triggerEdgeChanges:an}=be();if(Se){const ln=De.map(on=>lM(on,!0));Le(ln);return}Le(uI(de,new Set([...De]),!0)),an(uI(ze))},addSelectedEdges:De=>{const{multiSelectionActive:Se,edgeLookup:ze,nodeLookup:de,triggerNodeChanges:Le,triggerEdgeChanges:an}=be();if(Se){const ln=De.map(on=>lM(on,!0));an(ln);return}an(uI(ze,new Set([...De]))),Le(uI(de,new Set,!0))},unselectNodesAndEdges:({nodes:De,edges:Se}={})=>{const{edges:ze,nodes:de,nodeLookup:Le,triggerNodeChanges:an,triggerEdgeChanges:ln}=be(),on=De||de,Mn=Se||ze,Nn=[];for(const In of on){if(!In.selected)continue;const nt=Le.get(In.id);nt&&(nt.selected=!1),Nn.push(lM(In.id,!1))}const Ft=[];for(const In of Mn)In.selected&&Ft.push(lM(In.id,!1));an(Nn),ln(Ft)},setMinZoom:De=>{const{panZoom:Se,maxZoom:ze}=be();Se?.setScaleExtent([De,ze]),le({minZoom:De})},setMaxZoom:De=>{const{panZoom:Se,minZoom:ze}=be();Se?.setScaleExtent([ze,De]),le({maxZoom:De})},setTranslateExtent:De=>{be().panZoom?.setTranslateExtent(De),le({translateExtent:De})},resetSelectedElements:()=>{const{edges:De,nodes:Se,triggerNodeChanges:ze,triggerEdgeChanges:de,elementsSelectable:Le}=be();if(!Le)return;const an=Se.reduce((on,Mn)=>Mn.selected?[...on,lM(Mn.id,!1)]:on,[]),ln=De.reduce((on,Mn)=>Mn.selected?[...on,lM(Mn.id,!1)]:on,[]);ze(an),de(ln)},setNodeExtent:De=>{const{nodes:Se,nodeLookup:ze,parentLookup:de,nodeOrigin:Le,elevateNodesOnSelect:an,nodeExtent:ln,zIndexMode:on}=be();De[0][0]===ln[0][0]&&De[0][1]===ln[0][1]&&De[1][0]===ln[1][0]&&De[1][1]===ln[1][1]||(eke(Se,ze,de,{nodeOrigin:Le,nodeExtent:De,elevateNodesOnSelect:an,checkEquality:!1,zIndexMode:on}),le({nodeExtent:De}))},panBy:De=>{const{transform:Se,width:ze,height:de,panZoom:Le,translateExtent:an}=be();return lGn({delta:De,panZoom:Le,transform:Se,translateExtent:an,width:ze,height:de})},setCenter:async(De,Se,ze)=>{const{width:de,height:Le,maxZoom:an,panZoom:ln}=be();if(!ln)return Promise.resolve(!1);const on=typeof ze?.zoom<"u"?ze.zoom:an;return await ln.setViewport({x:de/2-De*on,y:Le/2-Se*on,zoom:on},{duration:ze?.duration,ease:ze?.ease,interpolate:ze?.interpolate}),Promise.resolve(!0)},cancelConnection:()=>{le({connection:{...Idn}})},updateConnection:De=>{le({connection:De})},reset:()=>le({...F1n()})}},Object.is);function TUn({initialNodes:g,initialEdges:j,defaultNodes:A,defaultEdges:x,initialWidth:D,initialHeight:$,initialMinZoom:E,initialMaxZoom:H,initialFitViewOptions:U,fitView:J,nodeOrigin:te,nodeExtent:Q,zIndexMode:Z,children:le}){const[be]=kn.useState(()=>MUn({nodes:g,edges:j,defaultNodes:A,defaultEdges:x,width:D,height:$,fitView:J,minZoom:E,maxZoom:H,fitViewOptions:U,nodeOrigin:te,nodeExtent:Q,zIndexMode:Z}));return re.jsx(UGn,{value:be,children:re.jsx(bqn,{children:le})})}function xUn({children:g,nodes:j,edges:A,defaultNodes:x,defaultEdges:D,width:$,height:E,fitView:H,fitViewOptions:U,minZoom:J,maxZoom:te,nodeOrigin:Q,nodeExtent:Z,zIndexMode:le}){return kn.useContext(Oue)?re.jsx(re.Fragment,{children:g}):re.jsx(TUn,{initialNodes:j,initialEdges:A,defaultNodes:x,defaultEdges:D,initialWidth:$,initialHeight:E,fitView:H,initialFitViewOptions:U,initialMinZoom:J,initialMaxZoom:te,nodeOrigin:Q,nodeExtent:Z,zIndexMode:le,children:g})}const CUn={width:"100%",height:"100%",overflow:"hidden",position:"relative",zIndex:0};function OUn({nodes:g,edges:j,defaultNodes:A,defaultEdges:x,className:D,nodeTypes:$,edgeTypes:E,onNodeClick:H,onEdgeClick:U,onInit:J,onMove:te,onMoveStart:Q,onMoveEnd:Z,onConnect:le,onConnectStart:be,onConnectEnd:ne,onClickConnectStart:De,onClickConnectEnd:Se,onNodeMouseEnter:ze,onNodeMouseMove:de,onNodeMouseLeave:Le,onNodeContextMenu:an,onNodeDoubleClick:ln,onNodeDragStart:on,onNodeDrag:Mn,onNodeDragStop:Nn,onNodesDelete:Ft,onEdgesDelete:In,onDelete:nt,onSelectionChange:Y,onSelectionDragStart:Fe,onSelectionDrag:gn,onSelectionDragStop:Ae,onSelectionContextMenu:Je,onSelectionStart:un,onSelectionEnd:jn,onBeforeDelete:En,connectionMode:Me,connectionLineType:rn=R7.Bezier,connectionLineStyle:ut,connectionLineComponent:st,connectionLineContainerStyle:Kt,deleteKeyCode:li="Backspace",selectionKeyCode:oi="Shift",selectionOnDrag:Jt=!1,selectionMode:hi=HG.Full,panActivationKeyCode:nc="Space",multiSelectionKeyCode:Xr=qG()?"Meta":"Control",zoomActivationKeyCode:pr=qG()?"Meta":"Control",snapToGrid:Ei,snapGrid:Bi,onlyRenderVisibleElements:Fc=!1,selectNodesOnDrag:Du,nodesDraggable:kl,autoPanOnNodeFocus:s1,nodesConnectable:Za,nodesFocusable:Wa,nodeOrigin:eu=f0n,edgesFocusable:Ng,edgesReconnectable:Ai,elementsSelectable:Mc=!0,defaultViewport:no=cqn,minZoom:bb=.5,maxZoom:l1=2,translateExtent:$m=FG,preventScrolling:pM=!0,nodeExtent:Zv,defaultMarkerColor:mM="#b1b1b7",zoomOnScroll:vM=!0,zoomOnPinch:Rm=!0,panOnScroll:eh=!1,panOnScrollSpeed:gb=.5,panOnScrollMode:nh=hM.Free,zoomOnDoubleClick:yM=!0,panOnDrag:kM=!0,onPaneClick:EM,onPaneMouseEnter:Wv,onPaneMouseMove:e5,onPaneMouseLeave:n5,onPaneScroll:Dg,onPaneContextMenu:t5,paneClickDistance:Bm=1,nodeClickDistance:jM=0,children:z7,onReconnect:U6,onReconnectStart:zm,onReconnectEnd:SM,onEdgeContextMenu:Fm,onEdgeDoubleClick:X6,onEdgeMouseEnter:F7,onEdgeMouseMove:Hm,onEdgeMouseLeave:K6,reconnectRadius:H7=10,onNodesChange:J7,onEdgesChange:Wd,noDragClassName:Ql="nodrag",noWheelClassName:Ea="nowheel",noPanClassName:Ig="nopan",fitView:i5,fitViewOptions:G7,connectOnClick:AM,attributionPosition:q7,proOptions:Jm,defaultEdgeOptions:V6,elevateNodesOnSelect:a2=!0,elevateEdgesOnSelect:h2=!1,disableKeyboardA11y:d2=!1,autoPanOnConnect:b2,autoPanOnNodeDrag:El,autoPanSpeed:r5,connectionRadius:U7,isValidConnection:_g,onError:g2,style:MM,id:Y6,nodeDragThreshold:X7,connectionDragThreshold:K7,viewport:Gm,onViewportChange:c5,width:e0,height:xh,colorMode:V7="light",debug:TM,onScroll:u5,ariaLabelConfig:Y7,zIndexMode:qm="basic",...xM},Ch){const Um=Y6||"1",Q7=lqn(V7),Q6=kn.useCallback(Lg=>{Lg.currentTarget.scrollTo({top:0,left:0,behavior:"instant"}),u5?.(Lg)},[u5]);return re.jsx("div",{"data-testid":"rf__wrapper",...xM,onScroll:Q6,style:{...MM,...CUn},ref:Ch,className:ka(["react-flow",D,Q7]),id:Y6,role:"application",children:re.jsxs(xUn,{nodes:g,edges:j,width:e0,height:xh,fitView:i5,fitViewOptions:G7,minZoom:bb,maxZoom:l1,nodeOrigin:eu,nodeExtent:Zv,zIndexMode:qm,children:[re.jsx(sqn,{nodes:g,edges:j,defaultNodes:A,defaultEdges:x,onConnect:le,onConnectStart:be,onConnectEnd:ne,onClickConnectStart:De,onClickConnectEnd:Se,nodesDraggable:kl,autoPanOnNodeFocus:s1,nodesConnectable:Za,nodesFocusable:Wa,edgesFocusable:Ng,edgesReconnectable:Ai,elementsSelectable:Mc,elevateNodesOnSelect:a2,elevateEdgesOnSelect:h2,minZoom:bb,maxZoom:l1,nodeExtent:Zv,onNodesChange:J7,onEdgesChange:Wd,snapToGrid:Ei,snapGrid:Bi,connectionMode:Me,translateExtent:$m,connectOnClick:AM,defaultEdgeOptions:V6,fitView:i5,fitViewOptions:G7,onNodesDelete:Ft,onEdgesDelete:In,onDelete:nt,onNodeDragStart:on,onNodeDrag:Mn,onNodeDragStop:Nn,onSelectionDrag:gn,onSelectionDragStart:Fe,onSelectionDragStop:Ae,onMove:te,onMoveStart:Q,onMoveEnd:Z,noPanClassName:Ig,nodeOrigin:eu,rfId:Um,autoPanOnConnect:b2,autoPanOnNodeDrag:El,autoPanSpeed:r5,onError:g2,connectionRadius:U7,isValidConnection:_g,selectNodesOnDrag:Du,nodeDragThreshold:X7,connectionDragThreshold:K7,onBeforeDelete:En,debug:TM,ariaLabelConfig:Y7,zIndexMode:qm}),re.jsx(AUn,{onInit:J,onNodeClick:H,onEdgeClick:U,onNodeMouseEnter:ze,onNodeMouseMove:de,onNodeMouseLeave:Le,onNodeContextMenu:an,onNodeDoubleClick:ln,nodeTypes:$,edgeTypes:E,connectionLineType:rn,connectionLineStyle:ut,connectionLineComponent:st,connectionLineContainerStyle:Kt,selectionKeyCode:oi,selectionOnDrag:Jt,selectionMode:hi,deleteKeyCode:li,multiSelectionKeyCode:Xr,panActivationKeyCode:nc,zoomActivationKeyCode:pr,onlyRenderVisibleElements:Fc,defaultViewport:no,translateExtent:$m,minZoom:bb,maxZoom:l1,preventScrolling:pM,zoomOnScroll:vM,zoomOnPinch:Rm,zoomOnDoubleClick:yM,panOnScroll:eh,panOnScrollSpeed:gb,panOnScrollMode:nh,panOnDrag:kM,onPaneClick:EM,onPaneMouseEnter:Wv,onPaneMouseMove:e5,onPaneMouseLeave:n5,onPaneScroll:Dg,onPaneContextMenu:t5,paneClickDistance:Bm,nodeClickDistance:jM,onSelectionContextMenu:Je,onSelectionStart:un,onSelectionEnd:jn,onReconnect:U6,onReconnectStart:zm,onReconnectEnd:SM,onEdgeContextMenu:Fm,onEdgeDoubleClick:X6,onEdgeMouseEnter:F7,onEdgeMouseMove:Hm,onEdgeMouseLeave:K6,reconnectRadius:H7,defaultMarkerColor:mM,noDragClassName:Ql,noWheelClassName:Ea,noPanClassName:Ig,rfId:Um,disableKeyboardA11y:d2,nodeExtent:Zv,viewport:Gm,onViewportChange:c5}),re.jsx(rqn,{onSelectionChange:Y}),z7,re.jsx(WGn,{proOptions:Jm,position:q7}),re.jsx(ZGn,{rfId:Um,disableKeyboardA11y:d2})]})})}var NUn=b0n(OUn);const DUn=g=>g.domNode?.querySelector(".react-flow__edgelabel-renderer");function IUn({children:g}){const j=Bu(DUn);return j?qGn.createPortal(g,j):null}function _Un(g){const[j,A]=kn.useState(g),x=kn.useCallback(D=>A($=>h0n(D,$)),[]);return[j,A,x]}function LUn(g){const[j,A]=kn.useState(g),x=kn.useCallback(D=>A($=>d0n(D,$)),[]);return[j,A,x]}function PUn({dimensions:g,lineWidth:j,variant:A,className:x}){return re.jsx("path",{strokeWidth:j,d:`M${g[0]/2} 0 V${g[1]} M0 ${g[1]/2} H${g[0]}`,className:ka(["react-flow__background-pattern",A,x])})}function $Un({radius:g,className:j}){return re.jsx("circle",{cx:g,cy:g,r:g,className:ka(["react-flow__background-pattern","dots",j])})}var B7;(function(g){g.Lines="lines",g.Dots="dots",g.Cross="cross"})(B7||(B7={}));const RUn={[B7.Dots]:1,[B7.Lines]:1,[B7.Cross]:6},BUn=g=>({transform:g.transform,patternId:`pattern-${g.rfId}`});function R0n({id:g,variant:j=B7.Dots,gap:A=20,size:x,lineWidth:D=1,offset:$=0,color:E,bgColor:H,style:U,className:J,patternClassName:te}){const Q=kn.useRef(null),{transform:Z,patternId:le}=Bu(BUn,vl),be=x||RUn[j],ne=j===B7.Dots,De=j===B7.Cross,Se=Array.isArray(A)?A:[A,A],ze=[Se[0]*Z[2]||1,Se[1]*Z[2]||1],de=be*Z[2],Le=Array.isArray($)?$:[$,$],an=De?[de,de]:ze,ln=[Le[0]*Z[2]||1+an[0]/2,Le[1]*Z[2]||1+an[1]/2],on=`${le}${g||""}`;return re.jsxs("svg",{className:ka(["react-flow__background",J]),style:{...U,...Due,"--xy-background-color-props":H,"--xy-background-pattern-color-props":E},ref:Q,"data-testid":"rf__background",children:[re.jsx("pattern",{id:on,x:Z[0]%ze[0],y:Z[1]%ze[1],width:ze[0],height:ze[1],patternUnits:"userSpaceOnUse",patternTransform:`translate(-${ln[0]},-${ln[1]})`,children:ne?re.jsx($Un,{radius:de/2,className:te}):re.jsx(PUn,{dimensions:an,lineWidth:D,variant:j,className:te})}),re.jsx("rect",{x:"0",y:"0",width:"100%",height:"100%",fill:`url(#${on})`})]})}R0n.displayName="Background";const zUn=kn.memo(R0n);function FUn(){return re.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",children:re.jsx("path",{d:"M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z"})})}function HUn(){return re.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 5",children:re.jsx("path",{d:"M0 0h32v4.2H0z"})})}function JUn(){return re.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 30",children:re.jsx("path",{d:"M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z"})})}function GUn(){return re.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32",children:re.jsx("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z"})})}function qUn(){return re.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32",children:re.jsx("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z"})})}function Wce({children:g,className:j,...A}){return re.jsx("button",{type:"button",className:ka(["react-flow__controls-button",j]),...A,children:g})}const UUn=g=>({isInteractive:g.nodesDraggable||g.nodesConnectable||g.elementsSelectable,minZoomReached:g.transform[2]<=g.minZoom,maxZoomReached:g.transform[2]>=g.maxZoom,ariaLabelConfig:g.ariaLabelConfig});function B0n({style:g,showZoom:j=!0,showFitView:A=!0,showInteractive:x=!0,fitViewOptions:D,onZoomIn:$,onZoomOut:E,onFitView:H,onInteractiveChange:U,className:J,children:te,position:Q="bottom-left",orientation:Z="vertical","aria-label":le}){const be=yl(),{isInteractive:ne,minZoomReached:De,maxZoomReached:Se,ariaLabelConfig:ze}=Bu(UUn,vl),{zoomIn:de,zoomOut:Le,fitView:an}=kke(),ln=()=>{de(),$?.()},on=()=>{Le(),E?.()},Mn=()=>{an(D),H?.()},Nn=()=>{be.setState({nodesDraggable:!ne,nodesConnectable:!ne,elementsSelectable:!ne}),U?.(!ne)},Ft=Z==="horizontal"?"horizontal":"vertical";return re.jsxs(Nue,{className:ka(["react-flow__controls",Ft,J]),position:Q,style:g,"data-testid":"rf__controls","aria-label":le??ze["controls.ariaLabel"],children:[j&&re.jsxs(re.Fragment,{children:[re.jsx(Wce,{onClick:ln,className:"react-flow__controls-zoomin",title:ze["controls.zoomIn.ariaLabel"],"aria-label":ze["controls.zoomIn.ariaLabel"],disabled:Se,children:re.jsx(FUn,{})}),re.jsx(Wce,{onClick:on,className:"react-flow__controls-zoomout",title:ze["controls.zoomOut.ariaLabel"],"aria-label":ze["controls.zoomOut.ariaLabel"],disabled:De,children:re.jsx(HUn,{})})]}),A&&re.jsx(Wce,{className:"react-flow__controls-fitview",onClick:Mn,title:ze["controls.fitView.ariaLabel"],"aria-label":ze["controls.fitView.ariaLabel"],children:re.jsx(JUn,{})}),x&&re.jsx(Wce,{className:"react-flow__controls-interactive",onClick:Nn,title:ze["controls.interactive.ariaLabel"],"aria-label":ze["controls.interactive.ariaLabel"],children:ne?re.jsx(qUn,{}):re.jsx(GUn,{})}),te]})}B0n.displayName="Controls";const XUn=kn.memo(B0n);function KUn({id:g,x:j,y:A,width:x,height:D,style:$,color:E,strokeColor:H,strokeWidth:U,className:J,borderRadius:te,shapeRendering:Q,selected:Z,onClick:le}){const{background:be,backgroundColor:ne}=$||{},De=E||be||ne;return re.jsx("rect",{className:ka(["react-flow__minimap-node",{selected:Z},J]),x:j,y:A,rx:te,ry:te,width:x,height:D,style:{fill:De,stroke:H,strokeWidth:U},shapeRendering:Q,onClick:le?Se=>le(Se,g):void 0})}const VUn=kn.memo(KUn),YUn=g=>g.nodes.map(j=>j.id),$7e=g=>g instanceof Function?g:()=>g;function QUn({nodeStrokeColor:g,nodeColor:j,nodeClassName:A="",nodeBorderRadius:x=5,nodeStrokeWidth:D,nodeComponent:$=VUn,onClick:E}){const H=Bu(YUn,vl),U=$7e(j),J=$7e(g),te=$7e(A),Q=typeof window>"u"||window.chrome?"crispEdges":"geometricPrecision";return re.jsx(re.Fragment,{children:H.map(Z=>re.jsx(WUn,{id:Z,nodeColorFunc:U,nodeStrokeColorFunc:J,nodeClassNameFunc:te,nodeBorderRadius:x,nodeStrokeWidth:D,NodeComponent:$,onClick:E,shapeRendering:Q},Z))})}function ZUn({id:g,nodeColorFunc:j,nodeStrokeColorFunc:A,nodeClassNameFunc:x,nodeBorderRadius:D,nodeStrokeWidth:$,shapeRendering:E,NodeComponent:H,onClick:U}){const{node:J,x:te,y:Q,width:Z,height:le}=Bu(be=>{const ne=be.nodeLookup.get(g);if(!ne)return{node:void 0,x:0,y:0,width:0,height:0};const De=ne.internals.userNode,{x:Se,y:ze}=ne.internals.positionAbsolute,{width:de,height:Le}=q6(De);return{node:De,x:Se,y:ze,width:de,height:Le}},vl);return!J||J.hidden||!zdn(J)?null:re.jsx(H,{x:te,y:Q,width:Z,height:le,style:J.style,selected:!!J.selected,className:x(J),color:j(J),borderRadius:D,strokeColor:A(J),strokeWidth:$,shapeRendering:E,onClick:U,id:J.id})}const WUn=kn.memo(ZUn);var eXn=kn.memo(QUn);const nXn=200,tXn=150,iXn=g=>!g.hidden,rXn=g=>{const j={x:-g.transform[0]/g.transform[2],y:-g.transform[1]/g.transform[2],width:g.width/g.transform[2],height:g.height/g.transform[2]};return{viewBB:j,boundingRect:g.nodeLookup.size>0?Bdn(QG(g.nodeLookup,{filter:iXn}),j):j,rfId:g.rfId,panZoom:g.panZoom,translateExtent:g.translateExtent,flowWidth:g.width,flowHeight:g.height,ariaLabelConfig:g.ariaLabelConfig}},cXn="react-flow__minimap-desc";function z0n({style:g,className:j,nodeStrokeColor:A,nodeColor:x,nodeClassName:D="",nodeBorderRadius:$=5,nodeStrokeWidth:E,nodeComponent:H,bgColor:U,maskColor:J,maskStrokeColor:te,maskStrokeWidth:Q,position:Z="bottom-right",onClick:le,onNodeClick:be,pannable:ne=!1,zoomable:De=!1,ariaLabel:Se,inversePan:ze,zoomStep:de=1,offsetScale:Le=5}){const an=yl(),ln=kn.useRef(null),{boundingRect:on,viewBB:Mn,rfId:Nn,panZoom:Ft,translateExtent:In,flowWidth:nt,flowHeight:Y,ariaLabelConfig:Fe}=Bu(rXn,vl),gn=g?.width??nXn,Ae=g?.height??tXn,Je=on.width/gn,un=on.height/Ae,jn=Math.max(Je,un),En=jn*gn,Me=jn*Ae,rn=Le*jn,ut=on.x-(En-on.width)/2-rn,st=on.y-(Me-on.height)/2-rn,Kt=En+rn*2,li=Me+rn*2,oi=`${cXn}-${Nn}`,Jt=kn.useRef(0),hi=kn.useRef();Jt.current=jn,kn.useEffect(()=>{if(ln.current&&Ft)return hi.current=mGn({domNode:ln.current,panZoom:Ft,getTransform:()=>an.getState().transform,getViewScale:()=>Jt.current}),()=>{hi.current?.destroy()}},[Ft]),kn.useEffect(()=>{hi.current?.update({translateExtent:In,width:nt,height:Y,inversePan:ze,pannable:ne,zoomStep:de,zoomable:De})},[ne,De,ze,de,In,nt,Y]);const nc=le?Ei=>{const[Bi,Fc]=hi.current?.pointer(Ei)||[0,0];le(Ei,{x:Bi,y:Fc})}:void 0,Xr=be?kn.useCallback((Ei,Bi)=>{const Fc=an.getState().nodeLookup.get(Bi).internals.userNode;be(Ei,Fc)},[]):void 0,pr=Se??Fe["minimap.ariaLabel"];return re.jsx(Nue,{position:Z,style:{...g,"--xy-minimap-background-color-props":typeof U=="string"?U:void 0,"--xy-minimap-mask-background-color-props":typeof J=="string"?J:void 0,"--xy-minimap-mask-stroke-color-props":typeof te=="string"?te:void 0,"--xy-minimap-mask-stroke-width-props":typeof Q=="number"?Q*jn:void 0,"--xy-minimap-node-background-color-props":typeof x=="string"?x:void 0,"--xy-minimap-node-stroke-color-props":typeof A=="string"?A:void 0,"--xy-minimap-node-stroke-width-props":typeof E=="number"?E:void 0},className:ka(["react-flow__minimap",j]),"data-testid":"rf__minimap",children:re.jsxs("svg",{width:gn,height:Ae,viewBox:`${ut} ${st} ${Kt} ${li}`,className:"react-flow__minimap-svg",role:"img","aria-labelledby":oi,ref:ln,onClick:nc,children:[pr&&re.jsx("title",{id:oi,children:pr}),re.jsx(eXn,{onClick:Xr,nodeColor:x,nodeStrokeColor:A,nodeBorderRadius:$,nodeClassName:D,nodeStrokeWidth:E,nodeComponent:H}),re.jsx("path",{className:"react-flow__minimap-mask",d:`M${ut-rn},${st-rn}h${Kt+rn*2}v${li+rn*2}h${-Kt-rn*2}z - M${Mn.x},${Mn.y}h${Mn.width}v${Mn.height}h${-Mn.width}z`,fillRule:"evenodd",pointerEvents:"none"})]})})}z0n.displayName="MiniMap";const uXn=kn.memo(z0n),oXn=g=>j=>g?`${Math.max(1/j.transform[2],1)}`:void 0,sXn={[gI.Line]:"right",[gI.Handle]:"bottom-right"};function lXn({nodeId:g,position:j,variant:A=gI.Handle,className:x,style:D=void 0,children:$,color:E,minWidth:H=10,minHeight:U=10,maxWidth:J=Number.MAX_VALUE,maxHeight:te=Number.MAX_VALUE,keepAspectRatio:Q=!1,resizeDirection:Z,autoScale:le=!0,shouldResize:be,onResizeStart:ne,onResize:De,onResizeEnd:Se}){const ze=m0n(),de=typeof g=="string"?g:ze,Le=yl(),an=kn.useRef(null),ln=A===gI.Handle,on=Bu(kn.useCallback(oXn(ln&&le),[ln,le]),vl),Mn=kn.useRef(null),Nn=j??sXn[A];kn.useEffect(()=>{if(!(!an.current||!de))return Mn.current||(Mn.current=DGn({domNode:an.current,nodeId:de,getStoreItems:()=>{const{nodeLookup:In,transform:nt,snapGrid:Y,snapToGrid:Fe,nodeOrigin:gn,domNode:Ae}=Le.getState();return{nodeLookup:In,transform:nt,snapGrid:Y,snapToGrid:Fe,nodeOrigin:gn,paneDomNode:Ae}},onChange:(In,nt)=>{const{triggerNodeChanges:Y,nodeLookup:Fe,parentLookup:gn,nodeOrigin:Ae}=Le.getState(),Je=[],un={x:In.x,y:In.y},jn=Fe.get(de);if(jn&&jn.expandParent&&jn.parentId){const En=jn.origin??Ae,Me=In.width??jn.measured.width??0,rn=In.height??jn.measured.height??0,ut={id:jn.id,parentId:jn.parentId,rect:{width:Me,height:rn,...Fdn({x:In.x??jn.position.x,y:In.y??jn.position.y},{width:Me,height:rn},jn.parentId,Fe,En)}},st=yke([ut],Fe,gn,Ae);Je.push(...st),un.x=In.x?Math.max(En[0]*Me,In.x):void 0,un.y=In.y?Math.max(En[1]*rn,In.y):void 0}if(un.x!==void 0&&un.y!==void 0){const En={id:de,type:"position",position:{...un}};Je.push(En)}if(In.width!==void 0&&In.height!==void 0){const Me={id:de,type:"dimensions",resizing:!0,setAttributes:Z?Z==="horizontal"?"width":"height":!0,dimensions:{width:In.width,height:In.height}};Je.push(Me)}for(const En of nt){const Me={...En,type:"position"};Je.push(Me)}Y(Je)},onEnd:({width:In,height:nt})=>{const Y={id:de,type:"dimensions",resizing:!1,dimensions:{width:In,height:nt}};Le.getState().triggerNodeChanges([Y])}})),Mn.current.update({controlPosition:Nn,boundaries:{minWidth:H,minHeight:U,maxWidth:J,maxHeight:te},keepAspectRatio:Q,resizeDirection:Z,onResizeStart:ne,onResize:De,onResizeEnd:Se,shouldResize:be}),()=>{Mn.current?.destroy()}},[Nn,H,U,J,te,Q,ne,De,Se,be]);const Ft=Nn.split("-");return re.jsx("div",{className:ka(["react-flow__resize-control","nodrag",...Ft,A,x]),ref:an,style:{...D,scale:on,...E&&{[ln?"backgroundColor":"borderColor"]:E}},children:$})}kn.memo(lXn);const fXn=g=>g.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),F0n=(...g)=>g.filter((j,A,x)=>!!j&&j.trim()!==""&&x.indexOf(j)===A).join(" ").trim();var aXn={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};const hXn=kn.forwardRef(({color:g="currentColor",size:j=24,strokeWidth:A=2,absoluteStrokeWidth:x,className:D="",children:$,iconNode:E,...H},U)=>kn.createElement("svg",{ref:U,...aXn,width:j,height:j,stroke:g,strokeWidth:x?Number(A)*24/Number(j):A,className:F0n("lucide",D),...H},[...E.map(([J,te])=>kn.createElement(J,te)),...Array.isArray($)?$:[$]]));const ed=(g,j)=>{const A=kn.forwardRef(({className:x,...D},$)=>kn.createElement(hXn,{ref:$,iconNode:j,className:F0n(`lucide-${fXn(g)}`,x),...D}));return A.displayName=`${g}`,A};const dXn=ed("Box",[["path",{d:"M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z",key:"hh9hay"}],["path",{d:"m3.3 7 8.7 5 8.7-5",key:"g66t2b"}],["path",{d:"M12 22V12",key:"d0xqtd"}]]);const bXn=ed("Boxes",[["path",{d:"M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z",key:"lc1i9w"}],["path",{d:"m7 16.5-4.74-2.85",key:"1o9zyk"}],["path",{d:"m7 16.5 5-3",key:"va8pkn"}],["path",{d:"M7 16.5v5.17",key:"jnp8gn"}],["path",{d:"M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z",key:"8zsnat"}],["path",{d:"m17 16.5-5-3",key:"8arw3v"}],["path",{d:"m17 16.5 4.74-2.85",key:"8rfmw"}],["path",{d:"M17 16.5v5.17",key:"k6z78m"}],["path",{d:"M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z",key:"1xygjf"}],["path",{d:"M12 8 7.26 5.15",key:"1vbdud"}],["path",{d:"m12 8 4.74-2.85",key:"3rx089"}],["path",{d:"M12 13.5V8",key:"1io7kd"}]]);const gXn=ed("Check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);const wXn=ed("CircleAlert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);const pXn=ed("Clock3",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["polyline",{points:"12 6 12 12 16.5 12",key:"1aq6pp"}]]);const mXn=ed("CodeXml",[["path",{d:"m18 16 4-4-4-4",key:"1inbqp"}],["path",{d:"m6 8-4 4 4 4",key:"15zrgr"}],["path",{d:"m14.5 4-5 16",key:"e7oirm"}]]);const vXn=ed("GitBranch",[["line",{x1:"6",x2:"6",y1:"3",y2:"15",key:"17qcm7"}],["circle",{cx:"18",cy:"6",r:"3",key:"1h7g24"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}],["path",{d:"M18 9a9 9 0 0 1-9 9",key:"n2h4wq"}]]);const H0n=ed("Layers",[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",key:"zw3jo"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",key:"1wduqc"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",key:"kqbvx6"}]]);const H1n=ed("Link2",[["path",{d:"M9 17H7A5 5 0 0 1 7 7h2",key:"8i5ue5"}],["path",{d:"M15 7h2a5 5 0 1 1 0 10h-2",key:"1b9ql8"}],["line",{x1:"8",x2:"16",y1:"12",y2:"12",key:"1jonct"}]]);const yXn=ed("Network",[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]]);const J0n=ed("Plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);const kXn=ed("Redo2",[["path",{d:"m15 14 5-5-5-5",key:"12vg1m"}],["path",{d:"M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13",key:"6uklza"}]]);const EXn=ed("Search",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]]);const J1n=ed("TriangleAlert",[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);const jXn=ed("Undo2",[["path",{d:"M9 14 4 9l5-5",key:"102s5s"}],["path",{d:"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11",key:"f3b9sd"}]]);const wI=ed("X",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);function SXn({mode:g,models:j,objects:A,application:x,initialModelType:D,suggestedSelector:$,onSubmit:E,onClose:H}){const U=x?.modelType||D||j[0]?.type||"",[J,te]=kn.useState(U),Q=j.find(En=>En.type===J)??null,[Z,le]=kn.useState(x?.name||x?.applicationId||q1n(Q)),[be,ne]=kn.useState(()=>G1n(Q,x)),De=x?.selector||$||TXn(A),[Se,ze]=kn.useState(De.multiplicity),[de,Le]=kn.useState(nue(De,"scale")),[an,ln]=kn.useState(nue(De,"kind")),[on,Mn]=kn.useState(nue(De,"species")),[Nn,Ft]=kn.useState(nue(De,"name")),[In,nt]=kn.useState(x?.timestep?"clock":"default"),[Y,Fe]=kn.useState("1.0"),[gn,Ae]=kn.useState("0.0");kn.useEffect(()=>{const En=j.find(Me=>Me.type===J)??null;ne(G1n(En,g==="update"?x:void 0)),g==="add"&&le(q1n(En))},[x,g,J,j]);const Je=kn.useMemo(()=>({scales:_G(A.map(En=>En.scale)),kinds:_G(A.map(En=>En.kind)),species:_G(A.map(En=>En.species)),names:_G(A.map(En=>En.name))}),[A]),un=kn.useMemo(()=>{const En=[de&&`scale ${de}`,an&&`kind ${an}`,on&&`species ${on}`,Nn&&`name ${Nn}`].filter(Boolean);return En.length?En.join(", "):"all scene objects"},[an,Nn,de,on]),jn=()=>{const En={selectors:[]};de&&(En.scale=de),an&&(En.kind=an),on&&(En.species=on),Nn&&(En.name=Nn),E({applicationId:x?.applicationId,modelType:J,name:Z.trim(),parameters:be,selector:{type:xXn(Se),multiplicity:Se,criteria:En,julia:""},timestep:In==="clock"?{mode:"clock",dt:Y,phase:gn}:{mode:"default"}})};return re.jsx("div",{className:"overlay-backdrop",onMouseDown:H,children:re.jsxs("section",{className:"overlay-panel application-form",onMouseDown:En=>En.stopPropagation(),"data-testid":"application-form",children:[re.jsxs("header",{children:[re.jsxs("div",{children:[re.jsx("strong",{children:g==="add"?"Add application":`Update ${x?.applicationId}`}),re.jsx("span",{children:"A configured use of a model on selected scene objects"})]}),re.jsx("button",{onClick:H,children:re.jsx(wI,{size:17})})]}),re.jsxs("div",{className:"overlay-content application-form-content",children:[re.jsxs("label",{children:["Model",re.jsx("select",{value:J,onChange:En=>te(En.target.value),"data-testid":"application-model-select",children:j.map(En=>re.jsxs("option",{value:En.type,children:[En.package?`${En.package} · `:"",En.name," (",En.process,")"]},En.type))})]}),re.jsxs("label",{children:["Application name",re.jsx("input",{value:Z,onChange:En=>le(En.target.value),"data-testid":"application-name"})]}),Q&&Q.constructor.fields.length>0&&re.jsxs("fieldset",{children:[re.jsx("legend",{children:"Model parameters"}),re.jsx(AXn,{fields:Q.constructor.fields,values:be,onChange:ne})]}),re.jsxs("fieldset",{children:[re.jsx("legend",{children:"Target selector"}),re.jsxs("div",{className:"form-grid",children:[re.jsxs("label",{children:["Multiplicity",re.jsxs("select",{value:Se,onChange:En=>ze(En.target.value),children:[re.jsx("option",{value:"one",children:"One"}),re.jsx("option",{value:"optional_one",children:"Optional one"}),re.jsx("option",{value:"many",children:"Many"})]})]}),re.jsx(eue,{label:"Scale",value:de,options:Je.scales,onChange:Le}),re.jsx(eue,{label:"Kind",value:an,options:Je.kinds,onChange:ln}),re.jsx(eue,{label:"Species",value:on,options:Je.species,onChange:Mn}),re.jsx(eue,{label:"Object name",value:Nn,options:Je.names,onChange:Ft})]}),re.jsxs("p",{className:"selector-summary",children:["Julia will resolve ",re.jsx("strong",{children:Se.replace("_"," ")})," target from ",un,"."]})]}),re.jsxs("fieldset",{children:[re.jsx("legend",{children:"Timestep"}),re.jsxs("div",{className:"form-grid",children:[re.jsxs("label",{children:["Mode",re.jsxs("select",{value:In,onChange:En=>nt(En.target.value),children:[re.jsx("option",{value:"default",children:"Model or environment default"}),re.jsx("option",{value:"clock",children:"Explicit clock"})]})]}),In==="clock"&&re.jsxs(re.Fragment,{children:[re.jsxs("label",{children:["Step",re.jsx("input",{value:Y,onChange:En=>Fe(En.target.value)})]}),re.jsxs("label",{children:["Phase",re.jsx("input",{value:gn,onChange:En=>Ae(En.target.value)})]})]})]})]})]}),re.jsxs("footer",{children:[re.jsx("button",{onClick:H,children:"Cancel"}),re.jsxs("button",{className:"primary",disabled:!J||!Z.trim(),onClick:jn,"data-testid":"application-submit",children:[re.jsx(gXn,{size:15})," ",g==="add"?"Add application":"Apply changes"]})]})]})})}function AXn({fields:g,values:j,onChange:A}){const x=new Map;for(const $ of g)$.typeParameter&&!x.has($.typeParameter)&&x.set($.typeParameter,$.name);const D=($,E)=>{const H=$.typeParameter?g.filter(U=>U.typeParameter===$.typeParameter).map(U=>U.name):[$.name];A(Object.fromEntries(Object.entries(j).map(([U,J])=>[U,H.includes(U)?{...J,type:E}:J])))};return re.jsx("div",{className:"parameter-list",children:g.map($=>{const E=j[$.name]||{type:$.inferredChoice,value:""},H=!$.typeParameter||x.get($.typeParameter)===$.name;return re.jsxs("div",{className:"parameter-row",children:[re.jsxs("label",{children:[re.jsx("span",{children:$.name}),re.jsx("small",{children:$.declaredType}),re.jsx("input",{value:E.value,onChange:U=>A({...j,[$.name]:{...E,value:U.target.value}})})]}),H&&re.jsxs("label",{className:"parameter-type",children:[re.jsx("span",{children:$.typeParameter?`${$.typeParameter} type`:"Value type"}),re.jsx("select",{value:E.type,onChange:U=>D($,U.target.value),children:$.choices.map(U=>re.jsx("option",{value:U,children:U},U))})]})]},$.name)})})}function eue({label:g,value:j,options:A,onChange:x}){return re.jsxs("label",{children:[g,re.jsxs("select",{value:j,onChange:D=>x(D.target.value),children:[re.jsx("option",{value:"",children:"Any"}),A.map(D=>re.jsx("option",{value:D,children:D},D))]})]})}function G1n(g,j){return g?Object.fromEntries(g.constructor.fields.map(A=>{const x=j?.modelParameters[A.name],D=x?.type||A.inferredChoice,$=x?x.julia:A.hasDefault?D==="julia"?A.defaultJulia||"":MXn(A.default,D):"";return[A.name,{type:D,value:$}]})):{}}function MXn(g,j){const A=g==null?"":String(g);return j==="symbol"?A.replace(/^:/,""):A}function q1n(g){return g?.process||g?.name||"application"}function TXn(g){const j=_G(g.map(A=>A.scale))[0];return{type:"Many",multiplicity:"many",criteria:j?{selectors:[],scale:j}:{selectors:[]},julia:""}}function nue(g,j){const A=g.criteria[j];return typeof A=="string"?A:""}function xXn(g){return g==="one"?"One":g==="optional_one"?"OptionalOne":"Many"}function _G(g){return[...new Set(g.filter(j=>!!j))].sort()}function CXn({endpoints:g,objects:j,onSubmit:A,onClose:x}){const D=OXn(g.sourceApplication.targetIds,g.targetApplication.targetIds),[$,E]=kn.useState(g.sourceApplication.targetCount>1&&g.targetApplication.targetCount===1?"many":"one"),[H,U]=kn.useState(D?"self":""),[J,te]=kn.useState(U1n(g.sourceApplication.targetScales)),[Q,Z]=kn.useState(U1n(g.sourceApplication.targetKinds)),[le,be]=kn.useState(""),ne=B7e(j.map(de=>de.scale)),De=B7e(j.map(de=>de.kind)),Se=B7e(j.map(de=>de.name)),ze=()=>{const de={selectors:[],application:g.sourceApplication.applicationId,var:g.sourcePort.name};H&&(de.relation=H),J&&(de.scale=J),Q&&(de.kind=Q),le&&(de.name=le),A({applicationId:g.targetApplication.applicationId,input:g.targetPort.name,selector:{type:NXn($),multiplicity:$,criteria:de,julia:""}})};return re.jsx("div",{className:"overlay-backdrop",onMouseDown:x,children:re.jsxs("section",{className:"overlay-panel binding-form",onMouseDown:de=>de.stopPropagation(),"data-testid":"binding-form",children:[re.jsxs("header",{children:[re.jsxs("div",{children:[re.jsx("strong",{children:"Connect applications"}),re.jsx("span",{children:"Julia resolves this declaration into concrete object bindings"})]}),re.jsx("button",{onClick:x,children:re.jsx(wI,{size:17})})]}),re.jsxs("div",{className:"overlay-content",children:[re.jsxs("div",{className:"binding-route",children:[re.jsxs("div",{children:[re.jsx("small",{children:"Producer"}),re.jsx("strong",{children:g.sourceApplication.applicationId}),re.jsx("code",{children:g.sourcePort.name})]}),re.jsx(H1n,{size:22}),re.jsxs("div",{children:[re.jsx("small",{children:"Consumer"}),re.jsx("strong",{children:g.targetApplication.applicationId}),re.jsx("code",{children:g.targetPort.name})]})]}),re.jsxs("fieldset",{children:[re.jsx("legend",{children:"Source object selector"}),re.jsxs("div",{className:"form-grid",children:[re.jsxs("label",{children:["Multiplicity",re.jsxs("select",{value:$,onChange:de=>E(de.target.value),children:[re.jsx("option",{value:"one",children:"One"}),re.jsx("option",{value:"optional_one",children:"Optional one"}),re.jsx("option",{value:"many",children:"Many"})]})]}),re.jsxs("label",{children:["Relation",re.jsxs("select",{value:H,onChange:de=>U(de.target.value),children:[re.jsx("option",{value:"",children:"Any relation"}),re.jsx("option",{value:"self",children:"Same object"}),re.jsx("option",{value:"parent",children:"Parent"}),re.jsx("option",{value:"children",children:"Children"}),re.jsx("option",{value:"ancestors",children:"Ancestors"}),re.jsx("option",{value:"descendants",children:"Descendants"}),re.jsx("option",{value:"siblings",children:"Siblings"})]})]}),re.jsx(R7e,{label:"Scale",value:J,options:ne,onChange:te}),re.jsx(R7e,{label:"Kind",value:Q,options:De,onChange:Z}),re.jsx(R7e,{label:"Object name",value:le,options:Se,onChange:be})]})]})]}),re.jsxs("footer",{children:[re.jsx("button",{onClick:x,children:"Cancel"}),re.jsxs("button",{className:"primary",onClick:ze,"data-testid":"binding-submit",children:[re.jsx(H1n,{size:15})," Apply binding"]})]})]})})}function R7e({label:g,value:j,options:A,onChange:x}){return re.jsxs("label",{children:[g,re.jsxs("select",{value:j,onChange:D=>x(D.target.value),children:[re.jsx("option",{value:"",children:"Any"}),A.map(D=>re.jsx("option",{value:D,children:D},D))]})]})}function OXn(g,j){return g.length===j.length&&g.every(A=>j.some(x=>String(x)===String(A)))}function U1n(g){return g.length===1?g[0]:""}function NXn(g){return g==="one"?"One":g==="optional_one"?"OptionalOne":"Many"}function B7e(g){return[...new Set(g.filter(j=>!!j))].sort()}function G0n(g){if(g.detailMode==="overview")return 190;const j=Math.max(g.applicationId.length,g.modelName.length,...g.inputs.map(A=>A.name.length),...g.outputs.map(A=>A.name.length));return Math.max(310,Math.min(540,235+j*7))}function DXn({data:g,selected:j}){const A=g.detailMode==="overview",x=new Set(g.requiredInputPortIds),D=new Set(g.candidatePortIds),$=new Set(g.previousTimeStepPortIds),E=LXn(g);return re.jsxs("section",{className:`model-node application-node ${A?"overview-node":""} ${g.cyclic?"cyclic":""} ${j?"selected":""}`,"data-testid":`application-node-${g.applicationId}`,style:{width:G0n(g)},children:[A&&re.jsx(_Xn,{inputs:g.inputs,outputs:g.outputs}),re.jsxs("header",{className:"node-header",children:[re.jsxs("div",{children:[re.jsx("div",{className:"process",children:g.name||g.applicationId}),re.jsx("div",{className:"model-type",children:g.modelName})]}),re.jsx(H0n,{size:18})]}),A?re.jsxs("div",{className:"overview-node-summary",children:[re.jsxs("span",{children:[g.targetCount," targets"]}),re.jsxs("span",{children:[g.inputs.length," in"]}),re.jsxs("span",{children:[g.outputs.length," out"]})]}):re.jsxs(re.Fragment,{children:[re.jsxs("div",{className:"node-meta",children:[re.jsxs("span",{className:"meta-chip",title:g.selector.julia,children:[re.jsx(dXn,{size:13})," ",E]}),re.jsxs("span",{className:"meta-chip",title:String(g.clock??"Default scene cadence"),children:[re.jsx(pXn,{size:13})," ",PXn(g)]})]}),re.jsxs("div",{className:"target-summary",children:[re.jsx("strong",{children:g.targetCount})," concrete target",g.targetCount===1?"":"s"]}),re.jsxs("div",{className:"ports-grid",children:[re.jsx(X1n,{title:"Inputs",side:"input",ports:g.inputs,required:x,candidates:D,previous:$,onCandidateClick:g.onCandidateClick,onPortClick:g.onPortClick}),re.jsx(X1n,{title:"Outputs",side:"output",ports:g.outputs,required:x,candidates:D,previous:$,onCandidateClick:g.onCandidateClick,onPortClick:g.onPortClick})]})]})]})}function IXn({data:g,selected:j}){return re.jsxs("section",{className:`entity-node ${g.nodeKind} ${j?"selected":""}`,"data-testid":`${g.nodeKind}-node`,children:[(g.inputPortIds?.length?g.inputPortIds:[void 0]).map((A,x)=>re.jsx(Yv,{id:A,type:"target",position:cr.Left,style:{top:`${kue(x,g.inputPortIds?.length??1)}%`}},A??"target")),re.jsxs("header",{children:[re.jsx("strong",{children:g.title}),re.jsx("span",{children:g.subtitle})]}),re.jsx("div",{className:"badges",children:g.badges.map(A=>re.jsx("span",{className:"meta-chip",children:A},A))}),(g.outputPortIds?.length?g.outputPortIds:[void 0]).map((A,x)=>re.jsx(Yv,{id:A,type:"source",position:cr.Right,style:{top:`${kue(x,g.outputPortIds?.length??1)}%`}},A??"source"))]})}function X1n({title:g,side:j,ports:A,required:x,candidates:D,previous:$,onCandidateClick:E,onPortClick:H}){return re.jsxs("div",{className:`port-column ${j}`,children:[re.jsx("div",{className:"port-title",children:g}),A.map(U=>re.jsxs("div",{className:`port ${x.has(U.id)?"required-input":""} ${$.has(U.id)?"previous":""}`,"data-testid":`port-${j}-${U.name}`,title:`${U.name}: ${U.defaultJulia}`,onClick:J=>{J.stopPropagation(),H?.(U)},children:[j==="input"&&re.jsx(Yv,{id:U.id,type:"target",position:cr.Left}),re.jsx("span",{children:U.name}),D.has(U.id)&&re.jsx("button",{className:"port-candidate-button nodrag nopan",type:"button",title:j==="input"?"Models that compute this variable":"Models that consume this variable","aria-label":j==="input"?`Models that compute ${U.name}`:`Models that consume ${U.name}`,onClick:J=>{J.stopPropagation();const te=J.currentTarget.getBoundingClientRect();E?.(U,{x:te.right,y:te.top+te.height/2})},children:re.jsx(J0n,{size:11})}),$.has(U.id)&&re.jsx("small",{className:"previous-label",children:"t-1"}),j==="output"&&re.jsx(Yv,{id:U.id,type:"source",position:cr.Right})]},U.id))]})}function _Xn({inputs:g,outputs:j}){return re.jsxs(re.Fragment,{children:[g.map((A,x)=>re.jsx(Yv,{id:A.id,type:"target",position:cr.Left,style:{top:`${kue(x,g.length)}%`}},A.id)),j.map((A,x)=>re.jsx(Yv,{id:A.id,type:"source",position:cr.Right,style:{top:`${kue(x,j.length)}%`}},A.id))]})}function kue(g,j){return j<=1?52:28+g/(j-1)*48}function LXn(g){const j=[...g.targetInstances,...g.targetScales,...g.targetKinds];return j.length>0?j.slice(0,2).join(" / "):g.selector.type}function PXn(g){return g.timestep===null||g.timestep===void 0?"default rate":String(g.timestep)}function $Xn({id:g,sourceX:j,sourceY:A,targetX:x,targetY:D,sourcePosition:$=cr.Right,targetPosition:E=cr.Left,markerEnd:H,style:U,data:J}){const[te,Q,Z]=vue({sourceX:j,sourceY:A,targetX:x,targetY:D,sourcePosition:$,targetPosition:E,borderRadius:14,offset:24}),le=RXn(J);return re.jsxs(re.Fragment,{children:[re.jsx(eq,{id:g,path:te,markerEnd:H,style:U,interactionWidth:18}),le&&re.jsx(IUn,{children:re.jsx("div",{className:`edge-chip ${J?.kind??""} ${J?.cycle?"cycle":""}`,style:{transform:`translate(-50%, -50%) translate(${Q}px, ${Z-12}px)`},children:le})})]})}function RXn(g){return g?g.kind==="manual_call"?g.call||"call":g.kind==="object_topology"||g.kind==="application_target"?"":g.sourceVariable&&g.targetVariable?g.sourceVariable===g.targetVariable?g.sourceVariable:`${g.sourceVariable} → ${g.targetVariable}`:g.kind.replaceAll("_"," "):""}function tue(g){throw new Error('Could not dynamically require "'+g+'". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.')}var z7e={exports:{}},K1n;function BXn(){return K1n||(K1n=1,(function(g,j){(function(A){g.exports=A()})(function(){return(function(){function A(x,D,$){function E(J,te){if(!D[J]){if(!x[J]){var Q=typeof tue=="function"&&tue;if(!te&&Q)return Q(J,!0);if(H)return H(J,!0);var Z=new Error("Cannot find module '"+J+"'");throw Z.code="MODULE_NOT_FOUND",Z}var le=D[J]={exports:{}};x[J][0].call(le.exports,function(be){var ne=x[J][1][be];return E(ne||be)},le,le.exports,A,x,D,$)}return D[J].exports}for(var H=typeof tue=="function"&&tue,U=0;U<$.length;U++)E($[U]);return E}return A})()({1:[function(A,x,D){Object.defineProperty(D,"__esModule",{value:!0}),D.default=void 0;function $(Z){"@babel/helpers - typeof";return $=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(le){return typeof le}:function(le){return le&&typeof Symbol=="function"&&le.constructor===Symbol&&le!==Symbol.prototype?"symbol":typeof le},$(Z)}function E(Z,le){if(!(Z instanceof le))throw new TypeError("Cannot call a class as a function")}function H(Z,le){for(var be=0;be0&&arguments[0]!==void 0?arguments[0]:{},ne=be.defaultLayoutOptions,De=ne===void 0?{}:ne,Se=be.algorithms,ze=Se===void 0?["layered","stress","mrtree","radial","force","disco","sporeOverlap","sporeCompaction","rectpacking"]:Se,de=be.workerFactory,Le=be.workerUrl;if(E(this,Z),this.defaultLayoutOptions=De,this.initialized=!1,typeof Le>"u"&&typeof de>"u")throw new Error("Cannot construct an ELK without both 'workerUrl' and 'workerFactory'.");var an=de;typeof Le<"u"&&typeof de>"u"&&(an=function(Mn){return new Worker(Mn)});var ln=an(Le);if(typeof ln.postMessage!="function")throw new TypeError("Created worker does not provide the required 'postMessage' function.");this.worker=new Q(ln),this.worker.postMessage({cmd:"register",algorithms:ze}).then(function(on){return le.initialized=!0}).catch(console.err)}return U(Z,[{key:"layout",value:function(be){var ne=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},De=ne.layoutOptions,Se=De===void 0?this.defaultLayoutOptions:De,ze=ne.logging,de=ze===void 0?!1:ze,Le=ne.measureExecutionTime,an=Le===void 0?!1:Le;return be?this.worker.postMessage({cmd:"layout",graph:be,layoutOptions:Se,options:{logging:de,measureExecutionTime:an}}):Promise.reject(new Error("Missing mandatory parameter 'graph'."))}},{key:"knownLayoutAlgorithms",value:function(){return this.worker.postMessage({cmd:"algorithms"})}},{key:"knownLayoutOptions",value:function(){return this.worker.postMessage({cmd:"options"})}},{key:"knownLayoutCategories",value:function(){return this.worker.postMessage({cmd:"categories"})}},{key:"terminateWorker",value:function(){this.worker&&this.worker.terminate()}}])})();var Q=(function(){function Z(le){var be=this;if(E(this,Z),le===void 0)throw new Error("Missing mandatory parameter 'worker'.");this.resolvers={},this.worker=le,this.worker.onmessage=function(ne){setTimeout(function(){be.receive(be,ne)},0)}}return U(Z,[{key:"postMessage",value:function(be){var ne=this.id||0;this.id=ne+1,be.id=ne;var De=this;return new Promise(function(Se,ze){De.resolvers[ne]=function(de,Le){de?(De.convertGwtStyleError(de),ze(de)):Se(Le)},De.worker.postMessage(be)})}},{key:"receive",value:function(be,ne){var De=ne.data,Se=be.resolvers[De.id];Se&&(delete be.resolvers[De.id],De.error?Se(De.error):Se(null,De.data))}},{key:"terminate",value:function(){this.worker&&this.worker.terminate()}},{key:"convertGwtStyleError",value:function(be){if(be){var ne=be.__java$exception;ne&&(ne.cause&&ne.cause.backingJsObject&&(be.cause=ne.cause.backingJsObject,this.convertGwtStyleError(be.cause)),delete be.__java$exception)}}}])})()},{}],2:[function(A,x,D){(function($){(function(){var E;typeof window<"u"?E=window:typeof $<"u"?E=$:typeof self<"u"&&(E=self);var H;function U(){}function J(){}function te(){}function Q(){}function Z(){}function le(){}function be(){}function ne(){}function De(){}function Se(){}function ze(){}function de(){}function Le(){}function an(){}function ln(){}function on(){}function Mn(){}function Nn(){}function Ft(){}function In(){}function nt(){}function Y(){}function Fe(){}function gn(){}function Ae(){}function Je(){}function un(){}function jn(){}function En(){}function Me(){}function rn(){}function ut(){}function st(){}function Kt(){}function li(){}function oi(){}function Jt(){}function hi(){}function nc(){}function Xr(){}function pr(){}function Ei(){}function Bi(){}function Fc(){}function Du(){}function kl(){}function s1(){}function Za(){}function Wa(){}function eu(){}function Ng(){}function Ai(){}function Mc(){}function no(){}function bb(){}function l1(){}function $m(){}function pM(){}function Zv(){}function mM(){}function vM(){}function Rm(){}function eh(){}function gb(){}function nh(){}function yM(){}function kM(){}function EM(){}function Wv(){}function e5(){}function n5(){}function Dg(){}function t5(){}function Bm(){}function jM(){}function z7(){}function U6(){}function zm(){}function SM(){}function Fm(){}function X6(){}function F7(){}function Hm(){}function K6(){}function H7(){}function J7(){}function Wd(){}function Ql(){}function Ea(){}function Ig(){}function i5(){}function G7(){}function AM(){}function q7(){}function Jm(){}function V6(){}function a2(){}function h2(){}function d2(){}function b2(){}function El(){}function r5(){}function U7(){}function _g(){}function g2(){}function MM(){}function Y6(){}function X7(){}function K7(){}function Gm(){}function c5(){}function e0(){}function xh(){}function V7(){}function TM(){}function u5(){}function Y7(){}function qm(){}function xM(){}function Ch(){}function Um(){}function Q7(){}function Q6(){}function Lg(){}function pI(){}function mI(){}function o5(){}function nq(){}function vI(){}function yI(){}function CM(){}function tq(){}function iq(){}function Z7(){}function Pg(){}function OM(){}function NM(){}function s5(){}function l5(){}function kI(){}function DM(){}function EI(){}function Z6(){}function $g(){}function IM(){}function W6(){}function w2(){}function _M(){}function W7(){}function jI(){}function ek(){}function nk(){}function SI(){}function Oh(){}function Xm(){}function tk(){}function ey(){}function rq(){}function LM(){}function PM(){}function ny(){}function ik(){}function AI(){}function cq(){}function uq(){}function oq(){}function $M(){}function sq(){}function lq(){}function fq(){}function aq(){}function hq(){}function MI(){}function dq(){}function bq(){}function gq(){}function wq(){}function RM(){}function pq(){}function mq(){}function vq(){}function TI(){}function yq(){}function kq(){}function Eq(){}function jq(){}function Sq(){}function Aq(){}function Mq(){}function Tq(){}function xq(){}function BM(){}function ty(){}function Cq(){}function xI(){}function CI(){}function OI(){}function NI(){}function DI(){}function f5(){}function Oq(){}function Nq(){}function Dq(){}function II(){}function _I(){}function iy(){}function ry(){}function Iq(){}function rk(){}function LI(){}function zM(){}function FM(){}function HM(){}function PI(){}function $I(){}function RI(){}function _q(){}function Lq(){}function Pq(){}function $q(){}function Rq(){}function f1(){}function cy(){}function BI(){}function zI(){}function FI(){}function HI(){}function JM(){}function Bq(){}function a5(){}function GM(){}function uy(){}function qM(){}function JI(){}function Km(){}function h5(){}function UM(){}function GI(){}function Vm(){}function qI(){}function UI(){}function XI(){}function zq(){}function Fq(){}function Hq(){}function KI(){}function VI(){}function XM(){}function n0(){}function ck(){}function nd(){}function d5(){}function KM(){}function uk(){}function ok(){}function VM(){}function Ym(){}function YI(){}function sk(){}function b5(){}function Jq(){}function a1(){}function YM(){}function Rg(){}function QI(){}function lk(){}function Qm(){}function QM(){}function ZI(){}function ZM(){}function WI(){}function td(){}function g5(){}function w5(){}function fk(){}function oy(){}function id(){}function rd(){}function p2(){}function wb(){}function pb(){}function Bg(){}function e_(){}function WM(){}function eT(){}function n_(){}function Qf(){}function Fo(){}function cu(){}function m2(){}function cd(){}function nT(){}function v2(){}function t_(){}function i_(){}function p5(){}function Zm(){}function m5(){}function y2(){}function tT(){}function k2(){}function zg(){}function E2(){}function Fg(){}function iT(){}function rT(){}function v5(){}function sy(){}function j2(){}function Zf(){}function ly(){}function cT(){}function Gq(){}function qq(){}function fy(){}function jl(){}function uT(){}function ay(){}function hy(){}function oT(){}function y5(){}function k5(){}function Uq(){}function r_(){}function Xq(){}function c_(){}function Wm(){}function sT(){}function ak(){}function u_(){}function E5(){}function lT(){}function hk(){}function dk(){}function o_(){}function s_(){}function e3(){}function n3(){}function l_(){}function fT(){}function j5(){}function dy(){}function bk(){}function by(){}function gk(){}function f_(){}function t3(){}function a_(){}function S2(){}function aT(){}function hT(){}function A2(){}function M2(){}function gy(){}function dT(){}function bT(){}function wy(){}function py(){}function h_(){}function d_(){}function S5(){}function wk(){}function b_(){}function gT(){}function wT(){}function h1(){}function ud(){}function T2(){}function pT(){}function g_(){}function x2(){}function d1(){}function Ys(){}function pk(){}function Hg(){}function dc(){}function mo(){}function Sl(){}function mk(){}function A5(){}function i3(){}function vk(){}function my(){}function M5(){}function Kq(){}function $s(){}function mT(){}function vT(){}function w_(){}function p_(){}function Vq(){}function yT(){}function kT(){}function ET(){}function th(){}function Qs(){}function yk(){}function vy(){}function kk(){}function jT(){}function Jg(){}function Ek(){}function ST(){}function AT(){}function m_(){}function v_(){}function y_(){}function Yq(){}function k_(){}function E_(){}function MT(){}function j_(){}function Qq(){}function S_(){}function A_(){}function M_(){}function TT(){}function T_(){}function x_(){}function C_(){}function O_(){}function N_(){}function Zq(){}function D_(){}function T5(){}function I_(){}function jk(){}function Sk(){}function __(){}function xT(){}function Wq(){}function L_(){}function P_(){}function $_(){}function R_(){}function B_(){}function CT(){}function z_(){}function F_(){}function OT(){}function H_(){}function J_(){}function NT(){}function yy(){}function G_(){}function Ak(){}function DT(){}function q_(){}function U_(){}function eU(){}function nU(){}function X_(){}function ky(){}function IT(){}function Mk(){}function K_(){}function _T(){}function Ey(){}function tU(){}function LT(){}function V_(){}function PT(){}function $T(){}function Y_(){}function Q_(){}function r3(){}function Z_(){}function od(){}function W_(){}function t0(){}function RT(){}function BT(){}function eL(){}function nL(){}function iU(){}function zT(){}function Al(){}function Wf(){}function tL(){}function iL(){}function rL(){}function cL(){}function jy(){}function uL(){}function Tk(){}function oL(){}function rU(){}function xk(){}function FT(){}function sL(){}function lL(){}function Be(){}function HT(){}function JT(){}function GT(){}function fL(){}function qT(){}function Ck(){}function UT(){}function aL(){}function XT(){}function hL(){}function Gg(){}function Sy(){}function cU(){}function dL(){}function i0(){}function KT(){}function bL(){}function Ok(){}function c3(){}function vo(){}function Nk(){}function uU(){}function VT(){}function Ay(){}function C2(){}function My(){}function gL(){}function Ty(){}function mb(){}function xy(){}function YT(){}function wL(){}function QT(){}function ZT(){}function u3(){}function pL(){}function vb(){}function Ml(){}function Cy(){}function WT(){}function yf(){}function oU(){}function mL(){}function vL(){}function js(){}function Nh(){}function qg(){}function yL(){}function kL(){}function EL(){}function sU(){}function Dk(){}function Dh(){}function r0(){}function jL(){}function Tl(){}function Ik(){}function Ug(){}function o3(){}function Xg(){}function ex(){}function nx(){}function c0(){}function SL(){}function x5(){}function Oy(){}function Ny(){}function C5(){}function AL(){}function ML(){}function Dy(){}function TL(){}function _k(){}function xL(){}function lU(){}function fU(){}function zu(){}function Do(){}function Hc(){}function nu(){}function to(){}function b1(){}function O2(){}function O5(){}function tx(){}function Kg(){}function Rs(){}function N2(){}function s3(){}function ix(){}function g1(){}function N5(){}function Iy(){}function Ih(){}function rx(){}function Lk(){}function CL(){}function Pk(){}function $k(){}function D2(){}function Zl(){}function I2(){}function D5(){}function Vg(){}function cx(){}function ux(){}function OL(){}function _y(){}function ox(){}function w1(){}function NL(){}function _h(){}function DL(){}function IL(){}function aU(){}function _2(){}function Rk(){}function sx(){}function I5(){}function _L(){}function LL(){}function PL(){}function $L(){}function Bk(){}function lx(){}function hU(){}function dU(){}function bU(){}function RL(){}function BL(){}function _5(){}function zk(){}function zL(){}function FL(){}function HL(){}function JL(){}function GL(){}function qL(){}function Fk(){}function UL(){}function XL(){}function io(){}function fx(){}function gU(){}function KL(){}function wU(){}function pU(){}function mU(){}function Hk(){}function L5(){}function ax(){}function Jk(){}function hx(){}function L2(){}function yb(){}function Ly(){}function vU(){}function VL(){}function dx(){}function YL(){}function QL(){}function bx(){sE()}function gx(){v0e()}function ZL(){$f()}function WL(){Cde()}function yU(){_O()}function wx(){xC()}function px(){nC()}function kU(){eC()}function EU(){vTe()}function Py(){C4()}function jU(){KPe()}function eP(){F9()}function Jc(){I0()}function mx(){Ohe()}function Gk(){R_e()}function vx(){Che()}function nP(){z_e()}function yx(){B_e()}function $y(){F_e()}function qk(){M$e()}function SU(){H_e()}function tP(){pBe()}function AU(){Ce()}function MU(){VP()}function iP(){gBe()}function rP(){wBe()}function yo(){zLe()}function kx(){Sge()}function ea(){mBe()}function TU(){G_e()}function cP(){T4()}function xU(){OFe()}function Ex(){N1()}function jx(){Y0e()}function Uk(){BO()}function uP(){zBe()}function oP(){Lbe()}function Sx(){vJe()}function Ax(){J_e()}function CU(){HXe()}function sP(){Au()}function OU(){La()}function lP(){Ube()}function NU(){_0()}function DU(){xZ()}function P2(){$Q()}function fP(){eB()}function Mx(){Ht()}function Tx(){kz()}function IU(){LB()}function _U(){cde()}function xx(){Hz()}function Xk(){PY()}function Zs(){SNe()}function LU(){Vbe()}function p1(e){_n(e)}function Cx(e){this.a=e}function Ry(e){this.a=e}function aP(e){this.a=e}function hP(e){this.a=e}function By(e){this.a=e}function m1(e){this.a=e}function Ox(e){this.a=e}function Kk(e){this.a=e}function u0(e){this.a=e}function PU(e){this.a=e}function $U(e){this.a=e}function P5(e){this.a=e}function dP(e){this.a=e}function RU(e){this.c=e}function BU(e){this.a=e}function Nx(e){this.a=e}function zU(e){this.a=e}function FU(e){this.a=e}function HU(e){this.a=e}function Dx(e){this.a=e}function bP(e){this.a=e}function $5(e){this.a=e}function l3(e){this.a=e}function gP(e){this.a=e}function Ix(e){this.a=e}function R5(e){this.a=e}function zy(e){this.a=e}function wP(e){this.a=e}function Vk(e){this.a=e}function _x(e){this.a=e}function Lx(e){this.a=e}function Yk(e){this.a=e}function pP(e){this.a=e}function mP(e){this.a=e}function JU(e){this.a=e}function vP(e){this.a=e}function GU(e){this.a=e}function Px(e){this.a=e}function qU(e){this.a=e}function Fy(e){this.a=e}function Hy(e){this.a=e}function f3(e){this.a=e}function Jy(e){this.a=e}function B5(e){this.b=e}function sd(){this.a=[]}function yP(e,n){e.a=n}function UU(e,n){e.a=n}function XU(e,n){e.b=n}function KU(e,n){e.c=n}function kP(e,n){e.c=n}function VU(e,n){e.d=n}function YU(e,n){e.d=n}function kf(e,n){e.k=n}function EP(e,n){e.j=n}function Iue(e,n){e.c=n}function Qk(e,n){e.c=n}function Zk(e,n){e.a=n}function $x(e,n){e.a=n}function jP(e,n){e.f=n}function QU(e,n){e.a=n}function SP(e,n){e.b=n}function kb(e,n){e.d=n}function o0(e,n){e.i=n}function Yg(e,n){e.o=n}function Wk(e,n){e.r=n}function eE(e,n){e.a=n}function a3(e,n){e.b=n}function ZU(e,n){e.e=n}function WU(e,n){e.f=n}function z5(e,n){e.g=n}function _ue(e,n){e.e=n}function eX(e,n){e.f=n}function Rx(e,n){e.f=n}function nE(e,n){e.a=n}function Bx(e,n){e.b=n}function zx(e,n){e.n=n}function Fx(e,n){e.a=n}function nX(e,n){e.c=n}function Gy(e,n){e.c=n}function tX(e,n){e.c=n}function AP(e,n){e.a=n}function Hx(e,n){e.a=n}function iX(e,n){e.d=n}function Lue(e,n){e.d=n}function Jx(e,n){e.e=n}function a(e,n){e.e=n}function d(e,n){e.g=n}function w(e,n){e.f=n}function k(e,n){e.j=n}function C(e,n){e.a=n}function N(e,n){e.a=n}function V(e,n){e.b=n}function ae(e){e.b=e.a}function Ze(e){e.c=e.d.d}function Ln(e){this.a=e}function et(e){this.a=e}function bt(e){this.a=e}function Hn(e){this.a=e}function Zn(e){this.a=e}function Di(e){this.a=e}function Tr(e){this.a=e}function ro(e){this.a=e}function yn(e){this.a=e}function tn(e){this.a=e}function Dn(e){this.a=e}function rt(e){this.a=e}function Hi(e){this.a=e}function Iu(e){this.a=e}function Ui(e){this.b=e}function Hr(e){this.b=e}function tc(e){this.b=e}function Gc(e){this.d=e}function jt(e){this.a=e}function rX(e){this.a=e}function Pue(e){this.a=e}function Ake(e){this.a=e}function Mke(e){this.a=e}function $ue(e){this.a=e}function Rue(e){this.a=e}function cX(e){this.c=e}function L(e){this.c=e}function Tke(e){this.c=e}function Bue(e){this.a=e}function zue(e){this.a=e}function Fue(e){this.a=e}function Hue(e){this.a=e}function qy(e){this.a=e}function xke(e){this.a=e}function Cke(e){this.a=e}function Uy(e){this.a=e}function Oke(e){this.a=e}function Nke(e){this.a=e}function Dke(e){this.a=e}function Ike(e){this.a=e}function _ke(e){this.a=e}function Lke(e){this.a=e}function Pke(e){this.a=e}function $ke(e){this.a=e}function Rke(e){this.a=e}function Bke(e){this.a=e}function zke(e){this.a=e}function tE(e){this.a=e}function Fke(e){this.a=e}function Hke(e){this.a=e}function MP(e){this.a=e}function Jke(e){this.a=e}function Gke(e){this.a=e}function Jue(e){this.a=e}function qke(e){this.a=e}function Uke(e){this.a=e}function Xke(e){this.a=e}function Gue(e){this.a=e}function que(e){this.a=e}function Uue(e){this.a=e}function iE(e){this.a=e}function Xy(e){this.a=e}function Kke(e){this.a=e}function F5(e){this.a=e}function Xue(e){this.a=e}function Vke(e){this.a=e}function Yke(e){this.a=e}function Qke(e){this.a=e}function Zke(e){this.a=e}function Wke(e){this.a=e}function eEe(e){this.a=e}function nEe(e){this.a=e}function tEe(e){this.a=e}function iEe(e){this.a=e}function rEe(e){this.a=e}function cEe(e){this.a=e}function Kue(e){this.a=e}function uEe(e){this.a=e}function oEe(e){this.a=e}function sEe(e){this.a=e}function lEe(e){this.a=e}function fEe(e){this.a=e}function aEe(e){this.a=e}function hEe(e){this.a=e}function dEe(e){this.a=e}function bEe(e){this.a=e}function gEe(e){this.a=e}function wEe(e){this.a=e}function pEe(e){this.a=e}function mEe(e){this.a=e}function vEe(e){this.a=e}function yEe(e){this.a=e}function kEe(e){this.a=e}function EEe(e){this.a=e}function jEe(e){this.a=e}function SEe(e){this.a=e}function AEe(e){this.a=e}function MEe(e){this.a=e}function TEe(e){this.a=e}function xEe(e){this.a=e}function CEe(e){this.a=e}function OEe(e){this.a=e}function NEe(e){this.a=e}function DEe(e){this.a=e}function IEe(e){this.a=e}function _Ee(e){this.a=e}function LEe(e){this.a=e}function PEe(e){this.a=e}function $Ee(e){this.a=e}function REe(e){this.a=e}function BEe(e){this.a=e}function zEe(e){this.a=e}function FEe(e){this.a=e}function HEe(e){this.b=e}function JEe(e){this.a=e}function GEe(e){this.a=e}function qEe(e){this.a=e}function UEe(e){this.a=e}function XEe(e){this.a=e}function KEe(e){this.a=e}function VEe(e){this.c=e}function YEe(e){this.a=e}function QEe(e){this.a=e}function ZEe(e){this.a=e}function WEe(e){this.a=e}function eje(e){this.a=e}function nje(e){this.a=e}function tje(e){this.a=e}function ije(e){this.a=e}function rje(e){this.a=e}function cje(e){this.a=e}function uje(e){this.a=e}function oje(e){this.a=e}function sje(e){this.a=e}function lje(e){this.a=e}function fje(e){this.a=e}function aje(e){this.a=e}function hje(e){this.a=e}function dje(e){this.a=e}function bje(e){this.a=e}function gje(e){this.a=e}function wje(e){this.a=e}function pje(e){this.a=e}function mje(e){this.a=e}function vje(e){this.a=e}function yje(e){this.a=e}function kje(e){this.a=e}function Eje(e){this.a=e}function v1(e){this.a=e}function h3(e){this.a=e}function jje(e){this.a=e}function Sje(e){this.a=e}function Aje(e){this.a=e}function Mje(e){this.a=e}function Tje(e){this.a=e}function xje(e){this.a=e}function Cje(e){this.a=e}function Oje(e){this.a=e}function Nje(e){this.a=e}function Dje(e){this.a=e}function Ije(e){this.a=e}function _je(e){this.a=e}function Lje(e){this.a=e}function Pje(e){this.a=e}function $je(e){this.a=e}function Rje(e){this.a=e}function Bje(e){this.a=e}function zje(e){this.a=e}function Fje(e){this.a=e}function Hje(e){this.a=e}function Jje(e){this.a=e}function Gje(e){this.a=e}function qje(e){this.a=e}function Uje(e){this.a=e}function Xje(e){this.a=e}function Kje(e){this.a=e}function TP(e){this.a=e}function Vje(e){this.f=e}function Yje(e){this.a=e}function Qje(e){this.a=e}function Zje(e){this.a=e}function Wje(e){this.a=e}function eSe(e){this.a=e}function nSe(e){this.a=e}function tSe(e){this.a=e}function iSe(e){this.a=e}function rSe(e){this.a=e}function cSe(e){this.a=e}function uSe(e){this.a=e}function oSe(e){this.a=e}function sSe(e){this.a=e}function lSe(e){this.a=e}function fSe(e){this.a=e}function aSe(e){this.a=e}function hSe(e){this.a=e}function dSe(e){this.a=e}function bSe(e){this.a=e}function gSe(e){this.a=e}function wSe(e){this.a=e}function pSe(e){this.a=e}function mSe(e){this.a=e}function vSe(e){this.a=e}function ySe(e){this.a=e}function kSe(e){this.a=e}function ESe(e){this.a=e}function uX(e){this.a=e}function Vue(e){this.a=e}function mi(e){this.b=e}function jSe(e){this.a=e}function SSe(e){this.a=e}function ASe(e){this.a=e}function MSe(e){this.a=e}function TSe(e){this.a=e}function xSe(e){this.a=e}function CSe(e){this.a=e}function OSe(e){this.b=e}function NSe(e){this.a=e}function Gx(e){this.a=e}function DSe(e){this.a=e}function ISe(e){this.a=e}function xP(e){this.a=e}function CP(e){this.a=e}function Yue(e){this.c=e}function OP(e){this.e=e}function NP(e){this.e=e}function oX(e){this.a=e}function _Se(e){this.d=e}function LSe(e){this.a=e}function Que(e){this.a=e}function Zue(e){this.a=e}function Qg(e){this.e=e}function q0n(){this.a=0}function xe(){EK(this)}function gt(){Fu(this)}function sX(){AIe(this)}function PSe(){}function Zg(){this.c=H8e}function $Se(e,n){e.b+=n}function U0n(e,n){n.Wb(e)}function X0n(e){return e.a}function K0n(e){return e.a}function V0n(e){return e.a}function Y0n(e){return e.a}function Q0n(e){return e.a}function P(e){return e.e}function Z0n(){return null}function W0n(){return null}function ebn(e){throw P(e)}function H5(e){this.a=Tt(e)}function RSe(){this.a=this}function Eb(){tOe.call(this)}function nbn(e){e.b.Mf(e.e)}function BSe(e){e.b=new AX}function rE(e,n){e.b=n-e.b}function cE(e,n){e.a=n-e.a}function zSe(e,n){n.gd(e.a)}function tbn(e,n){Ar(n,e)}function Jn(e,n){e.push(n)}function FSe(e,n){e.sort(n)}function ibn(e,n,t){e.Wd(t,n)}function qx(e,n){e.e=n,n.b=e}function rbn(){Noe(),MRn()}function HSe(e){j9(),Qne.je(e)}function Wue(){Eb.call(this)}function lX(){Eb.call(this)}function eoe(){tOe.call(this)}function JSe(){Eb.call(this)}function xl(){Eb.call(this)}function GSe(){Eb.call(this)}function Ux(){Eb.call(this)}function ts(){Eb.call(this)}function J5(){Eb.call(this)}function Ot(){Eb.call(this)}function fu(){Eb.call(this)}function qSe(){Eb.call(this)}function DP(){this.Bb|=256}function USe(){this.b=new nCe}function noe(){noe=Y,new gt}function $2(e,n){e.length=n}function IP(e,n){Te(e.a,n)}function cbn(e,n){k0e(e.c,n)}function ubn(e,n){ar(e.b,n)}function Ky(e,n){fi(e.e,n)}function obn(e,n){oz(e.a,n)}function sbn(e,n){fQ(e.a,n)}function G5(e){Az(e.c,e.b)}function lbn(e,n){e.kc().Nb(n)}function toe(e){this.a=vEn(e)}function fr(){this.a=new gt}function XSe(){this.a=new gt}function _P(){this.a=new xe}function fX(){this.a=new xe}function ioe(){this.a=new xe}function jb(){this.a=new RPe}function aX(){this.a=new hTe}function roe(){this.a=new x_e}function coe(){this.a=new KOe}function Wl(){this.a=new vM}function uoe(){this.a=new X6}function KSe(){this.a=new oLe}function VSe(){this.a=new xe}function YSe(){this.a=new xe}function ooe(){this.a=new xe}function QSe(){this.a=new xe}function ZSe(){this.d=new xe}function WSe(){this.a=new fr}function eAe(){this.a=new gt}function nAe(){this.b=new gt}function tAe(){this.b=new xe}function soe(){this.e=new xe}function iAe(){this.a=new Jc}function rAe(){this.d=new xe}function uE(){PSe.call(this)}function hX(){uE.call(this)}function q5(){PSe.call(this)}function loe(){q5.call(this)}function cAe(){Wue.call(this)}function LP(){_P.call(this)}function uAe(){J$.call(this)}function oAe(){ooe.call(this)}function sAe(){xe.call(this)}function lAe(){c_e.call(this)}function fAe(){c_e.call(this)}function aAe(){doe.call(this)}function hAe(){doe.call(this)}function dAe(){doe.call(this)}function bAe(){boe.call(this)}function oE(){Ok.call(this)}function foe(){Ok.call(this)}function Ss(){ji.call(this)}function gAe(){CAe.call(this)}function wAe(){CAe.call(this)}function pAe(){gt.call(this)}function mAe(){gt.call(this)}function vAe(){gt.call(this)}function dX(){aBe.call(this)}function yAe(){fr.call(this)}function kAe(){DP.call(this)}function bX(){Yse.call(this)}function aoe(){gt.call(this)}function gX(){Yse.call(this)}function wX(){gt.call(this)}function EAe(){gt.call(this)}function hoe(){u3.call(this)}function jAe(){hoe.call(this)}function SAe(){u3.call(this)}function AAe(){dx.call(this)}function doe(){this.a=new fr}function MAe(){this.a=new gt}function TAe(){this.a=new xe}function xAe(){this.j=new xe}function boe(){this.a=new gt}function U5(){this.a=new ji}function CAe(){this.a=new YT}function goe(){this.a=new R_}function OAe(){this.a=new TMe}function sE(){sE=Y,Jne=new J}function pX(){pX=Y,Gne=new DAe}function mX(){mX=Y,qne=new NAe}function NAe(){l3.call(this,"")}function DAe(){l3.call(this,"")}function IAe(e){FRe.call(this,e)}function _Ae(e){FRe.call(this,e)}function woe(e){m1.call(this,e)}function poe(e){zMe.call(this,e)}function fbn(e){zMe.call(this,e)}function abn(e){poe.call(this,e)}function hbn(e){poe.call(this,e)}function dbn(e){poe.call(this,e)}function LAe(e){eY.call(this,e)}function PAe(e){eY.call(this,e)}function $Ae(e){BCe.call(this,e)}function RAe(e){Roe.call(this,e)}function lE(e){UP.call(this,e)}function moe(e){UP.call(this,e)}function BAe(e){UP.call(this,e)}function au(e){IDe.call(this,e)}function zAe(e){au.call(this,e)}function X5(){Jy.call(this,{})}function vX(e){u9(),this.a=e}function FAe(e){e.b=null,e.c=0}function bbn(e,n){e.e=n,FUe(e,n)}function gbn(e,n){e.a=n,Txn(e)}function yX(e,n,t){e.a[n.g]=t}function wbn(e,n,t){KAn(t,e,n)}function pbn(e,n){K2n(n.i,e.n)}function HAe(e,n){skn(e).Ad(n)}function mbn(e,n){return e*e/n}function JAe(e,n){return e.g-n.g}function vbn(e,n){e.a.ec().Kc(n)}function ybn(e){return new f3(e)}function kbn(e){return new up(e)}function GAe(){GAe=Y,fme=new U}function voe(){voe=Y,ame=new an}function PP(){PP=Y,PS=new Mn}function $P(){$P=Y,Xne=new RCe}function qAe(){qAe=Y,Jen=new Ft}function RP(e){Uhe(),this.a=e}function kX(e){rV(),this.f=e}function s0(e){rV(),this.f=e}function UAe(e){jNe(),this.a=e}function BP(e){au.call(this,e)}function ko(e){au.call(this,e)}function XAe(e){au.call(this,e)}function EX(e){IDe.call(this,e)}function Vy(e){au.call(this,e)}function Gn(e){au.call(this,e)}function qc(e){au.call(this,e)}function KAe(e){au.call(this,e)}function K5(e){au.call(this,e)}function ld(e){au.call(this,e)}function Eu(e){_n(e),this.a=e}function fE(e){xfe(e,e.length)}function yoe(e){return qb(e),e}function R2(e){return!!e&&e.b}function Ebn(e){return!!e&&e.k}function jbn(e){return!!e&&e.j}function aE(e){return e.b==e.c}function $e(e){return _n(e),e}function W(e){return _n(e),e}function Xx(e){return _n(e),e}function koe(e){return _n(e),e}function Sbn(e){return _n(e),e}function ih(e){au.call(this,e)}function fd(e){au.call(this,e)}function V5(e){au.call(this,e)}function jX(e){au.call(this,e)}function Lt(e){au.call(this,e)}function SX(e){rle.call(this,e,0)}function AX(){gae.call(this,12,3)}function MX(){this.a=Dt(Tt(xo))}function VAe(){throw P(new Ot)}function Eoe(){throw P(new Ot)}function YAe(){throw P(new Ot)}function Abn(){throw P(new Ot)}function Mbn(){throw P(new Ot)}function Tbn(){throw P(new Ot)}function zP(){zP=Y,j9()}function ad(){Di.call(this,"")}function hE(){Di.call(this,"")}function l0(){Di.call(this,"")}function Y5(){Di.call(this,"")}function joe(e){ko.call(this,e)}function Soe(e){ko.call(this,e)}function rh(e){Gn.call(this,e)}function Yy(e){Hr.call(this,e)}function QAe(e){Yy.call(this,e)}function TX(e){$$.call(this,e)}function xbn(e,n,t){e.c.Cf(n,t)}function Cbn(e,n,t){n.Ad(e.a[t])}function Obn(e,n,t){n.Ne(e.a[t])}function Nbn(e,n){return e.a-n.a}function Dbn(e,n){return e.a-n.a}function Ibn(e,n){return e.a-n.a}function FP(e,n){return bY(e,n)}function B(e,n){return __e(e,n)}function _bn(e,n){return n in e.a}function ZAe(e){return e.a?e.b:0}function Lbn(e){return e.a?e.b:0}function WAe(e,n){return e.f=n,e}function Pbn(e,n){return e.b=n,e}function eMe(e,n){return e.c=n,e}function $bn(e,n){return e.g=n,e}function Aoe(e,n){return e.a=n,e}function Moe(e,n){return e.f=n,e}function Rbn(e,n){return e.f=n,e}function Toe(e,n){return e.e=n,e}function Bbn(e,n){return e.k=n,e}function xoe(e,n){return e.a=n,e}function zbn(e,n){return e.e=n,e}function Fbn(e,n){e.b=new gc(n)}function nMe(e,n){e._d(n),n.$d(e)}function Hbn(e,n){el(),n.n.a+=e}function Jbn(e,n){I0(),bu(n,e)}function Coe(e){$Ie.call(this,e)}function tMe(e){$Ie.call(this,e)}function iMe(){Pse.call(this,"")}function rMe(){this.b=0,this.a=0}function cMe(){cMe=Y,tnn=kMn()}function B2(e,n){return e.b=n,e}function HP(e,n){return e.a=n,e}function z2(e,n){return e.c=n,e}function F2(e,n){return e.d=n,e}function H2(e,n){return e.e=n,e}function Ooe(e,n){return e.f=n,e}function dE(e,n){return e.a=n,e}function Qy(e,n){return e.b=n,e}function Zy(e,n){return e.c=n,e}function Ge(e,n){return e.c=n,e}function sn(e,n){return e.b=n,e}function qe(e,n){return e.d=n,e}function Ue(e,n){return e.e=n,e}function Gbn(e,n){return e.f=n,e}function Xe(e,n){return e.g=n,e}function Ke(e,n){return e.a=n,e}function Ve(e,n){return e.i=n,e}function Ye(e,n){return e.j=n,e}function qbn(e,n){return n.pg(e)}function Ubn(e,n){return e.b-n.b}function Xbn(e,n){return e.g-n.g}function Kbn(e,n){return e.s-n.s}function Vbn(e,n){return e?0:n-1}function uMe(e,n){return e?0:n-1}function Ybn(e,n){return e?n-1:0}function oMe(e,n){return e.k=n,e}function Qbn(e,n){return e.j=n,e}function Kr(){this.a=0,this.b=0}function JP(e){FK.call(this,e)}function f0(e){pw.call(this,e)}function sMe(e){NV.call(this,e)}function lMe(e){NV.call(this,e)}function fMe(){fMe=Y,Lr=HMn()}function a0(){a0=Y,aan=DAn()}function Noe(){Noe=Y,jg=dj()}function Wy(){Wy=Y,F8e=IAn()}function aMe(){aMe=Y,Van=_An()}function Doe(){Doe=Y,Ru=Sxn()}function na(e){return e.e&&e.e()}function hMe(e,n){return e.c._b(n)}function dMe(e,n){return aFe(e.b,n)}function bMe(e,n){return jgn(e.a,n)}function gMe(e,n){e.b=0,wp(e,n)}function Zbn(e,n){e.c=n,e.b=!0}function d3(e,n){return e.a+=n,e}function xX(e,n){return e.a+=n,e}function hd(e,n){return e.a+=n,e}function Wg(e,n){return e.a+=n,e}function Sb(e){return E1(e),e.o}function Ioe(e){mVe(),BRn(this,e)}function wMe(){throw P(new Ot)}function pMe(){throw P(new Ot)}function mMe(){throw P(new Ot)}function vMe(){throw P(new Ot)}function yMe(){throw P(new Ot)}function kMe(){throw P(new Ot)}function GP(e){this.a=new Z5(e)}function dd(e){this.a=new lV(e)}function b3(e,n){for(;e.Pe(n););}function _oe(e,n){for(;e.zd(n););}function Wbn(e,n,t){tvn(e.a,n,t)}function Loe(e,n,t){e.splice(n,t)}function egn(e,n){return LLn(n,e)}function Poe(e,n){return e.d[n.p]}function Kx(e){return e.b!=e.d.c}function EMe(e){return e.l|e.m<<22}function CX(e){return e?e.d:null}function ngn(e){return e?e.g:null}function tgn(e){return e?e.i:null}function jMe(e,n){return tDn(e,n)}function e9(e){return m0(e),e.a}function SMe(e){e.c?iXe(e):rXe(e)}function AMe(){this.b=new Uj(K4e)}function MMe(){this.b=new Uj(Ure)}function TMe(){this.b=new Uj(Ure)}function xMe(){this.a=new Uj(C6e)}function CMe(){this.a=new Uj(W6e)}function qP(e){this.a=0,this.b=e}function OMe(){throw P(new Ot)}function NMe(){throw P(new Ot)}function DMe(){throw P(new Ot)}function IMe(){throw P(new Ot)}function _Me(){throw P(new Ot)}function LMe(){throw P(new Ot)}function PMe(){throw P(new Ot)}function $Me(){throw P(new Ot)}function RMe(){throw P(new Ot)}function BMe(){throw P(new Ot)}function ign(){throw P(new fu)}function rgn(){throw P(new fu)}function Vx(e){this.a=new oTe(e)}function n9(e,n){this.e=e,this.d=n}function $oe(e,n){this.b=e,this.c=n}function zMe(e){Xse(e.dc()),this.c=e}function Yx(e,n){T3.call(this,e,n)}function t9(e,n){Yx.call(this,e,n)}function FMe(e,n){this.a=e,this.b=n}function HMe(e,n){this.a=e,this.b=n}function JMe(e,n){this.a=e,this.b=n}function GMe(e,n){this.a=e,this.b=n}function qMe(e,n){this.a=e,this.b=n}function UMe(e,n){this.a=e,this.b=n}function XMe(e,n){this.a=e,this.b=n}function KMe(e,n){this.b=e,this.a=n}function VMe(e,n){this.b=e,this.a=n}function ew(e,n){this.g=e,this.i=n}function YMe(e,n){this.a=e,this.b=n}function QMe(e,n){this.b=e,this.a=n}function ZMe(e,n){this.a=e,this.b=n}function WMe(e,n){this.b=e,this.a=n}function UP(e){this.b=u(Tt(e),50)}function XP(e){this.b=u(Tt(e),92)}function Mt(e,n){this.f=e,this.g=n}function OX(e,n){this.a=e,this.b=n}function eTe(e,n){this.a=e,this.f=n}function nTe(e){this.a=u(Tt(e),16)}function Roe(e){this.a=u(Tt(e),16)}function tTe(e,n){this.b=e,this.c=n}function iTe(e){this.a=u(Tt(e),92)}function cgn(e,n){this.a=e,this.b=n}function rTe(e,n){this.a=e,this.b=n}function cTe(e,n){return oo(e.b,n)}function uTe(e,n){return e>n&&n0}function $X(e,n){return fo(e,n)<0}function MTe(e,n){return tV(e.a,n)}function Agn(e,n){C_e.call(this,e,n)}function Koe(e){SV(),zTn.call(this,e)}function Voe(e){SV(),Koe.call(this,e)}function Yoe(e){WK(),BCe.call(this,e)}function Qoe(e,n){kDe(e,e.length,n)}function tC(e,n){YDe(e,e.length,n)}function EE(e,n){return e.a.get(n)}function TTe(e,n){return oo(e.e,n)}function Zoe(e){return _n(e),!1}function xTe(){return cMe(),new tnn}function iC(e){return ft(e.a),e.b}function CTe(e,n){this.b=e,this.a=n}function t$(e,n){this.d=e,this.e=n}function OTe(e,n){this.a=e,this.b=n}function NTe(e,n){this.a=e,this.b=n}function DTe(e,n){this.a=e,this.b=n}function ITe(e,n){this.a=e,this.b=n}function _Te(e,n){this.b=e,this.a=n}function W5(e,n){this.a=e,this.b=n}function i$(e,n){Mt.call(this,e,n)}function RX(e,n){Mt.call(this,e,n)}function BX(e,n){Mt.call(this,e,n)}function zX(e,n){Mt.call(this,e,n)}function FX(e,n){Mt.call(this,e,n)}function r$(e,n){Mt.call(this,e,n)}function c$(e){wn.call(this,e,21)}function LTe(e,n){this.b=e,this.a=n}function Woe(e,n){this.b=e,this.a=n}function ese(e,n){this.b=e,this.a=n}function nse(e,n){Mt.call(this,e,n)}function HX(e,n){Mt.call(this,e,n)}function rC(e,n){Mt.call(this,e,n)}function tse(e,n){this.b=e,this.a=n}function c9(e,n){this.c=e,this.d=n}function u$(e,n){Mt.call(this,e,n)}function o$(e,n){Mt.call(this,e,n)}function PTe(e,n){this.e=e,this.d=n}function e4(e,n){Mt.call(this,e,n)}function $Te(e,n){this.a=e,this.b=n}function ise(e,n){Mt.call(this,e,n)}function dr(e,n){Mt.call(this,e,n)}function s$(e,n){Mt.call(this,e,n)}function jE(e,n,t){e.splice(n,0,t)}function Mgn(e,n,t){e.Mb(t)&&n.Ad(t)}function Tgn(e,n,t){n.Ne(e.a.We(t))}function xgn(e,n,t){n.Bd(e.a.Xe(t))}function Cgn(e,n,t){n.Ad(e.a.Kb(t))}function Ogn(e,n){return rs(e.c,n)}function Ngn(e,n){return rs(e.e,n)}function RTe(e,n){this.a=e,this.b=n}function BTe(e,n){this.a=e,this.b=n}function zTe(e,n){this.a=e,this.b=n}function FTe(e,n){this.a=e,this.b=n}function HTe(e,n){this.a=e,this.b=n}function JTe(e,n){this.a=e,this.b=n}function GTe(e,n){this.a=e,this.b=n}function qTe(e,n){this.a=e,this.b=n}function UTe(e,n){this.b=e,this.a=n}function XTe(e,n){this.b=e,this.a=n}function KTe(e,n){this.b=e,this.a=n}function VTe(e,n){this.b=n,this.c=e}function l$(e,n){Mt.call(this,e,n)}function cC(e,n){Mt.call(this,e,n)}function rse(e,n){Mt.call(this,e,n)}function SE(e,n){Mt.call(this,e,n)}function f$(e,n){Mt.call(this,e,n)}function JX(e,n){Mt.call(this,e,n)}function GX(e,n){Mt.call(this,e,n)}function AE(e,n){Mt.call(this,e,n)}function ME(e,n){Mt.call(this,e,n)}function cse(e,n){Mt.call(this,e,n)}function g3(e,n){Mt.call(this,e,n)}function qX(e,n){Mt.call(this,e,n)}function TE(e,n){Mt.call(this,e,n)}function use(e,n){Mt.call(this,e,n)}function q2(e,n){Mt.call(this,e,n)}function UX(e,n){Mt.call(this,e,n)}function XX(e,n){Mt.call(this,e,n)}function KX(e,n){Mt.call(this,e,n)}function ose(e,n){Mt.call(this,e,n)}function uC(e,n){Mt.call(this,e,n)}function sse(e,n){Mt.call(this,e,n)}function w3(e,n){Mt.call(this,e,n)}function VX(e,n){Mt.call(this,e,n)}function a$(e,n){Mt.call(this,e,n)}function oC(e,n){Mt.call(this,e,n)}function U2(e,n){Mt.call(this,e,n)}function h$(e,n){Mt.call(this,e,n)}function lse(e,n){Mt.call(this,e,n)}function YX(e,n){Mt.call(this,e,n)}function QX(e,n){Mt.call(this,e,n)}function ZX(e,n){Mt.call(this,e,n)}function WX(e,n){Mt.call(this,e,n)}function eK(e,n){Mt.call(this,e,n)}function nK(e,n){Mt.call(this,e,n)}function d$(e,n){Mt.call(this,e,n)}function YTe(e,n){this.b=e,this.a=n}function fse(e,n){Mt.call(this,e,n)}function QTe(e,n){this.a=e,this.b=n}function ZTe(e,n){this.a=e,this.b=n}function WTe(e,n){this.a=e,this.b=n}function ase(e,n){Mt.call(this,e,n)}function hse(e,n){Mt.call(this,e,n)}function exe(e,n){this.a=e,this.b=n}function Dgn(e,n){return h9(),n!=e}function tK(e){return DCn(e,e.c),e}function Ign(e){E.clearTimeout(e)}function dse(e,n){Mt.call(this,e,n)}function bse(e,n){Mt.call(this,e,n)}function nxe(e,n){this.a=e,this.b=n}function txe(e,n){this.a=e,this.b=n}function ixe(e,n){this.b=e,this.d=n}function rxe(e,n){this.a=e,this.b=n}function cxe(e,n){this.b=e,this.a=n}function b$(e,n){Mt.call(this,e,n)}function nw(e,n){Mt.call(this,e,n)}function iK(e,n){Mt.call(this,e,n)}function g$(e,n){Mt.call(this,e,n)}function gse(e,n){Mt.call(this,e,n)}function uxe(e,n){this.b=e,this.a=n}function oxe(e,n){this.b=e,this.a=n}function sxe(e,n){this.b=e,this.a=n}function lxe(e,n){this.b=e,this.a=n}function wse(e,n){Mt.call(this,e,n)}function sC(e,n){Mt.call(this,e,n)}function pse(e,n){Mt.call(this,e,n)}function rK(e,n){Mt.call(this,e,n)}function w$(e,n){Mt.call(this,e,n)}function cK(e,n){Mt.call(this,e,n)}function uK(e,n){Mt.call(this,e,n)}function p$(e,n){Mt.call(this,e,n)}function oK(e,n){Mt.call(this,e,n)}function mse(e,n){Mt.call(this,e,n)}function sK(e,n){Mt.call(this,e,n)}function lK(e,n){Mt.call(this,e,n)}function lC(e,n){Mt.call(this,e,n)}function fK(e,n){Mt.call(this,e,n)}function vse(e,n){Mt.call(this,e,n)}function fC(e,n){Mt.call(this,e,n)}function yse(e,n){Mt.call(this,e,n)}function kse(e,n){this.a=e,this.b=n}function fxe(e,n){this.a=e,this.b=n}function axe(e,n){this.a=e,this.b=n}function hxe(){U$(),this.a=new Mle}function dxe(){Lz(),this.a=new fr}function bxe(){zV(),this.b=new fr}function gxe(){dae(),pfe.call(this)}function wxe(){hae(),r_e.call(this)}function pxe(){hae(),r_e.call(this)}function aC(e,n){Mt.call(this,e,n)}function n4(e,n){Mt.call(this,e,n)}function xE(e,n){Mt.call(this,e,n)}function CE(e,n){Mt.call(this,e,n)}function hC(e,n){Mt.call(this,e,n)}function m$(e,n){Mt.call(this,e,n)}function aK(e,n){Mt.call(this,e,n)}function v$(e,n){Mt.call(this,e,n)}function OE(e,n){Mt.call(this,e,n)}function hK(e,n){Mt.call(this,e,n)}function y$(e,n){Mt.call(this,e,n)}function p3(e,n){Mt.call(this,e,n)}function dC(e,n){Mt.call(this,e,n)}function NE(e,n){Mt.call(this,e,n)}function DE(e,n){Mt.call(this,e,n)}function dK(e,n){Mt.call(this,e,n)}function bC(e,n){Mt.call(this,e,n)}function k$(e,n){Mt.call(this,e,n)}function m3(e,n){Mt.call(this,e,n)}function bK(e,n){Mt.call(this,e,n)}function gK(e,n){Mt.call(this,e,n)}function E$(e,n){Mt.call(this,e,n)}function ke(e,n){this.a=e,this.b=n}function mxe(e,n){this.a=e,this.b=n}function vxe(e,n){this.a=e,this.b=n}function yxe(e,n){this.a=e,this.b=n}function kxe(e,n){this.a=e,this.b=n}function Exe(e,n){this.a=e,this.b=n}function jxe(e,n){this.a=e,this.b=n}function yc(e,n){this.a=e,this.b=n}function Sxe(e,n){this.a=e,this.b=n}function Axe(e,n){this.a=e,this.b=n}function Mxe(e,n){this.a=e,this.b=n}function Txe(e,n){this.a=e,this.b=n}function xxe(e,n){this.a=e,this.b=n}function Cxe(e,n){this.a=e,this.b=n}function Oxe(e,n){this.b=e,this.a=n}function Nxe(e,n){this.b=e,this.a=n}function Dxe(e,n){this.b=e,this.a=n}function Ixe(e,n){this.b=e,this.a=n}function _xe(e,n){this.a=e,this.b=n}function Lxe(e,n){this.a=e,this.b=n}function Pxe(e,n){this.a=e,this.b=n}function $xe(e,n){this.a=e,this.b=n}function Rxe(e,n){this.f=e,this.c=n}function Ese(e,n){this.i=e,this.g=n}function j$(e,n){Mt.call(this,e,n)}function t4(e,n){Mt.call(this,e,n)}function S$(e,n){this.a=e,this.b=n}function Bxe(e,n){this.a=e,this.b=n}function jse(e,n){this.d=e,this.e=n}function zxe(e,n){this.a=e,this.b=n}function Fxe(e,n){this.a=e,this.b=n}function Hxe(e,n){this.d=e,this.b=n}function Jxe(e,n){this.e=e,this.a=n}function Sse(e,n){e.i=null,kB(e,n)}function _gn(e,n){e&&Qt(YD,e,n)}function Gxe(e,n){return vQ(e.a,n)}function Ase(e,n){return rs(e.g,n)}function Lgn(e,n){return rs(n.b,e)}function Pgn(e,n){return-e.b.$e(n)}function A$(e){return xO(e.c,e.b)}function $gn(e,n){Q9n(new ct(e),n)}function Rgn(e,n,t){BJe(n,dZ(e,t))}function Bgn(e,n,t){BJe(n,dZ(e,t))}function qxe(e,n){z9n(e.a,u(n,12))}function Uxe(e,n){this.a=e,this.b=n}function gC(e,n){this.b=e,this.c=n}function b0(e,n){return e.Pd().Xb(n)}function M$(e,n){return f7n(e.Jc(),n)}function hu(e){return e?e.kd():null}function ue(e){return e??null}function X2(e){return typeof e===X4}function K2(e){return typeof e===Mge}function Pr(e){return typeof e===cW}function IE(e,n){return fo(e,n)==0}function T$(e,n){return fo(e,n)>=0}function _E(e,n){return fo(e,n)!=0}function Mse(e,n){return e.a+=""+n,e}function zgn(e){return""+(_n(e),e)}function Xxe(e){return Ns(e),e.d.gc()}function Tse(e){return pn(e,0),null}function x$(e){return qE(e==null),e}function LE(e,n){return e.a+=""+n,e}function $c(e,n){return e.a+=""+n,e}function PE(e,n){return e.a+=""+n,e}function co(e,n){return e.a+=""+n,e}function Gt(e,n){return e.a+=""+n,e}function Kxe(e,n){e.q.setTime(Bb(n))}function Vxe(e,n){Sfe.call(this,e,n)}function Yxe(e,n){Sfe.call(this,e,n)}function C$(e,n){Sfe.call(this,e,n)}function bc(e,n){Xi(e,n,e.c.b,e.c)}function v3(e,n){Xi(e,n,e.a,e.a.a)}function Fgn(e,n){return e.j[n.p]==2}function Qxe(e,n){return e.a=n.g+1,e}function ta(e){return e.a=0,e.b=0,e}function Zxe(e){Fu(this),gj(this,e)}function Wxe(){this.b=0,this.a=!1}function eCe(){this.b=0,this.a=!1}function nCe(){this.b=new Z5(vp(12))}function tCe(){tCe=Y,Xnn=Ct(TQ())}function iCe(){iCe=Y,nin=Ct(DUe())}function rCe(){rCe=Y,Xon=Ct(ZBe())}function xse(){xse=Y,noe(),hme=new gt}function Hgn(e){return Tt(e),new $E(e)}function cCe(e,n){return ue(e)===ue(n)}function O$(e){return e<10?"0"+e:""+e}function uCe(e){return Io(e.l,e.m,e.h)}function uu(e){return typeof e===Mge}function wK(e,n){return rf(e.a,0,n)}function i4(e){return sc((_n(e),e))}function Jgn(e){return sc((_n(e),e))}function Ggn(e,n){return vi(e.a,n.a)}function Cse(e,n){return uo(e.a,n.a)}function qgn(e,n){return KDe(e.a,n.a)}function ch(e,n){return e.indexOf(n)}function Ose(e,n){x9(e,0,e.length,n)}function Wt(e,n){WP(),Qt(vG,e,n)}function cn(e,n){Li.call(this,e,n)}function pK(e,n){ep.call(this,e,n)}function y3(e,n){Ese.call(this,e,n)}function oCe(e,n){yC.call(this,e,n)}function mK(e,n){P9.call(this,e,n)}function Lh(){$ue.call(this,new E0)}function sCe(){sR.call(this,0,0,0,0)}function Nse(e){return gu(e.b.b,e,0)}function lCe(e,n){return uo(e.g,n.g)}function Ugn(e){return e==Xw||e==Kp}function Xgn(e){return e==Xw||e==Xp}function Kgn(e,n){return uo(e.g,n.g)}function Vgn(e,n){return el(),n.a+=e}function Ygn(e,n){return el(),n.a+=e}function Qgn(e,n){return el(),n.c+=e}function Zgn(e,n){return Te(e.c,n),e}function fCe(e,n){return Te(e.a,n),n}function Dse(e,n){return ul(e.a,n),e}function aCe(e){this.a=xTe(),this.b=e}function hCe(e){this.a=xTe(),this.b=e}function gc(e){this.a=e.a,this.b=e.b}function $E(e){this.a=e,bx.call(this)}function dCe(e){this.a=e,bx.call(this)}function Bs(e){return e.sh()&&e.th()}function k3(e){return e!=Ka&&e!=ub}function y1(e){return e==Zc||e==ru}function E3(e){return e==Ul||e==Ua}function bCe(e){return e==Ov||e==Cv}function N$(e){return ul(new ur,e)}function gCe(e){return AV(u(e,125))}function Wgn(e,n){return vi(n.f,e.f)}function wCe(e,n){return new P9(n,e)}function ewn(e,n){return new P9(n,e)}function Cl(e,n,t){Cs(e,n),Os(e,t)}function vK(e,n,t){hB(e,n),dB(e,t)}function tw(e,n,t){vw(e,n),mw(e,t)}function wC(e,n,t){P3(e,n),$3(e,t)}function pC(e,n,t){R3(e,n),B3(e,t)}function yK(e,n){J9(e,n),D9(e,e.D)}function kK(e){Rxe.call(this,e,!0)}function r4(){xf.call(this,0,0,0,0)}function pCe(){i$.call(this,"Head",1)}function mCe(){i$.call(this,"Tail",3)}function vCe(e,n,t){gle.call(this,e,n,t)}function iw(e){sR.call(this,e,e,e,e)}function g0(e){bh(),w7n.call(this,e)}function yCe(e){Ao(e.Qf(),new Hke(e))}function j3(e){return e!=null?Oi(e):0}function nwn(e,n){return gp(n,Ma(e))}function twn(e,n){return gp(n,Ma(e))}function iwn(e,n){return e[e.length]=n}function rwn(e,n){return e[e.length]=n}function cwn(e,n){return mB(yV(e.f),n)}function uwn(e,n){return mB(yV(e.n),n)}function own(e,n){return mB(yV(e.p),n)}function Ise(e){return g3n(e.b.Jc(),e.a)}function swn(e){return e==null?0:Oi(e)}function EK(e){e.c=oe(Mr,xn,1,0,5,1)}function kCe(e,n,t){tr(e.c[n.g],n.g,t)}function lwn(e,n,t){u(e.c,72).Ei(n,t)}function fwn(e,n,t){Cl(t,t.i+e,t.j+n)}function Vr(e,n){Li.call(this,e.b,n)}function awn(e,n){kt(Xu(e.a),Z_e(n))}function hwn(e,n){kt(xs(e.a),W_e(n))}function dwn(e,n){Fa||(e.b=n)}function jK(e,n,t){return tr(e,n,t),t}function Nt(){Nt=Y,new ECe,new xe}function ECe(){new gt,new gt,new gt}function bwn(){throw P(new ld(Ten))}function gwn(){throw P(new ld(Ten))}function wwn(){throw P(new ld(xen))}function pwn(){throw P(new ld(xen))}function jCe(){jCe=Y,cre=new Oj(kce)}function ja(){ja=Y,E.Math.log(2)}function Ol(){Ol=Y,o1=(ETe(),pan)}function RE(e){si(),Qg.call(this,e)}function SCe(e){this.a=e,Vle.call(this,e)}function SK(e){this.a=e,XP.call(this,e)}function AK(e){this.a=e,XP.call(this,e)}function xr(e,n){nV(e.c,e.c.length,n)}function du(e){return e.an?1:0}function Lse(e,n){return fo(e,n)>0?e:n}function Io(e,n,t){return{l:e,m:n,h:t}}function mwn(e,n){e.a!=null&&qxe(n,e.a)}function vwn(e){lc(e,null),Jr(e,null)}function ywn(e,n,t){return Qt(e.g,t,n)}function kwn(e,n){Tt(n),C3(e).Ic(new ze)}function MCe(){zde(),this.a=new Uj(s3e)}function D$(e){this.b=e,this.a=new xe}function TCe(e){this.b=new SM,this.a=e}function Pse(e){Tle.call(this),this.a=e}function xCe(e){iae.call(this),this.b=e}function CCe(){i$.call(this,"Range",2)}function I$(e){e.j=oe(Ame,je,324,0,0,1)}function OCe(e){e.a=new Kt,e.c=new Kt}function NCe(e){e.a=new gt,e.e=new gt}function $se(e){return new ke(e.c,e.d)}function Ewn(e){return new ke(e.c,e.d)}function wc(e){return new ke(e.a,e.b)}function jwn(e,n){return Qt(e.a,n.a,n)}function Swn(e,n,t){return Qt(e.k,t,n)}function S3(e,n,t){return ude(n,t,e.c)}function Rse(e,n){return ie(Bn(e.i,n))}function Bse(e,n){return ie(Bn(e.j,n))}function DCe(e,n){return r$n(e.a,n,null)}function BE(e,n){return bPn(e.c,e.b,n)}function q(e,n){return e!=null&&NQ(e,n)}function ICe(e,n){vt(e),e.Fc(u(n,16))}function Awn(e,n,t){e.c._c(n,u(t,136))}function Mwn(e,n,t){e.c.Si(n,u(t,136))}function Twn(e,n,t){return t$n(e,n,t),t}function xwn(e,n){return nl(),n.n.b+=e}function MK(e,n){return q7n(e.Jc(),n)!=-1}function Cwn(e,n){return new oOe(e.Jc(),n)}function _$(e){return e.Ob()?e.Pb():null}function _Ce(e){return ah(e,0,e.length)}function LCe(e){HV(e,null),JV(e,null)}function PCe(){yC.call(this,null,null)}function $Ce(){z$.call(this,null,null)}function RCe(){Mt.call(this,"INSTANCE",0)}function A3(){this.a=oe(Mr,xn,1,8,5,1)}function zse(e){this.a=e,gt.call(this)}function BCe(e){this.a=(vn(),new Yy(e))}function Own(e){this.b=(vn(),new cX(e))}function u9(){u9=Y,Lme=new vX(null)}function Fse(){Fse=Y,Fse(),cnn=new Jt}function Te(e,n){return Jn(e.c,n),!0}function zCe(e,n){e.c&&(sfe(n),w_e(n))}function Nwn(e,n){e.q.setHours(n),Qj(e,n)}function Hse(e,n){return e.a.Ac(n)!=null}function TK(e,n){return e.a.Ac(n)!=null}function Sa(e,n){return e.a[n.c.p][n.p]}function Dwn(e,n){return e.e[n.c.p][n.p]}function Iwn(e,n){return e.c[n.c.p][n.p]}function xK(e,n,t){return e.a[n.g][t.g]}function _wn(e,n){return e.j[n.p]=zOn(n)}function c4(e,n){return e.a*n.a+e.b*n.b}function Lwn(e,n){return e.a=e}function zwn(e,n,t){return t?n!=0:n!=e-1}function FCe(e,n,t){e.a=n^1502,e.b=t^PW}function Fwn(e,n,t){return e.a=n,e.b=t,e}function k1(e,n){return e.a*=n,e.b*=n,e}function zE(e,n,t){return tr(e.g,n,t),t}function Hwn(e,n,t,i){tr(e.a[n.g],t.g,i)}function mr(e,n,t){DC.call(this,e,n,t)}function L$(e,n,t){mr.call(this,e,n,t)}function is(e,n,t){mr.call(this,e,n,t)}function HCe(e,n,t){L$.call(this,e,n,t)}function Jse(e,n,t){DC.call(this,e,n,t)}function M3(e,n,t){DC.call(this,e,n,t)}function JCe(e,n,t){Q$.call(this,e,n,t)}function Gse(e,n,t){Q$.call(this,e,n,t)}function GCe(e,n,t){Gse.call(this,e,n,t)}function qCe(e,n,t){Jse.call(this,e,n,t)}function w0(e){this.c=e,this.a=this.c.a}function ct(e){this.i=e,this.f=this.i.j}function T3(e,n){this.a=e,XP.call(this,n)}function UCe(e,n){this.a=e,SX.call(this,n)}function XCe(e,n){this.a=e,SX.call(this,n)}function KCe(e,n){this.a=e,SX.call(this,n)}function qse(e){this.a=e,RU.call(this,e.d)}function VCe(e){e.b.Qb(),--e.d.f.d,fR(e.d)}function YCe(e){e.a=u(Un(e.b.a,4),129)}function QCe(e){e.a=u(Un(e.b.a,4),129)}function Jwn(e){BC(e,eWe),Oz(e,W$n(e))}function Use(e,n){return yEn(e,new l0,n).a}function Gwn(e){return Kx(e.a)?Q_e(e):null}function ZCe(e){l3.call(this,u(Tt(e),35))}function WCe(e){l3.call(this,u(Tt(e),35))}function Xse(e){if(!e)throw P(new Ux)}function Kse(e){if(!e)throw P(new ts)}function Vn(e,n){return Tt(n),new uOe(e,n)}function eOe(e,n){return new JGe(e.a,e.b,n)}function qwn(e){return e.l+e.m*Q4+e.h*eg}function Uwn(e){return e==null?null:e.name}function Vse(e,n,t){return e.indexOf(n,t)}function P$(e,n){return e.lastIndexOf(n)}function FE(e){return e==null?Ko:su(e)}function Pn(){Pn=Y,U0=!1,Q8=!0}function nOe(){nOe=Y,_X(),Uan=new LU}function Yse(){this.Bb|=256,this.Bb|=512}function tOe(){I$(this),AR(this),this.he()}function $$(e){Hr.call(this,e),this.a=e}function Qse(e){tc.call(this,e),this.a=e}function Zse(e){Yy.call(this,e),this.a=e}function nf(e){Di.call(this,(_n(e),e))}function Ws(e){Di.call(this,(_n(e),e))}function CK(e){$ue.call(this,new ehe(e))}function iOe(e){this.a=e,Ui.call(this,e)}function Wse(e,n){this.a=n,SX.call(this,e)}function rOe(e,n){this.a=n,eY.call(this,e)}function cOe(e,n){this.a=e,eY.call(this,n)}function uOe(e,n){this.a=n,UP.call(this,e)}function oOe(e,n){this.a=n,UP.call(this,e)}function ele(e){aX.call(this),fc(this,e)}function zs(e){return ft(e.a!=null),e.a}function sOe(e,n){return Te(n.a,e.a),e.a}function lOe(e,n){return Te(n.b,e.a),e.a}function rw(e,n){return Te(n.a,e.a),e.a}function mC(e,n,t){return zY(e,n,n,t),e}function R$(e,n){return++e.b,Te(e.a,n)}function nle(e,n){return++e.b,Go(e.a,n)}function Xwn(e,n){return vi(e.c.d,n.c.d)}function Kwn(e,n){return vi(e.c.c,n.c.c)}function Vwn(e,n){return vi(e.n.a,n.n.a)}function Ho(e,n){return u(wi(e.b,n),16)}function Ywn(e,n){return e.n.b=(_n(n),n)}function Qwn(e,n){return e.n.b=(_n(n),n)}function rs(e,n){return!!n&&e.b[n.g]==n}function HE(e){return du(e.a)||du(e.b)}function Zwn(e,n){return vi(e.e.b,n.e.b)}function Wwn(e,n){return vi(e.e.a,n.e.a)}function e2n(e,n,t){return KLe(e,n,t,e.b)}function tle(e,n,t){return KLe(e,n,t,e.c)}function n2n(e){return el(),!!e&&!e.dc()}function fOe(){pE(),this.b=new SEe(this)}function B$(){B$=Y,bH=new Li(HYe,0)}function u4(e){this.d=e,ct.call(this,e)}function o4(e){this.c=e,ct.call(this,e)}function vC(e){this.c=e,u4.call(this,e)}function ile(e,n){gde.call(this,e,n,null)}function s4(e){return e.a!=null?e.a:null}function cw(e){return e.$H||(e.$H=++EBn)}function wd(e){var n;n=e.a,e.a=e.b,e.b=n}function yC(e,n){kE(),this.a=e,this.b=n}function z$(e,n){gd(),this.b=e,this.c=n}function F$(e,n){rV(),this.f=n,this.d=e}function rle(e,n){Gae(n,e),this.c=e,this.b=n}function t2n(e,n){return oV(e.c).Kd().Xb(n)}function OK(e,n){return new aNe(e,e.gc(),n)}function i2n(e){return $P(),xt((q_e(),Pen),e)}function r2n(e){return new hp(3,e)}function Ph(e){return cl(e,$p),new So(e)}function aOe(e){return j9(),parseInt(e)||-1}function o9(e,n,t){return Vse(e,Uo(n),t)}function cle(e,n,t){u(iO(e,n),22).Ec(t)}function c2n(e,n,t){fQ(e.a,t),oz(e.a,n)}function s9(e,n,t){var i;i=e.dd(n),i.Rb(t)}function hOe(e,n,t,i){Efe.call(this,e,n,t,i)}function dOe(e){Qle.call(this,e,null,null)}function NK(e){J2(),this.b=e,this.a=!0}function bOe(e){KP(),this.b=e,this.a=!0}function gOe(e){if(!e)throw P(new xl)}function ule(e){if(!e)throw P(new Ux)}function u2n(e){if(!e)throw P(new lX)}function ft(e){if(!e)throw P(new fu)}function V2(e){if(!e)throw P(new ts)}function wOe(e){e.d=new dOe(e),e.e=new gt}function l9(e){return ft(e.b!=0),e.a.a.c}function Mf(e){return ft(e.b!=0),e.c.b.c}function o2n(e,n){return zY(e,n,n+1,""),e}function pOe(e){iW(),BSe(this),this.Df(e)}function mOe(e){this.c=e,this.a=1,this.b=1}function kC(e){q(e,161)&&u(e,161).mi()}function vOe(e){return e.b=u(Zfe(e.a),45)}function Y2(e,n){return u(Ca(e.a,n),35)}function di(e,n){return!!e.q&&oo(e.q,n)}function s2n(e,n){return e>0?n/(e*e):n*100}function l2n(e,n){return e>0?n*n/e:n*n*100}function f2n(e){return e.f!=null?e.f:""+e.g}function DK(e){return e.f!=null?e.f:""+e.g}function a2n(e){return N1(),e.e.a+e.f.a/2}function h2n(e){return N1(),e.e.b+e.f.b/2}function d2n(e,n,t){return N1(),t.e.b-e*n}function b2n(e,n,t){return N1(),t.e.a-e*n}function g2n(e,n,t){return YP(),t.Lg(e,n)}function w2n(e,n){return I0(),dn(e,n.e,n)}function p2n(e,n,t){return Te(n,JFe(e,t))}function m2n(e,n,t){eB(),e.nf(n)&&t.Ad(e)}function Q2(e,n,t){return e.a+=n,e.b+=t,e}function yOe(e,n,t){return e.a-=n,e.b-=t,e}function ole(e,n){return e.a=n.a,e.b=n.b,e}function H$(e){return e.a=-e.a,e.b=-e.b,e}function kOe(e){this.c=e,Cs(e,0),Os(e,0)}function EOe(e){ji.call(this),bj(this,e)}function jOe(){Mt.call(this,"GROW_TREE",0)}function Fs(e,n,t){us.call(this,e,n,t,2)}function SOe(e,n){gd(),sle.call(this,e,n)}function sle(e,n){gd(),z$.call(this,e,n)}function AOe(e,n){gd(),z$.call(this,e,n)}function MOe(e,n){kE(),yC.call(this,e,n)}function IK(e,n){Ol(),uR.call(this,e,n)}function TOe(e,n){Ol(),IK.call(this,e,n)}function lle(e,n){Ol(),IK.call(this,e,n)}function xOe(e,n){Ol(),lle.call(this,e,n)}function fle(e,n){Ol(),uR.call(this,e,n)}function COe(e,n){Ol(),fle.call(this,e,n)}function OOe(e,n){Ol(),uR.call(this,e,n)}function v2n(e,n){return e.c.Ec(u(n,136))}function y2n(e,n){return u(Bn(e.e,n),26)}function k2n(e,n){return u(Bn(e.e,n),26)}function ale(e,n,t){return Uz(cO(e,n),t)}function E2n(e,n,t){return n.xl(e.e,e.c,t)}function j2n(e,n,t){return n.yl(e.e,e.c,t)}function _K(e,n){return C0(e.e,u(n,52))}function S2n(e,n,t){Tj(Xu(e.a),n,Z_e(t))}function A2n(e,n,t){Tj(xs(e.a),n,W_e(t))}function NOe(e,n){return _n(e),e+zK(n)}function M2n(e){return e==null?null:su(e)}function T2n(e){return e==null?null:su(e)}function x2n(e){return e==null?null:YTn(e)}function C2n(e){return e==null?null:G$n(e)}function E1(e){e.o==null&&gOn(e)}function Pe(e){return qE(e==null||X2(e)),e}function ie(e){return qE(e==null||K2(e)),e}function Dt(e){return qE(e==null||Pr(e)),e}function O2n(e,n){return BQ(e,n),new EIe(e,n)}function EC(e,n){this.c=e,n9.call(this,e,n)}function JE(e,n){this.a=e,EC.call(this,e,n)}function N2n(e,n){this.d=e,Ze(this),this.b=n}function hle(){aBe.call(this),this.Bb|=kc}function DOe(){this.a=new bw,this.b=new bw}function dle(e){this.q=new E.Date(Bb(e))}function x3(){x3=Y,Iv=new mi("root")}function f9(){f9=Y,ZD=new gAe,new wAe}function Z2(){Z2=Y,Hme=We((Xs(),Eg))}function D2n(e,n){n.a?PCn(e,n):TK(e.a,n.b)}function IOe(e,n){Fa||Te(e.a,n)}function I2n(e,n){return nC(),L9(n.d.i,e)}function _2n(e,n){return C4(),new xXe(n,e)}function L2n(e,n,t){return e.Le(n,t)<=0?t:n}function P2n(e,n,t){return e.Le(n,t)<=0?n:t}function $2n(e,n){return u(Ca(e.b,n),144)}function R2n(e,n){return u(Ca(e.c,n),233)}function LK(e){return u(_e(e.a,e.b),295)}function _Oe(e){return new ke(e.c,e.d+e.a)}function LOe(e){return _n(e),e?1231:1237}function POe(e){return nl(),bCe(u(e,203))}function ble(e,n){return u(Bn(e.b,n),278)}function $Oe(e,n,t){++e.j,e.oj(n,e.Xi(n,t))}function jC(e,n,t){++e.j,e.rj(),lY(e,n,t)}function gle(e,n,t){QR.call(this,e,n,t,null)}function ROe(e,n,t){QR.call(this,e,n,t,null)}function wle(e,n){fY.call(this,e),this.a=n}function ple(e,n){fY.call(this,e),this.a=n}function Li(e,n){mi.call(this,e),this.a=n}function mle(e,n){Yue.call(this,e),this.a=n}function PK(e,n){Yue.call(this,e),this.a=n}function BOe(e,n){this.c=e,pw.call(this,n)}function zOe(e,n){this.a=e,OSe.call(this,n)}function SC(e,n){this.a=e,OSe.call(this,n)}function vle(e,n,t){return t=ll(e,n,3,t),t}function yle(e,n,t){return t=ll(e,n,6,t),t}function kle(e,n,t){return t=ll(e,n,9,t),t}function uh(e,n){return BC(n,qge),e.f=n,e}function Ele(e,n){return(n&ri)%e.d.length}function FOe(e,n,t){return cge(e.c,e.b,n,t)}function B2n(e,n,t){return e.apply(n,t)}function HOe(e,n,t){var i;i=e.dd(n),i.Rb(t)}function JOe(e,n,t){return e.a+=ah(n,0,t),e}function AC(e){return!e.a&&(e.a=new In),e.a}function jle(e,n){var t;return t=e.e,e.e=n,t}function Sle(e,n){var t;return t=n,!!e.De(t)}function Tb(e,n){return Pn(),e==n?0:e?1:-1}function W2(e,n){e.a._c(e.b,n),++e.b,e.c=-1}function z2n(e,n){var t;t=e[LW],t.call(e,n)}function F2n(e,n){var t;t=e[LW],t.call(e,n)}function H2n(e,n,t){Ab(),yP(e,n.Te(e.a,t))}function Ale(e,n,t){return w4(e,u(n,23),t)}function Tf(e,n){return FP(new Array(n),e)}function J2n(e){return _t(Nb(e,32))^_t(e)}function $K(e){return String.fromCharCode(e)}function G2n(e){return e==null?null:e.message}function RK(e){this.a=(vn(),new Dn(Tt(e)))}function GOe(e){this.a=(cl(e,$p),new So(e))}function qOe(e){this.a=(cl(e,$p),new So(e))}function UOe(){this.a=new xe,this.b=new xe}function XOe(){this.a=new X6,this.b=new USe}function Mle(){this.b=new E0,this.a=new E0}function KOe(){this.b=new Kr,this.c=new xe}function Tle(){this.n=new Kr,this.o=new Kr}function J$(){this.n=new q5,this.i=new r4}function VOe(){this.b=new fr,this.a=new fr}function YOe(){this.a=new xe,this.d=new xe}function QOe(){this.a=new MU,this.b=new i_}function ZOe(){this.b=new AMe,this.a=new pT}function WOe(){this.b=new gt,this.a=new gt}function eNe(){J$.call(this),this.a=new Kr}function xle(e,n,t,i){sR.call(this,e,n,t,i)}function q2n(e,n){return e.n.a=(_n(n),n+10)}function U2n(e,n){return e.n.a=(_n(n),n+10)}function X2n(e,n){return nC(),!L9(n.d.i,e)}function nNe(e){Fu(e.e),e.d.b=e.d,e.d.a=e.d}function MC(e){e.b?MC(e.b):e.f.c.yc(e.e,e.d)}function K2n(e,n){y1(e.f)?oOn(e,n):WMn(e,n)}function tNe(e,n,t){t!=null&&vB(n,HQ(e,t))}function iNe(e,n,t){t!=null&&yB(n,HQ(e,t))}function l4(e,n,t,i){we.call(this,e,n,t,i)}function Cle(e,n,t,i){we.call(this,e,n,t,i)}function rNe(e,n,t,i){Cle.call(this,e,n,t,i)}function cNe(e,n,t,i){wR.call(this,e,n,t,i)}function BK(e,n,t,i){wR.call(this,e,n,t,i)}function uNe(e,n,t,i){BK.call(this,e,n,t,i)}function Ole(e,n,t,i){wR.call(this,e,n,t,i)}function Cn(e,n,t,i){Ole.call(this,e,n,t,i)}function Nle(e,n,t,i){BK.call(this,e,n,t,i)}function oNe(e,n,t,i){Nle.call(this,e,n,t,i)}function sNe(e,n,t,i){Mfe.call(this,e,n,t,i)}function ep(e,n){ko.call(this,TS+e+cg+n)}function V2n(e,n){return n==e||r8(Cz(n),e)}function Dle(e,n){return e.hk().ti().oi(e,n)}function Ile(e,n){return e.hk().ti().qi(e,n)}function Y2n(e,n){return e.e=u(e.d.Kb(n),162)}function lNe(e,n){return Qt(e.a,n,"")==null}function fNe(e,n){return _n(e),ue(e)===ue(n)}function hn(e,n){return _n(e),ue(e)===ue(n)}function _le(e,n,t){return e.lastIndexOf(n,t)}function aNe(e,n,t){this.a=e,rle.call(this,n,t)}function hNe(e){this.c=e,C$.call(this,fN,0)}function dNe(e,n,t){this.c=n,this.b=t,this.a=e}function bi(e,n){return e.a+=n.a,e.b+=n.b,e}function Or(e,n){return e.a-=n.a,e.b-=n.b,e}function Q2n(e){return $2(e.j.c,0),e.a=-1,e}function Z2n(e,n){var t;return t=n.ni(e.a),t}function Lle(e,n,t){return t=ll(e,n,11,t),t}function W2n(e,n,t){return vi(e[n.a],e[t.a])}function epn(e,n){return uo(e.a.d.p,n.a.d.p)}function npn(e,n){return uo(n.a.d.p,e.a.d.p)}function tpn(e,n){return vi(e.c-e.s,n.c-n.s)}function ipn(e,n){return vi(e.b.e.a,n.b.e.a)}function rpn(e,n){return vi(e.c.e.a,n.c.e.a)}function cpn(e,n){return fe(n,(Ce(),sD),e)}function upn(e,n){return e.b.zd(new NTe(e,n))}function opn(e,n){return e.b.zd(new DTe(e,n))}function bNe(e,n){return e.b.zd(new ITe(e,n))}function gNe(e,n){return q(n,16)&&sXe(e.c,n)}function wNe(e){return e.c?gu(e.c.a,e,0):-1}function spn(e){return e<100?null:new f0(e)}function f4(e){return e==kg||e==c1||e==eo}function lpn(e,n,t){return u(e.c,72).Uk(n,t)}function G$(e,n,t){return u(e.c,72).Vk(n,t)}function fpn(e,n,t){return E2n(e,u(n,344),t)}function Ple(e,n,t){return j2n(e,u(n,344),t)}function apn(e,n,t){return VJe(e,u(n,344),t)}function pNe(e,n,t){return aTn(e,u(n,344),t)}function GE(e,n){return n==null?null:kp(e.b,n)}function hpn(e,n){Fa||n&&(e.d=n)}function $le(e,n){if(!e)throw P(new Gn(n))}function a9(e){if(!e)throw P(new qc(Tge))}function zK(e){return K2(e)?(_n(e),e):e.se()}function q$(e){return!isNaN(e)&&!isFinite(e)}function FK(e){OCe(this),Js(this),fc(this,e)}function ds(e){EK(this),Wle(this.c,0,e.Nc())}function TC(e){h9(),this.d=e,this.a=new A3}function mNe(e,n,t){this.d=e,this.b=t,this.a=n}function Nl(e,n,t){this.a=e,this.b=n,this.c=t}function vNe(e,n,t){this.a=e,this.b=n,this.c=t}function Rle(e,n){this.c=e,bV.call(this,e,n)}function yNe(e,n){v3n.call(this,e,e.length,n)}function HK(e,n){if(e!=n)throw P(new xl)}function kNe(e){this.a=e,bd(),_u(Date.now())}function ENe(e){As(e.a),Qae(e.c,e.b),e.b=null}function JK(){JK=Y,_me=new li,inn=new oi}function GK(e){var n;return n=new yM,n.e=e,n}function dpn(e,n,t){return Ab(),e.a.Wd(n,t),n}function Ble(e,n,t){this.b=e,this.c=n,this.a=t}function zle(e){var n;return n=new ZSe,n.b=e,n}function bpn(e){return sa(),xt((m$e(),ynn),e)}function gpn(e){return C9(),xt((D$e(),unn),e)}function wpn(e){return $l(),xt((p$e(),hnn),e)}function ppn(e){return gs(),xt((v$e(),Enn),e)}function mpn(e){return qo(),xt((y$e(),Snn),e)}function vpn(e){return Qz(),xt((tCe(),Xnn),e)}function ypn(e){return kw(),xt(($$e(),Vnn),e)}function kpn(e){return B9(),xt((R$e(),Btn),e)}function Epn(e){return oB(),xt((SPe(),rtn),e)}function jpn(e){return fj(),xt((w$e(),Otn),e)}function Spn(e){return Br(),xt((MRe(),_tn),e)}function Apn(e){return P4(),xt((P$e(),qtn),e)}function Mpn(e){return zn(),xt((KBe(),Vtn),e)}function Tpn(e){return _9(),xt((APe(),ein),e)}function qK(e){sR.call(this,e.d,e.c,e.a,e.b)}function Fle(e){sR.call(this,e.d,e.c,e.a,e.b)}function xpn(e){return qr(),xt((iCe(),nin),e)}function jNe(){jNe=Y,Ean=oe(Mr,xn,1,0,5,1)}function SNe(){SNe=Y,zan=oe(Mr,xn,1,0,5,1)}function Hle(){Hle=Y,Fan=oe(Mr,xn,1,0,5,1)}function xC(){xC=Y,yH=new rq,kH=new LM}function U$(){U$=Y,uin=new jq,cin=new Sq}function el(){el=Y,ain=new ck,hin=new nd}function Cpn(e){return yw(),xt((e$e(),Ein),e)}function Opn(e){return _f(),xt((H$e(),gin),e)}function Npn(e){return Mp(),xt((yRe(),pin),e)}function Dpn(e){return $z(),xt((QBe(),jin),e)}function Ipn(e){return z4(),xt((eBe(),Sin),e)}function _pn(e){return WR(),xt((oPe(),Ain),e)}function Lpn(e){return Cj(),xt((G$e(),Min),e)}function Ppn(e){return gB(),xt((VPe(),Tin),e)}function $pn(e){return UO(),xt((ize(),xin),e)}function Rpn(e){return sO(),xt((sPe(),Cin),e)}function Bpn(e){return Gb(),xt((YPe(),Nin),e)}function zpn(e){return Ez(),xt((WRe(),Din),e)}function Fpn(e){return tO(),xt((lPe(),Iin),e)}function Hpn(e){return RO(),xt((QRe(),_in),e)}function Jpn(e){return u8(),xt((ZRe(),Lin),e)}function Gpn(e){return Oc(),xt((yze(),Pin),e)}function qpn(e){return R9(),xt((QPe(),$in),e)}function Upn(e){return M0(),xt((ZPe(),Rin),e)}function Xpn(e){return C1(),xt((WPe(),zin),e)}function Kpn(e){return zR(),xt((fPe(),Fin),e)}function Vpn(e){return qs(),xt((ERe(),Jin),e)}function Ypn(e){return JR(),xt((aPe(),Gin),e)}function Qpn(e){return KO(),xt((rze(),Nun),e)}function Zpn(e){return jj(),xt((n$e(),Dun),e)}function Wpn(e){return Ap(),xt((z$e(),Iun),e)}function emn(e){return Ij(),xt((kRe(),_un),e)}function nmn(e){return P0(),xt((vze(),Lun),e)}function tmn(e){return P1(),xt((F$e(),Pun),e)}function imn(e){return rO(),xt((hPe(),$un),e)}function rmn(e){return Cc(),xt((t$e(),Bun),e)}function cmn(e){return OB(),xt((i$e(),zun),e)}function umn(e){return Ej(),xt((r$e(),Fun),e)}function omn(e){return G9(),xt((c$e(),Hun),e)}function smn(e){return bB(),xt((u$e(),Jun),e)}function lmn(e){return NB(),xt((o$e(),Gun),e)}function fmn(e){return IB(),xt((L$e(),rin),e)}function amn(e){return Ub(),xt((B$e(),lon),e)}function hmn(e,n){return _n(e),e+(_n(n),n)}function dmn(e){return sj(),xt((dPe(),bon),e)}function bmn(e){return oh(),xt((gPe(),kon),e)}function gmn(e){return Aa(),xt((bPe(),jon),e)}function wmn(e){return ca(),xt((wPe(),Ron),e)}function h9(){h9=Y,U4e=(Oe(),Kn),xJ=Wn}function pmn(e){return gw(),xt((pPe(),qon),e)}function mmn(e){return B4(),xt((X$e(),Uon),e)}function vmn(e){return Vj(),xt((rCe(),Xon),e)}function ymn(e){return kj(),xt((s$e(),Kon),e)}function kmn(e){return yj(),xt((J$e(),psn),e)}function Emn(e){return BR(),xt((mPe(),msn),e)}function jmn(e){return EB(),xt((vPe(),jsn),e)}function Smn(e){return pz(),xt((jRe(),Asn),e)}function Amn(e){return nB(),xt((yPe(),Msn),e)}function Mmn(e){return kO(),xt((l$e(),Tsn),e)}function Tmn(e){return lz(),xt((U$e(),Xsn),e)}function xmn(e){return CB(),xt((f$e(),Ksn),e)}function Cmn(e){return YB(),xt((a$e(),Vsn),e)}function Omn(e){return vz(),xt((q$e(),Qsn),e)}function Nmn(e){return qB(),xt((g$e(),eln),e)}function Dmn(e){return!e.e&&(e.e=new xe),e.e}function X$(e,n,t){this.e=n,this.b=e,this.d=t}function ANe(e,n,t){this.a=e,this.b=n,this.c=t}function MNe(e,n,t){this.a=e,this.b=n,this.c=t}function Jle(e,n,t){this.a=e,this.b=n,this.c=t}function TNe(e,n,t){this.a=e,this.b=n,this.c=t}function xNe(e,n,t){this.a=e,this.c=n,this.b=t}function K$(e,n,t){this.b=e,this.a=n,this.c=t}function CNe(e,n,t){this.b=e,this.a=n,this.c=t}function UK(e,n){this.c=e,this.a=n,this.b=n-e}function Imn(e){return zB(),xt((d$e(),Sln),e)}function _mn(e){return ZP(),xt((RLe(),Oln),e)}function Lmn(e){return YC(),xt((EPe(),Nln),e)}function Pmn(e){return zO(),xt((ARe(),Dln),e)}function $mn(e){return QP(),xt(($Le(),xln),e)}function Rmn(e){return qj(),xt((SRe(),Mln),e)}function Bmn(e){return MO(),xt((b$e(),Tln),e)}function zmn(e){return XR(),xt((kPe(),Eln),e)}function Fmn(e){return tB(),xt((h$e(),jln),e)}function Hmn(e){return mE(),xt((BLe(),Kln),e)}function Jmn(e){return gO(),xt((jPe(),Vln),e)}function Gmn(e){return dh(),xt((xRe(),nfn),e)}function qmn(e){return Qb(),xt((VBe(),ifn),e)}function Umn(e){return L1(),xt((V$e(),Bfn),e)}function Xmn(e){return vr(),xt((TRe(),Pfn),e)}function Kmn(e){return U9(),xt((K$e(),$fn),e)}function Vmn(e){return Oa(),xt((k$e(),Rfn),e)}function Ymn(e){return Gh(),xt((XRe(),rfn),e)}function Qmn(e){return Yb(),xt((KRe(),ffn),e)}function Zmn(e){return Op(),xt((uze(),qfn),e)}function Wmn(e){return G3(),xt((CRe(),Ufn),e)}function e3n(e){return Rr(),xt((YRe(),Xfn),e)}function n3n(e){return ws(),xt((VRe(),Kfn),e)}function t3n(e){return ol(),xt((Y$e(),Gfn),e)}function i3n(e){return yz(),xt((URe(),zfn),e)}function r3n(e){return _1(),xt((j$e(),Hfn),e)}function c3n(e){return GR(),xt((Q$e(),ran),e)}function u3n(e){return Is(),xt((cze(),tan),e)}function o3n(e){return I4(),xt((E$e(),ian),e)}function s3n(e){return Oe(),xt((ORe(),Vfn),e)}function l3n(e){return hj(),xt((S$e(),ean),e)}function f3n(e){return Xs(),xt((Z$e(),nan),e)}function a3n(e){return UB(),xt((W$e(),can),e)}function h3n(e){return _B(),xt((eRe(),san),e)}function d3n(e){return s8(),xt((YBe(),kan),e)}function ONe(e,n,t){Ol(),cae.call(this,e,n,t)}function XK(e,n,t){Ol(),zfe.call(this,e,n,t)}function NNe(e,n,t){Ol(),XK.call(this,e,n,t)}function Gle(e,n,t){Ol(),XK.call(this,e,n,t)}function DNe(e,n,t){Ol(),Gle.call(this,e,n,t)}function INe(e,n,t){Ol(),qle.call(this,e,n,t)}function qle(e,n,t){Ol(),zfe.call(this,e,n,t)}function Ule(e,n,t){Ol(),zfe.call(this,e,n,t)}function _Ne(e,n,t){Ol(),Ule.call(this,e,n,t)}function LNe(e,n,t){this.a=e,this.c=n,this.b=t}function PNe(e,n,t){this.a=e,this.b=n,this.c=t}function Xle(e,n,t){this.a=e,this.b=n,this.c=t}function Kle(e,n,t){this.a=e,this.b=n,this.c=t}function KK(e,n,t){this.a=e,this.b=n,this.c=t}function $Ne(e,n,t){this.a=e,this.b=n,this.c=t}function pd(e,n,t){this.e=e,this.a=n,this.c=t}function Vle(e){this.d=e,Ze(this),this.b=uvn(e.d)}function Yle(e,n){cgn.call(this,e,JB(new Eu(n)))}function CC(e,n){return Tt(e),Tt(n),new HMe(e,n)}function a4(e,n){return Tt(e),Tt(n),new KNe(e,n)}function b3n(e,n){return Tt(e),Tt(n),new VNe(e,n)}function g3n(e,n){return Tt(e),Tt(n),new WMe(e,n)}function VK(e){return ft(e.b!=0),_l(e,e.a.a)}function w3n(e){return ft(e.b!=0),_l(e,e.c.b)}function p3n(e){return!e.c&&(e.c=new Tl),e.c}function OC(e){var n;return n=new ji,IY(n,e),n}function RNe(e){var n;return n=new aX,IY(n,e),n}function m3n(e){var n;return n=new fr,kY(n,e),n}function d9(e){var n;return n=new xe,kY(n,e),n}function u(e,n){return qE(e==null||NQ(e,n)),e}function v3n(e,n,t){$De.call(this,n,t),this.a=e}function BNe(e,n){this.c=e,this.b=n,this.a=!1}function zNe(){this.a=";,;",this.b="",this.c=""}function FNe(e,n,t){this.b=e,Vxe.call(this,n,t)}function Qle(e,n,t){this.c=e,t$.call(this,n,t)}function Zle(e,n,t){c9.call(this,e,n),this.b=t}function Wle(e,n,t){q0e(t,0,e,n,t.length,!1)}function $h(e,n,t,i,r){e.b=n,e.c=t,e.d=i,e.a=r}function efe(e,n,t,i,r){e.d=n,e.c=t,e.a=i,e.b=r}function y3n(e,n){n&&(e.b=n,e.a=(m0(n),n.a))}function NC(e,n){if(!e)throw P(new Gn(n))}function h4(e,n){if(!e)throw P(new qc(n))}function nfe(e,n){if(!e)throw P(new XAe(n))}function k3n(e,n){return VP(),uo(e.d.p,n.d.p)}function E3n(e,n){return N1(),vi(e.e.b,n.e.b)}function j3n(e,n){return N1(),vi(e.e.a,n.e.a)}function S3n(e,n){return uo(eDe(e.d),eDe(n.d))}function V$(e,n){return n&&kR(e,n.d)?n:null}function A3n(e,n){return n==(Oe(),Kn)?e.c:e.d}function M3n(e){return new ke(e.c+e.b,e.d+e.a)}function HNe(e){return e!=null&&!wQ(e,YA,QA)}function T3n(e,n){return(SFe(e)<<4|SFe(n))&yr}function JNe(e,n,t,i,r){e.c=n,e.d=t,e.b=i,e.a=r}function tfe(e){var n,t;n=e.b,t=e.c,e.b=t,e.c=n}function ife(e){var n,t;t=e.d,n=e.a,e.d=n,e.a=t}function x3n(e,n){var t;return t=e.c,Ihe(e,n),t}function rfe(e,n){return n<0?e.g=-1:e.g=n,e}function Y$(e,n){return v8n(e),e.a*=n,e.b*=n,e}function DC(e,n,t){jse.call(this,e,n),this.c=t}function Q$(e,n,t){jse.call(this,e,n),this.c=t}function cfe(e){Hle(),u3.call(this),this._h(e)}function GNe(){M9(),zvn.call(this,(h0(),mf))}function qNe(e){return si(),new Rh(0,e)}function UNe(){UNe=Y,Rce=(vn(),new Dn(Ine))}function Z$(){Z$=Y,new mde((mX(),qne),(pX(),Gne))}function XNe(){this.b=W(ie(Ie(($f(),wte))))}function YK(e){this.b=e,this.a=Cb(this.b.a).Md()}function KNe(e,n){this.b=e,this.a=n,bx.call(this)}function VNe(e,n){this.a=e,this.b=n,bx.call(this)}function YNe(e,n,t){this.a=e,y3.call(this,n,t)}function QNe(e,n,t){this.a=e,y3.call(this,n,t)}function b9(e,n,t){var i;i=new up(t),Nf(e,n,i)}function ufe(e,n,t){var i;return i=e[n],e[n]=t,i}function W$(e){var n;return n=e.slice(),bY(n,e)}function eR(e){var n;return n=e.n,e.a.b+n.d+n.a}function ZNe(e){var n;return n=e.n,e.e.b+n.d+n.a}function ofe(e){var n;return n=e.n,e.e.a+n.b+n.c}function sfe(e){e.a.b=e.b,e.b.a=e.a,e.a=e.b=null}function qt(e,n){return Xi(e,n,e.c.b,e.c),!0}function C3n(e){return e.a?e.a:MV(e)}function qE(e){if(!e)throw P(new Vy(null))}function uw(e,n){return $j(e,new c9(n.a,n.b))}function O3n(e){return!cc(e)&&e.c.i.c==e.d.i.c}function N3n(e,n){return e.c=n)throw P(new cAe)}function Fu(e){e.f=new aCe(e),e.i=new hCe(e),++e.g}function bR(e){this.b=new So(11),this.a=(hw(),e)}function lV(e){this.b=null,this.a=(hw(),e||Dme)}function Sfe(e,n){this.e=e,this.d=(n&64)!=0?n|wh:n}function $De(e,n){this.c=0,this.d=e,this.b=n|64|wh}function RDe(e){this.a=RHe(e.a),this.b=new ds(e.b)}function md(e,n,t,i){var r;r=e.i,r.i=n,r.a=t,r.b=i}function Afe(e){var n;for(n=e;n.f;)n=n.f;return n}function dvn(e){return e.e?Kae(e.e):null}function VE(e){return ws(),!e.Gc(K1)&&!e.Gc(ob)}function BDe(e,n,t){return a8(),BY(e,n)&&BY(e,t)}function zDe(e,n,t){return KVe(e,u(n,12),u(t,12))}function fV(e,n){return n.Sh()?C0(e.b,u(n,52)):n}function gR(e){return new ke(e.c+e.b/2,e.d+e.a/2)}function bvn(e,n,t){n.of(t,W(ie(Bn(e.b,t)))*e.a)}function gvn(e,n){n.Tg("General 'Rotator",1),O$n(e)}function Dr(e,n,t,i,r){hY.call(this,e,n,t,i,r,-1)}function YE(e,n,t,i,r){eO.call(this,e,n,t,i,r,-1)}function we(e,n,t,i){mr.call(this,e,n,t),this.b=i}function wR(e,n,t,i){DC.call(this,e,n,t),this.b=i}function FDe(e){Rxe.call(this,e,!1),this.a=!1}function HDe(){gK.call(this,"LOOKAHEAD_LAYOUT",1)}function JDe(){gK.call(this,"LAYOUT_NEXT_LEVEL",3)}function GDe(e){this.b=e,u4.call(this,e),YCe(this)}function qDe(e){this.b=e,vC.call(this,e),QCe(this)}function UDe(e,n){this.b=e,RU.call(this,e.b),this.a=n}function rp(e,n,t){this.a=e,l4.call(this,n,t,5,6)}function Mfe(e,n,t,i){this.b=e,mr.call(this,n,t,i)}function Db(e,n,t){bh(),this.e=e,this.d=n,this.a=t}function Zr(e,n){for(_n(n);e.Ob();)n.Ad(e.Pb())}function pR(e,n){return si(),new Ffe(e,n,0)}function aV(e,n){return si(),new Ffe(6,e,n)}function wvn(e,n){return hn(e.substr(0,n.length),n)}function oo(e,n){return Pr(n)?IV(e,n):!!Uc(e.f,n)}function pvn(e){return Io(~e.l&_s,~e.m&_s,~e.h&B1)}function hV(e){return typeof e===uN||typeof e===uW}function zh(e){return new qn(new Wse(e.a.length,e.a))}function dV(e){return new bn(null,Avn(e,e.length))}function XDe(e){if(!e)throw P(new fu);return e.d}function g4(e){var n;return n=vj(e),ft(n!=null),n}function mvn(e){var n;return n=cEn(e),ft(n!=null),n}function w9(e,n){var t;return t=e.a.gc(),Gae(n,t),t-n}function ar(e,n){var t;return t=e.a.yc(n,e),t==null}function _C(e,n){return e.a.yc(n,(Pn(),U0))==null}function vvn(e,n){return e>0?E.Math.log(e/n):-100}function Tfe(e,n){return n?fc(e,n):!1}function w4(e,n,t){return If(e.a,n),ufe(e.b,n.g,t)}function yvn(e,n,t){g9(t,e.a.c.length),il(e.a,t,n)}function ce(e,n,t,i){Uze(n,t,e.length),kvn(e,n,t,i)}function kvn(e,n,t,i){var r;for(r=n;r0?1:0}function ZE(e){return e.e==0?e:new Db(-e.e,e.d,e.a)}function jvn(e){return e==Ki?FN:e==Nr?"-INF":""+e}function Svn(e){return e==Ki?FN:e==Nr?"-INF":""+e}function Avn(e,n){return g8n(n,e.length),new rDe(e,n)}function VDe(e,n,t,i,r){for(;n=e.g}function EV(e,n,t){var i;return i=DY(e,n,t),Nbe(e,i)}function oIe(e,n){var t;t=console[e],t.call(console,n)}function p4(e,n){var t;t=e.a.length,bp(e,t),YV(e,t,n)}function sIe(e,n){var t;++e.j,t=e.Cj(),e.pj(e.Xi(t,n))}function jV(e,n){for(_n(n);e.c=e?new Foe:B8n(e-1)}function tf(e){if(e==null)throw P(new J5);return e}function _n(e){if(e==null)throw P(new J5);return e}function Gvn(e){return!e.a&&(e.a=new mr(sb,e,4)),e.a}function fw(e){return!e.d&&(e.d=new mr(Pc,e,1)),e.d}function qvn(e){if(e.p!=3)throw P(new ts);return e.e}function Uvn(e){if(e.p!=4)throw P(new ts);return e.e}function Xvn(e){if(e.p!=6)throw P(new ts);return e.f}function Kvn(e){if(e.p!=3)throw P(new ts);return e.j}function Vvn(e){if(e.p!=4)throw P(new ts);return e.j}function Yvn(e){if(e.p!=6)throw P(new ts);return e.k}function ur(){xAe.call(this),$2(this.j.c,0),this.a=-1}function mIe(){Mt.call(this,"DELAUNAY_TRIANGULATION",0)}function Qvn(){return $P(),z(B(Len,1),ye,537,0,[Xne])}function Zvn(e,n,t){return N4(),t.Kg(e,u(n.jd(),147))}function Wvn(e,n){kt((!e.a&&(e.a=new SC(e,e)),e.a),n)}function Jfe(e,n){e.c<0||e.b.b=0?e.hi(t):P0e(e,n)}function v9(e,n){var t;return t=kV("",e),t.n=n,t.i=1,t}function aw(e){return e.c==-2&&tX(e,bTn(e.g,e.b)),e.c}function Gfe(e){return!e.b&&(e.b=new xP(new wX)),e.b}function vIe(e,n){return Z$(),new mde(new WCe(e),new ZCe(n))}function n5n(e){return cl(e,fW),sB(pc(pc(5,e),e/10|0))}function SV(){SV=Y,Ren=new Voe(z(B(sg,1),Zz,45,0,[]))}function yIe(){d0e.call(this,og,(aMe(),Van)),vPn(this)}function kIe(){d0e.call(this,lf,(Wy(),F8e)),TLn(this)}function EIe(e,n){Own.call(this,z8n(Tt(e),Tt(n))),this.a=n}function qfe(e,n,t,i){ew.call(this,e,n),this.d=t,this.a=i}function ER(e,n,t,i){ew.call(this,e,t),this.a=n,this.f=i}function jIe(e,n){this.b=e,bV.call(this,e,n),YCe(this)}function SIe(e,n){this.b=e,Rle.call(this,e,n),QCe(this)}function tj(e){this.d=e,this.a=this.d.b,this.b=this.d.c}function AIe(e){e.b=!1,e.c=!1,e.d=!1,e.a=!1}function y9(e){return!e.a&&(e.a=new QAe(e.c.vc())),e.a}function MIe(e){return!e.b&&(e.b=new Yy(e.c.ec())),e.b}function TIe(e){return!e.d&&(e.d=new Hr(e.c.Bc())),e.d}function Fh(e,n){for(;n-- >0;)e=e<<1|(e<0?1:0);return e}function xIe(e,n){var t;return t=new qu(e),Jn(n.c,t),t}function t5n(e,n){uV(u(n.b,68),e),Ao(n.a,new Jue(e))}function CIe(e,n){e.u.Gc((ws(),K1))&&fCn(e,n),a9n(e,n)}function Uu(e,n){return ue(e)===ue(n)||e!=null&&ai(e,n)}function Qt(e,n,t){return Pr(n)?Xc(e,n,t):Xo(e.f,n,t)}function Ufe(e){return vn(),e?e.Me():(hw(),hw(),Ime)}function i5n(){return QP(),z(B(Tye,1),ye,477,0,[Xre])}function r5n(){return ZP(),z(B(Cln,1),ye,546,0,[Kre])}function c5n(){return mE(),z(B(Kye,1),ye,527,0,[MD])}function Rc(e,n){return tV(e.a,n)?e.b[u(n,23).g]:null}function u5n(e){return String.fromCharCode.apply(null,e)}function ic(e,n){return Yn(n,e.length),e.charCodeAt(n)}function PC(e){return e.j.c.length=0,Vfe(e.c),Q2n(e.a),e}function k9(e){return e.e==K8&&a(e,xjn(e.g,e.b)),e.e}function $C(e){return e.f==K8&&w(e,pAn(e.g,e.b)),e.f}function o5n(e){return!e.b&&(e.b=new Cn(pt,e,4,7)),e.b}function Xfe(e){return!e.c&&(e.c=new Cn(pt,e,5,8)),e.c}function Kfe(e){return!e.c&&(e.c=new we(Ps,e,9,9)),e.c}function AV(e){return!e.n&&(e.n=new we(ku,e,1,7)),e.n}function C3(e){var n;return n=e.b,!n&&(e.b=n=new Kk(e)),n}function Vfe(e){var n;for(n=e.Jc();n.Ob();)n.Pb(),n.Qb()}function s5n(e,n,t){var i;i=u(e.d.Kb(t),162),i&&i.Nb(n)}function l5n(e,n){return new t_e(u(Tt(e),51),u(Tt(n),51))}function ci(e,n){return O0(e),new bn(e,new ohe(n,e.a))}function jo(e,n){return O0(e),new bn(e,new Xae(n,e.a))}function op(e,n){return O0(e),new wle(e,new JPe(n,e.a))}function jR(e,n){return O0(e),new ple(e,new GPe(n,e.a))}function OIe(e,n){J1e(e,W(D1(n,"x")),W(D1(n,"y")))}function NIe(e,n){J1e(e,W(D1(n,"x")),W(D1(n,"y")))}function f5n(e,n){return Hoe(),vi((_n(e),e),(_n(n),n))}function a5n(e,n){return vi(e.d.c+e.d.b/2,n.d.c+n.d.b/2)}function h5n(e,n){return vi(e.g.c+e.g.b/2,n.g.c+n.g.b/2)}function d5n(e){return e!=null&&bE(yG,e.toLowerCase())}function b5n(e){el();var n;n=u(e.g,9),n.n.a=e.d.c+n.d.b}function MV(e){var n;return n=H8n(e),n||null}function ni(e,n,t,i){return JBe(e,n,t,!1),BB(e,i),e}function g5n(e,n,t){kLn(e.a,t),z7n(t),XCn(e.b,t),HLn(n,t)}function m4(e,n,t,i){Mt.call(this,e,n),this.a=t,this.b=i}function SR(e,n,t,i){this.a=e,this.c=n,this.b=t,this.d=i}function Yfe(e,n,t,i){this.c=e,this.b=n,this.a=t,this.d=i}function DIe(e,n,t,i){this.c=e,this.b=n,this.d=t,this.a=i}function TV(e,n,t,i){this.a=e,this.e=n,this.d=t,this.c=i}function IIe(e,n,t,i){this.a=e,this.d=n,this.c=t,this.b=i}function xf(e,n,t,i){this.c=e,this.d=n,this.b=t,this.a=i}function xV(e,n,t){this.a=Ige,this.d=e,this.b=n,this.c=t}function Qfe(e,n){this.b=e,this.c=n,this.a=new Q5(this.b)}function _Ie(e,n){this.d=(_n(e),e),this.a=16449,this.c=n}function LIe(e,n,t,i){Rze.call(this,e,t,i,!1),this.f=n}function CV(e,n,t){var i,r;return i=yge(e),r=n.qi(t,i),r}function S1(e){var n,t;return t=(n=new Zg,n),N9(t,e),t}function OV(e){var n,t;return t=(n=new Zg,n),w0e(t,e),t}function PIe(e){return!e.b&&(e.b=new we(wr,e,12,3)),e.b}function $Ie(e){this.a=new xe,this.e=oe(It,je,54,e,0,2)}function NV(e){this.f=e,this.c=this.f.e,e.f>0&&IJe(this)}function RIe(e,n,t,i){this.a=e,this.c=n,this.d=t,this.b=i}function BIe(e,n,t,i){this.a=e,this.b=n,this.d=t,this.c=i}function zIe(e,n,t,i){this.a=e,this.b=n,this.c=t,this.d=i}function FIe(e,n,t,i){this.a=e,this.b=n,this.c=t,this.d=i}function _b(e,n,t,i){this.e=e,this.a=n,this.c=t,this.d=i}function HIe(e,n,t,i){Ol(),HPe.call(this,n,t,i),this.a=e}function JIe(e,n,t,i){Ol(),HPe.call(this,n,t,i),this.a=e}function GIe(e,n){this.a=e,N2n.call(this,e,u(e.d,16).dd(n))}function w5n(e,n){return vi(cs(e)*Hs(e),cs(n)*Hs(n))}function p5n(e,n){return vi(cs(e)*Hs(e),cs(n)*Hs(n))}function v4(e){var n;return n=e.f,n||(e.f=new n9(e,e.c))}function vn(){vn=Y,Ec=new Ae,Zh=new un,fH=new jn}function hw(){hw=Y,Dme=new Me,tte=new Me,Ime=new rn}function E9(e){if(Ns(e.d),e.d.d!=e.c)throw P(new xl)}function Js(e){e.a.a=e.c,e.c.b=e.a,e.a.b=e.c.a=null,e.b=0}function Zfe(e){return ft(e.b0?Of(e):new xe}function AR(e){return e.n&&(e.e!==sYe&&e.he(),e.j=null),e}function Wfe(e,n){return e.b=n.b,e.c=n.c,e.d=n.d,e.a=n.a,e}function v5n(e,n,t){return Te(e.a,(BQ(n,t),new ew(n,t))),e}function y5n(e,n){return u(T(e,(pe(),p6)),16).Ec(n),n}function k5n(e,n){return dn(e,u(T(n,(Ce(),rm)),15),n)}function E5n(e){return Cw(e)&&$e(Pe(ve(e,(Ce(),dg))))}function j5n(e,n,t){return pE(),AEn(u(Bn(e.e,n),516),t)}function S5n(e,n,t){e.i=0,e.e=0,n!=t&&_ze(e,n,t)}function A5n(e,n,t){e.i=0,e.e=0,n!=t&&Lze(e,n,t)}function qIe(e,n,t,i){this.b=e,this.c=i,C$.call(this,n,t)}function UIe(e,n){this.g=e,this.d=z(B(e1,1),Id,9,0,[n])}function XIe(e,n){e.d&&!e.d.a&&($Se(e.d,n),XIe(e.d,n))}function KIe(e,n){e.e&&!e.e.a&&($Se(e.e,n),KIe(e.e,n))}function VIe(e,n){return J3(e.j,n.s,n.c)+J3(n.e,e.s,e.c)}function M5n(e,n){return-vi(cs(e)*Hs(e),cs(n)*Hs(n))}function T5n(e){return u(e.jd(),147).Og()+":"+su(e.kd())}function YIe(){sZ(this,new xx),this.wb=(p0(),Rn),Wy()}function QIe(e){this.b=new r_,this.a=e,E.Math.random()}function ZIe(e){this.b=new xe,jr(this.b,this.b),this.a=e}function eae(e,n){new ji,this.a=new Ss,this.b=e,this.c=n}function WIe(){au.call(this,"There is no more element.")}function x5n(e){zP(),E.setTimeout(function(){throw e},0)}function C5n(e){e.Tg("No crossing minimization",1),e.Ug()}function O5n(e,n){return Gs(e),Gs(n),JAe(u(e,23),u(n,23))}function Lb(e,n,t){var i,r;i=zK(t),r=new f3(i),Nf(e,n,r)}function DV(e,n,t,i,r,c){eO.call(this,e,n,t,i,r,c?-2:-1)}function e_e(e,n,t,i){jse.call(this,n,t),this.b=e,this.a=i}function nae(e){this.b=e,this.c=e,e.e=null,e.c=null,this.a=1}function MR(e){return!e.a&&(e.a=new we($t,e,10,11)),e.a}function pi(e){return!e.q&&(e.q=new we(pf,e,11,10)),e.q}function ge(e){return!e.s&&(e.s=new we(es,e,21,17)),e.s}function n_e(e){return qE(e==null||hV(e)&&e.Rm!==nt),e}function TR(e,n){if(e==null)throw P(new K5(n));return e}function t_e(e,n){abn.call(this,new lV(e)),this.a=e,this.b=n}function IV(e,n){return n==null?!!Uc(e.f,null):W3n(e.i,n)}function _V(e){return q(e,18)?new tp(u(e,18)):m3n(e.Jc())}function xR(e){return vn(),q(e,59)?new TX(e):new $$(e)}function N5n(e){return Tt(e),VHe(new qn(Vn(e.a.Jc(),new ne)))}function D5n(e){return new UCe(e,e.e.Pd().gc()*e.c.Pd().gc())}function I5n(e){return new XCe(e,e.e.Pd().gc()*e.c.Pd().gc())}function tae(e){return e&&e.hashCode?e.hashCode():cw(e)}function _5n(e){e&&OR(e,e.ge())}function L5n(e,n){var t;return t=Hse(e.a,n),t&&(n.d=null),t}function i_e(e,n,t){return e.f?e.f.cf(n,t):!1}function RC(e,n,t,i){tr(e.c[n.g],t.g,i),tr(e.c[t.g],n.g,i)}function LV(e,n,t,i){tr(e.c[n.g],n.g,t),tr(e.b[n.g],n.g,i)}function P5n(e,n,t){return W(ie(t.a))<=e&&W(ie(t.b))>=n}function r_e(){this.d=new ji,this.b=new gt,this.c=new xe}function c_e(){this.b=new fr,this.d=new ji,this.e=new LP}function iae(){this.c=new Kr,this.d=new Kr,this.e=new Kr}function dw(){this.a=new Ss,this.b=(cl(3,$p),new So(3))}function u_e(e){this.c=e,this.b=new dd(u(Tt(new eh),51))}function o_e(e){this.c=e,this.b=new dd(u(Tt(new q7),51))}function s_e(e){this.b=e,this.a=new dd(u(Tt(new zm),51))}function vd(e,n){this.e=e,this.a=Mr,this.b=SXe(n),this.c=n}function CR(e){this.c=e.c,this.d=e.d,this.b=e.b,this.a=e.a}function l_e(e,n,t,i,r,c){this.a=e,TY.call(this,n,t,i,r,c)}function f_e(e,n,t,i,r,c){this.a=e,TY.call(this,n,t,i,r,c)}function v0(e,n,t,i,r,c,o){return new WV(e.e,n,t,i,r,c,o)}function $5n(e,n,t){return t>=0&&hn(e.substr(t,n.length),n)}function a_e(e,n){return q(n,147)&&hn(e.b,u(n,147).Og())}function R5n(e,n){return e.a?n.Dh().Jc():u(n.Dh(),72).Gi()}function h_e(e,n){var t;return t=e.b.Oc(n),cPe(t,e.b.gc()),t}function BC(e,n){if(e==null)throw P(new K5(n));return e}function tu(e){return e.u||(Ms(e),e.u=new zOe(e,e)),e.u}function Jo(e){var n;return n=u(Un(e,16),29),n||e.fi()}function OR(e,n){var t;return t=Sb(e.Pm),n==null?t:t+": "+n}function rf(e,n,t){return Yr(n,t,e.length),e.substr(n,t-n)}function d_e(e,n){J$.call(this),vhe(this),this.a=e,this.c=n}function b_e(){gK.call(this,"FIXED_INTEGER_RATIO_BOXES",2)}function B5n(){return WR(),z(B(uve,1),ye,422,0,[cve,Hte])}function z5n(){return sO(),z(B(gve,1),ye,419,0,[qH,bve])}function F5n(){return tO(),z(B(mve,1),ye,476,0,[pve,XH])}function H5n(){return zR(),z(B(Dve,1),ye,420,0,[lie,Nve])}function J5n(){return JR(),z(B(Uve,1),ye,423,0,[vie,mie])}function G5n(){return rO(),z(B(I4e,1),ye,421,0,[Qie,Zie])}function q5n(){return sj(),z(B(don,1),ye,518,0,[vA,mA])}function U5n(){return Aa(),z(B(Eon,1),ye,508,0,[mg,Ja])}function X5n(){return oh(),z(B(yon,1),ye,509,0,[i2,zd])}function K5n(){return ca(),z(B($on,1),ye,515,0,[hm,eb])}function V5n(){return gw(),z(B(Gon,1),ye,454,0,[nb,Nv])}function Y5n(){return BR(),z(B(x6e,1),ye,425,0,[yre,T6e])}function Q5n(){return EB(),z(B(C6e,1),ye,487,0,[$J,_v])}function Z5n(){return nB(),z(B(N6e,1),ye,426,0,[O6e,Mre])}function W5n(){return oB(),z(B(Ume,1),ye,424,0,[bte,gH])}function e4n(){return _9(),z(B(Wtn,1),ye,502,0,[VN,Tte])}function n4n(){return XR(),z(B(yye,1),ye,478,0,[Jre,vye])}function t4n(){return YC(),z(B(xye,1),ye,428,0,[Vre,KJ])}function i4n(){return gO(),z(B(Yye,1),ye,427,0,[YJ,Vye])}function NR(e,n,t,i){return t>=0?e.Rh(n,t,i):e.zh(null,t,i)}function zC(e){return e.b.b==0?e.a.uf():VK(e.b)}function r4n(e){if(e.p!=5)throw P(new ts);return _t(e.f)}function c4n(e){if(e.p!=5)throw P(new ts);return _t(e.k)}function rae(e){return ue(e.a)===ue((PY(),Lce))&&dPn(e),e.a}function g_e(e,n){eE(this,new ke(e.a,e.b)),a3(this,OC(n))}function bw(){hbn.call(this,new Z5(vp(12))),Xse(!0),this.a=2}function PV(e,n,t){si(),Qg.call(this,e),this.b=n,this.a=t}function cae(e,n,t){Ol(),OP.call(this,n),this.a=e,this.b=t}function u4n(e,n){var t=Yne[e.charCodeAt(0)];return t??e}function DR(e,n){return TR(e,"set1"),TR(n,"set2"),new rTe(e,n)}function IR(e,n){return iPe(n),T8n(e,oe(It,Zt,30,n,15,1),n)}function o4n(e,n){e.b=n,e.c>0&&e.b>0&&(e.g=cR(e.c,e.b,e.a))}function s4n(e,n){e.c=n,e.c>0&&e.b>0&&(e.g=cR(e.c,e.b,e.a))}function w_e(e){var n;n=e.c.d.b,e.b=n,e.a=e.c.d,n.a=e.c.d.b=e}function p_e(e){return e.b==0?null:(ft(e.b!=0),_l(e,e.a.a))}function so(e,n){return n==null?hu(Uc(e.f,null)):EE(e.i,n)}function m_e(e,n,t,i,r){return new fZ(e,(C9(),ute),n,t,i,r)}function $V(e,n,t,i){var r;r=new eNe,n.a[t.g]=r,w4(e.b,i,r)}function v_e(e,n){var t,i;return t=n,i=new hi,cVe(e,t,i),i.d}function l4n(e,n){var t;return t=k8n(e.f,n),bi(H$(t),e.f.d)}function FC(e){var n;L8n(e.a),yCe(e.a),n=new MP(e.a),Z1e(n)}function f4n(e,n){dXe(e,!0),Ao(e.e.Pf(),new Ble(e,!0,n))}function a4n(e,n){return N1(),u(T(n,(Au(),Th)),15).a==e}function sc(e){return Math.max(Math.min(e,ri),-2147483648)|0}function y_e(e){J$.call(this),vhe(this),this.a=e,this.c=!0}function uae(e,n,t){this.a=new xe,this.e=e,this.f=n,this.c=t}function _R(e,n,t){this.c=new xe,this.e=e,this.f=n,this.b=t}function k_e(e,n,t){this.i=new xe,this.b=e,this.g=n,this.a=t}function E_e(e){this.a=u(Tt(e),277),this.b=(vn(),new Zse(e))}function j9(){j9=Y;var e,n;n=!sjn(),e=new on,Qne=n?new ln:e}function oae(){oae=Y,gnn=new l1,pnn=new wfe,wnn=new Rm}function oh(){oh=Y,i2=new ase(W4,0),zd=new ase(Z4,1)}function Aa(){Aa=Y,mg=new hse(HW,0),Ja=new hse("UP",1)}function gw(){gw=Y,nb=new bse(Z4,0),Nv=new bse(W4,1)}function O3(e,n,t){LR(),e&&Qt(Dce,e,n),e&&Qt(YD,e,t)}function sae(e,n,t){var i;i=e.Fh(n),i>=0?e.$h(i,t):abe(e,n,t)}function j_e(e,n){var t;for(Tt(n),t=e.a;t;t=t.c)n.Wd(t.g,t.i)}function HC(e,n){var t;t=e.q.getHours(),e.q.setDate(n),Qj(e,t)}function S_e(e){var n;return n=new GP(vp(e.length)),l1e(n,e),n}function h4n(e){function n(){}return n.prototype=e||{},new n}function d4n(e,n){return lze(e,n)?(lBe(e),!0):!1}function A1(e,n){if(n==null)throw P(new J5);return djn(e,n)}function b4n(e){if(e.ye())return null;var n=e.n;return rH[n]}function sp(e){return e.Db>>16!=3?null:u(e.Cb,26)}function Ma(e){return e.Db>>16!=9?null:u(e.Cb,26)}function A_e(e){return e.Db>>16!=6?null:u(e.Cb,85)}function M_e(e,n){var t;return t=e.Fh(n),t>=0?e.Th(t):wZ(e,n)}function RV(e,n,t){var i;i=Cze(e,n,t),e.b=new SB(i.c.length)}function T_e(e){this.a=e,this.b=oe(fon,je,2005,e.e.length,0,2)}function x_e(){this.a=new Lh,this.e=new fr,this.g=0,this.i=0}function C_e(e,n){I$(this),this.f=n,this.g=e,AR(this),this.he()}function O_e(e,n){return e.b+=n.b,e.c+=n.c,e.d+=n.d,e.a+=n.a,e}function lae(e){var n;return n=e.d,n=e._i(e.f),kt(e,n),n.Ob()}function N_e(e,n){var t;return t=new hfe(n),oGe(t,e),new ds(t)}function g4n(e){if(e.p!=0)throw P(new ts);return _E(e.f,0)}function w4n(e){if(e.p!=0)throw P(new ts);return _E(e.k,0)}function D_e(e){return e.Db>>16!=7?null:u(e.Cb,241)}function fae(e){return e.Db>>16!=7?null:u(e.Cb,174)}function I_e(e){return e.Db>>16!=3?null:u(e.Cb,158)}function S9(e){return e.Db>>16!=6?null:u(e.Cb,241)}function zi(e){return e.Db>>16!=11?null:u(e.Cb,26)}function lp(e){return e.Db>>16!=17?null:u(e.Cb,29)}function ij(e,n,t,i,r,c){return new O1(e.e,n,e.Jj(),t,i,r,c)}function Xc(e,n,t){return n==null?Xo(e.f,null,t):Ew(e.i,n,t)}function BV(e,n){return E.Math.abs(e)0}function aae(e){var n;return O0(e),n=new fr,ci(e,new Lke(n))}function __e(e,n){var t=e.a=e.a||[];return t[n]||(t[n]=e.te(n))}function k4n(e,n){var t;t=e.q.getHours(),e.q.setMonth(n),Qj(e,t)}function lc(e,n){e.c&&Go(e.c.g,e),e.c=n,e.c&&Te(e.c.g,e)}function Cr(e,n){e.c&&Go(e.c.a,e),e.c=n,e.c&&Te(e.c.a,e)}function Jr(e,n){e.d&&Go(e.d.e,e),e.d=n,e.d&&Te(e.d.e,e)}function bu(e,n){e.i&&Go(e.i.j,e),e.i=n,e.i&&Te(e.i.j,e)}function L_e(e,n,t){this.a=n,this.c=e,this.b=(Tt(t),new ds(t))}function P_e(e,n,t){this.a=n,this.c=e,this.b=(Tt(t),new ds(t))}function $_e(e,n){this.a=e,this.c=wc(this.a),this.b=new CR(n)}function fp(e,n){if(e<0||e>n)throw P(new ko(Hge+e+Jge+n))}function R_e(){R_e=Y,Yun=Eo(new ur,(Br(),_c),(qr(),h6))}function hae(){hae=Y,Qun=Eo(new ur,(Br(),_c),(qr(),h6))}function B_e(){B_e=Y,qun=Eo(new ur,(Br(),_c),(qr(),h6))}function z_e(){z_e=Y,Uun=Eo(new ur,(Br(),_c),(qr(),h6))}function F_e(){F_e=Y,Xun=Eo(new ur,(Br(),_c),(qr(),h6))}function dae(){dae=Y,Kun=Eo(new ur,(Br(),_c),(qr(),h6))}function H_e(){H_e=Y,gon=Bt(new ur,(Br(),_c),(qr(),qS))}function nl(){nl=Y,mon=Bt(new ur,(Br(),_c),(qr(),qS))}function J_e(){J_e=Y,von=Bt(new ur,(Br(),_c),(qr(),qS))}function zV(){zV=Y,Son=Bt(new ur,(Br(),_c),(qr(),qS))}function G_e(){G_e=Y,vsn=Eo(new ur,(B4(),kA),(Vj(),Y4e))}function q_e(){q_e=Y,Pen=Ct(($P(),z(B(Len,1),ye,537,0,[Xne])))}function LR(){LR=Y,Dce=new gt,YD=new gt,_gn(nnn,new Ty)}function E4n(e,n){var t,i;t=n.c,i=t!=null,i&&p4(e,new up(n.c))}function U_e(e,n){Bvn(e,e.b,e.c),u(e.b.b,68),n&&u(n.b,68).b}function PR(e,n){q(e.Cb,184)&&(u(e.Cb,184).tb=null),Mo(e,n)}function FV(e,n){q(e.Cb,88)&&Cp(Ms(u(e.Cb,88)),4),Mo(e,n)}function j4n(e,n){H1e(e,n),q(e.Cb,88)&&Cp(Ms(u(e.Cb,88)),2)}function S4n(e,n){return vi(u(e.c,65).c.e.b,u(n.c,65).c.e.b)}function A4n(e,n){return vi(u(e.c,65).c.e.a,u(n.c,65).c.e.a)}function lo(e,n){return Tc(),yY(n)?new tR(n,e):new gC(n,e)}function HV(e,n){e.a&&Go(e.a.k,e),e.a=n,e.a&&Te(e.a.k,e)}function JV(e,n){e.b&&Go(e.b.f,e),e.b=n,e.b&&Te(e.b.f,e)}function y0(e,n,t){kFe(n,t,e.gc()),this.c=e,this.a=n,this.b=t-n}function y4(e){this.c=new ji,this.b=e.b,this.d=e.c,this.a=e.a}function GV(e){this.a=E.Math.cos(e),this.b=E.Math.sin(e)}function Pb(e,n,t,i){this.c=e,this.d=i,HV(this,n),JV(this,t)}function wn(e,n){this.b=(_n(e),e),this.a=(n&Rp)==0?n|64|wh:n}function M4n(e,n){FCe(e,_t($r(ow(n,24),tF)),_t($r(n,tF)))}function JC(e){return bh(),fo(e,0)>=0?N0(e):ZE(N0(Ed(e)))}function T4n(){return $l(),z(B(Yo,1),ye,130,0,[Bme,Vo,zme])}function X_e(e,n,t){return new fZ(e,(C9(),cte),null,!1,n,t)}function K_e(e,n,t){return new fZ(e,(C9(),ote),n,t,null,!1)}function V_e(e,n,t){var i;kFe(n,t,e.c.length),i=t-n,Loe(e.c,n,i)}function Y_e(e,n){var t;return t=u(kp(v4(e.a),n),18),t?t.gc():0}function bae(e){var n;return O0(e),n=(hw(),hw(),tte),lB(e,n)}function Q_e(e){for(var n;;)if(n=e.Pb(),!e.Ob())return n}function Z_e(e){var n,t;return t=(Wy(),n=new Zg,n),N9(t,e),t}function W_e(e){var n,t;return t=(Wy(),n=new Zg,n),N9(t,e),t}function N3(e){return pE(),q(e.g,9)?u(e.g,9):null}function x4n(){return yw(),z(B(Ite,1),ye,368,0,[Vw,V0,Kw])}function C4n(){return gB(),z(B(ave,1),ye,350,0,[fve,GH,Jte])}function O4n(){return Gb(),z(B(Oin,1),ye,449,0,[Qte,o7,vv])}function N4n(){return R9(),z(B(oie,1),ye,302,0,[cie,uie,eD])}function D4n(){return M0(),z(B(sie,1),ye,329,0,[nD,Ove,Wp])}function I4n(){return C1(),z(B(Bin,1),ye,315,0,[tD,kv,d6])}function _4n(){return jj(),z(B(j4e,1),ye,352,0,[Gie,E4e,EJ])}function L4n(){return Cc(),z(B(Run,1),ye,452,0,[gA,vs,No])}function P4n(){return OB(),z(B(P4e,1),ye,381,0,[_4e,Wie,L4e])}function $4n(){return Ej(),z(B($4e,1),ye,348,0,[nre,ere,gD])}function R4n(){return G9(),z(B(B4e,1),ye,349,0,[tre,R4e,wA])}function B4n(){return bB(),z(B(H4e,1),ye,351,0,[F4e,ire,z4e])}function z4n(){return NB(),z(B(J4e,1),ye,382,0,[rre,v7,am])}function F4n(){return fj(),z(B(o3e,1),ye,384,0,[mte,pte,vte])}function H4n(){return sa(),z(B(Up,1),ye,237,0,[xu,Oo,Cu])}function J4n(){return gs(),z(B(knn,1),ye,461,0,[Sh,X0,Bf])}function G4n(){return qo(),z(B(jnn,1),ye,462,0,[ba,K0,zf])}function q4n(){return kj(),z(B(u6e,1),ye,385,0,[c6e,ore,mD])}function U4n(){return kO(),z(B(_6e,1),ye,386,0,[RJ,D6e,I6e])}function X4n(){return qB(),z(B(tye,1),ye,387,0,[nye,Bre,eye])}function K4n(){return CB(),z(B(Z6e,1),ye,303,0,[Nre,Q6e,Y6e])}function V4n(){return YB(),z(B(W6e,1),ye,436,0,[MA,FJ,Dre])}function Y4n(){return zB(),z(B(Mye,1),ye,430,0,[Sye,Aye,qre])}function Q4n(){return MO(),z(B(Ure,1),ye,435,0,[qJ,UJ,XJ])}function Z4n(){return tB(),z(B(jye,1),ye,429,0,[Gre,Eye,kye])}function W4n(){return Oa(),z(B(X9e,1),ye,279,0,[T7,ym,x7])}function e6n(){return _1(),z(B(c8e,1),ye,347,0,[cG,Gd,FA])}function n6n(){return hj(),z(B(a8e,1),ye,300,0,[FD,jce,f8e])}function t6n(){return I4(),z(B(b8e,1),ye,281,0,[d8e,Em,aG])}function Ta(e){return wu(z(B(_r,1),je,8,0,[e.i.n,e.n,e.a]))}function i6n(e,n,t){var i;i=new gc(t.d),bi(i,e),J1e(n,i.a,i.b)}function eLe(e,n,t){var i;i=new aT,i.b=n,i.a=t,++n.b,Te(e.d,i)}function r6n(e,n,t){var i;return i=eS(e,n,!1),i.b<=n&&i.a<=t}function c6n(e){if(e.p!=2)throw P(new ts);return _t(e.f)&yr}function u6n(e){if(e.p!=2)throw P(new ts);return _t(e.k)&yr}function pn(e,n){if(e<0||e>=n)throw P(new ko(Hge+e+Jge+n))}function Yn(e,n){if(e<0||e>=n)throw P(new joe(Hge+e+Jge+n))}function o6n(e){return e.Db>>16!=6?null:u(vZ(e),241)}function nLe(e,n){var t,i;return i=w9(e,n),t=e.a.dd(i),new tTe(e,t)}function s6n(e,n){var t;return t=(_n(e),e).g,ule(!!t),_n(n),t(n)}function l6n(e){return e.a==(M9(),SG)&&Hx(e,BDn(e.g,e.b)),e.a}function k4(e){return e.d==(M9(),SG)&&Lue(e,$_n(e.g,e.b)),e.d}function gae(e,n){fbn.call(this,new Z5(vp(e))),cl(n,tYe),this.a=n}function tLe(e,n,t){Qg.call(this,25),this.b=e,this.a=n,this.c=t}function tl(e){si(),Qg.call(this,e),this.c=!1,this.a=!1}function iLe(e,n){Db.call(this,1,2,z(B(It,1),Zt,30,15,[e,n]))}function $r(e,n){return A0(svn(uu(e)?cf(e):e,uu(n)?cf(n):n))}function sh(e,n){return A0(lvn(uu(e)?cf(e):e,uu(n)?cf(n):n))}function qV(e,n){return A0(fvn(uu(e)?cf(e):e,uu(n)?cf(n):n))}function wae(e,n){return EDe(e.a,n)?ufe(e.b,u(n,23).g,null):null}function $b(e){return Tt(e),q(e,18)?new ds(u(e,18)):d9(e.Jc())}function UV(e){iR(),this.a=(vn(),q(e,59)?new TX(e):new $$(e))}function f6n(e){var n;return n=u(W$(e.b),10),new Nl(e.a,n,e.c)}function a6n(e,n){var t;t=W(ie(e.a.mf((Ht(),nG)))),xVe(e,n,t)}function h6n(e,n){return aj(),e.c==n.c?vi(n.d,e.d):vi(e.c,n.c)}function d6n(e,n){return aj(),e.c==n.c?vi(e.d,n.d):vi(e.c,n.c)}function b6n(e,n){return aj(),e.c==n.c?vi(e.d,n.d):vi(n.c,e.c)}function g6n(e,n){return aj(),e.c==n.c?vi(n.d,e.d):vi(n.c,e.c)}function w6n(e,n){e.b=e.b|n.b,e.c=e.c|n.c,e.d=e.d|n.d,e.a=e.a|n.a}function _(e){return ft(e.ai?1:0}function cLe(e,n){var t,i;return t=gY(n),i=t,u(Bn(e.c,i),15).a}function XV(e,n,t){var i;i=e.d[n.p],e.d[n.p]=e.d[t.p],e.d[t.p]=i}function y6n(e,n,t){var i;e.n&&n&&t&&(i=new dL,Te(e.e,i))}function KV(e,n){if(ar(e.a,n),n.d)throw P(new au(MYe));n.d=e}function vae(e,n){this.a=new xe,this.d=new xe,this.f=e,this.c=n}function uLe(){N4(),this.b=new gt,this.a=new gt,this.c=new xe}function oLe(){this.c=new MCe,this.a=new BPe,this.b=new nAe,mTe()}function sLe(e,n,t){this.d=e,this.j=n,this.e=t,this.o=-1,this.p=3}function lLe(e,n,t){this.d=e,this.k=n,this.f=t,this.o=-1,this.p=5}function fLe(e,n,t,i,r,c){yhe.call(this,e,n,t,i,r),c&&(this.o=-2)}function aLe(e,n,t,i,r,c){khe.call(this,e,n,t,i,r),c&&(this.o=-2)}function hLe(e,n,t,i,r,c){Lae.call(this,e,n,t,i,r),c&&(this.o=-2)}function dLe(e,n,t,i,r,c){She.call(this,e,n,t,i,r),c&&(this.o=-2)}function bLe(e,n,t,i,r,c){Pae.call(this,e,n,t,i,r),c&&(this.o=-2)}function gLe(e,n,t,i,r,c){Ehe.call(this,e,n,t,i,r),c&&(this.o=-2)}function wLe(e,n,t,i,r,c){jhe.call(this,e,n,t,i,r),c&&(this.o=-2)}function pLe(e,n,t,i,r,c){$ae.call(this,e,n,t,i,r),c&&(this.o=-2)}function mLe(e,n,t,i){OP.call(this,t),this.b=e,this.c=n,this.d=i}function vLe(e,n){this.f=e,this.a=(M9(),jG),this.c=jG,this.b=n}function yLe(e,n){this.g=e,this.d=(M9(),SG),this.a=SG,this.b=n}function yae(e,n){!e.c&&(e.c=new nr(e,0)),qz(e.c,(ki(),WA),n)}function k6n(e,n){return pOn(e,n,q(n,103)&&(u(n,19).Bb&kc)!=0)}function E6n(e,n){return KDe(_u(e.q.getTime()),_u(n.q.getTime()))}function kLe(e){return ZK(e.e.Pd().gc()*e.c.Pd().gc(),16,new P5(e))}function j6n(e){return!!e.u&&Xu(e.u.a).i!=0&&!(e.n&&LQ(e.n))}function S6n(e){return!!e.a&&xs(e.a.a).i!=0&&!(e.b&&PQ(e.b))}function kae(e,n){return n==0?!!e.o&&e.o.f!=0:CQ(e,n)}function ELe(e){return ft(e.b.b!=e.d.a),e.c=e.b=e.b.b,--e.a,e.c.c}function rj(e){for(;e.d>0&&e.a[--e.d]==0;);e.a[e.d++]==0&&(e.e=0)}function jLe(e){return e.a?e.e.length==0?e.a.a:e.a.a+(""+e.e):e.c}function Gr(e,n){this.a=e,Gc.call(this,e),fp(n,e.gc()),this.b=n}function SLe(e){this.a=oe(Mr,xn,1,c1e(E.Math.max(8,e))<<1,5,1)}function ALe(e){LY.call(this,e,(C9(),rte),null,!1,null,!1)}function MLe(e,n){var t;return t=1-n,e.a[t]=jB(e.a[t],t),jB(e,n)}function TLe(e,n){var t,i;return i=$r(e,Nc),t=Bh(n,32),sh(t,i)}function A6n(e,n,t){var i;return i=u(e.Zb().xc(n),18),!!i&&i.Gc(t)}function xLe(e,n,t){var i;return i=u(e.Zb().xc(n),18),!!i&&i.Kc(t)}function CLe(e,n,t){var i;i=(Tt(e),new ds(e)),cAn(new L_e(i,n,t))}function qC(e,n,t){var i;i=(Tt(e),new ds(e)),uAn(new P_e(i,n,t))}function M6n(e,n,t){e.a=n,e.c=t,e.b.a.$b(),Js(e.d),$2(e.e.a.c,0)}function OLe(e,n){var t;e.e=new goe,t=Np(n),xr(t,e.c),nXe(e,t,0)}function T6n(e,n){return new KK(n,yOe(wc(n.e),e,e),(Pn(),!0))}function x6n(e,n){return T4(),u(T(n,(Au(),Dv)),15).a>=e.gc()}function C6n(e){return nl(),!cc(e)&&!(!cc(e)&&e.c.i.c==e.d.i.c)}function lh(e){return u(Na(e,oe(e7,_8,17,e.c.length,0,1)),323)}function O6n(e){$Fe((!e.a&&(e.a=new we($t,e,10,11)),e.a),new OT)}function Eae(){var e,n,t;return n=(t=(e=new Zg,e),t),Te(Q8e,n),n}function ju(e,n,t,i,r,c){return JBe(e,n,t,c),_1e(e,i),L1e(e,r),e}function NLe(e,n,t,i){return e.a+=""+rf(n==null?Ko:su(n),t,i),e}function UC(e,n){if(e<0||e>=n)throw P(new ko(Jxn(e,n)));return e}function DLe(e,n,t){if(e<0||nt)throw P(new ko(fxn(e,n,t)))}function Ee(e,n,t,i){var r;r=new RT,r.a=n,r.b=t,r.c=i,qt(e.b,r)}function Gi(e,n,t,i){var r;r=new RT,r.a=n,r.b=t,r.c=i,qt(e.a,r)}function N6n(e,n,t){var i;i=Djn();try{return B2n(e,n,t)}finally{Kyn(i)}}function Bb(e){var n;return uu(e)?(n=e,n==-0?0:n):q9n(e)}function ILe(e,n){return q(n,45)?FQ(e.a,u(n,45)):!1}function _Le(e,n){return q(n,45)?FQ(e.a,u(n,45)):!1}function LLe(e,n){return q(n,45)?FQ(e.a,u(n,45)):!1}function D6n(e,n){return e.a<=e.b?(n.Bd(e.a++),!0):!1}function I6n(e){return C3(e).dc()?!1:(kwn(e,new de),!0)}function jae(e){var n;return m0(e),n=new ut,b3(e.a,new Dke(n)),n}function $R(e){var n;return m0(e),n=new st,b3(e.a,new Ike(n)),n}function _6n(e){if(!("stack"in e))try{throw e}catch{}return e}function RR(e){return new So((cl(e,fW),sB(pc(pc(5,e),e/10|0))))}function PLe(e){return u(Na(e,oe(Ytn,eQe,12,e.c.length,0,1)),2004)}function L6n(e){return ZK(e.e.Pd().gc()*e.c.Pd().gc(),273,new $U(e))}function $Le(){$Le=Y,xln=Ct((QP(),z(B(Tye,1),ye,477,0,[Xre])))}function RLe(){RLe=Y,Oln=Ct((ZP(),z(B(Cln,1),ye,546,0,[Kre])))}function BLe(){BLe=Y,Kln=Ct((mE(),z(B(Kye,1),ye,527,0,[MD])))}function zLe(){zLe=Y,q4e=vIe(me(1),me(4)),G4e=vIe(me(1),me(2))}function BR(){BR=Y,yre=new gse("DFS",0),T6e=new gse("BFS",1)}function zR(){zR=Y,lie=new ose(T8,0),Nve=new ose("TOP_LEFT",1)}function Sae(e,n,t){this.d=new XEe(this),this.e=e,this.i=n,this.f=t}function Aae(e,n,t,i){this.d=e,this.n=n,this.g=t,this.o=i,this.p=-1}function P6n(e,n,t){e.d&&Go(e.d.e,e),e.d=n,e.d&&xb(e.d.e,t,e)}function $6n(e,n,t){var i;return i=W9(t),Bz(e.n,i,n),Bz(e.o,n,t),n}function A9(e,n){var t,i;return t=bp(e,n),i=null,t&&(i=t.qe()),i}function cj(e,n){var t,i;return t=A1(e,n),i=null,t&&(i=t.qe()),i}function ww(e,n){var t,i;return t=A1(e,n),i=null,t&&(i=t.ne()),i}function M1(e,n){var t,i;return t=A1(e,n),i=null,t&&(i=j0e(t)),i}function uj(e,n){xRn(n,e),tfe(e.d),tfe(u(T(e,(Ce(),gJ)),213))}function VV(e,n){CRn(n,e),ife(e.d),ife(u(T(e,(Ce(),gJ)),213))}function k0(e,n){_n(n),e.b=e.b-1&e.a.length-1,tr(e.a,e.b,n),hJe(e)}function Mae(e,n){_n(n),tr(e.a,e.c,n),e.c=e.c+1&e.a.length-1,hJe(e)}function yt(e){return ft(e.b!=e.d.c),e.c=e.b,e.b=e.b.a,++e.a,e.c.c}function FLe(e){if(e.e.g!=e.b)throw P(new xl);return!!e.c&&e.d>0}function ap(e){return q(e,18)?u(e,18).dc():!e.Jc().Ob()}function R6n(e){return new wn(E8n(u(e.a.kd(),18).gc(),e.a.jd()),16)}function HLe(e){var n;n=e.Dh(),this.a=q(n,72)?u(n,72).Gi():n.Jc()}function Tae(e,n){var t;return t=u(Ca(e.b,n),66),!t&&(t=new ji),t}function B6n(e,n){var t;t=n.a,lc(t,n.c.d),Jr(t,n.d.d),pp(t.a,e.n)}function JLe(e,n,t,i){return q(t,59)?new hOe(e,n,t,i):new Efe(e,n,t,i)}function z6n(){return _f(),z(B(bin,1),ye,413,0,[Qp,t7,i7,Dte])}function F6n(){return kw(),z(B(Knn,1),ye,409,0,[qN,GN,hte,dte])}function H6n(){return B9(),z(B(Rtn,1),ye,408,0,[Xw,Kp,Xp,bv])}function J6n(){return C9(),z(B(aH,1),ye,309,0,[rte,cte,ute,ote])}function G6n(){return P4(),z(B(a3e,1),ye,383,0,[JS,f3e,Ste,Ate])}function q6n(){return IB(),z(B(iin,1),ye,367,0,[Nte,RH,BH,YN])}function U6n(){return Cj(),z(B(lve,1),ye,301,0,[XS,ove,ZN,sve])}function X6n(){return Ap(),z(B(Uie,1),ye,203,0,[jJ,qie,Ov,Cv])}function K6n(){return P1(),z(B(D4e,1),ye,269,0,[W0,N4e,Vie,Yie])}function V6n(){return Ub(),z(B(son,1),ye,404,0,[wD,pA,TJ,MJ])}function Y6n(e){var n;return e.j==(Oe(),dt)&&(n=Jqe(e),rs(n,Wn))}function Q6n(){return B4(),z(B(K4e,1),ye,398,0,[NJ,yA,kA,EA])}function GLe(e,n){return u(zs(ip(u(wi(e.k,n),16).Mc(),wv)),113)}function qLe(e,n){return u(zs(b4(u(wi(e.k,n),16).Mc(),wv)),113)}function Z6n(e,n){return c4(new ke(n.e.a+n.f.a/2,n.e.b+n.f.b/2),e)}function W6n(){return vz(),z(B(Ysn,1),ye,401,0,[Pre,Ire,Lre,_re])}function eyn(){return lz(),z(B(V6e,1),ye,354,0,[Ore,X6e,K6e,U6e])}function nyn(){return yj(),z(B(M6e,1),ye,353,0,[vre,PJ,mre,pre])}function tyn(){return U9(),z(B(U9e,1),ye,278,0,[LD,rG,G9e,q9e])}function iyn(){return L1(),z(B(kce,1),ye,222,0,[yce,PD,C7,I6])}function ryn(){return ol(),z(B(Jfn,1),ye,292,0,[RD,i1,rb,$D])}function cyn(){return GR(),z(B(UD,1),ye,288,0,[g8e,p8e,Ace,w8e])}function uyn(){return Xs(),z(B(UA,1),ye,380,0,[JD,Eg,HD,km])}function oyn(){return UB(),z(B(k8e,1),ye,326,0,[Mce,m8e,y8e,v8e])}function syn(){return _B(),z(B(oan,1),ye,407,0,[Tce,j8e,E8e,S8e])}function Dl(e,n,t){return n<0?wZ(e,t):u(t,69).uk().zk(e,e.ei(),n)}function lyn(e,n,t){var i;return i=W9(t),Bz(e.f,i,n),Qt(e.g,n,t),n}function fyn(e,n,t){var i;return i=W9(t),Bz(e.p,i,n),Qt(e.q,n,t),n}function ULe(e){var n,t;return n=(a0(),t=new c3,t),e&&Oz(n,e),n}function xae(e){var n;return n=e.$i(e.i),e.i>0&&Yu(e.g,0,n,0,e.i),n}function E4(e){return pE(),q(e.g,156)?u(e.g,156):null}function ayn(e){return LR(),oo(Dce,e)?u(Bn(Dce,e),342).Pg():null}function hyn(e){e.a=null,e.e=null,$2(e.b.c,0),$2(e.f.c,0),e.c=null}function XLe(e,n){var t;for(t=e.j.c.length;t>24}function byn(e){if(e.p!=1)throw P(new ts);return _t(e.k)<<24>>24}function gyn(e){if(e.p!=7)throw P(new ts);return _t(e.k)<<16>>16}function wyn(e){if(e.p!=7)throw P(new ts);return _t(e.f)<<16>>16}function D3(e,n){return n.e==0||e.e==0?RS:(h8(),jZ(e,n))}function YLe(e,n){return ue(n)===ue(e)?"(this Map)":n==null?Ko:su(n)}function pyn(e,n,t){return sV(ie(hu(Uc(e.f,n))),ie(hu(Uc(e.f,t))))}function myn(e,n,t){var i;i=u(Bn(e.g,t),60),Te(e.a.c,new yc(n,i))}function QLe(e,n){var t;return t=new Y5,e.Ed(t),t.a+="..",n.Fd(t),t.a}function ra(e){var n;for(n=0;e.Ob();)e.Pb(),n=pc(n,1);return sB(n)}function vyn(e,n,t,i,r){var c;c=NOn(r,t,i),Te(n,Lxn(r,c)),CTn(e,r,n)}function ZLe(e,n,t){e.i=0,e.e=0,n!=t&&(Lze(e,n,t),_ze(e,n,t))}function WLe(e,n,t,i){this.e=null,this.c=e,this.d=n,this.a=t,this.b=i}function Cae(e,n,t,i,r){this.i=e,this.a=n,this.e=t,this.j=i,this.f=r}function ePe(e,n){iae.call(this),this.a=e,this.b=n,Te(this.a.b,this)}function T1(e,n){bh(),Db.call(this,e,1,z(B(It,1),Zt,30,15,[n]))}function yyn(e,n,t){return g8(e,n,t,q(n,103)&&(u(n,19).Bb&kc)!=0)}function FR(e,n,t){return Fz(e,n,t,q(n,103)&&(u(n,19).Bb&kc)!=0)}function kyn(e,n,t){return jOn(e,n,t,q(n,103)&&(u(n,19).Bb&kc)!=0)}function Oae(e,n){return e==(zn(),Qi)&&n==Qi?4:e==Qi||n==Qi?8:32}function Eyn(e,n){return u(n==null?hu(Uc(e.f,null)):EE(e.i,n),290)}function nPe(e,n){var t;for(t=n;t;)Q2(e,t.i,t.j),t=zi(t);return e}function Xu(e){return e.n||(Ms(e),e.n=new xDe(e,Pc,e),tu(e)),e.n}function Hh(e,n){Tc();var t;return t=u(e,69).tk(),HTn(t,n),t.vl(n)}function oj(e){return ft(e.a"+pae(e.d):"e_"+cw(e)}function Syn(e,n){var t;return t=n!=null?so(e,n):hu(Uc(e.f,n)),x$(t)}function Ayn(e,n){var t;return t=n!=null?so(e,n):hu(Uc(e.f,n)),x$(t)}function cPe(e,n){var t;for(t=0;t=0&&e.a[t]===n[t];t--);return t<0}function Oyn(e,n){var t,i;i=!1;do t=jze(e,n),i=i|t;while(t);return i}function sj(){sj=Y,vA=new fse("UPPER",0),mA=new fse("LOWER",1)}function JR(){JR=Y,vie=new sse(aa,0),mie=new sse("ALTERNATING",1)}function GR(){GR=Y,g8e=new uDe,p8e=new HDe,Ace=new b_e,w8e=new JDe}function oPe(){oPe=Y,Ain=Ct((WR(),z(B(uve,1),ye,422,0,[cve,Hte])))}function sPe(){sPe=Y,Cin=Ct((sO(),z(B(gve,1),ye,419,0,[qH,bve])))}function lPe(){lPe=Y,Iin=Ct((tO(),z(B(mve,1),ye,476,0,[pve,XH])))}function fPe(){fPe=Y,Fin=Ct((zR(),z(B(Dve,1),ye,420,0,[lie,Nve])))}function aPe(){aPe=Y,Gin=Ct((JR(),z(B(Uve,1),ye,423,0,[vie,mie])))}function hPe(){hPe=Y,$un=Ct((rO(),z(B(I4e,1),ye,421,0,[Qie,Zie])))}function dPe(){dPe=Y,bon=Ct((sj(),z(B(don,1),ye,518,0,[vA,mA])))}function bPe(){bPe=Y,jon=Ct((Aa(),z(B(Eon,1),ye,508,0,[mg,Ja])))}function gPe(){gPe=Y,kon=Ct((oh(),z(B(yon,1),ye,509,0,[i2,zd])))}function wPe(){wPe=Y,Ron=Ct((ca(),z(B($on,1),ye,515,0,[hm,eb])))}function pPe(){pPe=Y,qon=Ct((gw(),z(B(Gon,1),ye,454,0,[nb,Nv])))}function mPe(){mPe=Y,msn=Ct((BR(),z(B(x6e,1),ye,425,0,[yre,T6e])))}function vPe(){vPe=Y,jsn=Ct((EB(),z(B(C6e,1),ye,487,0,[$J,_v])))}function yPe(){yPe=Y,Msn=Ct((nB(),z(B(N6e,1),ye,426,0,[O6e,Mre])))}function kPe(){kPe=Y,Eln=Ct((XR(),z(B(yye,1),ye,478,0,[Jre,vye])))}function EPe(){EPe=Y,Nln=Ct((YC(),z(B(xye,1),ye,428,0,[Vre,KJ])))}function jPe(){jPe=Y,Vln=Ct((gO(),z(B(Yye,1),ye,427,0,[YJ,Vye])))}function SPe(){SPe=Y,rtn=Ct((oB(),z(B(Ume,1),ye,424,0,[bte,gH])))}function APe(){APe=Y,ein=Ct((_9(),z(B(Wtn,1),ye,502,0,[VN,Tte])))}function qR(e){o0e(),FCe(this,_t($r(ow(e,24),tF)),_t($r(e,tF)))}function Nyn(e){return(e.k==(zn(),Qi)||e.k==gr)&&di(e,(pe(),QS))}function Dyn(e,n,t){return u(n==null?Xo(e.f,null,t):Ew(e.i,n,t),290)}function Iyn(){return vr(),z(B(BA,1),ye,86,0,[Xa,ru,Zc,Ua,Ul])}function _yn(){return Oe(),z(B(jc,1),Ju,64,0,[yu,Xn,Wn,dt,Kn])}function Lyn(e){return zP(),function(){return N6n(e,this,arguments)}}function MPe(e,n){var t;return t=n.jd(),new ew(t,e.e.pc(t,u(n.kd(),18)))}function TPe(e,n){var t,i;return t=n.jd(),i=e.De(t),!!i&&Uu(i.e,n.kd())}function rc(e,n){var t,i;for(_n(n),i=e.Jc();i.Ob();)t=i.Pb(),n.Ad(t)}function il(e,n,t){var i;return i=(pn(n,e.c.length),e.c[n]),e.c[n]=t,i}function _ae(e,n){var t,i;for(t=n,i=0;t>0;)i+=e.a[t],t-=t&-t;return i}function xPe(e,n){var t;for(t=n;t;)Q2(e,-t.i,-t.j),t=zi(t);return e}function Pyn(e,n){var t;return t=e.a.get(n),t??oe(Mr,xn,1,0,5,1)}function I3(e,n){return(O0(e),e9(new bn(e,new ohe(n,e.a)))).zd(s6)}function $yn(){return Br(),z(B(s3e,1),ye,363,0,[Ff,Wh,Zu,Wu,_c])}function CPe(e){qVe(),BSe(this),this.a=new ji,w1e(this,e),qt(this.a,e)}function OPe(){EK(this),this.b=new ke(Ki,Ki),this.a=new ke(Nr,Nr)}function nY(e){UR(),!Fa&&(this.c=e,this.e=!0,this.a=new xe)}function UR(){UR=Y,Fa=!0,snn=!1,lnn=!1,ann=!1,fnn=!1}function XR(){XR=Y,Jre=new mse(cwe,0),vye=new mse("TARGET_WIDTH",1)}function Ryn(){return pz(),z(B(Ssn,1),ye,364,0,[Sre,kre,Are,Ere,jre])}function Byn(){return Mp(),z(B(win,1),ye,371,0,[QN,HH,JH,FH,zH])}function zyn(){return Ij(),z(B(A4e,1),ye,328,0,[S4e,Xie,Kie,hA,dA])}function Fyn(){return qs(),z(B(qve,1),ye,165,0,[uD,eA,G1,nA,hg])}function Hyn(){return qj(),z(B(Aln,1),ye,369,0,[Lv,M6,DA,NA,AD])}function Jyn(){return zO(),z(B(Dye,1),ye,330,0,[Cye,Yre,Nye,Qre,Oye])}function Gyn(){return dh(),z(B(Ga,1),ye,160,0,[An,lr,pa,Hd,U1])}function qyn(){return G3(),z(B(JA,1),ye,257,0,[cb,BD,u8e,HA,o8e])}function tY(e,n){var t;return t=u(Ca(e.d,n),21),t||u(Ca(e.e,n),21)}function NPe(e){this.b=e,ct.call(this,e),this.a=u(Un(this.b.a,4),129)}function DPe(e){this.b=e,o4.call(this,e),this.a=u(Un(this.b.a,4),129)}function IPe(e,n){this.c=0,this.b=n,Yxe.call(this,e,17493),this.a=this.c}function Cf(e,n,t,i,r){zPe.call(this,n,i,r),this.c=e,this.b=t}function Lae(e,n,t,i,r){sLe.call(this,n,i,r),this.c=e,this.a=t}function Pae(e,n,t,i,r){lLe.call(this,n,i,r),this.c=e,this.a=t}function $ae(e,n,t,i,r){zPe.call(this,n,i,r),this.c=e,this.a=t}function Rae(e,n,t){e.a.c.length=0,mPn(e,n,t),e.a.c.length==0||GIn(e,n)}function XC(e){e.i=0,tC(e.b,null),tC(e.c,null),e.a=null,e.e=null,++e.g}function Uyn(e){return e.e=3,e.d=e.Yb(),e.e!=2?(e.e=0,!0):!1}function Bae(e,n){return q(n,144)?hn(e.c,u(n,144).c):!1}function _Pe(e){var n;return e.c||(n=e.r,q(n,88)&&(e.c=u(n,29))),e.c}function Ms(e){return e.t||(e.t=new xSe(e),Tj(new UAe(e),0,e.t)),e.t}function cc(e){return!e.c||!e.d?!1:!!e.c.i&&e.c.i==e.d.i}function j4(e,n){return n==0||e.e==0?e:n>0?nHe(e,n):HUe(e,-n)}function zae(e,n){return n==0||e.e==0?e:n>0?HUe(e,n):nHe(e,-n)}function tt(e){if(at(e))return e.c=e.a,e.a.Pb();throw P(new fu)}function LPe(e){var n;return n=e.length,hn($n.substr($n.length-n,n),e)}function PPe(e){var n,t;return n=e.c.i,t=e.d.i,n.k==(zn(),gr)&&t.k==gr}function iY(e){var n,t,i;return n=e&_s,t=e>>22&_s,i=e<0?B1:0,Io(n,t,i)}function Xyn(e,n){var t,i;t=u(Fkn(e.c,n),18),t&&(i=t.gc(),t.$b(),e.d-=i)}function Kyn(e){e&&Y9n((voe(),ame)),--cH,e&&uH!=-1&&(Ign(uH),uH=-1)}function Fae(e){Agn.call(this,e==null?Ko:su(e),q(e,80)?u(e,80):null)}function rY(e){var n;return n=new dw,Lu(n,e),fe(n,(Ce(),Qc),null),n}function cY(e,n,t){var i;return i=e.Fh(n),i>=0?e.Ih(i,t,!0):Ow(e,n,t)}function Vyn(e,n,t){return vi(c4(e8(e),wc(n.b)),c4(e8(e),wc(t.b)))}function Yyn(e,n,t){return vi(c4(e8(e),wc(n.e)),c4(e8(e),wc(t.e)))}function Qyn(e,n){return E.Math.min(j0(n.a,e.d.d.c),j0(n.b,e.d.d.c))}function $Pe(e,n,t){var i;i=new zse(e.a),gj(i,e.a.a),Xo(i.f,n,t),e.a.a=i}function Hae(e,n,t,i){var r;for(r=0;rn)throw P(new ko(D0e(e,n,"index")));return e}function qae(e){var n;return n=e.e+e.f,isNaN(n)&&q$(e.d)?e.d:n}function Wyn(e,n){var t;t=e.q.getHours()+(n/60|0),e.q.setMinutes(n),Qj(e,t)}function Uae(e,n){var t,i;return t=(_n(e),e),i=(_n(n),n),t==i?0:tn.p?-1:0}function UPe(e,n){return oo(e.a,n)?(S4(e.a,n),!0):!1}function i9n(e){var n,t;return n=e.jd(),t=u(e.kd(),18),CC(t.Lc(),new By(n))}function oY(e){var n;return n=e.b,n.b==0?null:u(Ku(n,0),65).b}function YR(e,n){return _n(n),e.c=0,"Initial capacity must not be negative")}function ZR(){ZR=Y,IA=new mi("org.eclipse.elk.labels.labelManager")}function KPe(){KPe=Y,eve=new Li("separateLayerConnections",(IB(),Nte))}function ca(){ca=Y,hm=new dse("REGULAR",0),eb=new dse("CRITICAL",1)}function YC(){YC=Y,Vre=new vse("FIXED",0),KJ=new vse("CENTER_NODE",1)}function WR(){WR=Y,cve=new rse("QUADRATIC",0),Hte=new rse("SCANLINE",1)}function VPe(){VPe=Y,Tin=Ct((gB(),z(B(ave,1),ye,350,0,[fve,GH,Jte])))}function YPe(){YPe=Y,Nin=Ct((Gb(),z(B(Oin,1),ye,449,0,[Qte,o7,vv])))}function QPe(){QPe=Y,$in=Ct((R9(),z(B(oie,1),ye,302,0,[cie,uie,eD])))}function ZPe(){ZPe=Y,Rin=Ct((M0(),z(B(sie,1),ye,329,0,[nD,Ove,Wp])))}function WPe(){WPe=Y,zin=Ct((C1(),z(B(Bin,1),ye,315,0,[tD,kv,d6])))}function e$e(){e$e=Y,Ein=Ct((yw(),z(B(Ite,1),ye,368,0,[Vw,V0,Kw])))}function n$e(){n$e=Y,Dun=Ct((jj(),z(B(j4e,1),ye,352,0,[Gie,E4e,EJ])))}function t$e(){t$e=Y,Bun=Ct((Cc(),z(B(Run,1),ye,452,0,[gA,vs,No])))}function i$e(){i$e=Y,zun=Ct((OB(),z(B(P4e,1),ye,381,0,[_4e,Wie,L4e])))}function r$e(){r$e=Y,Fun=Ct((Ej(),z(B($4e,1),ye,348,0,[nre,ere,gD])))}function c$e(){c$e=Y,Hun=Ct((G9(),z(B(B4e,1),ye,349,0,[tre,R4e,wA])))}function u$e(){u$e=Y,Jun=Ct((bB(),z(B(H4e,1),ye,351,0,[F4e,ire,z4e])))}function o$e(){o$e=Y,Gun=Ct((NB(),z(B(J4e,1),ye,382,0,[rre,v7,am])))}function s$e(){s$e=Y,Kon=Ct((kj(),z(B(u6e,1),ye,385,0,[c6e,ore,mD])))}function l$e(){l$e=Y,Tsn=Ct((kO(),z(B(_6e,1),ye,386,0,[RJ,D6e,I6e])))}function f$e(){f$e=Y,Ksn=Ct((CB(),z(B(Z6e,1),ye,303,0,[Nre,Q6e,Y6e])))}function a$e(){a$e=Y,Vsn=Ct((YB(),z(B(W6e,1),ye,436,0,[MA,FJ,Dre])))}function h$e(){h$e=Y,jln=Ct((tB(),z(B(jye,1),ye,429,0,[Gre,Eye,kye])))}function d$e(){d$e=Y,Sln=Ct((zB(),z(B(Mye,1),ye,430,0,[Sye,Aye,qre])))}function b$e(){b$e=Y,Tln=Ct((MO(),z(B(Ure,1),ye,435,0,[qJ,UJ,XJ])))}function g$e(){g$e=Y,eln=Ct((qB(),z(B(tye,1),ye,387,0,[nye,Bre,eye])))}function w$e(){w$e=Y,Otn=Ct((fj(),z(B(o3e,1),ye,384,0,[mte,pte,vte])))}function p$e(){p$e=Y,hnn=Ct(($l(),z(B(Yo,1),ye,130,0,[Bme,Vo,zme])))}function m$e(){m$e=Y,ynn=Ct((sa(),z(B(Up,1),ye,237,0,[xu,Oo,Cu])))}function v$e(){v$e=Y,Enn=Ct((gs(),z(B(knn,1),ye,461,0,[Sh,X0,Bf])))}function y$e(){y$e=Y,Snn=Ct((qo(),z(B(jnn,1),ye,462,0,[ba,K0,zf])))}function k$e(){k$e=Y,Rfn=Ct((Oa(),z(B(X9e,1),ye,279,0,[T7,ym,x7])))}function E$e(){E$e=Y,ian=Ct((I4(),z(B(b8e,1),ye,281,0,[d8e,Em,aG])))}function j$e(){j$e=Y,Hfn=Ct((_1(),z(B(c8e,1),ye,347,0,[cG,Gd,FA])))}function S$e(){S$e=Y,ean=Ct((hj(),z(B(a8e,1),ye,300,0,[FD,jce,f8e])))}function ua(e,n){return!e.o&&(e.o=new us((Hu(),u1),qd,e,0)),vQ(e.o,n)}function c9n(e){return!e.g&&(e.g=new xy),!e.g.d&&(e.g.d=new ASe(e)),e.g.d}function u9n(e){return!e.g&&(e.g=new xy),!e.g.b&&(e.g.b=new SSe(e)),e.g.b}function QC(e){return!e.g&&(e.g=new xy),!e.g.c&&(e.g.c=new TSe(e)),e.g.c}function o9n(e){return!e.g&&(e.g=new xy),!e.g.a&&(e.g.a=new MSe(e)),e.g.a}function s9n(e,n,t,i){return t&&(i=t.Oh(n,Fi(t.Ah(),e.c.sk()),null,i)),i}function l9n(e,n,t,i){return t&&(i=t.Qh(n,Fi(t.Ah(),e.c.sk()),null,i)),i}function sY(e,n,t,i){var r;return r=oe(It,Zt,30,n+1,15,1),A_n(r,e,n,t,i),r}function oe(e,n,t,i,r,c){var o;return o=iJe(r,i),r!=10&&z(B(e,c),n,t,r,o),o}function f9n(e,n,t){var i,r;for(r=new P9(n,e),i=0;it||n=0?e.Ih(t,!0,!0):Ow(e,n,!0)}function ZC(e,n){var t,i,r;return r=e.r,i=e.d,t=eS(e,n,!0),t.b!=r||t.a!=i}function x$e(e,n){return TTe(e.e,n)||Kb(e.e,n,new THe(n)),u(Ca(e.e,n),113)}function Ts(e,n,t,i){return _n(e),_n(n),_n(t),_n(i),new Ofe(e,n,new Du)}function WC(e,n,t){var i,r;return r=(i=l8(e.b,n),i),r?Uz(cO(e,r),t):null}function M9n(e,n,t){var i,r,c;i=A1(e,t),r=null,i&&(r=j0e(i)),c=r,SHe(n,t,c)}function T9n(e,n,t){var i,r,c;i=A1(e,t),r=null,i&&(r=j0e(i)),c=r,SHe(n,t,c)}function us(e,n,t,i){this.$j(),this.a=n,this.b=e,this.c=new Mfe(this,n,t,i)}function hY(e,n,t,i,r,c){Aae.call(this,n,i,r,c),this.c=e,this.b=t}function eO(e,n,t,i,r,c){Aae.call(this,n,i,r,c),this.c=e,this.a=t}function uhe(e,n,t,i,r){NCe(this),this.b=e,this.d=n,this.f=t,this.g=i,this.c=r}function ohe(e,n){C$.call(this,n.xd(),n.wd()&-16449),_n(e),this.a=e,this.c=n}function x9n(e,n){e.a.Le(n.d,e.b)>0&&(Te(e.c,new Zle(n.c,n.d,e.d)),e.b=n.d)}function dY(e){e.a=oe(It,Zt,30,e.b+1,15,1),e.c=oe(It,Zt,30,e.b,15,1),e.d=0}function C9n(e,n,t){var i;return i=Cze(e,n,t),e.b=new SB(i.c.length),jbe(e,i)}function O9n(e){if(e.b<=0)throw P(new fu);return--e.b,e.a-=e.c.c,me(e.a)}function N9n(e){var n;if(!e.a)throw P(new WIe);return n=e.a,e.a=zi(e.a),n}function C$e(e){var n;if(e.ll())for(n=e.i-1;n>=0;--n)X(e,n);return xae(e)}function M4(e){var n;return Tt(e),q(e,204)?(n=u(e,204),n):new zy(e)}function D9n(e){for(;!e.a;)if(!bNe(e.c,new _ke(e)))return!1;return!0}function she(e,n){if(e.g==null||n>=e.i)throw P(new pK(n,e.i));return e.g[n]}function O$e(e,n,t){if(H9(e,t),t!=null&&!e.dk(t))throw P(new lX);return t}function bY(e,n){return oO(n)!=10&&z(Gs(n),n.Qm,n.__elementTypeId$,oO(n),e),e}function N$e(e,n){var t,i;return i=n/e.c.Pd().gc()|0,t=n%e.c.Pd().gc(),A4(e,i,t)}function x9(e,n,t,i){var r;i=(hw(),i||Dme),r=e.slice(n,t),I0e(r,e,n,t,-n,i)}function Il(e,n,t,i,r){return n<0?Ow(e,t,i):u(t,69).uk().wk(e,e.ei(),n,i,r)}function I9n(e,n){return vi(W(ie(T(e,(pe(),Zw)))),W(ie(T(n,Zw))))}function D$e(){D$e=Y,unn=Ct((C9(),z(B(aH,1),ye,309,0,[rte,cte,ute,ote])))}function C9(){C9=Y,rte=new i$("All",0),cte=new pCe,ute=new CCe,ote=new mCe}function gs(){gs=Y,Sh=new zX(Z4,0),X0=new zX(T8,1),Bf=new zX(W4,2)}function I$e(){I$e=Y,Hz(),c7e=Ki,fhn=Nr,u7e=new Zn(Ki),ahn=new Zn(Nr)}function eB(){eB=Y,Zln=new r3,efn=new Z_,Wln=X7n((Ht(),pce),Zln,ib,efn)}function _9n(e){eB(),u(e.mf((Ht(),pm)),182).Ec((ws(),zD)),e.of(pce,null)}function L9n(e){return q(e,180)?""+u(e,180).a:e==null?null:su(e)}function P9n(e){return q(e,180)?""+u(e,180).a:e==null?null:su(e)}function lhe(e){var n,t;if(!e.b)return null;for(t=e.b;n=t.a[0];)t=n;return t}function _$e(e){var n,t;if(!e.b)return null;for(t=e.b;n=t.a[1];)t=n;return t}function nO(e){var n;for(n=e.p+1;n=0?rz(e,t,!0,!0):Ow(e,n,!0)}function H9n(e,n){f4(u(u(e.f,26).mf((Ht(),RA)),102))&&$Fe(Kfe(u(e.f,26)),n)}function sRe(e,n){Cs(e,n==null||q$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function lRe(e,n){Os(e,n==null||q$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function fRe(e,n){vw(e,n==null||q$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function aRe(e,n){mw(e,n==null||q$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function hRe(e){(this.q?this.q:(vn(),vn(),Zh)).zc(e.q?e.q:(vn(),vn(),Zh))}function vY(e,n,t){var i;return i=e.g[n],zE(e,n,e.Xi(n,t)),e.Pi(n,t,i),e.Li(),i}function uB(e,n){var t;return t=e.bd(n),t>=0?(e.ed(t),!0):!1}function yY(e){var n;return e.d!=e.r&&(n=of(e),e.e=!!n&&n.jk()==GWe,e.d=n),e.e}function kY(e,n){var t;for(Tt(e),Tt(n),t=!1;n.Ob();)t=t|e.Ec(n.Pb());return t}function Ca(e,n){var t;return t=u(Bn(e.e,n),393),t?(zCe(e,t),t.e):null}function dRe(e){var n,t;return n=e/60|0,t=e%60,t==0?""+n:""+n+":"+(""+t)}function ou(e,n){var t,i;return O0(e),i=new Xae(n,e.a),t=new hNe(i),new bn(e,t)}function bp(e,n){var t=e.a[n],i=(UY(),Zne)[typeof t];return i?i(t):D1e(typeof t)}function J9n(e,n){var t,i,r;r=n.c.i,t=u(Bn(e.f,r),60),i=t.d.c-t.e.c,Ghe(n.a,i,0)}function Jh(e,n,t){var i,r;for(i=10,r=0;r=0;)++n[0]}function mRe(e,n,t,i){si(),Qg.call(this,26),this.c=e,this.a=n,this.d=t,this.b=i}function O1(e,n,t,i,r,c,o){TY.call(this,n,i,r,c,o),this.c=e,this.b=t}function vRe(e){this.g=e,this.f=new xe,this.a=E.Math.min(this.g.c.c,this.g.d.c)}function aj(){aj=Y,Htn=new a2,Jtn=new h2,ztn=new d2,Ftn=new b2,Gtn=new El}function oB(){oB=Y,bte=new nse("EADES",0),gH=new nse("FRUCHTERMAN_REINGOLD",1)}function sO(){sO=Y,qH=new cse("READING_DIRECTION",0),bve=new cse("ROTATION",1)}function yRe(){yRe=Y,pin=Ct((Mp(),z(B(win,1),ye,371,0,[QN,HH,JH,FH,zH])))}function kRe(){kRe=Y,_un=Ct((Ij(),z(B(A4e,1),ye,328,0,[S4e,Xie,Kie,hA,dA])))}function ERe(){ERe=Y,Jin=Ct((qs(),z(B(qve,1),ye,165,0,[uD,eA,G1,nA,hg])))}function jRe(){jRe=Y,Asn=Ct((pz(),z(B(Ssn,1),ye,364,0,[Sre,kre,Are,Ere,jre])))}function SRe(){SRe=Y,Mln=Ct((qj(),z(B(Aln,1),ye,369,0,[Lv,M6,DA,NA,AD])))}function ARe(){ARe=Y,Dln=Ct((zO(),z(B(Dye,1),ye,330,0,[Cye,Yre,Nye,Qre,Oye])))}function MRe(){MRe=Y,_tn=Ct((Br(),z(B(s3e,1),ye,363,0,[Ff,Wh,Zu,Wu,_c])))}function TRe(){TRe=Y,Pfn=Ct((vr(),z(B(BA,1),ye,86,0,[Xa,ru,Zc,Ua,Ul])))}function xRe(){xRe=Y,nfn=Ct((dh(),z(B(Ga,1),ye,160,0,[An,lr,pa,Hd,U1])))}function CRe(){CRe=Y,Ufn=Ct((G3(),z(B(JA,1),ye,257,0,[cb,BD,u8e,HA,o8e])))}function ORe(){ORe=Y,Vfn=Ct((Oe(),z(B(jc,1),Ju,64,0,[yu,Xn,Wn,dt,Kn])))}function NRe(e){var n;return n=u(T(e,(pe(),Yw)),317),n?n.a==e:!1}function DRe(e){var n;return n=u(T(e,(pe(),Yw)),317),n?n.i==e:!1}function IRe(e,n){return _n(n),jfe(e),e.d.Ob()?(n.Ad(e.d.Pb()),!0):!1}function sB(e){return fo(e,ri)>0?ri:fo(e,Ur)<0?Ur:_t(e)}function Z9n(e,n){var t;return t=jw(e.e.c,n.e.c),t==0?vi(e.e.d,n.e.d):t}function jY(e,n){var t;return t=u(Bn(e.a,n),150),t||(t=new Hm,Qt(e.a,n,t)),t}function Nf(e,n,t){var i;if(n==null)throw P(new J5);return i=A1(e,n),jyn(e,n,t),i}function W9n(e,n){var t,i;for(i=n.c,t=i+1;t<=n.f;t++)e.a[t]>e.a[i]&&(i=t);return i}function e8n(e,n,t){var i;return i=e.a.e[u(n.a,9).p]-e.a.e[u(t.a,9).p],sc(IC(i))}function n8n(e,n,t){var i,r;for(r=new L(t);r.a0?n-1:n,oMe(Qbn(tBe(rfe(new U5,t),e.n),e.j),e.k)}function s8n(e,n,t,i){var r;e.j=-1,U0e(e,S0e(e,n,t),(Tc(),r=u(n,69).tk(),r.vl(i)))}function RRe(e,n,t,i,r,c){var o;o=rY(i),lc(o,r),Jr(o,c),dn(e.a,i,new K$(o,n,t.f))}function lB(e,n){var t;return O0(e),t=new qIe(e,e.a.xd(),e.a.wd()|4,n),new bn(e,t)}function l8n(e,n){var t,i;return t=u(kp(e.d,n),18),t?(i=n,e.e.pc(i,t)):null}function Sn(e,n){var t;return t=(e.i==null&&gh(e),e.i),n>=0&&n=-.01&&e.a<=$a&&(e.a=0),e.b>=-.01&&e.b<=$a&&(e.b=0),e}function _3(e){a8();var n,t;for(t=D2e,n=0;nt&&(t=e[n]);return t}function f8n(e){var n;return n=W(ie(T(e,(Ce(),Rd)))),n<0&&(n=0,fe(e,Rd,n)),n}function a8n(e,n){f4(u(T(u(e.e,9),(Ce(),Zi)),102))&&(vn(),xr(u(e.e,9).j,n))}function fB(e,n){var t,i;for(i=e.Jc();i.Ob();)t=u(i.Pb(),70),fe(t,(pe(),m6),n)}function h8n(e,n){var t,i,r;for(i=n.a.jd(),t=u(n.a.kd(),18).gc(),r=0;re||e>n)throw P(new Soe("fromIndex: 0, toIndex: "+e+Rge+n))}function HRe(e,n){yi(e,(qh(),Rre),n.f),yi(e,Wsn,n.e),yi(e,$re,n.d),yi(e,Zsn,n.c)}function Ao(e,n){var t,i,r,c;for(_n(n),i=e.c,r=0,c=i.length;r0&&(e.a/=n,e.b/=n),e}function JRe(e,n,t){var i,r;i=n;do r=W(e.p[i.p])+t,e.p[i.p]=r,i=e.a[i.p];while(i!=n)}function rl(e){var n;return e.w?e.w:(n=o6n(e),n&&!n.Sh()&&(e.w=n),n)}function phe(e,n){return ja(),Df(B0),E.Math.abs(e-n)<=B0||e==n||isNaN(e)&&isNaN(n)}function y8n(e){var n;return e==null?null:(n=u(e,195),sTn(n,n.length))}function X(e,n){if(e.g==null||n>=e.i)throw P(new pK(n,e.i));return e.Ui(n,e.g[n])}function sa(){sa=Y,xu=new BX("BEGIN",0),Oo=new BX(T8,1),Cu=new BX("END",2)}function Oa(){Oa=Y,T7=new aK(T8,0),ym=new aK("HEAD",1),x7=new aK("TAIL",2)}function T4(){T4=Y,ysn=hh(hh(hh(yE(new ur,(B4(),yA)),(Vj(),ure)),Z4e),t6e)}function N1(){N1=Y,Esn=hh(hh(hh(yE(new ur,(B4(),EA)),(Vj(),e6e)),V4e),W4e)}function L3(e,n){return tgn(wj(e,n,_t(ac(Kh,Fh(_t(ac(n==null?0:Oi(n),Vh)),15)))))}function mhe(e,n){return ja(),Df(B0),E.Math.abs(e-n)<=B0||e==n||isNaN(e)&&isNaN(n)}function N9(e,n){var t,i;i=e.a,t=nEn(e,n,null),i!=n&&!e.e&&(t=m8(e,n,t)),t&&t.mj()}function k8n(e,n){var t;return t=Or(wc(u(Bn(e.g,n),8)),$se(u(Bn(e.f,n),460).b)),t}function GRe(e,n,t){var i=function(){return e.apply(i,arguments)};return n.apply(i,t),i}function x4(e){var n;return qE(e==null||Array.isArray(e)&&(n=oO(e),!(n>=14&&n<=16))),e}function vhe(e){e.b=(gs(),X0),e.f=(qo(),K0),e.d=(cl(2,$p),new So(2)),e.e=new Kr}function aB(e){this.b=(Tt(e),new ds(e)),this.a=new xe,this.d=new xe,this.e=new Kr}function qRe(e){return O0(e),h4(!0,"n may not be negative"),new bn(e,new fBe(e.a))}function E8n(e,n){vn();var t,i;for(i=new xe,t=0;t0?u(_e(t.a,i-1),9):null}function Df(e){if(!(e>=0))throw P(new Gn("tolerance ("+e+") must be >= 0"));return e}function dj(){return nce||(nce=new jXe,D4(nce,z(B(l6,1),xn,148,0,[new Mx]))),nce}function bB(){bB=Y,F4e=new eK("NO",0),ire=new eK(cwe,1),z4e=new eK("LOOK_BACK",2)}function Cc(){Cc=Y,gA=new YX(lS,0),vs=new YX("INPUT",1),No=new YX("OUTPUT",2)}function gB(){gB=Y,fve=new JX("ARD",0),GH=new JX("MSD",1),Jte=new JX("MANUAL",2)}function C8n(){return UO(),z(B(dve,1),ye,267,0,[Ute,hve,Kte,Vte,Xte,Yte,WN,qte,Gte])}function O8n(){return KO(),z(B(k4e,1),ye,268,0,[Jie,m4e,v4e,Fie,p4e,y4e,kJ,zie,Hie])}function N8n(){return Is(),z(B(h8e,1),ye,266,0,[N7,qD,oG,XA,sG,fG,lG,Sce,GD])}function D8n(){aTe();for(var e=Hne,n=0;nt)throw P(new ep(n,t));return new Rle(e,n)}function wB(e){var n,t;for(t=e.c.Bc().Jc();t.Ob();)n=u(t.Pb(),18),n.$b();e.c.$b(),e.d=0}function I8n(e){var n,t,i,r;for(t=e.a,i=0,r=t.length;i=0),wjn(e.d,e.c)<0&&(e.a=e.a-1&e.d.a.length-1,e.b=e.d.c),e.c=-1}function fBe(e){C$.call(this,e.yd(64)?Lse(0,uf(e.xd(),1)):fN,e.wd()),this.b=1,this.a=e}function aBe(){Yse.call(this),this.n=-1,this.g=null,this.i=null,this.j=null,this.Bb|=Rf}function hBe(e,n,t,i){this.$j(),this.a=n,this.b=e,this.c=null,this.c=new sNe(this,n,t,i)}function TY(e,n,t,i,r){this.d=e,this.n=n,this.g=t,this.o=i,this.p=-1,r||(this.o=-2-i-1)}function dBe(e){Joe(),this.g=new gt,this.f=new gt,this.b=new gt,this.c=new bw,this.i=e}function xhe(){this.f=new Kr,this.d=new loe,this.c=new Kr,this.a=new xe,this.b=new xe}function L8n(e){var n,t;for(t=new L(lJe(e));t.a=0}function Che(){Che=Y,Zun=Bt(Bt(Bt(new ur,(Br(),Ff),(qr(),gv)),Wh,Yp),Zu,Vp)}function bBe(){bBe=Y,Wun=Bt(Bt(Bt(new ur,(Br(),Ff),(qr(),gv)),Wh,Yp),Zu,Vp)}function Ohe(){Ohe=Y,eon=Bt(Bt(Bt(new ur,(Br(),Ff),(qr(),gv)),Wh,Yp),Zu,Vp)}function gBe(){gBe=Y,non=Bt(Bt(Bt(new ur,(Br(),Ff),(qr(),gv)),Wh,Yp),Zu,Vp)}function wBe(){wBe=Y,ton=Bt(Bt(Bt(new ur,(Br(),Ff),(qr(),gv)),Wh,Yp),Zu,Vp)}function pBe(){pBe=Y,ion=Bt(Bt(Bt(new ur,(Br(),Ff),(qr(),gv)),Wh,Yp),Zu,Vp)}function mBe(){mBe=Y,uon=Eo(Bt(Bt(new ur,(Br(),Zu),(qr(),OH)),Wu,AH),_c,CH)}function vBe(){vBe=Y,qen=z(B(It,1),Zt,30,15,[0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15])}function Nhe(e,n){var t;t=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,0,t,e.b))}function Dhe(e,n){var t;t=e.c,e.c=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,1,t,e.c))}function xY(e,n){var t;t=e.c,e.c=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,4,t,e.c))}function Ihe(e,n){var t;t=e.c,e.c=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,1,t,e.c))}function _he(e,n){var t;t=e.d,e.d=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,1,t,e.d))}function I9(e,n){var t;t=e.k,e.k=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,2,t,e.k))}function CY(e,n){var t;t=e.D,e.D=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,2,t,e.D))}function vB(e,n){var t;t=e.f,e.f=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,8,t,e.f))}function yB(e,n){var t;t=e.i,e.i=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,7,t,e.i))}function Lhe(e,n){var t;t=e.a,e.a=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,8,t,e.a))}function Phe(e,n){var t;t=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,0,t,e.b))}function R8n(e,n,t){var i;e.b=n,e.a=t,i=(e.a&512)==512?new AAe:new dx,e.c=gDn(i,e.b,e.a)}function yBe(e,n){return $1(e.e,n)?(Tc(),yY(n)?new tR(n,e):new gC(n,e)):new Uxe(n,e)}function B8n(e){var n,t;return 0>e?new Foe:(n=e+1,t=new IPe(n,e),new ple(null,t))}function z8n(e,n){vn();var t;return t=new Z5(1),Pr(e)?Xc(t,e,n):Xo(t.f,e,n),new cX(t)}function F8n(e,n){var t;t=new Fm,u(n.b,68),u(n.b,68),u(n.b,68),Ao(n.a,new Kle(e,t,n))}function kBe(e,n){var t;return q(n,8)?(t=u(n,8),e.a==t.a&&e.b==t.b):!1}function H8n(e){var n;return n=T(e,(pe(),gi)),q(n,174)?HFe(u(n,174)):null}function EBe(e){var n;return e=E.Math.max(e,2),n=c1e(e),e>n?(n<<=1,n>0?n:rS):n}function OY(e){switch(Kse(e.e!=3),e.e){case 2:return!1;case 0:return!0}return Uyn(e)}function $he(e){var n;return e.b==null?(gd(),gd(),WD):(n=e.sl()?e.rl():e.ql(),n)}function jBe(e,n){var t,i;for(i=n.vc().Jc();i.Ob();)t=u(i.Pb(),45),PO(e,t.jd(),t.kd())}function Rhe(e,n){var t;t=e.d,e.d=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,11,t,e.d))}function kB(e,n){var t;t=e.j,e.j=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,13,t,e.j))}function Bhe(e,n){var t;t=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,21,t,e.b))}function zhe(e,n){e.r>0&&e.c0&&e.g!=0&&zhe(e.i,n/e.r*e.i.d))}function SBe(e,n,t){var i,r,c;for(c=e.a.length-1,r=e.b,i=0;i0?1:0:(!e.c&&(e.c=JC(_u(e.f))),e.c).e}function _Be(e,n){n?e.B==null&&(e.B=e.D,e.D=null):e.B!=null&&(e.D=e.B,e.B=null)}function X8n(e,n){n.Tg(fQe,1),Wi(ou(new bn(null,new wn(e.b,16)),new Lg),new pI),n.Ug()}function LY(e,n,t,i,r,c){var o;this.c=e,o=new xe,Ade(e,o,n,e.b,t,i,r,c),this.a=new Gr(o,0)}function ir(e,n,t,i,r,c,o,l,f,h,b,p,y){return YGe(e,n,t,i,r,c,o,l,f,h,b,p,y),hQ(e,!1),e}function K8n(e,n){typeof window===uN&&typeof window.$gwt===uN&&(window.$gwt[e]=n)}function V8n(e,n,t){var i,r,c;for(i=0,r=0;r>>31;i!=0&&(e[t]=i)}function Y8n(e,n,t){t.Tg("DFS Treeifying phase",1),ujn(e,n),$Nn(e,n),e.a=null,e.b=null,t.Ug()}function Q8n(e,n){var t;n.Tg("General Compactor",1),t=HEn(u(ve(e,(_0(),xre)),386)),t.Bg(e)}function Z8n(e,n){var t,i;return t=u(ve(e,(_0(),BJ)),15),i=u(ve(n,BJ),15),uo(t.a,i.a)}function Ghe(e,n,t){var i,r;for(r=Et(e,0);r.b!=r.d.c;)i=u(yt(r),8),i.a+=n,i.b+=t;return e}function W8n(e,n,t,i){var r;r=new X5,Lb(r,"x",gz(e,n,i.a)),Lb(r,"y",wz(e,n,i.b)),p4(t,r)}function e7n(e,n,t,i){var r;r=new X5,Lb(r,"x",gz(e,n,i.a)),Lb(r,"y",wz(e,n,i.b)),p4(t,r)}function n7n(){return P0(),z(B(O4e,1),ye,243,0,[SJ,dD,bD,T4e,x4e,M4e,C4e,AJ,m7,bA])}function t7n(){return Oc(),z(B(rie,1),ye,261,0,[VH,ql,VS,YH,f7,yv,YS,s7,l7,QH])}function PY(){PY=Y,ZA=new yAe,Lce=z(B(es,1),av,179,0,[]),Han=z(B(pf,1),Kpe,62,0,[])}function C4(){C4=Y,Ote=new Li("edgelabelcenterednessanalysis.includelabel",(Pn(),U0))}function LBe(e,n){return W(ie(zs(SO(jo(new bn(null,new wn(e.c.b,16)),new FEe(e)),n))))}function qhe(e,n){return W(ie(zs(SO(jo(new bn(null,new wn(e.c.b,16)),new zEe(e)),n))))}function Oi(e){return Pr(e)?Sd(e):K2(e)?i4(e):X2(e)?LOe(e):yfe(e)?e.Hb():gfe(e)?cw(e):tae(e)}function PBe(e,n){return ja(),Df($a),E.Math.abs(0-n)<=$a||n==0||isNaN(0)&&isNaN(n)?0:e/n}function i7n(e,n){return B9(),e==Xw&&n==Xp||e==Xw&&n==bv||e==Kp&&n==bv||e==Kp&&n==Xp}function r7n(e,n){return B9(),e==Xw&&n==Kp||e==Kp&&n==Xw||e==bv&&n==Xp||e==Xp&&n==bv}function os(){os=Y,p3e=new c5,g3e=new e0,w3e=new xh,b3e=new V7,m3e=new TM,v3e=new u5}function c7n(e){var n;return n=$R(e),IE(n.a,0)?(KP(),KP(),rnn):(KP(),new bOe(n.b))}function $Y(e){var n;return n=jae(e),IE(n.a,0)?(J2(),J2(),ite):(J2(),new NK(n.b))}function RY(e){var n;return n=jae(e),IE(n.a,0)?(J2(),J2(),ite):(J2(),new NK(n.c))}function u7n(e){return e.b.c.i.k==(zn(),gr)?u(T(e.b.c.i,(pe(),gi)),12):e.b.c}function $Be(e){return e.b.d.i.k==(zn(),gr)?u(T(e.b.d.i,(pe(),gi)),12):e.b.d}function RBe(e){switch(e.g){case 2:return Oe(),Kn;case 4:return Oe(),Wn;default:return e}}function BBe(e){switch(e.g){case 1:return Oe(),dt;case 3:return Oe(),Xn;default:return e}}function o7n(e,n){var t;return t=l0e(e),z0e(new ke(t.c,t.d),new ke(t.b,t.a),e.Kf(),n,e.$f())}function s7n(e,n){n.Tg(fQe,1),Z1e(dgn(new MP((wE(),new TV(e,!1,!1,new K7))))),n.Ug()}function Uhe(){Uhe=Y,oon=hh(Qxe(Bt(Bt(new ur,(Br(),Zu),(qr(),OH)),Wu,AH),_c),CH)}function zBe(){zBe=Y,aon=hh(Qxe(Bt(Bt(new ur,(Br(),Zu),(qr(),OH)),Wu,AH),_c),CH)}function FBe(e,n,t){this.g=e,this.d=n,this.e=t,this.a=new xe,Zxn(this),vn(),xr(this.a,null)}function Pl(e,n,t,i,r,c,o){Mt.call(this,e,n),this.d=t,this.e=i,this.c=r,this.b=c,this.a=Of(o)}function Xhe(e){this.i=e.gc(),this.i>0&&(this.g=this.$i(this.i+(this.i/8|0)+1),e.Oc(this.g))}function gj(e,n){var t,i;for(_n(n),i=n.vc().Jc();i.Ob();)t=u(i.Pb(),45),e.yc(t.jd(),t.kd())}function l7n(e,n,t){var i;for(i=t.Jc();i.Ob();)if(!FR(e,n,i.Pb()))return!1;return!0}function wj(e,n,t){var i;for(i=e.b[t&e.f];i;i=i.b)if(t==i.a&&j1(n,i.g))return i;return null}function pj(e,n,t){var i;for(i=e.c[t&e.f];i;i=i.d)if(t==i.f&&j1(n,i.i))return i;return null}function f7n(e,n){var t;for(Tt(n);e.Ob();)if(t=e.Pb(),!Qhe(u(t,9)))return!1;return!0}function a7n(e,n,t,i,r){var c;return t&&(c=Fi(n.Ah(),e.c),r=t.Oh(n,-1-(c==-1?i:c),null,r)),r}function h7n(e,n,t,i,r){var c;return t&&(c=Fi(n.Ah(),e.c),r=t.Qh(n,-1-(c==-1?i:c),null,r)),r}function HBe(e){var n;if(e.b==-2){if(e.e==0)n=-1;else for(n=0;e.a[n]==0;n++);e.b=n}return e.b}function d7n(e){var n,t,i;return e.j==(Oe(),Xn)&&(n=Jqe(e),t=rs(n,Wn),i=rs(n,Kn),i||i&&t)}function b7n(e){var n,t,i;for(i=0,t=new L(e.b);t.ar&&n.ac&&n.br?t=r:Yn(n,t+1),e.a=rf(e.a,0,n)+(""+i)+Pfe(e.a,t)}function JBe(e,n,t,i){q(e.Cb,184)&&(u(e.Cb,184).tb=null),Mo(e,t),n&&bCn(e,n),i&&e.el(!0)}function p7n(e,n){var t,i;for(i=new L(n.b);i.a1||e.Ob())return++e.a,e.g=0,n=e.i,e.Ob(),n;throw P(new fu)}function A7n(e,n){var t,i;for(i=new L(n);i.a>22),r=e.h+n.h+(i>>22),Io(t&_s,i&_s,r&B1)}function wze(e,n){var t,i,r;return t=e.l-n.l,i=e.m-n.m+(t>>22),r=e.h-n.h+(i>>22),Io(t&_s,i&_s,r&B1)}function qY(e){var n,t,i,r;for(r=new xe,i=e.Jc();i.Ob();)t=u(i.Pb(),26),n=Np(t),jr(r,n);return r}function G7n(e){var n;Nd(e,!0),n=Dd,di(e,(Ce(),g7))&&(n+=u(T(e,g7),15).a),fe(e,g7,me(n))}function pze(e,n,t){var i;Fu(e.a),Ao(t.i,new zje(e)),i=new D$(u(Bn(e.a,n.b),68)),bHe(e,i,n),t.f=i}function e1e(e){var n,t;return t=(a0(),n=new vo,n),e&&kt((!e.a&&(e.a=new we(Pi,e,6,6)),e.a),t),t}function O4(e,n){var t,i;if(i=0,e<64&&e<=n)for(n=n<64?n:63,t=e;t<=n;t++)i=sh(i,Bh(1,t));return i}function q7n(e,n){var t,i;for(TR(n,"predicate"),i=0;e.Ob();i++)if(t=e.Pb(),n.Lb(t))return i;return-1}function n1e(e,n){if(n===0){!e.o&&(e.o=new us((Hu(),u1),qd,e,0)),e.o.c.$b();return}aZ(e,n)}function mze(e){switch(e.g){case 1:return rb;case 2:return i1;case 3:return $D;default:return RD}}function t1e(e){vn();var n,t,i;for(i=0,t=e.Jc();t.Ob();)n=t.Pb(),i=i+(n!=null?Oi(n):0),i=i|0;return i}function U7n(e){var n;return n=new Nn,n.a=e,n.b=Z7n(e),n.c=oe(Re,je,2,2,6,1),n.c[0]=IBe(e),n.c[1]=IBe(e),n}function IB(){IB=Y,Nte=new s$(aa,0),RH=new s$(dQe,1),BH=new s$(bQe,2),YN=new s$("BOTH",3)}function B9(){B9=Y,Xw=new u$("Q1",0),Kp=new u$("Q4",1),Xp=new u$("Q2",2),bv=new u$("Q3",3)}function M0(){M0=Y,nD=new XX("ONLY_WITHIN_GROUP",0),Ove=new XX(KW,1),Wp=new XX("ENFORCED",2)}function Gb(){Gb=Y,Qte=new qX(aa,0),o7=new qX("INCOMING_ONLY",1),vv=new qX("OUTGOING_ONLY",2)}function N4(){N4=Y,Qln=new LT,Yln=new V_}function UY(){UY=Y,Zne={boolean:sgn,number:ybn,string:kbn,object:WGe,function:WGe,undefined:W0n}}function vze(){vze=Y,Lun=Ct((P0(),z(B(O4e,1),ye,243,0,[SJ,dD,bD,T4e,x4e,M4e,C4e,AJ,m7,bA])))}function yze(){yze=Y,Pin=Ct((Oc(),z(B(rie,1),ye,261,0,[VH,ql,VS,YH,f7,yv,YS,s7,l7,QH])))}function X7n(e,n,t,i){return new Voe(z(B(sg,1),Zz,45,0,[(BQ(e,n),new ew(e,n)),(BQ(t,i),new ew(t,i))]))}function K7n(e,n){var t,i;return t=u(u(Bn(e.g,n.a),49).a,68),i=u(u(Bn(e.g,n.b),49).a,68),lKe(t,i)}function i1e(e,n,t){var i;if(i=e.gc(),n>i)throw P(new ep(n,i));return e.Qi()&&(t=N_e(e,t)),e.Ci(n,t)}function kze(e){var n,t,i;return t=e.n,i=e.o,n=e.d,new xf(t.a-n.b,t.b-n.d,i.a+(n.b+n.c),i.b+(n.d+n.a))}function V7n(e,n){return!e||!n||e==n?!1:jw(e.b.c,n.b.c+n.b.b)<0&&jw(n.b.c,e.b.c+e.b.b)<0}function XY(e,n,t){return e>=128?!1:e<64?_E($r(Bh(1,e),t),0):_E($r(Bh(1,e-64),n),0)}function vO(e,n,t){switch(t.g){case 2:e.b=n;break;case 1:e.c=n;break;case 4:e.d=n;break;case 3:e.a=n}}function yO(e,n,t){return t==null?(!e.q&&(e.q=new gt),S4(e.q,n)):(!e.q&&(e.q=new gt),Qt(e.q,n,t)),e}function fe(e,n,t){return t==null?(!e.q&&(e.q=new gt),S4(e.q,n)):(!e.q&&(e.q=new gt),Qt(e.q,n,t)),e}function Eze(e){var n,t;return t=new KR,Lu(t,e),fe(t,(S0(),a6),e),n=new gt,J_n(e,t,n),y$n(e,t,n),t}function Y7n(e){a8();var n,t,i;for(t=oe(_r,je,8,2,0,1),i=0,n=0;n<2;n++)i+=.5,t[n]=CSn(i,e);return t}function jze(e,n){var t,i,r,c;for(t=!1,i=e.a[n].length,c=0;ce.f,t=e.u+e.e[e.o.p]*e.d>e.f*e.s*e.d,n||t}function r1e(e){var n;return(!e.c||(e.Bb&1)==0&&(e.c.Db&64)!=0)&&(n=of(e),q(n,88)&&(e.c=u(n,29))),e.c}function c1e(e){var n;if(e<0)return Ur;if(e==0)return 0;for(n=rS;(n&e)==0;n>>=1);return n}function Z7n(e){var n;return e==0?"Etc/GMT":(e<0?(e=-e,n="Etc/GMT-"):n="Etc/GMT+",n+dRe(e))}function Aze(e){var n,t;return t=GO(e.h),t==32?(n=GO(e.m),n==32?GO(e.l)+32:n+20-10):t-12}function KY(e){var n,t,i;n=~e.l+1&_s,t=~e.m+(n==0?1:0)&_s,i=~e.h+(n==0&&t==0?1:0)&B1,e.l=n,e.m=t,e.h=i}function vj(e){var n;return n=e.a[e.b],n==null?null:(tr(e.a,e.b,null),e.b=e.b+1&e.a.length-1,n)}function u1e(){this.o=null,this.k=null,this.j=null,this.d=null,this.b=null,this.n=null,this.a=null}function o1e(e,n){this.c=e,this.d=n,this.b=this.d/this.c.c.Pd().gc()|0,this.a=this.d%this.c.c.Pd().gc()}function Mze(e,n){this.b=e,y3.call(this,(u(X(ge((p0(),Rn).o),10),19),n.i),n.g),this.a=(PY(),Lce)}function s1e(e,n,t){this.q=new E.Date,this.q.setFullYear(e+z0,n,t),this.q.setHours(0,0,0,0),Qj(this,0)}function Tze(e,n,t){var i,r;return i=new aY(n,t),r=new hi,e.b=UUe(e,e.b,i,r),r.b||++e.c,e.b.b=!1,r.d}function l1e(e,n){vn();var t,i,r,c,o;for(o=!1,i=n,r=0,c=i.length;ro||i+r>c)throw P(new Wue)}function xze(e,n,t){var i,r,c,o;for(o=Aj(n,t),c=0,r=o.Jc();r.Ob();)i=u(r.Pb(),12),Qt(e.c,i,me(c++))}function T0(e){var n,t;for(t=new L(e.a.b);t.a=0,"Negative initial capacity"),NC(n>=0,"Non-positive load factor"),Fu(this)}function Ize(e,n){var t;for(t=0;t1||n>=0&&e.b<3)}function okn(){si();var e;return Fce||(e=r2n($0("M",!0)),e=lR($0("M",!1),e),Fce=e,Fce)}function Pze(e){if(e.g===0)return new Ey;throw P(new Gn(LF+(e.f!=null?e.f:""+e.g)))}function $ze(e){if(e.g===0)return new K_;throw P(new Gn(LF+(e.f!=null?e.f:""+e.g)))}function b1e(e,n,t){if(n===0){!e.o&&(e.o=new us((Hu(),u1),qd,e,0)),TB(e.o,t);return}bZ(e,n,t)}function YY(e,n,t){this.g=e,this.e=new Kr,this.f=new Kr,this.d=new ji,this.b=new ji,this.a=n,this.c=t}function QY(e,n,t,i){this.b=new xe,this.n=new xe,this.i=i,this.j=t,this.s=e,this.t=n,this.r=0,this.d=0}function Rze(e,n,t,i){this.b=new gt,this.g=new gt,this.d=(jj(),EJ),this.c=e,this.e=n,this.d=t,this.a=i}function H9(e,n){if(!e.Ji()&&n==null)throw P(new Gn("The 'no null' constraint is violated"));return n}function g1e(e){switch(e.g){case 1:return LQe;default:case 2:return 0;case 3:return PQe;case 4:return N2e}}function skn(e){return Te(e.c,(N4(),Qln)),phe(e.a,W(ie(Ie((mQ(),yJ)))))?new XT:new Uje(e)}function lkn(e){for(;!e.d||!e.d.Ob();)if(e.b&&!aE(e.b))e.d=u(g4(e.b),50);else return null;return e.d}function Sd(e){var n,t;for(n=0,t=0;ti?1:0}function Bze(e,n){var t,i,r;for(r=e.b;r;){if(t=e.a.Le(n,r.d),t==0)return r;i=t<0?0:1,r=r.a[i]}return null}function ZY(e,n){var t;return n===e?!0:q(n,229)?(t=u(n,229),ai(e.Zb(),t.Zb())):!1}function w1e(e,n){return OUe(e,n)?(dn(e.b,u(T(n,(pe(),J1)),22),n),qt(e.a,n),!0):!1}function hkn(e,n){return di(e,(pe(),Ci))&&di(n,Ci)?u(T(n,Ci),15).a-u(T(e,Ci),15).a:0}function dkn(e,n){return di(e,(pe(),Ci))&&di(n,Ci)?u(T(e,Ci),15).a-u(T(n,Ci),15).a:0}function zze(e){return Fa?oe(onn,EYe,567,0,0,1):u(Na(e.a,oe(onn,EYe,567,e.a.c.length,0,1)),840)}function Gs(e){return Pr(e)?Re:K2(e)?br:X2(e)?Yi:yfe(e)||gfe(e)?e.Pm:e.Pm||Array.isArray(e)&&B(Ben,1)||Ben}function F3(e,n,t){var i,r;return r=(i=new bX,i),Bc(r,n,t),kt((!e.q&&(e.q=new we(pf,e,11,10)),e.q),r),r}function WY(e){var n,t,i,r;for(r=Sgn(man,e),t=r.length,i=oe(Re,je,2,t,6,1),n=0;n=e.b.c.length||(p1e(e,2*n+1),t=2*n+2,t0&&(n.Ad(t),t.i&&Fjn(t))}function m1e(e,n,t){var i;for(i=t-1;i>=0&&e[i]===n[i];i--);return i<0?0:$X($r(e[i],Nc),$r(n[i],Nc))?-1:1}function gkn(e,n){var t;return!e||e==n||!di(n,(pe(),Qw))?!1:(t=u(T(n,(pe(),Qw)),9),t!=e)}function eQ(e){switch(e.i){case 2:return!0;case 1:return!1;case-1:++e.c;default:return e.Yl()}}function Fze(e,n,t){return e.d[n.p][t.p]||(lSn(e,n,t),e.d[n.p][t.p]=!0,e.d[t.p][n.p]=!0),e.a[n.p][t.p]}function Hze(e,n,t){var i,r;this.g=e,this.c=n,this.a=this,this.d=this,r=EBe(t),i=oe($en,aN,227,r,0,1),this.b=i}function wkn(e,n){var t,i;for(i=e.Zb().Bc().Jc();i.Ob();)if(t=u(i.Pb(),18),t.Gc(n))return!0;return!1}function Jze(e,n,t){var i,r,c,o;for(_n(t),o=!1,c=e.dd(n),r=t.Jc();r.Ob();)i=r.Pb(),c.Rb(i),o=!0;return o}function nQ(e,n){var t,i;return i=u(Un(e.a,4),129),t=oe(Ice,xne,415,n,0,1),i!=null&&Yu(i,0,t,0,i.length),t}function Gze(e,n){var t;return t=new TZ((e.f&256)!=0,e.i,e.a,e.d,(e.f&16)!=0,e.j,e.g,n),e.e!=null||(t.c=e),t}function pkn(e,n){var t;return e===n?!0:q(n,92)?(t=u(n,92),y0e(Cb(e),t.vc())):!1}function qze(e,n,t){var i,r;for(r=t.Jc();r.Ob();)if(i=u(r.Pb(),45),e.ze(n,i.kd()))return!0;return!1}function _B(){_B=Y,Tce=new j$("ELK",0),j8e=new j$("JSON",1),E8e=new j$("DOT",2),S8e=new j$("SVG",3)}function yj(){yj=Y,vre=new g$(KW,0),PJ=new g$(BQe,1),mre=new g$("FAN",2),pre=new g$("CONSTRAINT",3)}function kj(){kj=Y,c6e=new iK(aa,0),ore=new iK("MIDDLE_TO_MIDDLE",1),mD=new iK("AVOID_OVERLAP",2)}function kO(){kO=Y,RJ=new rK(aa,0),D6e=new rK("RADIAL_COMPACTION",1),I6e=new rK("WEDGE_COMPACTION",2)}function Ej(){Ej=Y,nre=new ZX("STACKED",0),ere=new ZX("REVERSE_STACKED",1),gD=new ZX("SEQUENCED",2)}function $l(){$l=Y,Bme=new RX("CONCURRENT",0),Vo=new RX("IDENTITY_FINISH",1),zme=new RX("UNORDERED",2)}function _1(){_1=Y,cG=new hK(Mpe,0),Gd=new hK("INCLUDE_CHILDREN",1),FA=new hK("SEPARATE_CHILDREN",2)}function LB(){LB=Y,r8e=new iw(15),Ffn=new Vr((Ht(),t1),r8e),zA=O6,e8e=hfn,n8e=yg,i8e=Bv,t8e=wm}function tQ(){tQ=Y,kte=S_e(z(B(BA,1),ye,86,0,[(vr(),Zc),ru])),Ete=S_e(z(B(BA,1),ye,86,0,[Ul,Ua]))}function mkn(e){var n,t,i;for(n=0,i=oe(_r,je,8,e.b,0,1),t=Et(e,0);t.b!=t.d.c;)i[n++]=u(yt(t),8);return i}function iQ(e,n,t){var i,r,c;for(i=new ji,c=Et(t,0);c.b!=c.d.c;)r=u(yt(c),8),qt(i,new gc(r));Jze(e,n,i)}function vkn(e,n){var t;t=Ie((mQ(),yJ))!=null&&n.Rg()!=null?W(ie(n.Rg()))/W(ie(Ie(yJ))):1,Qt(e.b,n,t)}function ykn(e,n){var t,i;return t=u(e.d.Ac(n),18),t?(i=e.e.hc(),i.Fc(t),e.e.d-=t.gc(),t.$b(),i):null}function v1e(e,n){var t,i;if(i=e.c[n],i!=0)for(e.c[n]=0,e.d-=i,t=n+1;t0)return g9(n-1,e.a.c.length),yd(e.a,n-1);throw P(new GSe)}function kkn(e,n,t){if(n<0)throw P(new ko(rZe+n));nn)throw P(new Gn(iF+e+jYe+n));if(e<0||n>t)throw P(new Soe(iF+e+Fge+n+Rge+t))}function Xze(e){if(!e.a||(e.a.i&8)==0)throw P(new qc("Enumeration class expected for layout option "+e.f))}function Kze(e){C_e.call(this,"The given string does not match the expected format for individual spacings.",e)}function Vze(e){switch(e.i){case-2:return!0;case-1:return!1;case 1:--e.c;default:return e.Zl()}}function Ad(e){switch(e.c){case 0:return WK(),lme;case 1:return new H5(oqe(new Q5(e)));default:return new $Ae(e)}}function Yze(e){switch(e.gc()){case 0:return WK(),lme;case 1:return new H5(e.Jc().Pb());default:return new Yoe(e)}}function k1e(e){var n;return n=(!e.a&&(e.a=new we(V1,e,9,5)),e.a),n.i!=0?Egn(u(X(n,0),684)):null}function Ekn(e,n){var t;return t=pc(e,n),$X(qV(e,n),0)|T$(qV(e,t),0)?t:pc(fN,qV(Nb(t,63),1))}function E1e(e,n,t){var i,r;return fp(n,e.c.length),i=t.Nc(),r=i.length,r==0?!1:(Wle(e.c,n,i),!0)}function jkn(e,n){var t,i;for(t=e.a.length-1;n!=e.b;)i=n-1&t,tr(e.a,n,e.a[i]),n=i;tr(e.a,e.b,null),e.b=e.b+1&t}function Skn(e,n){var t,i;for(t=e.a.length-1,e.c=e.c-1&t;n!=e.c;)i=n+1&t,tr(e.a,n,e.a[i]),n=i;tr(e.a,e.c,null)}function J9(e,n){e.D==null&&e.B!=null&&(e.D=e.B,e.B=null),CY(e,n==null?null:(_n(n),n)),e.C&&e.fl(null)}function H3(e){return(e.c!=e.b.b||e.i!=e.g.b)&&($2(e.a.c,0),jr(e.a,e.b),jr(e.a,e.g),e.c=e.b.b,e.i=e.g.b),e.a}function yp(e){var n;++e.j,e.i==0?e.g=null:e.ir&&(HJe(n.q,r),i=t!=n.q.d)),i}function cFe(e,n){var t,i,r,c,o,l,f,h;return f=n.i,h=n.j,i=e.f,r=i.i,c=i.j,o=f-r,l=h-c,t=E.Math.sqrt(o*o+l*l),t}function A1e(e,n){var t,i;return i=WB(e),i||(t=(KZ(),uUe(n)),i=new _Se(t),kt(i.Cl(),e)),i}function EO(e,n){var t,i;return t=u(e.c.Ac(n),18),t?(i=e.hc(),i.Fc(t),e.d-=t.gc(),t.$b(),e.mc(i)):e.jc()}function Okn(e){var n;if(!(e.c.c<0?e.a>=e.c.b:e.a<=e.c.b))throw P(new fu);return n=e.a,e.a+=e.c.c,++e.b,me(n)}function Nkn(e){var n,t;if(e==null)return!1;for(n=0,t=e.length;n=i||n=0?n:-n;i>0;)i%2==0?(t*=t,i=i/2|0):(r*=t,i-=1);return n<0?1/r:r}function Bkn(e,n){var t,i,r;for(r=1,t=e,i=n>=0?n:-n;i>0;)i%2==0?(t*=t,i=i/2|0):(r*=t,i-=1);return n<0?1/r:r}function C0(e,n){var t,i,r,c;return c=(r=e?WB(e):null,ZGe((i=n,r&&r.El(),i))),c==n&&(t=WB(e),t&&t.El()),c}function T1e(e,n,t){var i,r;return r=e.a,e.a=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Dr(e,1,1,r,n),t?t.lj(i):t=i),t}function sFe(e,n,t){var i,r;return r=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Dr(e,1,3,r,n),t?t.lj(i):t=i),t}function lFe(e,n,t){var i,r;return r=e.f,e.f=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Dr(e,1,0,r,n),t?t.lj(i):t=i),t}function fFe(e){var n,t;if(e!=null)for(t=0;t-129&&e<128?(lDe(),n=e+128,t=Sme[n],!t&&(t=Sme[n]=new Ln(e)),t):new Ln(e)}function me(e){var n,t;return e>-129&&e<128?(tDe(),n=e+128,t=yme[n],!t&&(t=yme[n]=new ro(e)),t):new ro(e)}function qkn(e,n,t,i,r){n==0||i==0||(n==1?r[i]=vde(r,t,i,e[0]):i==1?r[n]=vde(r,e,n,t[0]):_Cn(e,t,r,n,i))}function gFe(e,n){var t;e.c.length!=0&&(t=u(Na(e,oe(e1,Id,9,e.c.length,0,1)),199),Ose(t,new Oh),kqe(t,n))}function wFe(e,n){var t;e.c.length!=0&&(t=u(Na(e,oe(e1,Id,9,e.c.length,0,1)),199),Ose(t,new Xm),kqe(t,n))}function pFe(e,n){var t;e.a.c.length>0&&(t=u(_e(e.a,e.a.c.length-1),565),w1e(t,n))||Te(e.a,new CPe(n))}function Ukn(e){el();var n,t;n=e.d.c-e.e.c,t=u(e.g,156),Ao(t.b,new MEe(n)),Ao(t.c,new TEe(n)),rc(t.i,new xEe(n))}function mFe(e){var n;return n=new l0,n.a+="VerticalSegment ",co(n,e.e),n.a+=" ",Gt(n,Use(new MX,new L(e.k))),n.a}function Xkn(e,n){var t;e.c=n,e.a=qEn(n),e.a<54&&(e.f=(t=n.d>1?TLe(n.a[0],n.a[1]):TLe(n.a[0],0),Bb(n.e>0?t:Ed(t))))}function oQ(e,n){var t,i,r;for(t=0,r=pu(e,n).Jc();r.Ob();)i=u(r.Pb(),12),t+=T(i,(pe(),ms))!=null?1:0;return t}function J3(e,n,t){var i,r,c;for(i=0,c=Et(e,0);c.b!=c.d.c&&(r=W(ie(yt(c))),!(r>t));)r>=n&&++i;return i}function Kkn(e){var n;return n=u(Ca(e.c.c,""),233),n||(n=new y4(Zy(Qy(new t0,""),"Other")),Kb(e.c.c,"",n)),n}function Sj(e){var n;return(e.Db&64)!=0?Lf(e):(n=new nf(Lf(e)),n.a+=" (name: ",$c(n,e.zb),n.a+=")",n.a)}function O1e(e,n,t){var i,r;return r=e.sb,e.sb=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Dr(e,1,4,r,n),t?t.lj(i):t=i),t}function jO(e,n,t){var i;e.Zi(e.i+1),i=e.Xi(n,t),n!=e.i&&Yu(e.g,n,e.g,n+1,e.i-n),tr(e.g,n,i),++e.i,e.Ki(n,t),e.Li()}function N1e(e,n,t){var i,r;return r=e.r,e.r=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Dr(e,1,8,r,e.r),t?t.lj(i):t=i),t}function Vkn(e,n,t){var i,r;return i=new O1(e.e,3,13,null,(r=n.c,r||(mn(),Ya)),Cd(e,n),!1),t?t.lj(i):t=i,t}function Ykn(e,n,t){var i,r;return i=new O1(e.e,4,13,(r=n.c,r||(mn(),Ya)),null,Cd(e,n),!1),t?t.lj(i):t=i,t}function Qkn(e,n){var t,i,r,c;if(n.cj(e.a),c=u(Un(e.a,8),1997),c!=null)for(t=c,i=0,r=t.length;i>1&1431655765,e=(e>>2&858993459)+(e&858993459),e=(e>>4)+e&252645135,e+=e>>8,e+=e>>16,e&63}function Zkn(e){return e?(e.i&1)!=0?e==ns?Yi:e==It?Er:e==Cm?Z8:e==Fr?br:e==l2?qw:e==qv?Uw:e==hs?u6:$S:e:null}function ai(e,n){return Pr(e)?hn(e,n):K2(e)?fNe(e,n):X2(e)?(_n(e),ue(e)===ue(n)):yfe(e)?e.Fb(n):gfe(e)?cCe(e,n):mae(e,n)}function yFe(e){var n;return fo(e,0)<0&&(e=A0(pvn(uu(e)?cf(e):e))),n=_t(Nb(e,32)),64-(n!=0?GO(n):GO(_t(e))+32)}function SO(e,n){var t;return t=new Wa,e.a.zd(t)?(u9(),new vX(_n(iRe(e,t.a,n)))):(m0(e),u9(),u9(),Lme)}function Aj(e,n){switch(n.g){case 2:case 1:return pu(e,n);case 3:case 4:return Us(pu(e,n))}return vn(),vn(),Ec}function Wkn(e,n){var t;return n.a&&(t=n.a.a.length,e.a?Gt(e.a,e.b):e.a=new Ws(e.d),NLe(e.a,n.a,n.d.length,t)),e}function eEn(e){Qz();var n,t,i,r;for(t=TQ(),i=0,r=t.length;it)throw P(new ko(iF+e+Fge+n+", size: "+t));if(e>n)throw P(new Gn(iF+e+jYe+n))}function Rl(e,n,t){if(n<0)P0e(e,t);else{if(!t.pk())throw P(new Gn(G0+t.ve()+SS));u(t,69).uk().Ck(e,e.ei(),n)}}function sQ(e,n,t){return E.Math.abs(n-e)CF?e-t>CF:t-e>CF}function I1e(e,n,t,i){switch(n){case 1:return!e.n&&(e.n=new we(ku,e,1,7)),e.n;case 2:return e.k}return xde(e,n,t,i)}function EFe(e){var n;return(e.Db&64)!=0?Lf(e):(n=new nf(Lf(e)),n.a+=" (source: ",$c(n,e.d),n.a+=")",n.a)}function Td(e,n){var t;t=(e.Bb&256)!=0,n?e.Bb|=256:e.Bb&=-257,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Cf(e,1,2,t,n))}function _1e(e,n){var t;t=(e.Bb&256)!=0,n?e.Bb|=256:e.Bb&=-257,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Cf(e,1,8,t,n))}function L1e(e,n){var t;t=(e.Bb&512)!=0,n?e.Bb|=512:e.Bb&=-513,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Cf(e,1,9,t,n))}function xd(e,n){var t;t=(e.Bb&512)!=0,n?e.Bb|=512:e.Bb&=-513,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Cf(e,1,3,t,n))}function BB(e,n){var t;t=(e.Bb&256)!=0,n?e.Bb|=256:e.Bb&=-257,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Cf(e,1,8,t,n))}function nEn(e,n,t){var i,r;return r=e.a,e.a=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Dr(e,1,5,r,e.a),t?Wde(t,i):t=i),t}function Mj(e,n){var t;return e.b==-1&&e.a&&(t=e.a.nk(),e.b=t?e.c.Eh(e.a.Jj(),t):Fi(e.c.Ah(),e.a)),e.c.vh(e.b,n)}function jFe(e,n){var t,i;for(i=new ct(e);i.e!=i.i.gc();)if(t=u(lt(i),29),ue(n)===ue(t))return!0;return!1}function SFe(e){return e>=65&&e<=70?e-65+10:e>=97&&e<=102?e-97+10:e>=48&&e<=57?e-48:0}function P1e(e){var n,t;return n=e.k,n==(zn(),gr)?(t=u(T(e,(pe(),Ou)),64),t==(Oe(),Xn)||t==dt):!1}function AFe(e){var n;return n=jae(e),IE(n.a,0)?(J2(),J2(),ite):(J2(),new NK(PX(n.a,0)?qae(n)/Bb(n.a):0))}function tEn(e,n){var t;if(t=VO(e,n),q(t,335))return u(t,38);throw P(new Gn(G0+n+"' is not a valid attribute"))}function Tj(e,n,t){var i;if(i=e.gc(),n>i)throw P(new ep(n,i));if(e.Qi()&&e.Gc(t))throw P(new Gn(LN));e.Ei(n,t)}function MFe(e,n){var t,i;for(i=new ct(e);i.e!=i.i.gc();)if(t=u(lt(i),143),ue(n)===ue(t))return!0;return!1}function iEn(e,n,t){var i,r,c;return c=(r=l8(e.b,n),r),c&&(i=u(Uz(cO(e,c),""),29),i)?ube(e,i,n,t):null}function lQ(e,n,t){var i,r,c;return c=(r=l8(e.b,n),r),c&&(i=u(Uz(cO(e,c),""),29),i)?obe(e,i,n,t):null}function rEn(e){var n,t,i;for(i=0,t=e.length,n=0;n=0?N0(e):ZE(N0(Ed(e))))}function TFe(e,n,t,i,r,c){this.e=new xe,this.f=(Cc(),gA),Te(this.e,e),this.d=n,this.a=t,this.b=i,this.f=r,this.c=c}function vi(e,n){return en?1:e==n?e==0?vi(1/e,1/n):0:isNaN(e)?isNaN(n)?0:1:-1}function cEn(e){var n;return n=e.a[e.c-1&e.a.length-1],n==null?null:(e.c=e.c-1&e.a.length-1,tr(e.a,e.c,null),n)}function xFe(e){var n,t;for(t=e.p.a.ec().Jc();t.Ob();)if(n=u(t.Pb(),217),n.f&&e.b[n.c]<-1e-10)return n;return null}function uEn(e){var n,t,i;for(n=new xe,i=new L(e.b);i.a=1?ru:Ua):t}function hEn(e){var n,t;for(t=oUe(rl(e)).Jc();t.Ob();)if(n=Dt(t.Pb()),Yj(e,n))return Syn((jTe(),Oan),n);return null}function dEn(e,n,t){var i,r;for(r=e.a.ec().Jc();r.Ob();)if(i=u(r.Pb(),9),mO(t,u(_e(n,i.p),18)))return i;return null}function bEn(e,n,t){var i,r;for(r=q(n,103)&&(u(n,19).Bb&kc)!=0?new mK(n,e):new P9(n,e),i=0;i>10)+gN&yr,n[1]=(e&1023)+56320&yr,ah(n,0,n.length)}function F1e(e,n){var t;t=(e.Bb&kc)!=0,n?e.Bb|=kc:e.Bb&=-65537,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Cf(e,1,20,t,n))}function Q9(e,n){var t;t=(e.Bb&wh)!=0,n?e.Bb|=wh:e.Bb&=-16385,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Cf(e,1,16,t,n))}function hQ(e,n){var t;t=(e.Bb&$u)!=0,n?e.Bb|=$u:e.Bb&=-32769,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Cf(e,1,18,t,n))}function H1e(e,n){var t;t=(e.Bb&$u)!=0,n?e.Bb|=$u:e.Bb&=-32769,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Cf(e,1,18,t,n))}function pu(e,n){var t;return e.i||L0e(e),t=u(Rc(e.g,n),49),t?new y0(e.j,u(t.a,15).a,u(t.b,15).a):(vn(),vn(),Ec)}function pEn(e,n,t){var i,r;return i=u(n.mf(e.a),35),r=u(t.mf(e.a),35),i!=null&&r!=null?aO(i,r):i!=null?-1:r!=null?1:0}function J1e(e,n,t){var i,r;return i=(a0(),r=new Nk,r),hB(i,n),dB(i,t),e&&kt((!e.a&&(e.a=new mr(pl,e,5)),e.a),i),i}function G1e(e,n,t){var i;return i=0,n&&(E3(e.a)?i+=n.f.a/2:i+=n.f.b/2),t&&(E3(e.a)?i+=t.f.a/2:i+=t.f.b/2),i}function Ew(e,n,t){var i;return i=e.a.get(n),e.a.set(n,t===void 0?null:t),i===void 0?(++e.c,++e.b.g):++e.d,i}function dQ(e){var n;return(e.Db&64)!=0?Lf(e):(n=new nf(Lf(e)),n.a+=" (identifier: ",$c(n,e.k),n.a+=")",n.a)}function JB(e){var n;switch(e.gc()){case 0:return iR(),Kne;case 1:return new RK(Tt(e.Xb(0)));default:return n=e,new UV(n)}}function mEn(e){switch(u(T(e,(Ce(),q1)),222).g){case 1:return new cd;case 3:return new p5;default:return new m2}}function vEn(e){var n;return n=Tp(e),n>34028234663852886e22?Ki:n<-34028234663852886e22?Nr:n}function pc(e,n){var t;return uu(e)&&uu(n)&&(t=e+n,bNn){ELe(t);break}}mR(t,n)}function Qe(e,n){var t,i,r,c,o;if(t=n.f,Kb(e.c.d,t,n),n.g!=null)for(r=n.g,c=0,o=r.length;cn&&i.Le(e[c-1],e[c])>0;--c)o=e[c],tr(e,c,e[c-1]),tr(e,c-1,o)}function Bl(e,n,t,i){if(n<0)abe(e,t,i);else{if(!t.pk())throw P(new Gn(G0+t.ve()+SS));u(t,69).uk().Ak(e,e.ei(),n,i)}}function CEn(e,n){var t;if(t=VO(e.Ah(),n),q(t,103))return u(t,19);throw P(new Gn(G0+n+"' is not a valid reference"))}function GB(e,n){if(n==e.d)return e.e;if(n==e.e)return e.d;throw P(new Gn("Node "+n+" not part of edge "+e))}function U1e(e,n,t,i){switch(n){case 3:return e.f;case 4:return e.g;case 5:return e.i;case 6:return e.j}return I1e(e,n,t,i)}function OEn(e){return e.k!=(zn(),Qi)?!1:I3(new bn(null,new cp(new qn(Vn(Ni(e).a.Jc(),new ne)))),new QM)}function qs(){qs=Y,uD=new uC(aa,0),eA=new uC("FIRST",1),G1=new uC(dQe,2),nA=new uC("LAST",3),hg=new uC(bQe,4)}function Cj(){Cj=Y,XS=new f$("LAYER_SWEEP",0),ove=new f$("MEDIAN_LAYER_SWEEP",1),ZN=new f$(WW,2),sve=new f$(aa,3)}function qB(){qB=Y,nye=new oK("ASPECT_RATIO_DRIVEN",0),Bre=new oK("MAX_SCALE_DRIVEN",1),eye=new oK("AREA_DRIVEN",2)}function UB(){UB=Y,Mce=new E$(C2e,0),m8e=new E$("GROUP_DEC",1),y8e=new E$("GROUP_MIXED",2),v8e=new E$("GROUP_INC",3)}function NEn(e,n){return hn(n.b&&n.c?Rb(n.b)+"->"+Rb(n.c):"e_"+Oi(n),e.b&&e.c?Rb(e.b)+"->"+Rb(e.c):"e_"+Oi(e))}function DEn(e,n){return hn(n.b&&n.c?Rb(n.b)+"->"+Rb(n.c):"e_"+Oi(n),e.b&&e.c?Rb(e.b)+"->"+Rb(e.c):"e_"+Oi(e))}function jw(e,n){return ja(),Df(B0),E.Math.abs(e-n)<=B0||e==n||isNaN(e)&&isNaN(n)?0:en?1:Tb(isNaN(e),isNaN(n))}function X1e(e){mQ(),this.c=Of(z(B($Bn,1),xn,829,0,[Cun])),this.b=new gt,this.a=e,Qt(this.b,yJ,1),Ao(Oun,new qje(this))}function Oj(e){var n;this.a=(n=u(e.e&&e.e(),10),new Nl(n,u(Tf(n,n.length),10),0)),this.b=oe(Mr,xn,1,this.a.a.length,5,1)}function su(e){var n;return Array.isArray(e)&&e.Rm===nt?Sb(Gs(e))+"@"+(n=Oi(e)>>>0,n.toString(16)):e.toString()}function IEn(e){var n;return e==null?!0:(n=e.length,n>0&&(Yn(n-1,e.length),e.charCodeAt(n-1)==58)&&!wQ(e,YA,QA))}function wQ(e,n,t){var i,r;for(i=0,r=e.length;i=r)return n.c+t;return n.c+n.b.gc()}function $Fe(e,n){f9();var t,i,r,c;for(i=C$e(e),r=n,x9(i,0,i.length,r),t=0;t0&&(i+=r,++t);return t>1&&(i+=e.d*(t-1)),i}function V1e(e){var n,t,i;for(i=new ad,i.a+="[",n=0,t=e.gc();n=0;--i)for(n=t[i],r=0;r>5,n=e&31,i=oe(It,Zt,30,t+1,15,1),i[t]=1<0&&(n.lengthe.i&&tr(n,e.i,null),n}function XB(e){var n;return(e.Db&64)!=0?Sj(e):(n=new nf(Sj(e)),n.a+=" (instanceClassName: ",$c(n,e.D),n.a+=")",n.a)}function KB(e){var n,t,i,r;for(r=0,t=0,i=e.length;t0?(e.Zj(),i=n==null?0:Oi(n),r=(i&ri)%e.d.length,t=dUe(e,r,i,n),t!=-1):!1}function To(e,n,t){var i,r,c;return e.Nj()?(i=e.i,c=e.Oj(),jO(e,i,n),r=e.Gj(3,null,n,i,c),t?t.lj(r):t=r):jO(e,e.i,n),t}function la(e,n){var t,i,r;return e.f>0&&(e.Zj(),i=n==null?0:Oi(n),r=(i&ri)%e.d.length,t=J0e(e,r,i,n),t)?t.kd():null}function njn(e,n,t){var i,r;return i=new O1(e.e,3,10,null,(r=n.c,q(r,88)?u(r,29):(mn(),vf)),Cd(e,n),!1),t?t.lj(i):t=i,t}function tjn(e,n,t){var i,r;return i=new O1(e.e,4,10,(r=n.c,q(r,88)?u(r,29):(mn(),vf)),null,Cd(e,n),!1),t?t.lj(i):t=i,t}function KFe(e,n){var t,i,r;return q(n,45)?(t=u(n,45),i=t.jd(),r=kp(e.Pc(),i),j1(r,t.kd())&&(r!=null||e.Pc()._b(i))):!1}function rde(e,n){switch(n){case 3:mw(e,0);return;case 4:vw(e,0);return;case 5:Cs(e,0);return;case 6:Os(e,0);return}C1e(e,n)}function Sw(e,n){switch(n.g){case 1:return a4(e.j,(os(),g3e));case 2:return a4(e.j,(os(),p3e));default:return vn(),vn(),Ec}}function N0(e){bh();var n,t;return t=_t(e),n=_t(Nb(e,32)),n!=0?new iLe(t,n):t>10||t<0?new T1(1,t):Yen[t]}function VFe(e){Ap();var n;return(e.q?e.q:(vn(),vn(),Zh))._b((Ce(),n2))?n=u(T(e,n2),203):n=u(T(Ir(e),lA),203),n}function ijn(e,n,t,i){var r,c;if(c=t-n,c<3)for(;c<3;)e*=10,++c;else{for(r=1;c>3;)r*=10,--c;e=(e+(r>>1))/r|0}return i.i=e,!0}function YFe(e,n,t){nBe(),uAe.call(this),this.a=np(vnn,[je,Gge],[592,216],0,[dH,fte],2),this.c=new r4,this.g=e,this.f=n,this.d=t}function QFe(e){this.e=oe(It,Zt,30,e.length,15,1),this.c=oe(ns,fa,30,e.length,16,1),this.b=oe(ns,fa,30,e.length,16,1),this.f=0}function rjn(e){var n,t;for(e.j=oe(Fr,zc,30,e.p.c.length,15,1),t=new L(e.p);t.a>5,n&=31,r=e.d+t+(n==0?0:1),i=oe(It,Zt,30,r,15,1),tTn(i,e.a,t,n),c=new Db(e.e,r,i),rj(c),c}function Z9(e,n,t){var i,r,c;for(r=null,c=e.b;c;){if(i=e.a.Le(n,c.d),t&&i==0)return c;i>=0?c=c.a[1]:(r=c,c=c.a[0])}return r}function OO(e,n,t){var i,r,c;for(r=null,c=e.b;c;){if(i=e.a.Le(n,c.d),t&&i==0)return c;i<=0?c=c.a[0]:(r=c,c=c.a[1])}return r}function EQ(e,n){for(var t=0;!n[t]||n[t]=="";)t++;for(var i=n[t++];t0?(E.Error.stackTraceLimit=Error.stackTraceLimit=64,!0):"stack"in new Error}function ljn(e){var n;n=e.a;do n=u(tt(new qn(Vn(Ni(n).a.Jc(),new ne))),17).d.i,n.k==(zn(),hr)&&Te(e.e,n);while(n.k==(zn(),hr))}function fjn(e,n){var t,i,r;for(i=new qn(Vn(Ni(e).a.Jc(),new ne));at(i);)if(t=u(tt(i),17),r=t.d.i,r.c==n)return!1;return!0}function iHe(e,n,t){var i,r,c,o;for(r=u(Bn(e.b,t),171),i=0,o=new L(n.j);o.an?1:Tb(isNaN(e),isNaN(n)))>0}function sde(e,n){return ja(),ja(),Df(B0),(E.Math.abs(e-n)<=B0||e==n||isNaN(e)&&isNaN(n)?0:en?1:Tb(isNaN(e),isNaN(n)))<0}function sHe(e,n){return ja(),ja(),Df(B0),(E.Math.abs(e-n)<=B0||e==n||isNaN(e)&&isNaN(n)?0:en?1:Tb(isNaN(e),isNaN(n)))<=0}function lde(e){switch(e.g){case 12:case 13:case 14:case 15:case 16:case 17:case 18:case 19:case 20:return!0;default:return!1}}function fde(e,n,t,i,r,c){this.a=e,this.c=n,this.b=t,this.f=i,this.d=r,this.e=c,this.c>0&&this.b>0&&(this.g=cR(this.c,this.b,this.a))}function djn(e,n){var t=e.a,i;n=String(n),t.hasOwnProperty(n)&&(i=t[n]);var r=(UY(),Zne)[typeof i],c=r?r(i):D1e(typeof i);return c}function W9(e){var n,t,i;if(i=null,n=Eh in e.a,t=!n,t)throw P(new ih("Every element must have an id."));return i=H4(A1(e,Eh)),i}function Aw(e){var n,t;for(t=LGe(e),n=null;e.c==2;)ui(e),n||(n=(si(),si(),new RE(2)),Zb(n,t),t=n),t.Hm(LGe(e));return t}function QB(e,n){var t,i,r;return e.Zj(),i=n==null?0:Oi(n),r=(i&ri)%e.d.length,t=J0e(e,r,i,n),t?(oBe(e,t),t.kd()):null}function ah(e,n,t){var i,r,c,o;for(c=n+t,Yr(n,c,e.length),o="",r=n;rn.e?1:e.en.d?e.e:e.d=48&&e<48+E.Math.min(10,10)?e-48:e>=97&&e<97?e-97+10:e>=65&&e<65?e-65+10:-1}function bjn(e,n){if(n.c==e)return n.d;if(n.d==e)return n.c;throw P(new Gn("Input edge is not connected to the input port."))}function hh(e,n){if(e.a<0)throw P(new qc("Did not call before(...) or after(...) before calling add(...)."));return cle(e,e.a,n),e}function ade(e){return LR(),q(e,166)?u(Bn(YD,nnn),296).Qg(e):oo(YD,Gs(e))?u(Bn(YD,Gs(e)),296).Qg(e):null}function _o(e){var n,t;return(e.Db&32)==0&&(t=(n=u(Un(e,16),29),ht(n||e.fi())-ht(e.fi())),t!=0&&L4(e,32,oe(Mr,xn,1,t,5,1))),e}function L4(e,n,t){var i;(e.Db&n)!=0?t==null?ICn(e,n):(i=GQ(e,n),i==-1?e.Eb=t:tr(x4(e.Eb),i,t)):t!=null&&WNn(e,n,t)}function gjn(e,n,t,i){var r,c;n.c.length!=0&&(r=XOn(t,i),c=nCn(n),Wi(lB(new bn(null,new wn(c,1)),new d_),new IIe(e,t,r,i)))}function wjn(e,n){var t,i,r,c;return i=e.a.length-1,t=n-e.b&i,c=e.c-n&i,r=e.c-e.b&i,gOe(t=c?(Skn(e,n),-1):(jkn(e,n),1)}function pjn(e,n){var t,i;for(t=(Yn(n,e.length),e.charCodeAt(n)),i=n+1;in.e?1:e.fn.f?1:Oi(e)-Oi(n)}function dHe(e,n){var t;return ue(n)===ue(e)?!0:!q(n,22)||(t=u(n,22),t.gc()!=e.gc())?!1:e.Hc(t)}function ZB(e,n){return _n(e),n==null?!1:hn(e,n)?!0:e.length==n.length&&hn(e.toLowerCase(),n.toLowerCase())}function Sp(e){var n,t;return fo(e,-129)>0&&fo(e,128)<0?(sDe(),n=_t(e)+128,t=kme[n],!t&&(t=kme[n]=new yn(e)),t):new yn(e)}function P4(){P4=Y,JS=new o$(aa,0),f3e=new o$("INSIDE_PORT_SIDE_GROUPS",1),Ste=new o$("GROUP_MODEL_ORDER",2),Ate=new o$(KW,3)}function WB(e){var n,t,i;if(i=e.Gh(),!i)for(n=0,t=e.Mh();t;t=t.Mh()){if(++n>DW)return t.Nh();if(i=t.Gh(),i||t==e)break}return i}function yjn(e){var n;return e.b||Zbn(e,(n=Z2n(e.e,e.a),!n||!hn(une,la((!n.b&&(n.b=new Fs((mn(),Sc),Nu,n)),n.b),"qualified")))),e.c}function kjn(e){var n,t;for(t=new L(e.a.b);t.a2e3&&(zen=e,uH=E.setTimeout(bgn,10))),cH++==0?(V9n((voe(),ame)),!0):!1}function Ijn(e,n,t){var i;(snn?(UEn(e),!0):lnn||ann?(r9(),!0):fnn&&(r9(),!1))&&(i=new kNe(n),i.b=t,_Tn(e,i))}function AQ(e,n){var t;t=!e.A.Gc((Xs(),Eg))||e.q==(Rr(),eo),e.u.Gc((ws(),K1))?t?nRn(e,n):wVe(e,n):e.u.Gc(ob)&&(t?j$n(e,n):NVe(e,n))}function _jn(e,n,t){var i,r;uZ(e.e,n,t,(Oe(),Kn)),uZ(e.i,n,t,Wn),e.a&&(r=u(T(n,(pe(),gi)),12),i=u(T(t,gi),12),XV(e.g,r,i))}function mHe(e){var n;ue(ve(e,(Ht(),Pv)))===ue((_1(),cG))&&(zi(e)?(n=u(ve(zi(e),Pv),347),yi(e,Pv,n)):yi(e,Pv,FA))}function vHe(e,n,t){return new xf(E.Math.min(e.a,n.a)-t/2,E.Math.min(e.b,n.b)-t/2,E.Math.abs(e.a-n.a)+t,E.Math.abs(e.b-n.b)+t)}function yHe(e){var n;this.d=new xe,this.j=new Kr,this.g=new Kr,n=e.g.b,this.f=u(T(Ir(n),(Ce(),dl)),86),this.e=W(ie(tz(n,lm)))}function kHe(e){this.d=new xe,this.e=new E0,this.c=oe(It,Zt,30,(Oe(),z(B(jc,1),Ju,64,0,[yu,Xn,Wn,dt,Kn])).length,15,1),this.b=e}function wde(e,n,t){var i;switch(i=t[e.g][n],e.g){case 1:case 3:return new ke(0,i);case 2:case 4:return new ke(i,0);default:return null}}function Ljn(e,n){var t;if(t=L3(e.o,n),t==null)throw P(new ih("Node did not exist in input."));return gbe(e,n),NZ(e,n),cbe(e,n,t),null}function EHe(e,n){var t,i;for(i=e.a.length,n.lengthi&&tr(n,i,null),n}function Na(e,n){var t,i;for(i=e.c.length,n.lengthi&&tr(n,i,null),n}function MQ(e,n,t,i){var r;if(r=e.length,n>=r)return r;for(n=n>0?n:0;n0&&(Te(e.b,new BNe(n.a,t)),i=n.a.length,0i&&(n.a+=_Ce(oe(Vl,ph,30,-i,15,1))))}function AHe(e,n,t){var i,r,c;if(!t[n.d])for(t[n.d]=!0,r=new L(H3(n));r.a=e.b>>1)for(i=e.c,t=e.b;t>n;--t)i=i.b;else for(i=e.a.a,t=0;t=0?e.Th(r):wZ(e,i)):t<0?wZ(e,i):u(i,69).uk().zk(e,e.ei(),t)}function CHe(e){var n,t,i;for(i=(!e.o&&(e.o=new us((Hu(),u1),qd,e,0)),e.o),t=i.c.Jc();t.e!=t.i.gc();)n=u(t.Wj(),45),n.kd();return QC(i)}function Ie(e){var n;if(q(e.a,4)){if(n=ade(e.a),n==null)throw P(new qc(uZe+e.b+"'. "+cZe+(E1(QD),QD.k)+vpe));return n}else return e.a}function Ujn(e){var n;if(e==null)return null;if(n=lRn(ho(e,!0)),n==null)throw P(new jX("Invalid base64Binary value: '"+e+"'"));return n}function lt(e){var n;try{return n=e.i.Xb(e.e),e.Vj(),e.g=e.e++,n}catch(t){throw t=or(t),q(t,99)?(e.Vj(),P(new fu)):P(t)}}function OQ(e){var n;try{return n=e.c.Ti(e.e),e.Vj(),e.g=e.e++,n}catch(t){throw t=or(t),q(t,99)?(e.Vj(),P(new fu)):P(t)}}function nz(e){var n,t,i,r;for(r=0,t=0,i=e.length;t=64&&n<128&&(r=sh(r,Bh(1,n-64)));return r}function tz(e,n){var t,i;return i=null,di(e,(Ht(),N6))&&(t=u(T(e,N6),105),t.nf(n)&&(i=t.mf(n))),i==null&&Ir(e)&&(i=T(Ir(e),n)),i}function Xjn(e,n){var t;return t=u(T(e,(Ce(),Qc)),78),MK(n,Utn)?t?Js(t):(t=new Ss,fe(e,Qc,t)):t&&fe(e,Qc,null),t}function Kjn(e,n){var t,i,r;for(r=new So(n.gc()),i=n.Jc();i.Ob();)t=u(i.Pb(),294),t.c==t.f?o8(e,t,t.c):sxn(e,t)||Jn(r.c,t);return r}function OHe(e,n){var t,i,r;for(t=e.o,r=u(u(wi(e.r,n),22),83).Jc();r.Ob();)i=u(r.Pb(),115),i.e.a=YSn(i,t.a),i.e.b=t.b*W(ie(i.b.mf(bH)))}function Vjn(e,n){var t,i,r,c;return r=e.k,t=W(ie(T(e,(pe(),Zw)))),c=n.k,i=W(ie(T(n,Zw))),c!=(zn(),gr)?-1:r!=gr?1:t==i?0:tt.b)return!0}return!1}function IHe(e){var n;return n=new l0,n.a+="n",e.k!=(zn(),Qi)&&Gt(Gt((n.a+="(",n),DK(e.k).toLowerCase()),")"),Gt((n.a+="_",n),IO(e)),n.a}function Ij(){Ij=Y,S4e=new oC(C2e,0),Xie=new oC(WW,1),Kie=new oC("LINEAR_SEGMENTS",2),hA=new oC("BRANDES_KOEPF",3),dA=new oC(OQe,4)}function $4(e,n,t,i){var r;return t>=0?e.Ph(n,t,i):(e.Mh()&&(i=(r=e.Ch(),r>=0?e.xh(i):e.Mh().Qh(e,-1-r,null,i))),e.zh(n,t,i))}function pde(e,n){switch(n){case 7:!e.e&&(e.e=new Cn(wr,e,7,4)),vt(e.e);return;case 8:!e.d&&(e.d=new Cn(wr,e,8,5)),vt(e.d);return}rde(e,n)}function yi(e,n,t){return t==null?(!e.o&&(e.o=new us((Hu(),u1),qd,e,0)),QB(e.o,n)):(!e.o&&(e.o=new us((Hu(),u1),qd,e,0)),PO(e.o,n,t)),e}function Ku(e,n){var t;t=e.dd(n);try{return t.Pb()}catch(i){throw i=or(i),q(i,112)?P(new ko("Can't get element "+n)):P(i)}}function _He(e,n){var t;switch(t=u(Rc(e.b,n),127).n,n.g){case 1:e.t>=0&&(t.d=e.t);break;case 3:e.t>=0&&(t.a=e.t)}e.C&&(t.b=e.C.b,t.c=e.C.c)}function tSn(e){var n;n=e.a;do n=u(tt(new qn(Vn(rr(n).a.Jc(),new ne))),17).c.i,n.k==(zn(),hr)&&e.b.Ec(n);while(n.k==(zn(),hr));e.b=Us(e.b)}function LHe(e,n){var t,i,r;for(r=e,i=new qn(Vn(rr(n).a.Jc(),new ne));at(i);)t=u(tt(i),17),t.c.i.c&&(r=E.Math.max(r,t.c.i.c.p));return r}function iSn(e,n){var t,i,r;for(r=0,i=u(u(wi(e.r,n),22),83).Jc();i.Ob();)t=u(i.Pb(),115),r+=t.d.d+t.b.Kf().b+t.d.a,i.Ob()&&(r+=e.w);return r}function rSn(e,n){var t,i,r;for(r=0,i=u(u(wi(e.r,n),22),83).Jc();i.Ob();)t=u(i.Pb(),115),r+=t.d.b+t.b.Kf().a+t.d.c,i.Ob()&&(r+=e.w);return r}function PHe(e){var n,t,i,r;if(i=0,r=Np(e),r.c.length==0)return 1;for(t=new L(r);t.a=0?e.Ih(o,t,!0):Ow(e,c,t)):u(c,69).uk().wk(e,e.ei(),r,t,i)}function oSn(e,n,t,i){var r,c;c=n.nf((Ht(),Rv))?u(n.mf(Rv),22):e.j,r=eEn(c),r!=(Qz(),ate)&&(t&&!lde(r)||k0e(EOn(e,r,i),n))}function NQ(e,n){return Pr(e)?!!Ien[n]:e.Qm?!!e.Qm[n]:K2(e)?!!Den[n]:X2(e)?!!Nen[n]:!1}function sSn(e){switch(e.g){case 1:return kw(),qN;case 3:return kw(),GN;case 2:return kw(),dte;case 4:return kw(),hte;default:return null}}function lSn(e,n,t){if(e.e)switch(e.b){case 1:S5n(e.c,n,t);break;case 0:A5n(e.c,n,t)}else ZLe(e.c,n,t);e.a[n.p][t.p]=e.c.i,e.a[t.p][n.p]=e.c.e}function RHe(e){var n,t;if(e==null)return null;for(t=oe(e1,je,199,e.length,0,2),n=0;nc?1:0):0}function Ap(){Ap=Y,jJ=new a$(aa,0),qie=new a$("PORT_POSITION",1),Ov=new a$("NODE_SIZE_WHERE_SPACE_PERMITS",2),Cv=new a$("NODE_SIZE",3)}function fSn(e,n){var t,i,r;for(n.Tg("Untreeify",1),t=u(T(e,(xi(),a6e)),16),r=t.Jc();r.Ob();)i=u(r.Pb(),65),qt(i.b.d,i),qt(i.c.b,i);n.Ug()}function Gh(){Gh=Y,ice=new xE("AUTOMATIC",0),TD=new xE(Z4,1),xD=new xE(W4,2),WJ=new xE("TOP",3),QJ=new xE(Uge,4),ZJ=new xE(T8,5)}function q3(e,n,t){var i,r;if(r=e.gc(),n>=r)throw P(new ep(n,r));if(e.Qi()&&(i=e.bd(t),i>=0&&i!=n))throw P(new Gn(LN));return e.Vi(n,t)}function Cd(e,n){var t,i,r;if(r=yJe(e,n),r>=0)return r;if(e.ml()){for(i=0;i0||e==(pX(),Gne)||n==(mX(),qne))throw P(new Gn("Invalid range: "+QLe(e,n)))}function vde(e,n,t,i){h8();var r,c;for(r=0,c=0;c0),(n&-n)==n)return sc(n*Ds(e,31)*4656612873077393e-25);do t=Ds(e,31),i=t%n;while(t-i+(n-1)<0);return sc(i)}function aSn(e,n){var t,i,r;for(t=rw(new jb,e),r=new L(n);r.a1&&(c=aSn(e,n)),c}function wSn(e){var n,t,i;for(n=0,i=new L(e.c.a);i.a102?-1:e<=57?e-48:e<65?-1:e<=70?e-65+10:e<97?-1:e-97+10}function BQ(e,n){if(e==null)throw P(new K5("null key in entry: null="+n));if(n==null)throw P(new K5("null value in entry: "+e+"=null"))}function UHe(e,n){var t;return t=z(B(Fr,1),zc,30,15,[rQ(e.a[0],n),rQ(e.a[1],n),rQ(e.a[2],n)]),e.d&&(t[0]=E.Math.max(t[0],t[2]),t[2]=t[0]),t}function XHe(e,n){var t;return t=z(B(Fr,1),zc,30,15,[$B(e.a[0],n),$B(e.a[1],n),$B(e.a[2],n)]),e.d&&(t[0]=E.Math.max(t[0],t[2]),t[2]=t[0]),t}function jde(e,n,t){f4(u(T(n,(Ce(),Zi)),102))||(Rae(e,n,Od(n,t)),Rae(e,n,Od(n,(Oe(),dt))),Rae(e,n,Od(n,Xn)),vn(),xr(n.j,new UEe(e)))}function KHe(e){var n,t;for(e.c||pPn(e),t=new Ss,n=new L(e.a),_(n);n.a0&&(Yn(0,n.length),n.charCodeAt(0)==43)?(Yn(1,n.length+1),n.substr(1)):n))}function ISn(e){var n;return e==null?null:new g0((n=ho(e,!0),n.length>0&&(Yn(0,n.length),n.charCodeAt(0)==43)?(Yn(1,n.length+1),n.substr(1)):n))}function Ade(e,n,t,i,r,c,o,l){var f,h;i&&(f=i.a[0],f&&Ade(e,n,t,f,r,c,o,l),KQ(e,t,i.d,r,c,o,l)&&n.Ec(i),h=i.a[1],h&&Ade(e,n,t,h,r,c,o,l))}function _j(e,n){var t,i,r,c;for(c=e.gc(),n.lengthc&&tr(n,c,null),n}function _Sn(e,n){var t,i;if(i=e.gc(),n==null){for(t=0;t0&&(f+=r),h[b]=o,o+=l*(f+i)}function FSn(e){var n;for(n=0;n0?e.c:0),++r;e.b=i,e.d=c}function uJe(e,n){var t;return t=z(B(Fr,1),zc,30,15,[yde(e,(sa(),xu),n),yde(e,Oo,n),yde(e,Cu,n)]),e.f&&(t[0]=E.Math.max(t[0],t[2]),t[2]=t[0]),t}function oJe(e){var n;di(e,(Ce(),e2))&&(n=u(T(e,e2),22),n.Gc((Op(),Gf))?(n.Kc(Gf),n.Ec(qf)):n.Gc(qf)&&(n.Kc(qf),n.Ec(Gf)))}function sJe(e){var n;di(e,(Ce(),e2))&&(n=u(T(e,e2),22),n.Gc((Op(),Xf))?(n.Kc(Xf),n.Ec(bf)):n.Gc(bf)&&(n.Kc(bf),n.Ec(Xf)))}function qQ(e,n,t,i){var r,c,o,l;return e.a==null&&RTn(e,n),o=n.b.j.c.length,c=t.d.p,l=i.d.p,r=l-1,r<0&&(r=o-1),c<=r?e.a[r]-e.a[c]:e.a[o-1]-e.a[c]+e.a[r]}function HSn(e){var n;for(n=0;n0&&(r.b+=n),r}function az(e,n){var t,i,r;for(r=new Kr,i=e.Jc();i.Ob();)t=u(i.Pb(),37),d8(t,0,r.b),r.b+=t.f.b+n,r.a=E.Math.max(r.a,t.f.a);return r.a>0&&(r.a+=n),r}function fJe(e,n){var t,i;if(n.length==0)return 0;for(t=EV(e.a,n[0],(Oe(),Kn)),t+=EV(e.a,n[n.length-1],Wn),i=0;i>16==6?e.Cb.Qh(e,5,ma,n):(i=xc(u(Sn((t=u(Un(e,16),29),t||e.fi()),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function KSn(e){j9();var n=e.e;if(n&&n.stack){var t=n.stack,i=n+` -`;return t.substring(0,i.length)==i&&(t=t.substring(i.length)),t.split(` -`)}return[]}function VSn(e){var n;return n=(vBe(),qen),n[e>>>28]|n[e>>24&15]<<4|n[e>>20&15]<<8|n[e>>16&15]<<12|n[e>>12&15]<<16|n[e>>8&15]<<20|n[e>>4&15]<<24|n[e&15]<<28}function hJe(e){var n,t,i;e.b==e.c&&(i=e.a.length,t=c1e(E.Math.max(8,i))<<1,e.b!=0?(n=Tf(e.a,t),SBe(e,n,i),e.a=n,e.b=0):$2(e.a,t),e.c=i)}function YSn(e,n){var t;return t=e.b,t.nf((Ht(),Ls))?t.$f()==(Oe(),Kn)?-t.Kf().a-W(ie(t.mf(Ls))):n+W(ie(t.mf(Ls))):t.$f()==(Oe(),Kn)?-t.Kf().a:n}function IO(e){var n;return e.b.c.length!=0&&u(_e(e.b,0),70).a?u(_e(e.b,0),70).a:(n=MV(e),n??""+(e.c?gu(e.c.a,e,0):-1))}function hz(e){var n;return e.f.c.length!=0&&u(_e(e.f,0),70).a?u(_e(e.f,0),70).a:(n=MV(e),n??""+(e.i?gu(e.i.j,e,0):-1))}function QSn(e,n){var t,i;if(n<0||n>=e.gc())return null;for(t=n;t0?e.c:0),r=E.Math.max(r,n.d),++i;e.e=c,e.b=r}function ZSn(e){var n,t;if(!e.b)for(e.b=RR(u(e.f,125).jh().i),t=new ct(u(e.f,125).jh());t.e!=t.i.gc();)n=u(lt(t),157),Te(e.b,new kX(n));return e.b}function WSn(e,n){var t,i,r;if(n.dc())return f9(),f9(),ZD;for(t=new BOe(e,n.gc()),r=new ct(e);r.e!=r.i.gc();)i=lt(r),n.Gc(i)&&kt(t,i);return t}function xde(e,n,t,i){return n==0?i?(!e.o&&(e.o=new us((Hu(),u1),qd,e,0)),e.o):(!e.o&&(e.o=new us((Hu(),u1),qd,e,0)),QC(e.o)):rz(e,n,t,i)}function XQ(e){var n,t;if(e.rb)for(n=0,t=e.rb.i;n>22),r+=i>>22,r<0)?!1:(e.l=t&_s,e.m=i&_s,e.h=r&B1,!0)}function KQ(e,n,t,i,r,c,o){var l,f;return!(n.Re()&&(f=e.a.Le(t,i),f<0||!r&&f==0)||n.Se()&&(l=e.a.Le(t,c),l>0||!o&&l==0))}function iAn(e,n){F9();var t;if(t=e.j.g-n.j.g,t!=0)return 0;switch(e.j.g){case 2:return gQ(n,ive)-gQ(e,ive);case 4:return gQ(e,tve)-gQ(n,tve)}return 0}function rAn(e){switch(e.g){case 0:return Zte;case 1:return Wte;case 2:return eie;case 3:return nie;case 4:return UH;case 5:return tie;default:return null}}function Yc(e,n,t){var i,r;return i=(r=new gX,Xb(r,n),Mo(r,t),kt((!e.c&&(e.c=new we(c2,e,12,10)),e.c),r),r),jd(i,0),wp(i,1),xd(i,!0),Td(i,!0),i}function R4(e,n){var t,i;if(n>=e.i)throw P(new pK(n,e.i));return++e.j,t=e.g[n],i=e.i-n-1,i>0&&Yu(e.g,n+1,e.g,n,i),tr(e.g,--e.i,null),e.Oi(n,t),e.Li(),t}function dJe(e,n){var t,i;return e.Db>>16==17?e.Cb.Qh(e,21,wf,n):(i=xc(u(Sn((t=u(Un(e,16),29),t||e.fi()),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function cAn(e){var n,t,i,r;for(vn(),xr(e.c,e.a),r=new L(e.c);r.at.a.c.length))throw P(new Gn("index must be >= 0 and <= layer node count"));e.c&&Go(e.c.a,e),e.c=t,t&&xb(t.a,n,e)}function kJe(e,n){this.c=new gt,this.a=e,this.b=n,this.d=u(T(e,(pe(),Sv)),316),ue(T(e,(Ce(),Z5e)))===ue((tO(),XH))?this.e=new fAe:this.e=new lAe}function aAn(e,n){var t,i,r,c;for(c=0,i=new L(e);i.a0?n:0),++t;return new ke(i,r)}function hAn(e,n){var t,i;for(e.b=0,e.d=new LP,i=new L(n.a);i.a>16==6?e.Cb.Qh(e,6,wr,n):(i=xc(u(Sn((t=u(Un(e,16),29),t||(Hu(),hG)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function _de(e,n){var t,i;return e.Db>>16==7?e.Cb.Qh(e,1,XD,n):(i=xc(u(Sn((t=u(Un(e,16),29),t||(Hu(),M8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function Lde(e,n){var t,i;return e.Db>>16==9?e.Cb.Qh(e,9,$t,n):(i=xc(u(Sn((t=u(Un(e,16),29),t||(Hu(),x8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function SJe(e,n){var t,i;return e.Db>>16==5?e.Cb.Qh(e,9,kG,n):(i=xc(u(Sn((t=u(Un(e,16),29),t||(mn(),Xd)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function AJe(e,n){var t,i;return e.Db>>16==7?e.Cb.Qh(e,6,ma,n):(i=xc(u(Sn((t=u(Un(e,16),29),t||(mn(),Vd)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function Pde(e,n){var t,i;return e.Db>>16==3?e.Cb.Qh(e,0,VD,n):(i=xc(u(Sn((t=u(Un(e,16),29),t||(mn(),Ud)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function $de(e,n){var t,i;return e.Db>>16==3?e.Cb.Qh(e,12,$t,n):(i=xc(u(Sn((t=u(Un(e,16),29),t||(Hu(),A8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function wAn(e,n,t){var i,r,c;for(t<0&&(t=0),c=e.i,r=t;rDW)return t8(e,i);if(i==e)return!0}}return!1}function mAn(e){switch(B$(),e.q.g){case 5:hqe(e,(Oe(),Xn)),hqe(e,dt);break;case 4:mUe(e,(Oe(),Xn)),mUe(e,dt);break;default:yVe(e,(Oe(),Xn)),yVe(e,dt)}}function vAn(e){switch(B$(),e.q.g){case 5:Nqe(e,(Oe(),Wn)),Nqe(e,Kn);break;case 4:OHe(e,(Oe(),Wn)),OHe(e,Kn);break;default:kVe(e,(Oe(),Wn)),kVe(e,Kn)}}function yAn(e){var n,t;n=u(T(e,($f(),dtn)),15),n?(t=n.a,t==0?fe(e,(S0(),mH),new bQ):fe(e,(S0(),mH),new qR(t))):fe(e,(S0(),mH),new qR(1))}function kAn(e,n){var t;switch(t=e.i,n.g){case 1:return-(e.n.b+e.o.b);case 2:return e.n.a-t.o.a;case 3:return e.n.b-t.o.b;case 4:return-(e.n.a+e.o.a)}return 0}function EAn(e,n){switch(e.g){case 0:return n==(qs(),G1)?RH:BH;case 1:return n==(qs(),G1)?RH:YN;case 2:return n==(qs(),G1)?YN:BH;default:return YN}}function LO(e,n){var t,i,r;for(Go(e.a,n),e.e-=n.r+(e.a.c.length==0?0:e.c),r=Bee,i=new L(e.a);i.a>16==11?e.Cb.Qh(e,10,$t,n):(i=xc(u(Sn((t=u(Un(e,16),29),t||(Hu(),T8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function MJe(e,n){var t,i;return e.Db>>16==10?e.Cb.Qh(e,11,wf,n):(i=xc(u(Sn((t=u(Un(e,16),29),t||(mn(),Kd)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function TJe(e,n){var t,i;return e.Db>>16==10?e.Cb.Qh(e,12,pf,n):(i=xc(u(Sn((t=u(Un(e,16),29),t||(mn(),Tm)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function xJe(e,n){var t,i,r,c,o;if(n)for(r=n.a.length,t=new Ob(r),o=(t.b-t.a)*t.c<0?(d0(),hb):new w0(t);o.Ob();)c=u(o.Pb(),15),i=A9(n,c.a),i&&hUe(e,i)}function CAn(){Uoe();var e,n;for(YRn((p0(),Rn)),zRn(Rn),XQ(Rn),H8e=(mn(),Ya),n=new L(Q8e);n.a>19,h=n.h>>19,f!=h?h-f:(r=e.h,l=n.h,r!=l?r-l:(i=e.m,o=n.m,i!=o?i-o:(t=e.l,c=n.l,t-c)))}function CJe(e,n,t){var i,r,c,o,l;for(r=e[t.g],l=new L(n.d);l.a0?e.b:0),++t;n.b=i,n.e=r}function OJe(e){var n,t,i;if(i=e.b,uTe(e.i,i.length)){for(t=i.length*2,e.b=oe(Une,aN,308,t,0,1),e.c=oe(Une,aN,308,t,0,1),e.f=t-1,e.i=0,n=e.a;n;n=n.c)JO(e,n,n);++e.g}}function $j(e,n){return e.b.a=E.Math.min(e.b.a,n.c),e.b.b=E.Math.min(e.b.b,n.d),e.a.a=E.Math.max(e.a.a,n.c),e.a.b=E.Math.max(e.a.b,n.d),Jn(e.c,n),!0}function NAn(e,n,t){var i;i=n.c.i,i.k==(zn(),hr)?(fe(e,(pe(),ga),u(T(i,ga),12)),fe(e,hf,u(T(i,hf),12))):(fe(e,(pe(),ga),n.c),fe(e,hf,t.d))}function i8(e,n,t){a8();var i,r,c,o,l,f;return o=n/2,c=t/2,i=E.Math.abs(e.a),r=E.Math.abs(e.b),l=1,f=1,i>o&&(l=o/i),r>c&&(f=c/r),k1(e,E.Math.min(l,f)),e}function DAn(){Hz();var e,n;try{if(n=u(Vde((h0(),mf),q8),2075),n)return n}catch(t){if(t=or(t),q(t,101))e=t,Dfe((Nt(),e));else throw P(t)}return new uU}function IAn(){Hz();var e,n;try{if(n=u(Vde((h0(),mf),lf),2002),n)return n}catch(t){if(t=or(t),q(t,101))e=t,Dfe((Nt(),e));else throw P(t)}return new Ug}function _An(){I$e();var e,n;try{if(n=u(Vde((h0(),mf),og),2084),n)return n}catch(t){if(t=or(t),q(t,101))e=t,Dfe((Nt(),e));else throw P(t)}return new cx}function LAn(e,n,t){var i,r;return r=e.e,e.e=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Dr(e,1,4,r,n),t?t.lj(i):t=i),r!=n&&(n?t=m8(e,xz(e,n),t):t=m8(e,e.a,t)),t}function NJe(){e$.call(this),this.e=-1,this.a=!1,this.p=Ur,this.k=-1,this.c=-1,this.b=-1,this.g=!1,this.f=-1,this.j=-1,this.n=-1,this.i=-1,this.d=-1,this.o=Ur}function PAn(e,n){var t,i,r;if(i=e.b.d.d,e.a||(i+=e.b.d.a),r=n.b.d.d,n.a||(r+=n.b.d.a),t=vi(i,r),t==0){if(!e.a&&n.a)return-1;if(!n.a&&e.a)return 1}return t}function $An(e,n){var t,i,r;if(i=e.b.b.d,e.a||(i+=e.b.b.a),r=n.b.b.d,n.a||(r+=n.b.b.a),t=vi(i,r),t==0){if(!e.a&&n.a)return-1;if(!n.a&&e.a)return 1}return t}function RAn(e,n){var t,i,r;if(i=e.b.g.d,e.a||(i+=e.b.g.a),r=n.b.g.d,n.a||(r+=n.b.g.a),t=vi(i,r),t==0){if(!e.a&&n.a)return-1;if(!n.a&&e.a)return 1}return t}function zde(){zde=Y,Ntn=Eo(Bt(Bt(Bt(new ur,(Br(),Wu),(qr(),H3e)),Wu,J3e),_c,G3e),_c,N3e),Itn=Bt(Bt(new ur,Wu,S3e),Wu,D3e),Dtn=Eo(new ur,_c,_3e)}function BAn(e){var n,t,i,r,c;for(n=u(T(e,(pe(),QS)),92),c=e.n,i=n.Bc().Jc();i.Ob();)t=u(i.Pb(),318),r=t.i,r.c+=c.a,r.d+=c.b,t.c?iXe(t):rXe(t);fe(e,QS,null)}function zAn(e,n,t){var i,r;switch(r=e.b,i=r.d,n.g){case 1:return-i.d-t;case 2:return r.o.a+i.c+t;case 3:return r.o.b+i.a+t;case 4:return-i.b-t;default:return-1}}function DJe(e,n){var t,i;for(i=new L(n);i.a0&&(o=(c&ri)%e.d.length,r=J0e(e,o,c,n),r)?(l=r.ld(t),l):(i=e.ak(c,n,t),e.c.Ec(i),null)}function Jde(e,n){var t,i,r,c;switch(Md(e,n).Il()){case 3:case 2:{for(t=W3(n),r=0,c=t.i;r=0;i--)if(hn(e[i].d,n)||hn(e[i].d,t)){e.length>=i+1&&e.splice(0,i+1);break}return e}function $O(e,n){var t;return uu(e)&&uu(n)&&(t=e/n,bN0&&(e.b+=2,e.a+=i):(e.b+=1,e.a+=E.Math.min(i,r))}function BJe(e,n){var t,i;if(i=!1,Pr(n)&&(i=!0,p4(e,new up(Dt(n)))),i||q(n,242)&&(i=!0,p4(e,(t=zK(u(n,242)),new f3(t)))),!i)throw P(new EX($pe))}function iMn(e,n,t,i){var r,c,o;return r=new O1(e.e,1,10,(o=n.c,q(o,88)?u(o,29):(mn(),vf)),(c=t.c,q(c,88)?u(c,29):(mn(),vf)),Cd(e,n),!1),i?i.lj(r):i=r,i}function Ude(e){var n,t;switch(u(T(Ir(e),(Ce(),G5e)),420).g){case 0:return n=e.n,t=e.o,new ke(n.a+t.a/2,n.b+t.b/2);case 1:return new gc(e.n);default:return null}}function RO(){RO=Y,KH=new AE(aa,0),yve=new AE("LEFTUP",1),Eve=new AE("RIGHTUP",2),vve=new AE("LEFTDOWN",3),kve=new AE("RIGHTDOWN",4),iie=new AE("BALANCED",5)}function rMn(e,n,t){var i,r,c;if(i=vi(e.a[n.p],e.a[t.p]),i==0){if(r=u(T(n,(pe(),p6)),16),c=u(T(t,p6),16),r.Gc(t))return-1;if(c.Gc(n))return 1}return i}function cMn(e){switch(e.g){case 1:return new I_;case 2:return new jk;case 3:return new T5;case 0:return null;default:throw P(new Gn(Uee+(e.f!=null?e.f:""+e.g)))}}function Xde(e,n,t){switch(n){case 1:!e.n&&(e.n=new we(ku,e,1,7)),vt(e.n),!e.n&&(e.n=new we(ku,e,1,7)),er(e.n,u(t,18));return;case 2:I9(e,Dt(t));return}b1e(e,n,t)}function Kde(e,n,t){switch(n){case 3:mw(e,W(ie(t)));return;case 4:vw(e,W(ie(t)));return;case 5:Cs(e,W(ie(t)));return;case 6:Os(e,W(ie(t)));return}Xde(e,n,t)}function dz(e,n,t){var i,r,c;c=(i=new gX,i),r=Ia(c,n,null),r&&r.mj(),Mo(c,t),kt((!e.c&&(e.c=new we(c2,e,12,10)),e.c),c),jd(c,0),wp(c,1),xd(c,!0),Td(c,!0)}function Vde(e,n){var t,i,r;return t=EE(e.i,n),q(t,241)?(r=u(t,241),r.wi()==null,r.ti()):q(t,493)?(i=u(t,1999),r=i.b,r):null}function uMn(e,n,t,i){var r,c;return Tt(n),Tt(t),c=u(GE(e.d,n),15),bRe(!!c,"Row %s not in %s",n,e.e),r=u(GE(e.b,t),15),bRe(!!r,"Column %s not in %s",t,e.c),dze(e,c.a,r.a,i)}function oMn(e){var n,t,i,r,c,o;for(t=null,r=e,c=0,o=r.length;c1||l==-1?(c=u(f,16),r.Wb(GEn(e,c))):r.Wb(LZ(e,u(f,57)))))}function bMn(e,n,t,i){aTe();var r=Hne;function c(){for(var o=0;o0)return!1;return!0}function pMn(e){switch(u(T(e.b,(Ce(),$5e)),381).g){case 1:Wi(jo(ou(new bn(null,new wn(e.d,16)),new Bg),new e_),new WM);break;case 2:KDn(e);break;case 0:Fxn(e)}}function mMn(e,n,t){var i,r,c;for(i=t,!i&&(i=new U5),i.Tg("Layout",e.a.c.length),c=new L(e.a);c.aHee)return t;r>-1e-6&&++t}return t}function gz(e,n,t){if(q(n,271))return qOn(e,u(n,85),t);if(q(n,276))return jAn(e,u(n,276),t);throw P(new Gn(U8+_a(new Eu(z(B(Mr,1),xn,1,5,[n,t])))))}function wz(e,n,t){if(q(n,271))return UOn(e,u(n,85),t);if(q(n,276))return SAn(e,u(n,276),t);throw P(new Gn(U8+_a(new Eu(z(B(Mr,1),xn,1,5,[n,t])))))}function Qde(e,n){var t;n!=e.b?(t=null,e.b&&(t=NR(e.b,e,-4,t)),n&&(t=$4(n,e,-4,t)),t=sFe(e,n,t),t&&t.mj()):(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,3,n,n))}function JJe(e,n){var t;n!=e.f?(t=null,e.f&&(t=NR(e.f,e,-1,t)),n&&(t=$4(n,e,-1,t)),t=lFe(e,n,t),t&&t.mj()):(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,0,n,n))}function jMn(e,n,t,i){var r,c,o,l;return Bs(e.e)&&(r=n.Jk(),l=n.kd(),c=t.kd(),o=v0(e,1,r,l,c,r.Hk()?g8(e,r,c,q(r,103)&&(u(r,19).Bb&kc)!=0):-1,!0),i?i.lj(o):i=o),i}function GJe(e){var n,t,i;if(e==null)return null;if(t=u(e,16),t.dc())return"";for(i=new ad,n=t.Jc();n.Ob();)$c(i,(ki(),Dt(n.Pb()))),i.a+=" ";return wK(i,i.a.length-1)}function qJe(e){var n,t,i;if(e==null)return null;if(t=u(e,16),t.dc())return"";for(i=new ad,n=t.Jc();n.Ob();)$c(i,(ki(),Dt(n.Pb()))),i.a+=" ";return wK(i,i.a.length-1)}function SMn(e,n){var t,i,r,c,o;for(c=new L(n.a);c.a0&&ic(e,e.length-1)==33)try{return n=uUe(rf(e,0,e.length-1)),n.e==null}catch(t){if(t=or(t),!q(t,32))throw P(t)}return!1}function xMn(e,n,t){var i,r,c;switch(i=Ir(n),r=HB(i),c=new Vu,bu(c,n),t.g){case 1:Ar(c,TO(_4(r)));break;case 2:Ar(c,_4(r))}return fe(c,(Ce(),cm),ie(T(e,cm))),c}function Zde(e){var n,t;return n=u(tt(new qn(Vn(rr(e.a).a.Jc(),new ne))),17),t=u(tt(new qn(Vn(Ni(e.a).a.Jc(),new ne))),17),$e(Pe(T(n,(pe(),$d))))||$e(Pe(T(t,$d)))}function Mp(){Mp=Y,QN=new cC("ONE_SIDE",0),HH=new cC("TWO_SIDES_CORNER",1),JH=new cC("TWO_SIDES_OPPOSING",2),FH=new cC("THREE_SIDES",3),zH=new cC("FOUR_SIDES",4)}function KJe(e,n){var t,i,r,c;for(c=new xe,r=0,i=n.Jc();i.Ob();){for(t=me(u(i.Pb(),15).a+r);t.a=e.f)break;Jn(c.c,t)}return c}function CMn(e){var n,t;for(t=new L(e.e.b);t.a0&&gJe(this,this.c-1,(Oe(),Wn)),this.c0&&e[0].length>0&&(this.c=$e(Pe(T(Ir(e[0][0]),(pe(),Rve))))),this.a=oe(ron,je,2079,e.length,0,2),this.b=oe(con,je,2080,e.length,0,2),this.d=new tFe}function IMn(e){return e.c.length==0?!1:(pn(0,e.c.length),u(e.c[0],17)).c.i.k==(zn(),hr)?!0:I3(jo(new bn(null,new wn(e,16)),new ak),new c_)}function QJe(e,n){var t,i,r,c,o,l,f;for(l=Np(n),c=n.f,f=n.g,o=E.Math.sqrt(c*c+f*f),r=0,i=new L(l);i.a=0?(t=$O(e,eF),i=yQ(e,eF)):(n=Nb(e,1),t=$O(n,5e8),i=yQ(n,5e8),i=pc(Bh(i,1),$r(e,1))),sh(Bh(i,32),$r(t,Nc))}function qMn(e,n,t,i){var r,c,o,l,f;for(r=null,c=0,l=new L(n);l.a1;n>>=1)(n&1)!=0&&(i=D3(i,t)),t.d==1?t=D3(t,t):t=new wHe(GXe(t.a,t.d,oe(It,Zt,30,t.d<<1,15,1)));return i=D3(i,t),i}function o0e(){o0e=Y;var e,n,t,i;for(Pme=oe(Fr,zc,30,25,15,1),$me=oe(Fr,zc,30,33,15,1),i=152587890625e-16,n=32;n>=0;n--)$me[n]=i,i*=.5;for(t=1,e=24;e>=0;e--)Pme[e]=t,t*=.5}function YMn(e){var n,t;if($e(Pe(ve(e,(Ce(),im))))){for(t=new qn(Vn(L0(e).a.Jc(),new ne));at(t);)if(n=u(tt(t),85),Cw(n)&&$e(Pe(ve(n,dg))))return!0}return!1}function eGe(e){var n,t,i,r;for(n=new ji,t=new ji,r=Et(e,0);r.b!=r.d.c;)i=u(yt(r),12),i.e.c.length==0?Xi(t,i,t.c.b,t.c):Xi(n,i,n.c.b,n.c);return Us(n).Fc(t),n}function nGe(e,n){var t,i,r;ar(e.f,n)&&(n.b=e,i=n.c,gu(e.j,i,0)!=-1||Te(e.j,i),r=n.d,gu(e.j,r,0)!=-1||Te(e.j,r),t=n.a.b,t.c.length!=0&&(!e.i&&(e.i=new yHe(e)),A7n(e.i,t)))}function QMn(e){var n,t,i,r,c;return t=e.c.d,i=t.j,r=e.d.d,c=r.j,i==c?t.p=0&&hn(e.substr(n,3),"GMT")||n>=0&&hn(e.substr(n,3),"UTC"))&&(t[0]=n+3),Gbe(e,t,i)}function WMn(e,n){var t,i,r,c,o;for(c=e.g.a,o=e.g.b,i=new L(e.d);i.at;c--)e[c]|=n[c-t-1]>>>o,e[c-1]=n[c-t-1]<0&&Yu(e.g,n,e.g,n+i,l),o=t.Jc(),e.i+=i,r=0;r>4&15,c=e[i]&15,o[r++]=C8e[t],o[r++]=C8e[c];return ah(o,0,o.length)}function Uo(e){var n,t;return e>=kc?(n=gN+(e-kc>>10&1023)&yr,t=56320+(e-kc&1023)&yr,String.fromCharCode(n)+(""+String.fromCharCode(t))):String.fromCharCode(e&yr)}function lTn(e,n){Z2();var t,i,r,c;return r=u(u(wi(e.r,n),22),83),r.gc()>=2?(i=u(r.Jc().Pb(),115),t=e.u.Gc((ws(),qA)),c=e.u.Gc(_6),!i.a&&!t&&(r.gc()==2||c)):!1}function cGe(e,n,t,i,r){var c,o,l;for(c=VUe(e,n,t,i,r),l=!1;!c;)Mz(e,r,!0),l=!0,c=VUe(e,n,t,i,r);l&&Mz(e,r,!1),o=qY(r),o.c.length!=0&&(e.d&&e.d.Fg(o),cGe(e,r,t,i,o))}function vz(){vz=Y,Pre=new p$("NODE_SIZE_REORDERER",0),Ire=new p$("INTERACTIVE_NODE_REORDERER",1),Lre=new p$("MIN_SIZE_PRE_PROCESSOR",2),_re=new p$("MIN_SIZE_POST_PROCESSOR",3)}function yz(){yz=Y,Ece=new OE(aa,0),Y9e=new OE("DIRECTED",1),Z9e=new OE("UNDIRECTED",2),K9e=new OE("ASSOCIATION",3),Q9e=new OE("GENERALIZATION",4),V9e=new OE("DEPENDENCY",5)}function fTn(e,n){var t;if(!Ma(e))throw P(new qc(OZe));switch(t=Ma(e),n.g){case 1:return-(e.j+e.f);case 2:return e.i-t.g;case 3:return e.j-t.f;case 4:return-(e.i+e.g)}return 0}function aTn(e,n,t){var i,r,c;return i=n.Jk(),c=n.kd(),r=i.Hk()?v0(e,4,i,c,null,g8(e,i,c,q(i,103)&&(u(i,19).Bb&kc)!=0),!0):v0(e,i.rk()?2:1,i,c,i.gk(),-1,!0),t?t.lj(r):t=r,t}function c8(e,n){var t,i;for(_n(n),i=e.b.c.length,Te(e.b,n);i>0;){if(t=i,i=(i-1)/2|0,e.a.Le(_e(e.b,i),n)<=0)return il(e.b,t,n),!0;il(e.b,t,_e(e.b,i))}return il(e.b,i,n),!0}function f0e(e,n,t,i){var r,c;if(r=0,t)r=$B(e.a[t.g][n.g],i);else for(c=0;c=l)}function uGe(e){switch(e.g){case 0:return new G_;case 1:return new DT;default:throw P(new Gn("No implementation is available for the width approximator "+(e.f!=null?e.f:""+e.g)))}}function a0e(e,n,t,i){var r;if(r=!1,Pr(i)&&(r=!0,b9(n,t,Dt(i))),r||X2(i)&&(r=!0,a0e(e,n,t,i)),r||q(i,242)&&(r=!0,Lb(n,t,u(i,242))),!r)throw P(new EX($pe))}function dTn(e,n){var t,i,r;if(t=n.ni(e.a),t&&(r=la((!t.b&&(t.b=new Fs((mn(),Sc),Nu,t)),t.b),sf),r!=null)){for(i=1;i<(ss(),W8e).length;++i)if(hn(W8e[i],r))return i}return 0}function bTn(e,n){var t,i,r;if(t=n.ni(e.a),t&&(r=la((!t.b&&(t.b=new Fs((mn(),Sc),Nu,t)),t.b),sf),r!=null)){for(i=1;i<(ss(),e7e).length;++i)if(hn(e7e[i],r))return i}return 0}function oGe(e,n){var t,i,r,c;if(_n(n),c=e.a.gc(),c0?1:0;c.a[r]!=t;)c=c.a[r],r=e.a.Le(t.d,c.d)>0?1:0;c.a[r]=i,i.b=t.b,i.a[0]=t.a[0],i.a[1]=t.a[1],t.a[0]=null,t.a[1]=null}function pTn(e){var n,t,i,r;for(n=new xe,t=oe(ns,fa,30,e.a.c.length,16,1),xfe(t,t.length),r=new L(e.a);r.a0&&BXe((pn(0,t.c.length),u(t.c[0],25)),e),t.c.length>1&&BXe(u(_e(t,t.c.length-1),25),e),n.Ug()}function vTn(e){ws();var n,t;return n=Ti(K1,z(B(uG,1),ye,280,0,[ob])),!(dO(DR(n,e))>1||(t=Ti(qA,z(B(uG,1),ye,280,0,[GA,_6])),dO(DR(t,e))>1))}function d0e(e,n){var t;t=so((h0(),mf),e),q(t,493)?Xc(mf,e,new zxe(this,n)):Xc(mf,e,this),sZ(this,n),n==(Wy(),F8e)?(this.wb=u(this,2e3),u(n,2002)):this.wb=(p0(),Rn)}function yTn(e){var n,t,i;if(e==null)return null;for(n=null,t=0;tc}function aGe(e,n){var t,i,r;if(g0e(e,n))return!0;for(i=new L(n);i.a=r||n<0)throw P(new ko(Ene+n+cg+r));if(t>=r||t<0)throw P(new ko(jne+t+cg+r));return n!=t?i=(c=e.Aj(t),e.oj(n,c),c):i=e.vj(t),i}function dGe(e){var n,t,i;if(i=e,e)for(n=0,t=e.Bh();t;t=t.Bh()){if(++n>DW)return dGe(t);if(i=t,t==e)throw P(new qc("There is a cycle in the containment hierarchy of "+e))}return i}function _a(e){var n,t,i;for(i=new Jb(xo,"[","]"),t=e.Jc();t.Ob();)n=t.Pb(),x1(i,ue(n)===ue(e)?"(this Collection)":n==null?Ko:su(n));return i.a?i.e.length==0?i.a.a:i.a.a+(""+i.e):i.c}function g0e(e,n){var t,i;if(i=!1,n.gc()<2)return!1;for(t=0;t1&&(e.j.b+=e.e)):(e.j.a+=t.a,e.j.b=E.Math.max(e.j.b,t.b),e.d.c.length>1&&(e.j.a+=e.e))}function I0(){I0=Y,vin=z(B(jc,1),Ju,64,0,[(Oe(),Xn),Wn,dt]),min=z(B(jc,1),Ju,64,0,[Wn,dt,Kn]),yin=z(B(jc,1),Ju,64,0,[dt,Kn,Xn]),kin=z(B(jc,1),Ju,64,0,[Kn,Xn,Wn])}function gGe(e){var n,t,i,r,c,o,l,f,h;for(this.a=RHe(e),this.b=new xe,t=e,i=0,r=t.length;iLK(e.d).c?(e.i+=e.g.c,jQ(e.d)):LK(e.d).c>LK(e.g).c?(e.e+=e.d.c,jQ(e.g)):(e.i+=bDe(e.g),e.e+=bDe(e.d),jQ(e.g),jQ(e.d))}function CTn(e,n,t){var i,r,c,o;for(c=n.q,o=n.r,new Pb((ca(),eb),n,c,1),new Pb(eb,c,o,1),r=new L(t);r.al&&(f=l/i),r>c&&(h=c/r),o=E.Math.min(f,h),e.a+=o*(n.a-e.a),e.b+=o*(n.b-e.b)}function ITn(e,n,t,i,r){var c,o;for(o=!1,c=u(_e(t.b,0),26);P_n(e,n,c,i,r)&&(o=!0,vMn(t,c),t.b.c.length!=0);)c=u(_e(t.b,0),26);return t.b.c.length==0&&LO(t.j,t),o&&fz(n.q),o}function p0e(e,n,t,i){var r,c;return t==0?(!e.o&&(e.o=new us((Hu(),u1),qd,e,0)),G$(e.o,n,i)):(c=u(Sn((r=u(Un(e,16),29),r||e.fi()),t),69),c.uk().yk(e,_o(e),t-ht(e.fi()),n,i))}function sZ(e,n){var t;n!=e.sb?(t=null,e.sb&&(t=u(e.sb,52).Qh(e,1,KA,t)),n&&(t=u(n,52).Oh(e,1,KA,t)),t=O1e(e,n,t),t&&t.mj()):(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,4,n,n))}function vGe(e,n){var t,i,r,c;if(n)r=D1(n,"x"),t=new tSe(e),P3(t.a,(_n(r),r)),c=D1(n,"y"),i=new iSe(e),$3(i.a,(_n(c),c));else throw P(new ih("All edge sections need an end point."))}function yGe(e,n){var t,i,r,c;if(n)r=D1(n,"x"),t=new Wje(e),R3(t.a,(_n(r),r)),c=D1(n,"y"),i=new eSe(e),B3(i.a,(_n(c),c));else throw P(new ih("All edge sections need a start point."))}function _Tn(e,n){var t,i,r,c,o,l,f;for(i=zze(e),c=0,l=i.length;c>22-n,r=e.h<>22-n):n<44?(t=0,i=e.l<>44-n):(t=0,i=0,r=e.l<=Dd?"error":i>=900?"warn":i>=800?"info":"log"),oIe(t,e.a),e.b&&mbe(n,t,e.b,"Exception: ",!0))}function SGe(e,n){var t,i,r,c,o;for(r=n==1?Ete:kte,i=r.a.ec().Jc();i.Ob();)for(t=u(i.Pb(),86),o=u(wi(e.f.c,t),22).Jc();o.Ob();)c=u(o.Pb(),49),Te(e.b.b,u(c.b,82)),Te(e.b.a,u(c.b,82).d)}function AGe(e,n,t,i){var r,c,o,l,f;switch(f=e.b,c=n.d,o=c.j,l=wde(o,f.d[o.g],t),r=bi(wc(c.n),c.a),c.j.g){case 3:case 1:l.a+=r.a;break;case 2:l.b+=r.b;break;case 4:l.b+=r.b}Xi(i,l,i.c.b,i.c)}function RTn(e,n){var t,i,r,c;for(c=n.b.j,e.a=oe(It,Zt,30,c.c.length,15,1),r=0,i=0;ie)throw P(new Gn("k must be smaller than n"));return n==0||n==e?1:e==0?0:Gde(e)/(Gde(n)*Gde(e-n))}function m0e(e,n){var t,i,r,c;for(t=new kK(e);t.g==null&&!t.c?lae(t):t.g==null||t.i!=0&&u(t.g[t.i-1],50).Ob();)if(c=u(Tz(t),57),q(c,174))for(i=u(c,174),r=0;r>4],n[t*2+1]=MG[c&15];return ah(n,0,n.length)}function QTn(e){var n,t,i;switch(i=e.c.length,i){case 0:return SV(),Ren;case 1:return n=u(oqe(new L(e)),45),O2n(n.jd(),n.kd());default:return t=u(Na(e,oe(sg,Zz,45,e.c.length,0,1)),175),new Koe(t)}}function Od(e,n){switch(n.g){case 1:return a4(e.j,(os(),w3e));case 2:return a4(e.j,(os(),b3e));case 3:return a4(e.j,(os(),m3e));case 4:return a4(e.j,(os(),v3e));default:return vn(),vn(),Ec}}function ZTn(e,n){var t,i,r;t=A3n(n,e.e),i=u(Bn(e.g.f,t),15).a,r=e.a.c.length-1,e.a.c.length!=0&&u(_e(e.a,r),295).c==i?(++u(_e(e.a,r),295).a,++u(_e(e.a,r),295).b):Te(e.a,new mOe(i))}function _0(){_0=Y,qsn=(Ht(),O6),Usn=Jd,Fsn=yg,Hsn=Bv,Jsn=ib,zsn=Rv,z6e=ID,Gsn=pm,Tre=(Lbe(),Csn),xre=Osn,H6e=_sn,Cre=$sn,J6e=Lsn,G6e=Psn,F6e=Nsn,BJ=Dsn,zJ=Isn,ED=Rsn,q6e=Bsn,B6e=xsn}function TGe(e,n){var t,i,r,c,o;if(e.e<=n||r6n(e,e.g,n))return e.g;for(c=e.r,i=e.g,o=e.r,r=(c-i)/2+i;i+11&&(e.e.b+=e.a)):(e.e.a+=t.a,e.e.b=E.Math.max(e.e.b,t.b),e.d.c.length>1&&(e.e.a+=e.a))}function nxn(e){var n,t,i,r;switch(r=e.i,n=r.b,i=r.j,t=r.g,r.a.g){case 0:t.a=(e.g.b.o.a-i.a)/2;break;case 1:t.a=n.d.n.a+n.d.a.a;break;case 2:t.a=n.d.n.a+n.d.a.a-i.a;break;case 3:t.b=n.d.n.b+n.d.a.b}}function txn(e,n,t){var i,r,c;for(r=new qn(Vn(fh(t).a.Jc(),new ne));at(r);)i=u(tt(r),17),!cc(i)&&!(!cc(i)&&i.c.i.c==i.d.i.c)&&(c=kUe(e,i,t,new sAe),c.c.length>1&&Jn(n.c,c))}function OGe(e,n,t,i,r){if(ii&&(e.a=i),e.br&&(e.b=r),e}function ixn(e){if(q(e,144))return SNn(u(e,144));if(q(e,233))return LEn(u(e,233));if(q(e,21))return PTn(u(e,21));throw P(new Gn(U8+_a(new Eu(z(B(Mr,1),xn,1,5,[e])))))}function rxn(e,n,t,i,r){var c,o,l;for(c=!0,o=0;o>>r|t[o+i+1]<>>r,++o}return c}function E0e(e,n,t,i){var r,c,o;if(n.k==(zn(),hr)){for(c=new qn(Vn(rr(n).a.Jc(),new ne));at(c);)if(r=u(tt(c),17),o=r.c.i.k,o==hr&&e.c.a[r.c.i.c.p]==i&&e.c.a[n.c.p]==t)return!0}return!1}function cxn(e,n){var t,i,r,c;return n&=63,t=e.h&B1,n<22?(c=t>>>n,r=e.m>>n|t<<22-n,i=e.l>>n|e.m<<22-n):n<44?(c=0,r=t>>>n-22,i=e.m>>n-22|e.h<<44-n):(c=0,r=0,i=t>>>n-44),Io(i&_s,r&_s,c&B1)}function NGe(e,n,t,i){var r;this.b=i,this.e=e==(Ub(),pA),r=n[t],this.d=np(ns,[je,fa],[171,30],16,[r.length,r.length],2),this.a=np(It,[je,Zt],[54,30],15,[r.length,r.length],2),this.c=new i0e(n,t)}function uxn(e){var n,t,i;for(e.k=new gae((Oe(),z(B(jc,1),Ju,64,0,[yu,Xn,Wn,dt,Kn])).length,e.j.c.length),i=new L(e.j);i.a=t)return o8(e,n,i.p),!0;return!1}function V3(e,n,t,i){var r,c,o,l,f,h;for(o=t.length,c=0,r=-1,h=PRe((Yn(n,e.length+1),e.substr(n)),(JK(),_me)),l=0;lc&&wvn(h,PRe(t[l],_me))&&(r=l,c=f);return r>=0&&(i[0]=n+c),r}function lxn(e,n,t){var i,r,c,o,l,f,h,b;c=e.d.p,l=c.e,f=c.r,e.g=new TC(f),o=e.d.o.c.p,i=o>0?l[o-1]:oe(e1,Id,9,0,0,1),r=l[o],h=ot?D0e(e,t,"start index"):n<0||n>t?D0e(n,t,"end index"):Kj("end index (%s) must not be less than start index (%s)",z(B(Mr,1),xn,1,5,[me(n),me(e)]))}function PGe(e,n){var t,i,r,c;for(i=0,r=e.length;i0&&$Ge(e,c,t));n.p=0}function dxn(e){var n,t,i,r;for(n=Ib(Gt(new Ws("Predicates."),"and"),40),t=!0,r=new Gc(e);r.b=0?e.hi(r):P0e(e,i);else throw P(new Gn(G0+i.ve()+SS));else throw P(new Gn(FZe+n+HZe));else Rl(e,t,i)}function j0e(e){var n,t;if(t=null,n=!1,q(e,210)&&(n=!0,t=u(e,210).a),n||q(e,265)&&(n=!0,t=""+u(e,265).a),n||q(e,479)&&(n=!0,t=""+u(e,479).a),!n)throw P(new EX($pe));return t}function S0e(e,n,t){var i,r,c,o,l,f;for(f=Lo(e.e.Ah(),n),i=0,l=e.i,r=u(e.g,122),o=0;o=e.d.b.c.length&&(n=new qu(e.d),n.p=i.p-1,Te(e.d.b,n),t=new qu(e.d),t.p=i.p,Te(e.d.b,t)),Cr(i,u(_e(e.d.b,i.p),25))}function wxn(e){var n,t,i,r;for(t=new ji,fc(t,e.o),i=new LP;t.b!=0;)n=u(t.b==0?null:(ft(t.b!=0),_l(t,t.a.a)),500),r=TVe(e,n,!0),r&&Te(i.a,n);for(;i.a.c.length!=0;)n=u(y1e(i),500),TVe(e,n,!1)}function He(e){var n;this.c=new ji,this.f=e.e,this.e=e.d,this.i=e.g,this.d=e.c,this.b=e.b,this.k=e.j,this.a=e.a,e.i?this.j=e.i:this.j=(n=u(na(Ga),10),new Nl(n,u(Tf(n,n.length),10),0)),this.g=e.f}function Qb(){Qb=Y,Zye=new n4(lS,0),Sr=new n4("BOOLEAN",1),hc=new n4("INT",2),x6=new n4("STRING",3),Wr=new n4("DOUBLE",4),Ri=new n4("ENUM",5),T6=new n4("ENUMSET",6),qa=new n4("OBJECT",7)}function Bj(e,n){var t,i,r,c,o;i=E.Math.min(e.c,n.c),c=E.Math.min(e.d,n.d),r=E.Math.max(e.c+e.b,n.c+n.b),o=E.Math.max(e.d+e.a,n.d+n.a),r=(r/2|0))for(this.e=i?i.c:null,this.d=r;t++0;)ghe(this);this.b=n,this.a=null}function vxn(e,n){var t,i;n.a?HNn(e,n):(t=u(IX(e.b,n.b),60),t&&t==e.a[n.b.f]&&t.a&&t.a!=n.b.a&&t.c.Ec(n.b),i=u(DX(e.b,n.b),60),i&&e.a[i.f]==n.b&&i.a&&i.a!=n.b.a&&n.b.c.Ec(i),TK(e.b,n.b))}function GGe(e,n){var t,i;if(t=u(Rc(e.b,n),127),u(u(wi(e.r,n),22),83).dc()){t.n.b=0,t.n.c=0;return}t.n.b=e.C.b,t.n.c=e.C.c,e.A.Gc((Xs(),Eg))&&yXe(e,n),i=rSn(e,n),AZ(e,n)==(G3(),cb)&&(i+=2*e.w),t.a.a=i}function qGe(e,n){var t,i;if(t=u(Rc(e.b,n),127),u(u(wi(e.r,n),22),83).dc()){t.n.d=0,t.n.a=0;return}t.n.d=e.C.d,t.n.a=e.C.a,e.A.Gc((Xs(),Eg))&&kXe(e,n),i=iSn(e,n),AZ(e,n)==(G3(),cb)&&(i+=2*e.w),t.a.b=i}function yxn(e,n){var t,i,r,c;for(c=new xe,i=new L(n);i.ai&&(Yn(n-1,e.length),e.charCodeAt(n-1)<=32);)--n;return i>0||nt.a&&(i.Gc((Yb(),_A))?r=(n.a-t.a)/2:i.Gc(LA)&&(r=n.a-t.a)),n.b>t.b&&(i.Gc((Yb(),$A))?c=(n.b-t.b)/2:i.Gc(PA)&&(c=n.b-t.b)),h0e(e,r,c)}function YGe(e,n,t,i,r,c,o,l,f,h,b,p,y){q(e.Cb,88)&&Cp(Ms(u(e.Cb,88)),4),Mo(e,t),e.f=o,V9(e,l),Y9(e,f),X9(e,h),K9(e,b),xd(e,p),Q9(e,y),Td(e,!0),jd(e,r),e.Xk(c),Xb(e,n),i!=null&&(e.i=null,kB(e,i))}function D0e(e,n,t){if(e<0)return Kj(QVe,z(B(Mr,1),xn,1,5,[t,me(e)]));if(n<0)throw P(new Gn(ZVe+n));return Kj("%s (%s) must not be greater than size (%s)",z(B(Mr,1),xn,1,5,[t,me(e),me(n)]))}function I0e(e,n,t,i,r,c){var o,l,f,h;if(o=i-t,o<7){xEn(n,t,i,c);return}if(f=t+r,l=i+r,h=f+(l-f>>1),I0e(n,e,f,h,-r,c),I0e(n,e,h,l,-r,c),c.Le(e[h-1],e[h])<=0){for(;t=0?e.$h(c,t):abe(e,r,t);else throw P(new Gn(G0+r.ve()+SS));else throw P(new Gn(FZe+n+HZe));else Bl(e,i,r,t)}function QGe(e){var n,t;if(e.f){for(;e.n>0;){if(n=u(e.k.Xb(e.n-1),75),t=n.Jk(),q(t,103)&&(u(t,19).Bb&$u)!=0&&(!e.e||t.nk()!=D7||t.Jj()!=0)&&n.kd()!=null)return!0;--e.n}return!1}else return e.n>0}function ZGe(e){var n,t,i,r;if(t=u(e,52).Yh(),t)try{if(i=null,n=l8((h0(),mf),JXe(PEn(t))),n&&(r=n.Zh(),r&&(i=r.Dl(Sbn(t.e)))),i&&i!=e)return ZGe(i)}catch(c){if(c=or(c),!q(c,63))throw P(c)}return e}function Pxn(e,n,t){var i,r,c;t.Tg("Remove overlaps",1),t.bh(n,F2e),i=u(ve(n,(x3(),Iv)),26),e.f=i,e.a=DQ(u(ve(n,(_0(),ED)),303)),r=ie(ve(n,(Ht(),Jd))),z5(e,(_n(r),r)),c=Np(i),fVe(e,n,c,t),t.bh(n,DF)}function $xn(e){var n,t,i;if($e(Pe(ve(e,(Ht(),ND))))){for(i=new xe,t=new qn(Vn(L0(e).a.Jc(),new ne));at(t);)n=u(tt(t),85),Cw(n)&&$e(Pe(ve(n,fce)))&&Jn(i.c,n);return i}else return vn(),vn(),Ec}function WGe(e){if(!e)return qAe(),Jen;var n=e.valueOf?e.valueOf():e;if(n!==e){var t=Zne[typeof n];return t?t(n):D1e(typeof n)}else return e instanceof Array||e instanceof E.Array?new Fy(e):new Jy(e)}function eqe(e,n,t){var i,r,c;switch(c=e.o,i=u(Rc(e.p,t),253),r=i.i,r.b=Fj(i),r.a=zj(i),r.b=E.Math.max(r.b,c.a),r.b>c.a&&!n&&(r.b=c.a),r.c=-(r.b-c.a)/2,t.g){case 1:r.d=-r.a;break;case 3:r.d=c.b}RZ(i),BZ(i)}function nqe(e,n,t){var i,r,c;switch(c=e.o,i=u(Rc(e.p,t),253),r=i.i,r.b=Fj(i),r.a=zj(i),r.a=E.Math.max(r.a,c.b),r.a>c.b&&!n&&(r.a=c.b),r.d=-(r.a-c.b)/2,t.g){case 4:r.c=-r.b;break;case 2:r.c=c.a}RZ(i),BZ(i)}function Rxn(e,n){var t,i,r;return q(n.g,9)&&u(n.g,9).k==(zn(),gr)?Ki:(r=E4(n),r?E.Math.max(0,e.b/2-.5):(t=N3(n),t?(i=W(ie(jp(t,(Ce(),pg)))),E.Math.max(0,i/2-.5)):Ki))}function Bxn(e,n){var t,i,r;return q(n.g,9)&&u(n.g,9).k==(zn(),gr)?Ki:(r=E4(n),r?E.Math.max(0,e.b/2-.5):(t=N3(n),t?(i=W(ie(jp(t,(Ce(),pg)))),E.Math.max(0,i/2-.5)):Ki))}function zxn(e,n){var t,i,r,c,o;if(!n.dc()){if(r=u(n.Xb(0),132),n.gc()==1){BUe(e,r,r,1,0,n);return}for(t=1;t0)try{r=sl(n,Ur,ri)}catch(c){throw c=or(c),q(c,131)?(i=c,P(new rB(i))):P(c)}return t=(!e.a&&(e.a=new oX(e)),e.a),r=0?u(X(t,r),57):null}function Jxn(e,n){if(e<0)return Kj(QVe,z(B(Mr,1),xn,1,5,["index",me(e)]));if(n<0)throw P(new Gn(ZVe+n));return Kj("%s (%s) must be less than size (%s)",z(B(Mr,1),xn,1,5,["index",me(e),me(n)]))}function Gxn(e){var n,t,i,r,c;if(e==null)return Ko;for(c=new Jb(xo,"[","]"),t=e,i=0,r=t.length;i=0?e.Ih(t,!0,!0):Ow(e,r,!0),163)),u(i,219).Xl(n);else throw P(new Gn(G0+n.ve()+SS))}function $0e(e){var n,t;return e>-0x800000000000&&e<0x800000000000?e==0?0:(n=e<0,n&&(e=-e),t=sc(E.Math.floor(E.Math.log(e)/.6931471805599453)),(!n||e!=E.Math.pow(2,t))&&++t,t):yFe(_u(e))}function nCn(e){var n,t,i,r,c,o,l;for(c=new Lh,t=new L(e);t.a2&&l.e.b+l.j.b<=2&&(r=l,i=o),c.a.yc(r,c),r.q=i);return c}function tCn(e,n,t){t.Tg("Eades radial",1),t.bh(n,DF),e.d=u(ve(n,(x3(),Iv)),26),e.c=W(ie(ve(n,(_0(),zJ)))),e.e=DQ(u(ve(n,ED),303)),e.a=FEn(u(ve(n,q6e),426)),e.b=cMn(u(ve(n,F6e),354)),JAn(e),t.bh(n,DF)}function iCn(e,n){if(n.Tg("Target Width Setter",1),ua(e,(La(),Hre)))yi(e,(qh(),dm),ie(ve(e,Hre)));else throw P(new fd("A target width has to be set if the TargetWidthWidthApproximator should be used."));n.Ug()}function sqe(e,n){var t,i,r;return i=new Da(e),Lu(i,n),fe(i,(pe(),nJ),n),fe(i,(Ce(),Zi),(Rr(),eo)),fe(i,Ah,(Gh(),ZJ)),kf(i,(zn(),gr)),t=new Vu,bu(t,i),Ar(t,(Oe(),Kn)),r=new Vu,bu(r,i),Ar(r,Wn),i}function lqe(e,n){var t,i,r,c,o;for(e.c[n.p]=!0,Te(e.a,n),o=new L(n.j);o.a=c)o.$b();else for(r=o.Jc(),i=0;i0?Eoe():o<0&&bqe(e,n,-o),!0):!1}function zj(e){var n,t,i,r,c,o,l;if(l=0,e.b==0){for(o=UHe(e,!0),n=0,i=o,r=0,c=i.length;r0&&(l+=t,++n);n>1&&(l+=e.c*(n-1))}else l=ZAe($Y(op(ci(dV(e.a),new $m),new mM)));return l>0?l+e.n.d+e.n.a:0}function Fj(e){var n,t,i,r,c,o,l;if(l=0,e.b==0)l=ZAe($Y(op(ci(dV(e.a),new pM),new Zv)));else{for(o=XHe(e,!0),n=0,i=o,r=0,c=i.length;r0&&(l+=t,++n);n>1&&(l+=e.c*(n-1))}return l>0?l+e.n.b+e.n.c:0}function lCn(e){var n,t;if(e.c.length!=2)throw P(new qc("Order only allowed for two paths."));n=(pn(0,e.c.length),u(e.c[0],17)),t=(pn(1,e.c.length),u(e.c[1],17)),n.d.i!=t.c.i&&(e.c.length=0,Jn(e.c,t),Jn(e.c,n))}function gqe(e,n,t){var i;for(tw(t,n.g,n.f),Cl(t,n.i,n.j),i=0;i<(!n.a&&(n.a=new we($t,n,10,11)),n.a).i;i++)gqe(e,u(X((!n.a&&(n.a=new we($t,n,10,11)),n.a),i),26),u(X((!t.a&&(t.a=new we($t,t,10,11)),t.a),i),26))}function fCn(e,n){var t,i,r,c;for(c=u(Rc(e.b,n),127),t=c.a,r=u(u(wi(e.r,n),22),83).Jc();r.Ob();)i=u(r.Pb(),115),i.c&&(t.a=E.Math.max(t.a,ofe(i.c)));if(t.a>0)switch(n.g){case 2:c.n.c=e.s;break;case 4:c.n.b=e.s}}function aCn(e,n){var t,i,r;return t=u(T(n,($f(),f6)),15).a-u(T(e,f6),15).a,t==0?(i=Or(wc(u(T(e,(S0(),UN)),8)),u(T(e,HS),8)),r=Or(wc(u(T(n,UN),8)),u(T(n,HS),8)),vi(i.a*i.b,r.a*r.b)):t}function hCn(e,n){var t,i,r;return t=u(T(n,(Au(),LJ)),15).a-u(T(e,LJ),15).a,t==0?(i=Or(wc(u(T(e,(xi(),vD)),8)),u(T(e,y7),8)),r=Or(wc(u(T(n,vD),8)),u(T(n,y7),8)),vi(i.a*i.b,r.a*r.b)):t}function wqe(e){var n,t;return t=new l0,t.a+="e_",n=C7n(e),n!=null&&(t.a+=""+n),e.c&&e.d&&(Gt((t.a+=" ",t),hz(e.c)),Gt(co((t.a+="[",t),e.c.i),"]"),Gt((t.a+=VW,t),hz(e.d)),Gt(co((t.a+="[",t),e.d.i),"]")),t.a}function pqe(e){switch(e.g){case 0:return new TU;case 1:return new cP;case 2:return new xU;case 3:return new Ex;default:throw P(new Gn("No implementation is available for the layout phase "+(e.f!=null?e.f:""+e.g)))}}function z0e(e,n,t,i,r){var c;switch(c=0,r.g){case 1:c=E.Math.max(0,n.b+e.b-(t.b+i));break;case 3:c=E.Math.max(0,-e.b-i);break;case 2:c=E.Math.max(0,-e.a-i);break;case 4:c=E.Math.max(0,n.a+e.a-(t.a+i))}return c}function mqe(e,n,t){var i,r,c,o,l;if(t)for(r=t.a.length,i=new Ob(r),l=(i.b-i.a)*i.c<0?(d0(),hb):new w0(i);l.Ob();)o=u(l.Pb(),15),c=A9(t,o.a),Dpe in c.a||yne in c.a?wIn(e,c,n):$Rn(e,c,n),Jwn(u(Bn(e.c,W9(c)),85))}function F0e(e){var n,t;switch(e.b){case-1:return!0;case 0:return t=e.t,t>1||t==-1?(e.b=-1,!0):(n=of(e),n&&(Tc(),n.jk()==GWe)?(e.b=-1,!0):(e.b=1,!1));default:case 1:return!1}}function H0e(e,n){var t,i,r,c;if(ui(e),e.c!=0||e.a!=123)throw P(new Lt(Rt((Nt(),hWe))));if(c=n==112,i=e.d,t=o9(e.i,125,i),t<0)throw P(new Lt(Rt((Nt(),dWe))));return r=rf(e.i,i,t),e.d=t+1,T$e(r,c,(e.e&512)==512)}function dCn(e){var n,t,i,r,c,o,l;for(l=Ph(e.c.length),r=new L(e);r.a=0&&i=0?e.Ih(t,!0,!0):Ow(e,r,!0),163)),u(i,219).Ul(n);throw P(new Gn(G0+n.ve()+ane))}function gCn(){Uoe();var e;return Gan?u(l8((h0(),mf),lf),2e3):(Wt(sg,new CL),c$n(),e=u(q(so((h0(),mf),lf),548)?so(mf,lf):new kIe,548),Gan=!0,rBn(e),fBn(e),Qt((qoe(),z8e),e,new o3),Xc(mf,lf,e),e)}function wCn(e,n){var t,i,r,c;e.j=-1,Bs(e.e)?(t=e.i,c=e.i!=0,KC(e,n),i=new O1(e.e,3,e.c,null,n,t,c),r=n.xl(e.e,e.c,null),r=VJe(e,n,r),r?(r.lj(i),r.mj()):fi(e.e,i)):(KC(e,n),r=n.xl(e.e,e.c,null),r&&r.mj())}function Sz(e,n){var t,i,r;if(r=0,i=n[0],i>=e.length)return-1;for(t=(Yn(i,e.length),e.charCodeAt(i));t>=48&&t<=57&&(r=r*10+(t-48),++i,!(i>=e.length));)t=(Yn(i,e.length),e.charCodeAt(i));return i>n[0]?n[0]=i:r=-1,r}function pCn(e,n,t){var i,r,c,o,l;o=e.c,l=e.d,c=wu(z(B(_r,1),je,8,0,[o.i.n,o.n,o.a])).b,r=(c+wu(z(B(_r,1),je,8,0,[l.i.n,l.n,l.a])).b)/2,i=null,o.j==(Oe(),Wn)?i=new ke(n+o.i.c.c.a+t,r):i=new ke(n-t,r),s9(e.a,0,i)}function Cw(e){var n,t,i,r;for(n=null,i=zh(Ll(z(B(Gl,1),xn,20,0,[(!e.b&&(e.b=new Cn(pt,e,4,7)),e.b),(!e.c&&(e.c=new Cn(pt,e,5,8)),e.c)])));at(i);)if(t=u(tt(i),84),r=iu(t),!n)n=r;else if(n!=r)return!1;return!0}function pZ(e,n,t){var i;if(++e.j,n>=e.i)throw P(new ko(Ene+n+cg+e.i));if(t>=e.i)throw P(new ko(jne+t+cg+e.i));return i=e.g[t],n!=t&&(n>16),n=i>>16&16,t=16-n,e=e>>n,i=e-256,n=i>>16&8,t+=n,e<<=n,i=e-Rp,n=i>>16&4,t+=n,e<<=n,i=e-wh,n=i>>16&2,t+=n,e<<=n,i=e>>14,n=i&~(i>>1),t+2-n)}function mCn(e,n){var t,i,r;for(r=new xe,i=Et(n.a,0);i.b!=i.d.c;)t=u(yt(i),65),t.c.g==e.g&&ue(T(t.b,(Au(),Th)))!==ue(T(t.c,Th))&&!I3(new bn(null,new wn(r,16)),new kje(t))&&Jn(r.c,t);return xr(r,new pk),r}function yqe(e,n,t){var i,r,c,o;return q(n,155)&&q(t,155)?(c=u(n,155),o=u(t,155),e.a[c.a][o.a]+e.a[o.a][c.a]):q(n,251)&&q(t,251)&&(i=u(n,251),r=u(t,251),i.a==r.a)?u(T(r.a,($f(),f6)),15).a:0}function kqe(e,n){var t,i,r,c,o,l,f,h;for(h=W(ie(T(n,(Ce(),fA)))),f=e[0].n.a+e[0].o.a+e[0].d.c+h,l=1;l=0?t:(l=ej(Or(new ke(o.c+o.b/2,o.d+o.a/2),new ke(c.c+c.b/2,c.d+c.a/2))),-(ZXe(c,o)-1)*l)}function yCn(e,n,t){var i;Wi(new bn(null,(!t.a&&(t.a=new we(Pi,t,6,6)),new wn(t.a,16))),new Exe(e,n)),Wi(new bn(null,(!t.n&&(t.n=new we(ku,t,1,7)),new wn(t.n,16))),new jxe(e,n)),i=u(ve(t,(Ht(),$v)),78),i&&Ghe(i,e,n)}function Ow(e,n,t){var i,r,c;if(c=ev((ss(),ec),e.Ah(),n),c)return Tc(),u(c,69).vk()||(c=k4(Kc(ec,c))),r=(i=e.Fh(c),u(i>=0?e.Ih(i,!0,!0):Ow(e,c,!0),163)),u(r,219).Ql(n,t);throw P(new Gn(G0+n.ve()+ane))}function J0e(e,n,t,i){var r,c,o,l,f;if(r=e.d[n],r){if(c=r.g,f=r.i,i!=null){for(l=0;l=t&&(i=n,h=(f.c+f.a)/2,o=h-t,f.c<=h-t&&(r=new UK(f.c,o),xb(e,i++,r)),l=h+t,l<=f.a&&(c=new UK(l,f.a),fp(i,e.c.length),jE(e.c,i,c)))}function Aqe(e,n,t){var i,r,c,o,l,f;if(!n.dc()){for(r=new ji,f=n.Jc();f.Ob();)for(l=u(f.Pb(),40),Qt(e.a,me(l.g),me(t)),o=(i=Et(new v1(l).a.d,0),new h3(i));Kx(o.a);)c=u(yt(o.a),65).c,Xi(r,c,r.c.b,r.c);Aqe(e,r,t+1)}}function G0e(e){var n;if(!e.c&&e.g==null)e.d=e._i(e.f),kt(e,e.d),n=e.d;else{if(e.g==null)return!0;if(e.i==0)return!1;n=u(e.g[e.i-1],50)}return n==e.b&&null.Tm>=null.Sm()?(Tz(e),G0e(e)):n.Ob()}function Mqe(e){if(this.a=e,e.c.i.k==(zn(),gr))this.c=e.c,this.d=u(T(e.c.i,(pe(),Ou)),64);else if(e.d.i.k==gr)this.c=e.d,this.d=u(T(e.d.i,(pe(),Ou)),64);else throw P(new Gn("Edge "+e+" is not an external edge."))}function Tqe(e,n){var t,i,r;r=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,3,r,e.b)),n?n!=e&&(Mo(e,n.zb),MY(e,n.d),t=(i=n.c,i??n.zb),xY(e,t==null||hn(t,n.zb)?null:t)):(Mo(e,null),MY(e,0),xY(e,null))}function xqe(e){!Yne&&(Yne=hRn());var n=e.replace(/[\x00-\x1f\xad\u0600-\u0603\u06dd\u070f\u17b4\u17b5\u200b-\u200f\u2028-\u202e\u2060-\u2064\u206a-\u206f\ufeff\ufff9-\ufffb"\\]/g,function(t){return u4n(t)});return'"'+n+'"'}function q0e(e,n,t,i,r,c){var o,l,f,h,b;if(r!=0)for(ue(e)===ue(t)&&(e=e.slice(n,n+r),n=0),f=t,l=n,h=n+r;l=o)throw P(new ep(n,o));return r=t[n],o==1?i=null:(i=oe(Ice,xne,415,o-1,0,1),Yu(t,0,i,0,n),c=o-n-1,c>0&&Yu(t,n+1,i,n,c)),n8(e,i),VGe(e,n,r),r}function Cqe(e){var n,t;if(e.f){for(;e.n0)for(o=e.c.d,l=e.d.d,r=k1(Or(new ke(l.a,l.b),o),1/(i+1)),c=new ke(o.a,o.b),t=new L(e.a);t.a0?c=_4(t):c=TO(_4(t))),yi(n,b7,c)}function _qe(e,n){var t,i;if(e.c.length!=0){if(e.c.length==2)U4((pn(0,e.c.length),u(e.c[0],9)),(ol(),i1)),U4((pn(1,e.c.length),u(e.c[1],9)),rb);else for(i=new L(e);i.a0&&QO(e,t,n),c):i.a!=null?(QO(e,n,t),-1):r.a!=null?(QO(e,t,n),1):0}function Lqe(e){zV();var n,t,i,r,c,o,l;for(t=new E0,r=new L(e.e.b);r.a=0;)i=t[c],o.$l(i.Jk())&&kt(r,i);!DVe(e,r)&&Bs(e.e)&&Ky(e,n.Hk()?v0(e,6,n,(vn(),Ec),null,-1,!1):v0(e,n.rk()?2:1,n,null,null,-1,!1))}function OCn(e,n){var t,i,r,c,o;return e.a==(u8(),KS)?!0:(c=n.a.c,t=n.a.c+n.a.b,!(n.j&&(i=n.A,o=i.c.c.a-i.o.a/2,r=c-(i.n.a+i.o.a),r>o)||n.q&&(i=n.C,o=i.c.c.a-i.o.a/2,r=i.n.a-t,r>o)))}function $qe(e,n,t){var i,r,c,o,l,f;for(i=0,f=t,n||(i=t*(e.c.length-1),f*=-1),c=new L(e);c.a=0?e.xh(null):e.Mh().Qh(e,-1-n,null,null)),e.yh(u(r,52),t),i&&i.mj(),e.sh()&&e.th()&&t>-1&&fi(e,new Dr(e,9,t,c,r)),r):c}function V0e(e,n){var t,i,r,c,o;for(c=e.b.Ae(n),i=(t=e.a.get(c),t??oe(Mr,xn,1,0,5,1)),o=0;o>5,r>=e.d)return e.e<0;if(t=e.a[r],n=1<<(n&31),e.e<0){if(i=HBe(e),r>16)),16).bd(c),l0&&(!(y1(e.a.c)&&n.n.d)&&!(E3(e.a.c)&&n.n.b)&&(n.g.d+=E.Math.max(0,i/2-.5)),!(y1(e.a.c)&&n.n.a)&&!(E3(e.a.c)&&n.n.c)&&(n.g.a-=i-1))}function Yqe(e,n,t){var i,r,c,o,l,f;c=u(_e(n.e,0),17).c,i=c.i,r=i.k,f=u(_e(t.g,0),17).d,o=f.i,l=o.k,r==(zn(),hr)?fe(e,(pe(),ga),u(T(i,ga),12)):fe(e,(pe(),ga),c),l==hr?fe(e,(pe(),hf),u(T(o,hf),12)):fe(e,(pe(),hf),f)}function Qqe(e,n){var t,i,r,c,o,l;for(c=new L(e.b);c.a>n,c=e.m>>n|t<<22-n,r=e.l>>n|e.m<<22-n):n<44?(o=i?B1:0,c=t>>n-22,r=e.m>>n-22|t<<44-n):(o=i?B1:0,c=i?_s:0,r=t>>n-44),Io(r&_s,c&_s,o&B1)}function Zqe(e,n){var t,i,r,c,o,l,f,h,b;if(e.a.f>0&&q(n,45)&&(e.a.Zj(),h=u(n,45),f=h.jd(),c=f==null?0:Oi(f),o=Ele(e.a,c),t=e.a.d[o],t)){for(i=u(t.g,374),b=t.i,l=0;l=2)for(t=r.Jc(),n=ie(t.Pb());t.Ob();)c=n,n=ie(t.Pb()),i=E.Math.min(i,(_n(n),n-(_n(c),c)));return i}function WCn(e,n){var t,i,r;for(r=new xe,i=Et(n.a,0);i.b!=i.d.c;)t=u(yt(i),65),t.b.g==e.g&&!hn(t.b.c,OF)&&ue(T(t.b,(Au(),Th)))!==ue(T(t.c,Th))&&!I3(new bn(null,new wn(r,16)),new Eje(t))&&Jn(r.c,t);return xr(r,new Hg),r}function eOn(e,n){var t,i,r;if(ue(n)===ue(Tt(e)))return!0;if(!q(n,16)||(i=u(n,16),r=e.gc(),r!=i.gc()))return!1;if(q(i,59)){for(t=0;t0&&(r=t),o=new L(e.f.e);o.a0?r+=n:r+=1;return r}function sOn(e,n){var t,i,r,c,o,l,f,h,b,p;h=e,f=cj(h,"individualSpacings"),f&&(i=ua(n,(Ht(),N6)),o=!i,o&&(r=new Sy,yi(n,N6,r)),l=u(ve(n,N6),379),p=f,c=null,p&&(c=(b=_Y(p,oe(Re,je,2,0,6,1)),new NX(p,b))),c&&(t=new Ixe(p,l),rc(c,t)))}function lOn(e,n){var t,i,r,c,o,l,f,h,b,p,y;return f=null,p=e,b=null,(QZe in p.a||ZZe in p.a||BF in p.a)&&(h=null,y=e1e(n),o=cj(p,QZe),t=new uSe(y),zFe(t.a,o),l=cj(p,ZZe),i=new gSe(y),FFe(i.a,l),c=ww(p,BF),r=new mSe(y),h=(XJe(r.a,c),c),b=h),f=b,f}function fOn(e,n){var t,i,r;if(n===e)return!0;if(q(n,540)){if(r=u(n,833),e.a.d!=r.a.d||C3(e).gc()!=C3(r).gc())return!1;for(i=C3(r).Jc();i.Ob();)if(t=u(i.Pb(),416),Y_e(e,t.a.jd())!=u(t.a.kd(),18).gc())return!1;return!0}return!1}function aOn(e,n){var t,i,r,c;for(c=new L(n.a);c.an.c?1:e.bn.b?1:e.a!=n.a?Oi(e.a)-Oi(n.a):e.d==(sj(),vA)&&n.d==mA?-1:e.d==mA&&n.d==vA?1:0}function yZ(e){var n,t,i,r,c,o,l,f;for(r=Ki,i=Nr,t=new L(e.e.b);t.a0&&r0):r<0&&-r0):!1}function dOn(e,n,t,i){var r,c,o,l,f,h,b,p;for(r=(n-e.d)/e.c.c.length,c=0,e.a+=t,e.d=n,p=new L(e.c);p.a>24;return o}function gOn(e){if(e.xe()){var n=e.c;n.ye()?e.o="["+n.n:n.xe()?e.o="["+n.ve():e.o="[L"+n.ve()+";",e.b=n.ue()+"[]",e.k=n.we()+"[]";return}var t=e.j,i=e.d;i=i.split("/"),e.o=EQ(".",[t,EQ("$",i)]),e.b=EQ(".",[t,EQ(".",i)]),e.k=i[i.length-1]}function wOn(e,n){var t,i,r,c,o;for(o=null,c=new L(e.e.a);c.a0&&cN(n,(pn(i-1,e.c.length),u(e.c[i-1],9)),r)>0;)il(e,i,(pn(i-1,e.c.length),u(e.c[i-1],9))),--i;pn(i,e.c.length),e.c[i]=r}n.b=new gt,n.g=new gt}function fUe(e,n,t){var i,r,c;for(i=1;i0&&n.Le((pn(r-1,e.c.length),u(e.c[r-1],9)),c)>0;)il(e,r,(pn(r-1,e.c.length),u(e.c[r-1],9))),--r;pn(r,e.c.length),e.c[r]=c}t.a=new gt,t.b=new gt}function Mz(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(c=n.Jc();c.Ob();)r=u(c.Pb(),26),b=r.i+r.g/2,y=r.j+r.f/2,f=e.f,o=f.i+f.g/2,l=f.j+f.f/2,h=b-o,p=y-l,i=E.Math.sqrt(h*h+p*p),h*=e.e/i,p*=e.e/i,t?(b-=h,y-=p):(b+=h,y+=p),Cs(r,b-r.g/2),Os(r,y-r.f/2)}function Y3(e){var n,t,i;if(!e.c&&e.b!=null){for(n=e.b.length-4;n>=0;n-=2)for(t=0;t<=n;t+=2)(e.b[t]>e.b[t+2]||e.b[t]===e.b[t+2]&&e.b[t+1]>e.b[t+3])&&(i=e.b[t+2],e.b[t+2]=e.b[t],e.b[t]=i,i=e.b[t+3],e.b[t+3]=e.b[t+1],e.b[t+1]=i);e.c=!0}}function Lf(e){var n,t;return t=new Ws(Sb(e.Pm)),t.a+="@",Gt(t,(n=Oi(e)>>>0,n.toString(16))),e.Sh()?(t.a+=" (eProxyURI: ",co(t,e.Yh()),e.Hh()&&(t.a+=" eClass: ",co(t,e.Hh())),t.a+=")"):e.Hh()&&(t.a+=" (eClass: ",co(t,e.Hh()),t.a+=")"),t.a}function Gj(e){var n,t,i,r;if(e.e)throw P(new qc((E1(lte),zW+lte.k+FW)));for(e.d==(vr(),Xa)&&Xz(e,Zc),t=new L(e.a.a);t.a>24}return t}function EOn(e,n,t){var i,r,c;if(r=u(Rc(e.i,n),318),!r)if(r=new _Re(e.d,n,t),w4(e.i,n,r),lde(n))Hwn(e.a,n.c,n.b,r);else switch(c=pxn(n),i=u(Rc(e.p,c),253),c.g){case 1:case 3:r.j=!0,yX(i,n.b,r);break;case 4:case 2:r.k=!0,yX(i,n.c,r)}return r}function jOn(e,n,t,i){var r,c,o,l,f,h;if(l=new My,f=Lo(e.e.Ah(),n),r=u(e.g,122),Tc(),u(n,69).vk())for(o=0;o=0)return r;for(c=1,l=new L(n.j);l.a=0)return r;for(c=1,l=new L(n.j);l.a=0?(n||(n=new hE,i>0&&$c(n,(Yr(0,i,e.length),e.substr(0,i)))),n.a+="\\",m9(n,t&yr)):n&&m9(n,t&yr);return n?n.a:e}function AOn(e){var n,t,i;for(t=new L(e.a.a.b);t.a0&&(!(y1(e.a.c)&&n.n.d)&&!(E3(e.a.c)&&n.n.b)&&(n.g.d-=E.Math.max(0,i/2-.5)),!(y1(e.a.c)&&n.n.a)&&!(E3(e.a.c)&&n.n.c)&&(n.g.a+=E.Math.max(0,i-1)))}function wUe(e,n,t){var i,r;if((e.c-e.b&e.a.length-1)==2)n==(Oe(),Xn)||n==Wn?(fB(u(vj(e),16),(ol(),i1)),fB(u(vj(e),16),rb)):(fB(u(vj(e),16),(ol(),rb)),fB(u(vj(e),16),i1));else for(r=new tj(e);r.a!=r.b;)i=u(RB(r),16),fB(i,t)}function MOn(e,n,t){var i,r,c,o,l,f,h,b,p;for(b=-1,p=0,l=n,f=0,h=l.length;f0&&++p;++b}return p}function TOn(e,n){var t,i,r,c,o,l,f;for(r=d9(new Vue(e)),l=new Gr(r,r.c.length),c=d9(new Vue(n)),f=new Gr(c,c.c.length),o=null;l.b>0&&f.b>0&&(t=(ft(l.b>0),u(l.a.Xb(l.c=--l.b),26)),i=(ft(f.b>0),u(f.a.Xb(f.c=--f.b),26)),t==i);)o=t;return o}function xOn(e,n){var t,i,r,c;for(n.Tg("Self-Loop pre-processing",1),i=new L(e.a);i.acLe(e,t)?(i=pu(t,(Oe(),Wn)),e.d=i.dc()?0:QK(u(i.Xb(0),12)),o=pu(n,Kn),e.b=o.dc()?0:QK(u(o.Xb(0),12))):(r=pu(t,(Oe(),Kn)),e.d=r.dc()?0:QK(u(r.Xb(0),12)),c=pu(n,Wn),e.b=c.dc()?0:QK(u(c.Xb(0),12)))}function COn(e){var n,t,i,r,c,o,l,f;n=!0,r=null,c=null;e:for(f=new L(e.a);f.ae.c));o++)r.a>=e.s&&(c<0&&(c=o),l=o);return f=(e.s+e.c)/2,c>=0&&(i=bIn(e,n,c,l),f=vgn((pn(i,n.c.length),u(n.c[i],340))),SCn(n,i,t)),f}function St(e,n,t){var i,r,c,o,l,f,h;for(o=(c=new vb,c),_he(o,(_n(n),n)),h=(!o.b&&(o.b=new Fs((mn(),Sc),Nu,o)),o.b),f=1;f=2}function IOn(e,n,t,i,r){var c,o,l,f,h,b;for(c=e.c.d.j,o=u(Ku(t,0),8),b=1;b1||(n=Ti(Gf,z(B(Lc,1),ye,96,0,[X1,qf])),dO(DR(n,e))>1)||(i=Ti(Xf,z(B(Lc,1),ye,96,0,[r1,bf])),dO(DR(i,e))>1))}function vUe(e){var n,t,i,r,c,o,l;for(n=0,i=new L(e.a);i.a0&&(i.b.n-=i.c,i.b.n<=0&&i.b.u>0&&qt(n,i.b));for(r=new L(e.i);r.a0&&(i.a.u-=i.c,i.a.u<=0&&i.a.n>0&&qt(t,i.a))}function Tz(e){var n,t,i,r,c;if(e.g==null&&(e.d=e._i(e.f),kt(e,e.d),e.c))return c=e.f,c;if(n=u(e.g[e.i-1],50),r=n.Pb(),e.e=n,t=e._i(r),t.Ob())e.d=t,kt(e,t);else for(e.d=null;!n.Ob()&&(tr(e.g,--e.i,null),e.i!=0);)i=u(e.g[e.i-1],50),n=i;return r}function LOn(e,n){var t,i,r,c,o,l;if(i=n,r=i.Jk(),$1(e.e,r)){if(r.Qi()&&FR(e,r,i.kd()))return!1}else for(l=Lo(e.e.Ah(),r),t=u(e.g,122),c=0;c1||t>1)return 2;return n+t==1?2:0}function Ds(e,n){var t,i,r,c,o,l;return c=e.a*PW+e.b*1502,l=e.b*PW+11,t=E.Math.floor(l*pN),c+=t,l-=t*$ge,c%=$ge,e.a=c,e.b=l,n<=24?E.Math.floor(e.a*Pme[n]):(r=e.a*(1<=2147483648&&(i-=4294967296),i)}function EUe(e,n,t){var i,r,c,o,l,f,h;for(c=new xe,h=new ji,o=new ji,eLn(e,h,o,n),LPn(e,h,o,n,t),f=new L(e);f.ai.b.g&&Jn(c.c,i);return c}function FOn(e,n,t){var i,r,c,o,l,f;for(l=e.c,o=(t.q?t.q:(vn(),vn(),Zh)).vc().Jc();o.Ob();)c=u(o.Pb(),45),i=!e9(ci(new bn(null,new wn(l,16)),new Uy(new fxe(n,c)))).zd((Ab(),s6)),i&&(f=c.kd(),q(f,4)&&(r=ade(f),r!=null&&(f=r)),n.of(u(c.jd(),147),f))}function HOn(e,n){var t,i,r,c;for(n.Tg("Resize child graph to fit parent.",1),i=new L(e.b);i.a1)for(r=new L(e.a);r.a=0?e.Ih(i,!0,!0):Ow(e,c,!0),163)),u(r,219).Vl(n,t)}else throw P(new Gn(G0+n.ve()+SS))}function qOn(e,n,t){var i,r,c,o,l,f;if(f=ble(e,u(Bn(e.e,n),26)),l=null,f)switch(f.g){case 3:i=Rse(e,sp(n)),l=(_n(t),t+(_n(i),i));break;case 2:r=Rse(e,sp(n)),o=(_n(t),t+(_n(r),r)),c=Rse(e,u(Bn(e.e,n),26)),l=o-(_n(c),c);break;default:l=t}else l=t;return l}function UOn(e,n,t){var i,r,c,o,l,f;if(f=ble(e,u(Bn(e.e,n),26)),l=null,f)switch(f.g){case 3:i=Bse(e,sp(n)),l=(_n(t),t+(_n(i),i));break;case 2:r=Bse(e,sp(n)),o=(_n(t),t+(_n(r),r)),c=Bse(e,u(Bn(e.e,n),26)),l=o-(_n(c),c);break;default:l=t}else l=t;return l}function xz(e,n){var t,i,r,c,o;if(n){for(c=q(e.Cb,88)||q(e.Cb,103),o=!c&&q(e.Cb,335),i=new ct((!n.a&&(n.a=new UE(n,Pc,n)),n.a));i.e!=i.i.gc();)if(t=u(lt(i),87),r=zz(t),c?q(r,88):o?q(r,159):r)return r;return c?(mn(),vf):(mn(),Ya)}else return null}function XOn(e,n){var t,i,r,c,o;for(t=new xe,r=ou(new bn(null,new wn(e,16)),new S5),c=ou(new bn(null,new wn(e,16)),new wk),o=$9n(r9n(op(rNn(z(B(kBn,1),xn,832,0,[r,c])),new b_))),i=1;i=2*n&&Te(t,new UK(o[i-1]+n,o[i]-n));return t}function jUe(e,n,t){var i,r,c,o,l,f,h,b;if(t)for(c=t.a.length,i=new Ob(c),l=(i.b-i.a)*i.c<0?(d0(),hb):new w0(i);l.Ob();)o=u(l.Pb(),15),r=A9(t,o.a),r&&(f=fyn(e,(h=(a0(),b=new foe,b),n&&hbe(h,n),h),r),I9(f,M1(r,Eh)),mz(r,f),_0e(r,f),VY(e,r,f))}function Cz(e){var n,t,i,r,c,o;if(!e.j){if(o=new vL,n=ZA,c=n.a.yc(e,n),c==null){for(i=new ct(tu(e));i.e!=i.i.gc();)t=u(lt(i),29),r=Cz(t),er(o,r),kt(o,t);n.a.Ac(e)!=null}yp(o),e.j=new y3((u(X(ge((p0(),Rn).o),11),19),o.i),o.g),Ms(e).b&=-33}return e.j}function KOn(e){var n,t,i,r;if(e==null)return null;if(i=ho(e,!0),r=FN.length,hn(i.substr(i.length-r,r),FN)){if(t=i.length,t==4){if(n=(Yn(0,i.length),i.charCodeAt(0)),n==43)return u7e;if(n==45)return ahn}else if(t==3)return u7e}return new toe(i)}function VOn(e){var n,t,i;return t=e.l,(t&t-1)!=0||(i=e.m,(i&i-1)!=0)||(n=e.h,(n&n-1)!=0)||n==0&&i==0&&t==0?-1:n==0&&i==0&&t!=0?The(t):n==0&&i!=0&&t==0?The(i)+22:n!=0&&i==0&&t==0?The(n)+44:-1}function Q3(e,n){var t,i,r,c,o;for(r=n.a&e.f,c=null,i=e.b[r];;i=i.b){if(i==n){c?c.b=n.b:e.b[r]=n.b;break}c=i}for(o=n.f&e.f,c=null,t=e.c[o];;t=t.d){if(t==n){c?c.d=n.d:e.c[o]=n.d;break}c=t}n.e?n.e.c=n.c:e.a=n.c,n.c?n.c.e=n.e:e.e=n.e,--e.i,++e.g}function YOn(e,n){var t;n.d?n.d.b=n.b:e.a=n.b,n.b?n.b.d=n.d:e.e=n.d,!n.e&&!n.c?(t=u(tf(u(S4(e.b,n.a),262)),262),t.a=0,++e.c):(t=u(tf(u(Bn(e.b,n.a),262)),262),--t.a,n.e?n.e.c=n.c:t.b=u(tf(n.c),497),n.c?n.c.e=n.e:t.c=u(tf(n.e),497)),--e.d}function EZ(e,n){var t,i,r,c;for(c=new Gr(e,0),t=(ft(c.b0),c.a.Xb(c.c=--c.b),W2(c,r),ft(c.b3&&Jh(e,0,n-3))}function ZOn(e){var n,t,i,r;return ue(T(e,(Ce(),tm)))===ue((_1(),Gd))?!e.e&&ue(T(e,sD))!==ue((R9(),eD)):(i=u(T(e,Aie),302),r=$e(Pe(T(e,Mie)))||ue(T(e,uA))===ue((Cj(),ZN)),n=u(T(e,N5e),15).a,t=e.a.c.length,!r&&i!=(R9(),eD)&&(n==0||n>t))}function WOn(e,n){var t,i,r,c,o,l,f;for(r=e.Jc();r.Ob();)for(i=u(r.Pb(),9),l=new Vu,bu(l,i),Ar(l,(Oe(),Wn)),fe(l,(pe(),tJ),(Pn(),!0)),o=n.Jc();o.Ob();)c=u(o.Pb(),9),f=new Vu,bu(f,c),Ar(f,Kn),fe(f,tJ,!0),t=new dw,fe(t,tJ,!0),lc(t,l),Jr(t,f)}function eNn(e){var n,t;for(t=0;t0);t++);if(t>0&&t0);n++);return n>0&&t>16!=6&&n){if(t8(e,n))throw P(new Gn(AS+Rqe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Ide(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=$4(n,e,6,i)),i=yle(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,6,n,n))}function Oz(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=3&&n){if(t8(e,n))throw P(new Gn(AS+xKe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?$de(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=$4(n,e,12,i)),i=vle(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,3,n,n))}function hbe(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=9&&n){if(t8(e,n))throw P(new Gn(AS+AXe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Lde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=$4(n,e,9,i)),i=kle(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,9,n,n))}function f8(e){var n,t,i,r,c;if(i=of(e),c=e.j,c==null&&i)return e.Hk()?null:i.gk();if(q(i,159)){if(t=i.hk(),t&&(r=t.ti(),r!=e.i)){if(n=u(i,159),n.lk())try{e.g=r.qi(n,c)}catch(o){if(o=or(o),q(o,80))e.g=null;else throw P(o)}e.i=r}return e.g}return null}function xUe(e){var n;return n=new xe,Te(n,new W5(new ke(e.c,e.d),new ke(e.c+e.b,e.d))),Te(n,new W5(new ke(e.c,e.d),new ke(e.c,e.d+e.a))),Te(n,new W5(new ke(e.c+e.b,e.d+e.a),new ke(e.c+e.b,e.d))),Te(n,new W5(new ke(e.c+e.b,e.d+e.a),new ke(e.c,e.d+e.a))),n}function tNn(e){var n,t,i,r;for(i=e.a.d.j,r=e.c.d.j,t=new L(e.i.d);t.a>>0),t.toString(16)),Ijn(D7n(),(r9(),"Exception during lenientFormat for "+i),n),"<"+i+" threw "+Sb(n.Pm)+">";throw P(r)}}function rNn(e){var n,t,i,r,c,o,l,f,h;for(i=!1,n=336,t=0,c=new GOe(e.length),l=e,f=0,h=l.length;f1)for(n=rw((t=new jb,++e.b,t),e.d),l=Et(c,0);l.b!=l.d.c;)o=u(yt(l),124),Pf(Sf(jf(Af(Ef(new Wl,1),0),n),o))}function Nz(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=11&&n){if(t8(e,n))throw P(new Gn(AS+Ibe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Rde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=$4(n,e,10,i)),i=Lle(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,11,n,n))}function lNn(e,n,t){var i,r,c,o,l,f;if(c=0,o=0,e.c)for(f=new L(e.d.i.j);f.ac.a?-1:r.af){for(b=e.d,e.d=oe(N8e,Upe,67,2*f+4,0,1),c=0;c=9223372036854776e3?(O9(),dme):(r=!1,e<0&&(r=!0,e=-e),i=0,e>=eg&&(i=sc(e/eg),e-=i*eg),t=0,e>=Q4&&(t=sc(e/Q4),e-=t*Q4),n=sc(e),c=Io(n,t,i),r&&KY(c),c)}function kNn(e){var n,t,i,r,c;if(c=new xe,Ao(e.b,new Rke(c)),e.b.c.length=0,c.c.length!=0){for(n=(pn(0,c.c.length),u(c.c[0],80)),t=1,i=c.c.length;t>16!=7&&n){if(t8(e,n))throw P(new Gn(AS+IGe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?_de(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=u(n,52).Oh(e,1,XD,i)),i=mfe(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,7,n,n))}function NUe(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=3&&n){if(t8(e,n))throw P(new Gn(AS+EFe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Pde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=u(n,52).Oh(e,0,VD,i)),i=vfe(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,3,n,n))}function jZ(e,n){h8();var t,i,r,c,o,l,f,h,b;return n.d>e.d&&(l=e,e=n,n=l),n.d<63?aDn(e,n):(o=(e.d&-2)<<4,h=zae(e,o),b=zae(n,o),i=JZ(e,j4(h,o)),r=JZ(n,j4(b,o)),f=jZ(h,b),t=jZ(i,r),c=jZ(JZ(h,i),JZ(r,b)),c=YZ(YZ(c,f),t),c=j4(c,o),f=j4(f,o<<1),YZ(YZ(f,c),t))}function KO(){KO=Y,Jie=new w3(OQe,0),m4e=new w3("LONGEST_PATH",1),v4e=new w3("LONGEST_PATH_SOURCE",2),Fie=new w3("COFFMAN_GRAHAM",3),p4e=new w3(WW,4),y4e=new w3("STRETCH_WIDTH",5),kJ=new w3("MIN_WIDTH",6),zie=new w3("BF_MODEL_ORDER",7),Hie=new w3("DF_MODEL_ORDER",8)}function MNn(e,n){var t,i,r,c,o,l;if(!e.tb){for(c=(!e.rb&&(e.rb=new rp(e,va,e)),e.rb),l=new Z5(c.i),r=new ct(c);r.e!=r.i.gc();)i=u(lt(r),143),o=i.ve(),t=u(o==null?Xo(l.f,null,i):Ew(l.i,o,i),143),t&&(o==null?Xo(l.f,null,t):Ew(l.i,o,t));e.tb=l}return u(so(e.tb,n),143)}function VO(e,n){var t,i,r,c,o;if((e.i==null&&gh(e),e.i).length,!e.p){for(o=new Z5((3*e.g.i/2|0)+1),r=new o4(e.g);r.e!=r.i.gc();)i=u(OQ(r),179),c=i.ve(),t=u(c==null?Xo(o.f,null,i):Ew(o.i,c,i),179),t&&(c==null?Xo(o.f,null,t):Ew(o.i,c,t));e.p=o}return u(so(e.p,n),179)}function mbe(e,n,t,i,r){var c,o,l,f,h;for(jjn(i+OR(t,t.ge()),r),oIe(n,zEn(t)),c=t.f,c&&mbe(e,n,c,"Caused by: ",!1),l=(t.k==null&&(t.k=oe(Vne,je,80,0,0,1)),t.k),f=0,h=l.length;f=0;c+=t?1:-1)o=o|n.c.jg(f,c,t,i&&!$e(Pe(T(n.j,(pe(),Y0))))&&!$e(Pe(T(n.j,(pe(),jv))))),o=o|n.q.tg(f,c,t),o=o|mXe(e,f[c],t,i);return ar(e.c,n),o}function Iz(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(b=PLe(e.j),p=0,y=b.length;p1&&(e.a=!0),evn(u(t.b,68),bi(wc(u(n.b,68).c),k1(Or(wc(u(t.b,68).a),u(n.b,68).a),r))),U_e(e,n),IUe(e,t)}function _Ue(e){var n,t,i,r,c,o,l;for(c=new L(e.a.a);c.a0&&c>0?o.p=n++:i>0?o.p=t++:c>0?o.p=r++:o.p=t++}vn(),xr(e.j,new xq)}function NNn(e){var n,t;t=null,n=u(_e(e.g,0),17);do{if(t=n.d.i,di(t,(pe(),hf)))return u(T(t,hf),12).i;if(t.k!=(zn(),Qi)&&at(new qn(Vn(Ni(t).a.Jc(),new ne))))n=u(tt(new qn(Vn(Ni(t).a.Jc(),new ne))),17);else if(t.k!=Qi)return null}while(t&&t.k!=(zn(),Qi));return t}function DNn(e,n){var t,i,r,c,o,l,f,h,b;for(l=n.j,o=n.g,f=u(_e(l,l.c.length-1),113),b=(pn(0,l.c.length),u(l.c[0],113)),h=qQ(e,o,f,b),c=1;ch&&(f=t,b=r,h=i);n.a=b,n.c=f}function Nw(e,n,t,i){var r,c;if(r=ue(T(t,(Ce(),rA)))===ue((M0(),Wp)),c=u(T(t,O5e),16),di(e,(pe(),Ci)))if(r){if(c.Gc(T(e,cA))&&c.Gc(T(n,cA)))return i*u(T(e,cA),15).a+u(T(e,Ci),15).a}else return u(T(e,Ci),15).a;else return-1;return u(T(e,Ci),15).a}function INn(e,n,t){var i,r,c,o,l,f,h;for(h=new dd(new rje(e)),o=z(B(Ytn,1),eQe,12,0,[n,t]),l=0,f=o.length;lf-e.b&&lf-e.a&&lt.p?1:0:c.Ob()?1:-1}function FNn(e,n){var t,i,r,c,o,l;n.Tg(eZe,1),r=u(ve(e,(La(),CA)),104),c=(!e.a&&(e.a=new we($t,e,10,11)),e.a),o=sAn(c),l=E.Math.max(o.a,W(ie(ve(e,(qh(),xA))))-(r.b+r.c)),i=E.Math.max(o.b,W(ie(ve(e,HJ)))-(r.d+r.a)),t=i-o.b,yi(e,TA,t),yi(e,A6,l),yi(e,E7,i+t),n.Ug()}function _z(e){var n,t;if((!e.a&&(e.a=new we(Pi,e,6,6)),e.a).i==0)return e1e(e);for(n=u(X((!e.a&&(e.a=new we(Pi,e,6,6)),e.a),0),170),vt((!n.a&&(n.a=new mr(pl,n,5)),n.a)),R3(n,0),B3(n,0),P3(n,0),$3(n,0),t=(!e.a&&(e.a=new we(Pi,e,6,6)),e.a);t.i>1;)Dp(t,t.i-1);return n}function Lo(e,n){Tc();var t,i,r,c;return n?n==(ki(),lhn)||(n==Qan||n==Sg||n==Yan)&&e!=r7e?new pge(e,n):(i=u(n,682),t=i.Yk(),t||(k9(Kc((ss(),ec),n)),t=i.Yk()),c=(!t.i&&(t.i=new gt),t.i),r=u(hu(Uc(c.f,e)),2003),!r&&Qt(c,e,r=new pge(e,n)),r):Xan}function HNn(e,n){var t,i;if(i=_C(e.b,n.b),!i)throw P(new qc("Invalid hitboxes for scanline constraint calculation."));(bze(n.b,u(fgn(e.b,n.b),60))||bze(n.b,u(lgn(e.b,n.b),60)))&&bd(),e.a[n.b.f]=u(IX(e.b,n.b),60),t=u(DX(e.b,n.b),60),t&&(e.a[t.f]=n.b)}function JNn(e,n){var t,i,r,c,o,l,f,h,b;for(f=u(T(e,(pe(),gi)),12),h=wu(z(B(_r,1),je,8,0,[f.i.n,f.n,f.a])).a,b=e.i.n.b,t=lh(e.e),r=t,c=0,o=r.length;c0?c.a?(l=c.b.Kf().a,t>l&&(r=(t-l)/2,c.d.b=r,c.d.c=r)):c.d.c=e.s+t:VE(e.u)&&(i=l0e(c.b),i.c<0&&(c.d.b=-i.c),i.c+i.b>c.b.Kf().a&&(c.d.c=i.c+i.b-c.b.Kf().a))}function VNn(e,n){var t,i,r,c,o;o=new xe,t=n;do c=u(Bn(e.b,t),132),c.B=t.c,c.D=t.d,Jn(o.c,c),t=u(Bn(e.k,t),17);while(t);return i=(pn(0,o.c.length),u(o.c[0],132)),i.j=!0,i.A=u(i.d.a.ec().Jc().Pb(),17).c.i,r=u(_e(o,o.c.length-1),132),r.q=!0,r.C=u(r.d.a.ec().Jc().Pb(),17).d.i,o}function YNn(e){var n,t;t=u(T(e,(Ce(),vu)),165),n=u(T(e,(pe(),fg)),315),t==(qs(),G1)?(fe(e,vu,uD),fe(e,fg,(C1(),kv))):t==hg?(fe(e,vu,uD),fe(e,fg,(C1(),d6))):n==(C1(),kv)?(fe(e,vu,G1),fe(e,fg,tD)):n==d6&&(fe(e,vu,hg),fe(e,fg,tD))}function Lz(){Lz=Y,pD=new M2,Don=Bt(new ur,(Br(),Zu),(qr(),SH)),Lon=Eo(Bt(new ur,Zu,DH),_c,NH),Pon=hh(hh(yE(Eo(Bt(new ur,Ff,PH),_c,LH),Wu),_H),$H),Ion=Eo(Bt(Bt(Bt(new ur,Wh,MH),Wu,xH),Wu,n7),_c,TH),_on=Eo(Bt(Bt(new ur,Wu,n7),Wu,jH),_c,EH)}function Xj(){Xj=Y,Bon=Bt(Eo(new ur,(Br(),_c),(qr(),I3e)),Zu,SH),Jon=hh(hh(yE(Eo(Bt(new ur,Ff,PH),_c,LH),Wu),_H),$H),zon=Eo(Bt(Bt(Bt(new ur,Wh,MH),Wu,xH),Wu,n7),_c,TH),Hon=Bt(Bt(new ur,Zu,DH),_c,NH),Fon=Eo(Bt(Bt(new ur,Wu,n7),Wu,jH),_c,EH)}function QNn(e,n,t,i,r){var c,o;(!cc(n)&&n.c.i.c==n.d.i.c||!kBe(wu(z(B(_r,1),je,8,0,[r.i.n,r.n,r.a])),t))&&!cc(n)&&(n.c==r?s9(n.a,0,new gc(t)):qt(n.a,new gc(t)),i&&!ef(e.a,t)&&(o=u(T(n,(Ce(),Qc)),78),o||(o=new Ss,fe(n,Qc,o)),c=new gc(t),Xi(o,c,o.c.b,o.c),ar(e.a,c)))}function $Ue(e,n){var t,i,r,c;for(c=_t(ac(Kh,Fh(_t(ac(n==null?0:Oi(n),Vh)),15))),t=c&e.b.length-1,r=null,i=e.b[t];i;r=i,i=i.a)if(i.d==c&&j1(i.i,n))return r?r.a=i.a:e.b[t]=i.a,nMe(u(tf(i.c),593),u(tf(i.f),593)),qx(u(tf(i.b),227),u(tf(i.e),227)),--e.f,++e.e,!0;return!1}function ZNn(e){var n,t;for(t=new qn(Vn(rr(e).a.Jc(),new ne));at(t);)if(n=u(tt(t),17),n.c.i.k!=(zn(),Gu))throw P(new fd(ZW+IO(e)+"' has its layer constraint set to FIRST, but has at least one incoming edge that does not come from a FIRST_SEPARATE node. That must not happen."))}function RUe(e,n){var t,i,r,c,o,l,f,h,b,p,y;r=n?new Fg:new iT,c=!1;do for(c=!1,h=n?Us(e.b):e.b,f=h.Jc();f.Ob();)for(l=u(f.Pb(),25),y=$b(l.a),n||Us(y),p=new L(y);p.a=0;o+=r?1:-1){for(l=n[o],f=i==(Oe(),Wn)?r?pu(l,i):Us(pu(l,i)):r?Us(pu(l,i)):pu(l,i),c&&(e.c[l.p]=f.gc()),p=f.Jc();p.Ob();)b=u(p.Pb(),12),e.d[b.p]=h++;jr(t,f)}}function zUe(e,n,t){var i,r,c,o,l,f,h,b;for(c=W(ie(e.b.Jc().Pb())),h=W(ie(I7n(n.b))),i=k1(wc(e.a),h-t),r=k1(wc(n.a),t-c),b=bi(i,r),k1(b,1/(h-c)),this.a=b,this.b=new xe,l=!0,o=e.b.Jc(),o.Pb();o.Ob();)f=W(ie(o.Pb())),l&&f-t>Hee&&(this.b.Ec(t),l=!1),this.b.Ec(f);l&&this.b.Ec(t)}function eDn(e){var n,t,i,r;if(pIn(e,e.n),e.d.c.length>0){for(fE(e.c);Z0e(e,u(_(new L(e.e.a)),124))>5,n&=31,i>=e.d)return e.e<0?(bh(),Ven):(bh(),RS);if(c=e.d-i,r=oe(It,Zt,30,c+1,15,1),rxn(r,c,e.a,i,n),e.e<0){for(t=0;t0&&e.a[t]<<32-n!=0){for(t=0;t=0?!1:(t=ev((ss(),ec),r,n),t?(i=t.Gk(),(i>1||i==-1)&&aw(Kc(ec,t))!=3):!0)):!1}function uDn(e,n,t,i){var r,c,o,l,f,h,b,p,y,S;if(f=e.c.d,h=e.d.d,f.j!=h.j)for(S=e.b,b=null,l=null,o=kjn(e),o&&S.i&&(b=e.b.i.i,l=S.i.j),r=f.j,p=null;r!=h.j;)p=n==0?FB(r):R1e(r),c=wde(r,S.d[r.g],t),y=wde(p,S.d[p.g],t),o&&b&&l&&(r==b?IFe(c,b,l):p==b&&IFe(y,b,l)),qt(i,bi(c,y)),r=p}function kbe(e,n,t){var i,r,c,o,l,f;if(i=Ybn(t,e.length),o=e[i],c=uMe(t,o.length),o[c].k==(zn(),gr))for(f=n.j,r=0;r0&&(t[0]+=e.d,o-=t[0]),t[2]>0&&(t[2]+=e.d,o-=t[2]),c=E.Math.max(0,o),t[1]=E.Math.max(t[1],o),Hae(e,Oo,r.c+i.b+t[0]-(t[1]-o)/2,t),n==Oo&&(e.c.b=c,e.c.c=r.c+i.b+(c-o)/2)}function KUe(){this.c=oe(Fr,zc,30,(Oe(),z(B(jc,1),Ju,64,0,[yu,Xn,Wn,dt,Kn])).length,15,1),this.b=oe(Fr,zc,30,z(B(jc,1),Ju,64,0,[yu,Xn,Wn,dt,Kn]).length,15,1),this.a=oe(Fr,zc,30,z(B(jc,1),Ju,64,0,[yu,Xn,Wn,dt,Kn]).length,15,1),Qoe(this.c,Ki),Qoe(this.b,Nr),Qoe(this.a,Nr)}function hDn(e,n,t,i){var r,c,o,l,f;for(f=n.i,l=t[f.g][e.d[f.g]],r=!1,o=new L(n.d);o.a=r&&(e.c=!1,e.a=!1),e.b[i++]=r,e.b[i]=c,e.c||Y3(e)}}function dDn(e,n,t){var i,r,c,o,l,f,h;for(h=n.d,e.a=new So(h.c.length),e.c=new gt,l=new L(h);l.a=0?e.Ih(h,!1,!0):Ow(e,t,!1),61));e:for(c=p.Jc();c.Ob();){for(r=u(c.Pb(),57),b=0;be.d[o.p]&&(t+=_ae(e.b,c),k0(e.a,me(c)));for(;!aE(e.a);)bhe(e.b,u(g4(e.a),15).a)}return t}function QUe(e,n,t){var i,r,c,o;for(c=(!n.a&&(n.a=new we($t,n,10,11)),n.a).i,r=new ct((!n.a&&(n.a=new we($t,n,10,11)),n.a));r.e!=r.i.gc();)i=u(lt(r),26),(!i.a&&(i.a=new we($t,i,10,11)),i.a).i==0||(c+=QUe(e,i,!1));if(t)for(o=zi(n);o;)c+=(!o.a&&(o.a=new we($t,o,10,11)),o.a).i,o=zi(o);return c}function Dp(e,n){var t,i,r,c;return e.Nj()?(i=null,r=e.Oj(),e.Rj()&&(i=e.Tj(e.Yi(n),null)),t=e.Gj(4,c=R4(e,n),null,n,r),e.Kj()&&c!=null&&(i=e.Mj(c,i)),i?(i.lj(t),i.mj()):e.Hj(t),c):(c=R4(e,n),e.Kj()&&c!=null&&(i=e.Mj(c,null),i&&i.mj()),c)}function yDn(e){var n,t,i,r,c,o,l,f,h,b;for(h=e.a,n=new fr,f=0,i=new L(e.d);i.al.d&&(b=l.d+l.a+h));t.c.d=b,n.a.yc(t,n),f=E.Math.max(f,t.c.d+t.c.a)}return f}function kDn(e,n,t){var i,r,c,o,l,f;for(o=u(T(e,(pe(),aie)),16).Jc();o.Ob();){switch(c=u(o.Pb(),9),u(T(c,(Ce(),vu)),165).g){case 2:Cr(c,n);break;case 4:Cr(c,t)}for(r=new qn(Vn(fh(c).a.Jc(),new ne));at(r);)i=u(tt(r),17),!(i.c&&i.d)&&(l=!i.d,f=u(T(i,Hve),12),l?Jr(i,f):lc(i,f))}}function Oc(){Oc=Y,VH=new q2("COMMENTS",0),ql=new q2("EXTERNAL_PORTS",1),VS=new q2("HYPEREDGES",2),YH=new q2("HYPERNODES",3),f7=new q2("NON_FREE_PORTS",4),yv=new q2("NORTH_SOUTH_PORTS",5),YS=new q2(mQe,6),s7=new q2("CENTER_LABELS",7),l7=new q2("END_LABELS",8),QH=new q2("PARTITIONS",9)}function EDn(e,n,t,i,r){return i<0?(i=V3(e,r,z(B(Re,1),je,2,6,[dW,bW,gW,wW,V4,pW,mW,vW,yW,kW,EW,jW]),n),i<0&&(i=V3(e,r,z(B(Re,1),je,2,6,["Jan","Feb","Mar","Apr",V4,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),n)),i<0?!1:(t.k=i,!0)):i>0?(t.k=i-1,!0):!1}function jDn(e,n,t,i,r){return i<0?(i=V3(e,r,z(B(Re,1),je,2,6,[dW,bW,gW,wW,V4,pW,mW,vW,yW,kW,EW,jW]),n),i<0&&(i=V3(e,r,z(B(Re,1),je,2,6,["Jan","Feb","Mar","Apr",V4,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),n)),i<0?!1:(t.k=i,!0)):i>0?(t.k=i-1,!0):!1}function SDn(e,n,t,i,r,c){var o,l,f,h;if(l=32,i<0){if(n[0]>=e.length||(l=ic(e,n[0]),l!=43&&l!=45)||(++n[0],i=Sz(e,n),i<0))return!1;l==45&&(i=-i)}return l==32&&n[0]-t==2&&r.b==2&&(f=new e$,h=f.q.getFullYear()-z0+z0-80,o=h%100,c.a=i==o,i+=(h/100|0)*100+(i=0?N0(e):ZE(N0(Ed(e)))),BS[n]=T$(Bh(e,n),0)?N0(Bh(e,n)):ZE(N0(Ed(Bh(e,n)))),e=ac(e,5);for(;n=h&&(f=i);f&&(b=E.Math.max(b,f.a.o.a)),b>y&&(p=h,y=b)}return p}function CDn(e){var n,t,i,r,c,o,l;for(c=new dd(u(Tt(new z7),51)),l=Nr,t=new L(e.d);t.aKQe?xr(f,e.b):i<=KQe&&i>VQe?xr(f,e.d):i<=VQe&&i>YQe?xr(f,e.c):i<=YQe&&xr(f,e.a),c=nXe(e,f,c);return r}function tXe(e,n,t,i){var r,c,o,l,f,h;for(r=(i.c+i.a)/2,Js(n.j),qt(n.j,r),Js(t.e),qt(t.e,r),h=new rMe,l=new L(e.f);l.a1,l&&(i=new ke(r,t.b),qt(n.a,i)),bj(n.a,z(B(_r,1),je,8,0,[y,p]))}function Sbe(e,n,t){var i,r;for(n=48;t--)tM[t]=t-48<<24>>24;for(i=70;i>=65;i--)tM[i]=i-65+10<<24>>24;for(r=102;r>=97;r--)tM[r]=r-97+10<<24>>24;for(c=0;c<10;c++)MG[c]=48+c&yr;for(e=10;e<=15;e++)MG[e]=65+e-10&yr}function uXe(e,n){n.Tg("Process graph bounds",1),fe(e,(xi(),are),iC(RY(op(new bn(null,new wn(e.b,16)),new Kq)))),fe(e,hre,iC(RY(op(new bn(null,new wn(e.b,16)),new $s)))),fe(e,l6e,iC($Y(op(new bn(null,new wn(e.b,16)),new mT)))),fe(e,f6e,iC($Y(op(new bn(null,new wn(e.b,16)),new vT)))),n.Ug()}function _Dn(e){var n,t,i,r,c;r=u(T(e,(Ce(),bg)),22),c=u(T(e,pJ),22),t=new ke(e.f.a+e.d.b+e.d.c,e.f.b+e.d.d+e.d.a),n=new gc(t),r.Gc((Xs(),km))&&(i=u(T(e,d7),8),c.Gc((Is(),N7))&&(i.a<=0&&(i.a=20),i.b<=0&&(i.b=20)),n.a=E.Math.max(t.a,i.a),n.b=E.Math.max(t.b,i.b)),$e(Pe(T(e,Iie)))||uLn(e,t,n)}function LDn(e){var n,t,i,r,c,o,l;for(n=!1,t=0,r=new L(e.d.b);r.a>19!=0)return"-"+oXe(z9(e));for(t=e,i="";!(t.l==0&&t.m==0&&t.h==0);){if(r=iY(eF),t=lge(t,r,!0),n=""+EMe(q0),!(t.l==0&&t.m==0&&t.h==0))for(c=9-n.length;c>0;c--)n="0"+n;i=n+i}return i}function PDn(){if(!Object.create||!Object.getOwnPropertyNames)return!1;var e="__proto__",n=Object.create(null);if(n[e]!==void 0)return!1;var t=Object.getOwnPropertyNames(n);return!(t.length!=0||(n[e]=42,n[e]!==42)||Object.getOwnPropertyNames(n).length==0)}function $Dn(e,n,t){var i,r,c,o,l,f,h,b,p;for(i=t.c,r=t.d,l=Ta(n.c),f=Ta(n.d),i==n.c?(l=fbe(e,l,r),f=sGe(n.d)):(l=sGe(n.c),f=fbe(e,f,r)),h=new JP(n.a),Xi(h,l,h.a,h.a.a),Xi(h,f,h.c.b,h.c),o=n.c==i,p=new YSe,c=0;c=e.a||!c0e(n,t))return-1;if(ap(u(i.Kb(n),20)))return 1;for(r=0,o=u(i.Kb(n),20).Jc();o.Ob();)if(c=u(o.Pb(),17),f=c.c.i==n?c.d.i:c.c.i,l=Tbe(e,f,t,i),l==-1||(r=E.Math.max(r,l),r>e.c-1))return-1;return r+1}function La(){La=Y,GJ=new Vr((Ht(),j7),1.3),mln=new Vr(wm,(Pn(),!1)),hye=new iw(15),CA=new Vr(t1,hye),OA=new Vr(Jd,15),bln=CD,pln=yg,vln=Bv,yln=ib,wln=Rv,zre=ID,kln=pm,wye=(Ube(),aln),gye=fln,Hre=dln,pye=hln,aye=oln,Fre=uln,fye=cln,bye=lln,sye=DD,gln=ace,jD=tln,oye=nln,SD=iln,dye=sln,lye=rln}function sXe(e,n){var t,i,r,c,o,l;if(ue(n)===ue(e))return!0;if(!q(n,16)||(i=u(n,16),l=e.gc(),i.gc()!=l))return!1;if(o=i.Jc(),e.Wi()){for(t=0;t0){if(e.Zj(),n!=null){for(c=0;c>24;case 97:case 98:case 99:case 100:case 101:case 102:return e-97+10<<24>>24;case 65:case 66:case 67:case 68:case 69:case 70:return e-65+10<<24>>24;default:throw P(new rh("Invalid hexadecimal"))}}function fXe(e,n,t,i){var r,c,o,l,f,h;for(f=QQ(e,t),h=QQ(n,t),r=!1;f&&h&&(i||JSn(f,h,t));)o=QQ(f,t),l=QQ(h,t),nO(n),nO(e),c=f.c,QZ(f,!1),QZ(h,!1),t?(D0(n,h.p,c),n.p=h.p,D0(e,f.p+1,c),e.p=f.p):(D0(e,f.p,c),e.p=f.p,D0(n,h.p+1,c),n.p=h.p),Cr(f,null),Cr(h,null),f=o,h=l,r=!0;return r}function aXe(e){switch(e.g){case 0:return new tP;case 1:return new iP;case 3:return new yTe;case 4:return new hy;case 5:return new VOe;case 6:return new yo;case 2:return new rP;case 7:return new vx;case 8:return new mx;default:throw P(new Gn("No implementation is available for the layerer "+(e.f!=null?e.f:""+e.g)))}}function FDn(e,n,t,i){var r,c,o,l,f;for(r=!1,c=!1,l=new L(i.j);l.a=n.length)throw P(new ko("Greedy SwitchDecider: Free layer not in graph."));this.c=n[e],this.e=new TC(i),DY(this.e,this.c,(Oe(),Kn)),this.i=new TC(i),DY(this.i,this.c,Wn),this.f=new yDe(this.c),this.a=!c&&r.i&&!r.s&&this.c[0].k==(zn(),gr),this.a&&lxn(this,e,n.length)}function dXe(e,n){var t,i,r,c,o,l;c=!e.B.Gc((Is(),GD)),o=e.B.Gc(Sce),e.a=new YFe(o,c,e.c),e.n&&Wfe(e.a.n,e.n),yX(e.g,(sa(),Oo),e.a),n||(i=new Dj(1,c,e.c),i.n.a=e.k,w4(e.p,(Oe(),Xn),i),r=new Dj(1,c,e.c),r.n.d=e.k,w4(e.p,dt,r),l=new Dj(0,c,e.c),l.n.c=e.k,w4(e.p,Kn,l),t=new Dj(0,c,e.c),t.n.b=e.k,w4(e.p,Wn,t))}function JDn(e){var n,t,i;switch(n=u(T(e.d,(Ce(),q1)),222),n.g){case 2:t=NRn(e);break;case 3:t=(i=new xe,Wi(ci(jo(ou(ou(new bn(null,new wn(e.d.b,16)),new Rg),new QI),new lk),new n0),new _Ee(i)),i);break;default:throw P(new qc("Compaction not supported for "+n+" edges."))}nPn(e,t),rc(new et(e.g),new OEe(e))}function GDn(e,n){var t,i,r,c,o,l,f;if(n.Tg("Process directions",1),t=u(T(e,(Au(),r2)),86),t!=(vr(),Ua))for(r=Et(e.b,0);r.b!=r.d.c;){switch(i=u(yt(r),40),l=u(T(i,(xi(),yD)),15).a,f=u(T(i,kD),15).a,t.g){case 4:f*=-1;break;case 1:c=l,l=f,f=c;break;case 2:o=l,l=-f,f=o}fe(i,yD,me(l)),fe(i,kD,me(f))}n.Ug()}function qDn(e){var n,t,i,r,c,o,l,f;for(f=new OPe,l=new L(e.a);l.a0&&n=0)return!1;if(n.p=t.b,Te(t.e,n),r==(zn(),hr)||r==go){for(o=new L(n.j);o.ae.d[l.p]&&(t+=_ae(e.b,c),k0(e.a,me(c)))):++o;for(t+=e.b.d*o;!aE(e.a);)bhe(e.b,u(g4(e.a),15).a)}return t}function SXe(e){var n,t,i,r,c,o;return c=0,n=of(e),n.ik()&&(c|=4),(e.Bb&fs)!=0&&(c|=2),q(e,103)?(t=u(e,19),r=xc(t),(t.Bb&$u)!=0&&(c|=32),r&&(ht(lp(r)),c|=8,o=r.t,(o>1||o==-1)&&(c|=16),(r.Bb&$u)!=0&&(c|=64)),(t.Bb&kc)!=0&&(c|=R0),c|=Rf):q(n,459)?c|=512:(i=n.ik(),i&&(i.i&1)!=0&&(c|=256)),(e.Bb&512)!=0&&(c|=128),c}function iIn(e,n){var t;return e.f==Rce?(t=aw(Kc((ss(),ec),n)),e.e?t==4&&n!=(J4(),$6)&&n!=(J4(),P6)&&n!=(J4(),Bce)&&n!=(J4(),zce):t==2):e.d&&(e.d.Gc(n)||e.d.Gc(k4(Kc((ss(),ec),n)))||e.d.Gc(ev((ss(),ec),e.b,n)))?!0:e.f&&dbe((ss(),e.f),$C(Kc(ec,n)))?(t=aw(Kc(ec,n)),e.e?t==4:t==2):!1}function rIn(e,n){var t,i,r,c,o,l,f,h;for(c=new xe,n.b.c.length=0,t=u(bs(bae(new bn(null,new wn(new et(e.a.b),1))),Ts(new Bi,new Ei,new eu,z(B(Yo,1),ye,130,0,[($l(),Vo)]))),16),r=t.Jc();r.Ob();)if(i=u(r.Pb(),15),o=Tae(e.a,i),o.b!=0)for(l=new qu(n),Jn(c.c,l),l.p=i.a,h=Et(o,0);h.b!=h.d.c;)f=u(yt(h),9),Cr(f,l);jr(n.b,c)}function CZ(e){var n,t,i,r,c,o,l;for(l=new gt,i=new L(e.a.b);i.aig&&(r-=ig),l=u(ve(i,O6),8),h=l.a,p=l.b+e,c=E.Math.atan2(p,h),c<0&&(c+=ig),c+=n,c>ig&&(c-=ig),ja(),Df(1e-10),E.Math.abs(r-c)<=1e-10||r==c||isNaN(r)&&isNaN(c)?0:rc?1:Tb(isNaN(r),isNaN(c))}function Dbe(e,n,t,i){var r,c,o;n&&(c=W(ie(T(n,(xi(),Fd))))+i,o=t+W(ie(T(n,_J)))/2,fe(n,yD,me(_t(_u(E.Math.round(c))))),fe(n,kD,me(_t(_u(E.Math.round(o))))),n.d.b==0||Dbe(e,u(_$((r=Et(new v1(n).a.d,0),new h3(r))),40),t+W(ie(T(n,_J)))+e.b,i+W(ie(T(n,k7)))),T(n,bre)!=null&&Dbe(e,u(T(n,bre),40),t,i))}function sIn(e,n){var t,i,r,c;if(c=u(ve(e,(Ht(),zv)),64).g-u(ve(n,zv),64).g,c!=0)return c;if(t=u(ve(e,wce),15),i=u(ve(n,wce),15),t&&i&&(r=t.a-i.a,r!=0))return r;switch(u(ve(e,zv),64).g){case 1:return vi(e.i,n.i);case 2:return vi(e.j,n.j);case 3:return vi(n.i,e.i);case 4:return vi(n.j,e.j);default:throw P(new qc(rwe))}}function Ibe(e){var n,t,i;return(e.Db&64)!=0?lZ(e):(n=new Ws(Cpe),t=e.k,t?Gt(Gt((n.a+=' "',n),t),'"'):(!e.n&&(e.n=new we(ku,e,1,7)),e.n.i>0&&(i=(!e.n&&(e.n=new we(ku,e,1,7)),u(X(e.n,0),157)).a,!i||Gt(Gt((n.a+=' "',n),i),'"'))),Gt(Wg(Gt(Wg(Gt(Wg(Gt(Wg((n.a+=" (",n),e.i),","),e.j)," | "),e.g),","),e.f),")"),n.a)}function AXe(e){var n,t,i;return(e.Db&64)!=0?lZ(e):(n=new Ws(Ope),t=e.k,t?Gt(Gt((n.a+=' "',n),t),'"'):(!e.n&&(e.n=new we(ku,e,1,7)),e.n.i>0&&(i=(!e.n&&(e.n=new we(ku,e,1,7)),u(X(e.n,0),157)).a,!i||Gt(Gt((n.a+=' "',n),i),'"'))),Gt(Wg(Gt(Wg(Gt(Wg(Gt(Wg((n.a+=" (",n),e.i),","),e.j)," | "),e.g),","),e.f),")"),n.a)}function lIn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M;for(S=-1,M=0,b=n,p=0,y=b.length;p0&&++M;++S}return M}function fIn(e,n){var t,i,r,c,o;for(n==(Ej(),ere)&&FO(u(wi(e.a,(Mp(),QN)),16)),r=u(wi(e.a,(Mp(),QN)),16).Jc();r.Ob();)switch(i=u(r.Pb(),107),t=u(_e(i.j,0),113).d.j,c=new ds(i.j),xr(c,new w5),n.g){case 2:tZ(e,c,t,(yw(),V0),1);break;case 1:case 0:o=eNn(c),tZ(e,new y0(c,0,o),t,(yw(),V0),0),tZ(e,new y0(c,o,c.c.length),t,V0,1)}}function aIn(e){var n,t,i,r,c,o,l;for(r=u(T(e,(pe(),Qw)),9),i=e.j,t=(pn(0,i.c.length),u(i.c[0],12)),o=new L(r.j);o.ar.p?(Ar(c,dt),c.d&&(l=c.o.b,n=c.a.b,c.a.b=l-n)):c.j==dt&&r.p>e.p&&(Ar(c,Xn),c.d&&(l=c.o.b,n=c.a.b,c.a.b=-(l-n)));break}return r}function _be(e,n){var t,i,r,c,o,l,f;if(n==null||n.length==0)return null;if(r=u(so(e.a,n),144),!r){for(i=(l=new rt(e.b).a.vc().Jc(),new Hi(l));i.a.Ob();)if(t=(c=u(i.a.Pb(),45),u(c.kd(),144)),o=t.c,f=n.length,hn(o.substr(o.length-f,f),n)&&(n.length==o.length||ic(o,o.length-n.length-1)==46)){if(r)return null;r=t}r&&Xc(e.a,n,r)}return r}function d8(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(c=new ke(n,t),b=new L(e.a);b.a1,l&&(i=new ke(r,t.b),qt(n.a,i)),bj(n.a,z(B(_r,1),je,8,0,[y,p]))}function P0(){P0=Y,SJ=new U2(aa,0),dD=new U2("NIKOLOV",1),bD=new U2("NIKOLOV_PIXEL",2),T4e=new U2("NIKOLOV_IMPROVED",3),x4e=new U2("NIKOLOV_IMPROVED_PIXEL",4),M4e=new U2("DUMMYNODE_PERCENTAGE",5),C4e=new U2("NODECOUNT_PERCENTAGE",6),AJ=new U2("NO_BOUNDARY",7),m7=new U2("MODEL_ORDER_LEFT_TO_RIGHT",8),bA=new U2("MODEL_ORDER_RIGHT_TO_LEFT",9)}function NZ(e,n){var t,i,r,c,o,l,f,h,b,p,y,S;return b=null,y=ibe(e,n),i=null,l=u(ve(n,(Ht(),vfn)),300),l?i=l:i=(hj(),FD),S=i,S==(hj(),FD)&&(r=null,h=u(Bn(e.r,y),300),h?r=h:r=jce,S=r),Qt(e.r,n,S),c=null,f=u(ve(n,mfn),278),f?c=f:c=(U9(),LD),p=c,p==(U9(),LD)&&(o=null,t=u(Bn(e.b,y),278),t?o=t:o=rG,p=o),b=u(Qt(e.b,n,p),278),b}function EIn(e){var n,t,i,r,c;for(i=e.length,n=new hE,c=0;c=40,o&&k_n(e),ILn(e),eDn(e),t=xFe(e),i=0;t&&i0&&qt(e.g,c)):(e.d[o]-=h+1,e.d[o]<=0&&e.a[o]>0&&qt(e.f,c))))}function RXe(e,n,t,i){var r,c,o,l,f,h,b;for(f=new ke(t,i),Or(f,u(T(n,(xi(),y7)),8)),b=Et(n.b,0);b.b!=b.d.c;)h=u(yt(b),40),bi(h.e,f),qt(e.b,h);for(l=u(bs(aae(new bn(null,new wn(n.a,16))),Ts(new Bi,new Ei,new eu,z(B(Yo,1),ye,130,0,[($l(),Vo)]))),16).Jc();l.Ob();){for(o=u(l.Pb(),65),c=Et(o.a,0);c.b!=c.d.c;)r=u(yt(c),8),r.a+=f.a,r.b+=f.b;qt(e.a,o)}}function Hbe(e,n){var t,i,r,c;if(0<(q(e,18)?u(e,18).gc():ra(e.Jc()))){if(r=n,1=0&&f1)&&n==1&&u(e.a[e.b],9).k==(zn(),Gu)?U4(u(e.a[e.b],9),(ol(),i1)):i&&(!t||(e.c-e.b&e.a.length-1)>1)&&n==1&&u(e.a[e.c-1&e.a.length-1],9).k==(zn(),Gu)?U4(u(e.a[e.c-1&e.a.length-1],9),(ol(),rb)):(e.c-e.b&e.a.length-1)==2?(U4(u(vj(e),9),(ol(),i1)),U4(u(vj(e),9),rb)):vOn(e,r),Nae(e)}function BIn(e){var n,t,i,r,c,o,l,f;for(f=new gt,n=new fX,o=e.Jc();o.Ob();)r=u(o.Pb(),9),l=rw(Wx(new jb,r),n),Xo(f.f,r,l);for(c=e.Jc();c.Ob();)for(r=u(c.Pb(),9),i=new qn(Vn(Ni(r).a.Jc(),new ne));at(i);)t=u(tt(i),17),!cc(t)&&Pf(Sf(jf(Ef(Af(new Wl,E.Math.max(1,u(T(t,(Ce(),u4e)),15).a)),1),u(Bn(f,t.c.i),124)),u(Bn(f,t.d.i),124)));return n}function FXe(e,n,t,i){var r,c,o,l,f,h,b,p,y,S;if(w8n(e,n,t),c=n[t],S=i?(Oe(),Kn):(Oe(),Wn),zwn(n.length,t,i)){for(r=n[i?t-1:t+1],Vae(e,r,i?(Cc(),No):(Cc(),vs)),f=c,b=0,y=f.length;bc*2?(b=new aB(p),h=cs(o)/Hs(o),f=nW(b,n,new q5,t,i,r,h),bi(ta(b.e),f),p.c.length=0,c=0,Jn(p.c,b),Jn(p.c,o),c=cs(b)*Hs(b)+cs(o)*Hs(o)):(Jn(p.c,o),c+=cs(o)*Hs(o));return p}function FIn(e,n){var t,i,r,c,o,l,f;for(n.Tg("Port order processing",1),f=u(T(e,(Ce(),c4e)),421),i=new L(e.b);i.at?n:t;h<=p;++h)h==t?l=i++:(c=r[h],b=M.$l(c.Jk()),h==n&&(f=h==p&&!b?i-1:i),b&&++i);return y=u(xj(e,n,t),75),l!=f&&Ky(e,new eO(e.e,7,o,me(l),S.kd(),f)),y}}else return u(pZ(e,n,t),75);return u(xj(e,n,t),75)}function Jbe(e,n){var t,i,r,c,o,l,f,h,b,p;for(p=0,c=new A3,k0(c,n);c.b!=c.c;)for(f=u(g4(c),218),h=0,b=u(T(n.j,(Ce(),n1)),269),u(T(n.j,rA),329),o=W(ie(T(n.j,oD))),l=W(ie(T(n.j,kie))),b!=(P1(),W0)&&(h+=o*MOn(n.j,f.e,b),h+=l*lIn(n.j,f.e)),p+=fJe(f.d,f.e)+h,r=new L(f.b);r.a=0&&(l=tAn(e,o),!(l&&(h<22?f.l|=1<>>1,o.m=b>>>1|(p&1)<<21,o.l=y>>>1|(b&1)<<21,--h;return t&&KY(f),c&&(i?(q0=z9(e),r&&(q0=wze(q0,(O9(),bme)))):q0=Io(e.l,e.m,e.h)),f}function GIn(e,n){var t,i,r,c,o,l,f,h,b,p;for(h=e.e[n.c.p][n.p]+1,f=n.c.a.c.length+1,l=new L(e.a);l.a0&&(Yn(0,e.length),e.charCodeAt(0)==45||(Yn(0,e.length),e.charCodeAt(0)==43))?1:0,i=o;it)throw P(new rh(Pw+e+'"'));return l}function qIn(e){var n,t,i,r,c,o,l;for(o=new ji,c=new L(e.a);c.a=e.length)return t.o=0,!0;switch(ic(e,n[0])){case 43:r=1;break;case 45:r=-1;break;default:return t.o=0,!0}if(++n[0],c=n[0],o=Sz(e,n),o==0&&n[0]==c)return!1;if(n[0]l&&(l=r,b.c.length=0),r==l&&Te(b,new yc(t.c.i,t)));vn(),xr(b,e.c),xb(e.b,f.p,b)}}function QIn(e,n){var t,i,r,c,o,l,f,h,b;for(o=new L(n.b);o.al&&(l=r,b.c.length=0),r==l&&Te(b,new yc(t.d.i,t)));vn(),xr(b,e.c),xb(e.f,f.p,b)}}function ZIn(e){var n,t,i,r,c,o,l;for(c=Ma(e),r=new ct((!e.e&&(e.e=new Cn(wr,e,7,4)),e.e));r.e!=r.i.gc();)if(i=u(lt(r),85),l=iu(u(X((!i.c&&(i.c=new Cn(pt,i,5,8)),i.c),0),84)),!gp(l,c))return!0;for(t=new ct((!e.d&&(e.d=new Cn(wr,e,8,5)),e.d));t.e!=t.i.gc();)if(n=u(lt(t),85),o=iu(u(X((!n.b&&(n.b=new Cn(pt,n,4,7)),n.b),0),84)),!gp(o,c))return!0;return!1}function WIn(e){var n,t,i,r,c;i=u(T(e,(pe(),gi)),26),c=u(ve(i,(Ce(),bg)),182).Gc((Xs(),Eg)),e.e||(r=u(T(e,wo),22),n=new ke(e.f.a+e.d.b+e.d.c,e.f.b+e.d.d+e.d.a),r.Gc((Oc(),ql))?(yi(i,Zi,(Rr(),eo)),Iw(i,n.a,n.b,!1,!0)):$e(Pe(ve(i,Iie)))||Iw(i,n.a,n.b,!0,!0)),c?yi(i,bg,We(Eg)):yi(i,bg,(t=u(na(UA),10),new Nl(t,u(Tf(t,t.length),10),0)))}function e_n(e,n){var t,i,r,c,o,l,f,h;if(h=Pe(T(n,(Au(),hsn))),h==null||(_n(h),h)){for(CCn(e,n),r=new xe,f=Et(n.b,0);f.b!=f.d.c;)o=u(yt(f),40),t=T0e(e,o,null),t&&(Lu(t,n),Jn(r.c,t));if(e.a=null,e.b=null,r.c.length>1)for(i=new L(r);i.a=0&&l!=t&&(c=new Dr(e,1,l,o,null),i?i.lj(c):i=c),t>=0&&(c=new Dr(e,1,t,l==t?o:null,n),i?i.lj(c):i=c)),i}function JXe(e){var n,t,i;if(e.b==null){if(i=new ad,e.i!=null&&($c(i,e.i),i.a+=":"),(e.f&256)!=0){for((e.f&256)!=0&&e.a!=null&&(d5n(e.i)||(i.a+="//"),$c(i,e.a)),e.d!=null&&(i.a+="/",$c(i,e.d)),(e.f&16)!=0&&(i.a+="/"),n=0,t=e.j.length;ny?!1:(p=(f=eS(i,y,!1),f.a),b+l+p<=n.b&&(ZC(t,c-t.s),t.c=!0,ZC(i,c-t.s),NO(i,t.s,t.t+t.d+l),i.k=!0,Khe(t.q,i),S=!0,r&&(pB(n,i),i.j=n,e.c.length>o&&(LO((pn(o,e.c.length),u(e.c[o],186)),i),(pn(o,e.c.length),u(e.c[o],186)).a.c.length==0&&yd(e,o)))),S)}function o_n(e,n){var t,i,r,c,o,l;if(n.Tg("Partition midprocessing",1),r=new bw,Wi(ci(new bn(null,new wn(e.a,16)),new ty),new bEe(r)),r.d!=0){for(l=u(bs(bae((c=r.i,new bn(null,(c||(r.i=new T3(r,r.c))).Lc()))),Ts(new Bi,new Ei,new eu,z(B(Yo,1),ye,130,0,[($l(),Vo)]))),16),i=l.Jc(),t=u(i.Pb(),15);i.Ob();)o=u(i.Pb(),15),WOn(u(wi(r,t),22),u(wi(r,o),22)),t=o;n.Ug()}}function Yj(e,n){var t,i,r,c,o;if(e.Ab){if(e.Ab){if(o=e.Ab.i,o>0){if(r=u(e.Ab.g,1995),n==null){for(c=0;ct.s&&lf+M&&(O=p.g+y.g,y.a=(y.g*y.a+p.g*p.a)/O,y.g=O,p.f=y,t=!0)),c=l,p=y;return t}function f_n(e,n,t){var i,r,c,o,l,f,h,b;for(t.Tg($Qe,1),Fu(e.b),Fu(e.a),l=null,c=Et(n.b,0);!l&&c.b!=c.d.c;)h=u(yt(c),40),$e(Pe(T(h,(xi(),tb))))&&(l=h);for(f=new ji,Xi(f,l,f.c.b,f.c),EVe(e,f),b=Et(n.b,0);b.b!=b.d.c;)h=u(yt(b),40),o=Dt(T(h,(xi(),jA))),r=so(e.b,o)!=null?u(so(e.b,o),15).a:0,fe(h,fre,me(r)),i=1+(so(e.a,o)!=null?u(so(e.a,o),15).a:0),fe(h,s6e,me(i));t.Ug()}function VXe(e){G2(e,new xw(H2(B2(F2(z2(new od,Hw),"ELK Box"),"Algorithm for packing of unconnected boxes, i.e. graphs without edges."),new HT))),Ee(e,Hw,Fp,u9e),Ee(e,Hw,zp,15),Ee(e,Hw,kN,me(0)),Ee(e,Hw,ype,Ie(i9e)),Ee(e,Hw,cv,Ie(ufn)),Ee(e,Hw,n6,Ie(ofn)),Ee(e,Hw,O8,oZe),Ee(e,Hw,hS,Ie(r9e)),Ee(e,Hw,t6,Ie(c9e)),Ee(e,Hw,kpe,Ie(rce)),Ee(e,Hw,MF,Ie(cfn))}function YXe(e,n){var t,i,r,c,o,l,f,h,b;if(r=e.i,o=r.o.a,c=r.o.b,o<=0&&c<=0)return Oe(),yu;switch(h=e.n.a,b=e.n.b,l=e.o.a,t=e.o.b,n.g){case 2:case 1:if(h<0)return Oe(),Kn;if(h+l>o)return Oe(),Wn;break;case 4:case 3:if(b<0)return Oe(),Xn;if(b+t>c)return Oe(),dt}return f=(h+l/2)/o,i=(b+t/2)/c,f+i<=1&&f-i<=0?(Oe(),Kn):f+i>=1&&f-i>=0?(Oe(),Wn):i<.5?(Oe(),Xn):(Oe(),dt)}function QXe(e,n,t,i,r,c,o){var l,f,h,b,p,y;for(y=new r4,h=n.Jc();h.Ob();)for(l=u(h.Pb(),837),p=new L(l.Pf());p.a0?l.a?(h=l.b.Kf().b,r>h&&(e.v||l.c.d.c.length==1?(o=(r-h)/2,l.d.d=o,l.d.a=o):(t=u(_e(l.c.d,0),187).Kf().b,i=(t-h)/2,l.d.d=E.Math.max(0,i),l.d.a=r-i-h))):l.d.a=e.t+r:VE(e.u)&&(c=l0e(l.b),c.d<0&&(l.d.d=-c.d),c.d+c.a>l.b.Kf().b&&(l.d.a=c.d+c.a-l.b.Kf().b))}function $f(){$f=Y,f6=new Vr((Ht(),_D),me(1)),pH=new Vr(Jd,80),gtn=new Vr(P9e,5),ctn=new Vr(j7,C8),dtn=new Vr(mce,me(1)),btn=new Vr(vce,(Pn(),!0)),Yme=new iw(50),atn=new Vr(t1,Yme),Xme=DD,Qme=RA,utn=new Vr(sce,!1),Vme=ID,ltn=wm,ftn=ib,stn=yg,otn=Rv,htn=pm,Kme=(v0e(),Znn),wte=ttn,wH=Qnn,gte=Wnn,Zme=ntn,mtn=M7,vtn=tG,ptn=vm,wtn=A7,Wme=(I4(),Em),new Vr(D6,Wme)}function d_n(e,n){var t;switch(oO(e)){case 6:return Pr(n);case 7:return K2(n);case 8:return X2(n);case 3:return Array.isArray(n)&&(t=oO(n),!(t>=14&&t<=16));case 11:return n!=null&&typeof n===uW;case 12:return n!=null&&(typeof n===uN||typeof n==uW);case 0:return NQ(n,e.__elementTypeId$);case 2:return hV(n)&&n.Rm!==nt;case 1:return hV(n)&&n.Rm!==nt||NQ(n,e.__elementTypeId$);default:return!0}}function b_n(e){var n,t,i,r;i=e.o,Z2(),e.A.dc()||ai(e.A,Hme)?r=i.a:(e.D?r=E.Math.max(i.a,Fj(e.f)):r=Fj(e.f),e.A.Gc((Xs(),HD))&&!e.B.Gc((Is(),XA))&&(r=E.Math.max(r,Fj(u(Rc(e.p,(Oe(),Xn)),253))),r=E.Math.max(r,Fj(u(Rc(e.p,dt),253)))),n=UBe(e),n&&(r=E.Math.max(r,n.a))),$e(Pe(e.e.Rf().mf((Ht(),wm))))?i.a=E.Math.max(i.a,r):i.a=r,t=e.f.i,t.c=0,t.b=r,RZ(e.f)}function ZXe(e,n){var t,i,r,c;return i=E.Math.min(E.Math.abs(e.c-(n.c+n.b)),E.Math.abs(e.c+e.b-n.c)),c=E.Math.min(E.Math.abs(e.d-(n.d+n.a)),E.Math.abs(e.d+e.a-n.d)),t=E.Math.abs(e.c+e.b/2-(n.c+n.b/2)),t>e.b/2+n.b/2||(r=E.Math.abs(e.d+e.a/2-(n.d+n.a/2)),r>e.a/2+n.a/2)?1:t==0&&r==0?0:t==0?c/r+1:r==0?i/t+1:E.Math.min(i/t,c/r)+1}function g_n(e,n){var t,i,r,c,o,l,f;for(c=0,l=0,f=0,r=new L(e.f.e);r.a0&&e.d!=(fj(),vte)&&(l+=o*(i.d.a+e.a[n.a][i.a]*(n.d.a-i.d.a)/t)),t>0&&e.d!=(fj(),pte)&&(f+=o*(i.d.b+e.a[n.a][i.a]*(n.d.b-i.d.b)/t)));switch(e.d.g){case 1:return new ke(l/c,n.d.b);case 2:return new ke(n.d.a,f/c);default:return new ke(l/c,f/c)}}function WXe(e){var n,t,i,r,c,o;for(t=(!e.a&&(e.a=new mr(pl,e,5)),e.a).i+2,o=new So(t),Te(o,new ke(e.j,e.k)),Wi(new bn(null,(!e.a&&(e.a=new mr(pl,e,5)),new wn(e.a,16))),new Xje(o)),Te(o,new ke(e.b,e.c)),n=1;n0&&(vO(f,!1,(vr(),Zc)),vO(f,!0,ru)),Ao(n.g,new qTe(e,t)),Qt(e.g,n,t)}function Ube(){Ube=Y,sln=new cn(W2e,(Pn(),!1)),me(-1),nln=new cn(epe,me(-1)),me(-1),tln=new cn(npe,me(-1)),iln=new cn(tpe,!1),rln=new cn(ipe,!1),uye=(XR(),Jre),hln=new cn(rpe,uye),dln=new cn(cpe,-1),cye=(qB(),Bre),aln=new cn(upe,cye),fln=new cn(ope,!0),iye=(tB(),Gre),oln=new cn(spe,iye),uln=new cn(lpe,!1),me(1),cln=new cn(fpe,me(1)),rye=(zB(),qre),lln=new cn(ape,rye)}function tKe(){tKe=Y;var e;for(Eme=z(B(It,1),Zt,30,15,[-1,-1,30,19,15,13,11,11,10,9,9,8,8,8,8,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5]),Wne=oe(It,Zt,30,37,15,1),Uen=z(B(It,1),Zt,30,15,[-1,-1,63,40,32,28,25,23,21,20,19,19,18,18,17,17,16,16,16,15,15,15,15,14,14,14,14,14,14,13,13,13,13,13,13,13,13]),jme=oe(l2,gYe,30,37,14,1),e=2;e<=36;e++)Wne[e]=sc(E.Math.pow(e,Eme[e])),jme[e]=$O(fN,Wne[e])}function w_n(e){var n;if((!e.a&&(e.a=new we(Pi,e,6,6)),e.a).i!=1)throw P(new Gn(NZe+(!e.a&&(e.a=new we(Pi,e,6,6)),e.a).i));return n=new Ss,JY(u(X((!e.b&&(e.b=new Cn(pt,e,4,7)),e.b),0),84))&&fc(n,zVe(e,JY(u(X((!e.b&&(e.b=new Cn(pt,e,4,7)),e.b),0),84)),!1)),JY(u(X((!e.c&&(e.c=new Cn(pt,e,5,8)),e.c),0),84))&&fc(n,zVe(e,JY(u(X((!e.c&&(e.c=new Cn(pt,e,5,8)),e.c),0),84)),!0)),n}function iKe(e,n){var t,i,r,c,o;for(n.d?r=e.a.c==(oh(),i2)?rr(n.b):Ni(n.b):r=e.a.c==(oh(),zd)?rr(n.b):Ni(n.b),c=!1,i=new qn(Vn(r.a.Jc(),new ne));at(i);)if(t=u(tt(i),17),o=$e(e.a.f[e.a.g[n.b.p].p]),!(!o&&!cc(t)&&t.c.i.c==t.d.i.c)&&!($e(e.a.n[e.a.g[n.b.p].p])||$e(e.a.n[e.a.g[n.b.p].p]))&&(c=!0,ef(e.b,e.a.g[BSn(t,n.b).p])))return n.c=!0,n.a=t,n;return n.c=c,n.a=null,n}function Xbe(e,n,t){var i,r,c,o,l,f,h;if(i=t.gc(),i==0)return!1;if(e.Nj())if(f=e.Oj(),ede(e,n,t),o=i==1?e.Gj(3,null,t.Jc().Pb(),n,f):e.Gj(5,null,t,n,f),e.Kj()){for(l=i<100?null:new f0(i),c=n+i,r=n;r0){for(o=0;o>16==-15&&e.Cb.Vh()&&pY(new hY(e.Cb,9,13,t,e.c,Cd(xs(u(e.Cb,62)),e))):q(e.Cb,88)&&e.Db>>16==-23&&e.Cb.Vh()&&(n=e.c,q(n,88)||(n=(mn(),vf)),q(t,88)||(t=(mn(),vf)),pY(new hY(e.Cb,9,10,t,n,Cd(Xu(u(e.Cb,29)),e)))))),e.c}function uKe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M;if(n==t)return!0;if(n=Q0e(e,n),t=Q0e(e,t),i=RQ(n),i){if(b=RQ(t),b!=i)return b?(f=i.kk(),M=b.kk(),f==M&&f!=null):!1;if(o=(!n.d&&(n.d=new mr(Pc,n,1)),n.d),c=o.i,y=(!t.d&&(t.d=new mr(Pc,t,1)),t.d),c==y.i){for(h=0;h0,l=GB(n,c),nle(t?l.b:l.g,n),H3(l).c.length==1&&Xi(i,l,i.c.b,i.c),r=new yc(c,n),k0(e.o,r),Go(e.e.a,c))}function lKe(e,n){var t,i,r,c,o,l,f;return i=E.Math.abs(gR(e.b).a-gR(n.b).a),l=E.Math.abs(gR(e.b).b-gR(n.b).b),r=0,f=0,t=1,o=1,i>e.b.b/2+n.b.b/2&&(r=E.Math.min(E.Math.abs(e.b.c-(n.b.c+n.b.b)),E.Math.abs(e.b.c+e.b.b-n.b.c)),t=1-r/i),l>e.b.a/2+n.b.a/2&&(f=E.Math.min(E.Math.abs(e.b.d-(n.b.d+n.b.a)),E.Math.abs(e.b.d+e.b.a-n.b.d)),o=1-f/l),c=E.Math.min(t,o),(1-c)*E.Math.sqrt(i*i+l*l)}function E_n(e){var n,t,i,r;for(eW(e,e.e,e.f,(gw(),nb),!0,e.c,e.i),eW(e,e.e,e.f,nb,!1,e.c,e.i),eW(e,e.e,e.f,Nv,!0,e.c,e.i),eW(e,e.e,e.f,Nv,!1,e.c,e.i),v_n(e,e.c,e.e,e.f,e.i),i=new Gr(e.i,0);i.b=65;t--)Qa[t]=t-65<<24>>24;for(i=122;i>=97;i--)Qa[i]=i-97+26<<24>>24;for(r=57;r>=48;r--)Qa[r]=r-48+52<<24>>24;for(Qa[43]=62,Qa[47]=63,c=0;c<=25;c++)Yd[c]=65+c&yr;for(o=26,f=0;o<=51;++o,f++)Yd[o]=97+f&yr;for(e=52,l=0;e<=61;++e,l++)Yd[e]=48+l&yr;Yd[62]=43,Yd[63]=47}function fKe(e,n){var t,i,r,c,o,l;return r=Jhe(e),l=Jhe(n),r==l?e.e==n.e&&e.a<54&&n.a<54?e.fn.f?1:0:(i=e.e-n.e,t=(e.d>0?e.d:E.Math.floor((e.a-1)*wYe)+1)-(n.d>0?n.d:E.Math.floor((n.a-1)*wYe)+1),t>i+1?r:t0&&(o=D3(o,EKe(i))),aHe(c,o))):rh&&(y=0,S+=f+n,f=0),d8(o,y,S),t=E.Math.max(t,y+b.a),f=E.Math.max(f,b.b),y+=b.a+n;return new ke(t+n,S+f+n)}function Qbe(e,n){var t,i,r,c,o,l,f;if(!Ma(e))throw P(new qc(OZe));if(i=Ma(e),c=i.g,r=i.f,c<=0&&r<=0)return Oe(),yu;switch(l=e.i,f=e.j,n.g){case 2:case 1:if(l<0)return Oe(),Kn;if(l+e.g>c)return Oe(),Wn;break;case 4:case 3:if(f<0)return Oe(),Xn;if(f+e.f>r)return Oe(),dt}return o=(l+e.g/2)/c,t=(f+e.f/2)/r,o+t<=1&&o-t<=0?(Oe(),Kn):o+t>=1&&o-t>=0?(Oe(),Wn):t<.5?(Oe(),Xn):(Oe(),dt)}function A_n(e,n,t,i,r){var c,o;if(c=pc($r(n[0],Nc),$r(i[0],Nc)),e[0]=_t(c),c=ow(c,32),t>=r){for(o=1;o0&&(r.b[o++]=0,r.b[o++]=c.b[0]-1),n=1;n0&&(kb(f,f.d-r.d),r.c==(ca(),eb)&&QU(f,f.a-r.d),f.d<=0&&f.i>0&&Xi(n,f,n.c.b,n.c)));for(c=new L(e.f);c.a0&&(o0(l,l.i-r.d),r.c==(ca(),eb)&&SP(l,l.b-r.d),l.i<=0&&l.d>0&&Xi(t,l,t.c.b,t.c)))}function x_n(e,n,t,i,r){var c,o,l,f,h,b,p,y,S;for(vn(),xr(e,new qT),o=OC(e),S=new xe,y=new xe,l=null,f=0;o.b!=0;)c=u(o.b==0?null:(ft(o.b!=0),_l(o,o.a.a)),167),!l||cs(l)*Hs(l)/21&&(f>cs(l)*Hs(l)/2||o.b==0)&&(p=new aB(y),b=cs(l)/Hs(l),h=nW(p,n,new q5,t,i,r,b),bi(ta(p.e),h),l=p,Jn(S.c,p),f=0,y.c.length=0));return jr(S,y),S}function Yu(e,n,t,i,r){bd();var c,o,l,f,h,b,p;if(Cfe(e,"src"),Cfe(t,"dest"),p=Gs(e),f=Gs(t),nfe((p.i&4)!=0,"srcType is not an array"),nfe((f.i&4)!=0,"destType is not an array"),b=p.c,o=f.c,nfe((b.i&1)!=0?b==o:(o.i&1)==0,"Array types don't match"),W7n(e,n,t,i,r),(b.i&1)==0&&p!=f)if(h=x4(e),c=x4(t),ue(e)===ue(t)&&ni;)tr(c,l,h[--n]);else for(l=i+r;i0),i.a.Xb(i.c=--i.b),p>y+f&&As(i);for(o=new L(S);o.a0),i.a.Xb(i.c=--i.b)}}function O_n(){si();var e,n,t,i,r,c;if(Hce)return Hce;for(e=new tl(4),Lp(e,$0(zne,!0)),iS(e,$0("M",!0)),iS(e,$0("C",!0)),c=new tl(4),i=0;i<11;i++)ao(c,i,i);return n=new tl(4),Lp(n,$0("M",!0)),ao(n,4448,4607),ao(n,65438,65439),r=new RE(2),Zb(r,e),Zb(r,rM),t=new RE(2),t.Hm(lR(c,$0("L",!0))),t.Hm(n),t=new hp(3,t),t=new Nfe(r,t),Hce=t,Hce}function _p(e,n){var t,i,r,c,o,l,f,h;for(t=new RegExp(n,"g"),f=oe(Re,je,2,0,6,1),i=0,h=e,c=null;;)if(l=t.exec(h),l==null||h==""){f[i]=h;break}else o=l.index,f[i]=(Yr(0,o,h.length),h.substr(0,o)),h=rf(h,o+l[0].length,h.length),t.lastIndex=0,c==h&&(f[i]=(Yr(0,1,h.length),h.substr(0,1)),h=(Yn(1,h.length+1),h.substr(1))),c=h,++i;if(e.length>0){for(r=f.length;r>0&&f[r-1]=="";)--r;rb&&(b=f);for(h=E.Math.pow(4,n),b>h&&(h=b),y=(E.Math.log(h)-E.Math.log(1))/n,c=E.Math.exp(y),r=c,o=0;o0&&(p-=i[0]+e.c,i[0]+=e.c),i[2]>0&&(p-=i[2]+e.c),i[1]=E.Math.max(i[1],p),aR(e.a[1],t.c+n.b+i[0]-(i[1]-p)/2,i[1]);for(c=e.a,l=0,h=c.length;l0?(e.n.c.length-1)*e.i:0,i=new L(e.n);i.a1)for(i=Et(r,0);i.b!=i.d.c;)for(t=u(yt(i),235),c=0,f=new L(t.e);f.a0&&(n[0]+=e.c,p-=n[0]),n[2]>0&&(p-=n[2]+e.c),n[1]=E.Math.max(n[1],p),hR(e.a[1],i.d+t.d+n[0]-(n[1]-p)/2,n[1]);else for(M=i.d+t.d,S=i.a-t.d-t.a,o=e.a,f=0,b=o.length;f=n.o&&t.f<=n.f||n.a*.5<=t.f&&n.a*1.5>=t.f){if(o=u(_e(n.n,n.n.c.length-1),208),o.e+o.d+t.g+r<=i&&(c=u(_e(n.n,n.n.c.length-1),208),c.f-e.f+t.f<=e.b||e.a.c.length==1))return q1e(n,t),!0;if(n.s+t.g<=i&&n.t+n.d+t.f+r<=e.f+e.b)return Te(n.b,t),l=u(_e(n.n,n.n.c.length-1),208),Te(n.n,new _R(n.s,l.f+l.a+n.i,n.i)),Sde(u(_e(n.n,n.n.c.length-1),208),t),dKe(n,t),!0}return!1}function Fz(e,n,t,i){var r,c,o,l,f;if(f=Lo(e.e.Ah(),n),r=u(e.g,122),Tc(),u(n,69).vk()){for(o=0;o0||jw(r.b.d,e.b.d+e.b.a)==0&&i.b<0||jw(r.b.d+r.b.a,e.b.d)==0&&i.b>0){l=0;break}}else l=E.Math.min(l,iqe(e,r,i));l=E.Math.min(l,gKe(e,c,l,i))}return l}function Wbe(e,n){var t,i,r,c,o,l,f;if(e.b<2)throw P(new Gn("The vector chain must contain at least a source and a target point."));for(r=(ft(e.b!=0),u(e.a.a.c,8)),pC(n,r.a,r.b),f=new u4((!n.a&&(n.a=new mr(pl,n,5)),n.a)),o=Et(e,1);o.a=0&&c!=t))throw P(new Gn(LN));for(r=0,f=0;fW(Sa(o.g,o.d[0]).a)?(ft(f.b>0),f.a.Xb(f.c=--f.b),W2(f,o),r=!0):l.e&&l.e.gc()>0&&(c=(!l.e&&(l.e=new xe),l.e).Kc(n),h=(!l.e&&(l.e=new xe),l.e).Kc(t),(c||h)&&((!l.e&&(l.e=new xe),l.e).Ec(o),++o.c));r||Jn(i.c,o)}function B_n(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R;return p=e.a.i+e.a.g/2,y=e.a.i+e.a.g/2,M=n.i+n.g/2,I=n.j+n.f/2,l=new ke(M,I),h=u(ve(n,(Ht(),O6)),8),h.a=h.a+p,h.b=h.b+y,c=(l.b-h.b)/(l.a-h.a),i=l.b-c*l.a,O=t.i+t.g/2,R=t.j+t.f/2,f=new ke(O,R),b=u(ve(t,O6),8),b.a=b.a+p,b.b=b.b+y,o=(f.b-b.b)/(f.a-b.a),r=f.b-o*f.a,S=(i-r)/(o-c),h.a>>0,"0"+n.toString(16)),i="\\x"+rf(t,t.length-2,t.length)):e>=kc?(t=(n=e>>>0,"0"+n.toString(16)),i="\\v"+rf(t,t.length-6,t.length)):i=""+String.fromCharCode(e&yr)}return i}function yKe(e,n){var t,i,r,c,o,l,f,h,b;for(c=new L(e.b);c.at){n.Ug();return}switch(u(T(e,(Ce(),Rie)),350).g){case 2:c=new ly;break;case 0:c=new k2;break;default:c=new cT}if(i=c.mg(e,r),!c.ng())switch(u(T(e,vJ),351).g){case 2:i=rqe(r,i);break;case 1:i=KJe(r,i)}zLn(e,r,i),n.Ug()}function Qj(e,n){var t,i,r,c,o,l,f,h;n%=24,e.q.getHours()!=n&&(i=new E.Date(e.q.getTime()),i.setDate(i.getDate()+1),l=e.q.getTimezoneOffset()-i.getTimezoneOffset(),l>0&&(f=l/60|0,h=l%60,r=e.q.getDate(),t=e.q.getHours(),t+f>=24&&++r,c=new E.Date(e.q.getFullYear(),e.q.getMonth(),r,n+f,e.q.getMinutes()+h,e.q.getSeconds(),e.q.getMilliseconds()),e.q.setTime(c.getTime()))),o=e.q.getTime(),e.q.setTime(o+36e5),e.q.getHours()!=n&&e.q.setTime(o)}function U_n(e,n){var t,i,r,c;if(M4n(e.d,e.e),e.c.a.$b(),W(ie(T(n.j,(Ce(),oD))))!=0||W(ie(T(n.j,oD)))!=0)for(t=ov,ue(T(n.j,n1))!==ue((P1(),W0))&&fe(n.j,(pe(),Y0),(Pn(),!0)),c=u(T(n.j,aA),15).a,r=0;rr&&++h,Te(o,(pn(l+h,n.c.length),u(n.c[l+h],15))),f+=(pn(l+h,n.c.length),u(n.c[l+h],15)).a-i,++t;t=I&&e.e[f.p]>M*e.b||K>=t*I)&&(Jn(y.c,l),l=new xe,fc(o,c),c.a.$b(),h-=b,S=E.Math.max(S,h*e.b+O),h+=K,G=K,K=0,b=0,O=0);return new yc(S,y)}function zZ(e){var n,t,i,r,c,o,l;if(!e.d){if(l=new oU,n=ZA,c=n.a.yc(e,n),c==null){for(i=new ct(tu(e));i.e!=i.i.gc();)t=u(lt(i),29),er(l,zZ(t));n.a.Ac(e)!=null,n.a.gc()==0}for(o=l.i,r=(!e.q&&(e.q=new we(pf,e,11,10)),new ct(e.q));r.e!=r.i.gc();++o)u(lt(r),403);er(l,(!e.q&&(e.q=new we(pf,e,11,10)),e.q)),yp(l),e.d=new y3((u(X(ge((p0(),Rn).o),9),19),l.i),l.g),e.e=u(l.g,678),e.e==null&&(e.e=Han),Ms(e).b&=-17}return e.d}function g8(e,n,t,i){var r,c,o,l,f,h;if(h=Lo(e.e.Ah(),n),f=0,r=u(e.g,122),Tc(),u(n,69).vk()){for(o=0;o1||M==-1)if(p=u(O,72),y=u(b,72),p.dc())y.$b();else for(o=!!xc(n),c=0,l=e.a?p.Jc():p.Gi();l.Ob();)h=u(l.Pb(),57),r=u(Ca(e,h),57),r?(o?(f=y.bd(r),f==-1?y.Ei(c,r):c!=f&&y.Si(c,r)):y.Ei(c,r),++c):e.b&&!o&&(y.Ei(c,h),++c);else O==null?b.Wb(null):(r=Ca(e,O),r==null?e.b&&!xc(n)&&b.Wb(O):b.Wb(r))}function Q_n(e,n){var t,i,r,c,o,l,f,h;for(t=new ey,r=new qn(Vn(rr(n).a.Jc(),new ne));at(r);)if(i=u(tt(r),17),!cc(i)&&(l=i.c.i,c0e(l,kH))){if(h=Tbe(e,l,kH,yH),h==-1)continue;t.b=E.Math.max(t.b,h),!t.a&&(t.a=new xe),Te(t.a,l)}for(o=new qn(Vn(Ni(n).a.Jc(),new ne));at(o);)if(c=u(tt(o),17),!cc(c)&&(f=c.d.i,c0e(f,yH))){if(h=Tbe(e,f,yH,kH),h==-1)continue;t.d=E.Math.max(t.d,h),!t.c&&(t.c=new xe),Te(t.c,f)}return t}function Z_n(e,n,t,i){var r,c,o,l,f,h,b;if(t.d.i!=n.i){for(r=new Da(e),kf(r,(zn(),hr)),fe(r,(pe(),gi),t),fe(r,(Ce(),Zi),(Rr(),eo)),Jn(i.c,r),o=new Vu,bu(o,r),Ar(o,(Oe(),Kn)),l=new Vu,bu(l,r),Ar(l,Wn),b=t.d,Jr(t,o),c=new dw,Lu(c,t),fe(c,Qc,null),lc(c,l),Jr(c,b),h=new Gr(t.b,0);h.b1e6)throw P(new BP("power of ten too big"));if(e<=ri)return j4(qO(o6[1],n),n);for(i=qO(o6[1],ri),r=i,t=_u(e-ri),n=sc(e%ri);fo(t,ri)>0;)r=D3(r,i),t=uf(t,ri);for(r=D3(r,qO(o6[1],n)),r=j4(r,ri),t=_u(e-ri);fo(t,ri)>0;)r=j4(r,ri),t=uf(t,ri);return r=j4(r,n),r}function jKe(e){var n,t,i,r,c,o,l,f,h,b;for(f=new L(e.a);f.ah&&i>h)b=l,h=W(n.p[l.p])+W(n.d[l.p])+l.o.b+l.d.a;else{r=!1,t.$g()&&t.ah("bk node placement breaks on "+l+" which should have been after "+b);break}if(!r)break}return t.$g()&&t.ah(n+" is feasible: "+r),r}function tge(e,n,t,i){var r,c,o,l,f,h,b,p,y;if(c=new Da(e),kf(c,(zn(),go)),fe(c,(Ce(),Zi),(Rr(),eo)),r=0,n){for(o=new Vu,fe(o,(pe(),gi),n),fe(c,gi,n.i),Ar(o,(Oe(),Kn)),bu(o,c),y=lh(n.e),h=y,b=0,p=h.length;b0){if(r<0&&b.a&&(r=f,c=h[0],i=0),r>=0){if(l=b.b,f==r&&(l-=i++,l==0))return 0;if(!AVe(n,h,b,l,o)){f=r-1,h[0]=c;continue}}else if(r=-1,!AVe(n,h,b,0,o))return 0}else{if(r=-1,ic(b.c,0)==32){if(p=h[0],pRe(n,h),h[0]>p)continue}else if($5n(n,b.c,h[0])){h[0]+=b.c.length;continue}return 0}return q$n(o,t)?h[0]:0}function iLn(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(b=new bR(new qke(t)),l=oe(ns,fa,30,e.f.e.c.length,16,1),xfe(l,l.length),t[n.a]=0,h=new L(e.f.e);h.a=l.a?c.b>=l.b?(i.a=l.a+(c.a-l.a)/2+r,i.b=l.b+(c.b-l.b)/2-r-e.e.b):(i.a=l.a+(c.a-l.a)/2+r,i.b=c.b+(l.b-c.b)/2+r):c.b>=l.b?(i.a=c.a+(l.a-c.a)/2+r,i.b=l.b+(c.b-l.b)/2+r):(i.a=c.a+(l.a-c.a)/2+r,i.b=c.b+(l.b-c.b)/2-r-e.e.b))}function Wj(e){var n,t,i,r,c,o,l,f;if(!e.f){if(f=new WT,l=new WT,n=ZA,o=n.a.yc(e,n),o==null){for(c=new ct(tu(e));c.e!=c.i.gc();)r=u(lt(c),29),er(f,Wj(r));n.a.Ac(e)!=null,n.a.gc()==0}for(i=(!e.s&&(e.s=new we(es,e,21,17)),new ct(e.s));i.e!=i.i.gc();)t=u(lt(i),179),q(t,103)&&kt(l,u(t,19));yp(l),e.r=new QNe(e,(u(X(ge((p0(),Rn).o),6),19),l.i),l.g),er(f,e.r),yp(f),e.f=new y3((u(X(ge(Rn.o),5),19),f.i),f.g),Ms(e).b&=-3}return e.f}function Hz(){Hz=Y,C8e=z(B(Vl,1),ph,30,15,[48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70]),man=new RegExp(`[ -\r\f]+`);try{VA=z(B(HBn,1),xn,2076,0,[new Gx((xse(),VB("yyyy-MM-dd'T'HH:mm:ss'.'SSSZ",AC((PP(),PP(),PS))))),new Gx(VB("yyyy-MM-dd'T'HH:mm:ss'.'SSS",AC(PS))),new Gx(VB("yyyy-MM-dd'T'HH:mm:ss",AC(PS))),new Gx(VB("yyyy-MM-dd'T'HH:mm",AC(PS))),new Gx(VB("yyyy-MM-dd",AC(PS)))])}catch(e){if(e=or(e),!q(e,80))throw P(e)}}function rLn(e){var n,t,i,r,c,o,l;for(t=null,l=null,i=u(T(e.b,(Ce(),Tie)),348),i==(Ej(),gD)&&(t=new xe,l=new xe),o=new L(e.d);o.at);return c}function AKe(e,n){var t,i,r,c;if(r=Ds(e.d,1)!=0,i=jz(e,n),i==0&&$e(Pe(T(n.j,(pe(),Y0)))))return 0;!$e(Pe(T(n.j,(pe(),Y0))))&&!$e(Pe(T(n.j,jv)))||ue(T(n.j,(Ce(),n1)))===ue((P1(),W0))?n.c.kg(n.e,r):r=$e(Pe(T(n.j,Y0))),YO(e,n,r,!0),$e(Pe(T(n.j,jv)))&&fe(n.j,jv,(Pn(),!1)),$e(Pe(T(n.j,Y0)))&&(fe(n.j,Y0,(Pn(),!1)),fe(n.j,jv,!0)),t=jz(e,n);do{if(Hhe(e),t==0)return 0;r=!r,c=t,YO(e,n,r,!1),t=jz(e,n)}while(c>t);return c}function uLn(e,n,t){var i,r,c,o,l;if(i=u(T(e,(Ce(),Sie)),22),t.a>n.a&&(i.Gc((Yb(),_A))?e.c.a+=(t.a-n.a)/2:i.Gc(LA)&&(e.c.a+=t.a-n.a)),t.b>n.b&&(i.Gc((Yb(),$A))?e.c.b+=(t.b-n.b)/2:i.Gc(PA)&&(e.c.b+=t.b-n.b)),u(T(e,(pe(),wo)),22).Gc((Oc(),ql))&&(t.a>n.a||t.b>n.b))for(l=new L(e.a);l.an.a&&(i.Gc((Yb(),_A))?e.c.a+=(t.a-n.a)/2:i.Gc(LA)&&(e.c.a+=t.a-n.a)),t.b>n.b&&(i.Gc((Yb(),$A))?e.c.b+=(t.b-n.b)/2:i.Gc(PA)&&(e.c.b+=t.b-n.b)),u(T(e,(pe(),wo)),22).Gc((Oc(),ql))&&(t.a>n.a||t.b>n.b))for(o=new L(e.a);o.a=0&&p<=1&&y>=0&&y<=1?bi(new ke(e.a,e.b),k1(new ke(n.a,n.b),p)):null}function eS(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(c=0,o=e.t,r=0,i=0,f=0,y=0,p=0,t&&(e.n.c.length=0,Te(e.n,new _R(e.s,e.t,e.i))),l=0,b=new L(e.b);b.a0?e.i:0)>n&&f>0&&(c=0,o+=f+e.i,r=E.Math.max(r,y),i+=f+e.i,f=0,y=0,t&&(++p,Te(e.n,new _R(e.s,o,e.i))),l=0),y+=h.g+(l>0?e.i:0),f=E.Math.max(f,h.f),t&&Sde(u(_e(e.n,p),208),h),c+=h.g+(l>0?e.i:0),++l;return r=E.Math.max(r,y),i+=f,t&&(e.r=r,e.d=i,Tde(e.j)),new xf(e.s,e.t,r,i)}function Jz(e){var n,t,i;return t=ue(ve(e,(Ce(),j6)))===ue((UO(),Vte))||ue(ve(e,j6))===ue(Gte)||ue(ve(e,j6))===ue(qte)||ue(ve(e,j6))===ue(Xte)||ue(ve(e,j6))===ue(Yte)||ue(ve(e,j6))===ue(WN),i=ue(ve(e,hJ))===ue((KO(),zie))||ue(ve(e,hJ))===ue(Hie)||ue(ve(e,lD))===ue((P0(),m7))||ue(ve(e,lD))===ue((P0(),bA)),n=ue(ve(e,n1))!==ue((P1(),W0))||$e(Pe(ve(e,h7)))||ue(ve(e,iA))!==ue((P4(),JS))||W(ie(ve(e,oD)))!=0||W(ie(ve(e,kie)))!=0,t||i||n}function W3(e){var n,t,i,r,c,o,l,f;if(!e.a){if(e.o=null,f=new CSe(e),n=new yf,t=ZA,l=t.a.yc(e,t),l==null){for(o=new ct(tu(e));o.e!=o.i.gc();)c=u(lt(o),29),er(f,W3(c));t.a.Ac(e)!=null,t.a.gc()==0}for(r=(!e.s&&(e.s=new we(es,e,21,17)),new ct(e.s));r.e!=r.i.gc();)i=u(lt(r),179),q(i,335)&&kt(n,u(i,38));yp(n),e.k=new YNe(e,(u(X(ge((p0(),Rn).o),7),19),n.i),n.g),er(f,e.k),yp(f),e.a=new y3((u(X(ge(Rn.o),4),19),f.i),f.g),Ms(e).b&=-2}return e.a}function lLn(e){var n,t,i,r,c,o,l,f,h,b,p,y;if(l=e.d,p=u(T(e,(pe(),k6)),16),n=u(T(e,b6),16),!(!p&&!n)){if(c=W(ie(jp(e,(Ce(),_ie)))),o=W(ie(jp(e,o4e))),y=0,p){for(h=0,r=p.Jc();r.Ob();)i=u(r.Pb(),9),h=E.Math.max(h,i.o.b),y+=i.o.a;y+=c*(p.gc()-1),l.d+=h+o}if(t=0,n){for(h=0,r=n.Jc();r.Ob();)i=u(r.Pb(),9),h=E.Math.max(h,i.o.b),t+=i.o.a;t+=c*(n.gc()-1),l.a+=h+o}f=E.Math.max(y,t),f>e.o.a&&(b=(f-e.o.a)/2,l.b=E.Math.max(l.b,b),l.c=E.Math.max(l.c,b))}}function cge(e,n,t,i){var r,c,o,l,f,h,b;if(b=Lo(e.e.Ah(),n),r=0,c=u(e.g,122),f=null,Tc(),u(n,69).vk()){for(l=0;ll?1:-1:m1e(e.a,n.a,c),r==-1)p=-f,b=o==f?uY(n.a,l,e.a,c):sY(n.a,l,e.a,c);else if(p=o,o==f){if(r==0)return bh(),RS;b=uY(e.a,c,n.a,l)}else b=sY(e.a,c,n.a,l);return h=new Db(p,b.length,b),rj(h),h}function hLn(e,n){var t,i,r,c;if(c=aKe(n),!n.c&&(n.c=new we(Ps,n,9,9)),Wi(new bn(null,(!n.c&&(n.c=new we(Ps,n,9,9)),new wn(n.c,16))),new Vke(c)),r=u(T(c,(pe(),wo)),22),s$n(n,r),r.Gc((Oc(),ql)))for(i=new ct((!n.c&&(n.c=new we(Ps,n,9,9)),n.c));i.e!=i.i.gc();)t=u(lt(i),125),I$n(e,n,c,t);return u(ve(n,(Ce(),bg)),182).gc()!=0&&ZUe(n,c),$e(Pe(T(c,t4e)))&&r.Ec(QH),di(c,fD)&&HAe(new X1e(W(ie(T(c,fD)))),c),ue(ve(n,tm))===ue((_1(),Gd))?nBn(e,n,c):B$n(e,n,c),c}function ho(e,n){var t,i,r,c,o,l,f;if(e==null)return null;if(c=e.length,c==0)return"";for(f=oe(Vl,ph,30,c,15,1),Yr(0,c,e.length),Yr(0,c,f.length),VDe(e,0,c,f,0),t=null,l=n,r=0,o=0;r0?rf(t.a,0,c-1):""):(Yr(0,c-1,e.length),e.substr(0,c-1)):t?t.a:e}function dLn(e,n,t){var i,r,c;if(di(n,(Ce(),vu))&&(ue(T(n,vu))===ue((qs(),G1))||ue(T(n,vu))===ue(hg))||di(t,vu)&&(ue(T(t,vu))===ue((qs(),G1))||ue(T(t,vu))===ue(hg)))return 0;if(i=Ir(n),r=nIn(e,n,t),r!=0)return r;if(di(n,(pe(),Ci))&&di(t,Ci)){if(c=uo(Nw(n,t,i,u(T(i,Q0),15).a),Nw(t,n,i,u(T(i,Q0),15).a)),ue(T(i,rA))===ue((M0(),nD))&&ue(T(n,cA))!==ue(T(t,cA))&&(c=0),c<0)return QO(e,n,t),c;if(c>0)return QO(e,t,n),c}return xCn(e,n,t)}function MKe(e,n){var t,i,r,c,o,l,f,h,b,p,y;for(i=new qn(Vn(L0(n).a.Jc(),new ne));at(i);)t=u(tt(i),85),q(X((!t.b&&(t.b=new Cn(pt,t,4,7)),t.b),0),193)||(f=iu(u(X((!t.c&&(t.c=new Cn(pt,t,5,8)),t.c),0),84)),Jj(t)||(o=n.i+n.g/2,l=n.j+n.f/2,b=f.i+f.g/2,p=f.j+f.f/2,y=new Kr,y.a=b-o,y.b=p-l,c=new ke(y.a,y.b),i8(c,n.g,n.f),y.a-=c.a,y.b-=c.b,o=b-y.a,l=p-y.b,h=new ke(y.a,y.b),i8(h,f.g,f.f),y.a-=h.a,y.b-=h.b,b=o+y.a,p=l+y.b,r=_z(t),R3(r,o),B3(r,l),P3(r,b),$3(r,p),MKe(e,f)))}function Lp(e,n){var t,i,r,c,o;if(o=u(n,137),Y3(e),Y3(o),o.b!=null){if(e.c=!0,e.b==null){e.b=oe(It,Zt,30,o.b.length,15,1),Yu(o.b,0,e.b,0,o.b.length);return}for(c=oe(It,Zt,30,e.b.length+o.b.length,15,1),t=0,i=0,r=0;t=e.b.length?(c[r++]=o.b[i++],c[r++]=o.b[i++]):i>=o.b.length?(c[r++]=e.b[t++],c[r++]=e.b[t++]):o.b[i]0?e.i:0)),++n;for(B1e(e.n,f),e.d=t,e.r=i,e.g=0,e.f=0,e.e=0,e.o=Ki,e.p=Ki,c=new L(e.b);c.a0&&(r=(!e.n&&(e.n=new we(ku,e,1,7)),u(X(e.n,0),157)).a,!r||Gt(Gt((n.a+=' "',n),r),'"'))),t=(!e.b&&(e.b=new Cn(pt,e,4,7)),!(e.b.i<=1&&(!e.c&&(e.c=new Cn(pt,e,5,8)),e.c.i<=1))),t?n.a+=" [":n.a+=" ",Gt(n,Use(new MX,new ct(e.b))),t&&(n.a+="]"),n.a+=VW,t&&(n.a+="["),Gt(n,Use(new MX,new ct(e.c))),t&&(n.a+="]"),n.a)}function gLn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne,nn,en,Tn;for(he=e.c,se=n.c,t=gu(he.a,e,0),i=gu(se.a,n,0),K=u(Sw(e,(Cc(),vs)).Jc().Pb(),12),en=u(Sw(e,No).Jc().Pb(),12),ee=u(Sw(n,vs).Jc().Pb(),12),Tn=u(Sw(n,No).Jc().Pb(),12),R=lh(K.e),Ne=lh(en.g),G=lh(ee.e),nn=lh(Tn.g),D0(e,i,se),o=G,b=0,M=o.length;b0&&f[i]&&(M=S3(e.b,f[i],r)),O=E.Math.max(O,r.c.c.b+M);for(c=new L(b.e);c.ab?new Pb((ca(),hm),t,n,h-b):h>0&&b>0&&(new Pb((ca(),hm),n,t,0),new Pb(hm,t,n,0))),o)}function vLn(e,n,t){var i,r,c;for(e.a=new xe,c=Et(n.b,0);c.b!=c.d.c;){for(r=u(yt(c),40);u(T(r,(Au(),Th)),15).a>e.a.c.length-1;)Te(e.a,new yc(ov,D2e));i=u(T(r,Th),15).a,t==(vr(),Zc)||t==ru?(r.e.aW(ie(u(_e(e.a,i),49).b))&&Bx(u(_e(e.a,i),49),r.e.a+r.f.a)):(r.e.bW(ie(u(_e(e.a,i),49).b))&&Bx(u(_e(e.a,i),49),r.e.b+r.f.b))}}function CKe(e,n,t,i){var r,c,o,l,f,h,b;if(c=HB(i),l=$e(Pe(T(i,(Ce(),Y5e)))),(l||$e(Pe(T(e,aJ))))&&!k3(u(T(e,Zi),102)))r=_4(c),f=qbe(e,t,t==(Cc(),No)?r:TO(r));else switch(f=new Vu,bu(f,e),n?(b=f.n,b.a=n.a-e.n.a,b.b=n.b-e.n.b,OGe(b,0,0,e.o.a,e.o.b),Ar(f,YXe(f,c))):(r=_4(c),Ar(f,t==(Cc(),No)?r:TO(r))),o=u(T(i,(pe(),wo)),22),h=f.j,c.g){case 2:case 1:(h==(Oe(),Xn)||h==dt)&&o.Ec((Oc(),yv));break;case 4:case 3:(h==(Oe(),Wn)||h==Kn)&&o.Ec((Oc(),yv))}return f}function OKe(e,n){var t,i,r,c,o,l;for(o=new mp(new tn(e.f.b).a);o.b;){if(c=z3(o),r=u(c.jd(),591),n==1){if(r.yf()!=(vr(),Ul)&&r.yf()!=Ua)continue}else if(r.yf()!=(vr(),Zc)&&r.yf()!=ru)continue;switch(i=u(u(c.kd(),49).b,82),l=u(u(c.kd(),49).a,194),t=l.c,r.yf().g){case 2:i.g.c=e.e.a,i.g.b=E.Math.max(1,i.g.b+t);break;case 1:i.g.c=i.g.c+t,i.g.b=E.Math.max(1,i.g.b-t);break;case 4:i.g.d=e.e.b,i.g.a=E.Math.max(1,i.g.a+t);break;case 3:i.g.d=i.g.d+t,i.g.a=E.Math.max(1,i.g.a-t)}}}function yLn(e,n){var t,i,r,c,o,l,f,h,b,p;for(n.Tg("Simple node placement",1),p=u(T(e,(pe(),Sv)),316),l=0,c=new L(e.b);c.a1)throw P(new Gn(zN));f||(c=Hh(n,i.Jc().Pb()),o.Ec(c))}return i1e(e,S0e(e,n,t),o)}function qz(e,n,t){var i,r,c,o,l,f,h,b;if($1(e.e,n))f=(Tc(),u(n,69).vk()?new tR(n,e):new gC(n,e)),Az(f.c,f.b),BE(f,u(t,18));else{for(b=Lo(e.e.Ah(),n),i=u(e.g,122),o=0;o"}f!=null&&(n.a+=""+f)}else e.e?(l=e.e.zb,l!=null&&(n.a+=""+l)):(n.a+="?",e.b?(n.a+=" super ",qZ(e.b,n)):e.f&&(n.a+=" extends ",qZ(e.f,n)))}function TLn(e){e.b=null,e.a=null,e.o=null,e.q=null,e.v=null,e.w=null,e.B=null,e.p=null,e.Q=null,e.R=null,e.S=null,e.T=null,e.U=null,e.V=null,e.W=null,e.bb=null,e.eb=null,e.ab=null,e.H=null,e.db=null,e.c=null,e.d=null,e.f=null,e.n=null,e.r=null,e.s=null,e.u=null,e.G=null,e.J=null,e.e=null,e.j=null,e.i=null,e.g=null,e.k=null,e.t=null,e.F=null,e.I=null,e.L=null,e.M=null,e.O=null,e.P=null,e.$=null,e.N=null,e.Z=null,e.cb=null,e.K=null,e.D=null,e.A=null,e.C=null,e._=null,e.fb=null,e.X=null,e.Y=null,e.gb=!1,e.hb=!1}function xLn(e){var n,t,i,r;if(i=rW((!e.c&&(e.c=JC(_u(e.f))),e.c),0),e.e==0||e.a==0&&e.f!=-1&&e.e<0)return i;if(n=Jhe(e)<0?1:0,t=e.e,r=(i.length+1+E.Math.abs(sc(e.e)),new Y5),n==1&&(r.a+="-"),e.e>0)if(t-=i.length-n,t>=0){for(r.a+="0.";t>lg.length;t-=lg.length)pDe(r,lg);JOe(r,lg,sc(t)),Gt(r,(Yn(n,i.length+1),i.substr(n)))}else t=n-t,Gt(r,rf(i,n,sc(t))),r.a+=".",Gt(r,Pfe(i,sc(t)));else{for(Gt(r,(Yn(n,i.length+1),i.substr(n)));t<-lg.length;t+=lg.length)pDe(r,lg);JOe(r,lg,sc(-t))}return r.a}function UZ(e){var n,t,i,r,c,o,l,f,h;return!(e.k!=(zn(),Qi)||e.j.c.length<=1||(c=u(T(e,(Ce(),Zi)),102),c==(Rr(),eo))||(r=(Ap(),(e.q?e.q:(vn(),vn(),Zh))._b(n2)?i=u(T(e,n2),203):i=u(T(Ir(e),lA),203),i),r==jJ)||!(r==Ov||r==Cv)&&(o=W(ie(jp(e,fA))),n=u(T(e,hD),140),!n&&(n=new xle(o,o,o,o)),h=pu(e,(Oe(),Kn)),f=n.d+n.a+(h.gc()-1)*o,f>e.o.b||(t=pu(e,Wn),l=n.d+n.a+(t.gc()-1)*o,l>e.o.b)))}function CLn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I;n.Tg("Orthogonal edge routing",1),h=W(ie(T(e,(Ce(),fm)))),t=W(ie(T(e,sm))),i=W(ie(T(e,Z0))),y=new pV(0,t),I=0,o=new Gr(e.b,0),l=null,b=null,f=null,p=null;do b=o.b0?(S=(M-1)*t,l&&(S+=i),b&&(S+=i),S0;for(l=u(T(e.c.i,rm),15).a,c=u(bs(ci(n.Mc(),new wEe(l)),Ts(new Bi,new Ei,new eu,z(B(Yo,1),ye,130,0,[($l(),Vo)]))),16),o=new ji,b=new fr,qt(o,e.c.i),ar(b,e.c.i);o.b!=0;){if(t=u(o.b==0?null:(ft(o.b!=0),_l(o,o.a.a)),9),c.Gc(t))return!0;for(r=new qn(Vn(Ni(t).a.Jc(),new ne));at(r);)i=u(tt(r),17),f=i.d.i,b.a._b(f)||(b.a.yc(f,b),Xi(o,f,o.c.b,o.c))}return!1}function LKe(e,n,t){var i,r,c,o,l,f,h,b,p;for(p=new xe,b=new vae(0,t),c=0,pB(b,new QY(0,0,b,t)),r=0,h=new ct(e);h.e!=h.i.gc();)f=u(lt(h),26),i=u(_e(b.a,b.a.c.length-1),173),l=r+f.g+(u(_e(b.a,0),173).b.c.length==0?0:t),(l>n||$e(Pe(ve(f,(La(),SD)))))&&(r=0,c+=b.b+t,Jn(p.c,b),b=new vae(c,t),i=new QY(0,b.f,b,t),pB(b,i),r=0),i.b.c.length==0||!$e(Pe(ve(zi(f),(La(),Fre))))&&(f.f>=i.o&&f.f<=i.f||i.a*.5<=f.f&&i.a*1.5>=f.f)?q1e(i,f):(o=new QY(i.s+i.r+t,b.f,b,t),pB(b,o),q1e(o,f)),r=f.i+f.g;return Jn(p.c,b),p}function nS(e){var n,t,i,r;if(!(e.b==null||e.b.length<=2)&&!e.a){for(n=0,r=0;r=e.b[r+1])r+=2;else if(t0)for(i=new ds(u(wi(e.a,c),22)),vn(),xr(i,new Uue(n)),r=new Gr(c.b,0);r.b0&&i>=-6?i>=0?mC(c,t-sc(e.e),"."):(zY(c,n-1,n-1,"0."),mC(c,n+1,ah(lg,0,-sc(i)-1))):(t-n>=1&&(mC(c,n,"."),++t),mC(c,t,"E"),i>0&&mC(c,++t,"+"),mC(c,++t,""+KE(_u(i)))),e.g=c.a,e.g))}function BLn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne;i=W(ie(T(n,(Ce(),W5e)))),he=u(T(n,aA),15).a,y=4,r=3,se=20/he,S=!1,f=0,o=ri;do{for(c=f!=1,p=f!=0,Ne=0,I=e.a,G=0,ee=I.length;Ghe)?(f=2,o=ri):f==0?(f=1,o=Ne):(f=0,o=Ne)):(S=Ne>=o||o-Ne=kc?$c(t,z1e(i)):m9(t,i&yr),o=new PV(10,null,0),yvn(e.a,o,l-1)):(t=(o.Km().length+c,new hE),$c(t,o.Km())),n.e==0?(i=n.Im(),i>=kc?$c(t,z1e(i)):m9(t,i&yr)):$c(t,n.Km()),u(o,517).b=t.a}}function zLn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I;if(!t.dc()){for(l=0,y=0,i=t.Jc(),M=u(i.Pb(),15).a;l0?1:Tb(isNaN(i),isNaN(0)))>=0^(Df(kh),(E.Math.abs(l)<=kh||l==0||isNaN(l)&&isNaN(0)?0:l<0?-1:l>0?1:Tb(isNaN(l),isNaN(0)))>=0)?E.Math.max(l,i):(Df(kh),(E.Math.abs(i)<=kh||i==0||isNaN(i)&&isNaN(0)?0:i<0?-1:i>0?1:Tb(isNaN(i),isNaN(0)))>0?E.Math.sqrt(l*l+i*i):-E.Math.sqrt(l*l+i*i))}function GLn(e){var n,t,i,r;r=e.o,Z2(),e.A.dc()||ai(e.A,Hme)?n=r.b:(e.D?n=E.Math.max(r.b,zj(e.f)):n=zj(e.f),e.A.Gc((Xs(),HD))&&!e.B.Gc((Is(),XA))&&(n=E.Math.max(n,zj(u(Rc(e.p,(Oe(),Wn)),253))),n=E.Math.max(n,zj(u(Rc(e.p,Kn),253)))),t=UBe(e),t&&(n=E.Math.max(n,t.b)),e.A.Gc(JD)&&(e.q==(Rr(),c1)||e.q==eo)&&(n=E.Math.max(n,eR(u(Rc(e.b,(Oe(),Wn)),127))),n=E.Math.max(n,eR(u(Rc(e.b,Kn),127))))),$e(Pe(e.e.Rf().mf((Ht(),wm))))?r.b=E.Math.max(r.b,n):r.b=n,i=e.f.i,i.d=0,i.a=n,BZ(e.f)}function qLn(e,n,t,i,r,c,o,l){var f,h,b,p;switch(f=Of(z(B(PBn,1),xn,238,0,[n,t,i,r])),p=null,e.b.g){case 1:p=Of(z(B(mye,1),xn,523,0,[new Ak,new NT,new yy]));break;case 0:p=Of(z(B(mye,1),xn,523,0,[new yy,new NT,new Ak]));break;case 2:p=Of(z(B(mye,1),xn,523,0,[new NT,new Ak,new yy]))}for(b=new L(p);b.a1&&(f=h.Gg(f,e.a,l));return f.c.length==1?u(_e(f,f.c.length-1),238):f.c.length==2?NLn((pn(0,f.c.length),u(f.c[0],238)),(pn(1,f.c.length),u(f.c[1],238)),o,c):null}function ULn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M;r=new Jy(e),c=new Fqe,i=(XC(c.n),XC(c.p),Fu(c.c),XC(c.f),XC(c.o),Fu(c.q),Fu(c.d),Fu(c.g),Fu(c.k),Fu(c.e),Fu(c.i),Fu(c.j),Fu(c.r),Fu(c.b),y=aqe(c,r,null),hUe(c,r),y),n&&(f=new Jy(n),o=fLn(f),m0e(i,z(B(Qye,1),xn,524,0,[o]))),p=!1,b=!1,t&&(f=new Jy(t),HF in f.a&&(p=A1(f,HF).oe().a),tWe in f.a&&(b=A1(f,tWe).oe().a)),h=oMe(tBe(new U5,p),b),WTn(new PT,i,h),HF in r.a&&Nf(r,HF,null),(p||b)&&(l=new X5,cKe(h,l,p,b),Nf(r,HF,l)),S=new aSe(c),Nze(new kK(i),S),M=new hSe(c),Nze(new kK(i),M)}function XLn(e,n,t){var i,r,c,o,l,f,h;for(t.Tg("Find roots",1),e.a.c.length=0,r=Et(n.b,0);r.b!=r.d.c;)i=u(yt(r),40),i.b.b==0&&(fe(i,(xi(),tb),(Pn(),!0)),Te(e.a,i));switch(e.a.c.length){case 0:c=new YY(0,n,"DUMMY_ROOT"),fe(c,(xi(),tb),(Pn(),!0)),fe(c,lre,!0),qt(n.b,c);break;case 1:break;default:for(o=new YY(0,n,OF),f=new L(e.a);f.a=E.Math.abs(i.b)?(i.b=0,c.d+c.a>o.d&&c.do.c&&c.c0){if(n=new Ese(e.i,e.g),t=e.i,c=t<100?null:new f0(t),e.Rj())for(i=0;i0){for(l=e.g,h=e.i,lj(e),c=h<100?null:new f0(h),i=0;i>13|(e.m&15)<<9,r=e.m>>4&8191,c=e.m>>17|(e.h&255)<<5,o=(e.h&1048320)>>8,l=n.l&8191,f=n.l>>13|(n.m&15)<<9,h=n.m>>4&8191,b=n.m>>17|(n.h&255)<<5,p=(n.h&1048320)>>8,nn=t*l,en=i*l,Tn=r*l,On=c*l,ot=o*l,f!=0&&(en+=t*f,Tn+=i*f,On+=r*f,ot+=c*f),h!=0&&(Tn+=t*h,On+=i*h,ot+=r*h),b!=0&&(On+=t*b,ot+=i*b),p!=0&&(ot+=t*p),S=nn&_s,M=(en&511)<<13,y=S+M,I=nn>>22,R=en>>9,G=(Tn&262143)<<4,K=(On&31)<<17,O=I+R+G+K,he=Tn>>18,se=On>>5,Ne=(ot&4095)<<8,ee=he+se+Ne,O+=y>>22,y&=_s,ee+=O>>22,O&=_s,ee&=B1,Io(y,O,ee)}function BKe(e){var n,t,i,r,c,o,l;if(l=u(_e(e.j,0),12),l.g.c.length!=0&&l.e.c.length!=0)throw P(new qc("Interactive layout does not support NORTH/SOUTH ports with incoming _and_ outgoing edges."));if(l.g.c.length!=0){for(c=Ki,t=new L(l.g);t.a0&&$Ge(e,l,p);for(r=new L(p);r.a4)if(e.dk(n)){if(e.$k()){if(r=u(n,52),i=r.Bh(),f=i==e.e&&(e.kl()?r.vh(r.Ch(),e.gl())==e.hl():-1-r.Ch()==e.Jj()),e.ll()&&!f&&!i&&r.Gh()){for(c=0;ce.d[o.p]&&(t+=_ae(e.b,c)*u(f.b,15).a,k0(e.a,me(c)));for(;!aE(e.a);)bhe(e.b,u(g4(e.a),15).a)}return t}function ZLn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I;for(n.Tg(DQe,1),S=new xe,b=E.Math.max(e.a.c.length,u(T(e,(pe(),Q0)),15).a),t=b*u(T(e,iD),15).a,l=ue(T(e,(Ce(),E6)))===ue((M0(),Wp)),O=new L(e.a);O.a0&&(h=e.n.a/c);break;case 2:case 4:r=e.i.o.b,r>0&&(h=e.n.b/r)}fe(e,(pe(),Zw),h)}if(f=e.o,o=e.a,i)o.a=i.a,o.b=i.b,e.d=!0;else if(n!=Ka&&n!=ub&&l!=yu)switch(l.g){case 1:o.a=f.a/2;break;case 2:o.a=f.a,o.b=f.b/2;break;case 3:o.a=f.a/2,o.b=f.b;break;case 4:o.b=f.b/2}else o.a=f.a/2,o.b=f.b/2}function tS(e){var n,t,i,r,c,o,l,f,h,b;if(e.Nj())if(b=e.Cj(),f=e.Oj(),b>0)if(n=new Xhe(e.nj()),t=b,c=t<100?null:new f0(t),jC(e,t,n.g),r=t==1?e.Gj(4,X(n,0),null,0,f):e.Gj(6,n,null,-1,f),e.Kj()){for(i=new ct(n);i.e!=i.i.gc();)c=e.Mj(lt(i),c);c?(c.lj(r),c.mj()):e.Hj(r)}else c?(c.lj(r),c.mj()):e.Hj(r);else jC(e,e.Cj(),e.Dj()),e.Hj(e.Gj(6,(vn(),Ec),null,-1,f));else if(e.Kj())if(b=e.Cj(),b>0){for(l=e.Dj(),h=b,jC(e,b,l),c=h<100?null:new f0(h),i=0;i1&&cs(o)*Hs(o)/2>l[0]){for(c=0;cl[c];)++c;M=new y0(O,0,c+1),p=new aB(M),b=cs(o)/Hs(o),f=nW(p,n,new q5,t,i,r,b),bi(ta(p.e),f),h4(c8(y,p),A8),S=new y0(O,c+1,O.c.length),Nde(y,S),O.c.length=0,h=0,kDe(l,l.length,0)}else I=y.b.c.length==0?null:_e(y.b,0),I!=null&&NY(y,0),h>0&&(l[h]=l[h-1]),l[h]+=cs(o)*Hs(o),++h,Jn(O.c,o);return O}function oPn(e,n){var t,i,r,c;t=n.b,c=new ds(t.j),r=0,i=t.j,i.c.length=0,sw(u(Hb(e.b,(Oe(),Xn),(yw(),Vw)),16),t),r=DO(c,r,new oy,i),sw(u(Hb(e.b,Xn,V0),16),t),r=DO(c,r,new id,i),sw(u(Hb(e.b,Xn,Kw),16),t),sw(u(Hb(e.b,Wn,Vw),16),t),sw(u(Hb(e.b,Wn,V0),16),t),r=DO(c,r,new rd,i),sw(u(Hb(e.b,Wn,Kw),16),t),sw(u(Hb(e.b,dt,Vw),16),t),r=DO(c,r,new p2,i),sw(u(Hb(e.b,dt,V0),16),t),r=DO(c,r,new wb,i),sw(u(Hb(e.b,dt,Kw),16),t),sw(u(Hb(e.b,Kn,Vw),16),t),r=DO(c,r,new td,i),sw(u(Hb(e.b,Kn,V0),16),t),sw(u(Hb(e.b,Kn,Kw),16),t)}function sPn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O;for(n.Tg("Layer size calculation",1),b=Ki,h=Nr,r=!1,l=new L(e.b);l.a.5?R-=o*2*(M-.5):M<.5&&(R+=c*2*(.5-M)),r=l.d.b,RI.a-O-b&&(R=I.a-O-b),l.n.a=n+R}}function fPn(e){var n,t,i,r,c;if(i=u(T(e,(Ce(),vu)),165),i==(qs(),G1)){for(t=new qn(Vn(rr(e).a.Jc(),new ne));at(t);)if(n=u(tt(t),17),!PPe(n))throw P(new fd(ZW+IO(e)+"' has its layer constraint set to FIRST_SEPARATE, but has at least one incoming edge. FIRST_SEPARATE nodes must not have incoming edges."))}else if(i==hg){for(c=new qn(Vn(Ni(e).a.Jc(),new ne));at(c);)if(r=u(tt(c),17),!PPe(r))throw P(new fd(ZW+IO(e)+"' has its layer constraint set to LAST_SEPARATE, but has at least one outgoing edge. LAST_SEPARATE nodes must not have outgoing edges."))}}function tN(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M;if(e.e&&e.c.c>19!=0&&(n=z9(n),f=!f),o=VOn(n),c=!1,r=!1,i=!1,e.h==dN&&e.m==0&&e.l==0)if(r=!0,c=!0,o==-1)e=uCe((O9(),dme)),i=!0,f=!f;else return l=ebe(e,o),f&&KY(l),t&&(q0=Io(0,0,0)),l;else e.h>>19!=0&&(c=!0,e=z9(e),i=!0,f=!f);return o!=-1?ckn(e,o,f,c,t):Bde(e,n)<0?(t&&(c?q0=z9(e):q0=Io(e.l,e.m,e.h)),Io(0,0,0)):JIn(i?e:Io(e.l,e.m,e.h),n,f,c,r,t)}function YZ(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M;if(o=e.e,f=n.e,o==0)return n;if(f==0)return e;if(c=e.d,l=n.d,c+l==2)return t=$r(e.a[0],Nc),i=$r(n.a[0],Nc),o==f?(b=pc(t,i),M=_t(b),S=_t(Nb(b,32)),S==0?new T1(o,M):new Db(o,2,z(B(It,1),Zt,30,15,[M,S]))):(bh(),T$(o<0?uf(i,t):uf(t,i),0)?N0(o<0?uf(i,t):uf(t,i)):ZE(N0(Ed(o<0?uf(i,t):uf(t,i)))));if(o==f)y=o,p=c>=l?sY(e.a,c,n.a,l):sY(n.a,l,e.a,c);else{if(r=c!=l?c>l?1:-1:m1e(e.a,n.a,c),r==0)return bh(),RS;r==1?(y=o,p=uY(e.a,c,n.a,l)):(y=f,p=uY(n.a,l,e.a,c))}return h=new Db(y,p.length,p),rj(h),h}function hPn(e,n){var t,i,r,c,o,l,f;if(!(e.g>n.f||n.g>e.f)){for(t=0,i=0,o=e.w.a.ec().Jc();o.Ob();)r=u(o.Pb(),12),sQ(wu(z(B(_r,1),je,8,0,[r.i.n,r.n,r.a])).b,n.g,n.f)&&++t;for(l=e.r.a.ec().Jc();l.Ob();)r=u(l.Pb(),12),sQ(wu(z(B(_r,1),je,8,0,[r.i.n,r.n,r.a])).b,n.g,n.f)&&--t;for(f=n.w.a.ec().Jc();f.Ob();)r=u(f.Pb(),12),sQ(wu(z(B(_r,1),je,8,0,[r.i.n,r.n,r.a])).b,e.g,e.f)&&++i;for(c=n.r.a.ec().Jc();c.Ob();)r=u(c.Pb(),12),sQ(wu(z(B(_r,1),je,8,0,[r.i.n,r.n,r.a])).b,e.g,e.f)&&--i;t=0)return t;switch(aw(Kc(e,t))){case 2:{if(hn("",Md(e,t.ok()).ve())){if(f=$C(Kc(e,t)),l=k9(Kc(e,t)),b=ube(e,n,f,l),b)return b;for(r=Pbe(e,n),o=0,p=r.gc();o1)throw P(new Gn(zN));for(b=Lo(e.e.Ah(),n),i=u(e.g,122),o=0;o1,h=new xa(y.b);du(h.a)||du(h.b);)f=u(du(h.a)?_(h.a):_(h.b),17),p=f.c==y?f.d:f.c,E.Math.abs(wu(z(B(_r,1),je,8,0,[p.i.n,p.n,p.a])).b-o.b)>1&&QNn(e,f,o,c,y)}}function pPn(e){var n,t,i,r,c,o;if(r=new Gr(e.e,0),i=new Gr(e.a,0),e.d)for(t=0;tHee;){for(c=n,o=0;E.Math.abs(n-c)0),r.a.Xb(r.c=--r.b),C_n(e,e.b-o,c,i,r),ft(r.b0),i.a.Xb(i.c=--i.b)}if(!e.d)for(t=0;t0?(e.f[b.p]=S/(b.e.c.length+b.g.c.length),e.c=E.Math.min(e.c,e.f[b.p]),e.b=E.Math.max(e.b,e.f[b.p])):l&&(e.f[b.p]=S)}}function vPn(e){e.b=null,e.bb=null,e.fb=null,e.qb=null,e.a=null,e.c=null,e.d=null,e.e=null,e.f=null,e.n=null,e.M=null,e.L=null,e.Q=null,e.R=null,e.K=null,e.db=null,e.eb=null,e.g=null,e.i=null,e.j=null,e.k=null,e.gb=null,e.o=null,e.p=null,e.q=null,e.r=null,e.$=null,e.ib=null,e.S=null,e.T=null,e.t=null,e.s=null,e.u=null,e.v=null,e.w=null,e.B=null,e.A=null,e.C=null,e.D=null,e.F=null,e.G=null,e.H=null,e.I=null,e.J=null,e.P=null,e.Z=null,e.U=null,e.V=null,e.W=null,e.X=null,e.Y=null,e._=null,e.ab=null,e.cb=null,e.hb=null,e.nb=null,e.lb=null,e.mb=null,e.ob=null,e.pb=null,e.jb=null,e.kb=null,e.N=!1,e.O=!1}function yPn(e,n,t){var i,r,c,o;for(t.Tg("Graph transformation ("+e.a+")",1),o=$b(n.a),c=new L(n.b);c.a=l.b.c)&&(l.b=n),(!l.c||n.c<=l.c.c)&&(l.d=l.c,l.c=n),(!l.e||n.d>=l.e.d)&&(l.e=n),(!l.f||n.d<=l.f.d)&&(l.f=n);return i=new iz((B9(),Xw)),qC(e,Jtn,new Eu(z(B(XN,1),xn,377,0,[i]))),o=new iz(Kp),qC(e,Htn,new Eu(z(B(XN,1),xn,377,0,[o]))),r=new iz(Xp),qC(e,Ftn,new Eu(z(B(XN,1),xn,377,0,[r]))),c=new iz(bv),qC(e,ztn,new Eu(z(B(XN,1),xn,377,0,[c]))),EZ(i.c,Xw),EZ(r.c,Xp),EZ(c.c,bv),EZ(o.c,Kp),l.a.c.length=0,jr(l.a,i.c),jr(l.a,Us(r.c)),jr(l.a,c.c),jr(l.a,Us(o.c)),l}function jPn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M;for(n.Tg(eZe,1),S=W(ie(ve(e,(qh(),dm)))),o=W(ie(ve(e,(La(),OA)))),l=u(ve(e,CA),104),Fhe((!e.a&&(e.a=new we($t,e,10,11)),e.a)),b=LKe((!e.a&&(e.a=new we($t,e,10,11)),e.a),S,o),!e.a&&(e.a=new we($t,e,10,11)),h=new L(b);h.a0&&(e.a=f+(S-1)*c,n.c.b+=e.a,n.f.b+=e.a)),M.a.gc()!=0&&(y=new pV(1,c),S=dge(y,n,M,O,n.f.b+f-n.c.b),S>0&&(n.f.b+=f+(S-1)*c))}function HKe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee;for(b=W(ie(T(e,(Ce(),wg)))),i=W(ie(T(e,l4e))),y=new Sy,fe(y,wg,b+i),h=n,R=h.d,O=h.c.i,G=h.d.i,I=Nse(O.c),K=Nse(G.c),r=new xe,p=I;p<=K;p++)l=new Da(e),kf(l,(zn(),hr)),fe(l,(pe(),gi),h),fe(l,Zi,(Rr(),eo)),fe(l,mJ,y),S=u(_e(e.b,p),25),p==I?D0(l,S.a.c.length-t,S):Cr(l,S),ee=W(ie(T(h,Rd))),ee<0&&(ee=0,fe(h,Rd,ee)),l.o.b=ee,M=E.Math.floor(ee/2),o=new Vu,Ar(o,(Oe(),Kn)),bu(o,l),o.n.b=M,f=new Vu,Ar(f,Wn),bu(f,l),f.n.b=M,Jr(h,o),c=new dw,Lu(c,h),fe(c,Qc,null),lc(c,f),Jr(c,R),NAn(l,h,c),Jn(r.c,c),h=c;return r}function APn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K;if(O=n.b.c.length,!(O<3)){for(S=oe(It,Zt,30,O,15,1),p=0,b=new L(n.b);b.ao)&&ar(e.b,u(I.b,17));++l}c=o}}}function QZ(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K;for(f=u(Od(e,(Oe(),Kn)).Jc().Pb(),12).e,S=u(Od(e,Wn).Jc().Pb(),12).g,l=f.c.length,K=Ta(u(_e(e.j,0),12));l-- >0;){for(O=(pn(0,f.c.length),u(f.c[0],17)),r=(pn(0,S.c.length),u(S.c[0],17)),G=r.d.e,c=gu(G,r,0),P6n(O,r.d,c),lc(r,null),Jr(r,null),M=O.a,n&&qt(M,new gc(K)),i=Et(r.a,0);i.b!=i.d.c;)t=u(yt(i),8),qt(M,new gc(t));for(R=O.b,y=new L(r.b);y.a-2;default:return!1}switch(n=e.Pj(),e.p){case 0:return n!=null&&$e(Pe(n))!=_E(e.k,0);case 1:return n!=null&&u(n,221).a!=_t(e.k)<<24>>24;case 2:return n!=null&&u(n,180).a!=(_t(e.k)&yr);case 6:return n!=null&&_E(u(n,190).a,e.k);case 5:return n!=null&&u(n,15).a!=_t(e.k);case 7:return n!=null&&u(n,191).a!=_t(e.k)<<16>>16;case 3:return n!=null&&W(ie(n))!=e.j;case 4:return n!=null&&u(n,164).a!=e.j;default:return n==null?e.n!=null:!ai(n,e.n)}}function iN(e,n,t){var i,r,c,o;return e.ml()&&e.ll()&&(o=fV(e,u(t,57)),ue(o)!==ue(t))?(e.vj(n),e.Bj(n,O$e(e,n,o)),e.$k()&&(c=(r=u(t,52),e.kl()?e.il()?r.Qh(e.b,xc(u(Sn(Jo(e.b),e.Jj()),19)).n,u(Sn(Jo(e.b),e.Jj()).Fk(),29).ik(),null):r.Qh(e.b,Fi(r.Ah(),xc(u(Sn(Jo(e.b),e.Jj()),19))),null,null):r.Qh(e.b,-1-e.Jj(),null,null)),!u(o,52).Mh()&&(c=(i=u(o,52),e.kl()?e.il()?i.Oh(e.b,xc(u(Sn(Jo(e.b),e.Jj()),19)).n,u(Sn(Jo(e.b),e.Jj()).Fk(),29).ik(),c):i.Oh(e.b,Fi(i.Ah(),xc(u(Sn(Jo(e.b),e.Jj()),19))),null,c):i.Oh(e.b,-1-e.Jj(),null,c))),c&&c.mj()),Bs(e.b)&&e.Hj(e.Gj(9,t,o,n,!1)),o):t}function JKe(e){var n,t,i,r,c,o,l,f,h,b;for(i=new xe,o=new L(e.e.a);o.a0&&(o=E.Math.max(o,PBe(e.C.b+i.d.b,r))),b=i,p=r,y=c;e.C&&e.C.c>0&&(S=y+e.C.c,h&&(S+=b.d.c),o=E.Math.max(o,(ja(),Df($a),E.Math.abs(p-1)<=$a||p==1||isNaN(p)&&isNaN(1)?0:S/(1-p)))),t.n.b=0,t.a.a=o}function qKe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S;if(t=u(Rc(e.b,n),127),f=u(u(wi(e.r,n),22),83),f.dc()){t.n.d=0,t.n.a=0;return}for(h=e.u.Gc((ws(),K1)),o=0,e.A.Gc((Xs(),Eg))&&kXe(e,n),l=f.Jc(),b=null,y=0,p=0;l.Ob();)i=u(l.Pb(),115),c=W(ie(i.b.mf((B$(),bH)))),r=i.b.Kf().b,b?(S=p+b.d.a+e.w+i.d.d,o=E.Math.max(o,(ja(),Df($a),E.Math.abs(y-c)<=$a||y==c||isNaN(y)&&isNaN(c)?0:S/(c-y)))):e.C&&e.C.d>0&&(o=E.Math.max(o,PBe(e.C.d+i.d.d,c))),b=i,y=c,p=r;e.C&&e.C.a>0&&(S=p+e.C.a,h&&(S+=b.d.a),o=E.Math.max(o,(ja(),Df($a),E.Math.abs(y-1)<=$a||y==1||isNaN(y)&&isNaN(1)?0:S/(1-y)))),t.n.d=0,t.a.b=o}function UKe(e,n,t){var i,r,c,o,l,f;for(this.g=e,l=n.d.length,f=t.d.length,this.d=oe(e1,Id,9,l+f,0,1),o=0;o0?AY(this,this.f/this.a):Sa(n.g,n.d[0]).a!=null&&Sa(t.g,t.d[0]).a!=null?AY(this,(W(Sa(n.g,n.d[0]).a)+W(Sa(t.g,t.d[0]).a))/2):Sa(n.g,n.d[0]).a!=null?AY(this,Sa(n.g,n.d[0]).a):Sa(t.g,t.d[0]).a!=null&&AY(this,Sa(t.g,t.d[0]).a)}function TPn(e,n,t,i,r,c,o,l){var f,h,b,p,y,S,M,O,I,R;if(M=!1,h=bbe(t.q,n.f+n.b-t.q.f),S=i.f>n.b&&l,R=r-(t.q.e+h-o),p=(f=eS(i,R,!1),f.a),S&&p>i.f)return!1;if(S){for(y=0,I=new L(n.d);I.a=(pn(c,e.c.length),u(e.c[c],186)).e,!S&&p>n.b&&!b)?!1:((b||S||p<=n.b)&&(b&&p>n.b?(t.d=p,ZC(t,TGe(t,p))):(HJe(t.q,h),t.c=!0),ZC(i,r-(t.s+t.r)),NO(i,t.q.e+t.q.d,n.f),pB(n,i),e.c.length>c&&(LO((pn(c,e.c.length),u(e.c[c],186)),i),(pn(c,e.c.length),u(e.c[c],186)).a.c.length==0&&yd(e,c)),M=!0),M)}function xPn(e,n){var t,i,r,c,o,l,f,h,b,p;for(e.a=new fIe(ekn(BA)),i=new L(n.a);i.a0&&(Yn(0,t.length),t.charCodeAt(0)!=47)))throw P(new Gn("invalid opaquePart: "+t));if(e&&!(n!=null&&bE(yG,n.toLowerCase()))&&!(t==null||!wQ(t,YA,QA)))throw P(new Gn(DWe+t));if(e&&n!=null&&bE(yG,n.toLowerCase())&&!TMn(t))throw P(new Gn(DWe+t));if(!IEn(i))throw P(new Gn("invalid device: "+i));if(!Nkn(r))throw o=r==null?"invalid segments: null":"invalid segment: "+Akn(r),P(new Gn(o));if(!(c==null||ch(c,Uo(35))==-1))throw P(new Gn("invalid query: "+c))}function KKe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R;if(y=new gc(e.o),R=n.a/y.a,l=n.b/y.b,O=n.a-y.a,c=n.b-y.b,t)for(r=ue(T(e,(Ce(),Zi)))===ue((Rr(),eo)),M=new L(e.j);M.a=1&&(I-o>0&&p>=0?(f.n.a+=O,f.n.b+=c*o):I-o<0&&b>=0&&(f.n.a+=O*I,f.n.b+=c));e.o.a=n.a,e.o.b=n.b,fe(e,(Ce(),bg),(Xs(),i=u(na(UA),10),new Nl(i,u(Tf(i,i.length),10),0)))}function DPn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R;if(t.Tg("Network simplex layering",1),e.b=n,R=u(T(n,(Ce(),aA)),15).a*4,I=e.b.a,I.c.length<1){t.Ug();return}for(c=AIn(e,I),O=null,r=Et(c,0);r.b!=r.d.c;){for(i=u(yt(r),16),l=R*sc(E.Math.sqrt(i.gc())),o=BIn(i),_Z(Aoe(Bbn(Moe(GK(o),l),O),!0),t.dh(1)),y=e.b.b,M=new L(o.a);M.a1)for(O=oe(It,Zt,30,e.b.b.c.length,15,1),p=0,h=new L(e.b.b);h.a0){ez(e,t,0),t.a+=String.fromCharCode(i),r=pjn(n,c),ez(e,t,r),c+=r-1;continue}i==39?c+10&&M.a<=0){f.c.length=0,Jn(f.c,M);break}S=M.i-M.d,S>=l&&(S>l&&(f.c.length=0,l=S),Jn(f.c,M))}f.c.length!=0&&(o=u(_e(f,uz(r,f.c.length)),116),K.a.Ac(o)!=null,o.g=b++,Zbe(o,n,t,i),f.c.length=0)}for(I=e.c.length+1,y=new L(e);y.aNr||n.o==mg&&b=l&&r<=f)l<=r&&c<=f?(t[b++]=r,t[b++]=c,i+=2):l<=r?(t[b++]=r,t[b++]=f,e.b[i]=f+1,o+=2):c<=f?(t[b++]=l,t[b++]=c,i+=2):(t[b++]=l,t[b++]=f,e.b[i]=f+1);else if(fB0)&&l<10);Toe(e.c,new i5),VKe(e),Tvn(e.c),kPn(e.f)}function GPn(e,n){var t,i,r,c,o,l,f,h,b,p,y;switch(e.k.g){case 1:if(i=u(T(e,(pe(),gi)),17),t=u(T(i,Bve),78),t?$e(Pe(T(i,$d)))&&(t=h1e(t)):t=new Ss,h=u(T(e,ga),12),h){if(b=wu(z(B(_r,1),je,8,0,[h.i.n,h.n,h.a])),n<=b.a)return b.b;Xi(t,b,t.a,t.a.a)}if(p=u(T(e,hf),12),p){if(y=wu(z(B(_r,1),je,8,0,[p.i.n,p.n,p.a])),y.a<=n)return y.b;Xi(t,y,t.c.b,t.c)}if(t.b>=2){for(f=Et(t,0),o=u(yt(f),8),l=u(yt(f),8);l.a0&&vO(h,!0,(vr(),ru)),l.k==(zn(),gr)&&AIe(h),Qt(e.f,l,n)}}function QKe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G;for(h=Ki,b=Ki,l=Nr,f=Nr,y=new L(n.i);y.a=e.j?(++e.j,Te(e.b,me(1)),Te(e.c,b)):(i=e.d[n.p][1],il(e.b,h,me(u(_e(e.b,h),15).a+1-i)),il(e.c,h,W(ie(_e(e.c,h)))+b-i*e.f)),(e.r==(P0(),dD)&&(u(_e(e.b,h),15).a>e.k||u(_e(e.b,h-1),15).a>e.k)||e.r==bD&&(W(ie(_e(e.c,h)))>e.n||W(ie(_e(e.c,h-1)))>e.n))&&(f=!1),o=new qn(Vn(rr(n).a.Jc(),new ne));at(o);)c=u(tt(o),17),l=c.c.i,e.g[l.p]==h&&(p=ZKe(e,l),r=r+u(p.a,15).a,f=f&&$e(Pe(p.b)));return e.g[n.p]=h,r=r+e.d[n.p][0],new yc(me(r),(Pn(),!!f))}function UPn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se;return y=e.c[n],S=e.c[t],M=u(T(y,(pe(),p6)),16),!!M&&M.gc()!=0&&M.Gc(S)||(O=y.k!=(zn(),hr)&&S.k!=hr,I=u(T(y,Qw),9),R=u(T(S,Qw),9),G=I!=R,K=!!I&&I!=y||!!R&&R!=S,ee=zQ(y,(Oe(),Xn)),he=zQ(S,dt),K=K|(zQ(y,dt)||zQ(S,Xn)),se=K&&G||ee||he,O&&se)||y.k==(zn(),go)&&S.k==Qi||S.k==(zn(),go)&&y.k==Qi?!1:(b=e.c[n],c=e.c[t],r=_Je(e.e,b,c,(Oe(),Kn)),f=_Je(e.i,b,c,Wn),vNn(e.f,b,c),h=Fze(e.b,b,c)+u(r.a,15).a+u(f.a,15).a+e.f.d,l=Fze(e.b,c,b)+u(r.b,15).a+u(f.b,15).a+e.f.b,e.a&&(p=u(T(b,gi),12),o=u(T(c,gi),12),i=mJe(e.g,p,o),h+=u(i.a,15).a,l+=u(i.b,15).a),h>l)}function WKe(e,n){var t,i,r,c,o;t=W(ie(T(n,(Ce(),Hf)))),t<2&&fe(n,Hf,2),i=u(T(n,dl),86),i==(vr(),Xa)&&fe(n,dl,HB(n)),r=u(T(n,jun),15),r.a==0?fe(n,(pe(),v6),new bQ):fe(n,(pe(),v6),new qR(r.a)),c=Pe(T(n,sA)),c==null&&fe(n,sA,(Pn(),ue(T(n,q1))===ue((L1(),C7)))),Wi(new bn(null,new wn(n.a,16)),new Gue(e)),Wi(ou(new bn(null,new wn(n.b,16)),new Wd),new que(e)),o=new XKe(n),fe(n,(pe(),Sv),o),PC(e.a),ia(e.a,(Br(),Ff),u(T(n,j6),188)),ia(e.a,Wh,u(T(n,hJ),188)),ia(e.a,Zu,u(T(n,uA),188)),ia(e.a,Wu,u(T(n,wJ),188)),ia(e.a,_c,j7n(u(T(n,q1),222))),Dse(e.a,FRn(n)),fe(n,gie,tN(e.a,n))}function dge(e,n,t,i,r){var c,o,l,f,h,b,p,y,S,M,O,I,R;for(p=new gt,o=new xe,XGe(e,t,e.d.zg(),o,p),XGe(e,i,e.d.Ag(),o,p),e.b=.2*(O=eUe(ou(new bn(null,new wn(o,16)),new gT)),I=eUe(ou(new bn(null,new wn(o,16)),new wT)),E.Math.min(O,I)),c=0,l=0;l=2&&(R=EUe(o,!0,y),!e.e&&(e.e=new mje(e)),gjn(e.e,R,o,e.b)),WJe(o,y),ZPn(o),S=-1,b=new L(o);b.a0&&(t+=f.n.a+f.o.a/2,++p),M=new L(f.j);M.a0&&(t/=p),R=oe(Fr,zc,30,i.a.c.length,15,1),l=0,h=new L(i.a);h.a-1){for(r=Et(l,0);r.b!=r.d.c;)i=u(yt(r),132),i.v=o;for(;l.b!=0;)for(i=u(VQ(l,0),132),t=new L(i.i);t.a-1){for(c=new L(l);c.a0)&&(Yg(f,E.Math.min(f.o,r.o-1)),o0(f,f.i-1),f.i==0&&Jn(l.c,f))}}function tVe(e,n,t,i,r){var c,o,l,f;return f=Ki,o=!1,l=rge(e,Or(new ke(n.a,n.b),e),bi(new ke(t.a,t.b),r),Or(new ke(i.a,i.b),t)),c=!!l&&!(E.Math.abs(l.a-e.a)<=Fw&&E.Math.abs(l.b-e.b)<=Fw||E.Math.abs(l.a-n.a)<=Fw&&E.Math.abs(l.b-n.b)<=Fw),l=rge(e,Or(new ke(n.a,n.b),e),t,r),l&&((E.Math.abs(l.a-e.a)<=Fw&&E.Math.abs(l.b-e.b)<=Fw)==(E.Math.abs(l.a-n.a)<=Fw&&E.Math.abs(l.b-n.b)<=Fw)||c?f=E.Math.min(f,ej(Or(l,t))):o=!0),l=rge(e,Or(new ke(n.a,n.b),e),i,r),l&&(o||(E.Math.abs(l.a-e.a)<=Fw&&E.Math.abs(l.b-e.b)<=Fw)==(E.Math.abs(l.a-n.a)<=Fw&&E.Math.abs(l.b-n.b)<=Fw)||c)&&(f=E.Math.min(f,ej(Or(l,i)))),f}function iVe(e){G2(e,new xw(HP(H2(B2(F2(z2(new od,F0),YYe),"Minimizes the stress within a layout using stress majorization. Stress exists if the euclidean distance between a pair of nodes doesn't match their graph theoretic distance, that is, the shortest path between the two nodes. The method allows to specify individual edge lengths."),new H7),Po))),Ee(e,F0,hS,Ie(r3e)),Ee(e,F0,oF,(Pn(),!0)),Ee(e,F0,cv,Ie(Mtn)),Ee(e,F0,t6,Ie(Ttn)),Ee(e,F0,n6,Ie(xtn)),Ee(e,F0,D8,Ie(Atn)),Ee(e,F0,dS,Ie(u3e)),Ee(e,F0,I8,Ie(Ctn)),Ee(e,F0,Wge,Ie(i3e)),Ee(e,F0,nwe,Ie(n3e)),Ee(e,F0,twe,Ie(t3e)),Ee(e,F0,iwe,Ie(c3e)),Ee(e,F0,ewe,Ie(vH))}function WPn(e){var n,t,i,r,c,o,l,f;for(n=null,i=new L(e);i.a0&&t.c==0&&(!n&&(n=new xe),Jn(n.c,t));if(n)for(;n.c.length!=0;){if(t=u(yd(n,0),239),t.b&&t.b.c.length>0){for(c=(!t.b&&(t.b=new xe),new L(t.b));c.agu(e,t,0))return new yc(r,t)}else if(W(Sa(r.g,r.d[0]).a)>W(Sa(t.g,t.d[0]).a))return new yc(r,t)}for(l=(!t.e&&(t.e=new xe),t.e).Jc();l.Ob();)o=u(l.Pb(),239),f=(!o.b&&(o.b=new xe),o.b),fp(0,f.c.length),jE(f.c,0,t),o.c==f.c.length&&Jn(n.c,o)}return null}function iS(e,n){var t,i,r,c,o,l,f,h,b;if(n.e==5){YKe(e,n);return}if(h=n,!(h.b==null||e.b==null)){for(Y3(e),nS(e),Y3(h),nS(h),t=oe(It,Zt,30,e.b.length+h.b.length,15,1),b=0,i=0,o=0;i=l&&r<=f)l<=r&&c<=f?i+=2:l<=r?(e.b[i]=f+1,o+=2):c<=f?(t[b++]=r,t[b++]=l-1,i+=2):(t[b++]=r,t[b++]=l-1,e.b[i]=f+1,o+=2);else if(f0),u(b.a.Xb(b.c=--b.b),17));c!=i&&b.b>0;)e.a[c.p]=!0,e.a[i.p]=!0,c=(ft(b.b>0),u(b.a.Xb(b.c=--b.b),17));b.b>0&&As(b)}}function rVe(e,n,t){var i,r,c,o,l,f,h,b,p,y;if(t)for(i=-1,b=new Gr(n,0);b.b0?r-=864e5:r+=864e5,f=new dle(pc(_u(n.q.getTime()),r))),b=new Y5,h=e.a.length,c=0;c=97&&i<=122||i>=65&&i<=90){for(o=c+1;o=h)throw P(new Gn("Missing trailing '"));o+1=14&&b<=16))?n.a._b(i)?(t.a?Gt(t.a,t.b):t.a=new Ws(t.d),PE(t.a,"[...]")):(l=x4(i),h=new tp(n),x1(t,uVe(l,h))):q(i,171)?x1(t,Xxn(u(i,171))):q(i,195)?x1(t,LMn(u(i,195))):q(i,201)?x1(t,FTn(u(i,201))):q(i,2073)?x1(t,PMn(u(i,2073))):q(i,54)?x1(t,Uxn(u(i,54))):q(i,584)?x1(t,uCn(u(i,584))):q(i,830)?x1(t,qxn(u(i,830))):q(i,108)&&x1(t,Gxn(u(i,108))):x1(t,i==null?Ko:su(i));return t.a?t.e.length==0?t.a.a:t.a.a+(""+t.e):t.c}function p8(e,n){var t,i,r,c;c=e.F,n==null?(e.F=null,J9(e,null)):(e.F=(_n(n),n),i=ch(n,Uo(60)),i!=-1?(r=(Yr(0,i,n.length),n.substr(0,i)),ch(n,Uo(46))==-1&&!hn(r,X4)&&!hn(r,xS)&&!hn(r,qF)&&!hn(r,CS)&&!hn(r,OS)&&!hn(r,NS)&&!hn(r,DS)&&!hn(r,IS)&&(r=qWe),t=P$(n,Uo(62)),t!=-1&&(r+=""+(Yn(t+1,n.length+1),n.substr(t+1))),J9(e,r)):(r=n,ch(n,Uo(46))==-1&&(i=ch(n,Uo(91)),i!=-1&&(r=(Yr(0,i,n.length),n.substr(0,i))),!hn(r,X4)&&!hn(r,xS)&&!hn(r,qF)&&!hn(r,CS)&&!hn(r,OS)&&!hn(r,NS)&&!hn(r,DS)&&!hn(r,IS)?(r=qWe,i!=-1&&(r+=""+(Yn(i,n.length+1),n.substr(i)))):r=n),J9(e,r),r==n&&(e.F=e.D))),(e.Db&4)!=0&&(e.Db&1)==0&&fi(e,new Dr(e,1,5,c,n))}function u$n(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M;if(e.c=e.e,M=Pe(T(n,(Ce(),Sun))),S=M==null||(_n(M),M),c=u(T(n,(pe(),wo)),22).Gc((Oc(),ql)),r=u(T(n,Zi),102),t=!(r==(Rr(),kg)||r==c1||r==eo),S&&(t||!c)){for(p=new L(n.a);p.a=0)return r=CEn(e,(Yr(1,o,n.length),n.substr(1,o-1))),b=(Yr(o+1,f,n.length),n.substr(o+1,f-(o+1))),DRn(e,b,r)}else{if(t=-1,mme==null&&(mme=new RegExp("\\d")),mme.test(String.fromCharCode(l))&&(t=_le(n,Uo(46),f-1),t>=0)){i=u(cY(e,zRe(e,(Yr(1,t,n.length),n.substr(1,t-1))),!1),61),h=0;try{h=sl((Yn(t+1,n.length+1),n.substr(t+1)),Ur,ri)}catch(y){throw y=or(y),q(y,131)?(c=y,P(new rB(c))):P(y)}if(h>16==-10?t=u(e.Cb,293).Wk(n,t):e.Db>>16==-15&&(!n&&(n=(mn(),Ya)),!h&&(h=(mn(),Ya)),e.Cb.Vh()&&(f=new O1(e.Cb,1,13,h,n,Cd(xs(u(e.Cb,62)),e),!1),t?t.lj(f):t=f));else if(q(e.Cb,88))e.Db>>16==-23&&(q(n,88)||(n=(mn(),vf)),q(h,88)||(h=(mn(),vf)),e.Cb.Vh()&&(f=new O1(e.Cb,1,10,h,n,Cd(Xu(u(e.Cb,29)),e),!1),t?t.lj(f):t=f));else if(q(e.Cb,446))for(l=u(e.Cb,834),o=(!l.b&&(l.b=new xP(new wX)),l.b),c=(i=new mp(new tn(o.a).a),new CP(i));c.a.b;)r=u(z3(c.a).jd(),87),t=m8(r,xz(r,l),t)}return t}function s$n(e,n){var t,i,r,c,o,l,f,h,b,p,y;for(o=$e(Pe(ve(e,(Ce(),im)))),y=u(ve(e,um),22),f=!1,h=!1,p=new ct((!e.c&&(e.c=new we(Ps,e,9,9)),e.c));p.e!=p.i.gc()&&(!f||!h);){for(c=u(lt(p),125),l=0,r=zh(Ll(z(B(Gl,1),xn,20,0,[(!c.d&&(c.d=new Cn(wr,c,8,5)),c.d),(!c.e&&(c.e=new Cn(wr,c,7,4)),c.e)])));at(r)&&(i=u(tt(r),85),b=o&&Cw(i)&&$e(Pe(ve(i,dg))),t=zKe((!i.b&&(i.b=new Cn(pt,i,4,7)),i.b),c)?e==zi(iu(u(X((!i.c&&(i.c=new Cn(pt,i,5,8)),i.c),0),84))):e==zi(iu(u(X((!i.b&&(i.b=new Cn(pt,i,4,7)),i.b),0),84))),!((b||t)&&(++l,l>1))););(l>0||y.Gc((ws(),K1))&&(!c.n&&(c.n=new we(ku,c,1,7)),c.n).i>0)&&(f=!0),l>1&&(h=!0)}f&&n.Ec((Oc(),ql)),h&&n.Ec((Oc(),VS))}function sVe(e){var n,t,i,r,c,o,l,f,h,b,p,y;if(y=u(ve(e,(Ht(),yg)),22),y.dc())return null;if(l=0,o=0,y.Gc((Xs(),JD))){for(b=u(ve(e,RA),102),i=2,t=2,r=2,c=2,n=zi(e)?u(ve(zi(e),vg),86):u(ve(e,vg),86),h=new ct((!e.c&&(e.c=new we(Ps,e,9,9)),e.c));h.e!=h.i.gc();)if(f=u(lt(h),125),p=u(ve(f,zv),64),p==(Oe(),yu)&&(p=Qbe(f,n),yi(f,zv,p)),b==(Rr(),eo))switch(p.g){case 1:i=E.Math.max(i,f.i+f.g);break;case 2:t=E.Math.max(t,f.j+f.f);break;case 3:r=E.Math.max(r,f.i+f.g);break;case 4:c=E.Math.max(c,f.j+f.f)}else switch(p.g){case 1:i+=f.g+2;break;case 2:t+=f.f+2;break;case 3:r+=f.g+2;break;case 4:c+=f.f+2}l=E.Math.max(i,r),o=E.Math.max(t,c)}return Iw(e,l,o,!0,!0)}function l$n(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O;for(r=null,i=new L(n.a);i.a1)for(r=e.e.b,qt(e.e,f),l=f.a.ec().Jc();l.Ob();)o=u(l.Pb(),9),Qt(e.c,o,me(r))}}function f$n(e,n,t,i){var r,c,o,l,f,h,b,p,y,S;for(c=new Mqe(n),p=$Dn(e,n,c),S=E.Math.max(W(ie(T(n,(Ce(),Rd)))),1),b=new L(p.a);b.a=0){for(f=null,l=new Gr(b.a,h+1);l.b0,h?h&&(y=R.p,o?++y:--y,p=u(_e(R.c.a,y),9),i=kze(p),S=!(CUe(i,se,t[0])||BDe(i,se,t[0]))):S=!0),M=!1,he=n.D.i,he&&he.c&&l.e&&(b=o&&he.p>0||!o&&he.p=0&&Oo?1:Tb(isNaN(0),isNaN(o)))<0&&(Df(kh),(E.Math.abs(o-1)<=kh||o==1||isNaN(o)&&isNaN(1)?0:o<1?-1:o>1?1:Tb(isNaN(o),isNaN(1)))<0)&&(Df(kh),(E.Math.abs(0-l)<=kh||l==0||isNaN(0)&&isNaN(l)?0:0l?1:Tb(isNaN(0),isNaN(l)))<0)&&(Df(kh),(E.Math.abs(l-1)<=kh||l==1||isNaN(l)&&isNaN(1)?0:l<1?-1:l>1?1:Tb(isNaN(l),isNaN(1)))<0)),c)}function m$n(e){var n,t,i,r,c,o,l,f,h,b,p;for(e.j=oe(It,Zt,30,e.g,15,1),e.o=new xe,Wi(ou(new bn(null,new wn(e.e.b,16)),new n3),new dje(e)),e.a=oe(ns,fa,30,e.b,16,1),SO(new bn(null,new wn(e.e.b,16)),new gje(e)),i=(p=new xe,Wi(ci(ou(new bn(null,new wn(e.e.b,16)),new j5),new bje(e)),new WTe(e,p)),p),f=new L(i);f.a=h.c.c.length?b=Oae((zn(),Qi),hr):b=Oae((zn(),hr),hr),b*=2,c=t.a.g,t.a.g=E.Math.max(c,c+(b-c)),o=t.b.g,t.b.g=E.Math.max(o,o+(b-o)),r=n}}function Xz(e,n){var t;if(e.e)throw P(new qc((E1(lte),zW+lte.k+FW)));if(!Ngn(e.a,n))throw P(new au(xYe+n+CYe));if(n==e.d)return e;switch(t=e.d,e.d=n,t.g){case 0:switch(n.g){case 2:Mw(e);break;case 1:x0(e),Mw(e);break;case 4:X3(e),Mw(e);break;case 3:X3(e),x0(e),Mw(e)}break;case 2:switch(n.g){case 1:x0(e),CZ(e);break;case 4:X3(e),Mw(e);break;case 3:X3(e),x0(e),Mw(e)}break;case 1:switch(n.g){case 2:x0(e),CZ(e);break;case 4:x0(e),X3(e),Mw(e);break;case 3:x0(e),X3(e),x0(e),Mw(e)}break;case 4:switch(n.g){case 2:X3(e),Mw(e);break;case 1:X3(e),x0(e),Mw(e);break;case 3:x0(e),CZ(e)}break;case 3:switch(n.g){case 2:x0(e),X3(e),Mw(e);break;case 1:x0(e),X3(e),x0(e),Mw(e);break;case 4:x0(e),CZ(e)}}return e}function nv(e,n){var t;if(e.d)throw P(new qc((E1(jte),zW+jte.k+FW)));if(!Ogn(e.a,n))throw P(new au(xYe+n+CYe));if(n==e.c)return e;switch(t=e.c,e.c=n,t.g){case 0:switch(n.g){case 2:qb(e);break;case 1:T0(e),qb(e);break;case 4:K3(e),qb(e);break;case 3:K3(e),T0(e),qb(e)}break;case 2:switch(n.g){case 1:T0(e),OZ(e);break;case 4:K3(e),qb(e);break;case 3:K3(e),T0(e),qb(e)}break;case 1:switch(n.g){case 2:T0(e),OZ(e);break;case 4:T0(e),K3(e),qb(e);break;case 3:T0(e),K3(e),T0(e),qb(e)}break;case 4:switch(n.g){case 2:K3(e),qb(e);break;case 1:K3(e),T0(e),qb(e);break;case 3:T0(e),OZ(e)}break;case 3:switch(n.g){case 2:T0(e),K3(e),qb(e);break;case 1:T0(e),K3(e),T0(e),qb(e);break;case 4:T0(e),OZ(e)}}return e}function v$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K;for(p=e.b,b=new Gr(p,0),W2(b,new qu(e)),G=!1,o=1;b.b0&&(n.a+=xo),Kz(u(lt(l),174),n);for(n.a+=VW,f=new u4((!i.c&&(i.c=new Cn(pt,i,5,8)),i.c));f.e!=f.i.gc();)f.e>0&&(n.a+=xo),Kz(u(lt(f),174),n);n.a+=")"}}function y$n(e,n,t){var i,r,c,o,l,f,h,b;for(f=new ct((!e.a&&(e.a=new we($t,e,10,11)),e.a));f.e!=f.i.gc();)for(l=u(lt(f),26),r=new qn(Vn(L0(l).a.Jc(),new ne));at(r);){if(i=u(tt(r),85),!i.b&&(i.b=new Cn(pt,i,4,7)),!(i.b.i<=1&&(!i.c&&(i.c=new Cn(pt,i,5,8)),i.c.i<=1)))throw P(new V5("Graph must not contain hyperedges."));if(!Jj(i)&&l!=iu(u(X((!i.c&&(i.c=new Cn(pt,i,5,8)),i.c),0),84)))for(h=new UOe,Lu(h,i),fe(h,(S0(),a6),i),kP(h,u(hu(Uc(t.f,l)),155)),VU(h,u(Bn(t,iu(u(X((!i.c&&(i.c=new Cn(pt,i,5,8)),i.c),0),84))),155)),Te(n.c,h),o=new ct((!i.n&&(i.n=new we(ku,i,1,7)),i.n));o.e!=o.i.gc();)c=u(lt(o),157),b=new ePe(h,c.a),Lu(b,c),fe(b,a6,c),b.e.a=E.Math.max(c.g,1),b.e.b=E.Math.max(c.f,1),ige(b),Te(n.d,b)}}function k$n(e,n,t){var i,r,c,o,l,f,h,b,p,y;switch(t.Tg("Node promotion heuristic",1),e.i=n,e.r=u(T(n,(Ce(),lD)),243),e.r!=(P0(),m7)&&e.r!=bA?X$n(e):vDn(e),b=u(T(e.i,K5e),15).a,c=new Aq,e.r.g){case 2:case 1:w8(e,c);break;case 3:for(e.r=AJ,w8(e,c),f=0,l=new L(e.b);l.ae.k&&(e.r=dD,w8(e,c));break;case 4:for(e.r=AJ,w8(e,c),h=0,r=new L(e.c);r.ae.n&&(e.r=bD,w8(e,c));break;case 6:y=sc(E.Math.ceil(e.g.length*b/100)),w8(e,new hEe(y));break;case 5:p=sc(E.Math.ceil(e.e*b/100)),w8(e,new dEe(p));break;case 8:GVe(e,!0);break;case 9:GVe(e,!1);break;default:w8(e,c)}e.r!=m7&&e.r!=bA?BNn(e,n):rIn(e,n),t.Ug()}function E$n(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K;for(p=new mge(e),f4n(p,!(n==(vr(),Ul)||n==Ua)),b=p.a,y=new q5,r=(sa(),z(B(Up,1),ye,237,0,[xu,Oo,Cu])),o=0,f=r.length;o0&&(y.d+=b.n.d,y.d+=b.d),y.a>0&&(y.a+=b.n.a,y.a+=b.d),y.b>0&&(y.b+=b.n.b,y.b+=b.d),y.c>0&&(y.c+=b.n.c,y.c+=b.d),y}function aVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M;for(y=t.d,p=t.c,c=new ke(t.f.a+t.d.b+t.d.c,t.f.b+t.d.d+t.d.a),o=c.b,h=new L(e.a);h.a0&&(e.c[n.c.p][n.p].d+=Ds(e.i,24)*pN*.07000000029802322-.03500000014901161,e.c[n.c.p][n.p].a=e.c[n.c.p][n.p].d/e.c[n.c.p][n.p].b)}}function S$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,M,O,I;for(M=new L(e);M.ai.d,i.d=E.Math.max(i.d,n),l&&t&&(i.d=E.Math.max(i.d,i.a),i.a=i.d+r);break;case 3:t=n>i.a,i.a=E.Math.max(i.a,n),l&&t&&(i.a=E.Math.max(i.a,i.d),i.d=i.a+r);break;case 2:t=n>i.c,i.c=E.Math.max(i.c,n),l&&t&&(i.c=E.Math.max(i.b,i.c),i.b=i.c+r);break;case 4:t=n>i.b,i.b=E.Math.max(i.b,n),l&&t&&(i.b=E.Math.max(i.b,i.c),i.c=i.b+r)}}}function dVe(e,n){var t,i,r,c,o,l,f,h,b;return h="",n.length==0?e.le(Ige,aW,-1,-1):(b=xp(n),hn(b.substr(0,3),"at ")&&(b=(Yn(3,b.length+1),b.substr(3))),b=b.replace(/\[.*?\]/g,""),o=b.indexOf("("),o==-1?(o=b.indexOf("@"),o==-1?(h=b,b=""):(h=xp((Yn(o+1,b.length+1),b.substr(o+1))),b=xp((Yr(0,o,b.length),b.substr(0,o))))):(t=b.indexOf(")",o),h=(Yr(o+1,t,b.length),b.substr(o+1,t-(o+1))),b=xp((Yr(0,o,b.length),b.substr(0,o)))),o=ch(b,Uo(46)),o!=-1&&(b=(Yn(o+1,b.length+1),b.substr(o+1))),(b.length==0||hn(b,"Anonymous function"))&&(b=aW),l=P$(h,Uo(58)),r=_le(h,Uo(58),l-1),f=-1,i=-1,c=Ige,l!=-1&&r!=-1&&(c=(Yr(0,r,h.length),h.substr(0,r)),f=aOe((Yr(r+1,l,h.length),h.substr(r+1,l-(r+1)))),i=aOe((Yn(l+1,h.length+1),h.substr(l+1)))),e.le(c,b,f,i))}function M$n(e){var n,t,i,r,c,o,l,f,h,b,p;for(h=new L(e);h.a0||b.j==Kn&&b.e.c.length-b.g.c.length<0)){n=!1;break}for(r=new L(b.g);r.a=h&&he>=I&&(y+=M.n.b+O.n.b+O.a.b-ee,++l));if(t)for(o=new L(G.e);o.a=h&&he>=I&&(y+=M.n.b+O.n.b+O.a.b-ee,++l))}l>0&&(se+=y/l,++S)}S>0?(n.a=r*se/S,n.g=S):(n.a=0,n.g=0)}function wge(e,n,t,i){var r,c,o,l,f;return l=new mge(n),ENn(l,i),r=!0,e&&e.nf((Ht(),vg))&&(c=u(e.mf((Ht(),vg)),86),r=c==(vr(),Xa)||c==Zc||c==ru),dXe(l,!1),Ao(l.e.Pf(),new Ble(l,!1,r)),$V(l,l.f,(sa(),xu),(Oe(),Xn)),$V(l,l.f,Cu,dt),$V(l,l.g,xu,Kn),$V(l,l.g,Cu,Wn),_He(l,Xn),_He(l,dt),CIe(l,Wn),CIe(l,Kn),Z2(),o=l.A.Gc((Xs(),km))&&l.B.Gc((Is(),qD))?XFe(l):null,o&&Fbn(l.a,o),A$n(l),USn(l),XSn(l),n$n(l),b_n(l),mAn(l),AQ(l,Xn),AQ(l,dt),tIn(l),GLn(l),t&&(REn(l),vAn(l),AQ(l,Wn),AQ(l,Kn),f=l.B.Gc((Is(),XA)),eqe(l,f,Xn),eqe(l,f,dt),nqe(l,f,Wn),nqe(l,f,Kn),Wi(new bn(null,new wn(new rt(l.i),0)),new Wv),Wi(ci(new bn(null,Ife(l.r).a.oc()),new e5),new n5),NMn(l),l.e.Nf(l.o),Wi(new bn(null,Ife(l.r).a.oc()),new Dg)),l.o}function x$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,M,O;for(h=Ki,i=new L(e.a.b);i.a1)for(S=new oge(M,K,i),rc(K,new nxe(e,S)),Jn(o.c,S),p=K.a.ec().Jc();p.Ob();)b=u(p.Pb(),49),Go(c,b.b);if(l.a.gc()>1)for(S=new oge(M,l,i),rc(l,new txe(e,S)),Jn(o.c,S),p=l.a.ec().Jc();p.Ob();)b=u(p.Pb(),49),Go(c,b.b)}}function D$n(e,n){var t,i,r,c,o,l;if(u(T(n,(pe(),wo)),22).Gc((Oc(),ql))){for(l=new L(n.a);l.a=0&&o0&&(u(Rc(e.b,n),127).a.b=t)}function B$n(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R;for(S=0,i=new fr,c=new ct((!n.a&&(n.a=new we($t,n,10,11)),n.a));c.e!=c.i.gc();)r=u(lt(c),26),$e(Pe(ve(r,(Ce(),gg))))||(p=zi(r),Jz(p)&&!$e(Pe(ve(r,oJ)))&&(yi(r,(pe(),Ci),me(S)),++S,ua(r,nm)&&ar(i,u(ve(r,nm),15))),gVe(e,r,t));for(fe(t,(pe(),Q0),me(S)),fe(t,iD,me(i.a.gc())),S=0,b=new ct((!n.b&&(n.b=new we(wr,n,12,3)),n.b));b.e!=b.i.gc();)f=u(lt(b),85),Jz(n)&&(yi(f,Ci,me(S)),++S),I=oZ(f),R=hGe(f),y=$e(Pe(ve(I,(Ce(),im)))),O=!$e(Pe(ve(f,gg))),M=y&&Cw(f)&&$e(Pe(ve(f,dg))),o=zi(I)==n&&zi(I)==zi(R),l=(zi(I)==n&&R==n)^(zi(R)==n&&I==n),O&&!M&&(l||o)&&jge(e,f,n,t);if(zi(n))for(h=new ct(PIe(zi(n)));h.e!=h.i.gc();)f=u(lt(h),85),I=oZ(f),I==n&&Cw(f)&&(M=$e(Pe(ve(I,(Ce(),im))))&&$e(Pe(ve(f,dg))),M&&jge(e,f,n,t))}function z$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne,nn,en,Tn,On;for(se=new xe,M=new L(e.b);M.a=n.length)return{done:!0};var r=n[i++];return{value:[r,t.get(r)],done:!1}}}},PDn()||(e.prototype.createObject=function(){return{}},e.prototype.get=function(n){return this.obj[":"+n]},e.prototype.set=function(n,t){this.obj[":"+n]=t},e.prototype[LW]=function(n){delete this.obj[":"+n]},e.prototype.keys=function(){var n=[];for(var t in this.obj)t.charCodeAt(0)==58&&n.push(t.substring(1));return n}),e}function xi(){xi=Y,SA=new mi(Zge),new Li("DEPTH",me(0)),fre=new Li("FAN",me(0)),s6e=new Li(BQe,me(0)),tb=new Li("ROOT",(Pn(),!1)),dre=new Li("LEFTNEIGHBOR",null),Von=new Li("RIGHTNEIGHBOR",null),IJ=new Li("LEFTSIBLING",null),bre=new Li("RIGHTSIBLING",null),lre=new Li("DUMMY",!1),new Li("LEVEL",me(0)),a6e=new Li("REMOVABLE_EDGES",new ji),yD=new Li("XCOOR",me(0)),kD=new Li("YCOOR",me(0)),_J=new Li("LEVELHEIGHT",0),wa=new Li("LEVELMIN",0),Jf=new Li("LEVELMAX",0),are=new Li("GRAPH_XMIN",0),hre=new Li("GRAPH_YMIN",0),l6e=new Li("GRAPH_XMAX",0),f6e=new Li("GRAPH_YMAX",0),o6e=new Li("COMPACT_LEVEL_ASCENSION",!1),sre=new Li("COMPACT_CONSTRAINTS",new xe),jA=new Li("ID",""),AA=new Li("POSITION",me(0)),Fd=new Li("PRELIM",0),k7=new Li("MODIFIER",0),y7=new mi(KYe),vD=new mi(VYe)}function G$n(e){Ybe();var n,t,i,r,c,o,l,f,h,b,p,y,S,M,O,I;if(e==null)return null;if(p=e.length*8,p==0)return"";for(l=p%24,S=p/24|0,y=l!=0?S+1:S,c=null,c=oe(Vl,ph,30,y*4,15,1),h=0,b=0,n=0,t=0,i=0,o=0,r=0,f=0;f>24,h=(n&3)<<24>>24,M=(n&-128)==0?n>>2<<24>>24:(n>>2^192)<<24>>24,O=(t&-128)==0?t>>4<<24>>24:(t>>4^240)<<24>>24,I=(i&-128)==0?i>>6<<24>>24:(i>>6^252)<<24>>24,c[o++]=Yd[M],c[o++]=Yd[O|h<<4],c[o++]=Yd[b<<2|I],c[o++]=Yd[i&63];return l==8?(n=e[r],h=(n&3)<<24>>24,M=(n&-128)==0?n>>2<<24>>24:(n>>2^192)<<24>>24,c[o++]=Yd[M],c[o++]=Yd[h<<4],c[o++]=61,c[o++]=61):l==16&&(n=e[r],t=e[r+1],b=(t&15)<<24>>24,h=(n&3)<<24>>24,M=(n&-128)==0?n>>2<<24>>24:(n>>2^192)<<24>>24,O=(t&-128)==0?t>>4<<24>>24:(t>>4^240)<<24>>24,c[o++]=Yd[M],c[o++]=Yd[O|h<<4],c[o++]=Yd[b<<2],c[o++]=61),ah(c,0,c.length)}function q$n(e,n){var t,i,r,c,o,l,f;if(e.e==0&&e.p>0&&(e.p=-(e.p-1)),e.p>Ur&&Dae(n,e.p-z0),o=n.q.getDate(),HC(n,1),e.k>=0&&k4n(n,e.k),e.c>=0?HC(n,e.c):e.k>=0?(f=new s1e(n.q.getFullYear()-z0,n.q.getMonth(),35),i=35-f.q.getDate(),HC(n,E.Math.min(i,o))):HC(n,o),e.f<0&&(e.f=n.q.getHours()),e.b>0&&e.f<12&&(e.f+=12),Nwn(n,e.f==24&&e.g?0:e.f),e.j>=0&&Wyn(n,e.j),e.n>=0&&h9n(n,e.n),e.i>=0&&Kxe(n,pc(ac($O(_u(n.q.getTime()),Dd),Dd),e.i)),e.a&&(r=new e$,Dae(r,r.q.getFullYear()-z0-80),$X(_u(n.q.getTime()),_u(r.q.getTime()))&&Dae(n,r.q.getFullYear()-z0+100)),e.d>=0){if(e.c==-1)t=(7+e.d-n.q.getDay())%7,t>3&&(t-=7),l=n.q.getMonth(),HC(n,n.q.getDate()+t),n.q.getMonth()!=l&&HC(n,n.q.getDate()+(t>0?-7:7));else if(n.q.getDay()!=e.d)return!1}return e.o>Ur&&(c=n.q.getTimezoneOffset(),Kxe(n,pc(_u(n.q.getTime()),(e.o-c)*60*Dd))),!0}function vVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee;if(r=T(n,(pe(),gi)),!!q(r,206)){for(M=u(r,26),O=n.e,y=new gc(n.c),c=n.d,y.a+=c.b,y.b+=c.d,ee=u(ve(M,(Ce(),pJ)),182),rs(ee,(Is(),oG))&&(S=u(ve(M,e4e),104),UU(S,c.a),YU(S,c.d),XU(S,c.b),KU(S,c.c)),t=new xe,b=new L(n.a);b.ai.c.length-1;)Te(i,new yc(ov,D2e));t=u(T(r,Th),15).a,y1(u(T(e,r2),86))?(r.e.aW(ie((pn(t,i.c.length),u(i.c[t],49)).b))&&Bx((pn(t,i.c.length),u(i.c[t],49)),r.e.a+r.f.a)):(r.e.bW(ie((pn(t,i.c.length),u(i.c[t],49)).b))&&Bx((pn(t,i.c.length),u(i.c[t],49)),r.e.b+r.f.b))}for(c=Et(e.b,0);c.b!=c.d.c;)r=u(yt(c),40),t=u(T(r,(Au(),Th)),15).a,fe(r,(xi(),wa),ie((pn(t,i.c.length),u(i.c[t],49)).a)),fe(r,Jf,ie((pn(t,i.c.length),u(i.c[t],49)).b));n.Ug()}function X$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,M,O;for(e.o=W(ie(T(e.i,(Ce(),pg)))),e.f=W(ie(T(e.i,Z0))),e.j=e.i.b.c.length,l=e.j-1,y=0,e.k=0,e.n=0,e.b=Of(oe(Er,je,15,e.j,0,1)),e.c=Of(oe(br,je,346,e.j,7,1)),o=new L(e.i.b);o.a0&&Te(e.q,b),Te(e.p,b);n-=i,S=f+n,h+=n*e.f,il(e.b,l,me(S)),il(e.c,l,h),e.k=E.Math.max(e.k,S),e.n=E.Math.max(e.n,h),e.e+=n,n+=O}}function EVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K;if(n.b!=0){for(S=new ji,l=null,M=null,i=sc(E.Math.floor(E.Math.log(n.b)*E.Math.LOG10E)+1),f=0,K=Et(n,0);K.b!=K.d.c;)for(R=u(yt(K),40),ue(M)!==ue(T(R,(xi(),jA)))&&(M=Dt(T(R,jA)),f=0),M!=null?l=M+rLe(f++,i):l=rLe(f++,i),fe(R,jA,l),I=(r=Et(new v1(R).a.d,0),new h3(r));Kx(I.a);)O=u(yt(I.a),65).c,Xi(S,O,S.c.b,S.c),fe(O,jA,l);for(y=new gt,o=0;o0&&(K-=S),sge(o,K),b=0,y=new L(o.a);y.a0),l.a.Xb(l.c=--l.b)),f=.4*i*b,!c&&l.b0&&(f=(Yn(0,n.length),n.charCodeAt(0)),f!=64)){if(f==37&&(p=n.lastIndexOf("%"),h=!1,p!=0&&(p==y-1||(h=(Yn(p+1,n.length),n.charCodeAt(p+1)==46))))){if(o=(Yr(1,p,n.length),n.substr(1,p-1)),K=hn("%",o)?null:yge(o),i=0,h)try{i=sl((Yn(p+2,n.length+1),n.substr(p+2)),Ur,ri)}catch(ee){throw ee=or(ee),q(ee,131)?(l=ee,P(new rB(l))):P(ee)}for(I=$he(e.Dh());I.Ob();)if(M=xB(I),q(M,504)&&(r=u(M,587),G=r.d,(K==null?G==null:hn(K,G))&&i--==0))return r;return null}if(b=n.lastIndexOf("."),S=b==-1?n:(Yr(0,b,n.length),n.substr(0,b)),t=0,b!=-1)try{t=sl((Yn(b+1,n.length+1),n.substr(b+1)),Ur,ri)}catch(ee){if(ee=or(ee),q(ee,131))S=n;else throw P(ee)}for(S=hn("%",S)?null:yge(S),O=$he(e.Dh());O.Ob();)if(M=xB(O),q(M,197)&&(c=u(M,197),R=c.ve(),(S==null?R==null:hn(S,R))&&t--==0))return c;return null}return oVe(e,n)}function eRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G;for(b=new gt,f=new bw,i=new L(e.a.a.b);i.an.d.c){if(S=e.c[n.a.d],I=e.c[p.a.d],S==I)continue;Pf(Sf(jf(Af(Ef(new Wl,1),100),S),I))}}}}}function nRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se;if(y=u(u(wi(e.r,n),22),83),n==(Oe(),Wn)||n==Kn){wVe(e,n);return}for(c=n==Xn?(kw(),GN):(kw(),qN),ee=n==Xn?(qo(),ba):(qo(),zf),t=u(Rc(e.b,n),127),i=t.i,r=i.c+_3(z(B(Fr,1),zc,30,15,[t.n.b,e.C.b,e.k])),R=i.c+i.b-_3(z(B(Fr,1),zc,30,15,[t.n.c,e.C.c,e.k])),o=xoe(zle(c),e.t),G=n==Xn?Nr:Ki,p=y.Jc();p.Ob();)h=u(p.Pb(),115),!(!h.c||h.c.d.c.length<=0)&&(I=h.b.Kf(),O=h.e,S=h.c,M=S.i,M.b=(f=S.n,S.e.a+f.b+f.c),M.a=(l=S.n,S.e.b+l.d+l.a),BC(ee,qge),S.f=ee,oa(S,(gs(),Bf)),M.c=O.a-(M.b-I.a)/2,he=E.Math.min(r,O.a),se=E.Math.max(R,O.a+I.a),M.cse&&(M.c=se-M.b),Te(o.d,new cV(M,$1e(o,M))),G=n==Xn?E.Math.max(G,O.b+h.b.Kf().b):E.Math.min(G,O.b));for(G+=n==Xn?e.t:-e.t,K=tde((o.e=G,o)),K>0&&(u(Rc(e.b,n),127).a.b=K),b=y.Jc();b.Ob();)h=u(b.Pb(),115),!(!h.c||h.c.d.c.length<=0)&&(M=h.c.i,M.c-=h.e.a,M.d-=h.e.b)}function tRn(e,n){HZ();var t,i,r,c,o,l,f,h,b,p,y,S,M,O;if(f=fo(e,0)<0,f&&(e=Ed(e)),fo(e,0)==0)switch(n){case 0:return"0";case 1:return S8;case 2:return"0.00";case 3:return"0.000";case 4:return"0.0000";case 5:return"0.00000";case 6:return"0.000000";default:return S=new l0,n<0?S.a+="0E+":S.a+="0E",S.a+=n==Ur?"2147483648":""+-n,S.a}b=18,p=oe(Vl,ph,30,b+1,15,1),t=b,O=e;do h=O,O=$O(O,10),p[--t]=_t(pc(48,uf(h,ac(O,10))))&yr;while(fo(O,0)!=0);if(r=uf(uf(uf(b,t),n),1),n==0)return f&&(p[--t]=45),ah(p,t,b-t);if(n>0&&fo(r,-6)>=0){if(fo(r,0)>=0){for(c=t+_t(r),l=b-1;l>=c;l--)p[l+1]=p[l];return p[++c]=46,f&&(p[--t]=45),ah(p,t,b-t+1)}for(o=2;$X(o,pc(Ed(r),1));o++)p[--t]=48;return p[--t]=46,p[--t]=48,f&&(p[--t]=45),ah(p,t,b-t)}return M=t+1,i=b,y=new Y5,f&&(y.a+="-"),i-M>=1?(Ib(y,p[t]),y.a+=".",y.a+=ah(p,t+1,b-t-1)):y.a+=ah(p,t,b-t),y.a+="E",fo(r,0)>0&&(y.a+="+"),y.a+=""+KE(r),y.a}function jVe(e){G2(e,new xw(HP(H2(B2(F2(z2(new od,Fl),"ELK Radial"),'A radial layout provider which is based on the algorithm of Peter Eades published in "Drawing free trees.", published by International Institute for Advanced Study of Social Information Science, Fujitsu Limited in 1991. The radial layouter takes a tree and places the nodes in radial order around the root. The nodes of the same tree level are placed on the same radius.'),new xT),Fl))),Ee(e,Fl,TF,Ie(qsn)),Ee(e,Fl,zp,Ie(Usn)),Ee(e,Fl,cv,Ie(Fsn)),Ee(e,Fl,t6,Ie(Hsn)),Ee(e,Fl,n6,Ie(Jsn)),Ee(e,Fl,D8,Ie(zsn)),Ee(e,Fl,dS,Ie(z6e)),Ee(e,Fl,I8,Ie(Gsn)),Ee(e,Fl,Kee,Ie(Tre)),Ee(e,Fl,Xee,Ie(xre)),Ee(e,Fl,IF,Ie(H6e)),Ee(e,Fl,Vee,Ie(Cre)),Ee(e,Fl,Yee,Ie(J6e)),Ee(e,Fl,Y2e,Ie(G6e)),Ee(e,Fl,V2e,Ie(F6e)),Ee(e,Fl,q2e,Ie(BJ)),Ee(e,Fl,U2e,Ie(zJ)),Ee(e,Fl,X2e,Ie(ED)),Ee(e,Fl,K2e,Ie(q6e)),Ee(e,Fl,G2e,Ie(B6e))}function Iw(e,n,t,i,r){var c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se;if(I=new ke(e.g,e.f),O=C0e(e),O.a=E.Math.max(O.a,n),O.b=E.Math.max(O.b,t),se=O.a/I.a,b=O.b/I.b,ee=O.a-I.a,f=O.b-I.b,i)for(o=zi(e)?u(ve(zi(e),(Ht(),vg)),86):u(ve(e,(Ht(),vg)),86),l=ue(ve(e,(Ht(),RA)))===ue((Rr(),eo)),G=new ct((!e.c&&(e.c=new we(Ps,e,9,9)),e.c));G.e!=G.i.gc();)switch(R=u(lt(G),125),K=u(ve(R,zv),64),K==(Oe(),yu)&&(K=Qbe(R,o),yi(R,zv,K)),K.g){case 1:l||Cs(R,R.i*se);break;case 2:Cs(R,R.i+ee),l||Os(R,R.j*b);break;case 3:l||Cs(R,R.i*se),Os(R,R.j+f);break;case 4:l||Os(R,R.j*b)}if(tw(e,O.a,O.b),r)for(y=new ct((!e.n&&(e.n=new we(ku,e,1,7)),e.n));y.e!=y.i.gc();)p=u(lt(y),157),S=p.i+p.g/2,M=p.j+p.f/2,he=S/I.a,h=M/I.b,he+h>=1&&(he-h>0&&M>=0?(Cs(p,p.i+ee),Os(p,p.j+f*h)):he-h<0&&S>=0&&(Cs(p,p.i+ee*he),Os(p,p.j+f)));return yi(e,(Ht(),yg),(Xs(),c=u(na(UA),10),new Nl(c,u(Tf(c,c.length),10),0))),new ke(se,b)}function Vz(e){var n,t,i,r,c,o,l,f,h,b,p;if(e==null)throw P(new rh(Ko));if(h=e,c=e.length,f=!1,c>0&&(n=(Yn(0,e.length),e.charCodeAt(0)),(n==45||n==43)&&(e=(Yn(1,e.length+1),e.substr(1)),--c,f=n==45)),c==0)throw P(new rh(Pw+h+'"'));for(;e.length>0&&(Yn(0,e.length),e.charCodeAt(0)==48);)e=(Yn(1,e.length+1),e.substr(1)),--c;if(c>(tKe(),Uen)[10])throw P(new rh(Pw+h+'"'));for(r=0;r0&&(p=-parseInt((Yr(0,i,e.length),e.substr(0,i)),10),e=(Yn(i,e.length+1),e.substr(i)),c-=i,t=!1);c>=o;){if(i=parseInt((Yr(0,o,e.length),e.substr(0,o)),10),e=(Yn(o,e.length+1),e.substr(o)),c-=o,t)t=!1;else{if(fo(p,l)<0)throw P(new rh(Pw+h+'"'));p=ac(p,b)}p=uf(p,i)}if(fo(p,0)>0)throw P(new rh(Pw+h+'"'));if(!f&&(p=Ed(p),fo(p,0)<0))throw P(new rh(Pw+h+'"'));return p}function yge(e){KZ();var n,t,i,r,c,o,l,f;if(e==null)return null;if(r=ch(e,Uo(37)),r<0)return e;for(f=new Ws((Yr(0,r,e.length),e.substr(0,r))),n=oe(hs,fv,30,4,15,1),l=0,i=0,o=e.length;rr+2&&XY((Yn(r+1,e.length),e.charCodeAt(r+1)),L8e,P8e)&&XY((Yn(r+2,e.length),e.charCodeAt(r+2)),L8e,P8e))if(t=T3n((Yn(r+1,e.length),e.charCodeAt(r+1)),(Yn(r+2,e.length),e.charCodeAt(r+2))),r+=2,i>0?(t&192)==128?n[l++]=t<<24>>24:i=0:t>=128&&((t&224)==192?(n[l++]=t<<24>>24,i=2):(t&240)==224?(n[l++]=t<<24>>24,i=3):(t&248)==240&&(n[l++]=t<<24>>24,i=4)),i>0){if(l==i){switch(l){case 2:{Ib(f,((n[0]&31)<<6|n[1]&63)&yr);break}case 3:{Ib(f,((n[0]&15)<<12|(n[1]&63)<<6|n[2]&63)&yr);break}}l=0,i=0}}else{for(c=0;c=2){if((!e.a&&(e.a=new we(Pi,e,6,6)),e.a).i==0)t=(a0(),r=new vo,r),kt((!e.a&&(e.a=new we(Pi,e,6,6)),e.a),t);else if((!e.a&&(e.a=new we(Pi,e,6,6)),e.a).i>1)for(y=new u4((!e.a&&(e.a=new we(Pi,e,6,6)),e.a));y.e!=y.i.gc();)Rj(y);Wbe(n,u(X((!e.a&&(e.a=new we(Pi,e,6,6)),e.a),0),170))}if(p)for(i=new ct((!e.a&&(e.a=new we(Pi,e,6,6)),e.a));i.e!=i.i.gc();)for(t=u(lt(i),170),h=new ct((!t.a&&(t.a=new mr(pl,t,5)),t.a));h.e!=h.i.gc();)f=u(lt(h),372),l.a=E.Math.max(l.a,f.a),l.b=E.Math.max(l.b,f.b);for(o=new ct((!e.n&&(e.n=new we(ku,e,1,7)),e.n));o.e!=o.i.gc();)c=u(lt(o),157),b=u(ve(c,zA),8),b&&Cl(c,b.a,b.b),p&&(l.a=E.Math.max(l.a,c.i+c.g),l.b=E.Math.max(l.b,c.j+c.f));return l}function AVe(e,n,t,i,r){var c,o,l;if(pRe(e,n),o=n[0],c=ic(t.c,0),l=-1,d1e(t))if(i>0){if(o+i>e.length)return!1;l=Sz((Yr(0,o+i,e.length),e.substr(0,o+i)),n)}else l=Sz(e,n);switch(c){case 71:return l=V3(e,o,z(B(Re,1),je,2,6,[lYe,fYe]),n),r.e=l,!0;case 77:return EDn(e,n,r,l,o);case 76:return jDn(e,n,r,l,o);case 69:return Axn(e,n,o,r);case 99:return Mxn(e,n,o,r);case 97:return l=V3(e,o,z(B(Re,1),je,2,6,["AM","PM"]),n),r.b=l,!0;case 121:return SDn(e,n,o,l,t,r);case 100:return l<=0?!1:(r.c=l,!0);case 83:return l<0?!1:ijn(l,o,n[0],r);case 104:l==12&&(l=0);case 75:case 72:return l<0?!1:(r.f=l,r.g=!1,!0);case 107:return l<0?!1:(r.f=l,r.g=!0,!0);case 109:return l<0?!1:(r.j=l,!0);case 115:return l<0?!1:(r.n=l,!0);case 90:if(onn[f]&&(I=f),p=new L(e.a.b);p.a=l){ft(G.b>0),G.a.Xb(G.c=--G.b);break}else I.a>f&&(i?(jr(i.b,I.b),i.a=E.Math.max(i.a,I.a),As(G)):(Te(I.b,b),I.c=E.Math.min(I.c,f),I.a=E.Math.max(I.a,l),i=I));i||(i=new tAe,i.c=f,i.a=l,W2(G,i),Te(i.b,b))}for(o=e.b,h=0,R=new L(t);R.a1;){if(r=gNn(n),p=c.g,M=u(ve(n,CA),104),O=W(ie(ve(n,GJ))),(!n.a&&(n.a=new we($t,n,10,11)),n.a).i>1&&W(ie(ve(n,(qh(),Rre))))!=Ki&&(c.c+(M.b+M.c))/(c.b+(M.d+M.a))1&&W(ie(ve(n,(qh(),$re))))!=Ki&&(c.c+(M.b+M.c))/(c.b+(M.d+M.a))>O&&yi(r,(qh(),dm),E.Math.max(W(ie(ve(n,xA))),W(ie(ve(r,dm)))-W(ie(ve(n,$re))))),S=new kse(i,b),f=HVe(S,r,y),h=f.g,h>=p&&h==h){for(o=0;o<(!r.a&&(r.a=new we($t,r,10,11)),r.a).i;o++)gqe(e,u(X((!r.a&&(r.a=new we($t,r,10,11)),r.a),o),26),u(X((!n.a&&(n.a=new we($t,n,10,11)),n.a),o),26));HRe(n,S),s4n(c,f.c),o4n(c,f.b)}--l}yi(n,(qh(),E7),c.b),yi(n,A6,c.c),t.Ug()}function oRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne,nn,en;for(n.Tg("Compound graph postprocessor",1),t=$e(Pe(T(e,(Ce(),$ie)))),l=u(T(e,(pe(),Pve)),229),b=new fr,R=l.ec().Jc();R.Ob();){for(I=u(R.Pb(),17),o=new ds(l.cc(I)),vn(),xr(o,new Uue(e)),he=u7n((pn(0,o.c.length),u(o.c[0],250))),Ne=$Be(u(_e(o,o.c.length-1),250)),K=he.i,L9(Ne.i,K)?G=K.e:G=Ir(K),p=Xjn(I,o),Js(I.a),y=null,c=new L(o);c.avh,en=E.Math.abs(y.b-M.b)>vh,(!t&&nn&&en||t&&(nn||en))&&qt(I.a,ee)),fc(I.a,i),i.b==0?y=ee:y=(ft(i.b!=0),u(i.c.b.c,8)),$7n(S,p,O),$Be(r)==Ne&&(Ir(Ne.i)!=r.a&&(O=new Kr,M0e(O,Ir(Ne.i),G)),fe(I,pie,O)),JTn(S,I,G),b.a.yc(S,b);lc(I,he),Jr(I,Ne)}for(h=b.a.ec().Jc();h.Ob();)f=u(h.Pb(),17),lc(f,null),Jr(f,null);n.Ug()}function sRn(e,n){var t,i,r,c,o,l,f,h,b,p,y;for(r=u(T(e,(Au(),r2)),86),b=r==(vr(),Zc)||r==ru?Ua:ru,t=u(bs(ci(new bn(null,new wn(e.b,16)),new i3),Ts(new Bi,new Ei,new eu,z(B(Yo,1),ye,130,0,[($l(),Vo)]))),16),f=u(bs(jo(t.Mc(),new Mje(n)),Ts(new Bi,new Ei,new eu,z(B(Yo,1),ye,130,0,[Vo]))),16),f.Fc(u(bs(jo(t.Mc(),new Tje(n)),Ts(new Bi,new Ei,new eu,z(B(Yo,1),ye,130,0,[Vo]))),18)),f.gd(new xje(b)),y=new dd(new Cje(r)),i=new gt,l=f.Jc();l.Ob();)o=u(l.Pb(),240),h=u(o.a,40),$e(Pe(o.c))?(y.a.yc(h,(Pn(),U0))==null,new qy(y.a.Xc(h,!1)).a.gc()>0&&Qt(i,h,u(new qy(y.a.Xc(h,!1)).a.Tc(),40)),new qy(y.a.$c(h,!0)).a.gc()>1&&Qt(i,UFe(y,h),h)):(new qy(y.a.Xc(h,!1)).a.gc()>0&&(c=u(new qy(y.a.Xc(h,!1)).a.Tc(),40),ue(c)===ue(hu(Uc(i.f,h)))&&u(T(h,(xi(),sre)),16).Ec(c)),new qy(y.a.$c(h,!0)).a.gc()>1&&(p=UFe(y,h),ue(hu(Uc(i.f,p)))===ue(h)&&u(T(p,(xi(),sre)),16).Ec(h)),y.a.Ac(h)!=null)}function MVe(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee;if(e.gc()==1)return u(e.Xb(0),235);if(e.gc()<=0)return new KR;for(r=e.Jc();r.Ob();){for(t=u(r.Pb(),235),M=0,b=ri,p=ri,f=Ur,h=Ur,S=new L(t.e);S.al&&(K=0,ee+=o+R,o=0),PIn(O,t,K,ee),n=E.Math.max(n,K+I.a),o=E.Math.max(o,I.b),K+=I.a+R;return O}function lRn(e){Ybe();var n,t,i,r,c,o,l,f,h,b,p,y,S,M,O,I;if(e==null||(c=cB(e),M=rEn(c),M%4!=0))return null;if(O=M/4|0,O==0)return oe(hs,fv,30,0,15,1);for(p=null,n=0,t=0,i=0,r=0,o=0,l=0,f=0,h=0,S=0,y=0,b=0,p=oe(hs,fv,30,O*3,15,1);S>4)<<24>>24,p[y++]=((t&15)<<4|i>>2&15)<<24>>24,p[y++]=(i<<6|r)<<24>>24}return!Qx(o=c[b++])||!Qx(l=c[b++])?null:(n=Qa[o],t=Qa[l],f=c[b++],h=c[b++],Qa[f]==-1||Qa[h]==-1?f==61&&h==61?(t&15)!=0?null:(I=oe(hs,fv,30,S*3+1,15,1),Yu(p,0,I,0,S*3),I[y]=(n<<2|t>>4)<<24>>24,I):f!=61&&h==61?(i=Qa[f],(i&3)!=0?null:(I=oe(hs,fv,30,S*3+2,15,1),Yu(p,0,I,0,S*3),I[y++]=(n<<2|t>>4)<<24>>24,I[y]=((t&15)<<4|i>>2&15)<<24>>24,I)):null:(i=Qa[f],r=Qa[h],p[y++]=(n<<2|t>>4)<<24>>24,p[y++]=((t&15)<<4|i>>2&15)<<24>>24,p[y++]=(i<<6|r)<<24>>24,p))}function fRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he;for(n.Tg(gQe,1),M=u(T(e,(Ce(),q1)),222),r=new L(e.b);r.a=2){for(O=!0,y=new L(c.j),t=u(_(y),12),S=null;y.a0)if(i=p.gc(),h=sc(E.Math.floor((i+1)/2))-1,r=sc(E.Math.ceil((i+1)/2))-1,n.o==Ja)for(b=r;b>=h;b--)n.a[ee.p]==ee&&(O=u(p.Xb(b),49),M=u(O.a,9),!ef(t,O.b)&&S>e.b.e[M.p]&&(n.a[M.p]=ee,n.g[ee.p]=n.g[M.p],n.a[ee.p]=n.g[ee.p],n.f[n.g[ee.p].p]=(Pn(),!!($e(n.f[n.g[ee.p].p])&ee.k==(zn(),hr))),S=e.b.e[M.p]));else for(b=h;b<=r;b++)n.a[ee.p]==ee&&(R=u(p.Xb(b),49),I=u(R.a,9),!ef(t,R.b)&&S0&&(r=u(_e(I.c.a,se-1),9),o=e.i[r.p],nn=E.Math.ceil(S3(e.n,r,I)),c=he.a.e-I.d.d-(o.a.e+r.o.b+r.d.a)-nn),h=Ki,se0&&Ne.a.e.e-Ne.a.a-(Ne.b.e.e-Ne.b.a)<0,M=K.a.e.e-K.a.a-(K.b.e.e-K.b.a)<0&&Ne.a.e.e-Ne.a.a-(Ne.b.e.e-Ne.b.a)>0,S=K.a.e.e+K.b.aNe.b.e.e+Ne.a.a,ee=0,!O&&!M&&(y?c+p>0?ee=p:h-i>0&&(ee=i):S&&(c+l>0?ee=l:h-G>0&&(ee=G))),he.a.e+=ee,he.b&&(he.d.e+=ee),!1))}function xVe(e,n,t){var i,r,c,o,l,f,h,b,p,y;if(i=new xf(n.Jf().a,n.Jf().b,n.Kf().a,n.Kf().b),r=new r4,e.c)for(o=new L(n.Pf());o.a0&&Cr(S,(pn(t,n.c.length),u(n.c[t],25))),c=0,y=!0,R=Us($b(rr(S))),f=R.Jc();f.Ob();){for(l=u(f.Pb(),17),y=!1,p=l,h=0;h(pn(h,n.c.length),u(n.c[h],25)).a.c.length?Cr(r,(pn(h,n.c.length),u(n.c[h],25))):D0(r,i+c,(pn(h,n.c.length),u(n.c[h],25))),p=SZ(p,r);t>0&&(c+=1)}if(y){for(h=0;h(pn(h,n.c.length),u(n.c[h],25)).a.c.length?Cr(r,(pn(h,n.c.length),u(n.c[h],25))):D0(r,i+c,(pn(h,n.c.length),u(n.c[h],25)));t>0&&(c+=1)}for(o=!1,O=new qn(Vn(Ni(S).a.Jc(),new ne));at(O);){for(M=u(tt(O),17),p=M,b=t+1;b(pn(h,n.c.length),u(n.c[h],25)).a.c.length?Cr(I,(pn(h,n.c.length),u(n.c[h],25))):D0(I,i+1,(pn(h,n.c.length),u(n.c[h],25))));o&&(c+=1),o=!0}return c>0?c-1:0}function $0(e,n){si();var t,i,r,c,o,l,f,h,b,p,y,S,M;if(gE(_7)==0){for(p=oe(JBn,je,121,dhn.length,0,1),o=0;oh&&(i.a+=_Ce(oe(Vl,ph,30,-h,15,1))),i.a+="Is",ch(f,Uo(32))>=0)for(r=0;r=i.o.b/2}else G=!p;G?(R=u(T(i,(pe(),k6)),16),R?y?c=R:(r=u(T(i,b6),16),r?R.gc()<=r.gc()?c=R:c=r:(c=new xe,fe(i,b6,c))):(c=new xe,fe(i,k6,c))):(r=u(T(i,(pe(),b6)),16),r?p?c=r:(R=u(T(i,k6),16),R?r.gc()<=R.gc()?c=r:c=R:(c=new xe,fe(i,k6,c))):(c=new xe,fe(i,b6,c))),c.Ec(e),fe(e,(pe(),ZH),t),n.d==t?(Jr(n,null),t.e.c.length+t.g.c.length==0&&bu(t,null),fkn(t)):(lc(n,null),t.e.c.length+t.g.c.length==0&&bu(t,null)),Js(n.a)}function gRn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne,nn,en,Tn,On,ot,Xt,qi;for(t.Tg("MinWidth layering",1),S=n.b,Ne=n.a,qi=u(T(n,(Ce(),U5e)),15).a,l=u(T(n,X5e),15).a,e.b=W(ie(T(n,Hf))),e.d=Ki,ee=new L(Ne);ee.aS&&(c&&(bc(se,y),bc(nn,me(h.b-1))),Xt=t.b,qi+=y+n,y=0,b=E.Math.max(b,t.b+t.c+ot)),Cs(l,Xt),Os(l,qi),b=E.Math.max(b,Xt+ot+t.c),y=E.Math.max(y,p),Xt+=ot+n;if(b=E.Math.max(b,i),On=qi+y+t.a,On0?(h=0,I&&(h+=l),h+=(en-1)*o,K&&(h+=l),nn&&K&&(h=E.Math.max(h,LNn(K,o,G,Ne))),h=e.a&&(i=Q_n(e,G),b=E.Math.max(b,i.b),ee=E.Math.max(ee,i.d),Te(l,new yc(G,i)));for(nn=new xe,h=0;h0),I.a.Xb(I.c=--I.b),en=new qu(e.b),W2(I,en),ft(I.b0){for(y=b<100?null:new f0(b),h=new Xhe(n),M=h.g,R=oe(It,Zt,30,b,15,1),i=0,ee=new pw(b),r=0;r=0;)if(S!=null?ai(S,M[f]):ue(S)===ue(M[f])){R.length<=i&&(I=R,R=oe(It,Zt,30,2*R.length,15,1),Yu(I,0,R,0,i)),R[i++]=r,kt(ee,M[f]);break e}if(S=S,ue(S)===ue(l))break}}if(h=ee,M=ee.g,b=i,i>R.length&&(I=R,R=oe(It,Zt,30,i,15,1),Yu(I,0,R,0,i)),i>0){for(K=!0,c=0;c=0;)R4(e,R[o]);if(i!=b){for(r=b;--r>=i;)R4(h,r);I=R,R=oe(It,Zt,30,i,15,1),Yu(I,0,R,0,i)}n=h}}}else for(n=WSn(e,n),r=e.i;--r>=0;)n.Gc(e.g[r])&&(R4(e,r),K=!0);if(K){if(R!=null){for(t=n.gc(),p=t==1?ij(e,4,n.Jc().Pb(),null,R[0],O):ij(e,6,n,R,R[0],O),y=t<100?null:new f0(t),r=n.Jc();r.Ob();)S=r.Pb(),y=Ple(e,u(S,75),y);y?(y.lj(p),y.mj()):fi(e.e,p)}else{for(y=spn(n.gc()),r=n.Jc();r.Ob();)S=r.Pb(),y=Ple(e,u(S,75),y);y&&y.mj()}return!0}else return!1}function yRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K;for(t=new $He(n),t.a||XIn(n),h=qDn(n),f=new bw,I=new KUe,O=new L(n.a);O.a0||t.o==Ja&&r=t}function ERn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne,nn,en,Tn;for(K=e.a,ee=0,he=K.length;ee0?(p=u(_e(y.c.a,o-1),9),nn=S3(e.b,y,p),I=y.n.b-y.d.d-(p.n.b+p.o.b+p.d.a+nn)):I=y.n.b-y.d.d,h=E.Math.min(I,h),o1&&(o=E.Math.min(o,E.Math.abs(u(Ku(l.a,1),8).b-b.b)))));else for(O=new L(n.j);O.ar&&(c=y.a-r,o=ri,i.c.length=0,r=y.a),y.a>=r&&(Jn(i.c,l),l.a.b>1&&(o=E.Math.min(o,E.Math.abs(u(Ku(l.a,l.a.b-2),8).b-y.b)))));if(i.c.length!=0&&c>n.o.a/2&&o>n.o.b/2){for(S=new Vu,bu(S,n),Ar(S,(Oe(),Xn)),S.n.a=n.o.a/2,R=new Vu,bu(R,n),Ar(R,dt),R.n.a=n.o.a/2,R.n.b=n.o.b,f=new L(i);f.a=h.b?lc(l,R):lc(l,S)):(h=u(w3n(l.a),8),I=l.a.b==0?Ta(l.c):u(Mf(l.a),8),I.b>=h.b?Jr(l,R):Jr(l,S)),p=u(T(l,(Ce(),Qc)),78),p&&Ep(p,h,!0);n.n.a=r-n.o.a/2}}function ARn(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(l=Et(e.b,0);l.b!=l.d.c;)if(o=u(yt(l),40),!hn(o.c,OF))for(h=WCn(o,e),n==(vr(),Zc)||n==ru?xr(h,new y_):xr(h,new Qq),f=h.c.length,i=0;i=0?S=_4(l):S=TO(_4(l)),e.of(b7,S)),h=new Kr,y=!1,e.nf(t2)?(ole(h,u(e.mf(t2),8)),y=!0):Fwn(h,o.a/2,o.b/2),S.g){case 4:fe(b,vu,(qs(),G1)),fe(b,eJ,(Gb(),vv)),b.o.b=o.b,O<0&&(b.o.a=-O),Ar(p,(Oe(),Wn)),y||(h.a=o.a),h.a-=o.a;break;case 2:fe(b,vu,(qs(),hg)),fe(b,eJ,(Gb(),o7)),b.o.b=o.b,O<0&&(b.o.a=-O),Ar(p,(Oe(),Kn)),y||(h.a=0);break;case 1:fe(b,fg,(C1(),kv)),b.o.a=o.a,O<0&&(b.o.b=-O),Ar(p,(Oe(),dt)),y||(h.b=o.b),h.b-=o.b;break;case 3:fe(b,fg,(C1(),d6)),b.o.a=o.a,O<0&&(b.o.b=-O),Ar(p,(Oe(),Xn)),y||(h.b=0)}if(ole(p.n,h),fe(b,t2,h),n==kg||n==c1||n==eo){if(M=0,n==kg&&e.nf(Bd))switch(S.g){case 1:case 2:M=u(e.mf(Bd),15).a;break;case 3:case 4:M=-u(e.mf(Bd),15).a}else switch(S.g){case 4:case 2:M=c.b,n==c1&&(M/=r.b);break;case 1:case 3:M=c.a,n==c1&&(M/=r.a)}fe(b,Zw,M)}return fe(b,Ou,S),b}function MRn(){Noe();function e(i){var r=this;this.dispatch=function(c){var o=c.data;switch(o.cmd){case"algorithms":var l=nde((vn(),new Hr(new rt(jg.b))));i.postMessage({id:o.id,data:l});break;case"categories":var f=nde((vn(),new Hr(new rt(jg.c))));i.postMessage({id:o.id,data:f});break;case"options":var h=nde((vn(),new Hr(new rt(jg.d))));i.postMessage({id:o.id,data:h});break;case"register":WLn(o.algorithms),i.postMessage({id:o.id});break;case"layout":ULn(o.graph,o.layoutOptions||{},o.options||{}),i.postMessage({id:o.id,data:o.graph});break}},this.saveDispatch=function(c){try{r.dispatch(c)}catch(o){i.postMessage({id:c.data.id,error:o})}}}function n(i){var r=this;this.dispatcher=new e({postMessage:function(c){r.onmessage({data:c})}}),this.postMessage=function(c){setTimeout(function(){r.dispatcher.saveDispatch({data:c})},0)}}if(typeof document===BW&&typeof self!==BW){var t=new e(self);self.onmessage=t.saveDispatch}else typeof x!==BW&&x.exports&&(Object.defineProperty(D,"__esModule",{value:!0}),x.exports={default:n,Worker:n})}function nW(e,n,t,i,r,c,o){var l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne,nn,en,Tn,On,ot,Xt,qi;for(O=0,Tn=0,h=new L(e.b);h.aO&&(c&&(bc(se,S),bc(nn,me(b.b-1)),Te(e.d,M),l.c.length=0),Xt=t.b,qi+=S+n,S=0,p=E.Math.max(p,t.b+t.c+ot)),Jn(l.c,f),NHe(f,Xt,qi),p=E.Math.max(p,Xt+ot+t.c),S=E.Math.max(S,y),Xt+=ot+n,M=f;if(jr(e.a,l),Te(e.d,u(_e(l,l.c.length-1),167)),p=E.Math.max(p,i),On=qi+S+t.a,Onr.d.d+r.d.a?b.f.d=!0:(b.f.d=!0,b.f.a=!0))),i.b!=i.d.c&&(n=t);b&&(c=u(Bn(e.f,o.d.i),60),n.bc.d.d+c.d.a?b.f.d=!0:(b.f.d=!0,b.f.a=!0))}for(l=new qn(Vn(rr(S).a.Jc(),new ne));at(l);)o=u(tt(l),17),o.a.b!=0&&(n=u(Mf(o.a),8),o.d.j==(Oe(),Xn)&&(I=new Zj(n,new ke(n.a,r.d.d),r,o),I.f.a=!0,I.a=o.d,Jn(O.c,I)),o.d.j==dt&&(I=new Zj(n,new ke(n.a,r.d.d+r.d.a),r,o),I.f.d=!0,I.a=o.d,Jn(O.c,I)))}return O}function DRn(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(f=new xe,p=n.length,o=r1e(t),h=0;h=M&&(G>M&&(S.c.length=0,M=G),Jn(S.c,o));S.c.length!=0&&(y=u(_e(S,uz(n,S.c.length)),132),On.a.Ac(y)!=null,y.s=O++,lbe(y,en,se),S.c.length=0)}for(ee=e.c.length+1,l=new L(e);l.aTn.s&&(As(t),Go(Tn.i,i),i.c>0&&(i.a=Tn,Te(Tn.t,i),i.b=Ne,Te(Ne.i,i)))}function _Ve(e,n,t,i,r){var c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne,nn,en,Tn,On;for(O=new So(n.b),ee=new So(n.b),y=new So(n.b),nn=new So(n.b),I=new So(n.b),Ne=Et(n,0);Ne.b!=Ne.d.c;)for(he=u(yt(Ne),12),l=new L(he.g);l.a0,R=he.g.c.length>0,h&&R?Jn(y.c,he):h?Jn(O.c,he):R&&Jn(ee.c,he);for(M=new L(O);M.aG.mh()-h.b&&(y=G.mh()-h.b),S>G.nh()-h.d&&(S=G.nh()-h.d),b0){for(K=Et(e.f,0);K.b!=K.d.c;)G=u(yt(K),9),G.p+=y-e.e;A0e(e),Js(e.f),Sbe(e,i,S)}else{for(qt(e.f,S),S.p=i,e.e=E.Math.max(e.e,i),c=new qn(Vn(rr(S).a.Jc(),new ne));at(c);)r=u(tt(c),17),!r.c.i.c&&r.c.i.k==(zn(),Gu)&&(qt(e.f,r.c.i),r.c.i.p=i-1);e.c=i}else A0e(e),Js(e.f),i=0,at(new qn(Vn(rr(S).a.Jc(),new ne)))?(y=0,y=LHe(y,S),i=y+2,Sbe(e,i,S)):(qt(e.f,S),S.p=0,e.e=E.Math.max(e.e,0),e.b=u(_e(e.d.b,0),25),e.c=0);for(e.f.b==0||A0e(e),e.d.a.c.length=0,R=new xe,h=new L(e.d.b);h.a=48&&n<=57){for(i=n-48;r=48&&n<=57;)if(i=i*10+n-48,i<0)throw P(new Lt(Rt((Nt(),Gpe))))}else throw P(new Lt(Rt((Nt(),jWe))));if(t=i,n==44){if(r>=e.j)throw P(new Lt(Rt((Nt(),AWe))));if((n=ic(e.i,r++))>=48&&n<=57){for(t=n-48;r=48&&n<=57;)if(t=t*10+n-48,t<0)throw P(new Lt(Rt((Nt(),Gpe))));if(i>t)throw P(new Lt(Rt((Nt(),MWe))))}else t=-1}if(n!=125)throw P(new Lt(Rt((Nt(),SWe))));e._l(r)?(c=(si(),si(),new hp(9,c)),e.d=r+1):(c=(si(),si(),new hp(3,c)),e.d=r),c.Mm(i),c.Lm(t),ui(e)}}return c}function RRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he;for(r=1,S=new xe,i=0;i=u(_e(e.b,i),25).a.c.length/4)continue}if(u(_e(e.b,i),25).a.c.length>n){for(ee=new xe,Te(ee,u(_e(e.b,i),25)),o=0;o1)for(M=new u4((!e.a&&(e.a=new we(Pi,e,6,6)),e.a));M.e!=M.i.gc();)Rj(M);for(o=u(X((!e.a&&(e.a=new we(Pi,e,6,6)),e.a),0),170),I=Xt,Xt>he+ee?I=he+ee:Xtse+O?R=se+O:qihe-ee&&Ise-O&&RXt+ot?nn=Xt+ot:heqi+Ne?en=qi+Ne:seXt-ot&&nnqi-Ne&&ent&&(y=t-1),S=Qd+Ds(n,24)*pN*p-p/2,S<0?S=1:S>i&&(S=i-1),r=(a0(),f=new Nk,f),hB(r,y),dB(r,S),kt((!o.a&&(o.a=new mr(pl,o,5)),o.a),r)}function rW(e,n){HZ();var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne;if(K=e.e,b=e.d,r=e.a,K==0)switch(n){case 0:return"0";case 1:return S8;case 2:return"0.00";case 3:return"0.000";case 4:return"0.0000";case 5:return"0.00000";case 6:return"0.000000";default:return R=new l0,R.a+="0E",R.a+=-n,R.a}if(O=b*10+1+7,I=oe(Vl,ph,30,O+1,15,1),t=O,b==1)if(c=r[0],c<0){Ne=$r(c,Nc);do p=Ne,Ne=$O(Ne,10),I[--t]=48+_t(uf(p,ac(Ne,10)))&yr;while(fo(Ne,0)!=0)}else{Ne=c;do p=Ne,Ne=Ne/10|0,I[--t]=48+(p-Ne*10)&yr;while(Ne!=0)}else{ee=oe(It,Zt,30,b,15,1),se=b,Yu(r,0,ee,0,se);e:for(;;){for(G=0,l=se-1;l>=0;l--)he=pc(Bh(G,32),$r(ee[l],Nc)),S=GMn(he),ee[l]=_t(S),G=_t(ow(S,32));M=_t(G),y=t;do I[--t]=48+M%10&yr;while((M=M/10|0)!=0&&t!=0);for(i=9-y+t,o=0;o0;o++)I[--t]=48;for(f=se-1;ee[f]==0;f--)if(f==0)break e;se=f+1}for(;I[t]==48;)++t}return h=K<0,h&&(I[--t]=45),ah(I,t,O-t)}function RVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se;switch(e.c=n,e.g=new gt,t=(Mb(),new s0(e.c)),i=new MP(t),Z1e(i),K=Dt(ve(e.c,(BO(),Pye))),f=u(ve(e.c,Wre),330),he=u(ve(e.c,ece),427),o=u(ve(e.c,Iye),477),ee=u(ve(e.c,Zre),428),e.j=W(ie(ve(e.c,Lln))),l=e.a,f.g){case 0:l=e.a;break;case 1:l=e.b;break;case 2:l=e.i;break;case 3:l=e.e;break;case 4:l=e.f;break;default:throw P(new Gn(LF+(f.f!=null?f.f:""+f.g)))}if(e.d=new k_e(l,he,o),fe(e.d,($9(),FS),Pe(ve(e.c,Iln))),e.d.c=$e(Pe(ve(e.c,_ye))),MR(e.c).i==0)return e.d;for(p=new ct(MR(e.c));p.e!=p.i.gc();){for(b=u(lt(p),26),S=b.g/2,y=b.f/2,se=new ke(b.i+S,b.j+y);oo(e.g,se);)Q2(se,(E.Math.random()-.5)*vh,(E.Math.random()-.5)*vh);O=u(ve(b,(Ht(),S7)),140),I=new $_e(se,new xf(se.a-S-e.j/2-O.b,se.b-y-e.j/2-O.d,b.g+e.j+(O.b+O.c),b.f+e.j+(O.d+O.a))),Te(e.d.i,I),Qt(e.g,se,new yc(I,b))}switch(ee.g){case 0:if(K==null)e.d.d=u(_e(e.d.i,0),68);else for(G=new L(e.d.i);G.a0?ot+1:1);for(o=new L(se.g);o.a0?ot+1:1)}e.d[h]==0?qt(e.f,O):e.a[h]==0&&qt(e.g,O),++h}for(M=-1,S=1,p=new xe,e.e=u(T(n,(pe(),v6)),234);ml>0;){for(;e.f.b!=0;)qi=u(VK(e.f),9),e.c[qi.p]=M--,Fbe(e,qi),--ml;for(;e.g.b!=0;)Es=u(VK(e.g),9),e.c[Es.p]=S++,Fbe(e,Es),--ml;if(ml>0){for(y=Ur,G=new L(K);G.a=y&&(ee>y&&(p.c.length=0,y=ee),Jn(p.c,O)));b=e.qg(p),e.c[b.p]=S++,Fbe(e,b),--ml}}for(Xt=K.c.length+1,h=0;he.c[Wc]&&(Nd(i,!0),fe(n,g6,(Pn(),!0)));e.a=null,e.d=null,e.c=null,Js(e.g),Js(e.f),t.Ug()}function zVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se;for(he=u(X((!e.a&&(e.a=new we(Pi,e,6,6)),e.a),0),170),b=new Ss,ee=new gt,se=WXe(he),Xo(ee.f,he,se),y=new gt,i=new ji,M=zh(Ll(z(B(Gl,1),xn,20,0,[(!n.d&&(n.d=new Cn(wr,n,8,5)),n.d),(!n.e&&(n.e=new Cn(wr,n,7,4)),n.e)])));at(M);){if(S=u(tt(M),85),(!e.a&&(e.a=new we(Pi,e,6,6)),e.a).i!=1)throw P(new Gn(NZe+(!e.a&&(e.a=new we(Pi,e,6,6)),e.a).i));S!=e&&(I=u(X((!S.a&&(S.a=new we(Pi,S,6,6)),S.a),0),170),Xi(i,I,i.c.b,i.c),O=u(hu(Uc(ee.f,I)),13),O||(O=WXe(I),Xo(ee.f,I,O)),p=t?Or(new gc(u(_e(se,se.c.length-1),8)),u(_e(O,O.c.length-1),8)):Or(new gc((pn(0,se.c.length),u(se.c[0],8))),(pn(0,O.c.length),u(O.c[0],8))),Xo(y.f,I,p))}if(i.b!=0)for(R=u(_e(se,t?se.c.length-1:0),8),h=1;h1&&Xi(b,R,b.c.b,b.c),SY(r)));R=G}return b}function FVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne,nn,en,Tn;for(t.Tg(HQe,1),Tn=u(bs(ci(new bn(null,new wn(n,16)),new E_),Ts(new Bi,new Ei,new eu,z(B(Yo,1),ye,130,0,[($l(),Vo)]))),16),b=u(bs(ci(new bn(null,new wn(n,16)),new Nje(n)),Ts(new Bi,new Ei,new eu,z(B(Yo,1),ye,130,0,[Vo]))),16),M=u(bs(ci(new bn(null,new wn(n,16)),new Oje(n)),Ts(new Bi,new Ei,new eu,z(B(Yo,1),ye,130,0,[Vo]))),16),O=oe(DJ,NF,40,n.gc(),0,1),o=0;o=0&&en=0&&!O[S]){O[S]=r,b.ed(l),--l;break}if(S=en-y,S=0&&!O[S]){O[S]=r,b.ed(l),--l;break}}for(M.gd(new MT),f=O.length-1;f>=0;f--)!O[f]&&!M.dc()&&(O[f]=u(M.Xb(0),40),M.ed(0));for(h=0;hy&&LO((pn(y,n.c.length),u(n.c[y],186)),b),b=null;n.c.length>y&&(pn(y,n.c.length),u(n.c[y],186)).a.c.length==0;)Go(n,(pn(y,n.c.length),n.c[y]));if(!b){--o;continue}if(!$e(Pe(u(_e(b.b,0),26).mf((La(),SD))))&&u_n(n,M,c,b,I,t,y,i)){O=!0;continue}if(I){if(S=M.b,p=b.f,!$e(Pe(u(_e(b.b,0),26).mf(SD)))&&TPn(n,M,c,b,t,y,i,r)){if(O=!0,S=e.j){e.a=-1,e.c=1;return}if(n=ic(e.i,e.d++),e.a=n,e.b==1){switch(n){case 92:if(i=10,e.d>=e.j)throw P(new Lt(Rt((Nt(),JF))));e.a=ic(e.i,e.d++);break;case 45:(e.e&512)==512&&e.d=e.j||ic(e.i,e.d)!=63)break;if(++e.d>=e.j)throw P(new Lt(Rt((Nt(),Ane))));switch(n=ic(e.i,e.d++),n){case 58:i=13;break;case 61:i=14;break;case 33:i=15;break;case 91:i=19;break;case 62:i=18;break;case 60:if(e.d>=e.j)throw P(new Lt(Rt((Nt(),Ane))));if(n=ic(e.i,e.d++),n==61)i=16;else if(n==33)i=17;else throw P(new Lt(Rt((Nt(),cWe))));break;case 35:for(;e.d=e.j)throw P(new Lt(Rt((Nt(),JF))));e.a=ic(e.i,e.d++);break;default:i=0}e.c=i}function KRn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I;if(t.Tg("Process compaction",1),!!$e(Pe(T(n,(Au(),g6e))))){for(r=u(T(n,r2),86),S=W(ie(T(n,wre))),vLn(e,n,r),sRn(n,S/2/2),M=n.b,Fb(M,new jje(r)),h=Et(M,0);h.b!=h.d.c;)if(f=u(yt(h),40),!$e(Pe(T(f,(xi(),tb))))){if(i=UDn(f,r),O=F_n(f,n),p=0,y=0,i)switch(I=i.e,r.g){case 2:p=I.a-S-f.f.a,O.e.a-S-f.f.ap&&(p=O.e.a+O.f.a+S),y=p+f.f.a;break;case 4:p=I.b-S-f.f.b,O.e.b-S-f.f.bp&&(p=O.e.b+O.f.b+S),y=p+f.f.b}else if(O)switch(r.g){case 2:p=O.e.a-S-f.f.a,y=p+f.f.a;break;case 1:p=O.e.a+O.f.a+S,y=p+f.f.a;break;case 4:p=O.e.b-S-f.f.b,y=p+f.f.b;break;case 3:p=O.e.b+O.f.b+S,y=p+f.f.b}ue(T(n,gre))===ue((kj(),mD))?(c=p,o=y,l=I1(ci(new bn(null,new wn(e.a,16)),new cxe(c,o))),l.a!=null?r==(vr(),Zc)||r==ru?f.e.a=p:f.e.b=p:(r==(vr(),Zc)||r==Ul?l=I1(ci(qRe(new bn(null,new wn(e.a,16))),new Sje(c))):l=I1(ci(qRe(new bn(null,new wn(e.a,16))),new Aje(c))),l.a!=null&&(r==Zc||r==ru?f.e.a=W(ie((ft(l.a!=null),u(l.a,49)).a)):f.e.b=W(ie((ft(l.a!=null),u(l.a,49)).a)))),l.a!=null&&(b=gu(e.a,(ft(l.a!=null),l.a),0),b>0&&b!=u(T(f,Th),15).a&&(fe(f,o6e,(Pn(),!0)),fe(f,Th,me(b))))):r==(vr(),Zc)||r==ru?f.e.a=p:f.e.b=p}t.Ug()}}function VRn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he;if(t.Tg("Coffman-Graham Layering",1),n.a.c.length==0){t.Ug();return}for(he=u(T(n,(Ce(),q5e)),15).a,f=0,o=0,y=new L(n.a);y.a=he||!fjn(R,i))&&(i=xIe(n,b)),Cr(R,i),c=new qn(Vn(rr(R).a.Jc(),new ne));at(c);)r=u(tt(c),17),!e.a[r.p]&&(O=r.c.i,--e.e[O.p],e.e[O.p]==0&&h4(c8(S,O),A8));for(h=b.c.length-1;h>=0;--h)Te(n.b,(pn(h,b.c.length),u(b.c[h],25)));n.a.c.length=0,t.Ug()}function JVe(e){var n,t,i,r,c,o,l,f,h;for(e.b=1,ui(e),n=null,e.c==0&&e.a==94?(ui(e),n=(si(),si(),new tl(4)),ao(n,0,V8),l=new tl(4)):l=(si(),si(),new tl(4)),r=!0;(h=e.c)!=1;){if(h==0&&e.a==93&&!r){n&&(iS(n,l),l=n);break}if(t=e.a,i=!1,h==10)switch(t){case 100:case 68:case 119:case 87:case 115:case 83:Lp(l,b8(t)),i=!0;break;case 105:case 73:case 99:case 67:t=(Lp(l,b8(t)),-1),t<0&&(i=!0);break;case 112:case 80:if(f=H0e(e,t),!f)throw P(new Lt(Rt((Nt(),Mne))));Lp(l,f),i=!0;break;default:t=Mbe(e)}else if(h==24&&!r){if(n&&(iS(n,l),l=n),c=JVe(e),iS(l,c),e.c!=0||e.a!=93)throw P(new Lt(Rt((Nt(),gWe))));break}if(ui(e),!i){if(h==0){if(t==91)throw P(new Lt(Rt((Nt(),Hpe))));if(t==93)throw P(new Lt(Rt((Nt(),Jpe))));if(t==45&&!r&&e.a!=93)throw P(new Lt(Rt((Nt(),Tne))))}if(e.c!=0||e.a!=45||t==45&&r)ao(l,t,t);else{if(ui(e),(h=e.c)==1)throw P(new Lt(Rt((Nt(),GF))));if(h==0&&e.a==93)ao(l,t,t),ao(l,45,45);else{if(h==0&&e.a==93||h==24)throw P(new Lt(Rt((Nt(),Tne))));if(o=e.a,h==0){if(o==91)throw P(new Lt(Rt((Nt(),Hpe))));if(o==93)throw P(new Lt(Rt((Nt(),Jpe))));if(o==45)throw P(new Lt(Rt((Nt(),Tne))))}else h==10&&(o=Mbe(e));if(ui(e),t>o)throw P(new Lt(Rt((Nt(),mWe))));ao(l,t,o)}}}r=!1}if(e.c==1)throw P(new Lt(Rt((Nt(),GF))));return Y3(l),nS(l),e.b=0,ui(e),l}function GVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee;ee=!1;do for(ee=!1,c=n?new et(e.a.b).a.gc()-2:1;n?c>=0:cu(T(I,Ci),15).a)&&(K=!1);if(K){for(f=n?c+1:c-1,l=Tae(e.a,me(f)),o=!1,G=!0,i=!1,b=Et(l,0);b.b!=b.d.c;)h=u(yt(b),9),di(h,Ci)?h.p!=p.p&&(o=o|(n?u(T(h,Ci),15).au(T(p,Ci),15).a),G=!1):!o&&G&&h.k==(zn(),Gu)&&(i=!0,n?y=u(tt(new qn(Vn(rr(h).a.Jc(),new ne))),17).c.i:y=u(tt(new qn(Vn(Ni(h).a.Jc(),new ne))),17).d.i,y==p&&(n?t=u(tt(new qn(Vn(Ni(h).a.Jc(),new ne))),17).d.i:t=u(tt(new qn(Vn(rr(h).a.Jc(),new ne))),17).c.i,(n?u(Y2(e.a,t),15).a-u(Y2(e.a,y),15).a:u(Y2(e.a,y),15).a-u(Y2(e.a,t),15).a)<=2&&(G=!1)));if(i&&G&&(n?t=u(tt(new qn(Vn(Ni(p).a.Jc(),new ne))),17).d.i:t=u(tt(new qn(Vn(rr(p).a.Jc(),new ne))),17).c.i,(n?u(Y2(e.a,t),15).a-u(Y2(e.a,p),15).a:u(Y2(e.a,p),15).a-u(Y2(e.a,t),15).a)<=2&&t.k==(zn(),Qi)&&(G=!1)),o||G){for(O=AUe(e,p,n);O.a.gc()!=0;)M=u(O.a.ec().Jc().Pb(),9),O.a.Ac(M)!=null,fc(O,AUe(e,M,n));--S,ee=!0}}}while(ee)}function YRn(e){St(e.c,zt,z(B(Re,1),je,2,6,[oc,"http://www.w3.org/2001/XMLSchema#decimal"])),St(e.d,zt,z(B(Re,1),je,2,6,[oc,"http://www.w3.org/2001/XMLSchema#integer"])),St(e.e,zt,z(B(Re,1),je,2,6,[oc,"http://www.w3.org/2001/XMLSchema#boolean"])),St(e.f,zt,z(B(Re,1),je,2,6,[oc,"EBoolean",ii,"EBoolean:Object"])),St(e.i,zt,z(B(Re,1),je,2,6,[oc,"http://www.w3.org/2001/XMLSchema#byte"])),St(e.g,zt,z(B(Re,1),je,2,6,[oc,"http://www.w3.org/2001/XMLSchema#hexBinary"])),St(e.j,zt,z(B(Re,1),je,2,6,[oc,"EByte",ii,"EByte:Object"])),St(e.n,zt,z(B(Re,1),je,2,6,[oc,"EChar",ii,"EChar:Object"])),St(e.t,zt,z(B(Re,1),je,2,6,[oc,"http://www.w3.org/2001/XMLSchema#double"])),St(e.u,zt,z(B(Re,1),je,2,6,[oc,"EDouble",ii,"EDouble:Object"])),St(e.F,zt,z(B(Re,1),je,2,6,[oc,"http://www.w3.org/2001/XMLSchema#float"])),St(e.G,zt,z(B(Re,1),je,2,6,[oc,"EFloat",ii,"EFloat:Object"])),St(e.I,zt,z(B(Re,1),je,2,6,[oc,"http://www.w3.org/2001/XMLSchema#int"])),St(e.J,zt,z(B(Re,1),je,2,6,[oc,"EInt",ii,"EInt:Object"])),St(e.N,zt,z(B(Re,1),je,2,6,[oc,"http://www.w3.org/2001/XMLSchema#long"])),St(e.O,zt,z(B(Re,1),je,2,6,[oc,"ELong",ii,"ELong:Object"])),St(e.Z,zt,z(B(Re,1),je,2,6,[oc,"http://www.w3.org/2001/XMLSchema#short"])),St(e.$,zt,z(B(Re,1),je,2,6,[oc,"EShort",ii,"EShort:Object"])),St(e._,zt,z(B(Re,1),je,2,6,[oc,"http://www.w3.org/2001/XMLSchema#string"]))}function Ce(){Ce=Y,_ie=(Ht(),Sfn),o4e=Afn,aD=Mfn,Hf=Tfn,xv=P9e,wg=$9e,lm=R9e,w7=B9e,p7=z9e,Lie=nG,pg=Jd,Pie=xfn,fA=J9e,mJ=N6,fD=(Sge(),Icn),sm=_cn,Z0=Lcn,fm=Pcn,Eun=new Vr(_D,me(0)),g7=Ocn,u4e=Ncn,S6=Dcn,w4e=run,l4e=Bcn,f4e=Hcn,Rie=Vcn,a4e=qcn,h4e=Xcn,vJ=sun,Bie=cun,b4e=eun,d4e=Zcn,g4e=tun,V5e=hcn,Oie=scn,dJ=ocn,Nie=fcn,n2=Scn,lA=Acn,xie=Prn,R5e=Rrn,Tun=M7,xun=tG,Mun=vm,Aun=A7,s4e=(I4(),Em),new Vr(D6,s4e),n4e=new iw(12),e4e=new Vr(t1,n4e),L5e=(L1(),C7),q1=new Vr(d9e,L5e),cm=new Vr(Ls,0),jun=new Vr(mce,me(1)),rJ=new Vr(j7,C8),gg=eG,Zi=RA,b7=zv,gun=OD,Ah=afn,tm=Pv,Sun=new Vr(vce,(Pn(),!0)),im=ND,dg=fce,bg=yg,pJ=ib,Iie=wm,_5e=(vr(),Xa),dl=new Vr(vg,_5e),e2=Rv,gJ=k9e,um=pm,kun=pce,r4e=_9e,i4e=(G3(),BD),new Vr(C9e,i4e),mun=dce,vun=bce,yun=gce,pun=hce,$ie=Rcn,hJ=ucn,lD=ccn,aA=$cn,vu=Zrn,j6=xrn,uA=Trn,h7=hrn,N5e=drn,Aie=prn,sD=brn,Mie=Arn,Y5e=dcn,Q5e=bcn,G5e=Urn,wJ=xcn,Die=pcn,Cie=Frn,W5e=Ecn,$5e=_rn,Tie=Lrn,Sie=CD,Z5e=gcn,uJ=Kin,T5e=Xin,cJ=Uin,F5e=Grn,z5e=Jrn,H5e=qrn,d7=Bv,Qc=$v,Rd=gfn,Mh=lce,Tv=sce,D5e=vrn,Bd=wce,tA=bfn,aJ=pfn,t2=N9e,t4e=yfn,rm=kfn,U5e=ecn,X5e=tcn,om=O6,yie=qin,K5e=rcn,fJ=Nrn,lJ=Orn,bJ=S7,q5e=Vrn,sA=vcn,hD=F9e,I5e=Crn,c4e=Ccn,P5e=Drn,hun=krn,dun=Ern,wun=Qrn,bun=jrn,J5e=ace,oA=Wrn,sJ=Srn,n1=arn,Eie=srn,oD=Yin,kie=Qin,oJ=lrn,iA=Vin,jie=frn,nm=orn,cA=urn,aun=crn,E6=Zin,rA=rrn,O5e=irn,x5e=Win,C5e=nrn,B5e=Hrn}function QRn(e,n,t,i,r,c,o){var l,f,h,b,p,y,S,M;return y=u(i.a,15).a,S=u(i.b,15).a,p=e.b,M=e.c,l=0,b=0,n==(vr(),Zc)||n==ru?(b=iC(AFe(op(jo(new bn(null,new wn(t.b,16)),new T_),new jT))),p.e.b+p.f.b/2>b?(h=++S,l=W(ie(zs(ip(jo(new bn(null,new wn(t.b,16)),new sxe(r,h)),new Jg))))):(f=++y,l=W(ie(zs(b4(jo(new bn(null,new wn(t.b,16)),new lxe(r,f)),new Ek)))))):(b=iC(AFe(op(jo(new bn(null,new wn(t.b,16)),new v_),new vy))),p.e.a+p.f.a/2>b?(h=++S,l=W(ie(zs(ip(jo(new bn(null,new wn(t.b,16)),new oxe(r,h)),new ST))))):(f=++y,l=W(ie(zs(b4(jo(new bn(null,new wn(t.b,16)),new uxe(r,f)),new AT)))))),n==Zc?(bc(e.a,new ke(W(ie(T(p,(xi(),wa))))-r,l)),bc(e.a,new ke(M.e.a+M.f.a+r+c,l)),bc(e.a,new ke(M.e.a+M.f.a+r+c,M.e.b+M.f.b/2)),bc(e.a,new ke(M.e.a+M.f.a,M.e.b+M.f.b/2))):n==ru?(bc(e.a,new ke(W(ie(T(p,(xi(),Jf))))+r,p.e.b+p.f.b/2)),bc(e.a,new ke(p.e.a+p.f.a+r,l)),bc(e.a,new ke(M.e.a-r-c,l)),bc(e.a,new ke(M.e.a-r-c,M.e.b+M.f.b/2)),bc(e.a,new ke(M.e.a,M.e.b+M.f.b/2))):n==Ul?(bc(e.a,new ke(l,W(ie(T(p,(xi(),wa))))-r)),bc(e.a,new ke(l,M.e.b+M.f.b+r+c)),bc(e.a,new ke(M.e.a+M.f.a/2,M.e.b+M.f.b+r+c)),bc(e.a,new ke(M.e.a+M.f.a/2,M.e.b+M.f.b+r))):(e.a.b==0||(u(Mf(e.a),8).b=W(ie(T(p,(xi(),Jf))))+r*u(o.b,15).a),bc(e.a,new ke(l,W(ie(T(p,(xi(),Jf))))+r*u(o.b,15).a)),bc(e.a,new ke(l,M.e.b-r*u(o.a,15).a-c))),new yc(me(y),me(S))}function ZRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S;if(o=!0,p=null,i=null,r=null,n=!1,S=Tan,h=null,c=null,l=0,f=MQ(e,l,$8e,R8e),f=0&&hn(e.substr(l,2),"//")?(l+=2,f=MQ(e,l,YA,QA),i=(Yr(l,f,e.length),e.substr(l,f-l)),l=f):p!=null&&(l==e.length||(Yn(l,e.length),e.charCodeAt(l)!=47))&&(o=!1,f=Vse(e,Uo(35),l),f==-1&&(f=e.length),i=(Yr(l,f,e.length),e.substr(l,f-l)),l=f);if(!t&&l0&&ic(b,b.length-1)==58&&(r=b,l=f)),lo?(Ks(e,n,t),1):(Ks(e,t,n),-1)}for(G=e.f,K=0,ee=G.length;K0?Ks(e,n,t):Ks(e,t,n),i;if(!di(n,(pe(),Ci))||!di(t,Ci))return c=WQ(e,n),l=WQ(e,t),c>l?(Ks(e,n,t),1):(Ks(e,t,n),-1)}if(!y&&!M&&(i=UVe(e,n,t),i!=0))return i>0?Ks(e,n,t):Ks(e,t,n),i}return di(n,(pe(),Ci))&&di(t,Ci)?(c=Nw(n,t,e.c,u(T(e.c,Q0),15).a),l=Nw(t,n,e.c,u(T(e.c,Q0),15).a),c>l?(Ks(e,n,t),1):(Ks(e,t,n),-1)):(Ks(e,t,n),-1)}function qVe(){qVe=Y,iW(),Vt=new bw,dn(Vt,(Oe(),Kf),Va),dn(Vt,gf,Va),dn(Vt,ys,Va),dn(Vt,Vf,Va),dn(Vt,Wo,Va),dn(Vt,ks,Va),dn(Vt,Vf,Kf),dn(Vt,Va,Xl),dn(Vt,Kf,Xl),dn(Vt,gf,Xl),dn(Vt,ys,Xl),dn(Vt,Zo,Xl),dn(Vt,Vf,Xl),dn(Vt,Wo,Xl),dn(Vt,ks,Xl),dn(Vt,Bo,Xl),dn(Vt,Va,gl),dn(Vt,Kf,gl),dn(Vt,Xl,gl),dn(Vt,gf,gl),dn(Vt,ys,gl),dn(Vt,Zo,gl),dn(Vt,Vf,gl),dn(Vt,Bo,gl),dn(Vt,wl,gl),dn(Vt,Wo,gl),dn(Vt,as,gl),dn(Vt,ks,gl),dn(Vt,Kf,gf),dn(Vt,ys,gf),dn(Vt,Vf,gf),dn(Vt,ks,gf),dn(Vt,Kf,ys),dn(Vt,gf,ys),dn(Vt,Vf,ys),dn(Vt,ys,ys),dn(Vt,Wo,ys),dn(Vt,Va,Kl),dn(Vt,Kf,Kl),dn(Vt,Xl,Kl),dn(Vt,gl,Kl),dn(Vt,gf,Kl),dn(Vt,ys,Kl),dn(Vt,Zo,Kl),dn(Vt,Vf,Kl),dn(Vt,wl,Kl),dn(Vt,Bo,Kl),dn(Vt,ks,Kl),dn(Vt,Wo,Kl),dn(Vt,po,Kl),dn(Vt,Va,wl),dn(Vt,Kf,wl),dn(Vt,Xl,wl),dn(Vt,gf,wl),dn(Vt,ys,wl),dn(Vt,Zo,wl),dn(Vt,Vf,wl),dn(Vt,Bo,wl),dn(Vt,ks,wl),dn(Vt,as,wl),dn(Vt,po,wl),dn(Vt,Kf,Bo),dn(Vt,gf,Bo),dn(Vt,ys,Bo),dn(Vt,Vf,Bo),dn(Vt,wl,Bo),dn(Vt,ks,Bo),dn(Vt,Wo,Bo),dn(Vt,Va,Qo),dn(Vt,Kf,Qo),dn(Vt,Xl,Qo),dn(Vt,gf,Qo),dn(Vt,ys,Qo),dn(Vt,Zo,Qo),dn(Vt,Vf,Qo),dn(Vt,Bo,Qo),dn(Vt,ks,Qo),dn(Vt,Kf,Wo),dn(Vt,Xl,Wo),dn(Vt,gl,Wo),dn(Vt,ys,Wo),dn(Vt,Va,as),dn(Vt,Kf,as),dn(Vt,gl,as),dn(Vt,gf,as),dn(Vt,ys,as),dn(Vt,Zo,as),dn(Vt,Vf,as),dn(Vt,Vf,po),dn(Vt,ys,po),dn(Vt,Bo,Va),dn(Vt,Bo,gf),dn(Vt,Bo,Xl),dn(Vt,Zo,Va),dn(Vt,Zo,Kf),dn(Vt,Zo,gl)}function WRn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se;switch(t.Tg("Brandes & Koepf node placement",1),e.a=n,e.c=D_n(n),i=u(T(n,(Ce(),Die)),282),S=$e(Pe(T(n,sA))),e.d=i==(RO(),KH)&&!S||i==iie,APn(e,n),he=null,se=null,R=null,G=null,I=(cl(4,$p),new So(4)),u(T(n,Die),282).g){case 3:R=new Z3(n,e.c.d,(Aa(),mg),(oh(),zd)),Jn(I.c,R);break;case 1:G=new Z3(n,e.c.d,(Aa(),Ja),(oh(),zd)),Jn(I.c,G);break;case 4:he=new Z3(n,e.c.d,(Aa(),mg),(oh(),i2)),Jn(I.c,he);break;case 2:se=new Z3(n,e.c.d,(Aa(),Ja),(oh(),i2)),Jn(I.c,se);break;default:R=new Z3(n,e.c.d,(Aa(),mg),(oh(),zd)),G=new Z3(n,e.c.d,Ja,zd),he=new Z3(n,e.c.d,mg,i2),se=new Z3(n,e.c.d,Ja,i2),Jn(I.c,he),Jn(I.c,se),Jn(I.c,R),Jn(I.c,G)}for(r=new exe(n,e.c),l=new L(I);l.ayZ(c))&&(p=c);for(!p&&(p=(pn(0,I.c.length),u(I.c[0],185))),O=new L(n.b);O.a0?(Ks(e,t,n),1):(Ks(e,n,t),-1);if(b&&K)return Ks(e,t,n),1;if(p&&G)return Ks(e,n,t),-1;if(p&&K)return 0}else for(en=new L(h.j);en.ap&&(On=0,ot+=b+Ne,b=0),RXe(he,o,On,ot),n=E.Math.max(n,On+se.a),b=E.Math.max(b,se.b),On+=se.a+Ne;for(ee=new gt,t=new gt,en=new L(e);en.a=-1900?1:0,t>=4?Gt(e,z(B(Re,1),je,2,6,[lYe,fYe])[l]):Gt(e,z(B(Re,1),je,2,6,["BC","AD"])[l]);break;case 121:zjn(e,t,i);break;case 77:LIn(e,t,i);break;case 107:f=r.q.getHours(),f==0?Jh(e,24,t):Jh(e,f,t);break;case 83:QOn(e,t,r);break;case 69:b=i.q.getDay(),t==5?Gt(e,z(B(Re,1),je,2,6,["S","M","T","W","T","F","S"])[b]):t==4?Gt(e,z(B(Re,1),je,2,6,[SW,AW,MW,TW,xW,CW,OW])[b]):Gt(e,z(B(Re,1),je,2,6,["Sun","Mon","Tue","Wed","Thu","Fri","Sat"])[b]);break;case 97:r.q.getHours()>=12&&r.q.getHours()<24?Gt(e,z(B(Re,1),je,2,6,["AM","PM"])[1]):Gt(e,z(B(Re,1),je,2,6,["AM","PM"])[0]);break;case 104:p=r.q.getHours()%12,p==0?Jh(e,12,t):Jh(e,p,t);break;case 75:y=r.q.getHours()%12,Jh(e,y,t);break;case 72:S=r.q.getHours(),Jh(e,S,t);break;case 99:M=i.q.getDay(),t==5?Gt(e,z(B(Re,1),je,2,6,["S","M","T","W","T","F","S"])[M]):t==4?Gt(e,z(B(Re,1),je,2,6,[SW,AW,MW,TW,xW,CW,OW])[M]):t==3?Gt(e,z(B(Re,1),je,2,6,["Sun","Mon","Tue","Wed","Thu","Fri","Sat"])[M]):Jh(e,M,1);break;case 76:O=i.q.getMonth(),t==5?Gt(e,z(B(Re,1),je,2,6,["J","F","M","A","M","J","J","A","S","O","N","D"])[O]):t==4?Gt(e,z(B(Re,1),je,2,6,[dW,bW,gW,wW,V4,pW,mW,vW,yW,kW,EW,jW])[O]):t==3?Gt(e,z(B(Re,1),je,2,6,["Jan","Feb","Mar","Apr",V4,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"])[O]):Jh(e,O+1,t);break;case 81:I=i.q.getMonth()/3|0,t<4?Gt(e,z(B(Re,1),je,2,6,["Q1","Q2","Q3","Q4"])[I]):Gt(e,z(B(Re,1),je,2,6,["1st quarter","2nd quarter","3rd quarter","4th quarter"])[I]);break;case 100:R=i.q.getDate(),Jh(e,R,t);break;case 109:h=r.q.getMinutes(),Jh(e,h,t);break;case 115:o=r.q.getSeconds(),Jh(e,o,t);break;case 122:t<4?Gt(e,c.c[0]):Gt(e,c.c[1]);break;case 118:Gt(e,c.b);break;case 90:t<3?Gt(e,eCn(c)):t==3?Gt(e,rCn(c)):Gt(e,cCn(c.a));break;default:return!1}return!0}function jge(e,n,t,i){var r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne,nn,en,Tn,On,ot,Xt;if(MXe(n),f=u(X((!n.b&&(n.b=new Cn(pt,n,4,7)),n.b),0),84),b=u(X((!n.c&&(n.c=new Cn(pt,n,5,8)),n.c),0),84),l=iu(f),h=iu(b),o=(!n.a&&(n.a=new we(Pi,n,6,6)),n.a).i==0?null:u(X((!n.a&&(n.a=new we(Pi,n,6,6)),n.a),0),170),Ne=u(Bn(e.a,l),9),On=u(Bn(e.a,h),9),nn=null,ot=null,q(f,193)&&(se=u(Bn(e.a,f),246),q(se,12)?nn=u(se,12):q(se,9)&&(Ne=u(se,9),nn=u(_e(Ne.j,0),12))),q(b,193)&&(Tn=u(Bn(e.a,b),246),q(Tn,12)?ot=u(Tn,12):q(Tn,9)&&(On=u(Tn,9),ot=u(_e(On.j,0),12))),!Ne||!On)throw P(new V5("The source or the target of edge "+n+" could not be found. This usually happens when an edge connects a node laid out by ELK Layered to a node in another level of hierarchy laid out by either another instance of ELK Layered or another layout algorithm alltogether. The former can be solved by setting the hierarchyHandling option to INCLUDE_CHILDREN."));for(O=new dw,Lu(O,n),fe(O,(pe(),gi),n),fe(O,(Ce(),Qc),null),S=u(T(i,wo),22),Ne==On&&S.Ec((Oc(),YS)),nn||(he=(Cc(),No),en=null,o&&k3(u(T(Ne,Zi),102))&&(en=new ke(o.j,o.k),nPe(en,sp(n)),xPe(en,t),gp(h,l)&&(he=vs,bi(en,Ne.n))),nn=CKe(Ne,en,he,i)),ot||(he=(Cc(),vs),Xt=null,o&&k3(u(T(On,Zi),102))&&(Xt=new ke(o.b,o.c),nPe(Xt,sp(n)),xPe(Xt,t)),ot=CKe(On,Xt,he,Ir(On))),lc(O,nn),Jr(O,ot),(nn.e.c.length>1||nn.g.c.length>1||ot.e.c.length>1||ot.g.c.length>1)&&S.Ec((Oc(),VS)),y=new ct((!n.n&&(n.n=new we(ku,n,1,7)),n.n));y.e!=y.i.gc();)if(p=u(lt(y),157),!$e(Pe(ve(p,gg)))&&p.a)switch(I=cQ(p),Te(O.b,I),u(T(I,Mh),279).g){case 1:case 2:S.Ec((Oc(),l7));break;case 0:S.Ec((Oc(),s7)),fe(I,Mh,(Oa(),T7))}if(c=u(T(i,uA),301),R=u(T(i,wJ),328),r=c==(Cj(),ZN)||R==(Ij(),Xie),o&&(!o.a&&(o.a=new mr(pl,o,5)),o.a).i!=0&&r){for(G=exn(o),M=new Ss,ee=Et(G,0);ee.b!=ee.d.c;)K=u(yt(ee),8),qt(M,new gc(K));fe(O,Bve,M)}return O}function iBn(e,n,t,i){var r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne,nn,en,Tn,On,ot,Xt,qi;for(en=0,Tn=0,Ne=new gt,he=u(zs(ip(jo(new bn(null,new wn(e.b,16)),new m_),new kk)),15).a+1,nn=oe(It,Zt,30,he,15,1),I=oe(It,Zt,30,he,15,1),O=0;O1)for(l=ot+1;lh.b.e.b*(1-R)+h.c.e.b*R));M++);if(se.gc()>0&&(Xt=h.a.b==0?wc(h.b.e):u(Mf(h.a),8),K=bi(wc(u(se.Xb(se.gc()-1),40).e),u(se.Xb(se.gc()-1),40).f),y=bi(wc(u(se.Xb(0),40).e),u(se.Xb(0),40).f),M>=se.gc()-1&&Xt.b>K.b&&h.c.e.b>K.b||M<=0&&Xt.bh.b.e.a*(1-R)+h.c.e.a*R));M++);if(se.gc()>0&&(Xt=h.a.b==0?wc(h.b.e):u(Mf(h.a),8),K=bi(wc(u(se.Xb(se.gc()-1),40).e),u(se.Xb(se.gc()-1),40).f),y=bi(wc(u(se.Xb(0),40).e),u(se.Xb(0),40).f),M>=se.gc()-1&&Xt.a>K.a&&h.c.e.a>K.a||M<=0&&Xt.a=W(ie(T(e,(xi(),f6e))))&&++Tn):(S.f&&S.d.e.a<=W(ie(T(e,(xi(),are))))&&++en,S.g&&S.c.e.a+S.c.f.a>=W(ie(T(e,(xi(),l6e))))&&++Tn)}else ee==0?B0e(h):ee<0&&(++nn[ot],++I[qi],On=QRn(h,n,e,new yc(me(en),me(Tn)),t,i,new yc(me(I[qi]),me(nn[ot]))),en=u(On.a,15).a,Tn=u(On.b,15).a)}function rBn(e){e.gb||(e.gb=!0,e.b=Su(e,0),Vi(e.b,18),Ii(e.b,19),e.a=Su(e,1),Vi(e.a,1),Ii(e.a,2),Ii(e.a,3),Ii(e.a,4),Ii(e.a,5),e.o=Su(e,2),Vi(e.o,8),Vi(e.o,9),Ii(e.o,10),Ii(e.o,11),Ii(e.o,12),Ii(e.o,13),Ii(e.o,14),Ii(e.o,15),Ii(e.o,16),Ii(e.o,17),Ii(e.o,18),Ii(e.o,19),Ii(e.o,20),Ii(e.o,21),Ii(e.o,22),Ii(e.o,23),Vc(e.o),Vc(e.o),Vc(e.o),Vc(e.o),Vc(e.o),Vc(e.o),Vc(e.o),Vc(e.o),Vc(e.o),Vc(e.o),e.p=Su(e,3),Vi(e.p,2),Vi(e.p,3),Vi(e.p,4),Vi(e.p,5),Ii(e.p,6),Ii(e.p,7),Vc(e.p),Vc(e.p),e.q=Su(e,4),Vi(e.q,8),e.v=Su(e,5),Ii(e.v,9),Vc(e.v),Vc(e.v),Vc(e.v),e.w=Su(e,6),Vi(e.w,2),Vi(e.w,3),Vi(e.w,4),Ii(e.w,5),e.B=Su(e,7),Ii(e.B,1),Vc(e.B),Vc(e.B),Vc(e.B),e.Q=Su(e,8),Ii(e.Q,0),Vc(e.Q),e.R=Su(e,9),Vi(e.R,1),e.S=Su(e,10),Vc(e.S),Vc(e.S),Vc(e.S),Vc(e.S),Vc(e.S),Vc(e.S),Vc(e.S),Vc(e.S),Vc(e.S),Vc(e.S),Vc(e.S),Vc(e.S),Vc(e.S),Vc(e.S),Vc(e.S),e.T=Su(e,11),Ii(e.T,10),Ii(e.T,11),Ii(e.T,12),Ii(e.T,13),Ii(e.T,14),Vc(e.T),Vc(e.T),e.U=Su(e,12),Vi(e.U,2),Vi(e.U,3),Ii(e.U,4),Ii(e.U,5),Ii(e.U,6),Ii(e.U,7),Vc(e.U),e.V=Su(e,13),Ii(e.V,10),e.W=Su(e,14),Vi(e.W,18),Vi(e.W,19),Vi(e.W,20),Ii(e.W,21),Ii(e.W,22),Ii(e.W,23),e.bb=Su(e,15),Vi(e.bb,10),Vi(e.bb,11),Vi(e.bb,12),Vi(e.bb,13),Vi(e.bb,14),Vi(e.bb,15),Vi(e.bb,16),Ii(e.bb,17),Vc(e.bb),Vc(e.bb),e.eb=Su(e,16),Vi(e.eb,2),Vi(e.eb,3),Vi(e.eb,4),Vi(e.eb,5),Vi(e.eb,6),Vi(e.eb,7),Ii(e.eb,8),Ii(e.eb,9),e.ab=Su(e,17),Vi(e.ab,0),Vi(e.ab,1),e.H=Su(e,18),Ii(e.H,0),Ii(e.H,1),Ii(e.H,2),Ii(e.H,3),Ii(e.H,4),Ii(e.H,5),Vc(e.H),e.db=Su(e,19),Ii(e.db,2),e.c=ti(e,20),e.d=ti(e,21),e.e=ti(e,22),e.f=ti(e,23),e.i=ti(e,24),e.g=ti(e,25),e.j=ti(e,26),e.k=ti(e,27),e.n=ti(e,28),e.r=ti(e,29),e.s=ti(e,30),e.t=ti(e,31),e.u=ti(e,32),e.fb=ti(e,33),e.A=ti(e,34),e.C=ti(e,35),e.D=ti(e,36),e.F=ti(e,37),e.G=ti(e,38),e.I=ti(e,39),e.J=ti(e,40),e.L=ti(e,41),e.M=ti(e,42),e.N=ti(e,43),e.O=ti(e,44),e.P=ti(e,45),e.X=ti(e,46),e.Y=ti(e,47),e.Z=ti(e,48),e.$=ti(e,49),e._=ti(e,50),e.cb=ti(e,51),e.K=ti(e,52))}function cBn(e,n,t,i){var r,c,o,l,f,h,b,p,y,S,M;for(p=Et(e.b,0);p.b!=p.d.c;)if(b=u(yt(p),40),!hn(b.c,OF))for(c=u(bs(new bn(null,new wn(mCn(b,e),16)),Ts(new Bi,new Ei,new eu,z(B(Yo,1),ye,130,0,[($l(),Vo)]))),16),n==(vr(),Zc)||n==ru?c.gd(new S_):c.gd(new A_),M=c.gc(),r=0;r0&&(l=u(Mf(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(Mf(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&E.Math.abs(f-S)/(E.Math.abs(l-y)/40)>50&&(S>f?bc(u(c.Xb(r),65).a,new ke(b.e.a+b.f.a+i/5.3,b.e.b+b.f.b*o-i/2)):bc(u(c.Xb(r),65).a,new ke(b.e.a+b.f.a+i/5.3,b.e.b+b.f.b*o+i/2)))),bc(u(c.Xb(r),65).a,new ke(b.e.a+b.f.a,b.e.b+b.f.b*o))):n==ru?(h=W(ie(T(b,(xi(),wa)))),b.e.a-i>h?bc(u(c.Xb(r),65).a,new ke(h-t,b.e.b+b.f.b*o)):u(c.Xb(r),65).a.b>0&&(l=u(Mf(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(Mf(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&E.Math.abs(f-S)/(E.Math.abs(l-y)/40)>50&&(S>f?bc(u(c.Xb(r),65).a,new ke(b.e.a-i/5.3,b.e.b+b.f.b*o-i/2)):bc(u(c.Xb(r),65).a,new ke(b.e.a-i/5.3,b.e.b+b.f.b*o+i/2)))),bc(u(c.Xb(r),65).a,new ke(b.e.a,b.e.b+b.f.b*o))):n==Ul?(h=W(ie(T(b,(xi(),Jf)))),b.e.b+b.f.b+i0&&(l=u(Mf(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(Mf(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&E.Math.abs(l-y)/(E.Math.abs(f-S)/40)>50&&(y>l?bc(u(c.Xb(r),65).a,new ke(b.e.a+b.f.a*o-i/2,b.e.b+i/5.3+b.f.b)):bc(u(c.Xb(r),65).a,new ke(b.e.a+b.f.a*o+i/2,b.e.b+i/5.3+b.f.b)))),bc(u(c.Xb(r),65).a,new ke(b.e.a+b.f.a*o,b.e.b+b.f.b))):(h=W(ie(T(b,(xi(),wa)))),Oze(u(c.Xb(r),65),e)?bc(u(c.Xb(r),65).a,new ke(b.e.a+b.f.a*o,u(Mf(u(c.Xb(r),65).a),8).b)):b.e.b-i>h?bc(u(c.Xb(r),65).a,new ke(b.e.a+b.f.a*o,h-t)):u(c.Xb(r),65).a.b>0&&(l=u(Mf(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(Mf(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&E.Math.abs(l-y)/(E.Math.abs(f-S)/40)>50&&(y>l?bc(u(c.Xb(r),65).a,new ke(b.e.a+b.f.a*o-i/2,b.e.b-i/5.3)):bc(u(c.Xb(r),65).a,new ke(b.e.a+b.f.a*o+i/2,b.e.b-i/5.3)))),bc(u(c.Xb(r),65).a,new ke(b.e.a+b.f.a*o,b.e.b)))}function KVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se;if(o=n,y=t,oo(e.a,o)){if(ef(u(Bn(e.a,o),47),y))return 1}else Qt(e.a,o,new fr);if(oo(e.a,y)){if(ef(u(Bn(e.a,y),47),o))return-1}else Qt(e.a,y,new fr);if(oo(e.e,o)){if(ef(u(Bn(e.e,o),47),y))return-1}else Qt(e.e,o,new fr);if(oo(e.e,y)){if(ef(u(Bn(e.a,y),47),o))return 1}else Qt(e.e,y,new fr);if(o.j!=y.j)return he=Kgn(o.j,y.j),he>0?zl(e,o,y,1):zl(e,y,o,1),he;if(se=1,o.e.c.length!=0&&y.e.c.length!=0){if((o.j==(Oe(),Kn)&&y.j==Kn||o.j==Xn&&y.j==Xn||o.j==dt&&y.j==dt)&&(se=-se),b=u(_e(o.e,0),17).c,I=u(_e(y.e,0),17).c,f=b.i,M=I.i,f==M)for(K=new L(f.j);K.a0?(zl(e,o,y,se),se):(zl(e,y,o,se),-se);if(i=hFe(u(bs(dV(e.d),Ts(new Bi,new Ei,new eu,z(B(Yo,1),ye,130,0,[($l(),Vo)]))),20),f,M),i!=0)return i>0?(zl(e,o,y,se),se):(zl(e,y,o,se),-se);if(e.c&&(he=HHe(e,o,y),he!=0))return he>0?(zl(e,o,y,se),se):(zl(e,y,o,se),-se)}return o.g.c.length!=0&&y.g.c.length!=0?((o.j==(Oe(),Kn)&&y.j==Kn||o.j==dt&&y.j==dt)&&(se=-se),p=u(T(o,(pe(),die)),9),R=u(T(y,die),9),e.f==(P1(),Yie)&&p&&R&&di(p,Ci)&&di(R,Ci)?(l=Nw(p,R,e.b,u(T(e.b,Q0),15).a),S=Nw(R,p,e.b,u(T(e.b,Q0),15).a),l>S?(zl(e,o,y,se),se):(zl(e,y,o,se),-se)):e.c&&(he=HHe(e,o,y),he!=0)?he>0?(zl(e,o,y,se),se):(zl(e,y,o,se),-se):(h=0,O=0,di(u(_e(o.g,0),17),Ci)&&(h=Nw(u(_e(o.g,0),246),u(_e(y.g,0),246),e.b,o.g.c.length+o.e.c.length)),di(u(_e(y.g,0),17),Ci)&&(O=Nw(u(_e(y.g,0),246),u(_e(o.g,0),246),e.b,y.g.c.length+y.e.c.length)),p&&p==R||e.g&&(e.g._b(p)&&(h=u(e.g.xc(p),15).a),e.g._b(R)&&(O=u(e.g.xc(R),15).a)),h>O?(zl(e,o,y,se),se):(zl(e,y,o,se),-se))):o.e.c.length!=0&&y.g.c.length!=0?(zl(e,o,y,se),1):o.g.c.length!=0&&y.e.c.length!=0?(zl(e,y,o,se),-1):di(o,(pe(),Ci))&&di(y,Ci)?(c=o.i.j.c.length,l=Nw(o,y,e.b,c),S=Nw(y,o,e.b,c),(o.j==(Oe(),Kn)&&y.j==Kn||o.j==dt&&y.j==dt)&&(se=-se),l>S?(zl(e,o,y,se),se):(zl(e,y,o,se),-se)):(zl(e,y,o,se),-se)}function pe(){pe=Y;var e,n;gi=new mi(Zge),Lve=new mi("coordinateOrigin"),gie=new mi("processors"),_ve=new Li("compoundNode",(Pn(),!1)),rD=new Li("insideConnections",!1),Bve=new mi("originalBendpoints"),zve=new mi("originalDummyNodePosition"),Fve=new mi("originalLabelEdge"),ZS=new mi("representedLabels"),QS=new mi("endLabels"),w6=new mi("endLabel.origin"),m6=new Li("labelSide",(ol(),RD)),Ev=new Li("maxEdgeThickness",0),$d=new Li("reversed",!1),v6=new mi(XYe),ga=new Li("longEdgeSource",null),hf=new Li("longEdgeTarget",null),em=new Li("longEdgeHasLabelDummies",!1),cD=new Li("longEdgeBeforeLabelDummy",!1),eJ=new Li("edgeConstraint",(Gb(),Qte)),Qw=new mi("inLayerLayoutUnit"),fg=new Li("inLayerConstraint",(C1(),tD)),p6=new Li("inLayerSuccessorConstraint",new xe),Rve=new Li("inLayerSuccessorConstraintBetweenNonDummies",!1),ms=new mi("portDummy"),WH=new Li("crossingHint",me(0)),wo=new Li("graphProperties",(n=u(na(rie),10),new Nl(n,u(Tf(n,n.length),10),0))),Ou=new Li("externalPortSide",(Oe(),yu)),$ve=new Li("externalPortSize",new Kr),fie=new mi("externalPortReplacedDummies"),nJ=new mi("externalPortReplacedDummy"),J1=new Li("externalPortConnections",(e=u(na(jc),10),new Nl(e,u(Tf(e,e.length),10),0))),Zw=new Li(HYe,0),Ive=new mi("barycenterAssociates"),k6=new mi("TopSideComments"),b6=new mi("BottomSideComments"),ZH=new mi("CommentConnectionPort"),hie=new Li("inputCollect",!1),bie=new Li("outputCollect",!1),g6=new Li("cyclic",!1),Pve=new mi("crossHierarchyMap"),pie=new mi("targetOffset"),new Li("splineLabelSize",new Kr),Sv=new mi("spacings"),tJ=new Li("partitionConstraint",!1),Yw=new mi("breakingPoint.info"),Gve=new mi("splines.survivingEdge"),ag=new mi("splines.route.start"),Av=new mi("splines.edgeChain"),Jve=new mi("originalPortConstraints"),Ww=new mi("selfLoopHolder"),a7=new mi("splines.nsPortY"),Ci=new mi("modelOrder"),Q0=new mi("modelOrder.maximum"),iD=new mi("modelOrderGroups.cb.number"),die=new mi("longEdgeTargetNode"),Y0=new Li(vQe,!1),jv=new Li(vQe,!1),aie=new mi("layerConstraints.hiddenNodes"),Hve=new mi("layerConstraints.opposidePort"),wie=new mi("targetNode.modelOrder"),y6=new Li("tarjan.lowlink",me(ri)),WS=new Li("tarjan.id",me(-1)),iJ=new Li("tarjan.onstack",!1),Hin=new Li("partOfCycle",!1),Mv=new mi("medianHeuristic.weight")}function Ht(){Ht=Y;var e,n;C6=new mi(sZe),mm=new mi(lZe),s9e=(Gh(),ice),afn=new cn(s2e,s9e),j7=new cn(O8,null),hfn=new mi(Epe),f9e=(Yb(),Ti(uce,z(B(oce,1),ye,299,0,[cce]))),CD=new cn(MF,f9e),OD=new cn(DN,(Pn(),!1)),a9e=(vr(),Xa),vg=new cn(Pee,a9e),b9e=(L1(),yce),d9e=new cn(NN,b9e),wfn=new cn(ype,!1),w9e=(_1(),cG),Pv=new cn(AF,w9e),T9e=new iw(12),t1=new cn(Fp,T9e),DD=new cn(hS,!1),ace=new cn(xF,!1),ID=new cn(dS,!1),D9e=(Rr(),ub),RA=new cn(XW,D9e),O6=new mi(TF),_D=new mi(kN),mce=new mi(uF),vce=new mi(aS),v9e=new Ss,$v=new cn(v2e,v9e),bfn=new cn(j2e,!1),pfn=new cn(S2e,!1),new cn(fZe,0),y9e=new uE,S7=new cn(M2e,y9e),eG=new cn(u2e,!1),jfn=new cn(aZe,1),gm=new mi(hZe),bm=new mi(dZe),M7=new cn(EN,!1),new cn(bZe,!0),me(0),new cn(gZe,me(100)),new cn(wZe,!1),me(0),new cn(pZe,me(4e3)),me(0),new cn(mZe,me(400)),new cn(vZe,!1),new cn(yZe,!1),new cn(kZe,!0),new cn(EZe,!1),l9e=(UB(),Mce),dfn=new cn(kpe,l9e),m9e=(hj(),FD),vfn=new cn(jZe,m9e),p9e=(U9(),LD),mfn=new cn(SZe,p9e),Sfn=new cn(Kwe,10),Afn=new cn(Vwe,10),Mfn=new cn(Ywe,20),Tfn=new cn(Qwe,10),P9e=new cn(UW,2),$9e=new cn(Lee,10),R9e=new cn(Zwe,0),nG=new cn(n2e,5),B9e=new cn(Wwe,1),z9e=new cn(e2e,1),Jd=new cn(zp,20),xfn=new cn(t2e,10),J9e=new cn(i2e,10),N6=new mi(r2e),H9e=new sCe,F9e=new cn(T2e,H9e),kfn=new mi(Ree),x9e=!1,yfn=new cn($ee,x9e),E9e=new iw(5),k9e=new cn(a2e,E9e),j9e=(Op(),n=u(na(Lc),10),new Nl(n,u(Tf(n,n.length),10),0)),Rv=new cn(D8,j9e),O9e=(G3(),cb),C9e=new cn(b2e,O9e),dce=new mi(g2e),bce=new mi(w2e),gce=new mi(p2e),hce=new mi(m2e),S9e=(e=u(na(UA),10),new Nl(e,u(Tf(e,e.length),10),0)),yg=new cn(cv,S9e),M9e=We((Is(),N7)),ib=new cn(n6,M9e),A9e=new ke(0,0),Bv=new cn(t6,A9e),wm=new cn(N8,!1),h9e=(Oa(),T7),lce=new cn(k2e,h9e),sce=new cn(oF,!1),me(1),new cn(AZe,null),N9e=new mi(A2e),wce=new mi(E2e),L9e=(Oe(),yu),zv=new cn(o2e,L9e),Ls=new mi(c2e),I9e=(ws(),We(ob)),pm=new cn(I8,I9e),pce=new cn(h2e,!1),_9e=new cn(d2e,!0),me(1),Ifn=new cn(one,me(3)),me(1),Lfn=new cn(jpe,me(4)),tG=new cn(jN,1),iG=new cn(sne,null),vm=new cn(SN,150),A7=new cn(AN,1.414),D6=new cn(Rw,null),Cfn=new cn(Spe,1),ND=new cn(l2e,!1),fce=new cn(f2e,!1),gfn=new cn(y2e,1),g9e=(yz(),Ece),new cn(MZe,g9e),Efn=!0,_fn=(GR(),Ace),Nfn=(I4(),Em),Dfn=Em,Ofn=Em}function qr(){qr=Y,O3e=new dr("DIRECTION_PREPROCESSOR",0),T3e=new dr("COMMENT_PREPROCESSOR",1),gv=new dr("EDGE_AND_LAYER_CONSTRAINT_EDGE_REVERSER",2),xte=new dr("INTERACTIVE_EXTERNAL_PORT_POSITIONER",3),V3e=new dr("PARTITION_PREPROCESSOR",4),MH=new dr("LABEL_DUMMY_INSERTER",5),PH=new dr("SELF_LOOP_PREPROCESSOR",6),Yp=new dr("LAYER_CONSTRAINT_PREPROCESSOR",7),X3e=new dr("PARTITION_MIDPROCESSOR",8),R3e=new dr("HIGH_DEGREE_NODE_LAYER_PROCESSOR",9),q3e=new dr("NODE_PROMOTION",10),Vp=new dr("LAYER_CONSTRAINT_POSTPROCESSOR",11),K3e=new dr("PARTITION_POSTPROCESSOR",12),L3e=new dr("HIERARCHICAL_PORT_CONSTRAINT_PROCESSOR",13),Y3e=new dr("SEMI_INTERACTIVE_CROSSMIN_PROCESSOR",14),k3e=new dr("BREAKING_POINT_INSERTER",15),OH=new dr("LONG_EDGE_SPLITTER",16),Cte=new dr("PORT_SIDE_PROCESSOR",17),SH=new dr("INVERTED_PORT_PROCESSOR",18),IH=new dr("PORT_LIST_SORTER",19),Z3e=new dr("SORT_BY_INPUT_ORDER_OF_MODEL",20),DH=new dr("NORTH_SOUTH_PORT_PREPROCESSOR",21),E3e=new dr("BREAKING_POINT_PROCESSOR",22),U3e=new dr(aQe,23),W3e=new dr(hQe,24),_H=new dr("SELF_LOOP_PORT_RESTORER",25),y3e=new dr("ALTERNATING_LAYER_UNZIPPER",26),Q3e=new dr("SINGLE_EDGE_GRAPH_WRAPPER",27),AH=new dr("IN_LAYER_CONSTRAINT_PROCESSOR",28),D3e=new dr("END_NODE_PORT_LABEL_MANAGEMENT_PROCESSOR",29),J3e=new dr("LABEL_AND_NODE_SIZE_PROCESSOR",30),H3e=new dr("INNERMOST_NODE_MARGIN_CALCULATOR",31),$H=new dr("SELF_LOOP_ROUTER",32),A3e=new dr("COMMENT_NODE_MARGIN_CALCULATOR",33),jH=new dr("END_LABEL_PREPROCESSOR",34),xH=new dr("LABEL_DUMMY_SWITCHER",35),S3e=new dr("CENTER_LABEL_MANAGEMENT_PROCESSOR",36),n7=new dr("LABEL_SIDE_SELECTOR",37),z3e=new dr("HYPEREDGE_DUMMY_MERGER",38),P3e=new dr("HIERARCHICAL_PORT_DUMMY_SIZE_PROCESSOR",39),G3e=new dr("LAYER_SIZE_AND_GRAPH_HEIGHT_CALCULATOR",40),qS=new dr("HIERARCHICAL_PORT_POSITION_PROCESSOR",41),x3e=new dr("CONSTRAINTS_POSTPROCESSOR",42),M3e=new dr("COMMENT_POSTPROCESSOR",43),F3e=new dr("HYPERNODE_PROCESSOR",44),$3e=new dr("HIERARCHICAL_PORT_ORTHOGONAL_EDGE_ROUTER",45),CH=new dr("LONG_EDGE_JOINER",46),LH=new dr("SELF_LOOP_POSTPROCESSOR",47),j3e=new dr("BREAKING_POINT_REMOVER",48),NH=new dr("NORTH_SOUTH_PORT_POSTPROCESSOR",49),B3e=new dr("HORIZONTAL_COMPACTOR",50),TH=new dr("LABEL_DUMMY_REMOVER",51),I3e=new dr("FINAL_SPLINE_BENDPOINTS_CALCULATOR",52),N3e=new dr("END_LABEL_SORTER",53),h6=new dr("REVERSED_EDGE_RESTORER",54),EH=new dr("END_LABEL_POSTPROCESSOR",55),_3e=new dr("HIERARCHICAL_NODE_RESIZER",56),C3e=new dr("DIRECTION_POSTPROCESSOR",57)}function uBn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne,nn,en,Tn,On,ot,Xt,qi,Es,Wc,ml,Uv,Qd,Yf,Y1,Yl,B6,cM,Q1,ya,Zd,Ag,Mg,z6,Tg,xg,Z1,Om,m7e,f2,uM,Jce,F6,oM,Nm,sM,Gce,Shn;for(m7e=0,Xt=n,Wc=0,Qd=Xt.length;Wc0&&(e.a[ya.p]=m7e++)}for(oM=0,qi=t,ml=0,Yf=qi.length;ml0;){for(ya=(ft(z6.b>0),u(z6.a.Xb(z6.c=--z6.b),12)),Mg=0,l=new L(ya.e);l.a0&&(ya.j==(Oe(),Xn)?(e.a[ya.p]=oM,++oM):(e.a[ya.p]=oM+Y1+B6,++B6))}oM+=B6}for(Ag=new gt,M=new Lh,ot=n,Es=0,Uv=ot.length;Esh.b&&(h.b=Tg)):ya.i.c==Om&&(Tgh.c&&(h.c=Tg));for(x9(O,0,O.length,null),F6=oe(It,Zt,30,O.length,15,1),i=oe(It,Zt,30,oM+1,15,1),R=0;R0;)Ne%2>0&&(r+=Gce[Ne+1]),Ne=(Ne-1)/2|0,++Gce[Ne];for(en=oe(hon,xn,370,O.length*2,0,1),ee=0;ee0&&zC(Es.f),ve(R,iG)!=null&&(!R.a&&(R.a=new we($t,R,10,11)),!!R.a)&&(!R.a&&(R.a=new we($t,R,10,11)),R.a).i>0?(l=u(ve(R,iG),521),Mg=l.Sg(R),tw(R,E.Math.max(R.g,Mg.a+Y1.b+Y1.c),E.Math.max(R.f,Mg.b+Y1.d+Y1.a))):(!R.a&&(R.a=new we($t,R,10,11)),R.a).i!=0&&(Mg=new ke(W(ie(ve(R,vm))),W(ie(ve(R,vm)))/W(ie(ve(R,A7)))),tw(R,E.Math.max(R.g,Mg.a+Y1.b+Y1.c),E.Math.max(R.f,Mg.b+Y1.d+Y1.a)));if(Yf=u(ve(n,t1),104),S=n.g-(Yf.b+Yf.c),y=n.f-(Yf.d+Yf.a),xg.ah("Available Child Area: ("+S+"|"+y+")"),yi(n,j7,S/y),jHe(n,r,i.dh(Uv)),u(ve(n,D6),281)==aG&&(tW(n),tw(n,Yf.b+W(ie(ve(n,gm)))+Yf.c,Yf.d+W(ie(ve(n,bm)))+Yf.a)),xg.ah("Executed layout algorithm: "+Dt(ve(n,C6))+" on node "+n.k),u(ve(n,D6),281)==Em){if(S<0||y<0)throw P(new fd("The size defined by the parent parallel node is too small for the space provided by the paddings of the child hierarchical node. "+n.k));for(ua(n,gm)||ua(n,bm)||tW(n),O=W(ie(ve(n,gm))),M=W(ie(ve(n,bm))),xg.ah("Desired Child Area: ("+O+"|"+M+")"),B6=S/O,cM=y/M,Yl=E.Math.min(B6,E.Math.min(cM,W(ie(ve(n,Cfn))))),yi(n,tG,Yl),xg.ah(n.k+" -- Local Scale Factor (X|Y): ("+B6+"|"+cM+")"),ee=u(ve(n,CD),22),c=0,o=0,Yl'?":hn(cWe,e)?"'(?<' or '(? toIndex: ",Fge=", toIndex: ",Hge="Index: ",Jge=", Size: ",M8="org.eclipse.elk.alg.common",Ut={51:1},SYe="org.eclipse.elk.alg.common.compaction",AYe="Scanline/EventHandler",Yh="org.eclipse.elk.alg.common.compaction.oned",MYe="CNode belongs to another CGroup.",TYe="ISpacingsHandler/1",zW="The ",FW=" instance has been finished already.",xYe="The direction ",CYe=" is not supported by the CGraph instance.",OYe="OneDimensionalCompactor",NYe="OneDimensionalCompactor/lambda$0$Type",DYe="Quadruplet",IYe="ScanlineConstraintCalculator",_Ye="ScanlineConstraintCalculator/ConstraintsScanlineHandler",LYe="ScanlineConstraintCalculator/ConstraintsScanlineHandler/lambda$0$Type",PYe="ScanlineConstraintCalculator/Timestamp",$Ye="ScanlineConstraintCalculator/lambda$0$Type",mh={178:1,48:1},sS="org.eclipse.elk.alg.common.networksimplex",fa={171:1,3:1,4:1},RYe="org.eclipse.elk.alg.common.nodespacing",ng="org.eclipse.elk.alg.common.nodespacing.cellsystem",T8="CENTER",BYe={216:1,337:1},Gge={3:1,4:1,5:1,592:1},Z4="LEFT",W4="RIGHT",qge="Vertical alignment cannot be null",Uge="BOTTOM",rF="org.eclipse.elk.alg.common.nodespacing.internal",lS="UNDEFINED",$a=.01,mN="org.eclipse.elk.alg.common.nodespacing.internal.algorithm",zYe="LabelPlacer/lambda$0$Type",FYe="LabelPlacer/lambda$1$Type",HYe="portRatioOrPosition",x8="org.eclipse.elk.alg.common.overlaps",HW="DOWN",e6="org.eclipse.elk.alg.common.spore",Bp={3:1,4:1,5:1,198:1},JYe={3:1,6:1,4:1,5:1,90:1,110:1},JW="org.eclipse.elk.alg.force",Xge="ComponentsProcessor",GYe="ComponentsProcessor/1",Kge="ElkGraphImporter/lambda$0$Type",$w={214:1},rv="org.eclipse.elk.core",vN="org.eclipse.elk.graph.properties",qYe="IPropertyHolder",yN="org.eclipse.elk.alg.force.graph",UYe="Component Layout",Vge="org.eclipse.elk.alg.force.model",mu="org.eclipse.elk.core.data",cF="org.eclipse.elk.force.model",Yge="org.eclipse.elk.force.iterations",Qge="org.eclipse.elk.force.repulsivePower",GW="org.eclipse.elk.force.temperature",vh=.001,qW="org.eclipse.elk.force.repulsion",Ra={148:1},fS="org.eclipse.elk.alg.force.options",C8=1.600000023841858,Po="org.eclipse.elk.force",kN="org.eclipse.elk.priority",zp="org.eclipse.elk.spacing.nodeNode",UW="org.eclipse.elk.spacing.edgeLabel",O8="org.eclipse.elk.aspectRatio",uF="org.eclipse.elk.randomSeed",aS="org.eclipse.elk.separateConnectedComponents",Fp="org.eclipse.elk.padding",hS="org.eclipse.elk.interactive",XW="org.eclipse.elk.portConstraints",oF="org.eclipse.elk.edgeLabels.inline",dS="org.eclipse.elk.omitNodeMicroLayout",N8="org.eclipse.elk.nodeSize.fixedGraphSize",n6="org.eclipse.elk.nodeSize.options",cv="org.eclipse.elk.nodeSize.constraints",D8="org.eclipse.elk.nodeLabels.placement",I8="org.eclipse.elk.portLabels.placement",EN="org.eclipse.elk.topdownLayout",jN="org.eclipse.elk.topdown.scaleFactor",SN="org.eclipse.elk.topdown.hierarchicalNodeWidth",AN="org.eclipse.elk.topdown.hierarchicalNodeAspectRatio",Rw="org.eclipse.elk.topdown.nodeType",Zge="origin",XYe="random",KYe="boundingBox.upLeft",VYe="boundingBox.lowRight",Wge="org.eclipse.elk.stress.fixed",ewe="org.eclipse.elk.stress.desiredEdgeLength",nwe="org.eclipse.elk.stress.dimension",twe="org.eclipse.elk.stress.epsilon",iwe="org.eclipse.elk.stress.iterationLimit",F0="org.eclipse.elk.stress",YYe="ELK Stress",t6="org.eclipse.elk.nodeSize.minimum",sF="org.eclipse.elk.alg.force.stress",QYe="Layered layout",i6="org.eclipse.elk.alg.layered",MN="org.eclipse.elk.alg.layered.compaction.components",bS="org.eclipse.elk.alg.layered.compaction.oned",lF="org.eclipse.elk.alg.layered.compaction.oned.algs",tg="org.eclipse.elk.alg.layered.compaction.recthull",Ba="org.eclipse.elk.alg.layered.components",aa="NONE",KW="MODEL_ORDER",Ju={3:1,6:1,4:1,10:1,5:1,126:1},ZYe={3:1,6:1,4:1,5:1,135:1,90:1,110:1},fF="org.eclipse.elk.alg.layered.compound",Mi={43:1},Qu="org.eclipse.elk.alg.layered.graph",VW=" -> ",WYe="Not supported by LGraph",rwe="Port side is undefined",_8={3:1,6:1,4:1,5:1,323:1,135:1,90:1,110:1},Id={3:1,6:1,4:1,5:1,135:1,199:1,209:1,90:1,110:1},eQe={3:1,6:1,4:1,5:1,135:1,2004:1,209:1,90:1,110:1},nQe=`([{"' \r -`,tQe=`)]}"' \r -`,iQe="The given string contains parts that cannot be parsed as numbers.",TN="org.eclipse.elk.core.math",rQe={3:1,4:1,140:1,213:1,414:1},cQe={3:1,4:1,104:1,213:1,414:1},_d="org.eclipse.elk.alg.layered.graph.transform",uQe="ElkGraphImporter",oQe="ElkGraphImporter/lambda$1$Type",sQe="ElkGraphImporter/lambda$2$Type",lQe="ElkGraphImporter/lambda$4$Type",Qn="org.eclipse.elk.alg.layered.intermediate",fQe="Node margin calculation",aQe="ONE_SIDED_GREEDY_SWITCH",hQe="TWO_SIDED_GREEDY_SWITCH",YW="No implementation is available for the layout processor ",QW="IntermediateProcessorStrategy",ZW="Node '",dQe="FIRST_SEPARATE",bQe="LAST_SEPARATE",gQe="Odd port side processing",sr="org.eclipse.elk.alg.layered.intermediate.compaction",gS="org.eclipse.elk.alg.layered.intermediate.greedyswitch",Qh="org.eclipse.elk.alg.layered.p3order.counting",wS={220:1},r6="org.eclipse.elk.alg.layered.intermediate.loops",al="org.eclipse.elk.alg.layered.intermediate.loops.ordering",H0="org.eclipse.elk.alg.layered.intermediate.loops.routing",aF="org.eclipse.elk.alg.layered.intermediate.preserveorder",yh="org.eclipse.elk.alg.layered.intermediate.wrapping",Tu="org.eclipse.elk.alg.layered.options",WW="INTERACTIVE",cwe="GREEDY",wQe="DEPTH_FIRST",pQe="EDGE_LENGTH",mQe="SELF_LOOPS",vQe="firstTryWithInitialOrder",uwe="org.eclipse.elk.layered.directionCongruency",owe="org.eclipse.elk.layered.feedbackEdges",hF="org.eclipse.elk.layered.interactiveReferencePoint",swe="org.eclipse.elk.layered.mergeEdges",lwe="org.eclipse.elk.layered.mergeHierarchyEdges",fwe="org.eclipse.elk.layered.allowNonFlowPortsToSwitchSides",awe="org.eclipse.elk.layered.portSortingStrategy",hwe="org.eclipse.elk.layered.thoroughness",dwe="org.eclipse.elk.layered.unnecessaryBendpoints",bwe="org.eclipse.elk.layered.generatePositionAndLayerIds",xN="org.eclipse.elk.layered.cycleBreaking.strategy",CN="org.eclipse.elk.layered.layering.strategy",gwe="org.eclipse.elk.layered.layering.layerConstraint",wwe="org.eclipse.elk.layered.layering.layerChoiceConstraint",pwe="org.eclipse.elk.layered.layering.layerId",eee="org.eclipse.elk.layered.layering.minWidth.upperBoundOnWidth",nee="org.eclipse.elk.layered.layering.minWidth.upperLayerEstimationScalingFactor",tee="org.eclipse.elk.layered.layering.nodePromotion.strategy",iee="org.eclipse.elk.layered.layering.nodePromotion.maxIterations",ree="org.eclipse.elk.layered.layering.coffmanGraham.layerBound",pS="org.eclipse.elk.layered.crossingMinimization.strategy",mwe="org.eclipse.elk.layered.crossingMinimization.forceNodeModelOrder",cee="org.eclipse.elk.layered.crossingMinimization.hierarchicalSweepiness",uee="org.eclipse.elk.layered.crossingMinimization.semiInteractive",vwe="org.eclipse.elk.layered.crossingMinimization.inLayerPredOf",ywe="org.eclipse.elk.layered.crossingMinimization.inLayerSuccOf",kwe="org.eclipse.elk.layered.crossingMinimization.positionChoiceConstraint",Ewe="org.eclipse.elk.layered.crossingMinimization.positionId",jwe="org.eclipse.elk.layered.crossingMinimization.greedySwitch.activationThreshold",oee="org.eclipse.elk.layered.crossingMinimization.greedySwitch.type",dF="org.eclipse.elk.layered.crossingMinimization.greedySwitchHierarchical.type",uv="org.eclipse.elk.layered.nodePlacement.strategy",bF="org.eclipse.elk.layered.nodePlacement.favorStraightEdges",see="org.eclipse.elk.layered.nodePlacement.bk.edgeStraightening",lee="org.eclipse.elk.layered.nodePlacement.bk.fixedAlignment",fee="org.eclipse.elk.layered.nodePlacement.linearSegments.deflectionDampening",aee="org.eclipse.elk.layered.nodePlacement.networkSimplex.nodeFlexibility",hee="org.eclipse.elk.layered.nodePlacement.networkSimplex.nodeFlexibility.default",Swe="org.eclipse.elk.layered.edgeRouting.selfLoopDistribution",Awe="org.eclipse.elk.layered.edgeRouting.selfLoopOrdering",gF="org.eclipse.elk.layered.edgeRouting.splines.mode",wF="org.eclipse.elk.layered.edgeRouting.splines.sloppy.layerSpacingFactor",dee="org.eclipse.elk.layered.edgeRouting.polyline.slopedEdgeZoneWidth",Mwe="org.eclipse.elk.layered.spacing.baseValue",Twe="org.eclipse.elk.layered.spacing.edgeNodeBetweenLayers",xwe="org.eclipse.elk.layered.spacing.edgeEdgeBetweenLayers",Cwe="org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers",Owe="org.eclipse.elk.layered.priority.direction",Nwe="org.eclipse.elk.layered.priority.shortness",Dwe="org.eclipse.elk.layered.priority.straightness",bee="org.eclipse.elk.layered.compaction.connectedComponents",Iwe="org.eclipse.elk.layered.compaction.postCompaction.strategy",_we="org.eclipse.elk.layered.compaction.postCompaction.constraints",pF="org.eclipse.elk.layered.highDegreeNodes.treatment",gee="org.eclipse.elk.layered.highDegreeNodes.threshold",wee="org.eclipse.elk.layered.highDegreeNodes.treeHeight",z1="org.eclipse.elk.layered.wrapping.strategy",mF="org.eclipse.elk.layered.wrapping.additionalEdgeSpacing",vF="org.eclipse.elk.layered.wrapping.correctionFactor",mS="org.eclipse.elk.layered.wrapping.cutting.strategy",pee="org.eclipse.elk.layered.wrapping.cutting.cuts",mee="org.eclipse.elk.layered.wrapping.cutting.msd.freedom",yF="org.eclipse.elk.layered.wrapping.validify.strategy",kF="org.eclipse.elk.layered.wrapping.validify.forbiddenIndices",EF="org.eclipse.elk.layered.wrapping.multiEdge.improveCuts",jF="org.eclipse.elk.layered.wrapping.multiEdge.distancePenalty",vee="org.eclipse.elk.layered.wrapping.multiEdge.improveWrappedEdges",yee="org.eclipse.elk.layered.layerUnzipping.strategy",kee="org.eclipse.elk.layered.layerUnzipping.minimizeEdgeLength",Eee="org.eclipse.elk.layered.layerUnzipping.layerSplit",jee="org.eclipse.elk.layered.layerUnzipping.resetOnLongEdges",Lwe="org.eclipse.elk.layered.edgeLabels.sideSelection",Pwe="org.eclipse.elk.layered.edgeLabels.centerLabelPlacementStrategy",SF="org.eclipse.elk.layered.considerModelOrder.strategy",$we="org.eclipse.elk.layered.considerModelOrder.portModelOrder",ON="org.eclipse.elk.layered.considerModelOrder.noModelOrder",See="org.eclipse.elk.layered.considerModelOrder.components",Rwe="org.eclipse.elk.layered.considerModelOrder.longEdgeStrategy",Aee="org.eclipse.elk.layered.considerModelOrder.crossingCounterNodeInfluence",Mee="org.eclipse.elk.layered.considerModelOrder.crossingCounterPortInfluence",Tee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cycleBreakingId",xee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.crossingMinimizationId",Cee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.componentGroupId",Bwe="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cbGroupOrderStrategy",Oee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cbPreferredSourceId",Nee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cbPreferredTargetId",zwe="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cmGroupOrderStrategy",Fwe="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cmEnforcedGroupOrders",Dee="layering",yQe="layering.minWidth",kQe="layering.nodePromotion",L8="crossingMinimization",AF="org.eclipse.elk.hierarchyHandling",EQe="crossingMinimization.greedySwitch",jQe="nodePlacement",SQe="nodePlacement.bk",AQe="edgeRouting",NN="org.eclipse.elk.edgeRouting",za="spacing",Hwe="priority",Jwe="compaction",MQe="compaction.postCompaction",TQe="Specifies whether and how post-process compaction is applied.",Gwe="highDegreeNodes",qwe="wrapping",xQe="wrapping.cutting",CQe="wrapping.validify",Uwe="wrapping.multiEdge",Iee="layerUnzipping",_ee="edgeLabels",vS="considerModelOrder",P8="considerModelOrder.groupModelOrder",Xwe="Group ID of the Node Type",Kwe="org.eclipse.elk.spacing.commentComment",Vwe="org.eclipse.elk.spacing.commentNode",Ywe="org.eclipse.elk.spacing.componentComponent",Qwe="org.eclipse.elk.spacing.edgeEdge",Lee="org.eclipse.elk.spacing.edgeNode",Zwe="org.eclipse.elk.spacing.labelLabel",Wwe="org.eclipse.elk.spacing.labelPortHorizontal",e2e="org.eclipse.elk.spacing.labelPortVertical",n2e="org.eclipse.elk.spacing.labelNode",t2e="org.eclipse.elk.spacing.nodeSelfLoop",i2e="org.eclipse.elk.spacing.portPort",r2e="org.eclipse.elk.spacing.individual",c2e="org.eclipse.elk.port.borderOffset",u2e="org.eclipse.elk.noLayout",o2e="org.eclipse.elk.port.side",DN="org.eclipse.elk.debugMode",s2e="org.eclipse.elk.alignment",l2e="org.eclipse.elk.insideSelfLoops.activate",f2e="org.eclipse.elk.insideSelfLoops.yo",Pee="org.eclipse.elk.direction",a2e="org.eclipse.elk.nodeLabels.padding",h2e="org.eclipse.elk.portLabels.nextToPortIfPossible",d2e="org.eclipse.elk.portLabels.treatAsGroup",b2e="org.eclipse.elk.portAlignment.default",g2e="org.eclipse.elk.portAlignment.north",w2e="org.eclipse.elk.portAlignment.south",p2e="org.eclipse.elk.portAlignment.west",m2e="org.eclipse.elk.portAlignment.east",MF="org.eclipse.elk.contentAlignment",v2e="org.eclipse.elk.junctionPoints",y2e="org.eclipse.elk.edge.thickness",k2e="org.eclipse.elk.edgeLabels.placement",E2e="org.eclipse.elk.port.index",j2e="org.eclipse.elk.commentBox",S2e="org.eclipse.elk.hypernode",A2e="org.eclipse.elk.port.anchor",$ee="org.eclipse.elk.partitioning.activate",Ree="org.eclipse.elk.partitioning.partition",TF="org.eclipse.elk.position",M2e="org.eclipse.elk.margins",T2e="org.eclipse.elk.spacing.portsSurrounding",xF="org.eclipse.elk.interactiveLayout",Pu="org.eclipse.elk.core.util",x2e={3:1,4:1,5:1,590:1},OQe="NETWORK_SIMPLEX",C2e="SIMPLE",uc={95:1,43:1},Bw="org.eclipse.elk.alg.layered.p1cycles",NQe="Depth-first cycle removal",DQe="Model order cycle breaking",F1="org.eclipse.elk.alg.layered.p2layers",O2e={406:1,220:1},IQe={830:1,3:1,4:1},$o="org.eclipse.elk.alg.layered.p3order",ov=17976931348623157e292,Bee=5e-324,Ic="org.eclipse.elk.alg.layered.p4nodes",_Qe={3:1,4:1,5:1,838:1},kh=1e-5,J0="org.eclipse.elk.alg.layered.p4nodes.bk",zee="org.eclipse.elk.alg.layered.p5edges",ha="org.eclipse.elk.alg.layered.p5edges.orthogonal",Fee="org.eclipse.elk.alg.layered.p5edges.orthogonal.direction",Hee=1e-6,Hp="org.eclipse.elk.alg.layered.p5edges.splines",Jee=.09999999999999998,CF=1e-8,LQe=4.71238898038469,PQe=1.5707963267948966,N2e=3.141592653589793,H1="org.eclipse.elk.alg.mrtree",Gee=.10000000149011612,OF="SUPER_ROOT",yS="org.eclipse.elk.alg.mrtree.graph",D2e=-17976931348623157e292,bo="org.eclipse.elk.alg.mrtree.intermediate",$Qe="Processor compute fanout",NF={3:1,6:1,4:1,5:1,522:1,90:1,110:1},RQe="Set neighbors in level",IN="org.eclipse.elk.alg.mrtree.options",BQe="DESCENDANTS",I2e="org.eclipse.elk.mrtree.compaction",_2e="org.eclipse.elk.mrtree.edgeEndTextureLength",L2e="org.eclipse.elk.mrtree.treeLevel",P2e="org.eclipse.elk.mrtree.positionConstraint",$2e="org.eclipse.elk.mrtree.weighting",R2e="org.eclipse.elk.mrtree.edgeRoutingMode",B2e="org.eclipse.elk.mrtree.searchOrder",zQe="Position Constraint",Ro="org.eclipse.elk.mrtree",FQe="org.eclipse.elk.tree",HQe="Processor arrange level",$8="org.eclipse.elk.alg.mrtree.p2order",Vs="org.eclipse.elk.alg.mrtree.p4route",z2e="org.eclipse.elk.alg.radial",ig=6.283185307179586,F2e="Before",DF="After",H2e="org.eclipse.elk.alg.radial.intermediate",JQe="COMPACTION",qee="org.eclipse.elk.alg.radial.intermediate.compaction",GQe={3:1,4:1,5:1,90:1},J2e="org.eclipse.elk.alg.radial.intermediate.optimization",Uee="No implementation is available for the layout option ",kS="org.eclipse.elk.alg.radial.options",qQe="CompactionStrategy",G2e="org.eclipse.elk.radial.centerOnRoot",q2e="org.eclipse.elk.radial.orderId",U2e="org.eclipse.elk.radial.radius",IF="org.eclipse.elk.radial.rotate",Xee="org.eclipse.elk.radial.compactor",Kee="org.eclipse.elk.radial.compactionStepSize",X2e="org.eclipse.elk.radial.sorter",K2e="org.eclipse.elk.radial.wedgeCriteria",V2e="org.eclipse.elk.radial.optimizationCriteria",Vee="org.eclipse.elk.radial.rotation.targetAngle",Yee="org.eclipse.elk.radial.rotation.computeAdditionalWedgeSpace",Y2e="org.eclipse.elk.radial.rotation.outgoingEdgeAngles",UQe="Compaction",Q2e="rotation",Fl="org.eclipse.elk.radial",XQe="org.eclipse.elk.alg.radial.p1position.wedge",Z2e="org.eclipse.elk.alg.radial.sorting",KQe=5.497787143782138,VQe=3.9269908169872414,YQe=2.356194490192345,QQe="org.eclipse.elk.alg.rectpacking",ES="org.eclipse.elk.alg.rectpacking.intermediate",Qee="org.eclipse.elk.alg.rectpacking.options",W2e="org.eclipse.elk.rectpacking.trybox",epe="org.eclipse.elk.rectpacking.currentPosition",npe="org.eclipse.elk.rectpacking.desiredPosition",tpe="org.eclipse.elk.rectpacking.inNewRow",ipe="org.eclipse.elk.rectpacking.orderBySize",rpe="org.eclipse.elk.rectpacking.widthApproximation.strategy",cpe="org.eclipse.elk.rectpacking.widthApproximation.targetWidth",upe="org.eclipse.elk.rectpacking.widthApproximation.optimizationGoal",ope="org.eclipse.elk.rectpacking.widthApproximation.lastPlaceShift",spe="org.eclipse.elk.rectpacking.packing.strategy",lpe="org.eclipse.elk.rectpacking.packing.compaction.rowHeightReevaluation",fpe="org.eclipse.elk.rectpacking.packing.compaction.iterations",ape="org.eclipse.elk.rectpacking.whiteSpaceElimination.strategy",Zee="widthApproximation",ZQe="Compaction Strategy",WQe="packing.compaction",ps="org.eclipse.elk.rectpacking",R8="org.eclipse.elk.alg.rectpacking.p1widthapproximation",_F="org.eclipse.elk.alg.rectpacking.p2packing",eZe="No Compaction",hpe="org.eclipse.elk.alg.rectpacking.p3whitespaceelimination",_N="org.eclipse.elk.alg.rectpacking.util",LF="No implementation available for ",Jp="org.eclipse.elk.alg.spore",Gp="org.eclipse.elk.alg.spore.options",zw="org.eclipse.elk.sporeCompaction",Wee="org.eclipse.elk.underlyingLayoutAlgorithm",dpe="org.eclipse.elk.processingOrder.treeConstruction",bpe="org.eclipse.elk.processingOrder.spanningTreeCostFunction",ene="org.eclipse.elk.processingOrder.preferredRoot",nne="org.eclipse.elk.processingOrder.rootSelection",tne="org.eclipse.elk.structure.structureExtractionStrategy",gpe="org.eclipse.elk.compaction.compactionStrategy",wpe="org.eclipse.elk.compaction.orthogonal",ppe="org.eclipse.elk.overlapRemoval.maxIterations",mpe="org.eclipse.elk.overlapRemoval.runScanline",ine="processingOrder",nZe="overlapRemoval",B8="org.eclipse.elk.sporeOverlap",tZe="org.eclipse.elk.alg.spore.p1structure",rne="org.eclipse.elk.alg.spore.p2processingorder",cne="org.eclipse.elk.alg.spore.p3execution",iZe="Topdown Layout",rZe="Invalid index: ",z8="org.eclipse.elk.core.alg",sv={342:1},qp={296:1},cZe="Make sure its type is registered with the ",vpe=" utility class.",F8="true",une="false",uZe="Couldn't clone property '",Fw=.05,Co="org.eclipse.elk.core.options",oZe=1.2999999523162842,Hw="org.eclipse.elk.box",ype="org.eclipse.elk.expandNodes",kpe="org.eclipse.elk.box.packingMode",sZe="org.eclipse.elk.algorithm",lZe="org.eclipse.elk.resolvedAlgorithm",Epe="org.eclipse.elk.bendPoints",aBn="org.eclipse.elk.labelManager",fZe="org.eclipse.elk.softwrappingFuzziness",aZe="org.eclipse.elk.scaleFactor",hZe="org.eclipse.elk.childAreaWidth",dZe="org.eclipse.elk.childAreaHeight",bZe="org.eclipse.elk.animate",gZe="org.eclipse.elk.animTimeFactor",wZe="org.eclipse.elk.layoutAncestors",pZe="org.eclipse.elk.maxAnimTime",mZe="org.eclipse.elk.minAnimTime",vZe="org.eclipse.elk.progressBar",yZe="org.eclipse.elk.validateGraph",kZe="org.eclipse.elk.validateOptions",EZe="org.eclipse.elk.zoomToFit",jZe="org.eclipse.elk.json.shapeCoords",SZe="org.eclipse.elk.json.edgeCoords",hBn="org.eclipse.elk.font.name",AZe="org.eclipse.elk.font.size",one="org.eclipse.elk.topdown.sizeCategories",jpe="org.eclipse.elk.topdown.sizeCategoriesHierarchicalNodeWeight",sne="org.eclipse.elk.topdown.sizeApproximator",Spe="org.eclipse.elk.topdown.scaleCap",MZe="org.eclipse.elk.edge.type",TZe="partitioning",xZe="nodeLabels",PF="portAlignment",lne="nodeSize",fne="port",Ape="portLabels",H8="topdown",CZe="insideSelfLoops",Mpe="INHERIT",J8="org.eclipse.elk.fixed",$F="org.eclipse.elk.random",RF={3:1,35:1,23:1,521:1,288:1},OZe="port must have a parent node to calculate the port side",NZe="The edge needs to have exactly one edge section. Found: ",jS="org.eclipse.elk.core.util.adapters",Hl="org.eclipse.emf.ecore",lv="org.eclipse.elk.graph",DZe="EMapPropertyHolder",IZe="ElkBendPoint",_Ze="ElkGraphElement",LZe="ElkConnectableShape",Tpe="ElkEdge",PZe="ElkEdgeSection",$Ze="EModelElement",RZe="ENamedElement",xpe="ElkLabel",Cpe="ElkNode",Ope="ElkPort",BZe={94:1,93:1},c6="org.eclipse.emf.common.notify.impl",G0="The feature '",SS="' is not a valid changeable feature",zZe="Expecting null",ane="' is not a valid feature",FZe="The feature ID",HZe=" is not a valid feature ID",$u=32768,JZe={109:1,94:1,93:1,57:1,52:1,100:1},Fn="org.eclipse.emf.ecore.impl",rg="org.eclipse.elk.graph.impl",AS="Recursive containment not allowed for ",G8="The datatype '",Jw="' is not a valid classifier",hne="The value '",fv={195:1,3:1,4:1},dne="The class '",q8="http://www.eclipse.org/elk/ElkGraph",Npe="property",MS="value",bne="source",GZe="properties",qZe="identifier",gne="height",wne="width",pne="parent",mne="text",vne="children",UZe="hierarchical",Dpe="sources",yne="targets",kne="sections",BF="bendPoints",Ipe="outgoingShape",_pe="incomingShape",Lpe="outgoingSections",Ppe="incomingSections",vc="org.eclipse.emf.common.util",$pe="Severe implementation error in the Json to ElkGraph importer.",Eh="id",Qr="org.eclipse.elk.graph.json",U8="Unhandled parameter types: ",XZe="startPoint",KZe="An edge must have at least one source and one target (edge id: '",X8="').",VZe="Referenced edge section does not exist: ",YZe=" (edge id: '",Rpe="target",QZe="sourcePoint",ZZe="targetPoint",zF="group",ii="name",WZe="connectableShape cannot be null",eWe="edge cannot be null",nWe="Passed edge is not 'simple'.",FF="org.eclipse.elk.graph.util",LN="The 'no duplicates' constraint is violated",Ene="targetIndex=",cg=", size=",jne="sourceIndex=",jh={3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,67:1,61:1},Sne={3:1,4:1,20:1,31:1,56:1,18:1,50:1,16:1,59:1,71:1,67:1,61:1,585:1},HF="logging",tWe="measureExecutionTime",iWe="parser.parse.1",rWe="parser.parse.2",JF="parser.next.1",Ane="parser.next.2",cWe="parser.next.3",uWe="parser.next.4",ug="parser.factor.1",Bpe="parser.factor.2",oWe="parser.factor.3",sWe="parser.factor.4",lWe="parser.factor.5",fWe="parser.factor.6",aWe="parser.atom.1",hWe="parser.atom.2",dWe="parser.atom.3",zpe="parser.atom.4",Mne="parser.atom.5",Fpe="parser.cc.1",GF="parser.cc.2",bWe="parser.cc.3",gWe="parser.cc.5",Hpe="parser.cc.6",Jpe="parser.cc.7",Tne="parser.cc.8",wWe="parser.ope.1",pWe="parser.ope.2",mWe="parser.ope.3",Ld="parser.descape.1",vWe="parser.descape.2",yWe="parser.descape.3",kWe="parser.descape.4",EWe="parser.descape.5",Jl="parser.process.1",jWe="parser.quantifier.1",SWe="parser.quantifier.2",AWe="parser.quantifier.3",MWe="parser.quantifier.4",Gpe="parser.quantifier.5",TWe="org.eclipse.emf.common.notify",qpe={415:1,676:1},xWe={3:1,4:1,20:1,31:1,56:1,18:1,16:1,71:1,61:1},PN={373:1,151:1},TS="index=",xne={3:1,4:1,5:1,129:1},CWe={3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,61:1},Upe={3:1,6:1,4:1,5:1,198:1},OWe={3:1,4:1,5:1,175:1,374:1},Rf=1024,NWe=";/?:@&=+$,",DWe="invalid authority: ",IWe="EAnnotation",_We="ETypedElement",LWe="EStructuralFeature",PWe="EAttribute",$We="EClassifier",RWe="EEnumLiteral",BWe="EGenericType",zWe="EOperation",FWe="EParameter",HWe="EReference",JWe="ETypeParameter",$i="org.eclipse.emf.ecore.util",Cne={77:1},Xpe={3:1,20:1,18:1,16:1,61:1,586:1,77:1,72:1,98:1},GWe="org.eclipse.emf.ecore.util.FeatureMap$Entry",fs=8192,xS="byte",qF="char",CS="double",OS="float",NS="int",DS="long",IS="short",qWe="java.lang.Object",av={3:1,4:1,5:1,255:1},Kpe={3:1,4:1,5:1,678:1},UWe={3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,67:1,61:1,72:1},lu={3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,67:1,61:1,77:1,72:1,98:1},$N="mixed",zt="http:///org/eclipse/emf/ecore/util/ExtendedMetaData",sf="kind",XWe={3:1,4:1,5:1,679:1},Vpe={3:1,4:1,20:1,31:1,56:1,18:1,16:1,71:1,61:1,77:1,72:1,98:1},UF={20:1,31:1,56:1,18:1,16:1,61:1,72:1},XF={50:1,128:1,287:1},KF={75:1,344:1},VF="The value of type '",YF="' must be of type '",hv=1306,lf="http://www.eclipse.org/emf/2002/Ecore",QF=-32768,Gw="constraints",oc="baseType",KWe="getEStructuralFeature",VWe="getFeatureID",_S="feature",YWe="getOperationID",Ype="operation",QWe="defaultValue",ZWe="eTypeParameters",WWe="isInstance",een="getEEnumLiteral",nen="eContainingClass",ei={58:1},ten={3:1,4:1,5:1,122:1},ien="org.eclipse.emf.ecore.resource",ren={94:1,93:1,588:1,1996:1},One="org.eclipse.emf.ecore.resource.impl",Qpe="unspecified",RN="simple",ZF="attribute",cen="attributeWildcard",WF="element",Nne="elementWildcard",da="collapse",Dne="itemType",eH="namespace",BN="##targetNamespace",ff="whiteSpace",Zpe="wildcards",og="http://www.eclipse.org/emf/2003/XMLType",Ine="##any",K8="uninitialized",zN="The multiplicity constraint is violated",nH="org.eclipse.emf.ecore.xml.type",uen="ProcessingInstruction",oen="SimpleAnyType",sen="XMLTypeDocumentRoot",kr="org.eclipse.emf.ecore.xml.type.impl",FN="INF",len="processing",fen="ENTITIES_._base",Wpe="minLength",eme="ENTITY",tH="NCName",aen="IDREFS_._base",nme="integer",_ne="token",Lne="pattern",hen="[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*",tme="\\i\\c*",den="[\\i-[:]][\\c-[:]]*",ben="nonPositiveInteger",HN="maxInclusive",ime="NMTOKEN",gen="NMTOKENS_._base",rme="nonNegativeInteger",JN="minInclusive",wen="normalizedString",pen="unsignedByte",men="unsignedInt",ven="18446744073709551615",yen="unsignedShort",ken="processingInstruction",Pd="org.eclipse.emf.ecore.xml.type.internal",V8=1114111,Een="Internal Error: shorthands: \\u",LS="xml:isDigit",Pne="xml:isWord",$ne="xml:isSpace",Rne="xml:isNameChar",Bne="xml:isInitialNameChar",jen="09٠٩۰۹०९০৯੦੯૦૯୦୯௧௯౦౯೦೯൦൯๐๙໐໙༠༩",Sen="AZazÀÖØöøıĴľŁňŊžƀǃǍǰǴǵǺȗɐʨʻˁΆΆΈΊΌΌΎΡΣώϐϖϚϚϜϜϞϞϠϠϢϳЁЌЎяёќўҁҐӄӇӈӋӌӐӫӮӵӸӹԱՖՙՙաֆאתװײءغفيٱڷںھۀێېۓەەۥۦअहऽऽक़ॡঅঌএঐওনপরললশহড়ঢ়য়ৡৰৱਅਊਏਐਓਨਪਰਲਲ਼ਵਸ਼ਸਹਖ਼ੜਫ਼ਫ਼ੲੴઅઋઍઍએઑઓનપરલળવહઽઽૠૠଅଌଏଐଓନପରଲଳଶହଽଽଡ଼ଢ଼ୟୡஅஊஎஐஒகஙசஜஜஞடணதநபமவஷஹఅఌఎఐఒనపళవహౠౡಅಌಎಐಒನಪಳವಹೞೞೠೡഅഌഎഐഒനപഹൠൡกฮะะาำเๅກຂຄຄງຈຊຊຍຍດທນຟມຣລລວວສຫອຮະະາຳຽຽເໄཀཇཉཀྵႠჅაჶᄀᄀᄂᄃᄅᄇᄉᄉᄋᄌᄎᄒᄼᄼᄾᄾᅀᅀᅌᅌᅎᅎᅐᅐᅔᅕᅙᅙᅟᅡᅣᅣᅥᅥᅧᅧᅩᅩᅭᅮᅲᅳᅵᅵᆞᆞᆨᆨᆫᆫᆮᆯᆷᆸᆺᆺᆼᇂᇫᇫᇰᇰᇹᇹḀẛẠỹἀἕἘἝἠὅὈὍὐὗὙὙὛὛὝὝὟώᾀᾴᾶᾼιιῂῄῆῌῐΐῖΊῠῬῲῴῶῼΩΩKÅ℮℮ↀↂ〇〇〡〩ぁゔァヺㄅㄬ一龥가힣",Aen="Private Use",zne="ASSIGNED",Fne="\0€ÿĀſƀɏɐʯʰ˿̀ͯͰϿЀӿ԰֏֐׿؀ۿ܀ݏހ޿ऀॿঀ৿਀੿઀૿଀୿஀௿ఀ౿ಀ೿ഀൿ඀෿฀๿຀໿ༀ࿿က႟Ⴀჿᄀᇿሀ፿Ꭰ᏿᐀ᙿ ᚟ᚠ᛿ក៿᠀᢯Ḁỿἀ῿ ⁰₟₠⃏⃐⃿℀⅏⅐↏←⇿∀⋿⌀⏿␀␿⑀⑟①⓿─╿▀▟■◿☀⛿✀➿⠀⣿⺀⻿⼀⿟⿰⿿ 〿぀ゟ゠ヿ㄀ㄯ㄰㆏㆐㆟ㆠㆿ㈀㋿㌀㏿㐀䶵一鿿ꀀ꒏꒐꓏가힣豈﫿ffﭏﭐ﷿︠︯︰﹏﹐﹯ﹰ﻾\uFEFF\uFEFF＀￯",cme="UNASSIGNED",Y8={3:1,121:1},Men="org.eclipse.emf.ecore.xml.type.util",iH={3:1,4:1,5:1,376:1},ume="org.eclipse.xtext.xbase.lib",Ten="Cannot add elements to a Range",xen="Cannot set elements in a Range",Cen="Cannot remove elements from a Range",Oen="user.agent",s,rH,Hne;E.goog=E.goog||{},E.goog.global=E.goog.global||E,rH={},m(1,null,{},U),s.Fb=function(n){return cCe(this,n)},s.Gb=function(){return this.Pm},s.Hb=function(){return cw(this)},s.Ib=function(){var n;return Sb(Gs(this))+"@"+(n=Oi(this)>>>0,n.toString(16))},s.equals=function(e){return this.Fb(e)},s.hashCode=function(){return this.Hb()},s.toString=function(){return this.Ib()};var Nen,Den,Ien;m(298,1,{298:1,2086:1},u1e),s.te=function(n){var t;return t=new u1e,t.i=4,n>1?t.c=__e(this,n-1):t.c=this,t},s.ue=function(){return E1(this),this.b},s.ve=function(){return Sb(this)},s.we=function(){return E1(this),this.k},s.xe=function(){return(this.i&4)!=0},s.ye=function(){return(this.i&1)!=0},s.Ib=function(){return whe(this)},s.i=0;var Mr=v(Mu,"Object",1),ome=v(Mu,"Class",298);m(2058,1,oN),v(sN,"Optional",2058),m(1160,2058,oN,J),s.Fb=function(n){return n===this},s.Hb=function(){return 2040732332},s.Ib=function(){return"Optional.absent()"},s.Jb=function(n){return Tt(n),sE(),Jne};var Jne;v(sN,"Absent",1160),m(627,1,{},MX),v(sN,"Joiner",627);var dBn=Ji(sN,"Predicate");m(577,1,{178:1,577:1,3:1,48:1},Cx),s.Mb=function(n){return Ize(this,n)},s.Lb=function(n){return Ize(this,n)},s.Fb=function(n){var t;return q(n,577)?(t=u(n,577),tbe(this.a,t.a)):!1},s.Hb=function(){return a1e(this.a)+306654252},s.Ib=function(){return dxn(this.a)},v(sN,"Predicates/AndPredicate",577),m(411,2058,{411:1,3:1},Ry),s.Fb=function(n){var t;return q(n,411)?(t=u(n,411),ai(this.a,t.a)):!1},s.Hb=function(){return 1502476572+Oi(this.a)},s.Ib=function(){return WVe+this.a+")"},s.Jb=function(n){return new Ry(TR(n.Kb(this.a),"the Function passed to Optional.transform() must not return null."))},v(sN,"Present",411),m(204,1,v8),s.Nb=function(n){Zr(this,n)},s.Qb=function(){VAe()},v(fn,"UnmodifiableIterator",204),m(2038,204,y8),s.Qb=function(){VAe()},s.Rb=function(n){throw P(new Ot)},s.Wb=function(n){throw P(new Ot)},v(fn,"UnmodifiableListIterator",2038),m(392,2038,y8),s.Ob=function(){return this.b0},s.Pb=function(){if(this.b>=this.c)throw P(new fu);return this.Xb(this.b++)},s.Tb=function(){return this.b},s.Ub=function(){if(this.b<=0)throw P(new fu);return this.Xb(--this.b)},s.Vb=function(){return this.b-1},s.b=0,s.c=0,v(fn,"AbstractIndexedListIterator",392),m(702,204,v8),s.Ob=function(){return OY(this)},s.Pb=function(){return fhe(this)},s.e=1,v(fn,"AbstractIterator",702),m(2046,1,{229:1}),s.Zb=function(){var n;return n=this.f,n||(this.f=this.ac())},s.Fb=function(n){return ZY(this,n)},s.Hb=function(){return Oi(this.Zb())},s.dc=function(){return this.gc()==0},s.ec=function(){return d4(this)},s.Ib=function(){return su(this.Zb())},v(fn,"AbstractMultimap",2046),m(730,2046,Wb),s.$b=function(){wB(this)},s._b=function(n){return hMe(this,n)},s.ac=function(){return new n9(this,this.c)},s.ic=function(n){return this.hc()},s.bc=function(){return new T3(this,this.c)},s.jc=function(){return this.mc(this.hc())},s.kc=function(){return new _Ae(this)},s.lc=function(){return cZ(this.c.vc().Lc(),new Z,64,this.d)},s.cc=function(n){return wi(this,n)},s.fc=function(n){return EO(this,n)},s.gc=function(){return this.d},s.mc=function(n){return vn(),new Hr(n)},s.nc=function(){return new IAe(this)},s.oc=function(){return cZ(this.c.Bc().Lc(),new te,64,this.d)},s.pc=function(n,t){return new QR(this,n,t,null)},s.d=0,v(fn,"AbstractMapBasedMultimap",730),m(1661,730,Wb),s.hc=function(){return new So(this.a)},s.jc=function(){return vn(),vn(),Ec},s.cc=function(n){return u(wi(this,n),16)},s.fc=function(n){return u(EO(this,n),16)},s.Zb=function(){return v4(this)},s.Fb=function(n){return ZY(this,n)},s.qc=function(n){return u(wi(this,n),16)},s.rc=function(n){return u(EO(this,n),16)},s.mc=function(n){return xR(u(n,16))},s.pc=function(n,t){return JLe(this,n,u(t,16),null)},v(fn,"AbstractListMultimap",1661),m(736,1,zr),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.c.Ob()||this.e.Ob()},s.Pb=function(){var n;return this.e.Ob()||(n=u(this.c.Pb(),45),this.b=n.jd(),this.a=u(n.kd(),18),this.e=this.a.Jc()),this.sc(this.b,this.e.Pb())},s.Qb=function(){this.e.Qb(),u(tf(this.a),18).dc()&&this.c.Qb(),--this.d.d},v(fn,"AbstractMapBasedMultimap/Itr",736),m(1098,736,zr,IAe),s.sc=function(n,t){return t},v(fn,"AbstractMapBasedMultimap/1",1098),m(1099,1,{},te),s.Kb=function(n){return u(n,18).Lc()},v(fn,"AbstractMapBasedMultimap/1methodref$spliterator$Type",1099),m(1100,736,zr,_Ae),s.sc=function(n,t){return new ew(n,t)},v(fn,"AbstractMapBasedMultimap/2",1100);var sme=Ji(wt,"Map");m(2027,1,Lw),s.wc=function(n){hO(this,n)},s.$b=function(){this.vc().$b()},s.tc=function(n){return FQ(this,n)},s._b=function(n){return!!e0e(this,n,!1)},s.uc=function(n){var t,i,r;for(i=this.vc().Jc();i.Ob();)if(t=u(i.Pb(),45),r=t.kd(),ue(n)===ue(r)||n!=null&&ai(n,r))return!0;return!1},s.Fb=function(n){var t,i,r;if(n===this)return!0;if(!q(n,92)||(r=u(n,92),this.gc()!=r.gc()))return!1;for(i=r.vc().Jc();i.Ob();)if(t=u(i.Pb(),45),!this.tc(t))return!1;return!0},s.xc=function(n){return hu(e0e(this,n,!1))},s.Hb=function(){return t1e(this.vc())},s.dc=function(){return this.gc()==0},s.ec=function(){return new et(this)},s.yc=function(n,t){throw P(new ld("Put not supported on this map"))},s.zc=function(n){gj(this,n)},s.Ac=function(n){return hu(e0e(this,n,!0))},s.gc=function(){return this.vc().gc()},s.Ib=function(){return ZJe(this)},s.Bc=function(){return new rt(this)},v(wt,"AbstractMap",2027),m(2047,2027,Lw),s.bc=function(){return new XP(this)},s.vc=function(){return NDe(this)},s.ec=function(){var n;return n=this.g,n||(this.g=this.bc())},s.Bc=function(){var n;return n=this.i,n||(this.i=new iTe(this))},v(fn,"Maps/ViewCachingAbstractMap",2047),m(395,2047,Lw,n9),s.xc=function(n){return l8n(this,n)},s.Ac=function(n){return ykn(this,n)},s.$b=function(){this.d==this.e.c?this.e.$b():rR(new afe(this))},s._b=function(n){return aFe(this.d,n)},s.Dc=function(){return new aP(this)},s.Cc=function(){return this.Dc()},s.Fb=function(n){return this===n||ai(this.d,n)},s.Hb=function(){return Oi(this.d)},s.ec=function(){return this.e.ec()},s.gc=function(){return this.d.gc()},s.Ib=function(){return su(this.d)},v(fn,"AbstractMapBasedMultimap/AsMap",395);var Gl=Ji(Mu,"Iterable");m(31,1,Pp),s.Ic=function(n){rc(this,n)},s.Lc=function(){return new wn(this,0)},s.Mc=function(){return new bn(null,this.Lc())},s.Ec=function(n){throw P(new ld("Add not supported on this collection"))},s.Fc=function(n){return fc(this,n)},s.$b=function(){Vfe(this)},s.Gc=function(n){return Ep(this,n,!1)},s.Hc=function(n){return mO(this,n)},s.dc=function(){return this.gc()==0},s.Kc=function(n){return Ep(this,n,!0)},s.Nc=function(){return kfe(this)},s.Oc=function(n){return _j(this,n)},s.Ib=function(){return _a(this)},v(wt,"AbstractCollection",31);var af=Ji(wt,"Set");m(Pa,31,ls),s.Lc=function(){return new wn(this,1)},s.Fb=function(n){return dHe(this,n)},s.Hb=function(){return t1e(this)},v(wt,"AbstractSet",Pa),m(2030,Pa,ls),v(fn,"Sets/ImprovedAbstractSet",2030),m(2031,2030,ls),s.$b=function(){this.Pc().$b()},s.Gc=function(n){return KFe(this,n)},s.dc=function(){return this.Pc().dc()},s.Kc=function(n){var t;return this.Gc(n)&&q(n,45)?(t=u(n,45),this.Pc().ec().Kc(t.jd())):!1},s.gc=function(){return this.Pc().gc()},v(fn,"Maps/EntrySet",2031),m(1096,2031,ls,aP),s.Gc=function(n){return x1e(this.a.d.vc(),n)},s.Jc=function(){return new afe(this.a)},s.Pc=function(){return this.a},s.Kc=function(n){var t;return x1e(this.a.d.vc(),n)?(t=u(tf(u(n,45)),45),Xyn(this.a.e,t.jd()),!0):!1},s.Lc=function(){return CC(this.a.d.vc().Lc(),new hP(this.a))},v(fn,"AbstractMapBasedMultimap/AsMap/AsMapEntries",1096),m(1097,1,{},hP),s.Kb=function(n){return MPe(this.a,u(n,45))},v(fn,"AbstractMapBasedMultimap/AsMap/AsMapEntries/0methodref$wrapEntry$Type",1097),m(734,1,zr,afe),s.Nb=function(n){Zr(this,n)},s.Pb=function(){var n;return n=u(this.b.Pb(),45),this.a=u(n.kd(),18),MPe(this.c,n)},s.Ob=function(){return this.b.Ob()},s.Qb=function(){a9(!!this.a),this.b.Qb(),this.c.e.d-=this.a.gc(),this.a.$b(),this.a=null},v(fn,"AbstractMapBasedMultimap/AsMap/AsMapIterator",734),m(530,2030,ls,XP),s.$b=function(){this.b.$b()},s.Gc=function(n){return this.b._b(n)},s.Ic=function(n){Tt(n),this.b.wc(new Vk(n))},s.dc=function(){return this.b.dc()},s.Jc=function(){return new lE(this.b.vc().Jc())},s.Kc=function(n){return this.b._b(n)?(this.b.Ac(n),!0):!1},s.gc=function(){return this.b.gc()},v(fn,"Maps/KeySet",530),m(332,530,ls,T3),s.$b=function(){var n;rR((n=this.b.vc().Jc(),new $oe(this,n)))},s.Hc=function(n){return this.b.ec().Hc(n)},s.Fb=function(n){return this===n||ai(this.b.ec(),n)},s.Hb=function(){return Oi(this.b.ec())},s.Jc=function(){var n;return n=this.b.vc().Jc(),new $oe(this,n)},s.Kc=function(n){var t,i;return i=0,t=u(this.b.Ac(n),18),t&&(i=t.gc(),t.$b(),this.a.d-=i),i>0},s.Lc=function(){return this.b.ec().Lc()},v(fn,"AbstractMapBasedMultimap/KeySet",332),m(735,1,zr,$oe),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.c.Ob()},s.Pb=function(){return this.a=u(this.c.Pb(),45),this.a.jd()},s.Qb=function(){var n;a9(!!this.a),n=u(this.a.kd(),18),this.c.Qb(),this.b.a.d-=n.gc(),n.$b(),this.a=null},v(fn,"AbstractMapBasedMultimap/KeySet/1",735),m(489,395,{92:1,134:1},EC),s.bc=function(){return this.Qc()},s.ec=function(){return this.Sc()},s.Qc=function(){return new Yx(this.c,this.Uc())},s.Rc=function(){return this.Uc().Rc()},s.Sc=function(){var n;return n=this.b,n||(this.b=this.Qc())},s.Tc=function(){return this.Uc().Tc()},s.Uc=function(){return u(this.d,134)},v(fn,"AbstractMapBasedMultimap/SortedAsMap",489),m(437,489,xge,JE),s.bc=function(){return new t9(this.a,u(u(this.d,134),138))},s.Qc=function(){return new t9(this.a,u(u(this.d,134),138))},s.ec=function(){var n;return n=this.b,u(n||(this.b=new t9(this.a,u(u(this.d,134),138))),277)},s.Sc=function(){var n;return n=this.b,u(n||(this.b=new t9(this.a,u(u(this.d,134),138))),277)},s.Uc=function(){return u(u(this.d,134),138)},s.Vc=function(n){return u(u(this.d,134),138).Vc(n)},s.Wc=function(n){return u(u(this.d,134),138).Wc(n)},s.Xc=function(n,t){return new JE(this.a,u(u(this.d,134),138).Xc(n,t))},s.Yc=function(n){return u(u(this.d,134),138).Yc(n)},s.Zc=function(n){return u(u(this.d,134),138).Zc(n)},s.$c=function(n,t){return new JE(this.a,u(u(this.d,134),138).$c(n,t))},v(fn,"AbstractMapBasedMultimap/NavigableAsMap",437),m(488,332,eYe,Yx),s.Lc=function(){return this.b.ec().Lc()},v(fn,"AbstractMapBasedMultimap/SortedKeySet",488),m(394,488,Cge,t9),v(fn,"AbstractMapBasedMultimap/NavigableKeySet",394),m(539,31,Pp,QR),s.Ec=function(n){var t,i;return Ns(this),i=this.d.dc(),t=this.d.Ec(n),t&&(++this.f.d,i&&MC(this)),t},s.Fc=function(n){var t,i,r;return n.dc()?!1:(r=(Ns(this),this.d.gc()),t=this.d.Fc(n),t&&(i=this.d.gc(),this.f.d+=i-r,r==0&&MC(this)),t)},s.$b=function(){var n;n=(Ns(this),this.d.gc()),n!=0&&(this.d.$b(),this.f.d-=n,fR(this))},s.Gc=function(n){return Ns(this),this.d.Gc(n)},s.Hc=function(n){return Ns(this),this.d.Hc(n)},s.Fb=function(n){return n===this?!0:(Ns(this),ai(this.d,n))},s.Hb=function(){return Ns(this),Oi(this.d)},s.Jc=function(){return Ns(this),new Vle(this)},s.Kc=function(n){var t;return Ns(this),t=this.d.Kc(n),t&&(--this.f.d,fR(this)),t},s.gc=function(){return Xxe(this)},s.Lc=function(){return Ns(this),this.d.Lc()},s.Ib=function(){return Ns(this),su(this.d)},v(fn,"AbstractMapBasedMultimap/WrappedCollection",539);var hl=Ji(wt,"List");m(732,539,{20:1,31:1,18:1,16:1},Efe),s.gd=function(n){Fb(this,n)},s.Lc=function(){return Ns(this),this.d.Lc()},s._c=function(n,t){var i;Ns(this),i=this.d.dc(),u(this.d,16)._c(n,t),++this.a.d,i&&MC(this)},s.ad=function(n,t){var i,r,c;return t.dc()?!1:(c=(Ns(this),this.d.gc()),i=u(this.d,16).ad(n,t),i&&(r=this.d.gc(),this.a.d+=r-c,c==0&&MC(this)),i)},s.Xb=function(n){return Ns(this),u(this.d,16).Xb(n)},s.bd=function(n){return Ns(this),u(this.d,16).bd(n)},s.cd=function(){return Ns(this),new SCe(this)},s.dd=function(n){return Ns(this),new GIe(this,n)},s.ed=function(n){var t;return Ns(this),t=u(this.d,16).ed(n),--this.a.d,fR(this),t},s.fd=function(n,t){return Ns(this),u(this.d,16).fd(n,t)},s.hd=function(n,t){return Ns(this),JLe(this.a,this.e,u(this.d,16).hd(n,t),this.b?this.b:this)},v(fn,"AbstractMapBasedMultimap/WrappedList",732),m(1095,732,{20:1,31:1,18:1,16:1,59:1},hOe),v(fn,"AbstractMapBasedMultimap/RandomAccessWrappedList",1095),m(619,1,zr,Vle),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return E9(this),this.b.Ob()},s.Pb=function(){return E9(this),this.b.Pb()},s.Qb=function(){VCe(this)},v(fn,"AbstractMapBasedMultimap/WrappedCollection/WrappedIterator",619),m(733,619,Uh,SCe,GIe),s.Qb=function(){VCe(this)},s.Rb=function(n){var t;t=Xxe(this.a)==0,(E9(this),u(this.b,128)).Rb(n),++this.a.a.d,t&&MC(this.a)},s.Sb=function(){return(E9(this),u(this.b,128)).Sb()},s.Tb=function(){return(E9(this),u(this.b,128)).Tb()},s.Ub=function(){return(E9(this),u(this.b,128)).Ub()},s.Vb=function(){return(E9(this),u(this.b,128)).Vb()},s.Wb=function(n){(E9(this),u(this.b,128)).Wb(n)},v(fn,"AbstractMapBasedMultimap/WrappedList/WrappedListIterator",733),m(731,539,eYe,gle),s.Lc=function(){return Ns(this),this.d.Lc()},v(fn,"AbstractMapBasedMultimap/WrappedSortedSet",731),m(1094,731,Cge,vCe),v(fn,"AbstractMapBasedMultimap/WrappedNavigableSet",1094),m(1093,539,ls,ROe),s.Lc=function(){return Ns(this),this.d.Lc()},v(fn,"AbstractMapBasedMultimap/WrappedSet",1093),m(1102,1,{},Z),s.Kb=function(n){return i9n(u(n,45))},v(fn,"AbstractMapBasedMultimap/lambda$1$Type",1102),m(1101,1,{},By),s.Kb=function(n){return new ew(this.a,n)},v(fn,"AbstractMapBasedMultimap/lambda$2$Type",1101);var sg=Ji(wt,"Map/Entry");m(358,1,oW),s.Fb=function(n){var t;return q(n,45)?(t=u(n,45),j1(this.jd(),t.jd())&&j1(this.kd(),t.kd())):!1},s.Hb=function(){var n,t;return n=this.jd(),t=this.kd(),(n==null?0:Oi(n))^(t==null?0:Oi(t))},s.ld=function(n){throw P(new Ot)},s.Ib=function(){return this.jd()+"="+this.kd()},v(fn,nYe,358),m(R0,31,Pp),s.$b=function(){this.md().$b()},s.Gc=function(n){var t;return q(n,45)?(t=u(n,45),A6n(this.md(),t.jd(),t.kd())):!1},s.Kc=function(n){var t;return q(n,45)?(t=u(n,45),xLe(this.md(),t.jd(),t.kd())):!1},s.gc=function(){return this.md().d},v(fn,"Multimaps/Entries",R0),m(737,R0,Pp,m1),s.Jc=function(){return this.a.kc()},s.md=function(){return this.a},s.Lc=function(){return this.a.lc()},v(fn,"AbstractMultimap/Entries",737),m(738,737,ls,woe),s.Lc=function(){return this.a.lc()},s.Fb=function(n){return y0e(this,n)},s.Hb=function(){return DBe(this)},v(fn,"AbstractMultimap/EntrySet",738),m(739,31,Pp,Ox),s.$b=function(){this.a.$b()},s.Gc=function(n){return wkn(this.a,n)},s.Jc=function(){return this.a.nc()},s.gc=function(){return this.a.d},s.Lc=function(){return this.a.oc()},v(fn,"AbstractMultimap/Values",739),m(2049,31,{833:1,20:1,31:1,18:1}),s.Ic=function(n){Tt(n),C3(this).Ic(new GU(n))},s.Lc=function(){var n;return n=C3(this).Lc(),cZ(n,new Le,64|n.wd()&1296,this.a.d)},s.Ec=function(n){return Eoe(),!0},s.Fc=function(n){return Tt(this),Tt(n),q(n,540)?I6n(u(n,833)):!n.dc()&&kY(this,n.Jc())},s.Gc=function(n){var t;return t=u(kp(v4(this.a),n),18),(t?t.gc():0)>0},s.Fb=function(n){return fOn(this,n)},s.Hb=function(){return Oi(C3(this))},s.dc=function(){return C3(this).dc()},s.Kc=function(n){return bqe(this,n,1)>0},s.Ib=function(){return su(C3(this))},v(fn,"AbstractMultiset",2049),m(2051,2030,ls),s.$b=function(){wB(this.a.a)},s.Gc=function(n){var t,i;return q(n,490)?(i=u(n,416),u(i.a.kd(),18).gc()<=0?!1:(t=Y_e(this.a,i.a.jd()),t==u(i.a.kd(),18).gc())):!1},s.Kc=function(n){var t,i,r,c;return q(n,490)&&(i=u(n,416),t=i.a.jd(),r=u(i.a.kd(),18).gc(),r!=0)?(c=this.a,sCn(c,t,r)):!1},v(fn,"Multisets/EntrySet",2051),m(1108,2051,ls,Kk),s.Jc=function(){return new BAe(NDe(v4(this.a.a)).Jc())},s.gc=function(){return v4(this.a.a).gc()},v(fn,"AbstractMultiset/EntrySet",1108),m(618,730,Wb),s.hc=function(){return this.nd()},s.jc=function(){return this.od()},s.cc=function(n){return this.pd(n)},s.fc=function(n){return this.qd(n)},s.Zb=function(){var n;return n=this.f,n||(this.f=this.ac())},s.od=function(){return vn(),vn(),fH},s.Fb=function(n){return ZY(this,n)},s.pd=function(n){return u(wi(this,n),22)},s.qd=function(n){return u(EO(this,n),22)},s.mc=function(n){return vn(),new Yy(u(n,22))},s.pc=function(n,t){return new ROe(this,n,u(t,22))},v(fn,"AbstractSetMultimap",618),m(1689,618,Wb),s.hc=function(){return new dd(this.b)},s.nd=function(){return new dd(this.b)},s.jc=function(){return $fe(new dd(this.b))},s.od=function(){return $fe(new dd(this.b))},s.cc=function(n){return u(u(wi(this,n),22),83)},s.pd=function(n){return u(u(wi(this,n),22),83)},s.fc=function(n){return u(u(EO(this,n),22),83)},s.qd=function(n){return u(u(EO(this,n),22),83)},s.mc=function(n){return q(n,277)?$fe(u(n,277)):(vn(),new Zse(u(n,83)))},s.Zb=function(){var n;return n=this.f,n||(this.f=q(this.c,138)?new JE(this,u(this.c,138)):q(this.c,134)?new EC(this,u(this.c,134)):new n9(this,this.c))},s.pc=function(n,t){return q(t,277)?new vCe(this,n,u(t,277)):new gle(this,n,u(t,83))},v(fn,"AbstractSortedSetMultimap",1689),m(1690,1689,Wb),s.Zb=function(){var n;return n=this.f,u(u(n||(this.f=q(this.c,138)?new JE(this,u(this.c,138)):q(this.c,134)?new EC(this,u(this.c,134)):new n9(this,this.c)),134),138)},s.ec=function(){var n;return n=this.i,u(u(n||(this.i=q(this.c,138)?new t9(this,u(this.c,138)):q(this.c,134)?new Yx(this,u(this.c,134)):new T3(this,this.c)),83),277)},s.bc=function(){return q(this.c,138)?new t9(this,u(this.c,138)):q(this.c,134)?new Yx(this,u(this.c,134)):new T3(this,this.c)},v(fn,"AbstractSortedKeySortedSetMultimap",1690),m(2071,1,{2008:1}),s.Fb=function(n){return WAn(this,n)},s.Hb=function(){var n;return t1e((n=this.g,n||(this.g=new u0(this))))},s.Ib=function(){var n;return ZJe((n=this.f,n||(this.f=new qse(this))))},v(fn,"AbstractTable",2071),m(669,Pa,ls,u0),s.$b=function(){YAe()},s.Gc=function(n){var t,i;return q(n,468)?(t=u(n,687),i=u(kp(nIe(this.a),b0(t.c.e,t.b)),92),!!i&&x1e(i.vc(),new ew(b0(t.c.c,t.a),A4(t.c,t.b,t.a)))):!1},s.Jc=function(){return D5n(this.a)},s.Kc=function(n){var t,i;return q(n,468)?(t=u(n,687),i=u(kp(nIe(this.a),b0(t.c.e,t.b)),92),!!i&&zkn(i.vc(),new ew(b0(t.c.c,t.a),A4(t.c,t.b,t.a)))):!1},s.gc=function(){return oDe(this.a)},s.Lc=function(){return L6n(this.a)},v(fn,"AbstractTable/CellSet",669),m(1987,31,Pp,PU),s.$b=function(){YAe()},s.Gc=function(n){return JMn(this.a,n)},s.Jc=function(){return I5n(this.a)},s.gc=function(){return oDe(this.a)},s.Lc=function(){return kLe(this.a)},v(fn,"AbstractTable/Values",1987),m(1662,1661,Wb),v(fn,"ArrayListMultimapGwtSerializationDependencies",1662),m(506,1662,Wb,AX,gae),s.hc=function(){return new So(this.a)},s.a=0,v(fn,"ArrayListMultimap",506),m(668,2071,{668:1,2008:1,3:1},dqe),v(fn,"ArrayTable",668),m(1983,392,y8,UCe),s.Xb=function(n){return new o1e(this.a,n)},v(fn,"ArrayTable/1",1983),m(1984,1,{},$U),s.rd=function(n){return new o1e(this.a,n)},v(fn,"ArrayTable/1methodref$getCell$Type",1984),m(2072,1,{687:1}),s.Fb=function(n){var t;return n===this?!0:q(n,468)?(t=u(n,687),j1(b0(this.c.e,this.b),b0(t.c.e,t.b))&&j1(b0(this.c.c,this.a),b0(t.c.c,t.a))&&j1(A4(this.c,this.b,this.a),A4(t.c,t.b,t.a))):!1},s.Hb=function(){return PB(z(B(Mr,1),xn,1,5,[b0(this.c.e,this.b),b0(this.c.c,this.a),A4(this.c,this.b,this.a)]))},s.Ib=function(){return"("+b0(this.c.e,this.b)+","+b0(this.c.c,this.a)+")="+A4(this.c,this.b,this.a)},v(fn,"Tables/AbstractCell",2072),m(468,2072,{468:1,687:1},o1e),s.a=0,s.b=0,s.d=0,v(fn,"ArrayTable/2",468),m(1986,1,{},P5),s.rd=function(n){return N$e(this.a,n)},v(fn,"ArrayTable/2methodref$getValue$Type",1986),m(1985,392,y8,XCe),s.Xb=function(n){return N$e(this.a,n)},v(fn,"ArrayTable/3",1985),m(2039,2027,Lw),s.$b=function(){rR(this.kc())},s.vc=function(){return new Yk(this)},s.lc=function(){return new _Ie(this.kc(),this.gc())},v(fn,"Maps/IteratorBasedAbstractMap",2039),m(826,2039,Lw),s.$b=function(){throw P(new Ot)},s._b=function(n){return dMe(this.c,n)},s.kc=function(){return new KCe(this,this.c.b.c.gc())},s.lc=function(){return ZK(this.c.b.c.gc(),16,new dP(this))},s.xc=function(n){var t;return t=u(GE(this.c,n),15),t?this.td(t.a):null},s.dc=function(){return this.c.b.c.dc()},s.ec=function(){return oV(this.c)},s.yc=function(n,t){var i;if(i=u(GE(this.c,n),15),!i)throw P(new Gn(this.sd()+" "+n+" not in "+oV(this.c)));return this.ud(i.a,t)},s.Ac=function(n){throw P(new Ot)},s.gc=function(){return this.c.b.c.gc()},v(fn,"ArrayTable/ArrayMap",826),m(1982,1,{},dP),s.rd=function(n){return rIe(this.a,n)},v(fn,"ArrayTable/ArrayMap/0methodref$getEntry$Type",1982),m(1980,358,oW,FMe),s.jd=function(){return t2n(this.a,this.b)},s.kd=function(){return this.a.td(this.b)},s.ld=function(n){return this.a.ud(this.b,n)},s.b=0,v(fn,"ArrayTable/ArrayMap/1",1980),m(1981,392,y8,KCe),s.Xb=function(n){return rIe(this.a,n)},v(fn,"ArrayTable/ArrayMap/2",1981),m(1979,826,Lw,UDe),s.sd=function(){return"Column"},s.td=function(n){return A4(this.b,this.a,n)},s.ud=function(n,t){return dze(this.b,this.a,n,t)},s.a=0,v(fn,"ArrayTable/Row",1979),m(827,826,Lw,qse),s.td=function(n){return new UDe(this.a,n)},s.yc=function(n,t){return u(t,92),Abn()},s.ud=function(n,t){return u(t,92),Mbn()},s.sd=function(){return"Row"},v(fn,"ArrayTable/RowMap",827),m(1126,1,fl,HMe),s.yd=function(n){return(this.a.wd()&-262&n)!=0},s.wd=function(){return this.a.wd()&-262},s.xd=function(){return this.a.xd()},s.Nb=function(n){this.a.Nb(new GMe(n,this.b))},s.zd=function(n){return this.a.zd(new JMe(n,this.b))},v(fn,"CollectSpliterators/1",1126),m(1127,1,it,JMe),s.Ad=function(n){this.a.Ad(this.b.Kb(n))},v(fn,"CollectSpliterators/1/lambda$0$Type",1127),m(1128,1,it,GMe),s.Ad=function(n){this.a.Ad(this.b.Kb(n))},v(fn,"CollectSpliterators/1/lambda$1$Type",1128),m(1123,1,fl,dNe),s.yd=function(n){return((16464|this.b)&n)!=0},s.wd=function(){return 16464|this.b},s.xd=function(){return this.a.xd()},s.Nb=function(n){this.a.Oe(new UMe(n,this.c))},s.zd=function(n){return this.a.Pe(new qMe(n,this.c))},s.b=0,v(fn,"CollectSpliterators/1WithCharacteristics",1123),m(1124,1,lN,qMe),s.Bd=function(n){this.a.Ad(this.b.rd(n))},v(fn,"CollectSpliterators/1WithCharacteristics/lambda$0$Type",1124),m(1125,1,lN,UMe),s.Bd=function(n){this.a.Ad(this.b.rd(n))},v(fn,"CollectSpliterators/1WithCharacteristics/lambda$1$Type",1125),m(1119,1,fl),s.yd=function(n){return(this.a&n)!=0},s.wd=function(){return this.a},s.xd=function(){return this.e&&(this.b=Lse(this.b,this.e.xd())),Lse(this.b,0)},s.Nb=function(n){this.e&&(this.e.Nb(n),this.e=null),this.c.Nb(new XMe(this,n)),this.b=0},s.zd=function(n){for(;;){if(this.e&&this.e.zd(n))return _E(this.b,fN)&&(this.b=uf(this.b,1)),!0;if(this.e=null,!this.c.zd(new $5(this)))return!1}},s.a=0,s.b=0,v(fn,"CollectSpliterators/FlatMapSpliterator",1119),m(1121,1,it,$5),s.Ad=function(n){Y2n(this.a,n)},v(fn,"CollectSpliterators/FlatMapSpliterator/lambda$0$Type",1121),m(1122,1,it,XMe),s.Ad=function(n){s5n(this.a,this.b,n)},v(fn,"CollectSpliterators/FlatMapSpliterator/lambda$1$Type",1122),m(1120,1119,fl,WLe),v(fn,"CollectSpliterators/FlatMapSpliteratorOfObject",1120),m(254,1,sW),s.Dd=function(n){return this.Cd(u(n,254))},s.Cd=function(n){var t;return n==(mX(),qne)?1:n==(pX(),Gne)?-1:(t=(Z$(),aO(this.a,n.a)),t!=0?t:(Pn(),q(this,513)==q(n,513)?0:q(this,513)?1:-1))},s.Gd=function(){return this.a},s.Fb=function(n){return Mde(this,n)},v(fn,"Cut",254),m(1793,254,sW,DAe),s.Cd=function(n){return n==this?0:1},s.Ed=function(n){throw P(new eoe)},s.Fd=function(n){n.a+="+∞)"},s.Gd=function(){throw P(new qc(iYe))},s.Hb=function(){return bd(),dde(this)},s.Hd=function(n){return!1},s.Ib=function(){return"+∞"};var Gne;v(fn,"Cut/AboveAll",1793),m(513,254,{254:1,513:1,3:1,35:1},ZCe),s.Ed=function(n){co((n.a+="(",n),this.a)},s.Fd=function(n){Ib(co(n,this.a),93)},s.Hb=function(){return~Oi(this.a)},s.Hd=function(n){return Z$(),aO(this.a,n)<0},s.Ib=function(){return"/"+this.a+"\\"},v(fn,"Cut/AboveValue",513),m(1792,254,sW,NAe),s.Cd=function(n){return n==this?0:-1},s.Ed=function(n){n.a+="(-∞"},s.Fd=function(n){throw P(new eoe)},s.Gd=function(){throw P(new qc(iYe))},s.Hb=function(){return bd(),dde(this)},s.Hd=function(n){return!0},s.Ib=function(){return"-∞"};var qne;v(fn,"Cut/BelowAll",1792),m(1794,254,sW,WCe),s.Ed=function(n){co((n.a+="[",n),this.a)},s.Fd=function(n){Ib(co(n,this.a),41)},s.Hb=function(){return Oi(this.a)},s.Hd=function(n){return Z$(),aO(this.a,n)<=0},s.Ib=function(){return"\\"+this.a+"/"},v(fn,"Cut/BelowValue",1794),m(535,1,Xh),s.Ic=function(n){rc(this,n)},s.Ib=function(){return wEn(u(TR(this,"use Optional.orNull() instead of Optional.or(null)"),20).Jc())},v(fn,"FluentIterable",535),m(433,535,Xh,$E),s.Jc=function(){return new qn(Vn(this.a.Jc(),new ne))},v(fn,"FluentIterable/2",433),m(36,1,{},ne),s.Kb=function(n){return u(n,20).Jc()},s.Fb=function(n){return this===n},v(fn,"FluentIterable/2/0methodref$iterator$Type",36),m(1040,535,Xh,dCe),s.Jc=function(){return zh(this)},v(fn,"FluentIterable/3",1040),m(714,392,y8,Wse),s.Xb=function(n){return this.a[n].Jc()},v(fn,"FluentIterable/3/1",714),m(2032,1,{}),s.Ib=function(){return su(this.Id().b)},v(fn,"ForwardingObject",2032),m(2033,2032,rYe),s.Id=function(){return this.Jd()},s.Ic=function(n){rc(this,n)},s.Lc=function(){return new wn(this,0)},s.Mc=function(){return new bn(null,this.Lc())},s.Ec=function(n){return this.Jd(),pMe()},s.Fc=function(n){return this.Jd(),mMe()},s.$b=function(){this.Jd(),vMe()},s.Gc=function(n){return this.Jd().Gc(n)},s.Hc=function(n){return this.Jd().Hc(n)},s.dc=function(){return this.Jd().b.dc()},s.Jc=function(){return this.Jd().Jc()},s.Kc=function(n){return this.Jd(),yMe()},s.gc=function(){return this.Jd().b.gc()},s.Nc=function(){return this.Jd().Nc()},s.Oc=function(n){return this.Jd().Oc(n)},v(fn,"ForwardingCollection",2033),m(2040,31,Oge),s.Jc=function(){return this.Md()},s.Ec=function(n){throw P(new Ot)},s.Fc=function(n){throw P(new Ot)},s.Kd=function(){var n;return n=this.c,n||(this.c=this.Ld())},s.$b=function(){throw P(new Ot)},s.Gc=function(n){return n!=null&&Ep(this,n,!1)},s.Ld=function(){switch(this.gc()){case 0:return iR(),Kne;case 1:return new RK(Tt(this.Md().Pb()));default:return new Yle(this,this.Nc())}},s.Kc=function(n){throw P(new Ot)},v(fn,"ImmutableCollection",2040),m(1259,2040,Oge,gP),s.Jc=function(){return M4(new tc(this.a.b.Jc()))},s.Gc=function(n){return n!=null&&bE(this.a,n)},s.Hc=function(n){return Boe(this.a,n)},s.dc=function(){return this.a.b.dc()},s.Md=function(){return M4(new tc(this.a.b.Jc()))},s.gc=function(){return this.a.b.gc()},s.Nc=function(){return this.a.b.Nc()},s.Oc=function(n){return zoe(this.a,n)},s.Ib=function(){return su(this.a.b)},v(fn,"ForwardingImmutableCollection",1259),m(311,2040,k8),s.Jc=function(){return this.Md()},s.cd=function(){return this.Nd(0)},s.dd=function(n){return this.Nd(n)},s.gd=function(n){Fb(this,n)},s.Lc=function(){return new wn(this,16)},s.hd=function(n,t){return this.Od(n,t)},s._c=function(n,t){throw P(new Ot)},s.ad=function(n,t){throw P(new Ot)},s.Kd=function(){return this},s.Fb=function(n){return eOn(this,n)},s.Hb=function(){return T7n(this)},s.bd=function(n){return n==null?-1:_Sn(this,n)},s.Md=function(){return this.Nd(0)},s.Nd=function(n){return OK(this,n)},s.ed=function(n){throw P(new Ot)},s.fd=function(n,t){throw P(new Ot)},s.Od=function(n,t){var i;return JB((i=new nTe(this),new y0(i,n,t)))},v(fn,"ImmutableList",311),m(2067,311,k8),s.Jc=function(){return M4(this.Pd().Jc())},s.hd=function(n,t){return JB(this.Pd().hd(n,t))},s.Gc=function(n){return n!=null&&this.Pd().Gc(n)},s.Hc=function(n){return this.Pd().Hc(n)},s.Fb=function(n){return ai(this.Pd(),n)},s.Xb=function(n){return b0(this,n)},s.Hb=function(){return Oi(this.Pd())},s.bd=function(n){return this.Pd().bd(n)},s.dc=function(){return this.Pd().dc()},s.Md=function(){return M4(this.Pd().Jc())},s.gc=function(){return this.Pd().gc()},s.Od=function(n,t){return JB(this.Pd().hd(n,t))},s.Nc=function(){return this.Pd().Oc(oe(Mr,xn,1,this.Pd().gc(),5,1))},s.Oc=function(n){return this.Pd().Oc(n)},s.Ib=function(){return su(this.Pd())},v(fn,"ForwardingImmutableList",2067),m(717,1,E8),s.vc=function(){return Cb(this)},s.wc=function(n){hO(this,n)},s.ec=function(){return oV(this)},s.Bc=function(){return this.Td()},s.$b=function(){throw P(new Ot)},s._b=function(n){return this.xc(n)!=null},s.uc=function(n){return this.Td().Gc(n)},s.Rd=function(){return new zU(this)},s.Sd=function(){return new FU(this)},s.Fb=function(n){return pkn(this,n)},s.Hb=function(){return Cb(this).Hb()},s.dc=function(){return this.gc()==0},s.yc=function(n,t){return Tbn()},s.Ac=function(n){throw P(new Ot)},s.Ib=function(){return $Tn(this)},s.Td=function(){return this.e?this.e:this.e=this.Sd()},s.c=null,s.d=null,s.e=null,v(fn,"ImmutableMap",717),m(718,717,E8),s._b=function(n){return dMe(this,n)},s.uc=function(n){return sTe(this.b,n)},s.Qd=function(){return Yze(new bP(this))},s.Rd=function(){return Yze(MIe(this.b))},s.Sd=function(){return new gP(TIe(this.b))},s.Fb=function(n){return fTe(this.b,n)},s.xc=function(n){return GE(this,n)},s.Hb=function(){return Oi(this.b.c)},s.dc=function(){return this.b.c.dc()},s.gc=function(){return this.b.c.gc()},s.Ib=function(){return su(this.b.c)},v(fn,"ForwardingImmutableMap",718),m(2034,2033,lW),s.Id=function(){return this.Ud()},s.Jd=function(){return this.Ud()},s.Lc=function(){return new wn(this,1)},s.Fb=function(n){return n===this||this.Ud().Fb(n)},s.Hb=function(){return this.Ud().Hb()},v(fn,"ForwardingSet",2034),m(1055,2034,lW,bP),s.Id=function(){return y9(this.a.b)},s.Jd=function(){return y9(this.a.b)},s.Gc=function(n){if(q(n,45)&&u(n,45).jd()==null)return!1;try{return lTe(y9(this.a.b),n)}catch(t){if(t=or(t),q(t,211))return!1;throw P(t)}},s.Ud=function(){return y9(this.a.b)},s.Oc=function(n){var t,i;return t=h_e(y9(this.a.b),n),y9(this.a.b).b.gc()=0?"+":"")+(i/60|0),t=O$(E.Math.abs(i)%60),(fGe(),Wen)[this.q.getDay()]+" "+enn[this.q.getMonth()]+" "+O$(this.q.getDate())+" "+O$(this.q.getHours())+":"+O$(this.q.getMinutes())+":"+O$(this.q.getSeconds())+" GMT"+n+t+" "+this.q.getFullYear()};var oH=v(wt,"Date",205);m(1977,205,dYe,NJe),s.a=!1,s.b=0,s.c=0,s.d=0,s.e=0,s.f=0,s.g=!1,s.i=0,s.j=0,s.k=0,s.n=0,s.o=0,s.p=0,v("com.google.gwt.i18n.shared.impl","DateRecord",1977),m(2026,1,{}),s.ne=function(){return null},s.oe=function(){return null},s.pe=function(){return null},s.qe=function(){return null},s.re=function(){return null},v(Y4,"JSONValue",2026),m(139,2026,{139:1},sd,Fy),s.Fb=function(n){return q(n,139)?mae(this.a,u(n,139).a):!1},s.me=function(){return X0n},s.Hb=function(){return tae(this.a)},s.ne=function(){return this},s.Ib=function(){var n,t,i;for(i=new Ws("["),t=0,n=this.a.length;t0&&(i.a+=","),co(i,bp(this,t));return i.a+="]",i.a},v(Y4,"JSONArray",139),m(479,2026,{479:1},Hy),s.me=function(){return K0n},s.oe=function(){return this},s.Ib=function(){return Pn(),""+this.a},s.a=!1;var Fen,Hen;v(Y4,"JSONBoolean",479),m(981,63,R1,zAe),v(Y4,"JSONException",981),m(1017,2026,{},Ft),s.me=function(){return Z0n},s.Ib=function(){return Ko};var Jen;v(Y4,"JSONNull",1017),m(265,2026,{265:1},f3),s.Fb=function(n){return q(n,265)?this.a==u(n,265).a:!1},s.me=function(){return V0n},s.Hb=function(){return i4(this.a)},s.pe=function(){return this},s.Ib=function(){return this.a+""},s.a=0,v(Y4,"JSONNumber",265),m(149,2026,{149:1},X5,Jy),s.Fb=function(n){return q(n,149)?mae(this.a,u(n,149).a):!1},s.me=function(){return Y0n},s.Hb=function(){return tae(this.a)},s.qe=function(){return this},s.Ib=function(){var n,t,i,r,c,o,l;for(l=new Ws("{"),n=!0,o=_Y(this,oe(Re,je,2,0,6,1)),i=o,r=0,c=i.length;r=0?":"+this.c:"")+")"},s.c=0;var Ame=v(Mu,"StackTraceElement",324);Ien={3:1,472:1,35:1,2:1};var Re=v(Mu,Nge,2);m(111,418,{472:1},ad,hE,nf),v(Mu,"StringBuffer",111),m(106,418,{472:1},l0,Y5,Ws),v(Mu,"StringBuilder",106),m(691,99,nF,joe),v(Mu,"StringIndexOutOfBoundsException",691),m(2107,1,{});var Xen;m(46,63,{3:1,101:1,63:1,80:1,46:1},Ot,ld),v(Mu,"UnsupportedOperationException",46),m(247,242,{3:1,35:1,242:1,247:1},AO,Ioe),s.Dd=function(n){return fKe(this,u(n,247))},s.se=function(){return Tp($Ke(this))},s.Fb=function(n){var t;return this===n?!0:q(n,247)?(t=u(n,247),this.e==t.e&&fKe(this,t)==0):!1},s.Hb=function(){var n;return this.b!=0?this.b:this.a<54?(n=_u(this.f),this.b=_t($r(n,-1)),this.b=33*this.b+_t($r(ow(n,32),-1)),this.b=17*this.b+sc(this.e),this.b):(this.b=17*oFe(this.c)+sc(this.e),this.b)},s.Ib=function(){return $Ke(this)},s.a=0,s.b=0,s.d=0,s.e=0,s.f=0;var Ken,lg,Mme,Tme,xme,Cme,Ome,Nme,ete=v("java.math","BigDecimal",247);m(91,242,{3:1,35:1,242:1,91:1},T1,iLe,Db,wHe,g0),s.Dd=function(n){return aHe(this,u(n,91))},s.se=function(){return Tp(rW(this,0))},s.Fb=function(n){return Q1e(this,n)},s.Hb=function(){return oFe(this)},s.Ib=function(){return rW(this,0)},s.b=-2,s.c=0,s.d=0,s.e=0;var Ven,sH,Yen,nte,lH,RS,dv=v("java.math","BigInteger",91),Qen,Zen,o6,BS;m(484,2027,Lw),s.$b=function(){Fu(this)},s._b=function(n){return oo(this,n)},s.uc=function(n){return qze(this,n,this.i)||qze(this,n,this.f)},s.vc=function(){return new tn(this)},s.xc=function(n){return Bn(this,n)},s.yc=function(n,t){return Qt(this,n,t)},s.Ac=function(n){return S4(this,n)},s.gc=function(){return gE(this)},s.g=0,v(wt,"AbstractHashMap",484),m(306,Pa,ls,tn),s.$b=function(){this.a.$b()},s.Gc=function(n){return ILe(this,n)},s.Jc=function(){return new mp(this.a)},s.Kc=function(n){var t;return ILe(this,n)?(t=u(n,45).jd(),this.a.Ac(t),!0):!1},s.gc=function(){return this.a.gc()},v(wt,"AbstractHashMap/EntrySet",306),m(307,1,zr,mp),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return z3(this)},s.Ob=function(){return this.b},s.Qb=function(){uRe(this)},s.b=!1,s.d=0,v(wt,"AbstractHashMap/EntrySetIterator",307),m(417,1,zr,Gc),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return LX(this)},s.Pb=function(){return Zfe(this)},s.Qb=function(){As(this)},s.b=0,s.c=-1,v(wt,"AbstractList/IteratorImpl",417),m(97,417,Uh,Gr),s.Qb=function(){As(this)},s.Rb=function(n){W2(this,n)},s.Sb=function(){return this.b>0},s.Tb=function(){return this.b},s.Ub=function(){return ft(this.b>0),this.a.Xb(this.c=--this.b)},s.Vb=function(){return this.b-1},s.Wb=function(n){V2(this.c!=-1),this.a.fd(this.c,n)},v(wt,"AbstractList/ListIteratorImpl",97),m(258,56,j8,y0),s._c=function(n,t){fp(n,this.b),this.c._c(this.a+n,t),++this.b},s.Xb=function(n){return pn(n,this.b),this.c.Xb(this.a+n)},s.ed=function(n){var t;return pn(n,this.b),t=this.c.ed(this.a+n),--this.b,t},s.fd=function(n,t){return pn(n,this.b),this.c.fd(this.a+n,t)},s.gc=function(){return this.b},s.a=0,s.b=0,v(wt,"AbstractList/SubList",258),m(232,Pa,ls,et),s.$b=function(){this.a.$b()},s.Gc=function(n){return this.a._b(n)},s.Jc=function(){var n;return n=this.a.vc().Jc(),new bt(n)},s.Kc=function(n){return this.a._b(n)?(this.a.Ac(n),!0):!1},s.gc=function(){return this.a.gc()},v(wt,"AbstractMap/1",232),m(529,1,zr,bt),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.a.Ob()},s.Pb=function(){var n;return n=u(this.a.Pb(),45),n.jd()},s.Qb=function(){this.a.Qb()},v(wt,"AbstractMap/1/1",529),m(230,31,Pp,rt),s.$b=function(){this.a.$b()},s.Gc=function(n){return this.a.uc(n)},s.Jc=function(){var n;return n=this.a.vc().Jc(),new Hi(n)},s.gc=function(){return this.a.gc()},v(wt,"AbstractMap/2",230),m(304,1,zr,Hi),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.a.Ob()},s.Pb=function(){var n;return n=u(this.a.Pb(),45),n.kd()},s.Qb=function(){this.a.Qb()},v(wt,"AbstractMap/2/1",304),m(480,1,{480:1,45:1}),s.Fb=function(n){var t;return q(n,45)?(t=u(n,45),Uu(this.d,t.jd())&&Uu(this.e,t.kd())):!1},s.jd=function(){return this.d},s.kd=function(){return this.e},s.Hb=function(){return j3(this.d)^j3(this.e)},s.ld=function(n){return jle(this,n)},s.Ib=function(){return this.d+"="+this.e},v(wt,"AbstractMap/AbstractEntry",480),m(390,480,{480:1,390:1,45:1},t$),v(wt,"AbstractMap/SimpleEntry",390),m(2044,1,IW),s.Fb=function(n){var t;return q(n,45)?(t=u(n,45),Uu(this.jd(),t.jd())&&Uu(this.kd(),t.kd())):!1},s.Hb=function(){return j3(this.jd())^j3(this.kd())},s.Ib=function(){return this.jd()+"="+this.kd()},v(wt,nYe,2044),m(2052,2027,xge),s.Vc=function(n){return CX(this.Ce(n))},s.tc=function(n){return TPe(this,n)},s._b=function(n){return Sle(this,n)},s.vc=function(){return new Ui(this)},s.Rc=function(){return XDe(this.Ee())},s.Wc=function(n){return CX(this.Fe(n))},s.xc=function(n){var t;return t=n,hu(this.De(t))},s.Yc=function(n){return CX(this.Ge(n))},s.ec=function(){return new Iu(this)},s.Tc=function(){return XDe(this.He())},s.Zc=function(n){return CX(this.Ie(n))},v(wt,"AbstractNavigableMap",2052),m(620,Pa,ls,Ui),s.Gc=function(n){return q(n,45)&&TPe(this.b,u(n,45))},s.Jc=function(){return this.b.Be()},s.Kc=function(n){var t;return q(n,45)?(t=u(n,45),this.b.Je(t)):!1},s.gc=function(){return this.b.gc()},v(wt,"AbstractNavigableMap/EntrySet",620),m(1115,Pa,Cge,Iu),s.Lc=function(){return new c$(this)},s.$b=function(){this.a.$b()},s.Gc=function(n){return Sle(this.a,n)},s.Jc=function(){var n;return n=this.a.vc().b.Be(),new Ake(n)},s.Kc=function(n){return Sle(this.a,n)?(this.a.Ac(n),!0):!1},s.gc=function(){return this.a.gc()},v(wt,"AbstractNavigableMap/NavigableKeySet",1115),m(1116,1,zr,Ake),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return LX(this.a.a)},s.Pb=function(){var n;return n=vOe(this.a),n.jd()},s.Qb=function(){ENe(this.a)},v(wt,"AbstractNavigableMap/NavigableKeySet/1",1116),m(2065,31,Pp),s.Ec=function(n){return h4(c8(this,n),A8),!0},s.Fc=function(n){return _n(n),NC(n!=this,"Can't add a queue to itself"),fc(this,n)},s.$b=function(){for(;EY(this)!=null;);},v(wt,"AbstractQueue",2065),m(314,31,{4:1,20:1,31:1,18:1},A3,SLe),s.Ec=function(n){return Mae(this,n),!0},s.$b=function(){Nae(this)},s.Gc=function(n){return lze(new tj(this),n)},s.dc=function(){return aE(this)},s.Jc=function(){return new tj(this)},s.Kc=function(n){return d4n(new tj(this),n)},s.gc=function(){return this.c-this.b&this.a.length-1},s.Lc=function(){return new wn(this,272)},s.Oc=function(n){var t;return t=this.c-this.b&this.a.length-1,n.lengtht&&tr(n,t,null),n},s.b=0,s.c=0,v(wt,"ArrayDeque",314),m(448,1,zr,tj),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.a!=this.b},s.Pb=function(){return RB(this)},s.Qb=function(){lBe(this)},s.a=0,s.b=0,s.c=-1,v(wt,"ArrayDeque/IteratorImpl",448),m(13,56,pYe,xe,So,ds),s._c=function(n,t){xb(this,n,t)},s.Ec=function(n){return Te(this,n)},s.ad=function(n,t){return E1e(this,n,t)},s.Fc=function(n){return jr(this,n)},s.$b=function(){$2(this.c,0)},s.Gc=function(n){return gu(this,n,0)!=-1},s.Ic=function(n){Ao(this,n)},s.Xb=function(n){return _e(this,n)},s.bd=function(n){return gu(this,n,0)},s.dc=function(){return this.c.length==0},s.Jc=function(){return new L(this)},s.ed=function(n){return yd(this,n)},s.Kc=function(n){return Go(this,n)},s.ae=function(n,t){V_e(this,n,t)},s.fd=function(n,t){return il(this,n,t)},s.gc=function(){return this.c.length},s.gd=function(n){xr(this,n)},s.Nc=function(){return W$(this.c)},s.Oc=function(n){return Na(this,n)};var bBn=v(wt,"ArrayList",13);m(7,1,zr,L),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return du(this)},s.Pb=function(){return _(this)},s.Qb=function(){QE(this)},s.a=0,s.b=-1,v(wt,"ArrayList/1",7),m(2074,E.Function,{},gn),s.Ke=function(n,t){return vi(n,t)},m(123,56,mYe,Eu),s.Gc=function(n){return sBe(this,n)!=-1},s.Ic=function(n){var t,i,r,c;for(_n(n),i=this.a,r=0,c=i.length;r0)throw P(new Gn(Bge+n+" greater than "+this.e));return this.f.Re()?m_e(this.c,this.b,this.a,n,t):X_e(this.c,n,t)},s.yc=function(n,t){if(!KQ(this.c,this.f,n,this.b,this.a,this.e,this.d))throw P(new Gn(n+" outside the range "+this.b+" to "+this.e));return Tze(this.c,n,t)},s.Ac=function(n){var t;return t=n,KQ(this.c,this.f,t,this.b,this.a,this.e,this.d)?v_e(this.c,t):null},s.Je=function(n){return kR(this,n.jd())&&Qae(this.c,n)},s.gc=function(){var n,t,i;if(this.f.Re()?this.a?t=Z9(this.c,this.b,!0):t=Z9(this.c,this.b,!1):t=lhe(this.c),!(t&&kR(this,t.d)&&t))return 0;for(n=0,i=new LY(this.c,this.f,this.b,this.a,this.e,this.d);LX(i.a);i.b=u(Zfe(i.a),45))++n;return n},s.$c=function(n,t){if(this.f.Re()&&this.c.a.Le(n,this.b)<0)throw P(new Gn(Bge+n+kYe+this.b));return this.f.Se()?m_e(this.c,n,t,this.e,this.d):K_e(this.c,n,t)},s.a=!1,s.d=!1,v(wt,"TreeMap/SubMap",622),m(309,23,$W,i$),s.Re=function(){return!1},s.Se=function(){return!1};var rte,cte,ute,ote,aH=mt(wt,"TreeMap/SubMapType",309,At,J6n,gpn);m(1112,309,$W,pCe),s.Se=function(){return!0},mt(wt,"TreeMap/SubMapType/1",1112,aH,null,null),m(1113,309,$W,CCe),s.Re=function(){return!0},s.Se=function(){return!0},mt(wt,"TreeMap/SubMapType/2",1113,aH,null,null),m(1114,309,$W,mCe),s.Re=function(){return!0},mt(wt,"TreeMap/SubMapType/3",1114,aH,null,null);var unn;m(141,Pa,{3:1,20:1,31:1,18:1,277:1,22:1,83:1,141:1},aX,ele,dd,qy),s.Lc=function(){return new c$(this)},s.Ec=function(n){return _C(this,n)},s.$b=function(){this.a.$b()},s.Gc=function(n){return this.a._b(n)},s.Jc=function(){return this.a.ec().Jc()},s.Kc=function(n){return TK(this,n)},s.gc=function(){return this.a.gc()};var yBn=v(wt,"TreeSet",141);m(1052,1,{},xke),s.Te=function(n,t){return L2n(this.a,n,t)},v(RW,"BinaryOperator/lambda$0$Type",1052),m(1053,1,{},Cke),s.Te=function(n,t){return P2n(this.a,n,t)},v(RW,"BinaryOperator/lambda$1$Type",1053),m(935,1,{},Du),s.Kb=function(n){return n},v(RW,"Function/lambda$0$Type",935),m(388,1,Pt,Uy),s.Mb=function(n){return!this.a.Mb(n)},v(RW,"Predicate/lambda$2$Type",388),m(567,1,{567:1});var onn=v(oS,"Handler",567);m(2069,1,oN),s.ve=function(){return"DUMMY"},s.Ib=function(){return this.ve()};var Rme;v(oS,"Level",2069),m(1672,2069,oN,kl),s.ve=function(){return"INFO"},v(oS,"Level/LevelInfo",1672),m(1824,1,{},XSe);var ste;v(oS,"LogManager",1824),m(1866,1,oN,kNe),s.b=null,v(oS,"LogRecord",1866),m(511,1,{511:1},nY),s.e=!1;var snn=!1,lnn=!1,Fa=!1,fnn=!1,ann=!1;v(oS,"Logger",511),m(819,567,{567:1},Xr),v(oS,"SimpleConsoleLogHandler",819),m(130,23,{3:1,35:1,23:1,130:1},RX);var Bme,Vo,zme,Yo=mt(Dc,"Collector/Characteristics",130,At,T4n,wpn),hnn;m(746,1,{},Ofe),v(Dc,"CollectorImpl",746),m(1050,1,{},nc),s.Te=function(n,t){return Wkn(u(n,212),u(t,212))},v(Dc,"Collectors/10methodref$merge$Type",1050),m(1051,1,{},pr),s.Kb=function(n){return jLe(u(n,212))},v(Dc,"Collectors/11methodref$toString$Type",1051),m(152,1,{},Ei),s.Wd=function(n,t){u(n,18).Ec(t)},v(Dc,"Collectors/20methodref$add$Type",152),m(154,1,{},Bi),s.Ve=function(){return new xe},v(Dc,"Collectors/21methodref$ctor$Type",154),m(1049,1,{},Fc),s.Wd=function(n,t){x1(u(n,212),u(t,472))},v(Dc,"Collectors/9methodref$add$Type",1049),m(1048,1,{},zNe),s.Ve=function(){return new Jb(this.a,this.b,this.c)},v(Dc,"Collectors/lambda$15$Type",1048),m(153,1,{},eu),s.Te=function(n,t){return agn(u(n,18),u(t,18))},v(Dc,"Collectors/lambda$45$Type",153),m(538,1,{}),s.Ye=function(){nj(this)},s.d=!1,v(Dc,"TerminatableStream",538),m(768,538,zge,wle),s.Ye=function(){nj(this)},v(Dc,"DoubleStreamImpl",768),m(1297,724,fl,FNe),s.Pe=function(n){return MSn(this,u(n,189))},s.a=null,v(Dc,"DoubleStreamImpl/2",1297),m(1298,1,wN,Oke),s.Ne=function(n){rwn(this.a,n)},v(Dc,"DoubleStreamImpl/2/lambda$0$Type",1298),m(1295,1,wN,Nke),s.Ne=function(n){iwn(this.a,n)},v(Dc,"DoubleStreamImpl/lambda$0$Type",1295),m(1296,1,wN,Dke),s.Ne=function(n){WFe(this.a,n)},v(Dc,"DoubleStreamImpl/lambda$2$Type",1296),m(1351,723,fl,IPe),s.Pe=function(n){return D6n(this,u(n,202))},s.a=0,s.b=0,s.c=0,v(Dc,"IntStream/5",1351),m(793,538,zge,ple),s.Ye=function(){nj(this)},s.Ze=function(){return m0(this),this.a},v(Dc,"IntStreamImpl",793),m(794,538,zge,Foe),s.Ye=function(){nj(this)},s.Ze=function(){return m0(this),Fse(),cnn},v(Dc,"IntStreamImpl/Empty",794),m(1651,1,lN,Ike),s.Bd=function(n){qBe(this.a,n)},v(Dc,"IntStreamImpl/lambda$4$Type",1651);var kBn=Ji(Dc,"Stream");m(28,538,{520:1,677:1,832:1},bn),s.Ye=function(){nj(this)};var s6;v(Dc,"StreamImpl",28),m(1072,486,fl,hNe),s.zd=function(n){for(;D9n(this);){if(this.a.zd(n))return!0;nj(this.b),this.b=null,this.a=null}return!1},v(Dc,"StreamImpl/1",1072),m(1073,1,it,_ke),s.Ad=function(n){y3n(this.a,u(n,832))},v(Dc,"StreamImpl/1/lambda$0$Type",1073),m(1074,1,Pt,Lke),s.Mb=function(n){return ar(this.a,n)},v(Dc,"StreamImpl/1methodref$add$Type",1074),m(1075,486,fl,qIe),s.zd=function(n){var t;return this.a||(t=new xe,this.b.a.Nb(new Pke(t)),vn(),xr(t,this.c),this.a=new wn(t,16)),IRe(this.a,n)},s.a=null,v(Dc,"StreamImpl/5",1075),m(1076,1,it,Pke),s.Ad=function(n){Te(this.a,n)},v(Dc,"StreamImpl/5/2methodref$add$Type",1076),m(725,486,fl,ohe),s.zd=function(n){for(this.b=!1;!this.b&&this.c.zd(new OTe(this,n)););return this.b},s.b=!1,v(Dc,"StreamImpl/FilterSpliterator",725),m(1066,1,it,OTe),s.Ad=function(n){hvn(this.a,this.b,n)},v(Dc,"StreamImpl/FilterSpliterator/lambda$0$Type",1066),m(1061,724,fl,JPe),s.Pe=function(n){return upn(this,u(n,189))},v(Dc,"StreamImpl/MapToDoubleSpliterator",1061),m(1065,1,it,NTe),s.Ad=function(n){Tgn(this.a,this.b,n)},v(Dc,"StreamImpl/MapToDoubleSpliterator/lambda$0$Type",1065),m(1060,723,fl,GPe),s.Pe=function(n){return opn(this,u(n,202))},v(Dc,"StreamImpl/MapToIntSpliterator",1060),m(1064,1,it,DTe),s.Ad=function(n){xgn(this.a,this.b,n)},v(Dc,"StreamImpl/MapToIntSpliterator/lambda$0$Type",1064),m(722,486,fl,Xae),s.zd=function(n){return bNe(this,n)},v(Dc,"StreamImpl/MapToObjSpliterator",722),m(1063,1,it,ITe),s.Ad=function(n){Cgn(this.a,this.b,n)},v(Dc,"StreamImpl/MapToObjSpliterator/lambda$0$Type",1063),m(1062,486,fl,fBe),s.zd=function(n){for(;PX(this.b,0);){if(!this.a.zd(new Za))return!1;this.b=uf(this.b,1)}return this.a.zd(n)},s.b=0,v(Dc,"StreamImpl/SkipSpliterator",1062),m(1067,1,it,Za),s.Ad=function(n){},v(Dc,"StreamImpl/SkipSpliterator/lambda$0$Type",1067),m(617,1,it,Wa),s.Ad=function(n){yP(this,n)},v(Dc,"StreamImpl/ValueConsumer",617),m(1068,1,it,s1),s.Ad=function(n){Ab()},v(Dc,"StreamImpl/lambda$0$Type",1068),m(1069,1,it,Ng),s.Ad=function(n){Ab()},v(Dc,"StreamImpl/lambda$1$Type",1069),m(1070,1,{},$ke),s.Te=function(n,t){return dpn(this.a,n,t)},v(Dc,"StreamImpl/lambda$4$Type",1070),m(1071,1,it,_Te),s.Ad=function(n){H2n(this.b,this.a,n)},v(Dc,"StreamImpl/lambda$5$Type",1071),m(1077,1,it,Rke),s.Ad=function(n){O7n(this.a,u(n,375))},v(Dc,"TerminatableStream/lambda$0$Type",1077),m(2104,1,{}),m(1976,1,{},Ai),v("javaemul.internal","ConsoleLogger",1976);var EBn=0;m(2096,1,{}),m(1800,1,it,Mc),s.Ad=function(n){u(n,321)},v(M8,"BowyerWatsonTriangulation/lambda$0$Type",1800),m(1801,1,it,Bke),s.Ad=function(n){fc(this.a,u(n,321).e)},v(M8,"BowyerWatsonTriangulation/lambda$1$Type",1801),m(1802,1,it,no),s.Ad=function(n){u(n,177)},v(M8,"BowyerWatsonTriangulation/lambda$2$Type",1802),m(1797,1,Ut,zke),s.Le=function(n,t){return pyn(this.a,u(n,177),u(t,177))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(M8,"NaiveMinST/lambda$0$Type",1797),m(440,1,{},tE),v(M8,"NodeMicroLayout",440),m(177,1,{177:1},W5),s.Fb=function(n){var t;return q(n,177)?(t=u(n,177),Uu(this.a,t.a)&&Uu(this.b,t.b)||Uu(this.a,t.b)&&Uu(this.b,t.a)):!1},s.Hb=function(){return j3(this.a)+j3(this.b)};var jBn=v(M8,"TEdge",177);m(321,1,{321:1},ege),s.Fb=function(n){var t;return q(n,321)?(t=u(n,321),iB(this,t.a)&&iB(this,t.b)&&iB(this,t.c)):!1},s.Hb=function(){return j3(this.a)+j3(this.b)+j3(this.c)},v(M8,"TTriangle",321),m(225,1,{225:1},D$),v(M8,"Tree",225),m(1183,1,{},L_e),v(SYe,"Scanline",1183);var dnn=Ji(SYe,AYe);m(1728,1,{},LRe),v(Yh,"CGraph",1728),m(320,1,{320:1},x_e),s.b=0,s.c=0,s.d=0,s.g=0,s.i=0,s.k=Nr,v(Yh,"CGroup",320),m(814,1,{},roe),v(Yh,"CGroup/CGroupBuilder",814),m(60,1,{60:1},KOe),s.Ib=function(){var n;return this.j?Dt(this.j.Kb(this)):(E1(hH),hH.o+"@"+(n=cw(this)>>>0,n.toString(16)))},s.f=0,s.i=Nr;var hH=v(Yh,"CNode",60);m(813,1,{},coe),v(Yh,"CNode/CNodeBuilder",813);var bnn;m(1551,1,{},bb),s.df=function(n,t){return 0},s.ef=function(n,t){return 0},v(Yh,TYe,1551),m(1830,1,{},l1),s.af=function(n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I;for(b=Ki,r=new L(n.a.b);r.ar.d.c||r.d.c==o.d.c&&r.d.b0?n+this.n.d+this.n.a:0},s.gf=function(){var n,t,i,r,c;if(c=0,this.e)this.b?c=this.b.a:this.a[1][1]&&(c=this.a[1][1].gf());else if(this.g)c=K1e(this,YQ(this,null,!0));else for(t=(sa(),z(B(Up,1),ye,237,0,[xu,Oo,Cu])),i=0,r=t.length;i0?c+this.n.b+this.n.c:0},s.hf=function(){var n,t,i,r,c;if(this.g)for(n=YQ(this,null,!1),i=(sa(),z(B(Up,1),ye,237,0,[xu,Oo,Cu])),r=0,c=i.length;r0&&(r[0]+=this.d,i-=r[0]),r[2]>0&&(r[2]+=this.d,i-=r[2]),this.c.a=E.Math.max(0,i),this.c.d=t.d+n.d+(this.c.a-i)/2,r[1]=E.Math.max(r[1],i),Jae(this,Oo,t.d+n.d+r[0]-(r[1]-i)/2,r)},s.b=null,s.d=0,s.e=!1,s.f=!1,s.g=!1;var fte=0,dH=0;v(ng,"GridContainerCell",1499),m(461,23,{3:1,35:1,23:1,461:1},zX);var X0,Sh,Bf,knn=mt(ng,"HorizontalLabelAlignment",461,At,J4n,ppn),Enn;m(318,216,{216:1,318:1},y_e,_Re,d_e),s.ff=function(){return ZNe(this)},s.gf=function(){return ofe(this)},s.a=0,s.c=!1;var SBn=v(ng,"LabelCell",318);m(253,337,{216:1,337:1,253:1},Dj),s.ff=function(){return zj(this)},s.gf=function(){return Fj(this)},s.hf=function(){RZ(this)},s.jf=function(){BZ(this)},s.b=0,s.c=0,s.d=!1,v(ng,"StripContainerCell",253),m(1655,1,Pt,pM),s.Mb=function(n){return Ebn(u(n,216))},v(ng,"StripContainerCell/lambda$0$Type",1655),m(1656,1,{},Zv),s.We=function(n){return u(n,216).gf()},v(ng,"StripContainerCell/lambda$1$Type",1656),m(1657,1,Pt,$m),s.Mb=function(n){return jbn(u(n,216))},v(ng,"StripContainerCell/lambda$2$Type",1657),m(1658,1,{},mM),s.We=function(n){return u(n,216).ff()},v(ng,"StripContainerCell/lambda$3$Type",1658),m(462,23,{3:1,35:1,23:1,462:1},FX);var zf,K0,ba,jnn=mt(ng,"VerticalLabelAlignment",462,At,G4n,mpn),Snn;m(787,1,{},mge),s.c=0,s.d=0,s.k=0,s.s=0,s.t=0,s.v=!1,s.w=0,s.D=!1,s.F=!1,v(rF,"NodeContext",787),m(1497,1,Ut,kM),s.Le=function(n,t){return lCe(u(n,64),u(t,64))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(rF,"NodeContext/0methodref$comparePortSides$Type",1497),m(1498,1,Ut,EM),s.Le=function(n,t){return uTn(u(n,115),u(t,115))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(rF,"NodeContext/1methodref$comparePortContexts$Type",1498),m(168,23,{3:1,35:1,23:1,168:1},Pl);var Ann,Mnn,Tnn,xnn,Cnn,Onn,Nnn,Dnn,Inn,_nn,Lnn,Pnn,$nn,Rnn,Bnn,znn,Fnn,Hnn,Jnn,Gnn,qnn,ate,Unn=mt(rF,"NodeLabelLocation",168,At,TQ,vpn),Xnn;m(115,1,{115:1},Oqe),s.a=!1,v(rF,"PortContext",115),m(1502,1,it,Wv),s.Ad=function(n){SMe(u(n,318))},v(mN,zYe,1502),m(1503,1,Pt,e5),s.Mb=function(n){return!!u(n,115).c},v(mN,FYe,1503),m(1504,1,it,n5),s.Ad=function(n){SMe(u(n,115).c)},v(mN,"LabelPlacer/lambda$2$Type",1504);var Hme;m(1501,1,it,Dg),s.Ad=function(n){Z2(),nbn(u(n,115))},v(mN,"NodeLabelAndSizeUtilities/lambda$0$Type",1501),m(788,1,it,Ble),s.Ad=function(n){ggn(this.b,this.c,this.a,u(n,187))},s.a=!1,s.c=!1,v(mN,"NodeLabelCellCreator/lambda$0$Type",788),m(1500,1,it,Jke),s.Ad=function(n){cbn(this.a,u(n,187))},v(mN,"PortContextCreator/lambda$0$Type",1500);var bH;m(1872,1,{},t5),v(x8,"GreedyRectangleStripOverlapRemover",1872),m(1873,1,Ut,Bm),s.Le=function(n,t){return Xwn(u(n,226),u(t,226))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(x8,"GreedyRectangleStripOverlapRemover/0methodref$compareByYCoordinate$Type",1873),m(1826,1,{},ZSe),s.a=5,s.e=0,v(x8,"RectangleStripOverlapRemover",1826),m(1827,1,Ut,jM),s.Le=function(n,t){return Kwn(u(n,226),u(t,226))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(x8,"RectangleStripOverlapRemover/0methodref$compareLeftRectangleBorders$Type",1827),m(1829,1,Ut,z7),s.Le=function(n,t){return xvn(u(n,226),u(t,226))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(x8,"RectangleStripOverlapRemover/1methodref$compareRightRectangleBorders$Type",1829),m(409,23,{3:1,35:1,23:1,409:1},r$);var GN,hte,dte,qN,Knn=mt(x8,"RectangleStripOverlapRemover/OverlapRemovalDirection",409,At,F6n,ypn),Vnn;m(226,1,{226:1},cV),v(x8,"RectangleStripOverlapRemover/RectangleNode",226),m(1828,1,it,Gke),s.Ad=function(n){$Sn(this.a,u(n,226))},v(x8,"RectangleStripOverlapRemover/lambda$1$Type",1828);var Ynn=!1,zS,Jme;m(1798,1,it,U6),s.Ad=function(n){RKe(u(n,225))},v(e6,"DepthFirstCompaction/0methodref$compactTree$Type",1798),m(810,1,it,Jue),s.Ad=function(n){t5n(this.a,u(n,225))},v(e6,"DepthFirstCompaction/lambda$1$Type",810),m(1799,1,it,ANe),s.Ad=function(n){Sjn(this.a,this.b,this.c,u(n,225))},v(e6,"DepthFirstCompaction/lambda$2$Type",1799);var FS,Gme;m(68,1,{68:1},$_e),v(e6,"Node",68),m(1179,1,{},TCe),v(e6,"ScanlineOverlapCheck",1179),m(1180,1,{683:1},s_e),s._e=function(n){D2n(this,u(n,442))},v(e6,"ScanlineOverlapCheck/OverlapsScanlineHandler",1180),m(1181,1,Ut,zm),s.Le=function(n,t){return aEn(u(n,68),u(t,68))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(e6,"ScanlineOverlapCheck/OverlapsScanlineHandler/lambda$0$Type",1181),m(442,1,{442:1},ese),s.a=!1,v(e6,"ScanlineOverlapCheck/Timestamp",442),m(1182,1,Ut,SM),s.Le=function(n,t){return $An(u(n,442),u(t,442))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(e6,"ScanlineOverlapCheck/lambda$0$Type",1182),m(545,1,{},Fm),v("org.eclipse.elk.alg.common.utils","SVGImage",545),m(748,1,{},X6),v(JW,Xge,748),m(1164,1,Ut,F7),s.Le=function(n,t){return aCn(u(n,235),u(t,235))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(JW,GYe,1164),m(1165,1,it,LTe),s.Ad=function(n){i6n(this.b,this.a,u(n,251))},v(JW,Kge,1165),m(214,1,$w),v(rv,"AbstractLayoutProvider",214),m(726,214,$w,uoe),s.kf=function(n,t){yUe(this,n,t)},v(JW,"ForceLayoutProvider",726);var ABn=Ji(vN,qYe);m(150,1,{3:1,105:1,150:1},Hm),s.of=function(n,t){return yO(this,n,t)},s.lf=function(){return dDe(this)},s.mf=function(n){return T(this,n)},s.nf=function(n){return di(this,n)},v(vN,"MapPropertyHolder",150),m(313,150,{3:1,313:1,105:1,150:1}),v(yN,"FParticle",313),m(251,313,{3:1,251:1,313:1,105:1,150:1},ZDe),s.Ib=function(){var n;return this.a?(n=gu(this.a.a,this,0),n>=0?"b"+n+"["+QV(this.a)+"]":"b["+QV(this.a)+"]"):"b_"+cw(this)},v(yN,"FBendpoint",251),m(291,150,{3:1,291:1,105:1,150:1},UOe),s.Ib=function(){return QV(this)},v(yN,"FEdge",291),m(235,150,{3:1,235:1,105:1,150:1},KR);var MBn=v(yN,"FGraph",235);m(445,313,{3:1,445:1,313:1,105:1,150:1},ePe),s.Ib=function(){return this.b==null||this.b.length==0?"l["+QV(this.a)+"]":"l_"+this.b},v(yN,"FLabel",445),m(155,313,{3:1,155:1,313:1,105:1,150:1},xCe),s.Ib=function(){return pae(this)},s.a=0,v(yN,"FNode",155),m(2062,1,{}),s.qf=function(n){Kbe(this,n)},s.rf=function(){rJe(this)},s.d=0,v(Vge,"AbstractForceModel",2062),m(631,2062,{631:1},XBe),s.pf=function(n,t){var i,r,c,o,l;return FKe(this.f,n,t),c=Or(wc(t.d),n.d),l=E.Math.sqrt(c.a*c.a+c.b*c.b),r=E.Math.max(0,l-ej(n.e)/2-ej(t.e)/2),i=yqe(this.e,n,t),i>0?o=-vvn(r,this.c)*i:o=s2n(r,this.b)*u(T(n,($f(),f6)),15).a,k1(c,o/l),c},s.qf=function(n){Kbe(this,n),this.a=u(T(n,($f(),wH)),15).a,this.c=W(ie(T(n,pH))),this.b=W(ie(T(n,gte)))},s.sf=function(n){return n0&&(o-=mbn(r,this.a)*i),k1(c,o*this.b/l),c},s.qf=function(n){var t,i,r,c,o,l,f;for(Kbe(this,n),this.b=W(ie(T(n,($f(),wte)))),this.c=this.b/u(T(n,wH),15).a,r=n.e.c.length,o=0,c=0,f=new L(n.e);f.a0},s.a=0,s.b=0,s.c=0,v(Vge,"FruchtermanReingoldModel",632);var l6=Ji(mu,"ILayoutMetaDataProvider");m(844,1,Ra,gx),s.tf=function(n){Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,cF),""),"Force Model"),"Determines the model for force calculation."),qme),(Qb(),Ri)),Ume),We((dh(),An))))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Yge),""),"Iterations"),"The number of iterations on the force model."),me(300)),hc),Er),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Qge),""),"Repulsive Power"),"Determines how many bend points are added to the edge; such bend points are regarded as repelling particles in the force model"),me(0)),hc),Er),We(pa)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,GW),""),"FR Temperature"),"The temperature is used as a scaling factor for particle displacements."),vh),Wr),br),We(An)))),Gi(n,GW,cF,itn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,qW),""),"Eades Repulsion"),"Factor for repulsive forces in Eades' model."),5),Wr),br),We(An)))),Gi(n,qW,cF,etn),CVe((new ZL,n))};var Qnn,Znn,qme,Wnn,etn,ntn,ttn,itn;v(fS,"ForceMetaDataProvider",844),m(424,23,{3:1,35:1,23:1,424:1},nse);var bte,gH,Ume=mt(fS,"ForceModelStrategy",424,At,W5n,Epn),rtn;m(984,1,Ra,ZL),s.tf=function(n){CVe(n)};var ctn,utn,Xme,wH,Kme,otn,stn,ltn,ftn,Vme,atn,Yme,Qme,htn,f6,dtn,gte,Zme,btn,gtn,pH,wte,wtn,ptn,mtn,Wme,vtn;v(fS,"ForceOptions",984),m(985,1,{},K6),s.uf=function(){var n;return n=new uoe,n},s.vf=function(n){},v(fS,"ForceOptions/ForceFactory",985);var UN,HS,a6,mH;m(845,1,Ra,WL),s.tf=function(n){Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Wge),""),"Fixed Position"),"Prevent that the node is moved by the layout algorithm."),(Pn(),!1)),(Qb(),Sr)),Yi),We((dh(),lr))))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,ewe),""),"Desired Edge Length"),"Either specified for parent nodes or for individual edges, where the latter takes higher precedence."),100),Wr),br),Ti(An,z(B(Ga,1),ye,160,0,[pa]))))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,nwe),""),"Layout Dimension"),"Dimensions that are permitted to be altered during layout."),e3e),Ri),o3e),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,twe),""),"Stress Epsilon"),"Termination criterion for the iterative process."),vh),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,iwe),""),"Iteration Limit"),"Maximum number of performed iterations. Takes higher precedence than 'epsilon'."),me(ri)),hc),Er),We(An)))),iVe((new yU,n))};var ytn,ktn,e3e,Etn,jtn,Stn;v(fS,"StressMetaDataProvider",845),m(988,1,Ra,yU),s.tf=function(n){iVe(n)};var vH,n3e,t3e,i3e,r3e,c3e,Atn,Mtn,Ttn,xtn,u3e,Ctn;v(fS,"StressOptions",988),m(989,1,{},H7),s.uf=function(){var n;return n=new XOe,n},s.vf=function(n){},v(fS,"StressOptions/StressFactory",989),m(1080,214,$w,XOe),s.kf=function(n,t){var i,r,c,o,l;for(t.Tg(YYe,1),$e(Pe(ve(n,(_O(),r3e))))?$e(Pe(ve(n,u3e)))||FC((i=new tE((Mb(),new s0(n))),i)):yUe(new uoe,n,t.dh(1)),c=Eze(n),r=bKe(this.a,c),l=r.Jc();l.Ob();)o=u(l.Pb(),235),!(o.e.c.length<=1)&&(_Ln(this.b,o),uOn(this.b),Ao(o.d,new J7));c=MVe(r),LVe(c),t.Ug()},v(sF,"StressLayoutProvider",1080),m(1081,1,it,J7),s.Ad=function(n){ige(u(n,445))},v(sF,"StressLayoutProvider/lambda$0$Type",1081),m(986,1,{},USe),s.c=0,s.e=0,s.g=0,v(sF,"StressMajorization",986),m(384,23,{3:1,35:1,23:1,384:1},HX);var pte,mte,vte,o3e=mt(sF,"StressMajorization/Dimension",384,At,F4n,jpn),Otn;m(987,1,Ut,qke),s.Le=function(n,t){return W2n(this.a,u(n,155),u(t,155))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(sF,"StressMajorization/lambda$0$Type",987),m(1161,1,{},oLe),v(i6,"ElkLayered",1161),m(1162,1,it,Uke),s.Ad=function(n){Kxn(this.a,u(n,37))},v(i6,"ElkLayered/lambda$0$Type",1162),m(1163,1,it,Xke),s.Ad=function(n){cpn(this.a,u(n,37))},v(i6,"ElkLayered/lambda$1$Type",1163),m(1246,1,{},MCe);var Ntn,Dtn,Itn;v(i6,"GraphConfigurator",1246),m(757,1,it,Gue),s.Ad=function(n){kGe(this.a,u(n,9))},v(i6,"GraphConfigurator/lambda$0$Type",757),m(758,1,{},Wd),s.Kb=function(n){return zde(),new bn(null,new wn(u(n,25).a,16))},v(i6,"GraphConfigurator/lambda$1$Type",758),m(759,1,it,que),s.Ad=function(n){kGe(this.a,u(n,9))},v(i6,"GraphConfigurator/lambda$2$Type",759),m(1079,214,$w,KSe),s.kf=function(n,t){var i;i=hLn(new eAe,n),ue(ve(n,(Ce(),tm)))===ue((_1(),Gd))?kEn(this.a,i,t):tOn(this.a,i,t),t.Zg()||vVe(new px,i)},v(i6,"LayeredLayoutProvider",1079),m(363,23,{3:1,35:1,23:1,363:1},rC);var Ff,Wh,Zu,Wu,_c,s3e=mt(i6,"LayeredPhases",363,At,$yn,Spn),_tn;m(1683,1,{},dBe),s.i=0;var Ltn;v(MN,"ComponentsToCGraphTransformer",1683);var Ptn;m(1684,1,{},Ql),s.wf=function(n,t){return E.Math.min(n.a!=null?W(n.a):n.c.i,t.a!=null?W(t.a):t.c.i)},s.xf=function(n,t){return E.Math.min(n.a!=null?W(n.a):n.c.i,t.a!=null?W(t.a):t.c.i)},v(MN,"ComponentsToCGraphTransformer/1",1684),m(82,1,{82:1}),s.i=0,s.k=!0,s.o=Nr;var yte=v(bS,"CNode",82);m(460,82,{460:1,82:1},ile,gde),s.Ib=function(){return""},v(MN,"ComponentsToCGraphTransformer/CRectNode",460),m(1652,1,{},Ea);var kte,Ete;v(MN,"OneDimensionalComponentsCompaction",1652),m(1653,1,{},Ig),s.Kb=function(n){return v4n(u(n,49))},s.Fb=function(n){return this===n},v(MN,"OneDimensionalComponentsCompaction/lambda$0$Type",1653),m(1654,1,{},i5),s.Kb=function(n){return MEn(u(n,49))},s.Fb=function(n){return this===n},v(MN,"OneDimensionalComponentsCompaction/lambda$1$Type",1654),m(1686,1,{},fIe),v(bS,"CGraph",1686),m(194,1,{194:1},SQ),s.b=0,s.c=0,s.e=0,s.g=!0,s.i=Nr,v(bS,"CGroup",194),m(1685,1,{},G7),s.wf=function(n,t){return E.Math.max(n.a!=null?W(n.a):n.c.i,t.a!=null?W(t.a):t.c.i)},s.xf=function(n,t){return E.Math.max(n.a!=null?W(n.a):n.c.i,t.a!=null?W(t.a):t.c.i)},v(bS,TYe,1685),m(1687,1,{},Eqe),s.d=!1;var $tn,jte=v(bS,OYe,1687);m(1688,1,{},AM),s.Kb=function(n){return Goe(),Pn(),u(u(n,49).a,82).d.e!=0},s.Fb=function(n){return this===n},v(bS,NYe,1688),m(817,1,{},lfe),s.a=!1,s.b=!1,s.c=!1,s.d=!1,v(bS,DYe,817),m(1868,1,{},SDe),v(lF,IYe,1868);var XN=Ji(tg,AYe);m(1869,1,{377:1},o_e),s._e=function(n){lDn(this,u(n,465))},v(lF,_Ye,1869),m(1870,1,Ut,q7),s.Le=function(n,t){return h5n(u(n,82),u(t,82))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(lF,LYe,1870),m(465,1,{465:1},tse),s.a=!1,v(lF,PYe,465),m(1871,1,Ut,Jm),s.Le=function(n,t){return RAn(u(n,465),u(t,465))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(lF,$Ye,1871),m(146,1,{146:1},c9,Zle),s.Fb=function(n){var t;return n==null||TBn!=Gs(n)?!1:(t=u(n,146),Uu(this.c,t.c)&&Uu(this.d,t.d))},s.Hb=function(){return PB(z(B(Mr,1),xn,1,5,[this.c,this.d]))},s.Ib=function(){return"("+this.c+xo+this.d+(this.a?"cx":"")+this.b+")"},s.a=!0,s.c=0,s.d=0;var TBn=v(tg,"Point",146);m(408,23,{3:1,35:1,23:1,408:1},u$);var Xw,Xp,bv,Kp,Rtn=mt(tg,"Point/Quadrant",408,At,H6n,kpn),Btn;m(1674,1,{},VSe),s.b=null,s.c=null,s.d=null,s.e=null,s.f=null;var ztn,Ftn,Htn,Jtn,Gtn;v(tg,"RectilinearConvexHull",1674),m(569,1,{377:1},iz),s._e=function(n){x9n(this,u(n,146))},s.b=0;var l3e;v(tg,"RectilinearConvexHull/MaximalElementsEventHandler",569),m(1676,1,Ut,V6),s.Le=function(n,t){return f5n(ie(n),ie(t))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(tg,"RectilinearConvexHull/MaximalElementsEventHandler/lambda$0$Type",1676),m(1675,1,{377:1},vRe),s._e=function(n){ANn(this,u(n,146))},s.a=0,s.b=null,s.c=null,s.d=null,s.e=null,v(tg,"RectilinearConvexHull/RectangleEventHandler",1675),m(1677,1,Ut,a2),s.Le=function(n,t){return h6n(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(tg,"RectilinearConvexHull/lambda$0$Type",1677),m(1678,1,Ut,h2),s.Le=function(n,t){return d6n(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(tg,"RectilinearConvexHull/lambda$1$Type",1678),m(1679,1,Ut,d2),s.Le=function(n,t){return g6n(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(tg,"RectilinearConvexHull/lambda$2$Type",1679),m(1680,1,Ut,b2),s.Le=function(n,t){return b6n(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(tg,"RectilinearConvexHull/lambda$3$Type",1680),m(1681,1,Ut,El),s.Le=function(n,t){return kTn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(tg,"RectilinearConvexHull/lambda$4$Type",1681),m(1682,1,{},P_e),v(tg,"Scanline",1682),m(2066,1,{}),v(Ba,"AbstractGraphPlacer",2066),m(336,1,{336:1},pOe),s.Df=function(n){return this.Ef(n)?(dn(this.b,u(T(n,(pe(),J1)),22),n),!0):!1},s.Ef=function(n){var t,i,r,c;for(t=u(T(n,(pe(),J1)),22),c=u(wi(Si,t),22),r=c.Jc();r.Ob();)if(i=u(r.Pb(),22),!u(wi(this.b,i),16).dc())return!1;return!0};var Si;v(Ba,"ComponentGroup",336),m(766,2066,{},ooe),s.Ff=function(n){var t,i;for(i=new L(this.a);i.ai&&(p=0,y+=f+r,f=0),h=o.c,d8(o,p+h.a,y+h.b),ta(h),c=E.Math.max(c,p+b.a),f=E.Math.max(f,b.b),p+=b.a+r;t.f.a=c,t.f.b=y+f},s.Hf=function(n,t){var i,r,c,o,l;if(ue(T(t,(Ce(),iA)))===ue((P4(),JS))){for(r=n.Jc();r.Ob();){for(i=u(r.Pb(),37),l=0,o=new L(i.a);o.ai&&!u(T(o,(pe(),J1)),22).Gc((Oe(),Xn))||h&&u(T(h,(pe(),J1)),22).Gc((Oe(),Wn))||u(T(o,(pe(),J1)),22).Gc((Oe(),Kn)))&&(S=y,M+=f+r,f=0),b=o.c,u(T(o,(pe(),J1)),22).Gc((Oe(),Xn))&&(S=c+r),d8(o,S+b.a,M+b.b),c=E.Math.max(c,S+p.a),u(T(o,J1),22).Gc(dt)&&(y=E.Math.max(y,S+p.a+r)),ta(b),f=E.Math.max(f,p.b),S+=p.a+r,h=o;t.f.a=c,t.f.b=M+f},s.Hf=function(n,t){},v(Ba,"ModelOrderRowGraphPlacer",1277),m(1275,1,Ut,MM),s.Le=function(n,t){return x7n(u(n,37),u(t,37))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Ba,"SimpleRowGraphPlacer/1",1275);var Utn;m(1245,1,mh,Y6),s.Lb=function(n){var t;return t=u(T(u(n,250).b,(Ce(),Qc)),78),!!t&&t.b!=0},s.Fb=function(n){return this===n},s.Mb=function(n){var t;return t=u(T(u(n,250).b,(Ce(),Qc)),78),!!t&&t.b!=0},v(fF,"CompoundGraphPostprocessor/1",1245),m(1244,1,Mi,nAe),s.If=function(n,t){zHe(this,u(n,37),t)},v(fF,"CompoundGraphPreprocessor",1244),m(444,1,{444:1},TFe),s.c=!1,v(fF,"CompoundGraphPreprocessor/ExternalPort",444),m(250,1,{250:1},K$),s.Ib=function(){return DK(this.c)+":"+wqe(this.b)},v(fF,"CrossHierarchyEdge",250),m(764,1,Ut,Uue),s.Le=function(n,t){return fAn(this,u(n,250),u(t,250))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(fF,"CrossHierarchyEdgeComparator",764),m(246,150,{3:1,246:1,105:1,150:1}),s.p=0,v(Qu,"LGraphElement",246),m(17,246,{3:1,17:1,246:1,105:1,150:1},dw),s.Ib=function(){return wqe(this)};var e7=v(Qu,"LEdge",17);m(37,246,{3:1,20:1,37:1,246:1,105:1,150:1},xhe),s.Ic=function(n){rc(this,n)},s.Jc=function(){return new L(this.b)},s.Ib=function(){return this.b.c.length==0?"G-unlayered"+_a(this.a):this.a.c.length==0?"G-layered"+_a(this.b):"G[layerless"+_a(this.a)+", layers"+_a(this.b)+"]"};var Xtn=v(Qu,"LGraph",37),Ktn;m(655,1,{}),s.Jf=function(){return this.e.n},s.mf=function(n){return T(this.e,n)},s.Kf=function(){return this.e.o},s.Lf=function(){return this.e.p},s.nf=function(n){return di(this.e,n)},s.Mf=function(n){this.e.n.a=n.a,this.e.n.b=n.b},s.Nf=function(n){this.e.o.a=n.a,this.e.o.b=n.b},s.Of=function(n){this.e.p=n},v(Qu,"LGraphAdapters/AbstractLShapeAdapter",655),m(464,1,{837:1},iE),s.Pf=function(){var n,t;if(!this.b)for(this.b=Ph(this.a.b.c.length),t=new L(this.a.b);t.a0&&iFe((Yn(t-1,n.length),n.charCodeAt(t-1)),tQe);)--t;if(o> ",n),hz(i)),Gt(co((n.a+="[",n),i.i),"]")),n.a},s.c=!0,s.d=!1;var b3e,g3e,w3e,p3e,m3e,v3e,Ytn=v(Qu,"LPort",12);m(399,1,Xh,Xy),s.Ic=function(n){rc(this,n)},s.Jc=function(){var n;return n=new L(this.a.e),new Kke(n)},v(Qu,"LPort/1",399),m(1273,1,zr,Kke),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(_(this.a),17).c},s.Ob=function(){return du(this.a)},s.Qb=function(){QE(this.a)},v(Qu,"LPort/1/1",1273),m(365,1,Xh,F5),s.Ic=function(n){rc(this,n)},s.Jc=function(){var n;return n=new L(this.a.g),new Xue(n)},v(Qu,"LPort/2",365),m(763,1,zr,Xue),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(_(this.a),17).d},s.Ob=function(){return du(this.a)},s.Qb=function(){QE(this.a)},v(Qu,"LPort/2/1",763),m(1266,1,Xh,$Te),s.Ic=function(n){rc(this,n)},s.Jc=function(){return new xa(this)},v(Qu,"LPort/CombineIter",1266),m(207,1,zr,xa),s.Nb=function(n){Zr(this,n)},s.Qb=function(){wMe()},s.Ob=function(){return HE(this)},s.Pb=function(){return du(this.a)?_(this.a):_(this.b)},v(Qu,"LPort/CombineIter/1",207),m(1267,1,mh,c5),s.Lb=function(n){return LDe(n)},s.Fb=function(n){return this===n},s.Mb=function(n){return os(),u(n,12).g.c.length!=0},v(Qu,"LPort/lambda$0$Type",1267),m(1268,1,mh,e0),s.Lb=function(n){return PDe(n)},s.Fb=function(n){return this===n},s.Mb=function(n){return os(),u(n,12).e.c.length!=0},v(Qu,"LPort/lambda$1$Type",1268),m(1269,1,mh,xh),s.Lb=function(n){return os(),u(n,12).j==(Oe(),Xn)},s.Fb=function(n){return this===n},s.Mb=function(n){return os(),u(n,12).j==(Oe(),Xn)},v(Qu,"LPort/lambda$2$Type",1269),m(1270,1,mh,V7),s.Lb=function(n){return os(),u(n,12).j==(Oe(),Wn)},s.Fb=function(n){return this===n},s.Mb=function(n){return os(),u(n,12).j==(Oe(),Wn)},v(Qu,"LPort/lambda$3$Type",1270),m(1271,1,mh,TM),s.Lb=function(n){return os(),u(n,12).j==(Oe(),dt)},s.Fb=function(n){return this===n},s.Mb=function(n){return os(),u(n,12).j==(Oe(),dt)},v(Qu,"LPort/lambda$4$Type",1271),m(1272,1,mh,u5),s.Lb=function(n){return os(),u(n,12).j==(Oe(),Kn)},s.Fb=function(n){return this===n},s.Mb=function(n){return os(),u(n,12).j==(Oe(),Kn)},v(Qu,"LPort/lambda$5$Type",1272),m(25,246,{3:1,20:1,246:1,25:1,105:1,150:1},qu),s.Ic=function(n){rc(this,n)},s.Jc=function(){return new L(this.a)},s.Ib=function(){return"L_"+gu(this.b.b,this,0)+_a(this.a)},v(Qu,"Layer",25),m(1659,1,{},A$e),s.b=0,v(Qu,"Tarjan",1659),m(1282,1,{},eAe),v(_d,uQe,1282),m(1286,1,{},Y7),s.Kb=function(n){return iu(u(n,84))},v(_d,"ElkGraphImporter/0methodref$connectableShapeToNode$Type",1286),m(1289,1,{},qm),s.Kb=function(n){return iu(u(n,84))},v(_d,"ElkGraphImporter/1methodref$connectableShapeToNode$Type",1289),m(1283,1,it,Vke),s.Ad=function(n){Iqe(this.a,u(n,125))},v(_d,Kge,1283),m(1284,1,it,Yke),s.Ad=function(n){Iqe(this.a,u(n,125))},v(_d,oQe,1284),m(1285,1,{},Ch),s.Kb=function(n){return new bn(null,new wn(Xfe(u(n,85)),16))},v(_d,sQe,1285),m(1287,1,Pt,Qke),s.Mb=function(n){return nwn(this.a,u(n,26))},v(_d,lQe,1287),m(1288,1,{},Um),s.Kb=function(n){return new bn(null,new wn(o5n(u(n,85)),16))},v(_d,"ElkGraphImporter/lambda$5$Type",1288),m(1290,1,Pt,Zke),s.Mb=function(n){return twn(this.a,u(n,26))},v(_d,"ElkGraphImporter/lambda$7$Type",1290),m(1291,1,Pt,Q7),s.Mb=function(n){return E5n(u(n,85))},v(_d,"ElkGraphImporter/lambda$8$Type",1291),m(1261,1,{},px);var Qtn;v(_d,"ElkGraphLayoutTransferrer",1261),m(1262,1,Pt,Wke),s.Mb=function(n){return X2n(this.a,u(n,17))},v(_d,"ElkGraphLayoutTransferrer/lambda$0$Type",1262),m(1263,1,it,eEe),s.Ad=function(n){nC(),Te(this.a,u(n,17))},v(_d,"ElkGraphLayoutTransferrer/lambda$1$Type",1263),m(1264,1,Pt,nEe),s.Mb=function(n){return I2n(this.a,u(n,17))},v(_d,"ElkGraphLayoutTransferrer/lambda$2$Type",1264),m(1265,1,it,tEe),s.Ad=function(n){nC(),Te(this.a,u(n,17))},v(_d,"ElkGraphLayoutTransferrer/lambda$3$Type",1265),m(806,1,{},Mle),v(Qn,"BiLinkedHashMultiMap",806),m(1511,1,Mi,Q6),s.If=function(n,t){X8n(u(n,37),t)},v(Qn,"CommentNodeMarginCalculator",1511),m(1512,1,{},Lg),s.Kb=function(n){return new bn(null,new wn(u(n,25).a,16))},v(Qn,"CommentNodeMarginCalculator/lambda$0$Type",1512),m(1513,1,it,pI),s.Ad=function(n){lLn(u(n,9))},v(Qn,"CommentNodeMarginCalculator/lambda$1$Type",1513),m(1514,1,Mi,xM),s.If=function(n,t){wDn(u(n,37),t)},v(Qn,"CommentPostprocessor",1514),m(1515,1,Mi,mI),s.If=function(n,t){P$n(u(n,37),t)},v(Qn,"CommentPreprocessor",1515),m(1516,1,Mi,o5),s.If=function(n,t){CNn(u(n,37),t)},v(Qn,"ConstraintsPostprocessor",1516),m(1517,1,Mi,nq),s.If=function(n,t){m7n(u(n,37),t)},v(Qn,"EdgeAndLayerConstraintEdgeReverser",1517),m(1518,1,Mi,vI),s.If=function(n,t){XEn(u(n,37),t)},v(Qn,"EndLabelPostprocessor",1518),m(1519,1,{},yI),s.Kb=function(n){return new bn(null,new wn(u(n,25).a,16))},v(Qn,"EndLabelPostprocessor/lambda$0$Type",1519),m(1520,1,Pt,CM),s.Mb=function(n){return Nyn(u(n,9))},v(Qn,"EndLabelPostprocessor/lambda$1$Type",1520),m(1521,1,it,tq),s.Ad=function(n){BAn(u(n,9))},v(Qn,"EndLabelPostprocessor/lambda$2$Type",1521),m(1522,1,Mi,iq),s.If=function(n,t){kxn(u(n,37),t)},v(Qn,"EndLabelPreprocessor",1522),m(1523,1,{},Z7),s.Kb=function(n){return new bn(null,new wn(u(n,25).a,16))},v(Qn,"EndLabelPreprocessor/lambda$0$Type",1523),m(1524,1,it,MNe),s.Ad=function(n){wgn(this.a,this.b,this.c,u(n,9))},s.a=0,s.b=0,s.c=!1,v(Qn,"EndLabelPreprocessor/lambda$1$Type",1524),m(1525,1,Pt,Pg),s.Mb=function(n){return ue(T(u(n,70),(Ce(),Mh)))===ue((Oa(),x7))},v(Qn,"EndLabelPreprocessor/lambda$2$Type",1525),m(1526,1,it,iEe),s.Ad=function(n){qt(this.a,u(n,70))},v(Qn,"EndLabelPreprocessor/lambda$3$Type",1526),m(1527,1,Pt,OM),s.Mb=function(n){return ue(T(u(n,70),(Ce(),Mh)))===ue((Oa(),ym))},v(Qn,"EndLabelPreprocessor/lambda$4$Type",1527),m(1528,1,it,rEe),s.Ad=function(n){qt(this.a,u(n,70))},v(Qn,"EndLabelPreprocessor/lambda$5$Type",1528),m(1576,1,Mi,kU),s.If=function(n,t){sEn(u(n,37),t)};var Ztn;v(Qn,"EndLabelSorter",1576),m(1577,1,Ut,NM),s.Le=function(n,t){return Cjn(u(n,455),u(t,455))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qn,"EndLabelSorter/1",1577),m(455,1,{455:1},ZIe),v(Qn,"EndLabelSorter/LabelGroup",455),m(1578,1,{},s5),s.Kb=function(n){return eC(),new bn(null,new wn(u(n,25).a,16))},v(Qn,"EndLabelSorter/lambda$0$Type",1578),m(1579,1,Pt,l5),s.Mb=function(n){return eC(),u(n,9).k==(zn(),Qi)},v(Qn,"EndLabelSorter/lambda$1$Type",1579),m(1580,1,it,kI),s.Ad=function(n){LTn(u(n,9))},v(Qn,"EndLabelSorter/lambda$2$Type",1580),m(1581,1,Pt,DM),s.Mb=function(n){return eC(),ue(T(u(n,70),(Ce(),Mh)))===ue((Oa(),ym))},v(Qn,"EndLabelSorter/lambda$3$Type",1581),m(1582,1,Pt,EI),s.Mb=function(n){return eC(),ue(T(u(n,70),(Ce(),Mh)))===ue((Oa(),x7))},v(Qn,"EndLabelSorter/lambda$4$Type",1582),m(1529,1,Mi,Z6),s.If=function(n,t){MLn(this,u(n,37))},s.b=0,s.c=0,v(Qn,"FinalSplineBendpointsCalculator",1529),m(1530,1,{},$g),s.Kb=function(n){return new bn(null,new wn(u(n,25).a,16))},v(Qn,"FinalSplineBendpointsCalculator/lambda$0$Type",1530),m(1531,1,{},IM),s.Kb=function(n){return new bn(null,new cp(new qn(Vn(Ni(u(n,9)).a.Jc(),new ne))))},v(Qn,"FinalSplineBendpointsCalculator/lambda$1$Type",1531),m(1532,1,Pt,W6),s.Mb=function(n){return!cc(u(n,17))},v(Qn,"FinalSplineBendpointsCalculator/lambda$2$Type",1532),m(1533,1,Pt,w2),s.Mb=function(n){return di(u(n,17),(pe(),ag))},v(Qn,"FinalSplineBendpointsCalculator/lambda$3$Type",1533),m(1534,1,it,cEe),s.Ad=function(n){_In(this.a,u(n,132))},v(Qn,"FinalSplineBendpointsCalculator/lambda$4$Type",1534),m(1535,1,it,_M),s.Ad=function(n){FO(u(n,17).a)},v(Qn,"FinalSplineBendpointsCalculator/lambda$5$Type",1535),m(790,1,Mi,Kue),s.If=function(n,t){yPn(this,u(n,37),t)},v(Qn,"GraphTransformer",790),m(502,23,{3:1,35:1,23:1,502:1},ise);var Tte,VN,Wtn=mt(Qn,"GraphTransformer/Mode",502,At,e4n,Tpn),ein;m(1536,1,Mi,W7),s.If=function(n,t){HOn(u(n,37),t)},v(Qn,"HierarchicalNodeResizingProcessor",1536),m(1537,1,Mi,jI),s.If=function(n,t){_8n(u(n,37),t)},v(Qn,"HierarchicalPortConstraintProcessor",1537),m(1538,1,Ut,ek),s.Le=function(n,t){return Vjn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qn,"HierarchicalPortConstraintProcessor/NodeComparator",1538),m(1539,1,Mi,nk),s.If=function(n,t){T_n(u(n,37),t)},v(Qn,"HierarchicalPortDummySizeProcessor",1539),m(1540,1,Mi,SI),s.If=function(n,t){zDn(this,u(n,37),t)},s.a=0,v(Qn,"HierarchicalPortOrthogonalEdgeRouter",1540),m(1541,1,Ut,Oh),s.Le=function(n,t){return Vwn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qn,"HierarchicalPortOrthogonalEdgeRouter/1",1541),m(1542,1,Ut,Xm),s.Le=function(n,t){return I9n(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qn,"HierarchicalPortOrthogonalEdgeRouter/2",1542),m(1543,1,Mi,tk),s.If=function(n,t){mTn(u(n,37),t)},v(Qn,"HierarchicalPortPositionProcessor",1543),m(1544,1,Mi,wx),s.If=function(n,t){vRn(this,u(n,37))},s.a=0,s.c=0;var yH,kH;v(Qn,"HighDegreeNodeLayeringProcessor",1544),m(566,1,{566:1},ey),s.b=-1,s.d=-1,v(Qn,"HighDegreeNodeLayeringProcessor/HighDegreeNodeInformation",566),m(1545,1,{},rq),s.Kb=function(n){return xC(),rr(u(n,9))},s.Fb=function(n){return this===n},v(Qn,"HighDegreeNodeLayeringProcessor/lambda$0$Type",1545),m(1546,1,{},LM),s.Kb=function(n){return xC(),Ni(u(n,9))},s.Fb=function(n){return this===n},v(Qn,"HighDegreeNodeLayeringProcessor/lambda$1$Type",1546),m(1552,1,Mi,PM),s.If=function(n,t){m_n(this,u(n,37),t)},v(Qn,"HyperedgeDummyMerger",1552),m(791,1,{},Jle),s.a=!1,s.b=!1,s.c=!1,v(Qn,"HyperedgeDummyMerger/MergeState",791),m(1553,1,{},ny),s.Kb=function(n){return new bn(null,new wn(u(n,25).a,16))},v(Qn,"HyperedgeDummyMerger/lambda$0$Type",1553),m(1554,1,{},ik),s.Kb=function(n){return new bn(null,new wn(u(n,9).j,16))},v(Qn,"HyperedgeDummyMerger/lambda$1$Type",1554),m(1555,1,it,AI),s.Ad=function(n){u(n,12).p=-1},v(Qn,"HyperedgeDummyMerger/lambda$2$Type",1555),m(1556,1,Mi,uq),s.If=function(n,t){p_n(u(n,37),t)},v(Qn,"HypernodesProcessor",1556),m(1557,1,Mi,oq),s.If=function(n,t){M_n(u(n,37),t)},v(Qn,"InLayerConstraintProcessor",1557),m(1558,1,Mi,$M),s.If=function(n,t){s7n(u(n,37),t)},v(Qn,"InnermostNodeMarginCalculator",1558),m(1559,1,Mi,sq),s.If=function(n,t){D$n(this,u(n,37))},s.a=Nr,s.b=Nr,s.c=Ki,s.d=Ki;var xBn=v(Qn,"InteractiveExternalPortPositioner",1559);m(1560,1,{},lq),s.Kb=function(n){return u(n,17).d.i},s.Fb=function(n){return this===n},v(Qn,"InteractiveExternalPortPositioner/lambda$0$Type",1560),m(1561,1,{},uEe),s.Kb=function(n){return Ywn(this.a,ie(n))},s.Fb=function(n){return this===n},v(Qn,"InteractiveExternalPortPositioner/lambda$1$Type",1561),m(1562,1,{},fq),s.Kb=function(n){return u(n,17).c.i},s.Fb=function(n){return this===n},v(Qn,"InteractiveExternalPortPositioner/lambda$2$Type",1562),m(1563,1,{},oEe),s.Kb=function(n){return Qwn(this.a,ie(n))},s.Fb=function(n){return this===n},v(Qn,"InteractiveExternalPortPositioner/lambda$3$Type",1563),m(1564,1,{},sEe),s.Kb=function(n){return q2n(this.a,ie(n))},s.Fb=function(n){return this===n},v(Qn,"InteractiveExternalPortPositioner/lambda$4$Type",1564),m(1565,1,{},lEe),s.Kb=function(n){return U2n(this.a,ie(n))},s.Fb=function(n){return this===n},v(Qn,"InteractiveExternalPortPositioner/lambda$5$Type",1565),m(79,23,{3:1,35:1,23:1,79:1,196:1},dr),s.bg=function(){switch(this.g){case 15:return new zg;case 22:return new E2;case 48:return new rT;case 29:case 36:return new mq;case 33:return new Q6;case 43:return new xM;case 1:return new mI;case 42:return new o5;case 57:return new Kue((_9(),VN));case 0:return new Kue((_9(),Tte));case 2:return new nq;case 55:return new vI;case 34:return new iq;case 52:return new Z6;case 56:return new W7;case 13:return new jI;case 39:return new nk;case 45:return new SI;case 41:return new tk;case 9:return new wx;case 50:return new fOe;case 38:return new PM;case 44:return new uq;case 28:return new oq;case 31:return new $M;case 3:return new sq;case 18:return new cq;case 30:return new aq;case 5:return new EU;case 51:return new bq;case 35:return new Py;case 37:return new vq;case 53:return new kU;case 11:return new TI;case 7:return new jU;case 40:return new yq;case 46:return new kq;case 16:return new Eq;case 10:return new hxe;case 49:return new Mq;case 21:return new Tq;case 23:return new RP((Ub(),pA));case 8:return new BM;case 12:return new Cq;case 4:return new xI;case 19:return new eP;case 17:return new _I;case 54:return new iy;case 6:return new Pq;case 25:return new iAe;case 26:return new tT;case 47:return new FM;case 32:return new QOe;case 14:return new HI;case 27:return new Gq;case 20:return new uy;case 24:return new RP((Ub(),TJ));default:throw P(new Gn(YW+(this.f!=null?this.f:""+this.g)))}};var y3e,k3e,E3e,j3e,S3e,A3e,M3e,T3e,x3e,C3e,O3e,gv,EH,jH,N3e,D3e,I3e,_3e,L3e,P3e,$3e,qS,R3e,B3e,z3e,F3e,H3e,xte,SH,AH,J3e,MH,TH,xH,n7,Vp,Yp,G3e,CH,OH,q3e,NH,DH,U3e,X3e,K3e,V3e,IH,Cte,h6,_H,LH,PH,$H,Y3e,Q3e,Z3e,W3e,CBn=mt(Qn,QW,79,At,DUe,xpn),nin;m(1566,1,Mi,cq),s.If=function(n,t){C$n(u(n,37),t)},v(Qn,"InvertedPortProcessor",1566),m(1567,1,Mi,aq),s.If=function(n,t){xIn(u(n,37),t)},v(Qn,"LabelAndNodeSizeProcessor",1567),m(1568,1,Pt,hq),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Qn,"LabelAndNodeSizeProcessor/lambda$0$Type",1568),m(1569,1,Pt,MI),s.Mb=function(n){return u(n,9).k==(zn(),gr)},v(Qn,"LabelAndNodeSizeProcessor/lambda$1$Type",1569),m(1570,1,it,CNe),s.Ad=function(n){pgn(this.b,this.a,this.c,u(n,9))},s.a=!1,s.c=!1,v(Qn,"LabelAndNodeSizeProcessor/lambda$2$Type",1570),m(1571,1,Mi,EU),s.If=function(n,t){o$n(u(n,37),t)};var tin;v(Qn,"LabelDummyInserter",1571),m(1572,1,mh,dq),s.Lb=function(n){return ue(T(u(n,70),(Ce(),Mh)))===ue((Oa(),T7))},s.Fb=function(n){return this===n},s.Mb=function(n){return ue(T(u(n,70),(Ce(),Mh)))===ue((Oa(),T7))},v(Qn,"LabelDummyInserter/1",1572),m(1573,1,Mi,bq),s.If=function(n,t){KPn(u(n,37),t)},v(Qn,"LabelDummyRemover",1573),m(1574,1,Pt,gq),s.Mb=function(n){return $e(Pe(T(u(n,70),(Ce(),Tv))))},v(Qn,"LabelDummyRemover/lambda$0$Type",1574),m(1332,1,Mi,Py),s.If=function(n,t){HPn(this,u(n,37),t)},s.a=null;var Ote;v(Qn,"LabelDummySwitcher",1332),m(294,1,{294:1},xXe),s.c=0,s.d=null,s.f=0,v(Qn,"LabelDummySwitcher/LabelDummyInfo",294),m(1333,1,{},wq),s.Kb=function(n){return C4(),new bn(null,new wn(u(n,25).a,16))},v(Qn,"LabelDummySwitcher/lambda$0$Type",1333),m(1334,1,Pt,RM),s.Mb=function(n){return C4(),u(n,9).k==(zn(),Gu)},v(Qn,"LabelDummySwitcher/lambda$1$Type",1334),m(1335,1,{},fEe),s.Kb=function(n){return _2n(this.a,u(n,9))},v(Qn,"LabelDummySwitcher/lambda$2$Type",1335),m(1336,1,it,aEe),s.Ad=function(n){Pvn(this.a,u(n,294))},v(Qn,"LabelDummySwitcher/lambda$3$Type",1336),m(1337,1,Ut,pq),s.Le=function(n,t){return avn(u(n,294),u(t,294))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qn,"LabelDummySwitcher/lambda$4$Type",1337),m(789,1,Mi,mq),s.If=function(n,t){d9n(u(n,37),t)},v(Qn,"LabelManagementProcessor",789),m(1575,1,Mi,vq),s.If=function(n,t){cDn(u(n,37),t)},v(Qn,"LabelSideSelector",1575),m(1583,1,Mi,TI),s.If=function(n,t){G_n(u(n,37),t)},v(Qn,"LayerConstraintPostprocessor",1583),m(1584,1,Mi,jU),s.If=function(n,t){HCn(u(n,37),t)};var eve;v(Qn,"LayerConstraintPreprocessor",1584),m(367,23,{3:1,35:1,23:1,367:1},s$);var YN,RH,BH,Nte,iin=mt(Qn,"LayerConstraintPreprocessor/HiddenNodeConnections",367,At,q6n,fmn),rin;m(1585,1,Mi,yq),s.If=function(n,t){sPn(u(n,37),t)},v(Qn,"LayerSizeAndGraphHeightCalculator",1585),m(1586,1,Mi,kq),s.If=function(n,t){JOn(u(n,37),t)},v(Qn,"LongEdgeJoiner",1586),m(1587,1,Mi,Eq),s.If=function(n,t){RLn(u(n,37),t)},v(Qn,"LongEdgeSplitter",1587),m(1588,1,Mi,hxe),s.If=function(n,t){k$n(this,u(n,37),t)},s.e=0,s.f=0,s.j=0,s.k=0,s.n=0,s.o=0;var cin,uin;v(Qn,"NodePromotion",1588),m(1589,1,Ut,jq),s.Le=function(n,t){return hkn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qn,"NodePromotion/1",1589),m(1590,1,Ut,Sq),s.Le=function(n,t){return dkn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qn,"NodePromotion/2",1590),m(1591,1,{},Aq),s.Kb=function(n){return u(n,49),U$(),Pn(),!0},s.Fb=function(n){return this===n},v(Qn,"NodePromotion/lambda$0$Type",1591),m(1592,1,{},hEe),s.Kb=function(n){return m4n(this.a,u(n,49))},s.Fb=function(n){return this===n},s.a=0,v(Qn,"NodePromotion/lambda$1$Type",1592),m(1593,1,{},dEe),s.Kb=function(n){return p4n(this.a,u(n,49))},s.Fb=function(n){return this===n},s.a=0,v(Qn,"NodePromotion/lambda$2$Type",1593),m(1594,1,Mi,Mq),s.If=function(n,t){fRn(u(n,37),t)},v(Qn,"NorthSouthPortPostprocessor",1594),m(1595,1,Mi,Tq),s.If=function(n,t){wRn(u(n,37),t)},v(Qn,"NorthSouthPortPreprocessor",1595),m(1596,1,Ut,xq),s.Le=function(n,t){return N7n(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qn,"NorthSouthPortPreprocessor/lambda$0$Type",1596),m(1597,1,Mi,BM),s.If=function(n,t){o_n(u(n,37),t)},v(Qn,"PartitionMidprocessor",1597),m(1598,1,Pt,ty),s.Mb=function(n){return di(u(n,9),(Ce(),rm))},v(Qn,"PartitionMidprocessor/lambda$0$Type",1598),m(1599,1,it,bEe),s.Ad=function(n){k5n(this.a,u(n,9))},v(Qn,"PartitionMidprocessor/lambda$1$Type",1599),m(1600,1,Mi,Cq),s.If=function(n,t){fNn(u(n,37),t)},v(Qn,"PartitionPostprocessor",1600),m(1601,1,Mi,xI),s.If=function(n,t){dIn(u(n,37),t)},v(Qn,"PartitionPreprocessor",1601),m(1602,1,Pt,CI),s.Mb=function(n){return di(u(n,9),(Ce(),rm))},v(Qn,"PartitionPreprocessor/lambda$0$Type",1602),m(1603,1,Pt,OI),s.Mb=function(n){return di(u(n,9),(Ce(),rm))},v(Qn,"PartitionPreprocessor/lambda$1$Type",1603),m(1604,1,{},NI),s.Kb=function(n){return new bn(null,new cp(new qn(Vn(Ni(u(n,9)).a.Jc(),new ne))))},v(Qn,"PartitionPreprocessor/lambda$2$Type",1604),m(1605,1,Pt,gEe),s.Mb=function(n){return egn(this.a,u(n,17))},v(Qn,"PartitionPreprocessor/lambda$3$Type",1605),m(1606,1,it,DI),s.Ad=function(n){G7n(u(n,17))},v(Qn,"PartitionPreprocessor/lambda$4$Type",1606),m(1607,1,Pt,wEe),s.Mb=function(n){return $vn(this.a,u(n,9))},s.a=0,v(Qn,"PartitionPreprocessor/lambda$5$Type",1607),m(1608,1,Mi,eP),s.If=function(n,t){FIn(u(n,37),t)};var nve,oin,sin,lin,tve,ive;v(Qn,"PortListSorter",1608),m(1609,1,{},f5),s.Kb=function(n){return F9(),u(n,12).e},v(Qn,"PortListSorter/lambda$0$Type",1609),m(1610,1,{},Oq),s.Kb=function(n){return F9(),u(n,12).g},v(Qn,"PortListSorter/lambda$1$Type",1610),m(1611,1,Ut,Nq),s.Le=function(n,t){return tPe(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qn,"PortListSorter/lambda$2$Type",1611),m(1612,1,Ut,Dq),s.Le=function(n,t){return iAn(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qn,"PortListSorter/lambda$3$Type",1612),m(1613,1,Ut,II),s.Le=function(n,t){return eKe(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qn,"PortListSorter/lambda$4$Type",1613),m(1614,1,Mi,_I),s.If=function(n,t){VCn(u(n,37),t)},v(Qn,"PortSideProcessor",1614),m(1615,1,Mi,iy),s.If=function(n,t){WDn(u(n,37),t)},v(Qn,"ReversedEdgeRestorer",1615),m(1620,1,Mi,iAe),s.If=function(n,t){zSn(this,u(n,37),t)},v(Qn,"SelfLoopPortRestorer",1620),m(1621,1,{},ry),s.Kb=function(n){return new bn(null,new wn(u(n,25).a,16))},v(Qn,"SelfLoopPortRestorer/lambda$0$Type",1621),m(1622,1,Pt,Iq),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Qn,"SelfLoopPortRestorer/lambda$1$Type",1622),m(1623,1,Pt,rk),s.Mb=function(n){return di(u(n,9),(pe(),Ww))},v(Qn,"SelfLoopPortRestorer/lambda$2$Type",1623),m(1624,1,{},LI),s.Kb=function(n){return u(T(u(n,9),(pe(),Ww)),338)},v(Qn,"SelfLoopPortRestorer/lambda$3$Type",1624),m(1625,1,it,pEe),s.Ad=function(n){VTn(this.a,u(n,338))},v(Qn,"SelfLoopPortRestorer/lambda$4$Type",1625),m(792,1,it,zM),s.Ad=function(n){uxn(u(n,107))},v(Qn,"SelfLoopPortRestorer/lambda$5$Type",792),m(1627,1,Mi,FM),s.If=function(n,t){Zjn(u(n,37),t)},v(Qn,"SelfLoopPostProcessor",1627),m(1628,1,{},HM),s.Kb=function(n){return new bn(null,new wn(u(n,25).a,16))},v(Qn,"SelfLoopPostProcessor/lambda$0$Type",1628),m(1629,1,Pt,PI),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Qn,"SelfLoopPostProcessor/lambda$1$Type",1629),m(1630,1,Pt,$I),s.Mb=function(n){return di(u(n,9),(pe(),Ww))},v(Qn,"SelfLoopPostProcessor/lambda$2$Type",1630),m(1631,1,it,RI),s.Ad=function(n){tMn(u(n,9))},v(Qn,"SelfLoopPostProcessor/lambda$3$Type",1631),m(1632,1,{},_q),s.Kb=function(n){return new bn(null,new wn(u(n,107).f,1))},v(Qn,"SelfLoopPostProcessor/lambda$4$Type",1632),m(1633,1,it,mEe),s.Ad=function(n){B6n(this.a,u(n,341))},v(Qn,"SelfLoopPostProcessor/lambda$5$Type",1633),m(1634,1,Pt,Lq),s.Mb=function(n){return!!u(n,107).i},v(Qn,"SelfLoopPostProcessor/lambda$6$Type",1634),m(1635,1,it,vEe),s.Ad=function(n){pbn(this.a,u(n,107))},v(Qn,"SelfLoopPostProcessor/lambda$7$Type",1635),m(1616,1,Mi,Pq),s.If=function(n,t){xOn(u(n,37),t)},v(Qn,"SelfLoopPreProcessor",1616),m(1617,1,{},$q),s.Kb=function(n){return new bn(null,new wn(u(n,107).f,1))},v(Qn,"SelfLoopPreProcessor/lambda$0$Type",1617),m(1618,1,{},Rq),s.Kb=function(n){return u(n,341).a},v(Qn,"SelfLoopPreProcessor/lambda$1$Type",1618),m(1619,1,it,f1),s.Ad=function(n){vwn(u(n,17))},v(Qn,"SelfLoopPreProcessor/lambda$2$Type",1619),m(1636,1,Mi,QOe),s.If=function(n,t){DTn(this,u(n,37),t)},v(Qn,"SelfLoopRouter",1636),m(1637,1,{},cy),s.Kb=function(n){return new bn(null,new wn(u(n,25).a,16))},v(Qn,"SelfLoopRouter/lambda$0$Type",1637),m(1638,1,Pt,BI),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Qn,"SelfLoopRouter/lambda$1$Type",1638),m(1639,1,Pt,zI),s.Mb=function(n){return di(u(n,9),(pe(),Ww))},v(Qn,"SelfLoopRouter/lambda$2$Type",1639),m(1640,1,{},FI),s.Kb=function(n){return u(T(u(n,9),(pe(),Ww)),338)},v(Qn,"SelfLoopRouter/lambda$3$Type",1640),m(1641,1,it,RTe),s.Ad=function(n){g5n(this.a,this.b,u(n,338))},v(Qn,"SelfLoopRouter/lambda$4$Type",1641),m(1642,1,Mi,HI),s.If=function(n,t){UNn(u(n,37),t)},v(Qn,"SemiInteractiveCrossMinProcessor",1642),m(1643,1,Pt,JM),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Qn,"SemiInteractiveCrossMinProcessor/lambda$0$Type",1643),m(1644,1,Pt,Bq),s.Mb=function(n){return dDe(u(n,9))._b((Ce(),om))},v(Qn,"SemiInteractiveCrossMinProcessor/lambda$1$Type",1644),m(1645,1,Ut,a5),s.Le=function(n,t){return G8n(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qn,"SemiInteractiveCrossMinProcessor/lambda$2$Type",1645),m(1646,1,{},GM),s.Te=function(n,t){return y5n(u(n,9),u(t,9))},v(Qn,"SemiInteractiveCrossMinProcessor/lambda$3$Type",1646),m(1648,1,Mi,uy),s.If=function(n,t){MPn(u(n,37),t)},v(Qn,"SortByInputModelProcessor",1648),m(1649,1,Pt,qM),s.Mb=function(n){return u(n,12).g.c.length!=0},v(Qn,"SortByInputModelProcessor/lambda$0$Type",1649),m(1650,1,it,yEe),s.Ad=function(n){axn(this.a,u(n,12))},v(Qn,"SortByInputModelProcessor/lambda$1$Type",1650),m(1729,804,{},MBe),s.bf=function(n){var t,i,r,c;switch(this.c=n,this.a.g){case 2:t=new xe,Wi(ci(new bn(null,new wn(this.c.a.b,16)),new VI),new JTe(this,t)),HO(this,new Km),Ao(t,new h5),t.c.length=0,Wi(ci(new bn(null,new wn(this.c.a.b,16)),new UM),new EEe(t)),HO(this,new GI),Ao(t,new Vm),t.c.length=0,i=ACe(RY(op(new bn(null,new wn(this.c.a.b,16)),new jEe(this))),new qI),Wi(new bn(null,new wn(this.c.a.a,16)),new zTe(i,t)),HO(this,new XI),Ao(t,new zq),t.c.length=0;break;case 3:r=new xe,HO(this,new JI),c=ACe(RY(op(new bn(null,new wn(this.c.a.b,16)),new kEe(this))),new UI),Wi(ci(new bn(null,new wn(this.c.a.b,16)),new Fq),new HTe(c,r)),HO(this,new Hq),Ao(r,new KI),r.c.length=0;break;default:throw P(new qSe)}},s.b=0,v(sr,"EdgeAwareScanlineConstraintCalculation",1729),m(1730,1,mh,JI),s.Lb=function(n){return q(u(n,60).g,156)},s.Fb=function(n){return this===n},s.Mb=function(n){return q(u(n,60).g,156)},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$0$Type",1730),m(1731,1,{},kEe),s.We=function(n){return Rxn(this.a,u(n,60))},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$1$Type",1731),m(1739,1,Wz,BTe),s.be=function(){Pj(this.a,this.b,-1)},s.b=0,v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$10$Type",1739),m(1741,1,mh,Km),s.Lb=function(n){return q(u(n,60).g,156)},s.Fb=function(n){return this===n},s.Mb=function(n){return q(u(n,60).g,156)},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$11$Type",1741),m(1742,1,it,h5),s.Ad=function(n){u(n,375).be()},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$12$Type",1742),m(1743,1,Pt,UM),s.Mb=function(n){return q(u(n,60).g,9)},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$13$Type",1743),m(1745,1,it,EEe),s.Ad=function(n){TEn(this.a,u(n,60))},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$14$Type",1745),m(1744,1,Wz,UTe),s.be=function(){Pj(this.b,this.a,-1)},s.a=0,v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$15$Type",1744),m(1746,1,mh,GI),s.Lb=function(n){return q(u(n,60).g,9)},s.Fb=function(n){return this===n},s.Mb=function(n){return q(u(n,60).g,9)},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$16$Type",1746),m(1747,1,it,Vm),s.Ad=function(n){u(n,375).be()},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$17$Type",1747),m(1748,1,{},jEe),s.We=function(n){return Bxn(this.a,u(n,60))},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$18$Type",1748),m(1749,1,{},qI),s.Ue=function(){return 0},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$19$Type",1749),m(1732,1,{},UI),s.Ue=function(){return 0},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$2$Type",1732),m(1751,1,it,zTe),s.Ad=function(n){ivn(this.a,this.b,u(n,320))},s.a=0,v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$20$Type",1751),m(1750,1,Wz,FTe),s.be=function(){tUe(this.a,this.b,-1)},s.b=0,v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$21$Type",1750),m(1752,1,mh,XI),s.Lb=function(n){return u(n,60),!0},s.Fb=function(n){return this===n},s.Mb=function(n){return u(n,60),!0},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$22$Type",1752),m(1753,1,it,zq),s.Ad=function(n){u(n,375).be()},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$23$Type",1753),m(1733,1,Pt,Fq),s.Mb=function(n){return q(u(n,60).g,9)},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$3$Type",1733),m(1735,1,it,HTe),s.Ad=function(n){rvn(this.a,this.b,u(n,60))},s.a=0,v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$4$Type",1735),m(1734,1,Wz,XTe),s.be=function(){Pj(this.b,this.a,-1)},s.a=0,v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$5$Type",1734),m(1736,1,mh,Hq),s.Lb=function(n){return u(n,60),!0},s.Fb=function(n){return this===n},s.Mb=function(n){return u(n,60),!0},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$6$Type",1736),m(1737,1,it,KI),s.Ad=function(n){u(n,375).be()},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$7$Type",1737),m(1738,1,Pt,VI),s.Mb=function(n){return q(u(n,60).g,156)},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$8$Type",1738),m(1740,1,it,JTe),s.Ad=function(n){d8n(this.a,this.b,u(n,60))},v(sr,"EdgeAwareScanlineConstraintCalculation/lambda$9$Type",1740),m(1547,1,Mi,fOe),s.If=function(n,t){FLn(this,u(n,37),t)};var fin;v(sr,"HorizontalGraphCompactor",1547),m(1548,1,{},SEe),s.df=function(n,t){var i,r,c;return ahe(n,t)||(i=N3(n),r=N3(t),i&&i.k==(zn(),gr)||r&&r.k==(zn(),gr))?0:(c=u(T(this.a.a,(pe(),Sv)),316),e2n(c,i?i.k:(zn(),hr),r?r.k:(zn(),hr)))},s.ef=function(n,t){var i,r,c;return ahe(n,t)?1:(i=N3(n),r=N3(t),c=u(T(this.a.a,(pe(),Sv)),316),tle(c,i?i.k:(zn(),hr),r?r.k:(zn(),hr)))},v(sr,"HorizontalGraphCompactor/1",1548),m(1549,1,{},XM),s.cf=function(n,t){return pE(),n.a.i==0},v(sr,"HorizontalGraphCompactor/lambda$0$Type",1549),m(1550,1,{},AEe),s.cf=function(n,t){return j5n(this.a,n,t)},v(sr,"HorizontalGraphCompactor/lambda$1$Type",1550),m(1696,1,{},rRe);var ain,hin;v(sr,"LGraphToCGraphTransformer",1696),m(1704,1,Pt,n0),s.Mb=function(n){return n!=null},v(sr,"LGraphToCGraphTransformer/0methodref$nonNull$Type",1704),m(1697,1,{},ck),s.Kb=function(n){return el(),su(T(u(u(n,60).g,9),(pe(),gi)))},v(sr,"LGraphToCGraphTransformer/lambda$0$Type",1697),m(1698,1,{},nd),s.Kb=function(n){return el(),mFe(u(u(n,60).g,156))},v(sr,"LGraphToCGraphTransformer/lambda$1$Type",1698),m(1707,1,Pt,d5),s.Mb=function(n){return el(),q(u(n,60).g,9)},v(sr,"LGraphToCGraphTransformer/lambda$10$Type",1707),m(1708,1,it,KM),s.Ad=function(n){b5n(u(n,60))},v(sr,"LGraphToCGraphTransformer/lambda$11$Type",1708),m(1709,1,Pt,uk),s.Mb=function(n){return el(),q(u(n,60).g,156)},v(sr,"LGraphToCGraphTransformer/lambda$12$Type",1709),m(1713,1,it,ok),s.Ad=function(n){Ukn(u(n,60))},v(sr,"LGraphToCGraphTransformer/lambda$13$Type",1713),m(1710,1,it,MEe),s.Ad=function(n){Vgn(this.a,u(n,8))},s.a=0,v(sr,"LGraphToCGraphTransformer/lambda$14$Type",1710),m(1711,1,it,TEe),s.Ad=function(n){Qgn(this.a,u(n,119))},s.a=0,v(sr,"LGraphToCGraphTransformer/lambda$15$Type",1711),m(1712,1,it,xEe),s.Ad=function(n){Ygn(this.a,u(n,8))},s.a=0,v(sr,"LGraphToCGraphTransformer/lambda$16$Type",1712),m(1714,1,{},VM),s.Kb=function(n){return el(),new bn(null,new cp(new qn(Vn(Ni(u(n,9)).a.Jc(),new ne))))},v(sr,"LGraphToCGraphTransformer/lambda$17$Type",1714),m(1715,1,Pt,Ym),s.Mb=function(n){return el(),cc(u(n,17))},v(sr,"LGraphToCGraphTransformer/lambda$18$Type",1715),m(1716,1,it,CEe),s.Ad=function(n){J9n(this.a,u(n,17))},v(sr,"LGraphToCGraphTransformer/lambda$19$Type",1716),m(1700,1,it,OEe),s.Ad=function(n){m6n(this.a,u(n,156))},v(sr,"LGraphToCGraphTransformer/lambda$2$Type",1700),m(1717,1,{},YI),s.Kb=function(n){return el(),new bn(null,new wn(u(n,25).a,16))},v(sr,"LGraphToCGraphTransformer/lambda$20$Type",1717),m(1718,1,{},sk),s.Kb=function(n){return el(),new bn(null,new cp(new qn(Vn(Ni(u(n,9)).a.Jc(),new ne))))},v(sr,"LGraphToCGraphTransformer/lambda$21$Type",1718),m(1719,1,{},b5),s.Kb=function(n){return el(),u(T(u(n,17),(pe(),ag)),16)},v(sr,"LGraphToCGraphTransformer/lambda$22$Type",1719),m(1720,1,Pt,Jq),s.Mb=function(n){return n2n(u(n,16))},v(sr,"LGraphToCGraphTransformer/lambda$23$Type",1720),m(1721,1,it,NEe),s.Ad=function(n){zxn(this.a,u(n,16))},v(sr,"LGraphToCGraphTransformer/lambda$24$Type",1721),m(1722,1,{},a1),s.Kb=function(n){return el(),new bn(null,new cp(new qn(Vn(Ni(u(n,9)).a.Jc(),new ne))))},v(sr,"LGraphToCGraphTransformer/lambda$25$Type",1722),m(1723,1,Pt,YM),s.Mb=function(n){return el(),cc(u(n,17))},v(sr,"LGraphToCGraphTransformer/lambda$26$Type",1723),m(1725,1,it,DEe),s.Ad=function(n){P8n(this.a,u(n,17))},v(sr,"LGraphToCGraphTransformer/lambda$27$Type",1725),m(1724,1,it,IEe),s.Ad=function(n){Hbn(this.a,u(n,70))},s.a=0,v(sr,"LGraphToCGraphTransformer/lambda$28$Type",1724),m(1699,1,it,GTe),s.Ad=function(n){myn(this.a,this.b,u(n,156))},v(sr,"LGraphToCGraphTransformer/lambda$3$Type",1699),m(1701,1,{},Rg),s.Kb=function(n){return el(),new bn(null,new wn(u(n,25).a,16))},v(sr,"LGraphToCGraphTransformer/lambda$4$Type",1701),m(1702,1,{},QI),s.Kb=function(n){return el(),new bn(null,new cp(new qn(Vn(Ni(u(n,9)).a.Jc(),new ne))))},v(sr,"LGraphToCGraphTransformer/lambda$5$Type",1702),m(1703,1,{},lk),s.Kb=function(n){return el(),u(T(u(n,17),(pe(),ag)),16)},v(sr,"LGraphToCGraphTransformer/lambda$6$Type",1703),m(1705,1,it,_Ee),s.Ad=function(n){Qxn(this.a,u(n,16))},v(sr,"LGraphToCGraphTransformer/lambda$8$Type",1705),m(1706,1,it,qTe),s.Ad=function(n){ywn(this.a,this.b,u(n,156))},v(sr,"LGraphToCGraphTransformer/lambda$9$Type",1706),m(1695,1,{},Qm),s.af=function(n){var t,i,r,c,o;for(this.a=n,this.d=new fX,this.c=oe(Fme,xn,124,this.a.a.a.c.length,0,1),this.b=0,i=new L(this.a.a.a);i.a=I&&(Te(o,me(p)),K=E.Math.max(K,ee[p-1]-y),f+=O,R+=ee[p-1]-R,y=ee[p-1],O=h[p]),O=E.Math.max(O,h[p]),++p;f+=O}M=E.Math.min(1/K,1/t.b/f),M>r&&(r=M,i=o)}return i},s.ng=function(){return!1},v(yh,"MSDCutIndexHeuristic",803),m(1647,1,Mi,Gq),s.If=function(n,t){q_n(u(n,37),t)},v(yh,"SingleEdgeGraphWrapper",1647),m(231,23,{3:1,35:1,23:1,231:1},SE);var pv,r7,c7,Zp,US,mv,u7=mt(Tu,"CenterEdgeLabelPlacementStrategy",231,At,g9n,Ipn),Sin;m(422,23,{3:1,35:1,23:1,422:1},rse);var cve,Hte,uve=mt(Tu,"ConstraintCalculationStrategy",422,At,B5n,_pn),Ain;m(301,23,{3:1,35:1,23:1,301:1,188:1,196:1},f$),s.bg=function(){return aUe(this)},s.og=function(){return aUe(this)};var ZN,XS,ove,sve,lve=mt(Tu,"CrossingMinimizationStrategy",301,At,U6n,Lpn),Min;m(350,23,{3:1,35:1,23:1,350:1},JX);var fve,Jte,GH,ave=mt(Tu,"CuttingStrategy",350,At,C4n,Ppn),Tin;m(267,23,{3:1,35:1,23:1,267:1,188:1,196:1},g3),s.bg=function(){return gXe(this)},s.og=function(){return gXe(this)};var Gte,hve,qte,Ute,Xte,Kte,Vte,Yte,WN,dve=mt(Tu,"CycleBreakingStrategy",267,At,C8n,$pn),xin;m(419,23,{3:1,35:1,23:1,419:1},cse);var qH,bve,gve=mt(Tu,"DirectionCongruency",419,At,z5n,Rpn),Cin;m(449,23,{3:1,35:1,23:1,449:1},qX);var o7,Qte,vv,Oin=mt(Tu,"EdgeConstraint",449,At,O4n,Bpn),Nin;m(284,23,{3:1,35:1,23:1,284:1},TE);var Zte,Wte,eie,nie,UH,tie,wve=mt(Tu,"EdgeLabelSideSelection",284,At,w9n,zpn),Din;m(476,23,{3:1,35:1,23:1,476:1},use);var XH,pve,mve=mt(Tu,"EdgeStraighteningStrategy",476,At,F5n,Fpn),Iin;m(282,23,{3:1,35:1,23:1,282:1},AE);var iie,vve,yve,KH,kve,Eve,jve=mt(Tu,"FixedAlignment",282,At,p9n,Hpn),_in;m(283,23,{3:1,35:1,23:1,283:1},ME);var Sve,Ave,Mve,Tve,KS,xve,Cve=mt(Tu,"GraphCompactionStrategy",283,At,m9n,Jpn),Lin;m(261,23,{3:1,35:1,23:1,261:1},q2);var s7,VH,l7,ql,VS,YH,f7,yv,QH,YS,rie=mt(Tu,"GraphProperties",261,At,t7n,Gpn),Pin;m(302,23,{3:1,35:1,23:1,302:1},UX);var eD,cie,uie,oie=mt(Tu,"GreedySwitchType",302,At,N4n,qpn),$in;m(329,23,{3:1,35:1,23:1,329:1},XX);var Wp,Ove,nD,sie=mt(Tu,"GroupOrderStrategy",329,At,D4n,Upn),Rin;m(315,23,{3:1,35:1,23:1,315:1},KX);var d6,tD,kv,Bin=mt(Tu,"InLayerConstraint",315,At,I4n,Xpn),zin;m(420,23,{3:1,35:1,23:1,420:1},ose);var lie,Nve,Dve=mt(Tu,"InteractiveReferencePoint",420,At,H5n,Kpn),Fin,Ive,b6,Yw,iD,ZH,_ve,Lve,WH,Pve,g6,eJ,QS,w6,J1,fie,nJ,Ou,$ve,Y0,wo,aie,hie,rD,fg,Qw,p6,Rve,Hin,m6,cD,em,ga,hf,die,Ev,Q0,Ci,gi,Bve,zve,Fve,Hve,Jve,bie,tJ,ms,Zw,gie,v6,ZS,$d,jv,Ww,Sv,Av,a7,ag,Gve,wie,pie,WS,y6,iJ,k6,Mv;m(165,23,{3:1,35:1,23:1,165:1},uC);var eA,G1,nA,hg,uD,qve=mt(Tu,"LayerConstraint",165,At,Fyn,Vpn),Jin;m(423,23,{3:1,35:1,23:1,423:1},sse);var mie,vie,Uve=mt(Tu,"LayerUnzippingStrategy",423,At,J5n,Ypn),Gin;m(843,1,Ra,kx),s.tf=function(n){Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,uwe),""),"Direction Congruency"),"Specifies how drawings of the same graph with different layout directions compare to each other: either a natural reading direction is preserved or the drawings are rotated versions of each other."),c5e),(Qb(),Ri)),gve),We((dh(),An))))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,owe),""),"Feedback Edges"),"Whether feedback edges should be highlighted by routing around the nodes."),(Pn(),!1)),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,hF),""),"Interactive Reference Point"),"Determines which point of a node is considered by interactive layout phases."),a5e),Ri),Dve),We(An)))),Gi(n,hF,xN,Krn),Gi(n,hF,pS,Xrn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,swe),""),"Merge Edges"),"Edges that have no ports are merged so they touch the connected nodes at the same points. When this option is disabled, one port is created for each edge directly connected to a node. When it is enabled, all such incoming edges share an input port, and all outgoing edges share an output port."),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,lwe),""),"Merge Hierarchy-Crossing Edges"),"If hierarchical layout is active, hierarchy-crossing edges use as few hierarchical ports as possible. They are broken by the algorithm, with hierarchical ports inserted as required. Usually, one such port is created for each edge at each hierarchy crossing point. With this option set to true, we try to create as few hierarchical ports as possible in the process. In particular, all edges that form a hyperedge can share a port."),!0),Sr),Yi),We(An)))),Qe(n,new He(Gbn(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,fwe),""),"Allow Non-Flow Ports To Switch Sides"),"Specifies whether non-flow ports may switch sides if their node's port constraints are either FIXED_SIDE or FIXED_ORDER. A non-flow port is a port on a side that is not part of the currently configured layout flow. For instance, given a left-to-right layout direction, north and south ports would be considered non-flow ports. Further note that the underlying criterium whether to switch sides or not solely relies on the minimization of edge crossings. Hence, edge length and other aesthetics criteria are not addressed."),!1),Sr),Yi),We(Hd)),z(B(Re,1),je,2,6,["org.eclipse.elk.layered.northOrSouthPort"])))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,awe),""),"Port Sorting Strategy"),"Only relevant for nodes with FIXED_SIDE port constraints. Determines the way a node's ports are distributed on the sides of a node if their order is not prescribed. The option is set on parent nodes."),E5e),Ri),I4e),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,hwe),""),"Thoroughness"),"How much effort should be spent to produce a nice layout."),me(7)),hc),Er),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,dwe),""),"Add Unnecessary Bendpoints"),"Adds bend points even if an edge does not change direction. If true, each long edge dummy will contribute a bend point to its edges and hierarchy-crossing edges will always get a bend point where they cross hierarchy boundaries. By default, bend points are only added where an edge changes direction."),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,bwe),""),"Generate Position and Layer IDs"),"If enabled position id and layer id are generated, which are usually only used internally when setting the interactiveLayout option. This option should be specified on the root node."),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,xN),"cycleBreaking"),"Cycle Breaking Strategy"),"Strategy for cycle breaking. Cycle breaking looks for cycles in the graph and determines which edges to reverse to break the cycles. Reversed edges will end up pointing to the opposite direction of regular edges (that is, reversed edges will point left if edges usually point right)."),r5e),Ri),dve),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,CN),Dee),"Node Layering Strategy"),"Strategy for node layering."),b5e),Ri),k4e),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,gwe),Dee),"Layer Constraint"),"Determines a constraint on the placement of the node regarding the layering."),h5e),Ri),qve),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,wwe),Dee),"Layer Choice Constraint"),"Allows to set a constraint regarding the layer placement of a node. Let i be the value of teh constraint. Assumed the drawing has n layers and i < n. If set to i, it expresses that the node should be placed in i-th layer. Should i>=n be true then the node is placed in the last layer of the drawing. Note that this option is not part of any of ELK Layered's default configurations but is only evaluated as part of the `InteractiveLayeredGraphVisitor`, which must be applied manually or used via the `DiagramLayoutEngine."),null),hc),Er),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,pwe),Dee),"Layer ID"),"Layer identifier that was calculated by ELK Layered for a node. This is only generated if interactiveLayot or generatePositionAndLayerIds is set."),me(-1)),hc),Er),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,eee),yQe),"Upper Bound On Width [MinWidth Layerer]"),"Defines a loose upper bound on the width of the MinWidth layerer. If set to '-1' multiple values are tested and the best result is selected."),me(4)),hc),Er),We(An)))),Gi(n,eee,CN,ncn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,nee),yQe),"Upper Layer Estimation Scaling Factor [MinWidth Layerer]"),"Multiplied with Upper Bound On Width for defining an upper bound on the width of layers which haven't been determined yet, but whose maximum width had been (roughly) estimated by the MinWidth algorithm. Compensates for too high estimations. If set to '-1' multiple values are tested and the best result is selected."),me(2)),hc),Er),We(An)))),Gi(n,nee,CN,icn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,tee),kQe),"Node Promotion Strategy"),"Reduces number of dummy nodes after layering phase (if possible)."),d5e),Ri),O4e),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,iee),kQe),"Max Node Promotion Iterations"),"Limits the number of iterations for node promotion."),me(0)),hc),Er),We(An)))),Gi(n,iee,tee,null),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,ree),"layering.coffmanGraham"),"Layer Bound"),"The maximum number of nodes allowed per layer."),me(ri)),hc),Er),We(An)))),Gi(n,ree,CN,Yrn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,pS),L8),"Crossing Minimization Strategy"),"Strategy for crossing minimization."),i5e),Ri),lve),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,mwe),L8),"Force Node Model Order"),"The node order given by the model does not change to produce a better layout. E.g. if node A is before node B in the model this is not changed during crossing minimization. This assumes that the node model order is already respected before crossing minimization. This can be achieved by setting considerModelOrder.strategy to NODES_AND_EDGES."),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,cee),L8),"Hierarchical Sweepiness"),"How likely it is to use cross-hierarchy (1) vs bottom-up (-1)."),.1),Wr),br),We(An)))),Gi(n,cee,AF,yrn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,uee),L8),"Semi-Interactive Crossing Minimization"),"Preserves the order of nodes within a layer but still minimizes crossings between edges connecting long edge dummies. Derives the desired order from positions specified by the 'org.eclipse.elk.position' layout option. Requires a crossing minimization strategy that is able to process 'in-layer' constraints."),!1),Sr),Yi),We(An)))),Gi(n,uee,pS,Mrn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,vwe),L8),"In Layer Predecessor of"),"Allows to set a constraint which specifies of which node the current node is the predecessor. If set to 's' then the node is the predecessor of 's' and is in the same layer"),null),x6),Re),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,ywe),L8),"In Layer Successor of"),"Allows to set a constraint which specifies of which node the current node is the successor. If set to 's' then the node is the successor of 's' and is in the same layer"),null),x6),Re),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,kwe),L8),"Position Choice Constraint"),"Allows to set a constraint regarding the position placement of a node in a layer. Assumed the layer in which the node placed includes n other nodes and i < n. If set to i, it expresses that the node should be placed at the i-th position. Should i>=n be true then the node is placed at the last position in the layer. Note that this option is not part of any of ELK Layered's default configurations but is only evaluated as part of the `InteractiveLayeredGraphVisitor`, which must be applied manually or used via the `DiagramLayoutEngine."),null),hc),Er),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Ewe),L8),"Position ID"),"Position within a layer that was determined by ELK Layered for a node. This is only generated if interactiveLayot or generatePositionAndLayerIds is set."),me(-1)),hc),Er),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,jwe),EQe),"Greedy Switch Activation Threshold"),"By default it is decided automatically if the greedy switch is activated or not. The decision is based on whether the size of the input graph (without dummy nodes) is smaller than the value of this option. A '0' enforces the activation."),me(40)),hc),Er),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,oee),EQe),"Greedy Switch Crossing Minimization"),"Greedy Switch strategy for crossing minimization. The greedy switch heuristic is executed after the regular crossing minimization as a post-processor. Note that if 'hierarchyHandling' is set to 'INCLUDE_CHILDREN', the 'greedySwitchHierarchical.type' option must be used."),t5e),Ri),oie),We(An)))),Gi(n,oee,pS,mrn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,dF),"crossingMinimization.greedySwitchHierarchical"),"Greedy Switch Crossing Minimization (hierarchical)"),"Activates the greedy switch heuristic in case hierarchical layout is used. The differences to the non-hierarchical case (see 'greedySwitch.type') are: 1) greedy switch is inactive by default, 3) only the option value set on the node at which hierarchical layout starts is relevant, and 2) if it's activated by the user, it properly addresses hierarchy-crossing edges."),n5e),Ri),oie),We(An)))),Gi(n,dF,pS,grn),Gi(n,dF,AF,wrn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,uv),jQe),"Node Placement Strategy"),"Strategy for node placement."),k5e),Ri),A4e),We(An)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,bF),jQe),"Favor Straight Edges Over Balancing"),"Favor straight edges over a balanced node placement. The default behavior is determined automatically based on the used 'edgeRouting'. For an orthogonal style it is set to true, for all other styles to false."),Sr),Yi),We(An)))),Gi(n,bF,uv,ycn),Gi(n,bF,uv,kcn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,see),SQe),"BK Edge Straightening"),"Specifies whether the Brandes Koepf node placer tries to increase the number of straight edges at the expense of diagram size. There is a subtle difference to the 'favorStraightEdges' option, which decides whether a balanced placement of the nodes is desired, or not. In bk terms this means combining the four alignments into a single balanced one, or not. This option on the other hand tries to straighten additional edges during the creation of each of the four alignments."),m5e),Ri),mve),We(An)))),Gi(n,see,uv,wcn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,lee),SQe),"BK Fixed Alignment"),"Tells the BK node placer to use a certain alignment (out of its four) instead of the one producing the smallest height, or the combination of all four."),v5e),Ri),jve),We(An)))),Gi(n,lee,uv,mcn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,fee),"nodePlacement.linearSegments"),"Linear Segments Deflection Dampening"),"Dampens the movement of nodes to keep the diagram from getting too large."),.3),Wr),br),We(An)))),Gi(n,fee,uv,jcn),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,aee),"nodePlacement.networkSimplex"),"Node Flexibility"),"Aims at shorter and straighter edges. Two configurations are possible: (a) allow ports to move freely on the side they are assigned to (the order is always defined beforehand), (b) additionally allow to enlarge a node wherever it helps. If this option is not configured for a node, the 'nodeFlexibility.default' value is used, which is specified for the node's parent."),Ri),Uie),We(lr)))),Gi(n,aee,uv,Tcn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,hee),"nodePlacement.networkSimplex.nodeFlexibility"),"Node Flexibility Default"),"Default value of the 'nodeFlexibility' option for the children of a hierarchical node."),y5e),Ri),Uie),We(An)))),Gi(n,hee,uv,Mcn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Swe),AQe),"Self-Loop Distribution"),"Alter the distribution of the loops around the node. It only takes effect for PortConstraints.FREE."),s5e),Ri),P4e),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Awe),AQe),"Self-Loop Ordering"),"Alter the ordering of the loops they can either be stacked or sequenced. It only takes effect for PortConstraints.FREE."),l5e),Ri),$4e),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,gF),"edgeRouting.splines"),"Spline Routing Mode"),"Specifies the way control points are assembled for each individual edge. CONSERVATIVE ensures that edges are properly routed around the nodes but feels rather orthogonal at times. SLOPPY uses fewer control points to obtain curvier edge routes but may result in edges overlapping nodes."),f5e),Ri),B4e),We(An)))),Gi(n,gF,NN,$rn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,wF),"edgeRouting.splines.sloppy"),"Sloppy Spline Layer Spacing Factor"),"Spacing factor for routing area between layers when using sloppy spline routing."),.2),Wr),br),We(An)))),Gi(n,wF,NN,Brn),Gi(n,wF,gF,zrn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,dee),"edgeRouting.polyline"),"Sloped Edge Zone Width"),"Width of the strip to the left and to the right of each layer where the polyline edge router is allowed to refrain from ensuring that edges are routed horizontally. This prevents awkward bend points for nodes that extent almost to the edge of their layer."),2),Wr),br),We(An)))),Gi(n,dee,NN,Irn),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,Mwe),za),"Spacing Base Value"),"An optional base value for all other layout options of the 'spacing' group. It can be used to conveniently alter the overall 'spaciousness' of the drawing. Whenever an explicit value is set for the other layout options, this base value will have no effect. The base value is not inherited, i.e. it must be set for each hierarchical node."),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Twe),za),"Edge Node Between Layers Spacing"),"The spacing to be preserved between nodes and edges that are routed next to the node's layer. For the spacing between nodes and edges that cross the node's layer 'spacing.edgeNode' is used."),10),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,xwe),za),"Edge Edge Between Layer Spacing"),"Spacing to be preserved between pairs of edges that are routed between the same pair of layers. Note that 'spacing.edgeEdge' is used for the spacing between pairs of edges crossing the same layer."),10),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Cwe),za),"Node Node Between Layers Spacing"),"The spacing to be preserved between any pair of nodes of two adjacent layers. Note that 'spacing.nodeNode' is used for the spacing between nodes within the layer itself."),20),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Owe),Hwe),"Direction Priority"),"Defines how important it is to have a certain edge point into the direction of the overall layout. This option is evaluated during the cycle breaking phase."),me(0)),hc),Er),We(pa)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Nwe),Hwe),"Shortness Priority"),"Defines how important it is to keep an edge as short as possible. This option is evaluated during the layering phase."),me(0)),hc),Er),We(pa)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Dwe),Hwe),"Straightness Priority"),"Defines how important it is to keep an edge straight, i.e. aligned with one of the two axes. This option is evaluated during node placement."),me(0)),hc),Er),We(pa)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,bee),Jwe),"Connected Components Compaction"),"Tries to further compact components (disconnected sub-graphs)."),!1),Sr),Yi),We(An)))),Gi(n,bee,aS,!0),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Iwe),MQe),"Post Compaction Strategy"),TQe),Kve),Ri),Cve),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,_we),MQe),"Post Compaction Constraint Calculation"),TQe),Xve),Ri),uve),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,pF),Gwe),"High Degree Node Treatment"),"Makes room around high degree nodes to place leafs and trees."),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,gee),Gwe),"High Degree Node Threshold"),"Whether a node is considered to have a high degree."),me(16)),hc),Er),We(An)))),Gi(n,gee,pF,!0),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,wee),Gwe),"High Degree Node Maximum Tree Height"),"Maximum height of a subtree connected to a high degree node to be moved to separate layers."),me(5)),hc),Er),We(An)))),Gi(n,wee,pF,!0),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,z1),qwe),"Graph Wrapping Strategy"),"For certain graphs and certain prescribed drawing areas it may be desirable to split the laid out graph into chunks that are placed side by side. The edges that connect different chunks are 'wrapped' around from the end of one chunk to the start of the other chunk. The points between the chunks are referred to as 'cuts'."),A5e),Ri),J4e),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,mF),qwe),"Additional Wrapped Edges Spacing"),"To visually separate edges that are wrapped from regularly routed edges an additional spacing value can be specified in form of this layout option. The spacing is added to the regular edgeNode spacing."),10),Wr),br),We(An)))),Gi(n,mF,z1,zcn),Gi(n,mF,z1,Fcn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,vF),qwe),"Correction Factor for Wrapping"),"At times and for certain types of graphs the executed wrapping may produce results that are consistently biased in the same fashion: either wrapping to often or to rarely. This factor can be used to correct the bias. Internally, it is simply multiplied with the 'aspect ratio' layout option."),1),Wr),br),We(An)))),Gi(n,vF,z1,Jcn),Gi(n,vF,z1,Gcn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,mS),xQe),"Cutting Strategy"),"The strategy by which the layer indexes are determined at which the layering crumbles into chunks."),S5e),Ri),ave),We(An)))),Gi(n,mS,z1,Ycn),Gi(n,mS,z1,Qcn),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,pee),xQe),"Manually Specified Cuts"),"Allows the user to specify her own cuts for a certain graph."),qa),hl),We(An)))),Gi(n,pee,mS,Ucn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,mee),"wrapping.cutting.msd"),"MSD Freedom"),"The MSD cutting strategy starts with an initial guess on the number of chunks the graph should be split into. The freedom specifies how much the strategy may deviate from this guess. E.g. if an initial number of 3 is computed, a freedom of 1 allows 2, 3, and 4 cuts."),j5e),hc),Er),We(An)))),Gi(n,mee,mS,Kcn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,yF),CQe),"Validification Strategy"),"When wrapping graphs, one can specify indices that are not allowed as split points. The validification strategy makes sure every computed split point is allowed."),M5e),Ri),H4e),We(An)))),Gi(n,yF,z1,lun),Gi(n,yF,z1,fun),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,kF),CQe),"Valid Indices for Wrapping"),null),qa),hl),We(An)))),Gi(n,kF,z1,uun),Gi(n,kF,z1,oun),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,EF),Uwe),"Improve Cuts"),"For general graphs it is important that not too many edges wrap backwards. Thus a compromise between evenly-distributed cuts and the total number of cut edges is sought."),!0),Sr),Yi),We(An)))),Gi(n,EF,z1,nun),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,jF),Uwe),"Distance Penalty When Improving Cuts"),null),2),Wr),br),We(An)))),Gi(n,jF,z1,Wcn),Gi(n,jF,EF,!0),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,vee),Uwe),"Improve Wrapped Edges"),"The initial wrapping is performed in a very simple way. As a consequence, edges that wrap from one chunk to another may be unnecessarily long. Activating this option tries to shorten such edges."),!0),Sr),Yi),We(An)))),Gi(n,vee,z1,iun),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,yee),Iee),"Layer Unzipping Strategy"),"The strategy to use for unzipping a layer into multiple sublayers while maintaining the existing ordering of nodes and edges after crossing minimization. The default value is 'NONE'."),p5e),Ri),Uve),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,kee),Iee),"Minimize Edge Length Heuristic"),"Use a heuristic to decide whether or not to actually perform the layer split with the goal of minimizing the total edge length. This option only works when layerSplit is set to 2. The property can be set to the nodes in a layer, which then applies the property for the layer. If any node sets the value to true, then the value is set to true for the entire layer."),!1),Sr),Yi),We(lr)))),Gi(n,kee,Eee,lcn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Eee),Iee),"Unzipping Layer Split"),"Defines the number of sublayers to split a layer into. The property can be set to the nodes in a layer, which then applies the property for the layer. If multiple nodes set the value to different values, then the lowest value is chosen."),g5e),hc),Er),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,jee),Iee),"Reset Alternation on Long Edges"),"If set to true, nodes will always be placed in the first sublayer after a long edge when using the ALTERNATING strategy. Otherwise long edge dummies are treated the same as regular nodes. The default value is true. The property can be set to the nodes in a layer, which then applies the property for the layer. If any node sets the value to false, then the value is set to false for the entire layer."),w5e),Sr),Yi),We(lr)))),Gi(n,jee,yee,acn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Lwe),_ee),"Edge Label Side Selection"),"Method to decide on edge label sides."),o5e),Ri),wve),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Pwe),_ee),"Edge Center Label Placement Strategy"),"Determines in which layer center labels of long edges should be placed."),u5e),Ri),u7),Ti(An,z(B(Ga,1),ye,160,0,[U1]))))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,SF),vS),"Consider Model Order"),"Preserves the order of nodes and edges in the model file if this does not lead to additional edge crossings. Depending on the strategy this is not always possible since the node and edge order might be conflicting."),e5e),Ri),D4e),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,$we),vS),"Consider Port Order"),"If disabled the port order of output ports is derived from the edge order and input ports are ordered by their incoming connections. If enabled all ports are ordered by the port model order."),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,ON),vS),"No Model Order"),"Set on a node to not set a model order for this node even though it is a real node."),!1),Sr),Yi),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,See),vS),"Consider Model Order for Components"),"If set to NONE the usual ordering strategy (by cumulative node priority and size of nodes) is used. INSIDE_PORT_SIDES orders the components with external ports only inside the groups with the same port side. FORCE_MODEL_ORDER enforces the mode order on components. This option might produce bad alignments and sub optimal drawings in terms of used area since the ordering should be respected."),Vve),Ri),a3e),We(An)))),Gi(n,See,aS,null),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Rwe),vS),"Long Edge Ordering Strategy"),"Indicates whether long edges are sorted under, over, or equal to nodes that have no connection to a previous layer in a left-to-right or right-to-left layout. Under and over changes to right and left in a vertical layout."),Wve),Ri),j4e),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Aee),vS),"Crossing Counter Node Order Influence"),"Indicates with what percentage (1 for 100%) violations of the node model order are weighted against the crossings e.g. a value of 0.5 means two model order violations are as important as on edge crossing. This allows some edge crossings in favor of preserving the model order. It is advised to set this value to a very small positive value (e.g. 0.001) to have minimal crossing and a optimal node order. Defaults to no influence (0)."),0),Wr),br),We(An)))),Gi(n,Aee,SF,null),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Mee),vS),"Crossing Counter Port Order Influence"),"Indicates with what percentage (1 for 100%) violations of the port model order are weighted against the crossings e.g. a value of 0.5 means two model order violations are as important as on edge crossing. This allows some edge crossings in favor of preserving the model order. It is advised to set this value to a very small positive value (e.g. 0.001) to have minimal crossing and a optimal port order. Defaults to no influence (0)."),0),Wr),br),We(An)))),Gi(n,Mee,SF,null),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Tee),P8),Xwe),"Used to define partial ordering groups during cycle breaking. A lower group id means that the group is sorted before other groups. A group model order of 0 is the default group."),me(0)),hc),Er),We(lr)))),Gi(n,Tee,ON,!1),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,xee),P8),Xwe),"Used to define partial ordering groups during crossing minimization. A lower group id means that the group is sorted before other groups. A group model order of 0 is the default group."),me(0)),hc),Er),Ti(lr,z(B(Ga,1),ye,160,0,[pa,Hd]))))),Gi(n,xee,ON,!1),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Cee),P8),Xwe),"Used to define partial ordering groups during component packing. A lower group id means that the group is sorted before other groups. A group model order of 0 is the default group."),me(0)),hc),Er),Ti(lr,z(B(Ga,1),ye,160,0,[pa,Hd]))))),Gi(n,Cee,ON,!1),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Bwe),P8),"Cycle Breaking Group Ordering Strategy"),"Determines how to count ordering violations during cycle breaking. NONE: They do not count. ENFORCED: A group with a higher model order is before a node with a smaller. MODEL_ORDER: The model order counts instead of the model order group id ordering."),Yve),Ri),sie),We(An)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,Oee),P8),"Cycle Breaking Preferred Source Id"),"The model order group id for which should be preferred as a source if possible."),hc),Er),We(An)))),Gi(n,Oee,xN,ern),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,Nee),P8),"Cycle Breaking Preferred Target Id"),"The model order group id for which should be preferred as a target if possible."),hc),Er),We(An)))),Gi(n,Nee,xN,trn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,zwe),P8),"Crossing Minimization Group Ordering Strategy"),"Determines how to count ordering violations during crossing minimization. NONE: They do not count. ENFORCED: A group with a lower id is before a group with a higher id. MODEL_ORDER: The model order counts instead of the model order group id ordering."),Zve),Ri),sie),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Fwe),P8),"Crossing Minimization Enforced Group Orders"),"Holds all group ids which are enforcing their order during crossing minimization strategies. E.g. if only groups 2 and -1 (default) enforce their ordering. Other groups e.g. the group of timer nodes can be ordered arbitrarily if it helps and the mentioned groups may not change their order."),Qve),qa),hl),We(An)))),VVe((new AU,n))};var qin,Uin,Xin,Xve,Kin,Kve,Vin,Vve,Yin,Qin,Zin,Yve,Win,ern,nrn,trn,irn,Qve,rrn,Zve,crn,urn,orn,srn,Wve,lrn,frn,arn,e5e,hrn,drn,brn,n5e,grn,wrn,prn,t5e,mrn,vrn,yrn,krn,Ern,jrn,Srn,Arn,Mrn,Trn,i5e,xrn,r5e,Crn,c5e,Orn,u5e,Nrn,o5e,Drn,Irn,_rn,s5e,Lrn,l5e,Prn,f5e,$rn,Rrn,Brn,zrn,Frn,Hrn,Jrn,Grn,qrn,Urn,a5e,Xrn,Krn,Vrn,Yrn,Qrn,Zrn,h5e,Wrn,ecn,ncn,tcn,icn,rcn,ccn,d5e,ucn,b5e,ocn,g5e,scn,lcn,fcn,w5e,acn,hcn,p5e,dcn,bcn,gcn,m5e,wcn,pcn,v5e,mcn,vcn,ycn,kcn,Ecn,jcn,Scn,Acn,y5e,Mcn,Tcn,xcn,k5e,Ccn,E5e,Ocn,Ncn,Dcn,Icn,_cn,Lcn,Pcn,$cn,Rcn,Bcn,zcn,Fcn,Hcn,Jcn,Gcn,qcn,Ucn,Xcn,j5e,Kcn,Vcn,S5e,Ycn,Qcn,Zcn,Wcn,eun,nun,tun,iun,run,A5e,cun,uun,oun,sun,M5e,lun,fun;v(Tu,"LayeredMetaDataProvider",843),m(982,1,Ra,AU),s.tf=function(n){VVe(n)};var Ah,yie,rJ,tA,cJ,T5e,uJ,iA,oD,kie,E6,x5e,C5e,O5e,rA,aun,cA,nm,Eie,oJ,jie,n1,Sie,h7,N5e,sD,Aie,D5e,hun,dun,bun,sJ,Mie,uA,j6,gun,dl,I5e,_5e,lJ,Tv,Mh,fJ,q1,L5e,P5e,$5e,Tie,xie,R5e,Rd,Cie,B5e,tm,z5e,F5e,H5e,aJ,im,dg,J5e,G5e,Qc,q5e,wun,vu,oA,U5e,X5e,K5e,lD,hJ,dJ,Oie,Nie,V5e,bJ,Y5e,Q5e,gJ,e2,Z5e,Die,sA,W5e,n2,lA,wJ,bg,Iie,d7,pJ,gg,e4e,n4e,t4e,rm,i4e,pun,mun,vun,yun,t2,cm,Zi,Bd,kun,um,r4e,b7,c4e,om,Eun,g7,u4e,S6,jun,Sun,fD,_ie,o4e,aD,Hf,sm,xv,wg,Z0,mJ,lm,Lie,w7,p7,pg,fm,Pie,hD,fA,aA,Aun,Mun,Tun,s4e,xun,$ie,l4e,f4e,a4e,h4e,Rie,d4e,b4e,g4e,w4e,Bie,vJ;v(Tu,"LayeredOptions",982),m(983,1,{},qq),s.uf=function(){var n;return n=new KSe,n},s.vf=function(n){},v(Tu,"LayeredOptions/LayeredFactory",983),m(1345,1,{}),s.a=0;var Cun;v(Pu,"ElkSpacings/AbstractSpacingsBuilder",1345),m(778,1345,{},X1e);var yJ,Oun;v(Tu,"LayeredSpacings/LayeredSpacingsBuilder",778),m(268,23,{3:1,35:1,23:1,268:1,188:1,196:1},w3),s.bg=function(){return aXe(this)},s.og=function(){return aXe(this)};var zie,Fie,Hie,p4e,m4e,v4e,kJ,Jie,y4e,k4e=mt(Tu,"LayeringStrategy",268,At,O8n,Qpn),Nun;m(352,23,{3:1,35:1,23:1,352:1},VX);var Gie,E4e,EJ,j4e=mt(Tu,"LongEdgeOrderingStrategy",352,At,_4n,Zpn),Dun;m(203,23,{3:1,35:1,23:1,203:1},a$);var Cv,Ov,jJ,qie,Uie=mt(Tu,"NodeFlexibility",203,At,X6n,Wpn),Iun;m(328,23,{3:1,35:1,23:1,328:1,188:1,196:1},oC),s.bg=function(){return Wqe(this)},s.og=function(){return Wqe(this)};var hA,Xie,Kie,dA,S4e,A4e=mt(Tu,"NodePlacementStrategy",328,At,zyn,emn),_un;m(243,23,{3:1,35:1,23:1,243:1},U2);var M4e,m7,bA,dD,T4e,x4e,bD,C4e,SJ,AJ,O4e=mt(Tu,"NodePromotionStrategy",243,At,n7n,nmn),Lun;m(269,23,{3:1,35:1,23:1,269:1},h$);var N4e,W0,Vie,Yie,D4e=mt(Tu,"OrderingStrategy",269,At,K6n,tmn),Pun;m(421,23,{3:1,35:1,23:1,421:1},lse);var Qie,Zie,I4e=mt(Tu,"PortSortingStrategy",421,At,G5n,imn),$un;m(452,23,{3:1,35:1,23:1,452:1},YX);var vs,No,gA,Run=mt(Tu,"PortType",452,At,L4n,rmn),Bun;m(381,23,{3:1,35:1,23:1,381:1},QX);var _4e,Wie,L4e,P4e=mt(Tu,"SelfLoopDistributionStrategy",381,At,P4n,cmn),zun;m(348,23,{3:1,35:1,23:1,348:1},ZX);var ere,gD,nre,$4e=mt(Tu,"SelfLoopOrderingStrategy",348,At,$4n,umn),Fun;m(316,1,{316:1},XKe),v(Tu,"Spacings",316),m(349,23,{3:1,35:1,23:1,349:1},WX);var tre,R4e,wA,B4e=mt(Tu,"SplineRoutingMode",349,At,R4n,omn),Hun;m(351,23,{3:1,35:1,23:1,351:1},eK);var ire,z4e,F4e,H4e=mt(Tu,"ValidifyStrategy",351,At,B4n,smn),Jun;m(382,23,{3:1,35:1,23:1,382:1},nK);var am,rre,v7,J4e=mt(Tu,"WrappingStrategy",382,At,z4n,lmn),Gun;m(1361,1,uc,yx),s.pg=function(n){return u(n,37),qun},s.If=function(n,t){CPn(this,u(n,37),t)};var qun;v(Bw,"BFSNodeOrderCycleBreaker",1361),m(1359,1,uc,nP),s.pg=function(n){return u(n,37),Uun},s.If=function(n,t){SLn(this,u(n,37),t)};var Uun;v(Bw,"DFSNodeOrderCycleBreaker",1359),m(1360,1,it,xNe),s.Ad=function(n){MIn(this.a,this.c,this.b,u(n,17))},s.b=!1,v(Bw,"DFSNodeOrderCycleBreaker/lambda$0$Type",1360),m(1353,1,uc,$y),s.pg=function(n){return u(n,37),Xun},s.If=function(n,t){jLn(this,u(n,37),t)};var Xun;v(Bw,"DepthFirstCycleBreaker",1353),m(779,1,uc,pfe),s.pg=function(n){return u(n,37),Kun},s.If=function(n,t){GRn(this,u(n,37),t)},s.qg=function(n){return u(_e(n,uz(this.e,n.c.length)),9)};var Kun;v(Bw,"GreedyCycleBreaker",779),m(1356,779,uc,gxe),s.qg=function(n){var t,i,r,c,o,l,f,h,b;for(b=null,r=ri,h=E.Math.max(this.b.a.c.length,u(T(this.b,(pe(),Q0)),15).a),t=h*u(T(this.b,iD),15).a,c=new fy,i=ue(T(this.b,(Ce(),E6)))===ue((M0(),Wp)),f=new L(n);f.ao&&(r=o,b=l));return b||u(_e(n,uz(this.e,n.c.length)),9)},v(Bw,"GreedyModelOrderCycleBreaker",1356),m(505,1,{},fy),s.a=0,s.b=0,v(Bw,"GroupModelOrderCalculator",505),m(1354,1,uc,qk),s.pg=function(n){return u(n,37),Vun},s.If=function(n,t){YLn(this,u(n,37),t)};var Vun;v(Bw,"InteractiveCycleBreaker",1354),m(1355,1,uc,Gk),s.pg=function(n){return u(n,37),Yun},s.If=function(n,t){ZLn(u(n,37),t)};var Yun;v(Bw,"ModelOrderCycleBreaker",1355),m(780,1,uc),s.pg=function(n){return u(n,37),Qun},s.If=function(n,t){z_n(this,u(n,37),t)},s.rg=function(n,t){var i,r,c,o,l,f,h,b,p,y;for(l=0;lb&&(h=S,y=b),pra(new qn(Vn(Ni(f).a.Jc(),new ne))))for(c=new qn(Vn(rr(h).a.Jc(),new ne));at(c);)r=u(tt(c),17),u(Ku(this.d,l),22).Gc(r.c.i)&&Te(this.c,r);else for(c=new qn(Vn(Ni(f).a.Jc(),new ne));at(c);)r=u(tt(c),17),u(Ku(this.d,l),22).Gc(r.d.i)&&Te(this.c,r)}},v(Bw,"SCCNodeTypeCycleBreaker",1358),m(1357,780,uc,pxe),s.rg=function(n,t){var i,r,c,o,l,f,h,b,p,y,S,M;for(l=0;lb&&(h=S,y=b),pra(new qn(Vn(Ni(f).a.Jc(),new ne))))for(c=new qn(Vn(rr(h).a.Jc(),new ne));at(c);)r=u(tt(c),17),u(Ku(this.d,l),22).Gc(r.c.i)&&Te(this.c,r);else for(c=new qn(Vn(Ni(f).a.Jc(),new ne));at(c);)r=u(tt(c),17),u(Ku(this.d,l),22).Gc(r.d.i)&&Te(this.c,r)}},v(Bw,"SCConnectivity",1357),m(1373,1,uc,vx),s.pg=function(n){return u(n,37),Zun},s.If=function(n,t){K$n(this,u(n,37),t)};var Zun;v(F1,"BreadthFirstModelOrderLayerer",1373),m(1374,1,Ut,uT),s.Le=function(n,t){return Ixn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(F1,"BreadthFirstModelOrderLayerer/lambda$0$Type",1374),m(1364,1,uc,yTe),s.pg=function(n){return u(n,37),Wun},s.If=function(n,t){VRn(this,u(n,37),t)};var Wun;v(F1,"CoffmanGrahamLayerer",1364),m(1365,1,Ut,JEe),s.Le=function(n,t){return zNn(this.a,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(F1,"CoffmanGrahamLayerer/0methodref$compareNodesInTopo$Type",1365),m(1366,1,Ut,GEe),s.Le=function(n,t){return nvn(this.a,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(F1,"CoffmanGrahamLayerer/lambda$1$Type",1366),m(1375,1,uc,mx),s.pg=function(n){return u(n,37),eon},s.If=function(n,t){LRn(this,u(n,37),t)},s.c=0,s.e=0;var eon;v(F1,"DepthFirstModelOrderLayerer",1375),m(1376,1,Ut,ay),s.Le=function(n,t){return _xn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(F1,"DepthFirstModelOrderLayerer/lambda$0$Type",1376),m(1367,1,uc,hy),s.pg=function(n){return u(n,37),Bt(Bt(Bt(new ur,(Br(),Ff),(qr(),xte)),Wh,Yp),Zu,Vp)},s.If=function(n,t){cRn(u(n,37),t)},v(F1,"InteractiveLayerer",1367),m(564,1,{564:1},tAe),s.a=0,s.c=0,v(F1,"InteractiveLayerer/LayerSpan",564),m(1363,1,uc,iP),s.pg=function(n){return u(n,37),non},s.If=function(n,t){_Nn(this,u(n,37),t)};var non;v(F1,"LongestPathLayerer",1363),m(1372,1,uc,rP),s.pg=function(n){return u(n,37),ton},s.If=function(n,t){nDn(this,u(n,37),t)};var ton;v(F1,"LongestPathSourceLayerer",1372),m(1370,1,uc,yo),s.pg=function(n){return u(n,37),Bt(Bt(Bt(new ur,(Br(),Ff),(qr(),gv)),Wh,Yp),Zu,Vp)},s.If=function(n,t){gRn(this,u(n,37),t)},s.a=0,s.b=0,s.d=0;var G4e,q4e;v(F1,"MinWidthLayerer",1370),m(1371,1,Ut,qEe),s.Le=function(n,t){return E7n(this,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(F1,"MinWidthLayerer/MinOutgoingEdgesComparator",1371),m(1362,1,uc,tP),s.pg=function(n){return u(n,37),ion},s.If=function(n,t){DPn(this,u(n,37),t)};var ion;v(F1,"NetworkSimplexLayerer",1362),m(1368,1,uc,VOe),s.pg=function(n){return u(n,37),Bt(Bt(Bt(new ur,(Br(),Ff),(qr(),gv)),Wh,Yp),Zu,Vp)},s.If=function(n,t){p$n(this,u(n,37),t)},s.d=0,s.f=0,s.g=0,s.i=0,s.s=0,s.t=0,s.u=0,v(F1,"StretchWidthLayerer",1368),m(1369,1,Ut,Xq),s.Le=function(n,t){return t9n(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(F1,"StretchWidthLayerer/1",1369),m(406,1,O2e),s.eg=function(n,t,i,r,c,o){},s.tg=function(n,t,i){return FXe(this,n,t,i)},s.dg=function(){this.g=oe(Cm,IQe,30,this.d,15,1),this.f=oe(Cm,IQe,30,this.d,15,1)},s.fg=function(n,t){this.e[n]=oe(It,Zt,30,t[n].length,15,1)},s.gg=function(n,t,i){var r;r=i[n][t],r.p=t,this.e[n][t]=t},s.hg=function(n,t,i,r){u(_e(r[n][t].j,i),12).p=this.d++},s.b=0,s.c=0,s.d=0,v($o,"AbstractBarycenterPortDistributor",406),m(1663,1,Ut,UEe),s.Le=function(n,t){return Ojn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v($o,"AbstractBarycenterPortDistributor/lambda$0$Type",1663),m(816,1,wS,Sae),s.eg=function(n,t,i,r,c,o){},s.gg=function(n,t,i){},s.hg=function(n,t,i,r){},s.cg=function(){return!1},s.dg=function(){this.c=this.e.a,this.g=this.f.g},s.fg=function(n,t){t[n][0].c.p=n},s.ig=function(){return!1},s.ug=function(n,t,i,r){i?DJe(this,n):(RJe(this,n,r),rVe(this,n,t)),n.c.length>1&&($e(Pe(T(Ir((pn(0,n.c.length),u(n.c[0],9))),(Ce(),h7))))?fUe(n,this.d,u(this,660)):(vn(),xr(n,this.d)),eze(this.e,n))},s.jg=function(n,t,i,r){var c,o,l,f,h,b,p;for(t!=gDe(i,n.length)&&(o=n[t-(i?1:-1)],Vae(this.f,o,i?(Cc(),No):(Cc(),vs))),c=n[t][0],p=!r||c.k==(zn(),gr),b=Of(n[t]),this.ug(b,p,!1,i),l=0,h=new L(b);h.a"),n0?RV(this.a,n[t-1],n[t]):!i&&t1&&($e(Pe(T(Ir((pn(0,n.c.length),u(n.c[0],9))),(Ce(),h7))))?fUe(n,this.d,this):(vn(),xr(n,this.d)),$e(Pe(T(Ir((pn(0,n.c.length),u(n.c[0],9))),h7)))||eze(this.e,n))},v($o,"ModelOrderBarycenterHeuristic",660),m(1843,1,Ut,eje),s.Le=function(n,t){return dLn(this.a,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v($o,"ModelOrderBarycenterHeuristic/lambda$0$Type",1843),m(1383,1,uc,uP),s.pg=function(n){var t;return u(n,37),t=N$(aon),Bt(t,(Br(),Zu),(qr(),IH)),t},s.If=function(n,t){C5n((u(n,37),t))};var aon;v($o,"NoCrossingMinimizer",1383),m(796,406,O2e,Coe),s.sg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,M;switch(y=this.g,i.g){case 1:{for(c=0,o=0,p=new L(n.j);p.a1&&(c.j==(Oe(),Wn)?this.b[n]=!0:c.j==Kn&&n>0&&(this.b[n-1]=!0))},s.f=0,v(Qh,"AllCrossingsCounter",1838),m(583,1,{},SB),s.b=0,s.d=0,v(Qh,"BinaryIndexedTree",583),m(519,1,{},TC);var U4e,xJ;v(Qh,"CrossingsCounter",519),m(1912,1,Ut,nje),s.Le=function(n,t){return q3n(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qh,"CrossingsCounter/lambda$0$Type",1912),m(1913,1,Ut,tje),s.Le=function(n,t){return U3n(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qh,"CrossingsCounter/lambda$1$Type",1913),m(1914,1,Ut,ije),s.Le=function(n,t){return X3n(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qh,"CrossingsCounter/lambda$2$Type",1914),m(1915,1,Ut,rje),s.Le=function(n,t){return K3n(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Qh,"CrossingsCounter/lambda$3$Type",1915),m(1916,1,it,cje),s.Ad=function(n){B9n(this.a,u(n,12))},v(Qh,"CrossingsCounter/lambda$4$Type",1916),m(1917,1,Pt,uje),s.Mb=function(n){return Dgn(this.a,u(n,12))},v(Qh,"CrossingsCounter/lambda$5$Type",1917),m(1918,1,it,oje),s.Ad=function(n){qxe(this,n)},v(Qh,"CrossingsCounter/lambda$6$Type",1918),m(1919,1,it,YTe),s.Ad=function(n){var t;h9(),k0(this.b,(t=this.a,u(n,12),t))},v(Qh,"CrossingsCounter/lambda$7$Type",1919),m(823,1,mh,fT),s.Lb=function(n){return h9(),di(u(n,12),(pe(),ms))},s.Fb=function(n){return this===n},s.Mb=function(n){return h9(),di(u(n,12),(pe(),ms))},v(Qh,"CrossingsCounter/lambda$8$Type",823),m(1911,1,{},sje),v(Qh,"HyperedgeCrossingsCounter",1911),m(467,1,{35:1,467:1},YOe),s.Dd=function(n){return vjn(this,u(n,467))},s.b=0,s.c=0,s.e=0,s.f=0;var OBn=v(Qh,"HyperedgeCrossingsCounter/Hyperedge",467);m(370,1,{35:1,370:1},SR),s.Dd=function(n){return hOn(this,u(n,370))},s.b=0,s.c=0;var hon=v(Qh,"HyperedgeCrossingsCounter/HyperedgeCorner",370);m(518,23,{3:1,35:1,23:1,518:1},fse);var mA,vA,don=mt(Qh,"HyperedgeCrossingsCounter/HyperedgeCorner/Type",518,At,q5n,dmn),bon;m(1385,1,uc,SU),s.pg=function(n){return u(T(u(n,37),(pe(),wo)),22).Gc((Oc(),ql))?gon:null},s.If=function(n,t){HAn(this,u(n,37),t)};var gon;v(Ic,"InteractiveNodePlacer",1385),m(1386,1,uc,Sx),s.pg=function(n){return u(T(u(n,37),(pe(),wo)),22).Gc((Oc(),ql))?won:null},s.If=function(n,t){xSn(this,u(n,37),t)};var won,CJ,OJ;v(Ic,"LinearSegmentsNodePlacer",1386),m(263,1,{35:1,263:1},soe),s.Dd=function(n){return Ubn(this,u(n,263))},s.Fb=function(n){var t;return q(n,263)?(t=u(n,263),this.b==t.b):!1},s.Hb=function(){return this.b},s.Ib=function(){return"ls"+_a(this.e)},s.a=0,s.b=0,s.c=-1,s.d=-1,s.g=0;var pon=v(Ic,"LinearSegmentsNodePlacer/LinearSegment",263);m(1388,1,uc,ADe),s.pg=function(n){return u(T(u(n,37),(pe(),wo)),22).Gc((Oc(),ql))?mon:null},s.If=function(n,t){PRn(this,u(n,37),t)},s.b=0,s.g=0;var mon;v(Ic,"NetworkSimplexPlacer",1388),m(1407,1,Ut,Wm),s.Le=function(n,t){return uo(u(n,15).a,u(t,15).a)},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Ic,"NetworkSimplexPlacer/0methodref$compare$Type",1407),m(1409,1,Ut,sT),s.Le=function(n,t){return uo(u(n,15).a,u(t,15).a)},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Ic,"NetworkSimplexPlacer/1methodref$compare$Type",1409),m(644,1,{644:1},QTe);var NBn=v(Ic,"NetworkSimplexPlacer/EdgeRep",644);m(405,1,{405:1},Yfe),s.b=!1;var DBn=v(Ic,"NetworkSimplexPlacer/NodeRep",405);m(500,13,{3:1,4:1,20:1,31:1,56:1,13:1,18:1,16:1,59:1,500:1},sAe),v(Ic,"NetworkSimplexPlacer/Path",500),m(1389,1,{},ak),s.Kb=function(n){return u(n,17).d.i.k},v(Ic,"NetworkSimplexPlacer/Path/lambda$0$Type",1389),m(1390,1,Pt,c_),s.Mb=function(n){return u(n,249)==(zn(),hr)},v(Ic,"NetworkSimplexPlacer/Path/lambda$1$Type",1390),m(1391,1,{},u_),s.Kb=function(n){return u(n,17).d.i},v(Ic,"NetworkSimplexPlacer/Path/lambda$2$Type",1391),m(1392,1,Pt,lje),s.Mb=function(n){return POe(VFe(u(n,9)))},v(Ic,"NetworkSimplexPlacer/Path/lambda$3$Type",1392),m(1393,1,Pt,E5),s.Mb=function(n){return D3n(u(n,12))},v(Ic,"NetworkSimplexPlacer/lambda$0$Type",1393),m(1394,1,it,ZTe),s.Ad=function(n){Swn(this.a,this.b,u(n,12))},v(Ic,"NetworkSimplexPlacer/lambda$1$Type",1394),m(1403,1,it,fje),s.Ad=function(n){Wxn(this.a,u(n,17))},v(Ic,"NetworkSimplexPlacer/lambda$10$Type",1403),m(1404,1,{},lT),s.Kb=function(n){return nl(),new bn(null,new wn(u(n,25).a,16))},v(Ic,"NetworkSimplexPlacer/lambda$11$Type",1404),m(1405,1,it,aje),s.Ad=function(n){IDn(this.a,u(n,9))},v(Ic,"NetworkSimplexPlacer/lambda$12$Type",1405),m(1406,1,{},hk),s.Kb=function(n){return nl(),me(u(n,124).e)},v(Ic,"NetworkSimplexPlacer/lambda$13$Type",1406),m(1408,1,{},dk),s.Kb=function(n){return nl(),me(u(n,124).e)},v(Ic,"NetworkSimplexPlacer/lambda$15$Type",1408),m(1410,1,Pt,o_),s.Mb=function(n){return nl(),u(n,405).c.k==(zn(),Qi)},v(Ic,"NetworkSimplexPlacer/lambda$17$Type",1410),m(1411,1,Pt,s_),s.Mb=function(n){return nl(),u(n,405).c.j.c.length>1},v(Ic,"NetworkSimplexPlacer/lambda$18$Type",1411),m(1412,1,it,DIe),s.Ad=function(n){JEn(this.c,this.b,this.d,this.a,u(n,405))},s.c=0,s.d=0,v(Ic,"NetworkSimplexPlacer/lambda$19$Type",1412),m(1395,1,{},e3),s.Kb=function(n){return nl(),new bn(null,new wn(u(n,25).a,16))},v(Ic,"NetworkSimplexPlacer/lambda$2$Type",1395),m(1413,1,it,hje),s.Ad=function(n){xwn(this.a,u(n,12))},s.a=0,v(Ic,"NetworkSimplexPlacer/lambda$20$Type",1413),m(1414,1,{},n3),s.Kb=function(n){return nl(),new bn(null,new wn(u(n,25).a,16))},v(Ic,"NetworkSimplexPlacer/lambda$21$Type",1414),m(1415,1,it,dje),s.Ad=function(n){_wn(this.a,u(n,9))},v(Ic,"NetworkSimplexPlacer/lambda$22$Type",1415),m(1416,1,Pt,l_),s.Mb=function(n){return POe(n)},v(Ic,"NetworkSimplexPlacer/lambda$23$Type",1416),m(1417,1,{},j5),s.Kb=function(n){return nl(),new bn(null,new wn(u(n,25).a,16))},v(Ic,"NetworkSimplexPlacer/lambda$24$Type",1417),m(1418,1,Pt,bje),s.Mb=function(n){return Fgn(this.a,u(n,9))},v(Ic,"NetworkSimplexPlacer/lambda$25$Type",1418),m(1419,1,it,WTe),s.Ad=function(n){txn(this.a,this.b,u(n,9))},v(Ic,"NetworkSimplexPlacer/lambda$26$Type",1419),m(1420,1,Pt,dy),s.Mb=function(n){return nl(),!cc(u(n,17))},v(Ic,"NetworkSimplexPlacer/lambda$27$Type",1420),m(1421,1,Pt,bk),s.Mb=function(n){return nl(),!cc(u(n,17))},v(Ic,"NetworkSimplexPlacer/lambda$28$Type",1421),m(1422,1,{},gje),s.Te=function(n,t){return Twn(this.a,u(n,25),u(t,25))},v(Ic,"NetworkSimplexPlacer/lambda$29$Type",1422),m(1396,1,{},by),s.Kb=function(n){return nl(),new bn(null,new cp(new qn(Vn(Ni(u(n,9)).a.Jc(),new ne))))},v(Ic,"NetworkSimplexPlacer/lambda$3$Type",1396),m(1397,1,Pt,gk),s.Mb=function(n){return nl(),C6n(u(n,17))},v(Ic,"NetworkSimplexPlacer/lambda$4$Type",1397),m(1398,1,it,wje),s.Ad=function(n){H_n(this.a,u(n,17))},v(Ic,"NetworkSimplexPlacer/lambda$5$Type",1398),m(1399,1,{},f_),s.Kb=function(n){return nl(),new bn(null,new wn(u(n,25).a,16))},v(Ic,"NetworkSimplexPlacer/lambda$6$Type",1399),m(1400,1,Pt,t3),s.Mb=function(n){return nl(),u(n,9).k==(zn(),Qi)},v(Ic,"NetworkSimplexPlacer/lambda$7$Type",1400),m(1401,1,{},a_),s.Kb=function(n){return nl(),new bn(null,new cp(new qn(Vn(fh(u(n,9)).a.Jc(),new ne))))},v(Ic,"NetworkSimplexPlacer/lambda$8$Type",1401),m(1402,1,Pt,S2),s.Mb=function(n){return nl(),O3n(u(n,17))},v(Ic,"NetworkSimplexPlacer/lambda$9$Type",1402),m(1384,1,uc,Ax),s.pg=function(n){return u(T(u(n,37),(pe(),wo)),22).Gc((Oc(),ql))?von:null},s.If=function(n,t){yLn(u(n,37),t)};var von;v(Ic,"SimpleNodePlacer",1384),m(185,1,{185:1},Z3),s.Ib=function(){var n;return n="",this.c==(oh(),i2)?n+=W4:this.c==zd&&(n+=Z4),this.o==(Aa(),mg)?n+=HW:this.o==Ja?n+="UP":n+="BALANCED",n},v(J0,"BKAlignedLayout",185),m(509,23,{3:1,35:1,23:1,509:1},ase);var zd,i2,yon=mt(J0,"BKAlignedLayout/HDirection",509,At,X5n,bmn),kon;m(508,23,{3:1,35:1,23:1,508:1},hse);var mg,Ja,Eon=mt(J0,"BKAlignedLayout/VDirection",508,At,U5n,gmn),jon;m(1664,1,{},exe),v(J0,"BKAligner",1664),m(1667,1,{},kJe),v(J0,"BKCompactor",1667),m(652,1,{652:1},aT),s.a=0,v(J0,"BKCompactor/ClassEdge",652),m(456,1,{456:1},rAe),s.a=null,s.b=0,v(J0,"BKCompactor/ClassNode",456),m(1387,1,uc,bxe),s.pg=function(n){return u(T(u(n,37),(pe(),wo)),22).Gc((Oc(),ql))?Son:null},s.If=function(n,t){WRn(this,u(n,37),t)},s.d=!1;var Son;v(J0,"BKNodePlacer",1387),m(1665,1,{},hT),s.d=0,v(J0,"NeighborhoodInformation",1665),m(1666,1,Ut,pje),s.Le=function(n,t){return e8n(this,u(n,49),u(t,49))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(J0,"NeighborhoodInformation/NeighborComparator",1666),m(809,1,{}),v(J0,"ThresholdStrategy",809),m(1795,809,{},lAe),s.vg=function(n,t,i){return this.a.o==(Aa(),Ja)?Ki:Nr},s.wg=function(){},v(J0,"ThresholdStrategy/NullThresholdStrategy",1795),m(576,1,{576:1},ixe),s.c=!1,s.d=!1,v(J0,"ThresholdStrategy/Postprocessable",576),m(1796,809,{},fAe),s.vg=function(n,t,i){var r,c,o;return c=t==i,r=this.a.a[i.p]==t,c||r?(o=n,this.a.c==(oh(),i2)?(c&&(o=GZ(this,t,!0)),!isNaN(o)&&!isFinite(o)&&r&&(o=GZ(this,i,!1))):(c&&(o=GZ(this,t,!0)),!isNaN(o)&&!isFinite(o)&&r&&(o=GZ(this,i,!1))),o):n},s.wg=function(){for(var n,t,i,r,c;this.d.b!=0;)c=u(p_e(this.d),576),r=iKe(this,c),r.a&&(n=r.a,i=$e(this.a.f[this.a.g[c.b.p].p]),!(!i&&!cc(n)&&n.c.i.c==n.d.i.c)&&(t=cUe(this,c),t||fCe(this.e,c)));for(;this.e.a.c.length!=0;)cUe(this,u(y1e(this.e),576))},v(J0,"ThresholdStrategy/SimpleThresholdStrategy",1796),m(635,1,{635:1,188:1,196:1},A2),s.bg=function(){return WBe(this)},s.og=function(){return WBe(this)};var cre;v(zee,"EdgeRouterFactory",635),m(1445,1,uc,CU),s.pg=function(n){return fDn(u(n,37))},s.If=function(n,t){CLn(u(n,37),t)};var Aon,Mon,Ton,xon,Con,X4e,Oon,Non;v(zee,"OrthogonalEdgeRouter",1445),m(1438,1,uc,dxe),s.pg=function(n){return QAn(u(n,37))},s.If=function(n,t){Z$n(this,u(n,37),t)};var Don,Ion,_on,Lon,pD,Pon;v(zee,"PolylineEdgeRouter",1438),m(1439,1,mh,M2),s.Lb=function(n){return Qhe(u(n,9))},s.Fb=function(n){return this===n},s.Mb=function(n){return Qhe(u(n,9))},v(zee,"PolylineEdgeRouter/1",1439),m(1851,1,Pt,gy),s.Mb=function(n){return u(n,133).c==(ca(),eb)},v(ha,"HyperEdgeCycleDetector/lambda$0$Type",1851),m(1852,1,{},dT),s.Xe=function(n){return u(n,133).d},v(ha,"HyperEdgeCycleDetector/lambda$1$Type",1852),m(1853,1,Pt,bT),s.Mb=function(n){return u(n,133).c==(ca(),eb)},v(ha,"HyperEdgeCycleDetector/lambda$2$Type",1853),m(1854,1,{},wy),s.Xe=function(n){return u(n,133).d},v(ha,"HyperEdgeCycleDetector/lambda$3$Type",1854),m(1855,1,{},py),s.Xe=function(n){return u(n,133).d},v(ha,"HyperEdgeCycleDetector/lambda$4$Type",1855),m(1856,1,{},h_),s.Xe=function(n){return u(n,133).d},v(ha,"HyperEdgeCycleDetector/lambda$5$Type",1856),m(116,1,{35:1,116:1},wO),s.Dd=function(n){return Xbn(this,u(n,116))},s.Fb=function(n){var t;return q(n,116)?(t=u(n,116),this.g==t.g):!1},s.Hb=function(){return this.g},s.Ib=function(){var n,t,i,r;for(n=new Ws("{"),r=new L(this.n);r.a"+this.b+" ("+f2n(this.c)+")"},s.d=0,v(ha,"HyperEdgeSegmentDependency",133),m(515,23,{3:1,35:1,23:1,515:1},dse);var eb,hm,$on=mt(ha,"HyperEdgeSegmentDependency/DependencyType",515,At,K5n,wmn),Ron;m(1857,1,{},mje),v(ha,"HyperEdgeSegmentSplitter",1857),m(1858,1,{},rMe),s.a=0,s.b=0,v(ha,"HyperEdgeSegmentSplitter/AreaRating",1858),m(340,1,{340:1},UK),s.a=0,s.b=0,s.c=0,v(ha,"HyperEdgeSegmentSplitter/FreeArea",340),m(1859,1,Ut,d_),s.Le=function(n,t){return tpn(u(n,116),u(t,116))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(ha,"HyperEdgeSegmentSplitter/lambda$0$Type",1859),m(1860,1,it,IIe),s.Ad=function(n){vyn(this.a,this.d,this.c,this.b,u(n,116))},s.b=0,v(ha,"HyperEdgeSegmentSplitter/lambda$1$Type",1860),m(1861,1,{},S5),s.Kb=function(n){return new bn(null,new wn(u(n,116).e,16))},v(ha,"HyperEdgeSegmentSplitter/lambda$2$Type",1861),m(1862,1,{},wk),s.Kb=function(n){return new bn(null,new wn(u(n,116).j,16))},v(ha,"HyperEdgeSegmentSplitter/lambda$3$Type",1862),m(1863,1,{},b_),s.We=function(n){return W(ie(n))},v(ha,"HyperEdgeSegmentSplitter/lambda$4$Type",1863),m(653,1,{},pV),s.a=0,s.b=0,s.c=0,v(ha,"OrthogonalRoutingGenerator",653),m(1668,1,{},gT),s.Kb=function(n){return new bn(null,new wn(u(n,116).e,16))},v(ha,"OrthogonalRoutingGenerator/lambda$0$Type",1668),m(1669,1,{},wT),s.Kb=function(n){return new bn(null,new wn(u(n,116).j,16))},v(ha,"OrthogonalRoutingGenerator/lambda$1$Type",1669),m(661,1,{}),v(Fee,"BaseRoutingDirectionStrategy",661),m(1849,661,{},aAe),s.xg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,M,O,I;if(!(n.r&&!n.q))for(p=t+n.o*i,b=new L(n.n);b.avh&&(o=p,c=n,r=new ke(y,o),qt(l.a,r),Dw(this,l,c,r,!1),S=n.r,S&&(M=W(ie(Ku(S.e,0))),r=new ke(M,o),qt(l.a,r),Dw(this,l,c,r,!1),o=t+S.o*i,c=S,r=new ke(M,o),qt(l.a,r),Dw(this,l,c,r,!1)),r=new ke(I,o),qt(l.a,r),Dw(this,l,c,r,!1)))},s.yg=function(n){return n.i.n.a+n.n.a+n.a.a},s.zg=function(){return Oe(),dt},s.Ag=function(){return Oe(),Xn},v(Fee,"NorthToSouthRoutingStrategy",1849),m(1850,661,{},hAe),s.xg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,M,O,I;if(!(n.r&&!n.q))for(p=t-n.o*i,b=new L(n.n);b.avh&&(o=p,c=n,r=new ke(y,o),qt(l.a,r),Dw(this,l,c,r,!1),S=n.r,S&&(M=W(ie(Ku(S.e,0))),r=new ke(M,o),qt(l.a,r),Dw(this,l,c,r,!1),o=t-S.o*i,c=S,r=new ke(M,o),qt(l.a,r),Dw(this,l,c,r,!1)),r=new ke(I,o),qt(l.a,r),Dw(this,l,c,r,!1)))},s.yg=function(n){return n.i.n.a+n.n.a+n.a.a},s.zg=function(){return Oe(),Xn},s.Ag=function(){return Oe(),dt},v(Fee,"SouthToNorthRoutingStrategy",1850),m(1848,661,{},dAe),s.xg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,M,O,I;if(!(n.r&&!n.q))for(p=t+n.o*i,b=new L(n.n);b.avh&&(o=p,c=n,r=new ke(o,y),qt(l.a,r),Dw(this,l,c,r,!0),S=n.r,S&&(M=W(ie(Ku(S.e,0))),r=new ke(o,M),qt(l.a,r),Dw(this,l,c,r,!0),o=t+S.o*i,c=S,r=new ke(o,M),qt(l.a,r),Dw(this,l,c,r,!0)),r=new ke(o,I),qt(l.a,r),Dw(this,l,c,r,!0)))},s.yg=function(n){return n.i.n.b+n.n.b+n.a.b},s.zg=function(){return Oe(),Wn},s.Ag=function(){return Oe(),Kn},v(Fee,"WestToEastRoutingStrategy",1848),m(812,1,{},nge),s.Ib=function(){return _a(this.a)},s.b=0,s.c=!1,s.d=!1,s.f=0,v(Hp,"NubSpline",812),m(410,1,{410:1},zUe,g_e),v(Hp,"NubSpline/PolarCP",410),m(1440,1,uc,aJe),s.pg=function(n){return RMn(u(n,37))},s.If=function(n,t){mRn(this,u(n,37),t)};var Bon,zon,Fon,Hon,Jon;v(Hp,"SplineEdgeRouter",1440),m(273,1,{273:1},VR),s.Ib=function(){return this.a+" ->("+this.c+") "+this.b},s.c=0,v(Hp,"SplineEdgeRouter/Dependency",273),m(454,23,{3:1,35:1,23:1,454:1},bse);var nb,Nv,Gon=mt(Hp,"SplineEdgeRouter/SideToProcess",454,At,V5n,pmn),qon;m(1441,1,Pt,h1),s.Mb=function(n){return Xj(),!u(n,132).o},v(Hp,"SplineEdgeRouter/lambda$0$Type",1441),m(1442,1,{},ud),s.Xe=function(n){return Xj(),u(n,132).v+1},v(Hp,"SplineEdgeRouter/lambda$1$Type",1442),m(1443,1,it,nxe),s.Ad=function(n){L3n(this.a,this.b,u(n,49))},v(Hp,"SplineEdgeRouter/lambda$2$Type",1443),m(1444,1,it,txe),s.Ad=function(n){P3n(this.a,this.b,u(n,49))},v(Hp,"SplineEdgeRouter/lambda$3$Type",1444),m(132,1,{35:1,132:1},KGe,oge),s.Dd=function(n){return Kbn(this,u(n,132))},s.b=0,s.e=!1,s.f=0,s.g=0,s.j=!1,s.k=!1,s.n=0,s.o=!1,s.p=!1,s.q=!1,s.s=0,s.u=0,s.v=0,s.F=0,v(Hp,"SplineSegment",132),m(457,1,{457:1},T2),s.a=0,s.b=!1,s.c=!1,s.d=!1,s.e=!1,s.f=0,v(Hp,"SplineSegment/EdgeInformation",457),m(1167,1,{},pT),v(H1,Xge,1167),m(1168,1,Ut,g_),s.Le=function(n,t){return hCn(u(n,120),u(t,120))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(H1,GYe,1168),m(1166,1,{},AMe),v(H1,"MrTree",1166),m(398,23,{3:1,35:1,23:1,398:1,188:1,196:1},b$),s.bg=function(){return pqe(this)},s.og=function(){return pqe(this)};var NJ,yA,kA,EA,K4e=mt(H1,"TreeLayoutPhases",398,At,Q6n,mmn),Uon;m(1082,214,$w,ZOe),s.kf=function(n,t){var i,r,c,o,l,f,h,b;for($e(Pe(ve(n,(Au(),v6e))))||FC((i=new tE((Mb(),new s0(n))),i)),l=t.dh(Gee),l.Tg("build tGraph",1),f=(h=new VC,Lu(h,n),fe(h,(xi(),SA),n),b=new gt,KIn(n,h,b),a_n(n,h,b),h),l.Ug(),l=t.dh(Gee),l.Tg("Split graph",1),o=e_n(this.a,f),l.Ug(),c=new L(o);c.a"+Rb(this.c):"e_"+Oi(this)},v(yS,"TEdge",65),m(120,150,{3:1,120:1,105:1,150:1},VC),s.Ib=function(){var n,t,i,r,c;for(c=null,r=Et(this.b,0);r.b!=r.d.c;)i=u(yt(r),40),c+=(i.c==null||i.c.length==0?"n_"+i.g:"n_"+i.c)+` -`;for(t=Et(this.a,0);t.b!=t.d.c;)n=u(yt(t),65),c+=(n.b&&n.c?Rb(n.b)+"->"+Rb(n.c):"e_"+Oi(n))+` -`;return c};var IBn=v(yS,"TGraph",120);m(633,494,{3:1,494:1,633:1,105:1,150:1}),v(yS,"TShape",633),m(40,633,{3:1,494:1,40:1,633:1,105:1,150:1},YY),s.Ib=function(){return Rb(this)};var DJ=v(yS,"TNode",40);m(236,1,Xh,v1),s.Ic=function(n){rc(this,n)},s.Jc=function(){var n;return n=Et(this.a.d,0),new h3(n)},v(yS,"TNode/2",236),m(334,1,zr,h3),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(yt(this.a),65).c},s.Ob=function(){return Kx(this.a)},s.Qb=function(){SY(this.a)},v(yS,"TNode/2/1",334),m(1893,1,Mi,mo),s.If=function(n,t){KRn(this,u(n,120),t)},v(bo,"CompactionProcessor",1893),m(1894,1,Ut,jje),s.Le=function(n,t){return y7n(this.a,u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(bo,"CompactionProcessor/lambda$0$Type",1894),m(1895,1,Pt,cxe),s.Mb=function(n){return P5n(this.b,this.a,u(n,49))},s.a=0,s.b=0,v(bo,"CompactionProcessor/lambda$1$Type",1895),m(1904,1,Ut,Sl),s.Le=function(n,t){return Cvn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(bo,"CompactionProcessor/lambda$10$Type",1904),m(1905,1,Ut,mk),s.Le=function(n,t){return Zwn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(bo,"CompactionProcessor/lambda$11$Type",1905),m(1906,1,Ut,A5),s.Le=function(n,t){return Ovn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(bo,"CompactionProcessor/lambda$12$Type",1906),m(1896,1,Pt,Sje),s.Mb=function(n){return Rwn(this.a,u(n,49))},s.a=0,v(bo,"CompactionProcessor/lambda$2$Type",1896),m(1897,1,Pt,Aje),s.Mb=function(n){return Bwn(this.a,u(n,49))},s.a=0,v(bo,"CompactionProcessor/lambda$3$Type",1897),m(1898,1,Pt,i3),s.Mb=function(n){return u(n,40).c.indexOf(OF)==-1},v(bo,"CompactionProcessor/lambda$4$Type",1898),m(1899,1,{},Mje),s.Kb=function(n){return T6n(this.a,u(n,40))},s.a=0,v(bo,"CompactionProcessor/lambda$5$Type",1899),m(z0,1,{},Tje),s.Kb=function(n){return F9n(this.a,u(n,40))},s.a=0,v(bo,"CompactionProcessor/lambda$6$Type",z0),m(1901,1,Ut,xje),s.Le=function(n,t){return Vyn(this.a,u(n,240),u(t,240))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(bo,"CompactionProcessor/lambda$7$Type",1901),m(1902,1,Ut,Cje),s.Le=function(n,t){return Yyn(this.a,u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(bo,"CompactionProcessor/lambda$8$Type",1902),m(1903,1,Ut,vk),s.Le=function(n,t){return Wwn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(bo,"CompactionProcessor/lambda$9$Type",1903),m(1891,1,Mi,my),s.If=function(n,t){GDn(u(n,120),t)},v(bo,"DirectionProcessor",1891),m(1883,1,Mi,WOe),s.If=function(n,t){f_n(this,u(n,120),t)},v(bo,"FanProcessor",1883),m(1251,1,Mi,M5),s.If=function(n,t){uXe(u(n,120),t)},v(bo,"GraphBoundsProcessor",1251),m(1252,1,{},Kq),s.We=function(n){return u(n,40).e.a},v(bo,"GraphBoundsProcessor/lambda$0$Type",1252),m(1253,1,{},$s),s.We=function(n){return u(n,40).e.b},v(bo,"GraphBoundsProcessor/lambda$1$Type",1253),m(1254,1,{},mT),s.We=function(n){return ygn(u(n,40))},v(bo,"GraphBoundsProcessor/lambda$2$Type",1254),m(1255,1,{},vT),s.We=function(n){return kgn(u(n,40))},v(bo,"GraphBoundsProcessor/lambda$3$Type",1255),m(264,23,{3:1,35:1,23:1,264:1,196:1},nw),s.bg=function(){switch(this.g){case 0:return new TAe;case 1:return new WOe;case 2:return new MAe;case 3:return new kT;case 4:return new p_;case 8:return new w_;case 5:return new my;case 6:return new th;case 7:return new mo;case 9:return new M5;case 10:return new Qs;default:throw P(new Gn(YW+(this.f!=null?this.f:""+this.g)))}};var V4e,Y4e,Q4e,Z4e,W4e,e6e,n6e,t6e,i6e,r6e,ure,_Bn=mt(bo,QW,264,At,ZBe,vmn),Xon;m(1890,1,Mi,w_),s.If=function(n,t){U$n(u(n,120),t)},v(bo,"LevelCoordinatesProcessor",1890),m(1888,1,Mi,p_),s.If=function(n,t){bNn(this,u(n,120),t)},s.a=0,v(bo,"LevelHeightProcessor",1888),m(1889,1,Xh,Vq),s.Ic=function(n){rc(this,n)},s.Jc=function(){return vn(),i9(),W8},v(bo,"LevelHeightProcessor/1",1889),m(1884,1,Mi,MAe),s.If=function(n,t){TDn(this,u(n,120),t)},v(bo,"LevelProcessor",1884),m(1885,1,Pt,yT),s.Mb=function(n){return $e(Pe(T(u(n,40),(xi(),tb))))},v(bo,"LevelProcessor/lambda$0$Type",1885),m(1886,1,Mi,kT),s.If=function(n,t){Exn(this,u(n,120),t)},s.a=0,v(bo,"NeighborsProcessor",1886),m(1887,1,Xh,ET),s.Ic=function(n){rc(this,n)},s.Jc=function(){return vn(),i9(),W8},v(bo,"NeighborsProcessor/1",1887),m(1892,1,Mi,th),s.If=function(n,t){s_n(this,u(n,120),t)},s.a=0,v(bo,"NodePositionProcessor",1892),m(1882,1,Mi,TAe),s.If=function(n,t){XLn(this,u(n,120),t)},v(bo,"RootProcessor",1882),m(1907,1,Mi,Qs),s.If=function(n,t){fSn(u(n,120),t)},v(bo,"Untreeifyer",1907),m(385,23,{3:1,35:1,23:1,385:1},iK);var mD,ore,c6e,u6e=mt(IN,"EdgeRoutingMode",385,At,q4n,ymn),Kon,vD,y7,sre,o6e,s6e,lre,fre,l6e,are,f6e,hre,jA,dre,IJ,_J,Jf,wa,k7,SA,AA,Fd,a6e,Von,bre,tb,yD,kD;m(846,1,Ra,jx),s.tf=function(n){Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,I2e),""),zQe),"Turns on Tree compaction which decreases the size of the whole tree by placing nodes of multiple levels in one large level"),(Pn(),!1)),(Qb(),Sr)),Yi),We((dh(),An))))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,_2e),""),"Edge End Texture Length"),"Should be set to the length of the texture at the end of an edge. This value can be used to improve the Edge Routing."),7),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,L2e),""),"Tree Level"),"The index for the tree level the node is in"),me(0)),hc),Er),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,P2e),""),zQe),"When set to a positive number this option will force the algorithm to place the node to the specified position within the trees layer if weighting is set to constraint"),me(-1)),hc),Er),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,$2e),""),"Weighting of Nodes"),"Which weighting to use when computing a node order."),b6e),Ri),M6e),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,R2e),""),"Edge Routing Mode"),"Chooses an Edge Routing algorithm."),h6e),Ri),u6e),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,B2e),""),"Search Order"),"Which search order to use when computing a spanning tree."),d6e),Ri),x6e),We(An)))),OVe((new sP,n))};var Yon,Qon,Zon,h6e,Won,esn,d6e,nsn,tsn,b6e;v(IN,"MrTreeMetaDataProvider",846),m(990,1,Ra,sP),s.tf=function(n){OVe(n)};var isn,g6e,w6e,r2,p6e,m6e,gre,rsn,csn,usn,osn,ssn,lsn,fsn,v6e,y6e,k6e,asn,Dv,LJ,E6e,hsn,j6e,wre,dsn,bsn,gsn,S6e,wsn,Th,A6e;v(IN,"MrTreeOptions",990),m(991,1,{},yk),s.uf=function(){var n;return n=new ZOe,n},s.vf=function(n){},v(IN,"MrTreeOptions/MrtreeFactory",991),m(353,23,{3:1,35:1,23:1,353:1},g$);var pre,PJ,mre,vre,M6e=mt(IN,"OrderWeighting",353,At,nyn,kmn),psn;m(425,23,{3:1,35:1,23:1,425:1},gse);var T6e,yre,x6e=mt(IN,"TreeifyingOrder",425,At,Y5n,Emn),msn;m(1446,1,uc,TU),s.pg=function(n){return u(n,120),vsn},s.If=function(n,t){Y8n(this,u(n,120),t)};var vsn;v("org.eclipse.elk.alg.mrtree.p1treeify","DFSTreeifyer",1446),m(1447,1,uc,cP),s.pg=function(n){return u(n,120),ysn},s.If=function(n,t){NDn(this,u(n,120),t)};var ysn;v($8,"NodeOrderer",1447),m(1454,1,{},Yq),s.rd=function(n){return nDe(n)},v($8,"NodeOrderer/0methodref$lambda$6$Type",1454),m(1448,1,Pt,k_),s.Mb=function(n){return T4(),$e(Pe(T(u(n,40),(xi(),tb))))},v($8,"NodeOrderer/lambda$0$Type",1448),m(1449,1,Pt,E_),s.Mb=function(n){return T4(),u(T(u(n,40),(Au(),Dv)),15).a<0},v($8,"NodeOrderer/lambda$1$Type",1449),m(1450,1,Pt,Nje),s.Mb=function(n){return $8n(this.a,u(n,40))},v($8,"NodeOrderer/lambda$2$Type",1450),m(1451,1,Pt,Oje),s.Mb=function(n){return x6n(this.a,u(n,40))},v($8,"NodeOrderer/lambda$3$Type",1451),m(1452,1,Ut,MT),s.Le=function(n,t){return t8n(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v($8,"NodeOrderer/lambda$4$Type",1452),m(1453,1,Pt,j_),s.Mb=function(n){return T4(),u(T(u(n,40),(xi(),fre)),15).a!=0},v($8,"NodeOrderer/lambda$5$Type",1453),m(1455,1,uc,xU),s.pg=function(n){return u(n,120),ksn},s.If=function(n,t){$In(this,u(n,120),t)},s.b=0;var ksn;v("org.eclipse.elk.alg.mrtree.p3place","NodePlacer",1455),m(1456,1,uc,Ex),s.pg=function(n){return u(n,120),Esn},s.If=function(n,t){mIn(u(n,120),t)};var Esn,LBn=v(Vs,"EdgeRouter",1456);m(1458,1,Ut,kk),s.Le=function(n,t){return uo(u(n,15).a,u(t,15).a)},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Vs,"EdgeRouter/0methodref$compare$Type",1458),m(1463,1,{},jT),s.We=function(n){return W(ie(n))},v(Vs,"EdgeRouter/1methodref$doubleValue$Type",1463),m(1465,1,Ut,Jg),s.Le=function(n,t){return vi(W(ie(n)),W(ie(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Vs,"EdgeRouter/2methodref$compare$Type",1465),m(1467,1,Ut,Ek),s.Le=function(n,t){return vi(W(ie(n)),W(ie(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Vs,"EdgeRouter/3methodref$compare$Type",1467),m(1469,1,{},vy),s.We=function(n){return W(ie(n))},v(Vs,"EdgeRouter/4methodref$doubleValue$Type",1469),m(1471,1,Ut,ST),s.Le=function(n,t){return vi(W(ie(n)),W(ie(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Vs,"EdgeRouter/5methodref$compare$Type",1471),m(1473,1,Ut,AT),s.Le=function(n,t){return vi(W(ie(n)),W(ie(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Vs,"EdgeRouter/6methodref$compare$Type",1473),m(1457,1,{},m_),s.Kb=function(n){return N1(),u(T(u(n,40),(Au(),Th)),15)},v(Vs,"EdgeRouter/lambda$0$Type",1457),m(1468,1,{},v_),s.Kb=function(n){return a2n(u(n,40))},v(Vs,"EdgeRouter/lambda$11$Type",1468),m(1470,1,{},oxe),s.Kb=function(n){return I3n(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Vs,"EdgeRouter/lambda$13$Type",1470),m(1472,1,{},uxe),s.Kb=function(n){return b2n(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Vs,"EdgeRouter/lambda$15$Type",1472),m(1474,1,Ut,y_),s.Le=function(n,t){return Hjn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Vs,"EdgeRouter/lambda$17$Type",1474),m(1475,1,Ut,Qq),s.Le=function(n,t){return Jjn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Vs,"EdgeRouter/lambda$18$Type",1475),m(1476,1,Ut,S_),s.Le=function(n,t){return qjn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Vs,"EdgeRouter/lambda$19$Type",1476),m(1459,1,Pt,Dje),s.Mb=function(n){return a4n(this.a,u(n,40))},s.a=0,v(Vs,"EdgeRouter/lambda$2$Type",1459),m(1477,1,Ut,A_),s.Le=function(n,t){return Gjn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Vs,"EdgeRouter/lambda$20$Type",1477),m(1460,1,Ut,M_),s.Le=function(n,t){return E3n(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Vs,"EdgeRouter/lambda$3$Type",1460),m(1461,1,Ut,TT),s.Le=function(n,t){return j3n(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Vs,"EdgeRouter/lambda$4$Type",1461),m(1462,1,{},T_),s.Kb=function(n){return h2n(u(n,40))},v(Vs,"EdgeRouter/lambda$5$Type",1462),m(1464,1,{},sxe),s.Kb=function(n){return _3n(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Vs,"EdgeRouter/lambda$7$Type",1464),m(1466,1,{},lxe),s.Kb=function(n){return d2n(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Vs,"EdgeRouter/lambda$9$Type",1466),m(662,1,{662:1},eJe),s.e=0,s.f=!1,s.g=!1,v(Vs,"MultiLevelEdgeNodeNodeGap",662),m(1864,1,Ut,x_),s.Le=function(n,t){return S4n(u(n,240),u(t,240))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Vs,"MultiLevelEdgeNodeNodeGap/lambda$0$Type",1864),m(1865,1,Ut,C_),s.Le=function(n,t){return A4n(u(n,240),u(t,240))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Vs,"MultiLevelEdgeNodeNodeGap/lambda$1$Type",1865);var Iv;m(487,23,{3:1,35:1,23:1,487:1,188:1,196:1},wse),s.bg=function(){return RFe(this)},s.og=function(){return RFe(this)};var $J,_v,C6e=mt(z2e,"RadialLayoutPhases",487,At,Q5n,jmn),jsn;m(1083,214,$w,xMe),s.kf=function(n,t){var i,r,c,o,l,f;if(i=PUe(this,n),t.Tg("Radial layout",i.c.length),$e(Pe(ve(n,(_0(),z6e))))||FC((r=new tE((Mb(),new s0(n))),r)),f=FMn(n),yi(n,(x3(),Iv),f),!f)throw P(new Gn("The given graph is not a tree!"));for(c=W(ie(ve(n,zJ))),c==0&&(c=fqe(n)),yi(n,zJ,c),l=new L(PUe(this,n));l.a=3)for(se=u(X(ee,0),26),Ne=u(X(ee,1),26),o=0;o+2=se.f+Ne.f+p||Ne.f>=he.f+se.f+p){nn=!0;break}else++o;else nn=!0;if(!nn){for(S=ee.i,f=new ct(ee);f.e!=f.i.gc();)l=u(lt(f),26),yi(l,(Ht(),_D),me(S)),--S;hKe(n,new U5),t.Ug();return}for(i=(PC(this.a),ia(this.a,(YB(),MA),u(ve(n,pye),188)),ia(this.a,FJ,u(ve(n,aye),188)),ia(this.a,Dre,u(ve(n,bye),188)),Dse(this.a,(Tn=new ur,Bt(Tn,MA,(vz(),Lre)),Bt(Tn,FJ,_re),$e(Pe(ve(n,lye)))&&Bt(Tn,MA,Pre),$e(Pe(ve(n,sye)))&&Bt(Tn,MA,Ire),Tn)),tN(this.a,n)),b=1/i.c.length,O=new L(i);O.a0&&uFe((Yn(t-1,n.length),n.charCodeAt(t-1)),tQe);)--t;if(r>=t)throw P(new Gn("The given string does not contain any numbers."));if(c=_p((Yr(r,t,n.length),n.substr(r,t-r)),`,|;|\r| -`),c.length!=2)throw P(new Gn("Exactly two numbers are expected, "+c.length+" were found."));try{this.a=Tp(xp(c[0])),this.b=Tp(xp(c[1]))}catch(o){throw o=or(o),q(o,131)?(i=o,P(new Gn(iQe+i))):P(o)}},s.Ib=function(){return"("+this.a+","+this.b+")"},s.a=0,s.b=0;var _r=v(TN,"KVector",8);m(78,66,{3:1,4:1,20:1,31:1,56:1,18:1,66:1,16:1,78:1,414:1},Ss,JP,EOe),s.Nc=function(){return mkn(this)},s.ag=function(n){var t,i,r,c,o,l;r=_p(n,`,|;|\\(|\\)|\\[|\\]|\\{|\\}| | | -`),Js(this);try{for(i=0,o=0,c=0,l=0;i0&&(o%2==0?c=Tp(r[i]):l=Tp(r[i]),o>0&&o%2!=0&&qt(this,new ke(c,l)),++o),++i}catch(f){throw f=or(f),q(f,131)?(t=f,P(new Gn("The given string does not match the expected format for vectors."+t))):P(f)}},s.Ib=function(){var n,t,i;for(n=new Ws("("),t=Et(this,0);t.b!=t.d.c;)i=u(yt(t),8),Gt(n,i.a+","+i.b),t.b!=t.d.c&&(n.a+="; ");return(n.a+=")",n).a};var e9e=v(TN,"KVectorChain",78);m(256,23,{3:1,35:1,23:1,256:1},xE);var ice,QJ,ZJ,TD,xD,WJ,n9e=mt(Co,"Alignment",256,At,v9n,Ymn),rfn;m(975,1,Ra,Tx),s.tf=function(n){VXe(n)};var t9e,rce,cfn,i9e,r9e,ufn,c9e,ofn,sfn,u9e,o9e,lfn;v(Co,"BoxLayouterOptions",975),m(976,1,{},HT),s.uf=function(){var n;return n=new fL,n},s.vf=function(n){},v(Co,"BoxLayouterOptions/BoxFactory",976),m(299,23,{3:1,35:1,23:1,299:1},CE);var _A,cce,LA,PA,$A,uce,oce=mt(Co,"ContentAlignment",299,At,y9n,Qmn),ffn;m(689,1,Ra,Mx),s.tf=function(n){Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,sZe),""),"Layout Algorithm"),"Select a specific layout algorithm."),(Qb(),x6)),Re),We((dh(),An))))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,lZe),""),"Resolved Layout Algorithm"),"Meta data associated with the selected algorithm."),qa),RBn),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,s2e),""),"Alignment"),"Alignment of the selected node relative to other nodes; the exact meaning depends on the used algorithm."),s9e),Ri),n9e),We(lr)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,O8),""),"Aspect Ratio"),"The desired aspect ratio of the drawing, that is the quotient of width by height."),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,Epe),""),"Bend Points"),"A fixed list of bend points for the edge. This is used by the 'Fixed Layout' algorithm to specify a pre-defined routing for an edge. The vector chain must include the source point, any bend points, and the target point, so it must have at least two points."),qa),e9e),We(pa)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,MF),""),"Content Alignment"),"Specifies how the content of a node are aligned. Each node can individually control the alignment of its contents. I.e. if a node should be aligned top left in its parent node, the parent node should specify that option."),f9e),T6),oce),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,DN),""),"Debug Mode"),"Whether additional debug information shall be generated."),(Pn(),!1)),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Pee),""),"Direction"),"Overall direction of edges: horizontal (right / left) or vertical (down / up)."),a9e),Ri),BA),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,NN),""),"Edge Routing"),"What kind of edge routing style should be applied for the content of a parent node. Algorithms may also set this option to single edges in order to mark them as splines. The bend point list of edges with this option set to SPLINES must be interpreted as control points for a piecewise cubic spline."),b9e),Ri),kce),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,ype),""),"Expand Nodes"),"If active, nodes are expanded to fill the area of their parent."),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,AF),""),"Hierarchy Handling"),"Determines whether separate layout runs are triggered for different compound nodes in a hierarchical graph. Setting a node's hierarchy handling to `INCLUDE_CHILDREN` will lay out that node and all of its descendants in a single layout run, until a descendant is encountered which has its hierarchy handling set to `SEPARATE_CHILDREN`. In general, `SEPARATE_CHILDREN` will ensure that a new layout run is triggered for a node with that setting. Including multiple levels of hierarchy in a single layout run may allow cross-hierarchical edges to be laid out properly. If the root node is set to `INHERIT` (or not set at all), the default behavior is `SEPARATE_CHILDREN`."),w9e),Ri),c8e),Ti(An,z(B(Ga,1),ye,160,0,[lr]))))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Fp),""),"Padding"),"The padding to be left to a parent element's border when placing child elements. This can also serve as an output option of a layout algorithm if node size calculation is setup appropriately."),T9e),qa),d3e),Ti(An,z(B(Ga,1),ye,160,0,[lr]))))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,hS),""),"Interactive"),"Whether the algorithm should be run in interactive mode for the content of a parent node. What this means exactly depends on how the specific algorithm interprets this option. Usually in the interactive mode algorithms try to modify the current layout as little as possible."),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,xF),""),"interactive Layout"),"Whether the graph should be changeable interactively and by setting constraints"),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,dS),""),"Omit Node Micro Layout"),"Node micro layout comprises the computation of node dimensions (if requested), the placement of ports and their labels, and the placement of node labels. The functionality is implemented independent of any specific layout algorithm and shouldn't have any negative impact on the layout algorithm's performance itself. Yet, if any unforeseen behavior occurs, this option allows to deactivate the micro layout."),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,XW),""),"Port Constraints"),"Defines constraints of the position of the ports of a node."),D9e),Ri),s8e),We(lr)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,TF),""),"Position"),"The position of a node, port, or label. This is used by the 'Fixed Layout' algorithm to specify a pre-defined position."),qa),_r),Ti(lr,z(B(Ga,1),ye,160,0,[Hd,U1]))))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,kN),""),"Priority"),"Defines the priority of an object; its meaning depends on the specific layout algorithm and the context where it is used."),hc),Er),Ti(lr,z(B(Ga,1),ye,160,0,[pa]))))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,uF),""),"Randomization Seed"),"Seed used for pseudo-random number generators to control the layout algorithm. If the value is 0, the seed shall be determined pseudo-randomly (e.g. from the system time)."),hc),Er),We(An)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,aS),""),"Separate Connected Components"),"Whether each connected component should be processed separately."),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,v2e),""),"Junction Points"),"This option is not used as option, but as output of the layout algorithms. It is attached to edges and determines the points where junction symbols should be drawn in order to represent hyperedges with orthogonal routing. Whether such points are computed depends on the chosen layout algorithm and edge routing style. The points are put into the vector chain with no specific order."),v9e),qa),e9e),We(pa)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,j2e),""),"Comment Box"),"Whether the node should be regarded as a comment box instead of a regular node. In that case its placement should be similar to how labels are handled. Any edges incident to a comment box specify to which graph elements the comment is related."),!1),Sr),Yi),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,S2e),""),"Hypernode"),"Whether the node should be handled as a hypernode."),!1),Sr),Yi),We(lr)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,aBn),""),"Label Manager"),"Label managers can shorten labels upon a layout algorithm's request."),qa),GBn),Ti(An,z(B(Ga,1),ye,160,0,[U1]))))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,fZe),""),"Softwrapping Fuzziness"),"Determines the amount of fuzziness to be used when performing softwrapping on labels. The value expresses the percent of overhang that is permitted for each line. If the next line would take up less space than this threshold, it is appended to the current line instead of being placed in a new line."),0),Wr),br),We(U1)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,M2e),""),"Margins"),"Margins define additional space around the actual bounds of a graph element. For instance, ports or labels being placed on the outside of a node's border might introduce such a margin. The margin is used to guarantee non-overlap of other graph elements with those ports or labels."),y9e),qa),h3e),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,u2e),""),"No Layout"),"No layout is done for the associated element. This is used to mark parts of a diagram to avoid their inclusion in the layout graph, or to mark parts of the layout graph to prevent layout engines from processing them. If you wish to exclude the contents of a compound node from automatic layout, while the node itself is still considered on its own layer, use the 'Fixed Layout' algorithm for that node."),!1),Sr),Yi),Ti(lr,z(B(Ga,1),ye,160,0,[pa,Hd,U1]))))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,aZe),""),"Scale Factor"),"The scaling factor to be applied to the corresponding node in recursive layout. It causes the corresponding node's size to be adjusted, and its ports and labels to be sized and placed accordingly after the layout of that node has been determined (and before the node itself and its siblings are arranged). The scaling is not reverted afterwards, so the resulting layout graph contains the adjusted size and position data. This option is currently not supported if 'Layout Hierarchy' is set."),1),Wr),br),We(lr)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,hZe),""),"Child Area Width"),"The width of the area occupied by the laid out children of a node."),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,dZe),""),"Child Area Height"),"The height of the area occupied by the laid out children of a node."),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,EN),""),iZe),"Turns topdown layout on and off. If this option is enabled, hierarchical layout will be computed first for the root node and then for its children recursively. Layouts are then scaled down to fit the area provided by their parents. Graphs must follow a certain structure for topdown layout to work properly. {@link TopdownNodeTypes.PARALLEL_NODE} nodes must have children of type {@link TopdownNodeTypes.HIERARCHICAL_NODE} and must define {@link topdown.hierarchicalNodeWidth} and {@link topdown.hierarchicalNodeAspectRatio} for their children. Furthermore they need to be laid out using an algorithm that is a {@link TopdownLayoutProvider}. Hierarchical nodes can also be parents of other hierarchical nodes and can optionally use a {@link TopdownSizeApproximator} to dynamically set sizes during topdown layout. In this case {@link topdown.hierarchicalNodeWidth} and {@link topdown.hierarchicalNodeAspectRatio} should be set on the node itself rather than the parent. The values are then used by the size approximator as base values. Hierarchical nodes require the layout option {@link nodeSize.fixedGraphSize} to be true to prevent the algorithm used there from resizing the hierarchical node. This option is not supported if 'Hierarchy Handling' is set to 'INCLUDE_CHILDREN'"),!1),Sr),Yi),We(An)))),Gi(n,EN,Rw,null),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,bZe),""),"Animate"),"Whether the shift from the old layout to the new computed layout shall be animated."),!0),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,gZe),""),"Animation Time Factor"),"Factor for computation of animation time. The higher the value, the longer the animation time. If the value is 0, the resulting time is always equal to the minimum defined by 'Minimal Animation Time'."),me(100)),hc),Er),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,wZe),""),"Layout Ancestors"),"Whether the hierarchy levels on the path from the selected element to the root of the diagram shall be included in the layout process."),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,pZe),""),"Maximal Animation Time"),"The maximal time for animations, in milliseconds."),me(4e3)),hc),Er),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,mZe),""),"Minimal Animation Time"),"The minimal time for animations, in milliseconds."),me(400)),hc),Er),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,vZe),""),"Progress Bar"),"Whether a progress bar shall be displayed during layout computations."),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,yZe),""),"Validate Graph"),"Whether the graph shall be validated before any layout algorithm is applied. If this option is enabled and at least one error is found, the layout process is aborted and a message is shown to the user."),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,kZe),""),"Validate Options"),"Whether layout options shall be validated before any layout algorithm is applied. If this option is enabled and at least one error is found, the layout process is aborted and a message is shown to the user."),!0),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,EZe),""),"Zoom to Fit"),"Whether the zoom level shall be set to view the whole diagram after layout."),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,kpe),"box"),"Box Layout Mode"),"Configures the packing mode used by the {@link BoxLayoutProvider}. If SIMPLE is not required (neither priorities are used nor the interactive mode), GROUP_DEC can improve the packing and decrease the area. GROUP_MIXED and GROUP_INC may, in very specific scenarios, work better."),l9e),Ri),k8e),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,jZe),"json"),"Shape Coords"),"For layouts transferred into JSON graphs, specify the coordinate system to be used for nodes, ports, and labels of nodes and ports."),m9e),Ri),a8e),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,SZe),"json"),"Edge Coords"),"For layouts transferred into JSON graphs, specify the coordinate system to be used for edge route points and edge labels."),p9e),Ri),U9e),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Kwe),za),"Comment Comment Spacing"),"Spacing to be preserved between a comment box and other comment boxes connected to the same node. The space left between comment boxes of different nodes is controlled by the node-node spacing."),10),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Vwe),za),"Comment Node Spacing"),"Spacing to be preserved between a node and its connected comment boxes. The space left between a node and the comments of another node is controlled by the node-node spacing."),10),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Ywe),za),"Components Spacing"),"Spacing to be preserved between pairs of connected components. This option is only relevant if 'separateConnectedComponents' is activated."),20),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Qwe),za),"Edge Spacing"),"Spacing to be preserved between any two edges. Note that while this can somewhat easily be satisfied for the segments of orthogonally drawn edges, it is harder for general polylines or splines."),10),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,UW),za),"Edge Label Spacing"),"The minimal distance to be preserved between a label and the edge it is associated with. Note that the placement of a label is influenced by the 'edgelabels.placement' option."),2),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Lee),za),"Edge Node Spacing"),"Spacing to be preserved between nodes and edges."),10),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Zwe),za),"Label Spacing"),"Determines the amount of space to be left between two labels of the same graph element."),0),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,n2e),za),"Label Node Spacing"),"Spacing to be preserved between labels and the border of node they are associated with. Note that the placement of a label is influenced by the 'nodelabels.placement' option."),5),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Wwe),za),"Horizontal spacing between Label and Port"),"Horizontal spacing to be preserved between labels and the ports they are associated with. Note that the placement of a label is influenced by the 'portlabels.placement' option."),1),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,e2e),za),"Vertical spacing between Label and Port"),"Vertical spacing to be preserved between labels and the ports they are associated with. Note that the placement of a label is influenced by the 'portlabels.placement' option."),1),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,zp),za),"Node Spacing"),"The minimal distance to be preserved between each two nodes."),20),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,t2e),za),"Node Self Loop Spacing"),"Spacing to be preserved between a node and its self loops."),10),Wr),br),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,i2e),za),"Port Spacing"),"Spacing between pairs of ports of the same node."),10),Wr),br),Ti(An,z(B(Ga,1),ye,160,0,[lr]))))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,r2e),za),"Individual Spacing"),"Allows to specify individual spacing values for graph elements that shall be different from the value specified for the element's parent."),qa),uan),Ti(lr,z(B(Ga,1),ye,160,0,[pa,Hd,U1]))))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,T2e),za),"Additional Port Space"),"Additional space around the sets of ports on each node side. For each side of a node, this option can reserve additional space before and after the ports on each side. For example, a top spacing of 20 makes sure that the first port on the western and eastern side is 20 units away from the northern border."),H9e),qa),h3e),We(An)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,Ree),TZe),"Layout Partition"),"Partition to which the node belongs. This requires Layout Partitioning to be active. Nodes with lower partition IDs will appear to the left of nodes with higher partition IDs (assuming a left-to-right layout direction)."),hc),Er),Ti(An,z(B(Ga,1),ye,160,0,[lr]))))),Gi(n,Ree,$ee,Efn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,$ee),TZe),"Layout Partitioning"),"Whether to activate partitioned layout. This will allow to group nodes through the Layout Partition option. a pair of nodes with different partition indices is then placed such that the node with lower index is placed to the left of the other node (with left-to-right layout direction). Depending on the layout algorithm, this may only be guaranteed to work if all nodes have a layout partition configured, or at least if edges that cross partitions are not part of a partition-crossing cycle."),x9e),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,a2e),xZe),"Node Label Padding"),"Define padding for node labels that are placed inside of a node."),E9e),qa),d3e),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,D8),xZe),"Node Label Placement"),"Hints for where node labels are to be placed; if empty, the node label's position is not modified."),j9e),T6),Lc),Ti(lr,z(B(Ga,1),ye,160,0,[U1]))))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,b2e),PF),"Port Alignment"),"Defines the default port distribution for a node. May be overridden for each side individually."),O9e),Ri),JA),We(lr)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,g2e),PF),"Port Alignment (North)"),"Defines how ports on the northern side are placed, overriding the node's general port alignment."),Ri),JA),We(lr)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,w2e),PF),"Port Alignment (South)"),"Defines how ports on the southern side are placed, overriding the node's general port alignment."),Ri),JA),We(lr)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,p2e),PF),"Port Alignment (West)"),"Defines how ports on the western side are placed, overriding the node's general port alignment."),Ri),JA),We(lr)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,m2e),PF),"Port Alignment (East)"),"Defines how ports on the eastern side are placed, overriding the node's general port alignment."),Ri),JA),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,cv),lne),"Node Size Constraints"),"What should be taken into account when calculating a node's size. Empty size constraints specify that a node's size is already fixed and should not be changed."),S9e),T6),UA),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,n6),lne),"Node Size Options"),"Options modifying the behavior of the size constraints set on a node. Each member of the set specifies something that should be taken into account when calculating node sizes. The empty set corresponds to no further modifications."),M9e),T6),h8e),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,t6),lne),"Node Size Minimum"),"The minimal size to which a node can be reduced."),A9e),qa),_r),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,N8),lne),"Fixed Graph Size"),"By default, the fixed layout provider will enlarge a graph until it is large enough to contain its children. If this option is set, it won't do so."),!1),Sr),Yi),We(An)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,k2e),_ee),"Edge Label Placement"),"Gives a hint on where to put edge labels."),h9e),Ri),X9e),We(U1)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,oF),_ee),"Inline Edge Labels"),"If true, an edge label is placed directly on its edge. May only apply to center edge labels. This kind of label placement is only advisable if the label's rendering is such that it is not crossed by its edge and thus stays legible."),!1),Sr),Yi),We(U1)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,hBn),"font"),"Font Name"),"Font name used for a label."),x6),Re),We(U1)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,AZe),"font"),"Font Size"),"Font size used for a label."),hc),Er),We(U1)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,A2e),fne),"Port Anchor Offset"),"The offset to the port position where connections shall be attached."),qa),_r),We(Hd)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,E2e),fne),"Port Index"),"The index of a port in the fixed order around a node. The order is assumed as clockwise, starting with the leftmost port on the top side. This option must be set if 'Port Constraints' is set to FIXED_ORDER and no specific positions are given for the ports. Additionally, the option 'Port Side' must be defined in this case."),hc),Er),We(Hd)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,o2e),fne),"Port Side"),"The side of a node on which a port is situated. This option must be set if 'Port Constraints' is set to FIXED_SIDE or FIXED_ORDER and no specific positions are given for the ports."),L9e),Ri),jc),We(Hd)))),Qe(n,new He(Ve(Ke(Ye(Ge(Xe(qe(Ue(new Be,c2e),fne),"Port Border Offset"),"The offset of ports on the node border. With a positive offset the port is moved outside of the node, while with a negative offset the port is moved towards the inside. An offset of 0 means that the port is placed directly on the node border, i.e. if the port side is north, the port's south border touches the nodes's north border; if the port side is east, the port's west border touches the nodes's east border; if the port side is south, the port's north border touches the node's south border; if the port side is west, the port's east border touches the node's west border."),Wr),br),We(Hd)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,I8),Ape),"Port Label Placement"),"Decides on a placement method for port labels; if empty, the node label's position is not modified."),I9e),T6),uG),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,h2e),Ape),"Port Labels Next to Port"),"Use 'portLabels.placement': NEXT_TO_PORT_OF_POSSIBLE."),!1),Sr),Yi),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,d2e),Ape),"Treat Port Labels as Group"),"If this option is true (default), the labels of a port will be treated as a group when it comes to centering them next to their port. If this option is false, only the first label will be centered next to the port, with the others being placed below. This only applies to labels of eastern and western ports and will have no effect if labels are not placed next to their port."),!0),Sr),Yi),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,one),H8),"Number of size categories"),"Defines the number of categories to use for the FIXED_INTEGER_RATIO_BOXES size approximator."),me(3)),hc),Er),We(An)))),Gi(n,one,sne,_fn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,jpe),H8),"Weight of a node containing children for determining the graph size"),"When determining the graph size for the size categorisation, this value determines how many times a node containing children is weighted more than a simple node. For example setting this value to four would result in a graph containing a simple node and a hierarchical node to be counted as having a size of five."),me(4)),hc),Er),We(An)))),Gi(n,jpe,one,null),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,jN),H8),"Topdown Scale Factor"),"The scaling factor to be applied to the nodes laid out within the node in recursive topdown layout. The difference to 'Scale Factor' is that the node itself is not scaled. This value has to be set on hierarchical nodes."),1),Wr),br),We(An)))),Gi(n,jN,Rw,Nfn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,sne),H8),"Topdown Size Approximator"),"The size approximator to be used to set sizes of hierarchical nodes during topdown layout. The default value is null, which results in nodes keeping whatever size is defined for them e.g. through parent parallel node or by manually setting the size."),null),qa),BBn),We(lr)))),Gi(n,sne,Rw,Dfn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,SN),H8),"Topdown Hierarchical Node Width"),"The fixed size of a hierarchical node when using topdown layout. If this value is set on a parallel node it applies to its children, when set on a hierarchical node it applies to the node itself."),150),Wr),br),Ti(An,z(B(Ga,1),ye,160,0,[lr]))))),Gi(n,SN,Rw,null),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,AN),H8),"Topdown Hierarchical Node Aspect Ratio"),"The fixed aspect ratio of a hierarchical node when using topdown layout. Default is 1/sqrt(2). If this value is set on a parallel node it applies to its children, when set on a hierarchical node it applies to the node itself."),1.414),Wr),br),Ti(An,z(B(Ga,1),ye,160,0,[lr]))))),Gi(n,AN,Rw,null),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Rw),H8),"Topdown Node Type"),"The different node types used for topdown layout. If the node type is set to {@link TopdownNodeTypes.PARALLEL_NODE} the algorithm must be set to a {@link TopdownLayoutProvider} such as {@link TopdownPacking}. The {@link nodeSize.fixedGraphSize} option is technically only required for hierarchical nodes."),null),Ri),b8e),We(lr)))),Gi(n,Rw,N8,null),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,Spe),H8),"Topdown Scale Cap"),"Determines the upper limit for the topdown scale factor. The default value is 1.0 which ensures that nested children never end up appearing larger than their parents in terms of unit sizes such as the font size. If the limit is larger, nodes will fully utilize the available space, but it is counteriniuitive for inner nodes to have a larger scale than outer nodes."),1),Wr),br),We(An)))),Gi(n,Spe,Rw,Ofn),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,l2e),CZe),"Activate Inside Self Loops"),"Whether this node allows to route self loops inside of it instead of around it. If set to true, this will make the node a compound node if it isn't already, and will require the layout algorithm to support compound nodes with hierarchical ports."),!1),Sr),Yi),We(lr)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,f2e),CZe),"Inside Self Loop"),"Whether a self loop should be routed inside a node instead of around that node."),!1),Sr),Yi),We(pa)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,y2e),"edge"),"Edge Thickness"),"The thickness of an edge. This is a hint on the line width used to draw an edge, possibly requiring more space to be reserved for it."),1),Wr),br),We(pa)))),Qe(n,new He(Ve(Ke(Ye(sn(Ge(Xe(qe(Ue(new Be,MZe),"edge"),"Edge Type"),"The type of an edge. This is usually used for UML class diagrams, where associations must be handled differently from generalizations."),g9e),Ri),W9e),We(pa)))),vE(n,new y4(dE(Zy(Qy(new t0,$n),"Layered"),'The layer-based method was introduced by Sugiyama, Tagawa and Toda in 1981. It emphasizes the direction of edges by pointing as many edges as possible into the same direction. The nodes are arranged in layers, which are sometimes called "hierarchies", and then reordered such that the number of edge crossings is minimized. Afterwards, concrete coordinates are computed for the nodes and edge bend points.'))),vE(n,new y4(dE(Zy(Qy(new t0,"org.eclipse.elk.orthogonal"),"Orthogonal"),`Orthogonal methods that follow the "topology-shape-metrics" approach by Batini, Nardelli and Tamassia '86. The first phase determines the topology of the drawing by applying a planarization technique, which results in a planar representation of the graph. The orthogonal shape is computed in the second phase, which aims at minimizing the number of edge bends, and is called orthogonalization. The third phase leads to concrete coordinates for nodes and edge bend points by applying a compaction method, thus defining the metrics.`))),vE(n,new y4(dE(Zy(Qy(new t0,Po),"Force"),"Layout algorithms that follow physical analogies by simulating a system of attractive and repulsive forces. The first successful method of this kind was proposed by Eades in 1984."))),vE(n,new y4(dE(Zy(Qy(new t0,"org.eclipse.elk.circle"),"Circle"),"Circular layout algorithms emphasize cycles or biconnected components of a graph by arranging them in circles. This is useful if a drawing is desired where such components are clearly grouped, or where cycles are shown as prominent OPTIONS of the graph."))),vE(n,new y4(dE(Zy(Qy(new t0,FQe),"Tree"),"Specialized layout methods for trees, i.e. acyclic graphs. The regular structure of graphs that have no undirected cycles can be emphasized using an algorithm of this type."))),vE(n,new y4(dE(Zy(Qy(new t0,"org.eclipse.elk.planar"),"Planar"),"Algorithms that require a planar or upward planar graph. Most of these algorithms are theoretically interesting, but not practically usable."))),vE(n,new y4(dE(Zy(Qy(new t0,Fl),"Radial"),"Radial layout algorithms usually position the nodes of the graph on concentric circles."))),IXe((new IU,n)),VXe((new Tx,n)),cXe((new _U,n))};var C6,afn,s9e,j7,hfn,dfn,l9e,bm,gm,bfn,CD,f9e,OD,vg,a9e,sce,lce,h9e,d9e,b9e,gfn,g9e,wfn,Pv,w9e,pfn,ND,fce,DD,ace,mfn,p9e,vfn,m9e,$v,v9e,S7,y9e,k9e,E9e,Rv,j9e,yg,S9e,wm,Bv,A9e,ib,M9e,eG,ID,t1,T9e,yfn,x9e,kfn,Efn,C9e,O9e,hce,dce,bce,gce,N9e,Ls,RA,D9e,wce,pce,pm,I9e,_9e,zv,L9e,O6,_D,mce,mm,jfn,vce,Sfn,Afn,Mfn,Tfn,P9e,$9e,N6,R9e,nG,B9e,z9e,Jd,xfn,F9e,H9e,J9e,A7,vm,M7,D6,Cfn,Ofn,tG,Nfn,iG,Dfn,Ifn,_fn,Lfn;v(Co,"CoreOptions",689),m(86,23,{3:1,35:1,23:1,86:1},hC);var Ua,Zc,ru,Xa,Ul,BA=mt(Co,"Direction",86,At,Iyn,Xmn),Pfn;m(278,23,{3:1,35:1,23:1,278:1},m$);var rG,LD,G9e,q9e,U9e=mt(Co,"EdgeCoords",278,At,tyn,Kmn),$fn;m(279,23,{3:1,35:1,23:1,279:1},aK);var T7,ym,x7,X9e=mt(Co,"EdgeLabelPlacement",279,At,W4n,Vmn),Rfn;m(222,23,{3:1,35:1,23:1,222:1},v$);var C7,PD,I6,yce,kce=mt(Co,"EdgeRouting",222,At,iyn,Umn),Bfn;m(327,23,{3:1,35:1,23:1,327:1},OE);var K9e,V9e,Y9e,Q9e,Ece,Z9e,W9e=mt(Co,"EdgeType",327,At,j9n,i3n),zfn;m(973,1,Ra,IU),s.tf=function(n){IXe(n)};var e8e,n8e,t8e,i8e,Ffn,r8e,zA;v(Co,"FixedLayouterOptions",973),m(974,1,{},JT),s.uf=function(){var n;return n=new Gg,n},s.vf=function(n){},v(Co,"FixedLayouterOptions/FixedFactory",974),m(347,23,{3:1,35:1,23:1,347:1},hK);var Gd,cG,FA,c8e=mt(Co,"HierarchyHandling",347,At,e6n,r3n),Hfn,BBn=Ji(Co,"ITopdownSizeApproximator");m(292,23,{3:1,35:1,23:1,292:1},y$);var i1,rb,$D,RD,Jfn=mt(Co,"LabelSide",292,At,ryn,t3n),Gfn;m(96,23,{3:1,35:1,23:1,96:1},p3);var X1,Gf,df,qf,bl,Uf,bf,r1,Xf,Lc=mt(Co,"NodeLabelPlacement",96,At,S8n,Zmn),qfn;m(257,23,{3:1,35:1,23:1,257:1},dC);var u8e,HA,cb,o8e,BD,JA=mt(Co,"PortAlignment",257,At,qyn,Wmn),Ufn;m(102,23,{3:1,35:1,23:1,102:1},NE);var kg,eo,c1,O7,Ka,ub,s8e=mt(Co,"PortConstraints",102,At,E9n,e3n),Xfn;m(280,23,{3:1,35:1,23:1,280:1},DE);var GA,qA,K1,zD,ob,_6,uG=mt(Co,"PortLabelPlacement",280,At,k9n,n3n),Kfn;m(64,23,{3:1,35:1,23:1,64:1},bC);var Wn,Xn,Xl,Kl,Qo,Bo,Va,Kf,ys,as,po,ks,Zo,Wo,Vf,gl,wl,gf,dt,yu,Kn,jc=mt(Co,"PortSide",64,At,_yn,s3n),Vfn;m(977,1,Ra,_U),s.tf=function(n){cXe(n)};var Yfn,Qfn,l8e,Zfn,Wfn;v(Co,"RandomLayouterOptions",977),m(978,1,{},GT),s.uf=function(){var n;return n=new KT,n},s.vf=function(n){},v(Co,"RandomLayouterOptions/RandomFactory",978),m(300,23,{3:1,35:1,23:1,300:1},dK);var FD,jce,f8e,a8e=mt(Co,"ShapeCoords",300,At,n6n,l3n),ean;m(380,23,{3:1,35:1,23:1,380:1},k$);var km,HD,JD,Eg,UA=mt(Co,"SizeConstraint",380,At,uyn,f3n),nan;m(266,23,{3:1,35:1,23:1,266:1},m3);var GD,oG,N7,Sce,qD,XA,sG,lG,fG,h8e=mt(Co,"SizeOptions",266,At,N8n,u3n),tan;m(281,23,{3:1,35:1,23:1,281:1},bK);var Em,d8e,aG,b8e=mt(Co,"TopdownNodeTypes",281,At,t6n,o3n),ian;m(288,23,RF);var g8e,Ace,w8e,p8e,UD=mt(Co,"TopdownSizeApproximator",288,At,cyn,c3n);m(969,288,RF,uDe),s.Sg=function(n){return JHe(n)},mt(Co,"TopdownSizeApproximator/1",969,UD,null,null),m(970,288,RF,HDe),s.Sg=function(n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne,nn,en,Tn;for(t=u(ve(n,(Ht(),mm)),144),Ne=(a0(),M=new oE,M),XO(Ne,n),nn=new gt,o=new ct((!n.a&&(n.a=new we($t,n,10,11)),n.a));o.e!=o.i.gc();)r=u(lt(o),26),K=(S=new oE,S),Nz(K,Ne),XO(K,r),Tn=JHe(r),tw(K,E.Math.max(r.g,Tn.a),E.Math.max(r.f,Tn.b)),Xo(nn.f,r,K);for(c=new ct((!n.a&&(n.a=new we($t,n,10,11)),n.a));c.e!=c.i.gc();)for(r=u(lt(c),26),p=new ct((!r.e&&(r.e=new Cn(wr,r,7,4)),r.e));p.e!=p.i.gc();)b=u(lt(p),85),he=u(hu(Uc(nn.f,r)),26),se=u(Bn(nn,X((!b.c&&(b.c=new Cn(pt,b,5,8)),b.c),0)),26),ee=(y=new c3,y),kt((!ee.b&&(ee.b=new Cn(pt,ee,4,7)),ee.b),he),kt((!ee.c&&(ee.c=new Cn(pt,ee,5,8)),ee.c),se),Oz(ee,zi(he)),XO(ee,b);I=u(zC(t.f),214);try{I.kf(Ne,new i0),Jfe(t.f,I)}catch(On){throw On=or(On),q(On,101)?(O=On,P(O)):P(On)}return ua(Ne,gm)||ua(Ne,bm)||tW(Ne),h=W(ie(ve(Ne,gm))),f=W(ie(ve(Ne,bm))),l=h/f,i=W(ie(ve(Ne,vm)))*E.Math.sqrt((!Ne.a&&(Ne.a=new we($t,Ne,10,11)),Ne.a).i),en=u(ve(Ne,t1),104),G=en.b+en.c+1,R=en.d+en.a+1,new ke(E.Math.max(G,i),E.Math.max(R,i/l))},mt(Co,"TopdownSizeApproximator/2",970,UD,null,null),m(971,288,RF,b_e),s.Sg=function(n){var t,i,r,c,o,l;return i=W(ie(ve(n,(Ht(),vm)))),t=i/W(ie(ve(n,A7))),r=N_n(n),o=u(ve(n,t1),104),c=W(ie(Ie(Jd))),zi(n)&&(c=W(ie(ve(zi(n),Jd)))),l=k1(new ke(i,t),r),bi(l,new ke(-(o.b+o.c)-c,-(o.d+o.a)-c))},mt(Co,"TopdownSizeApproximator/3",971,UD,null,null),m(972,288,RF,JDe),s.Sg=function(n){var t,i,r,c,o,l,f,h,b,p;for(l=new ct((!n.a&&(n.a=new we($t,n,10,11)),n.a));l.e!=l.i.gc();)o=u(lt(l),26),ve(o,(Ht(),iG))!=null&&(!o.a&&(o.a=new we($t,o,10,11)),!!o.a)&&(!o.a&&(o.a=new we($t,o,10,11)),o.a).i>0?(i=u(ve(o,iG),521),p=i.Sg(o),b=u(ve(o,t1),104),tw(o,E.Math.max(o.g,p.a+b.b+b.c),E.Math.max(o.f,p.b+b.d+b.a))):(!o.a&&(o.a=new we($t,o,10,11)),o.a).i!=0&&tw(o,W(ie(ve(o,vm))),W(ie(ve(o,vm)))/W(ie(ve(o,A7))));t=u(ve(n,(Ht(),mm)),144),h=u(zC(t.f),214);try{h.kf(n,new i0),Jfe(t.f,h)}catch(y){throw y=or(y),q(y,101)?(f=y,P(f)):P(y)}return yi(n,C6,J8),rPe(n),tW(n),c=W(ie(ve(n,gm))),r=W(ie(ve(n,bm))),new ke(c,r)},mt(Co,"TopdownSizeApproximator/4",972,UD,null,null);var ran;m(345,1,{852:1},U5),s.Tg=function(n,t){return tGe(this,n,t)},s.Ug=function(){xGe(this)},s.Vg=function(){return this.q},s.Wg=function(){return this.f?xR(this.f):null},s.Xg=function(){return xR(this.a)},s.Yg=function(){return this.p},s.Zg=function(){return!1},s.$g=function(){return this.n},s._g=function(){return this.p!=null&&!this.b},s.ah=function(n){var t;this.n&&(t=n,Te(this.f,t))},s.bh=function(n,t){var i,r;this.n&&n&&y6n(this,(i=new iIe,r=LZ(i,n),w$n(i),r),(_B(),Tce))},s.dh=function(n){var t;return this.b?null:(t=o8n(this,this.g),qt(this.a,t),t.i=this,this.d=n,t)},s.eh=function(n){n>0&&!this.b&&zhe(this,n)},s.b=!1,s.c=0,s.d=-1,s.e=null,s.f=null,s.g=-1,s.j=!1,s.k=!1,s.n=!1,s.o=0,s.q=0,s.r=0,v(Pu,"BasicProgressMonitor",345),m(706,214,$w,fL),s.kf=function(n,t){hKe(n,t)},v(Pu,"BoxLayoutProvider",706),m(965,1,Ut,Gje),s.Le=function(n,t){return wNn(this,u(n,26),u(t,26))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},s.a=!1,v(Pu,"BoxLayoutProvider/1",965),m(167,1,{167:1},aB,kOe),s.Ib=function(){return this.c?Ibe(this.c):_a(this.b)},v(Pu,"BoxLayoutProvider/Group",167),m(326,23,{3:1,35:1,23:1,326:1},E$);var m8e,v8e,y8e,Mce,k8e=mt(Pu,"BoxLayoutProvider/PackingMode",326,At,oyn,a3n),can;m(966,1,Ut,qT),s.Le=function(n,t){return M5n(u(n,167),u(t,167))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Pu,"BoxLayoutProvider/lambda$0$Type",966),m(967,1,Ut,Ck),s.Le=function(n,t){return w5n(u(n,167),u(t,167))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Pu,"BoxLayoutProvider/lambda$1$Type",967),m(968,1,Ut,UT),s.Le=function(n,t){return p5n(u(n,167),u(t,167))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(Pu,"BoxLayoutProvider/lambda$2$Type",968),m(1338,1,{829:1},aL),s.Lg=function(n,t){return YP(),!q(t,174)||jMe((N4(),u(n,174)),t)},v(Pu,"ElkSpacings/AbstractSpacingsBuilder/lambda$0$Type",1338),m(1339,1,it,qje),s.Ad=function(n){vkn(this.a,u(n,147))},v(Pu,"ElkSpacings/AbstractSpacingsBuilder/lambda$1$Type",1339),m(1340,1,it,XT),s.Ad=function(n){u(n,105),YP()},v(Pu,"ElkSpacings/AbstractSpacingsBuilder/lambda$2$Type",1340),m(1344,1,it,Uje),s.Ad=function(n){q8n(this.a,u(n,105))},v(Pu,"ElkSpacings/AbstractSpacingsBuilder/lambda$3$Type",1344),m(1342,1,Pt,mxe),s.Mb=function(n){return nkn(this.a,this.b,u(n,147))},v(Pu,"ElkSpacings/AbstractSpacingsBuilder/lambda$4$Type",1342),m(1341,1,Pt,vxe),s.Mb=function(n){return g2n(this.a,this.b,u(n,829))},v(Pu,"ElkSpacings/AbstractSpacingsBuilder/lambda$5$Type",1341),m(1343,1,it,yxe),s.Ad=function(n){bvn(this.a,this.b,u(n,147))},v(Pu,"ElkSpacings/AbstractSpacingsBuilder/lambda$6$Type",1343),m(930,1,{},hL),s.Kb=function(n){return gCe(n)},s.Fb=function(n){return this===n},v(Pu,"ElkUtil/lambda$0$Type",930),m(931,1,it,kxe),s.Ad=function(n){yCn(this.a,this.b,u(n,85))},s.a=0,s.b=0,v(Pu,"ElkUtil/lambda$1$Type",931),m(932,1,it,Exe),s.Ad=function(n){wbn(this.a,this.b,u(n,170))},s.a=0,s.b=0,v(Pu,"ElkUtil/lambda$2$Type",932),m(933,1,it,jxe),s.Ad=function(n){fwn(this.a,this.b,u(n,157))},s.a=0,s.b=0,v(Pu,"ElkUtil/lambda$3$Type",933),m(934,1,it,Xje),s.Ad=function(n){$3n(this.a,u(n,372))},v(Pu,"ElkUtil/lambda$4$Type",934),m(331,1,{35:1,331:1},q0n),s.Dd=function(n){return Lwn(this,u(n,242))},s.Fb=function(n){var t;return q(n,331)?(t=u(n,331),this.a==t.a):!1},s.Hb=function(){return sc(this.a)},s.Ib=function(){return this.a+" (exclusive)"},s.a=0,v(Pu,"ExclusiveBounds/ExclusiveLowerBound",331),m(1088,214,$w,Gg),s.kf=function(n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G,K,ee,he,se,Ne,nn,en;for(t.Tg("Fixed Layout",1),o=u(ve(n,(Ht(),d9e)),222),y=0,S=0,K=new ct((!n.a&&(n.a=new we($t,n,10,11)),n.a));K.e!=K.i.gc();){for(R=u(lt(K),26),en=u(ve(R,(LB(),zA)),8),en&&(Cl(R,en.a,en.b),u(ve(R,n8e),182).Gc((Xs(),km))&&(M=u(ve(R,i8e),8),M.a>0&&M.b>0&&Iw(R,M.a,M.b,!0,!0))),y=E.Math.max(y,R.i+R.g),S=E.Math.max(S,R.j+R.f),b=new ct((!R.n&&(R.n=new we(ku,R,1,7)),R.n));b.e!=b.i.gc();)f=u(lt(b),157),en=u(ve(f,zA),8),en&&Cl(f,en.a,en.b),y=E.Math.max(y,R.i+f.i+f.g),S=E.Math.max(S,R.j+f.j+f.f);for(se=new ct((!R.c&&(R.c=new we(Ps,R,9,9)),R.c));se.e!=se.i.gc();)for(he=u(lt(se),125),en=u(ve(he,zA),8),en&&Cl(he,en.a,en.b),Ne=R.i+he.i,nn=R.j+he.j,y=E.Math.max(y,Ne+he.g),S=E.Math.max(S,nn+he.f),h=new ct((!he.n&&(he.n=new we(ku,he,1,7)),he.n));h.e!=h.i.gc();)f=u(lt(h),157),en=u(ve(f,zA),8),en&&Cl(f,en.a,en.b),y=E.Math.max(y,Ne+f.i+f.g),S=E.Math.max(S,nn+f.j+f.f);for(c=new qn(Vn(L0(R).a.Jc(),new ne));at(c);)i=u(tt(c),85),p=SVe(i),y=E.Math.max(y,p.a),S=E.Math.max(S,p.b);for(r=new qn(Vn(kZ(R).a.Jc(),new ne));at(r);)i=u(tt(r),85),zi(oZ(i))!=n&&(p=SVe(i),y=E.Math.max(y,p.a),S=E.Math.max(S,p.b))}if(o==(L1(),C7))for(G=new ct((!n.a&&(n.a=new we($t,n,10,11)),n.a));G.e!=G.i.gc();)for(R=u(lt(G),26),r=new qn(Vn(L0(R).a.Jc(),new ne));at(r);)i=u(tt(r),85),l=w_n(i),l.b==0?yi(i,$v,null):yi(i,$v,l);$e(Pe(ve(n,(LB(),t8e))))||(ee=u(ve(n,Ffn),104),I=y+ee.b+ee.c,O=S+ee.d+ee.a,Iw(n,I,O,!0,!0)),t.Ug()},v(Pu,"FixedLayoutProvider",1088),m(379,150,{3:1,414:1,379:1,105:1,150:1},Sy,hRe),s.ag=function(n){var t,i,r,c,o,l,f,h,b;if(n)try{for(h=_p(n,";,;"),o=h,l=0,f=o.length;l>16&yr|t^r<<16},s.Jc=function(){return new Kje(this)},s.Ib=function(){return this.a==null&&this.b==null?"pair(null,null)":this.a==null?"pair(null,"+su(this.b)+")":this.b==null?"pair("+su(this.a)+",null)":"pair("+su(this.a)+","+su(this.b)+")"},v(Pu,"Pair",49),m(979,1,zr,Kje),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return!this.c&&(!this.b&&this.a.a!=null||this.a.b!=null)},s.Pb=function(){if(!this.c&&!this.b&&this.a.a!=null)return this.b=!0,this.a.a;if(!this.c&&this.a.b!=null)return this.c=!0,this.a.b;throw P(new fu)},s.Qb=function(){throw this.c&&this.a.b!=null?this.a.b=null:this.b&&this.a.a!=null&&(this.a.a=null),P(new ts)},s.b=!1,s.c=!1,v(Pu,"Pair/1",979),m(1078,214,$w,KT),s.kf=function(n,t){var i,r,c,o,l;if(t.Tg("Random Layout",1),(!n.a&&(n.a=new we($t,n,10,11)),n.a).i==0){t.Ug();return}o=u(ve(n,(cde(),Zfn)),15),o&&o.a!=0?c=new qR(o.a):c=new bQ,i=Xx(ie(ve(n,Yfn))),l=Xx(ie(ve(n,Wfn))),r=u(ve(n,Qfn),104),$$n(n,c,i,l,r),t.Ug()},v(Pu,"RandomLayoutProvider",1078),m(240,1,{240:1},KK),s.Fb=function(n){return Uu(this.a,u(n,240).a)&&Uu(this.b,u(n,240).b)&&Uu(this.c,u(n,240).c)},s.Hb=function(){return PB(z(B(Mr,1),xn,1,5,[this.a,this.b,this.c]))},s.Ib=function(){return"("+this.a+xo+this.b+xo+this.c+")"},v(Pu,"Triple",240);var lan;m(550,1,{}),s.Jf=function(){return new ke(this.f.i,this.f.j)},s.mf=function(n){return a_e(n,(Ht(),Ls))?ve(this.f,fan):ve(this.f,n)},s.Kf=function(){return new ke(this.f.g,this.f.f)},s.Lf=function(){return this.g},s.nf=function(n){return ua(this.f,n)},s.Mf=function(n){Cs(this.f,n.a),Os(this.f,n.b)},s.Nf=function(n){vw(this.f,n.a),mw(this.f,n.b)},s.Of=function(n){this.g=n},s.g=0;var fan;v(jS,"ElkGraphAdapters/AbstractElkGraphElementAdapter",550),m(552,1,{837:1},TP),s.Pf=function(){var n,t;if(!this.b)for(this.b=RR(AV(this.a).i),t=new ct(AV(this.a));t.e!=t.i.gc();)n=u(lt(t),157),Te(this.b,new kX(n));return this.b},s.b=null,v(jS,"ElkGraphAdapters/ElkEdgeAdapter",552),m(260,550,{},s0),s.Qf=function(){return lJe(this)},s.a=null,v(jS,"ElkGraphAdapters/ElkGraphAdapter",260),m(630,550,{187:1},kX),v(jS,"ElkGraphAdapters/ElkLabelAdapter",630),m(551,550,{685:1},F$),s.Pf=function(){return GSn(this)},s.Tf=function(){var n;return n=u(ve(this.f,(Ht(),S7)),140),!n&&(n=new uE),n},s.Vf=function(){return qSn(this)},s.Xf=function(n){var t;t=new qK(n),yi(this.f,(Ht(),S7),t)},s.Yf=function(n){yi(this.f,(Ht(),t1),new Fle(n))},s.Rf=function(){return this.d},s.Sf=function(){var n,t;if(!this.a)for(this.a=new xe,t=new qn(Vn(kZ(u(this.f,26)).a.Jc(),new ne));at(t);)n=u(tt(t),85),Te(this.a,new TP(n));return this.a},s.Uf=function(){var n,t;if(!this.c)for(this.c=new xe,t=new qn(Vn(L0(u(this.f,26)).a.Jc(),new ne));at(t);)n=u(tt(t),85),Te(this.c,new TP(n));return this.c},s.Wf=function(){return MR(u(this.f,26)).i!=0||$e(Pe(u(this.f,26).mf((Ht(),ND))))},s.Zf=function(){H9n(this,(Mb(),lan))},s.a=null,s.b=null,s.c=null,s.d=null,s.e=null,v(jS,"ElkGraphAdapters/ElkNodeAdapter",551),m(1249,550,{836:1},Vje),s.Pf=function(){return ZSn(this)},s.Sf=function(){var n,t;if(!this.a)for(this.a=Ph(u(this.f,125).gh().i),t=new ct(u(this.f,125).gh());t.e!=t.i.gc();)n=u(lt(t),85),Te(this.a,new TP(n));return this.a},s.Uf=function(){var n,t;if(!this.c)for(this.c=Ph(u(this.f,125).hh().i),t=new ct(u(this.f,125).hh());t.e!=t.i.gc();)n=u(lt(t),85),Te(this.c,new TP(n));return this.c},s.$f=function(){return u(u(this.f,125).mf((Ht(),zv)),64)},s._f=function(){var n,t,i,r,c,o,l,f;for(r=Ma(u(this.f,125)),i=new ct(u(this.f,125).hh());i.e!=i.i.gc();)for(n=u(lt(i),85),f=new ct((!n.c&&(n.c=new Cn(pt,n,5,8)),n.c));f.e!=f.i.gc();){if(l=u(lt(f),84),gp(iu(l),r))return!0;if(iu(l)==r&&$e(Pe(ve(n,(Ht(),fce)))))return!0}for(t=new ct(u(this.f,125).gh());t.e!=t.i.gc();)for(n=u(lt(t),85),o=new ct((!n.b&&(n.b=new Cn(pt,n,4,7)),n.b));o.e!=o.i.gc();)if(c=u(lt(o),84),gp(iu(c),r))return!0;return!1},s.a=null,s.b=null,s.c=null,v(jS,"ElkGraphAdapters/ElkPortAdapter",1249),m(1250,1,Ut,bL),s.Le=function(n,t){return sIn(u(n,125),u(t,125))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(jS,"ElkGraphAdapters/PortComparator",1250);var sb=Ji(Hl,"EObject"),D7=Ji(lv,DZe),pl=Ji(lv,IZe),XD=Ji(lv,_Ze),KD=Ji(lv,"ElkShape"),pt=Ji(lv,LZe),wr=Ji(lv,Tpe),Pi=Ji(lv,PZe),VD=Ji(Hl,$Ze),KA=Ji(Hl,"EFactory"),aan,xce=Ji(Hl,RZe),ma=Ji(Hl,"EPackage"),Lr,han,dan,A8e,hG,ban,M8e,T8e,x8e,u1,gan,wan,ku=Ji(lv,xpe),$t=Ji(lv,Cpe),Ps=Ji(lv,Ope);m(93,1,BZe),s.qh=function(){return this.rh(),null},s.rh=function(){return null},s.sh=function(){return this.rh(),!1},s.th=function(){return!1},s.uh=function(n){fi(this,n)},v(c6,"BasicNotifierImpl",93),m(100,93,JZe),s.Vh=function(){return Bs(this)},s.vh=function(n,t){return n},s.wh=function(){throw P(new Ot)},s.xh=function(n){var t;return t=xc(u(Sn(this.Ah(),this.Ch()),19)),this.Mh().Qh(this,t.n,t.f,n)},s.yh=function(n,t){throw P(new Ot)},s.zh=function(n,t,i){return ll(this,n,t,i)},s.Ah=function(){var n;return this.wh()&&(n=this.wh().Lk(),n)?n:this.fi()},s.Bh=function(){return vZ(this)},s.Ch=function(){throw P(new Ot)},s.Dh=function(){var n,t;return t=this.Xh().Mk(),!t&&this.wh().Rk(t=(kE(),n=rae(gh(this.Ah())),n==null?Pce:new yC(this,n))),t},s.Eh=function(n,t){return n},s.Fh=function(n){var t;return t=n.nk(),t?n.Jj():Fi(this.Ah(),n)},s.Gh=function(){var n;return n=this.wh(),n?n.Ok():null},s.Hh=function(){return this.wh()?this.wh().Lk():null},s.Ih=function(n,t,i){return rz(this,n,t,i)},s.Jh=function(n){return T9(this,n)},s.Kh=function(n,t){return cY(this,n,t)},s.Lh=function(){var n;return n=this.wh(),!!n&&n.Pk()},s.Mh=function(){throw P(new Ot)},s.Nh=function(){return WB(this)},s.Oh=function(n,t,i,r){return $4(this,n,t,r)},s.Ph=function(n,t,i){var r;return r=u(Sn(this.Ah(),t),69),r.uk().xk(this,this.ei(),t-this.gi(),n,i)},s.Qh=function(n,t,i,r){return NR(this,n,t,r)},s.Rh=function(n,t,i){var r;return r=u(Sn(this.Ah(),t),69),r.uk().yk(this,this.ei(),t-this.gi(),n,i)},s.Sh=function(){return!!this.wh()&&!!this.wh().Nk()},s.Th=function(n){return CQ(this,n)},s.Uh=function(n){return M_e(this,n)},s.Wh=function(n){return oVe(this,n)},s.Xh=function(){throw P(new Ot)},s.Yh=function(){return this.wh()?this.wh().Nk():null},s.Zh=function(){return WB(this)},s.$h=function(n,t){bZ(this,n,t)},s._h=function(n){this.Xh().Qk(n)},s.ai=function(n){this.Xh().Tk(n)},s.bi=function(n){this.Xh().Sk(n)},s.ci=function(n,t){var i,r,c,o;return o=this.Gh(),o&&n&&(t=mc(o.Cl(),this,t),o.Gl(this)),r=this.Mh(),r&&((DZ(this,this.Mh(),this.Ch()).Bb&kc)!=0?(c=r.Nh(),c&&(n?!o&&c.Gl(this):c.Fl(this))):(t=(i=this.Ch(),i>=0?this.xh(t):this.Mh().Qh(this,-1-i,null,t)),t=this.zh(null,-1,t))),this.ai(n),t},s.di=function(n){var t,i,r,c,o,l,f,h;if(i=this.Ah(),o=Fi(i,n),t=this.gi(),o>=t)return u(n,69).uk().Bk(this,this.ei(),o-t);if(o<=-1)if(l=ev((ss(),ec),i,n),l){if(Tc(),u(l,69).vk()||(l=k4(Kc(ec,l))),c=(r=this.Fh(l),u(r>=0?this.Ih(r,!0,!0):Ow(this,l,!0),163)),h=l.Gk(),h>1||h==-1)return u(u(c,219).Ql(n,!1),77)}else throw P(new Gn(G0+n.ve()+ane));else if(n.Hk())return r=this.Fh(n),u(r>=0?this.Ih(r,!1,!0):Ow(this,n,!1),77);return f=new Bxe(this,n),f},s.ei=function(){return hhe(this)},s.fi=function(){return(p0(),Rn).S},s.gi=function(){return ht(this.fi())},s.hi=function(n){aZ(this,n)},s.Ib=function(){return Lf(this)},v(Fn,"BasicEObjectImpl",100);var pan;m(117,100,{109:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1}),s.ii=function(n){var t;return t=dhe(this),t[n]},s.ji=function(n,t){var i;i=dhe(this),tr(i,n,t)},s.ki=function(n){var t;t=dhe(this),tr(t,n,null)},s.qh=function(){return u(Un(this,4),129)},s.rh=function(){throw P(new Ot)},s.sh=function(){return(this.Db&4)!=0},s.wh=function(){throw P(new Ot)},s.li=function(n){L4(this,2,n)},s.yh=function(n,t){this.Db=t<<16|this.Db&255,this.li(n)},s.Ah=function(){return Jo(this)},s.Ch=function(){return this.Db>>16},s.Dh=function(){var n,t;return kE(),t=rae(gh((n=u(Un(this,16),29),n||this.fi()))),t==null?Pce:new yC(this,t)},s.th=function(){return(this.Db&1)==0},s.Gh=function(){return u(Un(this,128),1996)},s.Hh=function(){return u(Un(this,16),29)},s.Lh=function(){return(this.Db&32)!=0},s.Mh=function(){return u(Un(this,2),52)},s.Sh=function(){return(this.Db&64)!=0},s.Xh=function(){throw P(new Ot)},s.Yh=function(){return u(Un(this,64),290)},s._h=function(n){L4(this,16,n)},s.ai=function(n){L4(this,128,n)},s.bi=function(n){L4(this,64,n)},s.ei=function(){return _o(this)},s.Db=0,v(Fn,"MinimalEObjectImpl",117),m(118,117,{109:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.li=function(n){this.Cb=n},s.Mh=function(){return this.Cb},v(Fn,"MinimalEObjectImpl/Container",118),m(2045,118,{109:1,343:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return xde(this,n,t,i)},s.Rh=function(n,t,i){return p0e(this,n,t,i)},s.Th=function(n){return kae(this,n)},s.$h=function(n,t){b1e(this,n,t)},s.fi=function(){return Hu(),wan},s.hi=function(n){n1e(this,n)},s.lf=function(){return CHe(this)},s.fh=function(){return!this.o&&(this.o=new us((Hu(),u1),qd,this,0)),this.o},s.mf=function(n){return ve(this,n)},s.nf=function(n){return ua(this,n)},s.of=function(n,t){return yi(this,n,t)},v(rg,"EMapPropertyHolderImpl",2045),m(559,118,{109:1,372:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},Nk),s.Ih=function(n,t,i){switch(n){case 0:return this.a;case 1:return this.b}return rz(this,n,t,i)},s.Th=function(n){switch(n){case 0:return this.a!=0;case 1:return this.b!=0}return CQ(this,n)},s.$h=function(n,t){switch(n){case 0:hB(this,W(ie(t)));return;case 1:dB(this,W(ie(t)));return}bZ(this,n,t)},s.fi=function(){return Hu(),han},s.hi=function(n){switch(n){case 0:hB(this,0);return;case 1:dB(this,0);return}aZ(this,n)},s.Ib=function(){var n;return(this.Db&64)!=0?Lf(this):(n=new nf(Lf(this)),n.a+=" (x: ",d3(n,this.a),n.a+=", y: ",d3(n,this.b),n.a+=")",n.a)},s.a=0,s.b=0,v(rg,"ElkBendPointImpl",559),m(727,2045,{109:1,343:1,174:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return I1e(this,n,t,i)},s.Ph=function(n,t,i){return iZ(this,n,t,i)},s.Rh=function(n,t,i){return HY(this,n,t,i)},s.Th=function(n){return Vhe(this,n)},s.$h=function(n,t){Xde(this,n,t)},s.fi=function(){return Hu(),ban},s.hi=function(n){C1e(this,n)},s.ih=function(){return this.k},s.jh=function(){return AV(this)},s.Ib=function(){return dQ(this)},s.k=null,v(rg,"ElkGraphElementImpl",727),m(728,727,{109:1,343:1,174:1,276:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return U1e(this,n,t,i)},s.Th=function(n){return W1e(this,n)},s.$h=function(n,t){Kde(this,n,t)},s.fi=function(){return Hu(),gan},s.hi=function(n){rde(this,n)},s.kh=function(){return this.f},s.lh=function(){return this.g},s.mh=function(){return this.i},s.nh=function(){return this.j},s.oh=function(n,t){tw(this,n,t)},s.ph=function(n,t){Cl(this,n,t)},s.Ib=function(){return lZ(this)},s.f=0,s.g=0,s.i=0,s.j=0,v(rg,"ElkShapeImpl",728),m(729,728,{109:1,343:1,84:1,174:1,276:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return Ede(this,n,t,i)},s.Ph=function(n,t,i){return Fde(this,n,t,i)},s.Rh=function(n,t,i){return Hde(this,n,t,i)},s.Th=function(n){return f1e(this,n)},s.$h=function(n,t){nbe(this,n,t)},s.fi=function(){return Hu(),dan},s.hi=function(n){pde(this,n)},s.gh=function(){return!this.d&&(this.d=new Cn(wr,this,8,5)),this.d},s.hh=function(){return!this.e&&(this.e=new Cn(wr,this,7,4)),this.e},v(rg,"ElkConnectableShapeImpl",729),m(271,727,{109:1,343:1,85:1,174:1,271:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},c3),s.xh=function(n){return $de(this,n)},s.Ih=function(n,t,i){switch(n){case 3:return sp(this);case 4:return!this.b&&(this.b=new Cn(pt,this,4,7)),this.b;case 5:return!this.c&&(this.c=new Cn(pt,this,5,8)),this.c;case 6:return!this.a&&(this.a=new we(Pi,this,6,6)),this.a;case 7:return Pn(),!this.b&&(this.b=new Cn(pt,this,4,7)),!(this.b.i<=1&&(!this.c&&(this.c=new Cn(pt,this,5,8)),this.c.i<=1));case 8:return Pn(),!!Jj(this);case 9:return Pn(),!!Cw(this);case 10:return Pn(),!this.b&&(this.b=new Cn(pt,this,4,7)),this.b.i!=0&&(!this.c&&(this.c=new Cn(pt,this,5,8)),this.c.i!=0)}return I1e(this,n,t,i)},s.Ph=function(n,t,i){var r;switch(t){case 3:return this.Cb&&(i=(r=this.Db>>16,r>=0?$de(this,i):this.Cb.Qh(this,-1-r,null,i))),vle(this,u(n,26),i);case 4:return!this.b&&(this.b=new Cn(pt,this,4,7)),To(this.b,n,i);case 5:return!this.c&&(this.c=new Cn(pt,this,5,8)),To(this.c,n,i);case 6:return!this.a&&(this.a=new we(Pi,this,6,6)),To(this.a,n,i)}return iZ(this,n,t,i)},s.Rh=function(n,t,i){switch(t){case 3:return vle(this,null,i);case 4:return!this.b&&(this.b=new Cn(pt,this,4,7)),mc(this.b,n,i);case 5:return!this.c&&(this.c=new Cn(pt,this,5,8)),mc(this.c,n,i);case 6:return!this.a&&(this.a=new we(Pi,this,6,6)),mc(this.a,n,i)}return HY(this,n,t,i)},s.Th=function(n){switch(n){case 3:return!!sp(this);case 4:return!!this.b&&this.b.i!=0;case 5:return!!this.c&&this.c.i!=0;case 6:return!!this.a&&this.a.i!=0;case 7:return!this.b&&(this.b=new Cn(pt,this,4,7)),!(this.b.i<=1&&(!this.c&&(this.c=new Cn(pt,this,5,8)),this.c.i<=1));case 8:return Jj(this);case 9:return Cw(this);case 10:return!this.b&&(this.b=new Cn(pt,this,4,7)),this.b.i!=0&&(!this.c&&(this.c=new Cn(pt,this,5,8)),this.c.i!=0)}return Vhe(this,n)},s.$h=function(n,t){switch(n){case 3:Oz(this,u(t,26));return;case 4:!this.b&&(this.b=new Cn(pt,this,4,7)),vt(this.b),!this.b&&(this.b=new Cn(pt,this,4,7)),er(this.b,u(t,18));return;case 5:!this.c&&(this.c=new Cn(pt,this,5,8)),vt(this.c),!this.c&&(this.c=new Cn(pt,this,5,8)),er(this.c,u(t,18));return;case 6:!this.a&&(this.a=new we(Pi,this,6,6)),vt(this.a),!this.a&&(this.a=new we(Pi,this,6,6)),er(this.a,u(t,18));return}Xde(this,n,t)},s.fi=function(){return Hu(),A8e},s.hi=function(n){switch(n){case 3:Oz(this,null);return;case 4:!this.b&&(this.b=new Cn(pt,this,4,7)),vt(this.b);return;case 5:!this.c&&(this.c=new Cn(pt,this,5,8)),vt(this.c);return;case 6:!this.a&&(this.a=new we(Pi,this,6,6)),vt(this.a);return}C1e(this,n)},s.Ib=function(){return xKe(this)},v(rg,"ElkEdgeImpl",271),m(443,2045,{109:1,343:1,170:1,443:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},vo),s.xh=function(n){return Ide(this,n)},s.Ih=function(n,t,i){switch(n){case 1:return this.j;case 2:return this.k;case 3:return this.b;case 4:return this.c;case 5:return!this.a&&(this.a=new mr(pl,this,5)),this.a;case 6:return A_e(this);case 7:return t?_Q(this):this.i;case 8:return t?IQ(this):this.f;case 9:return!this.g&&(this.g=new Cn(Pi,this,9,10)),this.g;case 10:return!this.e&&(this.e=new Cn(Pi,this,10,9)),this.e;case 11:return this.d}return xde(this,n,t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 6:return this.Cb&&(i=(c=this.Db>>16,c>=0?Ide(this,i):this.Cb.Qh(this,-1-c,null,i))),yle(this,u(n,85),i);case 9:return!this.g&&(this.g=new Cn(Pi,this,9,10)),To(this.g,n,i);case 10:return!this.e&&(this.e=new Cn(Pi,this,10,9)),To(this.e,n,i)}return o=u(Sn((r=u(Un(this,16),29),r||(Hu(),hG)),t),69),o.uk().xk(this,_o(this),t-ht((Hu(),hG)),n,i)},s.Rh=function(n,t,i){switch(t){case 5:return!this.a&&(this.a=new mr(pl,this,5)),mc(this.a,n,i);case 6:return yle(this,null,i);case 9:return!this.g&&(this.g=new Cn(Pi,this,9,10)),mc(this.g,n,i);case 10:return!this.e&&(this.e=new Cn(Pi,this,10,9)),mc(this.e,n,i)}return p0e(this,n,t,i)},s.Th=function(n){switch(n){case 1:return this.j!=0;case 2:return this.k!=0;case 3:return this.b!=0;case 4:return this.c!=0;case 5:return!!this.a&&this.a.i!=0;case 6:return!!A_e(this);case 7:return!!this.i;case 8:return!!this.f;case 9:return!!this.g&&this.g.i!=0;case 10:return!!this.e&&this.e.i!=0;case 11:return this.d!=null}return kae(this,n)},s.$h=function(n,t){switch(n){case 1:R3(this,W(ie(t)));return;case 2:B3(this,W(ie(t)));return;case 3:P3(this,W(ie(t)));return;case 4:$3(this,W(ie(t)));return;case 5:!this.a&&(this.a=new mr(pl,this,5)),vt(this.a),!this.a&&(this.a=new mr(pl,this,5)),er(this.a,u(t,18));return;case 6:TUe(this,u(t,85));return;case 7:yB(this,u(t,84));return;case 8:vB(this,u(t,84));return;case 9:!this.g&&(this.g=new Cn(Pi,this,9,10)),vt(this.g),!this.g&&(this.g=new Cn(Pi,this,9,10)),er(this.g,u(t,18));return;case 10:!this.e&&(this.e=new Cn(Pi,this,10,9)),vt(this.e),!this.e&&(this.e=new Cn(Pi,this,10,9)),er(this.e,u(t,18));return;case 11:Rhe(this,Dt(t));return}b1e(this,n,t)},s.fi=function(){return Hu(),hG},s.hi=function(n){switch(n){case 1:R3(this,0);return;case 2:B3(this,0);return;case 3:P3(this,0);return;case 4:$3(this,0);return;case 5:!this.a&&(this.a=new mr(pl,this,5)),vt(this.a);return;case 6:TUe(this,null);return;case 7:yB(this,null);return;case 8:vB(this,null);return;case 9:!this.g&&(this.g=new Cn(Pi,this,9,10)),vt(this.g);return;case 10:!this.e&&(this.e=new Cn(Pi,this,10,9)),vt(this.e);return;case 11:Rhe(this,null);return}n1e(this,n)},s.Ib=function(){return Rqe(this)},s.b=0,s.c=0,s.d=null,s.j=0,s.k=0,v(rg,"ElkEdgeSectionImpl",443),m(161,118,{109:1,94:1,93:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1}),s.Ih=function(n,t,i){var r;return n==0?(!this.Ab&&(this.Ab=new we(Yt,this,0,3)),this.Ab):Il(this,n-ht(this.fi()),Sn((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Ph=function(n,t,i){var r,c;return t==0?(!this.Ab&&(this.Ab=new we(Yt,this,0,3)),To(this.Ab,n,i)):(c=u(Sn((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().xk(this,_o(this),t-ht(this.fi()),n,i))},s.Rh=function(n,t,i){var r,c;return t==0?(!this.Ab&&(this.Ab=new we(Yt,this,0,3)),mc(this.Ab,n,i)):(c=u(Sn((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,_o(this),t-ht(this.fi()),n,i))},s.Th=function(n){var t;return n==0?!!this.Ab&&this.Ab.i!=0:Dl(this,n-ht(this.fi()),Sn((t=u(Un(this,16),29),t||this.fi()),n))},s.Wh=function(n){return vge(this,n)},s.$h=function(n,t){var i;if(n===0){!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return}Bl(this,n-ht(this.fi()),Sn((i=u(Un(this,16),29),i||this.fi()),n),t)},s.ai=function(n){L4(this,128,n)},s.fi=function(){return mn(),Lan},s.hi=function(n){var t;if(n===0){!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return}Rl(this,n-ht(this.fi()),Sn((t=u(Un(this,16),29),t||this.fi()),n))},s.mi=function(){this.Bb|=1},s.ni=function(n){return Yj(this,n)},s.Bb=0,v(Fn,"EModelElementImpl",161),m(710,161,{109:1,94:1,93:1,469:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1},xx),s.oi=function(n,t){return eVe(this,n,t)},s.pi=function(n){var t,i,r,c,o;if(this.a!=rl(n)||(n.Bb&256)!=0)throw P(new Gn(dne+n.zb+Jw));for(r=tu(n);Xu(r.a).i!=0;){if(i=u(iN(r,0,(t=u(X(Xu(r.a),0),87),o=t.c,q(o,88)?u(o,29):(mn(),vf))),29),Tw(i))return c=rl(i).ti().pi(i),u(c,52)._h(n),c;r=tu(i)}return(n.D!=null?n.D:n.B)=="java.util.Map$Entry"?new cDe(n):new cfe(n)},s.qi=function(n,t){return _w(this,n,t)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),this.Ab;case 1:return this.a}return Il(this,n-ht((mn(),ab)),Sn((r=u(Un(this,16),29),r||ab),n),t,i)},s.Ph=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),To(this.Ab,n,i);case 1:return this.a&&(i=u(this.a,52).Qh(this,4,ma,i)),T1e(this,u(n,241),i)}return c=u(Sn((r=u(Un(this,16),29),r||(mn(),ab)),t),69),c.uk().xk(this,_o(this),t-ht((mn(),ab)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),mc(this.Ab,n,i);case 1:return T1e(this,null,i)}return c=u(Sn((r=u(Un(this,16),29),r||(mn(),ab)),t),69),c.uk().yk(this,_o(this),t-ht((mn(),ab)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return!!this.a}return Dl(this,n-ht((mn(),ab)),Sn((t=u(Un(this,16),29),t||ab),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return;case 1:bGe(this,u(t,241));return}Bl(this,n-ht((mn(),ab)),Sn((i=u(Un(this,16),29),i||ab),n),t)},s.fi=function(){return mn(),ab},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return;case 1:bGe(this,null);return}Rl(this,n-ht((mn(),ab)),Sn((t=u(Un(this,16),29),t||ab),n))};var VA,C8e,man;v(Fn,"EFactoryImpl",710),m(1018,710,{109:1,2075:1,94:1,93:1,469:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1},uU),s.oi=function(n,t){switch(n.fk()){case 12:return u(t,147).Og();case 13:return su(t);default:throw P(new Gn(G8+n.ve()+Jw))}},s.pi=function(n){var t,i,r,c,o,l,f,h;switch(n.G==-1&&(n.G=(t=rl(n),t?Cd(t.si(),n):-1)),n.G){case 4:return o=new VT,o;case 6:return l=new oE,l;case 7:return f=new foe,f;case 8:return r=new c3,r;case 9:return i=new Nk,i;case 10:return c=new vo,c;case 11:return h=new Ay,h;default:throw P(new Gn(dne+n.zb+Jw))}},s.qi=function(n,t){switch(n.fk()){case 13:case 12:return null;default:throw P(new Gn(G8+n.ve()+Jw))}},v(rg,"ElkGraphFactoryImpl",1018),m(439,161,{109:1,94:1,93:1,158:1,197:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1}),s.Dh=function(){var n,t;return t=(n=u(Un(this,16),29),rae(gh(n||this.fi()))),t==null?(kE(),kE(),Pce):new MOe(this,t)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),this.Ab;case 1:return this.ve()}return Il(this,n-ht(this.fi()),Sn((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null}return Dl(this,n-ht(this.fi()),Sn((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return;case 1:this.ri(Dt(t));return}Bl(this,n-ht(this.fi()),Sn((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return mn(),Pan},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return;case 1:this.ri(null);return}Rl(this,n-ht(this.fi()),Sn((t=u(Un(this,16),29),t||this.fi()),n))},s.ve=function(){return this.zb},s.ri=function(n){Mo(this,n)},s.Ib=function(){return Sj(this)},s.zb=null,v(Fn,"ENamedElementImpl",439),m(184,439,{109:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,52:1,100:1,161:1,184:1,117:1,118:1,680:1},YIe),s.xh=function(n){return AJe(this,n)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return this.yb;case 3:return this.xb;case 4:return this.sb;case 5:return!this.rb&&(this.rb=new rp(this,va,this)),this.rb;case 6:return!this.vb&&(this.vb=new l4(ma,this,6,7)),this.vb;case 7:return t?this.Db>>16==7?u(this.Cb,241):null:D_e(this)}return Il(this,n-ht((mn(),Vd)),Sn((r=u(Un(this,16),29),r||Vd),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),To(this.Ab,n,i);case 4:return this.sb&&(i=u(this.sb,52).Qh(this,1,KA,i)),O1e(this,u(n,469),i);case 5:return!this.rb&&(this.rb=new rp(this,va,this)),To(this.rb,n,i);case 6:return!this.vb&&(this.vb=new l4(ma,this,6,7)),To(this.vb,n,i);case 7:return this.Cb&&(i=(c=this.Db>>16,c>=0?AJe(this,i):this.Cb.Qh(this,-1-c,null,i))),ll(this,n,7,i)}return o=u(Sn((r=u(Un(this,16),29),r||(mn(),Vd)),t),69),o.uk().xk(this,_o(this),t-ht((mn(),Vd)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),mc(this.Ab,n,i);case 4:return O1e(this,null,i);case 5:return!this.rb&&(this.rb=new rp(this,va,this)),mc(this.rb,n,i);case 6:return!this.vb&&(this.vb=new l4(ma,this,6,7)),mc(this.vb,n,i);case 7:return ll(this,null,7,i)}return c=u(Sn((r=u(Un(this,16),29),r||(mn(),Vd)),t),69),c.uk().yk(this,_o(this),t-ht((mn(),Vd)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.yb!=null;case 3:return this.xb!=null;case 4:return!!this.sb;case 5:return!!this.rb&&this.rb.i!=0;case 6:return!!this.vb&&this.vb.i!=0;case 7:return!!D_e(this)}return Dl(this,n-ht((mn(),Vd)),Sn((t=u(Un(this,16),29),t||Vd),n))},s.Wh=function(n){var t;return t=MNn(this,n),t||vge(this,n)},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Dt(t));return;case 2:MB(this,Dt(t));return;case 3:AB(this,Dt(t));return;case 4:sZ(this,u(t,469));return;case 5:!this.rb&&(this.rb=new rp(this,va,this)),vt(this.rb),!this.rb&&(this.rb=new rp(this,va,this)),er(this.rb,u(t,18));return;case 6:!this.vb&&(this.vb=new l4(ma,this,6,7)),vt(this.vb),!this.vb&&(this.vb=new l4(ma,this,6,7)),er(this.vb,u(t,18));return}Bl(this,n-ht((mn(),Vd)),Sn((i=u(Un(this,16),29),i||Vd),n),t)},s.bi=function(n){var t,i;if(n&&this.rb)for(i=new ct(this.rb);i.e!=i.i.gc();)t=lt(i),q(t,360)&&(u(t,360).w=null);L4(this,64,n)},s.fi=function(){return mn(),Vd},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return;case 1:Mo(this,null);return;case 2:MB(this,null);return;case 3:AB(this,null);return;case 4:sZ(this,null);return;case 5:!this.rb&&(this.rb=new rp(this,va,this)),vt(this.rb);return;case 6:!this.vb&&(this.vb=new l4(ma,this,6,7)),vt(this.vb);return}Rl(this,n-ht((mn(),Vd)),Sn((t=u(Un(this,16),29),t||Vd),n))},s.mi=function(){XQ(this)},s.si=function(){return!this.rb&&(this.rb=new rp(this,va,this)),this.rb},s.ti=function(){return this.sb},s.ui=function(){return this.ub},s.vi=function(){return this.xb},s.wi=function(){return this.yb},s.xi=function(n){this.ub=n},s.Ib=function(){var n;return(this.Db&64)!=0?Sj(this):(n=new nf(Sj(this)),n.a+=" (nsURI: ",$c(n,this.yb),n.a+=", nsPrefix: ",$c(n,this.xb),n.a+=")",n.a)},s.xb=null,s.yb=null,v(Fn,"EPackageImpl",184),m(556,184,{109:1,2077:1,556:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,52:1,100:1,161:1,184:1,117:1,118:1,680:1},Uqe),s.q=!1,s.r=!1;var van=!1;v(rg,"ElkGraphPackageImpl",556),m(362,728,{109:1,343:1,174:1,157:1,276:1,362:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},VT),s.xh=function(n){return _de(this,n)},s.Ih=function(n,t,i){switch(n){case 7:return fae(this);case 8:return this.a}return U1e(this,n,t,i)},s.Ph=function(n,t,i){var r;return t===7?(this.Cb&&(i=(r=this.Db>>16,r>=0?_de(this,i):this.Cb.Qh(this,-1-r,null,i))),mfe(this,u(n,174),i)):iZ(this,n,t,i)},s.Rh=function(n,t,i){return t==7?mfe(this,null,i):HY(this,n,t,i)},s.Th=function(n){switch(n){case 7:return!!fae(this);case 8:return!hn("",this.a)}return W1e(this,n)},s.$h=function(n,t){switch(n){case 7:pbe(this,u(t,174));return;case 8:Lhe(this,Dt(t));return}Kde(this,n,t)},s.fi=function(){return Hu(),M8e},s.hi=function(n){switch(n){case 7:pbe(this,null);return;case 8:Lhe(this,"");return}rde(this,n)},s.Ib=function(){return IGe(this)},s.a="",v(rg,"ElkLabelImpl",362),m(206,729,{109:1,343:1,84:1,174:1,26:1,276:1,206:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},oE),s.xh=function(n){return Rde(this,n)},s.Ih=function(n,t,i){switch(n){case 9:return!this.c&&(this.c=new we(Ps,this,9,9)),this.c;case 10:return!this.a&&(this.a=new we($t,this,10,11)),this.a;case 11:return zi(this);case 12:return!this.b&&(this.b=new we(wr,this,12,3)),this.b;case 13:return Pn(),!this.a&&(this.a=new we($t,this,10,11)),this.a.i>0}return Ede(this,n,t,i)},s.Ph=function(n,t,i){var r;switch(t){case 9:return!this.c&&(this.c=new we(Ps,this,9,9)),To(this.c,n,i);case 10:return!this.a&&(this.a=new we($t,this,10,11)),To(this.a,n,i);case 11:return this.Cb&&(i=(r=this.Db>>16,r>=0?Rde(this,i):this.Cb.Qh(this,-1-r,null,i))),Lle(this,u(n,26),i);case 12:return!this.b&&(this.b=new we(wr,this,12,3)),To(this.b,n,i)}return Fde(this,n,t,i)},s.Rh=function(n,t,i){switch(t){case 9:return!this.c&&(this.c=new we(Ps,this,9,9)),mc(this.c,n,i);case 10:return!this.a&&(this.a=new we($t,this,10,11)),mc(this.a,n,i);case 11:return Lle(this,null,i);case 12:return!this.b&&(this.b=new we(wr,this,12,3)),mc(this.b,n,i)}return Hde(this,n,t,i)},s.Th=function(n){switch(n){case 9:return!!this.c&&this.c.i!=0;case 10:return!!this.a&&this.a.i!=0;case 11:return!!zi(this);case 12:return!!this.b&&this.b.i!=0;case 13:return!this.a&&(this.a=new we($t,this,10,11)),this.a.i>0}return f1e(this,n)},s.$h=function(n,t){switch(n){case 9:!this.c&&(this.c=new we(Ps,this,9,9)),vt(this.c),!this.c&&(this.c=new we(Ps,this,9,9)),er(this.c,u(t,18));return;case 10:!this.a&&(this.a=new we($t,this,10,11)),vt(this.a),!this.a&&(this.a=new we($t,this,10,11)),er(this.a,u(t,18));return;case 11:Nz(this,u(t,26));return;case 12:!this.b&&(this.b=new we(wr,this,12,3)),vt(this.b),!this.b&&(this.b=new we(wr,this,12,3)),er(this.b,u(t,18));return}nbe(this,n,t)},s.fi=function(){return Hu(),T8e},s.hi=function(n){switch(n){case 9:!this.c&&(this.c=new we(Ps,this,9,9)),vt(this.c);return;case 10:!this.a&&(this.a=new we($t,this,10,11)),vt(this.a);return;case 11:Nz(this,null);return;case 12:!this.b&&(this.b=new we(wr,this,12,3)),vt(this.b);return}pde(this,n)},s.Ib=function(){return Ibe(this)},v(rg,"ElkNodeImpl",206),m(193,729,{109:1,343:1,84:1,174:1,125:1,276:1,193:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},foe),s.xh=function(n){return Lde(this,n)},s.Ih=function(n,t,i){return n==9?Ma(this):Ede(this,n,t,i)},s.Ph=function(n,t,i){var r;return t===9?(this.Cb&&(i=(r=this.Db>>16,r>=0?Lde(this,i):this.Cb.Qh(this,-1-r,null,i))),kle(this,u(n,26),i)):Fde(this,n,t,i)},s.Rh=function(n,t,i){return t==9?kle(this,null,i):Hde(this,n,t,i)},s.Th=function(n){return n==9?!!Ma(this):f1e(this,n)},s.$h=function(n,t){if(n===9){hbe(this,u(t,26));return}nbe(this,n,t)},s.fi=function(){return Hu(),x8e},s.hi=function(n){if(n===9){hbe(this,null);return}pde(this,n)},s.Ib=function(){return AXe(this)},v(rg,"ElkPortImpl",193);var yan=Ji(vc,"BasicEMap/Entry");m(1091,118,{109:1,45:1,94:1,93:1,136:1,57:1,114:1,52:1,100:1,117:1,118:1},Ay),s.Fb=function(n){return this===n},s.jd=function(){return this.b},s.Hb=function(){return cw(this)},s.Ai=function(n){Nhe(this,u(n,147))},s.Ih=function(n,t,i){switch(n){case 0:return this.b;case 1:return this.c}return rz(this,n,t,i)},s.Th=function(n){switch(n){case 0:return!!this.b;case 1:return this.c!=null}return CQ(this,n)},s.$h=function(n,t){switch(n){case 0:Nhe(this,u(t,147));return;case 1:Dhe(this,t);return}bZ(this,n,t)},s.fi=function(){return Hu(),u1},s.hi=function(n){switch(n){case 0:Nhe(this,null);return;case 1:Dhe(this,null);return}aZ(this,n)},s.yi=function(){var n;return this.a==-1&&(n=this.b,this.a=n?Oi(n):0),this.a},s.kd=function(){return this.c},s.zi=function(n){this.a=n},s.ld=function(n){var t;return t=this.c,Dhe(this,n),t},s.Ib=function(){var n;return(this.Db&64)!=0?Lf(this):(n=new l0,Gt(Gt(Gt(n,this.b?this.b.Og():Ko),VW),FE(this.c)),n.a)},s.a=-1,s.c=null;var qd=v(rg,"ElkPropertyToValueMapEntryImpl",1091);m(980,1,{},C2),v(Qr,"JsonAdapter",980),m(215,63,R1,ih),v(Qr,"JsonImportException",215),m(850,1,{},Fqe),v(Qr,"JsonImporter",850),m(884,1,{},Sxe),s.Bi=function(n){LJe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$0$Type",884),m(885,1,{},Axe),s.Bi=function(n){mqe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$1$Type",885),m(893,1,{},Yje),s.Bi=function(n){OIe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$10$Type",893),m(895,1,{},Mxe),s.Bi=function(n){cqe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$11$Type",895),m(896,1,{},Txe),s.Bi=function(n){uqe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$12$Type",896),m(902,1,{},zIe),s.Bi=function(n){CGe(this.a,this.b,this.c,this.d,u(n,139))},v(Qr,"JsonImporter/lambda$13$Type",902),m(901,1,{},FIe),s.Bi=function(n){UXe(this.a,this.b,this.c,this.d,u(n,149))},v(Qr,"JsonImporter/lambda$14$Type",901),m(897,1,{},xxe),s.Bi=function(n){tNe(this.a,this.b,Dt(n))},v(Qr,"JsonImporter/lambda$15$Type",897),m(898,1,{},Cxe),s.Bi=function(n){iNe(this.a,this.b,Dt(n))},v(Qr,"JsonImporter/lambda$16$Type",898),m(899,1,{},Oxe),s.Bi=function(n){wJe(this.b,this.a,u(n,139))},v(Qr,"JsonImporter/lambda$17$Type",899),m(900,1,{},Nxe),s.Bi=function(n){pJe(this.b,this.a,u(n,139))},v(Qr,"JsonImporter/lambda$18$Type",900),m(905,1,{},Qje),s.Bi=function(n){yGe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$19$Type",905),m(886,1,{},Zje),s.Bi=function(n){xJe(this.a,u(n,139))},v(Qr,"JsonImporter/lambda$2$Type",886),m(903,1,{},Wje),s.Bi=function(n){R3(this.a,W(ie(n)))},v(Qr,"JsonImporter/lambda$20$Type",903),m(904,1,{},eSe),s.Bi=function(n){B3(this.a,W(ie(n)))},v(Qr,"JsonImporter/lambda$21$Type",904),m(908,1,{},nSe),s.Bi=function(n){vGe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$22$Type",908),m(906,1,{},tSe),s.Bi=function(n){P3(this.a,W(ie(n)))},v(Qr,"JsonImporter/lambda$23$Type",906),m(907,1,{},iSe),s.Bi=function(n){$3(this.a,W(ie(n)))},v(Qr,"JsonImporter/lambda$24$Type",907),m(910,1,{},rSe),s.Bi=function(n){UJe(this.a,u(n,139))},v(Qr,"JsonImporter/lambda$25$Type",910),m(909,1,{},cSe),s.Bi=function(n){NIe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$26$Type",909),m(911,1,it,Dxe),s.Ad=function(n){M9n(this.b,this.a,Dt(n))},v(Qr,"JsonImporter/lambda$27$Type",911),m(912,1,it,Ixe),s.Ad=function(n){T9n(this.b,this.a,Dt(n))},v(Qr,"JsonImporter/lambda$28$Type",912),m(913,1,{},_xe),s.Bi=function(n){nUe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$29$Type",913),m(889,1,{},uSe),s.Bi=function(n){zFe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$3$Type",889),m(914,1,{},Lxe),s.Bi=function(n){jUe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$30$Type",914),m(915,1,{},oSe),s.Bi=function(n){sRe(this.a,ie(n))},v(Qr,"JsonImporter/lambda$31$Type",915),m(916,1,{},sSe),s.Bi=function(n){lRe(this.a,ie(n))},v(Qr,"JsonImporter/lambda$32$Type",916),m(917,1,{},lSe),s.Bi=function(n){fRe(this.a,ie(n))},v(Qr,"JsonImporter/lambda$33$Type",917),m(918,1,{},fSe),s.Bi=function(n){aRe(this.a,ie(n))},v(Qr,"JsonImporter/lambda$34$Type",918),m(919,1,{},aSe),s.Bi=function(n){jTn(this.a,u(n,57))},v(Qr,"JsonImporter/lambda$35$Type",919),m(920,1,{},hSe),s.Bi=function(n){STn(this.a,u(n,57))},v(Qr,"JsonImporter/lambda$36$Type",920),m(924,1,{},BIe),v(Qr,"JsonImporter/lambda$37$Type",924),m(921,1,it,LNe),s.Ad=function(n){W8n(this.a,this.c,this.b,u(n,372))},v(Qr,"JsonImporter/lambda$38$Type",921),m(922,1,it,Pxe),s.Ad=function(n){Rgn(this.a,this.b,u(n,170))},v(Qr,"JsonImporter/lambda$39$Type",922),m(887,1,{},dSe),s.Bi=function(n){R3(this.a,W(ie(n)))},v(Qr,"JsonImporter/lambda$4$Type",887),m(923,1,it,$xe),s.Ad=function(n){Bgn(this.a,this.b,u(n,170))},v(Qr,"JsonImporter/lambda$40$Type",923),m(925,1,it,PNe),s.Ad=function(n){e7n(this.a,this.b,this.c,u(n,8))},v(Qr,"JsonImporter/lambda$41$Type",925),m(888,1,{},bSe),s.Bi=function(n){B3(this.a,W(ie(n)))},v(Qr,"JsonImporter/lambda$5$Type",888),m(892,1,{},gSe),s.Bi=function(n){FFe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$6$Type",892),m(890,1,{},wSe),s.Bi=function(n){P3(this.a,W(ie(n)))},v(Qr,"JsonImporter/lambda$7$Type",890),m(891,1,{},pSe),s.Bi=function(n){$3(this.a,W(ie(n)))},v(Qr,"JsonImporter/lambda$8$Type",891),m(894,1,{},mSe),s.Bi=function(n){XJe(this.a,u(n,139))},v(Qr,"JsonImporter/lambda$9$Type",894),m(944,1,it,vSe),s.Ad=function(n){p4(this.a,new up(Dt(n)))},v(Qr,"JsonMetaDataConverter/lambda$0$Type",944),m(945,1,it,ySe),s.Ad=function(n){Nvn(this.a,u(n,244))},v(Qr,"JsonMetaDataConverter/lambda$1$Type",945),m(946,1,it,kSe),s.Ad=function(n){E4n(this.a,u(n,144))},v(Qr,"JsonMetaDataConverter/lambda$2$Type",946),m(947,1,it,ESe),s.Ad=function(n){Dvn(this.a,u(n,160))},v(Qr,"JsonMetaDataConverter/lambda$3$Type",947),m(244,23,{3:1,35:1,23:1,244:1},t4);var dG,bG,Cce,gG,wG,pG,Oce,Nce,mG=mt(vN,"GraphFeature",244,At,c8n,d3n),kan;m(11,1,{35:1,147:1},mi,Li,cn,Vr),s.Dd=function(n){return Pwn(this,u(n,147))},s.Fb=function(n){return a_e(this,n)},s.Rg=function(){return Ie(this)},s.Og=function(){return this.b},s.Hb=function(){return Sd(this.b)},s.Ib=function(){return this.b},v(vN,"Property",11),m(657,1,Ut,uX),s.Le=function(n,t){return pEn(this,u(n,105),u(t,105))},s.Fb=function(n){return this===n},s.Me=function(){return new jt(this)},v(vN,"PropertyHolderComparator",657),m(698,1,zr,Vue),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return N9n(this)},s.Qb=function(){wMe()},s.Ob=function(){return!!this.a},v(FF,"ElkGraphUtil/AncestorIterator",698);var O8e=Ji(vc,"EList");m(71,56,{20:1,31:1,56:1,18:1,16:1,71:1,61:1}),s._c=function(n,t){Tj(this,n,t)},s.Ec=function(n){return kt(this,n)},s.ad=function(n,t){return i1e(this,n,t)},s.Fc=function(n){return er(this,n)},s.Gi=function(){return new o4(this)},s.Hi=function(){return new vC(this)},s.Ii=function(n){return fO(this,n)},s.Ji=function(){return!0},s.Ki=function(n,t){},s.Li=function(){},s.Mi=function(n,t){lY(this,n,t)},s.Ni=function(n,t,i){},s.Oi=function(n,t){},s.Pi=function(n,t,i){},s.Fb=function(n){return sXe(this,n)},s.Hb=function(){return Whe(this)},s.Qi=function(){return!1},s.Jc=function(){return new ct(this)},s.cd=function(){return new u4(this)},s.dd=function(n){var t;if(t=this.gc(),n<0||n>t)throw P(new ep(n,t));return new bV(this,n)},s.Si=function(n,t){this.Ri(n,this.bd(t))},s.Kc=function(n){return uB(this,n)},s.Ui=function(n,t){return t},s.fd=function(n,t){return q3(this,n,t)},s.Ib=function(){return V1e(this)},s.Wi=function(){return!0},s.Xi=function(n,t){return H9(this,t)},v(vc,"AbstractEList",71),m(67,71,jh,My,pw,Xhe),s.Ci=function(n,t){return rZ(this,n,t)},s.Di=function(n){return QHe(this,n)},s.Ei=function(n,t){jO(this,n,t)},s.Fi=function(n){KC(this,n)},s.Yi=function(n){return she(this,n)},s.$b=function(){lj(this)},s.Gc=function(n){return r8(this,n)},s.Xb=function(n){return X(this,n)},s.Zi=function(n){var t,i,r;++this.j,i=this.g==null?0:this.g.length,n>i&&(r=this.g,t=i+(i/2|0)+4,t=0?(this.ed(t),!0):!1},s.Vi=function(n,t){return this.Bj(n,this.Xi(n,t))},s.gc=function(){return this.Cj()},s.Nc=function(){return this.Dj()},s.Oc=function(n){return this.Ej(n)},s.Ib=function(){return this.Fj()},v(vc,"DelegatingEList",2055),m(2056,2055,xWe),s.Ci=function(n,t){return Xbe(this,n,t)},s.Di=function(n){return this.Ci(this.Cj(),n)},s.Ei=function(n,t){Xqe(this,n,t)},s.Fi=function(n){Pqe(this,n)},s.Ji=function(){return!this.Kj()},s.$b=function(){tS(this)},s.Gj=function(n,t,i,r,c){return new l_e(this,n,t,i,r,c)},s.Hj=function(n){fi(this.hj(),n)},s.Ij=function(){return null},s.Jj=function(){return-1},s.hj=function(){return null},s.Kj=function(){return!1},s.Lj=function(n,t){return t},s.Mj=function(n,t){return t},s.Nj=function(){return!1},s.Oj=function(){return!this.yj()},s.Ri=function(n,t){var i,r;return this.Nj()?(r=this.Oj(),i=b0e(this,n,t),this.Hj(this.Gj(7,me(t),i,n,r)),i):b0e(this,n,t)},s.ed=function(n){var t,i,r,c;return this.Nj()?(i=null,r=this.Oj(),t=this.Gj(4,c=nR(this,n),null,n,r),this.Kj()&&c?(i=this.Mj(c,i),i?(i.lj(t),i.mj()):this.Hj(t)):i?(i.lj(t),i.mj()):this.Hj(t),c):(c=nR(this,n),this.Kj()&&c&&(i=this.Mj(c,null),i&&i.mj()),c)},s.Vi=function(n,t){return rKe(this,n,t)},v(c6,"DelegatingNotifyingListImpl",2056),m(151,1,PN),s.lj=function(n){return Wde(this,n)},s.mj=function(){pY(this)},s.ej=function(){return this.d},s.Ij=function(){return null},s.Pj=function(){return null},s.fj=function(n){return-1},s.gj=function(){return JUe(this)},s.hj=function(){return null},s.ij=function(){return Ebe(this)},s.jj=function(){return this.o<0?this.o<-2?-2-this.o-1:-1:this.o},s.Qj=function(){return!1},s.kj=function(n){var t,i,r,c,o,l,f,h,b,p,y;switch(this.d){case 1:case 2:switch(c=n.ej(),c){case 1:case 2:if(o=n.hj(),ue(o)===ue(this.hj())&&this.fj(null)==n.fj(null))return this.g=n.gj(),n.ej()==1&&(this.d=1),!0}case 4:{switch(c=n.ej(),c){case 4:{if(o=n.hj(),ue(o)===ue(this.hj())&&this.fj(null)==n.fj(null))return b=age(this),h=this.o<0?this.o<-2?-2-this.o-1:-1:this.o,l=n.jj(),this.d=6,y=new pw(2),h<=l?(kt(y,this.n),kt(y,n.ij()),this.g=z(B(It,1),Zt,30,15,[this.o=h,l+1])):(kt(y,n.ij()),kt(y,this.n),this.g=z(B(It,1),Zt,30,15,[this.o=l,h])),this.n=y,b||(this.o=-2-this.o-1),!0;break}}break}case 6:{switch(c=n.ej(),c){case 4:{if(o=n.hj(),ue(o)===ue(this.hj())&&this.fj(null)==n.fj(null)){for(b=age(this),l=n.jj(),p=u(this.g,54),r=oe(It,Zt,30,p.length+1,15,1),t=0;t>>0,t.toString(16))),r.a+=" (eventType: ",this.d){case 1:{r.a+="SET";break}case 2:{r.a+="UNSET";break}case 3:{r.a+="ADD";break}case 5:{r.a+="ADD_MANY";break}case 4:{r.a+="REMOVE";break}case 6:{r.a+="REMOVE_MANY";break}case 7:{r.a+="MOVE";break}case 8:{r.a+="REMOVING_ADAPTER";break}case 9:{r.a+="RESOLVE";break}default:{xX(r,this.d);break}}if(NXe(this)&&(r.a+=", touch: true"),r.a+=", position: ",xX(r,this.o<0?this.o<-2?-2-this.o-1:-1:this.o),r.a+=", notifier: ",LE(r,this.hj()),r.a+=", feature: ",LE(r,this.Ij()),r.a+=", oldValue: ",LE(r,Ebe(this)),r.a+=", newValue: ",this.d==6&&q(this.g,54)){for(i=u(this.g,54),r.a+="[",n=0;n10?((!this.b||this.c.j!=this.a)&&(this.b=new tp(this),this.a=this.j),ef(this.b,n)):r8(this,n)},s.Wi=function(){return!0},s.a=0,v(vc,"AbstractEList/1",949),m(305,99,nF,ep),v(vc,"AbstractEList/BasicIndexOutOfBoundsException",305),m(42,1,zr,ct),s.Nb=function(n){Zr(this,n)},s.Vj=function(){if(this.i.j!=this.f)throw P(new xl)},s.Wj=function(){return lt(this)},s.Ob=function(){return this.e!=this.i.gc()},s.Pb=function(){return this.Wj()},s.Qb=function(){Rj(this)},s.e=0,s.f=0,s.g=-1,v(vc,"AbstractEList/EIterator",42),m(286,42,Uh,u4,bV),s.Qb=function(){Rj(this)},s.Rb=function(n){ZFe(this,n)},s.Xj=function(){var n;try{return n=this.d.Xb(--this.e),this.Vj(),this.g=this.e,n}catch(t){throw t=or(t),q(t,99)?(this.Vj(),P(new fu)):P(t)}},s.Yj=function(n){ZHe(this,n)},s.Sb=function(){return this.e!=0},s.Tb=function(){return this.e},s.Ub=function(){return this.Xj()},s.Vb=function(){return this.e-1},s.Wb=function(n){this.Yj(n)},v(vc,"AbstractEList/EListIterator",286),m(355,42,zr,o4),s.Wj=function(){return OQ(this)},s.Qb=function(){throw P(new Ot)},v(vc,"AbstractEList/NonResolvingEIterator",355),m(391,286,Uh,vC,Rle),s.Rb=function(n){throw P(new Ot)},s.Wj=function(){var n;try{return n=this.c.Ti(this.e),this.Vj(),this.g=this.e++,n}catch(t){throw t=or(t),q(t,99)?(this.Vj(),P(new fu)):P(t)}},s.Xj=function(){var n;try{return n=this.c.Ti(--this.e),this.Vj(),this.g=this.e,n}catch(t){throw t=or(t),q(t,99)?(this.Vj(),P(new fu)):P(t)}},s.Qb=function(){throw P(new Ot)},s.Wb=function(n){throw P(new Ot)},v(vc,"AbstractEList/NonResolvingEListIterator",391),m(2042,71,CWe),s.Ci=function(n,t){var i,r,c,o,l,f,h,b,p,y,S;if(c=t.gc(),c!=0){for(b=u(Un(this.a,4),129),p=b==null?0:b.length,S=p+c,r=nQ(this,S),y=p-n,y>0&&Yu(b,n,r,n+c,y),h=t.Jc(),l=0;li)throw P(new ep(n,i));return new SIe(this,n)},s.$b=function(){var n,t;++this.j,n=u(Un(this.a,4),129),t=n==null?0:n.length,n8(this,null),lY(this,t,n)},s.Gc=function(n){var t,i,r,c,o;if(t=u(Un(this.a,4),129),t!=null){if(n!=null){for(r=t,c=0,o=r.length;c=i)throw P(new ep(n,i));return t[n]},s.bd=function(n){var t,i,r;if(t=u(Un(this.a,4),129),t!=null){if(n!=null){for(i=0,r=t.length;ii)throw P(new ep(n,i));return new jIe(this,n)},s.Ri=function(n,t){var i,r,c;if(i=oHe(this),c=i==null?0:i.length,n>=c)throw P(new ko(Ene+n+cg+c));if(t>=c)throw P(new ko(jne+t+cg+c));return r=i[t],n!=t&&(n0&&Yu(n,0,t,0,i),t},s.Oc=function(n){var t,i,r;return t=u(Un(this.a,4),129),r=t==null?0:t.length,r>0&&(n.lengthr&&tr(n,r,null),n};var Ean;v(vc,"ArrayDelegatingEList",2042),m(1032,42,zr,NPe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw P(new xl)},s.Qb=function(){Rj(this),this.a=u(Un(this.b.a,4),129)},v(vc,"ArrayDelegatingEList/EIterator",1032),m(712,286,Uh,GDe,jIe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw P(new xl)},s.Yj=function(n){ZHe(this,n),this.a=u(Un(this.b.a,4),129)},s.Qb=function(){Rj(this),this.a=u(Un(this.b.a,4),129)},v(vc,"ArrayDelegatingEList/EListIterator",712),m(1033,355,zr,DPe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw P(new xl)},v(vc,"ArrayDelegatingEList/NonResolvingEIterator",1033),m(713,391,Uh,qDe,SIe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw P(new xl)},v(vc,"ArrayDelegatingEList/NonResolvingEListIterator",713),m(605,305,nF,pK),v(vc,"BasicEList/BasicIndexOutOfBoundsException",605),m(699,67,jh,Ese),s._c=function(n,t){throw P(new Ot)},s.Ec=function(n){throw P(new Ot)},s.ad=function(n,t){throw P(new Ot)},s.Fc=function(n){throw P(new Ot)},s.$b=function(){throw P(new Ot)},s.Zi=function(n){throw P(new Ot)},s.Jc=function(){return this.Gi()},s.cd=function(){return this.Hi()},s.dd=function(n){return this.Ii(n)},s.Ri=function(n,t){throw P(new Ot)},s.Si=function(n,t){throw P(new Ot)},s.ed=function(n){throw P(new Ot)},s.Kc=function(n){throw P(new Ot)},s.fd=function(n,t){throw P(new Ot)},v(vc,"BasicEList/UnmodifiableEList",699),m(711,1,{3:1,20:1,18:1,16:1,61:1,586:1}),s._c=function(n,t){Awn(this,n,u(t,45))},s.Ec=function(n){return v2n(this,u(n,45))},s.Ic=function(n){rc(this,n)},s.Xb=function(n){return u(X(this.c,n),136)},s.Ri=function(n,t){return u(this.c.Ri(n,t),45)},s.Si=function(n,t){Mwn(this,n,u(t,45))},s.ed=function(n){return u(this.c.ed(n),45)},s.fd=function(n,t){return _vn(this,n,u(t,45))},s.gd=function(n){Fb(this,n)},s.Lc=function(){return new wn(this,16)},s.Mc=function(){return new bn(null,new wn(this,16))},s.ad=function(n,t){return this.c.ad(n,t)},s.Fc=function(n){return this.c.Fc(n)},s.$b=function(){this.c.$b()},s.Gc=function(n){return this.c.Gc(n)},s.Hc=function(n){return mO(this.c,n)},s.Zj=function(){var n,t,i;if(this.d==null){for(this.d=oe(N8e,Upe,67,2*this.f+1,0,1),i=this.e,this.f=0,t=this.c.Jc();t.e!=t.i.gc();)n=u(t.Wj(),136),oz(this,n);this.e=i}},s.Fb=function(n){return gNe(this,n)},s.Hb=function(){return Whe(this.c)},s.bd=function(n){return this.c.bd(n)},s.$j=function(){this.c=new jSe(this)},s.dc=function(){return this.f==0},s.Jc=function(){return this.c.Jc()},s.cd=function(){return this.c.cd()},s.dd=function(n){return this.c.dd(n)},s._j=function(){return QC(this)},s.ak=function(n,t,i){return new $Ne(n,t,i)},s.bk=function(){return new wL},s.Kc=function(n){return oBe(this,n)},s.gc=function(){return this.f},s.hd=function(n,t){return new y0(this.c,n,t)},s.Nc=function(){return this.c.Nc()},s.Oc=function(n){return this.c.Oc(n)},s.Ib=function(){return V1e(this.c)},s.e=0,s.f=0,v(vc,"BasicEMap",711),m(1027,67,jh,jSe),s.Ki=function(n,t){obn(this,u(t,136))},s.Ni=function(n,t,i){var r;++(r=this,u(t,136),r).a.e},s.Oi=function(n,t){sbn(this,u(t,136))},s.Pi=function(n,t,i){c2n(this,u(t,136),u(i,136))},s.Mi=function(n,t){nze(this.a)},v(vc,"BasicEMap/1",1027),m(1028,67,jh,wL),s.$i=function(n){return oe(FBn,OWe,611,n,0,1)},v(vc,"BasicEMap/2",1028),m(1029,Pa,ls,SSe),s.$b=function(){this.a.c.$b()},s.Gc=function(n){return vQ(this.a,n)},s.Jc=function(){return this.a.f==0?(f9(),ZD.a):new sMe(this.a)},s.Kc=function(n){var t;return t=this.a.f,QB(this.a,n),this.a.f!=t},s.gc=function(){return this.a.f},v(vc,"BasicEMap/3",1029),m(1030,31,Pp,ASe),s.$b=function(){this.a.c.$b()},s.Gc=function(n){return lXe(this.a,n)},s.Jc=function(){return this.a.f==0?(f9(),ZD.a):new lMe(this.a)},s.gc=function(){return this.a.f},v(vc,"BasicEMap/4",1030),m(1031,Pa,ls,MSe),s.$b=function(){this.a.c.$b()},s.Gc=function(n){var t,i,r,c,o,l,f,h,b;if(this.a.f>0&&q(n,45)&&(this.a.Zj(),h=u(n,45),f=h.jd(),c=f==null?0:Oi(f),o=Ele(this.a,c),t=this.a.d[o],t)){for(i=u(t.g,374),b=t.i,l=0;l"+this.c},s.a=0;var FBn=v(vc,"BasicEMap/EntryImpl",611);m(534,1,{},xy),v(vc,"BasicEMap/View",534);var ZD;m(769,1,{}),s.Fb=function(n){return tbe((vn(),Ec),n)},s.Hb=function(){return a1e((vn(),Ec))},s.Ib=function(){return _a((vn(),Ec))},v(vc,"ECollections/BasicEmptyUnmodifiableEList",769),m(1302,1,Uh,YT),s.Nb=function(n){Zr(this,n)},s.Rb=function(n){throw P(new Ot)},s.Ob=function(){return!1},s.Sb=function(){return!1},s.Pb=function(){throw P(new fu)},s.Tb=function(){return 0},s.Ub=function(){throw P(new fu)},s.Vb=function(){return-1},s.Qb=function(){throw P(new Ot)},s.Wb=function(n){throw P(new Ot)},v(vc,"ECollections/BasicEmptyUnmodifiableEList/1",1302),m(1300,769,{20:1,18:1,16:1,61:1},gAe),s._c=function(n,t){NMe()},s.Ec=function(n){return OMe()},s.ad=function(n,t){return DMe()},s.Fc=function(n){return IMe()},s.$b=function(){_Me()},s.Gc=function(n){return!1},s.Hc=function(n){return!1},s.Ic=function(n){rc(this,n)},s.Xb=function(n){return Tse((vn(),n)),null},s.bd=function(n){return-1},s.dc=function(){return!0},s.Jc=function(){return this.a},s.cd=function(){return this.a},s.dd=function(n){return this.a},s.Ri=function(n,t){return LMe()},s.Si=function(n,t){PMe()},s.ed=function(n){return $Me()},s.Kc=function(n){return RMe()},s.fd=function(n,t){return BMe()},s.gc=function(){return 0},s.gd=function(n){Fb(this,n)},s.Lc=function(){return new wn(this,16)},s.Mc=function(){return new bn(null,new wn(this,16))},s.hd=function(n,t){return vn(),new y0(Ec,n,t)},s.Nc=function(){return kfe((vn(),Ec))},s.Oc=function(n){return vn(),_j(Ec,n)},v(vc,"ECollections/EmptyUnmodifiableEList",1300),m(1301,769,{20:1,18:1,16:1,61:1,586:1},wAe),s._c=function(n,t){NMe()},s.Ec=function(n){return OMe()},s.ad=function(n,t){return DMe()},s.Fc=function(n){return IMe()},s.$b=function(){_Me()},s.Gc=function(n){return!1},s.Hc=function(n){return!1},s.Ic=function(n){rc(this,n)},s.Xb=function(n){return Tse((vn(),n)),null},s.bd=function(n){return-1},s.dc=function(){return!0},s.Jc=function(){return this.a},s.cd=function(){return this.a},s.dd=function(n){return this.a},s.Ri=function(n,t){return LMe()},s.Si=function(n,t){PMe()},s.ed=function(n){return $Me()},s.Kc=function(n){return RMe()},s.fd=function(n,t){return BMe()},s.gc=function(){return 0},s.gd=function(n){Fb(this,n)},s.Lc=function(){return new wn(this,16)},s.Mc=function(){return new bn(null,new wn(this,16))},s.hd=function(n,t){return vn(),new y0(Ec,n,t)},s.Nc=function(){return kfe((vn(),Ec))},s.Oc=function(n){return vn(),_j(Ec,n)},s._j=function(){return vn(),vn(),Zh},v(vc,"ECollections/EmptyUnmodifiableEMap",1301);var I8e=Ji(vc,"Enumerator"),vG;m(290,1,{290:1},TZ),s.Fb=function(n){var t;return this===n?!0:q(n,290)?(t=u(n,290),this.f==t.f&&Q3n(this.i,t.i)&&eV(this.a,(this.f&256)!=0?(t.f&256)!=0?t.a:null:(t.f&256)!=0?null:t.a)&&eV(this.d,t.d)&&eV(this.g,t.g)&&eV(this.e,t.e)&&eSn(this,t)):!1},s.Hb=function(){return this.f},s.Ib=function(){return JXe(this)},s.f=0;var jan=0,San=0,Aan=0,Man=0,_8e=0,L8e=0,P8e=0,$8e=0,R8e=0,Tan,YA=0,QA=0,xan=0,Can=0,yG,B8e;v(vc,"URI",290),m(1090,44,iv,pAe),s.yc=function(n,t){return u(Xc(this,Dt(n),u(t,290)),290)},v(vc,"URI/URICache",1090),m(492,67,jh,QT,oR),s.Qi=function(){return!0},v(vc,"UniqueEList",492),m(578,63,R1,rB),v(vc,"WrappedException",578);var Yt=Ji(Hl,IWe),jm=Ji(Hl,_We),es=Ji(Hl,LWe),Sm=Ji(Hl,PWe),va=Ji(Hl,$We),wf=Ji(Hl,"EClass"),_ce=Ji(Hl,"EDataType"),Oan;m(1198,44,iv,mAe),s.xc=function(n){return Pr(n)?so(this,n):hu(Uc(this.f,n))},v(Hl,"EDataType/Internal/ConversionDelegate/Factory/Registry/Impl",1198);var kG=Ji(Hl,"EEnum"),V1=Ji(Hl,RWe),Pc=Ji(Hl,BWe),pf=Ji(Hl,zWe),mf,c2=Ji(Hl,FWe),Am=Ji(Hl,HWe);m(1023,1,{},ZT),s.Ib=function(){return"NIL"},v(Hl,"EStructuralFeature/Internal/DynamicValueHolder/1",1023);var Nan;m(1022,44,iv,vAe),s.xc=function(n){return Pr(n)?so(this,n):hu(Uc(this.f,n))},v(Hl,"EStructuralFeature/Internal/SettingDelegate/Factory/Registry/Impl",1022);var zo=Ji(Hl,JWe),L6=Ji(Hl,"EValidator/PatternMatcher"),z8e,F8e,Rn,Ud,Mm,lb,Dan,Ian,_an,fb,Xd,ab,u2,Ya,Lan,Pan,vf,Kd,$an,Vd,Tm,Fv,Sc,Ran,Ban,o2,EG=Ji($i,"FeatureMap/Entry");m(533,1,{75:1},S$),s.Jk=function(){return this.a},s.kd=function(){return this.b},v(Fn,"BasicEObjectImpl/1",533),m(1021,1,Cne,Bxe),s.Dk=function(n){return cY(this.a,this.b,n)},s.Oj=function(){return M_e(this.a,this.b)},s.Wb=function(n){sae(this.a,this.b,n)},s.Ek=function(){e5n(this.a,this.b)},v(Fn,"BasicEObjectImpl/4",1021),m(2043,1,{114:1}),s.Kk=function(n){this.e=n==0?zan:oe(Mr,xn,1,n,5,1)},s.ii=function(n){return this.e[n]},s.ji=function(n,t){this.e[n]=t},s.ki=function(n){this.e[n]=null},s.Lk=function(){return this.c},s.Mk=function(){throw P(new Ot)},s.Nk=function(){throw P(new Ot)},s.Ok=function(){return this.d},s.Pk=function(){return this.e!=null},s.Qk=function(n){this.c=n},s.Rk=function(n){throw P(new Ot)},s.Sk=function(n){throw P(new Ot)},s.Tk=function(n){this.d=n};var zan;v(Fn,"BasicEObjectImpl/EPropertiesHolderBaseImpl",2043),m(192,2043,{114:1},Zs),s.Mk=function(){return this.a},s.Nk=function(){return this.b},s.Rk=function(n){this.a=n},s.Sk=function(n){this.b=n},v(Fn,"BasicEObjectImpl/EPropertiesHolderImpl",192),m(501,100,JZe,u3),s.rh=function(){return this.f},s.wh=function(){return this.k},s.yh=function(n,t){this.g=n,this.i=t},s.Ah=function(){return(this.j&2)==0?this.fi():this.Xh().Lk()},s.Ch=function(){return this.i},s.th=function(){return(this.j&1)!=0},s.Mh=function(){return this.g},s.Sh=function(){return(this.j&4)!=0},s.Xh=function(){return!this.k&&(this.k=new Zs),this.k},s._h=function(n){this.Xh().Qk(n),n?this.j|=2:this.j&=-3},s.bi=function(n){this.Xh().Sk(n),n?this.j|=4:this.j&=-5},s.fi=function(){return(p0(),Rn).S},s.i=0,s.j=1,v(Fn,"EObjectImpl",501),m(785,501,{109:1,94:1,93:1,57:1,114:1,52:1,100:1},cfe),s.ii=function(n){return this.e[n]},s.ji=function(n,t){this.e[n]=t},s.ki=function(n){this.e[n]=null},s.Ah=function(){return this.d},s.Fh=function(n){return Fi(this.d,n)},s.Hh=function(){return this.d},s.Lh=function(){return this.e!=null},s.Xh=function(){return!this.k&&(this.k=new pL),this.k},s._h=function(n){this.d=n},s.ei=function(){var n;return this.e==null&&(n=ht(this.d),this.e=n==0?Fan:oe(Mr,xn,1,n,5,1)),this},s.gi=function(){return 0};var Fan;v(Fn,"DynamicEObjectImpl",785),m(1483,785,{109:1,45:1,94:1,93:1,136:1,57:1,114:1,52:1,100:1},cDe),s.Fb=function(n){return this===n},s.Hb=function(){return cw(this)},s._h=function(n){this.d=n,this.b=VO(n,"key"),this.c=VO(n,MS)},s.yi=function(){var n;return this.a==-1&&(n=mY(this,this.b),this.a=n==null?0:Oi(n)),this.a},s.jd=function(){return mY(this,this.b)},s.kd=function(){return mY(this,this.c)},s.zi=function(n){this.a=n},s.Ai=function(n){sae(this,this.b,n)},s.ld=function(n){var t;return t=mY(this,this.c),sae(this,this.c,n),t},s.a=0,v(Fn,"DynamicEObjectImpl/BasicEMapEntry",1483),m(1484,1,{114:1},pL),s.Kk=function(n){throw P(new Ot)},s.ii=function(n){throw P(new Ot)},s.ji=function(n,t){throw P(new Ot)},s.ki=function(n){throw P(new Ot)},s.Lk=function(){throw P(new Ot)},s.Mk=function(){return this.a},s.Nk=function(){return this.b},s.Ok=function(){return this.c},s.Pk=function(){throw P(new Ot)},s.Qk=function(n){throw P(new Ot)},s.Rk=function(n){this.a=n},s.Sk=function(n){this.b=n},s.Tk=function(n){this.c=n},v(Fn,"DynamicEObjectImpl/DynamicEPropertiesHolderImpl",1484),m(504,161,{109:1,94:1,93:1,587:1,158:1,57:1,114:1,52:1,100:1,504:1,161:1,117:1,118:1},vb),s.xh=function(n){return Pde(this,n)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),this.Ab;case 1:return this.d;case 2:return i?(!this.b&&(this.b=new Fs((mn(),Sc),Nu,this)),this.b):(!this.b&&(this.b=new Fs((mn(),Sc),Nu,this)),QC(this.b));case 3:return I_e(this);case 4:return!this.a&&(this.a=new mr(sb,this,4)),this.a;case 5:return!this.c&&(this.c=new M3(sb,this,5)),this.c}return Il(this,n-ht((mn(),Ud)),Sn((r=u(Un(this,16),29),r||Ud),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),To(this.Ab,n,i);case 3:return this.Cb&&(i=(c=this.Db>>16,c>=0?Pde(this,i):this.Cb.Qh(this,-1-c,null,i))),vfe(this,u(n,158),i)}return o=u(Sn((r=u(Un(this,16),29),r||(mn(),Ud)),t),69),o.uk().xk(this,_o(this),t-ht((mn(),Ud)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),mc(this.Ab,n,i);case 2:return!this.b&&(this.b=new Fs((mn(),Sc),Nu,this)),G$(this.b,n,i);case 3:return vfe(this,null,i);case 4:return!this.a&&(this.a=new mr(sb,this,4)),mc(this.a,n,i)}return c=u(Sn((r=u(Un(this,16),29),r||(mn(),Ud)),t),69),c.uk().yk(this,_o(this),t-ht((mn(),Ud)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.d!=null;case 2:return!!this.b&&this.b.f!=0;case 3:return!!I_e(this);case 4:return!!this.a&&this.a.i!=0;case 5:return!!this.c&&this.c.i!=0}return Dl(this,n-ht((mn(),Ud)),Sn((t=u(Un(this,16),29),t||Ud),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return;case 1:R3n(this,Dt(t));return;case 2:!this.b&&(this.b=new Fs((mn(),Sc),Nu,this)),TB(this.b,t);return;case 3:NUe(this,u(t,158));return;case 4:!this.a&&(this.a=new mr(sb,this,4)),vt(this.a),!this.a&&(this.a=new mr(sb,this,4)),er(this.a,u(t,18));return;case 5:!this.c&&(this.c=new M3(sb,this,5)),vt(this.c),!this.c&&(this.c=new M3(sb,this,5)),er(this.c,u(t,18));return}Bl(this,n-ht((mn(),Ud)),Sn((i=u(Un(this,16),29),i||Ud),n),t)},s.fi=function(){return mn(),Ud},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return;case 1:_he(this,null);return;case 2:!this.b&&(this.b=new Fs((mn(),Sc),Nu,this)),this.b.c.$b();return;case 3:NUe(this,null);return;case 4:!this.a&&(this.a=new mr(sb,this,4)),vt(this.a);return;case 5:!this.c&&(this.c=new M3(sb,this,5)),vt(this.c);return}Rl(this,n-ht((mn(),Ud)),Sn((t=u(Un(this,16),29),t||Ud),n))},s.Ib=function(){return EFe(this)},s.d=null,v(Fn,"EAnnotationImpl",504),m(142,711,Xpe,us),s.Ei=function(n,t){lwn(this,n,u(t,45))},s.Uk=function(n,t){return lpn(this,u(n,45),t)},s.Yi=function(n){return u(u(this.c,72).Yi(n),136)},s.Gi=function(){return u(this.c,72).Gi()},s.Hi=function(){return u(this.c,72).Hi()},s.Ii=function(n){return u(this.c,72).Ii(n)},s.Vk=function(n,t){return G$(this,n,t)},s.Dk=function(n){return u(this.c,77).Dk(n)},s.$j=function(){},s.Oj=function(){return u(this.c,77).Oj()},s.ak=function(n,t,i){var r;return r=u(rl(this.b).ti().pi(this.b),136),r.zi(n),r.Ai(t),r.ld(i),r},s.bk=function(){return new Que(this)},s.Wb=function(n){TB(this,n)},s.Ek=function(){u(this.c,77).Ek()},v($i,"EcoreEMap",142),m(169,142,Xpe,Fs),s.Zj=function(){var n,t,i,r,c,o;if(this.d==null){for(o=oe(N8e,Upe,67,2*this.f+1,0,1),i=this.c.Jc();i.e!=i.i.gc();)t=u(i.Wj(),136),r=t.yi(),c=(r&ri)%o.length,n=o[c],!n&&(n=o[c]=new Que(this)),n.Ec(t);this.d=o}},v(Fn,"EAnnotationImpl/1",169),m(293,439,{109:1,94:1,93:1,158:1,197:1,57:1,114:1,470:1,52:1,100:1,161:1,293:1,117:1,118:1}),s.Ih=function(n,t,i){var r,c;switch(n){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return me(this.s);case 5:return me(this.t);case 6:return Pn(),!!this.Hk();case 7:return Pn(),c=this.s,c>=1;case 8:return t?of(this):this.r;case 9:return this.q}return Il(this,n-ht(this.fi()),Sn((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),mc(this.Ab,n,i);case 9:return mV(this,i)}return c=u(Sn((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,_o(this),t-ht(this.fi()),n,i)},s.Th=function(n){var t,i;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return this.Hk();case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&fw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&fw(this.q).i==0)}return Dl(this,n-ht(this.fi()),Sn((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return;case 1:this.ri(Dt(t));return;case 2:Td(this,$e(Pe(t)));return;case 3:xd(this,$e(Pe(t)));return;case 4:jd(this,u(t,15).a);return;case 5:this.Xk(u(t,15).a);return;case 8:Xb(this,u(t,143));return;case 9:r=Ia(this,u(t,87),null),r&&r.mj();return}Bl(this,n-ht(this.fi()),Sn((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return mn(),Ban},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return;case 1:this.ri(null);return;case 2:Td(this,!0);return;case 3:xd(this,!0);return;case 4:jd(this,0);return;case 5:this.Xk(1);return;case 8:Xb(this,null);return;case 9:i=Ia(this,null,null),i&&i.mj();return}Rl(this,n-ht(this.fi()),Sn((t=u(Un(this,16),29),t||this.fi()),n))},s.mi=function(){of(this),this.Bb|=1},s.Fk=function(){return of(this)},s.Gk=function(){return this.t},s.Hk=function(){var n;return n=this.t,n>1||n==-1},s.Qi=function(){return(this.Bb&512)!=0},s.Wk=function(n,t){return N1e(this,n,t)},s.Xk=function(n){wp(this,n)},s.Ib=function(){return X0e(this)},s.s=0,s.t=1,v(Fn,"ETypedElementImpl",293),m(451,293,{109:1,94:1,93:1,158:1,197:1,57:1,179:1,69:1,114:1,470:1,52:1,100:1,161:1,451:1,293:1,117:1,118:1,682:1}),s.xh=function(n){return dJe(this,n)},s.Ih=function(n,t,i){var r,c;switch(n){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return me(this.s);case 5:return me(this.t);case 6:return Pn(),!!this.Hk();case 7:return Pn(),c=this.s,c>=1;case 8:return t?of(this):this.r;case 9:return this.q;case 10:return Pn(),(this.Bb&Rf)!=0;case 11:return Pn(),(this.Bb&R0)!=0;case 12:return Pn(),(this.Bb&Rp)!=0;case 13:return this.j;case 14:return f8(this);case 15:return Pn(),(this.Bb&fs)!=0;case 16:return Pn(),(this.Bb&wh)!=0;case 17:return lp(this)}return Il(this,n-ht(this.fi()),Sn((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),To(this.Ab,n,i);case 17:return this.Cb&&(i=(c=this.Db>>16,c>=0?dJe(this,i):this.Cb.Qh(this,-1-c,null,i))),ll(this,n,17,i)}return o=u(Sn((r=u(Un(this,16),29),r||this.fi()),t),69),o.uk().xk(this,_o(this),t-ht(this.fi()),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),mc(this.Ab,n,i);case 9:return mV(this,i);case 17:return ll(this,null,17,i)}return c=u(Sn((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,_o(this),t-ht(this.fi()),n,i)},s.Th=function(n){var t,i;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return this.Hk();case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&fw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&fw(this.q).i==0);case 10:return(this.Bb&Rf)==0;case 11:return(this.Bb&R0)!=0;case 12:return(this.Bb&Rp)!=0;case 13:return this.j!=null;case 14:return f8(this)!=null;case 15:return(this.Bb&fs)!=0;case 16:return(this.Bb&wh)!=0;case 17:return!!lp(this)}return Dl(this,n-ht(this.fi()),Sn((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return;case 1:FV(this,Dt(t));return;case 2:Td(this,$e(Pe(t)));return;case 3:xd(this,$e(Pe(t)));return;case 4:jd(this,u(t,15).a);return;case 5:this.Xk(u(t,15).a);return;case 8:Xb(this,u(t,143));return;case 9:r=Ia(this,u(t,87),null),r&&r.mj();return;case 10:X9(this,$e(Pe(t)));return;case 11:Y9(this,$e(Pe(t)));return;case 12:V9(this,$e(Pe(t)));return;case 13:Sse(this,Dt(t));return;case 15:K9(this,$e(Pe(t)));return;case 16:Q9(this,$e(Pe(t)));return}Bl(this,n-ht(this.fi()),Sn((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return mn(),Ran},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return;case 1:q(this.Cb,88)&&Cp(Ms(u(this.Cb,88)),4),Mo(this,null);return;case 2:Td(this,!0);return;case 3:xd(this,!0);return;case 4:jd(this,0);return;case 5:this.Xk(1);return;case 8:Xb(this,null);return;case 9:i=Ia(this,null,null),i&&i.mj();return;case 10:X9(this,!0);return;case 11:Y9(this,!1);return;case 12:V9(this,!1);return;case 13:this.i=null,kB(this,null);return;case 15:K9(this,!1);return;case 16:Q9(this,!1);return}Rl(this,n-ht(this.fi()),Sn((t=u(Un(this,16),29),t||this.fi()),n))},s.mi=function(){k9(Kc((ss(),ec),this)),of(this),this.Bb|=1},s.nk=function(){return this.f},s.gk=function(){return f8(this)},s.ok=function(){return lp(this)},s.sk=function(){return null},s.Yk=function(){return this.k},s.Jj=function(){return this.n},s.tk=function(){return bz(this)},s.uk=function(){var n,t,i,r,c,o,l,f,h;return this.p||(i=lp(this),(i.i==null&&gh(i),i.i).length,r=this.sk(),r&&ht(lp(r)),c=of(this),l=c.ik(),n=l?(l.i&1)!=0?l==ns?Yi:l==It?Er:l==Cm?Z8:l==Fr?br:l==l2?qw:l==qv?Uw:l==hs?u6:$S:l:null,t=f8(this),f=c.gk(),jEn(this),(this.Bb&wh)!=0&&((o=Jde((ss(),ec),i))&&o!=this||(o=k4(Kc(ec,this))))?this.p=new Fxe(this,o):this.Hk()?this.$k()?r?(this.Bb&fs)!=0?n?this._k()?this.p=new _b(47,n,this,r):this.p=new _b(5,n,this,r):this._k()?this.p=new zb(46,this,r):this.p=new zb(4,this,r):n?this._k()?this.p=new _b(49,n,this,r):this.p=new _b(7,n,this,r):this._k()?this.p=new zb(48,this,r):this.p=new zb(6,this,r):(this.Bb&fs)!=0?n?n==sg?this.p=new pd(50,yan,this):this._k()?this.p=new pd(43,n,this):this.p=new pd(1,n,this):this._k()?this.p=new vd(42,this):this.p=new vd(0,this):n?n==sg?this.p=new pd(41,yan,this):this._k()?this.p=new pd(45,n,this):this.p=new pd(3,n,this):this._k()?this.p=new vd(44,this):this.p=new vd(2,this):q(c,159)?n==EG?this.p=new vd(40,this):(this.Bb&512)!=0?(this.Bb&fs)!=0?n?this.p=new pd(9,n,this):this.p=new vd(8,this):n?this.p=new pd(11,n,this):this.p=new vd(10,this):(this.Bb&fs)!=0?n?this.p=new pd(13,n,this):this.p=new vd(12,this):n?this.p=new pd(15,n,this):this.p=new vd(14,this):r?(h=r.t,h>1||h==-1?this._k()?(this.Bb&fs)!=0?n?this.p=new _b(25,n,this,r):this.p=new zb(24,this,r):n?this.p=new _b(27,n,this,r):this.p=new zb(26,this,r):(this.Bb&fs)!=0?n?this.p=new _b(29,n,this,r):this.p=new zb(28,this,r):n?this.p=new _b(31,n,this,r):this.p=new zb(30,this,r):this._k()?(this.Bb&fs)!=0?n?this.p=new _b(33,n,this,r):this.p=new zb(32,this,r):n?this.p=new _b(35,n,this,r):this.p=new zb(34,this,r):(this.Bb&fs)!=0?n?this.p=new _b(37,n,this,r):this.p=new zb(36,this,r):n?this.p=new _b(39,n,this,r):this.p=new zb(38,this,r)):this._k()?(this.Bb&fs)!=0?n?this.p=new pd(17,n,this):this.p=new vd(16,this):n?this.p=new pd(19,n,this):this.p=new vd(18,this):(this.Bb&fs)!=0?n?this.p=new pd(21,n,this):this.p=new vd(20,this):n?this.p=new pd(23,n,this):this.p=new vd(22,this):this.Zk()?this._k()?this.p=new ONe(u(c,29),this,r):this.p=new cae(u(c,29),this,r):q(c,159)?n==EG?this.p=new vd(40,this):(this.Bb&fs)!=0?n?this.p=new TDe(t,f,this,(kQ(),l==It?K8e:l==ns?J8e:l==l2?V8e:l==Cm?X8e:l==Fr?U8e:l==qv?Y8e:l==hs?G8e:l==Vl?q8e:$ce)):this.p=new JIe(u(c,159),t,f,this):n?this.p=new MDe(t,f,this,(kQ(),l==It?K8e:l==ns?J8e:l==l2?V8e:l==Cm?X8e:l==Fr?U8e:l==qv?Y8e:l==hs?G8e:l==Vl?q8e:$ce)):this.p=new HIe(u(c,159),t,f,this):this.$k()?r?(this.Bb&fs)!=0?this._k()?this.p=new DNe(u(c,29),this,r):this.p=new Gle(u(c,29),this,r):this._k()?this.p=new NNe(u(c,29),this,r):this.p=new XK(u(c,29),this,r):(this.Bb&fs)!=0?this._k()?this.p=new xOe(u(c,29),this):this.p=new lle(u(c,29),this):this._k()?this.p=new TOe(u(c,29),this):this.p=new IK(u(c,29),this):this._k()?r?(this.Bb&fs)!=0?this.p=new INe(u(c,29),this,r):this.p=new qle(u(c,29),this,r):(this.Bb&fs)!=0?this.p=new COe(u(c,29),this):this.p=new fle(u(c,29),this):r?(this.Bb&fs)!=0?this.p=new _Ne(u(c,29),this,r):this.p=new Ule(u(c,29),this,r):(this.Bb&fs)!=0?this.p=new OOe(u(c,29),this):this.p=new uR(u(c,29),this)),this.p},s.pk=function(){return(this.Bb&Rf)!=0},s.Zk=function(){return!1},s.$k=function(){return!1},s.qk=function(){return(this.Bb&wh)!=0},s.vk=function(){return yY(this)},s._k=function(){return!1},s.rk=function(){return(this.Bb&fs)!=0},s.al=function(n){this.k=n},s.ri=function(n){FV(this,n)},s.Ib=function(){return Rz(this)},s.e=!1,s.n=0,v(Fn,"EStructuralFeatureImpl",451),m(335,451,{109:1,94:1,93:1,38:1,158:1,197:1,57:1,179:1,69:1,114:1,470:1,52:1,100:1,335:1,161:1,451:1,293:1,117:1,118:1,682:1},dX),s.Ih=function(n,t,i){var r,c;switch(n){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return me(this.s);case 5:return me(this.t);case 6:return Pn(),!!F0e(this);case 7:return Pn(),c=this.s,c>=1;case 8:return t?of(this):this.r;case 9:return this.q;case 10:return Pn(),(this.Bb&Rf)!=0;case 11:return Pn(),(this.Bb&R0)!=0;case 12:return Pn(),(this.Bb&Rp)!=0;case 13:return this.j;case 14:return f8(this);case 15:return Pn(),(this.Bb&fs)!=0;case 16:return Pn(),(this.Bb&wh)!=0;case 17:return lp(this);case 18:return Pn(),(this.Bb&$u)!=0;case 19:return t?FY(this):qPe(this)}return Il(this,n-ht((mn(),Mm)),Sn((r=u(Un(this,16),29),r||Mm),n),t,i)},s.Th=function(n){var t,i;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return F0e(this);case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&fw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&fw(this.q).i==0);case 10:return(this.Bb&Rf)==0;case 11:return(this.Bb&R0)!=0;case 12:return(this.Bb&Rp)!=0;case 13:return this.j!=null;case 14:return f8(this)!=null;case 15:return(this.Bb&fs)!=0;case 16:return(this.Bb&wh)!=0;case 17:return!!lp(this);case 18:return(this.Bb&$u)!=0;case 19:return!!qPe(this)}return Dl(this,n-ht((mn(),Mm)),Sn((t=u(Un(this,16),29),t||Mm),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return;case 1:FV(this,Dt(t));return;case 2:Td(this,$e(Pe(t)));return;case 3:xd(this,$e(Pe(t)));return;case 4:jd(this,u(t,15).a);return;case 5:gMe(this,u(t,15).a);return;case 8:Xb(this,u(t,143));return;case 9:r=Ia(this,u(t,87),null),r&&r.mj();return;case 10:X9(this,$e(Pe(t)));return;case 11:Y9(this,$e(Pe(t)));return;case 12:V9(this,$e(Pe(t)));return;case 13:Sse(this,Dt(t));return;case 15:K9(this,$e(Pe(t)));return;case 16:Q9(this,$e(Pe(t)));return;case 18:hQ(this,$e(Pe(t)));return}Bl(this,n-ht((mn(),Mm)),Sn((i=u(Un(this,16),29),i||Mm),n),t)},s.fi=function(){return mn(),Mm},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return;case 1:q(this.Cb,88)&&Cp(Ms(u(this.Cb,88)),4),Mo(this,null);return;case 2:Td(this,!0);return;case 3:xd(this,!0);return;case 4:jd(this,0);return;case 5:this.b=0,wp(this,1);return;case 8:Xb(this,null);return;case 9:i=Ia(this,null,null),i&&i.mj();return;case 10:X9(this,!0);return;case 11:Y9(this,!1);return;case 12:V9(this,!1);return;case 13:this.i=null,kB(this,null);return;case 15:K9(this,!1);return;case 16:Q9(this,!1);return;case 18:hQ(this,!1);return}Rl(this,n-ht((mn(),Mm)),Sn((t=u(Un(this,16),29),t||Mm),n))},s.mi=function(){FY(this),k9(Kc((ss(),ec),this)),of(this),this.Bb|=1},s.Hk=function(){return F0e(this)},s.Wk=function(n,t){return this.b=0,this.a=null,N1e(this,n,t)},s.Xk=function(n){gMe(this,n)},s.Ib=function(){var n;return(this.Db&64)!=0?Rz(this):(n=new nf(Rz(this)),n.a+=" (iD: ",hd(n,(this.Bb&$u)!=0),n.a+=")",n.a)},s.b=0,v(Fn,"EAttributeImpl",335),m(360,439,{109:1,94:1,93:1,143:1,158:1,197:1,57:1,114:1,52:1,100:1,360:1,161:1,117:1,118:1,681:1}),s.bl=function(n){return n.Ah()==this},s.xh=function(n){return UQ(this,n)},s.yh=function(n,t){this.w=null,this.Db=t<<16|this.Db&255,this.Cb=n},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return this.D!=null?this.D:this.B;case 3:return Tw(this);case 4:return this.gk();case 5:return this.F;case 6:return t?rl(this):S9(this);case 7:return!this.A&&(this.A=new is(zo,this,7)),this.A}return Il(this,n-ht(this.fi()),Sn((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),To(this.Ab,n,i);case 6:return this.Cb&&(i=(c=this.Db>>16,c>=0?UQ(this,i):this.Cb.Qh(this,-1-c,null,i))),ll(this,n,6,i)}return o=u(Sn((r=u(Un(this,16),29),r||this.fi()),t),69),o.uk().xk(this,_o(this),t-ht(this.fi()),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),mc(this.Ab,n,i);case 6:return ll(this,null,6,i);case 7:return!this.A&&(this.A=new is(zo,this,7)),mc(this.A,n,i)}return c=u(Sn((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,_o(this),t-ht(this.fi()),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.D!=null&&this.D==this.F;case 3:return!!Tw(this);case 4:return this.gk()!=null;case 5:return this.F!=null&&this.F!=this.D&&this.F!=this.B;case 6:return!!S9(this);case 7:return!!this.A&&this.A.i!=0}return Dl(this,n-ht(this.fi()),Sn((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return;case 1:PR(this,Dt(t));return;case 2:yK(this,Dt(t));return;case 5:p8(this,Dt(t));return;case 7:!this.A&&(this.A=new is(zo,this,7)),vt(this.A),!this.A&&(this.A=new is(zo,this,7)),er(this.A,u(t,18));return}Bl(this,n-ht(this.fi()),Sn((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return mn(),Dan},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return;case 1:q(this.Cb,184)&&(u(this.Cb,184).tb=null),Mo(this,null);return;case 2:J9(this,null),D9(this,this.D);return;case 5:p8(this,null);return;case 7:!this.A&&(this.A=new is(zo,this,7)),vt(this.A);return}Rl(this,n-ht(this.fi()),Sn((t=u(Un(this,16),29),t||this.fi()),n))},s.fk=function(){var n;return this.G==-1&&(this.G=(n=rl(this),n?Cd(n.si(),this):-1)),this.G},s.gk=function(){return null},s.hk=function(){return rl(this)},s.cl=function(){return this.v},s.ik=function(){return Tw(this)},s.jk=function(){return this.D!=null?this.D:this.B},s.kk=function(){return this.F},s.dk=function(n){return PZ(this,n)},s.dl=function(n){this.v=n},s.el=function(n){_Be(this,n)},s.fl=function(n){this.C=n},s.ri=function(n){PR(this,n)},s.Ib=function(){return XB(this)},s.C=null,s.D=null,s.G=-1,v(Fn,"EClassifierImpl",360),m(88,360,{109:1,94:1,93:1,29:1,143:1,158:1,197:1,57:1,114:1,52:1,100:1,88:1,360:1,161:1,471:1,117:1,118:1,681:1},Xk),s.bl=function(n){return V2n(this,n.Ah())},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return this.D!=null?this.D:this.B;case 3:return Tw(this);case 4:return null;case 5:return this.F;case 6:return t?rl(this):S9(this);case 7:return!this.A&&(this.A=new is(zo,this,7)),this.A;case 8:return Pn(),(this.Bb&256)!=0;case 9:return Pn(),(this.Bb&512)!=0;case 10:return tu(this);case 11:return!this.q&&(this.q=new we(pf,this,11,10)),this.q;case 12:return W3(this);case 13:return Wj(this);case 14:return Wj(this),this.r;case 15:return W3(this),this.k;case 16:return O0e(this);case 17:return zZ(this);case 18:return gh(this);case 19:return Cz(this);case 20:return W3(this),this.o;case 21:return!this.s&&(this.s=new we(es,this,21,17)),this.s;case 22:return Xu(this);case 23:return MZ(this)}return Il(this,n-ht((mn(),lb)),Sn((r=u(Un(this,16),29),r||lb),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),To(this.Ab,n,i);case 6:return this.Cb&&(i=(c=this.Db>>16,c>=0?UQ(this,i):this.Cb.Qh(this,-1-c,null,i))),ll(this,n,6,i);case 11:return!this.q&&(this.q=new we(pf,this,11,10)),To(this.q,n,i);case 21:return!this.s&&(this.s=new we(es,this,21,17)),To(this.s,n,i)}return o=u(Sn((r=u(Un(this,16),29),r||(mn(),lb)),t),69),o.uk().xk(this,_o(this),t-ht((mn(),lb)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),mc(this.Ab,n,i);case 6:return ll(this,null,6,i);case 7:return!this.A&&(this.A=new is(zo,this,7)),mc(this.A,n,i);case 11:return!this.q&&(this.q=new we(pf,this,11,10)),mc(this.q,n,i);case 21:return!this.s&&(this.s=new we(es,this,21,17)),mc(this.s,n,i);case 22:return mc(Xu(this),n,i)}return c=u(Sn((r=u(Un(this,16),29),r||(mn(),lb)),t),69),c.uk().yk(this,_o(this),t-ht((mn(),lb)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.D!=null&&this.D==this.F;case 3:return!!Tw(this);case 4:return!1;case 5:return this.F!=null&&this.F!=this.D&&this.F!=this.B;case 6:return!!S9(this);case 7:return!!this.A&&this.A.i!=0;case 8:return(this.Bb&256)!=0;case 9:return(this.Bb&512)!=0;case 10:return!!this.u&&Xu(this.u.a).i!=0&&!(this.n&&LQ(this.n));case 11:return!!this.q&&this.q.i!=0;case 12:return W3(this).i!=0;case 13:return Wj(this).i!=0;case 14:return Wj(this),this.r.i!=0;case 15:return W3(this),this.k.i!=0;case 16:return O0e(this).i!=0;case 17:return zZ(this).i!=0;case 18:return gh(this).i!=0;case 19:return Cz(this).i!=0;case 20:return W3(this),!!this.o;case 21:return!!this.s&&this.s.i!=0;case 22:return!!this.n&&LQ(this.n);case 23:return MZ(this).i!=0}return Dl(this,n-ht((mn(),lb)),Sn((t=u(Un(this,16),29),t||lb),n))},s.Wh=function(n){var t;return t=this.i==null||this.q&&this.q.i!=0?null:VO(this,n),t||vge(this,n)},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return;case 1:PR(this,Dt(t));return;case 2:yK(this,Dt(t));return;case 5:p8(this,Dt(t));return;case 7:!this.A&&(this.A=new is(zo,this,7)),vt(this.A),!this.A&&(this.A=new is(zo,this,7)),er(this.A,u(t,18));return;case 8:_1e(this,$e(Pe(t)));return;case 9:L1e(this,$e(Pe(t)));return;case 10:tS(tu(this)),er(tu(this),u(t,18));return;case 11:!this.q&&(this.q=new we(pf,this,11,10)),vt(this.q),!this.q&&(this.q=new we(pf,this,11,10)),er(this.q,u(t,18));return;case 21:!this.s&&(this.s=new we(es,this,21,17)),vt(this.s),!this.s&&(this.s=new we(es,this,21,17)),er(this.s,u(t,18));return;case 22:vt(Xu(this)),er(Xu(this),u(t,18));return}Bl(this,n-ht((mn(),lb)),Sn((i=u(Un(this,16),29),i||lb),n),t)},s.fi=function(){return mn(),lb},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return;case 1:q(this.Cb,184)&&(u(this.Cb,184).tb=null),Mo(this,null);return;case 2:J9(this,null),D9(this,this.D);return;case 5:p8(this,null);return;case 7:!this.A&&(this.A=new is(zo,this,7)),vt(this.A);return;case 8:_1e(this,!1);return;case 9:L1e(this,!1);return;case 10:this.u&&tS(this.u);return;case 11:!this.q&&(this.q=new we(pf,this,11,10)),vt(this.q);return;case 21:!this.s&&(this.s=new we(es,this,21,17)),vt(this.s);return;case 22:this.n&&vt(this.n);return}Rl(this,n-ht((mn(),lb)),Sn((t=u(Un(this,16),29),t||lb),n))},s.mi=function(){var n,t;if(W3(this),Wj(this),O0e(this),zZ(this),gh(this),Cz(this),MZ(this),lj(p3n(Ms(this))),this.s)for(n=0,t=this.s.i;n=0;--t)X(this,t);return ide(this,n)},s.Ek=function(){vt(this)},s.Xi=function(n,t){return uBe(this,n,t)},v($i,"EcoreEList",623),m(491,623,lu,DC),s.Ji=function(){return!1},s.Jj=function(){return this.c},s.Kj=function(){return!1},s.ml=function(){return!0},s.Qi=function(){return!0},s.Ui=function(n,t){return t},s.Wi=function(){return!1},s.c=0,v($i,"EObjectEList",491),m(81,491,lu,mr),s.Kj=function(){return!0},s.kl=function(){return!1},s.$k=function(){return!0},v($i,"EObjectContainmentEList",81),m(543,81,lu,L$),s.Li=function(){this.b=!0},s.Oj=function(){return this.b},s.Ek=function(){var n;vt(this),Bs(this.e)?(n=this.b,this.b=!1,fi(this.e,new Cf(this.e,2,this.c,n,!1))):this.b=!1},s.b=!1,v($i,"EObjectContainmentEList/Unsettable",543),m(1130,543,lu,xDe),s.Ri=function(n,t){var i,r;return i=u(xj(this,n,t),87),Bs(this.e)&&Ky(this,new eO(this.a,7,(mn(),Ian),me(t),(r=i.c,q(r,88)?u(r,29):vf),n)),i},s.Sj=function(n,t){return njn(this,u(n,87),t)},s.Tj=function(n,t){return tjn(this,u(n,87),t)},s.Uj=function(n,t,i){return iMn(this,u(n,87),u(t,87),i)},s.Gj=function(n,t,i,r,c){switch(n){case 3:return ij(this,n,t,i,r,this.i>1);case 5:return ij(this,n,t,i,r,this.i-u(i,16).gc()>0);default:return new O1(this.e,n,this.c,t,i,r,!0)}},s.Rj=function(){return!0},s.Oj=function(){return LQ(this)},s.Ek=function(){vt(this)},v(Fn,"EClassImpl/1",1130),m(1144,1143,qpe),s.bj=function(n){var t,i,r,c,o,l,f;if(i=n.ej(),i!=8){if(r=Bjn(n),r==0)switch(i){case 1:case 9:{f=n.ij(),f!=null&&(t=Ms(u(f,471)),!t.c&&(t.c=new Tl),uB(t.c,n.hj())),l=n.gj(),l!=null&&(c=u(l,471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Tl),kt(t.c,u(n.hj(),29))));break}case 3:{l=n.gj(),l!=null&&(c=u(l,471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Tl),kt(t.c,u(n.hj(),29))));break}case 5:{if(l=n.gj(),l!=null)for(o=u(l,18).Jc();o.Ob();)c=u(o.Pb(),471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Tl),kt(t.c,u(n.hj(),29)));break}case 4:{f=n.ij(),f!=null&&(c=u(f,471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Tl),uB(t.c,n.hj())));break}case 6:{if(f=n.ij(),f!=null)for(o=u(f,18).Jc();o.Ob();)c=u(o.Pb(),471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Tl),uB(t.c,n.hj()));break}}this.ol(r)}},s.ol=function(n){pXe(this,n)},s.b=63,v(Fn,"ESuperAdapter",1144),m(1145,1144,qpe,xSe),s.ol=function(n){Cp(this,n)},v(Fn,"EClassImpl/10",1145),m(1134,699,lu),s.Ci=function(n,t){return rZ(this,n,t)},s.Di=function(n){return QHe(this,n)},s.Ei=function(n,t){jO(this,n,t)},s.Fi=function(n){KC(this,n)},s.Yi=function(n){return she(this,n)},s.Vi=function(n,t){return vY(this,n,t)},s.Uk=function(n,t){throw P(new Ot)},s.Gi=function(){return new o4(this)},s.Hi=function(){return new vC(this)},s.Ii=function(n){return fO(this,n)},s.Vk=function(n,t){throw P(new Ot)},s.Dk=function(n){return this},s.Oj=function(){return this.i!=0},s.Wb=function(n){throw P(new Ot)},s.Ek=function(){throw P(new Ot)},v($i,"EcoreEList/UnmodifiableEList",1134),m(333,1134,lu,y3),s.Wi=function(){return!1},v($i,"EcoreEList/UnmodifiableEList/FastCompare",333),m(1137,333,lu,Mze),s.bd=function(n){var t,i,r;if(q(n,179)&&(t=u(n,179),i=t.Jj(),i!=-1)){for(r=this.i;i4)if(this.dk(n)){if(this.$k()){if(r=u(n,52),i=r.Bh(),f=i==this.b&&(this.kl()?r.vh(r.Ch(),u(Sn(Jo(this.b),this.Jj()).Fk(),29).ik())==xc(u(Sn(Jo(this.b),this.Jj()),19)).n:-1-r.Ch()==this.Jj()),this.ll()&&!f&&!i&&r.Gh()){for(c=0;c1||r==-1)):!1},s.kl=function(){var n,t,i;return t=Sn(Jo(this.b),this.Jj()),q(t,103)?(n=u(t,19),i=xc(n),!!i):!1},s.ll=function(){var n,t;return t=Sn(Jo(this.b),this.Jj()),q(t,103)?(n=u(t,19),(n.Bb&kc)!=0):!1},s.bd=function(n){var t,i,r,c;if(r=this.xj(n),r>=0)return r;if(this.ml()){for(i=0,c=this.Cj();i=0;--n)iN(this,n,this.vj(n));return this.Dj()},s.Oc=function(n){var t;if(this.ll())for(t=this.Cj()-1;t>=0;--t)iN(this,t,this.vj(t));return this.Ej(n)},s.Ek=function(){tS(this)},s.Xi=function(n,t){return O$e(this,n,t)},v($i,"DelegatingEcoreEList",744),m(1140,744,Vpe,zOe),s.oj=function(n,t){S2n(this,n,u(t,29))},s.pj=function(n){awn(this,u(n,29))},s.vj=function(n){var t,i;return t=u(X(Xu(this.a),n),87),i=t.c,q(i,88)?u(i,29):(mn(),vf)},s.Aj=function(n){var t,i;return t=u(Dp(Xu(this.a),n),87),i=t.c,q(i,88)?u(i,29):(mn(),vf)},s.Bj=function(n,t){return NSn(this,n,u(t,29))},s.Ji=function(){return!1},s.Gj=function(n,t,i,r,c){return null},s.qj=function(){return new NSe(this)},s.rj=function(){vt(Xu(this.a))},s.sj=function(n){return jFe(this,n)},s.tj=function(n){var t,i;for(i=n.Jc();i.Ob();)if(t=i.Pb(),!jFe(this,t))return!1;return!0},s.uj=function(n){var t,i,r;if(q(n,16)&&(r=u(n,16),r.gc()==Xu(this.a).i)){for(t=r.Jc(),i=new ct(this);t.Ob();)if(ue(t.Pb())!==ue(lt(i)))return!1;return!0}return!1},s.wj=function(){var n,t,i,r,c;for(i=1,t=new ct(Xu(this.a));t.e!=t.i.gc();)n=u(lt(t),87),r=(c=n.c,q(c,88)?u(c,29):(mn(),vf)),i=31*i+(r?cw(r):0);return i},s.xj=function(n){var t,i,r,c;for(r=0,i=new ct(Xu(this.a));i.e!=i.i.gc();){if(t=u(lt(i),87),ue(n)===ue((c=t.c,q(c,88)?u(c,29):(mn(),vf))))return r;++r}return-1},s.yj=function(){return Xu(this.a).i==0},s.zj=function(){return null},s.Cj=function(){return Xu(this.a).i},s.Dj=function(){var n,t,i,r,c,o;for(o=Xu(this.a).i,c=oe(Mr,xn,1,o,5,1),i=0,t=new ct(Xu(this.a));t.e!=t.i.gc();)n=u(lt(t),87),c[i++]=(r=n.c,q(r,88)?u(r,29):(mn(),vf));return c},s.Ej=function(n){var t,i,r,c,o,l,f;for(f=Xu(this.a).i,n.lengthf&&tr(n,f,null),r=0,i=new ct(Xu(this.a));i.e!=i.i.gc();)t=u(lt(i),87),o=(l=t.c,q(l,88)?u(l,29):(mn(),vf)),tr(n,r++,o);return n},s.Fj=function(){var n,t,i,r,c;for(c=new ad,c.a+="[",n=Xu(this.a),t=0,r=Xu(this.a).i;t>16,c>=0?UQ(this,i):this.Cb.Qh(this,-1-c,null,i))),ll(this,n,6,i);case 9:return!this.a&&(this.a=new we(V1,this,9,5)),To(this.a,n,i)}return o=u(Sn((r=u(Un(this,16),29),r||(mn(),fb)),t),69),o.uk().xk(this,_o(this),t-ht((mn(),fb)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),mc(this.Ab,n,i);case 6:return ll(this,null,6,i);case 7:return!this.A&&(this.A=new is(zo,this,7)),mc(this.A,n,i);case 9:return!this.a&&(this.a=new we(V1,this,9,5)),mc(this.a,n,i)}return c=u(Sn((r=u(Un(this,16),29),r||(mn(),fb)),t),69),c.uk().yk(this,_o(this),t-ht((mn(),fb)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.D!=null&&this.D==this.F;case 3:return!!Tw(this);case 4:return!!k1e(this);case 5:return this.F!=null&&this.F!=this.D&&this.F!=this.B;case 6:return!!S9(this);case 7:return!!this.A&&this.A.i!=0;case 8:return(this.Bb&256)==0;case 9:return!!this.a&&this.a.i!=0}return Dl(this,n-ht((mn(),fb)),Sn((t=u(Un(this,16),29),t||fb),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return;case 1:PR(this,Dt(t));return;case 2:yK(this,Dt(t));return;case 5:p8(this,Dt(t));return;case 7:!this.A&&(this.A=new is(zo,this,7)),vt(this.A),!this.A&&(this.A=new is(zo,this,7)),er(this.A,u(t,18));return;case 8:BB(this,$e(Pe(t)));return;case 9:!this.a&&(this.a=new we(V1,this,9,5)),vt(this.a),!this.a&&(this.a=new we(V1,this,9,5)),er(this.a,u(t,18));return}Bl(this,n-ht((mn(),fb)),Sn((i=u(Un(this,16),29),i||fb),n),t)},s.fi=function(){return mn(),fb},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return;case 1:q(this.Cb,184)&&(u(this.Cb,184).tb=null),Mo(this,null);return;case 2:J9(this,null),D9(this,this.D);return;case 5:p8(this,null);return;case 7:!this.A&&(this.A=new is(zo,this,7)),vt(this.A);return;case 8:BB(this,!0);return;case 9:!this.a&&(this.a=new we(V1,this,9,5)),vt(this.a);return}Rl(this,n-ht((mn(),fb)),Sn((t=u(Un(this,16),29),t||fb),n))},s.mi=function(){var n,t;if(this.a)for(n=0,t=this.a.i;n>16==5?u(this.Cb,675):null}return Il(this,n-ht((mn(),Xd)),Sn((r=u(Un(this,16),29),r||Xd),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),To(this.Ab,n,i);case 5:return this.Cb&&(i=(c=this.Db>>16,c>=0?SJe(this,i):this.Cb.Qh(this,-1-c,null,i))),ll(this,n,5,i)}return o=u(Sn((r=u(Un(this,16),29),r||(mn(),Xd)),t),69),o.uk().xk(this,_o(this),t-ht((mn(),Xd)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),mc(this.Ab,n,i);case 5:return ll(this,null,5,i)}return c=u(Sn((r=u(Un(this,16),29),r||(mn(),Xd)),t),69),c.uk().yk(this,_o(this),t-ht((mn(),Xd)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.d!=0;case 3:return!!this.b;case 4:return this.c!=null;case 5:return!!(this.Db>>16==5&&u(this.Cb,675))}return Dl(this,n-ht((mn(),Xd)),Sn((t=u(Un(this,16),29),t||Xd),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Dt(t));return;case 2:MY(this,u(t,15).a);return;case 3:Tqe(this,u(t,2001));return;case 4:xY(this,Dt(t));return}Bl(this,n-ht((mn(),Xd)),Sn((i=u(Un(this,16),29),i||Xd),n),t)},s.fi=function(){return mn(),Xd},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return;case 1:Mo(this,null);return;case 2:MY(this,0);return;case 3:Tqe(this,null);return;case 4:xY(this,null);return}Rl(this,n-ht((mn(),Xd)),Sn((t=u(Un(this,16),29),t||Xd),n))},s.Ib=function(){var n;return n=this.c,n??this.zb},s.b=null,s.c=null,s.d=0,v(Fn,"EEnumLiteralImpl",568);var HBn=Ji(Fn,"EFactoryImpl/InternalEDateTimeFormat");m(485,1,{2076:1},Gx),v(Fn,"EFactoryImpl/1ClientInternalEDateTimeFormat",485),m(248,118,{109:1,94:1,93:1,87:1,57:1,114:1,52:1,100:1,248:1,117:1,118:1},Zg),s.zh=function(n,t,i){var r;return i=ll(this,n,t,i),this.e&&q(n,179)&&(r=xz(this,this.e),r!=this.c&&(i=m8(this,r,i))),i},s.Ih=function(n,t,i){var r;switch(n){case 0:return this.f;case 1:return!this.d&&(this.d=new mr(Pc,this,1)),this.d;case 2:return t?zz(this):this.c;case 3:return this.b;case 4:return this.e;case 5:return t?RQ(this):this.a}return Il(this,n-ht((mn(),u2)),Sn((r=u(Un(this,16),29),r||u2),n),t,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return lFe(this,null,i);case 1:return!this.d&&(this.d=new mr(Pc,this,1)),mc(this.d,n,i);case 3:return sFe(this,null,i)}return c=u(Sn((r=u(Un(this,16),29),r||(mn(),u2)),t),69),c.uk().yk(this,_o(this),t-ht((mn(),u2)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.f;case 1:return!!this.d&&this.d.i!=0;case 2:return!!this.c;case 3:return!!this.b;case 4:return!!this.e;case 5:return!!this.a}return Dl(this,n-ht((mn(),u2)),Sn((t=u(Un(this,16),29),t||u2),n))},s.$h=function(n,t){var i;switch(n){case 0:JJe(this,u(t,87));return;case 1:!this.d&&(this.d=new mr(Pc,this,1)),vt(this.d),!this.d&&(this.d=new mr(Pc,this,1)),er(this.d,u(t,18));return;case 3:Qde(this,u(t,87));return;case 4:w0e(this,u(t,834));return;case 5:N9(this,u(t,143));return}Bl(this,n-ht((mn(),u2)),Sn((i=u(Un(this,16),29),i||u2),n),t)},s.fi=function(){return mn(),u2},s.hi=function(n){var t;switch(n){case 0:JJe(this,null);return;case 1:!this.d&&(this.d=new mr(Pc,this,1)),vt(this.d);return;case 3:Qde(this,null);return;case 4:w0e(this,null);return;case 5:N9(this,null);return}Rl(this,n-ht((mn(),u2)),Sn((t=u(Un(this,16),29),t||u2),n))},s.Ib=function(){var n;return n=new Ws(Lf(this)),n.a+=" (expression: ",qZ(this,n),n.a+=")",n.a};var H8e;v(Fn,"EGenericTypeImpl",248),m(2029,2024,UF),s.Ei=function(n,t){HOe(this,n,t)},s.Uk=function(n,t){return HOe(this,this.gc(),n),t},s.Yi=function(n){return Ku(this.nj(),n)},s.Gi=function(){return this.Hi()},s.nj=function(){return new LSe(this)},s.Hi=function(){return this.Ii(0)},s.Ii=function(n){return this.nj().dd(n)},s.Vk=function(n,t){return Ep(this,n,!0),t},s.Ri=function(n,t){var i,r;return r=VQ(this,t),i=this.dd(n),i.Rb(r),r},s.Si=function(n,t){var i;Ep(this,t,!0),i=this.dd(n),i.Rb(t)},v($i,"AbstractSequentialInternalEList",2029),m(482,2029,UF,yC),s.Yi=function(n){return Ku(this.nj(),n)},s.Gi=function(){return this.b==null?(gd(),gd(),WD):this.ql()},s.nj=function(){return new oCe(this.a,this.b)},s.Hi=function(){return this.b==null?(gd(),gd(),WD):this.ql()},s.Ii=function(n){var t,i;if(this.b==null){if(n<0||n>1)throw P(new ko(TS+n+", size=0"));return gd(),gd(),WD}for(i=this.ql(),t=0;t0;)if(t=this.c[--this.d],(!this.e||t.nk()!=D7||t.Jj()!=0)&&(!this.tl()||this.b.Uh(t))){if(o=this.b.Kh(t,this.sl()),this.f=(Tc(),u(t,69).vk()),this.f||t.Hk()){if(this.sl()?(r=u(o,16),this.k=r):(r=u(o,72),this.k=this.j=r),q(this.k,59)?(this.o=this.k.gc(),this.n=this.o):this.p=this.j?this.j.Ii(this.k.gc()):this.k.dd(this.k.gc()),this.p?FGe(this,this.p):QGe(this))return c=this.p?this.p.Ub():this.j?this.j.Yi(--this.n):this.k.Xb(--this.n),this.f?(n=u(c,75),n.Jk(),i=n.kd(),this.i=i):(i=c,this.i=i),this.g=-3,!0}else if(o!=null)return this.k=null,this.p=null,i=o,this.i=i,this.g=-2,!0}return this.k=null,this.p=null,this.g=-1,!1}else return c=this.p?this.p.Ub():this.j?this.j.Yi(--this.n):this.k.Xb(--this.n),this.f?(n=u(c,75),n.Jk(),i=n.kd(),this.i=i):(i=c,this.i=i),this.g=-3,!0}},s.Pb=function(){return xB(this)},s.Tb=function(){return this.a},s.Ub=function(){var n;if(this.g<-1||this.Sb())return--this.a,this.g=0,n=this.i,this.Sb(),n;throw P(new fu)},s.Vb=function(){return this.a-1},s.Qb=function(){throw P(new Ot)},s.sl=function(){return!1},s.Wb=function(n){throw P(new Ot)},s.tl=function(){return!0},s.a=0,s.d=0,s.f=!1,s.g=0,s.n=0,s.o=0;var WD;v($i,"EContentsEList/FeatureIteratorImpl",287),m(700,287,XF,sle),s.sl=function(){return!0},v($i,"EContentsEList/ResolvingFeatureIteratorImpl",700),m(1147,700,XF,SOe),s.tl=function(){return!1},v(Fn,"ENamedElementImpl/1/1",1147),m(1148,287,XF,AOe),s.tl=function(){return!1},v(Fn,"ENamedElementImpl/1/2",1148),m(39,151,PN,dp,ZV,Dr,hY,O1,Cf,yhe,fLe,khe,aLe,Lae,hLe,She,dLe,Pae,bLe,Ehe,gLe,YE,eO,DV,jhe,wLe,$ae,pLe),s.Ij=function(){return the(this)},s.Pj=function(){var n;return n=the(this),n?n.gk():null},s.fj=function(n){return this.b==-1&&this.a&&(this.b=this.c.Eh(this.a.Jj(),this.a.nk())),this.c.vh(this.b,n)},s.hj=function(){return this.c},s.Qj=function(){var n;return n=the(this),n?n.rk():!1},s.b=-1,v(Fn,"ENotificationImpl",39),m(403,293,{109:1,94:1,93:1,158:1,197:1,57:1,62:1,114:1,470:1,52:1,100:1,161:1,403:1,293:1,117:1,118:1},bX),s.xh=function(n){return MJe(this,n)},s.Ih=function(n,t,i){var r,c,o;switch(n){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return me(this.s);case 5:return me(this.t);case 6:return Pn(),o=this.t,o>1||o==-1;case 7:return Pn(),c=this.s,c>=1;case 8:return t?of(this):this.r;case 9:return this.q;case 10:return this.Db>>16==10?u(this.Cb,29):null;case 11:return!this.d&&(this.d=new is(zo,this,11)),this.d;case 12:return!this.c&&(this.c=new we(c2,this,12,10)),this.c;case 13:return!this.a&&(this.a=new SC(this,this)),this.a;case 14:return xs(this)}return Il(this,n-ht((mn(),Kd)),Sn((r=u(Un(this,16),29),r||Kd),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),To(this.Ab,n,i);case 10:return this.Cb&&(i=(c=this.Db>>16,c>=0?MJe(this,i):this.Cb.Qh(this,-1-c,null,i))),ll(this,n,10,i);case 12:return!this.c&&(this.c=new we(c2,this,12,10)),To(this.c,n,i)}return o=u(Sn((r=u(Un(this,16),29),r||(mn(),Kd)),t),69),o.uk().xk(this,_o(this),t-ht((mn(),Kd)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),mc(this.Ab,n,i);case 9:return mV(this,i);case 10:return ll(this,null,10,i);case 11:return!this.d&&(this.d=new is(zo,this,11)),mc(this.d,n,i);case 12:return!this.c&&(this.c=new we(c2,this,12,10)),mc(this.c,n,i);case 14:return mc(xs(this),n,i)}return c=u(Sn((r=u(Un(this,16),29),r||(mn(),Kd)),t),69),c.uk().yk(this,_o(this),t-ht((mn(),Kd)),n,i)},s.Th=function(n){var t,i,r;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return r=this.t,r>1||r==-1;case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&fw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&fw(this.q).i==0);case 10:return!!(this.Db>>16==10&&u(this.Cb,29));case 11:return!!this.d&&this.d.i!=0;case 12:return!!this.c&&this.c.i!=0;case 13:return!!this.a&&xs(this.a.a).i!=0&&!(this.b&&PQ(this.b));case 14:return!!this.b&&PQ(this.b)}return Dl(this,n-ht((mn(),Kd)),Sn((t=u(Un(this,16),29),t||Kd),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Dt(t));return;case 2:Td(this,$e(Pe(t)));return;case 3:xd(this,$e(Pe(t)));return;case 4:jd(this,u(t,15).a);return;case 5:wp(this,u(t,15).a);return;case 8:Xb(this,u(t,143));return;case 9:r=Ia(this,u(t,87),null),r&&r.mj();return;case 11:!this.d&&(this.d=new is(zo,this,11)),vt(this.d),!this.d&&(this.d=new is(zo,this,11)),er(this.d,u(t,18));return;case 12:!this.c&&(this.c=new we(c2,this,12,10)),vt(this.c),!this.c&&(this.c=new we(c2,this,12,10)),er(this.c,u(t,18));return;case 13:!this.a&&(this.a=new SC(this,this)),tS(this.a),!this.a&&(this.a=new SC(this,this)),er(this.a,u(t,18));return;case 14:vt(xs(this)),er(xs(this),u(t,18));return}Bl(this,n-ht((mn(),Kd)),Sn((i=u(Un(this,16),29),i||Kd),n),t)},s.fi=function(){return mn(),Kd},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return;case 1:Mo(this,null);return;case 2:Td(this,!0);return;case 3:xd(this,!0);return;case 4:jd(this,0);return;case 5:wp(this,1);return;case 8:Xb(this,null);return;case 9:i=Ia(this,null,null),i&&i.mj();return;case 11:!this.d&&(this.d=new is(zo,this,11)),vt(this.d);return;case 12:!this.c&&(this.c=new we(c2,this,12,10)),vt(this.c);return;case 13:this.a&&tS(this.a);return;case 14:this.b&&vt(this.b);return}Rl(this,n-ht((mn(),Kd)),Sn((t=u(Un(this,16),29),t||Kd),n))},s.mi=function(){var n,t;if(this.c)for(n=0,t=this.c.i;nf&&tr(n,f,null),r=0,i=new ct(xs(this.a));i.e!=i.i.gc();)t=u(lt(i),87),o=(l=t.c,l||(mn(),Ya)),tr(n,r++,o);return n},s.Fj=function(){var n,t,i,r,c;for(c=new ad,c.a+="[",n=xs(this.a),t=0,r=xs(this.a).i;t1);case 5:return ij(this,n,t,i,r,this.i-u(i,16).gc()>0);default:return new O1(this.e,n,this.c,t,i,r,!0)}},s.Rj=function(){return!0},s.Oj=function(){return PQ(this)},s.Ek=function(){vt(this)},v(Fn,"EOperationImpl/2",1331),m(493,1,{1999:1,493:1},zxe),v(Fn,"EPackageImpl/1",493),m(14,81,lu,we),s.gl=function(){return this.d},s.hl=function(){return this.b},s.kl=function(){return!0},s.b=0,v($i,"EObjectContainmentWithInverseEList",14),m(361,14,lu,l4),s.ll=function(){return!0},s.Ui=function(n,t){return q4(this,n,u(t,57))},v($i,"EObjectContainmentWithInverseEList/Resolving",361),m(312,361,lu,rp),s.Li=function(){this.a.tb=null},v(Fn,"EPackageImpl/2",312),m(1243,1,{},js),v(Fn,"EPackageImpl/3",1243),m(721,44,iv,aoe),s._b=function(n){return Pr(n)?IV(this,n):!!Uc(this.f,n)},v(Fn,"EPackageRegistryImpl",721),m(503,293,{109:1,94:1,93:1,158:1,197:1,57:1,2078:1,114:1,470:1,52:1,100:1,161:1,503:1,293:1,117:1,118:1},gX),s.xh=function(n){return TJe(this,n)},s.Ih=function(n,t,i){var r,c,o;switch(n){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return me(this.s);case 5:return me(this.t);case 6:return Pn(),o=this.t,o>1||o==-1;case 7:return Pn(),c=this.s,c>=1;case 8:return t?of(this):this.r;case 9:return this.q;case 10:return this.Db>>16==10?u(this.Cb,62):null}return Il(this,n-ht((mn(),Tm)),Sn((r=u(Un(this,16),29),r||Tm),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),To(this.Ab,n,i);case 10:return this.Cb&&(i=(c=this.Db>>16,c>=0?TJe(this,i):this.Cb.Qh(this,-1-c,null,i))),ll(this,n,10,i)}return o=u(Sn((r=u(Un(this,16),29),r||(mn(),Tm)),t),69),o.uk().xk(this,_o(this),t-ht((mn(),Tm)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),mc(this.Ab,n,i);case 9:return mV(this,i);case 10:return ll(this,null,10,i)}return c=u(Sn((r=u(Un(this,16),29),r||(mn(),Tm)),t),69),c.uk().yk(this,_o(this),t-ht((mn(),Tm)),n,i)},s.Th=function(n){var t,i,r;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return r=this.t,r>1||r==-1;case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&fw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&fw(this.q).i==0);case 10:return!!(this.Db>>16==10&&u(this.Cb,62))}return Dl(this,n-ht((mn(),Tm)),Sn((t=u(Un(this,16),29),t||Tm),n))},s.fi=function(){return mn(),Tm},v(Fn,"EParameterImpl",503),m(103,451,{109:1,94:1,93:1,158:1,197:1,57:1,19:1,179:1,69:1,114:1,470:1,52:1,100:1,161:1,103:1,451:1,293:1,117:1,118:1,682:1},hle),s.Ih=function(n,t,i){var r,c,o,l;switch(n){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return me(this.s);case 5:return me(this.t);case 6:return Pn(),l=this.t,l>1||l==-1;case 7:return Pn(),c=this.s,c>=1;case 8:return t?of(this):this.r;case 9:return this.q;case 10:return Pn(),(this.Bb&Rf)!=0;case 11:return Pn(),(this.Bb&R0)!=0;case 12:return Pn(),(this.Bb&Rp)!=0;case 13:return this.j;case 14:return f8(this);case 15:return Pn(),(this.Bb&fs)!=0;case 16:return Pn(),(this.Bb&wh)!=0;case 17:return lp(this);case 18:return Pn(),(this.Bb&$u)!=0;case 19:return Pn(),o=xc(this),!!(o&&(o.Bb&$u)!=0);case 20:return Pn(),(this.Bb&kc)!=0;case 21:return t?xc(this):this.b;case 22:return t?r1e(this):_Pe(this);case 23:return!this.a&&(this.a=new M3(Sm,this,23)),this.a}return Il(this,n-ht((mn(),Fv)),Sn((r=u(Un(this,16),29),r||Fv),n),t,i)},s.Th=function(n){var t,i,r,c;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return c=this.t,c>1||c==-1;case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&fw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&fw(this.q).i==0);case 10:return(this.Bb&Rf)==0;case 11:return(this.Bb&R0)!=0;case 12:return(this.Bb&Rp)!=0;case 13:return this.j!=null;case 14:return f8(this)!=null;case 15:return(this.Bb&fs)!=0;case 16:return(this.Bb&wh)!=0;case 17:return!!lp(this);case 18:return(this.Bb&$u)!=0;case 19:return r=xc(this),!!r&&(r.Bb&$u)!=0;case 20:return(this.Bb&kc)==0;case 21:return!!this.b;case 22:return!!_Pe(this);case 23:return!!this.a&&this.a.i!=0}return Dl(this,n-ht((mn(),Fv)),Sn((t=u(Un(this,16),29),t||Fv),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return;case 1:FV(this,Dt(t));return;case 2:Td(this,$e(Pe(t)));return;case 3:xd(this,$e(Pe(t)));return;case 4:jd(this,u(t,15).a);return;case 5:wp(this,u(t,15).a);return;case 8:Xb(this,u(t,143));return;case 9:r=Ia(this,u(t,87),null),r&&r.mj();return;case 10:X9(this,$e(Pe(t)));return;case 11:Y9(this,$e(Pe(t)));return;case 12:V9(this,$e(Pe(t)));return;case 13:Sse(this,Dt(t));return;case 15:K9(this,$e(Pe(t)));return;case 16:Q9(this,$e(Pe(t)));return;case 18:j4n(this,$e(Pe(t)));return;case 20:F1e(this,$e(Pe(t)));return;case 21:Bhe(this,u(t,19));return;case 23:!this.a&&(this.a=new M3(Sm,this,23)),vt(this.a),!this.a&&(this.a=new M3(Sm,this,23)),er(this.a,u(t,18));return}Bl(this,n-ht((mn(),Fv)),Sn((i=u(Un(this,16),29),i||Fv),n),t)},s.fi=function(){return mn(),Fv},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return;case 1:q(this.Cb,88)&&Cp(Ms(u(this.Cb,88)),4),Mo(this,null);return;case 2:Td(this,!0);return;case 3:xd(this,!0);return;case 4:jd(this,0);return;case 5:wp(this,1);return;case 8:Xb(this,null);return;case 9:i=Ia(this,null,null),i&&i.mj();return;case 10:X9(this,!0);return;case 11:Y9(this,!1);return;case 12:V9(this,!1);return;case 13:this.i=null,kB(this,null);return;case 15:K9(this,!1);return;case 16:Q9(this,!1);return;case 18:H1e(this,!1),q(this.Cb,88)&&Cp(Ms(u(this.Cb,88)),2);return;case 20:F1e(this,!0);return;case 21:Bhe(this,null);return;case 23:!this.a&&(this.a=new M3(Sm,this,23)),vt(this.a);return}Rl(this,n-ht((mn(),Fv)),Sn((t=u(Un(this,16),29),t||Fv),n))},s.mi=function(){r1e(this),k9(Kc((ss(),ec),this)),of(this),this.Bb|=1},s.sk=function(){return xc(this)},s.Zk=function(){var n;return n=xc(this),!!n&&(n.Bb&$u)!=0},s.$k=function(){return(this.Bb&$u)!=0},s._k=function(){return(this.Bb&kc)!=0},s.Wk=function(n,t){return this.c=null,N1e(this,n,t)},s.Ib=function(){var n;return(this.Db&64)!=0?Rz(this):(n=new nf(Rz(this)),n.a+=" (containment: ",hd(n,(this.Bb&$u)!=0),n.a+=", resolveProxies: ",hd(n,(this.Bb&kc)!=0),n.a+=")",n.a)},v(Fn,"EReferenceImpl",103),m(549,118,{109:1,45:1,94:1,93:1,136:1,57:1,114:1,52:1,100:1,549:1,117:1,118:1},Nh),s.Fb=function(n){return this===n},s.jd=function(){return this.b},s.kd=function(){return this.c},s.Hb=function(){return cw(this)},s.Ai=function(n){B3n(this,Dt(n))},s.ld=function(n){return x3n(this,Dt(n))},s.Ih=function(n,t,i){var r;switch(n){case 0:return this.b;case 1:return this.c}return Il(this,n-ht((mn(),Sc)),Sn((r=u(Un(this,16),29),r||Sc),n),t,i)},s.Th=function(n){var t;switch(n){case 0:return this.b!=null;case 1:return this.c!=null}return Dl(this,n-ht((mn(),Sc)),Sn((t=u(Un(this,16),29),t||Sc),n))},s.$h=function(n,t){var i;switch(n){case 0:z3n(this,Dt(t));return;case 1:Ihe(this,Dt(t));return}Bl(this,n-ht((mn(),Sc)),Sn((i=u(Un(this,16),29),i||Sc),n),t)},s.fi=function(){return mn(),Sc},s.hi=function(n){var t;switch(n){case 0:Phe(this,null);return;case 1:Ihe(this,null);return}Rl(this,n-ht((mn(),Sc)),Sn((t=u(Un(this,16),29),t||Sc),n))},s.yi=function(){var n;return this.a==-1&&(n=this.b,this.a=n==null?0:Sd(n)),this.a},s.zi=function(n){this.a=n},s.Ib=function(){var n;return(this.Db&64)!=0?Lf(this):(n=new nf(Lf(this)),n.a+=" (key: ",$c(n,this.b),n.a+=", value: ",$c(n,this.c),n.a+=")",n.a)},s.a=-1,s.b=null,s.c=null;var Nu=v(Fn,"EStringToStringMapEntryImpl",549),Jan=Ji($i,"FeatureMap/Entry/Internal");m(562,1,KF),s.vl=function(n){return this.wl(u(n,52))},s.wl=function(n){return this.vl(n)},s.Fb=function(n){var t,i;return this===n?!0:q(n,75)?(t=u(n,75),t.Jk()==this.c?(i=this.kd(),i==null?t.kd()==null:ai(i,t.kd())):!1):!1},s.Jk=function(){return this.c},s.Hb=function(){var n;return n=this.kd(),Oi(this.c)^(n==null?0:Oi(n))},s.Ib=function(){var n,t;return n=this.c,t=rl(n.ok()).vi(),n.ve(),(t!=null&&t.length!=0?t+":"+n.ve():n.ve())+"="+this.kd()},v(Fn,"EStructuralFeatureImpl/BasicFeatureMapEntry",562),m(777,562,KF,mle),s.wl=function(n){return new mle(this.c,n)},s.kd=function(){return this.a},s.xl=function(n,t,i){return a7n(this,n,this.a,t,i)},s.yl=function(n,t,i){return h7n(this,n,this.a,t,i)},v(Fn,"EStructuralFeatureImpl/ContainmentUpdatingFeatureMapEntry",777),m(1304,1,{},Fxe),s.wk=function(n,t,i,r,c){var o;return o=u(T9(n,this.b),219),o.Wl(this.a).Dk(r)},s.xk=function(n,t,i,r,c){var o;return o=u(T9(n,this.b),219),o.Nl(this.a,r,c)},s.yk=function(n,t,i,r,c){var o;return o=u(T9(n,this.b),219),o.Ol(this.a,r,c)},s.zk=function(n,t,i){var r;return r=u(T9(n,this.b),219),r.Wl(this.a).Oj()},s.Ak=function(n,t,i,r){var c;c=u(T9(n,this.b),219),c.Wl(this.a).Wb(r)},s.Bk=function(n,t,i){return u(T9(n,this.b),219).Wl(this.a)},s.Ck=function(n,t,i){var r;r=u(T9(n,this.b),219),r.Wl(this.a).Ek()},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateFeatureMapDelegator",1304),m(89,1,{},pd,_b,vd,zb),s.wk=function(n,t,i,r,c){var o;if(o=t.ii(i),o==null&&t.ji(i,o=Yz(this,n)),!c)switch(this.e){case 50:case 41:return u(o,586)._j();case 40:return u(o,219).Tl()}return o},s.xk=function(n,t,i,r,c){var o,l;return l=t.ii(i),l==null&&t.ji(i,l=Yz(this,n)),o=u(l,72).Uk(r,c),o},s.yk=function(n,t,i,r,c){var o;return o=t.ii(i),o!=null&&(c=u(o,72).Vk(r,c)),c},s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null&&u(r,77).Oj()},s.Ak=function(n,t,i,r){var c;c=u(t.ii(i),77),!c&&t.ji(i,c=Yz(this,n)),c.Wb(r)},s.Bk=function(n,t,i){var r,c;return c=t.ii(i),c==null&&t.ji(i,c=Yz(this,n)),q(c,77)?u(c,77):(r=u(t.ii(i),16),new ISe(r))},s.Ck=function(n,t,i){var r;r=u(t.ii(i),77),!r&&t.ji(i,r=Yz(this,n)),r.Ek()},s.b=0,s.e=0,v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateMany",89),m(498,1,{}),s.xk=function(n,t,i,r,c){throw P(new Ot)},s.yk=function(n,t,i,r,c){throw P(new Ot)},s.Bk=function(n,t,i){return new RIe(this,n,t,i)};var o1;v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingle",498),m(1321,1,Cne,RIe),s.Dk=function(n){return this.a.wk(this.c,this.d,this.b,n,!0)},s.Oj=function(){return this.a.zk(this.c,this.d,this.b)},s.Wb=function(n){this.a.Ak(this.c,this.d,this.b,n)},s.Ek=function(){this.a.Ck(this.c,this.d,this.b)},s.b=0,v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingle/1",1321),m(770,498,{},cae),s.wk=function(n,t,i,r,c){return DZ(n,n.Mh(),n.Ch())==this.b?this._k()&&r?vZ(n):n.Mh():null},s.xk=function(n,t,i,r,c){var o,l;return n.Mh()&&(c=(o=n.Ch(),o>=0?n.xh(c):n.Mh().Qh(n,-1-o,null,c))),l=Fi(n.Ah(),this.e),n.zh(r,l,c)},s.yk=function(n,t,i,r,c){var o;return o=Fi(n.Ah(),this.e),n.zh(null,o,c)},s.zk=function(n,t,i){var r;return r=Fi(n.Ah(),this.e),!!n.Mh()&&n.Ch()==r},s.Ak=function(n,t,i,r){var c,o,l,f,h;if(r!=null&&!PZ(this.a,r))throw P(new Vy(VF+(q(r,57)?Yde(u(r,57).Ah()):whe(Gs(r)))+YF+this.a+"'"));if(c=n.Mh(),l=Fi(n.Ah(),this.e),ue(r)!==ue(c)||n.Ch()!=l&&r!=null){if(t8(n,u(r,57)))throw P(new Gn(AS+n.Ib()));h=null,c&&(h=(o=n.Ch(),o>=0?n.xh(h):n.Mh().Qh(n,-1-o,null,h))),f=u(r,52),f&&(h=f.Oh(n,Fi(f.Ah(),this.b),null,h)),h=n.zh(f,l,h),h&&h.mj()}else n.sh()&&n.th()&&fi(n,new Dr(n,1,l,r,r))},s.Ck=function(n,t,i){var r,c,o,l;r=n.Mh(),r?(l=(c=n.Ch(),c>=0?n.xh(null):n.Mh().Qh(n,-1-c,null,null)),o=Fi(n.Ah(),this.e),l=n.zh(null,o,l),l&&l.mj()):n.sh()&&n.th()&&fi(n,new YE(n,1,this.e,null,null))},s._k=function(){return!1},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleContainer",770),m(1305,770,{},ONe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleContainerResolving",1305),m(560,498,{}),s.wk=function(n,t,i,r,c){var o;return o=t.ii(i),o==null?this.b:ue(o)===ue(o1)?null:o},s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null&&(ue(r)===ue(o1)||!ai(r,this.b))},s.Ak=function(n,t,i,r){var c,o;n.sh()&&n.th()?(c=(o=t.ii(i),o==null?this.b:ue(o)===ue(o1)?null:o),r==null?this.c!=null?(t.ji(i,null),r=this.b):this.b!=null?t.ji(i,o1):t.ji(i,null):(this.zl(r),t.ji(i,r)),fi(n,this.d.Al(n,1,this.e,c,r))):r==null?this.c!=null?t.ji(i,null):this.b!=null?t.ji(i,o1):t.ji(i,null):(this.zl(r),t.ji(i,r))},s.Ck=function(n,t,i){var r,c;n.sh()&&n.th()?(r=(c=t.ii(i),c==null?this.b:ue(c)===ue(o1)?null:c),t.ki(i),fi(n,this.d.Al(n,1,this.e,r,this.b))):t.ki(i)},s.zl=function(n){throw P(new JSe)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData",560),m(hv,1,{},qg),s.Al=function(n,t,i,r,c){return new YE(n,t,i,r,c)},s.Bl=function(n,t,i,r,c,o){return new DV(n,t,i,r,c,o)};var J8e,G8e,q8e,U8e,X8e,K8e,V8e,$ce,Y8e;v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator",hv),m(1322,hv,{},yL),s.Al=function(n,t,i,r,c){return new $ae(n,t,i,$e(Pe(r)),$e(Pe(c)))},s.Bl=function(n,t,i,r,c,o){return new pLe(n,t,i,$e(Pe(r)),$e(Pe(c)),o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/1",1322),m(1323,hv,{},kL),s.Al=function(n,t,i,r,c){return new yhe(n,t,i,u(r,221).a,u(c,221).a)},s.Bl=function(n,t,i,r,c,o){return new fLe(n,t,i,u(r,221).a,u(c,221).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/2",1323),m(1324,hv,{},EL),s.Al=function(n,t,i,r,c){return new khe(n,t,i,u(r,180).a,u(c,180).a)},s.Bl=function(n,t,i,r,c,o){return new aLe(n,t,i,u(r,180).a,u(c,180).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/3",1324),m(1325,hv,{},sU),s.Al=function(n,t,i,r,c){return new Lae(n,t,i,W(ie(r)),W(ie(c)))},s.Bl=function(n,t,i,r,c,o){return new hLe(n,t,i,W(ie(r)),W(ie(c)),o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/4",1325),m(1326,hv,{},Dk),s.Al=function(n,t,i,r,c){return new She(n,t,i,u(r,164).a,u(c,164).a)},s.Bl=function(n,t,i,r,c,o){return new dLe(n,t,i,u(r,164).a,u(c,164).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/5",1326),m(1327,hv,{},Dh),s.Al=function(n,t,i,r,c){return new Pae(n,t,i,u(r,15).a,u(c,15).a)},s.Bl=function(n,t,i,r,c,o){return new bLe(n,t,i,u(r,15).a,u(c,15).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/6",1327),m(1328,hv,{},r0),s.Al=function(n,t,i,r,c){return new Ehe(n,t,i,u(r,190).a,u(c,190).a)},s.Bl=function(n,t,i,r,c,o){return new gLe(n,t,i,u(r,190).a,u(c,190).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/7",1328),m(1329,hv,{},jL),s.Al=function(n,t,i,r,c){return new jhe(n,t,i,u(r,191).a,u(c,191).a)},s.Bl=function(n,t,i,r,c,o){return new wLe(n,t,i,u(r,191).a,u(c,191).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/8",1329),m(1307,560,{},HIe),s.zl=function(n){if(!this.a.dk(n))throw P(new Vy(VF+Gs(n)+YF+this.a+"'"))},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataDynamic",1307),m(1308,560,{},MDe),s.zl=function(n){},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataStatic",1308),m(771,560,{}),s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null},s.Ak=function(n,t,i,r){var c,o;n.sh()&&n.th()?(c=!0,o=t.ii(i),o==null?(c=!1,o=this.b):ue(o)===ue(o1)&&(o=null),r==null?this.c!=null?(t.ji(i,null),r=this.b):t.ji(i,o1):(this.zl(r),t.ji(i,r)),fi(n,this.d.Bl(n,1,this.e,o,r,!c))):r==null?this.c!=null?t.ji(i,null):t.ji(i,o1):(this.zl(r),t.ji(i,r))},s.Ck=function(n,t,i){var r,c;n.sh()&&n.th()?(r=!0,c=t.ii(i),c==null?(r=!1,c=this.b):ue(c)===ue(o1)&&(c=null),t.ki(i),fi(n,this.d.Bl(n,2,this.e,c,this.b,r))):t.ki(i)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettable",771),m(1309,771,{},JIe),s.zl=function(n){if(!this.a.dk(n))throw P(new Vy(VF+Gs(n)+YF+this.a+"'"))},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettableDynamic",1309),m(1310,771,{},TDe),s.zl=function(n){},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettableStatic",1310),m(402,498,{},uR),s.wk=function(n,t,i,r,c){var o,l,f,h,b;if(b=t.ii(i),this.rk()&&ue(b)===ue(o1))return null;if(this._k()&&r&&b!=null){if(f=u(b,52),f.Sh()&&(h=C0(n,f),f!=h)){if(!PZ(this.a,h))throw P(new Vy(VF+Gs(h)+YF+this.a+"'"));t.ji(i,b=h),this.$k()&&(o=u(h,52),l=f.Qh(n,this.b?Fi(f.Ah(),this.b):-1-Fi(n.Ah(),this.e),null,null),!o.Mh()&&(l=o.Oh(n,this.b?Fi(o.Ah(),this.b):-1-Fi(n.Ah(),this.e),null,l)),l&&l.mj()),n.sh()&&n.th()&&fi(n,new YE(n,9,this.e,f,h))}return b}else return b},s.xk=function(n,t,i,r,c){var o,l;return l=t.ii(i),ue(l)===ue(o1)&&(l=null),t.ji(i,r),this.Kj()?ue(l)!==ue(r)&&l!=null&&(o=u(l,52),c=o.Qh(n,Fi(o.Ah(),this.b),null,c)):this.$k()&&l!=null&&(c=u(l,52).Qh(n,-1-Fi(n.Ah(),this.e),null,c)),n.sh()&&n.th()&&(!c&&(c=new f0(4)),c.lj(new YE(n,1,this.e,l,r))),c},s.yk=function(n,t,i,r,c){var o;return o=t.ii(i),ue(o)===ue(o1)&&(o=null),t.ki(i),n.sh()&&n.th()&&(!c&&(c=new f0(4)),this.rk()?c.lj(new YE(n,2,this.e,o,null)):c.lj(new YE(n,1,this.e,o,null))),c},s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null},s.Ak=function(n,t,i,r){var c,o,l,f,h;if(r!=null&&!PZ(this.a,r))throw P(new Vy(VF+(q(r,57)?Yde(u(r,57).Ah()):whe(Gs(r)))+YF+this.a+"'"));h=t.ii(i),f=h!=null,this.rk()&&ue(h)===ue(o1)&&(h=null),l=null,this.Kj()?ue(h)!==ue(r)&&(h!=null&&(c=u(h,52),l=c.Qh(n,Fi(c.Ah(),this.b),null,l)),r!=null&&(c=u(r,52),l=c.Oh(n,Fi(c.Ah(),this.b),null,l))):this.$k()&&ue(h)!==ue(r)&&(h!=null&&(l=u(h,52).Qh(n,-1-Fi(n.Ah(),this.e),null,l)),r!=null&&(l=u(r,52).Oh(n,-1-Fi(n.Ah(),this.e),null,l))),r==null&&this.rk()?t.ji(i,o1):t.ji(i,r),n.sh()&&n.th()?(o=new DV(n,1,this.e,h,r,this.rk()&&!f),l?(l.lj(o),l.mj()):fi(n,o)):l&&l.mj()},s.Ck=function(n,t,i){var r,c,o,l,f;f=t.ii(i),l=f!=null,this.rk()&&ue(f)===ue(o1)&&(f=null),o=null,f!=null&&(this.Kj()?(r=u(f,52),o=r.Qh(n,Fi(r.Ah(),this.b),null,o)):this.$k()&&(o=u(f,52).Qh(n,-1-Fi(n.Ah(),this.e),null,o))),t.ki(i),n.sh()&&n.th()?(c=new DV(n,this.rk()?2:1,this.e,f,null,l),o?(o.lj(c),o.mj()):fi(n,c)):o&&o.mj()},s.Kj=function(){return!1},s.$k=function(){return!1},s._k=function(){return!1},s.rk=function(){return!1},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObject",402),m(561,402,{},IK),s.$k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainment",561),m(1313,561,{},TOe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentResolving",1313),m(773,561,{},lle),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentUnsettable",773),m(1315,773,{},xOe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentUnsettableResolving",1315),m(638,561,{},XK),s.Kj=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverse",638),m(1314,638,{},NNe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseResolving",1314),m(774,638,{},Gle),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseUnsettable",774),m(1316,774,{},DNe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseUnsettableResolving",1316),m(639,402,{},fle),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolving",639),m(1317,639,{},COe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingUnsettable",1317),m(775,639,{},qle),s.Kj=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingWithInverse",775),m(1318,775,{},INe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingWithInverseUnsettable",1318),m(1311,402,{},OOe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectUnsettable",1311),m(772,402,{},Ule),s.Kj=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectWithInverse",772),m(1312,772,{},_Ne),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectWithInverseUnsettable",1312),m(776,562,KF,Hfe),s.wl=function(n){return new Hfe(this.a,this.c,n)},s.kd=function(){return this.b},s.xl=function(n,t,i){return s9n(this,n,this.b,i)},s.yl=function(n,t,i){return l9n(this,n,this.b,i)},v(Fn,"EStructuralFeatureImpl/InverseUpdatingFeatureMapEntry",776),m(1319,1,Cne,ISe),s.Dk=function(n){return this.a},s.Oj=function(){return q(this.a,98)?u(this.a,98).Oj():!this.a.dc()},s.Wb=function(n){this.a.$b(),this.a.Fc(u(n,16))},s.Ek=function(){q(this.a,98)?u(this.a,98).Ek():this.a.$b()},v(Fn,"EStructuralFeatureImpl/SettingMany",1319),m(1320,562,KF,uPe),s.vl=function(n){return new PK((ki(),nM),this.b.oi(this.a,n))},s.kd=function(){return null},s.xl=function(n,t,i){return i},s.yl=function(n,t,i){return i},v(Fn,"EStructuralFeatureImpl/SimpleContentFeatureMapEntry",1320),m(640,562,KF,PK),s.vl=function(n){return new PK(this.c,n)},s.kd=function(){return this.a},s.xl=function(n,t,i){return i},s.yl=function(n,t,i){return i},v(Fn,"EStructuralFeatureImpl/SimpleFeatureMapEntry",640),m(396,492,jh,Tl),s.$i=function(n){return oe(wf,xn,29,n,0,1)},s.Wi=function(){return!1},v(Fn,"ESuperAdapter/1",396),m(446,439,{109:1,94:1,93:1,158:1,197:1,57:1,114:1,834:1,52:1,100:1,161:1,446:1,117:1,118:1},Ik),s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return!this.a&&(this.a=new UE(this,Pc,this)),this.a}return Il(this,n-ht((mn(),o2)),Sn((r=u(Un(this,16),29),r||o2),n),t,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new we(Yt,this,0,3)),mc(this.Ab,n,i);case 2:return!this.a&&(this.a=new UE(this,Pc,this)),mc(this.a,n,i)}return c=u(Sn((r=u(Un(this,16),29),r||(mn(),o2)),t),69),c.uk().yk(this,_o(this),t-ht((mn(),o2)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return!!this.a&&this.a.i!=0}return Dl(this,n-ht((mn(),o2)),Sn((t=u(Un(this,16),29),t||o2),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab),!this.Ab&&(this.Ab=new we(Yt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Dt(t));return;case 2:!this.a&&(this.a=new UE(this,Pc,this)),vt(this.a),!this.a&&(this.a=new UE(this,Pc,this)),er(this.a,u(t,18));return}Bl(this,n-ht((mn(),o2)),Sn((i=u(Un(this,16),29),i||o2),n),t)},s.fi=function(){return mn(),o2},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new we(Yt,this,0,3)),vt(this.Ab);return;case 1:Mo(this,null);return;case 2:!this.a&&(this.a=new UE(this,Pc,this)),vt(this.a);return}Rl(this,n-ht((mn(),o2)),Sn((t=u(Un(this,16),29),t||o2),n))},v(Fn,"ETypeParameterImpl",446),m(447,81,lu,UE),s.Lj=function(n,t){return eTn(this,u(n,87),t)},s.Mj=function(n,t){return nTn(this,u(n,87),t)},v(Fn,"ETypeParameterImpl/1",447),m(637,44,iv,wX),s.ec=function(){return new xP(this)},v(Fn,"ETypeParameterImpl/2",637),m(557,Pa,ls,xP),s.Ec=function(n){return lNe(this,u(n,87))},s.Fc=function(n){var t,i,r;for(r=!1,i=n.Jc();i.Ob();)t=u(i.Pb(),87),Qt(this.a,t,"")==null&&(r=!0);return r},s.$b=function(){Fu(this.a)},s.Gc=function(n){return oo(this.a,n)},s.Jc=function(){var n;return n=new mp(new tn(this.a).a),new CP(n)},s.Kc=function(n){return UPe(this,n)},s.gc=function(){return gE(this.a)},v(Fn,"ETypeParameterImpl/2/1",557),m(558,1,zr,CP),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(z3(this.a).jd(),87)},s.Ob=function(){return this.a.b},s.Qb=function(){uRe(this.a)},v(Fn,"ETypeParameterImpl/2/1/1",558),m(1281,44,iv,EAe),s._b=function(n){return Pr(n)?IV(this,n):!!Uc(this.f,n)},s.xc=function(n){var t,i;return t=Pr(n)?so(this,n):hu(Uc(this.f,n)),q(t,835)?(i=u(t,835),t=i.Ik(),Qt(this,u(n,241),t),t):t??(n==null?(_X(),qan):null)},v(Fn,"EValidatorRegistryImpl",1281),m(1303,710,{109:1,94:1,93:1,469:1,158:1,57:1,114:1,2002:1,52:1,100:1,161:1,117:1,118:1},Ug),s.oi=function(n,t){switch(n.fk()){case 21:case 22:case 23:case 24:case 26:case 31:case 32:case 37:case 38:case 39:case 40:case 43:case 44:case 48:case 49:case 20:return t==null?null:su(t);case 25:return y8n(t);case 27:return L9n(t);case 28:return P9n(t);case 29:return t==null?null:DCe(VA[0],u(t,205));case 41:return t==null?"":Sb(u(t,298));case 42:return su(t);case 50:return Dt(t);default:throw P(new Gn(G8+n.ve()+Jw))}},s.pi=function(n){var t,i,r,c,o,l,f,h,b,p,y,S,M,O,I,R;switch(n.G==-1&&(n.G=(S=rl(n),S?Cd(S.si(),n):-1)),n.G){case 0:return i=new dX,i;case 1:return t=new vb,t;case 2:return r=new Xk,r;case 4:return c=new DP,c;case 5:return o=new kAe,o;case 6:return l=new RSe,l;case 7:return f=new xx,f;case 10:return b=new u3,b;case 11:return p=new bX,p;case 12:return y=new YIe,y;case 13:return M=new gX,M;case 14:return O=new hle,O;case 17:return I=new Nh,I;case 18:return h=new Zg,h;case 19:return R=new Ik,R;default:throw P(new Gn(dne+n.zb+Jw))}},s.qi=function(n,t){switch(n.fk()){case 20:return t==null?null:new Ioe(t);case 21:return t==null?null:new g0(t);case 23:case 22:return t==null?null:mjn(t);case 26:case 24:return t==null?null:uO(sl(t,-128,127)<<24>>24);case 25:return bOn(t);case 27:return eAn(t);case 28:return nAn(t);case 29:return yTn(t);case 32:case 31:return t==null?null:Tp(t);case 38:case 37:return t==null?null:new toe(t);case 40:case 39:return t==null?null:me(sl(t,Ur,ri));case 41:return null;case 42:return t==null,null;case 44:case 43:return t==null?null:Sp(Vz(t));case 49:case 48:return t==null?null:q9(sl(t,QF,32767)<<16>>16);case 50:return t;default:throw P(new Gn(G8+n.ve()+Jw))}},v(Fn,"EcoreFactoryImpl",1303),m(548,184,{109:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,2e3:1,52:1,100:1,161:1,184:1,548:1,117:1,118:1,680:1},kIe),s.gb=!1,s.hb=!1;var Q8e,Gan=!1;v(Fn,"EcorePackageImpl",548),m(1199,1,{835:1},o3),s.Ik=function(){return nOe(),Uan},v(Fn,"EcorePackageImpl/1",1199),m(1208,1,ei,Xg),s.dk=function(n){return q(n,158)},s.ek=function(n){return oe(VD,xn,158,n,0,1)},v(Fn,"EcorePackageImpl/10",1208),m(1209,1,ei,ex),s.dk=function(n){return q(n,197)},s.ek=function(n){return oe(xce,xn,197,n,0,1)},v(Fn,"EcorePackageImpl/11",1209),m(1210,1,ei,nx),s.dk=function(n){return q(n,57)},s.ek=function(n){return oe(sb,xn,57,n,0,1)},v(Fn,"EcorePackageImpl/12",1210),m(1211,1,ei,c0),s.dk=function(n){return q(n,403)},s.ek=function(n){return oe(pf,Kpe,62,n,0,1)},v(Fn,"EcorePackageImpl/13",1211),m(1212,1,ei,SL),s.dk=function(n){return q(n,241)},s.ek=function(n){return oe(ma,xn,241,n,0,1)},v(Fn,"EcorePackageImpl/14",1212),m(1213,1,ei,x5),s.dk=function(n){return q(n,503)},s.ek=function(n){return oe(c2,xn,2078,n,0,1)},v(Fn,"EcorePackageImpl/15",1213),m(1214,1,ei,Oy),s.dk=function(n){return q(n,103)},s.ek=function(n){return oe(Am,av,19,n,0,1)},v(Fn,"EcorePackageImpl/16",1214),m(1215,1,ei,Ny),s.dk=function(n){return q(n,179)},s.ek=function(n){return oe(es,av,179,n,0,1)},v(Fn,"EcorePackageImpl/17",1215),m(1216,1,ei,C5),s.dk=function(n){return q(n,470)},s.ek=function(n){return oe(jm,xn,470,n,0,1)},v(Fn,"EcorePackageImpl/18",1216),m(1217,1,ei,AL),s.dk=function(n){return q(n,549)},s.ek=function(n){return oe(Nu,OWe,549,n,0,1)},v(Fn,"EcorePackageImpl/19",1217),m(1200,1,ei,ML),s.dk=function(n){return q(n,335)},s.ek=function(n){return oe(Sm,av,38,n,0,1)},v(Fn,"EcorePackageImpl/2",1200),m(1218,1,ei,Dy),s.dk=function(n){return q(n,248)},s.ek=function(n){return oe(Pc,XWe,87,n,0,1)},v(Fn,"EcorePackageImpl/20",1218),m(1219,1,ei,TL),s.dk=function(n){return q(n,446)},s.ek=function(n){return oe(zo,xn,834,n,0,1)},v(Fn,"EcorePackageImpl/21",1219),m(1220,1,ei,_k),s.dk=function(n){return X2(n)},s.ek=function(n){return oe(Yi,je,473,n,8,1)},v(Fn,"EcorePackageImpl/22",1220),m(1221,1,ei,xL),s.dk=function(n){return q(n,195)},s.ek=function(n){return oe(hs,je,195,n,0,2)},v(Fn,"EcorePackageImpl/23",1221),m(1222,1,ei,lU),s.dk=function(n){return q(n,221)},s.ek=function(n){return oe(u6,je,221,n,0,1)},v(Fn,"EcorePackageImpl/24",1222),m(1223,1,ei,fU),s.dk=function(n){return q(n,180)},s.ek=function(n){return oe($S,je,180,n,0,1)},v(Fn,"EcorePackageImpl/25",1223),m(1224,1,ei,zu),s.dk=function(n){return q(n,205)},s.ek=function(n){return oe(oH,je,205,n,0,1)},v(Fn,"EcorePackageImpl/26",1224),m(1225,1,ei,Do),s.dk=function(n){return!1},s.ek=function(n){return oe(g7e,xn,2174,n,0,1)},v(Fn,"EcorePackageImpl/27",1225),m(1226,1,ei,Hc),s.dk=function(n){return K2(n)},s.ek=function(n){return oe(br,je,346,n,7,1)},v(Fn,"EcorePackageImpl/28",1226),m(1227,1,ei,nu),s.dk=function(n){return q(n,61)},s.ek=function(n){return oe(O8e,Bp,61,n,0,1)},v(Fn,"EcorePackageImpl/29",1227),m(1201,1,ei,to),s.dk=function(n){return q(n,504)},s.ek=function(n){return oe(Yt,{3:1,4:1,5:1,1995:1},587,n,0,1)},v(Fn,"EcorePackageImpl/3",1201),m(1228,1,ei,b1),s.dk=function(n){return q(n,568)},s.ek=function(n){return oe(I8e,xn,2001,n,0,1)},v(Fn,"EcorePackageImpl/30",1228),m(1229,1,ei,O2),s.dk=function(n){return q(n,163)},s.ek=function(n){return oe(t7e,Bp,163,n,0,1)},v(Fn,"EcorePackageImpl/31",1229),m(1230,1,ei,O5),s.dk=function(n){return q(n,75)},s.ek=function(n){return oe(EG,ten,75,n,0,1)},v(Fn,"EcorePackageImpl/32",1230),m(1231,1,ei,tx),s.dk=function(n){return q(n,164)},s.ek=function(n){return oe(Z8,je,164,n,0,1)},v(Fn,"EcorePackageImpl/33",1231),m(1232,1,ei,Kg),s.dk=function(n){return q(n,15)},s.ek=function(n){return oe(Er,je,15,n,0,1)},v(Fn,"EcorePackageImpl/34",1232),m(1233,1,ei,Rs),s.dk=function(n){return q(n,298)},s.ek=function(n){return oe(ome,xn,298,n,0,1)},v(Fn,"EcorePackageImpl/35",1233),m(1234,1,ei,N2),s.dk=function(n){return q(n,190)},s.ek=function(n){return oe(qw,je,190,n,0,1)},v(Fn,"EcorePackageImpl/36",1234),m(1235,1,ei,s3),s.dk=function(n){return q(n,92)},s.ek=function(n){return oe(sme,xn,92,n,0,1)},v(Fn,"EcorePackageImpl/37",1235),m(1236,1,ei,ix),s.dk=function(n){return q(n,588)},s.ek=function(n){return oe(Z8e,xn,588,n,0,1)},v(Fn,"EcorePackageImpl/38",1236),m(1237,1,ei,g1),s.dk=function(n){return!1},s.ek=function(n){return oe(w7e,xn,2175,n,0,1)},v(Fn,"EcorePackageImpl/39",1237),m(1202,1,ei,N5),s.dk=function(n){return q(n,88)},s.ek=function(n){return oe(wf,xn,29,n,0,1)},v(Fn,"EcorePackageImpl/4",1202),m(1238,1,ei,Iy),s.dk=function(n){return q(n,191)},s.ek=function(n){return oe(Uw,je,191,n,0,1)},v(Fn,"EcorePackageImpl/40",1238),m(1239,1,ei,Ih),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(Fn,"EcorePackageImpl/41",1239),m(1240,1,ei,rx),s.dk=function(n){return q(n,585)},s.ek=function(n){return oe(D8e,xn,585,n,0,1)},v(Fn,"EcorePackageImpl/42",1240),m(1241,1,ei,Lk),s.dk=function(n){return!1},s.ek=function(n){return oe(p7e,je,2176,n,0,1)},v(Fn,"EcorePackageImpl/43",1241),m(1242,1,ei,CL),s.dk=function(n){return q(n,45)},s.ek=function(n){return oe(sg,Zz,45,n,0,1)},v(Fn,"EcorePackageImpl/44",1242),m(1203,1,ei,Pk),s.dk=function(n){return q(n,143)},s.ek=function(n){return oe(va,xn,143,n,0,1)},v(Fn,"EcorePackageImpl/5",1203),m(1204,1,ei,$k),s.dk=function(n){return q(n,159)},s.ek=function(n){return oe(_ce,xn,159,n,0,1)},v(Fn,"EcorePackageImpl/6",1204),m(1205,1,ei,D2),s.dk=function(n){return q(n,459)},s.ek=function(n){return oe(kG,xn,675,n,0,1)},v(Fn,"EcorePackageImpl/7",1205),m(1206,1,ei,Zl),s.dk=function(n){return q(n,568)},s.ek=function(n){return oe(V1,xn,684,n,0,1)},v(Fn,"EcorePackageImpl/8",1206),m(1207,1,ei,I2),s.dk=function(n){return q(n,469)},s.ek=function(n){return oe(KA,xn,469,n,0,1)},v(Fn,"EcorePackageImpl/9",1207),m(1019,2042,CWe,UAe),s.Ki=function(n,t){Qkn(this,u(t,415))},s.Oi=function(n,t){VGe(this,n,u(t,415))},v(Fn,"MinimalEObjectImpl/1ArrayDelegatingAdapterList",1019),m(1020,151,PN,aIe),s.hj=function(){return this.a.a},v(Fn,"MinimalEObjectImpl/1ArrayDelegatingAdapterList/1",1020),m(1047,1046,{},ECe),v("org.eclipse.emf.ecore.plugin","EcorePlugin",1047);var Z8e=Ji(ien,"Resource");m(786,1485,ren),s.Fl=function(n){},s.Gl=function(n){},s.Cl=function(){return!this.a&&(this.a=new oX(this)),this.a},s.Dl=function(n){var t,i,r,c,o;if(r=n.length,r>0)if(Yn(0,n.length),n.charCodeAt(0)==47){for(o=new So(4),c=1,t=1;t0&&(n=(Yr(0,i,n.length),n.substr(0,i))));return oCn(this,n)},s.El=function(){return this.c},s.Ib=function(){var n;return Sb(this.Pm)+"@"+(n=Oi(this)>>>0,n.toString(16))+" uri='"+this.d+"'"},s.b=!1,v(One,"ResourceImpl",786),m(1486,786,ren,_Se),v(One,"BinaryResourceImpl",1486),m(1159,697,Sne),s._i=function(n){return q(n,57)?R5n(this,u(n,57)):q(n,588)?new ct(u(n,588).Cl()):ue(n)===ue(this.f)?u(n,18).Jc():(f9(),ZD.a)},s.Ob=function(){return G0e(this)},s.a=!1,v($i,"EcoreUtil/ContentTreeIterator",1159),m(1487,1159,Sne,FDe),s._i=function(n){return ue(n)===ue(this.f)?u(n,16).Jc():new HLe(u(n,57))},v(One,"ResourceImpl/5",1487),m(647,2054,UWe,oX),s.Gc=function(n){return this.i<=4?r8(this,n):q(n,52)&&u(n,52).Gh()==this.a},s.Ki=function(n,t){n==this.i-1&&(this.a.b||(this.a.b=!0))},s.Mi=function(n,t){n==0?this.a.b||(this.a.b=!0):lY(this,n,t)},s.Oi=function(n,t){},s.Pi=function(n,t,i){},s.Jj=function(){return 2},s.hj=function(){return this.a},s.Kj=function(){return!0},s.Lj=function(n,t){var i;return i=u(n,52),t=i.ci(this.a,t),t},s.Mj=function(n,t){var i;return i=u(n,52),i.ci(null,t)},s.Nj=function(){return!1},s.Qi=function(){return!0},s.$i=function(n){return oe(sb,xn,57,n,0,1)},s.Wi=function(){return!1},v(One,"ResourceImpl/ContentsEList",647),m(953,2024,j8,LSe),s.dd=function(n){return this.a.Ii(n)},s.gc=function(){return this.a.gc()},v($i,"AbstractSequentialInternalEList/1",953);var W8e,e7e,ec,n7e;m(625,1,{},GNe);var jG,SG;v($i,"BasicExtendedMetaData",625),m(1150,1,{},Hxe),s.Hl=function(){return null},s.Il=function(){return this.a==-2&&Fx(this,dTn(this.d,this.b)),this.a},s.Jl=function(){return null},s.Kl=function(){return vn(),vn(),Ec},s.ve=function(){return this.c==K8&&nX(this,pHe(this.d,this.b)),this.c},s.Ll=function(){return 0},s.a=-2,s.c=K8,v($i,"BasicExtendedMetaData/EClassExtendedMetaDataImpl",1150),m(1151,1,{},vLe),s.Hl=function(){return this.a==(M9(),jG)&&AP(this,QDn(this.f,this.b)),this.a},s.Il=function(){return 0},s.Jl=function(){return this.c==(M9(),jG)&&Gy(this,ZDn(this.f,this.b)),this.c},s.Kl=function(){return!this.d&&iX(this,L_n(this.f,this.b)),this.d},s.ve=function(){return this.e==K8&&Jx(this,pHe(this.f,this.b)),this.e},s.Ll=function(){return this.g==-2&&d(this,_Mn(this.f,this.b)),this.g},s.e=K8,s.g=-2,v($i,"BasicExtendedMetaData/EDataTypeExtendedMetaDataImpl",1151),m(1149,1,{},Jxe),s.b=!1,s.c=!1,v($i,"BasicExtendedMetaData/EPackageExtendedMetaDataImpl",1149),m(1152,1,{},yLe),s.c=-2,s.e=K8,s.f=K8,v($i,"BasicExtendedMetaData/EStructuralFeatureExtendedMetaDataImpl",1152),m(581,623,lu,Q$),s.Jj=function(){return this.c},s.ml=function(){return!1},s.Ui=function(n,t){return t},s.c=0,v($i,"EDataTypeEList",581);var t7e=Ji($i,"FeatureMap");m(76,581,{3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,67:1,61:1,77:1,163:1,219:1,1998:1,72:1,98:1},nr),s._c=function(n,t){mNn(this,n,u(t,75))},s.Ec=function(n){return LOn(this,u(n,75))},s.Fi=function(n){Ivn(this,u(n,75))},s.Lj=function(n,t){return fpn(this,u(n,75),t)},s.Mj=function(n,t){return Ple(this,u(n,75),t)},s.Ri=function(n,t){return HIn(this,n,t)},s.Ui=function(n,t){return OPn(this,n,u(t,75))},s.fd=function(n,t){return iDn(this,n,u(t,75))},s.Sj=function(n,t){return apn(this,u(n,75),t)},s.Tj=function(n,t){return pNe(this,u(n,75),t)},s.Uj=function(n,t,i){return jMn(this,u(n,75),u(t,75),i)},s.Xi=function(n,t){return nZ(this,n,u(t,75))},s.Ml=function(n,t){return Rbe(this,n,t)},s.ad=function(n,t){var i,r,c,o,l,f,h,b,p;for(b=new pw(t.gc()),c=t.Jc();c.Ob();)if(r=u(c.Pb(),75),o=r.Jk(),$1(this.e,o))(!o.Qi()||!FR(this,o,r.kd())&&!r8(b,r))&&kt(b,r);else{for(p=Lo(this.e.Ah(),o),i=u(this.g,122),l=!0,f=0;f=0;)if(t=n[this.c],this.k.$l(t.Jk()))return this.j=this.f?t:t.kd(),this.i=-2,!0;return this.i=-1,this.g=-1,!1},v($i,"BasicFeatureMap/FeatureEIterator",412),m(666,412,Uh,mK),s.sl=function(){return!0},v($i,"BasicFeatureMap/ResolvingFeatureEIterator",666),m(951,482,UF,PCe),s.nj=function(){return this},v($i,"EContentsEList/1",951),m(952,482,UF,oCe),s.sl=function(){return!1},v($i,"EContentsEList/2",952),m(950,287,XF,$Ce),s.ul=function(n){},s.Ob=function(){return!1},s.Sb=function(){return!1},v($i,"EContentsEList/FeatureIteratorImpl/1",950),m(824,581,lu,Gse),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;vt(this),Bs(this.e)?(n=this.a,this.a=!1,fi(this.e,new Cf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EDataTypeEList/Unsettable",824),m(1920,581,lu,JCe),s.Qi=function(){return!0},v($i,"EDataTypeUniqueEList",1920),m(1921,824,lu,GCe),s.Qi=function(){return!0},v($i,"EDataTypeUniqueEList/Unsettable",1921),m(145,81,lu,is),s.ll=function(){return!0},s.Ui=function(n,t){return q4(this,n,u(t,57))},v($i,"EObjectContainmentEList/Resolving",145),m(1153,543,lu,HCe),s.ll=function(){return!0},s.Ui=function(n,t){return q4(this,n,u(t,57))},v($i,"EObjectContainmentEList/Unsettable/Resolving",1153),m(753,14,lu,Cle),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;vt(this),Bs(this.e)?(n=this.a,this.a=!1,fi(this.e,new Cf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EObjectContainmentWithInverseEList/Unsettable",753),m(1187,753,lu,rNe),s.ll=function(){return!0},s.Ui=function(n,t){return q4(this,n,u(t,57))},v($i,"EObjectContainmentWithInverseEList/Unsettable/Resolving",1187),m(745,491,lu,Jse),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;vt(this),Bs(this.e)?(n=this.a,this.a=!1,fi(this.e,new Cf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EObjectEList/Unsettable",745),m(339,491,lu,M3),s.ll=function(){return!0},s.Ui=function(n,t){return q4(this,n,u(t,57))},v($i,"EObjectResolvingEList",339),m(1825,745,lu,qCe),s.ll=function(){return!0},s.Ui=function(n,t){return q4(this,n,u(t,57))},v($i,"EObjectResolvingEList/Unsettable",1825),m(1488,1,{},D5);var qan;v($i,"EObjectValidator",1488),m(547,491,lu,wR),s.gl=function(){return this.d},s.hl=function(){return this.b},s.Kj=function(){return!0},s.kl=function(){return!0},s.b=0,v($i,"EObjectWithInverseEList",547),m(1190,547,lu,cNe),s.jl=function(){return!0},v($i,"EObjectWithInverseEList/ManyInverse",1190),m(626,547,lu,BK),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;vt(this),Bs(this.e)?(n=this.a,this.a=!1,fi(this.e,new Cf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EObjectWithInverseEList/Unsettable",626),m(1189,626,lu,uNe),s.jl=function(){return!0},v($i,"EObjectWithInverseEList/Unsettable/ManyInverse",1189),m(754,547,lu,Ole),s.ll=function(){return!0},s.Ui=function(n,t){return q4(this,n,u(t,57))},v($i,"EObjectWithInverseResolvingEList",754),m(33,754,lu,Cn),s.jl=function(){return!0},v($i,"EObjectWithInverseResolvingEList/ManyInverse",33),m(755,626,lu,Nle),s.ll=function(){return!0},s.Ui=function(n,t){return q4(this,n,u(t,57))},v($i,"EObjectWithInverseResolvingEList/Unsettable",755),m(1188,755,lu,oNe),s.jl=function(){return!0},v($i,"EObjectWithInverseResolvingEList/Unsettable/ManyInverse",1188),m(1154,623,lu),s.Ji=function(){return(this.b&1792)==0},s.Li=function(){this.b|=1},s.il=function(){return(this.b&4)!=0},s.Kj=function(){return(this.b&40)!=0},s.jl=function(){return(this.b&16)!=0},s.kl=function(){return(this.b&8)!=0},s.ll=function(){return(this.b&R0)!=0},s.$k=function(){return(this.b&32)!=0},s.ml=function(){return(this.b&Rf)!=0},s.dk=function(n){return this.d?VLe(this.d,n):this.Jk().Fk().dk(n)},s.Oj=function(){return(this.b&2)!=0?(this.b&1)!=0:this.i!=0},s.Qi=function(){return(this.b&128)!=0},s.Ek=function(){var n;vt(this),(this.b&2)!=0&&(Bs(this.e)?(n=(this.b&1)!=0,this.b&=-2,Ky(this,new Cf(this.e,2,Fi(this.e.Ah(),this.Jk()),n,!1))):this.b&=-2)},s.Wi=function(){return(this.b&1536)==0},s.b=0,v($i,"EcoreEList/Generic",1154),m(1155,1154,lu,e_e),s.Jk=function(){return this.a},v($i,"EcoreEList/Dynamic",1155),m(752,67,jh,Que),s.$i=function(n){return lO(this.a.a,n)},v($i,"EcoreEMap/1",752),m(751,81,lu,Mfe),s.Ki=function(n,t){oz(this.b,u(t,136))},s.Mi=function(n,t){nze(this.b)},s.Ni=function(n,t,i){var r;++(r=this.b,u(t,136),r).e},s.Oi=function(n,t){fQ(this.b,u(t,136))},s.Pi=function(n,t,i){fQ(this.b,u(i,136)),ue(i)===ue(t)&&u(i,136).zi(swn(u(t,136).jd())),oz(this.b,u(t,136))},v($i,"EcoreEMap/DelegateEObjectContainmentEList",751),m(1185,142,Xpe,hBe),v($i,"EcoreEMap/Unsettable",1185),m(1186,751,lu,sNe),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;vt(this),Bs(this.e)?(n=this.a,this.a=!1,fi(this.e,new Cf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EcoreEMap/Unsettable/UnsettableDelegateEObjectContainmentEList",1186),m(1158,223,iv,iIe),s.a=!1,s.b=!1,v($i,"EcoreUtil/Copier",1158),m(747,1,zr,HLe),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return tHe(this)},s.Pb=function(){var n;return tHe(this),n=this.b,this.b=null,n},s.Qb=function(){this.a.Qb()},v($i,"EcoreUtil/ProperContentIterator",747),m(1489,1488,{},LU);var Uan;v($i,"EcoreValidator",1489);var Xan;Ji($i,"FeatureMapUtil/Validator"),m(1258,1,{2003:1},Vg),s.$l=function(n){return!0},v($i,"FeatureMapUtil/1",1258),m(760,1,{2003:1},pge),s.$l=function(n){var t;return this.c==n?!0:(t=Pe(Bn(this.a,n)),t==null?iIn(this,n)?($Pe(this.a,n,(Pn(),Q8)),!0):($Pe(this.a,n,(Pn(),U0)),!1):t==(Pn(),Q8))},s.e=!1;var Rce;v($i,"FeatureMapUtil/BasicValidator",760),m(761,44,iv,zse),v($i,"FeatureMapUtil/BasicValidator/Cache",761),m(495,56,{20:1,31:1,56:1,18:1,16:1,61:1,77:1,72:1,98:1},gC),s._c=function(n,t){GUe(this.c,this.b,n,t)},s.Ec=function(n){return Rbe(this.c,this.b,n)},s.ad=function(n,t){return ELn(this.c,this.b,n,t)},s.Fc=function(n){return BE(this,n)},s.Ei=function(n,t){s8n(this.c,this.b,n,t)},s.Uk=function(n,t){return Obe(this.c,this.b,n,t)},s.Yi=function(n){return Gz(this.c,this.b,n,!1)},s.Gi=function(){return wCe(this.c,this.b)},s.Hi=function(){return ewn(this.c,this.b)},s.Ii=function(n){return f9n(this.c,this.b,n)},s.Vk=function(n,t){return FOe(this,n,t)},s.$b=function(){G5(this)},s.Gc=function(n){return FR(this.c,this.b,n)},s.Hc=function(n){return l7n(this.c,this.b,n)},s.Xb=function(n){return Gz(this.c,this.b,n,!0)},s.Dk=function(n){return this},s.bd=function(n){return yyn(this.c,this.b,n)},s.dc=function(){return A$(this)},s.Oj=function(){return!xO(this.c,this.b)},s.Jc=function(){return U9n(this.c,this.b)},s.cd=function(){return X9n(this.c,this.b)},s.dd=function(n){return bEn(this.c,this.b,n)},s.Ri=function(n,t){return oKe(this.c,this.b,n,t)},s.Si=function(n,t){b9n(this.c,this.b,n,t)},s.ed=function(n){return _Ge(this.c,this.b,n)},s.Kc=function(n){return TIn(this.c,this.b,n)},s.fd=function(n,t){return wKe(this.c,this.b,n,t)},s.Wb=function(n){Az(this.c,this.b),BE(this,u(n,16))},s.gc=function(){return gEn(this.c,this.b)},s.Nc=function(){return k6n(this.c,this.b)},s.Oc=function(n){return kyn(this.c,this.b,n)},s.Ib=function(){var n,t;for(t=new ad,t.a+="[",n=wCe(this.c,this.b);eQ(n);)$c(t,FE(cz(n))),eQ(n)&&(t.a+=xo);return t.a+="]",t.a},s.Ek=function(){Az(this.c,this.b)},v($i,"FeatureMapUtil/FeatureEList",495),m(634,39,PN,WV),s.fj=function(n){return Mj(this,n)},s.kj=function(n){var t,i,r,c,o,l,f;switch(this.d){case 1:case 2:{if(o=n.hj(),ue(o)===ue(this.c)&&Mj(this,null)==n.fj(null))return this.g=n.gj(),n.ej()==1&&(this.d=1),!0;break}case 3:{switch(c=n.ej(),c){case 3:{if(o=n.hj(),ue(o)===ue(this.c)&&Mj(this,null)==n.fj(null))return this.d=5,t=new pw(2),kt(t,this.g),kt(t,n.gj()),this.g=t,!0;break}}break}case 5:{switch(c=n.ej(),c){case 3:{if(o=n.hj(),ue(o)===ue(this.c)&&Mj(this,null)==n.fj(null))return i=u(this.g,18),i.Ec(n.gj()),!0;break}}break}case 4:{switch(c=n.ej(),c){case 3:{if(o=n.hj(),ue(o)===ue(this.c)&&Mj(this,null)==n.fj(null))return this.d=1,this.g=n.gj(),!0;break}case 4:{if(o=n.hj(),ue(o)===ue(this.c)&&Mj(this,null)==n.fj(null))return this.d=6,f=new pw(2),kt(f,this.n),kt(f,n.ij()),this.n=f,l=z(B(It,1),Zt,30,15,[this.o,n.jj()]),this.g=l,!0;break}}break}case 6:{switch(c=n.ej(),c){case 4:{if(o=n.hj(),ue(o)===ue(this.c)&&Mj(this,null)==n.fj(null))return i=u(this.n,18),i.Ec(n.ij()),l=u(this.g,54),r=oe(It,Zt,30,l.length+1,15,1),Yu(l,0,r,0,l.length),r[l.length]=n.jj(),this.g=r,!0;break}}break}}return!1},v($i,"FeatureMapUtil/FeatureENotificationImpl",634),m(553,495,{20:1,31:1,56:1,18:1,16:1,61:1,77:1,163:1,219:1,1998:1,72:1,98:1},tR),s.Ml=function(n,t){return Rbe(this.c,n,t)},s.Nl=function(n,t,i){return Obe(this.c,n,t,i)},s.Ol=function(n,t,i){return cge(this.c,n,t,i)},s.Pl=function(){return this},s.Ql=function(n,t){return nN(this.c,n,t)},s.Rl=function(n){return u(Gz(this.c,this.b,n,!1),75).Jk()},s.Sl=function(n){return u(Gz(this.c,this.b,n,!1),75).kd()},s.Tl=function(){return this.a},s.Ul=function(n){return!xO(this.c,n)},s.Vl=function(n,t){qz(this.c,n,t)},s.Wl=function(n){return yBe(this.c,n)},s.Xl=function(n){nJe(this.c,n)},v($i,"FeatureMapUtil/FeatureFeatureMap",553),m(1257,1,Cne,Uxe),s.Dk=function(n){return Gz(this.b,this.a,-1,n)},s.Oj=function(){return!xO(this.b,this.a)},s.Wb=function(n){qz(this.b,this.a,n)},s.Ek=function(){Az(this.b,this.a)},v($i,"FeatureMapUtil/FeatureValue",1257);var P6,Bce,zce,$6,Kan,eI=Ji(nH,"AnyType");m(670,63,R1,jX),v(nH,"InvalidDatatypeValueException",670);var AG=Ji(nH,uen),nI=Ji(nH,oen),i7e=Ji(nH,sen),Van,Ru,r7e,Sg,Yan,Qan,Zan,Wan,ehn,nhn,thn,ihn,rhn,chn,uhn,Hv,ohn,Jv,WA,shn,s2,tI,iI,lhn,eM,nM;m(828,501,{109:1,94:1,93:1,57:1,52:1,100:1,841:1},hoe),s.Ih=function(n,t,i){switch(n){case 0:return i?(!this.c&&(this.c=new nr(this,0)),this.c):(!this.c&&(this.c=new nr(this,0)),this.c.b);case 1:return i?(!this.c&&(this.c=new nr(this,0)),u(lo(this.c,(ki(),Sg)),163)):(!this.c&&(this.c=new nr(this,0)),u(u(lo(this.c,(ki(),Sg)),163),219)).Tl();case 2:return i?(!this.b&&(this.b=new nr(this,2)),this.b):(!this.b&&(this.b=new nr(this,2)),this.b.b)}return Il(this,n-ht(this.fi()),Sn((this.j&2)==0?this.fi():(!this.k&&(this.k=new Zs),this.k).Lk(),n),t,i)},s.Rh=function(n,t,i){var r;switch(t){case 0:return!this.c&&(this.c=new nr(this,0)),ZO(this.c,n,i);case 1:return(!this.c&&(this.c=new nr(this,0)),u(u(lo(this.c,(ki(),Sg)),163),72)).Vk(n,i);case 2:return!this.b&&(this.b=new nr(this,2)),ZO(this.b,n,i)}return r=u(Sn((this.j&2)==0?this.fi():(!this.k&&(this.k=new Zs),this.k).Lk(),t),69),r.uk().yk(this,hhe(this),t-ht(this.fi()),n,i)},s.Th=function(n){switch(n){case 0:return!!this.c&&this.c.i!=0;case 1:return!(!this.c&&(this.c=new nr(this,0)),u(lo(this.c,(ki(),Sg)),163)).dc();case 2:return!!this.b&&this.b.i!=0}return Dl(this,n-ht(this.fi()),Sn((this.j&2)==0?this.fi():(!this.k&&(this.k=new Zs),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),LC(this.c,t);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(u(lo(this.c,(ki(),Sg)),163),219)).Wb(t);return;case 2:!this.b&&(this.b=new nr(this,2)),LC(this.b,t);return}Bl(this,n-ht(this.fi()),Sn((this.j&2)==0?this.fi():(!this.k&&(this.k=new Zs),this.k).Lk(),n),t)},s.fi=function(){return ki(),r7e},s.hi=function(n){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),vt(this.c);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(lo(this.c,(ki(),Sg)),163)).$b();return;case 2:!this.b&&(this.b=new nr(this,2)),vt(this.b);return}Rl(this,n-ht(this.fi()),Sn((this.j&2)==0?this.fi():(!this.k&&(this.k=new Zs),this.k).Lk(),n))},s.Ib=function(){var n;return(this.j&4)!=0?Lf(this):(n=new nf(Lf(this)),n.a+=" (mixed: ",LE(n,this.c),n.a+=", anyAttribute: ",LE(n,this.b),n.a+=")",n.a)},v(kr,"AnyTypeImpl",828),m(671,501,{109:1,94:1,93:1,57:1,52:1,100:1,2081:1,671:1},aU),s.Ih=function(n,t,i){switch(n){case 0:return this.a;case 1:return this.b}return Il(this,n-ht((ki(),Hv)),Sn((this.j&2)==0?Hv:(!this.k&&(this.k=new Zs),this.k).Lk(),n),t,i)},s.Th=function(n){switch(n){case 0:return this.a!=null;case 1:return this.b!=null}return Dl(this,n-ht((ki(),Hv)),Sn((this.j&2)==0?Hv:(!this.k&&(this.k=new Zs),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:C(this,Dt(t));return;case 1:V(this,Dt(t));return}Bl(this,n-ht((ki(),Hv)),Sn((this.j&2)==0?Hv:(!this.k&&(this.k=new Zs),this.k).Lk(),n),t)},s.fi=function(){return ki(),Hv},s.hi=function(n){switch(n){case 0:this.a=null;return;case 1:this.b=null;return}Rl(this,n-ht((ki(),Hv)),Sn((this.j&2)==0?Hv:(!this.k&&(this.k=new Zs),this.k).Lk(),n))},s.Ib=function(){var n;return(this.j&4)!=0?Lf(this):(n=new nf(Lf(this)),n.a+=" (data: ",$c(n,this.a),n.a+=", target: ",$c(n,this.b),n.a+=")",n.a)},s.a=null,s.b=null,v(kr,"ProcessingInstructionImpl",671),m(672,828,{109:1,94:1,93:1,57:1,52:1,100:1,841:1,2082:1,672:1},jAe),s.Ih=function(n,t,i){switch(n){case 0:return i?(!this.c&&(this.c=new nr(this,0)),this.c):(!this.c&&(this.c=new nr(this,0)),this.c.b);case 1:return i?(!this.c&&(this.c=new nr(this,0)),u(lo(this.c,(ki(),Sg)),163)):(!this.c&&(this.c=new nr(this,0)),u(u(lo(this.c,(ki(),Sg)),163),219)).Tl();case 2:return i?(!this.b&&(this.b=new nr(this,2)),this.b):(!this.b&&(this.b=new nr(this,2)),this.b.b);case 3:return!this.c&&(this.c=new nr(this,0)),Dt(nN(this.c,(ki(),WA),!0));case 4:return Ile(this.a,(!this.c&&(this.c=new nr(this,0)),Dt(nN(this.c,(ki(),WA),!0))));case 5:return this.a}return Il(this,n-ht((ki(),Jv)),Sn((this.j&2)==0?Jv:(!this.k&&(this.k=new Zs),this.k).Lk(),n),t,i)},s.Th=function(n){switch(n){case 0:return!!this.c&&this.c.i!=0;case 1:return!(!this.c&&(this.c=new nr(this,0)),u(lo(this.c,(ki(),Sg)),163)).dc();case 2:return!!this.b&&this.b.i!=0;case 3:return!this.c&&(this.c=new nr(this,0)),Dt(nN(this.c,(ki(),WA),!0))!=null;case 4:return Ile(this.a,(!this.c&&(this.c=new nr(this,0)),Dt(nN(this.c,(ki(),WA),!0))))!=null;case 5:return!!this.a}return Dl(this,n-ht((ki(),Jv)),Sn((this.j&2)==0?Jv:(!this.k&&(this.k=new Zs),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),LC(this.c,t);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(u(lo(this.c,(ki(),Sg)),163),219)).Wb(t);return;case 2:!this.b&&(this.b=new nr(this,2)),LC(this.b,t);return;case 3:yae(this,Dt(t));return;case 4:yae(this,Dle(this.a,t));return;case 5:N(this,u(t,159));return}Bl(this,n-ht((ki(),Jv)),Sn((this.j&2)==0?Jv:(!this.k&&(this.k=new Zs),this.k).Lk(),n),t)},s.fi=function(){return ki(),Jv},s.hi=function(n){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),vt(this.c);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(lo(this.c,(ki(),Sg)),163)).$b();return;case 2:!this.b&&(this.b=new nr(this,2)),vt(this.b);return;case 3:!this.c&&(this.c=new nr(this,0)),qz(this.c,(ki(),WA),null);return;case 4:yae(this,Dle(this.a,null));return;case 5:this.a=null;return}Rl(this,n-ht((ki(),Jv)),Sn((this.j&2)==0?Jv:(!this.k&&(this.k=new Zs),this.k).Lk(),n))},v(kr,"SimpleAnyTypeImpl",672),m(673,501,{109:1,94:1,93:1,57:1,52:1,100:1,2083:1,673:1},SAe),s.Ih=function(n,t,i){switch(n){case 0:return i?(!this.a&&(this.a=new nr(this,0)),this.a):(!this.a&&(this.a=new nr(this,0)),this.a.b);case 1:return i?(!this.b&&(this.b=new us((mn(),Sc),Nu,this,1)),this.b):(!this.b&&(this.b=new us((mn(),Sc),Nu,this,1)),QC(this.b));case 2:return i?(!this.c&&(this.c=new us((mn(),Sc),Nu,this,2)),this.c):(!this.c&&(this.c=new us((mn(),Sc),Nu,this,2)),QC(this.c));case 3:return!this.a&&(this.a=new nr(this,0)),lo(this.a,(ki(),tI));case 4:return!this.a&&(this.a=new nr(this,0)),lo(this.a,(ki(),iI));case 5:return!this.a&&(this.a=new nr(this,0)),lo(this.a,(ki(),eM));case 6:return!this.a&&(this.a=new nr(this,0)),lo(this.a,(ki(),nM))}return Il(this,n-ht((ki(),s2)),Sn((this.j&2)==0?s2:(!this.k&&(this.k=new Zs),this.k).Lk(),n),t,i)},s.Rh=function(n,t,i){var r;switch(t){case 0:return!this.a&&(this.a=new nr(this,0)),ZO(this.a,n,i);case 1:return!this.b&&(this.b=new us((mn(),Sc),Nu,this,1)),G$(this.b,n,i);case 2:return!this.c&&(this.c=new us((mn(),Sc),Nu,this,2)),G$(this.c,n,i);case 5:return!this.a&&(this.a=new nr(this,0)),FOe(lo(this.a,(ki(),eM)),n,i)}return r=u(Sn((this.j&2)==0?(ki(),s2):(!this.k&&(this.k=new Zs),this.k).Lk(),t),69),r.uk().yk(this,hhe(this),t-ht((ki(),s2)),n,i)},s.Th=function(n){switch(n){case 0:return!!this.a&&this.a.i!=0;case 1:return!!this.b&&this.b.f!=0;case 2:return!!this.c&&this.c.f!=0;case 3:return!this.a&&(this.a=new nr(this,0)),!A$(lo(this.a,(ki(),tI)));case 4:return!this.a&&(this.a=new nr(this,0)),!A$(lo(this.a,(ki(),iI)));case 5:return!this.a&&(this.a=new nr(this,0)),!A$(lo(this.a,(ki(),eM)));case 6:return!this.a&&(this.a=new nr(this,0)),!A$(lo(this.a,(ki(),nM)))}return Dl(this,n-ht((ki(),s2)),Sn((this.j&2)==0?s2:(!this.k&&(this.k=new Zs),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:!this.a&&(this.a=new nr(this,0)),LC(this.a,t);return;case 1:!this.b&&(this.b=new us((mn(),Sc),Nu,this,1)),TB(this.b,t);return;case 2:!this.c&&(this.c=new us((mn(),Sc),Nu,this,2)),TB(this.c,t);return;case 3:!this.a&&(this.a=new nr(this,0)),G5(lo(this.a,(ki(),tI))),!this.a&&(this.a=new nr(this,0)),BE(lo(this.a,tI),u(t,18));return;case 4:!this.a&&(this.a=new nr(this,0)),G5(lo(this.a,(ki(),iI))),!this.a&&(this.a=new nr(this,0)),BE(lo(this.a,iI),u(t,18));return;case 5:!this.a&&(this.a=new nr(this,0)),G5(lo(this.a,(ki(),eM))),!this.a&&(this.a=new nr(this,0)),BE(lo(this.a,eM),u(t,18));return;case 6:!this.a&&(this.a=new nr(this,0)),G5(lo(this.a,(ki(),nM))),!this.a&&(this.a=new nr(this,0)),BE(lo(this.a,nM),u(t,18));return}Bl(this,n-ht((ki(),s2)),Sn((this.j&2)==0?s2:(!this.k&&(this.k=new Zs),this.k).Lk(),n),t)},s.fi=function(){return ki(),s2},s.hi=function(n){switch(n){case 0:!this.a&&(this.a=new nr(this,0)),vt(this.a);return;case 1:!this.b&&(this.b=new us((mn(),Sc),Nu,this,1)),this.b.c.$b();return;case 2:!this.c&&(this.c=new us((mn(),Sc),Nu,this,2)),this.c.c.$b();return;case 3:!this.a&&(this.a=new nr(this,0)),G5(lo(this.a,(ki(),tI)));return;case 4:!this.a&&(this.a=new nr(this,0)),G5(lo(this.a,(ki(),iI)));return;case 5:!this.a&&(this.a=new nr(this,0)),G5(lo(this.a,(ki(),eM)));return;case 6:!this.a&&(this.a=new nr(this,0)),G5(lo(this.a,(ki(),nM)));return}Rl(this,n-ht((ki(),s2)),Sn((this.j&2)==0?s2:(!this.k&&(this.k=new Zs),this.k).Lk(),n))},s.Ib=function(){var n;return(this.j&4)!=0?Lf(this):(n=new nf(Lf(this)),n.a+=" (mixed: ",LE(n,this.a),n.a+=")",n.a)},v(kr,"XMLTypeDocumentRootImpl",673),m(1990,710,{109:1,94:1,93:1,469:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1,2084:1},cx),s.oi=function(n,t){switch(n.fk()){case 7:case 8:case 9:case 10:case 16:case 22:case 23:case 24:case 25:case 26:case 32:case 33:case 34:case 36:case 37:case 44:case 45:case 50:case 51:case 53:case 55:case 56:case 57:case 58:case 60:case 61:case 4:return t==null?null:su(t);case 19:case 28:case 29:case 35:case 38:case 39:case 41:case 46:case 52:case 54:case 5:return Dt(t);case 6:return C2n(u(t,195));case 12:case 47:case 49:case 11:return eVe(this,n,t);case 13:return t==null?null:xLn(u(t,247));case 15:case 14:return t==null?null:jvn(W(ie(t)));case 17:return GJe((ki(),t));case 18:return GJe(t);case 21:case 20:return t==null?null:Svn(u(t,164).a);case 27:return x2n(u(t,195));case 30:return tJe((ki(),u(t,16)));case 31:return tJe(u(t,16));case 40:return T2n((ki(),t));case 42:return qJe((ki(),t));case 43:return qJe(t);case 59:case 48:return M2n((ki(),t));default:throw P(new Gn(G8+n.ve()+Jw))}},s.pi=function(n){var t,i,r,c,o;switch(n.G==-1&&(n.G=(i=rl(n),i?Cd(i.si(),n):-1)),n.G){case 0:return t=new hoe,t;case 1:return r=new aU,r;case 2:return c=new jAe,c;case 3:return o=new SAe,o;default:throw P(new Gn(dne+n.zb+Jw))}},s.qi=function(n,t){var i,r,c,o,l,f,h,b,p,y,S,M,O,I,R,G;switch(n.fk()){case 5:case 52:case 4:return t;case 6:return Ujn(t);case 8:case 7:return t==null?null:OMn(t);case 9:return t==null?null:uO(sl((r=ho(t,!0),r.length>0&&(Yn(0,r.length),r.charCodeAt(0)==43)?(Yn(1,r.length+1),r.substr(1)):r),-128,127)<<24>>24);case 10:return t==null?null:uO(sl((c=ho(t,!0),c.length>0&&(Yn(0,c.length),c.charCodeAt(0)==43)?(Yn(1,c.length+1),c.substr(1)):c),-128,127)<<24>>24);case 11:return Dt(_w(this,(ki(),Zan),t));case 12:return Dt(_w(this,(ki(),Wan),t));case 13:return t==null?null:new Ioe(ho(t,!0));case 15:case 14:return ROn(t);case 16:return Dt(_w(this,(ki(),ehn),t));case 17:return rHe((ki(),t));case 18:return rHe(t);case 28:case 29:case 35:case 38:case 39:case 41:case 54:case 19:return ho(t,!0);case 21:case 20:return KOn(t);case 22:return Dt(_w(this,(ki(),nhn),t));case 23:return Dt(_w(this,(ki(),thn),t));case 24:return Dt(_w(this,(ki(),ihn),t));case 25:return Dt(_w(this,(ki(),rhn),t));case 26:return Dt(_w(this,(ki(),chn),t));case 27:return $jn(t);case 30:return cHe((ki(),t));case 31:return cHe(t);case 32:return t==null?null:me(sl((p=ho(t,!0),p.length>0&&(Yn(0,p.length),p.charCodeAt(0)==43)?(Yn(1,p.length+1),p.substr(1)):p),Ur,ri));case 33:return t==null?null:new g0((y=ho(t,!0),y.length>0&&(Yn(0,y.length),y.charCodeAt(0)==43)?(Yn(1,y.length+1),y.substr(1)):y));case 34:return t==null?null:me(sl((S=ho(t,!0),S.length>0&&(Yn(0,S.length),S.charCodeAt(0)==43)?(Yn(1,S.length+1),S.substr(1)):S),Ur,ri));case 36:return t==null?null:Sp(Vz((M=ho(t,!0),M.length>0&&(Yn(0,M.length),M.charCodeAt(0)==43)?(Yn(1,M.length+1),M.substr(1)):M)));case 37:return t==null?null:Sp(Vz((O=ho(t,!0),O.length>0&&(Yn(0,O.length),O.charCodeAt(0)==43)?(Yn(1,O.length+1),O.substr(1)):O)));case 40:return ISn((ki(),t));case 42:return uHe((ki(),t));case 43:return uHe(t);case 44:return t==null?null:new g0((I=ho(t,!0),I.length>0&&(Yn(0,I.length),I.charCodeAt(0)==43)?(Yn(1,I.length+1),I.substr(1)):I));case 45:return t==null?null:new g0((R=ho(t,!0),R.length>0&&(Yn(0,R.length),R.charCodeAt(0)==43)?(Yn(1,R.length+1),R.substr(1)):R));case 46:return ho(t,!1);case 47:return Dt(_w(this,(ki(),uhn),t));case 59:case 48:return DSn((ki(),t));case 49:return Dt(_w(this,(ki(),ohn),t));case 50:return t==null?null:q9(sl((G=ho(t,!0),G.length>0&&(Yn(0,G.length),G.charCodeAt(0)==43)?(Yn(1,G.length+1),G.substr(1)):G),QF,32767)<<16>>16);case 51:return t==null?null:q9(sl((o=ho(t,!0),o.length>0&&(Yn(0,o.length),o.charCodeAt(0)==43)?(Yn(1,o.length+1),o.substr(1)):o),QF,32767)<<16>>16);case 53:return Dt(_w(this,(ki(),shn),t));case 55:return t==null?null:q9(sl((l=ho(t,!0),l.length>0&&(Yn(0,l.length),l.charCodeAt(0)==43)?(Yn(1,l.length+1),l.substr(1)):l),QF,32767)<<16>>16);case 56:return t==null?null:q9(sl((f=ho(t,!0),f.length>0&&(Yn(0,f.length),f.charCodeAt(0)==43)?(Yn(1,f.length+1),f.substr(1)):f),QF,32767)<<16>>16);case 57:return t==null?null:Sp(Vz((h=ho(t,!0),h.length>0&&(Yn(0,h.length),h.charCodeAt(0)==43)?(Yn(1,h.length+1),h.substr(1)):h)));case 58:return t==null?null:Sp(Vz((b=ho(t,!0),b.length>0&&(Yn(0,b.length),b.charCodeAt(0)==43)?(Yn(1,b.length+1),b.substr(1)):b)));case 60:return t==null?null:me(sl((i=ho(t,!0),i.length>0&&(Yn(0,i.length),i.charCodeAt(0)==43)?(Yn(1,i.length+1),i.substr(1)):i),Ur,ri));case 61:return t==null?null:me(sl(ho(t,!0),Ur,ri));default:throw P(new Gn(G8+n.ve()+Jw))}};var fhn,c7e,ahn,u7e;v(kr,"XMLTypeFactoryImpl",1990),m(582,184,{109:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,52:1,100:1,161:1,184:1,117:1,118:1,680:1,2006:1,582:1},yIe),s.N=!1,s.O=!1;var hhn=!1;v(kr,"XMLTypePackageImpl",582),m(1923,1,{835:1},ux),s.Ik=function(){return Vbe(),khn},v(kr,"XMLTypePackageImpl/1",1923),m(1932,1,ei,OL),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/10",1932),m(1933,1,ei,_y),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/11",1933),m(1934,1,ei,ox),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/12",1934),m(1935,1,ei,w1),s.dk=function(n){return K2(n)},s.ek=function(n){return oe(br,je,346,n,7,1)},v(kr,"XMLTypePackageImpl/13",1935),m(1936,1,ei,NL),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/14",1936),m(1937,1,ei,_h),s.dk=function(n){return q(n,16)},s.ek=function(n){return oe(hl,Bp,16,n,0,1)},v(kr,"XMLTypePackageImpl/15",1937),m(1938,1,ei,DL),s.dk=function(n){return q(n,16)},s.ek=function(n){return oe(hl,Bp,16,n,0,1)},v(kr,"XMLTypePackageImpl/16",1938),m(1939,1,ei,IL),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/17",1939),m(1940,1,ei,_2),s.dk=function(n){return q(n,164)},s.ek=function(n){return oe(Z8,je,164,n,0,1)},v(kr,"XMLTypePackageImpl/18",1940),m(1941,1,ei,Rk),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/19",1941),m(1924,1,ei,sx),s.dk=function(n){return q(n,841)},s.ek=function(n){return oe(eI,xn,841,n,0,1)},v(kr,"XMLTypePackageImpl/2",1924),m(1942,1,ei,I5),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/20",1942),m(1943,1,ei,_L),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/21",1943),m(1944,1,ei,LL),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/22",1944),m(1945,1,ei,PL),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/23",1945),m(1946,1,ei,$L),s.dk=function(n){return q(n,195)},s.ek=function(n){return oe(hs,je,195,n,0,2)},v(kr,"XMLTypePackageImpl/24",1946),m(1947,1,ei,Bk),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/25",1947),m(1948,1,ei,lx),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/26",1948),m(1949,1,ei,hU),s.dk=function(n){return q(n,16)},s.ek=function(n){return oe(hl,Bp,16,n,0,1)},v(kr,"XMLTypePackageImpl/27",1949),m(1950,1,ei,dU),s.dk=function(n){return q(n,16)},s.ek=function(n){return oe(hl,Bp,16,n,0,1)},v(kr,"XMLTypePackageImpl/28",1950),m(1951,1,ei,bU),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/29",1951),m(1925,1,ei,RL),s.dk=function(n){return q(n,671)},s.ek=function(n){return oe(AG,xn,2081,n,0,1)},v(kr,"XMLTypePackageImpl/3",1925),m(1952,1,ei,BL),s.dk=function(n){return q(n,15)},s.ek=function(n){return oe(Er,je,15,n,0,1)},v(kr,"XMLTypePackageImpl/30",1952),m(1953,1,ei,_5),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/31",1953),m(1954,1,ei,zk),s.dk=function(n){return q(n,190)},s.ek=function(n){return oe(qw,je,190,n,0,1)},v(kr,"XMLTypePackageImpl/32",1954),m(1955,1,ei,zL),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/33",1955),m(1956,1,ei,FL),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/34",1956),m(1957,1,ei,HL),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/35",1957),m(1958,1,ei,JL),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/36",1958),m(1959,1,ei,GL),s.dk=function(n){return q(n,16)},s.ek=function(n){return oe(hl,Bp,16,n,0,1)},v(kr,"XMLTypePackageImpl/37",1959),m(1960,1,ei,qL),s.dk=function(n){return q(n,16)},s.ek=function(n){return oe(hl,Bp,16,n,0,1)},v(kr,"XMLTypePackageImpl/38",1960),m(1961,1,ei,Fk),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/39",1961),m(1926,1,ei,UL),s.dk=function(n){return q(n,672)},s.ek=function(n){return oe(nI,xn,2082,n,0,1)},v(kr,"XMLTypePackageImpl/4",1926),m(1962,1,ei,XL),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/40",1962),m(1963,1,ei,io),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/41",1963),m(1964,1,ei,fx),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/42",1964),m(1965,1,ei,gU),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/43",1965),m(1966,1,ei,KL),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/44",1966),m(1967,1,ei,wU),s.dk=function(n){return q(n,191)},s.ek=function(n){return oe(Uw,je,191,n,0,1)},v(kr,"XMLTypePackageImpl/45",1967),m(1968,1,ei,pU),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/46",1968),m(1969,1,ei,mU),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/47",1969),m(1970,1,ei,Hk),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/48",1970),m(1971,1,ei,L5),s.dk=function(n){return q(n,191)},s.ek=function(n){return oe(Uw,je,191,n,0,1)},v(kr,"XMLTypePackageImpl/49",1971),m(1927,1,ei,ax),s.dk=function(n){return q(n,673)},s.ek=function(n){return oe(i7e,xn,2083,n,0,1)},v(kr,"XMLTypePackageImpl/5",1927),m(1972,1,ei,Jk),s.dk=function(n){return q(n,190)},s.ek=function(n){return oe(qw,je,190,n,0,1)},v(kr,"XMLTypePackageImpl/50",1972),m(1973,1,ei,hx),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/51",1973),m(1974,1,ei,L2),s.dk=function(n){return q(n,15)},s.ek=function(n){return oe(Er,je,15,n,0,1)},v(kr,"XMLTypePackageImpl/52",1974),m(1928,1,ei,yb),s.dk=function(n){return Pr(n)},s.ek=function(n){return oe(Re,je,2,n,6,1)},v(kr,"XMLTypePackageImpl/6",1928),m(1929,1,ei,Ly),s.dk=function(n){return q(n,195)},s.ek=function(n){return oe(hs,je,195,n,0,2)},v(kr,"XMLTypePackageImpl/7",1929),m(1930,1,ei,vU),s.dk=function(n){return X2(n)},s.ek=function(n){return oe(Yi,je,473,n,8,1)},v(kr,"XMLTypePackageImpl/8",1930),m(1931,1,ei,VL),s.dk=function(n){return q(n,221)},s.ek=function(n){return oe(u6,je,221,n,0,1)},v(kr,"XMLTypePackageImpl/9",1931);var Qa,Yd,tM,MG,F;m(53,63,R1,Lt),v(Pd,"RegEx/ParseException",53),m(820,1,{},dx),s._l=function(n){return ni*16)throw P(new Lt(Rt((Nt(),vWe))));i=i*16+c}while(!0);if(this.a!=125)throw P(new Lt(Rt((Nt(),yWe))));if(i>V8)throw P(new Lt(Rt((Nt(),kWe))));n=i}else{if(c=0,this.c!=0||(c=Vb(this.a))<0)throw P(new Lt(Rt((Nt(),Ld))));if(i=c,ui(this),this.c!=0||(c=Vb(this.a))<0)throw P(new Lt(Rt((Nt(),Ld))));i=i*16+c,n=i}break;case 117:if(r=0,ui(this),this.c!=0||(r=Vb(this.a))<0)throw P(new Lt(Rt((Nt(),Ld))));if(t=r,ui(this),this.c!=0||(r=Vb(this.a))<0)throw P(new Lt(Rt((Nt(),Ld))));if(t=t*16+r,ui(this),this.c!=0||(r=Vb(this.a))<0)throw P(new Lt(Rt((Nt(),Ld))));if(t=t*16+r,ui(this),this.c!=0||(r=Vb(this.a))<0)throw P(new Lt(Rt((Nt(),Ld))));t=t*16+r,n=t;break;case 118:if(ui(this),this.c!=0||(r=Vb(this.a))<0)throw P(new Lt(Rt((Nt(),Ld))));if(t=r,ui(this),this.c!=0||(r=Vb(this.a))<0)throw P(new Lt(Rt((Nt(),Ld))));if(t=t*16+r,ui(this),this.c!=0||(r=Vb(this.a))<0)throw P(new Lt(Rt((Nt(),Ld))));if(t=t*16+r,ui(this),this.c!=0||(r=Vb(this.a))<0)throw P(new Lt(Rt((Nt(),Ld))));if(t=t*16+r,ui(this),this.c!=0||(r=Vb(this.a))<0)throw P(new Lt(Rt((Nt(),Ld))));if(t=t*16+r,ui(this),this.c!=0||(r=Vb(this.a))<0)throw P(new Lt(Rt((Nt(),Ld))));if(t=t*16+r,t>V8)throw P(new Lt(Rt((Nt(),"parser.descappe.4"))));n=t;break;case 65:case 90:case 122:throw P(new Lt(Rt((Nt(),EWe))))}return n},s.bm=function(n){var t,i;switch(n){case 100:i=(this.e&32)==32?$0("Nd",!0):(si(),TG);break;case 68:i=(this.e&32)==32?$0("Nd",!1):(si(),h7e);break;case 119:i=(this.e&32)==32?$0("IsWord",!0):(si(),L7);break;case 87:i=(this.e&32)==32?$0("IsWord",!1):(si(),b7e);break;case 115:i=(this.e&32)==32?$0("IsSpace",!0):(si(),R6);break;case 83:i=(this.e&32)==32?$0("IsSpace",!1):(si(),d7e);break;default:throw P(new au((t=n,Een+t.toString(16))))}return i},s.cm=function(n){var t,i,r,c,o,l,f,h,b,p,y,S;for(this.b=1,ui(this),t=null,this.c==0&&this.a==94?(ui(this),n?p=(si(),si(),new tl(5)):(t=(si(),si(),new tl(4)),ao(t,0,V8),p=new tl(4))):p=(si(),si(),new tl(4)),c=!0;(S=this.c)!=1&&!(S==0&&this.a==93&&!c);){if(c=!1,i=this.a,r=!1,S==10)switch(i){case 100:case 68:case 119:case 87:case 115:case 83:Lp(p,this.bm(i)),r=!0;break;case 105:case 73:case 99:case 67:i=this.sm(p,i),i<0&&(r=!0);break;case 112:case 80:if(y=H0e(this,i),!y)throw P(new Lt(Rt((Nt(),Mne))));Lp(p,y),r=!0;break;default:i=this.am()}else if(S==20){if(l=o9(this.i,58,this.d),l<0)throw P(new Lt(Rt((Nt(),Fpe))));if(f=!0,ic(this.i,this.d)==94&&(++this.d,f=!1),o=rf(this.i,this.d,l),h=T$e(o,f,(this.e&512)==512),!h)throw P(new Lt(Rt((Nt(),bWe))));if(Lp(p,h),r=!0,l+1>=this.j||ic(this.i,l+1)!=93)throw P(new Lt(Rt((Nt(),Fpe))));this.d=l+2}if(ui(this),!r)if(this.c!=0||this.a!=45)ao(p,i,i);else{if(ui(this),(S=this.c)==1)throw P(new Lt(Rt((Nt(),GF))));S==0&&this.a==93?(ao(p,i,i),ao(p,45,45)):(b=this.a,S==10&&(b=this.am()),ui(this),ao(p,i,b))}(this.e&Rf)==Rf&&this.c==0&&this.a==44&&ui(this)}if(this.c==1)throw P(new Lt(Rt((Nt(),GF))));return t&&(iS(t,p),p=t),Y3(p),nS(p),this.b=0,ui(this),p},s.dm=function(){var n,t,i,r;for(i=this.cm(!1);(r=this.c)!=7;)if(n=this.a,r==0&&(n==45||n==38)||r==4){if(ui(this),this.c!=9)throw P(new Lt(Rt((Nt(),wWe))));if(t=this.cm(!1),r==4)Lp(i,t);else if(n==45)iS(i,t);else if(n==38)YKe(i,t);else throw P(new au("ASSERT"))}else throw P(new Lt(Rt((Nt(),pWe))));return ui(this),i},s.em=function(){var n,t;return n=this.a-48,t=(si(),si(),new PV(12,null,n)),!this.g&&(this.g=new _P),IP(this.g,new Zue(n)),ui(this),t},s.fm=function(){return ui(this),si(),ghn},s.gm=function(){return ui(this),si(),bhn},s.hm=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.im=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.jm=function(){return ui(this),okn()},s.km=function(){return ui(this),si(),phn},s.lm=function(){return ui(this),si(),vhn},s.mm=function(){var n;if(this.d>=this.j||((n=ic(this.i,this.d++))&65504)!=64)throw P(new Lt(Rt((Nt(),aWe))));return ui(this),si(),si(),new Rh(0,n-64)},s.nm=function(){return ui(this),O_n()},s.om=function(){return ui(this),si(),yhn},s.pm=function(){var n;return n=(si(),si(),new Rh(0,105)),ui(this),n},s.qm=function(){return ui(this),si(),mhn},s.rm=function(){return ui(this),si(),whn},s.sm=function(n,t){return this.am()},s.tm=function(){return ui(this),si(),f7e},s.um=function(){var n,t,i,r,c;if(this.d+1>=this.j)throw P(new Lt(Rt((Nt(),sWe))));if(r=-1,t=null,n=ic(this.i,this.d),49<=n&&n<=57){if(r=n-48,!this.g&&(this.g=new _P),IP(this.g,new Zue(r)),++this.d,ic(this.i,this.d)!=41)throw P(new Lt(Rt((Nt(),ug))));++this.d}else switch(n==63&&--this.d,ui(this),t=kge(this),t.e){case 20:case 21:case 22:case 23:break;case 8:if(this.c!=7)throw P(new Lt(Rt((Nt(),ug))));break;default:throw P(new Lt(Rt((Nt(),lWe))))}if(ui(this),c=Aw(this),i=null,c.e==2){if(c.Nm()!=2)throw P(new Lt(Rt((Nt(),fWe))));i=c.Jm(1),c=c.Jm(0)}if(this.c!=7)throw P(new Lt(Rt((Nt(),ug))));return ui(this),si(),si(),new mRe(r,t,c,i)},s.vm=function(){return ui(this),si(),a7e},s.wm=function(){var n;if(ui(this),n=pR(24,Aw(this)),this.c!=7)throw P(new Lt(Rt((Nt(),ug))));return ui(this),n},s.xm=function(){var n;if(ui(this),n=pR(20,Aw(this)),this.c!=7)throw P(new Lt(Rt((Nt(),ug))));return ui(this),n},s.ym=function(){var n;if(ui(this),n=pR(22,Aw(this)),this.c!=7)throw P(new Lt(Rt((Nt(),ug))));return ui(this),n},s.zm=function(){var n,t,i,r,c;for(n=0,i=0,t=-1;this.d=this.j)throw P(new Lt(Rt((Nt(),Bpe))));if(t==45){for(++this.d;this.d=this.j)throw P(new Lt(Rt((Nt(),Bpe))))}if(t==58){if(++this.d,ui(this),r=cIe(Aw(this),n,i),this.c!=7)throw P(new Lt(Rt((Nt(),ug))));ui(this)}else if(t==41)++this.d,ui(this),r=cIe(Aw(this),n,i);else throw P(new Lt(Rt((Nt(),oWe))));return r},s.Am=function(){var n;if(ui(this),n=pR(21,Aw(this)),this.c!=7)throw P(new Lt(Rt((Nt(),ug))));return ui(this),n},s.Bm=function(){var n;if(ui(this),n=pR(23,Aw(this)),this.c!=7)throw P(new Lt(Rt((Nt(),ug))));return ui(this),n},s.Cm=function(){var n,t;if(ui(this),n=this.f++,t=aV(Aw(this),n),this.c!=7)throw P(new Lt(Rt((Nt(),ug))));return ui(this),t},s.Dm=function(){var n;if(ui(this),n=aV(Aw(this),0),this.c!=7)throw P(new Lt(Rt((Nt(),ug))));return ui(this),n},s.Em=function(n){return ui(this),this.c==5?(ui(this),lR(n,(si(),si(),new hp(9,n)))):lR(n,(si(),si(),new hp(3,n)))},s.Fm=function(n){var t;return ui(this),t=(si(),si(),new RE(2)),this.c==5?(ui(this),Zb(t,rM),Zb(t,n)):(Zb(t,n),Zb(t,rM)),t},s.Gm=function(n){return ui(this),this.c==5?(ui(this),si(),si(),new hp(9,n)):(si(),si(),new hp(3,n))},s.a=0,s.b=0,s.c=0,s.d=0,s.e=0,s.f=1,s.g=null,s.j=0,v(Pd,"RegEx/RegexParser",820),m(1910,820,{},AAe),s._l=function(n){return!1},s.am=function(){return Mbe(this)},s.bm=function(n){return b8(n)},s.cm=function(n){return JVe(this)},s.dm=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.em=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.fm=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.gm=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.hm=function(){return ui(this),b8(67)},s.im=function(){return ui(this),b8(73)},s.jm=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.km=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.lm=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.mm=function(){return ui(this),b8(99)},s.nm=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.om=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.pm=function(){return ui(this),b8(105)},s.qm=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.rm=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.sm=function(n,t){return Lp(n,b8(t)),-1},s.tm=function(){return ui(this),si(),si(),new Rh(0,94)},s.um=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.vm=function(){return ui(this),si(),si(),new Rh(0,36)},s.wm=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.xm=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.ym=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.zm=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.Am=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.Bm=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.Cm=function(){var n;if(ui(this),n=aV(Aw(this),0),this.c!=7)throw P(new Lt(Rt((Nt(),ug))));return ui(this),n},s.Dm=function(){throw P(new Lt(Rt((Nt(),Jl))))},s.Em=function(n){return ui(this),lR(n,(si(),si(),new hp(3,n)))},s.Fm=function(n){var t;return ui(this),t=(si(),si(),new RE(2)),Zb(t,n),Zb(t,rM),t},s.Gm=function(n){return ui(this),si(),si(),new hp(3,n)};var Gv=null,I7=null;v(Pd,"RegEx/ParserForXMLSchema",1910),m(121,1,Y8,Qg),s.Hm=function(n){throw P(new au("Not supported."))},s.Im=function(){return-1},s.Jm=function(n){return null},s.Km=function(){return null},s.Lm=function(n){},s.Mm=function(n){},s.Nm=function(){return 0},s.Ib=function(){return this.Om(0)},s.Om=function(n){return this.e==11?".":""},s.e=0;var o7e,_7,iM,dhn,s7e,xm=null,TG,Fce=null,l7e,rM,Hce=null,f7e,a7e,h7e,d7e,b7e,bhn,R6,ghn,whn,phn,mhn,L7,vhn,yhn,JBn=v(Pd,"RegEx/Token",121);m(137,121,{3:1,137:1,121:1},tl),s.Om=function(n){var t,i,r;if(this.e==4)if(this==l7e)i=".";else if(this==TG)i="\\d";else if(this==L7)i="\\w";else if(this==R6)i="\\s";else{for(r=new ad,r.a+="[",t=0;t0&&(r.a+=","),this.b[t]===this.b[t+1]?$c(r,eN(this.b[t])):($c(r,eN(this.b[t])),r.a+="-",$c(r,eN(this.b[t+1])));r.a+="]",i=r.a}else if(this==h7e)i="\\D";else if(this==b7e)i="\\W";else if(this==d7e)i="\\S";else{for(r=new ad,r.a+="[^",t=0;t0&&(r.a+=","),this.b[t]===this.b[t+1]?$c(r,eN(this.b[t])):($c(r,eN(this.b[t])),r.a+="-",$c(r,eN(this.b[t+1])));r.a+="]",i=r.a}return i},s.a=!1,s.c=!1,v(Pd,"RegEx/RangeToken",137),m(580,1,{580:1},Zue),s.a=0,v(Pd,"RegEx/RegexParser/ReferencePosition",580),m(579,1,{3:1,579:1},oTe),s.Fb=function(n){var t;return n==null||!q(n,579)?!1:(t=u(n,579),hn(this.b,t.b)&&this.a==t.a)},s.Hb=function(){return Sd(this.b+"/"+vbe(this.a))},s.Ib=function(){return this.c.Om(this.a)},s.a=0,v(Pd,"RegEx/RegularExpression",579),m(228,121,Y8,Rh),s.Im=function(){return this.a},s.Om=function(n){var t,i,r;switch(this.e){case 0:switch(this.a){case 124:case 42:case 43:case 63:case 40:case 41:case 46:case 91:case 123:case 92:r="\\"+$K(this.a&yr);break;case 12:r="\\f";break;case 10:r="\\n";break;case 13:r="\\r";break;case 9:r="\\t";break;case 27:r="\\e";break;default:this.a>=kc?(i=(t=this.a>>>0,"0"+t.toString(16)),r="\\v"+rf(i,i.length-6,i.length)):r=""+$K(this.a&yr)}break;case 8:this==f7e||this==a7e?r=""+$K(this.a&yr):r="\\"+$K(this.a&yr);break;default:r=null}return r},s.a=0,v(Pd,"RegEx/Token/CharToken",228),m(322,121,Y8,hp),s.Jm=function(n){return this.a},s.Lm=function(n){this.b=n},s.Mm=function(n){this.c=n},s.Nm=function(){return 1},s.Om=function(n){var t;if(this.e==3)if(this.c<0&&this.b<0)t=this.a.Om(n)+"*";else if(this.c==this.b)t=this.a.Om(n)+"{"+this.c+"}";else if(this.c>=0&&this.b>=0)t=this.a.Om(n)+"{"+this.c+","+this.b+"}";else if(this.c>=0&&this.b<0)t=this.a.Om(n)+"{"+this.c+",}";else throw P(new au("Token#toString(): CLOSURE "+this.c+xo+this.b));else if(this.c<0&&this.b<0)t=this.a.Om(n)+"*?";else if(this.c==this.b)t=this.a.Om(n)+"{"+this.c+"}?";else if(this.c>=0&&this.b>=0)t=this.a.Om(n)+"{"+this.c+","+this.b+"}?";else if(this.c>=0&&this.b<0)t=this.a.Om(n)+"{"+this.c+",}?";else throw P(new au("Token#toString(): NONGREEDYCLOSURE "+this.c+xo+this.b));return t},s.b=0,s.c=0,v(Pd,"RegEx/Token/ClosureToken",322),m(821,121,Y8,Nfe),s.Jm=function(n){return n==0?this.a:this.b},s.Nm=function(){return 2},s.Om=function(n){var t;return this.b.e==3&&this.b.Jm(0)==this.a?t=this.a.Om(n)+"+":this.b.e==9&&this.b.Jm(0)==this.a?t=this.a.Om(n)+"+?":t=this.a.Om(n)+(""+this.b.Om(n)),t},v(Pd,"RegEx/Token/ConcatToken",821),m(1908,121,Y8,mRe),s.Jm=function(n){if(n==0)return this.d;if(n==1)return this.b;throw P(new au("Internal Error: "+n))},s.Nm=function(){return this.b?2:1},s.Om=function(n){var t;return this.c>0?t="(?("+this.c+")":this.a.e==8?t="(?("+this.a+")":t="(?"+this.a,this.b?t+=this.d+"|"+this.b+")":t+=this.d+")",t},s.c=0,v(Pd,"RegEx/Token/ConditionToken",1908),m(1909,121,Y8,tLe),s.Jm=function(n){return this.b},s.Nm=function(){return 1},s.Om=function(n){return"(?"+(this.a==0?"":vbe(this.a))+(this.c==0?"":vbe(this.c))+":"+this.b.Om(n)+")"},s.a=0,s.c=0,v(Pd,"RegEx/Token/ModifierToken",1909),m(822,121,Y8,Ffe),s.Jm=function(n){return this.a},s.Nm=function(){return 1},s.Om=function(n){var t;switch(t=null,this.e){case 6:this.b==0?t="(?:"+this.a.Om(n)+")":t="("+this.a.Om(n)+")";break;case 20:t="(?="+this.a.Om(n)+")";break;case 21:t="(?!"+this.a.Om(n)+")";break;case 22:t="(?<="+this.a.Om(n)+")";break;case 23:t="(?"+this.a.Om(n)+")"}return t},s.b=0,v(Pd,"RegEx/Token/ParenToken",822),m(517,121,{3:1,121:1,517:1},PV),s.Km=function(){return this.b},s.Om=function(n){return this.e==12?"\\"+this.a:SOn(this.b)},s.a=0,v(Pd,"RegEx/Token/StringToken",517),m(466,121,Y8,RE),s.Hm=function(n){Zb(this,n)},s.Jm=function(n){return u(lw(this.a,n),121)},s.Nm=function(){return this.a?this.a.a.c.length:0},s.Om=function(n){var t,i,r,c,o;if(this.e==1){if(this.a.a.c.length==2)t=u(lw(this.a,0),121),i=u(lw(this.a,1),121),i.e==3&&i.Jm(0)==t?c=t.Om(n)+"+":i.e==9&&i.Jm(0)==t?c=t.Om(n)+"+?":c=t.Om(n)+(""+i.Om(n));else{for(o=new ad,r=0;r=this.c.b:this.a<=this.c.b},s.Sb=function(){return this.b>0},s.Tb=function(){return this.b},s.Vb=function(){return this.b-1},s.Qb=function(){throw P(new ld(Cen))},s.a=0,s.b=0,v(ume,"ExclusiveRange/RangeIterator",259);var Vl=v9(qF,"C"),It=v9(NS,"I"),ns=v9(X4,"Z"),l2=v9(DS,"J"),hs=v9(xS,"B"),Fr=v9(CS,"D"),Cm=v9(OS,"F"),qv=v9(IS,"S"),GBn=Ji("org.eclipse.elk.core.labels","ILabelManager"),g7e=Ji(vc,"DiagnosticChain"),w7e=Ji(ien,"ResourceSet"),p7e=v(vc,"InvocationTargetException",null),Ehn=(zP(),Lyn),jhn=jhn=bMn;D8n(rbn),K8n("permProps",[[["locale","default"],[Oen,"gecko1_8"]],[["locale","default"],[Oen,"safari"]]]),jhn(null,"elk",null)}).call(this)}).call(this,typeof Ahn<"u"?Ahn:typeof self<"u"?self:typeof window<"u"?window:{})},{}],3:[function(A,x,D){function $(Se){"@babel/helpers - typeof";return $=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(ze){return typeof ze}:function(ze){return ze&&typeof Symbol=="function"&&ze.constructor===Symbol&&ze!==Symbol.prototype?"symbol":typeof ze},$(Se)}function E(Se,ze,de){return Object.defineProperty(Se,"prototype",{writable:!1}),Se}function H(Se,ze){if(!(Se instanceof ze))throw new TypeError("Cannot call a class as a function")}function U(Se,ze,de){return ze=Z(ze),J(Se,Q()?Reflect.construct(ze,de||[],Z(Se).constructor):ze.apply(Se,de))}function J(Se,ze){if(ze&&($(ze)=="object"||typeof ze=="function"))return ze;if(ze!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return te(Se)}function te(Se){if(Se===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return Se}function Q(){try{var Se=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch{}return(Q=function(){return!!Se})()}function Z(Se){return Z=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(ze){return ze.__proto__||Object.getPrototypeOf(ze)},Z(Se)}function le(Se,ze){if(typeof ze!="function"&&ze!==null)throw new TypeError("Super expression must either be null or a function");Se.prototype=Object.create(ze&&ze.prototype,{constructor:{value:Se,writable:!0,configurable:!0}}),Object.defineProperty(Se,"prototype",{writable:!1}),ze&&be(Se,ze)}function be(Se,ze){return be=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(de,Le){return de.__proto__=Le,de},be(Se,ze)}var ne=A("./elk-api.js").default,De=(function(Se){function ze(){var de=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};H(this,ze);var Le=Object.assign({},de),an=!1;try{A.resolve("web-worker"),an=!0}catch{}if(de.workerUrl)if(an){var ln=A("web-worker");Le.workerFactory=function(Nn){return new ln(Nn)}}else console.warn(`Web worker requested but 'web-worker' package not installed. -Consider installing the package or pass your own 'workerFactory' to ELK's constructor. -... Falling back to non-web worker version.`);if(!Le.workerFactory){var on=A("./elk-worker.min.js"),Mn=on.Worker;Le.workerFactory=function(Nn){return new Mn(Nn)}}return U(this,ze,[Le])}return le(ze,Se),E(ze)})(ne);Object.defineProperty(x.exports,"__esModule",{value:!0}),x.exports=De,De.default=De},{"./elk-api.js":1,"./elk-worker.min.js":2,"web-worker":4}],4:[function(A,x,D){var $=typeof Worker<"u"?Worker:void 0;x.exports=$},{}]},{},[3])(3)})})(z7e)),z7e.exports}var zXn=BXn();const FXn=rke(zXn),HXn=new FXn;async function JXn(g,j,A){const x={id:"root",layoutOptions:GXn(A),children:g.map(E=>({id:E.id,width:qXn(E.data),height:UXn(E.data),ports:E.data.nodeKind==="application"?[...E.data.inputs.map((H,U)=>iue(H.id,"WEST",U)),...E.data.outputs.map((H,U)=>iue(H.id,"EAST",U))]:[...(E.data.inputPortIds??[]).map((H,U)=>iue(H,"WEST",U)),...(E.data.outputPortIds??[]).map((H,U)=>iue(H,"EAST",U))],layoutOptions:{"org.eclipse.elk.portConstraints":"FIXED_ORDER"}})),edges:j.map(E=>({id:E.id,sources:[E.sourceHandle??E.source],targets:[E.targetHandle??E.target]}))},D=await HXn.layout(x),$=new Map((D.children??[]).map(E=>[E.id,{x:E.x??0,y:E.y??0}]));return g.map(E=>({...E,position:$.get(E.id)??E.position}))}function GXn(g){return{"elk.algorithm":g==="topology"?"mrtree":"layered","elk.direction":g==="topology"?"DOWN":"RIGHT","elk.spacing.nodeNode":g==="overview"?"24":g==="compact"?"32":"56","elk.layered.spacing.nodeNodeBetweenLayers":g==="overview"?"48":g==="compact"?"60":"110","elk.layered.nodePlacement.strategy":"BRANDES_KOEPF","elk.layered.crossingMinimization.semiInteractive":"true","elk.edgeRouting":"ORTHOGONAL"}}function iue(g,j,A){return{id:g,width:9,height:9,layoutOptions:{"org.eclipse.elk.port.side":j,"org.eclipse.elk.port.index":String(A)}}}function qXn(g){return g.nodeKind==="application"?G0n(g):240}function UXn(g){return g.nodeKind!=="application"?112:g.detailMode==="overview"?108:Math.max(178,142+Math.max(g.inputs.length,g.outputs.length)*27)}const F7e=jke("source","degree_days","ToyDegreeDaysCumulModel",[],["TT_cu"]),rue=jke("lai","lai_dynamic","ToyLAIModel",["TT_cu"],["LAI"]),H7e=jke("light","light_interception","Beer",["LAI"],["aPPFD"]),V1n={schemaVersion:1,level:"applications",metadata:{title:"PlantSimEngine Scene Graph",sceneRevision:0,objectCount:1,instanceCount:0,applicationCount:3,executionCount:3,bindingCount:2,callCount:0,unresolvedInitializationCount:1,cyclic:!1,strictlyCompiled:!0},objects:[{id:"object:plant",objectId:"plant",scale:"Plant",kind:"plant",species:null,name:"plant",instance:null,parent:null,children:[],hasGeometry:!1,hasStatus:!0}],instances:[],applications:[F7e,rue,H7e],executions:[F7e,rue,H7e].map(g=>({id:`execution:${g.applicationId}:plant`,applicationId:g.applicationId,applicationNodeId:g.id,objectId:"plant",objectNodeId:"object:plant",modelType:g.modelType,modelParameters:{},overridden:!1})),edges:[Q1n(F7e,"TT_cu",rue,"TT_cu"),Q1n(rue,"LAI",H7e,"LAI")],modelLibrary:[],initialization:[{applicationId:"source",objectId:"plant",variable:"TT",role:"input",disposition:"unresolved",value:"-Inf",valueJulia:"-Inf",expectedType:"Float64",sourceApplicationIds:[],sourceObjectIds:[],sourceVariable:null,origin:"missing",previousTimeStep:!1}],diagnostics:[],cycles:[],availableActions:["inspect"]};function jke(g,j,A,x,D){return{id:`application:${g}`,applicationId:g,name:g,process:j,modelType:A,modelName:A,module:"PlantSimEngine.Examples",package:"PlantSimEngine",modelParameters:{},selector:{type:"One",multiplicity:"one",criteria:{scale:"Plant"},julia:"One(scale=:Plant)"},targetIds:["plant"],targetCount:1,targetScales:["Plant"],targetKinds:["plant"],targetSpecies:[],targetInstances:[],timestep:null,clock:null,inputs:x.map($=>Y1n(g,"input",$)),outputs:D.map($=>Y1n(g,"output",$)),environmentInputs:[],environmentOutputs:[],modelStorage:"shared_application",objectOverrides:[]}}function Y1n(g,j,A){return{id:`application:${g}:${j}:${A}`,name:A,role:j,default:"-Inf",defaultJulia:"-Inf",expectedType:"Float64"}}function Q1n(g,j,A,x){return{id:`binding:${g.applicationId}:${j}:${A.applicationId}:${x}`,source:g.id,target:A.id,sourcePort:`application:${g.applicationId}:output:${j}`,targetPort:`application:${A.applicationId}:input:${x}`,sourceVariable:j,targetVariable:x,sourceApplicationId:g.applicationId,targetApplicationId:A.applicationId,kind:"inferred_same_object",cycle:!1,projection:"applications"}}const XXn={application:DXn,entity:IXn},KXn={sceneEdge:$Xn};function VXn(){const[g,j]=kn.useState(J7e),[A,x]=kn.useState(()=>J7e().level),[D,$]=kn.useState(()=>J7e().metadata.applicationCount>24?"overview":"detail"),[E,H]=kn.useState(""),[U,J]=kn.useState(null),[te,Q]=kn.useState(null),[Z,le]=kn.useState(null),[be,ne]=kn.useState(!1),[De,Se]=kn.useState(!1),[ze,de]=kn.useState(!1),[Le,an]=kn.useState(""),[ln,on]=kn.useState(null),[Mn,Nn]=kn.useState(!1),[Ft,In]=kn.useState(!1),[nt,Y]=kn.useState(!1),[Fe,gn]=kn.useState(null),[Ae,Je]=kn.useState(null),[un,jn]=kn.useState(null),[En,Me,rn]=_Un([]),[ut,st,Kt]=LUn([]),li=kn.useMemo(aKn,[]),oi=kn.useMemo(()=>new Map(g.applications.map(Ai=>[Ai.applicationId,Ai])),[g.applications]),Jt=kn.useMemo(()=>new Set(g.initialization.filter(Ai=>Ai.role==="input"&&Ai.disposition==="unresolved").map(Ai=>W1n(Ai.applicationId,"input",Ai.variable))),[g.initialization]),hi=kn.useMemo(()=>new Set(g.initialization.filter(Ai=>Ai.role==="input"&&Ai.previousTimeStep).map(Ai=>W1n(Ai.applicationId,"input",Ai.variable))),[g.initialization]),nc=kn.useMemo(()=>new Set(g.cycles.flatMap(Ai=>Ai.applicationIds)),[g.cycles]),Xr=kn.useMemo(()=>WXn(g),[g]),pr=kn.useMemo(()=>Z?eKn(g.modelLibrary,Z.port):[],[Z,g.modelLibrary]),Ei=kn.useMemo(()=>Z?tKn(g.applications,Z):[],[Z,g.applications]),Bi=kn.useMemo(()=>{const Ai=new Map;for(const Mc of g.applications)for(const no of[...Mc.inputs,...Mc.outputs])Ai.set(no.id,{application:Mc,port:no});return Ai},[g.applications]);kn.useEffect(()=>{if(!li?.websocketUrl)return;const Ai=new WebSocket(li.websocketUrl);return on(Ai),Ai.addEventListener("open",()=>{Nn(!0),gn(null)}),Ai.addEventListener("close",()=>{Nn(!1),gn("Editor connection closed.")}),Ai.addEventListener("message",Mc=>{const no=JSON.parse(Mc.data);no.graph&&j(no.graph),typeof no.sceneCode=="string"&&an(no.sceneCode),In(!!no.canUndo),Y(!!no.canRedo),gn(no.ok===!1?no.diagnostics?.[0]||"The edit failed.":null)}),()=>Ai.close()},[li?.websocketUrl]);const Fc=kn.useCallback(Ai=>{if(!ln||ln.readyState!==WebSocket.OPEN){gn("This action requires an interactive Julia editor session.");return}ln.send(JSON.stringify(Ai))},[ln]),Du=kn.useCallback((Ai,Mc,no)=>{J(Ai),Q(Mc),le({application:Ai,port:Mc,x:no.x,y:no.y})},[]);kn.useEffect(()=>{const Ai=YXn({graph:g,view:A,detailMode:D,query:E,unresolvedPortIds:Jt,previousPortIds:hi,candidatePortIds:Xr,cyclicApplications:nc,openCandidates:Du,onPortClick:Q}),Mc=new Set(Ai.map(l1=>l1.id)),no=QXn(g,A).filter(l1=>Mc.has(l1.source)&&Mc.has(l1.target));JXn(Ai,no,A==="topology"?"topology":D==="overview"?"overview":"data_flow").then(Me),st(no)},[Xr,nc,D,g,Du,hi,E,st,Me,Jt,A]);const kl=kn.useCallback((Ai,Mc)=>{Mc.data.nodeKind==="application"?J(oi.get(Mc.data.applicationId)??null):J(Mc.data.detail),Q(null)},[oi]),s1=kn.useCallback(Ai=>{Z&&(Je({mode:"add",initialModelType:Ai.type,suggestedSelector:fKn(Z.application)}),Mn||gn(`${Ai.name} matches ${Z.port.name}. Start an interactive Julia editor session to add it to the Scene.`),le(null))},[Z,Mn]),Za=kn.useCallback(Ai=>{if(!Mn){gn("Adding or updating an application requires an interactive Julia editor session."),Je(null);return}Fc({action:"edit",kind:Ai.applicationId?"update_application":"add_application",...Ai}),Je(null)},[Mn,Fc]),Wa=kn.useCallback(Ai=>{if(!Mn){gn("Creating a binding requires an interactive Julia editor session."),jn(null);return}Fc({action:"edit",kind:"set_input_binding",...Ai}),jn(null)},[Mn,Fc]),eu=kn.useCallback(Ai=>{if(!Ai.sourceHandle||!Ai.targetHandle)return;const Mc=Bi.get(Ai.sourceHandle),no=Bi.get(Ai.targetHandle);if(!Mc||!no||Mc.port.role!=="output"||no.port.role!=="input"){gn("Connect an application output to an application input.");return}jn({sourceApplication:Mc.application,sourcePort:Mc.port,targetApplication:no.application,targetPort:no.port})},[Bi]),Ng=kn.useMemo(()=>U?"applicationId"in U?g.initialization.filter(Ai=>Ai.applicationId===U.applicationId):"objectId"in U?g.initialization.filter(Ai=>String(Ai.objectId)===String(U.objectId)):g.initialization:g.initialization,[g.initialization,U]);return re.jsxs("main",{className:"scene-editor-shell","data-testid":"scene-graph-viewer",children:[re.jsxs("header",{className:"scene-toolbar",children:[re.jsxs("div",{className:"scene-brand",children:[re.jsx("span",{className:"brand-mark"}),re.jsxs("div",{children:[re.jsx("small",{children:"PLANTSIMENGINE"}),re.jsx("strong",{children:"Scene Graph"})]})]}),re.jsxs("div",{className:"scene-search",children:[re.jsx(EXn,{size:17}),re.jsx("input",{value:E,onChange:Ai=>H(Ai.target.value),placeholder:"Search application, object, or variable"}),E&&re.jsx("button",{"aria-label":"Clear search",onClick:()=>H(""),children:re.jsx(wI,{size:15})})]}),re.jsxs("div",{className:"scene-counts",children:[re.jsxs("span",{children:[g.metadata.applicationCount," applications"]}),re.jsxs("span",{children:[g.metadata.objectCount," objects"]}),g.metadata.unresolvedInitializationCount>0&&re.jsxs("button",{className:"count-warning",onClick:()=>Se(!0),children:[re.jsx(wXn,{size:14})," ",g.metadata.unresolvedInitializationCount," init"]}),g.diagnostics.length>0&&re.jsxs("button",{className:"count-error",onClick:()=>ne(!0),children:[re.jsx(J1n,{size:14})," ",g.diagnostics.length]})]}),re.jsxs("nav",{className:"view-tabs","aria-label":"Graph projection",children:[re.jsxs("button",{className:A==="applications"?"active":"",onClick:()=>x("applications"),children:[re.jsx(H0n,{size:15})," Applications"]}),re.jsxs("button",{className:A==="topology"?"active":"",onClick:()=>x("topology"),children:[re.jsx(vXn,{size:15})," Objects"]}),re.jsxs("button",{className:A==="resolved"?"active":"",onClick:()=>x("resolved"),children:[re.jsx(yXn,{size:15})," Executions"]})]}),re.jsxs("div",{className:"scene-actions",children:[A!=="topology"&&re.jsx("button",{className:D==="overview"?"overview-cta":"",onClick:()=>$(Ai=>Ai==="overview"?"detail":"overview"),children:D==="overview"?"Overview Mode - Show Detailed View":"Show Overview"}),li&&re.jsxs("button",{"data-testid":"add-application",onClick:()=>Je({mode:"add"}),children:[re.jsx(J0n,{size:15})," Add application"]}),li&&re.jsx("button",{disabled:!Ft,onClick:()=>Fc({action:"undo"}),"aria-label":"Undo",children:re.jsx(jXn,{size:15})}),li&&re.jsx("button",{disabled:!nt,onClick:()=>Fc({action:"redo"}),"aria-label":"Redo",children:re.jsx(kXn,{size:15})}),re.jsxs("button",{onClick:()=>de(!0),children:[re.jsx(mXn,{size:15})," Scene code"]})]})]}),g.metadata.cyclic&&re.jsxs("section",{className:"cycle-callout","data-testid":"cycle-callout",children:[re.jsx(J1n,{size:19}),re.jsxs("div",{children:[re.jsx("strong",{children:"Current-step dependency cycle"}),re.jsx("span",{children:"Select a cycle input to read its previous accepted timestep value."})]}),re.jsx("button",{onClick:()=>{ne(!0),J(g.edges.find(Ai=>Ai.cycle)??null)},children:"Choose a break point"})]}),Fe&&re.jsxs("div",{className:"editor-feedback",children:[Fe,re.jsx("button",{onClick:()=>gn(null),children:re.jsx(wI,{size:14})})]}),re.jsxs("section",{className:"scene-workspace",children:[re.jsx("div",{className:"flow-wrap",children:re.jsxs(NUn,{nodes:En,edges:ut,nodeTypes:XXn,edgeTypes:KXn,onNodesChange:rn,onEdgesChange:Kt,onConnect:eu,onNodeClick:kl,onEdgeClick:(Ai,Mc)=>J(Mc.data??null),fitView:!0,minZoom:.05,maxZoom:2,children:[re.jsx(zUn,{color:"#d8cdbc",gap:22,size:1}),re.jsx(XUn,{}),re.jsx(uXn,{pannable:!0,zoomable:!0})]})}),re.jsx(rKn,{selection:U,port:te,initialization:Ng,interactive:Mn,onEditApplication:Ai=>Je({mode:"update",application:Ai}),onRemoveApplication:Ai=>Fc({action:"edit",kind:"remove_application",applicationId:Ai.applicationId})})]}),Z&&(pr.length>0||Ei.length>0)&&re.jsx(nKn,{candidate:Z,models:pr,applications:Ei,onSelectModel:s1,onSelectApplication:Ai=>{jn(iKn(Z,Ai)),le(null)},onClose:()=>le(null)}),be&&re.jsx(cKn,{graph:g,onClose:()=>ne(!1),sendCommand:Fc,interactive:Mn}),De&&re.jsx(uKn,{graph:g,onClose:()=>Se(!1)}),ze&&re.jsx(oKn,{code:Le,onClose:()=>de(!1)}),Ae&&re.jsx(SXn,{mode:Ae.mode,models:g.modelLibrary,objects:g.objects,application:Ae.application,initialModelType:Ae.initialModelType,suggestedSelector:Ae.suggestedSelector,onSubmit:Za,onClose:()=>Je(null)}),un&&re.jsx(CXn,{endpoints:un,objects:g.objects,onSubmit:Wa,onClose:()=>jn(null)})]})}function YXn({graph:g,view:j,detailMode:A,query:x,unresolvedPortIds:D,previousPortIds:$,candidatePortIds:E,cyclicApplications:H,openCandidates:U,onPortClick:J}){const te=Q=>!x||JSON.stringify(Q).toLowerCase().includes(x.toLowerCase());if(j==="topology")return g.objects.filter(te).map(Q=>({id:Q.id,type:"entity",position:{x:0,y:0},data:{nodeKind:"object",title:Q.name||String(Q.objectId),subtitle:[Q.kind,Q.scale,Q.instance].filter(Boolean).join(" · "),badges:[Q.species,Q.hasStatus?"status":null,Q.hasGeometry?"geometry":null].filter(Boolean),detail:Q}}));if(j==="resolved"){const Q=new Map(g.applications.map(Z=>[Z.applicationId,Z]));return g.executions.filter(te).map(Z=>{const le=Q.get(Z.applicationId);return{id:Z.id,type:"entity",position:{x:0,y:0},data:{nodeKind:"execution",title:Z.applicationId,subtitle:`object ${String(Z.objectId)}`,badges:[lKn(Z.modelType),Z.overridden?"override":"shared"],inputPortIds:le?.inputs.map(be=>be.id)??[],outputPortIds:le?.outputs.map(be=>be.id)??[],detail:Z}}})}return g.applications.filter(te).map(Q=>({id:Q.id,type:"application",position:{x:0,y:0},data:{...Q,nodeKind:"application",detailMode:A,cyclic:H.has(Q.applicationId),requiredInputPortIds:Q.inputs.filter(Z=>D.has(Z.id)).map(Z=>Z.id),candidatePortIds:[...Q.inputs,...Q.outputs].filter(Z=>E.has(Z.id)).map(Z=>Z.id),previousTimeStepPortIds:Q.inputs.filter(Z=>$.has(Z.id)).map(Z=>Z.id),onCandidateClick:(Z,le)=>U(Q,Z,le),onPortClick:J}}))}function QXn(g,j){return g.edges.filter(A=>ZXn(A,j)).map(A=>({id:A.id,source:A.source,target:A.target,sourceHandle:A.sourcePort||void 0,targetHandle:A.targetPort||void 0,type:"sceneEdge",data:A,markerEnd:{type:JG.ArrowClosed,color:Z1n(A),width:16,height:16},style:{stroke:Z1n(A),strokeWidth:A.cycle?4:A.kind==="manual_call"?2.5:1.8,strokeDasharray:A.kind==="previous_timestep"?"7 5":A.kind==="manual_call"?"3 4":void 0}}))}function ZXn(g,j){const A=g.projection;return j==="topology"?g.kind==="object_topology":j==="resolved"?A==="resolved":A==="applications"||!A&&!["object_topology","application_target"].includes(g.kind)}function Z1n(g){return g.cycle?"#cf4937":g.kind==="previous_timestep"?"#317b62":g.kind==="manual_call"?"#be6a54":g.kind==="object_topology"?"#7b7167":"#a59687"}function WXn(g){const j=new Set;for(const A of g.applications){for(const x of A.inputs)g.modelLibrary.some(D=>Object.prototype.hasOwnProperty.call(D.outputs,x.name))&&j.add(x.id);for(const x of A.outputs)g.modelLibrary.some(D=>Object.prototype.hasOwnProperty.call(D.inputs,x.name))&&j.add(x.id)}return j}function eKn(g,j){const A=j.role==="input"?"outputs":"inputs";return g.filter(x=>Object.prototype.hasOwnProperty.call(x[A],j.name)).sort((x,D)=>`${x.package}.${x.name}`.localeCompare(`${D.package}.${D.name}`))}function nKn({candidate:g,models:j,applications:A,onSelectModel:x,onSelectApplication:D,onClose:$}){const E=g.port.role==="input"?`Models that compute ${g.port.name}`:`Models that consume ${g.port.name}`;return re.jsxs("section",{className:"candidate-popover",style:{left:Math.min(g.x+8,window.innerWidth-390),top:Math.min(g.y-20,window.innerHeight-480)},children:[re.jsxs("header",{children:[re.jsxs("div",{children:[re.jsx("strong",{children:E}),re.jsx("span",{children:"Exact declared variable-name matches"})]}),re.jsx("button",{onClick:$,children:re.jsx(wI,{size:15})})]}),re.jsxs("div",{className:"candidate-list",children:[A.length>0&&re.jsx("div",{className:"candidate-section-label",children:"Existing applications"}),A.map(H=>re.jsxs("button",{className:"candidate-card existing",onClick:()=>D(H),children:[re.jsx("strong",{children:H.name||H.applicationId}),re.jsx("span",{children:H.modelName}),re.jsxs("small",{children:[H.targetCount," target",H.targetCount===1?"":"s"]}),re.jsx("div",{children:"Connect without adding another application"})]},H.applicationId)),j.length>0&&re.jsx("div",{className:"candidate-section-label",children:"Available models"}),j.map(H=>re.jsxs("button",{className:"candidate-card",onClick:()=>x(H),children:[re.jsx("strong",{children:H.name}),re.jsx("span",{children:H.process}),re.jsx("small",{children:H.package||H.module}),re.jsxs("div",{children:[Object.keys(H.inputs).length," inputs · ",Object.keys(H.outputs).length," outputs"]})]},H.type))]})]})}function tKn(g,j){return g.filter(A=>A.applicationId!==j.application.applicationId).filter(A=>(j.port.role==="input"?A.outputs:A.inputs).some(D=>D.name===j.port.name)).sort((A,x)=>A.applicationId.localeCompare(x.applicationId))}function iKn(g,j){if(g.port.role==="input"){const x=j.outputs.find(D=>D.name===g.port.name);if(!x)throw new Error(`Application ${j.applicationId} does not output ${g.port.name}.`);return{sourceApplication:j,sourcePort:x,targetApplication:g.application,targetPort:g.port}}const A=j.inputs.find(x=>x.name===g.port.name);if(!A)throw new Error(`Application ${j.applicationId} does not input ${g.port.name}.`);return{sourceApplication:g.application,sourcePort:g.port,targetApplication:j,targetPort:A}}function rKn({selection:g,port:j,initialization:A,interactive:x,onEditApplication:D,onRemoveApplication:$}){const E=g&&"applicationId"in g&&"selector"in g?g:null;return re.jsxs("aside",{className:"scene-inspector",children:[re.jsxs("header",{children:[re.jsx("strong",{children:"Inspector"}),g&&re.jsx("span",{children:sKn(g)})]}),!g&&re.jsxs("div",{className:"empty-inspector",children:[re.jsx(bXn,{size:28}),re.jsx("p",{children:"Select an application, object, execution, or relationship."})]}),g&&re.jsx("pre",{children:JSON.stringify(g,null,2)}),E&&x&&re.jsxs("div",{className:"inspector-actions",children:[re.jsx("button",{onClick:()=>D(E),children:"Edit application"}),re.jsx("button",{className:"danger",onClick:()=>$(E),children:"Remove application"})]}),j&&re.jsxs("section",{children:[re.jsx("h4",{children:"Selected variable"}),re.jsx("code",{children:j.name}),re.jsx("p",{children:j.expectedType})]}),g&&A.length>0&&re.jsxs("section",{className:"inspector-initialization",children:[re.jsx("h4",{children:"Initialization"}),A.slice(0,8).map(H=>re.jsxs("div",{children:[re.jsx("code",{children:H.variable}),re.jsx("span",{className:H.disposition==="unresolved"?"unresolved":"",children:H.disposition})]},`${H.applicationId}:${H.objectId}:${H.variable}`))]})]})}function cKn({graph:g,onClose:j,sendCommand:A,interactive:x}){return re.jsxs(Ske,{title:"Diagnostics and cycles",onClose:j,children:[g.diagnostics.map(D=>re.jsxs("article",{className:"diagnostic-card",children:[re.jsx("strong",{children:D.code}),re.jsx("p",{children:D.message}),D.suggestions.map($=>re.jsx("small",{children:$},$))]},`${D.code}:${D.message}`)),g.cycles.map(D=>re.jsxs("article",{className:"cycle-card",children:[re.jsx("strong",{children:D.applicationIds.join(" → ")}),re.jsx("p",{children:"Choose an input to read from the previous timestep."}),D.breakCandidates.map($=>re.jsxs("button",{disabled:!x,onClick:()=>A({action:"edit",kind:"mark_previous_timestep",applicationId:$.applicationId,input:$.input}),children:[$.applicationId,".",$.input]},`${$.applicationId}:${$.objectId}:${$.input}`))]},D.id)),g.diagnostics.length===0&&g.cycles.length===0&&re.jsx("p",{children:"No diagnostics."})]})}function uKn({graph:g,onClose:j}){return re.jsx(Ske,{title:"Initialization",onClose:j,children:g.initialization.filter(A=>A.disposition==="unresolved").map(A=>re.jsxs("article",{className:"initialization-card",children:[re.jsx("strong",{children:A.variable}),re.jsxs("span",{children:[A.applicationId," · object ",String(A.objectId)]}),re.jsx("code",{children:A.valueJulia})]},`${A.applicationId}:${A.objectId}:${A.variable}`))})}function oKn({code:g,onClose:j}){return re.jsx(Ske,{title:"Scene code",onClose:j,children:re.jsx("pre",{className:"scene-code",children:g||"Scene code is available from an interactive editor session."})})}function Ske({title:g,onClose:j,children:A}){return re.jsx("div",{className:"overlay-backdrop",onMouseDown:j,children:re.jsxs("section",{className:"overlay-panel",onMouseDown:x=>x.stopPropagation(),children:[re.jsxs("header",{children:[re.jsx("strong",{children:g}),re.jsx("button",{onClick:j,children:re.jsx(wI,{size:17})})]}),re.jsx("div",{className:"overlay-content",children:A})]})})}function sKn(g){return"applicationId"in g?g.applicationId:"objectId"in g?String(g.objectId):g.kind.replaceAll("_"," ")}function lKn(g){return g.split(".").at(-1)||g}function fKn(g){const j={selectors:[]};return g.targetInstances.length===0&&g.targetScales.length===1&&(j.scale=g.targetScales[0]),g.targetKinds.length===1&&(j.kind=g.targetKinds[0]),g.targetSpecies.length===1&&(j.species=g.targetSpecies[0]),{type:g.targetCount===1?"One":"Many",multiplicity:g.targetCount===1?"one":"many",criteria:j,julia:""}}function W1n(g,j,A){return`application:${g}:${j}:${A}`}function J7e(){const g=document.getElementById("pse-scene-graph-data");if(!g?.textContent)return V1n;try{return JSON.parse(g.textContent)}catch{return V1n}}function aKn(){const g=document.getElementById("pse-editor-config");if(!g?.textContent)return null;try{return JSON.parse(g.textContent)}catch{return null}}ezn.createRoot(document.getElementById("root")).render(re.jsx(kn.StrictMode,{children:re.jsx(VXn,{})})); diff --git a/frontend/dist/index.html b/frontend/dist/index.html index 5e0a54c49..c460938f3 100644 --- a/frontend/dist/index.html +++ b/frontend/dist/index.html @@ -4,8 +4,8 @@ PlantSimEngine Dependency Graph - - + +
diff --git a/frontend/e2e/graph-editor.spec.ts b/frontend/e2e/graph-editor.spec.ts index ff6fd7ed5..b63aae681 100644 --- a/frontend/e2e/graph-editor.spec.ts +++ b/frontend/e2e/graph-editor.spec.ts @@ -37,6 +37,8 @@ test.describe.serial("PlantSimEngine Scene graph editor", () => { await page.goto(server.url); await openAddApplication(page, "Beer"); await page.getByTestId("application-name").fill("light"); + await page.getByTestId("application-target-preview").click(); + await expect(page.getByTestId("application-target-preview-result")).toContainText("1 target object"); await page.getByTestId("application-submit").click(); let state = await waitForState(request, server.url, (value) => value.graph.metadata.applicationCount === 1); @@ -46,16 +48,31 @@ test.describe.serial("PlantSimEngine Scene graph editor", () => { await page.getByTestId("application-node-light").click(); await page.getByRole("button", { name: "Edit application" }).click(); await page.getByTestId("application-param-k").fill("0.8"); + await page.locator(".application-form label", { hasText: "Mode" }).getByRole("combobox").selectOption("clock"); + await page.locator(".application-form label", { hasText: "Step" }).getByRole("textbox").fill("2.0"); await page.getByTestId("application-submit").click(); state = await waitForState(request, server.url, (value) => findApplication(value, "light").modelParameters.k?.value === 0.8); light = findApplication(state, "light"); expect(light.targetIds).toEqual(["leaf"]); + expect(String(light.timestep)).toContain("2.0"); + }); + + test("static viewer opens the inspector without an editor connection", async ({ page }) => { + const url = new URL(server.url); + url.pathname = "/static"; + await page.goto(url.toString()); + await expect(page.getByTestId("application-node-light")).toBeVisible(); + await page.getByTestId("application-node-light").click(); + await expect(page.locator(".scene-inspector")).toContainText("light"); + await expect(page.getByText("Edit application")).toHaveCount(0); }); test("creates and breaks a cycle directly in the graph", async ({ page, request }) => { await page.goto(server.url); - await openAddApplication(page, "ReebE2E"); + await page.getByTestId("port-input-LAI").getByRole("button").click(); + await page.locator(".candidate-card", { hasText: "ReebE2E" }).click(); + await expect(page.getByTestId("application-form")).toBeVisible(); await page.getByTestId("application-name").fill("reeb"); await page.getByTestId("application-submit").click(); @@ -84,13 +101,33 @@ test.describe.serial("PlantSimEngine Scene graph editor", () => { await page.getByTestId("application-submit").click(); await waitForState(request, server.url, (value) => value.graph.applications.some((application) => application.applicationId === "consumer")); + await page.getByTestId("application-node-light").click(); + await page.getByTestId("configure-application").click(); + await page.getByTestId("call-name").fill("consumer_call"); + await page.getByTestId("call-target").selectOption("consumer"); + await page.getByTestId("add-call-binding").click(); + await waitForState(request, server.url, (value) => value.graph.metadata.callCount === 1); + await page.getByTestId("environment-provider").fill("scene"); + await page.getByTestId("apply-environment-provider").click(); + let state = await waitForState(request, server.url, (value) => findApplication(value, "light").environment?.provider === "scene"); + expect(state.graph.edges.some((edge) => edge.kind === "manual_call" && edge.call === "consumer_call")).toBe(true); + await page.getByRole("button", { name: "Done" }).click(); + await page.getByTestId("port-output-aPPFD").first().getByRole("button").click(); await page.locator(".candidate-card.existing", { hasText: "consumer" }).click(); await expect(page.getByTestId("binding-form")).toBeVisible(); + await page.getByTestId("binding-preview-button").click(); + await expect(page.getByTestId("binding-preview")).toContainText("resolved binding"); await page.getByTestId("binding-submit").click(); - let state = await waitForState(request, server.url, (value) => value.graph.edges.some((edge) => edge.targetApplicationId === "consumer" && edge.targetVariable === "aPPFD")); + state = await waitForState(request, server.url, (value) => value.graph.edges.some((edge) => edge.targetApplicationId === "consumer" && edge.targetVariable === "aPPFD")); expect(state.graph.metadata.applicationCount).toBe(3); + await page.getByTestId("application-node-light").click(); + await page.getByTestId("configure-application").click(); + await page.getByTitle("Remove consumer_call call").click(); + await waitForState(request, server.url, (value) => value.graph.metadata.callCount === 0); + await page.getByRole("button", { name: "Done" }).click(); + await page.getByTestId("application-node-consumer").click(); await page.getByRole("button", { name: "Remove application" }).click(); await waitForState(request, server.url, (value) => !value.graph.applications.some((application) => application.applicationId === "consumer")); diff --git a/frontend/src/App.test.ts b/frontend/src/App.test.ts index f2207c616..6f27897fa 100644 --- a/frontend/src/App.test.ts +++ b/frontend/src/App.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { applicationPortId, applicationsForPort, deriveCandidatePortIds, endpointsForCandidate, modelsForPort, selectorSuggestion } from "./App"; -import type { ApplicationGraphNode, GraphPort, ModelDescriptor, SceneGraphView } from "./types"; +import { applicationPortId, applicationsForPort, deriveCandidatePortIds, endpointsForCandidate, modelsForPort, objectSubtreeIds, selectorSuggestion } from "./App"; +import type { ApplicationGraphNode, GraphPort, ModelDescriptor, ObjectGraphNode, SceneGraphView } from "./types"; const output: GraphPort = { id: applicationPortId("source", "output", "signal"), name: "signal", role: "output", default: 0, defaultJulia: "0", expectedType: "Int" }; const input: GraphPort = { id: applicationPortId("consumer", "input", "signal"), name: "signal", role: "input", default: 0, defaultJulia: "0", expectedType: "Int" }; @@ -40,12 +40,24 @@ describe("selector suggestions", () => { }); }); +describe("object topology scoping", () => { + it("includes the selected object and all descendants", () => { + const objects: ObjectGraphNode[] = [ + object("plant", null), + object("leaf", "plant"), + object("cell", "leaf"), + object("soil", null), + ]; + expect(new Set(objectSubtreeIds(objects, "plant"))).toEqual(new Set(["plant", "leaf", "cell"])); + }); +}); + function application(id: string, inputs: GraphPort[], outputs: GraphPort[], targetIds: unknown[]): ApplicationGraphNode { return { id: `application:${id}`, applicationId: id, name: id, process: id, modelType: id, modelName: id, module: "Main", package: null, modelParameters: {}, selector: { type: "One", multiplicity: "one", criteria: { selectors: [], scale: "Leaf" }, julia: "" }, targetIds, targetCount: targetIds.length, targetScales: ["Leaf"], targetKinds: [], targetSpecies: [], targetInstances: [], timestep: null, clock: null, - inputs, outputs, environmentInputs: [], environmentOutputs: [], modelStorage: "shared_application", objectOverrides: [], + inputs, outputs, environmentInputs: [], environmentOutputs: [], inputBindings: {}, callBindings: {}, environment: null, meteoBindings: {}, meteoWindow: null, outputRouting: {}, updates: [], modelStorage: "shared_application", objectOverrides: [], }; } @@ -53,6 +65,10 @@ function model(name: string, inputs: Record, outputs: Record; type FlowEdge = Edge; type CandidatePopover = { port: GraphPort; application: ApplicationGraphNode; x: number; y: number }; type CycleBreakSelection = { application: ApplicationGraphNode; port: GraphPort }; -type InspectorSelection = ApplicationGraphNode | ObjectGraphNode | ExecutionGraphNode | SceneGraphEdge | null; +type InspectorSelection = ApplicationGraphNode | InstanceDescriptor | ObjectGraphNode | ExecutionGraphNode | EnvironmentGraphNode | SceneRootDescriptor | SceneGraphEdge | null; +type GraphScopeFilter = { label: string; objectIds: unknown[] }; type ApplicationFormState = { mode: "add" | "update"; scope?: "application" | "template"; @@ -77,6 +84,7 @@ export default function App() { const [detailMode, setDetailMode] = useState(() => loadInitialGraph().metadata.applicationCount > 24 ? "overview" : "detail"); const [query, setQuery] = useState(""); const [selected, setSelected] = useState(null); + const [scopeFilter, setScopeFilter] = useState(null); const [selectedPort, setSelectedPort] = useState(null); const [candidate, setCandidate] = useState(null); const [showDiagnostics, setShowDiagnostics] = useState(false); @@ -94,9 +102,12 @@ export default function App() { const [canRedo, setCanRedo] = useState(false); const [feedback, setFeedback] = useState(null); const [applicationForm, setApplicationForm] = useState(null); + const [targetPreview, setTargetPreview] = useState(null); const [objectForm, setObjectForm] = useState(null); const [overrideApplication, setOverrideApplication] = useState(null); + const [configurationApplicationId, setConfigurationApplicationId] = useState(null); const [bindingForm, setBindingForm] = useState(null); + const [bindingPreview, setBindingPreview] = useState(null); const [cycleBreakMode, setCycleBreakMode] = useState(false); const [cycleBreakSelection, setCycleBreakSelection] = useState(null); const [nodes, setNodes, onNodesChange] = useNodesState([]); @@ -128,6 +139,10 @@ export default function App() { } return index; }, [graph.applications]); + const scopedObjectIds = useMemo( + () => scopeFilter ? new Set(scopeFilter.objectIds.map(objectKey)) : null, + [scopeFilter], + ); useEffect(() => { if (!editorConfig?.websocketUrl) return; @@ -142,6 +157,12 @@ export default function App() { setAutosavePath(payload.autosavePath ?? null); setSavePath(payload.savePath ?? null); setRecentPaths(payload.recentPaths ?? []); + if (payload.selectorPreview) setBindingPreview(payload.selectorPreview); + if (payload.targetPreview) setTargetPreview(payload.targetPreview); + if (payload.ok === false) { + setBindingPreview(null); + setTargetPreview(null); + } setCanUndo(Boolean(payload.canUndo)); setCanRedo(Boolean(payload.canRedo)); setFeedback(payload.ok === false ? payload.diagnostics?.[0] || "The edit failed." : null); @@ -176,6 +197,7 @@ export default function App() { view, detailMode, query, + scopedObjectIds, unresolvedPortIds, previousPortIds, candidatePortIds, @@ -191,19 +213,32 @@ export default function App() { const layoutMode: LayoutMode = view === "topology" ? "topology" : detailMode === "overview" ? "overview" : "data_flow"; layoutGraph(nextNodes, nextEdges, layoutMode).then(setNodes); setEdges(nextEdges); - }, [candidatePortIds, cycleBreakMode, cycleBreakPortIds, cyclicApplications, detailMode, graph, openCandidates, previousPortIds, query, setEdges, setNodes, unresolvedPortIds, view]); + }, [candidatePortIds, cycleBreakMode, cycleBreakPortIds, cyclicApplications, detailMode, graph, openCandidates, previousPortIds, query, scopedObjectIds, setEdges, setNodes, unresolvedPortIds, view]); const inspectSelection = useCallback((_: unknown, node: FlowNode) => { if (node.data.nodeKind === "application") { setSelected(applicationById.get(node.data.applicationId) ?? null); } else { setSelected(node.data.detail); + if (node.data.nodeKind === "object") { + const object = node.data.detail as ObjectGraphNode; + setScopeFilter({ + label: `subtree ${object.name || String(object.objectId)}`, + objectIds: objectSubtreeIds(graph.objects, object.objectId), + }); + } else if (node.data.nodeKind === "instance") { + const instance = node.data.detail as InstanceDescriptor; + setScopeFilter({ label: `instance ${instance.name}`, objectIds: instance.objectIds }); + } else if (node.data.nodeKind === "scene") { + setScopeFilter(null); + } } setSelectedPort(null); - }, [applicationById]); + }, [applicationById, graph.objects]); const selectCandidateModel = useCallback((model: ModelDescriptor) => { if (!candidate) return; + setTargetPreview(null); setApplicationForm({ mode: "add", initialModelType: model.type, @@ -267,6 +302,19 @@ export default function App() { setOverrideApplication(null); }, [connected, sendCommand]); + const removeOverride = useCallback((value: OverrideFormValue) => { + if (!connected) { + setFeedback("Removing an override requires an interactive Julia editor session."); + return; + } + sendCommand({ + action: "edit", + kind: value.scope === "instance" ? "remove_instance_override" : "remove_object_override", + ...value, + }); + setOverrideApplication(null); + }, [connected, sendCommand]); + const connectPorts = useCallback((connection: Connection) => { if (!connection.sourceHandle || !connection.targetHandle) return; const source = portIndex.get(connection.sourceHandle); @@ -281,12 +329,17 @@ export default function App() { targetApplication: target.application, targetPort: target.port, }); + setBindingPreview(null); }, [portIndex]); const activeInitialization = useMemo(() => { if (!selected) return graph.initialization; if ("applicationId" in selected) return graph.initialization.filter((row) => row.applicationId === selected.applicationId); if ("objectId" in selected) return graph.initialization.filter((row) => String(row.objectId) === String(selected.objectId)); + if ("objectIds" in selected) { + const ids = new Set(selected.objectIds.map(objectKey)); + return graph.initialization.filter((row) => ids.has(objectKey(row.objectId))); + } return graph.initialization; }, [graph.initialization, selected]); @@ -325,7 +378,7 @@ export default function App() { {detailMode === "overview" ? "Overview Mode - Show Detailed View" : "Show Overview"} )} - {editorConfig && } + {editorConfig && } {editorConfig && } {editorConfig && } {editorConfig && } @@ -351,6 +404,13 @@ export default function App() { )} {feedback &&
{feedback}
} + {scopeFilter && ( +
+ Showing {view === "resolved" ? "executions" : view === "applications" ? "applications" : "topology"} for {scopeFilter.label} ({scopeFilter.objectIds.length} objects) + {view === "topology" && } + +
+ )}
@@ -378,18 +438,22 @@ export default function App() { port={selectedPort} initialization={activeInitialization} interactive={connected} - onEditApplication={(application) => setApplicationForm({ - mode: "update", - scope: application.targetInstances.length > 0 ? "template" : "application", - instance: application.targetInstances[0], - application, - })} + onEditApplication={(application) => { + setTargetPreview(null); + setApplicationForm({ + mode: "update", + scope: application.targetInstances.length > 0 ? "template" : "application", + instance: application.targetInstances[0], + application, + }); + }} onRemoveApplication={(application) => sendCommand({ action: "edit", kind: application.targetInstances.length > 0 ? "remove_template_application" : "remove_application", instance: application.targetInstances[0], applicationId: application.applicationId, })} + onConfigureApplication={(application) => setConfigurationApplicationId(application.applicationId)} onOverrideApplication={setOverrideApplication} onEditObject={(object) => setObjectForm({ mode: "update", object })} onRemoveObject={(object) => sendCommand({ action: "edit", kind: "remove_object", objectId: object.objectId, recursive: true })} @@ -404,6 +468,7 @@ export default function App() { onSelectModel={selectCandidateModel} onSelectApplication={(application) => { setBindingForm(endpointsForCandidate(candidate, application)); + setBindingPreview(null); setCandidate(null); }} onClose={() => setCandidate(null)} @@ -423,18 +488,30 @@ export default function App() { initialModelType={applicationForm.initialModelType} suggestedSelector={applicationForm.suggestedSelector} nameReadOnly={applicationForm.scope === "template"} + preview={targetPreview} + onPreview={(selector) => { setTargetPreview(null); sendCommand({ action: "preview_application_targets", selector }); }} onSubmit={submitApplication} - onClose={() => setApplicationForm(null)} + onClose={() => { setApplicationForm(null); setTargetPreview(null); }} /> )} {bindingForm && ( - setBindingForm(null)} /> + { setBindingPreview(null); sendCommand({ action: "preview_input_binding", ...value }); }} + onSubmit={submitBinding} + onClose={() => { setBindingForm(null); setBindingPreview(null); }} + /> )} {objectForm && ( setObjectForm(null)} /> )} {overrideApplication && ( - setOverrideApplication(null)} /> + setOverrideApplication(null)} /> + )} + {configurationApplicationId && applicationById.get(configurationApplicationId) && ( + setConfigurationApplicationId(null)} /> )} {cycleBreakSelection && ( | null; unresolvedPortIds: Set; previousPortIds: Set; candidatePortIds: Set; @@ -489,7 +568,37 @@ function buildNodes({ }): FlowNode[] { const matches = (value: unknown) => !query || JSON.stringify(value).toLowerCase().includes(query.toLowerCase()); if (view === "topology") { - return graph.objects.filter(matches).map((object) => ({ + const sceneDetail: SceneRootDescriptor = { + entity: "scene", + objectCount: graph.metadata.objectCount, + instanceCount: graph.metadata.instanceCount, + applicationCount: graph.metadata.applicationCount, + }; + const sceneNode: FlowNode = { + id: "scene:root", + type: "entity", + position: { x: 0, y: 0 }, + data: { + nodeKind: "scene", + title: graph.metadata.title || "Scene", + subtitle: "scene root", + badges: [`${graph.metadata.instanceCount} instances`, `${graph.metadata.objectCount} objects`], + detail: sceneDetail, + }, + }; + const instanceNodes: FlowNode[] = graph.instances.filter(matches).map((instance) => ({ + id: instance.id, + type: "entity", + position: { x: 0, y: 0 }, + data: { + nodeKind: "instance", + title: instance.name, + subtitle: [instance.kind, instance.species].filter(Boolean).join(" · ") || "object instance", + badges: [`${instance.objectIds.length} objects`, `${instance.applicationIds.length} applications`, `${instance.instanceOverrides.length + instance.objectOverrides.length} overrides`], + detail: instance, + }, + })); + const objectNodes: FlowNode[] = graph.objects.filter(matches).map((object) => ({ id: object.id, type: "entity", position: { x: 0, y: 0 }, @@ -501,10 +610,13 @@ function buildNodes({ detail: object, }, })); + return [sceneNode, ...instanceNodes, ...objectNodes]; } if (view === "resolved") { const applications = new Map(graph.applications.map((application) => [application.applicationId, application])); - return graph.executions.filter(matches).map((execution) => { + const executionNodes: FlowNode[] = graph.executions + .filter((execution) => !scopedObjectIds || scopedObjectIds.has(objectKey(execution.objectId))) + .filter(matches).map((execution) => { const application = applications.get(execution.applicationId); return { id: execution.id, @@ -515,14 +627,17 @@ function buildNodes({ title: execution.applicationId, subtitle: `object ${String(execution.objectId)}`, badges: [shortType(execution.modelType), execution.overridden ? "override" : "shared"], - inputPortIds: application?.inputs.map((port) => port.id) ?? [], - outputPortIds: application?.outputs.map((port) => port.id) ?? [], + inputPortIds: [...(application?.inputs ?? []), ...(application?.environmentInputs ?? [])].map((port) => port.id), + outputPortIds: [...(application?.outputs ?? []), ...(application?.environmentOutputs ?? [])].map((port) => port.id), detail: execution, }, }; }); + return [...executionNodes, ...environmentNodes(graph, "resolved")]; } - return graph.applications.filter(matches).map((application) => ({ + const applicationNodes: FlowNode[] = graph.applications + .filter((application) => !scopedObjectIds || application.targetIds.some((id) => scopedObjectIds.has(objectKey(id)))) + .filter(matches).map((application) => ({ id: application.id, type: "application", position: { x: 0, y: 0 }, @@ -541,10 +656,38 @@ function buildNodes({ onCycleBreak, }, })); + return [...applicationNodes, ...environmentNodes(graph, "applications")]; } +function environmentNodes(graph: SceneGraphView, projection: "applications" | "resolved"): FlowNode[] { + const relevant = graph.edges.filter((edge) => edge.kind === "environment_binding" && edge.projection === projection); + const ids = new Set(relevant.flatMap((edge) => [edge.source, edge.target]).filter((id) => id.startsWith("environment:"))); + return [...ids].map((id) => { + const provider = id.slice("environment:".length); + const inputs = uniqueStrings(relevant.filter((edge) => edge.target === id).map((edge) => edge.targetPort).filter(Boolean) as string[]); + const outputs = uniqueStrings(relevant.filter((edge) => edge.source === id).map((edge) => edge.sourcePort).filter(Boolean) as string[]); + return { + id, + type: "entity", + position: { x: 0, y: 0 }, + data: { + nodeKind: "environment", + title: provider, + subtitle: "environment provider", + badges: [`${outputs.length} inputs`, `${inputs.length} outputs`], + inputPortIds: inputs, + outputPortIds: outputs, + detail: { provider }, + }, + }; + }); +} + +function uniqueStrings(values: string[]) { return [...new Set(values)]; } + function buildEdges(graph: SceneGraphView, view: GraphViewMode): FlowEdge[] { - return graph.edges + const sourceEdges = view === "topology" ? [...graph.edges, ...topologyContainerEdges(graph)] : graph.edges; + return sourceEdges .filter((edge) => edgeProjectionMatches(edge, view)) .map((edge) => ({ id: edge.id, @@ -563,6 +706,65 @@ function buildEdges(graph: SceneGraphView, view: GraphViewMode): FlowEdge[] { })); } +function topologyContainerEdges(graph: SceneGraphView): SceneGraphEdge[] { + const edges: SceneGraphEdge[] = []; + const instanceObjectIds = new Set(graph.instances.flatMap((instance) => instance.objectIds.map(objectKey))); + for (const instance of graph.instances) { + edges.push({ + id: `topology:scene:${instance.id}`, + source: "scene:root", + target: instance.id, + kind: "object_topology", + projection: "topology", + cycle: false, + }); + edges.push({ + id: `topology:${instance.id}:object:${String(instance.rootId)}`, + source: instance.id, + target: `object:${String(instance.rootId)}`, + kind: "object_topology", + projection: "topology", + cycle: false, + }); + } + for (const object of graph.objects) { + if (object.parent === null && !instanceObjectIds.has(objectKey(object.objectId))) { + edges.push({ + id: `topology:scene:${object.id}`, + source: "scene:root", + target: object.id, + kind: "object_topology", + projection: "topology", + cycle: false, + }); + } + } + return edges; +} + +export function objectSubtreeIds(objects: ObjectGraphNode[], rootId: unknown): unknown[] { + const children = new Map(); + for (const object of objects) { + if (object.parent === null) continue; + const key = objectKey(object.parent); + children.set(key, [...(children.get(key) ?? []), object.objectId]); + } + const result: unknown[] = []; + const pending: unknown[] = [rootId]; + const visited = new Set(); + while (pending.length > 0) { + const id = pending.pop()!; + const key = objectKey(id); + if (visited.has(key)) continue; + visited.add(key); + result.push(id); + pending.push(...(children.get(key) ?? [])); + } + return result; +} + +function objectKey(value: unknown) { return String(value); } + function edgeProjectionMatches(edge: SceneGraphEdge, view: GraphViewMode) { const projection = (edge as SceneGraphEdge & { projection?: string }).projection; if (view === "topology") return edge.kind === "object_topology"; @@ -575,6 +777,7 @@ function edgeColor(edge: SceneGraphEdge) { if (edge.kind === "previous_timestep") return "#317b62"; if (edge.kind === "manual_call") return "#be6a54"; if (edge.kind === "object_topology") return "#7b7167"; + if (edge.kind === "environment_binding") return "#367b8b"; return "#a59687"; } @@ -668,7 +871,7 @@ export function endpointsForCandidate(candidate: CandidatePopover, application: return { sourceApplication: candidate.application, sourcePort: candidate.port, targetApplication: application, targetPort }; } -function Inspector({ selection, port, initialization, interactive, onEditApplication, onRemoveApplication, onOverrideApplication, onEditObject, onRemoveObject }: { selection: InspectorSelection; port: GraphPort | null; initialization: SceneGraphView["initialization"]; interactive: boolean; onEditApplication: (application: ApplicationGraphNode) => void; onRemoveApplication: (application: ApplicationGraphNode) => void; onOverrideApplication: (application: ApplicationGraphNode) => void; onEditObject: (object: ObjectGraphNode) => void; onRemoveObject: (object: ObjectGraphNode) => void }) { +function Inspector({ selection, port, initialization, interactive, onEditApplication, onConfigureApplication, onRemoveApplication, onOverrideApplication, onEditObject, onRemoveObject }: { selection: InspectorSelection; port: GraphPort | null; initialization: SceneGraphView["initialization"]; interactive: boolean; onEditApplication: (application: ApplicationGraphNode) => void; onConfigureApplication: (application: ApplicationGraphNode) => void; onRemoveApplication: (application: ApplicationGraphNode) => void; onOverrideApplication: (application: ApplicationGraphNode) => void; onEditObject: (object: ObjectGraphNode) => void; onRemoveObject: (object: ObjectGraphNode) => void }) { const application = selection && "applicationId" in selection && "selector" in selection ? selection as ApplicationGraphNode : null; const object = selection && "objectId" in selection && !("applicationId" in selection) ? selection as ObjectGraphNode : null; return ( @@ -676,7 +879,7 @@ function Inspector({ selection, port, initialization, interactive, onEditApplica
Inspector{selection && {selectionLabel(selection)}}
{!selection &&

Select an application, object, execution, or relationship.

} {selection &&
{JSON.stringify(selection, null, 2)}
} - {application && interactive &&
{application.targetInstances.length > 0 && }
} + {application && interactive &&
{application.targetInstances.length === 0 && }{application.targetInstances.length > 0 && }
} {object && interactive &&
} {port &&

Selected variable

{port.name}

{port.expectedType}

} {selection && initialization.length > 0 && ( @@ -775,6 +978,9 @@ function Overlay({ title, onClose, children }: { title: string; onClose: () => v function selectionLabel(selection: Exclude) { if ("applicationId" in selection) return selection.applicationId; if ("objectId" in selection) return String(selection.objectId); + if ("objectIds" in selection) return selection.name; + if ("entity" in selection) return "Scene"; + if ("provider" in selection) return selection.provider; return selection.kind.replaceAll("_", " "); } diff --git a/frontend/src/ApplicationConfigurationForm.tsx b/frontend/src/ApplicationConfigurationForm.tsx new file mode 100644 index 000000000..9300d06c0 --- /dev/null +++ b/frontend/src/ApplicationConfigurationForm.tsx @@ -0,0 +1,88 @@ +import { useMemo, useState } from "react"; +import { Check, Plus, Trash2, X } from "lucide-react"; +import type { ApplicationGraphNode, SelectorDescriptor } from "./types"; + +type UpdateRule = { variables: string[]; after: string[] }; + +export function ApplicationConfigurationForm({ + application, + applications, + onCommand, + onClose, +}: { + application: ApplicationGraphNode; + applications: ApplicationGraphNode[]; + onCommand: (command: Record) => void; + onClose: () => void; +}) { + const otherApplications = applications.filter((item) => item.applicationId !== application.applicationId); + const [callName, setCallName] = useState(""); + const [calleeId, setCalleeId] = useState(otherApplications[0]?.applicationId || ""); + const [provider, setProvider] = useState(String(application.environment?.provider || "scene")); + const [updateRules, setUpdateRules] = useState(application.updates || []); + const [updateVariable, setUpdateVariable] = useState(application.outputs[0]?.name || ""); + const [updateAfter, setUpdateAfter] = useState(otherApplications[0]?.applicationId || ""); + const selectedCallee = otherApplications.find((item) => item.applicationId === calleeId); + const callSelector = useMemo(() => ({ + type: selectedCallee?.targetCount === 1 ? "One" : "Many", + multiplicity: selectedCallee?.targetCount === 1 ? "one" : "many", + criteria: { + selectors: [], + within: { type: "SceneScope" }, + application: calleeId, + }, + julia: "", + }), [calleeId, selectedCallee?.targetCount]); + + const addCall = () => { + if (!callName.trim() || !calleeId) return; + onCommand({ + action: "edit", + kind: "set_call_binding", + applicationId: application.applicationId, + call: callName.trim(), + selector: callSelector, + }); + setCallName(""); + }; + + const addUpdateRule = () => { + if (!updateVariable || !updateAfter) return; + setUpdateRules((current) => [ + ...current.filter((rule) => !rule.variables.includes(updateVariable)), + { variables: [updateVariable], after: [updateAfter] }, + ]); + }; + + return
+
event.stopPropagation()} data-testid="application-configuration-form"> +
Configure {application.applicationId}Authored coupling and execution policy, validated by Julia
+
+
Explicit input bindings + {Object.entries(application.inputBindings).length === 0 &&

No authored input bindings. Unique same-object producers may still be inferred.

} +
{Object.entries(application.inputBindings).map(([input, selector]) =>
{input}{selector.julia || selector.type}
)}
+
+ +
Manual calls +
{Object.entries(application.callBindings).map(([call, selector]) =>
{call}{selector.julia || selector.type}
)}
+
+
+ +
Environment +
+ {Object.keys(application.meteoBindings || {}).length > 0 && {JSON.stringify(application.meteoBindings)}} +
+ +
Output routing +
{application.outputs.map((output) => )}
+
+ +
Duplicate-writer ordering +
{updateRules.map((rule, index) =>
{rule.variables.join(", ")}after {rule.after.join(", ")}
)}
+
+
+
+
+
+
; +} diff --git a/frontend/src/ApplicationForm.tsx b/frontend/src/ApplicationForm.tsx index a887487cb..8e86e0a73 100644 --- a/frontend/src/ApplicationForm.tsx +++ b/frontend/src/ApplicationForm.tsx @@ -1,6 +1,6 @@ import { useEffect, useMemo, useState } from "react"; -import { Check, X } from "lucide-react"; -import type { ApplicationGraphNode, ModelConstructorField, ModelDescriptor, ObjectGraphNode, SelectorDescriptor } from "./types"; +import { Check, Eye, X } from "lucide-react"; +import type { ApplicationGraphNode, ModelConstructorField, ModelDescriptor, ObjectGraphNode, SelectorDescriptor, TargetPreview } from "./types"; export type ApplicationFormValue = { applicationId?: string; @@ -19,6 +19,8 @@ export function ApplicationForm({ initialModelType, suggestedSelector, nameReadOnly=false, + preview, + onPreview, onSubmit, onClose, }: { @@ -29,6 +31,8 @@ export function ApplicationForm({ initialModelType?: string; suggestedSelector?: SelectorDescriptor; nameReadOnly?: boolean; + preview: TargetPreview | null; + onPreview: (selector: SelectorDescriptor) => void; onSubmit: (value: ApplicationFormValue) => void; onClose: () => void; }) { @@ -43,6 +47,9 @@ export function ApplicationForm({ const [kind, setKind] = useState(stringCriterion(initialSelector, "kind")); const [species, setSpecies] = useState(stringCriterion(initialSelector, "species")); const [objectName, setObjectName] = useState(stringCriterion(initialSelector, "name")); + const initialWithin = structuredCriterion(initialSelector, "within"); + const [scope, setScope] = useState(initialWithin?.type === "Scope" ? "named_scope" : "scene"); + const [scopeName, setScopeName] = useState(initialWithin?.type === "Scope" ? String(initialWithin.name || "") : ""); const [timestepMode, setTimestepMode] = useState<"default" | "clock">(application?.timestep ? "clock" : "default"); const [dt, setDt] = useState("1.0"); const [phase, setPhase] = useState("0.0"); @@ -65,18 +72,23 @@ export function ApplicationForm({ return clauses.length ? clauses.join(", ") : "all scene objects"; }, [kind, objectName, scale, species]); - const submit = () => { + const selector = (): SelectorDescriptor => { const criteria: Record = { selectors: [] }; + criteria.within = scope === "named_scope" && scopeName ? { type: "Scope", name: scopeName } : { type: "SceneScope" }; if (scale) criteria.scale = scale; if (kind) criteria.kind = kind; if (species) criteria.species = species; if (objectName) criteria.name = objectName; + return { type: selectorType(multiplicity), multiplicity, criteria, julia: "" }; + }; + + const submit = () => { onSubmit({ applicationId: application?.applicationId, modelType, name: name.trim(), parameters, - selector: { type: selectorType(multiplicity), multiplicity, criteria, julia: "" }, + selector: selector(), timestep: timestepMode === "clock" ? { mode: "clock", dt, phase } : { mode: "default" }, }); }; @@ -94,12 +106,16 @@ export function ApplicationForm({
Target selector
+ + {scope === "named_scope" && }

Julia will resolve {multiplicity.replace("_", " ")} target from {targetSummary}.

+ + {preview &&
{preview.count} target object{preview.count === 1 ? "" : "s"}{preview.objectIds.map(String).join(", ") || "No targets"}
}
Timestep
{timestepMode === "clock" && <>}
@@ -142,5 +158,6 @@ function displayDefault(value: unknown, type: string) { function defaultApplicationName(model: ModelDescriptor | null) { return model?.process || model?.name || "application"; } function defaultSelector(objects: ObjectGraphNode[]): SelectorDescriptor { const scale = unique(objects.map((object) => object.scale))[0]; return { type: "Many", multiplicity: "many", criteria: scale ? { selectors: [], scale } : { selectors: [] }, julia: "" }; } function stringCriterion(selector: SelectorDescriptor, key: string) { const value = selector.criteria[key]; return typeof value === "string" ? value : ""; } +function structuredCriterion(selector: SelectorDescriptor, key: string) { const value = selector.criteria[key]; return value && typeof value === "object" ? value as Record : null; } function selectorType(value: SelectorDescriptor["multiplicity"]) { return value === "one" ? "One" : value === "optional_one" ? "OptionalOne" : "Many"; } function unique(values: Array) { return [...new Set(values.filter((value): value is string => Boolean(value)))].sort(); } diff --git a/frontend/src/BindingForm.tsx b/frontend/src/BindingForm.tsx index 42bfb99e2..951a9faa6 100644 --- a/frontend/src/BindingForm.tsx +++ b/frontend/src/BindingForm.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; -import { Link2, X } from "lucide-react"; -import type { ApplicationGraphNode, GraphPort, ObjectGraphNode, SelectorDescriptor } from "./types"; +import { Eye, Link2, X } from "lucide-react"; +import type { ApplicationGraphNode, GraphPort, ObjectGraphNode, SelectorDescriptor, SelectorPreview } from "./types"; export type BindingFormValue = { applicationId: string; @@ -15,32 +15,59 @@ export type BindingEndpoints = { targetPort: GraphPort; }; -export function BindingForm({ endpoints, objects, onSubmit, onClose }: { endpoints: BindingEndpoints; objects: ObjectGraphNode[]; onSubmit: (value: BindingFormValue) => void; onClose: () => void }) { +type BindingFormProps = { + endpoints: BindingEndpoints; + objects: ObjectGraphNode[]; + preview: SelectorPreview | null; + onPreview: (value: BindingFormValue) => void; + onSubmit: (value: BindingFormValue) => void; + onClose: () => void; +}; + +export function BindingForm({ endpoints, objects, preview, onPreview, onSubmit, onClose }: BindingFormProps) { const sameTargets = sameValues(endpoints.sourceApplication.targetIds, endpoints.targetApplication.targetIds); const [multiplicity, setMultiplicity] = useState(endpoints.sourceApplication.targetCount > 1 && endpoints.targetApplication.targetCount === 1 ? "many" : "one"); const [relation, setRelation] = useState(sameTargets ? "self" : ""); + const [scope, setScope] = useState("scene"); + const [scopeName, setScopeName] = useState(""); + const [ancestorScale, setAncestorScale] = useState(""); const [scale, setScale] = useState(onlyOrEmpty(endpoints.sourceApplication.targetScales)); const [kind, setKind] = useState(onlyOrEmpty(endpoints.sourceApplication.targetKinds)); + const [species, setSpecies] = useState(onlyOrEmpty(endpoints.sourceApplication.targetSpecies)); const [sourceName, setSourceName] = useState(""); + const [sourceFilter, setSourceFilter] = useState<"application" | "process">("application"); + const [policy, setPolicy] = useState("automatic"); + const [window, setWindow] = useState(""); const scales = unique(objects.map((object) => object.scale)); const kinds = unique(objects.map((object) => object.kind)); + const speciesOptions = unique(objects.map((object) => object.species)); const names = unique(objects.map((object) => object.name)); - const submit = () => { + const value = (): BindingFormValue => { const criteria: Record = { selectors: [], - application: endpoints.sourceApplication.applicationId, var: endpoints.sourcePort.name, }; + criteria[sourceFilter] = sourceFilter === "application" + ? endpoints.sourceApplication.applicationId + : endpoints.sourceApplication.process; + const within = scopeDescriptor(scope, scopeName, ancestorScale); + if (within) criteria.within = within; if (relation) criteria.relation = relation; if (scale) criteria.scale = scale; if (kind) criteria.kind = kind; + if (species) criteria.species = species; if (sourceName) criteria.name = sourceName; - onSubmit({ + if (policy !== "automatic") criteria.policy = { type: policyType(policy) }; + if (window.trim()) { + const parsed = Number(window); + if (Number.isFinite(parsed)) criteria.window = parsed; + } + return { applicationId: endpoints.targetApplication.applicationId, input: endpoints.targetPort.name, selector: { type: selectorType(multiplicity), multiplicity, criteria, julia: "" }, - }); + }; }; return
@@ -50,13 +77,26 @@ export function BindingForm({ endpoints, objects, onSubmit, onClose }: { endpoin
Producer{endpoints.sourceApplication.applicationId}{endpoints.sourcePort.name}
Consumer{endpoints.targetApplication.applicationId}{endpoints.targetPort.name}
Source object selector
+ + {scope === "ancestor" && } + {scope === "named_scope" && } + + + +
+ {preview &&
+ {preview.bindingCount} resolved binding{preview.bindingCount === 1 ? "" : "s"} + {preview.consumerObjectIds.length} consumer object{preview.consumerObjectIds.length === 1 ? "" : "s"} from {preview.sourceObjectIds.length} source object{preview.sourceObjectIds.length === 1 ? "" : "s"} + {preview.sourceApplicationIds.length > 0 && {preview.sourceApplicationIds.join(", ")}} + {preview.diagnostics.map((diagnostic) =>

{diagnostic}

)} +
}
-
+
; } @@ -69,3 +109,13 @@ function sameValues(left: unknown[], right: unknown[]) { return left.length === function onlyOrEmpty(values: string[]) { return values.length === 1 ? values[0] : ""; } function selectorType(value: SelectorDescriptor["multiplicity"]) { return value === "one" ? "One" : value === "optional_one" ? "OptionalOne" : "Many"; } function unique(values: Array) { return [...new Set(values.filter((value): value is string => Boolean(value)))].sort(); } +function policyType(value: string) { return value === "hold_last" ? "HoldLast" : value === "interpolate" ? "Interpolate" : value === "integrate" ? "Integrate" : "Aggregate"; } +function scopeDescriptor(scope: string, name: string, scale: string) { + if (scope === "scene") return { type: "SceneScope" }; + if (scope === "self") return { type: "Self" }; + if (scope === "subtree") return { type: "Subtree" }; + if (scope === "self_plant") return { type: "SelfPlant" }; + if (scope === "ancestor") return { type: "Ancestor", scale: scale || null }; + if (scope === "named_scope" && name) return { type: "Scope", name }; + return null; +} diff --git a/frontend/src/ModelNode.tsx b/frontend/src/ModelNode.tsx index 9376fe2c1..654461149 100644 --- a/frontend/src/ModelNode.tsx +++ b/frontend/src/ModelNode.tsx @@ -77,6 +77,32 @@ export function ApplicationNode({ data, selected }: NodeProps + {(data.environmentInputs.length > 0 || data.environmentOutputs.length > 0) &&
+ + +
} )} diff --git a/frontend/src/ObjectForm.tsx b/frontend/src/ObjectForm.tsx index 81d31f939..ec6693f46 100644 --- a/frontend/src/ObjectForm.tsx +++ b/frontend/src/ObjectForm.tsx @@ -66,7 +66,8 @@ export function ObjectForm({ ; } -function parentObjectId(parent: string | null | undefined) { - if (!parent) return ""; - return parent.startsWith("object:") ? parent.slice("object:".length) : parent; +function parentObjectId(parent: unknown | null | undefined) { + if (parent === null || parent === undefined || parent === "") return ""; + const value = String(parent); + return value.startsWith("object:") ? value.slice("object:".length) : value; } diff --git a/frontend/src/OverrideForm.tsx b/frontend/src/OverrideForm.tsx index 9dabe9d6e..bce13b057 100644 --- a/frontend/src/OverrideForm.tsx +++ b/frontend/src/OverrideForm.tsx @@ -1,4 +1,4 @@ -import { Check, X } from "lucide-react"; +import { Check, Trash2, X } from "lucide-react"; import { useMemo, useState } from "react"; import { ParameterFields, parameterDefaults } from "./ApplicationForm"; import type { ApplicationGraphNode, InstanceDescriptor, ModelDescriptor } from "./types"; @@ -17,12 +17,14 @@ export function OverrideForm({ models, instances, onSubmit, + onRemove, onClose, }: { application: ApplicationGraphNode; models: ModelDescriptor[]; instances: InstanceDescriptor[]; onSubmit: (value: OverrideFormValue) => void; + onRemove: (value: OverrideFormValue) => void; onClose: () => void; }) { const matchingModels = useMemo( @@ -37,6 +39,14 @@ export function OverrideForm({ const [modelType, setModelType] = useState(application.modelType); const model = matchingModels.find((item) => item.type === modelType) || matchingModels[0] || null; const [parameters, setParameters] = useState(() => parameterDefaults(model, application)); + const baseApplicationId = mountedApplicationName(application.applicationId, instanceName); + const hasInstanceOverride = Boolean(instance?.instanceOverrides.includes(baseApplicationId)); + const hasObjectOverride = Boolean(instance?.objectOverrides.some((entry) => { + const record = entry as Record; + return String(record.object ?? record.objectId ?? "") === String(objectId) && + String(record.application ?? record.applicationId ?? "") === baseApplicationId; + })); + const canRemove = scope === "instance" ? hasInstanceOverride : hasObjectOverride; const selectModel = (value: string) => { setModelType(value); @@ -65,7 +75,12 @@ export function OverrideForm({ {model && model.constructor.fields.length > 0 &&
Model parameters
}
{scope === "instance" ? `Override ${instanceName}` : `Override object ${String(objectId)}`}Julia validates that the replacement keeps the same process and declared variable contract.
-
+
{canRemove && }
; } + +function mountedApplicationName(applicationId: string, instance: string) { + const prefix = `${instance}__`; + return applicationId.startsWith(prefix) ? applicationId.slice(prefix.length) : applicationId; +} diff --git a/frontend/src/sampleGraph.ts b/frontend/src/sampleGraph.ts index 2daa25380..89fbae006 100644 --- a/frontend/src/sampleGraph.ts +++ b/frontend/src/sampleGraph.ts @@ -56,6 +56,13 @@ function application(applicationId: string, process: string, modelName: string, outputs: outputs.map((name) => port(applicationId, "output", name)), environmentInputs: [], environmentOutputs: [], + inputBindings: {}, + callBindings: {}, + environment: null, + meteoBindings: {}, + meteoWindow: null, + outputRouting: {}, + updates: [], modelStorage: "shared_application", objectOverrides: [], }; diff --git a/frontend/src/styles.css b/frontend/src/styles.css index da40b96da..d98581f1d 100644 --- a/frontend/src/styles.css +++ b/frontend/src/styles.css @@ -126,6 +126,31 @@ font-size: 12px; } +.graph-scope-filter { + align-items: center; + background: #edf5ef; + border-bottom: 1px solid #b9d4c0; + color: #365849; + display: flex; + font-size: 12px; + gap: 10px; + justify-content: center; + min-height: 38px; + padding: 6px 14px; +} + +.graph-scope-filter button { + align-items: center; + background: #fff; + border: 1px solid #b9d4c0; + border-radius: 5px; + color: #365849; + cursor: pointer; + display: inline-flex; + gap: 5px; + padding: 5px 8px; +} + .scene-workspace { min-height: 0; display: grid; grid-template-columns: minmax(0, 1fr) 310px; } .flow-wrap { min-width: 0; min-height: 0; position: relative; } .scene-inspector { overflow: auto; padding: 16px; background: #fffaf2; border-left: 1px solid #d9cdbd; } @@ -2615,3 +2640,58 @@ h1 { background: var(--sage-dark); border-color: transparent; } +.selector-preview { + display: grid; + gap: 6px; + padding: 12px; + border: 1px solid #b9d3c4; + background: #f2f8f4; + border-radius: 6px; +} + +.selector-preview span, +.selector-preview p { + margin: 0; + color: #615c55; + font-size: 0.86rem; +} + +.selector-preview p { + color: #a44b39; +} + +.selector-preview-button { + display: inline-flex; + align-items: center; + gap: 6px; + width: max-content; +} + +.environment-ports { + border-top: 1px solid #ded5c8; + margin-top: 8px; + padding-top: 8px; +} + +.entity-node.environment { + border-color: #8fb9c2; + background: #f3fafb; +} + +.application-configuration-form { width: min(820px, 100%); } +.application-configuration-content { display: grid; gap: 14px; } +.application-configuration-content fieldset { margin: 0; padding: 12px; border: 1px solid #ded2c2; border-radius: 5px; } +.application-configuration-content legend { padding: 0 6px; color: #2d6652; font-weight: 800; } +.application-configuration-content p { margin: 0; color: #786d64; } +.configuration-list { display: grid; gap: 7px; margin-bottom: 10px; } +.configuration-list > div, +.configuration-list > label { min-height: 36px; display: grid; grid-template-columns: minmax(100px, .6fr) minmax(0, 1fr) auto; align-items: center; gap: 10px; padding: 7px 9px; border: 1px solid #e4d9ca; border-radius: 5px; background: #fffdf8; } +.configuration-list span { overflow: hidden; color: #786d64; text-overflow: ellipsis; white-space: nowrap; } +.configuration-list select, +.compact-configuration-row input, +.compact-configuration-row select { width: 100%; min-height: 34px; border: 1px solid #cdbda8; border-radius: 5px; background: #fffdf8; color: #302923; } +.compact-configuration-row { align-items: end; } +.compact-configuration-row label { display: grid; gap: 5px; color: #655b52; font-size: 12px; font-weight: 700; } +.compact-configuration-row button { min-height: 34px; display: inline-flex; align-items: center; justify-content: center; gap: 5px; } +.icon-button { width: 32px; height: 32px; display: inline-grid; place-items: center; padding: 0; } +.application-configuration-form > footer { display: flex; justify-content: flex-end; padding: 12px 15px; border-top: 1px solid #ded2c2; } diff --git a/frontend/src/types.ts b/frontend/src/types.ts index bb802aabb..cad7f6875 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -46,6 +46,13 @@ export type ApplicationGraphNode = { outputs: GraphPort[]; environmentInputs: GraphPort[]; environmentOutputs: GraphPort[]; + inputBindings: Record; + callBindings: Record; + environment: Record | null; + meteoBindings: Record; + meteoWindow: unknown; + outputRouting: Record; + updates: Array<{ variables: string[]; after: string[] }>; modelStorage: "shared_application" | "per_object_override"; objectOverrides: Array>; }; @@ -58,8 +65,8 @@ export type ObjectGraphNode = { species: string | null; name: string | null; instance: string | null; - parent: string | null; - children: string[]; + parent: unknown | null; + children: unknown[]; hasGeometry: boolean; hasStatus: boolean; }; @@ -106,6 +113,8 @@ export type SceneGraphEdge = { policy?: string; selector?: SelectorDescriptor; call?: string; + variables?: string[]; + provider?: string; projection?: "applications" | "topology" | "resolved" | "targets"; cycle: boolean; }; @@ -233,13 +242,24 @@ export type RuntimeApplicationNode = ApplicationGraphNode & { }; export type RuntimeEntityNode = { - nodeKind: "object" | "execution"; + nodeKind: "scene" | "instance" | "object" | "execution" | "environment"; title: string; subtitle: string; badges: string[]; inputPortIds?: string[]; outputPortIds?: string[]; - detail: ObjectGraphNode | ExecutionGraphNode; + detail: SceneRootDescriptor | InstanceDescriptor | ObjectGraphNode | ExecutionGraphNode | EnvironmentGraphNode; +}; + +export type EnvironmentGraphNode = { + provider: string; +}; + +export type SceneRootDescriptor = { + entity: "scene"; + objectCount: number; + instanceCount: number; + applicationCount: number; }; export type EditorState = { @@ -253,4 +273,21 @@ export type EditorState = { autosavePath?: string | null; savePath?: string | null; recentPaths?: string[]; + selectorPreview?: SelectorPreview; + targetPreview?: TargetPreview; +}; + +export type SelectorPreview = { + applicationId: string; + input: string; + consumerObjectIds: unknown[]; + sourceObjectIds: unknown[]; + sourceApplicationIds: string[]; + bindingCount: number; + diagnostics: string[]; +}; + +export type TargetPreview = { + objectIds: unknown[]; + count: number; }; diff --git a/src/visualization/scene_graph_editor_api.jl b/src/visualization/scene_graph_editor_api.jl index 17e2f4534..afdd1294f 100644 --- a/src/visualization/scene_graph_editor_api.jl +++ b/src/visualization/scene_graph_editor_api.jl @@ -13,7 +13,10 @@ struct RemoveSceneTemplateApplication <: AbstractSceneGraphEdit application_id::Symbol end -RemoveSceneTemplateApplication(instance, application_id) = +RemoveSceneTemplateApplication( + instance::Union{Symbol,AbstractString}, + application_id::Union{Symbol,AbstractString}, +) = RemoveSceneTemplateApplication(Symbol(instance), Symbol(application_id)) struct ReplaceSceneApplicationModel{M<:AbstractModel} <: AbstractSceneGraphEdit @@ -124,7 +127,10 @@ struct ReparentSceneObject <: AbstractSceneGraphEdit parent_id::Union{Nothing,ObjectId} end -ReparentSceneObject(object_id, parent_id) = ReparentSceneObject( +ReparentSceneObject( + object_id::Union{ObjectId,Symbol,AbstractString,Integer}, + parent_id::Union{Nothing,ObjectId,Symbol,AbstractString,Integer}, +) = ReparentSceneObject( ObjectId(object_id), isnothing(parent_id) ? nothing : ObjectId(parent_id), ) @@ -156,7 +162,10 @@ struct RemoveSceneObjectStatus <: AbstractSceneGraphEdit variable::Symbol end -RemoveSceneObjectStatus(object_id, variable) = +RemoveSceneObjectStatus( + object_id::Union{ObjectId,Symbol,AbstractString,Integer}, + variable::Union{Symbol,AbstractString}, +) = RemoveSceneObjectStatus(ObjectId(object_id), Symbol(variable)) struct SetSceneObjectMetadata{C} <: AbstractSceneGraphEdit @@ -181,7 +190,10 @@ struct RemoveSceneInstanceOverride <: AbstractSceneGraphEdit application_id::Symbol end -RemoveSceneInstanceOverride(instance, application_id) = +RemoveSceneInstanceOverride( + instance::Union{Symbol,AbstractString}, + application_id::Union{Symbol,AbstractString}, +) = RemoveSceneInstanceOverride(Symbol(instance), Symbol(application_id)) struct SetSceneObjectOverride{M<:AbstractModel} <: AbstractSceneGraphEdit @@ -200,7 +212,11 @@ struct RemoveSceneObjectOverride <: AbstractSceneGraphEdit application_id::Symbol end -RemoveSceneObjectOverride(instance, object_id, application_id) = +RemoveSceneObjectOverride( + instance::Union{Symbol,AbstractString}, + object_id::Union{ObjectId,Symbol,AbstractString,Integer}, + application_id::Union{Symbol,AbstractString}, +) = RemoveSceneObjectOverride(Symbol(instance), ObjectId(object_id), Symbol(application_id)) """ @@ -328,7 +344,7 @@ function _apply_scene_graph_edit!(scene::Scene, edit::UpdateSceneApplication) "Scene application `$(edit.name)` already exists.", ) end - return _replace_scene_edit_spec!( + _replace_scene_edit_spec!( scene, edit.application_id, ModelSpec( @@ -339,6 +355,12 @@ function _apply_scene_graph_edit!(scene::Scene, edit::UpdateSceneApplication) timestep=edit.timestep, ), ) + edit.name == edit.application_id || _rewrite_scene_application_references!( + scene, + edit.application_id, + edit.name, + ) + return scene end function _apply_scene_graph_edit!(scene::Scene, edit::RenameSceneApplication) @@ -346,11 +368,54 @@ function _apply_scene_graph_edit!(scene::Scene, edit::RenameSceneApplication) "Scene application `$(edit.name)` already exists.", ) spec = _scene_edit_spec(scene, edit.application_id) - return _replace_scene_edit_spec!( + _replace_scene_edit_spec!( scene, edit.application_id, ModelSpec(spec; name=edit.name), ) + _rewrite_scene_application_references!(scene, edit.application_id, edit.name) + return scene +end + +function _rewrite_scene_selector_application(selector, old_id::Symbol, new_id::Symbol) + selector isa AbstractObjectMultiplicity || return selector + rewritten = (; ( + Symbol(key) => ( + Symbol(key) == :application && value == old_id ? new_id : value + ) + for (key, value) in pairs(criteria(selector)) + )...) + return _rebuild_selector(selector, rewritten) +end + +function _rewrite_scene_application_references!(scene::Scene, old_id::Symbol, new_id::Symbol) + for (index, raw_spec) in pairs(scene.applications) + spec = as_model_spec(raw_spec) + inputs = (; ( + Symbol(name) => _rewrite_scene_selector_application(selector, old_id, new_id) + for (name, selector) in pairs(spec.inputs) + )...) + calls = (; ( + Symbol(name) => _rewrite_scene_selector_application(selector, old_id, new_id) + for (name, selector) in pairs(spec.calls) + )...) + target = _rewrite_scene_selector_application(spec.applies_to, old_id, new_id) + updates_ = Tuple( + Updates( + _update_variables(update)...; + after=Tuple(item == old_id ? new_id : item for item in _update_after(update)), + ) + for update in spec.updates + ) + scene.applications[index] = ModelSpec( + spec; + inputs=inputs, + calls=calls, + applies_to=target, + updates=updates_, + ) + end + return scene end function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneApplicationTargets) @@ -663,6 +728,16 @@ function _scene_edit_template_application_id(instance::ObjectInstance, applicati return only(matches) end +function _scene_edit_template_application_spec(instance::ObjectInstance, application_id::Symbol) + base_name = _scene_edit_template_application_id(instance, application_id) + specs = Tuple(as_model_spec(item) for item in instance.template.applications) + matches = [ + spec for (index, spec) in pairs(specs) + if _mounted_application_name(spec, index) == base_name + ] + return base_name, only(matches) +end + function _scene_edit_normalize_instance( instance::ObjectInstance; template=instance.template, @@ -774,7 +849,12 @@ end function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneInstanceOverride) _, instance = _scene_edit_instance(scene, edit.instance) - application = _scene_edit_template_application_id(instance, edit.application_id) + application, spec = _scene_edit_template_application_spec(instance, edit.application_id) + _validate_model_override_contract!( + model_(spec), + edit.model; + description="Instance override for `$(edit.instance)` application `$(application)`", + ) overrides = _scene_edit_namedtuple_set(instance.overrides, application, edit.model) return _scene_edit_replace_instance( scene, @@ -799,7 +879,12 @@ end function _apply_scene_graph_edit!(scene::Scene, edit::SetSceneObjectOverride) _, instance = _scene_edit_instance(scene, edit.instance) - application = _scene_edit_template_application_id(instance, edit.application_id) + application, spec = _scene_edit_template_application_spec(instance, edit.application_id) + _validate_model_override_contract!( + model_(spec), + edit.model; + description="Object override for `$(edit.object_id.value)` application `$(application)`", + ) object_ids = Set(_instance_object_ids(scene, instance)) edit.object_id in object_ids || error( "Object `$(edit.object_id.value)` does not belong to instance `$(edit.instance)`.", diff --git a/src/visualization/scene_graph_view.jl b/src/visualization/scene_graph_view.jl index d8b86891d..44b63a5a7 100644 --- a/src/visualization/scene_graph_view.jl +++ b/src/visualization/scene_graph_view.jl @@ -26,6 +26,7 @@ cycle. """ struct SceneCompilationReport scene::Scene + initial_status_variables::Dict{ObjectId,Set{Symbol}} applications::Any input_bindings::Any call_bindings::Any @@ -174,6 +175,58 @@ function _scene_graph_compiled( ) end +function _scene_graph_fallback_application(scene, raw_spec, timeline) + spec = as_model_spec(raw_spec) + selector = applies_to(spec) + selector isa AbstractObjectMultiplicity || error( + "Model application for process `$(process(spec))` has no valid `AppliesTo(...)` selector.", + ) + application_id = something(application_name(spec), process(spec)) + target_ids = resolve_object_ids(scene, selector) + return CompiledSceneApplication( + application_id, + spec, + process(spec), + application_name(spec), + target_ids, + selector, + timestep(spec), + _scene_application_clock(scene, spec, target_ids, timeline), + _compiled_object_model_overrides(spec, target_ids, application_id), + ) +end + +function _scene_graph_compile_applications(scene, timeline, diagnostics) + applications = _scene_graph_phase!(diagnostics, :applications, CompiledSceneApplication[]) do + _compile_scene_applications(scene, Tuple(scene.applications), timeline) + end + isempty(applications) || return applications + isempty(scene.applications) && return applications + + recovered = CompiledSceneApplication[] + recovered_ids = Set{Symbol}() + for raw_spec in scene.applications + try + application = _scene_graph_fallback_application(scene, raw_spec, timeline) + application.id in recovered_ids && error( + "Duplicate recovered scene application id `$(application.id)`.", + ) + push!(recovered_ids, application.id) + push!(recovered, application) + catch err + spec = as_model_spec(raw_spec) + application_id = something(application_name(spec), process(spec)) + push!(diagnostics, _scene_graph_diagnostic( + err, + :application_recovery; + application_ids=[application_id], + suggestions=["Fix the application selector or authored configuration."], + )) + end + end + return recovered +end + """ compile_scene_report(scene; strict=false) @@ -185,6 +238,12 @@ function compile_scene_report(scene::Scene; strict::Bool=false) # Compilation wires reference carriers and prepares generated status fields. # A viewer must not mutate the user's editable or pre-run Scene merely by # inspecting it, so all diagnostic compilation happens on a structural copy. + initial_status_variables = Dict( + object.id => Set{Symbol}( + object.status isa Status ? Symbol.(propertynames(object.status)) : Symbol[], + ) + for object in values(scene.registry.objects) + ) scene = deepcopy(scene) if strict compiled = compile_scene(scene) @@ -195,6 +254,7 @@ function compile_scene_report(scene::Scene; strict::Bool=false) ) return SceneCompilationReport( scene, + initial_status_variables, compiled.applications, compiled.input_bindings, compiled.call_bindings, @@ -212,6 +272,7 @@ function compile_scene_report(scene::Scene; strict::Bool=false) end isnothing(timeline) && return SceneCompilationReport( scene, + initial_status_variables, CompiledSceneApplication[], CompiledSceneInputBinding[], CompiledSceneCallBinding[], @@ -222,11 +283,10 @@ function compile_scene_report(scene::Scene; strict::Bool=false) nothing, ) - applications = _scene_graph_phase!(diagnostics, :applications, CompiledSceneApplication[]) do - _compile_scene_applications(scene, Tuple(scene.applications), timeline) - end + applications = _scene_graph_compile_applications(scene, timeline, diagnostics) isempty(applications) && !isempty(scene.applications) && return SceneCompilationReport( scene, + initial_status_variables, applications, CompiledSceneInputBinding[], CompiledSceneCallBinding[], @@ -262,7 +322,14 @@ function compile_scene_report(scene::Scene; strict::Bool=false) _wire_scene_input_carriers!(scene, input_bindings) end - children = _scene_graph_dependency_children(applications, input_bindings, call_bindings) + children = Dict{Symbol,Set{Symbol}}() + _scene_graph_phase!(diagnostics, :dependency_inputs, nothing) do + call_owners = _scene_call_owners(call_bindings) + _scene_input_order_edges!(children, input_bindings, call_owners) + end + _scene_graph_phase!(diagnostics, :update_order, nothing) do + _scene_update_order_edges!(children, applications) + end cycles = _scene_graph_cycle_components(applications, children) application_order = Symbol[] if isempty(cycles) @@ -304,6 +371,7 @@ function compile_scene_report(scene::Scene; strict::Bool=false) return SceneCompilationReport( scene, + initial_status_variables, applications, input_bindings, call_bindings, @@ -431,11 +499,14 @@ end function _scene_graph_application_dict(scene, application) model = _application_default_model(application) + spec = application.spec inputs = inputs_(application.spec) outputs = outputs_(application.spec) environment_inputs = meteo_inputs_(application.spec) environment_outputs = meteo_outputs_(application.spec) target_objects = [_scene_object(scene, id) for id in application.target_ids] + environment = environment_config(spec) + environment_payload = environment isa EnvironmentConfig ? environment.config : environment return Dict{String,Any}( "id" => _scene_graph_application_node_id(application.id), "applicationId" => string(application.id), @@ -463,6 +534,25 @@ function _scene_graph_application_dict(scene, application) "outputs" => [_scene_graph_port(application, :output, name, value) for (name, value) in pairs(outputs)], "environmentInputs" => [_scene_graph_port(application, :environment_input, name, value) for (name, value) in pairs(environment_inputs)], "environmentOutputs" => [_scene_graph_port(application, :environment_output, name, value) for (name, value) in pairs(environment_outputs)], + "inputBindings" => Dict( + string(name) => _scene_graph_selector_dict(selector) + for (name, selector) in pairs(value_inputs(spec)) + ), + "callBindings" => Dict( + string(name) => _scene_graph_selector_dict(selector) + for (name, selector) in pairs(model_calls(spec)) + ), + "environment" => _scene_graph_json_value(environment_payload), + "meteoBindings" => _scene_graph_json_value(meteo_bindings(spec)), + "meteoWindow" => _scene_graph_json_value(meteo_window(spec)), + "outputRouting" => _scene_graph_json_value(output_routing(spec)), + "updates" => [ + Dict( + "variables" => collect(string.(_update_variables(update))), + "after" => collect(string.(_update_after(update))), + ) + for update in updates(spec) + ], "modelStorage" => isnothing(application.model_overrides) ? "shared_application" : "per_object_override", "objectOverrides" => isnothing(application.model_overrides) ? Any[] : [ Dict( @@ -661,6 +751,139 @@ function _scene_graph_call_edges(report, level) return collect(values(edges)) end +function _scene_graph_update_edges(report) + application_ids = Set(application.id for application in report.applications) + edges = Dict{String,Any}[] + for application in report.applications + for update in updates(application.spec) + variables = _update_variables(update) + for predecessor in _update_after(update) + predecessor in application_ids || continue + edge_id = string( + "update:", predecessor, ":", application.id, ":", + join(string.(variables), ","), + ) + push!(edges, Dict{String,Any}( + "id" => edge_id, + "source" => _scene_graph_application_node_id(predecessor), + "target" => _scene_graph_application_node_id(application.id), + "sourceApplicationId" => string(predecessor), + "targetApplicationId" => string(application.id), + "variables" => collect(string.(variables)), + "kind" => "update_order", + "projection" => "applications", + "cycle" => false, + )) + end + end + end + return edges +end + +function _scene_graph_environment_edges(report, level) + environment_bindings = try + _compile_environment_bindings_for_applications(report.scene, report.applications) + catch + Any[] + end + if isempty(environment_bindings) + for application in report.applications + required_inputs = Symbol.(keys(meteo_inputs_(application.spec))) + produced_outputs = Symbol.(keys(meteo_outputs_(application.spec))) + isempty(required_inputs) && isempty(produced_outputs) && continue + source_inputs = _environment_source_variable_names(application.spec) + config = environment_config(application.spec) + provider = try + _environment_provider_from_config( + config, + _environment_backend_from_config(report.scene, config), + ) + catch + :scene + end + for object_id in application.target_ids + push!(environment_bindings, ( + application_id=application.id, + object_id=object_id, + provider=provider, + required_inputs=required_inputs, + source_inputs=source_inputs, + produced_outputs=produced_outputs, + )) + end + end + end + edges = Dict{String,Dict{String,Any}}() + for binding in environment_bindings + provider_id = string("environment:", binding.provider) + target_id = level == :resolved ? + _scene_graph_execution_node_id(binding.application_id, binding.object_id) : + _scene_graph_application_node_id(binding.application_id) + object_suffix = level == :resolved ? string(":", binding.object_id.value) : "" + for (target_variable, source_variable) in zip(binding.required_inputs, binding.source_inputs) + edge_id = string( + "environment-input:", binding.provider, ":", source_variable, ":", + binding.application_id, ":", target_variable, object_suffix, + ) + edge = get!(edges, edge_id) do + Dict{String,Any}( + "id" => edge_id, + "source" => provider_id, + "target" => target_id, + "sourcePort" => string(provider_id, ":output:", source_variable), + "targetPort" => _scene_graph_port_id( + binding.application_id, + :environment_input, + target_variable, + ), + "sourceVariable" => string(source_variable), + "targetVariable" => string(target_variable), + "targetApplicationId" => string(binding.application_id), + "sourceObjectIds" => Any[], + "targetObjectIds" => Any[], + "provider" => string(binding.provider), + "kind" => "environment_binding", + "projection" => string(level), + "cycle" => false, + ) + end + push!(edge["targetObjectIds"], _scene_graph_json_value(binding.object_id.value)) + unique!(edge["targetObjectIds"]) + end + for variable in binding.produced_outputs + edge_id = string( + "environment-output:", binding.application_id, ":", variable, ":", + binding.provider, object_suffix, + ) + edge = get!(edges, edge_id) do + Dict{String,Any}( + "id" => edge_id, + "source" => target_id, + "target" => provider_id, + "sourcePort" => _scene_graph_port_id( + binding.application_id, + :environment_output, + variable, + ), + "targetPort" => string(provider_id, ":input:", variable), + "sourceVariable" => string(variable), + "targetVariable" => string(variable), + "sourceApplicationId" => string(binding.application_id), + "sourceObjectIds" => Any[], + "targetObjectIds" => Any[], + "provider" => string(binding.provider), + "kind" => "environment_binding", + "projection" => string(level), + "cycle" => false, + ) + end + push!(edge["sourceObjectIds"], _scene_graph_json_value(binding.object_id.value)) + unique!(edge["sourceObjectIds"]) + end + end + return collect(values(edges)) +end + function _scene_graph_structure_edges(scene, applications) edges = Dict{String,Any}[] for object in values(scene.registry.objects) @@ -704,12 +927,7 @@ function _scene_graph_diagnostic_dict(diagnostic::SceneGraphDiagnostic) end function _scene_graph_initialization(report) - supplied = Dict( - object.id => Set{Symbol}( - object.status isa Status ? Symbol.(propertynames(object.status)) : Symbol[], - ) - for object in values(report.scene.registry.objects) - ) + supplied = report.initial_status_variables bindings = Dict( (binding.application_id, binding.consumer_id, binding.input) => binding for binding in report.input_bindings @@ -893,6 +1111,12 @@ function compile_scene_graph(compiled::CompiledScene; level=:applications) ) report = SceneCompilationReport( compiled.scene, + Dict( + object.id => Set{Symbol}( + object.status isa Status ? Symbol.(propertynames(object.status)) : Symbol[], + ) + for object in values(compiled.scene.registry.objects) + ), compiled.applications, compiled.input_bindings, compiled.call_bindings, @@ -920,6 +1144,9 @@ function _scene_graph_view(report::SceneCompilationReport, level) _scene_graph_binding_edges(report, :resolved), _scene_graph_call_edges(report, :applications), _scene_graph_call_edges(report, :resolved), + _scene_graph_update_edges(report), + _scene_graph_environment_edges(report, :applications), + _scene_graph_environment_edges(report, :resolved), _scene_graph_structure_edges(report.scene, report.applications), ) sort!(edges; by=edge -> edge["id"]) diff --git a/test/test-scene-graph-editor-extension.jl b/test/test-scene-graph-editor-extension.jl index 67409446c..0f221a7a0 100644 --- a/test/test-scene-graph-editor-extension.jl +++ b/test/test-scene-graph-editor-extension.jl @@ -35,6 +35,10 @@ PlantSimEngine.outputs_(::EditorConsumerModel) = (result=-Inf,) @test payload["graph"]["metadata"]["applicationCount"] == 1 @test occursin("scene = Scene", payload["sceneCode"]) + static_view = HTTP.get("http://$(session.host):$(session.port)/static?token=$(session.token)") + @test static_view.status == 200 + @test occursin("pse-scene-graph-data", String(static_view.body)) + consumer_spec = ModelSpec(EditorConsumerModel(); name=:consumer) |> AppliesTo(One(name=:leaf)) apply_edit!(session, AddSceneApplication(consumer_spec)) @@ -81,10 +85,30 @@ end "action" => "open_scene_code", "path" => snapshot_path, )) + response["ok"] || error(join(response["diagnostics"], "\n")) @test response["ok"] @test length(scene_objects(current_scene(session))) == 1 @test session.save_path == snapshot_path @test first(session.recent_paths) == snapshot_path + + reopened = edit_graph(; port=0, open_browser=false, autosave=false) + try + @test snapshot_path in reopened.recent_paths + finally + close(reopened) + end + recovered = edit_graph( + ; + port=0, + open_browser=false, + autosave=false, + recover_path=snapshot_path, + ) + try + @test length(scene_objects(current_scene(recovered))) == 1 + finally + close(recovered) + end finally close(session) end @@ -111,6 +135,23 @@ end @test object_response["ok"] @test length(scene_objects(current_scene(session))) == 1 + target_history_length = length(session.history) + target_preview = editor_extension._handle_command!(session, Dict( + "action" => "preview_application_targets", + "selector" => Dict( + "multiplicity" => "many", + "criteria" => Dict( + "selectors" => Any[], + "within" => Dict("type" => "SceneScope"), + "scale" => "Leaf", + ), + ), + )) + @test target_preview["ok"] + @test target_preview["targetPreview"]["count"] == 1 + @test target_preview["targetPreview"]["objectIds"] == ["leaf"] + @test length(session.history) == target_history_length + source_response = editor_extension._handle_command!(session, Dict( "action" => "edit", "kind" => "add_application", @@ -127,6 +168,32 @@ end )) @test source_response["ok"] + environment_response = editor_extension._handle_command!(session, Dict( + "action" => "edit", + "kind" => "set_environment_provider", + "applicationId" => "source", + "provider" => "scene", + )) + @test environment_response["ok"] + source_application = only( + PlantSimEngine.as_model_spec(spec) for spec in current_scene(session).applications + if application_name(PlantSimEngine.as_model_spec(spec)) == :source + ) + @test environment_config(source_application).provider == :scene + + routing_response = editor_extension._handle_command!(session, Dict( + "action" => "edit", + "kind" => "set_output_routing", + "applicationId" => "source", + "output" => "signal", + "route" => "stream_only", + )) + @test routing_response["ok"] + @test only( + application for application in routing_response["graph"]["applications"] + if application["applicationId"] == "source" + )["outputRouting"]["signal"] == "stream_only" + consumer_response = editor_extension._handle_command!(session, Dict( "action" => "edit", "kind" => "add_application", @@ -141,9 +208,33 @@ end )) @test consumer_response["ok"] - binding_response = editor_extension._handle_command!(session, Dict( + call_selector = Dict( + "multiplicity" => "one", + "criteria" => Dict( + "selectors" => Any[], + "within" => Dict("type" => "Self"), + "application" => "source", + ), + ) + call_response = editor_extension._handle_command!(session, Dict( "action" => "edit", - "kind" => "set_input_binding", + "kind" => "set_call_binding", + "applicationId" => "consumer", + "call" => "source_call", + "selector" => call_selector, + )) + @test call_response["ok"] + @test call_response["graph"]["metadata"]["callCount"] == 1 + remove_call_response = editor_extension._handle_command!(session, Dict( + "action" => "edit", + "kind" => "remove_call_binding", + "applicationId" => "consumer", + "call" => "source_call", + )) + @test remove_call_response["ok"] + @test remove_call_response["graph"]["metadata"]["callCount"] == 0 + + binding_command = Dict( "applicationId" => "consumer", "input" => "signal", "selector" => Dict( @@ -156,6 +247,27 @@ end "policy" => Dict("type" => "HoldLast"), ), ), + ) + history_length = length(session.history) + preview_response = editor_extension._handle_command!(session, merge( + binding_command, + Dict("action" => "preview_input_binding"), + )) + @test preview_response["ok"] + @test preview_response["selectorPreview"]["bindingCount"] == 1 + @test preview_response["selectorPreview"]["consumerObjectIds"] == ["leaf"] + @test preview_response["selectorPreview"]["sourceObjectIds"] == ["leaf"] + @test preview_response["selectorPreview"]["sourceApplicationIds"] == ["source"] + @test length(session.history) == history_length + consumer_before = only( + PlantSimEngine.as_model_spec(spec) for spec in current_scene(session).applications + if application_name(PlantSimEngine.as_model_spec(spec)) == :consumer + ) + @test isempty(consumer_before.inputs) + + binding_response = editor_extension._handle_command!(session, merge( + binding_command, + Dict("action" => "edit", "kind" => "set_input_binding"), )) @test binding_response["ok"] @test binding_response["graph"]["metadata"]["bindingCount"] == 1 @@ -186,7 +298,9 @@ end template = ObjectTemplate( ( ModelSpec(EditorSourceModel(); name=:source) |> - AppliesTo(Many(scale=:Leaf)), + AppliesTo(Many(scale=:Leaf)) |> + Environment((provider=:scene,)) |> + OutputRouting((signal=:stream_only,)), ); kind=:plant, species=:test_species, @@ -208,9 +322,12 @@ end ), ) code = editor_extension._scene_to_julia(original) + @test occursin("defined in Main", code) @test occursin("template_1 = ObjectTemplate", code) @test occursin("instances = (", code) @test occursin("Override(", code) + @test occursin("Environment((provider = :scene,))", code) + @test occursin("OutputRouting((signal = :stream_only,))", code) @test !occursin("ObjectModelOverrides", code) restored = Base.include_string(Main, code, "generated_scene_editor_test.jl") @@ -220,4 +337,25 @@ end @test restored_view.metadata["instanceCount"] == original_view.metadata["instanceCount"] @test Set(application["applicationId"] for application in restored_view.applications) == Set(application["applicationId"] for application in original_view.applications) + restored_source = PlantSimEngine.as_model_spec(first(first(restored.instances).template.applications)) + @test environment_config(restored_source).config == (provider=:scene,) + @test output_routing(restored_source) == (signal=:stream_only,) + + local_scene = Scene(Object( + :local_leaf; + name=:local_leaf, + scale=:Leaf, + status=Status(driver=1.0), + applications=( + ModelSpec(EditorSourceModel(); name=:local_source) |> + AppliesTo(One(name=:local_leaf)), + ), + )) + local_code = editor_extension._scene_to_julia(local_scene) + @test occursin("applications=(ModelSpec", local_code) + restored_local = Base.include_string(Main, local_code, "generated_local_scene_editor_test.jl") + restored_local_application = only(only(scene_objects(restored_local)).applications) + @test PlantSimEngine.application_name( + PlantSimEngine.as_model_spec(restored_local_application), + ) == :local_source end diff --git a/test/test-scene-graph-view.jl b/test/test-scene-graph-view.jl index ea3aeac0b..c29cf5bb8 100644 --- a/test/test-scene-graph-view.jl +++ b/test/test-scene-graph-view.jl @@ -2,11 +2,13 @@ abstract type AbstractSceneGraphSourceModel <: PlantSimEngine.AbstractModel end abstract type AbstractSceneGraphConsumerModel <: PlantSimEngine.AbstractModel end abstract type AbstractSceneGraphCycleAModel <: PlantSimEngine.AbstractModel end abstract type AbstractSceneGraphCycleBModel <: PlantSimEngine.AbstractModel end +abstract type AbstractSceneGraphEnvironmentModel <: PlantSimEngine.AbstractModel end PlantSimEngine.process_(::Type{AbstractSceneGraphSourceModel}) = :scene_graph_source PlantSimEngine.process_(::Type{AbstractSceneGraphConsumerModel}) = :scene_graph_consumer PlantSimEngine.process_(::Type{AbstractSceneGraphCycleAModel}) = :scene_graph_cycle_a PlantSimEngine.process_(::Type{AbstractSceneGraphCycleBModel}) = :scene_graph_cycle_b +PlantSimEngine.process_(::Type{AbstractSceneGraphEnvironmentModel}) = :scene_graph_environment struct SceneGraphSourceModel{T} <: AbstractSceneGraphSourceModel coefficient::T @@ -28,6 +30,12 @@ struct SceneGraphCycleBModel <: AbstractSceneGraphCycleBModel end PlantSimEngine.inputs_(::SceneGraphCycleBModel) = (x=-Inf,) PlantSimEngine.outputs_(::SceneGraphCycleBModel) = (y=-Inf,) +struct SceneGraphEnvironmentModel <: AbstractSceneGraphEnvironmentModel end +PlantSimEngine.inputs_(::SceneGraphEnvironmentModel) = NamedTuple() +PlantSimEngine.outputs_(::SceneGraphEnvironmentModel) = (result=-Inf,) +PlantSimEngine.meteo_inputs_(::SceneGraphEnvironmentModel) = (T=-Inf,) +PlantSimEngine.meteo_outputs_(::SceneGraphEnvironmentModel) = (leaf_temperature=-Inf,) + @testset "Scene graph discovery" begin @test AbstractSceneGraphSourceModel in available_processes() @test SceneGraphSourceModel in available_models(:scene_graph_source) @@ -302,6 +310,47 @@ end @test environment_config(spec) == (provider=:scene, sources=(T=:temperature,)) @test output_routing(spec) == (signal=:stream_only,) @test only(updates(spec)).variables == (:signal,) + configured_application = only(scene_graph_view(configured).applications) + @test configured_application["environment"]["provider"] == "scene" + @test configured_application["outputRouting"]["signal"] == "stream_only" + @test configured_application["updates"] == [Dict( + "variables" => ["signal"], + "after" => ["driver"], + )] + + ordered_writers = Scene( + Object(:leaf; name=:leaf, scale=:Leaf, status=Status(driver=1.0)); + applications=( + ModelSpec(SceneGraphSourceModel(1.0); name=:first_writer) |> + AppliesTo(One(name=:leaf)), + ModelSpec(SceneGraphSourceModel(2.0); name=:second_writer) |> + AppliesTo(One(name=:leaf)) |> + Updates(:signal; after=:first_writer), + ), + ) + update_edge = only( + edge for edge in scene_graph_view(ordered_writers).edges + if edge["kind"] == "update_order" + ) + @test update_edge["sourceApplicationId"] == "first_writer" + @test update_edge["targetApplicationId"] == "second_writer" + @test update_edge["variables"] == ["signal"] + + environment_scene = Scene( + Object(:leaf; name=:leaf, scale=:Leaf); + applications=( + ModelSpec(SceneGraphEnvironmentModel(); name=:environment_user) |> + AppliesTo(One(name=:leaf)), + ), + ) + environment_edges = [ + edge for edge in scene_graph_view(environment_scene).edges + if edge["kind"] == "environment_binding" && edge["projection"] == "applications" + ] + @test length(environment_edges) == 2 + @test Set(edge["sourceVariable"] for edge in environment_edges) == + Set(["T", "leaf_temperature"]) + @test all(haskey(edge, "provider") for edge in environment_edges) metadata = apply_scene_graph_edit( configured, @@ -317,6 +366,102 @@ end @test only(scene_objects(metadata)).status.driver == 1.0 end +@testset "Scene graph edit command coverage" begin + scene = Scene( + Object(:source_object; name=:source_object, scale=:Leaf, status=Status(driver=1.0)), + Object(:consumer_object; name=:consumer_object, scale=:Plant); + applications=( + ModelSpec(SceneGraphSourceModel(); name=:source) |> + AppliesTo(One(name=:source_object)), + ModelSpec(SceneGraphConsumerModel(); name=:consumer) |> + AppliesTo(One(name=:consumer_object)), + ), + ) + + configured = apply_scene_graph_edit( + scene, + SetSceneApplicationTargets(:consumer, OptionalOne(name=:consumer_object)), + ) + configured = apply_scene_graph_edit( + configured, + SetSceneInputBinding( + :consumer, + :signal, + One(name=:source_object, application=:source, var=:signal), + ), + ) + configured = apply_scene_graph_edit( + configured, + SetSceneCallBinding( + :consumer, + :source_call, + One(name=:source_object, application=:source), + ), + ) + configured = apply_scene_graph_edit( + configured, + SetSceneApplicationTimeStep(:consumer, ClockSpec(2.0)), + ) + configured = apply_scene_graph_edit( + configured, + SetSceneUpdateOrdering(:consumer, (Updates(:result; after=:source),)), + ) + + consumer = PlantSimEngine._scene_edit_spec(configured, :consumer) + @test applies_to(consumer) isa OptionalOne + @test PlantSimEngine.criteria(value_inputs(consumer).signal).application == :source + @test PlantSimEngine.criteria(model_calls(consumer).source_call).application == :source + @test consumer.timestep == ClockSpec(2.0) + consumer_view = only( + application for application in scene_graph_view(configured).applications + if application["applicationId"] == "consumer" + ) + @test haskey(consumer_view["inputBindings"], "signal") + @test haskey(consumer_view["callBindings"], "source_call") + + renamed = apply_scene_graph_edit(configured, RenameSceneApplication(:source, :driver_source)) + renamed_consumer = PlantSimEngine._scene_edit_spec(renamed, :consumer) + @test PlantSimEngine.criteria(value_inputs(renamed_consumer).signal).application == :driver_source + @test PlantSimEngine.criteria(model_calls(renamed_consumer).source_call).application == :driver_source + @test PlantSimEngine._update_after(only(updates(renamed_consumer))) == (:driver_source,) + @test_throws "already exists" apply_scene_graph_edit( + renamed, + RenameSceneApplication(:driver_source, :consumer), + ) + + without_input = apply_scene_graph_edit( + renamed, + RemoveSceneInputBinding(:consumer, :signal), + ) + @test isempty(value_inputs(PlantSimEngine._scene_edit_spec(without_input, :consumer))) + without_call = apply_scene_graph_edit( + without_input, + RemoveSceneCallBinding(:consumer, :source_call), + ) + @test isempty(model_calls(PlantSimEngine._scene_edit_spec(without_call, :consumer))) + + with_objects = apply_scene_graph_edit( + without_call, + AddSceneObject(Object(:child; name=:child, scale=:Leaf, parent=:consumer_object)), + ) + with_objects = apply_scene_graph_edit( + with_objects, + ReparentSceneObject(:child, :source_object), + ) + @test PlantSimEngine._scene_object(with_objects, ObjectId(:child)).parent == ObjectId(:source_object) + with_objects = apply_scene_graph_edit( + with_objects, + SetSceneObjectStatuses([:source_object, :child], :shared_value, 5), + ) + @test all( + PlantSimEngine._scene_object(with_objects, ObjectId(id)).status.shared_value == 5 + for id in (:source_object, :child) + ) + without_child = apply_scene_graph_edit(with_objects, RemoveSceneObject(:child)) + @test !(ObjectId(:child) in object_ids(without_child)) + @test ObjectId(:child) in object_ids(with_objects) +end + function scene_graph_override_fixture() template = ObjectTemplate( ( @@ -375,6 +520,10 @@ end ) @test restored_b["modelParameters"]["coefficient"]["value"] == 1.0 @test scene_graph_view(scene).metadata["applicationCount"] == 2 + @test_throws Exception apply_scene_graph_edit( + scene, + SetSceneInstanceOverride(:plant_b, :source, SceneGraphConsumerModel()), + ) end @testset "Scene graph object override edit" begin @@ -400,6 +549,10 @@ end if application["applicationId"] == "plant_a__source" ) @test restored_application["modelStorage"] == "shared_application" + @test_throws Exception apply_scene_graph_edit( + scene, + SetSceneObjectOverride(:plant_a, :leaf_a, :source, SceneGraphConsumerModel()), + ) end From 9c3ffad2898dc39f03de17144fc81cda3b3b07e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Sat, 20 Jun 2026 10:51:10 +0200 Subject: [PATCH 25/33] Simplify examples in docs -> use defaults when possible --- README.md | 19 +++++----- docs/src/index.md | 26 +++++++------- docs/src/migration_scene_object.md | 7 ++-- docs/src/model_execution.md | 11 ++++-- docs/src/scene_object/quickstart.md | 19 +++++----- docs/src/step_by_step/advanced_coupling.md | 2 +- .../step_by_step/detailed_first_example.md | 4 +-- docs/src/step_by_step/model_switching.md | 4 +-- .../step_by_step/quick_and_dirty_examples.md | 35 +++++++----------- .../src/step_by_step/simple_model_coupling.md | 36 ++++++++----------- 10 files changed, 72 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 1432f5a7f..6f6c8843c 100644 --- a/README.md +++ b/README.md @@ -72,21 +72,18 @@ scene = Scene( Object(:scene; scale=:Scene, kind=:scene); applications=( ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(Beer(0.6); name=:light_interception) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ), environment=meteo_day, ) -sim = run!(scene; steps=30, constants=Constants()) +sim = run!(scene; steps=30, outputs=:all) out = collect_outputs(sim; sink=DataFrame) first(out, 6) ``` @@ -97,7 +94,7 @@ declared inputs and outputs: `ToyLAIModel` receives `TT_cu` from ```julia select( - DataFrame(explain_bindings(refresh_bindings!(scene))), + DataFrame(explain_bindings(scene)), :application_id, :input, :source_application_ids, @@ -188,10 +185,10 @@ Useful inspection helpers include: ```julia explain_objects(scene) explain_scopes(scene) -explain_bindings(refresh_bindings!(scene)) -explain_calls(refresh_bindings!(scene)) +explain_bindings(scene) +explain_calls(scene) explain_environment_bindings(scene) -explain_schedule(refresh_bindings!(scene)) +explain_schedule(scene) explain_execution_plan(scene) ``` diff --git a/docs/src/index.md b/docs/src/index.md index 0455c0674..cd64941b4 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -113,7 +113,8 @@ This example runs three existing toy models on one scene object: 3. `Beer` consumes LAI and meteorology to compute absorbed PAR. The model kernels are unchanged; the scene application layer says where they -run and at which cadence. +run. Since no `TimeStep` is specified, these applications use the daily +cadence of `meteo_day`. ```@example readme using PlantSimEngine, PlantMeteo, Dates, DataFrames @@ -128,21 +129,18 @@ scene = Scene( Object(:scene; scale=:Scene, kind=:scene); applications=( ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(Beer(0.6); name=:light_interception) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ), environment=meteo_day, ) -sim = run!(scene; steps=30, constants=Constants(), outputs=:all) +sim = run!(scene; steps=30, outputs=:all) out = collect_outputs(sim; sink=DataFrame) first(out, 6) ``` @@ -186,12 +184,12 @@ Use `Inputs(...)` when a model needs values from selected objects. Here the scene-scale LAI model reads live references to all plant surfaces in the scene: ```@example readme -plant_scene = PlantSimEngine.Scene( - PlantSimEngine.Object(:scene; scale=:Scene, kind=:scene), - PlantSimEngine.Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene, - status=Status(surface=12.0)), - PlantSimEngine.Object(:plant_2; scale=:Plant, kind=:plant, parent=:scene, - status=Status(surface=8.0)); +plant_scene = Scene( + Object(:scene; scale=:Scene, kind=:scene), + Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene, + status=Status(surface=12.0)), + Object(:plant_2; scale=:Plant, kind=:plant, parent=:scene, + status=Status(surface=8.0)); applications=( ModelSpec(ToyLAIfromLeafAreaModel(100.0); name=:scene_lai) |> AppliesTo(One(scale=:Scene)) |> diff --git a/docs/src/migration_scene_object.md b/docs/src/migration_scene_object.md index 2c2573db0..b9ece9974 100644 --- a/docs/src/migration_scene_object.md +++ b/docs/src/migration_scene_object.md @@ -108,10 +108,9 @@ scene = Scene( mtg; applications=applications, environment=meteo, - id=node -> Symbol(symbol(node), "_", node_id(node)), - kind=node -> node_kind(node), - species=node -> node_species(node), - geometry=node -> node_geometry(node), + kind=node_kind, + species=node_species, + geometry=node_geometry, ) ``` diff --git a/docs/src/model_execution.md b/docs/src/model_execution.md index be1950cb3..c62dd5824 100644 --- a/docs/src/model_execution.md +++ b/docs/src/model_execution.md @@ -239,6 +239,10 @@ Supported policies are: `Integrate(...)` and `Aggregate(...)` accept reducer objects or callables that take either `(values)` or `(values, durations_seconds)`. +For duration-aware reducers, each producer value is held until the next +producer execution and weighted by the portion of that interval overlapping +the consumer window. This includes the last value published before the window +when it remains active inside the window. Temporal windows are duration-based rolling windows. Calendar-aligned civil days and "previous complete period" selection are not part of the public API; @@ -274,7 +278,7 @@ model-to-model temporal values. Run a scene with: ```julia -sim = run!(scene; steps=30, constants=Constants()) +sim = run!(scene; steps=30) ``` The returned `SceneSimulation` contains the mutated scene, compiled bindings, @@ -362,7 +366,10 @@ Do not mutate `Object` topology, labels, or geometry fields directly. Direct field mutation bypasses registry indexes and cache invalidation and is unsupported. Use the lifecycle functions above. They validate prerequisites before mutating; in particular, `reparent_object!` rejects self-parenting and -descendant cycles without changing existing links. +descendant cycles without changing existing links. `ObjectInstance` roots are +immutable lifecycle anchors: removing or reparenting a root, or an ancestor +whose subtree contains one, is rejected atomically. Ordinary descendants may +still be added, removed, or reparented. Inside a lifecycle-capable model kernel, use `runtime_scene(extra)` to obtain the live scene. Objects created during a kernel call do not recursively execute diff --git a/docs/src/scene_object/quickstart.md b/docs/src/scene_object/quickstart.md index d7121d0be..f786ae162 100644 --- a/docs/src/scene_object/quickstart.md +++ b/docs/src/scene_object/quickstart.md @@ -37,8 +37,8 @@ to the ordinary Scene/Object representation: scene = Scene(ModelA(), ModelB(); status=(initial_value=1.0,)) ``` -Use the explicit form below when applications need names, selectors, cadence, -or other scenario policies. +Use the explicit form below when applications need names, selectors, or other +scenario policies. The first scene has one object, `:scene`, and three model applications: @@ -47,7 +47,8 @@ The first scene has one object, `:scene`, and three model applications: - `Beer` consumes LAI and meteorology to compute absorbed PAR. The model implementations are ordinary PlantSimEngine kernels. The scene -application layer decides where they run and at which cadence. +application layer decides where they run. With no explicit `TimeStep`, these +applications use the environment cadence. ```@example scene_object_quickstart meteo_day = read_weather( @@ -59,21 +60,18 @@ scene = Scene( Object(:scene; scale=:Scene, kind=:scene); applications=( ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(Beer(0.6); name=:light_interception) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ), environment=meteo_day, ) -sim = run!(scene; steps=30, constants=Constants()) +sim = run!(scene; steps=30, outputs=:all) out = collect_outputs(sim; sink=DataFrame) first(out, 6) ``` @@ -143,7 +141,6 @@ request = OutputRequest( requested_sim = run!( scene; steps=30, - constants=Constants(), outputs=request, ) diff --git a/docs/src/step_by_step/advanced_coupling.md b/docs/src/step_by_step/advanced_coupling.md index 1202b80e8..ad06b90b5 100644 --- a/docs/src/step_by_step/advanced_coupling.md +++ b/docs/src/step_by_step/advanced_coupling.md @@ -108,7 +108,7 @@ select( Run one timestep: ```@example scene_advanced_coupling -complex_sim = run!(complex_scene; steps=1, constants=Constants()) +complex_sim = run!(complex_scene; steps=1) complex_status = only(scene_objects(complex_scene; scale=:Scene)).status ( var3=complex_status.var3, diff --git a/docs/src/step_by_step/detailed_first_example.md b/docs/src/step_by_step/detailed_first_example.md index 7c4819a76..66a557304 100644 --- a/docs/src/step_by_step/detailed_first_example.md +++ b/docs/src/step_by_step/detailed_first_example.md @@ -128,7 +128,7 @@ select( Run the scene with [`run!`](@ref): ```@example detailed_scene -sim = run!(scene; steps=3, constants=Constants()) +sim = run!(scene; steps=3) ``` The object status stores the latest value: @@ -196,7 +196,7 @@ model sees the value written by the LAI model without copying it. Run the coupled scene: ```@example detailed_scene -coupled_sim = run!(coupled_scene; steps=5, constants=Constants()) +coupled_sim = run!(coupled_scene; steps=5, outputs=:all) first(collect_outputs(coupled_sim), 8) ``` diff --git a/docs/src/step_by_step/model_switching.md b/docs/src/step_by_step/model_switching.md index 3ff63fb8f..ef353f406 100644 --- a/docs/src/step_by_step/model_switching.md +++ b/docs/src/step_by_step/model_switching.md @@ -50,7 +50,7 @@ function plant_scene_with_growth(growth_model; growth_name=:growth) end rue_scene = plant_scene_with_growth(ToyRUEGrowthModel(0.2)) -rue_sim = run!(rue_scene; steps=10, constants=Constants()) +rue_sim = run!(rue_scene; steps=10) rue_status = only(scene_objects(rue_scene; scale=:Scene)).status (growth_model=:ToyRUEGrowthModel, biomass=rue_status.biomass) ``` @@ -77,7 +77,7 @@ respiration. The rest of the scene does not need to change: ```@example scene_model_switching assim_scene = plant_scene_with_growth(ToyAssimGrowthModel()) -assim_sim = run!(assim_scene; steps=10, constants=Constants()) +assim_sim = run!(assim_scene; steps=10) assim_status = only(scene_objects(assim_scene; scale=:Scene)).status ( growth_model=:ToyAssimGrowthModel, diff --git a/docs/src/step_by_step/quick_and_dirty_examples.md b/docs/src/step_by_step/quick_and_dirty_examples.md index 00ae1965c..8bce819b9 100644 --- a/docs/src/step_by_step/quick_and_dirty_examples.md +++ b/docs/src/step_by_step/quick_and_dirty_examples.md @@ -35,13 +35,12 @@ scene = Scene( ); applications=( ModelSpec(Beer(0.5); name=:light_interception) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ), environment=meteo_day, ) -sim = run!(scene; steps=3, constants=Constants()) +sim = run!(scene; steps=3, outputs=:all) first(collect_outputs(sim), 3) ``` @@ -53,22 +52,19 @@ value bindings from model inputs and outputs. ```@example quick_scene_examples lai_scene = Scene( - Object(:scene; scale=:Scene, kind=:scene, status=Status(TT_cu=0.0)); + Object(:scene; scale=:Scene, kind=:scene); applications=( ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(Beer(0.5); name=:light_interception) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ), environment=meteo_day, ) -lai_sim = run!(lai_scene; steps=5, constants=Constants()) +lai_sim = run!(lai_scene; steps=5, outputs=:all) first(collect_outputs(lai_sim), 8) ``` @@ -92,25 +88,21 @@ same object. ```@example quick_scene_examples growth_scene = Scene( - Object(:scene; scale=:Scene, kind=:scene, status=Status(TT_cu=0.0)); + Object(:scene; scale=:Scene, kind=:scene); applications=( ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(Beer(0.5); name=:light_interception) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(ToyRUEGrowthModel(0.2); name=:growth) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ), environment=meteo_day, ) -growth_sim = run!(growth_scene; steps=5, constants=Constants()) +growth_sim = run!(growth_scene; steps=5) growth_status = only(scene_objects(growth_scene; scale=:Scene)).status (LAI=growth_status.LAI, aPPFD=growth_status.aPPFD, biomass=growth_status.biomass) ``` @@ -132,7 +124,6 @@ request = OutputRequest( requested_sim = run!( growth_scene; steps=5, - constants=Constants(), outputs=request, ) diff --git a/docs/src/step_by_step/simple_model_coupling.md b/docs/src/step_by_step/simple_model_coupling.md index 53c1fc5e0..9683497a7 100644 --- a/docs/src/step_by_step/simple_model_coupling.md +++ b/docs/src/step_by_step/simple_model_coupling.md @@ -24,22 +24,21 @@ page. ## One object and one model -A scene contains objects. A model application says where a model runs and at -which timestep. Here a light interception model runs on the scene object and -reads `LAI` from that object's status: +A scene contains objects. A model application says where a model runs. Here a +light interception model runs on the scene object, uses the environment's +daily cadence, and reads `LAI` from that object's status: ```@example scene_coupling light_scene = Scene( Object(:scene; scale=:Scene, kind=:scene, status=Status(LAI=2.0)); applications=( ModelSpec(Beer(0.5); name=:light_interception) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ), environment=meteo_day, ) -light_sim = run!(light_scene; steps=3, constants=Constants()) +light_sim = run!(light_scene; steps=3, outputs=:all) first(collect_outputs(light_sim; sink=DataFrame), 3) ``` @@ -54,16 +53,13 @@ coupled_scene = Scene( Object(:scene; scale=:Scene, kind=:scene); applications=( ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(Beer(0.5); name=:light_interception) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ), environment=meteo_day, ) @@ -86,7 +82,7 @@ the timestep loop does not copy values between models. Run the coupled scene: ```@example scene_coupling -coupled_sim = run!(coupled_scene; steps=5, constants=Constants()) +coupled_sim = run!(coupled_scene; steps=5) coupled_status = only(scene_objects(coupled_scene; scale=:Scene)).status (TT_cu=coupled_status.TT_cu, LAI=coupled_status.LAI, aPPFD=coupled_status.aPPFD) ``` @@ -102,25 +98,21 @@ growth_scene = Scene( Object(:scene; scale=:Scene, kind=:scene); applications=( ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(Beer(0.5); name=:light_interception) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ModelSpec(ToyRUEGrowthModel(0.2); name=:growth) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), + AppliesTo(One(scale=:Scene)), ), environment=meteo_day, ) -growth_sim = run!(growth_scene; steps=5, constants=Constants()) +growth_sim = run!(growth_scene; steps=5) growth_status = only(scene_objects(growth_scene; scale=:Scene)).status (LAI=growth_status.LAI, aPPFD=growth_status.aPPFD, biomass=growth_status.biomass) ``` From b8fde7d5b28a0c59d159bc5ff4324985cd177ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Sat, 20 Jun 2026 10:58:36 +0200 Subject: [PATCH 26/33] Harden a bit more the API - Overlap-weighted temporal windows, including pre-window held samples and output resampling. - timestep_hint/meteo_hint delegation for object overrides. - Atomic rejection of instance-root or ancestor removal/reparenting. --- src/scene_object/registry_topology.jl | 23 ++++++++++++ src/scene_object/runtime_outputs.jl | 41 ++++++++++++++++---- test/test-scene-api-stabilization.jl | 39 +++++++++++++++++++ test/test-scene-meteo-sampling.jl | 34 +++++++++++++++++ test/test-scene-temporal-reducers.jl | 54 +++++++++++++++++++++++++++ test/test-scene-time-validation.jl | 34 +++++++++++++++++ 6 files changed, 218 insertions(+), 7 deletions(-) diff --git a/src/scene_object/registry_topology.jl b/src/scene_object/registry_topology.jl index e84f71dc9..656b78e81 100644 --- a/src/scene_object/registry_topology.jl +++ b/src/scene_object/registry_topology.jl @@ -161,6 +161,8 @@ outputs_(model::ObjectModelOverrides) = outputs_(model.base) dep(model::ObjectModelOverrides) = dep(model.base) timespec(model::ObjectModelOverrides) = timespec(model.base) output_policy(model::ObjectModelOverrides) = output_policy(model.base) +timestep_hint(model::ObjectModelOverrides) = timestep_hint(model.base) +meteo_hint(model::ObjectModelOverrides) = meteo_hint(model.base) meteo_inputs_(model::ObjectModelOverrides) = meteo_inputs_(model.base) meteo_outputs_(model::ObjectModelOverrides) = meteo_outputs_(model.base) @@ -747,8 +749,28 @@ function _remove_child_link!(scene::Scene, parent_id, child_id::ObjectId) return nothing end +function _instance_roots_in_subtree(scene::Scene, root_id::ObjectId) + subtree_ids = Set(_descendant_ids(scene, root_id)) + roots = ObjectId[ + _instance_root_id(instance) for instance in scene.instances + if _instance_root_id(instance) in subtree_ids + ] + return _sort_object_ids!(roots) +end + +function _validate_mutable_object_subtree!(scene::Scene, object::Object, operation::Symbol) + instance_roots = _instance_roots_in_subtree(scene, object.id) + isempty(instance_roots) && return nothing + error( + "Cannot $(operation) object `$(object.id.value)` because its subtree contains ", + "immutable ObjectInstance root(s) `$([id.value for id in instance_roots])`. ", + "Mutate ordinary instance descendants instead.", + ) +end + function remove_object!(scene::Scene, id; recursive::Bool=true) object = _scene_object(scene, id) + _validate_mutable_object_subtree!(scene, object, :remove) if !recursive && !isempty(object.children) error("Cannot remove object `$(object.id.value)` with children unless `recursive=true`.") end @@ -764,6 +786,7 @@ end function reparent_object!(scene::Scene, id, new_parent) object = _scene_object(scene, id) + _validate_mutable_object_subtree!(scene, object, :reparent) new_parent_id = isnothing(new_parent) ? nothing : ObjectId(new_parent) if !isnothing(new_parent_id) haskey(scene.registry.objects, new_parent_id) || error("No scene object with id `$(new_parent_id.value)`.") diff --git a/src/scene_object/runtime_outputs.jl b/src/scene_object/runtime_outputs.jl index 1595b7630..08479bcf1 100644 --- a/src/scene_object/runtime_outputs.jl +++ b/src/scene_object/runtime_outputs.jl @@ -275,8 +275,27 @@ function _scene_interpolated_sample(samples, time::Real, policy::Interpolate) return first(samples)[2] end -function _scene_window_samples(samples, t_start::Real, t_end::Real) - return [value for (sample_t, value) in samples if float(t_start) <= sample_t <= float(t_end)] +function _scene_window_segments(samples, t_start::Real, t_end::Real, base_step_seconds::Real) + value_type = fieldtype(eltype(samples), 2) + isempty(samples) && return (value_type[], Float64[]) + window_start = float(t_start) + window_stop = float(t_end) + 1.0 + first_index = findlast(sample -> sample[1] < window_start, samples) + first_index = isnothing(first_index) ? firstindex(samples) : first_index + + values = value_type[] + durations = Float64[] + for index in first_index:lastindex(samples) + sample_t, value = samples[index] + sample_t >= window_stop && break + next_t = index == lastindex(samples) ? window_stop : samples[index + 1][1] + segment_start = max(float(sample_t), window_start) + segment_stop = min(float(next_t), window_stop) + segment_stop > segment_start || continue + push!(values, value) + push!(durations, (segment_stop - segment_start) * float(base_step_seconds)) + end + return values, durations end function _scene_window_reduce(values, durations, policy) @@ -335,8 +354,12 @@ function _scene_temporal_source_value( elseif policy isa PreviousTimeStep return _scene_latest_sample(samples, float(time) - 1.0) elseif policy isa Union{Integrate,Aggregate} - values = _scene_window_samples(samples, t_start, time) - durations = fill(timeline.base_step_seconds, length(values)) + values, durations = _scene_window_segments( + samples, + t_start, + time, + timeline.base_step_seconds, + ) return _scene_window_reduce(values, durations, policy) elseif policy isa Interpolate return _scene_interpolated_sample(samples, time, policy) @@ -989,7 +1012,7 @@ function compile_scene_output_retention( for application_id in binding.source_application_ids source = _compiled_application_by_id(compiled, application_id) required = if binding.policy isa Union{Integrate,Aggregate} - float(window_steps) + float(window_steps) + max(0.0, float(source.clock.dt) - 1.0) elseif binding.policy isa Union{Interpolate,PreviousTimeStep} max(2.0, float(source.clock.dt) + 1.0) else @@ -1488,9 +1511,13 @@ function _scene_requested_value(samples, time, t_start, policy, timeline) value = _scene_interpolated_sample(samples, time, policy) return isnothing(value) ? missing : value elseif policy isa Union{Integrate,Aggregate} - values = _scene_window_samples(samples, t_start, time) + values, durations = _scene_window_segments( + samples, + t_start, + time, + timeline.base_step_seconds, + ) isempty(values) && return missing - durations = fill(timeline.base_step_seconds, length(values)) return _scene_window_reduce(values, durations, policy) end error("Unsupported scene output request policy `$(typeof(policy))`.") diff --git a/test/test-scene-api-stabilization.jl b/test/test-scene-api-stabilization.jl index b69d815dd..4959bc4b3 100644 --- a/test/test-scene-api-stabilization.jl +++ b/test/test-scene-api-stabilization.jl @@ -317,6 +317,45 @@ end @test isnothing(only(scene_objects(scene; scale=:Plant)).parent) end +@testset "instance roots are immutable lifecycle anchors" begin + template = ObjectTemplate(( + ModelSpec(StabilizationSourceModel(); name=:source) |> + AppliesTo(Many(scale=:Leaf)), + )) + instance = ObjectInstance( + :plant_instance, + template; + root=Object(:plant; scale=:Plant, parent=:branch), + objects=(Object(:leaf; scale=:Leaf, parent=:plant),), + ) + scene = Scene( + Object(:world; scale=:Scene), + Object(:outside; scale=:Branch), + Object(:branch; scale=:Branch, parent=:world), + instance, + ) + + @test_throws "immutable ObjectInstance root" remove_object!(scene, :plant) + @test_throws "immutable ObjectInstance root" remove_object!(scene, :branch) + @test object_ids(scene) == ObjectId.([:branch, :leaf, :outside, :plant, :world]) + @test only(scene_objects(scene; name=:plant_instance)).id == ObjectId(:plant) + + @test_throws "immutable ObjectInstance root" reparent_object!(scene, :plant, :outside) + @test_throws "immutable ObjectInstance root" reparent_object!(scene, :branch, :outside) + @test only(resolve_object_ids(scene, One(Relation(:parent)); context=:plant)) == + ObjectId(:branch) + @test only(resolve_object_ids(scene, One(Relation(:parent)); context=:branch)) == + ObjectId(:world) + + reparent_object!(scene, :leaf, :outside) + @test only(resolve_object_ids(scene, One(Relation(:parent)); context=:leaf)) == + ObjectId(:outside) + reparent_object!(scene, :leaf, :plant) + removed = remove_object!(scene, :leaf) + @test removed.id == ObjectId(:leaf) + @test isempty(resolve_object_ids(scene, Many(scale=:Leaf))) +end + @testset "continuation refreshes lifecycle targets and preserves history" begin scene = Scene( Object(:leaf_1; scale=:Leaf); diff --git a/test/test-scene-meteo-sampling.jl b/test/test-scene-meteo-sampling.jl index 60a97466a..51a3abe36 100644 --- a/test/test-scene-meteo-sampling.jl +++ b/test/test-scene-meteo-sampling.jl @@ -76,6 +76,40 @@ end @test values(:mean_Rh) == [0.5, 0.65] @test values(:mean_radiation) == [100.0, 250.0] @test values(:radiation_energy) ≈ [0.36, 1.8] + + template = ObjectTemplate(( + ModelSpec(MeteoSamplingProbeModel(); name=:probe) |> + AppliesTo(Many(scale=:Leaf)) |> + TimeStep(Hour(2)) |> + Environment(provider=:global), + )) + instance = ObjectInstance( + :plant, + template; + root=Object(:plant_root; scale=:Plant), + objects=( + Object(:leaf_a; scale=:Leaf, parent=:plant_root), + Object(:leaf_b; scale=:Leaf, parent=:plant_root), + ), + object_overrides=( + Override( + object=:leaf_b, + application=:probe, + model=MeteoSamplingProbeModel(), + ), + ), + ) + override_scene = Scene(instance; environment=weather) + override_compiled = Advanced.refresh_bindings!(override_scene) + override_spec = override_compiled.applications_by_id[:plant__probe].spec + @test meteo_window(override_spec) isa PlantMeteo.RollingWindow + @test meteo_window(override_spec).dt == 2.0 + @test meteo_bindings(override_spec).Ri_SW_q.reducer isa RadiationEnergy + run!(override_scene; steps=4) + for object in scene_objects(override_scene; scale=:Leaf) + @test object.status.mean_T == 25.0 + @test object.status.radiation_energy ≈ 1.8 + end else @test_skip "PlantMeteo weather sampler API unavailable" end diff --git a/test/test-scene-temporal-reducers.jl b/test/test-scene-temporal-reducers.jl index 0a172bd32..e5002400e 100644 --- a/test/test-scene-temporal-reducers.jl +++ b/test/test-scene-temporal-reducers.jl @@ -80,3 +80,57 @@ PlantSimEngine.run!(::TemporalReducerTwoArgModel, models, status, meteo, constan ) @test_throws "must accept values or values and durations" Advanced.refresh_bindings!(invalid_scene) end + +@testset "coarse producer samples are weighted by held overlap" begin + observed_segments = Ref{Any}(nothing) + integral = function (values, durations) + if length(values) == 3 + observed_segments[] = (values=copy(values), durations=copy(durations)) + end + return sum(values .* durations) + end + weighted_mean = (values, durations) -> sum(values .* durations) / sum(durations) + + scene = Scene( + Object(:leaf; scale=:Leaf); + applications=( + ModelSpec(TemporalReducerSourceModel(); name=:source) |> + AppliesTo(One(scale=:Leaf)) |> + TimeStep(Hour(2)), + ModelSpec(TemporalReducerTwoArgModel(); name=:integral) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs(:reduced => One( + scale=:Leaf, + application=:source, + var=:signal, + policy=Aggregate(integral), + window=Hour(4), + )) |> + TimeStep(Hour(4)), + ModelSpec(TemporalReducerOneArgModel(); name=:weighted_mean) |> + AppliesTo(One(scale=:Leaf)) |> + Inputs(:reduced => One( + scale=:Leaf, + application=:source, + var=:signal, + policy=Aggregate(weighted_mean), + window=Hour(4), + )) |> + TimeStep(Hour(4)), + ), + environment=(duration=Hour(1),), + ) + + retention = only( + row for row in explain_output_retention(scene) + if row.application_id == :source && row.variable == :signal + ) + @test retention.retention_steps == 5.0 + + run!(scene; steps=5) + @test observed_segments[].values == [1.0, 2.0, 3.0] + @test observed_segments[].durations == [3600.0, 7200.0, 3600.0] + status = only(scene_objects(scene)).status + @test status.two_arg == 28_800.0 + @test status.one_arg == 2.0 +end diff --git a/test/test-scene-time-validation.jl b/test/test-scene-time-validation.jl index 8b98d3734..03ae356f2 100644 --- a/test/test-scene-time-validation.jl +++ b/test/test-scene-time-validation.jl @@ -10,6 +10,15 @@ function PlantSimEngine.run!(::TimeValidationCounterModel, models, status, meteo status.count += 1 end +PlantSimEngine.@process "time_validation_override_hint" verbose = false +struct TimeValidationOverrideHintModel <: AbstractTime_Validation_Override_HintModel end +PlantSimEngine.inputs_(::TimeValidationOverrideHintModel) = NamedTuple() +PlantSimEngine.outputs_(::TimeValidationOverrideHintModel) = (count=0,) +PlantSimEngine.timestep_hint(::Type{<:TimeValidationOverrideHintModel}) = Day(1) +function PlantSimEngine.run!(::TimeValidationOverrideHintModel, models, status, meteo, constants, extra) + status.count += 1 +end + function time_validation_scene(environment; cadence=nothing) spec = ModelSpec(TimeValidationCounterModel(); name=:counter) |> AppliesTo(One(scale=:Scene)) @@ -59,3 +68,28 @@ end @test only(scene_objects(scene)).status.count == 1 @test length(scene_outputs(simulation)[(:counter, ObjectId(:scene), :count)]) == 1 end + +@testset "object overrides preserve timestep hints" begin + template = ObjectTemplate(( + ModelSpec(TimeValidationOverrideHintModel(); name=:counter) |> + AppliesTo(Many(scale=:Leaf)), + )) + instance = ObjectInstance( + :plant, + template; + root=Object(:plant_root; scale=:Plant), + objects=( + Object(:leaf_a; scale=:Leaf, parent=:plant_root), + Object(:leaf_b; scale=:Leaf, parent=:plant_root), + ), + object_overrides=( + Override( + object=:leaf_b, + application=:counter, + model=TimeValidationOverrideHintModel(), + ), + ), + ) + scene = Scene(instance; environment=(duration=Hour(1),)) + @test_throws "outside `timestep_hint.required=1 day`" Advanced.refresh_bindings!(scene) +end From 26250d019d89da45a7654667b5a7463f938c8c26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Sun, 21 Jun 2026 09:13:55 +0200 Subject: [PATCH 27/33] Fix benchmarks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dynamic organ creation repeatedly rebuilt scene bindings, selector matches, MTG IDs, and attribute/output structures—effectively rescanning the whole plant per organ. XPalm also had an ambiguous fruit-count producer binding. Incremental updates, caching, and explicit binding fixed it. --- src/scene_object/compilation.jl | 480 ++++++++++++++++++++++- src/scene_object/environment_bindings.jl | 44 ++- src/scene_object/registry_topology.jl | 67 +++- src/scene_object/runtime_outputs.jl | 284 ++++++++++++-- src/scene_object/selectors.jl | 252 +++++++++++- src/visualization/scene_graph_view.jl | 3 + test/test-unified-scene-object-api.jl | 39 +- 7 files changed, 1106 insertions(+), 63 deletions(-) diff --git a/src/scene_object/compilation.jl b/src/scene_object/compilation.jl index 654edab18..9983617b3 100644 --- a/src/scene_object/compilation.jl +++ b/src/scene_object/compilation.jl @@ -66,19 +66,44 @@ struct CompiledEnvironmentBindings{SC,B,I,S,C} environment_revision::Int end -struct CompiledScene{SC,AP,AI,IB,CB,IBI,CBI,MBI,AO} +struct CompiledScene{SC,AP,AI,ABO,IB,CB,IBI,CBI,DBI,MBI,AO,TL} scene::SC applications::AP applications_by_id::AI + applications_by_object::ABO input_bindings::IB call_bindings::CB input_bindings_by_target::IBI call_bindings_by_target::CBI + dynamic_input_binding_indices::DBI model_bundles_by_target::MBI application_order::AO + timeline::TL revision::Int end +function _dynamic_binding_scale_keys(scene::Scene, binding) + binding.origin == :inferred_same_object && return Symbol[] + selector_criteria = criteria(binding.selector) + explicit_scope = _criteria_scope(selector_criteria) + default_scope = _default_dependency_scope(scene, binding.consumer_id) + scope = isnothing(explicit_scope) ? default_scope : explicit_scope + scope isa Self && return Symbol[] + scale = _criteria_value(selector_criteria, :scale, Scale) + isnothing(scale) && return Union{Nothing,Symbol}[nothing] + return Symbol[Symbol(value) for value in (scale isa Tuple ? scale : (scale,))] +end + +function _index_dynamic_input_bindings(scene::Scene, bindings) + index = Dict{Union{Nothing,Symbol},Vector{Int}}() + for (binding_index, binding) in pairs(bindings) + for scale in _dynamic_binding_scale_keys(scene, binding) + push!(get!(index, scale, Int[]), binding_index) + end + end + return index +end + function _index_scene_bindings(bindings, application_field::Symbol, object_field::Symbol) grouped = Dict{Tuple{Symbol,ObjectId},Vector{Any}}() for binding in bindings @@ -132,6 +157,7 @@ function _compile_scene(scene::Scene, raw_specs; validate_required_inputs::Bool= applications, _manual_call_application_ids(call_bindings), ) + _share_many_input_bindings!(scene, input_bindings) _prepare_scene_bound_input_statuses!(scene, applications, input_bindings) _wire_scene_input_carriers!(scene, input_bindings) validate_required_inputs && @@ -149,12 +175,452 @@ function _compile_scene(scene::Scene, raw_specs; validate_required_inputs::Bool= scene, applications, applications_by_id, + _applications_by_object(applications), + input_bindings, + call_bindings, + input_bindings_by_target, + call_bindings_by_target, + _index_dynamic_input_bindings(scene, input_bindings), + model_bundles_by_target, + application_order, + timeline, + scene.revision, + ) +end + +function _new_application_targets(scene::Scene, applications, added_ids) + targets = Dict{Symbol,Vector{ObjectId}}() + for application in applications + matched = ObjectId[ + object_id for object_id in added_ids + if _selector_matches_object_id(scene, application.applies_to, object_id) + ] + isempty(matched) && continue + new_count = length(application.target_ids) + length(matched) + if (application.applies_to isa One && new_count != 1) || + (application.applies_to isa OptionalOne && new_count > 1) + return nothing + end + targets[application.id] = matched + end + return targets +end + +function _compile_added_consumer_bindings!( + bindings, + scene, + application, + consumer_id, + manual_application_ids, + applications_by_object, + applications_by_id, +) + declared_inputs = value_inputs(application.spec) + declared_inputs isa NamedTuple || (declared_inputs = NamedTuple()) + for (input_name, selector) in pairs(declared_inputs) + input_sym = Symbol(input_name) + origin = get(input_origins(application.spec), input_sym, :model_spec) + _validate_declared_scene_input_name!(application, input_sym) + _push_scene_input_binding!( + bindings, + scene, + application, + consumer_id, + input_sym, + selector, + origin, + applications_by_object, + applications_by_id, + ) + end + application.id in manual_application_ids && return bindings + _append_inferred_scene_input_bindings!( + bindings, + scene, + application, + consumer_id, + declared_inputs, + applications_by_object, + applications_by_id, + ) + return bindings +end + +function _many_binding_scope_anchor(scene::Scene, binding::CompiledSceneInputBinding) + selector_criteria = criteria(binding.selector) + !isnothing(_criteria_get(selector_criteria, :relation, nothing)) && + return (:consumer, binding.consumer_id) + explicit_scope = _criteria_scope(selector_criteria) + scope = isnothing(explicit_scope) ? + _default_dependency_scope(scene, binding.consumer_id) : explicit_scope + if isnothing(scope) || scope isa SceneScope + return (:scene,) + elseif scope isa SelfPlant + return (:plant, _ancestor_id(scene, binding.consumer_id; scale=:Plant)) + elseif scope isa Scope + return (:scope, scope.name) + elseif scope isa Ancestor + return ( + :ancestor, + _ancestor_id( + scene, + binding.consumer_id; + scale=scope.scale, + include_self=false, + ), + ) + end + return (:consumer, binding.consumer_id) +end + +function _many_binding_share_key(scene::Scene, binding::CompiledSceneInputBinding) + binding.multiplicity == :many || return nothing + isnothing(binding.carrier) && return nothing + return ( + binding.selector, + _many_binding_scope_anchor(scene, binding), + binding.source_var, + binding.process, + binding.application, + binding.carrier_hint, + typeof(binding.carrier), + ) +end + +function _binding_with_shared_many_sources( + binding::CompiledSceneInputBinding, + canonical::CompiledSceneInputBinding, +) + return CompiledSceneInputBinding( + binding.application_id, + binding.consumer_id, + binding.input, + binding.selector, + binding.origin, + canonical.source_ids, + canonical.source_application_ids, + binding.source_var, + binding.process, + binding.application, + binding.multiplicity, + binding.policy, + binding.window, + binding.carrier_hint, + canonical.carrier, + ) +end + +function _share_many_input_binding!(cache, scene::Scene, binding) + key = _many_binding_share_key(scene, binding) + isnothing(key) && return binding + canonical = get(cache, key, nothing) + if isnothing(canonical) + cache[key] = binding + return binding + end + if canonical.source_ids != binding.source_ids || + canonical.source_application_ids != binding.source_application_ids + cache[(key, binding.consumer_id)] = binding + return binding + end + return _binding_with_shared_many_sources(binding, canonical) +end + +function _share_many_input_bindings!(scene::Scene, bindings; cache=Dict{Any,Any}()) + for index in eachindex(bindings) + bindings[index] = _share_many_input_binding!(cache, scene, bindings[index]) + end + return cache +end + +function _many_input_binding_cache(scene::Scene, bindings) + cache = Dict{Any,Any}() + for binding in bindings + key = _many_binding_share_key(scene, binding) + isnothing(key) || haskey(cache, key) || (cache[key] = binding) + end + return cache +end + +function _append_added_many_sources!( + scene::Scene, + binding::CompiledSceneInputBinding, + added_ids, + applications_by_object, +) + binding.multiplicity == :many || return false + default_scope = _default_dependency_scope(scene, binding.consumer_id) + new_source_ids = ObjectId[ + object_id for object_id in added_ids if + _selector_matches_object_id( + scene, + binding.selector, + object_id; + context=binding.consumer_id, + default_to_context=true, + default_scope=default_scope, + ) && !(object_id in binding.source_ids) + ] + isempty(new_source_ids) && return true + _sort_object_ids!(new_source_ids) + + # The growth path allocates monotonically increasing IDs. Appending preserves the + # selector's stable order and, critically, keeps the carrier already installed in + # the consumer status. Non-monotonic explicit IDs use the general rebuild path. + if !isempty(binding.source_ids) && + !_object_id_isless(last(binding.source_ids), first(new_source_ids)) + return false + end + + new_carrier = _input_carrier(scene, binding.selector, new_source_ids, binding.source_var) + isnothing(new_carrier) && return false + existing_refs = parent(binding.carrier) + new_refs = parent(new_carrier) + eltype(new_refs) <: eltype(existing_refs) || return false + append!(existing_refs, new_refs) + append!(binding.source_ids, new_source_ids) + + new_application_ids = _matching_input_source_applications( + applications_by_object, + new_source_ids, + binding.source_var, + binding.process, + binding.application; + allow_empty=binding.selector isa OptionalOne, + ) + for application_id in new_application_ids + application_id in binding.source_application_ids || + push!(binding.source_application_ids, application_id) + end + return true +end + +function _extend_compiled_scene(scene::Scene, compiled::CompiledScene, added_objects) + added_ids = ObjectId[id for id in added_objects if haskey(scene.registry.objects, id)] + isempty(added_ids) && return compile_scene(scene, scene.applications) + new_targets = _new_application_targets(scene, compiled.applications, added_ids) + isnothing(new_targets) && return compile_scene(scene, scene.applications) + + for application in compiled.applications + append!(application.target_ids, get(new_targets, application.id, ObjectId[])) + _sort_object_ids!(application.target_ids) + end + applications = compiled.applications + applications_by_id = Dict(application.id => application for application in applications) + applications_by_object = compiled.applications_by_object + for application in applications + for object_id in get(new_targets, application.id, ObjectId[]) + push!(get!(applications_by_object, object_id, Any[]), application) + end + end + + has_calls = any(applications) do application + calls = model_calls(application.spec) + calls isa NamedTuple && !isempty(keys(calls)) + end + timeline = _scene_timeline(scene) + added_applications = CompiledSceneApplication[] + for application in applications + target_ids = get(new_targets, application.id, ObjectId[]) + isempty(target_ids) && continue + push!( + added_applications, + CompiledSceneApplication( + application.id, + application.spec, + application.process, + application.name, + target_ids, + application.applies_to, + application.timestep, + application.clock, + application.model_overrides, + ), + ) + end + existing_calls_affected = false + if has_calls + existing_calls_affected = any(compiled.call_bindings) do binding + _selector_matches_any_object_id( + scene, + binding.selector, + added_ids; + context=binding.consumer_id, + default_to_context=true, + default_scope=_default_dependency_scope(scene, binding.consumer_id), + ) + end + end + new_call_bindings = has_calls ? + _compile_scene_call_bindings( + scene, + added_applications, + applications, + ; + by_object=applications_by_object, + ) : CompiledSceneCallBinding[] + call_bindings = if existing_calls_affected + _compile_scene_call_bindings( + scene, + applications; + by_object=applications_by_object, + ) + else + bindings = copy(compiled.call_bindings) + append!(bindings, new_call_bindings) + bindings + end + _validate_scene_call_cadences!(applications, call_bindings, timeline) + _validate_scene_writers!(added_applications, call_bindings) + _prepare_scene_output_statuses!(scene, added_applications) + + manual_application_ids = _manual_call_application_ids(call_bindings) + input_bindings = copy(compiled.input_bindings) + input_bindings_by_target = copy(compiled.input_bindings_by_target) + changed_bindings = CompiledSceneInputBinding[] + processed_many_sources = IdDict{Any,Nothing}() + candidate_binding_indices = Set(get(compiled.dynamic_input_binding_indices, nothing, Int[])) + for object_id in added_ids + object_scale = _scene_object(scene, object_id).scale + isnothing(object_scale) || union!( + candidate_binding_indices, + get(compiled.dynamic_input_binding_indices, object_scale, Int[]), + ) + end + for index in candidate_binding_indices + binding = input_bindings[index] + if binding.multiplicity == :many && haskey(processed_many_sources, binding.source_ids) + continue + end + default_scope = _default_dependency_scope(scene, binding.consumer_id) + _selector_matches_any_object_id( + scene, + binding.selector, + added_ids; + context=binding.consumer_id, + default_to_context=true, + default_scope=default_scope, + ) || continue + _append_added_many_sources!( + scene, + binding, + added_ids, + applications_by_object, + ) && begin + processed_many_sources[binding.source_ids] = nothing + continue + end + application = applications_by_id[binding.application_id] + replacement = CompiledSceneInputBinding[] + _push_scene_input_binding!( + replacement, + scene, + application, + binding.consumer_id, + binding.input, + binding.selector, + binding.origin, + applications_by_object, + applications_by_id, + ) + replacement_binding = only(replacement) + input_bindings[index] = replacement_binding + push!(changed_bindings, replacement_binding) + target = (binding.application_id, binding.consumer_id) + input_bindings_by_target[target] = Tuple( + existing === binding ? replacement_binding : existing + for existing in get(input_bindings_by_target, target, ()) + ) + end + previous_binding_count = length(input_bindings) + many_binding_cache = _many_input_binding_cache(scene, input_bindings) + for application in applications + for consumer_id in get(new_targets, application.id, ObjectId[]) + first_new_binding = length(input_bindings) + 1 + _compile_added_consumer_bindings!( + input_bindings, + scene, + application, + consumer_id, + manual_application_ids, + applications_by_object, + applications_by_id, + ) + last_new_binding = length(input_bindings) + if first_new_binding <= last_new_binding + for binding_index in first_new_binding:last_new_binding + input_bindings[binding_index] = _share_many_input_binding!( + many_binding_cache, + scene, + input_bindings[binding_index], + ) + end + new_bindings = input_bindings[first_new_binding:last_new_binding] + append!(changed_bindings, new_bindings) + input_bindings_by_target[(application.id, consumer_id)] = Tuple(new_bindings) + end + end + end + dynamic_input_binding_indices = Dict{Union{Nothing,Symbol},Vector{Int}}( + scale => copy(indices) + for (scale, indices) in compiled.dynamic_input_binding_indices + ) + for binding_index in (previous_binding_count + 1):length(input_bindings) + binding = input_bindings[binding_index] + for scale in _dynamic_binding_scale_keys(scene, binding) + push!(get!(dynamic_input_binding_indices, scale, Int[]), binding_index) + end + end + + _prepare_scene_bound_input_statuses!(scene, added_applications, changed_bindings) + _wire_scene_input_carriers!(scene, changed_bindings) + _validate_scene_required_inputs!(scene, added_applications, changed_bindings) + call_bindings_by_target = if existing_calls_affected + _index_scene_bindings(call_bindings, :application_id, :consumer_id) + elseif isempty(new_call_bindings) + compiled.call_bindings_by_target + else + indexed = copy(compiled.call_bindings_by_target) + merge!( + indexed, + _index_scene_bindings(new_call_bindings, :application_id, :consumer_id), + ) + indexed + end + application_order = compiled.application_order + model_bundles_by_target = if existing_calls_affected + _compile_scene_model_bundles( + applications, + applications_by_id, + call_bindings_by_target, + ) + else + bundles = copy(compiled.model_bundles_by_target) + for application in added_applications + for object_id in application.target_ids + bundles[(application.id, object_id)] = _compile_scene_model_bundle( + applications_by_id, + call_bindings_by_target, + application, + object_id, + ) + end + end + bundles + end + return CompiledScene( + scene, + applications, + applications_by_id, + applications_by_object, input_bindings, call_bindings, input_bindings_by_target, call_bindings_by_target, + dynamic_input_binding_indices, model_bundles_by_target, application_order, + timeline, scene.revision, ) end @@ -1178,8 +1644,14 @@ function _matching_callee_applications(applications, object_id::ObjectId, proc, return matches end -function _compile_scene_call_bindings(scene::Scene, applications) - by_object = _applications_by_object(applications) +function _compile_scene_call_bindings( + scene::Scene, + applications, + lookup_applications=applications, + ; + by_object=nothing, +) + isnothing(by_object) && (by_object = _applications_by_object(lookup_applications)) bindings = CompiledSceneCallBinding[] for application in applications calls = model_calls(application.spec) @@ -1394,7 +1866,7 @@ function _application_model_dispatch(application::CompiledSceneApplication) end function explain_schedule(compiled::CompiledScene) - timeline = _scene_timeline(compiled.scene) + timeline = compiled.timeline manual_application_ids = _manual_call_application_ids(compiled) execution_positions = Dict(application_id => index for (index, application_id) in pairs(compiled.application_order)) return [ diff --git a/src/scene_object/environment_bindings.jl b/src/scene_object/environment_bindings.jl index 7fe95a321..2fc82b4ab 100644 --- a/src/scene_object/environment_bindings.jl +++ b/src/scene_object/environment_bindings.jl @@ -332,6 +332,25 @@ function _refresh_environment_bindings_for_objects( ) _update_scene_environment_indices!(scene, compiled) dirty = Set(dirty_object_ids) + for application in compiled.applications + selected_ids = ObjectId[id for id in application.target_ids if id in dirty] + isempty(selected_ids) && continue + has_new_target = any( + object_id -> !haskey(cached.by_target, (application.id, object_id)), + selected_ids, + ) + has_new_target || continue + config = environment_config(application.spec) + backend = _environment_backend_from_config(scene, config) + backend isa GlobalConstant && continue + bindings = _compile_environment_bindings(scene, compiled) + _validate_scene_environment_inputs!(bindings, compiled.applications_by_id) + by_target = Dict( + (binding.application_id, binding.object_id) => binding + for binding in bindings + ) + return _compiled_environment_bindings(scene, bindings, by_target) + end replacements = Dict{Tuple{Symbol,ObjectId},CompiledEnvironmentBinding}() for application in compiled.applications selected_ids = ObjectId[id for id in application.target_ids if id in dirty] @@ -351,10 +370,34 @@ function _refresh_environment_bindings_for_objects( replacements[(binding.application_id, binding.object_id)] = binding end end + added_targets = Tuple{Symbol,ObjectId}[ + target for target in keys(replacements) + if !haskey(cached.by_target, target) + ] + if length(added_targets) == length(replacements) + append!(cached.bindings, values(replacements)) + merge!(cached.by_target, replacements) + _validate_scene_environment_inputs!(values(replacements), compiled.applications_by_id) + return _compiled_environment_bindings( + scene, + cached.bindings, + cached.by_target, + cached.samplers_by_application, + cached.sample_cache, + ) + end bindings = CompiledEnvironmentBinding[ get(replacements, (binding.application_id, binding.object_id), binding) for binding in cached.bindings ] + cached_targets = Set(keys(cached.by_target)) + append!( + bindings, + ( + binding for (target, binding) in replacements + if !(target in cached_targets) + ), + ) _validate_scene_environment_inputs!(bindings, compiled.applications_by_id) by_target = Dict( (binding.application_id, binding.object_id) => binding for binding in bindings @@ -395,4 +438,3 @@ end function explain_environment_bindings(scene::Scene) return explain_environment_bindings(refresh_environment_bindings!(scene)) end - diff --git a/src/scene_object/registry_topology.jl b/src/scene_object/registry_topology.jl index 656b78e81..aea5d1c53 100644 --- a/src/scene_object/registry_topology.jl +++ b/src/scene_object/registry_topology.jl @@ -216,6 +216,7 @@ struct MTGObjectAdapter{I,S,K,SP,N,G,ST} name::N geometry::G status::ST + max_node_id::Base.RefValue{Int} end mutable struct Scene{R,A,E,I,SA} @@ -229,6 +230,7 @@ mutable struct Scene{R,A,E,I,SA} bindings_dirty::Bool environment_bindings_dirty::Bool environment_dirty_objects::Union{Nothing,Set{ObjectId}} + binding_dirty_objects::Union{Nothing,Set{ObjectId}} revision::Int environment_revision::Int end @@ -369,6 +371,7 @@ function Scene( true, true, nothing, + Set{ObjectId}(), 0, 0, ) @@ -454,7 +457,16 @@ function objects_from_mtg( geometry=node -> _mtg_attribute(node, :geometry, nothing), status=node -> _mtg_attribute(node, :plantsimengine_status, nothing), ) - adapter = MTGObjectAdapter(id, scale, kind, species, name, geometry, status) + adapter = MTGObjectAdapter( + id, + scale, + kind, + species, + name, + geometry, + status, + Ref(MultiScaleTreeGraph.max_id(root)), + ) return _objects_from_mtg(root, adapter) end @@ -500,7 +512,16 @@ function Scene( geometry=node -> _mtg_attribute(node, :geometry, nothing), status=node -> _mtg_attribute(node, :plantsimengine_status, nothing), ) - adapter = MTGObjectAdapter(id, scale, kind, species, name, geometry, status) + adapter = MTGObjectAdapter( + id, + scale, + kind, + species, + name, + geometry, + status, + Ref(MultiScaleTreeGraph.max_id(root)), + ) objects = _objects_from_mtg(root, adapter) return Scene( objects...; @@ -523,18 +544,23 @@ function _mark_environment_bindings_dirty!(scene::Scene, object_id::Union{Nothin return scene end -function _mark_bindings_dirty!(scene::Scene) - scene.binding_cache = nothing +function _mark_bindings_dirty!(scene::Scene, object_id::Union{Nothing,ObjectId}=nothing) + if isnothing(object_id) + scene.binding_cache = nothing + scene.binding_dirty_objects = nothing + elseif !isnothing(scene.binding_dirty_objects) + push!(scene.binding_dirty_objects, object_id) + end scene.bindings_dirty = true scene.revision += 1 - return _mark_environment_bindings_dirty!(scene) + return _mark_environment_bindings_dirty!(scene, object_id) end bindings_dirty(scene::Scene) = scene.bindings_dirty environment_bindings_dirty(scene::Scene) = scene.environment_bindings_dirty scene_revision(scene::Scene) = scene.revision environment_revision(scene::Scene) = scene.environment_revision -compiled_bindings(scene::Scene) = scene.binding_cache +compiled_bindings(scene::Scene) = scene.bindings_dirty ? nothing : scene.binding_cache compiled_environment_bindings(scene::Scene) = scene.environment_binding_cache mark_environment_binding_dirty!(scene::Scene) = _mark_environment_bindings_dirty!(scene) function mark_environment_binding_dirty!(scene::Scene, id) @@ -642,7 +668,7 @@ function register_object!(scene::Scene, object::Object; parent=object.parent) parent_object = registry.objects[object.parent] object.id in parent_object.children || push!(parent_object.children, object.id) end - _mark_bindings_dirty!(scene) + _mark_bindings_dirty!(scene, object.id) return object end @@ -690,7 +716,7 @@ function add_organ!( organ_symbol, mtg_scale::Integer; index::Integer=0, - id=MultiScaleTreeGraph.new_id(MultiScaleTreeGraph.get_root(parent_node)), + id=nothing, attributes=NamedTuple(), initial_status=NamedTuple(), kind=nothing, @@ -706,12 +732,19 @@ function add_organ!( parent_id = ObjectId(adapter.id(parent_node)) _scene_object(scene, parent_id) root = MultiScaleTreeGraph.get_root(parent_node) - isnothing(MultiScaleTreeGraph.get_node(root, Int(id))) || error( - "MTG node id `$(id)` already exists." + node_id = if isnothing(id) + adapter.max_node_id[] += 1 + else + explicit_id = Int(id) + adapter.max_node_id[] = max(adapter.max_node_id[], explicit_id) + explicit_id + end + isnothing(MultiScaleTreeGraph.get_node(root, node_id)) || error( + "MTG node id `$(node_id)` already exists." ) node = MultiScaleTreeGraph.Node( - Int(id), + node_id, parent_node, MultiScaleTreeGraph.NodeMTG( Symbol(link), @@ -868,8 +901,18 @@ function refresh_bindings!(scene::Scene, specs=scene.applications; force::Bool=f return compile_scene(scene, specs) end if force || scene.bindings_dirty || isnothing(scene.binding_cache) - scene.binding_cache = compile_scene(scene, scene.applications) + can_extend = !force && + !isnothing(scene.binding_cache) && + !isnothing(scene.binding_dirty_objects) && + !isempty(scene.binding_dirty_objects) + scene.binding_cache = can_extend ? + _extend_compiled_scene( + scene, + scene.binding_cache, + scene.binding_dirty_objects, + ) : compile_scene(scene, scene.applications) scene.bindings_dirty = false + scene.binding_dirty_objects = Set{ObjectId}() end return scene.binding_cache end diff --git a/src/scene_object/runtime_outputs.jl b/src/scene_object/runtime_outputs.jl index 08479bcf1..9e6e8f715 100644 --- a/src/scene_object/runtime_outputs.jl +++ b/src/scene_object/runtime_outputs.jl @@ -3,6 +3,8 @@ struct SceneOutputRetentionPlan temporal_dependencies::Set{Tuple{Symbol,Symbol}} requested_outputs::Set{Tuple{Symbol,Symbol}} dependency_horizons::Dict{Tuple{Symbol,Symbol},Float64} + retained_application_ids::Set{Symbol} + retained_outputs_by_application::Dict{Symbol,Vector{Symbol}} end """ @@ -39,6 +41,9 @@ struct SceneCallTarget{CS,EB,A,S,TS,OR,C} end abstract type AbstractSceneExecutionBatch end +struct UnspecifiedSceneMeteo end +const _UNSPECIFIED_SCENE_METEO = UnspecifiedSceneMeteo() +const _SCENE_RAW_METEO_CACHE_ID = Symbol("#raw_global_meteo") struct CompiledSceneExecutionTarget{M,S,MB,IB,EB} object_id::ObjectId @@ -149,6 +154,15 @@ function _scene_retain_output( key in retention.requested_outputs end +function _scene_retain_application( + retention::SceneOutputRetentionPlan, + application_id::Symbol, +) + return retention.retain_all || application_id in retention.retained_application_ids +end + +_scene_retain_application(::Nothing, application_id::Symbol) = true + _scene_retain_output(::Nothing, application_id::Symbol, variable::Symbol) = true function _scene_prune_dependency_stream!( @@ -182,11 +196,15 @@ function _scene_publish_outputs!( status, time::Real, retention=nothing, + retained_variables=nothing, ) isnothing(streams) && return nothing - for variable in keys(outputs_(application.spec)) + _scene_retain_application(retention, application.id) || return nothing + variables = isnothing(retained_variables) ? keys(outputs_(application.spec)) : retained_variables + for variable in variables var = Symbol(variable) - _scene_retain_output(retention, application.id, var) || continue + isnothing(retained_variables) && + !_scene_retain_output(retention, application.id, var) && continue hasproperty(status, var) || error( "Application `$(application.id)` declares output `$(var)`, but object ", "`$(object_id.value)` status has no such variable." @@ -204,8 +222,17 @@ function _scene_publish_outputs!( "Scene temporal streams require a stable output type." ) end - filter!(sample -> !isapprox(sample[1], float(time); atol=1.0e-8, rtol=0.0), samples) - push!(samples, (float(time), value)) + sample_time = float(time) + if !isempty(samples) && + isapprox(last(samples)[1], sample_time; atol=1.0e-8, rtol=0.0) + pop!(samples) + elseif !isempty(samples) && last(samples)[1] > sample_time + filter!( + sample -> !isapprox(sample[1], sample_time; atol=1.0e-8, rtol=0.0), + samples, + ) + end + push!(samples, (sample_time, value)) _scene_prune_dependency_stream!( samples, retention, @@ -218,15 +245,12 @@ function _scene_publish_outputs!( end function _scene_latest_sample(samples, time::Real) - latest = nothing - latest_t = -Inf - for (sample_t, value) in samples - sample_t <= float(time) || continue - sample_t >= latest_t || continue - latest = value - latest_t = sample_t + requested_time = float(time) + for index in reverse(eachindex(samples)) + sample_t, value = samples[index] + sample_t <= requested_time && return value end - return latest + return nothing end function _scene_linear_value(v_left, v_right, α) @@ -243,11 +267,43 @@ function _scene_linear_value(v_left, v_right, α) return v_left + increment end +function _scene_sample_last_le(samples, time::Real) + low = firstindex(samples) + high = lastindex(samples) + found = nothing + while low <= high + middle = low + ((high - low) >>> 1) + if samples[middle][1] <= time + found = middle + low = middle + 1 + else + high = middle - 1 + end + end + return found +end + +function _scene_sample_first_ge(samples, time::Real) + low = firstindex(samples) + high = lastindex(samples) + found = nothing + while low <= high + middle = low + ((high - low) >>> 1) + if samples[middle][1] >= time + found = middle + high = middle - 1 + else + low = middle + 1 + end + end + return found +end + function _scene_interpolated_sample(samples, time::Real, policy::Interpolate) isempty(samples) && return nothing t = float(time) - prev_idx = findlast(sample -> sample[1] <= t + 1.0e-8, samples) - next_idx = findfirst(sample -> sample[1] >= t - 1.0e-8, samples) + prev_idx = _scene_sample_last_le(samples, t + 1.0e-8) + next_idx = _scene_sample_first_ge(samples, t - 1.0e-8) if !isnothing(prev_idx) && !isnothing(next_idx) t_prev, v_prev = samples[prev_idx] @@ -393,6 +449,8 @@ function _scene_temporal_source_application(compiled::CompiledScene, binding::Co "has no resolved source application. Name the producer and add " * "`application=...` to `Inputs(...)`." ) + length(binding.source_application_ids) == 1 && + return only(binding.source_application_ids) matches = Symbol[ application_id for application_id in binding.source_application_ids if source_id in _compiled_application_by_id(compiled, application_id).target_ids @@ -520,7 +578,7 @@ function _materialize_scene_inputs!( for binding in bindings if binding.carrier_hint == :temporal_stream isnothing(streams) && continue - isnothing(timeline) && (timeline = _scene_timeline(compiled.scene)) + isnothing(timeline) && (timeline = compiled.timeline) value = _scene_temporal_input_value( compiled, binding, @@ -548,7 +606,7 @@ function _materialize_scene_inputs!( for binding in bindings if binding.carrier_hint == :temporal_stream isnothing(streams) && continue - isnothing(timeline) && (timeline = _scene_timeline(compiled.scene)) + isnothing(timeline) && (timeline = compiled.timeline) value = _scene_temporal_input_value( compiled, binding, @@ -583,14 +641,16 @@ function _scene_meteo_for_binding( isnothing(binding) && return nothing isnothing(binding.backend) && return nothing if binding.backend isa GlobalConstant + step = Int(round(time)) + key = (application.id, step) + haskey(env_bindings.sample_cache, key) && + return env_bindings.sample_cache[key] + raw_key = (_SCENE_RAW_METEO_CACHE_ID, step) + raw_row = get!(env_bindings.sample_cache, raw_key) do + _meteo_row_at_step(environment_meteo(binding.backend), step) + end sampler = get(env_bindings.samplers_by_application, application.id, nothing) if !isnothing(sampler) - step = Int(round(time)) - key = (application.id, step) - haskey(env_bindings.sample_cache, key) && - return env_bindings.sample_cache[key] - meteo = environment_meteo(binding.backend) - raw_row = _meteo_row_at_step(meteo, step) sampled = _sample_meteo_for_model( sampler, raw_row, @@ -601,6 +661,21 @@ function _scene_meteo_for_binding( env_bindings.sample_cache[key] = sampled return sampled end + bindings = meteo_bindings(application.spec) + has_bindings = bindings isa NamedTuple && !isempty(keys(bindings)) + environment_sources = _environment_source_overrides(application.spec) + if !has_bindings && isempty(keys(environment_sources)) + env_bindings.sample_cache[key] = raw_row + return raw_row + end + sampled = sample_environment( + binding.backend, + binding.support, + time, + application.spec, + ) + env_bindings.sample_cache[key] = sampled + return sampled end return sample_environment(binding.backend, binding.support, time, application.spec) end @@ -788,8 +863,14 @@ function _run_scene_execution_target!( constants=nothing, temporal_streams=nothing, output_retention=nothing, + meteo=_UNSPECIFIED_SCENE_METEO, + publish_outputs::Bool=true, + scatter_outputs::Bool=true, + retained_outputs=nothing, ) - status = _materialize_scene_inputs!( + status = isempty(target.input_bindings) ? + target.status : + _materialize_scene_inputs!( target.status, target.input_bindings, compiled, @@ -797,12 +878,13 @@ function _run_scene_execution_target!( temporal_streams, time, ) - meteo = _scene_meteo_for_binding( + meteo_value = meteo isa UnspecifiedSceneMeteo ? + _scene_meteo_for_binding( env_bindings, application, target.environment_binding, time, - ) + ) : meteo context = SceneRunContext( compiled, env_bindings, @@ -814,20 +896,21 @@ function _run_scene_execution_target!( constants, true, ) - run!(target.model, target.models, status, meteo, constants, context) - _scatter_scene_environment_outputs!( + run!(target.model, target.models, status, meteo_value, constants, context) + scatter_outputs && _scatter_scene_environment_outputs!( application, target.environment_binding, status, time, ) - _scene_publish_outputs!( + publish_outputs && _scene_publish_outputs!( temporal_streams, application, target.object_id, status, time, output_retention, + retained_outputs, ) return status end @@ -842,6 +925,26 @@ function _run_scene_execution_batch!( output_retention=nothing, ) _scene_application_should_run(batch.application, time) || return nothing + shared_meteo = if !isempty(batch.targets) && + !isnothing(first(batch.targets).environment_binding) && + first(batch.targets).environment_binding.backend isa GlobalConstant + _scene_meteo_for_binding( + env_bindings, + batch.application, + first(batch.targets).environment_binding, + time, + ) + else + _UNSPECIFIED_SCENE_METEO + end + publish_outputs = _scene_retain_application(output_retention, batch.application.id) + scatter_outputs = !isempty(keys(meteo_outputs_(batch.application.spec))) + retained_outputs = output_retention isa SceneOutputRetentionPlan ? + get( + output_retention.retained_outputs_by_application, + batch.application.id, + (), + ) : nothing for target in batch.targets _run_scene_execution_target!( compiled, @@ -852,6 +955,10 @@ function _run_scene_execution_batch!( constants=constants, temporal_streams=temporal_streams, output_retention=output_retention, + meteo=shared_meteo, + publish_outputs=publish_outputs, + scatter_outputs=scatter_outputs, + retained_outputs=retained_outputs, ) end return nothing @@ -882,6 +989,10 @@ function _compiled_scene_execution_target( (application.id, object_id), (), ) + temporal_input_bindings = Tuple( + binding for binding in input_bindings + if binding.carrier_hint == :temporal_stream + ) environment_binding = _environment_binding_for( env_bindings, application.id, @@ -892,7 +1003,7 @@ function _compiled_scene_execution_target( model, status, models, - input_bindings, + temporal_input_bindings, environment_binding, ) end @@ -962,6 +1073,38 @@ function compile_scene_execution_plan( ) end +function _extend_scene_execution_plan( + plan::CompiledSceneExecutionPlan, + compiled::CompiledScene, + env_bindings::CompiledEnvironmentBindings, + added_object_ids, +) + added = Set(added_object_ids) + manual_application_ids = _manual_call_application_ids(compiled) + for application in _ordered_scene_applications(compiled) + application.id in manual_application_ids && continue + for object_id in application.target_ids + object_id in added || continue + target = _compiled_scene_execution_target( + compiled, + env_bindings, + application, + object_id, + ) + batch_index = findfirst(plan.batches) do batch + batch.application.id == application.id && eltype(batch.targets) === typeof(target) + end + isnothing(batch_index) && return compile_scene_execution_plan(compiled, env_bindings) + push!(plan.batches[batch_index].targets, target) + end + end + return CompiledSceneExecutionPlan( + plan.batches, + compiled.revision, + env_bindings.environment_revision, + ) +end + function explain_execution_plan(plan::CompiledSceneExecutionPlan) return [ ( @@ -1004,7 +1147,7 @@ function compile_scene_output_retention( ) temporal_dependencies = Set{Tuple{Symbol,Symbol}}() dependency_horizons = Dict{Tuple{Symbol,Symbol},Float64}() - timeline = _scene_timeline(compiled.scene) + timeline = compiled.timeline for binding in compiled.input_bindings binding.carrier_hint == :temporal_stream || continue consumer = _compiled_application_by_id(compiled, binding.application_id) @@ -1044,11 +1187,38 @@ function compile_scene_output_retention( ) push!(requested_outputs, (application.id, request.var)) end + retained_outputs_by_application = Dict{Symbol,Vector{Symbol}}() + retained_keys = if retain_all + Set( + (application.id, Symbol(variable)) + for application in compiled.applications + for variable in keys(outputs_(application.spec)) + ) + else + union(temporal_dependencies, requested_outputs) + end + for (application_id, variable) in retained_keys + push!( + get!(retained_outputs_by_application, application_id, Symbol[]), + variable, + ) + end + for variables in values(retained_outputs_by_application) + sort!(variables; by=string) + end return SceneOutputRetentionPlan( retain_all, temporal_dependencies, requested_outputs, dependency_horizons, + Set{Symbol}( + application_id + for (application_id, _) in union( + temporal_dependencies, + requested_outputs, + ) + ), + retained_outputs_by_application, ) end @@ -1125,10 +1295,13 @@ function _scene_call_targets(context::SceneRunContext, name::Symbol) ) for binding in bindings binding.call == name || continue + single_callee_application = binding.multiplicity != :many && + length(binding.callee_application_ids) == 1 for application_id in binding.callee_application_ids callee_application = _compiled_application_by_id(context.compiled, application_id) for object_id in binding.callee_object_ids - object_id in callee_application.target_ids || continue + single_callee_application || + object_id in callee_application.target_ids || continue status = _scene_object_status(context.compiled.scene, object_id) push!( targets, @@ -1228,14 +1401,22 @@ end function _refresh_simulation_runtime!(simulation::SceneSimulation) scene = simulation.scene if bindings_dirty(scene) + added_object_ids = isnothing(scene.binding_dirty_objects) ? + nothing : copy(scene.binding_dirty_objects) simulation.compiled = refresh_bindings!(scene) simulation.environment_bindings = refresh_environment_bindings!( scene, simulation.compiled, ) - simulation.execution_plan = compile_scene_execution_plan( + simulation.execution_plan = isnothing(added_object_ids) ? + compile_scene_execution_plan( simulation.compiled, simulation.environment_bindings, + ) : _extend_scene_execution_plan( + simulation.execution_plan, + simulation.compiled, + simulation.environment_bindings, + added_object_ids, ) elseif environment_bindings_dirty(scene) simulation.environment_bindings = refresh_environment_bindings!( @@ -1255,8 +1436,12 @@ function _continue_scene!(simulation::SceneSimulation, steps::Integer) start_step = simulation.current_step + 1 final_step = simulation.current_step + steps for step in start_step:final_step + added_object_ids = bindings_dirty(simulation.scene) && + !isnothing(simulation.scene.binding_dirty_objects) ? + copy(simulation.scene.binding_dirty_objects) : nothing _refresh_simulation_runtime!(simulation) - _refresh_output_request_targets!(simulation) + _refresh_output_request_targets!(simulation, added_object_ids) + empty!(simulation.environment_bindings.sample_cache) for batch in simulation.execution_plan.batches _run_scene_execution_batch!( batch, @@ -1270,8 +1455,11 @@ function _continue_scene!(simulation::SceneSimulation, steps::Integer) end simulation.current_step = step end + added_object_ids = bindings_dirty(simulation.scene) && + !isnothing(simulation.scene.binding_dirty_objects) ? + copy(simulation.scene.binding_dirty_objects) : nothing _refresh_simulation_runtime!(simulation) - _refresh_output_request_targets!(simulation) + _refresh_output_request_targets!(simulation, added_object_ids) return simulation end @@ -1292,15 +1480,29 @@ function _initial_output_request_targets(scene, compiled, output_requests) return targets end -function _refresh_output_request_targets!(simulation::SceneSimulation) +function _refresh_output_request_targets!(simulation::SceneSimulation, added_object_ids=nothing) for request in simulation.output_requests _, object_scales = simulation.output_request_targets[request.name] - matched_ids = resolve_object_ids( - simulation.scene, - _selector_as_many(request.selector); - context=request.context, - ) - if request.selector isa Union{One,OptionalOne} && length(matched_ids) > 1 + matched_ids = if isnothing(added_object_ids) + resolve_object_ids( + simulation.scene, + _selector_as_many(request.selector); + context=request.context, + ) + else + ObjectId[ + object_id for object_id in added_object_ids + if _selector_matches_object_id( + simulation.scene, + request.selector, + object_id; + context=request.context, + ) + ] + end + match_count = isnothing(added_object_ids) ? + length(matched_ids) : length(object_scales) + length(matched_ids) + if request.selector isa Union{One,OptionalOne} && match_count > 1 error( "Output request `$(request.name)` expected at most one current object for selector ", "`$(request.selector)`, got $([id.value for id in matched_ids]).", diff --git a/src/scene_object/selectors.jl b/src/scene_object/selectors.jl index cd4c6ce22..b9749c791 100644 --- a/src/scene_object/selectors.jl +++ b/src/scene_object/selectors.jl @@ -313,7 +313,17 @@ function _mount_object_instance_applications(instances, instance_ids) return Tuple(mounted) end -_sort_object_ids!(ids) = sort!(ids; by=id -> string(id.value)) +function _object_id_isless(left::ObjectId, right::ObjectId) + left_value = left.value + right_value = right.value + if typeof(left_value) === typeof(right_value) && + hasmethod(isless, Tuple{typeof(left_value),typeof(right_value)}) + return isless(left_value, right_value) + end + return isless(string(left_value), string(right_value)) +end + +_sort_object_ids!(ids) = sort!(ids; lt=_object_id_isless) function _object_id_from_context(context) isnothing(context) && return nothing @@ -493,6 +503,84 @@ function _scope_object_ids(scene::Scene, scope, context) error("Unsupported object scope selector `$(scope)` of type `$(typeof(scope))`.") end +function _registry_selector_ids(index, requested) + isnothing(requested) && return nothing + requested_values = requested isa Tuple ? requested : (requested,) + ids = Set{ObjectId}() + for value in requested_values + union!(ids, get(index, Symbol(value), Set{ObjectId}())) + end + return ids +end + +function _indexed_object_ids(scene::Scene; scale=nothing, kind=nothing, species=nothing, name=nothing) + candidate_sets = Set{ObjectId}[] + for ids in ( + _registry_selector_ids(scene.registry.by_scale, scale), + _registry_selector_ids(scene.registry.by_kind, kind), + _registry_selector_ids(scene.registry.by_species, species), + ) + isnothing(ids) || push!(candidate_sets, ids) + end + if !isnothing(name) + names = name isa Tuple ? name : (name,) + push!( + candidate_sets, + Set{ObjectId}( + id for candidate_name in names + for id in (get(scene.registry.by_name, Symbol(candidate_name), nothing),) + if !isnothing(id) + ), + ) + end + isempty(candidate_sets) && return nothing + sort!(candidate_sets; by=length) + ids = copy(first(candidate_sets)) + for candidates in Iterators.drop(candidate_sets, 1) + intersect!(ids, candidates) + end + return ObjectId[ids...] +end + +function _object_is_in_subtree(scene::Scene, object_id::ObjectId, root_id::ObjectId) + current_id = object_id + while true + current_id == root_id && return true + parent_id = _scene_object(scene, current_id).parent + isnothing(parent_id) && return false + current_id = parent_id + end +end + +function _scope_contains_object(scene::Scene, scope, context, object_id::ObjectId) + (isnothing(scope) || scope isa SceneScope) && return true + return if scope isa Self + current_id = _object_id_from_context(context) + isnothing(current_id) && error("`Self()` selectors require a current object context.") + object_id == current_id + elseif scope isa Subtree + current_id = _object_id_from_context(context) + isnothing(current_id) && error("`Subtree()` selectors require a current object context.") + _object_is_in_subtree(scene, object_id, current_id) + elseif scope isa SelfPlant + current_id = _object_id_from_context(context) + isnothing(current_id) && error("`SelfPlant()` selectors require a current object context.") + plant_id = _ancestor_id(scene, current_id; scale=:Plant) + isnothing(plant_id) && error("No `scale=:Plant` ancestor found for object `$(current_id.value)`.") + _object_is_in_subtree(scene, object_id, plant_id) + elseif scope isa Ancestor + current_id = _object_id_from_context(context) + isnothing(current_id) && error("`Ancestor(...)` selectors require a current object context.") + ancestor_id = _ancestor_id(scene, current_id; scale=scope.scale, include_self=false) + isnothing(ancestor_id) && error( + "No matching ancestor found for object `$(current_id.value)` and selector `$(scope)`." + ) + _object_is_in_subtree(scene, object_id, ancestor_id) + else + object_id in Set(_scope_object_ids(scene, scope, context)) + end +end + function _symbol_edit_distance(left::Symbol, right::Symbol) a = collect(string(left)) b = collect(string(right)) @@ -579,6 +667,115 @@ function _matches_object_criteria(object::Object; scale=nothing, kind=nothing, s return true end +function _selector_matches_object_id( + scene::Scene, + selector::AbstractObjectMultiplicity, + object_id::ObjectId; + context=nothing, + default_to_context::Bool=false, + default_scope=nothing, +) + criteria_ = criteria(selector) + relation = _criteria_value(criteria_, :relation, Relation) + explicit_scope = _criteria_scope(criteria_) + scale = _criteria_value(criteria_, :scale, Scale) + kind = _criteria_value(criteria_, :kind, Kind) + species = _criteria_value(criteria_, :species, Species) + name = haskey(criteria_, :name) ? criteria_.name : nothing + if default_to_context && + isnothing(explicit_scope) && + isnothing(relation) && + isnothing(scale) && + isnothing(kind) && + isnothing(species) && + isnothing(name) && + !isnothing(context) + return object_id == _object_id_from_context(context) + end + object = _scene_object(scene, object_id) + _matches_object_criteria(object; scale=scale, kind=kind, species=species, name=name) || + return false + if isnothing(relation) + scope = isnothing(explicit_scope) ? default_scope : explicit_scope + if scope isa Ancestor && !isnothing(scale) && scale == scope.scale + current_id = _object_id_from_context(context) + isnothing(current_id) && error( + "`Ancestor(...)` selectors require a current object context." + ) + return object_id == _ancestor_id( + scene, + current_id; + scale=scope.scale, + include_self=false, + ) + end + return _scope_contains_object(scene, scope, context, object_id) + end + object_id in _relation_object_ids(scene, relation, context) || return false + return isnothing(explicit_scope) || + _scope_contains_object(scene, explicit_scope, context, object_id) +end + +function _selector_matches_any_object_id( + scene::Scene, + selector::AbstractObjectMultiplicity, + object_ids; + context=nothing, + default_to_context::Bool=false, + default_scope=nothing, +) + criteria_ = criteria(selector) + relation = _criteria_value(criteria_, :relation, Relation) + explicit_scope = _criteria_scope(criteria_) + scale = _criteria_value(criteria_, :scale, Scale) + kind = _criteria_value(criteria_, :kind, Kind) + species = _criteria_value(criteria_, :species, Species) + name = haskey(criteria_, :name) ? criteria_.name : nothing + if default_to_context && + isnothing(explicit_scope) && + isnothing(relation) && + isnothing(scale) && + isnothing(kind) && + isnothing(species) && + isnothing(name) && + !isnothing(context) + return _object_id_from_context(context) in object_ids + end + related_ids = isnothing(relation) ? nothing : Set(_relation_object_ids(scene, relation, context)) + scope = isnothing(explicit_scope) ? default_scope : explicit_scope + if isnothing(relation) && scope isa Ancestor && !isnothing(scale) && scale == scope.scale + current_id = _object_id_from_context(context) + isnothing(current_id) && error( + "`Ancestor(...)` selectors require a current object context." + ) + ancestor_id = _ancestor_id( + scene, + current_id; + scale=scope.scale, + include_self=false, + ) + isnothing(ancestor_id) && return false + ancestor_id in object_ids || return false + object = _scene_object(scene, ancestor_id) + return _matches_object_criteria( + object; + scale=scale, + kind=kind, + species=species, + name=name, + ) + end + for object_id in object_ids + object = _scene_object(scene, object_id) + _matches_object_criteria(object; scale=scale, kind=kind, species=species, name=name) || + continue + isnothing(related_ids) || object_id in related_ids || continue + _scope_contains_object(scene, scope, context, object_id) || continue + return true + end + return false +end + function resolve_object_ids(scene::Scene, selector::AbstractObjectMultiplicity; context=nothing) return _resolve_object_ids(scene, selector; context=context) end @@ -617,8 +814,39 @@ function _resolve_object_ids( return ObjectId[_object_id_from_context(context)] end + indexed_ids = _indexed_object_ids( + scene; + scale=scale, + kind=kind, + species=species, + name=name, + ) candidate_ids = if isnothing(relation) - _scope_object_ids(scene, scope, context) + if scope isa Ancestor && !isnothing(scale) && scale == scope.scale + current_id = _object_id_from_context(context) + isnothing(current_id) && error( + "`Ancestor(...)` selectors require a current object context." + ) + ancestor_id = _ancestor_id( + scene, + current_id; + scale=scope.scale, + include_self=false, + ) + isnothing(ancestor_id) ? ObjectId[] : ObjectId[ancestor_id] + elseif isnothing(indexed_ids) + _scope_object_ids(scene, scope, context) + elseif isnothing(scope) || scope isa SceneScope + indexed_ids + elseif length(indexed_ids) * 4 <= length(scene.registry.objects) + ObjectId[ + id for id in indexed_ids + if _scope_contains_object(scene, scope, context, id) + ] + else + scoped_ids = Set(_scope_object_ids(scene, scope, context)) + ObjectId[id for id in indexed_ids if id in scoped_ids] + end else related_ids = _relation_object_ids(scene, relation, context) if isnothing(explicit_scope) @@ -634,11 +862,27 @@ function _resolve_object_ids( ] _sort_object_ids!(ids) + diagnostic_candidate_ids = if isempty(ids) && !isnothing(indexed_ids) + if isnothing(relation) + _scope_object_ids(scene, scope, context) + else + related_ids = _relation_object_ids(scene, relation, context) + if isnothing(explicit_scope) + related_ids + else + scoped_ids = Set(_scope_object_ids(scene, explicit_scope, context)) + ObjectId[id for id in related_ids if id in scoped_ids] + end + end + else + candidate_ids + end + if selector isa One && length(ids) != 1 _selector_resolution_error( scene, selector, - candidate_ids, + diagnostic_candidate_ids, ids; context=context, scale=scale, @@ -650,7 +894,7 @@ function _resolve_object_ids( _selector_resolution_error( scene, selector, - candidate_ids, + diagnostic_candidate_ids, ids; context=context, scale=scale, diff --git a/src/visualization/scene_graph_view.jl b/src/visualization/scene_graph_view.jl index 44b63a5a7..367f21aad 100644 --- a/src/visualization/scene_graph_view.jl +++ b/src/visualization/scene_graph_view.jl @@ -165,12 +165,15 @@ function _scene_graph_compiled( scene, applications, applications_by_id, + _applications_by_object(applications), input_bindings, call_bindings, input_by_target, call_by_target, + _index_dynamic_input_bindings(scene, input_bindings), model_bundles, application_order, + _scene_timeline(scene), scene.revision, ) end diff --git a/test/test-unified-scene-object-api.jl b/test/test-unified-scene-object-api.jl index ad14a1160..9a2e351bc 100644 --- a/test/test-unified-scene-object-api.jl +++ b/test/test-unified-scene-object-api.jl @@ -810,6 +810,17 @@ end ) @test length(MultiScaleTreeGraph.children(mtg_plant)) == child_count + auto_id_leaf_status = add_organ!( + mtg_plant, + mtg_scene, + :+, + :Leaf, + 2; + index=3, + ) + @test node_id(auto_id_leaf_status.node) == 5 + @test auto_id_leaf_status.node[:plantsimengine_status] === auto_id_leaf_status + scene = Scene( Object(:scene; scale=:Scene, kind=:scene), Object(:plant_1; scale=:Plant, kind=:plant, species=:oil_palm, parent=:scene), @@ -2063,6 +2074,14 @@ end ) @test has_reference_carrier(leaf_area_binding) @test input_carrier(leaf_area_binding) isa PlantSimEngine.RefVector + leaf_2_area_binding = only( + binding for binding in carrier_compiled.input_bindings + if binding.application_id == :carrier_consumer && + binding.consumer_id == ObjectId(:leaf_2) && + binding.input == :leaf_areas + ) + @test input_carrier(leaf_2_area_binding) === input_carrier(leaf_area_binding) + @test leaf_2_area_binding.source_ids === leaf_area_binding.source_ids @test input_value(leaf_area_binding)[1] == 1.0 input_value(leaf_area_binding)[1] = 4.0 leaf_1_object = only(object for object in scene_objects(carrier_scene; scale=:Leaf) if object.id == ObjectId(:leaf_1)) @@ -2148,6 +2167,24 @@ end (:dual_sum, ObjectId(:scene), :total) ], ) == Tuple{Float64,SceneObjectDualLike{BigFloat}} + original_generic_carrier = input_carrier(generic_carrier_binding) + register_object!( + generic_carrier_scene, + Object( + :leaf_3; + scale=:Leaf, + kind=:plant, + status=Status(dual_value=SceneObjectDualLike(big"4.5", big"3.5")), + ); + parent=:scene, + ) + extended_generic_compiled = Advanced.refresh_bindings!(generic_carrier_scene) + extended_generic_binding = only(extended_generic_compiled.input_bindings) + @test input_carrier(extended_generic_binding) === original_generic_carrier + @test extended_generic_binding.source_ids == + ObjectId[ObjectId(:leaf_1), ObjectId(:leaf_2), ObjectId(:leaf_3)] + @test input_value(extended_generic_binding)[3] == + SceneObjectDualLike(big"4.5", big"3.5") cache_scene = Scene( Object(:scene; scale=:Scene, kind=:scene), @@ -2415,7 +2452,7 @@ end ) @test values == [10.0, 25.0] end - @test length(windowed_default_sim.environment_bindings.sample_cache) == 2 + @test isempty(windowed_default_sim.environment_bindings.sample_cache) @test all( row -> row.temporal_sampler, explain_environment_bindings(windowed_default_sim.environment_bindings), From 8a3c52be487c008df51d38e47c72eb46cc1907f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Sun, 21 Jun 2026 09:16:57 +0200 Subject: [PATCH 28/33] Delete runtests.jl --- docs/test/runtests.jl | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 docs/test/runtests.jl diff --git a/docs/test/runtests.jl b/docs/test/runtests.jl deleted file mode 100644 index 08d6c77ce..000000000 --- a/docs/test/runtests.jl +++ /dev/null @@ -1,8 +0,0 @@ -using Test - -ENV["PLANTSIMENGINE_DOCS_BUILD_ONLY"] = "true" -include(joinpath(@__DIR__, "..", "make.jl")) - -@testset "documentation build" begin - @test isdir(joinpath(@__DIR__, "..", "build")) -end From e8a0cc991ee6db92de109a6357b73eab4143d6e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Mon, 22 Jun 2026 09:09:43 +0200 Subject: [PATCH 29/33] Simplify examples in docs -> use ModelList equivalent whenever possible --- README.md | 16 ++---- docs/make.jl | 16 +++--- docs/src/API/API_public.md | 4 +- docs/src/guides/graph_visualizer_editor.md | 17 +++--- docs/src/index.md | 14 +---- .../installing_plantsimengine.md | 8 +-- docs/src/scene_object/quickstart.md | 24 +++----- .../step_by_step/detailed_first_example.md | 55 +++++-------------- .../step_by_step/quick_and_dirty_examples.md | 39 +++---------- .../src/step_by_step/simple_model_coupling.md | 39 +++---------- docs/src/working_with_data/fitting.md | 8 +-- examples/Beer.jl | 12 ++-- skills/plantsimengine/SKILL.md | 7 ++- src/evaluation/fit.jl | 6 +- src/scene_object/registry_topology.jl | 7 ++- test/test-scene-api-stabilization.jl | 11 ++++ 16 files changed, 105 insertions(+), 178 deletions(-) diff --git a/README.md b/README.md index 6f6c8843c..9b97b2021 100644 --- a/README.md +++ b/README.md @@ -69,17 +69,9 @@ meteo_day = read_weather( ) scene = Scene( - Object(:scene; scale=:Scene, kind=:scene); - applications=( - ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)), - - ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)), - - ModelSpec(Beer(0.6); name=:light_interception) |> - AppliesTo(One(scale=:Scene)), - ), + ToyDegreeDaysCumulModel(), + ToyLAIModel(), + Beer(0.6); environment=meteo_day, ) @@ -90,7 +82,7 @@ first(out, 6) The compiler infers the unambiguous same-object bindings from each model's declared inputs and outputs: `ToyLAIModel` receives `TT_cu` from -`:degree_days`, and `Beer` receives `LAI` from `:lai`. +`:Degreedays`, and `Beer` receives `LAI` from `:LAI_Dynamic`. ```julia select( diff --git a/docs/make.jl b/docs/make.jl index 3eeff4e8d..c7be112fc 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -11,15 +11,13 @@ function build_scene_graph_example() output_dir = joinpath(@__DIR__, "src", "assets") mkpath(output_dir) scene = Scene( - Object(:plant; name=:plant, scale=:Plant, kind=:plant, status=Status(TT=12.0)); - applications=( - ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(name=:plant)), - ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(name=:plant)), - ModelSpec(Beer(0.6); name=:light_interception) |> - AppliesTo(One(name=:plant)), - ), + ToyDegreeDaysCumulModel(), + ToyLAIModel(), + Beer(0.6); + status=(TT=12.0,), + id=:plant, + scale=:Plant, + kind=:plant, ) write_scene_graph_view( joinpath(output_dir, "scene_graph_example.html"), diff --git a/docs/src/API/API_public.md b/docs/src/API/API_public.md index dd3a54dd5..ba17573b5 100644 --- a/docs/src/API/API_public.md +++ b/docs/src/API/API_public.md @@ -5,8 +5,8 @@ ### Scenario and model applications - `Scene` stores objects, model applications, instances, and environment. -- `Scene(model, models...; status=...)` is the concise one-object form and - lowers to the same object/application representation. +- `Scene(model, models...; status=..., timestep=...)` is the concise one-object + form and lowers to the same object/application representation. - `Object` represents one runtime entity with stable identity and status. - `ObjectTemplate` and `ObjectInstance` reuse a model across instances. - `ModelSpec(model; name=...)` identifies one model application. diff --git a/docs/src/guides/graph_visualizer_editor.md b/docs/src/guides/graph_visualizer_editor.md index c822fd54d..f031afd45 100644 --- a/docs/src/guides/graph_visualizer_editor.md +++ b/docs/src/guides/graph_visualizer_editor.md @@ -20,16 +20,13 @@ using PlantSimEngine using PlantSimEngine.Examples scene = Scene( - Object(:plant; name=:plant, scale=:Plant, kind=:plant, - status=Status(TT=12.0)); - applications=( - ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(name=:plant)), - ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(name=:plant)), - ModelSpec(Beer(0.6); name=:light_interception) |> - AppliesTo(One(name=:plant)), - ), + ToyDegreeDaysCumulModel(), + ToyLAIModel(), + Beer(0.6); + status=(TT=12.0,), + id=:plant, + scale=:Plant, + kind=:plant, ) view = scene_graph_view(scene) diff --git a/docs/src/index.md b/docs/src/index.md index cd64941b4..d83ba9dfe 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -126,17 +126,9 @@ meteo_day = read_weather( ) scene = Scene( - Object(:scene; scale=:Scene, kind=:scene); - applications=( - ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)), - - ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)), - - ModelSpec(Beer(0.6); name=:light_interception) |> - AppliesTo(One(scale=:Scene)), - ), + ToyDegreeDaysCumulModel(), + ToyLAIModel(), + Beer(0.6); environment=meteo_day, ) diff --git a/docs/src/prerequisites/installing_plantsimengine.md b/docs/src/prerequisites/installing_plantsimengine.md index ff88587be..b4a0883cf 100644 --- a/docs/src/prerequisites/installing_plantsimengine.md +++ b/docs/src/prerequisites/installing_plantsimengine.md @@ -31,10 +31,10 @@ meteo = Atmosphere( ) scene = Scene( - Object(:leaf; scale=:Leaf, status=Status(LAI=2.0)); - applications=( - ModelSpec(Beer(0.5)) |> AppliesTo(One(scale=:Leaf)), - ), + Beer(0.5); + status=(LAI=2.0,), + id=:leaf, + scale=:Leaf, environment=meteo, ) diff --git a/docs/src/scene_object/quickstart.md b/docs/src/scene_object/quickstart.md index f786ae162..0bfb97e5b 100644 --- a/docs/src/scene_object/quickstart.md +++ b/docs/src/scene_object/quickstart.md @@ -37,8 +37,8 @@ to the ordinary Scene/Object representation: scene = Scene(ModelA(), ModelB(); status=(initial_value=1.0,)) ``` -Use the explicit form below when applications need names, selectors, or other -scenario policies. +Use the explicit form later in this guide when applications need names, +selectors, or other scenario policies. The first scene has one object, `:scene`, and three model applications: @@ -57,17 +57,9 @@ meteo_day = read_weather( ) scene = Scene( - Object(:scene; scale=:Scene, kind=:scene); - applications=( - ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)), - - ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)), - - ModelSpec(Beer(0.6); name=:light_interception) |> - AppliesTo(One(scale=:Scene)), - ), + ToyDegreeDaysCumulModel(), + ToyLAIModel(), + Beer(0.6); environment=meteo_day, ) @@ -93,8 +85,8 @@ missing, so it can be used to finish configuring a scene. The compiler infers unambiguous same-object dependencies from declared model inputs and outputs: -- `:lai` reads `TT_cu` from `:degree_days`; -- `:light_interception` reads `LAI` from `:lai`. +- `:LAI_Dynamic` reads `TT_cu` from `:Degreedays`; +- `:light_interception` reads `LAI` from `:LAI_Dynamic`. ```@example scene_object_quickstart select( @@ -133,7 +125,7 @@ request = OutputRequest( Many(scale=:Scene), :LAI; name=:lai_every_two_days, - application=:lai, + application=:LAI_Dynamic, policy=HoldLast(), clock=Day(2), ) diff --git a/docs/src/step_by_step/detailed_first_example.md b/docs/src/step_by_step/detailed_first_example.md index 66a557304..67e1a4dd2 100644 --- a/docs/src/step_by_step/detailed_first_example.md +++ b/docs/src/step_by_step/detailed_first_example.md @@ -68,27 +68,18 @@ model reads `LAI`, so we initialize that variable on the object status. ```@example detailed_scene scene = Scene( - Object( - :scene; - scale=:Scene, - kind=:scene, - status=Status(LAI=2.0), - ); - applications=( - ModelSpec(Beer(0.5); name=:light_interception) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), - ), + Beer(0.5); + status=(LAI=2.0,), environment=meteo_day, + timestep=Day(1), ) ``` -`ModelSpec(...)` wraps a reusable model kernel with scenario-level decisions: - -- `name=:light_interception` gives the application a stable name; -- `AppliesTo(One(scale=:Scene))` says it runs on the scene object; -- `TimeStep(Day(1))` says it runs daily; -- `environment=meteo_day` supplies weather values such as radiation. +The concise constructor creates one ordinary scene object and one application +for each supplied model. `status` initializes that object, `timestep` applies a +common daily cadence, and `environment` supplies weather values such as +radiation. Use explicit `ModelSpec` and selectors when applications need +different policies or targets. ## Inspecting The Compiled Scene @@ -158,26 +149,12 @@ Because `Beer` reads `LAI`, the compiler can infer the same-object binding. ```@example detailed_scene coupled_scene = Scene( - Object( - :scene; - scale=:Scene, - kind=:scene, - status=Status(TT_cu=0.0), - ); - applications=( - ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), - - ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), - - ModelSpec(Beer(0.5); name=:light_interception) |> - AppliesTo(One(scale=:Scene)) |> - TimeStep(Day(1)), - ), + ToyDegreeDaysCumulModel(), + ToyLAIModel(), + Beer(0.5); + status=(TT_cu=0.0,), environment=meteo_day, + timestep=Day(1), ) select( @@ -224,11 +201,7 @@ because no model in this scene computes it before `ToyLAIModel` reads it: ```@example detailed_scene bad_scene = Scene( - Object(:scene; scale=:Scene, kind=:scene); - applications=( - ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)), - ), + ToyLAIModel(); environment=meteo_day, ) diff --git a/docs/src/step_by_step/quick_and_dirty_examples.md b/docs/src/step_by_step/quick_and_dirty_examples.md index 8bce819b9..9847c3d34 100644 --- a/docs/src/step_by_step/quick_and_dirty_examples.md +++ b/docs/src/step_by_step/quick_and_dirty_examples.md @@ -27,16 +27,8 @@ Depth = 2 ```@example quick_scene_examples scene = Scene( - Object( - :scene; - scale=:Scene, - kind=:scene, - status=Status(LAI=2.0), - ); - applications=( - ModelSpec(Beer(0.5); name=:light_interception) |> - AppliesTo(One(scale=:Scene)), - ), + Beer(0.5); + status=(LAI=2.0,), environment=meteo_day, ) @@ -52,15 +44,9 @@ value bindings from model inputs and outputs. ```@example quick_scene_examples lai_scene = Scene( - Object(:scene; scale=:Scene, kind=:scene); - applications=( - ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)), - ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)), - ModelSpec(Beer(0.5); name=:light_interception) |> - AppliesTo(One(scale=:Scene)), - ), + ToyDegreeDaysCumulModel(), + ToyLAIModel(), + Beer(0.5); environment=meteo_day, ) @@ -88,17 +74,10 @@ same object. ```@example quick_scene_examples growth_scene = Scene( - Object(:scene; scale=:Scene, kind=:scene); - applications=( - ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)), - ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)), - ModelSpec(Beer(0.5); name=:light_interception) |> - AppliesTo(One(scale=:Scene)), - ModelSpec(ToyRUEGrowthModel(0.2); name=:growth) |> - AppliesTo(One(scale=:Scene)), - ), + ToyDegreeDaysCumulModel(), + ToyLAIModel(), + Beer(0.5), + ToyRUEGrowthModel(0.2); environment=meteo_day, ) diff --git a/docs/src/step_by_step/simple_model_coupling.md b/docs/src/step_by_step/simple_model_coupling.md index 9683497a7..6b036fcd1 100644 --- a/docs/src/step_by_step/simple_model_coupling.md +++ b/docs/src/step_by_step/simple_model_coupling.md @@ -30,11 +30,8 @@ daily cadence, and reads `LAI` from that object's status: ```@example scene_coupling light_scene = Scene( - Object(:scene; scale=:Scene, kind=:scene, status=Status(LAI=2.0)); - applications=( - ModelSpec(Beer(0.5); name=:light_interception) |> - AppliesTo(One(scale=:Scene)), - ), + Beer(0.5); + status=(LAI=2.0,), environment=meteo_day, ) @@ -50,17 +47,9 @@ an input, so the scene compiler infers the binding: ```@example scene_coupling coupled_scene = Scene( - Object(:scene; scale=:Scene, kind=:scene); - applications=( - ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)), - - ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)), - - ModelSpec(Beer(0.5); name=:light_interception) |> - AppliesTo(One(scale=:Scene)), - ), + ToyDegreeDaysCumulModel(), + ToyLAIModel(), + Beer(0.5); environment=meteo_day, ) @@ -95,20 +84,10 @@ same-object binding: ```@example scene_coupling growth_scene = Scene( - Object(:scene; scale=:Scene, kind=:scene); - applications=( - ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> - AppliesTo(One(scale=:Scene)), - - ModelSpec(ToyLAIModel(); name=:lai) |> - AppliesTo(One(scale=:Scene)), - - ModelSpec(Beer(0.5); name=:light_interception) |> - AppliesTo(One(scale=:Scene)), - - ModelSpec(ToyRUEGrowthModel(0.2); name=:growth) |> - AppliesTo(One(scale=:Scene)), - ), + ToyDegreeDaysCumulModel(), + ToyLAIModel(), + Beer(0.5), + ToyRUEGrowthModel(0.2); environment=meteo_day, ) diff --git a/docs/src/working_with_data/fitting.md b/docs/src/working_with_data/fitting.md index f49f989b2..9bedc7e94 100644 --- a/docs/src/working_with_data/fitting.md +++ b/docs/src/working_with_data/fitting.md @@ -33,10 +33,10 @@ meteo = Atmosphere( ) scene = Scene( - Object(:leaf; scale=:Leaf, status=Status(LAI=2.0)); - applications=( - ModelSpec(Beer(0.6)) |> AppliesTo(One(scale=:Leaf)), - ), + Beer(0.6); + status=(LAI=2.0,), + id=:leaf, + scale=:Leaf, environment=meteo, ) diff --git a/examples/Beer.jl b/examples/Beer.jl index c96bb90db..c3eeca60f 100644 --- a/examples/Beer.jl +++ b/examples/Beer.jl @@ -42,8 +42,10 @@ of light extinction. ```julia scene = Scene( - Object(:leaf; scale=:Leaf, status=Status(LAI=2.0)); - applications=(ModelSpec(Beer(0.5)) |> AppliesTo(One(scale=:Leaf)),), + Beer(0.5); + status=(LAI=2.0,), + id=:leaf, + scale=:Leaf, environment=Atmosphere( T=20.0, Wind=1.0, @@ -99,8 +101,10 @@ Create a model list with a Beer model, and fit it to the data: ```julia scene = Scene( - Object(:leaf; scale=:Leaf, status=Status(LAI=2.0)); - applications=(ModelSpec(Beer(0.6)) |> AppliesTo(One(scale=:Leaf)),), + Beer(0.6); + status=(LAI=2.0,), + id=:leaf, + scale=:Leaf, environment=meteo, ) run!(scene) diff --git a/skills/plantsimengine/SKILL.md b/skills/plantsimengine/SKILL.md index 166a4da33..c102e6181 100644 --- a/skills/plantsimengine/SKILL.md +++ b/skills/plantsimengine/SKILL.md @@ -44,7 +44,12 @@ For one object with ordinary same-object inference, use the thin constructor that lowers directly to the same Scene/Object runtime: ```julia -scene = Scene(ModelA(), ModelB(); status=(initial_value=1.0,)) +scene = Scene( + ModelA(), + ModelB(); + status=(initial_value=1.0,), + timestep=Dates.Hour(1), +) ``` Use the explicit object graph below as soon as models require different target diff --git a/src/evaluation/fit.jl b/src/evaluation/fit.jl index 6428c48d6..012fdcb2f 100644 --- a/src/evaluation/fit.jl +++ b/src/evaluation/fit.jl @@ -38,8 +38,10 @@ meteo = Atmosphere( duration=Hour(1), ) scene = Scene( - Object(:leaf; scale=:Leaf, status=Status(LAI=2.0)); - applications=(ModelSpec(Beer(0.6)) |> AppliesTo(One(scale=:Leaf)),), + Beer(0.6); + status=(LAI=2.0,), + id=:leaf, + scale=:Leaf, environment=meteo, ) run!(scene) diff --git a/src/scene_object/registry_topology.jl b/src/scene_object/registry_topology.jl index aea5d1c53..874d1ce6d 100644 --- a/src/scene_object/registry_topology.jl +++ b/src/scene_object/registry_topology.jl @@ -381,7 +381,7 @@ end """ Scene(model::AbstractModel, models::AbstractModel...; status=NamedTuple(), id=:scene, scale=:Scene, kind=:scene, - name=id, environment=nothing) + name=id, environment=nothing, timestep=nothing) Construct a concise one-object simulation. This is syntax lowering only: it creates one ordinary [`Object`](@ref), one normal `ModelSpec` per model, and a @@ -391,6 +391,7 @@ scenes. Use explicit `Object`, `ModelSpec`, and selector construction when applications need names, different cadences, explicit coupling, or different target sets. +Use `timestep` to apply one common cadence to every supplied model. """ function Scene( model::AbstractModel, @@ -401,6 +402,7 @@ function Scene( kind=:scene, name=id, environment=nothing, + timestep=nothing, ) object_name = isnothing(name) ? nothing : Symbol(string(name)) object_status = if status isa Status || isnothing(status) @@ -415,7 +417,8 @@ function Scene( end selector = isnothing(object_name) ? One(scale=scale) : One(name=object_name) applications = map((model, models...)) do application_model - ModelSpec(application_model) |> AppliesTo(selector) + application = ModelSpec(application_model) |> AppliesTo(selector) + isnothing(timestep) ? application : application |> TimeStep(timestep) end return Scene( Object( diff --git a/test/test-scene-api-stabilization.jl b/test/test-scene-api-stabilization.jl index 4959bc4b3..4b4f53c08 100644 --- a/test/test-scene-api-stabilization.jl +++ b/test/test-scene-api-stabilization.jl @@ -90,6 +90,17 @@ end @test length(scene.applications) == 2 @test only(scene_objects(scene)).id == ObjectId(:scene) + timed_scene = Scene( + StabilizationSourceModel(), + StabilizationConsumerModel(); + status=(supplied=2.0,), + timestep=Dates.Hour(2), + ) + @test all( + row -> row.timestep == Dates.Hour(2), + explain_scene_applications(timed_scene), + ) + explicit_scene = Scene( Object(:scene; scale=:Scene, kind=:scene, name=:scene, status=Status(supplied=2.0)); applications=( From 4e17d3b497a6c9aeb710ba38780da3b86955b8f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Vezy?= Date: Mon, 22 Jun 2026 15:12:41 +0200 Subject: [PATCH 30/33] Rename Scene into CompositeModel --- AGENTS.md | 42 +- README.md | 48 +- benchmark/benchmarks.jl | 16 +- benchmark/test-PSE-benchmark.jl | 38 +- benchmark/test-multirate-buffer-benchmark.jl | 12 +- benchmark/test-plantbiophysics.jl | 4 +- benchmark/test-xpalm.jl | 12 +- benchmark/test/runtests.jl | 12 +- docs/make.jl | 30 +- docs/src/API/API_public.md | 48 +- docs/src/API/public_symbols.md | 24 +- docs/src/agent_skill.md | 10 +- .../quickstart.md | 74 +- docs/src/dev/code_cleanup_audit.md | 22 +- ...md => composite_model_completion_audit.md} | 40 +- ...ct_design.md => composite_model_design.md} | 64 +- ...=> composite_model_implementation_plan.md} | 288 ++-- ...ene_handoff.md => maespa_model_handoff.md} | 20 +- .../public_api_refinement_completion_audit.md | 12 +- .../dev/public_api_refinement_decisions.md | 26 +- docs/src/dev/release_notes_handoff.md | 236 +-- docs/src/developers.md | 6 +- docs/src/guides/coupling.md | 2 +- docs/src/guides/data/outputs_plotting.md | 10 +- docs/src/guides/graph_visualizer_editor.md | 54 +- .../guides/modelers/port_existing_model.md | 12 +- docs/src/guides/multiscale/concepts.md | 8 +- docs/src/guides/multiscale/from_one_object.md | 4 +- docs/src/guides/multiscale/import_mtg.md | 8 +- docs/src/guides/multiscale/manual_calls.md | 2 +- .../multiscale/visualizing_structure.md | 2 +- docs/src/guides/time/hourly_daily_weekly.md | 6 +- docs/src/index.md | 56 +- docs/src/introduction/why_plantsimengine.md | 2 +- ...object.md => migration_composite_model.md} | 104 +- docs/src/model_execution.md | 74 +- docs/src/planned_features.md | 4 +- .../installing_plantsimengine.md | 6 +- docs/src/prerequisites/key_concepts.md | 18 +- docs/src/step_by_step/advanced_coupling.md | 12 +- .../step_by_step/detailed_first_example.md | 54 +- docs/src/step_by_step/model_switching.md | 22 +- .../step_by_step/quick_and_dirty_examples.md | 32 +- .../src/step_by_step/simple_model_coupling.md | 22 +- .../tutorials/growing_plant/part1_growth.md | 10 +- .../growing_plant/part2_roots_water.md | 4 +- .../growing_plant/part3_debugging.md | 12 +- docs/src/working_with_data/fitting.md | 6 +- examples/Beer.jl | 14 +- examples/ToyLAIModel.jl | 8 +- examples/ToyLightPartitioningModel.jl | 2 +- ...ene_example.jl => maespa_model_example.jl} | 66 +- examples/plantbiophysics_subsample/FvCB.jl | 2 +- .../plantbiophysics_subsample/Monteith.jl | 4 +- examples/plantbiophysics_subsample/Tuzet.jl | 2 +- ext/PlantSimEngineGraphEditorExt.jl | 248 ++-- frontend/dist/.vite/manifest.json | 4 +- ...{index-Do5n-jvW.css => index-DXj3JKAS.css} | 2 +- .../{index-D625qdlf.js => index-PMl5JtnR.js} | 38 +- frontend/dist/index.html | 4 +- frontend/e2e/graph-editor.spec.ts | 18 +- frontend/src/App.test.ts | 6 +- frontend/src/App.tsx | 136 +- frontend/src/DependencyEdge.tsx | 6 +- frontend/src/ErrorBoundary.tsx | 2 +- frontend/src/layout.ts | 4 +- .../{sampleGraph.ts => sampleModelGraph.ts} | 10 +- frontend/src/styles.css | 88 +- frontend/src/types.ts | 20 +- skills/plantsimengine/SKILL.md | 76 +- src/ModelSpec.jl | 16 +- src/PlantSimEngine.jl | 74 +- .../compilation.jl | 543 +++---- .../environment_bindings.jl | 110 +- .../registry_topology.jl | 429 +++--- .../runtime_outputs.jl | 650 ++++----- .../scenario_dsl.jl | 0 .../selectors.jl | 156 +- src/composite_model_api.jl | 9 + src/evaluation/fit.jl | 6 +- src/examples_import.jl | 2 +- src/processes/models_inputs_outputs.jl | 14 +- src/scene_object_api.jl | 9 - src/time/runtime/clocks.jl | 4 +- src/time/runtime/environment_backends.jl | 34 +- src/time/runtime/output_export.jl | 2 +- src/variables_wrappers.jl | 2 +- ...ditor_api.jl => model_graph_editor_api.jl} | 592 ++++---- ...cene_graph_view.jl => model_graph_view.jl} | 610 ++++---- test/runtests.jl | 64 +- test/test-fitting.jl | 6 +- ...xample.jl => test-maespa-model-example.jl} | 42 +- test/test-meteo-traits.jl | 4 +- ...ion.jl => test-model-api-stabilization.jl} | 187 ++- ...nce.jl => test-model-binding-inference.jl} | 10 +- ....jl => test-model-configuration-errors.jl} | 10 +- test/test-model-contract.jl | 4 +- ...l => test-model-graph-editor-extension.jl} | 82 +- ...graph-view.jl => test-model-graph-view.jl} | 436 +++--- ...hard-calls.jl => test-model-hard-calls.jl} | 26 +- ...mpling.jl => test-model-meteo-sampling.jl} | 14 +- ...jl => test-model-multirate-integration.jl} | 12 +- ...rity.jl => test-model-numerical-parity.jl} | 28 +- ...ies.jl => test-model-output-boundaries.jl} | 10 +- ...matrix.jl => test-model-runtime-matrix.jl} | 14 +- ...jl => test-model-status-initialization.jl} | 14 +- ...ers.jl => test-model-temporal-reducers.jl} | 16 +- ...ation.jl => test-model-time-validation.jl} | 18 +- test/test-toy_models.jl | 14 +- ...pi.jl => test-unified-model-object-api.jl} | 1290 ++++++++--------- test/test-updates.jl | 14 +- 111 files changed, 4027 insertions(+), 3940 deletions(-) rename docs/src/{scene_object => composite_model}/quickstart.md (76%) rename docs/src/dev/{unified_scene_object_completion_audit.md => composite_model_completion_audit.md} (64%) rename docs/src/dev/{unified_scene_object_design.md => composite_model_design.md} (93%) rename docs/src/dev/{unified_scene_object_implementation_plan.md => composite_model_implementation_plan.md} (81%) rename docs/src/dev/{maespa_scene_handoff.md => maespa_model_handoff.md} (79%) rename docs/src/{migration_scene_object.md => migration_composite_model.md} (84%) rename examples/{maespa_scene_example.jl => maespa_model_example.jl} (90%) rename frontend/dist/assets/{index-Do5n-jvW.css => index-DXj3JKAS.css} (96%) rename frontend/dist/assets/{index-D625qdlf.js => index-PMl5JtnR.js} (82%) rename frontend/src/{sampleGraph.ts => sampleModelGraph.ts} (93%) rename src/{scene_object => composite_model}/compilation.jl (80%) rename src/{scene_object => composite_model}/environment_bindings.jl (81%) rename src/{scene_object => composite_model}/registry_topology.jl (70%) rename src/{scene_object => composite_model}/runtime_outputs.jl (73%) rename src/{scene_object => composite_model}/scenario_dsl.jl (100%) rename src/{scene_object => composite_model}/selectors.jl (87%) create mode 100644 src/composite_model_api.jl delete mode 100644 src/scene_object_api.jl rename src/visualization/{scene_graph_editor_api.jl => model_graph_editor_api.jl} (50%) rename src/visualization/{scene_graph_view.jl => model_graph_view.jl} (67%) rename test/{test-maespa-scene-example.jl => test-maespa-model-example.jl} (86%) rename test/{test-scene-api-stabilization.jl => test-model-api-stabilization.jl} (71%) rename test/{test-scene-binding-inference.jl => test-model-binding-inference.jl} (94%) rename test/{test-scene-configuration-errors.jl => test-model-configuration-errors.jl} (93%) rename test/{test-scene-graph-editor-extension.jl => test-model-graph-editor-extension.jl} (85%) rename test/{test-scene-graph-view.jl => test-model-graph-view.jl} (51%) rename test/{test-scene-hard-calls.jl => test-model-hard-calls.jl} (90%) rename test/{test-scene-meteo-sampling.jl => test-model-meteo-sampling.jl} (92%) rename test/{test-scene-multirate-integration.jl => test-model-multirate-integration.jl} (93%) rename test/{test-scene-numerical-parity.jl => test-model-numerical-parity.jl} (93%) rename test/{test-scene-output-boundaries.jl => test-model-output-boundaries.jl} (93%) rename test/{test-scene-runtime-matrix.jl => test-model-runtime-matrix.jl} (89%) rename test/{test-scene-status-initialization.jl => test-model-status-initialization.jl} (84%) rename test/{test-scene-temporal-reducers.jl => test-model-temporal-reducers.jl} (94%) rename test/{test-scene-time-validation.jl => test-model-time-validation.jl} (88%) rename test/{test-unified-scene-object-api.jl => test-unified-model-object-api.jl} (75%) diff --git a/AGENTS.md b/AGENTS.md index 5b60c144f..ef099d92f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,7 +1,7 @@ # PlantSimEngine Agent And Developer Guide -PlantSimEngine composes process models over a unified scene/object registry. -The repository contains one scenario compiler and runtime: the scene/object +PlantSimEngine composes process models over a unified composite-model/object registry. +The repository contains one scenario compiler and runtime: the composite-model/object API. ## Core Model Contract @@ -21,21 +21,21 @@ PlantSimEngine.run!(model, models, status, meteo, constants, extra) ``` `models` is a process-keyed bundle compiled from `Calls(...)`. `extra` is a -`SceneRunContext` and provides `call_target`, `call_targets`, and lifecycle +`RunContext` and provides `call_target`, `call_targets`, and lifecycle access. -## Scene Structure +## CompositeModel Structure -- `Scene` owns a `SceneRegistry`, model applications, instances, and an +- `CompositeModel` owns a `ObjectRegistry`, model applications, instances, and an environment. - `Object` is one runtime entity with stable `ObjectId`, labels, parent, geometry, and `Status`. - Plant architecture is not prescribed. Users choose scales and topology. -- `ObjectTemplate` and `ObjectInstance` reuse the same model definitions across +- `CompositeModelTemplate` and `ObjectInstance` reuse the same model definitions across several plants or objects. - `Override` replaces one application model for selected objects without splitting the logical application. -- `objects_from_mtg` and `Scene(mtg; ...)` adapt MTG topology into the same +- `objects_from_mtg` and `CompositeModel(mtg; ...)` adapt MTG topology into the same registry. ## Model Applications @@ -135,7 +135,7 @@ know its scenario timestep unless the scientific model explicitly requires it. ## Lifecycle - `add_organ!` is the high-level operation for MTG-backed growth. It creates - the node, reuses the scene's MTG status policy, applies initial values, + the node, reuses the model's MTG status policy, applies initial values, attaches the status, and registers the object. - `register_object!`, `remove_object!`, and `reparent_object!` mutate topology. - Use `register_object!` directly only when the caller already owns a fully @@ -147,10 +147,10 @@ know its scenario timestep unless the scientific model explicitly requires it. ## Outputs -- `run!(scene; outputs=:none)` starts a fresh timeline and returns - `SceneSimulation`; use `outputs=:all` or output requests to retain streams. +- `run!(model; outputs=:none)` starts a fresh timeline and returns + `Simulation`; use `outputs=:all` or output requests to retain streams. - `continue!(simulation)` and `step!(simulation)` advance the same timeline. -- `scene_outputs(sim)` exposes retained typed streams. +- `outputs(sim)` exposes retained typed streams. - `OutputRequest` selects retained/resampled outputs. - `collect_outputs(sim)` materializes output rows. - `explain_output_retention(sim)` reports why each stream is retained. @@ -169,18 +169,18 @@ not overwrite each other. ## High-Signal Files -- `src/scene_object_api.jl`: dependency-ordered include boundary for the sole - Scene/Object compiler and runtime. -- `src/scene_object/registry_topology.jl`: objects, registry, templates, +- `src/composite_model_api.jl`: dependency-ordered include boundary for the sole + CompositeModel/Object compiler and runtime. +- `src/composite_model/registry_topology.jl`: objects, registry, templates, instances, overrides, topology, and lifecycle ownership. -- `src/scene_object/selectors.jl`: selector normalization and resolution. -- `src/scene_object/compilation.jl`: applications, carriers, calls, writer +- `src/composite_model/selectors.jl`: selector normalization and resolution. +- `src/composite_model/compilation.jl`: applications, carriers, calls, writer validation, schedules, and structured compilation explanations. -- `src/scene_object/environment_bindings.jl`: global/spatial environment +- `src/composite_model/environment_bindings.jl`: global/spatial environment bindings and invalidation. -- `src/scene_object/runtime_outputs.jl`: execution, temporal streams, hard-call +- `src/composite_model/runtime_outputs.jl`: execution, temporal streams, hard-call publication, retention, and output collection. -- `src/scene_object/scenario_dsl.jl`: small scenario construction helpers. +- `src/composite_model/scenario_dsl.jl`: small scenario construction helpers. - `src/ModelSpec.jl`: model application configuration. - `src/component_models/Status.jl`: reference-based status. - `src/component_models/RefVector.jl`: homogeneous reference vectors. @@ -188,8 +188,8 @@ not overwrite each other. - `src/time/runtime/clocks.jl`: Dates-based timing. - `src/time/runtime/meteo_sampling.jl`: weather sampling. - `src/time/runtime/environment_backends.jl`: environment backend contract. -- `test/test-unified-scene-object-api.jl`: broad integration coverage. -- `test/test-scene-*.jl`: focused Scene/Object behavioral contracts. +- `test/test-unified-model-object-api.jl`: broad integration coverage. +- `test/test-model-*.jl`: focused CompositeModel/Object behavioral contracts. ## Change Checklist diff --git a/README.md b/README.md index 9b97b2021..1f725f13c 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,10 @@ A modeler writes generic kernels with: `meteo_outputs_` traits - `run!(model, models, status, meteo, constants, extra)` -A simulation author assembles those kernels on objects in a scene with: +A simulation author assembles those kernels on objects in a model with: ```julia -Scene +CompositeModel Object ModelSpec AppliesTo @@ -35,7 +35,7 @@ Environment ``` This is the package API for multiscale, multi-plant, soil, microclimate, and -scene-scale simulations. +model-scale simulations. ## Installation @@ -53,7 +53,7 @@ using PlantSimEngine ## Quickstart -This example runs three existing toy models on one scene object: +This example runs three existing toy models on one model object: 1. `ToyDegreeDaysCumulModel` computes daily thermal time. 2. `ToyLAIModel` consumes cumulative thermal time and computes LAI. @@ -68,14 +68,14 @@ meteo_day = read_weather( duration=Dates.Day, ) -scene = Scene( +model = CompositeModel( ToyDegreeDaysCumulModel(), ToyLAIModel(), Beer(0.6); environment=meteo_day, ) -sim = run!(scene; steps=30, outputs=:all) +sim = run!(model; steps=30, outputs=:all) out = collect_outputs(sim; sink=DataFrame) first(out, 6) ``` @@ -86,7 +86,7 @@ declared inputs and outputs: `ToyLAIModel` receives `TT_cu` from ```julia select( - DataFrame(explain_bindings(scene)), + DataFrame(explain_bindings(model)), :application_id, :input, :source_application_ids, @@ -98,11 +98,11 @@ select( ## Multi-Object Coupling Use `Inputs(...)` when a model needs values from selected objects. This -scene-scale LAI model reads live references to the surface of every plant in -the scene: +model-scale LAI model reads live references to the surface of every plant in +the model: ```julia -plant_scene = Scene( +plant_scene = CompositeModel( Object(:scene; scale=:Scene, kind=:scene), Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene, status=Status(surface=12.0)), @@ -122,18 +122,18 @@ plant_scene = Scene( ) run!(plant_scene) -scene_status = only(scene_objects(plant_scene; scale=:Scene)).status +scene_status = only(model_objects(plant_scene; scale=:Scene)).status (total_surface=scene_status.total_surface, LAI=scene_status.LAI) ``` Use `within=Self()` for plant-local aggregations, for example a plant allocation model summing only the leaves inside the current plant. Use -`within=SceneScope()` for scene-wide aggregation. +`within=SceneScope()` for model-wide aggregation. ## Manual Calls Use `Calls(...)` when a parent model must directly run selected child models, -for example a scene energy-balance solver that iterates leaf temperatures: +for example a model energy-balance solver that iterates leaf temperatures: ```julia ModelSpec(SceneEnergyBalance(); name=:scene_energy) |> @@ -175,21 +175,21 @@ iterations, and `run_call!(target; publish=true)` publishes the accepted state. Useful inspection helpers include: ```julia -explain_objects(scene) -explain_scopes(scene) -explain_bindings(scene) -explain_calls(scene) -explain_environment_bindings(scene) -explain_schedule(scene) -explain_execution_plan(scene) +explain_objects(model) +explain_scopes(model) +explain_bindings(model) +explain_calls(model) +explain_environment_bindings(model) +explain_schedule(model) +explain_execution_plan(model) ``` ## Documentation - [Stable documentation](https://VirtualPlantLab.github.io/PlantSimEngine.jl/stable) - [Development documentation](https://VirtualPlantLab.github.io/PlantSimEngine.jl/dev) -- [Scene/object quickstart](https://VirtualPlantLab.github.io/PlantSimEngine.jl/dev/scene_object/quickstart/) -- [Scene/object migration guide](https://VirtualPlantLab.github.io/PlantSimEngine.jl/dev/migration_scene_object/) +- [CompositeModel/object quickstart](https://VirtualPlantLab.github.io/PlantSimEngine.jl/dev/composite_model/quickstart/) +- [CompositeModel/object migration guide](https://VirtualPlantLab.github.io/PlantSimEngine.jl/dev/migration_composite_model/) - [Public API reference](https://VirtualPlantLab.github.io/PlantSimEngine.jl/dev/API/API_public/) ## Projects That Use PlantSimEngine @@ -209,8 +209,8 @@ hundreds of microseconds, and PlantBiophysics.jl models using PlantSimEngine have been measured much faster than equivalent implementations in typical scientific scripting languages. -For performance-sensitive scenes, inspect the compiled representation with -`explain_execution_plan(scene)` to see homogeneous batches and concrete carrier +For performance-sensitive composite models, inspect the compiled representation with +`explain_execution_plan(model)` to see homogeneous batches and concrete carrier types. ## License And Contributions diff --git a/benchmark/benchmarks.jl b/benchmark/benchmarks.jl index eac16a2e1..fe0b84243 100644 --- a/benchmark/benchmarks.jl +++ b/benchmark/benchmarks.jl @@ -22,21 +22,21 @@ SUITE[suite_name] = BenchmarkGroup(["PSE", "PBP", "XPalm"]) # "PSE benchmark" include(joinpath(@__DIR__, "test-PSE-benchmark.jl")) SUITE[suite_name]["PSE"] = @benchmarkable benchmark_heavier_scene( - scene, + model, requests, nsteps, -) setup = ((scene, requests, nsteps) = setup_heavier_scene_benchmark()) +) setup = ((model, requests, nsteps) = setup_heavier_model_benchmark()) include(joinpath(@__DIR__, "test-multirate-buffer-benchmark.jl")) SUITE[suite_name]["PSE_multirate_retain_all_run"] = @benchmarkable benchmark_multirate_retain_all_run( - scene, + model, nsteps, -) setup = ((scene, ignored_requests, nsteps) = setup_multirate_buffer_benchmark()) +) setup = ((model, ignored_requests, nsteps) = setup_multirate_buffer_benchmark()) SUITE[suite_name]["PSE_multirate_output_request_run"] = @benchmarkable benchmark_multirate_output_request_run( - scene, + model, requests, nsteps, -) setup = ((scene, requests, nsteps) = setup_multirate_buffer_benchmark()) +) setup = ((model, requests, nsteps) = setup_multirate_buffer_benchmark()) # "PBP benchmark" include(joinpath(@__DIR__, "test-plantbiophysics.jl")) @@ -50,10 +50,10 @@ include(joinpath(@__DIR__, "test-xpalm.jl")) SUITE[suite_name]["XPalm_setup"] = @benchmarkable xpalm_default_param_create() seconds = 120 SUITE[suite_name]["XPalm_run"] = @benchmarkable xpalm_default_param_run( - scene, + model, requests, nsteps, -) setup = ((scene, requests, nsteps) = xpalm_default_param_create()) +) setup = ((model, requests, nsteps) = xpalm_default_param_create()) #tune!(SUITE) #results = run(SUITE, verbose=true) diff --git a/benchmark/test-PSE-benchmark.jl b/benchmark/test-PSE-benchmark.jl index f9b687232..029037738 100644 --- a/benchmark/test-PSE-benchmark.jl +++ b/benchmark/test-PSE-benchmark.jl @@ -20,7 +20,7 @@ PlantSimEngine.outputs_(m::ToyInternodeCrazyEmergence) = (TT_cu_emergence=0.0,) function PlantSimEngine.run!(m::ToyInternodeCrazyEmergence, models, status, meteo, constants=nothing, extra=nothing) - scene = runtime_scene(extra) + model = runtime_model(extra) #root = get_root(status.node) @@ -30,21 +30,21 @@ function PlantSimEngine.run!(m::ToyInternodeCrazyEmergence, models, status, mete if length(MultiScaleTreeGraph.children(status.node)) == 1 && status.TT_cu - status.TT_cu_emergence >= m.TT_emergence - status_new_internode = add_organ!(status.node, scene, "<", :Internode, 2; index=1, initial_status=(carbon_biomass=1.0, TT_cu_emergence=0.0)) - add_organ!(status_new_internode.node, scene, "+", :Leaf, 2; index=1, initial_status=(carbon_biomass=1.0,)) + status_new_internode = add_organ!(status.node, model, "<", :Internode, 2; index=1, initial_status=(carbon_biomass=1.0, TT_cu_emergence=0.0)) + add_organ!(status_new_internode.node, model, "+", :Leaf, 2; index=1, initial_status=(carbon_biomass=1.0,)) status_new_internode.TT_cu_emergence = status.TT_cu elseif (length(MultiScaleTreeGraph.children(status.node)) >= 2 && length(MultiScaleTreeGraph.children(status.node)) < 7) && status.TT_cu - status.TT_cu_emergence >= m.TT_emergence - status_new_internode = add_organ!(status.node, scene, "<", :Internode, 2; index=1, initial_status=(carbon_biomass=1.0, TT_cu_emergence=0.0)) - add_organ!(status.node, scene, "+", :Leaf, 2; index=4, initial_status=(carbon_biomass=1.0,)) - add_organ!(status.node, scene, "+", :Leaf, 2; index=5, initial_status=(carbon_biomass=1.0,)) + status_new_internode = add_organ!(status.node, model, "<", :Internode, 2; index=1, initial_status=(carbon_biomass=1.0, TT_cu_emergence=0.0)) + add_organ!(status.node, model, "+", :Leaf, 2; index=4, initial_status=(carbon_biomass=1.0,)) + add_organ!(status.node, model, "+", :Leaf, 2; index=5, initial_status=(carbon_biomass=1.0,)) status_new_internode.TT_cu_emergence = status.TT_cu elseif (length(MultiScaleTreeGraph.children(status.node)) >= 7 && length(MultiScaleTreeGraph.children(status.node)) < 30) && status.TT_cu - status.TT_cu_emergence >= m.TT_emergence - add_organ!(status.node, scene, "+", :Leaf, 2; index=6, initial_status=(carbon_biomass=1.0,)) - add_organ!(status.node, scene, "+", :Leaf, 2; index=7, initial_status=(carbon_biomass=1.0,)) - add_organ!(status.node, scene, "+", :Leaf, 2; index=8, initial_status=(carbon_biomass=1.0,)) - add_organ!(status.node, scene, "+", :Leaf, 2; index=9, initial_status=(carbon_biomass=1.0,)) - add_organ!(status.node, scene, "+", :Leaf, 2; index=10, initial_status=(carbon_biomass=1.0,)) - add_organ!(status.node, scene, "+", :Leaf, 2; index=11, initial_status=(carbon_biomass=1.0,)) + add_organ!(status.node, model, "+", :Leaf, 2; index=6, initial_status=(carbon_biomass=1.0,)) + add_organ!(status.node, model, "+", :Leaf, 2; index=7, initial_status=(carbon_biomass=1.0,)) + add_organ!(status.node, model, "+", :Leaf, 2; index=8, initial_status=(carbon_biomass=1.0,)) + add_organ!(status.node, model, "+", :Leaf, 2; index=9, initial_status=(carbon_biomass=1.0,)) + add_organ!(status.node, model, "+", :Leaf, 2; index=10, initial_status=(carbon_biomass=1.0,)) + add_organ!(status.node, model, "+", :Leaf, 2; index=11, initial_status=(carbon_biomass=1.0,)) end @@ -60,7 +60,7 @@ function _benchmark_mtg_status(node) return Status((; data...)) end -function setup_heavier_scene_benchmark() +function setup_heavier_model_benchmark() mtg = import_mtg_example() meteo_day = read_weather(joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"), duration=Day) @@ -104,7 +104,7 @@ function setup_heavier_scene_benchmark() AppliesTo(One(scale=:Soil)), ) - scene = Scene( + model = CompositeModel( mtg; applications=applications, environment=meteo_day, @@ -117,14 +117,14 @@ function setup_heavier_scene_benchmark() OutputRequest(:Plant, :carbon_offer; name=:plant_carbon_offer, application=:plant_allocation), OutputRequest(:Soil, :soil_water_content; name=:soil_water, application=:soil_water), ] - return scene, requests, length(meteo_day) + return model, requests, length(meteo_day) end -function benchmark_heavier_scene(scene, requests, nsteps) - return run!(scene; steps=nsteps, outputs=requests) +function benchmark_heavier_scene(model, requests, nsteps) + return run!(model; steps=nsteps, outputs=requests) end function do_benchmark_on_heavier_mtg() - scene, requests, nsteps = setup_heavier_scene_benchmark() - return benchmark_heavier_scene(scene, requests, nsteps) + model, requests, nsteps = setup_heavier_model_benchmark() + return benchmark_heavier_scene(model, requests, nsteps) end diff --git a/benchmark/test-multirate-buffer-benchmark.jl b/benchmark/test-multirate-buffer-benchmark.jl index 3bc0d1c53..b3e536ab6 100644 --- a/benchmark/test-multirate-buffer-benchmark.jl +++ b/benchmark/test-multirate-buffer-benchmark.jl @@ -71,20 +71,20 @@ function setup_multirate_buffer_benchmark(; nleaves=2000, ndays=30) nsteps = 24 * ndays meteo = [(T=20.0, Wind=1.0, Rh=0.65, duration=Hour(1)) for _ in 1:nsteps] - scene = Scene(objects...; applications=applications, environment=meteo) + model = CompositeModel(objects...; applications=applications, environment=meteo) reqs = [ OutputRequest(:Plant, :Y4; name=:four_hour_total, application=:four_hour_consumer), OutputRequest(:Plant, :Y24; name=:daily_total, application=:daily_consumer), OutputRequest(:Leaf, :X; name=:x_daily_sum, application=:hourly_source, policy=Integrate(), clock=Day(1)), ] - return scene, reqs, nsteps + return model, reqs, nsteps end -function benchmark_multirate_retain_all_run(scene, nsteps) - return run!(scene; steps=nsteps, outputs=:all) +function benchmark_multirate_retain_all_run(model, nsteps) + return run!(model; steps=nsteps, outputs=:all) end -function benchmark_multirate_output_request_run(scene, reqs, nsteps) - return run!(scene; steps=nsteps, outputs=reqs) +function benchmark_multirate_output_request_run(model, reqs, nsteps) + return run!(model; steps=nsteps, outputs=reqs) end diff --git a/benchmark/test-plantbiophysics.jl b/benchmark/test-plantbiophysics.jl index 2c438068a..bbe49f4f4 100644 --- a/benchmark/test-plantbiophysics.jl +++ b/benchmark/test-plantbiophysics.jl @@ -64,8 +64,8 @@ end function benchmark_plantbiophysics_batch(scenes) constants = Constants() - for scene in scenes - run!(scene; constants=constants, outputs=:none) + for model in scenes + run!(model; constants=constants, outputs=:none) end return nothing end diff --git a/benchmark/test-xpalm.jl b/benchmark/test-xpalm.jl index 0ded4f94b..c2bd903d1 100644 --- a/benchmark/test-xpalm.jl +++ b/benchmark/test-xpalm.jl @@ -5,8 +5,8 @@ using Dates using PlantSimEngine using XPalm -function _xpalm_output_requests(scene, vars) - applications = explain_scene_applications(scene) +function _xpalm_output_requests(model, vars) + applications = explain_applications(model) requests = OutputRequest[] for (scale, variables) in pairs(vars) for variable in variables @@ -60,13 +60,13 @@ function xpalm_default_param_create() initiation_age=0, parameters=XPalm.default_parameters(), ) - scene = XPalm.xpalm_scene(palm; environment=meteo) - return scene, _xpalm_output_requests(scene, vars), nrow(meteo) + model = XPalm.xpalm_scene(palm; environment=meteo) + return model, _xpalm_output_requests(model, vars), nrow(meteo) end -function xpalm_default_param_run(scene, requests, nsteps) +function xpalm_default_param_run(model, requests, nsteps) return PlantSimEngine.run!( - scene; + model; steps=nsteps, outputs=requests, ) diff --git a/benchmark/test/runtests.jl b/benchmark/test/runtests.jl index 796f35aec..0a35bf552 100644 --- a/benchmark/test/runtests.jl +++ b/benchmark/test/runtests.jl @@ -10,16 +10,16 @@ using Test @testset "PlantSimEngine benchmark API smoke" begin include(joinpath(@__DIR__, "..", "test-PSE-benchmark.jl")) - scene, requests, _ = setup_heavier_scene_benchmark() - simulation = benchmark_heavier_scene(scene, requests, 1) + model, requests, _ = setup_heavier_model_benchmark() + simulation = benchmark_heavier_scene(model, requests, 1) @test current_step(simulation) == 1 @test !isempty(collect_outputs(simulation; sink=nothing)) end @testset "multirate benchmark API smoke" begin include(joinpath(@__DIR__, "..", "test-multirate-buffer-benchmark.jl")) - scene, requests, nsteps = setup_multirate_buffer_benchmark(; ndays=1, nleaves=4) - simulation = benchmark_multirate_output_request_run(scene, requests, nsteps) + model, requests, nsteps = setup_multirate_buffer_benchmark(; ndays=1, nleaves=4) + simulation = benchmark_multirate_output_request_run(model, requests, nsteps) @test current_step(simulation) == nsteps @test !isempty(collect_outputs(simulation; sink=nothing)) end @@ -32,8 +32,8 @@ end @testset "XPalm benchmark API smoke" begin include(joinpath(@__DIR__, "..", "test-xpalm.jl")) - scene, requests, _ = xpalm_default_param_create() - simulation = PlantSimEngine.run!(scene; steps=1, outputs=requests) + model, requests, _ = xpalm_default_param_create() + simulation = PlantSimEngine.run!(model; steps=1, outputs=requests) @test current_step(simulation) == 1 @test !isempty(xpalm_default_param_collect_outputs(simulation)) end diff --git a/docs/make.jl b/docs/make.jl index c7be112fc..aab2c7aee 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -7,10 +7,10 @@ using Documenter using CairoMakie using PlantSimEngine.Examples -function build_scene_graph_example() +function build_model_graph_example() output_dir = joinpath(@__DIR__, "src", "assets") mkpath(output_dir) - scene = Scene( + model = PlantSimEngine.CompositeModel( ToyDegreeDaysCumulModel(), ToyLAIModel(), Beer(0.6); @@ -19,13 +19,13 @@ function build_scene_graph_example() scale=:Plant, kind=:plant, ) - write_scene_graph_view( - joinpath(output_dir, "scene_graph_example.html"), - scene, + write_model_graph_view( + joinpath(output_dir, "model_graph_example.html"), + model, ) end -build_scene_graph_example() +build_model_graph_example() DocMeta.setdocmeta!(PlantSimEngine, :DocTestSetup, :(using PlantSimEngine, PlantMeteo, DataFrames, CSV, CairoMakie); recursive=true) @@ -52,22 +52,22 @@ makedocs(; "Julia language basics" => "./prerequisites/julia_basics.md", ], "Getting Started" => [ - "Quickstart" => "./scene_object/quickstart.md", + "Quickstart" => "./composite_model/quickstart.md", "Detailed first simulation" => "./step_by_step/detailed_first_example.md", "Port an existing model" => "./guides/modelers/port_existing_model.md", - "Migrating from mappings" => "migration_scene_object.md", + "Migrating from mappings" => "migration_composite_model.md", ], "Building Scenarios" => [ "Coupling models" => "./guides/coupling.md", - "Visualize and edit a Scene" => "./guides/graph_visualizer_editor.md", + "Visualize and edit a composite model" => "./guides/graph_visualizer_editor.md", "Model Switching" => "./step_by_step/model_switching.md", "Implementing a process" => "./step_by_step/implement_a_process.md", "Implementing a model" => "./step_by_step/implement_a_model.md", "Advanced coupling and hard dependencies" => "./step_by_step/advanced_coupling.md", "Implementing a model : additional notes" => "./step_by_step/implement_a_model_additional.md", ], - "Multiscale Scenes" => [ - "How scenes execute" => "./guides/multiscale/concepts.md", + "Multiscale Composite Models" => [ + "How composite models execute" => "./guides/multiscale/concepts.md", "From one object" => "./guides/multiscale/from_one_object.md", "Value coupling" => "./guides/multiscale/value_coupling.md", "Importing an MTG" => "./guides/multiscale/import_mtg.md", @@ -109,10 +109,10 @@ makedocs(; "Development designs" => [ "Public API refinement decisions" => "./dev/public_api_refinement_decisions.md", "Public API refinement completion audit" => "./dev/public_api_refinement_completion_audit.md", - "Unified scene/object design" => "./dev/unified_scene_object_design.md", - "Unified scene/object implementation plan" => "./dev/unified_scene_object_implementation_plan.md", - "Unified scene/object completion audit" => "./dev/unified_scene_object_completion_audit.md", - "MAESPA-style scene example handoff" => "./dev/maespa_scene_handoff.md", + "Composite model/object design" => "./dev/composite_model_design.md", + "Composite model/object implementation plan" => "./dev/composite_model_implementation_plan.md", + "Composite model/object completion audit" => "./dev/composite_model_completion_audit.md", + "MAESPA-style composite-model example handoff" => "./dev/maespa_model_handoff.md", "Code cleanup audit" => "./dev/code_cleanup_audit.md", "Release notes handoff" => "./dev/release_notes_handoff.md", ], diff --git a/docs/src/API/API_public.md b/docs/src/API/API_public.md index ba17573b5..904f78fd8 100644 --- a/docs/src/API/API_public.md +++ b/docs/src/API/API_public.md @@ -1,14 +1,14 @@ # Public API -## Unified Scene/Object API +## Unified CompositeModel/Object API ### Scenario and model applications -- `Scene` stores objects, model applications, instances, and environment. -- `Scene(model, models...; status=..., timestep=...)` is the concise one-object +- `CompositeModel` stores objects, model applications, instances, and environment. +- `CompositeModel(model, models...; status=..., timestep=...)` is the concise one-object form and lowers to the same object/application representation. - `Object` represents one runtime entity with stable identity and status. -- `ObjectTemplate` and `ObjectInstance` reuse a model across instances. +- `CompositeModelTemplate` and `ObjectInstance` reuse a model across instances. - `ModelSpec(model; name=...)` identifies one model application. - `AppliesTo(...)` selects its target objects. @@ -41,18 +41,18 @@ ### Lifecycle -- `objects_from_mtg` and `Scene(mtg; ...)` adapt an MTG into the object +- `objects_from_mtg` and `CompositeModel(mtg; ...)` adapt an MTG into the object registry. -- `add_organ!` creates and initializes a new organ in an MTG-backed scene. -- `runtime_scene(extra)` gives lifecycle-capable kernels sanctioned access to - the live scene from their `SceneRunContext`. +- `add_organ!` creates and initializes a new organ in an MTG-backed model. +- `runtime_model(extra)` gives lifecycle-capable kernels sanctioned access to + the live model from their `RunContext`. - `register_object!`, `remove_object!`, and `reparent_object!` change topology. - `move_object!` and `update_geometry!` change spatial state. - Supported lifecycle operations automatically invalidate and refresh the affected structural or spatial bindings before the next timestep. -- `run!(scene; steps=..., outputs=:none)` starts a fresh result timeline and - returns a `SceneSimulation`. +- `run!(model; steps=..., outputs=:none)` starts a fresh result timeline and + returns a `Simulation`. - `continue!(simulation; steps=...)` and `step!(simulation)` advance an existing timeline without resetting temporal state. - `current_step(simulation)` reports the accepted timeline position. @@ -65,7 +65,7 @@ Use structured explanation helpers instead of inspecting internals: - `explain_objects` - `explain_instances` - `explain_scopes` -- `explain_scene_applications` +- `explain_applications` - `explain_bindings` - `explain_calls` - `explain_environment_bindings` @@ -77,21 +77,21 @@ Use structured explanation helpers instead of inspecting internals: - `explain_outputs` - `explain_initialization` -See [Migrating To The Scene/Object API](../migration_scene_object.md) for +See [Migrating To The CompositeModel/Object API](../migration_composite_model.md) for translations from removed APIs. -### Scene graph visualization and editing +### CompositeModel graph visualization and editing -- `compile_scene_report(scene; strict=false)` preserves partial graph state and - structured diagnostics for incomplete or cyclic scenes. -- `scene_graph_view(scene; level=:applications)` returns the typed graph view. -- `scene_graph_view_json(scene)` serializes the same DTO used by the browser. -- `write_scene_graph_view(path, scene)` writes a self-contained static viewer. -- `edit_graph(scene)` starts the optional HTTP editor after `using HTTP`. -- `current_scene(session)`, `undo!(session)`, `redo!(session)`, and +- `compile_model_report(model; strict=false)` preserves partial graph state and + structured diagnostics for incomplete or cyclic composite models. +- `model_graph_view(model; level=:applications)` returns the typed graph view. +- `model_graph_view_json(model)` serializes the same DTO used by the browser. +- `write_model_graph_view(path, model)` writes a self-contained static viewer. +- `edit_graph(model)` starts the optional HTTP editor after `using HTTP`. +- `current_model(session)`, `undo!(session)`, `redo!(session)`, and `close(session)` control an interactive session from Julia. -See [Visualize And Edit A Scene](../guides/graph_visualizer_editor.md) for the +See [Visualize And Edit A CompositeModel](../guides/graph_visualizer_editor.md) for the runnable workflow, model discovery, selector previews, cycle breaking, and Documenter embedding. @@ -105,10 +105,10 @@ Compiler representations, cache refresh operations, and low-level binding compilers live under `PlantSimEngine.Advanced`. They are intended for package integration, diagnostics development, and compiler work rather than ordinary scenario composition. Prefer the public `explain_*` functions, which accept a -`Scene` directly, over manually compiling and inspecting fields. +`CompositeModel` directly, over manually compiling and inspecting fields. -Examples include `Advanced.compile_scene`, `Advanced.refresh_bindings!`, and -the `Advanced.CompiledScene` family. These qualified APIs may evolve more +Examples include `Advanced.compile_composite_model`, `Advanced.refresh_bindings!`, and +the `Advanced.CompiledCompositeModel` family. These qualified APIs may evolve more quickly than the default modeling interface. ## Index diff --git a/docs/src/API/public_symbols.md b/docs/src/API/public_symbols.md index 1f2d3842c..9f3f477e7 100644 --- a/docs/src/API/public_symbols.md +++ b/docs/src/API/public_symbols.md @@ -6,7 +6,7 @@ and cache controls are intentionally listed separately under ## Scenario composition -- Scene structure: `Scene`, `Object`, `ObjectId`, `ObjectTemplate`, +- CompositeModel structure: `CompositeModel`, `Object`, `ObjectId`, `CompositeModelTemplate`, `ObjectInstance`, `Override`. - Applications: `ModelSpec`, `AppliesTo`, `Inputs`, `Calls`, `TimeStep`, `Environment`, `Updates`, `OutputRouting`. @@ -21,26 +21,26 @@ and cache controls are intentionally listed separately under `Scope`, `Relation`. - Labels: `Kind`, `Species`, `Scale`. - Normalized addresses: `ObjectAddress`, `object_address`. -- Queries: `object_ids`, `scene_objects`, `resolve_object_ids`, +- Queries: `object_ids`, `model_objects`, `resolve_object_ids`, `resolve_objects`. - Object data: `geometry`, `position`, `bounds`. ## Execution, lifecycle, and outputs -- Execution: `run!`, `continue!`, `step!`, `SceneSimulation`, `current_step`, - `runtime_scene`. -- Output selection and collection: `OutputRequest`, `scene_outputs`, +- Execution: `run!`, `continue!`, `step!`, `Simulation`, `current_step`, + `runtime_model`. +- Output selection and collection: `OutputRequest`, `outputs`, `collect_outputs`. - Lifecycle: `register_object!`, `add_organ!`, `remove_object!`, `reparent_object!`, `move_object!`, `update_geometry!`, `mark_environment_binding_dirty!`, `objects_from_mtg`. -- Hard calls: `SceneRunContext`, `SceneCallTarget`, `call_target`, +- Hard calls: `RunContext`, `CallTarget`, `call_target`, `call_targets`, `run_call!`. ## Structured explanations - Structure: `explain_objects`, `explain_instances`, `explain_scopes`. -- Compilation: `explain_scene_applications`, `explain_bindings`, +- Compilation: `explain_applications`, `explain_bindings`, `explain_calls`, `explain_model_bundles`, `explain_writers`, `explain_schedule`, `explain_execution_plan`. - Initialization, environment, and outputs: `explain_initialization`, @@ -89,17 +89,17 @@ user functions. `PlantSimEngine.Advanced` contains the qualified compiler and cache API: -- registries and compiled representations: `SceneRegistry`, `CompiledScene`, - `CompiledSceneApplication`, `CompiledSceneInputBinding`, - `CompiledSceneCallBinding`, `CompiledEnvironmentBinding`, +- registries and compiled representations: `ObjectRegistry`, `CompiledCompositeModel`, + `CompiledModelApplication`, `CompiledModelInputBinding`, + `CompiledModelCallBinding`, `CompiledEnvironmentBinding`, `CompiledEnvironmentBindings`; - carrier and adapter implementation types: `ObjectRefVector`, `TimeStepTable`; -- compiler and cache operations: `compile_scene`, `refresh_bindings!`, +- compiler and cache operations: `compile_composite_model`, `refresh_bindings!`, `refresh_environment_bindings!`, `compile_environment_bindings`, `bind_environment`; - cache diagnostics: `bindings_dirty`, `environment_bindings_dirty`, - `scene_revision`, `environment_revision`, `compiled_bindings`, + `model_revision`, `environment_revision`, `compiled_bindings`, `compiled_environment_bindings`. These names require explicit qualification or `using PlantSimEngine.Advanced`. diff --git a/docs/src/agent_skill.md b/docs/src/agent_skill.md index 67fdb97d8..e37a6e22c 100644 --- a/docs/src/agent_skill.md +++ b/docs/src/agent_skill.md @@ -12,7 +12,7 @@ Users can download the `skills/plantsimengine` folder and tell their agent to use the `plantsimengine` skill when working with PlantSimEngine.jl. The skill gives agents the package-specific conventions they need for: -- building object graphs with `Scene`, `Object`, `ObjectTemplate`, and +- building object graphs with `CompositeModel`, `Object`, `CompositeModelTemplate`, and `ObjectInstance`; - applying models with `ModelSpec` and `AppliesTo`; - coupling values and manual model calls with `Inputs` and `Calls`; @@ -24,16 +24,16 @@ gives agents the package-specific conventions they need for: - inspecting compiled scenarios with structured explanation helpers; - reporting supplied, generated, bound, and unresolved variables with `explain_initialization`; -- accessing the live scene from lifecycle-capable kernels with - `runtime_scene(extra)`; +- accessing the live model from lifecycle-capable kernels with + `runtime_model(extra)`; - inspecting homogeneous runtime batches with `explain_execution_plan`; -- collecting raw or requested scene outputs with `scene_outputs`, +- collecting raw or requested model outputs with `outputs`, `OutputRequest`, `collect_outputs`, and `explain_output_retention`; - implementing or wrapping models with `@process`, `inputs_`, `outputs_`, `run!`, hard dependencies, and model traits. The superseded `ModelMapping` and `MultiScaleModel` runtimes have been removed. -Use [Migrating To The Scene/Object API](migration_scene_object.md) when +Use [Migrating To The CompositeModel/Object API](migration_composite_model.md) when translating historical code. The canonical source is [`skills/plantsimengine/SKILL.md`](https://github.com/VirtualPlantLab/PlantSimEngine.jl/blob/main/skills/plantsimengine/SKILL.md). diff --git a/docs/src/scene_object/quickstart.md b/docs/src/composite_model/quickstart.md similarity index 76% rename from docs/src/scene_object/quickstart.md rename to docs/src/composite_model/quickstart.md index 0bfb97e5b..c6b511482 100644 --- a/docs/src/scene_object/quickstart.md +++ b/docs/src/composite_model/quickstart.md @@ -1,12 +1,12 @@ -# Scene/Object Quickstart +# CompositeModel/Object Quickstart -This page is the shortest path to a native scene/object simulation. +This page is the shortest path to a native composite-model/object simulation. Use this API for new multiscale, multi-plant, soil, microclimate, and -scene-scale simulations: +model-scale simulations: ```julia -Scene +CompositeModel Object ModelSpec AppliesTo @@ -17,13 +17,13 @@ TimeStep Environment ``` -Scenarios are defined with `Scene` and model applications. A model is a +Scenarios are defined with `CompositeModel` and model applications. A model is a reusable process implementation; an application is one configured use of that model, including its name, target objects, inputs, cadence, and environment. One application may run on many objects, and the same model may appear in several applications with different parameters or targets. -```@setup scene_object_quickstart +```@setup model_object_quickstart using PlantSimEngine, PlantMeteo, Dates, DataFrames using PlantSimEngine.Examples ``` @@ -31,56 +31,56 @@ using PlantSimEngine.Examples ## One Object, Several Models For models that all run on one object, the concise constructor lowers directly -to the ordinary Scene/Object representation: +to the ordinary CompositeModel/Object representation: ```julia -scene = Scene(ModelA(), ModelB(); status=(initial_value=1.0,)) +model = CompositeModel(ModelA(), ModelB(); status=(initial_value=1.0,)) ``` Use the explicit form later in this guide when applications need names, selectors, or other scenario policies. -The first scene has one object, `:scene`, and three model applications: +The first model has one object, `:scene`, and three model applications: - `ToyDegreeDaysCumulModel` computes daily thermal time; - `ToyLAIModel` consumes cumulative thermal time and computes LAI; - `Beer` consumes LAI and meteorology to compute absorbed PAR. -The model implementations are ordinary PlantSimEngine kernels. The scene +The model implementations are ordinary PlantSimEngine kernels. The model application layer decides where they run. With no explicit `TimeStep`, these applications use the environment cadence. -```@example scene_object_quickstart +```@example model_object_quickstart meteo_day = read_weather( joinpath(pkgdir(PlantSimEngine), "examples/meteo_day.csv"); duration=Dates.Day, ) -scene = Scene( +model = CompositeModel( ToyDegreeDaysCumulModel(), ToyLAIModel(), Beer(0.6); environment=meteo_day, ) -sim = run!(scene; steps=30, outputs=:all) +sim = run!(model; steps=30, outputs=:all) out = collect_outputs(sim; sink=DataFrame) first(out, 6) ``` -The scene object now holds the latest status values: +The model object now holds the latest status values: -```@example scene_object_quickstart -scene_status = only(scene_objects(scene; scale=:Scene)).status +```@example model_object_quickstart +scene_status = only(model_objects(model; scale=:Scene)).status (TT_cu=scene_status.TT_cu, LAI=scene_status.LAI, aPPFD=scene_status.aPPFD) ``` ## Inspect The Compiled Bindings -Before running, `explain_initialization(scene)` classifies each variable as +Before running, `explain_initialization(model)` classifies each variable as `:supplied`, `:generated`, `:producer_bound`, `:environment_bound`, or `:unresolved`. The report remains available when ordinary required values are -missing, so it can be used to finish configuring a scene. +missing, so it can be used to finish configuring a model. The compiler infers unambiguous same-object dependencies from declared model inputs and outputs: @@ -88,9 +88,9 @@ inputs and outputs: - `:LAI_Dynamic` reads `TT_cu` from `:Degreedays`; - `:light_interception` reads `LAI` from `:LAI_Dynamic`. -```@example scene_object_quickstart +```@example model_object_quickstart select( - DataFrame(explain_bindings(scene)), + DataFrame(explain_bindings(model)), :application_id, :input, :source_application_ids, @@ -104,9 +104,9 @@ consumer sees a shared reference rather than a copied value. For runtime performance diagnostics, inspect the execution plan: -```@example scene_object_quickstart +```@example model_object_quickstart select( - DataFrame(explain_execution_plan(scene)), + DataFrame(explain_execution_plan(model)), :application_id, :object_ids, :batch_size, @@ -116,11 +116,11 @@ select( ## Request Outputs -By default, scene runs retain no user output streams. Pass `outputs=:all` to +By default, model runs retain no user output streams. Pass `outputs=:all` to retain every publisher, or pass `OutputRequest` values to retain and materialize selected streams plus those required by temporal inputs. -```@example scene_object_quickstart +```@example model_object_quickstart request = OutputRequest( Many(scale=:Scene), :LAI; @@ -131,7 +131,7 @@ request = OutputRequest( ) requested_sim = run!( - scene; + model; steps=30, outputs=request, ) @@ -141,17 +141,17 @@ collect_outputs(requested_sim, :lai_every_two_days; sink=nothing)[1:4] The retention explanation reports why a stream was kept: -```@example scene_object_quickstart +```@example model_object_quickstart explain_output_retention(requested_sim) ``` ## Many Objects As Inputs Use `Inputs(...)` when a model needs values from selected objects. This -scene-scale LAI model reads live references to the surface of every plant: +model-scale LAI model reads live references to the surface of every plant: -```@example scene_object_quickstart -plant_scene = Scene( +```@example model_object_quickstart +plant_scene = CompositeModel( Object(:scene; scale=:Scene, kind=:scene), Object( :plant_1; @@ -181,13 +181,13 @@ plant_scene = Scene( ) run!(plant_scene) -plant_scene_status = only(scene_objects(plant_scene; scale=:Scene)).status -(total_surface=plant_scene_status.total_surface, LAI=plant_scene_status.LAI) +plant_model_status = only(model_objects(plant_scene; scale=:Scene)).status +(total_surface=plant_model_status.total_surface, LAI=plant_model_status.LAI) ``` The compiled binding shows a `RefVector` carrier: -```@example scene_object_quickstart +```@example model_object_quickstart select( DataFrame(explain_bindings(plant_scene)), :application_id, @@ -199,13 +199,13 @@ select( ``` If the consumer model runs on each plant, use `within=Subtree()` to read only -objects inside the current plant. Use `within=SceneScope()` when a scene model +objects inside the current plant. Use `within=SceneScope()` when a model model must aggregate all matching objects. ## Manual Calls Use `Calls(...)` when a parent model must directly run selected child models. -This is the mechanism for iterative solvers such as scene energy balance: +This is the mechanism for iterative solvers such as model energy balance: ```julia ModelSpec(SceneEnergyBalance(); name=:scene_energy) |> @@ -250,11 +250,11 @@ accepted state should use `publish=true`. ## Next Steps -- [Migrating To The Scene/Object API](../migration_scene_object.md) translates +- [Migrating To The CompositeModel/Object API](../migration_composite_model.md) translates scenarios written with removed APIs. - [Public API](../API/API_public.md) lists constructors, selectors, lifecycle helpers, environment helpers, and explanation helpers. - [Model traits](../model_traits.md) documents `inputs_`, `outputs_`, `dep`, `timespec`, `output_policy`, `meteo_inputs_`, and `meteo_outputs_`. -- [MAESPA-style scene example handoff](../dev/maespa_scene_handoff.md) - records the current multi-plant scene energy-balance acceptance example. +- [MAESPA-style model example handoff](../dev/maespa_model_handoff.md) + records the current multi-plant model energy-balance acceptance example. diff --git a/docs/src/dev/code_cleanup_audit.md b/docs/src/dev/code_cleanup_audit.md index b6ddda633..d803b9a5e 100644 --- a/docs/src/dev/code_cleanup_audit.md +++ b/docs/src/dev/code_cleanup_audit.md @@ -4,7 +4,7 @@ The compatibility cleanup is implemented on the `multi-plants` branch. -The package now has one scenario compiler and runtime: the scene/object API. +The package now has one scenario compiler and runtime: the composite-model/object API. The following superseded implementations were removed rather than deprecated: - `ModelList` and `SingleScaleModelSet`; @@ -12,26 +12,26 @@ The following superseded implementations were removed rather than deprecated: - `DependencyGraph`, `HardDependencyNode`, and `SoftDependencyNode`; - `GraphSimulation` and the MTG mapping runner; - mapping-specific multirate input resolution and output export; -- the unreleased domain prototype that preceded the scene/object API, +- the unreleased domain prototype that preceded the composite-model/object API, including `Domain`, `SimulationMapping`, `Route`, `AllDomains`, `HardDomains`, and its separate scheduler, runtime, environment bridge, and output publisher; - mapping-only initialization, dataframe, dimension, and topology helpers; - unused parallel-executor traits after removal of the executor runtime; - dead `UninitializedVar`, `RefVariable`, `TreeAlike`, and `StatusView` types; -- the unreleased `ObjectTemplate(...; mapping=...)` alias and legacy +- the unreleased `CompositeModelTemplate(...; mapping=...)` alias and legacy selector-to-mapping conversion helpers; - compatibility tests, tutorials, and executable examples. Migration details are retained in -[`migration_scene_object.md`](../migration_scene_object.md) and +[`migration_composite_model.md`](../migration_composite_model.md) and [`release_notes_handoff.md`](release_notes_handoff.md). ## Current Ownership | Concern | Owner | | --- | --- | -| Object registry, selectors, compilation, execution, lifecycle | `src/scene_object_api.jl` | +| Object registry, selectors, compilation, execution, lifecycle | `src/composite_model_api.jl` | | Model application configuration | `src/ModelSpec.jl` | | Status and reference vectors | `src/component_models/Status.jl`, `src/component_models/RefVector.jl` | | Dates-based clocks and policies | `src/time/multirate.jl`, `src/time/runtime/clocks.jl` | @@ -43,7 +43,7 @@ Migration details are retained in Future cleanup should reject: -- a second scenario/runtime abstraction parallel to `Scene`; +- a second scenario/runtime abstraction parallel to `CompositeModel`; - compatibility wrappers for unreleased APIs; - package-specific behavior in PlantSimEngine; - model kernels that know their scenario object, timestep, or coupling unless @@ -56,12 +56,12 @@ Future cleanup should reject: Current cleanup evidence: -- the `src` tree contains only the scene/object runtime and supporting status, +- the `src` tree contains only the composite-model/object runtime and supporting status, time, environment, fitting, trait, and example files; - empty directories left by removed subsystems were deleted; - `git diff --check` passes; - PlantSimEngine precompiles and loads from a clean Kaimon session; -- `test/test-unified-scene-object-api.jl` passes 576 tests; +- `test/test-unified-model-object-api.jl` passes 576 tests; - the complete package environment passes 885 tests, including Aqua and doctests; - `test/test-fitting.jl` passes; @@ -72,7 +72,7 @@ Current cleanup evidence: agent skill; - remaining `ModelMapping` and `MultiScaleModel` references outside development notes are migration text that explicitly points historical code - to the scene/object API. + to the composite-model/object API. - repository search finds no `Domain`, `SimulationMapping`, `Route`, `AllDomains`, or `HardDomains` implementations, exports, tests, examples, or public documentation. The only remaining references are development/release @@ -90,8 +90,8 @@ Current cleanup evidence: Downstream verification: - PlantBiophysics passes 117/117 tests against this working tree. -- XPalm's uncommitted Scene/Object migration executes 74/75 assertions. The +- XPalm's uncommitted CompositeModel/Object migration executes 74/75 assertions. The remaining assertion retains the removed runtime's first-step LAI value, while the explicit current dependency order produces zero from the initial - zero-biomass leaf before plant/scene aggregation. PlantSimEngine intentionally + zero-biomass leaf before plant/model aggregation. PlantSimEngine intentionally contains no package-specific compatibility workaround for that stale fixture. diff --git a/docs/src/dev/unified_scene_object_completion_audit.md b/docs/src/dev/composite_model_completion_audit.md similarity index 64% rename from docs/src/dev/unified_scene_object_completion_audit.md rename to docs/src/dev/composite_model_completion_audit.md index 38948cf9f..fb3adb7b1 100644 --- a/docs/src/dev/unified_scene_object_completion_audit.md +++ b/docs/src/dev/composite_model_completion_audit.md @@ -1,25 +1,25 @@ -# Unified Scene/Object Completion Audit +# Unified CompositeModel/Object Completion Audit -This audit records the evidence used to assess the breaking scene/object -redesign against `unified_scene_object_design.md` and -`unified_scene_object_implementation_plan.md`. +This audit records the evidence used to assess the breaking composite-model/object +redesign against `composite_model_design.md` and +`composite_model_implementation_plan.md`. Audit date: June 12, 2026. ## Result -The unified scene/object redesign is implemented as the primary public +The unified composite-model/object redesign is implemented as the primary public configuration API. The superseded mapping implementation and the unreleased intermediate -prototype were removed after the scene/object runtime replaced them. +prototype were removed after the composite-model/object runtime replaced them. ## Public Contract The exported scenario vocabulary centers on: ```julia -Scene +CompositeModel Object ModelSpec AppliesTo @@ -37,38 +37,38 @@ Evidence: - `src/PlantSimEngine.jl` - `docs/src/API/API_public.md` -- `docs/src/migration_scene_object.md` +- `docs/src/migration_composite_model.md` - `README.md` ## Requirement Evidence | Requirement | Evidence | | --- | --- | -| One scene object registry without prescribing plant topology | `Scene`, `Object`, selectors, relations, scopes, and `objects_from_mtg` in `src/scene_object_api.jl`; selector and MTG adapter tests in `test/test-unified-scene-object-api.jl` | -| Reusable species models and repeated plant instances | `ObjectTemplate`, `ObjectInstance`, `Override`, shared model ownership, and homogeneous/heterogeneous batches; four-instance and override tests | +| One model object registry without prescribing plant topology | `CompositeModel`, `Object`, selectors, relations, scopes, and `objects_from_mtg` in `src/composite_model_api.jl`; selector and MTG adapter tests in `test/test-unified-model-object-api.jl` | +| Reusable species models and repeated plant instances | `CompositeModelTemplate`, `ObjectInstance`, `Override`, shared model ownership, and homogeneous/heterogeneous batches; four-instance and override tests | | Soft/value dependencies | Compiled `Inputs(...)` bindings, same-object inference, scalar references, `RefVector`, `Advanced.ObjectRefVector`, temporal carriers, renaming, and optional inputs | -| Manual hard dependencies | Compiled `Calls(...)`, `SceneCallTarget`, `call_target(s)`, and `run_call!`; trial calls default to `publish=false` and accepted calls publish explicitly | +| Manual hard dependencies | Compiled `Calls(...)`, `CallTarget`, `call_target(s)`, and `run_call!`; trial calls default to `publish=false` and accepted calls publish explicitly | | Model-author dependency defaults | `Input(...)` and `Call(...)` entries from `dep(model)`, with `ModelSpec` overrides and binding provenance in explanations | | Multirate execution | `TimeStep(Dates.Period)`, model `timespec`, input policies, explicit windows, `PreviousTimeStep`, and stable compiled scheduling | -| Generic value types | Reference and temporal tests using `SceneObjectDualLike{BigFloat}` and `BigFloat` interpolation/integration without `Float64` conversion | +| Generic value types | Reference and temporal tests using `ModelObjectDualLike{BigFloat}` and `BigFloat` interpolation/integration without `Float64` conversion | | Reference semantics and low-copy execution | Shared scalar references, typed many-object carriers, preinstalled status bindings, zero-allocation materialization tests, and a zero-allocation warmed 128-object execution batch | | Duplicate writers | Canonical writer validation and `Updates(:variable; after=...)`, including pruning-after-allocation tests | | Growth, pruning, reparenting, and movement | Central lifecycle APIs, structural/environment cache invalidation, dynamic target/carrier rebuilding, removed-object output history, and object-scoped geometry refresh tests | | Automatic meteorology and microclimate | `meteo_inputs_`, `meteo_outputs_`, global and spatial backends, ancestor geometry fallback, source remapping, model hints, tabular aggregation, cached bindings, and mutable backend scattering | | Structured agent explanations | Object, instance, scope, application, binding, call, model-bundle, environment, schedule, writer, execution-plan, output, and retention explanations returning structured rows | | Initialization workflow | `explain_initialization` classifies supplied, generated, producer-bound, environment-bound, and unresolved variables without failing solely on unresolved values | -| Runtime scene access | `runtime_scene` is the public accessor used by lifecycle-capable kernels and accepts `Scene`, `SceneRunContext`, and `SceneSimulation` | -| One-object ergonomics | `Scene(model, models...; status=...)` lowers to the same object/application compiler and runtime as explicit construction | +| Runtime model access | `runtime_model` is the public accessor used by lifecycle-capable kernels and accepts `CompositeModel`, `RunContext`, and `Simulation` | +| One-object ergonomics | `CompositeModel(model, models...; status=...)` lowers to the same object/application compiler and runtime as explicit construction | | Output ownership and retention | Application-qualified streams, `OutputRouting`, `OutputRequest(application=...)`, dynamic-object exports, and bounded policy-specific dependency histories | -| MAESPA acceptance case | `build_maespa_scene` and `run_maespa_example` use `ObjectTemplate`, `ObjectInstance`, `AppliesTo`, `Inputs`, `Calls`, and `TimeStep`; verified by `test/test-maespa-scene-example.jl` | -| Documentation and migration | Scene/object-first README, home page, quickstart, execution guide, public API, migration guide, and explicitly labeled legacy reference sections | +| MAESPA acceptance case | `build_maespa_scene` and `run_maespa_example` use `CompositeModelTemplate`, `ObjectInstance`, `AppliesTo`, `Inputs`, `Calls`, and `TimeStep`; verified by `test/test-maespa-model-example.jl` | +| Documentation and migration | CompositeModel/object-first README, home page, quickstart, execution guide, public API, migration guide, and explicitly labeled legacy reference sections | ## Verification The following gates passed from a clean, controllable Kaimon Julia session: ```text -test/test-unified-scene-object-api.jl 576 passed +test/test-unified-model-object-api.jl 576 passed test/runtests.jl 885 passed docs/make.jl passed PlantBiophysics/test/runtests.jl 117 passed @@ -80,10 +80,10 @@ broad unified runtime regression, examples, fitting, and doctests. The documentation build completed its executable examples, doctests, cross-references, document checks, and HTML rendering successfully. -The current uncommitted XPalm Scene/Object migration loads this PlantSimEngine +The current uncommitted XPalm CompositeModel/Object migration loads this PlantSimEngine worktree and executes 74 of 75 downstream assertions. Its remaining assertion expects the removed runtime's first-step LAI (`0.000272`); the current compiled -dependency order correctly runs leaf area before plant and scene aggregation, +dependency order correctly runs leaf area before plant and model aggregation, so the initial zero-biomass leaf produces `0.0`. This is a downstream fixture expectation in a dirty migration worktree, not a package-specific behavior to restore in PlantSimEngine. No XPalm workaround was added here. @@ -91,7 +91,7 @@ restore in PlantSimEngine. No XPalm workaround was added here. ## Compatibility Boundary Historical mapping source and tests were removed. Migration guidance remains -in the documentation for downstream packages moving to the scene/object API. +in the documentation for downstream packages moving to the composite-model/object API. The branch-only intermediate prototype was also removed because it was never released and has no compatibility boundary. diff --git a/docs/src/dev/unified_scene_object_design.md b/docs/src/dev/composite_model_design.md similarity index 93% rename from docs/src/dev/unified_scene_object_design.md rename to docs/src/dev/composite_model_design.md index 05e7c583c..1516f2f8c 100644 --- a/docs/src/dev/unified_scene_object_design.md +++ b/docs/src/dev/composite_model_design.md @@ -1,11 +1,11 @@ -# Unified Scene/Object Design +# Unified CompositeModel/Object Design -This page records the target breaking design for one scene/object +This page records the target breaking design for one composite-model/object configuration and runtime API. The central idea is: -> Structural groupings and scales are selections over objects in one scene. +> Structural groupings and scales are selections over objects in one model. The engine should expose one way to say "this model input comes from these objects" and one way to say "this model must manually call these models". The @@ -15,7 +15,7 @@ temporal stream, materialized value, or callable model handle. The public API should be simple enough to remember as: ```julia -Scene +CompositeModel Object ModelSpec @@ -32,9 +32,9 @@ author, or an internal compiled carrier. ## Core Concepts -### Scene +### CompositeModel -A `Scene` is the whole simulation universe. It contains: +A `CompositeModel` is the whole simulation universe. It contains: - simulated objects; - model applications; @@ -43,7 +43,7 @@ A `Scene` is the whole simulation universe. It contains: - caches for object selections and environment bindings. Plants, soil, atmosphere, microclimate grids, organs, sensors, and artificial -objects all live in the same scene-level object graph. +objects all live in the same model-level object graph. ### Object @@ -63,7 +63,7 @@ The engine must not prescribe a plant architecture. A plant can be described as identity, labels, and relations. Existing `MultiScaleTreeGraph.Node` topologies enter the same registry through -`objects_from_mtg(root; ...)` or `Scene(root; ...)`. The adapter traverses once +`objects_from_mtg(root; ...)` or `CompositeModel(root; ...)`. The adapter traverses once and accepts accessors for ids, labels, status, and geometry; the timestep runtime does not query the MTG topology. @@ -105,10 +105,10 @@ must access siblings or state inside the containing plant. Reusable plant models should use scope-relative queries. If an allocation model is applied to each `:Plant`, `Many(scale=:Leaf, within=Subtree())` means -"the leaves inside this plant", not all leaves in the scene. The same query +"the leaves inside this plant", not all leaves in the model. The same query applied to an axis-scale model means "the leaves inside this axis". -Scene-level models widen the scope explicitly with `within=SceneScope()`. +CompositeModel-level models widen the scope explicitly with `within=SceneScope()`. Topology-relative selections use `Relation(...)`: @@ -129,19 +129,19 @@ results are normalized to stable object-id order before bindings are compiled. ### Object Template And Instance An object template is a reusable model/parameter bundle, for example one oil -palm species model. An object instance is one concrete object in the scene. +palm species model. An object instance is one concrete object in the model. The same template can be mounted several times: ```julia -oil_palm = ObjectTemplate( +oil_palm = CompositeModelTemplate( kind=:plant, species=:oil_palm, applications=oil_palm_applications, parameters=oil_palm_parameters, ) -scene = Scene( +model = CompositeModel( ObjectInstance(:palm_1, oil_palm; root=node1), ObjectInstance(:palm_2, oil_palm; root=node2), ObjectInstance(:palm_3, oil_palm; root=node3), @@ -192,7 +192,7 @@ rules, output routing, and environment binding behavior. The model kernel should not need to know: - the species it will be used with; -- the scene it will be embedded in; +- the model it will be embedded in; - the timestep chosen by the user; - whether its inputs come from local state, another scale, another object, a temporal stream, units, automatic differentiation values, or uncertainty @@ -245,7 +245,7 @@ application to a stable application id. Model authors should still declare `inputs_`, `outputs_`, and `dep`. In the final design, `dep(model)` is the model-level place for default dependency intent. Historical `ModelMapping` declarations are migration inputs to the -Scene/Object runtime, not a second supported path. +CompositeModel/Object runtime, not a second supported path. The rule is: @@ -390,7 +390,7 @@ ModelSpec(CarbonState()) |> `PreviousTimeStep(:x)` removes the producer-to-consumer edge from the current timestep graph and reads the latest source sample at or before the previous -scene timestep. Before a source sample exists, the initialized consumer status +model timestep. Before a source sample exists, the initialized consumer status value for `x` is used. This makes initialization part of the scenario contract instead of silently inventing a zero value. @@ -398,7 +398,7 @@ instead of silently inventing a zero value. Use `Calls(...)` when a model must manually run selected models, typically inside an iterative solver. This is the required public API name and must be -implemented as part of the unified scene/object redesign, not left as a later +implemented as part of the unified composite-model/object redesign, not left as a later rename. ```julia @@ -411,7 +411,7 @@ ModelSpec(SceneEB()) |> Calls(:soil => One(kind=:soil, application=:soil_water)) ``` -Inside `run!`, the scene model receives call handles and calls +Inside `run!`, the model model receives call handles and calls `run_call!(call)` during trial iterations, then `run_call!(call; publish=true)` for the accepted final solution. The default is deliberately `publish=false`: trial calls mutate target status for convergence @@ -490,11 +490,11 @@ the same compiled caches: The public mutation API should make cache invalidation explicit and centralized: ```julia -register_object!(scene, object; parent) -remove_object!(scene, object) -reparent_object!(scene, object, new_parent) -move_object!(scene, object, geometry_or_position) -Advanced.refresh_bindings!(scene) +register_object!(model, object; parent) +remove_object!(model, object) +reparent_object!(model, object, new_parent) +move_object!(model, object, geometry_or_position) +Advanced.refresh_bindings!(model) ``` Spatial environment backends should depend on a small geometry contract, not on @@ -541,7 +541,7 @@ meteo_inputs_(::LeafEnergyModel) = ( ) ``` -The runtime resolves those variables through the scene environment service. +The runtime resolves those variables through the model environment service. Default resolution: @@ -556,7 +556,7 @@ Users can override the binding contract: ```julia EnvironmentResolver( - bind=(scene, object) -> containing_cell(scene.microclimate, position(object)), + bind=(model, object) -> containing_cell(model.microclimate, position(object)), ) ``` @@ -567,10 +567,10 @@ backends. The environment backend protocol should be small and backend-oriented: ```julia -Advanced.bind_environment(scene, backend, object) +Advanced.bind_environment(model, backend, object) sample_environment(backend, binding, time, variables) scatter_environment!(backend, binding, values) -refresh_environment!(backend, scene) +refresh_environment!(backend, model) ``` `meteo_inputs_(model)` declares what a model reads from the active environment @@ -638,7 +638,7 @@ Invalidation events: Geometry APIs should provide ergonomic invalidation: ```julia -mark_environment_binding_dirty!(scene, object) +mark_environment_binding_dirty!(model, object) update_geometry!(object, geometry; invalidate_environment=true) ``` @@ -680,9 +680,9 @@ The final design must be understandable by agents through structured explanation helpers: ```julia -explain_objects(scene) -explain_instances(scene) -explain_scopes(scene) +explain_objects(model) +explain_instances(model) +explain_scopes(model) explain_bindings(sim) explain_calls(sim) explain_environment_bindings(sim) @@ -724,6 +724,6 @@ variables, and suggested fixes. This is a breaking design. It preserves model kernels and the `run!(model, models, status, meteo, constants, extra)` contract while replacing -the scenario configuration surface with `Scene`, `Object`, `ModelSpec`, +the scenario configuration surface with `CompositeModel`, `Object`, `ModelSpec`, selectors, `Inputs(...)`, `Calls(...)`, `TimeStep(...)`, and `Environment(...)`. diff --git a/docs/src/dev/unified_scene_object_implementation_plan.md b/docs/src/dev/composite_model_implementation_plan.md similarity index 81% rename from docs/src/dev/unified_scene_object_implementation_plan.md rename to docs/src/dev/composite_model_implementation_plan.md index 0f2bb04bb..7170ced4a 100644 --- a/docs/src/dev/unified_scene_object_implementation_plan.md +++ b/docs/src/dev/composite_model_implementation_plan.md @@ -1,7 +1,7 @@ -# Unified Scene/Object Implementation Plan +# Unified CompositeModel/Object Implementation Plan This plan is the persistent handoff for replacing the historical -multiscale-mapping system with one scene/object address system. +multiscale-mapping system with one composite-model/object address system. The implementation can be incremental internally, but the target API is breaking. Do not preserve experimental intermediate APIs as user-facing @@ -10,7 +10,7 @@ concepts in the final design. The target public surface should be centered on a small set of concepts: ```julia -Scene +CompositeModel Object ModelSpec @@ -36,7 +36,7 @@ types should be selectors, model traits, or internal compiled carriers. - Added initial selector and address types: `SceneScope`, `Self`, `SelfPlant`, `Ancestor`, `Scope`, `Kind`, `Species`, `Scale`, `Relation`, `One`, `OptionalOne`, `Many`, and `ObjectAddress`. -- Added initial `Scene`/`Object` registry types and lifecycle hooks: +- Added initial `CompositeModel`/`Object` registry types and lifecycle hooks: `register_object!`, `remove_object!`, `reparent_object!`, `move_object!`, and `Advanced.refresh_bindings!`. - Added registry-backed selector resolution with `resolve_object_ids` and @@ -44,8 +44,8 @@ types should be selectors, model traits, or internal compiled carriers. `Ancestor(...)`, `Scope(...)`, positional selectors such as `Kind(:plant)`/`Scale(:Leaf)`, and `One`/`OptionalOne`/`Many` cardinality checks. -- Added `explain_scopes(scene)` for agent-readable scope diagnostics. It - reports the global scene scope, each object subtree, each named +- Added `explain_scopes(model)` for agent-readable scope diagnostics. It + reports the global model scope, each object subtree, each named `Scope(...)`, and label groups by scale, kind, and species with concrete resolved object ids. - Selector cardinality and named-scope failures now report the consumer @@ -60,13 +60,13 @@ types should be selectors, model traits, or internal compiled carriers. - `ObjectAddress(selector)` now normalizes positional `Kind`, `Species`, `Scale`, scope, and `Relation` selectors instead of recording only keyword criteria. -- Started the object-address compiler with `Advanced.compile_scene(scene, specs)` and - compiled scene application/binding carriers. The compiler now resolves +- Started the object-address compiler with `Advanced.compile_composite_model(model, specs)` and + compiled model application/binding carriers. The compiler now resolves `AppliesTo(...)` target object ids, object-relative `Inputs(...)` source object ids, and object-relative `Calls(...)` callee object/application ids before runtime. -- Added `explain_scene_applications`, `explain_bindings`, and `explain_calls` - for the compiled scene view. These explanations expose application ids, +- Added `explain_applications`, `explain_bindings`, and `explain_calls` + for the compiled model view. These explanations expose application ids, processes, target ids, input source ids, call callee ids, temporal policy, window, and carrier hints. - Added status-backed compiled input carriers. When source objects already @@ -76,35 +76,35 @@ types should be selectors, model traits, or internal compiled carriers. and `has_reference_carrier` expose these carriers, and `explain_bindings` reports carrier kind, copy/reference semantics, carrier type, and reference availability. -- Added conservative same-object input inference in the scene compiler. When a +- Added conservative same-object input inference in the model compiler. When a model declares an `inputs_` variable that is not covered by explicit/default `Inputs(...)`, and exactly one other application on the same object outputs - the same variable, `Advanced.compile_scene` creates an inferred reference binding. + the same variable, `Advanced.compile_composite_model` creates an inferred reference binding. `explain_bindings` now reports binding `origin` values such as `:model_default`, `:model_spec`, and `:inferred_same_object`. - Compiled input bindings now carry producer metadata. When an `Inputs(...)` - selector uses `process=` or `application=`, `Advanced.compile_scene` validates that a + selector uses `process=` or `application=`, `Advanced.compile_composite_model` validates that a matching source application exists for the selected source objects. `explain_bindings` reports `source_application_ids`, `process`, and `application` for agent-readable dependency diagnostics. - Dependency selectors in `Inputs(...)` and `Calls(...)` now infer a default scope from the consumer object when no explicit `within=...` is provided: - scene objects default to `SceneScope()`, while non-scene objects default to - `Self()`. Shared scene/soil dependencies from organs should therefore use + model objects default to `SceneScope()`, while non-model objects default to + `Self()`. Shared model/soil dependencies from organs should therefore use `within=SceneScope()` explicitly. -- `Advanced.compile_scene` now validates required status inputs from `inputs_(model)`. +- `Advanced.compile_composite_model` now validates required status inputs from `inputs_(model)`. Each required input must either have a compiled binding or already exist on the target object `Status`; otherwise compilation errors with the concrete application id, object id, and input variable. -- Scene compilation creates an empty `Status` for model-targeted objects when +- CompositeModel compilation creates an empty `Status` for model-targeted objects when status is omitted, inserts missing `outputs_` and `meteo_outputs_` fields from model defaults, and inserts explicitly or implicitly bound input fields from `inputs_` defaults. Unbound external inputs remain compilation errors. -- `Advanced.compile_scene` now rejects `Inputs(...)` declarations whose left-hand +- `Advanced.compile_composite_model` now rejects `Inputs(...)` declarations whose left-hand variable is not declared by the target model's `inputs_`. This catches misspelled or stale scenario bindings before they create silent unused metadata. -- `Advanced.compile_scene` now validates source availability for status-backed +- `Advanced.compile_composite_model` now validates source availability for status-backed non-temporal `Inputs(...)` bindings. When selected source objects already have `Status` values, the requested source variable must resolve to references instead of silently compiling to an unused/no-op binding. @@ -117,7 +117,7 @@ types should be selectors, model traits, or internal compiled carriers. aliases the source `Ref`, records the renamed source variable in `explain_bindings`, and schedules the producer before the consumer. - Same-rate input carriers are installed directly into consumer `Status` - reference cells during scene compilation. Scalar bindings share the source + reference cells during model compilation. Scalar bindings share the source `Ref`; many-object bindings store the compiled `RefVector` or `Advanced.ObjectRefVector` once. The timestep runtime performs no assignment for these bindings, and a focused `Many(...)` materialization gate verifies zero @@ -125,12 +125,12 @@ types should be selectors, model traits, or internal compiled carriers. - Reference wiring adds a missing bound input field to the consumer `Status` schema when needed, instead of requiring users to duplicate compiler-owned input placeholders. -- Added call ambiguity validation in the compiled scene view: a call can select +- Added call ambiguity validation in the compiled model view: a call can select by process when unique, and must use `application=:name` when several model applications with the same process match the same object. -- Added a scene binding cache with `Advanced.refresh_bindings!`, `Advanced.bindings_dirty`, - `Advanced.compiled_bindings`, and `Advanced.scene_revision`. Object creation, removal, - and reparenting now invalidate the compiled binding cache and bump a scene +- Added a model binding cache with `Advanced.refresh_bindings!`, `Advanced.bindings_dirty`, + `Advanced.compiled_bindings`, and `Advanced.model_revision`. Object creation, removal, + and reparenting now invalidate the compiled binding cache and bump a model revision before the next refresh. - Added an environment binding cache with `Advanced.refresh_environment_bindings!`, `Advanced.compile_environment_bindings`, `Advanced.CompiledEnvironmentBinding`, @@ -140,11 +140,11 @@ types should be selectors, model traits, or internal compiled carriers. application/object environment provider, backend, required `meteo_inputs_`, produced `meteo_outputs_`, support descriptor, and backend cell before runtime. -- Added the minimal scene geometry contract: `geometry(object_or_status)`, +- Added the minimal model geometry contract: `geometry(object_or_status)`, `position(object_or_status)`, and `bounds(object_or_status)`. Environment binding refreshes now call `update_index!(backend, entities)` once per distinct backend before `Advanced.bind_environment`, giving spatial backends a current - scene-wide object/entity list for precomputed microclimate lookup. + model-wide object/entity list for precomputed microclimate lookup. - Automatic spatial binding now uses the nearest ancestor geometry when a target object has no geometry of its own. Existing backends still receive an `Object` carrying the target id/status, while its binding-time geometry comes @@ -158,21 +158,21 @@ types should be selectors, model traits, or internal compiled carriers. while application id, object, process, provider, backend, status, and geometry provenance remain unchanged, required/output metadata is updated while the cached cell is reused without `update_index!` or `Advanced.bind_environment`. -- `validate_meteo_inputs(scene)` and +- `validate_meteo_inputs(model)` and `validate_meteo_inputs(compiled_scene, meteo_or_backend)` now validate - scene/object model application `meteo_inputs_` against the active - environment or an explicit replacement. Errors report scene application ids, + composite-model/object model application `meteo_inputs_` against the active + environment or an explicit replacement. Errors report model application ids, so duplicate process applications remain diagnosable. - `Environment(; sources=(target=:source,))` now remaps model-facing environment variables to backend source variables. Environment binding refresh validates missing source variables when the backend can enumerate its variables, and `explain_environment_bindings` reports both `required_inputs` and `source_inputs`. -- Scene applications now infer model-author default environment source remaps +- CompositeModel applications now infer model-author default environment source remaps from `meteo_hint(...).bindings` when the scenario does not provide explicit meteo bindings. Scenario `Environment(; sources=...)` keeps precedence over the trait. -- Global tabular meteorology is now sampled at each scene application's +- Global tabular meteorology is now sampled at each model application's compiled clock. `meteo_hint(...).bindings` reducers and windows are applied through PlantMeteo, while `Environment(; sources=...)` replaces only the source variable and preserves the selected reducer. Prepared weather @@ -184,29 +184,29 @@ types should be selectors, model traits, or internal compiled carriers. so moving a leaf or changing its geometry can refresh microclimate lookup without rebuilding object/model binding carriers. - Added public geometry invalidation helpers: - `update_geometry!(scene, object, geometry; invalidate_environment=true)` - and object-scoped `mark_environment_binding_dirty!(scene, object)`. + `update_geometry!(model, object, geometry; invalidate_environment=true)` + and object-scoped `mark_environment_binding_dirty!(model, object)`. Geometry-only changes record the affected object ids and refresh only their compiled environment bindings; descendants inheriting moved ancestor geometry are invalidated as part of the same object-scoped refresh. -- Started scene/object execution with `run!(scene; steps=...)`. +- Started composite-model/object execution with `run!(model; steps=...)`. The runtime refreshes compiled object bindings and environment bindings, materializes precompiled `Inputs(...)` carriers into consumer `Status` fields, samples the bound environment backend, and calls generic model kernels through the existing `run!` contract. -- Scene/object execution now publishes model outputs to scene-local temporal +- CompositeModel/object execution now publishes model outputs to model-local temporal streams. Compiled `Inputs(...)` bindings marked as `:temporal_stream` can materialize `HoldLast`, `Interpolate`, `Integrate`, and `Aggregate` values before the consumer runs, using selector source ids, source variables, - windows, and the scene base timestep. -- Scene temporal `Inputs(...)` now honor producer `output_policy(...)` traits + windows, and the model base timestep. +- CompositeModel temporal `Inputs(...)` now honor producer `output_policy(...)` traits when the selector omits `policy=...` and resolves to a unique source application. Explicit selector policies remain scenario-level overrides. -- Scene `Interpolate(...)` matches the established multirate runtime: +- CompositeModel `Interpolate(...)` matches the established multirate runtime: bracketed samples use linear interpolation, online consumers use linear extrapolation from the last two samples when requested, and insufficient or non-interpolable values fall back to hold-last. `mode=:hold` and - `extrapolation=:hold` are supported, invalid modes fail during scene + `extrapolation=:hold` are supported, invalid modes fail during model compilation, and interpolation arithmetic preserves generic numeric value types without converting model values to `Float64`. - Unified `Inputs(...)` now supports explicit lagged dependencies with @@ -215,46 +215,46 @@ types should be selectors, model traits, or internal compiled carriers. status value until history exists, and do not add a same-timestep scheduling edge. This allows feedback cycles to compile without changing generic model kernels. -- Scene/object execution now scatters mutable environment outputs declared by +- CompositeModel/object execution now scatters mutable environment outputs declared by `meteo_outputs_(model)` back to the bound backend after each model call. This reuses `scatter_environment_outputs!`, so environment writers keep the existing generic model contract: compute a same-named status value, and let the runtime push it to the active microclimate backend. - Added root application scheduling from `TimeStep(...)` using `Dates.Period` - values and the scene environment base step. `explain_schedule` on a - `Advanced.CompiledScene` now reports each application clock, phase, timestep in base + values and the model environment base step. `explain_schedule` on a + `Advanced.CompiledCompositeModel` now reports each application clock, phase, timestep in base steps, timestep duration in seconds, and whether the application is scheduled as a root application or is manual-call-only. -- Scene application scheduling now also honors a model's `timespec(...)` trait +- CompositeModel application scheduling now also honors a model's `timespec(...)` trait when `TimeStep(...)` is omitted. Scenario-level `TimeStep(...)` keeps precedence over the model trait, matching the established multirate runtime. -- Scene application scheduling now validates `timestep_hint(...)` required +- CompositeModel application scheduling now validates `timestep_hint(...)` required bounds for base-step-derived clocks. Hints remain compatibility constraints; they do not override explicit `TimeStep(...)` or non-default `timespec(...)`. -- `Advanced.compile_scene` now computes a stable topological application order from +- `Advanced.compile_composite_model` now computes a stable topological application order from resolved `Inputs(...)` producer edges and `Updates(...)` writer-order edges. Inputs produced by manual-call-only applications are redirected to the parent - application that owns the `Calls(...)` call stack. `run!(scene)` uses this + application that owns the `Calls(...)` call stack. `run!(model)` uses this precompiled order instead of user declaration order, cycles fail at compile time, and `explain_schedule` reports `execution_index`. -- `Advanced.CompiledScene` now pre-indexes input and call bindings by +- `Advanced.CompiledCompositeModel` now pre-indexes input and call bindings by `(application_id, object_id)`. Per-object input materialization and `call_target(s)` lookup uses these indexes instead of scanning every - binding in the scene at each model call. -- `Advanced.CompiledScene` now also pre-indexes applications by application id. + binding in the model at each model call. +- `Advanced.CompiledCompositeModel` now also pre-indexes applications by application id. Hard-call target resolution and stable ordered-application materialization use this index instead of scanning or rebuilding lookup dictionaries. - `Advanced.CompiledEnvironmentBindings` now pre-indexes environment bindings by `(application_id, object_id)`. Environment sampling and mutable environment output scattering use direct lookup instead of scanning all environment bindings for every model invocation. -- Added `SceneRunContext` and `SceneCallTarget`. Models can retrieve manual +- Added `RunContext` and `CallTarget`. Models can retrieve manual `Calls(...)` targets with `call_target(s)(extra, :name)` and execute them with `run_call!`, preserving explicit call-stack control in the - scene/object runtime. Manual calls execute immediately under the parent call + composite-model/object runtime. Manual calls execute immediately under the parent call stack; applications selected by `Calls(...)` are skipped by the root - `run!(scene)` loop and only execute through `run_call!`. -- Added scene/object duplicate-writer validation. During `Advanced.compile_scene`, each + `run!(model)` loop and only execute through `run_call!`. +- Added composite-model/object duplicate-writer validation. During `Advanced.compile_composite_model`, each `(object, output variable)` now has one canonical writer unless later writers declare `Updates(:var; after=...)`. The `after` token can match a previous application id/name or process, so scenario authors can express @@ -271,31 +271,31 @@ types should be selectors, model traits, or internal compiled carriers. - Added model-level `Input(...)` defaults from `dep(model)` into `ModelSpec` value inputs. Scenario-level `ModelSpec(...) |> Inputs(...)` overrides those defaults before the native binding is compiled. -- Removed the intermediate scenario bridge after the scene/object compiler +- Removed the intermediate scenario bridge after the composite-model/object compiler gained native `Inputs(...)` support. Manual value-transfer carriers are not retained as user-authored API. - Removed the intermediate dependency resolver after `Calls(...)` became - native scene/object metadata. Manual model execution now goes through + native composite-model/object metadata. Manual model execution now goes through `call_target`, `call_targets`, and `run_call!`. - Added model-level `Call(...)` defaults from `dep(model)` into `ModelSpec` manual-call metadata. Scenario-level `ModelSpec(...) |> Calls(...)` overrides those defaults, and `dep(::ModelSpec)` excludes raw `Call(...)` trait entries so default calls are normalized through the same bridge as explicit calls. -- Migrated the MAESPA example's scene energy-balance hard calls to +- Migrated the MAESPA example's model energy-balance hard calls to scenario-level `ModelSpec(scene_model) |> Calls(...)`. -- Migrated the MAESPA example's scene LAI leaf-area transfer to consumer-side +- Migrated the MAESPA example's model LAI leaf-area transfer to consumer-side `ModelSpec(LAIModel(...)) |> Inputs(...)`. -- Started Phase 5 with `ObjectTemplate` and `ObjectInstance`. A template stores - reusable scene/object `ModelSpec`s plus default object labels, and an +- Started Phase 5 with `CompositeModelTemplate` and `ObjectInstance`. A template stores + reusable composite-model/object `ModelSpec`s plus default object labels, and an instance mounts those specs inside one named object subtree. -- `Scene(...)` accepts `ObjectInstance` values directly or through its +- `CompositeModel(...)` accepts `ObjectInstance` values directly or through its `instances` keyword. An instance root can be an owned `Object` or the id of - an object supplied separately to the scene. + an object supplied separately to the model. - Mounted template applications receive stable instance-prefixed application names and an implicit `Scope(instance_name)` on unqualified `AppliesTo(...)` selectors. Their `Inputs(...)`, `Calls(...)`, scheduling, - writer validation, and execution use the normal compiled scene/object path. + writer validation, and execution use the normal compiled composite-model/object path. - Instance overrides can replace one template application by application name or process. Overrides must be unambiguous and preserve process identity. Instances without overrides retain the exact shared model object from the @@ -322,55 +322,55 @@ types should be selectors, model traits, or internal compiled carriers. table. Structured application explanations report shared/per-object storage, concrete versus heterogeneous dispatch, overridden object ids, and model types. -- `Scene` retains mounted instance metadata and `explain_instances(scene)` +- `CompositeModel` retains mounted instance metadata and `explain_instances(model)` reports each instance root, current subtree object ids, mounted application ids, instance/object overrides, template labels, and parameter ownership. - `explain_objects(scene)` also reports instance membership. + `explain_objects(model)` also reports instance membership. - New objects registered below an instance automatically inherit missing template `kind` and `species` labels. Instance explanations derive membership from the current topology, so growth, pruning, and reparenting do not leave a separate stale membership list. -- Scene hard calls now accept explicit local meteorology: +- CompositeModel hard calls now accept explicit local meteorology: `run_call!(target; meteo=local_meteo, publish=false)`. The callee receives the supplied meteo object instead of resampling the bound environment, while `publish=false` still suppresses output publication and environment scattering. This supports iterative microclimate solvers such as the MAESPA - scene energy-balance loop. -- Scene runtime now builds a small process-keyed `models` bundle from compiled + model energy-balance loop. +- CompositeModel runtime now builds a small process-keyed `models` bundle from compiled `Calls(...)` edges before invoking a model kernel. This lets existing generic hard-dependency kernels such as `Monteith` continue to call `models.photosynthesis`, and lets `Fvcb` call `models.stomatal_conductance`, without wrapping or rewriting the model implementation. -- Scene duplicate-writer validation now ignores manual-call-only applications +- CompositeModel duplicate-writer validation now ignores manual-call-only applications when validating canonical root writers. This keeps hard-dependency children from being treated as independent root writers when they intentionally update the same object status inside their parent call stack. -- Added a unified scene/object MAESPA example path: +- Added a unified composite-model/object MAESPA example path: `build_maespa_scene(...)` and `run_maespa_example(...)`. - It uses `ObjectTemplate`, `ObjectInstance`, `AppliesTo`, `Inputs`, `Calls`, + It uses `CompositeModelTemplate`, `ObjectInstance`, `AppliesTo`, `Inputs`, `Calls`, and `TimeStep(Dates.Period)` with two plant species, one shared soil object, - scene LAI, and scene energy balance. -- `test/test-maespa-scene-example.jl` verifies the unified scene/object + model LAI, and model energy balance. +- `test/test-maespa-model-example.jl` verifies the unified composite-model/object MAESPA path. -- `run!(scene)` now returns a `SceneSimulation` wrapper with the mutated - `Scene`, compiled scene bindings, compiled environment bindings, and the - scene-local temporal output streams. This keeps existing status mutation - behavior but makes scene outputs inspectable after a run. -- Added `scene_outputs(sim::SceneSimulation)`, `collect_outputs(sim)`, and - `explain_outputs(sim)` for scene/object runs. The explanation reports object +- `run!(model)` now returns a `Simulation` wrapper with the mutated + `CompositeModel`, compiled model bindings, compiled environment bindings, and the + model-local temporal output streams. This keeps existing status mutation + behavior but makes model outputs inspectable after a run. +- Added `outputs(sim::Simulation)`, `collect_outputs(sim)`, and + `explain_outputs(sim)` for composite-model/object runs. The explanation reports object ids, variables, publishing application ids, sample counts, time bounds, and value types. -- `run!(scene; tracked_outputs=...)` now accepts `OutputRequest` and returns - requested scene outputs through `collect_outputs(sim)` or - `collect_outputs(sim, :request_name)`. Scene requests are materialized from +- `run!(model; tracked_outputs=...)` now accepts `OutputRequest` and returns + requested model outputs through `collect_outputs(sim)` or + `collect_outputs(sim, :request_name)`. CompositeModel requests are materialized from retained typed temporal streams after the run. They support `HoldLast`, `Interpolate`, `Integrate`, and `Aggregate`, `Dates.Period` export clocks, canonical-publisher inference when unique, explicit `process=...` selection, and dynamic objects over each object's own published sample interval. -- Scene output requests now compile a publisher-level retention plan. With - `tracked_outputs=nothing`, the scene runtime retains all output streams for +- CompositeModel output requests now compile a publisher-level retention plan. With + `tracked_outputs=nothing`, the model runtime retains all output streams for historical inspection. With explicit `tracked_outputs`, including an empty request vector, it retains only requested publisher streams plus streams required by temporal `Inputs(...)`. Dependency-only streams are pruned after @@ -385,43 +385,43 @@ types should be selectors, model traits, or internal compiled carriers. request, or a temporal dependency. Dependency-only rows also report their compiled `retention_steps`; unbounded requested/default rows report `nothing`. -- Scene temporal streams are now keyed by application id, object id, and +- CompositeModel temporal streams are now keyed by application id, object id, and variable. Multiple applications can publish the same variable on the same object without overwriting each other's stream samples. -- Scene output-export tests now cover requested-output `DataFrame` +- CompositeModel output-export tests now cover requested-output `DataFrame` materialization, canonical publisher inference when `process=` is omitted, rejection when only stream-only publishers exist, and ambiguity when an explicit process matches both a stream-only and a canonical publisher. -- Scene `OutputRequest(...)` now accepts `application=...` to select an +- CompositeModel `OutputRequest(...)` now accepts `application=...` to select an explicit application id/name when the same process is mounted more than once. Explicit application selection can retain and export a `:stream_only` publisher. -- Each scene temporal stream owns a concrete `Vector{Tuple{Float64,T}}` +- Each model temporal stream owns a concrete `Vector{Tuple{Float64,T}}` selected from its first published value rather than boxing all values as `Any`. Output type changes fail explicitly, and `Interpolate`/`Integrate` tests verify `BigFloat` histories and reduced values remain `BigFloat`. -- Scene `OutputRouting(; var=:stream_only)` now matches the unified graph +- CompositeModel `OutputRouting(; var=:stream_only)` now matches the unified graph semantics: stream-only outputs are excluded from canonical writer validation and same-object input inference, while remaining available in output streams and explicit `Inputs(..., application=:name)` selections. -- `run!(scene; steps=...)` now refreshes dirty structural bindings at timestep +- `run!(model; steps=...)` now refreshes dirty structural bindings at timestep boundaries. Objects created, removed, or reparented by a model during one timestep update `AppliesTo(...)` target sets, input carriers, call targets, writer validation, and scheduling before the next timestep. - Geometry-only mutations refresh environment bindings at the next timestep - without recompiling structural bindings. The returned `SceneSimulation` + without recompiling structural bindings. The returned `Simulation` always contains final compiled structural and environment bindings, including mutations performed on the last step. - Environment dirty tracking is now object-scoped for geometry-only changes. `move_object!`, `update_geometry!`, and - `mark_environment_binding_dirty!(scene, object)` retain unaffected compiled + `mark_environment_binding_dirty!(model, object)` retain unaffected compiled bindings and re-run `Advanced.bind_environment` only for applications targeting the changed object. Structural changes and provider-wide invalidation still rebuild the complete environment cache. - Runtime lifecycle tests cover a model-created leaf joining a leaf application and plant-local `RefVector`, a pruned leaf leaving both before the next step, and a moved leaf switching mock microclimate cells. -- `Advanced.CompiledScene` now precompiles a process-keyed model bundle for every +- `Advanced.CompiledCompositeModel` now precompiles a process-keyed model bundle for every `(application_id, object_id)` target. Generic hard-dependency kernels receive this cached bundle through their existing `models` argument without traversing `Calls(...)` or allocating temporary collections in the timestep @@ -430,7 +430,7 @@ types should be selectors, model traits, or internal compiled carriers. types available to every application/object kernel call. Tests verify zero-allocation repeated bundle lookup and the MAESPA leaf bundle `energy_balance -> photosynthesis -> stomatal_conductance`. -- Root scene execution now compiles contiguous homogeneous target batches. +- Root model execution now compiles contiguous homogeneous target batches. Each target prebinds its concrete model, `Status`, process-keyed model bundle, input-binding tuple, and environment binding. Runtime dispatch occurs once at the batch function barrier; the inner object loop is specialized on a @@ -443,12 +443,12 @@ types should be selectors, model traits, or internal compiled carriers. ids, concrete model/status/carrier types, batch sizes, and inner-loop dispatch semantics. A focused 128-leaf gate verifies zero allocations inside a warmed homogeneous no-output batch. -- Added `objects_from_mtg(root; ...)` and `Scene(root::MultiScaleTreeGraph.Node; +- Added `objects_from_mtg(root; ...)` and `CompositeModel(root::MultiScaleTreeGraph.Node; ...)`. Existing MTG topology is traversed once into the unified registry, preserving stable node-derived ids, parent relations, labels, geometry, and existing `:plantsimengine_status` objects through configurable accessors. -The scene/object compiler is executable: selectors normalize to object +The composite-model/object compiler is executable: selectors normalize to object addresses, resolve before runtime, and compile into reference, temporal, call, writer, and environment carriers. The historical mapping compiler has been removed. @@ -485,17 +485,17 @@ Acceptance tests: - structured explanations expose model kernel type, process, application name, and target object ids. -## Phase 1: Scene Object Registry +## Phase 1: CompositeModel Object Registry Goal: introduce the internal object model without changing public behavior yet. Implement: - `ObjectId` as the stable identity key for every runtime object. -- `SceneObject` metadata with labels: +- `ModelObject` metadata with labels: `scale`, `kind`, `species`, optional `name`, parent id, child ids, and optional geometry/position handle. -- `Advanced.SceneRegistry` storing objects, parent/child relations, and indexes by +- `Advanced.ObjectRegistry` storing objects, parent/child relations, and indexes by label. - adapters from existing MTG state into the registry: each selected root and each MTG node gets an object id; @@ -506,7 +506,7 @@ Implement: Acceptance tests: - the MAESPA example registers five leaf objects, two plant objects, one soil - object, and one scene object; + object, and one model object; - `status(sim, :plant_A, :Leaf)` and `status(sim, :Leaf)` can be expressed as registry queries; - add/remove/reparent updates object registry relations and status views. @@ -550,14 +550,14 @@ Definitions: - `Subtree()` means the current object and its descendants. - `SelfPlant()` means the nearest containing plant scope. - `Ancestor(scale=:Plant)` is the generic selector form for `SelfPlant()`. -- `SceneScope()` means the whole scene. +- `SceneScope()` means the whole model. - `Scope(name)` means a named scope or object collection. Rules: - unqualified selectors inside a reusable plant application bundle default to `within=Self()`; -- scene-level selectors default to `within=SceneScope()`; +- model-level selectors default to `within=SceneScope()`; - `One(...)` errors unless exactly one object resolves per consumer; - `Many(...)` preserves stable object-id order; - object-id order replaces incidental traversal order as the semantic default. @@ -567,8 +567,8 @@ Rules: Acceptance tests: - plant allocation on four oil palms reads only leaves under each plant; -- scene LAI reads leaves across all plant objects; -- a species-specific scene model can read only `species=:oil_palm` leaves; +- model LAI reads leaves across all plant objects; +- a species-specific model model can read only `species=:oil_palm` leaves; - a model application target set declared with `AppliesTo(...)` produces stable application/object pairs; - selector errors report available labels and near matches. @@ -610,7 +610,7 @@ Rules: - model authors still declare `inputs_`; scenario authors decide where those inputs come from; - `dep(model)` may provide defaults for common value-input bindings in - scene/object composition; + composite-model/object composition; - scenario-level `ModelSpec(...) |> Inputs(...)` always wins over `dep(model)` defaults; - same-rate local links should keep reference semantics where possible; @@ -637,7 +637,7 @@ Carrier expectations: Acceptance tests: -- the MAESPA scene LAI cross-object input is declared with `Inputs(...)` and +- the MAESPA model LAI cross-object input is declared with `Inputs(...)` and produces the same `lai` and `leaf_area`; - historical plant allocation `MultiScaleModel([:leaf_carbon => [:Leaf => :leaf_carbon]])` becomes `Inputs(...)` and remains plant-local; @@ -691,17 +691,17 @@ Rules: Acceptance tests: -- MAESPA scene energy balance uses `Calls(...)` and still controls iterative +- MAESPA model energy balance uses `Calls(...)` and still controls iterative leaf energy calls; - missing call selectors report `kind`, `scale`, `process`, and available matches; - final accepted calls publish exactly once per timestep. -- an iterative scene model can run selected leaf and soil calls several times +- an iterative model model can run selected leaf and soil calls several times with `publish=false` and publish only the accepted state. Implemented: -- `run_call!(::SceneCallTarget)` defaults to `publish=false`, matching the +- `run_call!(::CallTarget)` defaults to `publish=false`, matching the iterative manual-call contract. - One-shot accepted calls use `publish=true` explicitly. - An iterative hard-call regression executes two default non-publishing trials @@ -729,13 +729,13 @@ selective per-instance differences. Target API: ```julia -oil_palm = ObjectTemplate( +oil_palm = CompositeModelTemplate( kind=:plant, species=:oil_palm, mapping=oil_palm_mapping, ) -scene = Scene( +model = CompositeModel( ObjectInstance(:palm_1, oil_palm; root=node1), ObjectInstance(:palm_2, oil_palm; root=node2, overrides=( stomatal_conductance = Tuzet(; g1=3.2), @@ -757,17 +757,17 @@ Rules: - templates do not prescribe topology; they attach mappings to whatever object tree the instance provides; - default `Self()` selectors resolve inside the current instance; -- scene-wide models must opt into wider scope. +- model-wide models must opt into wider scope. Acceptance tests: - four oil palm instances share model objects/parameters when not overridden; - one palm instance can override one process parameter; -- allocation remains per plant while scene LAI sees all leaves. +- allocation remains per plant while model LAI sees all leaves. MAESPA status: -- the unified scene/object MAESPA path uses `ObjectTemplate` and +- the unified composite-model/object MAESPA path uses `CompositeModelTemplate` and `ObjectInstance` for species A and B. ## Phase 5B: Object Lifecycle And Cache Invalidation @@ -778,11 +778,11 @@ through one mutation path. Implement public lifecycle hooks: ```julia -register_object!(scene, object; parent) -remove_object!(scene, object) -reparent_object!(scene, object, new_parent) -move_object!(scene, object, geometry_or_position) -Advanced.refresh_bindings!(scene) +register_object!(model, object; parent) +remove_object!(model, object) +reparent_object!(model, object, new_parent) +move_object!(model, object, geometry_or_position) +Advanced.refresh_bindings!(model) ``` Implement invalidation for: @@ -804,7 +804,7 @@ Rules: Acceptance tests: -- creating a new leaf adds it to plant-local allocation and scene LAI before +- creating a new leaf adds it to plant-local allocation and model LAI before the next timestep; - pruning/removing a leaf removes it from many-object carriers and temporal stream ownership; @@ -907,7 +907,7 @@ Acceptance tests: - `explain_bindings(sim)` reports whether each dependency came from inference, `dep(model)`, or `ModelSpec`, and reports carrier/copy semantics. - no selector resolution occurs inside the per-object, per-model timestep loop - for static scenes. + for static composite models. - a warmed homogeneous execution batch performs no allocations beyond model, output-stream, or backend work requested by the application itself; - multirate simulations use the same object-address graph as same-rate @@ -928,7 +928,7 @@ Write migration notes: - `MultiScaleModel([:x => [:Leaf => :y]])` -> `Inputs(:x => Many(scale=:Leaf, var=:y))`; - cross-object value declarations -> consumer `Inputs(...)`; - manual dependency declarations -> `Calls(...)`; -- repeated species assemblies -> `ObjectTemplate` plus `ObjectInstance`; +- repeated species assemblies -> `CompositeModelTemplate` plus `ObjectInstance`; - explicit meteo wiring -> environment resolver/binding backend. - `InputBindings(...)` -> source and temporal policy information inside `Inputs(...)`; @@ -943,28 +943,28 @@ Regression tests must cover all migrated examples before removal. Migration documentation progress: -- Added `docs/src/migration_scene_object.md` with direct translations for +- Added `docs/src/migration_composite_model.md` with direct translations for `MultiScaleModel`, repeated object assemblies, `TimeStepModel`, `InputBindings`, `MeteoBindings`, `ScopeModel`, and `SameScale`. -- Documentation navigation and the home page now identify the scene/object API +- Documentation navigation and the home page now identify the composite-model/object API as the target for new multiscale and multi-plant work. -- The documentation home page now uses executable scene/object examples as the - primary quickstart. It shows `Scene`, `Object`, `ModelSpec`, `AppliesTo`, +- The documentation home page now uses executable composite-model/object examples as the + primary quickstart. It shows `CompositeModel`, `Object`, `ModelSpec`, `AppliesTo`, `Inputs`, `TimeStep`, automatic same-object binding inference, multi-object `Many(...)` inputs, and manual `Calls(...)` syntax. -- The repository README now mirrors the scene/object entry point instead of - teaching `ModelMapping` first. It includes smoke-tested `Scene`/`Object` +- The repository README now mirrors the composite-model/object entry point instead of + teaching `ModelMapping` first. It includes smoke-tested `CompositeModel`/`Object` quickstart code, `Inputs(...)` multi-object coupling, conceptual `Calls(...)` syntax, and links to the migration guide. -- Added `docs/src/scene_object/quickstart.md` as the first native - scene/object tutorial page and promoted it in the documentation navigation. +- Added `docs/src/composite_model/quickstart.md` as the first native + composite-model/object tutorial page and promoted it in the documentation navigation. The page contains docs-tested examples for one-object model chaining, inferred same-object bindings, `OutputRequest` retention, multi-object `Inputs(...)`, `RefVector` carrier explanations, and manual `Calls(...)` syntax. - The repository agent skill teaches the unified public vocabulary. -- The public API page now starts with curated scene/object groups for scenario +- The public API page now starts with curated composite-model/object groups for scenario construction, selectors, coupling, lifecycle, environment, runtime, and structured explanations. @@ -975,7 +975,7 @@ Current removal audit: examples, and documentation. - Public manual-call control now uses `call_target`, `call_targets`, and `run_call!`. -- `SceneRunContext` defines `call_target(s)` directly, including string-name +- `RunContext` defines `call_target(s)` directly, including string-name support. - The legacy mapping transforms are removed: `MultiScaleModel`, `SameScale`, `TimeStepModel`, `InputBindings`, @@ -986,39 +986,39 @@ Current removal audit: - Historical MTG mapping and mapping-level multirate pages were removed from the active documentation navigation. A future documentation cleanup can replace - these historical pages with equivalent scene/object tutorials rather than + these historical pages with equivalent composite-model/object tutorials rather than retaining them as migration reference. -- The model execution page has been rewritten as a scene/object-first guide. +- The model execution page has been rewritten as a composite-model/object-first guide. It now documents compilation, same-rate reference carriers, temporal `Inputs(...)`, manual `Calls(...)`, `Updates(...)`, `TimeStep(...)`, environment binding, output retention, lifecycle cache invalidation, and compatibility translations from the historical mapping runtime. -- The detailed first simulation tutorial now starts from the scene/object API +- The detailed first simulation tutorial now starts from the composite-model/object API instead of `ModelMapping`. It introduces model kernels, object status, - compiled applications, inferred same-object bindings, scene outputs, and a + compiled applications, inferred same-object bindings, model outputs, and a short compatibility note for historical mapping examples. -- The quick examples page now uses copy-pasteable scene/object examples for +- The quick examples page now uses copy-pasteable composite-model/object examples for Beer light interception, degree-days/LAI/light coupling, biomass growth, and retained `OutputRequest` exports. `ModelMapping` appears only in the compatibility note. - The standard model coupling, model switching, and coupling more complex - models step-by-step tutorials now teach `Scene`, `Object`, `ModelSpec`, + models step-by-step tutorials now teach `CompositeModel`, `Object`, `ModelSpec`, `AppliesTo`, `TimeStep`, inferred soft `Inputs(...)`, and manual `Calls(...)` first. Historical `PlantSimEngine.ModelMapping(...)` appears only in compatibility notes on those pages. -- The home page has been replaced by native scene/object examples. The - repository README has also been replaced by native scene/object examples, - and a dedicated scene/object quickstart is now available in the main +- The home page has been replaced by native composite-model/object examples. The + repository README has also been replaced by native composite-model/object examples, + and a dedicated composite-model/object quickstart is now available in the main documentation navigation. -- Scene/object tests cover scheduling, temporal policies, binding inference +- CompositeModel/object tests cover scheduling, temporal policies, binding inference and overrides, meteo contracts and aggregation, output routing and application-qualified export, and structured explanations. Legacy mapping regression tests were removed with the old runtime. -- Scene/object tests now include public meteo-contract validation parity for +- CompositeModel/object tests now include public meteo-contract validation parity for missing environment variables, explicit `Environment(; sources=...)` remapping, model-author `meteo_hint` source defaults, and validation against an explicit replacement meteo object/backend. -- Test code uses the canonical `TimeStep(...)` spelling and scene/object +- Test code uses the canonical `TimeStep(...)` spelling and composite-model/object modifiers. Legacy transform tests were removed with the old compatibility constructors. - The unified MAESPA path is implemented and tested through `AppliesTo`, @@ -1041,4 +1041,4 @@ Current removal audit: ## Completion Evidence The requirement-by-requirement evidence and final verification commands are -recorded in `unified_scene_object_completion_audit.md`. +recorded in `composite_model_completion_audit.md`. diff --git a/docs/src/dev/maespa_scene_handoff.md b/docs/src/dev/maespa_model_handoff.md similarity index 79% rename from docs/src/dev/maespa_scene_handoff.md rename to docs/src/dev/maespa_model_handoff.md index 043f12e60..fa4e1222f 100644 --- a/docs/src/dev/maespa_scene_handoff.md +++ b/docs/src/dev/maespa_model_handoff.md @@ -1,13 +1,13 @@ -# MAESPA-Style Scene Example Handoff +# MAESPA-Style CompositeModel Example Handoff -The executable acceptance example is `examples/maespa_scene_example.jl`, with -focused coverage in `test/test-maespa-scene-example.jl`. +The executable acceptance example is `examples/maespa_model_example.jl`, with +focused coverage in `test/test-maespa-model-example.jl`. -## Scene Shape +## CompositeModel Shape -- One `:Scene` object owns canopy microclimate and scene-scale fluxes. +- One `:Scene` object owns canopy microclimate and model-scale fluxes. - One shared `:Soil` object owns soil water state. -- Species A and B are reusable `ObjectTemplate`s mounted as independent +- Species A and B are reusable `CompositeModelTemplate`s mounted as independent `ObjectInstance`s. - Each plant instance contains one plant object, one internode object, and its own leaf objects. @@ -21,7 +21,7 @@ Leaf applications use the copied PlantBiophysics subsample models: ## Coupling -The scene energy-balance application controls iterative leaf and soil calls: +The model energy-balance application controls iterative leaf and soil calls: ```julia ModelSpec(scene_model; name=:scene_eb) |> @@ -39,7 +39,7 @@ Trial leaf calls use `run_call!(target)` and do not publish. The accepted solution uses `run_call!(target; publish=true)`, so temporal outputs and environment writes are emitted exactly once. -Scene LAI receives live references to every leaf area: +CompositeModel LAI receives live references to every leaf area: ```julia ModelSpec(LAIModel(ground_area); name=:lai_dynamic) |> @@ -67,7 +67,7 @@ ModelSpec(allocation; name=:allocation) |> ## Meteorology -Input meteorology is above-canopy forcing. Scene status stores the resulting +Input meteorology is above-canopy forcing. CompositeModel status stores the resulting below-canopy microclimate: - `canopy_tair`; @@ -85,7 +85,7 @@ The focused test verifies: - five leaves across two species and one shared soil object; - instance membership and mounted application ids; -- scene calls to all leaf energy-balance applications and the soil model; +- model calls to all leaf energy-balance applications and the soil model; - nested `Monteith -> Fvcb -> Tuzet` call bundles; - live-reference LAI and plant-local allocation bindings; - hourly energy balance and daily LAI/allocation schedules; diff --git a/docs/src/dev/public_api_refinement_completion_audit.md b/docs/src/dev/public_api_refinement_completion_audit.md index a81f99fae..8f8bfa1e2 100644 --- a/docs/src/dev/public_api_refinement_completion_audit.md +++ b/docs/src/dev/public_api_refinement_completion_audit.md @@ -8,15 +8,15 @@ It complements the [decision record](public_api_refinement_decisions.md) and the | Requirement | Supported contract | Evidence | |:--|:--|:--| -| Public boundary | Composition, model-author, diagnostic, and extension symbols are exported by default; compiler/cache representations live under `PlantSimEngine.Advanced`. | `test-scene-api-stabilization.jl` checks the namespace boundary; Documenter's missing-doc check covers exported docstrings. | +| Public boundary | Composition, model-author, diagnostic, and extension symbols are exported by default; compiler/cache representations live under `PlantSimEngine.Advanced`. | `test-model-api-stabilization.jl` checks the namespace boundary; Documenter's missing-doc check covers exported docstrings. | | Application identity | Repeated process applications require explicit names. Inputs, calls, outputs, and `Updates(...; after=...)` use canonical application IDs. Singular process lookup is a deprecation bridge; `Many(process=...)` remains explicit discovery. | Stabilization, binding-inference, hard-call, output, and update tests cover repeated applications and actionable ambiguity errors. | -| Selector grammar | `Self()` is one object, `Subtree()` is that object plus descendants, `SelfPlant()` is the containing plant, and `SceneScope()` is the scene. `One`, `OptionalOne`, and `Many` share the same criteria across targeting, coupling, lookup, and outputs. | Multi-plant selector tests, instance/template tests, lifecycle tests, and XPalm downstream tests. | +| Selector grammar | `Self()` is one object, `Subtree()` is that object plus descendants, `SelfPlant()` is the containing plant, and `SceneScope()` is the model. `One`, `OptionalOne`, and `Many` share the same criteria across targeting, coupling, lookup, and outputs. | Multi-plant selector tests, instance/template tests, lifecycle tests, and XPalm downstream tests. | | Outputs | `outputs=:none` is the safe default; `:all` and selector-based `OutputRequest`s are explicit. Request names are unique, application identity is preserved, and removed-object history remains collectable. | Output-boundary, runtime-matrix, multirate, lifecycle-history, and allocation tests. | | Execution ownership | `run!` starts a fresh simulation; `continue!` and `step!` advance its live handle without resetting time, streams, schedules, or environment position. | Split-run equivalence, multirate-boundary, environment-resume, and lifecycle-continuation tests. | -| Construction and initialization | `Scene(models...; status=...)` lowers to ordinary objects and `ModelSpec`s. `explain_initialization` reports application, object, origin, defaults, expected/provided types, and remedies without running kernels. | Concise/explicit lowering equivalence and initialization report tests. | -| Diagnostics | Supported explanation functions accept `Scene` directly and compiled views where useful; simulation overloads avoid field inspection. Results are structured vectors that can be filtered with ordinary Julia predicates. | Structured explanation assertions throughout the scene test matrix and documentation examples. | +| Construction and initialization | `CompositeModel(models...; status=...)` lowers to ordinary objects and `ModelSpec`s. `explain_initialization` reports application, object, origin, defaults, expected/provided types, and remedies without running kernels. | Concise/explicit lowering equivalence and initialization report tests. | +| Diagnostics | Supported explanation functions accept `CompositeModel` directly and compiled views where useful; simulation overloads avoid field inspection. Results are structured vectors that can be filtered with ordinary Julia predicates. | Structured explanation assertions throughout the model test matrix and documentation examples. | | Lifecycle | Registration, MTG growth, removal, reparenting, movement, and geometry updates are the supported mutation paths. Cycle/self-parent failures are atomic; structural and geometry invalidation remain targeted. | Stabilization, unified integration, environment, and lifecycle-output tests. | -| Model-author API | The kernel remains `run!(model, models, status, meteo, constants, extra)`. `runtime_scene`, call-target accessors, traits, and lifecycle helpers are the supported context surface. | `test-model-contract.jl`, hard-call tests, growing-plant tutorial, and downstream model suites. | +| Model-author API | The kernel remains `run!(model, models, status, meteo, constants, extra)`. `runtime_model`, call-target accessors, traits, and lifecycle helpers are the supported context surface. | `test-model-contract.jl`, hard-call tests, growing-plant tutorial, and downstream model suites. | | Compatibility | `tracked_outputs`, singular scenario `process=` references, output-request `process=`, and process-only overrides have targeted warnings and documented replacements, scheduled for removal in 0.15. Mapping runtimes are not restored. | Migration guide plus compatibility tests. | ## Validation matrix @@ -38,6 +38,6 @@ retention modes, fresh/continued execution, and homogeneous hot-loop allocation. ## Deliberate compatibility boundary Compiled structs and cache controls are qualified advanced APIs and may evolve. -Direct mutation of `Object` or `Scene` fields is unsupported. Historical +Direct mutation of `Object` or `CompositeModel` fields is unsupported. Historical `ModelMapping`, executor, and status-vector runtimes are outside the compatibility surface and must not be reintroduced. diff --git a/docs/src/dev/public_api_refinement_decisions.md b/docs/src/dev/public_api_refinement_decisions.md index 75fba4a22..d27fc9b23 100644 --- a/docs/src/dev/public_api_refinement_decisions.md +++ b/docs/src/dev/public_api_refinement_decisions.md @@ -1,7 +1,7 @@ # Public API refinement decisions -This decision record defines the target public contract for the Scene/Object -API. The Scene/Object compiler and runtime remain the only supported scenario +This decision record defines the target public contract for the CompositeModel/Object +API. The CompositeModel/Object compiler and runtime remain the only supported scenario runtime. ## Terminology and identity @@ -9,7 +9,7 @@ runtime. - An **object** is one runtime entity with a stable `ObjectId`. - A **model** is one scientific implementation of a process. - A **process** is model metadata and may have several applications. -- An **application** is one named, configured occurrence of a model in a scene. +- An **application** is one named, configured occurrence of a model in a model. - User declarations that identify a producer, writer, update predecessor, call target, or output stream use application identity. - Process queries are discovery filters. They are not substitutes for an @@ -32,7 +32,7 @@ Scope names have one meaning: - `Subtree()` selects the current object and all of its descendants. - `SelfPlant()` selects the current object's plant root and its descendants. - `Ancestor(...)` selects the matching ancestor's subtree. -- `SceneScope()` searches the whole scene. +- `SceneScope()` searches the whole model. - `Scope(name)` searches the named object's subtree. - `Relation(...)` selects objects with the requested topological relationship. @@ -45,10 +45,10 @@ Cross-object coupling is always visible in the declaration through `Subtree`, `run!` uses an explicit `outputs` keyword: ```julia -run!(scene; outputs=:none) -run!(scene; outputs=:all) -run!(scene; outputs=request) -run!(scene; outputs=requests) +run!(model; outputs=:none) +run!(model; outputs=:all) +run!(model; outputs=request) +run!(model; outputs=requests) ``` The default is `outputs=:none`. Temporal dependency streams required by the @@ -66,8 +66,8 @@ to `OutputRequest(Many(scale=:Leaf), :x)` during migration. ## Execution and continuation -`run!(scene; steps=n, ...)` starts a fresh result timeline at step one while -mutating scene status. It returns a live `SceneSimulation` execution handle. +`run!(model; steps=n, ...)` starts a fresh result timeline at step one while +mutating model status. It returns a live `Simulation` execution handle. `continue!(simulation; steps=n)` advances that simulation from its current step, preserving retained streams, temporal dependency history, environment @@ -75,7 +75,7 @@ position, and multirate clock phase. It returns the same simulation. `step!(simulation)` is equivalent to `continue!(simulation; steps=1)`. -Calling `run!` on an already-mutated scene intentionally creates a new result +Calling `run!` on an already-mutated model intentionally creates a new result timeline. Users who intend temporal continuation use `continue!`; the distinct operation prevents an accidental step-index reset. @@ -86,7 +86,7 @@ timestep using the existing targeted invalidation contract. The default namespace is organized around: -- scene composition and execution; +- model composition and execution; - model-author declarations and kernel helpers; - supported structured explanations; - documented environment extension interfaces. @@ -100,7 +100,7 @@ promise that ordinary users should depend on it. ## Compatibility policy - Removed legacy mapping/executor APIs are not restored. -- Current Scene/Object spellings receive targeted deprecations only when they +- Current CompositeModel/Object spellings receive targeted deprecations only when they have a clear replacement. - New canonical spellings are implemented and tested before deprecated aliases are removed. diff --git a/docs/src/dev/release_notes_handoff.md b/docs/src/dev/release_notes_handoff.md index e03a40e41..8cf0af376 100644 --- a/docs/src/dev/release_notes_handoff.md +++ b/docs/src/dev/release_notes_handoff.md @@ -1,6 +1,6 @@ # Release Notes Handoff -This page is the persistent release-note source for the scene/object redesign +This page is the persistent release-note source for the composite-model/object redesign and cleanup branch. Keep it factual: mark what is implemented, what is removed, and what is only planned. @@ -9,7 +9,7 @@ and what is only planned. Source details live in `code_cleanup_audit.md`. - Removed `ModelList`, `ModelMapping`, `GraphSimulation`, `MultiScaleModel`, - and the separate mapping dependency/runtime stack. Use `Scene`, `Object`, + and the separate mapping dependency/runtime stack. Use `CompositeModel`, `Object`, and model applications. - Removed direct and batch mapping `run!` methods. - Removed string scale names. Use symbols, for example `:Leaf`. @@ -23,19 +23,19 @@ Source details live in `code_cleanup_audit.md`. - Removed unused parallel-executor traits after deleting the executor runtime. - Removed dead mapping-era wrappers and traits: `UninitializedVar`, `RefVariable`, `TreeAlike`, and `StatusView`. -- Removed the unreleased `ObjectTemplate(...; mapping=...)` alias and dead +- Removed the unreleased `CompositeModelTemplate(...; mapping=...)` alias and dead selector-to-mapping conversion helpers. - Removed stale `PlantSimEngine.Examples` exports for the deleted `ToyInternodeEmergence` example. - Replaced many source-side validation `@assert`s with explicit errors. - Added `Updates(:var; after=:application)` for ordered duplicate writers. -- Added `runtime_scene(runtime)` as the sanctioned live-scene accessor for - `SceneRunContext` and `SceneSimulation`; kernels no longer need to inspect - `extra.compiled.scene`. -- Added `explain_initialization(scene)` with structured `:supplied`, +- Added `runtime_model(runtime)` as the sanctioned live-model accessor for + `RunContext` and `Simulation`; kernels no longer need to inspect + `extra.compiled.model`. +- Added `explain_initialization(model)` with structured `:supplied`, `:generated`, `:producer_bound`, `:environment_bound`, and `:unresolved` dispositions. -- Added `Scene(model, models...; status=...)` as a thin one-object constructor +- Added `CompositeModel(model, models...; status=...)` as a thin one-object constructor that lowers to the normal object and `ModelSpec` representation. - Calendar-aligned windows remain unsupported. Temporal windows use duration-based `Dates.Period` semantics. @@ -52,23 +52,23 @@ route materialization, environment bridge, graph runner, and output publisher. Because this API was never released, there is no compatibility layer or user migration path for it. -The reusable behavior now lives in the scene/object runtime: object selectors, +The reusable behavior now lives in the composite-model/object runtime: object selectors, compiled `Inputs(...)`, manual `Calls(...)`, `Dates`-based scheduling, environment backends, dynamic object lifecycle handling, and structured explanations. Dynamic MTG growth now has one public high-level operation: `add_organ!`. -An MTG-backed `Scene` retains the accessors and status initializer used during +An MTG-backed `CompositeModel` retains the accessors and status initializer used during initial adaptation. `add_organ!` reuses that policy for new nodes, merges -explicit initial values, attaches the resulting `Status`, registers the scene +explicit initial values, attaches the resulting `Status`, registers the model object, and invalidates runtime bindings. `register_object!` remains available as the low-level registry operation. XPalm and PlantGeom were migrated away from package-local wrappers that duplicated this lifecycle sequence. ## Implemented MAESPA-Style Example Changes -The current `examples/maespa_scene_example.jl` is the main executable example -for multi-plant scene coupling. +The current `examples/maespa_model_example.jl` is the main executable example +for multi-plant model coupling. - Uses copied PlantBiophysics subsample models: `Monteith`, `Fvcb`, and `Tuzet`. @@ -80,21 +80,21 @@ for multi-plant scene coupling. - Ports MAESPA-style canopy air temperature and VPD update through `tvpdcanopcalc` and `gbcanms`. - Treats input meteorology as above-canopy forcing and writes below-canopy - microclimate to scene status fields: + microclimate to model status fields: `canopy_tair`, `canopy_vpd`, `canopy_rh`, `canopy_htot`, and `canopy_gcanop`. - Adds `LAIModel` and declares plant leaf-area materialization with `ModelSpec(...) |> Inputs(...)`. - Computes plant allocation daily from plant-local `leaf_carbon` vectors. -- Adds `run_call!` for manually executing compiled scene call targets. +- Adds `run_call!` for manually executing compiled model call targets. - Adds model-level `Input(...)` and `Call(...)` dependency defaults through `dep(model)`, with scenario-level `Inputs(...)` and `Calls(...)` overriding those defaults in `ModelSpec`. -- Adds initial registry-backed scene selector resolution with +- Adds initial registry-backed model selector resolution with `resolve_object_ids` and `resolve_objects` for global, self-relative, plant-relative, ancestor-relative, and named-scope object selections. -- Adds `explain_scopes(scene)` for structured scope diagnostics. It reports - the scene scope, object subtree scopes, named `Scope(...)` entries, and +- Adds `explain_scopes(model)` for structured scope diagnostics. It reports + the model scope, object subtree scopes, named `Scope(...)` entries, and scale/kind/species label groups with concrete object ids. - Selector failures now include context, matched object ids, requested criteria, available labels, and near-match suggestions. Misspelled labels @@ -106,23 +106,23 @@ for multi-plant scene coupling. constrained by an explicit scope. - `ObjectAddress` explanations now preserve positional selector criteria such as `Scale(:Leaf)` and `Relation(:parent)`. -- Adds the first compiled scene/object view with `Advanced.compile_scene`, - `Advanced.CompiledScene`, `Advanced.CompiledSceneApplication`, `Advanced.CompiledSceneInputBinding`, - `Advanced.CompiledSceneCallBinding`, `explain_scene_applications`, +- Adds the first compiled composite-model/object view with `Advanced.compile_composite_model`, + `Advanced.CompiledCompositeModel`, `Advanced.CompiledModelApplication`, `Advanced.CompiledModelInputBinding`, + `Advanced.CompiledModelCallBinding`, `explain_applications`, `explain_bindings`, and `explain_calls`. -- The compiled scene view resolves `AppliesTo(...)`, `Inputs(...)`, and +- The compiled model view resolves `AppliesTo(...)`, `Inputs(...)`, and `Calls(...)` to object ids ahead of runtime, and reports temporal policy, window, carrier hints, and callee application ids for agent-readable diagnostics. -- Unscoped scene/object dependency selectors now infer scope from the consumer: - scene consumers default to `SceneScope()`, while non-scene consumers default +- Unscoped composite-model/object dependency selectors now infer scope from the consumer: + model consumers default to `SceneScope()`, while non-model consumers default to `Self()`. Cross-scope shared dependencies, such as leaf models reading soil state, should use `within=SceneScope()` explicitly. -- Adds status-backed compiled input carriers for the scene/object view: +- Adds status-backed compiled input carriers for the composite-model/object view: scalar shared refs, homogeneous `RefVector`s, and `Advanced.ObjectRefVector` fallback carriers. `input_carrier`, `input_value`, and `has_reference_carrier` expose them for tests, diagnostics, and future runtime execution. -- Same-rate scene inputs are now wired into consumer `Status` references once +- Same-rate model inputs are now wired into consumer `Status` references once during compilation. Scalar and `Many(...)` inputs remain live references, missing bound input fields are compiler-generated, and repeated non-temporal input materialization is allocation-free. @@ -136,19 +136,19 @@ for multi-plant scene coupling. - `explain_bindings` now reports stable carrier kind and copy/reference semantics, making reference-wired inputs and materialized temporal values explicit for users and agents. -- Adds scene binding cache helpers: +- Adds model binding cache helpers: `Advanced.refresh_bindings!`, `Advanced.bindings_dirty`, `Advanced.compiled_bindings`, and - `Advanced.scene_revision`. Object registration, removal, and reparenting invalidate + `Advanced.model_revision`. Object registration, removal, and reparenting invalidate cached compiled bindings before the next refresh. -- Adds scene/object environment binding cache helpers: +- Adds composite-model/object environment binding cache helpers: `Advanced.refresh_environment_bindings!`, `Advanced.compile_environment_bindings`, `Advanced.CompiledEnvironmentBinding`, `Advanced.CompiledEnvironmentBindings`, `Advanced.environment_bindings_dirty`, `Advanced.compiled_environment_bindings`, `Advanced.environment_revision`, and `explain_environment_bindings`. -- Adds `geometry`, `position`, and `bounds` accessors for scene objects/statuses. +- Adds `geometry`, `position`, and `bounds` accessors for model objects/statuses. Environment binding refreshes call `update_index!(backend, entities)` before binding objects to backend cells/layers, so spatial backends can precompute - scene-wide lookup structures. + model-wide lookup structures. - Spatial environment binding now falls back to the nearest ancestor geometry for objects without their own geometry. Binding explanations expose the geometry provenance, and moving an ancestor refreshes only descendants that @@ -158,99 +158,99 @@ for multi-plant scene coupling. when the application/object/provider/geometry contract is otherwise unchanged. - `Environment(; sources=(CO2=:Ca,))` now remaps model-facing environment - variables to backend source variables. Scene environment binding refresh + variables to backend source variables. CompositeModel environment binding refresh validates missing source variables for enumerable backends such as `GlobalConstant`, and explanations expose both `required_inputs` and `source_inputs`. -- `validate_meteo_inputs(scene)` and +- `validate_meteo_inputs(model)` and `validate_meteo_inputs(compiled_scene, meteo_or_backend)` now validate - scene/object environment contracts directly. Missing-variable diagnostics use - scene application ids, and validation honors both scenario + composite-model/object environment contracts directly. Missing-variable diagnostics use + model application ids, and validation honors both scenario `Environment(; sources=...)` remaps and model-author `meteo_hint` defaults. - Object movement now invalidates environment bindings without rebuilding the structural object/model binding cache. - Adds public geometry lifecycle helpers: - `update_geometry!(scene, object, geometry; invalidate_environment=true)` and - object-scoped `mark_environment_binding_dirty!(scene, object)`. They - currently invalidate the scene environment binding cache and leave room for + `update_geometry!(model, object, geometry; invalidate_environment=true)` and + object-scoped `mark_environment_binding_dirty!(model, object)`. They + currently invalidate the model environment binding cache and leave room for finer-grained dirty tracking later. -- Adds the first scene/object runtime with `run!(scene; steps=...)`. +- Adds the first composite-model/object runtime with `run!(model; steps=...)`. It materializes compiled `Inputs(...)` carriers, samples bound environment inputs, and executes generic model kernels on object `Status` values. -- Scene/object compiler now infers simple same-object value bindings from +- CompositeModel/object compiler now infers simple same-object value bindings from `inputs_`/`outputs_` when one producer is unambiguous. `explain_bindings` reports each binding origin, including `:model_default`, `:model_spec`, and `:inferred_same_object`. - Compiled input bindings now validate `Inputs(...)` `process=`/`application=` filters when they are provided, and `explain_bindings` reports `source_application_ids`, `process`, and `application`. -- `Advanced.compile_scene` now errors for required `inputs_(model)` variables that are +- `Advanced.compile_composite_model` now errors for required `inputs_(model)` variables that are neither bound through `Inputs(...)`/inference nor present on the target object `Status`. -- `Advanced.compile_scene` now prepares model-owned status schemas automatically: +- `Advanced.compile_composite_model` now prepares model-owned status schemas automatically: model-targeted objects may omit `Status`, declared model/environment outputs are inserted from trait defaults, and bound consumer inputs are generated from `inputs_` defaults. External unbound inputs still require explicit initialization. -- `Advanced.compile_scene` now rejects `Inputs(...)` entries whose receiving variable is +- `Advanced.compile_composite_model` now rejects `Inputs(...)` entries whose receiving variable is not declared by the model's `inputs_`, making binding typos explicit at compile time. -- `Advanced.compile_scene` now validates status-backed non-temporal `Inputs(...)` +- `Advanced.compile_composite_model` now validates status-backed non-temporal `Inputs(...)` source availability, so bindings that select existing source objects but no source `Status` reference fail at compile time instead of becoming no-ops. -- Scene/object runtime now publishes model outputs to scene-local temporal +- CompositeModel/object runtime now publishes model outputs to model-local temporal streams and resolves temporal `Inputs(...)` with `HoldLast`, `Integrate`, and `Aggregate` policies before consumer execution. -- Scene temporal `Inputs(...)` now use producer `output_policy(...)` traits as +- CompositeModel temporal `Inputs(...)` now use producer `output_policy(...)` traits as the default when the selector omits `policy=...` and resolves to a unique source application. Explicit selector policies override the trait. -- Scene applications now infer model-author default environment source remaps +- CompositeModel applications now infer model-author default environment source remaps from `meteo_hint(...).bindings` when the scenario does not provide explicit meteo bindings. Scenario `Environment(; sources=...)` remains the override. -- Scene/object runtime now scatters values declared by `meteo_outputs_(model)` +- CompositeModel/object runtime now scatters values declared by `meteo_outputs_(model)` back to the bound environment backend after each model call, using the existing `scatter_environment_outputs!` backend protocol. -- Scene/object root applications now honor `TimeStep(...)` values backed by +- CompositeModel/object root applications now honor `TimeStep(...)` values backed by `Dates.Period` scheduling. `explain_schedule` reports normalized clocks and whether an application is root-scheduled or manual-call-only. -- Scene/object root applications now also honor `timespec(...)` model traits +- CompositeModel/object root applications now also honor `timespec(...)` model traits when no explicit `TimeStep(...)` is provided. Scenario-level `TimeStep(...)` remains the override. -- Scene/object root applications now validate `timestep_hint(...)` required - bounds for clocks derived from the scene base step. Hints remain +- CompositeModel/object root applications now validate `timestep_hint(...)` required + bounds for clocks derived from the model base step. Hints remain compatibility constraints, not scheduling overrides. -- Scene/object execution now uses a stable topological application order +- CompositeModel/object execution now uses a stable topological application order compiled from `Inputs(...)` producer edges and `Updates(...)` ordering. Dependencies on manual-call-only applications are redirected to their parent caller, same-timestep cycles fail during compilation, and `explain_schedule` reports `execution_index`. -- `Advanced.CompiledScene` now pre-indexes input and call bindings by application and +- `Advanced.CompiledCompositeModel` now pre-indexes input and call bindings by application and object id. Runtime input materialization and hard-call lookup no longer scan - all scene bindings for every object/model invocation. -- `Advanced.CompiledScene` now pre-indexes applications by application id, removing + all model bindings for every object/model invocation. +- `Advanced.CompiledCompositeModel` now pre-indexes applications by application id, removing application scans from hard-call target resolution and dictionary rebuilding from ordered execution setup. - `Advanced.CompiledEnvironmentBindings` now pre-indexes environment bindings by - application and object id, removing the scene-wide binding scan from + application and object id, removing the model-wide binding scan from environment sampling and output scattering. -- Adds `SceneRunContext` and `SceneCallTarget`; scene/object models can use +- Adds `RunContext` and `CallTarget`; composite-model/object models can use `call_target(s)(extra, :name)` plus `run_call!` for manual `Calls(...)` execution. - Applications selected by `Calls(...)` are skipped by the root - `run!(scene)` loop and execute only through explicit `run_call!`, preserving + `run!(model)` loop and execute only through explicit `run_call!`, preserving parent-controlled hard-call execution. -- Adds scene/object duplicate-writer validation in `Advanced.compile_scene`. A variable +- Adds composite-model/object duplicate-writer validation in `Advanced.compile_composite_model`. A variable may have only one canonical writer per object unless later writers declare `Updates(:var; after=...)`, where `after` can match a previous application id/name or process. - Adds `explain_writers(compiled)` to report object-variable writer groups, duplicate writers, and the `Updates(...)` declarations that validate ordered updates. -- Adds the first reusable object-template path with `ObjectTemplate` and +- Adds the first reusable object-template path with `CompositeModelTemplate` and `ObjectInstance`. Templates bundle reusable `ModelSpec`s and default `kind`/`species` labels; instances mount them inside a named object subtree. -- `Scene(...)` accepts mounted instances whose roots are either owned objects - or references to separately supplied scene objects. +- `CompositeModel(...)` accepts mounted instances whose roots are either owned objects + or references to separately supplied model objects. - Template applications are scoped to their instance and receive stable instance-prefixed application ids. Unmodified instances share the template's model objects, while instance overrides can replace one application by name @@ -265,18 +265,18 @@ for multi-plant scene coupling. - Override validation requires the same process and declared status/environment variable names. Application explanations report model storage, dispatch mode, overridden object ids, and replacement model types. -- Adds `explain_instances(scene)` and instance membership in - `explain_objects(scene)`. Instance rows expose roots, current object +- Adds `explain_instances(model)` and instance membership in + `explain_objects(model)`. Instance rows expose roots, current object membership, mounted applications, overrides, template labels, and reference-based parameter ownership. - Objects created below a mounted instance inherit missing template `kind` and `species` labels. Membership explanations use the current topology rather than a copied instance object list. -- Scene hard calls now accept explicit local meteorology: +- CompositeModel hard calls now accept explicit local meteorology: `run_call!(target; meteo=local_meteo, publish=false)`. This lets iterative parent models pass trial microclimate to manually called child models without - resampling the scene environment. -- Scene hard calls now default to `publish=false`. Trial calls mutate target + resampling the model environment. +- CompositeModel hard calls now default to `publish=false`. Trial calls mutate target status without publishing temporal samples or environment writes; accepted states must use `run_call!(target; publish=true)`. Iterative-call tests verify that several trials followed by one accepted call publish exactly once. @@ -290,30 +290,30 @@ for multi-plant scene coupling. - Compiled `OptionalOne(...)` inputs and calls now accept zero matches. Optional inputs keep their declared model default, optional calls return an empty target collection, and both remain visible in structured explanations. -- Scene runtime now builds a process-keyed `models` bundle from compiled +- CompositeModel runtime now builds a process-keyed `models` bundle from compiled `Calls(...)` edges before invoking a model kernel. Existing hard-dependency kernels such as `Monteith` and `Fvcb` can therefore keep using `models.photosynthesis` and `models.stomatal_conductance`. -- Scene duplicate-writer validation now ignores manual-call-only applications +- CompositeModel duplicate-writer validation now ignores manual-call-only applications when validating canonical root writers, so hard-dependency children are not treated as independent root writers for variables they update inside a parent call stack. - Adds `build_maespa_scene(...)` and `run_maespa_example(...)`. - This unified scene/object MAESPA path uses `ObjectTemplate`, + This unified composite-model/object MAESPA path uses `CompositeModelTemplate`, `ObjectInstance`, `AppliesTo`, `Inputs`, `Calls`, and `TimeStep(Dates.Period)` with two plant species, one shared soil object, - scene LAI, and scene energy balance. -- `test/test-maespa-scene-example.jl` verifies the unified scene/object + model LAI, and model energy balance. +- `test/test-maespa-model-example.jl` verifies the unified composite-model/object MAESPA path. -- `run!(scene)` now returns a `SceneSimulation` wrapper containing the mutated - scene, compiled object bindings, compiled environment bindings, and - scene-local temporal output streams. -- Adds scene output inspection helpers: - `scene_outputs(sim::SceneSimulation)`, `collect_outputs(sim)`, and +- `run!(model)` now returns a `Simulation` wrapper containing the mutated + model, compiled object bindings, compiled environment bindings, and + model-local temporal output streams. +- Adds model output inspection helpers: + `outputs(sim::Simulation)`, `collect_outputs(sim)`, and `explain_outputs(sim)`. These expose object ids, variables, publishing application ids, sample counts, time bounds, and value types. -- `run!(scene; tracked_outputs=...)` now accepts `OutputRequest` for - scene/object runs. Requested outputs are collected from retained typed scene +- `run!(model; tracked_outputs=...)` now accepts `OutputRequest` for + composite-model/object runs. Requested outputs are collected from retained typed model streams after the run, can be read with `collect_outputs(sim)` or `collect_outputs(sim, :request_name)`, support the standard temporal policies and `Dates.Period` export clocks, and respect dynamic object @@ -329,60 +329,60 @@ for multi-plant scene coupling. still preserve complete histories, and export remains post-run rather than fully online. - Adds `explain_output_retention(sim)` for structured diagnostics of retained - scene output streams, their reasons, and the compiled retention horizon for + model output streams, their reasons, and the compiled retention horizon for dependency-only streams. -- Scene temporal streams are now keyed by application id, object id, and +- CompositeModel temporal streams are now keyed by application id, object id, and variable, so two applications can publish the same variable on the same object without overwriting each other's stream samples. -- Scene output-export tests now cover requested-output `DataFrame` +- CompositeModel output-export tests now cover requested-output `DataFrame` materialization, canonical publisher inference without `process=...`, rejection when only stream-only publishers exist, and ambiguity when an explicit process matches both a stream-only and a canonical publisher. -- `OutputRequest(...)` now accepts `application=...` for scene/object runs. +- `OutputRequest(...)` now accepts `application=...` for composite-model/object runs. This disambiguates repeated applications of the same process and permits explicit export of a named `:stream_only` publisher. -- Scene temporal streams now retain a concrete value type per +- CompositeModel temporal streams now retain a concrete value type per application/object/output stream. Type changes fail explicitly, while generic values such as `BigFloat` remain typed through publication, interpolation, and integration. -- Scene temporal `Inputs(...)` now implement the complete `Interpolate(...)` +- CompositeModel temporal `Inputs(...)` now implement the complete `Interpolate(...)` policy used by the existing multirate runtime: linear interpolation when samples bracket the requested time, online linear extrapolation from the last two samples, and configurable hold behavior. Interpolation modes are - validated during scene compilation, and arithmetic preserves generic value + validated during model compilation, and arithmetic preserves generic value types such as `BigFloat` instead of coercing model values to `Float64`. -- Scene `Inputs(...)` accepts +- CompositeModel `Inputs(...)` accepts `PreviousTimeStep(:input) => One(...)` or `Many(...)` for explicit lagged - dependencies. These bindings read the previous scene timestep, use the + dependencies. These bindings read the previous model timestep, use the consumer status initialization before history exists, and are excluded from same-timestep dependency edges so feedback loops can be compiled. -- Scene `OutputRouting(; var=:stream_only)` is honored by canonical writer +- CompositeModel `OutputRouting(; var=:stream_only)` is honored by canonical writer validation and same-object input inference. Stream-only outputs are excluded from canonical ownership, but remain available in output streams and explicit `Inputs(..., application=:name)` bindings. -- Scene execution now refreshes dirty structural bindings between timesteps. +- CompositeModel execution now refreshes dirty structural bindings between timesteps. Objects created, removed, or reparented by a model update application target sets, input carriers, call targets, writer validation, and scheduling before the next timestep. - Geometry-only changes refresh environment bindings at the next timestep - without rebuilding structural bindings. `SceneSimulation` returns the final + without rebuilding structural bindings. `Simulation` returns the final compiled structural and environment state, including changes made during the last timestep. - Geometry-only environment invalidation is now object-scoped. Moving or explicitly marking one organ dirty preserves unaffected compiled environment bindings and rebinds only model applications targeting that object; - structural scene changes still trigger a full rebuild. + structural model changes still trigger a full rebuild. - Added runtime lifecycle coverage for organ creation, pruning, plant-local `RefVector` refresh, historical output retention for removed objects, and movement between mock microclimate cells. -- `Advanced.CompiledScene` now precompiles one process-keyed model bundle per +- `Advanced.CompiledCompositeModel` now precompiles one process-keyed model bundle per application/object target. Generic hard-dependency kernels receive this cached bundle through the existing `models` argument, avoiding recursive `Calls(...)` traversal and temporary collection allocation in the timestep hot loop. - Adds `explain_model_bundles(compiled)` for structured inspection of the process names and model types passed to each application/object kernel. -- Scene root execution now uses compiled homogeneous target batches. Models, +- CompositeModel root execution now uses compiled homogeneous target batches. Models, statuses, model bundles, input bindings, and environment bindings are prebound, so dynamic dispatch happens once per batch instead of once per object. Heterogeneous object overrides split into ordered concrete batches. @@ -392,12 +392,12 @@ for multi-plant scene coupling. `call_target(extra, name)`/`call_targets(extra, name)` lookup API followed by `run_call!`. - Removed the unreleased intermediate authoring and runtime subsystem after - scene/object feature parity was established. -- Adds `objects_from_mtg(root; ...)` and `Scene(mtg; ...)` so existing MTG + composite-model/object feature parity was established. +- Adds `objects_from_mtg(root; ...)` and `CompositeModel(mtg; ...)` so existing MTG topology can be adapted once into the unified registry while preserving node-derived identity, parent relations, labels, geometry, and existing status objects. -- Scene applications now sample global tabular meteorology at their compiled +- CompositeModel applications now sample global tabular meteorology at their compiled `Dates.Period` clock. PlantMeteo reducers and windows from `meteo_hint` are honored; `Environment(; sources=...)` overrides the source while preserving the reducer. Prepared samplers are shared, and one sampled row is cached per @@ -405,18 +405,18 @@ for multi-plant scene coupling. ## Compatibility Boundary -The scene/object runtime and its MAESPA acceptance path are implemented. +The composite-model/object runtime and its MAESPA acceptance path are implemented. Historical mapping APIs and the unreleased intermediate prototype were removed. The design, implementation history, and completion evidence are documented in: -- `unified_scene_object_design.md` -- `unified_scene_object_implementation_plan.md` -- `unified_scene_object_completion_audit.md` +- `composite_model_design.md` +- `composite_model_implementation_plan.md` +- `composite_model_completion_audit.md` The completed public migration is: -- replace historical tutorials with native scene/object tutorials where +- replace historical tutorials with native composite-model/object tutorials where long-term coverage is still valuable; - model mappings should be described as model applications: `ModelSpec(model; name=...) |> AppliesTo(...) |> Inputs(...) |> Calls(...)`; @@ -437,47 +437,47 @@ The completed public migration is: cached environment bindings. Historical mapping examples, tests, and runtime files were removed after the -scene/object acceptance path reached feature parity. Migration information is +composite-model/object acceptance path reached feature parity. Migration information is kept in this release-note handoff and the user-facing migration guide. ## Migration Documentation Added -- Added `docs/src/migration_scene_object.md` as the user-facing migration guide - from historical mappings to the scene/object API. +- Added `docs/src/migration_composite_model.md` as the user-facing migration guide + from historical mappings to the composite-model/object API. - Updated documentation navigation, home-page guidance, multiscale warnings, and the canonical repository agent skill to direct new - scenarios toward `Scene`, `Object`, `AppliesTo`, `Inputs`, `Calls`, + scenarios toward `CompositeModel`, `Object`, `AppliesTo`, `Inputs`, `Calls`, `Updates`, `TimeStep`, and `Environment`. - Replaced the documentation home-page quickstart with executable - scene/object examples. The page now introduces `Scene`, `Object`, + composite-model/object examples. The page now introduces `CompositeModel`, `Object`, `ModelSpec`, `AppliesTo`, `Inputs`, `TimeStep`, inferred same-object bindings, multi-object `Many(...)` inputs, and manual `Calls(...)` syntax before linking to the migration guide. -- Replaced the repository README examples with scene/object-first examples. - The README now introduces `Scene`, `Object`, model applications, +- Replaced the repository README examples with composite-model/object-first examples. + The README now introduces `CompositeModel`, `Object`, model applications, multi-object `Inputs(...)`, and `Calls(...)`. -- Added a native scene/object quickstart page to the main documentation +- Added a native composite-model/object quickstart page to the main documentation navigation. It provides docs-tested examples for one-object model chaining, inferred bindings, requested output retention, multi-object `Inputs(...)`, reference carrier explanations, and manual `Calls(...)` syntax. -- Rewrote the model execution page as the current scene/object execution +- Rewrote the model execution page as the current composite-model/object execution guide. It now covers compilation, reference carriers, temporal `Inputs(...)`, manual `Calls(...)`, `Updates(...)`, `TimeStep(...)`, environment binding, retained outputs, lifecycle invalidation, and migration translations for historical mapping constructs. -- Rewrote the detailed first simulation tutorial to use the scene/object API. - It now introduces `Scene`, `Object`, `ModelSpec`, `AppliesTo`, `TimeStep`, - compiled applications, inferred same-object bindings, scene outputs, and a +- Rewrote the detailed first simulation tutorial to use the composite-model/object API. + It now introduces `CompositeModel`, `Object`, `ModelSpec`, `AppliesTo`, `TimeStep`, + compiled applications, inferred same-object bindings, model outputs, and a migration note for historical examples. -- Rewrote the quick examples page to use native scene/object snippets for +- Rewrote the quick examples page to use native composite-model/object snippets for Beer light interception, degree-days/LAI/light coupling, biomass growth, and retained `OutputRequest` exports. Historical mapping usage is confined to migration records. - Rewrote the standard model coupling, model switching, and coupling more - complex models tutorials around the scene/object API. These pages now show + complex models tutorials around the composite-model/object API. These pages now show inferred same-object value bindings, switching one `ModelSpec` application, execution-plan explanations, and `Calls(...)` manual-call wiring. - Removed legacy mapping transforms and their runtime implementations: `MultiScaleModel`, `SameScale`, `TimeStepModel`, `InputBindings`, `MeteoBindings`, `MeteoWindow`, and `ScopeModel`. -- Added a curated unified scene/object map to the public API page. +- Added a curated unified composite-model/object map to the public API page. diff --git a/docs/src/developers.md b/docs/src/developers.md index 4df15d38b..3df614d7f 100644 --- a/docs/src/developers.md +++ b/docs/src/developers.md @@ -37,7 +37,7 @@ julia --project=test test/runtests.jl ``` The current public runtime is sequential. Running with multiple Julia threads -does not enable a parallel Scene executor; parallel execution remains roadmap +does not enable a parallel Composite model executor; parallel execution remains roadmap work and requires dedicated correctness tests before it becomes public. ### Documentation @@ -118,8 +118,8 @@ stable `data-testid` attributes for commands and confirm mutations through the Julia `/state` endpoint. Avoid assertions against generated CSS classes or implementing PlantSimEngine selector semantics in TypeScript. -Core graph DTO and edit tests live in `test/test-scene-graph-view.jl`. -HTTP-extension tests live in `test/test-scene-graph-editor-extension.jl`. +Core graph DTO and edit tests live in `test/test-model-graph-view.jl`. +HTTP-extension tests live in `test/test-model-graph-editor-extension.jl`. When changing the graph schema, update those Julia tests, frontend types, unit tests, Playwright scenarios, and the committed production bundle in the same change. diff --git a/docs/src/guides/coupling.md b/docs/src/guides/coupling.md index 68da4bb02..50b251b66 100644 --- a/docs/src/guides/coupling.md +++ b/docs/src/guides/coupling.md @@ -12,7 +12,7 @@ Nested calls inherit publication suppression, so a descendant cannot publish inside an unpublished ancestor trial. `explain_calls` and `explain_schedule` show call-only targets and ordering. -`explain_initialization(scene)` classifies values as supplied, generated, +`explain_initialization(model)` classifies values as supplied, generated, producer-bound, environment-bound, or unresolved before execution. ## Value coupling diff --git a/docs/src/guides/data/outputs_plotting.md b/docs/src/guides/data/outputs_plotting.md index e80048f98..47fd5cfc1 100644 --- a/docs/src/guides/data/outputs_plotting.md +++ b/docs/src/guides/data/outputs_plotting.md @@ -1,17 +1,17 @@ # Collecting And Plotting Outputs -Run a scene to obtain `SceneSimulation`, then call `collect_outputs(sim)` for +Run a model to obtain `Simulation`, then call `collect_outputs(sim)` for ordinary analysis. Rows identify application, object, variable, timestep/time, and value, so repeated processes cannot overwrite one another. Convert the rows to a `DataFrame`, filter by application/object/variable, group, and plot. Runs default to `outputs=:none`. Use `outputs=:all` only when complete stream history is intentional; selected requests are the memory-safe choice for large -scenes. Raw rows have the stable columns `timestep`, `time`, `application_id`, +composite models. Raw rows have the stable columns `timestep`, `time`, `application_id`, `object_id`, `variable`, and `value`. Requested/resampled rows additionally identify `scale` and `process`. A temporal request emits `missing` when its policy cannot produce a value for a scheduled output time. -`time` is expressed in scene base-step coordinates; application clock metadata +`time` is expressed in model base-step coordinates; application clock metadata is reported by `explain_schedule(simulation)`. Values retain their concrete types, so unit-bearing model outputs remain unit-bearing in collected rows. @@ -32,9 +32,9 @@ PlantSimEngine.outputs_(::DocsOutputCounter) = (value=0,) PlantSimEngine.run!(::DocsOutputCounter, models, status, meteo, constants, extra) = (status.value += 1) -scene = Scene(DocsOutputCounter(); environment=(duration=Hour(1),)) +model = CompositeModel(DocsOutputCounter(); environment=(duration=Hour(1),)) simulation = run!( - scene; + model; steps=3, outputs=OutputRequest( One(scale=:Scene), diff --git a/docs/src/guides/graph_visualizer_editor.md b/docs/src/guides/graph_visualizer_editor.md index f031afd45..26ca198ec 100644 --- a/docs/src/guides/graph_visualizer_editor.md +++ b/docs/src/guides/graph_visualizer_editor.md @@ -2,14 +2,14 @@ CurrentModule = PlantSimEngine ``` -# Visualize And Edit A Scene +# Visualize And Edit A CompositeModel -The Scene graph shows how model applications, objects, and compiled value +The CompositeModel graph shows how model applications, objects, and compiled value bindings fit together before a simulation runs. Use the static visualizer when you want an inspectable HTML artifact, and the interactive editor when you want -browser actions to update a Julia [`Scene`](@ref). +browser actions to update a Julia [`CompositeModel`](@ref). -## A Small Scene +## A Small CompositeModel This example applies three toy models to one plant object. The compiler infers the same-object `TT_cu` and `LAI` bindings from the declared input and output @@ -19,7 +19,7 @@ names. using PlantSimEngine using PlantSimEngine.Examples -scene = Scene( +model = CompositeModel( ToyDegreeDaysCumulModel(), ToyLAIModel(), Beer(0.6); @@ -29,7 +29,7 @@ scene = Scene( kind=:plant, ) -view = scene_graph_view(scene) +view = model_graph_view(model) view.metadata ``` @@ -38,14 +38,14 @@ embeds it below. ```@raw html ``` @@ -53,7 +53,7 @@ The default **Applications** projection groups all concrete executions of one application into one card. Use **Objects** to inspect topology and **Executions** to inspect concrete `(application, object)` pairs. Search, diagnostics, initialization, selectors, parameters, and resolved edge details remain -available in the static viewer. The topology projection includes scene and +available in the static viewer. The topology projection includes model and instance containers; selecting an instance or object subtree scopes the application and execution projections until the filter is cleared. @@ -63,7 +63,7 @@ The static visualizer is part of PlantSimEngine core and does not load a web server: ```julia -path = write_scene_graph_view("scene-graph.html", scene) +path = write_model_graph_view("model-graph.html", model) ``` The output bundles the graph payload, JavaScript, and CSS in one HTML file. It @@ -73,7 +73,7 @@ package can generate the file from `docs/make.jl` and place it under ```julia mkpath(joinpath(@__DIR__, "src", "assets")) -write_scene_graph_view( +write_model_graph_view( joinpath(@__DIR__, "src", "assets", "default_scene.html"), default_scene(), ) @@ -99,38 +99,38 @@ Then start a session: using PlantSimEngine using HTTP -session = edit_graph(scene) +session = edit_graph(model) ``` The default browser opens automatically. The returned session also prints its URL and shutdown command. Julia remains authoritative: browser edits are sent -as semantic commands, applied transactionally to a candidate Scene, compiled, +as semantic commands, applied transactionally to a candidate CompositeModel, compiled, and returned as a fresh graph state. Inspect the current result or stop the server with: ```julia -edited_scene = current_scene(session) +edited_scene = current_model(session) close(session) ``` -Call `edit_graph()` without a Scene to start from an empty scenario. Use +Call `edit_graph()` without a CompositeModel to start from an empty scenario. Use `open_browser=false` on remote machines or when a test controls the browser. ## What Can Be Edited The editor supports: -- scene objects, metadata, status initialization, and parent topology; +- model objects, metadata, status initialization, and parent topology; - model applications, constructor parameters, target selectors, and cadence; - explicit value bindings, hard calls, output routing, and update ordering; - shared template applications plus instance-level and object-level overrides; - dependency cycles through an explicit `PreviousTimeStep` break action; -- undo, redo, temporary recovery autosaves, and readable Julia Scene scripts. +- undo, redo, temporary recovery autosaves, and readable Julia CompositeModel scripts. Application target and binding dialogs can ask Julia to preview the concrete objects selected by a declaration. This is important for `Many`, relative -scopes such as `SelfPlant`, and scenes containing several plant instances. +scopes such as `SelfPlant`, and composite models containing several plant instances. ## Models From Other Packages @@ -143,19 +143,19 @@ using PlantSimEngine using PlantBiophysics using HTTP -session = edit_graph(scene) +session = edit_graph(model) ``` The `+` buttons next to ports use exact declared variable names only. For an input named `LAI`, the editor lists loaded models whose `outputs_` contains `LAI`. For an output named `LAI`, it lists models whose `inputs_` contains -`LAI`, as well as compatible applications already present in the Scene. This is +`LAI`, as well as compatible applications already present in the CompositeModel. This is a composition aid, not a scientific compatibility inference. -When the Scene is saved as Julia code, required package imports are emitted for -the model types used by the Scene. +When the CompositeModel is saved as Julia code, required package imports are emitted for +the model types used by the CompositeModel. -## Invalid And Cyclic Scenes +## Invalid And Cyclic Composite Models Simulation compilation remains strict, but graph compilation preserves as much structure as possible and attaches diagnostics. This lets the editor display @@ -174,9 +174,9 @@ the application input policy for every target selected by that application. ## Saving And Recovery The **Save** action writes readable Julia code whose final binding is -`scene = Scene(...)`. Once a path is selected, every successful edit rewrites +`model = CompositeModel(...)`. Once a path is selected, every successful edit rewrites that file. The editor also keeps a temporary recovery file and lists recent -Scene scripts in **Open**. +CompositeModel scripts in **Open**. Generated code is best effort for arbitrary Julia values and external runtime resources. Review the code and keep important scenario scripts under Git. diff --git a/docs/src/guides/modelers/port_existing_model.md b/docs/src/guides/modelers/port_existing_model.md index 93eb8ae48..0da6897e7 100644 --- a/docs/src/guides/modelers/port_existing_model.md +++ b/docs/src/guides/modelers/port_existing_model.md @@ -20,21 +20,21 @@ function PlantSimEngine.run!(m::DocsLAIGrowth, models, status, meteo, constants, status.lai_next = status.lai + m.rate * meteo.T end -scene = Scene( +model = CompositeModel( DocsLAIGrowth(0.02); status=(lai=1.0,), environment=(T=10.0, duration=Day(1)), ) -simulation = run!(scene) -@assert only(scene_objects(scene)).status.lai_next == 1.2 +simulation = run!(model) +@assert only(model_objects(model)).status.lai_next == 1.2 ``` Test the scientific function first, then the kernel directly with a `Status`, -and finally the same model through a scene. These three levels separate a +and finally the same model through a model. These three levels separate a scientific error from a model-contract error and a scenario-binding error. -`explain_initialization(scene)` should show `lai` as supplied, `T` as +`explain_initialization(model)` should show `lai` as supplied, `T` as environment-bound, and `lai_next` as generated. -The concise constructor lowers to the ordinary Scene compiler; it is not a +The concise constructor lowers to the ordinary CompositeModel compiler; it is not a separate runtime. Move to explicit `Object` and `ModelSpec` construction only when the scenario needs multiple objects, selectors, or named applications. diff --git a/docs/src/guides/multiscale/concepts.md b/docs/src/guides/multiscale/concepts.md index eed732369..a59456c14 100644 --- a/docs/src/guides/multiscale/concepts.md +++ b/docs/src/guides/multiscale/concepts.md @@ -1,9 +1,9 @@ -# How Multiscale Scenes Execute +# How Multiscale Composite Models Execute One application executes once for every object selected by `AppliesTo`. State belongs to the object, while topology and labels belong to the scenario. `Self()` is the current object, `SelfPlant()` is its plant-instance root, and -`SceneScope()` is scene-wide. Cardinality wrappers decide whether zero, one, +`SceneScope()` is model-wide. Cardinality wrappers decide whether zero, one, or many matches are valid. More objects mean more qualified streams. Removing an object stops future @@ -22,10 +22,10 @@ Canonical selector patterns are: `Self()` always means the current target object. It never implicitly means the model, process, species, or plant. Prefer object IDs and labels for identity, -and use `Scope(name)` only when the scene explicitly defines that scope. +and use `Scope(name)` only when the model explicitly defines that scope. One application produces a separate stream for every selected object and output variable. Stream keys also include application identity, so repeated applications of the same process cannot overwrite each other. Use -`explain_scene_applications`, `explain_objects`, and `explain_bindings` to +`explain_applications`, `explain_objects`, and `explain_bindings` to verify target and source multiplicities before a long run. diff --git a/docs/src/guides/multiscale/from_one_object.md b/docs/src/guides/multiscale/from_one_object.md index d6857f58b..3ebfa4ca1 100644 --- a/docs/src/guides/multiscale/from_one_object.md +++ b/docs/src/guides/multiscale/from_one_object.md @@ -1,6 +1,6 @@ -# From One Object To A Multiscale Scene +# From One Object To A Multiscale CompositeModel -First run all models on one object with the concise `Scene(models...; +First run all models on one object with the concise `CompositeModel(models...; status=...)` constructor. Then move organ-specific applications to leaf objects and aggregate their outputs on a plant with `Many(..., within=SelfPlant())` or an instance-local selector. diff --git a/docs/src/guides/multiscale/import_mtg.md b/docs/src/guides/multiscale/import_mtg.md index 7db251d2a..a5c01cb57 100644 --- a/docs/src/guides/multiscale/import_mtg.md +++ b/docs/src/guides/multiscale/import_mtg.md @@ -1,10 +1,10 @@ # Importing An MTG -`objects_from_mtg(root)` converts MTG topology and labels into ordinary scene -objects. `Scene(root; applications=...)` performs the same adaptation and then -uses the normal Scene compiler. The MTG is an input representation, not a +`objects_from_mtg(root)` converts MTG topology and labels into ordinary model +objects. `CompositeModel(root; applications=...)` performs the same adaptation and then +uses the normal CompositeModel compiler. The MTG is an input representation, not a second runtime. -For growth, prefer `add_organ!`: it creates the MTG node, applies the scene's +For growth, prefer `add_organ!`: it creates the MTG node, applies the model's status policy, attaches status, and registers the corresponding object. diff --git a/docs/src/guides/multiscale/manual_calls.md b/docs/src/guides/multiscale/manual_calls.md index 1e543ec47..48ed4233f 100644 --- a/docs/src/guides/multiscale/manual_calls.md +++ b/docs/src/guides/multiscale/manual_calls.md @@ -2,7 +2,7 @@ Declare parent-owned execution with `Calls(:name => One(...))` or `Calls(:name => Many(...))`. In the kernel, obtain the resolved target from the -`SceneRunContext` and invoke `run_call!`. A target used only by calls is absent +`RunContext` and invoke `run_call!`. A target used only by calls is absent from root scheduling. Explicit target cadence must match the caller. A target without an explicit diff --git a/docs/src/guides/multiscale/visualizing_structure.md b/docs/src/guides/multiscale/visualizing_structure.md index 1be756575..1651d1679 100644 --- a/docs/src/guides/multiscale/visualizing_structure.md +++ b/docs/src/guides/multiscale/visualizing_structure.md @@ -1,4 +1,4 @@ -# Visualizing Scene Structure +# Visualizing Composite model Structure Use `explain_objects`, `explain_scopes`, and `explain_instances` to obtain stable rows for plotting. Draw nodes by object ID and edges from parent diff --git a/docs/src/guides/time/hourly_daily_weekly.md b/docs/src/guides/time/hourly_daily_weekly.md index f57201b4f..35e04abf5 100644 --- a/docs/src/guides/time/hourly_daily_weekly.md +++ b/docs/src/guides/time/hourly_daily_weekly.md @@ -31,7 +31,7 @@ PlantSimEngine.outputs_(::DocsDailyTotal) = (total=0.0,) PlantSimEngine.run!(::DocsDailyTotal, models, status, meteo, constants, extra) = (status.total = sum(status.fluxes)) -scene = Scene( +model = CompositeModel( Object(:plant; scale=:Plant), Object(:leaf_1; scale=:Leaf, parent=:plant, status=Status(rate=1.0)), Object(:leaf_2; scale=:Leaf, parent=:plant, status=Status(rate=2.0)); @@ -47,8 +47,8 @@ scene = Scene( ), environment=[(duration=Hour(1),) for _ in 1:25], ) -simulation = run!(scene; steps=25) -@assert only(object.status.total for object in scene_objects(scene) +simulation = run!(model; steps=25) +@assert only(object.status.total for object in model_objects(model) if object.id == ObjectId(:plant)) == 72.0 ``` diff --git a/docs/src/index.md b/docs/src/index.md index d83ba9dfe..24f198856 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -17,10 +17,10 @@ Depth = 4 ``` !!! warning "Configuration API migration" - New multiscale, multi-plant, soil, scene, and microclimate scenarios should - use the unified `Scene`/`Object` API with `AppliesTo`, `Inputs`, `Calls`, + New multiscale, multi-plant, soil, model, and microclimate scenarios should + use the unified `CompositeModel`/`Object` API with `AppliesTo`, `Inputs`, `Calls`, `Updates`, `TimeStep`, and `Environment`. See - [Migrating To The Scene/Object API](migration_scene_object.md). Superseded + [Migrating To The CompositeModel/Object API](migration_composite_model.md). Superseded mapping constructors and their implementation have been removed. ## Overview @@ -28,12 +28,12 @@ Depth = 4 `PlantSimEngine` is a Julia framework for building soil-plant-atmosphere simulations from small process models. A modeler writes reusable kernels with `inputs_`, `outputs_`, optional dependency traits, and `run!`. A simulation -author then assembles those kernels on objects in a `Scene`. +author then assembles those kernels on objects in a `CompositeModel`. The current public scenario API is organized around: ```julia -Scene +CompositeModel Object ModelSpec AppliesTo @@ -47,14 +47,14 @@ Environment ### Models And Applications A model is a reusable implementation of a process. A model application is one -configured use of that model in a scene: it gives the use a name, selects its +configured use of that model in a model: it gives the use a name, selects its target objects, and configures its inputs, calls, timestep, and environment. | Concept | Meaning | |:--|:--| | Process | The biological or physical operation, such as light interception | | Model | An implementation of that process, such as `Beer` | -| Application | One configured use of a model in a `Scene` | +| Application | One configured use of a model in a `CompositeModel` | | Target | An object on which that application executes | One application can target many objects. The same model can also be used in @@ -63,7 +63,7 @@ During compilation, PlantSimEngine resolves each application into its concrete `(application, object)` executions. This means the same model can be reused on one object, many leaves, several -plant species, a shared soil object, or a scene-scale energy-balance solver +plant species, a shared soil object, or a model-scale energy-balance solver without changing the model implementation. ## Why PlantSimEngine? @@ -73,12 +73,12 @@ without changing the model implementation. - **Explicit coupling**: `Inputs(...)` declares value dependencies, while `Calls(...)` gives iterative parent solvers manual control over hard model calls. -- **Object-based multiscale scenes**: scales are labels on objects, so a plant +- **Object-based multiscale composite models**: scales are labels on objects, so a plant can be described as plants, axes, internodes, leaves, roots, voxels, or any topology the model requires. - **Multirate execution**: use `TimeStep(Dates.Hour(1))`, `TimeStep(Dates.Day(1))`, and temporal policies such as `Integrate()` or - `HoldLast()` in the same scene. + `HoldLast()` in the same model. - **Automatic environment binding**: global weather and spatial microclimate backends are bound through `Environment(...)` and model `meteo_inputs_` / `meteo_outputs_` traits. @@ -104,15 +104,15 @@ Use it from Julia with: using PlantSimEngine ``` -## Quickstart: One Scene Object +## Quickstart: One CompositeModel Object -This example runs three existing toy models on one scene object: +This example runs three existing toy models on one model object: 1. `ToyDegreeDaysCumulModel` computes daily thermal time. 2. `ToyLAIModel` consumes cumulative thermal time and computes LAI. 3. `Beer` consumes LAI and meteorology to compute absorbed PAR. -The model kernels are unchanged; the scene application layer says where they +The model kernels are unchanged; the model application layer says where they run. Since no `TimeStep` is specified, these applications use the daily cadence of `meteo_day`. @@ -125,14 +125,14 @@ meteo_day = read_weather( duration=Dates.Day, ) -scene = Scene( +model = CompositeModel( ToyDegreeDaysCumulModel(), ToyLAIModel(), Beer(0.6); environment=meteo_day, ) -sim = run!(scene; steps=30, outputs=:all) +sim = run!(model; steps=30, outputs=:all) out = collect_outputs(sim; sink=DataFrame) first(out, 6) ``` @@ -143,7 +143,7 @@ bindings from each model's declared inputs and outputs: ```@example readme select( - DataFrame(explain_bindings(scene)), + DataFrame(explain_bindings(model)), :application_id, :input, :source_application_ids, @@ -173,10 +173,10 @@ fig ## Multi-Object Inputs Use `Inputs(...)` when a model needs values from selected objects. Here the -scene-scale LAI model reads live references to all plant surfaces in the scene: +model-scale LAI model reads live references to all plant surfaces in the model: ```@example readme -plant_scene = Scene( +plant_scene = CompositeModel( Object(:scene; scale=:Scene, kind=:scene), Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene, status=Status(surface=12.0)), @@ -196,20 +196,20 @@ plant_scene = Scene( ) run!(plant_scene) -scene_status = only(scene_objects(plant_scene; scale=:Scene)).status +scene_status = only(model_objects(plant_scene; scale=:Scene)).status scene_status ``` The same `Many(...)` selector would be plant-local if the consumer ran on a plant and used `within=Subtree()`. This is the same mechanism used for plant -allocation models that sum their own leaves, scene models that aggregate all +allocation models that sum their own leaves, model models that aggregate all plants, and microclimate solvers that select objects inside one environment cell. ## Manual Calls For Iterative Solvers Use `Calls(...)` when a parent model must directly run another model, for -example a scene energy-balance solver that iterates leaf temperatures until +example a model energy-balance solver that iterates leaf temperatures until convergence: ```julia @@ -239,11 +239,11 @@ environment writes are published exactly once. ## Where To Go Next -- [Scene/Object Quickstart](scene_object/quickstart.md) gives a compact +- [CompositeModel/Object Quickstart](composite_model/quickstart.md) gives a compact runnable workflow using the new API. -- [Migrating To The Scene/Object API](migration_scene_object.md) translates +- [Migrating To The CompositeModel/Object API](migration_composite_model.md) translates historical `ModelMapping` and `MultiScaleModel` examples. -- [Public API](API/API_public.md) lists the scene/object constructors, +- [Public API](API/API_public.md) lists the composite-model/object constructors, selectors, lifecycle hooks, and explanation helpers. - [Model traits](model_traits.md) explains `inputs_`, `outputs_`, `dep`, `timespec`, `output_policy`, `meteo_inputs_`, and `meteo_outputs_`. @@ -257,13 +257,13 @@ hundreds of microseconds, and PlantBiophysics.jl models using PlantSimEngine have been measured much faster than equivalent implementations in typical scientific scripting languages. -For performance-sensitive scenes, inspect the supported structured +For performance-sensitive composite models, inspect the supported structured explanations: ```julia -explain_bindings(scene) -explain_schedule(scene) -explain_execution_plan(scene) +explain_bindings(model) +explain_schedule(model) +explain_execution_plan(model) ``` These helpers expose resolved objects, carriers, copy/reference semantics, diff --git a/docs/src/introduction/why_plantsimengine.md b/docs/src/introduction/why_plantsimengine.md index ba99485ff..6813f1051 100644 --- a/docs/src/introduction/why_plantsimengine.md +++ b/docs/src/introduction/why_plantsimengine.md @@ -82,7 +82,7 @@ PlantSimEngine's approach to plant modeling represents a paradigm shift in how s - **Automatic Dependency Resolution:** The system automatically determines the relationships between different models and processes, eliminating the need for manual coupling. -- **Concrete Batched Execution:** The scene compiler groups compatible object +- **Concrete Batched Execution:** The model compiler groups compatible object targets into concrete execution batches. A public parallel or distributed executor is not currently provided; parallel execution remains planned work that requires explicit correctness and independence guarantees. diff --git a/docs/src/migration_scene_object.md b/docs/src/migration_composite_model.md similarity index 84% rename from docs/src/migration_scene_object.md rename to docs/src/migration_composite_model.md index b9ece9974..2a7b8f049 100644 --- a/docs/src/migration_scene_object.md +++ b/docs/src/migration_composite_model.md @@ -1,8 +1,8 @@ -# Migrating To The Scene/Object API +# Migrating To The CompositeModel/Object API -## Refining early Scene/Object code +## Refining early CompositeModel/Object code -The stabilized public surface makes several early Scene/Object behaviors +The stabilized public surface makes several early CompositeModel/Object behaviors explicit: | Early spelling or behavior | Stabilized API | @@ -12,7 +12,7 @@ explicit: | `tracked_outputs=requests` | `outputs=requests` | | `OutputRequest(:Leaf, :x; process=:p)` | `OutputRequest(Many(scale=:Leaf), :x; application=:app)` | | repeated unnamed applications gained numbered IDs | name every repeated application with `ModelSpec(...; name=...)` | -| calling `run!(scene)` again implicitly looked like continuation | use `continue!(simulation)` or `step!(simulation)` | +| calling `run!(model)` again implicitly looked like continuation | use `continue!(simulation)` or `step!(simulation)` | | compiler/cache types imported from the default namespace | qualify them through `PlantSimEngine.Advanced` | `tracked_outputs` remains a targeted deprecation bridge. It maps `nothing` to @@ -26,25 +26,25 @@ collecting several applications, such as the mounted applications from several instances. New singular scenario references should use `application=`. The deprecated bridges are scheduled for removal in PlantSimEngine 0.15. -Calling `run!(scene; ...)` always creates a fresh result timeline starting at +Calling `run!(model; ...)` always creates a fresh result timeline starting at step one, even when object status has already been mutated by an earlier run. Continue the same timeline, environment position, temporal histories, and multirate phase with: ```julia -simulation = run!(scene; steps=24, outputs=requests) +simulation = run!(model; steps=24, outputs=requests) continue!(simulation; steps=24) step!(simulation) @assert current_step(simulation) == 49 ``` -The scene/object API replaces the historical multiscale mapping system with +The composite-model/object API replaces the historical multiscale mapping system with one object-address graph. New scenario code should be organized around: ```julia -Scene +CompositeModel Object ModelSpec AppliesTo @@ -55,7 +55,7 @@ TimeStep Environment ``` -Model implementations do not need to know about scenes, plants, objects, or +Process-model implementations do not need to know about composite models, plants, objects, or timesteps. They keep the existing kernel contract: ```julia @@ -67,19 +67,19 @@ meteo_outputs_(model) run!(model, models, status, meteo, constants, extra) ``` -This page maps the legacy configuration concepts to their scene/object +This page maps the legacy configuration concepts to their composite-model/object equivalents. ## Scenario Structure Legacy simulations split configuration between `ModelMapping` and -`MultiScaleModel`. The unified API stores runtime entities in one `Scene`: +`MultiScaleModel`. The unified API stores runtime entities in one `CompositeModel`: `ModelMapping` has been removed. Historical code must be translated to the -scene/object form below. +composite-model/object form below. ```julia -scene = Scene( +model = CompositeModel( Object(:scene; scale=:Scene, kind=:scene), Object(:plant_1; scale=:Plant, kind=:plant, parent=:scene), Object(:leaf_1; scale=:Leaf, kind=:plant, parent=:plant_1), @@ -104,7 +104,7 @@ leaves, roots, fruits, or application-specific objects. An existing MTG can be adapted without rebuilding its topology manually: ```julia -scene = Scene( +model = CompositeModel( mtg; applications=applications, environment=meteo, @@ -115,7 +115,7 @@ scene = Scene( ``` `objects_from_mtg(mtg; ...)` exposes the intermediate object list when it is -useful to inspect or modify labels before constructing the scene. By default, +useful to inspect or modify labels before constructing the model. By default, the adapter uses MTG node ids and scales, and reuses an existing `:plantsimengine_status` attribute when present. @@ -149,7 +149,7 @@ ModelSpec(AllocationModel(); name=:allocation) |> `Self()` selects only the object where the consumer runs. A plant-scale allocation model uses `Subtree()` to read leaves below that plant. Use -`SceneScope()` for scene-wide aggregation and `SelfPlant()` to select the +`SceneScope()` for model-wide aggregation and `SelfPlant()` to select the nearest containing plant and its subtree from an organ. Same-object renaming uses the same syntax: @@ -167,7 +167,7 @@ Inputs( Same-rate bindings use shared references or reference vectors when possible. Cross-rate bindings use typed temporal streams. -## Scene-Wide Values +## CompositeModel-Wide Values Use an input selector on the consuming application: @@ -238,11 +238,11 @@ state must use `publish=true`. ## Multiple Plants And Species -Represent repeated plant configurations with `ObjectTemplate` and +Represent repeated plant configurations with `CompositeModelTemplate` and `ObjectInstance`. ```julia -oil_palm = ObjectTemplate( +oil_palm = CompositeModelTemplate( ( ModelSpec(LeafEnergy()) |> AppliesTo(Many(scale=:Leaf)), @@ -275,7 +275,7 @@ palm_2 = ObjectInstance( objects=(Object(:palm_2_leaf_1; scale=:Leaf, parent=:plant_2),), ) -scene = Scene( +model = CompositeModel( Object(:scene; scale=:Scene, kind=:scene), palm_1, palm_2, @@ -314,15 +314,15 @@ Use `HoldLast()`, `Interpolate()`, `Integrate()`, or `Aggregate()` according to the physical meaning of the input. `PreviousTimeStep(:x) => selector` expresses an explicit lag and breaks a same-timestep dependency cycle. -If the input selector omits `policy=...`, the scene compiler uses the +If the input selector omits `policy=...`, the model compiler uses the producer's `output_policy(...)` trait for the selected source variable when the publisher is unique. An explicit selector policy always wins over the trait. -If a model defines `timespec(::Type{<:MyModel})`, the scene scheduler uses that +If a model defines `timespec(::Type{<:MyModel})`, the model scheduler uses that cadence when the application has no explicit `TimeStep(...)`. A scenario-level `TimeStep(...)` always wins over the model trait. -If the clock falls back to the scene base step, `timestep_hint(...)` required +If the clock falls back to the model base step, `timestep_hint(...)` required bounds are validated against that base step. The hint is a compatibility constraint, not a scheduling override. @@ -346,7 +346,7 @@ the writer order. ## Environment And Microclimate Models declare environment variables with `meteo_inputs_` and -`meteo_outputs_`. The scene binds each object to the active environment +`meteo_outputs_`. The model binds each object to the active environment backend: ```julia @@ -367,13 +367,13 @@ ModelSpec(LeafGasExchange(); name=:gas_exchange) |> Environment(provider=:global, sources=(CO2=:Ca,)) ``` -The model still declares and reads `CO2`; the scene samples `Ca` from the +The model still declares and reads `CO2`; the model samples `Ca` from the active environment backend and exposes it to the model as `meteo.CO2`. `explain_environment_bindings(...)` reports both `required_inputs` and `source_inputs`, so remapped meteorology is visible to users and agents. Model authors can also provide default source remaps with -`meteo_hint(::Type{<:Model}) = (bindings=(CO2=(source=:Ca,),),)`. Scene +`meteo_hint(::Type{<:Model}) = (bindings=(CO2=(source=:Ca,),),)`. CompositeModel applications use those defaults when the scenario does not provide an explicit source for the same variable. `Environment(; sources=...)` remains the scenario-level override. @@ -403,23 +403,23 @@ then reused for all selected leaves. Use the public lifecycle operations: ```julia -register_object!(scene, new_leaf; parent=:plant_1) +register_object!(model, new_leaf; parent=:plant_1) new_leaf_status = add_organ!( parent_node, - scene, + model, :+, :Leaf, 3; initial_status=(biomass=0.0,), ) -remove_object!(scene, :old_leaf) -reparent_object!(scene, :leaf_2, :axis_3) -move_object!(scene, :leaf_3, new_geometry) +remove_object!(model, :old_leaf) +reparent_object!(model, :leaf_2, :axis_3) +move_object!(model, :leaf_3, new_geometry) ``` For MTG-backed growth, prefer `add_organ!`: it creates the MTG node and its -scene object together and reuses the status initialization policy from -`Scene(mtg; status=...)`. Use `register_object!` when adapting another topology +model object together and reuses the status initialization policy from +`CompositeModel(mtg; status=...)`. Use `register_object!` when adapting another topology backend or when a complete `Object` already exists. Structural changes refresh application targets, input carriers, call targets, @@ -435,8 +435,8 @@ batches. ## Output Collection -`run!(scene; steps=...)` returns a `SceneSimulation`. Use -`scene_outputs(sim)` for the retained typed streams, `explain_outputs(sim)` for +`run!(model; steps=...)` returns a `Simulation`. Use +`outputs(sim)` for the retained typed streams, `explain_outputs(sim)` for structured diagnostics, and `collect_outputs(sim)` for tabular rows. ```julia @@ -449,14 +449,14 @@ request = OutputRequest( clock=Day(1), ) -sim = run!(scene; steps=48, outputs=request) +sim = run!(model; steps=48, outputs=request) daily = collect_outputs(sim, :leaf_transpiration_daily) ``` -Scene output requests are materialized from retained temporal streams after +CompositeModel output requests are materialized from retained temporal streams after the run. They use the same temporal policies as multirate inputs and export dynamic objects only over the interval where that object published samples. -If several scene applications implement the same process, add +If several model applications implement the same process, add `application=:application_name` to select one explicitly. This is also the way to request a named `:stream_only` publisher. `outputs=:none` retains no user streams. Passing explicit requests retains only @@ -471,16 +471,16 @@ histories for post-run export. Export is not yet a fully online path. Use structured explanations instead of inspecting internal dictionaries: ```julia -explain_objects(scene) -explain_instances(scene) -explain_scopes(scene) -explain_scene_applications(scene) -explain_bindings(scene) -explain_calls(scene) -explain_environment_bindings(scene) -explain_schedule(scene) -explain_writers(scene) -explain_model_bundles(scene) +explain_objects(model) +explain_instances(model) +explain_scopes(model) +explain_applications(model) +explain_bindings(model) +explain_calls(model) +explain_environment_bindings(model) +explain_schedule(model) +explain_writers(model) +explain_model_bundles(model) ``` These functions return structured rows with concrete object ids, application @@ -489,9 +489,9 @@ targets. They are intended for both users and coding agents. ## Migration Table -| Legacy configuration | Scene/object replacement | +| Legacy configuration | CompositeModel/object replacement | | --- | --- | -| `ModelMapping` scale assembly | `Scene` objects plus model applications | +| `ModelMapping` scale assembly | `CompositeModel` objects plus model applications | | `MultiScaleModel(...)` | consumer `Inputs(...)` | | `TimeStepModel(...)` | `TimeStep(...)` | | `InputBindings(...)` | source, policy, and window on `Inputs(...)` | @@ -500,6 +500,6 @@ targets. They are intended for both users and coding agents. | `SameScale()` rename | `Inputs(:local => One(within=Self(), var=:source))` | The executable MAESPA migration in -`examples/maespa_scene_example.jl` demonstrates two plant species, shared -soil, plant-local aggregation, scene-wide iterative energy balance, hourly and +`examples/maespa_model_example.jl` demonstrates two plant species, shared +soil, plant-local aggregation, model-wide iterative energy balance, hourly and daily models, and automatic environment binding. diff --git a/docs/src/model_execution.md b/docs/src/model_execution.md index c62dd5824..ed2f2b5fb 100644 --- a/docs/src/model_execution.md +++ b/docs/src/model_execution.md @@ -1,13 +1,13 @@ # Model Execution -This page describes how the native scene/object runtime executes model +This page describes how the native composite-model/object runtime executes model applications. Use this path for new multi-object, multi-plant, soil, microclimate, and multirate simulations. The public configuration surface is: ```julia -Scene +CompositeModel Object ModelSpec AppliesTo @@ -18,7 +18,7 @@ TimeStep Environment ``` -Scenarios start from `Scene` and model applications. +Scenarios start from `CompositeModel` and model applications. ## Model Kernels And Applications @@ -32,7 +32,7 @@ A model kernel is still an ordinary PlantSimEngine model: - `run!(model, models, status, meteo, constants, extra)` contains the model equations. -The scene/object layer does not change that kernel contract. It adds a +The composite-model/object layer does not change that kernel contract. It adds a scenario-specific application around the kernel: ```julia @@ -50,7 +50,7 @@ provider is bound to it. The model implementation stays reusable. ## Compilation Before Runtime -Before the timestep loop, PlantSimEngine compiles the scene into concrete +Before the timestep loop, PlantSimEngine compiles the model into concrete runtime carriers: 1. `AppliesTo(...)` selectors are resolved to stable object ids. @@ -73,13 +73,13 @@ compiled indexes and carriers. Useful inspection helpers: ```julia -explain_scene_applications(scene) -explain_bindings(scene) -explain_calls(scene) -explain_environment_bindings(scene) -explain_schedule(scene) -explain_execution_plan(scene) -explain_writers(scene) +explain_applications(model) +explain_bindings(model) +explain_calls(model) +explain_environment_bindings(model) +explain_schedule(model) +explain_execution_plan(model) +explain_writers(model) ``` These explanations are intended for both users and agents. They report the @@ -106,7 +106,7 @@ ModelSpec(SceneLAI(ground_area); name=:scene_lai) |> ``` For same-rate inputs, the runtime installs a reference carrier into the -consumer status during compilation. A scene-scale model reading all leaf areas +consumer status during compilation. A model-scale model reading all leaf areas therefore sees a `RefVector`-like object: reading pulls current values from source leaf statuses, and writing through the carrier mutates source refs when the carrier supports it. @@ -167,7 +167,7 @@ do not publish temporal samples or scatter mutable environment outputs. Call `run_call!(target; publish=true)` once for the accepted state. Applications selected only by `Calls(...)` are marked manual-call-only in -`explain_schedule(scene)` and are skipped by the root `run!(scene)` loop. +`explain_schedule(model)` and are skipped by the root `run!(model)` loop. ## Duplicate Writers With Updates @@ -185,10 +185,10 @@ ModelSpec(LeafPruning(); name=:leaf_pruning) |> ``` This keeps ordinary duplicate outputs as errors while allowing cases such as -allocation followed by pruning. `explain_writers(scene)` reports writer +allocation followed by pruning. `explain_writers(model)` reports writer groups and the `Updates(...)` declarations that validate them. The `after` value is the canonical application identifier shown by -`explain_scene_applications(scene)`, not the process name. +`explain_applications(model)`, not the process name. ## Multirate Execution @@ -218,11 +218,11 @@ Clock precedence is: 1. explicit `TimeStep(...)` on the `ModelSpec`; 2. non-default `timespec(model)` trait; -3. the scene environment base step. +3. the model environment base step. `timestep_hint(model)` is a compatibility constraint and explanation hint. It does not silently choose a clock. If a model uses the environment base step and -that step violates `timestep_hint.required`, scene compilation errors. +that step violates `timestep_hint.required`, model compilation errors. Temporal input policy precedence is: @@ -275,16 +275,16 @@ model-to-model temporal values. ## Running And Outputs -Run a scene with: +Run a model with: ```julia -sim = run!(scene; steps=30) +sim = run!(model; steps=30) ``` -The returned `SceneSimulation` contains the mutated scene, compiled bindings, +The returned `Simulation` contains the mutated model, compiled bindings, environment bindings, execution plan, and retained temporal output streams. -By default, scene runs retain no user output streams. Pass `outputs=:all` to +By default, model runs retain no user output streams. Pass `outputs=:all` to retain every published stream, or pass `OutputRequest` values to retain only selected outputs and required temporal dependency streams: @@ -298,7 +298,7 @@ request = OutputRequest( clock=Dates.Day(1), ) -sim = run!(scene; steps=72, outputs=request) +sim = run!(model; steps=72, outputs=request) collect_outputs(sim, :leaf_assimilation_daily; sink=nothing) explain_output_retention(sim) ``` @@ -311,7 +311,7 @@ application directly and can also request an explicitly named `outputs=:none` retains no user output streams. Histories required by temporal dependencies are still maintained with bounded retention. -`run!(scene; ...)` always starts a fresh result timeline. Continue an existing +`run!(model; ...)` always starts a fresh result timeline. Continue an existing simulation without resetting its step index, environment position, multirate phase, or temporal histories with: @@ -331,14 +331,14 @@ for full-history streams. ## Lifecycle Changes -Scene objects may be added, removed, reparented, moved, or have their geometry +CompositeModel objects may be added, removed, reparented, moved, or have their geometry updated between or during timesteps: ```julia -register_object!(scene, Object(:new_leaf; scale=:Leaf); parent=:plant_1) +register_object!(model, Object(:new_leaf; scale=:Leaf); parent=:plant_1) leaf_status = add_organ!( parent_node, - scene, + model, :+, :Leaf, 3; @@ -346,14 +346,14 @@ leaf_status = add_organ!( attributes=(area=0.01,), initial_status=(biomass=0.0,), ) -remove_object!(scene, :old_leaf) -reparent_object!(scene, :leaf_3, :plant_2) -move_object!(scene, :leaf_4, new_geometry) -update_geometry!(scene, :leaf_5, new_geometry) +remove_object!(model, :old_leaf) +reparent_object!(model, :leaf_3, :plant_2) +move_object!(model, :leaf_4, new_geometry) +update_geometry!(model, :leaf_5, new_geometry) ``` -Use `add_organ!` for an MTG-backed scene. It creates the MTG node, initializes -and attaches its `Status` with the scene's MTG policy, registers the scene +Use `add_organ!` for an MTG-backed model. It creates the MTG node, initializes +and attaches its `Status` with the model's MTG policy, registers the model object, and invalidates the affected bindings. `register_object!` is the low-level operation for callers that already own a complete `Object`. @@ -371,8 +371,8 @@ immutable lifecycle anchors: removing or reparenting a root, or an ancestor whose subtree contains one, is rejected atomically. Ordinary descendants may still be added, removed, or reparented. -Inside a lifecycle-capable model kernel, use `runtime_scene(extra)` to obtain -the live scene. Objects created during a kernel call do not recursively execute +Inside a lifecycle-capable model kernel, use `runtime_model(extra)` to obtain +the live model. Objects created during a kernel call do not recursively execute inside that call. Structural targets, value carriers, call targets, writer validation, schedules, and output-request matches are refreshed at the next timestep boundary. Geometry-only mutations refresh affected environment @@ -383,7 +383,7 @@ removed objects. The historical mapping runtime has been removed. Translate old code as follows: -- `ModelMapping(...)` -> `Scene(...)` plus object-local `ModelSpec(...)` +- `ModelMapping(...)` -> `CompositeModel(...)` plus object-local `ModelSpec(...)` applications; - `MultiScaleModel(...)` -> `Inputs(...)`; - `InputBindings(...)` -> selector fields inside `Inputs(...)`; @@ -391,5 +391,5 @@ The historical mapping runtime has been removed. Translate old code as follows: `meteo_hint(...)`, and model/application clocks; - `TimeStepModel(...)` -> `TimeStep(...)`. -See [Migrating To The Scene/Object API](migration_scene_object.md) for worked +See [Migrating To The CompositeModel/Object API](migration_composite_model.md) for worked translation patterns. diff --git a/docs/src/planned_features.md b/docs/src/planned_features.md index 8dd0f801c..814af82d1 100644 --- a/docs/src/planned_features.md +++ b/docs/src/planned_features.md @@ -1,11 +1,11 @@ # Roadmap -PlantSimEngine now has one scene/object runtime for single-object, multiscale, +PlantSimEngine now has one composite-model/object runtime for single-object, multiscale, multi-plant, soil, microclimate, and multirate simulations. Current priorities are: -- migrate downstream model packages to `Scene`, `ObjectTemplate`, +- migrate downstream model packages to `CompositeModel`, `CompositeModelTemplate`, `ObjectInstance`, and `ModelSpec`; - strengthen type-stability and allocation tests for million-object workloads; - add broader lifecycle tests for object creation, removal, movement, and diff --git a/docs/src/prerequisites/installing_plantsimengine.md b/docs/src/prerequisites/installing_plantsimengine.md index b4a0883cf..a8358c4fa 100644 --- a/docs/src/prerequisites/installing_plantsimengine.md +++ b/docs/src/prerequisites/installing_plantsimengine.md @@ -30,7 +30,7 @@ meteo = Atmosphere( duration=Hour(1), ) -scene = Scene( +model = CompositeModel( Beer(0.5); status=(LAI=2.0,), id=:leaf, @@ -38,8 +38,8 @@ scene = Scene( environment=meteo, ) -run!(scene) -only(scene_objects(scene; scale=:Leaf)).status.aPPFD +run!(model) +only(model_objects(model; scale=:Leaf)).status.aPPFD ``` Example models are provided by the `PlantSimEngine.Examples` submodule. They diff --git a/docs/src/prerequisites/key_concepts.md b/docs/src/prerequisites/key_concepts.md index c6697701f..abef32246 100644 --- a/docs/src/prerequisites/key_concepts.md +++ b/docs/src/prerequisites/key_concepts.md @@ -20,10 +20,10 @@ The numerical kernel is implemented with: PlantSimEngine.run!(model, models, status, meteo, constants, extra) ``` -## Scenes And Objects +## Composite Models And Objects -A `Scene` contains objects and model applications. An `Object` can represent a -scene, plant, soil volume, axis, internode, leaf, sensor, or any other simulated +A `CompositeModel` contains objects and model applications. An `Object` can represent a +model, plant, soil volume, axis, internode, leaf, sensor, or any other simulated entity. PlantSimEngine does not impose one plant architecture. Objects can carry: @@ -35,8 +35,8 @@ Objects can carry: - mutable `Status`; - object-local model applications. -`ObjectTemplate` packages reusable applications for a species or object type. -`ObjectInstance` mounts the template in a scene. Several instances can share +`CompositeModelTemplate` packages reusable applications for a species or object type. +`ObjectInstance` mounts the template in a model. Several instances can share models and parameters while declaring targeted overrides for exceptional objects. @@ -53,7 +53,7 @@ objects. - `OutputRouting(...)` controls output publication. This keeps model implementations generic. Models do not need to know which -scene, object, timestep, or coupling scenario will use them. +model, object, timestep, or coupling scenario will use them. ## Soft And Manual Dependencies @@ -61,7 +61,7 @@ Ordinary dependencies are inferred by matching model inputs with outputs and are compiled into an acyclic execution order. `Inputs(...)` is used when the source is cross-object, renamed, temporal, or otherwise ambiguous. -Some algorithms need direct call-stack control. For example, a scene energy +Some algorithms need direct call-stack control. For example, a model energy balance may repeatedly call leaf energy-balance models until canopy microclimate converges. Such dependencies are bound with `Calls(...)`; the parent invokes them with `run_call!`. @@ -94,7 +94,7 @@ PlantSimEngine treats scale and object hierarchy as scenario data. A plant may use leaves directly under a plant, or axes, segments, internodes, roots, and other intermediate levels. Selectors express relationships such as one source, many descendants, the current plant, an ancestor, or all matching objects in -the scene. +the model. MultiScaleTreeGraph objects can be imported with `objects_from_mtg`, but the -runtime operates on the same scene/object representation afterward. +runtime operates on the same composite-model/object representation afterward. diff --git a/docs/src/step_by_step/advanced_coupling.md b/docs/src/step_by_step/advanced_coupling.md index ad06b90b5..dcca947e9 100644 --- a/docs/src/step_by_step/advanced_coupling.md +++ b/docs/src/step_by_step/advanced_coupling.md @@ -15,7 +15,7 @@ model reads it as an input. Some models need tighter control. For example, an energy-balance model may call photosynthesis and stomatal-conductance models several times while it iterates leaf temperature. -That second case is a manual call dependency. In the scene/object API it is +That second case is a manual call dependency. In the composite-model/object API it is declared with `Calls(...)`. ## Soft inputs and manual calls @@ -39,7 +39,7 @@ The example process models in `examples/dummy.jl` contain both patterns: scenario decides which concrete application is called. ```@example scene_advanced_coupling -complex_scene = Scene( +complex_scene = CompositeModel( Object(:scene; scale=:Scene, kind=:scene, status=Status(var0=2.0)); applications=( ModelSpec(Process4Model(); name=:prepare_inputs) |> @@ -89,7 +89,7 @@ Applications selected by `Calls(...)` are not scheduled as independent root applications under their caller. They run only when the parent calls them. This gives the parent full call-stack control. -## Running the coupled scene +## Running the coupled model The regular soft dependencies are still inferred from `inputs_` and `outputs_`. The scheduler combines those soft edges with the call ownership @@ -109,7 +109,7 @@ Run one timestep: ```@example scene_advanced_coupling complex_sim = run!(complex_scene; steps=1) -complex_status = only(scene_objects(complex_scene; scale=:Scene)).status +complex_status = only(model_objects(complex_scene; scale=:Scene)).status ( var3=complex_status.var3, var5=complex_status.var5, @@ -120,7 +120,7 @@ complex_status = only(scene_objects(complex_scene; scale=:Scene)).status ## Writing new hard-coupled models -For new scene/object models, retrieve manual-call targets from the runtime +For new composite-model/object models, retrieve manual-call targets from the runtime context and execute them explicitly: ```julia @@ -142,7 +142,7 @@ end Use `publish=true` for the accepted state so temporal streams and mutable environment outputs are published once. -The MAESPA-style example uses the same mechanism: a scene energy-balance model +The MAESPA-style example uses the same mechanism: a model energy-balance model calls all selected leaf energy-balance models and the shared soil model while it solves canopy microclimate. diff --git a/docs/src/step_by_step/detailed_first_example.md b/docs/src/step_by_step/detailed_first_example.md index 67e1a4dd2..9c1acb90d 100644 --- a/docs/src/step_by_step/detailed_first_example.md +++ b/docs/src/step_by_step/detailed_first_example.md @@ -1,6 +1,6 @@ # [Detailed Walkthrough Of A Simple Simulation](@id detailed-walkthrough-of-a-simple-simulation) -This page walks through a small scene/object simulation. It is written for +This page walks through a small composite-model/object simulation. It is written for readers who are still getting comfortable with Julia and PlantSimEngine. If you only want examples to copy and modify, see [Quick examples](quick_and_dirty_examples.md). For @@ -55,19 +55,19 @@ inputs(Beer(0.5)) outputs(Beer(0.5)) ``` -These declarations are the modeler's contract. The scene/object layer decides +These declarations are the modeler's contract. The composite-model/object layer decides where the model runs and where those values come from. -### Scene Objects +### CompositeModel Objects -A `Scene` contains simulated `Object`s. An object can represent a scene, plant, +A `CompositeModel` contains simulated `Object`s. An object can represent a model, plant, axis, leaf, soil layer, sensor, voxel, or any other simulated entity. -For a first example, we use one object representing the whole scene. The `Beer` +For a first example, we use one object representing the whole model. The `Beer` model reads `LAI`, so we initialize that variable on the object status. ```@example detailed_scene -scene = Scene( +model = CompositeModel( Beer(0.5); status=(LAI=2.0,), environment=meteo_day, @@ -75,38 +75,38 @@ scene = Scene( ) ``` -The concise constructor creates one ordinary scene object and one application +The concise constructor creates one ordinary model object and one application for each supplied model. `status` initializes that object, `timestep` applies a common daily cadence, and `environment` supplies weather values such as radiation. Use explicit `ModelSpec` and selectors when applications need different policies or targets. -## Inspecting The Compiled Scene +## Inspecting The Compiled CompositeModel -Before runtime, PlantSimEngine resolves selectors and builds a compiled scene. +Before runtime, PlantSimEngine resolves selectors and builds a compiled model. This avoids resolving object selections inside the timestep loop. ```@example detailed_scene select( - DataFrame(explain_scene_applications(scene)), + DataFrame(explain_applications(model)), :application_id, :process, :target_ids, ) ``` -`Beer` has no model-to-model value input in this first scene because `LAI` was +`Beer` has no model-to-model value input in this first model because `LAI` was initialized directly on the object status: ```@example detailed_scene -explain_bindings(scene) +explain_bindings(model) ``` The schedule tells us when each application runs: ```@example detailed_scene select( - DataFrame(explain_schedule(scene)), + DataFrame(explain_schedule(model)), :application_id, :dt_seconds, :root_scheduled, @@ -116,20 +116,20 @@ select( ## Running The Simulation -Run the scene with [`run!`](@ref): +Run the model with [`run!`](@ref): ```@example detailed_scene -sim = run!(scene; steps=3) +sim = run!(model; steps=3) ``` The object status stores the latest value: ```@example detailed_scene -scene_status = only(scene_objects(scene; scale=:Scene)).status +scene_status = only(model_objects(model; scale=:Scene)).status (LAI=scene_status.LAI, aPPFD=scene_status.aPPFD) ``` -The returned `SceneSimulation` stores retained output streams: +The returned `Simulation` stores retained output streams: ```@example detailed_scene first(collect_outputs(sim; sink=nothing), 3) @@ -148,7 +148,7 @@ runs. `ToyLAIModel` reads cumulative thermal time `TT_cu` and writes `LAI`. Because `Beer` reads `LAI`, the compiler can infer the same-object binding. ```@example detailed_scene -coupled_scene = Scene( +coupled_scene = CompositeModel( ToyDegreeDaysCumulModel(), ToyLAIModel(), Beer(0.5); @@ -170,7 +170,7 @@ select( The `LAI` binding uses a live reference carrier, so the light-interception model sees the value written by the LAI model without copying it. -Run the coupled scene: +Run the coupled model: ```@example detailed_scene coupled_sim = run!(coupled_scene; steps=5, outputs=:all) @@ -180,14 +180,14 @@ first(collect_outputs(coupled_sim), 8) The final object status contains the latest values from the coupled models: ```@example detailed_scene -coupled_status = only(scene_objects(coupled_scene; scale=:Scene)).status +coupled_status = only(model_objects(coupled_scene; scale=:Scene)).status (TT_cu=coupled_status.TT_cu, LAI=coupled_status.LAI, aPPFD=coupled_status.aPPFD) ``` ## What Needs Initialization? Model `inputs_(...)` lists every variable a model may need, but not all of -those variables need user initialization. In a coupled scene, some inputs are +those variables need user initialization. In a coupled model, some inputs are computed by upstream models. Use the compiler explanations to distinguish the two cases: @@ -196,11 +196,11 @@ Use the compiler explanations to distinguish the two cases: - a row in `explain_bindings(...)` is compiler-owned coupling; - a compile error means a required input is neither initialized nor bound. -For example, if we remove `TT_cu` from the scene status, compilation fails -because no model in this scene computes it before `ToyLAIModel` reads it: +For example, if we remove `TT_cu` from the model status, compilation fails +because no model in this model computes it before `ToyLAIModel` reads it: ```@example detailed_scene -bad_scene = Scene( +bad_scene = CompositeModel( ToyLAIModel(); environment=meteo_day, ) @@ -214,17 +214,17 @@ end ## Migration Note -The previous mapping runtime has been removed. Simulations use `Scene`, +The previous mapping runtime has been removed. Simulations use `CompositeModel`, `Object`, `ModelSpec`, `AppliesTo`, `Inputs`, `Calls`, `Updates`, `TimeStep`, and `Environment`. -See [Migrating To The Scene/Object API](../migration_scene_object.md) for the +See [Migrating To The CompositeModel/Object API](../migration_composite_model.md) for the translation from the historical mapping API. ## Next Steps - [Standard model coupling](@ref) shows more coupling patterns. -- [Scene/Object Quickstart](../scene_object/quickstart.md) is the shortest +- [CompositeModel/Object Quickstart](../composite_model/quickstart.md) is the shortest copy-pasteable path for the new API. - [Model execution](../model_execution.md) explains scheduling, temporal inputs, hard calls, output retention, and lifecycle refreshes. diff --git a/docs/src/step_by_step/model_switching.md b/docs/src/step_by_step/model_switching.md index ef353f406..2be136024 100644 --- a/docs/src/step_by_step/model_switching.md +++ b/docs/src/step_by_step/model_switching.md @@ -14,19 +14,19 @@ One main objective of PlantSimEngine is to let users switch between model implementations for a process without changing the engine or the other model kernels. -In the scene/object API, the switch happens at the model-application layer: +In the composite-model/object API, the switch happens at the model-application layer: replace the model inside a `ModelSpec`, keep the same `AppliesTo(...)` selector, and keep the same input contract when the replacement model needs the same variables. ## A first simulation -This scene computes degree-days, LAI, absorbed PAR, and growth on one scene +This model computes degree-days, LAI, absorbed PAR, and growth on one model object: ```@example scene_model_switching -function plant_scene_with_growth(growth_model; growth_name=:growth) - Scene( +function plant_model_with_growth(growth_model; growth_name=:growth) + CompositeModel( Object(:scene; scale=:Scene, kind=:scene); applications=( ModelSpec(ToyDegreeDaysCumulModel(); name=:degree_days) |> @@ -49,9 +49,9 @@ function plant_scene_with_growth(growth_model; growth_name=:growth) ) end -rue_scene = plant_scene_with_growth(ToyRUEGrowthModel(0.2)) +rue_scene = plant_model_with_growth(ToyRUEGrowthModel(0.2)) rue_sim = run!(rue_scene; steps=10) -rue_status = only(scene_objects(rue_scene; scale=:Scene)).status +rue_status = only(model_objects(rue_scene; scale=:Scene)).status (growth_model=:ToyRUEGrowthModel, biomass=rue_status.biomass) ``` @@ -73,12 +73,12 @@ select( `ToyAssimGrowthModel` implements the same `:growth` process, reads the same `aPPFD` input, and computes additional outputs such as carbon assimilation and -respiration. The rest of the scene does not need to change: +respiration. The rest of the model does not need to change: ```@example scene_model_switching -assim_scene = plant_scene_with_growth(ToyAssimGrowthModel()) +assim_scene = plant_model_with_growth(ToyAssimGrowthModel()) assim_sim = run!(assim_scene; steps=10) -assim_status = only(scene_objects(assim_scene; scale=:Scene)).status +assim_status = only(model_objects(assim_scene; scale=:Scene)).status ( growth_model=:ToyAssimGrowthModel, carbon_assimilation=assim_status.carbon_assimilation, @@ -100,10 +100,10 @@ select( ) ``` -This is the same principle used in larger scenes: switch one process +This is the same principle used in larger composite models: switch one process implementation by replacing one `ModelSpec` or by using an `ObjectInstance(...; overrides=...)` when the change applies to one plant instance or one organ. The removed mapping runtime expressed the same idea differently. New scenario -code expresses model switching through scene model applications. +code expresses model switching through model model applications. diff --git a/docs/src/step_by_step/quick_and_dirty_examples.md b/docs/src/step_by_step/quick_and_dirty_examples.md index 9847c3d34..9d3b17f4b 100644 --- a/docs/src/step_by_step/quick_and_dirty_examples.md +++ b/docs/src/step_by_step/quick_and_dirty_examples.md @@ -1,14 +1,14 @@ # Quick Examples -This page is for copy-paste experimentation with the native scene/object API. +This page is for copy-paste experimentation with the native composite-model/object API. If you want a slower explanation of the same ideas, see [Detailed Walkthrough Of A Simple Simulation](@ref detailed-walkthrough-of-a-simple-simulation). -The examples use one scene object, but the same pattern scales to plants, +The examples use one model object, but the same pattern scales to plants, organs, soil objects, and microclimate grids by adding more `Object`s and selecting them with `AppliesTo(...)` and `Inputs(...)`. -```@setup quick_scene_examples +```@setup quick_model_examples using PlantSimEngine, PlantMeteo, Dates, DataFrames using PlantSimEngine.Examples @@ -25,14 +25,14 @@ Depth = 2 ## One Light Interception Model -```@example quick_scene_examples -scene = Scene( +```@example quick_model_examples +model = CompositeModel( Beer(0.5); status=(LAI=2.0,), environment=meteo_day, ) -sim = run!(scene; steps=3, outputs=:all) +sim = run!(model; steps=3, outputs=:all) first(collect_outputs(sim), 3) ``` @@ -42,8 +42,8 @@ Here, `ToyDegreeDaysCumulModel` computes cumulative thermal time, `ToyLAIModel` computes `LAI`, and `Beer` consumes `LAI`. The compiler infers the same-object value bindings from model inputs and outputs. -```@example quick_scene_examples -lai_scene = Scene( +```@example quick_model_examples +lai_scene = CompositeModel( ToyDegreeDaysCumulModel(), ToyLAIModel(), Beer(0.5); @@ -56,7 +56,7 @@ first(collect_outputs(lai_sim), 8) Inspect the inferred coupling: -```@example quick_scene_examples +```@example quick_model_examples select( DataFrame(explain_bindings(lai_scene)), :application_id, @@ -72,8 +72,8 @@ select( input binding is needed because `Beer` is the unique producer of `aPPFD` on the same object. -```@example quick_scene_examples -growth_scene = Scene( +```@example quick_model_examples +growth_scene = CompositeModel( ToyDegreeDaysCumulModel(), ToyLAIModel(), Beer(0.5), @@ -82,7 +82,7 @@ growth_scene = Scene( ) growth_sim = run!(growth_scene; steps=5) -growth_status = only(scene_objects(growth_scene; scale=:Scene)).status +growth_status = only(model_objects(growth_scene; scale=:Scene)).status (LAI=growth_status.LAI, aPPFD=growth_status.aPPFD, biomass=growth_status.biomass) ``` @@ -90,7 +90,7 @@ growth_status = only(scene_objects(growth_scene; scale=:Scene)).status For larger simulations, request only the streams you want to keep: -```@example quick_scene_examples +```@example quick_model_examples request = OutputRequest( :Scene, :biomass; @@ -111,17 +111,17 @@ first(collect_outputs(requested_sim, :biomass_daily), 5) ## PlantBiophysics -The same scene/object API can host models from companion packages such as +The same composite-model/object API can host models from companion packages such as PlantBiophysics. A typical PlantBiophysics energy-balance setup uses `Calls(...)` so an iterative parent model can manually run photosynthesis and stomatal-conductance models, then call `run_call!(target; publish=true)` once for the accepted solution. -See [MAESPA-style scene example handoff](../dev/maespa_scene_handoff.md) for +See [MAESPA-style model example handoff](../dev/maespa_model_handoff.md) for the current multi-plant energy-balance acceptance example. ## Migration Note -The previous mapping runtime has been removed. Simulations start from `Scene`, +The previous mapping runtime has been removed. Simulations start from `CompositeModel`, `Object`, `ModelSpec`, `AppliesTo`, `Inputs`, `Calls`, `Updates`, `TimeStep`, and `Environment`. diff --git a/docs/src/step_by_step/simple_model_coupling.md b/docs/src/step_by_step/simple_model_coupling.md index 6b036fcd1..eaed4da89 100644 --- a/docs/src/step_by_step/simple_model_coupling.md +++ b/docs/src/step_by_step/simple_model_coupling.md @@ -12,7 +12,7 @@ meteo_day = read_weather( ``` This page shows the standard coupling case: one model computes a variable that -another model reads. In the scene/object API, the user describes model +another model reads. In the composite-model/object API, the user describes model applications on objects, and the compiler wires the value dependencies. ## Setting up your environment @@ -24,12 +24,12 @@ page. ## One object and one model -A scene contains objects. A model application says where a model runs. Here a -light interception model runs on the scene object, uses the environment's +A model contains objects. A model application says where a model runs. Here a +light interception model runs on the model object, uses the environment's daily cadence, and reads `LAI` from that object's status: ```@example scene_coupling -light_scene = Scene( +light_scene = CompositeModel( Beer(0.5); status=(LAI=2.0,), environment=meteo_day, @@ -43,10 +43,10 @@ first(collect_outputs(light_sim; sink=DataFrame), 3) Suppose we want `ToyLAIModel` to compute `LAI` for `Beer`. Both models can run on the same object. `ToyLAIModel` produces `LAI`, and `Beer` declares `LAI` as -an input, so the scene compiler infers the binding: +an input, so the model compiler infers the binding: ```@example scene_coupling -coupled_scene = Scene( +coupled_scene = CompositeModel( ToyDegreeDaysCumulModel(), ToyLAIModel(), Beer(0.5); @@ -68,11 +68,11 @@ The `:inferred_same_object` rows are soft dependencies: the consumer input is provided by another model output. Same-rate local links use live references, so the timestep loop does not copy values between models. -Run the coupled scene: +Run the coupled model: ```@example scene_coupling coupled_sim = run!(coupled_scene; steps=5) -coupled_status = only(scene_objects(coupled_scene; scale=:Scene)).status +coupled_status = only(model_objects(coupled_scene; scale=:Scene)).status (TT_cu=coupled_status.TT_cu, LAI=coupled_status.LAI, aPPFD=coupled_status.aPPFD) ``` @@ -83,7 +83,7 @@ consumes `aPPFD`, which is produced by `Beer`, so the compiler infers another same-object binding: ```@example scene_coupling -growth_scene = Scene( +growth_scene = CompositeModel( ToyDegreeDaysCumulModel(), ToyLAIModel(), Beer(0.5), @@ -92,10 +92,10 @@ growth_scene = Scene( ) growth_sim = run!(growth_scene; steps=5) -growth_status = only(scene_objects(growth_scene; scale=:Scene)).status +growth_status = only(model_objects(growth_scene; scale=:Scene)).status (LAI=growth_status.LAI, aPPFD=growth_status.aPPFD, biomass=growth_status.biomass) ``` Older examples used the removed mapping runtime for this workflow. New -scenarios start from `Scene`, `Object`, `ModelSpec`, `AppliesTo`, `Inputs`, +scenarios start from `CompositeModel`, `Object`, `ModelSpec`, `AppliesTo`, `Inputs`, `Calls`, and `TimeStep`. diff --git a/docs/src/tutorials/growing_plant/part1_growth.md b/docs/src/tutorials/growing_plant/part1_growth.md index cbecb9218..4a5646e19 100644 --- a/docs/src/tutorials/growing_plant/part1_growth.md +++ b/docs/src/tutorials/growing_plant/part1_growth.md @@ -1,4 +1,4 @@ -# Growing A Plant Scene +# Growing A Plant CompositeModel Begin with a plant object and leaf objects whose carbon production is gathered by a plant application through `Many(scale=:Leaf, within=Subtree())`. A growth @@ -12,7 +12,7 @@ same kernel call. Build the initial registry explicitly so ownership remains visible: ```julia -scene = Scene( +model = CompositeModel( Object(:plant; scale=:Plant, status=Status(carbon=0.0)), Object(:leaf_1; scale=:Leaf, parent=:plant, status=Status(area=1.0)); applications=(leaf_application, plant_balance, growth_application), @@ -21,12 +21,12 @@ scene = Scene( ``` The plant balance gathers leaf production with -`Many(scale=:Leaf, within=Subtree())`. The growth kernel obtains the live scene -with `runtime_scene(extra)`, checks its carbon and thermal thresholds, creates +`Many(scale=:Leaf, within=Subtree())`. The growth kernel obtains the live model +with `runtime_model(extra)`, checks its carbon and thermal thresholds, creates a fully initialized `Object`, and calls `register_object!`. It should deduct the construction cost exactly once before registration. After each step, assert both biology and structure: remaining plant carbon, the number of leaf objects, each new leaf's parent, and accepted historical -outputs. `explain_scene_applications` should show that the new leaf is absent +outputs. `explain_applications` should show that the new leaf is absent during its creation step and present after the between-step refresh. diff --git a/docs/src/tutorials/growing_plant/part2_roots_water.md b/docs/src/tutorials/growing_plant/part2_roots_water.md index 08866df6a..12ab9ee35 100644 --- a/docs/src/tutorials/growing_plant/part2_roots_water.md +++ b/docs/src/tutorials/growing_plant/part2_roots_water.md @@ -6,12 +6,12 @@ remains object-local. Environment precipitation is an environment input; root creation is an explicit `register_object!` operation with initialized status. When several plants share one soil object, select it explicitly with a -scene-wide `One` selector rather than relying on traversal order. +model-wide `One` selector rather than relying on traversal order. Keep stocks at the scale that owns conservation. A root model may publish an absorption rate per root, while the plant model integrates all root rates and updates one plant water stock. A soil model owns soil water; plants read it -through an explicit scene-wide selector. This avoids copying one stock into +through an explicit model-wide selector. This avoids copying one stock into every organ and makes duplicate writers visible. ```julia diff --git a/docs/src/tutorials/growing_plant/part3_debugging.md b/docs/src/tutorials/growing_plant/part3_debugging.md index 81afe7172..f513c2a31 100644 --- a/docs/src/tutorials/growing_plant/part3_debugging.md +++ b/docs/src/tutorials/growing_plant/part3_debugging.md @@ -12,11 +12,11 @@ ordering. Use this debugging order: -1. `explain_initialization(scene)` for missing state or environment values. -2. `explain_bindings(scene)` for source scope and multiplicity. -3. `explain_writers(scene)` for competing canonical outputs. -4. `explain_calls(scene)` for call-only targets and target cardinality. -5. `explain_schedule(scene)` for cadence and root ordering. +1. `explain_initialization(model)` for missing state or environment values. +2. `explain_bindings(model)` for source scope and multiplicity. +3. `explain_writers(model)` for competing canonical outputs. +4. `explain_calls(model)` for call-only targets and target cardinality. +5. `explain_schedule(model)` for cadence and root ordering. 6. `explain_outputs(simulation)` after execution for publication history. A trial call must not mutate accepted output history or scatter mutable @@ -25,7 +25,7 @@ Convergence and failure policy belongs to the parent model: it decides the iteration limit, tolerance, fallback, and whether any state is accepted. Structural mutation is also transactional at the timestep boundary. A new -organ is registered immediately in the scene registry but does not recursively +organ is registered immediately in the model registry but does not recursively run during the kernel that created it. Before the next timestep, compilation refreshes targets, carriers, calls, writer validation, schedules, and requested outputs. Geometry-only movement refreshes only affected spatial bindings where diff --git a/docs/src/working_with_data/fitting.md b/docs/src/working_with_data/fitting.md index 9bedc7e94..9db987efa 100644 --- a/docs/src/working_with_data/fitting.md +++ b/docs/src/working_with_data/fitting.md @@ -32,7 +32,7 @@ meteo = Atmosphere( duration=Hour(1), ) -scene = Scene( +model = CompositeModel( Beer(0.6); status=(LAI=2.0,), id=:leaf, @@ -40,8 +40,8 @@ scene = Scene( environment=meteo, ) -run!(scene) -leaf = only(scene_objects(scene; scale=:Leaf)) +run!(model) +leaf = only(model_objects(model; scale=:Leaf)) data = DataFrame( aPPFD=[leaf.status.aPPFD], LAI=[leaf.status.LAI], diff --git a/examples/Beer.jl b/examples/Beer.jl index c3eeca60f..e0924acc2 100644 --- a/examples/Beer.jl +++ b/examples/Beer.jl @@ -32,7 +32,7 @@ of light extinction. # Arguments - `::Beer`: a Beer model, from the model list (*i.e.* m.light_interception) -- `models`: the process-keyed model bundle supplied by the scene runtime. +- `models`: the process-keyed model bundle supplied by the model runtime. - `status`: the status of the model, usually the model list status (*i.e.* m.status) - `meteo`: meteorology structure, see [`Atmosphere`](https://palmstudio.github.io/PlantMeteo.jl/stable/#PlantMeteo.Atmosphere) - `constants = PlantMeteo.Constants()`: physical constants. See `PlantMeteo.Constants` for more details @@ -41,7 +41,7 @@ of light extinction. # Examples ```julia -scene = Scene( +model = CompositeModel( Beer(0.5); status=(LAI=2.0,), id=:leaf, @@ -55,8 +55,8 @@ scene = Scene( duration=Hour(1), ), ) -run!(scene) -only(scene_objects(scene; scale=:Leaf)).status.aPPFD +run!(model) +only(model_objects(model; scale=:Leaf)).status.aPPFD ``` """ function PlantSimEngine.run!(::Beer, models, status, meteo, constants, extra=nothing) @@ -100,15 +100,15 @@ using PlantSimEngine.Examples Create a model list with a Beer model, and fit it to the data: ```julia -scene = Scene( +model = CompositeModel( Beer(0.6); status=(LAI=2.0,), id=:leaf, scale=:Leaf, environment=meteo, ) -run!(scene) -leaf = only(scene_objects(scene; scale=:Leaf)) +run!(model) +leaf = only(model_objects(model; scale=:Leaf)) df = DataFrame(aPPFD=leaf.status.aPPFD, LAI=leaf.status.LAI, Ri_PAR_f=meteo.Ri_PAR_f[1]) fit(Beer, df) ``` diff --git a/examples/ToyLAIModel.jl b/examples/ToyLAIModel.jl index 07b55d787..3ca29ee5c 100644 --- a/examples/ToyLAIModel.jl +++ b/examples/ToyLAIModel.jl @@ -58,15 +58,15 @@ end # ToyLAIModel is independent of previous values and other objects. The current # public runtime remains sequential and owns execution policy. -# A second model at scene scale: +# A second model at model scale: """ ToyLAIfromLeafAreaModel() -Computes the Leaf Area Index (LAI) of the scene based on the plants leaf area. +Computes the Leaf Area Index (LAI) of the model based on the plants leaf area. # Arguments -- `scene_area`: the area of the scene, usually in m² +- `scene_area`: the area of the model, usually in m² # Inputs @@ -74,7 +74,7 @@ Computes the Leaf Area Index (LAI) of the scene based on the plants leaf area. # Outputs -- `LAI`: the Leaf Area Index of the scene, usually in m² m⁻² +- `LAI`: the Leaf Area Index of the model, usually in m² m⁻² - `total_surface`: the total surface of the plants, usually in m² """ struct ToyLAIfromLeafAreaModel{T} <: AbstractLai_DynamicModel diff --git a/examples/ToyLightPartitioningModel.jl b/examples/ToyLightPartitioningModel.jl index f8fc00c42..985e64644 100644 --- a/examples/ToyLightPartitioningModel.jl +++ b/examples/ToyLightPartitioningModel.jl @@ -11,7 +11,7 @@ Computes the light partitioning based on relative surface. # Inputs -- `aPPFD`: the absorbed photosynthetic photon flux density at the larger scale (*e.g.* scene), in mol[PAR] m⁻² time-step⁻¹ +- `aPPFD`: the absorbed photosynthetic photon flux density at the larger scale (*e.g.* model), in mol[PAR] m⁻² time-step⁻¹ # Outputs diff --git a/examples/maespa_scene_example.jl b/examples/maespa_model_example.jl similarity index 90% rename from examples/maespa_scene_example.jl rename to examples/maespa_model_example.jl index 7eb09ace2..d84bff809 100644 --- a/examples/maespa_scene_example.jl +++ b/examples/maespa_model_example.jl @@ -50,7 +50,7 @@ PlantSimEngine.run!(::LeafState, models, status, meteo, constants, extra=nothing """ LAIModel(area) -Compute scene leaf area and leaf area index from all selected leaves. +Compute model leaf area and leaf area index from all selected leaves. """ struct LAIModel{T} <: AbstractLai_DynamicModel area::T @@ -140,7 +140,7 @@ struct SceneEBSolverResult lai::Float64 end -function _scene_leaf_meteo(meteo, tair_canopy, vpd_canopy) +function _model_leaf_meteo(meteo, tair_canopy, vpd_canopy) return Atmosphere( T=tair_canopy, Rh=rh_from_vpd(tair_canopy, vpd_canopy), @@ -153,21 +153,21 @@ function _scene_leaf_meteo(meteo, tair_canopy, vpd_canopy) ) end -function _prepare_scene_leaf_target!(target, meteo, tair_canopy, vpd_canopy, psi_soil) +function _prepare_model_leaf_target!(target, meteo, tair_canopy, vpd_canopy, psi_soil) target.status.Ra_SW_f = meteo.Ri_SW_f target.status.aPPFD = meteo.Ri_PAR_f target.status.Ψₗ = psi_soil return nothing end -function _run_scene_leaf_targets!(leaf_targets, meteo, tair_canopy, vpd_canopy, psi_soil, ground_area; publish=false) +function _run_model_leaf_targets!(leaf_targets, meteo, tair_canopy, vpd_canopy, psi_soil, ground_area; publish=false) total_rn = 0.0 total_lambda_e = 0.0 total_h = 0.0 total_a = 0.0 - local_meteo = _scene_leaf_meteo(meteo, tair_canopy, vpd_canopy) + local_meteo = _model_leaf_meteo(meteo, tair_canopy, vpd_canopy) for target in leaf_targets - _prepare_scene_leaf_target!(target, meteo, tair_canopy, vpd_canopy, psi_soil) + _prepare_model_leaf_target!(target, meteo, tair_canopy, vpd_canopy, psi_soil) run_call!(target; meteo=local_meteo, publish=publish) leaf_area = target.status.leaf_area total_rn += target.status.Rn * leaf_area @@ -242,7 +242,7 @@ function tvpdcanopcalc(m::SceneEB, fluxes, meteo_above, canopy_meteo, constants) return (tair=tair_new, vpd=vpd_new, rh=rh_new, htot=htot, gcanop=gbcan_ms) end -function _solve_scene_energy_balance!(m::SceneEB, leaf_targets, soil_target, status, meteo, constants=PlantMeteo.Constants()) +function _solve_model_energy_balance!(m::SceneEB, leaf_targets, soil_target, status, meteo, constants=PlantMeteo.Constants()) tair_above = meteo.T vpd_above = max(0.01, meteo.VPD) tair_canopy = tair_above @@ -253,7 +253,7 @@ function _solve_scene_energy_balance!(m::SceneEB, leaf_targets, soil_target, sta for iter in 1:m.maxiter # Run the energy balance of each leaf, and aggregate the fluxes at the canopy scale: - fluxes = _run_scene_leaf_targets!(leaf_targets, meteo, tair_canopy, vpd_canopy, psi_soil, m.ground_area) + fluxes = _run_model_leaf_targets!(leaf_targets, meteo, tair_canopy, vpd_canopy, psi_soil, m.ground_area) fluxes = merge(fluxes, (lai=status.lai, rad_interc=0.0)) # Update the canopy-scale meteo based on the leaf fluxes, and check for convergence: final_meteo = fluxes.meteo @@ -267,7 +267,7 @@ function _solve_scene_energy_balance!(m::SceneEB, leaf_targets, soil_target, sta vpd_canopy, update.rh, psi_soil, - _scene_leaf_meteo(meteo, tair_canopy, vpd_canopy), + _model_leaf_meteo(meteo, tair_canopy, vpd_canopy), iter, update.htot, update.gcanop, @@ -285,8 +285,8 @@ function _solve_scene_energy_balance!(m::SceneEB, leaf_targets, soil_target, sta ) end -function _publish_scene_leaf_solution!(leaf_targets, solution::SceneEBSolverResult, meteo, ground_area) - fluxes = _run_scene_leaf_targets!( +function _publish_model_leaf_solution!(leaf_targets, solution::SceneEBSolverResult, meteo, ground_area) + fluxes = _run_model_leaf_targets!( leaf_targets, meteo, solution.tair, @@ -301,7 +301,7 @@ function _publish_scene_leaf_solution!(leaf_targets, solution::SceneEBSolverResu return fluxes end -function _run_scene_soil_feedback!(soil_target, transpiration_mm) +function _run_model_soil_feedback!(soil_target, transpiration_mm) soil_target.status.transpiration = transpiration_mm soil_target.status.infiltration = 0.0 run_call!(soil_target; publish=true) @@ -311,10 +311,10 @@ end function PlantSimEngine.run!(m::SceneEB, models, status, meteo, constants, extra) leaf_targets = call_targets(extra, :energy_balance) soil_target = call_target(extra, :soil) - solution = _solve_scene_energy_balance!(m, leaf_targets, soil_target, status, meteo, constants) - fluxes = _publish_scene_leaf_solution!(leaf_targets, solution, meteo, m.ground_area) + solution = _solve_model_energy_balance!(m, leaf_targets, soil_target, status, meteo, constants) + fluxes = _publish_model_leaf_solution!(leaf_targets, solution, meteo, m.ground_area) transpiration_mm = λE_to_E(fluxes.lambda_e, solution.final_meteo.λ) * duration_seconds(meteo) * 18.0e-6 - psi_soil = _run_scene_soil_feedback!(soil_target, transpiration_mm) + psi_soil = _run_model_soil_feedback!(soil_target, transpiration_mm) status.canopy_tair = solution.tair status.canopy_vpd = solution.vpd @@ -386,7 +386,7 @@ end _maespa_plant_status() = Status(leaf_carbon=[0.0], daily_growth=0.0, leaf_pool=0.0, wood_pool=0.0) -function _maespa_scene_status() +function _maespa_model_status() return Status( leaf_areas=[0.0], leaf_area=0.0, @@ -408,7 +408,7 @@ function _maespa_soil_status() end function _maespa_species_template(species; monteith, fvcb, tuzet, allocation) - return ObjectTemplate( + return CompositeModelTemplate( ( ModelSpec(monteith; name=:energy_balance) |> AppliesTo(Many(scale=:Leaf)) |> @@ -448,7 +448,7 @@ function _maespa_plant_instance(name, template; nleaves, leaf_area, sky_fraction return ObjectInstance( name, template; - root=Object(plant_id; scale=:Plant, parent=:scene, status=_maespa_plant_status()), + root=Object(plant_id; scale=:Plant, parent=:model, status=_maespa_plant_status()), objects=( Object(axis_id; scale=:Internode, parent=plant_id), leaves..., @@ -456,7 +456,7 @@ function _maespa_plant_instance(name, template; nleaves, leaf_area, sky_fraction ) end -function build_maespa_scene(; scene_model=SceneEB(25, 0.03, 0.005), meteo=maespa_meteo()) +function build_maespa_model(; scene_model=SceneEB(25, 0.03, 0.005), meteo=maespa_meteo()) template_a = _maespa_species_template( :A; monteith=Monteith(; ε=0.955, maxiter=20, ΔT=0.02), @@ -472,9 +472,9 @@ function build_maespa_scene(; scene_model=SceneEB(25, 0.03, 0.005), meteo=maespa allocation=AllocB(0.55, 0.35), ) ground_area = scene_model.ground_area - return Scene( - Object(:scene; scale=:Scene, kind=:scene, status=_maespa_scene_status()), - Object(:soil; scale=:Soil, kind=:soil, parent=:scene, status=_maespa_soil_status()), + return CompositeModel( + Object(:model; scale=:Scene, kind=:model, status=_maespa_model_status()), + Object(:soil; scale=:Soil, kind=:soil, parent=:model, status=_maespa_soil_status()), _maespa_plant_instance( :plant_A, template_a; @@ -534,17 +534,17 @@ function maespa_meteo(; nhours=24) end function run_maespa_example(; nhours=24, check=true) - scene = build_maespa_scene(; meteo=maespa_meteo(; nhours=nhours)) - compiled = Advanced.compile_scene(scene) - check && Advanced.refresh_environment_bindings!(scene, compiled) + model = build_maespa_model(; meteo=maespa_meteo(; nhours=nhours)) + compiled = Advanced.compile_composite_model(model) + check && Advanced.refresh_environment_bindings!(model, compiled) simulation = run!( - scene; + model; steps=nhours, constants=PlantMeteo.Constants(), outputs=:all, ) return ( - scene=scene, + model=model, compiled=simulation.compiled, environment=simulation.environment_bindings, simulation=simulation, @@ -553,13 +553,13 @@ end if abspath(PROGRAM_FILE) == @__FILE__ result = run_maespa_example() - scene = result.scene - println("leaf_count = ", length(scene_objects(scene; scale=:Leaf))) + model = result.model + println("leaf_count = ", length(model_objects(model; scale=:Leaf))) println( "scene_transpiration = ", - only(scene_objects(scene; scale=:Scene)).status.scene_transpiration, + only(model_objects(model; scale=:Scene)).status.scene_transpiration, ) - println("psi_soil = ", only(scene_objects(scene; kind=:soil)).status.psi_soil) - println("plant_A = ", only(scene_objects(scene; name=:plant_A)).status.daily_growth) - println("plant_B = ", only(scene_objects(scene; name=:plant_B)).status.daily_growth) + println("psi_soil = ", only(model_objects(model; kind=:soil)).status.psi_soil) + println("plant_A = ", only(model_objects(model; name=:plant_A)).status.daily_growth) + println("plant_B = ", only(model_objects(model; name=:plant_B)).status.daily_growth) end diff --git a/examples/plantbiophysics_subsample/FvCB.jl b/examples/plantbiophysics_subsample/FvCB.jl index 825010885..828031739 100644 --- a/examples/plantbiophysics_subsample/FvCB.jl +++ b/examples/plantbiophysics_subsample/FvCB.jl @@ -3,7 +3,7 @@ @process "photosynthesis" verbose = false # Default policy for assimilation rates when consumed at coarser clocks. -# An explicit scene `Inputs(...)` policy overrides this default. +# An explicit model `Inputs(...)` policy overrides this default. PlantSimEngine.output_policy(::Type{<:AbstractPhotosynthesisModel}) = (A=PlantSimEngine.Integrate(PlantMeteo.DurationSumReducer()),) diff --git a/examples/plantbiophysics_subsample/Monteith.jl b/examples/plantbiophysics_subsample/Monteith.jl index e7b029d0d..71c874276 100644 --- a/examples/plantbiophysics_subsample/Monteith.jl +++ b/examples/plantbiophysics_subsample/Monteith.jl @@ -479,7 +479,7 @@ the energy balance using the mass flux (~ Rn - λE). # Arguments - `::Monteith`: a Monteith model, usually from a model list (*i.e.* m.energy_balance) -- `models`: the process-keyed model bundle supplied by the scene runtime, with +- `models`: the process-keyed model bundle supplied by the model runtime, with initialisations for: - `Ra_SW_f` (W m-2): net shortwave radiation (PAR + NIR). Often computed from a light interception model - `sky_fraction` (0-2): view factor between the object and the sky for both faces (see details). @@ -550,7 +550,7 @@ function PlantSimEngine.run!(::Monteith, models, status, meteo, constants=PlantM status.sky_fraction, constants.K₀, constants.σ) #= ? NB: we use the sky fraction here (0-2) instead of the view factor (0-1) because: - we consider both sides of the leaf at the same time (1 -> leaf sees sky on one face) - - we consider all objects in the scene have the same temperature as the leaf + - we consider all objects in the model have the same temperature as the leaf of interest except the atmosphere. So the leaf exchange thermal energy_balance only with the atmosphere. =# # status.Ra_LW_f = (grey_body(meteo.T,1.0) - grey_body(status.Tₗ, 1.0))*status.sky_fraction diff --git a/examples/plantbiophysics_subsample/Tuzet.jl b/examples/plantbiophysics_subsample/Tuzet.jl index 47f07f9a1..ad7326df2 100644 --- a/examples/plantbiophysics_subsample/Tuzet.jl +++ b/examples/plantbiophysics_subsample/Tuzet.jl @@ -85,7 +85,7 @@ Stomatal closure for CO₂ according to Tuzet et al. (2003). # Arguments - `::Tuzet`: an instance of the `Tuzet` model type. -- `models`: the process-keyed model bundle supplied by the scene runtime. +- `models`: the process-keyed model bundle supplied by the model runtime. - `status`: A status struct holding the variables for the models. - `meteo`: meteorology structure, see [`Atmosphere`](https://palmstudio.github.io/PlantMeteo.jl/stable/#PlantMeteo.Atmosphere). Is not used in this model. - `constants`: A constants struct holding the constants for the models. Is not used in this model. diff --git a/ext/PlantSimEngineGraphEditorExt.jl b/ext/PlantSimEngineGraphEditorExt.jl index d9bcf9cc0..bef80c018 100644 --- a/ext/PlantSimEngineGraphEditorExt.jl +++ b/ext/PlantSimEngineGraphEditorExt.jl @@ -3,10 +3,10 @@ module PlantSimEngineGraphEditorExt import HTTP import JSON import PlantSimEngine -import PlantSimEngine: edit_graph, current_scene, apply_edit!, undo!, redo! +import PlantSimEngine: edit_graph, current_model, apply_edit!, undo!, redo! -mutable struct GraphEditorSession <: PlantSimEngine.AbstractSceneGraphEditorSession - scene::PlantSimEngine.Scene +mutable struct GraphEditorSession <: PlantSimEngine.AbstractModelGraphEditorSession + model::PlantSimEngine.CompositeModel history::Vector{Any} future::Vector{Any} server::Any @@ -20,7 +20,7 @@ mutable struct GraphEditorSession <: PlantSimEngine.AbstractSceneGraphEditorSess recent_paths::Vector{String} end -current_scene(session::GraphEditorSession) = session.scene +current_model(session::GraphEditorSession) = session.model function Base.close(session::GraphEditorSession) try @@ -32,29 +32,29 @@ function Base.close(session::GraphEditorSession) end function Base.show(io::IO, session::GraphEditorSession) - print(io, "GraphEditorSession(url=$(repr(session.url)), applications=$(length(session.scene.applications)))") + print(io, "GraphEditorSession(url=$(repr(session.url)), applications=$(length(session.model.applications)))") end function Base.show(io::IO, ::MIME"text/plain", session::GraphEditorSession) println(io, "PlantSimEngineGraphEditorExt.GraphEditorSession") println(io, " Open in browser: $(session.url)") println(io, " State JSON: $(_state_url(session))") - println(io, " Current scene: current_scene(session)") + println(io, " Current model: current_model(session)") println(io, " Quit session: close(session)") isnothing(session.autosave_path) || println(io, " Recovery autosave: $(session.autosave_path)") isnothing(session.save_path) || println(io, " Saving changes to: $(session.save_path)") end """ - edit_graph([scene]; host="127.0.0.1", port=0, open_browser=true, + edit_graph([model]; host="127.0.0.1", port=0, open_browser=true, autosave=true, allow_remote=false, allow_julia_eval=nothing) -Start a local Scene graph editor. Julia owns the current Scene and applies all +Start a local Model graph editor. Julia owns the current Composite model and applies all semantic edits received from the browser. Call `edit_graph()` to start from an -empty Scene and `close(session)` to stop the server. +empty Composite model and `close(session)` to stop the server. """ function edit_graph( - scene::PlantSimEngine.Scene=PlantSimEngine.Scene(); + model::PlantSimEngine.CompositeModel=PlantSimEngine.CompositeModel(); host::AbstractString="127.0.0.1", port::Integer=0, open_browser::Bool=true, @@ -70,7 +70,7 @@ function edit_graph( "Graph editor sessions are limited to localhost by default. Pass `allow_remote=true` only for a trusted network.", ) effective_allow_julia_eval = isnothing(allow_julia_eval) ? !allow_remote : allow_julia_eval - initial_scene = isnothing(recover_path) ? deepcopy(scene) : _load_scene_file( + initial_model = isnothing(recover_path) ? deepcopy(model) : _load_model_file( _normalized_path(recover_path); allow_julia_eval=effective_allow_julia_eval, ) @@ -84,7 +84,7 @@ function edit_graph( ) : nothing remembered_paths = isnothing(recent_paths) ? _load_recent_paths() : String.(recent_paths) session = GraphEditorSession( - initial_scene, + initial_model, Any[], Any[], server, @@ -100,34 +100,34 @@ function edit_graph( session_ref[] = session isnothing(session.save_path) || _remember_path!(session, session.save_path) isnothing(recover_path) || _remember_path!(session, _normalized_path(recover_path)) - _persist_scene!(session) + _persist_model!(session) open_browser && _open_in_default_browser(session.url) return session end -function apply_edit!(session::GraphEditorSession, edit::PlantSimEngine.AbstractSceneGraphEdit) - candidate = PlantSimEngine.apply_scene_graph_edit(session.scene, edit) - push!(session.history, session.scene) +function apply_edit!(session::GraphEditorSession, edit::PlantSimEngine.AbstractModelGraphEdit) + candidate = PlantSimEngine.apply_model_graph_edit(session.model, edit) + push!(session.history, session.model) empty!(session.future) - session.scene = candidate - _persist_scene!(session) - return session.scene + session.model = candidate + _persist_model!(session) + return session.model end function undo!(session::GraphEditorSession) - isempty(session.history) && return session.scene - push!(session.future, session.scene) - session.scene = pop!(session.history) - _persist_scene!(session) - return session.scene + isempty(session.history) && return session.model + push!(session.future, session.model) + session.model = pop!(session.history) + _persist_model!(session) + return session.model end function redo!(session::GraphEditorSession) - isempty(session.future) && return session.scene - push!(session.history, session.scene) - session.scene = pop!(session.future) - _persist_scene!(session) - return session.scene + isempty(session.future) && return session.model + push!(session.history, session.model) + session.model = pop!(session.future) + _persist_model!(session) + return session.model end _session_token(server) = string(hash((time_ns(), getpid(), objectid(server))); base=16) * @@ -166,12 +166,12 @@ function _handle_http(session::GraphEditorSession, stream::HTTP.Stream) if path == "/" || path == "/index.html" return _write_response(stream, 200, "text/html; charset=utf-8", _editor_html(session)) elseif path == "/static" - view = PlantSimEngine.scene_graph_view(session.scene) + view = PlantSimEngine.model_graph_view(session.model) return _write_response( stream, 200, "text/html; charset=utf-8", - PlantSimEngine.scene_graph_view_html(view), + PlantSimEngine.model_graph_view_html(view), ) elseif path == "/state" return _write_response(stream, 200, "application/json", _state_json(session)) @@ -249,25 +249,25 @@ function _handle_command!(session, command) redo!(session) elseif action == "edit" apply_edit!(session, _edit_from_command(session, command)) - elseif action == "save_scene_code" + elseif action == "save_model_code" session.save_path = _normalized_path(String(command["path"])) _remember_path!(session, session.save_path) - _persist_scene!(session) - elseif action == "open_scene_code" + _persist_model!(session) + elseif action == "open_model_code" path = _normalized_path(String(command["path"])) - candidate = _load_scene_file(path; allow_julia_eval=session.allow_julia_eval) - push!(session.history, session.scene) + candidate = _load_model_file(path; allow_julia_eval=session.allow_julia_eval) + push!(session.history, session.model) empty!(session.future) - session.scene = candidate + session.model = candidate session.save_path = path _remember_path!(session, path) - _persist_scene!(session) + _persist_model!(session) elseif action == "preview_input_binding" return _preview_input_binding_payload(session, command) elseif action == "preview_application_targets" return _preview_application_targets_payload(session, command) elseif action in ("open_add_application", "begin_add_application") - # This command only focuses/prefills frontend state. The Scene is + # This command only focuses/prefills frontend state. The Composite model is # changed by a subsequent add_application edit. else error("Unsupported graph editor command action `$(action)`.") @@ -280,10 +280,10 @@ end function _preview_application_targets_payload(session, command) selector = _selector_from_payload(command["selector"]) - target_ids = PlantSimEngine.resolve_object_ids(session.scene, selector) + target_ids = PlantSimEngine.resolve_object_ids(session.model, selector) payload = _state_payload(session) payload["targetPreview"] = Dict{String,Any}( - "objectIds" => [PlantSimEngine._scene_graph_json_value(id.value) for id in target_ids], + "objectIds" => [PlantSimEngine._model_graph_json_value(id.value) for id in target_ids], "count" => length(target_ids), ) return payload @@ -292,15 +292,15 @@ end function _preview_input_binding_payload(session, command) application_id = Symbol(command["applicationId"]) input = Symbol(command["input"]) - candidate = PlantSimEngine.apply_scene_graph_edit( - session.scene, - PlantSimEngine.SetSceneInputBinding( + candidate = PlantSimEngine.apply_model_graph_edit( + session.model, + PlantSimEngine.SetModelInputBinding( application_id, input, _selector_from_payload(command["selector"]), ), ) - report = PlantSimEngine.compile_scene_report(candidate) + report = PlantSimEngine.compile_model_report(candidate) bindings = [ binding for binding in report.input_bindings if binding.application_id == application_id && binding.input == input @@ -310,11 +310,11 @@ function _preview_input_binding_payload(session, command) "applicationId" => string(application_id), "input" => string(input), "consumerObjectIds" => unique([ - PlantSimEngine._scene_graph_json_value(binding.consumer_id.value) + PlantSimEngine._model_graph_json_value(binding.consumer_id.value) for binding in bindings ]), "sourceObjectIds" => unique([ - PlantSimEngine._scene_graph_json_value(source_id.value) + PlantSimEngine._model_graph_json_value(source_id.value) for binding in bindings for source_id in binding.source_ids ]), "sourceApplicationIds" => unique([ @@ -330,135 +330,135 @@ end function _edit_from_command(session, command) kind = String(get(command, "kind", "")) application_id = Symbol(get(command, "applicationId", "")) - kind == "remove_application" && return PlantSimEngine.RemoveSceneApplication(application_id) - kind == "remove_template_application" && return PlantSimEngine.RemoveSceneTemplateApplication( + kind == "remove_application" && return PlantSimEngine.RemoveModelApplication(application_id) + kind == "remove_template_application" && return PlantSimEngine.RemoveModelTemplateApplication( command["instance"], application_id, ) - kind == "mark_previous_timestep" && return PlantSimEngine.MarkScenePreviousTimeStep( + kind == "mark_previous_timestep" && return PlantSimEngine.MarkModelPreviousTimeStep( application_id, Symbol(command["input"]), ) - kind == "unmark_previous_timestep" && return PlantSimEngine.UnmarkScenePreviousTimeStep( + kind == "unmark_previous_timestep" && return PlantSimEngine.UnmarkModelPreviousTimeStep( application_id, Symbol(command["input"]), ) - kind == "break_cycle" && return PlantSimEngine.BreakSceneCycle( + kind == "break_cycle" && return PlantSimEngine.BreakModelCycle( application_id, Symbol(command["input"]), Bool(get(command, "initializeMissing", false)), _parameter_value(session, get(command, "initialValue", nothing)), ) - kind == "set_application_targets" && return PlantSimEngine.SetSceneApplicationTargets( + kind == "set_application_targets" && return PlantSimEngine.SetModelApplicationTargets( application_id, _selector_from_payload(command["selector"]), ) - kind == "set_input_binding" && return PlantSimEngine.SetSceneInputBinding( + kind == "set_input_binding" && return PlantSimEngine.SetModelInputBinding( application_id, Symbol(command["input"]), _selector_from_payload(command["selector"]), ) - kind == "remove_input_binding" && return PlantSimEngine.RemoveSceneInputBinding( + kind == "remove_input_binding" && return PlantSimEngine.RemoveModelInputBinding( application_id, Symbol(command["input"]), ) - kind == "set_call_binding" && return PlantSimEngine.SetSceneCallBinding( + kind == "set_call_binding" && return PlantSimEngine.SetModelCallBinding( application_id, Symbol(command["call"]), _selector_from_payload(command["selector"]), ) - kind == "remove_call_binding" && return PlantSimEngine.RemoveSceneCallBinding( + kind == "remove_call_binding" && return PlantSimEngine.RemoveModelCallBinding( application_id, Symbol(command["call"]), ) - kind == "set_timestep" && return PlantSimEngine.SetSceneApplicationTimeStep( + kind == "set_timestep" && return PlantSimEngine.SetModelApplicationTimeStep( application_id, _timestep_from_payload(get(command, "timestep", nothing)), ) - kind == "set_application_environment" && return PlantSimEngine.SetSceneApplicationEnvironment( + kind == "set_application_environment" && return PlantSimEngine.SetModelApplicationEnvironment( application_id, _configuration_from_payload(session, get(command, "configuration", nothing)), ) - kind == "set_environment_provider" && return PlantSimEngine.SetSceneApplicationEnvironment( + kind == "set_environment_provider" && return PlantSimEngine.SetModelApplicationEnvironment( application_id, - _environment_with_provider(session.scene, application_id, command["provider"]), + _environment_with_provider(session.model, application_id, command["provider"]), ) - kind == "set_output_routing" && return PlantSimEngine.SetSceneOutputRouting( + kind == "set_output_routing" && return PlantSimEngine.SetModelOutputRouting( application_id, Symbol(command["output"]), Symbol(command["route"]), ) - kind == "set_update_ordering" && return PlantSimEngine.SetSceneUpdateOrdering( + kind == "set_update_ordering" && return PlantSimEngine.SetModelUpdateOrdering( application_id, _updates_from_payload(get(command, "updates", Any[])), ) - kind == "set_object_status" && return PlantSimEngine.SetSceneObjectStatus( + kind == "set_object_status" && return PlantSimEngine.SetModelObjectStatus( command["objectId"], Symbol(command["variable"]), _parameter_value(session, command["value"]), ) - kind == "set_object_statuses" && return PlantSimEngine.SetSceneObjectStatuses( + kind == "set_object_statuses" && return PlantSimEngine.SetModelObjectStatuses( command["objectIds"], Symbol(command["variable"]), _parameter_value(session, command["value"]), ) - kind == "remove_object_status" && return PlantSimEngine.RemoveSceneObjectStatus( + kind == "remove_object_status" && return PlantSimEngine.RemoveModelObjectStatus( command["objectId"], Symbol(command["variable"]), ) - kind in ("set_object_metadata", "update_object") && return PlantSimEngine.SetSceneObjectMetadata( + kind in ("set_object_metadata", "update_object") && return PlantSimEngine.SetModelObjectMetadata( PlantSimEngine.ObjectId(command["objectId"]), _metadata_from_payload(get(command, "configuration", Dict())), ) - kind == "add_object" && return PlantSimEngine.AddSceneObject( + kind == "add_object" && return PlantSimEngine.AddModelObject( _object_from_command(session, command), ) - kind == "remove_object" && return PlantSimEngine.RemoveSceneObject( + kind == "remove_object" && return PlantSimEngine.RemoveModelObject( command["objectId"]; recursive=Bool(get(command, "recursive", true)), ) - kind == "reparent_object" && return PlantSimEngine.ReparentSceneObject( + kind == "reparent_object" && return PlantSimEngine.ReparentModelObject( command["objectId"], get(command, "parentId", nothing), ) - kind == "set_instance_override" && return PlantSimEngine.SetSceneInstanceOverride( + kind == "set_instance_override" && return PlantSimEngine.SetModelInstanceOverride( command["instance"], application_id, _construct_model(session, command["modelType"], get(command, "parameters", Dict())), ) - kind == "remove_instance_override" && return PlantSimEngine.RemoveSceneInstanceOverride( + kind == "remove_instance_override" && return PlantSimEngine.RemoveModelInstanceOverride( command["instance"], application_id, ) - kind == "set_object_override" && return PlantSimEngine.SetSceneObjectOverride( + kind == "set_object_override" && return PlantSimEngine.SetModelObjectOverride( command["instance"], command["objectId"], application_id, _construct_model(session, command["modelType"], get(command, "parameters", Dict())), ) - kind == "remove_object_override" && return PlantSimEngine.RemoveSceneObjectOverride( + kind == "remove_object_override" && return PlantSimEngine.RemoveModelObjectOverride( command["instance"], command["objectId"], application_id, ) kind == "add_application" && return _add_application_edit(session, command) kind == "update_application" && return _update_application_edit(session, command) - kind == "update_template_application" && return PlantSimEngine.UpdateSceneTemplateApplication( + kind == "update_template_application" && return PlantSimEngine.UpdateModelTemplateApplication( Symbol(command["instance"]), application_id, _construct_model(session, command["modelType"], get(command, "parameters", Dict())), _selector_from_payload(command["selector"]), _timestep_from_payload(get(command, "timestep", nothing)), ) - kind == "replace_application_model" && return PlantSimEngine.ReplaceSceneApplicationModel( + kind == "replace_application_model" && return PlantSimEngine.ReplaceModelApplicationModel( application_id, _construct_model(session, command["modelType"], get(command, "parameters", Dict())), ) - error("Unsupported Scene graph edit kind `$(kind)`.") + error("Unsupported Model graph edit kind `$(kind)`.") end -function _environment_with_provider(scene, application_id, provider) - spec = PlantSimEngine._scene_edit_spec(scene, application_id) +function _environment_with_provider(model, application_id, provider) + spec = PlantSimEngine._model_edit_spec(model, application_id) current = PlantSimEngine.environment_config(spec) current isa PlantSimEngine.EnvironmentConfig && (current = current.config) current_values = current isa NamedTuple ? current : NamedTuple() @@ -533,7 +533,7 @@ end function _update_application_edit(session, command) application_id = Symbol(command["applicationId"]) model = _construct_model(session, command["modelType"], get(command, "parameters", Dict())) - return PlantSimEngine.UpdateSceneApplication( + return PlantSimEngine.UpdateModelApplication( application_id, model, Symbol(get(command, "name", string(application_id))), @@ -553,7 +553,7 @@ function _add_application_edit(session, command) applies_to=selector, timestep=timestep, ) - return PlantSimEngine.AddSceneApplication(spec) + return PlantSimEngine.AddModelApplication(spec) end function _resolve_model_type(label) @@ -681,7 +681,7 @@ function _timestep_from_payload(payload) end function _state_payload(session; ok=true, diagnostics=String[]) - graph = JSON.parse(PlantSimEngine.scene_graph_view_json(session.scene)) + graph = JSON.parse(PlantSimEngine.model_graph_view_json(session.model)) return Dict{String,Any}( "ok" => ok, "diagnostics" => diagnostics, @@ -689,7 +689,7 @@ function _state_payload(session; ok=true, diagnostics=String[]) "canUndo" => !isempty(session.history), "canRedo" => !isempty(session.future), "url" => session.url, - "sceneCode" => _scene_to_julia(session.scene), + "modelCode" => _model_to_julia(session.model), "autosavePath" => session.autosave_path, "savePath" => session.save_path, "recentPaths" => session.recent_paths, @@ -706,7 +706,7 @@ function _remember_path!(session, path) end function _recent_paths_file() - return joinpath(tempdir(), "PlantSimEngineGraphEditor", "recent-scenes.json") + return joinpath(tempdir(), "PlantSimEngineGraphEditor", "recent-models.json") end function _load_recent_paths() @@ -728,46 +728,46 @@ function _persist_recent_paths!(paths) return path end -function _load_scene_file(path; allow_julia_eval::Bool) - allow_julia_eval || error("Opening Julia Scene files is disabled for this editor session.") - isfile(path) || error("Scene file `$(path)` does not exist.") +function _load_model_file(path; allow_julia_eval::Bool) + allow_julia_eval || error("Opening Julia Composite model files is disabled for this editor session.") + isfile(path) || error("Composite model file `$(path)` does not exist.") module_name = Symbol("PlantSimEngineGraphRecovery_", string(time_ns(); base=16)) workspace = Module(module_name) Core.eval(workspace, :(using PlantSimEngine)) included = Base.include(workspace, path) - scene = isdefined(workspace, :scene) ? getfield(workspace, :scene) : included - scene isa PlantSimEngine.Scene || error( - "Scene file `$(path)` must assign its final PlantSimEngine.Scene to `scene`.", + model = isdefined(workspace, :model) ? getfield(workspace, :model) : included + model isa PlantSimEngine.CompositeModel || error( + "Composite model file `$(path)` must assign its final PlantSimEngine.CompositeModel to `model`.", ) - return scene + return model end _state_json(session) = JSON.json(_state_payload(session)) function _editor_html(session) - view = PlantSimEngine.scene_graph_view(session.scene) - html = PlantSimEngine.scene_graph_view_html(view) + view = PlantSimEngine.model_graph_view(session.model) + html = PlantSimEngine.model_graph_view_html(view) config = replace(JSON.json(Dict("websocketUrl" => _websocket_url(session))), " "<\\/") script = "" return replace(html, "" => "$(script)") end -function _scene_to_julia(scene) +function _model_to_julia(model) io = IOBuffer() diagnostics = String[] - modules = _scene_code_modules(scene) + modules = _model_code_modules(model) for module_name in sort!(collect(modules)) println(io, "using $(module_name)") end println(io) - if !isnothing(scene.source_adapter) - push!(diagnostics, "The Scene source_adapter is runtime-specific and is not reconstructed by generated code.") + if !isnothing(model.source_adapter) + push!(diagnostics, "The Composite model source_adapter is runtime-specific and is not reconstructed by generated code.") end - for model in _scene_code_models(scene) + for model in _model_code_models(model) Base.moduleroot(parentmodule(typeof(model))) === Main || continue push!( diagnostics, - "Model $(typeof(model)) is defined in Main. Define or include that model before evaluating this generated Scene script.", + "Model $(typeof(model)) is defined in Main. Define or include that model before evaluating this generated Composite model script.", ) end for diagnostic in unique(diagnostics) @@ -775,13 +775,13 @@ function _scene_to_julia(scene) end isempty(diagnostics) || println(io) println(io, "objects = (") - for object in PlantSimEngine.scene_objects(scene) + for object in PlantSimEngine.model_objects(model) println(io, " ", _object_code(object), ",") end println(io, ")") templates = Any[] - for instance in scene.instances + for instance in model.instances any(template -> template === instance.template, templates) || push!(templates, instance.template) end for (index, template) in pairs(templates) @@ -789,10 +789,10 @@ function _scene_to_julia(scene) println(io, "template_$(index) = ", _template_code(template)) end - if !isempty(scene.instances) + if !isempty(model.instances) println(io) println(io, "instances = (") - for instance in scene.instances + for instance in model.instances template_index = only(index for (index, template) in pairs(templates) if template === instance.template) println(io, " ", _instance_code(instance, template_index), ",") end @@ -803,40 +803,40 @@ function _scene_to_julia(scene) end mounted_ids = Set{Symbol}() - for instance in scene.instances - union!(mounted_ids, PlantSimEngine._instance_application_ids(scene, instance)) + for instance in model.instances + union!(mounted_ids, PlantSimEngine._instance_application_ids(model, instance)) end global_applications = [ - application for application in scene.applications - if PlantSimEngine._scene_edit_application_id(application) ∉ mounted_ids + application for application in model.applications + if PlantSimEngine._model_edit_application_id(application) ∉ mounted_ids ] println(io, "applications = (") for application in global_applications println(io, " ", _application_code(PlantSimEngine.as_model_spec(application)), ",") end println(io, ")") - environment = isnothing(scene.environment) ? "nothing" : repr(scene.environment) - print(io, "scene = Scene(objects...; applications=applications, instances=instances, environment=$(environment))") + environment = isnothing(model.environment) ? "nothing" : repr(model.environment) + print(io, "model = CompositeModel(objects...; applications=applications, instances=instances, environment=$(environment))") return String(take!(io)) end -function _scene_code_models(scene) +function _model_code_models(model) models = Any[] add_application = function (application) - model = PlantSimEngine.model_(PlantSimEngine.as_model_spec(application)) - if model isa PlantSimEngine.ObjectModelOverrides - push!(models, model.base) - append!(models, values(model.overrides)) + process_model = PlantSimEngine.model_(PlantSimEngine.as_model_spec(application)) + if process_model isa PlantSimEngine.ObjectModelOverrides + push!(models, process_model.base) + append!(models, values(process_model.overrides)) else - push!(models, model) + push!(models, process_model) end end - foreach(add_application, scene.applications) - for object in PlantSimEngine.scene_objects(scene) + foreach(add_application, model.applications) + for object in PlantSimEngine.model_objects(model) isnothing(object.applications) && continue foreach(add_application, object.applications) end - for instance in scene.instances + for instance in model.instances foreach(add_application, instance.template.applications) append!(models, values(instance.overrides)) append!(models, (override.model for override in instance.object_overrides)) @@ -844,13 +844,13 @@ function _scene_code_models(scene) return models end -function _scene_code_modules(scene) +function _model_code_modules(model) modules = Set{String}(["PlantSimEngine"]) add_model = function (model) module_ = Base.moduleroot(parentmodule(typeof(model))) module_ in (Base, Core, Main) || push!(modules, string(nameof(module_))) end - foreach(add_model, _scene_code_models(scene)) + foreach(add_model, _model_code_models(model)) return modules end @@ -882,7 +882,7 @@ function _template_code(template) (" " * _application_code(PlantSimEngine.as_model_spec(application)) * "," for application in template.applications), "\n", ) - return "ObjectTemplate((\n$(applications)\n ); kind=$(repr(template.kind)), species=$(repr(template.species)), parameters=$(repr(template.parameters)))" + return "CompositeModelTemplate((\n$(applications)\n ); kind=$(repr(template.kind)), species=$(repr(template.species)), parameters=$(repr(template.parameters)))" end function _instance_code(instance, template_index) @@ -938,8 +938,8 @@ function _application_code(spec) return code end -function _persist_scene!(session) - code = _scene_to_julia(session.scene) * "\n" +function _persist_model!(session) + code = _model_to_julia(session.model) * "\n" isnothing(session.autosave_path) || _atomic_write(session.autosave_path, code) isnothing(session.save_path) || _atomic_write(session.save_path, code) return nothing @@ -965,7 +965,7 @@ function _default_autosave_path() tempdir(), "PlantSimEngineGraphEditor", string("session-", time_ns()), - "scene.autosave.jl", + "model.autosave.jl", ) end diff --git a/frontend/dist/.vite/manifest.json b/frontend/dist/.vite/manifest.json index 83b5405a5..b86df32fc 100644 --- a/frontend/dist/.vite/manifest.json +++ b/frontend/dist/.vite/manifest.json @@ -1,11 +1,11 @@ { "index.html": { - "file": "assets/index-D625qdlf.js", + "file": "assets/index-PMl5JtnR.js", "name": "index", "src": "index.html", "isEntry": true, "css": [ - "assets/index-Do5n-jvW.css" + "assets/index-DXj3JKAS.css" ] } } \ No newline at end of file diff --git a/frontend/dist/assets/index-Do5n-jvW.css b/frontend/dist/assets/index-DXj3JKAS.css similarity index 96% rename from frontend/dist/assets/index-Do5n-jvW.css rename to frontend/dist/assets/index-DXj3JKAS.css index 80bcc42f0..13d2c1b9c 100644 --- a/frontend/dist/assets/index-Do5n-jvW.css +++ b/frontend/dist/assets/index-DXj3JKAS.css @@ -1 +1 @@ -.react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #777;--xy-background-pattern-lines-color-default: #777;--xy-background-pattern-cross-color-default: #777;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}:root{--bg: #f3eee6;--paper: #fffaf2;--paper-strong: #fbf2e6;--ink: #312721;--muted: #80756c;--line: #ded2c3;--line-strong: #b7a696;--accent: #1f7a53;--accent-soft: rgba(31, 122, 83, .12);--sage: #7f8f73;--sage-dark: #596851;--ochre: #c99035;--clay: #bf6a54;--shadow: rgba(56, 43, 35, .12)}.scene-editor-shell{height:100vh;min-height:560px;display:grid;grid-template-rows:auto auto auto minmax(0,1fr);color:#302923;background:#f3eee5}.frontend-error{min-height:100vh;display:grid;place-content:center;justify-items:center;gap:10px;padding:24px;color:#743128;background:#fff5ef;text-align:center}.frontend-error h1,.frontend-error p{margin:0}.frontend-error p{max-width:620px;color:#655850}.frontend-error button{display:inline-flex;align-items:center;gap:6px;margin-top:8px;padding:8px 11px;border:1px solid #b95040;border-radius:5px;color:#fff;background:#b94435}.scene-toolbar{z-index:20;display:grid;grid-template-columns:auto minmax(260px,1fr) auto;gap:12px 18px;align-items:center;padding:14px 18px;background:#fffaf2;border-bottom:1px solid #d9cdbd;box-shadow:0 8px 24px #3c302514}.scene-brand{display:flex;align-items:center;gap:11px}.scene-brand .brand-mark{width:5px;height:42px;border-radius:3px;background:#1f7a58}.scene-brand div{display:grid}.scene-brand small{color:#81766c;font:10px/1.2 ui-monospace,monospace;letter-spacing:0}.scene-brand strong{font-size:20px;letter-spacing:0}.scene-search{display:flex;align-items:center;gap:8px;min-width:0;padding:8px 11px;border:1px solid #d9cdbd;border-radius:6px;background:#fffdf8}.scene-search input{width:100%;min-width:0;border:0;outline:0;background:transparent;font:inherit}.scene-search button,.scene-toolbar button,.overlay-panel button,.candidate-popover button,.editor-feedback button{border:0;background:transparent;color:inherit;cursor:pointer}.scene-counts{display:flex;align-items:center;justify-content:flex-end;gap:7px;flex-wrap:wrap}.scene-counts>span,.scene-counts>button{display:inline-flex;align-items:center;gap:5px;padding:5px 8px;border:1px solid #d9cdbd;border-radius:5px;background:#fffaf2;font:12px ui-monospace,monospace}.scene-counts .count-warning{color:#a96a13;border-color:#dfbd83}.scene-counts .count-error{color:#b44e3c;border-color:#dfa496}.view-tabs,.scene-actions{display:flex;align-items:center;gap:7px;flex-wrap:wrap}.view-tabs{grid-column:1 / 3}.scene-actions{justify-content:flex-end}.view-tabs button,.scene-actions button,.cycle-callout button{display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:6px 10px;border:1px solid #d4c7b6;border-radius:5px;background:#fffaf2;color:#4c433b}.view-tabs button.active{color:#176047;border-color:#83b59e;background:#e9f4ed}.scene-actions button:disabled{opacity:.4;cursor:default}.scene-actions .overview-cta{color:#176047;border-color:#86bba4;background:#e9f4ed;font-weight:700}.cycle-callout{display:flex;align-items:center;gap:12px;padding:10px 18px;color:#8f2f24;background:#fff0eb;border-bottom:1px solid #df9a8e}.cycle-callout div{display:grid;margin-right:auto}.cycle-callout span{font-size:12px}.cycle-callout button{border-color:#cf7668;color:#8f2f24}.cycle-callout button.active{color:#fff;border-color:#9d3428;background:#b94435}.editor-feedback{display:flex;align-items:center;justify-content:center;gap:12px;padding:7px 16px;background:#fff5d9;border-bottom:1px solid #dfc278;color:#6e5315;font-size:12px}.graph-scope-filter{align-items:center;background:#edf5ef;border-bottom:1px solid #b9d4c0;color:#365849;display:flex;font-size:12px;gap:10px;justify-content:center;min-height:38px;padding:6px 14px}.graph-scope-filter button{align-items:center;background:#fff;border:1px solid #b9d4c0;border-radius:5px;color:#365849;cursor:pointer;display:inline-flex;gap:5px;padding:5px 8px}.scene-workspace{min-height:0;display:grid;grid-template-columns:minmax(0,1fr) 310px}.flow-wrap{min-width:0;min-height:0;position:relative}.scene-inspector{overflow:auto;padding:16px;background:#fffaf2;border-left:1px solid #d9cdbd}.scene-inspector>header{display:grid;gap:2px;margin-bottom:14px}.scene-inspector>header span{color:#776c63;font:11px ui-monospace,monospace}.scene-inspector pre{overflow-wrap:anywhere;white-space:pre-wrap;font-size:10px;line-height:1.45}.empty-inspector{display:grid;place-items:center;padding:48px 20px;color:#8a7e74;text-align:center}.inspector-initialization>div{display:flex;justify-content:space-between;gap:8px;padding:6px 0;border-bottom:1px solid #eee4d6;font-size:11px}.inspector-initialization .unresolved{color:#b74635;font-weight:700}.application-node .target-summary{margin:-2px 0 10px;color:#766c63;font-size:11px}.application-node .target-summary strong{color:#2d6652}.application-node .previous-label{margin-left:auto;color:#1f7a58;font:10px ui-monospace,monospace}.cycle-port-break{display:inline-grid;place-items:center;width:24px;height:24px;margin-left:auto;border:1px solid #c94e3e!important;border-radius:50%;color:#a63428!important;background:#fff0eb!important;box-shadow:0 3px 9px #8c2d232e}.cycle-port-break:hover{color:#fff!important;background:#bd4435!important}.entity-node{position:relative;width:240px;min-height:105px;padding:13px;border:1px solid #d8cbbb;border-radius:6px;background:#fffaf2;box-shadow:0 7px 18px #392e241f}.entity-node.selected{border-color:#1f7a58;box-shadow:0 0 0 2px #1f7a5829}.entity-node header{display:grid;gap:3px}.entity-node header span{color:#766c63;font-size:11px}.entity-node .badges{display:flex;gap:5px;flex-wrap:wrap;margin-top:12px}.candidate-popover{position:fixed;z-index:80;width:370px;max-height:460px;display:grid;grid-template-rows:auto minmax(0,1fr);border:1px solid #cfbfaa;border-radius:7px;background:#fffaf2;box-shadow:0 20px 54px #342a203d}.candidate-popover>header{display:flex;gap:10px;padding:12px;border-bottom:1px solid #e1d5c5}.candidate-popover>header div{display:grid;margin-right:auto}.candidate-popover>header span{color:#7a7067;font-size:10px}.candidate-list{overflow:auto;display:grid;gap:7px;padding:9px}.candidate-card{display:grid;grid-template-columns:1fr auto;gap:2px 8px;padding:10px;border:1px solid #ded2c2!important;border-radius:5px;background:#fffdf8!important;text-align:left}.candidate-card:hover{border-color:#76a990!important;background:#edf6f0!important}.candidate-card span{color:#1f7053;font:11px ui-monospace,monospace}.candidate-card small{color:#7a7067}.candidate-card div{grid-column:1 / -1;color:#7a7067;font-size:10px}.candidate-card.existing{border-left:3px solid #1f7a58!important}.candidate-section-label{padding:5px 3px 2px;color:#71675e;font:700 10px ui-monospace,monospace;text-transform:uppercase}.overlay-backdrop{position:fixed;z-index:100;inset:0;display:grid;place-items:center;padding:24px;background:#2d251e61}.overlay-panel{width:min(720px,100%);max-height:min(760px,92vh);display:grid;grid-template-rows:auto minmax(0,1fr);border:1px solid #cdbda8;border-radius:7px;background:#fffaf2;box-shadow:0 24px 70px #271f194d}.overlay-panel>header{display:flex;justify-content:space-between;align-items:center;padding:14px 16px;border-bottom:1px solid #ded2c2}.overlay-content{overflow:auto;padding:15px}.diagnostic-card,.cycle-card,.initialization-card{display:grid;gap:5px;margin-bottom:10px;padding:11px;border:1px solid #ded2c2;border-radius:5px}.diagnostic-card{border-left:3px solid #c85240}.diagnostic-card p,.cycle-card p{margin:0}.diagnostic-card small{color:#766c63}.cycle-card button{width:fit-content;padding:6px 8px;border:1px solid #ce7768;border-radius:4px;color:#963529}.scene-code{min-height:320px;margin:0;padding:14px;overflow:auto;border-radius:5px;background:#292622;color:#f5eee5}.scene-file-dialog{display:grid;gap:14px}.scene-file-dialog>p{margin:0;line-height:1.5}.scene-file-dialog>label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.scene-path-input{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px}.scene-path-input input{width:100%;min-width:0;padding:8px 9px;border:1px solid #d3c5b4;border-radius:4px;background:#fffdf8;font:12px ui-monospace,monospace}.scene-path-input button{padding:8px 13px;border:1px solid #1d7052!important;border-radius:4px;color:#fff!important;background:#1f7a58!important}.scene-path-input button:disabled{opacity:.4;cursor:default}.scene-file-dialog section{display:grid;gap:7px}.recent-scene-list{display:grid;gap:6px}.recent-scene-list button{display:grid;gap:2px;padding:9px;border:1px solid #d8cbbb!important;border-radius:5px;background:#fffdf8!important;text-align:left}.recent-scene-list button:hover{border-color:#73a88e!important;background:#edf6f0!important}.recent-scene-list small,.scene-file-dialog>small{color:#776d64;overflow-wrap:anywhere}.recovery-path{padding:9px;border:1px dashed #c99035!important;border-radius:5px;color:#6d511d!important;background:#fff6e6!important;text-align:left;overflow-wrap:anywhere}.initialization-group{display:grid;gap:10px;margin-bottom:12px;padding:12px;border:1px solid #d8cbbb;border-radius:6px;background:#fffdf8}.initialization-group>header{display:flex;align-items:end;justify-content:space-between;gap:12px}.initialization-group>header div{display:grid}.initialization-group>header span,.initialization-group>header small{color:#786d64;font:10px ui-monospace,monospace}.initialization-group>p{margin:0;color:#6c625a;font-size:11px;line-height:1.45}.initialization-value{display:grid;grid-template-columns:120px minmax(0,1fr) auto;gap:8px;align-items:end}.initialization-value label{display:grid;gap:4px;color:#5f554d;font-size:10px;font-weight:700}.initialization-value input,.initialization-value select{width:100%;min-width:0;padding:7px 8px;border:1px solid #d3c5b4;border-radius:4px;background:#fff}.initialization-value button,.initialization-object-list button{padding:7px 9px;border:1px solid #85ad99!important;border-radius:4px;color:#176047!important;background:#edf6f0!important}.initialization-value button:disabled,.initialization-object-list button:disabled{opacity:.4;cursor:default}.initialization-object-list{display:grid;gap:5px}.initialization-object-list>div{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:8px;align-items:center;padding-top:5px;border-top:1px solid #ece1d2;font-size:11px}.initialization-object-list code{color:#a33d31}@media(max-width:980px){.scene-toolbar{grid-template-columns:1fr}.view-tabs{grid-column:auto}.scene-counts,.scene-actions{justify-content:flex-start}.scene-workspace{grid-template-columns:1fr}.scene-inspector{display:none}}.application-form{width:min(780px,100%)}.object-form{width:min(640px,100%)}.override-form{width:min(760px,100%)}.application-form>header div{display:grid;gap:2px}.object-form>header div{display:grid;gap:2px}.override-form>header div{display:grid;gap:2px}.application-form>header span{color:#786d64;font-size:11px}.object-form>header span{color:#786d64;font-size:11px}.override-form>header span{color:#786d64;font-size:11px}.application-form-content,.object-form-content,.override-form-content{display:grid;gap:14px}.application-form-content>label,.application-form fieldset label,.object-form-content label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.override-form-content label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.application-form input,.application-form select,.object-form input,.object-form select,.override-form input,.override-form select{width:100%;min-width:0;padding:8px 9px;border:1px solid #d3c5b4;border-radius:4px;background:#fffdf8;color:#332c26;font:12px ui-monospace,monospace}.override-form fieldset{margin:0;padding:12px;border:1px solid #ded2c2;border-radius:5px}.override-form legend{padding:0 6px;color:#2d6652;font-weight:800}.override-form fieldset label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.override-scope-choice{display:grid;grid-template-columns:1fr 1fr;gap:9px}.override-scope-choice button{display:grid;gap:3px;padding:11px;border:1px solid #d4c7b6!important;border-radius:5px;background:#fffdf8!important;text-align:left}.override-scope-choice button.active{border-color:#72a88d!important;background:#edf6f0!important}.override-scope-choice span{color:#756a61;font-size:10px;line-height:1.4}.override-warning{display:grid;gap:3px;padding:10px;border-left:3px solid #c99035;background:#fff6e6}.override-warning span{color:#74685e;font-size:11px}.application-form fieldset{margin:0;padding:12px;border:1px solid #ded2c2;border-radius:5px}.application-form legend{padding:0 6px;color:#2d6652;font-weight:800}.form-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.parameter-list{display:grid;gap:9px}.parameter-row{display:grid;grid-template-columns:minmax(0,1fr) 150px;gap:9px;align-items:end}.parameter-row>label>span{color:#3c342e}.parameter-row small{color:#8a7e74;font-weight:400}.selector-summary{margin:10px 0 0;color:#796e64;font-size:11px}.application-form>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.object-form>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.override-form>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.application-form>footer button,.object-form>footer button,.override-form>footer button,.inspector-actions button{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border:1px solid #d2c4b3;border-radius:4px;background:#fffdf8}.application-form>footer .primary,.object-form>footer .primary{color:#fff;border-color:#1d7052;background:#1f7a58}.override-form>footer .primary{color:#fff;border-color:#1d7052;background:#1f7a58}.application-form>footer button:disabled{opacity:.45}.object-form>footer button:disabled{opacity:.45}.override-form>footer button:disabled{opacity:.45}.inspector-actions{display:flex;gap:7px;margin:10px 0 16px}.inspector-actions .danger{color:#a33d31;border-color:#d8a296}.binding-form{width:min(680px,100%)}.binding-form>header div{display:grid;gap:2px}.binding-form>header span{color:#786d64;font-size:11px}.binding-form .overlay-content{display:grid;gap:14px}.binding-route{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:14px;padding:12px;border:1px solid #d8cbbb;border-radius:5px;background:#fffdf8}.binding-route>div{display:grid;gap:3px;min-width:0}.binding-route>div:last-child{text-align:right}.binding-route small{color:#7c7168;text-transform:uppercase;font-size:9px}.binding-route strong,.binding-route code{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.binding-route code{color:#1f7053}.binding-form fieldset{margin:0;padding:12px;border:1px solid #ded2c2;border-radius:5px}.binding-form legend{padding:0 6px;color:#2d6652;font-weight:800}.binding-form fieldset label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.binding-form select{width:100%;min-width:0;padding:8px 9px;border:1px solid #d3c5b4;border-radius:4px;background:#fffdf8;color:#332c26;font:12px ui-monospace,monospace}.binding-form>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.binding-form>footer button{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border:1px solid #d2c4b3;border-radius:4px;background:#fffdf8}.binding-form>footer .primary{color:#fff;border-color:#1d7052;background:#1f7a58}.cycle-break-dialog{width:min(620px,100%)}.cycle-break-dialog>header div{display:grid;gap:2px}.cycle-break-dialog>header span{color:#a33d31;font:11px ui-monospace,monospace}.cycle-break-dialog .overlay-content{display:grid;gap:13px}.cycle-break-dialog p{margin:0;line-height:1.5}.cycle-impact{display:grid;gap:3px;padding:10px;border-left:3px solid #c54c3c;background:#fff0eb}.cycle-impact span{color:#705f57;font-size:11px}.cycle-break-dialog fieldset{margin:0;padding:12px;border:1px solid #d8cbbb;border-radius:5px}.cycle-break-dialog legend{padding:0 6px;color:#9a392e;font-weight:800}.cycle-break-dialog label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.cycle-break-dialog input,.cycle-break-dialog select{width:100%;min-width:0;padding:8px 9px;border:1px solid #d3c5b4;border-radius:4px;background:#fffdf8}.cycle-break-dialog>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.cycle-break-dialog>footer button{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border:1px solid #d2c4b3;border-radius:4px;background:#fffdf8}.cycle-break-dialog>footer .primary{color:#fff;border-color:#a8392d;background:#b94435}.cycle-break-dialog>footer button:disabled{opacity:.45;cursor:default}@media(max-width:700px){.form-grid,.parameter-row,.override-scope-choice,.binding-route{grid-template-columns:1fr}.binding-route>div:last-child{text-align:left}.initialization-value{grid-template-columns:1fr}}*{box-sizing:border-box}body{margin:0;color:var(--ink);background:radial-gradient(circle at 20% 24%,rgba(31,122,83,.045),transparent 30%),radial-gradient(circle at 78% 68%,rgba(201,144,53,.055),transparent 34%),linear-gradient(180deg,rgba(255,250,242,.26),transparent 42%),var(--bg);font-family:Avenir Next,Trebuchet MS,Segoe UI,sans-serif}.app-shell{display:grid;grid-template-columns:minmax(0,1fr);height:100vh;position:relative}.app-shell.has-side-panel{grid-template-columns:minmax(0,1fr) 340px}.graph-panel{position:relative;min-width:0}.graph-panel:after{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;opacity:.2;background-image:radial-gradient(rgba(49,39,33,.11) .55px,transparent .55px);background-size:16px 16px;-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 22%,black 82%,transparent 100%);mask-image:linear-gradient(to bottom,transparent 0%,black 22%,black 82%,transparent 100%)}.topbar{position:absolute;z-index:10;top:18px;left:18px;right:18px;display:flex;align-items:center;gap:12px;padding:11px 14px;background:#fffaf2eb;border:1px solid var(--line);border-radius:14px;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.topbar:before{content:"";align-self:stretch;width:5px;border-radius:999px;background:var(--accent)}.editor-feedback{position:absolute;z-index:12;top:96px;left:18px;right:18px;padding:9px 12px;border:1px solid var(--line);border-radius:10px;background:#fffaf2f2;box-shadow:0 12px 30px var(--shadow);max-height:132px;overflow:auto;font-size:12px;line-height:1.45;white-space:normal;overflow-wrap:anywhere}.editor-feedback.error{color:var(--clay);border-color:#bf6a5466;background:#fff4eef7}.editor-feedback.info{color:var(--accent);border-color:#1f7a5359;background:#f5fcf8f7}.cycle-break-prompt{position:absolute;z-index:14;top:98px;left:18px;right:18px;display:flex;align-items:center;justify-content:space-between;gap:14px;padding:11px 12px;border:1px solid rgba(211,66,47,.32);border-radius:12px;background:linear-gradient(135deg,rgba(211,66,47,.12),transparent 62%),#fffaf2f5;box-shadow:0 16px 36px #382b2329}.cycle-break-prompt div{display:grid;gap:3px;min-width:0}.cycle-break-prompt strong{color:#7a2018;font-size:13px}.cycle-break-prompt span{color:var(--muted);font-size:12px;line-height:1.35}.cycle-break-prompt code{color:#7a2018;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.cycle-break-prompt.active{border-color:#d3422f85;box-shadow:0 16px 36px #382b2329,0 0 0 4px #d3422f14}.cycle-break-cta{flex:0 0 auto;justify-content:center;font-weight:780}.graph-workbench{flex-wrap:wrap;align-items:flex-start;max-height:min(36vh,250px);overflow:auto;scrollbar-width:thin}.brand-block{min-width:150px}.brand-block h1{font-size:clamp(16px,2.2vw,20px)}.search-box{position:relative;display:flex;align-items:center;gap:8px;flex:1 1 280px;max-width:460px;min-width:220px;padding:7px 9px;border:1px solid var(--line);border-radius:11px;background:#fffdf7db}.search-box input{width:100%;min-width:0;border:0;outline:0;color:var(--ink);background:transparent;font:inherit;font-size:13px}.clear-search{display:grid;place-items:center;width:20px;height:20px;border:0;border-radius:999px;background:#b7a6962e;color:var(--muted);cursor:pointer}.search-results{position:absolute;z-index:80;top:calc(100% + 8px);left:0;right:0;display:grid;gap:6px;max-height:360px;overflow:auto;padding:8px;border:1px solid var(--line);border-radius:12px;background:#fffaf2fa;box-shadow:0 18px 45px var(--shadow)}.search-result{display:grid;gap:2px;padding:8px 9px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--ink);text-align:left;cursor:pointer}.search-result:hover,.search-result:focus-visible{border-color:#1f7a533d;background:var(--accent-soft);outline:none}.search-result strong{overflow-wrap:anywhere;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.search-result span{color:var(--muted);font-size:11px}.eyebrow{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.08em;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}h1,h2,h3{margin:0;letter-spacing:0}h1{font-size:20px;font-weight:800}.metrics{display:flex;flex-wrap:wrap;gap:8px;margin-left:auto}.metrics span,.metric-button,.node-meta span{display:inline-flex;align-items:center;gap:5px;padding:5px 8px;border:1px solid var(--line);border-radius:9px;background:#fffaf2e6;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.metric-button{color:var(--ink);cursor:pointer}.metric-button.active,.metric-button:hover,.metric-button:focus-visible{outline:none;background:#fff6f0}.view-mode-button.overview-cta{border-color:#1f7a5361;color:var(--accent);background:#1f7a531a;box-shadow:0 8px 18px #1f7a531a;font-weight:800}.meta-chip{position:relative}.meta-chip:after{content:attr(data-tooltip);position:absolute;z-index:20;left:0;bottom:calc(100% + 10px);width:max-content;max-width:280px;padding:8px 10px;color:var(--paper);background:var(--ink);border-radius:10px;box-shadow:0 12px 28px #382b232e;font-family:Avenir Next,Trebuchet MS,Segoe UI,sans-serif;font-size:12px;line-height:1.3;white-space:normal;opacity:0;pointer-events:none;transform:translateY(4px);transition:opacity .12s ease,transform .12s ease}.meta-chip:before{content:"";position:absolute;z-index:21;left:16px;bottom:calc(100% + 4px);width:10px;height:10px;background:var(--ink);opacity:0;pointer-events:none;transform:rotate(45deg) translateY(4px);transition:opacity .12s ease,transform .12s ease}.meta-chip:hover:after,.meta-chip:hover:before,.meta-chip:focus-visible:after,.meta-chip:focus-visible:before{opacity:1;transform:translateY(0)}.meta-chip:hover:before,.meta-chip:focus-visible:before{transform:rotate(45deg) translateY(0)}.metrics .warn{color:var(--clay);border-color:#c9614a73}.metrics .caution{color:var(--ochre);border-color:#c9903573}.metrics .warn svg{flex:0 0 auto}.icon-button{display:grid;place-items:center;width:34px;height:34px;border:1px solid var(--line);border-radius:10px;background:var(--paper);color:var(--ink);cursor:pointer;box-shadow:0 8px 18px #382b2314}.icon-button.compact{width:28px;height:28px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:14px}.toolbar-group{display:flex;align-items:center;gap:8px}.open-button{flex:0 0 auto}.panel-switch{flex-wrap:wrap}.select-control{display:inline-flex;align-items:center;gap:6px;padding:5px 8px;border:1px solid var(--line);border-radius:10px;background:#fffaf2e6;color:var(--muted)}.select-control select{max-width:132px;border:0;outline:0;background:transparent;color:var(--ink);font:inherit;font-size:12px}.relationship-legend{position:absolute;z-index:35;top:116px;left:18px;display:grid;gap:7px;width:190px;padding:10px;border:1px solid var(--line);border-radius:13px;background:#fffaf2eb;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);max-height:min(480px,calc(100vh - 142px));overflow:auto}.legend-title,.legend-note{display:flex;align-items:center;gap:6px;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.relationship-legend button{display:flex;align-items:center;gap:8px;padding:6px 7px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--muted);font:inherit;font-size:12px;cursor:pointer}.relationship-legend button.active{color:var(--ink);border-color:#1f7a5338;background:var(--accent-soft)}.legend-line{width:28px;height:0;border-top:2px solid var(--line-strong)}.legend-line.mapped{border-color:var(--accent);border-style:dashed}.legend-line.call{border-color:var(--clay);border-style:dashed}.scale-controls{position:absolute;z-index:35;top:116px;left:220px;display:grid;gap:8px;width:190px;padding:10px;border:1px solid var(--line);border-radius:13px;background:#fffaf2eb;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);max-height:min(560px,calc(100vh - 142px));overflow:auto}.scale-list{display:grid;gap:6px}.scale-list button,.scale-reset{display:grid;gap:2px;width:100%;padding:7px 8px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--muted);font:inherit;text-align:left;cursor:pointer}.scale-list button.active{color:var(--ink);border-color:#1f7a5338;background:var(--accent-soft)}.scale-list button.collapsed{border-color:#b7a69657;border-style:dashed;background:#b7a69614}.scale-list span{overflow-wrap:anywhere;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;font-weight:700}.scale-list small{color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.06em}.scale-reset{color:var(--accent);border-color:#1f7a5333;background:#1f7a5314;text-align:center}.floating-panel{position:absolute;z-index:40;top:116px;right:18px;width:min(360px,calc(100vw - 36px));max-height:min(560px,calc(100vh - 142px));overflow:auto;padding:14px;background:#fffaf2f5;border:1px solid rgba(191,106,84,.32);border-radius:14px;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}.warnings-panel{border-color:#c9903559}.open-panel{left:18px;right:auto;border-color:#1f7a5352}.floating-panel-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}.floating-panel h2{font-size:18px}.react-flow{background:transparent;z-index:1}.react-flow__background{display:none}.react-flow__edges{z-index:8}.react-flow__edges:has(.react-flow__edge.highlighted){z-index:120}.react-flow__edgelabel-renderer{z-index:28}.react-flow__edgelabel-renderer:has(.edge-chip.highlighted){z-index:121}.react-flow__nodes{z-index:20}.model-node{position:relative;overflow:visible;background:#fffaf2f5;border:1px solid var(--line);border-radius:16px;box-shadow:0 18px 42px var(--shadow)}.model-remove-button{position:absolute;z-index:45;top:-11px;right:-11px;display:grid;place-items:center;width:30px;height:30px;border:1px solid rgba(191,106,84,.4);border-radius:999px;color:#fff;background:var(--clay);box-shadow:0 10px 24px #bf6a543d;cursor:pointer}.model-remove-button:hover,.model-remove-button:focus-visible{outline:none;background:#9b3f2e;transform:translateY(-1px)}.node-header{border-radius:16px 16px 0 0}.model-node.selected{border-color:#1f7a53b8;box-shadow:0 20px 48px #1f7a5324}.model-node.focused{border-color:#1f7a539e;box-shadow:0 20px 48px #1f7a5324,0 0 0 4px #1f7a5314}.model-node.dimmed{opacity:.2;filter:grayscale(.2)}.model-node.cyclic{border-color:#bf6a549e;box-shadow:0 18px 42px var(--shadow),0 0 0 3px #bf6a5414}.model-node.cyclic .node-header>svg{color:var(--clay)}.model-node.hard_dependency{border-color:#bf6a548c;border-style:dashed;background:#fff6f0f0;box-shadow:0 14px 30px #bf6a541f}.model-node.hard_dependency .node-header{background:linear-gradient(90deg,rgba(191,106,84,.08),transparent 60%),var(--paper-strong)}.model-node.hard_dependency .node-header>svg{color:var(--clay)}.model-node.hard_dependency .process:before{content:"↳ ";color:var(--clay)}.model-node.overview-node{border-radius:12px;box-shadow:0 10px 24px #382b231a}.model-node.overview-node .node-header{min-height:64px;padding:9px 10px;border-radius:12px 12px 0 0}.model-node.overview-node .process{font-size:12px;line-height:1.18;overflow-wrap:anywhere}.model-node.overview-node .model-type{font-size:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.overview-node-summary{display:flex;flex-wrap:wrap;gap:5px;padding:8px 10px 10px}.overview-node-summary span{min-width:0;max-width:100%;padding:3px 6px;border:1px solid rgba(183,166,150,.35);border-radius:999px;color:var(--muted);background:#fffdf7c2;font-size:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.overview-port-handles .react-flow__handle{width:7px;height:7px;border-color:#fffaf2e6;background:var(--line-strong)}.overview-port-handles .react-flow__handle-right{background:var(--accent)}.hard-chip{color:var(--clay);border-color:#bf6a5447!important;background:#fff6f0e6!important}.node-header{display:grid;grid-template-columns:1fr auto;align-items:flex-start;gap:11px;padding:11px 12px 12px;color:var(--ink);background:var(--paper-strong);border-bottom:1px solid var(--line)}.node-header>svg{color:var(--accent)}.process{font-weight:820;font-size:15px;letter-spacing:0}.model-type{margin-top:2px;font-size:12px;color:var(--muted);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.node-meta{display:flex;flex-wrap:wrap;gap:6px;padding:10px 12px 0}.ports-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;padding:12px}.port-title{margin-bottom:6px;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.08em;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.port{position:relative;display:flex;align-items:center;gap:5px;min-height:24px;margin:4px 0;padding:4px 7px;border:1px solid var(--line);border-radius:8px;background:#fffdf7e6;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.port:after{content:attr(data-default);position:absolute;z-index:60;left:50%;bottom:calc(100% + 8px);width:max-content;max-width:240px;padding:7px 9px;color:var(--paper);background:var(--ink);border-radius:9px;box-shadow:0 12px 28px #382b232e;font-family:Avenir Next,Trebuchet MS,Segoe UI,sans-serif;font-size:11px;line-height:1.3;white-space:normal;opacity:0;pointer-events:none;transform:translate(-50%,4px);transition:opacity .12s ease,transform .12s ease}.port:hover:after,.port.active:after{opacity:1;transform:translate(-50%)}.app-shell.has-candidate-popover .port.active:after{opacity:0}.port.output{justify-content:flex-end}.port.previous{color:var(--clay)}.port.mapped{border-color:#1f7a5361}.port-candidate-button{position:relative;z-index:8;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:16px;height:16px;padding:0;color:var(--accent);background:#fffdf7f0;border:1px solid rgba(31,122,83,.34);border-radius:999px;cursor:pointer}.port-candidate-button:hover,.port-candidate-button:focus-visible{color:#fffdfa;background:var(--accent);outline:none}.port-cycle-break-button{position:relative;z-index:9;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:18px;height:18px;padding:0;color:#9b2d22;background:#fff1ecf5;border:1px solid rgba(211,66,47,.45);border-radius:999px;cursor:pointer;box-shadow:0 5px 12px #d3422f29}.port-cycle-break-button:hover,.port-cycle-break-button:focus-visible{color:#fffdfa;background:#d3422f;outline:none}.port.active .port-candidate-button{color:var(--accent);background:#fffdfa}@keyframes cycleTargetPulse{0%,to{box-shadow:inset 3px 0 #d3422fc7,0 0 0 3px #d3422f14}50%{box-shadow:inset 3px 0 #d3422feb,0 0 0 6px #d3422f26}}.candidate-popover{position:fixed;z-index:1200;display:grid;grid-template-rows:auto minmax(0,1fr);overflow:hidden;color:var(--ink);border:1px solid rgba(31,122,83,.28);border-radius:8px;background:linear-gradient(135deg,rgba(31,122,83,.08),transparent 42%),#fffdf7fa;box-shadow:0 18px 42px #382b232e}.candidate-popover-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:12px 12px 9px;border-bottom:1px solid rgba(183,166,150,.32)}.candidate-popover-header h3{margin:3px 0 0;overflow-wrap:anywhere;font-size:15px}.candidate-popover-list{display:grid;gap:8px;overflow:auto;padding:10px}.candidate-model-card{display:grid;gap:5px;width:100%;padding:9px 10px;color:var(--ink);border:1px solid rgba(183,166,150,.38);border-left:4px solid var(--accent);border-radius:8px;background:#ffffffb3;font:inherit;text-align:left;cursor:pointer}.candidate-model-card:hover,.candidate-model-card:focus-visible{background:#fffdf7f5;border-color:#1f7a536b;outline:none}.candidate-model-card strong{overflow-wrap:anywhere;font-size:12px}.candidate-model-card span{color:var(--muted);font-size:11px}.candidate-model-card small{overflow-wrap:anywhere;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.04em}.port.required-input{border-color:#bf6a54b8;background:linear-gradient(90deg,#bf6a5429,#fffdf7eb 62%),#fffdf7e6;box-shadow:inset 3px 0 #bf6a54bd}.port.required-input .react-flow__handle{border-color:var(--clay);background:#fff6f0;box-shadow:0 0 0 3px #fffaf2eb,0 0 0 6px #bf6a5421}.port.cycle-break-target{border-color:#d3422fb8;background:linear-gradient(90deg,#d3422f2e,#fffdf7eb 62%),#fffdf7e6;box-shadow:inset 3px 0 #d3422fc7,0 0 0 3px #d3422f14}.cycle-break-mode .port.cycle-break-target{animation:cycleTargetPulse 1.35s ease-in-out infinite}.port.cycle-break-target .react-flow__handle{border-color:#d3422f;background:#fff1ec;box-shadow:0 0 0 3px #fffaf2eb,0 0 0 7px #d3422f26}.port.highlighted{border-color:var(--line);background:#fffdfa}.port.focused{border-color:#1f7a5394;background:var(--accent-soft)}.port.active{color:#fffdfa;border-color:var(--accent);background:var(--accent)}.port.cycle-break-target.active,.port.cycle-break-target.focused,.port.cycle-break-target.highlighted{color:#7a2018;border-color:#d3422fc7;background:linear-gradient(90deg,#d3422f33,#fffdf7f0 64%),#fffdf7f0;box-shadow:inset 3px 0 #d3422fdb,0 0 0 4px #d3422f1f}.port.cycle-break-target.active:after{background:#7a2018}.port.cycle-break-target.active .port-cycle-break-button,.port.cycle-break-target.focused .port-cycle-break-button,.port.cycle-break-target.highlighted .port-cycle-break-button{color:#9b2d22;background:#fff1ecfa}.react-flow__handle{width:9px;height:9px;border:1px solid var(--line-strong);background:var(--paper);z-index:25;box-shadow:0 0 0 3px #fffaf2eb}.react-flow__handle.call-handle{width:12px;height:36px;opacity:0;pointer-events:none}.react-flow__edge.multiscale path{stroke-dasharray:7 5}.react-flow__edge.mapped_variable path{stroke:var(--accent)}.react-flow__edge.hard_dependency path{stroke:var(--clay)}.react-flow__edge.cycle_dependency path,.react-flow__edge.cycle_edge path{stroke:#d3422f;filter:drop-shadow(0 0 5px rgba(211,66,47,.26))}.react-flow__edge.call_edge path{stroke-width:1.7;stroke-dasharray:3 6;opacity:.7}.react-flow__edge.highlighted path{stroke-width:3;stroke:var(--accent)}.react-flow__edge.focused path{stroke-width:2.8}.react-flow__edge.highlighted{z-index:80!important}.react-flow__edge.focused{z-index:70!important}.react-flow__edge.dimmed{opacity:.04}.overview-mode .react-flow__edge.variable_edge path{stroke-width:1.45!important;opacity:.74}.overview-mode .react-flow__edge.call_edge path{stroke-width:1.15!important;opacity:.4}.overview-mode .react-flow__edge.highlighted path,.overview-mode .react-flow__edge.focused path{stroke-width:2.8!important;opacity:1}.edge-chip{position:absolute;z-index:-1;display:inline-flex;align-items:center;gap:6px;padding:3px 7px;color:var(--ink);background:#fffaf2f0;border:1px solid var(--line);border-radius:999px;box-shadow:0 8px 20px #382b231a;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:1;pointer-events:none;white-space:nowrap}.edge-chip.mapped_variable,.edge-chip.multiscale{border-color:#1f7a534d;background:#f8faf1f5}.edge-chip.hard_dependency{border-color:#bf6a5452;background:#fff6f0f5}.edge-chip.cycle_dependency,.edge-chip.cycle_edge{color:#7a2018;border-color:#d3422f70;background:#ffeee9fa}.edge-chip small{color:var(--muted);font-size:9px;letter-spacing:.02em;text-transform:uppercase}.edge-chip.highlighted{z-index:80;color:#fffdfa;border-color:var(--accent);background:var(--accent);box-shadow:0 12px 24px #1f7a5338}.edge-chip.highlighted small{color:#fffdf7b8}.edge-chip.dimmed{opacity:.04}.overview-mode .edge-chip:not(.highlighted):not(.focused),.overview-mode .edge-terminal:not(.highlighted):not(.focused){display:none}.edge-chip.focused{z-index:70;border-color:#1f7a535c;box-shadow:0 10px 22px #1f7a5324}.edge-terminal{position:absolute;z-index:32;--terminal-color: var(--line-strong);width:18px;height:10px;pointer-events:none;opacity:.95}.edge-terminal:before{content:"";position:absolute;top:4px;left:2px;right:2px;height:2px;border-radius:999px;background:var(--terminal-color)}.edge-terminal.target:after{content:"";position:absolute;top:1px;width:0;height:0;border-top:4px solid transparent;border-bottom:4px solid transparent}.edge-terminal[data-side=left]:before{left:7px}.edge-terminal[data-side=right]:before{right:7px}.edge-terminal.target[data-side=left]:after{right:0;border-left:7px solid var(--terminal-color)}.edge-terminal.target[data-side=right]:after{left:0;border-right:7px solid var(--terminal-color)}.edge-terminal.highlighted:before{height:3px}.edge-terminal.dimmed{opacity:.12}.cycle-edge-card{border-color:#d3422f57;background:linear-gradient(135deg,rgba(211,66,47,.1),transparent 58%),#fffaf2e0}.cycle-break-button{justify-content:center;margin:8px 0 4px}.inspector{border-left:1px solid var(--line);background:#fffaf2d1;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);padding:18px;overflow:auto}.inspector:focus{outline:none}.inspector.guided-focus{animation:guided-panel-focus 1.8s ease-out}@keyframes guided-panel-focus{0%{box-shadow:inset 0 0 0 3px #1f7a537a,-18px 0 44px #1f7a5329;background:#fafff7f0}60%{box-shadow:inset 0 0 0 3px #1f7a535c,-12px 0 34px #1f7a531f}to{box-shadow:none;background:#fffaf2d1}}.inspector header{display:flex;align-items:center;gap:8px;margin-bottom:14px}.mapping-code-panel{display:grid;gap:10px}.row-with-actions{display:flex;align-items:center;justify-content:space-between;gap:8px}.mapping-code{width:100%;min-height:260px;resize:vertical;border:1px solid var(--line);border-radius:10px;background:#fffdf7e6;color:var(--ink);padding:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.45}.storage-grid{display:grid;gap:8px}.path-status{display:grid;gap:3px;border:1px solid var(--line);border-radius:10px;background:#fffaf2b8;padding:9px}.path-status span{color:var(--muted);font-size:11px;font-weight:720}.path-status strong{color:var(--ink);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow-wrap:anywhere}.recent-mappings{display:grid;gap:8px;margin-top:4px}.open-mapping-panel{display:grid;gap:12px}.recent-mapping-list{display:grid;gap:7px}.recent-mapping-item{display:grid;gap:3px;text-align:left;border:1px solid var(--line);border-radius:10px;background:#fffdf7d1;color:var(--ink);padding:9px;cursor:pointer}.recent-mapping-item:hover,.recent-mapping-item:focus-visible{border-color:#1f7a5352;background:#1f7a5314}.recent-mapping-item span{font-weight:720}.recent-mapping-item small{color:var(--muted);overflow-wrap:anywhere}.inline-field{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px}.inspector h2{font-size:17px}.inspector h3{margin-top:22px;margin-bottom:8px;color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.08em}.row{display:grid;grid-template-columns:84px minmax(0,1fr);gap:8px;padding:8px 0;border-top:1px solid rgba(183,166,150,.35)}.row span{color:var(--muted)}.row strong{overflow-wrap:anywhere;font-weight:620}.variable-card{border:1px solid var(--line);border-radius:12px;background:#fffaf2bf;padding:10px}.edge-detail-card{display:grid;gap:4px;margin-bottom:14px;padding:10px;border:1px solid rgba(31,122,83,.24);border-radius:12px;background:linear-gradient(135deg,rgba(31,122,83,.08),transparent 58%),#fffaf2d1}.edge-detail-card .diagnostic,.edge-detail-card .empty-state{margin-top:6px}.variable-card .row:first-of-type{margin-top:8px}.variable-card-title{display:flex;align-items:center;justify-content:space-between;gap:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.variable-card-title span{overflow-wrap:anywhere;font-weight:720}.variable-card-title small{color:var(--muted);text-transform:uppercase;letter-spacing:.08em}.diagnostic,.edit-suggestion,.initialization-note,.empty-state{border:1px solid var(--line);border-radius:12px;background:#fffaf2bf;padding:10px;color:var(--muted)}.diagnostic,.edit-suggestion,.initialization-note{display:flex;align-items:center;gap:7px}.diagnostic,.edit-suggestion{color:var(--clay);border-color:#c9614a47;background:#c9614a17}.initialization-note{color:#87533b;border-color:#bf6a5452;background:#bf6a541a}.initialization-list{display:grid;gap:8px}.initialization-list.compact{gap:6px}.initialization-group{display:grid;gap:7px}.initialization-group h4,.provenance-block h4{margin:6px 0 0;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700}.initialization-item{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:10px;width:100%;padding:9px 10px;color:var(--ink);background:#fffaf2c7;border:1px solid rgba(191,106,84,.28);border-left:4px solid var(--clay);border-radius:10px;font:inherit;text-align:left;cursor:pointer}.initialization-item:hover,.initialization-item:focus-visible{background:#fff6f0f5;outline:none}.initialization-item span{overflow:hidden;color:var(--muted);font-size:12px;text-overflow:ellipsis;white-space:nowrap}.initialization-item strong{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.initialization-item small{grid-column:1 / -1;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.06em}.initialization-item.previous_time_step{border-left-color:var(--ochre)}.initialization-item.mapped_unresolved{border-left-color:var(--accent)}.warning-list,.provenance-block{display:grid;gap:8px}.warning-group{display:grid;gap:7px}.warning-group h4{margin:6px 0 0;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700}.warning-item,.provenance-edge{display:grid;gap:3px;width:100%;padding:9px 10px;border:1px solid rgba(201,144,53,.28);border-left:4px solid var(--ochre);border-radius:10px;background:#fffaf2c7;color:var(--ink);font:inherit;text-align:left;cursor:pointer}.warning-item.error{border-color:#bf6a5457;border-left-color:var(--clay);background:#bf6a5414}.warning-item.info{border-color:#7f8f7342;border-left-color:var(--sage);background:#7f8f7314}.provenance-edge{border-color:#b7a6966b;border-left-color:var(--line-strong)}.provenance-edge.mapped_variable{border-left-color:var(--accent)}.provenance-edge.hard_dependency{border-left-color:var(--clay)}.warning-item:hover,.warning-item:focus-visible,.provenance-edge:hover,.provenance-edge:focus-visible{background:#fff6f0f5;outline:none}.warning-item strong,.provenance-edge strong{overflow-wrap:anywhere;font-size:12px}.warning-item span,.provenance-edge span,.provenance-edge small{color:var(--muted);font-size:11px;line-height:1.25}.provenance-block{margin-top:10px}.empty-state.compact{padding:7px 8px;font-size:11px}.live-session{border-left:1px solid rgba(183,166,150,.36);padding-left:10px}.live-pill{display:inline-flex;align-items:center;height:28px;padding:0 9px;border:1px solid rgba(191,106,84,.38);border-radius:999px;color:var(--clay);background:#bf6a5414;font-size:11px;font-weight:800;text-transform:uppercase}.live-pill.connected{border-color:#1f7a5357;color:var(--accent);background:#1f7a5317}.metric-button:disabled{cursor:not-allowed;opacity:.42}.model-browser{display:grid;gap:7px}.model-browser-control{display:grid;gap:4px}.model-browser-control span,.parameter-row label{color:var(--muted);font-size:11px;font-weight:700}.model-browser-control select,.model-browser-control input,.parameter-row input,.parameter-row select{min-width:0;border:1px solid rgba(183,166,150,.44);border-radius:8px;background:#ffffffc7;color:var(--ink);font:inherit}.model-browser-control select,.model-browser-control input{height:32px;padding:0 8px}.model-browser-item{display:grid;gap:7px;padding:8px 9px;border:1px solid rgba(183,166,150,.34);border-radius:10px;background:#ffffff9e}.model-browser-item.add-model-config{gap:10px;padding-bottom:0}.model-browser-title{display:grid;gap:2px}.model-browser-item strong{overflow-wrap:anywhere;font-size:12px}.model-browser-item span{color:var(--muted);font-size:11px}.existing-model-editor{display:grid;gap:8px;margin-top:12px;padding-top:10px;border-top:1px solid rgba(183,166,150,.32)}.variable-mapping-editor{display:grid;gap:8px;margin-top:10px;padding-top:10px;border-top:1px solid rgba(183,166,150,.32)}.variable-mapping-editor h4{margin:0;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700}.initialization-editor{display:grid;gap:12px}.initialization-editor-group{display:grid;gap:8px}.initialization-editor-group h3{margin:0}.initialization-editor-row{display:grid;grid-template-columns:minmax(0,.85fr) minmax(0,1fr) minmax(82px,.72fr) auto;gap:7px;align-items:center;padding:9px;border:1px solid rgba(191,106,84,.26);border-left:4px solid var(--clay);border-radius:8px;background:#fffaf2c7}.initialization-editor-row.provided{border-color:#1f7a533d;border-left-color:var(--accent)}.initialization-editor-row label{overflow-wrap:anywhere;font-weight:700}.initialization-editor-row input,.initialization-editor-row select{min-width:0;height:30px;border:1px solid rgba(183,166,150,.44);border-radius:8px;background:#ffffffc7;color:var(--ink);font:inherit}.initialization-editor-row input{padding:0 8px}.initialization-editor-row small{grid-column:1 / -1;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.05em}.parameter-row{display:grid;grid-template-columns:minmax(0,.75fr) minmax(0,1fr) minmax(78px,.8fr);gap:6px;align-items:center}.parameter-row input,.parameter-row select{height:28px;padding:0 7px;font-size:12px}.rate-editor{display:grid;gap:6px;padding:7px;border:1px solid rgba(183,166,150,.28);border-radius:8px;background:#fcf9f4b8}.add-model-footer{position:sticky;bottom:-18px;display:flex;justify-content:flex-end;margin:2px -9px 0;padding:10px 9px;border-top:1px solid rgba(183,166,150,.34);border-radius:0 0 10px 10px;background:#fffaf2f0;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.rate-summary{color:var(--muted);font-size:11px;line-height:1.35}.rate-clock-row{display:grid;grid-template-columns:1fr 1fr;gap:6px}.rate-clock-row label{display:grid;gap:4px;color:var(--muted);font-size:11px;font-weight:700}.rate-clock-row input{min-width:0;height:28px;border:1px solid rgba(183,166,150,.44);border-radius:8px;background:#ffffffc7;color:var(--ink);font:inherit;padding:0 7px}.metric-button.danger{border-color:#bf6a546b;color:#9b3f2e;background:#fff2ecdb}@media(max-width:900px){.app-shell{grid-template-columns:1fr}.inspector{position:absolute;z-index:28;top:150px;right:16px;bottom:16px;width:min(360px,calc(100% - 32px));border:1px solid var(--line);border-radius:14px;box-shadow:0 18px 45px var(--shadow)}.relationship-legend,.scale-controls,.floating-panel{top:150px}.scale-controls{left:220px}}@media(max-width:720px){.topbar{top:10px;left:10px;right:10px;gap:8px;padding:9px 10px}.topbar:before{display:none}.brand-block{min-width:128px}.search-box{flex-basis:100%;max-width:none;min-width:0}.metrics{margin-left:0}.relationship-legend,.scale-controls,.floating-panel{top:132px;left:10px;right:10px;width:auto}.scale-controls{top:326px}.react-flow__minimap{display:none}}.mapping-dialog-overlay{position:fixed;inset:0;z-index:1000;background:#31272161;display:flex;align-items:center;justify-content:center}.mapping-dialog{background:var(--paper);border:1px solid var(--line-strong);border-radius:10px;box-shadow:0 8px 32px var(--shadow);width:360px;max-width:calc(100vw - 32px);display:flex;flex-direction:column;gap:0;overflow:hidden}.mapping-dialog-header{display:flex;align-items:center;justify-content:space-between;padding:14px 16px 10px;border-bottom:1px solid var(--line)}.mapping-dialog-header .eyebrow{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.07em;color:var(--muted)}.mapping-dialog-body{padding:14px 16px;display:flex;flex-direction:column;gap:14px}.mapping-port-summary{display:flex;align-items:center;gap:8px;background:var(--paper-strong);border:1px solid var(--line);border-radius:6px;padding:10px 12px}.mapping-port{display:flex;flex-direction:column;gap:1px;flex:1;min-width:0}.mapping-port small{font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}.mapping-port strong{font-size:12px;font-weight:700;color:var(--accent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mapping-port span{font-size:11px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mapping-port.target strong{color:var(--clay)}.mapping-arrow{font-size:18px;color:var(--muted);flex-shrink:0}.mapping-mode-section{display:flex;flex-direction:column;gap:6px}.mapping-mode-label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);margin-bottom:2px}.mapping-radio{display:flex;align-items:center;gap:8px;font-size:13px;cursor:pointer;padding:4px 0}.mapping-radio input[type=radio]{accent-color:var(--accent);width:14px;height:14px;flex-shrink:0}.mapping-scale-picker{display:flex;flex-direction:column;gap:4px}.mapping-checkbox{display:flex;align-items:center;gap:8px;font-size:13px;cursor:pointer;padding:2px 0}.mapping-checkbox input[type=checkbox]{accent-color:var(--accent);width:14px;height:14px;flex-shrink:0}.mapping-dialog-footer{display:flex;justify-content:flex-end;gap:8px;padding:10px 16px 14px;border-top:1px solid var(--line)}.accent-button{background:var(--accent);color:#fff;border-color:transparent}.accent-button:hover:not(:disabled){background:var(--sage-dark);border-color:transparent}.selector-preview{display:grid;gap:6px;padding:12px;border:1px solid #b9d3c4;background:#f2f8f4;border-radius:6px}.selector-preview span,.selector-preview p{margin:0;color:#615c55;font-size:.86rem}.selector-preview p{color:#a44b39}.selector-preview-button{display:inline-flex;align-items:center;gap:6px;width:max-content}.environment-ports{border-top:1px solid #ded5c8;margin-top:8px;padding-top:8px}.entity-node.environment{border-color:#8fb9c2;background:#f3fafb}.application-configuration-form{width:min(820px,100%)}.application-configuration-content{display:grid;gap:14px}.application-configuration-content fieldset{margin:0;padding:12px;border:1px solid #ded2c2;border-radius:5px}.application-configuration-content legend{padding:0 6px;color:#2d6652;font-weight:800}.application-configuration-content p{margin:0;color:#786d64}.configuration-list{display:grid;gap:7px;margin-bottom:10px}.configuration-list>div,.configuration-list>label{min-height:36px;display:grid;grid-template-columns:minmax(100px,.6fr) minmax(0,1fr) auto;align-items:center;gap:10px;padding:7px 9px;border:1px solid #e4d9ca;border-radius:5px;background:#fffdf8}.configuration-list span{overflow:hidden;color:#786d64;text-overflow:ellipsis;white-space:nowrap}.configuration-list select,.compact-configuration-row input,.compact-configuration-row select{width:100%;min-height:34px;border:1px solid #cdbda8;border-radius:5px;background:#fffdf8;color:#302923}.compact-configuration-row{align-items:end}.compact-configuration-row label{display:grid;gap:5px;color:#655b52;font-size:12px;font-weight:700}.compact-configuration-row button{min-height:34px;display:inline-flex;align-items:center;justify-content:center;gap:5px}.icon-button{width:32px;height:32px;display:inline-grid;place-items:center;padding:0}.application-configuration-form>footer{display:flex;justify-content:flex-end;padding:12px 15px;border-top:1px solid #ded2c2} +.react-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgba(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.react-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgba(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #777;--xy-background-pattern-lines-color-default: #777;--xy-background-pattern-cross-color-default: #777;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.react-flow__background{background-color:var(--xy-background-color-props, var(--xy-background-color, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.react-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.react-flow__pane{z-index:1}.react-flow__pane.draggable{cursor:grab}.react-flow__pane.dragging{cursor:grabbing}.react-flow__pane.selection{cursor:pointer}.react-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.react-flow__renderer{z-index:4}.react-flow__selection{z-index:6}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible{outline:none}.react-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.react-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.react-flow .react-flow__edges{position:absolute}.react-flow .react-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.react-flow__edge{pointer-events:visibleStroke}.react-flow__edge.selectable{cursor:pointer}.react-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.react-flow__edge.animated path.react-flow__edge-interaction{stroke-dasharray:none;animation:none}.react-flow__edge.inactive{pointer-events:none}.react-flow__edge.selected,.react-flow__edge:focus,.react-flow__edge:focus-visible{outline:none}.react-flow__edge.selected .react-flow__edge-path,.react-flow__edge.selectable:focus .react-flow__edge-path,.react-flow__edge.selectable:focus-visible .react-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.react-flow__edge-textwrapper{pointer-events:all}.react-flow__edge .react-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__arrowhead polyline{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__arrowhead polyline.arrowclosed{fill:var(--xy-edge-stroke, var(--xy-edge-stroke-default))}.react-flow__connection{pointer-events:none}.react-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.react-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.react-flow__nodes{pointer-events:none;transform-origin:0 0}.react-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.react-flow__node.selectable{cursor:pointer}.react-flow__node.draggable{cursor:grab;pointer-events:all}.react-flow__node.draggable.dragging{cursor:grabbing}.react-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.react-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.react-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.react-flow__handle.connectingfrom{pointer-events:all}.react-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.react-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.react-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.react-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.react-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.react-flow__edgeupdater{cursor:move;pointer-events:all}.react-flow__pane.selection .react-flow__panel{pointer-events:none}.react-flow__panel{position:absolute;z-index:5;margin:15px}.react-flow__panel.top{top:0}.react-flow__panel.bottom{bottom:0}.react-flow__panel.top.center,.react-flow__panel.bottom.center{left:50%;transform:translate(-15px) translate(-50%)}.react-flow__panel.left{left:0}.react-flow__panel.right{right:0}.react-flow__panel.left.center,.react-flow__panel.right.center{top:50%;transform:translateY(-15px) translateY(-50%)}.react-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.react-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.react-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.react-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.react-flow__minimap-svg{display:block}.react-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.react-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.react-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.react-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.react-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.react-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.react-flow__controls.horizontal{flex-direction:row}.react-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.react-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.react-flow__edge.updating .react-flow__edge-path{stroke:#777}.react-flow__edge-text{font-size:10px}.react-flow__node.selectable:focus,.react-flow__node.selectable:focus-visible{outline:none}.react-flow__node-input,.react-flow__node-default,.react-flow__node-output,.react-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.react-flow__node-input.selectable:hover,.react-flow__node-default.selectable:hover,.react-flow__node-output.selectable:hover,.react-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.react-flow__node-input.selectable.selected,.react-flow__node-input.selectable:focus,.react-flow__node-input.selectable:focus-visible,.react-flow__node-default.selectable.selected,.react-flow__node-default.selectable:focus,.react-flow__node-default.selectable:focus-visible,.react-flow__node-output.selectable.selected,.react-flow__node-output.selectable:focus,.react-flow__node-output.selectable:focus-visible,.react-flow__node-group.selectable.selected,.react-flow__node-group.selectable:focus,.react-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.react-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.react-flow__nodesselection-rect,.react-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.react-flow__nodesselection-rect:focus,.react-flow__nodesselection-rect:focus-visible,.react-flow__selection:focus,.react-flow__selection:focus-visible{outline:none}.react-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.react-flow__controls-button:disabled{pointer-events:none}.react-flow__controls-button:disabled svg{fill-opacity:.4}.react-flow__controls-button:last-child{border-bottom:none}.react-flow__controls.horizontal .react-flow__controls-button{border-bottom:none;border-right:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) )}.react-flow__controls.horizontal .react-flow__controls-button:last-child{border-right:none}.react-flow__resize-control{position:absolute}.react-flow__resize-control.left,.react-flow__resize-control.right{cursor:ew-resize}.react-flow__resize-control.top,.react-flow__resize-control.bottom{cursor:ns-resize}.react-flow__resize-control.top.left,.react-flow__resize-control.bottom.right{cursor:nwse-resize}.react-flow__resize-control.bottom.left,.react-flow__resize-control.top.right{cursor:nesw-resize}.react-flow__resize-control.handle{width:5px;height:5px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));translate:-50% -50%}.react-flow__resize-control.handle.left{left:0;top:50%}.react-flow__resize-control.handle.right{left:100%;top:50%}.react-flow__resize-control.handle.top{left:50%;top:0}.react-flow__resize-control.handle.bottom{left:50%;top:100%}.react-flow__resize-control.handle.top.left,.react-flow__resize-control.handle.bottom.left{left:0}.react-flow__resize-control.handle.top.right,.react-flow__resize-control.handle.bottom.right{left:100%}.react-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.react-flow__resize-control.line.left,.react-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.react-flow__resize-control.line.left{left:0;border-left-width:1px}.react-flow__resize-control.line.right{left:100%;border-right-width:1px}.react-flow__resize-control.line.top,.react-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.react-flow__resize-control.line.top{top:0;border-top-width:1px}.react-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.react-flow__edge-textbg{fill:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.react-flow__edge-text{fill:var(--xy-edge-label-color, var(--xy-edge-label-color-default))}:root{--bg: #f3eee6;--paper: #fffaf2;--paper-strong: #fbf2e6;--ink: #312721;--muted: #80756c;--line: #ded2c3;--line-strong: #b7a696;--accent: #1f7a53;--accent-soft: rgba(31, 122, 83, .12);--sage: #7f8f73;--sage-dark: #596851;--ochre: #c99035;--clay: #bf6a54;--shadow: rgba(56, 43, 35, .12)}.model-editor-shell{height:100vh;min-height:560px;display:grid;grid-template-rows:auto auto auto minmax(0,1fr);color:#302923;background:#f3eee5}.frontend-error{min-height:100vh;display:grid;place-content:center;justify-items:center;gap:10px;padding:24px;color:#743128;background:#fff5ef;text-align:center}.frontend-error h1,.frontend-error p{margin:0}.frontend-error p{max-width:620px;color:#655850}.frontend-error button{display:inline-flex;align-items:center;gap:6px;margin-top:8px;padding:8px 11px;border:1px solid #b95040;border-radius:5px;color:#fff;background:#b94435}.model-toolbar{z-index:20;display:grid;grid-template-columns:auto minmax(260px,1fr) auto;gap:12px 18px;align-items:center;padding:14px 18px;background:#fffaf2;border-bottom:1px solid #d9cdbd;box-shadow:0 8px 24px #3c302514}.model-brand{display:flex;align-items:center;gap:11px}.model-brand .brand-mark{width:5px;height:42px;border-radius:3px;background:#1f7a58}.model-brand div{display:grid}.model-brand small{color:#81766c;font:10px/1.2 ui-monospace,monospace;letter-spacing:0}.model-brand strong{font-size:20px;letter-spacing:0}.model-search{display:flex;align-items:center;gap:8px;min-width:0;padding:8px 11px;border:1px solid #d9cdbd;border-radius:6px;background:#fffdf8}.model-search input{width:100%;min-width:0;border:0;outline:0;background:transparent;font:inherit}.model-search button,.model-toolbar button,.overlay-panel button,.candidate-popover button,.editor-feedback button{border:0;background:transparent;color:inherit;cursor:pointer}.model-counts{display:flex;align-items:center;justify-content:flex-end;gap:7px;flex-wrap:wrap}.model-counts>span,.model-counts>button{display:inline-flex;align-items:center;gap:5px;padding:5px 8px;border:1px solid #d9cdbd;border-radius:5px;background:#fffaf2;font:12px ui-monospace,monospace}.model-counts .count-warning{color:#a96a13;border-color:#dfbd83}.model-counts .count-error{color:#b44e3c;border-color:#dfa496}.view-tabs,.model-actions{display:flex;align-items:center;gap:7px;flex-wrap:wrap}.view-tabs{grid-column:1 / 3}.model-actions{justify-content:flex-end}.view-tabs button,.model-actions button,.cycle-callout button{display:inline-flex;align-items:center;gap:6px;min-height:32px;padding:6px 10px;border:1px solid #d4c7b6;border-radius:5px;background:#fffaf2;color:#4c433b}.view-tabs button.active{color:#176047;border-color:#83b59e;background:#e9f4ed}.model-actions button:disabled{opacity:.4;cursor:default}.model-actions .overview-cta{color:#176047;border-color:#86bba4;background:#e9f4ed;font-weight:700}.cycle-callout{display:flex;align-items:center;gap:12px;padding:10px 18px;color:#8f2f24;background:#fff0eb;border-bottom:1px solid #df9a8e}.cycle-callout div{display:grid;margin-right:auto}.cycle-callout span{font-size:12px}.cycle-callout button{border-color:#cf7668;color:#8f2f24}.cycle-callout button.active{color:#fff;border-color:#9d3428;background:#b94435}.editor-feedback{display:flex;align-items:center;justify-content:center;gap:12px;padding:7px 16px;background:#fff5d9;border-bottom:1px solid #dfc278;color:#6e5315;font-size:12px}.graph-scope-filter{align-items:center;background:#edf5ef;border-bottom:1px solid #b9d4c0;color:#365849;display:flex;font-size:12px;gap:10px;justify-content:center;min-height:38px;padding:6px 14px}.graph-scope-filter button{align-items:center;background:#fff;border:1px solid #b9d4c0;border-radius:5px;color:#365849;cursor:pointer;display:inline-flex;gap:5px;padding:5px 8px}.model-workspace{min-height:0;display:grid;grid-template-columns:minmax(0,1fr) 310px}.flow-wrap{min-width:0;min-height:0;position:relative}.model-inspector{overflow:auto;padding:16px;background:#fffaf2;border-left:1px solid #d9cdbd}.model-inspector>header{display:grid;gap:2px;margin-bottom:14px}.model-inspector>header span{color:#776c63;font:11px ui-monospace,monospace}.model-inspector pre{overflow-wrap:anywhere;white-space:pre-wrap;font-size:10px;line-height:1.45}.empty-inspector{display:grid;place-items:center;padding:48px 20px;color:#8a7e74;text-align:center}.inspector-initialization>div{display:flex;justify-content:space-between;gap:8px;padding:6px 0;border-bottom:1px solid #eee4d6;font-size:11px}.inspector-initialization .unresolved{color:#b74635;font-weight:700}.application-node .target-summary{margin:-2px 0 10px;color:#766c63;font-size:11px}.application-node .target-summary strong{color:#2d6652}.application-node .previous-label{margin-left:auto;color:#1f7a58;font:10px ui-monospace,monospace}.cycle-port-break{display:inline-grid;place-items:center;width:24px;height:24px;margin-left:auto;border:1px solid #c94e3e!important;border-radius:50%;color:#a63428!important;background:#fff0eb!important;box-shadow:0 3px 9px #8c2d232e}.cycle-port-break:hover{color:#fff!important;background:#bd4435!important}.entity-node{position:relative;width:240px;min-height:105px;padding:13px;border:1px solid #d8cbbb;border-radius:6px;background:#fffaf2;box-shadow:0 7px 18px #392e241f}.entity-node.selected{border-color:#1f7a58;box-shadow:0 0 0 2px #1f7a5829}.entity-node header{display:grid;gap:3px}.entity-node header span{color:#766c63;font-size:11px}.entity-node .badges{display:flex;gap:5px;flex-wrap:wrap;margin-top:12px}.candidate-popover{position:fixed;z-index:80;width:370px;max-height:460px;display:grid;grid-template-rows:auto minmax(0,1fr);border:1px solid #cfbfaa;border-radius:7px;background:#fffaf2;box-shadow:0 20px 54px #342a203d}.candidate-popover>header{display:flex;gap:10px;padding:12px;border-bottom:1px solid #e1d5c5}.candidate-popover>header div{display:grid;margin-right:auto}.candidate-popover>header span{color:#7a7067;font-size:10px}.candidate-list{overflow:auto;display:grid;gap:7px;padding:9px}.candidate-card{display:grid;grid-template-columns:1fr auto;gap:2px 8px;padding:10px;border:1px solid #ded2c2!important;border-radius:5px;background:#fffdf8!important;text-align:left}.candidate-card:hover{border-color:#76a990!important;background:#edf6f0!important}.candidate-card span{color:#1f7053;font:11px ui-monospace,monospace}.candidate-card small{color:#7a7067}.candidate-card div{grid-column:1 / -1;color:#7a7067;font-size:10px}.candidate-card.existing{border-left:3px solid #1f7a58!important}.candidate-section-label{padding:5px 3px 2px;color:#71675e;font:700 10px ui-monospace,monospace;text-transform:uppercase}.overlay-backdrop{position:fixed;z-index:100;inset:0;display:grid;place-items:center;padding:24px;background:#2d251e61}.overlay-panel{width:min(720px,100%);max-height:min(760px,92vh);display:grid;grid-template-rows:auto minmax(0,1fr);border:1px solid #cdbda8;border-radius:7px;background:#fffaf2;box-shadow:0 24px 70px #271f194d}.overlay-panel>header{display:flex;justify-content:space-between;align-items:center;padding:14px 16px;border-bottom:1px solid #ded2c2}.overlay-content{overflow:auto;padding:15px}.diagnostic-card,.cycle-card,.initialization-card{display:grid;gap:5px;margin-bottom:10px;padding:11px;border:1px solid #ded2c2;border-radius:5px}.diagnostic-card{border-left:3px solid #c85240}.diagnostic-card p,.cycle-card p{margin:0}.diagnostic-card small{color:#766c63}.cycle-card button{width:fit-content;padding:6px 8px;border:1px solid #ce7768;border-radius:4px;color:#963529}.model-code{min-height:320px;margin:0;padding:14px;overflow:auto;border-radius:5px;background:#292622;color:#f5eee5}.model-file-dialog{display:grid;gap:14px}.model-file-dialog>p{margin:0;line-height:1.5}.model-file-dialog>label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.model-path-input{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px}.model-path-input input{width:100%;min-width:0;padding:8px 9px;border:1px solid #d3c5b4;border-radius:4px;background:#fffdf8;font:12px ui-monospace,monospace}.model-path-input button{padding:8px 13px;border:1px solid #1d7052!important;border-radius:4px;color:#fff!important;background:#1f7a58!important}.model-path-input button:disabled{opacity:.4;cursor:default}.model-file-dialog section{display:grid;gap:7px}.recent-model-list{display:grid;gap:6px}.recent-model-list button{display:grid;gap:2px;padding:9px;border:1px solid #d8cbbb!important;border-radius:5px;background:#fffdf8!important;text-align:left}.recent-model-list button:hover{border-color:#73a88e!important;background:#edf6f0!important}.recent-model-list small,.model-file-dialog>small{color:#776d64;overflow-wrap:anywhere}.recovery-path{padding:9px;border:1px dashed #c99035!important;border-radius:5px;color:#6d511d!important;background:#fff6e6!important;text-align:left;overflow-wrap:anywhere}.initialization-group{display:grid;gap:10px;margin-bottom:12px;padding:12px;border:1px solid #d8cbbb;border-radius:6px;background:#fffdf8}.initialization-group>header{display:flex;align-items:end;justify-content:space-between;gap:12px}.initialization-group>header div{display:grid}.initialization-group>header span,.initialization-group>header small{color:#786d64;font:10px ui-monospace,monospace}.initialization-group>p{margin:0;color:#6c625a;font-size:11px;line-height:1.45}.initialization-value{display:grid;grid-template-columns:120px minmax(0,1fr) auto;gap:8px;align-items:end}.initialization-value label{display:grid;gap:4px;color:#5f554d;font-size:10px;font-weight:700}.initialization-value input,.initialization-value select{width:100%;min-width:0;padding:7px 8px;border:1px solid #d3c5b4;border-radius:4px;background:#fff}.initialization-value button,.initialization-object-list button{padding:7px 9px;border:1px solid #85ad99!important;border-radius:4px;color:#176047!important;background:#edf6f0!important}.initialization-value button:disabled,.initialization-object-list button:disabled{opacity:.4;cursor:default}.initialization-object-list{display:grid;gap:5px}.initialization-object-list>div{display:grid;grid-template-columns:minmax(0,1fr) auto auto;gap:8px;align-items:center;padding-top:5px;border-top:1px solid #ece1d2;font-size:11px}.initialization-object-list code{color:#a33d31}@media(max-width:980px){.model-toolbar{grid-template-columns:1fr}.view-tabs{grid-column:auto}.model-counts,.model-actions{justify-content:flex-start}.model-workspace{grid-template-columns:1fr}.model-inspector{display:none}}.application-form{width:min(780px,100%)}.object-form{width:min(640px,100%)}.override-form{width:min(760px,100%)}.application-form>header div{display:grid;gap:2px}.object-form>header div{display:grid;gap:2px}.override-form>header div{display:grid;gap:2px}.application-form>header span{color:#786d64;font-size:11px}.object-form>header span{color:#786d64;font-size:11px}.override-form>header span{color:#786d64;font-size:11px}.application-form-content,.object-form-content,.override-form-content{display:grid;gap:14px}.application-form-content>label,.application-form fieldset label,.object-form-content label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.override-form-content label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.application-form input,.application-form select,.object-form input,.object-form select,.override-form input,.override-form select{width:100%;min-width:0;padding:8px 9px;border:1px solid #d3c5b4;border-radius:4px;background:#fffdf8;color:#332c26;font:12px ui-monospace,monospace}.override-form fieldset{margin:0;padding:12px;border:1px solid #ded2c2;border-radius:5px}.override-form legend{padding:0 6px;color:#2d6652;font-weight:800}.override-form fieldset label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.override-scope-choice{display:grid;grid-template-columns:1fr 1fr;gap:9px}.override-scope-choice button{display:grid;gap:3px;padding:11px;border:1px solid #d4c7b6!important;border-radius:5px;background:#fffdf8!important;text-align:left}.override-scope-choice button.active{border-color:#72a88d!important;background:#edf6f0!important}.override-scope-choice span{color:#756a61;font-size:10px;line-height:1.4}.override-warning{display:grid;gap:3px;padding:10px;border-left:3px solid #c99035;background:#fff6e6}.override-warning span{color:#74685e;font-size:11px}.application-form fieldset{margin:0;padding:12px;border:1px solid #ded2c2;border-radius:5px}.application-form legend{padding:0 6px;color:#2d6652;font-weight:800}.form-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px}.parameter-list{display:grid;gap:9px}.parameter-row{display:grid;grid-template-columns:minmax(0,1fr) 150px;gap:9px;align-items:end}.parameter-row>label>span{color:#3c342e}.parameter-row small{color:#8a7e74;font-weight:400}.selector-summary{margin:10px 0 0;color:#796e64;font-size:11px}.application-form>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.object-form>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.override-form>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.application-form>footer button,.object-form>footer button,.override-form>footer button,.inspector-actions button{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border:1px solid #d2c4b3;border-radius:4px;background:#fffdf8}.application-form>footer .primary,.object-form>footer .primary{color:#fff;border-color:#1d7052;background:#1f7a58}.override-form>footer .primary{color:#fff;border-color:#1d7052;background:#1f7a58}.application-form>footer button:disabled{opacity:.45}.object-form>footer button:disabled{opacity:.45}.override-form>footer button:disabled{opacity:.45}.inspector-actions{display:flex;gap:7px;margin:10px 0 16px}.inspector-actions .danger{color:#a33d31;border-color:#d8a296}.binding-form{width:min(680px,100%)}.binding-form>header div{display:grid;gap:2px}.binding-form>header span{color:#786d64;font-size:11px}.binding-form .overlay-content{display:grid;gap:14px}.binding-route{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:center;gap:14px;padding:12px;border:1px solid #d8cbbb;border-radius:5px;background:#fffdf8}.binding-route>div{display:grid;gap:3px;min-width:0}.binding-route>div:last-child{text-align:right}.binding-route small{color:#7c7168;text-transform:uppercase;font-size:9px}.binding-route strong,.binding-route code{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.binding-route code{color:#1f7053}.binding-form fieldset{margin:0;padding:12px;border:1px solid #ded2c2;border-radius:5px}.binding-form legend{padding:0 6px;color:#2d6652;font-weight:800}.binding-form fieldset label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.binding-form select{width:100%;min-width:0;padding:8px 9px;border:1px solid #d3c5b4;border-radius:4px;background:#fffdf8;color:#332c26;font:12px ui-monospace,monospace}.binding-form>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.binding-form>footer button{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border:1px solid #d2c4b3;border-radius:4px;background:#fffdf8}.binding-form>footer .primary{color:#fff;border-color:#1d7052;background:#1f7a58}.cycle-break-dialog{width:min(620px,100%)}.cycle-break-dialog>header div{display:grid;gap:2px}.cycle-break-dialog>header span{color:#a33d31;font:11px ui-monospace,monospace}.cycle-break-dialog .overlay-content{display:grid;gap:13px}.cycle-break-dialog p{margin:0;line-height:1.5}.cycle-impact{display:grid;gap:3px;padding:10px;border-left:3px solid #c54c3c;background:#fff0eb}.cycle-impact span{color:#705f57;font-size:11px}.cycle-break-dialog fieldset{margin:0;padding:12px;border:1px solid #d8cbbb;border-radius:5px}.cycle-break-dialog legend{padding:0 6px;color:#9a392e;font-weight:800}.cycle-break-dialog label{display:grid;gap:5px;color:#5f554d;font-size:11px;font-weight:700}.cycle-break-dialog input,.cycle-break-dialog select{width:100%;min-width:0;padding:8px 9px;border:1px solid #d3c5b4;border-radius:4px;background:#fffdf8}.cycle-break-dialog>footer{display:flex;justify-content:flex-end;gap:8px;padding:12px 15px;border-top:1px solid #ded2c2}.cycle-break-dialog>footer button{display:inline-flex;align-items:center;gap:6px;padding:7px 10px;border:1px solid #d2c4b3;border-radius:4px;background:#fffdf8}.cycle-break-dialog>footer .primary{color:#fff;border-color:#a8392d;background:#b94435}.cycle-break-dialog>footer button:disabled{opacity:.45;cursor:default}@media(max-width:700px){.form-grid,.parameter-row,.override-scope-choice,.binding-route{grid-template-columns:1fr}.binding-route>div:last-child{text-align:left}.initialization-value{grid-template-columns:1fr}}*{box-sizing:border-box}body{margin:0;color:var(--ink);background:radial-gradient(circle at 20% 24%,rgba(31,122,83,.045),transparent 30%),radial-gradient(circle at 78% 68%,rgba(201,144,53,.055),transparent 34%),linear-gradient(180deg,rgba(255,250,242,.26),transparent 42%),var(--bg);font-family:Avenir Next,Trebuchet MS,Segoe UI,sans-serif}.app-shell{display:grid;grid-template-columns:minmax(0,1fr);height:100vh;position:relative}.app-shell.has-side-panel{grid-template-columns:minmax(0,1fr) 340px}.graph-panel{position:relative;min-width:0}.graph-panel:after{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;opacity:.2;background-image:radial-gradient(rgba(49,39,33,.11) .55px,transparent .55px);background-size:16px 16px;-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 22%,black 82%,transparent 100%);mask-image:linear-gradient(to bottom,transparent 0%,black 22%,black 82%,transparent 100%)}.topbar{position:absolute;z-index:10;top:18px;left:18px;right:18px;display:flex;align-items:center;gap:12px;padding:11px 14px;background:#fffaf2eb;border:1px solid var(--line);border-radius:14px;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.topbar:before{content:"";align-self:stretch;width:5px;border-radius:999px;background:var(--accent)}.editor-feedback{position:absolute;z-index:12;top:96px;left:18px;right:18px;padding:9px 12px;border:1px solid var(--line);border-radius:10px;background:#fffaf2f2;box-shadow:0 12px 30px var(--shadow);max-height:132px;overflow:auto;font-size:12px;line-height:1.45;white-space:normal;overflow-wrap:anywhere}.editor-feedback.error{color:var(--clay);border-color:#bf6a5466;background:#fff4eef7}.editor-feedback.info{color:var(--accent);border-color:#1f7a5359;background:#f5fcf8f7}.cycle-break-prompt{position:absolute;z-index:14;top:98px;left:18px;right:18px;display:flex;align-items:center;justify-content:space-between;gap:14px;padding:11px 12px;border:1px solid rgba(211,66,47,.32);border-radius:12px;background:linear-gradient(135deg,rgba(211,66,47,.12),transparent 62%),#fffaf2f5;box-shadow:0 16px 36px #382b2329}.cycle-break-prompt div{display:grid;gap:3px;min-width:0}.cycle-break-prompt strong{color:#7a2018;font-size:13px}.cycle-break-prompt span{color:var(--muted);font-size:12px;line-height:1.35}.cycle-break-prompt code{color:#7a2018;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.cycle-break-prompt.active{border-color:#d3422f85;box-shadow:0 16px 36px #382b2329,0 0 0 4px #d3422f14}.cycle-break-cta{flex:0 0 auto;justify-content:center;font-weight:780}.graph-workbench{flex-wrap:wrap;align-items:flex-start;max-height:min(36vh,250px);overflow:auto;scrollbar-width:thin}.brand-block{min-width:150px}.brand-block h1{font-size:clamp(16px,2.2vw,20px)}.search-box{position:relative;display:flex;align-items:center;gap:8px;flex:1 1 280px;max-width:460px;min-width:220px;padding:7px 9px;border:1px solid var(--line);border-radius:11px;background:#fffdf7db}.search-box input{width:100%;min-width:0;border:0;outline:0;color:var(--ink);background:transparent;font:inherit;font-size:13px}.clear-search{display:grid;place-items:center;width:20px;height:20px;border:0;border-radius:999px;background:#b7a6962e;color:var(--muted);cursor:pointer}.search-results{position:absolute;z-index:80;top:calc(100% + 8px);left:0;right:0;display:grid;gap:6px;max-height:360px;overflow:auto;padding:8px;border:1px solid var(--line);border-radius:12px;background:#fffaf2fa;box-shadow:0 18px 45px var(--shadow)}.search-result{display:grid;gap:2px;padding:8px 9px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--ink);text-align:left;cursor:pointer}.search-result:hover,.search-result:focus-visible{border-color:#1f7a533d;background:var(--accent-soft);outline:none}.search-result strong{overflow-wrap:anywhere;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.search-result span{color:var(--muted);font-size:11px}.eyebrow{color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.08em;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}h1,h2,h3{margin:0;letter-spacing:0}h1{font-size:20px;font-weight:800}.metrics{display:flex;flex-wrap:wrap;gap:8px;margin-left:auto}.metrics span,.metric-button,.node-meta span{display:inline-flex;align-items:center;gap:5px;padding:5px 8px;border:1px solid var(--line);border-radius:9px;background:#fffaf2e6;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.metric-button{color:var(--ink);cursor:pointer}.metric-button.active,.metric-button:hover,.metric-button:focus-visible{outline:none;background:#fff6f0}.view-mode-button.overview-cta{border-color:#1f7a5361;color:var(--accent);background:#1f7a531a;box-shadow:0 8px 18px #1f7a531a;font-weight:800}.meta-chip{position:relative}.meta-chip:after{content:attr(data-tooltip);position:absolute;z-index:20;left:0;bottom:calc(100% + 10px);width:max-content;max-width:280px;padding:8px 10px;color:var(--paper);background:var(--ink);border-radius:10px;box-shadow:0 12px 28px #382b232e;font-family:Avenir Next,Trebuchet MS,Segoe UI,sans-serif;font-size:12px;line-height:1.3;white-space:normal;opacity:0;pointer-events:none;transform:translateY(4px);transition:opacity .12s ease,transform .12s ease}.meta-chip:before{content:"";position:absolute;z-index:21;left:16px;bottom:calc(100% + 4px);width:10px;height:10px;background:var(--ink);opacity:0;pointer-events:none;transform:rotate(45deg) translateY(4px);transition:opacity .12s ease,transform .12s ease}.meta-chip:hover:after,.meta-chip:hover:before,.meta-chip:focus-visible:after,.meta-chip:focus-visible:before{opacity:1;transform:translateY(0)}.meta-chip:hover:before,.meta-chip:focus-visible:before{transform:rotate(45deg) translateY(0)}.metrics .warn{color:var(--clay);border-color:#c9614a73}.metrics .caution{color:var(--ochre);border-color:#c9903573}.metrics .warn svg{flex:0 0 auto}.icon-button{display:grid;place-items:center;width:34px;height:34px;border:1px solid var(--line);border-radius:10px;background:var(--paper);color:var(--ink);cursor:pointer;box-shadow:0 8px 18px #382b2314}.icon-button.compact{width:28px;height:28px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:14px}.toolbar-group{display:flex;align-items:center;gap:8px}.open-button{flex:0 0 auto}.panel-switch{flex-wrap:wrap}.select-control{display:inline-flex;align-items:center;gap:6px;padding:5px 8px;border:1px solid var(--line);border-radius:10px;background:#fffaf2e6;color:var(--muted)}.select-control select{max-width:132px;border:0;outline:0;background:transparent;color:var(--ink);font:inherit;font-size:12px}.relationship-legend{position:absolute;z-index:35;top:116px;left:18px;display:grid;gap:7px;width:190px;padding:10px;border:1px solid var(--line);border-radius:13px;background:#fffaf2eb;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);max-height:min(480px,calc(100vh - 142px));overflow:auto}.legend-title,.legend-note{display:flex;align-items:center;gap:6px;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.relationship-legend button{display:flex;align-items:center;gap:8px;padding:6px 7px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--muted);font:inherit;font-size:12px;cursor:pointer}.relationship-legend button.active{color:var(--ink);border-color:#1f7a5338;background:var(--accent-soft)}.legend-line{width:28px;height:0;border-top:2px solid var(--line-strong)}.legend-line.mapped{border-color:var(--accent);border-style:dashed}.legend-line.call{border-color:var(--clay);border-style:dashed}.scale-controls{position:absolute;z-index:35;top:116px;left:220px;display:grid;gap:8px;width:190px;padding:10px;border:1px solid var(--line);border-radius:13px;background:#fffaf2eb;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);max-height:min(560px,calc(100vh - 142px));overflow:auto}.scale-list{display:grid;gap:6px}.scale-list button,.scale-reset{display:grid;gap:2px;width:100%;padding:7px 8px;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--muted);font:inherit;text-align:left;cursor:pointer}.scale-list button.active{color:var(--ink);border-color:#1f7a5338;background:var(--accent-soft)}.scale-list button.collapsed{border-color:#b7a69657;border-style:dashed;background:#b7a69614}.scale-list span{overflow-wrap:anywhere;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;font-weight:700}.scale-list small{color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.06em}.scale-reset{color:var(--accent);border-color:#1f7a5333;background:#1f7a5314;text-align:center}.floating-panel{position:absolute;z-index:40;top:116px;right:18px;width:min(360px,calc(100vw - 36px));max-height:min(560px,calc(100vh - 142px));overflow:auto;padding:14px;background:#fffaf2f5;border:1px solid rgba(191,106,84,.32);border-radius:14px;box-shadow:0 18px 45px var(--shadow);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px)}.warnings-panel{border-color:#c9903559}.open-panel{left:18px;right:auto;border-color:#1f7a5352}.floating-panel-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:12px}.floating-panel h2{font-size:18px}.react-flow{background:transparent;z-index:1}.react-flow__background{display:none}.react-flow__edges{z-index:8}.react-flow__edges:has(.react-flow__edge.highlighted){z-index:120}.react-flow__edgelabel-renderer{z-index:28}.react-flow__edgelabel-renderer:has(.edge-chip.highlighted){z-index:121}.react-flow__nodes{z-index:20}.model-node{position:relative;overflow:visible;background:#fffaf2f5;border:1px solid var(--line);border-radius:16px;box-shadow:0 18px 42px var(--shadow)}.model-remove-button{position:absolute;z-index:45;top:-11px;right:-11px;display:grid;place-items:center;width:30px;height:30px;border:1px solid rgba(191,106,84,.4);border-radius:999px;color:#fff;background:var(--clay);box-shadow:0 10px 24px #bf6a543d;cursor:pointer}.model-remove-button:hover,.model-remove-button:focus-visible{outline:none;background:#9b3f2e;transform:translateY(-1px)}.node-header{border-radius:16px 16px 0 0}.model-node.selected{border-color:#1f7a53b8;box-shadow:0 20px 48px #1f7a5324}.model-node.focused{border-color:#1f7a539e;box-shadow:0 20px 48px #1f7a5324,0 0 0 4px #1f7a5314}.model-node.dimmed{opacity:.2;filter:grayscale(.2)}.model-node.cyclic{border-color:#bf6a549e;box-shadow:0 18px 42px var(--shadow),0 0 0 3px #bf6a5414}.model-node.cyclic .node-header>svg{color:var(--clay)}.model-node.hard_dependency{border-color:#bf6a548c;border-style:dashed;background:#fff6f0f0;box-shadow:0 14px 30px #bf6a541f}.model-node.hard_dependency .node-header{background:linear-gradient(90deg,rgba(191,106,84,.08),transparent 60%),var(--paper-strong)}.model-node.hard_dependency .node-header>svg{color:var(--clay)}.model-node.hard_dependency .process:before{content:"↳ ";color:var(--clay)}.model-node.overview-node{border-radius:12px;box-shadow:0 10px 24px #382b231a}.model-node.overview-node .node-header{min-height:64px;padding:9px 10px;border-radius:12px 12px 0 0}.model-node.overview-node .process{font-size:12px;line-height:1.18;overflow-wrap:anywhere}.model-node.overview-node .model-type{font-size:10px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.overview-node-summary{display:flex;flex-wrap:wrap;gap:5px;padding:8px 10px 10px}.overview-node-summary span{min-width:0;max-width:100%;padding:3px 6px;border:1px solid rgba(183,166,150,.35);border-radius:999px;color:var(--muted);background:#fffdf7c2;font-size:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.overview-port-handles .react-flow__handle{width:7px;height:7px;border-color:#fffaf2e6;background:var(--line-strong)}.overview-port-handles .react-flow__handle-right{background:var(--accent)}.hard-chip{color:var(--clay);border-color:#bf6a5447!important;background:#fff6f0e6!important}.node-header{display:grid;grid-template-columns:1fr auto;align-items:flex-start;gap:11px;padding:11px 12px 12px;color:var(--ink);background:var(--paper-strong);border-bottom:1px solid var(--line)}.node-header>svg{color:var(--accent)}.process{font-weight:820;font-size:15px;letter-spacing:0}.model-type{margin-top:2px;font-size:12px;color:var(--muted);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.node-meta{display:flex;flex-wrap:wrap;gap:6px;padding:10px 12px 0}.ports-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;padding:12px}.port-title{margin-bottom:6px;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.08em;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.port{position:relative;display:flex;align-items:center;gap:5px;min-height:24px;margin:4px 0;padding:4px 7px;border:1px solid var(--line);border-radius:8px;background:#fffdf7e6;font-size:12px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.port:after{content:attr(data-default);position:absolute;z-index:60;left:50%;bottom:calc(100% + 8px);width:max-content;max-width:240px;padding:7px 9px;color:var(--paper);background:var(--ink);border-radius:9px;box-shadow:0 12px 28px #382b232e;font-family:Avenir Next,Trebuchet MS,Segoe UI,sans-serif;font-size:11px;line-height:1.3;white-space:normal;opacity:0;pointer-events:none;transform:translate(-50%,4px);transition:opacity .12s ease,transform .12s ease}.port:hover:after,.port.active:after{opacity:1;transform:translate(-50%)}.app-shell.has-candidate-popover .port.active:after{opacity:0}.port.output{justify-content:flex-end}.port.previous{color:var(--clay)}.port.mapped{border-color:#1f7a5361}.port-candidate-button{position:relative;z-index:8;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:16px;height:16px;padding:0;color:var(--accent);background:#fffdf7f0;border:1px solid rgba(31,122,83,.34);border-radius:999px;cursor:pointer}.port-candidate-button:hover,.port-candidate-button:focus-visible{color:#fffdfa;background:var(--accent);outline:none}.port-cycle-break-button{position:relative;z-index:9;display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:18px;height:18px;padding:0;color:#9b2d22;background:#fff1ecf5;border:1px solid rgba(211,66,47,.45);border-radius:999px;cursor:pointer;box-shadow:0 5px 12px #d3422f29}.port-cycle-break-button:hover,.port-cycle-break-button:focus-visible{color:#fffdfa;background:#d3422f;outline:none}.port.active .port-candidate-button{color:var(--accent);background:#fffdfa}@keyframes cycleTargetPulse{0%,to{box-shadow:inset 3px 0 #d3422fc7,0 0 0 3px #d3422f14}50%{box-shadow:inset 3px 0 #d3422feb,0 0 0 6px #d3422f26}}.candidate-popover{position:fixed;z-index:1200;display:grid;grid-template-rows:auto minmax(0,1fr);overflow:hidden;color:var(--ink);border:1px solid rgba(31,122,83,.28);border-radius:8px;background:linear-gradient(135deg,rgba(31,122,83,.08),transparent 42%),#fffdf7fa;box-shadow:0 18px 42px #382b232e}.candidate-popover-header{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;padding:12px 12px 9px;border-bottom:1px solid rgba(183,166,150,.32)}.candidate-popover-header h3{margin:3px 0 0;overflow-wrap:anywhere;font-size:15px}.candidate-popover-list{display:grid;gap:8px;overflow:auto;padding:10px}.candidate-model-card{display:grid;gap:5px;width:100%;padding:9px 10px;color:var(--ink);border:1px solid rgba(183,166,150,.38);border-left:4px solid var(--accent);border-radius:8px;background:#ffffffb3;font:inherit;text-align:left;cursor:pointer}.candidate-model-card:hover,.candidate-model-card:focus-visible{background:#fffdf7f5;border-color:#1f7a536b;outline:none}.candidate-model-card strong{overflow-wrap:anywhere;font-size:12px}.candidate-model-card span{color:var(--muted);font-size:11px}.candidate-model-card small{overflow-wrap:anywhere;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.04em}.port.required-input{border-color:#bf6a54b8;background:linear-gradient(90deg,#bf6a5429,#fffdf7eb 62%),#fffdf7e6;box-shadow:inset 3px 0 #bf6a54bd}.port.required-input .react-flow__handle{border-color:var(--clay);background:#fff6f0;box-shadow:0 0 0 3px #fffaf2eb,0 0 0 6px #bf6a5421}.port.cycle-break-target{border-color:#d3422fb8;background:linear-gradient(90deg,#d3422f2e,#fffdf7eb 62%),#fffdf7e6;box-shadow:inset 3px 0 #d3422fc7,0 0 0 3px #d3422f14}.cycle-break-mode .port.cycle-break-target{animation:cycleTargetPulse 1.35s ease-in-out infinite}.port.cycle-break-target .react-flow__handle{border-color:#d3422f;background:#fff1ec;box-shadow:0 0 0 3px #fffaf2eb,0 0 0 7px #d3422f26}.port.highlighted{border-color:var(--line);background:#fffdfa}.port.focused{border-color:#1f7a5394;background:var(--accent-soft)}.port.active{color:#fffdfa;border-color:var(--accent);background:var(--accent)}.port.cycle-break-target.active,.port.cycle-break-target.focused,.port.cycle-break-target.highlighted{color:#7a2018;border-color:#d3422fc7;background:linear-gradient(90deg,#d3422f33,#fffdf7f0 64%),#fffdf7f0;box-shadow:inset 3px 0 #d3422fdb,0 0 0 4px #d3422f1f}.port.cycle-break-target.active:after{background:#7a2018}.port.cycle-break-target.active .port-cycle-break-button,.port.cycle-break-target.focused .port-cycle-break-button,.port.cycle-break-target.highlighted .port-cycle-break-button{color:#9b2d22;background:#fff1ecfa}.react-flow__handle{width:9px;height:9px;border:1px solid var(--line-strong);background:var(--paper);z-index:25;box-shadow:0 0 0 3px #fffaf2eb}.react-flow__handle.call-handle{width:12px;height:36px;opacity:0;pointer-events:none}.react-flow__edge.multiscale path{stroke-dasharray:7 5}.react-flow__edge.mapped_variable path{stroke:var(--accent)}.react-flow__edge.hard_dependency path{stroke:var(--clay)}.react-flow__edge.cycle_dependency path,.react-flow__edge.cycle_edge path{stroke:#d3422f;filter:drop-shadow(0 0 5px rgba(211,66,47,.26))}.react-flow__edge.call_edge path{stroke-width:1.7;stroke-dasharray:3 6;opacity:.7}.react-flow__edge.highlighted path{stroke-width:3;stroke:var(--accent)}.react-flow__edge.focused path{stroke-width:2.8}.react-flow__edge.highlighted{z-index:80!important}.react-flow__edge.focused{z-index:70!important}.react-flow__edge.dimmed{opacity:.04}.overview-mode .react-flow__edge.variable_edge path{stroke-width:1.45!important;opacity:.74}.overview-mode .react-flow__edge.call_edge path{stroke-width:1.15!important;opacity:.4}.overview-mode .react-flow__edge.highlighted path,.overview-mode .react-flow__edge.focused path{stroke-width:2.8!important;opacity:1}.edge-chip{position:absolute;z-index:-1;display:inline-flex;align-items:center;gap:6px;padding:3px 7px;color:var(--ink);background:#fffaf2f0;border:1px solid var(--line);border-radius:999px;box-shadow:0 8px 20px #382b231a;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:1;pointer-events:none;white-space:nowrap}.edge-chip.mapped_variable,.edge-chip.multiscale{border-color:#1f7a534d;background:#f8faf1f5}.edge-chip.hard_dependency{border-color:#bf6a5452;background:#fff6f0f5}.edge-chip.cycle_dependency,.edge-chip.cycle_edge{color:#7a2018;border-color:#d3422f70;background:#ffeee9fa}.edge-chip small{color:var(--muted);font-size:9px;letter-spacing:.02em;text-transform:uppercase}.edge-chip.highlighted{z-index:80;color:#fffdfa;border-color:var(--accent);background:var(--accent);box-shadow:0 12px 24px #1f7a5338}.edge-chip.highlighted small{color:#fffdf7b8}.edge-chip.dimmed{opacity:.04}.overview-mode .edge-chip:not(.highlighted):not(.focused),.overview-mode .edge-terminal:not(.highlighted):not(.focused){display:none}.edge-chip.focused{z-index:70;border-color:#1f7a535c;box-shadow:0 10px 22px #1f7a5324}.edge-terminal{position:absolute;z-index:32;--terminal-color: var(--line-strong);width:18px;height:10px;pointer-events:none;opacity:.95}.edge-terminal:before{content:"";position:absolute;top:4px;left:2px;right:2px;height:2px;border-radius:999px;background:var(--terminal-color)}.edge-terminal.target:after{content:"";position:absolute;top:1px;width:0;height:0;border-top:4px solid transparent;border-bottom:4px solid transparent}.edge-terminal[data-side=left]:before{left:7px}.edge-terminal[data-side=right]:before{right:7px}.edge-terminal.target[data-side=left]:after{right:0;border-left:7px solid var(--terminal-color)}.edge-terminal.target[data-side=right]:after{left:0;border-right:7px solid var(--terminal-color)}.edge-terminal.highlighted:before{height:3px}.edge-terminal.dimmed{opacity:.12}.cycle-edge-card{border-color:#d3422f57;background:linear-gradient(135deg,rgba(211,66,47,.1),transparent 58%),#fffaf2e0}.cycle-break-button{justify-content:center;margin:8px 0 4px}.inspector{border-left:1px solid var(--line);background:#fffaf2d1;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);padding:18px;overflow:auto}.inspector:focus{outline:none}.inspector.guided-focus{animation:guided-panel-focus 1.8s ease-out}@keyframes guided-panel-focus{0%{box-shadow:inset 0 0 0 3px #1f7a537a,-18px 0 44px #1f7a5329;background:#fafff7f0}60%{box-shadow:inset 0 0 0 3px #1f7a535c,-12px 0 34px #1f7a531f}to{box-shadow:none;background:#fffaf2d1}}.inspector header{display:flex;align-items:center;gap:8px;margin-bottom:14px}.mapping-code-panel{display:grid;gap:10px}.row-with-actions{display:flex;align-items:center;justify-content:space-between;gap:8px}.mapping-code{width:100%;min-height:260px;resize:vertical;border:1px solid var(--line);border-radius:10px;background:#fffdf7e6;color:var(--ink);padding:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.45}.storage-grid{display:grid;gap:8px}.path-status{display:grid;gap:3px;border:1px solid var(--line);border-radius:10px;background:#fffaf2b8;padding:9px}.path-status span{color:var(--muted);font-size:11px;font-weight:720}.path-status strong{color:var(--ink);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;overflow-wrap:anywhere}.recent-mappings{display:grid;gap:8px;margin-top:4px}.open-mapping-panel{display:grid;gap:12px}.recent-mapping-list{display:grid;gap:7px}.recent-mapping-item{display:grid;gap:3px;text-align:left;border:1px solid var(--line);border-radius:10px;background:#fffdf7d1;color:var(--ink);padding:9px;cursor:pointer}.recent-mapping-item:hover,.recent-mapping-item:focus-visible{border-color:#1f7a5352;background:#1f7a5314}.recent-mapping-item span{font-weight:720}.recent-mapping-item small{color:var(--muted);overflow-wrap:anywhere}.inline-field{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px}.inspector h2{font-size:17px}.inspector h3{margin-top:22px;margin-bottom:8px;color:var(--muted);font-size:11px;text-transform:uppercase;letter-spacing:.08em}.row{display:grid;grid-template-columns:84px minmax(0,1fr);gap:8px;padding:8px 0;border-top:1px solid rgba(183,166,150,.35)}.row span{color:var(--muted)}.row strong{overflow-wrap:anywhere;font-weight:620}.variable-card{border:1px solid var(--line);border-radius:12px;background:#fffaf2bf;padding:10px}.edge-detail-card{display:grid;gap:4px;margin-bottom:14px;padding:10px;border:1px solid rgba(31,122,83,.24);border-radius:12px;background:linear-gradient(135deg,rgba(31,122,83,.08),transparent 58%),#fffaf2d1}.edge-detail-card .diagnostic,.edge-detail-card .empty-state{margin-top:6px}.variable-card .row:first-of-type{margin-top:8px}.variable-card-title{display:flex;align-items:center;justify-content:space-between;gap:10px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.variable-card-title span{overflow-wrap:anywhere;font-weight:720}.variable-card-title small{color:var(--muted);text-transform:uppercase;letter-spacing:.08em}.diagnostic,.edit-suggestion,.initialization-note,.empty-state{border:1px solid var(--line);border-radius:12px;background:#fffaf2bf;padding:10px;color:var(--muted)}.diagnostic,.edit-suggestion,.initialization-note{display:flex;align-items:center;gap:7px}.diagnostic,.edit-suggestion{color:var(--clay);border-color:#c9614a47;background:#c9614a17}.initialization-note{color:#87533b;border-color:#bf6a5452;background:#bf6a541a}.initialization-list{display:grid;gap:8px}.initialization-list.compact{gap:6px}.initialization-group{display:grid;gap:7px}.initialization-group h4,.provenance-block h4{margin:6px 0 0;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700}.initialization-item{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:10px;width:100%;padding:9px 10px;color:var(--ink);background:#fffaf2c7;border:1px solid rgba(191,106,84,.28);border-left:4px solid var(--clay);border-radius:10px;font:inherit;text-align:left;cursor:pointer}.initialization-item:hover,.initialization-item:focus-visible{background:#fff6f0f5;outline:none}.initialization-item span{overflow:hidden;color:var(--muted);font-size:12px;text-overflow:ellipsis;white-space:nowrap}.initialization-item strong{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px}.initialization-item small{grid-column:1 / -1;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.06em}.initialization-item.previous_time_step{border-left-color:var(--ochre)}.initialization-item.mapped_unresolved{border-left-color:var(--accent)}.warning-list,.provenance-block{display:grid;gap:8px}.warning-group{display:grid;gap:7px}.warning-group h4{margin:6px 0 0;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700}.warning-item,.provenance-edge{display:grid;gap:3px;width:100%;padding:9px 10px;border:1px solid rgba(201,144,53,.28);border-left:4px solid var(--ochre);border-radius:10px;background:#fffaf2c7;color:var(--ink);font:inherit;text-align:left;cursor:pointer}.warning-item.error{border-color:#bf6a5457;border-left-color:var(--clay);background:#bf6a5414}.warning-item.info{border-color:#7f8f7342;border-left-color:var(--sage);background:#7f8f7314}.provenance-edge{border-color:#b7a6966b;border-left-color:var(--line-strong)}.provenance-edge.mapped_variable{border-left-color:var(--accent)}.provenance-edge.hard_dependency{border-left-color:var(--clay)}.warning-item:hover,.warning-item:focus-visible,.provenance-edge:hover,.provenance-edge:focus-visible{background:#fff6f0f5;outline:none}.warning-item strong,.provenance-edge strong{overflow-wrap:anywhere;font-size:12px}.warning-item span,.provenance-edge span,.provenance-edge small{color:var(--muted);font-size:11px;line-height:1.25}.provenance-block{margin-top:10px}.empty-state.compact{padding:7px 8px;font-size:11px}.live-session{border-left:1px solid rgba(183,166,150,.36);padding-left:10px}.live-pill{display:inline-flex;align-items:center;height:28px;padding:0 9px;border:1px solid rgba(191,106,84,.38);border-radius:999px;color:var(--clay);background:#bf6a5414;font-size:11px;font-weight:800;text-transform:uppercase}.live-pill.connected{border-color:#1f7a5357;color:var(--accent);background:#1f7a5317}.metric-button:disabled{cursor:not-allowed;opacity:.42}.model-browser{display:grid;gap:7px}.model-browser-control{display:grid;gap:4px}.model-browser-control span,.parameter-row label{color:var(--muted);font-size:11px;font-weight:700}.model-browser-control select,.model-browser-control input,.parameter-row input,.parameter-row select{min-width:0;border:1px solid rgba(183,166,150,.44);border-radius:8px;background:#ffffffc7;color:var(--ink);font:inherit}.model-browser-control select,.model-browser-control input{height:32px;padding:0 8px}.model-browser-item{display:grid;gap:7px;padding:8px 9px;border:1px solid rgba(183,166,150,.34);border-radius:10px;background:#ffffff9e}.model-browser-item.add-model-config{gap:10px;padding-bottom:0}.model-browser-title{display:grid;gap:2px}.model-browser-item strong{overflow-wrap:anywhere;font-size:12px}.model-browser-item span{color:var(--muted);font-size:11px}.existing-model-editor{display:grid;gap:8px;margin-top:12px;padding-top:10px;border-top:1px solid rgba(183,166,150,.32)}.variable-mapping-editor{display:grid;gap:8px;margin-top:10px;padding-top:10px;border-top:1px solid rgba(183,166,150,.32)}.variable-mapping-editor h4{margin:0;color:var(--muted);font-size:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700}.initialization-editor{display:grid;gap:12px}.initialization-editor-group{display:grid;gap:8px}.initialization-editor-group h3{margin:0}.initialization-editor-row{display:grid;grid-template-columns:minmax(0,.85fr) minmax(0,1fr) minmax(82px,.72fr) auto;gap:7px;align-items:center;padding:9px;border:1px solid rgba(191,106,84,.26);border-left:4px solid var(--clay);border-radius:8px;background:#fffaf2c7}.initialization-editor-row.provided{border-color:#1f7a533d;border-left-color:var(--accent)}.initialization-editor-row label{overflow-wrap:anywhere;font-weight:700}.initialization-editor-row input,.initialization-editor-row select{min-width:0;height:30px;border:1px solid rgba(183,166,150,.44);border-radius:8px;background:#ffffffc7;color:var(--ink);font:inherit}.initialization-editor-row input{padding:0 8px}.initialization-editor-row small{grid-column:1 / -1;color:var(--muted);font-size:10px;text-transform:uppercase;letter-spacing:.05em}.parameter-row{display:grid;grid-template-columns:minmax(0,.75fr) minmax(0,1fr) minmax(78px,.8fr);gap:6px;align-items:center}.parameter-row input,.parameter-row select{height:28px;padding:0 7px;font-size:12px}.rate-editor{display:grid;gap:6px;padding:7px;border:1px solid rgba(183,166,150,.28);border-radius:8px;background:#fcf9f4b8}.add-model-footer{position:sticky;bottom:-18px;display:flex;justify-content:flex-end;margin:2px -9px 0;padding:10px 9px;border-top:1px solid rgba(183,166,150,.34);border-radius:0 0 10px 10px;background:#fffaf2f0;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.rate-summary{color:var(--muted);font-size:11px;line-height:1.35}.rate-clock-row{display:grid;grid-template-columns:1fr 1fr;gap:6px}.rate-clock-row label{display:grid;gap:4px;color:var(--muted);font-size:11px;font-weight:700}.rate-clock-row input{min-width:0;height:28px;border:1px solid rgba(183,166,150,.44);border-radius:8px;background:#ffffffc7;color:var(--ink);font:inherit;padding:0 7px}.metric-button.danger{border-color:#bf6a546b;color:#9b3f2e;background:#fff2ecdb}@media(max-width:900px){.app-shell{grid-template-columns:1fr}.inspector{position:absolute;z-index:28;top:150px;right:16px;bottom:16px;width:min(360px,calc(100% - 32px));border:1px solid var(--line);border-radius:14px;box-shadow:0 18px 45px var(--shadow)}.relationship-legend,.scale-controls,.floating-panel{top:150px}.scale-controls{left:220px}}@media(max-width:720px){.topbar{top:10px;left:10px;right:10px;gap:8px;padding:9px 10px}.topbar:before{display:none}.brand-block{min-width:128px}.search-box{flex-basis:100%;max-width:none;min-width:0}.metrics{margin-left:0}.relationship-legend,.scale-controls,.floating-panel{top:132px;left:10px;right:10px;width:auto}.scale-controls{top:326px}.react-flow__minimap{display:none}}.mapping-dialog-overlay{position:fixed;inset:0;z-index:1000;background:#31272161;display:flex;align-items:center;justify-content:center}.mapping-dialog{background:var(--paper);border:1px solid var(--line-strong);border-radius:10px;box-shadow:0 8px 32px var(--shadow);width:360px;max-width:calc(100vw - 32px);display:flex;flex-direction:column;gap:0;overflow:hidden}.mapping-dialog-header{display:flex;align-items:center;justify-content:space-between;padding:14px 16px 10px;border-bottom:1px solid var(--line)}.mapping-dialog-header .eyebrow{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.07em;color:var(--muted)}.mapping-dialog-body{padding:14px 16px;display:flex;flex-direction:column;gap:14px}.mapping-port-summary{display:flex;align-items:center;gap:8px;background:var(--paper-strong);border:1px solid var(--line);border-radius:6px;padding:10px 12px}.mapping-port{display:flex;flex-direction:column;gap:1px;flex:1;min-width:0}.mapping-port small{font-size:10px;text-transform:uppercase;letter-spacing:.06em;color:var(--muted)}.mapping-port strong{font-size:12px;font-weight:700;color:var(--accent);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mapping-port span{font-size:11px;color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.mapping-port.target strong{color:var(--clay)}.mapping-arrow{font-size:18px;color:var(--muted);flex-shrink:0}.mapping-mode-section{display:flex;flex-direction:column;gap:6px}.mapping-mode-label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);margin-bottom:2px}.mapping-radio{display:flex;align-items:center;gap:8px;font-size:13px;cursor:pointer;padding:4px 0}.mapping-radio input[type=radio]{accent-color:var(--accent);width:14px;height:14px;flex-shrink:0}.mapping-scale-picker{display:flex;flex-direction:column;gap:4px}.mapping-checkbox{display:flex;align-items:center;gap:8px;font-size:13px;cursor:pointer;padding:2px 0}.mapping-checkbox input[type=checkbox]{accent-color:var(--accent);width:14px;height:14px;flex-shrink:0}.mapping-dialog-footer{display:flex;justify-content:flex-end;gap:8px;padding:10px 16px 14px;border-top:1px solid var(--line)}.accent-button{background:var(--accent);color:#fff;border-color:transparent}.accent-button:hover:not(:disabled){background:var(--sage-dark);border-color:transparent}.selector-preview{display:grid;gap:6px;padding:12px;border:1px solid #b9d3c4;background:#f2f8f4;border-radius:6px}.selector-preview span,.selector-preview p{margin:0;color:#615c55;font-size:.86rem}.selector-preview p{color:#a44b39}.selector-preview-button{display:inline-flex;align-items:center;gap:6px;width:max-content}.environment-ports{border-top:1px solid #ded5c8;margin-top:8px;padding-top:8px}.entity-node.environment{border-color:#8fb9c2;background:#f3fafb}.application-configuration-form{width:min(820px,100%)}.application-configuration-content{display:grid;gap:14px}.application-configuration-content fieldset{margin:0;padding:12px;border:1px solid #ded2c2;border-radius:5px}.application-configuration-content legend{padding:0 6px;color:#2d6652;font-weight:800}.application-configuration-content p{margin:0;color:#786d64}.configuration-list{display:grid;gap:7px;margin-bottom:10px}.configuration-list>div,.configuration-list>label{min-height:36px;display:grid;grid-template-columns:minmax(100px,.6fr) minmax(0,1fr) auto;align-items:center;gap:10px;padding:7px 9px;border:1px solid #e4d9ca;border-radius:5px;background:#fffdf8}.configuration-list span{overflow:hidden;color:#786d64;text-overflow:ellipsis;white-space:nowrap}.configuration-list select,.compact-configuration-row input,.compact-configuration-row select{width:100%;min-height:34px;border:1px solid #cdbda8;border-radius:5px;background:#fffdf8;color:#302923}.compact-configuration-row{align-items:end}.compact-configuration-row label{display:grid;gap:5px;color:#655b52;font-size:12px;font-weight:700}.compact-configuration-row button{min-height:34px;display:inline-flex;align-items:center;justify-content:center;gap:5px}.icon-button{width:32px;height:32px;display:inline-grid;place-items:center;padding:0}.application-configuration-form>footer{display:flex;justify-content:flex-end;padding:12px 15px;border-top:1px solid #ded2c2} diff --git a/frontend/dist/assets/index-D625qdlf.js b/frontend/dist/assets/index-PMl5JtnR.js similarity index 82% rename from frontend/dist/assets/index-D625qdlf.js rename to frontend/dist/assets/index-PMl5JtnR.js index 7485bab30..09c02888a 100644 --- a/frontend/dist/assets/index-D625qdlf.js +++ b/frontend/dist/assets/index-PMl5JtnR.js @@ -1,26 +1,26 @@ -(function(){const E=document.createElement("link").relList;if(E&&E.supports&&E.supports("modulepreload"))return;for(const N of document.querySelectorAll('link[rel="modulepreload"]'))M(N);new MutationObserver(N=>{for(const P of N)if(P.type==="childList")for(const k of P.addedNodes)k.tagName==="LINK"&&k.rel==="modulepreload"&&M(k)}).observe(document,{childList:!0,subtree:!0});function x(N){const P={};return N.integrity&&(P.integrity=N.integrity),N.referrerPolicy&&(P.referrerPolicy=N.referrerPolicy),N.crossOrigin==="use-credentials"?P.credentials="include":N.crossOrigin==="anonymous"?P.credentials="omit":P.credentials="same-origin",P}function M(N){if(N.ep)return;N.ep=!0;const P=x(N);fetch(N.href,P)}})();var _hn=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function bke(g){return g&&g.__esModule&&Object.prototype.hasOwnProperty.call(g,"default")?g.default:g}var T7e={exports:{}},OG={};var Lhn;function ezn(){if(Lhn)return OG;Lhn=1;var g=Symbol.for("react.transitional.element"),E=Symbol.for("react.fragment");function x(M,N,P){var k=null;if(P!==void 0&&(k=""+P),N.key!==void 0&&(k=""+N.key),"key"in N){P={};for(var H in N)H!=="key"&&(P[H]=N[H])}else P=N;return N=P.ref,{$$typeof:g,type:M,key:k,ref:N!==void 0?N:null,props:P}}return OG.Fragment=E,OG.jsx=x,OG.jsxs=x,OG}var Phn;function nzn(){return Phn||(Phn=1,T7e.exports=ezn()),T7e.exports}var F=nzn(),C7e={exports:{}},Mc={};var $hn;function tzn(){if($hn)return Mc;$hn=1;var g=Symbol.for("react.transitional.element"),E=Symbol.for("react.portal"),x=Symbol.for("react.fragment"),M=Symbol.for("react.strict_mode"),N=Symbol.for("react.profiler"),P=Symbol.for("react.consumer"),k=Symbol.for("react.context"),H=Symbol.for("react.forward_ref"),U=Symbol.for("react.suspense"),G=Symbol.for("react.memo"),ie=Symbol.for("react.lazy"),Z=Symbol.for("react.activity"),W=Symbol.iterator;function se(xe){return xe===null||typeof xe!="object"?null:(xe=W&&xe[W]||xe["@@iterator"],typeof xe=="function"?xe:null)}var le={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},ee=Object.assign,Oe={};function ge(xe,un,rt){this.props=xe,this.context=un,this.refs=Oe,this.updater=rt||le}ge.prototype.isReactComponent={},ge.prototype.setState=function(xe,un){if(typeof xe!="object"&&typeof xe!="function"&&xe!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,xe,un,"setState")},ge.prototype.forceUpdate=function(xe){this.updater.enqueueForceUpdate(this,xe,"forceUpdate")};function Pe(){}Pe.prototype=ge.prototype;function ae(xe,un,rt){this.props=xe,this.context=un,this.refs=Oe,this.updater=rt||le}var Me=ae.prototype=new Pe;Me.constructor=ae,ee(Me,ge.prototype),Me.isPureReactComponent=!0;var Be=Array.isArray;function rn(){}var ln={H:null,A:null,T:null,S:null},xn=Object.prototype.hasOwnProperty;function hn(xe,un,rt){var lt=rt.ref;return{$$typeof:g,type:xe,key:un,ref:lt!==void 0?lt:null,props:rt}}function wt(xe,un){return hn(xe.type,un,xe.props)}function Cn(xe){return typeof xe=="object"&&xe!==null&&xe.$$typeof===g}function Qn(xe){var un={"=":"=0",":":"=2"};return"$"+xe.replace(/[=:]/g,function(rt){return un[rt]})}var Y=/\/+/g;function Fe(xe,un){return typeof xe=="object"&&xe!==null&&xe.key!=null?Qn(""+xe.key):un.toString(36)}function mn(xe){switch(xe.status){case"fulfilled":return xe.value;case"rejected":throw xe.reason;default:switch(typeof xe.status=="string"?xe.then(rn,rn):(xe.status="pending",xe.then(function(un){xe.status==="pending"&&(xe.status="fulfilled",xe.value=un)},function(un){xe.status==="pending"&&(xe.status="rejected",xe.reason=un)})),xe.status){case"fulfilled":return xe.value;case"rejected":throw xe.reason}}throw xe}function Ae(xe,un,rt,lt,Bt){var hi=typeof xe;(hi==="undefined"||hi==="boolean")&&(xe=null);var Gt=!1;if(xe===null)Gt=!0;else switch(hi){case"bigint":case"string":case"number":Gt=!0;break;case"object":switch(xe.$$typeof){case g:case E:Gt=!0;break;case ie:return Gt=xe._init,Ae(Gt(xe._payload),un,rt,lt,Bt)}}if(Gt)return Bt=Bt(xe),Gt=lt===""?"."+Fe(xe,0):lt,Be(Bt)?(rt="",Gt!=null&&(rt=Gt.replace(Y,"$&/")+"/"),Ae(Bt,un,rt,"",function(Wr){return Wr})):Bt!=null&&(Cn(Bt)&&(Bt=wt(Bt,rt+(Bt.key==null||xe&&xe.key===Bt.key?"":(""+Bt.key).replace(Y,"$&/")+"/")+Gt)),un.push(Bt)),1;Gt=0;var At=lt===""?".":lt+":";if(Be(xe))for(var st=0;st>>1,kn=Ae[Sn];if(0>>1;SnN(rt,sn))ltN(Bt,rt)?(Ae[Sn]=Bt,Ae[lt]=sn,Sn=lt):(Ae[Sn]=rt,Ae[un]=sn,Sn=un);else if(ltN(Bt,sn))Ae[Sn]=Bt,Ae[lt]=sn,Sn=lt;else break e}}return Ze}function N(Ae,Ze){var sn=Ae.sortIndex-Ze.sortIndex;return sn!==0?sn:Ae.id-Ze.id}if(g.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var P=performance;g.unstable_now=function(){return P.now()}}else{var k=Date,H=k.now();g.unstable_now=function(){return k.now()-H}}var U=[],G=[],ie=1,Z=null,W=3,se=!1,le=!1,ee=!1,Oe=!1,ge=typeof setTimeout=="function"?setTimeout:null,Pe=typeof clearTimeout=="function"?clearTimeout:null,ae=typeof setImmediate<"u"?setImmediate:null;function Me(Ae){for(var Ze=x(G);Ze!==null;){if(Ze.callback===null)M(G);else if(Ze.startTime<=Ae)M(G),Ze.sortIndex=Ze.expirationTime,E(U,Ze);else break;Ze=x(G)}}function Be(Ae){if(ee=!1,Me(Ae),!le)if(x(U)!==null)le=!0,rn||(rn=!0,Qn());else{var Ze=x(G);Ze!==null&&mn(Be,Ze.startTime-Ae)}}var rn=!1,ln=-1,xn=5,hn=-1;function wt(){return Oe?!0:!(g.unstable_now()-hnAe&&wt());){var Sn=Z.callback;if(typeof Sn=="function"){Z.callback=null,W=Z.priorityLevel;var kn=Sn(Z.expirationTime<=Ae);if(Ae=g.unstable_now(),typeof kn=="function"){Z.callback=kn,Me(Ae),Ze=!0;break n}Z===x(U)&&M(U),Me(Ae)}else M(U);Z=x(U)}if(Z!==null)Ze=!0;else{var xe=x(G);xe!==null&&mn(Be,xe.startTime-Ae),Ze=!1}}break e}finally{Z=null,W=sn,se=!1}Ze=void 0}}finally{Ze?Qn():rn=!1}}}var Qn;if(typeof ae=="function")Qn=function(){ae(Cn)};else if(typeof MessageChannel<"u"){var Y=new MessageChannel,Fe=Y.port2;Y.port1.onmessage=Cn,Qn=function(){Fe.postMessage(null)}}else Qn=function(){ge(Cn,0)};function mn(Ae,Ze){ln=ge(function(){Ae(g.unstable_now())},Ze)}g.unstable_IdlePriority=5,g.unstable_ImmediatePriority=1,g.unstable_LowPriority=4,g.unstable_NormalPriority=3,g.unstable_Profiling=null,g.unstable_UserBlockingPriority=2,g.unstable_cancelCallback=function(Ae){Ae.callback=null},g.unstable_forceFrameRate=function(Ae){0>Ae||125Sn?(Ae.sortIndex=sn,E(G,Ae),x(U)===null&&Ae===x(G)&&(ee?(Pe(ln),ln=-1):ee=!0,mn(Be,sn-Sn))):(Ae.sortIndex=kn,E(U,Ae),le||se||(le=!0,rn||(rn=!0,Qn()))),Ae},g.unstable_shouldYield=wt,g.unstable_wrapCallback=function(Ae){var Ze=W;return function(){var sn=W;W=Ze;try{return Ae.apply(this,arguments)}finally{W=sn}}}})(D7e)),D7e}var zhn;function czn(){return zhn||(zhn=1,N7e.exports=rzn()),N7e.exports}var I7e={exports:{}},rd={};var Fhn;function uzn(){if(Fhn)return rd;Fhn=1;var g=WG();function E(U){var G="https://react.dev/errors/"+U;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(g)}catch(E){console.error(E)}}return g(),I7e.exports=uzn(),I7e.exports}var Hhn;function ozn(){if(Hhn)return NG;Hhn=1;var g=czn(),E=WG(),x=odn();function M(a){var d="https://react.dev/errors/"+a;if(1kn||(a.current=Sn[kn],Sn[kn]=null,kn--)}function rt(a,d){kn++,Sn[kn]=a.current,a.current=d}var lt=xe(null),Bt=xe(null),hi=xe(null),Gt=xe(null);function At(a,d){switch(rt(hi,d),rt(Bt,a),rt(lt,null),d.nodeType){case 9:case 11:a=(a=d.documentElement)&&(a=a.namespaceURI)?sP(a):0;break;default:if(a=d.tagName,d=d.namespaceURI)d=sP(d),a=lP(d,a);else switch(a){case"svg":a=1;break;case"math":a=2;break;default:a=0}}un(lt),rt(lt,a)}function st(){un(lt),un(Bt),un(hi)}function Wr(a){a.memoizedState!==null&&rt(Gt,a);var d=lt.current,w=lP(d,a.type);d!==w&&(rt(Bt,a),rt(lt,w))}function Mr(a){Bt.current===a&&(un(lt),un(Bt)),Gt.current===a&&(un(Gt),Y5._currentValue=sn)}var ur,mi;function Fi(a){if(ur===void 0)try{throw Error()}catch(w){var d=w.stack.trim().match(/\n( *(at )?)/);ur=d&&d[1]||"",mi=-1{for(const P of N)if(P.type==="childList")for(const k of P.addedNodes)k.tagName==="LINK"&&k.rel==="modulepreload"&&M(k)}).observe(document,{childList:!0,subtree:!0});function x(N){const P={};return N.integrity&&(P.integrity=N.integrity),N.referrerPolicy&&(P.referrerPolicy=N.referrerPolicy),N.crossOrigin==="use-credentials"?P.credentials="include":N.crossOrigin==="anonymous"?P.credentials="omit":P.credentials="same-origin",P}function M(N){if(N.ep)return;N.ep=!0;const P=x(N);fetch(N.href,P)}})();var _hn=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{};function bke(g){return g&&g.__esModule&&Object.prototype.hasOwnProperty.call(g,"default")?g.default:g}var C7e={exports:{}},OG={};var Lhn;function ezn(){if(Lhn)return OG;Lhn=1;var g=Symbol.for("react.transitional.element"),E=Symbol.for("react.fragment");function x(M,N,P){var k=null;if(P!==void 0&&(k=""+P),N.key!==void 0&&(k=""+N.key),"key"in N){P={};for(var H in N)H!=="key"&&(P[H]=N[H])}else P=N;return N=P.ref,{$$typeof:g,type:M,key:k,ref:N!==void 0?N:null,props:P}}return OG.Fragment=E,OG.jsx=x,OG.jsxs=x,OG}var Phn;function nzn(){return Phn||(Phn=1,C7e.exports=ezn()),C7e.exports}var F=nzn(),T7e={exports:{}},Mc={};var $hn;function tzn(){if($hn)return Mc;$hn=1;var g=Symbol.for("react.transitional.element"),E=Symbol.for("react.portal"),x=Symbol.for("react.fragment"),M=Symbol.for("react.strict_mode"),N=Symbol.for("react.profiler"),P=Symbol.for("react.consumer"),k=Symbol.for("react.context"),H=Symbol.for("react.forward_ref"),U=Symbol.for("react.suspense"),G=Symbol.for("react.memo"),ie=Symbol.for("react.lazy"),Z=Symbol.for("react.activity"),W=Symbol.iterator;function se(xe){return xe===null||typeof xe!="object"?null:(xe=W&&xe[W]||xe["@@iterator"],typeof xe=="function"?xe:null)}var le={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},ee=Object.assign,Oe={};function ge(xe,un,rt){this.props=xe,this.context=un,this.refs=Oe,this.updater=rt||le}ge.prototype.isReactComponent={},ge.prototype.setState=function(xe,un){if(typeof xe!="object"&&typeof xe!="function"&&xe!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,xe,un,"setState")},ge.prototype.forceUpdate=function(xe){this.updater.enqueueForceUpdate(this,xe,"forceUpdate")};function Pe(){}Pe.prototype=ge.prototype;function ae(xe,un,rt){this.props=xe,this.context=un,this.refs=Oe,this.updater=rt||le}var Me=ae.prototype=new Pe;Me.constructor=ae,ee(Me,ge.prototype),Me.isPureReactComponent=!0;var Be=Array.isArray;function rn(){}var ln={H:null,A:null,T:null,S:null},xn=Object.prototype.hasOwnProperty;function hn(xe,un,rt){var lt=rt.ref;return{$$typeof:g,type:xe,key:un,ref:lt!==void 0?lt:null,props:rt}}function wt(xe,un){return hn(xe.type,un,xe.props)}function Tn(xe){return typeof xe=="object"&&xe!==null&&xe.$$typeof===g}function Qn(xe){var un={"=":"=0",":":"=2"};return"$"+xe.replace(/[=:]/g,function(rt){return un[rt]})}var Y=/\/+/g;function Fe(xe,un){return typeof xe=="object"&&xe!==null&&xe.key!=null?Qn(""+xe.key):un.toString(36)}function mn(xe){switch(xe.status){case"fulfilled":return xe.value;case"rejected":throw xe.reason;default:switch(typeof xe.status=="string"?xe.then(rn,rn):(xe.status="pending",xe.then(function(un){xe.status==="pending"&&(xe.status="fulfilled",xe.value=un)},function(un){xe.status==="pending"&&(xe.status="rejected",xe.reason=un)})),xe.status){case"fulfilled":return xe.value;case"rejected":throw xe.reason}}throw xe}function Ae(xe,un,rt,lt,Bt){var hi=typeof xe;(hi==="undefined"||hi==="boolean")&&(xe=null);var Gt=!1;if(xe===null)Gt=!0;else switch(hi){case"bigint":case"string":case"number":Gt=!0;break;case"object":switch(xe.$$typeof){case g:case E:Gt=!0;break;case ie:return Gt=xe._init,Ae(Gt(xe._payload),un,rt,lt,Bt)}}if(Gt)return Bt=Bt(xe),Gt=lt===""?"."+Fe(xe,0):lt,Be(Bt)?(rt="",Gt!=null&&(rt=Gt.replace(Y,"$&/")+"/"),Ae(Bt,un,rt,"",function(Wr){return Wr})):Bt!=null&&(Tn(Bt)&&(Bt=wt(Bt,rt+(Bt.key==null||xe&&xe.key===Bt.key?"":(""+Bt.key).replace(Y,"$&/")+"/")+Gt)),un.push(Bt)),1;Gt=0;var At=lt===""?".":lt+":";if(Be(xe))for(var st=0;st>>1,kn=Ae[Sn];if(0>>1;SnN(rt,sn))ltN(Bt,rt)?(Ae[Sn]=Bt,Ae[lt]=sn,Sn=lt):(Ae[Sn]=rt,Ae[un]=sn,Sn=un);else if(ltN(Bt,sn))Ae[Sn]=Bt,Ae[lt]=sn,Sn=lt;else break e}}return Ze}function N(Ae,Ze){var sn=Ae.sortIndex-Ze.sortIndex;return sn!==0?sn:Ae.id-Ze.id}if(g.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var P=performance;g.unstable_now=function(){return P.now()}}else{var k=Date,H=k.now();g.unstable_now=function(){return k.now()-H}}var U=[],G=[],ie=1,Z=null,W=3,se=!1,le=!1,ee=!1,Oe=!1,ge=typeof setTimeout=="function"?setTimeout:null,Pe=typeof clearTimeout=="function"?clearTimeout:null,ae=typeof setImmediate<"u"?setImmediate:null;function Me(Ae){for(var Ze=x(G);Ze!==null;){if(Ze.callback===null)M(G);else if(Ze.startTime<=Ae)M(G),Ze.sortIndex=Ze.expirationTime,E(U,Ze);else break;Ze=x(G)}}function Be(Ae){if(ee=!1,Me(Ae),!le)if(x(U)!==null)le=!0,rn||(rn=!0,Qn());else{var Ze=x(G);Ze!==null&&mn(Be,Ze.startTime-Ae)}}var rn=!1,ln=-1,xn=5,hn=-1;function wt(){return Oe?!0:!(g.unstable_now()-hnAe&&wt());){var Sn=Z.callback;if(typeof Sn=="function"){Z.callback=null,W=Z.priorityLevel;var kn=Sn(Z.expirationTime<=Ae);if(Ae=g.unstable_now(),typeof kn=="function"){Z.callback=kn,Me(Ae),Ze=!0;break n}Z===x(U)&&M(U),Me(Ae)}else M(U);Z=x(U)}if(Z!==null)Ze=!0;else{var xe=x(G);xe!==null&&mn(Be,xe.startTime-Ae),Ze=!1}}break e}finally{Z=null,W=sn,se=!1}Ze=void 0}}finally{Ze?Qn():rn=!1}}}var Qn;if(typeof ae=="function")Qn=function(){ae(Tn)};else if(typeof MessageChannel<"u"){var Y=new MessageChannel,Fe=Y.port2;Y.port1.onmessage=Tn,Qn=function(){Fe.postMessage(null)}}else Qn=function(){ge(Tn,0)};function mn(Ae,Ze){ln=ge(function(){Ae(g.unstable_now())},Ze)}g.unstable_IdlePriority=5,g.unstable_ImmediatePriority=1,g.unstable_LowPriority=4,g.unstable_NormalPriority=3,g.unstable_Profiling=null,g.unstable_UserBlockingPriority=2,g.unstable_cancelCallback=function(Ae){Ae.callback=null},g.unstable_forceFrameRate=function(Ae){0>Ae||125Sn?(Ae.sortIndex=sn,E(G,Ae),x(U)===null&&Ae===x(G)&&(ee?(Pe(ln),ln=-1):ee=!0,mn(Be,sn-Sn))):(Ae.sortIndex=kn,E(U,Ae),le||se||(le=!0,rn||(rn=!0,Qn()))),Ae},g.unstable_shouldYield=wt,g.unstable_wrapCallback=function(Ae){var Ze=W;return function(){var sn=W;W=Ze;try{return Ae.apply(this,arguments)}finally{W=sn}}}})(D7e)),D7e}var zhn;function czn(){return zhn||(zhn=1,N7e.exports=rzn()),N7e.exports}var I7e={exports:{}},rd={};var Fhn;function uzn(){if(Fhn)return rd;Fhn=1;var g=WG();function E(U){var G="https://react.dev/errors/"+U;if(1"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(g)}catch(E){console.error(E)}}return g(),I7e.exports=uzn(),I7e.exports}var Hhn;function ozn(){if(Hhn)return NG;Hhn=1;var g=czn(),E=WG(),x=odn();function M(a){var d="https://react.dev/errors/"+a;if(1kn||(a.current=Sn[kn],Sn[kn]=null,kn--)}function rt(a,d){kn++,Sn[kn]=a.current,a.current=d}var lt=xe(null),Bt=xe(null),hi=xe(null),Gt=xe(null);function At(a,d){switch(rt(hi,d),rt(Bt,a),rt(lt,null),d.nodeType){case 9:case 11:a=(a=d.documentElement)&&(a=a.namespaceURI)?sP(a):0;break;default:if(a=d.tagName,d=d.namespaceURI)d=sP(d),a=lP(d,a);else switch(a){case"svg":a=1;break;case"math":a=2;break;default:a=0}}un(lt),rt(lt,a)}function st(){un(lt),un(Bt),un(hi)}function Wr(a){a.memoizedState!==null&&rt(Gt,a);var d=lt.current,w=lP(d,a.type);d!==w&&(rt(Bt,a),rt(lt,w))}function Mr(a){Bt.current===a&&(un(lt),un(Bt)),Gt.current===a&&(un(Gt),Y5._currentValue=sn)}var ur,mi;function Fi(a){if(ur===void 0)try{throw Error()}catch(w){var d=w.stack.trim().match(/\n( *(at )?)/);ur=d&&d[1]||"",mi=-1)":-1C||en[j]!==Ln[C]){var nt=` -`+en[j].replace(" at new "," at ");return a.displayName&&nt.includes("")&&(nt=nt.replace("",a.displayName)),nt}while(1<=j&&0<=C);break}}}finally{fu=!1,Error.prepareStackTrace=w}return(w=a?a.displayName||a.name:"")?Fi(w):""}function Ws(a,d){switch(a.tag){case 26:case 27:case 5:return Fi(a.type);case 16:return Fi("Lazy");case 13:return a.child!==d&&d!==null?Fi("Suspense Fallback"):Fi("Suspense");case 19:return Fi("SuspenseList");case 0:case 15:return Eu(a.type,!1);case 11:return Eu(a.type.render,!1);case 1:return Eu(a.type,!0);case 31:return Fi("Activity");default:return""}}function Dh(a){try{var d="",w=null;do d+=Ws(a,w),w=a,a=a.return;while(a);return d}catch(j){return` +`);for(T=j=0;jT||en[j]!==Ln[T]){var nt=` +`+en[j].replace(" at new "," at ");return a.displayName&&nt.includes("")&&(nt=nt.replace("",a.displayName)),nt}while(1<=j&&0<=T);break}}}finally{fu=!1,Error.prepareStackTrace=w}return(w=a?a.displayName||a.name:"")?Fi(w):""}function Ws(a,d){switch(a.tag){case 26:case 27:case 5:return Fi(a.type);case 16:return Fi("Lazy");case 13:return a.child!==d&&d!==null?Fi("Suspense Fallback"):Fi("Suspense");case 19:return Fi("SuspenseList");case 0:case 15:return Eu(a.type,!1);case 11:return Eu(a.type.render,!1);case 1:return Eu(a.type,!0);case 31:return Fi("Activity");default:return""}}function Dh(a){try{var d="",w=null;do d+=Ws(a,w),w=a,a=a.return;while(a);return d}catch(j){return` Error generating stack: `+j.message+` -`+j.stack}}var ef=Object.prototype.hasOwnProperty,ch=g.unstable_scheduleCallback,kc=g.unstable_cancelCallback,cd=g.unstable_shouldYield,jb=g.unstable_requestPaint,Rs=g.unstable_now,c0=g.unstable_getCurrentPriorityLevel,u0=g.unstable_ImmediatePriority,o0=g.unstable_UserBlockingPriority,Bg=g.unstable_NormalPriority,r6=g.unstable_LowPriority,zg=g.unstable_IdlePriority,c6=g.log,d1=g.unstable_setDisableYieldValue,ud=null,Sf=null;function b1(a){if(typeof c6=="function"&&d1(a),Sf&&typeof Sf.setStrictMode=="function")try{Sf.setStrictMode(ud,a)}catch{}}var xf=Math.clz32?Math.clz32:a5,l5=Math.log,f5=Math.LN2;function a5(a){return a>>>=0,a===0?32:31-(l5(a)/f5|0)|0}var Fg=256,Eb=262144,Jg=4194304;function _u(a){var d=a&42;if(d!==0)return d;switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return a&261888;case 262144:case 524288:case 1048576:case 2097152:return a&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return a&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return a}}function Hg(a,d,w){var j=a.pendingLanes;if(j===0)return 0;var C=0,D=a.suspendedLanes,Q=a.pingedLanes;a=a.warmLanes;var de=j&134217727;return de!==0?(j=de&~D,j!==0?C=_u(j):(Q&=de,Q!==0?C=_u(Q):w||(w=de&~a,w!==0&&(C=_u(w))))):(de=j&~D,de!==0?C=_u(de):Q!==0?C=_u(Q):w||(w=j&~a,w!==0&&(C=_u(w)))),C===0?0:d!==0&&d!==C&&(d&D)===0&&(D=C&-C,w=d&-d,D>=w||D===32&&(w&4194048)!==0)?d:C}function Gg(a,d){return(a.pendingLanes&~(a.suspendedLanes&~a.pingedLanes)&d)===0}function u6(a,d){switch(a){case 1:case 2:case 4:case 8:case 64:return d+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return d+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function h5(){var a=Jg;return Jg<<=1,(Jg&62914560)===0&&(Jg=4194304),a}function Wm(a){for(var d=[],w=0;31>w;w++)d.push(a);return d}function qg(a,d){a.pendingLanes|=d,d!==268435456&&(a.suspendedLanes=0,a.pingedLanes=0,a.warmLanes=0)}function o6(a,d,w,j,C,D){var Q=a.pendingLanes;a.pendingLanes=w,a.suspendedLanes=0,a.pingedLanes=0,a.warmLanes=0,a.expiredLanes&=w,a.entangledLanes&=w,a.errorRecoveryDisabledLanes&=w,a.shellSuspendCounter=0;var de=a.entanglements,en=a.expirationTimes,Ln=a.hiddenUpdates;for(w=Q&~w;0"u")return null;try{return a.activeElement||a.body}catch{return a.body}}var OA=/[\n"\\]/g;function _h(a){return a.replace(OA,function(d){return"\\"+d.charCodeAt(0).toString(16)+" "})}function i3(a,d,w,j,C,D,Q,de){a.name="",Q!=null&&typeof Q!="function"&&typeof Q!="symbol"&&typeof Q!="boolean"?a.type=Q:a.removeAttribute("type"),d!=null?Q==="number"?(d===0&&a.value===""||a.value!=d)&&(a.value=""+Ih(d)):a.value!==""+Ih(d)&&(a.value=""+Ih(d)):Q!=="submit"&&Q!=="reset"||a.removeAttribute("value"),d!=null?a6(a,Q,Ih(d)):w!=null?a6(a,Q,Ih(w)):j!=null&&a.removeAttribute("value"),C==null&&D!=null&&(a.defaultChecked=!!D),C!=null&&(a.checked=C&&typeof C!="function"&&typeof C!="symbol"),de!=null&&typeof de!="function"&&typeof de!="symbol"&&typeof de!="boolean"?a.name=""+Ih(de):a.removeAttribute("name")}function ok(a,d,w,j,C,D,Q,de){if(D!=null&&typeof D!="function"&&typeof D!="symbol"&&typeof D!="boolean"&&(a.type=D),d!=null||w!=null){if(!(D!=="submit"&&D!=="reset"||d!=null)){p5(a);return}w=w!=null?""+Ih(w):"",d=d!=null?""+Ih(d):w,de||d===a.value||(a.value=d),a.defaultValue=d}j=j??C,j=typeof j!="function"&&typeof j!="symbol"&&!!j,a.checked=de?a.checked:!!j,a.defaultChecked=!!j,Q!=null&&typeof Q!="function"&&typeof Q!="symbol"&&typeof Q!="boolean"&&(a.name=Q),p5(a)}function a6(a,d,w){d==="number"&&t3(a.ownerDocument)===a||a.defaultValue===""+w||(a.defaultValue=""+w)}function Vg(a,d,w,j){if(a=a.options,d){d={};for(var C=0;C"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),LA=!1;if(Qg)try{var d6={};Object.defineProperty(d6,"passive",{get:function(){LA=!0}}),window.addEventListener("test",d6,d6),window.removeEventListener("test",d6,d6)}catch{LA=!1}var Op=null,PA=null,lk=null;function xI(){if(lk)return lk;var a,d=PA,w=d.length,j,C="value"in Op?Op.value:Op.textContent,D=C.length;for(a=0;a=w6),NI=" ",DI=!1;function II(a,d){switch(a){case"keyup":return Iq.indexOf(d.keyCode)!==-1;case"keydown":return d.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function _I(a){return a=a.detail,typeof a=="object"&&"data"in a?a.data:null}var k5=!1;function Lq(a,d){switch(a){case"compositionend":return _I(d);case"keypress":return d.which!==32?null:(DI=!0,NI);case"textInput":return a=d.data,a===NI&&DI?null:a;default:return null}}function Pq(a,d){if(k5)return a==="compositionend"||!FA&&II(a,d)?(a=xI(),lk=PA=Op=null,k5=!1,a):null;switch(a){case"paste":return null;case"keypress":if(!(d.ctrlKey||d.altKey||d.metaKey)||d.ctrlKey&&d.altKey){if(d.char&&1=d)return{node:w,offset:d-a};a=j}e:{for(;w;){if(w.nextSibling){w=w.nextSibling;break e}w=w.parentNode}w=void 0}w=FI(w)}}function HI(a,d){return a&&d?a===d?!0:a&&a.nodeType===3?!1:d&&d.nodeType===3?HI(a,d.parentNode):"contains"in a?a.contains(d):a.compareDocumentPosition?!!(a.compareDocumentPosition(d)&16):!1:!1}function GI(a){a=a!=null&&a.ownerDocument!=null&&a.ownerDocument.defaultView!=null?a.ownerDocument.defaultView:window;for(var d=t3(a.document);d instanceof a.HTMLIFrameElement;){try{var w=typeof d.contentWindow.location.href=="string"}catch{w=!1}if(w)a=d.contentWindow;else break;d=t3(a.document)}return d}function qA(a){var d=a&&a.nodeName&&a.nodeName.toLowerCase();return d&&(d==="input"&&(a.type==="text"||a.type==="search"||a.type==="tel"||a.type==="url"||a.type==="password")||d==="textarea"||a.contentEditable==="true")}var Gq=Qg&&"documentMode"in document&&11>=document.documentMode,j5=null,UA=null,y6=null,XA=!1;function qI(a,d,w){var j=w.window===w?w.document:w.nodeType===9?w:w.ownerDocument;XA||j5==null||j5!==t3(j)||(j=j5,"selectionStart"in j&&qA(j)?j={start:j.selectionStart,end:j.selectionEnd}:(j=(j.ownerDocument&&j.ownerDocument.defaultView||window).getSelection(),j={anchorNode:j.anchorNode,anchorOffset:j.anchorOffset,focusNode:j.focusNode,focusOffset:j.focusOffset}),y6&&v6(y6,j)||(y6=j,j=nj(UA,"onSelect"),0>=Q,C-=Q,Sb=1<<32-xf(d)+C|w<Hr?(tc=Ui,Ui=null):tc=Ui.sibling;var qc=Jn(En,Ui,In[Hr],ct);if(qc===null){Ui===null&&(Ui=tc);break}a&&Ui&&qc.alternate===null&&d(En,Ui),on=D(qc,on,Hr),Lu===null?Ji=qc:Lu.sibling=qc,Lu=qc,Ui=tc}if(Hr===In.length)return w(En,Ui),cu&&Zg(En,Hr),Ji;if(Ui===null){for(;HrHr?(tc=Ui,Ui=null):tc=Ui.sibling;var xt=Jn(En,Ui,qc.value,ct);if(xt===null){Ui===null&&(Ui=tc);break}a&&Ui&&xt.alternate===null&&d(En,Ui),on=D(xt,on,Hr),Lu===null?Ji=xt:Lu.sibling=xt,Lu=xt,Ui=tc}if(qc.done)return w(En,Ui),cu&&Zg(En,Hr),Ji;if(Ui===null){for(;!qc.done;Hr++,qc=In.next())qc=gt(En,qc.value,ct),qc!==null&&(on=D(qc,on,Hr),Lu===null?Ji=qc:Lu.sibling=qc,Lu=qc);return cu&&Zg(En,Hr),Ji}for(Ui=j(Ui);!qc.done;Hr++,qc=In.next())qc=Zn(Ui,En,Hr,qc.value,ct),qc!==null&&(a&&qc.alternate!==null&&Ui.delete(qc.key===null?Hr:qc.key),on=D(qc,on,Hr),Lu===null?Ji=qc:Lu.sibling=qc,Lu=qc);return a&&Ui.forEach(function(lX){return d(En,lX)}),cu&&Zg(En,Hr),Ji}function co(En,on,In,ct){if(typeof In=="object"&&In!==null&&In.type===ee&&In.key===null&&(In=In.props.children),typeof In=="object"&&In!==null){switch(In.$$typeof){case se:e:{for(var Ji=In.key;on!==null;){if(on.key===Ji){if(Ji=In.type,Ji===ee){if(on.tag===7){w(En,on.sibling),ct=C(on,In.props.children),ct.return=En,En=ct;break e}}else if(on.elementType===Ji||typeof Ji=="object"&&Ji!==null&&Ji.$$typeof===xn&&a3(Ji)===on.type){w(En,on.sibling),ct=C(on,In.props),T6(ct,In),ct.return=En,En=ct;break e}w(En,on);break}else d(En,on);on=on.sibling}In.type===ee?(ct=s3(In.props.children,En.mode,ct,In.key),ct.return=En,En=ct):(ct=vk(In.type,In.key,In.props,null,En.mode,ct),T6(ct,In),ct.return=En,En=ct)}return Q(En);case le:e:{for(Ji=In.key;on!==null;){if(on.key===Ji)if(on.tag===4&&on.stateNode.containerInfo===In.containerInfo&&on.stateNode.implementation===In.implementation){w(En,on.sibling),ct=C(on,In.children||[]),ct.return=En,En=ct;break e}else{w(En,on);break}else d(En,on);on=on.sibling}ct=eM(In,En.mode,ct),ct.return=En,En=ct}return Q(En);case xn:return In=a3(In),co(En,on,In,ct)}if(mn(In))return Di(En,on,In,ct);if(Qn(In)){if(Ji=Qn(In),typeof Ji!="function")throw Error(M(150));return In=Ji.call(In),Tr(En,on,In,ct)}if(typeof In.then=="function")return co(En,on,Sk(In),ct);if(In.$$typeof===ae)return co(En,on,E6(En,In),ct);xk(En,In)}return typeof In=="string"&&In!==""||typeof In=="number"||typeof In=="bigint"?(In=""+In,on!==null&&on.tag===6?(w(En,on.sibling),ct=C(on,In),ct.return=En,En=ct):(w(En,on),ct=ZA(In,En.mode,ct),ct.return=En,En=ct),Q(En)):w(En,on)}return function(En,on,In,ct){try{M6=0;var Ji=co(En,on,In,ct);return _5=null,Ji}catch(Ui){if(Ui===I5||Ui===jk)throw Ui;var Lu=w1(29,Ui,null,En.mode);return Lu.lanes=ct,Lu.return=En,Lu}}}var d3=h_(!0),d_=h_(!1),Rp=!1;function dM(a){a.updateQueue={baseState:a.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function bM(a,d){a=a.updateQueue,d.updateQueue===a&&(d.updateQueue={baseState:a.baseState,firstBaseUpdate:a.firstBaseUpdate,lastBaseUpdate:a.lastBaseUpdate,shared:a.shared,callbacks:null})}function Bp(a){return{lane:a,tag:0,payload:null,callback:null,next:null}}function zp(a,d,w){var j=a.updateQueue;if(j===null)return null;if(j=j.shared,(Ju&2)!==0){var C=j.pending;return C===null?d.next=d:(d.next=C.next,C.next=d),j.pending=d,d=mk(a),WI(a,null,w),d}return pk(a,j,d,w),mk(a)}function C6(a,d,w){if(d=d.updateQueue,d!==null&&(d=d.shared,(w&4194048)!==0)){var j=d.lanes;j&=a.pendingLanes,w|=j,d.lanes=w,Zm(a,w)}}function gM(a,d){var w=a.updateQueue,j=a.alternate;if(j!==null&&(j=j.updateQueue,w===j)){var C=null,D=null;if(w=w.firstBaseUpdate,w!==null){do{var Q={lane:w.lane,tag:w.tag,payload:w.payload,callback:null,next:null};D===null?C=D=Q:D=D.next=Q,w=w.next}while(w!==null);D===null?C=D=d:D=D.next=d}else C=D=d;w={baseState:j.baseState,firstBaseUpdate:C,lastBaseUpdate:D,shared:j.shared,callbacks:j.callbacks},a.updateQueue=w;return}a=w.lastBaseUpdate,a===null?w.firstBaseUpdate=d:a.next=d,w.lastBaseUpdate=d}var wM=!1;function O6(){if(wM){var a=D5;if(a!==null)throw a}}function N6(a,d,w,j){wM=!1;var C=a.updateQueue;Rp=!1;var D=C.firstBaseUpdate,Q=C.lastBaseUpdate,de=C.shared.pending;if(de!==null){C.shared.pending=null;var en=de,Ln=en.next;en.next=null,Q===null?D=Ln:Q.next=Ln,Q=en;var nt=a.alternate;nt!==null&&(nt=nt.updateQueue,de=nt.lastBaseUpdate,de!==Q&&(de===null?nt.firstBaseUpdate=Ln:de.next=Ln,nt.lastBaseUpdate=en))}if(D!==null){var gt=C.baseState;Q=0,nt=Ln=en=null,de=D;do{var Jn=de.lane&-536870913,Zn=Jn!==de.lane;if(Zn?(nu&Jn)===Jn:(j&Jn)===Jn){Jn!==0&&Jn===N5&&(wM=!0),nt!==null&&(nt=nt.next={lane:0,tag:de.tag,payload:de.payload,callback:null,next:null});e:{var Di=a,Tr=de;Jn=d;var co=w;switch(Tr.tag){case 1:if(Di=Tr.payload,typeof Di=="function"){gt=Di.call(co,gt,Jn);break e}gt=Di;break e;case 3:Di.flags=Di.flags&-65537|128;case 0:if(Di=Tr.payload,Jn=typeof Di=="function"?Di.call(co,gt,Jn):Di,Jn==null)break e;gt=Z({},gt,Jn);break e;case 2:Rp=!0}}Jn=de.callback,Jn!==null&&(a.flags|=64,Zn&&(a.flags|=8192),Zn=C.callbacks,Zn===null?C.callbacks=[Jn]:Zn.push(Jn))}else Zn={lane:Jn,tag:de.tag,payload:de.payload,callback:de.callback,next:null},nt===null?(Ln=nt=Zn,en=gt):nt=nt.next=Zn,Q|=Jn;if(de=de.next,de===null){if(de=C.shared.pending,de===null)break;Zn=de,de=Zn.next,Zn.next=null,C.lastBaseUpdate=Zn,C.shared.pending=null}}while(!0);nt===null&&(en=gt),C.baseState=en,C.firstBaseUpdate=Ln,C.lastBaseUpdate=nt,D===null&&(C.shared.lanes=0),qp|=Q,a.lanes=Q,a.memoizedState=gt}}function b_(a,d){if(typeof a!="function")throw Error(M(191,a));a.call(d)}function g_(a,d){var w=a.callbacks;if(w!==null)for(a.callbacks=null,a=0;aD?D:8;var Q=Ae.T,de={};Ae.T=de,LM(a,!1,d,w);try{var en=C(),Ln=Ae.S;if(Ln!==null&&Ln(de,en),en!==null&&typeof en=="object"&&typeof en.then=="function"){var nt=Wq(en,j);L6(a,d,nt,k1(a))}else L6(a,d,j,k1(a))}catch(gt){L6(a,d,{then:function(){},status:"rejected",reason:gt},k1())}finally{Ze.p=D,Q!==null&&de.types!==null&&(Q.types=de.types),Ae.T=Q}}function IM(){}function _6(a,d,w,j){if(a.tag!==5)throw Error(M(476));var C=U_(a).queue;q_(a,C,d,sn,w===null?IM:function(){return Lk(a),w(j)})}function U_(a){var d=a.memoizedState;if(d!==null)return d;d={memoizedState:sn,baseState:sn,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:iw,lastRenderedState:sn},next:null};var w={};return d.next={memoizedState:w,baseState:w,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:iw,lastRenderedState:w},next:null},a.memoizedState=d,a=a.alternate,a!==null&&(a.memoizedState=d),d}function Lk(a){var d=U_(a);d.next===null&&(d=a.alternate.memoizedState),L6(a,d.next.queue,{},k1())}function _M(){return ca(Y5)}function X_(){return nl().memoizedState}function K_(){return nl().memoizedState}function cU(a){for(var d=a.return;d!==null;){switch(d.tag){case 24:case 3:var w=k1();a=Bp(w);var j=zp(d,a,w);j!==null&&(Bh(j,d,w),C6(j,d,w)),d={cache:sM()},a.payload=d;return}d=d.return}}function uU(a,d,w){var j=k1();w={lane:j,revertLane:0,gesture:null,action:w,hasEagerState:!1,eagerState:null,next:null},Pk(a)?Y_(d,w):(w=QA(a,d,w,j),w!==null&&(Bh(w,a,j),PM(w,d,j)))}function V_(a,d,w){var j=k1();L6(a,d,w,j)}function L6(a,d,w,j){var C={lane:j,revertLane:0,gesture:null,action:w,hasEagerState:!1,eagerState:null,next:null};if(Pk(a))Y_(d,C);else{var D=a.alternate;if(a.lanes===0&&(D===null||D.lanes===0)&&(D=d.lastRenderedReducer,D!==null))try{var Q=d.lastRenderedState,de=D(Q,w);if(C.hasEagerState=!0,C.eagerState=de,g1(de,Q))return pk(a,d,C,0),Io===null&&wk(),!1}catch{}if(w=QA(a,d,C,j),w!==null)return Bh(w,a,j),PM(w,d,j),!0}return!1}function LM(a,d,w,j){if(j={lane:2,revertLane:pT(),gesture:null,action:j,hasEagerState:!1,eagerState:null,next:null},Pk(a)){if(d)throw Error(M(479))}else d=QA(a,w,j,2),d!==null&&Bh(d,a,2)}function Pk(a){var d=a.alternate;return a===bc||d!==null&&d===bc}function Y_(a,d){P5=Tk=!0;var w=a.pending;w===null?d.next=d:(d.next=w.next,w.next=d),a.pending=d}function PM(a,d,w){if((w&4194048)!==0){var j=d.lanes;j&=a.pendingLanes,w|=j,d.lanes=w,Zm(a,w)}}var P6={readContext:ca,use:Nk,useCallback:Bs,useContext:Bs,useEffect:Bs,useImperativeHandle:Bs,useLayoutEffect:Bs,useInsertionEffect:Bs,useMemo:Bs,useReducer:Bs,useRef:Bs,useState:Bs,useDebugValue:Bs,useDeferredValue:Bs,useTransition:Bs,useSyncExternalStore:Bs,useId:Bs,useHostTransitionStatus:Bs,useFormState:Bs,useActionState:Bs,useOptimistic:Bs,useMemoCache:Bs,useCacheRefresh:Bs};P6.useEffectEvent=Bs;var oU={readContext:ca,use:Nk,useCallback:function(a,d){return uh().memoizedState=[a,d===void 0?null:d],a},useContext:ca,useEffect:P_,useImperativeHandle:function(a,d,w){w=w!=null?w.concat([a]):null,Ik(4194308,4,z_.bind(null,d,a),w)},useLayoutEffect:function(a,d){return Ik(4194308,4,a,d)},useInsertionEffect:function(a,d){Ik(4,2,a,d)},useMemo:function(a,d){var w=uh();d=d===void 0?null:d;var j=a();if(b3){b1(!0);try{a()}finally{b1(!1)}}return w.memoizedState=[j,d],j},useReducer:function(a,d,w){var j=uh();if(w!==void 0){var C=w(d);if(b3){b1(!0);try{w(d)}finally{b1(!1)}}}else C=d;return j.memoizedState=j.baseState=C,a={pending:null,lanes:0,dispatch:null,lastRenderedReducer:a,lastRenderedState:C},j.queue=a,a=a.dispatch=uU.bind(null,bc,a),[j.memoizedState,a]},useRef:function(a){var d=uh();return a={current:a},d.memoizedState=a},useState:function(a){a=TM(a);var d=a.queue,w=V_.bind(null,bc,d);return d.dispatch=w,[a.memoizedState,w]},useDebugValue:NM,useDeferredValue:function(a,d){var w=uh();return DM(w,a,d)},useTransition:function(){var a=TM(!1);return a=q_.bind(null,bc,a.queue,!0,!1),uh().memoizedState=a,[!1,a]},useSyncExternalStore:function(a,d,w){var j=bc,C=uh();if(cu){if(w===void 0)throw Error(M(407));w=w()}else{if(w=d(),Io===null)throw Error(M(349));(nu&127)!==0||k_(j,d,w)}C.memoizedState=w;var D={value:w,getSnapshot:d};return C.queue=D,P_(nU.bind(null,j,D,a),[a]),j.flags|=2048,R5(9,{destroy:void 0},j_.bind(null,j,D,w,d),null),w},useId:function(){var a=uh(),d=Io.identifierPrefix;if(cu){var w=xb,j=Sb;w=(j&~(1<<32-xf(j)-1)).toString(32)+w,d="_"+d+"R_"+w,w=Ck++,0<\/script>",D=D.removeChild(D.firstChild);break;case"select":D=typeof j.is=="string"?Q.createElement("select",{is:j.is}):Q.createElement("select"),j.multiple?D.multiple=!0:j.size&&(D.size=j.size);break;default:D=typeof j.is=="string"?Q.createElement(C,{is:j.is}):Q.createElement(C)}}D[Zs]=d,D[Ca]=j;e:for(Q=d.child;Q!==null;){if(Q.tag===5||Q.tag===6)D.appendChild(Q.stateNode);else if(Q.tag!==4&&Q.tag!==27&&Q.child!==null){Q.child.return=Q,Q=Q.child;continue}if(Q===d)break e;for(;Q.sibling===null;){if(Q.return===null||Q.return===d)break e;Q=Q.return}Q.sibling.return=Q.return,Q=Q.sibling}d.stateNode=D;e:switch(oa(D,C,j),C){case"button":case"input":case"select":case"textarea":j=!!j.autoFocus;break e;case"img":j=!0;break e;default:j=!1}j&&a0(d)}}return yo(d),YM(d,d.type,a===null?null:a.memoizedProps,d.pendingProps,w),null;case 6:if(a&&d.stateNode!=null)a.memoizedProps!==j&&a0(d);else{if(typeof j!="string"&&d.stateNode===null)throw Error(M(166));if(a=hi.current,T5(d)){if(a=d.stateNode,w=d.memoizedProps,j=null,C=ra,C!==null)switch(C.tag){case 27:case 5:j=C.memoizedProps}a[Zs]=d,a=!!(a.nodeValue===w||j!==null&&j.suppressHydrationWarning===!0||uP(a.nodeValue,w)),a||Ip(d,!0)}else a=tj(a).createTextNode(j),a[Zs]=d,d.stateNode=a}return yo(d),null;case 31:if(w=d.memoizedState,a===null||a.memoizedState!==null){if(j=T5(d),w!==null){if(a===null){if(!j)throw Error(M(318));if(a=d.memoizedState,a=a!==null?a.dehydrated:null,!a)throw Error(M(557));a[Zs]=d}else l3(),(d.flags&128)===0&&(d.memoizedState=null),d.flags|=4;yo(d),a=!1}else w=C5(),a!==null&&a.memoizedState!==null&&(a.memoizedState.hydrationErrors=w),a=!0;if(!a)return d.flags&256?(m1(d),d):(m1(d),null);if((d.flags&128)!==0)throw Error(M(558))}return yo(d),null;case 13:if(j=d.memoizedState,a===null||a.memoizedState!==null&&a.memoizedState.dehydrated!==null){if(C=T5(d),j!==null&&j.dehydrated!==null){if(a===null){if(!C)throw Error(M(318));if(C=d.memoizedState,C=C!==null?C.dehydrated:null,!C)throw Error(M(317));C[Zs]=d}else l3(),(d.flags&128)===0&&(d.memoizedState=null),d.flags|=4;yo(d),C=!1}else C=C5(),a!==null&&a.memoizedState!==null&&(a.memoizedState.hydrationErrors=C),C=!0;if(!C)return d.flags&256?(m1(d),d):(m1(d),null)}return m1(d),(d.flags&128)!==0?(d.lanes=w,d):(w=j!==null,a=a!==null&&a.memoizedState!==null,w&&(j=d.child,C=null,j.alternate!==null&&j.alternate.memoizedState!==null&&j.alternate.memoizedState.cachePool!==null&&(C=j.alternate.memoizedState.cachePool.pool),D=null,j.memoizedState!==null&&j.memoizedState.cachePool!==null&&(D=j.memoizedState.cachePool.pool),D!==C&&(j.flags|=2048)),w!==a&&w&&(d.child.flags|=8192),zk(d,d.updateQueue),yo(d),null);case 4:return st(),a===null&&kT(d.stateNode.containerInfo),yo(d),null;case 10:return nw(d.type),yo(d),null;case 19:if(un(el),j=d.memoizedState,j===null)return yo(d),null;if(C=(d.flags&128)!==0,D=j.rendering,D===null)if(C)w3(j,!1);else{if(zs!==0||a!==null&&(a.flags&128)!==0)for(a=d.child;a!==null;){if(D=Mk(a),D!==null){for(d.flags|=128,w3(j,!1),a=D.updateQueue,d.updateQueue=a,zk(d,a),d.subtreeFlags=0,a=w,w=d.child;w!==null;)ZI(w,a),w=w.sibling;return rt(el,el.current&1|2),cu&&Zg(d,j.treeForkCount),d.child}a=a.sibling}j.tail!==null&&Rs()>Uk&&(d.flags|=128,C=!0,w3(j,!1),d.lanes=4194304)}else{if(!C)if(a=Mk(D),a!==null){if(d.flags|=128,C=!0,a=a.updateQueue,d.updateQueue=a,zk(d,a),w3(j,!0),j.tail===null&&j.tailMode==="hidden"&&!D.alternate&&!cu)return yo(d),null}else 2*Rs()-j.renderingStartTime>Uk&&w!==536870912&&(d.flags|=128,C=!0,w3(j,!1),d.lanes=4194304);j.isBackwards?(D.sibling=d.child,d.child=D):(a=j.last,a!==null?a.sibling=D:d.child=D,j.last=D)}return j.tail!==null?(a=j.tail,j.rendering=a,j.tail=a.sibling,j.renderingStartTime=Rs(),a.sibling=null,w=el.current,rt(el,C?w&1|2:w&1),cu&&Zg(d,j.treeForkCount),a):(yo(d),null);case 22:case 23:return m1(d),mM(),j=d.memoizedState!==null,a!==null?a.memoizedState!==null!==j&&(d.flags|=8192):j&&(d.flags|=8192),j?(w&536870912)!==0&&(d.flags&128)===0&&(yo(d),d.subtreeFlags&6&&(d.flags|=8192)):yo(d),w=d.updateQueue,w!==null&&zk(d,w.retryQueue),w=null,a!==null&&a.memoizedState!==null&&a.memoizedState.cachePool!==null&&(w=a.memoizedState.cachePool.pool),j=null,d.memoizedState!==null&&d.memoizedState.cachePool!==null&&(j=d.memoizedState.cachePool.pool),j!==w&&(d.flags|=2048),a!==null&&un(f3),null;case 24:return w=null,a!==null&&(w=a.memoizedState.cache),d.memoizedState.cache!==w&&(d.flags|=2048),nw(Al),yo(d),null;case 25:return null;case 30:return null}throw Error(M(156,d.tag))}function aU(a,d){switch(tM(d),d.tag){case 1:return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 3:return nw(Al),st(),a=d.flags,(a&65536)!==0&&(a&128)===0?(d.flags=a&-65537|128,d):null;case 26:case 27:case 5:return Mr(d),null;case 31:if(d.memoizedState!==null){if(m1(d),d.alternate===null)throw Error(M(340));l3()}return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 13:if(m1(d),a=d.memoizedState,a!==null&&a.dehydrated!==null){if(d.alternate===null)throw Error(M(340));l3()}return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 19:return un(el),null;case 4:return st(),null;case 10:return nw(d.type),null;case 22:case 23:return m1(d),mM(),a!==null&&un(f3),a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 24:return nw(Al),null;case 25:return null;default:return null}}function QM(a,d){switch(tM(d),d.tag){case 3:nw(Al),st();break;case 26:case 27:case 5:Mr(d);break;case 4:st();break;case 31:d.memoizedState!==null&&m1(d);break;case 13:m1(d);break;case 19:un(el);break;case 10:nw(d.type);break;case 22:case 23:m1(d),mM(),a!==null&&un(f3);break;case 24:nw(Al)}}function B6(a,d){try{var w=d.updateQueue,j=w!==null?w.lastEffect:null;if(j!==null){var C=j.next;w=C;do{if((w.tag&a)===a){j=void 0;var D=w.create,Q=w.inst;j=D(),Q.destroy=j}w=w.next}while(w!==C)}}catch(de){ro(d,d.return,de)}}function Hp(a,d,w){try{var j=d.updateQueue,C=j!==null?j.lastEffect:null;if(C!==null){var D=C.next;j=D;do{if((j.tag&a)===a){var Q=j.inst,de=Q.destroy;if(de!==void 0){Q.destroy=void 0,C=d;var en=w,Ln=de;try{Ln()}catch(nt){ro(C,en,nt)}}}j=j.next}while(j!==D)}}catch(nt){ro(d,d.return,nt)}}function z6(a){var d=a.updateQueue;if(d!==null){var w=a.stateNode;try{g_(d,w)}catch(j){ro(a,a.return,j)}}}function pL(a,d,w){w.props=g3(a.type,a.memoizedProps),w.state=a.memoizedState;try{w.componentWillUnmount()}catch(j){ro(a,d,j)}}function F6(a,d){try{var w=a.ref;if(w!==null){switch(a.tag){case 26:case 27:case 5:var j=a.stateNode;break;case 30:j=a.stateNode;break;default:j=a.stateNode}typeof w=="function"?a.refCleanup=w(j):w.current=j}}catch(C){ro(a,d,C)}}function Ab(a,d){var w=a.ref,j=a.refCleanup;if(w!==null)if(typeof j=="function")try{j()}catch(C){ro(a,d,C)}finally{a.refCleanup=null,a=a.alternate,a!=null&&(a.refCleanup=null)}else if(typeof w=="function")try{w(null)}catch(C){ro(a,d,C)}else w.current=null}function J6(a){var d=a.type,w=a.memoizedProps,j=a.stateNode;try{e:switch(d){case"button":case"input":case"select":case"textarea":w.autoFocus&&j.focus();break e;case"img":w.src?j.src=w.src:w.srcSet&&(j.srcset=w.srcSet)}}catch(C){ro(a,a.return,C)}}function WM(a,d,w){try{var j=a.stateNode;DU(j,a.type,w,d),j[Ca]=d}catch(C){ro(a,a.return,C)}}function mL(a){return a.tag===5||a.tag===3||a.tag===26||a.tag===27&&Yp(a.type)||a.tag===4}function ZM(a){e:for(;;){for(;a.sibling===null;){if(a.return===null||mL(a.return))return null;a=a.return}for(a.sibling.return=a.return,a=a.sibling;a.tag!==5&&a.tag!==6&&a.tag!==18;){if(a.tag===27&&Yp(a.type)||a.flags&2||a.child===null||a.tag===4)continue e;a.child.return=a,a=a.child}if(!(a.flags&2))return a.stateNode}}function eT(a,d,w){var j=a.tag;if(j===5||j===6)a=a.stateNode,d?(w.nodeType===9?w.body:w.nodeName==="HTML"?w.ownerDocument.body:w).insertBefore(a,d):(d=w.nodeType===9?w.body:w.nodeName==="HTML"?w.ownerDocument.body:w,d.appendChild(a),w=w._reactRootContainer,w!=null||d.onclick!==null||(d.onclick=Yg));else if(j!==4&&(j===27&&Yp(a.type)&&(w=a.stateNode,d=null),a=a.child,a!==null))for(eT(a,d,w),a=a.sibling;a!==null;)eT(a,d,w),a=a.sibling}function p3(a,d,w){var j=a.tag;if(j===5||j===6)a=a.stateNode,d?w.insertBefore(a,d):w.appendChild(a);else if(j!==4&&(j===27&&Yp(a.type)&&(w=a.stateNode),a=a.child,a!==null))for(p3(a,d,w),a=a.sibling;a!==null;)p3(a,d,w),a=a.sibling}function vL(a){var d=a.stateNode,w=a.memoizedProps;try{for(var j=a.type,C=d.attributes;C.length;)d.removeAttributeNode(C[0]);oa(d,j,w),d[Zs]=a,d[Ca]=w}catch(D){ro(a,a.return,D)}}var Mb=!1,Cl=!1,H6=!1,nT=typeof WeakSet=="function"?WeakSet:Set,Mf=null;function hU(a,d){if(a=a.containerInfo,ST=Tf,a=GI(a),qA(a)){if("selectionStart"in a)var w={start:a.selectionStart,end:a.selectionEnd};else e:{w=(w=a.ownerDocument)&&w.defaultView||window;var j=w.getSelection&&w.getSelection();if(j&&j.rangeCount!==0){w=j.anchorNode;var C=j.anchorOffset,D=j.focusNode;j=j.focusOffset;try{w.nodeType,D.nodeType}catch{w=null;break e}var Q=0,de=-1,en=-1,Ln=0,nt=0,gt=a,Jn=null;n:for(;;){for(var Zn;gt!==w||C!==0&>.nodeType!==3||(de=Q+C),gt!==D||j!==0&>.nodeType!==3||(en=Q+j),gt.nodeType===3&&(Q+=gt.nodeValue.length),(Zn=gt.firstChild)!==null;)Jn=gt,gt=Zn;for(;;){if(gt===a)break n;if(Jn===w&&++Ln===C&&(de=Q),Jn===D&&++nt===j&&(en=Q),(Zn=gt.nextSibling)!==null)break;gt=Jn,Jn=gt.parentNode}gt=Zn}w=de===-1||en===-1?null:{start:de,end:en}}else w=null}w=w||{start:0,end:0}}else w=null;for(xT={focusedElem:a,selectionRange:w},Tf=!1,Mf=d;Mf!==null;)if(d=Mf,a=d.child,(d.subtreeFlags&1028)!==0&&a!==null)a.return=d,Mf=a;else for(;Mf!==null;){switch(d=Mf,D=d.alternate,a=d.flags,d.tag){case 0:if((a&4)!==0&&(a=d.updateQueue,a=a!==null?a.events:null,a!==null))for(w=0;w title"))),oa(D,j,w),D[Zs]=a,xl(D),j=D;break e;case"link":var Q=vP("link","href",C).get(j+(w.href||""));if(Q){for(var de=0;deco&&(Q=co,co=Tr,Tr=Q);var En=JI(de,Tr),on=JI(de,co);if(En&&on&&(Zn.rangeCount!==1||Zn.anchorNode!==En.node||Zn.anchorOffset!==En.offset||Zn.focusNode!==on.node||Zn.focusOffset!==on.offset)){var In=gt.createRange();In.setStart(En.node,En.offset),Zn.removeAllRanges(),Tr>co?(Zn.addRange(In),Zn.extend(on.node,on.offset)):(In.setEnd(on.node,on.offset),Zn.addRange(In))}}}}for(gt=[],Zn=de;Zn=Zn.parentNode;)Zn.nodeType===1&>.push({element:Zn,left:Zn.scrollLeft,top:Zn.scrollTop});for(typeof de.focus=="function"&&de.focus(),de=0;dew?32:w,Ae.T=null,w=sT,sT=null;var D=Xp,Q=lw;if(nf=0,H5=Xp=null,lw=0,(Ju&6)!==0)throw Error(M(331));var de=Ju;if(Ju|=4,CL(D.current),AL(D,D.current,Q,w),Ju=de,V6(0,!1),Sf&&typeof Sf.onPostCommitFiberRoot=="function")try{Sf.onPostCommitFiberRoot(ud,D)}catch{}return!0}finally{Ze.p=C,Ae.T=j,XL(a,d)}}function VL(a,d,w){d=sd(w,d),d=FM(a.stateNode,d,2),a=zp(a,d,2),a!==null&&(qg(a,2),Tb(a))}function ro(a,d,w){if(a.tag===3)VL(a,a,w);else for(;d!==null;){if(d.tag===3){VL(d,a,w);break}else if(d.tag===1){var j=d.stateNode;if(typeof d.type.getDerivedStateFromError=="function"||typeof j.componentDidCatch=="function"&&(Up===null||!Up.has(j))){a=sd(w,a),w=tL(2),j=zp(d,w,2),j!==null&&(iL(w,j,d,a),qg(j,2),Tb(j));break}}d=d.return}}function hT(a,d,w){var j=a.pingCache;if(j===null){j=a.pingCache=new gU;var C=new Set;j.set(d,C)}else C=j.get(d),C===void 0&&(C=new Set,j.set(d,C));C.has(w)||(rT=!0,C.add(w),a=yU.bind(null,a,d,w),d.then(a,a))}function yU(a,d,w){var j=a.pingCache;j!==null&&j.delete(d),a.pingedLanes|=a.suspendedLanes&w,a.warmLanes&=~w,Io===a&&(nu&w)===w&&(zs===4||zs===3&&(nu&62914560)===nu&&300>Rs()-qk?(Ju&2)===0&&G5(a,0):cT|=w,J5===nu&&(J5=0)),Tb(a)}function YL(a,d){d===0&&(d=h5()),a=o3(a,d),a!==null&&(qg(a,d),Tb(a))}function kU(a){var d=a.memoizedState,w=0;d!==null&&(w=d.retryLane),YL(a,w)}function jU(a,d){var w=0;switch(a.tag){case 31:case 13:var j=a.stateNode,C=a.memoizedState;C!==null&&(w=C.retryLane);break;case 19:j=a.stateNode;break;case 22:j=a.stateNode._retryCache;break;default:throw Error(M(314))}j!==null&&j.delete(d),YL(a,w)}function EU(a,d){return ch(a,d)}var Wk=null,U5=null,dT=!1,Zk=!1,bT=!1,Vp=0;function Tb(a){a!==U5&&a.next===null&&(U5===null?Wk=U5=a:U5=U5.next=a),Zk=!0,dT||(dT=!0,wT())}function V6(a,d){if(!bT&&Zk){bT=!0;do for(var w=!1,j=Wk;j!==null;){if(a!==0){var C=j.pendingLanes;if(C===0)var D=0;else{var Q=j.suspendedLanes,de=j.pingedLanes;D=(1<<31-xf(42|a)+1)-1,D&=C&~(Q&~de),D=D&201326741?D&201326741|1:D?D|2:0}D!==0&&(w=!0,ZL(j,D))}else D=nu,D=Hg(j,j===Io?D:0,j.cancelPendingCommit!==null||j.timeoutHandle!==-1),(D&3)===0||Gg(j,D)||(w=!0,ZL(j,D));j=j.next}while(w);bT=!1}}function SU(){QL()}function QL(){Zk=dT=!1;var a=0;Vp!==0&&_U()&&(a=Vp);for(var d=Rs(),w=null,j=Wk;j!==null;){var C=j.next,D=gT(j,d);D===0?(j.next=null,w===null?Wk=C:w.next=C,C===null&&(U5=w)):(w=j,(a!==0||(D&3)!==0)&&(Zk=!0)),j=C}nf!==0&&nf!==5||V6(a),Vp!==0&&(Vp=0)}function gT(a,d){for(var w=a.suspendedLanes,j=a.pingedLanes,C=a.expirationTimes,D=a.pendingLanes&-62914561;0de)break;var nt=en.transferSize,gt=en.initiatorType;nt&&oP(gt)&&(en=en.responseEnd,Q+=nt*(en"u"?null:document;function gP(a,d,w){var j=X5;if(j&&typeof d=="string"&&d){var C=_h(d);C='link[rel="'+a+'"][href="'+C+'"]',typeof w=="string"&&(C+='[crossorigin="'+w+'"]'),DT.has(C)||(DT.add(C),a={rel:a,crossOrigin:w,href:d},j.querySelector(C)===null&&(d=j.createElement("link"),oa(d,"link",a),xl(d),j.head.appendChild(d)))}}function HU(a){b0.D(a),gP("dns-prefetch",a,null)}function GU(a,d){b0.C(a,d),gP("preconnect",a,d)}function IT(a,d,w){b0.L(a,d,w);var j=X5;if(j&&a&&d){var C='link[rel="preload"][as="'+_h(d)+'"]';d==="image"&&w&&w.imageSrcSet?(C+='[imagesrcset="'+_h(w.imageSrcSet)+'"]',typeof w.imageSizes=="string"&&(C+='[imagesizes="'+_h(w.imageSizes)+'"]')):C+='[href="'+_h(a)+'"]';var D=C;switch(d){case"style":D=K5(a);break;case"script":D=V5(a)}E1.has(D)||(a=Z({rel:"preload",href:d==="image"&&w&&w.imageSrcSet?void 0:a,as:d},w),E1.set(D,a),j.querySelector(C)!==null||d==="style"&&j.querySelector(y3(D))||d==="script"&&j.querySelector(e9(D))||(d=j.createElement("link"),oa(d,"link",a),xl(d),j.head.appendChild(d)))}}function qU(a,d){b0.m(a,d);var w=X5;if(w&&a){var j=d&&typeof d.as=="string"?d.as:"script",C='link[rel="modulepreload"][as="'+_h(j)+'"][href="'+_h(a)+'"]',D=C;switch(j){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":D=V5(a)}if(!E1.has(D)&&(a=Z({rel:"modulepreload",href:a},d),E1.set(D,a),w.querySelector(C)===null)){switch(j){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(w.querySelector(e9(D)))return}j=w.createElement("link"),oa(j,"link",a),xl(j),w.head.appendChild(j)}}}function UU(a,d,w){b0.S(a,d,w);var j=X5;if(j&&a){var C=Tp(j).hoistableStyles,D=K5(a);d=d||"default";var Q=C.get(D);if(!Q){var de={loading:0,preload:null};if(Q=j.querySelector(y3(D)))de.loading=5;else{a=Z({rel:"stylesheet",href:a,"data-precedence":d},w),(w=E1.get(D))&&PT(a,w);var en=Q=j.createElement("link");xl(en),oa(en,"link",a),en._p=new Promise(function(Ln,nt){en.onload=Ln,en.onerror=nt}),en.addEventListener("load",function(){de.loading|=1}),en.addEventListener("error",function(){de.loading|=2}),de.loading|=4,cj(Q,d,j)}Q={type:"stylesheet",instance:Q,count:1,state:de},C.set(D,Q)}}}function XU(a,d){b0.X(a,d);var w=X5;if(w&&a){var j=Tp(w).hoistableScripts,C=V5(a),D=j.get(C);D||(D=w.querySelector(e9(C)),D||(a=Z({src:a,async:!0},d),(d=E1.get(C))&&$T(a,d),D=w.createElement("script"),xl(D),oa(D,"link",a),w.head.appendChild(D)),D={type:"script",instance:D,count:1,state:null},j.set(C,D))}}function _T(a,d){b0.M(a,d);var w=X5;if(w&&a){var j=Tp(w).hoistableScripts,C=V5(a),D=j.get(C);D||(D=w.querySelector(e9(C)),D||(a=Z({src:a,async:!0,type:"module"},d),(d=E1.get(C))&&$T(a,d),D=w.createElement("script"),xl(D),oa(D,"link",a),w.head.appendChild(D)),D={type:"script",instance:D,count:1,state:null},j.set(C,D))}}function wP(a,d,w,j){var C=(C=hi.current)?rj(C):null;if(!C)throw Error(M(446));switch(a){case"meta":case"title":return null;case"style":return typeof w.precedence=="string"&&typeof w.href=="string"?(d=K5(w.href),w=Tp(C).hoistableStyles,j=w.get(d),j||(j={type:"style",instance:null,count:0,state:null},w.set(d,j)),j):{type:"void",instance:null,count:0,state:null};case"link":if(w.rel==="stylesheet"&&typeof w.href=="string"&&typeof w.precedence=="string"){a=K5(w.href);var D=Tp(C).hoistableStyles,Q=D.get(a);if(Q||(C=C.ownerDocument||C,Q={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},D.set(a,Q),(D=C.querySelector(y3(a)))&&!D._p&&(Q.instance=D,Q.state.loading=5),E1.has(a)||(w={rel:"preload",as:"style",href:w.href,crossOrigin:w.crossOrigin,integrity:w.integrity,media:w.media,hrefLang:w.hrefLang,referrerPolicy:w.referrerPolicy},E1.set(a,w),D||LT(C,a,w,Q.state))),d&&j===null)throw Error(M(528,""));return Q}if(d&&j!==null)throw Error(M(529,""));return null;case"script":return d=w.async,w=w.src,typeof w=="string"&&d&&typeof d!="function"&&typeof d!="symbol"?(d=V5(w),w=Tp(C).hoistableScripts,j=w.get(d),j||(j={type:"script",instance:null,count:0,state:null},w.set(d,j)),j):{type:"void",instance:null,count:0,state:null};default:throw Error(M(444,a))}}function K5(a){return'href="'+_h(a)+'"'}function y3(a){return'link[rel="stylesheet"]['+a+"]"}function pP(a){return Z({},a,{"data-precedence":a.precedence,precedence:null})}function LT(a,d,w,j){a.querySelector('link[rel="preload"][as="style"]['+d+"]")?j.loading=1:(d=a.createElement("link"),j.preload=d,d.addEventListener("load",function(){return j.loading|=1}),d.addEventListener("error",function(){return j.loading|=2}),oa(d,"link",w),xl(d),a.head.appendChild(d))}function V5(a){return'[src="'+_h(a)+'"]'}function e9(a){return"script[async]"+a}function mP(a,d,w){if(d.count++,d.instance===null)switch(d.type){case"style":var j=a.querySelector('style[data-href~="'+_h(w.href)+'"]');if(j)return d.instance=j,xl(j),j;var C=Z({},w,{"data-href":w.href,"data-precedence":w.precedence,href:null,precedence:null});return j=(a.ownerDocument||a).createElement("style"),xl(j),oa(j,"style",C),cj(j,w.precedence,a),d.instance=j;case"stylesheet":C=K5(w.href);var D=a.querySelector(y3(C));if(D)return d.state.loading|=4,d.instance=D,xl(D),D;j=pP(w),(C=E1.get(C))&&PT(j,C),D=(a.ownerDocument||a).createElement("link"),xl(D);var Q=D;return Q._p=new Promise(function(de,en){Q.onload=de,Q.onerror=en}),oa(D,"link",j),d.state.loading|=4,cj(D,w.precedence,a),d.instance=D;case"script":return D=V5(w.src),(C=a.querySelector(e9(D)))?(d.instance=C,xl(C),C):(j=w,(C=E1.get(D))&&(j=Z({},w),$T(j,C)),a=a.ownerDocument||a,C=a.createElement("script"),xl(C),oa(C,"link",j),a.head.appendChild(C),d.instance=C);case"void":return null;default:throw Error(M(443,d.type))}else d.type==="stylesheet"&&(d.state.loading&4)===0&&(j=d.instance,d.state.loading|=4,cj(j,w.precedence,a));return d.instance}function cj(a,d,w){for(var j=w.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),C=j.length?j[j.length-1]:null,D=C,Q=0;Q title"):null)}function KU(a,d,w){if(w===1||d.itemProp!=null)return!1;switch(a){case"meta":case"title":return!0;case"style":if(typeof d.precedence!="string"||typeof d.href!="string"||d.href==="")break;return!0;case"link":if(typeof d.rel!="string"||typeof d.href!="string"||d.href===""||d.onLoad||d.onError)break;return d.rel==="stylesheet"?(a=d.disabled,typeof d.precedence=="string"&&a==null):!0;case"script":if(d.async&&typeof d.async!="function"&&typeof d.async!="symbol"&&!d.onLoad&&!d.onError&&d.src&&typeof d.src=="string")return!0}return!1}function kP(a){return!(a.type==="stylesheet"&&(a.state.loading&3)===0)}function VU(a,d,w,j){if(w.type==="stylesheet"&&(typeof j.media!="string"||matchMedia(j.media).matches!==!1)&&(w.state.loading&4)===0){if(w.instance===null){var C=K5(j.href),D=d.querySelector(y3(C));if(D){d=D._p,d!==null&&typeof d=="object"&&typeof d.then=="function"&&(a.count++,a=n9.bind(a),d.then(a,a)),w.state.loading|=4,w.instance=D,xl(D);return}D=d.ownerDocument||d,j=pP(j),(C=E1.get(C))&&PT(j,C),D=D.createElement("link"),xl(D);var Q=D;Q._p=new Promise(function(de,en){Q.onload=de,Q.onerror=en}),oa(D,"link",j),w.instance=D}a.stylesheets===null&&(a.stylesheets=new Map),a.stylesheets.set(w,d),(d=w.state.preload)&&(w.state.loading&3)===0&&(a.count++,w=n9.bind(a),d.addEventListener("load",w),d.addEventListener("error",w))}}var RT=0;function YU(a,d){return a.stylesheets&&a.count===0&&k3(a,a.stylesheets),0RT?50:800)+d);return a.unsuspend=w,function(){a.unsuspend=null,clearTimeout(j),clearTimeout(C)}}:null}function n9(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)k3(this,this.stylesheets);else if(this.unsuspend){var a=this.unsuspend;this.unsuspend=null,a()}}}var t9=null;function k3(a,d){a.stylesheets=null,a.unsuspend!==null&&(a.count++,t9=new Map,d.forEach(i9,a),t9=null,n9.call(a))}function i9(a,d){if(!(d.state.loading&4)){var w=t9.get(a);if(w)var j=w.get(null);else{w=new Map,t9.set(a,w);for(var C=a.querySelectorAll("link[data-precedence],style[data-precedence]"),D=0;D"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(g)}catch(E){console.error(E)}}return g(),O7e.exports=ozn(),O7e.exports}var lzn=szn();function Ta(g){if(typeof g=="string"||typeof g=="number")return""+g;let E="";if(Array.isArray(g))for(let x=0,M;x{}};function Oue(){for(var g=0,E=arguments.length,x={},M;g=0&&(M=x.slice(N+1),x=x.slice(0,N)),x&&!E.hasOwnProperty(x))throw new Error("unknown type: "+x);return{type:x,name:M}})}aue.prototype=Oue.prototype={constructor:aue,on:function(g,E){var x=this._,M=azn(g+"",x),N,P=-1,k=M.length;if(arguments.length<2){for(;++P0)for(var x=new Array(N),M=0,N,P;M=0&&(E=g.slice(0,x))!=="xmlns"&&(g=g.slice(x+1)),Uhn.hasOwnProperty(E)?{space:Uhn[E],local:g}:g}function dzn(g){return function(){var E=this.ownerDocument,x=this.namespaceURI;return x===Z7e&&E.documentElement.namespaceURI===Z7e?E.createElement(g):E.createElementNS(x,g)}}function bzn(g){return function(){return this.ownerDocument.createElementNS(g.space,g.local)}}function sdn(g){var E=Nue(g);return(E.local?bzn:dzn)(E)}function gzn(){}function gke(g){return g==null?gzn:function(){return this.querySelector(g)}}function wzn(g){typeof g!="function"&&(g=gke(g));for(var E=this._groups,x=E.length,M=new Array(x),N=0;N=ae&&(ae=Pe+1);!(Be=Oe[ae])&&++ae=0;)(k=M[N])&&(P&&k.compareDocumentPosition(P)^4&&P.parentNode.insertBefore(k,P),P=k);return this}function Fzn(g){g||(g=Jzn);function E(Z,W){return Z&&W?g(Z.__data__,W.__data__):!Z-!W}for(var x=this._groups,M=x.length,N=new Array(M),P=0;PE?1:g>=E?0:NaN}function Hzn(){var g=arguments[0];return arguments[0]=this,g.apply(null,arguments),this}function Gzn(){return Array.from(this)}function qzn(){for(var g=this._groups,E=0,x=g.length;E1?this.each((E==null?tFn:typeof E=="function"?rFn:iFn)(g,E,x??"")):hI(this.node(),g)}function hI(g,E){return g.style.getPropertyValue(E)||ddn(g).getComputedStyle(g,null).getPropertyValue(E)}function uFn(g){return function(){delete this[g]}}function oFn(g,E){return function(){this[g]=E}}function sFn(g,E){return function(){var x=E.apply(this,arguments);x==null?delete this[g]:this[g]=x}}function lFn(g,E){return arguments.length>1?this.each((E==null?uFn:typeof E=="function"?sFn:oFn)(g,E)):this.node()[g]}function bdn(g){return g.trim().split(/^|\s+/)}function wke(g){return g.classList||new gdn(g)}function gdn(g){this._node=g,this._names=bdn(g.getAttribute("class")||"")}gdn.prototype={add:function(g){var E=this._names.indexOf(g);E<0&&(this._names.push(g),this._node.setAttribute("class",this._names.join(" ")))},remove:function(g){var E=this._names.indexOf(g);E>=0&&(this._names.splice(E,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(g){return this._names.indexOf(g)>=0}};function wdn(g,E){for(var x=wke(g),M=-1,N=E.length;++M=0&&(x=E.slice(M+1),E=E.slice(0,M)),{type:E,name:x}})}function $Fn(g){return function(){var E=this.__on;if(E){for(var x=0,M=-1,N=E.length,P;x()=>g;function eke(g,{sourceEvent:E,subject:x,target:M,identifier:N,active:P,x:k,y:H,dx:U,dy:G,dispatch:ie}){Object.defineProperties(this,{type:{value:g,enumerable:!0,configurable:!0},sourceEvent:{value:E,enumerable:!0,configurable:!0},subject:{value:x,enumerable:!0,configurable:!0},target:{value:M,enumerable:!0,configurable:!0},identifier:{value:N,enumerable:!0,configurable:!0},active:{value:P,enumerable:!0,configurable:!0},x:{value:k,enumerable:!0,configurable:!0},y:{value:H,enumerable:!0,configurable:!0},dx:{value:U,enumerable:!0,configurable:!0},dy:{value:G,enumerable:!0,configurable:!0},_:{value:ie}})}eke.prototype.on=function(){var g=this._.on.apply(this._,arguments);return g===this._?this:g};function XFn(g){return!g.ctrlKey&&!g.button}function KFn(){return this.parentNode}function VFn(g,E){return E??{x:g.x,y:g.y}}function YFn(){return navigator.maxTouchPoints||"ontouchstart"in this}function jdn(){var g=XFn,E=KFn,x=VFn,M=YFn,N={},P=Oue("start","drag","end"),k=0,H,U,G,ie,Z=0;function W(Me){Me.on("mousedown.drag",se).filter(M).on("touchstart.drag",Oe).on("touchmove.drag",ge,UFn).on("touchend.drag touchcancel.drag",Pe).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function se(Me,Be){if(!(ie||!g.call(this,Me,Be))){var rn=ae(this,E.call(this,Me,Be),Me,Be,"mouse");rn&&($g(Me.view).on("mousemove.drag",le,zG).on("mouseup.drag",ee,zG),ydn(Me.view),_7e(Me),G=!1,H=Me.clientX,U=Me.clientY,rn("start",Me))}}function le(Me){if(fI(Me),!G){var Be=Me.clientX-H,rn=Me.clientY-U;G=Be*Be+rn*rn>Z}N.mouse("drag",Me)}function ee(Me){$g(Me.view).on("mousemove.drag mouseup.drag",null),kdn(Me.view,G),fI(Me),N.mouse("end",Me)}function Oe(Me,Be){if(g.call(this,Me,Be)){var rn=Me.changedTouches,ln=E.call(this,Me,Be),xn=rn.length,hn,wt;for(hn=0;hn>8&15|E>>4&240,E>>4&15|E&240,(E&15)<<4|E&15,1):x===8?Qce(E>>24&255,E>>16&255,E>>8&255,(E&255)/255):x===4?Qce(E>>12&15|E>>8&240,E>>8&15|E>>4&240,E>>4&15|E&240,((E&15)<<4|E&15)/255):null):(E=WFn.exec(g))?new kb(E[1],E[2],E[3],1):(E=ZFn.exec(g))?new kb(E[1]*255/100,E[2]*255/100,E[3]*255/100,1):(E=eJn.exec(g))?Qce(E[1],E[2],E[3],E[4]):(E=nJn.exec(g))?Qce(E[1]*255/100,E[2]*255/100,E[3]*255/100,E[4]):(E=tJn.exec(g))?Zhn(E[1],E[2]/100,E[3]/100,1):(E=iJn.exec(g))?Zhn(E[1],E[2]/100,E[3]/100,E[4]):Xhn.hasOwnProperty(g)?Yhn(Xhn[g]):g==="transparent"?new kb(NaN,NaN,NaN,0):null}function Yhn(g){return new kb(g>>16&255,g>>8&255,g&255,1)}function Qce(g,E,x,M){return M<=0&&(g=E=x=NaN),new kb(g,E,x,M)}function uJn(g){return g instanceof eq||(g=EA(g)),g?(g=g.rgb(),new kb(g.r,g.g,g.b,g.opacity)):new kb}function nke(g,E,x,M){return arguments.length===1?uJn(g):new kb(g,E,x,M??1)}function kb(g,E,x,M){this.r=+g,this.g=+E,this.b=+x,this.opacity=+M}pke(kb,nke,Edn(eq,{brighter(g){return g=g==null?mue:Math.pow(mue,g),new kb(this.r*g,this.g*g,this.b*g,this.opacity)},darker(g){return g=g==null?FG:Math.pow(FG,g),new kb(this.r*g,this.g*g,this.b*g,this.opacity)},rgb(){return this},clamp(){return new kb(kA(this.r),kA(this.g),kA(this.b),vue(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Qhn,formatHex:Qhn,formatHex8:oJn,formatRgb:Whn,toString:Whn}));function Qhn(){return`#${yA(this.r)}${yA(this.g)}${yA(this.b)}`}function oJn(){return`#${yA(this.r)}${yA(this.g)}${yA(this.b)}${yA((isNaN(this.opacity)?1:this.opacity)*255)}`}function Whn(){const g=vue(this.opacity);return`${g===1?"rgb(":"rgba("}${kA(this.r)}, ${kA(this.g)}, ${kA(this.b)}${g===1?")":`, ${g})`}`}function vue(g){return isNaN(g)?1:Math.max(0,Math.min(1,g))}function kA(g){return Math.max(0,Math.min(255,Math.round(g)||0))}function yA(g){return g=kA(g),(g<16?"0":"")+g.toString(16)}function Zhn(g,E,x,M){return M<=0?g=E=x=NaN:x<=0||x>=1?g=E=NaN:E<=0&&(g=NaN),new Xm(g,E,x,M)}function Sdn(g){if(g instanceof Xm)return new Xm(g.h,g.s,g.l,g.opacity);if(g instanceof eq||(g=EA(g)),!g)return new Xm;if(g instanceof Xm)return g;g=g.rgb();var E=g.r/255,x=g.g/255,M=g.b/255,N=Math.min(E,x,M),P=Math.max(E,x,M),k=NaN,H=P-N,U=(P+N)/2;return H?(E===P?k=(x-M)/H+(x0&&U<1?0:k,new Xm(k,H,U,g.opacity)}function sJn(g,E,x,M){return arguments.length===1?Sdn(g):new Xm(g,E,x,M??1)}function Xm(g,E,x,M){this.h=+g,this.s=+E,this.l=+x,this.opacity=+M}pke(Xm,sJn,Edn(eq,{brighter(g){return g=g==null?mue:Math.pow(mue,g),new Xm(this.h,this.s,this.l*g,this.opacity)},darker(g){return g=g==null?FG:Math.pow(FG,g),new Xm(this.h,this.s,this.l*g,this.opacity)},rgb(){var g=this.h%360+(this.h<0)*360,E=isNaN(g)||isNaN(this.s)?0:this.s,x=this.l,M=x+(x<.5?x:1-x)*E,N=2*x-M;return new kb(L7e(g>=240?g-240:g+120,N,M),L7e(g,N,M),L7e(g<120?g+240:g-120,N,M),this.opacity)},clamp(){return new Xm(e1n(this.h),Wce(this.s),Wce(this.l),vue(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const g=vue(this.opacity);return`${g===1?"hsl(":"hsla("}${e1n(this.h)}, ${Wce(this.s)*100}%, ${Wce(this.l)*100}%${g===1?")":`, ${g})`}`}}));function e1n(g){return g=(g||0)%360,g<0?g+360:g}function Wce(g){return Math.max(0,Math.min(1,g||0))}function L7e(g,E,x){return(g<60?E+(x-E)*g/60:g<180?x:g<240?E+(x-E)*(240-g)/60:E)*255}const mke=g=>()=>g;function lJn(g,E){return function(x){return g+x*E}}function fJn(g,E,x){return g=Math.pow(g,x),E=Math.pow(E,x)-g,x=1/x,function(M){return Math.pow(g+M*E,x)}}function aJn(g){return(g=+g)==1?xdn:function(E,x){return x-E?fJn(E,x,g):mke(isNaN(E)?x:E)}}function xdn(g,E){var x=E-g;return x?lJn(g,x):mke(isNaN(g)?E:g)}const yue=(function g(E){var x=aJn(E);function M(N,P){var k=x((N=nke(N)).r,(P=nke(P)).r),H=x(N.g,P.g),U=x(N.b,P.b),G=xdn(N.opacity,P.opacity);return function(ie){return N.r=k(ie),N.g=H(ie),N.b=U(ie),N.opacity=G(ie),N+""}}return M.gamma=g,M})(1);function hJn(g,E){E||(E=[]);var x=g?Math.min(E.length,g.length):0,M=E.slice(),N;return function(P){for(N=0;Nx&&(P=E.slice(x,P),H[k]?H[k]+=P:H[++k]=P),(M=M[0])===(N=N[0])?H[k]?H[k]+=N:H[++k]=N:(H[++k]=null,U.push({i:k,x:r5(M,N)})),x=P7e.lastIndex;return x180?ie+=360:ie-G>180&&(G+=360),W.push({i:Z.push(N(Z)+"rotate(",null,M)-2,x:r5(G,ie)})):ie&&Z.push(N(Z)+"rotate("+ie+M)}function H(G,ie,Z,W){G!==ie?W.push({i:Z.push(N(Z)+"skewX(",null,M)-2,x:r5(G,ie)}):ie&&Z.push(N(Z)+"skewX("+ie+M)}function U(G,ie,Z,W,se,le){if(G!==Z||ie!==W){var ee=se.push(N(se)+"scale(",null,",",null,")");le.push({i:ee-4,x:r5(G,Z)},{i:ee-2,x:r5(ie,W)})}else(Z!==1||W!==1)&&se.push(N(se)+"scale("+Z+","+W+")")}return function(G,ie){var Z=[],W=[];return G=g(G),ie=g(ie),P(G.translateX,G.translateY,ie.translateX,ie.translateY,Z,W),k(G.rotate,ie.rotate,Z,W),H(G.skewX,ie.skewX,Z,W),U(G.scaleX,G.scaleY,ie.scaleX,ie.scaleY,Z,W),G=ie=null,function(se){for(var le=-1,ee=W.length,Oe;++le=0&&g._call.call(void 0,E),g=g._next;--dI}function i1n(){SA=(jue=HG.now())+Due,dI=LG=0;try{MJn()}finally{dI=0,CJn(),SA=0}}function TJn(){var g=HG.now(),E=g-jue;E>Cdn&&(Due-=E,jue=g)}function CJn(){for(var g,E=kue,x,M=1/0;E;)E._call?(M>E._time&&(M=E._time),g=E,E=E._next):(x=E._next,E._next=null,E=g?g._next=x:kue=x);PG=g,rke(M)}function rke(g){if(!dI){LG&&(LG=clearTimeout(LG));var E=g-SA;E>24?(g<1/0&&(LG=setTimeout(i1n,g-HG.now()-Due)),DG&&(DG=clearInterval(DG))):(DG||(jue=HG.now(),DG=setInterval(TJn,Cdn)),dI=1,Odn(i1n))}}function r1n(g,E,x){var M=new Eue;return E=E==null?0:+E,M.restart(N=>{M.stop(),g(N+E)},E,x),M}var OJn=Oue("start","end","cancel","interrupt"),NJn=[],Ddn=0,c1n=1,cke=2,due=3,u1n=4,uke=5,bue=6;function Iue(g,E,x,M,N,P){var k=g.__transition;if(!k)g.__transition={};else if(x in k)return;DJn(g,x,{name:E,index:M,group:N,on:OJn,tween:NJn,time:P.time,delay:P.delay,duration:P.duration,ease:P.ease,timer:null,state:Ddn})}function yke(g,E){var x=Qm(g,E);if(x.state>Ddn)throw new Error("too late; already scheduled");return x}function s5(g,E){var x=Qm(g,E);if(x.state>due)throw new Error("too late; already running");return x}function Qm(g,E){var x=g.__transition;if(!x||!(x=x[E]))throw new Error("transition not found");return x}function DJn(g,E,x){var M=g.__transition,N;M[E]=x,x.timer=Ndn(P,0,x.time);function P(G){x.state=c1n,x.timer.restart(k,x.delay,x.time),x.delay<=G&&k(G-x.delay)}function k(G){var ie,Z,W,se;if(x.state!==c1n)return U();for(ie in M)if(se=M[ie],se.name===x.name){if(se.state===due)return r1n(k);se.state===u1n?(se.state=bue,se.timer.stop(),se.on.call("interrupt",g,g.__data__,se.index,se.group),delete M[ie]):+iecke&&M.state=0&&(E=E.slice(0,x)),!E||E==="start"})}function sHn(g,E,x){var M,N,P=oHn(E)?yke:s5;return function(){var k=P(this,g),H=k.on;H!==M&&(N=(M=H).copy()).on(E,x),k.on=N}}function lHn(g,E){var x=this._id;return arguments.length<2?Qm(this.node(),x).on.on(g):this.each(sHn(x,g,E))}function fHn(g){return function(){var E=this.parentNode;for(var x in this.__transition)if(+x!==g)return;E&&E.removeChild(this)}}function aHn(){return this.on("end.remove",fHn(this._id))}function hHn(g){var E=this._name,x=this._id;typeof g!="function"&&(g=gke(g));for(var M=this._groups,N=M.length,P=new Array(N),k=0;k()=>g;function $Hn(g,{sourceEvent:E,target:x,transform:M,dispatch:N}){Object.defineProperties(this,{type:{value:g,enumerable:!0,configurable:!0},sourceEvent:{value:E,enumerable:!0,configurable:!0},target:{value:x,enumerable:!0,configurable:!0},transform:{value:M,enumerable:!0,configurable:!0},_:{value:N}})}function e6(g,E,x){this.k=g,this.x=E,this.y=x}e6.prototype={constructor:e6,scale:function(g){return g===1?this:new e6(this.k*g,this.x,this.y)},translate:function(g,E){return g===0&E===0?this:new e6(this.k,this.x+this.k*g,this.y+this.k*E)},apply:function(g){return[g[0]*this.k+this.x,g[1]*this.k+this.y]},applyX:function(g){return g*this.k+this.x},applyY:function(g){return g*this.k+this.y},invert:function(g){return[(g[0]-this.x)/this.k,(g[1]-this.y)/this.k]},invertX:function(g){return(g-this.x)/this.k},invertY:function(g){return(g-this.y)/this.k},rescaleX:function(g){return g.copy().domain(g.range().map(this.invertX,this).map(g.invert,g))},rescaleY:function(g){return g.copy().domain(g.range().map(this.invertY,this).map(g.invert,g))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var _ue=new e6(1,0,0);Pdn.prototype=e6.prototype;function Pdn(g){for(;!g.__zoom;)if(!(g=g.parentNode))return _ue;return g.__zoom}function $7e(g){g.stopImmediatePropagation()}function IG(g){g.preventDefault(),g.stopImmediatePropagation()}function RHn(g){return(!g.ctrlKey||g.type==="wheel")&&!g.button}function BHn(){var g=this;return g instanceof SVGElement?(g=g.ownerSVGElement||g,g.hasAttribute("viewBox")?(g=g.viewBox.baseVal,[[g.x,g.y],[g.x+g.width,g.y+g.height]]):[[0,0],[g.width.baseVal.value,g.height.baseVal.value]]):[[0,0],[g.clientWidth,g.clientHeight]]}function o1n(){return this.__zoom||_ue}function zHn(g){return-g.deltaY*(g.deltaMode===1?.05:g.deltaMode?1:.002)*(g.ctrlKey?10:1)}function FHn(){return navigator.maxTouchPoints||"ontouchstart"in this}function JHn(g,E,x){var M=g.invertX(E[0][0])-x[0][0],N=g.invertX(E[1][0])-x[1][0],P=g.invertY(E[0][1])-x[0][1],k=g.invertY(E[1][1])-x[1][1];return g.translate(N>M?(M+N)/2:Math.min(0,M)||Math.max(0,N),k>P?(P+k)/2:Math.min(0,P)||Math.max(0,k))}function $dn(){var g=RHn,E=BHn,x=JHn,M=zHn,N=FHn,P=[0,1/0],k=[[-1/0,-1/0],[1/0,1/0]],H=250,U=hue,G=Oue("start","zoom","end"),ie,Z,W,se=500,le=150,ee=0,Oe=10;function ge(Fe){Fe.property("__zoom",o1n).on("wheel.zoom",xn,{passive:!1}).on("mousedown.zoom",hn).on("dblclick.zoom",wt).filter(N).on("touchstart.zoom",Cn).on("touchmove.zoom",Qn).on("touchend.zoom touchcancel.zoom",Y).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}ge.transform=function(Fe,mn,Ae,Ze){var sn=Fe.selection?Fe.selection():Fe;sn.property("__zoom",o1n),Fe!==sn?Be(Fe,mn,Ae,Ze):sn.interrupt().each(function(){rn(this,arguments).event(Ze).start().zoom(null,typeof mn=="function"?mn.apply(this,arguments):mn).end()})},ge.scaleBy=function(Fe,mn,Ae,Ze){ge.scaleTo(Fe,function(){var sn=this.__zoom.k,Sn=typeof mn=="function"?mn.apply(this,arguments):mn;return sn*Sn},Ae,Ze)},ge.scaleTo=function(Fe,mn,Ae,Ze){ge.transform(Fe,function(){var sn=E.apply(this,arguments),Sn=this.__zoom,kn=Ae==null?Me(sn):typeof Ae=="function"?Ae.apply(this,arguments):Ae,xe=Sn.invert(kn),un=typeof mn=="function"?mn.apply(this,arguments):mn;return x(ae(Pe(Sn,un),kn,xe),sn,k)},Ae,Ze)},ge.translateBy=function(Fe,mn,Ae,Ze){ge.transform(Fe,function(){return x(this.__zoom.translate(typeof mn=="function"?mn.apply(this,arguments):mn,typeof Ae=="function"?Ae.apply(this,arguments):Ae),E.apply(this,arguments),k)},null,Ze)},ge.translateTo=function(Fe,mn,Ae,Ze,sn){ge.transform(Fe,function(){var Sn=E.apply(this,arguments),kn=this.__zoom,xe=Ze==null?Me(Sn):typeof Ze=="function"?Ze.apply(this,arguments):Ze;return x(_ue.translate(xe[0],xe[1]).scale(kn.k).translate(typeof mn=="function"?-mn.apply(this,arguments):-mn,typeof Ae=="function"?-Ae.apply(this,arguments):-Ae),Sn,k)},Ze,sn)};function Pe(Fe,mn){return mn=Math.max(P[0],Math.min(P[1],mn)),mn===Fe.k?Fe:new e6(mn,Fe.x,Fe.y)}function ae(Fe,mn,Ae){var Ze=mn[0]-Ae[0]*Fe.k,sn=mn[1]-Ae[1]*Fe.k;return Ze===Fe.x&&sn===Fe.y?Fe:new e6(Fe.k,Ze,sn)}function Me(Fe){return[(+Fe[0][0]+ +Fe[1][0])/2,(+Fe[0][1]+ +Fe[1][1])/2]}function Be(Fe,mn,Ae,Ze){Fe.on("start.zoom",function(){rn(this,arguments).event(Ze).start()}).on("interrupt.zoom end.zoom",function(){rn(this,arguments).event(Ze).end()}).tween("zoom",function(){var sn=this,Sn=arguments,kn=rn(sn,Sn).event(Ze),xe=E.apply(sn,Sn),un=Ae==null?Me(xe):typeof Ae=="function"?Ae.apply(sn,Sn):Ae,rt=Math.max(xe[1][0]-xe[0][0],xe[1][1]-xe[0][1]),lt=sn.__zoom,Bt=typeof mn=="function"?mn.apply(sn,Sn):mn,hi=U(lt.invert(un).concat(rt/lt.k),Bt.invert(un).concat(rt/Bt.k));return function(Gt){if(Gt===1)Gt=Bt;else{var At=hi(Gt),st=rt/At[2];Gt=new e6(st,un[0]-At[0]*st,un[1]-At[1]*st)}kn.zoom(null,Gt)}})}function rn(Fe,mn,Ae){return!Ae&&Fe.__zooming||new ln(Fe,mn)}function ln(Fe,mn){this.that=Fe,this.args=mn,this.active=0,this.sourceEvent=null,this.extent=E.apply(Fe,mn),this.taps=0}ln.prototype={event:function(Fe){return Fe&&(this.sourceEvent=Fe),this},start:function(){return++this.active===1&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(Fe,mn){return this.mouse&&Fe!=="mouse"&&(this.mouse[1]=mn.invert(this.mouse[0])),this.touch0&&Fe!=="touch"&&(this.touch0[1]=mn.invert(this.touch0[0])),this.touch1&&Fe!=="touch"&&(this.touch1[1]=mn.invert(this.touch1[0])),this.that.__zoom=mn,this.emit("zoom"),this},end:function(){return--this.active===0&&(delete this.that.__zooming,this.emit("end")),this},emit:function(Fe){var mn=$g(this.that).datum();G.call(Fe,this.that,new $Hn(Fe,{sourceEvent:this.sourceEvent,target:ge,transform:this.that.__zoom,dispatch:G}),mn)}};function xn(Fe,...mn){if(!g.apply(this,arguments))return;var Ae=rn(this,mn).event(Fe),Ze=this.__zoom,sn=Math.max(P[0],Math.min(P[1],Ze.k*Math.pow(2,M.apply(this,arguments)))),Sn=Um(Fe);if(Ae.wheel)(Ae.mouse[0][0]!==Sn[0]||Ae.mouse[0][1]!==Sn[1])&&(Ae.mouse[1]=Ze.invert(Ae.mouse[0]=Sn)),clearTimeout(Ae.wheel);else{if(Ze.k===sn)return;Ae.mouse=[Sn,Ze.invert(Sn)],gue(this),Ae.start()}IG(Fe),Ae.wheel=setTimeout(kn,le),Ae.zoom("mouse",x(ae(Pe(Ze,sn),Ae.mouse[0],Ae.mouse[1]),Ae.extent,k));function kn(){Ae.wheel=null,Ae.end()}}function hn(Fe,...mn){if(W||!g.apply(this,arguments))return;var Ae=Fe.currentTarget,Ze=rn(this,mn,!0).event(Fe),sn=$g(Fe.view).on("mousemove.zoom",un,!0).on("mouseup.zoom",rt,!0),Sn=Um(Fe,Ae),kn=Fe.clientX,xe=Fe.clientY;ydn(Fe.view),$7e(Fe),Ze.mouse=[Sn,this.__zoom.invert(Sn)],gue(this),Ze.start();function un(lt){if(IG(lt),!Ze.moved){var Bt=lt.clientX-kn,hi=lt.clientY-xe;Ze.moved=Bt*Bt+hi*hi>ee}Ze.event(lt).zoom("mouse",x(ae(Ze.that.__zoom,Ze.mouse[0]=Um(lt,Ae),Ze.mouse[1]),Ze.extent,k))}function rt(lt){sn.on("mousemove.zoom mouseup.zoom",null),kdn(lt.view,Ze.moved),IG(lt),Ze.event(lt).end()}}function wt(Fe,...mn){if(g.apply(this,arguments)){var Ae=this.__zoom,Ze=Um(Fe.changedTouches?Fe.changedTouches[0]:Fe,this),sn=Ae.invert(Ze),Sn=Ae.k*(Fe.shiftKey?.5:2),kn=x(ae(Pe(Ae,Sn),Ze,sn),E.apply(this,mn),k);IG(Fe),H>0?$g(this).transition().duration(H).call(Be,kn,Ze,Fe):$g(this).call(ge.transform,kn,Ze,Fe)}}function Cn(Fe,...mn){if(g.apply(this,arguments)){var Ae=Fe.touches,Ze=Ae.length,sn=rn(this,mn,Fe.changedTouches.length===Ze).event(Fe),Sn,kn,xe,un;for($7e(Fe),kn=0;kn"[React Flow]: Seems like you have not used zustand provider as an ancestor. Help: https://reactflow.dev/error#001",error002:()=>"It looks like you've created a new nodeTypes or edgeTypes object. If this wasn't on purpose please define the nodeTypes/edgeTypes outside of the component or memoize them.",error003:g=>`Node type "${g}" not found. Using fallback type "default".`,error004:()=>"The React Flow parent container needs a width and a height to render the graph.",error005:()=>"Only child nodes can use a parent extent.",error006:()=>"Can't create edge. An edge needs a source and a target.",error007:g=>`The old edge with id=${g} does not exist.`,error009:g=>`Marker type "${g}" doesn't exist.`,error008:(g,{id:E,sourceHandle:x,targetHandle:M})=>`Couldn't create edge for ${g} handle id: "${g==="source"?x:M}", edge id: ${E}.`,error010:()=>"Handle: No node id found. Make sure to only use a Handle inside a custom Node.",error011:g=>`Edge type "${g}" not found. Using fallback type "default".`,error012:g=>`Node with id "${g}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`,error013:(g="react")=>`It seems that you haven't loaded the styles. Please import '@xyflow/${g}/dist/style.css' or base.css to make sure everything is working properly.`,error014:()=>"useNodeConnections: No node ID found. Call useNodeConnections inside a custom Node or provide a node ID.",error015:()=>"It seems that you are trying to drag a node that is not initialized. Please use onNodesChange as explained in the docs."},GG=[[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY],[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY]],Rdn=["Enter"," ","Escape"],Bdn={"node.a11yDescription.default":"Press enter or space to select a node. Press delete to remove it and escape to cancel.","node.a11yDescription.keyboardDisabled":"Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.","node.a11yDescription.ariaLiveMessage":({direction:g,x:E,y:x})=>`Moved selected node ${g}. New position, x: ${E}, y: ${x}`,"edge.a11yDescription.default":"Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.","controls.ariaLabel":"Control Panel","controls.zoomIn.ariaLabel":"Zoom In","controls.zoomOut.ariaLabel":"Zoom Out","controls.fitView.ariaLabel":"Fit View","controls.interactive.ariaLabel":"Toggle Interactivity","minimap.ariaLabel":"Mini Map","handle.ariaLabel":"Handle"};var bI;(function(g){g.Strict="strict",g.Loose="loose"})(bI||(bI={}));var jA;(function(g){g.Free="free",g.Vertical="vertical",g.Horizontal="horizontal"})(jA||(jA={}));var qG;(function(g){g.Partial="partial",g.Full="full"})(qG||(qG={}));const zdn={inProgress:!1,isValid:null,from:null,fromHandle:null,fromPosition:null,fromNode:null,to:null,toHandle:null,toPosition:null,toNode:null,pointer:null};var W7;(function(g){g.Bezier="default",g.Straight="straight",g.Step="step",g.SmoothStep="smoothstep",g.SimpleBezier="simplebezier"})(W7||(W7={}));var UG;(function(g){g.Arrow="arrow",g.ArrowClosed="arrowclosed"})(UG||(UG={}));var cr;(function(g){g.Left="left",g.Top="top",g.Right="right",g.Bottom="bottom"})(cr||(cr={}));const s1n={[cr.Left]:cr.Right,[cr.Right]:cr.Left,[cr.Top]:cr.Bottom,[cr.Bottom]:cr.Top};function Fdn(g){return g===null?null:g?"valid":"invalid"}const Jdn=g=>"id"in g&&"source"in g&&"target"in g,HHn=g=>"id"in g&&"position"in g&&!("source"in g)&&!("target"in g),jke=g=>"id"in g&&"internals"in g&&!("source"in g)&&!("target"in g),nq=(g,E=[0,0])=>{const{width:x,height:M}=i6(g),N=g.origin??E,P=x*N[0],k=M*N[1];return{x:g.position.x-P,y:g.position.y-k}},GHn=(g,E={nodeOrigin:[0,0]})=>{if(g.length===0)return{x:0,y:0,width:0,height:0};const x=g.reduce((M,N)=>{const P=typeof N=="string";let k=!E.nodeLookup&&!P?N:void 0;E.nodeLookup&&(k=P?E.nodeLookup.get(N):jke(N)?N:E.nodeLookup.get(N.id));const H=k?Sue(k,E.nodeOrigin):{x:0,y:0,x2:0,y2:0};return Lue(M,H)},{x:1/0,y:1/0,x2:-1/0,y2:-1/0});return Pue(x)},tq=(g,E={})=>{let x={x:1/0,y:1/0,x2:-1/0,y2:-1/0},M=!1;return g.forEach(N=>{(E.filter===void 0||E.filter(N))&&(x=Lue(x,Sue(N)),M=!0)}),M?Pue(x):{x:0,y:0,width:0,height:0}},Eke=(g,E,[x,M,N]=[0,0,1],P=!1,k=!1)=>{const H={...rq(E,[x,M,N]),width:E.width/N,height:E.height/N},U=[];for(const G of g.values()){const{measured:ie,selectable:Z=!0,hidden:W=!1}=G;if(k&&!Z||W)continue;const se=ie.width??G.width??G.initialWidth??null,le=ie.height??G.height??G.initialHeight??null,ee=XG(H,wI(G)),Oe=(se??0)*(le??0),ge=P&&ee>0;(!G.internals.handleBounds||ge||ee>=Oe||G.dragging)&&U.push(G)}return U},qHn=(g,E)=>{const x=new Set;return g.forEach(M=>{x.add(M.id)}),E.filter(M=>x.has(M.source)||x.has(M.target))};function UHn(g,E){const x=new Map,M=E?.nodes?new Set(E.nodes.map(N=>N.id)):null;return g.forEach(N=>{N.measured.width&&N.measured.height&&(E?.includeHiddenNodes||!N.hidden)&&(!M||M.has(N.id))&&x.set(N.id,N)}),x}async function XHn({nodes:g,width:E,height:x,panZoom:M,minZoom:N,maxZoom:P},k){if(g.size===0)return Promise.resolve(!0);const H=UHn(g,k),U=tq(H),G=Ske(U,E,x,k?.minZoom??N,k?.maxZoom??P,k?.padding??.1);return await M.setViewport(G,{duration:k?.duration,ease:k?.ease,interpolate:k?.interpolate}),Promise.resolve(!0)}function Hdn({nodeId:g,nextPosition:E,nodeLookup:x,nodeOrigin:M=[0,0],nodeExtent:N,onError:P}){const k=x.get(g),H=k.parentId?x.get(k.parentId):void 0,{x:U,y:G}=H?H.internals.positionAbsolute:{x:0,y:0},ie=k.origin??M;let Z=k.extent||N;if(k.extent==="parent"&&!k.expandParent)if(!H)P?.("005",u5.error005());else{const se=H.measured.width,le=H.measured.height;se&&le&&(Z=[[U,G],[U+se,G+le]])}else H&&pI(k.extent)&&(Z=[[k.extent[0][0]+U,k.extent[0][1]+G],[k.extent[1][0]+U,k.extent[1][1]+G]]);const W=pI(Z)?xA(E,Z,k.measured):E;return(k.measured.width===void 0||k.measured.height===void 0)&&P?.("015",u5.error015()),{position:{x:W.x-U+(k.measured.width??0)*ie[0],y:W.y-G+(k.measured.height??0)*ie[1]},positionAbsolute:W}}async function KHn({nodesToRemove:g=[],edgesToRemove:E=[],nodes:x,edges:M,onBeforeDelete:N}){const P=new Set(g.map(W=>W.id)),k=[];for(const W of x){if(W.deletable===!1)continue;const se=P.has(W.id),le=!se&&W.parentId&&k.find(ee=>ee.id===W.parentId);(se||le)&&k.push(W)}const H=new Set(E.map(W=>W.id)),U=M.filter(W=>W.deletable!==!1),ie=qHn(k,U);for(const W of U)H.has(W.id)&&!ie.find(le=>le.id===W.id)&&ie.push(W);if(!N)return{edges:ie,nodes:k};const Z=await N({nodes:k,edges:ie});return typeof Z=="boolean"?Z?{edges:ie,nodes:k}:{edges:[],nodes:[]}:Z}const gI=(g,E=0,x=1)=>Math.min(Math.max(g,E),x),xA=(g={x:0,y:0},E,x)=>({x:gI(g.x,E[0][0],E[1][0]-(x?.width??0)),y:gI(g.y,E[0][1],E[1][1]-(x?.height??0))});function Gdn(g,E,x){const{width:M,height:N}=i6(x),{x:P,y:k}=x.internals.positionAbsolute;return xA(g,[[P,k],[P+M,k+N]],E)}const l1n=(g,E,x)=>gx?-gI(Math.abs(g-x),1,E)/E:0,qdn=(g,E,x=15,M=40)=>{const N=l1n(g.x,M,E.width-M)*x,P=l1n(g.y,M,E.height-M)*x;return[N,P]},Lue=(g,E)=>({x:Math.min(g.x,E.x),y:Math.min(g.y,E.y),x2:Math.max(g.x2,E.x2),y2:Math.max(g.y2,E.y2)}),oke=({x:g,y:E,width:x,height:M})=>({x:g,y:E,x2:g+x,y2:E+M}),Pue=({x:g,y:E,x2:x,y2:M})=>({x:g,y:E,width:x-g,height:M-E}),wI=(g,E=[0,0])=>{const{x,y:M}=jke(g)?g.internals.positionAbsolute:nq(g,E);return{x,y:M,width:g.measured?.width??g.width??g.initialWidth??0,height:g.measured?.height??g.height??g.initialHeight??0}},Sue=(g,E=[0,0])=>{const{x,y:M}=jke(g)?g.internals.positionAbsolute:nq(g,E);return{x,y:M,x2:x+(g.measured?.width??g.width??g.initialWidth??0),y2:M+(g.measured?.height??g.height??g.initialHeight??0)}},Udn=(g,E)=>Pue(Lue(oke(g),oke(E))),XG=(g,E)=>{const x=Math.max(0,Math.min(g.x+g.width,E.x+E.width)-Math.max(g.x,E.x)),M=Math.max(0,Math.min(g.y+g.height,E.y+E.height)-Math.max(g.y,E.y));return Math.ceil(x*M)},f1n=g=>Km(g.width)&&Km(g.height)&&Km(g.x)&&Km(g.y),Km=g=>!isNaN(g)&&isFinite(g),VHn=(g,E)=>{},iq=(g,E=[1,1])=>({x:E[0]*Math.round(g.x/E[0]),y:E[1]*Math.round(g.y/E[1])}),rq=({x:g,y:E},[x,M,N],P=!1,k=[1,1])=>{const H={x:(g-x)/N,y:(E-M)/N};return P?iq(H,k):H},xue=({x:g,y:E},[x,M,N])=>({x:g*N+x,y:E*N+M});function uI(g,E){if(typeof g=="number")return Math.floor((E-E/(1+g))*.5);if(typeof g=="string"&&g.endsWith("px")){const x=parseFloat(g);if(!Number.isNaN(x))return Math.floor(x)}if(typeof g=="string"&&g.endsWith("%")){const x=parseFloat(g);if(!Number.isNaN(x))return Math.floor(E*x*.01)}return console.error(`[React Flow] The padding value "${g}" is invalid. Please provide a number or a string with a valid unit (px or %).`),0}function YHn(g,E,x){if(typeof g=="string"||typeof g=="number"){const M=uI(g,x),N=uI(g,E);return{top:M,right:N,bottom:M,left:N,x:N*2,y:M*2}}if(typeof g=="object"){const M=uI(g.top??g.y??0,x),N=uI(g.bottom??g.y??0,x),P=uI(g.left??g.x??0,E),k=uI(g.right??g.x??0,E);return{top:M,right:k,bottom:N,left:P,x:P+k,y:M+N}}return{top:0,right:0,bottom:0,left:0,x:0,y:0}}function QHn(g,E,x,M,N,P){const{x:k,y:H}=xue(g,[E,x,M]),{x:U,y:G}=xue({x:g.x+g.width,y:g.y+g.height},[E,x,M]),ie=N-U,Z=P-G;return{left:Math.floor(k),top:Math.floor(H),right:Math.floor(ie),bottom:Math.floor(Z)}}const Ske=(g,E,x,M,N,P)=>{const k=YHn(P,E,x),H=(E-k.x)/g.width,U=(x-k.y)/g.height,G=Math.min(H,U),ie=gI(G,M,N),Z=g.x+g.width/2,W=g.y+g.height/2,se=E/2-Z*ie,le=x/2-W*ie,ee=QHn(g,se,le,ie,E,x),Oe={left:Math.min(ee.left-k.left,0),top:Math.min(ee.top-k.top,0),right:Math.min(ee.right-k.right,0),bottom:Math.min(ee.bottom-k.bottom,0)};return{x:se-Oe.left+Oe.right,y:le-Oe.top+Oe.bottom,zoom:ie}},KG=()=>typeof navigator<"u"&&navigator?.userAgent?.indexOf("Mac")>=0;function pI(g){return g!=null&&g!=="parent"}function i6(g){return{width:g.measured?.width??g.width??g.initialWidth??0,height:g.measured?.height??g.height??g.initialHeight??0}}function Xdn(g){return(g.measured?.width??g.width??g.initialWidth)!==void 0&&(g.measured?.height??g.height??g.initialHeight)!==void 0}function Kdn(g,E={width:0,height:0},x,M,N){const P={...g},k=M.get(x);if(k){const H=k.origin||N;P.x+=k.internals.positionAbsolute.x-(E.width??0)*H[0],P.y+=k.internals.positionAbsolute.y-(E.height??0)*H[1]}return P}function a1n(g,E){if(g.size!==E.size)return!1;for(const x of g)if(!E.has(x))return!1;return!0}function WHn(){let g,E;return{promise:new Promise((M,N)=>{g=M,E=N}),resolve:g,reject:E}}function ZHn(g){return{...Bdn,...g||{}}}function BG(g,{snapGrid:E=[0,0],snapToGrid:x=!1,transform:M,containerBounds:N}){const{x:P,y:k}=Vm(g),H=rq({x:P-(N?.left??0),y:k-(N?.top??0)},M),{x:U,y:G}=x?iq(H,E):H;return{xSnapped:U,ySnapped:G,...H}}const xke=g=>({width:g.offsetWidth,height:g.offsetHeight}),Vdn=g=>g?.getRootNode?.()||window?.document,eGn=["INPUT","SELECT","TEXTAREA"];function Ydn(g){const E=g.composedPath?.()?.[0]||g.target;return E?.nodeType!==1?!1:eGn.includes(E.nodeName)||E.hasAttribute("contenteditable")||!!E.closest(".nokey")}const Qdn=g=>"clientX"in g,Vm=(g,E)=>{const x=Qdn(g),M=x?g.clientX:g.touches?.[0].clientX,N=x?g.clientY:g.touches?.[0].clientY;return{x:M-(E?.left??0),y:N-(E?.top??0)}},h1n=(g,E,x,M,N)=>{const P=E.querySelectorAll(`.${g}`);return!P||!P.length?null:Array.from(P).map(k=>{const H=k.getBoundingClientRect();return{id:k.getAttribute("data-handleid"),type:g,nodeId:N,position:k.getAttribute("data-handlepos"),x:(H.left-x.left)/M,y:(H.top-x.top)/M,...xke(k)}})};function Wdn({sourceX:g,sourceY:E,targetX:x,targetY:M,sourceControlX:N,sourceControlY:P,targetControlX:k,targetControlY:H}){const U=g*.125+N*.375+k*.375+x*.125,G=E*.125+P*.375+H*.375+M*.125,ie=Math.abs(U-g),Z=Math.abs(G-E);return[U,G,ie,Z]}function nue(g,E){return g>=0?.5*g:E*25*Math.sqrt(-g)}function d1n({pos:g,x1:E,y1:x,x2:M,y2:N,c:P}){switch(g){case cr.Left:return[E-nue(E-M,P),x];case cr.Right:return[E+nue(M-E,P),x];case cr.Top:return[E,x-nue(x-N,P)];case cr.Bottom:return[E,x+nue(N-x,P)]}}function Zdn({sourceX:g,sourceY:E,sourcePosition:x=cr.Bottom,targetX:M,targetY:N,targetPosition:P=cr.Top,curvature:k=.25}){const[H,U]=d1n({pos:x,x1:g,y1:E,x2:M,y2:N,c:k}),[G,ie]=d1n({pos:P,x1:M,y1:N,x2:g,y2:E,c:k}),[Z,W,se,le]=Wdn({sourceX:g,sourceY:E,targetX:M,targetY:N,sourceControlX:H,sourceControlY:U,targetControlX:G,targetControlY:ie});return[`M${g},${E} C${H},${U} ${G},${ie} ${M},${N}`,Z,W,se,le]}function e0n({sourceX:g,sourceY:E,targetX:x,targetY:M}){const N=Math.abs(x-g)/2,P=x0}const iGn=({source:g,sourceHandle:E,target:x,targetHandle:M})=>`xy-edge__${g}${E||""}-${x}${M||""}`,rGn=(g,E)=>E.some(x=>x.source===g.source&&x.target===g.target&&(x.sourceHandle===g.sourceHandle||!x.sourceHandle&&!g.sourceHandle)&&(x.targetHandle===g.targetHandle||!x.targetHandle&&!g.targetHandle)),cGn=(g,E,x={})=>{if(!g.source||!g.target)return E;const M=x.getEdgeId||iGn;let N;return Jdn(g)?N={...g}:N={...g,id:M(g)},rGn(N,E)?E:(N.sourceHandle===null&&delete N.sourceHandle,N.targetHandle===null&&delete N.targetHandle,E.concat(N))};function n0n({sourceX:g,sourceY:E,targetX:x,targetY:M}){const[N,P,k,H]=e0n({sourceX:g,sourceY:E,targetX:x,targetY:M});return[`M ${g},${E}L ${x},${M}`,N,P,k,H]}const b1n={[cr.Left]:{x:-1,y:0},[cr.Right]:{x:1,y:0},[cr.Top]:{x:0,y:-1},[cr.Bottom]:{x:0,y:1}},uGn=({source:g,sourcePosition:E=cr.Bottom,target:x})=>E===cr.Left||E===cr.Right?g.xMath.sqrt(Math.pow(E.x-g.x,2)+Math.pow(E.y-g.y,2));function oGn({source:g,sourcePosition:E=cr.Bottom,target:x,targetPosition:M=cr.Top,center:N,offset:P,stepPosition:k}){const H=b1n[E],U=b1n[M],G={x:g.x+H.x*P,y:g.y+H.y*P},ie={x:x.x+U.x*P,y:x.y+U.y*P},Z=uGn({source:G,sourcePosition:E,target:ie}),W=Z.x!==0?"x":"y",se=Z[W];let le=[],ee,Oe;const ge={x:0,y:0},Pe={x:0,y:0},[,,ae,Me]=e0n({sourceX:g.x,sourceY:g.y,targetX:x.x,targetY:x.y});if(H[W]*U[W]===-1){W==="x"?(ee=N.x??G.x+(ie.x-G.x)*k,Oe=N.y??(G.y+ie.y)/2):(ee=N.x??(G.x+ie.x)/2,Oe=N.y??G.y+(ie.y-G.y)*k);const xn=[{x:ee,y:G.y},{x:ee,y:ie.y}],hn=[{x:G.x,y:Oe},{x:ie.x,y:Oe}];H[W]===se?le=W==="x"?xn:hn:le=W==="x"?hn:xn}else{const xn=[{x:G.x,y:ie.y}],hn=[{x:ie.x,y:G.y}];if(W==="x"?le=H.x===se?hn:xn:le=H.y===se?xn:hn,E===M){const Fe=Math.abs(g[W]-x[W]);if(Fe<=P){const mn=Math.min(P-1,P-Fe);H[W]===se?ge[W]=(G[W]>g[W]?-1:1)*mn:Pe[W]=(ie[W]>x[W]?-1:1)*mn}}if(E!==M){const Fe=W==="x"?"y":"x",mn=H[W]===U[Fe],Ae=G[Fe]>ie[Fe],Ze=G[Fe]=Y?(ee=(wt.x+Cn.x)/2,Oe=le[0].y):(ee=le[0].x,Oe=(wt.y+Cn.y)/2)}const Be={x:G.x+ge.x,y:G.y+ge.y},rn={x:ie.x+Pe.x,y:ie.y+Pe.y};return[[g,...Be.x!==le[0].x||Be.y!==le[0].y?[Be]:[],...le,...rn.x!==le[le.length-1].x||rn.y!==le[le.length-1].y?[rn]:[],x],ee,Oe,ae,Me]}function sGn(g,E,x,M){const N=Math.min(g1n(g,E)/2,g1n(E,x)/2,M),{x:P,y:k}=E;if(g.x===P&&P===x.x||g.y===k&&k===x.y)return`L${P} ${k}`;if(g.y===k){const G=g.xx.id===E):g[0])||null}function ske(g,E){return g?typeof g=="string"?g:`${E?`${E}__`:""}${Object.keys(g).sort().map(M=>`${M}=${g[M]}`).join("&")}`:""}function fGn(g,{id:E,defaultColor:x,defaultMarkerStart:M,defaultMarkerEnd:N}){const P=new Set;return g.reduce((k,H)=>([H.markerStart||M,H.markerEnd||N].forEach(U=>{if(U&&typeof U=="object"){const G=ske(U,E);P.has(G)||(k.push({id:G,color:U.color||x,...U}),P.add(G))}}),k),[]).sort((k,H)=>k.id.localeCompare(H.id))}const t0n=1e3,aGn=10,Ake={nodeOrigin:[0,0],nodeExtent:GG,elevateNodesOnSelect:!0,zIndexMode:"basic",defaults:{}},hGn={...Ake,checkEquality:!0};function Mke(g,E){const x={...g};for(const M in E)E[M]!==void 0&&(x[M]=E[M]);return x}function dGn(g,E,x){const M=Mke(Ake,x);for(const N of g.values())if(N.parentId)Cke(N,g,E,M);else{const P=nq(N,M.nodeOrigin),k=pI(N.extent)?N.extent:M.nodeExtent,H=xA(P,k,i6(N));N.internals.positionAbsolute=H}}function bGn(g,E){if(!g.handles)return g.measured?E?.internals.handleBounds:void 0;const x=[],M=[];for(const N of g.handles){const P={id:N.id,width:N.width??1,height:N.height??1,nodeId:g.id,x:N.x,y:N.y,position:N.position,type:N.type};N.type==="source"?x.push(P):N.type==="target"&&M.push(P)}return{source:x,target:M}}function Tke(g){return g==="manual"}function lke(g,E,x,M={}){const N=Mke(hGn,M),P={i:0},k=new Map(E),H=N?.elevateNodesOnSelect&&!Tke(N.zIndexMode)?t0n:0;let U=g.length>0,G=!1;E.clear(),x.clear();for(const ie of g){let Z=k.get(ie.id);if(N.checkEquality&&ie===Z?.internals.userNode)E.set(ie.id,Z);else{const W=nq(ie,N.nodeOrigin),se=pI(ie.extent)?ie.extent:N.nodeExtent,le=xA(W,se,i6(ie));Z={...N.defaults,...ie,measured:{width:ie.measured?.width,height:ie.measured?.height},internals:{positionAbsolute:le,handleBounds:bGn(ie,Z),z:i0n(ie,H,N.zIndexMode),userNode:ie}},E.set(ie.id,Z)}(Z.measured===void 0||Z.measured.width===void 0||Z.measured.height===void 0)&&!Z.hidden&&(U=!1),ie.parentId&&Cke(Z,E,x,M,P),G||=ie.selected??!1}return{nodesInitialized:U,hasSelectedNodes:G}}function gGn(g,E){if(!g.parentId)return;const x=E.get(g.parentId);x?x.set(g.id,g):E.set(g.parentId,new Map([[g.id,g]]))}function Cke(g,E,x,M,N){const{elevateNodesOnSelect:P,nodeOrigin:k,nodeExtent:H,zIndexMode:U}=Mke(Ake,M),G=g.parentId,ie=E.get(G);if(!ie){console.warn(`Parent node ${G} not found. Please make sure that parent nodes are in front of their child nodes in the nodes array.`);return}gGn(g,x),N&&!ie.parentId&&ie.internals.rootParentIndex===void 0&&U==="auto"&&(ie.internals.rootParentIndex=++N.i,ie.internals.z=ie.internals.z+N.i*aGn),N&&ie.internals.rootParentIndex!==void 0&&(N.i=ie.internals.rootParentIndex);const Z=P&&!Tke(U)?t0n:0,{x:W,y:se,z:le}=wGn(g,ie,k,H,Z,U),{positionAbsolute:ee}=g.internals,Oe=W!==ee.x||se!==ee.y;(Oe||le!==g.internals.z)&&E.set(g.id,{...g,internals:{...g.internals,positionAbsolute:Oe?{x:W,y:se}:ee,z:le}})}function i0n(g,E,x){const M=Km(g.zIndex)?g.zIndex:0;return Tke(x)?M:M+(g.selected?E:0)}function wGn(g,E,x,M,N,P){const{x:k,y:H}=E.internals.positionAbsolute,U=i6(g),G=nq(g,x),ie=pI(g.extent)?xA(G,g.extent,U):G;let Z=xA({x:k+ie.x,y:H+ie.y},M,U);g.extent==="parent"&&(Z=Gdn(Z,U,E));const W=i0n(g,N,P),se=E.internals.z??0;return{x:Z.x,y:Z.y,z:se>=W?se+1:W}}function Oke(g,E,x,M=[0,0]){const N=[],P=new Map;for(const k of g){const H=E.get(k.parentId);if(!H)continue;const U=P.get(k.parentId)?.expandedRect??wI(H),G=Udn(U,k.rect);P.set(k.parentId,{expandedRect:G,parent:H})}return P.size>0&&P.forEach(({expandedRect:k,parent:H},U)=>{const G=H.internals.positionAbsolute,ie=i6(H),Z=H.origin??M,W=k.x0||se>0||Oe||ge)&&(N.push({id:U,type:"position",position:{x:H.position.x-W+Oe,y:H.position.y-se+ge}}),x.get(U)?.forEach(Pe=>{g.some(ae=>ae.id===Pe.id)||N.push({id:Pe.id,type:"position",position:{x:Pe.position.x+W,y:Pe.position.y+se}})})),(ie.width0){const se=Oke(W,E,x,N);G.push(...se)}return{changes:G,updatedInternals:U}}async function mGn({delta:g,panZoom:E,transform:x,translateExtent:M,width:N,height:P}){if(!E||!g.x&&!g.y)return Promise.resolve(!1);const k=await E.setViewportConstrained({x:x[0]+g.x,y:x[1]+g.y,zoom:x[2]},[[0,0],[N,P]],M),H=!!k&&(k.x!==x[0]||k.y!==x[1]||k.k!==x[2]);return Promise.resolve(H)}function v1n(g,E,x,M,N,P){let k=N;const H=M.get(k)||new Map;M.set(k,H.set(x,E)),k=`${N}-${g}`;const U=M.get(k)||new Map;if(M.set(k,U.set(x,E)),P){k=`${N}-${g}-${P}`;const G=M.get(k)||new Map;M.set(k,G.set(x,E))}}function r0n(g,E,x){g.clear(),E.clear();for(const M of x){const{source:N,target:P,sourceHandle:k=null,targetHandle:H=null}=M,U={edgeId:M.id,source:N,target:P,sourceHandle:k,targetHandle:H},G=`${N}-${k}--${P}-${H}`,ie=`${P}-${H}--${N}-${k}`;v1n("source",U,ie,g,N,k),v1n("target",U,G,g,P,H),E.set(M.id,M)}}function c0n(g,E){if(!g.parentId)return!1;const x=E.get(g.parentId);return x?x.selected?!0:c0n(x,E):!1}function y1n(g,E,x){let M=g;do{if(M?.matches?.(E))return!0;if(M===x)return!1;M=M?.parentElement}while(M);return!1}function vGn(g,E,x,M){const N=new Map;for(const[P,k]of g)if((k.selected||k.id===M)&&(!k.parentId||!c0n(k,g))&&(k.draggable||E&&typeof k.draggable>"u")){const H=g.get(P);H&&N.set(P,{id:P,position:H.position||{x:0,y:0},distance:{x:x.x-H.internals.positionAbsolute.x,y:x.y-H.internals.positionAbsolute.y},extent:H.extent,parentId:H.parentId,origin:H.origin,expandParent:H.expandParent,internals:{positionAbsolute:H.internals.positionAbsolute||{x:0,y:0}},measured:{width:H.measured.width??0,height:H.measured.height??0}})}return N}function R7e({nodeId:g,dragItems:E,nodeLookup:x,dragging:M=!0}){const N=[];for(const[k,H]of E){const U=x.get(k)?.internals.userNode;U&&N.push({...U,position:H.position,dragging:M})}if(!g)return[N[0],N];const P=x.get(g)?.internals.userNode;return[P?{...P,position:E.get(g)?.position||P.position,dragging:M}:N[0],N]}function yGn({dragItems:g,snapGrid:E,x,y:M}){const N=g.values().next().value;if(!N)return null;const P={x:x-N.distance.x,y:M-N.distance.y},k=iq(P,E);return{x:k.x-P.x,y:k.y-P.y}}function kGn({onNodeMouseDown:g,getStoreItems:E,onDragStart:x,onDrag:M,onDragStop:N}){let P={x:null,y:null},k=0,H=new Map,U=!1,G={x:0,y:0},ie=null,Z=!1,W=null,se=!1,le=!1,ee=null;function Oe({noDragClassName:Pe,handleSelector:ae,domNode:Me,isSelectable:Be,nodeId:rn,nodeClickDistance:ln=0}){W=$g(Me);function xn({x:Qn,y:Y}){const{nodeLookup:Fe,nodeExtent:mn,snapGrid:Ae,snapToGrid:Ze,nodeOrigin:sn,onNodeDrag:Sn,onSelectionDrag:kn,onError:xe,updateNodePositions:un}=E();P={x:Qn,y:Y};let rt=!1;const lt=H.size>1,Bt=lt&&mn?oke(tq(H)):null,hi=lt&&Ze?yGn({dragItems:H,snapGrid:Ae,x:Qn,y:Y}):null;for(const[Gt,At]of H){if(!Fe.has(Gt))continue;let st={x:Qn-At.distance.x,y:Y-At.distance.y};Ze&&(st=hi?{x:Math.round(st.x+hi.x),y:Math.round(st.y+hi.y)}:iq(st,Ae));let Wr=null;if(lt&&mn&&!At.extent&&Bt){const{positionAbsolute:mi}=At.internals,Fi=mi.x-Bt.x+mn[0][0],fu=mi.x+At.measured.width-Bt.x2+mn[1][0],Eu=mi.y-Bt.y+mn[0][1],Ws=mi.y+At.measured.height-Bt.y2+mn[1][1];Wr=[[Fi,Eu],[fu,Ws]]}const{position:Mr,positionAbsolute:ur}=Hdn({nodeId:Gt,nextPosition:st,nodeLookup:Fe,nodeExtent:Wr||mn,nodeOrigin:sn,onError:xe});rt=rt||At.position.x!==Mr.x||At.position.y!==Mr.y,At.position=Mr,At.internals.positionAbsolute=ur}if(le=le||rt,!!rt&&(un(H,!0),ee&&(M||Sn||!rn&&kn))){const[Gt,At]=R7e({nodeId:rn,dragItems:H,nodeLookup:Fe});M?.(ee,H,Gt,At),Sn?.(ee,Gt,At),rn||kn?.(ee,At)}}async function hn(){if(!ie)return;const{transform:Qn,panBy:Y,autoPanSpeed:Fe,autoPanOnNodeDrag:mn}=E();if(!mn){U=!1,cancelAnimationFrame(k);return}const[Ae,Ze]=qdn(G,ie,Fe);(Ae!==0||Ze!==0)&&(P.x=(P.x??0)-Ae/Qn[2],P.y=(P.y??0)-Ze/Qn[2],await Y({x:Ae,y:Ze})&&xn(P)),k=requestAnimationFrame(hn)}function wt(Qn){const{nodeLookup:Y,multiSelectionActive:Fe,nodesDraggable:mn,transform:Ae,snapGrid:Ze,snapToGrid:sn,selectNodesOnDrag:Sn,onNodeDragStart:kn,onSelectionDragStart:xe,unselectNodesAndEdges:un}=E();Z=!0,(!Sn||!Be)&&!Fe&&rn&&(Y.get(rn)?.selected||un()),Be&&Sn&&rn&&g?.(rn);const rt=BG(Qn.sourceEvent,{transform:Ae,snapGrid:Ze,snapToGrid:sn,containerBounds:ie});if(P=rt,H=vGn(Y,mn,rt,rn),H.size>0&&(x||kn||!rn&&xe)){const[lt,Bt]=R7e({nodeId:rn,dragItems:H,nodeLookup:Y});x?.(Qn.sourceEvent,H,lt,Bt),kn?.(Qn.sourceEvent,lt,Bt),rn||xe?.(Qn.sourceEvent,Bt)}}const Cn=jdn().clickDistance(ln).on("start",Qn=>{const{domNode:Y,nodeDragThreshold:Fe,transform:mn,snapGrid:Ae,snapToGrid:Ze}=E();ie=Y?.getBoundingClientRect()||null,se=!1,le=!1,ee=Qn.sourceEvent,Fe===0&&wt(Qn),P=BG(Qn.sourceEvent,{transform:mn,snapGrid:Ae,snapToGrid:Ze,containerBounds:ie}),G=Vm(Qn.sourceEvent,ie)}).on("drag",Qn=>{const{autoPanOnNodeDrag:Y,transform:Fe,snapGrid:mn,snapToGrid:Ae,nodeDragThreshold:Ze,nodeLookup:sn}=E(),Sn=BG(Qn.sourceEvent,{transform:Fe,snapGrid:mn,snapToGrid:Ae,containerBounds:ie});if(ee=Qn.sourceEvent,(Qn.sourceEvent.type==="touchmove"&&Qn.sourceEvent.touches.length>1||rn&&!sn.has(rn))&&(se=!0),!se){if(!U&&Y&&Z&&(U=!0,hn()),!Z){const kn=Vm(Qn.sourceEvent,ie),xe=kn.x-G.x,un=kn.y-G.y;Math.sqrt(xe*xe+un*un)>Ze&&wt(Qn)}(P.x!==Sn.xSnapped||P.y!==Sn.ySnapped)&&H&&Z&&(G=Vm(Qn.sourceEvent,ie),xn(Sn))}}).on("end",Qn=>{if(!(!Z||se)&&(U=!1,Z=!1,cancelAnimationFrame(k),H.size>0)){const{nodeLookup:Y,updateNodePositions:Fe,onNodeDragStop:mn,onSelectionDragStop:Ae}=E();if(le&&(Fe(H,!1),le=!1),N||mn||!rn&&Ae){const[Ze,sn]=R7e({nodeId:rn,dragItems:H,nodeLookup:Y,dragging:!1});N?.(Qn.sourceEvent,H,Ze,sn),mn?.(Qn.sourceEvent,Ze,sn),rn||Ae?.(Qn.sourceEvent,sn)}}}).filter(Qn=>{const Y=Qn.target;return!Qn.button&&(!Pe||!y1n(Y,`.${Pe}`,Me))&&(!ae||y1n(Y,ae,Me))});W.call(Cn)}function ge(){W?.on(".drag",null)}return{update:Oe,destroy:ge}}function jGn(g,E,x){const M=[],N={x:g.x-x,y:g.y-x,width:x*2,height:x*2};for(const P of E.values())XG(N,wI(P))>0&&M.push(P);return M}const EGn=250;function SGn(g,E,x,M){let N=[],P=1/0;const k=jGn(g,x,E+EGn);for(const H of k){const U=[...H.internals.handleBounds?.source??[],...H.internals.handleBounds?.target??[]];for(const G of U){if(M.nodeId===G.nodeId&&M.type===G.type&&M.id===G.id)continue;const{x:ie,y:Z}=AA(H,G,G.position,!0),W=Math.sqrt(Math.pow(ie-g.x,2)+Math.pow(Z-g.y,2));W>E||(W1){const H=M.type==="source"?"target":"source";return N.find(U=>U.type===H)??N[0]}return N[0]}function u0n(g,E,x,M,N,P=!1){const k=M.get(g);if(!k)return null;const H=N==="strict"?k.internals.handleBounds?.[E]:[...k.internals.handleBounds?.source??[],...k.internals.handleBounds?.target??[]],U=(x?H?.find(G=>G.id===x):H?.[0])??null;return U&&P?{...U,...AA(k,U,U.position,!0)}:U}function o0n(g,E){return g||(E?.classList.contains("target")?"target":E?.classList.contains("source")?"source":null)}function xGn(g,E){let x=null;return E?x=!0:g&&!E&&(x=!1),x}const s0n=()=>!0;function AGn(g,{connectionMode:E,connectionRadius:x,handleId:M,nodeId:N,edgeUpdaterType:P,isTarget:k,domNode:H,nodeLookup:U,lib:G,autoPanOnConnect:ie,flowId:Z,panBy:W,cancelConnection:se,onConnectStart:le,onConnect:ee,onConnectEnd:Oe,isValidConnection:ge=s0n,onReconnectEnd:Pe,updateConnection:ae,getTransform:Me,getFromHandle:Be,autoPanSpeed:rn,dragThreshold:ln=1,handleDomNode:xn}){const hn=Vdn(g.target);let wt=0,Cn;const{x:Qn,y:Y}=Vm(g),Fe=o0n(P,xn),mn=H?.getBoundingClientRect();let Ae=!1;if(!mn||!Fe)return;const Ze=u0n(N,Fe,M,U,E);if(!Ze)return;let sn=Vm(g,mn),Sn=!1,kn=null,xe=!1,un=null;function rt(){if(!ie||!mn)return;const[Mr,ur]=qdn(sn,mn,rn);W({x:Mr,y:ur}),wt=requestAnimationFrame(rt)}const lt={...Ze,nodeId:N,type:Fe,position:Ze.position},Bt=U.get(N);let Gt={inProgress:!0,isValid:null,from:AA(Bt,lt,cr.Left,!0),fromHandle:lt,fromPosition:lt.position,fromNode:Bt,to:sn,toHandle:null,toPosition:s1n[lt.position],toNode:null,pointer:sn};function At(){Ae=!0,ae(Gt),le?.(g,{nodeId:N,handleId:M,handleType:Fe})}ln===0&&At();function st(Mr){if(!Ae){const{x:Ws,y:Dh}=Vm(Mr),ef=Ws-Qn,ch=Dh-Y;if(!(ef*ef+ch*ch>ln*ln))return;At()}if(!Be()||!lt){Wr(Mr);return}const ur=Me();sn=Vm(Mr,mn),Cn=SGn(rq(sn,ur,!1,[1,1]),x,U,lt),Sn||(rt(),Sn=!0);const mi=l0n(Mr,{handle:Cn,connectionMode:E,fromNodeId:N,fromHandleId:M,fromType:k?"target":"source",isValidConnection:ge,doc:hn,lib:G,flowId:Z,nodeLookup:U});un=mi.handleDomNode,kn=mi.connection,xe=xGn(!!Cn,mi.isValid);const Fi=U.get(N),fu=Fi?AA(Fi,lt,cr.Left,!0):Gt.from,Eu={...Gt,from:fu,isValid:xe,to:mi.toHandle&&xe?xue({x:mi.toHandle.x,y:mi.toHandle.y},ur):sn,toHandle:mi.toHandle,toPosition:xe&&mi.toHandle?mi.toHandle.position:s1n[lt.position],toNode:mi.toHandle?U.get(mi.toHandle.nodeId):null,pointer:sn};ae(Eu),Gt=Eu}function Wr(Mr){if(!("touches"in Mr&&Mr.touches.length>0)){if(Ae){(Cn||un)&&kn&&xe&&ee?.(kn);const{inProgress:ur,...mi}=Gt,Fi={...mi,toPosition:Gt.toHandle?Gt.toPosition:null};Oe?.(Mr,Fi),P&&Pe?.(Mr,Fi)}se(),cancelAnimationFrame(wt),Sn=!1,xe=!1,kn=null,un=null,hn.removeEventListener("mousemove",st),hn.removeEventListener("mouseup",Wr),hn.removeEventListener("touchmove",st),hn.removeEventListener("touchend",Wr)}}hn.addEventListener("mousemove",st),hn.addEventListener("mouseup",Wr),hn.addEventListener("touchmove",st),hn.addEventListener("touchend",Wr)}function l0n(g,{handle:E,connectionMode:x,fromNodeId:M,fromHandleId:N,fromType:P,doc:k,lib:H,flowId:U,isValidConnection:G=s0n,nodeLookup:ie}){const Z=P==="target",W=E?k.querySelector(`.${H}-flow__handle[data-id="${U}-${E?.nodeId}-${E?.id}-${E?.type}"]`):null,{x:se,y:le}=Vm(g),ee=k.elementFromPoint(se,le),Oe=ee?.classList.contains(`${H}-flow__handle`)?ee:W,ge={handleDomNode:Oe,isValid:!1,connection:null,toHandle:null};if(Oe){const Pe=o0n(void 0,Oe),ae=Oe.getAttribute("data-nodeid"),Me=Oe.getAttribute("data-handleid"),Be=Oe.classList.contains("connectable"),rn=Oe.classList.contains("connectableend");if(!ae||!Pe)return ge;const ln={source:Z?ae:M,sourceHandle:Z?Me:N,target:Z?M:ae,targetHandle:Z?N:Me};ge.connection=ln;const hn=Be&&rn&&(x===bI.Strict?Z&&Pe==="source"||!Z&&Pe==="target":ae!==M||Me!==N);ge.isValid=hn&&G(ln),ge.toHandle=u0n(ae,Pe,Me,ie,x,!0)}return ge}const fke={onPointerDown:AGn,isValid:l0n};function MGn({domNode:g,panZoom:E,getTransform:x,getViewScale:M}){const N=$g(g);function P({translateExtent:H,width:U,height:G,zoomStep:ie=1,pannable:Z=!0,zoomable:W=!0,inversePan:se=!1}){const le=ae=>{if(ae.sourceEvent.type!=="wheel"||!E)return;const Me=x(),Be=ae.sourceEvent.ctrlKey&&KG()?10:1,rn=-ae.sourceEvent.deltaY*(ae.sourceEvent.deltaMode===1?.05:ae.sourceEvent.deltaMode?1:.002)*ie,ln=Me[2]*Math.pow(2,rn*Be);E.scaleTo(ln)};let ee=[0,0];const Oe=ae=>{(ae.sourceEvent.type==="mousedown"||ae.sourceEvent.type==="touchstart")&&(ee=[ae.sourceEvent.clientX??ae.sourceEvent.touches[0].clientX,ae.sourceEvent.clientY??ae.sourceEvent.touches[0].clientY])},ge=ae=>{const Me=x();if(ae.sourceEvent.type!=="mousemove"&&ae.sourceEvent.type!=="touchmove"||!E)return;const Be=[ae.sourceEvent.clientX??ae.sourceEvent.touches[0].clientX,ae.sourceEvent.clientY??ae.sourceEvent.touches[0].clientY],rn=[Be[0]-ee[0],Be[1]-ee[1]];ee=Be;const ln=M()*Math.max(Me[2],Math.log(Me[2]))*(se?-1:1),xn={x:Me[0]-rn[0]*ln,y:Me[1]-rn[1]*ln},hn=[[0,0],[U,G]];E.setViewportConstrained({x:xn.x,y:xn.y,zoom:Me[2]},hn,H)},Pe=$dn().on("start",Oe).on("zoom",Z?ge:null).on("zoom.wheel",W?le:null);N.call(Pe,{})}function k(){N.on("zoom",null)}return{update:P,destroy:k,pointer:Um}}const $ue=g=>({x:g.x,y:g.y,zoom:g.k}),B7e=({x:g,y:E,zoom:x})=>_ue.translate(g,E).scale(x),sI=(g,E)=>g.target.closest(`.${E}`),f0n=(g,E)=>E===2&&Array.isArray(g)&&g.includes(2),TGn=g=>((g*=2)<=1?g*g*g:(g-=2)*g*g+2)/2,z7e=(g,E=0,x=TGn,M=()=>{})=>{const N=typeof E=="number"&&E>0;return N||M(),N?g.transition().duration(E).ease(x).on("end",M):g},a0n=g=>{const E=g.ctrlKey&&KG()?10:1;return-g.deltaY*(g.deltaMode===1?.05:g.deltaMode?1:.002)*E};function CGn({zoomPanValues:g,noWheelClassName:E,d3Selection:x,d3Zoom:M,panOnScrollMode:N,panOnScrollSpeed:P,zoomOnPinch:k,onPanZoomStart:H,onPanZoom:U,onPanZoomEnd:G}){return ie=>{if(sI(ie,E))return ie.ctrlKey&&ie.preventDefault(),!1;ie.preventDefault(),ie.stopImmediatePropagation();const Z=x.property("__zoom").k||1;if(ie.ctrlKey&&k){const Oe=Um(ie),ge=a0n(ie),Pe=Z*Math.pow(2,ge);M.scaleTo(x,Pe,Oe,ie);return}const W=ie.deltaMode===1?20:1;let se=N===jA.Vertical?0:ie.deltaX*W,le=N===jA.Horizontal?0:ie.deltaY*W;!KG()&&ie.shiftKey&&N!==jA.Vertical&&(se=ie.deltaY*W,le=0),M.translateBy(x,-(se/Z)*P,-(le/Z)*P,{internal:!0});const ee=$ue(x.property("__zoom"));clearTimeout(g.panScrollTimeout),g.isPanScrolling?(U?.(ie,ee),g.panScrollTimeout=setTimeout(()=>{G?.(ie,ee),g.isPanScrolling=!1},150)):(g.isPanScrolling=!0,H?.(ie,ee))}}function OGn({noWheelClassName:g,preventScrolling:E,d3ZoomHandler:x}){return function(M,N){const P=M.type==="wheel",k=!E&&P&&!M.ctrlKey,H=sI(M,g);if(M.ctrlKey&&P&&H&&M.preventDefault(),k||H)return null;M.preventDefault(),x.call(this,M,N)}}function NGn({zoomPanValues:g,onDraggingChange:E,onPanZoomStart:x}){return M=>{if(M.sourceEvent?.internal)return;const N=$ue(M.transform);g.mouseButton=M.sourceEvent?.button||0,g.isZoomingOrPanning=!0,g.prevViewport=N,M.sourceEvent?.type==="mousedown"&&E(!0),x&&x?.(M.sourceEvent,N)}}function DGn({zoomPanValues:g,panOnDrag:E,onPaneContextMenu:x,onTransformChange:M,onPanZoom:N}){return P=>{g.usedRightMouseButton=!!(x&&f0n(E,g.mouseButton??0)),P.sourceEvent?.sync||M([P.transform.x,P.transform.y,P.transform.k]),N&&!P.sourceEvent?.internal&&N?.(P.sourceEvent,$ue(P.transform))}}function IGn({zoomPanValues:g,panOnDrag:E,panOnScroll:x,onDraggingChange:M,onPanZoomEnd:N,onPaneContextMenu:P}){return k=>{if(!k.sourceEvent?.internal&&(g.isZoomingOrPanning=!1,P&&f0n(E,g.mouseButton??0)&&!g.usedRightMouseButton&&k.sourceEvent&&P(k.sourceEvent),g.usedRightMouseButton=!1,M(!1),N)){const H=$ue(k.transform);g.prevViewport=H,clearTimeout(g.timerId),g.timerId=setTimeout(()=>{N?.(k.sourceEvent,H)},x?150:0)}}}function _Gn({zoomActivationKeyPressed:g,zoomOnScroll:E,zoomOnPinch:x,panOnDrag:M,panOnScroll:N,zoomOnDoubleClick:P,userSelectionActive:k,noWheelClassName:H,noPanClassName:U,lib:G,connectionInProgress:ie}){return Z=>{const W=g||E,se=x&&Z.ctrlKey,le=Z.type==="wheel";if(Z.button===1&&Z.type==="mousedown"&&(sI(Z,`${G}-flow__node`)||sI(Z,`${G}-flow__edge`)))return!0;if(!M&&!W&&!N&&!P&&!x||k||ie&&!le||sI(Z,H)&&le||sI(Z,U)&&(!le||N&&le&&!g)||!x&&Z.ctrlKey&&le)return!1;if(!x&&Z.type==="touchstart"&&Z.touches?.length>1)return Z.preventDefault(),!1;if(!W&&!N&&!se&&le||!M&&(Z.type==="mousedown"||Z.type==="touchstart")||Array.isArray(M)&&!M.includes(Z.button)&&Z.type==="mousedown")return!1;const ee=Array.isArray(M)&&M.includes(Z.button)||!Z.button||Z.button<=1;return(!Z.ctrlKey||le)&&ee}}function LGn({domNode:g,minZoom:E,maxZoom:x,translateExtent:M,viewport:N,onPanZoom:P,onPanZoomStart:k,onPanZoomEnd:H,onDraggingChange:U}){const G={isZoomingOrPanning:!1,usedRightMouseButton:!1,prevViewport:{},mouseButton:0,timerId:void 0,panScrollTimeout:void 0,isPanScrolling:!1},ie=g.getBoundingClientRect(),Z=$dn().scaleExtent([E,x]).translateExtent(M),W=$g(g).call(Z);Pe({x:N.x,y:N.y,zoom:gI(N.zoom,E,x)},[[0,0],[ie.width,ie.height]],M);const se=W.on("wheel.zoom"),le=W.on("dblclick.zoom");Z.wheelDelta(a0n);function ee(Cn,Qn){return W?new Promise(Y=>{Z?.interpolate(Qn?.interpolate==="linear"?RG:hue).transform(z7e(W,Qn?.duration,Qn?.ease,()=>Y(!0)),Cn)}):Promise.resolve(!1)}function Oe({noWheelClassName:Cn,noPanClassName:Qn,onPaneContextMenu:Y,userSelectionActive:Fe,panOnScroll:mn,panOnDrag:Ae,panOnScrollMode:Ze,panOnScrollSpeed:sn,preventScrolling:Sn,zoomOnPinch:kn,zoomOnScroll:xe,zoomOnDoubleClick:un,zoomActivationKeyPressed:rt,lib:lt,onTransformChange:Bt,connectionInProgress:hi,paneClickDistance:Gt,selectionOnDrag:At}){Fe&&!G.isZoomingOrPanning&&ge();const st=mn&&!rt&&!Fe;Z.clickDistance(At?1/0:!Km(Gt)||Gt<0?0:Gt);const Wr=st?CGn({zoomPanValues:G,noWheelClassName:Cn,d3Selection:W,d3Zoom:Z,panOnScrollMode:Ze,panOnScrollSpeed:sn,zoomOnPinch:kn,onPanZoomStart:k,onPanZoom:P,onPanZoomEnd:H}):OGn({noWheelClassName:Cn,preventScrolling:Sn,d3ZoomHandler:se});if(W.on("wheel.zoom",Wr,{passive:!1}),!Fe){const ur=NGn({zoomPanValues:G,onDraggingChange:U,onPanZoomStart:k});Z.on("start",ur);const mi=DGn({zoomPanValues:G,panOnDrag:Ae,onPaneContextMenu:!!Y,onPanZoom:P,onTransformChange:Bt});Z.on("zoom",mi);const Fi=IGn({zoomPanValues:G,panOnDrag:Ae,panOnScroll:mn,onPaneContextMenu:Y,onPanZoomEnd:H,onDraggingChange:U});Z.on("end",Fi)}const Mr=_Gn({zoomActivationKeyPressed:rt,panOnDrag:Ae,zoomOnScroll:xe,panOnScroll:mn,zoomOnDoubleClick:un,zoomOnPinch:kn,userSelectionActive:Fe,noPanClassName:Qn,noWheelClassName:Cn,lib:lt,connectionInProgress:hi});Z.filter(Mr),un?W.on("dblclick.zoom",le):W.on("dblclick.zoom",null)}function ge(){Z.on("zoom",null)}async function Pe(Cn,Qn,Y){const Fe=B7e(Cn),mn=Z?.constrain()(Fe,Qn,Y);return mn&&await ee(mn),new Promise(Ae=>Ae(mn))}async function ae(Cn,Qn){const Y=B7e(Cn);return await ee(Y,Qn),new Promise(Fe=>Fe(Y))}function Me(Cn){if(W){const Qn=B7e(Cn),Y=W.property("__zoom");(Y.k!==Cn.zoom||Y.x!==Cn.x||Y.y!==Cn.y)&&Z?.transform(W,Qn,null,{sync:!0})}}function Be(){const Cn=W?Pdn(W.node()):{x:0,y:0,k:1};return{x:Cn.x,y:Cn.y,zoom:Cn.k}}function rn(Cn,Qn){return W?new Promise(Y=>{Z?.interpolate(Qn?.interpolate==="linear"?RG:hue).scaleTo(z7e(W,Qn?.duration,Qn?.ease,()=>Y(!0)),Cn)}):Promise.resolve(!1)}function ln(Cn,Qn){return W?new Promise(Y=>{Z?.interpolate(Qn?.interpolate==="linear"?RG:hue).scaleBy(z7e(W,Qn?.duration,Qn?.ease,()=>Y(!0)),Cn)}):Promise.resolve(!1)}function xn(Cn){Z?.scaleExtent(Cn)}function hn(Cn){Z?.translateExtent(Cn)}function wt(Cn){const Qn=!Km(Cn)||Cn<0?0:Cn;Z?.clickDistance(Qn)}return{update:Oe,destroy:ge,setViewport:ae,setViewportConstrained:Pe,getViewport:Be,scaleTo:rn,scaleBy:ln,setScaleExtent:xn,setTranslateExtent:hn,syncViewport:Me,setClickDistance:wt}}var mI;(function(g){g.Line="line",g.Handle="handle"})(mI||(mI={}));function PGn({width:g,prevWidth:E,height:x,prevHeight:M,affectsX:N,affectsY:P}){const k=g-E,H=x-M,U=[k>0?1:k<0?-1:0,H>0?1:H<0?-1:0];return k&&N&&(U[0]=U[0]*-1),H&&P&&(U[1]=U[1]*-1),U}function k1n(g){const E=g.includes("right")||g.includes("left"),x=g.includes("bottom")||g.includes("top"),M=g.includes("left"),N=g.includes("top");return{isHorizontal:E,isVertical:x,affectsX:M,affectsY:N}}function Y7(g,E){return Math.max(0,E-g)}function Q7(g,E){return Math.max(0,g-E)}function tue(g,E,x){return Math.max(0,E-g,g-x)}function j1n(g,E){return g?!E:E}function $Gn(g,E,x,M,N,P,k,H){let{affectsX:U,affectsY:G}=E;const{isHorizontal:ie,isVertical:Z}=E,W=ie&&Z,{xSnapped:se,ySnapped:le}=x,{minWidth:ee,maxWidth:Oe,minHeight:ge,maxHeight:Pe}=M,{x:ae,y:Me,width:Be,height:rn,aspectRatio:ln}=g;let xn=Math.floor(ie?se-g.pointerX:0),hn=Math.floor(Z?le-g.pointerY:0);const wt=Be+(U?-xn:xn),Cn=rn+(G?-hn:hn),Qn=-P[0]*Be,Y=-P[1]*rn;let Fe=tue(wt,ee,Oe),mn=tue(Cn,ge,Pe);if(k){let sn=0,Sn=0;U&&xn<0?sn=Y7(ae+xn+Qn,k[0][0]):!U&&xn>0&&(sn=Q7(ae+wt+Qn,k[1][0])),G&&hn<0?Sn=Y7(Me+hn+Y,k[0][1]):!G&&hn>0&&(Sn=Q7(Me+Cn+Y,k[1][1])),Fe=Math.max(Fe,sn),mn=Math.max(mn,Sn)}if(H){let sn=0,Sn=0;U&&xn>0?sn=Q7(ae+xn,H[0][0]):!U&&xn<0&&(sn=Y7(ae+wt,H[1][0])),G&&hn>0?Sn=Q7(Me+hn,H[0][1]):!G&&hn<0&&(Sn=Y7(Me+Cn,H[1][1])),Fe=Math.max(Fe,sn),mn=Math.max(mn,Sn)}if(N){if(ie){const sn=tue(wt/ln,ge,Pe)*ln;if(Fe=Math.max(Fe,sn),k){let Sn=0;!U&&!G||U&&!G&&W?Sn=Q7(Me+Y+wt/ln,k[1][1])*ln:Sn=Y7(Me+Y+(U?xn:-xn)/ln,k[0][1])*ln,Fe=Math.max(Fe,Sn)}if(H){let Sn=0;!U&&!G||U&&!G&&W?Sn=Y7(Me+wt/ln,H[1][1])*ln:Sn=Q7(Me+(U?xn:-xn)/ln,H[0][1])*ln,Fe=Math.max(Fe,Sn)}}if(Z){const sn=tue(Cn*ln,ee,Oe)/ln;if(mn=Math.max(mn,sn),k){let Sn=0;!U&&!G||G&&!U&&W?Sn=Q7(ae+Cn*ln+Qn,k[1][0])/ln:Sn=Y7(ae+(G?hn:-hn)*ln+Qn,k[0][0])/ln,mn=Math.max(mn,Sn)}if(H){let Sn=0;!U&&!G||G&&!U&&W?Sn=Y7(ae+Cn*ln,H[1][0])/ln:Sn=Q7(ae+(G?hn:-hn)*ln,H[0][0])/ln,mn=Math.max(mn,Sn)}}}hn=hn+(hn<0?mn:-mn),xn=xn+(xn<0?Fe:-Fe),N&&(W?wt>Cn*ln?hn=(j1n(U,G)?-xn:xn)/ln:xn=(j1n(U,G)?-hn:hn)*ln:ie?(hn=xn/ln,G=U):(xn=hn*ln,U=G));const Ae=U?ae+xn:ae,Ze=G?Me+hn:Me;return{width:Be+(U?-xn:xn),height:rn+(G?-hn:hn),x:P[0]*xn*(U?-1:1)+Ae,y:P[1]*hn*(G?-1:1)+Ze}}const h0n={width:0,height:0,x:0,y:0},RGn={...h0n,pointerX:0,pointerY:0,aspectRatio:1};function BGn(g){return[[0,0],[g.measured.width,g.measured.height]]}function zGn(g,E,x){const M=E.position.x+g.position.x,N=E.position.y+g.position.y,P=g.measured.width??0,k=g.measured.height??0,H=x[0]*P,U=x[1]*k;return[[M-H,N-U],[M+P-H,N+k-U]]}function FGn({domNode:g,nodeId:E,getStoreItems:x,onChange:M,onEnd:N}){const P=$g(g);let k={controlDirection:k1n("bottom-right"),boundaries:{minWidth:0,minHeight:0,maxWidth:Number.MAX_VALUE,maxHeight:Number.MAX_VALUE},resizeDirection:void 0,keepAspectRatio:!1};function H({controlPosition:G,boundaries:ie,keepAspectRatio:Z,resizeDirection:W,onResizeStart:se,onResize:le,onResizeEnd:ee,shouldResize:Oe}){let ge={...h0n},Pe={...RGn};k={boundaries:ie,resizeDirection:W,keepAspectRatio:Z,controlDirection:k1n(G)};let ae,Me=null,Be=[],rn,ln,xn,hn=!1;const wt=jdn().on("start",Cn=>{const{nodeLookup:Qn,transform:Y,snapGrid:Fe,snapToGrid:mn,nodeOrigin:Ae,paneDomNode:Ze}=x();if(ae=Qn.get(E),!ae)return;Me=Ze?.getBoundingClientRect()??null;const{xSnapped:sn,ySnapped:Sn}=BG(Cn.sourceEvent,{transform:Y,snapGrid:Fe,snapToGrid:mn,containerBounds:Me});ge={width:ae.measured.width??0,height:ae.measured.height??0,x:ae.position.x??0,y:ae.position.y??0},Pe={...ge,pointerX:sn,pointerY:Sn,aspectRatio:ge.width/ge.height},rn=void 0,ae.parentId&&(ae.extent==="parent"||ae.expandParent)&&(rn=Qn.get(ae.parentId),ln=rn&&ae.extent==="parent"?BGn(rn):void 0),Be=[],xn=void 0;for(const[kn,xe]of Qn)if(xe.parentId===E&&(Be.push({id:kn,position:{...xe.position},extent:xe.extent}),xe.extent==="parent"||xe.expandParent)){const un=zGn(xe,ae,xe.origin??Ae);xn?xn=[[Math.min(un[0][0],xn[0][0]),Math.min(un[0][1],xn[0][1])],[Math.max(un[1][0],xn[1][0]),Math.max(un[1][1],xn[1][1])]]:xn=un}se?.(Cn,{...ge})}).on("drag",Cn=>{const{transform:Qn,snapGrid:Y,snapToGrid:Fe,nodeOrigin:mn}=x(),Ae=BG(Cn.sourceEvent,{transform:Qn,snapGrid:Y,snapToGrid:Fe,containerBounds:Me}),Ze=[];if(!ae)return;const{x:sn,y:Sn,width:kn,height:xe}=ge,un={},rt=ae.origin??mn,{width:lt,height:Bt,x:hi,y:Gt}=$Gn(Pe,k.controlDirection,Ae,k.boundaries,k.keepAspectRatio,rt,ln,xn),At=lt!==kn,st=Bt!==xe,Wr=hi!==sn&&At,Mr=Gt!==Sn&&st;if(!Wr&&!Mr&&!At&&!st)return;if((Wr||Mr||rt[0]===1||rt[1]===1)&&(un.x=Wr?hi:ge.x,un.y=Mr?Gt:ge.y,ge.x=un.x,ge.y=un.y,Be.length>0)){const fu=hi-sn,Eu=Gt-Sn;for(const Ws of Be)Ws.position={x:Ws.position.x-fu+rt[0]*(lt-kn),y:Ws.position.y-Eu+rt[1]*(Bt-xe)},Ze.push(Ws)}if((At||st)&&(un.width=At&&(!k.resizeDirection||k.resizeDirection==="horizontal")?lt:ge.width,un.height=st&&(!k.resizeDirection||k.resizeDirection==="vertical")?Bt:ge.height,ge.width=un.width,ge.height=un.height),rn&&ae.expandParent){const fu=rt[0]*(un.width??0);un.x&&un.x{hn&&(ee?.(Cn,{...ge}),N?.({...ge}),hn=!1)});P.call(wt)}function U(){P.on(".drag",null)}return{update:H,destroy:U}}var F7e={exports:{}},J7e={},H7e={exports:{}},G7e={};var E1n;function JGn(){if(E1n)return G7e;E1n=1;var g=WG();function E(Z,W){return Z===W&&(Z!==0||1/Z===1/W)||Z!==Z&&W!==W}var x=typeof Object.is=="function"?Object.is:E,M=g.useState,N=g.useEffect,P=g.useLayoutEffect,k=g.useDebugValue;function H(Z,W){var se=W(),le=M({inst:{value:se,getSnapshot:W}}),ee=le[0].inst,Oe=le[1];return P(function(){ee.value=se,ee.getSnapshot=W,U(ee)&&Oe({inst:ee})},[Z,se,W]),N(function(){return U(ee)&&Oe({inst:ee}),Z(function(){U(ee)&&Oe({inst:ee})})},[Z]),k(se),se}function U(Z){var W=Z.getSnapshot;Z=Z.value;try{var se=W();return!x(Z,se)}catch{return!0}}function G(Z,W){return W()}var ie=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?G:H;return G7e.useSyncExternalStore=g.useSyncExternalStore!==void 0?g.useSyncExternalStore:ie,G7e}var S1n;function HGn(){return S1n||(S1n=1,H7e.exports=JGn()),H7e.exports}var x1n;function GGn(){if(x1n)return J7e;x1n=1;var g=WG(),E=HGn();function x(G,ie){return G===ie&&(G!==0||1/G===1/ie)||G!==G&&ie!==ie}var M=typeof Object.is=="function"?Object.is:x,N=E.useSyncExternalStore,P=g.useRef,k=g.useEffect,H=g.useMemo,U=g.useDebugValue;return J7e.useSyncExternalStoreWithSelector=function(G,ie,Z,W,se){var le=P(null);if(le.current===null){var ee={hasValue:!1,value:null};le.current=ee}else ee=le.current;le=H(function(){function ge(rn){if(!Pe){if(Pe=!0,ae=rn,rn=W(rn),se!==void 0&&ee.hasValue){var ln=ee.value;if(se(ln,rn))return Me=ln}return Me=rn}if(ln=Me,M(ae,rn))return ln;var xn=W(rn);return se!==void 0&&se(ln,xn)?(ae=rn,ln):(ae=rn,Me=xn)}var Pe=!1,ae,Me,Be=Z===void 0?null:Z;return[function(){return ge(ie())},Be===null?void 0:function(){return ge(Be())}]},[ie,Z,W,se]);var Oe=N(G,le[0],le[1]);return k(function(){ee.hasValue=!0,ee.value=Oe},[Oe]),U(Oe),Oe},J7e}var A1n;function qGn(){return A1n||(A1n=1,F7e.exports=GGn()),F7e.exports}var UGn=qGn();const XGn=bke(UGn),KGn={},M1n=g=>{let E;const x=new Set,M=(ie,Z)=>{const W=typeof ie=="function"?ie(E):ie;if(!Object.is(W,E)){const se=E;E=Z??(typeof W!="object"||W===null)?W:Object.assign({},E,W),x.forEach(le=>le(E,se))}},N=()=>E,U={setState:M,getState:N,getInitialState:()=>G,subscribe:ie=>(x.add(ie),()=>x.delete(ie)),destroy:()=>{(KGn?"production":void 0)!=="production"&&console.warn("[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."),x.clear()}},G=E=g(M,N,U);return U},VGn=g=>g?M1n(g):M1n,{useDebugValue:YGn}=izn,{useSyncExternalStoreWithSelector:QGn}=XGn,WGn=g=>g;function d0n(g,E=WGn,x){const M=QGn(g.subscribe,g.getState,g.getServerState||g.getInitialState,E,x);return YGn(M),M}const T1n=(g,E)=>{const x=VGn(g),M=(N,P=E)=>d0n(x,N,P);return Object.assign(M,x),M},ZGn=(g,E)=>g?T1n(g,E):T1n;function El(g,E){if(Object.is(g,E))return!0;if(typeof g!="object"||g===null||typeof E!="object"||E===null)return!1;if(g instanceof Map&&E instanceof Map){if(g.size!==E.size)return!1;for(const[M,N]of g)if(!Object.is(N,E.get(M)))return!1;return!0}if(g instanceof Set&&E instanceof Set){if(g.size!==E.size)return!1;for(const M of g)if(!E.has(M))return!1;return!0}const x=Object.keys(g);if(x.length!==Object.keys(E).length)return!1;for(const M of x)if(!Object.prototype.hasOwnProperty.call(E,M)||!Object.is(g[M],E[M]))return!1;return!0}var eqn=odn();const Rue=He.createContext(null),nqn=Rue.Provider,b0n=u5.error001();function Fu(g,E){const x=He.useContext(Rue);if(x===null)throw new Error(b0n);return d0n(x,g,E)}function Sl(){const g=He.useContext(Rue);if(g===null)throw new Error(b0n);return He.useMemo(()=>({getState:g.getState,setState:g.setState,subscribe:g.subscribe}),[g])}const C1n={display:"none"},tqn={position:"absolute",width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0px, 0px, 0px, 0px)",clipPath:"inset(100%)"},g0n="react-flow__node-desc",w0n="react-flow__edge-desc",iqn="react-flow__aria-live",rqn=g=>g.ariaLiveMessage,cqn=g=>g.ariaLabelConfig;function uqn({rfId:g}){const E=Fu(rqn);return F.jsx("div",{id:`${iqn}-${g}`,"aria-live":"assertive","aria-atomic":"true",style:tqn,children:E})}function oqn({rfId:g,disableKeyboardA11y:E}){const x=Fu(cqn);return F.jsxs(F.Fragment,{children:[F.jsx("div",{id:`${g0n}-${g}`,style:C1n,children:E?x["node.a11yDescription.default"]:x["node.a11yDescription.keyboardDisabled"]}),F.jsx("div",{id:`${w0n}-${g}`,style:C1n,children:x["edge.a11yDescription.default"]}),!E&&F.jsx(uqn,{rfId:g})]})}const Bue=He.forwardRef(({position:g="top-left",children:E,className:x,style:M,...N},P)=>{const k=`${g}`.split("-");return F.jsx("div",{className:Ta(["react-flow__panel",x,...k]),style:M,ref:P,...N,children:E})});Bue.displayName="Panel";function sqn({proOptions:g,position:E="bottom-right"}){return g?.hideAttribution?null:F.jsx(Bue,{position:E,className:"react-flow__attribution","data-message":"Please only hide this attribution when you are subscribed to React Flow Pro: https://pro.reactflow.dev",children:F.jsx("a",{href:"https://reactflow.dev",target:"_blank",rel:"noopener noreferrer","aria-label":"React Flow attribution",children:"React Flow"})})}const lqn=g=>{const E=[],x=[];for(const[,M]of g.nodeLookup)M.selected&&E.push(M.internals.userNode);for(const[,M]of g.edgeLookup)M.selected&&x.push(M);return{selectedNodes:E,selectedEdges:x}},iue=g=>g.id;function fqn(g,E){return El(g.selectedNodes.map(iue),E.selectedNodes.map(iue))&&El(g.selectedEdges.map(iue),E.selectedEdges.map(iue))}function aqn({onSelectionChange:g}){const E=Sl(),{selectedNodes:x,selectedEdges:M}=Fu(lqn,fqn);return He.useEffect(()=>{const N={nodes:x,edges:M};g?.(N),E.getState().onSelectionChangeHandlers.forEach(P=>P(N))},[x,M,g]),null}const hqn=g=>!!g.onSelectionChangeHandlers;function dqn({onSelectionChange:g}){const E=Fu(hqn);return g||E?F.jsx(aqn,{onSelectionChange:g}):null}const ake=typeof window<"u"?He.useLayoutEffect:He.useEffect,p0n=[0,0],bqn={x:0,y:0,zoom:1},gqn=["nodes","edges","defaultNodes","defaultEdges","onConnect","onConnectStart","onConnectEnd","onClickConnectStart","onClickConnectEnd","nodesDraggable","autoPanOnNodeFocus","nodesConnectable","nodesFocusable","edgesFocusable","edgesReconnectable","elevateNodesOnSelect","elevateEdgesOnSelect","minZoom","maxZoom","nodeExtent","onNodesChange","onEdgesChange","elementsSelectable","connectionMode","snapGrid","snapToGrid","translateExtent","connectOnClick","defaultEdgeOptions","fitView","fitViewOptions","onNodesDelete","onEdgesDelete","onDelete","onNodeDrag","onNodeDragStart","onNodeDragStop","onSelectionDrag","onSelectionDragStart","onSelectionDragStop","onMoveStart","onMove","onMoveEnd","noPanClassName","nodeOrigin","autoPanOnConnect","autoPanOnNodeDrag","onError","connectionRadius","isValidConnection","selectNodesOnDrag","nodeDragThreshold","connectionDragThreshold","onBeforeDelete","debug","autoPanSpeed","ariaLabelConfig","zIndexMode"],O1n=[...gqn,"rfId"],wqn=g=>({setNodes:g.setNodes,setEdges:g.setEdges,setMinZoom:g.setMinZoom,setMaxZoom:g.setMaxZoom,setTranslateExtent:g.setTranslateExtent,setNodeExtent:g.setNodeExtent,reset:g.reset,setDefaultNodesAndEdges:g.setDefaultNodesAndEdges}),N1n={translateExtent:GG,nodeOrigin:p0n,minZoom:.5,maxZoom:2,elementsSelectable:!0,noPanClassName:"nopan",rfId:"1"};function pqn(g){const{setNodes:E,setEdges:x,setMinZoom:M,setMaxZoom:N,setTranslateExtent:P,setNodeExtent:k,reset:H,setDefaultNodesAndEdges:U}=Fu(wqn,El),G=Sl();ake(()=>(U(g.defaultNodes,g.defaultEdges),()=>{ie.current=N1n,H()}),[]);const ie=He.useRef(N1n);return ake(()=>{for(const Z of O1n){const W=g[Z],se=ie.current[Z];W!==se&&(typeof g[Z]>"u"||(Z==="nodes"?E(W):Z==="edges"?x(W):Z==="minZoom"?M(W):Z==="maxZoom"?N(W):Z==="translateExtent"?P(W):Z==="nodeExtent"?k(W):Z==="ariaLabelConfig"?G.setState({ariaLabelConfig:ZHn(W)}):Z==="fitView"?G.setState({fitViewQueued:W}):Z==="fitViewOptions"?G.setState({fitViewOptions:W}):G.setState({[Z]:W})))}ie.current=g},O1n.map(Z=>g[Z])),null}function D1n(){return typeof window>"u"||!window.matchMedia?null:window.matchMedia("(prefers-color-scheme: dark)")}function mqn(g){const[E,x]=He.useState(g==="system"?null:g);return He.useEffect(()=>{if(g!=="system"){x(g);return}const M=D1n(),N=()=>x(M?.matches?"dark":"light");return N(),M?.addEventListener("change",N),()=>{M?.removeEventListener("change",N)}},[g]),E!==null?E:D1n()?.matches?"dark":"light"}const I1n=typeof document<"u"?document:null;function VG(g=null,E={target:I1n,actInsideInputWithModifier:!0}){const[x,M]=He.useState(!1),N=He.useRef(!1),P=He.useRef(new Set([])),[k,H]=He.useMemo(()=>{if(g!==null){const G=(Array.isArray(g)?g:[g]).filter(Z=>typeof Z=="string").map(Z=>Z.replace("+",` +`+j.stack}}var ef=Object.prototype.hasOwnProperty,ch=g.unstable_scheduleCallback,kc=g.unstable_cancelCallback,cd=g.unstable_shouldYield,jb=g.unstable_requestPaint,Rs=g.unstable_now,c0=g.unstable_getCurrentPriorityLevel,u0=g.unstable_ImmediatePriority,o0=g.unstable_UserBlockingPriority,Bg=g.unstable_NormalPriority,r6=g.unstable_LowPriority,zg=g.unstable_IdlePriority,c6=g.log,d1=g.unstable_setDisableYieldValue,ud=null,Sf=null;function b1(a){if(typeof c6=="function"&&d1(a),Sf&&typeof Sf.setStrictMode=="function")try{Sf.setStrictMode(ud,a)}catch{}}var xf=Math.clz32?Math.clz32:a5,l5=Math.log,f5=Math.LN2;function a5(a){return a>>>=0,a===0?32:31-(l5(a)/f5|0)|0}var Fg=256,Eb=262144,Jg=4194304;function _u(a){var d=a&42;if(d!==0)return d;switch(a&-a){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return a&261888;case 262144:case 524288:case 1048576:case 2097152:return a&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return a&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return a}}function Hg(a,d,w){var j=a.pendingLanes;if(j===0)return 0;var T=0,D=a.suspendedLanes,Q=a.pingedLanes;a=a.warmLanes;var de=j&134217727;return de!==0?(j=de&~D,j!==0?T=_u(j):(Q&=de,Q!==0?T=_u(Q):w||(w=de&~a,w!==0&&(T=_u(w))))):(de=j&~D,de!==0?T=_u(de):Q!==0?T=_u(Q):w||(w=j&~a,w!==0&&(T=_u(w)))),T===0?0:d!==0&&d!==T&&(d&D)===0&&(D=T&-T,w=d&-d,D>=w||D===32&&(w&4194048)!==0)?d:T}function Gg(a,d){return(a.pendingLanes&~(a.suspendedLanes&~a.pingedLanes)&d)===0}function u6(a,d){switch(a){case 1:case 2:case 4:case 8:case 64:return d+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return d+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function h5(){var a=Jg;return Jg<<=1,(Jg&62914560)===0&&(Jg=4194304),a}function Wm(a){for(var d=[],w=0;31>w;w++)d.push(a);return d}function qg(a,d){a.pendingLanes|=d,d!==268435456&&(a.suspendedLanes=0,a.pingedLanes=0,a.warmLanes=0)}function o6(a,d,w,j,T,D){var Q=a.pendingLanes;a.pendingLanes=w,a.suspendedLanes=0,a.pingedLanes=0,a.warmLanes=0,a.expiredLanes&=w,a.entangledLanes&=w,a.errorRecoveryDisabledLanes&=w,a.shellSuspendCounter=0;var de=a.entanglements,en=a.expirationTimes,Ln=a.hiddenUpdates;for(w=Q&~w;0"u")return null;try{return a.activeElement||a.body}catch{return a.body}}var OA=/[\n"\\]/g;function _h(a){return a.replace(OA,function(d){return"\\"+d.charCodeAt(0).toString(16)+" "})}function i3(a,d,w,j,T,D,Q,de){a.name="",Q!=null&&typeof Q!="function"&&typeof Q!="symbol"&&typeof Q!="boolean"?a.type=Q:a.removeAttribute("type"),d!=null?Q==="number"?(d===0&&a.value===""||a.value!=d)&&(a.value=""+Ih(d)):a.value!==""+Ih(d)&&(a.value=""+Ih(d)):Q!=="submit"&&Q!=="reset"||a.removeAttribute("value"),d!=null?a6(a,Q,Ih(d)):w!=null?a6(a,Q,Ih(w)):j!=null&&a.removeAttribute("value"),T==null&&D!=null&&(a.defaultChecked=!!D),T!=null&&(a.checked=T&&typeof T!="function"&&typeof T!="symbol"),de!=null&&typeof de!="function"&&typeof de!="symbol"&&typeof de!="boolean"?a.name=""+Ih(de):a.removeAttribute("name")}function ok(a,d,w,j,T,D,Q,de){if(D!=null&&typeof D!="function"&&typeof D!="symbol"&&typeof D!="boolean"&&(a.type=D),d!=null||w!=null){if(!(D!=="submit"&&D!=="reset"||d!=null)){p5(a);return}w=w!=null?""+Ih(w):"",d=d!=null?""+Ih(d):w,de||d===a.value||(a.value=d),a.defaultValue=d}j=j??T,j=typeof j!="function"&&typeof j!="symbol"&&!!j,a.checked=de?a.checked:!!j,a.defaultChecked=!!j,Q!=null&&typeof Q!="function"&&typeof Q!="symbol"&&typeof Q!="boolean"&&(a.name=Q),p5(a)}function a6(a,d,w){d==="number"&&t3(a.ownerDocument)===a||a.defaultValue===""+w||(a.defaultValue=""+w)}function Vg(a,d,w,j){if(a=a.options,d){d={};for(var T=0;T"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),LA=!1;if(Qg)try{var d6={};Object.defineProperty(d6,"passive",{get:function(){LA=!0}}),window.addEventListener("test",d6,d6),window.removeEventListener("test",d6,d6)}catch{LA=!1}var Op=null,PA=null,lk=null;function xI(){if(lk)return lk;var a,d=PA,w=d.length,j,T="value"in Op?Op.value:Op.textContent,D=T.length;for(a=0;a=w6),NI=" ",DI=!1;function II(a,d){switch(a){case"keyup":return Iq.indexOf(d.keyCode)!==-1;case"keydown":return d.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function _I(a){return a=a.detail,typeof a=="object"&&"data"in a?a.data:null}var k5=!1;function Lq(a,d){switch(a){case"compositionend":return _I(d);case"keypress":return d.which!==32?null:(DI=!0,NI);case"textInput":return a=d.data,a===NI&&DI?null:a;default:return null}}function Pq(a,d){if(k5)return a==="compositionend"||!FA&&II(a,d)?(a=xI(),lk=PA=Op=null,k5=!1,a):null;switch(a){case"paste":return null;case"keypress":if(!(d.ctrlKey||d.altKey||d.metaKey)||d.ctrlKey&&d.altKey){if(d.char&&1=d)return{node:w,offset:d-a};a=j}e:{for(;w;){if(w.nextSibling){w=w.nextSibling;break e}w=w.parentNode}w=void 0}w=FI(w)}}function HI(a,d){return a&&d?a===d?!0:a&&a.nodeType===3?!1:d&&d.nodeType===3?HI(a,d.parentNode):"contains"in a?a.contains(d):a.compareDocumentPosition?!!(a.compareDocumentPosition(d)&16):!1:!1}function GI(a){a=a!=null&&a.ownerDocument!=null&&a.ownerDocument.defaultView!=null?a.ownerDocument.defaultView:window;for(var d=t3(a.document);d instanceof a.HTMLIFrameElement;){try{var w=typeof d.contentWindow.location.href=="string"}catch{w=!1}if(w)a=d.contentWindow;else break;d=t3(a.document)}return d}function qA(a){var d=a&&a.nodeName&&a.nodeName.toLowerCase();return d&&(d==="input"&&(a.type==="text"||a.type==="search"||a.type==="tel"||a.type==="url"||a.type==="password")||d==="textarea"||a.contentEditable==="true")}var Gq=Qg&&"documentMode"in document&&11>=document.documentMode,j5=null,UA=null,y6=null,XA=!1;function qI(a,d,w){var j=w.window===w?w.document:w.nodeType===9?w:w.ownerDocument;XA||j5==null||j5!==t3(j)||(j=j5,"selectionStart"in j&&qA(j)?j={start:j.selectionStart,end:j.selectionEnd}:(j=(j.ownerDocument&&j.ownerDocument.defaultView||window).getSelection(),j={anchorNode:j.anchorNode,anchorOffset:j.anchorOffset,focusNode:j.focusNode,focusOffset:j.focusOffset}),y6&&v6(y6,j)||(y6=j,j=nj(UA,"onSelect"),0>=Q,T-=Q,Sb=1<<32-xf(d)+T|w<Hr?(tc=Ui,Ui=null):tc=Ui.sibling;var qc=Jn(En,Ui,In[Hr],ct);if(qc===null){Ui===null&&(Ui=tc);break}a&&Ui&&qc.alternate===null&&d(En,Ui),on=D(qc,on,Hr),Lu===null?Ji=qc:Lu.sibling=qc,Lu=qc,Ui=tc}if(Hr===In.length)return w(En,Ui),cu&&Zg(En,Hr),Ji;if(Ui===null){for(;HrHr?(tc=Ui,Ui=null):tc=Ui.sibling;var xt=Jn(En,Ui,qc.value,ct);if(xt===null){Ui===null&&(Ui=tc);break}a&&Ui&&xt.alternate===null&&d(En,Ui),on=D(xt,on,Hr),Lu===null?Ji=xt:Lu.sibling=xt,Lu=xt,Ui=tc}if(qc.done)return w(En,Ui),cu&&Zg(En,Hr),Ji;if(Ui===null){for(;!qc.done;Hr++,qc=In.next())qc=gt(En,qc.value,ct),qc!==null&&(on=D(qc,on,Hr),Lu===null?Ji=qc:Lu.sibling=qc,Lu=qc);return cu&&Zg(En,Hr),Ji}for(Ui=j(Ui);!qc.done;Hr++,qc=In.next())qc=Zn(Ui,En,Hr,qc.value,ct),qc!==null&&(a&&qc.alternate!==null&&Ui.delete(qc.key===null?Hr:qc.key),on=D(qc,on,Hr),Lu===null?Ji=qc:Lu.sibling=qc,Lu=qc);return a&&Ui.forEach(function(lX){return d(En,lX)}),cu&&Zg(En,Hr),Ji}function co(En,on,In,ct){if(typeof In=="object"&&In!==null&&In.type===ee&&In.key===null&&(In=In.props.children),typeof In=="object"&&In!==null){switch(In.$$typeof){case se:e:{for(var Ji=In.key;on!==null;){if(on.key===Ji){if(Ji=In.type,Ji===ee){if(on.tag===7){w(En,on.sibling),ct=T(on,In.props.children),ct.return=En,En=ct;break e}}else if(on.elementType===Ji||typeof Ji=="object"&&Ji!==null&&Ji.$$typeof===xn&&a3(Ji)===on.type){w(En,on.sibling),ct=T(on,In.props),C6(ct,In),ct.return=En,En=ct;break e}w(En,on);break}else d(En,on);on=on.sibling}In.type===ee?(ct=s3(In.props.children,En.mode,ct,In.key),ct.return=En,En=ct):(ct=vk(In.type,In.key,In.props,null,En.mode,ct),C6(ct,In),ct.return=En,En=ct)}return Q(En);case le:e:{for(Ji=In.key;on!==null;){if(on.key===Ji)if(on.tag===4&&on.stateNode.containerInfo===In.containerInfo&&on.stateNode.implementation===In.implementation){w(En,on.sibling),ct=T(on,In.children||[]),ct.return=En,En=ct;break e}else{w(En,on);break}else d(En,on);on=on.sibling}ct=eM(In,En.mode,ct),ct.return=En,En=ct}return Q(En);case xn:return In=a3(In),co(En,on,In,ct)}if(mn(In))return Di(En,on,In,ct);if(Qn(In)){if(Ji=Qn(In),typeof Ji!="function")throw Error(M(150));return In=Ji.call(In),Cr(En,on,In,ct)}if(typeof In.then=="function")return co(En,on,Sk(In),ct);if(In.$$typeof===ae)return co(En,on,E6(En,In),ct);xk(En,In)}return typeof In=="string"&&In!==""||typeof In=="number"||typeof In=="bigint"?(In=""+In,on!==null&&on.tag===6?(w(En,on.sibling),ct=T(on,In),ct.return=En,En=ct):(w(En,on),ct=ZA(In,En.mode,ct),ct.return=En,En=ct),Q(En)):w(En,on)}return function(En,on,In,ct){try{M6=0;var Ji=co(En,on,In,ct);return _5=null,Ji}catch(Ui){if(Ui===I5||Ui===jk)throw Ui;var Lu=w1(29,Ui,null,En.mode);return Lu.lanes=ct,Lu.return=En,Lu}}}var d3=h_(!0),d_=h_(!1),Rp=!1;function dM(a){a.updateQueue={baseState:a.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,lanes:0,hiddenCallbacks:null},callbacks:null}}function bM(a,d){a=a.updateQueue,d.updateQueue===a&&(d.updateQueue={baseState:a.baseState,firstBaseUpdate:a.firstBaseUpdate,lastBaseUpdate:a.lastBaseUpdate,shared:a.shared,callbacks:null})}function Bp(a){return{lane:a,tag:0,payload:null,callback:null,next:null}}function zp(a,d,w){var j=a.updateQueue;if(j===null)return null;if(j=j.shared,(Ju&2)!==0){var T=j.pending;return T===null?d.next=d:(d.next=T.next,T.next=d),j.pending=d,d=mk(a),WI(a,null,w),d}return pk(a,j,d,w),mk(a)}function T6(a,d,w){if(d=d.updateQueue,d!==null&&(d=d.shared,(w&4194048)!==0)){var j=d.lanes;j&=a.pendingLanes,w|=j,d.lanes=w,Zm(a,w)}}function gM(a,d){var w=a.updateQueue,j=a.alternate;if(j!==null&&(j=j.updateQueue,w===j)){var T=null,D=null;if(w=w.firstBaseUpdate,w!==null){do{var Q={lane:w.lane,tag:w.tag,payload:w.payload,callback:null,next:null};D===null?T=D=Q:D=D.next=Q,w=w.next}while(w!==null);D===null?T=D=d:D=D.next=d}else T=D=d;w={baseState:j.baseState,firstBaseUpdate:T,lastBaseUpdate:D,shared:j.shared,callbacks:j.callbacks},a.updateQueue=w;return}a=w.lastBaseUpdate,a===null?w.firstBaseUpdate=d:a.next=d,w.lastBaseUpdate=d}var wM=!1;function O6(){if(wM){var a=D5;if(a!==null)throw a}}function N6(a,d,w,j){wM=!1;var T=a.updateQueue;Rp=!1;var D=T.firstBaseUpdate,Q=T.lastBaseUpdate,de=T.shared.pending;if(de!==null){T.shared.pending=null;var en=de,Ln=en.next;en.next=null,Q===null?D=Ln:Q.next=Ln,Q=en;var nt=a.alternate;nt!==null&&(nt=nt.updateQueue,de=nt.lastBaseUpdate,de!==Q&&(de===null?nt.firstBaseUpdate=Ln:de.next=Ln,nt.lastBaseUpdate=en))}if(D!==null){var gt=T.baseState;Q=0,nt=Ln=en=null,de=D;do{var Jn=de.lane&-536870913,Zn=Jn!==de.lane;if(Zn?(nu&Jn)===Jn:(j&Jn)===Jn){Jn!==0&&Jn===N5&&(wM=!0),nt!==null&&(nt=nt.next={lane:0,tag:de.tag,payload:de.payload,callback:null,next:null});e:{var Di=a,Cr=de;Jn=d;var co=w;switch(Cr.tag){case 1:if(Di=Cr.payload,typeof Di=="function"){gt=Di.call(co,gt,Jn);break e}gt=Di;break e;case 3:Di.flags=Di.flags&-65537|128;case 0:if(Di=Cr.payload,Jn=typeof Di=="function"?Di.call(co,gt,Jn):Di,Jn==null)break e;gt=Z({},gt,Jn);break e;case 2:Rp=!0}}Jn=de.callback,Jn!==null&&(a.flags|=64,Zn&&(a.flags|=8192),Zn=T.callbacks,Zn===null?T.callbacks=[Jn]:Zn.push(Jn))}else Zn={lane:Jn,tag:de.tag,payload:de.payload,callback:de.callback,next:null},nt===null?(Ln=nt=Zn,en=gt):nt=nt.next=Zn,Q|=Jn;if(de=de.next,de===null){if(de=T.shared.pending,de===null)break;Zn=de,de=Zn.next,Zn.next=null,T.lastBaseUpdate=Zn,T.shared.pending=null}}while(!0);nt===null&&(en=gt),T.baseState=en,T.firstBaseUpdate=Ln,T.lastBaseUpdate=nt,D===null&&(T.shared.lanes=0),qp|=Q,a.lanes=Q,a.memoizedState=gt}}function b_(a,d){if(typeof a!="function")throw Error(M(191,a));a.call(d)}function g_(a,d){var w=a.callbacks;if(w!==null)for(a.callbacks=null,a=0;aD?D:8;var Q=Ae.T,de={};Ae.T=de,LM(a,!1,d,w);try{var en=T(),Ln=Ae.S;if(Ln!==null&&Ln(de,en),en!==null&&typeof en=="object"&&typeof en.then=="function"){var nt=Wq(en,j);L6(a,d,nt,k1(a))}else L6(a,d,j,k1(a))}catch(gt){L6(a,d,{then:function(){},status:"rejected",reason:gt},k1())}finally{Ze.p=D,Q!==null&&de.types!==null&&(Q.types=de.types),Ae.T=Q}}function IM(){}function _6(a,d,w,j){if(a.tag!==5)throw Error(M(476));var T=U_(a).queue;q_(a,T,d,sn,w===null?IM:function(){return Lk(a),w(j)})}function U_(a){var d=a.memoizedState;if(d!==null)return d;d={memoizedState:sn,baseState:sn,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:iw,lastRenderedState:sn},next:null};var w={};return d.next={memoizedState:w,baseState:w,baseQueue:null,queue:{pending:null,lanes:0,dispatch:null,lastRenderedReducer:iw,lastRenderedState:w},next:null},a.memoizedState=d,a=a.alternate,a!==null&&(a.memoizedState=d),d}function Lk(a){var d=U_(a);d.next===null&&(d=a.alternate.memoizedState),L6(a,d.next.queue,{},k1())}function _M(){return ca(Y5)}function X_(){return nl().memoizedState}function K_(){return nl().memoizedState}function cU(a){for(var d=a.return;d!==null;){switch(d.tag){case 24:case 3:var w=k1();a=Bp(w);var j=zp(d,a,w);j!==null&&(Bh(j,d,w),T6(j,d,w)),d={cache:sM()},a.payload=d;return}d=d.return}}function uU(a,d,w){var j=k1();w={lane:j,revertLane:0,gesture:null,action:w,hasEagerState:!1,eagerState:null,next:null},Pk(a)?Y_(d,w):(w=QA(a,d,w,j),w!==null&&(Bh(w,a,j),PM(w,d,j)))}function V_(a,d,w){var j=k1();L6(a,d,w,j)}function L6(a,d,w,j){var T={lane:j,revertLane:0,gesture:null,action:w,hasEagerState:!1,eagerState:null,next:null};if(Pk(a))Y_(d,T);else{var D=a.alternate;if(a.lanes===0&&(D===null||D.lanes===0)&&(D=d.lastRenderedReducer,D!==null))try{var Q=d.lastRenderedState,de=D(Q,w);if(T.hasEagerState=!0,T.eagerState=de,g1(de,Q))return pk(a,d,T,0),Io===null&&wk(),!1}catch{}if(w=QA(a,d,T,j),w!==null)return Bh(w,a,j),PM(w,d,j),!0}return!1}function LM(a,d,w,j){if(j={lane:2,revertLane:pC(),gesture:null,action:j,hasEagerState:!1,eagerState:null,next:null},Pk(a)){if(d)throw Error(M(479))}else d=QA(a,w,j,2),d!==null&&Bh(d,a,2)}function Pk(a){var d=a.alternate;return a===bc||d!==null&&d===bc}function Y_(a,d){P5=Ck=!0;var w=a.pending;w===null?d.next=d:(d.next=w.next,w.next=d),a.pending=d}function PM(a,d,w){if((w&4194048)!==0){var j=d.lanes;j&=a.pendingLanes,w|=j,d.lanes=w,Zm(a,w)}}var P6={readContext:ca,use:Nk,useCallback:Bs,useContext:Bs,useEffect:Bs,useImperativeHandle:Bs,useLayoutEffect:Bs,useInsertionEffect:Bs,useMemo:Bs,useReducer:Bs,useRef:Bs,useState:Bs,useDebugValue:Bs,useDeferredValue:Bs,useTransition:Bs,useSyncExternalStore:Bs,useId:Bs,useHostTransitionStatus:Bs,useFormState:Bs,useActionState:Bs,useOptimistic:Bs,useMemoCache:Bs,useCacheRefresh:Bs};P6.useEffectEvent=Bs;var oU={readContext:ca,use:Nk,useCallback:function(a,d){return uh().memoizedState=[a,d===void 0?null:d],a},useContext:ca,useEffect:P_,useImperativeHandle:function(a,d,w){w=w!=null?w.concat([a]):null,Ik(4194308,4,z_.bind(null,d,a),w)},useLayoutEffect:function(a,d){return Ik(4194308,4,a,d)},useInsertionEffect:function(a,d){Ik(4,2,a,d)},useMemo:function(a,d){var w=uh();d=d===void 0?null:d;var j=a();if(b3){b1(!0);try{a()}finally{b1(!1)}}return w.memoizedState=[j,d],j},useReducer:function(a,d,w){var j=uh();if(w!==void 0){var T=w(d);if(b3){b1(!0);try{w(d)}finally{b1(!1)}}}else T=d;return j.memoizedState=j.baseState=T,a={pending:null,lanes:0,dispatch:null,lastRenderedReducer:a,lastRenderedState:T},j.queue=a,a=a.dispatch=uU.bind(null,bc,a),[j.memoizedState,a]},useRef:function(a){var d=uh();return a={current:a},d.memoizedState=a},useState:function(a){a=CM(a);var d=a.queue,w=V_.bind(null,bc,d);return d.dispatch=w,[a.memoizedState,w]},useDebugValue:NM,useDeferredValue:function(a,d){var w=uh();return DM(w,a,d)},useTransition:function(){var a=CM(!1);return a=q_.bind(null,bc,a.queue,!0,!1),uh().memoizedState=a,[!1,a]},useSyncExternalStore:function(a,d,w){var j=bc,T=uh();if(cu){if(w===void 0)throw Error(M(407));w=w()}else{if(w=d(),Io===null)throw Error(M(349));(nu&127)!==0||k_(j,d,w)}T.memoizedState=w;var D={value:w,getSnapshot:d};return T.queue=D,P_(nU.bind(null,j,D,a),[a]),j.flags|=2048,R5(9,{destroy:void 0},j_.bind(null,j,D,w,d),null),w},useId:function(){var a=uh(),d=Io.identifierPrefix;if(cu){var w=xb,j=Sb;w=(j&~(1<<32-xf(j)-1)).toString(32)+w,d="_"+d+"R_"+w,w=Tk++,0<\/script>",D=D.removeChild(D.firstChild);break;case"select":D=typeof j.is=="string"?Q.createElement("select",{is:j.is}):Q.createElement("select"),j.multiple?D.multiple=!0:j.size&&(D.size=j.size);break;default:D=typeof j.is=="string"?Q.createElement(T,{is:j.is}):Q.createElement(T)}}D[Zs]=d,D[Ta]=j;e:for(Q=d.child;Q!==null;){if(Q.tag===5||Q.tag===6)D.appendChild(Q.stateNode);else if(Q.tag!==4&&Q.tag!==27&&Q.child!==null){Q.child.return=Q,Q=Q.child;continue}if(Q===d)break e;for(;Q.sibling===null;){if(Q.return===null||Q.return===d)break e;Q=Q.return}Q.sibling.return=Q.return,Q=Q.sibling}d.stateNode=D;e:switch(oa(D,T,j),T){case"button":case"input":case"select":case"textarea":j=!!j.autoFocus;break e;case"img":j=!0;break e;default:j=!1}j&&a0(d)}}return yo(d),YM(d,d.type,a===null?null:a.memoizedProps,d.pendingProps,w),null;case 6:if(a&&d.stateNode!=null)a.memoizedProps!==j&&a0(d);else{if(typeof j!="string"&&d.stateNode===null)throw Error(M(166));if(a=hi.current,C5(d)){if(a=d.stateNode,w=d.memoizedProps,j=null,T=ra,T!==null)switch(T.tag){case 27:case 5:j=T.memoizedProps}a[Zs]=d,a=!!(a.nodeValue===w||j!==null&&j.suppressHydrationWarning===!0||uP(a.nodeValue,w)),a||Ip(d,!0)}else a=tj(a).createTextNode(j),a[Zs]=d,d.stateNode=a}return yo(d),null;case 31:if(w=d.memoizedState,a===null||a.memoizedState!==null){if(j=C5(d),w!==null){if(a===null){if(!j)throw Error(M(318));if(a=d.memoizedState,a=a!==null?a.dehydrated:null,!a)throw Error(M(557));a[Zs]=d}else l3(),(d.flags&128)===0&&(d.memoizedState=null),d.flags|=4;yo(d),a=!1}else w=T5(),a!==null&&a.memoizedState!==null&&(a.memoizedState.hydrationErrors=w),a=!0;if(!a)return d.flags&256?(m1(d),d):(m1(d),null);if((d.flags&128)!==0)throw Error(M(558))}return yo(d),null;case 13:if(j=d.memoizedState,a===null||a.memoizedState!==null&&a.memoizedState.dehydrated!==null){if(T=C5(d),j!==null&&j.dehydrated!==null){if(a===null){if(!T)throw Error(M(318));if(T=d.memoizedState,T=T!==null?T.dehydrated:null,!T)throw Error(M(317));T[Zs]=d}else l3(),(d.flags&128)===0&&(d.memoizedState=null),d.flags|=4;yo(d),T=!1}else T=T5(),a!==null&&a.memoizedState!==null&&(a.memoizedState.hydrationErrors=T),T=!0;if(!T)return d.flags&256?(m1(d),d):(m1(d),null)}return m1(d),(d.flags&128)!==0?(d.lanes=w,d):(w=j!==null,a=a!==null&&a.memoizedState!==null,w&&(j=d.child,T=null,j.alternate!==null&&j.alternate.memoizedState!==null&&j.alternate.memoizedState.cachePool!==null&&(T=j.alternate.memoizedState.cachePool.pool),D=null,j.memoizedState!==null&&j.memoizedState.cachePool!==null&&(D=j.memoizedState.cachePool.pool),D!==T&&(j.flags|=2048)),w!==a&&w&&(d.child.flags|=8192),zk(d,d.updateQueue),yo(d),null);case 4:return st(),a===null&&kC(d.stateNode.containerInfo),yo(d),null;case 10:return nw(d.type),yo(d),null;case 19:if(un(el),j=d.memoizedState,j===null)return yo(d),null;if(T=(d.flags&128)!==0,D=j.rendering,D===null)if(T)w3(j,!1);else{if(zs!==0||a!==null&&(a.flags&128)!==0)for(a=d.child;a!==null;){if(D=Mk(a),D!==null){for(d.flags|=128,w3(j,!1),a=D.updateQueue,d.updateQueue=a,zk(d,a),d.subtreeFlags=0,a=w,w=d.child;w!==null;)ZI(w,a),w=w.sibling;return rt(el,el.current&1|2),cu&&Zg(d,j.treeForkCount),d.child}a=a.sibling}j.tail!==null&&Rs()>Uk&&(d.flags|=128,T=!0,w3(j,!1),d.lanes=4194304)}else{if(!T)if(a=Mk(D),a!==null){if(d.flags|=128,T=!0,a=a.updateQueue,d.updateQueue=a,zk(d,a),w3(j,!0),j.tail===null&&j.tailMode==="hidden"&&!D.alternate&&!cu)return yo(d),null}else 2*Rs()-j.renderingStartTime>Uk&&w!==536870912&&(d.flags|=128,T=!0,w3(j,!1),d.lanes=4194304);j.isBackwards?(D.sibling=d.child,d.child=D):(a=j.last,a!==null?a.sibling=D:d.child=D,j.last=D)}return j.tail!==null?(a=j.tail,j.rendering=a,j.tail=a.sibling,j.renderingStartTime=Rs(),a.sibling=null,w=el.current,rt(el,T?w&1|2:w&1),cu&&Zg(d,j.treeForkCount),a):(yo(d),null);case 22:case 23:return m1(d),mM(),j=d.memoizedState!==null,a!==null?a.memoizedState!==null!==j&&(d.flags|=8192):j&&(d.flags|=8192),j?(w&536870912)!==0&&(d.flags&128)===0&&(yo(d),d.subtreeFlags&6&&(d.flags|=8192)):yo(d),w=d.updateQueue,w!==null&&zk(d,w.retryQueue),w=null,a!==null&&a.memoizedState!==null&&a.memoizedState.cachePool!==null&&(w=a.memoizedState.cachePool.pool),j=null,d.memoizedState!==null&&d.memoizedState.cachePool!==null&&(j=d.memoizedState.cachePool.pool),j!==w&&(d.flags|=2048),a!==null&&un(f3),null;case 24:return w=null,a!==null&&(w=a.memoizedState.cache),d.memoizedState.cache!==w&&(d.flags|=2048),nw(Al),yo(d),null;case 25:return null;case 30:return null}throw Error(M(156,d.tag))}function aU(a,d){switch(tM(d),d.tag){case 1:return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 3:return nw(Al),st(),a=d.flags,(a&65536)!==0&&(a&128)===0?(d.flags=a&-65537|128,d):null;case 26:case 27:case 5:return Mr(d),null;case 31:if(d.memoizedState!==null){if(m1(d),d.alternate===null)throw Error(M(340));l3()}return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 13:if(m1(d),a=d.memoizedState,a!==null&&a.dehydrated!==null){if(d.alternate===null)throw Error(M(340));l3()}return a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 19:return un(el),null;case 4:return st(),null;case 10:return nw(d.type),null;case 22:case 23:return m1(d),mM(),a!==null&&un(f3),a=d.flags,a&65536?(d.flags=a&-65537|128,d):null;case 24:return nw(Al),null;case 25:return null;default:return null}}function QM(a,d){switch(tM(d),d.tag){case 3:nw(Al),st();break;case 26:case 27:case 5:Mr(d);break;case 4:st();break;case 31:d.memoizedState!==null&&m1(d);break;case 13:m1(d);break;case 19:un(el);break;case 10:nw(d.type);break;case 22:case 23:m1(d),mM(),a!==null&&un(f3);break;case 24:nw(Al)}}function B6(a,d){try{var w=d.updateQueue,j=w!==null?w.lastEffect:null;if(j!==null){var T=j.next;w=T;do{if((w.tag&a)===a){j=void 0;var D=w.create,Q=w.inst;j=D(),Q.destroy=j}w=w.next}while(w!==T)}}catch(de){ro(d,d.return,de)}}function Hp(a,d,w){try{var j=d.updateQueue,T=j!==null?j.lastEffect:null;if(T!==null){var D=T.next;j=D;do{if((j.tag&a)===a){var Q=j.inst,de=Q.destroy;if(de!==void 0){Q.destroy=void 0,T=d;var en=w,Ln=de;try{Ln()}catch(nt){ro(T,en,nt)}}}j=j.next}while(j!==D)}}catch(nt){ro(d,d.return,nt)}}function z6(a){var d=a.updateQueue;if(d!==null){var w=a.stateNode;try{g_(d,w)}catch(j){ro(a,a.return,j)}}}function pL(a,d,w){w.props=g3(a.type,a.memoizedProps),w.state=a.memoizedState;try{w.componentWillUnmount()}catch(j){ro(a,d,j)}}function F6(a,d){try{var w=a.ref;if(w!==null){switch(a.tag){case 26:case 27:case 5:var j=a.stateNode;break;case 30:j=a.stateNode;break;default:j=a.stateNode}typeof w=="function"?a.refCleanup=w(j):w.current=j}}catch(T){ro(a,d,T)}}function Ab(a,d){var w=a.ref,j=a.refCleanup;if(w!==null)if(typeof j=="function")try{j()}catch(T){ro(a,d,T)}finally{a.refCleanup=null,a=a.alternate,a!=null&&(a.refCleanup=null)}else if(typeof w=="function")try{w(null)}catch(T){ro(a,d,T)}else w.current=null}function J6(a){var d=a.type,w=a.memoizedProps,j=a.stateNode;try{e:switch(d){case"button":case"input":case"select":case"textarea":w.autoFocus&&j.focus();break e;case"img":w.src?j.src=w.src:w.srcSet&&(j.srcset=w.srcSet)}}catch(T){ro(a,a.return,T)}}function WM(a,d,w){try{var j=a.stateNode;DU(j,a.type,w,d),j[Ta]=d}catch(T){ro(a,a.return,T)}}function mL(a){return a.tag===5||a.tag===3||a.tag===26||a.tag===27&&Yp(a.type)||a.tag===4}function ZM(a){e:for(;;){for(;a.sibling===null;){if(a.return===null||mL(a.return))return null;a=a.return}for(a.sibling.return=a.return,a=a.sibling;a.tag!==5&&a.tag!==6&&a.tag!==18;){if(a.tag===27&&Yp(a.type)||a.flags&2||a.child===null||a.tag===4)continue e;a.child.return=a,a=a.child}if(!(a.flags&2))return a.stateNode}}function eC(a,d,w){var j=a.tag;if(j===5||j===6)a=a.stateNode,d?(w.nodeType===9?w.body:w.nodeName==="HTML"?w.ownerDocument.body:w).insertBefore(a,d):(d=w.nodeType===9?w.body:w.nodeName==="HTML"?w.ownerDocument.body:w,d.appendChild(a),w=w._reactRootContainer,w!=null||d.onclick!==null||(d.onclick=Yg));else if(j!==4&&(j===27&&Yp(a.type)&&(w=a.stateNode,d=null),a=a.child,a!==null))for(eC(a,d,w),a=a.sibling;a!==null;)eC(a,d,w),a=a.sibling}function p3(a,d,w){var j=a.tag;if(j===5||j===6)a=a.stateNode,d?w.insertBefore(a,d):w.appendChild(a);else if(j!==4&&(j===27&&Yp(a.type)&&(w=a.stateNode),a=a.child,a!==null))for(p3(a,d,w),a=a.sibling;a!==null;)p3(a,d,w),a=a.sibling}function vL(a){var d=a.stateNode,w=a.memoizedProps;try{for(var j=a.type,T=d.attributes;T.length;)d.removeAttributeNode(T[0]);oa(d,j,w),d[Zs]=a,d[Ta]=w}catch(D){ro(a,a.return,D)}}var Mb=!1,Tl=!1,H6=!1,nC=typeof WeakSet=="function"?WeakSet:Set,Mf=null;function hU(a,d){if(a=a.containerInfo,SC=Cf,a=GI(a),qA(a)){if("selectionStart"in a)var w={start:a.selectionStart,end:a.selectionEnd};else e:{w=(w=a.ownerDocument)&&w.defaultView||window;var j=w.getSelection&&w.getSelection();if(j&&j.rangeCount!==0){w=j.anchorNode;var T=j.anchorOffset,D=j.focusNode;j=j.focusOffset;try{w.nodeType,D.nodeType}catch{w=null;break e}var Q=0,de=-1,en=-1,Ln=0,nt=0,gt=a,Jn=null;n:for(;;){for(var Zn;gt!==w||T!==0&>.nodeType!==3||(de=Q+T),gt!==D||j!==0&>.nodeType!==3||(en=Q+j),gt.nodeType===3&&(Q+=gt.nodeValue.length),(Zn=gt.firstChild)!==null;)Jn=gt,gt=Zn;for(;;){if(gt===a)break n;if(Jn===w&&++Ln===T&&(de=Q),Jn===D&&++nt===j&&(en=Q),(Zn=gt.nextSibling)!==null)break;gt=Jn,Jn=gt.parentNode}gt=Zn}w=de===-1||en===-1?null:{start:de,end:en}}else w=null}w=w||{start:0,end:0}}else w=null;for(xC={focusedElem:a,selectionRange:w},Cf=!1,Mf=d;Mf!==null;)if(d=Mf,a=d.child,(d.subtreeFlags&1028)!==0&&a!==null)a.return=d,Mf=a;else for(;Mf!==null;){switch(d=Mf,D=d.alternate,a=d.flags,d.tag){case 0:if((a&4)!==0&&(a=d.updateQueue,a=a!==null?a.events:null,a!==null))for(w=0;w title"))),oa(D,j,w),D[Zs]=a,xl(D),j=D;break e;case"link":var Q=vP("link","href",T).get(j+(w.href||""));if(Q){for(var de=0;deco&&(Q=co,co=Cr,Cr=Q);var En=JI(de,Cr),on=JI(de,co);if(En&&on&&(Zn.rangeCount!==1||Zn.anchorNode!==En.node||Zn.anchorOffset!==En.offset||Zn.focusNode!==on.node||Zn.focusOffset!==on.offset)){var In=gt.createRange();In.setStart(En.node,En.offset),Zn.removeAllRanges(),Cr>co?(Zn.addRange(In),Zn.extend(on.node,on.offset)):(In.setEnd(on.node,on.offset),Zn.addRange(In))}}}}for(gt=[],Zn=de;Zn=Zn.parentNode;)Zn.nodeType===1&>.push({element:Zn,left:Zn.scrollLeft,top:Zn.scrollTop});for(typeof de.focus=="function"&&de.focus(),de=0;dew?32:w,Ae.T=null,w=sC,sC=null;var D=Xp,Q=lw;if(nf=0,H5=Xp=null,lw=0,(Ju&6)!==0)throw Error(M(331));var de=Ju;if(Ju|=4,TL(D.current),AL(D,D.current,Q,w),Ju=de,V6(0,!1),Sf&&typeof Sf.onPostCommitFiberRoot=="function")try{Sf.onPostCommitFiberRoot(ud,D)}catch{}return!0}finally{Ze.p=T,Ae.T=j,XL(a,d)}}function VL(a,d,w){d=sd(w,d),d=FM(a.stateNode,d,2),a=zp(a,d,2),a!==null&&(qg(a,2),Cb(a))}function ro(a,d,w){if(a.tag===3)VL(a,a,w);else for(;d!==null;){if(d.tag===3){VL(d,a,w);break}else if(d.tag===1){var j=d.stateNode;if(typeof d.type.getDerivedStateFromError=="function"||typeof j.componentDidCatch=="function"&&(Up===null||!Up.has(j))){a=sd(w,a),w=tL(2),j=zp(d,w,2),j!==null&&(iL(w,j,d,a),qg(j,2),Cb(j));break}}d=d.return}}function hC(a,d,w){var j=a.pingCache;if(j===null){j=a.pingCache=new gU;var T=new Set;j.set(d,T)}else T=j.get(d),T===void 0&&(T=new Set,j.set(d,T));T.has(w)||(rC=!0,T.add(w),a=yU.bind(null,a,d,w),d.then(a,a))}function yU(a,d,w){var j=a.pingCache;j!==null&&j.delete(d),a.pingedLanes|=a.suspendedLanes&w,a.warmLanes&=~w,Io===a&&(nu&w)===w&&(zs===4||zs===3&&(nu&62914560)===nu&&300>Rs()-qk?(Ju&2)===0&&G5(a,0):cC|=w,J5===nu&&(J5=0)),Cb(a)}function YL(a,d){d===0&&(d=h5()),a=o3(a,d),a!==null&&(qg(a,d),Cb(a))}function kU(a){var d=a.memoizedState,w=0;d!==null&&(w=d.retryLane),YL(a,w)}function jU(a,d){var w=0;switch(a.tag){case 31:case 13:var j=a.stateNode,T=a.memoizedState;T!==null&&(w=T.retryLane);break;case 19:j=a.stateNode;break;case 22:j=a.stateNode._retryCache;break;default:throw Error(M(314))}j!==null&&j.delete(d),YL(a,w)}function EU(a,d){return ch(a,d)}var Wk=null,U5=null,dC=!1,Zk=!1,bC=!1,Vp=0;function Cb(a){a!==U5&&a.next===null&&(U5===null?Wk=U5=a:U5=U5.next=a),Zk=!0,dC||(dC=!0,wC())}function V6(a,d){if(!bC&&Zk){bC=!0;do for(var w=!1,j=Wk;j!==null;){if(a!==0){var T=j.pendingLanes;if(T===0)var D=0;else{var Q=j.suspendedLanes,de=j.pingedLanes;D=(1<<31-xf(42|a)+1)-1,D&=T&~(Q&~de),D=D&201326741?D&201326741|1:D?D|2:0}D!==0&&(w=!0,ZL(j,D))}else D=nu,D=Hg(j,j===Io?D:0,j.cancelPendingCommit!==null||j.timeoutHandle!==-1),(D&3)===0||Gg(j,D)||(w=!0,ZL(j,D));j=j.next}while(w);bC=!1}}function SU(){QL()}function QL(){Zk=dC=!1;var a=0;Vp!==0&&_U()&&(a=Vp);for(var d=Rs(),w=null,j=Wk;j!==null;){var T=j.next,D=gC(j,d);D===0?(j.next=null,w===null?Wk=T:w.next=T,T===null&&(U5=w)):(w=j,(a!==0||(D&3)!==0)&&(Zk=!0)),j=T}nf!==0&&nf!==5||V6(a),Vp!==0&&(Vp=0)}function gC(a,d){for(var w=a.suspendedLanes,j=a.pingedLanes,T=a.expirationTimes,D=a.pendingLanes&-62914561;0de)break;var nt=en.transferSize,gt=en.initiatorType;nt&&oP(gt)&&(en=en.responseEnd,Q+=nt*(en"u"?null:document;function gP(a,d,w){var j=X5;if(j&&typeof d=="string"&&d){var T=_h(d);T='link[rel="'+a+'"][href="'+T+'"]',typeof w=="string"&&(T+='[crossorigin="'+w+'"]'),DC.has(T)||(DC.add(T),a={rel:a,crossOrigin:w,href:d},j.querySelector(T)===null&&(d=j.createElement("link"),oa(d,"link",a),xl(d),j.head.appendChild(d)))}}function HU(a){b0.D(a),gP("dns-prefetch",a,null)}function GU(a,d){b0.C(a,d),gP("preconnect",a,d)}function IC(a,d,w){b0.L(a,d,w);var j=X5;if(j&&a&&d){var T='link[rel="preload"][as="'+_h(d)+'"]';d==="image"&&w&&w.imageSrcSet?(T+='[imagesrcset="'+_h(w.imageSrcSet)+'"]',typeof w.imageSizes=="string"&&(T+='[imagesizes="'+_h(w.imageSizes)+'"]')):T+='[href="'+_h(a)+'"]';var D=T;switch(d){case"style":D=K5(a);break;case"script":D=V5(a)}E1.has(D)||(a=Z({rel:"preload",href:d==="image"&&w&&w.imageSrcSet?void 0:a,as:d},w),E1.set(D,a),j.querySelector(T)!==null||d==="style"&&j.querySelector(y3(D))||d==="script"&&j.querySelector(e9(D))||(d=j.createElement("link"),oa(d,"link",a),xl(d),j.head.appendChild(d)))}}function qU(a,d){b0.m(a,d);var w=X5;if(w&&a){var j=d&&typeof d.as=="string"?d.as:"script",T='link[rel="modulepreload"][as="'+_h(j)+'"][href="'+_h(a)+'"]',D=T;switch(j){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":D=V5(a)}if(!E1.has(D)&&(a=Z({rel:"modulepreload",href:a},d),E1.set(D,a),w.querySelector(T)===null)){switch(j){case"audioworklet":case"paintworklet":case"serviceworker":case"sharedworker":case"worker":case"script":if(w.querySelector(e9(D)))return}j=w.createElement("link"),oa(j,"link",a),xl(j),w.head.appendChild(j)}}}function UU(a,d,w){b0.S(a,d,w);var j=X5;if(j&&a){var T=Cp(j).hoistableStyles,D=K5(a);d=d||"default";var Q=T.get(D);if(!Q){var de={loading:0,preload:null};if(Q=j.querySelector(y3(D)))de.loading=5;else{a=Z({rel:"stylesheet",href:a,"data-precedence":d},w),(w=E1.get(D))&&PC(a,w);var en=Q=j.createElement("link");xl(en),oa(en,"link",a),en._p=new Promise(function(Ln,nt){en.onload=Ln,en.onerror=nt}),en.addEventListener("load",function(){de.loading|=1}),en.addEventListener("error",function(){de.loading|=2}),de.loading|=4,cj(Q,d,j)}Q={type:"stylesheet",instance:Q,count:1,state:de},T.set(D,Q)}}}function XU(a,d){b0.X(a,d);var w=X5;if(w&&a){var j=Cp(w).hoistableScripts,T=V5(a),D=j.get(T);D||(D=w.querySelector(e9(T)),D||(a=Z({src:a,async:!0},d),(d=E1.get(T))&&$C(a,d),D=w.createElement("script"),xl(D),oa(D,"link",a),w.head.appendChild(D)),D={type:"script",instance:D,count:1,state:null},j.set(T,D))}}function _C(a,d){b0.M(a,d);var w=X5;if(w&&a){var j=Cp(w).hoistableScripts,T=V5(a),D=j.get(T);D||(D=w.querySelector(e9(T)),D||(a=Z({src:a,async:!0,type:"module"},d),(d=E1.get(T))&&$C(a,d),D=w.createElement("script"),xl(D),oa(D,"link",a),w.head.appendChild(D)),D={type:"script",instance:D,count:1,state:null},j.set(T,D))}}function wP(a,d,w,j){var T=(T=hi.current)?rj(T):null;if(!T)throw Error(M(446));switch(a){case"meta":case"title":return null;case"style":return typeof w.precedence=="string"&&typeof w.href=="string"?(d=K5(w.href),w=Cp(T).hoistableStyles,j=w.get(d),j||(j={type:"style",instance:null,count:0,state:null},w.set(d,j)),j):{type:"void",instance:null,count:0,state:null};case"link":if(w.rel==="stylesheet"&&typeof w.href=="string"&&typeof w.precedence=="string"){a=K5(w.href);var D=Cp(T).hoistableStyles,Q=D.get(a);if(Q||(T=T.ownerDocument||T,Q={type:"stylesheet",instance:null,count:0,state:{loading:0,preload:null}},D.set(a,Q),(D=T.querySelector(y3(a)))&&!D._p&&(Q.instance=D,Q.state.loading=5),E1.has(a)||(w={rel:"preload",as:"style",href:w.href,crossOrigin:w.crossOrigin,integrity:w.integrity,media:w.media,hrefLang:w.hrefLang,referrerPolicy:w.referrerPolicy},E1.set(a,w),D||LC(T,a,w,Q.state))),d&&j===null)throw Error(M(528,""));return Q}if(d&&j!==null)throw Error(M(529,""));return null;case"script":return d=w.async,w=w.src,typeof w=="string"&&d&&typeof d!="function"&&typeof d!="symbol"?(d=V5(w),w=Cp(T).hoistableScripts,j=w.get(d),j||(j={type:"script",instance:null,count:0,state:null},w.set(d,j)),j):{type:"void",instance:null,count:0,state:null};default:throw Error(M(444,a))}}function K5(a){return'href="'+_h(a)+'"'}function y3(a){return'link[rel="stylesheet"]['+a+"]"}function pP(a){return Z({},a,{"data-precedence":a.precedence,precedence:null})}function LC(a,d,w,j){a.querySelector('link[rel="preload"][as="style"]['+d+"]")?j.loading=1:(d=a.createElement("link"),j.preload=d,d.addEventListener("load",function(){return j.loading|=1}),d.addEventListener("error",function(){return j.loading|=2}),oa(d,"link",w),xl(d),a.head.appendChild(d))}function V5(a){return'[src="'+_h(a)+'"]'}function e9(a){return"script[async]"+a}function mP(a,d,w){if(d.count++,d.instance===null)switch(d.type){case"style":var j=a.querySelector('style[data-href~="'+_h(w.href)+'"]');if(j)return d.instance=j,xl(j),j;var T=Z({},w,{"data-href":w.href,"data-precedence":w.precedence,href:null,precedence:null});return j=(a.ownerDocument||a).createElement("style"),xl(j),oa(j,"style",T),cj(j,w.precedence,a),d.instance=j;case"stylesheet":T=K5(w.href);var D=a.querySelector(y3(T));if(D)return d.state.loading|=4,d.instance=D,xl(D),D;j=pP(w),(T=E1.get(T))&&PC(j,T),D=(a.ownerDocument||a).createElement("link"),xl(D);var Q=D;return Q._p=new Promise(function(de,en){Q.onload=de,Q.onerror=en}),oa(D,"link",j),d.state.loading|=4,cj(D,w.precedence,a),d.instance=D;case"script":return D=V5(w.src),(T=a.querySelector(e9(D)))?(d.instance=T,xl(T),T):(j=w,(T=E1.get(D))&&(j=Z({},w),$C(j,T)),a=a.ownerDocument||a,T=a.createElement("script"),xl(T),oa(T,"link",j),a.head.appendChild(T),d.instance=T);case"void":return null;default:throw Error(M(443,d.type))}else d.type==="stylesheet"&&(d.state.loading&4)===0&&(j=d.instance,d.state.loading|=4,cj(j,w.precedence,a));return d.instance}function cj(a,d,w){for(var j=w.querySelectorAll('link[rel="stylesheet"][data-precedence],style[data-precedence]'),T=j.length?j[j.length-1]:null,D=T,Q=0;Q title"):null)}function KU(a,d,w){if(w===1||d.itemProp!=null)return!1;switch(a){case"meta":case"title":return!0;case"style":if(typeof d.precedence!="string"||typeof d.href!="string"||d.href==="")break;return!0;case"link":if(typeof d.rel!="string"||typeof d.href!="string"||d.href===""||d.onLoad||d.onError)break;return d.rel==="stylesheet"?(a=d.disabled,typeof d.precedence=="string"&&a==null):!0;case"script":if(d.async&&typeof d.async!="function"&&typeof d.async!="symbol"&&!d.onLoad&&!d.onError&&d.src&&typeof d.src=="string")return!0}return!1}function kP(a){return!(a.type==="stylesheet"&&(a.state.loading&3)===0)}function VU(a,d,w,j){if(w.type==="stylesheet"&&(typeof j.media!="string"||matchMedia(j.media).matches!==!1)&&(w.state.loading&4)===0){if(w.instance===null){var T=K5(j.href),D=d.querySelector(y3(T));if(D){d=D._p,d!==null&&typeof d=="object"&&typeof d.then=="function"&&(a.count++,a=n9.bind(a),d.then(a,a)),w.state.loading|=4,w.instance=D,xl(D);return}D=d.ownerDocument||d,j=pP(j),(T=E1.get(T))&&PC(j,T),D=D.createElement("link"),xl(D);var Q=D;Q._p=new Promise(function(de,en){Q.onload=de,Q.onerror=en}),oa(D,"link",j),w.instance=D}a.stylesheets===null&&(a.stylesheets=new Map),a.stylesheets.set(w,d),(d=w.state.preload)&&(w.state.loading&3)===0&&(a.count++,w=n9.bind(a),d.addEventListener("load",w),d.addEventListener("error",w))}}var RC=0;function YU(a,d){return a.stylesheets&&a.count===0&&k3(a,a.stylesheets),0RC?50:800)+d);return a.unsuspend=w,function(){a.unsuspend=null,clearTimeout(j),clearTimeout(T)}}:null}function n9(){if(this.count--,this.count===0&&(this.imgCount===0||!this.waitingForImages)){if(this.stylesheets)k3(this,this.stylesheets);else if(this.unsuspend){var a=this.unsuspend;this.unsuspend=null,a()}}}var t9=null;function k3(a,d){a.stylesheets=null,a.unsuspend!==null&&(a.count++,t9=new Map,d.forEach(i9,a),t9=null,n9.call(a))}function i9(a,d){if(!(d.state.loading&4)){var w=t9.get(a);if(w)var j=w.get(null);else{w=new Map,t9.set(a,w);for(var T=a.querySelectorAll("link[data-precedence],style[data-precedence]"),D=0;D"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(g)}catch(E){console.error(E)}}return g(),O7e.exports=ozn(),O7e.exports}var lzn=szn();function Ca(g){if(typeof g=="string"||typeof g=="number")return""+g;let E="";if(Array.isArray(g))for(let x=0,M;x{}};function Oue(){for(var g=0,E=arguments.length,x={},M;g=0&&(M=x.slice(N+1),x=x.slice(0,N)),x&&!E.hasOwnProperty(x))throw new Error("unknown type: "+x);return{type:x,name:M}})}aue.prototype=Oue.prototype={constructor:aue,on:function(g,E){var x=this._,M=azn(g+"",x),N,P=-1,k=M.length;if(arguments.length<2){for(;++P0)for(var x=new Array(N),M=0,N,P;M=0&&(E=g.slice(0,x))!=="xmlns"&&(g=g.slice(x+1)),Uhn.hasOwnProperty(E)?{space:Uhn[E],local:g}:g}function dzn(g){return function(){var E=this.ownerDocument,x=this.namespaceURI;return x===Z7e&&E.documentElement.namespaceURI===Z7e?E.createElement(g):E.createElementNS(x,g)}}function bzn(g){return function(){return this.ownerDocument.createElementNS(g.space,g.local)}}function sdn(g){var E=Nue(g);return(E.local?bzn:dzn)(E)}function gzn(){}function gke(g){return g==null?gzn:function(){return this.querySelector(g)}}function wzn(g){typeof g!="function"&&(g=gke(g));for(var E=this._groups,x=E.length,M=new Array(x),N=0;N=ae&&(ae=Pe+1);!(Be=Oe[ae])&&++ae=0;)(k=M[N])&&(P&&k.compareDocumentPosition(P)^4&&P.parentNode.insertBefore(k,P),P=k);return this}function Fzn(g){g||(g=Jzn);function E(Z,W){return Z&&W?g(Z.__data__,W.__data__):!Z-!W}for(var x=this._groups,M=x.length,N=new Array(M),P=0;PE?1:g>=E?0:NaN}function Hzn(){var g=arguments[0];return arguments[0]=this,g.apply(null,arguments),this}function Gzn(){return Array.from(this)}function qzn(){for(var g=this._groups,E=0,x=g.length;E1?this.each((E==null?tFn:typeof E=="function"?rFn:iFn)(g,E,x??"")):hI(this.node(),g)}function hI(g,E){return g.style.getPropertyValue(E)||ddn(g).getComputedStyle(g,null).getPropertyValue(E)}function uFn(g){return function(){delete this[g]}}function oFn(g,E){return function(){this[g]=E}}function sFn(g,E){return function(){var x=E.apply(this,arguments);x==null?delete this[g]:this[g]=x}}function lFn(g,E){return arguments.length>1?this.each((E==null?uFn:typeof E=="function"?sFn:oFn)(g,E)):this.node()[g]}function bdn(g){return g.trim().split(/^|\s+/)}function wke(g){return g.classList||new gdn(g)}function gdn(g){this._node=g,this._names=bdn(g.getAttribute("class")||"")}gdn.prototype={add:function(g){var E=this._names.indexOf(g);E<0&&(this._names.push(g),this._node.setAttribute("class",this._names.join(" ")))},remove:function(g){var E=this._names.indexOf(g);E>=0&&(this._names.splice(E,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(g){return this._names.indexOf(g)>=0}};function wdn(g,E){for(var x=wke(g),M=-1,N=E.length;++M=0&&(x=E.slice(M+1),E=E.slice(0,M)),{type:E,name:x}})}function $Fn(g){return function(){var E=this.__on;if(E){for(var x=0,M=-1,N=E.length,P;x()=>g;function eke(g,{sourceEvent:E,subject:x,target:M,identifier:N,active:P,x:k,y:H,dx:U,dy:G,dispatch:ie}){Object.defineProperties(this,{type:{value:g,enumerable:!0,configurable:!0},sourceEvent:{value:E,enumerable:!0,configurable:!0},subject:{value:x,enumerable:!0,configurable:!0},target:{value:M,enumerable:!0,configurable:!0},identifier:{value:N,enumerable:!0,configurable:!0},active:{value:P,enumerable:!0,configurable:!0},x:{value:k,enumerable:!0,configurable:!0},y:{value:H,enumerable:!0,configurable:!0},dx:{value:U,enumerable:!0,configurable:!0},dy:{value:G,enumerable:!0,configurable:!0},_:{value:ie}})}eke.prototype.on=function(){var g=this._.on.apply(this._,arguments);return g===this._?this:g};function XFn(g){return!g.ctrlKey&&!g.button}function KFn(){return this.parentNode}function VFn(g,E){return E??{x:g.x,y:g.y}}function YFn(){return navigator.maxTouchPoints||"ontouchstart"in this}function jdn(){var g=XFn,E=KFn,x=VFn,M=YFn,N={},P=Oue("start","drag","end"),k=0,H,U,G,ie,Z=0;function W(Me){Me.on("mousedown.drag",se).filter(M).on("touchstart.drag",Oe).on("touchmove.drag",ge,UFn).on("touchend.drag touchcancel.drag",Pe).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function se(Me,Be){if(!(ie||!g.call(this,Me,Be))){var rn=ae(this,E.call(this,Me,Be),Me,Be,"mouse");rn&&($g(Me.view).on("mousemove.drag",le,zG).on("mouseup.drag",ee,zG),ydn(Me.view),_7e(Me),G=!1,H=Me.clientX,U=Me.clientY,rn("start",Me))}}function le(Me){if(fI(Me),!G){var Be=Me.clientX-H,rn=Me.clientY-U;G=Be*Be+rn*rn>Z}N.mouse("drag",Me)}function ee(Me){$g(Me.view).on("mousemove.drag mouseup.drag",null),kdn(Me.view,G),fI(Me),N.mouse("end",Me)}function Oe(Me,Be){if(g.call(this,Me,Be)){var rn=Me.changedTouches,ln=E.call(this,Me,Be),xn=rn.length,hn,wt;for(hn=0;hn>8&15|E>>4&240,E>>4&15|E&240,(E&15)<<4|E&15,1):x===8?Qce(E>>24&255,E>>16&255,E>>8&255,(E&255)/255):x===4?Qce(E>>12&15|E>>8&240,E>>8&15|E>>4&240,E>>4&15|E&240,((E&15)<<4|E&15)/255):null):(E=WFn.exec(g))?new kb(E[1],E[2],E[3],1):(E=ZFn.exec(g))?new kb(E[1]*255/100,E[2]*255/100,E[3]*255/100,1):(E=eJn.exec(g))?Qce(E[1],E[2],E[3],E[4]):(E=nJn.exec(g))?Qce(E[1]*255/100,E[2]*255/100,E[3]*255/100,E[4]):(E=tJn.exec(g))?Zhn(E[1],E[2]/100,E[3]/100,1):(E=iJn.exec(g))?Zhn(E[1],E[2]/100,E[3]/100,E[4]):Xhn.hasOwnProperty(g)?Yhn(Xhn[g]):g==="transparent"?new kb(NaN,NaN,NaN,0):null}function Yhn(g){return new kb(g>>16&255,g>>8&255,g&255,1)}function Qce(g,E,x,M){return M<=0&&(g=E=x=NaN),new kb(g,E,x,M)}function uJn(g){return g instanceof eq||(g=EA(g)),g?(g=g.rgb(),new kb(g.r,g.g,g.b,g.opacity)):new kb}function nke(g,E,x,M){return arguments.length===1?uJn(g):new kb(g,E,x,M??1)}function kb(g,E,x,M){this.r=+g,this.g=+E,this.b=+x,this.opacity=+M}pke(kb,nke,Edn(eq,{brighter(g){return g=g==null?mue:Math.pow(mue,g),new kb(this.r*g,this.g*g,this.b*g,this.opacity)},darker(g){return g=g==null?FG:Math.pow(FG,g),new kb(this.r*g,this.g*g,this.b*g,this.opacity)},rgb(){return this},clamp(){return new kb(kA(this.r),kA(this.g),kA(this.b),vue(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Qhn,formatHex:Qhn,formatHex8:oJn,formatRgb:Whn,toString:Whn}));function Qhn(){return`#${yA(this.r)}${yA(this.g)}${yA(this.b)}`}function oJn(){return`#${yA(this.r)}${yA(this.g)}${yA(this.b)}${yA((isNaN(this.opacity)?1:this.opacity)*255)}`}function Whn(){const g=vue(this.opacity);return`${g===1?"rgb(":"rgba("}${kA(this.r)}, ${kA(this.g)}, ${kA(this.b)}${g===1?")":`, ${g})`}`}function vue(g){return isNaN(g)?1:Math.max(0,Math.min(1,g))}function kA(g){return Math.max(0,Math.min(255,Math.round(g)||0))}function yA(g){return g=kA(g),(g<16?"0":"")+g.toString(16)}function Zhn(g,E,x,M){return M<=0?g=E=x=NaN:x<=0||x>=1?g=E=NaN:E<=0&&(g=NaN),new Xm(g,E,x,M)}function Sdn(g){if(g instanceof Xm)return new Xm(g.h,g.s,g.l,g.opacity);if(g instanceof eq||(g=EA(g)),!g)return new Xm;if(g instanceof Xm)return g;g=g.rgb();var E=g.r/255,x=g.g/255,M=g.b/255,N=Math.min(E,x,M),P=Math.max(E,x,M),k=NaN,H=P-N,U=(P+N)/2;return H?(E===P?k=(x-M)/H+(x0&&U<1?0:k,new Xm(k,H,U,g.opacity)}function sJn(g,E,x,M){return arguments.length===1?Sdn(g):new Xm(g,E,x,M??1)}function Xm(g,E,x,M){this.h=+g,this.s=+E,this.l=+x,this.opacity=+M}pke(Xm,sJn,Edn(eq,{brighter(g){return g=g==null?mue:Math.pow(mue,g),new Xm(this.h,this.s,this.l*g,this.opacity)},darker(g){return g=g==null?FG:Math.pow(FG,g),new Xm(this.h,this.s,this.l*g,this.opacity)},rgb(){var g=this.h%360+(this.h<0)*360,E=isNaN(g)||isNaN(this.s)?0:this.s,x=this.l,M=x+(x<.5?x:1-x)*E,N=2*x-M;return new kb(L7e(g>=240?g-240:g+120,N,M),L7e(g,N,M),L7e(g<120?g+240:g-120,N,M),this.opacity)},clamp(){return new Xm(e1n(this.h),Wce(this.s),Wce(this.l),vue(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const g=vue(this.opacity);return`${g===1?"hsl(":"hsla("}${e1n(this.h)}, ${Wce(this.s)*100}%, ${Wce(this.l)*100}%${g===1?")":`, ${g})`}`}}));function e1n(g){return g=(g||0)%360,g<0?g+360:g}function Wce(g){return Math.max(0,Math.min(1,g||0))}function L7e(g,E,x){return(g<60?E+(x-E)*g/60:g<180?x:g<240?E+(x-E)*(240-g)/60:E)*255}const mke=g=>()=>g;function lJn(g,E){return function(x){return g+x*E}}function fJn(g,E,x){return g=Math.pow(g,x),E=Math.pow(E,x)-g,x=1/x,function(M){return Math.pow(g+M*E,x)}}function aJn(g){return(g=+g)==1?xdn:function(E,x){return x-E?fJn(E,x,g):mke(isNaN(E)?x:E)}}function xdn(g,E){var x=E-g;return x?lJn(g,x):mke(isNaN(g)?E:g)}const yue=(function g(E){var x=aJn(E);function M(N,P){var k=x((N=nke(N)).r,(P=nke(P)).r),H=x(N.g,P.g),U=x(N.b,P.b),G=xdn(N.opacity,P.opacity);return function(ie){return N.r=k(ie),N.g=H(ie),N.b=U(ie),N.opacity=G(ie),N+""}}return M.gamma=g,M})(1);function hJn(g,E){E||(E=[]);var x=g?Math.min(E.length,g.length):0,M=E.slice(),N;return function(P){for(N=0;Nx&&(P=E.slice(x,P),H[k]?H[k]+=P:H[++k]=P),(M=M[0])===(N=N[0])?H[k]?H[k]+=N:H[++k]=N:(H[++k]=null,U.push({i:k,x:r5(M,N)})),x=P7e.lastIndex;return x180?ie+=360:ie-G>180&&(G+=360),W.push({i:Z.push(N(Z)+"rotate(",null,M)-2,x:r5(G,ie)})):ie&&Z.push(N(Z)+"rotate("+ie+M)}function H(G,ie,Z,W){G!==ie?W.push({i:Z.push(N(Z)+"skewX(",null,M)-2,x:r5(G,ie)}):ie&&Z.push(N(Z)+"skewX("+ie+M)}function U(G,ie,Z,W,se,le){if(G!==Z||ie!==W){var ee=se.push(N(se)+"scale(",null,",",null,")");le.push({i:ee-4,x:r5(G,Z)},{i:ee-2,x:r5(ie,W)})}else(Z!==1||W!==1)&&se.push(N(se)+"scale("+Z+","+W+")")}return function(G,ie){var Z=[],W=[];return G=g(G),ie=g(ie),P(G.translateX,G.translateY,ie.translateX,ie.translateY,Z,W),k(G.rotate,ie.rotate,Z,W),H(G.skewX,ie.skewX,Z,W),U(G.scaleX,G.scaleY,ie.scaleX,ie.scaleY,Z,W),G=ie=null,function(se){for(var le=-1,ee=W.length,Oe;++le=0&&g._call.call(void 0,E),g=g._next;--dI}function i1n(){SA=(jue=HG.now())+Due,dI=LG=0;try{MJn()}finally{dI=0,TJn(),SA=0}}function CJn(){var g=HG.now(),E=g-jue;E>Tdn&&(Due-=E,jue=g)}function TJn(){for(var g,E=kue,x,M=1/0;E;)E._call?(M>E._time&&(M=E._time),g=E,E=E._next):(x=E._next,E._next=null,E=g?g._next=x:kue=x);PG=g,rke(M)}function rke(g){if(!dI){LG&&(LG=clearTimeout(LG));var E=g-SA;E>24?(g<1/0&&(LG=setTimeout(i1n,g-HG.now()-Due)),DG&&(DG=clearInterval(DG))):(DG||(jue=HG.now(),DG=setInterval(CJn,Tdn)),dI=1,Odn(i1n))}}function r1n(g,E,x){var M=new Eue;return E=E==null?0:+E,M.restart(N=>{M.stop(),g(N+E)},E,x),M}var OJn=Oue("start","end","cancel","interrupt"),NJn=[],Ddn=0,c1n=1,cke=2,due=3,u1n=4,uke=5,bue=6;function Iue(g,E,x,M,N,P){var k=g.__transition;if(!k)g.__transition={};else if(x in k)return;DJn(g,x,{name:E,index:M,group:N,on:OJn,tween:NJn,time:P.time,delay:P.delay,duration:P.duration,ease:P.ease,timer:null,state:Ddn})}function yke(g,E){var x=Qm(g,E);if(x.state>Ddn)throw new Error("too late; already scheduled");return x}function s5(g,E){var x=Qm(g,E);if(x.state>due)throw new Error("too late; already running");return x}function Qm(g,E){var x=g.__transition;if(!x||!(x=x[E]))throw new Error("transition not found");return x}function DJn(g,E,x){var M=g.__transition,N;M[E]=x,x.timer=Ndn(P,0,x.time);function P(G){x.state=c1n,x.timer.restart(k,x.delay,x.time),x.delay<=G&&k(G-x.delay)}function k(G){var ie,Z,W,se;if(x.state!==c1n)return U();for(ie in M)if(se=M[ie],se.name===x.name){if(se.state===due)return r1n(k);se.state===u1n?(se.state=bue,se.timer.stop(),se.on.call("interrupt",g,g.__data__,se.index,se.group),delete M[ie]):+iecke&&M.state=0&&(E=E.slice(0,x)),!E||E==="start"})}function sHn(g,E,x){var M,N,P=oHn(E)?yke:s5;return function(){var k=P(this,g),H=k.on;H!==M&&(N=(M=H).copy()).on(E,x),k.on=N}}function lHn(g,E){var x=this._id;return arguments.length<2?Qm(this.node(),x).on.on(g):this.each(sHn(x,g,E))}function fHn(g){return function(){var E=this.parentNode;for(var x in this.__transition)if(+x!==g)return;E&&E.removeChild(this)}}function aHn(){return this.on("end.remove",fHn(this._id))}function hHn(g){var E=this._name,x=this._id;typeof g!="function"&&(g=gke(g));for(var M=this._groups,N=M.length,P=new Array(N),k=0;k()=>g;function $Hn(g,{sourceEvent:E,target:x,transform:M,dispatch:N}){Object.defineProperties(this,{type:{value:g,enumerable:!0,configurable:!0},sourceEvent:{value:E,enumerable:!0,configurable:!0},target:{value:x,enumerable:!0,configurable:!0},transform:{value:M,enumerable:!0,configurable:!0},_:{value:N}})}function e6(g,E,x){this.k=g,this.x=E,this.y=x}e6.prototype={constructor:e6,scale:function(g){return g===1?this:new e6(this.k*g,this.x,this.y)},translate:function(g,E){return g===0&E===0?this:new e6(this.k,this.x+this.k*g,this.y+this.k*E)},apply:function(g){return[g[0]*this.k+this.x,g[1]*this.k+this.y]},applyX:function(g){return g*this.k+this.x},applyY:function(g){return g*this.k+this.y},invert:function(g){return[(g[0]-this.x)/this.k,(g[1]-this.y)/this.k]},invertX:function(g){return(g-this.x)/this.k},invertY:function(g){return(g-this.y)/this.k},rescaleX:function(g){return g.copy().domain(g.range().map(this.invertX,this).map(g.invert,g))},rescaleY:function(g){return g.copy().domain(g.range().map(this.invertY,this).map(g.invert,g))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var _ue=new e6(1,0,0);Pdn.prototype=e6.prototype;function Pdn(g){for(;!g.__zoom;)if(!(g=g.parentNode))return _ue;return g.__zoom}function $7e(g){g.stopImmediatePropagation()}function IG(g){g.preventDefault(),g.stopImmediatePropagation()}function RHn(g){return(!g.ctrlKey||g.type==="wheel")&&!g.button}function BHn(){var g=this;return g instanceof SVGElement?(g=g.ownerSVGElement||g,g.hasAttribute("viewBox")?(g=g.viewBox.baseVal,[[g.x,g.y],[g.x+g.width,g.y+g.height]]):[[0,0],[g.width.baseVal.value,g.height.baseVal.value]]):[[0,0],[g.clientWidth,g.clientHeight]]}function o1n(){return this.__zoom||_ue}function zHn(g){return-g.deltaY*(g.deltaMode===1?.05:g.deltaMode?1:.002)*(g.ctrlKey?10:1)}function FHn(){return navigator.maxTouchPoints||"ontouchstart"in this}function JHn(g,E,x){var M=g.invertX(E[0][0])-x[0][0],N=g.invertX(E[1][0])-x[1][0],P=g.invertY(E[0][1])-x[0][1],k=g.invertY(E[1][1])-x[1][1];return g.translate(N>M?(M+N)/2:Math.min(0,M)||Math.max(0,N),k>P?(P+k)/2:Math.min(0,P)||Math.max(0,k))}function $dn(){var g=RHn,E=BHn,x=JHn,M=zHn,N=FHn,P=[0,1/0],k=[[-1/0,-1/0],[1/0,1/0]],H=250,U=hue,G=Oue("start","zoom","end"),ie,Z,W,se=500,le=150,ee=0,Oe=10;function ge(Fe){Fe.property("__zoom",o1n).on("wheel.zoom",xn,{passive:!1}).on("mousedown.zoom",hn).on("dblclick.zoom",wt).filter(N).on("touchstart.zoom",Tn).on("touchmove.zoom",Qn).on("touchend.zoom touchcancel.zoom",Y).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}ge.transform=function(Fe,mn,Ae,Ze){var sn=Fe.selection?Fe.selection():Fe;sn.property("__zoom",o1n),Fe!==sn?Be(Fe,mn,Ae,Ze):sn.interrupt().each(function(){rn(this,arguments).event(Ze).start().zoom(null,typeof mn=="function"?mn.apply(this,arguments):mn).end()})},ge.scaleBy=function(Fe,mn,Ae,Ze){ge.scaleTo(Fe,function(){var sn=this.__zoom.k,Sn=typeof mn=="function"?mn.apply(this,arguments):mn;return sn*Sn},Ae,Ze)},ge.scaleTo=function(Fe,mn,Ae,Ze){ge.transform(Fe,function(){var sn=E.apply(this,arguments),Sn=this.__zoom,kn=Ae==null?Me(sn):typeof Ae=="function"?Ae.apply(this,arguments):Ae,xe=Sn.invert(kn),un=typeof mn=="function"?mn.apply(this,arguments):mn;return x(ae(Pe(Sn,un),kn,xe),sn,k)},Ae,Ze)},ge.translateBy=function(Fe,mn,Ae,Ze){ge.transform(Fe,function(){return x(this.__zoom.translate(typeof mn=="function"?mn.apply(this,arguments):mn,typeof Ae=="function"?Ae.apply(this,arguments):Ae),E.apply(this,arguments),k)},null,Ze)},ge.translateTo=function(Fe,mn,Ae,Ze,sn){ge.transform(Fe,function(){var Sn=E.apply(this,arguments),kn=this.__zoom,xe=Ze==null?Me(Sn):typeof Ze=="function"?Ze.apply(this,arguments):Ze;return x(_ue.translate(xe[0],xe[1]).scale(kn.k).translate(typeof mn=="function"?-mn.apply(this,arguments):-mn,typeof Ae=="function"?-Ae.apply(this,arguments):-Ae),Sn,k)},Ze,sn)};function Pe(Fe,mn){return mn=Math.max(P[0],Math.min(P[1],mn)),mn===Fe.k?Fe:new e6(mn,Fe.x,Fe.y)}function ae(Fe,mn,Ae){var Ze=mn[0]-Ae[0]*Fe.k,sn=mn[1]-Ae[1]*Fe.k;return Ze===Fe.x&&sn===Fe.y?Fe:new e6(Fe.k,Ze,sn)}function Me(Fe){return[(+Fe[0][0]+ +Fe[1][0])/2,(+Fe[0][1]+ +Fe[1][1])/2]}function Be(Fe,mn,Ae,Ze){Fe.on("start.zoom",function(){rn(this,arguments).event(Ze).start()}).on("interrupt.zoom end.zoom",function(){rn(this,arguments).event(Ze).end()}).tween("zoom",function(){var sn=this,Sn=arguments,kn=rn(sn,Sn).event(Ze),xe=E.apply(sn,Sn),un=Ae==null?Me(xe):typeof Ae=="function"?Ae.apply(sn,Sn):Ae,rt=Math.max(xe[1][0]-xe[0][0],xe[1][1]-xe[0][1]),lt=sn.__zoom,Bt=typeof mn=="function"?mn.apply(sn,Sn):mn,hi=U(lt.invert(un).concat(rt/lt.k),Bt.invert(un).concat(rt/Bt.k));return function(Gt){if(Gt===1)Gt=Bt;else{var At=hi(Gt),st=rt/At[2];Gt=new e6(st,un[0]-At[0]*st,un[1]-At[1]*st)}kn.zoom(null,Gt)}})}function rn(Fe,mn,Ae){return!Ae&&Fe.__zooming||new ln(Fe,mn)}function ln(Fe,mn){this.that=Fe,this.args=mn,this.active=0,this.sourceEvent=null,this.extent=E.apply(Fe,mn),this.taps=0}ln.prototype={event:function(Fe){return Fe&&(this.sourceEvent=Fe),this},start:function(){return++this.active===1&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(Fe,mn){return this.mouse&&Fe!=="mouse"&&(this.mouse[1]=mn.invert(this.mouse[0])),this.touch0&&Fe!=="touch"&&(this.touch0[1]=mn.invert(this.touch0[0])),this.touch1&&Fe!=="touch"&&(this.touch1[1]=mn.invert(this.touch1[0])),this.that.__zoom=mn,this.emit("zoom"),this},end:function(){return--this.active===0&&(delete this.that.__zooming,this.emit("end")),this},emit:function(Fe){var mn=$g(this.that).datum();G.call(Fe,this.that,new $Hn(Fe,{sourceEvent:this.sourceEvent,target:ge,transform:this.that.__zoom,dispatch:G}),mn)}};function xn(Fe,...mn){if(!g.apply(this,arguments))return;var Ae=rn(this,mn).event(Fe),Ze=this.__zoom,sn=Math.max(P[0],Math.min(P[1],Ze.k*Math.pow(2,M.apply(this,arguments)))),Sn=Um(Fe);if(Ae.wheel)(Ae.mouse[0][0]!==Sn[0]||Ae.mouse[0][1]!==Sn[1])&&(Ae.mouse[1]=Ze.invert(Ae.mouse[0]=Sn)),clearTimeout(Ae.wheel);else{if(Ze.k===sn)return;Ae.mouse=[Sn,Ze.invert(Sn)],gue(this),Ae.start()}IG(Fe),Ae.wheel=setTimeout(kn,le),Ae.zoom("mouse",x(ae(Pe(Ze,sn),Ae.mouse[0],Ae.mouse[1]),Ae.extent,k));function kn(){Ae.wheel=null,Ae.end()}}function hn(Fe,...mn){if(W||!g.apply(this,arguments))return;var Ae=Fe.currentTarget,Ze=rn(this,mn,!0).event(Fe),sn=$g(Fe.view).on("mousemove.zoom",un,!0).on("mouseup.zoom",rt,!0),Sn=Um(Fe,Ae),kn=Fe.clientX,xe=Fe.clientY;ydn(Fe.view),$7e(Fe),Ze.mouse=[Sn,this.__zoom.invert(Sn)],gue(this),Ze.start();function un(lt){if(IG(lt),!Ze.moved){var Bt=lt.clientX-kn,hi=lt.clientY-xe;Ze.moved=Bt*Bt+hi*hi>ee}Ze.event(lt).zoom("mouse",x(ae(Ze.that.__zoom,Ze.mouse[0]=Um(lt,Ae),Ze.mouse[1]),Ze.extent,k))}function rt(lt){sn.on("mousemove.zoom mouseup.zoom",null),kdn(lt.view,Ze.moved),IG(lt),Ze.event(lt).end()}}function wt(Fe,...mn){if(g.apply(this,arguments)){var Ae=this.__zoom,Ze=Um(Fe.changedTouches?Fe.changedTouches[0]:Fe,this),sn=Ae.invert(Ze),Sn=Ae.k*(Fe.shiftKey?.5:2),kn=x(ae(Pe(Ae,Sn),Ze,sn),E.apply(this,mn),k);IG(Fe),H>0?$g(this).transition().duration(H).call(Be,kn,Ze,Fe):$g(this).call(ge.transform,kn,Ze,Fe)}}function Tn(Fe,...mn){if(g.apply(this,arguments)){var Ae=Fe.touches,Ze=Ae.length,sn=rn(this,mn,Fe.changedTouches.length===Ze).event(Fe),Sn,kn,xe,un;for($7e(Fe),kn=0;kn"[React Flow]: Seems like you have not used zustand provider as an ancestor. Help: https://reactflow.dev/error#001",error002:()=>"It looks like you've created a new nodeTypes or edgeTypes object. If this wasn't on purpose please define the nodeTypes/edgeTypes outside of the component or memoize them.",error003:g=>`Node type "${g}" not found. Using fallback type "default".`,error004:()=>"The React Flow parent container needs a width and a height to render the graph.",error005:()=>"Only child nodes can use a parent extent.",error006:()=>"Can't create edge. An edge needs a source and a target.",error007:g=>`The old edge with id=${g} does not exist.`,error009:g=>`Marker type "${g}" doesn't exist.`,error008:(g,{id:E,sourceHandle:x,targetHandle:M})=>`Couldn't create edge for ${g} handle id: "${g==="source"?x:M}", edge id: ${E}.`,error010:()=>"Handle: No node id found. Make sure to only use a Handle inside a custom Node.",error011:g=>`Edge type "${g}" not found. Using fallback type "default".`,error012:g=>`Node with id "${g}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`,error013:(g="react")=>`It seems that you haven't loaded the styles. Please import '@xyflow/${g}/dist/style.css' or base.css to make sure everything is working properly.`,error014:()=>"useNodeConnections: No node ID found. Call useNodeConnections inside a custom Node or provide a node ID.",error015:()=>"It seems that you are trying to drag a node that is not initialized. Please use onNodesChange as explained in the docs."},GG=[[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY],[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY]],Rdn=["Enter"," ","Escape"],Bdn={"node.a11yDescription.default":"Press enter or space to select a node. Press delete to remove it and escape to cancel.","node.a11yDescription.keyboardDisabled":"Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.","node.a11yDescription.ariaLiveMessage":({direction:g,x:E,y:x})=>`Moved selected node ${g}. New position, x: ${E}, y: ${x}`,"edge.a11yDescription.default":"Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.","controls.ariaLabel":"Control Panel","controls.zoomIn.ariaLabel":"Zoom In","controls.zoomOut.ariaLabel":"Zoom Out","controls.fitView.ariaLabel":"Fit View","controls.interactive.ariaLabel":"Toggle Interactivity","minimap.ariaLabel":"Mini Map","handle.ariaLabel":"Handle"};var bI;(function(g){g.Strict="strict",g.Loose="loose"})(bI||(bI={}));var jA;(function(g){g.Free="free",g.Vertical="vertical",g.Horizontal="horizontal"})(jA||(jA={}));var qG;(function(g){g.Partial="partial",g.Full="full"})(qG||(qG={}));const zdn={inProgress:!1,isValid:null,from:null,fromHandle:null,fromPosition:null,fromNode:null,to:null,toHandle:null,toPosition:null,toNode:null,pointer:null};var W7;(function(g){g.Bezier="default",g.Straight="straight",g.Step="step",g.SmoothStep="smoothstep",g.SimpleBezier="simplebezier"})(W7||(W7={}));var UG;(function(g){g.Arrow="arrow",g.ArrowClosed="arrowclosed"})(UG||(UG={}));var cr;(function(g){g.Left="left",g.Top="top",g.Right="right",g.Bottom="bottom"})(cr||(cr={}));const s1n={[cr.Left]:cr.Right,[cr.Right]:cr.Left,[cr.Top]:cr.Bottom,[cr.Bottom]:cr.Top};function Fdn(g){return g===null?null:g?"valid":"invalid"}const Jdn=g=>"id"in g&&"source"in g&&"target"in g,HHn=g=>"id"in g&&"position"in g&&!("source"in g)&&!("target"in g),jke=g=>"id"in g&&"internals"in g&&!("source"in g)&&!("target"in g),nq=(g,E=[0,0])=>{const{width:x,height:M}=i6(g),N=g.origin??E,P=x*N[0],k=M*N[1];return{x:g.position.x-P,y:g.position.y-k}},GHn=(g,E={nodeOrigin:[0,0]})=>{if(g.length===0)return{x:0,y:0,width:0,height:0};const x=g.reduce((M,N)=>{const P=typeof N=="string";let k=!E.nodeLookup&&!P?N:void 0;E.nodeLookup&&(k=P?E.nodeLookup.get(N):jke(N)?N:E.nodeLookup.get(N.id));const H=k?Sue(k,E.nodeOrigin):{x:0,y:0,x2:0,y2:0};return Lue(M,H)},{x:1/0,y:1/0,x2:-1/0,y2:-1/0});return Pue(x)},tq=(g,E={})=>{let x={x:1/0,y:1/0,x2:-1/0,y2:-1/0},M=!1;return g.forEach(N=>{(E.filter===void 0||E.filter(N))&&(x=Lue(x,Sue(N)),M=!0)}),M?Pue(x):{x:0,y:0,width:0,height:0}},Eke=(g,E,[x,M,N]=[0,0,1],P=!1,k=!1)=>{const H={...rq(E,[x,M,N]),width:E.width/N,height:E.height/N},U=[];for(const G of g.values()){const{measured:ie,selectable:Z=!0,hidden:W=!1}=G;if(k&&!Z||W)continue;const se=ie.width??G.width??G.initialWidth??null,le=ie.height??G.height??G.initialHeight??null,ee=XG(H,wI(G)),Oe=(se??0)*(le??0),ge=P&&ee>0;(!G.internals.handleBounds||ge||ee>=Oe||G.dragging)&&U.push(G)}return U},qHn=(g,E)=>{const x=new Set;return g.forEach(M=>{x.add(M.id)}),E.filter(M=>x.has(M.source)||x.has(M.target))};function UHn(g,E){const x=new Map,M=E?.nodes?new Set(E.nodes.map(N=>N.id)):null;return g.forEach(N=>{N.measured.width&&N.measured.height&&(E?.includeHiddenNodes||!N.hidden)&&(!M||M.has(N.id))&&x.set(N.id,N)}),x}async function XHn({nodes:g,width:E,height:x,panZoom:M,minZoom:N,maxZoom:P},k){if(g.size===0)return Promise.resolve(!0);const H=UHn(g,k),U=tq(H),G=Ske(U,E,x,k?.minZoom??N,k?.maxZoom??P,k?.padding??.1);return await M.setViewport(G,{duration:k?.duration,ease:k?.ease,interpolate:k?.interpolate}),Promise.resolve(!0)}function Hdn({nodeId:g,nextPosition:E,nodeLookup:x,nodeOrigin:M=[0,0],nodeExtent:N,onError:P}){const k=x.get(g),H=k.parentId?x.get(k.parentId):void 0,{x:U,y:G}=H?H.internals.positionAbsolute:{x:0,y:0},ie=k.origin??M;let Z=k.extent||N;if(k.extent==="parent"&&!k.expandParent)if(!H)P?.("005",u5.error005());else{const se=H.measured.width,le=H.measured.height;se&&le&&(Z=[[U,G],[U+se,G+le]])}else H&&pI(k.extent)&&(Z=[[k.extent[0][0]+U,k.extent[0][1]+G],[k.extent[1][0]+U,k.extent[1][1]+G]]);const W=pI(Z)?xA(E,Z,k.measured):E;return(k.measured.width===void 0||k.measured.height===void 0)&&P?.("015",u5.error015()),{position:{x:W.x-U+(k.measured.width??0)*ie[0],y:W.y-G+(k.measured.height??0)*ie[1]},positionAbsolute:W}}async function KHn({nodesToRemove:g=[],edgesToRemove:E=[],nodes:x,edges:M,onBeforeDelete:N}){const P=new Set(g.map(W=>W.id)),k=[];for(const W of x){if(W.deletable===!1)continue;const se=P.has(W.id),le=!se&&W.parentId&&k.find(ee=>ee.id===W.parentId);(se||le)&&k.push(W)}const H=new Set(E.map(W=>W.id)),U=M.filter(W=>W.deletable!==!1),ie=qHn(k,U);for(const W of U)H.has(W.id)&&!ie.find(le=>le.id===W.id)&&ie.push(W);if(!N)return{edges:ie,nodes:k};const Z=await N({nodes:k,edges:ie});return typeof Z=="boolean"?Z?{edges:ie,nodes:k}:{edges:[],nodes:[]}:Z}const gI=(g,E=0,x=1)=>Math.min(Math.max(g,E),x),xA=(g={x:0,y:0},E,x)=>({x:gI(g.x,E[0][0],E[1][0]-(x?.width??0)),y:gI(g.y,E[0][1],E[1][1]-(x?.height??0))});function Gdn(g,E,x){const{width:M,height:N}=i6(x),{x:P,y:k}=x.internals.positionAbsolute;return xA(g,[[P,k],[P+M,k+N]],E)}const l1n=(g,E,x)=>gx?-gI(Math.abs(g-x),1,E)/E:0,qdn=(g,E,x=15,M=40)=>{const N=l1n(g.x,M,E.width-M)*x,P=l1n(g.y,M,E.height-M)*x;return[N,P]},Lue=(g,E)=>({x:Math.min(g.x,E.x),y:Math.min(g.y,E.y),x2:Math.max(g.x2,E.x2),y2:Math.max(g.y2,E.y2)}),oke=({x:g,y:E,width:x,height:M})=>({x:g,y:E,x2:g+x,y2:E+M}),Pue=({x:g,y:E,x2:x,y2:M})=>({x:g,y:E,width:x-g,height:M-E}),wI=(g,E=[0,0])=>{const{x,y:M}=jke(g)?g.internals.positionAbsolute:nq(g,E);return{x,y:M,width:g.measured?.width??g.width??g.initialWidth??0,height:g.measured?.height??g.height??g.initialHeight??0}},Sue=(g,E=[0,0])=>{const{x,y:M}=jke(g)?g.internals.positionAbsolute:nq(g,E);return{x,y:M,x2:x+(g.measured?.width??g.width??g.initialWidth??0),y2:M+(g.measured?.height??g.height??g.initialHeight??0)}},Udn=(g,E)=>Pue(Lue(oke(g),oke(E))),XG=(g,E)=>{const x=Math.max(0,Math.min(g.x+g.width,E.x+E.width)-Math.max(g.x,E.x)),M=Math.max(0,Math.min(g.y+g.height,E.y+E.height)-Math.max(g.y,E.y));return Math.ceil(x*M)},f1n=g=>Km(g.width)&&Km(g.height)&&Km(g.x)&&Km(g.y),Km=g=>!isNaN(g)&&isFinite(g),VHn=(g,E)=>{},iq=(g,E=[1,1])=>({x:E[0]*Math.round(g.x/E[0]),y:E[1]*Math.round(g.y/E[1])}),rq=({x:g,y:E},[x,M,N],P=!1,k=[1,1])=>{const H={x:(g-x)/N,y:(E-M)/N};return P?iq(H,k):H},xue=({x:g,y:E},[x,M,N])=>({x:g*N+x,y:E*N+M});function uI(g,E){if(typeof g=="number")return Math.floor((E-E/(1+g))*.5);if(typeof g=="string"&&g.endsWith("px")){const x=parseFloat(g);if(!Number.isNaN(x))return Math.floor(x)}if(typeof g=="string"&&g.endsWith("%")){const x=parseFloat(g);if(!Number.isNaN(x))return Math.floor(E*x*.01)}return console.error(`[React Flow] The padding value "${g}" is invalid. Please provide a number or a string with a valid unit (px or %).`),0}function YHn(g,E,x){if(typeof g=="string"||typeof g=="number"){const M=uI(g,x),N=uI(g,E);return{top:M,right:N,bottom:M,left:N,x:N*2,y:M*2}}if(typeof g=="object"){const M=uI(g.top??g.y??0,x),N=uI(g.bottom??g.y??0,x),P=uI(g.left??g.x??0,E),k=uI(g.right??g.x??0,E);return{top:M,right:k,bottom:N,left:P,x:P+k,y:M+N}}return{top:0,right:0,bottom:0,left:0,x:0,y:0}}function QHn(g,E,x,M,N,P){const{x:k,y:H}=xue(g,[E,x,M]),{x:U,y:G}=xue({x:g.x+g.width,y:g.y+g.height},[E,x,M]),ie=N-U,Z=P-G;return{left:Math.floor(k),top:Math.floor(H),right:Math.floor(ie),bottom:Math.floor(Z)}}const Ske=(g,E,x,M,N,P)=>{const k=YHn(P,E,x),H=(E-k.x)/g.width,U=(x-k.y)/g.height,G=Math.min(H,U),ie=gI(G,M,N),Z=g.x+g.width/2,W=g.y+g.height/2,se=E/2-Z*ie,le=x/2-W*ie,ee=QHn(g,se,le,ie,E,x),Oe={left:Math.min(ee.left-k.left,0),top:Math.min(ee.top-k.top,0),right:Math.min(ee.right-k.right,0),bottom:Math.min(ee.bottom-k.bottom,0)};return{x:se-Oe.left+Oe.right,y:le-Oe.top+Oe.bottom,zoom:ie}},KG=()=>typeof navigator<"u"&&navigator?.userAgent?.indexOf("Mac")>=0;function pI(g){return g!=null&&g!=="parent"}function i6(g){return{width:g.measured?.width??g.width??g.initialWidth??0,height:g.measured?.height??g.height??g.initialHeight??0}}function Xdn(g){return(g.measured?.width??g.width??g.initialWidth)!==void 0&&(g.measured?.height??g.height??g.initialHeight)!==void 0}function Kdn(g,E={width:0,height:0},x,M,N){const P={...g},k=M.get(x);if(k){const H=k.origin||N;P.x+=k.internals.positionAbsolute.x-(E.width??0)*H[0],P.y+=k.internals.positionAbsolute.y-(E.height??0)*H[1]}return P}function a1n(g,E){if(g.size!==E.size)return!1;for(const x of g)if(!E.has(x))return!1;return!0}function WHn(){let g,E;return{promise:new Promise((M,N)=>{g=M,E=N}),resolve:g,reject:E}}function ZHn(g){return{...Bdn,...g||{}}}function BG(g,{snapGrid:E=[0,0],snapToGrid:x=!1,transform:M,containerBounds:N}){const{x:P,y:k}=Vm(g),H=rq({x:P-(N?.left??0),y:k-(N?.top??0)},M),{x:U,y:G}=x?iq(H,E):H;return{xSnapped:U,ySnapped:G,...H}}const xke=g=>({width:g.offsetWidth,height:g.offsetHeight}),Vdn=g=>g?.getRootNode?.()||window?.document,eGn=["INPUT","SELECT","TEXTAREA"];function Ydn(g){const E=g.composedPath?.()?.[0]||g.target;return E?.nodeType!==1?!1:eGn.includes(E.nodeName)||E.hasAttribute("contenteditable")||!!E.closest(".nokey")}const Qdn=g=>"clientX"in g,Vm=(g,E)=>{const x=Qdn(g),M=x?g.clientX:g.touches?.[0].clientX,N=x?g.clientY:g.touches?.[0].clientY;return{x:M-(E?.left??0),y:N-(E?.top??0)}},h1n=(g,E,x,M,N)=>{const P=E.querySelectorAll(`.${g}`);return!P||!P.length?null:Array.from(P).map(k=>{const H=k.getBoundingClientRect();return{id:k.getAttribute("data-handleid"),type:g,nodeId:N,position:k.getAttribute("data-handlepos"),x:(H.left-x.left)/M,y:(H.top-x.top)/M,...xke(k)}})};function Wdn({sourceX:g,sourceY:E,targetX:x,targetY:M,sourceControlX:N,sourceControlY:P,targetControlX:k,targetControlY:H}){const U=g*.125+N*.375+k*.375+x*.125,G=E*.125+P*.375+H*.375+M*.125,ie=Math.abs(U-g),Z=Math.abs(G-E);return[U,G,ie,Z]}function nue(g,E){return g>=0?.5*g:E*25*Math.sqrt(-g)}function d1n({pos:g,x1:E,y1:x,x2:M,y2:N,c:P}){switch(g){case cr.Left:return[E-nue(E-M,P),x];case cr.Right:return[E+nue(M-E,P),x];case cr.Top:return[E,x-nue(x-N,P)];case cr.Bottom:return[E,x+nue(N-x,P)]}}function Zdn({sourceX:g,sourceY:E,sourcePosition:x=cr.Bottom,targetX:M,targetY:N,targetPosition:P=cr.Top,curvature:k=.25}){const[H,U]=d1n({pos:x,x1:g,y1:E,x2:M,y2:N,c:k}),[G,ie]=d1n({pos:P,x1:M,y1:N,x2:g,y2:E,c:k}),[Z,W,se,le]=Wdn({sourceX:g,sourceY:E,targetX:M,targetY:N,sourceControlX:H,sourceControlY:U,targetControlX:G,targetControlY:ie});return[`M${g},${E} C${H},${U} ${G},${ie} ${M},${N}`,Z,W,se,le]}function e0n({sourceX:g,sourceY:E,targetX:x,targetY:M}){const N=Math.abs(x-g)/2,P=x0}const iGn=({source:g,sourceHandle:E,target:x,targetHandle:M})=>`xy-edge__${g}${E||""}-${x}${M||""}`,rGn=(g,E)=>E.some(x=>x.source===g.source&&x.target===g.target&&(x.sourceHandle===g.sourceHandle||!x.sourceHandle&&!g.sourceHandle)&&(x.targetHandle===g.targetHandle||!x.targetHandle&&!g.targetHandle)),cGn=(g,E,x={})=>{if(!g.source||!g.target)return E;const M=x.getEdgeId||iGn;let N;return Jdn(g)?N={...g}:N={...g,id:M(g)},rGn(N,E)?E:(N.sourceHandle===null&&delete N.sourceHandle,N.targetHandle===null&&delete N.targetHandle,E.concat(N))};function n0n({sourceX:g,sourceY:E,targetX:x,targetY:M}){const[N,P,k,H]=e0n({sourceX:g,sourceY:E,targetX:x,targetY:M});return[`M ${g},${E}L ${x},${M}`,N,P,k,H]}const b1n={[cr.Left]:{x:-1,y:0},[cr.Right]:{x:1,y:0},[cr.Top]:{x:0,y:-1},[cr.Bottom]:{x:0,y:1}},uGn=({source:g,sourcePosition:E=cr.Bottom,target:x})=>E===cr.Left||E===cr.Right?g.xMath.sqrt(Math.pow(E.x-g.x,2)+Math.pow(E.y-g.y,2));function oGn({source:g,sourcePosition:E=cr.Bottom,target:x,targetPosition:M=cr.Top,center:N,offset:P,stepPosition:k}){const H=b1n[E],U=b1n[M],G={x:g.x+H.x*P,y:g.y+H.y*P},ie={x:x.x+U.x*P,y:x.y+U.y*P},Z=uGn({source:G,sourcePosition:E,target:ie}),W=Z.x!==0?"x":"y",se=Z[W];let le=[],ee,Oe;const ge={x:0,y:0},Pe={x:0,y:0},[,,ae,Me]=e0n({sourceX:g.x,sourceY:g.y,targetX:x.x,targetY:x.y});if(H[W]*U[W]===-1){W==="x"?(ee=N.x??G.x+(ie.x-G.x)*k,Oe=N.y??(G.y+ie.y)/2):(ee=N.x??(G.x+ie.x)/2,Oe=N.y??G.y+(ie.y-G.y)*k);const xn=[{x:ee,y:G.y},{x:ee,y:ie.y}],hn=[{x:G.x,y:Oe},{x:ie.x,y:Oe}];H[W]===se?le=W==="x"?xn:hn:le=W==="x"?hn:xn}else{const xn=[{x:G.x,y:ie.y}],hn=[{x:ie.x,y:G.y}];if(W==="x"?le=H.x===se?hn:xn:le=H.y===se?xn:hn,E===M){const Fe=Math.abs(g[W]-x[W]);if(Fe<=P){const mn=Math.min(P-1,P-Fe);H[W]===se?ge[W]=(G[W]>g[W]?-1:1)*mn:Pe[W]=(ie[W]>x[W]?-1:1)*mn}}if(E!==M){const Fe=W==="x"?"y":"x",mn=H[W]===U[Fe],Ae=G[Fe]>ie[Fe],Ze=G[Fe]=Y?(ee=(wt.x+Tn.x)/2,Oe=le[0].y):(ee=le[0].x,Oe=(wt.y+Tn.y)/2)}const Be={x:G.x+ge.x,y:G.y+ge.y},rn={x:ie.x+Pe.x,y:ie.y+Pe.y};return[[g,...Be.x!==le[0].x||Be.y!==le[0].y?[Be]:[],...le,...rn.x!==le[le.length-1].x||rn.y!==le[le.length-1].y?[rn]:[],x],ee,Oe,ae,Me]}function sGn(g,E,x,M){const N=Math.min(g1n(g,E)/2,g1n(E,x)/2,M),{x:P,y:k}=E;if(g.x===P&&P===x.x||g.y===k&&k===x.y)return`L${P} ${k}`;if(g.y===k){const G=g.xx.id===E):g[0])||null}function ske(g,E){return g?typeof g=="string"?g:`${E?`${E}__`:""}${Object.keys(g).sort().map(M=>`${M}=${g[M]}`).join("&")}`:""}function fGn(g,{id:E,defaultColor:x,defaultMarkerStart:M,defaultMarkerEnd:N}){const P=new Set;return g.reduce((k,H)=>([H.markerStart||M,H.markerEnd||N].forEach(U=>{if(U&&typeof U=="object"){const G=ske(U,E);P.has(G)||(k.push({id:G,color:U.color||x,...U}),P.add(G))}}),k),[]).sort((k,H)=>k.id.localeCompare(H.id))}const t0n=1e3,aGn=10,Ake={nodeOrigin:[0,0],nodeExtent:GG,elevateNodesOnSelect:!0,zIndexMode:"basic",defaults:{}},hGn={...Ake,checkEquality:!0};function Mke(g,E){const x={...g};for(const M in E)E[M]!==void 0&&(x[M]=E[M]);return x}function dGn(g,E,x){const M=Mke(Ake,x);for(const N of g.values())if(N.parentId)Tke(N,g,E,M);else{const P=nq(N,M.nodeOrigin),k=pI(N.extent)?N.extent:M.nodeExtent,H=xA(P,k,i6(N));N.internals.positionAbsolute=H}}function bGn(g,E){if(!g.handles)return g.measured?E?.internals.handleBounds:void 0;const x=[],M=[];for(const N of g.handles){const P={id:N.id,width:N.width??1,height:N.height??1,nodeId:g.id,x:N.x,y:N.y,position:N.position,type:N.type};N.type==="source"?x.push(P):N.type==="target"&&M.push(P)}return{source:x,target:M}}function Cke(g){return g==="manual"}function lke(g,E,x,M={}){const N=Mke(hGn,M),P={i:0},k=new Map(E),H=N?.elevateNodesOnSelect&&!Cke(N.zIndexMode)?t0n:0;let U=g.length>0,G=!1;E.clear(),x.clear();for(const ie of g){let Z=k.get(ie.id);if(N.checkEquality&&ie===Z?.internals.userNode)E.set(ie.id,Z);else{const W=nq(ie,N.nodeOrigin),se=pI(ie.extent)?ie.extent:N.nodeExtent,le=xA(W,se,i6(ie));Z={...N.defaults,...ie,measured:{width:ie.measured?.width,height:ie.measured?.height},internals:{positionAbsolute:le,handleBounds:bGn(ie,Z),z:i0n(ie,H,N.zIndexMode),userNode:ie}},E.set(ie.id,Z)}(Z.measured===void 0||Z.measured.width===void 0||Z.measured.height===void 0)&&!Z.hidden&&(U=!1),ie.parentId&&Tke(Z,E,x,M,P),G||=ie.selected??!1}return{nodesInitialized:U,hasSelectedNodes:G}}function gGn(g,E){if(!g.parentId)return;const x=E.get(g.parentId);x?x.set(g.id,g):E.set(g.parentId,new Map([[g.id,g]]))}function Tke(g,E,x,M,N){const{elevateNodesOnSelect:P,nodeOrigin:k,nodeExtent:H,zIndexMode:U}=Mke(Ake,M),G=g.parentId,ie=E.get(G);if(!ie){console.warn(`Parent node ${G} not found. Please make sure that parent nodes are in front of their child nodes in the nodes array.`);return}gGn(g,x),N&&!ie.parentId&&ie.internals.rootParentIndex===void 0&&U==="auto"&&(ie.internals.rootParentIndex=++N.i,ie.internals.z=ie.internals.z+N.i*aGn),N&&ie.internals.rootParentIndex!==void 0&&(N.i=ie.internals.rootParentIndex);const Z=P&&!Cke(U)?t0n:0,{x:W,y:se,z:le}=wGn(g,ie,k,H,Z,U),{positionAbsolute:ee}=g.internals,Oe=W!==ee.x||se!==ee.y;(Oe||le!==g.internals.z)&&E.set(g.id,{...g,internals:{...g.internals,positionAbsolute:Oe?{x:W,y:se}:ee,z:le}})}function i0n(g,E,x){const M=Km(g.zIndex)?g.zIndex:0;return Cke(x)?M:M+(g.selected?E:0)}function wGn(g,E,x,M,N,P){const{x:k,y:H}=E.internals.positionAbsolute,U=i6(g),G=nq(g,x),ie=pI(g.extent)?xA(G,g.extent,U):G;let Z=xA({x:k+ie.x,y:H+ie.y},M,U);g.extent==="parent"&&(Z=Gdn(Z,U,E));const W=i0n(g,N,P),se=E.internals.z??0;return{x:Z.x,y:Z.y,z:se>=W?se+1:W}}function Oke(g,E,x,M=[0,0]){const N=[],P=new Map;for(const k of g){const H=E.get(k.parentId);if(!H)continue;const U=P.get(k.parentId)?.expandedRect??wI(H),G=Udn(U,k.rect);P.set(k.parentId,{expandedRect:G,parent:H})}return P.size>0&&P.forEach(({expandedRect:k,parent:H},U)=>{const G=H.internals.positionAbsolute,ie=i6(H),Z=H.origin??M,W=k.x0||se>0||Oe||ge)&&(N.push({id:U,type:"position",position:{x:H.position.x-W+Oe,y:H.position.y-se+ge}}),x.get(U)?.forEach(Pe=>{g.some(ae=>ae.id===Pe.id)||N.push({id:Pe.id,type:"position",position:{x:Pe.position.x+W,y:Pe.position.y+se}})})),(ie.width0){const se=Oke(W,E,x,N);G.push(...se)}return{changes:G,updatedInternals:U}}async function mGn({delta:g,panZoom:E,transform:x,translateExtent:M,width:N,height:P}){if(!E||!g.x&&!g.y)return Promise.resolve(!1);const k=await E.setViewportConstrained({x:x[0]+g.x,y:x[1]+g.y,zoom:x[2]},[[0,0],[N,P]],M),H=!!k&&(k.x!==x[0]||k.y!==x[1]||k.k!==x[2]);return Promise.resolve(H)}function v1n(g,E,x,M,N,P){let k=N;const H=M.get(k)||new Map;M.set(k,H.set(x,E)),k=`${N}-${g}`;const U=M.get(k)||new Map;if(M.set(k,U.set(x,E)),P){k=`${N}-${g}-${P}`;const G=M.get(k)||new Map;M.set(k,G.set(x,E))}}function r0n(g,E,x){g.clear(),E.clear();for(const M of x){const{source:N,target:P,sourceHandle:k=null,targetHandle:H=null}=M,U={edgeId:M.id,source:N,target:P,sourceHandle:k,targetHandle:H},G=`${N}-${k}--${P}-${H}`,ie=`${P}-${H}--${N}-${k}`;v1n("source",U,ie,g,N,k),v1n("target",U,G,g,P,H),E.set(M.id,M)}}function c0n(g,E){if(!g.parentId)return!1;const x=E.get(g.parentId);return x?x.selected?!0:c0n(x,E):!1}function y1n(g,E,x){let M=g;do{if(M?.matches?.(E))return!0;if(M===x)return!1;M=M?.parentElement}while(M);return!1}function vGn(g,E,x,M){const N=new Map;for(const[P,k]of g)if((k.selected||k.id===M)&&(!k.parentId||!c0n(k,g))&&(k.draggable||E&&typeof k.draggable>"u")){const H=g.get(P);H&&N.set(P,{id:P,position:H.position||{x:0,y:0},distance:{x:x.x-H.internals.positionAbsolute.x,y:x.y-H.internals.positionAbsolute.y},extent:H.extent,parentId:H.parentId,origin:H.origin,expandParent:H.expandParent,internals:{positionAbsolute:H.internals.positionAbsolute||{x:0,y:0}},measured:{width:H.measured.width??0,height:H.measured.height??0}})}return N}function R7e({nodeId:g,dragItems:E,nodeLookup:x,dragging:M=!0}){const N=[];for(const[k,H]of E){const U=x.get(k)?.internals.userNode;U&&N.push({...U,position:H.position,dragging:M})}if(!g)return[N[0],N];const P=x.get(g)?.internals.userNode;return[P?{...P,position:E.get(g)?.position||P.position,dragging:M}:N[0],N]}function yGn({dragItems:g,snapGrid:E,x,y:M}){const N=g.values().next().value;if(!N)return null;const P={x:x-N.distance.x,y:M-N.distance.y},k=iq(P,E);return{x:k.x-P.x,y:k.y-P.y}}function kGn({onNodeMouseDown:g,getStoreItems:E,onDragStart:x,onDrag:M,onDragStop:N}){let P={x:null,y:null},k=0,H=new Map,U=!1,G={x:0,y:0},ie=null,Z=!1,W=null,se=!1,le=!1,ee=null;function Oe({noDragClassName:Pe,handleSelector:ae,domNode:Me,isSelectable:Be,nodeId:rn,nodeClickDistance:ln=0}){W=$g(Me);function xn({x:Qn,y:Y}){const{nodeLookup:Fe,nodeExtent:mn,snapGrid:Ae,snapToGrid:Ze,nodeOrigin:sn,onNodeDrag:Sn,onSelectionDrag:kn,onError:xe,updateNodePositions:un}=E();P={x:Qn,y:Y};let rt=!1;const lt=H.size>1,Bt=lt&&mn?oke(tq(H)):null,hi=lt&&Ze?yGn({dragItems:H,snapGrid:Ae,x:Qn,y:Y}):null;for(const[Gt,At]of H){if(!Fe.has(Gt))continue;let st={x:Qn-At.distance.x,y:Y-At.distance.y};Ze&&(st=hi?{x:Math.round(st.x+hi.x),y:Math.round(st.y+hi.y)}:iq(st,Ae));let Wr=null;if(lt&&mn&&!At.extent&&Bt){const{positionAbsolute:mi}=At.internals,Fi=mi.x-Bt.x+mn[0][0],fu=mi.x+At.measured.width-Bt.x2+mn[1][0],Eu=mi.y-Bt.y+mn[0][1],Ws=mi.y+At.measured.height-Bt.y2+mn[1][1];Wr=[[Fi,Eu],[fu,Ws]]}const{position:Mr,positionAbsolute:ur}=Hdn({nodeId:Gt,nextPosition:st,nodeLookup:Fe,nodeExtent:Wr||mn,nodeOrigin:sn,onError:xe});rt=rt||At.position.x!==Mr.x||At.position.y!==Mr.y,At.position=Mr,At.internals.positionAbsolute=ur}if(le=le||rt,!!rt&&(un(H,!0),ee&&(M||Sn||!rn&&kn))){const[Gt,At]=R7e({nodeId:rn,dragItems:H,nodeLookup:Fe});M?.(ee,H,Gt,At),Sn?.(ee,Gt,At),rn||kn?.(ee,At)}}async function hn(){if(!ie)return;const{transform:Qn,panBy:Y,autoPanSpeed:Fe,autoPanOnNodeDrag:mn}=E();if(!mn){U=!1,cancelAnimationFrame(k);return}const[Ae,Ze]=qdn(G,ie,Fe);(Ae!==0||Ze!==0)&&(P.x=(P.x??0)-Ae/Qn[2],P.y=(P.y??0)-Ze/Qn[2],await Y({x:Ae,y:Ze})&&xn(P)),k=requestAnimationFrame(hn)}function wt(Qn){const{nodeLookup:Y,multiSelectionActive:Fe,nodesDraggable:mn,transform:Ae,snapGrid:Ze,snapToGrid:sn,selectNodesOnDrag:Sn,onNodeDragStart:kn,onSelectionDragStart:xe,unselectNodesAndEdges:un}=E();Z=!0,(!Sn||!Be)&&!Fe&&rn&&(Y.get(rn)?.selected||un()),Be&&Sn&&rn&&g?.(rn);const rt=BG(Qn.sourceEvent,{transform:Ae,snapGrid:Ze,snapToGrid:sn,containerBounds:ie});if(P=rt,H=vGn(Y,mn,rt,rn),H.size>0&&(x||kn||!rn&&xe)){const[lt,Bt]=R7e({nodeId:rn,dragItems:H,nodeLookup:Y});x?.(Qn.sourceEvent,H,lt,Bt),kn?.(Qn.sourceEvent,lt,Bt),rn||xe?.(Qn.sourceEvent,Bt)}}const Tn=jdn().clickDistance(ln).on("start",Qn=>{const{domNode:Y,nodeDragThreshold:Fe,transform:mn,snapGrid:Ae,snapToGrid:Ze}=E();ie=Y?.getBoundingClientRect()||null,se=!1,le=!1,ee=Qn.sourceEvent,Fe===0&&wt(Qn),P=BG(Qn.sourceEvent,{transform:mn,snapGrid:Ae,snapToGrid:Ze,containerBounds:ie}),G=Vm(Qn.sourceEvent,ie)}).on("drag",Qn=>{const{autoPanOnNodeDrag:Y,transform:Fe,snapGrid:mn,snapToGrid:Ae,nodeDragThreshold:Ze,nodeLookup:sn}=E(),Sn=BG(Qn.sourceEvent,{transform:Fe,snapGrid:mn,snapToGrid:Ae,containerBounds:ie});if(ee=Qn.sourceEvent,(Qn.sourceEvent.type==="touchmove"&&Qn.sourceEvent.touches.length>1||rn&&!sn.has(rn))&&(se=!0),!se){if(!U&&Y&&Z&&(U=!0,hn()),!Z){const kn=Vm(Qn.sourceEvent,ie),xe=kn.x-G.x,un=kn.y-G.y;Math.sqrt(xe*xe+un*un)>Ze&&wt(Qn)}(P.x!==Sn.xSnapped||P.y!==Sn.ySnapped)&&H&&Z&&(G=Vm(Qn.sourceEvent,ie),xn(Sn))}}).on("end",Qn=>{if(!(!Z||se)&&(U=!1,Z=!1,cancelAnimationFrame(k),H.size>0)){const{nodeLookup:Y,updateNodePositions:Fe,onNodeDragStop:mn,onSelectionDragStop:Ae}=E();if(le&&(Fe(H,!1),le=!1),N||mn||!rn&&Ae){const[Ze,sn]=R7e({nodeId:rn,dragItems:H,nodeLookup:Y,dragging:!1});N?.(Qn.sourceEvent,H,Ze,sn),mn?.(Qn.sourceEvent,Ze,sn),rn||Ae?.(Qn.sourceEvent,sn)}}}).filter(Qn=>{const Y=Qn.target;return!Qn.button&&(!Pe||!y1n(Y,`.${Pe}`,Me))&&(!ae||y1n(Y,ae,Me))});W.call(Tn)}function ge(){W?.on(".drag",null)}return{update:Oe,destroy:ge}}function jGn(g,E,x){const M=[],N={x:g.x-x,y:g.y-x,width:x*2,height:x*2};for(const P of E.values())XG(N,wI(P))>0&&M.push(P);return M}const EGn=250;function SGn(g,E,x,M){let N=[],P=1/0;const k=jGn(g,x,E+EGn);for(const H of k){const U=[...H.internals.handleBounds?.source??[],...H.internals.handleBounds?.target??[]];for(const G of U){if(M.nodeId===G.nodeId&&M.type===G.type&&M.id===G.id)continue;const{x:ie,y:Z}=AA(H,G,G.position,!0),W=Math.sqrt(Math.pow(ie-g.x,2)+Math.pow(Z-g.y,2));W>E||(W1){const H=M.type==="source"?"target":"source";return N.find(U=>U.type===H)??N[0]}return N[0]}function u0n(g,E,x,M,N,P=!1){const k=M.get(g);if(!k)return null;const H=N==="strict"?k.internals.handleBounds?.[E]:[...k.internals.handleBounds?.source??[],...k.internals.handleBounds?.target??[]],U=(x?H?.find(G=>G.id===x):H?.[0])??null;return U&&P?{...U,...AA(k,U,U.position,!0)}:U}function o0n(g,E){return g||(E?.classList.contains("target")?"target":E?.classList.contains("source")?"source":null)}function xGn(g,E){let x=null;return E?x=!0:g&&!E&&(x=!1),x}const s0n=()=>!0;function AGn(g,{connectionMode:E,connectionRadius:x,handleId:M,nodeId:N,edgeUpdaterType:P,isTarget:k,domNode:H,nodeLookup:U,lib:G,autoPanOnConnect:ie,flowId:Z,panBy:W,cancelConnection:se,onConnectStart:le,onConnect:ee,onConnectEnd:Oe,isValidConnection:ge=s0n,onReconnectEnd:Pe,updateConnection:ae,getTransform:Me,getFromHandle:Be,autoPanSpeed:rn,dragThreshold:ln=1,handleDomNode:xn}){const hn=Vdn(g.target);let wt=0,Tn;const{x:Qn,y:Y}=Vm(g),Fe=o0n(P,xn),mn=H?.getBoundingClientRect();let Ae=!1;if(!mn||!Fe)return;const Ze=u0n(N,Fe,M,U,E);if(!Ze)return;let sn=Vm(g,mn),Sn=!1,kn=null,xe=!1,un=null;function rt(){if(!ie||!mn)return;const[Mr,ur]=qdn(sn,mn,rn);W({x:Mr,y:ur}),wt=requestAnimationFrame(rt)}const lt={...Ze,nodeId:N,type:Fe,position:Ze.position},Bt=U.get(N);let Gt={inProgress:!0,isValid:null,from:AA(Bt,lt,cr.Left,!0),fromHandle:lt,fromPosition:lt.position,fromNode:Bt,to:sn,toHandle:null,toPosition:s1n[lt.position],toNode:null,pointer:sn};function At(){Ae=!0,ae(Gt),le?.(g,{nodeId:N,handleId:M,handleType:Fe})}ln===0&&At();function st(Mr){if(!Ae){const{x:Ws,y:Dh}=Vm(Mr),ef=Ws-Qn,ch=Dh-Y;if(!(ef*ef+ch*ch>ln*ln))return;At()}if(!Be()||!lt){Wr(Mr);return}const ur=Me();sn=Vm(Mr,mn),Tn=SGn(rq(sn,ur,!1,[1,1]),x,U,lt),Sn||(rt(),Sn=!0);const mi=l0n(Mr,{handle:Tn,connectionMode:E,fromNodeId:N,fromHandleId:M,fromType:k?"target":"source",isValidConnection:ge,doc:hn,lib:G,flowId:Z,nodeLookup:U});un=mi.handleDomNode,kn=mi.connection,xe=xGn(!!Tn,mi.isValid);const Fi=U.get(N),fu=Fi?AA(Fi,lt,cr.Left,!0):Gt.from,Eu={...Gt,from:fu,isValid:xe,to:mi.toHandle&&xe?xue({x:mi.toHandle.x,y:mi.toHandle.y},ur):sn,toHandle:mi.toHandle,toPosition:xe&&mi.toHandle?mi.toHandle.position:s1n[lt.position],toNode:mi.toHandle?U.get(mi.toHandle.nodeId):null,pointer:sn};ae(Eu),Gt=Eu}function Wr(Mr){if(!("touches"in Mr&&Mr.touches.length>0)){if(Ae){(Tn||un)&&kn&&xe&&ee?.(kn);const{inProgress:ur,...mi}=Gt,Fi={...mi,toPosition:Gt.toHandle?Gt.toPosition:null};Oe?.(Mr,Fi),P&&Pe?.(Mr,Fi)}se(),cancelAnimationFrame(wt),Sn=!1,xe=!1,kn=null,un=null,hn.removeEventListener("mousemove",st),hn.removeEventListener("mouseup",Wr),hn.removeEventListener("touchmove",st),hn.removeEventListener("touchend",Wr)}}hn.addEventListener("mousemove",st),hn.addEventListener("mouseup",Wr),hn.addEventListener("touchmove",st),hn.addEventListener("touchend",Wr)}function l0n(g,{handle:E,connectionMode:x,fromNodeId:M,fromHandleId:N,fromType:P,doc:k,lib:H,flowId:U,isValidConnection:G=s0n,nodeLookup:ie}){const Z=P==="target",W=E?k.querySelector(`.${H}-flow__handle[data-id="${U}-${E?.nodeId}-${E?.id}-${E?.type}"]`):null,{x:se,y:le}=Vm(g),ee=k.elementFromPoint(se,le),Oe=ee?.classList.contains(`${H}-flow__handle`)?ee:W,ge={handleDomNode:Oe,isValid:!1,connection:null,toHandle:null};if(Oe){const Pe=o0n(void 0,Oe),ae=Oe.getAttribute("data-nodeid"),Me=Oe.getAttribute("data-handleid"),Be=Oe.classList.contains("connectable"),rn=Oe.classList.contains("connectableend");if(!ae||!Pe)return ge;const ln={source:Z?ae:M,sourceHandle:Z?Me:N,target:Z?M:ae,targetHandle:Z?N:Me};ge.connection=ln;const hn=Be&&rn&&(x===bI.Strict?Z&&Pe==="source"||!Z&&Pe==="target":ae!==M||Me!==N);ge.isValid=hn&&G(ln),ge.toHandle=u0n(ae,Pe,Me,ie,x,!0)}return ge}const fke={onPointerDown:AGn,isValid:l0n};function MGn({domNode:g,panZoom:E,getTransform:x,getViewScale:M}){const N=$g(g);function P({translateExtent:H,width:U,height:G,zoomStep:ie=1,pannable:Z=!0,zoomable:W=!0,inversePan:se=!1}){const le=ae=>{if(ae.sourceEvent.type!=="wheel"||!E)return;const Me=x(),Be=ae.sourceEvent.ctrlKey&&KG()?10:1,rn=-ae.sourceEvent.deltaY*(ae.sourceEvent.deltaMode===1?.05:ae.sourceEvent.deltaMode?1:.002)*ie,ln=Me[2]*Math.pow(2,rn*Be);E.scaleTo(ln)};let ee=[0,0];const Oe=ae=>{(ae.sourceEvent.type==="mousedown"||ae.sourceEvent.type==="touchstart")&&(ee=[ae.sourceEvent.clientX??ae.sourceEvent.touches[0].clientX,ae.sourceEvent.clientY??ae.sourceEvent.touches[0].clientY])},ge=ae=>{const Me=x();if(ae.sourceEvent.type!=="mousemove"&&ae.sourceEvent.type!=="touchmove"||!E)return;const Be=[ae.sourceEvent.clientX??ae.sourceEvent.touches[0].clientX,ae.sourceEvent.clientY??ae.sourceEvent.touches[0].clientY],rn=[Be[0]-ee[0],Be[1]-ee[1]];ee=Be;const ln=M()*Math.max(Me[2],Math.log(Me[2]))*(se?-1:1),xn={x:Me[0]-rn[0]*ln,y:Me[1]-rn[1]*ln},hn=[[0,0],[U,G]];E.setViewportConstrained({x:xn.x,y:xn.y,zoom:Me[2]},hn,H)},Pe=$dn().on("start",Oe).on("zoom",Z?ge:null).on("zoom.wheel",W?le:null);N.call(Pe,{})}function k(){N.on("zoom",null)}return{update:P,destroy:k,pointer:Um}}const $ue=g=>({x:g.x,y:g.y,zoom:g.k}),B7e=({x:g,y:E,zoom:x})=>_ue.translate(g,E).scale(x),sI=(g,E)=>g.target.closest(`.${E}`),f0n=(g,E)=>E===2&&Array.isArray(g)&&g.includes(2),CGn=g=>((g*=2)<=1?g*g*g:(g-=2)*g*g+2)/2,z7e=(g,E=0,x=CGn,M=()=>{})=>{const N=typeof E=="number"&&E>0;return N||M(),N?g.transition().duration(E).ease(x).on("end",M):g},a0n=g=>{const E=g.ctrlKey&&KG()?10:1;return-g.deltaY*(g.deltaMode===1?.05:g.deltaMode?1:.002)*E};function TGn({zoomPanValues:g,noWheelClassName:E,d3Selection:x,d3Zoom:M,panOnScrollMode:N,panOnScrollSpeed:P,zoomOnPinch:k,onPanZoomStart:H,onPanZoom:U,onPanZoomEnd:G}){return ie=>{if(sI(ie,E))return ie.ctrlKey&&ie.preventDefault(),!1;ie.preventDefault(),ie.stopImmediatePropagation();const Z=x.property("__zoom").k||1;if(ie.ctrlKey&&k){const Oe=Um(ie),ge=a0n(ie),Pe=Z*Math.pow(2,ge);M.scaleTo(x,Pe,Oe,ie);return}const W=ie.deltaMode===1?20:1;let se=N===jA.Vertical?0:ie.deltaX*W,le=N===jA.Horizontal?0:ie.deltaY*W;!KG()&&ie.shiftKey&&N!==jA.Vertical&&(se=ie.deltaY*W,le=0),M.translateBy(x,-(se/Z)*P,-(le/Z)*P,{internal:!0});const ee=$ue(x.property("__zoom"));clearTimeout(g.panScrollTimeout),g.isPanScrolling?(U?.(ie,ee),g.panScrollTimeout=setTimeout(()=>{G?.(ie,ee),g.isPanScrolling=!1},150)):(g.isPanScrolling=!0,H?.(ie,ee))}}function OGn({noWheelClassName:g,preventScrolling:E,d3ZoomHandler:x}){return function(M,N){const P=M.type==="wheel",k=!E&&P&&!M.ctrlKey,H=sI(M,g);if(M.ctrlKey&&P&&H&&M.preventDefault(),k||H)return null;M.preventDefault(),x.call(this,M,N)}}function NGn({zoomPanValues:g,onDraggingChange:E,onPanZoomStart:x}){return M=>{if(M.sourceEvent?.internal)return;const N=$ue(M.transform);g.mouseButton=M.sourceEvent?.button||0,g.isZoomingOrPanning=!0,g.prevViewport=N,M.sourceEvent?.type==="mousedown"&&E(!0),x&&x?.(M.sourceEvent,N)}}function DGn({zoomPanValues:g,panOnDrag:E,onPaneContextMenu:x,onTransformChange:M,onPanZoom:N}){return P=>{g.usedRightMouseButton=!!(x&&f0n(E,g.mouseButton??0)),P.sourceEvent?.sync||M([P.transform.x,P.transform.y,P.transform.k]),N&&!P.sourceEvent?.internal&&N?.(P.sourceEvent,$ue(P.transform))}}function IGn({zoomPanValues:g,panOnDrag:E,panOnScroll:x,onDraggingChange:M,onPanZoomEnd:N,onPaneContextMenu:P}){return k=>{if(!k.sourceEvent?.internal&&(g.isZoomingOrPanning=!1,P&&f0n(E,g.mouseButton??0)&&!g.usedRightMouseButton&&k.sourceEvent&&P(k.sourceEvent),g.usedRightMouseButton=!1,M(!1),N)){const H=$ue(k.transform);g.prevViewport=H,clearTimeout(g.timerId),g.timerId=setTimeout(()=>{N?.(k.sourceEvent,H)},x?150:0)}}}function _Gn({zoomActivationKeyPressed:g,zoomOnScroll:E,zoomOnPinch:x,panOnDrag:M,panOnScroll:N,zoomOnDoubleClick:P,userSelectionActive:k,noWheelClassName:H,noPanClassName:U,lib:G,connectionInProgress:ie}){return Z=>{const W=g||E,se=x&&Z.ctrlKey,le=Z.type==="wheel";if(Z.button===1&&Z.type==="mousedown"&&(sI(Z,`${G}-flow__node`)||sI(Z,`${G}-flow__edge`)))return!0;if(!M&&!W&&!N&&!P&&!x||k||ie&&!le||sI(Z,H)&&le||sI(Z,U)&&(!le||N&&le&&!g)||!x&&Z.ctrlKey&&le)return!1;if(!x&&Z.type==="touchstart"&&Z.touches?.length>1)return Z.preventDefault(),!1;if(!W&&!N&&!se&&le||!M&&(Z.type==="mousedown"||Z.type==="touchstart")||Array.isArray(M)&&!M.includes(Z.button)&&Z.type==="mousedown")return!1;const ee=Array.isArray(M)&&M.includes(Z.button)||!Z.button||Z.button<=1;return(!Z.ctrlKey||le)&&ee}}function LGn({domNode:g,minZoom:E,maxZoom:x,translateExtent:M,viewport:N,onPanZoom:P,onPanZoomStart:k,onPanZoomEnd:H,onDraggingChange:U}){const G={isZoomingOrPanning:!1,usedRightMouseButton:!1,prevViewport:{},mouseButton:0,timerId:void 0,panScrollTimeout:void 0,isPanScrolling:!1},ie=g.getBoundingClientRect(),Z=$dn().scaleExtent([E,x]).translateExtent(M),W=$g(g).call(Z);Pe({x:N.x,y:N.y,zoom:gI(N.zoom,E,x)},[[0,0],[ie.width,ie.height]],M);const se=W.on("wheel.zoom"),le=W.on("dblclick.zoom");Z.wheelDelta(a0n);function ee(Tn,Qn){return W?new Promise(Y=>{Z?.interpolate(Qn?.interpolate==="linear"?RG:hue).transform(z7e(W,Qn?.duration,Qn?.ease,()=>Y(!0)),Tn)}):Promise.resolve(!1)}function Oe({noWheelClassName:Tn,noPanClassName:Qn,onPaneContextMenu:Y,userSelectionActive:Fe,panOnScroll:mn,panOnDrag:Ae,panOnScrollMode:Ze,panOnScrollSpeed:sn,preventScrolling:Sn,zoomOnPinch:kn,zoomOnScroll:xe,zoomOnDoubleClick:un,zoomActivationKeyPressed:rt,lib:lt,onTransformChange:Bt,connectionInProgress:hi,paneClickDistance:Gt,selectionOnDrag:At}){Fe&&!G.isZoomingOrPanning&&ge();const st=mn&&!rt&&!Fe;Z.clickDistance(At?1/0:!Km(Gt)||Gt<0?0:Gt);const Wr=st?TGn({zoomPanValues:G,noWheelClassName:Tn,d3Selection:W,d3Zoom:Z,panOnScrollMode:Ze,panOnScrollSpeed:sn,zoomOnPinch:kn,onPanZoomStart:k,onPanZoom:P,onPanZoomEnd:H}):OGn({noWheelClassName:Tn,preventScrolling:Sn,d3ZoomHandler:se});if(W.on("wheel.zoom",Wr,{passive:!1}),!Fe){const ur=NGn({zoomPanValues:G,onDraggingChange:U,onPanZoomStart:k});Z.on("start",ur);const mi=DGn({zoomPanValues:G,panOnDrag:Ae,onPaneContextMenu:!!Y,onPanZoom:P,onTransformChange:Bt});Z.on("zoom",mi);const Fi=IGn({zoomPanValues:G,panOnDrag:Ae,panOnScroll:mn,onPaneContextMenu:Y,onPanZoomEnd:H,onDraggingChange:U});Z.on("end",Fi)}const Mr=_Gn({zoomActivationKeyPressed:rt,panOnDrag:Ae,zoomOnScroll:xe,panOnScroll:mn,zoomOnDoubleClick:un,zoomOnPinch:kn,userSelectionActive:Fe,noPanClassName:Qn,noWheelClassName:Tn,lib:lt,connectionInProgress:hi});Z.filter(Mr),un?W.on("dblclick.zoom",le):W.on("dblclick.zoom",null)}function ge(){Z.on("zoom",null)}async function Pe(Tn,Qn,Y){const Fe=B7e(Tn),mn=Z?.constrain()(Fe,Qn,Y);return mn&&await ee(mn),new Promise(Ae=>Ae(mn))}async function ae(Tn,Qn){const Y=B7e(Tn);return await ee(Y,Qn),new Promise(Fe=>Fe(Y))}function Me(Tn){if(W){const Qn=B7e(Tn),Y=W.property("__zoom");(Y.k!==Tn.zoom||Y.x!==Tn.x||Y.y!==Tn.y)&&Z?.transform(W,Qn,null,{sync:!0})}}function Be(){const Tn=W?Pdn(W.node()):{x:0,y:0,k:1};return{x:Tn.x,y:Tn.y,zoom:Tn.k}}function rn(Tn,Qn){return W?new Promise(Y=>{Z?.interpolate(Qn?.interpolate==="linear"?RG:hue).scaleTo(z7e(W,Qn?.duration,Qn?.ease,()=>Y(!0)),Tn)}):Promise.resolve(!1)}function ln(Tn,Qn){return W?new Promise(Y=>{Z?.interpolate(Qn?.interpolate==="linear"?RG:hue).scaleBy(z7e(W,Qn?.duration,Qn?.ease,()=>Y(!0)),Tn)}):Promise.resolve(!1)}function xn(Tn){Z?.scaleExtent(Tn)}function hn(Tn){Z?.translateExtent(Tn)}function wt(Tn){const Qn=!Km(Tn)||Tn<0?0:Tn;Z?.clickDistance(Qn)}return{update:Oe,destroy:ge,setViewport:ae,setViewportConstrained:Pe,getViewport:Be,scaleTo:rn,scaleBy:ln,setScaleExtent:xn,setTranslateExtent:hn,syncViewport:Me,setClickDistance:wt}}var mI;(function(g){g.Line="line",g.Handle="handle"})(mI||(mI={}));function PGn({width:g,prevWidth:E,height:x,prevHeight:M,affectsX:N,affectsY:P}){const k=g-E,H=x-M,U=[k>0?1:k<0?-1:0,H>0?1:H<0?-1:0];return k&&N&&(U[0]=U[0]*-1),H&&P&&(U[1]=U[1]*-1),U}function k1n(g){const E=g.includes("right")||g.includes("left"),x=g.includes("bottom")||g.includes("top"),M=g.includes("left"),N=g.includes("top");return{isHorizontal:E,isVertical:x,affectsX:M,affectsY:N}}function Y7(g,E){return Math.max(0,E-g)}function Q7(g,E){return Math.max(0,g-E)}function tue(g,E,x){return Math.max(0,E-g,g-x)}function j1n(g,E){return g?!E:E}function $Gn(g,E,x,M,N,P,k,H){let{affectsX:U,affectsY:G}=E;const{isHorizontal:ie,isVertical:Z}=E,W=ie&&Z,{xSnapped:se,ySnapped:le}=x,{minWidth:ee,maxWidth:Oe,minHeight:ge,maxHeight:Pe}=M,{x:ae,y:Me,width:Be,height:rn,aspectRatio:ln}=g;let xn=Math.floor(ie?se-g.pointerX:0),hn=Math.floor(Z?le-g.pointerY:0);const wt=Be+(U?-xn:xn),Tn=rn+(G?-hn:hn),Qn=-P[0]*Be,Y=-P[1]*rn;let Fe=tue(wt,ee,Oe),mn=tue(Tn,ge,Pe);if(k){let sn=0,Sn=0;U&&xn<0?sn=Y7(ae+xn+Qn,k[0][0]):!U&&xn>0&&(sn=Q7(ae+wt+Qn,k[1][0])),G&&hn<0?Sn=Y7(Me+hn+Y,k[0][1]):!G&&hn>0&&(Sn=Q7(Me+Tn+Y,k[1][1])),Fe=Math.max(Fe,sn),mn=Math.max(mn,Sn)}if(H){let sn=0,Sn=0;U&&xn>0?sn=Q7(ae+xn,H[0][0]):!U&&xn<0&&(sn=Y7(ae+wt,H[1][0])),G&&hn>0?Sn=Q7(Me+hn,H[0][1]):!G&&hn<0&&(Sn=Y7(Me+Tn,H[1][1])),Fe=Math.max(Fe,sn),mn=Math.max(mn,Sn)}if(N){if(ie){const sn=tue(wt/ln,ge,Pe)*ln;if(Fe=Math.max(Fe,sn),k){let Sn=0;!U&&!G||U&&!G&&W?Sn=Q7(Me+Y+wt/ln,k[1][1])*ln:Sn=Y7(Me+Y+(U?xn:-xn)/ln,k[0][1])*ln,Fe=Math.max(Fe,Sn)}if(H){let Sn=0;!U&&!G||U&&!G&&W?Sn=Y7(Me+wt/ln,H[1][1])*ln:Sn=Q7(Me+(U?xn:-xn)/ln,H[0][1])*ln,Fe=Math.max(Fe,Sn)}}if(Z){const sn=tue(Tn*ln,ee,Oe)/ln;if(mn=Math.max(mn,sn),k){let Sn=0;!U&&!G||G&&!U&&W?Sn=Q7(ae+Tn*ln+Qn,k[1][0])/ln:Sn=Y7(ae+(G?hn:-hn)*ln+Qn,k[0][0])/ln,mn=Math.max(mn,Sn)}if(H){let Sn=0;!U&&!G||G&&!U&&W?Sn=Y7(ae+Tn*ln,H[1][0])/ln:Sn=Q7(ae+(G?hn:-hn)*ln,H[0][0])/ln,mn=Math.max(mn,Sn)}}}hn=hn+(hn<0?mn:-mn),xn=xn+(xn<0?Fe:-Fe),N&&(W?wt>Tn*ln?hn=(j1n(U,G)?-xn:xn)/ln:xn=(j1n(U,G)?-hn:hn)*ln:ie?(hn=xn/ln,G=U):(xn=hn*ln,U=G));const Ae=U?ae+xn:ae,Ze=G?Me+hn:Me;return{width:Be+(U?-xn:xn),height:rn+(G?-hn:hn),x:P[0]*xn*(U?-1:1)+Ae,y:P[1]*hn*(G?-1:1)+Ze}}const h0n={width:0,height:0,x:0,y:0},RGn={...h0n,pointerX:0,pointerY:0,aspectRatio:1};function BGn(g){return[[0,0],[g.measured.width,g.measured.height]]}function zGn(g,E,x){const M=E.position.x+g.position.x,N=E.position.y+g.position.y,P=g.measured.width??0,k=g.measured.height??0,H=x[0]*P,U=x[1]*k;return[[M-H,N-U],[M+P-H,N+k-U]]}function FGn({domNode:g,nodeId:E,getStoreItems:x,onChange:M,onEnd:N}){const P=$g(g);let k={controlDirection:k1n("bottom-right"),boundaries:{minWidth:0,minHeight:0,maxWidth:Number.MAX_VALUE,maxHeight:Number.MAX_VALUE},resizeDirection:void 0,keepAspectRatio:!1};function H({controlPosition:G,boundaries:ie,keepAspectRatio:Z,resizeDirection:W,onResizeStart:se,onResize:le,onResizeEnd:ee,shouldResize:Oe}){let ge={...h0n},Pe={...RGn};k={boundaries:ie,resizeDirection:W,keepAspectRatio:Z,controlDirection:k1n(G)};let ae,Me=null,Be=[],rn,ln,xn,hn=!1;const wt=jdn().on("start",Tn=>{const{nodeLookup:Qn,transform:Y,snapGrid:Fe,snapToGrid:mn,nodeOrigin:Ae,paneDomNode:Ze}=x();if(ae=Qn.get(E),!ae)return;Me=Ze?.getBoundingClientRect()??null;const{xSnapped:sn,ySnapped:Sn}=BG(Tn.sourceEvent,{transform:Y,snapGrid:Fe,snapToGrid:mn,containerBounds:Me});ge={width:ae.measured.width??0,height:ae.measured.height??0,x:ae.position.x??0,y:ae.position.y??0},Pe={...ge,pointerX:sn,pointerY:Sn,aspectRatio:ge.width/ge.height},rn=void 0,ae.parentId&&(ae.extent==="parent"||ae.expandParent)&&(rn=Qn.get(ae.parentId),ln=rn&&ae.extent==="parent"?BGn(rn):void 0),Be=[],xn=void 0;for(const[kn,xe]of Qn)if(xe.parentId===E&&(Be.push({id:kn,position:{...xe.position},extent:xe.extent}),xe.extent==="parent"||xe.expandParent)){const un=zGn(xe,ae,xe.origin??Ae);xn?xn=[[Math.min(un[0][0],xn[0][0]),Math.min(un[0][1],xn[0][1])],[Math.max(un[1][0],xn[1][0]),Math.max(un[1][1],xn[1][1])]]:xn=un}se?.(Tn,{...ge})}).on("drag",Tn=>{const{transform:Qn,snapGrid:Y,snapToGrid:Fe,nodeOrigin:mn}=x(),Ae=BG(Tn.sourceEvent,{transform:Qn,snapGrid:Y,snapToGrid:Fe,containerBounds:Me}),Ze=[];if(!ae)return;const{x:sn,y:Sn,width:kn,height:xe}=ge,un={},rt=ae.origin??mn,{width:lt,height:Bt,x:hi,y:Gt}=$Gn(Pe,k.controlDirection,Ae,k.boundaries,k.keepAspectRatio,rt,ln,xn),At=lt!==kn,st=Bt!==xe,Wr=hi!==sn&&At,Mr=Gt!==Sn&&st;if(!Wr&&!Mr&&!At&&!st)return;if((Wr||Mr||rt[0]===1||rt[1]===1)&&(un.x=Wr?hi:ge.x,un.y=Mr?Gt:ge.y,ge.x=un.x,ge.y=un.y,Be.length>0)){const fu=hi-sn,Eu=Gt-Sn;for(const Ws of Be)Ws.position={x:Ws.position.x-fu+rt[0]*(lt-kn),y:Ws.position.y-Eu+rt[1]*(Bt-xe)},Ze.push(Ws)}if((At||st)&&(un.width=At&&(!k.resizeDirection||k.resizeDirection==="horizontal")?lt:ge.width,un.height=st&&(!k.resizeDirection||k.resizeDirection==="vertical")?Bt:ge.height,ge.width=un.width,ge.height=un.height),rn&&ae.expandParent){const fu=rt[0]*(un.width??0);un.x&&un.x{hn&&(ee?.(Tn,{...ge}),N?.({...ge}),hn=!1)});P.call(wt)}function U(){P.on(".drag",null)}return{update:H,destroy:U}}var F7e={exports:{}},J7e={},H7e={exports:{}},G7e={};var E1n;function JGn(){if(E1n)return G7e;E1n=1;var g=WG();function E(Z,W){return Z===W&&(Z!==0||1/Z===1/W)||Z!==Z&&W!==W}var x=typeof Object.is=="function"?Object.is:E,M=g.useState,N=g.useEffect,P=g.useLayoutEffect,k=g.useDebugValue;function H(Z,W){var se=W(),le=M({inst:{value:se,getSnapshot:W}}),ee=le[0].inst,Oe=le[1];return P(function(){ee.value=se,ee.getSnapshot=W,U(ee)&&Oe({inst:ee})},[Z,se,W]),N(function(){return U(ee)&&Oe({inst:ee}),Z(function(){U(ee)&&Oe({inst:ee})})},[Z]),k(se),se}function U(Z){var W=Z.getSnapshot;Z=Z.value;try{var se=W();return!x(Z,se)}catch{return!0}}function G(Z,W){return W()}var ie=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?G:H;return G7e.useSyncExternalStore=g.useSyncExternalStore!==void 0?g.useSyncExternalStore:ie,G7e}var S1n;function HGn(){return S1n||(S1n=1,H7e.exports=JGn()),H7e.exports}var x1n;function GGn(){if(x1n)return J7e;x1n=1;var g=WG(),E=HGn();function x(G,ie){return G===ie&&(G!==0||1/G===1/ie)||G!==G&&ie!==ie}var M=typeof Object.is=="function"?Object.is:x,N=E.useSyncExternalStore,P=g.useRef,k=g.useEffect,H=g.useMemo,U=g.useDebugValue;return J7e.useSyncExternalStoreWithSelector=function(G,ie,Z,W,se){var le=P(null);if(le.current===null){var ee={hasValue:!1,value:null};le.current=ee}else ee=le.current;le=H(function(){function ge(rn){if(!Pe){if(Pe=!0,ae=rn,rn=W(rn),se!==void 0&&ee.hasValue){var ln=ee.value;if(se(ln,rn))return Me=ln}return Me=rn}if(ln=Me,M(ae,rn))return ln;var xn=W(rn);return se!==void 0&&se(ln,xn)?(ae=rn,ln):(ae=rn,Me=xn)}var Pe=!1,ae,Me,Be=Z===void 0?null:Z;return[function(){return ge(ie())},Be===null?void 0:function(){return ge(Be())}]},[ie,Z,W,se]);var Oe=N(G,le[0],le[1]);return k(function(){ee.hasValue=!0,ee.value=Oe},[Oe]),U(Oe),Oe},J7e}var A1n;function qGn(){return A1n||(A1n=1,F7e.exports=GGn()),F7e.exports}var UGn=qGn();const XGn=bke(UGn),KGn={},M1n=g=>{let E;const x=new Set,M=(ie,Z)=>{const W=typeof ie=="function"?ie(E):ie;if(!Object.is(W,E)){const se=E;E=Z??(typeof W!="object"||W===null)?W:Object.assign({},E,W),x.forEach(le=>le(E,se))}},N=()=>E,U={setState:M,getState:N,getInitialState:()=>G,subscribe:ie=>(x.add(ie),()=>x.delete(ie)),destroy:()=>{(KGn?"production":void 0)!=="production"&&console.warn("[DEPRECATED] The `destroy` method will be unsupported in a future version. Instead use unsubscribe function returned by subscribe. Everything will be garbage-collected if store is garbage-collected."),x.clear()}},G=E=g(M,N,U);return U},VGn=g=>g?M1n(g):M1n,{useDebugValue:YGn}=izn,{useSyncExternalStoreWithSelector:QGn}=XGn,WGn=g=>g;function d0n(g,E=WGn,x){const M=QGn(g.subscribe,g.getState,g.getServerState||g.getInitialState,E,x);return YGn(M),M}const C1n=(g,E)=>{const x=VGn(g),M=(N,P=E)=>d0n(x,N,P);return Object.assign(M,x),M},ZGn=(g,E)=>g?C1n(g,E):C1n;function El(g,E){if(Object.is(g,E))return!0;if(typeof g!="object"||g===null||typeof E!="object"||E===null)return!1;if(g instanceof Map&&E instanceof Map){if(g.size!==E.size)return!1;for(const[M,N]of g)if(!Object.is(N,E.get(M)))return!1;return!0}if(g instanceof Set&&E instanceof Set){if(g.size!==E.size)return!1;for(const M of g)if(!E.has(M))return!1;return!0}const x=Object.keys(g);if(x.length!==Object.keys(E).length)return!1;for(const M of x)if(!Object.prototype.hasOwnProperty.call(E,M)||!Object.is(g[M],E[M]))return!1;return!0}var eqn=odn();const Rue=He.createContext(null),nqn=Rue.Provider,b0n=u5.error001();function Fu(g,E){const x=He.useContext(Rue);if(x===null)throw new Error(b0n);return d0n(x,g,E)}function Sl(){const g=He.useContext(Rue);if(g===null)throw new Error(b0n);return He.useMemo(()=>({getState:g.getState,setState:g.setState,subscribe:g.subscribe}),[g])}const T1n={display:"none"},tqn={position:"absolute",width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0px, 0px, 0px, 0px)",clipPath:"inset(100%)"},g0n="react-flow__node-desc",w0n="react-flow__edge-desc",iqn="react-flow__aria-live",rqn=g=>g.ariaLiveMessage,cqn=g=>g.ariaLabelConfig;function uqn({rfId:g}){const E=Fu(rqn);return F.jsx("div",{id:`${iqn}-${g}`,"aria-live":"assertive","aria-atomic":"true",style:tqn,children:E})}function oqn({rfId:g,disableKeyboardA11y:E}){const x=Fu(cqn);return F.jsxs(F.Fragment,{children:[F.jsx("div",{id:`${g0n}-${g}`,style:T1n,children:E?x["node.a11yDescription.default"]:x["node.a11yDescription.keyboardDisabled"]}),F.jsx("div",{id:`${w0n}-${g}`,style:T1n,children:x["edge.a11yDescription.default"]}),!E&&F.jsx(uqn,{rfId:g})]})}const Bue=He.forwardRef(({position:g="top-left",children:E,className:x,style:M,...N},P)=>{const k=`${g}`.split("-");return F.jsx("div",{className:Ca(["react-flow__panel",x,...k]),style:M,ref:P,...N,children:E})});Bue.displayName="Panel";function sqn({proOptions:g,position:E="bottom-right"}){return g?.hideAttribution?null:F.jsx(Bue,{position:E,className:"react-flow__attribution","data-message":"Please only hide this attribution when you are subscribed to React Flow Pro: https://pro.reactflow.dev",children:F.jsx("a",{href:"https://reactflow.dev",target:"_blank",rel:"noopener noreferrer","aria-label":"React Flow attribution",children:"React Flow"})})}const lqn=g=>{const E=[],x=[];for(const[,M]of g.nodeLookup)M.selected&&E.push(M.internals.userNode);for(const[,M]of g.edgeLookup)M.selected&&x.push(M);return{selectedNodes:E,selectedEdges:x}},iue=g=>g.id;function fqn(g,E){return El(g.selectedNodes.map(iue),E.selectedNodes.map(iue))&&El(g.selectedEdges.map(iue),E.selectedEdges.map(iue))}function aqn({onSelectionChange:g}){const E=Sl(),{selectedNodes:x,selectedEdges:M}=Fu(lqn,fqn);return He.useEffect(()=>{const N={nodes:x,edges:M};g?.(N),E.getState().onSelectionChangeHandlers.forEach(P=>P(N))},[x,M,g]),null}const hqn=g=>!!g.onSelectionChangeHandlers;function dqn({onSelectionChange:g}){const E=Fu(hqn);return g||E?F.jsx(aqn,{onSelectionChange:g}):null}const ake=typeof window<"u"?He.useLayoutEffect:He.useEffect,p0n=[0,0],bqn={x:0,y:0,zoom:1},gqn=["nodes","edges","defaultNodes","defaultEdges","onConnect","onConnectStart","onConnectEnd","onClickConnectStart","onClickConnectEnd","nodesDraggable","autoPanOnNodeFocus","nodesConnectable","nodesFocusable","edgesFocusable","edgesReconnectable","elevateNodesOnSelect","elevateEdgesOnSelect","minZoom","maxZoom","nodeExtent","onNodesChange","onEdgesChange","elementsSelectable","connectionMode","snapGrid","snapToGrid","translateExtent","connectOnClick","defaultEdgeOptions","fitView","fitViewOptions","onNodesDelete","onEdgesDelete","onDelete","onNodeDrag","onNodeDragStart","onNodeDragStop","onSelectionDrag","onSelectionDragStart","onSelectionDragStop","onMoveStart","onMove","onMoveEnd","noPanClassName","nodeOrigin","autoPanOnConnect","autoPanOnNodeDrag","onError","connectionRadius","isValidConnection","selectNodesOnDrag","nodeDragThreshold","connectionDragThreshold","onBeforeDelete","debug","autoPanSpeed","ariaLabelConfig","zIndexMode"],O1n=[...gqn,"rfId"],wqn=g=>({setNodes:g.setNodes,setEdges:g.setEdges,setMinZoom:g.setMinZoom,setMaxZoom:g.setMaxZoom,setTranslateExtent:g.setTranslateExtent,setNodeExtent:g.setNodeExtent,reset:g.reset,setDefaultNodesAndEdges:g.setDefaultNodesAndEdges}),N1n={translateExtent:GG,nodeOrigin:p0n,minZoom:.5,maxZoom:2,elementsSelectable:!0,noPanClassName:"nopan",rfId:"1"};function pqn(g){const{setNodes:E,setEdges:x,setMinZoom:M,setMaxZoom:N,setTranslateExtent:P,setNodeExtent:k,reset:H,setDefaultNodesAndEdges:U}=Fu(wqn,El),G=Sl();ake(()=>(U(g.defaultNodes,g.defaultEdges),()=>{ie.current=N1n,H()}),[]);const ie=He.useRef(N1n);return ake(()=>{for(const Z of O1n){const W=g[Z],se=ie.current[Z];W!==se&&(typeof g[Z]>"u"||(Z==="nodes"?E(W):Z==="edges"?x(W):Z==="minZoom"?M(W):Z==="maxZoom"?N(W):Z==="translateExtent"?P(W):Z==="nodeExtent"?k(W):Z==="ariaLabelConfig"?G.setState({ariaLabelConfig:ZHn(W)}):Z==="fitView"?G.setState({fitViewQueued:W}):Z==="fitViewOptions"?G.setState({fitViewOptions:W}):G.setState({[Z]:W})))}ie.current=g},O1n.map(Z=>g[Z])),null}function D1n(){return typeof window>"u"||!window.matchMedia?null:window.matchMedia("(prefers-color-scheme: dark)")}function mqn(g){const[E,x]=He.useState(g==="system"?null:g);return He.useEffect(()=>{if(g!=="system"){x(g);return}const M=D1n(),N=()=>x(M?.matches?"dark":"light");return N(),M?.addEventListener("change",N),()=>{M?.removeEventListener("change",N)}},[g]),E!==null?E:D1n()?.matches?"dark":"light"}const I1n=typeof document<"u"?document:null;function VG(g=null,E={target:I1n,actInsideInputWithModifier:!0}){const[x,M]=He.useState(!1),N=He.useRef(!1),P=He.useRef(new Set([])),[k,H]=He.useMemo(()=>{if(g!==null){const G=(Array.isArray(g)?g:[g]).filter(Z=>typeof Z=="string").map(Z=>Z.replace("+",` `).replace(` `,` +`).split(` -`)),ie=G.reduce((Z,W)=>Z.concat(...W),[]);return[G,ie]}return[[],[]]},[g]);return He.useEffect(()=>{const U=E?.target??I1n,G=E?.actInsideInputWithModifier??!0;if(g!==null){const ie=se=>{if(N.current=se.ctrlKey||se.metaKey||se.shiftKey||se.altKey,(!N.current||N.current&&!G)&&Ydn(se))return!1;const ee=L1n(se.code,H);if(P.current.add(se[ee]),_1n(k,P.current,!1)){const Oe=se.composedPath?.()?.[0]||se.target,ge=Oe?.nodeName==="BUTTON"||Oe?.nodeName==="A";E.preventDefault!==!1&&(N.current||!ge)&&se.preventDefault(),M(!0)}},Z=se=>{const le=L1n(se.code,H);_1n(k,P.current,!0)?(M(!1),P.current.clear()):P.current.delete(se[le]),se.key==="Meta"&&P.current.clear(),N.current=!1},W=()=>{P.current.clear(),M(!1)};return U?.addEventListener("keydown",ie),U?.addEventListener("keyup",Z),window.addEventListener("blur",W),window.addEventListener("contextmenu",W),()=>{U?.removeEventListener("keydown",ie),U?.removeEventListener("keyup",Z),window.removeEventListener("blur",W),window.removeEventListener("contextmenu",W)}}},[g,M]),x}function _1n(g,E,x){return g.filter(M=>x||M.length===E.size).some(M=>M.every(N=>E.has(N)))}function L1n(g,E){return E.includes(g)?"code":"key"}const vqn=()=>{const g=Sl();return He.useMemo(()=>({zoomIn:E=>{const{panZoom:x}=g.getState();return x?x.scaleBy(1.2,E):Promise.resolve(!1)},zoomOut:E=>{const{panZoom:x}=g.getState();return x?x.scaleBy(1/1.2,E):Promise.resolve(!1)},zoomTo:(E,x)=>{const{panZoom:M}=g.getState();return M?M.scaleTo(E,x):Promise.resolve(!1)},getZoom:()=>g.getState().transform[2],setViewport:async(E,x)=>{const{transform:[M,N,P],panZoom:k}=g.getState();return k?(await k.setViewport({x:E.x??M,y:E.y??N,zoom:E.zoom??P},x),Promise.resolve(!0)):Promise.resolve(!1)},getViewport:()=>{const[E,x,M]=g.getState().transform;return{x:E,y:x,zoom:M}},setCenter:async(E,x,M)=>g.getState().setCenter(E,x,M),fitBounds:async(E,x)=>{const{width:M,height:N,minZoom:P,maxZoom:k,panZoom:H}=g.getState(),U=Ske(E,M,N,P,k,x?.padding??.1);return H?(await H.setViewport(U,{duration:x?.duration,ease:x?.ease,interpolate:x?.interpolate}),Promise.resolve(!0)):Promise.resolve(!1)},screenToFlowPosition:(E,x={})=>{const{transform:M,snapGrid:N,snapToGrid:P,domNode:k}=g.getState();if(!k)return E;const{x:H,y:U}=k.getBoundingClientRect(),G={x:E.x-H,y:E.y-U},ie=x.snapGrid??N,Z=x.snapToGrid??P;return rq(G,M,Z,ie)},flowToScreenPosition:E=>{const{transform:x,domNode:M}=g.getState();if(!M)return E;const{x:N,y:P}=M.getBoundingClientRect(),k=xue(E,x);return{x:k.x+N,y:k.y+P}}}),[])};function m0n(g,E){const x=[],M=new Map,N=[];for(const P of g)if(P.type==="add"){N.push(P);continue}else if(P.type==="remove"||P.type==="replace")M.set(P.id,[P]);else{const k=M.get(P.id);k?k.push(P):M.set(P.id,[P])}for(const P of E){const k=M.get(P.id);if(!k){x.push(P);continue}if(k[0].type==="remove")continue;if(k[0].type==="replace"){x.push({...k[0].item});continue}const H={...P};for(const U of k)yqn(U,H);x.push(H)}return N.length&&N.forEach(P=>{P.index!==void 0?x.splice(P.index,0,{...P.item}):x.push({...P.item})}),x}function yqn(g,E){switch(g.type){case"select":{E.selected=g.selected;break}case"position":{typeof g.position<"u"&&(E.position=g.position),typeof g.dragging<"u"&&(E.dragging=g.dragging);break}case"dimensions":{typeof g.dimensions<"u"&&(E.measured={...g.dimensions},g.setAttributes&&((g.setAttributes===!0||g.setAttributes==="width")&&(E.width=g.dimensions.width),(g.setAttributes===!0||g.setAttributes==="height")&&(E.height=g.dimensions.height))),typeof g.resizing=="boolean"&&(E.resizing=g.resizing);break}}}function v0n(g,E){return m0n(g,E)}function y0n(g,E){return m0n(g,E)}function vA(g,E){return{id:g,type:"select",selected:E}}function lI(g,E=new Set,x=!1){const M=[];for(const[N,P]of g){const k=E.has(N);!(P.selected===void 0&&!k)&&P.selected!==k&&(x&&(P.selected=k),M.push(vA(P.id,k)))}return M}function P1n({items:g=[],lookup:E}){const x=[],M=new Map(g.map(N=>[N.id,N]));for(const[N,P]of g.entries()){const k=E.get(P.id),H=k?.internals?.userNode??k;H!==void 0&&H!==P&&x.push({id:P.id,item:P,type:"replace"}),H===void 0&&x.push({item:P,type:"add",index:N})}for(const[N]of E)M.get(N)===void 0&&x.push({id:N,type:"remove"});return x}function $1n(g){return{id:g.id,type:"remove"}}const R1n=g=>HHn(g),kqn=g=>Jdn(g);function k0n(g){return He.forwardRef(g)}function B1n(g){const[E,x]=He.useState(BigInt(0)),[M]=He.useState(()=>jqn(()=>x(N=>N+BigInt(1))));return ake(()=>{const N=M.get();N.length&&(g(N),M.reset())},[E]),M}function jqn(g){let E=[];return{get:()=>E,reset:()=>{E=[]},push:x=>{E.push(x),g()}}}const j0n=He.createContext(null);function Eqn({children:g}){const E=Sl(),x=He.useCallback(H=>{const{nodes:U=[],setNodes:G,hasDefaultNodes:ie,onNodesChange:Z,nodeLookup:W,fitViewQueued:se,onNodesChangeMiddlewareMap:le}=E.getState();let ee=U;for(const ge of H)ee=typeof ge=="function"?ge(ee):ge;let Oe=P1n({items:ee,lookup:W});for(const ge of le.values())Oe=ge(Oe);ie&&G(ee),Oe.length>0?Z?.(Oe):se&&window.requestAnimationFrame(()=>{const{fitViewQueued:ge,nodes:Pe,setNodes:ae}=E.getState();ge&&ae(Pe)})},[]),M=B1n(x),N=He.useCallback(H=>{const{edges:U=[],setEdges:G,hasDefaultEdges:ie,onEdgesChange:Z,edgeLookup:W}=E.getState();let se=U;for(const le of H)se=typeof le=="function"?le(se):le;ie?G(se):Z&&Z(P1n({items:se,lookup:W}))},[]),P=B1n(N),k=He.useMemo(()=>({nodeQueue:M,edgeQueue:P}),[]);return F.jsx(j0n.Provider,{value:k,children:g})}function Sqn(){const g=He.useContext(j0n);if(!g)throw new Error("useBatchContext must be used within a BatchProvider");return g}const xqn=g=>!!g.panZoom;function Nke(){const g=vqn(),E=Sl(),x=Sqn(),M=Fu(xqn),N=He.useMemo(()=>{const P=Z=>E.getState().nodeLookup.get(Z),k=Z=>{x.nodeQueue.push(Z)},H=Z=>{x.edgeQueue.push(Z)},U=Z=>{const{nodeLookup:W,nodeOrigin:se}=E.getState(),le=R1n(Z)?Z:W.get(Z.id),ee=le.parentId?Kdn(le.position,le.measured,le.parentId,W,se):le.position,Oe={...le,position:ee,width:le.measured?.width??le.width,height:le.measured?.height??le.height};return wI(Oe)},G=(Z,W,se={replace:!1})=>{k(le=>le.map(ee=>{if(ee.id===Z){const Oe=typeof W=="function"?W(ee):W;return se.replace&&R1n(Oe)?Oe:{...ee,...Oe}}return ee}))},ie=(Z,W,se={replace:!1})=>{H(le=>le.map(ee=>{if(ee.id===Z){const Oe=typeof W=="function"?W(ee):W;return se.replace&&kqn(Oe)?Oe:{...ee,...Oe}}return ee}))};return{getNodes:()=>E.getState().nodes.map(Z=>({...Z})),getNode:Z=>P(Z)?.internals.userNode,getInternalNode:P,getEdges:()=>{const{edges:Z=[]}=E.getState();return Z.map(W=>({...W}))},getEdge:Z=>E.getState().edgeLookup.get(Z),setNodes:k,setEdges:H,addNodes:Z=>{const W=Array.isArray(Z)?Z:[Z];x.nodeQueue.push(se=>[...se,...W])},addEdges:Z=>{const W=Array.isArray(Z)?Z:[Z];x.edgeQueue.push(se=>[...se,...W])},toObject:()=>{const{nodes:Z=[],edges:W=[],transform:se}=E.getState(),[le,ee,Oe]=se;return{nodes:Z.map(ge=>({...ge})),edges:W.map(ge=>({...ge})),viewport:{x:le,y:ee,zoom:Oe}}},deleteElements:async({nodes:Z=[],edges:W=[]})=>{const{nodes:se,edges:le,onNodesDelete:ee,onEdgesDelete:Oe,triggerNodeChanges:ge,triggerEdgeChanges:Pe,onDelete:ae,onBeforeDelete:Me}=E.getState(),{nodes:Be,edges:rn}=await KHn({nodesToRemove:Z,edgesToRemove:W,nodes:se,edges:le,onBeforeDelete:Me}),ln=rn.length>0,xn=Be.length>0;if(ln){const hn=rn.map($1n);Oe?.(rn),Pe(hn)}if(xn){const hn=Be.map($1n);ee?.(Be),ge(hn)}return(xn||ln)&&ae?.({nodes:Be,edges:rn}),{deletedNodes:Be,deletedEdges:rn}},getIntersectingNodes:(Z,W=!0,se)=>{const le=f1n(Z),ee=le?Z:U(Z),Oe=se!==void 0;return ee?(se||E.getState().nodes).filter(ge=>{const Pe=E.getState().nodeLookup.get(ge.id);if(Pe&&!le&&(ge.id===Z.id||!Pe.internals.positionAbsolute))return!1;const ae=wI(Oe?ge:Pe),Me=XG(ae,ee);return W&&Me>0||Me>=ae.width*ae.height||Me>=ee.width*ee.height}):[]},isNodeIntersecting:(Z,W,se=!0)=>{const ee=f1n(Z)?Z:U(Z);if(!ee)return!1;const Oe=XG(ee,W);return se&&Oe>0||Oe>=W.width*W.height||Oe>=ee.width*ee.height},updateNode:G,updateNodeData:(Z,W,se={replace:!1})=>{G(Z,le=>{const ee=typeof W=="function"?W(le):W;return se.replace?{...le,data:ee}:{...le,data:{...le.data,...ee}}},se)},updateEdge:ie,updateEdgeData:(Z,W,se={replace:!1})=>{ie(Z,le=>{const ee=typeof W=="function"?W(le):W;return se.replace?{...le,data:ee}:{...le,data:{...le.data,...ee}}},se)},getNodesBounds:Z=>{const{nodeLookup:W,nodeOrigin:se}=E.getState();return GHn(Z,{nodeLookup:W,nodeOrigin:se})},getHandleConnections:({type:Z,id:W,nodeId:se})=>Array.from(E.getState().connectionLookup.get(`${se}-${Z}${W?`-${W}`:""}`)?.values()??[]),getNodeConnections:({type:Z,handleId:W,nodeId:se})=>Array.from(E.getState().connectionLookup.get(`${se}${Z?W?`-${Z}-${W}`:`-${Z}`:""}`)?.values()??[]),fitView:async Z=>{const W=E.getState().fitViewResolver??WHn();return E.setState({fitViewQueued:!0,fitViewOptions:Z,fitViewResolver:W}),x.nodeQueue.push(se=>[...se]),W.promise}}},[]);return He.useMemo(()=>({...N,...g,viewportInitialized:M}),[M])}const z1n=g=>g.selected,Aqn=typeof window<"u"?window:void 0;function Mqn({deleteKeyCode:g,multiSelectionKeyCode:E}){const x=Sl(),{deleteElements:M}=Nke(),N=VG(g,{actInsideInputWithModifier:!1}),P=VG(E,{target:Aqn});He.useEffect(()=>{if(N){const{edges:k,nodes:H}=x.getState();M({nodes:H.filter(z1n),edges:k.filter(z1n)}),x.setState({nodesSelectionActive:!1})}},[N]),He.useEffect(()=>{x.setState({multiSelectionActive:P})},[P])}function Tqn(g){const E=Sl();He.useEffect(()=>{const x=()=>{if(!g.current||!(g.current.checkVisibility?.()??!0))return!1;const M=xke(g.current);(M.height===0||M.width===0)&&E.getState().onError?.("004",u5.error004()),E.setState({width:M.width||500,height:M.height||500})};if(g.current){x(),window.addEventListener("resize",x);const M=new ResizeObserver(()=>x());return M.observe(g.current),()=>{window.removeEventListener("resize",x),M&&g.current&&M.unobserve(g.current)}}},[])}const zue={position:"absolute",width:"100%",height:"100%",top:0,left:0},Cqn=g=>({userSelectionActive:g.userSelectionActive,lib:g.lib,connectionInProgress:g.connection.inProgress});function Oqn({onPaneContextMenu:g,zoomOnScroll:E=!0,zoomOnPinch:x=!0,panOnScroll:M=!1,panOnScrollSpeed:N=.5,panOnScrollMode:P=jA.Free,zoomOnDoubleClick:k=!0,panOnDrag:H=!0,defaultViewport:U,translateExtent:G,minZoom:ie,maxZoom:Z,zoomActivationKeyCode:W,preventScrolling:se=!0,children:le,noWheelClassName:ee,noPanClassName:Oe,onViewportChange:ge,isControlledViewport:Pe,paneClickDistance:ae,selectionOnDrag:Me}){const Be=Sl(),rn=He.useRef(null),{userSelectionActive:ln,lib:xn,connectionInProgress:hn}=Fu(Cqn,El),wt=VG(W),Cn=He.useRef();Tqn(rn);const Qn=He.useCallback(Y=>{ge?.({x:Y[0],y:Y[1],zoom:Y[2]}),Pe||Be.setState({transform:Y})},[ge,Pe]);return He.useEffect(()=>{if(rn.current){Cn.current=LGn({domNode:rn.current,minZoom:ie,maxZoom:Z,translateExtent:G,viewport:U,onDraggingChange:Ae=>Be.setState(Ze=>Ze.paneDragging===Ae?Ze:{paneDragging:Ae}),onPanZoomStart:(Ae,Ze)=>{const{onViewportChangeStart:sn,onMoveStart:Sn}=Be.getState();Sn?.(Ae,Ze),sn?.(Ze)},onPanZoom:(Ae,Ze)=>{const{onViewportChange:sn,onMove:Sn}=Be.getState();Sn?.(Ae,Ze),sn?.(Ze)},onPanZoomEnd:(Ae,Ze)=>{const{onViewportChangeEnd:sn,onMoveEnd:Sn}=Be.getState();Sn?.(Ae,Ze),sn?.(Ze)}});const{x:Y,y:Fe,zoom:mn}=Cn.current.getViewport();return Be.setState({panZoom:Cn.current,transform:[Y,Fe,mn],domNode:rn.current.closest(".react-flow")}),()=>{Cn.current?.destroy()}}},[]),He.useEffect(()=>{Cn.current?.update({onPaneContextMenu:g,zoomOnScroll:E,zoomOnPinch:x,panOnScroll:M,panOnScrollSpeed:N,panOnScrollMode:P,zoomOnDoubleClick:k,panOnDrag:H,zoomActivationKeyPressed:wt,preventScrolling:se,noPanClassName:Oe,userSelectionActive:ln,noWheelClassName:ee,lib:xn,onTransformChange:Qn,connectionInProgress:hn,selectionOnDrag:Me,paneClickDistance:ae})},[g,E,x,M,N,P,k,H,wt,se,Oe,ln,ee,xn,Qn,hn,Me,ae]),F.jsx("div",{className:"react-flow__renderer",ref:rn,style:zue,children:le})}const Nqn=g=>({userSelectionActive:g.userSelectionActive,userSelectionRect:g.userSelectionRect});function Dqn(){const{userSelectionActive:g,userSelectionRect:E}=Fu(Nqn,El);return g&&E?F.jsx("div",{className:"react-flow__selection react-flow__container",style:{width:E.width,height:E.height,transform:`translate(${E.x}px, ${E.y}px)`}}):null}const q7e=(g,E)=>x=>{x.target===E.current&&g?.(x)},Iqn=g=>({userSelectionActive:g.userSelectionActive,elementsSelectable:g.elementsSelectable,connectionInProgress:g.connection.inProgress,dragging:g.paneDragging});function _qn({isSelecting:g,selectionKeyPressed:E,selectionMode:x=qG.Full,panOnDrag:M,paneClickDistance:N,selectionOnDrag:P,onSelectionStart:k,onSelectionEnd:H,onPaneClick:U,onPaneContextMenu:G,onPaneScroll:ie,onPaneMouseEnter:Z,onPaneMouseMove:W,onPaneMouseLeave:se,children:le}){const ee=Sl(),{userSelectionActive:Oe,elementsSelectable:ge,dragging:Pe,connectionInProgress:ae}=Fu(Iqn,El),Me=ge&&(g||Oe),Be=He.useRef(null),rn=He.useRef(),ln=He.useRef(new Set),xn=He.useRef(new Set),hn=He.useRef(!1),wt=sn=>{if(hn.current||ae){hn.current=!1;return}U?.(sn),ee.getState().resetSelectedElements(),ee.setState({nodesSelectionActive:!1})},Cn=sn=>{if(Array.isArray(M)&&M?.includes(2)){sn.preventDefault();return}G?.(sn)},Qn=ie?sn=>ie(sn):void 0,Y=sn=>{hn.current&&(sn.stopPropagation(),hn.current=!1)},Fe=sn=>{const{domNode:Sn}=ee.getState();if(rn.current=Sn?.getBoundingClientRect(),!rn.current)return;const kn=sn.target===Be.current;if(!kn&&!!sn.target.closest(".nokey")||!g||!(P&&kn||E)||sn.button!==0||!sn.isPrimary)return;sn.target?.setPointerCapture?.(sn.pointerId),hn.current=!1;const{x:rt,y:lt}=Vm(sn.nativeEvent,rn.current);ee.setState({userSelectionRect:{width:0,height:0,startX:rt,startY:lt,x:rt,y:lt}}),kn||(sn.stopPropagation(),sn.preventDefault())},mn=sn=>{const{userSelectionRect:Sn,transform:kn,nodeLookup:xe,edgeLookup:un,connectionLookup:rt,triggerNodeChanges:lt,triggerEdgeChanges:Bt,defaultEdgeOptions:hi,resetSelectedElements:Gt}=ee.getState();if(!rn.current||!Sn)return;const{x:At,y:st}=Vm(sn.nativeEvent,rn.current),{startX:Wr,startY:Mr}=Sn;if(!hn.current){const Eu=E?0:N;if(Math.hypot(At-Wr,st-Mr)<=Eu)return;Gt(),k?.(sn)}hn.current=!0;const ur={startX:Wr,startY:Mr,x:AtEu.id)),xn.current=new Set;const fu=hi?.selectable??!0;for(const Eu of ln.current){const Ws=rt.get(Eu);if(Ws)for(const{edgeId:Dh}of Ws.values()){const ef=un.get(Dh);ef&&(ef.selectable??fu)&&xn.current.add(Dh)}}if(!a1n(mi,ln.current)){const Eu=lI(xe,ln.current,!0);lt(Eu)}if(!a1n(Fi,xn.current)){const Eu=lI(un,xn.current);Bt(Eu)}ee.setState({userSelectionRect:ur,userSelectionActive:!0,nodesSelectionActive:!1})},Ae=sn=>{sn.button===0&&(sn.target?.releasePointerCapture?.(sn.pointerId),!Oe&&sn.target===Be.current&&ee.getState().userSelectionRect&&wt?.(sn),ee.setState({userSelectionActive:!1,userSelectionRect:null}),hn.current&&(H?.(sn),ee.setState({nodesSelectionActive:ln.current.size>0})))},Ze=M===!0||Array.isArray(M)&&M.includes(0);return F.jsxs("div",{className:Ta(["react-flow__pane",{draggable:Ze,dragging:Pe,selection:g}]),onClick:Me?void 0:q7e(wt,Be),onContextMenu:q7e(Cn,Be),onWheel:q7e(Qn,Be),onPointerEnter:Me?void 0:Z,onPointerMove:Me?mn:W,onPointerUp:Me?Ae:void 0,onPointerDownCapture:Me?Fe:void 0,onClickCapture:Me?Y:void 0,onPointerLeave:se,ref:Be,style:zue,children:[le,F.jsx(Dqn,{})]})}function hke({id:g,store:E,unselect:x=!1,nodeRef:M}){const{addSelectedNodes:N,unselectNodesAndEdges:P,multiSelectionActive:k,nodeLookup:H,onError:U}=E.getState(),G=H.get(g);if(!G){U?.("012",u5.error012(g));return}E.setState({nodesSelectionActive:!1}),G.selected?(x||G.selected&&k)&&(P({nodes:[G],edges:[]}),requestAnimationFrame(()=>M?.current?.blur())):N([g])}function E0n({nodeRef:g,disabled:E=!1,noDragClassName:x,handleSelector:M,nodeId:N,isSelectable:P,nodeClickDistance:k}){const H=Sl(),[U,G]=He.useState(!1),ie=He.useRef();return He.useEffect(()=>{ie.current=kGn({getStoreItems:()=>H.getState(),onNodeMouseDown:Z=>{hke({id:Z,store:H,nodeRef:g})},onDragStart:()=>{G(!0)},onDragStop:()=>{G(!1)}})},[]),He.useEffect(()=>{if(!(E||!g.current||!ie.current))return ie.current.update({noDragClassName:x,handleSelector:M,domNode:g.current,isSelectable:P,nodeId:N,nodeClickDistance:k}),()=>{ie.current?.destroy()}},[x,M,E,P,g,N,k]),U}const Lqn=g=>E=>E.selected&&(E.draggable||g&&typeof E.draggable>"u");function S0n(){const g=Sl();return He.useCallback(x=>{const{nodeExtent:M,snapToGrid:N,snapGrid:P,nodesDraggable:k,onError:H,updateNodePositions:U,nodeLookup:G,nodeOrigin:ie}=g.getState(),Z=new Map,W=Lqn(k),se=N?P[0]:5,le=N?P[1]:5,ee=x.direction.x*se*x.factor,Oe=x.direction.y*le*x.factor;for(const[,ge]of G){if(!W(ge))continue;let Pe={x:ge.internals.positionAbsolute.x+ee,y:ge.internals.positionAbsolute.y+Oe};N&&(Pe=iq(Pe,P));const{position:ae,positionAbsolute:Me}=Hdn({nodeId:ge.id,nextPosition:Pe,nodeLookup:G,nodeExtent:M,nodeOrigin:ie,onError:H});ge.position=ae,ge.internals.positionAbsolute=Me,Z.set(ge.id,ge)}U(Z)},[])}const Dke=He.createContext(null),Pqn=Dke.Provider;Dke.Consumer;const x0n=()=>He.useContext(Dke),$qn=g=>({connectOnClick:g.connectOnClick,noPanClassName:g.noPanClassName,rfId:g.rfId}),Rqn=(g,E,x)=>M=>{const{connectionClickStartHandle:N,connectionMode:P,connection:k}=M,{fromHandle:H,toHandle:U,isValid:G}=k,ie=U?.nodeId===g&&U?.id===E&&U?.type===x;return{connectingFrom:H?.nodeId===g&&H?.id===E&&H?.type===x,connectingTo:ie,clickConnecting:N?.nodeId===g&&N?.id===E&&N?.type===x,isPossibleEndHandle:P===bI.Strict?H?.type!==x:g!==H?.nodeId||E!==H?.id,connectionInProcess:!!H,clickConnectionInProcess:!!N,valid:ie&&G}};function Bqn({type:g="source",position:E=cr.Top,isValidConnection:x,isConnectable:M=!0,isConnectableStart:N=!0,isConnectableEnd:P=!0,id:k,onConnect:H,children:U,className:G,onMouseDown:ie,onTouchStart:Z,...W},se){const le=k||null,ee=g==="target",Oe=Sl(),ge=x0n(),{connectOnClick:Pe,noPanClassName:ae,rfId:Me}=Fu($qn,El),{connectingFrom:Be,connectingTo:rn,clickConnecting:ln,isPossibleEndHandle:xn,connectionInProcess:hn,clickConnectionInProcess:wt,valid:Cn}=Fu(Rqn(ge,le,g),El);ge||Oe.getState().onError?.("010",u5.error010());const Qn=mn=>{const{defaultEdgeOptions:Ae,onConnect:Ze,hasDefaultEdges:sn}=Oe.getState(),Sn={...Ae,...mn};if(sn){const{edges:kn,setEdges:xe}=Oe.getState();xe(cGn(Sn,kn))}Ze?.(Sn),H?.(Sn)},Y=mn=>{if(!ge)return;const Ae=Qdn(mn.nativeEvent);if(N&&(Ae&&mn.button===0||!Ae)){const Ze=Oe.getState();fke.onPointerDown(mn.nativeEvent,{handleDomNode:mn.currentTarget,autoPanOnConnect:Ze.autoPanOnConnect,connectionMode:Ze.connectionMode,connectionRadius:Ze.connectionRadius,domNode:Ze.domNode,nodeLookup:Ze.nodeLookup,lib:Ze.lib,isTarget:ee,handleId:le,nodeId:ge,flowId:Ze.rfId,panBy:Ze.panBy,cancelConnection:Ze.cancelConnection,onConnectStart:Ze.onConnectStart,onConnectEnd:(...sn)=>Oe.getState().onConnectEnd?.(...sn),updateConnection:Ze.updateConnection,onConnect:Qn,isValidConnection:x||((...sn)=>Oe.getState().isValidConnection?.(...sn)??!0),getTransform:()=>Oe.getState().transform,getFromHandle:()=>Oe.getState().connection.fromHandle,autoPanSpeed:Ze.autoPanSpeed,dragThreshold:Ze.connectionDragThreshold})}Ae?ie?.(mn):Z?.(mn)},Fe=mn=>{const{onClickConnectStart:Ae,onClickConnectEnd:Ze,connectionClickStartHandle:sn,connectionMode:Sn,isValidConnection:kn,lib:xe,rfId:un,nodeLookup:rt,connection:lt}=Oe.getState();if(!ge||!sn&&!N)return;if(!sn){Ae?.(mn.nativeEvent,{nodeId:ge,handleId:le,handleType:g}),Oe.setState({connectionClickStartHandle:{nodeId:ge,type:g,id:le}});return}const Bt=Vdn(mn.target),hi=x||kn,{connection:Gt,isValid:At}=fke.isValid(mn.nativeEvent,{handle:{nodeId:ge,id:le,type:g},connectionMode:Sn,fromNodeId:sn.nodeId,fromHandleId:sn.id||null,fromType:sn.type,isValidConnection:hi,flowId:un,doc:Bt,lib:xe,nodeLookup:rt});At&&Gt&&Qn(Gt);const st=structuredClone(lt);delete st.inProgress,st.toPosition=st.toHandle?st.toHandle.position:null,Ze?.(mn,st),Oe.setState({connectionClickStartHandle:null})};return F.jsx("div",{"data-handleid":le,"data-nodeid":ge,"data-handlepos":E,"data-id":`${Me}-${ge}-${le}-${g}`,className:Ta(["react-flow__handle",`react-flow__handle-${E}`,"nodrag",ae,G,{source:!ee,target:ee,connectable:M,connectablestart:N,connectableend:P,clickconnecting:ln,connectingfrom:Be,connectingto:rn,valid:Cn,connectionindicator:M&&(!hn||xn)&&(hn||wt?P:N)}]),onMouseDown:Y,onTouchStart:Y,onClick:Pe?Fe:void 0,ref:se,...W,children:U})}const o5=He.memo(k0n(Bqn));function zqn({data:g,isConnectable:E,sourcePosition:x=cr.Bottom}){return F.jsxs(F.Fragment,{children:[g?.label,F.jsx(o5,{type:"source",position:x,isConnectable:E})]})}function Fqn({data:g,isConnectable:E,targetPosition:x=cr.Top,sourcePosition:M=cr.Bottom}){return F.jsxs(F.Fragment,{children:[F.jsx(o5,{type:"target",position:x,isConnectable:E}),g?.label,F.jsx(o5,{type:"source",position:M,isConnectable:E})]})}function Jqn(){return null}function Hqn({data:g,isConnectable:E,targetPosition:x=cr.Top}){return F.jsxs(F.Fragment,{children:[F.jsx(o5,{type:"target",position:x,isConnectable:E}),g?.label]})}const Mue={ArrowUp:{x:0,y:-1},ArrowDown:{x:0,y:1},ArrowLeft:{x:-1,y:0},ArrowRight:{x:1,y:0}},F1n={input:zqn,default:Fqn,output:Hqn,group:Jqn};function Gqn(g){return g.internals.handleBounds===void 0?{width:g.width??g.initialWidth??g.style?.width,height:g.height??g.initialHeight??g.style?.height}:{width:g.width??g.style?.width,height:g.height??g.style?.height}}const qqn=g=>{const{width:E,height:x,x:M,y:N}=tq(g.nodeLookup,{filter:P=>!!P.selected});return{width:Km(E)?E:null,height:Km(x)?x:null,userSelectionActive:g.userSelectionActive,transformString:`translate(${g.transform[0]}px,${g.transform[1]}px) scale(${g.transform[2]}) translate(${M}px,${N}px)`}};function Uqn({onSelectionContextMenu:g,noPanClassName:E,disableKeyboardA11y:x}){const M=Sl(),{width:N,height:P,transformString:k,userSelectionActive:H}=Fu(qqn,El),U=S0n(),G=He.useRef(null);He.useEffect(()=>{x||G.current?.focus({preventScroll:!0})},[x]);const ie=!H&&N!==null&&P!==null;if(E0n({nodeRef:G,disabled:!ie}),!ie)return null;const Z=g?se=>{const le=M.getState().nodes.filter(ee=>ee.selected);g(se,le)}:void 0,W=se=>{Object.prototype.hasOwnProperty.call(Mue,se.key)&&(se.preventDefault(),U({direction:Mue[se.key],factor:se.shiftKey?4:1}))};return F.jsx("div",{className:Ta(["react-flow__nodesselection","react-flow__container",E]),style:{transform:k},children:F.jsx("div",{ref:G,className:"react-flow__nodesselection-rect",onContextMenu:Z,tabIndex:x?void 0:-1,onKeyDown:x?void 0:W,style:{width:N,height:P}})})}const J1n=typeof window<"u"?window:void 0,Xqn=g=>({nodesSelectionActive:g.nodesSelectionActive,userSelectionActive:g.userSelectionActive});function A0n({children:g,onPaneClick:E,onPaneMouseEnter:x,onPaneMouseMove:M,onPaneMouseLeave:N,onPaneContextMenu:P,onPaneScroll:k,paneClickDistance:H,deleteKeyCode:U,selectionKeyCode:G,selectionOnDrag:ie,selectionMode:Z,onSelectionStart:W,onSelectionEnd:se,multiSelectionKeyCode:le,panActivationKeyCode:ee,zoomActivationKeyCode:Oe,elementsSelectable:ge,zoomOnScroll:Pe,zoomOnPinch:ae,panOnScroll:Me,panOnScrollSpeed:Be,panOnScrollMode:rn,zoomOnDoubleClick:ln,panOnDrag:xn,defaultViewport:hn,translateExtent:wt,minZoom:Cn,maxZoom:Qn,preventScrolling:Y,onSelectionContextMenu:Fe,noWheelClassName:mn,noPanClassName:Ae,disableKeyboardA11y:Ze,onViewportChange:sn,isControlledViewport:Sn}){const{nodesSelectionActive:kn,userSelectionActive:xe}=Fu(Xqn,El),un=VG(G,{target:J1n}),rt=VG(ee,{target:J1n}),lt=rt||xn,Bt=rt||Me,hi=ie&<!==!0,Gt=un||xe||hi;return Mqn({deleteKeyCode:U,multiSelectionKeyCode:le}),F.jsx(Oqn,{onPaneContextMenu:P,elementsSelectable:ge,zoomOnScroll:Pe,zoomOnPinch:ae,panOnScroll:Bt,panOnScrollSpeed:Be,panOnScrollMode:rn,zoomOnDoubleClick:ln,panOnDrag:!un&<,defaultViewport:hn,translateExtent:wt,minZoom:Cn,maxZoom:Qn,zoomActivationKeyCode:Oe,preventScrolling:Y,noWheelClassName:mn,noPanClassName:Ae,onViewportChange:sn,isControlledViewport:Sn,paneClickDistance:H,selectionOnDrag:hi,children:F.jsxs(_qn,{onSelectionStart:W,onSelectionEnd:se,onPaneClick:E,onPaneMouseEnter:x,onPaneMouseMove:M,onPaneMouseLeave:N,onPaneContextMenu:P,onPaneScroll:k,panOnDrag:lt,isSelecting:!!Gt,selectionMode:Z,selectionKeyPressed:un,paneClickDistance:H,selectionOnDrag:hi,children:[g,kn&&F.jsx(Uqn,{onSelectionContextMenu:Fe,noPanClassName:Ae,disableKeyboardA11y:Ze})]})})}A0n.displayName="FlowRenderer";const Kqn=He.memo(A0n),Vqn=g=>E=>g?Eke(E.nodeLookup,{x:0,y:0,width:E.width,height:E.height},E.transform,!0).map(x=>x.id):Array.from(E.nodeLookup.keys());function Yqn(g){return Fu(He.useCallback(Vqn(g),[g]),El)}const Qqn=g=>g.updateNodeInternals;function Wqn(){const g=Fu(Qqn),[E]=He.useState(()=>typeof ResizeObserver>"u"?null:new ResizeObserver(x=>{const M=new Map;x.forEach(N=>{const P=N.target.getAttribute("data-id");M.set(P,{id:P,nodeElement:N.target,force:!0})}),g(M)}));return He.useEffect(()=>()=>{E?.disconnect()},[E]),E}function Zqn({node:g,nodeType:E,hasDimensions:x,resizeObserver:M}){const N=Sl(),P=He.useRef(null),k=He.useRef(null),H=He.useRef(g.sourcePosition),U=He.useRef(g.targetPosition),G=He.useRef(E),ie=x&&!!g.internals.handleBounds;return He.useEffect(()=>{P.current&&!g.hidden&&(!ie||k.current!==P.current)&&(k.current&&M?.unobserve(k.current),M?.observe(P.current),k.current=P.current)},[ie,g.hidden]),He.useEffect(()=>()=>{k.current&&(M?.unobserve(k.current),k.current=null)},[]),He.useEffect(()=>{if(P.current){const Z=G.current!==E,W=H.current!==g.sourcePosition,se=U.current!==g.targetPosition;(Z||W||se)&&(G.current=E,H.current=g.sourcePosition,U.current=g.targetPosition,N.getState().updateNodeInternals(new Map([[g.id,{id:g.id,nodeElement:P.current,force:!0}]])))}},[g.id,E,g.sourcePosition,g.targetPosition]),P}function eUn({id:g,onClick:E,onMouseEnter:x,onMouseMove:M,onMouseLeave:N,onContextMenu:P,onDoubleClick:k,nodesDraggable:H,elementsSelectable:U,nodesConnectable:G,nodesFocusable:ie,resizeObserver:Z,noDragClassName:W,noPanClassName:se,disableKeyboardA11y:le,rfId:ee,nodeTypes:Oe,nodeClickDistance:ge,onError:Pe}){const{node:ae,internals:Me,isParent:Be}=Fu(At=>{const st=At.nodeLookup.get(g),Wr=At.parentLookup.has(g);return{node:st,internals:st.internals,isParent:Wr}},El);let rn=ae.type||"default",ln=Oe?.[rn]||F1n[rn];ln===void 0&&(Pe?.("003",u5.error003(rn)),rn="default",ln=Oe?.default||F1n.default);const xn=!!(ae.draggable||H&&typeof ae.draggable>"u"),hn=!!(ae.selectable||U&&typeof ae.selectable>"u"),wt=!!(ae.connectable||G&&typeof ae.connectable>"u"),Cn=!!(ae.focusable||ie&&typeof ae.focusable>"u"),Qn=Sl(),Y=Xdn(ae),Fe=Zqn({node:ae,nodeType:rn,hasDimensions:Y,resizeObserver:Z}),mn=E0n({nodeRef:Fe,disabled:ae.hidden||!xn,noDragClassName:W,handleSelector:ae.dragHandle,nodeId:g,isSelectable:hn,nodeClickDistance:ge}),Ae=S0n();if(ae.hidden)return null;const Ze=i6(ae),sn=Gqn(ae),Sn=hn||xn||E||x||M||N,kn=x?At=>x(At,{...Me.userNode}):void 0,xe=M?At=>M(At,{...Me.userNode}):void 0,un=N?At=>N(At,{...Me.userNode}):void 0,rt=P?At=>P(At,{...Me.userNode}):void 0,lt=k?At=>k(At,{...Me.userNode}):void 0,Bt=At=>{const{selectNodesOnDrag:st,nodeDragThreshold:Wr}=Qn.getState();hn&&(!st||!xn||Wr>0)&&hke({id:g,store:Qn,nodeRef:Fe}),E&&E(At,{...Me.userNode})},hi=At=>{if(!(Ydn(At.nativeEvent)||le)){if(Rdn.includes(At.key)&&hn){const st=At.key==="Escape";hke({id:g,store:Qn,unselect:st,nodeRef:Fe})}else if(xn&&ae.selected&&Object.prototype.hasOwnProperty.call(Mue,At.key)){At.preventDefault();const{ariaLabelConfig:st}=Qn.getState();Qn.setState({ariaLiveMessage:st["node.a11yDescription.ariaLiveMessage"]({direction:At.key.replace("Arrow","").toLowerCase(),x:~~Me.positionAbsolute.x,y:~~Me.positionAbsolute.y})}),Ae({direction:Mue[At.key],factor:At.shiftKey?4:1})}}},Gt=()=>{if(le||!Fe.current?.matches(":focus-visible"))return;const{transform:At,width:st,height:Wr,autoPanOnNodeFocus:Mr,setCenter:ur}=Qn.getState();if(!Mr)return;Eke(new Map([[g,ae]]),{x:0,y:0,width:st,height:Wr},At,!0).length>0||ur(ae.position.x+Ze.width/2,ae.position.y+Ze.height/2,{zoom:At[2]})};return F.jsx("div",{className:Ta(["react-flow__node",`react-flow__node-${rn}`,{[se]:xn},ae.className,{selected:ae.selected,selectable:hn,parent:Be,draggable:xn,dragging:mn}]),ref:Fe,style:{zIndex:Me.z,transform:`translate(${Me.positionAbsolute.x}px,${Me.positionAbsolute.y}px)`,pointerEvents:Sn?"all":"none",visibility:Y?"visible":"hidden",...ae.style,...sn},"data-id":g,"data-testid":`rf__node-${g}`,onMouseEnter:kn,onMouseMove:xe,onMouseLeave:un,onContextMenu:rt,onClick:Bt,onDoubleClick:lt,onKeyDown:Cn?hi:void 0,tabIndex:Cn?0:void 0,onFocus:Cn?Gt:void 0,role:ae.ariaRole??(Cn?"group":void 0),"aria-roledescription":"node","aria-describedby":le?void 0:`${g0n}-${ee}`,"aria-label":ae.ariaLabel,...ae.domAttributes,children:F.jsx(Pqn,{value:g,children:F.jsx(ln,{id:g,data:ae.data,type:rn,positionAbsoluteX:Me.positionAbsolute.x,positionAbsoluteY:Me.positionAbsolute.y,selected:ae.selected??!1,selectable:hn,draggable:xn,deletable:ae.deletable??!0,isConnectable:wt,sourcePosition:ae.sourcePosition,targetPosition:ae.targetPosition,dragging:mn,dragHandle:ae.dragHandle,zIndex:Me.z,parentId:ae.parentId,...Ze})})})}var nUn=He.memo(eUn);const tUn=g=>({nodesDraggable:g.nodesDraggable,nodesConnectable:g.nodesConnectable,nodesFocusable:g.nodesFocusable,elementsSelectable:g.elementsSelectable,onError:g.onError});function M0n(g){const{nodesDraggable:E,nodesConnectable:x,nodesFocusable:M,elementsSelectable:N,onError:P}=Fu(tUn,El),k=Yqn(g.onlyRenderVisibleElements),H=Wqn();return F.jsx("div",{className:"react-flow__nodes",style:zue,children:k.map(U=>F.jsx(nUn,{id:U,nodeTypes:g.nodeTypes,nodeExtent:g.nodeExtent,onClick:g.onNodeClick,onMouseEnter:g.onNodeMouseEnter,onMouseMove:g.onNodeMouseMove,onMouseLeave:g.onNodeMouseLeave,onContextMenu:g.onNodeContextMenu,onDoubleClick:g.onNodeDoubleClick,noDragClassName:g.noDragClassName,noPanClassName:g.noPanClassName,rfId:g.rfId,disableKeyboardA11y:g.disableKeyboardA11y,resizeObserver:H,nodesDraggable:E,nodesConnectable:x,nodesFocusable:M,elementsSelectable:N,nodeClickDistance:g.nodeClickDistance,onError:P},U))})}M0n.displayName="NodeRenderer";const iUn=He.memo(M0n);function rUn(g){return Fu(He.useCallback(x=>{if(!g)return x.edges.map(N=>N.id);const M=[];if(x.width&&x.height)for(const N of x.edges){const P=x.nodeLookup.get(N.source),k=x.nodeLookup.get(N.target);P&&k&&tGn({sourceNode:P,targetNode:k,width:x.width,height:x.height,transform:x.transform})&&M.push(N.id)}return M},[g]),El)}const cUn=({color:g="none",strokeWidth:E=1})=>{const x={strokeWidth:E,...g&&{stroke:g}};return F.jsx("polyline",{className:"arrow",style:x,strokeLinecap:"round",fill:"none",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4"})},uUn=({color:g="none",strokeWidth:E=1})=>{const x={strokeWidth:E,...g&&{stroke:g,fill:g}};return F.jsx("polyline",{className:"arrowclosed",style:x,strokeLinecap:"round",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4 -5,-4"})},H1n={[UG.Arrow]:cUn,[UG.ArrowClosed]:uUn};function oUn(g){const E=Sl();return He.useMemo(()=>Object.prototype.hasOwnProperty.call(H1n,g)?H1n[g]:(E.getState().onError?.("009",u5.error009(g)),null),[g])}const sUn=({id:g,type:E,color:x,width:M=12.5,height:N=12.5,markerUnits:P="strokeWidth",strokeWidth:k,orient:H="auto-start-reverse"})=>{const U=oUn(E);return U?F.jsx("marker",{className:"react-flow__arrowhead",id:g,markerWidth:`${M}`,markerHeight:`${N}`,viewBox:"-10 -10 20 20",markerUnits:P,orient:H,refX:"0",refY:"0",children:F.jsx(U,{color:x,strokeWidth:k})}):null},T0n=({defaultColor:g,rfId:E})=>{const x=Fu(P=>P.edges),M=Fu(P=>P.defaultEdgeOptions),N=He.useMemo(()=>fGn(x,{id:E,defaultColor:g,defaultMarkerStart:M?.markerStart,defaultMarkerEnd:M?.markerEnd}),[x,M,E,g]);return N.length?F.jsx("svg",{className:"react-flow__marker","aria-hidden":"true",children:F.jsx("defs",{children:N.map(P=>F.jsx(sUn,{id:P.id,type:P.type,color:P.color,width:P.width,height:P.height,markerUnits:P.markerUnits,strokeWidth:P.strokeWidth,orient:P.orient},P.id))})}):null};T0n.displayName="MarkerDefinitions";var lUn=He.memo(T0n);function C0n({x:g,y:E,label:x,labelStyle:M,labelShowBg:N=!0,labelBgStyle:P,labelBgPadding:k=[2,4],labelBgBorderRadius:H=2,children:U,className:G,...ie}){const[Z,W]=He.useState({x:1,y:0,width:0,height:0}),se=Ta(["react-flow__edge-textwrapper",G]),le=He.useRef(null);return He.useEffect(()=>{if(le.current){const ee=le.current.getBBox();W({x:ee.x,y:ee.y,width:ee.width,height:ee.height})}},[x]),x?F.jsxs("g",{transform:`translate(${g-Z.width/2} ${E-Z.height/2})`,className:se,visibility:Z.width?"visible":"hidden",...ie,children:[N&&F.jsx("rect",{width:Z.width+2*k[0],x:-k[0],y:-k[1],height:Z.height+2*k[1],className:"react-flow__edge-textbg",style:P,rx:H,ry:H}),F.jsx("text",{className:"react-flow__edge-text",y:Z.height/2,dy:"0.3em",ref:le,style:M,children:x}),U]}):null}C0n.displayName="EdgeText";const fUn=He.memo(C0n);function cq({path:g,labelX:E,labelY:x,label:M,labelStyle:N,labelShowBg:P,labelBgStyle:k,labelBgPadding:H,labelBgBorderRadius:U,interactionWidth:G=20,...ie}){return F.jsxs(F.Fragment,{children:[F.jsx("path",{...ie,d:g,fill:"none",className:Ta(["react-flow__edge-path",ie.className])}),G?F.jsx("path",{d:g,fill:"none",strokeOpacity:0,strokeWidth:G,className:"react-flow__edge-interaction"}):null,M&&Km(E)&&Km(x)?F.jsx(fUn,{x:E,y:x,label:M,labelStyle:N,labelShowBg:P,labelBgStyle:k,labelBgPadding:H,labelBgBorderRadius:U}):null]})}function G1n({pos:g,x1:E,y1:x,x2:M,y2:N}){return g===cr.Left||g===cr.Right?[.5*(E+M),x]:[E,.5*(x+N)]}function O0n({sourceX:g,sourceY:E,sourcePosition:x=cr.Bottom,targetX:M,targetY:N,targetPosition:P=cr.Top}){const[k,H]=G1n({pos:x,x1:g,y1:E,x2:M,y2:N}),[U,G]=G1n({pos:P,x1:M,y1:N,x2:g,y2:E}),[ie,Z,W,se]=Wdn({sourceX:g,sourceY:E,targetX:M,targetY:N,sourceControlX:k,sourceControlY:H,targetControlX:U,targetControlY:G});return[`M${g},${E} C${k},${H} ${U},${G} ${M},${N}`,ie,Z,W,se]}function N0n(g){return He.memo(({id:E,sourceX:x,sourceY:M,targetX:N,targetY:P,sourcePosition:k,targetPosition:H,label:U,labelStyle:G,labelShowBg:ie,labelBgStyle:Z,labelBgPadding:W,labelBgBorderRadius:se,style:le,markerEnd:ee,markerStart:Oe,interactionWidth:ge})=>{const[Pe,ae,Me]=O0n({sourceX:x,sourceY:M,sourcePosition:k,targetX:N,targetY:P,targetPosition:H}),Be=g.isInternal?void 0:E;return F.jsx(cq,{id:Be,path:Pe,labelX:ae,labelY:Me,label:U,labelStyle:G,labelShowBg:ie,labelBgStyle:Z,labelBgPadding:W,labelBgBorderRadius:se,style:le,markerEnd:ee,markerStart:Oe,interactionWidth:ge})})}const aUn=N0n({isInternal:!1}),D0n=N0n({isInternal:!0});aUn.displayName="SimpleBezierEdge";D0n.displayName="SimpleBezierEdgeInternal";function I0n(g){return He.memo(({id:E,sourceX:x,sourceY:M,targetX:N,targetY:P,label:k,labelStyle:H,labelShowBg:U,labelBgStyle:G,labelBgPadding:ie,labelBgBorderRadius:Z,style:W,sourcePosition:se=cr.Bottom,targetPosition:le=cr.Top,markerEnd:ee,markerStart:Oe,pathOptions:ge,interactionWidth:Pe})=>{const[ae,Me,Be]=Aue({sourceX:x,sourceY:M,sourcePosition:se,targetX:N,targetY:P,targetPosition:le,borderRadius:ge?.borderRadius,offset:ge?.offset,stepPosition:ge?.stepPosition}),rn=g.isInternal?void 0:E;return F.jsx(cq,{id:rn,path:ae,labelX:Me,labelY:Be,label:k,labelStyle:H,labelShowBg:U,labelBgStyle:G,labelBgPadding:ie,labelBgBorderRadius:Z,style:W,markerEnd:ee,markerStart:Oe,interactionWidth:Pe})})}const _0n=I0n({isInternal:!1}),L0n=I0n({isInternal:!0});_0n.displayName="SmoothStepEdge";L0n.displayName="SmoothStepEdgeInternal";function P0n(g){return He.memo(({id:E,...x})=>{const M=g.isInternal?void 0:E;return F.jsx(_0n,{...x,id:M,pathOptions:He.useMemo(()=>({borderRadius:0,offset:x.pathOptions?.offset}),[x.pathOptions?.offset])})})}const hUn=P0n({isInternal:!1}),$0n=P0n({isInternal:!0});hUn.displayName="StepEdge";$0n.displayName="StepEdgeInternal";function R0n(g){return He.memo(({id:E,sourceX:x,sourceY:M,targetX:N,targetY:P,label:k,labelStyle:H,labelShowBg:U,labelBgStyle:G,labelBgPadding:ie,labelBgBorderRadius:Z,style:W,markerEnd:se,markerStart:le,interactionWidth:ee})=>{const[Oe,ge,Pe]=n0n({sourceX:x,sourceY:M,targetX:N,targetY:P}),ae=g.isInternal?void 0:E;return F.jsx(cq,{id:ae,path:Oe,labelX:ge,labelY:Pe,label:k,labelStyle:H,labelShowBg:U,labelBgStyle:G,labelBgPadding:ie,labelBgBorderRadius:Z,style:W,markerEnd:se,markerStart:le,interactionWidth:ee})})}const dUn=R0n({isInternal:!1}),B0n=R0n({isInternal:!0});dUn.displayName="StraightEdge";B0n.displayName="StraightEdgeInternal";function z0n(g){return He.memo(({id:E,sourceX:x,sourceY:M,targetX:N,targetY:P,sourcePosition:k=cr.Bottom,targetPosition:H=cr.Top,label:U,labelStyle:G,labelShowBg:ie,labelBgStyle:Z,labelBgPadding:W,labelBgBorderRadius:se,style:le,markerEnd:ee,markerStart:Oe,pathOptions:ge,interactionWidth:Pe})=>{const[ae,Me,Be]=Zdn({sourceX:x,sourceY:M,sourcePosition:k,targetX:N,targetY:P,targetPosition:H,curvature:ge?.curvature}),rn=g.isInternal?void 0:E;return F.jsx(cq,{id:rn,path:ae,labelX:Me,labelY:Be,label:U,labelStyle:G,labelShowBg:ie,labelBgStyle:Z,labelBgPadding:W,labelBgBorderRadius:se,style:le,markerEnd:ee,markerStart:Oe,interactionWidth:Pe})})}const bUn=z0n({isInternal:!1}),F0n=z0n({isInternal:!0});bUn.displayName="BezierEdge";F0n.displayName="BezierEdgeInternal";const q1n={default:F0n,straight:B0n,step:$0n,smoothstep:L0n,simplebezier:D0n},U1n={sourceX:null,sourceY:null,targetX:null,targetY:null,sourcePosition:null,targetPosition:null},gUn=(g,E,x)=>x===cr.Left?g-E:x===cr.Right?g+E:g,wUn=(g,E,x)=>x===cr.Top?g-E:x===cr.Bottom?g+E:g,X1n="react-flow__edgeupdater";function K1n({position:g,centerX:E,centerY:x,radius:M=10,onMouseDown:N,onMouseEnter:P,onMouseOut:k,type:H}){return F.jsx("circle",{onMouseDown:N,onMouseEnter:P,onMouseOut:k,className:Ta([X1n,`${X1n}-${H}`]),cx:gUn(E,M,g),cy:wUn(x,M,g),r:M,stroke:"transparent",fill:"transparent"})}function pUn({isReconnectable:g,reconnectRadius:E,edge:x,sourceX:M,sourceY:N,targetX:P,targetY:k,sourcePosition:H,targetPosition:U,onReconnect:G,onReconnectStart:ie,onReconnectEnd:Z,setReconnecting:W,setUpdateHover:se}){const le=Sl(),ee=(Me,Be)=>{if(Me.button!==0)return;const{autoPanOnConnect:rn,domNode:ln,connectionMode:xn,connectionRadius:hn,lib:wt,onConnectStart:Cn,cancelConnection:Qn,nodeLookup:Y,rfId:Fe,panBy:mn,updateConnection:Ae}=le.getState(),Ze=Be.type==="target",sn=(xe,un)=>{W(!1),Z?.(xe,x,Be.type,un)},Sn=xe=>G?.(x,xe),kn=(xe,un)=>{W(!0),ie?.(Me,x,Be.type),Cn?.(xe,un)};fke.onPointerDown(Me.nativeEvent,{autoPanOnConnect:rn,connectionMode:xn,connectionRadius:hn,domNode:ln,handleId:Be.id,nodeId:Be.nodeId,nodeLookup:Y,isTarget:Ze,edgeUpdaterType:Be.type,lib:wt,flowId:Fe,cancelConnection:Qn,panBy:mn,isValidConnection:(...xe)=>le.getState().isValidConnection?.(...xe)??!0,onConnect:Sn,onConnectStart:kn,onConnectEnd:(...xe)=>le.getState().onConnectEnd?.(...xe),onReconnectEnd:sn,updateConnection:Ae,getTransform:()=>le.getState().transform,getFromHandle:()=>le.getState().connection.fromHandle,dragThreshold:le.getState().connectionDragThreshold,handleDomNode:Me.currentTarget})},Oe=Me=>ee(Me,{nodeId:x.target,id:x.targetHandle??null,type:"target"}),ge=Me=>ee(Me,{nodeId:x.source,id:x.sourceHandle??null,type:"source"}),Pe=()=>se(!0),ae=()=>se(!1);return F.jsxs(F.Fragment,{children:[(g===!0||g==="source")&&F.jsx(K1n,{position:H,centerX:M,centerY:N,radius:E,onMouseDown:Oe,onMouseEnter:Pe,onMouseOut:ae,type:"source"}),(g===!0||g==="target")&&F.jsx(K1n,{position:U,centerX:P,centerY:k,radius:E,onMouseDown:ge,onMouseEnter:Pe,onMouseOut:ae,type:"target"})]})}function mUn({id:g,edgesFocusable:E,edgesReconnectable:x,elementsSelectable:M,onClick:N,onDoubleClick:P,onContextMenu:k,onMouseEnter:H,onMouseMove:U,onMouseLeave:G,reconnectRadius:ie,onReconnect:Z,onReconnectStart:W,onReconnectEnd:se,rfId:le,edgeTypes:ee,noPanClassName:Oe,onError:ge,disableKeyboardA11y:Pe}){let ae=Fu(ur=>ur.edgeLookup.get(g));const Me=Fu(ur=>ur.defaultEdgeOptions);ae=Me?{...Me,...ae}:ae;let Be=ae.type||"default",rn=ee?.[Be]||q1n[Be];rn===void 0&&(ge?.("011",u5.error011(Be)),Be="default",rn=ee?.default||q1n.default);const ln=!!(ae.focusable||E&&typeof ae.focusable>"u"),xn=typeof Z<"u"&&(ae.reconnectable||x&&typeof ae.reconnectable>"u"),hn=!!(ae.selectable||M&&typeof ae.selectable>"u"),wt=He.useRef(null),[Cn,Qn]=He.useState(!1),[Y,Fe]=He.useState(!1),mn=Sl(),{zIndex:Ae,sourceX:Ze,sourceY:sn,targetX:Sn,targetY:kn,sourcePosition:xe,targetPosition:un}=Fu(He.useCallback(ur=>{const mi=ur.nodeLookup.get(ae.source),Fi=ur.nodeLookup.get(ae.target);if(!mi||!Fi)return{zIndex:ae.zIndex,...U1n};const fu=lGn({id:g,sourceNode:mi,targetNode:Fi,sourceHandle:ae.sourceHandle||null,targetHandle:ae.targetHandle||null,connectionMode:ur.connectionMode,onError:ge});return{zIndex:nGn({selected:ae.selected,zIndex:ae.zIndex,sourceNode:mi,targetNode:Fi,elevateOnSelect:ur.elevateEdgesOnSelect,zIndexMode:ur.zIndexMode}),...fu||U1n}},[ae.source,ae.target,ae.sourceHandle,ae.targetHandle,ae.selected,ae.zIndex]),El),rt=He.useMemo(()=>ae.markerStart?`url('#${ske(ae.markerStart,le)}')`:void 0,[ae.markerStart,le]),lt=He.useMemo(()=>ae.markerEnd?`url('#${ske(ae.markerEnd,le)}')`:void 0,[ae.markerEnd,le]);if(ae.hidden||Ze===null||sn===null||Sn===null||kn===null)return null;const Bt=ur=>{const{addSelectedEdges:mi,unselectNodesAndEdges:Fi,multiSelectionActive:fu}=mn.getState();hn&&(mn.setState({nodesSelectionActive:!1}),ae.selected&&fu?(Fi({nodes:[],edges:[ae]}),wt.current?.blur()):mi([g])),N&&N(ur,ae)},hi=P?ur=>{P(ur,{...ae})}:void 0,Gt=k?ur=>{k(ur,{...ae})}:void 0,At=H?ur=>{H(ur,{...ae})}:void 0,st=U?ur=>{U(ur,{...ae})}:void 0,Wr=G?ur=>{G(ur,{...ae})}:void 0,Mr=ur=>{if(!Pe&&Rdn.includes(ur.key)&&hn){const{unselectNodesAndEdges:mi,addSelectedEdges:Fi}=mn.getState();ur.key==="Escape"?(wt.current?.blur(),mi({edges:[ae]})):Fi([g])}};return F.jsx("svg",{style:{zIndex:Ae},children:F.jsxs("g",{className:Ta(["react-flow__edge",`react-flow__edge-${Be}`,ae.className,Oe,{selected:ae.selected,animated:ae.animated,inactive:!hn&&!N,updating:Cn,selectable:hn}]),onClick:Bt,onDoubleClick:hi,onContextMenu:Gt,onMouseEnter:At,onMouseMove:st,onMouseLeave:Wr,onKeyDown:ln?Mr:void 0,tabIndex:ln?0:void 0,role:ae.ariaRole??(ln?"group":"img"),"aria-roledescription":"edge","data-id":g,"data-testid":`rf__edge-${g}`,"aria-label":ae.ariaLabel===null?void 0:ae.ariaLabel||`Edge from ${ae.source} to ${ae.target}`,"aria-describedby":ln?`${w0n}-${le}`:void 0,ref:wt,...ae.domAttributes,children:[!Y&&F.jsx(rn,{id:g,source:ae.source,target:ae.target,type:ae.type,selected:ae.selected,animated:ae.animated,selectable:hn,deletable:ae.deletable??!0,label:ae.label,labelStyle:ae.labelStyle,labelShowBg:ae.labelShowBg,labelBgStyle:ae.labelBgStyle,labelBgPadding:ae.labelBgPadding,labelBgBorderRadius:ae.labelBgBorderRadius,sourceX:Ze,sourceY:sn,targetX:Sn,targetY:kn,sourcePosition:xe,targetPosition:un,data:ae.data,style:ae.style,sourceHandleId:ae.sourceHandle,targetHandleId:ae.targetHandle,markerStart:rt,markerEnd:lt,pathOptions:"pathOptions"in ae?ae.pathOptions:void 0,interactionWidth:ae.interactionWidth}),xn&&F.jsx(pUn,{edge:ae,isReconnectable:xn,reconnectRadius:ie,onReconnect:Z,onReconnectStart:W,onReconnectEnd:se,sourceX:Ze,sourceY:sn,targetX:Sn,targetY:kn,sourcePosition:xe,targetPosition:un,setUpdateHover:Qn,setReconnecting:Fe})]})})}var vUn=He.memo(mUn);const yUn=g=>({edgesFocusable:g.edgesFocusable,edgesReconnectable:g.edgesReconnectable,elementsSelectable:g.elementsSelectable,connectionMode:g.connectionMode,onError:g.onError});function J0n({defaultMarkerColor:g,onlyRenderVisibleElements:E,rfId:x,edgeTypes:M,noPanClassName:N,onReconnect:P,onEdgeContextMenu:k,onEdgeMouseEnter:H,onEdgeMouseMove:U,onEdgeMouseLeave:G,onEdgeClick:ie,reconnectRadius:Z,onEdgeDoubleClick:W,onReconnectStart:se,onReconnectEnd:le,disableKeyboardA11y:ee}){const{edgesFocusable:Oe,edgesReconnectable:ge,elementsSelectable:Pe,onError:ae}=Fu(yUn,El),Me=rUn(E);return F.jsxs("div",{className:"react-flow__edges",children:[F.jsx(lUn,{defaultColor:g,rfId:x}),Me.map(Be=>F.jsx(vUn,{id:Be,edgesFocusable:Oe,edgesReconnectable:ge,elementsSelectable:Pe,noPanClassName:N,onReconnect:P,onContextMenu:k,onMouseEnter:H,onMouseMove:U,onMouseLeave:G,onClick:ie,reconnectRadius:Z,onDoubleClick:W,onReconnectStart:se,onReconnectEnd:le,rfId:x,onError:ae,edgeTypes:M,disableKeyboardA11y:ee},Be))]})}J0n.displayName="EdgeRenderer";const kUn=He.memo(J0n),jUn=g=>`translate(${g.transform[0]}px,${g.transform[1]}px) scale(${g.transform[2]})`;function EUn({children:g}){const E=Fu(jUn);return F.jsx("div",{className:"react-flow__viewport xyflow__viewport react-flow__container",style:{transform:E},children:g})}function SUn(g){const E=Nke(),x=He.useRef(!1);He.useEffect(()=>{!x.current&&E.viewportInitialized&&g&&(setTimeout(()=>g(E),1),x.current=!0)},[g,E.viewportInitialized])}const xUn=g=>g.panZoom?.syncViewport;function AUn(g){const E=Fu(xUn),x=Sl();return He.useEffect(()=>{g&&(E?.(g),x.setState({transform:[g.x,g.y,g.zoom]}))},[g,E]),null}function MUn(g){return g.connection.inProgress?{...g.connection,to:rq(g.connection.to,g.transform)}:{...g.connection}}function TUn(g){return MUn}function CUn(g){const E=TUn();return Fu(E,El)}const OUn=g=>({nodesConnectable:g.nodesConnectable,isValid:g.connection.isValid,inProgress:g.connection.inProgress,width:g.width,height:g.height});function NUn({containerStyle:g,style:E,type:x,component:M}){const{nodesConnectable:N,width:P,height:k,isValid:H,inProgress:U}=Fu(OUn,El);return!(P&&N&&U)?null:F.jsx("svg",{style:g,width:P,height:k,className:"react-flow__connectionline react-flow__container",children:F.jsx("g",{className:Ta(["react-flow__connection",Fdn(H)]),children:F.jsx(H0n,{style:E,type:x,CustomComponent:M,isValid:H})})})}const H0n=({style:g,type:E=W7.Bezier,CustomComponent:x,isValid:M})=>{const{inProgress:N,from:P,fromNode:k,fromHandle:H,fromPosition:U,to:G,toNode:ie,toHandle:Z,toPosition:W,pointer:se}=CUn();if(!N)return;if(x)return F.jsx(x,{connectionLineType:E,connectionLineStyle:g,fromNode:k,fromHandle:H,fromX:P.x,fromY:P.y,toX:G.x,toY:G.y,fromPosition:U,toPosition:W,connectionStatus:Fdn(M),toNode:ie,toHandle:Z,pointer:se});let le="";const ee={sourceX:P.x,sourceY:P.y,sourcePosition:U,targetX:G.x,targetY:G.y,targetPosition:W};switch(E){case W7.Bezier:[le]=Zdn(ee);break;case W7.SimpleBezier:[le]=O0n(ee);break;case W7.Step:[le]=Aue({...ee,borderRadius:0});break;case W7.SmoothStep:[le]=Aue(ee);break;default:[le]=n0n(ee)}return F.jsx("path",{d:le,fill:"none",className:"react-flow__connection-path",style:g})};H0n.displayName="ConnectionLine";const DUn={};function V1n(g=DUn){He.useRef(g),Sl(),He.useEffect(()=>{},[g])}function IUn(){Sl(),He.useRef(!1),He.useEffect(()=>{},[])}function G0n({nodeTypes:g,edgeTypes:E,onInit:x,onNodeClick:M,onEdgeClick:N,onNodeDoubleClick:P,onEdgeDoubleClick:k,onNodeMouseEnter:H,onNodeMouseMove:U,onNodeMouseLeave:G,onNodeContextMenu:ie,onSelectionContextMenu:Z,onSelectionStart:W,onSelectionEnd:se,connectionLineType:le,connectionLineStyle:ee,connectionLineComponent:Oe,connectionLineContainerStyle:ge,selectionKeyCode:Pe,selectionOnDrag:ae,selectionMode:Me,multiSelectionKeyCode:Be,panActivationKeyCode:rn,zoomActivationKeyCode:ln,deleteKeyCode:xn,onlyRenderVisibleElements:hn,elementsSelectable:wt,defaultViewport:Cn,translateExtent:Qn,minZoom:Y,maxZoom:Fe,preventScrolling:mn,defaultMarkerColor:Ae,zoomOnScroll:Ze,zoomOnPinch:sn,panOnScroll:Sn,panOnScrollSpeed:kn,panOnScrollMode:xe,zoomOnDoubleClick:un,panOnDrag:rt,onPaneClick:lt,onPaneMouseEnter:Bt,onPaneMouseMove:hi,onPaneMouseLeave:Gt,onPaneScroll:At,onPaneContextMenu:st,paneClickDistance:Wr,nodeClickDistance:Mr,onEdgeContextMenu:ur,onEdgeMouseEnter:mi,onEdgeMouseMove:Fi,onEdgeMouseLeave:fu,reconnectRadius:Eu,onReconnect:Ws,onReconnectStart:Dh,onReconnectEnd:ef,noDragClassName:ch,noWheelClassName:kc,noPanClassName:cd,disableKeyboardA11y:jb,nodeExtent:Rs,rfId:c0,viewport:u0,onViewportChange:o0}){return V1n(g),V1n(E),IUn(),SUn(x),AUn(u0),F.jsx(Kqn,{onPaneClick:lt,onPaneMouseEnter:Bt,onPaneMouseMove:hi,onPaneMouseLeave:Gt,onPaneContextMenu:st,onPaneScroll:At,paneClickDistance:Wr,deleteKeyCode:xn,selectionKeyCode:Pe,selectionOnDrag:ae,selectionMode:Me,onSelectionStart:W,onSelectionEnd:se,multiSelectionKeyCode:Be,panActivationKeyCode:rn,zoomActivationKeyCode:ln,elementsSelectable:wt,zoomOnScroll:Ze,zoomOnPinch:sn,zoomOnDoubleClick:un,panOnScroll:Sn,panOnScrollSpeed:kn,panOnScrollMode:xe,panOnDrag:rt,defaultViewport:Cn,translateExtent:Qn,minZoom:Y,maxZoom:Fe,onSelectionContextMenu:Z,preventScrolling:mn,noDragClassName:ch,noWheelClassName:kc,noPanClassName:cd,disableKeyboardA11y:jb,onViewportChange:o0,isControlledViewport:!!u0,children:F.jsxs(EUn,{children:[F.jsx(kUn,{edgeTypes:E,onEdgeClick:N,onEdgeDoubleClick:k,onReconnect:Ws,onReconnectStart:Dh,onReconnectEnd:ef,onlyRenderVisibleElements:hn,onEdgeContextMenu:ur,onEdgeMouseEnter:mi,onEdgeMouseMove:Fi,onEdgeMouseLeave:fu,reconnectRadius:Eu,defaultMarkerColor:Ae,noPanClassName:cd,disableKeyboardA11y:jb,rfId:c0}),F.jsx(NUn,{style:ee,type:le,component:Oe,containerStyle:ge}),F.jsx("div",{className:"react-flow__edgelabel-renderer"}),F.jsx(iUn,{nodeTypes:g,onNodeClick:M,onNodeDoubleClick:P,onNodeMouseEnter:H,onNodeMouseMove:U,onNodeMouseLeave:G,onNodeContextMenu:ie,nodeClickDistance:Mr,onlyRenderVisibleElements:hn,noPanClassName:cd,noDragClassName:ch,disableKeyboardA11y:jb,nodeExtent:Rs,rfId:c0}),F.jsx("div",{className:"react-flow__viewport-portal"})]})})}G0n.displayName="GraphView";const _Un=He.memo(G0n),Y1n=({nodes:g,edges:E,defaultNodes:x,defaultEdges:M,width:N,height:P,fitView:k,fitViewOptions:H,minZoom:U=.5,maxZoom:G=2,nodeOrigin:ie,nodeExtent:Z,zIndexMode:W="basic"}={})=>{const se=new Map,le=new Map,ee=new Map,Oe=new Map,ge=M??E??[],Pe=x??g??[],ae=ie??[0,0],Me=Z??GG;r0n(ee,Oe,ge);const{nodesInitialized:Be}=lke(Pe,se,le,{nodeOrigin:ae,nodeExtent:Me,zIndexMode:W});let rn=[0,0,1];if(k&&N&&P){const ln=tq(se,{filter:Cn=>!!((Cn.width||Cn.initialWidth)&&(Cn.height||Cn.initialHeight))}),{x:xn,y:hn,zoom:wt}=Ske(ln,N,P,U,G,H?.padding??.1);rn=[xn,hn,wt]}return{rfId:"1",width:N??0,height:P??0,transform:rn,nodes:Pe,nodesInitialized:Be,nodeLookup:se,parentLookup:le,edges:ge,edgeLookup:Oe,connectionLookup:ee,onNodesChange:null,onEdgesChange:null,hasDefaultNodes:x!==void 0,hasDefaultEdges:M!==void 0,panZoom:null,minZoom:U,maxZoom:G,translateExtent:GG,nodeExtent:Me,nodesSelectionActive:!1,userSelectionActive:!1,userSelectionRect:null,connectionMode:bI.Strict,domNode:null,paneDragging:!1,noPanClassName:"nopan",nodeOrigin:ae,nodeDragThreshold:1,connectionDragThreshold:1,snapGrid:[15,15],snapToGrid:!1,nodesDraggable:!0,nodesConnectable:!0,nodesFocusable:!0,edgesFocusable:!0,edgesReconnectable:!0,elementsSelectable:!0,elevateNodesOnSelect:!0,elevateEdgesOnSelect:!0,selectNodesOnDrag:!0,multiSelectionActive:!1,fitViewQueued:k??!1,fitViewOptions:H,fitViewResolver:null,connection:{...zdn},connectionClickStartHandle:null,connectOnClick:!0,ariaLiveMessage:"",autoPanOnConnect:!0,autoPanOnNodeDrag:!0,autoPanOnNodeFocus:!0,autoPanSpeed:15,connectionRadius:20,onError:VHn,isValidConnection:void 0,onSelectionChangeHandlers:[],lib:"react",debug:!1,ariaLabelConfig:Bdn,zIndexMode:W,onNodesChangeMiddlewareMap:new Map,onEdgesChangeMiddlewareMap:new Map}},LUn=({nodes:g,edges:E,defaultNodes:x,defaultEdges:M,width:N,height:P,fitView:k,fitViewOptions:H,minZoom:U,maxZoom:G,nodeOrigin:ie,nodeExtent:Z,zIndexMode:W})=>ZGn((se,le)=>{async function ee(){const{nodeLookup:Oe,panZoom:ge,fitViewOptions:Pe,fitViewResolver:ae,width:Me,height:Be,minZoom:rn,maxZoom:ln}=le();ge&&(await XHn({nodes:Oe,width:Me,height:Be,panZoom:ge,minZoom:rn,maxZoom:ln},Pe),ae?.resolve(!0),se({fitViewResolver:null}))}return{...Y1n({nodes:g,edges:E,width:N,height:P,fitView:k,fitViewOptions:H,minZoom:U,maxZoom:G,nodeOrigin:ie,nodeExtent:Z,defaultNodes:x,defaultEdges:M,zIndexMode:W}),setNodes:Oe=>{const{nodeLookup:ge,parentLookup:Pe,nodeOrigin:ae,elevateNodesOnSelect:Me,fitViewQueued:Be,zIndexMode:rn,nodesSelectionActive:ln}=le(),{nodesInitialized:xn,hasSelectedNodes:hn}=lke(Oe,ge,Pe,{nodeOrigin:ae,nodeExtent:Z,elevateNodesOnSelect:Me,checkEquality:!0,zIndexMode:rn}),wt=ln&&hn;Be&&xn?(ee(),se({nodes:Oe,nodesInitialized:xn,fitViewQueued:!1,fitViewOptions:void 0,nodesSelectionActive:wt})):se({nodes:Oe,nodesInitialized:xn,nodesSelectionActive:wt})},setEdges:Oe=>{const{connectionLookup:ge,edgeLookup:Pe}=le();r0n(ge,Pe,Oe),se({edges:Oe})},setDefaultNodesAndEdges:(Oe,ge)=>{if(Oe){const{setNodes:Pe}=le();Pe(Oe),se({hasDefaultNodes:!0})}if(ge){const{setEdges:Pe}=le();Pe(ge),se({hasDefaultEdges:!0})}},updateNodeInternals:Oe=>{const{triggerNodeChanges:ge,nodeLookup:Pe,parentLookup:ae,domNode:Me,nodeOrigin:Be,nodeExtent:rn,debug:ln,fitViewQueued:xn,zIndexMode:hn}=le(),{changes:wt,updatedInternals:Cn}=pGn(Oe,Pe,ae,Me,Be,rn,hn);Cn&&(dGn(Pe,ae,{nodeOrigin:Be,nodeExtent:rn,zIndexMode:hn}),xn?(ee(),se({fitViewQueued:!1,fitViewOptions:void 0})):se({}),wt?.length>0&&(ln&&console.log("React Flow: trigger node changes",wt),ge?.(wt)))},updateNodePositions:(Oe,ge=!1)=>{const Pe=[];let ae=[];const{nodeLookup:Me,triggerNodeChanges:Be,connection:rn,updateConnection:ln,onNodesChangeMiddlewareMap:xn}=le();for(const[hn,wt]of Oe){const Cn=Me.get(hn),Qn=!!(Cn?.expandParent&&Cn?.parentId&&wt?.position),Y={id:hn,type:"position",position:Qn?{x:Math.max(0,wt.position.x),y:Math.max(0,wt.position.y)}:wt.position,dragging:ge};if(Cn&&rn.inProgress&&rn.fromNode.id===Cn.id){const Fe=AA(Cn,rn.fromHandle,cr.Left,!0);ln({...rn,from:Fe})}Qn&&Cn.parentId&&Pe.push({id:hn,parentId:Cn.parentId,rect:{...wt.internals.positionAbsolute,width:wt.measured.width??0,height:wt.measured.height??0}}),ae.push(Y)}if(Pe.length>0){const{parentLookup:hn,nodeOrigin:wt}=le(),Cn=Oke(Pe,Me,hn,wt);ae.push(...Cn)}for(const hn of xn.values())ae=hn(ae);Be(ae)},triggerNodeChanges:Oe=>{const{onNodesChange:ge,setNodes:Pe,nodes:ae,hasDefaultNodes:Me,debug:Be}=le();if(Oe?.length){if(Me){const rn=v0n(Oe,ae);Pe(rn)}Be&&console.log("React Flow: trigger node changes",Oe),ge?.(Oe)}},triggerEdgeChanges:Oe=>{const{onEdgesChange:ge,setEdges:Pe,edges:ae,hasDefaultEdges:Me,debug:Be}=le();if(Oe?.length){if(Me){const rn=y0n(Oe,ae);Pe(rn)}Be&&console.log("React Flow: trigger edge changes",Oe),ge?.(Oe)}},addSelectedNodes:Oe=>{const{multiSelectionActive:ge,edgeLookup:Pe,nodeLookup:ae,triggerNodeChanges:Me,triggerEdgeChanges:Be}=le();if(ge){const rn=Oe.map(ln=>vA(ln,!0));Me(rn);return}Me(lI(ae,new Set([...Oe]),!0)),Be(lI(Pe))},addSelectedEdges:Oe=>{const{multiSelectionActive:ge,edgeLookup:Pe,nodeLookup:ae,triggerNodeChanges:Me,triggerEdgeChanges:Be}=le();if(ge){const rn=Oe.map(ln=>vA(ln,!0));Be(rn);return}Be(lI(Pe,new Set([...Oe]))),Me(lI(ae,new Set,!0))},unselectNodesAndEdges:({nodes:Oe,edges:ge}={})=>{const{edges:Pe,nodes:ae,nodeLookup:Me,triggerNodeChanges:Be,triggerEdgeChanges:rn}=le(),ln=Oe||ae,xn=ge||Pe,hn=[];for(const Cn of ln){if(!Cn.selected)continue;const Qn=Me.get(Cn.id);Qn&&(Qn.selected=!1),hn.push(vA(Cn.id,!1))}const wt=[];for(const Cn of xn)Cn.selected&&wt.push(vA(Cn.id,!1));Be(hn),rn(wt)},setMinZoom:Oe=>{const{panZoom:ge,maxZoom:Pe}=le();ge?.setScaleExtent([Oe,Pe]),se({minZoom:Oe})},setMaxZoom:Oe=>{const{panZoom:ge,minZoom:Pe}=le();ge?.setScaleExtent([Pe,Oe]),se({maxZoom:Oe})},setTranslateExtent:Oe=>{le().panZoom?.setTranslateExtent(Oe),se({translateExtent:Oe})},resetSelectedElements:()=>{const{edges:Oe,nodes:ge,triggerNodeChanges:Pe,triggerEdgeChanges:ae,elementsSelectable:Me}=le();if(!Me)return;const Be=ge.reduce((ln,xn)=>xn.selected?[...ln,vA(xn.id,!1)]:ln,[]),rn=Oe.reduce((ln,xn)=>xn.selected?[...ln,vA(xn.id,!1)]:ln,[]);Pe(Be),ae(rn)},setNodeExtent:Oe=>{const{nodes:ge,nodeLookup:Pe,parentLookup:ae,nodeOrigin:Me,elevateNodesOnSelect:Be,nodeExtent:rn,zIndexMode:ln}=le();Oe[0][0]===rn[0][0]&&Oe[0][1]===rn[0][1]&&Oe[1][0]===rn[1][0]&&Oe[1][1]===rn[1][1]||(lke(ge,Pe,ae,{nodeOrigin:Me,nodeExtent:Oe,elevateNodesOnSelect:Be,checkEquality:!1,zIndexMode:ln}),se({nodeExtent:Oe}))},panBy:Oe=>{const{transform:ge,width:Pe,height:ae,panZoom:Me,translateExtent:Be}=le();return mGn({delta:Oe,panZoom:Me,transform:ge,translateExtent:Be,width:Pe,height:ae})},setCenter:async(Oe,ge,Pe)=>{const{width:ae,height:Me,maxZoom:Be,panZoom:rn}=le();if(!rn)return Promise.resolve(!1);const ln=typeof Pe?.zoom<"u"?Pe.zoom:Be;return await rn.setViewport({x:ae/2-Oe*ln,y:Me/2-ge*ln,zoom:ln},{duration:Pe?.duration,ease:Pe?.ease,interpolate:Pe?.interpolate}),Promise.resolve(!0)},cancelConnection:()=>{se({connection:{...zdn}})},updateConnection:Oe=>{se({connection:Oe})},reset:()=>se({...Y1n()})}},Object.is);function PUn({initialNodes:g,initialEdges:E,defaultNodes:x,defaultEdges:M,initialWidth:N,initialHeight:P,initialMinZoom:k,initialMaxZoom:H,initialFitViewOptions:U,fitView:G,nodeOrigin:ie,nodeExtent:Z,zIndexMode:W,children:se}){const[le]=He.useState(()=>LUn({nodes:g,edges:E,defaultNodes:x,defaultEdges:M,width:N,height:P,fitView:G,minZoom:k,maxZoom:H,fitViewOptions:U,nodeOrigin:ie,nodeExtent:Z,zIndexMode:W}));return F.jsx(nqn,{value:le,children:F.jsx(Eqn,{children:se})})}function $Un({children:g,nodes:E,edges:x,defaultNodes:M,defaultEdges:N,width:P,height:k,fitView:H,fitViewOptions:U,minZoom:G,maxZoom:ie,nodeOrigin:Z,nodeExtent:W,zIndexMode:se}){return He.useContext(Rue)?F.jsx(F.Fragment,{children:g}):F.jsx(PUn,{initialNodes:E,initialEdges:x,defaultNodes:M,defaultEdges:N,initialWidth:P,initialHeight:k,fitView:H,initialFitViewOptions:U,initialMinZoom:G,initialMaxZoom:ie,nodeOrigin:Z,nodeExtent:W,zIndexMode:se,children:g})}const RUn={width:"100%",height:"100%",overflow:"hidden",position:"relative",zIndex:0};function BUn({nodes:g,edges:E,defaultNodes:x,defaultEdges:M,className:N,nodeTypes:P,edgeTypes:k,onNodeClick:H,onEdgeClick:U,onInit:G,onMove:ie,onMoveStart:Z,onMoveEnd:W,onConnect:se,onConnectStart:le,onConnectEnd:ee,onClickConnectStart:Oe,onClickConnectEnd:ge,onNodeMouseEnter:Pe,onNodeMouseMove:ae,onNodeMouseLeave:Me,onNodeContextMenu:Be,onNodeDoubleClick:rn,onNodeDragStart:ln,onNodeDrag:xn,onNodeDragStop:hn,onNodesDelete:wt,onEdgesDelete:Cn,onDelete:Qn,onSelectionChange:Y,onSelectionDragStart:Fe,onSelectionDrag:mn,onSelectionDragStop:Ae,onSelectionContextMenu:Ze,onSelectionStart:sn,onSelectionEnd:Sn,onBeforeDelete:kn,connectionMode:xe,connectionLineType:un=W7.Bezier,connectionLineStyle:rt,connectionLineComponent:lt,connectionLineContainerStyle:Bt,deleteKeyCode:hi="Backspace",selectionKeyCode:Gt="Shift",selectionOnDrag:At=!1,selectionMode:st=qG.Full,panActivationKeyCode:Wr="Space",multiSelectionKeyCode:Mr=KG()?"Meta":"Control",zoomActivationKeyCode:ur=KG()?"Meta":"Control",snapToGrid:mi,snapGrid:Fi,onlyRenderVisibleElements:fu=!1,selectNodesOnDrag:Eu,nodesDraggable:Ws,autoPanOnNodeFocus:Dh,nodesConnectable:ef,nodesFocusable:ch,nodeOrigin:kc=p0n,edgesFocusable:cd,edgesReconnectable:jb,elementsSelectable:Rs=!0,defaultViewport:c0=bqn,minZoom:u0=.5,maxZoom:o0=2,translateExtent:Bg=GG,preventScrolling:r6=!0,nodeExtent:zg,defaultMarkerColor:c6="#b1b1b7",zoomOnScroll:d1=!0,zoomOnPinch:ud=!0,panOnScroll:Sf=!1,panOnScrollSpeed:b1=.5,panOnScrollMode:xf=jA.Free,zoomOnDoubleClick:l5=!0,panOnDrag:f5=!0,onPaneClick:a5,onPaneMouseEnter:Fg,onPaneMouseMove:Eb,onPaneMouseLeave:Jg,onPaneScroll:_u,onPaneContextMenu:Hg,paneClickDistance:Gg=1,nodeClickDistance:u6=0,children:h5,onReconnect:Wm,onReconnectStart:qg,onReconnectEnd:o6,onEdgeContextMenu:Ug,onEdgeDoubleClick:Zm,onEdgeMouseEnter:d5,onEdgeMouseMove:Mt,onEdgeMouseLeave:Tc,reconnectRadius:dc=10,onNodesChange:s6,onEdgesChange:Af,noDragClassName:Zs="nodrag",noWheelClassName:Ca="nowheel",noPanClassName:Xg="nopan",fitView:b5,fitViewOptions:ek,connectOnClick:MA,attributionPosition:nk,proOptions:e3,defaultEdgeOptions:l6,elevateNodesOnSelect:xp=!0,elevateEdgesOnSelect:Ap=!1,disableKeyboardA11y:Mp=!1,autoPanOnConnect:Tp,autoPanOnNodeDrag:xl,autoPanSpeed:g5,connectionRadius:tk,isValidConnection:Kg,onError:Cp,style:TA,id:f6,nodeDragThreshold:ik,connectionDragThreshold:rk,viewport:n3,onViewportChange:w5,width:s0,height:Ih,colorMode:ck="light",debug:CA,onScroll:p5,ariaLabelConfig:uk,zIndexMode:t3="basic",...OA},_h){const i3=f6||"1",ok=mqn(ck),a6=He.useCallback(Vg=>{Vg.currentTarget.scrollTo({top:0,left:0,behavior:"instant"}),p5?.(Vg)},[p5]);return F.jsx("div",{"data-testid":"rf__wrapper",...OA,onScroll:a6,style:{...TA,...RUn},ref:_h,className:Ta(["react-flow",N,ok]),id:f6,role:"application",children:F.jsxs($Un,{nodes:g,edges:E,width:s0,height:Ih,fitView:b5,fitViewOptions:ek,minZoom:u0,maxZoom:o0,nodeOrigin:kc,nodeExtent:zg,zIndexMode:t3,children:[F.jsx(pqn,{nodes:g,edges:E,defaultNodes:x,defaultEdges:M,onConnect:se,onConnectStart:le,onConnectEnd:ee,onClickConnectStart:Oe,onClickConnectEnd:ge,nodesDraggable:Ws,autoPanOnNodeFocus:Dh,nodesConnectable:ef,nodesFocusable:ch,edgesFocusable:cd,edgesReconnectable:jb,elementsSelectable:Rs,elevateNodesOnSelect:xp,elevateEdgesOnSelect:Ap,minZoom:u0,maxZoom:o0,nodeExtent:zg,onNodesChange:s6,onEdgesChange:Af,snapToGrid:mi,snapGrid:Fi,connectionMode:xe,translateExtent:Bg,connectOnClick:MA,defaultEdgeOptions:l6,fitView:b5,fitViewOptions:ek,onNodesDelete:wt,onEdgesDelete:Cn,onDelete:Qn,onNodeDragStart:ln,onNodeDrag:xn,onNodeDragStop:hn,onSelectionDrag:mn,onSelectionDragStart:Fe,onSelectionDragStop:Ae,onMove:ie,onMoveStart:Z,onMoveEnd:W,noPanClassName:Xg,nodeOrigin:kc,rfId:i3,autoPanOnConnect:Tp,autoPanOnNodeDrag:xl,autoPanSpeed:g5,onError:Cp,connectionRadius:tk,isValidConnection:Kg,selectNodesOnDrag:Eu,nodeDragThreshold:ik,connectionDragThreshold:rk,onBeforeDelete:kn,debug:CA,ariaLabelConfig:uk,zIndexMode:t3}),F.jsx(_Un,{onInit:G,onNodeClick:H,onEdgeClick:U,onNodeMouseEnter:Pe,onNodeMouseMove:ae,onNodeMouseLeave:Me,onNodeContextMenu:Be,onNodeDoubleClick:rn,nodeTypes:P,edgeTypes:k,connectionLineType:un,connectionLineStyle:rt,connectionLineComponent:lt,connectionLineContainerStyle:Bt,selectionKeyCode:Gt,selectionOnDrag:At,selectionMode:st,deleteKeyCode:hi,multiSelectionKeyCode:Mr,panActivationKeyCode:Wr,zoomActivationKeyCode:ur,onlyRenderVisibleElements:fu,defaultViewport:c0,translateExtent:Bg,minZoom:u0,maxZoom:o0,preventScrolling:r6,zoomOnScroll:d1,zoomOnPinch:ud,zoomOnDoubleClick:l5,panOnScroll:Sf,panOnScrollSpeed:b1,panOnScrollMode:xf,panOnDrag:f5,onPaneClick:a5,onPaneMouseEnter:Fg,onPaneMouseMove:Eb,onPaneMouseLeave:Jg,onPaneScroll:_u,onPaneContextMenu:Hg,paneClickDistance:Gg,nodeClickDistance:u6,onSelectionContextMenu:Ze,onSelectionStart:sn,onSelectionEnd:Sn,onReconnect:Wm,onReconnectStart:qg,onReconnectEnd:o6,onEdgeContextMenu:Ug,onEdgeDoubleClick:Zm,onEdgeMouseEnter:d5,onEdgeMouseMove:Mt,onEdgeMouseLeave:Tc,reconnectRadius:dc,defaultMarkerColor:c6,noDragClassName:Zs,noWheelClassName:Ca,noPanClassName:Xg,rfId:i3,disableKeyboardA11y:Mp,nodeExtent:zg,viewport:n3,onViewportChange:w5}),F.jsx(dqn,{onSelectionChange:Y}),h5,F.jsx(sqn,{proOptions:e3,position:nk}),F.jsx(oqn,{rfId:i3,disableKeyboardA11y:Mp})]})})}var zUn=k0n(BUn);const FUn=g=>g.domNode?.querySelector(".react-flow__edgelabel-renderer");function JUn({children:g}){const E=Fu(FUn);return E?eqn.createPortal(g,E):null}function HUn(g){const[E,x]=He.useState(g),M=He.useCallback(N=>x(P=>v0n(N,P)),[]);return[E,x,M]}function GUn(g){const[E,x]=He.useState(g),M=He.useCallback(N=>x(P=>y0n(N,P)),[]);return[E,x,M]}function qUn({dimensions:g,lineWidth:E,variant:x,className:M}){return F.jsx("path",{strokeWidth:E,d:`M${g[0]/2} 0 V${g[1]} M0 ${g[1]/2} H${g[0]}`,className:Ta(["react-flow__background-pattern",x,M])})}function UUn({radius:g,className:E}){return F.jsx("circle",{cx:g,cy:g,r:g,className:Ta(["react-flow__background-pattern","dots",E])})}var Z7;(function(g){g.Lines="lines",g.Dots="dots",g.Cross="cross"})(Z7||(Z7={}));const XUn={[Z7.Dots]:1,[Z7.Lines]:1,[Z7.Cross]:6},KUn=g=>({transform:g.transform,patternId:`pattern-${g.rfId}`});function q0n({id:g,variant:E=Z7.Dots,gap:x=20,size:M,lineWidth:N=1,offset:P=0,color:k,bgColor:H,style:U,className:G,patternClassName:ie}){const Z=He.useRef(null),{transform:W,patternId:se}=Fu(KUn,El),le=M||XUn[E],ee=E===Z7.Dots,Oe=E===Z7.Cross,ge=Array.isArray(x)?x:[x,x],Pe=[ge[0]*W[2]||1,ge[1]*W[2]||1],ae=le*W[2],Me=Array.isArray(P)?P:[P,P],Be=Oe?[ae,ae]:Pe,rn=[Me[0]*W[2]||1+Be[0]/2,Me[1]*W[2]||1+Be[1]/2],ln=`${se}${g||""}`;return F.jsxs("svg",{className:Ta(["react-flow__background",G]),style:{...U,...zue,"--xy-background-color-props":H,"--xy-background-pattern-color-props":k},ref:Z,"data-testid":"rf__background",children:[F.jsx("pattern",{id:ln,x:W[0]%Pe[0],y:W[1]%Pe[1],width:Pe[0],height:Pe[1],patternUnits:"userSpaceOnUse",patternTransform:`translate(-${rn[0]},-${rn[1]})`,children:ee?F.jsx(UUn,{radius:ae/2,className:ie}):F.jsx(qUn,{dimensions:Be,lineWidth:N,variant:E,className:ie})}),F.jsx("rect",{x:"0",y:"0",width:"100%",height:"100%",fill:`url(#${ln})`})]})}q0n.displayName="Background";const VUn=He.memo(q0n);function YUn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",children:F.jsx("path",{d:"M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z"})})}function QUn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 5",children:F.jsx("path",{d:"M0 0h32v4.2H0z"})})}function WUn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 30",children:F.jsx("path",{d:"M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z"})})}function ZUn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32",children:F.jsx("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z"})})}function eXn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32",children:F.jsx("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z"})})}function rue({children:g,className:E,...x}){return F.jsx("button",{type:"button",className:Ta(["react-flow__controls-button",E]),...x,children:g})}const nXn=g=>({isInteractive:g.nodesDraggable||g.nodesConnectable||g.elementsSelectable,minZoomReached:g.transform[2]<=g.minZoom,maxZoomReached:g.transform[2]>=g.maxZoom,ariaLabelConfig:g.ariaLabelConfig});function U0n({style:g,showZoom:E=!0,showFitView:x=!0,showInteractive:M=!0,fitViewOptions:N,onZoomIn:P,onZoomOut:k,onFitView:H,onInteractiveChange:U,className:G,children:ie,position:Z="bottom-left",orientation:W="vertical","aria-label":se}){const le=Sl(),{isInteractive:ee,minZoomReached:Oe,maxZoomReached:ge,ariaLabelConfig:Pe}=Fu(nXn,El),{zoomIn:ae,zoomOut:Me,fitView:Be}=Nke(),rn=()=>{ae(),P?.()},ln=()=>{Me(),k?.()},xn=()=>{Be(N),H?.()},hn=()=>{le.setState({nodesDraggable:!ee,nodesConnectable:!ee,elementsSelectable:!ee}),U?.(!ee)},wt=W==="horizontal"?"horizontal":"vertical";return F.jsxs(Bue,{className:Ta(["react-flow__controls",wt,G]),position:Z,style:g,"data-testid":"rf__controls","aria-label":se??Pe["controls.ariaLabel"],children:[E&&F.jsxs(F.Fragment,{children:[F.jsx(rue,{onClick:rn,className:"react-flow__controls-zoomin",title:Pe["controls.zoomIn.ariaLabel"],"aria-label":Pe["controls.zoomIn.ariaLabel"],disabled:ge,children:F.jsx(YUn,{})}),F.jsx(rue,{onClick:ln,className:"react-flow__controls-zoomout",title:Pe["controls.zoomOut.ariaLabel"],"aria-label":Pe["controls.zoomOut.ariaLabel"],disabled:Oe,children:F.jsx(QUn,{})})]}),x&&F.jsx(rue,{className:"react-flow__controls-fitview",onClick:xn,title:Pe["controls.fitView.ariaLabel"],"aria-label":Pe["controls.fitView.ariaLabel"],children:F.jsx(WUn,{})}),M&&F.jsx(rue,{className:"react-flow__controls-interactive",onClick:hn,title:Pe["controls.interactive.ariaLabel"],"aria-label":Pe["controls.interactive.ariaLabel"],children:ee?F.jsx(eXn,{}):F.jsx(ZUn,{})}),ie]})}U0n.displayName="Controls";const tXn=He.memo(U0n);function iXn({id:g,x:E,y:x,width:M,height:N,style:P,color:k,strokeColor:H,strokeWidth:U,className:G,borderRadius:ie,shapeRendering:Z,selected:W,onClick:se}){const{background:le,backgroundColor:ee}=P||{},Oe=k||le||ee;return F.jsx("rect",{className:Ta(["react-flow__minimap-node",{selected:W},G]),x:E,y:x,rx:ie,ry:ie,width:M,height:N,style:{fill:Oe,stroke:H,strokeWidth:U},shapeRendering:Z,onClick:se?ge=>se(ge,g):void 0})}const rXn=He.memo(iXn),cXn=g=>g.nodes.map(E=>E.id),U7e=g=>g instanceof Function?g:()=>g;function uXn({nodeStrokeColor:g,nodeColor:E,nodeClassName:x="",nodeBorderRadius:M=5,nodeStrokeWidth:N,nodeComponent:P=rXn,onClick:k}){const H=Fu(cXn,El),U=U7e(E),G=U7e(g),ie=U7e(x),Z=typeof window>"u"||window.chrome?"crispEdges":"geometricPrecision";return F.jsx(F.Fragment,{children:H.map(W=>F.jsx(sXn,{id:W,nodeColorFunc:U,nodeStrokeColorFunc:G,nodeClassNameFunc:ie,nodeBorderRadius:M,nodeStrokeWidth:N,NodeComponent:P,onClick:k,shapeRendering:Z},W))})}function oXn({id:g,nodeColorFunc:E,nodeStrokeColorFunc:x,nodeClassNameFunc:M,nodeBorderRadius:N,nodeStrokeWidth:P,shapeRendering:k,NodeComponent:H,onClick:U}){const{node:G,x:ie,y:Z,width:W,height:se}=Fu(le=>{const ee=le.nodeLookup.get(g);if(!ee)return{node:void 0,x:0,y:0,width:0,height:0};const Oe=ee.internals.userNode,{x:ge,y:Pe}=ee.internals.positionAbsolute,{width:ae,height:Me}=i6(Oe);return{node:Oe,x:ge,y:Pe,width:ae,height:Me}},El);return!G||G.hidden||!Xdn(G)?null:F.jsx(H,{x:ie,y:Z,width:W,height:se,style:G.style,selected:!!G.selected,className:M(G),color:E(G),borderRadius:N,strokeColor:x(G),strokeWidth:P,shapeRendering:k,onClick:U,id:G.id})}const sXn=He.memo(oXn);var lXn=He.memo(uXn);const fXn=200,aXn=150,hXn=g=>!g.hidden,dXn=g=>{const E={x:-g.transform[0]/g.transform[2],y:-g.transform[1]/g.transform[2],width:g.width/g.transform[2],height:g.height/g.transform[2]};return{viewBB:E,boundingRect:g.nodeLookup.size>0?Udn(tq(g.nodeLookup,{filter:hXn}),E):E,rfId:g.rfId,panZoom:g.panZoom,translateExtent:g.translateExtent,flowWidth:g.width,flowHeight:g.height,ariaLabelConfig:g.ariaLabelConfig}},bXn="react-flow__minimap-desc";function X0n({style:g,className:E,nodeStrokeColor:x,nodeColor:M,nodeClassName:N="",nodeBorderRadius:P=5,nodeStrokeWidth:k,nodeComponent:H,bgColor:U,maskColor:G,maskStrokeColor:ie,maskStrokeWidth:Z,position:W="bottom-right",onClick:se,onNodeClick:le,pannable:ee=!1,zoomable:Oe=!1,ariaLabel:ge,inversePan:Pe,zoomStep:ae=1,offsetScale:Me=5}){const Be=Sl(),rn=He.useRef(null),{boundingRect:ln,viewBB:xn,rfId:hn,panZoom:wt,translateExtent:Cn,flowWidth:Qn,flowHeight:Y,ariaLabelConfig:Fe}=Fu(dXn,El),mn=g?.width??fXn,Ae=g?.height??aXn,Ze=ln.width/mn,sn=ln.height/Ae,Sn=Math.max(Ze,sn),kn=Sn*mn,xe=Sn*Ae,un=Me*Sn,rt=ln.x-(kn-ln.width)/2-un,lt=ln.y-(xe-ln.height)/2-un,Bt=kn+un*2,hi=xe+un*2,Gt=`${bXn}-${hn}`,At=He.useRef(0),st=He.useRef();At.current=Sn,He.useEffect(()=>{if(rn.current&&wt)return st.current=MGn({domNode:rn.current,panZoom:wt,getTransform:()=>Be.getState().transform,getViewScale:()=>At.current}),()=>{st.current?.destroy()}},[wt]),He.useEffect(()=>{st.current?.update({translateExtent:Cn,width:Qn,height:Y,inversePan:Pe,pannable:ee,zoomStep:ae,zoomable:Oe})},[ee,Oe,Pe,ae,Cn,Qn,Y]);const Wr=se?mi=>{const[Fi,fu]=st.current?.pointer(mi)||[0,0];se(mi,{x:Fi,y:fu})}:void 0,Mr=le?He.useCallback((mi,Fi)=>{const fu=Be.getState().nodeLookup.get(Fi).internals.userNode;le(mi,fu)},[]):void 0,ur=ge??Fe["minimap.ariaLabel"];return F.jsx(Bue,{position:W,style:{...g,"--xy-minimap-background-color-props":typeof U=="string"?U:void 0,"--xy-minimap-mask-background-color-props":typeof G=="string"?G:void 0,"--xy-minimap-mask-stroke-color-props":typeof ie=="string"?ie:void 0,"--xy-minimap-mask-stroke-width-props":typeof Z=="number"?Z*Sn:void 0,"--xy-minimap-node-background-color-props":typeof M=="string"?M:void 0,"--xy-minimap-node-stroke-color-props":typeof x=="string"?x:void 0,"--xy-minimap-node-stroke-width-props":typeof k=="number"?k:void 0},className:Ta(["react-flow__minimap",E]),"data-testid":"rf__minimap",children:F.jsxs("svg",{width:mn,height:Ae,viewBox:`${rt} ${lt} ${Bt} ${hi}`,className:"react-flow__minimap-svg",role:"img","aria-labelledby":Gt,ref:rn,onClick:Wr,children:[ur&&F.jsx("title",{id:Gt,children:ur}),F.jsx(lXn,{onClick:Mr,nodeColor:M,nodeStrokeColor:x,nodeBorderRadius:P,nodeClassName:N,nodeStrokeWidth:k,nodeComponent:H}),F.jsx("path",{className:"react-flow__minimap-mask",d:`M${rt-un},${lt-un}h${Bt+un*2}v${hi+un*2}h${-Bt-un*2}z - M${xn.x},${xn.y}h${xn.width}v${xn.height}h${-xn.width}z`,fillRule:"evenodd",pointerEvents:"none"})]})})}X0n.displayName="MiniMap";const gXn=He.memo(X0n),wXn=g=>E=>g?`${Math.max(1/E.transform[2],1)}`:void 0,pXn={[mI.Line]:"right",[mI.Handle]:"bottom-right"};function mXn({nodeId:g,position:E,variant:x=mI.Handle,className:M,style:N=void 0,children:P,color:k,minWidth:H=10,minHeight:U=10,maxWidth:G=Number.MAX_VALUE,maxHeight:ie=Number.MAX_VALUE,keepAspectRatio:Z=!1,resizeDirection:W,autoScale:se=!0,shouldResize:le,onResizeStart:ee,onResize:Oe,onResizeEnd:ge}){const Pe=x0n(),ae=typeof g=="string"?g:Pe,Me=Sl(),Be=He.useRef(null),rn=x===mI.Handle,ln=Fu(He.useCallback(wXn(rn&&se),[rn,se]),El),xn=He.useRef(null),hn=E??pXn[x];He.useEffect(()=>{if(!(!Be.current||!ae))return xn.current||(xn.current=FGn({domNode:Be.current,nodeId:ae,getStoreItems:()=>{const{nodeLookup:Cn,transform:Qn,snapGrid:Y,snapToGrid:Fe,nodeOrigin:mn,domNode:Ae}=Me.getState();return{nodeLookup:Cn,transform:Qn,snapGrid:Y,snapToGrid:Fe,nodeOrigin:mn,paneDomNode:Ae}},onChange:(Cn,Qn)=>{const{triggerNodeChanges:Y,nodeLookup:Fe,parentLookup:mn,nodeOrigin:Ae}=Me.getState(),Ze=[],sn={x:Cn.x,y:Cn.y},Sn=Fe.get(ae);if(Sn&&Sn.expandParent&&Sn.parentId){const kn=Sn.origin??Ae,xe=Cn.width??Sn.measured.width??0,un=Cn.height??Sn.measured.height??0,rt={id:Sn.id,parentId:Sn.parentId,rect:{width:xe,height:un,...Kdn({x:Cn.x??Sn.position.x,y:Cn.y??Sn.position.y},{width:xe,height:un},Sn.parentId,Fe,kn)}},lt=Oke([rt],Fe,mn,Ae);Ze.push(...lt),sn.x=Cn.x?Math.max(kn[0]*xe,Cn.x):void 0,sn.y=Cn.y?Math.max(kn[1]*un,Cn.y):void 0}if(sn.x!==void 0&&sn.y!==void 0){const kn={id:ae,type:"position",position:{...sn}};Ze.push(kn)}if(Cn.width!==void 0&&Cn.height!==void 0){const xe={id:ae,type:"dimensions",resizing:!0,setAttributes:W?W==="horizontal"?"width":"height":!0,dimensions:{width:Cn.width,height:Cn.height}};Ze.push(xe)}for(const kn of Qn){const xe={...kn,type:"position"};Ze.push(xe)}Y(Ze)},onEnd:({width:Cn,height:Qn})=>{const Y={id:ae,type:"dimensions",resizing:!1,dimensions:{width:Cn,height:Qn}};Me.getState().triggerNodeChanges([Y])}})),xn.current.update({controlPosition:hn,boundaries:{minWidth:H,minHeight:U,maxWidth:G,maxHeight:ie},keepAspectRatio:Z,resizeDirection:W,onResizeStart:ee,onResize:Oe,onResizeEnd:ge,shouldResize:le}),()=>{xn.current?.destroy()}},[hn,H,U,G,ie,Z,ee,Oe,ge,le]);const wt=hn.split("-");return F.jsx("div",{className:Ta(["react-flow__resize-control","nodrag",...wt,x,M]),ref:Be,style:{...N,scale:ln,...k&&{[rn?"backgroundColor":"borderColor"]:k}},children:P})}He.memo(mXn);const vXn=g=>g.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),K0n=(...g)=>g.filter((E,x,M)=>!!E&&E.trim()!==""&&M.indexOf(E)===x).join(" ").trim();var yXn={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};const kXn=He.forwardRef(({color:g="currentColor",size:E=24,strokeWidth:x=2,absoluteStrokeWidth:M,className:N="",children:P,iconNode:k,...H},U)=>He.createElement("svg",{ref:U,...yXn,width:E,height:E,stroke:g,strokeWidth:M?Number(x)*24/Number(E):x,className:K0n("lucide",N),...H},[...k.map(([G,ie])=>He.createElement(G,ie)),...Array.isArray(P)?P:[P]]));const Ef=(g,E)=>{const x=He.forwardRef(({className:M,...N},P)=>He.createElement(kXn,{ref:P,iconNode:E,className:K0n(`lucide-${vXn(g)}`,M),...N}));return x.displayName=`${g}`,x};const jXn=Ef("Box",[["path",{d:"M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z",key:"hh9hay"}],["path",{d:"m3.3 7 8.7 5 8.7-5",key:"g66t2b"}],["path",{d:"M12 22V12",key:"d0xqtd"}]]);const EXn=Ef("Boxes",[["path",{d:"M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z",key:"lc1i9w"}],["path",{d:"m7 16.5-4.74-2.85",key:"1o9zyk"}],["path",{d:"m7 16.5 5-3",key:"va8pkn"}],["path",{d:"M7 16.5v5.17",key:"jnp8gn"}],["path",{d:"M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z",key:"8zsnat"}],["path",{d:"m17 16.5-5-3",key:"8arw3v"}],["path",{d:"m17 16.5 4.74-2.85",key:"8rfmw"}],["path",{d:"M17 16.5v5.17",key:"k6z78m"}],["path",{d:"M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z",key:"1xygjf"}],["path",{d:"M12 8 7.26 5.15",key:"1vbdud"}],["path",{d:"m12 8 4.74-2.85",key:"3rx089"}],["path",{d:"M12 13.5V8",key:"1io7kd"}]]);const YG=Ef("Check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);const SXn=Ef("CircleAlert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);const xXn=Ef("Clock3",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["polyline",{points:"12 6 12 12 16.5 12",key:"1aq6pp"}]]);const AXn=Ef("CodeXml",[["path",{d:"m18 16 4-4-4-4",key:"1inbqp"}],["path",{d:"m6 8-4 4 4 4",key:"15zrgr"}],["path",{d:"m14.5 4-5 16",key:"e7oirm"}]]);const V0n=Ef("Eye",[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);const MXn=Ef("FolderOpen",[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2",key:"usdka0"}]]);const TXn=Ef("GitBranch",[["line",{x1:"6",x2:"6",y1:"3",y2:"15",key:"17qcm7"}],["circle",{cx:"18",cy:"6",r:"3",key:"1h7g24"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}],["path",{d:"M18 9a9 9 0 0 1-9 9",key:"n2h4wq"}]]);const Y0n=Ef("Layers",[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",key:"zw3jo"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",key:"1wduqc"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",key:"kqbvx6"}]]);const Q1n=Ef("Link2",[["path",{d:"M9 17H7A5 5 0 0 1 7 7h2",key:"8i5ue5"}],["path",{d:"M15 7h2a5 5 0 1 1 0 10h-2",key:"1b9ql8"}],["line",{x1:"8",x2:"16",y1:"12",y2:"12",key:"1jonct"}]]);const CXn=Ef("Network",[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]]);const QG=Ef("Plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);const OXn=Ef("Redo2",[["path",{d:"m15 14 5-5-5-5",key:"12vg1m"}],["path",{d:"M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13",key:"6uklza"}]]);const NXn=Ef("RotateCcw",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]]);const DXn=Ef("Save",[["path",{d:"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",key:"1c8476"}],["path",{d:"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7",key:"1ydtos"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7",key:"t51u73"}]]);const Q0n=Ef("Scissors",[["circle",{cx:"6",cy:"6",r:"3",key:"1lh9wr"}],["path",{d:"M8.12 8.12 12 12",key:"1alkpv"}],["path",{d:"M20 4 8.12 15.88",key:"xgtan2"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}],["path",{d:"M14.8 14.8 20 20",key:"ptml3r"}]]);const IXn=Ef("Search",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]]);const wue=Ef("Trash2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]]);const dke=Ef("TriangleAlert",[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);const _Xn=Ef("Undo2",[["path",{d:"M9 14 4 9l5-5",key:"102s5s"}],["path",{d:"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11",key:"f3b9sd"}]]);const Ym=Ef("X",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);function LXn({mode:g,models:E,objects:x,application:M,initialModelType:N,suggestedSelector:P,nameReadOnly:k=!1,preview:H,onPreview:U,onSubmit:G,onClose:ie}){const Z=M?.modelType||N||E[0]?.type||"",[W,se]=He.useState(Z),le=E.find(st=>st.type===W)??null,[ee,Oe]=He.useState(M?.name||M?.applicationId||W1n(le)),[ge,Pe]=He.useState(()=>Tue(le,M)),ae=M?.selector||P||$Xn(x),[Me,Be]=He.useState(ae.multiplicity),[rn,ln]=He.useState(cue(ae,"scale")),[xn,hn]=He.useState(cue(ae,"kind")),[wt,Cn]=He.useState(cue(ae,"species")),[Qn,Y]=He.useState(cue(ae,"name")),Fe=RXn(ae,"within"),[mn,Ae]=He.useState(Fe?.type==="Scope"?"named_scope":"scene"),[Ze,sn]=He.useState(Fe?.type==="Scope"?String(Fe.name||""):""),[Sn,kn]=He.useState(M?.timestep?"clock":"default"),[xe,un]=He.useState("1.0"),[rt,lt]=He.useState("0.0");He.useEffect(()=>{const st=E.find(Wr=>Wr.type===W)??null;Pe(Tue(st,g==="update"?M:void 0)),g==="add"&&Oe(W1n(st))},[M,g,W,E]);const Bt=He.useMemo(()=>({scales:$G(x.map(st=>st.scale)),kinds:$G(x.map(st=>st.kind)),species:$G(x.map(st=>st.species)),names:$G(x.map(st=>st.name))}),[x]),hi=He.useMemo(()=>{const st=[rn&&`scale ${rn}`,xn&&`kind ${xn}`,wt&&`species ${wt}`,Qn&&`name ${Qn}`].filter(Boolean);return st.length?st.join(", "):"all scene objects"},[xn,Qn,rn,wt]),Gt=()=>{const st={selectors:[]};return st.within=mn==="named_scope"&&Ze?{type:"Scope",name:Ze}:{type:"SceneScope"},rn&&(st.scale=rn),xn&&(st.kind=xn),wt&&(st.species=wt),Qn&&(st.name=Qn),{type:BXn(Me),multiplicity:Me,criteria:st,julia:""}},At=()=>{G({applicationId:M?.applicationId,modelType:W,name:ee.trim(),parameters:ge,selector:Gt(),timestep:Sn==="clock"?{mode:"clock",dt:xe,phase:rt}:{mode:"default"}})};return F.jsx("div",{className:"overlay-backdrop",onMouseDown:ie,children:F.jsxs("section",{className:"overlay-panel application-form",onMouseDown:st=>st.stopPropagation(),"data-testid":"application-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:g==="add"?"Add application":`Update ${M?.applicationId}`}),F.jsx("span",{children:"A configured use of a model on selected scene objects"})]}),F.jsx("button",{onClick:ie,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content application-form-content",children:[F.jsxs("label",{children:["Model",F.jsx("select",{value:W,onChange:st=>se(st.target.value),"data-testid":"application-model-select",children:E.map(st=>F.jsxs("option",{value:st.type,children:[st.package?`${st.package} · `:"",st.name," (",st.process,")"]},st.type))})]}),F.jsxs("label",{children:["Application name",F.jsx("input",{value:ee,disabled:k,onChange:st=>Oe(st.target.value),"data-testid":"application-name"})]}),le&&le.constructor.fields.length>0&&F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Model parameters"}),F.jsx(W0n,{fields:le.constructor.fields,values:ge,onChange:Pe})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Target selector"}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Multiplicity",F.jsxs("select",{value:Me,onChange:st=>Be(st.target.value),children:[F.jsx("option",{value:"one",children:"One"}),F.jsx("option",{value:"optional_one",children:"Optional one"}),F.jsx("option",{value:"many",children:"Many"})]})]}),F.jsxs("label",{children:["Scope",F.jsxs("select",{value:mn,onChange:st=>Ae(st.target.value),children:[F.jsx("option",{value:"scene",children:"Whole scene"}),F.jsx("option",{value:"named_scope",children:"Named object subtree"})]})]}),mn==="named_scope"&&F.jsx(_G,{label:"Scope root",value:Ze,options:Bt.names,onChange:sn}),F.jsx(_G,{label:"Scale",value:rn,options:Bt.scales,onChange:ln}),F.jsx(_G,{label:"Kind",value:xn,options:Bt.kinds,onChange:hn}),F.jsx(_G,{label:"Species",value:wt,options:Bt.species,onChange:Cn}),F.jsx(_G,{label:"Object name",value:Qn,options:Bt.names,onChange:Y})]}),F.jsxs("p",{className:"selector-summary",children:["Julia will resolve ",F.jsx("strong",{children:Me.replace("_"," ")})," target from ",hi,"."]}),F.jsxs("button",{className:"selector-preview-button",type:"button",onClick:()=>U(Gt()),"data-testid":"application-target-preview",children:[F.jsx(V0n,{size:15})," Preview targets in Julia"]}),H&&F.jsxs("section",{className:"selector-preview","data-testid":"application-target-preview-result",children:[F.jsxs("strong",{children:[H.count," target object",H.count===1?"":"s"]}),F.jsx("code",{children:H.objectIds.map(String).join(", ")||"No targets"})]})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Timestep"}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Mode",F.jsxs("select",{value:Sn,onChange:st=>kn(st.target.value),children:[F.jsx("option",{value:"default",children:"Model or environment default"}),F.jsx("option",{value:"clock",children:"Explicit clock"})]})]}),Sn==="clock"&&F.jsxs(F.Fragment,{children:[F.jsxs("label",{children:["Step",F.jsx("input",{value:xe,onChange:st=>un(st.target.value)})]}),F.jsxs("label",{children:["Phase",F.jsx("input",{value:rt,onChange:st=>lt(st.target.value)})]})]})]})]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:ie,children:"Cancel"}),F.jsxs("button",{className:"primary",disabled:!W||!ee.trim(),onClick:At,"data-testid":"application-submit",children:[F.jsx(YG,{size:15})," ",g==="add"?"Add application":"Apply changes"]})]})]})})}function W0n({fields:g,values:E,onChange:x}){const M=new Map;for(const P of g)P.typeParameter&&!M.has(P.typeParameter)&&M.set(P.typeParameter,P.name);const N=(P,k)=>{const H=P.typeParameter?g.filter(U=>U.typeParameter===P.typeParameter).map(U=>U.name):[P.name];x(Object.fromEntries(Object.entries(E).map(([U,G])=>[U,H.includes(U)?{...G,type:k}:G])))};return F.jsx("div",{className:"parameter-list",children:g.map(P=>{const k=E[P.name]||{type:P.inferredChoice,value:""},H=!P.typeParameter||M.get(P.typeParameter)===P.name;return F.jsxs("div",{className:"parameter-row",children:[F.jsxs("label",{children:[F.jsx("span",{children:P.name}),F.jsx("small",{children:P.declaredType}),F.jsx("input",{"data-testid":`application-param-${P.name}`,value:k.value,onChange:U=>x({...E,[P.name]:{...k,value:U.target.value}})})]}),H&&F.jsxs("label",{className:"parameter-type",children:[F.jsx("span",{children:P.typeParameter?`${P.typeParameter} type`:"Value type"}),F.jsx("select",{"data-testid":`application-param-type-${P.name}`,value:k.type,onChange:U=>N(P,U.target.value),children:P.choices.map(U=>F.jsx("option",{value:U,children:U},U))})]})]},P.name)})})}function _G({label:g,value:E,options:x,onChange:M}){return F.jsxs("label",{children:[g,F.jsxs("select",{value:E,onChange:N=>M(N.target.value),children:[F.jsx("option",{value:"",children:"Any"}),x.map(N=>F.jsx("option",{value:N,children:N},N))]})]})}function Tue(g,E){return g?Object.fromEntries(g.constructor.fields.map(x=>{const M=E?.modelParameters[x.name],N=M?.type||x.inferredChoice,P=M?M.julia:x.hasDefault?N==="julia"?x.defaultJulia||"":PXn(x.default,N):"";return[x.name,{type:N,value:P}]})):{}}function PXn(g,E){const x=g==null?"":String(g);return E==="symbol"?x.replace(/^:/,""):x}function W1n(g){return g?.process||g?.name||"application"}function $Xn(g){const E=$G(g.map(x=>x.scale))[0];return{type:"Many",multiplicity:"many",criteria:E?{selectors:[],scale:E}:{selectors:[]},julia:""}}function cue(g,E){const x=g.criteria[E];return typeof x=="string"?x:""}function RXn(g,E){const x=g.criteria[E];return x&&typeof x=="object"?x:null}function BXn(g){return g==="one"?"One":g==="optional_one"?"OptionalOne":"Many"}function $G(g){return[...new Set(g.filter(E=>!!E))].sort()}function zXn({application:g,applications:E,onCommand:x,onClose:M}){const N=E.filter(Be=>Be.applicationId!==g.applicationId),[P,k]=He.useState(""),[H,U]=He.useState(N[0]?.applicationId||""),[G,ie]=He.useState(String(g.environment?.provider||"scene")),[Z,W]=He.useState(g.updates||[]),[se,le]=He.useState(g.outputs[0]?.name||""),[ee,Oe]=He.useState(N[0]?.applicationId||""),ge=N.find(Be=>Be.applicationId===H),Pe=He.useMemo(()=>({type:ge?.targetCount===1?"One":"Many",multiplicity:ge?.targetCount===1?"one":"many",criteria:{selectors:[],within:{type:"SceneScope"},application:H},julia:""}),[H,ge?.targetCount]),ae=()=>{!P.trim()||!H||(x({action:"edit",kind:"set_call_binding",applicationId:g.applicationId,call:P.trim(),selector:Pe}),k(""))},Me=()=>{!se||!ee||W(Be=>[...Be.filter(rn=>!rn.variables.includes(se)),{variables:[se],after:[ee]}])};return F.jsx("div",{className:"overlay-backdrop",onMouseDown:M,children:F.jsxs("section",{className:"overlay-panel application-configuration-form",onMouseDown:Be=>Be.stopPropagation(),"data-testid":"application-configuration-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsxs("strong",{children:["Configure ",g.applicationId]}),F.jsx("span",{children:"Authored coupling and execution policy, validated by Julia"})]}),F.jsx("button",{onClick:M,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content application-configuration-content",children:[F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Explicit input bindings"}),Object.entries(g.inputBindings).length===0&&F.jsx("p",{children:"No authored input bindings. Unique same-object producers may still be inferred."}),F.jsx("div",{className:"configuration-list",children:Object.entries(g.inputBindings).map(([Be,rn])=>F.jsxs("div",{children:[F.jsx("code",{children:Be}),F.jsx("span",{children:rn.julia||rn.type}),F.jsx("button",{className:"danger icon-button",title:`Remove ${Be} binding`,onClick:()=>x({action:"edit",kind:"remove_input_binding",applicationId:g.applicationId,input:Be}),children:F.jsx(wue,{size:14})})]},Be))})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Manual calls"}),F.jsx("div",{className:"configuration-list",children:Object.entries(g.callBindings).map(([Be,rn])=>F.jsxs("div",{children:[F.jsx("code",{children:Be}),F.jsx("span",{children:rn.julia||rn.type}),F.jsx("button",{className:"danger icon-button",title:`Remove ${Be} call`,onClick:()=>x({action:"edit",kind:"remove_call_binding",applicationId:g.applicationId,call:Be}),children:F.jsx(wue,{size:14})})]},Be))}),F.jsxs("div",{className:"form-grid compact-configuration-row",children:[F.jsxs("label",{children:["Call name",F.jsx("input",{"data-testid":"call-name",value:P,onChange:Be=>k(Be.target.value),placeholder:"child"})]}),F.jsxs("label",{children:["Target application",F.jsxs("select",{"data-testid":"call-target",value:H,onChange:Be=>U(Be.target.value),children:[F.jsx("option",{value:"",children:"Choose application"}),N.map(Be=>F.jsx("option",{value:Be.applicationId,children:Be.applicationId},Be.applicationId))]})]}),F.jsxs("button",{type:"button","data-testid":"add-call-binding",disabled:!P.trim()||!H,onClick:ae,children:[F.jsx(QG,{size:14})," Add call"]})]})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Environment"}),F.jsxs("div",{className:"form-grid compact-configuration-row",children:[F.jsxs("label",{children:["Provider",F.jsx("input",{"data-testid":"environment-provider",value:G,onChange:Be=>ie(Be.target.value),placeholder:"scene"})]}),F.jsxs("button",{type:"button","data-testid":"apply-environment-provider",disabled:!G.trim(),onClick:()=>x({action:"edit",kind:"set_environment_provider",applicationId:g.applicationId,provider:G.trim()}),children:[F.jsx(YG,{size:14})," Apply provider"]})]}),Object.keys(g.meteoBindings||{}).length>0&&F.jsx("code",{children:JSON.stringify(g.meteoBindings)})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Output routing"}),F.jsx("div",{className:"configuration-list",children:g.outputs.map(Be=>F.jsxs("label",{children:[F.jsx("code",{children:Be.name}),F.jsxs("select",{"data-testid":`output-routing-${Be.name}`,value:g.outputRouting[Be.name]||"canonical",onChange:rn=>x({action:"edit",kind:"set_output_routing",applicationId:g.applicationId,output:Be.name,route:rn.target.value}),children:[F.jsx("option",{value:"canonical",children:"Canonical status owner"}),F.jsx("option",{value:"stream_only",children:"Stream only"})]})]},Be.name))})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Duplicate-writer ordering"}),F.jsx("div",{className:"configuration-list",children:Z.map((Be,rn)=>F.jsxs("div",{children:[F.jsx("code",{children:Be.variables.join(", ")}),F.jsxs("span",{children:["after ",Be.after.join(", ")]}),F.jsx("button",{className:"danger icon-button",title:"Remove update ordering",onClick:()=>W(ln=>ln.filter((xn,hn)=>hn!==rn)),children:F.jsx(wue,{size:14})})]},`${Be.variables.join(",")}:${Be.after.join(",")}`))}),F.jsxs("div",{className:"form-grid compact-configuration-row",children:[F.jsxs("label",{children:["Output",F.jsx("select",{value:se,onChange:Be=>le(Be.target.value),children:g.outputs.map(Be=>F.jsx("option",{value:Be.name,children:Be.name},Be.name))})]}),F.jsxs("label",{children:["Run after",F.jsxs("select",{value:ee,onChange:Be=>Oe(Be.target.value),children:[F.jsx("option",{value:"",children:"Choose application"}),N.map(Be=>F.jsx("option",{value:Be.applicationId,children:Be.applicationId},Be.applicationId))]})]}),F.jsxs("button",{type:"button",disabled:!se||!ee,onClick:Me,children:[F.jsx(QG,{size:14})," Add rule"]}),F.jsxs("button",{type:"button",onClick:()=>x({action:"edit",kind:"set_update_ordering",applicationId:g.applicationId,updates:Z}),children:[F.jsx(YG,{size:14})," Apply ordering"]})]})]})]}),F.jsx("footer",{children:F.jsx("button",{className:"primary",onClick:M,children:"Done"})})]})})}function FXn({endpoints:g,objects:E,preview:x,onPreview:M,onSubmit:N,onClose:P}){const k=JXn(g.sourceApplication.targetIds,g.targetApplication.targetIds),[H,U]=He.useState(g.sourceApplication.targetCount>1&&g.targetApplication.targetCount===1?"many":"one"),[G,ie]=He.useState(k?"self":""),[Z,W]=He.useState("scene"),[se,le]=He.useState(""),[ee,Oe]=He.useState(""),[ge,Pe]=He.useState(X7e(g.sourceApplication.targetScales)),[ae,Me]=He.useState(X7e(g.sourceApplication.targetKinds)),[Be,rn]=He.useState(X7e(g.sourceApplication.targetSpecies)),[ln,xn]=He.useState(""),[hn,wt]=He.useState("application"),[Cn,Qn]=He.useState("automatic"),[Y,Fe]=He.useState(""),mn=uue(E.map(kn=>kn.scale)),Ae=uue(E.map(kn=>kn.kind)),Ze=uue(E.map(kn=>kn.species)),sn=uue(E.map(kn=>kn.name)),Sn=()=>{const kn={selectors:[],var:g.sourcePort.name};kn[hn]=hn==="application"?g.sourceApplication.applicationId:g.sourceApplication.process;const xe=qXn(Z,se,ee);if(xe&&(kn.within=xe),G&&(kn.relation=G),ge&&(kn.scale=ge),ae&&(kn.kind=ae),Be&&(kn.species=Be),ln&&(kn.name=ln),Cn!=="automatic"&&(kn.policy={type:GXn(Cn)}),Y.trim()){const un=Number(Y);Number.isFinite(un)&&(kn.window=un)}return{applicationId:g.targetApplication.applicationId,input:g.targetPort.name,selector:{type:HXn(H),multiplicity:H,criteria:kn,julia:""}}};return F.jsx("div",{className:"overlay-backdrop",onMouseDown:P,children:F.jsxs("section",{className:"overlay-panel binding-form",onMouseDown:kn=>kn.stopPropagation(),"data-testid":"binding-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:"Connect applications"}),F.jsx("span",{children:"Julia resolves this declaration into concrete object bindings"})]}),F.jsx("button",{onClick:P,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content",children:[F.jsxs("div",{className:"binding-route",children:[F.jsxs("div",{children:[F.jsx("small",{children:"Producer"}),F.jsx("strong",{children:g.sourceApplication.applicationId}),F.jsx("code",{children:g.sourcePort.name})]}),F.jsx(Q1n,{size:22}),F.jsxs("div",{children:[F.jsx("small",{children:"Consumer"}),F.jsx("strong",{children:g.targetApplication.applicationId}),F.jsx("code",{children:g.targetPort.name})]})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Source object selector"}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Multiplicity",F.jsxs("select",{value:H,onChange:kn=>U(kn.target.value),children:[F.jsx("option",{value:"one",children:"One"}),F.jsx("option",{value:"optional_one",children:"Optional one"}),F.jsx("option",{value:"many",children:"Many"})]})]}),F.jsxs("label",{children:["Scope",F.jsxs("select",{value:Z,onChange:kn=>W(kn.target.value),children:[F.jsx("option",{value:"scene",children:"Whole scene"}),F.jsx("option",{value:"self",children:"Consumer object"}),F.jsx("option",{value:"subtree",children:"Consumer subtree"}),F.jsx("option",{value:"self_plant",children:"Consumer plant"}),F.jsx("option",{value:"ancestor",children:"Ancestor subtree"}),F.jsx("option",{value:"named_scope",children:"Named object subtree"})]})]}),Z==="ancestor"&&F.jsx(oI,{label:"Ancestor scale",value:ee,options:mn,onChange:Oe}),Z==="named_scope"&&F.jsx(oI,{label:"Scope root",value:se,options:sn,onChange:le}),F.jsxs("label",{children:["Relation",F.jsxs("select",{value:G,onChange:kn=>ie(kn.target.value),children:[F.jsx("option",{value:"",children:"Any relation"}),F.jsx("option",{value:"self",children:"Same object"}),F.jsx("option",{value:"parent",children:"Parent"}),F.jsx("option",{value:"children",children:"Children"}),F.jsx("option",{value:"ancestors",children:"Ancestors"}),F.jsx("option",{value:"descendants",children:"Descendants"}),F.jsx("option",{value:"siblings",children:"Siblings"})]})]}),F.jsxs("label",{children:["Producer filter",F.jsxs("select",{value:hn,onChange:kn=>wt(kn.target.value),children:[F.jsx("option",{value:"application",children:"This application"}),F.jsx("option",{value:"process",children:"Any application of this process"})]})]}),F.jsx(oI,{label:"Scale",value:ge,options:mn,onChange:Pe}),F.jsx(oI,{label:"Kind",value:ae,options:Ae,onChange:Me}),F.jsx(oI,{label:"Species",value:Be,options:Ze,onChange:rn}),F.jsx(oI,{label:"Object name",value:ln,options:sn,onChange:xn}),F.jsxs("label",{children:["Temporal policy",F.jsxs("select",{value:Cn,onChange:kn=>Qn(kn.target.value),children:[F.jsx("option",{value:"automatic",children:"Automatic"}),F.jsx("option",{value:"hold_last",children:"Hold last"}),F.jsx("option",{value:"interpolate",children:"Interpolate"}),F.jsx("option",{value:"integrate",children:"Integrate"}),F.jsx("option",{value:"aggregate",children:"Aggregate"})]})]}),F.jsxs("label",{children:["Window (scene steps)",F.jsx("input",{type:"number",min:"0",step:"1",value:Y,onChange:kn=>Fe(kn.target.value),placeholder:"Automatic"})]})]})]}),x&&F.jsxs("section",{className:"selector-preview","data-testid":"binding-preview",children:[F.jsxs("strong",{children:[x.bindingCount," resolved binding",x.bindingCount===1?"":"s"]}),F.jsxs("span",{children:[x.consumerObjectIds.length," consumer object",x.consumerObjectIds.length===1?"":"s"," from ",x.sourceObjectIds.length," source object",x.sourceObjectIds.length===1?"":"s"]}),x.sourceApplicationIds.length>0&&F.jsx("code",{children:x.sourceApplicationIds.join(", ")}),x.diagnostics.map(kn=>F.jsx("p",{children:kn},kn))]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:P,children:"Cancel"}),F.jsxs("button",{onClick:()=>M(Sn()),"data-testid":"binding-preview-button",children:[F.jsx(V0n,{size:15})," Preview resolution"]}),F.jsxs("button",{className:"primary",onClick:()=>N(Sn()),"data-testid":"binding-submit",children:[F.jsx(Q1n,{size:15})," Apply binding"]})]})]})})}function oI({label:g,value:E,options:x,onChange:M}){return F.jsxs("label",{children:[g,F.jsxs("select",{value:E,onChange:N=>M(N.target.value),children:[F.jsx("option",{value:"",children:"Any"}),x.map(N=>F.jsx("option",{value:N,children:N},N))]})]})}function JXn(g,E){return g.length===E.length&&g.every(x=>E.some(M=>String(M)===String(x)))}function X7e(g){return g.length===1?g[0]:""}function HXn(g){return g==="one"?"One":g==="optional_one"?"OptionalOne":"Many"}function uue(g){return[...new Set(g.filter(E=>!!E))].sort()}function GXn(g){return g==="hold_last"?"HoldLast":g==="interpolate"?"Interpolate":g==="integrate"?"Integrate":"Aggregate"}function qXn(g,E,x){return g==="scene"?{type:"SceneScope"}:g==="self"?{type:"Self"}:g==="subtree"?{type:"Subtree"}:g==="self_plant"?{type:"SelfPlant"}:g==="ancestor"?{type:"Ancestor",scale:x||null}:g==="named_scope"&&E?{type:"Scope",name:E}:null}function UXn({mode:g,objects:E,object:x,onSubmit:M,onClose:N}){const[P,k]=He.useState(String(x?.objectId??"")),[H,U]=He.useState(XXn(x?.parent)),[G,ie]=He.useState(x?.scale||""),[Z,W]=He.useState(x?.kind||""),[se,le]=He.useState(x?.species||""),[ee,Oe]=He.useState(x?.name||""),ge=He.useMemo(()=>E.filter(ae=>String(ae.objectId)!==P),[P,E]),Pe=()=>M({objectId:P.trim(),configuration:{parent:H||null,scale:G.trim()||null,kind:Z.trim()||null,species:se.trim()||null,name:ee.trim()||null}});return F.jsx("div",{className:"overlay-backdrop",onMouseDown:N,children:F.jsxs("section",{className:"overlay-panel object-form",onMouseDown:ae=>ae.stopPropagation(),"data-testid":"object-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:g==="add"?"Add scene object":`Update object ${String(x?.objectId)}`}),F.jsx("span",{children:"Objects define the concrete entities and topology targeted by applications"})]}),F.jsx("button",{onClick:N,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content object-form-content",children:[F.jsxs("label",{children:["Stable object ID",F.jsx("input",{value:P,disabled:g==="update",onChange:ae=>k(ae.target.value),"data-testid":"object-id"})]}),F.jsxs("label",{children:["Parent object",F.jsxs("select",{value:H,onChange:ae=>U(ae.target.value),children:[F.jsx("option",{value:"",children:"No parent"}),ge.map(ae=>F.jsxs("option",{value:String(ae.objectId),children:[ae.name||String(ae.objectId)," · ",ae.scale||"unscaled"]},ae.id))]})]}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Scale",F.jsx("input",{value:G,onChange:ae=>ie(ae.target.value)})]}),F.jsxs("label",{children:["Kind",F.jsx("input",{value:Z,onChange:ae=>W(ae.target.value)})]}),F.jsxs("label",{children:["Species",F.jsx("input",{value:se,onChange:ae=>le(ae.target.value)})]}),F.jsxs("label",{children:["Name",F.jsx("input",{value:ee,onChange:ae=>Oe(ae.target.value)})]})]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:N,children:"Cancel"}),F.jsxs("button",{className:"primary",disabled:!P.trim(),onClick:Pe,"data-testid":"object-submit",children:[F.jsx(YG,{size:15})," ",g==="add"?"Add object":"Apply changes"]})]})]})})}function XXn(g){if(g==null||g==="")return"";const E=String(g);return E.startsWith("object:")?E.slice(7):E}function KXn({application:g,models:E,instances:x,onSubmit:M,onRemove:N,onClose:P}){const k=He.useMemo(()=>E.filter(hn=>hn.process===g.process),[g.process,E]),[H,U]=He.useState("instance"),[G,ie]=He.useState(g.targetInstances[0]||x[0]?.name||""),Z=x.find(hn=>hn.name===G),W=(Z?.objectIds||[]).filter(hn=>g.targetIds.some(wt=>String(wt)===String(hn))),[se,le]=He.useState(W[0]??""),[ee,Oe]=He.useState(g.modelType),ge=k.find(hn=>hn.type===ee)||k[0]||null,[Pe,ae]=He.useState(()=>Tue(ge,g)),Me=VXn(g.applicationId,G),Be=!!Z?.instanceOverrides.includes(Me),rn=!!Z?.objectOverrides.some(hn=>{const wt=hn;return String(wt.object??wt.objectId??"")===String(se)&&String(wt.application??wt.applicationId??"")===Me}),ln=H==="instance"?Be:rn,xn=hn=>{Oe(hn),ae(Tue(k.find(wt=>wt.type===hn)||null))};return F.jsx("div",{className:"overlay-backdrop",onMouseDown:P,children:F.jsxs("section",{className:"overlay-panel override-form",onMouseDown:hn=>hn.stopPropagation(),"data-testid":"override-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:"Create a model override"}),F.jsx("span",{children:"The shared template remains unchanged outside the selected scope"})]}),F.jsx("button",{onClick:P,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content override-form-content",children:[F.jsxs("div",{className:"override-scope-choice",children:[F.jsxs("button",{className:H==="instance"?"active":"",onClick:()=>U("instance"),children:[F.jsx("strong",{children:"One instance"}),F.jsx("span",{children:"All targets of this application in one plant or object instance"})]}),F.jsxs("button",{className:H==="object"?"active":"",onClick:()=>U("object"),children:[F.jsx("strong",{children:"One object"}),F.jsx("span",{children:"Only one concrete execution receives the replacement model"})]})]}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Instance",F.jsx("select",{value:G,onChange:hn=>{const wt=hn.target.value,Qn=(x.find(Y=>Y.name===wt)?.objectIds||[]).find(Y=>g.targetIds.some(Fe=>String(Fe)===String(Y)));ie(wt),le(Qn??"")},children:x.filter(hn=>g.targetInstances.includes(hn.name)).map(hn=>F.jsx("option",{value:hn.name,children:hn.name},hn.name))})]}),H==="object"&&F.jsxs("label",{children:["Object",F.jsx("select",{value:String(se),onChange:hn=>le(hn.target.value),children:W.map(hn=>F.jsx("option",{value:String(hn),children:String(hn)},String(hn)))})]}),F.jsxs("label",{children:["Replacement model",F.jsx("select",{value:ee,onChange:hn=>xn(hn.target.value),children:k.map(hn=>F.jsxs("option",{value:hn.type,children:[hn.package?`${hn.package} · `:"",hn.name]},hn.type))})]})]}),ge&&ge.constructor.fields.length>0&&F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Model parameters"}),F.jsx(W0n,{fields:ge.constructor.fields,values:Pe,onChange:ae})]}),F.jsxs("div",{className:"override-warning",children:[F.jsx("strong",{children:H==="instance"?`Override ${G}`:`Override object ${String(se)}`}),F.jsx("span",{children:"Julia validates that the replacement keeps the same process and declared variable contract."})]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:P,children:"Cancel"}),ln&&F.jsxs("button",{className:"danger","data-testid":"remove-override",onClick:()=>N({scope:H,instance:G,objectId:H==="object"?se:void 0,applicationId:g.applicationId,modelType:ee,parameters:Pe}),children:[F.jsx(wue,{size:15})," Remove override"]}),F.jsxs("button",{className:"primary",disabled:!G||!ee||H==="object"&&!String(se),onClick:()=>M({scope:H,instance:G,objectId:H==="object"?se:void 0,applicationId:g.applicationId,modelType:ee,parameters:Pe}),children:[F.jsx(YG,{size:15})," Apply override"]})]})]})})}function VXn(g,E){const x=`${E}__`;return g.startsWith(x)?g.slice(x.length):g}function Z0n(g){if(g.detailMode==="overview")return 190;const E=Math.max(g.applicationId.length,g.modelName.length,...g.inputs.map(x=>x.name.length),...g.outputs.map(x=>x.name.length));return Math.max(310,Math.min(540,235+E*7))}function YXn({data:g,selected:E}){const x=g.detailMode==="overview",M=new Set(g.requiredInputPortIds),N=new Set(g.candidatePortIds),P=new Set(g.previousTimeStepPortIds),k=new Set(g.cycleBreakInputPortIds),H=ZXn(g);return F.jsxs("section",{className:`model-node application-node ${x?"overview-node":""} ${g.cyclic?"cyclic":""} ${E?"selected":""}`,"data-testid":`application-node-${g.applicationId}`,style:{width:Z0n(g)},children:[x&&F.jsx(WXn,{inputs:g.inputs,outputs:g.outputs}),F.jsxs("header",{className:"node-header",children:[F.jsxs("div",{children:[F.jsx("div",{className:"process",children:g.name||g.applicationId}),F.jsx("div",{className:"model-type",children:g.modelName})]}),F.jsx(Y0n,{size:18})]}),x?F.jsxs("div",{className:"overview-node-summary",children:[F.jsxs("span",{children:[g.targetCount," targets"]}),F.jsxs("span",{children:[g.inputs.length," in"]}),F.jsxs("span",{children:[g.outputs.length," out"]})]}):F.jsxs(F.Fragment,{children:[F.jsxs("div",{className:"node-meta",children:[F.jsxs("span",{className:"meta-chip",title:g.selector.julia,children:[F.jsx(jXn,{size:13})," ",H]}),F.jsxs("span",{className:"meta-chip",title:String(g.clock??"Default scene cadence"),children:[F.jsx(xXn,{size:13})," ",eKn(g)]})]}),F.jsxs("div",{className:"target-summary",children:[F.jsx("strong",{children:g.targetCount})," concrete target",g.targetCount===1?"":"s"]}),F.jsxs("div",{className:"ports-grid",children:[F.jsx(oue,{title:"Inputs",side:"input",ports:g.inputs,required:M,candidates:N,previous:P,cycleBreaks:k,cycleBreakMode:g.cycleBreakMode,application:g,onCandidateClick:g.onCandidateClick,onPortClick:g.onPortClick,onCycleBreak:g.onCycleBreak}),F.jsx(oue,{title:"Outputs",side:"output",ports:g.outputs,required:M,candidates:N,previous:P,cycleBreaks:k,cycleBreakMode:g.cycleBreakMode,application:g,onCandidateClick:g.onCandidateClick,onPortClick:g.onPortClick,onCycleBreak:g.onCycleBreak})]}),(g.environmentInputs.length>0||g.environmentOutputs.length>0)&&F.jsxs("div",{className:"ports-grid environment-ports",children:[F.jsx(oue,{title:"Environment inputs",side:"input",ports:g.environmentInputs,required:M,candidates:new Set,previous:new Set,cycleBreaks:new Set,cycleBreakMode:!1,application:g,onPortClick:g.onPortClick}),F.jsx(oue,{title:"Environment outputs",side:"output",ports:g.environmentOutputs,required:M,candidates:new Set,previous:new Set,cycleBreaks:new Set,cycleBreakMode:!1,application:g,onPortClick:g.onPortClick})]})]})]})}function QXn({data:g,selected:E}){return F.jsxs("section",{className:`entity-node ${g.nodeKind} ${E?"selected":""}`,"data-testid":`${g.nodeKind}-node`,children:[(g.inputPortIds?.length?g.inputPortIds:[void 0]).map((x,M)=>F.jsx(o5,{id:x,type:"target",position:cr.Left,style:{top:`${Cue(M,g.inputPortIds?.length??1)}%`}},x??"target")),F.jsxs("header",{children:[F.jsx("strong",{children:g.title}),F.jsx("span",{children:g.subtitle})]}),F.jsx("div",{className:"badges",children:g.badges.map(x=>F.jsx("span",{className:"meta-chip",children:x},x))}),(g.outputPortIds?.length?g.outputPortIds:[void 0]).map((x,M)=>F.jsx(o5,{id:x,type:"source",position:cr.Right,style:{top:`${Cue(M,g.outputPortIds?.length??1)}%`}},x??"source"))]})}function oue({title:g,side:E,ports:x,required:M,candidates:N,previous:P,cycleBreaks:k,cycleBreakMode:H,application:U,onCandidateClick:G,onPortClick:ie,onCycleBreak:Z}){return F.jsxs("div",{className:`port-column ${E}`,children:[F.jsx("div",{className:"port-title",children:g}),x.map(W=>F.jsxs("div",{className:`port ${M.has(W.id)?"required-input":""} ${P.has(W.id)?"previous":""}`,"data-testid":`port-${E}-${W.name}`,title:`${W.name}: ${W.defaultJulia}`,onClick:se=>{se.stopPropagation(),ie?.(W)},children:[E==="input"&&F.jsx(o5,{id:W.id,type:"target",position:cr.Left}),F.jsx("span",{children:W.name}),N.has(W.id)&&F.jsx("button",{className:"port-candidate-button nodrag nopan",type:"button",title:E==="input"?"Models that compute this variable":"Models that consume this variable","aria-label":E==="input"?`Models that compute ${W.name}`:`Models that consume ${W.name}`,onClick:se=>{se.stopPropagation();const le=se.currentTarget.getBoundingClientRect();G?.(W,{x:le.right,y:le.top+le.height/2})},children:F.jsx(QG,{size:11})}),E==="input"&&H&&k.has(W.id)&&F.jsx("button",{className:"cycle-port-break nodrag nopan",type:"button",title:`Read ${W.name} from the previous accepted timestep`,"aria-label":`Break cycle at ${U.applicationId}.${W.name}`,"data-testid":`cycle-break-${U.applicationId}-${W.name}`,onClick:se=>{se.stopPropagation(),Z?.(U,W)},children:F.jsx(Q0n,{size:12})}),P.has(W.id)&&F.jsx("small",{className:"previous-label",children:"t-1"}),E==="output"&&F.jsx(o5,{id:W.id,type:"source",position:cr.Right})]},W.id))]})}function WXn({inputs:g,outputs:E}){return F.jsxs(F.Fragment,{children:[g.map((x,M)=>F.jsx(o5,{id:x.id,type:"target",position:cr.Left,style:{top:`${Cue(M,g.length)}%`}},x.id)),E.map((x,M)=>F.jsx(o5,{id:x.id,type:"source",position:cr.Right,style:{top:`${Cue(M,E.length)}%`}},x.id))]})}function Cue(g,E){return E<=1?52:28+g/(E-1)*48}function ZXn(g){const E=[...g.targetInstances,...g.targetScales,...g.targetKinds];return E.length>0?E.slice(0,2).join(" / "):g.selector.type}function eKn(g){return g.timestep===null||g.timestep===void 0?"default rate":String(g.timestep)}function nKn({id:g,sourceX:E,sourceY:x,targetX:M,targetY:N,sourcePosition:P=cr.Right,targetPosition:k=cr.Left,markerEnd:H,style:U,data:G}){const[ie,Z,W]=Aue({sourceX:E,sourceY:x,targetX:M,targetY:N,sourcePosition:P,targetPosition:k,borderRadius:14,offset:24}),se=tKn(G);return F.jsxs(F.Fragment,{children:[F.jsx(cq,{id:g,path:ie,markerEnd:H,style:U,interactionWidth:18}),se&&F.jsx(JUn,{children:F.jsx("div",{className:`edge-chip ${G?.kind??""} ${G?.cycle?"cycle":""}`,style:{transform:`translate(-50%, -50%) translate(${Z}px, ${W-12}px)`},children:se})})]})}function tKn(g){return g?g.kind==="manual_call"?g.call||"call":g.kind==="object_topology"||g.kind==="application_target"?"":g.sourceVariable&&g.targetVariable?g.sourceVariable===g.targetVariable?g.sourceVariable:`${g.sourceVariable} → ${g.targetVariable}`:g.kind.replaceAll("_"," "):""}function sue(g){throw new Error('Could not dynamically require "'+g+'". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.')}var K7e={exports:{}},Z1n;function iKn(){return Z1n||(Z1n=1,(function(g,E){(function(x){g.exports=x()})(function(){return(function(){function x(M,N,P){function k(G,ie){if(!N[G]){if(!M[G]){var Z=typeof sue=="function"&&sue;if(!ie&&Z)return Z(G,!0);if(H)return H(G,!0);var W=new Error("Cannot find module '"+G+"'");throw W.code="MODULE_NOT_FOUND",W}var se=N[G]={exports:{}};M[G][0].call(se.exports,function(le){var ee=M[G][1][le];return k(ee||le)},se,se.exports,x,M,N,P)}return N[G].exports}for(var H=typeof sue=="function"&&sue,U=0;U0&&arguments[0]!==void 0?arguments[0]:{},ee=le.defaultLayoutOptions,Oe=ee===void 0?{}:ee,ge=le.algorithms,Pe=ge===void 0?["layered","stress","mrtree","radial","force","disco","sporeOverlap","sporeCompaction","rectpacking"]:ge,ae=le.workerFactory,Me=le.workerUrl;if(k(this,W),this.defaultLayoutOptions=Oe,this.initialized=!1,typeof Me>"u"&&typeof ae>"u")throw new Error("Cannot construct an ELK without both 'workerUrl' and 'workerFactory'.");var Be=ae;typeof Me<"u"&&typeof ae>"u"&&(Be=function(xn){return new Worker(xn)});var rn=Be(Me);if(typeof rn.postMessage!="function")throw new TypeError("Created worker does not provide the required 'postMessage' function.");this.worker=new Z(rn),this.worker.postMessage({cmd:"register",algorithms:Pe}).then(function(ln){return se.initialized=!0}).catch(console.err)}return U(W,[{key:"layout",value:function(le){var ee=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},Oe=ee.layoutOptions,ge=Oe===void 0?this.defaultLayoutOptions:Oe,Pe=ee.logging,ae=Pe===void 0?!1:Pe,Me=ee.measureExecutionTime,Be=Me===void 0?!1:Me;return le?this.worker.postMessage({cmd:"layout",graph:le,layoutOptions:ge,options:{logging:ae,measureExecutionTime:Be}}):Promise.reject(new Error("Missing mandatory parameter 'graph'."))}},{key:"knownLayoutAlgorithms",value:function(){return this.worker.postMessage({cmd:"algorithms"})}},{key:"knownLayoutOptions",value:function(){return this.worker.postMessage({cmd:"options"})}},{key:"knownLayoutCategories",value:function(){return this.worker.postMessage({cmd:"categories"})}},{key:"terminateWorker",value:function(){this.worker&&this.worker.terminate()}}])})();var Z=(function(){function W(se){var le=this;if(k(this,W),se===void 0)throw new Error("Missing mandatory parameter 'worker'.");this.resolvers={},this.worker=se,this.worker.onmessage=function(ee){setTimeout(function(){le.receive(le,ee)},0)}}return U(W,[{key:"postMessage",value:function(le){var ee=this.id||0;this.id=ee+1,le.id=ee;var Oe=this;return new Promise(function(ge,Pe){Oe.resolvers[ee]=function(ae,Me){ae?(Oe.convertGwtStyleError(ae),Pe(ae)):ge(Me)},Oe.worker.postMessage(le)})}},{key:"receive",value:function(le,ee){var Oe=ee.data,ge=le.resolvers[Oe.id];ge&&(delete le.resolvers[Oe.id],Oe.error?ge(Oe.error):ge(null,Oe.data))}},{key:"terminate",value:function(){this.worker&&this.worker.terminate()}},{key:"convertGwtStyleError",value:function(le){if(le){var ee=le.__java$exception;ee&&(ee.cause&&ee.cause.backingJsObject&&(le.cause=ee.cause.backingJsObject,this.convertGwtStyleError(le.cause)),delete le.__java$exception)}}}])})()},{}],2:[function(x,M,N){(function(P){(function(){var k;typeof window<"u"?k=window:typeof P<"u"?k=P:typeof self<"u"&&(k=self);var H;function U(){}function G(){}function ie(){}function Z(){}function W(){}function se(){}function le(){}function ee(){}function Oe(){}function ge(){}function Pe(){}function ae(){}function Me(){}function Be(){}function rn(){}function ln(){}function xn(){}function hn(){}function wt(){}function Cn(){}function Qn(){}function Y(){}function Fe(){}function mn(){}function Ae(){}function Ze(){}function sn(){}function Sn(){}function kn(){}function xe(){}function un(){}function rt(){}function lt(){}function Bt(){}function hi(){}function Gt(){}function At(){}function st(){}function Wr(){}function Mr(){}function ur(){}function mi(){}function Fi(){}function fu(){}function Eu(){}function Ws(){}function Dh(){}function ef(){}function ch(){}function kc(){}function cd(){}function jb(){}function Rs(){}function c0(){}function u0(){}function o0(){}function Bg(){}function r6(){}function zg(){}function c6(){}function d1(){}function ud(){}function Sf(){}function b1(){}function xf(){}function l5(){}function f5(){}function a5(){}function Fg(){}function Eb(){}function Jg(){}function _u(){}function Hg(){}function Gg(){}function u6(){}function h5(){}function Wm(){}function qg(){}function o6(){}function Ug(){}function Zm(){}function d5(){}function Mt(){}function Tc(){}function dc(){}function s6(){}function Af(){}function Zs(){}function Ca(){}function Xg(){}function b5(){}function ek(){}function MA(){}function nk(){}function e3(){}function l6(){}function xp(){}function Ap(){}function Mp(){}function Tp(){}function xl(){}function g5(){}function tk(){}function Kg(){}function Cp(){}function TA(){}function f6(){}function ik(){}function rk(){}function n3(){}function w5(){}function s0(){}function Ih(){}function ck(){}function CA(){}function p5(){}function uk(){}function t3(){}function OA(){}function _h(){}function i3(){}function ok(){}function a6(){}function Vg(){}function vI(){}function yI(){}function m5(){}function uq(){}function kI(){}function jI(){}function NA(){}function oq(){}function sq(){}function sk(){}function Yg(){}function DA(){}function IA(){}function v5(){}function y5(){}function EI(){}function _A(){}function SI(){}function h6(){}function Qg(){}function LA(){}function d6(){}function Op(){}function PA(){}function lk(){}function xI(){}function fk(){}function ak(){}function AI(){}function Lh(){}function r3(){}function hk(){}function b6(){}function lq(){}function $A(){}function RA(){}function g6(){}function dk(){}function MI(){}function fq(){}function aq(){}function hq(){}function BA(){}function dq(){}function bq(){}function gq(){}function wq(){}function pq(){}function TI(){}function mq(){}function vq(){}function yq(){}function kq(){}function zA(){}function jq(){}function Eq(){}function Sq(){}function CI(){}function xq(){}function Aq(){}function Mq(){}function Tq(){}function Cq(){}function Oq(){}function Nq(){}function Dq(){}function Iq(){}function FA(){}function w6(){}function _q(){}function OI(){}function NI(){}function DI(){}function II(){}function _I(){}function k5(){}function Lq(){}function Pq(){}function $q(){}function LI(){}function PI(){}function p6(){}function m6(){}function Rq(){}function bk(){}function $I(){}function JA(){}function HA(){}function GA(){}function RI(){}function BI(){}function zI(){}function Bq(){}function zq(){}function Fq(){}function Jq(){}function Hq(){}function g1(){}function v6(){}function FI(){}function JI(){}function HI(){}function GI(){}function qA(){}function Gq(){}function j5(){}function UA(){}function y6(){}function XA(){}function qI(){}function c3(){}function E5(){}function KA(){}function UI(){}function u3(){}function XI(){}function KI(){}function VI(){}function qq(){}function Uq(){}function Xq(){}function YI(){}function QI(){}function VA(){}function l0(){}function gk(){}function od(){}function S5(){}function YA(){}function wk(){}function pk(){}function QA(){}function o3(){}function WI(){}function mk(){}function x5(){}function Kq(){}function w1(){}function WA(){}function Wg(){}function ZI(){}function vk(){}function s3(){}function ZA(){}function e_(){}function eM(){}function n_(){}function sd(){}function A5(){}function M5(){}function yk(){}function k6(){}function ld(){}function fd(){}function Np(){}function Sb(){}function xb(){}function Zg(){}function t_(){}function nM(){}function tM(){}function i_(){}function ra(){}function Jo(){}function cu(){}function Dp(){}function ad(){}function iM(){}function Ip(){}function r_(){}function c_(){}function T5(){}function l3(){}function C5(){}function _p(){}function rM(){}function Lp(){}function ew(){}function Pp(){}function nw(){}function cM(){}function uM(){}function O5(){}function j6(){}function $p(){}function ca(){}function E6(){}function oM(){}function Vq(){}function Yq(){}function S6(){}function Al(){}function sM(){}function x6(){}function A6(){}function lM(){}function N5(){}function D5(){}function Qq(){}function u_(){}function Wq(){}function o_(){}function f3(){}function fM(){}function kk(){}function s_(){}function I5(){}function aM(){}function jk(){}function Ek(){}function l_(){}function f_(){}function a3(){}function h3(){}function a_(){}function hM(){}function _5(){}function M6(){}function Sk(){}function T6(){}function xk(){}function h_(){}function d3(){}function d_(){}function Rp(){}function dM(){}function bM(){}function Bp(){}function zp(){}function C6(){}function gM(){}function wM(){}function O6(){}function N6(){}function b_(){}function g_(){}function L5(){}function Ak(){}function w_(){}function pM(){}function mM(){}function p1(){}function hd(){}function Fp(){}function vM(){}function p_(){}function Jp(){}function m1(){}function el(){}function Mk(){}function tw(){}function bc(){}function vo(){}function Ml(){}function Tk(){}function P5(){}function b3(){}function Ck(){}function D6(){}function $5(){}function Zq(){}function Bs(){}function yM(){}function kM(){}function m_(){}function v_(){}function eU(){}function jM(){}function EM(){}function SM(){}function uh(){}function nl(){}function Ok(){}function I6(){}function Nk(){}function xM(){}function iw(){}function Dk(){}function AM(){}function MM(){}function y_(){}function k_(){}function j_(){}function nU(){}function E_(){}function S_(){}function TM(){}function x_(){}function tU(){}function A_(){}function M_(){}function T_(){}function CM(){}function C_(){}function O_(){}function N_(){}function D_(){}function I_(){}function iU(){}function __(){}function R5(){}function L_(){}function Ik(){}function _k(){}function P_(){}function OM(){}function rU(){}function $_(){}function R_(){}function B_(){}function z_(){}function F_(){}function NM(){}function J_(){}function H_(){}function DM(){}function G_(){}function q_(){}function IM(){}function _6(){}function U_(){}function Lk(){}function _M(){}function X_(){}function K_(){}function cU(){}function uU(){}function V_(){}function L6(){}function LM(){}function Pk(){}function Y_(){}function PM(){}function P6(){}function oU(){}function $M(){}function Q_(){}function RM(){}function BM(){}function W_(){}function Z_(){}function g3(){}function eL(){}function dd(){}function nL(){}function f0(){}function zM(){}function FM(){}function tL(){}function iL(){}function sU(){}function JM(){}function Tl(){}function ua(){}function rL(){}function cL(){}function uL(){}function oL(){}function $6(){}function sL(){}function $k(){}function lL(){}function lU(){}function Rk(){}function HM(){}function fL(){}function aL(){}function Je(){}function GM(){}function qM(){}function UM(){}function hL(){}function XM(){}function Bk(){}function KM(){}function dL(){}function VM(){}function bL(){}function rw(){}function R6(){}function fU(){}function gL(){}function a0(){}function YM(){}function wL(){}function zk(){}function w3(){}function yo(){}function Fk(){}function aU(){}function QM(){}function B6(){}function Hp(){}function z6(){}function pL(){}function F6(){}function Ab(){}function J6(){}function WM(){}function mL(){}function ZM(){}function eT(){}function p3(){}function vL(){}function Mb(){}function Cl(){}function H6(){}function nT(){}function Mf(){}function hU(){}function yL(){}function kL(){}function Ss(){}function Ph(){}function cw(){}function jL(){}function EL(){}function SL(){}function dU(){}function Jk(){}function $h(){}function h0(){}function xL(){}function Ol(){}function Hk(){}function uw(){}function m3(){}function ow(){}function tT(){}function iT(){}function d0(){}function AL(){}function B5(){}function G6(){}function q6(){}function z5(){}function ML(){}function TL(){}function U6(){}function CL(){}function Gk(){}function OL(){}function bU(){}function gU(){}function Ju(){}function Io(){}function Hc(){}function nu(){}function io(){}function v1(){}function Gp(){}function F5(){}function rT(){}function sw(){}function zs(){}function qp(){}function v3(){}function cT(){}function y1(){}function J5(){}function X6(){}function Rh(){}function uT(){}function qk(){}function NL(){}function Uk(){}function Xk(){}function Up(){}function nf(){}function Xp(){}function H5(){}function lw(){}function oT(){}function sT(){}function DL(){}function K6(){}function lT(){}function k1(){}function IL(){}function Bh(){}function _L(){}function LL(){}function wU(){}function Kp(){}function Kk(){}function fT(){}function G5(){}function PL(){}function $L(){}function RL(){}function BL(){}function Vk(){}function aT(){}function pU(){}function mU(){}function vU(){}function zL(){}function FL(){}function q5(){}function Yk(){}function JL(){}function HL(){}function GL(){}function qL(){}function UL(){}function XL(){}function Qk(){}function KL(){}function VL(){}function ro(){}function hT(){}function yU(){}function YL(){}function kU(){}function jU(){}function EU(){}function Wk(){}function U5(){}function dT(){}function Zk(){}function bT(){}function Vp(){}function Tb(){}function V6(){}function SU(){}function QL(){}function gT(){}function WL(){}function ZL(){}function wT(){mj()}function pT(){T0e()}function eP(){Gf()}function nP(){Rde()}function xU(){PO()}function mT(){OC()}function vT(){iC()}function AU(){tC()}function MU(){TMe()}function Y6(){z4()}function TU(){i$e()}function tP(){n8()}function Gc(){F0()}function yT(){Bhe()}function ej(){X_e()}function kT(){Rhe()}function iP(){V_e()}function jT(){K_e()}function Q6(){Y_e()}function nj(){L$e()}function CU(){Q_e()}function rP(){ABe()}function OU(){Ne()}function NU(){QP()}function cP(){SBe()}function uP(){xBe()}function ko(){VLe()}function ET(){Ige()}function oa(){MBe()}function DU(){Z_e()}function oP(){R4()}function IU(){BFe()}function ST(){P1()}function xT(){cbe()}function tj(){FO()}function sP(){VBe()}function lP(){Gbe()}function AT(){THe()}function MT(){W_e()}function _U(){QXe()}function fP(){Mu()}function LU(){Ja()}function aP(){nge()}function PU(){J0()}function $U(){IW()}function Yp(){JQ()}function hP(){tB()}function TT(){Xt()}function CT(){Ez()}function RU(){$B()}function BU(){bde()}function OT(){Gz()}function ij(){FY()}function tl(){INe()}function zU(){rge()}function j1(e){_n(e)}function NT(e){this.a=e}function W6(e){this.a=e}function dP(e){this.a=e}function bP(e){this.a=e}function Z6(e){this.a=e}function E1(e){this.a=e}function DT(e){this.a=e}function rj(e){this.a=e}function b0(e){this.a=e}function FU(e){this.a=e}function JU(e){this.a=e}function X5(e){this.a=e}function gP(e){this.a=e}function HU(e){this.c=e}function GU(e){this.a=e}function IT(e){this.a=e}function qU(e){this.a=e}function UU(e){this.a=e}function XU(e){this.a=e}function _T(e){this.a=e}function wP(e){this.a=e}function K5(e){this.a=e}function y3(e){this.a=e}function pP(e){this.a=e}function LT(e){this.a=e}function V5(e){this.a=e}function e9(e){this.a=e}function mP(e){this.a=e}function cj(e){this.a=e}function PT(e){this.a=e}function $T(e){this.a=e}function uj(e){this.a=e}function vP(e){this.a=e}function yP(e){this.a=e}function KU(e){this.a=e}function kP(e){this.a=e}function VU(e){this.a=e}function RT(e){this.a=e}function YU(e){this.a=e}function n9(e){this.a=e}function t9(e){this.a=e}function k3(e){this.a=e}function i9(e){this.a=e}function Y5(e){this.b=e}function bd(){this.a=[]}function jP(e,n){e.a=n}function QU(e,n){e.a=n}function WU(e,n){e.b=n}function ZU(e,n){e.c=n}function EP(e,n){e.c=n}function eX(e,n){e.d=n}function nX(e,n){e.d=n}function Tf(e,n){e.k=n}function SP(e,n){e.j=n}function Jue(e,n){e.c=n}function oj(e,n){e.c=n}function sj(e,n){e.a=n}function BT(e,n){e.a=n}function xP(e,n){e.f=n}function tX(e,n){e.a=n}function AP(e,n){e.b=n}function Cb(e,n){e.d=n}function g0(e,n){e.i=n}function fw(e,n){e.o=n}function lj(e,n){e.r=n}function fj(e,n){e.a=n}function j3(e,n){e.b=n}function iX(e,n){e.e=n}function rX(e,n){e.f=n}function Q5(e,n){e.g=n}function Hue(e,n){e.e=n}function cX(e,n){e.f=n}function zT(e,n){e.f=n}function aj(e,n){e.a=n}function FT(e,n){e.b=n}function JT(e,n){e.n=n}function HT(e,n){e.a=n}function uX(e,n){e.c=n}function r9(e,n){e.c=n}function oX(e,n){e.c=n}function MP(e,n){e.a=n}function GT(e,n){e.a=n}function sX(e,n){e.d=n}function Gue(e,n){e.d=n}function qT(e,n){e.e=n}function a(e,n){e.e=n}function d(e,n){e.g=n}function w(e,n){e.f=n}function j(e,n){e.j=n}function C(e,n){e.a=n}function D(e,n){e.a=n}function Q(e,n){e.b=n}function de(e){e.b=e.a}function en(e){e.c=e.d.d}function Ln(e){this.a=e}function nt(e){this.a=e}function gt(e){this.a=e}function Jn(e){this.a=e}function Zn(e){this.a=e}function Di(e){this.a=e}function Tr(e){this.a=e}function co(e){this.a=e}function En(e){this.a=e}function on(e){this.a=e}function In(e){this.a=e}function ct(e){this.a=e}function Ji(e){this.a=e}function Lu(e){this.a=e}function Ui(e){this.b=e}function Hr(e){this.b=e}function tc(e){this.b=e}function qc(e){this.d=e}function xt(e){this.a=e}function lX(e){this.a=e}function que(e){this.a=e}function _ke(e){this.a=e}function Lke(e){this.a=e}function Uue(e){this.a=e}function Xue(e){this.a=e}function fX(e){this.c=e}function L(e){this.c=e}function Pke(e){this.c=e}function Kue(e){this.a=e}function Vue(e){this.a=e}function Yue(e){this.a=e}function Que(e){this.a=e}function c9(e){this.a=e}function $ke(e){this.a=e}function Rke(e){this.a=e}function u9(e){this.a=e}function Bke(e){this.a=e}function zke(e){this.a=e}function Fke(e){this.a=e}function Jke(e){this.a=e}function Hke(e){this.a=e}function Gke(e){this.a=e}function qke(e){this.a=e}function Uke(e){this.a=e}function Xke(e){this.a=e}function Kke(e){this.a=e}function Vke(e){this.a=e}function hj(e){this.a=e}function Yke(e){this.a=e}function Qke(e){this.a=e}function TP(e){this.a=e}function Wke(e){this.a=e}function Zke(e){this.a=e}function Wue(e){this.a=e}function eje(e){this.a=e}function nje(e){this.a=e}function tje(e){this.a=e}function Zue(e){this.a=e}function eoe(e){this.a=e}function noe(e){this.a=e}function dj(e){this.a=e}function o9(e){this.a=e}function ije(e){this.a=e}function W5(e){this.a=e}function toe(e){this.a=e}function rje(e){this.a=e}function cje(e){this.a=e}function uje(e){this.a=e}function oje(e){this.a=e}function sje(e){this.a=e}function lje(e){this.a=e}function fje(e){this.a=e}function aje(e){this.a=e}function hje(e){this.a=e}function dje(e){this.a=e}function bje(e){this.a=e}function ioe(e){this.a=e}function gje(e){this.a=e}function wje(e){this.a=e}function pje(e){this.a=e}function mje(e){this.a=e}function vje(e){this.a=e}function yje(e){this.a=e}function kje(e){this.a=e}function jje(e){this.a=e}function Eje(e){this.a=e}function Sje(e){this.a=e}function xje(e){this.a=e}function Aje(e){this.a=e}function Mje(e){this.a=e}function Tje(e){this.a=e}function Cje(e){this.a=e}function Oje(e){this.a=e}function Nje(e){this.a=e}function Dje(e){this.a=e}function Ije(e){this.a=e}function _je(e){this.a=e}function Lje(e){this.a=e}function Pje(e){this.a=e}function $je(e){this.a=e}function Rje(e){this.a=e}function Bje(e){this.a=e}function zje(e){this.a=e}function Fje(e){this.a=e}function Jje(e){this.a=e}function Hje(e){this.a=e}function Gje(e){this.a=e}function qje(e){this.a=e}function Uje(e){this.a=e}function Xje(e){this.a=e}function Kje(e){this.a=e}function Vje(e){this.a=e}function Yje(e){this.a=e}function Qje(e){this.b=e}function Wje(e){this.a=e}function Zje(e){this.a=e}function eEe(e){this.a=e}function nEe(e){this.a=e}function tEe(e){this.a=e}function iEe(e){this.a=e}function rEe(e){this.c=e}function cEe(e){this.a=e}function uEe(e){this.a=e}function oEe(e){this.a=e}function sEe(e){this.a=e}function lEe(e){this.a=e}function fEe(e){this.a=e}function aEe(e){this.a=e}function hEe(e){this.a=e}function dEe(e){this.a=e}function bEe(e){this.a=e}function gEe(e){this.a=e}function wEe(e){this.a=e}function pEe(e){this.a=e}function mEe(e){this.a=e}function vEe(e){this.a=e}function yEe(e){this.a=e}function kEe(e){this.a=e}function jEe(e){this.a=e}function EEe(e){this.a=e}function SEe(e){this.a=e}function xEe(e){this.a=e}function AEe(e){this.a=e}function MEe(e){this.a=e}function TEe(e){this.a=e}function CEe(e){this.a=e}function OEe(e){this.a=e}function NEe(e){this.a=e}function S1(e){this.a=e}function E3(e){this.a=e}function DEe(e){this.a=e}function IEe(e){this.a=e}function _Ee(e){this.a=e}function LEe(e){this.a=e}function PEe(e){this.a=e}function $Ee(e){this.a=e}function REe(e){this.a=e}function BEe(e){this.a=e}function zEe(e){this.a=e}function FEe(e){this.a=e}function JEe(e){this.a=e}function HEe(e){this.a=e}function GEe(e){this.a=e}function qEe(e){this.a=e}function UEe(e){this.a=e}function XEe(e){this.a=e}function KEe(e){this.a=e}function VEe(e){this.a=e}function YEe(e){this.a=e}function QEe(e){this.a=e}function WEe(e){this.a=e}function ZEe(e){this.a=e}function eSe(e){this.a=e}function nSe(e){this.a=e}function tSe(e){this.a=e}function iSe(e){this.a=e}function CP(e){this.a=e}function rSe(e){this.f=e}function cSe(e){this.a=e}function uSe(e){this.a=e}function oSe(e){this.a=e}function sSe(e){this.a=e}function lSe(e){this.a=e}function fSe(e){this.a=e}function aSe(e){this.a=e}function hSe(e){this.a=e}function dSe(e){this.a=e}function bSe(e){this.a=e}function gSe(e){this.a=e}function wSe(e){this.a=e}function pSe(e){this.a=e}function mSe(e){this.a=e}function vSe(e){this.a=e}function ySe(e){this.a=e}function kSe(e){this.a=e}function jSe(e){this.a=e}function ESe(e){this.a=e}function SSe(e){this.a=e}function xSe(e){this.a=e}function ASe(e){this.a=e}function MSe(e){this.a=e}function TSe(e){this.a=e}function CSe(e){this.a=e}function OSe(e){this.a=e}function NSe(e){this.a=e}function aX(e){this.a=e}function roe(e){this.a=e}function yi(e){this.b=e}function DSe(e){this.a=e}function ISe(e){this.a=e}function _Se(e){this.a=e}function LSe(e){this.a=e}function PSe(e){this.a=e}function $Se(e){this.a=e}function RSe(e){this.a=e}function BSe(e){this.b=e}function zSe(e){this.a=e}function UT(e){this.a=e}function FSe(e){this.a=e}function JSe(e){this.a=e}function OP(e){this.a=e}function NP(e){this.a=e}function coe(e){this.c=e}function DP(e){this.e=e}function IP(e){this.e=e}function hX(e){this.a=e}function HSe(e){this.d=e}function GSe(e){this.a=e}function uoe(e){this.a=e}function ooe(e){this.a=e}function aw(e){this.e=e}function ebn(){this.a=0}function Ce(){MK(this)}function pt(){Hu(this)}function dX(){_Ie(this)}function qSe(){}function hw(){this.c=Q8e}function USe(e,n){e.b+=n}function nbn(e,n){n.Wb(e)}function tbn(e){return e.a}function ibn(e){return e.a}function rbn(e){return e.a}function cbn(e){return e.a}function ubn(e){return e.a}function $(e){return e.e}function obn(){return null}function sbn(){return null}function lbn(e){throw $(e)}function Z5(e){this.a=Nt(e)}function XSe(){this.a=this}function Ob(){aOe.call(this)}function fbn(e){e.b.Mf(e.e)}function KSe(e){e.b=new OX}function bj(e,n){e.b=n-e.b}function gj(e,n){e.a=n-e.a}function VSe(e,n){n.gd(e.a)}function abn(e,n){xr(n,e)}function Hn(e,n){e.push(n)}function YSe(e,n){e.sort(n)}function hbn(e,n,t){e.Wd(t,n)}function XT(e,n){e.e=n,n.b=e}function dbn(){zoe(),LRn()}function QSe(e){$9(),tte.je(e)}function soe(){Ob.call(this)}function bX(){Ob.call(this)}function loe(){aOe.call(this)}function WSe(){Ob.call(this)}function Nl(){Ob.call(this)}function ZSe(){Ob.call(this)}function KT(){Ob.call(this)}function is(){Ob.call(this)}function e4(){Ob.call(this)}function _t(){Ob.call(this)}function au(){Ob.call(this)}function exe(){Ob.call(this)}function _P(){this.Bb|=256}function nxe(){this.b=new fCe}function foe(){foe=Y,new pt}function Qp(e,n){e.length=n}function LP(e,n){Te(e.a,n)}function bbn(e,n){O0e(e.c,n)}function gbn(e,n){hr(e.b,n)}function s9(e,n){ai(e.e,n)}function wbn(e,n){lz(e.a,n)}function pbn(e,n){gQ(e.a,n)}function n4(e){Mz(e.c,e.b)}function mbn(e,n){e.kc().Nb(n)}function aoe(e){this.a=Tjn(e)}function ar(){this.a=new pt}function txe(){this.a=new pt}function PP(){this.a=new Ce}function gX(){this.a=new Ce}function hoe(){this.a=new Ce}function Nb(){this.a=new XPe}function wX(){this.a=new kMe}function doe(){this.a=new $_e}function boe(){this.a=new iNe}function tf(){this.a=new d1}function goe(){this.a=new Zm}function ixe(){this.a=new wLe}function rxe(){this.a=new Ce}function cxe(){this.a=new Ce}function woe(){this.a=new Ce}function uxe(){this.a=new Ce}function oxe(){this.d=new Ce}function sxe(){this.a=new ar}function lxe(){this.a=new pt}function fxe(){this.b=new pt}function axe(){this.b=new Ce}function poe(){this.e=new Ce}function hxe(){this.a=new Gc}function dxe(){this.d=new Ce}function wj(){qSe.call(this)}function pX(){wj.call(this)}function t4(){qSe.call(this)}function moe(){t4.call(this)}function bxe(){soe.call(this)}function $P(){PP.call(this)}function gxe(){q$.call(this)}function wxe(){woe.call(this)}function pxe(){Ce.call(this)}function mxe(){b_e.call(this)}function vxe(){b_e.call(this)}function yxe(){joe.call(this)}function kxe(){joe.call(this)}function jxe(){joe.call(this)}function Exe(){Eoe.call(this)}function pj(){zk.call(this)}function voe(){zk.call(this)}function xs(){Si.call(this)}function Sxe(){Rxe.call(this)}function xxe(){Rxe.call(this)}function Axe(){pt.call(this)}function Mxe(){pt.call(this)}function Txe(){pt.call(this)}function mX(){yBe.call(this)}function Cxe(){ar.call(this)}function Oxe(){_P.call(this)}function vX(){cle.call(this)}function yoe(){pt.call(this)}function yX(){cle.call(this)}function kX(){pt.call(this)}function Nxe(){pt.call(this)}function koe(){p3.call(this)}function Dxe(){koe.call(this)}function Ixe(){p3.call(this)}function _xe(){gT.call(this)}function joe(){this.a=new ar}function Lxe(){this.a=new pt}function Pxe(){this.a=new Ce}function $xe(){this.j=new Ce}function Eoe(){this.a=new pt}function i4(){this.a=new Si}function Rxe(){this.a=new WM}function Soe(){this.a=new z_}function Bxe(){this.a=new PAe}function mj(){mj=Y,Kne=new G}function jX(){jX=Y,Vne=new Fxe}function EX(){EX=Y,Yne=new zxe}function zxe(){y3.call(this,"")}function Fxe(){y3.call(this,"")}function Jxe(e){YRe.call(this,e)}function Hxe(e){YRe.call(this,e)}function xoe(e){E1.call(this,e)}function Aoe(e){VAe.call(this,e)}function vbn(e){VAe.call(this,e)}function ybn(e){Aoe.call(this,e)}function kbn(e){Aoe.call(this,e)}function jbn(e){Aoe.call(this,e)}function Gxe(e){cY.call(this,e)}function qxe(e){cY.call(this,e)}function Uxe(e){KCe.call(this,e)}function Xxe(e){Xoe.call(this,e)}function vj(e){KP.call(this,e)}function Moe(e){KP.call(this,e)}function Kxe(e){KP.call(this,e)}function hu(e){JDe.call(this,e)}function Vxe(e){hu.call(this,e)}function r4(){i9.call(this,{})}function SX(e){y9(),this.a=e}function Yxe(e){e.b=null,e.c=0}function Ebn(e,n){e.e=n,YUe(e,n)}function Sbn(e,n){e.a=n,PTn(e)}function xX(e,n,t){e.a[n.g]=t}function xbn(e,n,t){iAn(t,e,n)}function Abn(e,n){i2n(n.i,e.n)}function Qxe(e,n){pkn(e).Ad(n)}function Mbn(e,n){return e*e/n}function Wxe(e,n){return e.g-n.g}function Tbn(e,n){e.a.ec().Kc(n)}function Cbn(e){return new k3(e)}function Obn(e){return new y2(e)}function Zxe(){Zxe=Y,vme=new U}function Toe(){Toe=Y,yme=new Be}function RP(){RP=Y,US=new xn}function BP(){BP=Y,Wne=new XCe}function eAe(){eAe=Y,Wen=new wt}function zP(e){n1e(),this.a=e}function AX(e){lV(),this.f=e}function w0(e){lV(),this.f=e}function nAe(e){DNe(),this.a=e}function FP(e){hu.call(this,e)}function jo(e){hu.call(this,e)}function tAe(e){hu.call(this,e)}function MX(e){JDe.call(this,e)}function l9(e){hu.call(this,e)}function Gn(e){hu.call(this,e)}function Uc(e){hu.call(this,e)}function iAe(e){hu.call(this,e)}function c4(e){hu.call(this,e)}function gd(e){hu.call(this,e)}function Su(e){_n(e),this.a=e}function yj(e){$fe(e,e.length)}function Coe(e){return Zb(e),e}function Wp(e){return!!e&&e.b}function Nbn(e){return!!e&&e.k}function Dbn(e){return!!e&&e.j}function kj(e){return e.b==e.c}function Re(e){return _n(e),e}function ne(e){return _n(e),e}function VT(e){return _n(e),e}function Ooe(e){return _n(e),e}function Ibn(e){return _n(e),e}function oh(e){hu.call(this,e)}function wd(e){hu.call(this,e)}function u4(e){hu.call(this,e)}function TX(e){hu.call(this,e)}function zt(e){hu.call(this,e)}function CX(e){dle.call(this,e,0)}function OX(){Sae.call(this,12,3)}function NX(){this.a=Pt(Nt(Co))}function rAe(){throw $(new _t)}function Noe(){throw $(new _t)}function cAe(){throw $(new _t)}function _bn(){throw $(new _t)}function Lbn(){throw $(new _t)}function Pbn(){throw $(new _t)}function JP(){JP=Y,$9()}function pd(){Di.call(this,"")}function jj(){Di.call(this,"")}function p0(){Di.call(this,"")}function o4(){Di.call(this,"")}function Doe(e){jo.call(this,e)}function Ioe(e){jo.call(this,e)}function sh(e){Gn.call(this,e)}function f9(e){Hr.call(this,e)}function uAe(e){f9.call(this,e)}function DX(e){B$.call(this,e)}function $bn(e,n,t){e.c.Cf(n,t)}function Rbn(e,n,t){n.Ad(e.a[t])}function Bbn(e,n,t){n.Ne(e.a[t])}function zbn(e,n){return e.a-n.a}function Fbn(e,n){return e.a-n.a}function Jbn(e,n){return e.a-n.a}function HP(e,n){return vY(e,n)}function B(e,n){return H_e(e,n)}function Hbn(e,n){return n in e.a}function oAe(e){return e.a?e.b:0}function Gbn(e){return e.a?e.b:0}function sAe(e,n){return e.f=n,e}function qbn(e,n){return e.b=n,e}function lAe(e,n){return e.c=n,e}function Ubn(e,n){return e.g=n,e}function _oe(e,n){return e.a=n,e}function Loe(e,n){return e.f=n,e}function Xbn(e,n){return e.f=n,e}function Poe(e,n){return e.e=n,e}function Kbn(e,n){return e.k=n,e}function $oe(e,n){return e.a=n,e}function Vbn(e,n){return e.e=n,e}function Ybn(e,n){e.b=new wc(n)}function fAe(e,n){e._d(n),n.$d(e)}function Qbn(e,n){rl(),n.n.a+=e}function Wbn(e,n){F0(),gu(n,e)}function Roe(e){UIe.call(this,e)}function aAe(e){UIe.call(this,e)}function hAe(){qse.call(this,"")}function dAe(){this.b=0,this.a=0}function bAe(){bAe=Y,ann=OAn()}function Zp(e,n){return e.b=n,e}function GP(e,n){return e.a=n,e}function e2(e,n){return e.c=n,e}function n2(e,n){return e.d=n,e}function t2(e,n){return e.e=n,e}function Boe(e,n){return e.f=n,e}function Ej(e,n){return e.a=n,e}function a9(e,n){return e.b=n,e}function h9(e,n){return e.c=n,e}function qe(e,n){return e.c=n,e}function an(e,n){return e.b=n,e}function Ue(e,n){return e.d=n,e}function Xe(e,n){return e.e=n,e}function Zbn(e,n){return e.f=n,e}function Ke(e,n){return e.g=n,e}function Ve(e,n){return e.a=n,e}function Ye(e,n){return e.i=n,e}function Qe(e,n){return e.j=n,e}function egn(e,n){return n.pg(e)}function ngn(e,n){return e.b-n.b}function tgn(e,n){return e.g-n.g}function ign(e,n){return e.s-n.s}function rgn(e,n){return e?0:n-1}function gAe(e,n){return e?0:n-1}function cgn(e,n){return e?n-1:0}function wAe(e,n){return e.k=n,e}function ugn(e,n){return e.j=n,e}function Kr(){this.a=0,this.b=0}function qP(e){UK.call(this,e)}function m0(e){Nw.call(this,e)}function pAe(e){PV.call(this,e)}function mAe(e){PV.call(this,e)}function vAe(){vAe=Y,Pr=QAn()}function v0(){v0=Y,yan=Fxn()}function zoe(){zoe=Y,Ng=EE()}function d9(){d9=Y,Y8e=Jxn()}function yAe(){yAe=Y,rhn=Hxn()}function Foe(){Foe=Y,zu=ITn()}function sa(e){return e.e&&e.e()}function kAe(e,n){return e.c._b(n)}function jAe(e,n){return yFe(e.b,n)}function EAe(e,n){return Dgn(e.a,n)}function SAe(e,n){e.b=0,O2(e,n)}function ogn(e,n){e.c=n,e.b=!0}function S3(e,n){return e.a+=n,e}function IX(e,n){return e.a+=n,e}function md(e,n){return e.a+=n,e}function dw(e,n){return e.a+=n,e}function Db(e){return M1(e),e.o}function Joe(e){MVe(),KRn(this,e)}function xAe(){throw $(new _t)}function AAe(){throw $(new _t)}function MAe(){throw $(new _t)}function TAe(){throw $(new _t)}function CAe(){throw $(new _t)}function OAe(){throw $(new _t)}function UP(e){this.a=new l4(e)}function vd(e){this.a=new bV(e)}function x3(e,n){for(;e.Pe(n););}function Hoe(e,n){for(;e.zd(n););}function sgn(e,n,t){avn(e.a,n,t)}function Goe(e,n,t){e.splice(n,t)}function lgn(e,n){return GLn(n,e)}function qoe(e,n){return e.d[n.p]}function YT(e){return e.b!=e.d.c}function NAe(e){return e.l|e.m<<22}function _X(e){return e?e.d:null}function fgn(e){return e?e.g:null}function agn(e){return e?e.i:null}function DAe(e,n){return aDn(e,n)}function b9(e){return A0(e),e.a}function IAe(e){e.c?hXe(e):dXe(e)}function _Ae(){this.b=new tS(iye)}function LAe(){this.b=new tS(Qre)}function PAe(){this.b=new tS(Qre)}function $Ae(){this.a=new tS(Rye)}function RAe(){this.a=new tS(s6e)}function XP(e){this.a=0,this.b=e}function BAe(){throw $(new _t)}function zAe(){throw $(new _t)}function FAe(){throw $(new _t)}function JAe(){throw $(new _t)}function HAe(){throw $(new _t)}function GAe(){throw $(new _t)}function qAe(){throw $(new _t)}function UAe(){throw $(new _t)}function XAe(){throw $(new _t)}function KAe(){throw $(new _t)}function hgn(){throw $(new au)}function dgn(){throw $(new au)}function QT(e){this.a=new wMe(e)}function g9(e,n){this.e=e,this.d=n}function Uoe(e,n){this.b=e,this.c=n}function VAe(e){tle(e.dc()),this.c=e}function WT(e,n){R3.call(this,e,n)}function w9(e,n){WT.call(this,e,n)}function YAe(e,n){this.a=e,this.b=n}function QAe(e,n){this.a=e,this.b=n}function WAe(e,n){this.a=e,this.b=n}function ZAe(e,n){this.a=e,this.b=n}function eMe(e,n){this.a=e,this.b=n}function nMe(e,n){this.a=e,this.b=n}function tMe(e,n){this.a=e,this.b=n}function iMe(e,n){this.b=e,this.a=n}function rMe(e,n){this.b=e,this.a=n}function bw(e,n){this.g=e,this.i=n}function cMe(e,n){this.a=e,this.b=n}function uMe(e,n){this.b=e,this.a=n}function oMe(e,n){this.a=e,this.b=n}function sMe(e,n){this.b=e,this.a=n}function KP(e){this.b=u(Nt(e),50)}function VP(e){this.b=u(Nt(e),92)}function Ot(e,n){this.f=e,this.g=n}function LX(e,n){this.a=e,this.b=n}function lMe(e,n){this.a=e,this.f=n}function fMe(e){this.a=u(Nt(e),16)}function Xoe(e){this.a=u(Nt(e),16)}function aMe(e,n){this.b=e,this.c=n}function hMe(e){this.a=u(Nt(e),92)}function bgn(e,n){this.a=e,this.b=n}function dMe(e,n){this.a=e,this.b=n}function bMe(e,n){return so(e.b,n)}function gMe(e,n){return e>n&&n0}function JX(e,n){return ao(e,n)<0}function LMe(e,n){return oV(e.a,n)}function _gn(e,n){R_e.call(this,e,n)}function ise(e){CV(),VMn.call(this,e)}function rse(e){CV(),ise.call(this,e)}function cse(e){rV(),KCe.call(this,e)}function use(e,n){ODe(e,e.length,n)}function rC(e,n){cIe(e,e.length,n)}function Dj(e,n){return e.a.get(n)}function PMe(e,n){return so(e.e,n)}function ose(e){return _n(e),!1}function $Me(){return bAe(),new ann}function cC(e){return at(e.a),e.b}function RMe(e,n){this.b=e,this.a=n}function r$(e,n){this.d=e,this.e=n}function BMe(e,n){this.a=e,this.b=n}function zMe(e,n){this.a=e,this.b=n}function FMe(e,n){this.a=e,this.b=n}function JMe(e,n){this.a=e,this.b=n}function HMe(e,n){this.b=e,this.a=n}function f4(e,n){this.a=e,this.b=n}function c$(e,n){Ot.call(this,e,n)}function HX(e,n){Ot.call(this,e,n)}function GX(e,n){Ot.call(this,e,n)}function qX(e,n){Ot.call(this,e,n)}function UX(e,n){Ot.call(this,e,n)}function u$(e,n){Ot.call(this,e,n)}function o$(e){pn.call(this,e,21)}function GMe(e,n){this.b=e,this.a=n}function sse(e,n){this.b=e,this.a=n}function lse(e,n){this.b=e,this.a=n}function fse(e,n){Ot.call(this,e,n)}function XX(e,n){Ot.call(this,e,n)}function uC(e,n){Ot.call(this,e,n)}function ase(e,n){this.b=e,this.a=n}function v9(e,n){this.c=e,this.d=n}function s$(e,n){Ot.call(this,e,n)}function l$(e,n){Ot.call(this,e,n)}function qMe(e,n){this.e=e,this.d=n}function a4(e,n){Ot.call(this,e,n)}function UMe(e,n){this.a=e,this.b=n}function hse(e,n){Ot.call(this,e,n)}function br(e,n){Ot.call(this,e,n)}function f$(e,n){Ot.call(this,e,n)}function Ij(e,n,t){e.splice(n,0,t)}function Lgn(e,n,t){e.Mb(t)&&n.Ad(t)}function Pgn(e,n,t){n.Ne(e.a.We(t))}function $gn(e,n,t){n.Bd(e.a.Xe(t))}function Rgn(e,n,t){n.Ad(e.a.Kb(t))}function Bgn(e,n){return cs(e.c,n)}function zgn(e,n){return cs(e.e,n)}function XMe(e,n){this.a=e,this.b=n}function KMe(e,n){this.a=e,this.b=n}function VMe(e,n){this.a=e,this.b=n}function YMe(e,n){this.a=e,this.b=n}function QMe(e,n){this.a=e,this.b=n}function WMe(e,n){this.a=e,this.b=n}function ZMe(e,n){this.a=e,this.b=n}function eTe(e,n){this.a=e,this.b=n}function nTe(e,n){this.b=e,this.a=n}function tTe(e,n){this.b=e,this.a=n}function iTe(e,n){this.b=e,this.a=n}function rTe(e,n){this.b=n,this.c=e}function a$(e,n){Ot.call(this,e,n)}function oC(e,n){Ot.call(this,e,n)}function dse(e,n){Ot.call(this,e,n)}function _j(e,n){Ot.call(this,e,n)}function h$(e,n){Ot.call(this,e,n)}function KX(e,n){Ot.call(this,e,n)}function VX(e,n){Ot.call(this,e,n)}function Lj(e,n){Ot.call(this,e,n)}function Pj(e,n){Ot.call(this,e,n)}function bse(e,n){Ot.call(this,e,n)}function A3(e,n){Ot.call(this,e,n)}function YX(e,n){Ot.call(this,e,n)}function $j(e,n){Ot.call(this,e,n)}function gse(e,n){Ot.call(this,e,n)}function c2(e,n){Ot.call(this,e,n)}function QX(e,n){Ot.call(this,e,n)}function WX(e,n){Ot.call(this,e,n)}function ZX(e,n){Ot.call(this,e,n)}function wse(e,n){Ot.call(this,e,n)}function sC(e,n){Ot.call(this,e,n)}function pse(e,n){Ot.call(this,e,n)}function M3(e,n){Ot.call(this,e,n)}function eK(e,n){Ot.call(this,e,n)}function d$(e,n){Ot.call(this,e,n)}function lC(e,n){Ot.call(this,e,n)}function u2(e,n){Ot.call(this,e,n)}function b$(e,n){Ot.call(this,e,n)}function mse(e,n){Ot.call(this,e,n)}function nK(e,n){Ot.call(this,e,n)}function tK(e,n){Ot.call(this,e,n)}function iK(e,n){Ot.call(this,e,n)}function rK(e,n){Ot.call(this,e,n)}function cK(e,n){Ot.call(this,e,n)}function uK(e,n){Ot.call(this,e,n)}function g$(e,n){Ot.call(this,e,n)}function cTe(e,n){this.b=e,this.a=n}function vse(e,n){Ot.call(this,e,n)}function uTe(e,n){this.a=e,this.b=n}function oTe(e,n){this.a=e,this.b=n}function sTe(e,n){this.a=e,this.b=n}function yse(e,n){Ot.call(this,e,n)}function kse(e,n){Ot.call(this,e,n)}function lTe(e,n){this.a=e,this.b=n}function Fgn(e,n){return A9(),n!=e}function oK(e){return FCn(e,e.c),e}function Jgn(e){k.clearTimeout(e)}function jse(e,n){Ot.call(this,e,n)}function Ese(e,n){Ot.call(this,e,n)}function fTe(e,n){this.a=e,this.b=n}function aTe(e,n){this.a=e,this.b=n}function hTe(e,n){this.b=e,this.d=n}function dTe(e,n){this.a=e,this.b=n}function bTe(e,n){this.b=e,this.a=n}function w$(e,n){Ot.call(this,e,n)}function gw(e,n){Ot.call(this,e,n)}function sK(e,n){Ot.call(this,e,n)}function p$(e,n){Ot.call(this,e,n)}function Sse(e,n){Ot.call(this,e,n)}function gTe(e,n){this.b=e,this.a=n}function wTe(e,n){this.b=e,this.a=n}function pTe(e,n){this.b=e,this.a=n}function mTe(e,n){this.b=e,this.a=n}function xse(e,n){Ot.call(this,e,n)}function fC(e,n){Ot.call(this,e,n)}function Ase(e,n){Ot.call(this,e,n)}function lK(e,n){Ot.call(this,e,n)}function m$(e,n){Ot.call(this,e,n)}function fK(e,n){Ot.call(this,e,n)}function aK(e,n){Ot.call(this,e,n)}function v$(e,n){Ot.call(this,e,n)}function hK(e,n){Ot.call(this,e,n)}function Mse(e,n){Ot.call(this,e,n)}function dK(e,n){Ot.call(this,e,n)}function bK(e,n){Ot.call(this,e,n)}function aC(e,n){Ot.call(this,e,n)}function gK(e,n){Ot.call(this,e,n)}function Tse(e,n){Ot.call(this,e,n)}function hC(e,n){Ot.call(this,e,n)}function Cse(e,n){Ot.call(this,e,n)}function Ose(e,n){this.a=e,this.b=n}function vTe(e,n){this.a=e,this.b=n}function yTe(e,n){this.a=e,this.b=n}function kTe(){K$(),this.a=new Lle}function jTe(){$z(),this.a=new ar}function ETe(){qV(),this.b=new ar}function STe(){jae(),Afe.call(this)}function xTe(){kae(),d_e.call(this)}function ATe(){kae(),d_e.call(this)}function dC(e,n){Ot.call(this,e,n)}function h4(e,n){Ot.call(this,e,n)}function Rj(e,n){Ot.call(this,e,n)}function Bj(e,n){Ot.call(this,e,n)}function bC(e,n){Ot.call(this,e,n)}function y$(e,n){Ot.call(this,e,n)}function wK(e,n){Ot.call(this,e,n)}function k$(e,n){Ot.call(this,e,n)}function zj(e,n){Ot.call(this,e,n)}function pK(e,n){Ot.call(this,e,n)}function j$(e,n){Ot.call(this,e,n)}function T3(e,n){Ot.call(this,e,n)}function gC(e,n){Ot.call(this,e,n)}function Fj(e,n){Ot.call(this,e,n)}function Jj(e,n){Ot.call(this,e,n)}function mK(e,n){Ot.call(this,e,n)}function wC(e,n){Ot.call(this,e,n)}function E$(e,n){Ot.call(this,e,n)}function C3(e,n){Ot.call(this,e,n)}function vK(e,n){Ot.call(this,e,n)}function yK(e,n){Ot.call(this,e,n)}function S$(e,n){Ot.call(this,e,n)}function je(e,n){this.a=e,this.b=n}function MTe(e,n){this.a=e,this.b=n}function TTe(e,n){this.a=e,this.b=n}function CTe(e,n){this.a=e,this.b=n}function OTe(e,n){this.a=e,this.b=n}function NTe(e,n){this.a=e,this.b=n}function DTe(e,n){this.a=e,this.b=n}function jc(e,n){this.a=e,this.b=n}function ITe(e,n){this.a=e,this.b=n}function _Te(e,n){this.a=e,this.b=n}function LTe(e,n){this.a=e,this.b=n}function PTe(e,n){this.a=e,this.b=n}function $Te(e,n){this.a=e,this.b=n}function RTe(e,n){this.a=e,this.b=n}function BTe(e,n){this.b=e,this.a=n}function zTe(e,n){this.b=e,this.a=n}function FTe(e,n){this.b=e,this.a=n}function JTe(e,n){this.b=e,this.a=n}function HTe(e,n){this.a=e,this.b=n}function GTe(e,n){this.a=e,this.b=n}function qTe(e,n){this.a=e,this.b=n}function UTe(e,n){this.a=e,this.b=n}function XTe(e,n){this.f=e,this.c=n}function Nse(e,n){this.i=e,this.g=n}function x$(e,n){Ot.call(this,e,n)}function d4(e,n){Ot.call(this,e,n)}function A$(e,n){this.a=e,this.b=n}function KTe(e,n){this.a=e,this.b=n}function Dse(e,n){this.d=e,this.e=n}function VTe(e,n){this.a=e,this.b=n}function YTe(e,n){this.a=e,this.b=n}function QTe(e,n){this.d=e,this.b=n}function WTe(e,n){this.e=e,this.a=n}function Ise(e,n){e.i=null,EB(e,n)}function Hgn(e,n){e&&ei(WD,e,n)}function ZTe(e,n){return SQ(e.a,n)}function _se(e,n){return cs(e.g,n)}function Ggn(e,n){return cs(n.b,e)}function qgn(e,n){return-e.b.$e(n)}function M$(e){return OO(e.c,e.b)}function Ugn(e,n){u8n(new ut(e),n)}function Xgn(e,n,t){KHe(n,mW(e,t))}function Kgn(e,n,t){KHe(n,mW(e,t))}function eCe(e,n){V9n(e.a,u(n,12))}function nCe(e,n){this.a=e,this.b=n}function pC(e,n){this.b=e,this.c=n}function j0(e,n){return e.Pd().Xb(n)}function T$(e,n){return v7n(e.Jc(),n)}function du(e){return e?e.kd():null}function ue(e){return e??null}function o2(e){return typeof e===ry}function s2(e){return typeof e===Lge}function $r(e){return typeof e===fZ}function Hj(e,n){return ao(e,n)==0}function C$(e,n){return ao(e,n)>=0}function Gj(e,n){return ao(e,n)!=0}function Lse(e,n){return e.a+=""+n,e}function Vgn(e){return""+(_n(e),e)}function tCe(e){return Ds(e),e.d.gc()}function Pse(e){return vn(e,0),null}function O$(e){return nE(e==null),e}function qj(e,n){return e.a+=""+n,e}function Bc(e,n){return e.a+=""+n,e}function Uj(e,n){return e.a+=""+n,e}function uo(e,n){return e.a+=""+n,e}function Kt(e,n){return e.a+=""+n,e}function iCe(e,n){e.q.setTime(Xb(n))}function rCe(e,n){Ife.call(this,e,n)}function cCe(e,n){Ife.call(this,e,n)}function N$(e,n){Ife.call(this,e,n)}function gc(e,n){Xi(e,n,e.c.b,e.c)}function O3(e,n){Xi(e,n,e.a,e.a.a)}function Ygn(e,n){return e.j[n.p]==2}function uCe(e,n){return e.a=n.g+1,e}function la(e){return e.a=0,e.b=0,e}function oCe(e){Hu(this),xE(this,e)}function sCe(){this.b=0,this.a=!1}function lCe(){this.b=0,this.a=!1}function fCe(){this.b=new l4(I2(12))}function aCe(){aCe=Y,ttn=It(DQ())}function hCe(){hCe=Y,fin=It(FUe())}function dCe(){dCe=Y,tsn=It(oze())}function $se(){$se=Y,foe(),kme=new pt}function Qgn(e){return Nt(e),new Xj(e)}function bCe(e,n){return ue(e)===ue(n)}function D$(e){return e<10?"0"+e:""+e}function gCe(e){return _o(e.l,e.m,e.h)}function uu(e){return typeof e===Lge}function kK(e,n){return of(e.a,0,n)}function b4(e){return sc((_n(e),e))}function Wgn(e){return sc((_n(e),e))}function Zgn(e,n){return ki(e.a,n.a)}function Rse(e,n){return oo(e.a,n.a)}function ewn(e,n){return iIe(e.a,n.a)}function lh(e,n){return e.indexOf(n)}function Bse(e,n){J9(e,0,e.length,n)}function ti(e,n){n$(),ei(kG,e,n)}function fn(e,n){Li.call(this,e,n)}function jK(e,n){b2.call(this,e,n)}function N3(e,n){Nse.call(this,e,n)}function wCe(e,n){jC.call(this,e,n)}function EK(e,n){Y9.call(this,e,n)}function zh(){Uue.call(this,new O0)}function pCe(){fR.call(this,0,0,0,0)}function zse(e){return wu(e.b.b,e,0)}function mCe(e,n){return oo(e.g,n.g)}function nwn(e){return e==op||e==sm}function twn(e){return e==op||e==om}function iwn(e,n){return oo(e.g,n.g)}function rwn(e,n){return rl(),n.a+=e}function cwn(e,n){return rl(),n.a+=e}function uwn(e,n){return rl(),n.c+=e}function own(e,n){return Te(e.c,n),e}function vCe(e,n){return Te(e.a,n),n}function Fse(e,n){return fl(e.a,n),e}function yCe(e){this.a=$Me(),this.b=e}function kCe(e){this.a=$Me(),this.b=e}function wc(e){this.a=e.a,this.b=e.b}function Xj(e){this.a=e,wT.call(this)}function jCe(e){this.a=e,wT.call(this)}function Fs(e){return e.sh()&&e.th()}function D3(e){return e!=nh&&e!=bb}function x1(e){return e==Zc||e==ru}function I3(e){return e==Vl||e==Za}function ECe(e){return e==Fv||e==zv}function I$(e){return fl(new or,e)}function SCe(e){return OV(u(e,125))}function swn(e,n){return ki(n.f,e.f)}function xCe(e,n){return new Y9(n,e)}function lwn(e,n){return new Y9(n,e)}function Dl(e,n,t){Os(e,n),Ns(e,t)}function SK(e,n,t){bB(e,n),gB(e,t)}function ww(e,n,t){Iw(e,n),Dw(e,t)}function mC(e,n,t){X3(e,n),K3(e,t)}function vC(e,n,t){V3(e,n),Y3(e,t)}function xK(e,n){i8(e,n),U9(e,e.D)}function AK(e){XTe.call(this,e,!0)}function g4(){Lf.call(this,0,0,0,0)}function ACe(){c$.call(this,"Head",1)}function MCe(){c$.call(this,"Tail",3)}function TCe(e,n,t){Sle.call(this,e,n,t)}function pw(e){fR.call(this,e,e,e,e)}function E0(e){mh(),x7n.call(this,e)}function CCe(e){Ao(e.Qf(),new Qke(e))}function _3(e){return e!=null?Oi(e):0}function fwn(e,n){return C2(n,Ia(e))}function awn(e,n){return C2(n,Ia(e))}function hwn(e,n){return e[e.length]=n}function dwn(e,n){return e[e.length]=n}function bwn(e,n){return yB(xV(e.f),n)}function gwn(e,n){return yB(xV(e.n),n)}function wwn(e,n){return yB(xV(e.p),n)}function Jse(e){return S3n(e.b.Jc(),e.a)}function pwn(e){return e==null?0:Oi(e)}function MK(e){e.c=oe(Ar,On,1,0,5,1)}function OCe(e,n,t){tr(e.c[n.g],n.g,t)}function mwn(e,n,t){u(e.c,72).Ei(n,t)}function vwn(e,n,t){Dl(t,t.i+e,t.j+n)}function Vr(e,n){Li.call(this,e.b,n)}function ywn(e,n){Et(Vu(e.a),oLe(n))}function kwn(e,n){Et(Cs(e.a),sLe(n))}function jwn(e,n){Ka||(e.b=n)}function TK(e,n,t){return tr(e,n,t),t}function Lt(){Lt=Y,new NCe,new Ce}function NCe(){new pt,new pt,new pt}function Ewn(){throw $(new gd(Pen))}function Swn(){throw $(new gd(Pen))}function xwn(){throw $(new gd($en))}function Awn(){throw $(new gd($en))}function DCe(){DCe=Y,fre=new zE(Ace)}function Oa(){Oa=Y,k.Math.log(2)}function Il(){Il=Y,h1=(NMe(),Aan)}function Kj(e){fi(),aw.call(this,e)}function ICe(e){this.a=e,rfe.call(this,e)}function CK(e){this.a=e,VP.call(this,e)}function OK(e){this.a=e,VP.call(this,e)}function Cr(e,n){uV(e.c,e.c.length,n)}function bu(e){return e.an?1:0}function Gse(e,n){return ao(e,n)>0?e:n}function _o(e,n,t){return{l:e,m:n,h:t}}function Mwn(e,n){e.a!=null&&eCe(n,e.a)}function Twn(e){lc(e,null),Gr(e,null)}function Cwn(e,n,t){return ei(e.g,t,n)}function Own(e,n){Nt(n),z3(e).Ic(new Pe)}function LCe(){Vde(),this.a=new tS(p3e)}function _$(e){this.b=e,this.a=new Ce}function PCe(e){this.b=new o6,this.a=e}function qse(e){Ple.call(this),this.a=e}function $Ce(e){hae.call(this),this.b=e}function RCe(){c$.call(this,"Range",2)}function L$(e){e.j=oe(_me,Se,324,0,0,1)}function BCe(e){e.a=new Bt,e.c=new Bt}function zCe(e){e.a=new pt,e.e=new pt}function Use(e){return new je(e.c,e.d)}function Nwn(e){return new je(e.c,e.d)}function pc(e){return new je(e.a,e.b)}function Dwn(e,n){return ei(e.a,n.a,n)}function Iwn(e,n,t){return ei(e.k,t,n)}function L3(e,n,t){return gde(n,t,e.c)}function Xse(e,n){return re(Bn(e.i,n))}function Kse(e,n){return re(Bn(e.j,n))}function FCe(e,n){return d$n(e.a,n,null)}function Vj(e,n){return EPn(e.c,e.b,n)}function X(e,n){return e!=null&&PQ(e,n)}function JCe(e,n){kt(e),e.Fc(u(n,16))}function _wn(e,n,t){e.c._c(n,u(t,136))}function Lwn(e,n,t){e.c.Si(n,u(t,136))}function Pwn(e,n,t){return a$n(e,n,t),t}function $wn(e,n){return cl(),n.n.b+=e}function NK(e,n){return ekn(e.Jc(),n)!=-1}function Rwn(e,n){return new wOe(e.Jc(),n)}function P$(e){return e.Ob()?e.Pb():null}function HCe(e){return gh(e,0,e.length)}function GCe(e){XV(e,null),KV(e,null)}function qCe(){jC.call(this,null,null)}function UCe(){J$.call(this,null,null)}function XCe(){Ot.call(this,"INSTANCE",0)}function P3(){this.a=oe(Ar,On,1,8,5,1)}function Vse(e){this.a=e,pt.call(this)}function KCe(e){this.a=(jn(),new f9(e))}function Bwn(e){this.b=(jn(),new fX(e))}function y9(){y9=Y,Gme=new SX(null)}function Yse(){Yse=Y,Yse(),bnn=new At}function Te(e,n){return Hn(e.c,n),!0}function VCe(e,n){e.c&&(pfe(n),x_e(n))}function zwn(e,n){e.q.setHours(n),oS(e,n)}function Qse(e,n){return e.a.Ac(n)!=null}function DK(e,n){return e.a.Ac(n)!=null}function Na(e,n){return e.a[n.c.p][n.p]}function Fwn(e,n){return e.e[n.c.p][n.p]}function Jwn(e,n){return e.c[n.c.p][n.p]}function IK(e,n,t){return e.a[n.g][t.g]}function Hwn(e,n){return e.j[n.p]=VOn(n)}function w4(e,n){return e.a*n.a+e.b*n.b}function Gwn(e,n){return e.a=e}function Vwn(e,n,t){return t?n!=0:n!=e-1}function YCe(e,n,t){e.a=n^1502,e.b=t^FZ}function Ywn(e,n,t){return e.a=n,e.b=t,e}function A1(e,n){return e.a*=n,e.b*=n,e}function Yj(e,n,t){return tr(e.g,n,t),t}function Qwn(e,n,t,i){tr(e.a[n.g],t.g,i)}function mr(e,n,t){_C.call(this,e,n,t)}function $$(e,n,t){mr.call(this,e,n,t)}function rs(e,n,t){mr.call(this,e,n,t)}function QCe(e,n,t){$$.call(this,e,n,t)}function Wse(e,n,t){_C.call(this,e,n,t)}function $3(e,n,t){_C.call(this,e,n,t)}function WCe(e,n,t){Z$.call(this,e,n,t)}function Zse(e,n,t){Z$.call(this,e,n,t)}function ZCe(e,n,t){Zse.call(this,e,n,t)}function eOe(e,n,t){Wse.call(this,e,n,t)}function S0(e){this.c=e,this.a=this.c.a}function ut(e){this.i=e,this.f=this.i.j}function R3(e,n){this.a=e,VP.call(this,n)}function nOe(e,n){this.a=e,CX.call(this,n)}function tOe(e,n){this.a=e,CX.call(this,n)}function iOe(e,n){this.a=e,CX.call(this,n)}function ele(e){this.a=e,HU.call(this,e.d)}function rOe(e){e.b.Qb(),--e.d.f.d,hR(e.d)}function cOe(e){e.a=u(Un(e.b.a,4),129)}function uOe(e){e.a=u(Un(e.b.a,4),129)}function Wwn(e){FC(e,lZe),Dz(e,sRn(e))}function nle(e,n){return Cjn(e,new p0,n).a}function Zwn(e){return YT(e.a)?uLe(e):null}function oOe(e){y3.call(this,u(Nt(e),35))}function sOe(e){y3.call(this,u(Nt(e),35))}function tle(e){if(!e)throw $(new KT)}function ile(e){if(!e)throw $(new is)}function Vn(e,n){return Nt(n),new gOe(e,n)}function lOe(e,n){return new WGe(e.a,e.b,n)}function epn(e){return e.l+e.m*sy+e.h*sg}function npn(e){return e==null?null:e.name}function rle(e,n,t){return e.indexOf(n,t)}function R$(e,n){return e.lastIndexOf(n)}function Qj(e){return e==null?Vo:su(e)}function Pn(){Pn=Y,eb=!1,a7=!0}function fOe(){fOe=Y,BX(),nhn=new zU}function cle(){this.Bb|=256,this.Bb|=512}function aOe(){L$(this),MR(this),this.he()}function B$(e){Hr.call(this,e),this.a=e}function ule(e){tc.call(this,e),this.a=e}function ole(e){f9.call(this,e),this.a=e}function cf(e){Di.call(this,(_n(e),e))}function il(e){Di.call(this,(_n(e),e))}function _K(e){Uue.call(this,new lhe(e))}function hOe(e){this.a=e,Ui.call(this,e)}function sle(e,n){this.a=n,CX.call(this,e)}function dOe(e,n){this.a=n,cY.call(this,e)}function bOe(e,n){this.a=e,cY.call(this,n)}function gOe(e,n){this.a=n,KP.call(this,e)}function wOe(e,n){this.a=n,KP.call(this,e)}function lle(e){wX.call(this),fc(this,e)}function Js(e){return at(e.a!=null),e.a}function pOe(e,n){return Te(n.a,e.a),e.a}function mOe(e,n){return Te(n.b,e.a),e.a}function mw(e,n){return Te(n.a,e.a),e.a}function yC(e,n,t){return qY(e,n,n,t),e}function z$(e,n){return++e.b,Te(e.a,n)}function fle(e,n){return++e.b,qo(e.a,n)}function tpn(e,n){return ki(e.c.d,n.c.d)}function ipn(e,n){return ki(e.c.c,n.c.c)}function rpn(e,n){return ki(e.n.a,n.n.a)}function Ho(e,n){return u(pi(e.b,n),16)}function cpn(e,n){return e.n.b=(_n(n),n)}function upn(e,n){return e.n.b=(_n(n),n)}function cs(e,n){return!!n&&e.b[n.g]==n}function Wj(e){return bu(e.a)||bu(e.b)}function opn(e,n){return ki(e.e.b,n.e.b)}function spn(e,n){return ki(e.e.a,n.e.a)}function lpn(e,n,t){return iPe(e,n,t,e.b)}function ale(e,n,t){return iPe(e,n,t,e.c)}function fpn(e){return rl(),!!e&&!e.dc()}function vOe(){Mj(),this.b=new Ije(this)}function F$(){F$=Y,wJ=new Li(QYe,0)}function p4(e){this.d=e,ut.call(this,e)}function m4(e){this.c=e,ut.call(this,e)}function kC(e){this.c=e,p4.call(this,e)}function hle(e,n){Sde.call(this,e,n,null)}function v4(e){return e.a!=null?e.a:null}function vw(e){return e.$H||(e.$H=++NBn)}function jd(e){var n;n=e.a,e.a=e.b,e.b=n}function jC(e,n){Nj(),this.a=e,this.b=n}function J$(e,n){kd(),this.b=e,this.c=n}function H$(e,n){lV(),this.f=n,this.d=e}function dle(e,n){Zae(n,e),this.c=e,this.b=n}function apn(e,n){return hV(e.c).Kd().Xb(n)}function LK(e,n){return new yNe(e,e.gc(),n)}function hpn(e){return BP(),Dt((eLe(),qen),e)}function dpn(e){return new A2(3,e)}function Fh(e){return ll(e,Q2),new xo(e)}function yOe(e){return $9(),parseInt(e)||-1}function k9(e,n,t){return rle(e,Xo(n),t)}function ble(e,n,t){u(cO(e,n),22).Ec(t)}function bpn(e,n,t){gQ(e.a,t),lz(e.a,n)}function j9(e,n,t){var i;i=e.dd(n),i.Rb(t)}function kOe(e,n,t,i){Nfe.call(this,e,n,t,i)}function jOe(e){ufe.call(this,e,null,null)}function PK(e){i2(),this.b=e,this.a=!0}function EOe(e){YP(),this.b=e,this.a=!0}function SOe(e){if(!e)throw $(new Nl)}function gle(e){if(!e)throw $(new KT)}function gpn(e){if(!e)throw $(new bX)}function at(e){if(!e)throw $(new au)}function l2(e){if(!e)throw $(new is)}function xOe(e){e.d=new jOe(e),e.e=new pt}function E9(e){return at(e.b!=0),e.a.a.c}function If(e){return at(e.b!=0),e.c.b.c}function wpn(e,n){return qY(e,n,n+1,""),e}function AOe(e){sZ(),KSe(this),this.Df(e)}function MOe(e){this.c=e,this.a=1,this.b=1}function EC(e){X(e,161)&&u(e,161).mi()}function TOe(e){return e.b=u(oae(e.a),45)}function f2(e,n){return u(Pa(e.a,n),35)}function bi(e,n){return!!e.q&&so(e.q,n)}function ppn(e,n){return e>0?n/(e*e):n*100}function mpn(e,n){return e>0?n*n/e:n*n*100}function vpn(e){return e.f!=null?e.f:""+e.g}function $K(e){return e.f!=null?e.f:""+e.g}function ypn(e){return P1(),e.e.a+e.f.a/2}function kpn(e){return P1(),e.e.b+e.f.b/2}function jpn(e,n,t){return P1(),t.e.b-e*n}function Epn(e,n,t){return P1(),t.e.a-e*n}function Spn(e,n,t){return WP(),t.Lg(e,n)}function xpn(e,n){return F0(),gn(e,n.e,n)}function Apn(e,n,t){return Te(n,WFe(e,t))}function Mpn(e,n,t){tB(),e.nf(n)&&t.Ad(e)}function a2(e,n,t){return e.a+=n,e.b+=t,e}function COe(e,n,t){return e.a-=n,e.b-=t,e}function wle(e,n){return e.a=n.a,e.b=n.b,e}function G$(e){return e.a=-e.a,e.b=-e.b,e}function OOe(e){this.c=e,Os(e,0),Ns(e,0)}function NOe(e){Si.call(this),SE(this,e)}function DOe(){Ot.call(this,"GROW_TREE",0)}function Hs(e,n,t){os.call(this,e,n,t,2)}function IOe(e,n){kd(),ple.call(this,e,n)}function ple(e,n){kd(),J$.call(this,e,n)}function _Oe(e,n){kd(),J$.call(this,e,n)}function LOe(e,n){Nj(),jC.call(this,e,n)}function RK(e,n){Il(),sR.call(this,e,n)}function POe(e,n){Il(),RK.call(this,e,n)}function mle(e,n){Il(),RK.call(this,e,n)}function $Oe(e,n){Il(),mle.call(this,e,n)}function vle(e,n){Il(),sR.call(this,e,n)}function ROe(e,n){Il(),vle.call(this,e,n)}function BOe(e,n){Il(),sR.call(this,e,n)}function Tpn(e,n){return e.c.Ec(u(n,136))}function Cpn(e,n){return u(Bn(e.e,n),26)}function Opn(e,n){return u(Bn(e.e,n),26)}function yle(e,n,t){return Kz(oO(e,n),t)}function Npn(e,n,t){return n.xl(e.e,e.c,t)}function Dpn(e,n,t){return n.yl(e.e,e.c,t)}function BK(e,n){return $0(e.e,u(n,52))}function Ipn(e,n,t){$E(Vu(e.a),n,oLe(t))}function _pn(e,n,t){$E(Cs(e.a),n,sLe(t))}function zOe(e,n){return _n(e),e+qK(n)}function Lpn(e){return e==null?null:su(e)}function Ppn(e){return e==null?null:su(e)}function $pn(e){return e==null?null:cTn(e)}function Rpn(e){return e==null?null:Z$n(e)}function M1(e){e.o==null&&SOn(e)}function $e(e){return nE(e==null||o2(e)),e}function re(e){return nE(e==null||s2(e)),e}function Pt(e){return nE(e==null||$r(e)),e}function Bpn(e,n){return GQ(e,n),new NIe(e,n)}function SC(e,n){this.c=e,g9.call(this,e,n)}function Zj(e,n){this.a=e,SC.call(this,e,n)}function zpn(e,n){this.d=e,en(this),this.b=n}function kle(){yBe.call(this),this.Bb|=Ec}function FOe(){this.a=new Tw,this.b=new Tw}function jle(e){this.q=new k.Date(Xb(e))}function B3(){B3=Y,Gv=new yi("root")}function S9(){S9=Y,eI=new Sxe,new xxe}function h2(){h2=Y,Qme=nn((Vs(),Og))}function Fpn(e,n){n.a?qCn(e,n):DK(e.a,n.b)}function JOe(e,n){Ka||Te(e.a,n)}function Jpn(e,n){return iC(),V9(n.d.i,e)}function Hpn(e,n){return z4(),new $Xe(n,e)}function Gpn(e,n,t){return e.Le(n,t)<=0?t:n}function qpn(e,n,t){return e.Le(n,t)<=0?n:t}function Upn(e,n){return u(Pa(e.b,n),144)}function Xpn(e,n){return u(Pa(e.c,n),233)}function zK(e){return u(Le(e.a,e.b),295)}function HOe(e){return new je(e.c,e.d+e.a)}function GOe(e){return _n(e),e?1231:1237}function qOe(e){return cl(),ECe(u(e,203))}function Ele(e,n){return u(Bn(e.b,n),278)}function UOe(e,n,t){++e.j,e.oj(n,e.Xi(n,t))}function xC(e,n,t){++e.j,e.rj(),bY(e,n,t)}function Sle(e,n,t){ZR.call(this,e,n,t,null)}function XOe(e,n,t){ZR.call(this,e,n,t,null)}function xle(e,n){gY.call(this,e),this.a=n}function Ale(e,n){gY.call(this,e),this.a=n}function Li(e,n){yi.call(this,e),this.a=n}function Mle(e,n){coe.call(this,e),this.a=n}function FK(e,n){coe.call(this,e),this.a=n}function KOe(e,n){this.c=e,Nw.call(this,n)}function VOe(e,n){this.a=e,BSe.call(this,n)}function AC(e,n){this.a=e,BSe.call(this,n)}function Tle(e,n,t){return t=dl(e,n,3,t),t}function Cle(e,n,t){return t=dl(e,n,6,t),t}function Ole(e,n,t){return t=dl(e,n,9,t),t}function fh(e,n){return FC(n,ewe),e.f=n,e}function Nle(e,n){return(n&oi)%e.d.length}function YOe(e,n,t){return bge(e.c,e.b,n,t)}function Kpn(e,n,t){return e.apply(n,t)}function QOe(e,n,t){var i;i=e.dd(n),i.Rb(t)}function WOe(e,n,t){return e.a+=gh(n,0,t),e}function MC(e){return!e.a&&(e.a=new Cn),e.a}function Dle(e,n){var t;return t=e.e,e.e=n,t}function Ile(e,n){var t;return t=n,!!e.De(t)}function Lb(e,n){return Pn(),e==n?0:e?1:-1}function d2(e,n){e.a._c(e.b,n),++e.b,e.c=-1}function Vpn(e,n){var t;t=e[zZ],t.call(e,n)}function Ypn(e,n){var t;t=e[zZ],t.call(e,n)}function Qpn(e,n,t){Ib(),jP(e,n.Te(e.a,t))}function _le(e,n,t){return M4(e,u(n,23),t)}function _f(e,n){return HP(new Array(n),e)}function Wpn(e){return Rt(Bb(e,32))^Rt(e)}function JK(e){return String.fromCharCode(e)}function Zpn(e){return e==null?null:e.message}function HK(e){this.a=(jn(),new In(Nt(e)))}function ZOe(e){this.a=(ll(e,Q2),new xo(e))}function eNe(e){this.a=(ll(e,Q2),new xo(e))}function nNe(){this.a=new Ce,this.b=new Ce}function tNe(){this.a=new Zm,this.b=new nxe}function Lle(){this.b=new O0,this.a=new O0}function iNe(){this.b=new Kr,this.c=new Ce}function Ple(){this.n=new Kr,this.o=new Kr}function q$(){this.n=new t4,this.i=new g4}function rNe(){this.b=new ar,this.a=new ar}function cNe(){this.a=new Ce,this.d=new Ce}function uNe(){this.a=new NU,this.b=new c_}function oNe(){this.b=new _Ae,this.a=new vM}function sNe(){this.b=new pt,this.a=new pt}function lNe(){q$.call(this),this.a=new Kr}function $le(e,n,t,i){fR.call(this,e,n,t,i)}function e2n(e,n){return e.n.a=(_n(n),n+10)}function n2n(e,n){return e.n.a=(_n(n),n+10)}function t2n(e,n){return iC(),!V9(n.d.i,e)}function fNe(e){Hu(e.e),e.d.b=e.d,e.d.a=e.d}function TC(e){e.b?TC(e.b):e.f.c.yc(e.e,e.d)}function i2n(e,n){x1(e.f)?wOn(e,n):sMn(e,n)}function aNe(e,n,t){t!=null&&kB(n,XQ(e,t))}function hNe(e,n,t){t!=null&&jB(n,XQ(e,t))}function y4(e,n,t,i){pe.call(this,e,n,t,i)}function Rle(e,n,t,i){pe.call(this,e,n,t,i)}function dNe(e,n,t,i){Rle.call(this,e,n,t,i)}function bNe(e,n,t,i){mR.call(this,e,n,t,i)}function GK(e,n,t,i){mR.call(this,e,n,t,i)}function gNe(e,n,t,i){GK.call(this,e,n,t,i)}function Ble(e,n,t,i){mR.call(this,e,n,t,i)}function Nn(e,n,t,i){Ble.call(this,e,n,t,i)}function zle(e,n,t,i){GK.call(this,e,n,t,i)}function wNe(e,n,t,i){zle.call(this,e,n,t,i)}function pNe(e,n,t,i){Lfe.call(this,e,n,t,i)}function b2(e,n){jo.call(this,$S+e+dg+n)}function r2n(e,n){return n==e||m8(Nz(n),e)}function Fle(e,n){return e.hk().ti().oi(e,n)}function Jle(e,n){return e.hk().ti().qi(e,n)}function c2n(e,n){return e.e=u(e.d.Kb(n),162)}function mNe(e,n){return ei(e.a,n,"")==null}function vNe(e,n){return _n(e),ue(e)===ue(n)}function bn(e,n){return _n(e),ue(e)===ue(n)}function Hle(e,n,t){return e.lastIndexOf(n,t)}function yNe(e,n,t){this.a=e,dle.call(this,n,t)}function kNe(e){this.c=e,N$.call(this,hN,0)}function jNe(e,n,t){this.c=n,this.b=t,this.a=e}function gi(e,n){return e.a+=n.a,e.b+=n.b,e}function Nr(e,n){return e.a-=n.a,e.b-=n.b,e}function u2n(e){return Qp(e.j.c,0),e.a=-1,e}function o2n(e,n){var t;return t=n.ni(e.a),t}function Gle(e,n,t){return t=dl(e,n,11,t),t}function s2n(e,n,t){return ki(e[n.a],e[t.a])}function l2n(e,n){return oo(e.a.d.p,n.a.d.p)}function f2n(e,n){return oo(n.a.d.p,e.a.d.p)}function a2n(e,n){return ki(e.c-e.s,n.c-n.s)}function h2n(e,n){return ki(e.b.e.a,n.b.e.a)}function d2n(e,n){return ki(e.c.e.a,n.c.e.a)}function b2n(e,n){return he(n,(Ne(),fD),e)}function g2n(e,n){return e.b.zd(new zMe(e,n))}function w2n(e,n){return e.b.zd(new FMe(e,n))}function ENe(e,n){return e.b.zd(new JMe(e,n))}function SNe(e,n){return X(n,16)&&pXe(e.c,n)}function xNe(e){return e.c?wu(e.c.a,e,0):-1}function p2n(e){return e<100?null:new m0(e)}function k4(e){return e==Cg||e==f1||e==to}function m2n(e,n,t){return u(e.c,72).Uk(n,t)}function U$(e,n,t){return u(e.c,72).Vk(n,t)}function v2n(e,n,t){return Npn(e,u(n,344),t)}function qle(e,n,t){return Dpn(e,u(n,344),t)}function y2n(e,n,t){return rGe(e,u(n,344),t)}function ANe(e,n,t){return yMn(e,u(n,344),t)}function eE(e,n){return n==null?null:L2(e.b,n)}function k2n(e,n){Ka||n&&(e.d=n)}function Ule(e,n){if(!e)throw $(new Gn(n))}function x9(e){if(!e)throw $(new Uc(Pge))}function qK(e){return s2(e)?(_n(e),e):e.se()}function X$(e){return!isNaN(e)&&!isFinite(e)}function UK(e){BCe(this),qs(this),fc(this,e)}function bs(e){MK(this),sfe(this.c,0,e.Nc())}function CC(e){A9(),this.d=e,this.a=new P3}function MNe(e,n,t){this.d=e,this.b=t,this.a=n}function _l(e,n,t){this.a=e,this.b=n,this.c=t}function TNe(e,n,t){this.a=e,this.b=n,this.c=t}function Xle(e,n){this.c=e,vV.call(this,e,n)}function CNe(e,n){T3n.call(this,e,e.length,n)}function XK(e,n){if(e!=n)throw $(new Nl)}function ONe(e){this.a=e,yd(),Pu(Date.now())}function NNe(e){As(e.a),uhe(e.c,e.b),e.b=null}function KK(){KK=Y,Hme=new hi,hnn=new Gt}function VK(e){var n;return n=new l5,n.e=e,n}function j2n(e,n,t){return Ib(),e.a.Wd(n,t),n}function Kle(e,n,t){this.b=e,this.c=n,this.a=t}function Vle(e){var n;return n=new oxe,n.b=e,n}function E2n(e){return ga(),Dt((M$e(),Cnn),e)}function S2n(e){return H9(),Dt((F$e(),gnn),e)}function x2n(e){return zl(),Dt((A$e(),knn),e)}function A2n(e){return ws(),Dt((T$e(),Nnn),e)}function M2n(e){return Uo(),Dt((C$e(),Inn),e)}function T2n(e){return Zz(),Dt((aCe(),ttn),e)}function C2n(e){return Lw(),Dt((U$e(),rtn),e)}function O2n(e){return Z9(),Dt((X$e(),Ktn),e)}function N2n(e){return lB(),Dt((IPe(),dtn),e)}function D2n(e){return yE(),Dt((x$e(),Btn),e)}function I2n(e){return zr(),Dt((LRe(),Htn),e)}function _2n(e){return X4(),Dt((q$e(),ein),e)}function L2n(e){return zn(),Dt((ize(),rin),e)}function P2n(e){return K9(),Dt((_Pe(),lin),e)}function YK(e){fR.call(this,e.d,e.c,e.a,e.b)}function Yle(e){fR.call(this,e.d,e.c,e.a,e.b)}function $2n(e){return Ur(),Dt((hCe(),fin),e)}function DNe(){DNe=Y,Nan=oe(Ar,On,1,0,5,1)}function INe(){INe=Y,Van=oe(Ar,On,1,0,5,1)}function Qle(){Qle=Y,Yan=oe(Ar,On,1,0,5,1)}function OC(){OC=Y,jJ=new lq,EJ=new $A}function K$(){K$=Y,gin=new Tq,bin=new Cq}function rl(){rl=Y,yin=new gk,kin=new od}function R2n(e){return _w(),Dt((l$e(),Nin),e)}function B2n(e){return Ff(),Dt((Q$e(),Sin),e)}function z2n(e){return z2(),Dt((CRe(),Ain),e)}function F2n(e){return Bz(),Dt((uze(),Din),e)}function J2n(e){return Q4(),Dt((lBe(),Iin),e)}function H2n(e){return nB(),Dt((wPe(),_in),e)}function G2n(e){return BE(),Dt((Z$e(),Lin),e)}function q2n(e){return pB(),Dt((r$e(),Pin),e)}function U2n(e){return KO(),Dt((hze(),$in),e)}function X2n(e){return fO(),Dt((pPe(),Rin),e)}function K2n(e){return Wb(),Dt((c$e(),zin),e)}function V2n(e){return Sz(),Dt((sBe(),Fin),e)}function Y2n(e){return rO(),Dt((mPe(),Jin),e)}function Q2n(e){return zO(),Dt((uBe(),Hin),e)}function W2n(e){return y8(),Dt((oBe(),Gin),e)}function Z2n(e){return Dc(),Dt((Cze(),qin),e)}function emn(e){return W9(),Dt((u$e(),Uin),e)}function nmn(e){return _0(),Dt((o$e(),Xin),e)}function tmn(e){return _1(),Dt((s$e(),Vin),e)}function imn(e){return JR(),Dt((vPe(),Yin),e)}function rmn(e){return Xs(),Dt((NRe(),Win),e)}function cmn(e){return qR(),Dt((yPe(),Zin),e)}function umn(e){return YO(),Dt((dze(),zun),e)}function omn(e){return IE(),Dt((f$e(),Fun),e)}function smn(e){return B2(),Dt((V$e(),Jun),e)}function lmn(e){return HE(),Dt((ORe(),Hun),e)}function fmn(e){return G0(),Dt((Tze(),Gun),e)}function amn(e){return F1(),Dt((Y$e(),qun),e)}function hmn(e){return uO(),Dt((kPe(),Uun),e)}function dmn(e){return Nc(),Dt((a$e(),Kun),e)}function bmn(e){return DB(),Dt((h$e(),Vun),e)}function gmn(e){return DE(),Dt((d$e(),Yun),e)}function wmn(e){return r8(),Dt((b$e(),Qun),e)}function pmn(e){return wB(),Dt((g$e(),Wun),e)}function mmn(e){return IB(),Dt((w$e(),Zun),e)}function vmn(e){return LB(),Dt((G$e(),din),e)}function ymn(e){return eg(),Dt((K$e(),mon),e)}function kmn(e,n){return _n(e),e+(_n(n),n)}function jmn(e){return mE(),Dt((jPe(),Eon),e)}function Emn(e){return ah(),Dt((SPe(),Oon),e)}function Smn(e){return Da(),Dt((EPe(),Don),e)}function xmn(e){return ha(),Dt((xPe(),Xon),e)}function A9(){A9=Y,nye=(De(),Kn),OH=et}function Amn(e){return Cw(),Dt((APe(),esn),e)}function Mmn(e){return Y4(),Dt((tRe(),nsn),e)}function Tmn(e){return cS(),Dt((dCe(),tsn),e)}function Cmn(e){return NE(),Dt((p$e(),isn),e)}function Omn(e){return OE(),Dt((W$e(),Asn),e)}function Nmn(e){return FR(),Dt((MPe(),Msn),e)}function Dmn(e){return SB(),Dt((TPe(),Dsn),e)}function Imn(e){return vz(),Dt((DRe(),_sn),e)}function _mn(e){return iB(),Dt((CPe(),Lsn),e)}function Lmn(e){return EO(),Dt((m$e(),Psn),e)}function Pmn(e){return az(),Dt((nRe(),tln),e)}function $mn(e){return NB(),Dt((v$e(),iln),e)}function Rmn(e){return WB(),Dt((y$e(),rln),e)}function Bmn(e){return kz(),Dt((eRe(),uln),e)}function zmn(e){return XB(),Dt((S$e(),lln),e)}function Fmn(e){return!e.e&&(e.e=new Ce),e.e}function V$(e,n,t){this.e=n,this.b=e,this.d=t}function _Ne(e,n,t){this.a=e,this.b=n,this.c=t}function LNe(e,n,t){this.a=e,this.b=n,this.c=t}function Wle(e,n,t){this.a=e,this.b=n,this.c=t}function PNe(e,n,t){this.a=e,this.b=n,this.c=t}function $Ne(e,n,t){this.a=e,this.c=n,this.b=t}function Y$(e,n,t){this.b=e,this.a=n,this.c=t}function RNe(e,n,t){this.b=e,this.a=n,this.c=t}function QK(e,n){this.c=e,this.a=n,this.b=n-e}function Jmn(e){return JB(),Dt((j$e(),Iln),e)}function Hmn(e){return e$(),Dt((XLe(),Bln),e)}function Gmn(e){return WC(),Dt((NPe(),zln),e)}function qmn(e){return JO(),Dt((_Re(),Fln),e)}function Umn(e){return ZP(),Dt((ULe(),$ln),e)}function Xmn(e){return nS(),Dt((IRe(),Lln),e)}function Kmn(e){return TO(),Dt((E$e(),Pln),e)}function Vmn(e){return VR(),Dt((OPe(),Nln),e)}function Ymn(e){return rB(),Dt((k$e(),Dln),e)}function Qmn(e){return Tj(),Dt((KLe(),ifn),e)}function Wmn(e){return pO(),Dt((DPe(),rfn),e)}function Zmn(e){return ph(),Dt(($Re(),ffn),e)}function e3n(e){return cg(),Dt((rze(),hfn),e)}function n3n(e){return z1(),Dt((rRe(),Kfn),e)}function t3n(e){return vr(),Dt((PRe(),qfn),e)}function i3n(e){return u8(),Dt((iRe(),Ufn),e)}function r3n(e){return $a(),Dt((O$e(),Xfn),e)}function c3n(e){return Vh(),Dt((tBe(),dfn),e)}function u3n(e){return rg(),Dt((iBe(),vfn),e)}function o3n(e){return G2(),Dt((gze(),ean),e)}function s3n(e){return nv(),Dt((RRe(),nan),e)}function l3n(e){return Br(),Dt((cBe(),tan),e)}function f3n(e){return ps(),Dt((rBe(),ian),e)}function a3n(e){return al(),Dt((cRe(),Zfn),e)}function h3n(e){return jz(),Dt((nBe(),Vfn),e)}function d3n(e){return B1(),Dt((D$e(),Qfn),e)}function b3n(e){return UR(),Dt((uRe(),dan),e)}function g3n(e){return _s(),Dt((bze(),aan),e)}function w3n(e){return G4(),Dt((N$e(),han),e)}function p3n(e){return De(),Dt((BRe(),ran),e)}function m3n(e){return jE(),Dt((I$e(),lan),e)}function v3n(e){return Vs(),Dt((oRe(),fan),e)}function y3n(e){return KB(),Dt((sRe(),ban),e)}function k3n(e){return PB(),Dt((lRe(),pan),e)}function j3n(e){return j8(),Dt((cze(),Oan),e)}function BNe(e,n,t){Il(),bae.call(this,e,n,t)}function WK(e,n,t){Il(),Vfe.call(this,e,n,t)}function zNe(e,n,t){Il(),WK.call(this,e,n,t)}function Zle(e,n,t){Il(),WK.call(this,e,n,t)}function FNe(e,n,t){Il(),Zle.call(this,e,n,t)}function JNe(e,n,t){Il(),efe.call(this,e,n,t)}function efe(e,n,t){Il(),Vfe.call(this,e,n,t)}function nfe(e,n,t){Il(),Vfe.call(this,e,n,t)}function HNe(e,n,t){Il(),nfe.call(this,e,n,t)}function GNe(e,n,t){this.a=e,this.c=n,this.b=t}function qNe(e,n,t){this.a=e,this.b=n,this.c=t}function tfe(e,n,t){this.a=e,this.b=n,this.c=t}function ife(e,n,t){this.a=e,this.b=n,this.c=t}function ZK(e,n,t){this.a=e,this.b=n,this.c=t}function UNe(e,n,t){this.a=e,this.b=n,this.c=t}function Ed(e,n,t){this.e=e,this.a=n,this.c=t}function rfe(e){this.d=e,en(this),this.b=gvn(e.d)}function cfe(e,n){bgn.call(this,e,qB(new Su(n)))}function NC(e,n){return Nt(e),Nt(n),new QAe(e,n)}function j4(e,n){return Nt(e),Nt(n),new iDe(e,n)}function E3n(e,n){return Nt(e),Nt(n),new rDe(e,n)}function S3n(e,n){return Nt(e),Nt(n),new sMe(e,n)}function eV(e){return at(e.b!=0),$l(e,e.a.a)}function x3n(e){return at(e.b!=0),$l(e,e.c.b)}function A3n(e){return!e.c&&(e.c=new Ol),e.c}function DC(e){var n;return n=new Si,RY(n,e),n}function XNe(e){var n;return n=new wX,RY(n,e),n}function M3n(e){var n;return n=new ar,AY(n,e),n}function M9(e){var n;return n=new Ce,AY(n,e),n}function u(e,n){return nE(e==null||PQ(e,n)),e}function T3n(e,n,t){UDe.call(this,n,t),this.a=e}function KNe(e,n){this.c=e,this.b=n,this.a=!1}function VNe(){this.a=";,;",this.b="",this.c=""}function YNe(e,n,t){this.b=e,rCe.call(this,n,t)}function ufe(e,n,t){this.c=e,r$.call(this,n,t)}function ofe(e,n,t){v9.call(this,e,n),this.b=t}function sfe(e,n,t){ebe(t,0,e,n,t.length,!1)}function Jh(e,n,t,i,r){e.b=n,e.c=t,e.d=i,e.a=r}function lfe(e,n,t,i,r){e.d=n,e.c=t,e.a=i,e.b=r}function C3n(e,n){n&&(e.b=n,e.a=(A0(n),n.a))}function IC(e,n){if(!e)throw $(new Gn(n))}function E4(e,n){if(!e)throw $(new Uc(n))}function ffe(e,n){if(!e)throw $(new tAe(n))}function O3n(e,n){return QP(),oo(e.d.p,n.d.p)}function N3n(e,n){return P1(),ki(e.e.b,n.e.b)}function D3n(e,n){return P1(),ki(e.e.a,n.e.a)}function I3n(e,n){return oo(lDe(e.d),lDe(n.d))}function Q$(e,n){return n&&ER(e,n.d)?n:null}function _3n(e,n){return n==(De(),Kn)?e.c:e.d}function L3n(e){return new je(e.c+e.b,e.d+e.a)}function QNe(e){return e!=null&&!kQ(e,uA,oA)}function P3n(e,n){return(IFe(e)<<4|IFe(n))&yr}function WNe(e,n,t,i,r){e.c=n,e.d=t,e.b=i,e.a=r}function afe(e){var n,t;n=e.b,t=e.c,e.b=t,e.c=n}function hfe(e){var n,t;t=e.d,n=e.a,e.d=n,e.a=t}function $3n(e,n){var t;return t=e.c,Jhe(e,n),t}function dfe(e,n){return n<0?e.g=-1:e.g=n,e}function W$(e,n){return T8n(e),e.a*=n,e.b*=n,e}function _C(e,n,t){Dse.call(this,e,n),this.c=t}function Z$(e,n,t){Dse.call(this,e,n),this.c=t}function bfe(e){Qle(),p3.call(this),this._h(e)}function ZNe(){z9(),Vvn.call(this,(y0(),kf))}function eDe(e){return fi(),new Hh(0,e)}function nDe(){nDe=Y,Hce=(jn(),new In(Rne))}function eR(){eR=Y,new Mde((EX(),Yne),(jX(),Vne))}function tDe(){this.b=ne(re(_e((Gf(),kte))))}function nV(e){this.b=e,this.a=$b(this.b.a).Md()}function iDe(e,n){this.b=e,this.a=n,wT.call(this)}function rDe(e,n){this.a=e,this.b=n,wT.call(this)}function cDe(e,n,t){this.a=e,N3.call(this,n,t)}function uDe(e,n,t){this.a=e,N3.call(this,n,t)}function T9(e,n,t){var i;i=new y2(t),Rf(e,n,i)}function gfe(e,n,t){var i;return i=e[n],e[n]=t,i}function nR(e){var n;return n=e.slice(),vY(n,e)}function tR(e){var n;return n=e.n,e.a.b+n.d+n.a}function oDe(e){var n;return n=e.n,e.e.b+n.d+n.a}function wfe(e){var n;return n=e.n,e.e.a+n.b+n.c}function pfe(e){e.a.b=e.b,e.b.a=e.a,e.a=e.b=null}function Vt(e,n){return Xi(e,n,e.c.b,e.c),!0}function R3n(e){return e.a?e.a:NV(e)}function nE(e){if(!e)throw $(new l9(null))}function yw(e,n){return XE(e,new v9(n.a,n.b))}function B3n(e){return!cc(e)&&e.c.i.c==e.d.i.c}function z3n(e,n){return e.c=n)throw $(new bxe)}function Hu(e){e.f=new yCe(e),e.i=new kCe(e),++e.g}function wR(e){this.b=new xo(11),this.a=(Aw(),e)}function bV(e){this.b=null,this.a=(Aw(),e||Fme)}function Ife(e,n){this.e=e,this.d=(n&64)!=0?n|yh:n}function UDe(e,n){this.c=0,this.d=e,this.b=n|64|yh}function XDe(e){this.a=XJe(e.a),this.b=new bs(e.b)}function Sd(e,n,t,i){var r;r=e.i,r.i=n,r.a=t,r.b=i}function _fe(e){var n;for(n=e;n.f;)n=n.f;return n}function jvn(e){return e.e?ihe(e.e):null}function cE(e){return ps(),!e.Gc(Z1)&&!e.Gc(gb)}function KDe(e,n,t){return x8(),GY(e,n)&&GY(e,t)}function VDe(e,n,t){return iYe(e,u(n,12),u(t,12))}function gV(e,n){return n.Sh()?$0(e.b,u(n,52)):n}function pR(e){return new je(e.c+e.b/2,e.d+e.a/2)}function Evn(e,n,t){n.of(t,ne(re(Bn(e.b,t)))*e.a)}function Svn(e,n){n.Tg("General 'Rotator",1),B$n(e)}function Ir(e,n,t,i,r){pY.call(this,e,n,t,i,r,-1)}function uE(e,n,t,i,r){tO.call(this,e,n,t,i,r,-1)}function pe(e,n,t,i){mr.call(this,e,n,t),this.b=i}function mR(e,n,t,i){_C.call(this,e,n,t),this.b=i}function YDe(e){XTe.call(this,e,!1),this.a=!1}function QDe(){yK.call(this,"LOOKAHEAD_LAYOUT",1)}function WDe(){yK.call(this,"LAYOUT_NEXT_LEVEL",3)}function ZDe(e){this.b=e,p4.call(this,e),cOe(this)}function eIe(e){this.b=e,kC.call(this,e),uOe(this)}function nIe(e,n){this.b=e,HU.call(this,e.b),this.a=n}function m2(e,n,t){this.a=e,y4.call(this,n,t,5,6)}function Lfe(e,n,t,i){this.b=e,mr.call(this,n,t,i)}function zb(e,n,t){mh(),this.e=e,this.d=n,this.a=t}function Zr(e,n){for(_n(n);e.Ob();)n.Ad(e.Pb())}function vR(e,n){return fi(),new Yfe(e,n,0)}function wV(e,n){return fi(),new Yfe(6,e,n)}function xvn(e,n){return bn(e.substr(0,n.length),n)}function so(e,n){return $r(n)?RV(e,n):!!Xc(e.f,n)}function Avn(e){return _o(~e.l&Ls,~e.m&Ls,~e.h&G1)}function pV(e){return typeof e===sN||typeof e===aZ}function qh(e){return new qn(new sle(e.a.length,e.a))}function mV(e){return new wn(null,_vn(e,e.length))}function tIe(e){if(!e)throw $(new au);return e.d}function A4(e){var n;return n=CE(e),at(n!=null),n}function Mvn(e){var n;return n=bjn(e),at(n!=null),n}function O9(e,n){var t;return t=e.a.gc(),Zae(n,t),t-n}function hr(e,n){var t;return t=e.a.yc(n,e),t==null}function PC(e,n){return e.a.yc(n,(Pn(),eb))==null}function Tvn(e,n){return e>0?k.Math.log(e/n):-100}function Pfe(e,n){return n?fc(e,n):!1}function M4(e,n,t){return zf(e.a,n),gfe(e.b,n.g,t)}function Cvn(e,n,t){C9(t,e.a.c.length),ol(e.a,t,n)}function ce(e,n,t,i){nFe(n,t,e.length),Ovn(e,n,t,i)}function Ovn(e,n,t,i){var r;for(r=n;r0?1:0}function sE(e){return e.e==0?e:new zb(-e.e,e.d,e.a)}function Dvn(e){return e==Ki?HN:e==Dr?"-INF":""+e}function Ivn(e){return e==Ki?HN:e==Dr?"-INF":""+e}function _vn(e,n){return S8n(n,e.length),new dDe(e,n)}function rIe(e,n,t,i,r){for(;n=e.g}function MV(e,n,t){var i;return i=$Y(e,n,t),zbe(e,i)}function wIe(e,n){var t;t=console[e],t.call(console,n)}function T4(e,n){var t;t=e.a.length,T2(e,t),nY(e,t,n)}function pIe(e,n){var t;++e.j,t=e.Cj(),e.pj(e.Xi(t,n))}function TV(e,n){for(_n(n);e.c=e?new Yoe:K8n(e-1)}function uf(e){if(e==null)throw $(new e4);return e}function _n(e){if(e==null)throw $(new e4);return e}function Zvn(e){return!e.a&&(e.a=new mr(wb,e,4)),e.a}function Sw(e){return!e.d&&(e.d=new mr(Rc,e,1)),e.d}function e5n(e){if(e.p!=3)throw $(new is);return e.e}function n5n(e){if(e.p!=4)throw $(new is);return e.e}function t5n(e){if(e.p!=6)throw $(new is);return e.f}function i5n(e){if(e.p!=3)throw $(new is);return e.j}function r5n(e){if(e.p!=4)throw $(new is);return e.j}function c5n(e){if(e.p!=6)throw $(new is);return e.k}function or(){$xe.call(this),Qp(this.j.c,0),this.a=-1}function MIe(){Ot.call(this,"DELAUNAY_TRIANGULATION",0)}function u5n(){return BP(),z(B(Gen,1),ke,537,0,[Wne])}function o5n(e,n,t){return J4(),t.Kg(e,u(n.jd(),147))}function s5n(e,n){Et((!e.a&&(e.a=new AC(e,e)),e.a),n)}function Wfe(e,n){e.c<0||e.b.b=0?e.hi(t):q0e(e,n)}function I9(e,n){var t;return t=AV("",e),t.n=n,t.i=1,t}function xw(e){return e.c==-2&&oX(e,EMn(e.g,e.b)),e.c}function Zfe(e){return!e.b&&(e.b=new OP(new kX)),e.b}function TIe(e,n){return eR(),new Mde(new sOe(e),new oOe(n))}function f5n(e){return ll(e,gZ),fB(mc(mc(5,e),e/10|0))}function CV(){CV=Y,Xen=new rse(z(B(wg,1),eF,45,0,[]))}function CIe(){j0e.call(this,gg,(yAe(),rhn)),TPn(this)}function OIe(){j0e.call(this,hf,(d9(),Y8e)),PLn(this)}function NIe(e,n){Bwn.call(this,V8n(Nt(e),Nt(n))),this.a=n}function eae(e,n,t,i){bw.call(this,e,n),this.d=t,this.a=i}function SR(e,n,t,i){bw.call(this,e,t),this.a=n,this.f=i}function DIe(e,n){this.b=e,vV.call(this,e,n),cOe(this)}function IIe(e,n){this.b=e,Xle.call(this,e,n),uOe(this)}function hE(e){this.d=e,this.a=this.d.b,this.b=this.d.c}function _Ie(e){e.b=!1,e.c=!1,e.d=!1,e.a=!1}function _9(e){return!e.a&&(e.a=new uAe(e.c.vc())),e.a}function LIe(e){return!e.b&&(e.b=new f9(e.c.ec())),e.b}function PIe(e){return!e.d&&(e.d=new Hr(e.c.Bc())),e.d}function Uh(e,n){for(;n-- >0;)e=e<<1|(e<0?1:0);return e}function $Ie(e,n){var t;return t=new Xu(e),Hn(n.c,t),t}function a5n(e,n){aV(u(n.b,68),e),Ao(n.a,new Wue(e))}function RIe(e,n){e.u.Gc((ps(),Z1))&&vCn(e,n),y9n(e,n)}function Ku(e,n){return ue(e)===ue(n)||e!=null&&di(e,n)}function ei(e,n,t){return $r(n)?Kc(e,n,t):Ko(e.f,n,t)}function nae(e){return jn(),e?e.Me():(Aw(),Aw(),Jme)}function h5n(){return ZP(),z(B(P6e,1),ke,477,0,[Wre])}function d5n(){return e$(),z(B(Rln,1),ke,546,0,[Zre])}function b5n(){return Tj(),z(B(i9e,1),ke,527,0,[TD])}function zc(e,n){return oV(e.a,n)?e.b[u(n,23).g]:null}function g5n(e){return String.fromCharCode.apply(null,e)}function ic(e,n){return Yn(n,e.length),e.charCodeAt(n)}function RC(e){return e.j.c.length=0,rae(e.c),u2n(e.a),e}function L9(e){return e.e==s7&&a(e,$En(e.g,e.b)),e.e}function BC(e){return e.f==s7&&w(e,Axn(e.g,e.b)),e.f}function w5n(e){return!e.b&&(e.b=new Nn(vt,e,4,7)),e.b}function tae(e){return!e.c&&(e.c=new Nn(vt,e,5,8)),e.c}function iae(e){return!e.c&&(e.c=new pe($s,e,9,9)),e.c}function OV(e){return!e.n&&(e.n=new pe(ju,e,1,7)),e.n}function z3(e){var n;return n=e.b,!n&&(e.b=n=new rj(e)),n}function rae(e){var n;for(n=e.Jc();n.Ob();)n.Pb(),n.Qb()}function p5n(e,n,t){var i;i=u(e.d.Kb(t),162),i&&i.Nb(n)}function m5n(e,n){return new a_e(u(Nt(e),51),u(Nt(n),51))}function si(e,n){return R0(e),new wn(e,new whe(n,e.a))}function So(e,n){return R0(e),new wn(e,new the(n,e.a))}function k2(e,n){return R0(e),new xle(e,new WPe(n,e.a))}function xR(e,n){return R0(e),new Ale(e,new ZPe(n,e.a))}function BIe(e,n){W1e(e,ne($1(n,"x")),ne($1(n,"y")))}function zIe(e,n){W1e(e,ne($1(n,"x")),ne($1(n,"y")))}function v5n(e,n){return Qoe(),ki((_n(e),e),(_n(n),n))}function y5n(e,n){return ki(e.d.c+e.d.b/2,n.d.c+n.d.b/2)}function k5n(e,n){return ki(e.g.c+e.g.b/2,n.g.c+n.g.b/2)}function j5n(e){return e!=null&&Sj(jG,e.toLowerCase())}function E5n(e){rl();var n;n=u(e.g,9),n.n.a=e.d.c+n.d.b}function NV(e){var n;return n=Q8n(e),n||null}function ri(e,n,t,i){return WBe(e,n,t,!1),FB(e,i),e}function S5n(e,n,t){OLn(e.a,t),V7n(t),tOn(e.b,t),QLn(n,t)}function C4(e,n,t,i){Ot.call(this,e,n),this.a=t,this.b=i}function AR(e,n,t,i){this.a=e,this.c=n,this.b=t,this.d=i}function cae(e,n,t,i){this.c=e,this.b=n,this.a=t,this.d=i}function FIe(e,n,t,i){this.c=e,this.b=n,this.d=t,this.a=i}function DV(e,n,t,i){this.a=e,this.e=n,this.d=t,this.c=i}function JIe(e,n,t,i){this.a=e,this.d=n,this.c=t,this.b=i}function Lf(e,n,t,i){this.c=e,this.d=n,this.b=t,this.a=i}function IV(e,n,t){this.a=Jge,this.d=e,this.b=n,this.c=t}function uae(e,n){this.b=e,this.c=n,this.a=new s4(this.b)}function HIe(e,n){this.d=(_n(e),e),this.a=16449,this.c=n}function GIe(e,n,t,i){Xze.call(this,e,t,i,!1),this.f=n}function _V(e,n,t){var i,r;return i=Cge(e),r=n.qi(t,i),r}function C1(e){var n,t;return t=(n=new hw,n),q9(t,e),t}function LV(e){var n,t;return t=(n=new hw,n),x0e(t,e),t}function qIe(e){return!e.b&&(e.b=new pe(pr,e,12,3)),e.b}function UIe(e){this.a=new Ce,this.e=oe($t,Se,54,e,0,2)}function PV(e){this.f=e,this.c=this.f.e,e.f>0&&JHe(this)}function XIe(e,n,t,i){this.a=e,this.c=n,this.d=t,this.b=i}function KIe(e,n,t,i){this.a=e,this.b=n,this.d=t,this.c=i}function VIe(e,n,t,i){this.a=e,this.b=n,this.c=t,this.d=i}function YIe(e,n,t,i){this.a=e,this.b=n,this.c=t,this.d=i}function Jb(e,n,t,i){this.e=e,this.a=n,this.c=t,this.d=i}function QIe(e,n,t,i){Il(),QPe.call(this,n,t,i),this.a=e}function WIe(e,n,t,i){Il(),QPe.call(this,n,t,i),this.a=e}function ZIe(e,n){this.a=e,zpn.call(this,e,u(e.d,16).dd(n))}function x5n(e,n){return ki(us(e)*Gs(e),us(n)*Gs(n))}function A5n(e,n){return ki(us(e)*Gs(e),us(n)*Gs(n))}function O4(e){var n;return n=e.f,n||(e.f=new g9(e,e.c))}function jn(){jn=Y,Sc=new Ae,i1=new sn,hJ=new Sn}function Aw(){Aw=Y,Fme=new xe,ote=new xe,Jme=new un}function P9(e){if(Ds(e.d),e.d.d!=e.c)throw $(new Nl)}function qs(e){e.a.a=e.c,e.c.b=e.a,e.a.b=e.c.a=null,e.b=0}function oae(e){return at(e.b0?$f(e):new Ce}function MR(e){return e.n&&(e.e!==pYe&&e.he(),e.j=null),e}function sae(e,n){return e.b=n.b,e.c=n.c,e.d=n.d,e.a=n.a,e}function T5n(e,n,t){return Te(e.a,(GQ(n,t),new bw(n,t))),e}function C5n(e,n){return u(T(e,(me(),Ty)),16).Ec(n),n}function O5n(e,n){return gn(e,u(T(n,(Ne(),mm)),15),n)}function N5n(e){return Hw(e)&&Re($e(ye(e,(Ne(),kg))))}function D5n(e,n,t){return Mj(),_jn(u(Bn(e.e,n),516),t)}function I5n(e,n,t){e.i=0,e.e=0,n!=t&&Hze(e,n,t)}function _5n(e,n,t){e.i=0,e.e=0,n!=t&&Gze(e,n,t)}function e_e(e,n,t,i){this.b=e,this.c=i,N$.call(this,n,t)}function n_e(e,n){this.g=e,this.d=z(B(c1,1),Bd,9,0,[n])}function t_e(e,n){e.d&&!e.d.a&&(USe(e.d,n),t_e(e.d,n))}function i_e(e,n){e.e&&!e.e.a&&(USe(e.e,n),i_e(e.e,n))}function r_e(e,n){return ev(e.j,n.s,n.c)+ev(n.e,e.s,e.c)}function L5n(e,n){return-ki(us(e)*Gs(e),us(n)*Gs(n))}function P5n(e){return u(e.jd(),147).Og()+":"+su(e.kd())}function c_e(){dW(this,new OT),this.wb=(x0(),Rn),d9()}function u_e(e){this.b=new u_,this.a=e,k.Math.random()}function o_e(e){this.b=new Ce,Er(this.b,this.b),this.a=e}function lae(e,n){new Si,this.a=new xs,this.b=e,this.c=n}function s_e(){hu.call(this,"There is no more element.")}function $5n(e){JP(),k.setTimeout(function(){throw e},0)}function R5n(e){e.Tg("No crossing minimization",1),e.Ug()}function B5n(e,n){return Us(e),Us(n),Wxe(u(e,23),u(n,23))}function Hb(e,n,t){var i,r;i=qK(t),r=new k3(i),Rf(e,n,r)}function $V(e,n,t,i,r,c){tO.call(this,e,n,t,i,r,c?-2:-1)}function l_e(e,n,t,i){Dse.call(this,n,t),this.b=e,this.a=i}function fae(e){this.b=e,this.c=e,e.e=null,e.c=null,this.a=1}function TR(e){return!e.a&&(e.a=new pe(Jt,e,10,11)),e.a}function vi(e){return!e.q&&(e.q=new pe(yf,e,11,10)),e.q}function we(e){return!e.s&&(e.s=new pe(ns,e,21,17)),e.s}function f_e(e){return nE(e==null||pV(e)&&e.Rm!==Qn),e}function CR(e,n){if(e==null)throw $(new c4(n));return e}function a_e(e,n){ybn.call(this,new bV(e)),this.a=e,this.b=n}function RV(e,n){return n==null?!!Xc(e.f,null):svn(e.i,n)}function BV(e){return X(e,18)?new w2(u(e,18)):M3n(e.Jc())}function OR(e){return jn(),X(e,59)?new DX(e):new B$(e)}function z5n(e){return Nt(e),rHe(new qn(Vn(e.a.Jc(),new ee)))}function F5n(e){return new nOe(e,e.e.Pd().gc()*e.c.Pd().gc())}function J5n(e){return new tOe(e,e.e.Pd().gc()*e.c.Pd().gc())}function aae(e){return e&&e.hashCode?e.hashCode():vw(e)}function H5n(e){e&&DR(e,e.ge())}function G5n(e,n){var t;return t=Qse(e.a,n),t&&(n.d=null),t}function h_e(e,n,t){return e.f?e.f.cf(n,t):!1}function zC(e,n,t,i){tr(e.c[n.g],t.g,i),tr(e.c[t.g],n.g,i)}function zV(e,n,t,i){tr(e.c[n.g],n.g,t),tr(e.b[n.g],n.g,i)}function q5n(e,n,t){return ne(re(t.a))<=e&&ne(re(t.b))>=n}function d_e(){this.d=new Si,this.b=new pt,this.c=new Ce}function b_e(){this.b=new ar,this.d=new Si,this.e=new $P}function hae(){this.c=new Kr,this.d=new Kr,this.e=new Kr}function Mw(){this.a=new xs,this.b=(ll(3,Q2),new xo(3))}function g_e(e){this.c=e,this.b=new vd(u(Nt(new Sf),51))}function w_e(e){this.c=e,this.b=new vd(u(Nt(new nk),51))}function p_e(e){this.b=e,this.a=new vd(u(Nt(new qg),51))}function xd(e,n){this.e=e,this.a=Ar,this.b=IXe(n),this.c=n}function NR(e){this.c=e.c,this.d=e.d,this.b=e.b,this.a=e.a}function m_e(e,n,t,i,r,c){this.a=e,DY.call(this,n,t,i,r,c)}function v_e(e,n,t,i,r,c){this.a=e,DY.call(this,n,t,i,r,c)}function M0(e,n,t,i,r,c,o){return new rY(e.e,n,t,i,r,c,o)}function U5n(e,n,t){return t>=0&&bn(e.substr(t,n.length),n)}function y_e(e,n){return X(n,147)&&bn(e.b,u(n,147).Og())}function X5n(e,n){return e.a?n.Dh().Jc():u(n.Dh(),72).Gi()}function k_e(e,n){var t;return t=e.b.Oc(n),bPe(t,e.b.gc()),t}function FC(e,n){if(e==null)throw $(new c4(n));return e}function tu(e){return e.u||(Ms(e),e.u=new VOe(e,e)),e.u}function Go(e){var n;return n=u(Un(e,16),29),n||e.fi()}function DR(e,n){var t;return t=Db(e.Pm),n==null?t:t+": "+n}function of(e,n,t){return Yr(n,t,e.length),e.substr(n,t-n)}function j_e(e,n){q$.call(this),The(this),this.a=e,this.c=n}function E_e(){yK.call(this,"FIXED_INTEGER_RATIO_BOXES",2)}function K5n(){return nB(),z(B(gve,1),ke,422,0,[bve,Xte])}function V5n(){return fO(),z(B(Sve,1),ke,419,0,[XJ,Eve])}function Y5n(){return rO(),z(B(Mve,1),ke,476,0,[Ave,VJ])}function Q5n(){return JR(),z(B(Fve,1),ke,420,0,[bie,zve])}function W5n(){return qR(),z(B(n5e,1),ke,423,0,[Sie,Eie])}function Z5n(){return uO(),z(B(J4e,1),ke,421,0,[tre,ire])}function e4n(){return mE(),z(B(jon,1),ke,518,0,[Cx,Tx])}function n4n(){return Da(),z(B(Non,1),ke,508,0,[Ag,Ya])}function t4n(){return ah(),z(B(Con,1),ke,509,0,[pp,Ud])}function i4n(){return ha(),z(B(Uon,1),ke,515,0,[Am,sb])}function r4n(){return Cw(),z(B(Zon,1),ke,454,0,[lb,Jv])}function c4n(){return FR(),z(B($ye,1),ke,425,0,[xre,Pye])}function u4n(){return SB(),z(B(Rye,1),ke,487,0,[BH,qv])}function o4n(){return iB(),z(B(zye,1),ke,426,0,[Bye,Nre])}function s4n(){return lB(),z(B(n3e,1),ke,424,0,[vte,pJ])}function l4n(){return K9(),z(B(sin,1),ke,502,0,[QN,Dte])}function f4n(){return VR(),z(B(C6e,1),ke,478,0,[Kre,T6e])}function a4n(){return WC(),z(B($6e,1),ke,428,0,[ece,YH])}function h4n(){return pO(),z(B(c9e,1),ke,427,0,[WH,r9e])}function IR(e,n,t,i){return t>=0?e.Rh(n,t,i):e.zh(null,t,i)}function JC(e){return e.b.b==0?e.a.uf():eV(e.b)}function d4n(e){if(e.p!=5)throw $(new is);return Rt(e.f)}function b4n(e){if(e.p!=5)throw $(new is);return Rt(e.k)}function dae(e){return ue(e.a)===ue((FY(),zce))&&jPn(e),e.a}function S_e(e,n){fj(this,new je(e.a,e.b)),j3(this,DC(n))}function Tw(){kbn.call(this,new l4(I2(12))),tle(!0),this.a=2}function FV(e,n,t){fi(),aw.call(this,e),this.b=n,this.a=t}function bae(e,n,t){Il(),DP.call(this,n),this.a=e,this.b=t}function g4n(e,n){var t=nte[e.charCodeAt(0)];return t??e}function _R(e,n){return CR(e,"set1"),CR(n,"set2"),new dMe(e,n)}function LR(e,n){return hPe(n),P8n(e,oe($t,ni,30,n,15,1),n)}function w4n(e,n){e.b=n,e.c>0&&e.b>0&&(e.g=oR(e.c,e.b,e.a))}function p4n(e,n){e.c=n,e.c>0&&e.b>0&&(e.g=oR(e.c,e.b,e.a))}function x_e(e){var n;n=e.c.d.b,e.b=n,e.a=e.c.d,n.a=e.c.d.b=e}function A_e(e){return e.b==0?null:(at(e.b!=0),$l(e,e.a.a))}function lo(e,n){return n==null?du(Xc(e.f,null)):Dj(e.i,n)}function M_e(e,n,t,i,r){return new gW(e,(H9(),ate),n,t,i,r)}function JV(e,n,t,i){var r;r=new lNe,n.a[t.g]=r,M4(e.b,i,r)}function T_e(e,n){var t,i;return t=n,i=new st,bVe(e,t,i),i.d}function m4n(e,n){var t;return t=O8n(e.f,n),gi(G$(t),e.f.d)}function HC(e){var n;G8n(e.a),CCe(e.a),n=new TP(e.a),ode(n)}function v4n(e,n){jXe(e,!0),Ao(e.e.Pf(),new Kle(e,!0,n))}function y4n(e,n){return P1(),u(T(n,(Mu(),Nh)),15).a==e}function sc(e){return Math.max(Math.min(e,oi),-2147483648)|0}function C_e(e){q$.call(this),The(this),this.a=e,this.c=!0}function gae(e,n,t){this.a=new Ce,this.e=e,this.f=n,this.c=t}function PR(e,n,t){this.c=new Ce,this.e=e,this.f=n,this.b=t}function O_e(e,n,t){this.i=new Ce,this.b=e,this.g=n,this.a=t}function N_e(e){this.a=u(Nt(e),277),this.b=(jn(),new ole(e))}function $9(){$9=Y;var e,n;n=!pEn(),e=new ln,tte=n?new rn:e}function wae(){wae=Y,Snn=new o0,Ann=new xfe,xnn=new ud}function ah(){ah=Y,pp=new yse(fy,0),Ud=new yse(ly,1)}function Da(){Da=Y,Ag=new kse(XZ,0),Ya=new kse("UP",1)}function Cw(){Cw=Y,lb=new Ese(ly,0),Jv=new Ese(fy,1)}function F3(e,n,t){$R(),e&&ei($ce,e,n),e&&ei(WD,e,t)}function pae(e,n,t){var i;i=e.Fh(n),i>=0?e.$h(i,t):ybe(e,n,t)}function D_e(e,n){var t;for(Nt(n),t=e.a;t;t=t.c)n.Wd(t.g,t.i)}function GC(e,n){var t;t=e.q.getHours(),e.q.setDate(n),oS(e,t)}function I_e(e){var n;return n=new UP(I2(e.length)),m1e(n,e),n}function k4n(e){function n(){}return n.prototype=e||{},new n}function j4n(e,n){return mze(e,n)?(mBe(e),!0):!1}function O1(e,n){if(n==null)throw $(new e4);return jEn(e,n)}function E4n(e){if(e.ye())return null;var n=e.n;return uJ[n]}function j2(e){return e.Db>>16!=3?null:u(e.Cb,26)}function Ia(e){return e.Db>>16!=9?null:u(e.Cb,26)}function __e(e){return e.Db>>16!=6?null:u(e.Cb,85)}function L_e(e,n){var t;return t=e.Fh(n),t>=0?e.Th(t):kW(e,n)}function HV(e,n,t){var i;i=Rze(e,n,t),e.b=new AB(i.c.length)}function P_e(e){this.a=e,this.b=oe(von,Se,2005,e.e.length,0,2)}function $_e(){this.a=new zh,this.e=new ar,this.g=0,this.i=0}function R_e(e,n){L$(this),this.f=n,this.g=e,MR(this),this.he()}function B_e(e,n){return e.b+=n.b,e.c+=n.c,e.d+=n.d,e.a+=n.a,e}function mae(e){var n;return n=e.d,n=e._i(e.f),Et(e,n),n.Ob()}function z_e(e,n){var t;return t=new kfe(n),wGe(t,e),new bs(t)}function S4n(e){if(e.p!=0)throw $(new is);return Gj(e.f,0)}function x4n(e){if(e.p!=0)throw $(new is);return Gj(e.k,0)}function F_e(e){return e.Db>>16!=7?null:u(e.Cb,241)}function vae(e){return e.Db>>16!=7?null:u(e.Cb,174)}function J_e(e){return e.Db>>16!=3?null:u(e.Cb,158)}function R9(e){return e.Db>>16!=6?null:u(e.Cb,241)}function Bi(e){return e.Db>>16!=11?null:u(e.Cb,26)}function E2(e){return e.Db>>16!=17?null:u(e.Cb,29)}function dE(e,n,t,i,r,c){return new L1(e.e,n,e.Jj(),t,i,r,c)}function Kc(e,n,t){return n==null?Ko(e.f,null,t):Pw(e.i,n,t)}function GV(e,n){return k.Math.abs(e)0}function yae(e){var n;return R0(e),n=new ar,si(e,new Gke(n))}function H_e(e,n){var t=e.a=e.a||[];return t[n]||(t[n]=e.te(n))}function O4n(e,n){var t;t=e.q.getHours(),e.q.setMonth(n),oS(e,t)}function lc(e,n){e.c&&qo(e.c.g,e),e.c=n,e.c&&Te(e.c.g,e)}function Or(e,n){e.c&&qo(e.c.a,e),e.c=n,e.c&&Te(e.c.a,e)}function Gr(e,n){e.d&&qo(e.d.e,e),e.d=n,e.d&&Te(e.d.e,e)}function gu(e,n){e.i&&qo(e.i.j,e),e.i=n,e.i&&Te(e.i.j,e)}function G_e(e,n,t){this.a=n,this.c=e,this.b=(Nt(t),new bs(t))}function q_e(e,n,t){this.a=n,this.c=e,this.b=(Nt(t),new bs(t))}function U_e(e,n){this.a=e,this.c=pc(this.a),this.b=new NR(n)}function S2(e,n){if(e<0||e>n)throw $(new jo(Qge+e+Wge+n))}function X_e(){X_e=Y,con=Eo(new or,(zr(),Pc),(Ur(),Ey))}function kae(){kae=Y,uon=Eo(new or,(zr(),Pc),(Ur(),Ey))}function K_e(){K_e=Y,eon=Eo(new or,(zr(),Pc),(Ur(),Ey))}function V_e(){V_e=Y,non=Eo(new or,(zr(),Pc),(Ur(),Ey))}function Y_e(){Y_e=Y,ton=Eo(new or,(zr(),Pc),(Ur(),Ey))}function jae(){jae=Y,ion=Eo(new or,(zr(),Pc),(Ur(),Ey))}function Q_e(){Q_e=Y,Son=qt(new or,(zr(),Pc),(Ur(),nx))}function cl(){cl=Y,Mon=qt(new or,(zr(),Pc),(Ur(),nx))}function W_e(){W_e=Y,Ton=qt(new or,(zr(),Pc),(Ur(),nx))}function qV(){qV=Y,Ion=qt(new or,(zr(),Pc),(Ur(),nx))}function Z_e(){Z_e=Y,Tsn=Eo(new or,(Y4(),Nx),(cS(),cye))}function eLe(){eLe=Y,qen=It((BP(),z(B(Gen,1),ke,537,0,[Wne])))}function $R(){$R=Y,$ce=new pt,WD=new pt,Hgn(fnn,new F6)}function N4n(e,n){var t,i;t=n.c,i=t!=null,i&&T4(e,new y2(n.c))}function nLe(e,n){Kvn(e,e.b,e.c),u(e.b.b,68),n&&u(n.b,68).b}function RR(e,n){X(e.Cb,184)&&(u(e.Cb,184).tb=null),Mo(e,n)}function UV(e,n){X(e.Cb,88)&&H2(Ms(u(e.Cb,88)),4),Mo(e,n)}function D4n(e,n){Q1e(e,n),X(e.Cb,88)&&H2(Ms(u(e.Cb,88)),2)}function I4n(e,n){return ki(u(e.c,65).c.e.b,u(n.c,65).c.e.b)}function _4n(e,n){return ki(u(e.c,65).c.e.a,u(n.c,65).c.e.a)}function fo(e,n){return Cc(),xY(n)?new rR(n,e):new pC(n,e)}function XV(e,n){e.a&&qo(e.a.k,e),e.a=n,e.a&&Te(e.a.k,e)}function KV(e,n){e.b&&qo(e.b.f,e),e.b=n,e.b&&Te(e.b.f,e)}function T0(e,n,t){OFe(n,t,e.gc()),this.c=e,this.a=n,this.b=t-n}function N4(e){this.c=new Si,this.b=e.b,this.d=e.c,this.a=e.a}function VV(e){this.a=k.Math.cos(e),this.b=k.Math.sin(e)}function Gb(e,n,t,i){this.c=e,this.d=i,XV(this,n),KV(this,t)}function pn(e,n){this.b=(_n(e),e),this.a=(n&W2)==0?n|64|yh:n}function L4n(e,n){YCe(e,Rt(Rr(kw(n,24),rF)),Rt(Rr(n,rF)))}function qC(e){return mh(),ao(e,0)>=0?B0(e):sE(B0(Td(e)))}function P4n(){return zl(),z(B(Qo,1),ke,130,0,[Kme,Yo,Vme])}function tLe(e,n,t){return new gW(e,(H9(),fte),null,!1,n,t)}function iLe(e,n,t){return new gW(e,(H9(),hte),n,t,null,!1)}function rLe(e,n,t){var i;OFe(n,t,e.c.length),i=t-n,Goe(e.c,n,i)}function cLe(e,n){var t;return t=u(L2(O4(e.a),n),18),t?t.gc():0}function Eae(e){var n;return R0(e),n=(Aw(),Aw(),ote),aB(e,n)}function uLe(e){for(var n;;)if(n=e.Pb(),!e.Ob())return n}function oLe(e){var n,t;return t=(d9(),n=new hw,n),q9(t,e),t}function sLe(e){var n,t;return t=(d9(),n=new hw,n),q9(t,e),t}function J3(e){return Mj(),X(e.g,9)?u(e.g,9):null}function $4n(){return _w(),z(B(Rte,1),ke,368,0,[lp,ib,sp])}function R4n(){return pB(),z(B(yve,1),ke,350,0,[vve,UJ,Kte])}function B4n(){return Wb(),z(B(Bin,1),ke,449,0,[tie,k7,Ov])}function z4n(){return W9(),z(B(hie,1),ke,302,0,[fie,aie,tD])}function F4n(){return _0(),z(B(die,1),ke,329,0,[iD,Bve,dm])}function J4n(){return _1(),z(B(Kin,1),ke,315,0,[rD,Dv,Sy])}function H4n(){return IE(),z(B(D4e,1),ke,352,0,[Vie,N4e,SH])}function G4n(){return Nc(),z(B(Xun,1),ke,452,0,[xx,ys,Do])}function q4n(){return DB(),z(B(q4e,1),ke,381,0,[H4e,rre,G4e])}function U4n(){return DE(),z(B(U4e,1),ke,348,0,[ure,cre,pD])}function X4n(){return r8(),z(B(K4e,1),ke,349,0,[ore,X4e,Ax])}function K4n(){return wB(),z(B(Q4e,1),ke,351,0,[Y4e,sre,V4e])}function V4n(){return IB(),z(B(W4e,1),ke,382,0,[lre,I7,xm])}function Y4n(){return yE(),z(B(w3e,1),ke,384,0,[Ete,jte,Ste])}function Q4n(){return ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])}function W4n(){return ws(),z(B(Onn,1),ke,461,0,[Th,nb,Uf])}function Z4n(){return Uo(),z(B(Dnn,1),ke,462,0,[ka,tb,Xf])}function eyn(){return NE(),z(B(gye,1),ke,385,0,[bye,hre,yD])}function nyn(){return EO(),z(B(Hye,1),ke,386,0,[zH,Fye,Jye])}function tyn(){return XB(),z(B(a6e,1),ke,387,0,[f6e,Gre,l6e])}function iyn(){return NB(),z(B(o6e,1),ke,303,0,[Pre,u6e,c6e])}function ryn(){return WB(),z(B(s6e,1),ke,436,0,[Px,HH,$re])}function cyn(){return JB(),z(B(L6e,1),ke,430,0,[I6e,_6e,Yre])}function uyn(){return TO(),z(B(Qre,1),ke,435,0,[XH,KH,VH])}function oyn(){return rB(),z(B(D6e,1),ke,429,0,[Vre,N6e,O6e])}function syn(){return $a(),z(B(t8e,1),ke,279,0,[F7,_m,J7])}function lyn(){return B1(),z(B(b8e,1),ke,347,0,[oG,Yd,Qx])}function fyn(){return jE(),z(B(y8e,1),ke,300,0,[HD,Tce,v8e])}function ayn(){return G4(),z(B(E8e,1),ke,281,0,[j8e,Pm,dG])}function _a(e){return pu(z(B(Lr,1),Se,8,0,[e.i.n,e.n,e.a]))}function hyn(e,n,t){var i;i=new wc(t.d),gi(i,e),W1e(n,i.a,i.b)}function lLe(e,n,t){var i;i=new dM,i.b=n,i.a=t,++n.b,Te(e.d,i)}function dyn(e,n,t){var i;return i=fS(e,n,!1),i.b<=n&&i.a<=t}function byn(e){if(e.p!=2)throw $(new is);return Rt(e.f)&yr}function gyn(e){if(e.p!=2)throw $(new is);return Rt(e.k)&yr}function vn(e,n){if(e<0||e>=n)throw $(new jo(Qge+e+Wge+n))}function Yn(e,n){if(e<0||e>=n)throw $(new Doe(Qge+e+Wge+n))}function wyn(e){return e.Db>>16!=6?null:u(SW(e),241)}function fLe(e,n){var t,i;return i=O9(e,n),t=e.a.dd(i),new aMe(e,t)}function pyn(e,n){var t;return t=(_n(e),e).g,gle(!!t),_n(n),t(n)}function myn(e){return e.a==(z9(),AG)&>(e,KDn(e.g,e.b)),e.a}function D4(e){return e.d==(z9(),AG)&&Gue(e,U_n(e.g,e.b)),e.d}function Sae(e,n){vbn.call(this,new l4(I2(e))),ll(n,aYe),this.a=n}function aLe(e,n,t){aw.call(this,25),this.b=e,this.a=n,this.c=t}function ul(e){fi(),aw.call(this,e),this.c=!1,this.a=!1}function hLe(e,n){zb.call(this,1,2,z(B($t,1),ni,30,15,[e,n]))}function Rr(e,n){return I0(pvn(uu(e)?sf(e):e,uu(n)?sf(n):n))}function hh(e,n){return I0(mvn(uu(e)?sf(e):e,uu(n)?sf(n):n))}function YV(e,n){return I0(vvn(uu(e)?sf(e):e,uu(n)?sf(n):n))}function xae(e,n){return NDe(e.a,n)?gfe(e.b,u(n,23).g,null):null}function qb(e){return Nt(e),X(e,18)?new bs(u(e,18)):M9(e.Jc())}function QV(e){cR(),this.a=(jn(),X(e,59)?new DX(e):new B$(e))}function vyn(e){var n;return n=u(nR(e.b),10),new _l(e.a,n,e.c)}function yyn(e,n){var t;t=ne(re(e.a.mf((Xt(),iG)))),$Ve(e,n,t)}function kyn(e,n){return kE(),e.c==n.c?ki(n.d,e.d):ki(e.c,n.c)}function jyn(e,n){return kE(),e.c==n.c?ki(e.d,n.d):ki(e.c,n.c)}function Eyn(e,n){return kE(),e.c==n.c?ki(e.d,n.d):ki(n.c,e.c)}function Syn(e,n){return kE(),e.c==n.c?ki(n.d,e.d):ki(n.c,e.c)}function xyn(e,n){e.b=e.b|n.b,e.c=e.c|n.c,e.d=e.d|n.d,e.a=e.a|n.a}function _(e){return at(e.ai?1:0}function bLe(e,n){var t,i;return t=yY(n),i=t,u(Bn(e.c,i),15).a}function WV(e,n,t){var i;i=e.d[n.p],e.d[n.p]=e.d[t.p],e.d[t.p]=i}function Cyn(e,n,t){var i;e.n&&n&&t&&(i=new gL,Te(e.e,i))}function ZV(e,n){if(hr(e.a,n),n.d)throw $(new hu(LYe));n.d=e}function Tae(e,n){this.a=new Ce,this.d=new Ce,this.f=e,this.c=n}function gLe(){J4(),this.b=new pt,this.a=new pt,this.c=new Ce}function wLe(){this.c=new LCe,this.a=new KPe,this.b=new fxe,MMe()}function pLe(e,n,t){this.d=e,this.j=n,this.e=t,this.o=-1,this.p=3}function mLe(e,n,t){this.d=e,this.k=n,this.f=t,this.o=-1,this.p=5}function vLe(e,n,t,i,r,c){Che.call(this,e,n,t,i,r),c&&(this.o=-2)}function yLe(e,n,t,i,r,c){Ohe.call(this,e,n,t,i,r),c&&(this.o=-2)}function kLe(e,n,t,i,r,c){Gae.call(this,e,n,t,i,r),c&&(this.o=-2)}function jLe(e,n,t,i,r,c){Ihe.call(this,e,n,t,i,r),c&&(this.o=-2)}function ELe(e,n,t,i,r,c){qae.call(this,e,n,t,i,r),c&&(this.o=-2)}function SLe(e,n,t,i,r,c){Nhe.call(this,e,n,t,i,r),c&&(this.o=-2)}function xLe(e,n,t,i,r,c){Dhe.call(this,e,n,t,i,r),c&&(this.o=-2)}function ALe(e,n,t,i,r,c){Uae.call(this,e,n,t,i,r),c&&(this.o=-2)}function MLe(e,n,t,i){DP.call(this,t),this.b=e,this.c=n,this.d=i}function TLe(e,n){this.f=e,this.a=(z9(),xG),this.c=xG,this.b=n}function CLe(e,n){this.g=e,this.d=(z9(),AG),this.a=AG,this.b=n}function Cae(e,n){!e.c&&(e.c=new nr(e,0)),Xz(e.c,(Ei(),lA),n)}function Oyn(e,n){return AOn(e,n,X(n,103)&&(u(n,19).Bb&Ec)!=0)}function Nyn(e,n){return iIe(Pu(e.q.getTime()),Pu(n.q.getTime()))}function OLe(e){return iV(e.e.Pd().gc()*e.c.Pd().gc(),16,new X5(e))}function Dyn(e){return!!e.u&&Vu(e.u.a).i!=0&&!(e.n&&zQ(e.n))}function Iyn(e){return!!e.a&&Cs(e.a.a).i!=0&&!(e.b&&FQ(e.b))}function Oae(e,n){return n==0?!!e.o&&e.o.f!=0:_Q(e,n)}function NLe(e){return at(e.b.b!=e.d.a),e.c=e.b=e.b.b,--e.a,e.c.c}function bE(e){for(;e.d>0&&e.a[--e.d]==0;);e.a[e.d++]==0&&(e.e=0)}function DLe(e){return e.a?e.e.length==0?e.a.a:e.a.a+(""+e.e):e.c}function qr(e,n){this.a=e,qc.call(this,e),S2(n,e.gc()),this.b=n}function ILe(e){this.a=oe(Ar,On,1,b1e(k.Math.max(8,e))<<1,5,1)}function _Le(e){zY.call(this,e,(H9(),lte),null,!1,null,!1)}function LLe(e,n){var t;return t=1-n,e.a[t]=xB(e.a[t],t),xB(e,n)}function PLe(e,n){var t,i;return i=Rr(e,Ic),t=Gh(n,32),hh(t,i)}function _yn(e,n,t){var i;return i=u(e.Zb().xc(n),18),!!i&&i.Gc(t)}function $Le(e,n,t){var i;return i=u(e.Zb().xc(n),18),!!i&&i.Kc(t)}function RLe(e,n,t){var i;i=(Nt(e),new bs(e)),bxn(new G_e(i,n,t))}function XC(e,n,t){var i;i=(Nt(e),new bs(e)),gxn(new q_e(i,n,t))}function Lyn(e,n,t){e.a=n,e.c=t,e.b.a.$b(),qs(e.d),Qp(e.e.a.c,0)}function BLe(e,n){var t;e.e=new Soe,t=q2(n),Cr(t,e.c),fXe(e,t,0)}function Pyn(e,n){return new ZK(n,COe(pc(n.e),e,e),(Pn(),!0))}function $yn(e,n){return R4(),u(T(n,(Mu(),Hv)),15).a>=e.gc()}function Ryn(e){return cl(),!cc(e)&&!(!cc(e)&&e.c.i.c==e.d.i.c)}function dh(e){return u(Ra(e,oe(b7,K8,17,e.c.length,0,1)),323)}function Byn(e){UFe((!e.a&&(e.a=new pe(Jt,e,10,11)),e.a),new DM)}function Nae(){var e,n,t;return n=(t=(e=new hw,e),t),Te(u7e,n),n}function xu(e,n,t,i,r,c){return WBe(e,n,t,c),H1e(e,i),G1e(e,r),e}function zLe(e,n,t,i){return e.a+=""+of(n==null?Vo:su(n),t,i),e}function KC(e,n){if(e<0||e>=n)throw $(new jo(WTn(e,n)));return e}function FLe(e,n,t){if(e<0||nt)throw $(new jo(vTn(e,n,t)))}function Ee(e,n,t,i){var r;r=new zM,r.a=n,r.b=t,r.c=i,Vt(e.b,r)}function Gi(e,n,t,i){var r;r=new zM,r.a=n,r.b=t,r.c=i,Vt(e.a,r)}function zyn(e,n,t){var i;i=FEn();try{return Kpn(e,n,t)}finally{i9n(i)}}function Xb(e){var n;return uu(e)?(n=e,n==-0?0:n):e8n(e)}function JLe(e,n){return X(n,45)?UQ(e.a,u(n,45)):!1}function HLe(e,n){return X(n,45)?UQ(e.a,u(n,45)):!1}function GLe(e,n){return X(n,45)?UQ(e.a,u(n,45)):!1}function Fyn(e,n){return e.a<=e.b?(n.Bd(e.a++),!0):!1}function Jyn(e){return z3(e).dc()?!1:(Own(e,new ae),!0)}function Dae(e){var n;return A0(e),n=new rt,x3(e.a,new Fke(n)),n}function BR(e){var n;return A0(e),n=new lt,x3(e.a,new Jke(n)),n}function Hyn(e){if(!("stack"in e))try{throw e}catch{}return e}function zR(e){return new xo((ll(e,gZ),fB(mc(mc(5,e),e/10|0))))}function qLe(e){return u(Ra(e,oe(cin,lQe,12,e.c.length,0,1)),2004)}function Gyn(e){return iV(e.e.Pd().gc()*e.c.Pd().gc(),273,new JU(e))}function ULe(){ULe=Y,$ln=It((ZP(),z(B(P6e,1),ke,477,0,[Wre])))}function XLe(){XLe=Y,Bln=It((e$(),z(B(Rln,1),ke,546,0,[Zre])))}function KLe(){KLe=Y,ifn=It((Tj(),z(B(i9e,1),ke,527,0,[TD])))}function VLe(){VLe=Y,eye=TIe(ve(1),ve(4)),Z4e=TIe(ve(1),ve(2))}function FR(){FR=Y,xre=new Sse("DFS",0),Pye=new Sse("BFS",1)}function JR(){JR=Y,bie=new wse(F8,0),zve=new wse("TOP_LEFT",1)}function Iae(e,n,t){this.d=new tEe(this),this.e=e,this.i=n,this.f=t}function _ae(e,n,t,i){this.d=e,this.n=n,this.g=t,this.o=i,this.p=-1}function qyn(e,n,t){e.d&&qo(e.d.e,e),e.d=n,e.d&&Pb(e.d.e,t,e)}function Uyn(e,n,t){var i;return i=d8(t),Fz(e.n,i,n),Fz(e.o,n,t),n}function B9(e,n){var t,i;return t=T2(e,n),i=null,t&&(i=t.qe()),i}function gE(e,n){var t,i;return t=O1(e,n),i=null,t&&(i=t.qe()),i}function Ow(e,n){var t,i;return t=O1(e,n),i=null,t&&(i=t.ne()),i}function N1(e,n){var t,i;return t=O1(e,n),i=null,t&&(i=D0e(t)),i}function wE(e,n){$Rn(n,e),afe(e.d),afe(u(T(e,(Ne(),pH)),213))}function eY(e,n){RRn(n,e),hfe(e.d),hfe(u(T(e,(Ne(),pH)),213))}function C0(e,n){_n(n),e.b=e.b-1&e.a.length-1,tr(e.a,e.b,n),kHe(e)}function Lae(e,n){_n(n),tr(e.a,e.c,n),e.c=e.c+1&e.a.length-1,kHe(e)}function jt(e){return at(e.b!=e.d.c),e.c=e.b,e.b=e.b.a,++e.a,e.c.c}function YLe(e){if(e.e.g!=e.b)throw $(new Nl);return!!e.c&&e.d>0}function x2(e){return X(e,18)?u(e,18).dc():!e.Jc().Ob()}function Xyn(e){return new pn(N8n(u(e.a.kd(),18).gc(),e.a.jd()),16)}function QLe(e){var n;n=e.Dh(),this.a=X(n,72)?u(n,72).Gi():n.Jc()}function Pae(e,n){var t;return t=u(Pa(e.b,n),66),!t&&(t=new Si),t}function Kyn(e,n){var t;t=n.a,lc(t,n.c.d),Gr(t,n.d.d),N2(t.a,e.n)}function WLe(e,n,t,i){return X(t,59)?new kOe(e,n,t,i):new Nfe(e,n,t,i)}function Vyn(){return Ff(),z(B(Ein,1),ke,413,0,[am,w7,p7,$te])}function Yyn(){return Lw(),z(B(itn,1),ke,409,0,[XN,UN,pte,mte])}function Qyn(){return Z9(),z(B(Xtn,1),ke,408,0,[op,sm,om,xv])}function Wyn(){return H9(),z(B(dJ,1),ke,309,0,[lte,fte,ate,hte])}function Zyn(){return X4(),z(B(y3e,1),ke,383,0,[ZS,v3e,Cte,Ote])}function e6n(){return LB(),z(B(hin,1),ke,367,0,[Pte,zJ,FJ,WN])}function n6n(){return BE(),z(B(mve,1),ke,301,0,[ix,wve,eD,pve])}function t6n(){return B2(),z(B(Qie,1),ke,203,0,[xH,Yie,Fv,zv])}function i6n(){return F1(),z(B(F4e,1),ke,269,0,[ob,z4e,ere,nre])}function r6n(){return eg(),z(B(pon,1),ke,404,0,[mD,Mx,CH,TH])}function c6n(e){var n;return e.j==(De(),bt)&&(n=Wqe(e),cs(n,et))}function u6n(){return Y4(),z(B(iye,1),ke,398,0,[IH,Ox,Nx,Dx])}function ZLe(e,n){return u(Js(p2(u(pi(e.k,n),16).Mc(),Mv)),113)}function ePe(e,n){return u(Js(x4(u(pi(e.k,n),16).Mc(),Mv)),113)}function o6n(e,n){return w4(new je(n.e.a+n.f.a/2,n.e.b+n.f.b/2),e)}function s6n(){return kz(),z(B(cln,1),ke,401,0,[Fre,Rre,zre,Bre])}function l6n(){return az(),z(B(r6e,1),ke,354,0,[Lre,t6e,i6e,n6e])}function f6n(){return OE(),z(B(Lye,1),ke,353,0,[Sre,RH,Ere,jre])}function a6n(){return u8(),z(B(n8e,1),ke,278,0,[$D,uG,Z9e,e8e])}function h6n(){return z1(),z(B(Ace,1),ke,222,0,[xce,RD,H7,Gy])}function d6n(){return al(),z(B(Wfn,1),ke,292,0,[zD,s1,hb,BD])}function b6n(){return UR(),z(B(KD,1),ke,288,0,[S8e,A8e,Oce,x8e])}function g6n(){return Vs(),z(B(tA,1),ke,380,0,[qD,Og,GD,Lm])}function w6n(){return KB(),z(B(O8e,1),ke,326,0,[Nce,M8e,C8e,T8e])}function p6n(){return PB(),z(B(wan,1),ke,407,0,[Dce,D8e,N8e,I8e])}function Ll(e,n,t){return n<0?kW(e,t):u(t,69).uk().zk(e,e.ei(),n)}function m6n(e,n,t){var i;return i=d8(t),Fz(e.f,i,n),ei(e.g,n,t),n}function v6n(e,n,t){var i;return i=d8(t),Fz(e.p,i,n),ei(e.q,n,t),n}function nPe(e){var n,t;return n=(v0(),t=new w3,t),e&&Dz(n,e),n}function $ae(e){var n;return n=e.$i(e.i),e.i>0&&Wu(e.g,0,n,0,e.i),n}function I4(e){return Mj(),X(e.g,156)?u(e.g,156):null}function y6n(e){return $R(),so($ce,e)?u(Bn($ce,e),342).Pg():null}function k6n(e){e.a=null,e.e=null,Qp(e.b.c,0),Qp(e.f.c,0),e.c=null}function tPe(e,n){var t;for(t=e.j.c.length;t>24}function E6n(e){if(e.p!=1)throw $(new is);return Rt(e.k)<<24>>24}function S6n(e){if(e.p!=7)throw $(new is);return Rt(e.k)<<16>>16}function x6n(e){if(e.p!=7)throw $(new is);return Rt(e.f)<<16>>16}function H3(e,n){return n.e==0||e.e==0?KS:(A8(),TW(e,n))}function cPe(e,n){return ue(n)===ue(e)?"(this Map)":n==null?Vo:su(n)}function A6n(e,n,t){return dV(re(du(Xc(e.f,n))),re(du(Xc(e.f,t))))}function M6n(e,n,t){var i;i=u(Bn(e.g,t),60),Te(e.a.c,new jc(n,i))}function uPe(e,n){var t;return t=new o4,e.Ed(t),t.a+="..",n.Fd(t),t.a}function aa(e){var n;for(n=0;e.Ob();)e.Pb(),n=mc(n,1);return fB(n)}function T6n(e,n,t,i,r){var c;c=zOn(r,t,i),Te(n,GTn(r,c)),RMn(e,r,n)}function oPe(e,n,t){e.i=0,e.e=0,n!=t&&(Gze(e,n,t),Hze(e,n,t))}function sPe(e,n,t,i){this.e=null,this.c=e,this.d=n,this.a=t,this.b=i}function Rae(e,n,t,i,r){this.i=e,this.a=n,this.e=t,this.j=i,this.f=r}function lPe(e,n){hae.call(this),this.a=e,this.b=n,Te(this.a.b,this)}function D1(e,n){mh(),zb.call(this,e,1,z(B($t,1),ni,30,15,[n]))}function C6n(e,n,t){return C8(e,n,t,X(n,103)&&(u(n,19).Bb&Ec)!=0)}function HR(e,n,t){return Hz(e,n,t,X(n,103)&&(u(n,19).Bb&Ec)!=0)}function O6n(e,n,t){return DOn(e,n,t,X(n,103)&&(u(n,19).Bb&Ec)!=0)}function Bae(e,n){return e==(zn(),Qi)&&n==Qi?4:e==Qi||n==Qi?8:32}function N6n(e,n){return u(n==null?du(Xc(e.f,null)):Dj(e.i,n),290)}function fPe(e,n){var t;for(t=n;t;)a2(e,t.i,t.j),t=Bi(t);return e}function Vu(e){return e.n||(Ms(e),e.n=new $De(e,Rc,e),tu(e)),e.n}function Xh(e,n){Cc();var t;return t=u(e,69).tk(),QMn(t,n),t.vl(n)}function pE(e){return at(e.a"+Aae(e.d):"e_"+vw(e)}function I6n(e,n){var t;return t=n!=null?lo(e,n):du(Xc(e.f,n)),O$(t)}function _6n(e,n){var t;return t=n!=null?lo(e,n):du(Xc(e.f,n)),O$(t)}function bPe(e,n){var t;for(t=0;t=0&&e.a[t]===n[t];t--);return t<0}function B6n(e,n){var t,i;i=!1;do t=Dze(e,n),i=i|t;while(t);return i}function mE(){mE=Y,Cx=new vse("UPPER",0),Tx=new vse("LOWER",1)}function qR(){qR=Y,Sie=new pse(ma,0),Eie=new pse("ALTERNATING",1)}function UR(){UR=Y,S8e=new gDe,A8e=new QDe,Oce=new E_e,x8e=new WDe}function wPe(){wPe=Y,_in=It((nB(),z(B(gve,1),ke,422,0,[bve,Xte])))}function pPe(){pPe=Y,Rin=It((fO(),z(B(Sve,1),ke,419,0,[XJ,Eve])))}function mPe(){mPe=Y,Jin=It((rO(),z(B(Mve,1),ke,476,0,[Ave,VJ])))}function vPe(){vPe=Y,Yin=It((JR(),z(B(Fve,1),ke,420,0,[bie,zve])))}function yPe(){yPe=Y,Zin=It((qR(),z(B(n5e,1),ke,423,0,[Sie,Eie])))}function kPe(){kPe=Y,Uun=It((uO(),z(B(J4e,1),ke,421,0,[tre,ire])))}function jPe(){jPe=Y,Eon=It((mE(),z(B(jon,1),ke,518,0,[Cx,Tx])))}function EPe(){EPe=Y,Don=It((Da(),z(B(Non,1),ke,508,0,[Ag,Ya])))}function SPe(){SPe=Y,Oon=It((ah(),z(B(Con,1),ke,509,0,[pp,Ud])))}function xPe(){xPe=Y,Xon=It((ha(),z(B(Uon,1),ke,515,0,[Am,sb])))}function APe(){APe=Y,esn=It((Cw(),z(B(Zon,1),ke,454,0,[lb,Jv])))}function MPe(){MPe=Y,Msn=It((FR(),z(B($ye,1),ke,425,0,[xre,Pye])))}function TPe(){TPe=Y,Dsn=It((SB(),z(B(Rye,1),ke,487,0,[BH,qv])))}function CPe(){CPe=Y,Lsn=It((iB(),z(B(zye,1),ke,426,0,[Bye,Nre])))}function OPe(){OPe=Y,Nln=It((VR(),z(B(C6e,1),ke,478,0,[Kre,T6e])))}function NPe(){NPe=Y,zln=It((WC(),z(B($6e,1),ke,428,0,[ece,YH])))}function DPe(){DPe=Y,rfn=It((pO(),z(B(c9e,1),ke,427,0,[WH,r9e])))}function IPe(){IPe=Y,dtn=It((lB(),z(B(n3e,1),ke,424,0,[vte,pJ])))}function _Pe(){_Pe=Y,lin=It((K9(),z(B(sin,1),ke,502,0,[QN,Dte])))}function XR(e){w0e(),YCe(this,Rt(Rr(kw(e,24),rF)),Rt(Rr(e,rF)))}function z6n(e){return(e.k==(zn(),Qi)||e.k==wr)&&bi(e,(me(),ox))}function F6n(e,n,t){return u(n==null?Ko(e.f,null,t):Pw(e.i,n,t),290)}function J6n(){return vr(),z(B(Vx,1),ke,86,0,[eh,ru,Zc,Za,Vl])}function H6n(){return De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])}function G6n(e){return JP(),function(){return zyn(e,this,arguments)}}function LPe(e,n){var t;return t=n.jd(),new bw(t,e.e.pc(t,u(n.kd(),18)))}function PPe(e,n){var t,i;return t=n.jd(),i=e.De(t),!!i&&Ku(i.e,n.kd())}function rc(e,n){var t,i;for(_n(n),i=e.Jc();i.Ob();)t=i.Pb(),n.Ad(t)}function ol(e,n,t){var i;return i=(vn(n,e.c.length),e.c[n]),e.c[n]=t,i}function Hae(e,n){var t,i;for(t=n,i=0;t>0;)i+=e.a[t],t-=t&-t;return i}function $Pe(e,n){var t;for(t=n;t;)a2(e,-t.i,-t.j),t=Bi(t);return e}function q6n(e,n){var t;return t=e.a.get(n),t??oe(Ar,On,1,0,5,1)}function G3(e,n){return(R0(e),b9(new wn(e,new whe(n,e.a)))).zd(vy)}function U6n(){return zr(),z(B(p3e,1),ke,363,0,[Kf,r1,eo,no,Pc])}function RPe(e){eYe(),KSe(this),this.a=new Si,x1e(this,e),Vt(this.a,e)}function BPe(){MK(this),this.b=new je(Ki,Ki),this.a=new je(Dr,Dr)}function uY(e){KR(),!Ka&&(this.c=e,this.e=!0,this.a=new Ce)}function KR(){KR=Y,Ka=!0,pnn=!1,mnn=!1,ynn=!1,vnn=!1}function VR(){VR=Y,Kre=new Mse(bwe,0),T6e=new Mse("TARGET_WIDTH",1)}function X6n(){return vz(),z(B(Isn,1),ke,364,0,[Cre,Are,Ore,Mre,Tre])}function K6n(){return z2(),z(B(xin,1),ke,371,0,[ZN,GJ,qJ,HJ,JJ])}function V6n(){return HE(),z(B(_4e,1),ke,328,0,[I4e,Wie,Zie,jx,Ex])}function Y6n(){return Xs(),z(B(e5e,1),ke,165,0,[sD,fx,V1,ax,yg])}function Q6n(){return nS(),z(B(_ln,1),ke,369,0,[Uv,$y,Jx,Fx,MD])}function W6n(){return JO(),z(B(F6e,1),ke,330,0,[R6e,nce,z6e,tce,B6e])}function Z6n(){return ph(),z(B(Qa,1),ke,160,0,[Mn,fr,Sa,Kd,Q1])}function e9n(){return nv(),z(B(Zx,1),ke,257,0,[db,FD,g8e,Wx,w8e])}function oY(e,n){var t;return t=u(Pa(e.d,n),21),t||u(Pa(e.e,n),21)}function zPe(e){this.b=e,ut.call(this,e),this.a=u(Un(this.b.a,4),129)}function FPe(e){this.b=e,m4.call(this,e),this.a=u(Un(this.b.a,4),129)}function JPe(e,n){this.c=0,this.b=n,cCe.call(this,e,17493),this.a=this.c}function Pf(e,n,t,i,r){VPe.call(this,n,i,r),this.c=e,this.b=t}function Gae(e,n,t,i,r){pLe.call(this,n,i,r),this.c=e,this.a=t}function qae(e,n,t,i,r){mLe.call(this,n,i,r),this.c=e,this.a=t}function Uae(e,n,t,i,r){VPe.call(this,n,i,r),this.c=e,this.a=t}function Xae(e,n,t){e.a.c.length=0,MPn(e,n,t),e.a.c.length==0||ZIn(e,n)}function VC(e){e.i=0,rC(e.b,null),rC(e.c,null),e.a=null,e.e=null,++e.g}function n9n(e){return e.e=3,e.d=e.Yb(),e.e!=2?(e.e=0,!0):!1}function Kae(e,n){return X(n,144)?bn(e.c,u(n,144).c):!1}function HPe(e){var n;return e.c||(n=e.r,X(n,88)&&(e.c=u(n,29))),e.c}function Ms(e){return e.t||(e.t=new $Se(e),$E(new nAe(e),0,e.t)),e.t}function cc(e){return!e.c||!e.d?!1:!!e.c.i&&e.c.i==e.d.i}function _4(e,n){return n==0||e.e==0?e:n>0?fJe(e,n):QUe(e,-n)}function Vae(e,n){return n==0||e.e==0?e:n>0?QUe(e,n):fJe(e,-n)}function tt(e){if(ht(e))return e.c=e.a,e.a.Pb();throw $(new au)}function GPe(e){var n;return n=e.length,bn($n.substr($n.length-n,n),e)}function qPe(e){var n,t;return n=e.c.i,t=e.d.i,n.k==(zn(),wr)&&t.k==wr}function sY(e){var n,t,i;return n=e&Ls,t=e>>22&Ls,i=e<0?G1:0,_o(n,t,i)}function t9n(e,n){var t,i;t=u(Ykn(e.c,n),18),t&&(i=t.gc(),t.$b(),e.d-=i)}function i9n(e){e&&c8n((Toe(),yme)),--oJ,e&&sJ!=-1&&(Jgn(sJ),sJ=-1)}function Yae(e){_gn.call(this,e==null?Vo:su(e),X(e,80)?u(e,80):null)}function lY(e){var n;return n=new Mw,$u(n,e),he(n,(Ne(),Wc),null),n}function fY(e,n,t){var i;return i=e.Fh(n),i>=0?e.Ih(i,t,!0):Gw(e,n,t)}function r9n(e,n,t){return ki(w4(b8(e),pc(n.b)),w4(b8(e),pc(t.b)))}function c9n(e,n,t){return ki(w4(b8(e),pc(n.e)),w4(b8(e),pc(t.e)))}function u9n(e,n){return k.Math.min(N0(n.a,e.d.d.c),N0(n.b,e.d.d.c))}function UPe(e,n,t){var i;i=new Vse(e.a),xE(i,e.a.a),Ko(i.f,n,t),e.a.a=i}function Qae(e,n,t,i){var r;for(r=0;rn)throw $(new jo(F0e(e,n,"index")));return e}function ehe(e){var n;return n=e.e+e.f,isNaN(n)&&X$(e.d)?e.d:n}function s9n(e,n){var t;t=e.q.getHours()+(n/60|0),e.q.setMinutes(n),oS(e,t)}function nhe(e,n){var t,i;return t=(_n(e),e),i=(_n(n),n),t==i?0:tn.p?-1:0}function n$e(e,n){return so(e.a,n)?(L4(e.a,n),!0):!1}function h9n(e){var n,t;return n=e.jd(),t=u(e.kd(),18),NC(t.Lc(),new Z6(n))}function hY(e){var n;return n=e.b,n.b==0?null:u(Yu(n,0),65).b}function WR(e,n){return _n(n),e.c=0,"Initial capacity must not be negative")}function eB(){eB=Y,Hx=new yi("org.eclipse.elk.labels.labelManager")}function i$e(){i$e=Y,lve=new Li("separateLayerConnections",(LB(),Pte))}function ha(){ha=Y,Am=new jse("REGULAR",0),sb=new jse("CRITICAL",1)}function WC(){WC=Y,ece=new Tse("FIXED",0),YH=new Tse("CENTER_NODE",1)}function nB(){nB=Y,bve=new dse("QUADRATIC",0),Xte=new dse("SCANLINE",1)}function r$e(){r$e=Y,Pin=It((pB(),z(B(yve,1),ke,350,0,[vve,UJ,Kte])))}function c$e(){c$e=Y,zin=It((Wb(),z(B(Bin,1),ke,449,0,[tie,k7,Ov])))}function u$e(){u$e=Y,Uin=It((W9(),z(B(hie,1),ke,302,0,[fie,aie,tD])))}function o$e(){o$e=Y,Xin=It((_0(),z(B(die,1),ke,329,0,[iD,Bve,dm])))}function s$e(){s$e=Y,Vin=It((_1(),z(B(Kin,1),ke,315,0,[rD,Dv,Sy])))}function l$e(){l$e=Y,Nin=It((_w(),z(B(Rte,1),ke,368,0,[lp,ib,sp])))}function f$e(){f$e=Y,Fun=It((IE(),z(B(D4e,1),ke,352,0,[Vie,N4e,SH])))}function a$e(){a$e=Y,Kun=It((Nc(),z(B(Xun,1),ke,452,0,[xx,ys,Do])))}function h$e(){h$e=Y,Vun=It((DB(),z(B(q4e,1),ke,381,0,[H4e,rre,G4e])))}function d$e(){d$e=Y,Yun=It((DE(),z(B(U4e,1),ke,348,0,[ure,cre,pD])))}function b$e(){b$e=Y,Qun=It((r8(),z(B(K4e,1),ke,349,0,[ore,X4e,Ax])))}function g$e(){g$e=Y,Wun=It((wB(),z(B(Q4e,1),ke,351,0,[Y4e,sre,V4e])))}function w$e(){w$e=Y,Zun=It((IB(),z(B(W4e,1),ke,382,0,[lre,I7,xm])))}function p$e(){p$e=Y,isn=It((NE(),z(B(gye,1),ke,385,0,[bye,hre,yD])))}function m$e(){m$e=Y,Psn=It((EO(),z(B(Hye,1),ke,386,0,[zH,Fye,Jye])))}function v$e(){v$e=Y,iln=It((NB(),z(B(o6e,1),ke,303,0,[Pre,u6e,c6e])))}function y$e(){y$e=Y,rln=It((WB(),z(B(s6e,1),ke,436,0,[Px,HH,$re])))}function k$e(){k$e=Y,Dln=It((rB(),z(B(D6e,1),ke,429,0,[Vre,N6e,O6e])))}function j$e(){j$e=Y,Iln=It((JB(),z(B(L6e,1),ke,430,0,[I6e,_6e,Yre])))}function E$e(){E$e=Y,Pln=It((TO(),z(B(Qre,1),ke,435,0,[XH,KH,VH])))}function S$e(){S$e=Y,lln=It((XB(),z(B(a6e,1),ke,387,0,[f6e,Gre,l6e])))}function x$e(){x$e=Y,Btn=It((yE(),z(B(w3e,1),ke,384,0,[Ete,jte,Ste])))}function A$e(){A$e=Y,knn=It((zl(),z(B(Qo,1),ke,130,0,[Kme,Yo,Vme])))}function M$e(){M$e=Y,Cnn=It((ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])))}function T$e(){T$e=Y,Nnn=It((ws(),z(B(Onn,1),ke,461,0,[Th,nb,Uf])))}function C$e(){C$e=Y,Inn=It((Uo(),z(B(Dnn,1),ke,462,0,[ka,tb,Xf])))}function O$e(){O$e=Y,Xfn=It(($a(),z(B(t8e,1),ke,279,0,[F7,_m,J7])))}function N$e(){N$e=Y,han=It((G4(),z(B(E8e,1),ke,281,0,[j8e,Pm,dG])))}function D$e(){D$e=Y,Qfn=It((B1(),z(B(b8e,1),ke,347,0,[oG,Yd,Qx])))}function I$e(){I$e=Y,lan=It((jE(),z(B(y8e,1),ke,300,0,[HD,Tce,v8e])))}function da(e,n){return!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),SQ(e.o,n)}function b9n(e){return!e.g&&(e.g=new J6),!e.g.d&&(e.g.d=new _Se(e)),e.g.d}function g9n(e){return!e.g&&(e.g=new J6),!e.g.b&&(e.g.b=new ISe(e)),e.g.b}function ZC(e){return!e.g&&(e.g=new J6),!e.g.c&&(e.g.c=new PSe(e)),e.g.c}function w9n(e){return!e.g&&(e.g=new J6),!e.g.a&&(e.g.a=new LSe(e)),e.g.a}function p9n(e,n,t,i){return t&&(i=t.Oh(n,zi(t.Ah(),e.c.sk()),null,i)),i}function m9n(e,n,t,i){return t&&(i=t.Qh(n,zi(t.Ah(),e.c.sk()),null,i)),i}function dY(e,n,t,i){var r;return r=oe($t,ni,30,n+1,15,1),__n(r,e,n,t,i),r}function oe(e,n,t,i,r,c){var o;return o=hHe(r,i),r!=10&&z(B(e,c),n,t,r,o),o}function v9n(e,n,t){var i,r;for(r=new Y9(n,e),i=0;it||n=0?e.Ih(t,!0,!0):Gw(e,n,!0)}function eO(e,n){var t,i,r;return r=e.r,i=e.d,t=fS(e,n,!0),t.b!=r||t.a!=i}function $$e(e,n){return PMe(e.e,n)||tg(e.e,n,new PJe(n)),u(Pa(e.e,n),113)}function Ts(e,n,t,i){return _n(e),_n(n),_n(t),_n(i),new Bfe(e,n,new Eu)}function nO(e,n,t){var i,r;return r=(i=E8(e.b,n),i),r?Kz(oO(e,r),t):null}function L9n(e,n,t){var i,r,c;i=O1(e,t),r=null,i&&(r=D0e(i)),c=r,IJe(n,t,c)}function P9n(e,n,t){var i,r,c;i=O1(e,t),r=null,i&&(r=D0e(i)),c=r,IJe(n,t,c)}function os(e,n,t,i){this.$j(),this.a=n,this.b=e,this.c=new Lfe(this,n,t,i)}function pY(e,n,t,i,r,c){_ae.call(this,n,i,r,c),this.c=e,this.b=t}function tO(e,n,t,i,r,c){_ae.call(this,n,i,r,c),this.c=e,this.a=t}function ghe(e,n,t,i,r){zCe(this),this.b=e,this.d=n,this.f=t,this.g=i,this.c=r}function whe(e,n){N$.call(this,n.xd(),n.wd()&-16449),_n(e),this.a=e,this.c=n}function $9n(e,n){e.a.Le(n.d,e.b)>0&&(Te(e.c,new ofe(n.c,n.d,e.d)),e.b=n.d)}function mY(e){e.a=oe($t,ni,30,e.b+1,15,1),e.c=oe($t,ni,30,e.b,15,1),e.d=0}function R9n(e,n,t){var i;return i=Rze(e,n,t),e.b=new AB(i.c.length),Dbe(e,i)}function B9n(e){if(e.b<=0)throw $(new au);return--e.b,e.a-=e.c.c,ve(e.a)}function z9n(e){var n;if(!e.a)throw $(new s_e);return n=e.a,e.a=Bi(e.a),n}function R$e(e){var n;if(e.ll())for(n=e.i-1;n>=0;--n)K(e,n);return $ae(e)}function $4(e){var n;return Nt(e),X(e,204)?(n=u(e,204),n):new e9(e)}function F9n(e){for(;!e.a;)if(!ENe(e.c,new Hke(e)))return!1;return!0}function phe(e,n){if(e.g==null||n>=e.i)throw $(new jK(n,e.i));return e.g[n]}function B$e(e,n,t){if(t8(e,t),t!=null&&!e.dk(t))throw $(new bX);return t}function vY(e,n){return lO(n)!=10&&z(Us(n),n.Qm,n.__elementTypeId$,lO(n),e),e}function z$e(e,n){var t,i;return i=n/e.c.Pd().gc()|0,t=n%e.c.Pd().gc(),P4(e,i,t)}function J9(e,n,t,i){var r;i=(Aw(),i||Fme),r=e.slice(n,t),J0e(r,e,n,t,-n,i)}function Pl(e,n,t,i,r){return n<0?Gw(e,t,i):u(t,69).uk().wk(e,e.ei(),n,i,r)}function J9n(e,n){return ki(ne(re(T(e,(me(),hp)))),ne(re(T(n,hp))))}function F$e(){F$e=Y,gnn=It((H9(),z(B(dJ,1),ke,309,0,[lte,fte,ate,hte])))}function H9(){H9=Y,lte=new c$("All",0),fte=new ACe,ate=new RCe,hte=new MCe}function ws(){ws=Y,Th=new qX(ly,0),nb=new qX(F8,1),Uf=new qX(fy,2)}function J$e(){J$e=Y,Gz(),b7e=Ki,vhn=Dr,g7e=new Zn(Ki),yhn=new Zn(Dr)}function tB(){tB=Y,ofn=new g3,lfn=new eL,sfn=tkn((Xt(),jce),ofn,ab,lfn)}function H9n(e){tB(),u(e.mf((Xt(),Nm)),182).Ec((ps(),JD)),e.of(jce,null)}function G9n(e){return X(e,180)?""+u(e,180).a:e==null?null:su(e)}function q9n(e){return X(e,180)?""+u(e,180).a:e==null?null:su(e)}function mhe(e){var n,t;if(!e.b)return null;for(t=e.b;n=t.a[0];)t=n;return t}function H$e(e){var n,t;if(!e.b)return null;for(t=e.b;n=t.a[1];)t=n;return t}function iO(e){var n;for(n=e.p+1;n=0?uz(e,t,!0,!0):Gw(e,n,!0)}function Q9n(e,n){k4(u(u(e.f,26).mf((Xt(),Kx)),102))&&UFe(iae(u(e.f,26)),n)}function pRe(e,n){Os(e,n==null||X$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function mRe(e,n){Ns(e,n==null||X$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function vRe(e,n){Iw(e,n==null||X$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function yRe(e,n){Dw(e,n==null||X$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function kRe(e){(this.q?this.q:(jn(),jn(),i1)).zc(e.q?e.q:(jn(),jn(),i1))}function SY(e,n,t){var i;return i=e.g[n],Yj(e,n,e.Xi(n,t)),e.Pi(n,t,i),e.Li(),i}function sB(e,n){var t;return t=e.bd(n),t>=0?(e.ed(t),!0):!1}function xY(e){var n;return e.d!=e.r&&(n=ff(e),e.e=!!n&&n.jk()==ZZe,e.d=n),e.e}function AY(e,n){var t;for(Nt(e),Nt(n),t=!1;n.Ob();)t=t|e.Ec(n.Pb());return t}function Pa(e,n){var t;return t=u(Bn(e.e,n),393),t?(VCe(e,t),t.e):null}function jRe(e){var n,t;return n=e/60|0,t=e%60,t==0?""+n:""+n+":"+(""+t)}function ou(e,n){var t,i;return R0(e),i=new the(n,e.a),t=new kNe(i),new wn(e,t)}function T2(e,n){var t=e.a[n],i=(QY(),ite)[typeof t];return i?i(t):F1e(typeof t)}function W9n(e,n){var t,i,r;r=n.c.i,t=u(Bn(e.f,r),60),i=t.d.c-t.e.c,Zhe(n.a,i,0)}function Kh(e,n,t){var i,r;for(i=10,r=0;r=0;)++n[0]}function MRe(e,n,t,i){fi(),aw.call(this,26),this.c=e,this.a=n,this.d=t,this.b=i}function L1(e,n,t,i,r,c,o){DY.call(this,n,i,r,c,o),this.c=e,this.b=t}function TRe(e){this.g=e,this.f=new Ce,this.a=k.Math.min(this.g.c.c,this.g.d.c)}function kE(){kE=Y,Qtn=new xp,Wtn=new Ap,Vtn=new Mp,Ytn=new Tp,Ztn=new xl}function lB(){lB=Y,vte=new fse("EADES",0),pJ=new fse("FRUCHTERMAN_REINGOLD",1)}function fO(){fO=Y,XJ=new bse("READING_DIRECTION",0),Eve=new bse("ROTATION",1)}function CRe(){CRe=Y,Ain=It((z2(),z(B(xin,1),ke,371,0,[ZN,GJ,qJ,HJ,JJ])))}function ORe(){ORe=Y,Hun=It((HE(),z(B(_4e,1),ke,328,0,[I4e,Wie,Zie,jx,Ex])))}function NRe(){NRe=Y,Win=It((Xs(),z(B(e5e,1),ke,165,0,[sD,fx,V1,ax,yg])))}function DRe(){DRe=Y,_sn=It((vz(),z(B(Isn,1),ke,364,0,[Cre,Are,Ore,Mre,Tre])))}function IRe(){IRe=Y,Lln=It((nS(),z(B(_ln,1),ke,369,0,[Uv,$y,Jx,Fx,MD])))}function _Re(){_Re=Y,Fln=It((JO(),z(B(F6e,1),ke,330,0,[R6e,nce,z6e,tce,B6e])))}function LRe(){LRe=Y,Htn=It((zr(),z(B(p3e,1),ke,363,0,[Kf,r1,eo,no,Pc])))}function PRe(){PRe=Y,qfn=It((vr(),z(B(Vx,1),ke,86,0,[eh,ru,Zc,Za,Vl])))}function $Re(){$Re=Y,ffn=It((ph(),z(B(Qa,1),ke,160,0,[Mn,fr,Sa,Kd,Q1])))}function RRe(){RRe=Y,nan=It((nv(),z(B(Zx,1),ke,257,0,[db,FD,g8e,Wx,w8e])))}function BRe(){BRe=Y,ran=It((De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])))}function zRe(e){var n;return n=u(T(e,(me(),fp)),317),n?n.a==e:!1}function FRe(e){var n;return n=u(T(e,(me(),fp)),317),n?n.i==e:!1}function JRe(e,n){return _n(n),Dfe(e),e.d.Ob()?(n.Ad(e.d.Pb()),!0):!1}function fB(e){return ao(e,oi)>0?oi:ao(e,Xr)<0?Xr:Rt(e)}function o8n(e,n){var t;return t=$w(e.e.c,n.e.c),t==0?ki(e.e.d,n.e.d):t}function TY(e,n){var t;return t=u(Bn(e.a,n),150),t||(t=new Mt,ei(e.a,n,t)),t}function Rf(e,n,t){var i;if(n==null)throw $(new e4);return i=O1(e,n),D6n(e,n,t),i}function s8n(e,n){var t,i;for(i=n.c,t=i+1;t<=n.f;t++)e.a[t]>e.a[i]&&(i=t);return i}function l8n(e,n,t){var i;return i=e.a.e[u(n.a,9).p]-e.a.e[u(t.a,9).p],sc(LC(i))}function f8n(e,n,t){var i,r;for(r=new L(t);r.a0?n-1:n,wAe(ugn(aBe(dfe(new i4,t),e.n),e.j),e.k)}function p8n(e,n,t,i){var r;e.j=-1,nbe(e,I0e(e,n,t),(Cc(),r=u(n,69).tk(),r.vl(i)))}function XRe(e,n,t,i,r,c){var o;o=lY(i),lc(o,r),Gr(o,c),gn(e.a,i,new Y$(o,n,t.f))}function aB(e,n){var t;return R0(e),t=new e_e(e,e.a.xd(),e.a.wd()|4,n),new wn(e,t)}function m8n(e,n){var t,i;return t=u(L2(e.d,n),18),t?(i=n,e.e.pc(i,t)):null}function An(e,n){var t;return t=(e.i==null&&vh(e),e.i),n>=0&&n=-.01&&e.a<=Ga&&(e.a=0),e.b>=-.01&&e.b<=Ga&&(e.b=0),e}function q3(e){x8();var n,t;for(t=Fpe,n=0;nt&&(t=e[n]);return t}function v8n(e){var n;return n=ne(re(T(e,(Ne(),Gd)))),n<0&&(n=0,he(e,Gd,n)),n}function y8n(e,n){k4(u(T(u(e.e,9),(Ne(),Wi)),102))&&(jn(),Cr(u(e.e,9).j,n))}function hB(e,n){var t,i;for(i=e.Jc();i.Ob();)t=u(i.Pb(),70),he(t,(me(),Cy),n)}function k8n(e,n){var t,i,r;for(i=n.a.jd(),t=u(n.a.kd(),18).gc(),r=0;re||e>n)throw $(new Ioe("fromIndex: 0, toIndex: "+e+Xge+n))}function QRe(e,n){ji(e,(Yh(),Hre),n.f),ji(e,sln,n.e),ji(e,Jre,n.d),ji(e,oln,n.c)}function Ao(e,n){var t,i,r,c;for(_n(n),i=e.c,r=0,c=i.length;r0&&(e.a/=n,e.b/=n),e}function WRe(e,n,t){var i,r;i=n;do r=ne(e.p[i.p])+t,e.p[i.p]=r,i=e.a[i.p];while(i!=n)}function sl(e){var n;return e.w?e.w:(n=wyn(e),n&&!n.Sh()&&(e.w=n),n)}function Ahe(e,n){return Oa(),Bf(X0),k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)}function C8n(e){var n;return e==null?null:(n=u(e,195),pMn(n,n.length))}function K(e,n){if(e.g==null||n>=e.i)throw $(new jK(n,e.i));return e.Ui(n,e.g[n])}function ga(){ga=Y,Ou=new GX("BEGIN",0),No=new GX(F8,1),Nu=new GX("END",2)}function $a(){$a=Y,F7=new wK(F8,0),_m=new wK("HEAD",1),J7=new wK("TAIL",2)}function R4(){R4=Y,Csn=wh(wh(wh(Oj(new or,(Y4(),Ox)),(cS(),are)),oye),aye)}function P1(){P1=Y,Nsn=wh(wh(wh(Oj(new or,(Y4(),Dx)),(cS(),lye)),rye),sye)}function U3(e,n){return agn(AE(e,n,Rt(ac(Zh,Uh(Rt(ac(n==null?0:Oi(n),e1)),15)))))}function Mhe(e,n){return Oa(),Bf(X0),k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)}function q9(e,n){var t,i;i=e.a,t=fjn(e,n,null),i!=n&&!e.e&&(t=D8(e,n,t)),t&&t.mj()}function O8n(e,n){var t;return t=Nr(pc(u(Bn(e.g,n),8)),Use(u(Bn(e.f,n),460).b)),t}function ZRe(e,n,t){var i=function(){return e.apply(i,arguments)};return n.apply(i,t),i}function B4(e){var n;return nE(e==null||Array.isArray(e)&&(n=lO(e),!(n>=14&&n<=16))),e}function The(e){e.b=(ws(),nb),e.f=(Uo(),tb),e.d=(ll(2,Q2),new xo(2)),e.e=new Kr}function dB(e){this.b=(Nt(e),new bs(e)),this.a=new Ce,this.d=new Ce,this.e=new Kr}function eBe(e){return R0(e),E4(!0,"n may not be negative"),new wn(e,new vBe(e.a))}function N8n(e,n){jn();var t,i;for(i=new Ce,t=0;t0?u(Le(t.a,i-1),9):null}function Bf(e){if(!(e>=0))throw $(new Gn("tolerance ("+e+") must be >= 0"));return e}function EE(){return uce||(uce=new DXe,H4(uce,z(B(yy,1),On,148,0,[new TT]))),uce}function wB(){wB=Y,Y4e=new cK("NO",0),sre=new cK(bwe,1),V4e=new cK("LOOK_BACK",2)}function Nc(){Nc=Y,xx=new nK(vS,0),ys=new nK("INPUT",1),Do=new nK("OUTPUT",2)}function pB(){pB=Y,vve=new KX("ARD",0),UJ=new KX("MSD",1),Kte=new KX("MANUAL",2)}function R8n(){return KO(),z(B(jve,1),ke,267,0,[Qte,kve,Zte,eie,Wte,nie,nD,Yte,Vte])}function B8n(){return YO(),z(B(O4e,1),ke,268,0,[Kie,M4e,T4e,Uie,A4e,C4e,EH,qie,Xie])}function z8n(){return _s(),z(B(k8e,1),ke,266,0,[q7,XD,lG,iA,fG,hG,aG,Cce,UD])}function F8n(){yMe();for(var e=Xne,n=0;nt)throw $(new b2(n,t));return new Xle(e,n)}function mB(e){var n,t;for(t=e.c.Bc().Jc();t.Ob();)n=u(t.Pb(),18),n.$b();e.c.$b(),e.d=0}function J8n(e){var n,t,i,r;for(t=e.a,i=0,r=t.length;i=0),xEn(e.d,e.c)<0&&(e.a=e.a-1&e.d.a.length-1,e.b=e.d.c),e.c=-1}function vBe(e){N$.call(this,e.yd(64)?Gse(0,lf(e.xd(),1)):hN,e.wd()),this.b=1,this.a=e}function yBe(){cle.call(this),this.n=-1,this.g=null,this.i=null,this.j=null,this.Bb|=qf}function kBe(e,n,t,i){this.$j(),this.a=n,this.b=e,this.c=null,this.c=new pNe(this,n,t,i)}function DY(e,n,t,i,r){this.d=e,this.n=n,this.g=t,this.o=i,this.p=-1,r||(this.o=-2-i-1)}function jBe(e){Woe(),this.g=new pt,this.f=new pt,this.b=new pt,this.c=new Tw,this.i=e}function $he(){this.f=new Kr,this.d=new moe,this.c=new Kr,this.a=new Ce,this.b=new Ce}function G8n(e){var n,t;for(t=new L(mHe(e));t.a=0}function Rhe(){Rhe=Y,oon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function EBe(){EBe=Y,son=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function Bhe(){Bhe=Y,lon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function SBe(){SBe=Y,fon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function xBe(){xBe=Y,aon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function ABe(){ABe=Y,hon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function MBe(){MBe=Y,gon=Eo(qt(qt(new or,(zr(),eo),(Ur(),DJ)),no,MJ),Pc,NJ)}function TBe(){TBe=Y,enn=z(B($t,1),ni,30,15,[0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15])}function zhe(e,n){var t;t=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,0,t,e.b))}function Fhe(e,n){var t;t=e.c,e.c=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,1,t,e.c))}function IY(e,n){var t;t=e.c,e.c=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,4,t,e.c))}function Jhe(e,n){var t;t=e.c,e.c=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,1,t,e.c))}function Hhe(e,n){var t;t=e.d,e.d=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,1,t,e.d))}function X9(e,n){var t;t=e.k,e.k=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,2,t,e.k))}function _Y(e,n){var t;t=e.D,e.D=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,2,t,e.D))}function kB(e,n){var t;t=e.f,e.f=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,8,t,e.f))}function jB(e,n){var t;t=e.i,e.i=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,7,t,e.i))}function Ghe(e,n){var t;t=e.a,e.a=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,8,t,e.a))}function qhe(e,n){var t;t=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,0,t,e.b))}function X8n(e,n,t){var i;e.b=n,e.a=t,i=(e.a&512)==512?new _xe:new gT,e.c=SDn(i,e.b,e.a)}function CBe(e,n){return J1(e.e,n)?(Cc(),xY(n)?new rR(n,e):new pC(n,e)):new nCe(n,e)}function K8n(e){var n,t;return 0>e?new Yoe:(n=e+1,t=new JPe(n,e),new Ale(null,t))}function V8n(e,n){jn();var t;return t=new l4(1),$r(e)?Kc(t,e,n):Ko(t.f,e,n),new fX(t)}function Y8n(e,n){var t;t=new Ug,u(n.b,68),u(n.b,68),u(n.b,68),Ao(n.a,new ife(e,t,n))}function OBe(e,n){var t;return X(n,8)?(t=u(n,8),e.a==t.a&&e.b==t.b):!1}function Q8n(e){var n;return n=T(e,(me(),wi)),X(n,174)?QFe(u(n,174)):null}function NBe(e){var n;return e=k.Math.max(e,2),n=b1e(e),e>n?(n<<=1,n>0?n:bS):n}function LY(e){switch(ile(e.e!=3),e.e){case 2:return!1;case 0:return!0}return n9n(e)}function Uhe(e){var n;return e.b==null?(kd(),kd(),nI):(n=e.sl()?e.rl():e.ql(),n)}function DBe(e,n){var t,i;for(i=n.vc().Jc();i.Ob();)t=u(i.Pb(),45),RO(e,t.jd(),t.kd())}function Xhe(e,n){var t;t=e.d,e.d=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,11,t,e.d))}function EB(e,n){var t;t=e.j,e.j=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,13,t,e.j))}function Khe(e,n){var t;t=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,21,t,e.b))}function Vhe(e,n){e.r>0&&e.c0&&e.g!=0&&Vhe(e.i,n/e.r*e.i.d))}function IBe(e,n,t){var i,r,c;for(c=e.a.length-1,r=e.b,i=0;i0?1:0:(!e.c&&(e.c=qC(Pu(e.f))),e.c).e}function HBe(e,n){n?e.B==null&&(e.B=e.D,e.D=null):e.B!=null&&(e.D=e.B,e.B=null)}function t7n(e,n){n.Tg(vQe,1),Zi(ou(new wn(null,new pn(e.b,16)),new Vg),new vI),n.Ug()}function zY(e,n,t,i,r,c){var o;this.c=e,o=new Ce,_de(e,o,n,e.b,t,i,r,c),this.a=new qr(o,0)}function ir(e,n,t,i,r,c,o,l,f,h,b,p,y){return cqe(e,n,t,i,r,c,o,l,f,h,b,p,y),pQ(e,!1),e}function i7n(e,n){typeof window===sN&&typeof window.$gwt===sN&&(window.$gwt[e]=n)}function r7n(e,n,t){var i,r,c;for(i=0,r=0;r>>31;i!=0&&(e[t]=i)}function c7n(e,n,t){t.Tg("DFS Treeifying phase",1),gEn(e,n),UNn(e,n),e.a=null,e.b=null,t.Ug()}function u7n(e,n){var t;n.Tg("General Compactor",1),t=Qjn(u(ye(e,(J0(),Ire)),386)),t.Bg(e)}function o7n(e,n){var t,i;return t=u(ye(e,(J0(),FH)),15),i=u(ye(n,FH),15),oo(t.a,i.a)}function Zhe(e,n,t){var i,r;for(r=St(e,0);r.b!=r.d.c;)i=u(jt(r),8),i.a+=n,i.b+=t;return e}function s7n(e,n,t,i){var r;r=new r4,Hb(r,"x",pz(e,n,i.a)),Hb(r,"y",mz(e,n,i.b)),T4(t,r)}function l7n(e,n,t,i){var r;r=new r4,Hb(r,"x",pz(e,n,i.a)),Hb(r,"y",mz(e,n,i.b)),T4(t,r)}function f7n(){return G0(),z(B(B4e,1),ke,243,0,[AH,gD,wD,P4e,$4e,L4e,R4e,MH,D7,Sx])}function a7n(){return Dc(),z(B(lie,1),ke,261,0,[QJ,Kl,cx,WJ,S7,Nv,ux,j7,E7,ZJ])}function FY(){FY=Y,sA=new Cxe,zce=z(B(ns,1),jv,179,0,[]),Qan=z(B(yf,1),ime,62,0,[])}function z4(){z4=Y,Lte=new Li("edgelabelcenterednessanalysis.includelabel",(Pn(),eb))}function GBe(e,n){return ne(re(Js(AO(So(new wn(null,new pn(e.c.b,16)),new Yje(e)),n))))}function e1e(e,n){return ne(re(Js(AO(So(new wn(null,new pn(e.c.b,16)),new Vje(e)),n))))}function Oi(e){return $r(e)?Od(e):s2(e)?b4(e):o2(e)?GOe(e):Cfe(e)?e.Hb():Sfe(e)?vw(e):aae(e)}function qBe(e,n){return Oa(),Bf(Ga),k.Math.abs(0-n)<=Ga||n==0||isNaN(0)&&isNaN(n)?0:e/n}function h7n(e,n){return Z9(),e==op&&n==om||e==op&&n==xv||e==sm&&n==xv||e==sm&&n==om}function d7n(e,n){return Z9(),e==op&&n==sm||e==sm&&n==op||e==xv&&n==om||e==om&&n==xv}function ss(){ss=Y,A3e=new w5,S3e=new s0,x3e=new Ih,E3e=new ck,M3e=new CA,T3e=new p5}function b7n(e){var n;return n=BR(e),Hj(n.a,0)?(YP(),YP(),dnn):(YP(),new EOe(n.b))}function JY(e){var n;return n=Dae(e),Hj(n.a,0)?(i2(),i2(),ste):(i2(),new PK(n.b))}function HY(e){var n;return n=Dae(e),Hj(n.a,0)?(i2(),i2(),ste):(i2(),new PK(n.c))}function g7n(e){return e.b.c.i.k==(zn(),wr)?u(T(e.b.c.i,(me(),wi)),12):e.b.c}function UBe(e){return e.b.d.i.k==(zn(),wr)?u(T(e.b.d.i,(me(),wi)),12):e.b.d}function XBe(e){switch(e.g){case 2:return De(),Kn;case 4:return De(),et;default:return e}}function KBe(e){switch(e.g){case 1:return De(),bt;case 3:return De(),Xn;default:return e}}function w7n(e,n){var t;return t=m0e(e),V0e(new je(t.c,t.d),new je(t.b,t.a),e.Kf(),n,e.$f())}function p7n(e,n){n.Tg(vQe,1),ode(jgn(new TP((Aj(),new DV(e,!1,!1,new rk))))),n.Ug()}function n1e(){n1e=Y,won=wh(uCe(qt(qt(new or,(zr(),eo),(Ur(),DJ)),no,MJ),Pc),NJ)}function VBe(){VBe=Y,yon=wh(uCe(qt(qt(new or,(zr(),eo),(Ur(),DJ)),no,MJ),Pc),NJ)}function YBe(e,n,t){this.g=e,this.d=n,this.e=t,this.a=new Ce,oCn(this),jn(),Cr(this.a,null)}function Bl(e,n,t,i,r,c,o){Ot.call(this,e,n),this.d=t,this.e=i,this.c=r,this.b=c,this.a=$f(o)}function t1e(e){this.i=e.gc(),this.i>0&&(this.g=this.$i(this.i+(this.i/8|0)+1),e.Oc(this.g))}function xE(e,n){var t,i;for(_n(n),i=n.vc().Jc();i.Ob();)t=u(i.Pb(),45),e.yc(t.jd(),t.kd())}function m7n(e,n,t){var i;for(i=t.Jc();i.Ob();)if(!HR(e,n,i.Pb()))return!1;return!0}function AE(e,n,t){var i;for(i=e.b[t&e.f];i;i=i.b)if(t==i.a&&T1(n,i.g))return i;return null}function ME(e,n,t){var i;for(i=e.c[t&e.f];i;i=i.d)if(t==i.f&&T1(n,i.i))return i;return null}function v7n(e,n){var t;for(Nt(n);e.Ob();)if(t=e.Pb(),!u1e(u(t,9)))return!1;return!0}function y7n(e,n,t,i,r){var c;return t&&(c=zi(n.Ah(),e.c),r=t.Oh(n,-1-(c==-1?i:c),null,r)),r}function k7n(e,n,t,i,r){var c;return t&&(c=zi(n.Ah(),e.c),r=t.Qh(n,-1-(c==-1?i:c),null,r)),r}function QBe(e){var n;if(e.b==-2){if(e.e==0)n=-1;else for(n=0;e.a[n]==0;n++);e.b=n}return e.b}function j7n(e){var n,t,i;return e.j==(De(),Xn)&&(n=Wqe(e),t=cs(n,et),i=cs(n,Kn),i||i&&t)}function E7n(e){var n,t,i;for(i=0,t=new L(e.b);t.ar&&n.ac&&n.br?t=r:Yn(n,t+1),e.a=of(e.a,0,n)+(""+i)+qfe(e.a,t)}function WBe(e,n,t,i){X(e.Cb,184)&&(u(e.Cb,184).tb=null),Mo(e,t),n&&ECn(e,n),i&&e.el(!0)}function A7n(e,n){var t,i;for(i=new L(n.b);i.a1||e.Ob())return++e.a,e.g=0,n=e.i,e.Ob(),n;throw $(new au)}function _7n(e,n){var t,i;for(i=new L(n);i.a>22),r=e.h+n.h+(i>>22),_o(t&Ls,i&Ls,r&G1)}function xze(e,n){var t,i,r;return t=e.l-n.l,i=e.m-n.m+(t>>22),r=e.h-n.h+(i>>22),_o(t&Ls,i&Ls,r&G1)}function YY(e){var n,t,i,r;for(r=new Ce,i=e.Jc();i.Ob();)t=u(i.Pb(),26),n=q2(t),Er(r,n);return r}function Z7n(e){var n;$d(e,!0),n=Rd,bi(e,(Ne(),C7))&&(n+=u(T(e,C7),15).a),he(e,C7,ve(n))}function Aze(e,n,t){var i;Hu(e.a),Ao(t.i,new VEe(e)),i=new _$(u(Bn(e.a,n.b),68)),EJe(e,i,n),t.f=i}function l1e(e){var n,t;return t=(v0(),n=new yo,n),e&&Et((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),t),t}function F4(e,n){var t,i;if(i=0,e<64&&e<=n)for(n=n<64?n:63,t=e;t<=n;t++)i=hh(i,Gh(1,t));return i}function ekn(e,n){var t,i;for(CR(n,"predicate"),i=0;e.Ob();i++)if(t=e.Pb(),n.Lb(t))return i;return-1}function f1e(e,n){if(n===0){!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),e.o.c.$b();return}wW(e,n)}function Mze(e){switch(e.g){case 1:return hb;case 2:return s1;case 3:return BD;default:return zD}}function a1e(e){jn();var n,t,i;for(i=0,t=e.Jc();t.Ob();)n=t.Pb(),i=i+(n!=null?Oi(n):0),i=i|0;return i}function nkn(e){var n;return n=new hn,n.a=e,n.b=okn(e),n.c=oe(ze,Se,2,2,6,1),n.c[0]=JBe(e),n.c[1]=JBe(e),n}function LB(){LB=Y,Pte=new f$(ma,0),zJ=new f$(jQe,1),FJ=new f$(EQe,2),WN=new f$("BOTH",3)}function Z9(){Z9=Y,op=new s$("Q1",0),sm=new s$("Q4",1),om=new s$("Q2",2),xv=new s$("Q3",3)}function _0(){_0=Y,iD=new WX("ONLY_WITHIN_GROUP",0),Bve=new WX(ZZ,1),dm=new WX("ENFORCED",2)}function Wb(){Wb=Y,tie=new YX(ma,0),k7=new YX("INCOMING_ONLY",1),Ov=new YX("OUTGOING_ONLY",2)}function J4(){J4=Y,ufn=new $M,cfn=new Q_}function QY(){QY=Y,ite={boolean:pgn,number:Cbn,string:Obn,object:sqe,function:sqe,undefined:sbn}}function Tze(){Tze=Y,Gun=It((G0(),z(B(B4e,1),ke,243,0,[AH,gD,wD,P4e,$4e,L4e,R4e,MH,D7,Sx])))}function Cze(){Cze=Y,qin=It((Dc(),z(B(lie,1),ke,261,0,[QJ,Kl,cx,WJ,S7,Nv,ux,j7,E7,ZJ])))}function tkn(e,n,t,i){return new rse(z(B(wg,1),eF,45,0,[(GQ(e,n),new bw(e,n)),(GQ(t,i),new bw(t,i))]))}function ikn(e,n){var t,i;return t=u(u(Bn(e.g,n.a),49).a,68),i=u(u(Bn(e.g,n.b),49).a,68),mKe(t,i)}function h1e(e,n,t){var i;if(i=e.gc(),n>i)throw $(new b2(n,i));return e.Qi()&&(t=z_e(e,t)),e.Ci(n,t)}function Oze(e){var n,t,i;return t=e.n,i=e.o,n=e.d,new Lf(t.a-n.b,t.b-n.d,i.a+(n.b+n.c),i.b+(n.d+n.a))}function rkn(e,n){return!e||!n||e==n?!1:$w(e.b.c,n.b.c+n.b.b)<0&&$w(n.b.c,e.b.c+e.b.b)<0}function WY(e,n,t){return e>=128?!1:e<64?Gj(Rr(Gh(1,e),t),0):Gj(Rr(Gh(1,e-64),n),0)}function kO(e,n,t){switch(t.g){case 2:e.b=n;break;case 1:e.c=n;break;case 4:e.d=n;break;case 3:e.a=n}}function jO(e,n,t){return t==null?(!e.q&&(e.q=new pt),L4(e.q,n)):(!e.q&&(e.q=new pt),ei(e.q,n,t)),e}function he(e,n,t){return t==null?(!e.q&&(e.q=new pt),L4(e.q,n)):(!e.q&&(e.q=new pt),ei(e.q,n,t)),e}function Nze(e){var n,t;return t=new YR,$u(t,e),he(t,(D0(),jy),e),n=new pt,W_n(e,t,n),C$n(e,t,n),t}function ckn(e){x8();var n,t,i;for(t=oe(Lr,Se,8,2,0,1),i=0,n=0;n<2;n++)i+=.5,t[n]=RSn(i,e);return t}function Dze(e,n){var t,i,r,c;for(t=!1,i=e.a[n].length,c=0;ce.f,t=e.u+e.e[e.o.p]*e.d>e.f*e.s*e.d,n||t}function d1e(e){var n;return(!e.c||(e.Bb&1)==0&&(e.c.Db&64)!=0)&&(n=ff(e),X(n,88)&&(e.c=u(n,29))),e.c}function b1e(e){var n;if(e<0)return Xr;if(e==0)return 0;for(n=bS;(n&e)==0;n>>=1);return n}function okn(e){var n;return e==0?"Etc/GMT":(e<0?(e=-e,n="Etc/GMT-"):n="Etc/GMT+",n+jRe(e))}function _ze(e){var n,t;return t=UO(e.h),t==32?(n=UO(e.m),n==32?UO(e.l)+32:n+20-10):t-12}function ZY(e){var n,t,i;n=~e.l+1&Ls,t=~e.m+(n==0?1:0)&Ls,i=~e.h+(n==0&&t==0?1:0)&G1,e.l=n,e.m=t,e.h=i}function CE(e){var n;return n=e.a[e.b],n==null?null:(tr(e.a,e.b,null),e.b=e.b+1&e.a.length-1,n)}function g1e(){this.o=null,this.k=null,this.j=null,this.d=null,this.b=null,this.n=null,this.a=null}function w1e(e,n){this.c=e,this.d=n,this.b=this.d/this.c.c.Pd().gc()|0,this.a=this.d%this.c.c.Pd().gc()}function Lze(e,n){this.b=e,N3.call(this,(u(K(we((x0(),Rn).o),10),19),n.i),n.g),this.a=(FY(),zce)}function p1e(e,n,t){this.q=new k.Date,this.q.setFullYear(e+K0,n,t),this.q.setHours(0,0,0,0),oS(this,0)}function Pze(e,n,t){var i,r;return i=new wY(n,t),r=new st,e.b=nXe(e,e.b,i,r),r.b||++e.c,e.b.b=!1,r.d}function m1e(e,n){jn();var t,i,r,c,o;for(o=!1,i=n,r=0,c=i.length;ro||i+r>c)throw $(new soe)}function $ze(e,n,t){var i,r,c,o;for(o=LE(n,t),c=0,r=o.Jc();r.Ob();)i=u(r.Pb(),12),ei(e.c,i,ve(c++))}function L0(e){var n,t;for(t=new L(e.a.b);t.a=0,"Negative initial capacity"),IC(n>=0,"Non-positive load factor"),Hu(this)}function Jze(e,n){var t;for(t=0;t1||n>=0&&e.b<3)}function wkn(){fi();var e;return Uce||(e=dpn(q0("M",!0)),e=aR(q0("M",!1),e),Uce=e,Uce)}function qze(e){if(e.g===0)return new P6;throw $(new Gn($F+(e.f!=null?e.f:""+e.g)))}function Uze(e){if(e.g===0)return new Y_;throw $(new Gn($F+(e.f!=null?e.f:""+e.g)))}function E1e(e,n,t){if(n===0){!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),CB(e.o,t);return}vW(e,n,t)}function nQ(e,n,t){this.g=e,this.e=new Kr,this.f=new Kr,this.d=new Si,this.b=new Si,this.a=n,this.c=t}function tQ(e,n,t,i){this.b=new Ce,this.n=new Ce,this.i=i,this.j=t,this.s=e,this.t=n,this.r=0,this.d=0}function Xze(e,n,t,i){this.b=new pt,this.g=new pt,this.d=(IE(),SH),this.c=e,this.e=n,this.d=t,this.a=i}function t8(e,n){if(!e.Ji()&&n==null)throw $(new Gn("The 'no null' constraint is violated"));return n}function S1e(e){switch(e.g){case 1:return GQe;default:case 2:return 0;case 3:return qQe;case 4:return zpe}}function pkn(e){return Te(e.c,(J4(),ufn)),Ahe(e.a,ne(re(_e((EQ(),jH)))))?new VM:new nSe(e)}function mkn(e){for(;!e.d||!e.d.Ob();)if(e.b&&!kj(e.b))e.d=u(A4(e.b),50);else return null;return e.d}function Od(e){var n,t;for(n=0,t=0;ti?1:0}function Kze(e,n){var t,i,r;for(r=e.b;r;){if(t=e.a.Le(n,r.d),t==0)return r;i=t<0?0:1,r=r.a[i]}return null}function iQ(e,n){var t;return n===e?!0:X(n,229)?(t=u(n,229),di(e.Zb(),t.Zb())):!1}function x1e(e,n){return BUe(e,n)?(gn(e.b,u(T(n,(me(),K1)),22),n),Vt(e.a,n),!0):!1}function kkn(e,n){return bi(e,(me(),Ci))&&bi(n,Ci)?u(T(n,Ci),15).a-u(T(e,Ci),15).a:0}function jkn(e,n){return bi(e,(me(),Ci))&&bi(n,Ci)?u(T(e,Ci),15).a-u(T(n,Ci),15).a:0}function Vze(e){return Ka?oe(wnn,NYe,567,0,0,1):u(Ra(e.a,oe(wnn,NYe,567,e.a.c.length,0,1)),840)}function Us(e){return $r(e)?ze:s2(e)?gr:o2(e)?Yi:Cfe(e)||Sfe(e)?e.Pm:e.Pm||Array.isArray(e)&&B(Ken,1)||Ken}function W3(e,n,t){var i,r;return r=(i=new vX,i),Fc(r,n,t),Et((!e.q&&(e.q=new pe(yf,e,11,10)),e.q),r),r}function rQ(e){var n,t,i,r;for(r=Ign(Man,e),t=r.length,i=oe(ze,Se,2,t,6,1),n=0;n=e.b.c.length||(A1e(e,2*n+1),t=2*n+2,t0&&(n.Ad(t),t.i&&YEn(t))}function M1e(e,n,t){var i;for(i=t-1;i>=0&&e[i]===n[i];i--);return i<0?0:JX(Rr(e[i],Ic),Rr(n[i],Ic))?-1:1}function Skn(e,n){var t;return!e||e==n||!bi(n,(me(),ap))?!1:(t=u(T(n,(me(),ap)),9),t!=e)}function cQ(e){switch(e.i){case 2:return!0;case 1:return!1;case-1:++e.c;default:return e.Yl()}}function Yze(e,n,t){return e.d[n.p][t.p]||(mSn(e,n,t),e.d[n.p][t.p]=!0,e.d[t.p][n.p]=!0),e.a[n.p][t.p]}function Qze(e,n,t){var i,r;this.g=e,this.c=n,this.a=this,this.d=this,r=NBe(t),i=oe(Uen,dN,227,r,0,1),this.b=i}function xkn(e,n){var t,i;for(i=e.Zb().Bc().Jc();i.Ob();)if(t=u(i.Pb(),18),t.Gc(n))return!0;return!1}function Wze(e,n,t){var i,r,c,o;for(_n(t),o=!1,c=e.dd(n),r=t.Jc();r.Ob();)i=r.Pb(),c.Rb(i),o=!0;return o}function uQ(e,n){var t,i;return i=u(Un(e.a,4),129),t=oe(Rce,Ine,415,n,0,1),i!=null&&Wu(i,0,t,0,i.length),t}function Zze(e,n){var t;return t=new DW((e.f&256)!=0,e.i,e.a,e.d,(e.f&16)!=0,e.j,e.g,n),e.e!=null||(t.c=e),t}function Akn(e,n){var t;return e===n?!0:X(n,92)?(t=u(n,92),C0e($b(e),t.vc())):!1}function eFe(e,n,t){var i,r;for(r=t.Jc();r.Ob();)if(i=u(r.Pb(),45),e.ze(n,i.kd()))return!0;return!1}function PB(){PB=Y,Dce=new x$("ELK",0),D8e=new x$("JSON",1),N8e=new x$("DOT",2),I8e=new x$("SVG",3)}function OE(){OE=Y,Sre=new p$(ZZ,0),RH=new p$(KQe,1),Ere=new p$("FAN",2),jre=new p$("CONSTRAINT",3)}function NE(){NE=Y,bye=new sK(ma,0),hre=new sK("MIDDLE_TO_MIDDLE",1),yD=new sK("AVOID_OVERLAP",2)}function EO(){EO=Y,zH=new lK(ma,0),Fye=new lK("RADIAL_COMPACTION",1),Jye=new lK("WEDGE_COMPACTION",2)}function DE(){DE=Y,ure=new iK("STACKED",0),cre=new iK("REVERSE_STACKED",1),pD=new iK("SEQUENCED",2)}function zl(){zl=Y,Kme=new HX("CONCURRENT",0),Yo=new HX("IDENTITY_FINISH",1),Vme=new HX("UNORDERED",2)}function B1(){B1=Y,oG=new pK(L2e,0),Yd=new pK("INCLUDE_CHILDREN",1),Qx=new pK("SEPARATE_CHILDREN",2)}function $B(){$B=Y,d8e=new pw(15),Yfn=new Vr((Xt(),o1),d8e),Yx=Fy,l8e=kfn,f8e=Tg,h8e=Yv,a8e=Om}function oQ(){oQ=Y,Ate=I_e(z(B(Vx,1),ke,86,0,[(vr(),Zc),ru])),Mte=I_e(z(B(Vx,1),ke,86,0,[Vl,Za]))}function Mkn(e){var n,t,i;for(n=0,i=oe(Lr,Se,8,e.b,0,1),t=St(e,0);t.b!=t.d.c;)i[n++]=u(jt(t),8);return i}function sQ(e,n,t){var i,r,c;for(i=new Si,c=St(t,0);c.b!=c.d.c;)r=u(jt(c),8),Vt(i,new wc(r));Wze(e,n,i)}function Tkn(e,n){var t;t=_e((EQ(),jH))!=null&&n.Rg()!=null?ne(re(n.Rg()))/ne(re(_e(jH))):1,ei(e.b,n,t)}function Ckn(e,n){var t,i;return t=u(e.d.Ac(n),18),t?(i=e.e.hc(),i.Fc(t),e.e.d-=t.gc(),t.$b(),i):null}function T1e(e,n){var t,i;if(i=e.c[n],i!=0)for(e.c[n]=0,e.d-=i,t=n+1;t0)return C9(n-1,e.a.c.length),Ad(e.a,n-1);throw $(new ZSe)}function Okn(e,n,t){if(n<0)throw $(new jo(dWe+n));nn)throw $(new Gn(cF+e+DYe+n));if(e<0||n>t)throw $(new Ioe(cF+e+Yge+n+Xge+t))}function tFe(e){if(!e.a||(e.a.i&8)==0)throw $(new Uc("Enumeration class expected for layout option "+e.f))}function iFe(e){R_e.call(this,"The given string does not match the expected format for individual spacings.",e)}function rFe(e){switch(e.i){case-2:return!0;case-1:return!1;case 1:--e.c;default:return e.Zl()}}function Nd(e){switch(e.c){case 0:return rV(),mme;case 1:return new Z5(wqe(new s4(e)));default:return new Uxe(e)}}function cFe(e){switch(e.gc()){case 0:return rV(),mme;case 1:return new Z5(e.Jc().Pb());default:return new cse(e)}}function O1e(e){var n;return n=(!e.a&&(e.a=new pe(ed,e,9,5)),e.a),n.i!=0?Ngn(u(K(n,0),684)):null}function Nkn(e,n){var t;return t=mc(e,n),JX(YV(e,n),0)|C$(YV(e,t),0)?t:mc(hN,YV(Bb(t,63),1))}function N1e(e,n,t){var i,r;return S2(n,e.c.length),i=t.Nc(),r=i.length,r==0?!1:(sfe(e.c,n,i),!0)}function Dkn(e,n){var t,i;for(t=e.a.length-1;n!=e.b;)i=n-1&t,tr(e.a,n,e.a[i]),n=i;tr(e.a,e.b,null),e.b=e.b+1&t}function Ikn(e,n){var t,i;for(t=e.a.length-1,e.c=e.c-1&t;n!=e.c;)i=n+1&t,tr(e.a,n,e.a[i]),n=i;tr(e.a,e.c,null)}function i8(e,n){e.D==null&&e.B!=null&&(e.D=e.B,e.B=null),_Y(e,n==null?null:(_n(n),n)),e.C&&e.fl(null)}function Z3(e){return(e.c!=e.b.b||e.i!=e.g.b)&&(Qp(e.a.c,0),Er(e.a,e.b),Er(e.a,e.g),e.c=e.b.b,e.i=e.g.b),e.a}function _2(e){var n;++e.j,e.i==0?e.g=null:e.ir&&(QHe(n.q,r),i=t!=n.q.d)),i}function bFe(e,n){var t,i,r,c,o,l,f,h;return f=n.i,h=n.j,i=e.f,r=i.i,c=i.j,o=f-r,l=h-c,t=k.Math.sqrt(o*o+l*l),t}function _1e(e,n){var t,i;return i=nz(e),i||(t=(ZW(),gUe(n)),i=new HSe(t),Et(i.Cl(),e)),i}function SO(e,n){var t,i;return t=u(e.c.Ac(n),18),t?(i=e.hc(),i.Fc(t),e.d-=t.gc(),t.$b(),e.mc(i)):e.jc()}function Bkn(e){var n;if(!(e.c.c<0?e.a>=e.c.b:e.a<=e.c.b))throw $(new au);return n=e.a,e.a+=e.c.c,++e.b,ve(n)}function zkn(e){var n,t;if(e==null)return!1;for(n=0,t=e.length;n=i||n=0?n:-n;i>0;)i%2==0?(t*=t,i=i/2|0):(r*=t,i-=1);return n<0?1/r:r}function Kkn(e,n){var t,i,r;for(r=1,t=e,i=n>=0?n:-n;i>0;)i%2==0?(t*=t,i=i/2|0):(r*=t,i-=1);return n<0?1/r:r}function $0(e,n){var t,i,r,c;return c=(r=e?nz(e):null,oqe((i=n,r&&r.El(),i))),c==n&&(t=nz(e),t&&t.El()),c}function P1e(e,n,t){var i,r;return r=e.a,e.a=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,1,r,n),t?t.lj(i):t=i),t}function pFe(e,n,t){var i,r;return r=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,3,r,n),t?t.lj(i):t=i),t}function mFe(e,n,t){var i,r;return r=e.f,e.f=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,0,r,n),t?t.lj(i):t=i),t}function vFe(e){var n,t;if(e!=null)for(t=0;t-129&&e<128?(mDe(),n=e+128,t=Ime[n],!t&&(t=Ime[n]=new Ln(e)),t):new Ln(e)}function ve(e){var n,t;return e>-129&&e<128?(aDe(),n=e+128,t=Cme[n],!t&&(t=Cme[n]=new co(e)),t):new co(e)}function ejn(e,n,t,i,r){n==0||i==0||(n==1?r[i]=Tde(r,t,i,e[0]):i==1?r[n]=Tde(r,e,n,t[0]):HCn(e,t,r,n,i))}function SFe(e,n){var t;e.c.length!=0&&(t=u(Ra(e,oe(c1,Bd,9,e.c.length,0,1)),199),Bse(t,new Lh),Oqe(t,n))}function xFe(e,n){var t;e.c.length!=0&&(t=u(Ra(e,oe(c1,Bd,9,e.c.length,0,1)),199),Bse(t,new r3),Oqe(t,n))}function AFe(e,n){var t;e.a.c.length>0&&(t=u(Le(e.a,e.a.c.length-1),565),x1e(t,n))||Te(e.a,new RPe(n))}function njn(e){rl();var n,t;n=e.d.c-e.e.c,t=u(e.g,156),Ao(t.b,new Lje(n)),Ao(t.c,new Pje(n)),rc(t.i,new $je(n))}function MFe(e){var n;return n=new p0,n.a+="VerticalSegment ",uo(n,e.e),n.a+=" ",Kt(n,nle(new NX,new L(e.k))),n.a}function tjn(e,n){var t;e.c=n,e.a=eEn(n),e.a<54&&(e.f=(t=n.d>1?PLe(n.a[0],n.a[1]):PLe(n.a[0],0),Xb(n.e>0?t:Td(t))))}function hQ(e,n){var t,i,r;for(t=0,r=mu(e,n).Jc();r.Ob();)i=u(r.Pb(),12),t+=T(i,(me(),vs))!=null?1:0;return t}function ev(e,n,t){var i,r,c;for(i=0,c=St(e,0);c.b!=c.d.c&&(r=ne(re(jt(c))),!(r>t));)r>=n&&++i;return i}function ijn(e){var n;return n=u(Pa(e.c.c,""),233),n||(n=new N4(h9(a9(new f0,""),"Other")),tg(e.c.c,"",n)),n}function _E(e){var n;return(e.Db&64)!=0?Jf(e):(n=new cf(Jf(e)),n.a+=" (name: ",Bc(n,e.zb),n.a+=")",n.a)}function B1e(e,n,t){var i,r;return r=e.sb,e.sb=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,4,r,n),t?t.lj(i):t=i),t}function xO(e,n,t){var i;e.Zi(e.i+1),i=e.Xi(n,t),n!=e.i&&Wu(e.g,n,e.g,n+1,e.i-n),tr(e.g,n,i),++e.i,e.Ki(n,t),e.Li()}function z1e(e,n,t){var i,r;return r=e.r,e.r=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,8,r,e.r),t?t.lj(i):t=i),t}function rjn(e,n,t){var i,r;return i=new L1(e.e,3,13,null,(r=n.c,r||(yn(),ih)),Ld(e,n),!1),t?t.lj(i):t=i,t}function cjn(e,n,t){var i,r;return i=new L1(e.e,4,13,(r=n.c,r||(yn(),ih)),null,Ld(e,n),!1),t?t.lj(i):t=i,t}function ujn(e,n){var t,i,r,c;if(n.cj(e.a),c=u(Un(e.a,8),1997),c!=null)for(t=c,i=0,r=t.length;i>1&1431655765,e=(e>>2&858993459)+(e&858993459),e=(e>>4)+e&252645135,e+=e>>8,e+=e>>16,e&63}function ojn(e){return e?(e.i&1)!=0?e==ts?Yi:e==$t?jr:e==Hm?h7:e==Jr?gr:e==Ep?cp:e==t5?up:e==ds?py:XS:e:null}function di(e,n){return $r(e)?bn(e,n):s2(e)?vNe(e,n):o2(e)?(_n(e),ue(e)===ue(n)):Cfe(e)?e.Fb(n):Sfe(e)?bCe(e,n):Mae(e,n)}function CFe(e){var n;return ao(e,0)<0&&(e=I0(Avn(uu(e)?sf(e):e))),n=Rt(Bb(e,32)),64-(n!=0?UO(n):UO(Rt(e))+32)}function AO(e,n){var t;return t=new ch,e.a.zd(t)?(y9(),new SX(_n(hRe(e,t.a,n)))):(A0(e),y9(),y9(),Gme)}function LE(e,n){switch(n.g){case 2:case 1:return mu(e,n);case 3:case 4:return Ks(mu(e,n))}return jn(),jn(),Sc}function sjn(e,n){var t;return n.a&&(t=n.a.a.length,e.a?Kt(e.a,e.b):e.a=new il(e.d),zLe(e.a,n.a,n.d.length,t)),e}function ljn(e){Zz();var n,t,i,r;for(t=DQ(),i=0,r=t.length;it)throw $(new jo(cF+e+Yge+n+", size: "+t));if(e>n)throw $(new Gn(cF+e+DYe+n))}function Fl(e,n,t){if(n<0)q0e(e,t);else{if(!t.pk())throw $(new Gn(W0+t.ve()+_S));u(t,69).uk().Ck(e,e.ei(),n)}}function dQ(e,n,t){return k.Math.abs(n-e)NF?e-t>NF:t-e>NF}function J1e(e,n,t,i){switch(n){case 1:return!e.n&&(e.n=new pe(ju,e,1,7)),e.n;case 2:return e.k}return $de(e,n,t,i)}function NFe(e){var n;return(e.Db&64)!=0?Jf(e):(n=new cf(Jf(e)),n.a+=" (source: ",Bc(n,e.d),n.a+=")",n.a)}function Id(e,n){var t;t=(e.Bb&256)!=0,n?e.Bb|=256:e.Bb&=-257,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,2,t,n))}function H1e(e,n){var t;t=(e.Bb&256)!=0,n?e.Bb|=256:e.Bb&=-257,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,8,t,n))}function G1e(e,n){var t;t=(e.Bb&512)!=0,n?e.Bb|=512:e.Bb&=-513,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,9,t,n))}function _d(e,n){var t;t=(e.Bb&512)!=0,n?e.Bb|=512:e.Bb&=-513,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,3,t,n))}function FB(e,n){var t;t=(e.Bb&256)!=0,n?e.Bb|=256:e.Bb&=-257,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,8,t,n))}function fjn(e,n,t){var i,r;return r=e.a,e.a=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,5,r,e.a),t?s0e(t,i):t=i),t}function PE(e,n){var t;return e.b==-1&&e.a&&(t=e.a.nk(),e.b=t?e.c.Eh(e.a.Jj(),t):zi(e.c.Ah(),e.a)),e.c.vh(e.b,n)}function DFe(e,n){var t,i;for(i=new ut(e);i.e!=i.i.gc();)if(t=u(ft(i),29),ue(n)===ue(t))return!0;return!1}function IFe(e){return e>=65&&e<=70?e-65+10:e>=97&&e<=102?e-97+10:e>=48&&e<=57?e-48:0}function q1e(e){var n,t;return n=e.k,n==(zn(),wr)?(t=u(T(e,(me(),Du)),64),t==(De(),Xn)||t==bt):!1}function _Fe(e){var n;return n=Dae(e),Hj(n.a,0)?(i2(),i2(),ste):(i2(),new PK(FX(n.a,0)?ehe(n)/Xb(n.a):0))}function ajn(e,n){var t;if(t=QO(e,n),X(t,335))return u(t,38);throw $(new Gn(W0+n+"' is not a valid attribute"))}function $E(e,n,t){var i;if(i=e.gc(),n>i)throw $(new b2(n,i));if(e.Qi()&&e.Gc(t))throw $(new Gn($N));e.Ei(n,t)}function LFe(e,n){var t,i;for(i=new ut(e);i.e!=i.i.gc();)if(t=u(ft(i),143),ue(n)===ue(t))return!0;return!1}function hjn(e,n,t){var i,r,c;return c=(r=E8(e.b,n),r),c&&(i=u(Kz(oO(e,c),""),29),i)?gbe(e,i,n,t):null}function bQ(e,n,t){var i,r,c;return c=(r=E8(e.b,n),r),c&&(i=u(Kz(oO(e,c),""),29),i)?wbe(e,i,n,t):null}function djn(e){var n,t,i;for(i=0,t=e.length,n=0;n=0?B0(e):sE(B0(Td(e))))}function PFe(e,n,t,i,r,c){this.e=new Ce,this.f=(Nc(),xx),Te(this.e,e),this.d=n,this.a=t,this.b=i,this.f=r,this.c=c}function ki(e,n){return en?1:e==n?e==0?ki(1/e,1/n):0:isNaN(e)?isNaN(n)?0:1:-1}function bjn(e){var n;return n=e.a[e.c-1&e.a.length-1],n==null?null:(e.c=e.c-1&e.a.length-1,tr(e.a,e.c,null),n)}function $Fe(e){var n,t;for(t=e.p.a.ec().Jc();t.Ob();)if(n=u(t.Pb(),217),n.f&&e.b[n.c]<-1e-10)return n;return null}function gjn(e){var n,t,i;for(n=new Ce,i=new L(e.b);i.a=1?ru:Za):t}function kjn(e){var n,t;for(t=wUe(sl(e)).Jc();t.Ob();)if(n=Pt(t.Pb()),uS(e,n))return I6n((DMe(),Ban),n);return null}function jjn(e,n,t){var i,r;for(r=e.a.ec().Jc();r.Ob();)if(i=u(r.Pb(),9),yO(t,u(Le(n,i.p),18)))return i;return null}function Ejn(e,n,t){var i,r;for(r=X(n,103)&&(u(n,19).Bb&Ec)!=0?new EK(n,e):new Y9(n,e),i=0;i>10)+pN&yr,n[1]=(e&1023)+56320&yr,gh(n,0,n.length)}function Y1e(e,n){var t;t=(e.Bb&Ec)!=0,n?e.Bb|=Ec:e.Bb&=-65537,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,20,t,n))}function a8(e,n){var t;t=(e.Bb&yh)!=0,n?e.Bb|=yh:e.Bb&=-16385,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,16,t,n))}function pQ(e,n){var t;t=(e.Bb&Bu)!=0,n?e.Bb|=Bu:e.Bb&=-32769,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,18,t,n))}function Q1e(e,n){var t;t=(e.Bb&Bu)!=0,n?e.Bb|=Bu:e.Bb&=-32769,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,18,t,n))}function mu(e,n){var t;return e.i||G0e(e),t=u(zc(e.g,n),49),t?new T0(e.j,u(t.a,15).a,u(t.b,15).a):(jn(),jn(),Sc)}function Ajn(e,n,t){var i,r;return i=u(n.mf(e.a),35),r=u(t.mf(e.a),35),i!=null&&r!=null?dO(i,r):i!=null?-1:r!=null?1:0}function W1e(e,n,t){var i,r;return i=(v0(),r=new Fk,r),bB(i,n),gB(i,t),e&&Et((!e.a&&(e.a=new mr(kl,e,5)),e.a),i),i}function Z1e(e,n,t){var i;return i=0,n&&(I3(e.a)?i+=n.f.a/2:i+=n.f.b/2),t&&(I3(e.a)?i+=t.f.a/2:i+=t.f.b/2),i}function Pw(e,n,t){var i;return i=e.a.get(n),e.a.set(n,t===void 0?null:t),i===void 0?(++e.c,++e.b.g):++e.d,i}function mQ(e){var n;return(e.Db&64)!=0?Jf(e):(n=new cf(Jf(e)),n.a+=" (identifier: ",Bc(n,e.k),n.a+=")",n.a)}function qB(e){var n;switch(e.gc()){case 0:return cR(),Zne;case 1:return new HK(Nt(e.Xb(0)));default:return n=e,new QV(n)}}function Mjn(e){switch(u(T(e,(Ne(),Y1)),222).g){case 1:return new ad;case 3:return new T5;default:return new Dp}}function Tjn(e){var n;return n=F2(e),n>34028234663852886e22?Ki:n<-34028234663852886e22?Dr:n}function mc(e,n){var t;return uu(e)&&uu(n)&&(t=e+n,wNn){NLe(t);break}}yR(t,n)}function We(e,n){var t,i,r,c,o;if(t=n.f,tg(e.c.d,t,n),n.g!=null)for(r=n.g,c=0,o=r.length;cn&&i.Le(e[c-1],e[c])>0;--c)o=e[c],tr(e,c,e[c-1]),tr(e,c-1,o)}function Jl(e,n,t,i){if(n<0)ybe(e,t,i);else{if(!t.pk())throw $(new Gn(W0+t.ve()+_S));u(t,69).uk().Ak(e,e.ei(),n,i)}}function Rjn(e,n){var t;if(t=QO(e.Ah(),n),X(t,103))return u(t,19);throw $(new Gn(W0+n+"' is not a valid reference"))}function UB(e,n){if(n==e.d)return e.e;if(n==e.e)return e.d;throw $(new Gn("Node "+n+" not part of edge "+e))}function nde(e,n,t,i){switch(n){case 3:return e.f;case 4:return e.g;case 5:return e.i;case 6:return e.j}return J1e(e,n,t,i)}function Bjn(e){return e.k!=(zn(),Qi)?!1:G3(new wn(null,new v2(new qn(Vn(Ni(e).a.Jc(),new ee)))),new ZA)}function Xs(){Xs=Y,sD=new sC(ma,0),fx=new sC("FIRST",1),V1=new sC(jQe,2),ax=new sC("LAST",3),yg=new sC(EQe,4)}function BE(){BE=Y,ix=new h$("LAYER_SWEEP",0),wve=new h$("MEDIAN_LAYER_SWEEP",1),eD=new h$(ree,2),pve=new h$(ma,3)}function XB(){XB=Y,f6e=new hK("ASPECT_RATIO_DRIVEN",0),Gre=new hK("MAX_SCALE_DRIVEN",1),l6e=new hK("AREA_DRIVEN",2)}function KB(){KB=Y,Nce=new S$(Rpe,0),M8e=new S$("GROUP_DEC",1),C8e=new S$("GROUP_MIXED",2),T8e=new S$("GROUP_INC",3)}function zjn(e,n){return bn(n.b&&n.c?Ub(n.b)+"->"+Ub(n.c):"e_"+Oi(n),e.b&&e.c?Ub(e.b)+"->"+Ub(e.c):"e_"+Oi(e))}function Fjn(e,n){return bn(n.b&&n.c?Ub(n.b)+"->"+Ub(n.c):"e_"+Oi(n),e.b&&e.c?Ub(e.b)+"->"+Ub(e.c):"e_"+Oi(e))}function $w(e,n){return Oa(),Bf(X0),k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)?0:en?1:Lb(isNaN(e),isNaN(n))}function tde(e){EQ(),this.c=$f(z(B(UBn,1),On,829,0,[Run])),this.b=new pt,this.a=e,ei(this.b,jH,1),Ao(Bun,new eSe(this))}function zE(e){var n;this.a=(n=u(e.e&&e.e(),10),new _l(n,u(_f(n,n.length),10),0)),this.b=oe(Ar,On,1,this.a.a.length,5,1)}function su(e){var n;return Array.isArray(e)&&e.Rm===Qn?Db(Us(e))+"@"+(n=Oi(e)>>>0,n.toString(16)):e.toString()}function Jjn(e){var n;return e==null?!0:(n=e.length,n>0&&(Yn(n-1,e.length),e.charCodeAt(n-1)==58)&&!kQ(e,uA,oA))}function kQ(e,n,t){var i,r;for(i=0,r=e.length;i=r)return n.c+t;return n.c+n.b.gc()}function UFe(e,n){S9();var t,i,r,c;for(i=R$e(e),r=n,J9(i,0,i.length,r),t=0;t0&&(i+=r,++t);return t>1&&(i+=e.d*(t-1)),i}function rde(e){var n,t,i;for(i=new pd,i.a+="[",n=0,t=e.gc();n=0;--i)for(n=t[i],r=0;r>5,n=e&31,i=oe($t,ni,30,t+1,15,1),i[t]=1<0&&(n.lengthe.i&&tr(n,e.i,null),n}function VB(e){var n;return(e.Db&64)!=0?_E(e):(n=new cf(_E(e)),n.a+=" (instanceClassName: ",Bc(n,e.D),n.a+=")",n.a)}function YB(e){var n,t,i,r;for(r=0,t=0,i=e.length;t0?(e.Zj(),i=n==null?0:Oi(n),r=(i&oi)%e.d.length,t=jUe(e,r,i,n),t!=-1):!1}function To(e,n,t){var i,r,c;return e.Nj()?(i=e.i,c=e.Oj(),xO(e,i,n),r=e.Gj(3,null,n,i,c),t?t.lj(r):t=r):xO(e,e.i,n),t}function wa(e,n){var t,i,r;return e.f>0&&(e.Zj(),i=n==null?0:Oi(n),r=(i&oi)%e.d.length,t=W0e(e,r,i,n),t)?t.kd():null}function fEn(e,n,t){var i,r;return i=new L1(e.e,3,10,null,(r=n.c,X(r,88)?u(r,29):(yn(),jf)),Ld(e,n),!1),t?t.lj(i):t=i,t}function aEn(e,n,t){var i,r;return i=new L1(e.e,4,10,(r=n.c,X(r,88)?u(r,29):(yn(),jf)),null,Ld(e,n),!1),t?t.lj(i):t=i,t}function iJe(e,n){var t,i,r;return X(n,45)?(t=u(n,45),i=t.jd(),r=L2(e.Pc(),i),T1(r,t.kd())&&(r!=null||e.Pc()._b(i))):!1}function dde(e,n){switch(n){case 3:Dw(e,0);return;case 4:Iw(e,0);return;case 5:Os(e,0);return;case 6:Ns(e,0);return}R1e(e,n)}function Rw(e,n){switch(n.g){case 1:return j4(e.j,(ss(),S3e));case 2:return j4(e.j,(ss(),A3e));default:return jn(),jn(),Sc}}function B0(e){mh();var n,t;return t=Rt(e),n=Rt(Bb(e,32)),n!=0?new hLe(t,n):t>10||t<0?new D1(1,t):cnn[t]}function rJe(e){B2();var n;return(e.q?e.q:(jn(),jn(),i1))._b((Ne(),gp))?n=u(T(e,gp),203):n=u(T(_r(e),vx),203),n}function hEn(e,n,t,i){var r,c;if(c=t-n,c<3)for(;c<3;)e*=10,++c;else{for(r=1;c>3;)r*=10,--c;e=(e+(r>>1))/r|0}return i.i=e,!0}function cJe(e,n,t){fBe(),gxe.call(this),this.a=g2(Tnn,[Se,Zge],[592,216],0,[gJ,gte],2),this.c=new g4,this.g=e,this.f=n,this.d=t}function uJe(e){this.e=oe($t,ni,30,e.length,15,1),this.c=oe(ts,pa,30,e.length,16,1),this.b=oe(ts,pa,30,e.length,16,1),this.f=0}function dEn(e){var n,t;for(e.j=oe(Jr,Jc,30,e.p.c.length,15,1),t=new L(e.p);t.a>5,n&=31,r=e.d+t+(n==0?0:1),i=oe($t,ni,30,r,15,1),aMn(i,e.a,t,n),c=new zb(e.e,r,i),bE(c),c}function h8(e,n,t){var i,r,c;for(r=null,c=e.b;c;){if(i=e.a.Le(n,c.d),t&&i==0)return c;i>=0?c=c.a[1]:(r=c,c=c.a[0])}return r}function DO(e,n,t){var i,r,c;for(r=null,c=e.b;c;){if(i=e.a.Le(n,c.d),t&&i==0)return c;i<=0?c=c.a[0]:(r=c,c=c.a[1])}return r}function MQ(e,n){for(var t=0;!n[t]||n[t]=="";)t++;for(var i=n[t++];t0?(k.Error.stackTraceLimit=Error.stackTraceLimit=64,!0):"stack"in new Error}function mEn(e){var n;n=e.a;do n=u(tt(new qn(Vn(Ni(n).a.Jc(),new ee))),17).d.i,n.k==(zn(),dr)&&Te(e.e,n);while(n.k==(zn(),dr))}function vEn(e,n){var t,i,r;for(i=new qn(Vn(Ni(e).a.Jc(),new ee));ht(i);)if(t=u(tt(i),17),r=t.d.i,r.c==n)return!1;return!0}function hJe(e,n,t){var i,r,c,o;for(r=u(Bn(e.b,t),171),i=0,o=new L(n.j);o.an?1:Lb(isNaN(e),isNaN(n)))>0}function pde(e,n){return Oa(),Oa(),Bf(X0),(k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)?0:en?1:Lb(isNaN(e),isNaN(n)))<0}function pJe(e,n){return Oa(),Oa(),Bf(X0),(k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)?0:en?1:Lb(isNaN(e),isNaN(n)))<=0}function mde(e){switch(e.g){case 12:case 13:case 14:case 15:case 16:case 17:case 18:case 19:case 20:return!0;default:return!1}}function vde(e,n,t,i,r,c){this.a=e,this.c=n,this.b=t,this.f=i,this.d=r,this.e=c,this.c>0&&this.b>0&&(this.g=oR(this.c,this.b,this.a))}function jEn(e,n){var t=e.a,i;n=String(n),t.hasOwnProperty(n)&&(i=t[n]);var r=(QY(),ite)[typeof i],c=r?r(i):F1e(typeof i);return c}function d8(e){var n,t,i;if(i=null,n=Ah in e.a,t=!n,t)throw $(new oh("Every element must have an id."));return i=Z4(O1(e,Ah)),i}function Bw(e){var n,t;for(t=GGe(e),n=null;e.c==2;)li(e),n||(n=(fi(),fi(),new Kj(2)),ug(n,t),t=n),t.Hm(GGe(e));return t}function ZB(e,n){var t,i,r;return e.Zj(),i=n==null?0:Oi(n),r=(i&oi)%e.d.length,t=W0e(e,r,i,n),t?(wBe(e,t),t.kd()):null}function gh(e,n,t){var i,r,c,o;for(c=n+t,Yr(n,c,e.length),o="",r=n;rn.e?1:e.en.d?e.e:e.d=48&&e<48+k.Math.min(10,10)?e-48:e>=97&&e<97?e-97+10:e>=65&&e<65?e-65+10:-1}function EEn(e,n){if(n.c==e)return n.d;if(n.d==e)return n.c;throw $(new Gn("Input edge is not connected to the input port."))}function wh(e,n){if(e.a<0)throw $(new Uc("Did not call before(...) or after(...) before calling add(...)."));return ble(e,e.a,n),e}function yde(e){return $R(),X(e,166)?u(Bn(WD,fnn),296).Qg(e):so(WD,Us(e))?u(Bn(WD,Us(e)),296).Qg(e):null}function Lo(e){var n,t;return(e.Db&32)==0&&(t=(n=u(Un(e,16),29),dt(n||e.fi())-dt(e.fi())),t!=0&&U4(e,32,oe(Ar,On,1,t,5,1))),e}function U4(e,n,t){var i;(e.Db&n)!=0?t==null?JCn(e,n):(i=VQ(e,n),i==-1?e.Eb=t:tr(B4(e.Eb),i,t)):t!=null&&sDn(e,n,t)}function SEn(e,n,t,i){var r,c;n.c.length!=0&&(r=tNn(t,i),c=fCn(n),Zi(aB(new wn(null,new pn(c,1)),new g_),new JIe(e,t,r,i)))}function xEn(e,n){var t,i,r,c;return i=e.a.length-1,t=n-e.b&i,c=e.c-n&i,r=e.c-e.b&i,SOe(t=c?(Ikn(e,n),-1):(Dkn(e,n),1)}function AEn(e,n){var t,i;for(t=(Yn(n,e.length),e.charCodeAt(n)),i=n+1;in.e?1:e.fn.f?1:Oi(e)-Oi(n)}function jJe(e,n){var t;return ue(n)===ue(e)?!0:!X(n,22)||(t=u(n,22),t.gc()!=e.gc())?!1:e.Hc(t)}function ez(e,n){return _n(e),n==null?!1:bn(e,n)?!0:e.length==n.length&&bn(e.toLowerCase(),n.toLowerCase())}function R2(e){var n,t;return ao(e,-129)>0&&ao(e,128)<0?(pDe(),n=Rt(e)+128,t=Ome[n],!t&&(t=Ome[n]=new En(e)),t):new En(e)}function X4(){X4=Y,ZS=new l$(ma,0),v3e=new l$("INSIDE_PORT_SIDE_GROUPS",1),Cte=new l$("GROUP_MODEL_ORDER",2),Ote=new l$(ZZ,3)}function nz(e){var n,t,i;if(i=e.Gh(),!i)for(n=0,t=e.Mh();t;t=t.Mh()){if(++n>$Z)return t.Nh();if(i=t.Gh(),i||t==e)break}return i}function CEn(e){var n;return e.b||ogn(e,(n=o2n(e.e,e.a),!n||!bn(ane,wa((!n.b&&(n.b=new Hs((yn(),Ac),Iu,n)),n.b),"qualified")))),e.c}function OEn(e){var n,t;for(t=new L(e.a.b);t.a2e3&&(Ven=e,sJ=k.setTimeout(Egn,10))),oJ++==0?(r8n((Toe(),yme)),!0):!1}function JEn(e,n,t){var i;(pnn?(nEn(e),!0):mnn||ynn?(m9(),!0):vnn&&(m9(),!1))&&(i=new ONe(n),i.b=t,HMn(e,i))}function OQ(e,n){var t;t=!e.A.Gc((Vs(),Og))||e.q==(Br(),to),e.u.Gc((ps(),Z1))?t?fRn(e,n):xVe(e,n):e.u.Gc(gb)&&(t?D$n(e,n):zVe(e,n))}function HEn(e,n,t){var i,r;aW(e.e,n,t,(De(),Kn)),aW(e.i,n,t,et),e.a&&(r=u(T(n,(me(),wi)),12),i=u(T(t,wi),12),WV(e.g,r,i))}function MJe(e){var n;ue(ye(e,(Xt(),Xv)))===ue((B1(),oG))&&(Bi(e)?(n=u(ye(Bi(e),Xv),347),ji(e,Xv,n)):ji(e,Xv,Qx))}function TJe(e,n,t){return new Lf(k.Math.min(e.a,n.a)-t/2,k.Math.min(e.b,n.b)-t/2,k.Math.abs(e.a-n.a)+t,k.Math.abs(e.b-n.b)+t)}function CJe(e){var n;this.d=new Ce,this.j=new Kr,this.g=new Kr,n=e.g.b,this.f=u(T(_r(n),(Ne(),pl)),86),this.e=ne(re(rz(n,Em)))}function OJe(e){this.d=new Ce,this.e=new O0,this.c=oe($t,ni,30,(De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])).length,15,1),this.b=e}function xde(e,n,t){var i;switch(i=t[e.g][n],e.g){case 1:case 3:return new je(0,i);case 2:case 4:return new je(i,0);default:return null}}function GEn(e,n){var t;if(t=U3(e.o,n),t==null)throw $(new oh("Node did not exist in input."));return Sbe(e,n),PW(e,n),bbe(e,n,t),null}function NJe(e,n){var t,i;for(i=e.a.length,n.lengthi&&tr(n,i,null),n}function Ra(e,n){var t,i;for(i=e.c.length,n.lengthi&&tr(n,i,null),n}function NQ(e,n,t,i){var r;if(r=e.length,n>=r)return r;for(n=n>0?n:0;n0&&(Te(e.b,new KNe(n.a,t)),i=n.a.length,0i&&(n.a+=HCe(oe(Wl,kh,30,-i,15,1))))}function _Je(e,n,t){var i,r,c;if(!t[n.d])for(t[n.d]=!0,r=new L(Z3(n));r.a=e.b>>1)for(i=e.c,t=e.b;t>n;--t)i=i.b;else for(i=e.a.a,t=0;t=0?e.Th(r):kW(e,i)):t<0?kW(e,i):u(i,69).uk().zk(e,e.ei(),t)}function RJe(e){var n,t,i;for(i=(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),e.o),t=i.c.Jc();t.e!=t.i.gc();)n=u(t.Wj(),45),n.kd();return ZC(i)}function _e(e){var n;if(X(e.a,4)){if(n=yde(e.a),n==null)throw $(new Uc(gWe+e.b+"'. "+bWe+(M1(ZD),ZD.k)+T2e));return n}else return e.a}function nSn(e){var n;if(e==null)return null;if(n=mRn(bo(e,!0)),n==null)throw $(new TX("Invalid base64Binary value: '"+e+"'"));return n}function ft(e){var n;try{return n=e.i.Xb(e.e),e.Vj(),e.g=e.e++,n}catch(t){throw t=sr(t),X(t,99)?(e.Vj(),$(new au)):$(t)}}function LQ(e){var n;try{return n=e.c.Ti(e.e),e.Vj(),e.g=e.e++,n}catch(t){throw t=sr(t),X(t,99)?(e.Vj(),$(new au)):$(t)}}function iz(e){var n,t,i,r;for(r=0,t=0,i=e.length;t=64&&n<128&&(r=hh(r,Gh(1,n-64)));return r}function rz(e,n){var t,i;return i=null,bi(e,(Xt(),Jy))&&(t=u(T(e,Jy),105),t.nf(n)&&(i=t.mf(n))),i==null&&_r(e)&&(i=T(_r(e),n)),i}function tSn(e,n){var t;return t=u(T(e,(Ne(),Wc)),78),NK(n,nin)?t?qs(t):(t=new xs,he(e,Wc,t)):t&&he(e,Wc,null),t}function iSn(e,n){var t,i,r;for(r=new xo(n.gc()),i=n.Jc();i.Ob();)t=u(i.Pb(),294),t.c==t.f?k8(e,t,t.c):pTn(e,t)||Hn(r.c,t);return r}function BJe(e,n){var t,i,r;for(t=e.o,r=u(u(pi(e.r,n),22),83).Jc();r.Ob();)i=u(r.Pb(),115),i.e.a=cxn(i,t.a),i.e.b=t.b*ne(re(i.b.mf(wJ)))}function rSn(e,n){var t,i,r,c;return r=e.k,t=ne(re(T(e,(me(),hp)))),c=n.k,i=ne(re(T(n,hp))),c!=(zn(),wr)?-1:r!=wr?1:t==i?0:tt.b)return!0}return!1}function JJe(e){var n;return n=new p0,n.a+="n",e.k!=(zn(),Qi)&&Kt(Kt((n.a+="(",n),$K(e.k).toLowerCase()),")"),Kt((n.a+="_",n),LO(e)),n.a}function HE(){HE=Y,I4e=new lC(Rpe,0),Wie=new lC(ree,1),Zie=new lC("LINEAR_SEGMENTS",2),jx=new lC("BRANDES_KOEPF",3),Ex=new lC(BQe,4)}function K4(e,n,t,i){var r;return t>=0?e.Ph(n,t,i):(e.Mh()&&(i=(r=e.Ch(),r>=0?e.xh(i):e.Mh().Qh(e,-1-r,null,i))),e.zh(n,t,i))}function Ade(e,n){switch(n){case 7:!e.e&&(e.e=new Nn(pr,e,7,4)),kt(e.e);return;case 8:!e.d&&(e.d=new Nn(pr,e,8,5)),kt(e.d);return}dde(e,n)}function ji(e,n,t){return t==null?(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),ZB(e.o,n)):(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),RO(e.o,n,t)),e}function Yu(e,n){var t;t=e.dd(n);try{return t.Pb()}catch(i){throw i=sr(i),X(i,112)?$(new jo("Can't get element "+n)):$(i)}}function HJe(e,n){var t;switch(t=u(zc(e.b,n),127).n,n.g){case 1:e.t>=0&&(t.d=e.t);break;case 3:e.t>=0&&(t.a=e.t)}e.C&&(t.b=e.C.b,t.c=e.C.c)}function aSn(e){var n;n=e.a;do n=u(tt(new qn(Vn(rr(n).a.Jc(),new ee))),17).c.i,n.k==(zn(),dr)&&e.b.Ec(n);while(n.k==(zn(),dr));e.b=Ks(e.b)}function GJe(e,n){var t,i,r;for(r=e,i=new qn(Vn(rr(n).a.Jc(),new ee));ht(i);)t=u(tt(i),17),t.c.i.c&&(r=k.Math.max(r,t.c.i.c.p));return r}function hSn(e,n){var t,i,r;for(r=0,i=u(u(pi(e.r,n),22),83).Jc();i.Ob();)t=u(i.Pb(),115),r+=t.d.d+t.b.Kf().b+t.d.a,i.Ob()&&(r+=e.w);return r}function dSn(e,n){var t,i,r;for(r=0,i=u(u(pi(e.r,n),22),83).Jc();i.Ob();)t=u(i.Pb(),115),r+=t.d.b+t.b.Kf().a+t.d.c,i.Ob()&&(r+=e.w);return r}function qJe(e){var n,t,i,r;if(i=0,r=q2(e),r.c.length==0)return 1;for(t=new L(r);t.a=0?e.Ih(o,t,!0):Gw(e,c,t)):u(c,69).uk().wk(e,e.ei(),r,t,i)}function wSn(e,n,t,i){var r,c;c=n.nf((Xt(),Vv))?u(n.mf(Vv),22):e.j,r=ljn(c),r!=(Zz(),wte)&&(t&&!mde(r)||O0e(NOn(e,r,i),n))}function PQ(e,n){return $r(e)?!!Jen[n]:e.Qm?!!e.Qm[n]:s2(e)?!!Fen[n]:o2(e)?!!zen[n]:!1}function pSn(e){switch(e.g){case 1:return Lw(),XN;case 3:return Lw(),UN;case 2:return Lw(),mte;case 4:return Lw(),pte;default:return null}}function mSn(e,n,t){if(e.e)switch(e.b){case 1:I5n(e.c,n,t);break;case 0:_5n(e.c,n,t)}else oPe(e.c,n,t);e.a[n.p][t.p]=e.c.i,e.a[t.p][n.p]=e.c.e}function XJe(e){var n,t;if(e==null)return null;for(t=oe(c1,Se,199,e.length,0,2),n=0;nc?1:0):0}function B2(){B2=Y,xH=new d$(ma,0),Yie=new d$("PORT_POSITION",1),Fv=new d$("NODE_SIZE_WHERE_SPACE_PERMITS",2),zv=new d$("NODE_SIZE",3)}function vSn(e,n){var t,i,r;for(n.Tg("Untreeify",1),t=u(T(e,(Ti(),yye)),16),r=t.Jc();r.Ob();)i=u(r.Pb(),65),Vt(i.b.d,i),Vt(i.c.b,i);n.Ug()}function Vh(){Vh=Y,sce=new Rj("AUTOMATIC",0),CD=new Rj(ly,1),OD=new Rj(fy,2),nG=new Rj("TOP",3),ZH=new Rj(nwe,4),eG=new Rj(F8,5)}function tv(e,n,t){var i,r;if(r=e.gc(),n>=r)throw $(new b2(n,r));if(e.Qi()&&(i=e.bd(t),i>=0&&i!=n))throw $(new Gn($N));return e.Vi(n,t)}function Ld(e,n){var t,i,r;if(r=CHe(e,n),r>=0)return r;if(e.ml()){for(i=0;i0||e==(jX(),Vne)||n==(EX(),Yne))throw $(new Gn("Invalid range: "+uPe(e,n)))}function Tde(e,n,t,i){A8();var r,c;for(r=0,c=0;c0),(n&-n)==n)return sc(n*Is(e,31)*4656612873077393e-25);do t=Is(e,31),i=t%n;while(t-i+(n-1)<0);return sc(i)}function ySn(e,n){var t,i,r;for(t=mw(new Nb,e),r=new L(n);r.a1&&(c=ySn(e,n)),c}function xSn(e){var n,t,i;for(n=0,i=new L(e.c.a);i.a102?-1:e<=57?e-48:e<65?-1:e<=70?e-65+10:e<97?-1:e-97+10}function GQ(e,n){if(e==null)throw $(new c4("null key in entry: null="+n));if(n==null)throw $(new c4("null value in entry: "+e+"=null"))}function nHe(e,n){var t;return t=z(B(Jr,1),Jc,30,15,[lQ(e.a[0],n),lQ(e.a[1],n),lQ(e.a[2],n)]),e.d&&(t[0]=k.Math.max(t[0],t[2]),t[2]=t[0]),t}function tHe(e,n){var t;return t=z(B(Jr,1),Jc,30,15,[BB(e.a[0],n),BB(e.a[1],n),BB(e.a[2],n)]),e.d&&(t[0]=k.Math.max(t[0],t[2]),t[2]=t[0]),t}function Dde(e,n,t){k4(u(T(n,(Ne(),Wi)),102))||(Xae(e,n,Pd(n,t)),Xae(e,n,Pd(n,(De(),bt))),Xae(e,n,Pd(n,Xn)),jn(),Cr(n.j,new nEe(e)))}function iHe(e){var n,t;for(e.c||APn(e),t=new xs,n=new L(e.a),_(n);n.a0&&(Yn(0,n.length),n.charCodeAt(0)==43)?(Yn(1,n.length+1),n.substr(1)):n))}function JSn(e){var n;return e==null?null:new E0((n=bo(e,!0),n.length>0&&(Yn(0,n.length),n.charCodeAt(0)==43)?(Yn(1,n.length+1),n.substr(1)):n))}function _de(e,n,t,i,r,c,o,l){var f,h;i&&(f=i.a[0],f&&_de(e,n,t,f,r,c,o,l),ZQ(e,t,i.d,r,c,o,l)&&n.Ec(i),h=i.a[1],h&&_de(e,n,t,h,r,c,o,l))}function GE(e,n){var t,i,r,c;for(c=e.gc(),n.lengthc&&tr(n,c,null),n}function HSn(e,n){var t,i;if(i=e.gc(),n==null){for(t=0;t0&&(f+=r),h[b]=o,o+=l*(f+i)}function YSn(e){var n;for(n=0;n0?e.c:0),++r;e.b=i,e.d=c}function gHe(e,n){var t;return t=z(B(Jr,1),Jc,30,15,[Cde(e,(ga(),Ou),n),Cde(e,No,n),Cde(e,Nu,n)]),e.f&&(t[0]=k.Math.max(t[0],t[2]),t[2]=t[0]),t}function wHe(e){var n;bi(e,(Ne(),bp))&&(n=u(T(e,bp),22),n.Gc((G2(),Qf))?(n.Kc(Qf),n.Ec(Wf)):n.Gc(Wf)&&(n.Kc(Wf),n.Ec(Qf)))}function pHe(e){var n;bi(e,(Ne(),bp))&&(n=u(T(e,bp),22),n.Gc((G2(),ea))?(n.Kc(ea),n.Ec(pf)):n.Gc(pf)&&(n.Kc(pf),n.Ec(ea)))}function YQ(e,n,t,i){var r,c,o,l;return e.a==null&&XMn(e,n),o=n.b.j.c.length,c=t.d.p,l=i.d.p,r=l-1,r<0&&(r=o-1),c<=r?e.a[r]-e.a[c]:e.a[o-1]-e.a[c]+e.a[r]}function QSn(e){var n;for(n=0;n0&&(r.b+=n),r}function dz(e,n){var t,i,r;for(r=new Kr,i=e.Jc();i.Ob();)t=u(i.Pb(),37),M8(t,0,r.b),r.b+=t.f.b+n,r.a=k.Math.max(r.a,t.f.a);return r.a>0&&(r.a+=n),r}function vHe(e,n){var t,i;if(n.length==0)return 0;for(t=MV(e.a,n[0],(De(),Kn)),t+=MV(e.a,n[n.length-1],et),i=0;i>16==6?e.Cb.Qh(e,5,xa,n):(i=Oc(u(An((t=u(Un(e,16),29),t||e.fi()),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function ixn(e){$9();var n=e.e;if(n&&n.stack){var t=n.stack,i=n+` +`)),ie=G.reduce((Z,W)=>Z.concat(...W),[]);return[G,ie]}return[[],[]]},[g]);return He.useEffect(()=>{const U=E?.target??I1n,G=E?.actInsideInputWithModifier??!0;if(g!==null){const ie=se=>{if(N.current=se.ctrlKey||se.metaKey||se.shiftKey||se.altKey,(!N.current||N.current&&!G)&&Ydn(se))return!1;const ee=L1n(se.code,H);if(P.current.add(se[ee]),_1n(k,P.current,!1)){const Oe=se.composedPath?.()?.[0]||se.target,ge=Oe?.nodeName==="BUTTON"||Oe?.nodeName==="A";E.preventDefault!==!1&&(N.current||!ge)&&se.preventDefault(),M(!0)}},Z=se=>{const le=L1n(se.code,H);_1n(k,P.current,!0)?(M(!1),P.current.clear()):P.current.delete(se[le]),se.key==="Meta"&&P.current.clear(),N.current=!1},W=()=>{P.current.clear(),M(!1)};return U?.addEventListener("keydown",ie),U?.addEventListener("keyup",Z),window.addEventListener("blur",W),window.addEventListener("contextmenu",W),()=>{U?.removeEventListener("keydown",ie),U?.removeEventListener("keyup",Z),window.removeEventListener("blur",W),window.removeEventListener("contextmenu",W)}}},[g,M]),x}function _1n(g,E,x){return g.filter(M=>x||M.length===E.size).some(M=>M.every(N=>E.has(N)))}function L1n(g,E){return E.includes(g)?"code":"key"}const vqn=()=>{const g=Sl();return He.useMemo(()=>({zoomIn:E=>{const{panZoom:x}=g.getState();return x?x.scaleBy(1.2,E):Promise.resolve(!1)},zoomOut:E=>{const{panZoom:x}=g.getState();return x?x.scaleBy(1/1.2,E):Promise.resolve(!1)},zoomTo:(E,x)=>{const{panZoom:M}=g.getState();return M?M.scaleTo(E,x):Promise.resolve(!1)},getZoom:()=>g.getState().transform[2],setViewport:async(E,x)=>{const{transform:[M,N,P],panZoom:k}=g.getState();return k?(await k.setViewport({x:E.x??M,y:E.y??N,zoom:E.zoom??P},x),Promise.resolve(!0)):Promise.resolve(!1)},getViewport:()=>{const[E,x,M]=g.getState().transform;return{x:E,y:x,zoom:M}},setCenter:async(E,x,M)=>g.getState().setCenter(E,x,M),fitBounds:async(E,x)=>{const{width:M,height:N,minZoom:P,maxZoom:k,panZoom:H}=g.getState(),U=Ske(E,M,N,P,k,x?.padding??.1);return H?(await H.setViewport(U,{duration:x?.duration,ease:x?.ease,interpolate:x?.interpolate}),Promise.resolve(!0)):Promise.resolve(!1)},screenToFlowPosition:(E,x={})=>{const{transform:M,snapGrid:N,snapToGrid:P,domNode:k}=g.getState();if(!k)return E;const{x:H,y:U}=k.getBoundingClientRect(),G={x:E.x-H,y:E.y-U},ie=x.snapGrid??N,Z=x.snapToGrid??P;return rq(G,M,Z,ie)},flowToScreenPosition:E=>{const{transform:x,domNode:M}=g.getState();if(!M)return E;const{x:N,y:P}=M.getBoundingClientRect(),k=xue(E,x);return{x:k.x+N,y:k.y+P}}}),[])};function m0n(g,E){const x=[],M=new Map,N=[];for(const P of g)if(P.type==="add"){N.push(P);continue}else if(P.type==="remove"||P.type==="replace")M.set(P.id,[P]);else{const k=M.get(P.id);k?k.push(P):M.set(P.id,[P])}for(const P of E){const k=M.get(P.id);if(!k){x.push(P);continue}if(k[0].type==="remove")continue;if(k[0].type==="replace"){x.push({...k[0].item});continue}const H={...P};for(const U of k)yqn(U,H);x.push(H)}return N.length&&N.forEach(P=>{P.index!==void 0?x.splice(P.index,0,{...P.item}):x.push({...P.item})}),x}function yqn(g,E){switch(g.type){case"select":{E.selected=g.selected;break}case"position":{typeof g.position<"u"&&(E.position=g.position),typeof g.dragging<"u"&&(E.dragging=g.dragging);break}case"dimensions":{typeof g.dimensions<"u"&&(E.measured={...g.dimensions},g.setAttributes&&((g.setAttributes===!0||g.setAttributes==="width")&&(E.width=g.dimensions.width),(g.setAttributes===!0||g.setAttributes==="height")&&(E.height=g.dimensions.height))),typeof g.resizing=="boolean"&&(E.resizing=g.resizing);break}}}function v0n(g,E){return m0n(g,E)}function y0n(g,E){return m0n(g,E)}function vA(g,E){return{id:g,type:"select",selected:E}}function lI(g,E=new Set,x=!1){const M=[];for(const[N,P]of g){const k=E.has(N);!(P.selected===void 0&&!k)&&P.selected!==k&&(x&&(P.selected=k),M.push(vA(P.id,k)))}return M}function P1n({items:g=[],lookup:E}){const x=[],M=new Map(g.map(N=>[N.id,N]));for(const[N,P]of g.entries()){const k=E.get(P.id),H=k?.internals?.userNode??k;H!==void 0&&H!==P&&x.push({id:P.id,item:P,type:"replace"}),H===void 0&&x.push({item:P,type:"add",index:N})}for(const[N]of E)M.get(N)===void 0&&x.push({id:N,type:"remove"});return x}function $1n(g){return{id:g.id,type:"remove"}}const R1n=g=>HHn(g),kqn=g=>Jdn(g);function k0n(g){return He.forwardRef(g)}function B1n(g){const[E,x]=He.useState(BigInt(0)),[M]=He.useState(()=>jqn(()=>x(N=>N+BigInt(1))));return ake(()=>{const N=M.get();N.length&&(g(N),M.reset())},[E]),M}function jqn(g){let E=[];return{get:()=>E,reset:()=>{E=[]},push:x=>{E.push(x),g()}}}const j0n=He.createContext(null);function Eqn({children:g}){const E=Sl(),x=He.useCallback(H=>{const{nodes:U=[],setNodes:G,hasDefaultNodes:ie,onNodesChange:Z,nodeLookup:W,fitViewQueued:se,onNodesChangeMiddlewareMap:le}=E.getState();let ee=U;for(const ge of H)ee=typeof ge=="function"?ge(ee):ge;let Oe=P1n({items:ee,lookup:W});for(const ge of le.values())Oe=ge(Oe);ie&&G(ee),Oe.length>0?Z?.(Oe):se&&window.requestAnimationFrame(()=>{const{fitViewQueued:ge,nodes:Pe,setNodes:ae}=E.getState();ge&&ae(Pe)})},[]),M=B1n(x),N=He.useCallback(H=>{const{edges:U=[],setEdges:G,hasDefaultEdges:ie,onEdgesChange:Z,edgeLookup:W}=E.getState();let se=U;for(const le of H)se=typeof le=="function"?le(se):le;ie?G(se):Z&&Z(P1n({items:se,lookup:W}))},[]),P=B1n(N),k=He.useMemo(()=>({nodeQueue:M,edgeQueue:P}),[]);return F.jsx(j0n.Provider,{value:k,children:g})}function Sqn(){const g=He.useContext(j0n);if(!g)throw new Error("useBatchContext must be used within a BatchProvider");return g}const xqn=g=>!!g.panZoom;function Nke(){const g=vqn(),E=Sl(),x=Sqn(),M=Fu(xqn),N=He.useMemo(()=>{const P=Z=>E.getState().nodeLookup.get(Z),k=Z=>{x.nodeQueue.push(Z)},H=Z=>{x.edgeQueue.push(Z)},U=Z=>{const{nodeLookup:W,nodeOrigin:se}=E.getState(),le=R1n(Z)?Z:W.get(Z.id),ee=le.parentId?Kdn(le.position,le.measured,le.parentId,W,se):le.position,Oe={...le,position:ee,width:le.measured?.width??le.width,height:le.measured?.height??le.height};return wI(Oe)},G=(Z,W,se={replace:!1})=>{k(le=>le.map(ee=>{if(ee.id===Z){const Oe=typeof W=="function"?W(ee):W;return se.replace&&R1n(Oe)?Oe:{...ee,...Oe}}return ee}))},ie=(Z,W,se={replace:!1})=>{H(le=>le.map(ee=>{if(ee.id===Z){const Oe=typeof W=="function"?W(ee):W;return se.replace&&kqn(Oe)?Oe:{...ee,...Oe}}return ee}))};return{getNodes:()=>E.getState().nodes.map(Z=>({...Z})),getNode:Z=>P(Z)?.internals.userNode,getInternalNode:P,getEdges:()=>{const{edges:Z=[]}=E.getState();return Z.map(W=>({...W}))},getEdge:Z=>E.getState().edgeLookup.get(Z),setNodes:k,setEdges:H,addNodes:Z=>{const W=Array.isArray(Z)?Z:[Z];x.nodeQueue.push(se=>[...se,...W])},addEdges:Z=>{const W=Array.isArray(Z)?Z:[Z];x.edgeQueue.push(se=>[...se,...W])},toObject:()=>{const{nodes:Z=[],edges:W=[],transform:se}=E.getState(),[le,ee,Oe]=se;return{nodes:Z.map(ge=>({...ge})),edges:W.map(ge=>({...ge})),viewport:{x:le,y:ee,zoom:Oe}}},deleteElements:async({nodes:Z=[],edges:W=[]})=>{const{nodes:se,edges:le,onNodesDelete:ee,onEdgesDelete:Oe,triggerNodeChanges:ge,triggerEdgeChanges:Pe,onDelete:ae,onBeforeDelete:Me}=E.getState(),{nodes:Be,edges:rn}=await KHn({nodesToRemove:Z,edgesToRemove:W,nodes:se,edges:le,onBeforeDelete:Me}),ln=rn.length>0,xn=Be.length>0;if(ln){const hn=rn.map($1n);Oe?.(rn),Pe(hn)}if(xn){const hn=Be.map($1n);ee?.(Be),ge(hn)}return(xn||ln)&&ae?.({nodes:Be,edges:rn}),{deletedNodes:Be,deletedEdges:rn}},getIntersectingNodes:(Z,W=!0,se)=>{const le=f1n(Z),ee=le?Z:U(Z),Oe=se!==void 0;return ee?(se||E.getState().nodes).filter(ge=>{const Pe=E.getState().nodeLookup.get(ge.id);if(Pe&&!le&&(ge.id===Z.id||!Pe.internals.positionAbsolute))return!1;const ae=wI(Oe?ge:Pe),Me=XG(ae,ee);return W&&Me>0||Me>=ae.width*ae.height||Me>=ee.width*ee.height}):[]},isNodeIntersecting:(Z,W,se=!0)=>{const ee=f1n(Z)?Z:U(Z);if(!ee)return!1;const Oe=XG(ee,W);return se&&Oe>0||Oe>=W.width*W.height||Oe>=ee.width*ee.height},updateNode:G,updateNodeData:(Z,W,se={replace:!1})=>{G(Z,le=>{const ee=typeof W=="function"?W(le):W;return se.replace?{...le,data:ee}:{...le,data:{...le.data,...ee}}},se)},updateEdge:ie,updateEdgeData:(Z,W,se={replace:!1})=>{ie(Z,le=>{const ee=typeof W=="function"?W(le):W;return se.replace?{...le,data:ee}:{...le,data:{...le.data,...ee}}},se)},getNodesBounds:Z=>{const{nodeLookup:W,nodeOrigin:se}=E.getState();return GHn(Z,{nodeLookup:W,nodeOrigin:se})},getHandleConnections:({type:Z,id:W,nodeId:se})=>Array.from(E.getState().connectionLookup.get(`${se}-${Z}${W?`-${W}`:""}`)?.values()??[]),getNodeConnections:({type:Z,handleId:W,nodeId:se})=>Array.from(E.getState().connectionLookup.get(`${se}${Z?W?`-${Z}-${W}`:`-${Z}`:""}`)?.values()??[]),fitView:async Z=>{const W=E.getState().fitViewResolver??WHn();return E.setState({fitViewQueued:!0,fitViewOptions:Z,fitViewResolver:W}),x.nodeQueue.push(se=>[...se]),W.promise}}},[]);return He.useMemo(()=>({...N,...g,viewportInitialized:M}),[M])}const z1n=g=>g.selected,Aqn=typeof window<"u"?window:void 0;function Mqn({deleteKeyCode:g,multiSelectionKeyCode:E}){const x=Sl(),{deleteElements:M}=Nke(),N=VG(g,{actInsideInputWithModifier:!1}),P=VG(E,{target:Aqn});He.useEffect(()=>{if(N){const{edges:k,nodes:H}=x.getState();M({nodes:H.filter(z1n),edges:k.filter(z1n)}),x.setState({nodesSelectionActive:!1})}},[N]),He.useEffect(()=>{x.setState({multiSelectionActive:P})},[P])}function Cqn(g){const E=Sl();He.useEffect(()=>{const x=()=>{if(!g.current||!(g.current.checkVisibility?.()??!0))return!1;const M=xke(g.current);(M.height===0||M.width===0)&&E.getState().onError?.("004",u5.error004()),E.setState({width:M.width||500,height:M.height||500})};if(g.current){x(),window.addEventListener("resize",x);const M=new ResizeObserver(()=>x());return M.observe(g.current),()=>{window.removeEventListener("resize",x),M&&g.current&&M.unobserve(g.current)}}},[])}const zue={position:"absolute",width:"100%",height:"100%",top:0,left:0},Tqn=g=>({userSelectionActive:g.userSelectionActive,lib:g.lib,connectionInProgress:g.connection.inProgress});function Oqn({onPaneContextMenu:g,zoomOnScroll:E=!0,zoomOnPinch:x=!0,panOnScroll:M=!1,panOnScrollSpeed:N=.5,panOnScrollMode:P=jA.Free,zoomOnDoubleClick:k=!0,panOnDrag:H=!0,defaultViewport:U,translateExtent:G,minZoom:ie,maxZoom:Z,zoomActivationKeyCode:W,preventScrolling:se=!0,children:le,noWheelClassName:ee,noPanClassName:Oe,onViewportChange:ge,isControlledViewport:Pe,paneClickDistance:ae,selectionOnDrag:Me}){const Be=Sl(),rn=He.useRef(null),{userSelectionActive:ln,lib:xn,connectionInProgress:hn}=Fu(Tqn,El),wt=VG(W),Tn=He.useRef();Cqn(rn);const Qn=He.useCallback(Y=>{ge?.({x:Y[0],y:Y[1],zoom:Y[2]}),Pe||Be.setState({transform:Y})},[ge,Pe]);return He.useEffect(()=>{if(rn.current){Tn.current=LGn({domNode:rn.current,minZoom:ie,maxZoom:Z,translateExtent:G,viewport:U,onDraggingChange:Ae=>Be.setState(Ze=>Ze.paneDragging===Ae?Ze:{paneDragging:Ae}),onPanZoomStart:(Ae,Ze)=>{const{onViewportChangeStart:sn,onMoveStart:Sn}=Be.getState();Sn?.(Ae,Ze),sn?.(Ze)},onPanZoom:(Ae,Ze)=>{const{onViewportChange:sn,onMove:Sn}=Be.getState();Sn?.(Ae,Ze),sn?.(Ze)},onPanZoomEnd:(Ae,Ze)=>{const{onViewportChangeEnd:sn,onMoveEnd:Sn}=Be.getState();Sn?.(Ae,Ze),sn?.(Ze)}});const{x:Y,y:Fe,zoom:mn}=Tn.current.getViewport();return Be.setState({panZoom:Tn.current,transform:[Y,Fe,mn],domNode:rn.current.closest(".react-flow")}),()=>{Tn.current?.destroy()}}},[]),He.useEffect(()=>{Tn.current?.update({onPaneContextMenu:g,zoomOnScroll:E,zoomOnPinch:x,panOnScroll:M,panOnScrollSpeed:N,panOnScrollMode:P,zoomOnDoubleClick:k,panOnDrag:H,zoomActivationKeyPressed:wt,preventScrolling:se,noPanClassName:Oe,userSelectionActive:ln,noWheelClassName:ee,lib:xn,onTransformChange:Qn,connectionInProgress:hn,selectionOnDrag:Me,paneClickDistance:ae})},[g,E,x,M,N,P,k,H,wt,se,Oe,ln,ee,xn,Qn,hn,Me,ae]),F.jsx("div",{className:"react-flow__renderer",ref:rn,style:zue,children:le})}const Nqn=g=>({userSelectionActive:g.userSelectionActive,userSelectionRect:g.userSelectionRect});function Dqn(){const{userSelectionActive:g,userSelectionRect:E}=Fu(Nqn,El);return g&&E?F.jsx("div",{className:"react-flow__selection react-flow__container",style:{width:E.width,height:E.height,transform:`translate(${E.x}px, ${E.y}px)`}}):null}const q7e=(g,E)=>x=>{x.target===E.current&&g?.(x)},Iqn=g=>({userSelectionActive:g.userSelectionActive,elementsSelectable:g.elementsSelectable,connectionInProgress:g.connection.inProgress,dragging:g.paneDragging});function _qn({isSelecting:g,selectionKeyPressed:E,selectionMode:x=qG.Full,panOnDrag:M,paneClickDistance:N,selectionOnDrag:P,onSelectionStart:k,onSelectionEnd:H,onPaneClick:U,onPaneContextMenu:G,onPaneScroll:ie,onPaneMouseEnter:Z,onPaneMouseMove:W,onPaneMouseLeave:se,children:le}){const ee=Sl(),{userSelectionActive:Oe,elementsSelectable:ge,dragging:Pe,connectionInProgress:ae}=Fu(Iqn,El),Me=ge&&(g||Oe),Be=He.useRef(null),rn=He.useRef(),ln=He.useRef(new Set),xn=He.useRef(new Set),hn=He.useRef(!1),wt=sn=>{if(hn.current||ae){hn.current=!1;return}U?.(sn),ee.getState().resetSelectedElements(),ee.setState({nodesSelectionActive:!1})},Tn=sn=>{if(Array.isArray(M)&&M?.includes(2)){sn.preventDefault();return}G?.(sn)},Qn=ie?sn=>ie(sn):void 0,Y=sn=>{hn.current&&(sn.stopPropagation(),hn.current=!1)},Fe=sn=>{const{domNode:Sn}=ee.getState();if(rn.current=Sn?.getBoundingClientRect(),!rn.current)return;const kn=sn.target===Be.current;if(!kn&&!!sn.target.closest(".nokey")||!g||!(P&&kn||E)||sn.button!==0||!sn.isPrimary)return;sn.target?.setPointerCapture?.(sn.pointerId),hn.current=!1;const{x:rt,y:lt}=Vm(sn.nativeEvent,rn.current);ee.setState({userSelectionRect:{width:0,height:0,startX:rt,startY:lt,x:rt,y:lt}}),kn||(sn.stopPropagation(),sn.preventDefault())},mn=sn=>{const{userSelectionRect:Sn,transform:kn,nodeLookup:xe,edgeLookup:un,connectionLookup:rt,triggerNodeChanges:lt,triggerEdgeChanges:Bt,defaultEdgeOptions:hi,resetSelectedElements:Gt}=ee.getState();if(!rn.current||!Sn)return;const{x:At,y:st}=Vm(sn.nativeEvent,rn.current),{startX:Wr,startY:Mr}=Sn;if(!hn.current){const Eu=E?0:N;if(Math.hypot(At-Wr,st-Mr)<=Eu)return;Gt(),k?.(sn)}hn.current=!0;const ur={startX:Wr,startY:Mr,x:AtEu.id)),xn.current=new Set;const fu=hi?.selectable??!0;for(const Eu of ln.current){const Ws=rt.get(Eu);if(Ws)for(const{edgeId:Dh}of Ws.values()){const ef=un.get(Dh);ef&&(ef.selectable??fu)&&xn.current.add(Dh)}}if(!a1n(mi,ln.current)){const Eu=lI(xe,ln.current,!0);lt(Eu)}if(!a1n(Fi,xn.current)){const Eu=lI(un,xn.current);Bt(Eu)}ee.setState({userSelectionRect:ur,userSelectionActive:!0,nodesSelectionActive:!1})},Ae=sn=>{sn.button===0&&(sn.target?.releasePointerCapture?.(sn.pointerId),!Oe&&sn.target===Be.current&&ee.getState().userSelectionRect&&wt?.(sn),ee.setState({userSelectionActive:!1,userSelectionRect:null}),hn.current&&(H?.(sn),ee.setState({nodesSelectionActive:ln.current.size>0})))},Ze=M===!0||Array.isArray(M)&&M.includes(0);return F.jsxs("div",{className:Ca(["react-flow__pane",{draggable:Ze,dragging:Pe,selection:g}]),onClick:Me?void 0:q7e(wt,Be),onContextMenu:q7e(Tn,Be),onWheel:q7e(Qn,Be),onPointerEnter:Me?void 0:Z,onPointerMove:Me?mn:W,onPointerUp:Me?Ae:void 0,onPointerDownCapture:Me?Fe:void 0,onClickCapture:Me?Y:void 0,onPointerLeave:se,ref:Be,style:zue,children:[le,F.jsx(Dqn,{})]})}function hke({id:g,store:E,unselect:x=!1,nodeRef:M}){const{addSelectedNodes:N,unselectNodesAndEdges:P,multiSelectionActive:k,nodeLookup:H,onError:U}=E.getState(),G=H.get(g);if(!G){U?.("012",u5.error012(g));return}E.setState({nodesSelectionActive:!1}),G.selected?(x||G.selected&&k)&&(P({nodes:[G],edges:[]}),requestAnimationFrame(()=>M?.current?.blur())):N([g])}function E0n({nodeRef:g,disabled:E=!1,noDragClassName:x,handleSelector:M,nodeId:N,isSelectable:P,nodeClickDistance:k}){const H=Sl(),[U,G]=He.useState(!1),ie=He.useRef();return He.useEffect(()=>{ie.current=kGn({getStoreItems:()=>H.getState(),onNodeMouseDown:Z=>{hke({id:Z,store:H,nodeRef:g})},onDragStart:()=>{G(!0)},onDragStop:()=>{G(!1)}})},[]),He.useEffect(()=>{if(!(E||!g.current||!ie.current))return ie.current.update({noDragClassName:x,handleSelector:M,domNode:g.current,isSelectable:P,nodeId:N,nodeClickDistance:k}),()=>{ie.current?.destroy()}},[x,M,E,P,g,N,k]),U}const Lqn=g=>E=>E.selected&&(E.draggable||g&&typeof E.draggable>"u");function S0n(){const g=Sl();return He.useCallback(x=>{const{nodeExtent:M,snapToGrid:N,snapGrid:P,nodesDraggable:k,onError:H,updateNodePositions:U,nodeLookup:G,nodeOrigin:ie}=g.getState(),Z=new Map,W=Lqn(k),se=N?P[0]:5,le=N?P[1]:5,ee=x.direction.x*se*x.factor,Oe=x.direction.y*le*x.factor;for(const[,ge]of G){if(!W(ge))continue;let Pe={x:ge.internals.positionAbsolute.x+ee,y:ge.internals.positionAbsolute.y+Oe};N&&(Pe=iq(Pe,P));const{position:ae,positionAbsolute:Me}=Hdn({nodeId:ge.id,nextPosition:Pe,nodeLookup:G,nodeExtent:M,nodeOrigin:ie,onError:H});ge.position=ae,ge.internals.positionAbsolute=Me,Z.set(ge.id,ge)}U(Z)},[])}const Dke=He.createContext(null),Pqn=Dke.Provider;Dke.Consumer;const x0n=()=>He.useContext(Dke),$qn=g=>({connectOnClick:g.connectOnClick,noPanClassName:g.noPanClassName,rfId:g.rfId}),Rqn=(g,E,x)=>M=>{const{connectionClickStartHandle:N,connectionMode:P,connection:k}=M,{fromHandle:H,toHandle:U,isValid:G}=k,ie=U?.nodeId===g&&U?.id===E&&U?.type===x;return{connectingFrom:H?.nodeId===g&&H?.id===E&&H?.type===x,connectingTo:ie,clickConnecting:N?.nodeId===g&&N?.id===E&&N?.type===x,isPossibleEndHandle:P===bI.Strict?H?.type!==x:g!==H?.nodeId||E!==H?.id,connectionInProcess:!!H,clickConnectionInProcess:!!N,valid:ie&&G}};function Bqn({type:g="source",position:E=cr.Top,isValidConnection:x,isConnectable:M=!0,isConnectableStart:N=!0,isConnectableEnd:P=!0,id:k,onConnect:H,children:U,className:G,onMouseDown:ie,onTouchStart:Z,...W},se){const le=k||null,ee=g==="target",Oe=Sl(),ge=x0n(),{connectOnClick:Pe,noPanClassName:ae,rfId:Me}=Fu($qn,El),{connectingFrom:Be,connectingTo:rn,clickConnecting:ln,isPossibleEndHandle:xn,connectionInProcess:hn,clickConnectionInProcess:wt,valid:Tn}=Fu(Rqn(ge,le,g),El);ge||Oe.getState().onError?.("010",u5.error010());const Qn=mn=>{const{defaultEdgeOptions:Ae,onConnect:Ze,hasDefaultEdges:sn}=Oe.getState(),Sn={...Ae,...mn};if(sn){const{edges:kn,setEdges:xe}=Oe.getState();xe(cGn(Sn,kn))}Ze?.(Sn),H?.(Sn)},Y=mn=>{if(!ge)return;const Ae=Qdn(mn.nativeEvent);if(N&&(Ae&&mn.button===0||!Ae)){const Ze=Oe.getState();fke.onPointerDown(mn.nativeEvent,{handleDomNode:mn.currentTarget,autoPanOnConnect:Ze.autoPanOnConnect,connectionMode:Ze.connectionMode,connectionRadius:Ze.connectionRadius,domNode:Ze.domNode,nodeLookup:Ze.nodeLookup,lib:Ze.lib,isTarget:ee,handleId:le,nodeId:ge,flowId:Ze.rfId,panBy:Ze.panBy,cancelConnection:Ze.cancelConnection,onConnectStart:Ze.onConnectStart,onConnectEnd:(...sn)=>Oe.getState().onConnectEnd?.(...sn),updateConnection:Ze.updateConnection,onConnect:Qn,isValidConnection:x||((...sn)=>Oe.getState().isValidConnection?.(...sn)??!0),getTransform:()=>Oe.getState().transform,getFromHandle:()=>Oe.getState().connection.fromHandle,autoPanSpeed:Ze.autoPanSpeed,dragThreshold:Ze.connectionDragThreshold})}Ae?ie?.(mn):Z?.(mn)},Fe=mn=>{const{onClickConnectStart:Ae,onClickConnectEnd:Ze,connectionClickStartHandle:sn,connectionMode:Sn,isValidConnection:kn,lib:xe,rfId:un,nodeLookup:rt,connection:lt}=Oe.getState();if(!ge||!sn&&!N)return;if(!sn){Ae?.(mn.nativeEvent,{nodeId:ge,handleId:le,handleType:g}),Oe.setState({connectionClickStartHandle:{nodeId:ge,type:g,id:le}});return}const Bt=Vdn(mn.target),hi=x||kn,{connection:Gt,isValid:At}=fke.isValid(mn.nativeEvent,{handle:{nodeId:ge,id:le,type:g},connectionMode:Sn,fromNodeId:sn.nodeId,fromHandleId:sn.id||null,fromType:sn.type,isValidConnection:hi,flowId:un,doc:Bt,lib:xe,nodeLookup:rt});At&&Gt&&Qn(Gt);const st=structuredClone(lt);delete st.inProgress,st.toPosition=st.toHandle?st.toHandle.position:null,Ze?.(mn,st),Oe.setState({connectionClickStartHandle:null})};return F.jsx("div",{"data-handleid":le,"data-nodeid":ge,"data-handlepos":E,"data-id":`${Me}-${ge}-${le}-${g}`,className:Ca(["react-flow__handle",`react-flow__handle-${E}`,"nodrag",ae,G,{source:!ee,target:ee,connectable:M,connectablestart:N,connectableend:P,clickconnecting:ln,connectingfrom:Be,connectingto:rn,valid:Tn,connectionindicator:M&&(!hn||xn)&&(hn||wt?P:N)}]),onMouseDown:Y,onTouchStart:Y,onClick:Pe?Fe:void 0,ref:se,...W,children:U})}const o5=He.memo(k0n(Bqn));function zqn({data:g,isConnectable:E,sourcePosition:x=cr.Bottom}){return F.jsxs(F.Fragment,{children:[g?.label,F.jsx(o5,{type:"source",position:x,isConnectable:E})]})}function Fqn({data:g,isConnectable:E,targetPosition:x=cr.Top,sourcePosition:M=cr.Bottom}){return F.jsxs(F.Fragment,{children:[F.jsx(o5,{type:"target",position:x,isConnectable:E}),g?.label,F.jsx(o5,{type:"source",position:M,isConnectable:E})]})}function Jqn(){return null}function Hqn({data:g,isConnectable:E,targetPosition:x=cr.Top}){return F.jsxs(F.Fragment,{children:[F.jsx(o5,{type:"target",position:x,isConnectable:E}),g?.label]})}const Mue={ArrowUp:{x:0,y:-1},ArrowDown:{x:0,y:1},ArrowLeft:{x:-1,y:0},ArrowRight:{x:1,y:0}},F1n={input:zqn,default:Fqn,output:Hqn,group:Jqn};function Gqn(g){return g.internals.handleBounds===void 0?{width:g.width??g.initialWidth??g.style?.width,height:g.height??g.initialHeight??g.style?.height}:{width:g.width??g.style?.width,height:g.height??g.style?.height}}const qqn=g=>{const{width:E,height:x,x:M,y:N}=tq(g.nodeLookup,{filter:P=>!!P.selected});return{width:Km(E)?E:null,height:Km(x)?x:null,userSelectionActive:g.userSelectionActive,transformString:`translate(${g.transform[0]}px,${g.transform[1]}px) scale(${g.transform[2]}) translate(${M}px,${N}px)`}};function Uqn({onSelectionContextMenu:g,noPanClassName:E,disableKeyboardA11y:x}){const M=Sl(),{width:N,height:P,transformString:k,userSelectionActive:H}=Fu(qqn,El),U=S0n(),G=He.useRef(null);He.useEffect(()=>{x||G.current?.focus({preventScroll:!0})},[x]);const ie=!H&&N!==null&&P!==null;if(E0n({nodeRef:G,disabled:!ie}),!ie)return null;const Z=g?se=>{const le=M.getState().nodes.filter(ee=>ee.selected);g(se,le)}:void 0,W=se=>{Object.prototype.hasOwnProperty.call(Mue,se.key)&&(se.preventDefault(),U({direction:Mue[se.key],factor:se.shiftKey?4:1}))};return F.jsx("div",{className:Ca(["react-flow__nodesselection","react-flow__container",E]),style:{transform:k},children:F.jsx("div",{ref:G,className:"react-flow__nodesselection-rect",onContextMenu:Z,tabIndex:x?void 0:-1,onKeyDown:x?void 0:W,style:{width:N,height:P}})})}const J1n=typeof window<"u"?window:void 0,Xqn=g=>({nodesSelectionActive:g.nodesSelectionActive,userSelectionActive:g.userSelectionActive});function A0n({children:g,onPaneClick:E,onPaneMouseEnter:x,onPaneMouseMove:M,onPaneMouseLeave:N,onPaneContextMenu:P,onPaneScroll:k,paneClickDistance:H,deleteKeyCode:U,selectionKeyCode:G,selectionOnDrag:ie,selectionMode:Z,onSelectionStart:W,onSelectionEnd:se,multiSelectionKeyCode:le,panActivationKeyCode:ee,zoomActivationKeyCode:Oe,elementsSelectable:ge,zoomOnScroll:Pe,zoomOnPinch:ae,panOnScroll:Me,panOnScrollSpeed:Be,panOnScrollMode:rn,zoomOnDoubleClick:ln,panOnDrag:xn,defaultViewport:hn,translateExtent:wt,minZoom:Tn,maxZoom:Qn,preventScrolling:Y,onSelectionContextMenu:Fe,noWheelClassName:mn,noPanClassName:Ae,disableKeyboardA11y:Ze,onViewportChange:sn,isControlledViewport:Sn}){const{nodesSelectionActive:kn,userSelectionActive:xe}=Fu(Xqn,El),un=VG(G,{target:J1n}),rt=VG(ee,{target:J1n}),lt=rt||xn,Bt=rt||Me,hi=ie&<!==!0,Gt=un||xe||hi;return Mqn({deleteKeyCode:U,multiSelectionKeyCode:le}),F.jsx(Oqn,{onPaneContextMenu:P,elementsSelectable:ge,zoomOnScroll:Pe,zoomOnPinch:ae,panOnScroll:Bt,panOnScrollSpeed:Be,panOnScrollMode:rn,zoomOnDoubleClick:ln,panOnDrag:!un&<,defaultViewport:hn,translateExtent:wt,minZoom:Tn,maxZoom:Qn,zoomActivationKeyCode:Oe,preventScrolling:Y,noWheelClassName:mn,noPanClassName:Ae,onViewportChange:sn,isControlledViewport:Sn,paneClickDistance:H,selectionOnDrag:hi,children:F.jsxs(_qn,{onSelectionStart:W,onSelectionEnd:se,onPaneClick:E,onPaneMouseEnter:x,onPaneMouseMove:M,onPaneMouseLeave:N,onPaneContextMenu:P,onPaneScroll:k,panOnDrag:lt,isSelecting:!!Gt,selectionMode:Z,selectionKeyPressed:un,paneClickDistance:H,selectionOnDrag:hi,children:[g,kn&&F.jsx(Uqn,{onSelectionContextMenu:Fe,noPanClassName:Ae,disableKeyboardA11y:Ze})]})})}A0n.displayName="FlowRenderer";const Kqn=He.memo(A0n),Vqn=g=>E=>g?Eke(E.nodeLookup,{x:0,y:0,width:E.width,height:E.height},E.transform,!0).map(x=>x.id):Array.from(E.nodeLookup.keys());function Yqn(g){return Fu(He.useCallback(Vqn(g),[g]),El)}const Qqn=g=>g.updateNodeInternals;function Wqn(){const g=Fu(Qqn),[E]=He.useState(()=>typeof ResizeObserver>"u"?null:new ResizeObserver(x=>{const M=new Map;x.forEach(N=>{const P=N.target.getAttribute("data-id");M.set(P,{id:P,nodeElement:N.target,force:!0})}),g(M)}));return He.useEffect(()=>()=>{E?.disconnect()},[E]),E}function Zqn({node:g,nodeType:E,hasDimensions:x,resizeObserver:M}){const N=Sl(),P=He.useRef(null),k=He.useRef(null),H=He.useRef(g.sourcePosition),U=He.useRef(g.targetPosition),G=He.useRef(E),ie=x&&!!g.internals.handleBounds;return He.useEffect(()=>{P.current&&!g.hidden&&(!ie||k.current!==P.current)&&(k.current&&M?.unobserve(k.current),M?.observe(P.current),k.current=P.current)},[ie,g.hidden]),He.useEffect(()=>()=>{k.current&&(M?.unobserve(k.current),k.current=null)},[]),He.useEffect(()=>{if(P.current){const Z=G.current!==E,W=H.current!==g.sourcePosition,se=U.current!==g.targetPosition;(Z||W||se)&&(G.current=E,H.current=g.sourcePosition,U.current=g.targetPosition,N.getState().updateNodeInternals(new Map([[g.id,{id:g.id,nodeElement:P.current,force:!0}]])))}},[g.id,E,g.sourcePosition,g.targetPosition]),P}function eUn({id:g,onClick:E,onMouseEnter:x,onMouseMove:M,onMouseLeave:N,onContextMenu:P,onDoubleClick:k,nodesDraggable:H,elementsSelectable:U,nodesConnectable:G,nodesFocusable:ie,resizeObserver:Z,noDragClassName:W,noPanClassName:se,disableKeyboardA11y:le,rfId:ee,nodeTypes:Oe,nodeClickDistance:ge,onError:Pe}){const{node:ae,internals:Me,isParent:Be}=Fu(At=>{const st=At.nodeLookup.get(g),Wr=At.parentLookup.has(g);return{node:st,internals:st.internals,isParent:Wr}},El);let rn=ae.type||"default",ln=Oe?.[rn]||F1n[rn];ln===void 0&&(Pe?.("003",u5.error003(rn)),rn="default",ln=Oe?.default||F1n.default);const xn=!!(ae.draggable||H&&typeof ae.draggable>"u"),hn=!!(ae.selectable||U&&typeof ae.selectable>"u"),wt=!!(ae.connectable||G&&typeof ae.connectable>"u"),Tn=!!(ae.focusable||ie&&typeof ae.focusable>"u"),Qn=Sl(),Y=Xdn(ae),Fe=Zqn({node:ae,nodeType:rn,hasDimensions:Y,resizeObserver:Z}),mn=E0n({nodeRef:Fe,disabled:ae.hidden||!xn,noDragClassName:W,handleSelector:ae.dragHandle,nodeId:g,isSelectable:hn,nodeClickDistance:ge}),Ae=S0n();if(ae.hidden)return null;const Ze=i6(ae),sn=Gqn(ae),Sn=hn||xn||E||x||M||N,kn=x?At=>x(At,{...Me.userNode}):void 0,xe=M?At=>M(At,{...Me.userNode}):void 0,un=N?At=>N(At,{...Me.userNode}):void 0,rt=P?At=>P(At,{...Me.userNode}):void 0,lt=k?At=>k(At,{...Me.userNode}):void 0,Bt=At=>{const{selectNodesOnDrag:st,nodeDragThreshold:Wr}=Qn.getState();hn&&(!st||!xn||Wr>0)&&hke({id:g,store:Qn,nodeRef:Fe}),E&&E(At,{...Me.userNode})},hi=At=>{if(!(Ydn(At.nativeEvent)||le)){if(Rdn.includes(At.key)&&hn){const st=At.key==="Escape";hke({id:g,store:Qn,unselect:st,nodeRef:Fe})}else if(xn&&ae.selected&&Object.prototype.hasOwnProperty.call(Mue,At.key)){At.preventDefault();const{ariaLabelConfig:st}=Qn.getState();Qn.setState({ariaLiveMessage:st["node.a11yDescription.ariaLiveMessage"]({direction:At.key.replace("Arrow","").toLowerCase(),x:~~Me.positionAbsolute.x,y:~~Me.positionAbsolute.y})}),Ae({direction:Mue[At.key],factor:At.shiftKey?4:1})}}},Gt=()=>{if(le||!Fe.current?.matches(":focus-visible"))return;const{transform:At,width:st,height:Wr,autoPanOnNodeFocus:Mr,setCenter:ur}=Qn.getState();if(!Mr)return;Eke(new Map([[g,ae]]),{x:0,y:0,width:st,height:Wr},At,!0).length>0||ur(ae.position.x+Ze.width/2,ae.position.y+Ze.height/2,{zoom:At[2]})};return F.jsx("div",{className:Ca(["react-flow__node",`react-flow__node-${rn}`,{[se]:xn},ae.className,{selected:ae.selected,selectable:hn,parent:Be,draggable:xn,dragging:mn}]),ref:Fe,style:{zIndex:Me.z,transform:`translate(${Me.positionAbsolute.x}px,${Me.positionAbsolute.y}px)`,pointerEvents:Sn?"all":"none",visibility:Y?"visible":"hidden",...ae.style,...sn},"data-id":g,"data-testid":`rf__node-${g}`,onMouseEnter:kn,onMouseMove:xe,onMouseLeave:un,onContextMenu:rt,onClick:Bt,onDoubleClick:lt,onKeyDown:Tn?hi:void 0,tabIndex:Tn?0:void 0,onFocus:Tn?Gt:void 0,role:ae.ariaRole??(Tn?"group":void 0),"aria-roledescription":"node","aria-describedby":le?void 0:`${g0n}-${ee}`,"aria-label":ae.ariaLabel,...ae.domAttributes,children:F.jsx(Pqn,{value:g,children:F.jsx(ln,{id:g,data:ae.data,type:rn,positionAbsoluteX:Me.positionAbsolute.x,positionAbsoluteY:Me.positionAbsolute.y,selected:ae.selected??!1,selectable:hn,draggable:xn,deletable:ae.deletable??!0,isConnectable:wt,sourcePosition:ae.sourcePosition,targetPosition:ae.targetPosition,dragging:mn,dragHandle:ae.dragHandle,zIndex:Me.z,parentId:ae.parentId,...Ze})})})}var nUn=He.memo(eUn);const tUn=g=>({nodesDraggable:g.nodesDraggable,nodesConnectable:g.nodesConnectable,nodesFocusable:g.nodesFocusable,elementsSelectable:g.elementsSelectable,onError:g.onError});function M0n(g){const{nodesDraggable:E,nodesConnectable:x,nodesFocusable:M,elementsSelectable:N,onError:P}=Fu(tUn,El),k=Yqn(g.onlyRenderVisibleElements),H=Wqn();return F.jsx("div",{className:"react-flow__nodes",style:zue,children:k.map(U=>F.jsx(nUn,{id:U,nodeTypes:g.nodeTypes,nodeExtent:g.nodeExtent,onClick:g.onNodeClick,onMouseEnter:g.onNodeMouseEnter,onMouseMove:g.onNodeMouseMove,onMouseLeave:g.onNodeMouseLeave,onContextMenu:g.onNodeContextMenu,onDoubleClick:g.onNodeDoubleClick,noDragClassName:g.noDragClassName,noPanClassName:g.noPanClassName,rfId:g.rfId,disableKeyboardA11y:g.disableKeyboardA11y,resizeObserver:H,nodesDraggable:E,nodesConnectable:x,nodesFocusable:M,elementsSelectable:N,nodeClickDistance:g.nodeClickDistance,onError:P},U))})}M0n.displayName="NodeRenderer";const iUn=He.memo(M0n);function rUn(g){return Fu(He.useCallback(x=>{if(!g)return x.edges.map(N=>N.id);const M=[];if(x.width&&x.height)for(const N of x.edges){const P=x.nodeLookup.get(N.source),k=x.nodeLookup.get(N.target);P&&k&&tGn({sourceNode:P,targetNode:k,width:x.width,height:x.height,transform:x.transform})&&M.push(N.id)}return M},[g]),El)}const cUn=({color:g="none",strokeWidth:E=1})=>{const x={strokeWidth:E,...g&&{stroke:g}};return F.jsx("polyline",{className:"arrow",style:x,strokeLinecap:"round",fill:"none",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4"})},uUn=({color:g="none",strokeWidth:E=1})=>{const x={strokeWidth:E,...g&&{stroke:g,fill:g}};return F.jsx("polyline",{className:"arrowclosed",style:x,strokeLinecap:"round",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4 -5,-4"})},H1n={[UG.Arrow]:cUn,[UG.ArrowClosed]:uUn};function oUn(g){const E=Sl();return He.useMemo(()=>Object.prototype.hasOwnProperty.call(H1n,g)?H1n[g]:(E.getState().onError?.("009",u5.error009(g)),null),[g])}const sUn=({id:g,type:E,color:x,width:M=12.5,height:N=12.5,markerUnits:P="strokeWidth",strokeWidth:k,orient:H="auto-start-reverse"})=>{const U=oUn(E);return U?F.jsx("marker",{className:"react-flow__arrowhead",id:g,markerWidth:`${M}`,markerHeight:`${N}`,viewBox:"-10 -10 20 20",markerUnits:P,orient:H,refX:"0",refY:"0",children:F.jsx(U,{color:x,strokeWidth:k})}):null},C0n=({defaultColor:g,rfId:E})=>{const x=Fu(P=>P.edges),M=Fu(P=>P.defaultEdgeOptions),N=He.useMemo(()=>fGn(x,{id:E,defaultColor:g,defaultMarkerStart:M?.markerStart,defaultMarkerEnd:M?.markerEnd}),[x,M,E,g]);return N.length?F.jsx("svg",{className:"react-flow__marker","aria-hidden":"true",children:F.jsx("defs",{children:N.map(P=>F.jsx(sUn,{id:P.id,type:P.type,color:P.color,width:P.width,height:P.height,markerUnits:P.markerUnits,strokeWidth:P.strokeWidth,orient:P.orient},P.id))})}):null};C0n.displayName="MarkerDefinitions";var lUn=He.memo(C0n);function T0n({x:g,y:E,label:x,labelStyle:M,labelShowBg:N=!0,labelBgStyle:P,labelBgPadding:k=[2,4],labelBgBorderRadius:H=2,children:U,className:G,...ie}){const[Z,W]=He.useState({x:1,y:0,width:0,height:0}),se=Ca(["react-flow__edge-textwrapper",G]),le=He.useRef(null);return He.useEffect(()=>{if(le.current){const ee=le.current.getBBox();W({x:ee.x,y:ee.y,width:ee.width,height:ee.height})}},[x]),x?F.jsxs("g",{transform:`translate(${g-Z.width/2} ${E-Z.height/2})`,className:se,visibility:Z.width?"visible":"hidden",...ie,children:[N&&F.jsx("rect",{width:Z.width+2*k[0],x:-k[0],y:-k[1],height:Z.height+2*k[1],className:"react-flow__edge-textbg",style:P,rx:H,ry:H}),F.jsx("text",{className:"react-flow__edge-text",y:Z.height/2,dy:"0.3em",ref:le,style:M,children:x}),U]}):null}T0n.displayName="EdgeText";const fUn=He.memo(T0n);function cq({path:g,labelX:E,labelY:x,label:M,labelStyle:N,labelShowBg:P,labelBgStyle:k,labelBgPadding:H,labelBgBorderRadius:U,interactionWidth:G=20,...ie}){return F.jsxs(F.Fragment,{children:[F.jsx("path",{...ie,d:g,fill:"none",className:Ca(["react-flow__edge-path",ie.className])}),G?F.jsx("path",{d:g,fill:"none",strokeOpacity:0,strokeWidth:G,className:"react-flow__edge-interaction"}):null,M&&Km(E)&&Km(x)?F.jsx(fUn,{x:E,y:x,label:M,labelStyle:N,labelShowBg:P,labelBgStyle:k,labelBgPadding:H,labelBgBorderRadius:U}):null]})}function G1n({pos:g,x1:E,y1:x,x2:M,y2:N}){return g===cr.Left||g===cr.Right?[.5*(E+M),x]:[E,.5*(x+N)]}function O0n({sourceX:g,sourceY:E,sourcePosition:x=cr.Bottom,targetX:M,targetY:N,targetPosition:P=cr.Top}){const[k,H]=G1n({pos:x,x1:g,y1:E,x2:M,y2:N}),[U,G]=G1n({pos:P,x1:M,y1:N,x2:g,y2:E}),[ie,Z,W,se]=Wdn({sourceX:g,sourceY:E,targetX:M,targetY:N,sourceControlX:k,sourceControlY:H,targetControlX:U,targetControlY:G});return[`M${g},${E} C${k},${H} ${U},${G} ${M},${N}`,ie,Z,W,se]}function N0n(g){return He.memo(({id:E,sourceX:x,sourceY:M,targetX:N,targetY:P,sourcePosition:k,targetPosition:H,label:U,labelStyle:G,labelShowBg:ie,labelBgStyle:Z,labelBgPadding:W,labelBgBorderRadius:se,style:le,markerEnd:ee,markerStart:Oe,interactionWidth:ge})=>{const[Pe,ae,Me]=O0n({sourceX:x,sourceY:M,sourcePosition:k,targetX:N,targetY:P,targetPosition:H}),Be=g.isInternal?void 0:E;return F.jsx(cq,{id:Be,path:Pe,labelX:ae,labelY:Me,label:U,labelStyle:G,labelShowBg:ie,labelBgStyle:Z,labelBgPadding:W,labelBgBorderRadius:se,style:le,markerEnd:ee,markerStart:Oe,interactionWidth:ge})})}const aUn=N0n({isInternal:!1}),D0n=N0n({isInternal:!0});aUn.displayName="SimpleBezierEdge";D0n.displayName="SimpleBezierEdgeInternal";function I0n(g){return He.memo(({id:E,sourceX:x,sourceY:M,targetX:N,targetY:P,label:k,labelStyle:H,labelShowBg:U,labelBgStyle:G,labelBgPadding:ie,labelBgBorderRadius:Z,style:W,sourcePosition:se=cr.Bottom,targetPosition:le=cr.Top,markerEnd:ee,markerStart:Oe,pathOptions:ge,interactionWidth:Pe})=>{const[ae,Me,Be]=Aue({sourceX:x,sourceY:M,sourcePosition:se,targetX:N,targetY:P,targetPosition:le,borderRadius:ge?.borderRadius,offset:ge?.offset,stepPosition:ge?.stepPosition}),rn=g.isInternal?void 0:E;return F.jsx(cq,{id:rn,path:ae,labelX:Me,labelY:Be,label:k,labelStyle:H,labelShowBg:U,labelBgStyle:G,labelBgPadding:ie,labelBgBorderRadius:Z,style:W,markerEnd:ee,markerStart:Oe,interactionWidth:Pe})})}const _0n=I0n({isInternal:!1}),L0n=I0n({isInternal:!0});_0n.displayName="SmoothStepEdge";L0n.displayName="SmoothStepEdgeInternal";function P0n(g){return He.memo(({id:E,...x})=>{const M=g.isInternal?void 0:E;return F.jsx(_0n,{...x,id:M,pathOptions:He.useMemo(()=>({borderRadius:0,offset:x.pathOptions?.offset}),[x.pathOptions?.offset])})})}const hUn=P0n({isInternal:!1}),$0n=P0n({isInternal:!0});hUn.displayName="StepEdge";$0n.displayName="StepEdgeInternal";function R0n(g){return He.memo(({id:E,sourceX:x,sourceY:M,targetX:N,targetY:P,label:k,labelStyle:H,labelShowBg:U,labelBgStyle:G,labelBgPadding:ie,labelBgBorderRadius:Z,style:W,markerEnd:se,markerStart:le,interactionWidth:ee})=>{const[Oe,ge,Pe]=n0n({sourceX:x,sourceY:M,targetX:N,targetY:P}),ae=g.isInternal?void 0:E;return F.jsx(cq,{id:ae,path:Oe,labelX:ge,labelY:Pe,label:k,labelStyle:H,labelShowBg:U,labelBgStyle:G,labelBgPadding:ie,labelBgBorderRadius:Z,style:W,markerEnd:se,markerStart:le,interactionWidth:ee})})}const dUn=R0n({isInternal:!1}),B0n=R0n({isInternal:!0});dUn.displayName="StraightEdge";B0n.displayName="StraightEdgeInternal";function z0n(g){return He.memo(({id:E,sourceX:x,sourceY:M,targetX:N,targetY:P,sourcePosition:k=cr.Bottom,targetPosition:H=cr.Top,label:U,labelStyle:G,labelShowBg:ie,labelBgStyle:Z,labelBgPadding:W,labelBgBorderRadius:se,style:le,markerEnd:ee,markerStart:Oe,pathOptions:ge,interactionWidth:Pe})=>{const[ae,Me,Be]=Zdn({sourceX:x,sourceY:M,sourcePosition:k,targetX:N,targetY:P,targetPosition:H,curvature:ge?.curvature}),rn=g.isInternal?void 0:E;return F.jsx(cq,{id:rn,path:ae,labelX:Me,labelY:Be,label:U,labelStyle:G,labelShowBg:ie,labelBgStyle:Z,labelBgPadding:W,labelBgBorderRadius:se,style:le,markerEnd:ee,markerStart:Oe,interactionWidth:Pe})})}const bUn=z0n({isInternal:!1}),F0n=z0n({isInternal:!0});bUn.displayName="BezierEdge";F0n.displayName="BezierEdgeInternal";const q1n={default:F0n,straight:B0n,step:$0n,smoothstep:L0n,simplebezier:D0n},U1n={sourceX:null,sourceY:null,targetX:null,targetY:null,sourcePosition:null,targetPosition:null},gUn=(g,E,x)=>x===cr.Left?g-E:x===cr.Right?g+E:g,wUn=(g,E,x)=>x===cr.Top?g-E:x===cr.Bottom?g+E:g,X1n="react-flow__edgeupdater";function K1n({position:g,centerX:E,centerY:x,radius:M=10,onMouseDown:N,onMouseEnter:P,onMouseOut:k,type:H}){return F.jsx("circle",{onMouseDown:N,onMouseEnter:P,onMouseOut:k,className:Ca([X1n,`${X1n}-${H}`]),cx:gUn(E,M,g),cy:wUn(x,M,g),r:M,stroke:"transparent",fill:"transparent"})}function pUn({isReconnectable:g,reconnectRadius:E,edge:x,sourceX:M,sourceY:N,targetX:P,targetY:k,sourcePosition:H,targetPosition:U,onReconnect:G,onReconnectStart:ie,onReconnectEnd:Z,setReconnecting:W,setUpdateHover:se}){const le=Sl(),ee=(Me,Be)=>{if(Me.button!==0)return;const{autoPanOnConnect:rn,domNode:ln,connectionMode:xn,connectionRadius:hn,lib:wt,onConnectStart:Tn,cancelConnection:Qn,nodeLookup:Y,rfId:Fe,panBy:mn,updateConnection:Ae}=le.getState(),Ze=Be.type==="target",sn=(xe,un)=>{W(!1),Z?.(xe,x,Be.type,un)},Sn=xe=>G?.(x,xe),kn=(xe,un)=>{W(!0),ie?.(Me,x,Be.type),Tn?.(xe,un)};fke.onPointerDown(Me.nativeEvent,{autoPanOnConnect:rn,connectionMode:xn,connectionRadius:hn,domNode:ln,handleId:Be.id,nodeId:Be.nodeId,nodeLookup:Y,isTarget:Ze,edgeUpdaterType:Be.type,lib:wt,flowId:Fe,cancelConnection:Qn,panBy:mn,isValidConnection:(...xe)=>le.getState().isValidConnection?.(...xe)??!0,onConnect:Sn,onConnectStart:kn,onConnectEnd:(...xe)=>le.getState().onConnectEnd?.(...xe),onReconnectEnd:sn,updateConnection:Ae,getTransform:()=>le.getState().transform,getFromHandle:()=>le.getState().connection.fromHandle,dragThreshold:le.getState().connectionDragThreshold,handleDomNode:Me.currentTarget})},Oe=Me=>ee(Me,{nodeId:x.target,id:x.targetHandle??null,type:"target"}),ge=Me=>ee(Me,{nodeId:x.source,id:x.sourceHandle??null,type:"source"}),Pe=()=>se(!0),ae=()=>se(!1);return F.jsxs(F.Fragment,{children:[(g===!0||g==="source")&&F.jsx(K1n,{position:H,centerX:M,centerY:N,radius:E,onMouseDown:Oe,onMouseEnter:Pe,onMouseOut:ae,type:"source"}),(g===!0||g==="target")&&F.jsx(K1n,{position:U,centerX:P,centerY:k,radius:E,onMouseDown:ge,onMouseEnter:Pe,onMouseOut:ae,type:"target"})]})}function mUn({id:g,edgesFocusable:E,edgesReconnectable:x,elementsSelectable:M,onClick:N,onDoubleClick:P,onContextMenu:k,onMouseEnter:H,onMouseMove:U,onMouseLeave:G,reconnectRadius:ie,onReconnect:Z,onReconnectStart:W,onReconnectEnd:se,rfId:le,edgeTypes:ee,noPanClassName:Oe,onError:ge,disableKeyboardA11y:Pe}){let ae=Fu(ur=>ur.edgeLookup.get(g));const Me=Fu(ur=>ur.defaultEdgeOptions);ae=Me?{...Me,...ae}:ae;let Be=ae.type||"default",rn=ee?.[Be]||q1n[Be];rn===void 0&&(ge?.("011",u5.error011(Be)),Be="default",rn=ee?.default||q1n.default);const ln=!!(ae.focusable||E&&typeof ae.focusable>"u"),xn=typeof Z<"u"&&(ae.reconnectable||x&&typeof ae.reconnectable>"u"),hn=!!(ae.selectable||M&&typeof ae.selectable>"u"),wt=He.useRef(null),[Tn,Qn]=He.useState(!1),[Y,Fe]=He.useState(!1),mn=Sl(),{zIndex:Ae,sourceX:Ze,sourceY:sn,targetX:Sn,targetY:kn,sourcePosition:xe,targetPosition:un}=Fu(He.useCallback(ur=>{const mi=ur.nodeLookup.get(ae.source),Fi=ur.nodeLookup.get(ae.target);if(!mi||!Fi)return{zIndex:ae.zIndex,...U1n};const fu=lGn({id:g,sourceNode:mi,targetNode:Fi,sourceHandle:ae.sourceHandle||null,targetHandle:ae.targetHandle||null,connectionMode:ur.connectionMode,onError:ge});return{zIndex:nGn({selected:ae.selected,zIndex:ae.zIndex,sourceNode:mi,targetNode:Fi,elevateOnSelect:ur.elevateEdgesOnSelect,zIndexMode:ur.zIndexMode}),...fu||U1n}},[ae.source,ae.target,ae.sourceHandle,ae.targetHandle,ae.selected,ae.zIndex]),El),rt=He.useMemo(()=>ae.markerStart?`url('#${ske(ae.markerStart,le)}')`:void 0,[ae.markerStart,le]),lt=He.useMemo(()=>ae.markerEnd?`url('#${ske(ae.markerEnd,le)}')`:void 0,[ae.markerEnd,le]);if(ae.hidden||Ze===null||sn===null||Sn===null||kn===null)return null;const Bt=ur=>{const{addSelectedEdges:mi,unselectNodesAndEdges:Fi,multiSelectionActive:fu}=mn.getState();hn&&(mn.setState({nodesSelectionActive:!1}),ae.selected&&fu?(Fi({nodes:[],edges:[ae]}),wt.current?.blur()):mi([g])),N&&N(ur,ae)},hi=P?ur=>{P(ur,{...ae})}:void 0,Gt=k?ur=>{k(ur,{...ae})}:void 0,At=H?ur=>{H(ur,{...ae})}:void 0,st=U?ur=>{U(ur,{...ae})}:void 0,Wr=G?ur=>{G(ur,{...ae})}:void 0,Mr=ur=>{if(!Pe&&Rdn.includes(ur.key)&&hn){const{unselectNodesAndEdges:mi,addSelectedEdges:Fi}=mn.getState();ur.key==="Escape"?(wt.current?.blur(),mi({edges:[ae]})):Fi([g])}};return F.jsx("svg",{style:{zIndex:Ae},children:F.jsxs("g",{className:Ca(["react-flow__edge",`react-flow__edge-${Be}`,ae.className,Oe,{selected:ae.selected,animated:ae.animated,inactive:!hn&&!N,updating:Tn,selectable:hn}]),onClick:Bt,onDoubleClick:hi,onContextMenu:Gt,onMouseEnter:At,onMouseMove:st,onMouseLeave:Wr,onKeyDown:ln?Mr:void 0,tabIndex:ln?0:void 0,role:ae.ariaRole??(ln?"group":"img"),"aria-roledescription":"edge","data-id":g,"data-testid":`rf__edge-${g}`,"aria-label":ae.ariaLabel===null?void 0:ae.ariaLabel||`Edge from ${ae.source} to ${ae.target}`,"aria-describedby":ln?`${w0n}-${le}`:void 0,ref:wt,...ae.domAttributes,children:[!Y&&F.jsx(rn,{id:g,source:ae.source,target:ae.target,type:ae.type,selected:ae.selected,animated:ae.animated,selectable:hn,deletable:ae.deletable??!0,label:ae.label,labelStyle:ae.labelStyle,labelShowBg:ae.labelShowBg,labelBgStyle:ae.labelBgStyle,labelBgPadding:ae.labelBgPadding,labelBgBorderRadius:ae.labelBgBorderRadius,sourceX:Ze,sourceY:sn,targetX:Sn,targetY:kn,sourcePosition:xe,targetPosition:un,data:ae.data,style:ae.style,sourceHandleId:ae.sourceHandle,targetHandleId:ae.targetHandle,markerStart:rt,markerEnd:lt,pathOptions:"pathOptions"in ae?ae.pathOptions:void 0,interactionWidth:ae.interactionWidth}),xn&&F.jsx(pUn,{edge:ae,isReconnectable:xn,reconnectRadius:ie,onReconnect:Z,onReconnectStart:W,onReconnectEnd:se,sourceX:Ze,sourceY:sn,targetX:Sn,targetY:kn,sourcePosition:xe,targetPosition:un,setUpdateHover:Qn,setReconnecting:Fe})]})})}var vUn=He.memo(mUn);const yUn=g=>({edgesFocusable:g.edgesFocusable,edgesReconnectable:g.edgesReconnectable,elementsSelectable:g.elementsSelectable,connectionMode:g.connectionMode,onError:g.onError});function J0n({defaultMarkerColor:g,onlyRenderVisibleElements:E,rfId:x,edgeTypes:M,noPanClassName:N,onReconnect:P,onEdgeContextMenu:k,onEdgeMouseEnter:H,onEdgeMouseMove:U,onEdgeMouseLeave:G,onEdgeClick:ie,reconnectRadius:Z,onEdgeDoubleClick:W,onReconnectStart:se,onReconnectEnd:le,disableKeyboardA11y:ee}){const{edgesFocusable:Oe,edgesReconnectable:ge,elementsSelectable:Pe,onError:ae}=Fu(yUn,El),Me=rUn(E);return F.jsxs("div",{className:"react-flow__edges",children:[F.jsx(lUn,{defaultColor:g,rfId:x}),Me.map(Be=>F.jsx(vUn,{id:Be,edgesFocusable:Oe,edgesReconnectable:ge,elementsSelectable:Pe,noPanClassName:N,onReconnect:P,onContextMenu:k,onMouseEnter:H,onMouseMove:U,onMouseLeave:G,onClick:ie,reconnectRadius:Z,onDoubleClick:W,onReconnectStart:se,onReconnectEnd:le,rfId:x,onError:ae,edgeTypes:M,disableKeyboardA11y:ee},Be))]})}J0n.displayName="EdgeRenderer";const kUn=He.memo(J0n),jUn=g=>`translate(${g.transform[0]}px,${g.transform[1]}px) scale(${g.transform[2]})`;function EUn({children:g}){const E=Fu(jUn);return F.jsx("div",{className:"react-flow__viewport xyflow__viewport react-flow__container",style:{transform:E},children:g})}function SUn(g){const E=Nke(),x=He.useRef(!1);He.useEffect(()=>{!x.current&&E.viewportInitialized&&g&&(setTimeout(()=>g(E),1),x.current=!0)},[g,E.viewportInitialized])}const xUn=g=>g.panZoom?.syncViewport;function AUn(g){const E=Fu(xUn),x=Sl();return He.useEffect(()=>{g&&(E?.(g),x.setState({transform:[g.x,g.y,g.zoom]}))},[g,E]),null}function MUn(g){return g.connection.inProgress?{...g.connection,to:rq(g.connection.to,g.transform)}:{...g.connection}}function CUn(g){return MUn}function TUn(g){const E=CUn();return Fu(E,El)}const OUn=g=>({nodesConnectable:g.nodesConnectable,isValid:g.connection.isValid,inProgress:g.connection.inProgress,width:g.width,height:g.height});function NUn({containerStyle:g,style:E,type:x,component:M}){const{nodesConnectable:N,width:P,height:k,isValid:H,inProgress:U}=Fu(OUn,El);return!(P&&N&&U)?null:F.jsx("svg",{style:g,width:P,height:k,className:"react-flow__connectionline react-flow__container",children:F.jsx("g",{className:Ca(["react-flow__connection",Fdn(H)]),children:F.jsx(H0n,{style:E,type:x,CustomComponent:M,isValid:H})})})}const H0n=({style:g,type:E=W7.Bezier,CustomComponent:x,isValid:M})=>{const{inProgress:N,from:P,fromNode:k,fromHandle:H,fromPosition:U,to:G,toNode:ie,toHandle:Z,toPosition:W,pointer:se}=TUn();if(!N)return;if(x)return F.jsx(x,{connectionLineType:E,connectionLineStyle:g,fromNode:k,fromHandle:H,fromX:P.x,fromY:P.y,toX:G.x,toY:G.y,fromPosition:U,toPosition:W,connectionStatus:Fdn(M),toNode:ie,toHandle:Z,pointer:se});let le="";const ee={sourceX:P.x,sourceY:P.y,sourcePosition:U,targetX:G.x,targetY:G.y,targetPosition:W};switch(E){case W7.Bezier:[le]=Zdn(ee);break;case W7.SimpleBezier:[le]=O0n(ee);break;case W7.Step:[le]=Aue({...ee,borderRadius:0});break;case W7.SmoothStep:[le]=Aue(ee);break;default:[le]=n0n(ee)}return F.jsx("path",{d:le,fill:"none",className:"react-flow__connection-path",style:g})};H0n.displayName="ConnectionLine";const DUn={};function V1n(g=DUn){He.useRef(g),Sl(),He.useEffect(()=>{},[g])}function IUn(){Sl(),He.useRef(!1),He.useEffect(()=>{},[])}function G0n({nodeTypes:g,edgeTypes:E,onInit:x,onNodeClick:M,onEdgeClick:N,onNodeDoubleClick:P,onEdgeDoubleClick:k,onNodeMouseEnter:H,onNodeMouseMove:U,onNodeMouseLeave:G,onNodeContextMenu:ie,onSelectionContextMenu:Z,onSelectionStart:W,onSelectionEnd:se,connectionLineType:le,connectionLineStyle:ee,connectionLineComponent:Oe,connectionLineContainerStyle:ge,selectionKeyCode:Pe,selectionOnDrag:ae,selectionMode:Me,multiSelectionKeyCode:Be,panActivationKeyCode:rn,zoomActivationKeyCode:ln,deleteKeyCode:xn,onlyRenderVisibleElements:hn,elementsSelectable:wt,defaultViewport:Tn,translateExtent:Qn,minZoom:Y,maxZoom:Fe,preventScrolling:mn,defaultMarkerColor:Ae,zoomOnScroll:Ze,zoomOnPinch:sn,panOnScroll:Sn,panOnScrollSpeed:kn,panOnScrollMode:xe,zoomOnDoubleClick:un,panOnDrag:rt,onPaneClick:lt,onPaneMouseEnter:Bt,onPaneMouseMove:hi,onPaneMouseLeave:Gt,onPaneScroll:At,onPaneContextMenu:st,paneClickDistance:Wr,nodeClickDistance:Mr,onEdgeContextMenu:ur,onEdgeMouseEnter:mi,onEdgeMouseMove:Fi,onEdgeMouseLeave:fu,reconnectRadius:Eu,onReconnect:Ws,onReconnectStart:Dh,onReconnectEnd:ef,noDragClassName:ch,noWheelClassName:kc,noPanClassName:cd,disableKeyboardA11y:jb,nodeExtent:Rs,rfId:c0,viewport:u0,onViewportChange:o0}){return V1n(g),V1n(E),IUn(),SUn(x),AUn(u0),F.jsx(Kqn,{onPaneClick:lt,onPaneMouseEnter:Bt,onPaneMouseMove:hi,onPaneMouseLeave:Gt,onPaneContextMenu:st,onPaneScroll:At,paneClickDistance:Wr,deleteKeyCode:xn,selectionKeyCode:Pe,selectionOnDrag:ae,selectionMode:Me,onSelectionStart:W,onSelectionEnd:se,multiSelectionKeyCode:Be,panActivationKeyCode:rn,zoomActivationKeyCode:ln,elementsSelectable:wt,zoomOnScroll:Ze,zoomOnPinch:sn,zoomOnDoubleClick:un,panOnScroll:Sn,panOnScrollSpeed:kn,panOnScrollMode:xe,panOnDrag:rt,defaultViewport:Tn,translateExtent:Qn,minZoom:Y,maxZoom:Fe,onSelectionContextMenu:Z,preventScrolling:mn,noDragClassName:ch,noWheelClassName:kc,noPanClassName:cd,disableKeyboardA11y:jb,onViewportChange:o0,isControlledViewport:!!u0,children:F.jsxs(EUn,{children:[F.jsx(kUn,{edgeTypes:E,onEdgeClick:N,onEdgeDoubleClick:k,onReconnect:Ws,onReconnectStart:Dh,onReconnectEnd:ef,onlyRenderVisibleElements:hn,onEdgeContextMenu:ur,onEdgeMouseEnter:mi,onEdgeMouseMove:Fi,onEdgeMouseLeave:fu,reconnectRadius:Eu,defaultMarkerColor:Ae,noPanClassName:cd,disableKeyboardA11y:jb,rfId:c0}),F.jsx(NUn,{style:ee,type:le,component:Oe,containerStyle:ge}),F.jsx("div",{className:"react-flow__edgelabel-renderer"}),F.jsx(iUn,{nodeTypes:g,onNodeClick:M,onNodeDoubleClick:P,onNodeMouseEnter:H,onNodeMouseMove:U,onNodeMouseLeave:G,onNodeContextMenu:ie,nodeClickDistance:Mr,onlyRenderVisibleElements:hn,noPanClassName:cd,noDragClassName:ch,disableKeyboardA11y:jb,nodeExtent:Rs,rfId:c0}),F.jsx("div",{className:"react-flow__viewport-portal"})]})})}G0n.displayName="GraphView";const _Un=He.memo(G0n),Y1n=({nodes:g,edges:E,defaultNodes:x,defaultEdges:M,width:N,height:P,fitView:k,fitViewOptions:H,minZoom:U=.5,maxZoom:G=2,nodeOrigin:ie,nodeExtent:Z,zIndexMode:W="basic"}={})=>{const se=new Map,le=new Map,ee=new Map,Oe=new Map,ge=M??E??[],Pe=x??g??[],ae=ie??[0,0],Me=Z??GG;r0n(ee,Oe,ge);const{nodesInitialized:Be}=lke(Pe,se,le,{nodeOrigin:ae,nodeExtent:Me,zIndexMode:W});let rn=[0,0,1];if(k&&N&&P){const ln=tq(se,{filter:Tn=>!!((Tn.width||Tn.initialWidth)&&(Tn.height||Tn.initialHeight))}),{x:xn,y:hn,zoom:wt}=Ske(ln,N,P,U,G,H?.padding??.1);rn=[xn,hn,wt]}return{rfId:"1",width:N??0,height:P??0,transform:rn,nodes:Pe,nodesInitialized:Be,nodeLookup:se,parentLookup:le,edges:ge,edgeLookup:Oe,connectionLookup:ee,onNodesChange:null,onEdgesChange:null,hasDefaultNodes:x!==void 0,hasDefaultEdges:M!==void 0,panZoom:null,minZoom:U,maxZoom:G,translateExtent:GG,nodeExtent:Me,nodesSelectionActive:!1,userSelectionActive:!1,userSelectionRect:null,connectionMode:bI.Strict,domNode:null,paneDragging:!1,noPanClassName:"nopan",nodeOrigin:ae,nodeDragThreshold:1,connectionDragThreshold:1,snapGrid:[15,15],snapToGrid:!1,nodesDraggable:!0,nodesConnectable:!0,nodesFocusable:!0,edgesFocusable:!0,edgesReconnectable:!0,elementsSelectable:!0,elevateNodesOnSelect:!0,elevateEdgesOnSelect:!0,selectNodesOnDrag:!0,multiSelectionActive:!1,fitViewQueued:k??!1,fitViewOptions:H,fitViewResolver:null,connection:{...zdn},connectionClickStartHandle:null,connectOnClick:!0,ariaLiveMessage:"",autoPanOnConnect:!0,autoPanOnNodeDrag:!0,autoPanOnNodeFocus:!0,autoPanSpeed:15,connectionRadius:20,onError:VHn,isValidConnection:void 0,onSelectionChangeHandlers:[],lib:"react",debug:!1,ariaLabelConfig:Bdn,zIndexMode:W,onNodesChangeMiddlewareMap:new Map,onEdgesChangeMiddlewareMap:new Map}},LUn=({nodes:g,edges:E,defaultNodes:x,defaultEdges:M,width:N,height:P,fitView:k,fitViewOptions:H,minZoom:U,maxZoom:G,nodeOrigin:ie,nodeExtent:Z,zIndexMode:W})=>ZGn((se,le)=>{async function ee(){const{nodeLookup:Oe,panZoom:ge,fitViewOptions:Pe,fitViewResolver:ae,width:Me,height:Be,minZoom:rn,maxZoom:ln}=le();ge&&(await XHn({nodes:Oe,width:Me,height:Be,panZoom:ge,minZoom:rn,maxZoom:ln},Pe),ae?.resolve(!0),se({fitViewResolver:null}))}return{...Y1n({nodes:g,edges:E,width:N,height:P,fitView:k,fitViewOptions:H,minZoom:U,maxZoom:G,nodeOrigin:ie,nodeExtent:Z,defaultNodes:x,defaultEdges:M,zIndexMode:W}),setNodes:Oe=>{const{nodeLookup:ge,parentLookup:Pe,nodeOrigin:ae,elevateNodesOnSelect:Me,fitViewQueued:Be,zIndexMode:rn,nodesSelectionActive:ln}=le(),{nodesInitialized:xn,hasSelectedNodes:hn}=lke(Oe,ge,Pe,{nodeOrigin:ae,nodeExtent:Z,elevateNodesOnSelect:Me,checkEquality:!0,zIndexMode:rn}),wt=ln&&hn;Be&&xn?(ee(),se({nodes:Oe,nodesInitialized:xn,fitViewQueued:!1,fitViewOptions:void 0,nodesSelectionActive:wt})):se({nodes:Oe,nodesInitialized:xn,nodesSelectionActive:wt})},setEdges:Oe=>{const{connectionLookup:ge,edgeLookup:Pe}=le();r0n(ge,Pe,Oe),se({edges:Oe})},setDefaultNodesAndEdges:(Oe,ge)=>{if(Oe){const{setNodes:Pe}=le();Pe(Oe),se({hasDefaultNodes:!0})}if(ge){const{setEdges:Pe}=le();Pe(ge),se({hasDefaultEdges:!0})}},updateNodeInternals:Oe=>{const{triggerNodeChanges:ge,nodeLookup:Pe,parentLookup:ae,domNode:Me,nodeOrigin:Be,nodeExtent:rn,debug:ln,fitViewQueued:xn,zIndexMode:hn}=le(),{changes:wt,updatedInternals:Tn}=pGn(Oe,Pe,ae,Me,Be,rn,hn);Tn&&(dGn(Pe,ae,{nodeOrigin:Be,nodeExtent:rn,zIndexMode:hn}),xn?(ee(),se({fitViewQueued:!1,fitViewOptions:void 0})):se({}),wt?.length>0&&(ln&&console.log("React Flow: trigger node changes",wt),ge?.(wt)))},updateNodePositions:(Oe,ge=!1)=>{const Pe=[];let ae=[];const{nodeLookup:Me,triggerNodeChanges:Be,connection:rn,updateConnection:ln,onNodesChangeMiddlewareMap:xn}=le();for(const[hn,wt]of Oe){const Tn=Me.get(hn),Qn=!!(Tn?.expandParent&&Tn?.parentId&&wt?.position),Y={id:hn,type:"position",position:Qn?{x:Math.max(0,wt.position.x),y:Math.max(0,wt.position.y)}:wt.position,dragging:ge};if(Tn&&rn.inProgress&&rn.fromNode.id===Tn.id){const Fe=AA(Tn,rn.fromHandle,cr.Left,!0);ln({...rn,from:Fe})}Qn&&Tn.parentId&&Pe.push({id:hn,parentId:Tn.parentId,rect:{...wt.internals.positionAbsolute,width:wt.measured.width??0,height:wt.measured.height??0}}),ae.push(Y)}if(Pe.length>0){const{parentLookup:hn,nodeOrigin:wt}=le(),Tn=Oke(Pe,Me,hn,wt);ae.push(...Tn)}for(const hn of xn.values())ae=hn(ae);Be(ae)},triggerNodeChanges:Oe=>{const{onNodesChange:ge,setNodes:Pe,nodes:ae,hasDefaultNodes:Me,debug:Be}=le();if(Oe?.length){if(Me){const rn=v0n(Oe,ae);Pe(rn)}Be&&console.log("React Flow: trigger node changes",Oe),ge?.(Oe)}},triggerEdgeChanges:Oe=>{const{onEdgesChange:ge,setEdges:Pe,edges:ae,hasDefaultEdges:Me,debug:Be}=le();if(Oe?.length){if(Me){const rn=y0n(Oe,ae);Pe(rn)}Be&&console.log("React Flow: trigger edge changes",Oe),ge?.(Oe)}},addSelectedNodes:Oe=>{const{multiSelectionActive:ge,edgeLookup:Pe,nodeLookup:ae,triggerNodeChanges:Me,triggerEdgeChanges:Be}=le();if(ge){const rn=Oe.map(ln=>vA(ln,!0));Me(rn);return}Me(lI(ae,new Set([...Oe]),!0)),Be(lI(Pe))},addSelectedEdges:Oe=>{const{multiSelectionActive:ge,edgeLookup:Pe,nodeLookup:ae,triggerNodeChanges:Me,triggerEdgeChanges:Be}=le();if(ge){const rn=Oe.map(ln=>vA(ln,!0));Be(rn);return}Be(lI(Pe,new Set([...Oe]))),Me(lI(ae,new Set,!0))},unselectNodesAndEdges:({nodes:Oe,edges:ge}={})=>{const{edges:Pe,nodes:ae,nodeLookup:Me,triggerNodeChanges:Be,triggerEdgeChanges:rn}=le(),ln=Oe||ae,xn=ge||Pe,hn=[];for(const Tn of ln){if(!Tn.selected)continue;const Qn=Me.get(Tn.id);Qn&&(Qn.selected=!1),hn.push(vA(Tn.id,!1))}const wt=[];for(const Tn of xn)Tn.selected&&wt.push(vA(Tn.id,!1));Be(hn),rn(wt)},setMinZoom:Oe=>{const{panZoom:ge,maxZoom:Pe}=le();ge?.setScaleExtent([Oe,Pe]),se({minZoom:Oe})},setMaxZoom:Oe=>{const{panZoom:ge,minZoom:Pe}=le();ge?.setScaleExtent([Pe,Oe]),se({maxZoom:Oe})},setTranslateExtent:Oe=>{le().panZoom?.setTranslateExtent(Oe),se({translateExtent:Oe})},resetSelectedElements:()=>{const{edges:Oe,nodes:ge,triggerNodeChanges:Pe,triggerEdgeChanges:ae,elementsSelectable:Me}=le();if(!Me)return;const Be=ge.reduce((ln,xn)=>xn.selected?[...ln,vA(xn.id,!1)]:ln,[]),rn=Oe.reduce((ln,xn)=>xn.selected?[...ln,vA(xn.id,!1)]:ln,[]);Pe(Be),ae(rn)},setNodeExtent:Oe=>{const{nodes:ge,nodeLookup:Pe,parentLookup:ae,nodeOrigin:Me,elevateNodesOnSelect:Be,nodeExtent:rn,zIndexMode:ln}=le();Oe[0][0]===rn[0][0]&&Oe[0][1]===rn[0][1]&&Oe[1][0]===rn[1][0]&&Oe[1][1]===rn[1][1]||(lke(ge,Pe,ae,{nodeOrigin:Me,nodeExtent:Oe,elevateNodesOnSelect:Be,checkEquality:!1,zIndexMode:ln}),se({nodeExtent:Oe}))},panBy:Oe=>{const{transform:ge,width:Pe,height:ae,panZoom:Me,translateExtent:Be}=le();return mGn({delta:Oe,panZoom:Me,transform:ge,translateExtent:Be,width:Pe,height:ae})},setCenter:async(Oe,ge,Pe)=>{const{width:ae,height:Me,maxZoom:Be,panZoom:rn}=le();if(!rn)return Promise.resolve(!1);const ln=typeof Pe?.zoom<"u"?Pe.zoom:Be;return await rn.setViewport({x:ae/2-Oe*ln,y:Me/2-ge*ln,zoom:ln},{duration:Pe?.duration,ease:Pe?.ease,interpolate:Pe?.interpolate}),Promise.resolve(!0)},cancelConnection:()=>{se({connection:{...zdn}})},updateConnection:Oe=>{se({connection:Oe})},reset:()=>se({...Y1n()})}},Object.is);function PUn({initialNodes:g,initialEdges:E,defaultNodes:x,defaultEdges:M,initialWidth:N,initialHeight:P,initialMinZoom:k,initialMaxZoom:H,initialFitViewOptions:U,fitView:G,nodeOrigin:ie,nodeExtent:Z,zIndexMode:W,children:se}){const[le]=He.useState(()=>LUn({nodes:g,edges:E,defaultNodes:x,defaultEdges:M,width:N,height:P,fitView:G,minZoom:k,maxZoom:H,fitViewOptions:U,nodeOrigin:ie,nodeExtent:Z,zIndexMode:W}));return F.jsx(nqn,{value:le,children:F.jsx(Eqn,{children:se})})}function $Un({children:g,nodes:E,edges:x,defaultNodes:M,defaultEdges:N,width:P,height:k,fitView:H,fitViewOptions:U,minZoom:G,maxZoom:ie,nodeOrigin:Z,nodeExtent:W,zIndexMode:se}){return He.useContext(Rue)?F.jsx(F.Fragment,{children:g}):F.jsx(PUn,{initialNodes:E,initialEdges:x,defaultNodes:M,defaultEdges:N,initialWidth:P,initialHeight:k,fitView:H,initialFitViewOptions:U,initialMinZoom:G,initialMaxZoom:ie,nodeOrigin:Z,nodeExtent:W,zIndexMode:se,children:g})}const RUn={width:"100%",height:"100%",overflow:"hidden",position:"relative",zIndex:0};function BUn({nodes:g,edges:E,defaultNodes:x,defaultEdges:M,className:N,nodeTypes:P,edgeTypes:k,onNodeClick:H,onEdgeClick:U,onInit:G,onMove:ie,onMoveStart:Z,onMoveEnd:W,onConnect:se,onConnectStart:le,onConnectEnd:ee,onClickConnectStart:Oe,onClickConnectEnd:ge,onNodeMouseEnter:Pe,onNodeMouseMove:ae,onNodeMouseLeave:Me,onNodeContextMenu:Be,onNodeDoubleClick:rn,onNodeDragStart:ln,onNodeDrag:xn,onNodeDragStop:hn,onNodesDelete:wt,onEdgesDelete:Tn,onDelete:Qn,onSelectionChange:Y,onSelectionDragStart:Fe,onSelectionDrag:mn,onSelectionDragStop:Ae,onSelectionContextMenu:Ze,onSelectionStart:sn,onSelectionEnd:Sn,onBeforeDelete:kn,connectionMode:xe,connectionLineType:un=W7.Bezier,connectionLineStyle:rt,connectionLineComponent:lt,connectionLineContainerStyle:Bt,deleteKeyCode:hi="Backspace",selectionKeyCode:Gt="Shift",selectionOnDrag:At=!1,selectionMode:st=qG.Full,panActivationKeyCode:Wr="Space",multiSelectionKeyCode:Mr=KG()?"Meta":"Control",zoomActivationKeyCode:ur=KG()?"Meta":"Control",snapToGrid:mi,snapGrid:Fi,onlyRenderVisibleElements:fu=!1,selectNodesOnDrag:Eu,nodesDraggable:Ws,autoPanOnNodeFocus:Dh,nodesConnectable:ef,nodesFocusable:ch,nodeOrigin:kc=p0n,edgesFocusable:cd,edgesReconnectable:jb,elementsSelectable:Rs=!0,defaultViewport:c0=bqn,minZoom:u0=.5,maxZoom:o0=2,translateExtent:Bg=GG,preventScrolling:r6=!0,nodeExtent:zg,defaultMarkerColor:c6="#b1b1b7",zoomOnScroll:d1=!0,zoomOnPinch:ud=!0,panOnScroll:Sf=!1,panOnScrollSpeed:b1=.5,panOnScrollMode:xf=jA.Free,zoomOnDoubleClick:l5=!0,panOnDrag:f5=!0,onPaneClick:a5,onPaneMouseEnter:Fg,onPaneMouseMove:Eb,onPaneMouseLeave:Jg,onPaneScroll:_u,onPaneContextMenu:Hg,paneClickDistance:Gg=1,nodeClickDistance:u6=0,children:h5,onReconnect:Wm,onReconnectStart:qg,onReconnectEnd:o6,onEdgeContextMenu:Ug,onEdgeDoubleClick:Zm,onEdgeMouseEnter:d5,onEdgeMouseMove:Mt,onEdgeMouseLeave:Cc,reconnectRadius:dc=10,onNodesChange:s6,onEdgesChange:Af,noDragClassName:Zs="nodrag",noWheelClassName:Ta="nowheel",noPanClassName:Xg="nopan",fitView:b5,fitViewOptions:ek,connectOnClick:MA,attributionPosition:nk,proOptions:e3,defaultEdgeOptions:l6,elevateNodesOnSelect:xp=!0,elevateEdgesOnSelect:Ap=!1,disableKeyboardA11y:Mp=!1,autoPanOnConnect:Cp,autoPanOnNodeDrag:xl,autoPanSpeed:g5,connectionRadius:tk,isValidConnection:Kg,onError:Tp,style:CA,id:f6,nodeDragThreshold:ik,connectionDragThreshold:rk,viewport:n3,onViewportChange:w5,width:s0,height:Ih,colorMode:ck="light",debug:TA,onScroll:p5,ariaLabelConfig:uk,zIndexMode:t3="basic",...OA},_h){const i3=f6||"1",ok=mqn(ck),a6=He.useCallback(Vg=>{Vg.currentTarget.scrollTo({top:0,left:0,behavior:"instant"}),p5?.(Vg)},[p5]);return F.jsx("div",{"data-testid":"rf__wrapper",...OA,onScroll:a6,style:{...CA,...RUn},ref:_h,className:Ca(["react-flow",N,ok]),id:f6,role:"application",children:F.jsxs($Un,{nodes:g,edges:E,width:s0,height:Ih,fitView:b5,fitViewOptions:ek,minZoom:u0,maxZoom:o0,nodeOrigin:kc,nodeExtent:zg,zIndexMode:t3,children:[F.jsx(pqn,{nodes:g,edges:E,defaultNodes:x,defaultEdges:M,onConnect:se,onConnectStart:le,onConnectEnd:ee,onClickConnectStart:Oe,onClickConnectEnd:ge,nodesDraggable:Ws,autoPanOnNodeFocus:Dh,nodesConnectable:ef,nodesFocusable:ch,edgesFocusable:cd,edgesReconnectable:jb,elementsSelectable:Rs,elevateNodesOnSelect:xp,elevateEdgesOnSelect:Ap,minZoom:u0,maxZoom:o0,nodeExtent:zg,onNodesChange:s6,onEdgesChange:Af,snapToGrid:mi,snapGrid:Fi,connectionMode:xe,translateExtent:Bg,connectOnClick:MA,defaultEdgeOptions:l6,fitView:b5,fitViewOptions:ek,onNodesDelete:wt,onEdgesDelete:Tn,onDelete:Qn,onNodeDragStart:ln,onNodeDrag:xn,onNodeDragStop:hn,onSelectionDrag:mn,onSelectionDragStart:Fe,onSelectionDragStop:Ae,onMove:ie,onMoveStart:Z,onMoveEnd:W,noPanClassName:Xg,nodeOrigin:kc,rfId:i3,autoPanOnConnect:Cp,autoPanOnNodeDrag:xl,autoPanSpeed:g5,onError:Tp,connectionRadius:tk,isValidConnection:Kg,selectNodesOnDrag:Eu,nodeDragThreshold:ik,connectionDragThreshold:rk,onBeforeDelete:kn,debug:TA,ariaLabelConfig:uk,zIndexMode:t3}),F.jsx(_Un,{onInit:G,onNodeClick:H,onEdgeClick:U,onNodeMouseEnter:Pe,onNodeMouseMove:ae,onNodeMouseLeave:Me,onNodeContextMenu:Be,onNodeDoubleClick:rn,nodeTypes:P,edgeTypes:k,connectionLineType:un,connectionLineStyle:rt,connectionLineComponent:lt,connectionLineContainerStyle:Bt,selectionKeyCode:Gt,selectionOnDrag:At,selectionMode:st,deleteKeyCode:hi,multiSelectionKeyCode:Mr,panActivationKeyCode:Wr,zoomActivationKeyCode:ur,onlyRenderVisibleElements:fu,defaultViewport:c0,translateExtent:Bg,minZoom:u0,maxZoom:o0,preventScrolling:r6,zoomOnScroll:d1,zoomOnPinch:ud,zoomOnDoubleClick:l5,panOnScroll:Sf,panOnScrollSpeed:b1,panOnScrollMode:xf,panOnDrag:f5,onPaneClick:a5,onPaneMouseEnter:Fg,onPaneMouseMove:Eb,onPaneMouseLeave:Jg,onPaneScroll:_u,onPaneContextMenu:Hg,paneClickDistance:Gg,nodeClickDistance:u6,onSelectionContextMenu:Ze,onSelectionStart:sn,onSelectionEnd:Sn,onReconnect:Wm,onReconnectStart:qg,onReconnectEnd:o6,onEdgeContextMenu:Ug,onEdgeDoubleClick:Zm,onEdgeMouseEnter:d5,onEdgeMouseMove:Mt,onEdgeMouseLeave:Cc,reconnectRadius:dc,defaultMarkerColor:c6,noDragClassName:Zs,noWheelClassName:Ta,noPanClassName:Xg,rfId:i3,disableKeyboardA11y:Mp,nodeExtent:zg,viewport:n3,onViewportChange:w5}),F.jsx(dqn,{onSelectionChange:Y}),h5,F.jsx(sqn,{proOptions:e3,position:nk}),F.jsx(oqn,{rfId:i3,disableKeyboardA11y:Mp})]})})}var zUn=k0n(BUn);const FUn=g=>g.domNode?.querySelector(".react-flow__edgelabel-renderer");function JUn({children:g}){const E=Fu(FUn);return E?eqn.createPortal(g,E):null}function HUn(g){const[E,x]=He.useState(g),M=He.useCallback(N=>x(P=>v0n(N,P)),[]);return[E,x,M]}function GUn(g){const[E,x]=He.useState(g),M=He.useCallback(N=>x(P=>y0n(N,P)),[]);return[E,x,M]}function qUn({dimensions:g,lineWidth:E,variant:x,className:M}){return F.jsx("path",{strokeWidth:E,d:`M${g[0]/2} 0 V${g[1]} M0 ${g[1]/2} H${g[0]}`,className:Ca(["react-flow__background-pattern",x,M])})}function UUn({radius:g,className:E}){return F.jsx("circle",{cx:g,cy:g,r:g,className:Ca(["react-flow__background-pattern","dots",E])})}var Z7;(function(g){g.Lines="lines",g.Dots="dots",g.Cross="cross"})(Z7||(Z7={}));const XUn={[Z7.Dots]:1,[Z7.Lines]:1,[Z7.Cross]:6},KUn=g=>({transform:g.transform,patternId:`pattern-${g.rfId}`});function q0n({id:g,variant:E=Z7.Dots,gap:x=20,size:M,lineWidth:N=1,offset:P=0,color:k,bgColor:H,style:U,className:G,patternClassName:ie}){const Z=He.useRef(null),{transform:W,patternId:se}=Fu(KUn,El),le=M||XUn[E],ee=E===Z7.Dots,Oe=E===Z7.Cross,ge=Array.isArray(x)?x:[x,x],Pe=[ge[0]*W[2]||1,ge[1]*W[2]||1],ae=le*W[2],Me=Array.isArray(P)?P:[P,P],Be=Oe?[ae,ae]:Pe,rn=[Me[0]*W[2]||1+Be[0]/2,Me[1]*W[2]||1+Be[1]/2],ln=`${se}${g||""}`;return F.jsxs("svg",{className:Ca(["react-flow__background",G]),style:{...U,...zue,"--xy-background-color-props":H,"--xy-background-pattern-color-props":k},ref:Z,"data-testid":"rf__background",children:[F.jsx("pattern",{id:ln,x:W[0]%Pe[0],y:W[1]%Pe[1],width:Pe[0],height:Pe[1],patternUnits:"userSpaceOnUse",patternTransform:`translate(-${rn[0]},-${rn[1]})`,children:ee?F.jsx(UUn,{radius:ae/2,className:ie}):F.jsx(qUn,{dimensions:Be,lineWidth:N,variant:E,className:ie})}),F.jsx("rect",{x:"0",y:"0",width:"100%",height:"100%",fill:`url(#${ln})`})]})}q0n.displayName="Background";const VUn=He.memo(q0n);function YUn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",children:F.jsx("path",{d:"M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z"})})}function QUn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 5",children:F.jsx("path",{d:"M0 0h32v4.2H0z"})})}function WUn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 30",children:F.jsx("path",{d:"M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z"})})}function ZUn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32",children:F.jsx("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z"})})}function eXn(){return F.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32",children:F.jsx("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z"})})}function rue({children:g,className:E,...x}){return F.jsx("button",{type:"button",className:Ca(["react-flow__controls-button",E]),...x,children:g})}const nXn=g=>({isInteractive:g.nodesDraggable||g.nodesConnectable||g.elementsSelectable,minZoomReached:g.transform[2]<=g.minZoom,maxZoomReached:g.transform[2]>=g.maxZoom,ariaLabelConfig:g.ariaLabelConfig});function U0n({style:g,showZoom:E=!0,showFitView:x=!0,showInteractive:M=!0,fitViewOptions:N,onZoomIn:P,onZoomOut:k,onFitView:H,onInteractiveChange:U,className:G,children:ie,position:Z="bottom-left",orientation:W="vertical","aria-label":se}){const le=Sl(),{isInteractive:ee,minZoomReached:Oe,maxZoomReached:ge,ariaLabelConfig:Pe}=Fu(nXn,El),{zoomIn:ae,zoomOut:Me,fitView:Be}=Nke(),rn=()=>{ae(),P?.()},ln=()=>{Me(),k?.()},xn=()=>{Be(N),H?.()},hn=()=>{le.setState({nodesDraggable:!ee,nodesConnectable:!ee,elementsSelectable:!ee}),U?.(!ee)},wt=W==="horizontal"?"horizontal":"vertical";return F.jsxs(Bue,{className:Ca(["react-flow__controls",wt,G]),position:Z,style:g,"data-testid":"rf__controls","aria-label":se??Pe["controls.ariaLabel"],children:[E&&F.jsxs(F.Fragment,{children:[F.jsx(rue,{onClick:rn,className:"react-flow__controls-zoomin",title:Pe["controls.zoomIn.ariaLabel"],"aria-label":Pe["controls.zoomIn.ariaLabel"],disabled:ge,children:F.jsx(YUn,{})}),F.jsx(rue,{onClick:ln,className:"react-flow__controls-zoomout",title:Pe["controls.zoomOut.ariaLabel"],"aria-label":Pe["controls.zoomOut.ariaLabel"],disabled:Oe,children:F.jsx(QUn,{})})]}),x&&F.jsx(rue,{className:"react-flow__controls-fitview",onClick:xn,title:Pe["controls.fitView.ariaLabel"],"aria-label":Pe["controls.fitView.ariaLabel"],children:F.jsx(WUn,{})}),M&&F.jsx(rue,{className:"react-flow__controls-interactive",onClick:hn,title:Pe["controls.interactive.ariaLabel"],"aria-label":Pe["controls.interactive.ariaLabel"],children:ee?F.jsx(eXn,{}):F.jsx(ZUn,{})}),ie]})}U0n.displayName="Controls";const tXn=He.memo(U0n);function iXn({id:g,x:E,y:x,width:M,height:N,style:P,color:k,strokeColor:H,strokeWidth:U,className:G,borderRadius:ie,shapeRendering:Z,selected:W,onClick:se}){const{background:le,backgroundColor:ee}=P||{},Oe=k||le||ee;return F.jsx("rect",{className:Ca(["react-flow__minimap-node",{selected:W},G]),x:E,y:x,rx:ie,ry:ie,width:M,height:N,style:{fill:Oe,stroke:H,strokeWidth:U},shapeRendering:Z,onClick:se?ge=>se(ge,g):void 0})}const rXn=He.memo(iXn),cXn=g=>g.nodes.map(E=>E.id),U7e=g=>g instanceof Function?g:()=>g;function uXn({nodeStrokeColor:g,nodeColor:E,nodeClassName:x="",nodeBorderRadius:M=5,nodeStrokeWidth:N,nodeComponent:P=rXn,onClick:k}){const H=Fu(cXn,El),U=U7e(E),G=U7e(g),ie=U7e(x),Z=typeof window>"u"||window.chrome?"crispEdges":"geometricPrecision";return F.jsx(F.Fragment,{children:H.map(W=>F.jsx(sXn,{id:W,nodeColorFunc:U,nodeStrokeColorFunc:G,nodeClassNameFunc:ie,nodeBorderRadius:M,nodeStrokeWidth:N,NodeComponent:P,onClick:k,shapeRendering:Z},W))})}function oXn({id:g,nodeColorFunc:E,nodeStrokeColorFunc:x,nodeClassNameFunc:M,nodeBorderRadius:N,nodeStrokeWidth:P,shapeRendering:k,NodeComponent:H,onClick:U}){const{node:G,x:ie,y:Z,width:W,height:se}=Fu(le=>{const ee=le.nodeLookup.get(g);if(!ee)return{node:void 0,x:0,y:0,width:0,height:0};const Oe=ee.internals.userNode,{x:ge,y:Pe}=ee.internals.positionAbsolute,{width:ae,height:Me}=i6(Oe);return{node:Oe,x:ge,y:Pe,width:ae,height:Me}},El);return!G||G.hidden||!Xdn(G)?null:F.jsx(H,{x:ie,y:Z,width:W,height:se,style:G.style,selected:!!G.selected,className:M(G),color:E(G),borderRadius:N,strokeColor:x(G),strokeWidth:P,shapeRendering:k,onClick:U,id:G.id})}const sXn=He.memo(oXn);var lXn=He.memo(uXn);const fXn=200,aXn=150,hXn=g=>!g.hidden,dXn=g=>{const E={x:-g.transform[0]/g.transform[2],y:-g.transform[1]/g.transform[2],width:g.width/g.transform[2],height:g.height/g.transform[2]};return{viewBB:E,boundingRect:g.nodeLookup.size>0?Udn(tq(g.nodeLookup,{filter:hXn}),E):E,rfId:g.rfId,panZoom:g.panZoom,translateExtent:g.translateExtent,flowWidth:g.width,flowHeight:g.height,ariaLabelConfig:g.ariaLabelConfig}},bXn="react-flow__minimap-desc";function X0n({style:g,className:E,nodeStrokeColor:x,nodeColor:M,nodeClassName:N="",nodeBorderRadius:P=5,nodeStrokeWidth:k,nodeComponent:H,bgColor:U,maskColor:G,maskStrokeColor:ie,maskStrokeWidth:Z,position:W="bottom-right",onClick:se,onNodeClick:le,pannable:ee=!1,zoomable:Oe=!1,ariaLabel:ge,inversePan:Pe,zoomStep:ae=1,offsetScale:Me=5}){const Be=Sl(),rn=He.useRef(null),{boundingRect:ln,viewBB:xn,rfId:hn,panZoom:wt,translateExtent:Tn,flowWidth:Qn,flowHeight:Y,ariaLabelConfig:Fe}=Fu(dXn,El),mn=g?.width??fXn,Ae=g?.height??aXn,Ze=ln.width/mn,sn=ln.height/Ae,Sn=Math.max(Ze,sn),kn=Sn*mn,xe=Sn*Ae,un=Me*Sn,rt=ln.x-(kn-ln.width)/2-un,lt=ln.y-(xe-ln.height)/2-un,Bt=kn+un*2,hi=xe+un*2,Gt=`${bXn}-${hn}`,At=He.useRef(0),st=He.useRef();At.current=Sn,He.useEffect(()=>{if(rn.current&&wt)return st.current=MGn({domNode:rn.current,panZoom:wt,getTransform:()=>Be.getState().transform,getViewScale:()=>At.current}),()=>{st.current?.destroy()}},[wt]),He.useEffect(()=>{st.current?.update({translateExtent:Tn,width:Qn,height:Y,inversePan:Pe,pannable:ee,zoomStep:ae,zoomable:Oe})},[ee,Oe,Pe,ae,Tn,Qn,Y]);const Wr=se?mi=>{const[Fi,fu]=st.current?.pointer(mi)||[0,0];se(mi,{x:Fi,y:fu})}:void 0,Mr=le?He.useCallback((mi,Fi)=>{const fu=Be.getState().nodeLookup.get(Fi).internals.userNode;le(mi,fu)},[]):void 0,ur=ge??Fe["minimap.ariaLabel"];return F.jsx(Bue,{position:W,style:{...g,"--xy-minimap-background-color-props":typeof U=="string"?U:void 0,"--xy-minimap-mask-background-color-props":typeof G=="string"?G:void 0,"--xy-minimap-mask-stroke-color-props":typeof ie=="string"?ie:void 0,"--xy-minimap-mask-stroke-width-props":typeof Z=="number"?Z*Sn:void 0,"--xy-minimap-node-background-color-props":typeof M=="string"?M:void 0,"--xy-minimap-node-stroke-color-props":typeof x=="string"?x:void 0,"--xy-minimap-node-stroke-width-props":typeof k=="number"?k:void 0},className:Ca(["react-flow__minimap",E]),"data-testid":"rf__minimap",children:F.jsxs("svg",{width:mn,height:Ae,viewBox:`${rt} ${lt} ${Bt} ${hi}`,className:"react-flow__minimap-svg",role:"img","aria-labelledby":Gt,ref:rn,onClick:Wr,children:[ur&&F.jsx("title",{id:Gt,children:ur}),F.jsx(lXn,{onClick:Mr,nodeColor:M,nodeStrokeColor:x,nodeBorderRadius:P,nodeClassName:N,nodeStrokeWidth:k,nodeComponent:H}),F.jsx("path",{className:"react-flow__minimap-mask",d:`M${rt-un},${lt-un}h${Bt+un*2}v${hi+un*2}h${-Bt-un*2}z + M${xn.x},${xn.y}h${xn.width}v${xn.height}h${-xn.width}z`,fillRule:"evenodd",pointerEvents:"none"})]})})}X0n.displayName="MiniMap";const gXn=He.memo(X0n),wXn=g=>E=>g?`${Math.max(1/E.transform[2],1)}`:void 0,pXn={[mI.Line]:"right",[mI.Handle]:"bottom-right"};function mXn({nodeId:g,position:E,variant:x=mI.Handle,className:M,style:N=void 0,children:P,color:k,minWidth:H=10,minHeight:U=10,maxWidth:G=Number.MAX_VALUE,maxHeight:ie=Number.MAX_VALUE,keepAspectRatio:Z=!1,resizeDirection:W,autoScale:se=!0,shouldResize:le,onResizeStart:ee,onResize:Oe,onResizeEnd:ge}){const Pe=x0n(),ae=typeof g=="string"?g:Pe,Me=Sl(),Be=He.useRef(null),rn=x===mI.Handle,ln=Fu(He.useCallback(wXn(rn&&se),[rn,se]),El),xn=He.useRef(null),hn=E??pXn[x];He.useEffect(()=>{if(!(!Be.current||!ae))return xn.current||(xn.current=FGn({domNode:Be.current,nodeId:ae,getStoreItems:()=>{const{nodeLookup:Tn,transform:Qn,snapGrid:Y,snapToGrid:Fe,nodeOrigin:mn,domNode:Ae}=Me.getState();return{nodeLookup:Tn,transform:Qn,snapGrid:Y,snapToGrid:Fe,nodeOrigin:mn,paneDomNode:Ae}},onChange:(Tn,Qn)=>{const{triggerNodeChanges:Y,nodeLookup:Fe,parentLookup:mn,nodeOrigin:Ae}=Me.getState(),Ze=[],sn={x:Tn.x,y:Tn.y},Sn=Fe.get(ae);if(Sn&&Sn.expandParent&&Sn.parentId){const kn=Sn.origin??Ae,xe=Tn.width??Sn.measured.width??0,un=Tn.height??Sn.measured.height??0,rt={id:Sn.id,parentId:Sn.parentId,rect:{width:xe,height:un,...Kdn({x:Tn.x??Sn.position.x,y:Tn.y??Sn.position.y},{width:xe,height:un},Sn.parentId,Fe,kn)}},lt=Oke([rt],Fe,mn,Ae);Ze.push(...lt),sn.x=Tn.x?Math.max(kn[0]*xe,Tn.x):void 0,sn.y=Tn.y?Math.max(kn[1]*un,Tn.y):void 0}if(sn.x!==void 0&&sn.y!==void 0){const kn={id:ae,type:"position",position:{...sn}};Ze.push(kn)}if(Tn.width!==void 0&&Tn.height!==void 0){const xe={id:ae,type:"dimensions",resizing:!0,setAttributes:W?W==="horizontal"?"width":"height":!0,dimensions:{width:Tn.width,height:Tn.height}};Ze.push(xe)}for(const kn of Qn){const xe={...kn,type:"position"};Ze.push(xe)}Y(Ze)},onEnd:({width:Tn,height:Qn})=>{const Y={id:ae,type:"dimensions",resizing:!1,dimensions:{width:Tn,height:Qn}};Me.getState().triggerNodeChanges([Y])}})),xn.current.update({controlPosition:hn,boundaries:{minWidth:H,minHeight:U,maxWidth:G,maxHeight:ie},keepAspectRatio:Z,resizeDirection:W,onResizeStart:ee,onResize:Oe,onResizeEnd:ge,shouldResize:le}),()=>{xn.current?.destroy()}},[hn,H,U,G,ie,Z,ee,Oe,ge,le]);const wt=hn.split("-");return F.jsx("div",{className:Ca(["react-flow__resize-control","nodrag",...wt,x,M]),ref:Be,style:{...N,scale:ln,...k&&{[rn?"backgroundColor":"borderColor"]:k}},children:P})}He.memo(mXn);const vXn=g=>g.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),K0n=(...g)=>g.filter((E,x,M)=>!!E&&E.trim()!==""&&M.indexOf(E)===x).join(" ").trim();var yXn={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};const kXn=He.forwardRef(({color:g="currentColor",size:E=24,strokeWidth:x=2,absoluteStrokeWidth:M,className:N="",children:P,iconNode:k,...H},U)=>He.createElement("svg",{ref:U,...yXn,width:E,height:E,stroke:g,strokeWidth:M?Number(x)*24/Number(E):x,className:K0n("lucide",N),...H},[...k.map(([G,ie])=>He.createElement(G,ie)),...Array.isArray(P)?P:[P]]));const Ef=(g,E)=>{const x=He.forwardRef(({className:M,...N},P)=>He.createElement(kXn,{ref:P,iconNode:E,className:K0n(`lucide-${vXn(g)}`,M),...N}));return x.displayName=`${g}`,x};const jXn=Ef("Box",[["path",{d:"M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z",key:"hh9hay"}],["path",{d:"m3.3 7 8.7 5 8.7-5",key:"g66t2b"}],["path",{d:"M12 22V12",key:"d0xqtd"}]]);const EXn=Ef("Boxes",[["path",{d:"M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z",key:"lc1i9w"}],["path",{d:"m7 16.5-4.74-2.85",key:"1o9zyk"}],["path",{d:"m7 16.5 5-3",key:"va8pkn"}],["path",{d:"M7 16.5v5.17",key:"jnp8gn"}],["path",{d:"M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z",key:"8zsnat"}],["path",{d:"m17 16.5-5-3",key:"8arw3v"}],["path",{d:"m17 16.5 4.74-2.85",key:"8rfmw"}],["path",{d:"M17 16.5v5.17",key:"k6z78m"}],["path",{d:"M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z",key:"1xygjf"}],["path",{d:"M12 8 7.26 5.15",key:"1vbdud"}],["path",{d:"m12 8 4.74-2.85",key:"3rx089"}],["path",{d:"M12 13.5V8",key:"1io7kd"}]]);const YG=Ef("Check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]]);const SXn=Ef("CircleAlert",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["line",{x1:"12",x2:"12",y1:"8",y2:"12",key:"1pkeuh"}],["line",{x1:"12",x2:"12.01",y1:"16",y2:"16",key:"4dfq90"}]]);const xXn=Ef("Clock3",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["polyline",{points:"12 6 12 12 16.5 12",key:"1aq6pp"}]]);const AXn=Ef("CodeXml",[["path",{d:"m18 16 4-4-4-4",key:"1inbqp"}],["path",{d:"m6 8-4 4 4 4",key:"15zrgr"}],["path",{d:"m14.5 4-5 16",key:"e7oirm"}]]);const V0n=Ef("Eye",[["path",{d:"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0",key:"1nclc0"}],["circle",{cx:"12",cy:"12",r:"3",key:"1v7zrd"}]]);const MXn=Ef("FolderOpen",[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2",key:"usdka0"}]]);const CXn=Ef("GitBranch",[["line",{x1:"6",x2:"6",y1:"3",y2:"15",key:"17qcm7"}],["circle",{cx:"18",cy:"6",r:"3",key:"1h7g24"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}],["path",{d:"M18 9a9 9 0 0 1-9 9",key:"n2h4wq"}]]);const Y0n=Ef("Layers",[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",key:"zw3jo"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",key:"1wduqc"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",key:"kqbvx6"}]]);const Q1n=Ef("Link2",[["path",{d:"M9 17H7A5 5 0 0 1 7 7h2",key:"8i5ue5"}],["path",{d:"M15 7h2a5 5 0 1 1 0 10h-2",key:"1b9ql8"}],["line",{x1:"8",x2:"16",y1:"12",y2:"12",key:"1jonct"}]]);const TXn=Ef("Network",[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]]);const QG=Ef("Plus",[["path",{d:"M5 12h14",key:"1ays0h"}],["path",{d:"M12 5v14",key:"s699le"}]]);const OXn=Ef("Redo2",[["path",{d:"m15 14 5-5-5-5",key:"12vg1m"}],["path",{d:"M20 9H9.5A5.5 5.5 0 0 0 4 14.5A5.5 5.5 0 0 0 9.5 20H13",key:"6uklza"}]]);const NXn=Ef("RotateCcw",[["path",{d:"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8",key:"1357e3"}],["path",{d:"M3 3v5h5",key:"1xhq8a"}]]);const DXn=Ef("Save",[["path",{d:"M15.2 3a2 2 0 0 1 1.4.6l3.8 3.8a2 2 0 0 1 .6 1.4V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2z",key:"1c8476"}],["path",{d:"M17 21v-7a1 1 0 0 0-1-1H8a1 1 0 0 0-1 1v7",key:"1ydtos"}],["path",{d:"M7 3v4a1 1 0 0 0 1 1h7",key:"t51u73"}]]);const Q0n=Ef("Scissors",[["circle",{cx:"6",cy:"6",r:"3",key:"1lh9wr"}],["path",{d:"M8.12 8.12 12 12",key:"1alkpv"}],["path",{d:"M20 4 8.12 15.88",key:"xgtan2"}],["circle",{cx:"6",cy:"18",r:"3",key:"fqmcym"}],["path",{d:"M14.8 14.8 20 20",key:"ptml3r"}]]);const IXn=Ef("Search",[["circle",{cx:"11",cy:"11",r:"8",key:"4ej97u"}],["path",{d:"m21 21-4.3-4.3",key:"1qie3q"}]]);const wue=Ef("Trash2",[["path",{d:"M3 6h18",key:"d0wm0j"}],["path",{d:"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6",key:"4alrt4"}],["path",{d:"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2",key:"v07s0e"}],["line",{x1:"10",x2:"10",y1:"11",y2:"17",key:"1uufr5"}],["line",{x1:"14",x2:"14",y1:"11",y2:"17",key:"xtxkd"}]]);const dke=Ef("TriangleAlert",[["path",{d:"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3",key:"wmoenq"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);const _Xn=Ef("Undo2",[["path",{d:"M9 14 4 9l5-5",key:"102s5s"}],["path",{d:"M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11",key:"f3b9sd"}]]);const Ym=Ef("X",[["path",{d:"M18 6 6 18",key:"1bl5f8"}],["path",{d:"m6 6 12 12",key:"d8bk6v"}]]);function LXn({mode:g,models:E,objects:x,application:M,initialModelType:N,suggestedSelector:P,nameReadOnly:k=!1,preview:H,onPreview:U,onSubmit:G,onClose:ie}){const Z=M?.modelType||N||E[0]?.type||"",[W,se]=He.useState(Z),le=E.find(st=>st.type===W)??null,[ee,Oe]=He.useState(M?.name||M?.applicationId||W1n(le)),[ge,Pe]=He.useState(()=>Cue(le,M)),ae=M?.selector||P||$Xn(x),[Me,Be]=He.useState(ae.multiplicity),[rn,ln]=He.useState(cue(ae,"scale")),[xn,hn]=He.useState(cue(ae,"kind")),[wt,Tn]=He.useState(cue(ae,"species")),[Qn,Y]=He.useState(cue(ae,"name")),Fe=RXn(ae,"within"),[mn,Ae]=He.useState(Fe?.type==="Scope"?"named_scope":"scene"),[Ze,sn]=He.useState(Fe?.type==="Scope"?String(Fe.name||""):""),[Sn,kn]=He.useState(M?.timestep?"clock":"default"),[xe,un]=He.useState("1.0"),[rt,lt]=He.useState("0.0");He.useEffect(()=>{const st=E.find(Wr=>Wr.type===W)??null;Pe(Cue(st,g==="update"?M:void 0)),g==="add"&&Oe(W1n(st))},[M,g,W,E]);const Bt=He.useMemo(()=>({scales:$G(x.map(st=>st.scale)),kinds:$G(x.map(st=>st.kind)),species:$G(x.map(st=>st.species)),names:$G(x.map(st=>st.name))}),[x]),hi=He.useMemo(()=>{const st=[rn&&`scale ${rn}`,xn&&`kind ${xn}`,wt&&`species ${wt}`,Qn&&`name ${Qn}`].filter(Boolean);return st.length?st.join(", "):"all scene objects"},[xn,Qn,rn,wt]),Gt=()=>{const st={selectors:[]};return st.within=mn==="named_scope"&&Ze?{type:"Scope",name:Ze}:{type:"SceneScope"},rn&&(st.scale=rn),xn&&(st.kind=xn),wt&&(st.species=wt),Qn&&(st.name=Qn),{type:BXn(Me),multiplicity:Me,criteria:st,julia:""}},At=()=>{G({applicationId:M?.applicationId,modelType:W,name:ee.trim(),parameters:ge,selector:Gt(),timestep:Sn==="clock"?{mode:"clock",dt:xe,phase:rt}:{mode:"default"}})};return F.jsx("div",{className:"overlay-backdrop",onMouseDown:ie,children:F.jsxs("section",{className:"overlay-panel application-form",onMouseDown:st=>st.stopPropagation(),"data-testid":"application-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:g==="add"?"Add application":`Update ${M?.applicationId}`}),F.jsx("span",{children:"A configured use of a model on selected scene objects"})]}),F.jsx("button",{onClick:ie,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content application-form-content",children:[F.jsxs("label",{children:["Model",F.jsx("select",{value:W,onChange:st=>se(st.target.value),"data-testid":"application-model-select",children:E.map(st=>F.jsxs("option",{value:st.type,children:[st.package?`${st.package} · `:"",st.name," (",st.process,")"]},st.type))})]}),F.jsxs("label",{children:["Application name",F.jsx("input",{value:ee,disabled:k,onChange:st=>Oe(st.target.value),"data-testid":"application-name"})]}),le&&le.constructor.fields.length>0&&F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Model parameters"}),F.jsx(W0n,{fields:le.constructor.fields,values:ge,onChange:Pe})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Target selector"}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Multiplicity",F.jsxs("select",{value:Me,onChange:st=>Be(st.target.value),children:[F.jsx("option",{value:"one",children:"One"}),F.jsx("option",{value:"optional_one",children:"Optional one"}),F.jsx("option",{value:"many",children:"Many"})]})]}),F.jsxs("label",{children:["Scope",F.jsxs("select",{value:mn,onChange:st=>Ae(st.target.value),children:[F.jsx("option",{value:"scene",children:"Whole scene"}),F.jsx("option",{value:"named_scope",children:"Named object subtree"})]})]}),mn==="named_scope"&&F.jsx(_G,{label:"Scope root",value:Ze,options:Bt.names,onChange:sn}),F.jsx(_G,{label:"Scale",value:rn,options:Bt.scales,onChange:ln}),F.jsx(_G,{label:"Kind",value:xn,options:Bt.kinds,onChange:hn}),F.jsx(_G,{label:"Species",value:wt,options:Bt.species,onChange:Tn}),F.jsx(_G,{label:"Object name",value:Qn,options:Bt.names,onChange:Y})]}),F.jsxs("p",{className:"selector-summary",children:["Julia will resolve ",F.jsx("strong",{children:Me.replace("_"," ")})," target from ",hi,"."]}),F.jsxs("button",{className:"selector-preview-button",type:"button",onClick:()=>U(Gt()),"data-testid":"application-target-preview",children:[F.jsx(V0n,{size:15})," Preview targets in Julia"]}),H&&F.jsxs("section",{className:"selector-preview","data-testid":"application-target-preview-result",children:[F.jsxs("strong",{children:[H.count," target object",H.count===1?"":"s"]}),F.jsx("code",{children:H.objectIds.map(String).join(", ")||"No targets"})]})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Timestep"}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Mode",F.jsxs("select",{value:Sn,onChange:st=>kn(st.target.value),children:[F.jsx("option",{value:"default",children:"Model or environment default"}),F.jsx("option",{value:"clock",children:"Explicit clock"})]})]}),Sn==="clock"&&F.jsxs(F.Fragment,{children:[F.jsxs("label",{children:["Step",F.jsx("input",{value:xe,onChange:st=>un(st.target.value)})]}),F.jsxs("label",{children:["Phase",F.jsx("input",{value:rt,onChange:st=>lt(st.target.value)})]})]})]})]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:ie,children:"Cancel"}),F.jsxs("button",{className:"primary",disabled:!W||!ee.trim(),onClick:At,"data-testid":"application-submit",children:[F.jsx(YG,{size:15})," ",g==="add"?"Add application":"Apply changes"]})]})]})})}function W0n({fields:g,values:E,onChange:x}){const M=new Map;for(const P of g)P.typeParameter&&!M.has(P.typeParameter)&&M.set(P.typeParameter,P.name);const N=(P,k)=>{const H=P.typeParameter?g.filter(U=>U.typeParameter===P.typeParameter).map(U=>U.name):[P.name];x(Object.fromEntries(Object.entries(E).map(([U,G])=>[U,H.includes(U)?{...G,type:k}:G])))};return F.jsx("div",{className:"parameter-list",children:g.map(P=>{const k=E[P.name]||{type:P.inferredChoice,value:""},H=!P.typeParameter||M.get(P.typeParameter)===P.name;return F.jsxs("div",{className:"parameter-row",children:[F.jsxs("label",{children:[F.jsx("span",{children:P.name}),F.jsx("small",{children:P.declaredType}),F.jsx("input",{"data-testid":`application-param-${P.name}`,value:k.value,onChange:U=>x({...E,[P.name]:{...k,value:U.target.value}})})]}),H&&F.jsxs("label",{className:"parameter-type",children:[F.jsx("span",{children:P.typeParameter?`${P.typeParameter} type`:"Value type"}),F.jsx("select",{"data-testid":`application-param-type-${P.name}`,value:k.type,onChange:U=>N(P,U.target.value),children:P.choices.map(U=>F.jsx("option",{value:U,children:U},U))})]})]},P.name)})})}function _G({label:g,value:E,options:x,onChange:M}){return F.jsxs("label",{children:[g,F.jsxs("select",{value:E,onChange:N=>M(N.target.value),children:[F.jsx("option",{value:"",children:"Any"}),x.map(N=>F.jsx("option",{value:N,children:N},N))]})]})}function Cue(g,E){return g?Object.fromEntries(g.constructor.fields.map(x=>{const M=E?.modelParameters[x.name],N=M?.type||x.inferredChoice,P=M?M.julia:x.hasDefault?N==="julia"?x.defaultJulia||"":PXn(x.default,N):"";return[x.name,{type:N,value:P}]})):{}}function PXn(g,E){const x=g==null?"":String(g);return E==="symbol"?x.replace(/^:/,""):x}function W1n(g){return g?.process||g?.name||"application"}function $Xn(g){const E=$G(g.map(x=>x.scale))[0];return{type:"Many",multiplicity:"many",criteria:E?{selectors:[],scale:E}:{selectors:[]},julia:""}}function cue(g,E){const x=g.criteria[E];return typeof x=="string"?x:""}function RXn(g,E){const x=g.criteria[E];return x&&typeof x=="object"?x:null}function BXn(g){return g==="one"?"One":g==="optional_one"?"OptionalOne":"Many"}function $G(g){return[...new Set(g.filter(E=>!!E))].sort()}function zXn({application:g,applications:E,onCommand:x,onClose:M}){const N=E.filter(Be=>Be.applicationId!==g.applicationId),[P,k]=He.useState(""),[H,U]=He.useState(N[0]?.applicationId||""),[G,ie]=He.useState(String(g.environment?.provider||"scene")),[Z,W]=He.useState(g.updates||[]),[se,le]=He.useState(g.outputs[0]?.name||""),[ee,Oe]=He.useState(N[0]?.applicationId||""),ge=N.find(Be=>Be.applicationId===H),Pe=He.useMemo(()=>({type:ge?.targetCount===1?"One":"Many",multiplicity:ge?.targetCount===1?"one":"many",criteria:{selectors:[],within:{type:"SceneScope"},application:H},julia:""}),[H,ge?.targetCount]),ae=()=>{!P.trim()||!H||(x({action:"edit",kind:"set_call_binding",applicationId:g.applicationId,call:P.trim(),selector:Pe}),k(""))},Me=()=>{!se||!ee||W(Be=>[...Be.filter(rn=>!rn.variables.includes(se)),{variables:[se],after:[ee]}])};return F.jsx("div",{className:"overlay-backdrop",onMouseDown:M,children:F.jsxs("section",{className:"overlay-panel application-configuration-form",onMouseDown:Be=>Be.stopPropagation(),"data-testid":"application-configuration-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsxs("strong",{children:["Configure ",g.applicationId]}),F.jsx("span",{children:"Authored coupling and execution policy, validated by Julia"})]}),F.jsx("button",{onClick:M,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content application-configuration-content",children:[F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Explicit input bindings"}),Object.entries(g.inputBindings).length===0&&F.jsx("p",{children:"No authored input bindings. Unique same-object producers may still be inferred."}),F.jsx("div",{className:"configuration-list",children:Object.entries(g.inputBindings).map(([Be,rn])=>F.jsxs("div",{children:[F.jsx("code",{children:Be}),F.jsx("span",{children:rn.julia||rn.type}),F.jsx("button",{className:"danger icon-button",title:`Remove ${Be} binding`,onClick:()=>x({action:"edit",kind:"remove_input_binding",applicationId:g.applicationId,input:Be}),children:F.jsx(wue,{size:14})})]},Be))})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Manual calls"}),F.jsx("div",{className:"configuration-list",children:Object.entries(g.callBindings).map(([Be,rn])=>F.jsxs("div",{children:[F.jsx("code",{children:Be}),F.jsx("span",{children:rn.julia||rn.type}),F.jsx("button",{className:"danger icon-button",title:`Remove ${Be} call`,onClick:()=>x({action:"edit",kind:"remove_call_binding",applicationId:g.applicationId,call:Be}),children:F.jsx(wue,{size:14})})]},Be))}),F.jsxs("div",{className:"form-grid compact-configuration-row",children:[F.jsxs("label",{children:["Call name",F.jsx("input",{"data-testid":"call-name",value:P,onChange:Be=>k(Be.target.value),placeholder:"child"})]}),F.jsxs("label",{children:["Target application",F.jsxs("select",{"data-testid":"call-target",value:H,onChange:Be=>U(Be.target.value),children:[F.jsx("option",{value:"",children:"Choose application"}),N.map(Be=>F.jsx("option",{value:Be.applicationId,children:Be.applicationId},Be.applicationId))]})]}),F.jsxs("button",{type:"button","data-testid":"add-call-binding",disabled:!P.trim()||!H,onClick:ae,children:[F.jsx(QG,{size:14})," Add call"]})]})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Environment"}),F.jsxs("div",{className:"form-grid compact-configuration-row",children:[F.jsxs("label",{children:["Provider",F.jsx("input",{"data-testid":"environment-provider",value:G,onChange:Be=>ie(Be.target.value),placeholder:"scene"})]}),F.jsxs("button",{type:"button","data-testid":"apply-environment-provider",disabled:!G.trim(),onClick:()=>x({action:"edit",kind:"set_environment_provider",applicationId:g.applicationId,provider:G.trim()}),children:[F.jsx(YG,{size:14})," Apply provider"]})]}),Object.keys(g.meteoBindings||{}).length>0&&F.jsx("code",{children:JSON.stringify(g.meteoBindings)})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Output routing"}),F.jsx("div",{className:"configuration-list",children:g.outputs.map(Be=>F.jsxs("label",{children:[F.jsx("code",{children:Be.name}),F.jsxs("select",{"data-testid":`output-routing-${Be.name}`,value:g.outputRouting[Be.name]||"canonical",onChange:rn=>x({action:"edit",kind:"set_output_routing",applicationId:g.applicationId,output:Be.name,route:rn.target.value}),children:[F.jsx("option",{value:"canonical",children:"Canonical status owner"}),F.jsx("option",{value:"stream_only",children:"Stream only"})]})]},Be.name))})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Duplicate-writer ordering"}),F.jsx("div",{className:"configuration-list",children:Z.map((Be,rn)=>F.jsxs("div",{children:[F.jsx("code",{children:Be.variables.join(", ")}),F.jsxs("span",{children:["after ",Be.after.join(", ")]}),F.jsx("button",{className:"danger icon-button",title:"Remove update ordering",onClick:()=>W(ln=>ln.filter((xn,hn)=>hn!==rn)),children:F.jsx(wue,{size:14})})]},`${Be.variables.join(",")}:${Be.after.join(",")}`))}),F.jsxs("div",{className:"form-grid compact-configuration-row",children:[F.jsxs("label",{children:["Output",F.jsx("select",{value:se,onChange:Be=>le(Be.target.value),children:g.outputs.map(Be=>F.jsx("option",{value:Be.name,children:Be.name},Be.name))})]}),F.jsxs("label",{children:["Run after",F.jsxs("select",{value:ee,onChange:Be=>Oe(Be.target.value),children:[F.jsx("option",{value:"",children:"Choose application"}),N.map(Be=>F.jsx("option",{value:Be.applicationId,children:Be.applicationId},Be.applicationId))]})]}),F.jsxs("button",{type:"button",disabled:!se||!ee,onClick:Me,children:[F.jsx(QG,{size:14})," Add rule"]}),F.jsxs("button",{type:"button",onClick:()=>x({action:"edit",kind:"set_update_ordering",applicationId:g.applicationId,updates:Z}),children:[F.jsx(YG,{size:14})," Apply ordering"]})]})]})]}),F.jsx("footer",{children:F.jsx("button",{className:"primary",onClick:M,children:"Done"})})]})})}function FXn({endpoints:g,objects:E,preview:x,onPreview:M,onSubmit:N,onClose:P}){const k=JXn(g.sourceApplication.targetIds,g.targetApplication.targetIds),[H,U]=He.useState(g.sourceApplication.targetCount>1&&g.targetApplication.targetCount===1?"many":"one"),[G,ie]=He.useState(k?"self":""),[Z,W]=He.useState("scene"),[se,le]=He.useState(""),[ee,Oe]=He.useState(""),[ge,Pe]=He.useState(X7e(g.sourceApplication.targetScales)),[ae,Me]=He.useState(X7e(g.sourceApplication.targetKinds)),[Be,rn]=He.useState(X7e(g.sourceApplication.targetSpecies)),[ln,xn]=He.useState(""),[hn,wt]=He.useState("application"),[Tn,Qn]=He.useState("automatic"),[Y,Fe]=He.useState(""),mn=uue(E.map(kn=>kn.scale)),Ae=uue(E.map(kn=>kn.kind)),Ze=uue(E.map(kn=>kn.species)),sn=uue(E.map(kn=>kn.name)),Sn=()=>{const kn={selectors:[],var:g.sourcePort.name};kn[hn]=hn==="application"?g.sourceApplication.applicationId:g.sourceApplication.process;const xe=qXn(Z,se,ee);if(xe&&(kn.within=xe),G&&(kn.relation=G),ge&&(kn.scale=ge),ae&&(kn.kind=ae),Be&&(kn.species=Be),ln&&(kn.name=ln),Tn!=="automatic"&&(kn.policy={type:GXn(Tn)}),Y.trim()){const un=Number(Y);Number.isFinite(un)&&(kn.window=un)}return{applicationId:g.targetApplication.applicationId,input:g.targetPort.name,selector:{type:HXn(H),multiplicity:H,criteria:kn,julia:""}}};return F.jsx("div",{className:"overlay-backdrop",onMouseDown:P,children:F.jsxs("section",{className:"overlay-panel binding-form",onMouseDown:kn=>kn.stopPropagation(),"data-testid":"binding-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:"Connect applications"}),F.jsx("span",{children:"Julia resolves this declaration into concrete object bindings"})]}),F.jsx("button",{onClick:P,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content",children:[F.jsxs("div",{className:"binding-route",children:[F.jsxs("div",{children:[F.jsx("small",{children:"Producer"}),F.jsx("strong",{children:g.sourceApplication.applicationId}),F.jsx("code",{children:g.sourcePort.name})]}),F.jsx(Q1n,{size:22}),F.jsxs("div",{children:[F.jsx("small",{children:"Consumer"}),F.jsx("strong",{children:g.targetApplication.applicationId}),F.jsx("code",{children:g.targetPort.name})]})]}),F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Source object selector"}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Multiplicity",F.jsxs("select",{value:H,onChange:kn=>U(kn.target.value),children:[F.jsx("option",{value:"one",children:"One"}),F.jsx("option",{value:"optional_one",children:"Optional one"}),F.jsx("option",{value:"many",children:"Many"})]})]}),F.jsxs("label",{children:["Scope",F.jsxs("select",{value:Z,onChange:kn=>W(kn.target.value),children:[F.jsx("option",{value:"scene",children:"Whole scene"}),F.jsx("option",{value:"self",children:"Consumer object"}),F.jsx("option",{value:"subtree",children:"Consumer subtree"}),F.jsx("option",{value:"self_plant",children:"Consumer plant"}),F.jsx("option",{value:"ancestor",children:"Ancestor subtree"}),F.jsx("option",{value:"named_scope",children:"Named object subtree"})]})]}),Z==="ancestor"&&F.jsx(oI,{label:"Ancestor scale",value:ee,options:mn,onChange:Oe}),Z==="named_scope"&&F.jsx(oI,{label:"Scope root",value:se,options:sn,onChange:le}),F.jsxs("label",{children:["Relation",F.jsxs("select",{value:G,onChange:kn=>ie(kn.target.value),children:[F.jsx("option",{value:"",children:"Any relation"}),F.jsx("option",{value:"self",children:"Same object"}),F.jsx("option",{value:"parent",children:"Parent"}),F.jsx("option",{value:"children",children:"Children"}),F.jsx("option",{value:"ancestors",children:"Ancestors"}),F.jsx("option",{value:"descendants",children:"Descendants"}),F.jsx("option",{value:"siblings",children:"Siblings"})]})]}),F.jsxs("label",{children:["Producer filter",F.jsxs("select",{value:hn,onChange:kn=>wt(kn.target.value),children:[F.jsx("option",{value:"application",children:"This application"}),F.jsx("option",{value:"process",children:"Any application of this process"})]})]}),F.jsx(oI,{label:"Scale",value:ge,options:mn,onChange:Pe}),F.jsx(oI,{label:"Kind",value:ae,options:Ae,onChange:Me}),F.jsx(oI,{label:"Species",value:Be,options:Ze,onChange:rn}),F.jsx(oI,{label:"Object name",value:ln,options:sn,onChange:xn}),F.jsxs("label",{children:["Temporal policy",F.jsxs("select",{value:Tn,onChange:kn=>Qn(kn.target.value),children:[F.jsx("option",{value:"automatic",children:"Automatic"}),F.jsx("option",{value:"hold_last",children:"Hold last"}),F.jsx("option",{value:"interpolate",children:"Interpolate"}),F.jsx("option",{value:"integrate",children:"Integrate"}),F.jsx("option",{value:"aggregate",children:"Aggregate"})]})]}),F.jsxs("label",{children:["Window (scene steps)",F.jsx("input",{type:"number",min:"0",step:"1",value:Y,onChange:kn=>Fe(kn.target.value),placeholder:"Automatic"})]})]})]}),x&&F.jsxs("section",{className:"selector-preview","data-testid":"binding-preview",children:[F.jsxs("strong",{children:[x.bindingCount," resolved binding",x.bindingCount===1?"":"s"]}),F.jsxs("span",{children:[x.consumerObjectIds.length," consumer object",x.consumerObjectIds.length===1?"":"s"," from ",x.sourceObjectIds.length," source object",x.sourceObjectIds.length===1?"":"s"]}),x.sourceApplicationIds.length>0&&F.jsx("code",{children:x.sourceApplicationIds.join(", ")}),x.diagnostics.map(kn=>F.jsx("p",{children:kn},kn))]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:P,children:"Cancel"}),F.jsxs("button",{onClick:()=>M(Sn()),"data-testid":"binding-preview-button",children:[F.jsx(V0n,{size:15})," Preview resolution"]}),F.jsxs("button",{className:"primary",onClick:()=>N(Sn()),"data-testid":"binding-submit",children:[F.jsx(Q1n,{size:15})," Apply binding"]})]})]})})}function oI({label:g,value:E,options:x,onChange:M}){return F.jsxs("label",{children:[g,F.jsxs("select",{value:E,onChange:N=>M(N.target.value),children:[F.jsx("option",{value:"",children:"Any"}),x.map(N=>F.jsx("option",{value:N,children:N},N))]})]})}function JXn(g,E){return g.length===E.length&&g.every(x=>E.some(M=>String(M)===String(x)))}function X7e(g){return g.length===1?g[0]:""}function HXn(g){return g==="one"?"One":g==="optional_one"?"OptionalOne":"Many"}function uue(g){return[...new Set(g.filter(E=>!!E))].sort()}function GXn(g){return g==="hold_last"?"HoldLast":g==="interpolate"?"Interpolate":g==="integrate"?"Integrate":"Aggregate"}function qXn(g,E,x){return g==="scene"?{type:"SceneScope"}:g==="self"?{type:"Self"}:g==="subtree"?{type:"Subtree"}:g==="self_plant"?{type:"SelfPlant"}:g==="ancestor"?{type:"Ancestor",scale:x||null}:g==="named_scope"&&E?{type:"Scope",name:E}:null}function UXn({mode:g,objects:E,object:x,onSubmit:M,onClose:N}){const[P,k]=He.useState(String(x?.objectId??"")),[H,U]=He.useState(XXn(x?.parent)),[G,ie]=He.useState(x?.scale||""),[Z,W]=He.useState(x?.kind||""),[se,le]=He.useState(x?.species||""),[ee,Oe]=He.useState(x?.name||""),ge=He.useMemo(()=>E.filter(ae=>String(ae.objectId)!==P),[P,E]),Pe=()=>M({objectId:P.trim(),configuration:{parent:H||null,scale:G.trim()||null,kind:Z.trim()||null,species:se.trim()||null,name:ee.trim()||null}});return F.jsx("div",{className:"overlay-backdrop",onMouseDown:N,children:F.jsxs("section",{className:"overlay-panel object-form",onMouseDown:ae=>ae.stopPropagation(),"data-testid":"object-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:g==="add"?"Add scene object":`Update object ${String(x?.objectId)}`}),F.jsx("span",{children:"Objects define the concrete entities and topology targeted by applications"})]}),F.jsx("button",{onClick:N,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content object-form-content",children:[F.jsxs("label",{children:["Stable object ID",F.jsx("input",{value:P,disabled:g==="update",onChange:ae=>k(ae.target.value),"data-testid":"object-id"})]}),F.jsxs("label",{children:["Parent object",F.jsxs("select",{value:H,onChange:ae=>U(ae.target.value),children:[F.jsx("option",{value:"",children:"No parent"}),ge.map(ae=>F.jsxs("option",{value:String(ae.objectId),children:[ae.name||String(ae.objectId)," · ",ae.scale||"unscaled"]},ae.id))]})]}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Scale",F.jsx("input",{value:G,onChange:ae=>ie(ae.target.value)})]}),F.jsxs("label",{children:["Kind",F.jsx("input",{value:Z,onChange:ae=>W(ae.target.value)})]}),F.jsxs("label",{children:["Species",F.jsx("input",{value:se,onChange:ae=>le(ae.target.value)})]}),F.jsxs("label",{children:["Name",F.jsx("input",{value:ee,onChange:ae=>Oe(ae.target.value)})]})]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:N,children:"Cancel"}),F.jsxs("button",{className:"primary",disabled:!P.trim(),onClick:Pe,"data-testid":"object-submit",children:[F.jsx(YG,{size:15})," ",g==="add"?"Add object":"Apply changes"]})]})]})})}function XXn(g){if(g==null||g==="")return"";const E=String(g);return E.startsWith("object:")?E.slice(7):E}function KXn({application:g,models:E,instances:x,onSubmit:M,onRemove:N,onClose:P}){const k=He.useMemo(()=>E.filter(hn=>hn.process===g.process),[g.process,E]),[H,U]=He.useState("instance"),[G,ie]=He.useState(g.targetInstances[0]||x[0]?.name||""),Z=x.find(hn=>hn.name===G),W=(Z?.objectIds||[]).filter(hn=>g.targetIds.some(wt=>String(wt)===String(hn))),[se,le]=He.useState(W[0]??""),[ee,Oe]=He.useState(g.modelType),ge=k.find(hn=>hn.type===ee)||k[0]||null,[Pe,ae]=He.useState(()=>Cue(ge,g)),Me=VXn(g.applicationId,G),Be=!!Z?.instanceOverrides.includes(Me),rn=!!Z?.objectOverrides.some(hn=>{const wt=hn;return String(wt.object??wt.objectId??"")===String(se)&&String(wt.application??wt.applicationId??"")===Me}),ln=H==="instance"?Be:rn,xn=hn=>{Oe(hn),ae(Cue(k.find(wt=>wt.type===hn)||null))};return F.jsx("div",{className:"overlay-backdrop",onMouseDown:P,children:F.jsxs("section",{className:"overlay-panel override-form",onMouseDown:hn=>hn.stopPropagation(),"data-testid":"override-form",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:"Create a model override"}),F.jsx("span",{children:"The shared template remains unchanged outside the selected scope"})]}),F.jsx("button",{onClick:P,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content override-form-content",children:[F.jsxs("div",{className:"override-scope-choice",children:[F.jsxs("button",{className:H==="instance"?"active":"",onClick:()=>U("instance"),children:[F.jsx("strong",{children:"One instance"}),F.jsx("span",{children:"All targets of this application in one plant or object instance"})]}),F.jsxs("button",{className:H==="object"?"active":"",onClick:()=>U("object"),children:[F.jsx("strong",{children:"One object"}),F.jsx("span",{children:"Only one concrete execution receives the replacement model"})]})]}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Instance",F.jsx("select",{value:G,onChange:hn=>{const wt=hn.target.value,Qn=(x.find(Y=>Y.name===wt)?.objectIds||[]).find(Y=>g.targetIds.some(Fe=>String(Fe)===String(Y)));ie(wt),le(Qn??"")},children:x.filter(hn=>g.targetInstances.includes(hn.name)).map(hn=>F.jsx("option",{value:hn.name,children:hn.name},hn.name))})]}),H==="object"&&F.jsxs("label",{children:["Object",F.jsx("select",{value:String(se),onChange:hn=>le(hn.target.value),children:W.map(hn=>F.jsx("option",{value:String(hn),children:String(hn)},String(hn)))})]}),F.jsxs("label",{children:["Replacement model",F.jsx("select",{value:ee,onChange:hn=>xn(hn.target.value),children:k.map(hn=>F.jsxs("option",{value:hn.type,children:[hn.package?`${hn.package} · `:"",hn.name]},hn.type))})]})]}),ge&&ge.constructor.fields.length>0&&F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Model parameters"}),F.jsx(W0n,{fields:ge.constructor.fields,values:Pe,onChange:ae})]}),F.jsxs("div",{className:"override-warning",children:[F.jsx("strong",{children:H==="instance"?`Override ${G}`:`Override object ${String(se)}`}),F.jsx("span",{children:"Julia validates that the replacement keeps the same process and declared variable contract."})]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:P,children:"Cancel"}),ln&&F.jsxs("button",{className:"danger","data-testid":"remove-override",onClick:()=>N({scope:H,instance:G,objectId:H==="object"?se:void 0,applicationId:g.applicationId,modelType:ee,parameters:Pe}),children:[F.jsx(wue,{size:15})," Remove override"]}),F.jsxs("button",{className:"primary",disabled:!G||!ee||H==="object"&&!String(se),onClick:()=>M({scope:H,instance:G,objectId:H==="object"?se:void 0,applicationId:g.applicationId,modelType:ee,parameters:Pe}),children:[F.jsx(YG,{size:15})," Apply override"]})]})]})})}function VXn(g,E){const x=`${E}__`;return g.startsWith(x)?g.slice(x.length):g}function Z0n(g){if(g.detailMode==="overview")return 190;const E=Math.max(g.applicationId.length,g.modelName.length,...g.inputs.map(x=>x.name.length),...g.outputs.map(x=>x.name.length));return Math.max(310,Math.min(540,235+E*7))}function YXn({data:g,selected:E}){const x=g.detailMode==="overview",M=new Set(g.requiredInputPortIds),N=new Set(g.candidatePortIds),P=new Set(g.previousTimeStepPortIds),k=new Set(g.cycleBreakInputPortIds),H=ZXn(g);return F.jsxs("section",{className:`model-node application-node ${x?"overview-node":""} ${g.cyclic?"cyclic":""} ${E?"selected":""}`,"data-testid":`application-node-${g.applicationId}`,style:{width:Z0n(g)},children:[x&&F.jsx(WXn,{inputs:g.inputs,outputs:g.outputs}),F.jsxs("header",{className:"node-header",children:[F.jsxs("div",{children:[F.jsx("div",{className:"process",children:g.name||g.applicationId}),F.jsx("div",{className:"model-type",children:g.modelName})]}),F.jsx(Y0n,{size:18})]}),x?F.jsxs("div",{className:"overview-node-summary",children:[F.jsxs("span",{children:[g.targetCount," targets"]}),F.jsxs("span",{children:[g.inputs.length," in"]}),F.jsxs("span",{children:[g.outputs.length," out"]})]}):F.jsxs(F.Fragment,{children:[F.jsxs("div",{className:"node-meta",children:[F.jsxs("span",{className:"meta-chip",title:g.selector.julia,children:[F.jsx(jXn,{size:13})," ",H]}),F.jsxs("span",{className:"meta-chip",title:String(g.clock??"Default scene cadence"),children:[F.jsx(xXn,{size:13})," ",eKn(g)]})]}),F.jsxs("div",{className:"target-summary",children:[F.jsx("strong",{children:g.targetCount})," concrete target",g.targetCount===1?"":"s"]}),F.jsxs("div",{className:"ports-grid",children:[F.jsx(oue,{title:"Inputs",side:"input",ports:g.inputs,required:M,candidates:N,previous:P,cycleBreaks:k,cycleBreakMode:g.cycleBreakMode,application:g,onCandidateClick:g.onCandidateClick,onPortClick:g.onPortClick,onCycleBreak:g.onCycleBreak}),F.jsx(oue,{title:"Outputs",side:"output",ports:g.outputs,required:M,candidates:N,previous:P,cycleBreaks:k,cycleBreakMode:g.cycleBreakMode,application:g,onCandidateClick:g.onCandidateClick,onPortClick:g.onPortClick,onCycleBreak:g.onCycleBreak})]}),(g.environmentInputs.length>0||g.environmentOutputs.length>0)&&F.jsxs("div",{className:"ports-grid environment-ports",children:[F.jsx(oue,{title:"Environment inputs",side:"input",ports:g.environmentInputs,required:M,candidates:new Set,previous:new Set,cycleBreaks:new Set,cycleBreakMode:!1,application:g,onPortClick:g.onPortClick}),F.jsx(oue,{title:"Environment outputs",side:"output",ports:g.environmentOutputs,required:M,candidates:new Set,previous:new Set,cycleBreaks:new Set,cycleBreakMode:!1,application:g,onPortClick:g.onPortClick})]})]})]})}function QXn({data:g,selected:E}){return F.jsxs("section",{className:`entity-node ${g.nodeKind} ${E?"selected":""}`,"data-testid":`${g.nodeKind}-node`,children:[(g.inputPortIds?.length?g.inputPortIds:[void 0]).map((x,M)=>F.jsx(o5,{id:x,type:"target",position:cr.Left,style:{top:`${Tue(M,g.inputPortIds?.length??1)}%`}},x??"target")),F.jsxs("header",{children:[F.jsx("strong",{children:g.title}),F.jsx("span",{children:g.subtitle})]}),F.jsx("div",{className:"badges",children:g.badges.map(x=>F.jsx("span",{className:"meta-chip",children:x},x))}),(g.outputPortIds?.length?g.outputPortIds:[void 0]).map((x,M)=>F.jsx(o5,{id:x,type:"source",position:cr.Right,style:{top:`${Tue(M,g.outputPortIds?.length??1)}%`}},x??"source"))]})}function oue({title:g,side:E,ports:x,required:M,candidates:N,previous:P,cycleBreaks:k,cycleBreakMode:H,application:U,onCandidateClick:G,onPortClick:ie,onCycleBreak:Z}){return F.jsxs("div",{className:`port-column ${E}`,children:[F.jsx("div",{className:"port-title",children:g}),x.map(W=>F.jsxs("div",{className:`port ${M.has(W.id)?"required-input":""} ${P.has(W.id)?"previous":""}`,"data-testid":`port-${E}-${W.name}`,title:`${W.name}: ${W.defaultJulia}`,onClick:se=>{se.stopPropagation(),ie?.(W)},children:[E==="input"&&F.jsx(o5,{id:W.id,type:"target",position:cr.Left}),F.jsx("span",{children:W.name}),N.has(W.id)&&F.jsx("button",{className:"port-candidate-button nodrag nopan",type:"button",title:E==="input"?"Models that compute this variable":"Models that consume this variable","aria-label":E==="input"?`Models that compute ${W.name}`:`Models that consume ${W.name}`,onClick:se=>{se.stopPropagation();const le=se.currentTarget.getBoundingClientRect();G?.(W,{x:le.right,y:le.top+le.height/2})},children:F.jsx(QG,{size:11})}),E==="input"&&H&&k.has(W.id)&&F.jsx("button",{className:"cycle-port-break nodrag nopan",type:"button",title:`Read ${W.name} from the previous accepted timestep`,"aria-label":`Break cycle at ${U.applicationId}.${W.name}`,"data-testid":`cycle-break-${U.applicationId}-${W.name}`,onClick:se=>{se.stopPropagation(),Z?.(U,W)},children:F.jsx(Q0n,{size:12})}),P.has(W.id)&&F.jsx("small",{className:"previous-label",children:"t-1"}),E==="output"&&F.jsx(o5,{id:W.id,type:"source",position:cr.Right})]},W.id))]})}function WXn({inputs:g,outputs:E}){return F.jsxs(F.Fragment,{children:[g.map((x,M)=>F.jsx(o5,{id:x.id,type:"target",position:cr.Left,style:{top:`${Tue(M,g.length)}%`}},x.id)),E.map((x,M)=>F.jsx(o5,{id:x.id,type:"source",position:cr.Right,style:{top:`${Tue(M,E.length)}%`}},x.id))]})}function Tue(g,E){return E<=1?52:28+g/(E-1)*48}function ZXn(g){const E=[...g.targetInstances,...g.targetScales,...g.targetKinds];return E.length>0?E.slice(0,2).join(" / "):g.selector.type}function eKn(g){return g.timestep===null||g.timestep===void 0?"default rate":String(g.timestep)}function nKn({id:g,sourceX:E,sourceY:x,targetX:M,targetY:N,sourcePosition:P=cr.Right,targetPosition:k=cr.Left,markerEnd:H,style:U,data:G}){const[ie,Z,W]=Aue({sourceX:E,sourceY:x,targetX:M,targetY:N,sourcePosition:P,targetPosition:k,borderRadius:14,offset:24}),se=tKn(G);return F.jsxs(F.Fragment,{children:[F.jsx(cq,{id:g,path:ie,markerEnd:H,style:U,interactionWidth:18}),se&&F.jsx(JUn,{children:F.jsx("div",{className:`edge-chip ${G?.kind??""} ${G?.cycle?"cycle":""}`,style:{transform:`translate(-50%, -50%) translate(${Z}px, ${W-12}px)`},children:se})})]})}function tKn(g){return g?g.kind==="manual_call"?g.call||"call":g.kind==="object_topology"||g.kind==="application_target"?"":g.sourceVariable&&g.targetVariable?g.sourceVariable===g.targetVariable?g.sourceVariable:`${g.sourceVariable} → ${g.targetVariable}`:g.kind.replaceAll("_"," "):""}function sue(g){throw new Error('Could not dynamically require "'+g+'". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.')}var K7e={exports:{}},Z1n;function iKn(){return Z1n||(Z1n=1,(function(g,E){(function(x){g.exports=x()})(function(){return(function(){function x(M,N,P){function k(G,ie){if(!N[G]){if(!M[G]){var Z=typeof sue=="function"&&sue;if(!ie&&Z)return Z(G,!0);if(H)return H(G,!0);var W=new Error("Cannot find module '"+G+"'");throw W.code="MODULE_NOT_FOUND",W}var se=N[G]={exports:{}};M[G][0].call(se.exports,function(le){var ee=M[G][1][le];return k(ee||le)},se,se.exports,x,M,N,P)}return N[G].exports}for(var H=typeof sue=="function"&&sue,U=0;U0&&arguments[0]!==void 0?arguments[0]:{},ee=le.defaultLayoutOptions,Oe=ee===void 0?{}:ee,ge=le.algorithms,Pe=ge===void 0?["layered","stress","mrtree","radial","force","disco","sporeOverlap","sporeCompaction","rectpacking"]:ge,ae=le.workerFactory,Me=le.workerUrl;if(k(this,W),this.defaultLayoutOptions=Oe,this.initialized=!1,typeof Me>"u"&&typeof ae>"u")throw new Error("Cannot construct an ELK without both 'workerUrl' and 'workerFactory'.");var Be=ae;typeof Me<"u"&&typeof ae>"u"&&(Be=function(xn){return new Worker(xn)});var rn=Be(Me);if(typeof rn.postMessage!="function")throw new TypeError("Created worker does not provide the required 'postMessage' function.");this.worker=new Z(rn),this.worker.postMessage({cmd:"register",algorithms:Pe}).then(function(ln){return se.initialized=!0}).catch(console.err)}return U(W,[{key:"layout",value:function(le){var ee=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},Oe=ee.layoutOptions,ge=Oe===void 0?this.defaultLayoutOptions:Oe,Pe=ee.logging,ae=Pe===void 0?!1:Pe,Me=ee.measureExecutionTime,Be=Me===void 0?!1:Me;return le?this.worker.postMessage({cmd:"layout",graph:le,layoutOptions:ge,options:{logging:ae,measureExecutionTime:Be}}):Promise.reject(new Error("Missing mandatory parameter 'graph'."))}},{key:"knownLayoutAlgorithms",value:function(){return this.worker.postMessage({cmd:"algorithms"})}},{key:"knownLayoutOptions",value:function(){return this.worker.postMessage({cmd:"options"})}},{key:"knownLayoutCategories",value:function(){return this.worker.postMessage({cmd:"categories"})}},{key:"terminateWorker",value:function(){this.worker&&this.worker.terminate()}}])})();var Z=(function(){function W(se){var le=this;if(k(this,W),se===void 0)throw new Error("Missing mandatory parameter 'worker'.");this.resolvers={},this.worker=se,this.worker.onmessage=function(ee){setTimeout(function(){le.receive(le,ee)},0)}}return U(W,[{key:"postMessage",value:function(le){var ee=this.id||0;this.id=ee+1,le.id=ee;var Oe=this;return new Promise(function(ge,Pe){Oe.resolvers[ee]=function(ae,Me){ae?(Oe.convertGwtStyleError(ae),Pe(ae)):ge(Me)},Oe.worker.postMessage(le)})}},{key:"receive",value:function(le,ee){var Oe=ee.data,ge=le.resolvers[Oe.id];ge&&(delete le.resolvers[Oe.id],Oe.error?ge(Oe.error):ge(null,Oe.data))}},{key:"terminate",value:function(){this.worker&&this.worker.terminate()}},{key:"convertGwtStyleError",value:function(le){if(le){var ee=le.__java$exception;ee&&(ee.cause&&ee.cause.backingJsObject&&(le.cause=ee.cause.backingJsObject,this.convertGwtStyleError(le.cause)),delete le.__java$exception)}}}])})()},{}],2:[function(x,M,N){(function(P){(function(){var k;typeof window<"u"?k=window:typeof P<"u"?k=P:typeof self<"u"&&(k=self);var H;function U(){}function G(){}function ie(){}function Z(){}function W(){}function se(){}function le(){}function ee(){}function Oe(){}function ge(){}function Pe(){}function ae(){}function Me(){}function Be(){}function rn(){}function ln(){}function xn(){}function hn(){}function wt(){}function Tn(){}function Qn(){}function Y(){}function Fe(){}function mn(){}function Ae(){}function Ze(){}function sn(){}function Sn(){}function kn(){}function xe(){}function un(){}function rt(){}function lt(){}function Bt(){}function hi(){}function Gt(){}function At(){}function st(){}function Wr(){}function Mr(){}function ur(){}function mi(){}function Fi(){}function fu(){}function Eu(){}function Ws(){}function Dh(){}function ef(){}function ch(){}function kc(){}function cd(){}function jb(){}function Rs(){}function c0(){}function u0(){}function o0(){}function Bg(){}function r6(){}function zg(){}function c6(){}function d1(){}function ud(){}function Sf(){}function b1(){}function xf(){}function l5(){}function f5(){}function a5(){}function Fg(){}function Eb(){}function Jg(){}function _u(){}function Hg(){}function Gg(){}function u6(){}function h5(){}function Wm(){}function qg(){}function o6(){}function Ug(){}function Zm(){}function d5(){}function Mt(){}function Cc(){}function dc(){}function s6(){}function Af(){}function Zs(){}function Ta(){}function Xg(){}function b5(){}function ek(){}function MA(){}function nk(){}function e3(){}function l6(){}function xp(){}function Ap(){}function Mp(){}function Cp(){}function xl(){}function g5(){}function tk(){}function Kg(){}function Tp(){}function CA(){}function f6(){}function ik(){}function rk(){}function n3(){}function w5(){}function s0(){}function Ih(){}function ck(){}function TA(){}function p5(){}function uk(){}function t3(){}function OA(){}function _h(){}function i3(){}function ok(){}function a6(){}function Vg(){}function vI(){}function yI(){}function m5(){}function uq(){}function kI(){}function jI(){}function NA(){}function oq(){}function sq(){}function sk(){}function Yg(){}function DA(){}function IA(){}function v5(){}function y5(){}function EI(){}function _A(){}function SI(){}function h6(){}function Qg(){}function LA(){}function d6(){}function Op(){}function PA(){}function lk(){}function xI(){}function fk(){}function ak(){}function AI(){}function Lh(){}function r3(){}function hk(){}function b6(){}function lq(){}function $A(){}function RA(){}function g6(){}function dk(){}function MI(){}function fq(){}function aq(){}function hq(){}function BA(){}function dq(){}function bq(){}function gq(){}function wq(){}function pq(){}function CI(){}function mq(){}function vq(){}function yq(){}function kq(){}function zA(){}function jq(){}function Eq(){}function Sq(){}function TI(){}function xq(){}function Aq(){}function Mq(){}function Cq(){}function Tq(){}function Oq(){}function Nq(){}function Dq(){}function Iq(){}function FA(){}function w6(){}function _q(){}function OI(){}function NI(){}function DI(){}function II(){}function _I(){}function k5(){}function Lq(){}function Pq(){}function $q(){}function LI(){}function PI(){}function p6(){}function m6(){}function Rq(){}function bk(){}function $I(){}function JA(){}function HA(){}function GA(){}function RI(){}function BI(){}function zI(){}function Bq(){}function zq(){}function Fq(){}function Jq(){}function Hq(){}function g1(){}function v6(){}function FI(){}function JI(){}function HI(){}function GI(){}function qA(){}function Gq(){}function j5(){}function UA(){}function y6(){}function XA(){}function qI(){}function c3(){}function E5(){}function KA(){}function UI(){}function u3(){}function XI(){}function KI(){}function VI(){}function qq(){}function Uq(){}function Xq(){}function YI(){}function QI(){}function VA(){}function l0(){}function gk(){}function od(){}function S5(){}function YA(){}function wk(){}function pk(){}function QA(){}function o3(){}function WI(){}function mk(){}function x5(){}function Kq(){}function w1(){}function WA(){}function Wg(){}function ZI(){}function vk(){}function s3(){}function ZA(){}function e_(){}function eM(){}function n_(){}function sd(){}function A5(){}function M5(){}function yk(){}function k6(){}function ld(){}function fd(){}function Np(){}function Sb(){}function xb(){}function Zg(){}function t_(){}function nM(){}function tM(){}function i_(){}function ra(){}function Jo(){}function cu(){}function Dp(){}function ad(){}function iM(){}function Ip(){}function r_(){}function c_(){}function C5(){}function l3(){}function T5(){}function _p(){}function rM(){}function Lp(){}function ew(){}function Pp(){}function nw(){}function cM(){}function uM(){}function O5(){}function j6(){}function $p(){}function ca(){}function E6(){}function oM(){}function Vq(){}function Yq(){}function S6(){}function Al(){}function sM(){}function x6(){}function A6(){}function lM(){}function N5(){}function D5(){}function Qq(){}function u_(){}function Wq(){}function o_(){}function f3(){}function fM(){}function kk(){}function s_(){}function I5(){}function aM(){}function jk(){}function Ek(){}function l_(){}function f_(){}function a3(){}function h3(){}function a_(){}function hM(){}function _5(){}function M6(){}function Sk(){}function C6(){}function xk(){}function h_(){}function d3(){}function d_(){}function Rp(){}function dM(){}function bM(){}function Bp(){}function zp(){}function T6(){}function gM(){}function wM(){}function O6(){}function N6(){}function b_(){}function g_(){}function L5(){}function Ak(){}function w_(){}function pM(){}function mM(){}function p1(){}function hd(){}function Fp(){}function vM(){}function p_(){}function Jp(){}function m1(){}function el(){}function Mk(){}function tw(){}function bc(){}function vo(){}function Ml(){}function Ck(){}function P5(){}function b3(){}function Tk(){}function D6(){}function $5(){}function Zq(){}function Bs(){}function yM(){}function kM(){}function m_(){}function v_(){}function eU(){}function jM(){}function EM(){}function SM(){}function uh(){}function nl(){}function Ok(){}function I6(){}function Nk(){}function xM(){}function iw(){}function Dk(){}function AM(){}function MM(){}function y_(){}function k_(){}function j_(){}function nU(){}function E_(){}function S_(){}function CM(){}function x_(){}function tU(){}function A_(){}function M_(){}function C_(){}function TM(){}function T_(){}function O_(){}function N_(){}function D_(){}function I_(){}function iU(){}function __(){}function R5(){}function L_(){}function Ik(){}function _k(){}function P_(){}function OM(){}function rU(){}function $_(){}function R_(){}function B_(){}function z_(){}function F_(){}function NM(){}function J_(){}function H_(){}function DM(){}function G_(){}function q_(){}function IM(){}function _6(){}function U_(){}function Lk(){}function _M(){}function X_(){}function K_(){}function cU(){}function uU(){}function V_(){}function L6(){}function LM(){}function Pk(){}function Y_(){}function PM(){}function P6(){}function oU(){}function $M(){}function Q_(){}function RM(){}function BM(){}function W_(){}function Z_(){}function g3(){}function eL(){}function dd(){}function nL(){}function f0(){}function zM(){}function FM(){}function tL(){}function iL(){}function sU(){}function JM(){}function Cl(){}function ua(){}function rL(){}function cL(){}function uL(){}function oL(){}function $6(){}function sL(){}function $k(){}function lL(){}function lU(){}function Rk(){}function HM(){}function fL(){}function aL(){}function Je(){}function GM(){}function qM(){}function UM(){}function hL(){}function XM(){}function Bk(){}function KM(){}function dL(){}function VM(){}function bL(){}function rw(){}function R6(){}function fU(){}function gL(){}function a0(){}function YM(){}function wL(){}function zk(){}function w3(){}function yo(){}function Fk(){}function aU(){}function QM(){}function B6(){}function Hp(){}function z6(){}function pL(){}function F6(){}function Ab(){}function J6(){}function WM(){}function mL(){}function ZM(){}function eC(){}function p3(){}function vL(){}function Mb(){}function Tl(){}function H6(){}function nC(){}function Mf(){}function hU(){}function yL(){}function kL(){}function Ss(){}function Ph(){}function cw(){}function jL(){}function EL(){}function SL(){}function dU(){}function Jk(){}function $h(){}function h0(){}function xL(){}function Ol(){}function Hk(){}function uw(){}function m3(){}function ow(){}function tC(){}function iC(){}function d0(){}function AL(){}function B5(){}function G6(){}function q6(){}function z5(){}function ML(){}function CL(){}function U6(){}function TL(){}function Gk(){}function OL(){}function bU(){}function gU(){}function Ju(){}function Io(){}function Hc(){}function nu(){}function io(){}function v1(){}function Gp(){}function F5(){}function rC(){}function sw(){}function zs(){}function qp(){}function v3(){}function cC(){}function y1(){}function J5(){}function X6(){}function Rh(){}function uC(){}function qk(){}function NL(){}function Uk(){}function Xk(){}function Up(){}function nf(){}function Xp(){}function H5(){}function lw(){}function oC(){}function sC(){}function DL(){}function K6(){}function lC(){}function k1(){}function IL(){}function Bh(){}function _L(){}function LL(){}function wU(){}function Kp(){}function Kk(){}function fC(){}function G5(){}function PL(){}function $L(){}function RL(){}function BL(){}function Vk(){}function aC(){}function pU(){}function mU(){}function vU(){}function zL(){}function FL(){}function q5(){}function Yk(){}function JL(){}function HL(){}function GL(){}function qL(){}function UL(){}function XL(){}function Qk(){}function KL(){}function VL(){}function ro(){}function hC(){}function yU(){}function YL(){}function kU(){}function jU(){}function EU(){}function Wk(){}function U5(){}function dC(){}function Zk(){}function bC(){}function Vp(){}function Cb(){}function V6(){}function SU(){}function QL(){}function gC(){}function WL(){}function ZL(){}function wC(){mj()}function pC(){C0e()}function eP(){Gf()}function nP(){Rde()}function xU(){PO()}function mC(){OT()}function vC(){iT()}function AU(){tT()}function MU(){CMe()}function Y6(){z4()}function CU(){i$e()}function tP(){n8()}function Gc(){F0()}function yC(){Bhe()}function ej(){X_e()}function kC(){Rhe()}function iP(){V_e()}function jC(){K_e()}function Q6(){Y_e()}function nj(){L$e()}function TU(){Q_e()}function rP(){ABe()}function OU(){Ne()}function NU(){QP()}function cP(){SBe()}function uP(){xBe()}function ko(){VLe()}function EC(){Ige()}function oa(){MBe()}function DU(){Z_e()}function oP(){R4()}function IU(){BFe()}function SC(){P1()}function xC(){cbe()}function tj(){FO()}function sP(){VBe()}function lP(){Gbe()}function AC(){CHe()}function MC(){W_e()}function _U(){QXe()}function fP(){Mu()}function LU(){Ja()}function aP(){nge()}function PU(){J0()}function $U(){IW()}function Yp(){JQ()}function hP(){tB()}function CC(){Xt()}function TC(){Ez()}function RU(){$B()}function BU(){bde()}function OC(){Gz()}function ij(){FY()}function tl(){INe()}function zU(){rge()}function j1(e){_n(e)}function NC(e){this.a=e}function W6(e){this.a=e}function dP(e){this.a=e}function bP(e){this.a=e}function Z6(e){this.a=e}function E1(e){this.a=e}function DC(e){this.a=e}function rj(e){this.a=e}function b0(e){this.a=e}function FU(e){this.a=e}function JU(e){this.a=e}function X5(e){this.a=e}function gP(e){this.a=e}function HU(e){this.c=e}function GU(e){this.a=e}function IC(e){this.a=e}function qU(e){this.a=e}function UU(e){this.a=e}function XU(e){this.a=e}function _C(e){this.a=e}function wP(e){this.a=e}function K5(e){this.a=e}function y3(e){this.a=e}function pP(e){this.a=e}function LC(e){this.a=e}function V5(e){this.a=e}function e9(e){this.a=e}function mP(e){this.a=e}function cj(e){this.a=e}function PC(e){this.a=e}function $C(e){this.a=e}function uj(e){this.a=e}function vP(e){this.a=e}function yP(e){this.a=e}function KU(e){this.a=e}function kP(e){this.a=e}function VU(e){this.a=e}function RC(e){this.a=e}function YU(e){this.a=e}function n9(e){this.a=e}function t9(e){this.a=e}function k3(e){this.a=e}function i9(e){this.a=e}function Y5(e){this.b=e}function bd(){this.a=[]}function jP(e,n){e.a=n}function QU(e,n){e.a=n}function WU(e,n){e.b=n}function ZU(e,n){e.c=n}function EP(e,n){e.c=n}function eX(e,n){e.d=n}function nX(e,n){e.d=n}function Cf(e,n){e.k=n}function SP(e,n){e.j=n}function Jue(e,n){e.c=n}function oj(e,n){e.c=n}function sj(e,n){e.a=n}function BC(e,n){e.a=n}function xP(e,n){e.f=n}function tX(e,n){e.a=n}function AP(e,n){e.b=n}function Tb(e,n){e.d=n}function g0(e,n){e.i=n}function fw(e,n){e.o=n}function lj(e,n){e.r=n}function fj(e,n){e.a=n}function j3(e,n){e.b=n}function iX(e,n){e.e=n}function rX(e,n){e.f=n}function Q5(e,n){e.g=n}function Hue(e,n){e.e=n}function cX(e,n){e.f=n}function zC(e,n){e.f=n}function aj(e,n){e.a=n}function FC(e,n){e.b=n}function JC(e,n){e.n=n}function HC(e,n){e.a=n}function uX(e,n){e.c=n}function r9(e,n){e.c=n}function oX(e,n){e.c=n}function MP(e,n){e.a=n}function GC(e,n){e.a=n}function sX(e,n){e.d=n}function Gue(e,n){e.d=n}function qC(e,n){e.e=n}function a(e,n){e.e=n}function d(e,n){e.g=n}function w(e,n){e.f=n}function j(e,n){e.j=n}function T(e,n){e.a=n}function D(e,n){e.a=n}function Q(e,n){e.b=n}function de(e){e.b=e.a}function en(e){e.c=e.d.d}function Ln(e){this.a=e}function nt(e){this.a=e}function gt(e){this.a=e}function Jn(e){this.a=e}function Zn(e){this.a=e}function Di(e){this.a=e}function Cr(e){this.a=e}function co(e){this.a=e}function En(e){this.a=e}function on(e){this.a=e}function In(e){this.a=e}function ct(e){this.a=e}function Ji(e){this.a=e}function Lu(e){this.a=e}function Ui(e){this.b=e}function Hr(e){this.b=e}function tc(e){this.b=e}function qc(e){this.d=e}function xt(e){this.a=e}function lX(e){this.a=e}function que(e){this.a=e}function _ke(e){this.a=e}function Lke(e){this.a=e}function Uue(e){this.a=e}function Xue(e){this.a=e}function fX(e){this.c=e}function L(e){this.c=e}function Pke(e){this.c=e}function Kue(e){this.a=e}function Vue(e){this.a=e}function Yue(e){this.a=e}function Que(e){this.a=e}function c9(e){this.a=e}function $ke(e){this.a=e}function Rke(e){this.a=e}function u9(e){this.a=e}function Bke(e){this.a=e}function zke(e){this.a=e}function Fke(e){this.a=e}function Jke(e){this.a=e}function Hke(e){this.a=e}function Gke(e){this.a=e}function qke(e){this.a=e}function Uke(e){this.a=e}function Xke(e){this.a=e}function Kke(e){this.a=e}function Vke(e){this.a=e}function hj(e){this.a=e}function Yke(e){this.a=e}function Qke(e){this.a=e}function CP(e){this.a=e}function Wke(e){this.a=e}function Zke(e){this.a=e}function Wue(e){this.a=e}function eje(e){this.a=e}function nje(e){this.a=e}function tje(e){this.a=e}function Zue(e){this.a=e}function eoe(e){this.a=e}function noe(e){this.a=e}function dj(e){this.a=e}function o9(e){this.a=e}function ije(e){this.a=e}function W5(e){this.a=e}function toe(e){this.a=e}function rje(e){this.a=e}function cje(e){this.a=e}function uje(e){this.a=e}function oje(e){this.a=e}function sje(e){this.a=e}function lje(e){this.a=e}function fje(e){this.a=e}function aje(e){this.a=e}function hje(e){this.a=e}function dje(e){this.a=e}function bje(e){this.a=e}function ioe(e){this.a=e}function gje(e){this.a=e}function wje(e){this.a=e}function pje(e){this.a=e}function mje(e){this.a=e}function vje(e){this.a=e}function yje(e){this.a=e}function kje(e){this.a=e}function jje(e){this.a=e}function Eje(e){this.a=e}function Sje(e){this.a=e}function xje(e){this.a=e}function Aje(e){this.a=e}function Mje(e){this.a=e}function Cje(e){this.a=e}function Tje(e){this.a=e}function Oje(e){this.a=e}function Nje(e){this.a=e}function Dje(e){this.a=e}function Ije(e){this.a=e}function _je(e){this.a=e}function Lje(e){this.a=e}function Pje(e){this.a=e}function $je(e){this.a=e}function Rje(e){this.a=e}function Bje(e){this.a=e}function zje(e){this.a=e}function Fje(e){this.a=e}function Jje(e){this.a=e}function Hje(e){this.a=e}function Gje(e){this.a=e}function qje(e){this.a=e}function Uje(e){this.a=e}function Xje(e){this.a=e}function Kje(e){this.a=e}function Vje(e){this.a=e}function Yje(e){this.a=e}function Qje(e){this.b=e}function Wje(e){this.a=e}function Zje(e){this.a=e}function eEe(e){this.a=e}function nEe(e){this.a=e}function tEe(e){this.a=e}function iEe(e){this.a=e}function rEe(e){this.c=e}function cEe(e){this.a=e}function uEe(e){this.a=e}function oEe(e){this.a=e}function sEe(e){this.a=e}function lEe(e){this.a=e}function fEe(e){this.a=e}function aEe(e){this.a=e}function hEe(e){this.a=e}function dEe(e){this.a=e}function bEe(e){this.a=e}function gEe(e){this.a=e}function wEe(e){this.a=e}function pEe(e){this.a=e}function mEe(e){this.a=e}function vEe(e){this.a=e}function yEe(e){this.a=e}function kEe(e){this.a=e}function jEe(e){this.a=e}function EEe(e){this.a=e}function SEe(e){this.a=e}function xEe(e){this.a=e}function AEe(e){this.a=e}function MEe(e){this.a=e}function CEe(e){this.a=e}function TEe(e){this.a=e}function OEe(e){this.a=e}function NEe(e){this.a=e}function S1(e){this.a=e}function E3(e){this.a=e}function DEe(e){this.a=e}function IEe(e){this.a=e}function _Ee(e){this.a=e}function LEe(e){this.a=e}function PEe(e){this.a=e}function $Ee(e){this.a=e}function REe(e){this.a=e}function BEe(e){this.a=e}function zEe(e){this.a=e}function FEe(e){this.a=e}function JEe(e){this.a=e}function HEe(e){this.a=e}function GEe(e){this.a=e}function qEe(e){this.a=e}function UEe(e){this.a=e}function XEe(e){this.a=e}function KEe(e){this.a=e}function VEe(e){this.a=e}function YEe(e){this.a=e}function QEe(e){this.a=e}function WEe(e){this.a=e}function ZEe(e){this.a=e}function eSe(e){this.a=e}function nSe(e){this.a=e}function tSe(e){this.a=e}function iSe(e){this.a=e}function TP(e){this.a=e}function rSe(e){this.f=e}function cSe(e){this.a=e}function uSe(e){this.a=e}function oSe(e){this.a=e}function sSe(e){this.a=e}function lSe(e){this.a=e}function fSe(e){this.a=e}function aSe(e){this.a=e}function hSe(e){this.a=e}function dSe(e){this.a=e}function bSe(e){this.a=e}function gSe(e){this.a=e}function wSe(e){this.a=e}function pSe(e){this.a=e}function mSe(e){this.a=e}function vSe(e){this.a=e}function ySe(e){this.a=e}function kSe(e){this.a=e}function jSe(e){this.a=e}function ESe(e){this.a=e}function SSe(e){this.a=e}function xSe(e){this.a=e}function ASe(e){this.a=e}function MSe(e){this.a=e}function CSe(e){this.a=e}function TSe(e){this.a=e}function OSe(e){this.a=e}function NSe(e){this.a=e}function aX(e){this.a=e}function roe(e){this.a=e}function yi(e){this.b=e}function DSe(e){this.a=e}function ISe(e){this.a=e}function _Se(e){this.a=e}function LSe(e){this.a=e}function PSe(e){this.a=e}function $Se(e){this.a=e}function RSe(e){this.a=e}function BSe(e){this.b=e}function zSe(e){this.a=e}function UC(e){this.a=e}function FSe(e){this.a=e}function JSe(e){this.a=e}function OP(e){this.a=e}function NP(e){this.a=e}function coe(e){this.c=e}function DP(e){this.e=e}function IP(e){this.e=e}function hX(e){this.a=e}function HSe(e){this.d=e}function GSe(e){this.a=e}function uoe(e){this.a=e}function ooe(e){this.a=e}function aw(e){this.e=e}function ebn(){this.a=0}function Te(){MK(this)}function pt(){Hu(this)}function dX(){_Ie(this)}function qSe(){}function hw(){this.c=Q8e}function USe(e,n){e.b+=n}function nbn(e,n){n.Wb(e)}function tbn(e){return e.a}function ibn(e){return e.a}function rbn(e){return e.a}function cbn(e){return e.a}function ubn(e){return e.a}function $(e){return e.e}function obn(){return null}function sbn(){return null}function lbn(e){throw $(e)}function Z5(e){this.a=Nt(e)}function XSe(){this.a=this}function Ob(){aOe.call(this)}function fbn(e){e.b.Mf(e.e)}function KSe(e){e.b=new OX}function bj(e,n){e.b=n-e.b}function gj(e,n){e.a=n-e.a}function VSe(e,n){n.gd(e.a)}function abn(e,n){xr(n,e)}function Hn(e,n){e.push(n)}function YSe(e,n){e.sort(n)}function hbn(e,n,t){e.Wd(t,n)}function XC(e,n){e.e=n,n.b=e}function dbn(){zoe(),LRn()}function QSe(e){$9(),tte.je(e)}function soe(){Ob.call(this)}function bX(){Ob.call(this)}function loe(){aOe.call(this)}function WSe(){Ob.call(this)}function Nl(){Ob.call(this)}function ZSe(){Ob.call(this)}function KC(){Ob.call(this)}function is(){Ob.call(this)}function e4(){Ob.call(this)}function _t(){Ob.call(this)}function au(){Ob.call(this)}function exe(){Ob.call(this)}function _P(){this.Bb|=256}function nxe(){this.b=new fTe}function foe(){foe=Y,new pt}function Qp(e,n){e.length=n}function LP(e,n){Ce(e.a,n)}function bbn(e,n){O0e(e.c,n)}function gbn(e,n){hr(e.b,n)}function s9(e,n){ai(e.e,n)}function wbn(e,n){lz(e.a,n)}function pbn(e,n){gQ(e.a,n)}function n4(e){Mz(e.c,e.b)}function mbn(e,n){e.kc().Nb(n)}function aoe(e){this.a=Cjn(e)}function ar(){this.a=new pt}function txe(){this.a=new pt}function PP(){this.a=new Te}function gX(){this.a=new Te}function hoe(){this.a=new Te}function Nb(){this.a=new XPe}function wX(){this.a=new kMe}function doe(){this.a=new $_e}function boe(){this.a=new iNe}function tf(){this.a=new d1}function goe(){this.a=new Zm}function ixe(){this.a=new wLe}function rxe(){this.a=new Te}function cxe(){this.a=new Te}function woe(){this.a=new Te}function uxe(){this.a=new Te}function oxe(){this.d=new Te}function sxe(){this.a=new ar}function lxe(){this.a=new pt}function fxe(){this.b=new pt}function axe(){this.b=new Te}function poe(){this.e=new Te}function hxe(){this.a=new Gc}function dxe(){this.d=new Te}function wj(){qSe.call(this)}function pX(){wj.call(this)}function t4(){qSe.call(this)}function moe(){t4.call(this)}function bxe(){soe.call(this)}function $P(){PP.call(this)}function gxe(){q$.call(this)}function wxe(){woe.call(this)}function pxe(){Te.call(this)}function mxe(){b_e.call(this)}function vxe(){b_e.call(this)}function yxe(){joe.call(this)}function kxe(){joe.call(this)}function jxe(){joe.call(this)}function Exe(){Eoe.call(this)}function pj(){zk.call(this)}function voe(){zk.call(this)}function xs(){Si.call(this)}function Sxe(){Rxe.call(this)}function xxe(){Rxe.call(this)}function Axe(){pt.call(this)}function Mxe(){pt.call(this)}function Cxe(){pt.call(this)}function mX(){yBe.call(this)}function Txe(){ar.call(this)}function Oxe(){_P.call(this)}function vX(){cle.call(this)}function yoe(){pt.call(this)}function yX(){cle.call(this)}function kX(){pt.call(this)}function Nxe(){pt.call(this)}function koe(){p3.call(this)}function Dxe(){koe.call(this)}function Ixe(){p3.call(this)}function _xe(){gC.call(this)}function joe(){this.a=new ar}function Lxe(){this.a=new pt}function Pxe(){this.a=new Te}function $xe(){this.j=new Te}function Eoe(){this.a=new pt}function i4(){this.a=new Si}function Rxe(){this.a=new WM}function Soe(){this.a=new z_}function Bxe(){this.a=new PAe}function mj(){mj=Y,Kne=new G}function jX(){jX=Y,Vne=new Fxe}function EX(){EX=Y,Yne=new zxe}function zxe(){y3.call(this,"")}function Fxe(){y3.call(this,"")}function Jxe(e){YRe.call(this,e)}function Hxe(e){YRe.call(this,e)}function xoe(e){E1.call(this,e)}function Aoe(e){VAe.call(this,e)}function vbn(e){VAe.call(this,e)}function ybn(e){Aoe.call(this,e)}function kbn(e){Aoe.call(this,e)}function jbn(e){Aoe.call(this,e)}function Gxe(e){cY.call(this,e)}function qxe(e){cY.call(this,e)}function Uxe(e){KTe.call(this,e)}function Xxe(e){Xoe.call(this,e)}function vj(e){KP.call(this,e)}function Moe(e){KP.call(this,e)}function Kxe(e){KP.call(this,e)}function hu(e){JDe.call(this,e)}function Vxe(e){hu.call(this,e)}function r4(){i9.call(this,{})}function SX(e){y9(),this.a=e}function Yxe(e){e.b=null,e.c=0}function Ebn(e,n){e.e=n,YUe(e,n)}function Sbn(e,n){e.a=n,PCn(e)}function xX(e,n,t){e.a[n.g]=t}function xbn(e,n,t){iAn(t,e,n)}function Abn(e,n){i2n(n.i,e.n)}function Qxe(e,n){pkn(e).Ad(n)}function Mbn(e,n){return e*e/n}function Wxe(e,n){return e.g-n.g}function Cbn(e,n){e.a.ec().Kc(n)}function Tbn(e){return new k3(e)}function Obn(e){return new y2(e)}function Zxe(){Zxe=Y,vme=new U}function Coe(){Coe=Y,yme=new Be}function RP(){RP=Y,US=new xn}function BP(){BP=Y,Wne=new XTe}function eAe(){eAe=Y,Wen=new wt}function zP(e){n1e(),this.a=e}function AX(e){lV(),this.f=e}function w0(e){lV(),this.f=e}function nAe(e){DNe(),this.a=e}function FP(e){hu.call(this,e)}function jo(e){hu.call(this,e)}function tAe(e){hu.call(this,e)}function MX(e){JDe.call(this,e)}function l9(e){hu.call(this,e)}function Gn(e){hu.call(this,e)}function Uc(e){hu.call(this,e)}function iAe(e){hu.call(this,e)}function c4(e){hu.call(this,e)}function gd(e){hu.call(this,e)}function Su(e){_n(e),this.a=e}function yj(e){$fe(e,e.length)}function Toe(e){return Zb(e),e}function Wp(e){return!!e&&e.b}function Nbn(e){return!!e&&e.k}function Dbn(e){return!!e&&e.j}function kj(e){return e.b==e.c}function Re(e){return _n(e),e}function ne(e){return _n(e),e}function VC(e){return _n(e),e}function Ooe(e){return _n(e),e}function Ibn(e){return _n(e),e}function oh(e){hu.call(this,e)}function wd(e){hu.call(this,e)}function u4(e){hu.call(this,e)}function CX(e){hu.call(this,e)}function zt(e){hu.call(this,e)}function TX(e){dle.call(this,e,0)}function OX(){Sae.call(this,12,3)}function NX(){this.a=Pt(Nt(To))}function rAe(){throw $(new _t)}function Noe(){throw $(new _t)}function cAe(){throw $(new _t)}function _bn(){throw $(new _t)}function Lbn(){throw $(new _t)}function Pbn(){throw $(new _t)}function JP(){JP=Y,$9()}function pd(){Di.call(this,"")}function jj(){Di.call(this,"")}function p0(){Di.call(this,"")}function o4(){Di.call(this,"")}function Doe(e){jo.call(this,e)}function Ioe(e){jo.call(this,e)}function sh(e){Gn.call(this,e)}function f9(e){Hr.call(this,e)}function uAe(e){f9.call(this,e)}function DX(e){B$.call(this,e)}function $bn(e,n,t){e.c.Cf(n,t)}function Rbn(e,n,t){n.Ad(e.a[t])}function Bbn(e,n,t){n.Ne(e.a[t])}function zbn(e,n){return e.a-n.a}function Fbn(e,n){return e.a-n.a}function Jbn(e,n){return e.a-n.a}function HP(e,n){return vY(e,n)}function B(e,n){return H_e(e,n)}function Hbn(e,n){return n in e.a}function oAe(e){return e.a?e.b:0}function Gbn(e){return e.a?e.b:0}function sAe(e,n){return e.f=n,e}function qbn(e,n){return e.b=n,e}function lAe(e,n){return e.c=n,e}function Ubn(e,n){return e.g=n,e}function _oe(e,n){return e.a=n,e}function Loe(e,n){return e.f=n,e}function Xbn(e,n){return e.f=n,e}function Poe(e,n){return e.e=n,e}function Kbn(e,n){return e.k=n,e}function $oe(e,n){return e.a=n,e}function Vbn(e,n){return e.e=n,e}function Ybn(e,n){e.b=new wc(n)}function fAe(e,n){e._d(n),n.$d(e)}function Qbn(e,n){rl(),n.n.a+=e}function Wbn(e,n){F0(),gu(n,e)}function Roe(e){UIe.call(this,e)}function aAe(e){UIe.call(this,e)}function hAe(){qse.call(this,"")}function dAe(){this.b=0,this.a=0}function bAe(){bAe=Y,ann=OAn()}function Zp(e,n){return e.b=n,e}function GP(e,n){return e.a=n,e}function e2(e,n){return e.c=n,e}function n2(e,n){return e.d=n,e}function t2(e,n){return e.e=n,e}function Boe(e,n){return e.f=n,e}function Ej(e,n){return e.a=n,e}function a9(e,n){return e.b=n,e}function h9(e,n){return e.c=n,e}function qe(e,n){return e.c=n,e}function an(e,n){return e.b=n,e}function Ue(e,n){return e.d=n,e}function Xe(e,n){return e.e=n,e}function Zbn(e,n){return e.f=n,e}function Ke(e,n){return e.g=n,e}function Ve(e,n){return e.a=n,e}function Ye(e,n){return e.i=n,e}function Qe(e,n){return e.j=n,e}function egn(e,n){return n.pg(e)}function ngn(e,n){return e.b-n.b}function tgn(e,n){return e.g-n.g}function ign(e,n){return e.s-n.s}function rgn(e,n){return e?0:n-1}function gAe(e,n){return e?0:n-1}function cgn(e,n){return e?n-1:0}function wAe(e,n){return e.k=n,e}function ugn(e,n){return e.j=n,e}function Kr(){this.a=0,this.b=0}function qP(e){UK.call(this,e)}function m0(e){Nw.call(this,e)}function pAe(e){PV.call(this,e)}function mAe(e){PV.call(this,e)}function vAe(){vAe=Y,Pr=QAn()}function v0(){v0=Y,yan=Fxn()}function zoe(){zoe=Y,Ng=EE()}function d9(){d9=Y,Y8e=Jxn()}function yAe(){yAe=Y,rhn=Hxn()}function Foe(){Foe=Y,zu=ICn()}function sa(e){return e.e&&e.e()}function kAe(e,n){return e.c._b(n)}function jAe(e,n){return yFe(e.b,n)}function EAe(e,n){return Dgn(e.a,n)}function SAe(e,n){e.b=0,O2(e,n)}function ogn(e,n){e.c=n,e.b=!0}function S3(e,n){return e.a+=n,e}function IX(e,n){return e.a+=n,e}function md(e,n){return e.a+=n,e}function dw(e,n){return e.a+=n,e}function Db(e){return M1(e),e.o}function Joe(e){MVe(),KRn(this,e)}function xAe(){throw $(new _t)}function AAe(){throw $(new _t)}function MAe(){throw $(new _t)}function CAe(){throw $(new _t)}function TAe(){throw $(new _t)}function OAe(){throw $(new _t)}function UP(e){this.a=new l4(e)}function vd(e){this.a=new bV(e)}function x3(e,n){for(;e.Pe(n););}function Hoe(e,n){for(;e.zd(n););}function sgn(e,n,t){avn(e.a,n,t)}function Goe(e,n,t){e.splice(n,t)}function lgn(e,n){return GLn(n,e)}function qoe(e,n){return e.d[n.p]}function YC(e){return e.b!=e.d.c}function NAe(e){return e.l|e.m<<22}function _X(e){return e?e.d:null}function fgn(e){return e?e.g:null}function agn(e){return e?e.i:null}function DAe(e,n){return aDn(e,n)}function b9(e){return A0(e),e.a}function IAe(e){e.c?hXe(e):dXe(e)}function _Ae(){this.b=new tS(iye)}function LAe(){this.b=new tS(Qre)}function PAe(){this.b=new tS(Qre)}function $Ae(){this.a=new tS(Rye)}function RAe(){this.a=new tS(s6e)}function XP(e){this.a=0,this.b=e}function BAe(){throw $(new _t)}function zAe(){throw $(new _t)}function FAe(){throw $(new _t)}function JAe(){throw $(new _t)}function HAe(){throw $(new _t)}function GAe(){throw $(new _t)}function qAe(){throw $(new _t)}function UAe(){throw $(new _t)}function XAe(){throw $(new _t)}function KAe(){throw $(new _t)}function hgn(){throw $(new au)}function dgn(){throw $(new au)}function QC(e){this.a=new wMe(e)}function g9(e,n){this.e=e,this.d=n}function Uoe(e,n){this.b=e,this.c=n}function VAe(e){tle(e.dc()),this.c=e}function WC(e,n){R3.call(this,e,n)}function w9(e,n){WC.call(this,e,n)}function YAe(e,n){this.a=e,this.b=n}function QAe(e,n){this.a=e,this.b=n}function WAe(e,n){this.a=e,this.b=n}function ZAe(e,n){this.a=e,this.b=n}function eMe(e,n){this.a=e,this.b=n}function nMe(e,n){this.a=e,this.b=n}function tMe(e,n){this.a=e,this.b=n}function iMe(e,n){this.b=e,this.a=n}function rMe(e,n){this.b=e,this.a=n}function bw(e,n){this.g=e,this.i=n}function cMe(e,n){this.a=e,this.b=n}function uMe(e,n){this.b=e,this.a=n}function oMe(e,n){this.a=e,this.b=n}function sMe(e,n){this.b=e,this.a=n}function KP(e){this.b=u(Nt(e),50)}function VP(e){this.b=u(Nt(e),92)}function Ot(e,n){this.f=e,this.g=n}function LX(e,n){this.a=e,this.b=n}function lMe(e,n){this.a=e,this.f=n}function fMe(e){this.a=u(Nt(e),16)}function Xoe(e){this.a=u(Nt(e),16)}function aMe(e,n){this.b=e,this.c=n}function hMe(e){this.a=u(Nt(e),92)}function bgn(e,n){this.a=e,this.b=n}function dMe(e,n){this.a=e,this.b=n}function bMe(e,n){return so(e.b,n)}function gMe(e,n){return e>n&&n0}function JX(e,n){return ao(e,n)<0}function LMe(e,n){return oV(e.a,n)}function _gn(e,n){R_e.call(this,e,n)}function ise(e){TV(),VMn.call(this,e)}function rse(e){TV(),ise.call(this,e)}function cse(e){rV(),KTe.call(this,e)}function use(e,n){ODe(e,e.length,n)}function rT(e,n){cIe(e,e.length,n)}function Dj(e,n){return e.a.get(n)}function PMe(e,n){return so(e.e,n)}function ose(e){return _n(e),!1}function $Me(){return bAe(),new ann}function cT(e){return at(e.a),e.b}function RMe(e,n){this.b=e,this.a=n}function r$(e,n){this.d=e,this.e=n}function BMe(e,n){this.a=e,this.b=n}function zMe(e,n){this.a=e,this.b=n}function FMe(e,n){this.a=e,this.b=n}function JMe(e,n){this.a=e,this.b=n}function HMe(e,n){this.b=e,this.a=n}function f4(e,n){this.a=e,this.b=n}function c$(e,n){Ot.call(this,e,n)}function HX(e,n){Ot.call(this,e,n)}function GX(e,n){Ot.call(this,e,n)}function qX(e,n){Ot.call(this,e,n)}function UX(e,n){Ot.call(this,e,n)}function u$(e,n){Ot.call(this,e,n)}function o$(e){pn.call(this,e,21)}function GMe(e,n){this.b=e,this.a=n}function sse(e,n){this.b=e,this.a=n}function lse(e,n){this.b=e,this.a=n}function fse(e,n){Ot.call(this,e,n)}function XX(e,n){Ot.call(this,e,n)}function uT(e,n){Ot.call(this,e,n)}function ase(e,n){this.b=e,this.a=n}function v9(e,n){this.c=e,this.d=n}function s$(e,n){Ot.call(this,e,n)}function l$(e,n){Ot.call(this,e,n)}function qMe(e,n){this.e=e,this.d=n}function a4(e,n){Ot.call(this,e,n)}function UMe(e,n){this.a=e,this.b=n}function hse(e,n){Ot.call(this,e,n)}function br(e,n){Ot.call(this,e,n)}function f$(e,n){Ot.call(this,e,n)}function Ij(e,n,t){e.splice(n,0,t)}function Lgn(e,n,t){e.Mb(t)&&n.Ad(t)}function Pgn(e,n,t){n.Ne(e.a.We(t))}function $gn(e,n,t){n.Bd(e.a.Xe(t))}function Rgn(e,n,t){n.Ad(e.a.Kb(t))}function Bgn(e,n){return cs(e.c,n)}function zgn(e,n){return cs(e.e,n)}function XMe(e,n){this.a=e,this.b=n}function KMe(e,n){this.a=e,this.b=n}function VMe(e,n){this.a=e,this.b=n}function YMe(e,n){this.a=e,this.b=n}function QMe(e,n){this.a=e,this.b=n}function WMe(e,n){this.a=e,this.b=n}function ZMe(e,n){this.a=e,this.b=n}function eCe(e,n){this.a=e,this.b=n}function nCe(e,n){this.b=e,this.a=n}function tCe(e,n){this.b=e,this.a=n}function iCe(e,n){this.b=e,this.a=n}function rCe(e,n){this.b=n,this.c=e}function a$(e,n){Ot.call(this,e,n)}function oT(e,n){Ot.call(this,e,n)}function dse(e,n){Ot.call(this,e,n)}function _j(e,n){Ot.call(this,e,n)}function h$(e,n){Ot.call(this,e,n)}function KX(e,n){Ot.call(this,e,n)}function VX(e,n){Ot.call(this,e,n)}function Lj(e,n){Ot.call(this,e,n)}function Pj(e,n){Ot.call(this,e,n)}function bse(e,n){Ot.call(this,e,n)}function A3(e,n){Ot.call(this,e,n)}function YX(e,n){Ot.call(this,e,n)}function $j(e,n){Ot.call(this,e,n)}function gse(e,n){Ot.call(this,e,n)}function c2(e,n){Ot.call(this,e,n)}function QX(e,n){Ot.call(this,e,n)}function WX(e,n){Ot.call(this,e,n)}function ZX(e,n){Ot.call(this,e,n)}function wse(e,n){Ot.call(this,e,n)}function sT(e,n){Ot.call(this,e,n)}function pse(e,n){Ot.call(this,e,n)}function M3(e,n){Ot.call(this,e,n)}function eK(e,n){Ot.call(this,e,n)}function d$(e,n){Ot.call(this,e,n)}function lT(e,n){Ot.call(this,e,n)}function u2(e,n){Ot.call(this,e,n)}function b$(e,n){Ot.call(this,e,n)}function mse(e,n){Ot.call(this,e,n)}function nK(e,n){Ot.call(this,e,n)}function tK(e,n){Ot.call(this,e,n)}function iK(e,n){Ot.call(this,e,n)}function rK(e,n){Ot.call(this,e,n)}function cK(e,n){Ot.call(this,e,n)}function uK(e,n){Ot.call(this,e,n)}function g$(e,n){Ot.call(this,e,n)}function cCe(e,n){this.b=e,this.a=n}function vse(e,n){Ot.call(this,e,n)}function uCe(e,n){this.a=e,this.b=n}function oCe(e,n){this.a=e,this.b=n}function sCe(e,n){this.a=e,this.b=n}function yse(e,n){Ot.call(this,e,n)}function kse(e,n){Ot.call(this,e,n)}function lCe(e,n){this.a=e,this.b=n}function Fgn(e,n){return A9(),n!=e}function oK(e){return FTn(e,e.c),e}function Jgn(e){k.clearTimeout(e)}function jse(e,n){Ot.call(this,e,n)}function Ese(e,n){Ot.call(this,e,n)}function fCe(e,n){this.a=e,this.b=n}function aCe(e,n){this.a=e,this.b=n}function hCe(e,n){this.b=e,this.d=n}function dCe(e,n){this.a=e,this.b=n}function bCe(e,n){this.b=e,this.a=n}function w$(e,n){Ot.call(this,e,n)}function gw(e,n){Ot.call(this,e,n)}function sK(e,n){Ot.call(this,e,n)}function p$(e,n){Ot.call(this,e,n)}function Sse(e,n){Ot.call(this,e,n)}function gCe(e,n){this.b=e,this.a=n}function wCe(e,n){this.b=e,this.a=n}function pCe(e,n){this.b=e,this.a=n}function mCe(e,n){this.b=e,this.a=n}function xse(e,n){Ot.call(this,e,n)}function fT(e,n){Ot.call(this,e,n)}function Ase(e,n){Ot.call(this,e,n)}function lK(e,n){Ot.call(this,e,n)}function m$(e,n){Ot.call(this,e,n)}function fK(e,n){Ot.call(this,e,n)}function aK(e,n){Ot.call(this,e,n)}function v$(e,n){Ot.call(this,e,n)}function hK(e,n){Ot.call(this,e,n)}function Mse(e,n){Ot.call(this,e,n)}function dK(e,n){Ot.call(this,e,n)}function bK(e,n){Ot.call(this,e,n)}function aT(e,n){Ot.call(this,e,n)}function gK(e,n){Ot.call(this,e,n)}function Cse(e,n){Ot.call(this,e,n)}function hT(e,n){Ot.call(this,e,n)}function Tse(e,n){Ot.call(this,e,n)}function Ose(e,n){this.a=e,this.b=n}function vCe(e,n){this.a=e,this.b=n}function yCe(e,n){this.a=e,this.b=n}function kCe(){K$(),this.a=new Lle}function jCe(){$z(),this.a=new ar}function ECe(){qV(),this.b=new ar}function SCe(){jae(),Afe.call(this)}function xCe(){kae(),d_e.call(this)}function ACe(){kae(),d_e.call(this)}function dT(e,n){Ot.call(this,e,n)}function h4(e,n){Ot.call(this,e,n)}function Rj(e,n){Ot.call(this,e,n)}function Bj(e,n){Ot.call(this,e,n)}function bT(e,n){Ot.call(this,e,n)}function y$(e,n){Ot.call(this,e,n)}function wK(e,n){Ot.call(this,e,n)}function k$(e,n){Ot.call(this,e,n)}function zj(e,n){Ot.call(this,e,n)}function pK(e,n){Ot.call(this,e,n)}function j$(e,n){Ot.call(this,e,n)}function C3(e,n){Ot.call(this,e,n)}function gT(e,n){Ot.call(this,e,n)}function Fj(e,n){Ot.call(this,e,n)}function Jj(e,n){Ot.call(this,e,n)}function mK(e,n){Ot.call(this,e,n)}function wT(e,n){Ot.call(this,e,n)}function E$(e,n){Ot.call(this,e,n)}function T3(e,n){Ot.call(this,e,n)}function vK(e,n){Ot.call(this,e,n)}function yK(e,n){Ot.call(this,e,n)}function S$(e,n){Ot.call(this,e,n)}function je(e,n){this.a=e,this.b=n}function MCe(e,n){this.a=e,this.b=n}function CCe(e,n){this.a=e,this.b=n}function TCe(e,n){this.a=e,this.b=n}function OCe(e,n){this.a=e,this.b=n}function NCe(e,n){this.a=e,this.b=n}function DCe(e,n){this.a=e,this.b=n}function jc(e,n){this.a=e,this.b=n}function ICe(e,n){this.a=e,this.b=n}function _Ce(e,n){this.a=e,this.b=n}function LCe(e,n){this.a=e,this.b=n}function PCe(e,n){this.a=e,this.b=n}function $Ce(e,n){this.a=e,this.b=n}function RCe(e,n){this.a=e,this.b=n}function BCe(e,n){this.b=e,this.a=n}function zCe(e,n){this.b=e,this.a=n}function FCe(e,n){this.b=e,this.a=n}function JCe(e,n){this.b=e,this.a=n}function HCe(e,n){this.a=e,this.b=n}function GCe(e,n){this.a=e,this.b=n}function qCe(e,n){this.a=e,this.b=n}function UCe(e,n){this.a=e,this.b=n}function XCe(e,n){this.f=e,this.c=n}function Nse(e,n){this.i=e,this.g=n}function x$(e,n){Ot.call(this,e,n)}function d4(e,n){Ot.call(this,e,n)}function A$(e,n){this.a=e,this.b=n}function KCe(e,n){this.a=e,this.b=n}function Dse(e,n){this.d=e,this.e=n}function VCe(e,n){this.a=e,this.b=n}function YCe(e,n){this.a=e,this.b=n}function QCe(e,n){this.d=e,this.b=n}function WCe(e,n){this.e=e,this.a=n}function Ise(e,n){e.i=null,EB(e,n)}function Hgn(e,n){e&&ei(WD,e,n)}function ZCe(e,n){return SQ(e.a,n)}function _se(e,n){return cs(e.g,n)}function Ggn(e,n){return cs(n.b,e)}function qgn(e,n){return-e.b.$e(n)}function M$(e){return OO(e.c,e.b)}function Ugn(e,n){u8n(new ut(e),n)}function Xgn(e,n,t){KHe(n,mW(e,t))}function Kgn(e,n,t){KHe(n,mW(e,t))}function eTe(e,n){V9n(e.a,u(n,12))}function nTe(e,n){this.a=e,this.b=n}function pT(e,n){this.b=e,this.c=n}function j0(e,n){return e.Pd().Xb(n)}function C$(e,n){return v7n(e.Jc(),n)}function du(e){return e?e.kd():null}function ue(e){return e??null}function o2(e){return typeof e===ry}function s2(e){return typeof e===Lge}function $r(e){return typeof e===fZ}function Hj(e,n){return ao(e,n)==0}function T$(e,n){return ao(e,n)>=0}function Gj(e,n){return ao(e,n)!=0}function Lse(e,n){return e.a+=""+n,e}function Vgn(e){return""+(_n(e),e)}function tTe(e){return Ds(e),e.d.gc()}function Pse(e){return vn(e,0),null}function O$(e){return nE(e==null),e}function qj(e,n){return e.a+=""+n,e}function Bc(e,n){return e.a+=""+n,e}function Uj(e,n){return e.a+=""+n,e}function uo(e,n){return e.a+=""+n,e}function Kt(e,n){return e.a+=""+n,e}function iTe(e,n){e.q.setTime(Xb(n))}function rTe(e,n){Ife.call(this,e,n)}function cTe(e,n){Ife.call(this,e,n)}function N$(e,n){Ife.call(this,e,n)}function gc(e,n){Xi(e,n,e.c.b,e.c)}function O3(e,n){Xi(e,n,e.a,e.a.a)}function Ygn(e,n){return e.j[n.p]==2}function uTe(e,n){return e.a=n.g+1,e}function la(e){return e.a=0,e.b=0,e}function oTe(e){Hu(this),xE(this,e)}function sTe(){this.b=0,this.a=!1}function lTe(){this.b=0,this.a=!1}function fTe(){this.b=new l4(I2(12))}function aTe(){aTe=Y,ttn=It(DQ())}function hTe(){hTe=Y,fin=It(FUe())}function dTe(){dTe=Y,tsn=It(oze())}function $se(){$se=Y,foe(),kme=new pt}function Qgn(e){return Nt(e),new Xj(e)}function bTe(e,n){return ue(e)===ue(n)}function D$(e){return e<10?"0"+e:""+e}function gTe(e){return _o(e.l,e.m,e.h)}function uu(e){return typeof e===Lge}function kK(e,n){return of(e.a,0,n)}function b4(e){return sc((_n(e),e))}function Wgn(e){return sc((_n(e),e))}function Zgn(e,n){return ki(e.a,n.a)}function Rse(e,n){return oo(e.a,n.a)}function ewn(e,n){return iIe(e.a,n.a)}function lh(e,n){return e.indexOf(n)}function Bse(e,n){J9(e,0,e.length,n)}function ti(e,n){n$(),ei(kG,e,n)}function fn(e,n){Li.call(this,e,n)}function jK(e,n){b2.call(this,e,n)}function N3(e,n){Nse.call(this,e,n)}function wTe(e,n){jT.call(this,e,n)}function EK(e,n){Y9.call(this,e,n)}function zh(){Uue.call(this,new O0)}function pTe(){fR.call(this,0,0,0,0)}function zse(e){return wu(e.b.b,e,0)}function mTe(e,n){return oo(e.g,n.g)}function nwn(e){return e==op||e==sm}function twn(e){return e==op||e==om}function iwn(e,n){return oo(e.g,n.g)}function rwn(e,n){return rl(),n.a+=e}function cwn(e,n){return rl(),n.a+=e}function uwn(e,n){return rl(),n.c+=e}function own(e,n){return Ce(e.c,n),e}function vTe(e,n){return Ce(e.a,n),n}function Fse(e,n){return fl(e.a,n),e}function yTe(e){this.a=$Me(),this.b=e}function kTe(e){this.a=$Me(),this.b=e}function wc(e){this.a=e.a,this.b=e.b}function Xj(e){this.a=e,wC.call(this)}function jTe(e){this.a=e,wC.call(this)}function Fs(e){return e.sh()&&e.th()}function D3(e){return e!=nh&&e!=bb}function x1(e){return e==Zc||e==ru}function I3(e){return e==Vl||e==Za}function ETe(e){return e==Fv||e==zv}function I$(e){return fl(new or,e)}function STe(e){return OV(u(e,125))}function swn(e,n){return ki(n.f,e.f)}function xTe(e,n){return new Y9(n,e)}function lwn(e,n){return new Y9(n,e)}function Dl(e,n,t){Os(e,n),Ns(e,t)}function SK(e,n,t){bB(e,n),gB(e,t)}function ww(e,n,t){Iw(e,n),Dw(e,t)}function mT(e,n,t){X3(e,n),K3(e,t)}function vT(e,n,t){V3(e,n),Y3(e,t)}function xK(e,n){i8(e,n),U9(e,e.D)}function AK(e){XCe.call(this,e,!0)}function g4(){Lf.call(this,0,0,0,0)}function ATe(){c$.call(this,"Head",1)}function MTe(){c$.call(this,"Tail",3)}function CTe(e,n,t){Sle.call(this,e,n,t)}function pw(e){fR.call(this,e,e,e,e)}function E0(e){mh(),x7n.call(this,e)}function TTe(e){Ao(e.Qf(),new Qke(e))}function _3(e){return e!=null?Oi(e):0}function fwn(e,n){return T2(n,Ia(e))}function awn(e,n){return T2(n,Ia(e))}function hwn(e,n){return e[e.length]=n}function dwn(e,n){return e[e.length]=n}function bwn(e,n){return yB(xV(e.f),n)}function gwn(e,n){return yB(xV(e.n),n)}function wwn(e,n){return yB(xV(e.p),n)}function Jse(e){return S3n(e.b.Jc(),e.a)}function pwn(e){return e==null?0:Oi(e)}function MK(e){e.c=oe(Ar,On,1,0,5,1)}function OTe(e,n,t){tr(e.c[n.g],n.g,t)}function mwn(e,n,t){u(e.c,72).Ei(n,t)}function vwn(e,n,t){Dl(t,t.i+e,t.j+n)}function Vr(e,n){Li.call(this,e.b,n)}function ywn(e,n){Et(Vu(e.a),oLe(n))}function kwn(e,n){Et(Ts(e.a),sLe(n))}function jwn(e,n){Ka||(e.b=n)}function CK(e,n,t){return tr(e,n,t),t}function Lt(){Lt=Y,new NTe,new Te}function NTe(){new pt,new pt,new pt}function Ewn(){throw $(new gd(Pen))}function Swn(){throw $(new gd(Pen))}function xwn(){throw $(new gd($en))}function Awn(){throw $(new gd($en))}function DTe(){DTe=Y,fre=new zE(Ace)}function Oa(){Oa=Y,k.Math.log(2)}function Il(){Il=Y,h1=(NMe(),Aan)}function Kj(e){fi(),aw.call(this,e)}function ITe(e){this.a=e,rfe.call(this,e)}function TK(e){this.a=e,VP.call(this,e)}function OK(e){this.a=e,VP.call(this,e)}function Tr(e,n){uV(e.c,e.c.length,n)}function bu(e){return e.an?1:0}function Gse(e,n){return ao(e,n)>0?e:n}function _o(e,n,t){return{l:e,m:n,h:t}}function Mwn(e,n){e.a!=null&&eTe(n,e.a)}function Cwn(e){lc(e,null),Gr(e,null)}function Twn(e,n,t){return ei(e.g,t,n)}function Own(e,n){Nt(n),z3(e).Ic(new Pe)}function LTe(){Vde(),this.a=new tS(p3e)}function _$(e){this.b=e,this.a=new Te}function PTe(e){this.b=new o6,this.a=e}function qse(e){Ple.call(this),this.a=e}function $Te(e){hae.call(this),this.b=e}function RTe(){c$.call(this,"Range",2)}function L$(e){e.j=oe(_me,Se,324,0,0,1)}function BTe(e){e.a=new Bt,e.c=new Bt}function zTe(e){e.a=new pt,e.e=new pt}function Use(e){return new je(e.c,e.d)}function Nwn(e){return new je(e.c,e.d)}function pc(e){return new je(e.a,e.b)}function Dwn(e,n){return ei(e.a,n.a,n)}function Iwn(e,n,t){return ei(e.k,t,n)}function L3(e,n,t){return gde(n,t,e.c)}function Xse(e,n){return re(Bn(e.i,n))}function Kse(e,n){return re(Bn(e.j,n))}function FTe(e,n){return d$n(e.a,n,null)}function Vj(e,n){return EPn(e.c,e.b,n)}function X(e,n){return e!=null&&PQ(e,n)}function JTe(e,n){kt(e),e.Fc(u(n,16))}function _wn(e,n,t){e.c._c(n,u(t,136))}function Lwn(e,n,t){e.c.Si(n,u(t,136))}function Pwn(e,n,t){return a$n(e,n,t),t}function $wn(e,n){return cl(),n.n.b+=e}function NK(e,n){return ekn(e.Jc(),n)!=-1}function Rwn(e,n){return new wOe(e.Jc(),n)}function P$(e){return e.Ob()?e.Pb():null}function HTe(e){return gh(e,0,e.length)}function GTe(e){XV(e,null),KV(e,null)}function qTe(){jT.call(this,null,null)}function UTe(){J$.call(this,null,null)}function XTe(){Ot.call(this,"INSTANCE",0)}function P3(){this.a=oe(Ar,On,1,8,5,1)}function Vse(e){this.a=e,pt.call(this)}function KTe(e){this.a=(jn(),new f9(e))}function Bwn(e){this.b=(jn(),new fX(e))}function y9(){y9=Y,Gme=new SX(null)}function Yse(){Yse=Y,Yse(),bnn=new At}function Ce(e,n){return Hn(e.c,n),!0}function VTe(e,n){e.c&&(pfe(n),x_e(n))}function zwn(e,n){e.q.setHours(n),oS(e,n)}function Qse(e,n){return e.a.Ac(n)!=null}function DK(e,n){return e.a.Ac(n)!=null}function Na(e,n){return e.a[n.c.p][n.p]}function Fwn(e,n){return e.e[n.c.p][n.p]}function Jwn(e,n){return e.c[n.c.p][n.p]}function IK(e,n,t){return e.a[n.g][t.g]}function Hwn(e,n){return e.j[n.p]=VOn(n)}function w4(e,n){return e.a*n.a+e.b*n.b}function Gwn(e,n){return e.a=e}function Vwn(e,n,t){return t?n!=0:n!=e-1}function YTe(e,n,t){e.a=n^1502,e.b=t^FZ}function Ywn(e,n,t){return e.a=n,e.b=t,e}function A1(e,n){return e.a*=n,e.b*=n,e}function Yj(e,n,t){return tr(e.g,n,t),t}function Qwn(e,n,t,i){tr(e.a[n.g],t.g,i)}function mr(e,n,t){_T.call(this,e,n,t)}function $$(e,n,t){mr.call(this,e,n,t)}function rs(e,n,t){mr.call(this,e,n,t)}function QTe(e,n,t){$$.call(this,e,n,t)}function Wse(e,n,t){_T.call(this,e,n,t)}function $3(e,n,t){_T.call(this,e,n,t)}function WTe(e,n,t){Z$.call(this,e,n,t)}function Zse(e,n,t){Z$.call(this,e,n,t)}function ZTe(e,n,t){Zse.call(this,e,n,t)}function eOe(e,n,t){Wse.call(this,e,n,t)}function S0(e){this.c=e,this.a=this.c.a}function ut(e){this.i=e,this.f=this.i.j}function R3(e,n){this.a=e,VP.call(this,n)}function nOe(e,n){this.a=e,TX.call(this,n)}function tOe(e,n){this.a=e,TX.call(this,n)}function iOe(e,n){this.a=e,TX.call(this,n)}function ele(e){this.a=e,HU.call(this,e.d)}function rOe(e){e.b.Qb(),--e.d.f.d,hR(e.d)}function cOe(e){e.a=u(Un(e.b.a,4),129)}function uOe(e){e.a=u(Un(e.b.a,4),129)}function Wwn(e){FT(e,lZe),Dz(e,sRn(e))}function nle(e,n){return Tjn(e,new p0,n).a}function Zwn(e){return YC(e.a)?uLe(e):null}function oOe(e){y3.call(this,u(Nt(e),35))}function sOe(e){y3.call(this,u(Nt(e),35))}function tle(e){if(!e)throw $(new KC)}function ile(e){if(!e)throw $(new is)}function Vn(e,n){return Nt(n),new gOe(e,n)}function lOe(e,n){return new WGe(e.a,e.b,n)}function epn(e){return e.l+e.m*sy+e.h*sg}function npn(e){return e==null?null:e.name}function rle(e,n,t){return e.indexOf(n,t)}function R$(e,n){return e.lastIndexOf(n)}function Qj(e){return e==null?Vo:su(e)}function Pn(){Pn=Y,eb=!1,a7=!0}function fOe(){fOe=Y,BX(),nhn=new zU}function cle(){this.Bb|=256,this.Bb|=512}function aOe(){L$(this),MR(this),this.he()}function B$(e){Hr.call(this,e),this.a=e}function ule(e){tc.call(this,e),this.a=e}function ole(e){f9.call(this,e),this.a=e}function cf(e){Di.call(this,(_n(e),e))}function il(e){Di.call(this,(_n(e),e))}function _K(e){Uue.call(this,new lhe(e))}function hOe(e){this.a=e,Ui.call(this,e)}function sle(e,n){this.a=n,TX.call(this,e)}function dOe(e,n){this.a=n,cY.call(this,e)}function bOe(e,n){this.a=e,cY.call(this,n)}function gOe(e,n){this.a=n,KP.call(this,e)}function wOe(e,n){this.a=n,KP.call(this,e)}function lle(e){wX.call(this),fc(this,e)}function Js(e){return at(e.a!=null),e.a}function pOe(e,n){return Ce(n.a,e.a),e.a}function mOe(e,n){return Ce(n.b,e.a),e.a}function mw(e,n){return Ce(n.a,e.a),e.a}function yT(e,n,t){return qY(e,n,n,t),e}function z$(e,n){return++e.b,Ce(e.a,n)}function fle(e,n){return++e.b,qo(e.a,n)}function tpn(e,n){return ki(e.c.d,n.c.d)}function ipn(e,n){return ki(e.c.c,n.c.c)}function rpn(e,n){return ki(e.n.a,n.n.a)}function Ho(e,n){return u(pi(e.b,n),16)}function cpn(e,n){return e.n.b=(_n(n),n)}function upn(e,n){return e.n.b=(_n(n),n)}function cs(e,n){return!!n&&e.b[n.g]==n}function Wj(e){return bu(e.a)||bu(e.b)}function opn(e,n){return ki(e.e.b,n.e.b)}function spn(e,n){return ki(e.e.a,n.e.a)}function lpn(e,n,t){return iPe(e,n,t,e.b)}function ale(e,n,t){return iPe(e,n,t,e.c)}function fpn(e){return rl(),!!e&&!e.dc()}function vOe(){Mj(),this.b=new Ije(this)}function F$(){F$=Y,wJ=new Li(QYe,0)}function p4(e){this.d=e,ut.call(this,e)}function m4(e){this.c=e,ut.call(this,e)}function kT(e){this.c=e,p4.call(this,e)}function hle(e,n){Sde.call(this,e,n,null)}function v4(e){return e.a!=null?e.a:null}function vw(e){return e.$H||(e.$H=++NBn)}function jd(e){var n;n=e.a,e.a=e.b,e.b=n}function jT(e,n){Nj(),this.a=e,this.b=n}function J$(e,n){kd(),this.b=e,this.c=n}function H$(e,n){lV(),this.f=n,this.d=e}function dle(e,n){Zae(n,e),this.c=e,this.b=n}function apn(e,n){return hV(e.c).Kd().Xb(n)}function LK(e,n){return new yNe(e,e.gc(),n)}function hpn(e){return BP(),Dt((eLe(),qen),e)}function dpn(e){return new A2(3,e)}function Fh(e){return ll(e,Q2),new xo(e)}function yOe(e){return $9(),parseInt(e)||-1}function k9(e,n,t){return rle(e,Xo(n),t)}function ble(e,n,t){u(cO(e,n),22).Ec(t)}function bpn(e,n,t){gQ(e.a,t),lz(e.a,n)}function j9(e,n,t){var i;i=e.dd(n),i.Rb(t)}function kOe(e,n,t,i){Nfe.call(this,e,n,t,i)}function jOe(e){ufe.call(this,e,null,null)}function PK(e){i2(),this.b=e,this.a=!0}function EOe(e){YP(),this.b=e,this.a=!0}function SOe(e){if(!e)throw $(new Nl)}function gle(e){if(!e)throw $(new KC)}function gpn(e){if(!e)throw $(new bX)}function at(e){if(!e)throw $(new au)}function l2(e){if(!e)throw $(new is)}function xOe(e){e.d=new jOe(e),e.e=new pt}function E9(e){return at(e.b!=0),e.a.a.c}function If(e){return at(e.b!=0),e.c.b.c}function wpn(e,n){return qY(e,n,n+1,""),e}function AOe(e){sZ(),KSe(this),this.Df(e)}function MOe(e){this.c=e,this.a=1,this.b=1}function ET(e){X(e,161)&&u(e,161).mi()}function COe(e){return e.b=u(oae(e.a),45)}function f2(e,n){return u(Pa(e.a,n),35)}function bi(e,n){return!!e.q&&so(e.q,n)}function ppn(e,n){return e>0?n/(e*e):n*100}function mpn(e,n){return e>0?n*n/e:n*n*100}function vpn(e){return e.f!=null?e.f:""+e.g}function $K(e){return e.f!=null?e.f:""+e.g}function ypn(e){return P1(),e.e.a+e.f.a/2}function kpn(e){return P1(),e.e.b+e.f.b/2}function jpn(e,n,t){return P1(),t.e.b-e*n}function Epn(e,n,t){return P1(),t.e.a-e*n}function Spn(e,n,t){return WP(),t.Lg(e,n)}function xpn(e,n){return F0(),gn(e,n.e,n)}function Apn(e,n,t){return Ce(n,WFe(e,t))}function Mpn(e,n,t){tB(),e.nf(n)&&t.Ad(e)}function a2(e,n,t){return e.a+=n,e.b+=t,e}function TOe(e,n,t){return e.a-=n,e.b-=t,e}function wle(e,n){return e.a=n.a,e.b=n.b,e}function G$(e){return e.a=-e.a,e.b=-e.b,e}function OOe(e){this.c=e,Os(e,0),Ns(e,0)}function NOe(e){Si.call(this),SE(this,e)}function DOe(){Ot.call(this,"GROW_TREE",0)}function Hs(e,n,t){os.call(this,e,n,t,2)}function IOe(e,n){kd(),ple.call(this,e,n)}function ple(e,n){kd(),J$.call(this,e,n)}function _Oe(e,n){kd(),J$.call(this,e,n)}function LOe(e,n){Nj(),jT.call(this,e,n)}function RK(e,n){Il(),sR.call(this,e,n)}function POe(e,n){Il(),RK.call(this,e,n)}function mle(e,n){Il(),RK.call(this,e,n)}function $Oe(e,n){Il(),mle.call(this,e,n)}function vle(e,n){Il(),sR.call(this,e,n)}function ROe(e,n){Il(),vle.call(this,e,n)}function BOe(e,n){Il(),sR.call(this,e,n)}function Cpn(e,n){return e.c.Ec(u(n,136))}function Tpn(e,n){return u(Bn(e.e,n),26)}function Opn(e,n){return u(Bn(e.e,n),26)}function yle(e,n,t){return Kz(oO(e,n),t)}function Npn(e,n,t){return n.xl(e.e,e.c,t)}function Dpn(e,n,t){return n.yl(e.e,e.c,t)}function BK(e,n){return $0(e.e,u(n,52))}function Ipn(e,n,t){$E(Vu(e.a),n,oLe(t))}function _pn(e,n,t){$E(Ts(e.a),n,sLe(t))}function zOe(e,n){return _n(e),e+qK(n)}function Lpn(e){return e==null?null:su(e)}function Ppn(e){return e==null?null:su(e)}function $pn(e){return e==null?null:cCn(e)}function Rpn(e){return e==null?null:Z$n(e)}function M1(e){e.o==null&&SOn(e)}function $e(e){return nE(e==null||o2(e)),e}function re(e){return nE(e==null||s2(e)),e}function Pt(e){return nE(e==null||$r(e)),e}function Bpn(e,n){return GQ(e,n),new NIe(e,n)}function ST(e,n){this.c=e,g9.call(this,e,n)}function Zj(e,n){this.a=e,ST.call(this,e,n)}function zpn(e,n){this.d=e,en(this),this.b=n}function kle(){yBe.call(this),this.Bb|=Ec}function FOe(){this.a=new Cw,this.b=new Cw}function jle(e){this.q=new k.Date(Xb(e))}function B3(){B3=Y,Gv=new yi("root")}function S9(){S9=Y,eI=new Sxe,new xxe}function h2(){h2=Y,Qme=nn((Vs(),Og))}function Fpn(e,n){n.a?qTn(e,n):DK(e.a,n.b)}function JOe(e,n){Ka||Ce(e.a,n)}function Jpn(e,n){return iT(),V9(n.d.i,e)}function Hpn(e,n){return z4(),new $Xe(n,e)}function Gpn(e,n,t){return e.Le(n,t)<=0?t:n}function qpn(e,n,t){return e.Le(n,t)<=0?n:t}function Upn(e,n){return u(Pa(e.b,n),144)}function Xpn(e,n){return u(Pa(e.c,n),233)}function zK(e){return u(Le(e.a,e.b),295)}function HOe(e){return new je(e.c,e.d+e.a)}function GOe(e){return _n(e),e?1231:1237}function qOe(e){return cl(),ETe(u(e,203))}function Ele(e,n){return u(Bn(e.b,n),278)}function UOe(e,n,t){++e.j,e.oj(n,e.Xi(n,t))}function xT(e,n,t){++e.j,e.rj(),bY(e,n,t)}function Sle(e,n,t){ZR.call(this,e,n,t,null)}function XOe(e,n,t){ZR.call(this,e,n,t,null)}function xle(e,n){gY.call(this,e),this.a=n}function Ale(e,n){gY.call(this,e),this.a=n}function Li(e,n){yi.call(this,e),this.a=n}function Mle(e,n){coe.call(this,e),this.a=n}function FK(e,n){coe.call(this,e),this.a=n}function KOe(e,n){this.c=e,Nw.call(this,n)}function VOe(e,n){this.a=e,BSe.call(this,n)}function AT(e,n){this.a=e,BSe.call(this,n)}function Cle(e,n,t){return t=dl(e,n,3,t),t}function Tle(e,n,t){return t=dl(e,n,6,t),t}function Ole(e,n,t){return t=dl(e,n,9,t),t}function fh(e,n){return FT(n,ewe),e.f=n,e}function Nle(e,n){return(n&oi)%e.d.length}function YOe(e,n,t){return bge(e.c,e.b,n,t)}function Kpn(e,n,t){return e.apply(n,t)}function QOe(e,n,t){var i;i=e.dd(n),i.Rb(t)}function WOe(e,n,t){return e.a+=gh(n,0,t),e}function MT(e){return!e.a&&(e.a=new Tn),e.a}function Dle(e,n){var t;return t=e.e,e.e=n,t}function Ile(e,n){var t;return t=n,!!e.De(t)}function Lb(e,n){return Pn(),e==n?0:e?1:-1}function d2(e,n){e.a._c(e.b,n),++e.b,e.c=-1}function Vpn(e,n){var t;t=e[zZ],t.call(e,n)}function Ypn(e,n){var t;t=e[zZ],t.call(e,n)}function Qpn(e,n,t){Ib(),jP(e,n.Te(e.a,t))}function _le(e,n,t){return M4(e,u(n,23),t)}function _f(e,n){return HP(new Array(n),e)}function Wpn(e){return Rt(Bb(e,32))^Rt(e)}function JK(e){return String.fromCharCode(e)}function Zpn(e){return e==null?null:e.message}function HK(e){this.a=(jn(),new In(Nt(e)))}function ZOe(e){this.a=(ll(e,Q2),new xo(e))}function eNe(e){this.a=(ll(e,Q2),new xo(e))}function nNe(){this.a=new Te,this.b=new Te}function tNe(){this.a=new Zm,this.b=new nxe}function Lle(){this.b=new O0,this.a=new O0}function iNe(){this.b=new Kr,this.c=new Te}function Ple(){this.n=new Kr,this.o=new Kr}function q$(){this.n=new t4,this.i=new g4}function rNe(){this.b=new ar,this.a=new ar}function cNe(){this.a=new Te,this.d=new Te}function uNe(){this.a=new NU,this.b=new c_}function oNe(){this.b=new _Ae,this.a=new vM}function sNe(){this.b=new pt,this.a=new pt}function lNe(){q$.call(this),this.a=new Kr}function $le(e,n,t,i){fR.call(this,e,n,t,i)}function e2n(e,n){return e.n.a=(_n(n),n+10)}function n2n(e,n){return e.n.a=(_n(n),n+10)}function t2n(e,n){return iT(),!V9(n.d.i,e)}function fNe(e){Hu(e.e),e.d.b=e.d,e.d.a=e.d}function CT(e){e.b?CT(e.b):e.f.c.yc(e.e,e.d)}function i2n(e,n){x1(e.f)?wOn(e,n):sMn(e,n)}function aNe(e,n,t){t!=null&&kB(n,XQ(e,t))}function hNe(e,n,t){t!=null&&jB(n,XQ(e,t))}function y4(e,n,t,i){pe.call(this,e,n,t,i)}function Rle(e,n,t,i){pe.call(this,e,n,t,i)}function dNe(e,n,t,i){Rle.call(this,e,n,t,i)}function bNe(e,n,t,i){mR.call(this,e,n,t,i)}function GK(e,n,t,i){mR.call(this,e,n,t,i)}function gNe(e,n,t,i){GK.call(this,e,n,t,i)}function Ble(e,n,t,i){mR.call(this,e,n,t,i)}function Nn(e,n,t,i){Ble.call(this,e,n,t,i)}function zle(e,n,t,i){GK.call(this,e,n,t,i)}function wNe(e,n,t,i){zle.call(this,e,n,t,i)}function pNe(e,n,t,i){Lfe.call(this,e,n,t,i)}function b2(e,n){jo.call(this,$S+e+dg+n)}function r2n(e,n){return n==e||m8(Nz(n),e)}function Fle(e,n){return e.hk().ti().oi(e,n)}function Jle(e,n){return e.hk().ti().qi(e,n)}function c2n(e,n){return e.e=u(e.d.Kb(n),162)}function mNe(e,n){return ei(e.a,n,"")==null}function vNe(e,n){return _n(e),ue(e)===ue(n)}function bn(e,n){return _n(e),ue(e)===ue(n)}function Hle(e,n,t){return e.lastIndexOf(n,t)}function yNe(e,n,t){this.a=e,dle.call(this,n,t)}function kNe(e){this.c=e,N$.call(this,hN,0)}function jNe(e,n,t){this.c=n,this.b=t,this.a=e}function gi(e,n){return e.a+=n.a,e.b+=n.b,e}function Nr(e,n){return e.a-=n.a,e.b-=n.b,e}function u2n(e){return Qp(e.j.c,0),e.a=-1,e}function o2n(e,n){var t;return t=n.ni(e.a),t}function Gle(e,n,t){return t=dl(e,n,11,t),t}function s2n(e,n,t){return ki(e[n.a],e[t.a])}function l2n(e,n){return oo(e.a.d.p,n.a.d.p)}function f2n(e,n){return oo(n.a.d.p,e.a.d.p)}function a2n(e,n){return ki(e.c-e.s,n.c-n.s)}function h2n(e,n){return ki(e.b.e.a,n.b.e.a)}function d2n(e,n){return ki(e.c.e.a,n.c.e.a)}function b2n(e,n){return he(n,(Ne(),fD),e)}function g2n(e,n){return e.b.zd(new zMe(e,n))}function w2n(e,n){return e.b.zd(new FMe(e,n))}function ENe(e,n){return e.b.zd(new JMe(e,n))}function SNe(e,n){return X(n,16)&&pXe(e.c,n)}function xNe(e){return e.c?wu(e.c.a,e,0):-1}function p2n(e){return e<100?null:new m0(e)}function k4(e){return e==Tg||e==f1||e==to}function m2n(e,n,t){return u(e.c,72).Uk(n,t)}function U$(e,n,t){return u(e.c,72).Vk(n,t)}function v2n(e,n,t){return Npn(e,u(n,344),t)}function qle(e,n,t){return Dpn(e,u(n,344),t)}function y2n(e,n,t){return rGe(e,u(n,344),t)}function ANe(e,n,t){return yMn(e,u(n,344),t)}function eE(e,n){return n==null?null:L2(e.b,n)}function k2n(e,n){Ka||n&&(e.d=n)}function Ule(e,n){if(!e)throw $(new Gn(n))}function x9(e){if(!e)throw $(new Uc(Pge))}function qK(e){return s2(e)?(_n(e),e):e.se()}function X$(e){return!isNaN(e)&&!isFinite(e)}function UK(e){BTe(this),qs(this),fc(this,e)}function bs(e){MK(this),sfe(this.c,0,e.Nc())}function TT(e){A9(),this.d=e,this.a=new P3}function MNe(e,n,t){this.d=e,this.b=t,this.a=n}function _l(e,n,t){this.a=e,this.b=n,this.c=t}function CNe(e,n,t){this.a=e,this.b=n,this.c=t}function Xle(e,n){this.c=e,vV.call(this,e,n)}function TNe(e,n){C3n.call(this,e,e.length,n)}function XK(e,n){if(e!=n)throw $(new Nl)}function ONe(e){this.a=e,yd(),Pu(Date.now())}function NNe(e){As(e.a),uhe(e.c,e.b),e.b=null}function KK(){KK=Y,Hme=new hi,hnn=new Gt}function VK(e){var n;return n=new l5,n.e=e,n}function j2n(e,n,t){return Ib(),e.a.Wd(n,t),n}function Kle(e,n,t){this.b=e,this.c=n,this.a=t}function Vle(e){var n;return n=new oxe,n.b=e,n}function E2n(e){return ga(),Dt((M$e(),Tnn),e)}function S2n(e){return H9(),Dt((F$e(),gnn),e)}function x2n(e){return zl(),Dt((A$e(),knn),e)}function A2n(e){return ws(),Dt((C$e(),Nnn),e)}function M2n(e){return Uo(),Dt((T$e(),Inn),e)}function C2n(e){return Zz(),Dt((aTe(),ttn),e)}function T2n(e){return Lw(),Dt((U$e(),rtn),e)}function O2n(e){return Z9(),Dt((X$e(),Ktn),e)}function N2n(e){return lB(),Dt((IPe(),dtn),e)}function D2n(e){return yE(),Dt((x$e(),Btn),e)}function I2n(e){return zr(),Dt((LRe(),Htn),e)}function _2n(e){return X4(),Dt((q$e(),ein),e)}function L2n(e){return zn(),Dt((ize(),rin),e)}function P2n(e){return K9(),Dt((_Pe(),lin),e)}function YK(e){fR.call(this,e.d,e.c,e.a,e.b)}function Yle(e){fR.call(this,e.d,e.c,e.a,e.b)}function $2n(e){return Ur(),Dt((hTe(),fin),e)}function DNe(){DNe=Y,Nan=oe(Ar,On,1,0,5,1)}function INe(){INe=Y,Van=oe(Ar,On,1,0,5,1)}function Qle(){Qle=Y,Yan=oe(Ar,On,1,0,5,1)}function OT(){OT=Y,jJ=new lq,EJ=new $A}function K$(){K$=Y,gin=new Cq,bin=new Tq}function rl(){rl=Y,yin=new gk,kin=new od}function R2n(e){return _w(),Dt((l$e(),Nin),e)}function B2n(e){return Ff(),Dt((Q$e(),Sin),e)}function z2n(e){return z2(),Dt((TRe(),Ain),e)}function F2n(e){return Bz(),Dt((uze(),Din),e)}function J2n(e){return Q4(),Dt((lBe(),Iin),e)}function H2n(e){return nB(),Dt((wPe(),_in),e)}function G2n(e){return BE(),Dt((Z$e(),Lin),e)}function q2n(e){return pB(),Dt((r$e(),Pin),e)}function U2n(e){return KO(),Dt((hze(),$in),e)}function X2n(e){return fO(),Dt((pPe(),Rin),e)}function K2n(e){return Wb(),Dt((c$e(),zin),e)}function V2n(e){return Sz(),Dt((sBe(),Fin),e)}function Y2n(e){return rO(),Dt((mPe(),Jin),e)}function Q2n(e){return zO(),Dt((uBe(),Hin),e)}function W2n(e){return y8(),Dt((oBe(),Gin),e)}function Z2n(e){return Dc(),Dt((Tze(),qin),e)}function emn(e){return W9(),Dt((u$e(),Uin),e)}function nmn(e){return _0(),Dt((o$e(),Xin),e)}function tmn(e){return _1(),Dt((s$e(),Vin),e)}function imn(e){return JR(),Dt((vPe(),Yin),e)}function rmn(e){return Xs(),Dt((NRe(),Win),e)}function cmn(e){return qR(),Dt((yPe(),Zin),e)}function umn(e){return YO(),Dt((dze(),zun),e)}function omn(e){return IE(),Dt((f$e(),Fun),e)}function smn(e){return B2(),Dt((V$e(),Jun),e)}function lmn(e){return HE(),Dt((ORe(),Hun),e)}function fmn(e){return G0(),Dt((Cze(),Gun),e)}function amn(e){return F1(),Dt((Y$e(),qun),e)}function hmn(e){return uO(),Dt((kPe(),Uun),e)}function dmn(e){return Nc(),Dt((a$e(),Kun),e)}function bmn(e){return DB(),Dt((h$e(),Vun),e)}function gmn(e){return DE(),Dt((d$e(),Yun),e)}function wmn(e){return r8(),Dt((b$e(),Qun),e)}function pmn(e){return wB(),Dt((g$e(),Wun),e)}function mmn(e){return IB(),Dt((w$e(),Zun),e)}function vmn(e){return LB(),Dt((G$e(),din),e)}function ymn(e){return eg(),Dt((K$e(),mon),e)}function kmn(e,n){return _n(e),e+(_n(n),n)}function jmn(e){return mE(),Dt((jPe(),Eon),e)}function Emn(e){return ah(),Dt((SPe(),Oon),e)}function Smn(e){return Da(),Dt((EPe(),Don),e)}function xmn(e){return ha(),Dt((xPe(),Xon),e)}function A9(){A9=Y,nye=(De(),Kn),OH=et}function Amn(e){return Tw(),Dt((APe(),esn),e)}function Mmn(e){return Y4(),Dt((tRe(),nsn),e)}function Cmn(e){return cS(),Dt((dTe(),tsn),e)}function Tmn(e){return NE(),Dt((p$e(),isn),e)}function Omn(e){return OE(),Dt((W$e(),Asn),e)}function Nmn(e){return FR(),Dt((MPe(),Msn),e)}function Dmn(e){return SB(),Dt((CPe(),Dsn),e)}function Imn(e){return vz(),Dt((DRe(),_sn),e)}function _mn(e){return iB(),Dt((TPe(),Lsn),e)}function Lmn(e){return EO(),Dt((m$e(),Psn),e)}function Pmn(e){return az(),Dt((nRe(),tln),e)}function $mn(e){return NB(),Dt((v$e(),iln),e)}function Rmn(e){return WB(),Dt((y$e(),rln),e)}function Bmn(e){return kz(),Dt((eRe(),uln),e)}function zmn(e){return XB(),Dt((S$e(),lln),e)}function Fmn(e){return!e.e&&(e.e=new Te),e.e}function V$(e,n,t){this.e=n,this.b=e,this.d=t}function _Ne(e,n,t){this.a=e,this.b=n,this.c=t}function LNe(e,n,t){this.a=e,this.b=n,this.c=t}function Wle(e,n,t){this.a=e,this.b=n,this.c=t}function PNe(e,n,t){this.a=e,this.b=n,this.c=t}function $Ne(e,n,t){this.a=e,this.c=n,this.b=t}function Y$(e,n,t){this.b=e,this.a=n,this.c=t}function RNe(e,n,t){this.b=e,this.a=n,this.c=t}function QK(e,n){this.c=e,this.a=n,this.b=n-e}function Jmn(e){return JB(),Dt((j$e(),Iln),e)}function Hmn(e){return e$(),Dt((XLe(),Bln),e)}function Gmn(e){return WT(),Dt((NPe(),zln),e)}function qmn(e){return JO(),Dt((_Re(),Fln),e)}function Umn(e){return ZP(),Dt((ULe(),$ln),e)}function Xmn(e){return nS(),Dt((IRe(),Lln),e)}function Kmn(e){return CO(),Dt((E$e(),Pln),e)}function Vmn(e){return VR(),Dt((OPe(),Nln),e)}function Ymn(e){return rB(),Dt((k$e(),Dln),e)}function Qmn(e){return Cj(),Dt((KLe(),ifn),e)}function Wmn(e){return pO(),Dt((DPe(),rfn),e)}function Zmn(e){return ph(),Dt(($Re(),ffn),e)}function e3n(e){return cg(),Dt((rze(),hfn),e)}function n3n(e){return z1(),Dt((rRe(),Kfn),e)}function t3n(e){return vr(),Dt((PRe(),qfn),e)}function i3n(e){return u8(),Dt((iRe(),Ufn),e)}function r3n(e){return $a(),Dt((O$e(),Xfn),e)}function c3n(e){return Vh(),Dt((tBe(),dfn),e)}function u3n(e){return rg(),Dt((iBe(),vfn),e)}function o3n(e){return G2(),Dt((gze(),ean),e)}function s3n(e){return nv(),Dt((RRe(),nan),e)}function l3n(e){return Br(),Dt((cBe(),tan),e)}function f3n(e){return ps(),Dt((rBe(),ian),e)}function a3n(e){return al(),Dt((cRe(),Zfn),e)}function h3n(e){return jz(),Dt((nBe(),Vfn),e)}function d3n(e){return B1(),Dt((D$e(),Qfn),e)}function b3n(e){return UR(),Dt((uRe(),dan),e)}function g3n(e){return _s(),Dt((bze(),aan),e)}function w3n(e){return G4(),Dt((N$e(),han),e)}function p3n(e){return De(),Dt((BRe(),ran),e)}function m3n(e){return jE(),Dt((I$e(),lan),e)}function v3n(e){return Vs(),Dt((oRe(),fan),e)}function y3n(e){return KB(),Dt((sRe(),ban),e)}function k3n(e){return PB(),Dt((lRe(),pan),e)}function j3n(e){return j8(),Dt((cze(),Oan),e)}function BNe(e,n,t){Il(),bae.call(this,e,n,t)}function WK(e,n,t){Il(),Vfe.call(this,e,n,t)}function zNe(e,n,t){Il(),WK.call(this,e,n,t)}function Zle(e,n,t){Il(),WK.call(this,e,n,t)}function FNe(e,n,t){Il(),Zle.call(this,e,n,t)}function JNe(e,n,t){Il(),efe.call(this,e,n,t)}function efe(e,n,t){Il(),Vfe.call(this,e,n,t)}function nfe(e,n,t){Il(),Vfe.call(this,e,n,t)}function HNe(e,n,t){Il(),nfe.call(this,e,n,t)}function GNe(e,n,t){this.a=e,this.c=n,this.b=t}function qNe(e,n,t){this.a=e,this.b=n,this.c=t}function tfe(e,n,t){this.a=e,this.b=n,this.c=t}function ife(e,n,t){this.a=e,this.b=n,this.c=t}function ZK(e,n,t){this.a=e,this.b=n,this.c=t}function UNe(e,n,t){this.a=e,this.b=n,this.c=t}function Ed(e,n,t){this.e=e,this.a=n,this.c=t}function rfe(e){this.d=e,en(this),this.b=gvn(e.d)}function cfe(e,n){bgn.call(this,e,qB(new Su(n)))}function NT(e,n){return Nt(e),Nt(n),new QAe(e,n)}function j4(e,n){return Nt(e),Nt(n),new iDe(e,n)}function E3n(e,n){return Nt(e),Nt(n),new rDe(e,n)}function S3n(e,n){return Nt(e),Nt(n),new sMe(e,n)}function eV(e){return at(e.b!=0),$l(e,e.a.a)}function x3n(e){return at(e.b!=0),$l(e,e.c.b)}function A3n(e){return!e.c&&(e.c=new Ol),e.c}function DT(e){var n;return n=new Si,RY(n,e),n}function XNe(e){var n;return n=new wX,RY(n,e),n}function M3n(e){var n;return n=new ar,AY(n,e),n}function M9(e){var n;return n=new Te,AY(n,e),n}function u(e,n){return nE(e==null||PQ(e,n)),e}function C3n(e,n,t){UDe.call(this,n,t),this.a=e}function KNe(e,n){this.c=e,this.b=n,this.a=!1}function VNe(){this.a=";,;",this.b="",this.c=""}function YNe(e,n,t){this.b=e,rTe.call(this,n,t)}function ufe(e,n,t){this.c=e,r$.call(this,n,t)}function ofe(e,n,t){v9.call(this,e,n),this.b=t}function sfe(e,n,t){ebe(t,0,e,n,t.length,!1)}function Jh(e,n,t,i,r){e.b=n,e.c=t,e.d=i,e.a=r}function lfe(e,n,t,i,r){e.d=n,e.c=t,e.a=i,e.b=r}function T3n(e,n){n&&(e.b=n,e.a=(A0(n),n.a))}function IT(e,n){if(!e)throw $(new Gn(n))}function E4(e,n){if(!e)throw $(new Uc(n))}function ffe(e,n){if(!e)throw $(new tAe(n))}function O3n(e,n){return QP(),oo(e.d.p,n.d.p)}function N3n(e,n){return P1(),ki(e.e.b,n.e.b)}function D3n(e,n){return P1(),ki(e.e.a,n.e.a)}function I3n(e,n){return oo(lDe(e.d),lDe(n.d))}function Q$(e,n){return n&&ER(e,n.d)?n:null}function _3n(e,n){return n==(De(),Kn)?e.c:e.d}function L3n(e){return new je(e.c+e.b,e.d+e.a)}function QNe(e){return e!=null&&!kQ(e,uA,oA)}function P3n(e,n){return(IFe(e)<<4|IFe(n))&yr}function WNe(e,n,t,i,r){e.c=n,e.d=t,e.b=i,e.a=r}function afe(e){var n,t;n=e.b,t=e.c,e.b=t,e.c=n}function hfe(e){var n,t;t=e.d,n=e.a,e.d=n,e.a=t}function $3n(e,n){var t;return t=e.c,Jhe(e,n),t}function dfe(e,n){return n<0?e.g=-1:e.g=n,e}function W$(e,n){return C8n(e),e.a*=n,e.b*=n,e}function _T(e,n,t){Dse.call(this,e,n),this.c=t}function Z$(e,n,t){Dse.call(this,e,n),this.c=t}function bfe(e){Qle(),p3.call(this),this._h(e)}function ZNe(){z9(),Vvn.call(this,(y0(),kf))}function eDe(e){return fi(),new Hh(0,e)}function nDe(){nDe=Y,Hce=(jn(),new In(Rne))}function eR(){eR=Y,new Mde((EX(),Yne),(jX(),Vne))}function tDe(){this.b=ne(re(_e((Gf(),kte))))}function nV(e){this.b=e,this.a=$b(this.b.a).Md()}function iDe(e,n){this.b=e,this.a=n,wC.call(this)}function rDe(e,n){this.a=e,this.b=n,wC.call(this)}function cDe(e,n,t){this.a=e,N3.call(this,n,t)}function uDe(e,n,t){this.a=e,N3.call(this,n,t)}function C9(e,n,t){var i;i=new y2(t),Rf(e,n,i)}function gfe(e,n,t){var i;return i=e[n],e[n]=t,i}function nR(e){var n;return n=e.slice(),vY(n,e)}function tR(e){var n;return n=e.n,e.a.b+n.d+n.a}function oDe(e){var n;return n=e.n,e.e.b+n.d+n.a}function wfe(e){var n;return n=e.n,e.e.a+n.b+n.c}function pfe(e){e.a.b=e.b,e.b.a=e.a,e.a=e.b=null}function Vt(e,n){return Xi(e,n,e.c.b,e.c),!0}function R3n(e){return e.a?e.a:NV(e)}function nE(e){if(!e)throw $(new l9(null))}function yw(e,n){return XE(e,new v9(n.a,n.b))}function B3n(e){return!cc(e)&&e.c.i.c==e.d.i.c}function z3n(e,n){return e.c=n)throw $(new bxe)}function Hu(e){e.f=new yTe(e),e.i=new kTe(e),++e.g}function wR(e){this.b=new xo(11),this.a=(Aw(),e)}function bV(e){this.b=null,this.a=(Aw(),e||Fme)}function Ife(e,n){this.e=e,this.d=(n&64)!=0?n|yh:n}function UDe(e,n){this.c=0,this.d=e,this.b=n|64|yh}function XDe(e){this.a=XJe(e.a),this.b=new bs(e.b)}function Sd(e,n,t,i){var r;r=e.i,r.i=n,r.a=t,r.b=i}function _fe(e){var n;for(n=e;n.f;)n=n.f;return n}function jvn(e){return e.e?ihe(e.e):null}function cE(e){return ps(),!e.Gc(Z1)&&!e.Gc(gb)}function KDe(e,n,t){return x8(),GY(e,n)&&GY(e,t)}function VDe(e,n,t){return iYe(e,u(n,12),u(t,12))}function gV(e,n){return n.Sh()?$0(e.b,u(n,52)):n}function pR(e){return new je(e.c+e.b/2,e.d+e.a/2)}function Evn(e,n,t){n.of(t,ne(re(Bn(e.b,t)))*e.a)}function Svn(e,n){n.Tg("General 'Rotator",1),B$n(e)}function Ir(e,n,t,i,r){pY.call(this,e,n,t,i,r,-1)}function uE(e,n,t,i,r){tO.call(this,e,n,t,i,r,-1)}function pe(e,n,t,i){mr.call(this,e,n,t),this.b=i}function mR(e,n,t,i){_T.call(this,e,n,t),this.b=i}function YDe(e){XCe.call(this,e,!1),this.a=!1}function QDe(){yK.call(this,"LOOKAHEAD_LAYOUT",1)}function WDe(){yK.call(this,"LAYOUT_NEXT_LEVEL",3)}function ZDe(e){this.b=e,p4.call(this,e),cOe(this)}function eIe(e){this.b=e,kT.call(this,e),uOe(this)}function nIe(e,n){this.b=e,HU.call(this,e.b),this.a=n}function m2(e,n,t){this.a=e,y4.call(this,n,t,5,6)}function Lfe(e,n,t,i){this.b=e,mr.call(this,n,t,i)}function zb(e,n,t){mh(),this.e=e,this.d=n,this.a=t}function Zr(e,n){for(_n(n);e.Ob();)n.Ad(e.Pb())}function vR(e,n){return fi(),new Yfe(e,n,0)}function wV(e,n){return fi(),new Yfe(6,e,n)}function xvn(e,n){return bn(e.substr(0,n.length),n)}function so(e,n){return $r(n)?RV(e,n):!!Xc(e.f,n)}function Avn(e){return _o(~e.l&Ls,~e.m&Ls,~e.h&G1)}function pV(e){return typeof e===sN||typeof e===aZ}function qh(e){return new qn(new sle(e.a.length,e.a))}function mV(e){return new wn(null,_vn(e,e.length))}function tIe(e){if(!e)throw $(new au);return e.d}function A4(e){var n;return n=TE(e),at(n!=null),n}function Mvn(e){var n;return n=bjn(e),at(n!=null),n}function O9(e,n){var t;return t=e.a.gc(),Zae(n,t),t-n}function hr(e,n){var t;return t=e.a.yc(n,e),t==null}function PT(e,n){return e.a.yc(n,(Pn(),eb))==null}function Cvn(e,n){return e>0?k.Math.log(e/n):-100}function Pfe(e,n){return n?fc(e,n):!1}function M4(e,n,t){return zf(e.a,n),gfe(e.b,n.g,t)}function Tvn(e,n,t){T9(t,e.a.c.length),ol(e.a,t,n)}function ce(e,n,t,i){nFe(n,t,e.length),Ovn(e,n,t,i)}function Ovn(e,n,t,i){var r;for(r=n;r0?1:0}function sE(e){return e.e==0?e:new zb(-e.e,e.d,e.a)}function Dvn(e){return e==Ki?HN:e==Dr?"-INF":""+e}function Ivn(e){return e==Ki?HN:e==Dr?"-INF":""+e}function _vn(e,n){return S8n(n,e.length),new dDe(e,n)}function rIe(e,n,t,i,r){for(;n=e.g}function MV(e,n,t){var i;return i=$Y(e,n,t),zbe(e,i)}function wIe(e,n){var t;t=console[e],t.call(console,n)}function C4(e,n){var t;t=e.a.length,C2(e,t),nY(e,t,n)}function pIe(e,n){var t;++e.j,t=e.Cj(),e.pj(e.Xi(t,n))}function CV(e,n){for(_n(n);e.c=e?new Yoe:K8n(e-1)}function uf(e){if(e==null)throw $(new e4);return e}function _n(e){if(e==null)throw $(new e4);return e}function Zvn(e){return!e.a&&(e.a=new mr(wb,e,4)),e.a}function Sw(e){return!e.d&&(e.d=new mr(Rc,e,1)),e.d}function e5n(e){if(e.p!=3)throw $(new is);return e.e}function n5n(e){if(e.p!=4)throw $(new is);return e.e}function t5n(e){if(e.p!=6)throw $(new is);return e.f}function i5n(e){if(e.p!=3)throw $(new is);return e.j}function r5n(e){if(e.p!=4)throw $(new is);return e.j}function c5n(e){if(e.p!=6)throw $(new is);return e.k}function or(){$xe.call(this),Qp(this.j.c,0),this.a=-1}function MIe(){Ot.call(this,"DELAUNAY_TRIANGULATION",0)}function u5n(){return BP(),z(B(Gen,1),ke,537,0,[Wne])}function o5n(e,n,t){return J4(),t.Kg(e,u(n.jd(),147))}function s5n(e,n){Et((!e.a&&(e.a=new AT(e,e)),e.a),n)}function Wfe(e,n){e.c<0||e.b.b=0?e.hi(t):q0e(e,n)}function I9(e,n){var t;return t=AV("",e),t.n=n,t.i=1,t}function xw(e){return e.c==-2&&oX(e,EMn(e.g,e.b)),e.c}function Zfe(e){return!e.b&&(e.b=new OP(new kX)),e.b}function CIe(e,n){return eR(),new Mde(new sOe(e),new oOe(n))}function f5n(e){return ll(e,gZ),fB(mc(mc(5,e),e/10|0))}function TV(){TV=Y,Xen=new rse(z(B(wg,1),eF,45,0,[]))}function TIe(){j0e.call(this,gg,(yAe(),rhn)),CPn(this)}function OIe(){j0e.call(this,hf,(d9(),Y8e)),PLn(this)}function NIe(e,n){Bwn.call(this,V8n(Nt(e),Nt(n))),this.a=n}function eae(e,n,t,i){bw.call(this,e,n),this.d=t,this.a=i}function SR(e,n,t,i){bw.call(this,e,t),this.a=n,this.f=i}function DIe(e,n){this.b=e,vV.call(this,e,n),cOe(this)}function IIe(e,n){this.b=e,Xle.call(this,e,n),uOe(this)}function hE(e){this.d=e,this.a=this.d.b,this.b=this.d.c}function _Ie(e){e.b=!1,e.c=!1,e.d=!1,e.a=!1}function _9(e){return!e.a&&(e.a=new uAe(e.c.vc())),e.a}function LIe(e){return!e.b&&(e.b=new f9(e.c.ec())),e.b}function PIe(e){return!e.d&&(e.d=new Hr(e.c.Bc())),e.d}function Uh(e,n){for(;n-- >0;)e=e<<1|(e<0?1:0);return e}function $Ie(e,n){var t;return t=new Xu(e),Hn(n.c,t),t}function a5n(e,n){aV(u(n.b,68),e),Ao(n.a,new Wue(e))}function RIe(e,n){e.u.Gc((ps(),Z1))&&vTn(e,n),y9n(e,n)}function Ku(e,n){return ue(e)===ue(n)||e!=null&&di(e,n)}function ei(e,n,t){return $r(n)?Kc(e,n,t):Ko(e.f,n,t)}function nae(e){return jn(),e?e.Me():(Aw(),Aw(),Jme)}function h5n(){return ZP(),z(B(P6e,1),ke,477,0,[Wre])}function d5n(){return e$(),z(B(Rln,1),ke,546,0,[Zre])}function b5n(){return Cj(),z(B(i9e,1),ke,527,0,[CD])}function zc(e,n){return oV(e.a,n)?e.b[u(n,23).g]:null}function g5n(e){return String.fromCharCode.apply(null,e)}function ic(e,n){return Yn(n,e.length),e.charCodeAt(n)}function RT(e){return e.j.c.length=0,rae(e.c),u2n(e.a),e}function L9(e){return e.e==s7&&a(e,$En(e.g,e.b)),e.e}function BT(e){return e.f==s7&&w(e,Axn(e.g,e.b)),e.f}function w5n(e){return!e.b&&(e.b=new Nn(vt,e,4,7)),e.b}function tae(e){return!e.c&&(e.c=new Nn(vt,e,5,8)),e.c}function iae(e){return!e.c&&(e.c=new pe($s,e,9,9)),e.c}function OV(e){return!e.n&&(e.n=new pe(ju,e,1,7)),e.n}function z3(e){var n;return n=e.b,!n&&(e.b=n=new rj(e)),n}function rae(e){var n;for(n=e.Jc();n.Ob();)n.Pb(),n.Qb()}function p5n(e,n,t){var i;i=u(e.d.Kb(t),162),i&&i.Nb(n)}function m5n(e,n){return new a_e(u(Nt(e),51),u(Nt(n),51))}function si(e,n){return R0(e),new wn(e,new whe(n,e.a))}function So(e,n){return R0(e),new wn(e,new the(n,e.a))}function k2(e,n){return R0(e),new xle(e,new WPe(n,e.a))}function xR(e,n){return R0(e),new Ale(e,new ZPe(n,e.a))}function BIe(e,n){W1e(e,ne($1(n,"x")),ne($1(n,"y")))}function zIe(e,n){W1e(e,ne($1(n,"x")),ne($1(n,"y")))}function v5n(e,n){return Qoe(),ki((_n(e),e),(_n(n),n))}function y5n(e,n){return ki(e.d.c+e.d.b/2,n.d.c+n.d.b/2)}function k5n(e,n){return ki(e.g.c+e.g.b/2,n.g.c+n.g.b/2)}function j5n(e){return e!=null&&Sj(jG,e.toLowerCase())}function E5n(e){rl();var n;n=u(e.g,9),n.n.a=e.d.c+n.d.b}function NV(e){var n;return n=Q8n(e),n||null}function ri(e,n,t,i){return WBe(e,n,t,!1),FB(e,i),e}function S5n(e,n,t){OLn(e.a,t),V7n(t),tOn(e.b,t),QLn(n,t)}function T4(e,n,t,i){Ot.call(this,e,n),this.a=t,this.b=i}function AR(e,n,t,i){this.a=e,this.c=n,this.b=t,this.d=i}function cae(e,n,t,i){this.c=e,this.b=n,this.a=t,this.d=i}function FIe(e,n,t,i){this.c=e,this.b=n,this.d=t,this.a=i}function DV(e,n,t,i){this.a=e,this.e=n,this.d=t,this.c=i}function JIe(e,n,t,i){this.a=e,this.d=n,this.c=t,this.b=i}function Lf(e,n,t,i){this.c=e,this.d=n,this.b=t,this.a=i}function IV(e,n,t){this.a=Jge,this.d=e,this.b=n,this.c=t}function uae(e,n){this.b=e,this.c=n,this.a=new s4(this.b)}function HIe(e,n){this.d=(_n(e),e),this.a=16449,this.c=n}function GIe(e,n,t,i){Xze.call(this,e,t,i,!1),this.f=n}function _V(e,n,t){var i,r;return i=Tge(e),r=n.qi(t,i),r}function T1(e){var n,t;return t=(n=new hw,n),q9(t,e),t}function LV(e){var n,t;return t=(n=new hw,n),x0e(t,e),t}function qIe(e){return!e.b&&(e.b=new pe(pr,e,12,3)),e.b}function UIe(e){this.a=new Te,this.e=oe($t,Se,54,e,0,2)}function PV(e){this.f=e,this.c=this.f.e,e.f>0&&JHe(this)}function XIe(e,n,t,i){this.a=e,this.c=n,this.d=t,this.b=i}function KIe(e,n,t,i){this.a=e,this.b=n,this.d=t,this.c=i}function VIe(e,n,t,i){this.a=e,this.b=n,this.c=t,this.d=i}function YIe(e,n,t,i){this.a=e,this.b=n,this.c=t,this.d=i}function Jb(e,n,t,i){this.e=e,this.a=n,this.c=t,this.d=i}function QIe(e,n,t,i){Il(),QPe.call(this,n,t,i),this.a=e}function WIe(e,n,t,i){Il(),QPe.call(this,n,t,i),this.a=e}function ZIe(e,n){this.a=e,zpn.call(this,e,u(e.d,16).dd(n))}function x5n(e,n){return ki(us(e)*Gs(e),us(n)*Gs(n))}function A5n(e,n){return ki(us(e)*Gs(e),us(n)*Gs(n))}function O4(e){var n;return n=e.f,n||(e.f=new g9(e,e.c))}function jn(){jn=Y,Sc=new Ae,i1=new sn,hJ=new Sn}function Aw(){Aw=Y,Fme=new xe,ote=new xe,Jme=new un}function P9(e){if(Ds(e.d),e.d.d!=e.c)throw $(new Nl)}function qs(e){e.a.a=e.c,e.c.b=e.a,e.a.b=e.c.a=null,e.b=0}function oae(e){return at(e.b0?$f(e):new Te}function MR(e){return e.n&&(e.e!==pYe&&e.he(),e.j=null),e}function sae(e,n){return e.b=n.b,e.c=n.c,e.d=n.d,e.a=n.a,e}function C5n(e,n,t){return Ce(e.a,(GQ(n,t),new bw(n,t))),e}function T5n(e,n){return u(C(e,(me(),Cy)),16).Ec(n),n}function O5n(e,n){return gn(e,u(C(n,(Ne(),mm)),15),n)}function N5n(e){return Hw(e)&&Re($e(ye(e,(Ne(),kg))))}function D5n(e,n,t){return Mj(),_jn(u(Bn(e.e,n),516),t)}function I5n(e,n,t){e.i=0,e.e=0,n!=t&&Hze(e,n,t)}function _5n(e,n,t){e.i=0,e.e=0,n!=t&&Gze(e,n,t)}function e_e(e,n,t,i){this.b=e,this.c=i,N$.call(this,n,t)}function n_e(e,n){this.g=e,this.d=z(B(c1,1),Bd,9,0,[n])}function t_e(e,n){e.d&&!e.d.a&&(USe(e.d,n),t_e(e.d,n))}function i_e(e,n){e.e&&!e.e.a&&(USe(e.e,n),i_e(e.e,n))}function r_e(e,n){return ev(e.j,n.s,n.c)+ev(n.e,e.s,e.c)}function L5n(e,n){return-ki(us(e)*Gs(e),us(n)*Gs(n))}function P5n(e){return u(e.jd(),147).Og()+":"+su(e.kd())}function c_e(){dW(this,new OC),this.wb=(x0(),Rn),d9()}function u_e(e){this.b=new u_,this.a=e,k.Math.random()}function o_e(e){this.b=new Te,Er(this.b,this.b),this.a=e}function lae(e,n){new Si,this.a=new xs,this.b=e,this.c=n}function s_e(){hu.call(this,"There is no more element.")}function $5n(e){JP(),k.setTimeout(function(){throw e},0)}function R5n(e){e.Tg("No crossing minimization",1),e.Ug()}function B5n(e,n){return Us(e),Us(n),Wxe(u(e,23),u(n,23))}function Hb(e,n,t){var i,r;i=qK(t),r=new k3(i),Rf(e,n,r)}function $V(e,n,t,i,r,c){tO.call(this,e,n,t,i,r,c?-2:-1)}function l_e(e,n,t,i){Dse.call(this,n,t),this.b=e,this.a=i}function fae(e){this.b=e,this.c=e,e.e=null,e.c=null,this.a=1}function CR(e){return!e.a&&(e.a=new pe(Jt,e,10,11)),e.a}function vi(e){return!e.q&&(e.q=new pe(yf,e,11,10)),e.q}function we(e){return!e.s&&(e.s=new pe(ns,e,21,17)),e.s}function f_e(e){return nE(e==null||pV(e)&&e.Rm!==Qn),e}function TR(e,n){if(e==null)throw $(new c4(n));return e}function a_e(e,n){ybn.call(this,new bV(e)),this.a=e,this.b=n}function RV(e,n){return n==null?!!Xc(e.f,null):svn(e.i,n)}function BV(e){return X(e,18)?new w2(u(e,18)):M3n(e.Jc())}function OR(e){return jn(),X(e,59)?new DX(e):new B$(e)}function z5n(e){return Nt(e),rHe(new qn(Vn(e.a.Jc(),new ee)))}function F5n(e){return new nOe(e,e.e.Pd().gc()*e.c.Pd().gc())}function J5n(e){return new tOe(e,e.e.Pd().gc()*e.c.Pd().gc())}function aae(e){return e&&e.hashCode?e.hashCode():vw(e)}function H5n(e){e&&DR(e,e.ge())}function G5n(e,n){var t;return t=Qse(e.a,n),t&&(n.d=null),t}function h_e(e,n,t){return e.f?e.f.cf(n,t):!1}function zT(e,n,t,i){tr(e.c[n.g],t.g,i),tr(e.c[t.g],n.g,i)}function zV(e,n,t,i){tr(e.c[n.g],n.g,t),tr(e.b[n.g],n.g,i)}function q5n(e,n,t){return ne(re(t.a))<=e&&ne(re(t.b))>=n}function d_e(){this.d=new Si,this.b=new pt,this.c=new Te}function b_e(){this.b=new ar,this.d=new Si,this.e=new $P}function hae(){this.c=new Kr,this.d=new Kr,this.e=new Kr}function Mw(){this.a=new xs,this.b=(ll(3,Q2),new xo(3))}function g_e(e){this.c=e,this.b=new vd(u(Nt(new Sf),51))}function w_e(e){this.c=e,this.b=new vd(u(Nt(new nk),51))}function p_e(e){this.b=e,this.a=new vd(u(Nt(new qg),51))}function xd(e,n){this.e=e,this.a=Ar,this.b=IXe(n),this.c=n}function NR(e){this.c=e.c,this.d=e.d,this.b=e.b,this.a=e.a}function m_e(e,n,t,i,r,c){this.a=e,DY.call(this,n,t,i,r,c)}function v_e(e,n,t,i,r,c){this.a=e,DY.call(this,n,t,i,r,c)}function M0(e,n,t,i,r,c,o){return new rY(e.e,n,t,i,r,c,o)}function U5n(e,n,t){return t>=0&&bn(e.substr(t,n.length),n)}function y_e(e,n){return X(n,147)&&bn(e.b,u(n,147).Og())}function X5n(e,n){return e.a?n.Dh().Jc():u(n.Dh(),72).Gi()}function k_e(e,n){var t;return t=e.b.Oc(n),bPe(t,e.b.gc()),t}function FT(e,n){if(e==null)throw $(new c4(n));return e}function tu(e){return e.u||(Ms(e),e.u=new VOe(e,e)),e.u}function Go(e){var n;return n=u(Un(e,16),29),n||e.fi()}function DR(e,n){var t;return t=Db(e.Pm),n==null?t:t+": "+n}function of(e,n,t){return Yr(n,t,e.length),e.substr(n,t-n)}function j_e(e,n){q$.call(this),Che(this),this.a=e,this.c=n}function E_e(){yK.call(this,"FIXED_INTEGER_RATIO_BOXES",2)}function K5n(){return nB(),z(B(gve,1),ke,422,0,[bve,Xte])}function V5n(){return fO(),z(B(Sve,1),ke,419,0,[XJ,Eve])}function Y5n(){return rO(),z(B(Mve,1),ke,476,0,[Ave,VJ])}function Q5n(){return JR(),z(B(Fve,1),ke,420,0,[bie,zve])}function W5n(){return qR(),z(B(n5e,1),ke,423,0,[Sie,Eie])}function Z5n(){return uO(),z(B(J4e,1),ke,421,0,[tre,ire])}function e4n(){return mE(),z(B(jon,1),ke,518,0,[Tx,Cx])}function n4n(){return Da(),z(B(Non,1),ke,508,0,[Ag,Ya])}function t4n(){return ah(),z(B(Ton,1),ke,509,0,[pp,Ud])}function i4n(){return ha(),z(B(Uon,1),ke,515,0,[Am,sb])}function r4n(){return Tw(),z(B(Zon,1),ke,454,0,[lb,Jv])}function c4n(){return FR(),z(B($ye,1),ke,425,0,[xre,Pye])}function u4n(){return SB(),z(B(Rye,1),ke,487,0,[BH,qv])}function o4n(){return iB(),z(B(zye,1),ke,426,0,[Bye,Nre])}function s4n(){return lB(),z(B(n3e,1),ke,424,0,[vte,pJ])}function l4n(){return K9(),z(B(sin,1),ke,502,0,[QN,Dte])}function f4n(){return VR(),z(B(T6e,1),ke,478,0,[Kre,C6e])}function a4n(){return WT(),z(B($6e,1),ke,428,0,[ece,YH])}function h4n(){return pO(),z(B(c9e,1),ke,427,0,[WH,r9e])}function IR(e,n,t,i){return t>=0?e.Rh(n,t,i):e.zh(null,t,i)}function JT(e){return e.b.b==0?e.a.uf():eV(e.b)}function d4n(e){if(e.p!=5)throw $(new is);return Rt(e.f)}function b4n(e){if(e.p!=5)throw $(new is);return Rt(e.k)}function dae(e){return ue(e.a)===ue((FY(),zce))&&jPn(e),e.a}function S_e(e,n){fj(this,new je(e.a,e.b)),j3(this,DT(n))}function Cw(){kbn.call(this,new l4(I2(12))),tle(!0),this.a=2}function FV(e,n,t){fi(),aw.call(this,e),this.b=n,this.a=t}function bae(e,n,t){Il(),DP.call(this,n),this.a=e,this.b=t}function g4n(e,n){var t=nte[e.charCodeAt(0)];return t??e}function _R(e,n){return TR(e,"set1"),TR(n,"set2"),new dMe(e,n)}function LR(e,n){return hPe(n),P8n(e,oe($t,ni,30,n,15,1),n)}function w4n(e,n){e.b=n,e.c>0&&e.b>0&&(e.g=oR(e.c,e.b,e.a))}function p4n(e,n){e.c=n,e.c>0&&e.b>0&&(e.g=oR(e.c,e.b,e.a))}function x_e(e){var n;n=e.c.d.b,e.b=n,e.a=e.c.d,n.a=e.c.d.b=e}function A_e(e){return e.b==0?null:(at(e.b!=0),$l(e,e.a.a))}function lo(e,n){return n==null?du(Xc(e.f,null)):Dj(e.i,n)}function M_e(e,n,t,i,r){return new gW(e,(H9(),ate),n,t,i,r)}function JV(e,n,t,i){var r;r=new lNe,n.a[t.g]=r,M4(e.b,i,r)}function C_e(e,n){var t,i;return t=n,i=new st,bVe(e,t,i),i.d}function m4n(e,n){var t;return t=O8n(e.f,n),gi(G$(t),e.f.d)}function HT(e){var n;G8n(e.a),TTe(e.a),n=new CP(e.a),ode(n)}function v4n(e,n){jXe(e,!0),Ao(e.e.Pf(),new Kle(e,!0,n))}function y4n(e,n){return P1(),u(C(n,(Mu(),Nh)),15).a==e}function sc(e){return Math.max(Math.min(e,oi),-2147483648)|0}function T_e(e){q$.call(this),Che(this),this.a=e,this.c=!0}function gae(e,n,t){this.a=new Te,this.e=e,this.f=n,this.c=t}function PR(e,n,t){this.c=new Te,this.e=e,this.f=n,this.b=t}function O_e(e,n,t){this.i=new Te,this.b=e,this.g=n,this.a=t}function N_e(e){this.a=u(Nt(e),277),this.b=(jn(),new ole(e))}function $9(){$9=Y;var e,n;n=!pEn(),e=new ln,tte=n?new rn:e}function wae(){wae=Y,Snn=new o0,Ann=new xfe,xnn=new ud}function ah(){ah=Y,pp=new yse(fy,0),Ud=new yse(ly,1)}function Da(){Da=Y,Ag=new kse(XZ,0),Ya=new kse("UP",1)}function Tw(){Tw=Y,lb=new Ese(ly,0),Jv=new Ese(fy,1)}function F3(e,n,t){$R(),e&&ei($ce,e,n),e&&ei(WD,e,t)}function pae(e,n,t){var i;i=e.Fh(n),i>=0?e.$h(i,t):ybe(e,n,t)}function D_e(e,n){var t;for(Nt(n),t=e.a;t;t=t.c)n.Wd(t.g,t.i)}function GT(e,n){var t;t=e.q.getHours(),e.q.setDate(n),oS(e,t)}function I_e(e){var n;return n=new UP(I2(e.length)),m1e(n,e),n}function k4n(e){function n(){}return n.prototype=e||{},new n}function j4n(e,n){return mze(e,n)?(mBe(e),!0):!1}function O1(e,n){if(n==null)throw $(new e4);return jEn(e,n)}function E4n(e){if(e.ye())return null;var n=e.n;return uJ[n]}function j2(e){return e.Db>>16!=3?null:u(e.Cb,26)}function Ia(e){return e.Db>>16!=9?null:u(e.Cb,26)}function __e(e){return e.Db>>16!=6?null:u(e.Cb,85)}function L_e(e,n){var t;return t=e.Fh(n),t>=0?e.Th(t):kW(e,n)}function HV(e,n,t){var i;i=Rze(e,n,t),e.b=new AB(i.c.length)}function P_e(e){this.a=e,this.b=oe(von,Se,2005,e.e.length,0,2)}function $_e(){this.a=new zh,this.e=new ar,this.g=0,this.i=0}function R_e(e,n){L$(this),this.f=n,this.g=e,MR(this),this.he()}function B_e(e,n){return e.b+=n.b,e.c+=n.c,e.d+=n.d,e.a+=n.a,e}function mae(e){var n;return n=e.d,n=e._i(e.f),Et(e,n),n.Ob()}function z_e(e,n){var t;return t=new kfe(n),wGe(t,e),new bs(t)}function S4n(e){if(e.p!=0)throw $(new is);return Gj(e.f,0)}function x4n(e){if(e.p!=0)throw $(new is);return Gj(e.k,0)}function F_e(e){return e.Db>>16!=7?null:u(e.Cb,241)}function vae(e){return e.Db>>16!=7?null:u(e.Cb,174)}function J_e(e){return e.Db>>16!=3?null:u(e.Cb,158)}function R9(e){return e.Db>>16!=6?null:u(e.Cb,241)}function Bi(e){return e.Db>>16!=11?null:u(e.Cb,26)}function E2(e){return e.Db>>16!=17?null:u(e.Cb,29)}function dE(e,n,t,i,r,c){return new L1(e.e,n,e.Jj(),t,i,r,c)}function Kc(e,n,t){return n==null?Ko(e.f,null,t):Pw(e.i,n,t)}function GV(e,n){return k.Math.abs(e)0}function yae(e){var n;return R0(e),n=new ar,si(e,new Gke(n))}function H_e(e,n){var t=e.a=e.a||[];return t[n]||(t[n]=e.te(n))}function O4n(e,n){var t;t=e.q.getHours(),e.q.setMonth(n),oS(e,t)}function lc(e,n){e.c&&qo(e.c.g,e),e.c=n,e.c&&Ce(e.c.g,e)}function Or(e,n){e.c&&qo(e.c.a,e),e.c=n,e.c&&Ce(e.c.a,e)}function Gr(e,n){e.d&&qo(e.d.e,e),e.d=n,e.d&&Ce(e.d.e,e)}function gu(e,n){e.i&&qo(e.i.j,e),e.i=n,e.i&&Ce(e.i.j,e)}function G_e(e,n,t){this.a=n,this.c=e,this.b=(Nt(t),new bs(t))}function q_e(e,n,t){this.a=n,this.c=e,this.b=(Nt(t),new bs(t))}function U_e(e,n){this.a=e,this.c=pc(this.a),this.b=new NR(n)}function S2(e,n){if(e<0||e>n)throw $(new jo(Qge+e+Wge+n))}function X_e(){X_e=Y,con=Eo(new or,(zr(),Pc),(Ur(),Ey))}function kae(){kae=Y,uon=Eo(new or,(zr(),Pc),(Ur(),Ey))}function K_e(){K_e=Y,eon=Eo(new or,(zr(),Pc),(Ur(),Ey))}function V_e(){V_e=Y,non=Eo(new or,(zr(),Pc),(Ur(),Ey))}function Y_e(){Y_e=Y,ton=Eo(new or,(zr(),Pc),(Ur(),Ey))}function jae(){jae=Y,ion=Eo(new or,(zr(),Pc),(Ur(),Ey))}function Q_e(){Q_e=Y,Son=qt(new or,(zr(),Pc),(Ur(),nx))}function cl(){cl=Y,Mon=qt(new or,(zr(),Pc),(Ur(),nx))}function W_e(){W_e=Y,Con=qt(new or,(zr(),Pc),(Ur(),nx))}function qV(){qV=Y,Ion=qt(new or,(zr(),Pc),(Ur(),nx))}function Z_e(){Z_e=Y,Csn=Eo(new or,(Y4(),Nx),(cS(),cye))}function eLe(){eLe=Y,qen=It((BP(),z(B(Gen,1),ke,537,0,[Wne])))}function $R(){$R=Y,$ce=new pt,WD=new pt,Hgn(fnn,new F6)}function N4n(e,n){var t,i;t=n.c,i=t!=null,i&&C4(e,new y2(n.c))}function nLe(e,n){Kvn(e,e.b,e.c),u(e.b.b,68),n&&u(n.b,68).b}function RR(e,n){X(e.Cb,184)&&(u(e.Cb,184).tb=null),Mo(e,n)}function UV(e,n){X(e.Cb,88)&&H2(Ms(u(e.Cb,88)),4),Mo(e,n)}function D4n(e,n){Q1e(e,n),X(e.Cb,88)&&H2(Ms(u(e.Cb,88)),2)}function I4n(e,n){return ki(u(e.c,65).c.e.b,u(n.c,65).c.e.b)}function _4n(e,n){return ki(u(e.c,65).c.e.a,u(n.c,65).c.e.a)}function fo(e,n){return Tc(),xY(n)?new rR(n,e):new pT(n,e)}function XV(e,n){e.a&&qo(e.a.k,e),e.a=n,e.a&&Ce(e.a.k,e)}function KV(e,n){e.b&&qo(e.b.f,e),e.b=n,e.b&&Ce(e.b.f,e)}function C0(e,n,t){OFe(n,t,e.gc()),this.c=e,this.a=n,this.b=t-n}function N4(e){this.c=new Si,this.b=e.b,this.d=e.c,this.a=e.a}function VV(e){this.a=k.Math.cos(e),this.b=k.Math.sin(e)}function Gb(e,n,t,i){this.c=e,this.d=i,XV(this,n),KV(this,t)}function pn(e,n){this.b=(_n(e),e),this.a=(n&W2)==0?n|64|yh:n}function L4n(e,n){YTe(e,Rt(Rr(kw(n,24),rF)),Rt(Rr(n,rF)))}function qT(e){return mh(),ao(e,0)>=0?B0(e):sE(B0(Cd(e)))}function P4n(){return zl(),z(B(Qo,1),ke,130,0,[Kme,Yo,Vme])}function tLe(e,n,t){return new gW(e,(H9(),fte),null,!1,n,t)}function iLe(e,n,t){return new gW(e,(H9(),hte),n,t,null,!1)}function rLe(e,n,t){var i;OFe(n,t,e.c.length),i=t-n,Goe(e.c,n,i)}function cLe(e,n){var t;return t=u(L2(O4(e.a),n),18),t?t.gc():0}function Eae(e){var n;return R0(e),n=(Aw(),Aw(),ote),aB(e,n)}function uLe(e){for(var n;;)if(n=e.Pb(),!e.Ob())return n}function oLe(e){var n,t;return t=(d9(),n=new hw,n),q9(t,e),t}function sLe(e){var n,t;return t=(d9(),n=new hw,n),q9(t,e),t}function J3(e){return Mj(),X(e.g,9)?u(e.g,9):null}function $4n(){return _w(),z(B(Rte,1),ke,368,0,[lp,ib,sp])}function R4n(){return pB(),z(B(yve,1),ke,350,0,[vve,UJ,Kte])}function B4n(){return Wb(),z(B(Bin,1),ke,449,0,[tie,k7,Ov])}function z4n(){return W9(),z(B(hie,1),ke,302,0,[fie,aie,tD])}function F4n(){return _0(),z(B(die,1),ke,329,0,[iD,Bve,dm])}function J4n(){return _1(),z(B(Kin,1),ke,315,0,[rD,Dv,Sy])}function H4n(){return IE(),z(B(D4e,1),ke,352,0,[Vie,N4e,SH])}function G4n(){return Nc(),z(B(Xun,1),ke,452,0,[xx,ys,Do])}function q4n(){return DB(),z(B(q4e,1),ke,381,0,[H4e,rre,G4e])}function U4n(){return DE(),z(B(U4e,1),ke,348,0,[ure,cre,pD])}function X4n(){return r8(),z(B(K4e,1),ke,349,0,[ore,X4e,Ax])}function K4n(){return wB(),z(B(Q4e,1),ke,351,0,[Y4e,sre,V4e])}function V4n(){return IB(),z(B(W4e,1),ke,382,0,[lre,I7,xm])}function Y4n(){return yE(),z(B(w3e,1),ke,384,0,[Ete,jte,Ste])}function Q4n(){return ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])}function W4n(){return ws(),z(B(Onn,1),ke,461,0,[Ch,nb,Uf])}function Z4n(){return Uo(),z(B(Dnn,1),ke,462,0,[ka,tb,Xf])}function eyn(){return NE(),z(B(gye,1),ke,385,0,[bye,hre,yD])}function nyn(){return EO(),z(B(Hye,1),ke,386,0,[zH,Fye,Jye])}function tyn(){return XB(),z(B(a6e,1),ke,387,0,[f6e,Gre,l6e])}function iyn(){return NB(),z(B(o6e,1),ke,303,0,[Pre,u6e,c6e])}function ryn(){return WB(),z(B(s6e,1),ke,436,0,[Px,HH,$re])}function cyn(){return JB(),z(B(L6e,1),ke,430,0,[I6e,_6e,Yre])}function uyn(){return CO(),z(B(Qre,1),ke,435,0,[XH,KH,VH])}function oyn(){return rB(),z(B(D6e,1),ke,429,0,[Vre,N6e,O6e])}function syn(){return $a(),z(B(t8e,1),ke,279,0,[F7,_m,J7])}function lyn(){return B1(),z(B(b8e,1),ke,347,0,[oG,Yd,Qx])}function fyn(){return jE(),z(B(y8e,1),ke,300,0,[HD,Cce,v8e])}function ayn(){return G4(),z(B(E8e,1),ke,281,0,[j8e,Pm,dG])}function _a(e){return pu(z(B(Lr,1),Se,8,0,[e.i.n,e.n,e.a]))}function hyn(e,n,t){var i;i=new wc(t.d),gi(i,e),W1e(n,i.a,i.b)}function lLe(e,n,t){var i;i=new dM,i.b=n,i.a=t,++n.b,Ce(e.d,i)}function dyn(e,n,t){var i;return i=fS(e,n,!1),i.b<=n&&i.a<=t}function byn(e){if(e.p!=2)throw $(new is);return Rt(e.f)&yr}function gyn(e){if(e.p!=2)throw $(new is);return Rt(e.k)&yr}function vn(e,n){if(e<0||e>=n)throw $(new jo(Qge+e+Wge+n))}function Yn(e,n){if(e<0||e>=n)throw $(new Doe(Qge+e+Wge+n))}function wyn(e){return e.Db>>16!=6?null:u(SW(e),241)}function fLe(e,n){var t,i;return i=O9(e,n),t=e.a.dd(i),new aMe(e,t)}function pyn(e,n){var t;return t=(_n(e),e).g,gle(!!t),_n(n),t(n)}function myn(e){return e.a==(z9(),AG)&&GC(e,KDn(e.g,e.b)),e.a}function D4(e){return e.d==(z9(),AG)&&Gue(e,U_n(e.g,e.b)),e.d}function Sae(e,n){vbn.call(this,new l4(I2(e))),ll(n,aYe),this.a=n}function aLe(e,n,t){aw.call(this,25),this.b=e,this.a=n,this.c=t}function ul(e){fi(),aw.call(this,e),this.c=!1,this.a=!1}function hLe(e,n){zb.call(this,1,2,z(B($t,1),ni,30,15,[e,n]))}function Rr(e,n){return I0(pvn(uu(e)?sf(e):e,uu(n)?sf(n):n))}function hh(e,n){return I0(mvn(uu(e)?sf(e):e,uu(n)?sf(n):n))}function YV(e,n){return I0(vvn(uu(e)?sf(e):e,uu(n)?sf(n):n))}function xae(e,n){return NDe(e.a,n)?gfe(e.b,u(n,23).g,null):null}function qb(e){return Nt(e),X(e,18)?new bs(u(e,18)):M9(e.Jc())}function QV(e){cR(),this.a=(jn(),X(e,59)?new DX(e):new B$(e))}function vyn(e){var n;return n=u(nR(e.b),10),new _l(e.a,n,e.c)}function yyn(e,n){var t;t=ne(re(e.a.mf((Xt(),iG)))),$Ve(e,n,t)}function kyn(e,n){return kE(),e.c==n.c?ki(n.d,e.d):ki(e.c,n.c)}function jyn(e,n){return kE(),e.c==n.c?ki(e.d,n.d):ki(e.c,n.c)}function Eyn(e,n){return kE(),e.c==n.c?ki(e.d,n.d):ki(n.c,e.c)}function Syn(e,n){return kE(),e.c==n.c?ki(n.d,e.d):ki(n.c,e.c)}function xyn(e,n){e.b=e.b|n.b,e.c=e.c|n.c,e.d=e.d|n.d,e.a=e.a|n.a}function _(e){return at(e.ai?1:0}function bLe(e,n){var t,i;return t=yY(n),i=t,u(Bn(e.c,i),15).a}function WV(e,n,t){var i;i=e.d[n.p],e.d[n.p]=e.d[t.p],e.d[t.p]=i}function Tyn(e,n,t){var i;e.n&&n&&t&&(i=new gL,Ce(e.e,i))}function ZV(e,n){if(hr(e.a,n),n.d)throw $(new hu(LYe));n.d=e}function Cae(e,n){this.a=new Te,this.d=new Te,this.f=e,this.c=n}function gLe(){J4(),this.b=new pt,this.a=new pt,this.c=new Te}function wLe(){this.c=new LTe,this.a=new KPe,this.b=new fxe,MMe()}function pLe(e,n,t){this.d=e,this.j=n,this.e=t,this.o=-1,this.p=3}function mLe(e,n,t){this.d=e,this.k=n,this.f=t,this.o=-1,this.p=5}function vLe(e,n,t,i,r,c){The.call(this,e,n,t,i,r),c&&(this.o=-2)}function yLe(e,n,t,i,r,c){Ohe.call(this,e,n,t,i,r),c&&(this.o=-2)}function kLe(e,n,t,i,r,c){Gae.call(this,e,n,t,i,r),c&&(this.o=-2)}function jLe(e,n,t,i,r,c){Ihe.call(this,e,n,t,i,r),c&&(this.o=-2)}function ELe(e,n,t,i,r,c){qae.call(this,e,n,t,i,r),c&&(this.o=-2)}function SLe(e,n,t,i,r,c){Nhe.call(this,e,n,t,i,r),c&&(this.o=-2)}function xLe(e,n,t,i,r,c){Dhe.call(this,e,n,t,i,r),c&&(this.o=-2)}function ALe(e,n,t,i,r,c){Uae.call(this,e,n,t,i,r),c&&(this.o=-2)}function MLe(e,n,t,i){DP.call(this,t),this.b=e,this.c=n,this.d=i}function CLe(e,n){this.f=e,this.a=(z9(),xG),this.c=xG,this.b=n}function TLe(e,n){this.g=e,this.d=(z9(),AG),this.a=AG,this.b=n}function Tae(e,n){!e.c&&(e.c=new nr(e,0)),Xz(e.c,(Ei(),lA),n)}function Oyn(e,n){return AOn(e,n,X(n,103)&&(u(n,19).Bb&Ec)!=0)}function Nyn(e,n){return iIe(Pu(e.q.getTime()),Pu(n.q.getTime()))}function OLe(e){return iV(e.e.Pd().gc()*e.c.Pd().gc(),16,new X5(e))}function Dyn(e){return!!e.u&&Vu(e.u.a).i!=0&&!(e.n&&zQ(e.n))}function Iyn(e){return!!e.a&&Ts(e.a.a).i!=0&&!(e.b&&FQ(e.b))}function Oae(e,n){return n==0?!!e.o&&e.o.f!=0:_Q(e,n)}function NLe(e){return at(e.b.b!=e.d.a),e.c=e.b=e.b.b,--e.a,e.c.c}function bE(e){for(;e.d>0&&e.a[--e.d]==0;);e.a[e.d++]==0&&(e.e=0)}function DLe(e){return e.a?e.e.length==0?e.a.a:e.a.a+(""+e.e):e.c}function qr(e,n){this.a=e,qc.call(this,e),S2(n,e.gc()),this.b=n}function ILe(e){this.a=oe(Ar,On,1,b1e(k.Math.max(8,e))<<1,5,1)}function _Le(e){zY.call(this,e,(H9(),lte),null,!1,null,!1)}function LLe(e,n){var t;return t=1-n,e.a[t]=xB(e.a[t],t),xB(e,n)}function PLe(e,n){var t,i;return i=Rr(e,Ic),t=Gh(n,32),hh(t,i)}function _yn(e,n,t){var i;return i=u(e.Zb().xc(n),18),!!i&&i.Gc(t)}function $Le(e,n,t){var i;return i=u(e.Zb().xc(n),18),!!i&&i.Kc(t)}function RLe(e,n,t){var i;i=(Nt(e),new bs(e)),bxn(new G_e(i,n,t))}function XT(e,n,t){var i;i=(Nt(e),new bs(e)),gxn(new q_e(i,n,t))}function Lyn(e,n,t){e.a=n,e.c=t,e.b.a.$b(),qs(e.d),Qp(e.e.a.c,0)}function BLe(e,n){var t;e.e=new Soe,t=q2(n),Tr(t,e.c),fXe(e,t,0)}function Pyn(e,n){return new ZK(n,TOe(pc(n.e),e,e),(Pn(),!0))}function $yn(e,n){return R4(),u(C(n,(Mu(),Hv)),15).a>=e.gc()}function Ryn(e){return cl(),!cc(e)&&!(!cc(e)&&e.c.i.c==e.d.i.c)}function dh(e){return u(Ra(e,oe(b7,K8,17,e.c.length,0,1)),323)}function Byn(e){UFe((!e.a&&(e.a=new pe(Jt,e,10,11)),e.a),new DM)}function Nae(){var e,n,t;return n=(t=(e=new hw,e),t),Ce(u7e,n),n}function xu(e,n,t,i,r,c){return WBe(e,n,t,c),H1e(e,i),G1e(e,r),e}function zLe(e,n,t,i){return e.a+=""+of(n==null?Vo:su(n),t,i),e}function KT(e,n){if(e<0||e>=n)throw $(new jo(WCn(e,n)));return e}function FLe(e,n,t){if(e<0||nt)throw $(new jo(vCn(e,n,t)))}function Ee(e,n,t,i){var r;r=new zM,r.a=n,r.b=t,r.c=i,Vt(e.b,r)}function Gi(e,n,t,i){var r;r=new zM,r.a=n,r.b=t,r.c=i,Vt(e.a,r)}function zyn(e,n,t){var i;i=FEn();try{return Kpn(e,n,t)}finally{i9n(i)}}function Xb(e){var n;return uu(e)?(n=e,n==-0?0:n):e8n(e)}function JLe(e,n){return X(n,45)?UQ(e.a,u(n,45)):!1}function HLe(e,n){return X(n,45)?UQ(e.a,u(n,45)):!1}function GLe(e,n){return X(n,45)?UQ(e.a,u(n,45)):!1}function Fyn(e,n){return e.a<=e.b?(n.Bd(e.a++),!0):!1}function Jyn(e){return z3(e).dc()?!1:(Own(e,new ae),!0)}function Dae(e){var n;return A0(e),n=new rt,x3(e.a,new Fke(n)),n}function BR(e){var n;return A0(e),n=new lt,x3(e.a,new Jke(n)),n}function Hyn(e){if(!("stack"in e))try{throw e}catch{}return e}function zR(e){return new xo((ll(e,gZ),fB(mc(mc(5,e),e/10|0))))}function qLe(e){return u(Ra(e,oe(cin,lQe,12,e.c.length,0,1)),2004)}function Gyn(e){return iV(e.e.Pd().gc()*e.c.Pd().gc(),273,new JU(e))}function ULe(){ULe=Y,$ln=It((ZP(),z(B(P6e,1),ke,477,0,[Wre])))}function XLe(){XLe=Y,Bln=It((e$(),z(B(Rln,1),ke,546,0,[Zre])))}function KLe(){KLe=Y,ifn=It((Cj(),z(B(i9e,1),ke,527,0,[CD])))}function VLe(){VLe=Y,eye=CIe(ve(1),ve(4)),Z4e=CIe(ve(1),ve(2))}function FR(){FR=Y,xre=new Sse("DFS",0),Pye=new Sse("BFS",1)}function JR(){JR=Y,bie=new wse(F8,0),zve=new wse("TOP_LEFT",1)}function Iae(e,n,t){this.d=new tEe(this),this.e=e,this.i=n,this.f=t}function _ae(e,n,t,i){this.d=e,this.n=n,this.g=t,this.o=i,this.p=-1}function qyn(e,n,t){e.d&&qo(e.d.e,e),e.d=n,e.d&&Pb(e.d.e,t,e)}function Uyn(e,n,t){var i;return i=d8(t),Fz(e.n,i,n),Fz(e.o,n,t),n}function B9(e,n){var t,i;return t=C2(e,n),i=null,t&&(i=t.qe()),i}function gE(e,n){var t,i;return t=O1(e,n),i=null,t&&(i=t.qe()),i}function Ow(e,n){var t,i;return t=O1(e,n),i=null,t&&(i=t.ne()),i}function N1(e,n){var t,i;return t=O1(e,n),i=null,t&&(i=D0e(t)),i}function wE(e,n){$Rn(n,e),afe(e.d),afe(u(C(e,(Ne(),pH)),213))}function eY(e,n){RRn(n,e),hfe(e.d),hfe(u(C(e,(Ne(),pH)),213))}function T0(e,n){_n(n),e.b=e.b-1&e.a.length-1,tr(e.a,e.b,n),kHe(e)}function Lae(e,n){_n(n),tr(e.a,e.c,n),e.c=e.c+1&e.a.length-1,kHe(e)}function jt(e){return at(e.b!=e.d.c),e.c=e.b,e.b=e.b.a,++e.a,e.c.c}function YLe(e){if(e.e.g!=e.b)throw $(new Nl);return!!e.c&&e.d>0}function x2(e){return X(e,18)?u(e,18).dc():!e.Jc().Ob()}function Xyn(e){return new pn(N8n(u(e.a.kd(),18).gc(),e.a.jd()),16)}function QLe(e){var n;n=e.Dh(),this.a=X(n,72)?u(n,72).Gi():n.Jc()}function Pae(e,n){var t;return t=u(Pa(e.b,n),66),!t&&(t=new Si),t}function Kyn(e,n){var t;t=n.a,lc(t,n.c.d),Gr(t,n.d.d),N2(t.a,e.n)}function WLe(e,n,t,i){return X(t,59)?new kOe(e,n,t,i):new Nfe(e,n,t,i)}function Vyn(){return Ff(),z(B(Ein,1),ke,413,0,[am,w7,p7,$te])}function Yyn(){return Lw(),z(B(itn,1),ke,409,0,[XN,UN,pte,mte])}function Qyn(){return Z9(),z(B(Xtn,1),ke,408,0,[op,sm,om,xv])}function Wyn(){return H9(),z(B(dJ,1),ke,309,0,[lte,fte,ate,hte])}function Zyn(){return X4(),z(B(y3e,1),ke,383,0,[ZS,v3e,Tte,Ote])}function e6n(){return LB(),z(B(hin,1),ke,367,0,[Pte,zJ,FJ,WN])}function n6n(){return BE(),z(B(mve,1),ke,301,0,[ix,wve,eD,pve])}function t6n(){return B2(),z(B(Qie,1),ke,203,0,[xH,Yie,Fv,zv])}function i6n(){return F1(),z(B(F4e,1),ke,269,0,[ob,z4e,ere,nre])}function r6n(){return eg(),z(B(pon,1),ke,404,0,[mD,Mx,TH,CH])}function c6n(e){var n;return e.j==(De(),bt)&&(n=Wqe(e),cs(n,et))}function u6n(){return Y4(),z(B(iye,1),ke,398,0,[IH,Ox,Nx,Dx])}function ZLe(e,n){return u(Js(p2(u(pi(e.k,n),16).Mc(),Mv)),113)}function ePe(e,n){return u(Js(x4(u(pi(e.k,n),16).Mc(),Mv)),113)}function o6n(e,n){return w4(new je(n.e.a+n.f.a/2,n.e.b+n.f.b/2),e)}function s6n(){return kz(),z(B(cln,1),ke,401,0,[Fre,Rre,zre,Bre])}function l6n(){return az(),z(B(r6e,1),ke,354,0,[Lre,t6e,i6e,n6e])}function f6n(){return OE(),z(B(Lye,1),ke,353,0,[Sre,RH,Ere,jre])}function a6n(){return u8(),z(B(n8e,1),ke,278,0,[$D,uG,Z9e,e8e])}function h6n(){return z1(),z(B(Ace,1),ke,222,0,[xce,RD,H7,Gy])}function d6n(){return al(),z(B(Wfn,1),ke,292,0,[zD,s1,hb,BD])}function b6n(){return UR(),z(B(KD,1),ke,288,0,[S8e,A8e,Oce,x8e])}function g6n(){return Vs(),z(B(tA,1),ke,380,0,[qD,Og,GD,Lm])}function w6n(){return KB(),z(B(O8e,1),ke,326,0,[Nce,M8e,T8e,C8e])}function p6n(){return PB(),z(B(wan,1),ke,407,0,[Dce,D8e,N8e,I8e])}function Ll(e,n,t){return n<0?kW(e,t):u(t,69).uk().zk(e,e.ei(),n)}function m6n(e,n,t){var i;return i=d8(t),Fz(e.f,i,n),ei(e.g,n,t),n}function v6n(e,n,t){var i;return i=d8(t),Fz(e.p,i,n),ei(e.q,n,t),n}function nPe(e){var n,t;return n=(v0(),t=new w3,t),e&&Dz(n,e),n}function $ae(e){var n;return n=e.$i(e.i),e.i>0&&Wu(e.g,0,n,0,e.i),n}function I4(e){return Mj(),X(e.g,156)?u(e.g,156):null}function y6n(e){return $R(),so($ce,e)?u(Bn($ce,e),342).Pg():null}function k6n(e){e.a=null,e.e=null,Qp(e.b.c,0),Qp(e.f.c,0),e.c=null}function tPe(e,n){var t;for(t=e.j.c.length;t>24}function E6n(e){if(e.p!=1)throw $(new is);return Rt(e.k)<<24>>24}function S6n(e){if(e.p!=7)throw $(new is);return Rt(e.k)<<16>>16}function x6n(e){if(e.p!=7)throw $(new is);return Rt(e.f)<<16>>16}function H3(e,n){return n.e==0||e.e==0?KS:(A8(),CW(e,n))}function cPe(e,n){return ue(n)===ue(e)?"(this Map)":n==null?Vo:su(n)}function A6n(e,n,t){return dV(re(du(Xc(e.f,n))),re(du(Xc(e.f,t))))}function M6n(e,n,t){var i;i=u(Bn(e.g,t),60),Ce(e.a.c,new jc(n,i))}function uPe(e,n){var t;return t=new o4,e.Ed(t),t.a+="..",n.Fd(t),t.a}function aa(e){var n;for(n=0;e.Ob();)e.Pb(),n=mc(n,1);return fB(n)}function C6n(e,n,t,i,r){var c;c=zOn(r,t,i),Ce(n,GCn(r,c)),RMn(e,r,n)}function oPe(e,n,t){e.i=0,e.e=0,n!=t&&(Gze(e,n,t),Hze(e,n,t))}function sPe(e,n,t,i){this.e=null,this.c=e,this.d=n,this.a=t,this.b=i}function Rae(e,n,t,i,r){this.i=e,this.a=n,this.e=t,this.j=i,this.f=r}function lPe(e,n){hae.call(this),this.a=e,this.b=n,Ce(this.a.b,this)}function D1(e,n){mh(),zb.call(this,e,1,z(B($t,1),ni,30,15,[n]))}function T6n(e,n,t){return T8(e,n,t,X(n,103)&&(u(n,19).Bb&Ec)!=0)}function HR(e,n,t){return Hz(e,n,t,X(n,103)&&(u(n,19).Bb&Ec)!=0)}function O6n(e,n,t){return DOn(e,n,t,X(n,103)&&(u(n,19).Bb&Ec)!=0)}function Bae(e,n){return e==(zn(),Qi)&&n==Qi?4:e==Qi||n==Qi?8:32}function N6n(e,n){return u(n==null?du(Xc(e.f,null)):Dj(e.i,n),290)}function fPe(e,n){var t;for(t=n;t;)a2(e,t.i,t.j),t=Bi(t);return e}function Vu(e){return e.n||(Ms(e),e.n=new $De(e,Rc,e),tu(e)),e.n}function Xh(e,n){Tc();var t;return t=u(e,69).tk(),QMn(t,n),t.vl(n)}function pE(e){return at(e.a"+Aae(e.d):"e_"+vw(e)}function I6n(e,n){var t;return t=n!=null?lo(e,n):du(Xc(e.f,n)),O$(t)}function _6n(e,n){var t;return t=n!=null?lo(e,n):du(Xc(e.f,n)),O$(t)}function bPe(e,n){var t;for(t=0;t=0&&e.a[t]===n[t];t--);return t<0}function B6n(e,n){var t,i;i=!1;do t=Dze(e,n),i=i|t;while(t);return i}function mE(){mE=Y,Tx=new vse("UPPER",0),Cx=new vse("LOWER",1)}function qR(){qR=Y,Sie=new pse(ma,0),Eie=new pse("ALTERNATING",1)}function UR(){UR=Y,S8e=new gDe,A8e=new QDe,Oce=new E_e,x8e=new WDe}function wPe(){wPe=Y,_in=It((nB(),z(B(gve,1),ke,422,0,[bve,Xte])))}function pPe(){pPe=Y,Rin=It((fO(),z(B(Sve,1),ke,419,0,[XJ,Eve])))}function mPe(){mPe=Y,Jin=It((rO(),z(B(Mve,1),ke,476,0,[Ave,VJ])))}function vPe(){vPe=Y,Yin=It((JR(),z(B(Fve,1),ke,420,0,[bie,zve])))}function yPe(){yPe=Y,Zin=It((qR(),z(B(n5e,1),ke,423,0,[Sie,Eie])))}function kPe(){kPe=Y,Uun=It((uO(),z(B(J4e,1),ke,421,0,[tre,ire])))}function jPe(){jPe=Y,Eon=It((mE(),z(B(jon,1),ke,518,0,[Tx,Cx])))}function EPe(){EPe=Y,Don=It((Da(),z(B(Non,1),ke,508,0,[Ag,Ya])))}function SPe(){SPe=Y,Oon=It((ah(),z(B(Ton,1),ke,509,0,[pp,Ud])))}function xPe(){xPe=Y,Xon=It((ha(),z(B(Uon,1),ke,515,0,[Am,sb])))}function APe(){APe=Y,esn=It((Tw(),z(B(Zon,1),ke,454,0,[lb,Jv])))}function MPe(){MPe=Y,Msn=It((FR(),z(B($ye,1),ke,425,0,[xre,Pye])))}function CPe(){CPe=Y,Dsn=It((SB(),z(B(Rye,1),ke,487,0,[BH,qv])))}function TPe(){TPe=Y,Lsn=It((iB(),z(B(zye,1),ke,426,0,[Bye,Nre])))}function OPe(){OPe=Y,Nln=It((VR(),z(B(T6e,1),ke,478,0,[Kre,C6e])))}function NPe(){NPe=Y,zln=It((WT(),z(B($6e,1),ke,428,0,[ece,YH])))}function DPe(){DPe=Y,rfn=It((pO(),z(B(c9e,1),ke,427,0,[WH,r9e])))}function IPe(){IPe=Y,dtn=It((lB(),z(B(n3e,1),ke,424,0,[vte,pJ])))}function _Pe(){_Pe=Y,lin=It((K9(),z(B(sin,1),ke,502,0,[QN,Dte])))}function XR(e){w0e(),YTe(this,Rt(Rr(kw(e,24),rF)),Rt(Rr(e,rF)))}function z6n(e){return(e.k==(zn(),Qi)||e.k==wr)&&bi(e,(me(),ox))}function F6n(e,n,t){return u(n==null?Ko(e.f,null,t):Pw(e.i,n,t),290)}function J6n(){return vr(),z(B(Vx,1),ke,86,0,[eh,ru,Zc,Za,Vl])}function H6n(){return De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])}function G6n(e){return JP(),function(){return zyn(e,this,arguments)}}function LPe(e,n){var t;return t=n.jd(),new bw(t,e.e.pc(t,u(n.kd(),18)))}function PPe(e,n){var t,i;return t=n.jd(),i=e.De(t),!!i&&Ku(i.e,n.kd())}function rc(e,n){var t,i;for(_n(n),i=e.Jc();i.Ob();)t=i.Pb(),n.Ad(t)}function ol(e,n,t){var i;return i=(vn(n,e.c.length),e.c[n]),e.c[n]=t,i}function Hae(e,n){var t,i;for(t=n,i=0;t>0;)i+=e.a[t],t-=t&-t;return i}function $Pe(e,n){var t;for(t=n;t;)a2(e,-t.i,-t.j),t=Bi(t);return e}function q6n(e,n){var t;return t=e.a.get(n),t??oe(Ar,On,1,0,5,1)}function G3(e,n){return(R0(e),b9(new wn(e,new whe(n,e.a)))).zd(vy)}function U6n(){return zr(),z(B(p3e,1),ke,363,0,[Kf,r1,eo,no,Pc])}function RPe(e){eYe(),KSe(this),this.a=new Si,x1e(this,e),Vt(this.a,e)}function BPe(){MK(this),this.b=new je(Ki,Ki),this.a=new je(Dr,Dr)}function uY(e){KR(),!Ka&&(this.c=e,this.e=!0,this.a=new Te)}function KR(){KR=Y,Ka=!0,pnn=!1,mnn=!1,ynn=!1,vnn=!1}function VR(){VR=Y,Kre=new Mse(bwe,0),C6e=new Mse("TARGET_WIDTH",1)}function X6n(){return vz(),z(B(Isn,1),ke,364,0,[Tre,Are,Ore,Mre,Cre])}function K6n(){return z2(),z(B(xin,1),ke,371,0,[ZN,GJ,qJ,HJ,JJ])}function V6n(){return HE(),z(B(_4e,1),ke,328,0,[I4e,Wie,Zie,jx,Ex])}function Y6n(){return Xs(),z(B(e5e,1),ke,165,0,[sD,fx,V1,ax,yg])}function Q6n(){return nS(),z(B(_ln,1),ke,369,0,[Uv,$y,Jx,Fx,MD])}function W6n(){return JO(),z(B(F6e,1),ke,330,0,[R6e,nce,z6e,tce,B6e])}function Z6n(){return ph(),z(B(Qa,1),ke,160,0,[Mn,fr,Sa,Kd,Q1])}function e9n(){return nv(),z(B(Zx,1),ke,257,0,[db,FD,g8e,Wx,w8e])}function oY(e,n){var t;return t=u(Pa(e.d,n),21),t||u(Pa(e.e,n),21)}function zPe(e){this.b=e,ut.call(this,e),this.a=u(Un(this.b.a,4),129)}function FPe(e){this.b=e,m4.call(this,e),this.a=u(Un(this.b.a,4),129)}function JPe(e,n){this.c=0,this.b=n,cTe.call(this,e,17493),this.a=this.c}function Pf(e,n,t,i,r){VPe.call(this,n,i,r),this.c=e,this.b=t}function Gae(e,n,t,i,r){pLe.call(this,n,i,r),this.c=e,this.a=t}function qae(e,n,t,i,r){mLe.call(this,n,i,r),this.c=e,this.a=t}function Uae(e,n,t,i,r){VPe.call(this,n,i,r),this.c=e,this.a=t}function Xae(e,n,t){e.a.c.length=0,MPn(e,n,t),e.a.c.length==0||ZIn(e,n)}function VT(e){e.i=0,rT(e.b,null),rT(e.c,null),e.a=null,e.e=null,++e.g}function n9n(e){return e.e=3,e.d=e.Yb(),e.e!=2?(e.e=0,!0):!1}function Kae(e,n){return X(n,144)?bn(e.c,u(n,144).c):!1}function HPe(e){var n;return e.c||(n=e.r,X(n,88)&&(e.c=u(n,29))),e.c}function Ms(e){return e.t||(e.t=new $Se(e),$E(new nAe(e),0,e.t)),e.t}function cc(e){return!e.c||!e.d?!1:!!e.c.i&&e.c.i==e.d.i}function _4(e,n){return n==0||e.e==0?e:n>0?fJe(e,n):QUe(e,-n)}function Vae(e,n){return n==0||e.e==0?e:n>0?QUe(e,n):fJe(e,-n)}function tt(e){if(ht(e))return e.c=e.a,e.a.Pb();throw $(new au)}function GPe(e){var n;return n=e.length,bn($n.substr($n.length-n,n),e)}function qPe(e){var n,t;return n=e.c.i,t=e.d.i,n.k==(zn(),wr)&&t.k==wr}function sY(e){var n,t,i;return n=e&Ls,t=e>>22&Ls,i=e<0?G1:0,_o(n,t,i)}function t9n(e,n){var t,i;t=u(Ykn(e.c,n),18),t&&(i=t.gc(),t.$b(),e.d-=i)}function i9n(e){e&&c8n((Coe(),yme)),--oJ,e&&sJ!=-1&&(Jgn(sJ),sJ=-1)}function Yae(e){_gn.call(this,e==null?Vo:su(e),X(e,80)?u(e,80):null)}function lY(e){var n;return n=new Mw,$u(n,e),he(n,(Ne(),Wc),null),n}function fY(e,n,t){var i;return i=e.Fh(n),i>=0?e.Ih(i,t,!0):Gw(e,n,t)}function r9n(e,n,t){return ki(w4(b8(e),pc(n.b)),w4(b8(e),pc(t.b)))}function c9n(e,n,t){return ki(w4(b8(e),pc(n.e)),w4(b8(e),pc(t.e)))}function u9n(e,n){return k.Math.min(N0(n.a,e.d.d.c),N0(n.b,e.d.d.c))}function UPe(e,n,t){var i;i=new Vse(e.a),xE(i,e.a.a),Ko(i.f,n,t),e.a.a=i}function Qae(e,n,t,i){var r;for(r=0;rn)throw $(new jo(F0e(e,n,"index")));return e}function ehe(e){var n;return n=e.e+e.f,isNaN(n)&&X$(e.d)?e.d:n}function s9n(e,n){var t;t=e.q.getHours()+(n/60|0),e.q.setMinutes(n),oS(e,t)}function nhe(e,n){var t,i;return t=(_n(e),e),i=(_n(n),n),t==i?0:tn.p?-1:0}function n$e(e,n){return so(e.a,n)?(L4(e.a,n),!0):!1}function h9n(e){var n,t;return n=e.jd(),t=u(e.kd(),18),NT(t.Lc(),new Z6(n))}function hY(e){var n;return n=e.b,n.b==0?null:u(Yu(n,0),65).b}function WR(e,n){return _n(n),e.c=0,"Initial capacity must not be negative")}function eB(){eB=Y,Hx=new yi("org.eclipse.elk.labels.labelManager")}function i$e(){i$e=Y,lve=new Li("separateLayerConnections",(LB(),Pte))}function ha(){ha=Y,Am=new jse("REGULAR",0),sb=new jse("CRITICAL",1)}function WT(){WT=Y,ece=new Cse("FIXED",0),YH=new Cse("CENTER_NODE",1)}function nB(){nB=Y,bve=new dse("QUADRATIC",0),Xte=new dse("SCANLINE",1)}function r$e(){r$e=Y,Pin=It((pB(),z(B(yve,1),ke,350,0,[vve,UJ,Kte])))}function c$e(){c$e=Y,zin=It((Wb(),z(B(Bin,1),ke,449,0,[tie,k7,Ov])))}function u$e(){u$e=Y,Uin=It((W9(),z(B(hie,1),ke,302,0,[fie,aie,tD])))}function o$e(){o$e=Y,Xin=It((_0(),z(B(die,1),ke,329,0,[iD,Bve,dm])))}function s$e(){s$e=Y,Vin=It((_1(),z(B(Kin,1),ke,315,0,[rD,Dv,Sy])))}function l$e(){l$e=Y,Nin=It((_w(),z(B(Rte,1),ke,368,0,[lp,ib,sp])))}function f$e(){f$e=Y,Fun=It((IE(),z(B(D4e,1),ke,352,0,[Vie,N4e,SH])))}function a$e(){a$e=Y,Kun=It((Nc(),z(B(Xun,1),ke,452,0,[xx,ys,Do])))}function h$e(){h$e=Y,Vun=It((DB(),z(B(q4e,1),ke,381,0,[H4e,rre,G4e])))}function d$e(){d$e=Y,Yun=It((DE(),z(B(U4e,1),ke,348,0,[ure,cre,pD])))}function b$e(){b$e=Y,Qun=It((r8(),z(B(K4e,1),ke,349,0,[ore,X4e,Ax])))}function g$e(){g$e=Y,Wun=It((wB(),z(B(Q4e,1),ke,351,0,[Y4e,sre,V4e])))}function w$e(){w$e=Y,Zun=It((IB(),z(B(W4e,1),ke,382,0,[lre,I7,xm])))}function p$e(){p$e=Y,isn=It((NE(),z(B(gye,1),ke,385,0,[bye,hre,yD])))}function m$e(){m$e=Y,Psn=It((EO(),z(B(Hye,1),ke,386,0,[zH,Fye,Jye])))}function v$e(){v$e=Y,iln=It((NB(),z(B(o6e,1),ke,303,0,[Pre,u6e,c6e])))}function y$e(){y$e=Y,rln=It((WB(),z(B(s6e,1),ke,436,0,[Px,HH,$re])))}function k$e(){k$e=Y,Dln=It((rB(),z(B(D6e,1),ke,429,0,[Vre,N6e,O6e])))}function j$e(){j$e=Y,Iln=It((JB(),z(B(L6e,1),ke,430,0,[I6e,_6e,Yre])))}function E$e(){E$e=Y,Pln=It((CO(),z(B(Qre,1),ke,435,0,[XH,KH,VH])))}function S$e(){S$e=Y,lln=It((XB(),z(B(a6e,1),ke,387,0,[f6e,Gre,l6e])))}function x$e(){x$e=Y,Btn=It((yE(),z(B(w3e,1),ke,384,0,[Ete,jte,Ste])))}function A$e(){A$e=Y,knn=It((zl(),z(B(Qo,1),ke,130,0,[Kme,Yo,Vme])))}function M$e(){M$e=Y,Tnn=It((ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])))}function C$e(){C$e=Y,Nnn=It((ws(),z(B(Onn,1),ke,461,0,[Ch,nb,Uf])))}function T$e(){T$e=Y,Inn=It((Uo(),z(B(Dnn,1),ke,462,0,[ka,tb,Xf])))}function O$e(){O$e=Y,Xfn=It(($a(),z(B(t8e,1),ke,279,0,[F7,_m,J7])))}function N$e(){N$e=Y,han=It((G4(),z(B(E8e,1),ke,281,0,[j8e,Pm,dG])))}function D$e(){D$e=Y,Qfn=It((B1(),z(B(b8e,1),ke,347,0,[oG,Yd,Qx])))}function I$e(){I$e=Y,lan=It((jE(),z(B(y8e,1),ke,300,0,[HD,Cce,v8e])))}function da(e,n){return!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),SQ(e.o,n)}function b9n(e){return!e.g&&(e.g=new J6),!e.g.d&&(e.g.d=new _Se(e)),e.g.d}function g9n(e){return!e.g&&(e.g=new J6),!e.g.b&&(e.g.b=new ISe(e)),e.g.b}function ZT(e){return!e.g&&(e.g=new J6),!e.g.c&&(e.g.c=new PSe(e)),e.g.c}function w9n(e){return!e.g&&(e.g=new J6),!e.g.a&&(e.g.a=new LSe(e)),e.g.a}function p9n(e,n,t,i){return t&&(i=t.Oh(n,zi(t.Ah(),e.c.sk()),null,i)),i}function m9n(e,n,t,i){return t&&(i=t.Qh(n,zi(t.Ah(),e.c.sk()),null,i)),i}function dY(e,n,t,i){var r;return r=oe($t,ni,30,n+1,15,1),__n(r,e,n,t,i),r}function oe(e,n,t,i,r,c){var o;return o=hHe(r,i),r!=10&&z(B(e,c),n,t,r,o),o}function v9n(e,n,t){var i,r;for(r=new Y9(n,e),i=0;it||n=0?e.Ih(t,!0,!0):Gw(e,n,!0)}function eO(e,n){var t,i,r;return r=e.r,i=e.d,t=fS(e,n,!0),t.b!=r||t.a!=i}function $$e(e,n){return PMe(e.e,n)||tg(e.e,n,new PJe(n)),u(Pa(e.e,n),113)}function Cs(e,n,t,i){return _n(e),_n(n),_n(t),_n(i),new Bfe(e,n,new Eu)}function nO(e,n,t){var i,r;return r=(i=E8(e.b,n),i),r?Kz(oO(e,r),t):null}function L9n(e,n,t){var i,r,c;i=O1(e,t),r=null,i&&(r=D0e(i)),c=r,IJe(n,t,c)}function P9n(e,n,t){var i,r,c;i=O1(e,t),r=null,i&&(r=D0e(i)),c=r,IJe(n,t,c)}function os(e,n,t,i){this.$j(),this.a=n,this.b=e,this.c=new Lfe(this,n,t,i)}function pY(e,n,t,i,r,c){_ae.call(this,n,i,r,c),this.c=e,this.b=t}function tO(e,n,t,i,r,c){_ae.call(this,n,i,r,c),this.c=e,this.a=t}function ghe(e,n,t,i,r){zTe(this),this.b=e,this.d=n,this.f=t,this.g=i,this.c=r}function whe(e,n){N$.call(this,n.xd(),n.wd()&-16449),_n(e),this.a=e,this.c=n}function $9n(e,n){e.a.Le(n.d,e.b)>0&&(Ce(e.c,new ofe(n.c,n.d,e.d)),e.b=n.d)}function mY(e){e.a=oe($t,ni,30,e.b+1,15,1),e.c=oe($t,ni,30,e.b,15,1),e.d=0}function R9n(e,n,t){var i;return i=Rze(e,n,t),e.b=new AB(i.c.length),Dbe(e,i)}function B9n(e){if(e.b<=0)throw $(new au);return--e.b,e.a-=e.c.c,ve(e.a)}function z9n(e){var n;if(!e.a)throw $(new s_e);return n=e.a,e.a=Bi(e.a),n}function R$e(e){var n;if(e.ll())for(n=e.i-1;n>=0;--n)K(e,n);return $ae(e)}function $4(e){var n;return Nt(e),X(e,204)?(n=u(e,204),n):new e9(e)}function F9n(e){for(;!e.a;)if(!ENe(e.c,new Hke(e)))return!1;return!0}function phe(e,n){if(e.g==null||n>=e.i)throw $(new jK(n,e.i));return e.g[n]}function B$e(e,n,t){if(t8(e,t),t!=null&&!e.dk(t))throw $(new bX);return t}function vY(e,n){return lO(n)!=10&&z(Us(n),n.Qm,n.__elementTypeId$,lO(n),e),e}function z$e(e,n){var t,i;return i=n/e.c.Pd().gc()|0,t=n%e.c.Pd().gc(),P4(e,i,t)}function J9(e,n,t,i){var r;i=(Aw(),i||Fme),r=e.slice(n,t),J0e(r,e,n,t,-n,i)}function Pl(e,n,t,i,r){return n<0?Gw(e,t,i):u(t,69).uk().wk(e,e.ei(),n,i,r)}function J9n(e,n){return ki(ne(re(C(e,(me(),hp)))),ne(re(C(n,hp))))}function F$e(){F$e=Y,gnn=It((H9(),z(B(dJ,1),ke,309,0,[lte,fte,ate,hte])))}function H9(){H9=Y,lte=new c$("All",0),fte=new ATe,ate=new RTe,hte=new MTe}function ws(){ws=Y,Ch=new qX(ly,0),nb=new qX(F8,1),Uf=new qX(fy,2)}function J$e(){J$e=Y,Gz(),b7e=Ki,vhn=Dr,g7e=new Zn(Ki),yhn=new Zn(Dr)}function tB(){tB=Y,ofn=new g3,lfn=new eL,sfn=tkn((Xt(),jce),ofn,ab,lfn)}function H9n(e){tB(),u(e.mf((Xt(),Nm)),182).Ec((ps(),JD)),e.of(jce,null)}function G9n(e){return X(e,180)?""+u(e,180).a:e==null?null:su(e)}function q9n(e){return X(e,180)?""+u(e,180).a:e==null?null:su(e)}function mhe(e){var n,t;if(!e.b)return null;for(t=e.b;n=t.a[0];)t=n;return t}function H$e(e){var n,t;if(!e.b)return null;for(t=e.b;n=t.a[1];)t=n;return t}function iO(e){var n;for(n=e.p+1;n=0?uz(e,t,!0,!0):Gw(e,n,!0)}function Q9n(e,n){k4(u(u(e.f,26).mf((Xt(),Kx)),102))&&UFe(iae(u(e.f,26)),n)}function pRe(e,n){Os(e,n==null||X$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function mRe(e,n){Ns(e,n==null||X$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function vRe(e,n){Iw(e,n==null||X$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function yRe(e,n){Dw(e,n==null||X$((_n(n),n))||isNaN((_n(n),n))?0:(_n(n),n))}function kRe(e){(this.q?this.q:(jn(),jn(),i1)).zc(e.q?e.q:(jn(),jn(),i1))}function SY(e,n,t){var i;return i=e.g[n],Yj(e,n,e.Xi(n,t)),e.Pi(n,t,i),e.Li(),i}function sB(e,n){var t;return t=e.bd(n),t>=0?(e.ed(t),!0):!1}function xY(e){var n;return e.d!=e.r&&(n=ff(e),e.e=!!n&&n.jk()==ZZe,e.d=n),e.e}function AY(e,n){var t;for(Nt(e),Nt(n),t=!1;n.Ob();)t=t|e.Ec(n.Pb());return t}function Pa(e,n){var t;return t=u(Bn(e.e,n),393),t?(VTe(e,t),t.e):null}function jRe(e){var n,t;return n=e/60|0,t=e%60,t==0?""+n:""+n+":"+(""+t)}function ou(e,n){var t,i;return R0(e),i=new the(n,e.a),t=new kNe(i),new wn(e,t)}function C2(e,n){var t=e.a[n],i=(QY(),ite)[typeof t];return i?i(t):F1e(typeof t)}function W9n(e,n){var t,i,r;r=n.c.i,t=u(Bn(e.f,r),60),i=t.d.c-t.e.c,Zhe(n.a,i,0)}function Kh(e,n,t){var i,r;for(i=10,r=0;r=0;)++n[0]}function MRe(e,n,t,i){fi(),aw.call(this,26),this.c=e,this.a=n,this.d=t,this.b=i}function L1(e,n,t,i,r,c,o){DY.call(this,n,i,r,c,o),this.c=e,this.b=t}function CRe(e){this.g=e,this.f=new Te,this.a=k.Math.min(this.g.c.c,this.g.d.c)}function kE(){kE=Y,Qtn=new xp,Wtn=new Ap,Vtn=new Mp,Ytn=new Cp,Ztn=new xl}function lB(){lB=Y,vte=new fse("EADES",0),pJ=new fse("FRUCHTERMAN_REINGOLD",1)}function fO(){fO=Y,XJ=new bse("READING_DIRECTION",0),Eve=new bse("ROTATION",1)}function TRe(){TRe=Y,Ain=It((z2(),z(B(xin,1),ke,371,0,[ZN,GJ,qJ,HJ,JJ])))}function ORe(){ORe=Y,Hun=It((HE(),z(B(_4e,1),ke,328,0,[I4e,Wie,Zie,jx,Ex])))}function NRe(){NRe=Y,Win=It((Xs(),z(B(e5e,1),ke,165,0,[sD,fx,V1,ax,yg])))}function DRe(){DRe=Y,_sn=It((vz(),z(B(Isn,1),ke,364,0,[Tre,Are,Ore,Mre,Cre])))}function IRe(){IRe=Y,Lln=It((nS(),z(B(_ln,1),ke,369,0,[Uv,$y,Jx,Fx,MD])))}function _Re(){_Re=Y,Fln=It((JO(),z(B(F6e,1),ke,330,0,[R6e,nce,z6e,tce,B6e])))}function LRe(){LRe=Y,Htn=It((zr(),z(B(p3e,1),ke,363,0,[Kf,r1,eo,no,Pc])))}function PRe(){PRe=Y,qfn=It((vr(),z(B(Vx,1),ke,86,0,[eh,ru,Zc,Za,Vl])))}function $Re(){$Re=Y,ffn=It((ph(),z(B(Qa,1),ke,160,0,[Mn,fr,Sa,Kd,Q1])))}function RRe(){RRe=Y,nan=It((nv(),z(B(Zx,1),ke,257,0,[db,FD,g8e,Wx,w8e])))}function BRe(){BRe=Y,ran=It((De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])))}function zRe(e){var n;return n=u(C(e,(me(),fp)),317),n?n.a==e:!1}function FRe(e){var n;return n=u(C(e,(me(),fp)),317),n?n.i==e:!1}function JRe(e,n){return _n(n),Dfe(e),e.d.Ob()?(n.Ad(e.d.Pb()),!0):!1}function fB(e){return ao(e,oi)>0?oi:ao(e,Xr)<0?Xr:Rt(e)}function o8n(e,n){var t;return t=$w(e.e.c,n.e.c),t==0?ki(e.e.d,n.e.d):t}function CY(e,n){var t;return t=u(Bn(e.a,n),150),t||(t=new Mt,ei(e.a,n,t)),t}function Rf(e,n,t){var i;if(n==null)throw $(new e4);return i=O1(e,n),D6n(e,n,t),i}function s8n(e,n){var t,i;for(i=n.c,t=i+1;t<=n.f;t++)e.a[t]>e.a[i]&&(i=t);return i}function l8n(e,n,t){var i;return i=e.a.e[u(n.a,9).p]-e.a.e[u(t.a,9).p],sc(LT(i))}function f8n(e,n,t){var i,r;for(r=new L(t);r.a0?n-1:n,wAe(ugn(aBe(dfe(new i4,t),e.n),e.j),e.k)}function p8n(e,n,t,i){var r;e.j=-1,nbe(e,I0e(e,n,t),(Tc(),r=u(n,69).tk(),r.vl(i)))}function XRe(e,n,t,i,r,c){var o;o=lY(i),lc(o,r),Gr(o,c),gn(e.a,i,new Y$(o,n,t.f))}function aB(e,n){var t;return R0(e),t=new e_e(e,e.a.xd(),e.a.wd()|4,n),new wn(e,t)}function m8n(e,n){var t,i;return t=u(L2(e.d,n),18),t?(i=n,e.e.pc(i,t)):null}function An(e,n){var t;return t=(e.i==null&&vh(e),e.i),n>=0&&n=-.01&&e.a<=Ga&&(e.a=0),e.b>=-.01&&e.b<=Ga&&(e.b=0),e}function q3(e){x8();var n,t;for(t=Fpe,n=0;nt&&(t=e[n]);return t}function v8n(e){var n;return n=ne(re(C(e,(Ne(),Gd)))),n<0&&(n=0,he(e,Gd,n)),n}function y8n(e,n){k4(u(C(u(e.e,9),(Ne(),Wi)),102))&&(jn(),Tr(u(e.e,9).j,n))}function hB(e,n){var t,i;for(i=e.Jc();i.Ob();)t=u(i.Pb(),70),he(t,(me(),Ty),n)}function k8n(e,n){var t,i,r;for(i=n.a.jd(),t=u(n.a.kd(),18).gc(),r=0;re||e>n)throw $(new Ioe("fromIndex: 0, toIndex: "+e+Xge+n))}function QRe(e,n){ji(e,(Yh(),Hre),n.f),ji(e,sln,n.e),ji(e,Jre,n.d),ji(e,oln,n.c)}function Ao(e,n){var t,i,r,c;for(_n(n),i=e.c,r=0,c=i.length;r0&&(e.a/=n,e.b/=n),e}function WRe(e,n,t){var i,r;i=n;do r=ne(e.p[i.p])+t,e.p[i.p]=r,i=e.a[i.p];while(i!=n)}function sl(e){var n;return e.w?e.w:(n=wyn(e),n&&!n.Sh()&&(e.w=n),n)}function Ahe(e,n){return Oa(),Bf(X0),k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)}function T8n(e){var n;return e==null?null:(n=u(e,195),pMn(n,n.length))}function K(e,n){if(e.g==null||n>=e.i)throw $(new jK(n,e.i));return e.Ui(n,e.g[n])}function ga(){ga=Y,Ou=new GX("BEGIN",0),No=new GX(F8,1),Nu=new GX("END",2)}function $a(){$a=Y,F7=new wK(F8,0),_m=new wK("HEAD",1),J7=new wK("TAIL",2)}function R4(){R4=Y,Tsn=wh(wh(wh(Oj(new or,(Y4(),Ox)),(cS(),are)),oye),aye)}function P1(){P1=Y,Nsn=wh(wh(wh(Oj(new or,(Y4(),Dx)),(cS(),lye)),rye),sye)}function U3(e,n){return agn(AE(e,n,Rt(ac(Zh,Uh(Rt(ac(n==null?0:Oi(n),e1)),15)))))}function Mhe(e,n){return Oa(),Bf(X0),k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)}function q9(e,n){var t,i;i=e.a,t=fjn(e,n,null),i!=n&&!e.e&&(t=D8(e,n,t)),t&&t.mj()}function O8n(e,n){var t;return t=Nr(pc(u(Bn(e.g,n),8)),Use(u(Bn(e.f,n),460).b)),t}function ZRe(e,n,t){var i=function(){return e.apply(i,arguments)};return n.apply(i,t),i}function B4(e){var n;return nE(e==null||Array.isArray(e)&&(n=lO(e),!(n>=14&&n<=16))),e}function Che(e){e.b=(ws(),nb),e.f=(Uo(),tb),e.d=(ll(2,Q2),new xo(2)),e.e=new Kr}function dB(e){this.b=(Nt(e),new bs(e)),this.a=new Te,this.d=new Te,this.e=new Kr}function eBe(e){return R0(e),E4(!0,"n may not be negative"),new wn(e,new vBe(e.a))}function N8n(e,n){jn();var t,i;for(i=new Te,t=0;t0?u(Le(t.a,i-1),9):null}function Bf(e){if(!(e>=0))throw $(new Gn("tolerance ("+e+") must be >= 0"));return e}function EE(){return uce||(uce=new DXe,H4(uce,z(B(yy,1),On,148,0,[new CC]))),uce}function wB(){wB=Y,Y4e=new cK("NO",0),sre=new cK(bwe,1),V4e=new cK("LOOK_BACK",2)}function Nc(){Nc=Y,xx=new nK(vS,0),ys=new nK("INPUT",1),Do=new nK("OUTPUT",2)}function pB(){pB=Y,vve=new KX("ARD",0),UJ=new KX("MSD",1),Kte=new KX("MANUAL",2)}function R8n(){return KO(),z(B(jve,1),ke,267,0,[Qte,kve,Zte,eie,Wte,nie,nD,Yte,Vte])}function B8n(){return YO(),z(B(O4e,1),ke,268,0,[Kie,M4e,C4e,Uie,A4e,T4e,EH,qie,Xie])}function z8n(){return _s(),z(B(k8e,1),ke,266,0,[q7,XD,lG,iA,fG,hG,aG,Tce,UD])}function F8n(){yMe();for(var e=Xne,n=0;nt)throw $(new b2(n,t));return new Xle(e,n)}function mB(e){var n,t;for(t=e.c.Bc().Jc();t.Ob();)n=u(t.Pb(),18),n.$b();e.c.$b(),e.d=0}function J8n(e){var n,t,i,r;for(t=e.a,i=0,r=t.length;i=0),xEn(e.d,e.c)<0&&(e.a=e.a-1&e.d.a.length-1,e.b=e.d.c),e.c=-1}function vBe(e){N$.call(this,e.yd(64)?Gse(0,lf(e.xd(),1)):hN,e.wd()),this.b=1,this.a=e}function yBe(){cle.call(this),this.n=-1,this.g=null,this.i=null,this.j=null,this.Bb|=qf}function kBe(e,n,t,i){this.$j(),this.a=n,this.b=e,this.c=null,this.c=new pNe(this,n,t,i)}function DY(e,n,t,i,r){this.d=e,this.n=n,this.g=t,this.o=i,this.p=-1,r||(this.o=-2-i-1)}function jBe(e){Woe(),this.g=new pt,this.f=new pt,this.b=new pt,this.c=new Cw,this.i=e}function $he(){this.f=new Kr,this.d=new moe,this.c=new Kr,this.a=new Te,this.b=new Te}function G8n(e){var n,t;for(t=new L(mHe(e));t.a=0}function Rhe(){Rhe=Y,oon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function EBe(){EBe=Y,son=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function Bhe(){Bhe=Y,lon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function SBe(){SBe=Y,fon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function xBe(){xBe=Y,aon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function ABe(){ABe=Y,hon=qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)}function MBe(){MBe=Y,gon=Eo(qt(qt(new or,(zr(),eo),(Ur(),DJ)),no,MJ),Pc,NJ)}function CBe(){CBe=Y,enn=z(B($t,1),ni,30,15,[0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15])}function zhe(e,n){var t;t=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,0,t,e.b))}function Fhe(e,n){var t;t=e.c,e.c=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,1,t,e.c))}function IY(e,n){var t;t=e.c,e.c=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,4,t,e.c))}function Jhe(e,n){var t;t=e.c,e.c=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,1,t,e.c))}function Hhe(e,n){var t;t=e.d,e.d=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,1,t,e.d))}function X9(e,n){var t;t=e.k,e.k=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,2,t,e.k))}function _Y(e,n){var t;t=e.D,e.D=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,2,t,e.D))}function kB(e,n){var t;t=e.f,e.f=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,8,t,e.f))}function jB(e,n){var t;t=e.i,e.i=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,7,t,e.i))}function Ghe(e,n){var t;t=e.a,e.a=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,8,t,e.a))}function qhe(e,n){var t;t=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,0,t,e.b))}function X8n(e,n,t){var i;e.b=n,e.a=t,i=(e.a&512)==512?new _xe:new gC,e.c=SDn(i,e.b,e.a)}function TBe(e,n){return J1(e.e,n)?(Tc(),xY(n)?new rR(n,e):new pT(n,e)):new nTe(n,e)}function K8n(e){var n,t;return 0>e?new Yoe:(n=e+1,t=new JPe(n,e),new Ale(null,t))}function V8n(e,n){jn();var t;return t=new l4(1),$r(e)?Kc(t,e,n):Ko(t.f,e,n),new fX(t)}function Y8n(e,n){var t;t=new Ug,u(n.b,68),u(n.b,68),u(n.b,68),Ao(n.a,new ife(e,t,n))}function OBe(e,n){var t;return X(n,8)?(t=u(n,8),e.a==t.a&&e.b==t.b):!1}function Q8n(e){var n;return n=C(e,(me(),wi)),X(n,174)?QFe(u(n,174)):null}function NBe(e){var n;return e=k.Math.max(e,2),n=b1e(e),e>n?(n<<=1,n>0?n:bS):n}function LY(e){switch(ile(e.e!=3),e.e){case 2:return!1;case 0:return!0}return n9n(e)}function Uhe(e){var n;return e.b==null?(kd(),kd(),nI):(n=e.sl()?e.rl():e.ql(),n)}function DBe(e,n){var t,i;for(i=n.vc().Jc();i.Ob();)t=u(i.Pb(),45),RO(e,t.jd(),t.kd())}function Xhe(e,n){var t;t=e.d,e.d=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,11,t,e.d))}function EB(e,n){var t;t=e.j,e.j=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,13,t,e.j))}function Khe(e,n){var t;t=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,21,t,e.b))}function Vhe(e,n){e.r>0&&e.c0&&e.g!=0&&Vhe(e.i,n/e.r*e.i.d))}function IBe(e,n,t){var i,r,c;for(c=e.a.length-1,r=e.b,i=0;i0?1:0:(!e.c&&(e.c=qT(Pu(e.f))),e.c).e}function HBe(e,n){n?e.B==null&&(e.B=e.D,e.D=null):e.B!=null&&(e.D=e.B,e.B=null)}function t7n(e,n){n.Tg(vQe,1),Zi(ou(new wn(null,new pn(e.b,16)),new Vg),new vI),n.Ug()}function zY(e,n,t,i,r,c){var o;this.c=e,o=new Te,_de(e,o,n,e.b,t,i,r,c),this.a=new qr(o,0)}function ir(e,n,t,i,r,c,o,l,f,h,b,p,y){return cqe(e,n,t,i,r,c,o,l,f,h,b,p,y),pQ(e,!1),e}function i7n(e,n){typeof window===sN&&typeof window.$gwt===sN&&(window.$gwt[e]=n)}function r7n(e,n,t){var i,r,c;for(i=0,r=0;r>>31;i!=0&&(e[t]=i)}function c7n(e,n,t){t.Tg("DFS Treeifying phase",1),gEn(e,n),UNn(e,n),e.a=null,e.b=null,t.Ug()}function u7n(e,n){var t;n.Tg("General Compactor",1),t=Qjn(u(ye(e,(J0(),Ire)),386)),t.Bg(e)}function o7n(e,n){var t,i;return t=u(ye(e,(J0(),FH)),15),i=u(ye(n,FH),15),oo(t.a,i.a)}function Zhe(e,n,t){var i,r;for(r=St(e,0);r.b!=r.d.c;)i=u(jt(r),8),i.a+=n,i.b+=t;return e}function s7n(e,n,t,i){var r;r=new r4,Hb(r,"x",pz(e,n,i.a)),Hb(r,"y",mz(e,n,i.b)),C4(t,r)}function l7n(e,n,t,i){var r;r=new r4,Hb(r,"x",pz(e,n,i.a)),Hb(r,"y",mz(e,n,i.b)),C4(t,r)}function f7n(){return G0(),z(B(B4e,1),ke,243,0,[AH,gD,wD,P4e,$4e,L4e,R4e,MH,D7,Sx])}function a7n(){return Dc(),z(B(lie,1),ke,261,0,[QJ,Kl,cx,WJ,S7,Nv,ux,j7,E7,ZJ])}function FY(){FY=Y,sA=new Txe,zce=z(B(ns,1),jv,179,0,[]),Qan=z(B(yf,1),ime,62,0,[])}function z4(){z4=Y,Lte=new Li("edgelabelcenterednessanalysis.includelabel",(Pn(),eb))}function GBe(e,n){return ne(re(Js(AO(So(new wn(null,new pn(e.c.b,16)),new Yje(e)),n))))}function e1e(e,n){return ne(re(Js(AO(So(new wn(null,new pn(e.c.b,16)),new Vje(e)),n))))}function Oi(e){return $r(e)?Od(e):s2(e)?b4(e):o2(e)?GOe(e):Tfe(e)?e.Hb():Sfe(e)?vw(e):aae(e)}function qBe(e,n){return Oa(),Bf(Ga),k.Math.abs(0-n)<=Ga||n==0||isNaN(0)&&isNaN(n)?0:e/n}function h7n(e,n){return Z9(),e==op&&n==om||e==op&&n==xv||e==sm&&n==xv||e==sm&&n==om}function d7n(e,n){return Z9(),e==op&&n==sm||e==sm&&n==op||e==xv&&n==om||e==om&&n==xv}function ss(){ss=Y,A3e=new w5,S3e=new s0,x3e=new Ih,E3e=new ck,M3e=new TA,C3e=new p5}function b7n(e){var n;return n=BR(e),Hj(n.a,0)?(YP(),YP(),dnn):(YP(),new EOe(n.b))}function JY(e){var n;return n=Dae(e),Hj(n.a,0)?(i2(),i2(),ste):(i2(),new PK(n.b))}function HY(e){var n;return n=Dae(e),Hj(n.a,0)?(i2(),i2(),ste):(i2(),new PK(n.c))}function g7n(e){return e.b.c.i.k==(zn(),wr)?u(C(e.b.c.i,(me(),wi)),12):e.b.c}function UBe(e){return e.b.d.i.k==(zn(),wr)?u(C(e.b.d.i,(me(),wi)),12):e.b.d}function XBe(e){switch(e.g){case 2:return De(),Kn;case 4:return De(),et;default:return e}}function KBe(e){switch(e.g){case 1:return De(),bt;case 3:return De(),Xn;default:return e}}function w7n(e,n){var t;return t=m0e(e),V0e(new je(t.c,t.d),new je(t.b,t.a),e.Kf(),n,e.$f())}function p7n(e,n){n.Tg(vQe,1),ode(jgn(new CP((Aj(),new DV(e,!1,!1,new rk))))),n.Ug()}function n1e(){n1e=Y,won=wh(uTe(qt(qt(new or,(zr(),eo),(Ur(),DJ)),no,MJ),Pc),NJ)}function VBe(){VBe=Y,yon=wh(uTe(qt(qt(new or,(zr(),eo),(Ur(),DJ)),no,MJ),Pc),NJ)}function YBe(e,n,t){this.g=e,this.d=n,this.e=t,this.a=new Te,oTn(this),jn(),Tr(this.a,null)}function Bl(e,n,t,i,r,c,o){Ot.call(this,e,n),this.d=t,this.e=i,this.c=r,this.b=c,this.a=$f(o)}function t1e(e){this.i=e.gc(),this.i>0&&(this.g=this.$i(this.i+(this.i/8|0)+1),e.Oc(this.g))}function xE(e,n){var t,i;for(_n(n),i=n.vc().Jc();i.Ob();)t=u(i.Pb(),45),e.yc(t.jd(),t.kd())}function m7n(e,n,t){var i;for(i=t.Jc();i.Ob();)if(!HR(e,n,i.Pb()))return!1;return!0}function AE(e,n,t){var i;for(i=e.b[t&e.f];i;i=i.b)if(t==i.a&&C1(n,i.g))return i;return null}function ME(e,n,t){var i;for(i=e.c[t&e.f];i;i=i.d)if(t==i.f&&C1(n,i.i))return i;return null}function v7n(e,n){var t;for(Nt(n);e.Ob();)if(t=e.Pb(),!u1e(u(t,9)))return!1;return!0}function y7n(e,n,t,i,r){var c;return t&&(c=zi(n.Ah(),e.c),r=t.Oh(n,-1-(c==-1?i:c),null,r)),r}function k7n(e,n,t,i,r){var c;return t&&(c=zi(n.Ah(),e.c),r=t.Qh(n,-1-(c==-1?i:c),null,r)),r}function QBe(e){var n;if(e.b==-2){if(e.e==0)n=-1;else for(n=0;e.a[n]==0;n++);e.b=n}return e.b}function j7n(e){var n,t,i;return e.j==(De(),Xn)&&(n=Wqe(e),t=cs(n,et),i=cs(n,Kn),i||i&&t)}function E7n(e){var n,t,i;for(i=0,t=new L(e.b);t.ar&&n.ac&&n.br?t=r:Yn(n,t+1),e.a=of(e.a,0,n)+(""+i)+qfe(e.a,t)}function WBe(e,n,t,i){X(e.Cb,184)&&(u(e.Cb,184).tb=null),Mo(e,t),n&&ETn(e,n),i&&e.el(!0)}function A7n(e,n){var t,i;for(i=new L(n.b);i.a1||e.Ob())return++e.a,e.g=0,n=e.i,e.Ob(),n;throw $(new au)}function _7n(e,n){var t,i;for(i=new L(n);i.a>22),r=e.h+n.h+(i>>22),_o(t&Ls,i&Ls,r&G1)}function xze(e,n){var t,i,r;return t=e.l-n.l,i=e.m-n.m+(t>>22),r=e.h-n.h+(i>>22),_o(t&Ls,i&Ls,r&G1)}function YY(e){var n,t,i,r;for(r=new Te,i=e.Jc();i.Ob();)t=u(i.Pb(),26),n=q2(t),Er(r,n);return r}function Z7n(e){var n;$d(e,!0),n=Rd,bi(e,(Ne(),T7))&&(n+=u(C(e,T7),15).a),he(e,T7,ve(n))}function Aze(e,n,t){var i;Hu(e.a),Ao(t.i,new VEe(e)),i=new _$(u(Bn(e.a,n.b),68)),EJe(e,i,n),t.f=i}function l1e(e){var n,t;return t=(v0(),n=new yo,n),e&&Et((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),t),t}function F4(e,n){var t,i;if(i=0,e<64&&e<=n)for(n=n<64?n:63,t=e;t<=n;t++)i=hh(i,Gh(1,t));return i}function ekn(e,n){var t,i;for(TR(n,"predicate"),i=0;e.Ob();i++)if(t=e.Pb(),n.Lb(t))return i;return-1}function f1e(e,n){if(n===0){!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),e.o.c.$b();return}wW(e,n)}function Mze(e){switch(e.g){case 1:return hb;case 2:return s1;case 3:return BD;default:return zD}}function a1e(e){jn();var n,t,i;for(i=0,t=e.Jc();t.Ob();)n=t.Pb(),i=i+(n!=null?Oi(n):0),i=i|0;return i}function nkn(e){var n;return n=new hn,n.a=e,n.b=okn(e),n.c=oe(ze,Se,2,2,6,1),n.c[0]=JBe(e),n.c[1]=JBe(e),n}function LB(){LB=Y,Pte=new f$(ma,0),zJ=new f$(jQe,1),FJ=new f$(EQe,2),WN=new f$("BOTH",3)}function Z9(){Z9=Y,op=new s$("Q1",0),sm=new s$("Q4",1),om=new s$("Q2",2),xv=new s$("Q3",3)}function _0(){_0=Y,iD=new WX("ONLY_WITHIN_GROUP",0),Bve=new WX(ZZ,1),dm=new WX("ENFORCED",2)}function Wb(){Wb=Y,tie=new YX(ma,0),k7=new YX("INCOMING_ONLY",1),Ov=new YX("OUTGOING_ONLY",2)}function J4(){J4=Y,ufn=new $M,cfn=new Q_}function QY(){QY=Y,ite={boolean:pgn,number:Tbn,string:Obn,object:sqe,function:sqe,undefined:sbn}}function Cze(){Cze=Y,Gun=It((G0(),z(B(B4e,1),ke,243,0,[AH,gD,wD,P4e,$4e,L4e,R4e,MH,D7,Sx])))}function Tze(){Tze=Y,qin=It((Dc(),z(B(lie,1),ke,261,0,[QJ,Kl,cx,WJ,S7,Nv,ux,j7,E7,ZJ])))}function tkn(e,n,t,i){return new rse(z(B(wg,1),eF,45,0,[(GQ(e,n),new bw(e,n)),(GQ(t,i),new bw(t,i))]))}function ikn(e,n){var t,i;return t=u(u(Bn(e.g,n.a),49).a,68),i=u(u(Bn(e.g,n.b),49).a,68),mKe(t,i)}function h1e(e,n,t){var i;if(i=e.gc(),n>i)throw $(new b2(n,i));return e.Qi()&&(t=z_e(e,t)),e.Ci(n,t)}function Oze(e){var n,t,i;return t=e.n,i=e.o,n=e.d,new Lf(t.a-n.b,t.b-n.d,i.a+(n.b+n.c),i.b+(n.d+n.a))}function rkn(e,n){return!e||!n||e==n?!1:$w(e.b.c,n.b.c+n.b.b)<0&&$w(n.b.c,e.b.c+e.b.b)<0}function WY(e,n,t){return e>=128?!1:e<64?Gj(Rr(Gh(1,e),t),0):Gj(Rr(Gh(1,e-64),n),0)}function kO(e,n,t){switch(t.g){case 2:e.b=n;break;case 1:e.c=n;break;case 4:e.d=n;break;case 3:e.a=n}}function jO(e,n,t){return t==null?(!e.q&&(e.q=new pt),L4(e.q,n)):(!e.q&&(e.q=new pt),ei(e.q,n,t)),e}function he(e,n,t){return t==null?(!e.q&&(e.q=new pt),L4(e.q,n)):(!e.q&&(e.q=new pt),ei(e.q,n,t)),e}function Nze(e){var n,t;return t=new YR,$u(t,e),he(t,(D0(),jy),e),n=new pt,W_n(e,t,n),T$n(e,t,n),t}function ckn(e){x8();var n,t,i;for(t=oe(Lr,Se,8,2,0,1),i=0,n=0;n<2;n++)i+=.5,t[n]=RSn(i,e);return t}function Dze(e,n){var t,i,r,c;for(t=!1,i=e.a[n].length,c=0;ce.f,t=e.u+e.e[e.o.p]*e.d>e.f*e.s*e.d,n||t}function d1e(e){var n;return(!e.c||(e.Bb&1)==0&&(e.c.Db&64)!=0)&&(n=ff(e),X(n,88)&&(e.c=u(n,29))),e.c}function b1e(e){var n;if(e<0)return Xr;if(e==0)return 0;for(n=bS;(n&e)==0;n>>=1);return n}function okn(e){var n;return e==0?"Etc/GMT":(e<0?(e=-e,n="Etc/GMT-"):n="Etc/GMT+",n+jRe(e))}function _ze(e){var n,t;return t=UO(e.h),t==32?(n=UO(e.m),n==32?UO(e.l)+32:n+20-10):t-12}function ZY(e){var n,t,i;n=~e.l+1&Ls,t=~e.m+(n==0?1:0)&Ls,i=~e.h+(n==0&&t==0?1:0)&G1,e.l=n,e.m=t,e.h=i}function TE(e){var n;return n=e.a[e.b],n==null?null:(tr(e.a,e.b,null),e.b=e.b+1&e.a.length-1,n)}function g1e(){this.o=null,this.k=null,this.j=null,this.d=null,this.b=null,this.n=null,this.a=null}function w1e(e,n){this.c=e,this.d=n,this.b=this.d/this.c.c.Pd().gc()|0,this.a=this.d%this.c.c.Pd().gc()}function Lze(e,n){this.b=e,N3.call(this,(u(K(we((x0(),Rn).o),10),19),n.i),n.g),this.a=(FY(),zce)}function p1e(e,n,t){this.q=new k.Date,this.q.setFullYear(e+K0,n,t),this.q.setHours(0,0,0,0),oS(this,0)}function Pze(e,n,t){var i,r;return i=new wY(n,t),r=new st,e.b=nXe(e,e.b,i,r),r.b||++e.c,e.b.b=!1,r.d}function m1e(e,n){jn();var t,i,r,c,o;for(o=!1,i=n,r=0,c=i.length;ro||i+r>c)throw $(new soe)}function $ze(e,n,t){var i,r,c,o;for(o=LE(n,t),c=0,r=o.Jc();r.Ob();)i=u(r.Pb(),12),ei(e.c,i,ve(c++))}function L0(e){var n,t;for(t=new L(e.a.b);t.a=0,"Negative initial capacity"),IT(n>=0,"Non-positive load factor"),Hu(this)}function Jze(e,n){var t;for(t=0;t1||n>=0&&e.b<3)}function wkn(){fi();var e;return Uce||(e=dpn(q0("M",!0)),e=aR(q0("M",!1),e),Uce=e,Uce)}function qze(e){if(e.g===0)return new P6;throw $(new Gn($F+(e.f!=null?e.f:""+e.g)))}function Uze(e){if(e.g===0)return new Y_;throw $(new Gn($F+(e.f!=null?e.f:""+e.g)))}function E1e(e,n,t){if(n===0){!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),TB(e.o,t);return}vW(e,n,t)}function nQ(e,n,t){this.g=e,this.e=new Kr,this.f=new Kr,this.d=new Si,this.b=new Si,this.a=n,this.c=t}function tQ(e,n,t,i){this.b=new Te,this.n=new Te,this.i=i,this.j=t,this.s=e,this.t=n,this.r=0,this.d=0}function Xze(e,n,t,i){this.b=new pt,this.g=new pt,this.d=(IE(),SH),this.c=e,this.e=n,this.d=t,this.a=i}function t8(e,n){if(!e.Ji()&&n==null)throw $(new Gn("The 'no null' constraint is violated"));return n}function S1e(e){switch(e.g){case 1:return GQe;default:case 2:return 0;case 3:return qQe;case 4:return zpe}}function pkn(e){return Ce(e.c,(J4(),ufn)),Ahe(e.a,ne(re(_e((EQ(),jH)))))?new VM:new nSe(e)}function mkn(e){for(;!e.d||!e.d.Ob();)if(e.b&&!kj(e.b))e.d=u(A4(e.b),50);else return null;return e.d}function Od(e){var n,t;for(n=0,t=0;ti?1:0}function Kze(e,n){var t,i,r;for(r=e.b;r;){if(t=e.a.Le(n,r.d),t==0)return r;i=t<0?0:1,r=r.a[i]}return null}function iQ(e,n){var t;return n===e?!0:X(n,229)?(t=u(n,229),di(e.Zb(),t.Zb())):!1}function x1e(e,n){return BUe(e,n)?(gn(e.b,u(C(n,(me(),K1)),22),n),Vt(e.a,n),!0):!1}function kkn(e,n){return bi(e,(me(),Ti))&&bi(n,Ti)?u(C(n,Ti),15).a-u(C(e,Ti),15).a:0}function jkn(e,n){return bi(e,(me(),Ti))&&bi(n,Ti)?u(C(e,Ti),15).a-u(C(n,Ti),15).a:0}function Vze(e){return Ka?oe(wnn,NYe,567,0,0,1):u(Ra(e.a,oe(wnn,NYe,567,e.a.c.length,0,1)),840)}function Us(e){return $r(e)?ze:s2(e)?gr:o2(e)?Yi:Tfe(e)||Sfe(e)?e.Pm:e.Pm||Array.isArray(e)&&B(Ken,1)||Ken}function W3(e,n,t){var i,r;return r=(i=new vX,i),Fc(r,n,t),Et((!e.q&&(e.q=new pe(yf,e,11,10)),e.q),r),r}function rQ(e){var n,t,i,r;for(r=Ign(Man,e),t=r.length,i=oe(ze,Se,2,t,6,1),n=0;n=e.b.c.length||(A1e(e,2*n+1),t=2*n+2,t0&&(n.Ad(t),t.i&&YEn(t))}function M1e(e,n,t){var i;for(i=t-1;i>=0&&e[i]===n[i];i--);return i<0?0:JX(Rr(e[i],Ic),Rr(n[i],Ic))?-1:1}function Skn(e,n){var t;return!e||e==n||!bi(n,(me(),ap))?!1:(t=u(C(n,(me(),ap)),9),t!=e)}function cQ(e){switch(e.i){case 2:return!0;case 1:return!1;case-1:++e.c;default:return e.Yl()}}function Yze(e,n,t){return e.d[n.p][t.p]||(mSn(e,n,t),e.d[n.p][t.p]=!0,e.d[t.p][n.p]=!0),e.a[n.p][t.p]}function Qze(e,n,t){var i,r;this.g=e,this.c=n,this.a=this,this.d=this,r=NBe(t),i=oe(Uen,dN,227,r,0,1),this.b=i}function xkn(e,n){var t,i;for(i=e.Zb().Bc().Jc();i.Ob();)if(t=u(i.Pb(),18),t.Gc(n))return!0;return!1}function Wze(e,n,t){var i,r,c,o;for(_n(t),o=!1,c=e.dd(n),r=t.Jc();r.Ob();)i=r.Pb(),c.Rb(i),o=!0;return o}function uQ(e,n){var t,i;return i=u(Un(e.a,4),129),t=oe(Rce,Ine,415,n,0,1),i!=null&&Wu(i,0,t,0,i.length),t}function Zze(e,n){var t;return t=new DW((e.f&256)!=0,e.i,e.a,e.d,(e.f&16)!=0,e.j,e.g,n),e.e!=null||(t.c=e),t}function Akn(e,n){var t;return e===n?!0:X(n,92)?(t=u(n,92),T0e($b(e),t.vc())):!1}function eFe(e,n,t){var i,r;for(r=t.Jc();r.Ob();)if(i=u(r.Pb(),45),e.ze(n,i.kd()))return!0;return!1}function PB(){PB=Y,Dce=new x$("ELK",0),D8e=new x$("JSON",1),N8e=new x$("DOT",2),I8e=new x$("SVG",3)}function OE(){OE=Y,Sre=new p$(ZZ,0),RH=new p$(KQe,1),Ere=new p$("FAN",2),jre=new p$("CONSTRAINT",3)}function NE(){NE=Y,bye=new sK(ma,0),hre=new sK("MIDDLE_TO_MIDDLE",1),yD=new sK("AVOID_OVERLAP",2)}function EO(){EO=Y,zH=new lK(ma,0),Fye=new lK("RADIAL_COMPACTION",1),Jye=new lK("WEDGE_COMPACTION",2)}function DE(){DE=Y,ure=new iK("STACKED",0),cre=new iK("REVERSE_STACKED",1),pD=new iK("SEQUENCED",2)}function zl(){zl=Y,Kme=new HX("CONCURRENT",0),Yo=new HX("IDENTITY_FINISH",1),Vme=new HX("UNORDERED",2)}function B1(){B1=Y,oG=new pK(L2e,0),Yd=new pK("INCLUDE_CHILDREN",1),Qx=new pK("SEPARATE_CHILDREN",2)}function $B(){$B=Y,d8e=new pw(15),Yfn=new Vr((Xt(),o1),d8e),Yx=Fy,l8e=kfn,f8e=Cg,h8e=Yv,a8e=Om}function oQ(){oQ=Y,Ate=I_e(z(B(Vx,1),ke,86,0,[(vr(),Zc),ru])),Mte=I_e(z(B(Vx,1),ke,86,0,[Vl,Za]))}function Mkn(e){var n,t,i;for(n=0,i=oe(Lr,Se,8,e.b,0,1),t=St(e,0);t.b!=t.d.c;)i[n++]=u(jt(t),8);return i}function sQ(e,n,t){var i,r,c;for(i=new Si,c=St(t,0);c.b!=c.d.c;)r=u(jt(c),8),Vt(i,new wc(r));Wze(e,n,i)}function Ckn(e,n){var t;t=_e((EQ(),jH))!=null&&n.Rg()!=null?ne(re(n.Rg()))/ne(re(_e(jH))):1,ei(e.b,n,t)}function Tkn(e,n){var t,i;return t=u(e.d.Ac(n),18),t?(i=e.e.hc(),i.Fc(t),e.e.d-=t.gc(),t.$b(),i):null}function C1e(e,n){var t,i;if(i=e.c[n],i!=0)for(e.c[n]=0,e.d-=i,t=n+1;t0)return T9(n-1,e.a.c.length),Ad(e.a,n-1);throw $(new ZSe)}function Okn(e,n,t){if(n<0)throw $(new jo(dWe+n));nn)throw $(new Gn(cF+e+DYe+n));if(e<0||n>t)throw $(new Ioe(cF+e+Yge+n+Xge+t))}function tFe(e){if(!e.a||(e.a.i&8)==0)throw $(new Uc("Enumeration class expected for layout option "+e.f))}function iFe(e){R_e.call(this,"The given string does not match the expected format for individual spacings.",e)}function rFe(e){switch(e.i){case-2:return!0;case-1:return!1;case 1:--e.c;default:return e.Zl()}}function Nd(e){switch(e.c){case 0:return rV(),mme;case 1:return new Z5(wqe(new s4(e)));default:return new Uxe(e)}}function cFe(e){switch(e.gc()){case 0:return rV(),mme;case 1:return new Z5(e.Jc().Pb());default:return new cse(e)}}function O1e(e){var n;return n=(!e.a&&(e.a=new pe(ed,e,9,5)),e.a),n.i!=0?Ngn(u(K(n,0),684)):null}function Nkn(e,n){var t;return t=mc(e,n),JX(YV(e,n),0)|T$(YV(e,t),0)?t:mc(hN,YV(Bb(t,63),1))}function N1e(e,n,t){var i,r;return S2(n,e.c.length),i=t.Nc(),r=i.length,r==0?!1:(sfe(e.c,n,i),!0)}function Dkn(e,n){var t,i;for(t=e.a.length-1;n!=e.b;)i=n-1&t,tr(e.a,n,e.a[i]),n=i;tr(e.a,e.b,null),e.b=e.b+1&t}function Ikn(e,n){var t,i;for(t=e.a.length-1,e.c=e.c-1&t;n!=e.c;)i=n+1&t,tr(e.a,n,e.a[i]),n=i;tr(e.a,e.c,null)}function i8(e,n){e.D==null&&e.B!=null&&(e.D=e.B,e.B=null),_Y(e,n==null?null:(_n(n),n)),e.C&&e.fl(null)}function Z3(e){return(e.c!=e.b.b||e.i!=e.g.b)&&(Qp(e.a.c,0),Er(e.a,e.b),Er(e.a,e.g),e.c=e.b.b,e.i=e.g.b),e.a}function _2(e){var n;++e.j,e.i==0?e.g=null:e.ir&&(QHe(n.q,r),i=t!=n.q.d)),i}function bFe(e,n){var t,i,r,c,o,l,f,h;return f=n.i,h=n.j,i=e.f,r=i.i,c=i.j,o=f-r,l=h-c,t=k.Math.sqrt(o*o+l*l),t}function _1e(e,n){var t,i;return i=nz(e),i||(t=(ZW(),gUe(n)),i=new HSe(t),Et(i.Cl(),e)),i}function SO(e,n){var t,i;return t=u(e.c.Ac(n),18),t?(i=e.hc(),i.Fc(t),e.d-=t.gc(),t.$b(),e.mc(i)):e.jc()}function Bkn(e){var n;if(!(e.c.c<0?e.a>=e.c.b:e.a<=e.c.b))throw $(new au);return n=e.a,e.a+=e.c.c,++e.b,ve(n)}function zkn(e){var n,t;if(e==null)return!1;for(n=0,t=e.length;n=i||n=0?n:-n;i>0;)i%2==0?(t*=t,i=i/2|0):(r*=t,i-=1);return n<0?1/r:r}function Kkn(e,n){var t,i,r;for(r=1,t=e,i=n>=0?n:-n;i>0;)i%2==0?(t*=t,i=i/2|0):(r*=t,i-=1);return n<0?1/r:r}function $0(e,n){var t,i,r,c;return c=(r=e?nz(e):null,oqe((i=n,r&&r.El(),i))),c==n&&(t=nz(e),t&&t.El()),c}function P1e(e,n,t){var i,r;return r=e.a,e.a=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,1,r,n),t?t.lj(i):t=i),t}function pFe(e,n,t){var i,r;return r=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,3,r,n),t?t.lj(i):t=i),t}function mFe(e,n,t){var i,r;return r=e.f,e.f=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,0,r,n),t?t.lj(i):t=i),t}function vFe(e){var n,t;if(e!=null)for(t=0;t-129&&e<128?(mDe(),n=e+128,t=Ime[n],!t&&(t=Ime[n]=new Ln(e)),t):new Ln(e)}function ve(e){var n,t;return e>-129&&e<128?(aDe(),n=e+128,t=Tme[n],!t&&(t=Tme[n]=new co(e)),t):new co(e)}function ejn(e,n,t,i,r){n==0||i==0||(n==1?r[i]=Cde(r,t,i,e[0]):i==1?r[n]=Cde(r,e,n,t[0]):HTn(e,t,r,n,i))}function SFe(e,n){var t;e.c.length!=0&&(t=u(Ra(e,oe(c1,Bd,9,e.c.length,0,1)),199),Bse(t,new Lh),Oqe(t,n))}function xFe(e,n){var t;e.c.length!=0&&(t=u(Ra(e,oe(c1,Bd,9,e.c.length,0,1)),199),Bse(t,new r3),Oqe(t,n))}function AFe(e,n){var t;e.a.c.length>0&&(t=u(Le(e.a,e.a.c.length-1),565),x1e(t,n))||Ce(e.a,new RPe(n))}function njn(e){rl();var n,t;n=e.d.c-e.e.c,t=u(e.g,156),Ao(t.b,new Lje(n)),Ao(t.c,new Pje(n)),rc(t.i,new $je(n))}function MFe(e){var n;return n=new p0,n.a+="VerticalSegment ",uo(n,e.e),n.a+=" ",Kt(n,nle(new NX,new L(e.k))),n.a}function tjn(e,n){var t;e.c=n,e.a=eEn(n),e.a<54&&(e.f=(t=n.d>1?PLe(n.a[0],n.a[1]):PLe(n.a[0],0),Xb(n.e>0?t:Cd(t))))}function hQ(e,n){var t,i,r;for(t=0,r=mu(e,n).Jc();r.Ob();)i=u(r.Pb(),12),t+=C(i,(me(),vs))!=null?1:0;return t}function ev(e,n,t){var i,r,c;for(i=0,c=St(e,0);c.b!=c.d.c&&(r=ne(re(jt(c))),!(r>t));)r>=n&&++i;return i}function ijn(e){var n;return n=u(Pa(e.c.c,""),233),n||(n=new N4(h9(a9(new f0,""),"Other")),tg(e.c.c,"",n)),n}function _E(e){var n;return(e.Db&64)!=0?Jf(e):(n=new cf(Jf(e)),n.a+=" (name: ",Bc(n,e.zb),n.a+=")",n.a)}function B1e(e,n,t){var i,r;return r=e.sb,e.sb=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,4,r,n),t?t.lj(i):t=i),t}function xO(e,n,t){var i;e.Zi(e.i+1),i=e.Xi(n,t),n!=e.i&&Wu(e.g,n,e.g,n+1,e.i-n),tr(e.g,n,i),++e.i,e.Ki(n,t),e.Li()}function z1e(e,n,t){var i,r;return r=e.r,e.r=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,8,r,e.r),t?t.lj(i):t=i),t}function rjn(e,n,t){var i,r;return i=new L1(e.e,3,13,null,(r=n.c,r||(yn(),ih)),Ld(e,n),!1),t?t.lj(i):t=i,t}function cjn(e,n,t){var i,r;return i=new L1(e.e,4,13,(r=n.c,r||(yn(),ih)),null,Ld(e,n),!1),t?t.lj(i):t=i,t}function ujn(e,n){var t,i,r,c;if(n.cj(e.a),c=u(Un(e.a,8),1997),c!=null)for(t=c,i=0,r=t.length;i>1&1431655765,e=(e>>2&858993459)+(e&858993459),e=(e>>4)+e&252645135,e+=e>>8,e+=e>>16,e&63}function ojn(e){return e?(e.i&1)!=0?e==ts?Yi:e==$t?jr:e==Hm?h7:e==Jr?gr:e==Ep?cp:e==t5?up:e==ds?py:XS:e:null}function di(e,n){return $r(e)?bn(e,n):s2(e)?vNe(e,n):o2(e)?(_n(e),ue(e)===ue(n)):Tfe(e)?e.Fb(n):Sfe(e)?bTe(e,n):Mae(e,n)}function TFe(e){var n;return ao(e,0)<0&&(e=I0(Avn(uu(e)?sf(e):e))),n=Rt(Bb(e,32)),64-(n!=0?UO(n):UO(Rt(e))+32)}function AO(e,n){var t;return t=new ch,e.a.zd(t)?(y9(),new SX(_n(hRe(e,t.a,n)))):(A0(e),y9(),y9(),Gme)}function LE(e,n){switch(n.g){case 2:case 1:return mu(e,n);case 3:case 4:return Ks(mu(e,n))}return jn(),jn(),Sc}function sjn(e,n){var t;return n.a&&(t=n.a.a.length,e.a?Kt(e.a,e.b):e.a=new il(e.d),zLe(e.a,n.a,n.d.length,t)),e}function ljn(e){Zz();var n,t,i,r;for(t=DQ(),i=0,r=t.length;it)throw $(new jo(cF+e+Yge+n+", size: "+t));if(e>n)throw $(new Gn(cF+e+DYe+n))}function Fl(e,n,t){if(n<0)q0e(e,t);else{if(!t.pk())throw $(new Gn(W0+t.ve()+_S));u(t,69).uk().Ck(e,e.ei(),n)}}function dQ(e,n,t){return k.Math.abs(n-e)NF?e-t>NF:t-e>NF}function J1e(e,n,t,i){switch(n){case 1:return!e.n&&(e.n=new pe(ju,e,1,7)),e.n;case 2:return e.k}return $de(e,n,t,i)}function NFe(e){var n;return(e.Db&64)!=0?Jf(e):(n=new cf(Jf(e)),n.a+=" (source: ",Bc(n,e.d),n.a+=")",n.a)}function Id(e,n){var t;t=(e.Bb&256)!=0,n?e.Bb|=256:e.Bb&=-257,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,2,t,n))}function H1e(e,n){var t;t=(e.Bb&256)!=0,n?e.Bb|=256:e.Bb&=-257,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,8,t,n))}function G1e(e,n){var t;t=(e.Bb&512)!=0,n?e.Bb|=512:e.Bb&=-513,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,9,t,n))}function _d(e,n){var t;t=(e.Bb&512)!=0,n?e.Bb|=512:e.Bb&=-513,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,3,t,n))}function FB(e,n){var t;t=(e.Bb&256)!=0,n?e.Bb|=256:e.Bb&=-257,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,8,t,n))}function fjn(e,n,t){var i,r;return r=e.a,e.a=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,5,r,e.a),t?s0e(t,i):t=i),t}function PE(e,n){var t;return e.b==-1&&e.a&&(t=e.a.nk(),e.b=t?e.c.Eh(e.a.Jj(),t):zi(e.c.Ah(),e.a)),e.c.vh(e.b,n)}function DFe(e,n){var t,i;for(i=new ut(e);i.e!=i.i.gc();)if(t=u(ft(i),29),ue(n)===ue(t))return!0;return!1}function IFe(e){return e>=65&&e<=70?e-65+10:e>=97&&e<=102?e-97+10:e>=48&&e<=57?e-48:0}function q1e(e){var n,t;return n=e.k,n==(zn(),wr)?(t=u(C(e,(me(),Du)),64),t==(De(),Xn)||t==bt):!1}function _Fe(e){var n;return n=Dae(e),Hj(n.a,0)?(i2(),i2(),ste):(i2(),new PK(FX(n.a,0)?ehe(n)/Xb(n.a):0))}function ajn(e,n){var t;if(t=QO(e,n),X(t,335))return u(t,38);throw $(new Gn(W0+n+"' is not a valid attribute"))}function $E(e,n,t){var i;if(i=e.gc(),n>i)throw $(new b2(n,i));if(e.Qi()&&e.Gc(t))throw $(new Gn($N));e.Ei(n,t)}function LFe(e,n){var t,i;for(i=new ut(e);i.e!=i.i.gc();)if(t=u(ft(i),143),ue(n)===ue(t))return!0;return!1}function hjn(e,n,t){var i,r,c;return c=(r=E8(e.b,n),r),c&&(i=u(Kz(oO(e,c),""),29),i)?gbe(e,i,n,t):null}function bQ(e,n,t){var i,r,c;return c=(r=E8(e.b,n),r),c&&(i=u(Kz(oO(e,c),""),29),i)?wbe(e,i,n,t):null}function djn(e){var n,t,i;for(i=0,t=e.length,n=0;n=0?B0(e):sE(B0(Cd(e))))}function PFe(e,n,t,i,r,c){this.e=new Te,this.f=(Nc(),xx),Ce(this.e,e),this.d=n,this.a=t,this.b=i,this.f=r,this.c=c}function ki(e,n){return en?1:e==n?e==0?ki(1/e,1/n):0:isNaN(e)?isNaN(n)?0:1:-1}function bjn(e){var n;return n=e.a[e.c-1&e.a.length-1],n==null?null:(e.c=e.c-1&e.a.length-1,tr(e.a,e.c,null),n)}function $Fe(e){var n,t;for(t=e.p.a.ec().Jc();t.Ob();)if(n=u(t.Pb(),217),n.f&&e.b[n.c]<-1e-10)return n;return null}function gjn(e){var n,t,i;for(n=new Te,i=new L(e.b);i.a=1?ru:Za):t}function kjn(e){var n,t;for(t=wUe(sl(e)).Jc();t.Ob();)if(n=Pt(t.Pb()),uS(e,n))return I6n((DMe(),Ban),n);return null}function jjn(e,n,t){var i,r;for(r=e.a.ec().Jc();r.Ob();)if(i=u(r.Pb(),9),yO(t,u(Le(n,i.p),18)))return i;return null}function Ejn(e,n,t){var i,r;for(r=X(n,103)&&(u(n,19).Bb&Ec)!=0?new EK(n,e):new Y9(n,e),i=0;i>10)+pN&yr,n[1]=(e&1023)+56320&yr,gh(n,0,n.length)}function Y1e(e,n){var t;t=(e.Bb&Ec)!=0,n?e.Bb|=Ec:e.Bb&=-65537,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,20,t,n))}function a8(e,n){var t;t=(e.Bb&yh)!=0,n?e.Bb|=yh:e.Bb&=-16385,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,16,t,n))}function pQ(e,n){var t;t=(e.Bb&Bu)!=0,n?e.Bb|=Bu:e.Bb&=-32769,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,18,t,n))}function Q1e(e,n){var t;t=(e.Bb&Bu)!=0,n?e.Bb|=Bu:e.Bb&=-32769,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Pf(e,1,18,t,n))}function mu(e,n){var t;return e.i||G0e(e),t=u(zc(e.g,n),49),t?new C0(e.j,u(t.a,15).a,u(t.b,15).a):(jn(),jn(),Sc)}function Ajn(e,n,t){var i,r;return i=u(n.mf(e.a),35),r=u(t.mf(e.a),35),i!=null&&r!=null?dO(i,r):i!=null?-1:r!=null?1:0}function W1e(e,n,t){var i,r;return i=(v0(),r=new Fk,r),bB(i,n),gB(i,t),e&&Et((!e.a&&(e.a=new mr(kl,e,5)),e.a),i),i}function Z1e(e,n,t){var i;return i=0,n&&(I3(e.a)?i+=n.f.a/2:i+=n.f.b/2),t&&(I3(e.a)?i+=t.f.a/2:i+=t.f.b/2),i}function Pw(e,n,t){var i;return i=e.a.get(n),e.a.set(n,t===void 0?null:t),i===void 0?(++e.c,++e.b.g):++e.d,i}function mQ(e){var n;return(e.Db&64)!=0?Jf(e):(n=new cf(Jf(e)),n.a+=" (identifier: ",Bc(n,e.k),n.a+=")",n.a)}function qB(e){var n;switch(e.gc()){case 0:return cR(),Zne;case 1:return new HK(Nt(e.Xb(0)));default:return n=e,new QV(n)}}function Mjn(e){switch(u(C(e,(Ne(),Y1)),222).g){case 1:return new ad;case 3:return new C5;default:return new Dp}}function Cjn(e){var n;return n=F2(e),n>34028234663852886e22?Ki:n<-34028234663852886e22?Dr:n}function mc(e,n){var t;return uu(e)&&uu(n)&&(t=e+n,wNn){NLe(t);break}}yR(t,n)}function We(e,n){var t,i,r,c,o;if(t=n.f,tg(e.c.d,t,n),n.g!=null)for(r=n.g,c=0,o=r.length;cn&&i.Le(e[c-1],e[c])>0;--c)o=e[c],tr(e,c,e[c-1]),tr(e,c-1,o)}function Jl(e,n,t,i){if(n<0)ybe(e,t,i);else{if(!t.pk())throw $(new Gn(W0+t.ve()+_S));u(t,69).uk().Ak(e,e.ei(),n,i)}}function Rjn(e,n){var t;if(t=QO(e.Ah(),n),X(t,103))return u(t,19);throw $(new Gn(W0+n+"' is not a valid reference"))}function UB(e,n){if(n==e.d)return e.e;if(n==e.e)return e.d;throw $(new Gn("Node "+n+" not part of edge "+e))}function nde(e,n,t,i){switch(n){case 3:return e.f;case 4:return e.g;case 5:return e.i;case 6:return e.j}return J1e(e,n,t,i)}function Bjn(e){return e.k!=(zn(),Qi)?!1:G3(new wn(null,new v2(new qn(Vn(Ni(e).a.Jc(),new ee)))),new ZA)}function Xs(){Xs=Y,sD=new sT(ma,0),fx=new sT("FIRST",1),V1=new sT(jQe,2),ax=new sT("LAST",3),yg=new sT(EQe,4)}function BE(){BE=Y,ix=new h$("LAYER_SWEEP",0),wve=new h$("MEDIAN_LAYER_SWEEP",1),eD=new h$(ree,2),pve=new h$(ma,3)}function XB(){XB=Y,f6e=new hK("ASPECT_RATIO_DRIVEN",0),Gre=new hK("MAX_SCALE_DRIVEN",1),l6e=new hK("AREA_DRIVEN",2)}function KB(){KB=Y,Nce=new S$(Rpe,0),M8e=new S$("GROUP_DEC",1),T8e=new S$("GROUP_MIXED",2),C8e=new S$("GROUP_INC",3)}function zjn(e,n){return bn(n.b&&n.c?Ub(n.b)+"->"+Ub(n.c):"e_"+Oi(n),e.b&&e.c?Ub(e.b)+"->"+Ub(e.c):"e_"+Oi(e))}function Fjn(e,n){return bn(n.b&&n.c?Ub(n.b)+"->"+Ub(n.c):"e_"+Oi(n),e.b&&e.c?Ub(e.b)+"->"+Ub(e.c):"e_"+Oi(e))}function $w(e,n){return Oa(),Bf(X0),k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)?0:en?1:Lb(isNaN(e),isNaN(n))}function tde(e){EQ(),this.c=$f(z(B(UBn,1),On,829,0,[Run])),this.b=new pt,this.a=e,ei(this.b,jH,1),Ao(Bun,new eSe(this))}function zE(e){var n;this.a=(n=u(e.e&&e.e(),10),new _l(n,u(_f(n,n.length),10),0)),this.b=oe(Ar,On,1,this.a.a.length,5,1)}function su(e){var n;return Array.isArray(e)&&e.Rm===Qn?Db(Us(e))+"@"+(n=Oi(e)>>>0,n.toString(16)):e.toString()}function Jjn(e){var n;return e==null?!0:(n=e.length,n>0&&(Yn(n-1,e.length),e.charCodeAt(n-1)==58)&&!kQ(e,uA,oA))}function kQ(e,n,t){var i,r;for(i=0,r=e.length;i=r)return n.c+t;return n.c+n.b.gc()}function UFe(e,n){S9();var t,i,r,c;for(i=R$e(e),r=n,J9(i,0,i.length,r),t=0;t0&&(i+=r,++t);return t>1&&(i+=e.d*(t-1)),i}function rde(e){var n,t,i;for(i=new pd,i.a+="[",n=0,t=e.gc();n=0;--i)for(n=t[i],r=0;r>5,n=e&31,i=oe($t,ni,30,t+1,15,1),i[t]=1<0&&(n.lengthe.i&&tr(n,e.i,null),n}function VB(e){var n;return(e.Db&64)!=0?_E(e):(n=new cf(_E(e)),n.a+=" (instanceClassName: ",Bc(n,e.D),n.a+=")",n.a)}function YB(e){var n,t,i,r;for(r=0,t=0,i=e.length;t0?(e.Zj(),i=n==null?0:Oi(n),r=(i&oi)%e.d.length,t=jUe(e,r,i,n),t!=-1):!1}function Co(e,n,t){var i,r,c;return e.Nj()?(i=e.i,c=e.Oj(),xO(e,i,n),r=e.Gj(3,null,n,i,c),t?t.lj(r):t=r):xO(e,e.i,n),t}function wa(e,n){var t,i,r;return e.f>0&&(e.Zj(),i=n==null?0:Oi(n),r=(i&oi)%e.d.length,t=W0e(e,r,i,n),t)?t.kd():null}function fEn(e,n,t){var i,r;return i=new L1(e.e,3,10,null,(r=n.c,X(r,88)?u(r,29):(yn(),jf)),Ld(e,n),!1),t?t.lj(i):t=i,t}function aEn(e,n,t){var i,r;return i=new L1(e.e,4,10,(r=n.c,X(r,88)?u(r,29):(yn(),jf)),null,Ld(e,n),!1),t?t.lj(i):t=i,t}function iJe(e,n){var t,i,r;return X(n,45)?(t=u(n,45),i=t.jd(),r=L2(e.Pc(),i),C1(r,t.kd())&&(r!=null||e.Pc()._b(i))):!1}function dde(e,n){switch(n){case 3:Dw(e,0);return;case 4:Iw(e,0);return;case 5:Os(e,0);return;case 6:Ns(e,0);return}R1e(e,n)}function Rw(e,n){switch(n.g){case 1:return j4(e.j,(ss(),S3e));case 2:return j4(e.j,(ss(),A3e));default:return jn(),jn(),Sc}}function B0(e){mh();var n,t;return t=Rt(e),n=Rt(Bb(e,32)),n!=0?new hLe(t,n):t>10||t<0?new D1(1,t):cnn[t]}function rJe(e){B2();var n;return(e.q?e.q:(jn(),jn(),i1))._b((Ne(),gp))?n=u(C(e,gp),203):n=u(C(_r(e),vx),203),n}function hEn(e,n,t,i){var r,c;if(c=t-n,c<3)for(;c<3;)e*=10,++c;else{for(r=1;c>3;)r*=10,--c;e=(e+(r>>1))/r|0}return i.i=e,!0}function cJe(e,n,t){fBe(),gxe.call(this),this.a=g2(Cnn,[Se,Zge],[592,216],0,[gJ,gte],2),this.c=new g4,this.g=e,this.f=n,this.d=t}function uJe(e){this.e=oe($t,ni,30,e.length,15,1),this.c=oe(ts,pa,30,e.length,16,1),this.b=oe(ts,pa,30,e.length,16,1),this.f=0}function dEn(e){var n,t;for(e.j=oe(Jr,Jc,30,e.p.c.length,15,1),t=new L(e.p);t.a>5,n&=31,r=e.d+t+(n==0?0:1),i=oe($t,ni,30,r,15,1),aMn(i,e.a,t,n),c=new zb(e.e,r,i),bE(c),c}function h8(e,n,t){var i,r,c;for(r=null,c=e.b;c;){if(i=e.a.Le(n,c.d),t&&i==0)return c;i>=0?c=c.a[1]:(r=c,c=c.a[0])}return r}function DO(e,n,t){var i,r,c;for(r=null,c=e.b;c;){if(i=e.a.Le(n,c.d),t&&i==0)return c;i<=0?c=c.a[0]:(r=c,c=c.a[1])}return r}function MQ(e,n){for(var t=0;!n[t]||n[t]=="";)t++;for(var i=n[t++];t0?(k.Error.stackTraceLimit=Error.stackTraceLimit=64,!0):"stack"in new Error}function mEn(e){var n;n=e.a;do n=u(tt(new qn(Vn(Ni(n).a.Jc(),new ee))),17).d.i,n.k==(zn(),dr)&&Ce(e.e,n);while(n.k==(zn(),dr))}function vEn(e,n){var t,i,r;for(i=new qn(Vn(Ni(e).a.Jc(),new ee));ht(i);)if(t=u(tt(i),17),r=t.d.i,r.c==n)return!1;return!0}function hJe(e,n,t){var i,r,c,o;for(r=u(Bn(e.b,t),171),i=0,o=new L(n.j);o.an?1:Lb(isNaN(e),isNaN(n)))>0}function pde(e,n){return Oa(),Oa(),Bf(X0),(k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)?0:en?1:Lb(isNaN(e),isNaN(n)))<0}function pJe(e,n){return Oa(),Oa(),Bf(X0),(k.Math.abs(e-n)<=X0||e==n||isNaN(e)&&isNaN(n)?0:en?1:Lb(isNaN(e),isNaN(n)))<=0}function mde(e){switch(e.g){case 12:case 13:case 14:case 15:case 16:case 17:case 18:case 19:case 20:return!0;default:return!1}}function vde(e,n,t,i,r,c){this.a=e,this.c=n,this.b=t,this.f=i,this.d=r,this.e=c,this.c>0&&this.b>0&&(this.g=oR(this.c,this.b,this.a))}function jEn(e,n){var t=e.a,i;n=String(n),t.hasOwnProperty(n)&&(i=t[n]);var r=(QY(),ite)[typeof i],c=r?r(i):F1e(typeof i);return c}function d8(e){var n,t,i;if(i=null,n=Ah in e.a,t=!n,t)throw $(new oh("Every element must have an id."));return i=Z4(O1(e,Ah)),i}function Bw(e){var n,t;for(t=GGe(e),n=null;e.c==2;)li(e),n||(n=(fi(),fi(),new Kj(2)),ug(n,t),t=n),t.Hm(GGe(e));return t}function ZB(e,n){var t,i,r;return e.Zj(),i=n==null?0:Oi(n),r=(i&oi)%e.d.length,t=W0e(e,r,i,n),t?(wBe(e,t),t.kd()):null}function gh(e,n,t){var i,r,c,o;for(c=n+t,Yr(n,c,e.length),o="",r=n;rn.e?1:e.en.d?e.e:e.d=48&&e<48+k.Math.min(10,10)?e-48:e>=97&&e<97?e-97+10:e>=65&&e<65?e-65+10:-1}function EEn(e,n){if(n.c==e)return n.d;if(n.d==e)return n.c;throw $(new Gn("Input edge is not connected to the input port."))}function wh(e,n){if(e.a<0)throw $(new Uc("Did not call before(...) or after(...) before calling add(...)."));return ble(e,e.a,n),e}function yde(e){return $R(),X(e,166)?u(Bn(WD,fnn),296).Qg(e):so(WD,Us(e))?u(Bn(WD,Us(e)),296).Qg(e):null}function Lo(e){var n,t;return(e.Db&32)==0&&(t=(n=u(Un(e,16),29),dt(n||e.fi())-dt(e.fi())),t!=0&&U4(e,32,oe(Ar,On,1,t,5,1))),e}function U4(e,n,t){var i;(e.Db&n)!=0?t==null?JTn(e,n):(i=VQ(e,n),i==-1?e.Eb=t:tr(B4(e.Eb),i,t)):t!=null&&sDn(e,n,t)}function SEn(e,n,t,i){var r,c;n.c.length!=0&&(r=tNn(t,i),c=fTn(n),Zi(aB(new wn(null,new pn(c,1)),new g_),new JIe(e,t,r,i)))}function xEn(e,n){var t,i,r,c;return i=e.a.length-1,t=n-e.b&i,c=e.c-n&i,r=e.c-e.b&i,SOe(t=c?(Ikn(e,n),-1):(Dkn(e,n),1)}function AEn(e,n){var t,i;for(t=(Yn(n,e.length),e.charCodeAt(n)),i=n+1;in.e?1:e.fn.f?1:Oi(e)-Oi(n)}function jJe(e,n){var t;return ue(n)===ue(e)?!0:!X(n,22)||(t=u(n,22),t.gc()!=e.gc())?!1:e.Hc(t)}function ez(e,n){return _n(e),n==null?!1:bn(e,n)?!0:e.length==n.length&&bn(e.toLowerCase(),n.toLowerCase())}function R2(e){var n,t;return ao(e,-129)>0&&ao(e,128)<0?(pDe(),n=Rt(e)+128,t=Ome[n],!t&&(t=Ome[n]=new En(e)),t):new En(e)}function X4(){X4=Y,ZS=new l$(ma,0),v3e=new l$("INSIDE_PORT_SIDE_GROUPS",1),Tte=new l$("GROUP_MODEL_ORDER",2),Ote=new l$(ZZ,3)}function nz(e){var n,t,i;if(i=e.Gh(),!i)for(n=0,t=e.Mh();t;t=t.Mh()){if(++n>$Z)return t.Nh();if(i=t.Gh(),i||t==e)break}return i}function TEn(e){var n;return e.b||ogn(e,(n=o2n(e.e,e.a),!n||!bn(ane,wa((!n.b&&(n.b=new Hs((yn(),Ac),Iu,n)),n.b),"qualified")))),e.c}function OEn(e){var n,t;for(t=new L(e.a.b);t.a2e3&&(Ven=e,sJ=k.setTimeout(Egn,10))),oJ++==0?(r8n((Coe(),yme)),!0):!1}function JEn(e,n,t){var i;(pnn?(nEn(e),!0):mnn||ynn?(m9(),!0):vnn&&(m9(),!1))&&(i=new ONe(n),i.b=t,HMn(e,i))}function OQ(e,n){var t;t=!e.A.Gc((Vs(),Og))||e.q==(Br(),to),e.u.Gc((ps(),Z1))?t?fRn(e,n):xVe(e,n):e.u.Gc(gb)&&(t?D$n(e,n):zVe(e,n))}function HEn(e,n,t){var i,r;aW(e.e,n,t,(De(),Kn)),aW(e.i,n,t,et),e.a&&(r=u(C(n,(me(),wi)),12),i=u(C(t,wi),12),WV(e.g,r,i))}function MJe(e){var n;ue(ye(e,(Xt(),Xv)))===ue((B1(),oG))&&(Bi(e)?(n=u(ye(Bi(e),Xv),347),ji(e,Xv,n)):ji(e,Xv,Qx))}function CJe(e,n,t){return new Lf(k.Math.min(e.a,n.a)-t/2,k.Math.min(e.b,n.b)-t/2,k.Math.abs(e.a-n.a)+t,k.Math.abs(e.b-n.b)+t)}function TJe(e){var n;this.d=new Te,this.j=new Kr,this.g=new Kr,n=e.g.b,this.f=u(C(_r(n),(Ne(),pl)),86),this.e=ne(re(rz(n,Em)))}function OJe(e){this.d=new Te,this.e=new O0,this.c=oe($t,ni,30,(De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])).length,15,1),this.b=e}function xde(e,n,t){var i;switch(i=t[e.g][n],e.g){case 1:case 3:return new je(0,i);case 2:case 4:return new je(i,0);default:return null}}function GEn(e,n){var t;if(t=U3(e.o,n),t==null)throw $(new oh("Node did not exist in input."));return Sbe(e,n),PW(e,n),bbe(e,n,t),null}function NJe(e,n){var t,i;for(i=e.a.length,n.lengthi&&tr(n,i,null),n}function Ra(e,n){var t,i;for(i=e.c.length,n.lengthi&&tr(n,i,null),n}function NQ(e,n,t,i){var r;if(r=e.length,n>=r)return r;for(n=n>0?n:0;n0&&(Ce(e.b,new KNe(n.a,t)),i=n.a.length,0i&&(n.a+=HTe(oe(Wl,kh,30,-i,15,1))))}function _Je(e,n,t){var i,r,c;if(!t[n.d])for(t[n.d]=!0,r=new L(Z3(n));r.a=e.b>>1)for(i=e.c,t=e.b;t>n;--t)i=i.b;else for(i=e.a.a,t=0;t=0?e.Th(r):kW(e,i)):t<0?kW(e,i):u(i,69).uk().zk(e,e.ei(),t)}function RJe(e){var n,t,i;for(i=(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),e.o),t=i.c.Jc();t.e!=t.i.gc();)n=u(t.Wj(),45),n.kd();return ZT(i)}function _e(e){var n;if(X(e.a,4)){if(n=yde(e.a),n==null)throw $(new Uc(gWe+e.b+"'. "+bWe+(M1(ZD),ZD.k)+C2e));return n}else return e.a}function nSn(e){var n;if(e==null)return null;if(n=mRn(bo(e,!0)),n==null)throw $(new CX("Invalid base64Binary value: '"+e+"'"));return n}function ft(e){var n;try{return n=e.i.Xb(e.e),e.Vj(),e.g=e.e++,n}catch(t){throw t=sr(t),X(t,99)?(e.Vj(),$(new au)):$(t)}}function LQ(e){var n;try{return n=e.c.Ti(e.e),e.Vj(),e.g=e.e++,n}catch(t){throw t=sr(t),X(t,99)?(e.Vj(),$(new au)):$(t)}}function iz(e){var n,t,i,r;for(r=0,t=0,i=e.length;t=64&&n<128&&(r=hh(r,Gh(1,n-64)));return r}function rz(e,n){var t,i;return i=null,bi(e,(Xt(),Jy))&&(t=u(C(e,Jy),105),t.nf(n)&&(i=t.mf(n))),i==null&&_r(e)&&(i=C(_r(e),n)),i}function tSn(e,n){var t;return t=u(C(e,(Ne(),Wc)),78),NK(n,nin)?t?qs(t):(t=new xs,he(e,Wc,t)):t&&he(e,Wc,null),t}function iSn(e,n){var t,i,r;for(r=new xo(n.gc()),i=n.Jc();i.Ob();)t=u(i.Pb(),294),t.c==t.f?k8(e,t,t.c):pCn(e,t)||Hn(r.c,t);return r}function BJe(e,n){var t,i,r;for(t=e.o,r=u(u(pi(e.r,n),22),83).Jc();r.Ob();)i=u(r.Pb(),115),i.e.a=cxn(i,t.a),i.e.b=t.b*ne(re(i.b.mf(wJ)))}function rSn(e,n){var t,i,r,c;return r=e.k,t=ne(re(C(e,(me(),hp)))),c=n.k,i=ne(re(C(n,hp))),c!=(zn(),wr)?-1:r!=wr?1:t==i?0:tt.b)return!0}return!1}function JJe(e){var n;return n=new p0,n.a+="n",e.k!=(zn(),Qi)&&Kt(Kt((n.a+="(",n),$K(e.k).toLowerCase()),")"),Kt((n.a+="_",n),LO(e)),n.a}function HE(){HE=Y,I4e=new lT(Rpe,0),Wie=new lT(ree,1),Zie=new lT("LINEAR_SEGMENTS",2),jx=new lT("BRANDES_KOEPF",3),Ex=new lT(BQe,4)}function K4(e,n,t,i){var r;return t>=0?e.Ph(n,t,i):(e.Mh()&&(i=(r=e.Ch(),r>=0?e.xh(i):e.Mh().Qh(e,-1-r,null,i))),e.zh(n,t,i))}function Ade(e,n){switch(n){case 7:!e.e&&(e.e=new Nn(pr,e,7,4)),kt(e.e);return;case 8:!e.d&&(e.d=new Nn(pr,e,8,5)),kt(e.d);return}dde(e,n)}function ji(e,n,t){return t==null?(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),ZB(e.o,n)):(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),RO(e.o,n,t)),e}function Yu(e,n){var t;t=e.dd(n);try{return t.Pb()}catch(i){throw i=sr(i),X(i,112)?$(new jo("Can't get element "+n)):$(i)}}function HJe(e,n){var t;switch(t=u(zc(e.b,n),127).n,n.g){case 1:e.t>=0&&(t.d=e.t);break;case 3:e.t>=0&&(t.a=e.t)}e.C&&(t.b=e.C.b,t.c=e.C.c)}function aSn(e){var n;n=e.a;do n=u(tt(new qn(Vn(rr(n).a.Jc(),new ee))),17).c.i,n.k==(zn(),dr)&&e.b.Ec(n);while(n.k==(zn(),dr));e.b=Ks(e.b)}function GJe(e,n){var t,i,r;for(r=e,i=new qn(Vn(rr(n).a.Jc(),new ee));ht(i);)t=u(tt(i),17),t.c.i.c&&(r=k.Math.max(r,t.c.i.c.p));return r}function hSn(e,n){var t,i,r;for(r=0,i=u(u(pi(e.r,n),22),83).Jc();i.Ob();)t=u(i.Pb(),115),r+=t.d.d+t.b.Kf().b+t.d.a,i.Ob()&&(r+=e.w);return r}function dSn(e,n){var t,i,r;for(r=0,i=u(u(pi(e.r,n),22),83).Jc();i.Ob();)t=u(i.Pb(),115),r+=t.d.b+t.b.Kf().a+t.d.c,i.Ob()&&(r+=e.w);return r}function qJe(e){var n,t,i,r;if(i=0,r=q2(e),r.c.length==0)return 1;for(t=new L(r);t.a=0?e.Ih(o,t,!0):Gw(e,c,t)):u(c,69).uk().wk(e,e.ei(),r,t,i)}function wSn(e,n,t,i){var r,c;c=n.nf((Xt(),Vv))?u(n.mf(Vv),22):e.j,r=ljn(c),r!=(Zz(),wte)&&(t&&!mde(r)||O0e(NOn(e,r,i),n))}function PQ(e,n){return $r(e)?!!Jen[n]:e.Qm?!!e.Qm[n]:s2(e)?!!Fen[n]:o2(e)?!!zen[n]:!1}function pSn(e){switch(e.g){case 1:return Lw(),XN;case 3:return Lw(),UN;case 2:return Lw(),mte;case 4:return Lw(),pte;default:return null}}function mSn(e,n,t){if(e.e)switch(e.b){case 1:I5n(e.c,n,t);break;case 0:_5n(e.c,n,t)}else oPe(e.c,n,t);e.a[n.p][t.p]=e.c.i,e.a[t.p][n.p]=e.c.e}function XJe(e){var n,t;if(e==null)return null;for(t=oe(c1,Se,199,e.length,0,2),n=0;nc?1:0):0}function B2(){B2=Y,xH=new d$(ma,0),Yie=new d$("PORT_POSITION",1),Fv=new d$("NODE_SIZE_WHERE_SPACE_PERMITS",2),zv=new d$("NODE_SIZE",3)}function vSn(e,n){var t,i,r;for(n.Tg("Untreeify",1),t=u(C(e,(Ci(),yye)),16),r=t.Jc();r.Ob();)i=u(r.Pb(),65),Vt(i.b.d,i),Vt(i.c.b,i);n.Ug()}function Vh(){Vh=Y,sce=new Rj("AUTOMATIC",0),TD=new Rj(ly,1),OD=new Rj(fy,2),nG=new Rj("TOP",3),ZH=new Rj(nwe,4),eG=new Rj(F8,5)}function tv(e,n,t){var i,r;if(r=e.gc(),n>=r)throw $(new b2(n,r));if(e.Qi()&&(i=e.bd(t),i>=0&&i!=n))throw $(new Gn($N));return e.Vi(n,t)}function Ld(e,n){var t,i,r;if(r=THe(e,n),r>=0)return r;if(e.ml()){for(i=0;i0||e==(jX(),Vne)||n==(EX(),Yne))throw $(new Gn("Invalid range: "+uPe(e,n)))}function Cde(e,n,t,i){A8();var r,c;for(r=0,c=0;c0),(n&-n)==n)return sc(n*Is(e,31)*4656612873077393e-25);do t=Is(e,31),i=t%n;while(t-i+(n-1)<0);return sc(i)}function ySn(e,n){var t,i,r;for(t=mw(new Nb,e),r=new L(n);r.a1&&(c=ySn(e,n)),c}function xSn(e){var n,t,i;for(n=0,i=new L(e.c.a);i.a102?-1:e<=57?e-48:e<65?-1:e<=70?e-65+10:e<97?-1:e-97+10}function GQ(e,n){if(e==null)throw $(new c4("null key in entry: null="+n));if(n==null)throw $(new c4("null value in entry: "+e+"=null"))}function nHe(e,n){var t;return t=z(B(Jr,1),Jc,30,15,[lQ(e.a[0],n),lQ(e.a[1],n),lQ(e.a[2],n)]),e.d&&(t[0]=k.Math.max(t[0],t[2]),t[2]=t[0]),t}function tHe(e,n){var t;return t=z(B(Jr,1),Jc,30,15,[BB(e.a[0],n),BB(e.a[1],n),BB(e.a[2],n)]),e.d&&(t[0]=k.Math.max(t[0],t[2]),t[2]=t[0]),t}function Dde(e,n,t){k4(u(C(n,(Ne(),Wi)),102))||(Xae(e,n,Pd(n,t)),Xae(e,n,Pd(n,(De(),bt))),Xae(e,n,Pd(n,Xn)),jn(),Tr(n.j,new nEe(e)))}function iHe(e){var n,t;for(e.c||APn(e),t=new xs,n=new L(e.a),_(n);n.a0&&(Yn(0,n.length),n.charCodeAt(0)==43)?(Yn(1,n.length+1),n.substr(1)):n))}function JSn(e){var n;return e==null?null:new E0((n=bo(e,!0),n.length>0&&(Yn(0,n.length),n.charCodeAt(0)==43)?(Yn(1,n.length+1),n.substr(1)):n))}function _de(e,n,t,i,r,c,o,l){var f,h;i&&(f=i.a[0],f&&_de(e,n,t,f,r,c,o,l),ZQ(e,t,i.d,r,c,o,l)&&n.Ec(i),h=i.a[1],h&&_de(e,n,t,h,r,c,o,l))}function GE(e,n){var t,i,r,c;for(c=e.gc(),n.lengthc&&tr(n,c,null),n}function HSn(e,n){var t,i;if(i=e.gc(),n==null){for(t=0;t0&&(f+=r),h[b]=o,o+=l*(f+i)}function YSn(e){var n;for(n=0;n0?e.c:0),++r;e.b=i,e.d=c}function gHe(e,n){var t;return t=z(B(Jr,1),Jc,30,15,[Tde(e,(ga(),Ou),n),Tde(e,No,n),Tde(e,Nu,n)]),e.f&&(t[0]=k.Math.max(t[0],t[2]),t[2]=t[0]),t}function wHe(e){var n;bi(e,(Ne(),bp))&&(n=u(C(e,bp),22),n.Gc((G2(),Qf))?(n.Kc(Qf),n.Ec(Wf)):n.Gc(Wf)&&(n.Kc(Wf),n.Ec(Qf)))}function pHe(e){var n;bi(e,(Ne(),bp))&&(n=u(C(e,bp),22),n.Gc((G2(),ea))?(n.Kc(ea),n.Ec(pf)):n.Gc(pf)&&(n.Kc(pf),n.Ec(ea)))}function YQ(e,n,t,i){var r,c,o,l;return e.a==null&&XMn(e,n),o=n.b.j.c.length,c=t.d.p,l=i.d.p,r=l-1,r<0&&(r=o-1),c<=r?e.a[r]-e.a[c]:e.a[o-1]-e.a[c]+e.a[r]}function QSn(e){var n;for(n=0;n0&&(r.b+=n),r}function dz(e,n){var t,i,r;for(r=new Kr,i=e.Jc();i.Ob();)t=u(i.Pb(),37),M8(t,0,r.b),r.b+=t.f.b+n,r.a=k.Math.max(r.a,t.f.a);return r.a>0&&(r.a+=n),r}function vHe(e,n){var t,i;if(n.length==0)return 0;for(t=MV(e.a,n[0],(De(),Kn)),t+=MV(e.a,n[n.length-1],et),i=0;i>16==6?e.Cb.Qh(e,5,xa,n):(i=Oc(u(An((t=u(Un(e,16),29),t||e.fi()),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function ixn(e){$9();var n=e.e;if(n&&n.stack){var t=n.stack,i=n+` `;return t.substring(0,i.length)==i&&(t=t.substring(i.length)),t.split(` -`)}return[]}function rxn(e){var n;return n=(TBe(),enn),n[e>>>28]|n[e>>24&15]<<4|n[e>>20&15]<<8|n[e>>16&15]<<12|n[e>>12&15]<<16|n[e>>8&15]<<20|n[e>>4&15]<<24|n[e&15]<<28}function kHe(e){var n,t,i;e.b==e.c&&(i=e.a.length,t=b1e(k.Math.max(8,i))<<1,e.b!=0?(n=_f(e.a,t),IBe(e,n,i),e.a=n,e.b=0):Qp(e.a,t),e.c=i)}function cxn(e,n){var t;return t=e.b,t.nf((Xt(),Ps))?t.$f()==(De(),Kn)?-t.Kf().a-ne(re(t.mf(Ps))):n+ne(re(t.mf(Ps))):t.$f()==(De(),Kn)?-t.Kf().a:n}function LO(e){var n;return e.b.c.length!=0&&u(Le(e.b,0),70).a?u(Le(e.b,0),70).a:(n=NV(e),n??""+(e.c?wu(e.c.a,e,0):-1))}function bz(e){var n;return e.f.c.length!=0&&u(Le(e.f,0),70).a?u(Le(e.f,0),70).a:(n=NV(e),n??""+(e.i?wu(e.i.j,e,0):-1))}function uxn(e,n){var t,i;if(n<0||n>=e.gc())return null;for(t=n;t0?e.c:0),r=k.Math.max(r,n.d),++i;e.e=c,e.b=r}function oxn(e){var n,t;if(!e.b)for(e.b=zR(u(e.f,125).jh().i),t=new ut(u(e.f,125).jh());t.e!=t.i.gc();)n=u(ft(t),157),Te(e.b,new AX(n));return e.b}function sxn(e,n){var t,i,r;if(n.dc())return S9(),S9(),eI;for(t=new KOe(e,n.gc()),r=new ut(e);r.e!=r.i.gc();)i=ft(r),n.Gc(i)&&Et(t,i);return t}function $de(e,n,t,i){return n==0?i?(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),e.o):(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),ZC(e.o)):uz(e,n,t,i)}function WQ(e){var n,t;if(e.rb)for(n=0,t=e.rb.i;n>22),r+=i>>22,r<0)?!1:(e.l=t&Ls,e.m=i&Ls,e.h=r&G1,!0)}function ZQ(e,n,t,i,r,c,o){var l,f;return!(n.Re()&&(f=e.a.Le(t,i),f<0||!r&&f==0)||n.Se()&&(l=e.a.Le(t,c),l>0||!o&&l==0))}function hxn(e,n){n8();var t;if(t=e.j.g-n.j.g,t!=0)return 0;switch(e.j.g){case 2:return yQ(n,hve)-yQ(e,hve);case 4:return yQ(e,ave)-yQ(n,ave)}return 0}function dxn(e){switch(e.g){case 0:return iie;case 1:return rie;case 2:return cie;case 3:return uie;case 4:return KJ;case 5:return oie;default:return null}}function Qc(e,n,t){var i,r;return i=(r=new yX,ng(r,n),Mo(r,t),Et((!e.c&&(e.c=new pe(vp,e,12,10)),e.c),r),r),Cd(i,0),O2(i,1),_d(i,!0),Id(i,!0),i}function V4(e,n){var t,i;if(n>=e.i)throw $(new jK(n,e.i));return++e.j,t=e.g[n],i=e.i-n-1,i>0&&Wu(e.g,n+1,e.g,n,i),tr(e.g,--e.i,null),e.Oi(n,t),e.Li(),t}function jHe(e,n){var t,i;return e.Db>>16==17?e.Cb.Qh(e,21,vf,n):(i=Oc(u(An((t=u(Un(e,16),29),t||e.fi()),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function bxn(e){var n,t,i,r;for(jn(),Cr(e.c,e.a),r=new L(e.c);r.at.a.c.length))throw $(new Gn("index must be >= 0 and <= layer node count"));e.c&&qo(e.c.a,e),e.c=t,t&&Pb(t.a,n,e)}function OHe(e,n){this.c=new pt,this.a=e,this.b=n,this.d=u(T(e,(me(),Lv)),316),ue(T(e,(Ne(),o4e)))===ue((rO(),VJ))?this.e=new vxe:this.e=new mxe}function yxn(e,n){var t,i,r,c;for(c=0,i=new L(e);i.a0?n:0),++t;return new je(i,r)}function kxn(e,n){var t,i;for(e.b=0,e.d=new $P,i=new L(n.a);i.a>16==6?e.Cb.Qh(e,6,pr,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),bG)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function Hde(e,n){var t,i;return e.Db>>16==7?e.Cb.Qh(e,1,VD,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),L8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function Gde(e,n){var t,i;return e.Db>>16==9?e.Cb.Qh(e,9,Jt,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),$8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function IHe(e,n){var t,i;return e.Db>>16==5?e.Cb.Qh(e,9,EG,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),Zd)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function _He(e,n){var t,i;return e.Db>>16==7?e.Cb.Qh(e,6,xa,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),n0)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function qde(e,n){var t,i;return e.Db>>16==3?e.Cb.Qh(e,0,QD,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),Wd)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function Ude(e,n){var t,i;return e.Db>>16==3?e.Cb.Qh(e,12,Jt,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),_8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function xxn(e,n,t){var i,r,c;for(t<0&&(t=0),c=e.i,r=t;r$Z)return w8(e,i);if(i==e)return!0}}return!1}function Mxn(e){switch(F$(),e.q.g){case 5:kqe(e,(De(),Xn)),kqe(e,bt);break;case 4:MUe(e,(De(),Xn)),MUe(e,bt);break;default:CVe(e,(De(),Xn)),CVe(e,bt)}}function Txn(e){switch(F$(),e.q.g){case 5:zqe(e,(De(),et)),zqe(e,Kn);break;case 4:BJe(e,(De(),et)),BJe(e,Kn);break;default:OVe(e,(De(),et)),OVe(e,Kn)}}function Cxn(e){var n,t;n=u(T(e,(Gf(),jtn)),15),n?(t=n.a,t==0?he(e,(D0(),yJ),new vQ):he(e,(D0(),yJ),new XR(t))):he(e,(D0(),yJ),new XR(1))}function Oxn(e,n){var t;switch(t=e.i,n.g){case 1:return-(e.n.b+e.o.b);case 2:return e.n.a-t.o.a;case 3:return e.n.b-t.o.b;case 4:return-(e.n.a+e.o.a)}return 0}function Nxn(e,n){switch(e.g){case 0:return n==(Xs(),V1)?zJ:FJ;case 1:return n==(Xs(),V1)?zJ:WN;case 2:return n==(Xs(),V1)?WN:FJ;default:return WN}}function $O(e,n){var t,i,r;for(qo(e.a,n),e.e-=n.r+(e.a.c.length==0?0:e.c),r=Gee,i=new L(e.a);i.a>16==11?e.Cb.Qh(e,10,Jt,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),P8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function LHe(e,n){var t,i;return e.Db>>16==10?e.Cb.Qh(e,11,vf,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),e0)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function PHe(e,n){var t,i;return e.Db>>16==10?e.Cb.Qh(e,12,yf,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),Fm)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function $He(e,n){var t,i,r,c,o;if(n)for(r=n.a.length,t=new Rb(r),o=(t.b-t.a)*t.c<0?(k0(),yb):new S0(t);o.Ob();)c=u(o.Pb(),15),i=B9(n,c.a),i&&kUe(e,i)}function Rxn(){nse();var e,n;for(cBn((x0(),Rn)),VRn(Rn),WQ(Rn),Q8e=(yn(),ih),n=new L(u7e);n.a>19,h=n.h>>19,f!=h?h-f:(r=e.h,l=n.h,r!=l?r-l:(i=e.m,o=n.m,i!=o?i-o:(t=e.l,c=n.l,t-c)))}function RHe(e,n,t){var i,r,c,o,l;for(r=e[t.g],l=new L(n.d);l.a0?e.b:0),++t;n.b=i,n.e=r}function BHe(e){var n,t,i;if(i=e.b,gMe(e.i,i.length)){for(t=i.length*2,e.b=oe(Qne,dN,308,t,0,1),e.c=oe(Qne,dN,308,t,0,1),e.f=t-1,e.i=0,n=e.a;n;n=n.c)qO(e,n,n);++e.g}}function XE(e,n){return e.b.a=k.Math.min(e.b.a,n.c),e.b.b=k.Math.min(e.b.b,n.d),e.a.a=k.Math.max(e.a.a,n.c),e.a.b=k.Math.max(e.a.b,n.d),Hn(e.c,n),!0}function zxn(e,n,t){var i;i=n.c.i,i.k==(zn(),dr)?(he(e,(me(),ja),u(T(i,ja),12)),he(e,gf,u(T(i,gf),12))):(he(e,(me(),ja),n.c),he(e,gf,t.d))}function p8(e,n,t){x8();var i,r,c,o,l,f;return o=n/2,c=t/2,i=k.Math.abs(e.a),r=k.Math.abs(e.b),l=1,f=1,i>o&&(l=o/i),r>c&&(f=c/r),A1(e,k.Math.min(l,f)),e}function Fxn(){Gz();var e,n;try{if(n=u(r0e((y0(),kf),c7),2075),n)return n}catch(t){if(t=sr(t),X(t,101))e=t,Ffe((Lt(),e));else throw $(t)}return new aU}function Jxn(){Gz();var e,n;try{if(n=u(r0e((y0(),kf),hf),2002),n)return n}catch(t){if(t=sr(t),X(t,101))e=t,Ffe((Lt(),e));else throw $(t)}return new uw}function Hxn(){J$e();var e,n;try{if(n=u(r0e((y0(),kf),gg),2084),n)return n}catch(t){if(t=sr(t),X(t,101))e=t,Ffe((Lt(),e));else throw $(t)}return new oT}function Gxn(e,n,t){var i,r;return r=e.e,e.e=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,4,r,n),t?t.lj(i):t=i),r!=n&&(n?t=D8(e,Oz(e,n),t):t=D8(e,e.a,t)),t}function zHe(){t$.call(this),this.e=-1,this.a=!1,this.p=Xr,this.k=-1,this.c=-1,this.b=-1,this.g=!1,this.f=-1,this.j=-1,this.n=-1,this.i=-1,this.d=-1,this.o=Xr}function qxn(e,n){var t,i,r;if(i=e.b.d.d,e.a||(i+=e.b.d.a),r=n.b.d.d,n.a||(r+=n.b.d.a),t=ki(i,r),t==0){if(!e.a&&n.a)return-1;if(!n.a&&e.a)return 1}return t}function Uxn(e,n){var t,i,r;if(i=e.b.b.d,e.a||(i+=e.b.b.a),r=n.b.b.d,n.a||(r+=n.b.b.a),t=ki(i,r),t==0){if(!e.a&&n.a)return-1;if(!n.a&&e.a)return 1}return t}function Xxn(e,n){var t,i,r;if(i=e.b.g.d,e.a||(i+=e.b.g.a),r=n.b.g.d,n.a||(r+=n.b.g.a),t=ki(i,r),t==0){if(!e.a&&n.a)return-1;if(!n.a&&e.a)return 1}return t}function Vde(){Vde=Y,ztn=Eo(qt(qt(qt(new or,(zr(),no),(Ur(),Q3e)),no,W3e),Pc,Z3e),Pc,z3e),Jtn=qt(qt(new or,no,I3e),no,F3e),Ftn=Eo(new or,Pc,H3e)}function Kxn(e){var n,t,i,r,c;for(n=u(T(e,(me(),ox)),92),c=e.n,i=n.Bc().Jc();i.Ob();)t=u(i.Pb(),318),r=t.i,r.c+=c.a,r.d+=c.b,t.c?hXe(t):dXe(t);he(e,ox,null)}function Vxn(e,n,t){var i,r;switch(r=e.b,i=r.d,n.g){case 1:return-i.d-t;case 2:return r.o.a+i.c+t;case 3:return r.o.b+i.a+t;case 4:return-i.b-t;default:return-1}}function FHe(e,n){var t,i;for(i=new L(n);i.a0&&(o=(c&oi)%e.d.length,r=W0e(e,o,c,n),r)?(l=r.ld(t),l):(i=e.ak(c,n,t),e.c.Ec(i),null)}function Wde(e,n){var t,i,r,c;switch(Dd(e,n).Il()){case 3:case 2:{for(t=fv(n),r=0,c=t.i;r=0;i--)if(bn(e[i].d,n)||bn(e[i].d,t)){e.length>=i+1&&e.splice(0,i+1);break}return e}function BO(e,n){var t;return uu(e)&&uu(n)&&(t=e/n,wN0&&(e.b+=2,e.a+=i):(e.b+=1,e.a+=k.Math.min(i,r))}function KHe(e,n){var t,i;if(i=!1,$r(n)&&(i=!0,T4(e,new y2(Pt(n)))),i||X(n,242)&&(i=!0,T4(e,(t=qK(u(n,242)),new k3(t)))),!i)throw $(new MX(U2e))}function hAn(e,n,t,i){var r,c,o;return r=new L1(e.e,1,10,(o=n.c,X(o,88)?u(o,29):(yn(),jf)),(c=t.c,X(c,88)?u(c,29):(yn(),jf)),Ld(e,n),!1),i?i.lj(r):i=r,i}function n0e(e){var n,t;switch(u(T(_r(e),(Ne(),Z5e)),420).g){case 0:return n=e.n,t=e.o,new je(n.a+t.a/2,n.b+t.b/2);case 1:return new wc(e.n);default:return null}}function zO(){zO=Y,YJ=new Lj(ma,0),Cve=new Lj("LEFTUP",1),Nve=new Lj("RIGHTUP",2),Tve=new Lj("LEFTDOWN",3),Ove=new Lj("RIGHTDOWN",4),sie=new Lj("BALANCED",5)}function dAn(e,n,t){var i,r,c;if(i=ki(e.a[n.p],e.a[t.p]),i==0){if(r=u(T(n,(me(),Ty)),16),c=u(T(t,Ty),16),r.Gc(t))return-1;if(c.Gc(n))return 1}return i}function bAn(e){switch(e.g){case 1:return new L_;case 2:return new Ik;case 3:return new R5;case 0:return null;default:throw $(new Gn(Qee+(e.f!=null?e.f:""+e.g)))}}function t0e(e,n,t){switch(n){case 1:!e.n&&(e.n=new pe(ju,e,1,7)),kt(e.n),!e.n&&(e.n=new pe(ju,e,1,7)),er(e.n,u(t,18));return;case 2:X9(e,Pt(t));return}E1e(e,n,t)}function i0e(e,n,t){switch(n){case 3:Dw(e,ne(re(t)));return;case 4:Iw(e,ne(re(t)));return;case 5:Os(e,ne(re(t)));return;case 6:Ns(e,ne(re(t)));return}t0e(e,n,t)}function gz(e,n,t){var i,r,c;c=(i=new yX,i),r=za(c,n,null),r&&r.mj(),Mo(c,t),Et((!e.c&&(e.c=new pe(vp,e,12,10)),e.c),c),Cd(c,0),O2(c,1),_d(c,!0),Id(c,!0)}function r0e(e,n){var t,i,r;return t=Dj(e.i,n),X(t,241)?(r=u(t,241),r.wi()==null,r.ti()):X(t,493)?(i=u(t,1999),r=i.b,r):null}function gAn(e,n,t,i){var r,c;return Nt(n),Nt(t),c=u(eE(e.d,n),15),ERe(!!c,"Row %s not in %s",n,e.e),r=u(eE(e.b,t),15),ERe(!!r,"Column %s not in %s",t,e.c),jze(e,c.a,r.a,i)}function wAn(e){var n,t,i,r,c,o;for(t=null,r=e,c=0,o=r.length;c1||l==-1?(c=u(f,16),r.Wb(Zjn(e,c))):r.Wb(zW(e,u(f,57)))))}function EAn(e,n,t,i){yMe();var r=Xne;function c(){for(var o=0;o0)return!1;return!0}function AAn(e){switch(u(T(e.b,(Ne(),U5e)),381).g){case 1:Zi(So(ou(new wn(null,new pn(e.d,16)),new Zg),new t_),new nM);break;case 2:iIn(e);break;case 0:YTn(e)}}function MAn(e,n,t){var i,r,c;for(i=t,!i&&(i=new i4),i.Tg("Layout",e.a.c.length),c=new L(e.a);c.aXee)return t;r>-1e-6&&++t}return t}function pz(e,n,t){if(X(n,271))return eNn(e,u(n,85),t);if(X(n,276))return Dxn(e,u(n,276),t);throw $(new Gn(u7+Fa(new Su(z(B(Ar,1),On,1,5,[n,t])))))}function mz(e,n,t){if(X(n,271))return nNn(e,u(n,85),t);if(X(n,276))return Ixn(e,u(n,276),t);throw $(new Gn(u7+Fa(new Su(z(B(Ar,1),On,1,5,[n,t])))))}function u0e(e,n){var t;n!=e.b?(t=null,e.b&&(t=IR(e.b,e,-4,t)),n&&(t=K4(n,e,-4,t)),t=pFe(e,n,t),t&&t.mj()):(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,3,n,n))}function WHe(e,n){var t;n!=e.f?(t=null,e.f&&(t=IR(e.f,e,-1,t)),n&&(t=K4(n,e,-1,t)),t=mFe(e,n,t),t&&t.mj()):(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,0,n,n))}function DAn(e,n,t,i){var r,c,o,l;return Fs(e.e)&&(r=n.Jk(),l=n.kd(),c=t.kd(),o=M0(e,1,r,l,c,r.Hk()?C8(e,r,c,X(r,103)&&(u(r,19).Bb&Ec)!=0):-1,!0),i?i.lj(o):i=o),i}function ZHe(e){var n,t,i;if(e==null)return null;if(t=u(e,16),t.dc())return"";for(i=new pd,n=t.Jc();n.Ob();)Bc(i,(Ei(),Pt(n.Pb()))),i.a+=" ";return kK(i,i.a.length-1)}function eGe(e){var n,t,i;if(e==null)return null;if(t=u(e,16),t.dc())return"";for(i=new pd,n=t.Jc();n.Ob();)Bc(i,(Ei(),Pt(n.Pb()))),i.a+=" ";return kK(i,i.a.length-1)}function IAn(e,n){var t,i,r,c,o;for(c=new L(n.a);c.a0&&ic(e,e.length-1)==33)try{return n=gUe(of(e,0,e.length-1)),n.e==null}catch(t){if(t=sr(t),!X(t,32))throw $(t)}return!1}function $An(e,n,t){var i,r,c;switch(i=_r(n),r=GB(i),c=new Qu,gu(c,n),t.g){case 1:xr(c,CO(q4(r)));break;case 2:xr(c,q4(r))}return he(c,(Ne(),vm),re(T(e,vm))),c}function o0e(e){var n,t;return n=u(tt(new qn(Vn(rr(e.a).a.Jc(),new ee))),17),t=u(tt(new qn(Vn(Ni(e.a).a.Jc(),new ee))),17),Re($e(T(n,(me(),Hd))))||Re($e(T(t,Hd)))}function z2(){z2=Y,ZN=new oC("ONE_SIDE",0),GJ=new oC("TWO_SIDES_CORNER",1),qJ=new oC("TWO_SIDES_OPPOSING",2),HJ=new oC("THREE_SIDES",3),JJ=new oC("FOUR_SIDES",4)}function iGe(e,n){var t,i,r,c;for(c=new Ce,r=0,i=n.Jc();i.Ob();){for(t=ve(u(i.Pb(),15).a+r);t.a=e.f)break;Hn(c.c,t)}return c}function RAn(e){var n,t;for(t=new L(e.e.b);t.a0&&SHe(this,this.c-1,(De(),et)),this.c0&&e[0].length>0&&(this.c=Re($e(T(_r(e[0][0]),(me(),Xve))))),this.a=oe(don,Se,2079,e.length,0,2),this.b=oe(bon,Se,2080,e.length,0,2),this.d=new aFe}function JAn(e){return e.c.length==0?!1:(vn(0,e.c.length),u(e.c[0],17)).c.i.k==(zn(),dr)?!0:G3(So(new wn(null,new pn(e,16)),new kk),new o_)}function uGe(e,n){var t,i,r,c,o,l,f;for(l=q2(n),c=n.f,f=n.g,o=k.Math.sqrt(c*c+f*f),r=0,i=new L(l);i.a=0?(t=BO(e,tF),i=xQ(e,tF)):(n=Bb(e,1),t=BO(n,5e8),i=xQ(n,5e8),i=mc(Gh(i,1),Rr(e,1))),hh(Gh(i,32),Rr(t,Ic))}function eMn(e,n,t,i){var r,c,o,l,f;for(r=null,c=0,l=new L(n);l.a1;n>>=1)(n&1)!=0&&(i=H3(i,t)),t.d==1?t=H3(t,t):t=new xJe(ZXe(t.a,t.d,oe($t,ni,30,t.d<<1,15,1)));return i=H3(i,t),i}function w0e(){w0e=Y;var e,n,t,i;for(qme=oe(Jr,Jc,30,25,15,1),Ume=oe(Jr,Jc,30,33,15,1),i=152587890625e-16,n=32;n>=0;n--)Ume[n]=i,i*=.5;for(t=1,e=24;e>=0;e--)qme[e]=t,t*=.5}function cMn(e){var n,t;if(Re($e(ye(e,(Ne(),pm))))){for(t=new qn(Vn(H0(e).a.Jc(),new ee));ht(t);)if(n=u(tt(t),85),Hw(n)&&Re($e(ye(n,kg))))return!0}return!1}function lGe(e){var n,t,i,r;for(n=new Si,t=new Si,r=St(e,0);r.b!=r.d.c;)i=u(jt(r),12),i.e.c.length==0?Xi(t,i,t.c.b,t.c):Xi(n,i,n.c.b,n.c);return Ks(n).Fc(t),n}function fGe(e,n){var t,i,r;hr(e.f,n)&&(n.b=e,i=n.c,wu(e.j,i,0)!=-1||Te(e.j,i),r=n.d,wu(e.j,r,0)!=-1||Te(e.j,r),t=n.a.b,t.c.length!=0&&(!e.i&&(e.i=new CJe(e)),_7n(e.i,t)))}function uMn(e){var n,t,i,r,c;return t=e.c.d,i=t.j,r=e.d.d,c=r.j,i==c?t.p=0&&bn(e.substr(n,3),"GMT")||n>=0&&bn(e.substr(n,3),"UTC"))&&(t[0]=n+3),Zbe(e,t,i)}function sMn(e,n){var t,i,r,c,o;for(c=e.g.a,o=e.g.b,i=new L(e.d);i.at;c--)e[c]|=n[c-t-1]>>>o,e[c-1]=n[c-t-1]<0&&Wu(e.g,n,e.g,n+i,l),o=t.Jc(),e.i+=i,r=0;r>4&15,c=e[i]&15,o[r++]=R8e[t],o[r++]=R8e[c];return gh(o,0,o.length)}function Xo(e){var n,t;return e>=Ec?(n=pN+(e-Ec>>10&1023)&yr,t=56320+(e-Ec&1023)&yr,String.fromCharCode(n)+(""+String.fromCharCode(t))):String.fromCharCode(e&yr)}function mMn(e,n){h2();var t,i,r,c;return r=u(u(pi(e.r,n),22),83),r.gc()>=2?(i=u(r.Jc().Pb(),115),t=e.u.Gc((ps(),nA)),c=e.u.Gc(qy),!i.a&&!t&&(r.gc()==2||c)):!1}function bGe(e,n,t,i,r){var c,o,l;for(c=rXe(e,n,t,i,r),l=!1;!c;)Tz(e,r,!0),l=!0,c=rXe(e,n,t,i,r);l&&Tz(e,r,!1),o=YY(r),o.c.length!=0&&(e.d&&e.d.Fg(o),bGe(e,r,t,i,o))}function kz(){kz=Y,Fre=new v$("NODE_SIZE_REORDERER",0),Rre=new v$("INTERACTIVE_NODE_REORDERER",1),zre=new v$("MIN_SIZE_PRE_PROCESSOR",2),Bre=new v$("MIN_SIZE_POST_PROCESSOR",3)}function jz(){jz=Y,Mce=new zj(ma,0),c8e=new zj("DIRECTED",1),o8e=new zj("UNDIRECTED",2),i8e=new zj("ASSOCIATION",3),u8e=new zj("GENERALIZATION",4),r8e=new zj("DEPENDENCY",5)}function vMn(e,n){var t;if(!Ia(e))throw $(new Uc(BWe));switch(t=Ia(e),n.g){case 1:return-(e.j+e.f);case 2:return e.i-t.g;case 3:return e.j-t.f;case 4:return-(e.i+e.g)}return 0}function yMn(e,n,t){var i,r,c;return i=n.Jk(),c=n.kd(),r=i.Hk()?M0(e,4,i,c,null,C8(e,i,c,X(i,103)&&(u(i,19).Bb&Ec)!=0),!0):M0(e,i.rk()?2:1,i,c,i.gk(),-1,!0),t?t.lj(r):t=r,t}function v8(e,n){var t,i;for(_n(n),i=e.b.c.length,Te(e.b,n);i>0;){if(t=i,i=(i-1)/2|0,e.a.Le(Le(e.b,i),n)<=0)return ol(e.b,t,n),!0;ol(e.b,t,Le(e.b,i))}return ol(e.b,i,n),!0}function v0e(e,n,t,i){var r,c;if(r=0,t)r=BB(e.a[t.g][n.g],i);else for(c=0;c=l)}function gGe(e){switch(e.g){case 0:return new U_;case 1:return new _M;default:throw $(new Gn("No implementation is available for the width approximator "+(e.f!=null?e.f:""+e.g)))}}function y0e(e,n,t,i){var r;if(r=!1,$r(i)&&(r=!0,T9(n,t,Pt(i))),r||o2(i)&&(r=!0,y0e(e,n,t,i)),r||X(i,242)&&(r=!0,Hb(n,t,u(i,242))),!r)throw $(new MX(U2e))}function jMn(e,n){var t,i,r;if(t=n.ni(e.a),t&&(r=wa((!t.b&&(t.b=new Hs((yn(),Ac),Iu,t)),t.b),af),r!=null)){for(i=1;i<(ls(),s7e).length;++i)if(bn(s7e[i],r))return i}return 0}function EMn(e,n){var t,i,r;if(t=n.ni(e.a),t&&(r=wa((!t.b&&(t.b=new Hs((yn(),Ac),Iu,t)),t.b),af),r!=null)){for(i=1;i<(ls(),l7e).length;++i)if(bn(l7e[i],r))return i}return 0}function wGe(e,n){var t,i,r,c;if(_n(n),c=e.a.gc(),c0?1:0;c.a[r]!=t;)c=c.a[r],r=e.a.Le(t.d,c.d)>0?1:0;c.a[r]=i,i.b=t.b,i.a[0]=t.a[0],i.a[1]=t.a[1],t.a[0]=null,t.a[1]=null}function AMn(e){var n,t,i,r;for(n=new Ce,t=oe(ts,pa,30,e.a.c.length,16,1),$fe(t,t.length),r=new L(e.a);r.a0&&KXe((vn(0,t.c.length),u(t.c[0],25)),e),t.c.length>1&&KXe(u(Le(t,t.c.length-1),25),e),n.Ug()}function TMn(e){ps();var n,t;return n=Mi(Z1,z(B(sG,1),ke,280,0,[gb])),!(gO(_R(n,e))>1||(t=Mi(nA,z(B(sG,1),ke,280,0,[eA,qy])),gO(_R(t,e))>1))}function j0e(e,n){var t;t=lo((y0(),kf),e),X(t,493)?Kc(kf,e,new VTe(this,n)):Kc(kf,e,this),dW(this,n),n==(d9(),Y8e)?(this.wb=u(this,2e3),u(n,2002)):this.wb=(x0(),Rn)}function CMn(e){var n,t,i;if(e==null)return null;for(n=null,t=0;tc}function yGe(e,n){var t,i,r;if(S0e(e,n))return!0;for(i=new L(n);i.a=r||n<0)throw $(new jo(Mne+n+dg+r));if(t>=r||t<0)throw $(new jo(Tne+t+dg+r));return n!=t?i=(c=e.Aj(t),e.oj(n,c),c):i=e.vj(t),i}function jGe(e){var n,t,i;if(i=e,e)for(n=0,t=e.Bh();t;t=t.Bh()){if(++n>$Z)return jGe(t);if(i=t,t==e)throw $(new Uc("There is a cycle in the containment hierarchy of "+e))}return i}function Fa(e){var n,t,i;for(i=new Qb(Co,"[","]"),t=e.Jc();t.Ob();)n=t.Pb(),I1(i,ue(n)===ue(e)?"(this Collection)":n==null?Vo:su(n));return i.a?i.e.length==0?i.a.a:i.a.a+(""+i.e):i.c}function S0e(e,n){var t,i;if(i=!1,n.gc()<2)return!1;for(t=0;t1&&(e.j.b+=e.e)):(e.j.a+=t.a,e.j.b=k.Math.max(e.j.b,t.b),e.d.c.length>1&&(e.j.a+=e.e))}function F0(){F0=Y,Tin=z(B(xc,1),qu,64,0,[(De(),Xn),et,bt]),Min=z(B(xc,1),qu,64,0,[et,bt,Kn]),Cin=z(B(xc,1),qu,64,0,[bt,Kn,Xn]),Oin=z(B(xc,1),qu,64,0,[Kn,Xn,et])}function SGe(e){var n,t,i,r,c,o,l,f,h;for(this.a=XJe(e),this.b=new Ce,t=e,i=0,r=t.length;izK(e.d).c?(e.i+=e.g.c,TQ(e.d)):zK(e.d).c>zK(e.g).c?(e.e+=e.d.c,TQ(e.g)):(e.i+=EDe(e.g),e.e+=EDe(e.d),TQ(e.g),TQ(e.d))}function RMn(e,n,t){var i,r,c,o;for(c=n.q,o=n.r,new Gb((ha(),sb),n,c,1),new Gb(sb,c,o,1),r=new L(t);r.al&&(f=l/i),r>c&&(h=c/r),o=k.Math.min(f,h),e.a+=o*(n.a-e.a),e.b+=o*(n.b-e.b)}function JMn(e,n,t,i,r){var c,o;for(o=!1,c=u(Le(t.b,0),26);q_n(e,n,c,i,r)&&(o=!0,TAn(t,c),t.b.c.length!=0);)c=u(Le(t.b,0),26);return t.b.c.length==0&&$O(t.j,t),o&&hz(n.q),o}function A0e(e,n,t,i){var r,c;return t==0?(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),U$(e.o,n,i)):(c=u(An((r=u(Un(e,16),29),r||e.fi()),t),69),c.uk().yk(e,Lo(e),t-dt(e.fi()),n,i))}function dW(e,n){var t;n!=e.sb?(t=null,e.sb&&(t=u(e.sb,52).Qh(e,1,rA,t)),n&&(t=u(n,52).Oh(e,1,rA,t)),t=B1e(e,n,t),t&&t.mj()):(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,4,n,n))}function TGe(e,n){var t,i,r,c;if(n)r=$1(n,"x"),t=new aSe(e),X3(t.a,(_n(r),r)),c=$1(n,"y"),i=new hSe(e),K3(i.a,(_n(c),c));else throw $(new oh("All edge sections need an end point."))}function CGe(e,n){var t,i,r,c;if(n)r=$1(n,"x"),t=new sSe(e),V3(t.a,(_n(r),r)),c=$1(n,"y"),i=new lSe(e),Y3(i.a,(_n(c),c));else throw $(new oh("All edge sections need a start point."))}function HMn(e,n){var t,i,r,c,o,l,f;for(i=Vze(e),c=0,l=i.length;c>22-n,r=e.h<>22-n):n<44?(t=0,i=e.l<>44-n):(t=0,i=0,r=e.l<=Rd?"error":i>=900?"warn":i>=800?"info":"log"),wIe(t,e.a),e.b&&Mbe(n,t,e.b,"Exception: ",!0))}function IGe(e,n){var t,i,r,c,o;for(r=n==1?Mte:Ate,i=r.a.ec().Jc();i.Ob();)for(t=u(i.Pb(),86),o=u(pi(e.f.c,t),22).Jc();o.Ob();)c=u(o.Pb(),49),Te(e.b.b,u(c.b,82)),Te(e.b.a,u(c.b,82).d)}function _Ge(e,n,t,i){var r,c,o,l,f;switch(f=e.b,c=n.d,o=c.j,l=xde(o,f.d[o.g],t),r=gi(pc(c.n),c.a),c.j.g){case 3:case 1:l.a+=r.a;break;case 2:l.b+=r.b;break;case 4:l.b+=r.b}Xi(i,l,i.c.b,i.c)}function XMn(e,n){var t,i,r,c;for(c=n.b.j,e.a=oe($t,ni,30,c.c.length,15,1),r=0,i=0;ie)throw $(new Gn("k must be smaller than n"));return n==0||n==e?1:e==0?0:Zde(e)/(Zde(n)*Zde(e-n))}function M0e(e,n){var t,i,r,c;for(t=new AK(e);t.g==null&&!t.c?mae(t):t.g==null||t.i!=0&&u(t.g[t.i-1],50).Ob();)if(c=u(Cz(t),57),X(c,174))for(i=u(c,174),r=0;r>4],n[t*2+1]=TG[c&15];return gh(n,0,n.length)}function uTn(e){var n,t,i;switch(i=e.c.length,i){case 0:return CV(),Xen;case 1:return n=u(wqe(new L(e)),45),Bpn(n.jd(),n.kd());default:return t=u(Ra(e,oe(wg,eF,45,e.c.length,0,1)),175),new ise(t)}}function Pd(e,n){switch(n.g){case 1:return j4(e.j,(ss(),x3e));case 2:return j4(e.j,(ss(),E3e));case 3:return j4(e.j,(ss(),M3e));case 4:return j4(e.j,(ss(),T3e));default:return jn(),jn(),Sc}}function oTn(e,n){var t,i,r;t=_3n(n,e.e),i=u(Bn(e.g.f,t),15).a,r=e.a.c.length-1,e.a.c.length!=0&&u(Le(e.a,r),295).c==i?(++u(Le(e.a,r),295).a,++u(Le(e.a,r),295).b):Te(e.a,new MOe(i))}function J0(){J0=Y,eln=(Xt(),Fy),nln=Vd,Ysn=Tg,Qsn=Yv,Wsn=ab,Vsn=Vv,Vye=LD,Zsn=Nm,Dre=(Gbe(),Rsn),Ire=Bsn,Qye=Hsn,_re=Usn,Wye=Gsn,Zye=qsn,Yye=zsn,FH=Fsn,JH=Jsn,SD=Xsn,e6e=Ksn,Kye=$sn}function PGe(e,n){var t,i,r,c,o;if(e.e<=n||dyn(e,e.g,n))return e.g;for(c=e.r,i=e.g,o=e.r,r=(c-i)/2+i;i+11&&(e.e.b+=e.a)):(e.e.a+=t.a,e.e.b=k.Math.max(e.e.b,t.b),e.d.c.length>1&&(e.e.a+=e.a))}function fTn(e){var n,t,i,r;switch(r=e.i,n=r.b,i=r.j,t=r.g,r.a.g){case 0:t.a=(e.g.b.o.a-i.a)/2;break;case 1:t.a=n.d.n.a+n.d.a.a;break;case 2:t.a=n.d.n.a+n.d.a.a-i.a;break;case 3:t.b=n.d.n.b+n.d.a.b}}function aTn(e,n,t){var i,r,c;for(r=new qn(Vn(bh(t).a.Jc(),new ee));ht(r);)i=u(tt(r),17),!cc(i)&&!(!cc(i)&&i.c.i.c==i.d.i.c)&&(c=OUe(e,i,t,new pxe),c.c.length>1&&Hn(n.c,c))}function BGe(e,n,t,i,r){if(ii&&(e.a=i),e.br&&(e.b=r),e}function hTn(e){if(X(e,144))return INn(u(e,144));if(X(e,233))return Gjn(u(e,233));if(X(e,21))return qMn(u(e,21));throw $(new Gn(u7+Fa(new Su(z(B(Ar,1),On,1,5,[e])))))}function dTn(e,n,t,i,r){var c,o,l;for(c=!0,o=0;o>>r|t[o+i+1]<>>r,++o}return c}function N0e(e,n,t,i){var r,c,o;if(n.k==(zn(),dr)){for(c=new qn(Vn(rr(n).a.Jc(),new ee));ht(c);)if(r=u(tt(c),17),o=r.c.i.k,o==dr&&e.c.a[r.c.i.c.p]==i&&e.c.a[n.c.p]==t)return!0}return!1}function bTn(e,n){var t,i,r,c;return n&=63,t=e.h&G1,n<22?(c=t>>>n,r=e.m>>n|t<<22-n,i=e.l>>n|e.m<<22-n):n<44?(c=0,r=t>>>n-22,i=e.m>>n-22|e.h<<44-n):(c=0,r=0,i=t>>>n-44),_o(i&Ls,r&Ls,c&G1)}function zGe(e,n,t,i){var r;this.b=i,this.e=e==(eg(),Mx),r=n[t],this.d=g2(ts,[Se,pa],[171,30],16,[r.length,r.length],2),this.a=g2($t,[Se,ni],[54,30],15,[r.length,r.length],2),this.c=new h0e(n,t)}function gTn(e){var n,t,i;for(e.k=new Sae((De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])).length,e.j.c.length),i=new L(e.j);i.a=t)return k8(e,n,i.p),!0;return!1}function uv(e,n,t,i){var r,c,o,l,f,h;for(o=t.length,c=0,r=-1,h=qRe((Yn(n,e.length+1),e.substr(n)),(KK(),Hme)),l=0;lc&&xvn(h,qRe(t[l],Hme))&&(r=l,c=f);return r>=0&&(i[0]=n+c),r}function mTn(e,n,t){var i,r,c,o,l,f,h,b;c=e.d.p,l=c.e,f=c.r,e.g=new CC(f),o=e.d.o.c.p,i=o>0?l[o-1]:oe(c1,Bd,9,0,0,1),r=l[o],h=ot?F0e(e,t,"start index"):n<0||n>t?F0e(n,t,"end index"):rS("end index (%s) must not be less than start index (%s)",z(B(Ar,1),On,1,5,[ve(n),ve(e)]))}function qGe(e,n){var t,i,r,c;for(i=0,r=e.length;i0&&UGe(e,c,t));n.p=0}function jTn(e){var n,t,i,r;for(n=Fb(Kt(new il("Predicates."),"and"),40),t=!0,r=new qc(e);r.b=0?e.hi(r):q0e(e,i);else throw $(new Gn(W0+i.ve()+_S));else throw $(new Gn(YWe+n+QWe));else Fl(e,t,i)}function D0e(e){var n,t;if(t=null,n=!1,X(e,210)&&(n=!0,t=u(e,210).a),n||X(e,265)&&(n=!0,t=""+u(e,265).a),n||X(e,479)&&(n=!0,t=""+u(e,479).a),!n)throw $(new MX(U2e));return t}function I0e(e,n,t){var i,r,c,o,l,f;for(f=Po(e.e.Ah(),n),i=0,l=e.i,r=u(e.g,122),o=0;o=e.d.b.c.length&&(n=new Xu(e.d),n.p=i.p-1,Te(e.d.b,n),t=new Xu(e.d),t.p=i.p,Te(e.d.b,t)),Or(i,u(Le(e.d.b,i.p),25))}function xTn(e){var n,t,i,r;for(t=new Si,fc(t,e.o),i=new $P;t.b!=0;)n=u(t.b==0?null:(at(t.b!=0),$l(t,t.a.a)),500),r=PVe(e,n,!0),r&&Te(i.a,n);for(;i.a.c.length!=0;)n=u(C1e(i),500),PVe(e,n,!1)}function Ge(e){var n;this.c=new Si,this.f=e.e,this.e=e.d,this.i=e.g,this.d=e.c,this.b=e.b,this.k=e.j,this.a=e.a,e.i?this.j=e.i:this.j=(n=u(sa(Qa),10),new _l(n,u(_f(n,n.length),10),0)),this.g=e.f}function cg(){cg=Y,o9e=new h4(vS,0),Sr=new h4("BOOLEAN",1),hc=new h4("INT",2),By=new h4("STRING",3),ec=new h4("DOUBLE",4),Ri=new h4("ENUM",5),Ry=new h4("ENUMSET",6),Wa=new h4("OBJECT",7)}function VE(e,n){var t,i,r,c,o;i=k.Math.min(e.c,n.c),c=k.Math.min(e.d,n.d),r=k.Math.max(e.c+e.b,n.c+n.b),o=k.Math.max(e.d+e.a,n.d+n.a),r=(r/2|0))for(this.e=i?i.c:null,this.d=r;t++0;)She(this);this.b=n,this.a=null}function TTn(e,n){var t,i;n.a?QNn(e,n):(t=u(RX(e.b,n.b),60),t&&t==e.a[n.b.f]&&t.a&&t.a!=n.b.a&&t.c.Ec(n.b),i=u($X(e.b,n.b),60),i&&e.a[i.f]==n.b&&i.a&&i.a!=n.b.a&&n.b.c.Ec(i),DK(e.b,n.b))}function ZGe(e,n){var t,i;if(t=u(zc(e.b,n),127),u(u(pi(e.r,n),22),83).dc()){t.n.b=0,t.n.c=0;return}t.n.b=e.C.b,t.n.c=e.C.c,e.A.Gc((Vs(),Og))&&CXe(e,n),i=dSn(e,n),OW(e,n)==(nv(),db)&&(i+=2*e.w),t.a.a=i}function eqe(e,n){var t,i;if(t=u(zc(e.b,n),127),u(u(pi(e.r,n),22),83).dc()){t.n.d=0,t.n.a=0;return}t.n.d=e.C.d,t.n.a=e.C.a,e.A.Gc((Vs(),Og))&&OXe(e,n),i=hSn(e,n),OW(e,n)==(nv(),db)&&(i+=2*e.w),t.a.b=i}function CTn(e,n){var t,i,r,c;for(c=new Ce,i=new L(n);i.ai&&(Yn(n-1,e.length),e.charCodeAt(n-1)<=32);)--n;return i>0||nt.a&&(i.Gc((rg(),Gx))?r=(n.a-t.a)/2:i.Gc(qx)&&(r=n.a-t.a)),n.b>t.b&&(i.Gc((rg(),Xx))?c=(n.b-t.b)/2:i.Gc(Ux)&&(c=n.b-t.b)),k0e(e,r,c)}function cqe(e,n,t,i,r,c,o,l,f,h,b,p,y){X(e.Cb,88)&&H2(Ms(u(e.Cb,88)),4),Mo(e,t),e.f=o,l8(e,l),f8(e,f),o8(e,h),s8(e,b),_d(e,p),a8(e,y),Id(e,!0),Cd(e,r),e.Xk(c),ng(e,n),i!=null&&(e.i=null,EB(e,i))}function F0e(e,n,t){if(e<0)return rS(uYe,z(B(Ar,1),On,1,5,[t,ve(e)]));if(n<0)throw $(new Gn(oYe+n));return rS("%s (%s) must not be greater than size (%s)",z(B(Ar,1),On,1,5,[t,ve(e),ve(n)]))}function J0e(e,n,t,i,r,c){var o,l,f,h;if(o=i-t,o<7){$jn(n,t,i,c);return}if(f=t+r,l=i+r,h=f+(l-f>>1),J0e(n,e,f,h,-r,c),J0e(n,e,h,l,-r,c),c.Le(e[h-1],e[h])<=0){for(;t=0?e.$h(c,t):ybe(e,r,t);else throw $(new Gn(W0+r.ve()+_S));else throw $(new Gn(YWe+n+QWe));else Jl(e,i,r,t)}function uqe(e){var n,t;if(e.f){for(;e.n>0;){if(n=u(e.k.Xb(e.n-1),75),t=n.Jk(),X(t,103)&&(u(t,19).Bb&Bu)!=0&&(!e.e||t.nk()!=U7||t.Jj()!=0)&&n.kd()!=null)return!0;--e.n}return!1}else return e.n>0}function oqe(e){var n,t,i,r;if(t=u(e,52).Yh(),t)try{if(i=null,n=E8((y0(),kf),WXe(qjn(t))),n&&(r=n.Zh(),r&&(i=r.Dl(Ibn(t.e)))),i&&i!=e)return oqe(i)}catch(c){if(c=sr(c),!X(c,63))throw $(c)}return e}function qTn(e,n,t){var i,r,c;t.Tg("Remove overlaps",1),t.bh(n,Ype),i=u(ye(n,(B3(),Gv)),26),e.f=i,e.a=$Q(u(ye(n,(J0(),SD)),303)),r=re(ye(n,(Xt(),Vd))),Q5(e,(_n(r),r)),c=q2(i),vVe(e,n,c,t),t.bh(n,_F)}function UTn(e){var n,t,i;if(Re($e(ye(e,(Xt(),ID))))){for(i=new Ce,t=new qn(Vn(H0(e).a.Jc(),new ee));ht(t);)n=u(tt(t),85),Hw(n)&&Re($e(ye(n,gce)))&&Hn(i.c,n);return i}else return jn(),jn(),Sc}function sqe(e){if(!e)return eAe(),Wen;var n=e.valueOf?e.valueOf():e;if(n!==e){var t=ite[typeof n];return t?t(n):F1e(typeof n)}else return e instanceof Array||e instanceof k.Array?new n9(e):new i9(e)}function lqe(e,n,t){var i,r,c;switch(c=e.o,i=u(zc(e.p,t),253),r=i.i,r.b=QE(i),r.a=YE(i),r.b=k.Math.max(r.b,c.a),r.b>c.a&&!n&&(r.b=c.a),r.c=-(r.b-c.a)/2,t.g){case 1:r.d=-r.a;break;case 3:r.d=c.b}HW(i),GW(i)}function fqe(e,n,t){var i,r,c;switch(c=e.o,i=u(zc(e.p,t),253),r=i.i,r.b=QE(i),r.a=YE(i),r.a=k.Math.max(r.a,c.b),r.a>c.b&&!n&&(r.a=c.b),r.d=-(r.a-c.b)/2,t.g){case 4:r.c=-r.b;break;case 2:r.c=c.a}HW(i),GW(i)}function XTn(e,n){var t,i,r;return X(n.g,9)&&u(n.g,9).k==(zn(),wr)?Ki:(r=I4(n),r?k.Math.max(0,e.b/2-.5):(t=J3(n),t?(i=ne(re($2(t,(Ne(),xg)))),k.Math.max(0,i/2-.5)):Ki))}function KTn(e,n){var t,i,r;return X(n.g,9)&&u(n.g,9).k==(zn(),wr)?Ki:(r=I4(n),r?k.Math.max(0,e.b/2-.5):(t=J3(n),t?(i=ne(re($2(t,(Ne(),xg)))),k.Math.max(0,i/2-.5)):Ki))}function VTn(e,n){var t,i,r,c,o;if(!n.dc()){if(r=u(n.Xb(0),132),n.gc()==1){KUe(e,r,r,1,0,n);return}for(t=1;t0)try{r=hl(n,Xr,oi)}catch(c){throw c=sr(c),X(c,131)?(i=c,$(new uB(i))):$(c)}return t=(!e.a&&(e.a=new hX(e)),e.a),r=0?u(K(t,r),57):null}function WTn(e,n){if(e<0)return rS(uYe,z(B(Ar,1),On,1,5,["index",ve(e)]));if(n<0)throw $(new Gn(oYe+n));return rS("%s (%s) must be less than size (%s)",z(B(Ar,1),On,1,5,["index",ve(e),ve(n)]))}function ZTn(e){var n,t,i,r,c;if(e==null)return Vo;for(c=new Qb(Co,"[","]"),t=e,i=0,r=t.length;i=0?e.Ih(t,!0,!0):Gw(e,r,!0),163)),u(i,219).Xl(n);else throw $(new Gn(W0+n.ve()+_S))}function U0e(e){var n,t;return e>-0x800000000000&&e<0x800000000000?e==0?0:(n=e<0,n&&(e=-e),t=sc(k.Math.floor(k.Math.log(e)/.6931471805599453)),(!n||e!=k.Math.pow(2,t))&&++t,t):CFe(Pu(e))}function fCn(e){var n,t,i,r,c,o,l;for(c=new zh,t=new L(e);t.a2&&l.e.b+l.j.b<=2&&(r=l,i=o),c.a.yc(r,c),r.q=i);return c}function aCn(e,n,t){t.Tg("Eades radial",1),t.bh(n,_F),e.d=u(ye(n,(B3(),Gv)),26),e.c=ne(re(ye(n,(J0(),JH)))),e.e=$Q(u(ye(n,SD),303)),e.a=Yjn(u(ye(n,e6e),426)),e.b=bAn(u(ye(n,Yye),354)),Wxn(e),t.bh(n,_F)}function hCn(e,n){if(n.Tg("Target Width Setter",1),da(e,(Ja(),Xre)))ji(e,(Yh(),Mm),re(ye(e,Xre)));else throw $(new wd("A target width has to be set if the TargetWidthWidthApproximator should be used."));n.Ug()}function pqe(e,n){var t,i,r;return i=new Ba(e),$u(i,n),he(i,(me(),iH),n),he(i,(Ne(),Wi),(Br(),to)),he(i,Ch,(Vh(),eG)),Tf(i,(zn(),wr)),t=new Qu,gu(t,i),xr(t,(De(),Kn)),r=new Qu,gu(r,i),xr(r,et),i}function mqe(e,n){var t,i,r,c,o;for(e.c[n.p]=!0,Te(e.a,n),o=new L(n.j);o.a=c)o.$b();else for(r=o.Jc(),i=0;i0?Noe():o<0&&Eqe(e,n,-o),!0):!1}function YE(e){var n,t,i,r,c,o,l;if(l=0,e.b==0){for(o=nHe(e,!0),n=0,i=o,r=0,c=i.length;r0&&(l+=t,++n);n>1&&(l+=e.c*(n-1))}else l=oAe(JY(k2(si(mV(e.a),new Bg),new c6)));return l>0?l+e.n.d+e.n.a:0}function QE(e){var n,t,i,r,c,o,l;if(l=0,e.b==0)l=oAe(JY(k2(si(mV(e.a),new r6),new zg)));else{for(o=tHe(e,!0),n=0,i=o,r=0,c=i.length;r0&&(l+=t,++n);n>1&&(l+=e.c*(n-1))}return l>0?l+e.n.b+e.n.c:0}function mCn(e){var n,t;if(e.c.length!=2)throw $(new Uc("Order only allowed for two paths."));n=(vn(0,e.c.length),u(e.c[0],17)),t=(vn(1,e.c.length),u(e.c[1],17)),n.d.i!=t.c.i&&(e.c.length=0,Hn(e.c,t),Hn(e.c,n))}function Sqe(e,n,t){var i;for(ww(t,n.g,n.f),Dl(t,n.i,n.j),i=0;i<(!n.a&&(n.a=new pe(Jt,n,10,11)),n.a).i;i++)Sqe(e,u(K((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a),i),26),u(K((!t.a&&(t.a=new pe(Jt,t,10,11)),t.a),i),26))}function vCn(e,n){var t,i,r,c;for(c=u(zc(e.b,n),127),t=c.a,r=u(u(pi(e.r,n),22),83).Jc();r.Ob();)i=u(r.Pb(),115),i.c&&(t.a=k.Math.max(t.a,wfe(i.c)));if(t.a>0)switch(n.g){case 2:c.n.c=e.s;break;case 4:c.n.b=e.s}}function yCn(e,n){var t,i,r;return t=u(T(n,(Gf(),ky)),15).a-u(T(e,ky),15).a,t==0?(i=Nr(pc(u(T(e,(D0(),KN)),8)),u(T(e,WS),8)),r=Nr(pc(u(T(n,KN),8)),u(T(n,WS),8)),ki(i.a*i.b,r.a*r.b)):t}function kCn(e,n){var t,i,r;return t=u(T(n,(Mu(),$H)),15).a-u(T(e,$H),15).a,t==0?(i=Nr(pc(u(T(e,(Ti(),kD)),8)),u(T(e,_7),8)),r=Nr(pc(u(T(n,kD),8)),u(T(n,_7),8)),ki(i.a*i.b,r.a*r.b)):t}function xqe(e){var n,t;return t=new p0,t.a+="e_",n=R7n(e),n!=null&&(t.a+=""+n),e.c&&e.d&&(Kt((t.a+=" ",t),bz(e.c)),Kt(uo((t.a+="[",t),e.c.i),"]"),Kt((t.a+=eee,t),bz(e.d)),Kt(uo((t.a+="[",t),e.d.i),"]")),t.a}function Aqe(e){switch(e.g){case 0:return new DU;case 1:return new oP;case 2:return new IU;case 3:return new ST;default:throw $(new Gn("No implementation is available for the layout phase "+(e.f!=null?e.f:""+e.g)))}}function V0e(e,n,t,i,r){var c;switch(c=0,r.g){case 1:c=k.Math.max(0,n.b+e.b-(t.b+i));break;case 3:c=k.Math.max(0,-e.b-i);break;case 2:c=k.Math.max(0,-e.a-i);break;case 4:c=k.Math.max(0,n.a+e.a-(t.a+i))}return c}function Mqe(e,n,t){var i,r,c,o,l;if(t)for(r=t.a.length,i=new Rb(r),l=(i.b-i.a)*i.c<0?(k0(),yb):new S0(i);l.Ob();)o=u(l.Pb(),15),c=B9(t,o.a),F2e in c.a||xne in c.a?xIn(e,c,n):URn(e,c,n),Wwn(u(Bn(e.c,d8(c)),85))}function Y0e(e){var n,t;switch(e.b){case-1:return!0;case 0:return t=e.t,t>1||t==-1?(e.b=-1,!0):(n=ff(e),n&&(Cc(),n.jk()==ZZe)?(e.b=-1,!0):(e.b=1,!1));default:case 1:return!1}}function Q0e(e,n){var t,i,r,c;if(li(e),e.c!=0||e.a!=123)throw $(new zt(Ht((Lt(),kZe))));if(c=n==112,i=e.d,t=k9(e.i,125,i),t<0)throw $(new zt(Ht((Lt(),jZe))));return r=of(e.i,i,t),e.d=t+1,P$e(r,c,(e.e&512)==512)}function jCn(e){var n,t,i,r,c,o,l;for(l=Fh(e.c.length),r=new L(e);r.a=0&&i=0?e.Ih(t,!0,!0):Gw(e,r,!0),163)),u(i,219).Ul(n);throw $(new Gn(W0+n.ve()+wne))}function SCn(){nse();var e;return Zan?u(E8((y0(),kf),hf),2e3):(ti(wg,new NL),b$n(),e=u(X(lo((y0(),kf),hf),548)?lo(kf,hf):new OIe,548),Zan=!0,dBn(e),vBn(e),ei((ese(),V8e),e,new m3),Kc(kf,hf,e),e)}function xCn(e,n){var t,i,r,c;e.j=-1,Fs(e.e)?(t=e.i,c=e.i!=0,YC(e,n),i=new L1(e.e,3,e.c,null,n,t,c),r=n.xl(e.e,e.c,null),r=rGe(e,n,r),r?(r.lj(i),r.mj()):ai(e.e,i)):(YC(e,n),r=n.xl(e.e,e.c,null),r&&r.mj())}function Az(e,n){var t,i,r;if(r=0,i=n[0],i>=e.length)return-1;for(t=(Yn(i,e.length),e.charCodeAt(i));t>=48&&t<=57&&(r=r*10+(t-48),++i,!(i>=e.length));)t=(Yn(i,e.length),e.charCodeAt(i));return i>n[0]?n[0]=i:r=-1,r}function ACn(e,n,t){var i,r,c,o,l;o=e.c,l=e.d,c=pu(z(B(Lr,1),Se,8,0,[o.i.n,o.n,o.a])).b,r=(c+pu(z(B(Lr,1),Se,8,0,[l.i.n,l.n,l.a])).b)/2,i=null,o.j==(De(),et)?i=new je(n+o.i.c.c.a+t,r):i=new je(n-t,r),j9(e.a,0,i)}function Hw(e){var n,t,i,r;for(n=null,i=qh(Rl(z(B(Xl,1),On,20,0,[(!e.b&&(e.b=new Nn(vt,e,4,7)),e.b),(!e.c&&(e.c=new Nn(vt,e,5,8)),e.c)])));ht(i);)if(t=u(tt(i),84),r=iu(t),!n)n=r;else if(n!=r)return!1;return!0}function jW(e,n,t){var i;if(++e.j,n>=e.i)throw $(new jo(Mne+n+dg+e.i));if(t>=e.i)throw $(new jo(Tne+t+dg+e.i));return i=e.g[t],n!=t&&(n>16),n=i>>16&16,t=16-n,e=e>>n,i=e-256,n=i>>16&8,t+=n,e<<=n,i=e-W2,n=i>>16&4,t+=n,e<<=n,i=e-yh,n=i>>16&2,t+=n,e<<=n,i=e>>14,n=i&~(i>>1),t+2-n)}function MCn(e,n){var t,i,r;for(r=new Ce,i=St(n.a,0);i.b!=i.d.c;)t=u(jt(i),65),t.c.g==e.g&&ue(T(t.b,(Mu(),Nh)))!==ue(T(t.c,Nh))&&!G3(new wn(null,new pn(r,16)),new OEe(t))&&Hn(r.c,t);return Cr(r,new Mk),r}function Cqe(e,n,t){var i,r,c,o;return X(n,155)&&X(t,155)?(c=u(n,155),o=u(t,155),e.a[c.a][o.a]+e.a[o.a][c.a]):X(n,251)&&X(t,251)&&(i=u(n,251),r=u(t,251),i.a==r.a)?u(T(r.a,(Gf(),ky)),15).a:0}function Oqe(e,n){var t,i,r,c,o,l,f,h;for(h=ne(re(T(n,(Ne(),yx)))),f=e[0].n.a+e[0].o.a+e[0].d.c+h,l=1;l=0?t:(l=fE(Nr(new je(o.c+o.b/2,o.d+o.a/2),new je(c.c+c.b/2,c.d+c.a/2))),-(oKe(c,o)-1)*l)}function CCn(e,n,t){var i;Zi(new wn(null,(!t.a&&(t.a=new pe(Pi,t,6,6)),new pn(t.a,16))),new NTe(e,n)),Zi(new wn(null,(!t.n&&(t.n=new pe(ju,t,1,7)),new pn(t.n,16))),new DTe(e,n)),i=u(ye(t,(Xt(),Kv)),78),i&&Zhe(i,e,n)}function Gw(e,n,t){var i,r,c;if(c=av((ls(),nc),e.Ah(),n),c)return Cc(),u(c,69).vk()||(c=D4(Vc(nc,c))),r=(i=e.Fh(c),u(i>=0?e.Ih(i,!0,!0):Gw(e,c,!0),163)),u(r,219).Ql(n,t);throw $(new Gn(W0+n.ve()+wne))}function W0e(e,n,t,i){var r,c,o,l,f;if(r=e.d[n],r){if(c=r.g,f=r.i,i!=null){for(l=0;l=t&&(i=n,h=(f.c+f.a)/2,o=h-t,f.c<=h-t&&(r=new QK(f.c,o),Pb(e,i++,r)),l=h+t,l<=f.a&&(c=new QK(l,f.a),S2(i,e.c.length),Ij(e.c,i,c)))}function _qe(e,n,t){var i,r,c,o,l,f;if(!n.dc()){for(r=new Si,f=n.Jc();f.Ob();)for(l=u(f.Pb(),40),ei(e.a,ve(l.g),ve(t)),o=(i=St(new S1(l).a.d,0),new E3(i));YT(o.a);)c=u(jt(o.a),65).c,Xi(r,c,r.c.b,r.c);_qe(e,r,t+1)}}function Z0e(e){var n;if(!e.c&&e.g==null)e.d=e._i(e.f),Et(e,e.d),n=e.d;else{if(e.g==null)return!0;if(e.i==0)return!1;n=u(e.g[e.i-1],50)}return n==e.b&&null.Tm>=null.Sm()?(Cz(e),Z0e(e)):n.Ob()}function Lqe(e){if(this.a=e,e.c.i.k==(zn(),wr))this.c=e.c,this.d=u(T(e.c.i,(me(),Du)),64);else if(e.d.i.k==wr)this.c=e.d,this.d=u(T(e.d.i,(me(),Du)),64);else throw $(new Gn("Edge "+e+" is not an external edge."))}function Pqe(e,n){var t,i,r;r=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,3,r,e.b)),n?n!=e&&(Mo(e,n.zb),NY(e,n.d),t=(i=n.c,i??n.zb),IY(e,t==null||bn(t,n.zb)?null:t)):(Mo(e,null),NY(e,0),IY(e,null))}function $qe(e){!nte&&(nte=kRn());var n=e.replace(/[\x00-\x1f\xad\u0600-\u0603\u06dd\u070f\u17b4\u17b5\u200b-\u200f\u2028-\u202e\u2060-\u2064\u206a-\u206f\ufeff\ufff9-\ufffb"\\]/g,function(t){return g4n(t)});return'"'+n+'"'}function ebe(e,n,t,i,r,c){var o,l,f,h,b;if(r!=0)for(ue(e)===ue(t)&&(e=e.slice(n,n+r),n=0),f=t,l=n,h=n+r;l=o)throw $(new b2(n,o));return r=t[n],o==1?i=null:(i=oe(Rce,Ine,415,o-1,0,1),Wu(t,0,i,0,n),c=o-n-1,c>0&&Wu(t,n+1,i,n,c)),g8(e,i),rqe(e,n,r),r}function Rqe(e){var n,t;if(e.f){for(;e.n0)for(o=e.c.d,l=e.d.d,r=A1(Nr(new je(l.a,l.b),o),1/(i+1)),c=new je(o.a,o.b),t=new L(e.a);t.a0?c=q4(t):c=CO(q4(t))),ji(n,T7,c)}function Hqe(e,n){var t,i;if(e.c.length!=0){if(e.c.length==2)iy((vn(0,e.c.length),u(e.c[0],9)),(al(),s1)),iy((vn(1,e.c.length),u(e.c[1],9)),hb);else for(i=new L(e);i.a0&&ZO(e,t,n),c):i.a!=null?(ZO(e,n,t),-1):r.a!=null?(ZO(e,t,n),1):0}function Gqe(e){qV();var n,t,i,r,c,o,l;for(t=new O0,r=new L(e.e.b);r.a=0;)i=t[c],o.$l(i.Jk())&&Et(r,i);!FVe(e,r)&&Fs(e.e)&&s9(e,n.Hk()?M0(e,6,n,(jn(),Sc),null,-1,!1):M0(e,n.rk()?2:1,n,null,null,-1,!1))}function BCn(e,n){var t,i,r,c,o;return e.a==(y8(),rx)?!0:(c=n.a.c,t=n.a.c+n.a.b,!(n.j&&(i=n.A,o=i.c.c.a-i.o.a/2,r=c-(i.n.a+i.o.a),r>o)||n.q&&(i=n.C,o=i.c.c.a-i.o.a/2,r=i.n.a-t,r>o)))}function Uqe(e,n,t){var i,r,c,o,l,f;for(i=0,f=t,n||(i=t*(e.c.length-1),f*=-1),c=new L(e);c.a=0?e.xh(null):e.Mh().Qh(e,-1-n,null,null)),e.yh(u(r,52),t),i&&i.mj(),e.sh()&&e.th()&&t>-1&&ai(e,new Ir(e,9,t,c,r)),r):c}function rbe(e,n){var t,i,r,c,o;for(c=e.b.Ae(n),i=(t=e.a.get(c),t??oe(Ar,On,1,0,5,1)),o=0;o>5,r>=e.d)return e.e<0;if(t=e.a[r],n=1<<(n&31),e.e<0){if(i=QBe(e),r>16)),16).bd(c),l0&&(!(x1(e.a.c)&&n.n.d)&&!(I3(e.a.c)&&n.n.b)&&(n.g.d+=k.Math.max(0,i/2-.5)),!(x1(e.a.c)&&n.n.a)&&!(I3(e.a.c)&&n.n.c)&&(n.g.a-=i-1))}function cUe(e,n,t){var i,r,c,o,l,f;c=u(Le(n.e,0),17).c,i=c.i,r=i.k,f=u(Le(t.g,0),17).d,o=f.i,l=o.k,r==(zn(),dr)?he(e,(me(),ja),u(T(i,ja),12)):he(e,(me(),ja),c),l==dr?he(e,(me(),gf),u(T(o,gf),12)):he(e,(me(),gf),f)}function uUe(e,n){var t,i,r,c,o,l;for(c=new L(e.b);c.a>n,c=e.m>>n|t<<22-n,r=e.l>>n|e.m<<22-n):n<44?(o=i?G1:0,c=t>>n-22,r=e.m>>n-22|t<<44-n):(o=i?G1:0,c=i?Ls:0,r=t>>n-44),_o(r&Ls,c&Ls,o&G1)}function oUe(e,n){var t,i,r,c,o,l,f,h,b;if(e.a.f>0&&X(n,45)&&(e.a.Zj(),h=u(n,45),f=h.jd(),c=f==null?0:Oi(f),o=Nle(e.a,c),t=e.a.d[o],t)){for(i=u(t.g,374),b=t.i,l=0;l=2)for(t=r.Jc(),n=re(t.Pb());t.Ob();)c=n,n=re(t.Pb()),i=k.Math.min(i,(_n(n),n-(_n(c),c)));return i}function sOn(e,n){var t,i,r;for(r=new Ce,i=St(n.a,0);i.b!=i.d.c;)t=u(jt(i),65),t.b.g==e.g&&!bn(t.b.c,DF)&&ue(T(t.b,(Mu(),Nh)))!==ue(T(t.c,Nh))&&!G3(new wn(null,new pn(r,16)),new NEe(t))&&Hn(r.c,t);return Cr(r,new tw),r}function lOn(e,n){var t,i,r;if(ue(n)===ue(Nt(e)))return!0;if(!X(n,16)||(i=u(n,16),r=e.gc(),r!=i.gc()))return!1;if(X(i,59)){for(t=0;t0&&(r=t),o=new L(e.f.e);o.a0?r+=n:r+=1;return r}function pOn(e,n){var t,i,r,c,o,l,f,h,b,p;h=e,f=gE(h,"individualSpacings"),f&&(i=da(n,(Xt(),Jy)),o=!i,o&&(r=new R6,ji(n,Jy,r)),l=u(ye(n,Jy),379),p=f,c=null,p&&(c=(b=BY(p,oe(ze,Se,2,0,6,1)),new PX(p,b))),c&&(t=new JTe(p,l),rc(c,t)))}function mOn(e,n){var t,i,r,c,o,l,f,h,b,p,y;return f=null,p=e,b=null,(uZe in p.a||oZe in p.a||FF in p.a)&&(h=null,y=l1e(n),o=gE(p,uZe),t=new gSe(y),VFe(t.a,o),l=gE(p,oZe),i=new SSe(y),YFe(i.a,l),c=Ow(p,FF),r=new MSe(y),h=(tGe(r.a,c),c),b=h),f=b,f}function vOn(e,n){var t,i,r;if(n===e)return!0;if(X(n,540)){if(r=u(n,833),e.a.d!=r.a.d||z3(e).gc()!=z3(r).gc())return!1;for(i=z3(r).Jc();i.Ob();)if(t=u(i.Pb(),416),cLe(e,t.a.jd())!=u(t.a.kd(),18).gc())return!1;return!0}return!1}function yOn(e,n){var t,i,r,c;for(c=new L(n.a);c.an.c?1:e.bn.b?1:e.a!=n.a?Oi(e.a)-Oi(n.a):e.d==(mE(),Cx)&&n.d==Tx?-1:e.d==Tx&&n.d==Cx?1:0}function xW(e){var n,t,i,r,c,o,l,f;for(r=Ki,i=Dr,t=new L(e.e.b);t.a0&&r0):r<0&&-r0):!1}function jOn(e,n,t,i){var r,c,o,l,f,h,b,p;for(r=(n-e.d)/e.c.c.length,c=0,e.a+=t,e.d=n,p=new L(e.c);p.a>24;return o}function SOn(e){if(e.xe()){var n=e.c;n.ye()?e.o="["+n.n:n.xe()?e.o="["+n.ve():e.o="[L"+n.ve()+";",e.b=n.ue()+"[]",e.k=n.we()+"[]";return}var t=e.j,i=e.d;i=i.split("/"),e.o=MQ(".",[t,MQ("$",i)]),e.b=MQ(".",[t,MQ(".",i)]),e.k=i[i.length-1]}function xOn(e,n){var t,i,r,c,o;for(o=null,c=new L(e.e.a);c.a0&&oN(n,(vn(i-1,e.c.length),u(e.c[i-1],9)),r)>0;)ol(e,i,(vn(i-1,e.c.length),u(e.c[i-1],9))),--i;vn(i,e.c.length),e.c[i]=r}n.b=new pt,n.g=new pt}function vUe(e,n,t){var i,r,c;for(i=1;i0&&n.Le((vn(r-1,e.c.length),u(e.c[r-1],9)),c)>0;)ol(e,r,(vn(r-1,e.c.length),u(e.c[r-1],9))),--r;vn(r,e.c.length),e.c[r]=c}t.a=new pt,t.b=new pt}function Tz(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(c=n.Jc();c.Ob();)r=u(c.Pb(),26),b=r.i+r.g/2,y=r.j+r.f/2,f=e.f,o=f.i+f.g/2,l=f.j+f.f/2,h=b-o,p=y-l,i=k.Math.sqrt(h*h+p*p),h*=e.e/i,p*=e.e/i,t?(b-=h,y-=p):(b+=h,y+=p),Os(r,b-r.g/2),Ns(r,y-r.f/2)}function ov(e){var n,t,i;if(!e.c&&e.b!=null){for(n=e.b.length-4;n>=0;n-=2)for(t=0;t<=n;t+=2)(e.b[t]>e.b[t+2]||e.b[t]===e.b[t+2]&&e.b[t+1]>e.b[t+3])&&(i=e.b[t+2],e.b[t+2]=e.b[t],e.b[t]=i,i=e.b[t+3],e.b[t+3]=e.b[t+1],e.b[t+1]=i);e.c=!0}}function Jf(e){var n,t;return t=new il(Db(e.Pm)),t.a+="@",Kt(t,(n=Oi(e)>>>0,n.toString(16))),e.Sh()?(t.a+=" (eProxyURI: ",uo(t,e.Yh()),e.Hh()&&(t.a+=" eClass: ",uo(t,e.Hh())),t.a+=")"):e.Hh()&&(t.a+=" (eClass: ",uo(t,e.Hh()),t.a+=")"),t.a}function eS(e){var n,t,i,r;if(e.e)throw $(new Uc((M1(bte),qZ+bte.k+UZ)));for(e.d==(vr(),eh)&&Vz(e,Zc),t=new L(e.a.a);t.a>24}return t}function NOn(e,n,t){var i,r,c;if(r=u(zc(e.i,n),318),!r)if(r=new HRe(e.d,n,t),M4(e.i,n,r),mde(n))Qwn(e.a,n.c,n.b,r);else switch(c=ATn(n),i=u(zc(e.p,c),253),c.g){case 1:case 3:r.j=!0,xX(i,n.b,r);break;case 4:case 2:r.k=!0,xX(i,n.c,r)}return r}function DOn(e,n,t,i){var r,c,o,l,f,h;if(l=new z6,f=Po(e.e.Ah(),n),r=u(e.g,122),Cc(),u(n,69).vk())for(o=0;o=0)return r;for(c=1,l=new L(n.j);l.a=0)return r;for(c=1,l=new L(n.j);l.a=0?(n||(n=new jj,i>0&&Bc(n,(Yr(0,i,e.length),e.substr(0,i)))),n.a+="\\",D9(n,t&yr)):n&&D9(n,t&yr);return n?n.a:e}function _On(e){var n,t,i;for(t=new L(e.a.a.b);t.a0&&(!(x1(e.a.c)&&n.n.d)&&!(I3(e.a.c)&&n.n.b)&&(n.g.d-=k.Math.max(0,i/2-.5)),!(x1(e.a.c)&&n.n.a)&&!(I3(e.a.c)&&n.n.c)&&(n.g.a+=k.Math.max(0,i-1)))}function xUe(e,n,t){var i,r;if((e.c-e.b&e.a.length-1)==2)n==(De(),Xn)||n==et?(hB(u(CE(e),16),(al(),s1)),hB(u(CE(e),16),hb)):(hB(u(CE(e),16),(al(),hb)),hB(u(CE(e),16),s1));else for(r=new hE(e);r.a!=r.b;)i=u(zB(r),16),hB(i,t)}function LOn(e,n,t){var i,r,c,o,l,f,h,b,p;for(b=-1,p=0,l=n,f=0,h=l.length;f0&&++p;++b}return p}function POn(e,n){var t,i,r,c,o,l,f;for(r=M9(new roe(e)),l=new qr(r,r.c.length),c=M9(new roe(n)),f=new qr(c,c.c.length),o=null;l.b>0&&f.b>0&&(t=(at(l.b>0),u(l.a.Xb(l.c=--l.b),26)),i=(at(f.b>0),u(f.a.Xb(f.c=--f.b),26)),t==i);)o=t;return o}function $On(e,n){var t,i,r,c;for(n.Tg("Self-Loop pre-processing",1),i=new L(e.a);i.abLe(e,t)?(i=mu(t,(De(),et)),e.d=i.dc()?0:tV(u(i.Xb(0),12)),o=mu(n,Kn),e.b=o.dc()?0:tV(u(o.Xb(0),12))):(r=mu(t,(De(),Kn)),e.d=r.dc()?0:tV(u(r.Xb(0),12)),c=mu(n,et),e.b=c.dc()?0:tV(u(c.Xb(0),12)))}function ROn(e){var n,t,i,r,c,o,l,f;n=!0,r=null,c=null;e:for(f=new L(e.a);f.ae.c));o++)r.a>=e.s&&(c<0&&(c=o),l=o);return f=(e.s+e.c)/2,c>=0&&(i=EIn(e,n,c,l),f=Tgn((vn(i,n.c.length),u(n.c[i],340))),ICn(n,i,t)),f}function Tt(e,n,t){var i,r,c,o,l,f,h;for(o=(c=new Mb,c),Hhe(o,(_n(n),n)),h=(!o.b&&(o.b=new Hs((yn(),Ac),Iu,o)),o.b),f=1;f=2}function JOn(e,n,t,i,r){var c,o,l,f,h,b;for(c=e.c.d.j,o=u(Yu(t,0),8),b=1;b1||(n=Mi(Qf,z(B($c,1),ke,96,0,[W1,Wf])),gO(_R(n,e))>1)||(i=Mi(ea,z(B($c,1),ke,96,0,[l1,pf])),gO(_R(i,e))>1))}function TUe(e){var n,t,i,r,c,o,l;for(n=0,i=new L(e.a);i.a0&&(i.b.n-=i.c,i.b.n<=0&&i.b.u>0&&Vt(n,i.b));for(r=new L(e.i);r.a0&&(i.a.u-=i.c,i.a.u<=0&&i.a.n>0&&Vt(t,i.a))}function Cz(e){var n,t,i,r,c;if(e.g==null&&(e.d=e._i(e.f),Et(e,e.d),e.c))return c=e.f,c;if(n=u(e.g[e.i-1],50),r=n.Pb(),e.e=n,t=e._i(r),t.Ob())e.d=t,Et(e,t);else for(e.d=null;!n.Ob()&&(tr(e.g,--e.i,null),e.i!=0);)i=u(e.g[e.i-1],50),n=i;return r}function GOn(e,n){var t,i,r,c,o,l;if(i=n,r=i.Jk(),J1(e.e,r)){if(r.Qi()&&HR(e,r,i.kd()))return!1}else for(l=Po(e.e.Ah(),r),t=u(e.g,122),c=0;c1||t>1)return 2;return n+t==1?2:0}function Is(e,n){var t,i,r,c,o,l;return c=e.a*FZ+e.b*1502,l=e.b*FZ+11,t=k.Math.floor(l*vN),c+=t,l-=t*Uge,c%=Uge,e.a=c,e.b=l,n<=24?k.Math.floor(e.a*qme[n]):(r=e.a*(1<=2147483648&&(i-=4294967296),i)}function NUe(e,n,t){var i,r,c,o,l,f,h;for(c=new Ce,h=new Si,o=new Si,lLn(e,h,o,n),GPn(e,h,o,n,t),f=new L(e);f.ai.b.g&&Hn(c.c,i);return c}function YOn(e,n,t){var i,r,c,o,l,f;for(l=e.c,o=(t.q?t.q:(jn(),jn(),i1)).vc().Jc();o.Ob();)c=u(o.Pb(),45),i=!b9(si(new wn(null,new pn(l,16)),new u9(new vTe(n,c)))).zd((Ib(),vy)),i&&(f=c.kd(),X(f,4)&&(r=yde(f),r!=null&&(f=r)),n.of(u(c.jd(),147),f))}function QOn(e,n){var t,i,r,c;for(n.Tg("Resize child graph to fit parent.",1),i=new L(e.b);i.a1)for(r=new L(e.a);r.a=0?e.Ih(i,!0,!0):Gw(e,c,!0),163)),u(r,219).Vl(n,t)}else throw $(new Gn(W0+n.ve()+_S))}function eNn(e,n,t){var i,r,c,o,l,f;if(f=Ele(e,u(Bn(e.e,n),26)),l=null,f)switch(f.g){case 3:i=Xse(e,j2(n)),l=(_n(t),t+(_n(i),i));break;case 2:r=Xse(e,j2(n)),o=(_n(t),t+(_n(r),r)),c=Xse(e,u(Bn(e.e,n),26)),l=o-(_n(c),c);break;default:l=t}else l=t;return l}function nNn(e,n,t){var i,r,c,o,l,f;if(f=Ele(e,u(Bn(e.e,n),26)),l=null,f)switch(f.g){case 3:i=Kse(e,j2(n)),l=(_n(t),t+(_n(i),i));break;case 2:r=Kse(e,j2(n)),o=(_n(t),t+(_n(r),r)),c=Kse(e,u(Bn(e.e,n),26)),l=o-(_n(c),c);break;default:l=t}else l=t;return l}function Oz(e,n){var t,i,r,c,o;if(n){for(c=X(e.Cb,88)||X(e.Cb,103),o=!c&&X(e.Cb,335),i=new ut((!n.a&&(n.a=new tE(n,Rc,n)),n.a));i.e!=i.i.gc();)if(t=u(ft(i),87),r=Jz(t),c?X(r,88):o?X(r,159):r)return r;return c?(yn(),jf):(yn(),ih)}else return null}function tNn(e,n){var t,i,r,c,o;for(t=new Ce,r=ou(new wn(null,new pn(e,16)),new L5),c=ou(new wn(null,new pn(e,16)),new Ak),o=U9n(d9n(k2(dNn(z(B(OBn,1),On,832,0,[r,c])),new w_))),i=1;i=2*n&&Te(t,new QK(o[i-1]+n,o[i]-n));return t}function DUe(e,n,t){var i,r,c,o,l,f,h,b;if(t)for(c=t.a.length,i=new Rb(c),l=(i.b-i.a)*i.c<0?(k0(),yb):new S0(i);l.Ob();)o=u(l.Pb(),15),r=B9(t,o.a),r&&(f=v6n(e,(h=(v0(),b=new voe,b),n&&kbe(h,n),h),r),X9(f,N1(r,Ah)),yz(r,f),H0e(r,f),eQ(e,r,f))}function Nz(e){var n,t,i,r,c,o;if(!e.j){if(o=new kL,n=sA,c=n.a.yc(e,n),c==null){for(i=new ut(tu(e));i.e!=i.i.gc();)t=u(ft(i),29),r=Nz(t),er(o,r),Et(o,t);n.a.Ac(e)!=null}_2(o),e.j=new N3((u(K(we((x0(),Rn).o),11),19),o.i),o.g),Ms(e).b&=-33}return e.j}function iNn(e){var n,t,i,r;if(e==null)return null;if(i=bo(e,!0),r=HN.length,bn(i.substr(i.length-r,r),HN)){if(t=i.length,t==4){if(n=(Yn(0,i.length),i.charCodeAt(0)),n==43)return g7e;if(n==45)return yhn}else if(t==3)return g7e}return new aoe(i)}function rNn(e){var n,t,i;return t=e.l,(t&t-1)!=0||(i=e.m,(i&i-1)!=0)||(n=e.h,(n&n-1)!=0)||n==0&&i==0&&t==0?-1:n==0&&i==0&&t!=0?Phe(t):n==0&&i!=0&&t==0?Phe(i)+22:n!=0&&i==0&&t==0?Phe(n)+44:-1}function sv(e,n){var t,i,r,c,o;for(r=n.a&e.f,c=null,i=e.b[r];;i=i.b){if(i==n){c?c.b=n.b:e.b[r]=n.b;break}c=i}for(o=n.f&e.f,c=null,t=e.c[o];;t=t.d){if(t==n){c?c.d=n.d:e.c[o]=n.d;break}c=t}n.e?n.e.c=n.c:e.a=n.c,n.c?n.c.e=n.e:e.e=n.e,--e.i,++e.g}function cNn(e,n){var t;n.d?n.d.b=n.b:e.a=n.b,n.b?n.b.d=n.d:e.e=n.d,!n.e&&!n.c?(t=u(uf(u(L4(e.b,n.a),262)),262),t.a=0,++e.c):(t=u(uf(u(Bn(e.b,n.a),262)),262),--t.a,n.e?n.e.c=n.c:t.b=u(uf(n.c),497),n.c?n.c.e=n.e:t.c=u(uf(n.e),497)),--e.d}function MW(e,n){var t,i,r,c;for(c=new qr(e,0),t=(at(c.b0),c.a.Xb(c.c=--c.b),d2(c,r),at(c.b3&&Kh(e,0,n-3))}function oNn(e){var n,t,i,r;return ue(T(e,(Ne(),wm)))===ue((B1(),Yd))?!e.e&&ue(T(e,fD))!==ue((W9(),tD)):(i=u(T(e,Oie),302),r=Re($e(T(e,Nie)))||ue(T(e,wx))===ue((BE(),eD)),n=u(T(e,z5e),15).a,t=e.a.c.length,!r&&i!=(W9(),tD)&&(n==0||n>t))}function sNn(e,n){var t,i,r,c,o,l,f;for(r=e.Jc();r.Ob();)for(i=u(r.Pb(),9),l=new Qu,gu(l,i),xr(l,(De(),et)),he(l,(me(),rH),(Pn(),!0)),o=n.Jc();o.Ob();)c=u(o.Pb(),9),f=new Qu,gu(f,c),xr(f,Kn),he(f,rH,!0),t=new Mw,he(t,rH,!0),lc(t,l),Gr(t,f)}function lNn(e){var n,t;for(t=0;t0);t++);if(t>0&&t0);n++);return n>0&&t>16!=6&&n){if(w8(e,n))throw $(new Gn(LS+Xqe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Jde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=K4(n,e,6,i)),i=Cle(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,6,n,n))}function Dz(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=3&&n){if(w8(e,n))throw $(new Gn(LS+$Ke(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Ude(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=K4(n,e,12,i)),i=Tle(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,3,n,n))}function kbe(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=9&&n){if(w8(e,n))throw $(new Gn(LS+_Xe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Gde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=K4(n,e,9,i)),i=Ole(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,9,n,n))}function S8(e){var n,t,i,r,c;if(i=ff(e),c=e.j,c==null&&i)return e.Hk()?null:i.gk();if(X(i,159)){if(t=i.hk(),t&&(r=t.ti(),r!=e.i)){if(n=u(i,159),n.lk())try{e.g=r.qi(n,c)}catch(o){if(o=sr(o),X(o,80))e.g=null;else throw $(o)}e.i=r}return e.g}return null}function $Ue(e){var n;return n=new Ce,Te(n,new f4(new je(e.c,e.d),new je(e.c+e.b,e.d))),Te(n,new f4(new je(e.c,e.d),new je(e.c,e.d+e.a))),Te(n,new f4(new je(e.c+e.b,e.d+e.a),new je(e.c+e.b,e.d))),Te(n,new f4(new je(e.c+e.b,e.d+e.a),new je(e.c,e.d+e.a))),n}function aNn(e){var n,t,i,r;for(i=e.a.d.j,r=e.c.d.j,t=new L(e.i.d);t.a>>0),t.toString(16)),JEn(F7n(),(m9(),"Exception during lenientFormat for "+i),n),"<"+i+" threw "+Db(n.Pm)+">";throw $(r)}}function dNn(e){var n,t,i,r,c,o,l,f,h;for(i=!1,n=336,t=0,c=new ZOe(e.length),l=e,f=0,h=l.length;f1)for(n=mw((t=new Nb,++e.b,t),e.d),l=St(c,0);l.b!=l.d.c;)o=u(jt(l),124),Hf(Nf(Of(Df(Cf(new tf,1),0),n),o))}function Iz(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=11&&n){if(w8(e,n))throw $(new Gn(LS+Jbe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Xde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=K4(n,e,10,i)),i=Gle(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,11,n,n))}function mNn(e,n,t){var i,r,c,o,l,f;if(c=0,o=0,e.c)for(f=new L(e.d.i.j);f.ac.a?-1:r.af){for(b=e.d,e.d=oe(z8e,nme,67,2*f+4,0,1),c=0;c=9223372036854776e3?(G9(),jme):(r=!1,e<0&&(r=!0,e=-e),i=0,e>=sg&&(i=sc(e/sg),e-=i*sg),t=0,e>=sy&&(t=sc(e/sy),e-=t*sy),n=sc(e),c=_o(n,t,i),r&&ZY(c),c)}function ONn(e){var n,t,i,r,c;if(c=new Ce,Ao(e.b,new Xke(c)),e.b.c.length=0,c.c.length!=0){for(n=(vn(0,c.c.length),u(c.c[0],80)),t=1,i=c.c.length;t>16!=7&&n){if(w8(e,n))throw $(new Gn(LS+JGe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Hde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=u(n,52).Oh(e,1,VD,i)),i=Mfe(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,7,n,n))}function zUe(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=3&&n){if(w8(e,n))throw $(new Gn(LS+NFe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?qde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=u(n,52).Oh(e,0,QD,i)),i=Tfe(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,3,n,n))}function TW(e,n){A8();var t,i,r,c,o,l,f,h,b;return n.d>e.d&&(l=e,e=n,n=l),n.d<63?yDn(e,n):(o=(e.d&-2)<<4,h=Vae(e,o),b=Vae(n,o),i=KW(e,_4(h,o)),r=KW(n,_4(b,o)),f=TW(h,b),t=TW(i,r),c=TW(KW(h,i),KW(r,b)),c=nZ(nZ(c,f),t),c=_4(c,o),f=_4(f,o<<1),nZ(nZ(f,c),t))}function YO(){YO=Y,Kie=new M3(BQe,0),M4e=new M3("LONGEST_PATH",1),T4e=new M3("LONGEST_PATH_SOURCE",2),Uie=new M3("COFFMAN_GRAHAM",3),A4e=new M3(ree,4),C4e=new M3("STRETCH_WIDTH",5),EH=new M3("MIN_WIDTH",6),qie=new M3("BF_MODEL_ORDER",7),Xie=new M3("DF_MODEL_ORDER",8)}function LNn(e,n){var t,i,r,c,o,l;if(!e.tb){for(c=(!e.rb&&(e.rb=new m2(e,Aa,e)),e.rb),l=new l4(c.i),r=new ut(c);r.e!=r.i.gc();)i=u(ft(r),143),o=i.ve(),t=u(o==null?Ko(l.f,null,i):Pw(l.i,o,i),143),t&&(o==null?Ko(l.f,null,t):Pw(l.i,o,t));e.tb=l}return u(lo(e.tb,n),143)}function QO(e,n){var t,i,r,c,o;if((e.i==null&&vh(e),e.i).length,!e.p){for(o=new l4((3*e.g.i/2|0)+1),r=new m4(e.g);r.e!=r.i.gc();)i=u(LQ(r),179),c=i.ve(),t=u(c==null?Ko(o.f,null,i):Pw(o.i,c,i),179),t&&(c==null?Ko(o.f,null,t):Pw(o.i,c,t));e.p=o}return u(lo(e.p,n),179)}function Mbe(e,n,t,i,r){var c,o,l,f,h;for(DEn(i+DR(t,t.ge()),r),wIe(n,Vjn(t)),c=t.f,c&&Mbe(e,n,c,"Caused by: ",!1),l=(t.k==null&&(t.k=oe(ete,Se,80,0,0,1)),t.k),f=0,h=l.length;f=0;c+=t?1:-1)o=o|n.c.jg(f,c,t,i&&!Re($e(T(n.j,(me(),rb))))&&!Re($e(T(n.j,(me(),_v))))),o=o|n.q.tg(f,c,t),o=o|MXe(e,f[c],t,i);return hr(e.c,n),o}function Lz(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(b=qLe(e.j),p=0,y=b.length;p1&&(e.a=!0),lvn(u(t.b,68),gi(pc(u(n.b,68).c),A1(Nr(pc(u(t.b,68).a),u(n.b,68).a),r))),nLe(e,n),JUe(e,t)}function HUe(e){var n,t,i,r,c,o,l;for(c=new L(e.a.a);c.a0&&c>0?o.p=n++:i>0?o.p=t++:c>0?o.p=r++:o.p=t++}jn(),Cr(e.j,new Iq)}function zNn(e){var n,t;t=null,n=u(Le(e.g,0),17);do{if(t=n.d.i,bi(t,(me(),gf)))return u(T(t,gf),12).i;if(t.k!=(zn(),Qi)&&ht(new qn(Vn(Ni(t).a.Jc(),new ee))))n=u(tt(new qn(Vn(Ni(t).a.Jc(),new ee))),17);else if(t.k!=Qi)return null}while(t&&t.k!=(zn(),Qi));return t}function FNn(e,n){var t,i,r,c,o,l,f,h,b;for(l=n.j,o=n.g,f=u(Le(l,l.c.length-1),113),b=(vn(0,l.c.length),u(l.c[0],113)),h=YQ(e,o,f,b),c=1;ch&&(f=t,b=r,h=i);n.a=b,n.c=f}function qw(e,n,t,i){var r,c;if(r=ue(T(t,(Ne(),bx)))===ue((_0(),dm)),c=u(T(t,B5e),16),bi(e,(me(),Ci)))if(r){if(c.Gc(T(e,gx))&&c.Gc(T(n,gx)))return i*u(T(e,gx),15).a+u(T(e,Ci),15).a}else return u(T(e,Ci),15).a;else return-1;return u(T(e,Ci),15).a}function JNn(e,n,t){var i,r,c,o,l,f,h;for(h=new vd(new dEe(e)),o=z(B(cin,1),lQe,12,0,[n,t]),l=0,f=o.length;lf-e.b&&lf-e.a&&lt.p?1:0:c.Ob()?1:-1}function YNn(e,n){var t,i,r,c,o,l;n.Tg(lWe,1),r=u(ye(e,(Ja(),Bx)),104),c=(!e.a&&(e.a=new pe(Jt,e,10,11)),e.a),o=pxn(c),l=k.Math.max(o.a,ne(re(ye(e,(Yh(),Rx))))-(r.b+r.c)),i=k.Math.max(o.b,ne(re(ye(e,GH)))-(r.d+r.a)),t=i-o.b,ji(e,$x,t),ji(e,Py,l),ji(e,P7,i+t),n.Ug()}function Pz(e){var n,t;if((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i==0)return l1e(e);for(n=u(K((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),0),170),kt((!n.a&&(n.a=new mr(kl,n,5)),n.a)),V3(n,0),Y3(n,0),X3(n,0),K3(n,0),t=(!e.a&&(e.a=new pe(Pi,e,6,6)),e.a);t.i>1;)U2(t,t.i-1);return n}function Po(e,n){Cc();var t,i,r,c;return n?n==(Ei(),mhn)||(n==uhn||n==Dg||n==chn)&&e!=d7e?new Age(e,n):(i=u(n,682),t=i.Yk(),t||(L9(Vc((ls(),nc),n)),t=i.Yk()),c=(!t.i&&(t.i=new pt),t.i),r=u(du(Xc(c.f,e)),2003),!r&&ei(c,e,r=new Age(e,n)),r):thn}function QNn(e,n){var t,i;if(i=PC(e.b,n.b),!i)throw $(new Uc("Invalid hitboxes for scanline constraint calculation."));(Eze(n.b,u(vgn(e.b,n.b),60))||Eze(n.b,u(mgn(e.b,n.b),60)))&&yd(),e.a[n.b.f]=u(RX(e.b,n.b),60),t=u($X(e.b,n.b),60),t&&(e.a[t.f]=n.b)}function WNn(e,n){var t,i,r,c,o,l,f,h,b;for(f=u(T(e,(me(),wi)),12),h=pu(z(B(Lr,1),Se,8,0,[f.i.n,f.n,f.a])).a,b=e.i.n.b,t=dh(e.e),r=t,c=0,o=r.length;c0?c.a?(l=c.b.Kf().a,t>l&&(r=(t-l)/2,c.d.b=r,c.d.c=r)):c.d.c=e.s+t:cE(e.u)&&(i=m0e(c.b),i.c<0&&(c.d.b=-i.c),i.c+i.b>c.b.Kf().a&&(c.d.c=i.c+i.b-c.b.Kf().a))}function rDn(e,n){var t,i,r,c,o;o=new Ce,t=n;do c=u(Bn(e.b,t),132),c.B=t.c,c.D=t.d,Hn(o.c,c),t=u(Bn(e.k,t),17);while(t);return i=(vn(0,o.c.length),u(o.c[0],132)),i.j=!0,i.A=u(i.d.a.ec().Jc().Pb(),17).c.i,r=u(Le(o,o.c.length-1),132),r.q=!0,r.C=u(r.d.a.ec().Jc().Pb(),17).d.i,o}function cDn(e){var n,t;t=u(T(e,(Ne(),yu)),165),n=u(T(e,(me(),mg)),315),t==(Xs(),V1)?(he(e,yu,sD),he(e,mg,(_1(),Dv))):t==yg?(he(e,yu,sD),he(e,mg,(_1(),Sy))):n==(_1(),Dv)?(he(e,yu,V1),he(e,mg,rD)):n==Sy&&(he(e,yu,yg),he(e,mg,rD))}function $z(){$z=Y,vD=new zp,Fon=qt(new or,(zr(),eo),(Ur(),AJ)),Gon=Eo(qt(new or,eo,_J),Pc,IJ),qon=wh(wh(Oj(Eo(qt(new or,Kf,RJ),Pc,$J),no),PJ),BJ),Jon=Eo(qt(qt(qt(new or,r1,TJ),no,OJ),no,g7),Pc,CJ),Hon=Eo(qt(qt(new or,no,g7),no,xJ),Pc,SJ)}function iS(){iS=Y,Kon=qt(Eo(new or,(zr(),Pc),(Ur(),J3e)),eo,AJ),Won=wh(wh(Oj(Eo(qt(new or,Kf,RJ),Pc,$J),no),PJ),BJ),Von=Eo(qt(qt(qt(new or,r1,TJ),no,OJ),no,g7),Pc,CJ),Qon=qt(qt(new or,eo,_J),Pc,IJ),Yon=Eo(qt(qt(new or,no,g7),no,xJ),Pc,SJ)}function uDn(e,n,t,i,r){var c,o;(!cc(n)&&n.c.i.c==n.d.i.c||!OBe(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])),t))&&!cc(n)&&(n.c==r?j9(n.a,0,new wc(t)):Vt(n.a,new wc(t)),i&&!rf(e.a,t)&&(o=u(T(n,(Ne(),Wc)),78),o||(o=new xs,he(n,Wc,o)),c=new wc(t),Xi(o,c,o.c.b,o.c),hr(e.a,c)))}function UUe(e,n){var t,i,r,c;for(c=Rt(ac(Zh,Uh(Rt(ac(n==null?0:Oi(n),e1)),15))),t=c&e.b.length-1,r=null,i=e.b[t];i;r=i,i=i.a)if(i.d==c&&T1(i.i,n))return r?r.a=i.a:e.b[t]=i.a,fAe(u(uf(i.c),593),u(uf(i.f),593)),XT(u(uf(i.b),227),u(uf(i.e),227)),--e.f,++e.e,!0;return!1}function oDn(e){var n,t;for(t=new qn(Vn(rr(e).a.Jc(),new ee));ht(t);)if(n=u(tt(t),17),n.c.i.k!=(zn(),Uu))throw $(new wd(iee+LO(e)+"' has its layer constraint set to FIRST, but has at least one incoming edge that does not come from a FIRST_SEPARATE node. That must not happen."))}function XUe(e,n){var t,i,r,c,o,l,f,h,b,p,y;r=n?new nw:new cM,c=!1;do for(c=!1,h=n?Ks(e.b):e.b,f=h.Jc();f.Ob();)for(l=u(f.Pb(),25),y=qb(l.a),n||Ks(y),p=new L(y);p.a=0;o+=r?1:-1){for(l=n[o],f=i==(De(),et)?r?mu(l,i):Ks(mu(l,i)):r?Ks(mu(l,i)):mu(l,i),c&&(e.c[l.p]=f.gc()),p=f.Jc();p.Ob();)b=u(p.Pb(),12),e.d[b.p]=h++;Er(t,f)}}function VUe(e,n,t){var i,r,c,o,l,f,h,b;for(c=ne(re(e.b.Jc().Pb())),h=ne(re(J7n(n.b))),i=A1(pc(e.a),h-t),r=A1(pc(n.a),t-c),b=gi(i,r),A1(b,1/(h-c)),this.a=b,this.b=new Ce,l=!0,o=e.b.Jc(),o.Pb();o.Ob();)f=ne(re(o.Pb())),l&&f-t>Xee&&(this.b.Ec(t),l=!1),this.b.Ec(f);l&&this.b.Ec(t)}function lDn(e){var n,t,i,r;if(AIn(e,e.n),e.d.c.length>0){for(yj(e.c);obe(e,u(_(new L(e.e.a)),124))>5,n&=31,i>=e.d)return e.e<0?(mh(),rnn):(mh(),KS);if(c=e.d-i,r=oe($t,ni,30,c+1,15,1),dTn(r,c,e.a,i,n),e.e<0){for(t=0;t0&&e.a[t]<<32-n!=0){for(t=0;t=0?!1:(t=av((ls(),nc),r,n),t?(i=t.Gk(),(i>1||i==-1)&&xw(Vc(nc,t))!=3):!0)):!1}function gDn(e,n,t,i){var r,c,o,l,f,h,b,p,y,S;if(f=e.c.d,h=e.d.d,f.j!=h.j)for(S=e.b,b=null,l=null,o=OEn(e),o&&S.i&&(b=e.b.i.i,l=S.i.j),r=f.j,p=null;r!=h.j;)p=n==0?HB(r):X1e(r),c=xde(r,S.d[r.g],t),y=xde(p,S.d[p.g],t),o&&b&&l&&(r==b?JFe(c,b,l):p==b&&JFe(y,b,l)),Vt(i,gi(c,y)),r=p}function Obe(e,n,t){var i,r,c,o,l,f;if(i=cgn(t,e.length),o=e[i],c=gAe(t,o.length),o[c].k==(zn(),wr))for(f=n.j,r=0;r0&&(t[0]+=e.d,o-=t[0]),t[2]>0&&(t[2]+=e.d,o-=t[2]),c=k.Math.max(0,o),t[1]=k.Math.max(t[1],o),Qae(e,No,r.c+i.b+t[0]-(t[1]-o)/2,t),n==No&&(e.c.b=c,e.c.c=r.c+i.b+(c-o)/2)}function iXe(){this.c=oe(Jr,Jc,30,(De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])).length,15,1),this.b=oe(Jr,Jc,30,z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn]).length,15,1),this.a=oe(Jr,Jc,30,z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn]).length,15,1),use(this.c,Ki),use(this.b,Dr),use(this.a,Dr)}function kDn(e,n,t,i){var r,c,o,l,f;for(f=n.i,l=t[f.g][e.d[f.g]],r=!1,o=new L(n.d);o.a=r&&(e.c=!1,e.a=!1),e.b[i++]=r,e.b[i]=c,e.c||ov(e)}}function jDn(e,n,t){var i,r,c,o,l,f,h;for(h=n.d,e.a=new xo(h.c.length),e.c=new pt,l=new L(h);l.a=0?e.Ih(h,!1,!0):Gw(e,t,!1),61));e:for(c=p.Jc();c.Ob();){for(r=u(c.Pb(),57),b=0;be.d[o.p]&&(t+=Hae(e.b,c),C0(e.a,ve(c)));for(;!kj(e.a);)Ehe(e.b,u(A4(e.a),15).a)}return t}function uXe(e,n,t){var i,r,c,o;for(c=(!n.a&&(n.a=new pe(Jt,n,10,11)),n.a).i,r=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));r.e!=r.i.gc();)i=u(ft(r),26),(!i.a&&(i.a=new pe(Jt,i,10,11)),i.a).i==0||(c+=uXe(e,i,!1));if(t)for(o=Bi(n);o;)c+=(!o.a&&(o.a=new pe(Jt,o,10,11)),o.a).i,o=Bi(o);return c}function U2(e,n){var t,i,r,c;return e.Nj()?(i=null,r=e.Oj(),e.Rj()&&(i=e.Tj(e.Yi(n),null)),t=e.Gj(4,c=V4(e,n),null,n,r),e.Kj()&&c!=null&&(i=e.Mj(c,i)),i?(i.lj(t),i.mj()):e.Hj(t),c):(c=V4(e,n),e.Kj()&&c!=null&&(i=e.Mj(c,null),i&&i.mj()),c)}function CDn(e){var n,t,i,r,c,o,l,f,h,b;for(h=e.a,n=new ar,f=0,i=new L(e.d);i.al.d&&(b=l.d+l.a+h));t.c.d=b,n.a.yc(t,n),f=k.Math.max(f,t.c.d+t.c.a)}return f}function ODn(e,n,t){var i,r,c,o,l,f;for(o=u(T(e,(me(),wie)),16).Jc();o.Ob();){switch(c=u(o.Pb(),9),u(T(c,(Ne(),yu)),165).g){case 2:Or(c,n);break;case 4:Or(c,t)}for(r=new qn(Vn(bh(c).a.Jc(),new ee));ht(r);)i=u(tt(r),17),!(i.c&&i.d)&&(l=!i.d,f=u(T(i,Qve),12),l?Gr(i,f):lc(i,f))}}function Dc(){Dc=Y,QJ=new c2("COMMENTS",0),Kl=new c2("EXTERNAL_PORTS",1),cx=new c2("HYPEREDGES",2),WJ=new c2("HYPERNODES",3),S7=new c2("NON_FREE_PORTS",4),Nv=new c2("NORTH_SOUTH_PORTS",5),ux=new c2(MQe,6),j7=new c2("CENTER_LABELS",7),E7=new c2("END_LABELS",8),ZJ=new c2("PARTITIONS",9)}function NDn(e,n,t,i,r){return i<0?(i=uv(e,r,z(B(ze,1),Se,2,6,[mZ,vZ,yZ,kZ,uy,jZ,EZ,SZ,xZ,AZ,MZ,TZ]),n),i<0&&(i=uv(e,r,z(B(ze,1),Se,2,6,["Jan","Feb","Mar","Apr",uy,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),n)),i<0?!1:(t.k=i,!0)):i>0?(t.k=i-1,!0):!1}function DDn(e,n,t,i,r){return i<0?(i=uv(e,r,z(B(ze,1),Se,2,6,[mZ,vZ,yZ,kZ,uy,jZ,EZ,SZ,xZ,AZ,MZ,TZ]),n),i<0&&(i=uv(e,r,z(B(ze,1),Se,2,6,["Jan","Feb","Mar","Apr",uy,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),n)),i<0?!1:(t.k=i,!0)):i>0?(t.k=i-1,!0):!1}function IDn(e,n,t,i,r,c){var o,l,f,h;if(l=32,i<0){if(n[0]>=e.length||(l=ic(e,n[0]),l!=43&&l!=45)||(++n[0],i=Az(e,n),i<0))return!1;l==45&&(i=-i)}return l==32&&n[0]-t==2&&r.b==2&&(f=new t$,h=f.q.getFullYear()-K0+K0-80,o=h%100,c.a=i==o,i+=(h/100|0)*100+(i=0?B0(e):sE(B0(Td(e)))),VS[n]=C$(Gh(e,n),0)?B0(Gh(e,n)):sE(B0(Td(Gh(e,n)))),e=ac(e,5);for(;n=h&&(f=i);f&&(b=k.Math.max(b,f.a.o.a)),b>y&&(p=h,y=b)}return p}function RDn(e){var n,t,i,r,c,o,l;for(c=new vd(u(Nt(new h5),51)),l=Dr,t=new L(e.d);t.aiWe?Cr(f,e.b):i<=iWe&&i>rWe?Cr(f,e.d):i<=rWe&&i>cWe?Cr(f,e.c):i<=cWe&&Cr(f,e.a),c=fXe(e,f,c);return r}function aXe(e,n,t,i){var r,c,o,l,f,h;for(r=(i.c+i.a)/2,qs(n.j),Vt(n.j,r),qs(t.e),Vt(t.e,r),h=new dAe,l=new L(e.f);l.a1,l&&(i=new je(r,t.b),Vt(n.a,i)),SE(n.a,z(B(Lr,1),Se,8,0,[y,p]))}function Ibe(e,n,t){var i,r;for(n=48;t--)hA[t]=t-48<<24>>24;for(i=70;i>=65;i--)hA[i]=i-65+10<<24>>24;for(r=102;r>=97;r--)hA[r]=r-97+10<<24>>24;for(c=0;c<10;c++)TG[c]=48+c&yr;for(e=10;e<=15;e++)TG[e]=65+e-10&yr}function gXe(e,n){n.Tg("Process graph bounds",1),he(e,(Ti(),wre),cC(HY(k2(new wn(null,new pn(e.b,16)),new Zq)))),he(e,pre,cC(HY(k2(new wn(null,new pn(e.b,16)),new Bs)))),he(e,mye,cC(JY(k2(new wn(null,new pn(e.b,16)),new yM)))),he(e,vye,cC(JY(k2(new wn(null,new pn(e.b,16)),new kM)))),n.Ug()}function HDn(e){var n,t,i,r,c;r=u(T(e,(Ne(),jg)),22),c=u(T(e,vH),22),t=new je(e.f.a+e.d.b+e.d.c,e.f.b+e.d.d+e.d.a),n=new wc(t),r.Gc((Vs(),Lm))&&(i=u(T(e,M7),8),c.Gc((_s(),q7))&&(i.a<=0&&(i.a=20),i.b<=0&&(i.b=20)),n.a=k.Math.max(t.a,i.a),n.b=k.Math.max(t.b,i.b)),Re($e(T(e,Rie)))||gLn(e,t,n)}function GDn(e){var n,t,i,r,c,o,l;for(n=!1,t=0,r=new L(e.d.b);r.a>19!=0)return"-"+wXe(e8(e));for(t=e,i="";!(t.l==0&&t.m==0&&t.h==0);){if(r=sY(tF),t=mge(t,r,!0),n=""+NAe(Z0),!(t.l==0&&t.m==0&&t.h==0))for(c=9-n.length;c>0;c--)n="0"+n;i=n+i}return i}function qDn(){if(!Object.create||!Object.getOwnPropertyNames)return!1;var e="__proto__",n=Object.create(null);if(n[e]!==void 0)return!1;var t=Object.getOwnPropertyNames(n);return!(t.length!=0||(n[e]=42,n[e]!==42)||Object.getOwnPropertyNames(n).length==0)}function UDn(e,n,t){var i,r,c,o,l,f,h,b,p;for(i=t.c,r=t.d,l=_a(n.c),f=_a(n.d),i==n.c?(l=vbe(e,l,r),f=pGe(n.d)):(l=pGe(n.c),f=vbe(e,f,r)),h=new qP(n.a),Xi(h,l,h.a,h.a.a),Xi(h,f,h.c.b,h.c),o=n.c==i,p=new cxe,c=0;c=e.a||!b0e(n,t))return-1;if(x2(u(i.Kb(n),20)))return 1;for(r=0,o=u(i.Kb(n),20).Jc();o.Ob();)if(c=u(o.Pb(),17),f=c.c.i==n?c.d.i:c.c.i,l=Pbe(e,f,t,i),l==-1||(r=k.Math.max(r,l),r>e.c-1))return-1;return r+1}function Ja(){Ja=Y,UH=new Vr((Xt(),$7),1.3),Mln=new Vr(Om,(Pn(),!1)),k6e=new pw(15),Bx=new Vr(o1,k6e),zx=new Vr(Vd,15),Eln=ND,Aln=Tg,Tln=Yv,Cln=ab,xln=Vv,qre=LD,Oln=Nm,x6e=(nge(),yln),S6e=vln,Xre=jln,A6e=kln,y6e=wln,Ure=gln,v6e=bln,E6e=mln,p6e=_D,Sln=wce,xD=aln,w6e=fln,AD=hln,j6e=pln,m6e=dln}function pXe(e,n){var t,i,r,c,o,l;if(ue(n)===ue(e))return!0;if(!X(n,16)||(i=u(n,16),l=e.gc(),i.gc()!=l))return!1;if(o=i.Jc(),e.Wi()){for(t=0;t0){if(e.Zj(),n!=null){for(c=0;c>24;case 97:case 98:case 99:case 100:case 101:case 102:return e-97+10<<24>>24;case 65:case 66:case 67:case 68:case 69:case 70:return e-65+10<<24>>24;default:throw $(new sh("Invalid hexadecimal"))}}function vXe(e,n,t,i){var r,c,o,l,f,h;for(f=tW(e,t),h=tW(n,t),r=!1;f&&h&&(i||WSn(f,h,t));)o=tW(f,t),l=tW(h,t),iO(n),iO(e),c=f.c,tZ(f,!1),tZ(h,!1),t?(z0(n,h.p,c),n.p=h.p,z0(e,f.p+1,c),e.p=f.p):(z0(e,f.p,c),e.p=f.p,z0(n,h.p+1,c),n.p=h.p),Or(f,null),Or(h,null),f=o,h=l,r=!0;return r}function yXe(e){switch(e.g){case 0:return new rP;case 1:return new cP;case 3:return new CMe;case 4:return new A6;case 5:return new rNe;case 6:return new ko;case 2:return new uP;case 7:return new kT;case 8:return new yT;default:throw $(new Gn("No implementation is available for the layerer "+(e.f!=null?e.f:""+e.g)))}}function YDn(e,n,t,i){var r,c,o,l,f;for(r=!1,c=!1,l=new L(i.j);l.a=n.length)throw $(new jo("Greedy SwitchDecider: Free layer not in graph."));this.c=n[e],this.e=new CC(i),$Y(this.e,this.c,(De(),Kn)),this.i=new CC(i),$Y(this.i,this.c,et),this.f=new CDe(this.c),this.a=!c&&r.i&&!r.s&&this.c[0].k==(zn(),wr),this.a&&mTn(this,e,n.length)}function jXe(e,n){var t,i,r,c,o,l;c=!e.B.Gc((_s(),UD)),o=e.B.Gc(Cce),e.a=new cJe(o,c,e.c),e.n&&sae(e.a.n,e.n),xX(e.g,(ga(),No),e.a),n||(i=new JE(1,c,e.c),i.n.a=e.k,M4(e.p,(De(),Xn),i),r=new JE(1,c,e.c),r.n.d=e.k,M4(e.p,bt,r),l=new JE(0,c,e.c),l.n.c=e.k,M4(e.p,Kn,l),t=new JE(0,c,e.c),t.n.b=e.k,M4(e.p,et,t))}function WDn(e){var n,t,i;switch(n=u(T(e.d,(Ne(),Y1)),222),n.g){case 2:t=zRn(e);break;case 3:t=(i=new Ce,Zi(si(So(ou(ou(new wn(null,new pn(e.d.b,16)),new Wg),new ZI),new vk),new l0),new Hje(i)),i);break;default:throw $(new Uc("Compaction not supported for "+n+" edges."))}fPn(e,t),rc(new nt(e.g),new Bje(e))}function ZDn(e,n){var t,i,r,c,o,l,f;if(n.Tg("Process directions",1),t=u(T(e,(Mu(),mp)),86),t!=(vr(),Za))for(r=St(e.b,0);r.b!=r.d.c;){switch(i=u(jt(r),40),l=u(T(i,(Ti(),jD)),15).a,f=u(T(i,ED),15).a,t.g){case 4:f*=-1;break;case 1:c=l,l=f,f=c;break;case 2:o=l,l=-f,f=o}he(i,jD,ve(l)),he(i,ED,ve(f))}n.Ug()}function eIn(e){var n,t,i,r,c,o,l,f;for(f=new BPe,l=new L(e.a);l.a0&&n=0)return!1;if(n.p=t.b,Te(t.e,n),r==(zn(),dr)||r==wo){for(o=new L(n.j);o.ae.d[l.p]&&(t+=Hae(e.b,c),C0(e.a,ve(c)))):++o;for(t+=e.b.d*o;!kj(e.a);)Ehe(e.b,u(A4(e.a),15).a)}return t}function IXe(e){var n,t,i,r,c,o;return c=0,n=ff(e),n.ik()&&(c|=4),(e.Bb&as)!=0&&(c|=2),X(e,103)?(t=u(e,19),r=Oc(t),(t.Bb&Bu)!=0&&(c|=32),r&&(dt(E2(r)),c|=8,o=r.t,(o>1||o==-1)&&(c|=16),(r.Bb&Bu)!=0&&(c|=64)),(t.Bb&Ec)!=0&&(c|=U0),c|=qf):X(n,459)?c|=512:(i=n.ik(),i&&(i.i&1)!=0&&(c|=256)),(e.Bb&512)!=0&&(c|=128),c}function hIn(e,n){var t;return e.f==Hce?(t=xw(Vc((ls(),nc),n)),e.e?t==4&&n!=(ey(),Ky)&&n!=(ey(),Xy)&&n!=(ey(),Gce)&&n!=(ey(),qce):t==2):e.d&&(e.d.Gc(n)||e.d.Gc(D4(Vc((ls(),nc),n)))||e.d.Gc(av((ls(),nc),e.b,n)))?!0:e.f&&jbe((ls(),e.f),BC(Vc(nc,n)))?(t=xw(Vc(nc,n)),e.e?t==4:t==2):!1}function dIn(e,n){var t,i,r,c,o,l,f,h;for(c=new Ce,n.b.c.length=0,t=u(gs(Eae(new wn(null,new pn(new nt(e.a.b),1))),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),r=t.Jc();r.Ob();)if(i=u(r.Pb(),15),o=Pae(e.a,i),o.b!=0)for(l=new Xu(n),Hn(c.c,l),l.p=i.a,h=St(o,0);h.b!=h.d.c;)f=u(jt(h),9),Or(f,l);Er(n.b,c)}function _W(e){var n,t,i,r,c,o,l;for(l=new pt,i=new L(e.a.b);i.aag&&(r-=ag),l=u(ye(i,Fy),8),h=l.a,p=l.b+e,c=k.Math.atan2(p,h),c<0&&(c+=ag),c+=n,c>ag&&(c-=ag),Oa(),Bf(1e-10),k.Math.abs(r-c)<=1e-10||r==c||isNaN(r)&&isNaN(c)?0:rc?1:Lb(isNaN(r),isNaN(c))}function Fbe(e,n,t,i){var r,c,o;n&&(c=ne(re(T(n,(Ti(),Xd))))+i,o=t+ne(re(T(n,PH)))/2,he(n,jD,ve(Rt(Pu(k.Math.round(c))))),he(n,ED,ve(Rt(Pu(k.Math.round(o))))),n.d.b==0||Fbe(e,u(P$((r=St(new S1(n).a.d,0),new E3(r))),40),t+ne(re(T(n,PH)))+e.b,i+ne(re(T(n,L7)))),T(n,vre)!=null&&Fbe(e,u(T(n,vre),40),t,i))}function pIn(e,n){var t,i,r,c;if(c=u(ye(e,(Xt(),Qv)),64).g-u(ye(n,Qv),64).g,c!=0)return c;if(t=u(ye(e,kce),15),i=u(ye(n,kce),15),t&&i&&(r=t.a-i.a,r!=0))return r;switch(u(ye(e,Qv),64).g){case 1:return ki(e.i,n.i);case 2:return ki(e.j,n.j);case 3:return ki(n.i,e.i);case 4:return ki(n.j,e.j);default:throw $(new Uc(dwe))}}function Jbe(e){var n,t,i;return(e.Db&64)!=0?bW(e):(n=new il(R2e),t=e.k,t?Kt(Kt((n.a+=' "',n),t),'"'):(!e.n&&(e.n=new pe(ju,e,1,7)),e.n.i>0&&(i=(!e.n&&(e.n=new pe(ju,e,1,7)),u(K(e.n,0),157)).a,!i||Kt(Kt((n.a+=' "',n),i),'"'))),Kt(dw(Kt(dw(Kt(dw(Kt(dw((n.a+=" (",n),e.i),","),e.j)," | "),e.g),","),e.f),")"),n.a)}function _Xe(e){var n,t,i;return(e.Db&64)!=0?bW(e):(n=new il(B2e),t=e.k,t?Kt(Kt((n.a+=' "',n),t),'"'):(!e.n&&(e.n=new pe(ju,e,1,7)),e.n.i>0&&(i=(!e.n&&(e.n=new pe(ju,e,1,7)),u(K(e.n,0),157)).a,!i||Kt(Kt((n.a+=' "',n),i),'"'))),Kt(dw(Kt(dw(Kt(dw(Kt(dw((n.a+=" (",n),e.i),","),e.j)," | "),e.g),","),e.f),")"),n.a)}function mIn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;for(S=-1,A=0,b=n,p=0,y=b.length;p0&&++A;++S}return A}function vIn(e,n){var t,i,r,c,o;for(n==(DE(),cre)&&HO(u(pi(e.a,(z2(),ZN)),16)),r=u(pi(e.a,(z2(),ZN)),16).Jc();r.Ob();)switch(i=u(r.Pb(),107),t=u(Le(i.j,0),113).d.j,c=new bs(i.j),Cr(c,new M5),n.g){case 2:oW(e,c,t,(_w(),ib),1);break;case 1:case 0:o=lNn(c),oW(e,new T0(c,0,o),t,(_w(),ib),0),oW(e,new T0(c,o,c.c.length),t,ib,1)}}function yIn(e){var n,t,i,r,c,o,l;for(r=u(T(e,(me(),ap)),9),i=e.j,t=(vn(0,i.c.length),u(i.c[0],12)),o=new L(r.j);o.ar.p?(xr(c,bt),c.d&&(l=c.o.b,n=c.a.b,c.a.b=l-n)):c.j==bt&&r.p>e.p&&(xr(c,Xn),c.d&&(l=c.o.b,n=c.a.b,c.a.b=-(l-n)));break}return r}function Hbe(e,n){var t,i,r,c,o,l,f;if(n==null||n.length==0)return null;if(r=u(lo(e.a,n),144),!r){for(i=(l=new ct(e.b).a.vc().Jc(),new Ji(l));i.a.Ob();)if(t=(c=u(i.a.Pb(),45),u(c.kd(),144)),o=t.c,f=n.length,bn(o.substr(o.length-f,f),n)&&(n.length==o.length||ic(o,o.length-n.length-1)==46)){if(r)return null;r=t}r&&Kc(e.a,n,r)}return r}function M8(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(c=new je(n,t),b=new L(e.a);b.a1,l&&(i=new je(r,t.b),Vt(n.a,i)),SE(n.a,z(B(Lr,1),Se,8,0,[y,p]))}function G0(){G0=Y,AH=new u2(ma,0),gD=new u2("NIKOLOV",1),wD=new u2("NIKOLOV_PIXEL",2),P4e=new u2("NIKOLOV_IMPROVED",3),$4e=new u2("NIKOLOV_IMPROVED_PIXEL",4),L4e=new u2("DUMMYNODE_PERCENTAGE",5),R4e=new u2("NODECOUNT_PERCENTAGE",6),MH=new u2("NO_BOUNDARY",7),D7=new u2("MODEL_ORDER_LEFT_TO_RIGHT",8),Sx=new u2("MODEL_ORDER_RIGHT_TO_LEFT",9)}function PW(e,n){var t,i,r,c,o,l,f,h,b,p,y,S;return b=null,y=hbe(e,n),i=null,l=u(ye(n,(Xt(),Tfn)),300),l?i=l:i=(jE(),HD),S=i,S==(jE(),HD)&&(r=null,h=u(Bn(e.r,y),300),h?r=h:r=Tce,S=r),ei(e.r,n,S),c=null,f=u(ye(n,Mfn),278),f?c=f:c=(u8(),$D),p=c,p==(u8(),$D)&&(o=null,t=u(Bn(e.b,y),278),t?o=t:o=uG,p=o),b=u(ei(e.b,n,p),278),b}function NIn(e){var n,t,i,r,c;for(i=e.length,n=new jj,c=0;c=40,o&&O_n(e),JLn(e),lDn(e),t=$Fe(e),i=0;t&&i0&&Vt(e.g,c)):(e.d[o]-=h+1,e.d[o]<=0&&e.a[o]>0&&Vt(e.f,c))))}function XXe(e,n,t,i){var r,c,o,l,f,h,b;for(f=new je(t,i),Nr(f,u(T(n,(Ti(),_7)),8)),b=St(n.b,0);b.b!=b.d.c;)h=u(jt(b),40),gi(h.e,f),Vt(e.b,h);for(l=u(gs(yae(new wn(null,new pn(n.a,16))),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16).Jc();l.Ob();){for(o=u(l.Pb(),65),c=St(o.a,0);c.b!=c.d.c;)r=u(jt(c),8),r.a+=f.a,r.b+=f.b;Vt(e.a,o)}}function Qbe(e,n){var t,i,r,c;if(0<(X(e,18)?u(e,18).gc():aa(e.Jc()))){if(r=n,1=0&&f1)&&n==1&&u(e.a[e.b],9).k==(zn(),Uu)?iy(u(e.a[e.b],9),(al(),s1)):i&&(!t||(e.c-e.b&e.a.length-1)>1)&&n==1&&u(e.a[e.c-1&e.a.length-1],9).k==(zn(),Uu)?iy(u(e.a[e.c-1&e.a.length-1],9),(al(),hb)):(e.c-e.b&e.a.length-1)==2?(iy(u(CE(e),9),(al(),s1)),iy(u(CE(e),9),hb)):TOn(e,r),zae(e)}function KIn(e){var n,t,i,r,c,o,l,f;for(f=new pt,n=new gX,o=e.Jc();o.Ob();)r=u(o.Pb(),9),l=mw(nC(new Nb,r),n),Ko(f.f,r,l);for(c=e.Jc();c.Ob();)for(r=u(c.Pb(),9),i=new qn(Vn(Ni(r).a.Jc(),new ee));ht(i);)t=u(tt(i),17),!cc(t)&&Hf(Nf(Of(Cf(Df(new tf,k.Math.max(1,u(T(t,(Ne(),g4e)),15).a)),1),u(Bn(f,t.c.i),124)),u(Bn(f,t.d.i),124)));return n}function YXe(e,n,t,i){var r,c,o,l,f,h,b,p,y,S;if(x8n(e,n,t),c=n[t],S=i?(De(),Kn):(De(),et),Vwn(n.length,t,i)){for(r=n[i?t-1:t+1],rhe(e,r,i?(Nc(),Do):(Nc(),ys)),f=c,b=0,y=f.length;bc*2?(b=new dB(p),h=us(o)/Gs(o),f=uZ(b,n,new t4,t,i,r,h),gi(la(b.e),f),p.c.length=0,c=0,Hn(p.c,b),Hn(p.c,o),c=us(b)*Gs(b)+us(o)*Gs(o)):(Hn(p.c,o),c+=us(o)*Gs(o));return p}function YIn(e,n){var t,i,r,c,o,l,f;for(n.Tg("Port order processing",1),f=u(T(e,(Ne(),b4e)),421),i=new L(e.b);i.at?n:t;h<=p;++h)h==t?l=i++:(c=r[h],b=A.$l(c.Jk()),h==n&&(f=h==p&&!b?i-1:i),b&&++i);return y=u(RE(e,n,t),75),l!=f&&s9(e,new tO(e.e,7,o,ve(l),S.kd(),f)),y}}else return u(jW(e,n,t),75);return u(RE(e,n,t),75)}function Wbe(e,n){var t,i,r,c,o,l,f,h,b,p;for(p=0,c=new P3,C0(c,n);c.b!=c.c;)for(f=u(A4(c),218),h=0,b=u(T(n.j,(Ne(),u1)),269),u(T(n.j,bx),329),o=ne(re(T(n.j,lD))),l=ne(re(T(n.j,Aie))),b!=(F1(),ob)&&(h+=o*LOn(n.j,f.e,b),h+=l*mIn(n.j,f.e)),p+=vHe(f.d,f.e)+h,r=new L(f.b);r.a=0&&(l=axn(e,o),!(l&&(h<22?f.l|=1<>>1,o.m=b>>>1|(p&1)<<21,o.l=y>>>1|(b&1)<<21,--h;return t&&ZY(f),c&&(i?(Z0=e8(e),r&&(Z0=xze(Z0,(G9(),Eme)))):Z0=_o(e.l,e.m,e.h)),f}function ZIn(e,n){var t,i,r,c,o,l,f,h,b,p;for(h=e.e[n.c.p][n.p]+1,f=n.c.a.c.length+1,l=new L(e.a);l.a0&&(Yn(0,e.length),e.charCodeAt(0)==45||(Yn(0,e.length),e.charCodeAt(0)==43))?1:0,i=o;it)throw $(new sh(Yw+e+'"'));return l}function e_n(e){var n,t,i,r,c,o,l;for(o=new Si,c=new L(e.a);c.a=e.length)return t.o=0,!0;switch(ic(e,n[0])){case 43:r=1;break;case 45:r=-1;break;default:return t.o=0,!0}if(++n[0],c=n[0],o=Az(e,n),o==0&&n[0]==c)return!1;if(n[0]l&&(l=r,b.c.length=0),r==l&&Te(b,new jc(t.c.i,t)));jn(),Cr(b,e.c),Pb(e.b,f.p,b)}}function u_n(e,n){var t,i,r,c,o,l,f,h,b;for(o=new L(n.b);o.al&&(l=r,b.c.length=0),r==l&&Te(b,new jc(t.d.i,t)));jn(),Cr(b,e.c),Pb(e.f,f.p,b)}}function o_n(e){var n,t,i,r,c,o,l;for(c=Ia(e),r=new ut((!e.e&&(e.e=new Nn(pr,e,7,4)),e.e));r.e!=r.i.gc();)if(i=u(ft(r),85),l=iu(u(K((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c),0),84)),!C2(l,c))return!0;for(t=new ut((!e.d&&(e.d=new Nn(pr,e,8,5)),e.d));t.e!=t.i.gc();)if(n=u(ft(t),85),o=iu(u(K((!n.b&&(n.b=new Nn(vt,n,4,7)),n.b),0),84)),!C2(o,c))return!0;return!1}function s_n(e){var n,t,i,r,c;i=u(T(e,(me(),wi)),26),c=u(ye(i,(Ne(),jg)),182).Gc((Vs(),Og)),e.e||(r=u(T(e,po),22),n=new je(e.f.a+e.d.b+e.d.c,e.f.b+e.d.d+e.d.a),r.Gc((Dc(),Kl))?(ji(i,Wi,(Br(),to)),Xw(i,n.a,n.b,!1,!0)):Re($e(ye(i,Rie)))||Xw(i,n.a,n.b,!0,!0)),c?ji(i,jg,nn(Og)):ji(i,jg,(t=u(sa(tA),10),new _l(t,u(_f(t,t.length),10),0)))}function l_n(e,n){var t,i,r,c,o,l,f,h;if(h=$e(T(n,(Mu(),ksn))),h==null||(_n(h),h)){for(RCn(e,n),r=new Ce,f=St(n.b,0);f.b!=f.d.c;)o=u(jt(f),40),t=P0e(e,o,null),t&&($u(t,n),Hn(r.c,t));if(e.a=null,e.b=null,r.c.length>1)for(i=new L(r);i.a=0&&l!=t&&(c=new Ir(e,1,l,o,null),i?i.lj(c):i=c),t>=0&&(c=new Ir(e,1,t,l==t?o:null,n),i?i.lj(c):i=c)),i}function WXe(e){var n,t,i;if(e.b==null){if(i=new pd,e.i!=null&&(Bc(i,e.i),i.a+=":"),(e.f&256)!=0){for((e.f&256)!=0&&e.a!=null&&(j5n(e.i)||(i.a+="//"),Bc(i,e.a)),e.d!=null&&(i.a+="/",Bc(i,e.d)),(e.f&16)!=0&&(i.a+="/"),n=0,t=e.j.length;ny?!1:(p=(f=fS(i,y,!1),f.a),b+l+p<=n.b&&(eO(t,c-t.s),t.c=!0,eO(i,c-t.s),IO(i,t.s,t.t+t.d+l),i.k=!0,i1e(t.q,i),S=!0,r&&(vB(n,i),i.j=n,e.c.length>o&&($O((vn(o,e.c.length),u(e.c[o],186)),i),(vn(o,e.c.length),u(e.c[o],186)).a.c.length==0&&Ad(e,o)))),S)}function w_n(e,n){var t,i,r,c,o,l;if(n.Tg("Partition midprocessing",1),r=new Tw,Zi(si(new wn(null,new pn(e.a,16)),new w6),new Eje(r)),r.d!=0){for(l=u(gs(Eae((c=r.i,new wn(null,(c||(r.i=new R3(r,r.c))).Lc()))),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),i=l.Jc(),t=u(i.Pb(),15);i.Ob();)o=u(i.Pb(),15),sNn(u(pi(r,t),22),u(pi(r,o),22)),t=o;n.Ug()}}function uS(e,n){var t,i,r,c,o;if(e.Ab){if(e.Ab){if(o=e.Ab.i,o>0){if(r=u(e.Ab.g,1995),n==null){for(c=0;ct.s&&lf+A&&(O=p.g+y.g,y.a=(y.g*y.a+p.g*p.a)/O,y.g=O,p.f=y,t=!0)),c=l,p=y;return t}function v_n(e,n,t){var i,r,c,o,l,f,h,b;for(t.Tg(UQe,1),Hu(e.b),Hu(e.a),l=null,c=St(n.b,0);!l&&c.b!=c.d.c;)h=u(jt(c),40),Re($e(T(h,(Ti(),fb))))&&(l=h);for(f=new Si,Xi(f,l,f.c.b,f.c),NVe(e,f),b=St(n.b,0);b.b!=b.d.c;)h=u(jt(b),40),o=Pt(T(h,(Ti(),Ix))),r=lo(e.b,o)!=null?u(lo(e.b,o),15).a:0,he(h,gre,ve(r)),i=1+(lo(e.a,o)!=null?u(lo(e.a,o),15).a:0),he(h,pye,ve(i));t.Ug()}function rKe(e){r2(e,new Jw(t2(Zp(n2(e2(new dd,tp),"ELK Box"),"Algorithm for packing of unconnected boxes, i.e. graphs without edges."),new GM))),Ee(e,tp,nm,g9e),Ee(e,tp,em,15),Ee(e,tp,EN,ve(0)),Ee(e,tp,C2e,_e(h9e)),Ee(e,tp,wv,_e(gfn)),Ee(e,tp,hy,_e(wfn)),Ee(e,tp,G8,wWe),Ee(e,tp,jS,_e(d9e)),Ee(e,tp,dy,_e(b9e)),Ee(e,tp,O2e,_e(lce)),Ee(e,tp,TF,_e(bfn))}function cKe(e,n){var t,i,r,c,o,l,f,h,b;if(r=e.i,o=r.o.a,c=r.o.b,o<=0&&c<=0)return De(),ku;switch(h=e.n.a,b=e.n.b,l=e.o.a,t=e.o.b,n.g){case 2:case 1:if(h<0)return De(),Kn;if(h+l>o)return De(),et;break;case 4:case 3:if(b<0)return De(),Xn;if(b+t>c)return De(),bt}return f=(h+l/2)/o,i=(b+t/2)/c,f+i<=1&&f-i<=0?(De(),Kn):f+i>=1&&f-i>=0?(De(),et):i<.5?(De(),Xn):(De(),bt)}function uKe(e,n,t,i,r,c,o){var l,f,h,b,p,y;for(y=new g4,h=n.Jc();h.Ob();)for(l=u(h.Pb(),837),p=new L(l.Pf());p.a0?l.a?(h=l.b.Kf().b,r>h&&(e.v||l.c.d.c.length==1?(o=(r-h)/2,l.d.d=o,l.d.a=o):(t=u(Le(l.c.d,0),187).Kf().b,i=(t-h)/2,l.d.d=k.Math.max(0,i),l.d.a=r-i-h))):l.d.a=e.t+r:cE(e.u)&&(c=m0e(l.b),c.d<0&&(l.d.d=-c.d),c.d+c.a>l.b.Kf().b&&(l.d.a=c.d+c.a-l.b.Kf().b))}function Gf(){Gf=Y,ky=new Vr((Xt(),PD),ve(1)),vJ=new Vr(Vd,80),Stn=new Vr(q9e,5),btn=new Vr($7,H8),jtn=new Vr(Ece,ve(1)),Etn=new Vr(Sce,(Pn(),!0)),c3e=new pw(50),ytn=new Vr(o1,c3e),t3e=_D,u3e=Kx,gtn=new Vr(dce,!1),r3e=LD,mtn=Om,vtn=ab,ptn=Tg,wtn=Vv,ktn=Nm,i3e=(T0e(),otn),kte=atn,mJ=utn,yte=stn,o3e=ftn,Mtn=z7,Ttn=rG,Atn=Im,xtn=B7,s3e=(G4(),Pm),new Vr(Hy,s3e)}function j_n(e,n){var t;switch(lO(e)){case 6:return $r(n);case 7:return s2(n);case 8:return o2(n);case 3:return Array.isArray(n)&&(t=lO(n),!(t>=14&&t<=16));case 11:return n!=null&&typeof n===aZ;case 12:return n!=null&&(typeof n===sN||typeof n==aZ);case 0:return PQ(n,e.__elementTypeId$);case 2:return pV(n)&&n.Rm!==Qn;case 1:return pV(n)&&n.Rm!==Qn||PQ(n,e.__elementTypeId$);default:return!0}}function E_n(e){var n,t,i,r;i=e.o,h2(),e.A.dc()||di(e.A,Qme)?r=i.a:(e.D?r=k.Math.max(i.a,QE(e.f)):r=QE(e.f),e.A.Gc((Vs(),GD))&&!e.B.Gc((_s(),iA))&&(r=k.Math.max(r,QE(u(zc(e.p,(De(),Xn)),253))),r=k.Math.max(r,QE(u(zc(e.p,bt),253)))),n=nze(e),n&&(r=k.Math.max(r,n.a))),Re($e(e.e.Rf().mf((Xt(),Om))))?i.a=k.Math.max(i.a,r):i.a=r,t=e.f.i,t.c=0,t.b=r,HW(e.f)}function oKe(e,n){var t,i,r,c;return i=k.Math.min(k.Math.abs(e.c-(n.c+n.b)),k.Math.abs(e.c+e.b-n.c)),c=k.Math.min(k.Math.abs(e.d-(n.d+n.a)),k.Math.abs(e.d+e.a-n.d)),t=k.Math.abs(e.c+e.b/2-(n.c+n.b/2)),t>e.b/2+n.b/2||(r=k.Math.abs(e.d+e.a/2-(n.d+n.a/2)),r>e.a/2+n.a/2)?1:t==0&&r==0?0:t==0?c/r+1:r==0?i/t+1:k.Math.min(i/t,c/r)+1}function S_n(e,n){var t,i,r,c,o,l,f;for(c=0,l=0,f=0,r=new L(e.f.e);r.a0&&e.d!=(yE(),Ste)&&(l+=o*(i.d.a+e.a[n.a][i.a]*(n.d.a-i.d.a)/t)),t>0&&e.d!=(yE(),jte)&&(f+=o*(i.d.b+e.a[n.a][i.a]*(n.d.b-i.d.b)/t)));switch(e.d.g){case 1:return new je(l/c,n.d.b);case 2:return new je(n.d.a,f/c);default:return new je(l/c,f/c)}}function sKe(e){var n,t,i,r,c,o;for(t=(!e.a&&(e.a=new mr(kl,e,5)),e.a).i+2,o=new xo(t),Te(o,new je(e.j,e.k)),Zi(new wn(null,(!e.a&&(e.a=new mr(kl,e,5)),new pn(e.a,16))),new tSe(o)),Te(o,new je(e.b,e.c)),n=1;n0&&(kO(f,!1,(vr(),Zc)),kO(f,!0,ru)),Ao(n.g,new eTe(e,t)),ei(e.g,n,t)}function nge(){nge=Y,pln=new fn(s2e,(Pn(),!1)),ve(-1),fln=new fn(l2e,ve(-1)),ve(-1),aln=new fn(f2e,ve(-1)),hln=new fn(a2e,!1),dln=new fn(h2e,!1),g6e=(VR(),Kre),kln=new fn(d2e,g6e),jln=new fn(b2e,-1),b6e=(XB(),Gre),yln=new fn(g2e,b6e),vln=new fn(w2e,!0),h6e=(rB(),Vre),wln=new fn(p2e,h6e),gln=new fn(m2e,!1),ve(1),bln=new fn(v2e,ve(1)),d6e=(JB(),Yre),mln=new fn(y2e,d6e)}function aKe(){aKe=Y;var e;for(Nme=z(B($t,1),ni,30,15,[-1,-1,30,19,15,13,11,11,10,9,9,8,8,8,8,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5]),rte=oe($t,ni,30,37,15,1),nnn=z(B($t,1),ni,30,15,[-1,-1,63,40,32,28,25,23,21,20,19,19,18,18,17,17,16,16,16,15,15,15,15,14,14,14,14,14,14,13,13,13,13,13,13,13,13]),Dme=oe(Ep,SYe,30,37,14,1),e=2;e<=36;e++)rte[e]=sc(k.Math.pow(e,Nme[e])),Dme[e]=BO(hN,rte[e])}function x_n(e){var n;if((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i!=1)throw $(new Gn(zWe+(!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i));return n=new xs,KY(u(K((!e.b&&(e.b=new Nn(vt,e,4,7)),e.b),0),84))&&fc(n,VVe(e,KY(u(K((!e.b&&(e.b=new Nn(vt,e,4,7)),e.b),0),84)),!1)),KY(u(K((!e.c&&(e.c=new Nn(vt,e,5,8)),e.c),0),84))&&fc(n,VVe(e,KY(u(K((!e.c&&(e.c=new Nn(vt,e,5,8)),e.c),0),84)),!0)),n}function hKe(e,n){var t,i,r,c,o;for(n.d?r=e.a.c==(ah(),pp)?rr(n.b):Ni(n.b):r=e.a.c==(ah(),Ud)?rr(n.b):Ni(n.b),c=!1,i=new qn(Vn(r.a.Jc(),new ee));ht(i);)if(t=u(tt(i),17),o=Re(e.a.f[e.a.g[n.b.p].p]),!(!o&&!cc(t)&&t.c.i.c==t.d.i.c)&&!(Re(e.a.n[e.a.g[n.b.p].p])||Re(e.a.n[e.a.g[n.b.p].p]))&&(c=!0,rf(e.b,e.a.g[KSn(t,n.b).p])))return n.c=!0,n.a=t,n;return n.c=c,n.a=null,n}function tge(e,n,t){var i,r,c,o,l,f,h;if(i=t.gc(),i==0)return!1;if(e.Nj())if(f=e.Oj(),lde(e,n,t),o=i==1?e.Gj(3,null,t.Jc().Pb(),n,f):e.Gj(5,null,t,n,f),e.Kj()){for(l=i<100?null:new m0(i),c=n+i,r=n;r0){for(o=0;o>16==-15&&e.Cb.Vh()&&jY(new pY(e.Cb,9,13,t,e.c,Ld(Cs(u(e.Cb,62)),e))):X(e.Cb,88)&&e.Db>>16==-23&&e.Cb.Vh()&&(n=e.c,X(n,88)||(n=(yn(),jf)),X(t,88)||(t=(yn(),jf)),jY(new pY(e.Cb,9,10,t,n,Ld(Vu(u(e.Cb,29)),e)))))),e.c}function gKe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A;if(n==t)return!0;if(n=ube(e,n),t=ube(e,t),i=HQ(n),i){if(b=HQ(t),b!=i)return b?(f=i.kk(),A=b.kk(),f==A&&f!=null):!1;if(o=(!n.d&&(n.d=new mr(Rc,n,1)),n.d),c=o.i,y=(!t.d&&(t.d=new mr(Rc,t,1)),t.d),c==y.i){for(h=0;h0,l=UB(n,c),fle(t?l.b:l.g,n),Z3(l).c.length==1&&Xi(i,l,i.c.b,i.c),r=new jc(c,n),C0(e.o,r),qo(e.e.a,c))}function mKe(e,n){var t,i,r,c,o,l,f;return i=k.Math.abs(pR(e.b).a-pR(n.b).a),l=k.Math.abs(pR(e.b).b-pR(n.b).b),r=0,f=0,t=1,o=1,i>e.b.b/2+n.b.b/2&&(r=k.Math.min(k.Math.abs(e.b.c-(n.b.c+n.b.b)),k.Math.abs(e.b.c+e.b.b-n.b.c)),t=1-r/i),l>e.b.a/2+n.b.a/2&&(f=k.Math.min(k.Math.abs(e.b.d-(n.b.d+n.b.a)),k.Math.abs(e.b.d+e.b.a-n.b.d)),o=1-f/l),c=k.Math.min(t,o),(1-c)*k.Math.sqrt(i*i+l*l)}function N_n(e){var n,t,i,r;for(cZ(e,e.e,e.f,(Cw(),lb),!0,e.c,e.i),cZ(e,e.e,e.f,lb,!1,e.c,e.i),cZ(e,e.e,e.f,Jv,!0,e.c,e.i),cZ(e,e.e,e.f,Jv,!1,e.c,e.i),T_n(e,e.c,e.e,e.f,e.i),i=new qr(e.i,0);i.b=65;t--)rh[t]=t-65<<24>>24;for(i=122;i>=97;i--)rh[i]=i-97+26<<24>>24;for(r=57;r>=48;r--)rh[r]=r-48+52<<24>>24;for(rh[43]=62,rh[47]=63,c=0;c<=25;c++)t0[c]=65+c&yr;for(o=26,f=0;o<=51;++o,f++)t0[o]=97+f&yr;for(e=52,l=0;e<=61;++e,l++)t0[e]=48+l&yr;t0[62]=43,t0[63]=47}function vKe(e,n){var t,i,r,c,o,l;return r=Whe(e),l=Whe(n),r==l?e.e==n.e&&e.a<54&&n.a<54?e.fn.f?1:0:(i=e.e-n.e,t=(e.d>0?e.d:k.Math.floor((e.a-1)*xYe)+1)-(n.d>0?n.d:k.Math.floor((n.a-1)*xYe)+1),t>i+1?r:t0&&(o=H3(o,NKe(i))),yJe(c,o))):rh&&(y=0,S+=f+n,f=0),M8(o,y,S),t=k.Math.max(t,y+b.a),f=k.Math.max(f,b.b),y+=b.a+n;return new je(t+n,S+f+n)}function uge(e,n){var t,i,r,c,o,l,f;if(!Ia(e))throw $(new Uc(BWe));if(i=Ia(e),c=i.g,r=i.f,c<=0&&r<=0)return De(),ku;switch(l=e.i,f=e.j,n.g){case 2:case 1:if(l<0)return De(),Kn;if(l+e.g>c)return De(),et;break;case 4:case 3:if(f<0)return De(),Xn;if(f+e.f>r)return De(),bt}return o=(l+e.g/2)/c,t=(f+e.f/2)/r,o+t<=1&&o-t<=0?(De(),Kn):o+t>=1&&o-t>=0?(De(),et):t<.5?(De(),Xn):(De(),bt)}function __n(e,n,t,i,r){var c,o;if(c=mc(Rr(n[0],Ic),Rr(i[0],Ic)),e[0]=Rt(c),c=kw(c,32),t>=r){for(o=1;o0&&(r.b[o++]=0,r.b[o++]=c.b[0]-1),n=1;n0&&(Cb(f,f.d-r.d),r.c==(ha(),sb)&&tX(f,f.a-r.d),f.d<=0&&f.i>0&&Xi(n,f,n.c.b,n.c)));for(c=new L(e.f);c.a0&&(g0(l,l.i-r.d),r.c==(ha(),sb)&&AP(l,l.b-r.d),l.i<=0&&l.d>0&&Xi(t,l,t.c.b,t.c)))}function $_n(e,n,t,i,r){var c,o,l,f,h,b,p,y,S;for(jn(),Cr(e,new XM),o=DC(e),S=new Ce,y=new Ce,l=null,f=0;o.b!=0;)c=u(o.b==0?null:(at(o.b!=0),$l(o,o.a.a)),167),!l||us(l)*Gs(l)/21&&(f>us(l)*Gs(l)/2||o.b==0)&&(p=new dB(y),b=us(l)/Gs(l),h=uZ(p,n,new t4,t,i,r,b),gi(la(p.e),h),l=p,Hn(S.c,p),f=0,y.c.length=0));return Er(S,y),S}function Wu(e,n,t,i,r){yd();var c,o,l,f,h,b,p;if(Rfe(e,"src"),Rfe(t,"dest"),p=Us(e),f=Us(t),ffe((p.i&4)!=0,"srcType is not an array"),ffe((f.i&4)!=0,"destType is not an array"),b=p.c,o=f.c,ffe((b.i&1)!=0?b==o:(o.i&1)==0,"Array types don't match"),skn(e,n,t,i,r),(b.i&1)==0&&p!=f)if(h=B4(e),c=B4(t),ue(e)===ue(t)&&ni;)tr(c,l,h[--n]);else for(l=i+r;i0),i.a.Xb(i.c=--i.b),p>y+f&&As(i);for(o=new L(S);o.a0),i.a.Xb(i.c=--i.b)}}function B_n(){fi();var e,n,t,i,r,c;if(Xce)return Xce;for(e=new ul(4),V2(e,q0(qne,!0)),dS(e,q0("M",!0)),dS(e,q0("C",!0)),c=new ul(4),i=0;i<11;i++)ho(c,i,i);return n=new ul(4),V2(n,q0("M",!0)),ho(n,4448,4607),ho(n,65438,65439),r=new Kj(2),ug(r,e),ug(r,bA),t=new Kj(2),t.Hm(aR(c,q0("L",!0))),t.Hm(n),t=new A2(3,t),t=new zfe(r,t),Xce=t,Xce}function K2(e,n){var t,i,r,c,o,l,f,h;for(t=new RegExp(n,"g"),f=oe(ze,Se,2,0,6,1),i=0,h=e,c=null;;)if(l=t.exec(h),l==null||h==""){f[i]=h;break}else o=l.index,f[i]=(Yr(0,o,h.length),h.substr(0,o)),h=of(h,o+l[0].length,h.length),t.lastIndex=0,c==h&&(f[i]=(Yr(0,1,h.length),h.substr(0,1)),h=(Yn(1,h.length+1),h.substr(1))),c=h,++i;if(e.length>0){for(r=f.length;r>0&&f[r-1]=="";)--r;rb&&(b=f);for(h=k.Math.pow(4,n),b>h&&(h=b),y=(k.Math.log(h)-k.Math.log(1))/n,c=k.Math.exp(y),r=c,o=0;o0&&(p-=i[0]+e.c,i[0]+=e.c),i[2]>0&&(p-=i[2]+e.c),i[1]=k.Math.max(i[1],p),dR(e.a[1],t.c+n.b+i[0]-(i[1]-p)/2,i[1]);for(c=e.a,l=0,h=c.length;l0?(e.n.c.length-1)*e.i:0,i=new L(e.n);i.a1)for(i=St(r,0);i.b!=i.d.c;)for(t=u(jt(i),235),c=0,f=new L(t.e);f.a0&&(n[0]+=e.c,p-=n[0]),n[2]>0&&(p-=n[2]+e.c),n[1]=k.Math.max(n[1],p),bR(e.a[1],i.d+t.d+n[0]-(n[1]-p)/2,n[1]);else for(A=i.d+t.d,S=i.a-t.d-t.a,o=e.a,f=0,b=o.length;f=n.o&&t.f<=n.f||n.a*.5<=t.f&&n.a*1.5>=t.f){if(o=u(Le(n.n,n.n.c.length-1),208),o.e+o.d+t.g+r<=i&&(c=u(Le(n.n,n.n.c.length-1),208),c.f-e.f+t.f<=e.b||e.a.c.length==1))return ede(n,t),!0;if(n.s+t.g<=i&&n.t+n.d+t.f+r<=e.f+e.b)return Te(n.b,t),l=u(Le(n.n,n.n.c.length-1),208),Te(n.n,new PR(n.s,l.f+l.a+n.i,n.i)),Ide(u(Le(n.n,n.n.c.length-1),208),t),jKe(n,t),!0}return!1}function Hz(e,n,t,i){var r,c,o,l,f;if(f=Po(e.e.Ah(),n),r=u(e.g,122),Cc(),u(n,69).vk()){for(o=0;o0||$w(r.b.d,e.b.d+e.b.a)==0&&i.b<0||$w(r.b.d+r.b.a,e.b.d)==0&&i.b>0){l=0;break}}else l=k.Math.min(l,hqe(e,r,i));l=k.Math.min(l,SKe(e,c,l,i))}return l}function sge(e,n){var t,i,r,c,o,l,f;if(e.b<2)throw $(new Gn("The vector chain must contain at least a source and a target point."));for(r=(at(e.b!=0),u(e.a.a.c,8)),vC(n,r.a,r.b),f=new p4((!n.a&&(n.a=new mr(kl,n,5)),n.a)),o=St(e,1);o.a=0&&c!=t))throw $(new Gn($N));for(r=0,f=0;fne(Na(o.g,o.d[0]).a)?(at(f.b>0),f.a.Xb(f.c=--f.b),d2(f,o),r=!0):l.e&&l.e.gc()>0&&(c=(!l.e&&(l.e=new Ce),l.e).Kc(n),h=(!l.e&&(l.e=new Ce),l.e).Kc(t),(c||h)&&((!l.e&&(l.e=new Ce),l.e).Ec(o),++o.c));r||Hn(i.c,o)}function K_n(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;return p=e.a.i+e.a.g/2,y=e.a.i+e.a.g/2,A=n.i+n.g/2,I=n.j+n.f/2,l=new je(A,I),h=u(ye(n,(Xt(),Fy)),8),h.a=h.a+p,h.b=h.b+y,c=(l.b-h.b)/(l.a-h.a),i=l.b-c*l.a,O=t.i+t.g/2,R=t.j+t.f/2,f=new je(O,R),b=u(ye(t,Fy),8),b.a=b.a+p,b.b=b.b+y,o=(f.b-b.b)/(f.a-b.a),r=f.b-o*f.a,S=(i-r)/(o-c),h.a>>0,"0"+n.toString(16)),i="\\x"+of(t,t.length-2,t.length)):e>=Ec?(t=(n=e>>>0,"0"+n.toString(16)),i="\\v"+of(t,t.length-6,t.length)):i=""+String.fromCharCode(e&yr)}return i}function CKe(e,n){var t,i,r,c,o,l,f,h,b;for(c=new L(e.b);c.at){n.Ug();return}switch(u(T(e,(Ne(),Hie)),350).g){case 2:c=new E6;break;case 0:c=new Lp;break;default:c=new oM}if(i=c.mg(e,r),!c.ng())switch(u(T(e,kH),351).g){case 2:i=dqe(r,i);break;case 1:i=iGe(r,i)}VLn(e,r,i),n.Ug()}function oS(e,n){var t,i,r,c,o,l,f,h;n%=24,e.q.getHours()!=n&&(i=new k.Date(e.q.getTime()),i.setDate(i.getDate()+1),l=e.q.getTimezoneOffset()-i.getTimezoneOffset(),l>0&&(f=l/60|0,h=l%60,r=e.q.getDate(),t=e.q.getHours(),t+f>=24&&++r,c=new k.Date(e.q.getFullYear(),e.q.getMonth(),r,n+f,e.q.getMinutes()+h,e.q.getSeconds(),e.q.getMilliseconds()),e.q.setTime(c.getTime()))),o=e.q.getTime(),e.q.setTime(o+36e5),e.q.getHours()!=n&&e.q.setTime(o)}function nLn(e,n){var t,i,r,c;if(L4n(e.d,e.e),e.c.a.$b(),ne(re(T(n.j,(Ne(),lD))))!=0||ne(re(T(n.j,lD)))!=0)for(t=mv,ue(T(n.j,u1))!==ue((F1(),ob))&&he(n.j,(me(),rb),(Pn(),!0)),c=u(T(n.j,kx),15).a,r=0;rr&&++h,Te(o,(vn(l+h,n.c.length),u(n.c[l+h],15))),f+=(vn(l+h,n.c.length),u(n.c[l+h],15)).a-i,++t;t=I&&e.e[f.p]>A*e.b||V>=t*I)&&(Hn(y.c,l),l=new Ce,fc(o,c),c.a.$b(),h-=b,S=k.Math.max(S,h*e.b+O),h+=V,q=V,V=0,b=0,O=0);return new jc(S,y)}function qW(e){var n,t,i,r,c,o,l;if(!e.d){if(l=new hU,n=sA,c=n.a.yc(e,n),c==null){for(i=new ut(tu(e));i.e!=i.i.gc();)t=u(ft(i),29),er(l,qW(t));n.a.Ac(e)!=null,n.a.gc()==0}for(o=l.i,r=(!e.q&&(e.q=new pe(yf,e,11,10)),new ut(e.q));r.e!=r.i.gc();++o)u(ft(r),403);er(l,(!e.q&&(e.q=new pe(yf,e,11,10)),e.q)),_2(l),e.d=new N3((u(K(we((x0(),Rn).o),9),19),l.i),l.g),e.e=u(l.g,678),e.e==null&&(e.e=Qan),Ms(e).b&=-17}return e.d}function C8(e,n,t,i){var r,c,o,l,f,h;if(h=Po(e.e.Ah(),n),f=0,r=u(e.g,122),Cc(),u(n,69).vk()){for(o=0;o1||A==-1)if(p=u(O,72),y=u(b,72),p.dc())y.$b();else for(o=!!Oc(n),c=0,l=e.a?p.Jc():p.Gi();l.Ob();)h=u(l.Pb(),57),r=u(Pa(e,h),57),r?(o?(f=y.bd(r),f==-1?y.Ei(c,r):c!=f&&y.Si(c,r)):y.Ei(c,r),++c):e.b&&!o&&(y.Ei(c,h),++c);else O==null?b.Wb(null):(r=Pa(e,O),r==null?e.b&&!Oc(n)&&b.Wb(O):b.Wb(r))}function uLn(e,n){var t,i,r,c,o,l,f,h;for(t=new b6,r=new qn(Vn(rr(n).a.Jc(),new ee));ht(r);)if(i=u(tt(r),17),!cc(i)&&(l=i.c.i,b0e(l,EJ))){if(h=Pbe(e,l,EJ,jJ),h==-1)continue;t.b=k.Math.max(t.b,h),!t.a&&(t.a=new Ce),Te(t.a,l)}for(o=new qn(Vn(Ni(n).a.Jc(),new ee));ht(o);)if(c=u(tt(o),17),!cc(c)&&(f=c.d.i,b0e(f,jJ))){if(h=Pbe(e,f,jJ,EJ),h==-1)continue;t.d=k.Math.max(t.d,h),!t.c&&(t.c=new Ce),Te(t.c,f)}return t}function oLn(e,n,t,i){var r,c,o,l,f,h,b;if(t.d.i!=n.i){for(r=new Ba(e),Tf(r,(zn(),dr)),he(r,(me(),wi),t),he(r,(Ne(),Wi),(Br(),to)),Hn(i.c,r),o=new Qu,gu(o,r),xr(o,(De(),Kn)),l=new Qu,gu(l,r),xr(l,et),b=t.d,Gr(t,o),c=new Mw,$u(c,t),he(c,Wc,null),lc(c,l),Gr(c,b),h=new qr(t.b,0);h.b1e6)throw $(new FP("power of ten too big"));if(e<=oi)return _4(XO(my[1],n),n);for(i=XO(my[1],oi),r=i,t=Pu(e-oi),n=sc(e%oi);ao(t,oi)>0;)r=H3(r,i),t=lf(t,oi);for(r=H3(r,XO(my[1],n)),r=_4(r,oi),t=Pu(e-oi);ao(t,oi)>0;)r=_4(r,oi),t=lf(t,oi);return r=_4(r,n),r}function DKe(e){var n,t,i,r,c,o,l,f,h,b;for(f=new L(e.a);f.ah&&i>h)b=l,h=ne(n.p[l.p])+ne(n.d[l.p])+l.o.b+l.d.a;else{r=!1,t.$g()&&t.ah("bk node placement breaks on "+l+" which should have been after "+b);break}if(!r)break}return t.$g()&&t.ah(n+" is feasible: "+r),r}function age(e,n,t,i){var r,c,o,l,f,h,b,p,y;if(c=new Ba(e),Tf(c,(zn(),wo)),he(c,(Ne(),Wi),(Br(),to)),r=0,n){for(o=new Qu,he(o,(me(),wi),n),he(c,wi,n.i),xr(o,(De(),Kn)),gu(o,c),y=dh(n.e),h=y,b=0,p=h.length;b0){if(r<0&&b.a&&(r=f,c=h[0],i=0),r>=0){if(l=b.b,f==r&&(l-=i++,l==0))return 0;if(!_Ve(n,h,b,l,o)){f=r-1,h[0]=c;continue}}else if(r=-1,!_Ve(n,h,b,0,o))return 0}else{if(r=-1,ic(b.c,0)==32){if(p=h[0],ARe(n,h),h[0]>p)continue}else if(U5n(n,b.c,h[0])){h[0]+=b.c.length;continue}return 0}return eRn(o,t)?h[0]:0}function hLn(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(b=new wR(new eje(t)),l=oe(ts,pa,30,e.f.e.c.length,16,1),$fe(l,l.length),t[n.a]=0,h=new L(e.f.e);h.a=l.a?c.b>=l.b?(i.a=l.a+(c.a-l.a)/2+r,i.b=l.b+(c.b-l.b)/2-r-e.e.b):(i.a=l.a+(c.a-l.a)/2+r,i.b=c.b+(l.b-c.b)/2+r):c.b>=l.b?(i.a=c.a+(l.a-c.a)/2+r,i.b=l.b+(c.b-l.b)/2+r):(i.a=c.a+(l.a-c.a)/2+r,i.b=c.b+(l.b-c.b)/2-r-e.e.b))}function lS(e){var n,t,i,r,c,o,l,f;if(!e.f){if(f=new nT,l=new nT,n=sA,o=n.a.yc(e,n),o==null){for(c=new ut(tu(e));c.e!=c.i.gc();)r=u(ft(c),29),er(f,lS(r));n.a.Ac(e)!=null,n.a.gc()==0}for(i=(!e.s&&(e.s=new pe(ns,e,21,17)),new ut(e.s));i.e!=i.i.gc();)t=u(ft(i),179),X(t,103)&&Et(l,u(t,19));_2(l),e.r=new uDe(e,(u(K(we((x0(),Rn).o),6),19),l.i),l.g),er(f,e.r),_2(f),e.f=new N3((u(K(we(Rn.o),5),19),f.i),f.g),Ms(e).b&=-3}return e.f}function Gz(){Gz=Y,R8e=z(B(Wl,1),kh,30,15,[48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70]),Man=new RegExp(`[ -\r\f]+`);try{cA=z(B(QBn,1),On,2076,0,[new UT(($se(),QB("yyyy-MM-dd'T'HH:mm:ss'.'SSSZ",MC((RP(),RP(),US))))),new UT(QB("yyyy-MM-dd'T'HH:mm:ss'.'SSS",MC(US))),new UT(QB("yyyy-MM-dd'T'HH:mm:ss",MC(US))),new UT(QB("yyyy-MM-dd'T'HH:mm",MC(US))),new UT(QB("yyyy-MM-dd",MC(US)))])}catch(e){if(e=sr(e),!X(e,80))throw $(e)}}function dLn(e){var n,t,i,r,c,o,l;for(t=null,l=null,i=u(T(e.b,(Ne(),Die)),348),i==(DE(),pD)&&(t=new Ce,l=new Ce),o=new L(e.d);o.at);return c}function _Ke(e,n){var t,i,r,c;if(r=Is(e.d,1)!=0,i=xz(e,n),i==0&&Re($e(T(n.j,(me(),rb)))))return 0;!Re($e(T(n.j,(me(),rb))))&&!Re($e(T(n.j,_v)))||ue(T(n.j,(Ne(),u1)))===ue((F1(),ob))?n.c.kg(n.e,r):r=Re($e(T(n.j,rb))),WO(e,n,r,!0),Re($e(T(n.j,_v)))&&he(n.j,_v,(Pn(),!1)),Re($e(T(n.j,rb)))&&(he(n.j,rb,(Pn(),!1)),he(n.j,_v,!0)),t=xz(e,n);do{if(Qhe(e),t==0)return 0;r=!r,c=t,WO(e,n,r,!1),t=xz(e,n)}while(c>t);return c}function gLn(e,n,t){var i,r,c,o,l;if(i=u(T(e,(Ne(),Cie)),22),t.a>n.a&&(i.Gc((rg(),Gx))?e.c.a+=(t.a-n.a)/2:i.Gc(qx)&&(e.c.a+=t.a-n.a)),t.b>n.b&&(i.Gc((rg(),Xx))?e.c.b+=(t.b-n.b)/2:i.Gc(Ux)&&(e.c.b+=t.b-n.b)),u(T(e,(me(),po)),22).Gc((Dc(),Kl))&&(t.a>n.a||t.b>n.b))for(l=new L(e.a);l.an.a&&(i.Gc((rg(),Gx))?e.c.a+=(t.a-n.a)/2:i.Gc(qx)&&(e.c.a+=t.a-n.a)),t.b>n.b&&(i.Gc((rg(),Xx))?e.c.b+=(t.b-n.b)/2:i.Gc(Ux)&&(e.c.b+=t.b-n.b)),u(T(e,(me(),po)),22).Gc((Dc(),Kl))&&(t.a>n.a||t.b>n.b))for(o=new L(e.a);o.a=0&&p<=1&&y>=0&&y<=1?gi(new je(e.a,e.b),A1(new je(n.a,n.b),p)):null}function fS(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(c=0,o=e.t,r=0,i=0,f=0,y=0,p=0,t&&(e.n.c.length=0,Te(e.n,new PR(e.s,e.t,e.i))),l=0,b=new L(e.b);b.a0?e.i:0)>n&&f>0&&(c=0,o+=f+e.i,r=k.Math.max(r,y),i+=f+e.i,f=0,y=0,t&&(++p,Te(e.n,new PR(e.s,o,e.i))),l=0),y+=h.g+(l>0?e.i:0),f=k.Math.max(f,h.f),t&&Ide(u(Le(e.n,p),208),h),c+=h.g+(l>0?e.i:0),++l;return r=k.Math.max(r,y),i+=f,t&&(e.r=r,e.d=i,Pde(e.j)),new Lf(e.s,e.t,r,i)}function qz(e){var n,t,i;return t=ue(ye(e,(Ne(),_y)))===ue((KO(),eie))||ue(ye(e,_y))===ue(Vte)||ue(ye(e,_y))===ue(Yte)||ue(ye(e,_y))===ue(Wte)||ue(ye(e,_y))===ue(nie)||ue(ye(e,_y))===ue(nD),i=ue(ye(e,bH))===ue((YO(),qie))||ue(ye(e,bH))===ue(Xie)||ue(ye(e,aD))===ue((G0(),D7))||ue(ye(e,aD))===ue((G0(),Sx)),n=ue(ye(e,u1))!==ue((F1(),ob))||Re($e(ye(e,A7)))||ue(ye(e,dx))!==ue((X4(),ZS))||ne(re(ye(e,lD)))!=0||ne(re(ye(e,Aie)))!=0,t||i||n}function fv(e){var n,t,i,r,c,o,l,f;if(!e.a){if(e.o=null,f=new RSe(e),n=new Mf,t=sA,l=t.a.yc(e,t),l==null){for(o=new ut(tu(e));o.e!=o.i.gc();)c=u(ft(o),29),er(f,fv(c));t.a.Ac(e)!=null,t.a.gc()==0}for(r=(!e.s&&(e.s=new pe(ns,e,21,17)),new ut(e.s));r.e!=r.i.gc();)i=u(ft(r),179),X(i,335)&&Et(n,u(i,38));_2(n),e.k=new cDe(e,(u(K(we((x0(),Rn).o),7),19),n.i),n.g),er(f,e.k),_2(f),e.a=new N3((u(K(we(Rn.o),4),19),f.i),f.g),Ms(e).b&=-2}return e.a}function mLn(e){var n,t,i,r,c,o,l,f,h,b,p,y;if(l=e.d,p=u(T(e,(me(),Dy)),16),n=u(T(e,xy),16),!(!p&&!n)){if(c=ne(re($2(e,(Ne(),Bie)))),o=ne(re($2(e,w4e))),y=0,p){for(h=0,r=p.Jc();r.Ob();)i=u(r.Pb(),9),h=k.Math.max(h,i.o.b),y+=i.o.a;y+=c*(p.gc()-1),l.d+=h+o}if(t=0,n){for(h=0,r=n.Jc();r.Ob();)i=u(r.Pb(),9),h=k.Math.max(h,i.o.b),t+=i.o.a;t+=c*(n.gc()-1),l.a+=h+o}f=k.Math.max(y,t),f>e.o.a&&(b=(f-e.o.a)/2,l.b=k.Math.max(l.b,b),l.c=k.Math.max(l.c,b))}}function bge(e,n,t,i){var r,c,o,l,f,h,b;if(b=Po(e.e.Ah(),n),r=0,c=u(e.g,122),f=null,Cc(),u(n,69).vk()){for(l=0;ll?1:-1:M1e(e.a,n.a,c),r==-1)p=-f,b=o==f?aY(n.a,l,e.a,c):dY(n.a,l,e.a,c);else if(p=o,o==f){if(r==0)return mh(),KS;b=aY(e.a,c,n.a,l)}else b=dY(e.a,c,n.a,l);return h=new zb(p,b.length,b),bE(h),h}function kLn(e,n){var t,i,r,c;if(c=yKe(n),!n.c&&(n.c=new pe($s,n,9,9)),Zi(new wn(null,(!n.c&&(n.c=new pe($s,n,9,9)),new pn(n.c,16))),new rje(c)),r=u(T(c,(me(),po)),22),p$n(n,r),r.Gc((Dc(),Kl)))for(i=new ut((!n.c&&(n.c=new pe($s,n,9,9)),n.c));i.e!=i.i.gc();)t=u(ft(i),125),J$n(e,n,c,t);return u(ye(n,(Ne(),jg)),182).gc()!=0&&oXe(n,c),Re($e(T(c,a4e)))&&r.Ec(ZJ),bi(c,hD)&&Qxe(new tde(ne(re(T(c,hD)))),c),ue(ye(n,wm))===ue((B1(),Yd))?fBn(e,n,c):K$n(e,n,c),c}function bo(e,n){var t,i,r,c,o,l,f;if(e==null)return null;if(c=e.length,c==0)return"";for(f=oe(Wl,kh,30,c,15,1),Yr(0,c,e.length),Yr(0,c,f.length),rIe(e,0,c,f,0),t=null,l=n,r=0,o=0;r0?of(t.a,0,c-1):""):(Yr(0,c-1,e.length),e.substr(0,c-1)):t?t.a:e}function jLn(e,n,t){var i,r,c;if(bi(n,(Ne(),yu))&&(ue(T(n,yu))===ue((Xs(),V1))||ue(T(n,yu))===ue(yg))||bi(t,yu)&&(ue(T(t,yu))===ue((Xs(),V1))||ue(T(t,yu))===ue(yg)))return 0;if(i=_r(n),r=fIn(e,n,t),r!=0)return r;if(bi(n,(me(),Ci))&&bi(t,Ci)){if(c=oo(qw(n,t,i,u(T(i,cb),15).a),qw(t,n,i,u(T(i,cb),15).a)),ue(T(i,bx))===ue((_0(),iD))&&ue(T(n,gx))!==ue(T(t,gx))&&(c=0),c<0)return ZO(e,n,t),c;if(c>0)return ZO(e,t,n),c}return $Cn(e,n,t)}function LKe(e,n){var t,i,r,c,o,l,f,h,b,p,y;for(i=new qn(Vn(H0(n).a.Jc(),new ee));ht(i);)t=u(tt(i),85),X(K((!t.b&&(t.b=new Nn(vt,t,4,7)),t.b),0),193)||(f=iu(u(K((!t.c&&(t.c=new Nn(vt,t,5,8)),t.c),0),84)),ZE(t)||(o=n.i+n.g/2,l=n.j+n.f/2,b=f.i+f.g/2,p=f.j+f.f/2,y=new Kr,y.a=b-o,y.b=p-l,c=new je(y.a,y.b),p8(c,n.g,n.f),y.a-=c.a,y.b-=c.b,o=b-y.a,l=p-y.b,h=new je(y.a,y.b),p8(h,f.g,f.f),y.a-=h.a,y.b-=h.b,b=o+y.a,p=l+y.b,r=Pz(t),V3(r,o),Y3(r,l),X3(r,b),K3(r,p),LKe(e,f)))}function V2(e,n){var t,i,r,c,o;if(o=u(n,137),ov(e),ov(o),o.b!=null){if(e.c=!0,e.b==null){e.b=oe($t,ni,30,o.b.length,15,1),Wu(o.b,0,e.b,0,o.b.length);return}for(c=oe($t,ni,30,e.b.length+o.b.length,15,1),t=0,i=0,r=0;t=e.b.length?(c[r++]=o.b[i++],c[r++]=o.b[i++]):i>=o.b.length?(c[r++]=e.b[t++],c[r++]=e.b[t++]):o.b[i]0?e.i:0)),++n;for(K1e(e.n,f),e.d=t,e.r=i,e.g=0,e.f=0,e.e=0,e.o=Ki,e.p=Ki,c=new L(e.b);c.a0&&(r=(!e.n&&(e.n=new pe(ju,e,1,7)),u(K(e.n,0),157)).a,!r||Kt(Kt((n.a+=' "',n),r),'"'))),t=(!e.b&&(e.b=new Nn(vt,e,4,7)),!(e.b.i<=1&&(!e.c&&(e.c=new Nn(vt,e,5,8)),e.c.i<=1))),t?n.a+=" [":n.a+=" ",Kt(n,nle(new NX,new ut(e.b))),t&&(n.a+="]"),n.a+=eee,t&&(n.a+="["),Kt(n,nle(new NX,new ut(e.c))),t&&(n.a+="]"),n.a)}function SLn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn;for(be=e.c,fe=n.c,t=wu(be.a,e,0),i=wu(fe.a,n,0),V=u(Rw(e,(Nc(),ys)).Jc().Pb(),12),tn=u(Rw(e,Do).Jc().Pb(),12),te=u(Rw(n,ys).Jc().Pb(),12),Tn=u(Rw(n,Do).Jc().Pb(),12),R=dh(V.e),Ie=dh(tn.g),q=dh(te.e),cn=dh(Tn.g),z0(e,i,fe),o=q,b=0,A=o.length;b0&&f[i]&&(A=L3(e.b,f[i],r)),O=k.Math.max(O,r.c.c.b+A);for(c=new L(b.e);c.ab?new Gb((ha(),Am),t,n,h-b):h>0&&b>0&&(new Gb((ha(),Am),n,t,0),new Gb(Am,t,n,0))),o)}function TLn(e,n,t){var i,r,c;for(e.a=new Ce,c=St(n.b,0);c.b!=c.d.c;){for(r=u(jt(c),40);u(T(r,(Mu(),Nh)),15).a>e.a.c.length-1;)Te(e.a,new jc(mv,Fpe));i=u(T(r,Nh),15).a,t==(vr(),Zc)||t==ru?(r.e.ane(re(u(Le(e.a,i),49).b))&&FT(u(Le(e.a,i),49),r.e.a+r.f.a)):(r.e.bne(re(u(Le(e.a,i),49).b))&&FT(u(Le(e.a,i),49),r.e.b+r.f.b))}}function RKe(e,n,t,i){var r,c,o,l,f,h,b;if(c=GB(i),l=Re($e(T(i,(Ne(),c4e)))),(l||Re($e(T(e,dH))))&&!D3(u(T(e,Wi),102)))r=q4(c),f=ege(e,t,t==(Nc(),Do)?r:CO(r));else switch(f=new Qu,gu(f,e),n?(b=f.n,b.a=n.a-e.n.a,b.b=n.b-e.n.b,BGe(b,0,0,e.o.a,e.o.b),xr(f,cKe(f,c))):(r=q4(c),xr(f,t==(Nc(),Do)?r:CO(r))),o=u(T(i,(me(),po)),22),h=f.j,c.g){case 2:case 1:(h==(De(),Xn)||h==bt)&&o.Ec((Dc(),Nv));break;case 4:case 3:(h==(De(),et)||h==Kn)&&o.Ec((Dc(),Nv))}return f}function BKe(e,n){var t,i,r,c,o,l;for(o=new D2(new on(e.f.b).a);o.b;){if(c=Q3(o),r=u(c.jd(),591),n==1){if(r.yf()!=(vr(),Vl)&&r.yf()!=Za)continue}else if(r.yf()!=(vr(),Zc)&&r.yf()!=ru)continue;switch(i=u(u(c.kd(),49).b,82),l=u(u(c.kd(),49).a,194),t=l.c,r.yf().g){case 2:i.g.c=e.e.a,i.g.b=k.Math.max(1,i.g.b+t);break;case 1:i.g.c=i.g.c+t,i.g.b=k.Math.max(1,i.g.b-t);break;case 4:i.g.d=e.e.b,i.g.a=k.Math.max(1,i.g.a+t);break;case 3:i.g.d=i.g.d+t,i.g.a=k.Math.max(1,i.g.a-t)}}}function CLn(e,n){var t,i,r,c,o,l,f,h,b,p;for(n.Tg("Simple node placement",1),p=u(T(e,(me(),Lv)),316),l=0,c=new L(e.b);c.a1)throw $(new Gn(JN));f||(c=Xh(n,i.Jc().Pb()),o.Ec(c))}return h1e(e,I0e(e,n,t),o)}function Xz(e,n,t){var i,r,c,o,l,f,h,b;if(J1(e.e,n))f=(Cc(),u(n,69).vk()?new rR(n,e):new pC(n,e)),Mz(f.c,f.b),Vj(f,u(t,18));else{for(b=Po(e.e.Ah(),n),i=u(e.g,122),o=0;o"}f!=null&&(n.a+=""+f)}else e.e?(l=e.e.zb,l!=null&&(n.a+=""+l)):(n.a+="?",e.b?(n.a+=" super ",YW(e.b,n)):e.f&&(n.a+=" extends ",YW(e.f,n)))}function PLn(e){e.b=null,e.a=null,e.o=null,e.q=null,e.v=null,e.w=null,e.B=null,e.p=null,e.Q=null,e.R=null,e.S=null,e.T=null,e.U=null,e.V=null,e.W=null,e.bb=null,e.eb=null,e.ab=null,e.H=null,e.db=null,e.c=null,e.d=null,e.f=null,e.n=null,e.r=null,e.s=null,e.u=null,e.G=null,e.J=null,e.e=null,e.j=null,e.i=null,e.g=null,e.k=null,e.t=null,e.F=null,e.I=null,e.L=null,e.M=null,e.O=null,e.P=null,e.$=null,e.N=null,e.Z=null,e.cb=null,e.K=null,e.D=null,e.A=null,e.C=null,e._=null,e.fb=null,e.X=null,e.Y=null,e.gb=!1,e.hb=!1}function $Ln(e){var n,t,i,r;if(i=lZ((!e.c&&(e.c=qC(Pu(e.f))),e.c),0),e.e==0||e.a==0&&e.f!=-1&&e.e<0)return i;if(n=Whe(e)<0?1:0,t=e.e,r=(i.length+1+k.Math.abs(sc(e.e)),new o4),n==1&&(r.a+="-"),e.e>0)if(t-=i.length-n,t>=0){for(r.a+="0.";t>pg.length;t-=pg.length)ADe(r,pg);WOe(r,pg,sc(t)),Kt(r,(Yn(n,i.length+1),i.substr(n)))}else t=n-t,Kt(r,of(i,n,sc(t))),r.a+=".",Kt(r,qfe(i,sc(t)));else{for(Kt(r,(Yn(n,i.length+1),i.substr(n)));t<-pg.length;t+=pg.length)ADe(r,pg);WOe(r,pg,sc(-t))}return r.a}function QW(e){var n,t,i,r,c,o,l,f,h;return!(e.k!=(zn(),Qi)||e.j.c.length<=1||(c=u(T(e,(Ne(),Wi)),102),c==(Br(),to))||(r=(B2(),(e.q?e.q:(jn(),jn(),i1))._b(gp)?i=u(T(e,gp),203):i=u(T(_r(e),vx),203),i),r==xH)||!(r==Fv||r==zv)&&(o=ne(re($2(e,yx))),n=u(T(e,bD),140),!n&&(n=new $le(o,o,o,o)),h=mu(e,(De(),Kn)),f=n.d+n.a+(h.gc()-1)*o,f>e.o.b||(t=mu(e,et),l=n.d+n.a+(t.gc()-1)*o,l>e.o.b)))}function RLn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;n.Tg("Orthogonal edge routing",1),h=ne(re(T(e,(Ne(),Sm)))),t=ne(re(T(e,jm))),i=ne(re(T(e,ub))),y=new jV(0,t),I=0,o=new qr(e.b,0),l=null,b=null,f=null,p=null;do b=o.b0?(S=(A-1)*t,l&&(S+=i),b&&(S+=i),S0;for(l=u(T(e.c.i,mm),15).a,c=u(gs(si(n.Mc(),new xje(l)),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),o=new Si,b=new ar,Vt(o,e.c.i),hr(b,e.c.i);o.b!=0;){if(t=u(o.b==0?null:(at(o.b!=0),$l(o,o.a.a)),9),c.Gc(t))return!0;for(r=new qn(Vn(Ni(t).a.Jc(),new ee));ht(r);)i=u(tt(r),17),f=i.d.i,b.a._b(f)||(b.a.yc(f,b),Xi(o,f,o.c.b,o.c))}return!1}function GKe(e,n,t){var i,r,c,o,l,f,h,b,p;for(p=new Ce,b=new Tae(0,t),c=0,vB(b,new tQ(0,0,b,t)),r=0,h=new ut(e);h.e!=h.i.gc();)f=u(ft(h),26),i=u(Le(b.a,b.a.c.length-1),173),l=r+f.g+(u(Le(b.a,0),173).b.c.length==0?0:t),(l>n||Re($e(ye(f,(Ja(),AD)))))&&(r=0,c+=b.b+t,Hn(p.c,b),b=new Tae(c,t),i=new tQ(0,b.f,b,t),vB(b,i),r=0),i.b.c.length==0||!Re($e(ye(Bi(f),(Ja(),Ure))))&&(f.f>=i.o&&f.f<=i.f||i.a*.5<=f.f&&i.a*1.5>=f.f)?ede(i,f):(o=new tQ(i.s+i.r+t,b.f,b,t),vB(b,o),ede(o,f)),r=f.i+f.g;return Hn(p.c,b),p}function aS(e){var n,t,i,r;if(!(e.b==null||e.b.length<=2)&&!e.a){for(n=0,r=0;r=e.b[r+1])r+=2;else if(t0)for(i=new bs(u(pi(e.a,c),22)),jn(),Cr(i,new noe(n)),r=new qr(c.b,0);r.b0&&i>=-6?i>=0?yC(c,t-sc(e.e),"."):(qY(c,n-1,n-1,"0."),yC(c,n+1,gh(pg,0,-sc(i)-1))):(t-n>=1&&(yC(c,n,"."),++t),yC(c,t,"E"),i>0&&yC(c,++t,"+"),yC(c,++t,""+rE(Pu(i)))),e.g=c.a,e.g))}function KLn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie;i=ne(re(T(n,(Ne(),s4e)))),be=u(T(n,kx),15).a,y=4,r=3,fe=20/be,S=!1,f=0,o=oi;do{for(c=f!=1,p=f!=0,Ie=0,I=e.a,q=0,te=I.length;qbe)?(f=2,o=oi):f==0?(f=1,o=Ie):(f=0,o=Ie)):(S=Ie>=o||o-Ie=Ec?Bc(t,V1e(i)):D9(t,i&yr),o=new FV(10,null,0),Cvn(e.a,o,l-1)):(t=(o.Km().length+c,new jj),Bc(t,o.Km())),n.e==0?(i=n.Im(),i>=Ec?Bc(t,V1e(i)):D9(t,i&yr)):Bc(t,n.Km()),u(o,517).b=t.a}}function VLn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I;if(!t.dc()){for(l=0,y=0,i=t.Jc(),A=u(i.Pb(),15).a;l0?1:Lb(isNaN(i),isNaN(0)))>=0^(Bf(xh),(k.Math.abs(l)<=xh||l==0||isNaN(l)&&isNaN(0)?0:l<0?-1:l>0?1:Lb(isNaN(l),isNaN(0)))>=0)?k.Math.max(l,i):(Bf(xh),(k.Math.abs(i)<=xh||i==0||isNaN(i)&&isNaN(0)?0:i<0?-1:i>0?1:Lb(isNaN(i),isNaN(0)))>0?k.Math.sqrt(l*l+i*i):-k.Math.sqrt(l*l+i*i))}function ZLn(e){var n,t,i,r;r=e.o,h2(),e.A.dc()||di(e.A,Qme)?n=r.b:(e.D?n=k.Math.max(r.b,YE(e.f)):n=YE(e.f),e.A.Gc((Vs(),GD))&&!e.B.Gc((_s(),iA))&&(n=k.Math.max(n,YE(u(zc(e.p,(De(),et)),253))),n=k.Math.max(n,YE(u(zc(e.p,Kn),253)))),t=nze(e),t&&(n=k.Math.max(n,t.b)),e.A.Gc(qD)&&(e.q==(Br(),f1)||e.q==to)&&(n=k.Math.max(n,tR(u(zc(e.b,(De(),et)),127))),n=k.Math.max(n,tR(u(zc(e.b,Kn),127))))),Re($e(e.e.Rf().mf((Xt(),Om))))?r.b=k.Math.max(r.b,n):r.b=n,i=e.f.i,i.d=0,i.a=n,GW(e.f)}function ePn(e,n,t,i,r,c,o,l){var f,h,b,p;switch(f=$f(z(B(qBn,1),On,238,0,[n,t,i,r])),p=null,e.b.g){case 1:p=$f(z(B(M6e,1),On,523,0,[new Lk,new IM,new _6]));break;case 0:p=$f(z(B(M6e,1),On,523,0,[new _6,new IM,new Lk]));break;case 2:p=$f(z(B(M6e,1),On,523,0,[new IM,new Lk,new _6]))}for(b=new L(p);b.a1&&(f=h.Gg(f,e.a,l));return f.c.length==1?u(Le(f,f.c.length-1),238):f.c.length==2?zLn((vn(0,f.c.length),u(f.c[0],238)),(vn(1,f.c.length),u(f.c[1],238)),o,c):null}function nPn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A;r=new i9(e),c=new Yqe,i=(VC(c.n),VC(c.p),Hu(c.c),VC(c.f),VC(c.o),Hu(c.q),Hu(c.d),Hu(c.g),Hu(c.k),Hu(c.e),Hu(c.i),Hu(c.j),Hu(c.r),Hu(c.b),y=yqe(c,r,null),kUe(c,r),y),n&&(f=new i9(n),o=vLn(f),M0e(i,z(B(u9e,1),On,524,0,[o]))),p=!1,b=!1,t&&(f=new i9(t),GF in f.a&&(p=O1(f,GF).oe().a),aZe in f.a&&(b=O1(f,aZe).oe().a)),h=wAe(aBe(new i4,p),b),sTn(new RM,i,h),GF in r.a&&Rf(r,GF,null),(p||b)&&(l=new r4,bKe(h,l,p,b),Rf(r,GF,l)),S=new ySe(c),zze(new AK(i),S),A=new kSe(c),zze(new AK(i),A)}function tPn(e,n,t){var i,r,c,o,l,f,h;for(t.Tg("Find roots",1),e.a.c.length=0,r=St(n.b,0);r.b!=r.d.c;)i=u(jt(r),40),i.b.b==0&&(he(i,(Ti(),fb),(Pn(),!0)),Te(e.a,i));switch(e.a.c.length){case 0:c=new nQ(0,n,"DUMMY_ROOT"),he(c,(Ti(),fb),(Pn(),!0)),he(c,bre,!0),Vt(n.b,c);break;case 1:break;default:for(o=new nQ(0,n,DF),f=new L(e.a);f.a=k.Math.abs(i.b)?(i.b=0,c.d+c.a>o.d&&c.do.c&&c.c0){if(n=new Nse(e.i,e.g),t=e.i,c=t<100?null:new m0(t),e.Rj())for(i=0;i0){for(l=e.g,h=e.i,vE(e),c=h<100?null:new m0(h),i=0;i>13|(e.m&15)<<9,r=e.m>>4&8191,c=e.m>>17|(e.h&255)<<5,o=(e.h&1048320)>>8,l=n.l&8191,f=n.l>>13|(n.m&15)<<9,h=n.m>>4&8191,b=n.m>>17|(n.h&255)<<5,p=(n.h&1048320)>>8,cn=t*l,tn=i*l,Tn=r*l,Dn=c*l,ot=o*l,f!=0&&(tn+=t*f,Tn+=i*f,Dn+=r*f,ot+=c*f),h!=0&&(Tn+=t*h,Dn+=i*h,ot+=r*h),b!=0&&(Dn+=t*b,ot+=i*b),p!=0&&(ot+=t*p),S=cn&Ls,A=(tn&511)<<13,y=S+A,I=cn>>22,R=tn>>9,q=(Tn&262143)<<4,V=(Dn&31)<<17,O=I+R+q+V,be=Tn>>18,fe=Dn>>5,Ie=(ot&4095)<<8,te=be+fe+Ie,O+=y>>22,y&=Ls,te+=O>>22,O&=Ls,te&=G1,_o(y,O,te)}function KKe(e){var n,t,i,r,c,o,l;if(l=u(Le(e.j,0),12),l.g.c.length!=0&&l.e.c.length!=0)throw $(new Uc("Interactive layout does not support NORTH/SOUTH ports with incoming _and_ outgoing edges."));if(l.g.c.length!=0){for(c=Ki,t=new L(l.g);t.a0&&UGe(e,l,p);for(r=new L(p);r.a4)if(e.dk(n)){if(e.$k()){if(r=u(n,52),i=r.Bh(),f=i==e.e&&(e.kl()?r.vh(r.Ch(),e.gl())==e.hl():-1-r.Ch()==e.Jj()),e.ll()&&!f&&!i&&r.Gh()){for(c=0;ce.d[o.p]&&(t+=Hae(e.b,c)*u(f.b,15).a,C0(e.a,ve(c)));for(;!kj(e.a);)Ehe(e.b,u(A4(e.a),15).a)}return t}function oPn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;for(n.Tg(FQe,1),S=new Ce,b=k.Math.max(e.a.c.length,u(T(e,(me(),cb)),15).a),t=b*u(T(e,cD),15).a,l=ue(T(e,(Ne(),Iy)))===ue((_0(),dm)),O=new L(e.a);O.a0&&(h=e.n.a/c);break;case 2:case 4:r=e.i.o.b,r>0&&(h=e.n.b/r)}he(e,(me(),hp),h)}if(f=e.o,o=e.a,i)o.a=i.a,o.b=i.b,e.d=!0;else if(n!=nh&&n!=bb&&l!=ku)switch(l.g){case 1:o.a=f.a/2;break;case 2:o.a=f.a,o.b=f.b/2;break;case 3:o.a=f.a/2,o.b=f.b;break;case 4:o.b=f.b/2}else o.a=f.a/2,o.b=f.b/2}function hS(e){var n,t,i,r,c,o,l,f,h,b;if(e.Nj())if(b=e.Cj(),f=e.Oj(),b>0)if(n=new t1e(e.nj()),t=b,c=t<100?null:new m0(t),xC(e,t,n.g),r=t==1?e.Gj(4,K(n,0),null,0,f):e.Gj(6,n,null,-1,f),e.Kj()){for(i=new ut(n);i.e!=i.i.gc();)c=e.Mj(ft(i),c);c?(c.lj(r),c.mj()):e.Hj(r)}else c?(c.lj(r),c.mj()):e.Hj(r);else xC(e,e.Cj(),e.Dj()),e.Hj(e.Gj(6,(jn(),Sc),null,-1,f));else if(e.Kj())if(b=e.Cj(),b>0){for(l=e.Dj(),h=b,xC(e,b,l),c=h<100?null:new m0(h),i=0;i1&&us(o)*Gs(o)/2>l[0]){for(c=0;cl[c];)++c;A=new T0(O,0,c+1),p=new dB(A),b=us(o)/Gs(o),f=uZ(p,n,new t4,t,i,r,b),gi(la(p.e),f),E4(v8(y,p),B8),S=new T0(O,c+1,O.c.length),zde(y,S),O.c.length=0,h=0,ODe(l,l.length,0)}else I=y.b.c.length==0?null:Le(y.b,0),I!=null&&PY(y,0),h>0&&(l[h]=l[h-1]),l[h]+=us(o)*Gs(o),++h,Hn(O.c,o);return O}function wPn(e,n){var t,i,r,c;t=n.b,c=new bs(t.j),r=0,i=t.j,i.c.length=0,jw(u(Yb(e.b,(De(),Xn),(_w(),lp)),16),t),r=_O(c,r,new k6,i),jw(u(Yb(e.b,Xn,ib),16),t),r=_O(c,r,new ld,i),jw(u(Yb(e.b,Xn,sp),16),t),jw(u(Yb(e.b,et,lp),16),t),jw(u(Yb(e.b,et,ib),16),t),r=_O(c,r,new fd,i),jw(u(Yb(e.b,et,sp),16),t),jw(u(Yb(e.b,bt,lp),16),t),r=_O(c,r,new Np,i),jw(u(Yb(e.b,bt,ib),16),t),r=_O(c,r,new Sb,i),jw(u(Yb(e.b,bt,sp),16),t),jw(u(Yb(e.b,Kn,lp),16),t),r=_O(c,r,new sd,i),jw(u(Yb(e.b,Kn,ib),16),t),jw(u(Yb(e.b,Kn,sp),16),t)}function pPn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O;for(n.Tg("Layer size calculation",1),b=Ki,h=Dr,r=!1,l=new L(e.b);l.a.5?R-=o*2*(A-.5):A<.5&&(R+=c*2*(.5-A)),r=l.d.b,RI.a-O-b&&(R=I.a-O-b),l.n.a=n+R}}function vPn(e){var n,t,i,r,c;if(i=u(T(e,(Ne(),yu)),165),i==(Xs(),V1)){for(t=new qn(Vn(rr(e).a.Jc(),new ee));ht(t);)if(n=u(tt(t),17),!qPe(n))throw $(new wd(iee+LO(e)+"' has its layer constraint set to FIRST_SEPARATE, but has at least one incoming edge. FIRST_SEPARATE nodes must not have incoming edges."))}else if(i==yg){for(c=new qn(Vn(Ni(e).a.Jc(),new ee));ht(c);)if(r=u(tt(c),17),!qPe(r))throw $(new wd(iee+LO(e)+"' has its layer constraint set to LAST_SEPARATE, but has at least one outgoing edge. LAST_SEPARATE nodes must not have outgoing edges."))}}function rN(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;if(e.e&&e.c.c>19!=0&&(n=e8(n),f=!f),o=rNn(n),c=!1,r=!1,i=!1,e.h==gN&&e.m==0&&e.l==0)if(r=!0,c=!0,o==-1)e=gCe((G9(),jme)),i=!0,f=!f;else return l=lbe(e,o),f&&ZY(l),t&&(Z0=_o(0,0,0)),l;else e.h>>19!=0&&(c=!0,e=e8(e),i=!0,f=!f);return o!=-1?bkn(e,o,f,c,t):Kde(e,n)<0?(t&&(c?Z0=e8(e):Z0=_o(e.l,e.m,e.h)),_o(0,0,0)):WIn(i?e:_o(e.l,e.m,e.h),n,f,c,r,t)}function nZ(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;if(o=e.e,f=n.e,o==0)return n;if(f==0)return e;if(c=e.d,l=n.d,c+l==2)return t=Rr(e.a[0],Ic),i=Rr(n.a[0],Ic),o==f?(b=mc(t,i),A=Rt(b),S=Rt(Bb(b,32)),S==0?new D1(o,A):new zb(o,2,z(B($t,1),ni,30,15,[A,S]))):(mh(),C$(o<0?lf(i,t):lf(t,i),0)?B0(o<0?lf(i,t):lf(t,i)):sE(B0(Td(o<0?lf(i,t):lf(t,i)))));if(o==f)y=o,p=c>=l?dY(e.a,c,n.a,l):dY(n.a,l,e.a,c);else{if(r=c!=l?c>l?1:-1:M1e(e.a,n.a,c),r==0)return mh(),KS;r==1?(y=o,p=aY(e.a,c,n.a,l)):(y=f,p=aY(n.a,l,e.a,c))}return h=new zb(y,p.length,p),bE(h),h}function kPn(e,n){var t,i,r,c,o,l,f;if(!(e.g>n.f||n.g>e.f)){for(t=0,i=0,o=e.w.a.ec().Jc();o.Ob();)r=u(o.Pb(),12),dQ(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])).b,n.g,n.f)&&++t;for(l=e.r.a.ec().Jc();l.Ob();)r=u(l.Pb(),12),dQ(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])).b,n.g,n.f)&&--t;for(f=n.w.a.ec().Jc();f.Ob();)r=u(f.Pb(),12),dQ(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])).b,e.g,e.f)&&++i;for(c=n.r.a.ec().Jc();c.Ob();)r=u(c.Pb(),12),dQ(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])).b,e.g,e.f)&&--i;t=0)return t;switch(xw(Vc(e,t))){case 2:{if(bn("",Dd(e,t.ok()).ve())){if(f=BC(Vc(e,t)),l=L9(Vc(e,t)),b=gbe(e,n,f,l),b)return b;for(r=qbe(e,n),o=0,p=r.gc();o1)throw $(new Gn(JN));for(b=Po(e.e.Ah(),n),i=u(e.g,122),o=0;o1,h=new La(y.b);bu(h.a)||bu(h.b);)f=u(bu(h.a)?_(h.a):_(h.b),17),p=f.c==y?f.d:f.c,k.Math.abs(pu(z(B(Lr,1),Se,8,0,[p.i.n,p.n,p.a])).b-o.b)>1&&uDn(e,f,o,c,y)}}function APn(e){var n,t,i,r,c,o;if(r=new qr(e.e,0),i=new qr(e.a,0),e.d)for(t=0;tXee;){for(c=n,o=0;k.Math.abs(n-c)0),r.a.Xb(r.c=--r.b),R_n(e,e.b-o,c,i,r),at(r.b0),i.a.Xb(i.c=--i.b)}if(!e.d)for(t=0;t0?(e.f[b.p]=S/(b.e.c.length+b.g.c.length),e.c=k.Math.min(e.c,e.f[b.p]),e.b=k.Math.max(e.b,e.f[b.p])):l&&(e.f[b.p]=S)}}function TPn(e){e.b=null,e.bb=null,e.fb=null,e.qb=null,e.a=null,e.c=null,e.d=null,e.e=null,e.f=null,e.n=null,e.M=null,e.L=null,e.Q=null,e.R=null,e.K=null,e.db=null,e.eb=null,e.g=null,e.i=null,e.j=null,e.k=null,e.gb=null,e.o=null,e.p=null,e.q=null,e.r=null,e.$=null,e.ib=null,e.S=null,e.T=null,e.t=null,e.s=null,e.u=null,e.v=null,e.w=null,e.B=null,e.A=null,e.C=null,e.D=null,e.F=null,e.G=null,e.H=null,e.I=null,e.J=null,e.P=null,e.Z=null,e.U=null,e.V=null,e.W=null,e.X=null,e.Y=null,e._=null,e.ab=null,e.cb=null,e.hb=null,e.nb=null,e.lb=null,e.mb=null,e.ob=null,e.pb=null,e.jb=null,e.kb=null,e.N=!1,e.O=!1}function CPn(e,n,t){var i,r,c,o;for(t.Tg("Graph transformation ("+e.a+")",1),o=qb(n.a),c=new L(n.b);c.a=l.b.c)&&(l.b=n),(!l.c||n.c<=l.c.c)&&(l.d=l.c,l.c=n),(!l.e||n.d>=l.e.d)&&(l.e=n),(!l.f||n.d<=l.f.d)&&(l.f=n);return i=new cz((Z9(),op)),XC(e,Wtn,new Su(z(B(VN,1),On,377,0,[i]))),o=new cz(sm),XC(e,Qtn,new Su(z(B(VN,1),On,377,0,[o]))),r=new cz(om),XC(e,Ytn,new Su(z(B(VN,1),On,377,0,[r]))),c=new cz(xv),XC(e,Vtn,new Su(z(B(VN,1),On,377,0,[c]))),MW(i.c,op),MW(r.c,om),MW(c.c,xv),MW(o.c,sm),l.a.c.length=0,Er(l.a,i.c),Er(l.a,Ks(r.c)),Er(l.a,c.c),Er(l.a,Ks(o.c)),l}function DPn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;for(n.Tg(lWe,1),S=ne(re(ye(e,(Yh(),Mm)))),o=ne(re(ye(e,(Ja(),zx)))),l=u(ye(e,Bx),104),Yhe((!e.a&&(e.a=new pe(Jt,e,10,11)),e.a)),b=GKe((!e.a&&(e.a=new pe(Jt,e,10,11)),e.a),S,o),!e.a&&(e.a=new pe(Jt,e,10,11)),h=new L(b);h.a0&&(e.a=f+(S-1)*c,n.c.b+=e.a,n.f.b+=e.a)),A.a.gc()!=0&&(y=new jV(1,c),S=jge(y,n,A,O,n.f.b+f-n.c.b),S>0&&(n.f.b+=f+(S-1)*c))}function QKe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te;for(b=ne(re(T(e,(Ne(),Sg)))),i=ne(re(T(e,m4e))),y=new R6,he(y,Sg,b+i),h=n,R=h.d,O=h.c.i,q=h.d.i,I=zse(O.c),V=zse(q.c),r=new Ce,p=I;p<=V;p++)l=new Ba(e),Tf(l,(zn(),dr)),he(l,(me(),wi),h),he(l,Wi,(Br(),to)),he(l,yH,y),S=u(Le(e.b,p),25),p==I?z0(l,S.a.c.length-t,S):Or(l,S),te=ne(re(T(h,Gd))),te<0&&(te=0,he(h,Gd,te)),l.o.b=te,A=k.Math.floor(te/2),o=new Qu,xr(o,(De(),Kn)),gu(o,l),o.n.b=A,f=new Qu,xr(f,et),gu(f,l),f.n.b=A,Gr(h,o),c=new Mw,$u(c,h),he(c,Wc,null),lc(c,f),Gr(c,R),zxn(l,h,c),Hn(r.c,c),h=c;return r}function _Pn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;if(O=n.b.c.length,!(O<3)){for(S=oe($t,ni,30,O,15,1),p=0,b=new L(n.b);b.ao)&&hr(e.b,u(I.b,17));++l}c=o}}}function tZ(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;for(f=u(Pd(e,(De(),Kn)).Jc().Pb(),12).e,S=u(Pd(e,et).Jc().Pb(),12).g,l=f.c.length,V=_a(u(Le(e.j,0),12));l-- >0;){for(O=(vn(0,f.c.length),u(f.c[0],17)),r=(vn(0,S.c.length),u(S.c[0],17)),q=r.d.e,c=wu(q,r,0),qyn(O,r.d,c),lc(r,null),Gr(r,null),A=O.a,n&&Vt(A,new wc(V)),i=St(r.a,0);i.b!=i.d.c;)t=u(jt(i),8),Vt(A,new wc(t));for(R=O.b,y=new L(r.b);y.a-2;default:return!1}switch(n=e.Pj(),e.p){case 0:return n!=null&&Re($e(n))!=Gj(e.k,0);case 1:return n!=null&&u(n,221).a!=Rt(e.k)<<24>>24;case 2:return n!=null&&u(n,180).a!=(Rt(e.k)&yr);case 6:return n!=null&&Gj(u(n,190).a,e.k);case 5:return n!=null&&u(n,15).a!=Rt(e.k);case 7:return n!=null&&u(n,191).a!=Rt(e.k)<<16>>16;case 3:return n!=null&&ne(re(n))!=e.j;case 4:return n!=null&&u(n,164).a!=e.j;default:return n==null?e.n!=null:!di(n,e.n)}}function cN(e,n,t){var i,r,c,o;return e.ml()&&e.ll()&&(o=gV(e,u(t,57)),ue(o)!==ue(t))?(e.vj(n),e.Bj(n,B$e(e,n,o)),e.$k()&&(c=(r=u(t,52),e.kl()?e.il()?r.Qh(e.b,Oc(u(An(Go(e.b),e.Jj()),19)).n,u(An(Go(e.b),e.Jj()).Fk(),29).ik(),null):r.Qh(e.b,zi(r.Ah(),Oc(u(An(Go(e.b),e.Jj()),19))),null,null):r.Qh(e.b,-1-e.Jj(),null,null)),!u(o,52).Mh()&&(c=(i=u(o,52),e.kl()?e.il()?i.Oh(e.b,Oc(u(An(Go(e.b),e.Jj()),19)).n,u(An(Go(e.b),e.Jj()).Fk(),29).ik(),c):i.Oh(e.b,zi(i.Ah(),Oc(u(An(Go(e.b),e.Jj()),19))),null,c):i.Oh(e.b,-1-e.Jj(),null,c))),c&&c.mj()),Fs(e.b)&&e.Hj(e.Gj(9,t,o,n,!1)),o):t}function WKe(e){var n,t,i,r,c,o,l,f,h,b;for(i=new Ce,o=new L(e.e.a);o.a0&&(o=k.Math.max(o,qBe(e.C.b+i.d.b,r))),b=i,p=r,y=c;e.C&&e.C.c>0&&(S=y+e.C.c,h&&(S+=b.d.c),o=k.Math.max(o,(Oa(),Bf(Ga),k.Math.abs(p-1)<=Ga||p==1||isNaN(p)&&isNaN(1)?0:S/(1-p)))),t.n.b=0,t.a.a=o}function eVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S;if(t=u(zc(e.b,n),127),f=u(u(pi(e.r,n),22),83),f.dc()){t.n.d=0,t.n.a=0;return}for(h=e.u.Gc((ps(),Z1)),o=0,e.A.Gc((Vs(),Og))&&OXe(e,n),l=f.Jc(),b=null,y=0,p=0;l.Ob();)i=u(l.Pb(),115),c=ne(re(i.b.mf((F$(),wJ)))),r=i.b.Kf().b,b?(S=p+b.d.a+e.w+i.d.d,o=k.Math.max(o,(Oa(),Bf(Ga),k.Math.abs(y-c)<=Ga||y==c||isNaN(y)&&isNaN(c)?0:S/(c-y)))):e.C&&e.C.d>0&&(o=k.Math.max(o,qBe(e.C.d+i.d.d,c))),b=i,y=c,p=r;e.C&&e.C.a>0&&(S=p+e.C.a,h&&(S+=b.d.a),o=k.Math.max(o,(Oa(),Bf(Ga),k.Math.abs(y-1)<=Ga||y==1||isNaN(y)&&isNaN(1)?0:S/(1-y)))),t.n.d=0,t.a.b=o}function nVe(e,n,t){var i,r,c,o,l,f;for(this.g=e,l=n.d.length,f=t.d.length,this.d=oe(c1,Bd,9,l+f,0,1),o=0;o0?OY(this,this.f/this.a):Na(n.g,n.d[0]).a!=null&&Na(t.g,t.d[0]).a!=null?OY(this,(ne(Na(n.g,n.d[0]).a)+ne(Na(t.g,t.d[0]).a))/2):Na(n.g,n.d[0]).a!=null?OY(this,Na(n.g,n.d[0]).a):Na(t.g,t.d[0]).a!=null&&OY(this,Na(t.g,t.d[0]).a)}function PPn(e,n,t,i,r,c,o,l){var f,h,b,p,y,S,A,O,I,R;if(A=!1,h=Ebe(t.q,n.f+n.b-t.q.f),S=i.f>n.b&&l,R=r-(t.q.e+h-o),p=(f=fS(i,R,!1),f.a),S&&p>i.f)return!1;if(S){for(y=0,I=new L(n.d);I.a=(vn(c,e.c.length),u(e.c[c],186)).e,!S&&p>n.b&&!b)?!1:((b||S||p<=n.b)&&(b&&p>n.b?(t.d=p,eO(t,PGe(t,p))):(QHe(t.q,h),t.c=!0),eO(i,r-(t.s+t.r)),IO(i,t.q.e+t.q.d,n.f),vB(n,i),e.c.length>c&&($O((vn(c,e.c.length),u(e.c[c],186)),i),(vn(c,e.c.length),u(e.c[c],186)).a.c.length==0&&Ad(e,c)),A=!0),A)}function $Pn(e,n){var t,i,r,c,o,l,f,h,b,p;for(e.a=new vIe(lkn(Vx)),i=new L(n.a);i.a0&&(Yn(0,t.length),t.charCodeAt(0)!=47)))throw $(new Gn("invalid opaquePart: "+t));if(e&&!(n!=null&&Sj(jG,n.toLowerCase()))&&!(t==null||!kQ(t,uA,oA)))throw $(new Gn(FZe+t));if(e&&n!=null&&Sj(jG,n.toLowerCase())&&!PAn(t))throw $(new Gn(FZe+t));if(!Jjn(i))throw $(new Gn("invalid device: "+i));if(!zkn(r))throw o=r==null?"invalid segments: null":"invalid segment: "+_kn(r),$(new Gn(o));if(!(c==null||lh(c,Xo(35))==-1))throw $(new Gn("invalid query: "+c))}function iVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;if(y=new wc(e.o),R=n.a/y.a,l=n.b/y.b,O=n.a-y.a,c=n.b-y.b,t)for(r=ue(T(e,(Ne(),Wi)))===ue((Br(),to)),A=new L(e.j);A.a=1&&(I-o>0&&p>=0?(f.n.a+=O,f.n.b+=c*o):I-o<0&&b>=0&&(f.n.a+=O*I,f.n.b+=c));e.o.a=n.a,e.o.b=n.b,he(e,(Ne(),jg),(Vs(),i=u(sa(tA),10),new _l(i,u(_f(i,i.length),10),0)))}function FPn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;if(t.Tg("Network simplex layering",1),e.b=n,R=u(T(n,(Ne(),kx)),15).a*4,I=e.b.a,I.c.length<1){t.Ug();return}for(c=_In(e,I),O=null,r=St(c,0);r.b!=r.d.c;){for(i=u(jt(r),16),l=R*sc(k.Math.sqrt(i.gc())),o=KIn(i),BW(_oe(Kbn(Loe(VK(o),l),O),!0),t.dh(1)),y=e.b.b,A=new L(o.a);A.a1)for(O=oe($t,ni,30,e.b.b.c.length,15,1),p=0,h=new L(e.b.b);h.a0){tz(e,t,0),t.a+=String.fromCharCode(i),r=AEn(n,c),tz(e,t,r),c+=r-1;continue}i==39?c+10&&A.a<=0){f.c.length=0,Hn(f.c,A);break}S=A.i-A.d,S>=l&&(S>l&&(f.c.length=0,l=S),Hn(f.c,A))}f.c.length!=0&&(o=u(Le(f,sz(r,f.c.length)),116),V.a.Ac(o)!=null,o.g=b++,oge(o,n,t,i),f.c.length=0)}for(I=e.c.length+1,y=new L(e);y.aDr||n.o==Ag&&b=l&&r<=f)l<=r&&c<=f?(t[b++]=r,t[b++]=c,i+=2):l<=r?(t[b++]=r,t[b++]=f,e.b[i]=f+1,o+=2):c<=f?(t[b++]=l,t[b++]=c,i+=2):(t[b++]=l,t[b++]=f,e.b[i]=f+1);else if(fX0)&&l<10);Poe(e.c,new b5),rVe(e),Pvn(e.c),OPn(e.f)}function ZPn(e,n){var t,i,r,c,o,l,f,h,b,p,y;switch(e.k.g){case 1:if(i=u(T(e,(me(),wi)),17),t=u(T(i,Kve),78),t?Re($e(T(i,Hd)))&&(t=k1e(t)):t=new xs,h=u(T(e,ja),12),h){if(b=pu(z(B(Lr,1),Se,8,0,[h.i.n,h.n,h.a])),n<=b.a)return b.b;Xi(t,b,t.a,t.a.a)}if(p=u(T(e,gf),12),p){if(y=pu(z(B(Lr,1),Se,8,0,[p.i.n,p.n,p.a])),y.a<=n)return y.b;Xi(t,y,t.c.b,t.c)}if(t.b>=2){for(f=St(t,0),o=u(jt(f),8),l=u(jt(f),8);l.a0&&kO(h,!0,(vr(),ru)),l.k==(zn(),wr)&&_Ie(h),ei(e.f,l,n)}}function uVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q;for(h=Ki,b=Ki,l=Dr,f=Dr,y=new L(n.i);y.a=e.j?(++e.j,Te(e.b,ve(1)),Te(e.c,b)):(i=e.d[n.p][1],ol(e.b,h,ve(u(Le(e.b,h),15).a+1-i)),ol(e.c,h,ne(re(Le(e.c,h)))+b-i*e.f)),(e.r==(G0(),gD)&&(u(Le(e.b,h),15).a>e.k||u(Le(e.b,h-1),15).a>e.k)||e.r==wD&&(ne(re(Le(e.c,h)))>e.n||ne(re(Le(e.c,h-1)))>e.n))&&(f=!1),o=new qn(Vn(rr(n).a.Jc(),new ee));ht(o);)c=u(tt(o),17),l=c.c.i,e.g[l.p]==h&&(p=oVe(e,l),r=r+u(p.a,15).a,f=f&&Re($e(p.b)));return e.g[n.p]=h,r=r+e.d[n.p][0],new jc(ve(r),(Pn(),!!f))}function n$n(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;return y=e.c[n],S=e.c[t],A=u(T(y,(me(),Ty)),16),!!A&&A.gc()!=0&&A.Gc(S)||(O=y.k!=(zn(),dr)&&S.k!=dr,I=u(T(y,ap),9),R=u(T(S,ap),9),q=I!=R,V=!!I&&I!=y||!!R&&R!=S,te=qQ(y,(De(),Xn)),be=qQ(S,bt),V=V|(qQ(y,bt)||qQ(S,Xn)),fe=V&&q||te||be,O&&fe)||y.k==(zn(),wo)&&S.k==Qi||S.k==(zn(),wo)&&y.k==Qi?!1:(b=e.c[n],c=e.c[t],r=HHe(e.e,b,c,(De(),Kn)),f=HHe(e.i,b,c,et),TNn(e.f,b,c),h=Yze(e.b,b,c)+u(r.a,15).a+u(f.a,15).a+e.f.d,l=Yze(e.b,c,b)+u(r.b,15).a+u(f.b,15).a+e.f.b,e.a&&(p=u(T(b,wi),12),o=u(T(c,wi),12),i=MHe(e.g,p,o),h+=u(i.a,15).a,l+=u(i.b,15).a),h>l)}function sVe(e,n){var t,i,r,c,o;t=ne(re(T(n,(Ne(),Vf)))),t<2&&he(n,Vf,2),i=u(T(n,pl),86),i==(vr(),eh)&&he(n,pl,GB(n)),r=u(T(n,Dun),15),r.a==0?he(n,(me(),Oy),new vQ):he(n,(me(),Oy),new XR(r.a)),c=$e(T(n,mx)),c==null&&he(n,mx,(Pn(),ue(T(n,Y1))===ue((z1(),H7)))),Zi(new wn(null,new pn(n.a,16)),new Zue(e)),Zi(ou(new wn(null,new pn(n.b,16)),new Af),new eoe(e)),o=new tVe(n),he(n,(me(),Lv),o),RC(e.a),fa(e.a,(zr(),Kf),u(T(n,_y),188)),fa(e.a,r1,u(T(n,bH),188)),fa(e.a,eo,u(T(n,wx),188)),fa(e.a,no,u(T(n,mH),188)),fa(e.a,Pc,D7n(u(T(n,Y1),222))),Fse(e.a,YRn(n)),he(n,yie,rN(e.a,n))}function jge(e,n,t,i,r){var c,o,l,f,h,b,p,y,S,A,O,I,R;for(p=new pt,o=new Ce,tqe(e,t,e.d.zg(),o,p),tqe(e,i,e.d.Ag(),o,p),e.b=.2*(O=lUe(ou(new wn(null,new pn(o,16)),new pM)),I=lUe(ou(new wn(null,new pn(o,16)),new mM)),k.Math.min(O,I)),c=0,l=0;l=2&&(R=NUe(o,!0,y),!e.e&&(e.e=new MEe(e)),SEn(e.e,R,o,e.b)),sGe(o,y),o$n(o),S=-1,b=new L(o);b.a0&&(t+=f.n.a+f.o.a/2,++p),A=new L(f.j);A.a0&&(t/=p),R=oe(Jr,Jc,30,i.a.c.length,15,1),l=0,h=new L(i.a);h.a-1){for(r=St(l,0);r.b!=r.d.c;)i=u(jt(r),132),i.v=o;for(;l.b!=0;)for(i=u(eW(l,0),132),t=new L(i.i);t.a-1){for(c=new L(l);c.a0)&&(fw(f,k.Math.min(f.o,r.o-1)),g0(f,f.i-1),f.i==0&&Hn(l.c,f))}}function aVe(e,n,t,i,r){var c,o,l,f;return f=Ki,o=!1,l=dge(e,Nr(new je(n.a,n.b),e),gi(new je(t.a,t.b),r),Nr(new je(i.a,i.b),t)),c=!!l&&!(k.Math.abs(l.a-e.a)<=np&&k.Math.abs(l.b-e.b)<=np||k.Math.abs(l.a-n.a)<=np&&k.Math.abs(l.b-n.b)<=np),l=dge(e,Nr(new je(n.a,n.b),e),t,r),l&&((k.Math.abs(l.a-e.a)<=np&&k.Math.abs(l.b-e.b)<=np)==(k.Math.abs(l.a-n.a)<=np&&k.Math.abs(l.b-n.b)<=np)||c?f=k.Math.min(f,fE(Nr(l,t))):o=!0),l=dge(e,Nr(new je(n.a,n.b),e),i,r),l&&(o||(k.Math.abs(l.a-e.a)<=np&&k.Math.abs(l.b-e.b)<=np)==(k.Math.abs(l.a-n.a)<=np&&k.Math.abs(l.b-n.b)<=np)||c)&&(f=k.Math.min(f,fE(Nr(l,i)))),f}function hVe(e){r2(e,new Jw(GP(t2(Zp(n2(e2(new dd,V0),cQe),"Minimizes the stress within a layout using stress majorization. Stress exists if the euclidean distance between a pair of nodes doesn't match their graph theoretic distance, that is, the shortest path between the two nodes. The method allows to specify individual edge lengths."),new dc),$o))),Ee(e,V0,jS,_e(d3e)),Ee(e,V0,lF,(Pn(),!0)),Ee(e,V0,wv,_e(Ltn)),Ee(e,V0,dy,_e(Ptn)),Ee(e,V0,hy,_e($tn)),Ee(e,V0,U8,_e(_tn)),Ee(e,V0,ES,_e(g3e)),Ee(e,V0,X8,_e(Rtn)),Ee(e,V0,swe,_e(h3e)),Ee(e,V0,fwe,_e(f3e)),Ee(e,V0,awe,_e(a3e)),Ee(e,V0,hwe,_e(b3e)),Ee(e,V0,lwe,_e(kJ))}function s$n(e){var n,t,i,r,c,o,l,f;for(n=null,i=new L(e);i.a0&&t.c==0&&(!n&&(n=new Ce),Hn(n.c,t));if(n)for(;n.c.length!=0;){if(t=u(Ad(n,0),239),t.b&&t.b.c.length>0){for(c=(!t.b&&(t.b=new Ce),new L(t.b));c.awu(e,t,0))return new jc(r,t)}else if(ne(Na(r.g,r.d[0]).a)>ne(Na(t.g,t.d[0]).a))return new jc(r,t)}for(l=(!t.e&&(t.e=new Ce),t.e).Jc();l.Ob();)o=u(l.Pb(),239),f=(!o.b&&(o.b=new Ce),o.b),S2(0,f.c.length),Ij(f.c,0,t),o.c==f.c.length&&Hn(n.c,o)}return null}function dS(e,n){var t,i,r,c,o,l,f,h,b;if(n.e==5){cVe(e,n);return}if(h=n,!(h.b==null||e.b==null)){for(ov(e),aS(e),ov(h),aS(h),t=oe($t,ni,30,e.b.length+h.b.length,15,1),b=0,i=0,o=0;i=l&&r<=f)l<=r&&c<=f?i+=2:l<=r?(e.b[i]=f+1,o+=2):c<=f?(t[b++]=r,t[b++]=l-1,i+=2):(t[b++]=r,t[b++]=l-1,e.b[i]=f+1,o+=2);else if(f0),u(b.a.Xb(b.c=--b.b),17));c!=i&&b.b>0;)e.a[c.p]=!0,e.a[i.p]=!0,c=(at(b.b>0),u(b.a.Xb(b.c=--b.b),17));b.b>0&&As(b)}}function dVe(e,n,t){var i,r,c,o,l,f,h,b,p,y;if(t)for(i=-1,b=new qr(n,0);b.b0?r-=864e5:r+=864e5,f=new jle(mc(Pu(n.q.getTime()),r))),b=new o4,h=e.a.length,c=0;c=97&&i<=122||i>=65&&i<=90){for(o=c+1;o=h)throw $(new Gn("Missing trailing '"));o+1=14&&b<=16))?n.a._b(i)?(t.a?Kt(t.a,t.b):t.a=new il(t.d),Uj(t.a,"[...]")):(l=B4(i),h=new w2(n),I1(t,gVe(l,h))):X(i,171)?I1(t,tCn(u(i,171))):X(i,195)?I1(t,GAn(u(i,195))):X(i,201)?I1(t,YMn(u(i,201))):X(i,2073)?I1(t,qAn(u(i,2073))):X(i,54)?I1(t,nCn(u(i,54))):X(i,584)?I1(t,gCn(u(i,584))):X(i,830)?I1(t,eCn(u(i,830))):X(i,108)&&I1(t,ZTn(u(i,108))):I1(t,i==null?Vo:su(i));return t.a?t.e.length==0?t.a.a:t.a.a+(""+t.e):t.c}function N8(e,n){var t,i,r,c;c=e.F,n==null?(e.F=null,i8(e,null)):(e.F=(_n(n),n),i=lh(n,Xo(60)),i!=-1?(r=(Yr(0,i,n.length),n.substr(0,i)),lh(n,Xo(46))==-1&&!bn(r,ry)&&!bn(r,RS)&&!bn(r,XF)&&!bn(r,BS)&&!bn(r,zS)&&!bn(r,FS)&&!bn(r,JS)&&!bn(r,HS)&&(r=een),t=R$(n,Xo(62)),t!=-1&&(r+=""+(Yn(t+1,n.length+1),n.substr(t+1))),i8(e,r)):(r=n,lh(n,Xo(46))==-1&&(i=lh(n,Xo(91)),i!=-1&&(r=(Yr(0,i,n.length),n.substr(0,i))),!bn(r,ry)&&!bn(r,RS)&&!bn(r,XF)&&!bn(r,BS)&&!bn(r,zS)&&!bn(r,FS)&&!bn(r,JS)&&!bn(r,HS)?(r=een,i!=-1&&(r+=""+(Yn(i,n.length+1),n.substr(i)))):r=n),i8(e,r),r==n&&(e.F=e.D))),(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,5,c,n))}function g$n(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;if(e.c=e.e,A=$e(T(n,(Ne(),Iun))),S=A==null||(_n(A),A),c=u(T(n,(me(),po)),22).Gc((Dc(),Kl)),r=u(T(n,Wi),102),t=!(r==(Br(),Cg)||r==f1||r==to),S&&(t||!c)){for(p=new L(n.a);p.a=0)return r=Rjn(e,(Yr(1,o,n.length),n.substr(1,o-1))),b=(Yr(o+1,f,n.length),n.substr(o+1,f-(o+1))),FRn(e,b,r)}else{if(t=-1,Mme==null&&(Mme=new RegExp("\\d")),Mme.test(String.fromCharCode(l))&&(t=Hle(n,Xo(46),f-1),t>=0)){i=u(fY(e,VRe(e,(Yr(1,t,n.length),n.substr(1,t-1))),!1),61),h=0;try{h=hl((Yn(t+1,n.length+1),n.substr(t+1)),Xr,oi)}catch(y){throw y=sr(y),X(y,131)?(c=y,$(new uB(c))):$(y)}if(h>16==-10?t=u(e.Cb,293).Wk(n,t):e.Db>>16==-15&&(!n&&(n=(yn(),ih)),!h&&(h=(yn(),ih)),e.Cb.Vh()&&(f=new L1(e.Cb,1,13,h,n,Ld(Cs(u(e.Cb,62)),e),!1),t?t.lj(f):t=f));else if(X(e.Cb,88))e.Db>>16==-23&&(X(n,88)||(n=(yn(),jf)),X(h,88)||(h=(yn(),jf)),e.Cb.Vh()&&(f=new L1(e.Cb,1,10,h,n,Ld(Vu(u(e.Cb,29)),e),!1),t?t.lj(f):t=f));else if(X(e.Cb,446))for(l=u(e.Cb,834),o=(!l.b&&(l.b=new OP(new kX)),l.b),c=(i=new D2(new on(o.a).a),new NP(i));c.a.b;)r=u(Q3(c.a).jd(),87),t=D8(r,Oz(r,l),t)}return t}function p$n(e,n){var t,i,r,c,o,l,f,h,b,p,y;for(o=Re($e(ye(e,(Ne(),pm)))),y=u(ye(e,ym),22),f=!1,h=!1,p=new ut((!e.c&&(e.c=new pe($s,e,9,9)),e.c));p.e!=p.i.gc()&&(!f||!h);){for(c=u(ft(p),125),l=0,r=qh(Rl(z(B(Xl,1),On,20,0,[(!c.d&&(c.d=new Nn(pr,c,8,5)),c.d),(!c.e&&(c.e=new Nn(pr,c,7,4)),c.e)])));ht(r)&&(i=u(tt(r),85),b=o&&Hw(i)&&Re($e(ye(i,kg))),t=VKe((!i.b&&(i.b=new Nn(vt,i,4,7)),i.b),c)?e==Bi(iu(u(K((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c),0),84))):e==Bi(iu(u(K((!i.b&&(i.b=new Nn(vt,i,4,7)),i.b),0),84))),!((b||t)&&(++l,l>1))););(l>0||y.Gc((ps(),Z1))&&(!c.n&&(c.n=new pe(ju,c,1,7)),c.n).i>0)&&(f=!0),l>1&&(h=!0)}f&&n.Ec((Dc(),Kl)),h&&n.Ec((Dc(),cx))}function pVe(e){var n,t,i,r,c,o,l,f,h,b,p,y;if(y=u(ye(e,(Xt(),Tg)),22),y.dc())return null;if(l=0,o=0,y.Gc((Vs(),qD))){for(b=u(ye(e,Kx),102),i=2,t=2,r=2,c=2,n=Bi(e)?u(ye(Bi(e),Mg),86):u(ye(e,Mg),86),h=new ut((!e.c&&(e.c=new pe($s,e,9,9)),e.c));h.e!=h.i.gc();)if(f=u(ft(h),125),p=u(ye(f,Qv),64),p==(De(),ku)&&(p=uge(f,n),ji(f,Qv,p)),b==(Br(),to))switch(p.g){case 1:i=k.Math.max(i,f.i+f.g);break;case 2:t=k.Math.max(t,f.j+f.f);break;case 3:r=k.Math.max(r,f.i+f.g);break;case 4:c=k.Math.max(c,f.j+f.f)}else switch(p.g){case 1:i+=f.g+2;break;case 2:t+=f.f+2;break;case 3:r+=f.g+2;break;case 4:c+=f.f+2}l=k.Math.max(i,r),o=k.Math.max(t,c)}return Xw(e,l,o,!0,!0)}function m$n(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O;for(r=null,i=new L(n.a);i.a1)for(r=e.e.b,Vt(e.e,f),l=f.a.ec().Jc();l.Ob();)o=u(l.Pb(),9),ei(e.c,o,ve(r))}}function v$n(e,n,t,i){var r,c,o,l,f,h,b,p,y,S;for(c=new Lqe(n),p=UDn(e,n,c),S=k.Math.max(ne(re(T(n,(Ne(),Gd)))),1),b=new L(p.a);b.a=0){for(f=null,l=new qr(b.a,h+1);l.b0,h?h&&(y=R.p,o?++y:--y,p=u(Le(R.c.a,y),9),i=Oze(p),S=!(RUe(i,fe,t[0])||KDe(i,fe,t[0]))):S=!0),A=!1,be=n.D.i,be&&be.c&&l.e&&(b=o&&be.p>0||!o&&be.p=0&&Oo?1:Lb(isNaN(0),isNaN(o)))<0&&(Bf(xh),(k.Math.abs(o-1)<=xh||o==1||isNaN(o)&&isNaN(1)?0:o<1?-1:o>1?1:Lb(isNaN(o),isNaN(1)))<0)&&(Bf(xh),(k.Math.abs(0-l)<=xh||l==0||isNaN(0)&&isNaN(l)?0:0l?1:Lb(isNaN(0),isNaN(l)))<0)&&(Bf(xh),(k.Math.abs(l-1)<=xh||l==1||isNaN(l)&&isNaN(1)?0:l<1?-1:l>1?1:Lb(isNaN(l),isNaN(1)))<0)),c)}function M$n(e){var n,t,i,r,c,o,l,f,h,b,p;for(e.j=oe($t,ni,30,e.g,15,1),e.o=new Ce,Zi(ou(new wn(null,new pn(e.e.b,16)),new h3),new jEe(e)),e.a=oe(ts,pa,30,e.b,16,1),AO(new wn(null,new pn(e.e.b,16)),new SEe(e)),i=(p=new Ce,Zi(si(ou(new wn(null,new pn(e.e.b,16)),new _5),new EEe(e)),new sTe(e,p)),p),f=new L(i);f.a=h.c.c.length?b=Bae((zn(),Qi),dr):b=Bae((zn(),dr),dr),b*=2,c=t.a.g,t.a.g=k.Math.max(c,c+(b-c)),o=t.b.g,t.b.g=k.Math.max(o,o+(b-o)),r=n}}function Vz(e,n){var t;if(e.e)throw $(new Uc((M1(bte),qZ+bte.k+UZ)));if(!zgn(e.a,n))throw $(new hu($Ye+n+RYe));if(n==e.d)return e;switch(t=e.d,e.d=n,t.g){case 0:switch(n.g){case 2:zw(e);break;case 1:P0(e),zw(e);break;case 4:rv(e),zw(e);break;case 3:rv(e),P0(e),zw(e)}break;case 2:switch(n.g){case 1:P0(e),_W(e);break;case 4:rv(e),zw(e);break;case 3:rv(e),P0(e),zw(e)}break;case 1:switch(n.g){case 2:P0(e),_W(e);break;case 4:P0(e),rv(e),zw(e);break;case 3:P0(e),rv(e),P0(e),zw(e)}break;case 4:switch(n.g){case 2:rv(e),zw(e);break;case 1:rv(e),P0(e),zw(e);break;case 3:P0(e),_W(e)}break;case 3:switch(n.g){case 2:P0(e),rv(e),zw(e);break;case 1:P0(e),rv(e),P0(e),zw(e);break;case 4:P0(e),_W(e)}}return e}function hv(e,n){var t;if(e.d)throw $(new Uc((M1(Tte),qZ+Tte.k+UZ)));if(!Bgn(e.a,n))throw $(new hu($Ye+n+RYe));if(n==e.c)return e;switch(t=e.c,e.c=n,t.g){case 0:switch(n.g){case 2:Zb(e);break;case 1:L0(e),Zb(e);break;case 4:cv(e),Zb(e);break;case 3:cv(e),L0(e),Zb(e)}break;case 2:switch(n.g){case 1:L0(e),LW(e);break;case 4:cv(e),Zb(e);break;case 3:cv(e),L0(e),Zb(e)}break;case 1:switch(n.g){case 2:L0(e),LW(e);break;case 4:L0(e),cv(e),Zb(e);break;case 3:L0(e),cv(e),L0(e),Zb(e)}break;case 4:switch(n.g){case 2:cv(e),Zb(e);break;case 1:cv(e),L0(e),Zb(e);break;case 3:L0(e),LW(e)}break;case 3:switch(n.g){case 2:L0(e),cv(e),Zb(e);break;case 1:L0(e),cv(e),L0(e),Zb(e);break;case 4:L0(e),LW(e)}}return e}function T$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;for(p=e.b,b=new qr(p,0),d2(b,new Xu(e)),q=!1,o=1;b.b0&&(n.a+=Co),Yz(u(ft(l),174),n);for(n.a+=eee,f=new p4((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c));f.e!=f.i.gc();)f.e>0&&(n.a+=Co),Yz(u(ft(f),174),n);n.a+=")"}}function C$n(e,n,t){var i,r,c,o,l,f,h,b;for(f=new ut((!e.a&&(e.a=new pe(Jt,e,10,11)),e.a));f.e!=f.i.gc();)for(l=u(ft(f),26),r=new qn(Vn(H0(l).a.Jc(),new ee));ht(r);){if(i=u(tt(r),85),!i.b&&(i.b=new Nn(vt,i,4,7)),!(i.b.i<=1&&(!i.c&&(i.c=new Nn(vt,i,5,8)),i.c.i<=1)))throw $(new u4("Graph must not contain hyperedges."));if(!ZE(i)&&l!=iu(u(K((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c),0),84)))for(h=new nNe,$u(h,i),he(h,(D0(),jy),i),EP(h,u(du(Xc(t.f,l)),155)),eX(h,u(Bn(t,iu(u(K((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c),0),84))),155)),Te(n.c,h),o=new ut((!i.n&&(i.n=new pe(ju,i,1,7)),i.n));o.e!=o.i.gc();)c=u(ft(o),157),b=new lPe(h,c.a),$u(b,c),he(b,jy,c),b.e.a=k.Math.max(c.g,1),b.e.b=k.Math.max(c.f,1),hge(b),Te(n.d,b)}}function O$n(e,n,t){var i,r,c,o,l,f,h,b,p,y;switch(t.Tg("Node promotion heuristic",1),e.i=n,e.r=u(T(n,(Ne(),aD)),243),e.r!=(G0(),D7)&&e.r!=Sx?tRn(e):TDn(e),b=u(T(e.i,i4e),15).a,c=new Oq,e.r.g){case 2:case 1:O8(e,c);break;case 3:for(e.r=MH,O8(e,c),f=0,l=new L(e.b);l.ae.k&&(e.r=gD,O8(e,c));break;case 4:for(e.r=MH,O8(e,c),h=0,r=new L(e.c);r.ae.n&&(e.r=wD,O8(e,c));break;case 6:y=sc(k.Math.ceil(e.g.length*b/100)),O8(e,new kje(y));break;case 5:p=sc(k.Math.ceil(e.e*b/100)),O8(e,new jje(p));break;case 8:ZVe(e,!0);break;case 9:ZVe(e,!1);break;default:O8(e,c)}e.r!=D7&&e.r!=Sx?KNn(e,n):dIn(e,n),t.Ug()}function N$n(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;for(p=new Mge(e),v4n(p,!(n==(vr(),Vl)||n==Za)),b=p.a,y=new t4,r=(ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])),o=0,f=r.length;o0&&(y.d+=b.n.d,y.d+=b.d),y.a>0&&(y.a+=b.n.a,y.a+=b.d),y.b>0&&(y.b+=b.n.b,y.b+=b.d),y.c>0&&(y.c+=b.n.c,y.c+=b.d),y}function yVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A;for(y=t.d,p=t.c,c=new je(t.f.a+t.d.b+t.d.c,t.f.b+t.d.d+t.d.a),o=c.b,h=new L(e.a);h.a0&&(e.c[n.c.p][n.p].d+=Is(e.i,24)*vN*.07000000029802322-.03500000014901161,e.c[n.c.p][n.p].a=e.c[n.c.p][n.p].d/e.c[n.c.p][n.p].b)}}function I$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;for(A=new L(e);A.ai.d,i.d=k.Math.max(i.d,n),l&&t&&(i.d=k.Math.max(i.d,i.a),i.a=i.d+r);break;case 3:t=n>i.a,i.a=k.Math.max(i.a,n),l&&t&&(i.a=k.Math.max(i.a,i.d),i.d=i.a+r);break;case 2:t=n>i.c,i.c=k.Math.max(i.c,n),l&&t&&(i.c=k.Math.max(i.b,i.c),i.b=i.c+r);break;case 4:t=n>i.b,i.b=k.Math.max(i.b,n),l&&t&&(i.b=k.Math.max(i.b,i.c),i.c=i.b+r)}}}function jVe(e,n){var t,i,r,c,o,l,f,h,b;return h="",n.length==0?e.le(Jge,wZ,-1,-1):(b=J2(n),bn(b.substr(0,3),"at ")&&(b=(Yn(3,b.length+1),b.substr(3))),b=b.replace(/\[.*?\]/g,""),o=b.indexOf("("),o==-1?(o=b.indexOf("@"),o==-1?(h=b,b=""):(h=J2((Yn(o+1,b.length+1),b.substr(o+1))),b=J2((Yr(0,o,b.length),b.substr(0,o))))):(t=b.indexOf(")",o),h=(Yr(o+1,t,b.length),b.substr(o+1,t-(o+1))),b=J2((Yr(0,o,b.length),b.substr(0,o)))),o=lh(b,Xo(46)),o!=-1&&(b=(Yn(o+1,b.length+1),b.substr(o+1))),(b.length==0||bn(b,"Anonymous function"))&&(b=wZ),l=R$(h,Xo(58)),r=Hle(h,Xo(58),l-1),f=-1,i=-1,c=Jge,l!=-1&&r!=-1&&(c=(Yr(0,r,h.length),h.substr(0,r)),f=yOe((Yr(r+1,l,h.length),h.substr(r+1,l-(r+1)))),i=yOe((Yn(l+1,h.length+1),h.substr(l+1)))),e.le(c,b,f,i))}function L$n(e){var n,t,i,r,c,o,l,f,h,b,p;for(h=new L(e);h.a0||b.j==Kn&&b.e.c.length-b.g.c.length<0)){n=!1;break}for(r=new L(b.g);r.a=h&&be>=I&&(y+=A.n.b+O.n.b+O.a.b-te,++l));if(t)for(o=new L(q.e);o.a=h&&be>=I&&(y+=A.n.b+O.n.b+O.a.b-te,++l))}l>0&&(fe+=y/l,++S)}S>0?(n.a=r*fe/S,n.g=S):(n.a=0,n.g=0)}function xge(e,n,t,i){var r,c,o,l,f;return l=new Mge(n),NNn(l,i),r=!0,e&&e.nf((Xt(),Mg))&&(c=u(e.mf((Xt(),Mg)),86),r=c==(vr(),eh)||c==Zc||c==ru),jXe(l,!1),Ao(l.e.Pf(),new Kle(l,!1,r)),JV(l,l.f,(ga(),Ou),(De(),Xn)),JV(l,l.f,Nu,bt),JV(l,l.g,Ou,Kn),JV(l,l.g,Nu,et),HJe(l,Xn),HJe(l,bt),RIe(l,et),RIe(l,Kn),h2(),o=l.A.Gc((Vs(),Lm))&&l.B.Gc((_s(),XD))?tJe(l):null,o&&Ybn(l.a,o),_$n(l),nxn(l),txn(l),f$n(l),E_n(l),Mxn(l),OQ(l,Xn),OQ(l,bt),aIn(l),ZLn(l),t&&(Xjn(l),Txn(l),OQ(l,et),OQ(l,Kn),f=l.B.Gc((_s(),iA)),lqe(l,f,Xn),lqe(l,f,bt),fqe(l,f,et),fqe(l,f,Kn),Zi(new wn(null,new pn(new ct(l.i),0)),new Fg),Zi(si(new wn(null,Jfe(l.r).a.oc()),new Eb),new Jg),zAn(l),l.e.Nf(l.o),Zi(new wn(null,Jfe(l.r).a.oc()),new _u)),l.o}function $$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O;for(h=Ki,i=new L(e.a.b);i.a1)for(S=new wge(A,V,i),rc(V,new fTe(e,S)),Hn(o.c,S),p=V.a.ec().Jc();p.Ob();)b=u(p.Pb(),49),qo(c,b.b);if(l.a.gc()>1)for(S=new wge(A,l,i),rc(l,new aTe(e,S)),Hn(o.c,S),p=l.a.ec().Jc();p.Ob();)b=u(p.Pb(),49),qo(c,b.b)}}function F$n(e,n){var t,i,r,c,o,l;if(u(T(n,(me(),po)),22).Gc((Dc(),Kl))){for(l=new L(n.a);l.a=0&&o0&&(u(zc(e.b,n),127).a.b=t)}function K$n(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;for(S=0,i=new ar,c=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));c.e!=c.i.gc();)r=u(ft(c),26),Re($e(ye(r,(Ne(),Eg))))||(p=Bi(r),qz(p)&&!Re($e(ye(r,lH)))&&(ji(r,(me(),Ci),ve(S)),++S,da(r,gm)&&hr(i,u(ye(r,gm),15))),SVe(e,r,t));for(he(t,(me(),cb),ve(S)),he(t,cD,ve(i.a.gc())),S=0,b=new ut((!n.b&&(n.b=new pe(pr,n,12,3)),n.b));b.e!=b.i.gc();)f=u(ft(b),85),qz(n)&&(ji(f,Ci,ve(S)),++S),I=hW(f),R=kGe(f),y=Re($e(ye(I,(Ne(),pm)))),O=!Re($e(ye(f,Eg))),A=y&&Hw(f)&&Re($e(ye(f,kg))),o=Bi(I)==n&&Bi(I)==Bi(R),l=(Bi(I)==n&&R==n)^(Bi(R)==n&&I==n),O&&!A&&(l||o)&&Dge(e,f,n,t);if(Bi(n))for(h=new ut(qIe(Bi(n)));h.e!=h.i.gc();)f=u(ft(h),85),I=hW(f),I==n&&Hw(f)&&(A=Re($e(ye(I,(Ne(),pm))))&&Re($e(ye(f,kg))),A&&Dge(e,f,n,t))}function V$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn,Dn;for(fe=new Ce,A=new L(e.b);A.a=n.length)return{done:!0};var r=n[i++];return{value:[r,t.get(r)],done:!1}}}},qDn()||(e.prototype.createObject=function(){return{}},e.prototype.get=function(n){return this.obj[":"+n]},e.prototype.set=function(n,t){this.obj[":"+n]=t},e.prototype[zZ]=function(n){delete this.obj[":"+n]},e.prototype.keys=function(){var n=[];for(var t in this.obj)t.charCodeAt(0)==58&&n.push(t.substring(1));return n}),e}function Ti(){Ti=Y,_x=new yi(owe),new Li("DEPTH",ve(0)),gre=new Li("FAN",ve(0)),pye=new Li(KQe,ve(0)),fb=new Li("ROOT",(Pn(),!1)),mre=new Li("LEFTNEIGHBOR",null),rsn=new Li("RIGHTNEIGHBOR",null),LH=new Li("LEFTSIBLING",null),vre=new Li("RIGHTSIBLING",null),bre=new Li("DUMMY",!1),new Li("LEVEL",ve(0)),yye=new Li("REMOVABLE_EDGES",new Si),jD=new Li("XCOOR",ve(0)),ED=new Li("YCOOR",ve(0)),PH=new Li("LEVELHEIGHT",0),Ea=new Li("LEVELMIN",0),Yf=new Li("LEVELMAX",0),wre=new Li("GRAPH_XMIN",0),pre=new Li("GRAPH_YMIN",0),mye=new Li("GRAPH_XMAX",0),vye=new Li("GRAPH_YMAX",0),wye=new Li("COMPACT_LEVEL_ASCENSION",!1),dre=new Li("COMPACT_CONSTRAINTS",new Ce),Ix=new Li("ID",""),Lx=new Li("POSITION",ve(0)),Xd=new Li("PRELIM",0),L7=new Li("MODIFIER",0),_7=new yi(iQe),kD=new yi(rQe)}function Z$n(e){cge();var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;if(e==null)return null;if(p=e.length*8,p==0)return"";for(l=p%24,S=p/24|0,y=l!=0?S+1:S,c=null,c=oe(Wl,kh,30,y*4,15,1),h=0,b=0,n=0,t=0,i=0,o=0,r=0,f=0;f>24,h=(n&3)<<24>>24,A=(n&-128)==0?n>>2<<24>>24:(n>>2^192)<<24>>24,O=(t&-128)==0?t>>4<<24>>24:(t>>4^240)<<24>>24,I=(i&-128)==0?i>>6<<24>>24:(i>>6^252)<<24>>24,c[o++]=t0[A],c[o++]=t0[O|h<<4],c[o++]=t0[b<<2|I],c[o++]=t0[i&63];return l==8?(n=e[r],h=(n&3)<<24>>24,A=(n&-128)==0?n>>2<<24>>24:(n>>2^192)<<24>>24,c[o++]=t0[A],c[o++]=t0[h<<4],c[o++]=61,c[o++]=61):l==16&&(n=e[r],t=e[r+1],b=(t&15)<<24>>24,h=(n&3)<<24>>24,A=(n&-128)==0?n>>2<<24>>24:(n>>2^192)<<24>>24,O=(t&-128)==0?t>>4<<24>>24:(t>>4^240)<<24>>24,c[o++]=t0[A],c[o++]=t0[O|h<<4],c[o++]=t0[b<<2],c[o++]=61),gh(c,0,c.length)}function eRn(e,n){var t,i,r,c,o,l,f;if(e.e==0&&e.p>0&&(e.p=-(e.p-1)),e.p>Xr&&Fae(n,e.p-K0),o=n.q.getDate(),GC(n,1),e.k>=0&&O4n(n,e.k),e.c>=0?GC(n,e.c):e.k>=0?(f=new p1e(n.q.getFullYear()-K0,n.q.getMonth(),35),i=35-f.q.getDate(),GC(n,k.Math.min(i,o))):GC(n,o),e.f<0&&(e.f=n.q.getHours()),e.b>0&&e.f<12&&(e.f+=12),zwn(n,e.f==24&&e.g?0:e.f),e.j>=0&&s9n(n,e.j),e.n>=0&&k9n(n,e.n),e.i>=0&&iCe(n,mc(ac(BO(Pu(n.q.getTime()),Rd),Rd),e.i)),e.a&&(r=new t$,Fae(r,r.q.getFullYear()-K0-80),JX(Pu(n.q.getTime()),Pu(r.q.getTime()))&&Fae(n,r.q.getFullYear()-K0+100)),e.d>=0){if(e.c==-1)t=(7+e.d-n.q.getDay())%7,t>3&&(t-=7),l=n.q.getMonth(),GC(n,n.q.getDate()+t),n.q.getMonth()!=l&&GC(n,n.q.getDate()+(t>0?-7:7));else if(n.q.getDay()!=e.d)return!1}return e.o>Xr&&(c=n.q.getTimezoneOffset(),iCe(n,mc(Pu(n.q.getTime()),(e.o-c)*60*Rd))),!0}function TVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te;if(r=T(n,(me(),wi)),!!X(r,206)){for(A=u(r,26),O=n.e,y=new wc(n.c),c=n.d,y.a+=c.b,y.b+=c.d,te=u(ye(A,(Ne(),vH)),182),cs(te,(_s(),lG))&&(S=u(ye(A,l4e),104),QU(S,c.a),nX(S,c.d),WU(S,c.b),ZU(S,c.c)),t=new Ce,b=new L(n.a);b.ai.c.length-1;)Te(i,new jc(mv,Fpe));t=u(T(r,Nh),15).a,x1(u(T(e,mp),86))?(r.e.ane(re((vn(t,i.c.length),u(i.c[t],49)).b))&&FT((vn(t,i.c.length),u(i.c[t],49)),r.e.a+r.f.a)):(r.e.bne(re((vn(t,i.c.length),u(i.c[t],49)).b))&&FT((vn(t,i.c.length),u(i.c[t],49)),r.e.b+r.f.b))}for(c=St(e.b,0);c.b!=c.d.c;)r=u(jt(c),40),t=u(T(r,(Mu(),Nh)),15).a,he(r,(Ti(),Ea),re((vn(t,i.c.length),u(i.c[t],49)).a)),he(r,Yf,re((vn(t,i.c.length),u(i.c[t],49)).b));n.Ug()}function tRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O;for(e.o=ne(re(T(e.i,(Ne(),xg)))),e.f=ne(re(T(e.i,ub))),e.j=e.i.b.c.length,l=e.j-1,y=0,e.k=0,e.n=0,e.b=$f(oe(jr,Se,15,e.j,0,1)),e.c=$f(oe(gr,Se,346,e.j,7,1)),o=new L(e.i.b);o.a0&&Te(e.q,b),Te(e.p,b);n-=i,S=f+n,h+=n*e.f,ol(e.b,l,ve(S)),ol(e.c,l,h),e.k=k.Math.max(e.k,S),e.n=k.Math.max(e.n,h),e.e+=n,n+=O}}function NVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;if(n.b!=0){for(S=new Si,l=null,A=null,i=sc(k.Math.floor(k.Math.log(n.b)*k.Math.LOG10E)+1),f=0,V=St(n,0);V.b!=V.d.c;)for(R=u(jt(V),40),ue(A)!==ue(T(R,(Ti(),Ix)))&&(A=Pt(T(R,Ix)),f=0),A!=null?l=A+dLe(f++,i):l=dLe(f++,i),he(R,Ix,l),I=(r=St(new S1(R).a.d,0),new E3(r));YT(I.a);)O=u(jt(I.a),65).c,Xi(S,O,S.c.b,S.c),he(O,Ix,l);for(y=new pt,o=0;o0&&(V-=S),pge(o,V),b=0,y=new L(o.a);y.a0),l.a.Xb(l.c=--l.b)),f=.4*i*b,!c&&l.b0&&(f=(Yn(0,n.length),n.charCodeAt(0)),f!=64)){if(f==37&&(p=n.lastIndexOf("%"),h=!1,p!=0&&(p==y-1||(h=(Yn(p+1,n.length),n.charCodeAt(p+1)==46))))){if(o=(Yr(1,p,n.length),n.substr(1,p-1)),V=bn("%",o)?null:Cge(o),i=0,h)try{i=hl((Yn(p+2,n.length+1),n.substr(p+2)),Xr,oi)}catch(te){throw te=sr(te),X(te,131)?(l=te,$(new uB(l))):$(te)}for(I=Uhe(e.Dh());I.Ob();)if(A=OB(I),X(A,504)&&(r=u(A,587),q=r.d,(V==null?q==null:bn(V,q))&&i--==0))return r;return null}if(b=n.lastIndexOf("."),S=b==-1?n:(Yr(0,b,n.length),n.substr(0,b)),t=0,b!=-1)try{t=hl((Yn(b+1,n.length+1),n.substr(b+1)),Xr,oi)}catch(te){if(te=sr(te),X(te,131))S=n;else throw $(te)}for(S=bn("%",S)?null:Cge(S),O=Uhe(e.Dh());O.Ob();)if(A=OB(O),X(A,197)&&(c=u(A,197),R=c.ve(),(S==null?R==null:bn(S,R))&&t--==0))return c;return null}return wVe(e,n)}function lRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q;for(b=new pt,f=new Tw,i=new L(e.a.a.b);i.an.d.c){if(S=e.c[n.a.d],I=e.c[p.a.d],S==I)continue;Hf(Nf(Of(Df(Cf(new tf,1),100),S),I))}}}}}function fRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;if(y=u(u(pi(e.r,n),22),83),n==(De(),et)||n==Kn){xVe(e,n);return}for(c=n==Xn?(Lw(),UN):(Lw(),XN),te=n==Xn?(Uo(),ka):(Uo(),Xf),t=u(zc(e.b,n),127),i=t.i,r=i.c+q3(z(B(Jr,1),Jc,30,15,[t.n.b,e.C.b,e.k])),R=i.c+i.b-q3(z(B(Jr,1),Jc,30,15,[t.n.c,e.C.c,e.k])),o=$oe(Vle(c),e.t),q=n==Xn?Dr:Ki,p=y.Jc();p.Ob();)h=u(p.Pb(),115),!(!h.c||h.c.d.c.length<=0)&&(I=h.b.Kf(),O=h.e,S=h.c,A=S.i,A.b=(f=S.n,S.e.a+f.b+f.c),A.a=(l=S.n,S.e.b+l.d+l.a),FC(te,ewe),S.f=te,ba(S,(ws(),Uf)),A.c=O.a-(A.b-I.a)/2,be=k.Math.min(r,O.a),fe=k.Math.max(R,O.a+I.a),A.cfe&&(A.c=fe-A.b),Te(o.d,new fV(A,U1e(o,A))),q=n==Xn?k.Math.max(q,O.b+h.b.Kf().b):k.Math.min(q,O.b));for(q+=n==Xn?e.t:-e.t,V=ade((o.e=q,o)),V>0&&(u(zc(e.b,n),127).a.b=V),b=y.Jc();b.Ob();)h=u(b.Pb(),115),!(!h.c||h.c.d.c.length<=0)&&(A=h.c.i,A.c-=h.e.a,A.d-=h.e.b)}function aRn(e,n){XW();var t,i,r,c,o,l,f,h,b,p,y,S,A,O;if(f=ao(e,0)<0,f&&(e=Td(e)),ao(e,0)==0)switch(n){case 0:return"0";case 1:return R8;case 2:return"0.00";case 3:return"0.000";case 4:return"0.0000";case 5:return"0.00000";case 6:return"0.000000";default:return S=new p0,n<0?S.a+="0E+":S.a+="0E",S.a+=n==Xr?"2147483648":""+-n,S.a}b=18,p=oe(Wl,kh,30,b+1,15,1),t=b,O=e;do h=O,O=BO(O,10),p[--t]=Rt(mc(48,lf(h,ac(O,10))))&yr;while(ao(O,0)!=0);if(r=lf(lf(lf(b,t),n),1),n==0)return f&&(p[--t]=45),gh(p,t,b-t);if(n>0&&ao(r,-6)>=0){if(ao(r,0)>=0){for(c=t+Rt(r),l=b-1;l>=c;l--)p[l+1]=p[l];return p[++c]=46,f&&(p[--t]=45),gh(p,t,b-t+1)}for(o=2;JX(o,mc(Td(r),1));o++)p[--t]=48;return p[--t]=46,p[--t]=48,f&&(p[--t]=45),gh(p,t,b-t)}return A=t+1,i=b,y=new o4,f&&(y.a+="-"),i-A>=1?(Fb(y,p[t]),y.a+=".",y.a+=gh(p,t+1,b-t-1)):y.a+=gh(p,t,b-t),y.a+="E",ao(r,0)>0&&(y.a+="+"),y.a+=""+rE(r),y.a}function DVe(e){r2(e,new Jw(GP(t2(Zp(n2(e2(new dd,Gl),"ELK Radial"),'A radial layout provider which is based on the algorithm of Peter Eades published in "Drawing free trees.", published by International Institute for Advanced Study of Social Information Science, Fujitsu Limited in 1991. The radial layouter takes a tree and places the nodes in radial order around the root. The nodes of the same tree level are placed on the same radius.'),new OM),Gl))),Ee(e,Gl,CF,_e(eln)),Ee(e,Gl,em,_e(nln)),Ee(e,Gl,wv,_e(Ysn)),Ee(e,Gl,dy,_e(Qsn)),Ee(e,Gl,hy,_e(Wsn)),Ee(e,Gl,U8,_e(Vsn)),Ee(e,Gl,ES,_e(Vye)),Ee(e,Gl,X8,_e(Zsn)),Ee(e,Gl,Zee,_e(Dre)),Ee(e,Gl,Wee,_e(Ire)),Ee(e,Gl,LF,_e(Qye)),Ee(e,Gl,ene,_e(_re)),Ee(e,Gl,nne,_e(Wye)),Ee(e,Gl,c2e,_e(Zye)),Ee(e,Gl,r2e,_e(Yye)),Ee(e,Gl,e2e,_e(FH)),Ee(e,Gl,n2e,_e(JH)),Ee(e,Gl,t2e,_e(SD)),Ee(e,Gl,i2e,_e(e6e)),Ee(e,Gl,Zpe,_e(Kye))}function Xw(e,n,t,i,r){var c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;if(I=new je(e.g,e.f),O=R0e(e),O.a=k.Math.max(O.a,n),O.b=k.Math.max(O.b,t),fe=O.a/I.a,b=O.b/I.b,te=O.a-I.a,f=O.b-I.b,i)for(o=Bi(e)?u(ye(Bi(e),(Xt(),Mg)),86):u(ye(e,(Xt(),Mg)),86),l=ue(ye(e,(Xt(),Kx)))===ue((Br(),to)),q=new ut((!e.c&&(e.c=new pe($s,e,9,9)),e.c));q.e!=q.i.gc();)switch(R=u(ft(q),125),V=u(ye(R,Qv),64),V==(De(),ku)&&(V=uge(R,o),ji(R,Qv,V)),V.g){case 1:l||Os(R,R.i*fe);break;case 2:Os(R,R.i+te),l||Ns(R,R.j*b);break;case 3:l||Os(R,R.i*fe),Ns(R,R.j+f);break;case 4:l||Ns(R,R.j*b)}if(ww(e,O.a,O.b),r)for(y=new ut((!e.n&&(e.n=new pe(ju,e,1,7)),e.n));y.e!=y.i.gc();)p=u(ft(y),157),S=p.i+p.g/2,A=p.j+p.f/2,be=S/I.a,h=A/I.b,be+h>=1&&(be-h>0&&A>=0?(Os(p,p.i+te),Ns(p,p.j+f*h)):be-h<0&&S>=0&&(Os(p,p.i+te*be),Ns(p,p.j+f)));return ji(e,(Xt(),Tg),(Vs(),c=u(sa(tA),10),new _l(c,u(_f(c,c.length),10),0))),new je(fe,b)}function Qz(e){var n,t,i,r,c,o,l,f,h,b,p;if(e==null)throw $(new sh(Vo));if(h=e,c=e.length,f=!1,c>0&&(n=(Yn(0,e.length),e.charCodeAt(0)),(n==45||n==43)&&(e=(Yn(1,e.length+1),e.substr(1)),--c,f=n==45)),c==0)throw $(new sh(Yw+h+'"'));for(;e.length>0&&(Yn(0,e.length),e.charCodeAt(0)==48);)e=(Yn(1,e.length+1),e.substr(1)),--c;if(c>(aKe(),nnn)[10])throw $(new sh(Yw+h+'"'));for(r=0;r0&&(p=-parseInt((Yr(0,i,e.length),e.substr(0,i)),10),e=(Yn(i,e.length+1),e.substr(i)),c-=i,t=!1);c>=o;){if(i=parseInt((Yr(0,o,e.length),e.substr(0,o)),10),e=(Yn(o,e.length+1),e.substr(o)),c-=o,t)t=!1;else{if(ao(p,l)<0)throw $(new sh(Yw+h+'"'));p=ac(p,b)}p=lf(p,i)}if(ao(p,0)>0)throw $(new sh(Yw+h+'"'));if(!f&&(p=Td(p),ao(p,0)<0))throw $(new sh(Yw+h+'"'));return p}function Cge(e){ZW();var n,t,i,r,c,o,l,f;if(e==null)return null;if(r=lh(e,Xo(37)),r<0)return e;for(f=new il((Yr(0,r,e.length),e.substr(0,r))),n=oe(ds,kv,30,4,15,1),l=0,i=0,o=e.length;rr+2&&WY((Yn(r+1,e.length),e.charCodeAt(r+1)),G8e,q8e)&&WY((Yn(r+2,e.length),e.charCodeAt(r+2)),G8e,q8e))if(t=P3n((Yn(r+1,e.length),e.charCodeAt(r+1)),(Yn(r+2,e.length),e.charCodeAt(r+2))),r+=2,i>0?(t&192)==128?n[l++]=t<<24>>24:i=0:t>=128&&((t&224)==192?(n[l++]=t<<24>>24,i=2):(t&240)==224?(n[l++]=t<<24>>24,i=3):(t&248)==240&&(n[l++]=t<<24>>24,i=4)),i>0){if(l==i){switch(l){case 2:{Fb(f,((n[0]&31)<<6|n[1]&63)&yr);break}case 3:{Fb(f,((n[0]&15)<<12|(n[1]&63)<<6|n[2]&63)&yr);break}}l=0,i=0}}else{for(c=0;c=2){if((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i==0)t=(v0(),r=new yo,r),Et((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),t);else if((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i>1)for(y=new p4((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a));y.e!=y.i.gc();)KE(y);sge(n,u(K((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),0),170))}if(p)for(i=new ut((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a));i.e!=i.i.gc();)for(t=u(ft(i),170),h=new ut((!t.a&&(t.a=new mr(kl,t,5)),t.a));h.e!=h.i.gc();)f=u(ft(h),372),l.a=k.Math.max(l.a,f.a),l.b=k.Math.max(l.b,f.b);for(o=new ut((!e.n&&(e.n=new pe(ju,e,1,7)),e.n));o.e!=o.i.gc();)c=u(ft(o),157),b=u(ye(c,Yx),8),b&&Dl(c,b.a,b.b),p&&(l.a=k.Math.max(l.a,c.i+c.g),l.b=k.Math.max(l.b,c.j+c.f));return l}function _Ve(e,n,t,i,r){var c,o,l;if(ARe(e,n),o=n[0],c=ic(t.c,0),l=-1,j1e(t))if(i>0){if(o+i>e.length)return!1;l=Az((Yr(0,o+i,e.length),e.substr(0,o+i)),n)}else l=Az(e,n);switch(c){case 71:return l=uv(e,o,z(B(ze,1),Se,2,6,[mYe,vYe]),n),r.e=l,!0;case 77:return NDn(e,n,r,l,o);case 76:return DDn(e,n,r,l,o);case 69:return _Tn(e,n,o,r);case 99:return LTn(e,n,o,r);case 97:return l=uv(e,o,z(B(ze,1),Se,2,6,["AM","PM"]),n),r.b=l,!0;case 121:return IDn(e,n,o,l,t,r);case 100:return l<=0?!1:(r.c=l,!0);case 83:return l<0?!1:hEn(l,o,n[0],r);case 104:l==12&&(l=0);case 75:case 72:return l<0?!1:(r.f=l,r.g=!1,!0);case 107:return l<0?!1:(r.f=l,r.g=!0,!0);case 109:return l<0?!1:(r.j=l,!0);case 115:return l<0?!1:(r.n=l,!0);case 90:if(ocn[f]&&(I=f),p=new L(e.a.b);p.a=l){at(q.b>0),q.a.Xb(q.c=--q.b);break}else I.a>f&&(i?(Er(i.b,I.b),i.a=k.Math.max(i.a,I.a),As(q)):(Te(I.b,b),I.c=k.Math.min(I.c,f),I.a=k.Math.max(I.a,l),i=I));i||(i=new axe,i.c=f,i.a=l,d2(q,i),Te(i.b,b))}for(o=e.b,h=0,R=new L(t);R.a1;){if(r=SNn(n),p=c.g,A=u(ye(n,Bx),104),O=ne(re(ye(n,UH))),(!n.a&&(n.a=new pe(Jt,n,10,11)),n.a).i>1&&ne(re(ye(n,(Yh(),Hre))))!=Ki&&(c.c+(A.b+A.c))/(c.b+(A.d+A.a))1&&ne(re(ye(n,(Yh(),Jre))))!=Ki&&(c.c+(A.b+A.c))/(c.b+(A.d+A.a))>O&&ji(r,(Yh(),Mm),k.Math.max(ne(re(ye(n,Rx))),ne(re(ye(r,Mm)))-ne(re(ye(n,Jre))))),S=new Ose(i,b),f=QVe(S,r,y),h=f.g,h>=p&&h==h){for(o=0;o<(!r.a&&(r.a=new pe(Jt,r,10,11)),r.a).i;o++)Sqe(e,u(K((!r.a&&(r.a=new pe(Jt,r,10,11)),r.a),o),26),u(K((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a),o),26));QRe(n,S),p4n(c,f.c),w4n(c,f.b)}--l}ji(n,(Yh(),P7),c.b),ji(n,Py,c.c),t.Ug()}function wRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn;for(n.Tg("Compound graph postprocessor",1),t=Re($e(T(e,(Ne(),Jie)))),l=u(T(e,(me(),qve)),229),b=new ar,R=l.ec().Jc();R.Ob();){for(I=u(R.Pb(),17),o=new bs(l.cc(I)),jn(),Cr(o,new noe(e)),be=g7n((vn(0,o.c.length),u(o.c[0],250))),Ie=UBe(u(Le(o,o.c.length-1),250)),V=be.i,V9(Ie.i,V)?q=V.e:q=_r(V),p=tSn(I,o),qs(I.a),y=null,c=new L(o);c.aEh,tn=k.Math.abs(y.b-A.b)>Eh,(!t&&cn&&tn||t&&(cn||tn))&&Vt(I.a,te)),fc(I.a,i),i.b==0?y=te:y=(at(i.b!=0),u(i.c.b.c,8)),U7n(S,p,O),UBe(r)==Ie&&(_r(Ie.i)!=r.a&&(O=new Kr,L0e(O,_r(Ie.i),q)),he(I,jie,O)),WMn(S,I,q),b.a.yc(S,b);lc(I,be),Gr(I,Ie)}for(h=b.a.ec().Jc();h.Ob();)f=u(h.Pb(),17),lc(f,null),Gr(f,null);n.Ug()}function pRn(e,n){var t,i,r,c,o,l,f,h,b,p,y;for(r=u(T(e,(Mu(),mp)),86),b=r==(vr(),Zc)||r==ru?Za:ru,t=u(gs(si(new wn(null,new pn(e.b,16)),new b3),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),f=u(gs(So(t.Mc(),new LEe(n)),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[Yo]))),16),f.Fc(u(gs(So(t.Mc(),new PEe(n)),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[Yo]))),18)),f.gd(new $Ee(b)),y=new vd(new REe(r)),i=new pt,l=f.Jc();l.Ob();)o=u(l.Pb(),240),h=u(o.a,40),Re($e(o.c))?(y.a.yc(h,(Pn(),eb))==null,new c9(y.a.Xc(h,!1)).a.gc()>0&&ei(i,h,u(new c9(y.a.Xc(h,!1)).a.Tc(),40)),new c9(y.a.$c(h,!0)).a.gc()>1&&ei(i,nJe(y,h),h)):(new c9(y.a.Xc(h,!1)).a.gc()>0&&(c=u(new c9(y.a.Xc(h,!1)).a.Tc(),40),ue(c)===ue(du(Xc(i.f,h)))&&u(T(h,(Ti(),dre)),16).Ec(c)),new c9(y.a.$c(h,!0)).a.gc()>1&&(p=nJe(y,h),ue(du(Xc(i.f,p)))===ue(h)&&u(T(p,(Ti(),dre)),16).Ec(h)),y.a.Ac(h)!=null)}function LVe(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te;if(e.gc()==1)return u(e.Xb(0),235);if(e.gc()<=0)return new YR;for(r=e.Jc();r.Ob();){for(t=u(r.Pb(),235),A=0,b=oi,p=oi,f=Xr,h=Xr,S=new L(t.e);S.al&&(V=0,te+=o+R,o=0),qIn(O,t,V,te),n=k.Math.max(n,V+I.a),o=k.Math.max(o,I.b),V+=I.a+R;return O}function mRn(e){cge();var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;if(e==null||(c=oB(e),A=djn(c),A%4!=0))return null;if(O=A/4|0,O==0)return oe(ds,kv,30,0,15,1);for(p=null,n=0,t=0,i=0,r=0,o=0,l=0,f=0,h=0,S=0,y=0,b=0,p=oe(ds,kv,30,O*3,15,1);S>4)<<24>>24,p[y++]=((t&15)<<4|i>>2&15)<<24>>24,p[y++]=(i<<6|r)<<24>>24}return!ZT(o=c[b++])||!ZT(l=c[b++])?null:(n=rh[o],t=rh[l],f=c[b++],h=c[b++],rh[f]==-1||rh[h]==-1?f==61&&h==61?(t&15)!=0?null:(I=oe(ds,kv,30,S*3+1,15,1),Wu(p,0,I,0,S*3),I[y]=(n<<2|t>>4)<<24>>24,I):f!=61&&h==61?(i=rh[f],(i&3)!=0?null:(I=oe(ds,kv,30,S*3+2,15,1),Wu(p,0,I,0,S*3),I[y++]=(n<<2|t>>4)<<24>>24,I[y]=((t&15)<<4|i>>2&15)<<24>>24,I)):null:(i=rh[f],r=rh[h],p[y++]=(n<<2|t>>4)<<24>>24,p[y++]=((t&15)<<4|i>>2&15)<<24>>24,p[y++]=(i<<6|r)<<24>>24,p))}function vRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be;for(n.Tg(SQe,1),A=u(T(e,(Ne(),Y1)),222),r=new L(e.b);r.a=2){for(O=!0,y=new L(c.j),t=u(_(y),12),S=null;y.a0)if(i=p.gc(),h=sc(k.Math.floor((i+1)/2))-1,r=sc(k.Math.ceil((i+1)/2))-1,n.o==Ya)for(b=r;b>=h;b--)n.a[te.p]==te&&(O=u(p.Xb(b),49),A=u(O.a,9),!rf(t,O.b)&&S>e.b.e[A.p]&&(n.a[A.p]=te,n.g[te.p]=n.g[A.p],n.a[te.p]=n.g[te.p],n.f[n.g[te.p].p]=(Pn(),!!(Re(n.f[n.g[te.p].p])&te.k==(zn(),dr))),S=e.b.e[A.p]));else for(b=h;b<=r;b++)n.a[te.p]==te&&(R=u(p.Xb(b),49),I=u(R.a,9),!rf(t,R.b)&&S0&&(r=u(Le(I.c.a,fe-1),9),o=e.i[r.p],cn=k.Math.ceil(L3(e.n,r,I)),c=be.a.e-I.d.d-(o.a.e+r.o.b+r.d.a)-cn),h=Ki,fe0&&Ie.a.e.e-Ie.a.a-(Ie.b.e.e-Ie.b.a)<0,A=V.a.e.e-V.a.a-(V.b.e.e-V.b.a)<0&&Ie.a.e.e-Ie.a.a-(Ie.b.e.e-Ie.b.a)>0,S=V.a.e.e+V.b.aIe.b.e.e+Ie.a.a,te=0,!O&&!A&&(y?c+p>0?te=p:h-i>0&&(te=i):S&&(c+l>0?te=l:h-q>0&&(te=q))),be.a.e+=te,be.b&&(be.d.e+=te),!1))}function $Ve(e,n,t){var i,r,c,o,l,f,h,b,p,y;if(i=new Lf(n.Jf().a,n.Jf().b,n.Kf().a,n.Kf().b),r=new g4,e.c)for(o=new L(n.Pf());o.a0&&Or(S,(vn(t,n.c.length),u(n.c[t],25))),c=0,y=!0,R=Ks(qb(rr(S))),f=R.Jc();f.Ob();){for(l=u(f.Pb(),17),y=!1,p=l,h=0;h(vn(h,n.c.length),u(n.c[h],25)).a.c.length?Or(r,(vn(h,n.c.length),u(n.c[h],25))):z0(r,i+c,(vn(h,n.c.length),u(n.c[h],25))),p=CW(p,r);t>0&&(c+=1)}if(y){for(h=0;h(vn(h,n.c.length),u(n.c[h],25)).a.c.length?Or(r,(vn(h,n.c.length),u(n.c[h],25))):z0(r,i+c,(vn(h,n.c.length),u(n.c[h],25)));t>0&&(c+=1)}for(o=!1,O=new qn(Vn(Ni(S).a.Jc(),new ee));ht(O);){for(A=u(tt(O),17),p=A,b=t+1;b(vn(h,n.c.length),u(n.c[h],25)).a.c.length?Or(I,(vn(h,n.c.length),u(n.c[h],25))):z0(I,i+1,(vn(h,n.c.length),u(n.c[h],25))));o&&(c+=1),o=!0}return c>0?c-1:0}function q0(e,n){fi();var t,i,r,c,o,l,f,h,b,p,y,S,A;if(xj(K7)==0){for(p=oe(WBn,Se,121,jhn.length,0,1),o=0;oh&&(i.a+=HCe(oe(Wl,kh,30,-h,15,1))),i.a+="Is",lh(f,Xo(32))>=0)for(r=0;r=i.o.b/2}else q=!p;q?(R=u(T(i,(me(),Dy)),16),R?y?c=R:(r=u(T(i,xy),16),r?R.gc()<=r.gc()?c=R:c=r:(c=new Ce,he(i,xy,c))):(c=new Ce,he(i,Dy,c))):(r=u(T(i,(me(),xy)),16),r?p?c=r:(R=u(T(i,Dy),16),R?r.gc()<=R.gc()?c=r:c=R:(c=new Ce,he(i,Dy,c))):(c=new Ce,he(i,xy,c))),c.Ec(e),he(e,(me(),eH),t),n.d==t?(Gr(n,null),t.e.c.length+t.g.c.length==0&&gu(t,null),vkn(t)):(lc(n,null),t.e.c.length+t.g.c.length==0&&gu(t,null)),qs(n.a)}function SRn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn,Dn,ot,Qt,qi;for(t.Tg("MinWidth layering",1),S=n.b,Ie=n.a,qi=u(T(n,(Ne(),n4e)),15).a,l=u(T(n,t4e),15).a,e.b=ne(re(T(n,Vf))),e.d=Ki,te=new L(Ie);te.aS&&(c&&(gc(fe,y),gc(cn,ve(h.b-1))),Qt=t.b,qi+=y+n,y=0,b=k.Math.max(b,t.b+t.c+ot)),Os(l,Qt),Ns(l,qi),b=k.Math.max(b,Qt+ot+t.c),y=k.Math.max(y,p),Qt+=ot+n;if(b=k.Math.max(b,i),Dn=qi+y+t.a,Dn0?(h=0,I&&(h+=l),h+=(tn-1)*o,V&&(h+=l),cn&&V&&(h=k.Math.max(h,GNn(V,o,q,Ie))),h=e.a&&(i=uLn(e,q),b=k.Math.max(b,i.b),te=k.Math.max(te,i.d),Te(l,new jc(q,i)));for(cn=new Ce,h=0;h0),I.a.Xb(I.c=--I.b),tn=new Xu(e.b),d2(I,tn),at(I.b0){for(y=b<100?null:new m0(b),h=new t1e(n),A=h.g,R=oe($t,ni,30,b,15,1),i=0,te=new Nw(b),r=0;r=0;)if(S!=null?di(S,A[f]):ue(S)===ue(A[f])){R.length<=i&&(I=R,R=oe($t,ni,30,2*R.length,15,1),Wu(I,0,R,0,i)),R[i++]=r,Et(te,A[f]);break e}if(S=S,ue(S)===ue(l))break}}if(h=te,A=te.g,b=i,i>R.length&&(I=R,R=oe($t,ni,30,i,15,1),Wu(I,0,R,0,i)),i>0){for(V=!0,c=0;c=0;)V4(e,R[o]);if(i!=b){for(r=b;--r>=i;)V4(h,r);I=R,R=oe($t,ni,30,i,15,1),Wu(I,0,R,0,i)}n=h}}}else for(n=sxn(e,n),r=e.i;--r>=0;)n.Gc(e.g[r])&&(V4(e,r),V=!0);if(V){if(R!=null){for(t=n.gc(),p=t==1?dE(e,4,n.Jc().Pb(),null,R[0],O):dE(e,6,n,R,R[0],O),y=t<100?null:new m0(t),r=n.Jc();r.Ob();)S=r.Pb(),y=qle(e,u(S,75),y);y?(y.lj(p),y.mj()):ai(e.e,p)}else{for(y=p2n(n.gc()),r=n.Jc();r.Ob();)S=r.Pb(),y=qle(e,u(S,75),y);y&&y.mj()}return!0}else return!1}function CRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;for(t=new UJe(n),t.a||t_n(n),h=eIn(n),f=new Tw,I=new iXe,O=new L(n.a);O.a0||t.o==Ya&&r=t}function NRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn;for(V=e.a,te=0,be=V.length;te0?(p=u(Le(y.c.a,o-1),9),cn=L3(e.b,y,p),I=y.n.b-y.d.d-(p.n.b+p.o.b+p.d.a+cn)):I=y.n.b-y.d.d,h=k.Math.min(I,h),o1&&(o=k.Math.min(o,k.Math.abs(u(Yu(l.a,1),8).b-b.b)))));else for(O=new L(n.j);O.ar&&(c=y.a-r,o=oi,i.c.length=0,r=y.a),y.a>=r&&(Hn(i.c,l),l.a.b>1&&(o=k.Math.min(o,k.Math.abs(u(Yu(l.a,l.a.b-2),8).b-y.b)))));if(i.c.length!=0&&c>n.o.a/2&&o>n.o.b/2){for(S=new Qu,gu(S,n),xr(S,(De(),Xn)),S.n.a=n.o.a/2,R=new Qu,gu(R,n),xr(R,bt),R.n.a=n.o.a/2,R.n.b=n.o.b,f=new L(i);f.a=h.b?lc(l,R):lc(l,S)):(h=u(x3n(l.a),8),I=l.a.b==0?_a(l.c):u(If(l.a),8),I.b>=h.b?Gr(l,R):Gr(l,S)),p=u(T(l,(Ne(),Wc)),78),p&&P2(p,h,!0);n.n.a=r-n.o.a/2}}function _Rn(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(l=St(e.b,0);l.b!=l.d.c;)if(o=u(jt(l),40),!bn(o.c,DF))for(h=sOn(o,e),n==(vr(),Zc)||n==ru?Cr(h,new j_):Cr(h,new tU),f=h.c.length,i=0;i=0?S=q4(l):S=CO(q4(l)),e.of(T7,S)),h=new Kr,y=!1,e.nf(wp)?(wle(h,u(e.mf(wp),8)),y=!0):Ywn(h,o.a/2,o.b/2),S.g){case 4:he(b,yu,(Xs(),V1)),he(b,tH,(Wb(),Ov)),b.o.b=o.b,O<0&&(b.o.a=-O),xr(p,(De(),et)),y||(h.a=o.a),h.a-=o.a;break;case 2:he(b,yu,(Xs(),yg)),he(b,tH,(Wb(),k7)),b.o.b=o.b,O<0&&(b.o.a=-O),xr(p,(De(),Kn)),y||(h.a=0);break;case 1:he(b,mg,(_1(),Dv)),b.o.a=o.a,O<0&&(b.o.b=-O),xr(p,(De(),bt)),y||(h.b=o.b),h.b-=o.b;break;case 3:he(b,mg,(_1(),Sy)),b.o.a=o.a,O<0&&(b.o.b=-O),xr(p,(De(),Xn)),y||(h.b=0)}if(wle(p.n,h),he(b,wp,h),n==Cg||n==f1||n==to){if(A=0,n==Cg&&e.nf(qd))switch(S.g){case 1:case 2:A=u(e.mf(qd),15).a;break;case 3:case 4:A=-u(e.mf(qd),15).a}else switch(S.g){case 4:case 2:A=c.b,n==f1&&(A/=r.b);break;case 1:case 3:A=c.a,n==f1&&(A/=r.a)}he(b,hp,A)}return he(b,Du,S),b}function LRn(){zoe();function e(i){var r=this;this.dispatch=function(c){var o=c.data;switch(o.cmd){case"algorithms":var l=fde((jn(),new Hr(new ct(Ng.b))));i.postMessage({id:o.id,data:l});break;case"categories":var f=fde((jn(),new Hr(new ct(Ng.c))));i.postMessage({id:o.id,data:f});break;case"options":var h=fde((jn(),new Hr(new ct(Ng.d))));i.postMessage({id:o.id,data:h});break;case"register":sPn(o.algorithms),i.postMessage({id:o.id});break;case"layout":nPn(o.graph,o.layoutOptions||{},o.options||{}),i.postMessage({id:o.id,data:o.graph});break}},this.saveDispatch=function(c){try{r.dispatch(c)}catch(o){i.postMessage({id:c.data.id,error:o})}}}function n(i){var r=this;this.dispatcher=new e({postMessage:function(c){r.onmessage({data:c})}}),this.postMessage=function(c){setTimeout(function(){r.dispatcher.saveDispatch({data:c})},0)}}if(typeof document===GZ&&typeof self!==GZ){var t=new e(self);self.onmessage=t.saveDispatch}else typeof M!==GZ&&M.exports&&(Object.defineProperty(N,"__esModule",{value:!0}),M.exports={default:n,Worker:n})}function uZ(e,n,t,i,r,c,o){var l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn,Dn,ot,Qt,qi;for(O=0,Tn=0,h=new L(e.b);h.aO&&(c&&(gc(fe,S),gc(cn,ve(b.b-1)),Te(e.d,A),l.c.length=0),Qt=t.b,qi+=S+n,S=0,p=k.Math.max(p,t.b+t.c+ot)),Hn(l.c,f),zJe(f,Qt,qi),p=k.Math.max(p,Qt+ot+t.c),S=k.Math.max(S,y),Qt+=ot+n,A=f;if(Er(e.a,l),Te(e.d,u(Le(l,l.c.length-1),167)),p=k.Math.max(p,i),Dn=qi+S+t.a,Dnr.d.d+r.d.a?b.f.d=!0:(b.f.d=!0,b.f.a=!0))),i.b!=i.d.c&&(n=t);b&&(c=u(Bn(e.f,o.d.i),60),n.bc.d.d+c.d.a?b.f.d=!0:(b.f.d=!0,b.f.a=!0))}for(l=new qn(Vn(rr(S).a.Jc(),new ee));ht(l);)o=u(tt(l),17),o.a.b!=0&&(n=u(If(o.a),8),o.d.j==(De(),Xn)&&(I=new sS(n,new je(n.a,r.d.d),r,o),I.f.a=!0,I.a=o.d,Hn(O.c,I)),o.d.j==bt&&(I=new sS(n,new je(n.a,r.d.d+r.d.a),r,o),I.f.d=!0,I.a=o.d,Hn(O.c,I)))}return O}function FRn(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(f=new Ce,p=n.length,o=d1e(t),h=0;h=A&&(q>A&&(S.c.length=0,A=q),Hn(S.c,o));S.c.length!=0&&(y=u(Le(S,sz(n,S.c.length)),132),Dn.a.Ac(y)!=null,y.s=O++,mbe(y,tn,fe),S.c.length=0)}for(te=e.c.length+1,l=new L(e);l.aTn.s&&(As(t),qo(Tn.i,i),i.c>0&&(i.a=Tn,Te(Tn.t,i),i.b=Ie,Te(Ie.i,i)))}function HVe(e,n,t,i,r){var c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn,Dn;for(O=new xo(n.b),te=new xo(n.b),y=new xo(n.b),cn=new xo(n.b),I=new xo(n.b),Ie=St(n,0);Ie.b!=Ie.d.c;)for(be=u(jt(Ie),12),l=new L(be.g);l.a0,R=be.g.c.length>0,h&&R?Hn(y.c,be):h?Hn(O.c,be):R&&Hn(te.c,be);for(A=new L(O);A.aq.mh()-h.b&&(y=q.mh()-h.b),S>q.nh()-h.d&&(S=q.nh()-h.d),b0){for(V=St(e.f,0);V.b!=V.d.c;)q=u(jt(V),9),q.p+=y-e.e;_0e(e),qs(e.f),Ibe(e,i,S)}else{for(Vt(e.f,S),S.p=i,e.e=k.Math.max(e.e,i),c=new qn(Vn(rr(S).a.Jc(),new ee));ht(c);)r=u(tt(c),17),!r.c.i.c&&r.c.i.k==(zn(),Uu)&&(Vt(e.f,r.c.i),r.c.i.p=i-1);e.c=i}else _0e(e),qs(e.f),i=0,ht(new qn(Vn(rr(S).a.Jc(),new ee)))?(y=0,y=GJe(y,S),i=y+2,Ibe(e,i,S)):(Vt(e.f,S),S.p=0,e.e=k.Math.max(e.e,0),e.b=u(Le(e.d.b,0),25),e.c=0);for(e.f.b==0||_0e(e),e.d.a.c.length=0,R=new Ce,h=new L(e.d.b);h.a=48&&n<=57){for(i=n-48;r=48&&n<=57;)if(i=i*10+n-48,i<0)throw $(new zt(Ht((Lt(),Z2e))))}else throw $(new zt(Ht((Lt(),DZe))));if(t=i,n==44){if(r>=e.j)throw $(new zt(Ht((Lt(),_Ze))));if((n=ic(e.i,r++))>=48&&n<=57){for(t=n-48;r=48&&n<=57;)if(t=t*10+n-48,t<0)throw $(new zt(Ht((Lt(),Z2e))));if(i>t)throw $(new zt(Ht((Lt(),LZe))))}else t=-1}if(n!=125)throw $(new zt(Ht((Lt(),IZe))));e._l(r)?(c=(fi(),fi(),new A2(9,c)),e.d=r+1):(c=(fi(),fi(),new A2(3,c)),e.d=r),c.Mm(i),c.Lm(t),li(e)}}return c}function XRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be;for(r=1,S=new Ce,i=0;i=u(Le(e.b,i),25).a.c.length/4)continue}if(u(Le(e.b,i),25).a.c.length>n){for(te=new Ce,Te(te,u(Le(e.b,i),25)),o=0;o1)for(A=new p4((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a));A.e!=A.i.gc();)KE(A);for(o=u(K((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),0),170),I=Qt,Qt>be+te?I=be+te:Qtfe+O?R=fe+O:qibe-te&&Ife-O&&RQt+ot?cn=Qt+ot:beqi+Ie?tn=qi+Ie:feQt-ot&&cnqi-Ie&&tnt&&(y=t-1),S=i0+Is(n,24)*vN*p-p/2,S<0?S=1:S>i&&(S=i-1),r=(v0(),f=new Fk,f),bB(r,y),gB(r,S),Et((!o.a&&(o.a=new mr(kl,o,5)),o.a),r)}function lZ(e,n){XW();var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie;if(V=e.e,b=e.d,r=e.a,V==0)switch(n){case 0:return"0";case 1:return R8;case 2:return"0.00";case 3:return"0.000";case 4:return"0.0000";case 5:return"0.00000";case 6:return"0.000000";default:return R=new p0,R.a+="0E",R.a+=-n,R.a}if(O=b*10+1+7,I=oe(Wl,kh,30,O+1,15,1),t=O,b==1)if(c=r[0],c<0){Ie=Rr(c,Ic);do p=Ie,Ie=BO(Ie,10),I[--t]=48+Rt(lf(p,ac(Ie,10)))&yr;while(ao(Ie,0)!=0)}else{Ie=c;do p=Ie,Ie=Ie/10|0,I[--t]=48+(p-Ie*10)&yr;while(Ie!=0)}else{te=oe($t,ni,30,b,15,1),fe=b,Wu(r,0,te,0,fe);e:for(;;){for(q=0,l=fe-1;l>=0;l--)be=mc(Gh(q,32),Rr(te[l],Ic)),S=ZAn(be),te[l]=Rt(S),q=Rt(kw(S,32));A=Rt(q),y=t;do I[--t]=48+A%10&yr;while((A=A/10|0)!=0&&t!=0);for(i=9-y+t,o=0;o0;o++)I[--t]=48;for(f=fe-1;te[f]==0;f--)if(f==0)break e;fe=f+1}for(;I[t]==48;)++t}return h=V<0,h&&(I[--t]=45),gh(I,t,O-t)}function XVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;switch(e.c=n,e.g=new pt,t=(_b(),new w0(e.c)),i=new TP(t),ode(i),V=Pt(ye(e.c,(FO(),q6e))),f=u(ye(e.c,rce),330),be=u(ye(e.c,cce),427),o=u(ye(e.c,J6e),477),te=u(ye(e.c,ice),428),e.j=ne(re(ye(e.c,Gln))),l=e.a,f.g){case 0:l=e.a;break;case 1:l=e.b;break;case 2:l=e.i;break;case 3:l=e.e;break;case 4:l=e.f;break;default:throw $(new Gn($F+(f.f!=null?f.f:""+f.g)))}if(e.d=new O_e(l,be,o),he(e.d,(Q9(),QS),$e(ye(e.c,Jln))),e.d.c=Re($e(ye(e.c,H6e))),TR(e.c).i==0)return e.d;for(p=new ut(TR(e.c));p.e!=p.i.gc();){for(b=u(ft(p),26),S=b.g/2,y=b.f/2,fe=new je(b.i+S,b.j+y);so(e.g,fe);)a2(fe,(k.Math.random()-.5)*Eh,(k.Math.random()-.5)*Eh);O=u(ye(b,(Xt(),R7)),140),I=new U_e(fe,new Lf(fe.a-S-e.j/2-O.b,fe.b-y-e.j/2-O.d,b.g+e.j+(O.b+O.c),b.f+e.j+(O.d+O.a))),Te(e.d.i,I),ei(e.g,fe,new jc(I,b))}switch(te.g){case 0:if(V==null)e.d.d=u(Le(e.d.i,0),68);else for(q=new L(e.d.i);q.a0?ot+1:1);for(o=new L(fe.g);o.a0?ot+1:1)}e.d[h]==0?Vt(e.f,O):e.a[h]==0&&Vt(e.g,O),++h}for(A=-1,S=1,p=new Ce,e.e=u(T(n,(me(),Oy)),234);jl>0;){for(;e.f.b!=0;)qi=u(eV(e.f),9),e.c[qi.p]=A--,Ybe(e,qi),--jl;for(;e.g.b!=0;)Es=u(eV(e.g),9),e.c[Es.p]=S++,Ybe(e,Es),--jl;if(jl>0){for(y=Xr,q=new L(V);q.a=y&&(te>y&&(p.c.length=0,y=te),Hn(p.c,O)));b=e.qg(p),e.c[b.p]=S++,Ybe(e,b),--jl}}for(Qt=V.c.length+1,h=0;he.c[eu]&&($d(i,!0),he(n,Ay,(Pn(),!0)));e.a=null,e.d=null,e.c=null,qs(e.g),qs(e.f),t.Ug()}function VVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;for(be=u(K((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),0),170),b=new xs,te=new pt,fe=sKe(be),Ko(te.f,be,fe),y=new pt,i=new Si,A=qh(Rl(z(B(Xl,1),On,20,0,[(!n.d&&(n.d=new Nn(pr,n,8,5)),n.d),(!n.e&&(n.e=new Nn(pr,n,7,4)),n.e)])));ht(A);){if(S=u(tt(A),85),(!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i!=1)throw $(new Gn(zWe+(!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i));S!=e&&(I=u(K((!S.a&&(S.a=new pe(Pi,S,6,6)),S.a),0),170),Xi(i,I,i.c.b,i.c),O=u(du(Xc(te.f,I)),13),O||(O=sKe(I),Ko(te.f,I,O)),p=t?Nr(new wc(u(Le(fe,fe.c.length-1),8)),u(Le(O,O.c.length-1),8)):Nr(new wc((vn(0,fe.c.length),u(fe.c[0],8))),(vn(0,O.c.length),u(O.c[0],8))),Ko(y.f,I,p))}if(i.b!=0)for(R=u(Le(fe,t?fe.c.length-1:0),8),h=1;h1&&Xi(b,R,b.c.b,b.c),CY(r)));R=q}return b}function YVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn;for(t.Tg(QQe,1),Tn=u(gs(si(new wn(null,new pn(n,16)),new S_),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),b=u(gs(si(new wn(null,new pn(n,16)),new zEe(n)),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[Yo]))),16),A=u(gs(si(new wn(null,new pn(n,16)),new BEe(n)),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[Yo]))),16),O=oe(_H,IF,40,n.gc(),0,1),o=0;o=0&&tn=0&&!O[S]){O[S]=r,b.ed(l),--l;break}if(S=tn-y,S=0&&!O[S]){O[S]=r,b.ed(l),--l;break}}for(A.gd(new TM),f=O.length-1;f>=0;f--)!O[f]&&!A.dc()&&(O[f]=u(A.Xb(0),40),A.ed(0));for(h=0;hy&&$O((vn(y,n.c.length),u(n.c[y],186)),b),b=null;n.c.length>y&&(vn(y,n.c.length),u(n.c[y],186)).a.c.length==0;)qo(n,(vn(y,n.c.length),n.c[y]));if(!b){--o;continue}if(!Re($e(u(Le(b.b,0),26).mf((Ja(),AD))))&&g_n(n,A,c,b,I,t,y,i)){O=!0;continue}if(I){if(S=A.b,p=b.f,!Re($e(u(Le(b.b,0),26).mf(AD)))&&PPn(n,A,c,b,t,y,i,r)){if(O=!0,S=e.j){e.a=-1,e.c=1;return}if(n=ic(e.i,e.d++),e.a=n,e.b==1){switch(n){case 92:if(i=10,e.d>=e.j)throw $(new zt(Ht((Lt(),qF))));e.a=ic(e.i,e.d++);break;case 45:(e.e&512)==512&&e.d=e.j||ic(e.i,e.d)!=63)break;if(++e.d>=e.j)throw $(new zt(Ht((Lt(),One))));switch(n=ic(e.i,e.d++),n){case 58:i=13;break;case 61:i=14;break;case 33:i=15;break;case 91:i=19;break;case 62:i=18;break;case 60:if(e.d>=e.j)throw $(new zt(Ht((Lt(),One))));if(n=ic(e.i,e.d++),n==61)i=16;else if(n==33)i=17;else throw $(new zt(Ht((Lt(),bZe))));break;case 35:for(;e.d=e.j)throw $(new zt(Ht((Lt(),qF))));e.a=ic(e.i,e.d++);break;default:i=0}e.c=i}function iBn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I;if(t.Tg("Process compaction",1),!!Re($e(T(n,(Mu(),Sye))))){for(r=u(T(n,mp),86),S=ne(re(T(n,kre))),TLn(e,n,r),pRn(n,S/2/2),A=n.b,Vb(A,new DEe(r)),h=St(A,0);h.b!=h.d.c;)if(f=u(jt(h),40),!Re($e(T(f,(Ti(),fb))))){if(i=nIn(f,r),O=Y_n(f,n),p=0,y=0,i)switch(I=i.e,r.g){case 2:p=I.a-S-f.f.a,O.e.a-S-f.f.ap&&(p=O.e.a+O.f.a+S),y=p+f.f.a;break;case 4:p=I.b-S-f.f.b,O.e.b-S-f.f.bp&&(p=O.e.b+O.f.b+S),y=p+f.f.b}else if(O)switch(r.g){case 2:p=O.e.a-S-f.f.a,y=p+f.f.a;break;case 1:p=O.e.a+O.f.a+S,y=p+f.f.a;break;case 4:p=O.e.b-S-f.f.b,y=p+f.f.b;break;case 3:p=O.e.b+O.f.b+S,y=p+f.f.b}ue(T(n,yre))===ue((NE(),yD))?(c=p,o=y,l=R1(si(new wn(null,new pn(e.a,16)),new bTe(c,o))),l.a!=null?r==(vr(),Zc)||r==ru?f.e.a=p:f.e.b=p:(r==(vr(),Zc)||r==Vl?l=R1(si(eBe(new wn(null,new pn(e.a,16))),new IEe(c))):l=R1(si(eBe(new wn(null,new pn(e.a,16))),new _Ee(c))),l.a!=null&&(r==Zc||r==ru?f.e.a=ne(re((at(l.a!=null),u(l.a,49)).a)):f.e.b=ne(re((at(l.a!=null),u(l.a,49)).a)))),l.a!=null&&(b=wu(e.a,(at(l.a!=null),l.a),0),b>0&&b!=u(T(f,Nh),15).a&&(he(f,wye,(Pn(),!0)),he(f,Nh,ve(b))))):r==(vr(),Zc)||r==ru?f.e.a=p:f.e.b=p}t.Ug()}}function rBn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be;if(t.Tg("Coffman-Graham Layering",1),n.a.c.length==0){t.Ug();return}for(be=u(T(n,(Ne(),e4e)),15).a,f=0,o=0,y=new L(n.a);y.a=be||!vEn(R,i))&&(i=$Ie(n,b)),Or(R,i),c=new qn(Vn(rr(R).a.Jc(),new ee));ht(c);)r=u(tt(c),17),!e.a[r.p]&&(O=r.c.i,--e.e[O.p],e.e[O.p]==0&&E4(v8(S,O),B8));for(h=b.c.length-1;h>=0;--h)Te(n.b,(vn(h,b.c.length),u(b.c[h],25)));n.a.c.length=0,t.Ug()}function WVe(e){var n,t,i,r,c,o,l,f,h;for(e.b=1,li(e),n=null,e.c==0&&e.a==94?(li(e),n=(fi(),fi(),new ul(4)),ho(n,0,l7),l=new ul(4)):l=(fi(),fi(),new ul(4)),r=!0;(h=e.c)!=1;){if(h==0&&e.a==93&&!r){n&&(dS(n,l),l=n);break}if(t=e.a,i=!1,h==10)switch(t){case 100:case 68:case 119:case 87:case 115:case 83:V2(l,T8(t)),i=!0;break;case 105:case 73:case 99:case 67:t=(V2(l,T8(t)),-1),t<0&&(i=!0);break;case 112:case 80:if(f=Q0e(e,t),!f)throw $(new zt(Ht((Lt(),Nne))));V2(l,f),i=!0;break;default:t=Lbe(e)}else if(h==24&&!r){if(n&&(dS(n,l),l=n),c=WVe(e),dS(l,c),e.c!=0||e.a!=93)throw $(new zt(Ht((Lt(),SZe))));break}if(li(e),!i){if(h==0){if(t==91)throw $(new zt(Ht((Lt(),Q2e))));if(t==93)throw $(new zt(Ht((Lt(),W2e))));if(t==45&&!r&&e.a!=93)throw $(new zt(Ht((Lt(),Dne))))}if(e.c!=0||e.a!=45||t==45&&r)ho(l,t,t);else{if(li(e),(h=e.c)==1)throw $(new zt(Ht((Lt(),UF))));if(h==0&&e.a==93)ho(l,t,t),ho(l,45,45);else{if(h==0&&e.a==93||h==24)throw $(new zt(Ht((Lt(),Dne))));if(o=e.a,h==0){if(o==91)throw $(new zt(Ht((Lt(),Q2e))));if(o==93)throw $(new zt(Ht((Lt(),W2e))));if(o==45)throw $(new zt(Ht((Lt(),Dne))))}else h==10&&(o=Lbe(e));if(li(e),t>o)throw $(new zt(Ht((Lt(),MZe))));ho(l,t,o)}}}r=!1}if(e.c==1)throw $(new zt(Ht((Lt(),UF))));return ov(l),aS(l),e.b=0,li(e),l}function ZVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te;te=!1;do for(te=!1,c=n?new nt(e.a.b).a.gc()-2:1;n?c>=0:cu(T(I,Ci),15).a)&&(V=!1);if(V){for(f=n?c+1:c-1,l=Pae(e.a,ve(f)),o=!1,q=!0,i=!1,b=St(l,0);b.b!=b.d.c;)h=u(jt(b),9),bi(h,Ci)?h.p!=p.p&&(o=o|(n?u(T(h,Ci),15).au(T(p,Ci),15).a),q=!1):!o&&q&&h.k==(zn(),Uu)&&(i=!0,n?y=u(tt(new qn(Vn(rr(h).a.Jc(),new ee))),17).c.i:y=u(tt(new qn(Vn(Ni(h).a.Jc(),new ee))),17).d.i,y==p&&(n?t=u(tt(new qn(Vn(Ni(h).a.Jc(),new ee))),17).d.i:t=u(tt(new qn(Vn(rr(h).a.Jc(),new ee))),17).c.i,(n?u(f2(e.a,t),15).a-u(f2(e.a,y),15).a:u(f2(e.a,y),15).a-u(f2(e.a,t),15).a)<=2&&(q=!1)));if(i&&q&&(n?t=u(tt(new qn(Vn(Ni(p).a.Jc(),new ee))),17).d.i:t=u(tt(new qn(Vn(rr(p).a.Jc(),new ee))),17).c.i,(n?u(f2(e.a,t),15).a-u(f2(e.a,p),15).a:u(f2(e.a,p),15).a-u(f2(e.a,t),15).a)<=2&&t.k==(zn(),Qi)&&(q=!1)),o||q){for(O=_Ue(e,p,n);O.a.gc()!=0;)A=u(O.a.ec().Jc().Pb(),9),O.a.Ac(A)!=null,fc(O,_Ue(e,A,n));--S,te=!0}}}while(te)}function cBn(e){Tt(e.c,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#decimal"])),Tt(e.d,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#integer"])),Tt(e.e,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#boolean"])),Tt(e.f,Ut,z(B(ze,1),Se,2,6,[oc,"EBoolean",ui,"EBoolean:Object"])),Tt(e.i,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#byte"])),Tt(e.g,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#hexBinary"])),Tt(e.j,Ut,z(B(ze,1),Se,2,6,[oc,"EByte",ui,"EByte:Object"])),Tt(e.n,Ut,z(B(ze,1),Se,2,6,[oc,"EChar",ui,"EChar:Object"])),Tt(e.t,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#double"])),Tt(e.u,Ut,z(B(ze,1),Se,2,6,[oc,"EDouble",ui,"EDouble:Object"])),Tt(e.F,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#float"])),Tt(e.G,Ut,z(B(ze,1),Se,2,6,[oc,"EFloat",ui,"EFloat:Object"])),Tt(e.I,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#int"])),Tt(e.J,Ut,z(B(ze,1),Se,2,6,[oc,"EInt",ui,"EInt:Object"])),Tt(e.N,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#long"])),Tt(e.O,Ut,z(B(ze,1),Se,2,6,[oc,"ELong",ui,"ELong:Object"])),Tt(e.Z,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#short"])),Tt(e.$,Ut,z(B(ze,1),Se,2,6,[oc,"EShort",ui,"EShort:Object"])),Tt(e._,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#string"]))}function Ne(){Ne=Y,Bie=(Xt(),Ifn),w4e=_fn,dD=Lfn,Vf=Pfn,Bv=q9e,Sg=U9e,Em=X9e,O7=K9e,N7=V9e,zie=iG,xg=Vd,Fie=$fn,yx=W9e,yH=Jy,hD=(Ige(),Jcn),jm=Hcn,ub=Gcn,Sm=qcn,Nun=new Vr(PD,ve(0)),C7=Bcn,g4e=zcn,Ly=Fcn,x4e=dun,m4e=Kcn,v4e=Qcn,Hie=run,y4e=eun,k4e=tun,kH=pun,Gie=bun,E4e=lun,j4e=oun,S4e=aun,r4e=kcn,Lie=pcn,gH=wcn,Pie=vcn,gp=Icn,vx=_cn,Iie=qrn,X5e=Xrn,Pun=z7,$un=rG,Lun=Im,_un=B7,p4e=(G4(),Pm),new Vr(Hy,p4e),f4e=new pw(12),l4e=new Vr(o1,f4e),G5e=(z1(),H7),Y1=new Vr(j9e,G5e),vm=new Vr(Ps,0),Dun=new Vr(Ece,ve(1)),uH=new Vr($7,H8),Eg=tG,Wi=Kx,T7=Qv,Sun=DD,Ch=yfn,wm=Xv,Iun=new Vr(Sce,(Pn(),!0)),pm=ID,kg=gce,jg=Tg,vH=ab,Rie=Om,H5e=(vr(),eh),pl=new Vr(Mg,H5e),bp=Vv,pH=O9e,ym=Nm,Oun=jce,d4e=H9e,h4e=(nv(),FD),new Vr(R9e,h4e),Mun=mce,Tun=vce,Cun=yce,Aun=pce,Jie=Xcn,bH=gcn,aD=bcn,kx=Ucn,yu=ocn,_y=$rn,wx=Prn,A7=krn,z5e=jrn,Oie=Arn,fD=Ern,Nie=_rn,c4e=jcn,u4e=Ecn,Z5e=ncn,mH=$cn,$ie=Acn,_ie=Yrn,s4e=Ncn,U5e=Hrn,Die=Grn,Cie=ND,o4e=Scn,sH=irn,P5e=trn,oH=nrn,Y5e=Zrn,V5e=Wrn,Q5e=ecn,M7=Yv,Wc=Kv,Gd=Sfn,Oh=bce,Rv=dce,F5e=Trn,qd=kce,hx=Efn,dH=Afn,wp=z9e,a4e=Cfn,mm=Ofn,n4e=lcn,t4e=acn,km=Fy,xie=ern,i4e=dcn,hH=zrn,aH=Brn,wH=R7,e4e=rcn,mx=Tcn,bD=Y9e,J5e=Rrn,b4e=Rcn,q5e=Frn,kun=Orn,jun=Nrn,xun=ucn,Eun=Drn,W5e=wce,px=scn,fH=Irn,u1=yrn,Mie=prn,lD=crn,Aie=urn,lH=mrn,dx=rrn,Tie=vrn,gm=wrn,gx=grn,yun=brn,Iy=orn,bx=drn,B5e=hrn,$5e=srn,R5e=frn,K5e=Qrn}function uBn(e,n,t,i,r,c,o){var l,f,h,b,p,y,S,A;return y=u(i.a,15).a,S=u(i.b,15).a,p=e.b,A=e.c,l=0,b=0,n==(vr(),Zc)||n==ru?(b=cC(_Fe(k2(So(new wn(null,new pn(t.b,16)),new C_),new xM))),p.e.b+p.f.b/2>b?(h=++S,l=ne(re(Js(p2(So(new wn(null,new pn(t.b,16)),new pTe(r,h)),new iw))))):(f=++y,l=ne(re(Js(x4(So(new wn(null,new pn(t.b,16)),new mTe(r,f)),new Dk)))))):(b=cC(_Fe(k2(So(new wn(null,new pn(t.b,16)),new k_),new I6))),p.e.a+p.f.a/2>b?(h=++S,l=ne(re(Js(p2(So(new wn(null,new pn(t.b,16)),new wTe(r,h)),new AM))))):(f=++y,l=ne(re(Js(x4(So(new wn(null,new pn(t.b,16)),new gTe(r,f)),new MM)))))),n==Zc?(gc(e.a,new je(ne(re(T(p,(Ti(),Ea))))-r,l)),gc(e.a,new je(A.e.a+A.f.a+r+c,l)),gc(e.a,new je(A.e.a+A.f.a+r+c,A.e.b+A.f.b/2)),gc(e.a,new je(A.e.a+A.f.a,A.e.b+A.f.b/2))):n==ru?(gc(e.a,new je(ne(re(T(p,(Ti(),Yf))))+r,p.e.b+p.f.b/2)),gc(e.a,new je(p.e.a+p.f.a+r,l)),gc(e.a,new je(A.e.a-r-c,l)),gc(e.a,new je(A.e.a-r-c,A.e.b+A.f.b/2)),gc(e.a,new je(A.e.a,A.e.b+A.f.b/2))):n==Vl?(gc(e.a,new je(l,ne(re(T(p,(Ti(),Ea))))-r)),gc(e.a,new je(l,A.e.b+A.f.b+r+c)),gc(e.a,new je(A.e.a+A.f.a/2,A.e.b+A.f.b+r+c)),gc(e.a,new je(A.e.a+A.f.a/2,A.e.b+A.f.b+r))):(e.a.b==0||(u(If(e.a),8).b=ne(re(T(p,(Ti(),Yf))))+r*u(o.b,15).a),gc(e.a,new je(l,ne(re(T(p,(Ti(),Yf))))+r*u(o.b,15).a)),gc(e.a,new je(l,A.e.b-r*u(o.a,15).a-c))),new jc(ve(y),ve(S))}function oBn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S;if(o=!0,p=null,i=null,r=null,n=!1,S=Pan,h=null,c=null,l=0,f=NQ(e,l,U8e,X8e),f=0&&bn(e.substr(l,2),"//")?(l+=2,f=NQ(e,l,uA,oA),i=(Yr(l,f,e.length),e.substr(l,f-l)),l=f):p!=null&&(l==e.length||(Yn(l,e.length),e.charCodeAt(l)!=47))&&(o=!1,f=rle(e,Xo(35),l),f==-1&&(f=e.length),i=(Yr(l,f,e.length),e.substr(l,f-l)),l=f);if(!t&&l0&&ic(b,b.length-1)==58&&(r=b,l=f)),lo?(Ys(e,n,t),1):(Ys(e,t,n),-1)}for(q=e.f,V=0,te=q.length;V0?Ys(e,n,t):Ys(e,t,n),i;if(!bi(n,(me(),Ci))||!bi(t,Ci))return c=rW(e,n),l=rW(e,t),c>l?(Ys(e,n,t),1):(Ys(e,t,n),-1)}if(!y&&!A&&(i=nYe(e,n,t),i!=0))return i>0?Ys(e,n,t):Ys(e,t,n),i}return bi(n,(me(),Ci))&&bi(t,Ci)?(c=qw(n,t,e.c,u(T(e.c,cb),15).a),l=qw(t,n,e.c,u(T(e.c,cb),15).a),c>l?(Ys(e,n,t),1):(Ys(e,t,n),-1)):(Ys(e,t,n),-1)}function eYe(){eYe=Y,sZ(),Wt=new Tw,gn(Wt,(De(),na),th),gn(Wt,mf,th),gn(Wt,ks,th),gn(Wt,ta,th),gn(Wt,es,th),gn(Wt,js,th),gn(Wt,ta,na),gn(Wt,th,Yl),gn(Wt,na,Yl),gn(Wt,mf,Yl),gn(Wt,ks,Yl),gn(Wt,Zo,Yl),gn(Wt,ta,Yl),gn(Wt,es,Yl),gn(Wt,js,Yl),gn(Wt,zo,Yl),gn(Wt,th,vl),gn(Wt,na,vl),gn(Wt,Yl,vl),gn(Wt,mf,vl),gn(Wt,ks,vl),gn(Wt,Zo,vl),gn(Wt,ta,vl),gn(Wt,zo,vl),gn(Wt,yl,vl),gn(Wt,es,vl),gn(Wt,hs,vl),gn(Wt,js,vl),gn(Wt,na,mf),gn(Wt,ks,mf),gn(Wt,ta,mf),gn(Wt,js,mf),gn(Wt,na,ks),gn(Wt,mf,ks),gn(Wt,ta,ks),gn(Wt,ks,ks),gn(Wt,es,ks),gn(Wt,th,Ql),gn(Wt,na,Ql),gn(Wt,Yl,Ql),gn(Wt,vl,Ql),gn(Wt,mf,Ql),gn(Wt,ks,Ql),gn(Wt,Zo,Ql),gn(Wt,ta,Ql),gn(Wt,yl,Ql),gn(Wt,zo,Ql),gn(Wt,js,Ql),gn(Wt,es,Ql),gn(Wt,mo,Ql),gn(Wt,th,yl),gn(Wt,na,yl),gn(Wt,Yl,yl),gn(Wt,mf,yl),gn(Wt,ks,yl),gn(Wt,Zo,yl),gn(Wt,ta,yl),gn(Wt,zo,yl),gn(Wt,js,yl),gn(Wt,hs,yl),gn(Wt,mo,yl),gn(Wt,na,zo),gn(Wt,mf,zo),gn(Wt,ks,zo),gn(Wt,ta,zo),gn(Wt,yl,zo),gn(Wt,js,zo),gn(Wt,es,zo),gn(Wt,th,Wo),gn(Wt,na,Wo),gn(Wt,Yl,Wo),gn(Wt,mf,Wo),gn(Wt,ks,Wo),gn(Wt,Zo,Wo),gn(Wt,ta,Wo),gn(Wt,zo,Wo),gn(Wt,js,Wo),gn(Wt,na,es),gn(Wt,Yl,es),gn(Wt,vl,es),gn(Wt,ks,es),gn(Wt,th,hs),gn(Wt,na,hs),gn(Wt,vl,hs),gn(Wt,mf,hs),gn(Wt,ks,hs),gn(Wt,Zo,hs),gn(Wt,ta,hs),gn(Wt,ta,mo),gn(Wt,ks,mo),gn(Wt,zo,th),gn(Wt,zo,mf),gn(Wt,zo,Yl),gn(Wt,Zo,th),gn(Wt,Zo,na),gn(Wt,Zo,vl)}function sBn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;switch(t.Tg("Brandes & Koepf node placement",1),e.a=n,e.c=F_n(n),i=u(T(n,(Ne(),$ie)),282),S=Re($e(T(n,mx))),e.d=i==(zO(),YJ)&&!S||i==sie,_Pn(e,n),be=null,fe=null,R=null,q=null,I=(ll(4,Q2),new xo(4)),u(T(n,$ie),282).g){case 3:R=new lv(n,e.c.d,(Da(),Ag),(ah(),Ud)),Hn(I.c,R);break;case 1:q=new lv(n,e.c.d,(Da(),Ya),(ah(),Ud)),Hn(I.c,q);break;case 4:be=new lv(n,e.c.d,(Da(),Ag),(ah(),pp)),Hn(I.c,be);break;case 2:fe=new lv(n,e.c.d,(Da(),Ya),(ah(),pp)),Hn(I.c,fe);break;default:R=new lv(n,e.c.d,(Da(),Ag),(ah(),Ud)),q=new lv(n,e.c.d,Ya,Ud),be=new lv(n,e.c.d,Ag,pp),fe=new lv(n,e.c.d,Ya,pp),Hn(I.c,be),Hn(I.c,fe),Hn(I.c,R),Hn(I.c,q)}for(r=new lTe(n,e.c),l=new L(I);l.axW(c))&&(p=c);for(!p&&(p=(vn(0,I.c.length),u(I.c[0],185))),O=new L(n.b);O.a0?(Ys(e,t,n),1):(Ys(e,n,t),-1);if(b&&V)return Ys(e,t,n),1;if(p&&q)return Ys(e,n,t),-1;if(p&&V)return 0}else for(tn=new L(h.j);tn.ap&&(Dn=0,ot+=b+Ie,b=0),XXe(be,o,Dn,ot),n=k.Math.max(n,Dn+fe.a),b=k.Math.max(b,fe.b),Dn+=fe.a+Ie;for(te=new pt,t=new pt,tn=new L(e);tn.a=-1900?1:0,t>=4?Kt(e,z(B(ze,1),Se,2,6,[mYe,vYe])[l]):Kt(e,z(B(ze,1),Se,2,6,["BC","AD"])[l]);break;case 121:VEn(e,t,i);break;case 77:GIn(e,t,i);break;case 107:f=r.q.getHours(),f==0?Kh(e,24,t):Kh(e,f,t);break;case 83:uNn(e,t,r);break;case 69:b=i.q.getDay(),t==5?Kt(e,z(B(ze,1),Se,2,6,["S","M","T","W","T","F","S"])[b]):t==4?Kt(e,z(B(ze,1),Se,2,6,[CZ,OZ,NZ,DZ,IZ,_Z,LZ])[b]):Kt(e,z(B(ze,1),Se,2,6,["Sun","Mon","Tue","Wed","Thu","Fri","Sat"])[b]);break;case 97:r.q.getHours()>=12&&r.q.getHours()<24?Kt(e,z(B(ze,1),Se,2,6,["AM","PM"])[1]):Kt(e,z(B(ze,1),Se,2,6,["AM","PM"])[0]);break;case 104:p=r.q.getHours()%12,p==0?Kh(e,12,t):Kh(e,p,t);break;case 75:y=r.q.getHours()%12,Kh(e,y,t);break;case 72:S=r.q.getHours(),Kh(e,S,t);break;case 99:A=i.q.getDay(),t==5?Kt(e,z(B(ze,1),Se,2,6,["S","M","T","W","T","F","S"])[A]):t==4?Kt(e,z(B(ze,1),Se,2,6,[CZ,OZ,NZ,DZ,IZ,_Z,LZ])[A]):t==3?Kt(e,z(B(ze,1),Se,2,6,["Sun","Mon","Tue","Wed","Thu","Fri","Sat"])[A]):Kh(e,A,1);break;case 76:O=i.q.getMonth(),t==5?Kt(e,z(B(ze,1),Se,2,6,["J","F","M","A","M","J","J","A","S","O","N","D"])[O]):t==4?Kt(e,z(B(ze,1),Se,2,6,[mZ,vZ,yZ,kZ,uy,jZ,EZ,SZ,xZ,AZ,MZ,TZ])[O]):t==3?Kt(e,z(B(ze,1),Se,2,6,["Jan","Feb","Mar","Apr",uy,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"])[O]):Kh(e,O+1,t);break;case 81:I=i.q.getMonth()/3|0,t<4?Kt(e,z(B(ze,1),Se,2,6,["Q1","Q2","Q3","Q4"])[I]):Kt(e,z(B(ze,1),Se,2,6,["1st quarter","2nd quarter","3rd quarter","4th quarter"])[I]);break;case 100:R=i.q.getDate(),Kh(e,R,t);break;case 109:h=r.q.getMinutes(),Kh(e,h,t);break;case 115:o=r.q.getSeconds(),Kh(e,o,t);break;case 122:t<4?Kt(e,c.c[0]):Kt(e,c.c[1]);break;case 118:Kt(e,c.b);break;case 90:t<3?Kt(e,lCn(c)):t==3?Kt(e,dCn(c)):Kt(e,bCn(c.a));break;default:return!1}return!0}function Dge(e,n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn,Dn,ot,Qt;if(LXe(n),f=u(K((!n.b&&(n.b=new Nn(vt,n,4,7)),n.b),0),84),b=u(K((!n.c&&(n.c=new Nn(vt,n,5,8)),n.c),0),84),l=iu(f),h=iu(b),o=(!n.a&&(n.a=new pe(Pi,n,6,6)),n.a).i==0?null:u(K((!n.a&&(n.a=new pe(Pi,n,6,6)),n.a),0),170),Ie=u(Bn(e.a,l),9),Dn=u(Bn(e.a,h),9),cn=null,ot=null,X(f,193)&&(fe=u(Bn(e.a,f),246),X(fe,12)?cn=u(fe,12):X(fe,9)&&(Ie=u(fe,9),cn=u(Le(Ie.j,0),12))),X(b,193)&&(Tn=u(Bn(e.a,b),246),X(Tn,12)?ot=u(Tn,12):X(Tn,9)&&(Dn=u(Tn,9),ot=u(Le(Dn.j,0),12))),!Ie||!Dn)throw $(new u4("The source or the target of edge "+n+" could not be found. This usually happens when an edge connects a node laid out by ELK Layered to a node in another level of hierarchy laid out by either another instance of ELK Layered or another layout algorithm alltogether. The former can be solved by setting the hierarchyHandling option to INCLUDE_CHILDREN."));for(O=new Mw,$u(O,n),he(O,(me(),wi),n),he(O,(Ne(),Wc),null),S=u(T(i,po),22),Ie==Dn&&S.Ec((Dc(),ux)),cn||(be=(Nc(),Do),tn=null,o&&D3(u(T(Ie,Wi),102))&&(tn=new je(o.j,o.k),fPe(tn,j2(n)),$Pe(tn,t),C2(h,l)&&(be=ys,gi(tn,Ie.n))),cn=RKe(Ie,tn,be,i)),ot||(be=(Nc(),ys),Qt=null,o&&D3(u(T(Dn,Wi),102))&&(Qt=new je(o.b,o.c),fPe(Qt,j2(n)),$Pe(Qt,t)),ot=RKe(Dn,Qt,be,_r(Dn))),lc(O,cn),Gr(O,ot),(cn.e.c.length>1||cn.g.c.length>1||ot.e.c.length>1||ot.g.c.length>1)&&S.Ec((Dc(),cx)),y=new ut((!n.n&&(n.n=new pe(ju,n,1,7)),n.n));y.e!=y.i.gc();)if(p=u(ft(y),157),!Re($e(ye(p,Eg)))&&p.a)switch(I=fQ(p),Te(O.b,I),u(T(I,Oh),279).g){case 1:case 2:S.Ec((Dc(),E7));break;case 0:S.Ec((Dc(),j7)),he(I,Oh,($a(),F7))}if(c=u(T(i,wx),301),R=u(T(i,mH),328),r=c==(BE(),eD)||R==(HE(),Wie),o&&(!o.a&&(o.a=new mr(kl,o,5)),o.a).i!=0&&r){for(q=lTn(o),A=new xs,te=St(q,0);te.b!=te.d.c;)V=u(jt(te),8),Vt(A,new wc(V));he(O,Kve,A)}return O}function hBn(e,n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn,Dn,ot,Qt,qi;for(tn=0,Tn=0,Ie=new pt,be=u(Js(p2(So(new wn(null,new pn(e.b,16)),new y_),new Nk)),15).a+1,cn=oe($t,ni,30,be,15,1),I=oe($t,ni,30,be,15,1),O=0;O1)for(l=ot+1;lh.b.e.b*(1-R)+h.c.e.b*R));A++);if(fe.gc()>0&&(Qt=h.a.b==0?pc(h.b.e):u(If(h.a),8),V=gi(pc(u(fe.Xb(fe.gc()-1),40).e),u(fe.Xb(fe.gc()-1),40).f),y=gi(pc(u(fe.Xb(0),40).e),u(fe.Xb(0),40).f),A>=fe.gc()-1&&Qt.b>V.b&&h.c.e.b>V.b||A<=0&&Qt.bh.b.e.a*(1-R)+h.c.e.a*R));A++);if(fe.gc()>0&&(Qt=h.a.b==0?pc(h.b.e):u(If(h.a),8),V=gi(pc(u(fe.Xb(fe.gc()-1),40).e),u(fe.Xb(fe.gc()-1),40).f),y=gi(pc(u(fe.Xb(0),40).e),u(fe.Xb(0),40).f),A>=fe.gc()-1&&Qt.a>V.a&&h.c.e.a>V.a||A<=0&&Qt.a=ne(re(T(e,(Ti(),vye))))&&++Tn):(S.f&&S.d.e.a<=ne(re(T(e,(Ti(),wre))))&&++tn,S.g&&S.c.e.a+S.c.f.a>=ne(re(T(e,(Ti(),mye))))&&++Tn)}else te==0?K0e(h):te<0&&(++cn[ot],++I[qi],Dn=uBn(h,n,e,new jc(ve(tn),ve(Tn)),t,i,new jc(ve(I[qi]),ve(cn[ot]))),tn=u(Dn.a,15).a,Tn=u(Dn.b,15).a)}function dBn(e){e.gb||(e.gb=!0,e.b=Au(e,0),Vi(e.b,18),Ii(e.b,19),e.a=Au(e,1),Vi(e.a,1),Ii(e.a,2),Ii(e.a,3),Ii(e.a,4),Ii(e.a,5),e.o=Au(e,2),Vi(e.o,8),Vi(e.o,9),Ii(e.o,10),Ii(e.o,11),Ii(e.o,12),Ii(e.o,13),Ii(e.o,14),Ii(e.o,15),Ii(e.o,16),Ii(e.o,17),Ii(e.o,18),Ii(e.o,19),Ii(e.o,20),Ii(e.o,21),Ii(e.o,22),Ii(e.o,23),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),e.p=Au(e,3),Vi(e.p,2),Vi(e.p,3),Vi(e.p,4),Vi(e.p,5),Ii(e.p,6),Ii(e.p,7),Yc(e.p),Yc(e.p),e.q=Au(e,4),Vi(e.q,8),e.v=Au(e,5),Ii(e.v,9),Yc(e.v),Yc(e.v),Yc(e.v),e.w=Au(e,6),Vi(e.w,2),Vi(e.w,3),Vi(e.w,4),Ii(e.w,5),e.B=Au(e,7),Ii(e.B,1),Yc(e.B),Yc(e.B),Yc(e.B),e.Q=Au(e,8),Ii(e.Q,0),Yc(e.Q),e.R=Au(e,9),Vi(e.R,1),e.S=Au(e,10),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),e.T=Au(e,11),Ii(e.T,10),Ii(e.T,11),Ii(e.T,12),Ii(e.T,13),Ii(e.T,14),Yc(e.T),Yc(e.T),e.U=Au(e,12),Vi(e.U,2),Vi(e.U,3),Ii(e.U,4),Ii(e.U,5),Ii(e.U,6),Ii(e.U,7),Yc(e.U),e.V=Au(e,13),Ii(e.V,10),e.W=Au(e,14),Vi(e.W,18),Vi(e.W,19),Vi(e.W,20),Ii(e.W,21),Ii(e.W,22),Ii(e.W,23),e.bb=Au(e,15),Vi(e.bb,10),Vi(e.bb,11),Vi(e.bb,12),Vi(e.bb,13),Vi(e.bb,14),Vi(e.bb,15),Vi(e.bb,16),Ii(e.bb,17),Yc(e.bb),Yc(e.bb),e.eb=Au(e,16),Vi(e.eb,2),Vi(e.eb,3),Vi(e.eb,4),Vi(e.eb,5),Vi(e.eb,6),Vi(e.eb,7),Ii(e.eb,8),Ii(e.eb,9),e.ab=Au(e,17),Vi(e.ab,0),Vi(e.ab,1),e.H=Au(e,18),Ii(e.H,0),Ii(e.H,1),Ii(e.H,2),Ii(e.H,3),Ii(e.H,4),Ii(e.H,5),Yc(e.H),e.db=Au(e,19),Ii(e.db,2),e.c=ci(e,20),e.d=ci(e,21),e.e=ci(e,22),e.f=ci(e,23),e.i=ci(e,24),e.g=ci(e,25),e.j=ci(e,26),e.k=ci(e,27),e.n=ci(e,28),e.r=ci(e,29),e.s=ci(e,30),e.t=ci(e,31),e.u=ci(e,32),e.fb=ci(e,33),e.A=ci(e,34),e.C=ci(e,35),e.D=ci(e,36),e.F=ci(e,37),e.G=ci(e,38),e.I=ci(e,39),e.J=ci(e,40),e.L=ci(e,41),e.M=ci(e,42),e.N=ci(e,43),e.O=ci(e,44),e.P=ci(e,45),e.X=ci(e,46),e.Y=ci(e,47),e.Z=ci(e,48),e.$=ci(e,49),e._=ci(e,50),e.cb=ci(e,51),e.K=ci(e,52))}function bBn(e,n,t,i){var r,c,o,l,f,h,b,p,y,S,A;for(p=St(e.b,0);p.b!=p.d.c;)if(b=u(jt(p),40),!bn(b.c,DF))for(c=u(gs(new wn(null,new pn(MCn(b,e),16)),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),n==(vr(),Zc)||n==ru?c.gd(new A_):c.gd(new M_),A=c.gc(),r=0;r0&&(l=u(If(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(If(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&k.Math.abs(f-S)/(k.Math.abs(l-y)/40)>50&&(S>f?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a+i/5.3,b.e.b+b.f.b*o-i/2)):gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a+i/5.3,b.e.b+b.f.b*o+i/2)))),gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a,b.e.b+b.f.b*o))):n==ru?(h=ne(re(T(b,(Ti(),Ea)))),b.e.a-i>h?gc(u(c.Xb(r),65).a,new je(h-t,b.e.b+b.f.b*o)):u(c.Xb(r),65).a.b>0&&(l=u(If(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(If(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&k.Math.abs(f-S)/(k.Math.abs(l-y)/40)>50&&(S>f?gc(u(c.Xb(r),65).a,new je(b.e.a-i/5.3,b.e.b+b.f.b*o-i/2)):gc(u(c.Xb(r),65).a,new je(b.e.a-i/5.3,b.e.b+b.f.b*o+i/2)))),gc(u(c.Xb(r),65).a,new je(b.e.a,b.e.b+b.f.b*o))):n==Vl?(h=ne(re(T(b,(Ti(),Yf)))),b.e.b+b.f.b+i0&&(l=u(If(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(If(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&k.Math.abs(l-y)/(k.Math.abs(f-S)/40)>50&&(y>l?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o-i/2,b.e.b+i/5.3+b.f.b)):gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o+i/2,b.e.b+i/5.3+b.f.b)))),gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o,b.e.b+b.f.b))):(h=ne(re(T(b,(Ti(),Ea)))),Bze(u(c.Xb(r),65),e)?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o,u(If(u(c.Xb(r),65).a),8).b)):b.e.b-i>h?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o,h-t)):u(c.Xb(r),65).a.b>0&&(l=u(If(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(If(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&k.Math.abs(l-y)/(k.Math.abs(f-S)/40)>50&&(y>l?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o-i/2,b.e.b-i/5.3)):gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o+i/2,b.e.b-i/5.3)))),gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o,b.e.b)))}function iYe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;if(o=n,y=t,so(e.a,o)){if(rf(u(Bn(e.a,o),47),y))return 1}else ei(e.a,o,new ar);if(so(e.a,y)){if(rf(u(Bn(e.a,y),47),o))return-1}else ei(e.a,y,new ar);if(so(e.e,o)){if(rf(u(Bn(e.e,o),47),y))return-1}else ei(e.e,o,new ar);if(so(e.e,y)){if(rf(u(Bn(e.a,y),47),o))return 1}else ei(e.e,y,new ar);if(o.j!=y.j)return be=iwn(o.j,y.j),be>0?Hl(e,o,y,1):Hl(e,y,o,1),be;if(fe=1,o.e.c.length!=0&&y.e.c.length!=0){if((o.j==(De(),Kn)&&y.j==Kn||o.j==Xn&&y.j==Xn||o.j==bt&&y.j==bt)&&(fe=-fe),b=u(Le(o.e,0),17).c,I=u(Le(y.e,0),17).c,f=b.i,A=I.i,f==A)for(V=new L(f.j);V.a0?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe);if(i=kFe(u(gs(mV(e.d),Ts(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),20),f,A),i!=0)return i>0?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe);if(e.c&&(be=QJe(e,o,y),be!=0))return be>0?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe)}return o.g.c.length!=0&&y.g.c.length!=0?((o.j==(De(),Kn)&&y.j==Kn||o.j==bt&&y.j==bt)&&(fe=-fe),p=u(T(o,(me(),mie)),9),R=u(T(y,mie),9),e.f==(F1(),nre)&&p&&R&&bi(p,Ci)&&bi(R,Ci)?(l=qw(p,R,e.b,u(T(e.b,cb),15).a),S=qw(R,p,e.b,u(T(e.b,cb),15).a),l>S?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe)):e.c&&(be=QJe(e,o,y),be!=0)?be>0?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe):(h=0,O=0,bi(u(Le(o.g,0),17),Ci)&&(h=qw(u(Le(o.g,0),246),u(Le(y.g,0),246),e.b,o.g.c.length+o.e.c.length)),bi(u(Le(y.g,0),17),Ci)&&(O=qw(u(Le(y.g,0),246),u(Le(o.g,0),246),e.b,y.g.c.length+y.e.c.length)),p&&p==R||e.g&&(e.g._b(p)&&(h=u(e.g.xc(p),15).a),e.g._b(R)&&(O=u(e.g.xc(R),15).a)),h>O?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe))):o.e.c.length!=0&&y.g.c.length!=0?(Hl(e,o,y,fe),1):o.g.c.length!=0&&y.e.c.length!=0?(Hl(e,y,o,fe),-1):bi(o,(me(),Ci))&&bi(y,Ci)?(c=o.i.j.c.length,l=qw(o,y,e.b,c),S=qw(y,o,e.b,c),(o.j==(De(),Kn)&&y.j==Kn||o.j==bt&&y.j==bt)&&(fe=-fe),l>S?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe)):(Hl(e,y,o,fe),-fe)}function me(){me=Y;var e,n;wi=new yi(owe),Gve=new yi("coordinateOrigin"),yie=new yi("processors"),Hve=new Li("compoundNode",(Pn(),!1)),uD=new Li("insideConnections",!1),Kve=new yi("originalBendpoints"),Vve=new yi("originalDummyNodePosition"),Yve=new yi("originalLabelEdge"),sx=new yi("representedLabels"),ox=new yi("endLabels"),My=new yi("endLabel.origin"),Cy=new Li("labelSide",(al(),zD)),Iv=new Li("maxEdgeThickness",0),Hd=new Li("reversed",!1),Oy=new yi(tQe),ja=new Li("longEdgeSource",null),gf=new Li("longEdgeTarget",null),bm=new Li("longEdgeHasLabelDummies",!1),oD=new Li("longEdgeBeforeLabelDummy",!1),tH=new Li("edgeConstraint",(Wb(),tie)),ap=new yi("inLayerLayoutUnit"),mg=new Li("inLayerConstraint",(_1(),rD)),Ty=new Li("inLayerSuccessorConstraint",new Ce),Xve=new Li("inLayerSuccessorConstraintBetweenNonDummies",!1),vs=new yi("portDummy"),nH=new Li("crossingHint",ve(0)),po=new Li("graphProperties",(n=u(sa(lie),10),new _l(n,u(_f(n,n.length),10),0))),Du=new Li("externalPortSide",(De(),ku)),Uve=new Li("externalPortSize",new Kr),gie=new yi("externalPortReplacedDummies"),iH=new yi("externalPortReplacedDummy"),K1=new Li("externalPortConnections",(e=u(sa(xc),10),new _l(e,u(_f(e,e.length),10),0))),hp=new Li(QYe,0),Jve=new yi("barycenterAssociates"),Dy=new yi("TopSideComments"),xy=new yi("BottomSideComments"),eH=new yi("CommentConnectionPort"),pie=new Li("inputCollect",!1),vie=new Li("outputCollect",!1),Ay=new Li("cyclic",!1),qve=new yi("crossHierarchyMap"),jie=new yi("targetOffset"),new Li("splineLabelSize",new Kr),Lv=new yi("spacings"),rH=new Li("partitionConstraint",!1),fp=new yi("breakingPoint.info"),Zve=new yi("splines.survivingEdge"),vg=new yi("splines.route.start"),Pv=new yi("splines.edgeChain"),Wve=new yi("originalPortConstraints"),dp=new yi("selfLoopHolder"),x7=new yi("splines.nsPortY"),Ci=new yi("modelOrder"),cb=new yi("modelOrder.maximum"),cD=new yi("modelOrderGroups.cb.number"),mie=new yi("longEdgeTargetNode"),rb=new Li(TQe,!1),_v=new Li(TQe,!1),wie=new yi("layerConstraints.hiddenNodes"),Qve=new yi("layerConstraints.opposidePort"),kie=new yi("targetNode.modelOrder"),Ny=new Li("tarjan.lowlink",ve(oi)),lx=new Li("tarjan.id",ve(-1)),cH=new Li("tarjan.onstack",!1),Qin=new Li("partOfCycle",!1),$v=new yi("medianHeuristic.weight")}function Xt(){Xt=Y;var e,n;zy=new yi(pWe),Dm=new yi(mWe),p9e=(Vh(),sce),yfn=new fn(ppe,p9e),$7=new fn(G8,null),kfn=new yi(N2e),v9e=(rg(),Mi(ace,z(B(hce,1),ke,299,0,[fce]))),ND=new fn(TF,v9e),DD=new fn(_N,(Pn(),!1)),y9e=(vr(),eh),Mg=new fn(Fee,y9e),E9e=(z1(),xce),j9e=new fn(IN,E9e),xfn=new fn(C2e,!1),x9e=(B1(),oG),Xv=new fn(MF,x9e),P9e=new pw(12),o1=new fn(nm,P9e),_D=new fn(jS,!1),wce=new fn(OF,!1),LD=new fn(ES,!1),F9e=(Br(),bb),Kx=new fn(WZ,F9e),Fy=new yi(CF),PD=new yi(EN),Ece=new yi(sF),Sce=new yi(kS),T9e=new xs,Kv=new fn(Tpe,T9e),Efn=new fn(Dpe,!1),Afn=new fn(Ipe,!1),new fn(vWe,0),C9e=new wj,R7=new fn(Lpe,C9e),tG=new fn(gpe,!1),Dfn=new fn(yWe,1),Cm=new yi(kWe),Tm=new yi(jWe),z7=new fn(SN,!1),new fn(EWe,!0),ve(0),new fn(SWe,ve(100)),new fn(xWe,!1),ve(0),new fn(AWe,ve(4e3)),ve(0),new fn(MWe,ve(400)),new fn(TWe,!1),new fn(CWe,!1),new fn(OWe,!0),new fn(NWe,!1),m9e=(KB(),Nce),jfn=new fn(O2e,m9e),M9e=(jE(),HD),Tfn=new fn(DWe,M9e),A9e=(u8(),$D),Mfn=new fn(IWe,A9e),Ifn=new fn(ipe,10),_fn=new fn(rpe,10),Lfn=new fn(cpe,20),Pfn=new fn(upe,10),q9e=new fn(QZ,2),U9e=new fn(zee,10),X9e=new fn(ope,0),iG=new fn(fpe,5),K9e=new fn(spe,1),V9e=new fn(lpe,1),Vd=new fn(em,20),$fn=new fn(ape,10),W9e=new fn(hpe,10),Jy=new yi(dpe),Q9e=new pCe,Y9e=new fn(Ppe,Q9e),Ofn=new yi(Hee),$9e=!1,Cfn=new fn(Jee,$9e),N9e=new pw(5),O9e=new fn(ype,N9e),D9e=(G2(),n=u(sa($c),10),new _l(n,u(_f(n,n.length),10),0)),Vv=new fn(U8,D9e),B9e=(nv(),db),R9e=new fn(Epe,B9e),mce=new yi(Spe),vce=new yi(xpe),yce=new yi(Ape),pce=new yi(Mpe),I9e=(e=u(sa(tA),10),new _l(e,u(_f(e,e.length),10),0)),Tg=new fn(wv,I9e),L9e=nn((_s(),q7)),ab=new fn(hy,L9e),_9e=new je(0,0),Yv=new fn(dy,_9e),Om=new fn(q8,!1),k9e=($a(),F7),bce=new fn(Ope,k9e),dce=new fn(lF,!1),ve(1),new fn(_We,null),z9e=new yi(_pe),kce=new yi(Npe),G9e=(De(),ku),Qv=new fn(wpe,G9e),Ps=new yi(bpe),J9e=(ps(),nn(gb)),Nm=new fn(X8,J9e),jce=new fn(kpe,!1),H9e=new fn(jpe,!0),ve(1),Jfn=new fn(hne,ve(3)),ve(1),Gfn=new fn(D2e,ve(4)),rG=new fn(xN,1),cG=new fn(dne,null),Im=new fn(AN,150),B7=new fn(MN,1.414),Hy=new fn(Ww,null),Rfn=new fn(I2e,1),ID=new fn(mpe,!1),gce=new fn(vpe,!1),Sfn=new fn(Cpe,1),S9e=(jz(),Mce),new fn(LWe,S9e),Nfn=!0,Hfn=(UR(),Oce),zfn=(G4(),Pm),Ffn=Pm,Bfn=Pm}function Ur(){Ur=Y,B3e=new br("DIRECTION_PREPROCESSOR",0),P3e=new br("COMMENT_PREPROCESSOR",1),Av=new br("EDGE_AND_LAYER_CONSTRAINT_EDGE_REVERSER",2),Ite=new br("INTERACTIVE_EXTERNAL_PORT_POSITIONER",3),rve=new br("PARTITION_PREPROCESSOR",4),TJ=new br("LABEL_DUMMY_INSERTER",5),RJ=new br("SELF_LOOP_PREPROCESSOR",6),fm=new br("LAYER_CONSTRAINT_PREPROCESSOR",7),tve=new br("PARTITION_MIDPROCESSOR",8),X3e=new br("HIGH_DEGREE_NODE_LAYER_PROCESSOR",9),eve=new br("NODE_PROMOTION",10),lm=new br("LAYER_CONSTRAINT_POSTPROCESSOR",11),ive=new br("PARTITION_POSTPROCESSOR",12),G3e=new br("HIERARCHICAL_PORT_CONSTRAINT_PROCESSOR",13),cve=new br("SEMI_INTERACTIVE_CROSSMIN_PROCESSOR",14),O3e=new br("BREAKING_POINT_INSERTER",15),DJ=new br("LONG_EDGE_SPLITTER",16),_te=new br("PORT_SIDE_PROCESSOR",17),AJ=new br("INVERTED_PORT_PROCESSOR",18),LJ=new br("PORT_LIST_SORTER",19),ove=new br("SORT_BY_INPUT_ORDER_OF_MODEL",20),_J=new br("NORTH_SOUTH_PORT_PREPROCESSOR",21),N3e=new br("BREAKING_POINT_PROCESSOR",22),nve=new br(yQe,23),sve=new br(kQe,24),PJ=new br("SELF_LOOP_PORT_RESTORER",25),C3e=new br("ALTERNATING_LAYER_UNZIPPER",26),uve=new br("SINGLE_EDGE_GRAPH_WRAPPER",27),MJ=new br("IN_LAYER_CONSTRAINT_PROCESSOR",28),F3e=new br("END_NODE_PORT_LABEL_MANAGEMENT_PROCESSOR",29),W3e=new br("LABEL_AND_NODE_SIZE_PROCESSOR",30),Q3e=new br("INNERMOST_NODE_MARGIN_CALCULATOR",31),BJ=new br("SELF_LOOP_ROUTER",32),_3e=new br("COMMENT_NODE_MARGIN_CALCULATOR",33),xJ=new br("END_LABEL_PREPROCESSOR",34),OJ=new br("LABEL_DUMMY_SWITCHER",35),I3e=new br("CENTER_LABEL_MANAGEMENT_PROCESSOR",36),g7=new br("LABEL_SIDE_SELECTOR",37),V3e=new br("HYPEREDGE_DUMMY_MERGER",38),q3e=new br("HIERARCHICAL_PORT_DUMMY_SIZE_PROCESSOR",39),Z3e=new br("LAYER_SIZE_AND_GRAPH_HEIGHT_CALCULATOR",40),nx=new br("HIERARCHICAL_PORT_POSITION_PROCESSOR",41),$3e=new br("CONSTRAINTS_POSTPROCESSOR",42),L3e=new br("COMMENT_POSTPROCESSOR",43),Y3e=new br("HYPERNODE_PROCESSOR",44),U3e=new br("HIERARCHICAL_PORT_ORTHOGONAL_EDGE_ROUTER",45),NJ=new br("LONG_EDGE_JOINER",46),$J=new br("SELF_LOOP_POSTPROCESSOR",47),D3e=new br("BREAKING_POINT_REMOVER",48),IJ=new br("NORTH_SOUTH_PORT_POSTPROCESSOR",49),K3e=new br("HORIZONTAL_COMPACTOR",50),CJ=new br("LABEL_DUMMY_REMOVER",51),J3e=new br("FINAL_SPLINE_BENDPOINTS_CALCULATOR",52),z3e=new br("END_LABEL_SORTER",53),Ey=new br("REVERSED_EDGE_RESTORER",54),SJ=new br("END_LABEL_POSTPROCESSOR",55),H3e=new br("HIERARCHICAL_NODE_RESIZER",56),R3e=new br("DIRECTION_POSTPROCESSOR",57)}function gBn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn,Dn,ot,Qt,qi,Es,eu,jl,i5,i0,ia,nd,Zl,Yy,gA,td,Ma,r0,Ig,_g,Qy,Lg,Pg,id,Gm,M7e,Sp,wA,Kce,Wy,pA,qm,mA,Vce,Ihn;for(M7e=0,Qt=n,eu=0,i0=Qt.length;eu0&&(e.a[Ma.p]=M7e++)}for(pA=0,qi=t,jl=0,ia=qi.length;jl0;){for(Ma=(at(Qy.b>0),u(Qy.a.Xb(Qy.c=--Qy.b),12)),_g=0,l=new L(Ma.e);l.a0&&(Ma.j==(De(),Xn)?(e.a[Ma.p]=pA,++pA):(e.a[Ma.p]=pA+nd+Yy,++Yy))}pA+=Yy}for(Ig=new pt,A=new zh,ot=n,Es=0,i5=ot.length;Esh.b&&(h.b=Lg)):Ma.i.c==Gm&&(Lgh.c&&(h.c=Lg));for(J9(O,0,O.length,null),Wy=oe($t,ni,30,O.length,15,1),i=oe($t,ni,30,pA+1,15,1),R=0;R0;)Ie%2>0&&(r+=Vce[Ie+1]),Ie=(Ie-1)/2|0,++Vce[Ie];for(tn=oe(kon,On,370,O.length*2,0,1),te=0;te0&&JC(Es.f),ye(R,cG)!=null&&(!R.a&&(R.a=new pe(Jt,R,10,11)),!!R.a)&&(!R.a&&(R.a=new pe(Jt,R,10,11)),R.a).i>0?(l=u(ye(R,cG),521),_g=l.Sg(R),ww(R,k.Math.max(R.g,_g.a+nd.b+nd.c),k.Math.max(R.f,_g.b+nd.d+nd.a))):(!R.a&&(R.a=new pe(Jt,R,10,11)),R.a).i!=0&&(_g=new je(ne(re(ye(R,Im))),ne(re(ye(R,Im)))/ne(re(ye(R,B7)))),ww(R,k.Math.max(R.g,_g.a+nd.b+nd.c),k.Math.max(R.f,_g.b+nd.d+nd.a)));if(ia=u(ye(n,o1),104),S=n.g-(ia.b+ia.c),y=n.f-(ia.d+ia.a),Pg.ah("Available Child Area: ("+S+"|"+y+")"),ji(n,$7,S/y),DJe(n,r,i.dh(i5)),u(ye(n,Hy),281)==dG&&(oZ(n),ww(n,ia.b+ne(re(ye(n,Cm)))+ia.c,ia.d+ne(re(ye(n,Tm)))+ia.a)),Pg.ah("Executed layout algorithm: "+Pt(ye(n,zy))+" on node "+n.k),u(ye(n,Hy),281)==Pm){if(S<0||y<0)throw $(new wd("The size defined by the parent parallel node is too small for the space provided by the paddings of the child hierarchical node. "+n.k));for(da(n,Cm)||da(n,Tm)||oZ(n),O=ne(re(ye(n,Cm))),A=ne(re(ye(n,Tm))),Pg.ah("Desired Child Area: ("+O+"|"+A+")"),Yy=S/O,gA=y/A,Zl=k.Math.min(Yy,k.Math.min(gA,ne(re(ye(n,Rfn))))),ji(n,rG,Zl),Pg.ah(n.k+" -- Local Scale Factor (X|Y): ("+Yy+"|"+gA+")"),te=u(ye(n,ND),22),c=0,o=0,Zl'?":bn(bZe,e)?"'(?<' or '(? toIndex: ",Yge=", toIndex: ",Qge="Index: ",Wge=", Size: ",z8="org.eclipse.elk.alg.common",Yt={51:1},IYe="org.eclipse.elk.alg.common.compaction",_Ye="Scanline/EventHandler",n1="org.eclipse.elk.alg.common.compaction.oned",LYe="CNode belongs to another CGroup.",PYe="ISpacingsHandler/1",qZ="The ",UZ=" instance has been finished already.",$Ye="The direction ",RYe=" is not supported by the CGraph instance.",BYe="OneDimensionalCompactor",zYe="OneDimensionalCompactor/lambda$0$Type",FYe="Quadruplet",JYe="ScanlineConstraintCalculator",HYe="ScanlineConstraintCalculator/ConstraintsScanlineHandler",GYe="ScanlineConstraintCalculator/ConstraintsScanlineHandler/lambda$0$Type",qYe="ScanlineConstraintCalculator/Timestamp",UYe="ScanlineConstraintCalculator/lambda$0$Type",jh={178:1,48:1},mS="org.eclipse.elk.alg.common.networksimplex",pa={171:1,3:1,4:1},XYe="org.eclipse.elk.alg.common.nodespacing",lg="org.eclipse.elk.alg.common.nodespacing.cellsystem",F8="CENTER",KYe={216:1,337:1},Zge={3:1,4:1,5:1,592:1},ly="LEFT",fy="RIGHT",ewe="Vertical alignment cannot be null",nwe="BOTTOM",uF="org.eclipse.elk.alg.common.nodespacing.internal",vS="UNDEFINED",Ga=.01,yN="org.eclipse.elk.alg.common.nodespacing.internal.algorithm",VYe="LabelPlacer/lambda$0$Type",YYe="LabelPlacer/lambda$1$Type",QYe="portRatioOrPosition",J8="org.eclipse.elk.alg.common.overlaps",XZ="DOWN",ay="org.eclipse.elk.alg.common.spore",Z2={3:1,4:1,5:1,198:1},WYe={3:1,6:1,4:1,5:1,90:1,110:1},KZ="org.eclipse.elk.alg.force",twe="ComponentsProcessor",ZYe="ComponentsProcessor/1",iwe="ElkGraphImporter/lambda$0$Type",Qw={214:1},gv="org.eclipse.elk.core",kN="org.eclipse.elk.graph.properties",eQe="IPropertyHolder",jN="org.eclipse.elk.alg.force.graph",nQe="Component Layout",rwe="org.eclipse.elk.alg.force.model",vu="org.eclipse.elk.core.data",oF="org.eclipse.elk.force.model",cwe="org.eclipse.elk.force.iterations",uwe="org.eclipse.elk.force.repulsivePower",VZ="org.eclipse.elk.force.temperature",Eh=.001,YZ="org.eclipse.elk.force.repulsion",qa={148:1},yS="org.eclipse.elk.alg.force.options",H8=1.600000023841858,$o="org.eclipse.elk.force",EN="org.eclipse.elk.priority",em="org.eclipse.elk.spacing.nodeNode",QZ="org.eclipse.elk.spacing.edgeLabel",G8="org.eclipse.elk.aspectRatio",sF="org.eclipse.elk.randomSeed",kS="org.eclipse.elk.separateConnectedComponents",nm="org.eclipse.elk.padding",jS="org.eclipse.elk.interactive",WZ="org.eclipse.elk.portConstraints",lF="org.eclipse.elk.edgeLabels.inline",ES="org.eclipse.elk.omitNodeMicroLayout",q8="org.eclipse.elk.nodeSize.fixedGraphSize",hy="org.eclipse.elk.nodeSize.options",wv="org.eclipse.elk.nodeSize.constraints",U8="org.eclipse.elk.nodeLabels.placement",X8="org.eclipse.elk.portLabels.placement",SN="org.eclipse.elk.topdownLayout",xN="org.eclipse.elk.topdown.scaleFactor",AN="org.eclipse.elk.topdown.hierarchicalNodeWidth",MN="org.eclipse.elk.topdown.hierarchicalNodeAspectRatio",Ww="org.eclipse.elk.topdown.nodeType",owe="origin",tQe="random",iQe="boundingBox.upLeft",rQe="boundingBox.lowRight",swe="org.eclipse.elk.stress.fixed",lwe="org.eclipse.elk.stress.desiredEdgeLength",fwe="org.eclipse.elk.stress.dimension",awe="org.eclipse.elk.stress.epsilon",hwe="org.eclipse.elk.stress.iterationLimit",V0="org.eclipse.elk.stress",cQe="ELK Stress",dy="org.eclipse.elk.nodeSize.minimum",fF="org.eclipse.elk.alg.force.stress",uQe="Layered layout",by="org.eclipse.elk.alg.layered",TN="org.eclipse.elk.alg.layered.compaction.components",SS="org.eclipse.elk.alg.layered.compaction.oned",aF="org.eclipse.elk.alg.layered.compaction.oned.algs",fg="org.eclipse.elk.alg.layered.compaction.recthull",Ua="org.eclipse.elk.alg.layered.components",ma="NONE",ZZ="MODEL_ORDER",qu={3:1,6:1,4:1,10:1,5:1,126:1},oQe={3:1,6:1,4:1,5:1,135:1,90:1,110:1},hF="org.eclipse.elk.alg.layered.compound",Ai={43:1},Zu="org.eclipse.elk.alg.layered.graph",eee=" -> ",sQe="Not supported by LGraph",dwe="Port side is undefined",K8={3:1,6:1,4:1,5:1,323:1,135:1,90:1,110:1},Bd={3:1,6:1,4:1,5:1,135:1,199:1,209:1,90:1,110:1},lQe={3:1,6:1,4:1,5:1,135:1,2004:1,209:1,90:1,110:1},fQe=`([{"' \r +`)}return[]}function rxn(e){var n;return n=(CBe(),enn),n[e>>>28]|n[e>>24&15]<<4|n[e>>20&15]<<8|n[e>>16&15]<<12|n[e>>12&15]<<16|n[e>>8&15]<<20|n[e>>4&15]<<24|n[e&15]<<28}function kHe(e){var n,t,i;e.b==e.c&&(i=e.a.length,t=b1e(k.Math.max(8,i))<<1,e.b!=0?(n=_f(e.a,t),IBe(e,n,i),e.a=n,e.b=0):Qp(e.a,t),e.c=i)}function cxn(e,n){var t;return t=e.b,t.nf((Xt(),Ps))?t.$f()==(De(),Kn)?-t.Kf().a-ne(re(t.mf(Ps))):n+ne(re(t.mf(Ps))):t.$f()==(De(),Kn)?-t.Kf().a:n}function LO(e){var n;return e.b.c.length!=0&&u(Le(e.b,0),70).a?u(Le(e.b,0),70).a:(n=NV(e),n??""+(e.c?wu(e.c.a,e,0):-1))}function bz(e){var n;return e.f.c.length!=0&&u(Le(e.f,0),70).a?u(Le(e.f,0),70).a:(n=NV(e),n??""+(e.i?wu(e.i.j,e,0):-1))}function uxn(e,n){var t,i;if(n<0||n>=e.gc())return null;for(t=n;t0?e.c:0),r=k.Math.max(r,n.d),++i;e.e=c,e.b=r}function oxn(e){var n,t;if(!e.b)for(e.b=zR(u(e.f,125).jh().i),t=new ut(u(e.f,125).jh());t.e!=t.i.gc();)n=u(ft(t),157),Ce(e.b,new AX(n));return e.b}function sxn(e,n){var t,i,r;if(n.dc())return S9(),S9(),eI;for(t=new KOe(e,n.gc()),r=new ut(e);r.e!=r.i.gc();)i=ft(r),n.Gc(i)&&Et(t,i);return t}function $de(e,n,t,i){return n==0?i?(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),e.o):(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),ZT(e.o)):uz(e,n,t,i)}function WQ(e){var n,t;if(e.rb)for(n=0,t=e.rb.i;n>22),r+=i>>22,r<0)?!1:(e.l=t&Ls,e.m=i&Ls,e.h=r&G1,!0)}function ZQ(e,n,t,i,r,c,o){var l,f;return!(n.Re()&&(f=e.a.Le(t,i),f<0||!r&&f==0)||n.Se()&&(l=e.a.Le(t,c),l>0||!o&&l==0))}function hxn(e,n){n8();var t;if(t=e.j.g-n.j.g,t!=0)return 0;switch(e.j.g){case 2:return yQ(n,hve)-yQ(e,hve);case 4:return yQ(e,ave)-yQ(n,ave)}return 0}function dxn(e){switch(e.g){case 0:return iie;case 1:return rie;case 2:return cie;case 3:return uie;case 4:return KJ;case 5:return oie;default:return null}}function Qc(e,n,t){var i,r;return i=(r=new yX,ng(r,n),Mo(r,t),Et((!e.c&&(e.c=new pe(vp,e,12,10)),e.c),r),r),Td(i,0),O2(i,1),_d(i,!0),Id(i,!0),i}function V4(e,n){var t,i;if(n>=e.i)throw $(new jK(n,e.i));return++e.j,t=e.g[n],i=e.i-n-1,i>0&&Wu(e.g,n+1,e.g,n,i),tr(e.g,--e.i,null),e.Oi(n,t),e.Li(),t}function jHe(e,n){var t,i;return e.Db>>16==17?e.Cb.Qh(e,21,vf,n):(i=Oc(u(An((t=u(Un(e,16),29),t||e.fi()),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function bxn(e){var n,t,i,r;for(jn(),Tr(e.c,e.a),r=new L(e.c);r.at.a.c.length))throw $(new Gn("index must be >= 0 and <= layer node count"));e.c&&qo(e.c.a,e),e.c=t,t&&Pb(t.a,n,e)}function OHe(e,n){this.c=new pt,this.a=e,this.b=n,this.d=u(C(e,(me(),Lv)),316),ue(C(e,(Ne(),o4e)))===ue((rO(),VJ))?this.e=new vxe:this.e=new mxe}function yxn(e,n){var t,i,r,c;for(c=0,i=new L(e);i.a0?n:0),++t;return new je(i,r)}function kxn(e,n){var t,i;for(e.b=0,e.d=new $P,i=new L(n.a);i.a>16==6?e.Cb.Qh(e,6,pr,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),bG)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function Hde(e,n){var t,i;return e.Db>>16==7?e.Cb.Qh(e,1,VD,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),L8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function Gde(e,n){var t,i;return e.Db>>16==9?e.Cb.Qh(e,9,Jt,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),$8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function IHe(e,n){var t,i;return e.Db>>16==5?e.Cb.Qh(e,9,EG,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),Zd)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function _He(e,n){var t,i;return e.Db>>16==7?e.Cb.Qh(e,6,xa,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),n0)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function qde(e,n){var t,i;return e.Db>>16==3?e.Cb.Qh(e,0,QD,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),Wd)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function Ude(e,n){var t,i;return e.Db>>16==3?e.Cb.Qh(e,12,Jt,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),_8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function xxn(e,n,t){var i,r,c;for(t<0&&(t=0),c=e.i,r=t;r$Z)return w8(e,i);if(i==e)return!0}}return!1}function Mxn(e){switch(F$(),e.q.g){case 5:kqe(e,(De(),Xn)),kqe(e,bt);break;case 4:MUe(e,(De(),Xn)),MUe(e,bt);break;default:TVe(e,(De(),Xn)),TVe(e,bt)}}function Cxn(e){switch(F$(),e.q.g){case 5:zqe(e,(De(),et)),zqe(e,Kn);break;case 4:BJe(e,(De(),et)),BJe(e,Kn);break;default:OVe(e,(De(),et)),OVe(e,Kn)}}function Txn(e){var n,t;n=u(C(e,(Gf(),jtn)),15),n?(t=n.a,t==0?he(e,(D0(),yJ),new vQ):he(e,(D0(),yJ),new XR(t))):he(e,(D0(),yJ),new XR(1))}function Oxn(e,n){var t;switch(t=e.i,n.g){case 1:return-(e.n.b+e.o.b);case 2:return e.n.a-t.o.a;case 3:return e.n.b-t.o.b;case 4:return-(e.n.a+e.o.a)}return 0}function Nxn(e,n){switch(e.g){case 0:return n==(Xs(),V1)?zJ:FJ;case 1:return n==(Xs(),V1)?zJ:WN;case 2:return n==(Xs(),V1)?WN:FJ;default:return WN}}function $O(e,n){var t,i,r;for(qo(e.a,n),e.e-=n.r+(e.a.c.length==0?0:e.c),r=Gee,i=new L(e.a);i.a>16==11?e.Cb.Qh(e,10,Jt,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(Gu(),P8e)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function LHe(e,n){var t,i;return e.Db>>16==10?e.Cb.Qh(e,11,vf,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),e0)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function PHe(e,n){var t,i;return e.Db>>16==10?e.Cb.Qh(e,12,yf,n):(i=Oc(u(An((t=u(Un(e,16),29),t||(yn(),Fm)),e.Db>>16),19)),e.Cb.Qh(e,i.n,i.f,n))}function $He(e,n){var t,i,r,c,o;if(n)for(r=n.a.length,t=new Rb(r),o=(t.b-t.a)*t.c<0?(k0(),yb):new S0(t);o.Ob();)c=u(o.Pb(),15),i=B9(n,c.a),i&&kUe(e,i)}function Rxn(){nse();var e,n;for(cBn((x0(),Rn)),VRn(Rn),WQ(Rn),Q8e=(yn(),ih),n=new L(u7e);n.a>19,h=n.h>>19,f!=h?h-f:(r=e.h,l=n.h,r!=l?r-l:(i=e.m,o=n.m,i!=o?i-o:(t=e.l,c=n.l,t-c)))}function RHe(e,n,t){var i,r,c,o,l;for(r=e[t.g],l=new L(n.d);l.a0?e.b:0),++t;n.b=i,n.e=r}function BHe(e){var n,t,i;if(i=e.b,gMe(e.i,i.length)){for(t=i.length*2,e.b=oe(Qne,dN,308,t,0,1),e.c=oe(Qne,dN,308,t,0,1),e.f=t-1,e.i=0,n=e.a;n;n=n.c)qO(e,n,n);++e.g}}function XE(e,n){return e.b.a=k.Math.min(e.b.a,n.c),e.b.b=k.Math.min(e.b.b,n.d),e.a.a=k.Math.max(e.a.a,n.c),e.a.b=k.Math.max(e.a.b,n.d),Hn(e.c,n),!0}function zxn(e,n,t){var i;i=n.c.i,i.k==(zn(),dr)?(he(e,(me(),ja),u(C(i,ja),12)),he(e,gf,u(C(i,gf),12))):(he(e,(me(),ja),n.c),he(e,gf,t.d))}function p8(e,n,t){x8();var i,r,c,o,l,f;return o=n/2,c=t/2,i=k.Math.abs(e.a),r=k.Math.abs(e.b),l=1,f=1,i>o&&(l=o/i),r>c&&(f=c/r),A1(e,k.Math.min(l,f)),e}function Fxn(){Gz();var e,n;try{if(n=u(r0e((y0(),kf),c7),2075),n)return n}catch(t){if(t=sr(t),X(t,101))e=t,Ffe((Lt(),e));else throw $(t)}return new aU}function Jxn(){Gz();var e,n;try{if(n=u(r0e((y0(),kf),hf),2002),n)return n}catch(t){if(t=sr(t),X(t,101))e=t,Ffe((Lt(),e));else throw $(t)}return new uw}function Hxn(){J$e();var e,n;try{if(n=u(r0e((y0(),kf),gg),2084),n)return n}catch(t){if(t=sr(t),X(t,101))e=t,Ffe((Lt(),e));else throw $(t)}return new oC}function Gxn(e,n,t){var i,r;return r=e.e,e.e=n,(e.Db&4)!=0&&(e.Db&1)==0&&(i=new Ir(e,1,4,r,n),t?t.lj(i):t=i),r!=n&&(n?t=D8(e,Oz(e,n),t):t=D8(e,e.a,t)),t}function zHe(){t$.call(this),this.e=-1,this.a=!1,this.p=Xr,this.k=-1,this.c=-1,this.b=-1,this.g=!1,this.f=-1,this.j=-1,this.n=-1,this.i=-1,this.d=-1,this.o=Xr}function qxn(e,n){var t,i,r;if(i=e.b.d.d,e.a||(i+=e.b.d.a),r=n.b.d.d,n.a||(r+=n.b.d.a),t=ki(i,r),t==0){if(!e.a&&n.a)return-1;if(!n.a&&e.a)return 1}return t}function Uxn(e,n){var t,i,r;if(i=e.b.b.d,e.a||(i+=e.b.b.a),r=n.b.b.d,n.a||(r+=n.b.b.a),t=ki(i,r),t==0){if(!e.a&&n.a)return-1;if(!n.a&&e.a)return 1}return t}function Xxn(e,n){var t,i,r;if(i=e.b.g.d,e.a||(i+=e.b.g.a),r=n.b.g.d,n.a||(r+=n.b.g.a),t=ki(i,r),t==0){if(!e.a&&n.a)return-1;if(!n.a&&e.a)return 1}return t}function Vde(){Vde=Y,ztn=Eo(qt(qt(qt(new or,(zr(),no),(Ur(),Q3e)),no,W3e),Pc,Z3e),Pc,z3e),Jtn=qt(qt(new or,no,I3e),no,F3e),Ftn=Eo(new or,Pc,H3e)}function Kxn(e){var n,t,i,r,c;for(n=u(C(e,(me(),ox)),92),c=e.n,i=n.Bc().Jc();i.Ob();)t=u(i.Pb(),318),r=t.i,r.c+=c.a,r.d+=c.b,t.c?hXe(t):dXe(t);he(e,ox,null)}function Vxn(e,n,t){var i,r;switch(r=e.b,i=r.d,n.g){case 1:return-i.d-t;case 2:return r.o.a+i.c+t;case 3:return r.o.b+i.a+t;case 4:return-i.b-t;default:return-1}}function FHe(e,n){var t,i;for(i=new L(n);i.a0&&(o=(c&oi)%e.d.length,r=W0e(e,o,c,n),r)?(l=r.ld(t),l):(i=e.ak(c,n,t),e.c.Ec(i),null)}function Wde(e,n){var t,i,r,c;switch(Dd(e,n).Il()){case 3:case 2:{for(t=fv(n),r=0,c=t.i;r=0;i--)if(bn(e[i].d,n)||bn(e[i].d,t)){e.length>=i+1&&e.splice(0,i+1);break}return e}function BO(e,n){var t;return uu(e)&&uu(n)&&(t=e/n,wN0&&(e.b+=2,e.a+=i):(e.b+=1,e.a+=k.Math.min(i,r))}function KHe(e,n){var t,i;if(i=!1,$r(n)&&(i=!0,C4(e,new y2(Pt(n)))),i||X(n,242)&&(i=!0,C4(e,(t=qK(u(n,242)),new k3(t)))),!i)throw $(new MX(U2e))}function hAn(e,n,t,i){var r,c,o;return r=new L1(e.e,1,10,(o=n.c,X(o,88)?u(o,29):(yn(),jf)),(c=t.c,X(c,88)?u(c,29):(yn(),jf)),Ld(e,n),!1),i?i.lj(r):i=r,i}function n0e(e){var n,t;switch(u(C(_r(e),(Ne(),Z5e)),420).g){case 0:return n=e.n,t=e.o,new je(n.a+t.a/2,n.b+t.b/2);case 1:return new wc(e.n);default:return null}}function zO(){zO=Y,YJ=new Lj(ma,0),Tve=new Lj("LEFTUP",1),Nve=new Lj("RIGHTUP",2),Cve=new Lj("LEFTDOWN",3),Ove=new Lj("RIGHTDOWN",4),sie=new Lj("BALANCED",5)}function dAn(e,n,t){var i,r,c;if(i=ki(e.a[n.p],e.a[t.p]),i==0){if(r=u(C(n,(me(),Cy)),16),c=u(C(t,Cy),16),r.Gc(t))return-1;if(c.Gc(n))return 1}return i}function bAn(e){switch(e.g){case 1:return new L_;case 2:return new Ik;case 3:return new R5;case 0:return null;default:throw $(new Gn(Qee+(e.f!=null?e.f:""+e.g)))}}function t0e(e,n,t){switch(n){case 1:!e.n&&(e.n=new pe(ju,e,1,7)),kt(e.n),!e.n&&(e.n=new pe(ju,e,1,7)),er(e.n,u(t,18));return;case 2:X9(e,Pt(t));return}E1e(e,n,t)}function i0e(e,n,t){switch(n){case 3:Dw(e,ne(re(t)));return;case 4:Iw(e,ne(re(t)));return;case 5:Os(e,ne(re(t)));return;case 6:Ns(e,ne(re(t)));return}t0e(e,n,t)}function gz(e,n,t){var i,r,c;c=(i=new yX,i),r=za(c,n,null),r&&r.mj(),Mo(c,t),Et((!e.c&&(e.c=new pe(vp,e,12,10)),e.c),c),Td(c,0),O2(c,1),_d(c,!0),Id(c,!0)}function r0e(e,n){var t,i,r;return t=Dj(e.i,n),X(t,241)?(r=u(t,241),r.wi()==null,r.ti()):X(t,493)?(i=u(t,1999),r=i.b,r):null}function gAn(e,n,t,i){var r,c;return Nt(n),Nt(t),c=u(eE(e.d,n),15),ERe(!!c,"Row %s not in %s",n,e.e),r=u(eE(e.b,t),15),ERe(!!r,"Column %s not in %s",t,e.c),jze(e,c.a,r.a,i)}function wAn(e){var n,t,i,r,c,o;for(t=null,r=e,c=0,o=r.length;c1||l==-1?(c=u(f,16),r.Wb(Zjn(e,c))):r.Wb(zW(e,u(f,57)))))}function EAn(e,n,t,i){yMe();var r=Xne;function c(){for(var o=0;o0)return!1;return!0}function AAn(e){switch(u(C(e.b,(Ne(),U5e)),381).g){case 1:Zi(So(ou(new wn(null,new pn(e.d,16)),new Zg),new t_),new nM);break;case 2:iIn(e);break;case 0:YCn(e)}}function MAn(e,n,t){var i,r,c;for(i=t,!i&&(i=new i4),i.Tg("Layout",e.a.c.length),c=new L(e.a);c.aXee)return t;r>-1e-6&&++t}return t}function pz(e,n,t){if(X(n,271))return eNn(e,u(n,85),t);if(X(n,276))return Dxn(e,u(n,276),t);throw $(new Gn(u7+Fa(new Su(z(B(Ar,1),On,1,5,[n,t])))))}function mz(e,n,t){if(X(n,271))return nNn(e,u(n,85),t);if(X(n,276))return Ixn(e,u(n,276),t);throw $(new Gn(u7+Fa(new Su(z(B(Ar,1),On,1,5,[n,t])))))}function u0e(e,n){var t;n!=e.b?(t=null,e.b&&(t=IR(e.b,e,-4,t)),n&&(t=K4(n,e,-4,t)),t=pFe(e,n,t),t&&t.mj()):(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,3,n,n))}function WHe(e,n){var t;n!=e.f?(t=null,e.f&&(t=IR(e.f,e,-1,t)),n&&(t=K4(n,e,-1,t)),t=mFe(e,n,t),t&&t.mj()):(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,0,n,n))}function DAn(e,n,t,i){var r,c,o,l;return Fs(e.e)&&(r=n.Jk(),l=n.kd(),c=t.kd(),o=M0(e,1,r,l,c,r.Hk()?T8(e,r,c,X(r,103)&&(u(r,19).Bb&Ec)!=0):-1,!0),i?i.lj(o):i=o),i}function ZHe(e){var n,t,i;if(e==null)return null;if(t=u(e,16),t.dc())return"";for(i=new pd,n=t.Jc();n.Ob();)Bc(i,(Ei(),Pt(n.Pb()))),i.a+=" ";return kK(i,i.a.length-1)}function eGe(e){var n,t,i;if(e==null)return null;if(t=u(e,16),t.dc())return"";for(i=new pd,n=t.Jc();n.Ob();)Bc(i,(Ei(),Pt(n.Pb()))),i.a+=" ";return kK(i,i.a.length-1)}function IAn(e,n){var t,i,r,c,o;for(c=new L(n.a);c.a0&&ic(e,e.length-1)==33)try{return n=gUe(of(e,0,e.length-1)),n.e==null}catch(t){if(t=sr(t),!X(t,32))throw $(t)}return!1}function $An(e,n,t){var i,r,c;switch(i=_r(n),r=GB(i),c=new Qu,gu(c,n),t.g){case 1:xr(c,TO(q4(r)));break;case 2:xr(c,q4(r))}return he(c,(Ne(),vm),re(C(e,vm))),c}function o0e(e){var n,t;return n=u(tt(new qn(Vn(rr(e.a).a.Jc(),new ee))),17),t=u(tt(new qn(Vn(Ni(e.a).a.Jc(),new ee))),17),Re($e(C(n,(me(),Hd))))||Re($e(C(t,Hd)))}function z2(){z2=Y,ZN=new oT("ONE_SIDE",0),GJ=new oT("TWO_SIDES_CORNER",1),qJ=new oT("TWO_SIDES_OPPOSING",2),HJ=new oT("THREE_SIDES",3),JJ=new oT("FOUR_SIDES",4)}function iGe(e,n){var t,i,r,c;for(c=new Te,r=0,i=n.Jc();i.Ob();){for(t=ve(u(i.Pb(),15).a+r);t.a=e.f)break;Hn(c.c,t)}return c}function RAn(e){var n,t;for(t=new L(e.e.b);t.a0&&SHe(this,this.c-1,(De(),et)),this.c0&&e[0].length>0&&(this.c=Re($e(C(_r(e[0][0]),(me(),Xve))))),this.a=oe(don,Se,2079,e.length,0,2),this.b=oe(bon,Se,2080,e.length,0,2),this.d=new aFe}function JAn(e){return e.c.length==0?!1:(vn(0,e.c.length),u(e.c[0],17)).c.i.k==(zn(),dr)?!0:G3(So(new wn(null,new pn(e,16)),new kk),new o_)}function uGe(e,n){var t,i,r,c,o,l,f;for(l=q2(n),c=n.f,f=n.g,o=k.Math.sqrt(c*c+f*f),r=0,i=new L(l);i.a=0?(t=BO(e,tF),i=xQ(e,tF)):(n=Bb(e,1),t=BO(n,5e8),i=xQ(n,5e8),i=mc(Gh(i,1),Rr(e,1))),hh(Gh(i,32),Rr(t,Ic))}function eMn(e,n,t,i){var r,c,o,l,f;for(r=null,c=0,l=new L(n);l.a1;n>>=1)(n&1)!=0&&(i=H3(i,t)),t.d==1?t=H3(t,t):t=new xJe(ZXe(t.a,t.d,oe($t,ni,30,t.d<<1,15,1)));return i=H3(i,t),i}function w0e(){w0e=Y;var e,n,t,i;for(qme=oe(Jr,Jc,30,25,15,1),Ume=oe(Jr,Jc,30,33,15,1),i=152587890625e-16,n=32;n>=0;n--)Ume[n]=i,i*=.5;for(t=1,e=24;e>=0;e--)qme[e]=t,t*=.5}function cMn(e){var n,t;if(Re($e(ye(e,(Ne(),pm))))){for(t=new qn(Vn(H0(e).a.Jc(),new ee));ht(t);)if(n=u(tt(t),85),Hw(n)&&Re($e(ye(n,kg))))return!0}return!1}function lGe(e){var n,t,i,r;for(n=new Si,t=new Si,r=St(e,0);r.b!=r.d.c;)i=u(jt(r),12),i.e.c.length==0?Xi(t,i,t.c.b,t.c):Xi(n,i,n.c.b,n.c);return Ks(n).Fc(t),n}function fGe(e,n){var t,i,r;hr(e.f,n)&&(n.b=e,i=n.c,wu(e.j,i,0)!=-1||Ce(e.j,i),r=n.d,wu(e.j,r,0)!=-1||Ce(e.j,r),t=n.a.b,t.c.length!=0&&(!e.i&&(e.i=new TJe(e)),_7n(e.i,t)))}function uMn(e){var n,t,i,r,c;return t=e.c.d,i=t.j,r=e.d.d,c=r.j,i==c?t.p=0&&bn(e.substr(n,3),"GMT")||n>=0&&bn(e.substr(n,3),"UTC"))&&(t[0]=n+3),Zbe(e,t,i)}function sMn(e,n){var t,i,r,c,o;for(c=e.g.a,o=e.g.b,i=new L(e.d);i.at;c--)e[c]|=n[c-t-1]>>>o,e[c-1]=n[c-t-1]<0&&Wu(e.g,n,e.g,n+i,l),o=t.Jc(),e.i+=i,r=0;r>4&15,c=e[i]&15,o[r++]=R8e[t],o[r++]=R8e[c];return gh(o,0,o.length)}function Xo(e){var n,t;return e>=Ec?(n=pN+(e-Ec>>10&1023)&yr,t=56320+(e-Ec&1023)&yr,String.fromCharCode(n)+(""+String.fromCharCode(t))):String.fromCharCode(e&yr)}function mMn(e,n){h2();var t,i,r,c;return r=u(u(pi(e.r,n),22),83),r.gc()>=2?(i=u(r.Jc().Pb(),115),t=e.u.Gc((ps(),nA)),c=e.u.Gc(qy),!i.a&&!t&&(r.gc()==2||c)):!1}function bGe(e,n,t,i,r){var c,o,l;for(c=rXe(e,n,t,i,r),l=!1;!c;)Cz(e,r,!0),l=!0,c=rXe(e,n,t,i,r);l&&Cz(e,r,!1),o=YY(r),o.c.length!=0&&(e.d&&e.d.Fg(o),bGe(e,r,t,i,o))}function kz(){kz=Y,Fre=new v$("NODE_SIZE_REORDERER",0),Rre=new v$("INTERACTIVE_NODE_REORDERER",1),zre=new v$("MIN_SIZE_PRE_PROCESSOR",2),Bre=new v$("MIN_SIZE_POST_PROCESSOR",3)}function jz(){jz=Y,Mce=new zj(ma,0),c8e=new zj("DIRECTED",1),o8e=new zj("UNDIRECTED",2),i8e=new zj("ASSOCIATION",3),u8e=new zj("GENERALIZATION",4),r8e=new zj("DEPENDENCY",5)}function vMn(e,n){var t;if(!Ia(e))throw $(new Uc(BWe));switch(t=Ia(e),n.g){case 1:return-(e.j+e.f);case 2:return e.i-t.g;case 3:return e.j-t.f;case 4:return-(e.i+e.g)}return 0}function yMn(e,n,t){var i,r,c;return i=n.Jk(),c=n.kd(),r=i.Hk()?M0(e,4,i,c,null,T8(e,i,c,X(i,103)&&(u(i,19).Bb&Ec)!=0),!0):M0(e,i.rk()?2:1,i,c,i.gk(),-1,!0),t?t.lj(r):t=r,t}function v8(e,n){var t,i;for(_n(n),i=e.b.c.length,Ce(e.b,n);i>0;){if(t=i,i=(i-1)/2|0,e.a.Le(Le(e.b,i),n)<=0)return ol(e.b,t,n),!0;ol(e.b,t,Le(e.b,i))}return ol(e.b,i,n),!0}function v0e(e,n,t,i){var r,c;if(r=0,t)r=BB(e.a[t.g][n.g],i);else for(c=0;c=l)}function gGe(e){switch(e.g){case 0:return new U_;case 1:return new _M;default:throw $(new Gn("No implementation is available for the width approximator "+(e.f!=null?e.f:""+e.g)))}}function y0e(e,n,t,i){var r;if(r=!1,$r(i)&&(r=!0,C9(n,t,Pt(i))),r||o2(i)&&(r=!0,y0e(e,n,t,i)),r||X(i,242)&&(r=!0,Hb(n,t,u(i,242))),!r)throw $(new MX(U2e))}function jMn(e,n){var t,i,r;if(t=n.ni(e.a),t&&(r=wa((!t.b&&(t.b=new Hs((yn(),Ac),Iu,t)),t.b),af),r!=null)){for(i=1;i<(ls(),s7e).length;++i)if(bn(s7e[i],r))return i}return 0}function EMn(e,n){var t,i,r;if(t=n.ni(e.a),t&&(r=wa((!t.b&&(t.b=new Hs((yn(),Ac),Iu,t)),t.b),af),r!=null)){for(i=1;i<(ls(),l7e).length;++i)if(bn(l7e[i],r))return i}return 0}function wGe(e,n){var t,i,r,c;if(_n(n),c=e.a.gc(),c0?1:0;c.a[r]!=t;)c=c.a[r],r=e.a.Le(t.d,c.d)>0?1:0;c.a[r]=i,i.b=t.b,i.a[0]=t.a[0],i.a[1]=t.a[1],t.a[0]=null,t.a[1]=null}function AMn(e){var n,t,i,r;for(n=new Te,t=oe(ts,pa,30,e.a.c.length,16,1),$fe(t,t.length),r=new L(e.a);r.a0&&KXe((vn(0,t.c.length),u(t.c[0],25)),e),t.c.length>1&&KXe(u(Le(t,t.c.length-1),25),e),n.Ug()}function CMn(e){ps();var n,t;return n=Mi(Z1,z(B(sG,1),ke,280,0,[gb])),!(gO(_R(n,e))>1||(t=Mi(nA,z(B(sG,1),ke,280,0,[eA,qy])),gO(_R(t,e))>1))}function j0e(e,n){var t;t=lo((y0(),kf),e),X(t,493)?Kc(kf,e,new VCe(this,n)):Kc(kf,e,this),dW(this,n),n==(d9(),Y8e)?(this.wb=u(this,2e3),u(n,2002)):this.wb=(x0(),Rn)}function TMn(e){var n,t,i;if(e==null)return null;for(n=null,t=0;tc}function yGe(e,n){var t,i,r;if(S0e(e,n))return!0;for(i=new L(n);i.a=r||n<0)throw $(new jo(Mne+n+dg+r));if(t>=r||t<0)throw $(new jo(Cne+t+dg+r));return n!=t?i=(c=e.Aj(t),e.oj(n,c),c):i=e.vj(t),i}function jGe(e){var n,t,i;if(i=e,e)for(n=0,t=e.Bh();t;t=t.Bh()){if(++n>$Z)return jGe(t);if(i=t,t==e)throw $(new Uc("There is a cycle in the containment hierarchy of "+e))}return i}function Fa(e){var n,t,i;for(i=new Qb(To,"[","]"),t=e.Jc();t.Ob();)n=t.Pb(),I1(i,ue(n)===ue(e)?"(this Collection)":n==null?Vo:su(n));return i.a?i.e.length==0?i.a.a:i.a.a+(""+i.e):i.c}function S0e(e,n){var t,i;if(i=!1,n.gc()<2)return!1;for(t=0;t1&&(e.j.b+=e.e)):(e.j.a+=t.a,e.j.b=k.Math.max(e.j.b,t.b),e.d.c.length>1&&(e.j.a+=e.e))}function F0(){F0=Y,Cin=z(B(xc,1),qu,64,0,[(De(),Xn),et,bt]),Min=z(B(xc,1),qu,64,0,[et,bt,Kn]),Tin=z(B(xc,1),qu,64,0,[bt,Kn,Xn]),Oin=z(B(xc,1),qu,64,0,[Kn,Xn,et])}function SGe(e){var n,t,i,r,c,o,l,f,h;for(this.a=XJe(e),this.b=new Te,t=e,i=0,r=t.length;izK(e.d).c?(e.i+=e.g.c,CQ(e.d)):zK(e.d).c>zK(e.g).c?(e.e+=e.d.c,CQ(e.g)):(e.i+=EDe(e.g),e.e+=EDe(e.d),CQ(e.g),CQ(e.d))}function RMn(e,n,t){var i,r,c,o;for(c=n.q,o=n.r,new Gb((ha(),sb),n,c,1),new Gb(sb,c,o,1),r=new L(t);r.al&&(f=l/i),r>c&&(h=c/r),o=k.Math.min(f,h),e.a+=o*(n.a-e.a),e.b+=o*(n.b-e.b)}function JMn(e,n,t,i,r){var c,o;for(o=!1,c=u(Le(t.b,0),26);q_n(e,n,c,i,r)&&(o=!0,CAn(t,c),t.b.c.length!=0);)c=u(Le(t.b,0),26);return t.b.c.length==0&&$O(t.j,t),o&&hz(n.q),o}function A0e(e,n,t,i){var r,c;return t==0?(!e.o&&(e.o=new os((Gu(),a1),Qd,e,0)),U$(e.o,n,i)):(c=u(An((r=u(Un(e,16),29),r||e.fi()),t),69),c.uk().yk(e,Lo(e),t-dt(e.fi()),n,i))}function dW(e,n){var t;n!=e.sb?(t=null,e.sb&&(t=u(e.sb,52).Qh(e,1,rA,t)),n&&(t=u(n,52).Oh(e,1,rA,t)),t=B1e(e,n,t),t&&t.mj()):(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,4,n,n))}function CGe(e,n){var t,i,r,c;if(n)r=$1(n,"x"),t=new aSe(e),X3(t.a,(_n(r),r)),c=$1(n,"y"),i=new hSe(e),K3(i.a,(_n(c),c));else throw $(new oh("All edge sections need an end point."))}function TGe(e,n){var t,i,r,c;if(n)r=$1(n,"x"),t=new sSe(e),V3(t.a,(_n(r),r)),c=$1(n,"y"),i=new lSe(e),Y3(i.a,(_n(c),c));else throw $(new oh("All edge sections need a start point."))}function HMn(e,n){var t,i,r,c,o,l,f;for(i=Vze(e),c=0,l=i.length;c>22-n,r=e.h<>22-n):n<44?(t=0,i=e.l<>44-n):(t=0,i=0,r=e.l<=Rd?"error":i>=900?"warn":i>=800?"info":"log"),wIe(t,e.a),e.b&&Mbe(n,t,e.b,"Exception: ",!0))}function IGe(e,n){var t,i,r,c,o;for(r=n==1?Mte:Ate,i=r.a.ec().Jc();i.Ob();)for(t=u(i.Pb(),86),o=u(pi(e.f.c,t),22).Jc();o.Ob();)c=u(o.Pb(),49),Ce(e.b.b,u(c.b,82)),Ce(e.b.a,u(c.b,82).d)}function _Ge(e,n,t,i){var r,c,o,l,f;switch(f=e.b,c=n.d,o=c.j,l=xde(o,f.d[o.g],t),r=gi(pc(c.n),c.a),c.j.g){case 3:case 1:l.a+=r.a;break;case 2:l.b+=r.b;break;case 4:l.b+=r.b}Xi(i,l,i.c.b,i.c)}function XMn(e,n){var t,i,r,c;for(c=n.b.j,e.a=oe($t,ni,30,c.c.length,15,1),r=0,i=0;ie)throw $(new Gn("k must be smaller than n"));return n==0||n==e?1:e==0?0:Zde(e)/(Zde(n)*Zde(e-n))}function M0e(e,n){var t,i,r,c;for(t=new AK(e);t.g==null&&!t.c?mae(t):t.g==null||t.i!=0&&u(t.g[t.i-1],50).Ob();)if(c=u(Tz(t),57),X(c,174))for(i=u(c,174),r=0;r>4],n[t*2+1]=CG[c&15];return gh(n,0,n.length)}function uCn(e){var n,t,i;switch(i=e.c.length,i){case 0:return TV(),Xen;case 1:return n=u(wqe(new L(e)),45),Bpn(n.jd(),n.kd());default:return t=u(Ra(e,oe(wg,eF,45,e.c.length,0,1)),175),new ise(t)}}function Pd(e,n){switch(n.g){case 1:return j4(e.j,(ss(),x3e));case 2:return j4(e.j,(ss(),E3e));case 3:return j4(e.j,(ss(),M3e));case 4:return j4(e.j,(ss(),C3e));default:return jn(),jn(),Sc}}function oCn(e,n){var t,i,r;t=_3n(n,e.e),i=u(Bn(e.g.f,t),15).a,r=e.a.c.length-1,e.a.c.length!=0&&u(Le(e.a,r),295).c==i?(++u(Le(e.a,r),295).a,++u(Le(e.a,r),295).b):Ce(e.a,new MOe(i))}function J0(){J0=Y,eln=(Xt(),Fy),nln=Vd,Ysn=Cg,Qsn=Yv,Wsn=ab,Vsn=Vv,Vye=LD,Zsn=Nm,Dre=(Gbe(),Rsn),Ire=Bsn,Qye=Hsn,_re=Usn,Wye=Gsn,Zye=qsn,Yye=zsn,FH=Fsn,JH=Jsn,SD=Xsn,e6e=Ksn,Kye=$sn}function PGe(e,n){var t,i,r,c,o;if(e.e<=n||dyn(e,e.g,n))return e.g;for(c=e.r,i=e.g,o=e.r,r=(c-i)/2+i;i+11&&(e.e.b+=e.a)):(e.e.a+=t.a,e.e.b=k.Math.max(e.e.b,t.b),e.d.c.length>1&&(e.e.a+=e.a))}function fCn(e){var n,t,i,r;switch(r=e.i,n=r.b,i=r.j,t=r.g,r.a.g){case 0:t.a=(e.g.b.o.a-i.a)/2;break;case 1:t.a=n.d.n.a+n.d.a.a;break;case 2:t.a=n.d.n.a+n.d.a.a-i.a;break;case 3:t.b=n.d.n.b+n.d.a.b}}function aCn(e,n,t){var i,r,c;for(r=new qn(Vn(bh(t).a.Jc(),new ee));ht(r);)i=u(tt(r),17),!cc(i)&&!(!cc(i)&&i.c.i.c==i.d.i.c)&&(c=OUe(e,i,t,new pxe),c.c.length>1&&Hn(n.c,c))}function BGe(e,n,t,i,r){if(ii&&(e.a=i),e.br&&(e.b=r),e}function hCn(e){if(X(e,144))return INn(u(e,144));if(X(e,233))return Gjn(u(e,233));if(X(e,21))return qMn(u(e,21));throw $(new Gn(u7+Fa(new Su(z(B(Ar,1),On,1,5,[e])))))}function dCn(e,n,t,i,r){var c,o,l;for(c=!0,o=0;o>>r|t[o+i+1]<>>r,++o}return c}function N0e(e,n,t,i){var r,c,o;if(n.k==(zn(),dr)){for(c=new qn(Vn(rr(n).a.Jc(),new ee));ht(c);)if(r=u(tt(c),17),o=r.c.i.k,o==dr&&e.c.a[r.c.i.c.p]==i&&e.c.a[n.c.p]==t)return!0}return!1}function bCn(e,n){var t,i,r,c;return n&=63,t=e.h&G1,n<22?(c=t>>>n,r=e.m>>n|t<<22-n,i=e.l>>n|e.m<<22-n):n<44?(c=0,r=t>>>n-22,i=e.m>>n-22|e.h<<44-n):(c=0,r=0,i=t>>>n-44),_o(i&Ls,r&Ls,c&G1)}function zGe(e,n,t,i){var r;this.b=i,this.e=e==(eg(),Mx),r=n[t],this.d=g2(ts,[Se,pa],[171,30],16,[r.length,r.length],2),this.a=g2($t,[Se,ni],[54,30],15,[r.length,r.length],2),this.c=new h0e(n,t)}function gCn(e){var n,t,i;for(e.k=new Sae((De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])).length,e.j.c.length),i=new L(e.j);i.a=t)return k8(e,n,i.p),!0;return!1}function uv(e,n,t,i){var r,c,o,l,f,h;for(o=t.length,c=0,r=-1,h=qRe((Yn(n,e.length+1),e.substr(n)),(KK(),Hme)),l=0;lc&&xvn(h,qRe(t[l],Hme))&&(r=l,c=f);return r>=0&&(i[0]=n+c),r}function mCn(e,n,t){var i,r,c,o,l,f,h,b;c=e.d.p,l=c.e,f=c.r,e.g=new TT(f),o=e.d.o.c.p,i=o>0?l[o-1]:oe(c1,Bd,9,0,0,1),r=l[o],h=ot?F0e(e,t,"start index"):n<0||n>t?F0e(n,t,"end index"):rS("end index (%s) must not be less than start index (%s)",z(B(Ar,1),On,1,5,[ve(n),ve(e)]))}function qGe(e,n){var t,i,r,c;for(i=0,r=e.length;i0&&UGe(e,c,t));n.p=0}function jCn(e){var n,t,i,r;for(n=Fb(Kt(new il("Predicates."),"and"),40),t=!0,r=new qc(e);r.b=0?e.hi(r):q0e(e,i);else throw $(new Gn(W0+i.ve()+_S));else throw $(new Gn(YWe+n+QWe));else Fl(e,t,i)}function D0e(e){var n,t;if(t=null,n=!1,X(e,210)&&(n=!0,t=u(e,210).a),n||X(e,265)&&(n=!0,t=""+u(e,265).a),n||X(e,479)&&(n=!0,t=""+u(e,479).a),!n)throw $(new MX(U2e));return t}function I0e(e,n,t){var i,r,c,o,l,f;for(f=Po(e.e.Ah(),n),i=0,l=e.i,r=u(e.g,122),o=0;o=e.d.b.c.length&&(n=new Xu(e.d),n.p=i.p-1,Ce(e.d.b,n),t=new Xu(e.d),t.p=i.p,Ce(e.d.b,t)),Or(i,u(Le(e.d.b,i.p),25))}function xCn(e){var n,t,i,r;for(t=new Si,fc(t,e.o),i=new $P;t.b!=0;)n=u(t.b==0?null:(at(t.b!=0),$l(t,t.a.a)),500),r=PVe(e,n,!0),r&&Ce(i.a,n);for(;i.a.c.length!=0;)n=u(T1e(i),500),PVe(e,n,!1)}function Ge(e){var n;this.c=new Si,this.f=e.e,this.e=e.d,this.i=e.g,this.d=e.c,this.b=e.b,this.k=e.j,this.a=e.a,e.i?this.j=e.i:this.j=(n=u(sa(Qa),10),new _l(n,u(_f(n,n.length),10),0)),this.g=e.f}function cg(){cg=Y,o9e=new h4(vS,0),Sr=new h4("BOOLEAN",1),hc=new h4("INT",2),By=new h4("STRING",3),ec=new h4("DOUBLE",4),Ri=new h4("ENUM",5),Ry=new h4("ENUMSET",6),Wa=new h4("OBJECT",7)}function VE(e,n){var t,i,r,c,o;i=k.Math.min(e.c,n.c),c=k.Math.min(e.d,n.d),r=k.Math.max(e.c+e.b,n.c+n.b),o=k.Math.max(e.d+e.a,n.d+n.a),r=(r/2|0))for(this.e=i?i.c:null,this.d=r;t++0;)She(this);this.b=n,this.a=null}function CCn(e,n){var t,i;n.a?QNn(e,n):(t=u(RX(e.b,n.b),60),t&&t==e.a[n.b.f]&&t.a&&t.a!=n.b.a&&t.c.Ec(n.b),i=u($X(e.b,n.b),60),i&&e.a[i.f]==n.b&&i.a&&i.a!=n.b.a&&n.b.c.Ec(i),DK(e.b,n.b))}function ZGe(e,n){var t,i;if(t=u(zc(e.b,n),127),u(u(pi(e.r,n),22),83).dc()){t.n.b=0,t.n.c=0;return}t.n.b=e.C.b,t.n.c=e.C.c,e.A.Gc((Vs(),Og))&&TXe(e,n),i=dSn(e,n),OW(e,n)==(nv(),db)&&(i+=2*e.w),t.a.a=i}function eqe(e,n){var t,i;if(t=u(zc(e.b,n),127),u(u(pi(e.r,n),22),83).dc()){t.n.d=0,t.n.a=0;return}t.n.d=e.C.d,t.n.a=e.C.a,e.A.Gc((Vs(),Og))&&OXe(e,n),i=hSn(e,n),OW(e,n)==(nv(),db)&&(i+=2*e.w),t.a.b=i}function TCn(e,n){var t,i,r,c;for(c=new Te,i=new L(n);i.ai&&(Yn(n-1,e.length),e.charCodeAt(n-1)<=32);)--n;return i>0||nt.a&&(i.Gc((rg(),Gx))?r=(n.a-t.a)/2:i.Gc(qx)&&(r=n.a-t.a)),n.b>t.b&&(i.Gc((rg(),Xx))?c=(n.b-t.b)/2:i.Gc(Ux)&&(c=n.b-t.b)),k0e(e,r,c)}function cqe(e,n,t,i,r,c,o,l,f,h,b,p,y){X(e.Cb,88)&&H2(Ms(u(e.Cb,88)),4),Mo(e,t),e.f=o,l8(e,l),f8(e,f),o8(e,h),s8(e,b),_d(e,p),a8(e,y),Id(e,!0),Td(e,r),e.Xk(c),ng(e,n),i!=null&&(e.i=null,EB(e,i))}function F0e(e,n,t){if(e<0)return rS(uYe,z(B(Ar,1),On,1,5,[t,ve(e)]));if(n<0)throw $(new Gn(oYe+n));return rS("%s (%s) must not be greater than size (%s)",z(B(Ar,1),On,1,5,[t,ve(e),ve(n)]))}function J0e(e,n,t,i,r,c){var o,l,f,h;if(o=i-t,o<7){$jn(n,t,i,c);return}if(f=t+r,l=i+r,h=f+(l-f>>1),J0e(n,e,f,h,-r,c),J0e(n,e,h,l,-r,c),c.Le(e[h-1],e[h])<=0){for(;t=0?e.$h(c,t):ybe(e,r,t);else throw $(new Gn(W0+r.ve()+_S));else throw $(new Gn(YWe+n+QWe));else Jl(e,i,r,t)}function uqe(e){var n,t;if(e.f){for(;e.n>0;){if(n=u(e.k.Xb(e.n-1),75),t=n.Jk(),X(t,103)&&(u(t,19).Bb&Bu)!=0&&(!e.e||t.nk()!=U7||t.Jj()!=0)&&n.kd()!=null)return!0;--e.n}return!1}else return e.n>0}function oqe(e){var n,t,i,r;if(t=u(e,52).Yh(),t)try{if(i=null,n=E8((y0(),kf),WXe(qjn(t))),n&&(r=n.Zh(),r&&(i=r.Dl(Ibn(t.e)))),i&&i!=e)return oqe(i)}catch(c){if(c=sr(c),!X(c,63))throw $(c)}return e}function qCn(e,n,t){var i,r,c;t.Tg("Remove overlaps",1),t.bh(n,Ype),i=u(ye(n,(B3(),Gv)),26),e.f=i,e.a=$Q(u(ye(n,(J0(),SD)),303)),r=re(ye(n,(Xt(),Vd))),Q5(e,(_n(r),r)),c=q2(i),vVe(e,n,c,t),t.bh(n,_F)}function UCn(e){var n,t,i;if(Re($e(ye(e,(Xt(),ID))))){for(i=new Te,t=new qn(Vn(H0(e).a.Jc(),new ee));ht(t);)n=u(tt(t),85),Hw(n)&&Re($e(ye(n,gce)))&&Hn(i.c,n);return i}else return jn(),jn(),Sc}function sqe(e){if(!e)return eAe(),Wen;var n=e.valueOf?e.valueOf():e;if(n!==e){var t=ite[typeof n];return t?t(n):F1e(typeof n)}else return e instanceof Array||e instanceof k.Array?new n9(e):new i9(e)}function lqe(e,n,t){var i,r,c;switch(c=e.o,i=u(zc(e.p,t),253),r=i.i,r.b=QE(i),r.a=YE(i),r.b=k.Math.max(r.b,c.a),r.b>c.a&&!n&&(r.b=c.a),r.c=-(r.b-c.a)/2,t.g){case 1:r.d=-r.a;break;case 3:r.d=c.b}HW(i),GW(i)}function fqe(e,n,t){var i,r,c;switch(c=e.o,i=u(zc(e.p,t),253),r=i.i,r.b=QE(i),r.a=YE(i),r.a=k.Math.max(r.a,c.b),r.a>c.b&&!n&&(r.a=c.b),r.d=-(r.a-c.b)/2,t.g){case 4:r.c=-r.b;break;case 2:r.c=c.a}HW(i),GW(i)}function XCn(e,n){var t,i,r;return X(n.g,9)&&u(n.g,9).k==(zn(),wr)?Ki:(r=I4(n),r?k.Math.max(0,e.b/2-.5):(t=J3(n),t?(i=ne(re($2(t,(Ne(),xg)))),k.Math.max(0,i/2-.5)):Ki))}function KCn(e,n){var t,i,r;return X(n.g,9)&&u(n.g,9).k==(zn(),wr)?Ki:(r=I4(n),r?k.Math.max(0,e.b/2-.5):(t=J3(n),t?(i=ne(re($2(t,(Ne(),xg)))),k.Math.max(0,i/2-.5)):Ki))}function VCn(e,n){var t,i,r,c,o;if(!n.dc()){if(r=u(n.Xb(0),132),n.gc()==1){KUe(e,r,r,1,0,n);return}for(t=1;t0)try{r=hl(n,Xr,oi)}catch(c){throw c=sr(c),X(c,131)?(i=c,$(new uB(i))):$(c)}return t=(!e.a&&(e.a=new hX(e)),e.a),r=0?u(K(t,r),57):null}function WCn(e,n){if(e<0)return rS(uYe,z(B(Ar,1),On,1,5,["index",ve(e)]));if(n<0)throw $(new Gn(oYe+n));return rS("%s (%s) must be less than size (%s)",z(B(Ar,1),On,1,5,["index",ve(e),ve(n)]))}function ZCn(e){var n,t,i,r,c;if(e==null)return Vo;for(c=new Qb(To,"[","]"),t=e,i=0,r=t.length;i=0?e.Ih(t,!0,!0):Gw(e,r,!0),163)),u(i,219).Xl(n);else throw $(new Gn(W0+n.ve()+_S))}function U0e(e){var n,t;return e>-0x800000000000&&e<0x800000000000?e==0?0:(n=e<0,n&&(e=-e),t=sc(k.Math.floor(k.Math.log(e)/.6931471805599453)),(!n||e!=k.Math.pow(2,t))&&++t,t):TFe(Pu(e))}function fTn(e){var n,t,i,r,c,o,l;for(c=new zh,t=new L(e);t.a2&&l.e.b+l.j.b<=2&&(r=l,i=o),c.a.yc(r,c),r.q=i);return c}function aTn(e,n,t){t.Tg("Eades radial",1),t.bh(n,_F),e.d=u(ye(n,(B3(),Gv)),26),e.c=ne(re(ye(n,(J0(),JH)))),e.e=$Q(u(ye(n,SD),303)),e.a=Yjn(u(ye(n,e6e),426)),e.b=bAn(u(ye(n,Yye),354)),Wxn(e),t.bh(n,_F)}function hTn(e,n){if(n.Tg("Target Width Setter",1),da(e,(Ja(),Xre)))ji(e,(Yh(),Mm),re(ye(e,Xre)));else throw $(new wd("A target width has to be set if the TargetWidthWidthApproximator should be used."));n.Ug()}function pqe(e,n){var t,i,r;return i=new Ba(e),$u(i,n),he(i,(me(),iH),n),he(i,(Ne(),Wi),(Br(),to)),he(i,Th,(Vh(),eG)),Cf(i,(zn(),wr)),t=new Qu,gu(t,i),xr(t,(De(),Kn)),r=new Qu,gu(r,i),xr(r,et),i}function mqe(e,n){var t,i,r,c,o;for(e.c[n.p]=!0,Ce(e.a,n),o=new L(n.j);o.a=c)o.$b();else for(r=o.Jc(),i=0;i0?Noe():o<0&&Eqe(e,n,-o),!0):!1}function YE(e){var n,t,i,r,c,o,l;if(l=0,e.b==0){for(o=nHe(e,!0),n=0,i=o,r=0,c=i.length;r0&&(l+=t,++n);n>1&&(l+=e.c*(n-1))}else l=oAe(JY(k2(si(mV(e.a),new Bg),new c6)));return l>0?l+e.n.d+e.n.a:0}function QE(e){var n,t,i,r,c,o,l;if(l=0,e.b==0)l=oAe(JY(k2(si(mV(e.a),new r6),new zg)));else{for(o=tHe(e,!0),n=0,i=o,r=0,c=i.length;r0&&(l+=t,++n);n>1&&(l+=e.c*(n-1))}return l>0?l+e.n.b+e.n.c:0}function mTn(e){var n,t;if(e.c.length!=2)throw $(new Uc("Order only allowed for two paths."));n=(vn(0,e.c.length),u(e.c[0],17)),t=(vn(1,e.c.length),u(e.c[1],17)),n.d.i!=t.c.i&&(e.c.length=0,Hn(e.c,t),Hn(e.c,n))}function Sqe(e,n,t){var i;for(ww(t,n.g,n.f),Dl(t,n.i,n.j),i=0;i<(!n.a&&(n.a=new pe(Jt,n,10,11)),n.a).i;i++)Sqe(e,u(K((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a),i),26),u(K((!t.a&&(t.a=new pe(Jt,t,10,11)),t.a),i),26))}function vTn(e,n){var t,i,r,c;for(c=u(zc(e.b,n),127),t=c.a,r=u(u(pi(e.r,n),22),83).Jc();r.Ob();)i=u(r.Pb(),115),i.c&&(t.a=k.Math.max(t.a,wfe(i.c)));if(t.a>0)switch(n.g){case 2:c.n.c=e.s;break;case 4:c.n.b=e.s}}function yTn(e,n){var t,i,r;return t=u(C(n,(Gf(),ky)),15).a-u(C(e,ky),15).a,t==0?(i=Nr(pc(u(C(e,(D0(),KN)),8)),u(C(e,WS),8)),r=Nr(pc(u(C(n,KN),8)),u(C(n,WS),8)),ki(i.a*i.b,r.a*r.b)):t}function kTn(e,n){var t,i,r;return t=u(C(n,(Mu(),$H)),15).a-u(C(e,$H),15).a,t==0?(i=Nr(pc(u(C(e,(Ci(),kD)),8)),u(C(e,_7),8)),r=Nr(pc(u(C(n,kD),8)),u(C(n,_7),8)),ki(i.a*i.b,r.a*r.b)):t}function xqe(e){var n,t;return t=new p0,t.a+="e_",n=R7n(e),n!=null&&(t.a+=""+n),e.c&&e.d&&(Kt((t.a+=" ",t),bz(e.c)),Kt(uo((t.a+="[",t),e.c.i),"]"),Kt((t.a+=eee,t),bz(e.d)),Kt(uo((t.a+="[",t),e.d.i),"]")),t.a}function Aqe(e){switch(e.g){case 0:return new DU;case 1:return new oP;case 2:return new IU;case 3:return new SC;default:throw $(new Gn("No implementation is available for the layout phase "+(e.f!=null?e.f:""+e.g)))}}function V0e(e,n,t,i,r){var c;switch(c=0,r.g){case 1:c=k.Math.max(0,n.b+e.b-(t.b+i));break;case 3:c=k.Math.max(0,-e.b-i);break;case 2:c=k.Math.max(0,-e.a-i);break;case 4:c=k.Math.max(0,n.a+e.a-(t.a+i))}return c}function Mqe(e,n,t){var i,r,c,o,l;if(t)for(r=t.a.length,i=new Rb(r),l=(i.b-i.a)*i.c<0?(k0(),yb):new S0(i);l.Ob();)o=u(l.Pb(),15),c=B9(t,o.a),F2e in c.a||xne in c.a?xIn(e,c,n):URn(e,c,n),Wwn(u(Bn(e.c,d8(c)),85))}function Y0e(e){var n,t;switch(e.b){case-1:return!0;case 0:return t=e.t,t>1||t==-1?(e.b=-1,!0):(n=ff(e),n&&(Tc(),n.jk()==ZZe)?(e.b=-1,!0):(e.b=1,!1));default:case 1:return!1}}function Q0e(e,n){var t,i,r,c;if(li(e),e.c!=0||e.a!=123)throw $(new zt(Ht((Lt(),kZe))));if(c=n==112,i=e.d,t=k9(e.i,125,i),t<0)throw $(new zt(Ht((Lt(),jZe))));return r=of(e.i,i,t),e.d=t+1,P$e(r,c,(e.e&512)==512)}function jTn(e){var n,t,i,r,c,o,l;for(l=Fh(e.c.length),r=new L(e);r.a=0&&i=0?e.Ih(t,!0,!0):Gw(e,r,!0),163)),u(i,219).Ul(n);throw $(new Gn(W0+n.ve()+wne))}function STn(){nse();var e;return Zan?u(E8((y0(),kf),hf),2e3):(ti(wg,new NL),b$n(),e=u(X(lo((y0(),kf),hf),548)?lo(kf,hf):new OIe,548),Zan=!0,dBn(e),vBn(e),ei((ese(),V8e),e,new m3),Kc(kf,hf,e),e)}function xTn(e,n){var t,i,r,c;e.j=-1,Fs(e.e)?(t=e.i,c=e.i!=0,YT(e,n),i=new L1(e.e,3,e.c,null,n,t,c),r=n.xl(e.e,e.c,null),r=rGe(e,n,r),r?(r.lj(i),r.mj()):ai(e.e,i)):(YT(e,n),r=n.xl(e.e,e.c,null),r&&r.mj())}function Az(e,n){var t,i,r;if(r=0,i=n[0],i>=e.length)return-1;for(t=(Yn(i,e.length),e.charCodeAt(i));t>=48&&t<=57&&(r=r*10+(t-48),++i,!(i>=e.length));)t=(Yn(i,e.length),e.charCodeAt(i));return i>n[0]?n[0]=i:r=-1,r}function ATn(e,n,t){var i,r,c,o,l;o=e.c,l=e.d,c=pu(z(B(Lr,1),Se,8,0,[o.i.n,o.n,o.a])).b,r=(c+pu(z(B(Lr,1),Se,8,0,[l.i.n,l.n,l.a])).b)/2,i=null,o.j==(De(),et)?i=new je(n+o.i.c.c.a+t,r):i=new je(n-t,r),j9(e.a,0,i)}function Hw(e){var n,t,i,r;for(n=null,i=qh(Rl(z(B(Xl,1),On,20,0,[(!e.b&&(e.b=new Nn(vt,e,4,7)),e.b),(!e.c&&(e.c=new Nn(vt,e,5,8)),e.c)])));ht(i);)if(t=u(tt(i),84),r=iu(t),!n)n=r;else if(n!=r)return!1;return!0}function jW(e,n,t){var i;if(++e.j,n>=e.i)throw $(new jo(Mne+n+dg+e.i));if(t>=e.i)throw $(new jo(Cne+t+dg+e.i));return i=e.g[t],n!=t&&(n>16),n=i>>16&16,t=16-n,e=e>>n,i=e-256,n=i>>16&8,t+=n,e<<=n,i=e-W2,n=i>>16&4,t+=n,e<<=n,i=e-yh,n=i>>16&2,t+=n,e<<=n,i=e>>14,n=i&~(i>>1),t+2-n)}function MTn(e,n){var t,i,r;for(r=new Te,i=St(n.a,0);i.b!=i.d.c;)t=u(jt(i),65),t.c.g==e.g&&ue(C(t.b,(Mu(),Nh)))!==ue(C(t.c,Nh))&&!G3(new wn(null,new pn(r,16)),new OEe(t))&&Hn(r.c,t);return Tr(r,new Mk),r}function Tqe(e,n,t){var i,r,c,o;return X(n,155)&&X(t,155)?(c=u(n,155),o=u(t,155),e.a[c.a][o.a]+e.a[o.a][c.a]):X(n,251)&&X(t,251)&&(i=u(n,251),r=u(t,251),i.a==r.a)?u(C(r.a,(Gf(),ky)),15).a:0}function Oqe(e,n){var t,i,r,c,o,l,f,h;for(h=ne(re(C(n,(Ne(),yx)))),f=e[0].n.a+e[0].o.a+e[0].d.c+h,l=1;l=0?t:(l=fE(Nr(new je(o.c+o.b/2,o.d+o.a/2),new je(c.c+c.b/2,c.d+c.a/2))),-(oKe(c,o)-1)*l)}function TTn(e,n,t){var i;Zi(new wn(null,(!t.a&&(t.a=new pe(Pi,t,6,6)),new pn(t.a,16))),new NCe(e,n)),Zi(new wn(null,(!t.n&&(t.n=new pe(ju,t,1,7)),new pn(t.n,16))),new DCe(e,n)),i=u(ye(t,(Xt(),Kv)),78),i&&Zhe(i,e,n)}function Gw(e,n,t){var i,r,c;if(c=av((ls(),nc),e.Ah(),n),c)return Tc(),u(c,69).vk()||(c=D4(Vc(nc,c))),r=(i=e.Fh(c),u(i>=0?e.Ih(i,!0,!0):Gw(e,c,!0),163)),u(r,219).Ql(n,t);throw $(new Gn(W0+n.ve()+wne))}function W0e(e,n,t,i){var r,c,o,l,f;if(r=e.d[n],r){if(c=r.g,f=r.i,i!=null){for(l=0;l=t&&(i=n,h=(f.c+f.a)/2,o=h-t,f.c<=h-t&&(r=new QK(f.c,o),Pb(e,i++,r)),l=h+t,l<=f.a&&(c=new QK(l,f.a),S2(i,e.c.length),Ij(e.c,i,c)))}function _qe(e,n,t){var i,r,c,o,l,f;if(!n.dc()){for(r=new Si,f=n.Jc();f.Ob();)for(l=u(f.Pb(),40),ei(e.a,ve(l.g),ve(t)),o=(i=St(new S1(l).a.d,0),new E3(i));YC(o.a);)c=u(jt(o.a),65).c,Xi(r,c,r.c.b,r.c);_qe(e,r,t+1)}}function Z0e(e){var n;if(!e.c&&e.g==null)e.d=e._i(e.f),Et(e,e.d),n=e.d;else{if(e.g==null)return!0;if(e.i==0)return!1;n=u(e.g[e.i-1],50)}return n==e.b&&null.Tm>=null.Sm()?(Tz(e),Z0e(e)):n.Ob()}function Lqe(e){if(this.a=e,e.c.i.k==(zn(),wr))this.c=e.c,this.d=u(C(e.c.i,(me(),Du)),64);else if(e.d.i.k==wr)this.c=e.d,this.d=u(C(e.d.i,(me(),Du)),64);else throw $(new Gn("Edge "+e+" is not an external edge."))}function Pqe(e,n){var t,i,r;r=e.b,e.b=n,(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,3,r,e.b)),n?n!=e&&(Mo(e,n.zb),NY(e,n.d),t=(i=n.c,i??n.zb),IY(e,t==null||bn(t,n.zb)?null:t)):(Mo(e,null),NY(e,0),IY(e,null))}function $qe(e){!nte&&(nte=kRn());var n=e.replace(/[\x00-\x1f\xad\u0600-\u0603\u06dd\u070f\u17b4\u17b5\u200b-\u200f\u2028-\u202e\u2060-\u2064\u206a-\u206f\ufeff\ufff9-\ufffb"\\]/g,function(t){return g4n(t)});return'"'+n+'"'}function ebe(e,n,t,i,r,c){var o,l,f,h,b;if(r!=0)for(ue(e)===ue(t)&&(e=e.slice(n,n+r),n=0),f=t,l=n,h=n+r;l=o)throw $(new b2(n,o));return r=t[n],o==1?i=null:(i=oe(Rce,Ine,415,o-1,0,1),Wu(t,0,i,0,n),c=o-n-1,c>0&&Wu(t,n+1,i,n,c)),g8(e,i),rqe(e,n,r),r}function Rqe(e){var n,t;if(e.f){for(;e.n0)for(o=e.c.d,l=e.d.d,r=A1(Nr(new je(l.a,l.b),o),1/(i+1)),c=new je(o.a,o.b),t=new L(e.a);t.a0?c=q4(t):c=TO(q4(t))),ji(n,C7,c)}function Hqe(e,n){var t,i;if(e.c.length!=0){if(e.c.length==2)iy((vn(0,e.c.length),u(e.c[0],9)),(al(),s1)),iy((vn(1,e.c.length),u(e.c[1],9)),hb);else for(i=new L(e);i.a0&&ZO(e,t,n),c):i.a!=null?(ZO(e,n,t),-1):r.a!=null?(ZO(e,t,n),1):0}function Gqe(e){qV();var n,t,i,r,c,o,l;for(t=new O0,r=new L(e.e.b);r.a=0;)i=t[c],o.$l(i.Jk())&&Et(r,i);!FVe(e,r)&&Fs(e.e)&&s9(e,n.Hk()?M0(e,6,n,(jn(),Sc),null,-1,!1):M0(e,n.rk()?2:1,n,null,null,-1,!1))}function BTn(e,n){var t,i,r,c,o;return e.a==(y8(),rx)?!0:(c=n.a.c,t=n.a.c+n.a.b,!(n.j&&(i=n.A,o=i.c.c.a-i.o.a/2,r=c-(i.n.a+i.o.a),r>o)||n.q&&(i=n.C,o=i.c.c.a-i.o.a/2,r=i.n.a-t,r>o)))}function Uqe(e,n,t){var i,r,c,o,l,f;for(i=0,f=t,n||(i=t*(e.c.length-1),f*=-1),c=new L(e);c.a=0?e.xh(null):e.Mh().Qh(e,-1-n,null,null)),e.yh(u(r,52),t),i&&i.mj(),e.sh()&&e.th()&&t>-1&&ai(e,new Ir(e,9,t,c,r)),r):c}function rbe(e,n){var t,i,r,c,o;for(c=e.b.Ae(n),i=(t=e.a.get(c),t??oe(Ar,On,1,0,5,1)),o=0;o>5,r>=e.d)return e.e<0;if(t=e.a[r],n=1<<(n&31),e.e<0){if(i=QBe(e),r>16)),16).bd(c),l0&&(!(x1(e.a.c)&&n.n.d)&&!(I3(e.a.c)&&n.n.b)&&(n.g.d+=k.Math.max(0,i/2-.5)),!(x1(e.a.c)&&n.n.a)&&!(I3(e.a.c)&&n.n.c)&&(n.g.a-=i-1))}function cUe(e,n,t){var i,r,c,o,l,f;c=u(Le(n.e,0),17).c,i=c.i,r=i.k,f=u(Le(t.g,0),17).d,o=f.i,l=o.k,r==(zn(),dr)?he(e,(me(),ja),u(C(i,ja),12)):he(e,(me(),ja),c),l==dr?he(e,(me(),gf),u(C(o,gf),12)):he(e,(me(),gf),f)}function uUe(e,n){var t,i,r,c,o,l;for(c=new L(e.b);c.a>n,c=e.m>>n|t<<22-n,r=e.l>>n|e.m<<22-n):n<44?(o=i?G1:0,c=t>>n-22,r=e.m>>n-22|t<<44-n):(o=i?G1:0,c=i?Ls:0,r=t>>n-44),_o(r&Ls,c&Ls,o&G1)}function oUe(e,n){var t,i,r,c,o,l,f,h,b;if(e.a.f>0&&X(n,45)&&(e.a.Zj(),h=u(n,45),f=h.jd(),c=f==null?0:Oi(f),o=Nle(e.a,c),t=e.a.d[o],t)){for(i=u(t.g,374),b=t.i,l=0;l=2)for(t=r.Jc(),n=re(t.Pb());t.Ob();)c=n,n=re(t.Pb()),i=k.Math.min(i,(_n(n),n-(_n(c),c)));return i}function sOn(e,n){var t,i,r;for(r=new Te,i=St(n.a,0);i.b!=i.d.c;)t=u(jt(i),65),t.b.g==e.g&&!bn(t.b.c,DF)&&ue(C(t.b,(Mu(),Nh)))!==ue(C(t.c,Nh))&&!G3(new wn(null,new pn(r,16)),new NEe(t))&&Hn(r.c,t);return Tr(r,new tw),r}function lOn(e,n){var t,i,r;if(ue(n)===ue(Nt(e)))return!0;if(!X(n,16)||(i=u(n,16),r=e.gc(),r!=i.gc()))return!1;if(X(i,59)){for(t=0;t0&&(r=t),o=new L(e.f.e);o.a0?r+=n:r+=1;return r}function pOn(e,n){var t,i,r,c,o,l,f,h,b,p;h=e,f=gE(h,"individualSpacings"),f&&(i=da(n,(Xt(),Jy)),o=!i,o&&(r=new R6,ji(n,Jy,r)),l=u(ye(n,Jy),379),p=f,c=null,p&&(c=(b=BY(p,oe(ze,Se,2,0,6,1)),new PX(p,b))),c&&(t=new JCe(p,l),rc(c,t)))}function mOn(e,n){var t,i,r,c,o,l,f,h,b,p,y;return f=null,p=e,b=null,(uZe in p.a||oZe in p.a||FF in p.a)&&(h=null,y=l1e(n),o=gE(p,uZe),t=new gSe(y),VFe(t.a,o),l=gE(p,oZe),i=new SSe(y),YFe(i.a,l),c=Ow(p,FF),r=new MSe(y),h=(tGe(r.a,c),c),b=h),f=b,f}function vOn(e,n){var t,i,r;if(n===e)return!0;if(X(n,540)){if(r=u(n,833),e.a.d!=r.a.d||z3(e).gc()!=z3(r).gc())return!1;for(i=z3(r).Jc();i.Ob();)if(t=u(i.Pb(),416),cLe(e,t.a.jd())!=u(t.a.kd(),18).gc())return!1;return!0}return!1}function yOn(e,n){var t,i,r,c;for(c=new L(n.a);c.an.c?1:e.bn.b?1:e.a!=n.a?Oi(e.a)-Oi(n.a):e.d==(mE(),Tx)&&n.d==Cx?-1:e.d==Cx&&n.d==Tx?1:0}function xW(e){var n,t,i,r,c,o,l,f;for(r=Ki,i=Dr,t=new L(e.e.b);t.a0&&r0):r<0&&-r0):!1}function jOn(e,n,t,i){var r,c,o,l,f,h,b,p;for(r=(n-e.d)/e.c.c.length,c=0,e.a+=t,e.d=n,p=new L(e.c);p.a>24;return o}function SOn(e){if(e.xe()){var n=e.c;n.ye()?e.o="["+n.n:n.xe()?e.o="["+n.ve():e.o="[L"+n.ve()+";",e.b=n.ue()+"[]",e.k=n.we()+"[]";return}var t=e.j,i=e.d;i=i.split("/"),e.o=MQ(".",[t,MQ("$",i)]),e.b=MQ(".",[t,MQ(".",i)]),e.k=i[i.length-1]}function xOn(e,n){var t,i,r,c,o;for(o=null,c=new L(e.e.a);c.a0&&oN(n,(vn(i-1,e.c.length),u(e.c[i-1],9)),r)>0;)ol(e,i,(vn(i-1,e.c.length),u(e.c[i-1],9))),--i;vn(i,e.c.length),e.c[i]=r}n.b=new pt,n.g=new pt}function vUe(e,n,t){var i,r,c;for(i=1;i0&&n.Le((vn(r-1,e.c.length),u(e.c[r-1],9)),c)>0;)ol(e,r,(vn(r-1,e.c.length),u(e.c[r-1],9))),--r;vn(r,e.c.length),e.c[r]=c}t.a=new pt,t.b=new pt}function Cz(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(c=n.Jc();c.Ob();)r=u(c.Pb(),26),b=r.i+r.g/2,y=r.j+r.f/2,f=e.f,o=f.i+f.g/2,l=f.j+f.f/2,h=b-o,p=y-l,i=k.Math.sqrt(h*h+p*p),h*=e.e/i,p*=e.e/i,t?(b-=h,y-=p):(b+=h,y+=p),Os(r,b-r.g/2),Ns(r,y-r.f/2)}function ov(e){var n,t,i;if(!e.c&&e.b!=null){for(n=e.b.length-4;n>=0;n-=2)for(t=0;t<=n;t+=2)(e.b[t]>e.b[t+2]||e.b[t]===e.b[t+2]&&e.b[t+1]>e.b[t+3])&&(i=e.b[t+2],e.b[t+2]=e.b[t],e.b[t]=i,i=e.b[t+3],e.b[t+3]=e.b[t+1],e.b[t+1]=i);e.c=!0}}function Jf(e){var n,t;return t=new il(Db(e.Pm)),t.a+="@",Kt(t,(n=Oi(e)>>>0,n.toString(16))),e.Sh()?(t.a+=" (eProxyURI: ",uo(t,e.Yh()),e.Hh()&&(t.a+=" eClass: ",uo(t,e.Hh())),t.a+=")"):e.Hh()&&(t.a+=" (eClass: ",uo(t,e.Hh()),t.a+=")"),t.a}function eS(e){var n,t,i,r;if(e.e)throw $(new Uc((M1(bte),qZ+bte.k+UZ)));for(e.d==(vr(),eh)&&Vz(e,Zc),t=new L(e.a.a);t.a>24}return t}function NOn(e,n,t){var i,r,c;if(r=u(zc(e.i,n),318),!r)if(r=new HRe(e.d,n,t),M4(e.i,n,r),mde(n))Qwn(e.a,n.c,n.b,r);else switch(c=ACn(n),i=u(zc(e.p,c),253),c.g){case 1:case 3:r.j=!0,xX(i,n.b,r);break;case 4:case 2:r.k=!0,xX(i,n.c,r)}return r}function DOn(e,n,t,i){var r,c,o,l,f,h;if(l=new z6,f=Po(e.e.Ah(),n),r=u(e.g,122),Tc(),u(n,69).vk())for(o=0;o=0)return r;for(c=1,l=new L(n.j);l.a=0)return r;for(c=1,l=new L(n.j);l.a=0?(n||(n=new jj,i>0&&Bc(n,(Yr(0,i,e.length),e.substr(0,i)))),n.a+="\\",D9(n,t&yr)):n&&D9(n,t&yr);return n?n.a:e}function _On(e){var n,t,i;for(t=new L(e.a.a.b);t.a0&&(!(x1(e.a.c)&&n.n.d)&&!(I3(e.a.c)&&n.n.b)&&(n.g.d-=k.Math.max(0,i/2-.5)),!(x1(e.a.c)&&n.n.a)&&!(I3(e.a.c)&&n.n.c)&&(n.g.a+=k.Math.max(0,i-1)))}function xUe(e,n,t){var i,r;if((e.c-e.b&e.a.length-1)==2)n==(De(),Xn)||n==et?(hB(u(TE(e),16),(al(),s1)),hB(u(TE(e),16),hb)):(hB(u(TE(e),16),(al(),hb)),hB(u(TE(e),16),s1));else for(r=new hE(e);r.a!=r.b;)i=u(zB(r),16),hB(i,t)}function LOn(e,n,t){var i,r,c,o,l,f,h,b,p;for(b=-1,p=0,l=n,f=0,h=l.length;f0&&++p;++b}return p}function POn(e,n){var t,i,r,c,o,l,f;for(r=M9(new roe(e)),l=new qr(r,r.c.length),c=M9(new roe(n)),f=new qr(c,c.c.length),o=null;l.b>0&&f.b>0&&(t=(at(l.b>0),u(l.a.Xb(l.c=--l.b),26)),i=(at(f.b>0),u(f.a.Xb(f.c=--f.b),26)),t==i);)o=t;return o}function $On(e,n){var t,i,r,c;for(n.Tg("Self-Loop pre-processing",1),i=new L(e.a);i.abLe(e,t)?(i=mu(t,(De(),et)),e.d=i.dc()?0:tV(u(i.Xb(0),12)),o=mu(n,Kn),e.b=o.dc()?0:tV(u(o.Xb(0),12))):(r=mu(t,(De(),Kn)),e.d=r.dc()?0:tV(u(r.Xb(0),12)),c=mu(n,et),e.b=c.dc()?0:tV(u(c.Xb(0),12)))}function ROn(e){var n,t,i,r,c,o,l,f;n=!0,r=null,c=null;e:for(f=new L(e.a);f.ae.c));o++)r.a>=e.s&&(c<0&&(c=o),l=o);return f=(e.s+e.c)/2,c>=0&&(i=EIn(e,n,c,l),f=Cgn((vn(i,n.c.length),u(n.c[i],340))),ITn(n,i,t)),f}function Ct(e,n,t){var i,r,c,o,l,f,h;for(o=(c=new Mb,c),Hhe(o,(_n(n),n)),h=(!o.b&&(o.b=new Hs((yn(),Ac),Iu,o)),o.b),f=1;f=2}function JOn(e,n,t,i,r){var c,o,l,f,h,b;for(c=e.c.d.j,o=u(Yu(t,0),8),b=1;b1||(n=Mi(Qf,z(B($c,1),ke,96,0,[W1,Wf])),gO(_R(n,e))>1)||(i=Mi(ea,z(B($c,1),ke,96,0,[l1,pf])),gO(_R(i,e))>1))}function CUe(e){var n,t,i,r,c,o,l;for(n=0,i=new L(e.a);i.a0&&(i.b.n-=i.c,i.b.n<=0&&i.b.u>0&&Vt(n,i.b));for(r=new L(e.i);r.a0&&(i.a.u-=i.c,i.a.u<=0&&i.a.n>0&&Vt(t,i.a))}function Tz(e){var n,t,i,r,c;if(e.g==null&&(e.d=e._i(e.f),Et(e,e.d),e.c))return c=e.f,c;if(n=u(e.g[e.i-1],50),r=n.Pb(),e.e=n,t=e._i(r),t.Ob())e.d=t,Et(e,t);else for(e.d=null;!n.Ob()&&(tr(e.g,--e.i,null),e.i!=0);)i=u(e.g[e.i-1],50),n=i;return r}function GOn(e,n){var t,i,r,c,o,l;if(i=n,r=i.Jk(),J1(e.e,r)){if(r.Qi()&&HR(e,r,i.kd()))return!1}else for(l=Po(e.e.Ah(),r),t=u(e.g,122),c=0;c1||t>1)return 2;return n+t==1?2:0}function Is(e,n){var t,i,r,c,o,l;return c=e.a*FZ+e.b*1502,l=e.b*FZ+11,t=k.Math.floor(l*vN),c+=t,l-=t*Uge,c%=Uge,e.a=c,e.b=l,n<=24?k.Math.floor(e.a*qme[n]):(r=e.a*(1<=2147483648&&(i-=4294967296),i)}function NUe(e,n,t){var i,r,c,o,l,f,h;for(c=new Te,h=new Si,o=new Si,lLn(e,h,o,n),GPn(e,h,o,n,t),f=new L(e);f.ai.b.g&&Hn(c.c,i);return c}function YOn(e,n,t){var i,r,c,o,l,f;for(l=e.c,o=(t.q?t.q:(jn(),jn(),i1)).vc().Jc();o.Ob();)c=u(o.Pb(),45),i=!b9(si(new wn(null,new pn(l,16)),new u9(new vCe(n,c)))).zd((Ib(),vy)),i&&(f=c.kd(),X(f,4)&&(r=yde(f),r!=null&&(f=r)),n.of(u(c.jd(),147),f))}function QOn(e,n){var t,i,r,c;for(n.Tg("Resize child graph to fit parent.",1),i=new L(e.b);i.a1)for(r=new L(e.a);r.a=0?e.Ih(i,!0,!0):Gw(e,c,!0),163)),u(r,219).Vl(n,t)}else throw $(new Gn(W0+n.ve()+_S))}function eNn(e,n,t){var i,r,c,o,l,f;if(f=Ele(e,u(Bn(e.e,n),26)),l=null,f)switch(f.g){case 3:i=Xse(e,j2(n)),l=(_n(t),t+(_n(i),i));break;case 2:r=Xse(e,j2(n)),o=(_n(t),t+(_n(r),r)),c=Xse(e,u(Bn(e.e,n),26)),l=o-(_n(c),c);break;default:l=t}else l=t;return l}function nNn(e,n,t){var i,r,c,o,l,f;if(f=Ele(e,u(Bn(e.e,n),26)),l=null,f)switch(f.g){case 3:i=Kse(e,j2(n)),l=(_n(t),t+(_n(i),i));break;case 2:r=Kse(e,j2(n)),o=(_n(t),t+(_n(r),r)),c=Kse(e,u(Bn(e.e,n),26)),l=o-(_n(c),c);break;default:l=t}else l=t;return l}function Oz(e,n){var t,i,r,c,o;if(n){for(c=X(e.Cb,88)||X(e.Cb,103),o=!c&&X(e.Cb,335),i=new ut((!n.a&&(n.a=new tE(n,Rc,n)),n.a));i.e!=i.i.gc();)if(t=u(ft(i),87),r=Jz(t),c?X(r,88):o?X(r,159):r)return r;return c?(yn(),jf):(yn(),ih)}else return null}function tNn(e,n){var t,i,r,c,o;for(t=new Te,r=ou(new wn(null,new pn(e,16)),new L5),c=ou(new wn(null,new pn(e,16)),new Ak),o=U9n(d9n(k2(dNn(z(B(OBn,1),On,832,0,[r,c])),new w_))),i=1;i=2*n&&Ce(t,new QK(o[i-1]+n,o[i]-n));return t}function DUe(e,n,t){var i,r,c,o,l,f,h,b;if(t)for(c=t.a.length,i=new Rb(c),l=(i.b-i.a)*i.c<0?(k0(),yb):new S0(i);l.Ob();)o=u(l.Pb(),15),r=B9(t,o.a),r&&(f=v6n(e,(h=(v0(),b=new voe,b),n&&kbe(h,n),h),r),X9(f,N1(r,Ah)),yz(r,f),H0e(r,f),eQ(e,r,f))}function Nz(e){var n,t,i,r,c,o;if(!e.j){if(o=new kL,n=sA,c=n.a.yc(e,n),c==null){for(i=new ut(tu(e));i.e!=i.i.gc();)t=u(ft(i),29),r=Nz(t),er(o,r),Et(o,t);n.a.Ac(e)!=null}_2(o),e.j=new N3((u(K(we((x0(),Rn).o),11),19),o.i),o.g),Ms(e).b&=-33}return e.j}function iNn(e){var n,t,i,r;if(e==null)return null;if(i=bo(e,!0),r=HN.length,bn(i.substr(i.length-r,r),HN)){if(t=i.length,t==4){if(n=(Yn(0,i.length),i.charCodeAt(0)),n==43)return g7e;if(n==45)return yhn}else if(t==3)return g7e}return new aoe(i)}function rNn(e){var n,t,i;return t=e.l,(t&t-1)!=0||(i=e.m,(i&i-1)!=0)||(n=e.h,(n&n-1)!=0)||n==0&&i==0&&t==0?-1:n==0&&i==0&&t!=0?Phe(t):n==0&&i!=0&&t==0?Phe(i)+22:n!=0&&i==0&&t==0?Phe(n)+44:-1}function sv(e,n){var t,i,r,c,o;for(r=n.a&e.f,c=null,i=e.b[r];;i=i.b){if(i==n){c?c.b=n.b:e.b[r]=n.b;break}c=i}for(o=n.f&e.f,c=null,t=e.c[o];;t=t.d){if(t==n){c?c.d=n.d:e.c[o]=n.d;break}c=t}n.e?n.e.c=n.c:e.a=n.c,n.c?n.c.e=n.e:e.e=n.e,--e.i,++e.g}function cNn(e,n){var t;n.d?n.d.b=n.b:e.a=n.b,n.b?n.b.d=n.d:e.e=n.d,!n.e&&!n.c?(t=u(uf(u(L4(e.b,n.a),262)),262),t.a=0,++e.c):(t=u(uf(u(Bn(e.b,n.a),262)),262),--t.a,n.e?n.e.c=n.c:t.b=u(uf(n.c),497),n.c?n.c.e=n.e:t.c=u(uf(n.e),497)),--e.d}function MW(e,n){var t,i,r,c;for(c=new qr(e,0),t=(at(c.b0),c.a.Xb(c.c=--c.b),d2(c,r),at(c.b3&&Kh(e,0,n-3))}function oNn(e){var n,t,i,r;return ue(C(e,(Ne(),wm)))===ue((B1(),Yd))?!e.e&&ue(C(e,fD))!==ue((W9(),tD)):(i=u(C(e,Oie),302),r=Re($e(C(e,Nie)))||ue(C(e,wx))===ue((BE(),eD)),n=u(C(e,z5e),15).a,t=e.a.c.length,!r&&i!=(W9(),tD)&&(n==0||n>t))}function sNn(e,n){var t,i,r,c,o,l,f;for(r=e.Jc();r.Ob();)for(i=u(r.Pb(),9),l=new Qu,gu(l,i),xr(l,(De(),et)),he(l,(me(),rH),(Pn(),!0)),o=n.Jc();o.Ob();)c=u(o.Pb(),9),f=new Qu,gu(f,c),xr(f,Kn),he(f,rH,!0),t=new Mw,he(t,rH,!0),lc(t,l),Gr(t,f)}function lNn(e){var n,t;for(t=0;t0);t++);if(t>0&&t0);n++);return n>0&&t>16!=6&&n){if(w8(e,n))throw $(new Gn(LS+Xqe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Jde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=K4(n,e,6,i)),i=Tle(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,6,n,n))}function Dz(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=3&&n){if(w8(e,n))throw $(new Gn(LS+$Ke(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Ude(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=K4(n,e,12,i)),i=Cle(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,3,n,n))}function kbe(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=9&&n){if(w8(e,n))throw $(new Gn(LS+_Xe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Gde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=K4(n,e,9,i)),i=Ole(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,9,n,n))}function S8(e){var n,t,i,r,c;if(i=ff(e),c=e.j,c==null&&i)return e.Hk()?null:i.gk();if(X(i,159)){if(t=i.hk(),t&&(r=t.ti(),r!=e.i)){if(n=u(i,159),n.lk())try{e.g=r.qi(n,c)}catch(o){if(o=sr(o),X(o,80))e.g=null;else throw $(o)}e.i=r}return e.g}return null}function $Ue(e){var n;return n=new Te,Ce(n,new f4(new je(e.c,e.d),new je(e.c+e.b,e.d))),Ce(n,new f4(new je(e.c,e.d),new je(e.c,e.d+e.a))),Ce(n,new f4(new je(e.c+e.b,e.d+e.a),new je(e.c+e.b,e.d))),Ce(n,new f4(new je(e.c+e.b,e.d+e.a),new je(e.c,e.d+e.a))),n}function aNn(e){var n,t,i,r;for(i=e.a.d.j,r=e.c.d.j,t=new L(e.i.d);t.a>>0),t.toString(16)),JEn(F7n(),(m9(),"Exception during lenientFormat for "+i),n),"<"+i+" threw "+Db(n.Pm)+">";throw $(r)}}function dNn(e){var n,t,i,r,c,o,l,f,h;for(i=!1,n=336,t=0,c=new ZOe(e.length),l=e,f=0,h=l.length;f1)for(n=mw((t=new Nb,++e.b,t),e.d),l=St(c,0);l.b!=l.d.c;)o=u(jt(l),124),Hf(Nf(Of(Df(Tf(new tf,1),0),n),o))}function Iz(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=11&&n){if(w8(e,n))throw $(new Gn(LS+Jbe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Xde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=K4(n,e,10,i)),i=Gle(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,11,n,n))}function mNn(e,n,t){var i,r,c,o,l,f;if(c=0,o=0,e.c)for(f=new L(e.d.i.j);f.ac.a?-1:r.af){for(b=e.d,e.d=oe(z8e,nme,67,2*f+4,0,1),c=0;c=9223372036854776e3?(G9(),jme):(r=!1,e<0&&(r=!0,e=-e),i=0,e>=sg&&(i=sc(e/sg),e-=i*sg),t=0,e>=sy&&(t=sc(e/sy),e-=t*sy),n=sc(e),c=_o(n,t,i),r&&ZY(c),c)}function ONn(e){var n,t,i,r,c;if(c=new Te,Ao(e.b,new Xke(c)),e.b.c.length=0,c.c.length!=0){for(n=(vn(0,c.c.length),u(c.c[0],80)),t=1,i=c.c.length;t>16!=7&&n){if(w8(e,n))throw $(new Gn(LS+JGe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?Hde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=u(n,52).Oh(e,1,VD,i)),i=Mfe(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,7,n,n))}function zUe(e,n){var t,i;if(n!=e.Cb||e.Db>>16!=3&&n){if(w8(e,n))throw $(new Gn(LS+NFe(e)));i=null,e.Cb&&(i=(t=e.Db>>16,t>=0?qde(e,i):e.Cb.Qh(e,-1-t,null,i))),n&&(i=u(n,52).Oh(e,0,QD,i)),i=Cfe(e,n,i),i&&i.mj()}else(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,3,n,n))}function CW(e,n){A8();var t,i,r,c,o,l,f,h,b;return n.d>e.d&&(l=e,e=n,n=l),n.d<63?yDn(e,n):(o=(e.d&-2)<<4,h=Vae(e,o),b=Vae(n,o),i=KW(e,_4(h,o)),r=KW(n,_4(b,o)),f=CW(h,b),t=CW(i,r),c=CW(KW(h,i),KW(r,b)),c=nZ(nZ(c,f),t),c=_4(c,o),f=_4(f,o<<1),nZ(nZ(f,c),t))}function YO(){YO=Y,Kie=new M3(BQe,0),M4e=new M3("LONGEST_PATH",1),C4e=new M3("LONGEST_PATH_SOURCE",2),Uie=new M3("COFFMAN_GRAHAM",3),A4e=new M3(ree,4),T4e=new M3("STRETCH_WIDTH",5),EH=new M3("MIN_WIDTH",6),qie=new M3("BF_MODEL_ORDER",7),Xie=new M3("DF_MODEL_ORDER",8)}function LNn(e,n){var t,i,r,c,o,l;if(!e.tb){for(c=(!e.rb&&(e.rb=new m2(e,Aa,e)),e.rb),l=new l4(c.i),r=new ut(c);r.e!=r.i.gc();)i=u(ft(r),143),o=i.ve(),t=u(o==null?Ko(l.f,null,i):Pw(l.i,o,i),143),t&&(o==null?Ko(l.f,null,t):Pw(l.i,o,t));e.tb=l}return u(lo(e.tb,n),143)}function QO(e,n){var t,i,r,c,o;if((e.i==null&&vh(e),e.i).length,!e.p){for(o=new l4((3*e.g.i/2|0)+1),r=new m4(e.g);r.e!=r.i.gc();)i=u(LQ(r),179),c=i.ve(),t=u(c==null?Ko(o.f,null,i):Pw(o.i,c,i),179),t&&(c==null?Ko(o.f,null,t):Pw(o.i,c,t));e.p=o}return u(lo(e.p,n),179)}function Mbe(e,n,t,i,r){var c,o,l,f,h;for(DEn(i+DR(t,t.ge()),r),wIe(n,Vjn(t)),c=t.f,c&&Mbe(e,n,c,"Caused by: ",!1),l=(t.k==null&&(t.k=oe(ete,Se,80,0,0,1)),t.k),f=0,h=l.length;f=0;c+=t?1:-1)o=o|n.c.jg(f,c,t,i&&!Re($e(C(n.j,(me(),rb))))&&!Re($e(C(n.j,(me(),_v))))),o=o|n.q.tg(f,c,t),o=o|MXe(e,f[c],t,i);return hr(e.c,n),o}function Lz(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(b=qLe(e.j),p=0,y=b.length;p1&&(e.a=!0),lvn(u(t.b,68),gi(pc(u(n.b,68).c),A1(Nr(pc(u(t.b,68).a),u(n.b,68).a),r))),nLe(e,n),JUe(e,t)}function HUe(e){var n,t,i,r,c,o,l;for(c=new L(e.a.a);c.a0&&c>0?o.p=n++:i>0?o.p=t++:c>0?o.p=r++:o.p=t++}jn(),Tr(e.j,new Iq)}function zNn(e){var n,t;t=null,n=u(Le(e.g,0),17);do{if(t=n.d.i,bi(t,(me(),gf)))return u(C(t,gf),12).i;if(t.k!=(zn(),Qi)&&ht(new qn(Vn(Ni(t).a.Jc(),new ee))))n=u(tt(new qn(Vn(Ni(t).a.Jc(),new ee))),17);else if(t.k!=Qi)return null}while(t&&t.k!=(zn(),Qi));return t}function FNn(e,n){var t,i,r,c,o,l,f,h,b;for(l=n.j,o=n.g,f=u(Le(l,l.c.length-1),113),b=(vn(0,l.c.length),u(l.c[0],113)),h=YQ(e,o,f,b),c=1;ch&&(f=t,b=r,h=i);n.a=b,n.c=f}function qw(e,n,t,i){var r,c;if(r=ue(C(t,(Ne(),bx)))===ue((_0(),dm)),c=u(C(t,B5e),16),bi(e,(me(),Ti)))if(r){if(c.Gc(C(e,gx))&&c.Gc(C(n,gx)))return i*u(C(e,gx),15).a+u(C(e,Ti),15).a}else return u(C(e,Ti),15).a;else return-1;return u(C(e,Ti),15).a}function JNn(e,n,t){var i,r,c,o,l,f,h;for(h=new vd(new dEe(e)),o=z(B(cin,1),lQe,12,0,[n,t]),l=0,f=o.length;lf-e.b&&lf-e.a&&lt.p?1:0:c.Ob()?1:-1}function YNn(e,n){var t,i,r,c,o,l;n.Tg(lWe,1),r=u(ye(e,(Ja(),Bx)),104),c=(!e.a&&(e.a=new pe(Jt,e,10,11)),e.a),o=pxn(c),l=k.Math.max(o.a,ne(re(ye(e,(Yh(),Rx))))-(r.b+r.c)),i=k.Math.max(o.b,ne(re(ye(e,GH)))-(r.d+r.a)),t=i-o.b,ji(e,$x,t),ji(e,Py,l),ji(e,P7,i+t),n.Ug()}function Pz(e){var n,t;if((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i==0)return l1e(e);for(n=u(K((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),0),170),kt((!n.a&&(n.a=new mr(kl,n,5)),n.a)),V3(n,0),Y3(n,0),X3(n,0),K3(n,0),t=(!e.a&&(e.a=new pe(Pi,e,6,6)),e.a);t.i>1;)U2(t,t.i-1);return n}function Po(e,n){Tc();var t,i,r,c;return n?n==(Ei(),mhn)||(n==uhn||n==Dg||n==chn)&&e!=d7e?new Age(e,n):(i=u(n,682),t=i.Yk(),t||(L9(Vc((ls(),nc),n)),t=i.Yk()),c=(!t.i&&(t.i=new pt),t.i),r=u(du(Xc(c.f,e)),2003),!r&&ei(c,e,r=new Age(e,n)),r):thn}function QNn(e,n){var t,i;if(i=PT(e.b,n.b),!i)throw $(new Uc("Invalid hitboxes for scanline constraint calculation."));(Eze(n.b,u(vgn(e.b,n.b),60))||Eze(n.b,u(mgn(e.b,n.b),60)))&&yd(),e.a[n.b.f]=u(RX(e.b,n.b),60),t=u($X(e.b,n.b),60),t&&(e.a[t.f]=n.b)}function WNn(e,n){var t,i,r,c,o,l,f,h,b;for(f=u(C(e,(me(),wi)),12),h=pu(z(B(Lr,1),Se,8,0,[f.i.n,f.n,f.a])).a,b=e.i.n.b,t=dh(e.e),r=t,c=0,o=r.length;c0?c.a?(l=c.b.Kf().a,t>l&&(r=(t-l)/2,c.d.b=r,c.d.c=r)):c.d.c=e.s+t:cE(e.u)&&(i=m0e(c.b),i.c<0&&(c.d.b=-i.c),i.c+i.b>c.b.Kf().a&&(c.d.c=i.c+i.b-c.b.Kf().a))}function rDn(e,n){var t,i,r,c,o;o=new Te,t=n;do c=u(Bn(e.b,t),132),c.B=t.c,c.D=t.d,Hn(o.c,c),t=u(Bn(e.k,t),17);while(t);return i=(vn(0,o.c.length),u(o.c[0],132)),i.j=!0,i.A=u(i.d.a.ec().Jc().Pb(),17).c.i,r=u(Le(o,o.c.length-1),132),r.q=!0,r.C=u(r.d.a.ec().Jc().Pb(),17).d.i,o}function cDn(e){var n,t;t=u(C(e,(Ne(),yu)),165),n=u(C(e,(me(),mg)),315),t==(Xs(),V1)?(he(e,yu,sD),he(e,mg,(_1(),Dv))):t==yg?(he(e,yu,sD),he(e,mg,(_1(),Sy))):n==(_1(),Dv)?(he(e,yu,V1),he(e,mg,rD)):n==Sy&&(he(e,yu,yg),he(e,mg,rD))}function $z(){$z=Y,vD=new zp,Fon=qt(new or,(zr(),eo),(Ur(),AJ)),Gon=Eo(qt(new or,eo,_J),Pc,IJ),qon=wh(wh(Oj(Eo(qt(new or,Kf,RJ),Pc,$J),no),PJ),BJ),Jon=Eo(qt(qt(qt(new or,r1,CJ),no,OJ),no,g7),Pc,TJ),Hon=Eo(qt(qt(new or,no,g7),no,xJ),Pc,SJ)}function iS(){iS=Y,Kon=qt(Eo(new or,(zr(),Pc),(Ur(),J3e)),eo,AJ),Won=wh(wh(Oj(Eo(qt(new or,Kf,RJ),Pc,$J),no),PJ),BJ),Von=Eo(qt(qt(qt(new or,r1,CJ),no,OJ),no,g7),Pc,TJ),Qon=qt(qt(new or,eo,_J),Pc,IJ),Yon=Eo(qt(qt(new or,no,g7),no,xJ),Pc,SJ)}function uDn(e,n,t,i,r){var c,o;(!cc(n)&&n.c.i.c==n.d.i.c||!OBe(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])),t))&&!cc(n)&&(n.c==r?j9(n.a,0,new wc(t)):Vt(n.a,new wc(t)),i&&!rf(e.a,t)&&(o=u(C(n,(Ne(),Wc)),78),o||(o=new xs,he(n,Wc,o)),c=new wc(t),Xi(o,c,o.c.b,o.c),hr(e.a,c)))}function UUe(e,n){var t,i,r,c;for(c=Rt(ac(Zh,Uh(Rt(ac(n==null?0:Oi(n),e1)),15))),t=c&e.b.length-1,r=null,i=e.b[t];i;r=i,i=i.a)if(i.d==c&&C1(i.i,n))return r?r.a=i.a:e.b[t]=i.a,fAe(u(uf(i.c),593),u(uf(i.f),593)),XC(u(uf(i.b),227),u(uf(i.e),227)),--e.f,++e.e,!0;return!1}function oDn(e){var n,t;for(t=new qn(Vn(rr(e).a.Jc(),new ee));ht(t);)if(n=u(tt(t),17),n.c.i.k!=(zn(),Uu))throw $(new wd(iee+LO(e)+"' has its layer constraint set to FIRST, but has at least one incoming edge that does not come from a FIRST_SEPARATE node. That must not happen."))}function XUe(e,n){var t,i,r,c,o,l,f,h,b,p,y;r=n?new nw:new cM,c=!1;do for(c=!1,h=n?Ks(e.b):e.b,f=h.Jc();f.Ob();)for(l=u(f.Pb(),25),y=qb(l.a),n||Ks(y),p=new L(y);p.a=0;o+=r?1:-1){for(l=n[o],f=i==(De(),et)?r?mu(l,i):Ks(mu(l,i)):r?Ks(mu(l,i)):mu(l,i),c&&(e.c[l.p]=f.gc()),p=f.Jc();p.Ob();)b=u(p.Pb(),12),e.d[b.p]=h++;Er(t,f)}}function VUe(e,n,t){var i,r,c,o,l,f,h,b;for(c=ne(re(e.b.Jc().Pb())),h=ne(re(J7n(n.b))),i=A1(pc(e.a),h-t),r=A1(pc(n.a),t-c),b=gi(i,r),A1(b,1/(h-c)),this.a=b,this.b=new Te,l=!0,o=e.b.Jc(),o.Pb();o.Ob();)f=ne(re(o.Pb())),l&&f-t>Xee&&(this.b.Ec(t),l=!1),this.b.Ec(f);l&&this.b.Ec(t)}function lDn(e){var n,t,i,r;if(AIn(e,e.n),e.d.c.length>0){for(yj(e.c);obe(e,u(_(new L(e.e.a)),124))>5,n&=31,i>=e.d)return e.e<0?(mh(),rnn):(mh(),KS);if(c=e.d-i,r=oe($t,ni,30,c+1,15,1),dCn(r,c,e.a,i,n),e.e<0){for(t=0;t0&&e.a[t]<<32-n!=0){for(t=0;t=0?!1:(t=av((ls(),nc),r,n),t?(i=t.Gk(),(i>1||i==-1)&&xw(Vc(nc,t))!=3):!0)):!1}function gDn(e,n,t,i){var r,c,o,l,f,h,b,p,y,S;if(f=e.c.d,h=e.d.d,f.j!=h.j)for(S=e.b,b=null,l=null,o=OEn(e),o&&S.i&&(b=e.b.i.i,l=S.i.j),r=f.j,p=null;r!=h.j;)p=n==0?HB(r):X1e(r),c=xde(r,S.d[r.g],t),y=xde(p,S.d[p.g],t),o&&b&&l&&(r==b?JFe(c,b,l):p==b&&JFe(y,b,l)),Vt(i,gi(c,y)),r=p}function Obe(e,n,t){var i,r,c,o,l,f;if(i=cgn(t,e.length),o=e[i],c=gAe(t,o.length),o[c].k==(zn(),wr))for(f=n.j,r=0;r0&&(t[0]+=e.d,o-=t[0]),t[2]>0&&(t[2]+=e.d,o-=t[2]),c=k.Math.max(0,o),t[1]=k.Math.max(t[1],o),Qae(e,No,r.c+i.b+t[0]-(t[1]-o)/2,t),n==No&&(e.c.b=c,e.c.c=r.c+i.b+(c-o)/2)}function iXe(){this.c=oe(Jr,Jc,30,(De(),z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn])).length,15,1),this.b=oe(Jr,Jc,30,z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn]).length,15,1),this.a=oe(Jr,Jc,30,z(B(xc,1),qu,64,0,[ku,Xn,et,bt,Kn]).length,15,1),use(this.c,Ki),use(this.b,Dr),use(this.a,Dr)}function kDn(e,n,t,i){var r,c,o,l,f;for(f=n.i,l=t[f.g][e.d[f.g]],r=!1,o=new L(n.d);o.a=r&&(e.c=!1,e.a=!1),e.b[i++]=r,e.b[i]=c,e.c||ov(e)}}function jDn(e,n,t){var i,r,c,o,l,f,h;for(h=n.d,e.a=new xo(h.c.length),e.c=new pt,l=new L(h);l.a=0?e.Ih(h,!1,!0):Gw(e,t,!1),61));e:for(c=p.Jc();c.Ob();){for(r=u(c.Pb(),57),b=0;be.d[o.p]&&(t+=Hae(e.b,c),T0(e.a,ve(c)));for(;!kj(e.a);)Ehe(e.b,u(A4(e.a),15).a)}return t}function uXe(e,n,t){var i,r,c,o;for(c=(!n.a&&(n.a=new pe(Jt,n,10,11)),n.a).i,r=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));r.e!=r.i.gc();)i=u(ft(r),26),(!i.a&&(i.a=new pe(Jt,i,10,11)),i.a).i==0||(c+=uXe(e,i,!1));if(t)for(o=Bi(n);o;)c+=(!o.a&&(o.a=new pe(Jt,o,10,11)),o.a).i,o=Bi(o);return c}function U2(e,n){var t,i,r,c;return e.Nj()?(i=null,r=e.Oj(),e.Rj()&&(i=e.Tj(e.Yi(n),null)),t=e.Gj(4,c=V4(e,n),null,n,r),e.Kj()&&c!=null&&(i=e.Mj(c,i)),i?(i.lj(t),i.mj()):e.Hj(t),c):(c=V4(e,n),e.Kj()&&c!=null&&(i=e.Mj(c,null),i&&i.mj()),c)}function TDn(e){var n,t,i,r,c,o,l,f,h,b;for(h=e.a,n=new ar,f=0,i=new L(e.d);i.al.d&&(b=l.d+l.a+h));t.c.d=b,n.a.yc(t,n),f=k.Math.max(f,t.c.d+t.c.a)}return f}function ODn(e,n,t){var i,r,c,o,l,f;for(o=u(C(e,(me(),wie)),16).Jc();o.Ob();){switch(c=u(o.Pb(),9),u(C(c,(Ne(),yu)),165).g){case 2:Or(c,n);break;case 4:Or(c,t)}for(r=new qn(Vn(bh(c).a.Jc(),new ee));ht(r);)i=u(tt(r),17),!(i.c&&i.d)&&(l=!i.d,f=u(C(i,Qve),12),l?Gr(i,f):lc(i,f))}}function Dc(){Dc=Y,QJ=new c2("COMMENTS",0),Kl=new c2("EXTERNAL_PORTS",1),cx=new c2("HYPEREDGES",2),WJ=new c2("HYPERNODES",3),S7=new c2("NON_FREE_PORTS",4),Nv=new c2("NORTH_SOUTH_PORTS",5),ux=new c2(MQe,6),j7=new c2("CENTER_LABELS",7),E7=new c2("END_LABELS",8),ZJ=new c2("PARTITIONS",9)}function NDn(e,n,t,i,r){return i<0?(i=uv(e,r,z(B(ze,1),Se,2,6,[mZ,vZ,yZ,kZ,uy,jZ,EZ,SZ,xZ,AZ,MZ,CZ]),n),i<0&&(i=uv(e,r,z(B(ze,1),Se,2,6,["Jan","Feb","Mar","Apr",uy,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),n)),i<0?!1:(t.k=i,!0)):i>0?(t.k=i-1,!0):!1}function DDn(e,n,t,i,r){return i<0?(i=uv(e,r,z(B(ze,1),Se,2,6,[mZ,vZ,yZ,kZ,uy,jZ,EZ,SZ,xZ,AZ,MZ,CZ]),n),i<0&&(i=uv(e,r,z(B(ze,1),Se,2,6,["Jan","Feb","Mar","Apr",uy,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"]),n)),i<0?!1:(t.k=i,!0)):i>0?(t.k=i-1,!0):!1}function IDn(e,n,t,i,r,c){var o,l,f,h;if(l=32,i<0){if(n[0]>=e.length||(l=ic(e,n[0]),l!=43&&l!=45)||(++n[0],i=Az(e,n),i<0))return!1;l==45&&(i=-i)}return l==32&&n[0]-t==2&&r.b==2&&(f=new t$,h=f.q.getFullYear()-K0+K0-80,o=h%100,c.a=i==o,i+=(h/100|0)*100+(i=0?B0(e):sE(B0(Cd(e)))),VS[n]=T$(Gh(e,n),0)?B0(Gh(e,n)):sE(B0(Cd(Gh(e,n)))),e=ac(e,5);for(;n=h&&(f=i);f&&(b=k.Math.max(b,f.a.o.a)),b>y&&(p=h,y=b)}return p}function RDn(e){var n,t,i,r,c,o,l;for(c=new vd(u(Nt(new h5),51)),l=Dr,t=new L(e.d);t.aiWe?Tr(f,e.b):i<=iWe&&i>rWe?Tr(f,e.d):i<=rWe&&i>cWe?Tr(f,e.c):i<=cWe&&Tr(f,e.a),c=fXe(e,f,c);return r}function aXe(e,n,t,i){var r,c,o,l,f,h;for(r=(i.c+i.a)/2,qs(n.j),Vt(n.j,r),qs(t.e),Vt(t.e,r),h=new dAe,l=new L(e.f);l.a1,l&&(i=new je(r,t.b),Vt(n.a,i)),SE(n.a,z(B(Lr,1),Se,8,0,[y,p]))}function Ibe(e,n,t){var i,r;for(n=48;t--)hA[t]=t-48<<24>>24;for(i=70;i>=65;i--)hA[i]=i-65+10<<24>>24;for(r=102;r>=97;r--)hA[r]=r-97+10<<24>>24;for(c=0;c<10;c++)CG[c]=48+c&yr;for(e=10;e<=15;e++)CG[e]=65+e-10&yr}function gXe(e,n){n.Tg("Process graph bounds",1),he(e,(Ci(),wre),cT(HY(k2(new wn(null,new pn(e.b,16)),new Zq)))),he(e,pre,cT(HY(k2(new wn(null,new pn(e.b,16)),new Bs)))),he(e,mye,cT(JY(k2(new wn(null,new pn(e.b,16)),new yM)))),he(e,vye,cT(JY(k2(new wn(null,new pn(e.b,16)),new kM)))),n.Ug()}function HDn(e){var n,t,i,r,c;r=u(C(e,(Ne(),jg)),22),c=u(C(e,vH),22),t=new je(e.f.a+e.d.b+e.d.c,e.f.b+e.d.d+e.d.a),n=new wc(t),r.Gc((Vs(),Lm))&&(i=u(C(e,M7),8),c.Gc((_s(),q7))&&(i.a<=0&&(i.a=20),i.b<=0&&(i.b=20)),n.a=k.Math.max(t.a,i.a),n.b=k.Math.max(t.b,i.b)),Re($e(C(e,Rie)))||gLn(e,t,n)}function GDn(e){var n,t,i,r,c,o,l;for(n=!1,t=0,r=new L(e.d.b);r.a>19!=0)return"-"+wXe(e8(e));for(t=e,i="";!(t.l==0&&t.m==0&&t.h==0);){if(r=sY(tF),t=mge(t,r,!0),n=""+NAe(Z0),!(t.l==0&&t.m==0&&t.h==0))for(c=9-n.length;c>0;c--)n="0"+n;i=n+i}return i}function qDn(){if(!Object.create||!Object.getOwnPropertyNames)return!1;var e="__proto__",n=Object.create(null);if(n[e]!==void 0)return!1;var t=Object.getOwnPropertyNames(n);return!(t.length!=0||(n[e]=42,n[e]!==42)||Object.getOwnPropertyNames(n).length==0)}function UDn(e,n,t){var i,r,c,o,l,f,h,b,p;for(i=t.c,r=t.d,l=_a(n.c),f=_a(n.d),i==n.c?(l=vbe(e,l,r),f=pGe(n.d)):(l=pGe(n.c),f=vbe(e,f,r)),h=new qP(n.a),Xi(h,l,h.a,h.a.a),Xi(h,f,h.c.b,h.c),o=n.c==i,p=new cxe,c=0;c=e.a||!b0e(n,t))return-1;if(x2(u(i.Kb(n),20)))return 1;for(r=0,o=u(i.Kb(n),20).Jc();o.Ob();)if(c=u(o.Pb(),17),f=c.c.i==n?c.d.i:c.c.i,l=Pbe(e,f,t,i),l==-1||(r=k.Math.max(r,l),r>e.c-1))return-1;return r+1}function Ja(){Ja=Y,UH=new Vr((Xt(),$7),1.3),Mln=new Vr(Om,(Pn(),!1)),k6e=new pw(15),Bx=new Vr(o1,k6e),zx=new Vr(Vd,15),Eln=ND,Aln=Cg,Cln=Yv,Tln=ab,xln=Vv,qre=LD,Oln=Nm,x6e=(nge(),yln),S6e=vln,Xre=jln,A6e=kln,y6e=wln,Ure=gln,v6e=bln,E6e=mln,p6e=_D,Sln=wce,xD=aln,w6e=fln,AD=hln,j6e=pln,m6e=dln}function pXe(e,n){var t,i,r,c,o,l;if(ue(n)===ue(e))return!0;if(!X(n,16)||(i=u(n,16),l=e.gc(),i.gc()!=l))return!1;if(o=i.Jc(),e.Wi()){for(t=0;t0){if(e.Zj(),n!=null){for(c=0;c>24;case 97:case 98:case 99:case 100:case 101:case 102:return e-97+10<<24>>24;case 65:case 66:case 67:case 68:case 69:case 70:return e-65+10<<24>>24;default:throw $(new sh("Invalid hexadecimal"))}}function vXe(e,n,t,i){var r,c,o,l,f,h;for(f=tW(e,t),h=tW(n,t),r=!1;f&&h&&(i||WSn(f,h,t));)o=tW(f,t),l=tW(h,t),iO(n),iO(e),c=f.c,tZ(f,!1),tZ(h,!1),t?(z0(n,h.p,c),n.p=h.p,z0(e,f.p+1,c),e.p=f.p):(z0(e,f.p,c),e.p=f.p,z0(n,h.p+1,c),n.p=h.p),Or(f,null),Or(h,null),f=o,h=l,r=!0;return r}function yXe(e){switch(e.g){case 0:return new rP;case 1:return new cP;case 3:return new TMe;case 4:return new A6;case 5:return new rNe;case 6:return new ko;case 2:return new uP;case 7:return new kC;case 8:return new yC;default:throw $(new Gn("No implementation is available for the layerer "+(e.f!=null?e.f:""+e.g)))}}function YDn(e,n,t,i){var r,c,o,l,f;for(r=!1,c=!1,l=new L(i.j);l.a=n.length)throw $(new jo("Greedy SwitchDecider: Free layer not in graph."));this.c=n[e],this.e=new TT(i),$Y(this.e,this.c,(De(),Kn)),this.i=new TT(i),$Y(this.i,this.c,et),this.f=new TDe(this.c),this.a=!c&&r.i&&!r.s&&this.c[0].k==(zn(),wr),this.a&&mCn(this,e,n.length)}function jXe(e,n){var t,i,r,c,o,l;c=!e.B.Gc((_s(),UD)),o=e.B.Gc(Tce),e.a=new cJe(o,c,e.c),e.n&&sae(e.a.n,e.n),xX(e.g,(ga(),No),e.a),n||(i=new JE(1,c,e.c),i.n.a=e.k,M4(e.p,(De(),Xn),i),r=new JE(1,c,e.c),r.n.d=e.k,M4(e.p,bt,r),l=new JE(0,c,e.c),l.n.c=e.k,M4(e.p,Kn,l),t=new JE(0,c,e.c),t.n.b=e.k,M4(e.p,et,t))}function WDn(e){var n,t,i;switch(n=u(C(e.d,(Ne(),Y1)),222),n.g){case 2:t=zRn(e);break;case 3:t=(i=new Te,Zi(si(So(ou(ou(new wn(null,new pn(e.d.b,16)),new Wg),new ZI),new vk),new l0),new Hje(i)),i);break;default:throw $(new Uc("Compaction not supported for "+n+" edges."))}fPn(e,t),rc(new nt(e.g),new Bje(e))}function ZDn(e,n){var t,i,r,c,o,l,f;if(n.Tg("Process directions",1),t=u(C(e,(Mu(),mp)),86),t!=(vr(),Za))for(r=St(e.b,0);r.b!=r.d.c;){switch(i=u(jt(r),40),l=u(C(i,(Ci(),jD)),15).a,f=u(C(i,ED),15).a,t.g){case 4:f*=-1;break;case 1:c=l,l=f,f=c;break;case 2:o=l,l=-f,f=o}he(i,jD,ve(l)),he(i,ED,ve(f))}n.Ug()}function eIn(e){var n,t,i,r,c,o,l,f;for(f=new BPe,l=new L(e.a);l.a0&&n=0)return!1;if(n.p=t.b,Ce(t.e,n),r==(zn(),dr)||r==wo){for(o=new L(n.j);o.ae.d[l.p]&&(t+=Hae(e.b,c),T0(e.a,ve(c)))):++o;for(t+=e.b.d*o;!kj(e.a);)Ehe(e.b,u(A4(e.a),15).a)}return t}function IXe(e){var n,t,i,r,c,o;return c=0,n=ff(e),n.ik()&&(c|=4),(e.Bb&as)!=0&&(c|=2),X(e,103)?(t=u(e,19),r=Oc(t),(t.Bb&Bu)!=0&&(c|=32),r&&(dt(E2(r)),c|=8,o=r.t,(o>1||o==-1)&&(c|=16),(r.Bb&Bu)!=0&&(c|=64)),(t.Bb&Ec)!=0&&(c|=U0),c|=qf):X(n,459)?c|=512:(i=n.ik(),i&&(i.i&1)!=0&&(c|=256)),(e.Bb&512)!=0&&(c|=128),c}function hIn(e,n){var t;return e.f==Hce?(t=xw(Vc((ls(),nc),n)),e.e?t==4&&n!=(ey(),Ky)&&n!=(ey(),Xy)&&n!=(ey(),Gce)&&n!=(ey(),qce):t==2):e.d&&(e.d.Gc(n)||e.d.Gc(D4(Vc((ls(),nc),n)))||e.d.Gc(av((ls(),nc),e.b,n)))?!0:e.f&&jbe((ls(),e.f),BT(Vc(nc,n)))?(t=xw(Vc(nc,n)),e.e?t==4:t==2):!1}function dIn(e,n){var t,i,r,c,o,l,f,h;for(c=new Te,n.b.c.length=0,t=u(gs(Eae(new wn(null,new pn(new nt(e.a.b),1))),Cs(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),r=t.Jc();r.Ob();)if(i=u(r.Pb(),15),o=Pae(e.a,i),o.b!=0)for(l=new Xu(n),Hn(c.c,l),l.p=i.a,h=St(o,0);h.b!=h.d.c;)f=u(jt(h),9),Or(f,l);Er(n.b,c)}function _W(e){var n,t,i,r,c,o,l;for(l=new pt,i=new L(e.a.b);i.aag&&(r-=ag),l=u(ye(i,Fy),8),h=l.a,p=l.b+e,c=k.Math.atan2(p,h),c<0&&(c+=ag),c+=n,c>ag&&(c-=ag),Oa(),Bf(1e-10),k.Math.abs(r-c)<=1e-10||r==c||isNaN(r)&&isNaN(c)?0:rc?1:Lb(isNaN(r),isNaN(c))}function Fbe(e,n,t,i){var r,c,o;n&&(c=ne(re(C(n,(Ci(),Xd))))+i,o=t+ne(re(C(n,PH)))/2,he(n,jD,ve(Rt(Pu(k.Math.round(c))))),he(n,ED,ve(Rt(Pu(k.Math.round(o))))),n.d.b==0||Fbe(e,u(P$((r=St(new S1(n).a.d,0),new E3(r))),40),t+ne(re(C(n,PH)))+e.b,i+ne(re(C(n,L7)))),C(n,vre)!=null&&Fbe(e,u(C(n,vre),40),t,i))}function pIn(e,n){var t,i,r,c;if(c=u(ye(e,(Xt(),Qv)),64).g-u(ye(n,Qv),64).g,c!=0)return c;if(t=u(ye(e,kce),15),i=u(ye(n,kce),15),t&&i&&(r=t.a-i.a,r!=0))return r;switch(u(ye(e,Qv),64).g){case 1:return ki(e.i,n.i);case 2:return ki(e.j,n.j);case 3:return ki(n.i,e.i);case 4:return ki(n.j,e.j);default:throw $(new Uc(dwe))}}function Jbe(e){var n,t,i;return(e.Db&64)!=0?bW(e):(n=new il(R2e),t=e.k,t?Kt(Kt((n.a+=' "',n),t),'"'):(!e.n&&(e.n=new pe(ju,e,1,7)),e.n.i>0&&(i=(!e.n&&(e.n=new pe(ju,e,1,7)),u(K(e.n,0),157)).a,!i||Kt(Kt((n.a+=' "',n),i),'"'))),Kt(dw(Kt(dw(Kt(dw(Kt(dw((n.a+=" (",n),e.i),","),e.j)," | "),e.g),","),e.f),")"),n.a)}function _Xe(e){var n,t,i;return(e.Db&64)!=0?bW(e):(n=new il(B2e),t=e.k,t?Kt(Kt((n.a+=' "',n),t),'"'):(!e.n&&(e.n=new pe(ju,e,1,7)),e.n.i>0&&(i=(!e.n&&(e.n=new pe(ju,e,1,7)),u(K(e.n,0),157)).a,!i||Kt(Kt((n.a+=' "',n),i),'"'))),Kt(dw(Kt(dw(Kt(dw(Kt(dw((n.a+=" (",n),e.i),","),e.j)," | "),e.g),","),e.f),")"),n.a)}function mIn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;for(S=-1,A=0,b=n,p=0,y=b.length;p0&&++A;++S}return A}function vIn(e,n){var t,i,r,c,o;for(n==(DE(),cre)&&HO(u(pi(e.a,(z2(),ZN)),16)),r=u(pi(e.a,(z2(),ZN)),16).Jc();r.Ob();)switch(i=u(r.Pb(),107),t=u(Le(i.j,0),113).d.j,c=new bs(i.j),Tr(c,new M5),n.g){case 2:oW(e,c,t,(_w(),ib),1);break;case 1:case 0:o=lNn(c),oW(e,new C0(c,0,o),t,(_w(),ib),0),oW(e,new C0(c,o,c.c.length),t,ib,1)}}function yIn(e){var n,t,i,r,c,o,l;for(r=u(C(e,(me(),ap)),9),i=e.j,t=(vn(0,i.c.length),u(i.c[0],12)),o=new L(r.j);o.ar.p?(xr(c,bt),c.d&&(l=c.o.b,n=c.a.b,c.a.b=l-n)):c.j==bt&&r.p>e.p&&(xr(c,Xn),c.d&&(l=c.o.b,n=c.a.b,c.a.b=-(l-n)));break}return r}function Hbe(e,n){var t,i,r,c,o,l,f;if(n==null||n.length==0)return null;if(r=u(lo(e.a,n),144),!r){for(i=(l=new ct(e.b).a.vc().Jc(),new Ji(l));i.a.Ob();)if(t=(c=u(i.a.Pb(),45),u(c.kd(),144)),o=t.c,f=n.length,bn(o.substr(o.length-f,f),n)&&(n.length==o.length||ic(o,o.length-n.length-1)==46)){if(r)return null;r=t}r&&Kc(e.a,n,r)}return r}function M8(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(c=new je(n,t),b=new L(e.a);b.a1,l&&(i=new je(r,t.b),Vt(n.a,i)),SE(n.a,z(B(Lr,1),Se,8,0,[y,p]))}function G0(){G0=Y,AH=new u2(ma,0),gD=new u2("NIKOLOV",1),wD=new u2("NIKOLOV_PIXEL",2),P4e=new u2("NIKOLOV_IMPROVED",3),$4e=new u2("NIKOLOV_IMPROVED_PIXEL",4),L4e=new u2("DUMMYNODE_PERCENTAGE",5),R4e=new u2("NODECOUNT_PERCENTAGE",6),MH=new u2("NO_BOUNDARY",7),D7=new u2("MODEL_ORDER_LEFT_TO_RIGHT",8),Sx=new u2("MODEL_ORDER_RIGHT_TO_LEFT",9)}function PW(e,n){var t,i,r,c,o,l,f,h,b,p,y,S;return b=null,y=hbe(e,n),i=null,l=u(ye(n,(Xt(),Cfn)),300),l?i=l:i=(jE(),HD),S=i,S==(jE(),HD)&&(r=null,h=u(Bn(e.r,y),300),h?r=h:r=Cce,S=r),ei(e.r,n,S),c=null,f=u(ye(n,Mfn),278),f?c=f:c=(u8(),$D),p=c,p==(u8(),$D)&&(o=null,t=u(Bn(e.b,y),278),t?o=t:o=uG,p=o),b=u(ei(e.b,n,p),278),b}function NIn(e){var n,t,i,r,c;for(i=e.length,n=new jj,c=0;c=40,o&&O_n(e),JLn(e),lDn(e),t=$Fe(e),i=0;t&&i0&&Vt(e.g,c)):(e.d[o]-=h+1,e.d[o]<=0&&e.a[o]>0&&Vt(e.f,c))))}function XXe(e,n,t,i){var r,c,o,l,f,h,b;for(f=new je(t,i),Nr(f,u(C(n,(Ci(),_7)),8)),b=St(n.b,0);b.b!=b.d.c;)h=u(jt(b),40),gi(h.e,f),Vt(e.b,h);for(l=u(gs(yae(new wn(null,new pn(n.a,16))),Cs(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16).Jc();l.Ob();){for(o=u(l.Pb(),65),c=St(o.a,0);c.b!=c.d.c;)r=u(jt(c),8),r.a+=f.a,r.b+=f.b;Vt(e.a,o)}}function Qbe(e,n){var t,i,r,c;if(0<(X(e,18)?u(e,18).gc():aa(e.Jc()))){if(r=n,1=0&&f1)&&n==1&&u(e.a[e.b],9).k==(zn(),Uu)?iy(u(e.a[e.b],9),(al(),s1)):i&&(!t||(e.c-e.b&e.a.length-1)>1)&&n==1&&u(e.a[e.c-1&e.a.length-1],9).k==(zn(),Uu)?iy(u(e.a[e.c-1&e.a.length-1],9),(al(),hb)):(e.c-e.b&e.a.length-1)==2?(iy(u(TE(e),9),(al(),s1)),iy(u(TE(e),9),hb)):COn(e,r),zae(e)}function KIn(e){var n,t,i,r,c,o,l,f;for(f=new pt,n=new gX,o=e.Jc();o.Ob();)r=u(o.Pb(),9),l=mw(nT(new Nb,r),n),Ko(f.f,r,l);for(c=e.Jc();c.Ob();)for(r=u(c.Pb(),9),i=new qn(Vn(Ni(r).a.Jc(),new ee));ht(i);)t=u(tt(i),17),!cc(t)&&Hf(Nf(Of(Tf(Df(new tf,k.Math.max(1,u(C(t,(Ne(),g4e)),15).a)),1),u(Bn(f,t.c.i),124)),u(Bn(f,t.d.i),124)));return n}function YXe(e,n,t,i){var r,c,o,l,f,h,b,p,y,S;if(x8n(e,n,t),c=n[t],S=i?(De(),Kn):(De(),et),Vwn(n.length,t,i)){for(r=n[i?t-1:t+1],rhe(e,r,i?(Nc(),Do):(Nc(),ys)),f=c,b=0,y=f.length;bc*2?(b=new dB(p),h=us(o)/Gs(o),f=uZ(b,n,new t4,t,i,r,h),gi(la(b.e),f),p.c.length=0,c=0,Hn(p.c,b),Hn(p.c,o),c=us(b)*Gs(b)+us(o)*Gs(o)):(Hn(p.c,o),c+=us(o)*Gs(o));return p}function YIn(e,n){var t,i,r,c,o,l,f;for(n.Tg("Port order processing",1),f=u(C(e,(Ne(),b4e)),421),i=new L(e.b);i.at?n:t;h<=p;++h)h==t?l=i++:(c=r[h],b=A.$l(c.Jk()),h==n&&(f=h==p&&!b?i-1:i),b&&++i);return y=u(RE(e,n,t),75),l!=f&&s9(e,new tO(e.e,7,o,ve(l),S.kd(),f)),y}}else return u(jW(e,n,t),75);return u(RE(e,n,t),75)}function Wbe(e,n){var t,i,r,c,o,l,f,h,b,p;for(p=0,c=new P3,T0(c,n);c.b!=c.c;)for(f=u(A4(c),218),h=0,b=u(C(n.j,(Ne(),u1)),269),u(C(n.j,bx),329),o=ne(re(C(n.j,lD))),l=ne(re(C(n.j,Aie))),b!=(F1(),ob)&&(h+=o*LOn(n.j,f.e,b),h+=l*mIn(n.j,f.e)),p+=vHe(f.d,f.e)+h,r=new L(f.b);r.a=0&&(l=axn(e,o),!(l&&(h<22?f.l|=1<>>1,o.m=b>>>1|(p&1)<<21,o.l=y>>>1|(b&1)<<21,--h;return t&&ZY(f),c&&(i?(Z0=e8(e),r&&(Z0=xze(Z0,(G9(),Eme)))):Z0=_o(e.l,e.m,e.h)),f}function ZIn(e,n){var t,i,r,c,o,l,f,h,b,p;for(h=e.e[n.c.p][n.p]+1,f=n.c.a.c.length+1,l=new L(e.a);l.a0&&(Yn(0,e.length),e.charCodeAt(0)==45||(Yn(0,e.length),e.charCodeAt(0)==43))?1:0,i=o;it)throw $(new sh(Yw+e+'"'));return l}function e_n(e){var n,t,i,r,c,o,l;for(o=new Si,c=new L(e.a);c.a=e.length)return t.o=0,!0;switch(ic(e,n[0])){case 43:r=1;break;case 45:r=-1;break;default:return t.o=0,!0}if(++n[0],c=n[0],o=Az(e,n),o==0&&n[0]==c)return!1;if(n[0]l&&(l=r,b.c.length=0),r==l&&Ce(b,new jc(t.c.i,t)));jn(),Tr(b,e.c),Pb(e.b,f.p,b)}}function u_n(e,n){var t,i,r,c,o,l,f,h,b;for(o=new L(n.b);o.al&&(l=r,b.c.length=0),r==l&&Ce(b,new jc(t.d.i,t)));jn(),Tr(b,e.c),Pb(e.f,f.p,b)}}function o_n(e){var n,t,i,r,c,o,l;for(c=Ia(e),r=new ut((!e.e&&(e.e=new Nn(pr,e,7,4)),e.e));r.e!=r.i.gc();)if(i=u(ft(r),85),l=iu(u(K((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c),0),84)),!T2(l,c))return!0;for(t=new ut((!e.d&&(e.d=new Nn(pr,e,8,5)),e.d));t.e!=t.i.gc();)if(n=u(ft(t),85),o=iu(u(K((!n.b&&(n.b=new Nn(vt,n,4,7)),n.b),0),84)),!T2(o,c))return!0;return!1}function s_n(e){var n,t,i,r,c;i=u(C(e,(me(),wi)),26),c=u(ye(i,(Ne(),jg)),182).Gc((Vs(),Og)),e.e||(r=u(C(e,po),22),n=new je(e.f.a+e.d.b+e.d.c,e.f.b+e.d.d+e.d.a),r.Gc((Dc(),Kl))?(ji(i,Wi,(Br(),to)),Xw(i,n.a,n.b,!1,!0)):Re($e(ye(i,Rie)))||Xw(i,n.a,n.b,!0,!0)),c?ji(i,jg,nn(Og)):ji(i,jg,(t=u(sa(tA),10),new _l(t,u(_f(t,t.length),10),0)))}function l_n(e,n){var t,i,r,c,o,l,f,h;if(h=$e(C(n,(Mu(),ksn))),h==null||(_n(h),h)){for(RTn(e,n),r=new Te,f=St(n.b,0);f.b!=f.d.c;)o=u(jt(f),40),t=P0e(e,o,null),t&&($u(t,n),Hn(r.c,t));if(e.a=null,e.b=null,r.c.length>1)for(i=new L(r);i.a=0&&l!=t&&(c=new Ir(e,1,l,o,null),i?i.lj(c):i=c),t>=0&&(c=new Ir(e,1,t,l==t?o:null,n),i?i.lj(c):i=c)),i}function WXe(e){var n,t,i;if(e.b==null){if(i=new pd,e.i!=null&&(Bc(i,e.i),i.a+=":"),(e.f&256)!=0){for((e.f&256)!=0&&e.a!=null&&(j5n(e.i)||(i.a+="//"),Bc(i,e.a)),e.d!=null&&(i.a+="/",Bc(i,e.d)),(e.f&16)!=0&&(i.a+="/"),n=0,t=e.j.length;ny?!1:(p=(f=fS(i,y,!1),f.a),b+l+p<=n.b&&(eO(t,c-t.s),t.c=!0,eO(i,c-t.s),IO(i,t.s,t.t+t.d+l),i.k=!0,i1e(t.q,i),S=!0,r&&(vB(n,i),i.j=n,e.c.length>o&&($O((vn(o,e.c.length),u(e.c[o],186)),i),(vn(o,e.c.length),u(e.c[o],186)).a.c.length==0&&Ad(e,o)))),S)}function w_n(e,n){var t,i,r,c,o,l;if(n.Tg("Partition midprocessing",1),r=new Cw,Zi(si(new wn(null,new pn(e.a,16)),new w6),new Eje(r)),r.d!=0){for(l=u(gs(Eae((c=r.i,new wn(null,(c||(r.i=new R3(r,r.c))).Lc()))),Cs(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),i=l.Jc(),t=u(i.Pb(),15);i.Ob();)o=u(i.Pb(),15),sNn(u(pi(r,t),22),u(pi(r,o),22)),t=o;n.Ug()}}function uS(e,n){var t,i,r,c,o;if(e.Ab){if(e.Ab){if(o=e.Ab.i,o>0){if(r=u(e.Ab.g,1995),n==null){for(c=0;ct.s&&lf+A&&(O=p.g+y.g,y.a=(y.g*y.a+p.g*p.a)/O,y.g=O,p.f=y,t=!0)),c=l,p=y;return t}function v_n(e,n,t){var i,r,c,o,l,f,h,b;for(t.Tg(UQe,1),Hu(e.b),Hu(e.a),l=null,c=St(n.b,0);!l&&c.b!=c.d.c;)h=u(jt(c),40),Re($e(C(h,(Ci(),fb))))&&(l=h);for(f=new Si,Xi(f,l,f.c.b,f.c),NVe(e,f),b=St(n.b,0);b.b!=b.d.c;)h=u(jt(b),40),o=Pt(C(h,(Ci(),Ix))),r=lo(e.b,o)!=null?u(lo(e.b,o),15).a:0,he(h,gre,ve(r)),i=1+(lo(e.a,o)!=null?u(lo(e.a,o),15).a:0),he(h,pye,ve(i));t.Ug()}function rKe(e){r2(e,new Jw(t2(Zp(n2(e2(new dd,tp),"ELK Box"),"Algorithm for packing of unconnected boxes, i.e. graphs without edges."),new GM))),Ee(e,tp,nm,g9e),Ee(e,tp,em,15),Ee(e,tp,EN,ve(0)),Ee(e,tp,T2e,_e(h9e)),Ee(e,tp,wv,_e(gfn)),Ee(e,tp,hy,_e(wfn)),Ee(e,tp,G8,wWe),Ee(e,tp,jS,_e(d9e)),Ee(e,tp,dy,_e(b9e)),Ee(e,tp,O2e,_e(lce)),Ee(e,tp,CF,_e(bfn))}function cKe(e,n){var t,i,r,c,o,l,f,h,b;if(r=e.i,o=r.o.a,c=r.o.b,o<=0&&c<=0)return De(),ku;switch(h=e.n.a,b=e.n.b,l=e.o.a,t=e.o.b,n.g){case 2:case 1:if(h<0)return De(),Kn;if(h+l>o)return De(),et;break;case 4:case 3:if(b<0)return De(),Xn;if(b+t>c)return De(),bt}return f=(h+l/2)/o,i=(b+t/2)/c,f+i<=1&&f-i<=0?(De(),Kn):f+i>=1&&f-i>=0?(De(),et):i<.5?(De(),Xn):(De(),bt)}function uKe(e,n,t,i,r,c,o){var l,f,h,b,p,y;for(y=new g4,h=n.Jc();h.Ob();)for(l=u(h.Pb(),837),p=new L(l.Pf());p.a0?l.a?(h=l.b.Kf().b,r>h&&(e.v||l.c.d.c.length==1?(o=(r-h)/2,l.d.d=o,l.d.a=o):(t=u(Le(l.c.d,0),187).Kf().b,i=(t-h)/2,l.d.d=k.Math.max(0,i),l.d.a=r-i-h))):l.d.a=e.t+r:cE(e.u)&&(c=m0e(l.b),c.d<0&&(l.d.d=-c.d),c.d+c.a>l.b.Kf().b&&(l.d.a=c.d+c.a-l.b.Kf().b))}function Gf(){Gf=Y,ky=new Vr((Xt(),PD),ve(1)),vJ=new Vr(Vd,80),Stn=new Vr(q9e,5),btn=new Vr($7,H8),jtn=new Vr(Ece,ve(1)),Etn=new Vr(Sce,(Pn(),!0)),c3e=new pw(50),ytn=new Vr(o1,c3e),t3e=_D,u3e=Kx,gtn=new Vr(dce,!1),r3e=LD,mtn=Om,vtn=ab,ptn=Cg,wtn=Vv,ktn=Nm,i3e=(C0e(),otn),kte=atn,mJ=utn,yte=stn,o3e=ftn,Mtn=z7,Ctn=rG,Atn=Im,xtn=B7,s3e=(G4(),Pm),new Vr(Hy,s3e)}function j_n(e,n){var t;switch(lO(e)){case 6:return $r(n);case 7:return s2(n);case 8:return o2(n);case 3:return Array.isArray(n)&&(t=lO(n),!(t>=14&&t<=16));case 11:return n!=null&&typeof n===aZ;case 12:return n!=null&&(typeof n===sN||typeof n==aZ);case 0:return PQ(n,e.__elementTypeId$);case 2:return pV(n)&&n.Rm!==Qn;case 1:return pV(n)&&n.Rm!==Qn||PQ(n,e.__elementTypeId$);default:return!0}}function E_n(e){var n,t,i,r;i=e.o,h2(),e.A.dc()||di(e.A,Qme)?r=i.a:(e.D?r=k.Math.max(i.a,QE(e.f)):r=QE(e.f),e.A.Gc((Vs(),GD))&&!e.B.Gc((_s(),iA))&&(r=k.Math.max(r,QE(u(zc(e.p,(De(),Xn)),253))),r=k.Math.max(r,QE(u(zc(e.p,bt),253)))),n=nze(e),n&&(r=k.Math.max(r,n.a))),Re($e(e.e.Rf().mf((Xt(),Om))))?i.a=k.Math.max(i.a,r):i.a=r,t=e.f.i,t.c=0,t.b=r,HW(e.f)}function oKe(e,n){var t,i,r,c;return i=k.Math.min(k.Math.abs(e.c-(n.c+n.b)),k.Math.abs(e.c+e.b-n.c)),c=k.Math.min(k.Math.abs(e.d-(n.d+n.a)),k.Math.abs(e.d+e.a-n.d)),t=k.Math.abs(e.c+e.b/2-(n.c+n.b/2)),t>e.b/2+n.b/2||(r=k.Math.abs(e.d+e.a/2-(n.d+n.a/2)),r>e.a/2+n.a/2)?1:t==0&&r==0?0:t==0?c/r+1:r==0?i/t+1:k.Math.min(i/t,c/r)+1}function S_n(e,n){var t,i,r,c,o,l,f;for(c=0,l=0,f=0,r=new L(e.f.e);r.a0&&e.d!=(yE(),Ste)&&(l+=o*(i.d.a+e.a[n.a][i.a]*(n.d.a-i.d.a)/t)),t>0&&e.d!=(yE(),jte)&&(f+=o*(i.d.b+e.a[n.a][i.a]*(n.d.b-i.d.b)/t)));switch(e.d.g){case 1:return new je(l/c,n.d.b);case 2:return new je(n.d.a,f/c);default:return new je(l/c,f/c)}}function sKe(e){var n,t,i,r,c,o;for(t=(!e.a&&(e.a=new mr(kl,e,5)),e.a).i+2,o=new xo(t),Ce(o,new je(e.j,e.k)),Zi(new wn(null,(!e.a&&(e.a=new mr(kl,e,5)),new pn(e.a,16))),new tSe(o)),Ce(o,new je(e.b,e.c)),n=1;n0&&(kO(f,!1,(vr(),Zc)),kO(f,!0,ru)),Ao(n.g,new eCe(e,t)),ei(e.g,n,t)}function nge(){nge=Y,pln=new fn(s2e,(Pn(),!1)),ve(-1),fln=new fn(l2e,ve(-1)),ve(-1),aln=new fn(f2e,ve(-1)),hln=new fn(a2e,!1),dln=new fn(h2e,!1),g6e=(VR(),Kre),kln=new fn(d2e,g6e),jln=new fn(b2e,-1),b6e=(XB(),Gre),yln=new fn(g2e,b6e),vln=new fn(w2e,!0),h6e=(rB(),Vre),wln=new fn(p2e,h6e),gln=new fn(m2e,!1),ve(1),bln=new fn(v2e,ve(1)),d6e=(JB(),Yre),mln=new fn(y2e,d6e)}function aKe(){aKe=Y;var e;for(Nme=z(B($t,1),ni,30,15,[-1,-1,30,19,15,13,11,11,10,9,9,8,8,8,8,7,7,7,7,7,7,7,6,6,6,6,6,6,6,6,6,6,6,6,6,6,5]),rte=oe($t,ni,30,37,15,1),nnn=z(B($t,1),ni,30,15,[-1,-1,63,40,32,28,25,23,21,20,19,19,18,18,17,17,16,16,16,15,15,15,15,14,14,14,14,14,14,13,13,13,13,13,13,13,13]),Dme=oe(Ep,SYe,30,37,14,1),e=2;e<=36;e++)rte[e]=sc(k.Math.pow(e,Nme[e])),Dme[e]=BO(hN,rte[e])}function x_n(e){var n;if((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i!=1)throw $(new Gn(zWe+(!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i));return n=new xs,KY(u(K((!e.b&&(e.b=new Nn(vt,e,4,7)),e.b),0),84))&&fc(n,VVe(e,KY(u(K((!e.b&&(e.b=new Nn(vt,e,4,7)),e.b),0),84)),!1)),KY(u(K((!e.c&&(e.c=new Nn(vt,e,5,8)),e.c),0),84))&&fc(n,VVe(e,KY(u(K((!e.c&&(e.c=new Nn(vt,e,5,8)),e.c),0),84)),!0)),n}function hKe(e,n){var t,i,r,c,o;for(n.d?r=e.a.c==(ah(),pp)?rr(n.b):Ni(n.b):r=e.a.c==(ah(),Ud)?rr(n.b):Ni(n.b),c=!1,i=new qn(Vn(r.a.Jc(),new ee));ht(i);)if(t=u(tt(i),17),o=Re(e.a.f[e.a.g[n.b.p].p]),!(!o&&!cc(t)&&t.c.i.c==t.d.i.c)&&!(Re(e.a.n[e.a.g[n.b.p].p])||Re(e.a.n[e.a.g[n.b.p].p]))&&(c=!0,rf(e.b,e.a.g[KSn(t,n.b).p])))return n.c=!0,n.a=t,n;return n.c=c,n.a=null,n}function tge(e,n,t){var i,r,c,o,l,f,h;if(i=t.gc(),i==0)return!1;if(e.Nj())if(f=e.Oj(),lde(e,n,t),o=i==1?e.Gj(3,null,t.Jc().Pb(),n,f):e.Gj(5,null,t,n,f),e.Kj()){for(l=i<100?null:new m0(i),c=n+i,r=n;r0){for(o=0;o>16==-15&&e.Cb.Vh()&&jY(new pY(e.Cb,9,13,t,e.c,Ld(Ts(u(e.Cb,62)),e))):X(e.Cb,88)&&e.Db>>16==-23&&e.Cb.Vh()&&(n=e.c,X(n,88)||(n=(yn(),jf)),X(t,88)||(t=(yn(),jf)),jY(new pY(e.Cb,9,10,t,n,Ld(Vu(u(e.Cb,29)),e)))))),e.c}function gKe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A;if(n==t)return!0;if(n=ube(e,n),t=ube(e,t),i=HQ(n),i){if(b=HQ(t),b!=i)return b?(f=i.kk(),A=b.kk(),f==A&&f!=null):!1;if(o=(!n.d&&(n.d=new mr(Rc,n,1)),n.d),c=o.i,y=(!t.d&&(t.d=new mr(Rc,t,1)),t.d),c==y.i){for(h=0;h0,l=UB(n,c),fle(t?l.b:l.g,n),Z3(l).c.length==1&&Xi(i,l,i.c.b,i.c),r=new jc(c,n),T0(e.o,r),qo(e.e.a,c))}function mKe(e,n){var t,i,r,c,o,l,f;return i=k.Math.abs(pR(e.b).a-pR(n.b).a),l=k.Math.abs(pR(e.b).b-pR(n.b).b),r=0,f=0,t=1,o=1,i>e.b.b/2+n.b.b/2&&(r=k.Math.min(k.Math.abs(e.b.c-(n.b.c+n.b.b)),k.Math.abs(e.b.c+e.b.b-n.b.c)),t=1-r/i),l>e.b.a/2+n.b.a/2&&(f=k.Math.min(k.Math.abs(e.b.d-(n.b.d+n.b.a)),k.Math.abs(e.b.d+e.b.a-n.b.d)),o=1-f/l),c=k.Math.min(t,o),(1-c)*k.Math.sqrt(i*i+l*l)}function N_n(e){var n,t,i,r;for(cZ(e,e.e,e.f,(Tw(),lb),!0,e.c,e.i),cZ(e,e.e,e.f,lb,!1,e.c,e.i),cZ(e,e.e,e.f,Jv,!0,e.c,e.i),cZ(e,e.e,e.f,Jv,!1,e.c,e.i),C_n(e,e.c,e.e,e.f,e.i),i=new qr(e.i,0);i.b=65;t--)rh[t]=t-65<<24>>24;for(i=122;i>=97;i--)rh[i]=i-97+26<<24>>24;for(r=57;r>=48;r--)rh[r]=r-48+52<<24>>24;for(rh[43]=62,rh[47]=63,c=0;c<=25;c++)t0[c]=65+c&yr;for(o=26,f=0;o<=51;++o,f++)t0[o]=97+f&yr;for(e=52,l=0;e<=61;++e,l++)t0[e]=48+l&yr;t0[62]=43,t0[63]=47}function vKe(e,n){var t,i,r,c,o,l;return r=Whe(e),l=Whe(n),r==l?e.e==n.e&&e.a<54&&n.a<54?e.fn.f?1:0:(i=e.e-n.e,t=(e.d>0?e.d:k.Math.floor((e.a-1)*xYe)+1)-(n.d>0?n.d:k.Math.floor((n.a-1)*xYe)+1),t>i+1?r:t0&&(o=H3(o,NKe(i))),yJe(c,o))):rh&&(y=0,S+=f+n,f=0),M8(o,y,S),t=k.Math.max(t,y+b.a),f=k.Math.max(f,b.b),y+=b.a+n;return new je(t+n,S+f+n)}function uge(e,n){var t,i,r,c,o,l,f;if(!Ia(e))throw $(new Uc(BWe));if(i=Ia(e),c=i.g,r=i.f,c<=0&&r<=0)return De(),ku;switch(l=e.i,f=e.j,n.g){case 2:case 1:if(l<0)return De(),Kn;if(l+e.g>c)return De(),et;break;case 4:case 3:if(f<0)return De(),Xn;if(f+e.f>r)return De(),bt}return o=(l+e.g/2)/c,t=(f+e.f/2)/r,o+t<=1&&o-t<=0?(De(),Kn):o+t>=1&&o-t>=0?(De(),et):t<.5?(De(),Xn):(De(),bt)}function __n(e,n,t,i,r){var c,o;if(c=mc(Rr(n[0],Ic),Rr(i[0],Ic)),e[0]=Rt(c),c=kw(c,32),t>=r){for(o=1;o0&&(r.b[o++]=0,r.b[o++]=c.b[0]-1),n=1;n0&&(Tb(f,f.d-r.d),r.c==(ha(),sb)&&tX(f,f.a-r.d),f.d<=0&&f.i>0&&Xi(n,f,n.c.b,n.c)));for(c=new L(e.f);c.a0&&(g0(l,l.i-r.d),r.c==(ha(),sb)&&AP(l,l.b-r.d),l.i<=0&&l.d>0&&Xi(t,l,t.c.b,t.c)))}function $_n(e,n,t,i,r){var c,o,l,f,h,b,p,y,S;for(jn(),Tr(e,new XM),o=DT(e),S=new Te,y=new Te,l=null,f=0;o.b!=0;)c=u(o.b==0?null:(at(o.b!=0),$l(o,o.a.a)),167),!l||us(l)*Gs(l)/21&&(f>us(l)*Gs(l)/2||o.b==0)&&(p=new dB(y),b=us(l)/Gs(l),h=uZ(p,n,new t4,t,i,r,b),gi(la(p.e),h),l=p,Hn(S.c,p),f=0,y.c.length=0));return Er(S,y),S}function Wu(e,n,t,i,r){yd();var c,o,l,f,h,b,p;if(Rfe(e,"src"),Rfe(t,"dest"),p=Us(e),f=Us(t),ffe((p.i&4)!=0,"srcType is not an array"),ffe((f.i&4)!=0,"destType is not an array"),b=p.c,o=f.c,ffe((b.i&1)!=0?b==o:(o.i&1)==0,"Array types don't match"),skn(e,n,t,i,r),(b.i&1)==0&&p!=f)if(h=B4(e),c=B4(t),ue(e)===ue(t)&&ni;)tr(c,l,h[--n]);else for(l=i+r;i0),i.a.Xb(i.c=--i.b),p>y+f&&As(i);for(o=new L(S);o.a0),i.a.Xb(i.c=--i.b)}}function B_n(){fi();var e,n,t,i,r,c;if(Xce)return Xce;for(e=new ul(4),V2(e,q0(qne,!0)),dS(e,q0("M",!0)),dS(e,q0("C",!0)),c=new ul(4),i=0;i<11;i++)ho(c,i,i);return n=new ul(4),V2(n,q0("M",!0)),ho(n,4448,4607),ho(n,65438,65439),r=new Kj(2),ug(r,e),ug(r,bA),t=new Kj(2),t.Hm(aR(c,q0("L",!0))),t.Hm(n),t=new A2(3,t),t=new zfe(r,t),Xce=t,Xce}function K2(e,n){var t,i,r,c,o,l,f,h;for(t=new RegExp(n,"g"),f=oe(ze,Se,2,0,6,1),i=0,h=e,c=null;;)if(l=t.exec(h),l==null||h==""){f[i]=h;break}else o=l.index,f[i]=(Yr(0,o,h.length),h.substr(0,o)),h=of(h,o+l[0].length,h.length),t.lastIndex=0,c==h&&(f[i]=(Yr(0,1,h.length),h.substr(0,1)),h=(Yn(1,h.length+1),h.substr(1))),c=h,++i;if(e.length>0){for(r=f.length;r>0&&f[r-1]=="";)--r;rb&&(b=f);for(h=k.Math.pow(4,n),b>h&&(h=b),y=(k.Math.log(h)-k.Math.log(1))/n,c=k.Math.exp(y),r=c,o=0;o0&&(p-=i[0]+e.c,i[0]+=e.c),i[2]>0&&(p-=i[2]+e.c),i[1]=k.Math.max(i[1],p),dR(e.a[1],t.c+n.b+i[0]-(i[1]-p)/2,i[1]);for(c=e.a,l=0,h=c.length;l0?(e.n.c.length-1)*e.i:0,i=new L(e.n);i.a1)for(i=St(r,0);i.b!=i.d.c;)for(t=u(jt(i),235),c=0,f=new L(t.e);f.a0&&(n[0]+=e.c,p-=n[0]),n[2]>0&&(p-=n[2]+e.c),n[1]=k.Math.max(n[1],p),bR(e.a[1],i.d+t.d+n[0]-(n[1]-p)/2,n[1]);else for(A=i.d+t.d,S=i.a-t.d-t.a,o=e.a,f=0,b=o.length;f=n.o&&t.f<=n.f||n.a*.5<=t.f&&n.a*1.5>=t.f){if(o=u(Le(n.n,n.n.c.length-1),208),o.e+o.d+t.g+r<=i&&(c=u(Le(n.n,n.n.c.length-1),208),c.f-e.f+t.f<=e.b||e.a.c.length==1))return ede(n,t),!0;if(n.s+t.g<=i&&n.t+n.d+t.f+r<=e.f+e.b)return Ce(n.b,t),l=u(Le(n.n,n.n.c.length-1),208),Ce(n.n,new PR(n.s,l.f+l.a+n.i,n.i)),Ide(u(Le(n.n,n.n.c.length-1),208),t),jKe(n,t),!0}return!1}function Hz(e,n,t,i){var r,c,o,l,f;if(f=Po(e.e.Ah(),n),r=u(e.g,122),Tc(),u(n,69).vk()){for(o=0;o0||$w(r.b.d,e.b.d+e.b.a)==0&&i.b<0||$w(r.b.d+r.b.a,e.b.d)==0&&i.b>0){l=0;break}}else l=k.Math.min(l,hqe(e,r,i));l=k.Math.min(l,SKe(e,c,l,i))}return l}function sge(e,n){var t,i,r,c,o,l,f;if(e.b<2)throw $(new Gn("The vector chain must contain at least a source and a target point."));for(r=(at(e.b!=0),u(e.a.a.c,8)),vT(n,r.a,r.b),f=new p4((!n.a&&(n.a=new mr(kl,n,5)),n.a)),o=St(e,1);o.a=0&&c!=t))throw $(new Gn($N));for(r=0,f=0;fne(Na(o.g,o.d[0]).a)?(at(f.b>0),f.a.Xb(f.c=--f.b),d2(f,o),r=!0):l.e&&l.e.gc()>0&&(c=(!l.e&&(l.e=new Te),l.e).Kc(n),h=(!l.e&&(l.e=new Te),l.e).Kc(t),(c||h)&&((!l.e&&(l.e=new Te),l.e).Ec(o),++o.c));r||Hn(i.c,o)}function K_n(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;return p=e.a.i+e.a.g/2,y=e.a.i+e.a.g/2,A=n.i+n.g/2,I=n.j+n.f/2,l=new je(A,I),h=u(ye(n,(Xt(),Fy)),8),h.a=h.a+p,h.b=h.b+y,c=(l.b-h.b)/(l.a-h.a),i=l.b-c*l.a,O=t.i+t.g/2,R=t.j+t.f/2,f=new je(O,R),b=u(ye(t,Fy),8),b.a=b.a+p,b.b=b.b+y,o=(f.b-b.b)/(f.a-b.a),r=f.b-o*f.a,S=(i-r)/(o-c),h.a>>0,"0"+n.toString(16)),i="\\x"+of(t,t.length-2,t.length)):e>=Ec?(t=(n=e>>>0,"0"+n.toString(16)),i="\\v"+of(t,t.length-6,t.length)):i=""+String.fromCharCode(e&yr)}return i}function TKe(e,n){var t,i,r,c,o,l,f,h,b;for(c=new L(e.b);c.at){n.Ug();return}switch(u(C(e,(Ne(),Hie)),350).g){case 2:c=new E6;break;case 0:c=new Lp;break;default:c=new oM}if(i=c.mg(e,r),!c.ng())switch(u(C(e,kH),351).g){case 2:i=dqe(r,i);break;case 1:i=iGe(r,i)}VLn(e,r,i),n.Ug()}function oS(e,n){var t,i,r,c,o,l,f,h;n%=24,e.q.getHours()!=n&&(i=new k.Date(e.q.getTime()),i.setDate(i.getDate()+1),l=e.q.getTimezoneOffset()-i.getTimezoneOffset(),l>0&&(f=l/60|0,h=l%60,r=e.q.getDate(),t=e.q.getHours(),t+f>=24&&++r,c=new k.Date(e.q.getFullYear(),e.q.getMonth(),r,n+f,e.q.getMinutes()+h,e.q.getSeconds(),e.q.getMilliseconds()),e.q.setTime(c.getTime()))),o=e.q.getTime(),e.q.setTime(o+36e5),e.q.getHours()!=n&&e.q.setTime(o)}function nLn(e,n){var t,i,r,c;if(L4n(e.d,e.e),e.c.a.$b(),ne(re(C(n.j,(Ne(),lD))))!=0||ne(re(C(n.j,lD)))!=0)for(t=mv,ue(C(n.j,u1))!==ue((F1(),ob))&&he(n.j,(me(),rb),(Pn(),!0)),c=u(C(n.j,kx),15).a,r=0;rr&&++h,Ce(o,(vn(l+h,n.c.length),u(n.c[l+h],15))),f+=(vn(l+h,n.c.length),u(n.c[l+h],15)).a-i,++t;t=I&&e.e[f.p]>A*e.b||V>=t*I)&&(Hn(y.c,l),l=new Te,fc(o,c),c.a.$b(),h-=b,S=k.Math.max(S,h*e.b+O),h+=V,q=V,V=0,b=0,O=0);return new jc(S,y)}function qW(e){var n,t,i,r,c,o,l;if(!e.d){if(l=new hU,n=sA,c=n.a.yc(e,n),c==null){for(i=new ut(tu(e));i.e!=i.i.gc();)t=u(ft(i),29),er(l,qW(t));n.a.Ac(e)!=null,n.a.gc()==0}for(o=l.i,r=(!e.q&&(e.q=new pe(yf,e,11,10)),new ut(e.q));r.e!=r.i.gc();++o)u(ft(r),403);er(l,(!e.q&&(e.q=new pe(yf,e,11,10)),e.q)),_2(l),e.d=new N3((u(K(we((x0(),Rn).o),9),19),l.i),l.g),e.e=u(l.g,678),e.e==null&&(e.e=Qan),Ms(e).b&=-17}return e.d}function T8(e,n,t,i){var r,c,o,l,f,h;if(h=Po(e.e.Ah(),n),f=0,r=u(e.g,122),Tc(),u(n,69).vk()){for(o=0;o1||A==-1)if(p=u(O,72),y=u(b,72),p.dc())y.$b();else for(o=!!Oc(n),c=0,l=e.a?p.Jc():p.Gi();l.Ob();)h=u(l.Pb(),57),r=u(Pa(e,h),57),r?(o?(f=y.bd(r),f==-1?y.Ei(c,r):c!=f&&y.Si(c,r)):y.Ei(c,r),++c):e.b&&!o&&(y.Ei(c,h),++c);else O==null?b.Wb(null):(r=Pa(e,O),r==null?e.b&&!Oc(n)&&b.Wb(O):b.Wb(r))}function uLn(e,n){var t,i,r,c,o,l,f,h;for(t=new b6,r=new qn(Vn(rr(n).a.Jc(),new ee));ht(r);)if(i=u(tt(r),17),!cc(i)&&(l=i.c.i,b0e(l,EJ))){if(h=Pbe(e,l,EJ,jJ),h==-1)continue;t.b=k.Math.max(t.b,h),!t.a&&(t.a=new Te),Ce(t.a,l)}for(o=new qn(Vn(Ni(n).a.Jc(),new ee));ht(o);)if(c=u(tt(o),17),!cc(c)&&(f=c.d.i,b0e(f,jJ))){if(h=Pbe(e,f,jJ,EJ),h==-1)continue;t.d=k.Math.max(t.d,h),!t.c&&(t.c=new Te),Ce(t.c,f)}return t}function oLn(e,n,t,i){var r,c,o,l,f,h,b;if(t.d.i!=n.i){for(r=new Ba(e),Cf(r,(zn(),dr)),he(r,(me(),wi),t),he(r,(Ne(),Wi),(Br(),to)),Hn(i.c,r),o=new Qu,gu(o,r),xr(o,(De(),Kn)),l=new Qu,gu(l,r),xr(l,et),b=t.d,Gr(t,o),c=new Mw,$u(c,t),he(c,Wc,null),lc(c,l),Gr(c,b),h=new qr(t.b,0);h.b1e6)throw $(new FP("power of ten too big"));if(e<=oi)return _4(XO(my[1],n),n);for(i=XO(my[1],oi),r=i,t=Pu(e-oi),n=sc(e%oi);ao(t,oi)>0;)r=H3(r,i),t=lf(t,oi);for(r=H3(r,XO(my[1],n)),r=_4(r,oi),t=Pu(e-oi);ao(t,oi)>0;)r=_4(r,oi),t=lf(t,oi);return r=_4(r,n),r}function DKe(e){var n,t,i,r,c,o,l,f,h,b;for(f=new L(e.a);f.ah&&i>h)b=l,h=ne(n.p[l.p])+ne(n.d[l.p])+l.o.b+l.d.a;else{r=!1,t.$g()&&t.ah("bk node placement breaks on "+l+" which should have been after "+b);break}if(!r)break}return t.$g()&&t.ah(n+" is feasible: "+r),r}function age(e,n,t,i){var r,c,o,l,f,h,b,p,y;if(c=new Ba(e),Cf(c,(zn(),wo)),he(c,(Ne(),Wi),(Br(),to)),r=0,n){for(o=new Qu,he(o,(me(),wi),n),he(c,wi,n.i),xr(o,(De(),Kn)),gu(o,c),y=dh(n.e),h=y,b=0,p=h.length;b0){if(r<0&&b.a&&(r=f,c=h[0],i=0),r>=0){if(l=b.b,f==r&&(l-=i++,l==0))return 0;if(!_Ve(n,h,b,l,o)){f=r-1,h[0]=c;continue}}else if(r=-1,!_Ve(n,h,b,0,o))return 0}else{if(r=-1,ic(b.c,0)==32){if(p=h[0],ARe(n,h),h[0]>p)continue}else if(U5n(n,b.c,h[0])){h[0]+=b.c.length;continue}return 0}return eRn(o,t)?h[0]:0}function hLn(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(b=new wR(new eje(t)),l=oe(ts,pa,30,e.f.e.c.length,16,1),$fe(l,l.length),t[n.a]=0,h=new L(e.f.e);h.a=l.a?c.b>=l.b?(i.a=l.a+(c.a-l.a)/2+r,i.b=l.b+(c.b-l.b)/2-r-e.e.b):(i.a=l.a+(c.a-l.a)/2+r,i.b=c.b+(l.b-c.b)/2+r):c.b>=l.b?(i.a=c.a+(l.a-c.a)/2+r,i.b=l.b+(c.b-l.b)/2+r):(i.a=c.a+(l.a-c.a)/2+r,i.b=c.b+(l.b-c.b)/2-r-e.e.b))}function lS(e){var n,t,i,r,c,o,l,f;if(!e.f){if(f=new nC,l=new nC,n=sA,o=n.a.yc(e,n),o==null){for(c=new ut(tu(e));c.e!=c.i.gc();)r=u(ft(c),29),er(f,lS(r));n.a.Ac(e)!=null,n.a.gc()==0}for(i=(!e.s&&(e.s=new pe(ns,e,21,17)),new ut(e.s));i.e!=i.i.gc();)t=u(ft(i),179),X(t,103)&&Et(l,u(t,19));_2(l),e.r=new uDe(e,(u(K(we((x0(),Rn).o),6),19),l.i),l.g),er(f,e.r),_2(f),e.f=new N3((u(K(we(Rn.o),5),19),f.i),f.g),Ms(e).b&=-3}return e.f}function Gz(){Gz=Y,R8e=z(B(Wl,1),kh,30,15,[48,49,50,51,52,53,54,55,56,57,65,66,67,68,69,70]),Man=new RegExp(`[ +\r\f]+`);try{cA=z(B(QBn,1),On,2076,0,[new UC(($se(),QB("yyyy-MM-dd'T'HH:mm:ss'.'SSSZ",MT((RP(),RP(),US))))),new UC(QB("yyyy-MM-dd'T'HH:mm:ss'.'SSS",MT(US))),new UC(QB("yyyy-MM-dd'T'HH:mm:ss",MT(US))),new UC(QB("yyyy-MM-dd'T'HH:mm",MT(US))),new UC(QB("yyyy-MM-dd",MT(US)))])}catch(e){if(e=sr(e),!X(e,80))throw $(e)}}function dLn(e){var n,t,i,r,c,o,l;for(t=null,l=null,i=u(C(e.b,(Ne(),Die)),348),i==(DE(),pD)&&(t=new Te,l=new Te),o=new L(e.d);o.at);return c}function _Ke(e,n){var t,i,r,c;if(r=Is(e.d,1)!=0,i=xz(e,n),i==0&&Re($e(C(n.j,(me(),rb)))))return 0;!Re($e(C(n.j,(me(),rb))))&&!Re($e(C(n.j,_v)))||ue(C(n.j,(Ne(),u1)))===ue((F1(),ob))?n.c.kg(n.e,r):r=Re($e(C(n.j,rb))),WO(e,n,r,!0),Re($e(C(n.j,_v)))&&he(n.j,_v,(Pn(),!1)),Re($e(C(n.j,rb)))&&(he(n.j,rb,(Pn(),!1)),he(n.j,_v,!0)),t=xz(e,n);do{if(Qhe(e),t==0)return 0;r=!r,c=t,WO(e,n,r,!1),t=xz(e,n)}while(c>t);return c}function gLn(e,n,t){var i,r,c,o,l;if(i=u(C(e,(Ne(),Tie)),22),t.a>n.a&&(i.Gc((rg(),Gx))?e.c.a+=(t.a-n.a)/2:i.Gc(qx)&&(e.c.a+=t.a-n.a)),t.b>n.b&&(i.Gc((rg(),Xx))?e.c.b+=(t.b-n.b)/2:i.Gc(Ux)&&(e.c.b+=t.b-n.b)),u(C(e,(me(),po)),22).Gc((Dc(),Kl))&&(t.a>n.a||t.b>n.b))for(l=new L(e.a);l.an.a&&(i.Gc((rg(),Gx))?e.c.a+=(t.a-n.a)/2:i.Gc(qx)&&(e.c.a+=t.a-n.a)),t.b>n.b&&(i.Gc((rg(),Xx))?e.c.b+=(t.b-n.b)/2:i.Gc(Ux)&&(e.c.b+=t.b-n.b)),u(C(e,(me(),po)),22).Gc((Dc(),Kl))&&(t.a>n.a||t.b>n.b))for(o=new L(e.a);o.a=0&&p<=1&&y>=0&&y<=1?gi(new je(e.a,e.b),A1(new je(n.a,n.b),p)):null}function fS(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(c=0,o=e.t,r=0,i=0,f=0,y=0,p=0,t&&(e.n.c.length=0,Ce(e.n,new PR(e.s,e.t,e.i))),l=0,b=new L(e.b);b.a0?e.i:0)>n&&f>0&&(c=0,o+=f+e.i,r=k.Math.max(r,y),i+=f+e.i,f=0,y=0,t&&(++p,Ce(e.n,new PR(e.s,o,e.i))),l=0),y+=h.g+(l>0?e.i:0),f=k.Math.max(f,h.f),t&&Ide(u(Le(e.n,p),208),h),c+=h.g+(l>0?e.i:0),++l;return r=k.Math.max(r,y),i+=f,t&&(e.r=r,e.d=i,Pde(e.j)),new Lf(e.s,e.t,r,i)}function qz(e){var n,t,i;return t=ue(ye(e,(Ne(),_y)))===ue((KO(),eie))||ue(ye(e,_y))===ue(Vte)||ue(ye(e,_y))===ue(Yte)||ue(ye(e,_y))===ue(Wte)||ue(ye(e,_y))===ue(nie)||ue(ye(e,_y))===ue(nD),i=ue(ye(e,bH))===ue((YO(),qie))||ue(ye(e,bH))===ue(Xie)||ue(ye(e,aD))===ue((G0(),D7))||ue(ye(e,aD))===ue((G0(),Sx)),n=ue(ye(e,u1))!==ue((F1(),ob))||Re($e(ye(e,A7)))||ue(ye(e,dx))!==ue((X4(),ZS))||ne(re(ye(e,lD)))!=0||ne(re(ye(e,Aie)))!=0,t||i||n}function fv(e){var n,t,i,r,c,o,l,f;if(!e.a){if(e.o=null,f=new RSe(e),n=new Mf,t=sA,l=t.a.yc(e,t),l==null){for(o=new ut(tu(e));o.e!=o.i.gc();)c=u(ft(o),29),er(f,fv(c));t.a.Ac(e)!=null,t.a.gc()==0}for(r=(!e.s&&(e.s=new pe(ns,e,21,17)),new ut(e.s));r.e!=r.i.gc();)i=u(ft(r),179),X(i,335)&&Et(n,u(i,38));_2(n),e.k=new cDe(e,(u(K(we((x0(),Rn).o),7),19),n.i),n.g),er(f,e.k),_2(f),e.a=new N3((u(K(we(Rn.o),4),19),f.i),f.g),Ms(e).b&=-2}return e.a}function mLn(e){var n,t,i,r,c,o,l,f,h,b,p,y;if(l=e.d,p=u(C(e,(me(),Dy)),16),n=u(C(e,xy),16),!(!p&&!n)){if(c=ne(re($2(e,(Ne(),Bie)))),o=ne(re($2(e,w4e))),y=0,p){for(h=0,r=p.Jc();r.Ob();)i=u(r.Pb(),9),h=k.Math.max(h,i.o.b),y+=i.o.a;y+=c*(p.gc()-1),l.d+=h+o}if(t=0,n){for(h=0,r=n.Jc();r.Ob();)i=u(r.Pb(),9),h=k.Math.max(h,i.o.b),t+=i.o.a;t+=c*(n.gc()-1),l.a+=h+o}f=k.Math.max(y,t),f>e.o.a&&(b=(f-e.o.a)/2,l.b=k.Math.max(l.b,b),l.c=k.Math.max(l.c,b))}}function bge(e,n,t,i){var r,c,o,l,f,h,b;if(b=Po(e.e.Ah(),n),r=0,c=u(e.g,122),f=null,Tc(),u(n,69).vk()){for(l=0;ll?1:-1:M1e(e.a,n.a,c),r==-1)p=-f,b=o==f?aY(n.a,l,e.a,c):dY(n.a,l,e.a,c);else if(p=o,o==f){if(r==0)return mh(),KS;b=aY(e.a,c,n.a,l)}else b=dY(e.a,c,n.a,l);return h=new zb(p,b.length,b),bE(h),h}function kLn(e,n){var t,i,r,c;if(c=yKe(n),!n.c&&(n.c=new pe($s,n,9,9)),Zi(new wn(null,(!n.c&&(n.c=new pe($s,n,9,9)),new pn(n.c,16))),new rje(c)),r=u(C(c,(me(),po)),22),p$n(n,r),r.Gc((Dc(),Kl)))for(i=new ut((!n.c&&(n.c=new pe($s,n,9,9)),n.c));i.e!=i.i.gc();)t=u(ft(i),125),J$n(e,n,c,t);return u(ye(n,(Ne(),jg)),182).gc()!=0&&oXe(n,c),Re($e(C(c,a4e)))&&r.Ec(ZJ),bi(c,hD)&&Qxe(new tde(ne(re(C(c,hD)))),c),ue(ye(n,wm))===ue((B1(),Yd))?fBn(e,n,c):K$n(e,n,c),c}function bo(e,n){var t,i,r,c,o,l,f;if(e==null)return null;if(c=e.length,c==0)return"";for(f=oe(Wl,kh,30,c,15,1),Yr(0,c,e.length),Yr(0,c,f.length),rIe(e,0,c,f,0),t=null,l=n,r=0,o=0;r0?of(t.a,0,c-1):""):(Yr(0,c-1,e.length),e.substr(0,c-1)):t?t.a:e}function jLn(e,n,t){var i,r,c;if(bi(n,(Ne(),yu))&&(ue(C(n,yu))===ue((Xs(),V1))||ue(C(n,yu))===ue(yg))||bi(t,yu)&&(ue(C(t,yu))===ue((Xs(),V1))||ue(C(t,yu))===ue(yg)))return 0;if(i=_r(n),r=fIn(e,n,t),r!=0)return r;if(bi(n,(me(),Ti))&&bi(t,Ti)){if(c=oo(qw(n,t,i,u(C(i,cb),15).a),qw(t,n,i,u(C(i,cb),15).a)),ue(C(i,bx))===ue((_0(),iD))&&ue(C(n,gx))!==ue(C(t,gx))&&(c=0),c<0)return ZO(e,n,t),c;if(c>0)return ZO(e,t,n),c}return $Tn(e,n,t)}function LKe(e,n){var t,i,r,c,o,l,f,h,b,p,y;for(i=new qn(Vn(H0(n).a.Jc(),new ee));ht(i);)t=u(tt(i),85),X(K((!t.b&&(t.b=new Nn(vt,t,4,7)),t.b),0),193)||(f=iu(u(K((!t.c&&(t.c=new Nn(vt,t,5,8)),t.c),0),84)),ZE(t)||(o=n.i+n.g/2,l=n.j+n.f/2,b=f.i+f.g/2,p=f.j+f.f/2,y=new Kr,y.a=b-o,y.b=p-l,c=new je(y.a,y.b),p8(c,n.g,n.f),y.a-=c.a,y.b-=c.b,o=b-y.a,l=p-y.b,h=new je(y.a,y.b),p8(h,f.g,f.f),y.a-=h.a,y.b-=h.b,b=o+y.a,p=l+y.b,r=Pz(t),V3(r,o),Y3(r,l),X3(r,b),K3(r,p),LKe(e,f)))}function V2(e,n){var t,i,r,c,o;if(o=u(n,137),ov(e),ov(o),o.b!=null){if(e.c=!0,e.b==null){e.b=oe($t,ni,30,o.b.length,15,1),Wu(o.b,0,e.b,0,o.b.length);return}for(c=oe($t,ni,30,e.b.length+o.b.length,15,1),t=0,i=0,r=0;t=e.b.length?(c[r++]=o.b[i++],c[r++]=o.b[i++]):i>=o.b.length?(c[r++]=e.b[t++],c[r++]=e.b[t++]):o.b[i]0?e.i:0)),++n;for(K1e(e.n,f),e.d=t,e.r=i,e.g=0,e.f=0,e.e=0,e.o=Ki,e.p=Ki,c=new L(e.b);c.a0&&(r=(!e.n&&(e.n=new pe(ju,e,1,7)),u(K(e.n,0),157)).a,!r||Kt(Kt((n.a+=' "',n),r),'"'))),t=(!e.b&&(e.b=new Nn(vt,e,4,7)),!(e.b.i<=1&&(!e.c&&(e.c=new Nn(vt,e,5,8)),e.c.i<=1))),t?n.a+=" [":n.a+=" ",Kt(n,nle(new NX,new ut(e.b))),t&&(n.a+="]"),n.a+=eee,t&&(n.a+="["),Kt(n,nle(new NX,new ut(e.c))),t&&(n.a+="]"),n.a)}function SLn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Cn;for(be=e.c,fe=n.c,t=wu(be.a,e,0),i=wu(fe.a,n,0),V=u(Rw(e,(Nc(),ys)).Jc().Pb(),12),tn=u(Rw(e,Do).Jc().Pb(),12),te=u(Rw(n,ys).Jc().Pb(),12),Cn=u(Rw(n,Do).Jc().Pb(),12),R=dh(V.e),Ie=dh(tn.g),q=dh(te.e),cn=dh(Cn.g),z0(e,i,fe),o=q,b=0,A=o.length;b0&&f[i]&&(A=L3(e.b,f[i],r)),O=k.Math.max(O,r.c.c.b+A);for(c=new L(b.e);c.ab?new Gb((ha(),Am),t,n,h-b):h>0&&b>0&&(new Gb((ha(),Am),n,t,0),new Gb(Am,t,n,0))),o)}function CLn(e,n,t){var i,r,c;for(e.a=new Te,c=St(n.b,0);c.b!=c.d.c;){for(r=u(jt(c),40);u(C(r,(Mu(),Nh)),15).a>e.a.c.length-1;)Ce(e.a,new jc(mv,Fpe));i=u(C(r,Nh),15).a,t==(vr(),Zc)||t==ru?(r.e.ane(re(u(Le(e.a,i),49).b))&&FC(u(Le(e.a,i),49),r.e.a+r.f.a)):(r.e.bne(re(u(Le(e.a,i),49).b))&&FC(u(Le(e.a,i),49),r.e.b+r.f.b))}}function RKe(e,n,t,i){var r,c,o,l,f,h,b;if(c=GB(i),l=Re($e(C(i,(Ne(),c4e)))),(l||Re($e(C(e,dH))))&&!D3(u(C(e,Wi),102)))r=q4(c),f=ege(e,t,t==(Nc(),Do)?r:TO(r));else switch(f=new Qu,gu(f,e),n?(b=f.n,b.a=n.a-e.n.a,b.b=n.b-e.n.b,BGe(b,0,0,e.o.a,e.o.b),xr(f,cKe(f,c))):(r=q4(c),xr(f,t==(Nc(),Do)?r:TO(r))),o=u(C(i,(me(),po)),22),h=f.j,c.g){case 2:case 1:(h==(De(),Xn)||h==bt)&&o.Ec((Dc(),Nv));break;case 4:case 3:(h==(De(),et)||h==Kn)&&o.Ec((Dc(),Nv))}return f}function BKe(e,n){var t,i,r,c,o,l;for(o=new D2(new on(e.f.b).a);o.b;){if(c=Q3(o),r=u(c.jd(),591),n==1){if(r.yf()!=(vr(),Vl)&&r.yf()!=Za)continue}else if(r.yf()!=(vr(),Zc)&&r.yf()!=ru)continue;switch(i=u(u(c.kd(),49).b,82),l=u(u(c.kd(),49).a,194),t=l.c,r.yf().g){case 2:i.g.c=e.e.a,i.g.b=k.Math.max(1,i.g.b+t);break;case 1:i.g.c=i.g.c+t,i.g.b=k.Math.max(1,i.g.b-t);break;case 4:i.g.d=e.e.b,i.g.a=k.Math.max(1,i.g.a+t);break;case 3:i.g.d=i.g.d+t,i.g.a=k.Math.max(1,i.g.a-t)}}}function TLn(e,n){var t,i,r,c,o,l,f,h,b,p;for(n.Tg("Simple node placement",1),p=u(C(e,(me(),Lv)),316),l=0,c=new L(e.b);c.a1)throw $(new Gn(JN));f||(c=Xh(n,i.Jc().Pb()),o.Ec(c))}return h1e(e,I0e(e,n,t),o)}function Xz(e,n,t){var i,r,c,o,l,f,h,b;if(J1(e.e,n))f=(Tc(),u(n,69).vk()?new rR(n,e):new pT(n,e)),Mz(f.c,f.b),Vj(f,u(t,18));else{for(b=Po(e.e.Ah(),n),i=u(e.g,122),o=0;o"}f!=null&&(n.a+=""+f)}else e.e?(l=e.e.zb,l!=null&&(n.a+=""+l)):(n.a+="?",e.b?(n.a+=" super ",YW(e.b,n)):e.f&&(n.a+=" extends ",YW(e.f,n)))}function PLn(e){e.b=null,e.a=null,e.o=null,e.q=null,e.v=null,e.w=null,e.B=null,e.p=null,e.Q=null,e.R=null,e.S=null,e.T=null,e.U=null,e.V=null,e.W=null,e.bb=null,e.eb=null,e.ab=null,e.H=null,e.db=null,e.c=null,e.d=null,e.f=null,e.n=null,e.r=null,e.s=null,e.u=null,e.G=null,e.J=null,e.e=null,e.j=null,e.i=null,e.g=null,e.k=null,e.t=null,e.F=null,e.I=null,e.L=null,e.M=null,e.O=null,e.P=null,e.$=null,e.N=null,e.Z=null,e.cb=null,e.K=null,e.D=null,e.A=null,e.C=null,e._=null,e.fb=null,e.X=null,e.Y=null,e.gb=!1,e.hb=!1}function $Ln(e){var n,t,i,r;if(i=lZ((!e.c&&(e.c=qT(Pu(e.f))),e.c),0),e.e==0||e.a==0&&e.f!=-1&&e.e<0)return i;if(n=Whe(e)<0?1:0,t=e.e,r=(i.length+1+k.Math.abs(sc(e.e)),new o4),n==1&&(r.a+="-"),e.e>0)if(t-=i.length-n,t>=0){for(r.a+="0.";t>pg.length;t-=pg.length)ADe(r,pg);WOe(r,pg,sc(t)),Kt(r,(Yn(n,i.length+1),i.substr(n)))}else t=n-t,Kt(r,of(i,n,sc(t))),r.a+=".",Kt(r,qfe(i,sc(t)));else{for(Kt(r,(Yn(n,i.length+1),i.substr(n)));t<-pg.length;t+=pg.length)ADe(r,pg);WOe(r,pg,sc(-t))}return r.a}function QW(e){var n,t,i,r,c,o,l,f,h;return!(e.k!=(zn(),Qi)||e.j.c.length<=1||(c=u(C(e,(Ne(),Wi)),102),c==(Br(),to))||(r=(B2(),(e.q?e.q:(jn(),jn(),i1))._b(gp)?i=u(C(e,gp),203):i=u(C(_r(e),vx),203),i),r==xH)||!(r==Fv||r==zv)&&(o=ne(re($2(e,yx))),n=u(C(e,bD),140),!n&&(n=new $le(o,o,o,o)),h=mu(e,(De(),Kn)),f=n.d+n.a+(h.gc()-1)*o,f>e.o.b||(t=mu(e,et),l=n.d+n.a+(t.gc()-1)*o,l>e.o.b)))}function RLn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;n.Tg("Orthogonal edge routing",1),h=ne(re(C(e,(Ne(),Sm)))),t=ne(re(C(e,jm))),i=ne(re(C(e,ub))),y=new jV(0,t),I=0,o=new qr(e.b,0),l=null,b=null,f=null,p=null;do b=o.b0?(S=(A-1)*t,l&&(S+=i),b&&(S+=i),S0;for(l=u(C(e.c.i,mm),15).a,c=u(gs(si(n.Mc(),new xje(l)),Cs(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),o=new Si,b=new ar,Vt(o,e.c.i),hr(b,e.c.i);o.b!=0;){if(t=u(o.b==0?null:(at(o.b!=0),$l(o,o.a.a)),9),c.Gc(t))return!0;for(r=new qn(Vn(Ni(t).a.Jc(),new ee));ht(r);)i=u(tt(r),17),f=i.d.i,b.a._b(f)||(b.a.yc(f,b),Xi(o,f,o.c.b,o.c))}return!1}function GKe(e,n,t){var i,r,c,o,l,f,h,b,p;for(p=new Te,b=new Cae(0,t),c=0,vB(b,new tQ(0,0,b,t)),r=0,h=new ut(e);h.e!=h.i.gc();)f=u(ft(h),26),i=u(Le(b.a,b.a.c.length-1),173),l=r+f.g+(u(Le(b.a,0),173).b.c.length==0?0:t),(l>n||Re($e(ye(f,(Ja(),AD)))))&&(r=0,c+=b.b+t,Hn(p.c,b),b=new Cae(c,t),i=new tQ(0,b.f,b,t),vB(b,i),r=0),i.b.c.length==0||!Re($e(ye(Bi(f),(Ja(),Ure))))&&(f.f>=i.o&&f.f<=i.f||i.a*.5<=f.f&&i.a*1.5>=f.f)?ede(i,f):(o=new tQ(i.s+i.r+t,b.f,b,t),vB(b,o),ede(o,f)),r=f.i+f.g;return Hn(p.c,b),p}function aS(e){var n,t,i,r;if(!(e.b==null||e.b.length<=2)&&!e.a){for(n=0,r=0;r=e.b[r+1])r+=2;else if(t0)for(i=new bs(u(pi(e.a,c),22)),jn(),Tr(i,new noe(n)),r=new qr(c.b,0);r.b0&&i>=-6?i>=0?yT(c,t-sc(e.e),"."):(qY(c,n-1,n-1,"0."),yT(c,n+1,gh(pg,0,-sc(i)-1))):(t-n>=1&&(yT(c,n,"."),++t),yT(c,t,"E"),i>0&&yT(c,++t,"+"),yT(c,++t,""+rE(Pu(i)))),e.g=c.a,e.g))}function KLn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie;i=ne(re(C(n,(Ne(),s4e)))),be=u(C(n,kx),15).a,y=4,r=3,fe=20/be,S=!1,f=0,o=oi;do{for(c=f!=1,p=f!=0,Ie=0,I=e.a,q=0,te=I.length;qbe)?(f=2,o=oi):f==0?(f=1,o=Ie):(f=0,o=Ie)):(S=Ie>=o||o-Ie=Ec?Bc(t,V1e(i)):D9(t,i&yr),o=new FV(10,null,0),Tvn(e.a,o,l-1)):(t=(o.Km().length+c,new jj),Bc(t,o.Km())),n.e==0?(i=n.Im(),i>=Ec?Bc(t,V1e(i)):D9(t,i&yr)):Bc(t,n.Km()),u(o,517).b=t.a}}function VLn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I;if(!t.dc()){for(l=0,y=0,i=t.Jc(),A=u(i.Pb(),15).a;l0?1:Lb(isNaN(i),isNaN(0)))>=0^(Bf(xh),(k.Math.abs(l)<=xh||l==0||isNaN(l)&&isNaN(0)?0:l<0?-1:l>0?1:Lb(isNaN(l),isNaN(0)))>=0)?k.Math.max(l,i):(Bf(xh),(k.Math.abs(i)<=xh||i==0||isNaN(i)&&isNaN(0)?0:i<0?-1:i>0?1:Lb(isNaN(i),isNaN(0)))>0?k.Math.sqrt(l*l+i*i):-k.Math.sqrt(l*l+i*i))}function ZLn(e){var n,t,i,r;r=e.o,h2(),e.A.dc()||di(e.A,Qme)?n=r.b:(e.D?n=k.Math.max(r.b,YE(e.f)):n=YE(e.f),e.A.Gc((Vs(),GD))&&!e.B.Gc((_s(),iA))&&(n=k.Math.max(n,YE(u(zc(e.p,(De(),et)),253))),n=k.Math.max(n,YE(u(zc(e.p,Kn),253)))),t=nze(e),t&&(n=k.Math.max(n,t.b)),e.A.Gc(qD)&&(e.q==(Br(),f1)||e.q==to)&&(n=k.Math.max(n,tR(u(zc(e.b,(De(),et)),127))),n=k.Math.max(n,tR(u(zc(e.b,Kn),127))))),Re($e(e.e.Rf().mf((Xt(),Om))))?r.b=k.Math.max(r.b,n):r.b=n,i=e.f.i,i.d=0,i.a=n,GW(e.f)}function ePn(e,n,t,i,r,c,o,l){var f,h,b,p;switch(f=$f(z(B(qBn,1),On,238,0,[n,t,i,r])),p=null,e.b.g){case 1:p=$f(z(B(M6e,1),On,523,0,[new Lk,new IM,new _6]));break;case 0:p=$f(z(B(M6e,1),On,523,0,[new _6,new IM,new Lk]));break;case 2:p=$f(z(B(M6e,1),On,523,0,[new IM,new Lk,new _6]))}for(b=new L(p);b.a1&&(f=h.Gg(f,e.a,l));return f.c.length==1?u(Le(f,f.c.length-1),238):f.c.length==2?zLn((vn(0,f.c.length),u(f.c[0],238)),(vn(1,f.c.length),u(f.c[1],238)),o,c):null}function nPn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A;r=new i9(e),c=new Yqe,i=(VT(c.n),VT(c.p),Hu(c.c),VT(c.f),VT(c.o),Hu(c.q),Hu(c.d),Hu(c.g),Hu(c.k),Hu(c.e),Hu(c.i),Hu(c.j),Hu(c.r),Hu(c.b),y=yqe(c,r,null),kUe(c,r),y),n&&(f=new i9(n),o=vLn(f),M0e(i,z(B(u9e,1),On,524,0,[o]))),p=!1,b=!1,t&&(f=new i9(t),GF in f.a&&(p=O1(f,GF).oe().a),aZe in f.a&&(b=O1(f,aZe).oe().a)),h=wAe(aBe(new i4,p),b),sCn(new RM,i,h),GF in r.a&&Rf(r,GF,null),(p||b)&&(l=new r4,bKe(h,l,p,b),Rf(r,GF,l)),S=new ySe(c),zze(new AK(i),S),A=new kSe(c),zze(new AK(i),A)}function tPn(e,n,t){var i,r,c,o,l,f,h;for(t.Tg("Find roots",1),e.a.c.length=0,r=St(n.b,0);r.b!=r.d.c;)i=u(jt(r),40),i.b.b==0&&(he(i,(Ci(),fb),(Pn(),!0)),Ce(e.a,i));switch(e.a.c.length){case 0:c=new nQ(0,n,"DUMMY_ROOT"),he(c,(Ci(),fb),(Pn(),!0)),he(c,bre,!0),Vt(n.b,c);break;case 1:break;default:for(o=new nQ(0,n,DF),f=new L(e.a);f.a=k.Math.abs(i.b)?(i.b=0,c.d+c.a>o.d&&c.do.c&&c.c0){if(n=new Nse(e.i,e.g),t=e.i,c=t<100?null:new m0(t),e.Rj())for(i=0;i0){for(l=e.g,h=e.i,vE(e),c=h<100?null:new m0(h),i=0;i>13|(e.m&15)<<9,r=e.m>>4&8191,c=e.m>>17|(e.h&255)<<5,o=(e.h&1048320)>>8,l=n.l&8191,f=n.l>>13|(n.m&15)<<9,h=n.m>>4&8191,b=n.m>>17|(n.h&255)<<5,p=(n.h&1048320)>>8,cn=t*l,tn=i*l,Cn=r*l,Dn=c*l,ot=o*l,f!=0&&(tn+=t*f,Cn+=i*f,Dn+=r*f,ot+=c*f),h!=0&&(Cn+=t*h,Dn+=i*h,ot+=r*h),b!=0&&(Dn+=t*b,ot+=i*b),p!=0&&(ot+=t*p),S=cn&Ls,A=(tn&511)<<13,y=S+A,I=cn>>22,R=tn>>9,q=(Cn&262143)<<4,V=(Dn&31)<<17,O=I+R+q+V,be=Cn>>18,fe=Dn>>5,Ie=(ot&4095)<<8,te=be+fe+Ie,O+=y>>22,y&=Ls,te+=O>>22,O&=Ls,te&=G1,_o(y,O,te)}function KKe(e){var n,t,i,r,c,o,l;if(l=u(Le(e.j,0),12),l.g.c.length!=0&&l.e.c.length!=0)throw $(new Uc("Interactive layout does not support NORTH/SOUTH ports with incoming _and_ outgoing edges."));if(l.g.c.length!=0){for(c=Ki,t=new L(l.g);t.a0&&UGe(e,l,p);for(r=new L(p);r.a4)if(e.dk(n)){if(e.$k()){if(r=u(n,52),i=r.Bh(),f=i==e.e&&(e.kl()?r.vh(r.Ch(),e.gl())==e.hl():-1-r.Ch()==e.Jj()),e.ll()&&!f&&!i&&r.Gh()){for(c=0;ce.d[o.p]&&(t+=Hae(e.b,c)*u(f.b,15).a,T0(e.a,ve(c)));for(;!kj(e.a);)Ehe(e.b,u(A4(e.a),15).a)}return t}function oPn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;for(n.Tg(FQe,1),S=new Te,b=k.Math.max(e.a.c.length,u(C(e,(me(),cb)),15).a),t=b*u(C(e,cD),15).a,l=ue(C(e,(Ne(),Iy)))===ue((_0(),dm)),O=new L(e.a);O.a0&&(h=e.n.a/c);break;case 2:case 4:r=e.i.o.b,r>0&&(h=e.n.b/r)}he(e,(me(),hp),h)}if(f=e.o,o=e.a,i)o.a=i.a,o.b=i.b,e.d=!0;else if(n!=nh&&n!=bb&&l!=ku)switch(l.g){case 1:o.a=f.a/2;break;case 2:o.a=f.a,o.b=f.b/2;break;case 3:o.a=f.a/2,o.b=f.b;break;case 4:o.b=f.b/2}else o.a=f.a/2,o.b=f.b/2}function hS(e){var n,t,i,r,c,o,l,f,h,b;if(e.Nj())if(b=e.Cj(),f=e.Oj(),b>0)if(n=new t1e(e.nj()),t=b,c=t<100?null:new m0(t),xT(e,t,n.g),r=t==1?e.Gj(4,K(n,0),null,0,f):e.Gj(6,n,null,-1,f),e.Kj()){for(i=new ut(n);i.e!=i.i.gc();)c=e.Mj(ft(i),c);c?(c.lj(r),c.mj()):e.Hj(r)}else c?(c.lj(r),c.mj()):e.Hj(r);else xT(e,e.Cj(),e.Dj()),e.Hj(e.Gj(6,(jn(),Sc),null,-1,f));else if(e.Kj())if(b=e.Cj(),b>0){for(l=e.Dj(),h=b,xT(e,b,l),c=h<100?null:new m0(h),i=0;i1&&us(o)*Gs(o)/2>l[0]){for(c=0;cl[c];)++c;A=new C0(O,0,c+1),p=new dB(A),b=us(o)/Gs(o),f=uZ(p,n,new t4,t,i,r,b),gi(la(p.e),f),E4(v8(y,p),B8),S=new C0(O,c+1,O.c.length),zde(y,S),O.c.length=0,h=0,ODe(l,l.length,0)}else I=y.b.c.length==0?null:Le(y.b,0),I!=null&&PY(y,0),h>0&&(l[h]=l[h-1]),l[h]+=us(o)*Gs(o),++h,Hn(O.c,o);return O}function wPn(e,n){var t,i,r,c;t=n.b,c=new bs(t.j),r=0,i=t.j,i.c.length=0,jw(u(Yb(e.b,(De(),Xn),(_w(),lp)),16),t),r=_O(c,r,new k6,i),jw(u(Yb(e.b,Xn,ib),16),t),r=_O(c,r,new ld,i),jw(u(Yb(e.b,Xn,sp),16),t),jw(u(Yb(e.b,et,lp),16),t),jw(u(Yb(e.b,et,ib),16),t),r=_O(c,r,new fd,i),jw(u(Yb(e.b,et,sp),16),t),jw(u(Yb(e.b,bt,lp),16),t),r=_O(c,r,new Np,i),jw(u(Yb(e.b,bt,ib),16),t),r=_O(c,r,new Sb,i),jw(u(Yb(e.b,bt,sp),16),t),jw(u(Yb(e.b,Kn,lp),16),t),r=_O(c,r,new sd,i),jw(u(Yb(e.b,Kn,ib),16),t),jw(u(Yb(e.b,Kn,sp),16),t)}function pPn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O;for(n.Tg("Layer size calculation",1),b=Ki,h=Dr,r=!1,l=new L(e.b);l.a.5?R-=o*2*(A-.5):A<.5&&(R+=c*2*(.5-A)),r=l.d.b,RI.a-O-b&&(R=I.a-O-b),l.n.a=n+R}}function vPn(e){var n,t,i,r,c;if(i=u(C(e,(Ne(),yu)),165),i==(Xs(),V1)){for(t=new qn(Vn(rr(e).a.Jc(),new ee));ht(t);)if(n=u(tt(t),17),!qPe(n))throw $(new wd(iee+LO(e)+"' has its layer constraint set to FIRST_SEPARATE, but has at least one incoming edge. FIRST_SEPARATE nodes must not have incoming edges."))}else if(i==yg){for(c=new qn(Vn(Ni(e).a.Jc(),new ee));ht(c);)if(r=u(tt(c),17),!qPe(r))throw $(new wd(iee+LO(e)+"' has its layer constraint set to LAST_SEPARATE, but has at least one outgoing edge. LAST_SEPARATE nodes must not have outgoing edges."))}}function rN(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;if(e.e&&e.c.c>19!=0&&(n=e8(n),f=!f),o=rNn(n),c=!1,r=!1,i=!1,e.h==gN&&e.m==0&&e.l==0)if(r=!0,c=!0,o==-1)e=gTe((G9(),jme)),i=!0,f=!f;else return l=lbe(e,o),f&&ZY(l),t&&(Z0=_o(0,0,0)),l;else e.h>>19!=0&&(c=!0,e=e8(e),i=!0,f=!f);return o!=-1?bkn(e,o,f,c,t):Kde(e,n)<0?(t&&(c?Z0=e8(e):Z0=_o(e.l,e.m,e.h)),_o(0,0,0)):WIn(i?e:_o(e.l,e.m,e.h),n,f,c,r,t)}function nZ(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;if(o=e.e,f=n.e,o==0)return n;if(f==0)return e;if(c=e.d,l=n.d,c+l==2)return t=Rr(e.a[0],Ic),i=Rr(n.a[0],Ic),o==f?(b=mc(t,i),A=Rt(b),S=Rt(Bb(b,32)),S==0?new D1(o,A):new zb(o,2,z(B($t,1),ni,30,15,[A,S]))):(mh(),T$(o<0?lf(i,t):lf(t,i),0)?B0(o<0?lf(i,t):lf(t,i)):sE(B0(Cd(o<0?lf(i,t):lf(t,i)))));if(o==f)y=o,p=c>=l?dY(e.a,c,n.a,l):dY(n.a,l,e.a,c);else{if(r=c!=l?c>l?1:-1:M1e(e.a,n.a,c),r==0)return mh(),KS;r==1?(y=o,p=aY(e.a,c,n.a,l)):(y=f,p=aY(n.a,l,e.a,c))}return h=new zb(y,p.length,p),bE(h),h}function kPn(e,n){var t,i,r,c,o,l,f;if(!(e.g>n.f||n.g>e.f)){for(t=0,i=0,o=e.w.a.ec().Jc();o.Ob();)r=u(o.Pb(),12),dQ(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])).b,n.g,n.f)&&++t;for(l=e.r.a.ec().Jc();l.Ob();)r=u(l.Pb(),12),dQ(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])).b,n.g,n.f)&&--t;for(f=n.w.a.ec().Jc();f.Ob();)r=u(f.Pb(),12),dQ(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])).b,e.g,e.f)&&++i;for(c=n.r.a.ec().Jc();c.Ob();)r=u(c.Pb(),12),dQ(pu(z(B(Lr,1),Se,8,0,[r.i.n,r.n,r.a])).b,e.g,e.f)&&--i;t=0)return t;switch(xw(Vc(e,t))){case 2:{if(bn("",Dd(e,t.ok()).ve())){if(f=BT(Vc(e,t)),l=L9(Vc(e,t)),b=gbe(e,n,f,l),b)return b;for(r=qbe(e,n),o=0,p=r.gc();o1)throw $(new Gn(JN));for(b=Po(e.e.Ah(),n),i=u(e.g,122),o=0;o1,h=new La(y.b);bu(h.a)||bu(h.b);)f=u(bu(h.a)?_(h.a):_(h.b),17),p=f.c==y?f.d:f.c,k.Math.abs(pu(z(B(Lr,1),Se,8,0,[p.i.n,p.n,p.a])).b-o.b)>1&&uDn(e,f,o,c,y)}}function APn(e){var n,t,i,r,c,o;if(r=new qr(e.e,0),i=new qr(e.a,0),e.d)for(t=0;tXee;){for(c=n,o=0;k.Math.abs(n-c)0),r.a.Xb(r.c=--r.b),R_n(e,e.b-o,c,i,r),at(r.b0),i.a.Xb(i.c=--i.b)}if(!e.d)for(t=0;t0?(e.f[b.p]=S/(b.e.c.length+b.g.c.length),e.c=k.Math.min(e.c,e.f[b.p]),e.b=k.Math.max(e.b,e.f[b.p])):l&&(e.f[b.p]=S)}}function CPn(e){e.b=null,e.bb=null,e.fb=null,e.qb=null,e.a=null,e.c=null,e.d=null,e.e=null,e.f=null,e.n=null,e.M=null,e.L=null,e.Q=null,e.R=null,e.K=null,e.db=null,e.eb=null,e.g=null,e.i=null,e.j=null,e.k=null,e.gb=null,e.o=null,e.p=null,e.q=null,e.r=null,e.$=null,e.ib=null,e.S=null,e.T=null,e.t=null,e.s=null,e.u=null,e.v=null,e.w=null,e.B=null,e.A=null,e.C=null,e.D=null,e.F=null,e.G=null,e.H=null,e.I=null,e.J=null,e.P=null,e.Z=null,e.U=null,e.V=null,e.W=null,e.X=null,e.Y=null,e._=null,e.ab=null,e.cb=null,e.hb=null,e.nb=null,e.lb=null,e.mb=null,e.ob=null,e.pb=null,e.jb=null,e.kb=null,e.N=!1,e.O=!1}function TPn(e,n,t){var i,r,c,o;for(t.Tg("Graph transformation ("+e.a+")",1),o=qb(n.a),c=new L(n.b);c.a=l.b.c)&&(l.b=n),(!l.c||n.c<=l.c.c)&&(l.d=l.c,l.c=n),(!l.e||n.d>=l.e.d)&&(l.e=n),(!l.f||n.d<=l.f.d)&&(l.f=n);return i=new cz((Z9(),op)),XT(e,Wtn,new Su(z(B(VN,1),On,377,0,[i]))),o=new cz(sm),XT(e,Qtn,new Su(z(B(VN,1),On,377,0,[o]))),r=new cz(om),XT(e,Ytn,new Su(z(B(VN,1),On,377,0,[r]))),c=new cz(xv),XT(e,Vtn,new Su(z(B(VN,1),On,377,0,[c]))),MW(i.c,op),MW(r.c,om),MW(c.c,xv),MW(o.c,sm),l.a.c.length=0,Er(l.a,i.c),Er(l.a,Ks(r.c)),Er(l.a,c.c),Er(l.a,Ks(o.c)),l}function DPn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;for(n.Tg(lWe,1),S=ne(re(ye(e,(Yh(),Mm)))),o=ne(re(ye(e,(Ja(),zx)))),l=u(ye(e,Bx),104),Yhe((!e.a&&(e.a=new pe(Jt,e,10,11)),e.a)),b=GKe((!e.a&&(e.a=new pe(Jt,e,10,11)),e.a),S,o),!e.a&&(e.a=new pe(Jt,e,10,11)),h=new L(b);h.a0&&(e.a=f+(S-1)*c,n.c.b+=e.a,n.f.b+=e.a)),A.a.gc()!=0&&(y=new jV(1,c),S=jge(y,n,A,O,n.f.b+f-n.c.b),S>0&&(n.f.b+=f+(S-1)*c))}function QKe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te;for(b=ne(re(C(e,(Ne(),Sg)))),i=ne(re(C(e,m4e))),y=new R6,he(y,Sg,b+i),h=n,R=h.d,O=h.c.i,q=h.d.i,I=zse(O.c),V=zse(q.c),r=new Te,p=I;p<=V;p++)l=new Ba(e),Cf(l,(zn(),dr)),he(l,(me(),wi),h),he(l,Wi,(Br(),to)),he(l,yH,y),S=u(Le(e.b,p),25),p==I?z0(l,S.a.c.length-t,S):Or(l,S),te=ne(re(C(h,Gd))),te<0&&(te=0,he(h,Gd,te)),l.o.b=te,A=k.Math.floor(te/2),o=new Qu,xr(o,(De(),Kn)),gu(o,l),o.n.b=A,f=new Qu,xr(f,et),gu(f,l),f.n.b=A,Gr(h,o),c=new Mw,$u(c,h),he(c,Wc,null),lc(c,f),Gr(c,R),zxn(l,h,c),Hn(r.c,c),h=c;return r}function _Pn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;if(O=n.b.c.length,!(O<3)){for(S=oe($t,ni,30,O,15,1),p=0,b=new L(n.b);b.ao)&&hr(e.b,u(I.b,17));++l}c=o}}}function tZ(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;for(f=u(Pd(e,(De(),Kn)).Jc().Pb(),12).e,S=u(Pd(e,et).Jc().Pb(),12).g,l=f.c.length,V=_a(u(Le(e.j,0),12));l-- >0;){for(O=(vn(0,f.c.length),u(f.c[0],17)),r=(vn(0,S.c.length),u(S.c[0],17)),q=r.d.e,c=wu(q,r,0),qyn(O,r.d,c),lc(r,null),Gr(r,null),A=O.a,n&&Vt(A,new wc(V)),i=St(r.a,0);i.b!=i.d.c;)t=u(jt(i),8),Vt(A,new wc(t));for(R=O.b,y=new L(r.b);y.a-2;default:return!1}switch(n=e.Pj(),e.p){case 0:return n!=null&&Re($e(n))!=Gj(e.k,0);case 1:return n!=null&&u(n,221).a!=Rt(e.k)<<24>>24;case 2:return n!=null&&u(n,180).a!=(Rt(e.k)&yr);case 6:return n!=null&&Gj(u(n,190).a,e.k);case 5:return n!=null&&u(n,15).a!=Rt(e.k);case 7:return n!=null&&u(n,191).a!=Rt(e.k)<<16>>16;case 3:return n!=null&&ne(re(n))!=e.j;case 4:return n!=null&&u(n,164).a!=e.j;default:return n==null?e.n!=null:!di(n,e.n)}}function cN(e,n,t){var i,r,c,o;return e.ml()&&e.ll()&&(o=gV(e,u(t,57)),ue(o)!==ue(t))?(e.vj(n),e.Bj(n,B$e(e,n,o)),e.$k()&&(c=(r=u(t,52),e.kl()?e.il()?r.Qh(e.b,Oc(u(An(Go(e.b),e.Jj()),19)).n,u(An(Go(e.b),e.Jj()).Fk(),29).ik(),null):r.Qh(e.b,zi(r.Ah(),Oc(u(An(Go(e.b),e.Jj()),19))),null,null):r.Qh(e.b,-1-e.Jj(),null,null)),!u(o,52).Mh()&&(c=(i=u(o,52),e.kl()?e.il()?i.Oh(e.b,Oc(u(An(Go(e.b),e.Jj()),19)).n,u(An(Go(e.b),e.Jj()).Fk(),29).ik(),c):i.Oh(e.b,zi(i.Ah(),Oc(u(An(Go(e.b),e.Jj()),19))),null,c):i.Oh(e.b,-1-e.Jj(),null,c))),c&&c.mj()),Fs(e.b)&&e.Hj(e.Gj(9,t,o,n,!1)),o):t}function WKe(e){var n,t,i,r,c,o,l,f,h,b;for(i=new Te,o=new L(e.e.a);o.a0&&(o=k.Math.max(o,qBe(e.C.b+i.d.b,r))),b=i,p=r,y=c;e.C&&e.C.c>0&&(S=y+e.C.c,h&&(S+=b.d.c),o=k.Math.max(o,(Oa(),Bf(Ga),k.Math.abs(p-1)<=Ga||p==1||isNaN(p)&&isNaN(1)?0:S/(1-p)))),t.n.b=0,t.a.a=o}function eVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S;if(t=u(zc(e.b,n),127),f=u(u(pi(e.r,n),22),83),f.dc()){t.n.d=0,t.n.a=0;return}for(h=e.u.Gc((ps(),Z1)),o=0,e.A.Gc((Vs(),Og))&&OXe(e,n),l=f.Jc(),b=null,y=0,p=0;l.Ob();)i=u(l.Pb(),115),c=ne(re(i.b.mf((F$(),wJ)))),r=i.b.Kf().b,b?(S=p+b.d.a+e.w+i.d.d,o=k.Math.max(o,(Oa(),Bf(Ga),k.Math.abs(y-c)<=Ga||y==c||isNaN(y)&&isNaN(c)?0:S/(c-y)))):e.C&&e.C.d>0&&(o=k.Math.max(o,qBe(e.C.d+i.d.d,c))),b=i,y=c,p=r;e.C&&e.C.a>0&&(S=p+e.C.a,h&&(S+=b.d.a),o=k.Math.max(o,(Oa(),Bf(Ga),k.Math.abs(y-1)<=Ga||y==1||isNaN(y)&&isNaN(1)?0:S/(1-y)))),t.n.d=0,t.a.b=o}function nVe(e,n,t){var i,r,c,o,l,f;for(this.g=e,l=n.d.length,f=t.d.length,this.d=oe(c1,Bd,9,l+f,0,1),o=0;o0?OY(this,this.f/this.a):Na(n.g,n.d[0]).a!=null&&Na(t.g,t.d[0]).a!=null?OY(this,(ne(Na(n.g,n.d[0]).a)+ne(Na(t.g,t.d[0]).a))/2):Na(n.g,n.d[0]).a!=null?OY(this,Na(n.g,n.d[0]).a):Na(t.g,t.d[0]).a!=null&&OY(this,Na(t.g,t.d[0]).a)}function PPn(e,n,t,i,r,c,o,l){var f,h,b,p,y,S,A,O,I,R;if(A=!1,h=Ebe(t.q,n.f+n.b-t.q.f),S=i.f>n.b&&l,R=r-(t.q.e+h-o),p=(f=fS(i,R,!1),f.a),S&&p>i.f)return!1;if(S){for(y=0,I=new L(n.d);I.a=(vn(c,e.c.length),u(e.c[c],186)).e,!S&&p>n.b&&!b)?!1:((b||S||p<=n.b)&&(b&&p>n.b?(t.d=p,eO(t,PGe(t,p))):(QHe(t.q,h),t.c=!0),eO(i,r-(t.s+t.r)),IO(i,t.q.e+t.q.d,n.f),vB(n,i),e.c.length>c&&($O((vn(c,e.c.length),u(e.c[c],186)),i),(vn(c,e.c.length),u(e.c[c],186)).a.c.length==0&&Ad(e,c)),A=!0),A)}function $Pn(e,n){var t,i,r,c,o,l,f,h,b,p;for(e.a=new vIe(lkn(Vx)),i=new L(n.a);i.a0&&(Yn(0,t.length),t.charCodeAt(0)!=47)))throw $(new Gn("invalid opaquePart: "+t));if(e&&!(n!=null&&Sj(jG,n.toLowerCase()))&&!(t==null||!kQ(t,uA,oA)))throw $(new Gn(FZe+t));if(e&&n!=null&&Sj(jG,n.toLowerCase())&&!PAn(t))throw $(new Gn(FZe+t));if(!Jjn(i))throw $(new Gn("invalid device: "+i));if(!zkn(r))throw o=r==null?"invalid segments: null":"invalid segment: "+_kn(r),$(new Gn(o));if(!(c==null||lh(c,Xo(35))==-1))throw $(new Gn("invalid query: "+c))}function iVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;if(y=new wc(e.o),R=n.a/y.a,l=n.b/y.b,O=n.a-y.a,c=n.b-y.b,t)for(r=ue(C(e,(Ne(),Wi)))===ue((Br(),to)),A=new L(e.j);A.a=1&&(I-o>0&&p>=0?(f.n.a+=O,f.n.b+=c*o):I-o<0&&b>=0&&(f.n.a+=O*I,f.n.b+=c));e.o.a=n.a,e.o.b=n.b,he(e,(Ne(),jg),(Vs(),i=u(sa(tA),10),new _l(i,u(_f(i,i.length),10),0)))}function FPn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;if(t.Tg("Network simplex layering",1),e.b=n,R=u(C(n,(Ne(),kx)),15).a*4,I=e.b.a,I.c.length<1){t.Ug();return}for(c=_In(e,I),O=null,r=St(c,0);r.b!=r.d.c;){for(i=u(jt(r),16),l=R*sc(k.Math.sqrt(i.gc())),o=KIn(i),BW(_oe(Kbn(Loe(VK(o),l),O),!0),t.dh(1)),y=e.b.b,A=new L(o.a);A.a1)for(O=oe($t,ni,30,e.b.b.c.length,15,1),p=0,h=new L(e.b.b);h.a0){tz(e,t,0),t.a+=String.fromCharCode(i),r=AEn(n,c),tz(e,t,r),c+=r-1;continue}i==39?c+10&&A.a<=0){f.c.length=0,Hn(f.c,A);break}S=A.i-A.d,S>=l&&(S>l&&(f.c.length=0,l=S),Hn(f.c,A))}f.c.length!=0&&(o=u(Le(f,sz(r,f.c.length)),116),V.a.Ac(o)!=null,o.g=b++,oge(o,n,t,i),f.c.length=0)}for(I=e.c.length+1,y=new L(e);y.aDr||n.o==Ag&&b=l&&r<=f)l<=r&&c<=f?(t[b++]=r,t[b++]=c,i+=2):l<=r?(t[b++]=r,t[b++]=f,e.b[i]=f+1,o+=2):c<=f?(t[b++]=l,t[b++]=c,i+=2):(t[b++]=l,t[b++]=f,e.b[i]=f+1);else if(fX0)&&l<10);Poe(e.c,new b5),rVe(e),Pvn(e.c),OPn(e.f)}function ZPn(e,n){var t,i,r,c,o,l,f,h,b,p,y;switch(e.k.g){case 1:if(i=u(C(e,(me(),wi)),17),t=u(C(i,Kve),78),t?Re($e(C(i,Hd)))&&(t=k1e(t)):t=new xs,h=u(C(e,ja),12),h){if(b=pu(z(B(Lr,1),Se,8,0,[h.i.n,h.n,h.a])),n<=b.a)return b.b;Xi(t,b,t.a,t.a.a)}if(p=u(C(e,gf),12),p){if(y=pu(z(B(Lr,1),Se,8,0,[p.i.n,p.n,p.a])),y.a<=n)return y.b;Xi(t,y,t.c.b,t.c)}if(t.b>=2){for(f=St(t,0),o=u(jt(f),8),l=u(jt(f),8);l.a0&&kO(h,!0,(vr(),ru)),l.k==(zn(),wr)&&_Ie(h),ei(e.f,l,n)}}function uVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q;for(h=Ki,b=Ki,l=Dr,f=Dr,y=new L(n.i);y.a=e.j?(++e.j,Ce(e.b,ve(1)),Ce(e.c,b)):(i=e.d[n.p][1],ol(e.b,h,ve(u(Le(e.b,h),15).a+1-i)),ol(e.c,h,ne(re(Le(e.c,h)))+b-i*e.f)),(e.r==(G0(),gD)&&(u(Le(e.b,h),15).a>e.k||u(Le(e.b,h-1),15).a>e.k)||e.r==wD&&(ne(re(Le(e.c,h)))>e.n||ne(re(Le(e.c,h-1)))>e.n))&&(f=!1),o=new qn(Vn(rr(n).a.Jc(),new ee));ht(o);)c=u(tt(o),17),l=c.c.i,e.g[l.p]==h&&(p=oVe(e,l),r=r+u(p.a,15).a,f=f&&Re($e(p.b)));return e.g[n.p]=h,r=r+e.d[n.p][0],new jc(ve(r),(Pn(),!!f))}function n$n(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;return y=e.c[n],S=e.c[t],A=u(C(y,(me(),Cy)),16),!!A&&A.gc()!=0&&A.Gc(S)||(O=y.k!=(zn(),dr)&&S.k!=dr,I=u(C(y,ap),9),R=u(C(S,ap),9),q=I!=R,V=!!I&&I!=y||!!R&&R!=S,te=qQ(y,(De(),Xn)),be=qQ(S,bt),V=V|(qQ(y,bt)||qQ(S,Xn)),fe=V&&q||te||be,O&&fe)||y.k==(zn(),wo)&&S.k==Qi||S.k==(zn(),wo)&&y.k==Qi?!1:(b=e.c[n],c=e.c[t],r=HHe(e.e,b,c,(De(),Kn)),f=HHe(e.i,b,c,et),CNn(e.f,b,c),h=Yze(e.b,b,c)+u(r.a,15).a+u(f.a,15).a+e.f.d,l=Yze(e.b,c,b)+u(r.b,15).a+u(f.b,15).a+e.f.b,e.a&&(p=u(C(b,wi),12),o=u(C(c,wi),12),i=MHe(e.g,p,o),h+=u(i.a,15).a,l+=u(i.b,15).a),h>l)}function sVe(e,n){var t,i,r,c,o;t=ne(re(C(n,(Ne(),Vf)))),t<2&&he(n,Vf,2),i=u(C(n,pl),86),i==(vr(),eh)&&he(n,pl,GB(n)),r=u(C(n,Dun),15),r.a==0?he(n,(me(),Oy),new vQ):he(n,(me(),Oy),new XR(r.a)),c=$e(C(n,mx)),c==null&&he(n,mx,(Pn(),ue(C(n,Y1))===ue((z1(),H7)))),Zi(new wn(null,new pn(n.a,16)),new Zue(e)),Zi(ou(new wn(null,new pn(n.b,16)),new Af),new eoe(e)),o=new tVe(n),he(n,(me(),Lv),o),RT(e.a),fa(e.a,(zr(),Kf),u(C(n,_y),188)),fa(e.a,r1,u(C(n,bH),188)),fa(e.a,eo,u(C(n,wx),188)),fa(e.a,no,u(C(n,mH),188)),fa(e.a,Pc,D7n(u(C(n,Y1),222))),Fse(e.a,YRn(n)),he(n,yie,rN(e.a,n))}function jge(e,n,t,i,r){var c,o,l,f,h,b,p,y,S,A,O,I,R;for(p=new pt,o=new Te,tqe(e,t,e.d.zg(),o,p),tqe(e,i,e.d.Ag(),o,p),e.b=.2*(O=lUe(ou(new wn(null,new pn(o,16)),new pM)),I=lUe(ou(new wn(null,new pn(o,16)),new mM)),k.Math.min(O,I)),c=0,l=0;l=2&&(R=NUe(o,!0,y),!e.e&&(e.e=new MEe(e)),SEn(e.e,R,o,e.b)),sGe(o,y),o$n(o),S=-1,b=new L(o);b.a0&&(t+=f.n.a+f.o.a/2,++p),A=new L(f.j);A.a0&&(t/=p),R=oe(Jr,Jc,30,i.a.c.length,15,1),l=0,h=new L(i.a);h.a-1){for(r=St(l,0);r.b!=r.d.c;)i=u(jt(r),132),i.v=o;for(;l.b!=0;)for(i=u(eW(l,0),132),t=new L(i.i);t.a-1){for(c=new L(l);c.a0)&&(fw(f,k.Math.min(f.o,r.o-1)),g0(f,f.i-1),f.i==0&&Hn(l.c,f))}}function aVe(e,n,t,i,r){var c,o,l,f;return f=Ki,o=!1,l=dge(e,Nr(new je(n.a,n.b),e),gi(new je(t.a,t.b),r),Nr(new je(i.a,i.b),t)),c=!!l&&!(k.Math.abs(l.a-e.a)<=np&&k.Math.abs(l.b-e.b)<=np||k.Math.abs(l.a-n.a)<=np&&k.Math.abs(l.b-n.b)<=np),l=dge(e,Nr(new je(n.a,n.b),e),t,r),l&&((k.Math.abs(l.a-e.a)<=np&&k.Math.abs(l.b-e.b)<=np)==(k.Math.abs(l.a-n.a)<=np&&k.Math.abs(l.b-n.b)<=np)||c?f=k.Math.min(f,fE(Nr(l,t))):o=!0),l=dge(e,Nr(new je(n.a,n.b),e),i,r),l&&(o||(k.Math.abs(l.a-e.a)<=np&&k.Math.abs(l.b-e.b)<=np)==(k.Math.abs(l.a-n.a)<=np&&k.Math.abs(l.b-n.b)<=np)||c)&&(f=k.Math.min(f,fE(Nr(l,i)))),f}function hVe(e){r2(e,new Jw(GP(t2(Zp(n2(e2(new dd,V0),cQe),"Minimizes the stress within a layout using stress majorization. Stress exists if the euclidean distance between a pair of nodes doesn't match their graph theoretic distance, that is, the shortest path between the two nodes. The method allows to specify individual edge lengths."),new dc),$o))),Ee(e,V0,jS,_e(d3e)),Ee(e,V0,lF,(Pn(),!0)),Ee(e,V0,wv,_e(Ltn)),Ee(e,V0,dy,_e(Ptn)),Ee(e,V0,hy,_e($tn)),Ee(e,V0,U8,_e(_tn)),Ee(e,V0,ES,_e(g3e)),Ee(e,V0,X8,_e(Rtn)),Ee(e,V0,swe,_e(h3e)),Ee(e,V0,fwe,_e(f3e)),Ee(e,V0,awe,_e(a3e)),Ee(e,V0,hwe,_e(b3e)),Ee(e,V0,lwe,_e(kJ))}function s$n(e){var n,t,i,r,c,o,l,f;for(n=null,i=new L(e);i.a0&&t.c==0&&(!n&&(n=new Te),Hn(n.c,t));if(n)for(;n.c.length!=0;){if(t=u(Ad(n,0),239),t.b&&t.b.c.length>0){for(c=(!t.b&&(t.b=new Te),new L(t.b));c.awu(e,t,0))return new jc(r,t)}else if(ne(Na(r.g,r.d[0]).a)>ne(Na(t.g,t.d[0]).a))return new jc(r,t)}for(l=(!t.e&&(t.e=new Te),t.e).Jc();l.Ob();)o=u(l.Pb(),239),f=(!o.b&&(o.b=new Te),o.b),S2(0,f.c.length),Ij(f.c,0,t),o.c==f.c.length&&Hn(n.c,o)}return null}function dS(e,n){var t,i,r,c,o,l,f,h,b;if(n.e==5){cVe(e,n);return}if(h=n,!(h.b==null||e.b==null)){for(ov(e),aS(e),ov(h),aS(h),t=oe($t,ni,30,e.b.length+h.b.length,15,1),b=0,i=0,o=0;i=l&&r<=f)l<=r&&c<=f?i+=2:l<=r?(e.b[i]=f+1,o+=2):c<=f?(t[b++]=r,t[b++]=l-1,i+=2):(t[b++]=r,t[b++]=l-1,e.b[i]=f+1,o+=2);else if(f0),u(b.a.Xb(b.c=--b.b),17));c!=i&&b.b>0;)e.a[c.p]=!0,e.a[i.p]=!0,c=(at(b.b>0),u(b.a.Xb(b.c=--b.b),17));b.b>0&&As(b)}}function dVe(e,n,t){var i,r,c,o,l,f,h,b,p,y;if(t)for(i=-1,b=new qr(n,0);b.b0?r-=864e5:r+=864e5,f=new jle(mc(Pu(n.q.getTime()),r))),b=new o4,h=e.a.length,c=0;c=97&&i<=122||i>=65&&i<=90){for(o=c+1;o=h)throw $(new Gn("Missing trailing '"));o+1=14&&b<=16))?n.a._b(i)?(t.a?Kt(t.a,t.b):t.a=new il(t.d),Uj(t.a,"[...]")):(l=B4(i),h=new w2(n),I1(t,gVe(l,h))):X(i,171)?I1(t,tTn(u(i,171))):X(i,195)?I1(t,GAn(u(i,195))):X(i,201)?I1(t,YMn(u(i,201))):X(i,2073)?I1(t,qAn(u(i,2073))):X(i,54)?I1(t,nTn(u(i,54))):X(i,584)?I1(t,gTn(u(i,584))):X(i,830)?I1(t,eTn(u(i,830))):X(i,108)&&I1(t,ZCn(u(i,108))):I1(t,i==null?Vo:su(i));return t.a?t.e.length==0?t.a.a:t.a.a+(""+t.e):t.c}function N8(e,n){var t,i,r,c;c=e.F,n==null?(e.F=null,i8(e,null)):(e.F=(_n(n),n),i=lh(n,Xo(60)),i!=-1?(r=(Yr(0,i,n.length),n.substr(0,i)),lh(n,Xo(46))==-1&&!bn(r,ry)&&!bn(r,RS)&&!bn(r,XF)&&!bn(r,BS)&&!bn(r,zS)&&!bn(r,FS)&&!bn(r,JS)&&!bn(r,HS)&&(r=een),t=R$(n,Xo(62)),t!=-1&&(r+=""+(Yn(t+1,n.length+1),n.substr(t+1))),i8(e,r)):(r=n,lh(n,Xo(46))==-1&&(i=lh(n,Xo(91)),i!=-1&&(r=(Yr(0,i,n.length),n.substr(0,i))),!bn(r,ry)&&!bn(r,RS)&&!bn(r,XF)&&!bn(r,BS)&&!bn(r,zS)&&!bn(r,FS)&&!bn(r,JS)&&!bn(r,HS)?(r=een,i!=-1&&(r+=""+(Yn(i,n.length+1),n.substr(i)))):r=n),i8(e,r),r==n&&(e.F=e.D))),(e.Db&4)!=0&&(e.Db&1)==0&&ai(e,new Ir(e,1,5,c,n))}function g$n(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A;if(e.c=e.e,A=$e(C(n,(Ne(),Iun))),S=A==null||(_n(A),A),c=u(C(n,(me(),po)),22).Gc((Dc(),Kl)),r=u(C(n,Wi),102),t=!(r==(Br(),Tg)||r==f1||r==to),S&&(t||!c)){for(p=new L(n.a);p.a=0)return r=Rjn(e,(Yr(1,o,n.length),n.substr(1,o-1))),b=(Yr(o+1,f,n.length),n.substr(o+1,f-(o+1))),FRn(e,b,r)}else{if(t=-1,Mme==null&&(Mme=new RegExp("\\d")),Mme.test(String.fromCharCode(l))&&(t=Hle(n,Xo(46),f-1),t>=0)){i=u(fY(e,VRe(e,(Yr(1,t,n.length),n.substr(1,t-1))),!1),61),h=0;try{h=hl((Yn(t+1,n.length+1),n.substr(t+1)),Xr,oi)}catch(y){throw y=sr(y),X(y,131)?(c=y,$(new uB(c))):$(y)}if(h>16==-10?t=u(e.Cb,293).Wk(n,t):e.Db>>16==-15&&(!n&&(n=(yn(),ih)),!h&&(h=(yn(),ih)),e.Cb.Vh()&&(f=new L1(e.Cb,1,13,h,n,Ld(Ts(u(e.Cb,62)),e),!1),t?t.lj(f):t=f));else if(X(e.Cb,88))e.Db>>16==-23&&(X(n,88)||(n=(yn(),jf)),X(h,88)||(h=(yn(),jf)),e.Cb.Vh()&&(f=new L1(e.Cb,1,10,h,n,Ld(Vu(u(e.Cb,29)),e),!1),t?t.lj(f):t=f));else if(X(e.Cb,446))for(l=u(e.Cb,834),o=(!l.b&&(l.b=new OP(new kX)),l.b),c=(i=new D2(new on(o.a).a),new NP(i));c.a.b;)r=u(Q3(c.a).jd(),87),t=D8(r,Oz(r,l),t)}return t}function p$n(e,n){var t,i,r,c,o,l,f,h,b,p,y;for(o=Re($e(ye(e,(Ne(),pm)))),y=u(ye(e,ym),22),f=!1,h=!1,p=new ut((!e.c&&(e.c=new pe($s,e,9,9)),e.c));p.e!=p.i.gc()&&(!f||!h);){for(c=u(ft(p),125),l=0,r=qh(Rl(z(B(Xl,1),On,20,0,[(!c.d&&(c.d=new Nn(pr,c,8,5)),c.d),(!c.e&&(c.e=new Nn(pr,c,7,4)),c.e)])));ht(r)&&(i=u(tt(r),85),b=o&&Hw(i)&&Re($e(ye(i,kg))),t=VKe((!i.b&&(i.b=new Nn(vt,i,4,7)),i.b),c)?e==Bi(iu(u(K((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c),0),84))):e==Bi(iu(u(K((!i.b&&(i.b=new Nn(vt,i,4,7)),i.b),0),84))),!((b||t)&&(++l,l>1))););(l>0||y.Gc((ps(),Z1))&&(!c.n&&(c.n=new pe(ju,c,1,7)),c.n).i>0)&&(f=!0),l>1&&(h=!0)}f&&n.Ec((Dc(),Kl)),h&&n.Ec((Dc(),cx))}function pVe(e){var n,t,i,r,c,o,l,f,h,b,p,y;if(y=u(ye(e,(Xt(),Cg)),22),y.dc())return null;if(l=0,o=0,y.Gc((Vs(),qD))){for(b=u(ye(e,Kx),102),i=2,t=2,r=2,c=2,n=Bi(e)?u(ye(Bi(e),Mg),86):u(ye(e,Mg),86),h=new ut((!e.c&&(e.c=new pe($s,e,9,9)),e.c));h.e!=h.i.gc();)if(f=u(ft(h),125),p=u(ye(f,Qv),64),p==(De(),ku)&&(p=uge(f,n),ji(f,Qv,p)),b==(Br(),to))switch(p.g){case 1:i=k.Math.max(i,f.i+f.g);break;case 2:t=k.Math.max(t,f.j+f.f);break;case 3:r=k.Math.max(r,f.i+f.g);break;case 4:c=k.Math.max(c,f.j+f.f)}else switch(p.g){case 1:i+=f.g+2;break;case 2:t+=f.f+2;break;case 3:r+=f.g+2;break;case 4:c+=f.f+2}l=k.Math.max(i,r),o=k.Math.max(t,c)}return Xw(e,l,o,!0,!0)}function m$n(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O;for(r=null,i=new L(n.a);i.a1)for(r=e.e.b,Vt(e.e,f),l=f.a.ec().Jc();l.Ob();)o=u(l.Pb(),9),ei(e.c,o,ve(r))}}function v$n(e,n,t,i){var r,c,o,l,f,h,b,p,y,S;for(c=new Lqe(n),p=UDn(e,n,c),S=k.Math.max(ne(re(C(n,(Ne(),Gd)))),1),b=new L(p.a);b.a=0){for(f=null,l=new qr(b.a,h+1);l.b0,h?h&&(y=R.p,o?++y:--y,p=u(Le(R.c.a,y),9),i=Oze(p),S=!(RUe(i,fe,t[0])||KDe(i,fe,t[0]))):S=!0),A=!1,be=n.D.i,be&&be.c&&l.e&&(b=o&&be.p>0||!o&&be.p=0&&Oo?1:Lb(isNaN(0),isNaN(o)))<0&&(Bf(xh),(k.Math.abs(o-1)<=xh||o==1||isNaN(o)&&isNaN(1)?0:o<1?-1:o>1?1:Lb(isNaN(o),isNaN(1)))<0)&&(Bf(xh),(k.Math.abs(0-l)<=xh||l==0||isNaN(0)&&isNaN(l)?0:0l?1:Lb(isNaN(0),isNaN(l)))<0)&&(Bf(xh),(k.Math.abs(l-1)<=xh||l==1||isNaN(l)&&isNaN(1)?0:l<1?-1:l>1?1:Lb(isNaN(l),isNaN(1)))<0)),c)}function M$n(e){var n,t,i,r,c,o,l,f,h,b,p;for(e.j=oe($t,ni,30,e.g,15,1),e.o=new Te,Zi(ou(new wn(null,new pn(e.e.b,16)),new h3),new jEe(e)),e.a=oe(ts,pa,30,e.b,16,1),AO(new wn(null,new pn(e.e.b,16)),new SEe(e)),i=(p=new Te,Zi(si(ou(new wn(null,new pn(e.e.b,16)),new _5),new EEe(e)),new sCe(e,p)),p),f=new L(i);f.a=h.c.c.length?b=Bae((zn(),Qi),dr):b=Bae((zn(),dr),dr),b*=2,c=t.a.g,t.a.g=k.Math.max(c,c+(b-c)),o=t.b.g,t.b.g=k.Math.max(o,o+(b-o)),r=n}}function Vz(e,n){var t;if(e.e)throw $(new Uc((M1(bte),qZ+bte.k+UZ)));if(!zgn(e.a,n))throw $(new hu($Ye+n+RYe));if(n==e.d)return e;switch(t=e.d,e.d=n,t.g){case 0:switch(n.g){case 2:zw(e);break;case 1:P0(e),zw(e);break;case 4:rv(e),zw(e);break;case 3:rv(e),P0(e),zw(e)}break;case 2:switch(n.g){case 1:P0(e),_W(e);break;case 4:rv(e),zw(e);break;case 3:rv(e),P0(e),zw(e)}break;case 1:switch(n.g){case 2:P0(e),_W(e);break;case 4:P0(e),rv(e),zw(e);break;case 3:P0(e),rv(e),P0(e),zw(e)}break;case 4:switch(n.g){case 2:rv(e),zw(e);break;case 1:rv(e),P0(e),zw(e);break;case 3:P0(e),_W(e)}break;case 3:switch(n.g){case 2:P0(e),rv(e),zw(e);break;case 1:P0(e),rv(e),P0(e),zw(e);break;case 4:P0(e),_W(e)}}return e}function hv(e,n){var t;if(e.d)throw $(new Uc((M1(Cte),qZ+Cte.k+UZ)));if(!Bgn(e.a,n))throw $(new hu($Ye+n+RYe));if(n==e.c)return e;switch(t=e.c,e.c=n,t.g){case 0:switch(n.g){case 2:Zb(e);break;case 1:L0(e),Zb(e);break;case 4:cv(e),Zb(e);break;case 3:cv(e),L0(e),Zb(e)}break;case 2:switch(n.g){case 1:L0(e),LW(e);break;case 4:cv(e),Zb(e);break;case 3:cv(e),L0(e),Zb(e)}break;case 1:switch(n.g){case 2:L0(e),LW(e);break;case 4:L0(e),cv(e),Zb(e);break;case 3:L0(e),cv(e),L0(e),Zb(e)}break;case 4:switch(n.g){case 2:cv(e),Zb(e);break;case 1:cv(e),L0(e),Zb(e);break;case 3:L0(e),LW(e)}break;case 3:switch(n.g){case 2:L0(e),cv(e),Zb(e);break;case 1:L0(e),cv(e),L0(e),Zb(e);break;case 4:L0(e),LW(e)}}return e}function C$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;for(p=e.b,b=new qr(p,0),d2(b,new Xu(e)),q=!1,o=1;b.b0&&(n.a+=To),Yz(u(ft(l),174),n);for(n.a+=eee,f=new p4((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c));f.e!=f.i.gc();)f.e>0&&(n.a+=To),Yz(u(ft(f),174),n);n.a+=")"}}function T$n(e,n,t){var i,r,c,o,l,f,h,b;for(f=new ut((!e.a&&(e.a=new pe(Jt,e,10,11)),e.a));f.e!=f.i.gc();)for(l=u(ft(f),26),r=new qn(Vn(H0(l).a.Jc(),new ee));ht(r);){if(i=u(tt(r),85),!i.b&&(i.b=new Nn(vt,i,4,7)),!(i.b.i<=1&&(!i.c&&(i.c=new Nn(vt,i,5,8)),i.c.i<=1)))throw $(new u4("Graph must not contain hyperedges."));if(!ZE(i)&&l!=iu(u(K((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c),0),84)))for(h=new nNe,$u(h,i),he(h,(D0(),jy),i),EP(h,u(du(Xc(t.f,l)),155)),eX(h,u(Bn(t,iu(u(K((!i.c&&(i.c=new Nn(vt,i,5,8)),i.c),0),84))),155)),Ce(n.c,h),o=new ut((!i.n&&(i.n=new pe(ju,i,1,7)),i.n));o.e!=o.i.gc();)c=u(ft(o),157),b=new lPe(h,c.a),$u(b,c),he(b,jy,c),b.e.a=k.Math.max(c.g,1),b.e.b=k.Math.max(c.f,1),hge(b),Ce(n.d,b)}}function O$n(e,n,t){var i,r,c,o,l,f,h,b,p,y;switch(t.Tg("Node promotion heuristic",1),e.i=n,e.r=u(C(n,(Ne(),aD)),243),e.r!=(G0(),D7)&&e.r!=Sx?tRn(e):CDn(e),b=u(C(e.i,i4e),15).a,c=new Oq,e.r.g){case 2:case 1:O8(e,c);break;case 3:for(e.r=MH,O8(e,c),f=0,l=new L(e.b);l.ae.k&&(e.r=gD,O8(e,c));break;case 4:for(e.r=MH,O8(e,c),h=0,r=new L(e.c);r.ae.n&&(e.r=wD,O8(e,c));break;case 6:y=sc(k.Math.ceil(e.g.length*b/100)),O8(e,new kje(y));break;case 5:p=sc(k.Math.ceil(e.e*b/100)),O8(e,new jje(p));break;case 8:ZVe(e,!0);break;case 9:ZVe(e,!1);break;default:O8(e,c)}e.r!=D7&&e.r!=Sx?KNn(e,n):dIn(e,n),t.Ug()}function N$n(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;for(p=new Mge(e),v4n(p,!(n==(vr(),Vl)||n==Za)),b=p.a,y=new t4,r=(ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])),o=0,f=r.length;o0&&(y.d+=b.n.d,y.d+=b.d),y.a>0&&(y.a+=b.n.a,y.a+=b.d),y.b>0&&(y.b+=b.n.b,y.b+=b.d),y.c>0&&(y.c+=b.n.c,y.c+=b.d),y}function yVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A;for(y=t.d,p=t.c,c=new je(t.f.a+t.d.b+t.d.c,t.f.b+t.d.d+t.d.a),o=c.b,h=new L(e.a);h.a0&&(e.c[n.c.p][n.p].d+=Is(e.i,24)*vN*.07000000029802322-.03500000014901161,e.c[n.c.p][n.p].a=e.c[n.c.p][n.p].d/e.c[n.c.p][n.p].b)}}function I$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;for(A=new L(e);A.ai.d,i.d=k.Math.max(i.d,n),l&&t&&(i.d=k.Math.max(i.d,i.a),i.a=i.d+r);break;case 3:t=n>i.a,i.a=k.Math.max(i.a,n),l&&t&&(i.a=k.Math.max(i.a,i.d),i.d=i.a+r);break;case 2:t=n>i.c,i.c=k.Math.max(i.c,n),l&&t&&(i.c=k.Math.max(i.b,i.c),i.b=i.c+r);break;case 4:t=n>i.b,i.b=k.Math.max(i.b,n),l&&t&&(i.b=k.Math.max(i.b,i.c),i.c=i.b+r)}}}function jVe(e,n){var t,i,r,c,o,l,f,h,b;return h="",n.length==0?e.le(Jge,wZ,-1,-1):(b=J2(n),bn(b.substr(0,3),"at ")&&(b=(Yn(3,b.length+1),b.substr(3))),b=b.replace(/\[.*?\]/g,""),o=b.indexOf("("),o==-1?(o=b.indexOf("@"),o==-1?(h=b,b=""):(h=J2((Yn(o+1,b.length+1),b.substr(o+1))),b=J2((Yr(0,o,b.length),b.substr(0,o))))):(t=b.indexOf(")",o),h=(Yr(o+1,t,b.length),b.substr(o+1,t-(o+1))),b=J2((Yr(0,o,b.length),b.substr(0,o)))),o=lh(b,Xo(46)),o!=-1&&(b=(Yn(o+1,b.length+1),b.substr(o+1))),(b.length==0||bn(b,"Anonymous function"))&&(b=wZ),l=R$(h,Xo(58)),r=Hle(h,Xo(58),l-1),f=-1,i=-1,c=Jge,l!=-1&&r!=-1&&(c=(Yr(0,r,h.length),h.substr(0,r)),f=yOe((Yr(r+1,l,h.length),h.substr(r+1,l-(r+1)))),i=yOe((Yn(l+1,h.length+1),h.substr(l+1)))),e.le(c,b,f,i))}function L$n(e){var n,t,i,r,c,o,l,f,h,b,p;for(h=new L(e);h.a0||b.j==Kn&&b.e.c.length-b.g.c.length<0)){n=!1;break}for(r=new L(b.g);r.a=h&&be>=I&&(y+=A.n.b+O.n.b+O.a.b-te,++l));if(t)for(o=new L(q.e);o.a=h&&be>=I&&(y+=A.n.b+O.n.b+O.a.b-te,++l))}l>0&&(fe+=y/l,++S)}S>0?(n.a=r*fe/S,n.g=S):(n.a=0,n.g=0)}function xge(e,n,t,i){var r,c,o,l,f;return l=new Mge(n),NNn(l,i),r=!0,e&&e.nf((Xt(),Mg))&&(c=u(e.mf((Xt(),Mg)),86),r=c==(vr(),eh)||c==Zc||c==ru),jXe(l,!1),Ao(l.e.Pf(),new Kle(l,!1,r)),JV(l,l.f,(ga(),Ou),(De(),Xn)),JV(l,l.f,Nu,bt),JV(l,l.g,Ou,Kn),JV(l,l.g,Nu,et),HJe(l,Xn),HJe(l,bt),RIe(l,et),RIe(l,Kn),h2(),o=l.A.Gc((Vs(),Lm))&&l.B.Gc((_s(),XD))?tJe(l):null,o&&Ybn(l.a,o),_$n(l),nxn(l),txn(l),f$n(l),E_n(l),Mxn(l),OQ(l,Xn),OQ(l,bt),aIn(l),ZLn(l),t&&(Xjn(l),Cxn(l),OQ(l,et),OQ(l,Kn),f=l.B.Gc((_s(),iA)),lqe(l,f,Xn),lqe(l,f,bt),fqe(l,f,et),fqe(l,f,Kn),Zi(new wn(null,new pn(new ct(l.i),0)),new Fg),Zi(si(new wn(null,Jfe(l.r).a.oc()),new Eb),new Jg),zAn(l),l.e.Nf(l.o),Zi(new wn(null,Jfe(l.r).a.oc()),new _u)),l.o}function $$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O;for(h=Ki,i=new L(e.a.b);i.a1)for(S=new wge(A,V,i),rc(V,new fCe(e,S)),Hn(o.c,S),p=V.a.ec().Jc();p.Ob();)b=u(p.Pb(),49),qo(c,b.b);if(l.a.gc()>1)for(S=new wge(A,l,i),rc(l,new aCe(e,S)),Hn(o.c,S),p=l.a.ec().Jc();p.Ob();)b=u(p.Pb(),49),qo(c,b.b)}}function F$n(e,n){var t,i,r,c,o,l;if(u(C(n,(me(),po)),22).Gc((Dc(),Kl))){for(l=new L(n.a);l.a=0&&o0&&(u(zc(e.b,n),127).a.b=t)}function K$n(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;for(S=0,i=new ar,c=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));c.e!=c.i.gc();)r=u(ft(c),26),Re($e(ye(r,(Ne(),Eg))))||(p=Bi(r),qz(p)&&!Re($e(ye(r,lH)))&&(ji(r,(me(),Ti),ve(S)),++S,da(r,gm)&&hr(i,u(ye(r,gm),15))),SVe(e,r,t));for(he(t,(me(),cb),ve(S)),he(t,cD,ve(i.a.gc())),S=0,b=new ut((!n.b&&(n.b=new pe(pr,n,12,3)),n.b));b.e!=b.i.gc();)f=u(ft(b),85),qz(n)&&(ji(f,Ti,ve(S)),++S),I=hW(f),R=kGe(f),y=Re($e(ye(I,(Ne(),pm)))),O=!Re($e(ye(f,Eg))),A=y&&Hw(f)&&Re($e(ye(f,kg))),o=Bi(I)==n&&Bi(I)==Bi(R),l=(Bi(I)==n&&R==n)^(Bi(R)==n&&I==n),O&&!A&&(l||o)&&Dge(e,f,n,t);if(Bi(n))for(h=new ut(qIe(Bi(n)));h.e!=h.i.gc();)f=u(ft(h),85),I=hW(f),I==n&&Hw(f)&&(A=Re($e(ye(I,(Ne(),pm))))&&Re($e(ye(f,kg))),A&&Dge(e,f,n,t))}function V$n(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Cn,Dn;for(fe=new Te,A=new L(e.b);A.a=n.length)return{done:!0};var r=n[i++];return{value:[r,t.get(r)],done:!1}}}},qDn()||(e.prototype.createObject=function(){return{}},e.prototype.get=function(n){return this.obj[":"+n]},e.prototype.set=function(n,t){this.obj[":"+n]=t},e.prototype[zZ]=function(n){delete this.obj[":"+n]},e.prototype.keys=function(){var n=[];for(var t in this.obj)t.charCodeAt(0)==58&&n.push(t.substring(1));return n}),e}function Ci(){Ci=Y,_x=new yi(owe),new Li("DEPTH",ve(0)),gre=new Li("FAN",ve(0)),pye=new Li(KQe,ve(0)),fb=new Li("ROOT",(Pn(),!1)),mre=new Li("LEFTNEIGHBOR",null),rsn=new Li("RIGHTNEIGHBOR",null),LH=new Li("LEFTSIBLING",null),vre=new Li("RIGHTSIBLING",null),bre=new Li("DUMMY",!1),new Li("LEVEL",ve(0)),yye=new Li("REMOVABLE_EDGES",new Si),jD=new Li("XCOOR",ve(0)),ED=new Li("YCOOR",ve(0)),PH=new Li("LEVELHEIGHT",0),Ea=new Li("LEVELMIN",0),Yf=new Li("LEVELMAX",0),wre=new Li("GRAPH_XMIN",0),pre=new Li("GRAPH_YMIN",0),mye=new Li("GRAPH_XMAX",0),vye=new Li("GRAPH_YMAX",0),wye=new Li("COMPACT_LEVEL_ASCENSION",!1),dre=new Li("COMPACT_CONSTRAINTS",new Te),Ix=new Li("ID",""),Lx=new Li("POSITION",ve(0)),Xd=new Li("PRELIM",0),L7=new Li("MODIFIER",0),_7=new yi(iQe),kD=new yi(rQe)}function Z$n(e){cge();var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;if(e==null)return null;if(p=e.length*8,p==0)return"";for(l=p%24,S=p/24|0,y=l!=0?S+1:S,c=null,c=oe(Wl,kh,30,y*4,15,1),h=0,b=0,n=0,t=0,i=0,o=0,r=0,f=0;f>24,h=(n&3)<<24>>24,A=(n&-128)==0?n>>2<<24>>24:(n>>2^192)<<24>>24,O=(t&-128)==0?t>>4<<24>>24:(t>>4^240)<<24>>24,I=(i&-128)==0?i>>6<<24>>24:(i>>6^252)<<24>>24,c[o++]=t0[A],c[o++]=t0[O|h<<4],c[o++]=t0[b<<2|I],c[o++]=t0[i&63];return l==8?(n=e[r],h=(n&3)<<24>>24,A=(n&-128)==0?n>>2<<24>>24:(n>>2^192)<<24>>24,c[o++]=t0[A],c[o++]=t0[h<<4],c[o++]=61,c[o++]=61):l==16&&(n=e[r],t=e[r+1],b=(t&15)<<24>>24,h=(n&3)<<24>>24,A=(n&-128)==0?n>>2<<24>>24:(n>>2^192)<<24>>24,O=(t&-128)==0?t>>4<<24>>24:(t>>4^240)<<24>>24,c[o++]=t0[A],c[o++]=t0[O|h<<4],c[o++]=t0[b<<2],c[o++]=61),gh(c,0,c.length)}function eRn(e,n){var t,i,r,c,o,l,f;if(e.e==0&&e.p>0&&(e.p=-(e.p-1)),e.p>Xr&&Fae(n,e.p-K0),o=n.q.getDate(),GT(n,1),e.k>=0&&O4n(n,e.k),e.c>=0?GT(n,e.c):e.k>=0?(f=new p1e(n.q.getFullYear()-K0,n.q.getMonth(),35),i=35-f.q.getDate(),GT(n,k.Math.min(i,o))):GT(n,o),e.f<0&&(e.f=n.q.getHours()),e.b>0&&e.f<12&&(e.f+=12),zwn(n,e.f==24&&e.g?0:e.f),e.j>=0&&s9n(n,e.j),e.n>=0&&k9n(n,e.n),e.i>=0&&iTe(n,mc(ac(BO(Pu(n.q.getTime()),Rd),Rd),e.i)),e.a&&(r=new t$,Fae(r,r.q.getFullYear()-K0-80),JX(Pu(n.q.getTime()),Pu(r.q.getTime()))&&Fae(n,r.q.getFullYear()-K0+100)),e.d>=0){if(e.c==-1)t=(7+e.d-n.q.getDay())%7,t>3&&(t-=7),l=n.q.getMonth(),GT(n,n.q.getDate()+t),n.q.getMonth()!=l&>(n,n.q.getDate()+(t>0?-7:7));else if(n.q.getDay()!=e.d)return!1}return e.o>Xr&&(c=n.q.getTimezoneOffset(),iTe(n,mc(Pu(n.q.getTime()),(e.o-c)*60*Rd))),!0}function CVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te;if(r=C(n,(me(),wi)),!!X(r,206)){for(A=u(r,26),O=n.e,y=new wc(n.c),c=n.d,y.a+=c.b,y.b+=c.d,te=u(ye(A,(Ne(),vH)),182),cs(te,(_s(),lG))&&(S=u(ye(A,l4e),104),QU(S,c.a),nX(S,c.d),WU(S,c.b),ZU(S,c.c)),t=new Te,b=new L(n.a);b.ai.c.length-1;)Ce(i,new jc(mv,Fpe));t=u(C(r,Nh),15).a,x1(u(C(e,mp),86))?(r.e.ane(re((vn(t,i.c.length),u(i.c[t],49)).b))&&FC((vn(t,i.c.length),u(i.c[t],49)),r.e.a+r.f.a)):(r.e.bne(re((vn(t,i.c.length),u(i.c[t],49)).b))&&FC((vn(t,i.c.length),u(i.c[t],49)),r.e.b+r.f.b))}for(c=St(e.b,0);c.b!=c.d.c;)r=u(jt(c),40),t=u(C(r,(Mu(),Nh)),15).a,he(r,(Ci(),Ea),re((vn(t,i.c.length),u(i.c[t],49)).a)),he(r,Yf,re((vn(t,i.c.length),u(i.c[t],49)).b));n.Ug()}function tRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O;for(e.o=ne(re(C(e.i,(Ne(),xg)))),e.f=ne(re(C(e.i,ub))),e.j=e.i.b.c.length,l=e.j-1,y=0,e.k=0,e.n=0,e.b=$f(oe(jr,Se,15,e.j,0,1)),e.c=$f(oe(gr,Se,346,e.j,7,1)),o=new L(e.i.b);o.a0&&Ce(e.q,b),Ce(e.p,b);n-=i,S=f+n,h+=n*e.f,ol(e.b,l,ve(S)),ol(e.c,l,h),e.k=k.Math.max(e.k,S),e.n=k.Math.max(e.n,h),e.e+=n,n+=O}}function NVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;if(n.b!=0){for(S=new Si,l=null,A=null,i=sc(k.Math.floor(k.Math.log(n.b)*k.Math.LOG10E)+1),f=0,V=St(n,0);V.b!=V.d.c;)for(R=u(jt(V),40),ue(A)!==ue(C(R,(Ci(),Ix)))&&(A=Pt(C(R,Ix)),f=0),A!=null?l=A+dLe(f++,i):l=dLe(f++,i),he(R,Ix,l),I=(r=St(new S1(R).a.d,0),new E3(r));YC(I.a);)O=u(jt(I.a),65).c,Xi(S,O,S.c.b,S.c),he(O,Ix,l);for(y=new pt,o=0;o0&&(V-=S),pge(o,V),b=0,y=new L(o.a);y.a0),l.a.Xb(l.c=--l.b)),f=.4*i*b,!c&&l.b0&&(f=(Yn(0,n.length),n.charCodeAt(0)),f!=64)){if(f==37&&(p=n.lastIndexOf("%"),h=!1,p!=0&&(p==y-1||(h=(Yn(p+1,n.length),n.charCodeAt(p+1)==46))))){if(o=(Yr(1,p,n.length),n.substr(1,p-1)),V=bn("%",o)?null:Tge(o),i=0,h)try{i=hl((Yn(p+2,n.length+1),n.substr(p+2)),Xr,oi)}catch(te){throw te=sr(te),X(te,131)?(l=te,$(new uB(l))):$(te)}for(I=Uhe(e.Dh());I.Ob();)if(A=OB(I),X(A,504)&&(r=u(A,587),q=r.d,(V==null?q==null:bn(V,q))&&i--==0))return r;return null}if(b=n.lastIndexOf("."),S=b==-1?n:(Yr(0,b,n.length),n.substr(0,b)),t=0,b!=-1)try{t=hl((Yn(b+1,n.length+1),n.substr(b+1)),Xr,oi)}catch(te){if(te=sr(te),X(te,131))S=n;else throw $(te)}for(S=bn("%",S)?null:Tge(S),O=Uhe(e.Dh());O.Ob();)if(A=OB(O),X(A,197)&&(c=u(A,197),R=c.ve(),(S==null?R==null:bn(S,R))&&t--==0))return c;return null}return wVe(e,n)}function lRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q;for(b=new pt,f=new Cw,i=new L(e.a.a.b);i.an.d.c){if(S=e.c[n.a.d],I=e.c[p.a.d],S==I)continue;Hf(Nf(Of(Df(Tf(new tf,1),100),S),I))}}}}}function fRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;if(y=u(u(pi(e.r,n),22),83),n==(De(),et)||n==Kn){xVe(e,n);return}for(c=n==Xn?(Lw(),UN):(Lw(),XN),te=n==Xn?(Uo(),ka):(Uo(),Xf),t=u(zc(e.b,n),127),i=t.i,r=i.c+q3(z(B(Jr,1),Jc,30,15,[t.n.b,e.C.b,e.k])),R=i.c+i.b-q3(z(B(Jr,1),Jc,30,15,[t.n.c,e.C.c,e.k])),o=$oe(Vle(c),e.t),q=n==Xn?Dr:Ki,p=y.Jc();p.Ob();)h=u(p.Pb(),115),!(!h.c||h.c.d.c.length<=0)&&(I=h.b.Kf(),O=h.e,S=h.c,A=S.i,A.b=(f=S.n,S.e.a+f.b+f.c),A.a=(l=S.n,S.e.b+l.d+l.a),FT(te,ewe),S.f=te,ba(S,(ws(),Uf)),A.c=O.a-(A.b-I.a)/2,be=k.Math.min(r,O.a),fe=k.Math.max(R,O.a+I.a),A.cfe&&(A.c=fe-A.b),Ce(o.d,new fV(A,U1e(o,A))),q=n==Xn?k.Math.max(q,O.b+h.b.Kf().b):k.Math.min(q,O.b));for(q+=n==Xn?e.t:-e.t,V=ade((o.e=q,o)),V>0&&(u(zc(e.b,n),127).a.b=V),b=y.Jc();b.Ob();)h=u(b.Pb(),115),!(!h.c||h.c.d.c.length<=0)&&(A=h.c.i,A.c-=h.e.a,A.d-=h.e.b)}function aRn(e,n){XW();var t,i,r,c,o,l,f,h,b,p,y,S,A,O;if(f=ao(e,0)<0,f&&(e=Cd(e)),ao(e,0)==0)switch(n){case 0:return"0";case 1:return R8;case 2:return"0.00";case 3:return"0.000";case 4:return"0.0000";case 5:return"0.00000";case 6:return"0.000000";default:return S=new p0,n<0?S.a+="0E+":S.a+="0E",S.a+=n==Xr?"2147483648":""+-n,S.a}b=18,p=oe(Wl,kh,30,b+1,15,1),t=b,O=e;do h=O,O=BO(O,10),p[--t]=Rt(mc(48,lf(h,ac(O,10))))&yr;while(ao(O,0)!=0);if(r=lf(lf(lf(b,t),n),1),n==0)return f&&(p[--t]=45),gh(p,t,b-t);if(n>0&&ao(r,-6)>=0){if(ao(r,0)>=0){for(c=t+Rt(r),l=b-1;l>=c;l--)p[l+1]=p[l];return p[++c]=46,f&&(p[--t]=45),gh(p,t,b-t+1)}for(o=2;JX(o,mc(Cd(r),1));o++)p[--t]=48;return p[--t]=46,p[--t]=48,f&&(p[--t]=45),gh(p,t,b-t)}return A=t+1,i=b,y=new o4,f&&(y.a+="-"),i-A>=1?(Fb(y,p[t]),y.a+=".",y.a+=gh(p,t+1,b-t-1)):y.a+=gh(p,t,b-t),y.a+="E",ao(r,0)>0&&(y.a+="+"),y.a+=""+rE(r),y.a}function DVe(e){r2(e,new Jw(GP(t2(Zp(n2(e2(new dd,Gl),"ELK Radial"),'A radial layout provider which is based on the algorithm of Peter Eades published in "Drawing free trees.", published by International Institute for Advanced Study of Social Information Science, Fujitsu Limited in 1991. The radial layouter takes a tree and places the nodes in radial order around the root. The nodes of the same tree level are placed on the same radius.'),new OM),Gl))),Ee(e,Gl,TF,_e(eln)),Ee(e,Gl,em,_e(nln)),Ee(e,Gl,wv,_e(Ysn)),Ee(e,Gl,dy,_e(Qsn)),Ee(e,Gl,hy,_e(Wsn)),Ee(e,Gl,U8,_e(Vsn)),Ee(e,Gl,ES,_e(Vye)),Ee(e,Gl,X8,_e(Zsn)),Ee(e,Gl,Zee,_e(Dre)),Ee(e,Gl,Wee,_e(Ire)),Ee(e,Gl,LF,_e(Qye)),Ee(e,Gl,ene,_e(_re)),Ee(e,Gl,nne,_e(Wye)),Ee(e,Gl,c2e,_e(Zye)),Ee(e,Gl,r2e,_e(Yye)),Ee(e,Gl,e2e,_e(FH)),Ee(e,Gl,n2e,_e(JH)),Ee(e,Gl,t2e,_e(SD)),Ee(e,Gl,i2e,_e(e6e)),Ee(e,Gl,Zpe,_e(Kye))}function Xw(e,n,t,i,r){var c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;if(I=new je(e.g,e.f),O=R0e(e),O.a=k.Math.max(O.a,n),O.b=k.Math.max(O.b,t),fe=O.a/I.a,b=O.b/I.b,te=O.a-I.a,f=O.b-I.b,i)for(o=Bi(e)?u(ye(Bi(e),(Xt(),Mg)),86):u(ye(e,(Xt(),Mg)),86),l=ue(ye(e,(Xt(),Kx)))===ue((Br(),to)),q=new ut((!e.c&&(e.c=new pe($s,e,9,9)),e.c));q.e!=q.i.gc();)switch(R=u(ft(q),125),V=u(ye(R,Qv),64),V==(De(),ku)&&(V=uge(R,o),ji(R,Qv,V)),V.g){case 1:l||Os(R,R.i*fe);break;case 2:Os(R,R.i+te),l||Ns(R,R.j*b);break;case 3:l||Os(R,R.i*fe),Ns(R,R.j+f);break;case 4:l||Ns(R,R.j*b)}if(ww(e,O.a,O.b),r)for(y=new ut((!e.n&&(e.n=new pe(ju,e,1,7)),e.n));y.e!=y.i.gc();)p=u(ft(y),157),S=p.i+p.g/2,A=p.j+p.f/2,be=S/I.a,h=A/I.b,be+h>=1&&(be-h>0&&A>=0?(Os(p,p.i+te),Ns(p,p.j+f*h)):be-h<0&&S>=0&&(Os(p,p.i+te*be),Ns(p,p.j+f)));return ji(e,(Xt(),Cg),(Vs(),c=u(sa(tA),10),new _l(c,u(_f(c,c.length),10),0))),new je(fe,b)}function Qz(e){var n,t,i,r,c,o,l,f,h,b,p;if(e==null)throw $(new sh(Vo));if(h=e,c=e.length,f=!1,c>0&&(n=(Yn(0,e.length),e.charCodeAt(0)),(n==45||n==43)&&(e=(Yn(1,e.length+1),e.substr(1)),--c,f=n==45)),c==0)throw $(new sh(Yw+h+'"'));for(;e.length>0&&(Yn(0,e.length),e.charCodeAt(0)==48);)e=(Yn(1,e.length+1),e.substr(1)),--c;if(c>(aKe(),nnn)[10])throw $(new sh(Yw+h+'"'));for(r=0;r0&&(p=-parseInt((Yr(0,i,e.length),e.substr(0,i)),10),e=(Yn(i,e.length+1),e.substr(i)),c-=i,t=!1);c>=o;){if(i=parseInt((Yr(0,o,e.length),e.substr(0,o)),10),e=(Yn(o,e.length+1),e.substr(o)),c-=o,t)t=!1;else{if(ao(p,l)<0)throw $(new sh(Yw+h+'"'));p=ac(p,b)}p=lf(p,i)}if(ao(p,0)>0)throw $(new sh(Yw+h+'"'));if(!f&&(p=Cd(p),ao(p,0)<0))throw $(new sh(Yw+h+'"'));return p}function Tge(e){ZW();var n,t,i,r,c,o,l,f;if(e==null)return null;if(r=lh(e,Xo(37)),r<0)return e;for(f=new il((Yr(0,r,e.length),e.substr(0,r))),n=oe(ds,kv,30,4,15,1),l=0,i=0,o=e.length;rr+2&&WY((Yn(r+1,e.length),e.charCodeAt(r+1)),G8e,q8e)&&WY((Yn(r+2,e.length),e.charCodeAt(r+2)),G8e,q8e))if(t=P3n((Yn(r+1,e.length),e.charCodeAt(r+1)),(Yn(r+2,e.length),e.charCodeAt(r+2))),r+=2,i>0?(t&192)==128?n[l++]=t<<24>>24:i=0:t>=128&&((t&224)==192?(n[l++]=t<<24>>24,i=2):(t&240)==224?(n[l++]=t<<24>>24,i=3):(t&248)==240&&(n[l++]=t<<24>>24,i=4)),i>0){if(l==i){switch(l){case 2:{Fb(f,((n[0]&31)<<6|n[1]&63)&yr);break}case 3:{Fb(f,((n[0]&15)<<12|(n[1]&63)<<6|n[2]&63)&yr);break}}l=0,i=0}}else{for(c=0;c=2){if((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i==0)t=(v0(),r=new yo,r),Et((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),t);else if((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i>1)for(y=new p4((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a));y.e!=y.i.gc();)KE(y);sge(n,u(K((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),0),170))}if(p)for(i=new ut((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a));i.e!=i.i.gc();)for(t=u(ft(i),170),h=new ut((!t.a&&(t.a=new mr(kl,t,5)),t.a));h.e!=h.i.gc();)f=u(ft(h),372),l.a=k.Math.max(l.a,f.a),l.b=k.Math.max(l.b,f.b);for(o=new ut((!e.n&&(e.n=new pe(ju,e,1,7)),e.n));o.e!=o.i.gc();)c=u(ft(o),157),b=u(ye(c,Yx),8),b&&Dl(c,b.a,b.b),p&&(l.a=k.Math.max(l.a,c.i+c.g),l.b=k.Math.max(l.b,c.j+c.f));return l}function _Ve(e,n,t,i,r){var c,o,l;if(ARe(e,n),o=n[0],c=ic(t.c,0),l=-1,j1e(t))if(i>0){if(o+i>e.length)return!1;l=Az((Yr(0,o+i,e.length),e.substr(0,o+i)),n)}else l=Az(e,n);switch(c){case 71:return l=uv(e,o,z(B(ze,1),Se,2,6,[mYe,vYe]),n),r.e=l,!0;case 77:return NDn(e,n,r,l,o);case 76:return DDn(e,n,r,l,o);case 69:return _Cn(e,n,o,r);case 99:return LCn(e,n,o,r);case 97:return l=uv(e,o,z(B(ze,1),Se,2,6,["AM","PM"]),n),r.b=l,!0;case 121:return IDn(e,n,o,l,t,r);case 100:return l<=0?!1:(r.c=l,!0);case 83:return l<0?!1:hEn(l,o,n[0],r);case 104:l==12&&(l=0);case 75:case 72:return l<0?!1:(r.f=l,r.g=!1,!0);case 107:return l<0?!1:(r.f=l,r.g=!0,!0);case 109:return l<0?!1:(r.j=l,!0);case 115:return l<0?!1:(r.n=l,!0);case 90:if(ocn[f]&&(I=f),p=new L(e.a.b);p.a=l){at(q.b>0),q.a.Xb(q.c=--q.b);break}else I.a>f&&(i?(Er(i.b,I.b),i.a=k.Math.max(i.a,I.a),As(q)):(Ce(I.b,b),I.c=k.Math.min(I.c,f),I.a=k.Math.max(I.a,l),i=I));i||(i=new axe,i.c=f,i.a=l,d2(q,i),Ce(i.b,b))}for(o=e.b,h=0,R=new L(t);R.a1;){if(r=SNn(n),p=c.g,A=u(ye(n,Bx),104),O=ne(re(ye(n,UH))),(!n.a&&(n.a=new pe(Jt,n,10,11)),n.a).i>1&&ne(re(ye(n,(Yh(),Hre))))!=Ki&&(c.c+(A.b+A.c))/(c.b+(A.d+A.a))1&&ne(re(ye(n,(Yh(),Jre))))!=Ki&&(c.c+(A.b+A.c))/(c.b+(A.d+A.a))>O&&ji(r,(Yh(),Mm),k.Math.max(ne(re(ye(n,Rx))),ne(re(ye(r,Mm)))-ne(re(ye(n,Jre))))),S=new Ose(i,b),f=QVe(S,r,y),h=f.g,h>=p&&h==h){for(o=0;o<(!r.a&&(r.a=new pe(Jt,r,10,11)),r.a).i;o++)Sqe(e,u(K((!r.a&&(r.a=new pe(Jt,r,10,11)),r.a),o),26),u(K((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a),o),26));QRe(n,S),p4n(c,f.c),w4n(c,f.b)}--l}ji(n,(Yh(),P7),c.b),ji(n,Py,c.c),t.Ug()}function wRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn;for(n.Tg("Compound graph postprocessor",1),t=Re($e(C(e,(Ne(),Jie)))),l=u(C(e,(me(),qve)),229),b=new ar,R=l.ec().Jc();R.Ob();){for(I=u(R.Pb(),17),o=new bs(l.cc(I)),jn(),Tr(o,new noe(e)),be=g7n((vn(0,o.c.length),u(o.c[0],250))),Ie=UBe(u(Le(o,o.c.length-1),250)),V=be.i,V9(Ie.i,V)?q=V.e:q=_r(V),p=tSn(I,o),qs(I.a),y=null,c=new L(o);c.aEh,tn=k.Math.abs(y.b-A.b)>Eh,(!t&&cn&&tn||t&&(cn||tn))&&Vt(I.a,te)),fc(I.a,i),i.b==0?y=te:y=(at(i.b!=0),u(i.c.b.c,8)),U7n(S,p,O),UBe(r)==Ie&&(_r(Ie.i)!=r.a&&(O=new Kr,L0e(O,_r(Ie.i),q)),he(I,jie,O)),WMn(S,I,q),b.a.yc(S,b);lc(I,be),Gr(I,Ie)}for(h=b.a.ec().Jc();h.Ob();)f=u(h.Pb(),17),lc(f,null),Gr(f,null);n.Ug()}function pRn(e,n){var t,i,r,c,o,l,f,h,b,p,y;for(r=u(C(e,(Mu(),mp)),86),b=r==(vr(),Zc)||r==ru?Za:ru,t=u(gs(si(new wn(null,new pn(e.b,16)),new b3),Cs(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),f=u(gs(So(t.Mc(),new LEe(n)),Cs(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[Yo]))),16),f.Fc(u(gs(So(t.Mc(),new PEe(n)),Cs(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[Yo]))),18)),f.gd(new $Ee(b)),y=new vd(new REe(r)),i=new pt,l=f.Jc();l.Ob();)o=u(l.Pb(),240),h=u(o.a,40),Re($e(o.c))?(y.a.yc(h,(Pn(),eb))==null,new c9(y.a.Xc(h,!1)).a.gc()>0&&ei(i,h,u(new c9(y.a.Xc(h,!1)).a.Tc(),40)),new c9(y.a.$c(h,!0)).a.gc()>1&&ei(i,nJe(y,h),h)):(new c9(y.a.Xc(h,!1)).a.gc()>0&&(c=u(new c9(y.a.Xc(h,!1)).a.Tc(),40),ue(c)===ue(du(Xc(i.f,h)))&&u(C(h,(Ci(),dre)),16).Ec(c)),new c9(y.a.$c(h,!0)).a.gc()>1&&(p=nJe(y,h),ue(du(Xc(i.f,p)))===ue(h)&&u(C(p,(Ci(),dre)),16).Ec(h)),y.a.Ac(h)!=null)}function LVe(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te;if(e.gc()==1)return u(e.Xb(0),235);if(e.gc()<=0)return new YR;for(r=e.Jc();r.Ob();){for(t=u(r.Pb(),235),A=0,b=oi,p=oi,f=Xr,h=Xr,S=new L(t.e);S.al&&(V=0,te+=o+R,o=0),qIn(O,t,V,te),n=k.Math.max(n,V+I.a),o=k.Math.max(o,I.b),V+=I.a+R;return O}function mRn(e){cge();var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;if(e==null||(c=oB(e),A=djn(c),A%4!=0))return null;if(O=A/4|0,O==0)return oe(ds,kv,30,0,15,1);for(p=null,n=0,t=0,i=0,r=0,o=0,l=0,f=0,h=0,S=0,y=0,b=0,p=oe(ds,kv,30,O*3,15,1);S>4)<<24>>24,p[y++]=((t&15)<<4|i>>2&15)<<24>>24,p[y++]=(i<<6|r)<<24>>24}return!ZC(o=c[b++])||!ZC(l=c[b++])?null:(n=rh[o],t=rh[l],f=c[b++],h=c[b++],rh[f]==-1||rh[h]==-1?f==61&&h==61?(t&15)!=0?null:(I=oe(ds,kv,30,S*3+1,15,1),Wu(p,0,I,0,S*3),I[y]=(n<<2|t>>4)<<24>>24,I):f!=61&&h==61?(i=rh[f],(i&3)!=0?null:(I=oe(ds,kv,30,S*3+2,15,1),Wu(p,0,I,0,S*3),I[y++]=(n<<2|t>>4)<<24>>24,I[y]=((t&15)<<4|i>>2&15)<<24>>24,I)):null:(i=rh[f],r=rh[h],p[y++]=(n<<2|t>>4)<<24>>24,p[y++]=((t&15)<<4|i>>2&15)<<24>>24,p[y++]=(i<<6|r)<<24>>24,p))}function vRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be;for(n.Tg(SQe,1),A=u(C(e,(Ne(),Y1)),222),r=new L(e.b);r.a=2){for(O=!0,y=new L(c.j),t=u(_(y),12),S=null;y.a0)if(i=p.gc(),h=sc(k.Math.floor((i+1)/2))-1,r=sc(k.Math.ceil((i+1)/2))-1,n.o==Ya)for(b=r;b>=h;b--)n.a[te.p]==te&&(O=u(p.Xb(b),49),A=u(O.a,9),!rf(t,O.b)&&S>e.b.e[A.p]&&(n.a[A.p]=te,n.g[te.p]=n.g[A.p],n.a[te.p]=n.g[te.p],n.f[n.g[te.p].p]=(Pn(),!!(Re(n.f[n.g[te.p].p])&te.k==(zn(),dr))),S=e.b.e[A.p]));else for(b=h;b<=r;b++)n.a[te.p]==te&&(R=u(p.Xb(b),49),I=u(R.a,9),!rf(t,R.b)&&S0&&(r=u(Le(I.c.a,fe-1),9),o=e.i[r.p],cn=k.Math.ceil(L3(e.n,r,I)),c=be.a.e-I.d.d-(o.a.e+r.o.b+r.d.a)-cn),h=Ki,fe0&&Ie.a.e.e-Ie.a.a-(Ie.b.e.e-Ie.b.a)<0,A=V.a.e.e-V.a.a-(V.b.e.e-V.b.a)<0&&Ie.a.e.e-Ie.a.a-(Ie.b.e.e-Ie.b.a)>0,S=V.a.e.e+V.b.aIe.b.e.e+Ie.a.a,te=0,!O&&!A&&(y?c+p>0?te=p:h-i>0&&(te=i):S&&(c+l>0?te=l:h-q>0&&(te=q))),be.a.e+=te,be.b&&(be.d.e+=te),!1))}function $Ve(e,n,t){var i,r,c,o,l,f,h,b,p,y;if(i=new Lf(n.Jf().a,n.Jf().b,n.Kf().a,n.Kf().b),r=new g4,e.c)for(o=new L(n.Pf());o.a0&&Or(S,(vn(t,n.c.length),u(n.c[t],25))),c=0,y=!0,R=Ks(qb(rr(S))),f=R.Jc();f.Ob();){for(l=u(f.Pb(),17),y=!1,p=l,h=0;h(vn(h,n.c.length),u(n.c[h],25)).a.c.length?Or(r,(vn(h,n.c.length),u(n.c[h],25))):z0(r,i+c,(vn(h,n.c.length),u(n.c[h],25))),p=TW(p,r);t>0&&(c+=1)}if(y){for(h=0;h(vn(h,n.c.length),u(n.c[h],25)).a.c.length?Or(r,(vn(h,n.c.length),u(n.c[h],25))):z0(r,i+c,(vn(h,n.c.length),u(n.c[h],25)));t>0&&(c+=1)}for(o=!1,O=new qn(Vn(Ni(S).a.Jc(),new ee));ht(O);){for(A=u(tt(O),17),p=A,b=t+1;b(vn(h,n.c.length),u(n.c[h],25)).a.c.length?Or(I,(vn(h,n.c.length),u(n.c[h],25))):z0(I,i+1,(vn(h,n.c.length),u(n.c[h],25))));o&&(c+=1),o=!0}return c>0?c-1:0}function q0(e,n){fi();var t,i,r,c,o,l,f,h,b,p,y,S,A;if(xj(K7)==0){for(p=oe(WBn,Se,121,jhn.length,0,1),o=0;oh&&(i.a+=HTe(oe(Wl,kh,30,-h,15,1))),i.a+="Is",lh(f,Xo(32))>=0)for(r=0;r=i.o.b/2}else q=!p;q?(R=u(C(i,(me(),Dy)),16),R?y?c=R:(r=u(C(i,xy),16),r?R.gc()<=r.gc()?c=R:c=r:(c=new Te,he(i,xy,c))):(c=new Te,he(i,Dy,c))):(r=u(C(i,(me(),xy)),16),r?p?c=r:(R=u(C(i,Dy),16),R?r.gc()<=R.gc()?c=r:c=R:(c=new Te,he(i,Dy,c))):(c=new Te,he(i,xy,c))),c.Ec(e),he(e,(me(),eH),t),n.d==t?(Gr(n,null),t.e.c.length+t.g.c.length==0&&gu(t,null),vkn(t)):(lc(n,null),t.e.c.length+t.g.c.length==0&&gu(t,null)),qs(n.a)}function SRn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Cn,Dn,ot,Qt,qi;for(t.Tg("MinWidth layering",1),S=n.b,Ie=n.a,qi=u(C(n,(Ne(),n4e)),15).a,l=u(C(n,t4e),15).a,e.b=ne(re(C(n,Vf))),e.d=Ki,te=new L(Ie);te.aS&&(c&&(gc(fe,y),gc(cn,ve(h.b-1))),Qt=t.b,qi+=y+n,y=0,b=k.Math.max(b,t.b+t.c+ot)),Os(l,Qt),Ns(l,qi),b=k.Math.max(b,Qt+ot+t.c),y=k.Math.max(y,p),Qt+=ot+n;if(b=k.Math.max(b,i),Dn=qi+y+t.a,Dn0?(h=0,I&&(h+=l),h+=(tn-1)*o,V&&(h+=l),cn&&V&&(h=k.Math.max(h,GNn(V,o,q,Ie))),h=e.a&&(i=uLn(e,q),b=k.Math.max(b,i.b),te=k.Math.max(te,i.d),Ce(l,new jc(q,i)));for(cn=new Te,h=0;h0),I.a.Xb(I.c=--I.b),tn=new Xu(e.b),d2(I,tn),at(I.b0){for(y=b<100?null:new m0(b),h=new t1e(n),A=h.g,R=oe($t,ni,30,b,15,1),i=0,te=new Nw(b),r=0;r=0;)if(S!=null?di(S,A[f]):ue(S)===ue(A[f])){R.length<=i&&(I=R,R=oe($t,ni,30,2*R.length,15,1),Wu(I,0,R,0,i)),R[i++]=r,Et(te,A[f]);break e}if(S=S,ue(S)===ue(l))break}}if(h=te,A=te.g,b=i,i>R.length&&(I=R,R=oe($t,ni,30,i,15,1),Wu(I,0,R,0,i)),i>0){for(V=!0,c=0;c=0;)V4(e,R[o]);if(i!=b){for(r=b;--r>=i;)V4(h,r);I=R,R=oe($t,ni,30,i,15,1),Wu(I,0,R,0,i)}n=h}}}else for(n=sxn(e,n),r=e.i;--r>=0;)n.Gc(e.g[r])&&(V4(e,r),V=!0);if(V){if(R!=null){for(t=n.gc(),p=t==1?dE(e,4,n.Jc().Pb(),null,R[0],O):dE(e,6,n,R,R[0],O),y=t<100?null:new m0(t),r=n.Jc();r.Ob();)S=r.Pb(),y=qle(e,u(S,75),y);y?(y.lj(p),y.mj()):ai(e.e,p)}else{for(y=p2n(n.gc()),r=n.Jc();r.Ob();)S=r.Pb(),y=qle(e,u(S,75),y);y&&y.mj()}return!0}else return!1}function TRn(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V;for(t=new UJe(n),t.a||t_n(n),h=eIn(n),f=new Cw,I=new iXe,O=new L(n.a);O.a0||t.o==Ya&&r=t}function NRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Cn;for(V=e.a,te=0,be=V.length;te0?(p=u(Le(y.c.a,o-1),9),cn=L3(e.b,y,p),I=y.n.b-y.d.d-(p.n.b+p.o.b+p.d.a+cn)):I=y.n.b-y.d.d,h=k.Math.min(I,h),o1&&(o=k.Math.min(o,k.Math.abs(u(Yu(l.a,1),8).b-b.b)))));else for(O=new L(n.j);O.ar&&(c=y.a-r,o=oi,i.c.length=0,r=y.a),y.a>=r&&(Hn(i.c,l),l.a.b>1&&(o=k.Math.min(o,k.Math.abs(u(Yu(l.a,l.a.b-2),8).b-y.b)))));if(i.c.length!=0&&c>n.o.a/2&&o>n.o.b/2){for(S=new Qu,gu(S,n),xr(S,(De(),Xn)),S.n.a=n.o.a/2,R=new Qu,gu(R,n),xr(R,bt),R.n.a=n.o.a/2,R.n.b=n.o.b,f=new L(i);f.a=h.b?lc(l,R):lc(l,S)):(h=u(x3n(l.a),8),I=l.a.b==0?_a(l.c):u(If(l.a),8),I.b>=h.b?Gr(l,R):Gr(l,S)),p=u(C(l,(Ne(),Wc)),78),p&&P2(p,h,!0);n.n.a=r-n.o.a/2}}function _Rn(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(l=St(e.b,0);l.b!=l.d.c;)if(o=u(jt(l),40),!bn(o.c,DF))for(h=sOn(o,e),n==(vr(),Zc)||n==ru?Tr(h,new j_):Tr(h,new tU),f=h.c.length,i=0;i=0?S=q4(l):S=TO(q4(l)),e.of(C7,S)),h=new Kr,y=!1,e.nf(wp)?(wle(h,u(e.mf(wp),8)),y=!0):Ywn(h,o.a/2,o.b/2),S.g){case 4:he(b,yu,(Xs(),V1)),he(b,tH,(Wb(),Ov)),b.o.b=o.b,O<0&&(b.o.a=-O),xr(p,(De(),et)),y||(h.a=o.a),h.a-=o.a;break;case 2:he(b,yu,(Xs(),yg)),he(b,tH,(Wb(),k7)),b.o.b=o.b,O<0&&(b.o.a=-O),xr(p,(De(),Kn)),y||(h.a=0);break;case 1:he(b,mg,(_1(),Dv)),b.o.a=o.a,O<0&&(b.o.b=-O),xr(p,(De(),bt)),y||(h.b=o.b),h.b-=o.b;break;case 3:he(b,mg,(_1(),Sy)),b.o.a=o.a,O<0&&(b.o.b=-O),xr(p,(De(),Xn)),y||(h.b=0)}if(wle(p.n,h),he(b,wp,h),n==Tg||n==f1||n==to){if(A=0,n==Tg&&e.nf(qd))switch(S.g){case 1:case 2:A=u(e.mf(qd),15).a;break;case 3:case 4:A=-u(e.mf(qd),15).a}else switch(S.g){case 4:case 2:A=c.b,n==f1&&(A/=r.b);break;case 1:case 3:A=c.a,n==f1&&(A/=r.a)}he(b,hp,A)}return he(b,Du,S),b}function LRn(){zoe();function e(i){var r=this;this.dispatch=function(c){var o=c.data;switch(o.cmd){case"algorithms":var l=fde((jn(),new Hr(new ct(Ng.b))));i.postMessage({id:o.id,data:l});break;case"categories":var f=fde((jn(),new Hr(new ct(Ng.c))));i.postMessage({id:o.id,data:f});break;case"options":var h=fde((jn(),new Hr(new ct(Ng.d))));i.postMessage({id:o.id,data:h});break;case"register":sPn(o.algorithms),i.postMessage({id:o.id});break;case"layout":nPn(o.graph,o.layoutOptions||{},o.options||{}),i.postMessage({id:o.id,data:o.graph});break}},this.saveDispatch=function(c){try{r.dispatch(c)}catch(o){i.postMessage({id:c.data.id,error:o})}}}function n(i){var r=this;this.dispatcher=new e({postMessage:function(c){r.onmessage({data:c})}}),this.postMessage=function(c){setTimeout(function(){r.dispatcher.saveDispatch({data:c})},0)}}if(typeof document===GZ&&typeof self!==GZ){var t=new e(self);self.onmessage=t.saveDispatch}else typeof M!==GZ&&M.exports&&(Object.defineProperty(N,"__esModule",{value:!0}),M.exports={default:n,Worker:n})}function uZ(e,n,t,i,r,c,o){var l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Cn,Dn,ot,Qt,qi;for(O=0,Cn=0,h=new L(e.b);h.aO&&(c&&(gc(fe,S),gc(cn,ve(b.b-1)),Ce(e.d,A),l.c.length=0),Qt=t.b,qi+=S+n,S=0,p=k.Math.max(p,t.b+t.c+ot)),Hn(l.c,f),zJe(f,Qt,qi),p=k.Math.max(p,Qt+ot+t.c),S=k.Math.max(S,y),Qt+=ot+n,A=f;if(Er(e.a,l),Ce(e.d,u(Le(l,l.c.length-1),167)),p=k.Math.max(p,i),Dn=qi+S+t.a,Dnr.d.d+r.d.a?b.f.d=!0:(b.f.d=!0,b.f.a=!0))),i.b!=i.d.c&&(n=t);b&&(c=u(Bn(e.f,o.d.i),60),n.bc.d.d+c.d.a?b.f.d=!0:(b.f.d=!0,b.f.a=!0))}for(l=new qn(Vn(rr(S).a.Jc(),new ee));ht(l);)o=u(tt(l),17),o.a.b!=0&&(n=u(If(o.a),8),o.d.j==(De(),Xn)&&(I=new sS(n,new je(n.a,r.d.d),r,o),I.f.a=!0,I.a=o.d,Hn(O.c,I)),o.d.j==bt&&(I=new sS(n,new je(n.a,r.d.d+r.d.a),r,o),I.f.d=!0,I.a=o.d,Hn(O.c,I)))}return O}function FRn(e,n,t){var i,r,c,o,l,f,h,b,p,y;for(f=new Te,p=n.length,o=d1e(t),h=0;h=A&&(q>A&&(S.c.length=0,A=q),Hn(S.c,o));S.c.length!=0&&(y=u(Le(S,sz(n,S.c.length)),132),Dn.a.Ac(y)!=null,y.s=O++,mbe(y,tn,fe),S.c.length=0)}for(te=e.c.length+1,l=new L(e);l.aCn.s&&(As(t),qo(Cn.i,i),i.c>0&&(i.a=Cn,Ce(Cn.t,i),i.b=Ie,Ce(Ie.i,i)))}function HVe(e,n,t,i,r){var c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Cn,Dn;for(O=new xo(n.b),te=new xo(n.b),y=new xo(n.b),cn=new xo(n.b),I=new xo(n.b),Ie=St(n,0);Ie.b!=Ie.d.c;)for(be=u(jt(Ie),12),l=new L(be.g);l.a0,R=be.g.c.length>0,h&&R?Hn(y.c,be):h?Hn(O.c,be):R&&Hn(te.c,be);for(A=new L(O);A.aq.mh()-h.b&&(y=q.mh()-h.b),S>q.nh()-h.d&&(S=q.nh()-h.d),b0){for(V=St(e.f,0);V.b!=V.d.c;)q=u(jt(V),9),q.p+=y-e.e;_0e(e),qs(e.f),Ibe(e,i,S)}else{for(Vt(e.f,S),S.p=i,e.e=k.Math.max(e.e,i),c=new qn(Vn(rr(S).a.Jc(),new ee));ht(c);)r=u(tt(c),17),!r.c.i.c&&r.c.i.k==(zn(),Uu)&&(Vt(e.f,r.c.i),r.c.i.p=i-1);e.c=i}else _0e(e),qs(e.f),i=0,ht(new qn(Vn(rr(S).a.Jc(),new ee)))?(y=0,y=GJe(y,S),i=y+2,Ibe(e,i,S)):(Vt(e.f,S),S.p=0,e.e=k.Math.max(e.e,0),e.b=u(Le(e.d.b,0),25),e.c=0);for(e.f.b==0||_0e(e),e.d.a.c.length=0,R=new Te,h=new L(e.d.b);h.a=48&&n<=57){for(i=n-48;r=48&&n<=57;)if(i=i*10+n-48,i<0)throw $(new zt(Ht((Lt(),Z2e))))}else throw $(new zt(Ht((Lt(),DZe))));if(t=i,n==44){if(r>=e.j)throw $(new zt(Ht((Lt(),_Ze))));if((n=ic(e.i,r++))>=48&&n<=57){for(t=n-48;r=48&&n<=57;)if(t=t*10+n-48,t<0)throw $(new zt(Ht((Lt(),Z2e))));if(i>t)throw $(new zt(Ht((Lt(),LZe))))}else t=-1}if(n!=125)throw $(new zt(Ht((Lt(),IZe))));e._l(r)?(c=(fi(),fi(),new A2(9,c)),e.d=r+1):(c=(fi(),fi(),new A2(3,c)),e.d=r),c.Mm(i),c.Lm(t),li(e)}}return c}function XRn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be;for(r=1,S=new Te,i=0;i=u(Le(e.b,i),25).a.c.length/4)continue}if(u(Le(e.b,i),25).a.c.length>n){for(te=new Te,Ce(te,u(Le(e.b,i),25)),o=0;o1)for(A=new p4((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a));A.e!=A.i.gc();)KE(A);for(o=u(K((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),0),170),I=Qt,Qt>be+te?I=be+te:Qtfe+O?R=fe+O:qibe-te&&Ife-O&&RQt+ot?cn=Qt+ot:beqi+Ie?tn=qi+Ie:feQt-ot&&cnqi-Ie&&tnt&&(y=t-1),S=i0+Is(n,24)*vN*p-p/2,S<0?S=1:S>i&&(S=i-1),r=(v0(),f=new Fk,f),bB(r,y),gB(r,S),Et((!o.a&&(o.a=new mr(kl,o,5)),o.a),r)}function lZ(e,n){XW();var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie;if(V=e.e,b=e.d,r=e.a,V==0)switch(n){case 0:return"0";case 1:return R8;case 2:return"0.00";case 3:return"0.000";case 4:return"0.0000";case 5:return"0.00000";case 6:return"0.000000";default:return R=new p0,R.a+="0E",R.a+=-n,R.a}if(O=b*10+1+7,I=oe(Wl,kh,30,O+1,15,1),t=O,b==1)if(c=r[0],c<0){Ie=Rr(c,Ic);do p=Ie,Ie=BO(Ie,10),I[--t]=48+Rt(lf(p,ac(Ie,10)))&yr;while(ao(Ie,0)!=0)}else{Ie=c;do p=Ie,Ie=Ie/10|0,I[--t]=48+(p-Ie*10)&yr;while(Ie!=0)}else{te=oe($t,ni,30,b,15,1),fe=b,Wu(r,0,te,0,fe);e:for(;;){for(q=0,l=fe-1;l>=0;l--)be=mc(Gh(q,32),Rr(te[l],Ic)),S=ZAn(be),te[l]=Rt(S),q=Rt(kw(S,32));A=Rt(q),y=t;do I[--t]=48+A%10&yr;while((A=A/10|0)!=0&&t!=0);for(i=9-y+t,o=0;o0;o++)I[--t]=48;for(f=fe-1;te[f]==0;f--)if(f==0)break e;fe=f+1}for(;I[t]==48;)++t}return h=V<0,h&&(I[--t]=45),gh(I,t,O-t)}function XVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;switch(e.c=n,e.g=new pt,t=(_b(),new w0(e.c)),i=new CP(t),ode(i),V=Pt(ye(e.c,(FO(),q6e))),f=u(ye(e.c,rce),330),be=u(ye(e.c,cce),427),o=u(ye(e.c,J6e),477),te=u(ye(e.c,ice),428),e.j=ne(re(ye(e.c,Gln))),l=e.a,f.g){case 0:l=e.a;break;case 1:l=e.b;break;case 2:l=e.i;break;case 3:l=e.e;break;case 4:l=e.f;break;default:throw $(new Gn($F+(f.f!=null?f.f:""+f.g)))}if(e.d=new O_e(l,be,o),he(e.d,(Q9(),QS),$e(ye(e.c,Jln))),e.d.c=Re($e(ye(e.c,H6e))),CR(e.c).i==0)return e.d;for(p=new ut(CR(e.c));p.e!=p.i.gc();){for(b=u(ft(p),26),S=b.g/2,y=b.f/2,fe=new je(b.i+S,b.j+y);so(e.g,fe);)a2(fe,(k.Math.random()-.5)*Eh,(k.Math.random()-.5)*Eh);O=u(ye(b,(Xt(),R7)),140),I=new U_e(fe,new Lf(fe.a-S-e.j/2-O.b,fe.b-y-e.j/2-O.d,b.g+e.j+(O.b+O.c),b.f+e.j+(O.d+O.a))),Ce(e.d.i,I),ei(e.g,fe,new jc(I,b))}switch(te.g){case 0:if(V==null)e.d.d=u(Le(e.d.i,0),68);else for(q=new L(e.d.i);q.a0?ot+1:1);for(o=new L(fe.g);o.a0?ot+1:1)}e.d[h]==0?Vt(e.f,O):e.a[h]==0&&Vt(e.g,O),++h}for(A=-1,S=1,p=new Te,e.e=u(C(n,(me(),Oy)),234);jl>0;){for(;e.f.b!=0;)qi=u(eV(e.f),9),e.c[qi.p]=A--,Ybe(e,qi),--jl;for(;e.g.b!=0;)Es=u(eV(e.g),9),e.c[Es.p]=S++,Ybe(e,Es),--jl;if(jl>0){for(y=Xr,q=new L(V);q.a=y&&(te>y&&(p.c.length=0,y=te),Hn(p.c,O)));b=e.qg(p),e.c[b.p]=S++,Ybe(e,b),--jl}}for(Qt=V.c.length+1,h=0;he.c[eu]&&($d(i,!0),he(n,Ay,(Pn(),!0)));e.a=null,e.d=null,e.c=null,qs(e.g),qs(e.f),t.Ug()}function VVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;for(be=u(K((!e.a&&(e.a=new pe(Pi,e,6,6)),e.a),0),170),b=new xs,te=new pt,fe=sKe(be),Ko(te.f,be,fe),y=new pt,i=new Si,A=qh(Rl(z(B(Xl,1),On,20,0,[(!n.d&&(n.d=new Nn(pr,n,8,5)),n.d),(!n.e&&(n.e=new Nn(pr,n,7,4)),n.e)])));ht(A);){if(S=u(tt(A),85),(!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i!=1)throw $(new Gn(zWe+(!e.a&&(e.a=new pe(Pi,e,6,6)),e.a).i));S!=e&&(I=u(K((!S.a&&(S.a=new pe(Pi,S,6,6)),S.a),0),170),Xi(i,I,i.c.b,i.c),O=u(du(Xc(te.f,I)),13),O||(O=sKe(I),Ko(te.f,I,O)),p=t?Nr(new wc(u(Le(fe,fe.c.length-1),8)),u(Le(O,O.c.length-1),8)):Nr(new wc((vn(0,fe.c.length),u(fe.c[0],8))),(vn(0,O.c.length),u(O.c[0],8))),Ko(y.f,I,p))}if(i.b!=0)for(R=u(Le(fe,t?fe.c.length-1:0),8),h=1;h1&&Xi(b,R,b.c.b,b.c),TY(r)));R=q}return b}function YVe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Cn;for(t.Tg(QQe,1),Cn=u(gs(si(new wn(null,new pn(n,16)),new S_),Cs(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),b=u(gs(si(new wn(null,new pn(n,16)),new zEe(n)),Cs(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[Yo]))),16),A=u(gs(si(new wn(null,new pn(n,16)),new BEe(n)),Cs(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[Yo]))),16),O=oe(_H,IF,40,n.gc(),0,1),o=0;o=0&&tn=0&&!O[S]){O[S]=r,b.ed(l),--l;break}if(S=tn-y,S=0&&!O[S]){O[S]=r,b.ed(l),--l;break}}for(A.gd(new CM),f=O.length-1;f>=0;f--)!O[f]&&!A.dc()&&(O[f]=u(A.Xb(0),40),A.ed(0));for(h=0;hy&&$O((vn(y,n.c.length),u(n.c[y],186)),b),b=null;n.c.length>y&&(vn(y,n.c.length),u(n.c[y],186)).a.c.length==0;)qo(n,(vn(y,n.c.length),n.c[y]));if(!b){--o;continue}if(!Re($e(u(Le(b.b,0),26).mf((Ja(),AD))))&&g_n(n,A,c,b,I,t,y,i)){O=!0;continue}if(I){if(S=A.b,p=b.f,!Re($e(u(Le(b.b,0),26).mf(AD)))&&PPn(n,A,c,b,t,y,i,r)){if(O=!0,S=e.j){e.a=-1,e.c=1;return}if(n=ic(e.i,e.d++),e.a=n,e.b==1){switch(n){case 92:if(i=10,e.d>=e.j)throw $(new zt(Ht((Lt(),qF))));e.a=ic(e.i,e.d++);break;case 45:(e.e&512)==512&&e.d=e.j||ic(e.i,e.d)!=63)break;if(++e.d>=e.j)throw $(new zt(Ht((Lt(),One))));switch(n=ic(e.i,e.d++),n){case 58:i=13;break;case 61:i=14;break;case 33:i=15;break;case 91:i=19;break;case 62:i=18;break;case 60:if(e.d>=e.j)throw $(new zt(Ht((Lt(),One))));if(n=ic(e.i,e.d++),n==61)i=16;else if(n==33)i=17;else throw $(new zt(Ht((Lt(),bZe))));break;case 35:for(;e.d=e.j)throw $(new zt(Ht((Lt(),qF))));e.a=ic(e.i,e.d++);break;default:i=0}e.c=i}function iBn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I;if(t.Tg("Process compaction",1),!!Re($e(C(n,(Mu(),Sye))))){for(r=u(C(n,mp),86),S=ne(re(C(n,kre))),CLn(e,n,r),pRn(n,S/2/2),A=n.b,Vb(A,new DEe(r)),h=St(A,0);h.b!=h.d.c;)if(f=u(jt(h),40),!Re($e(C(f,(Ci(),fb))))){if(i=nIn(f,r),O=Y_n(f,n),p=0,y=0,i)switch(I=i.e,r.g){case 2:p=I.a-S-f.f.a,O.e.a-S-f.f.ap&&(p=O.e.a+O.f.a+S),y=p+f.f.a;break;case 4:p=I.b-S-f.f.b,O.e.b-S-f.f.bp&&(p=O.e.b+O.f.b+S),y=p+f.f.b}else if(O)switch(r.g){case 2:p=O.e.a-S-f.f.a,y=p+f.f.a;break;case 1:p=O.e.a+O.f.a+S,y=p+f.f.a;break;case 4:p=O.e.b-S-f.f.b,y=p+f.f.b;break;case 3:p=O.e.b+O.f.b+S,y=p+f.f.b}ue(C(n,yre))===ue((NE(),yD))?(c=p,o=y,l=R1(si(new wn(null,new pn(e.a,16)),new bCe(c,o))),l.a!=null?r==(vr(),Zc)||r==ru?f.e.a=p:f.e.b=p:(r==(vr(),Zc)||r==Vl?l=R1(si(eBe(new wn(null,new pn(e.a,16))),new IEe(c))):l=R1(si(eBe(new wn(null,new pn(e.a,16))),new _Ee(c))),l.a!=null&&(r==Zc||r==ru?f.e.a=ne(re((at(l.a!=null),u(l.a,49)).a)):f.e.b=ne(re((at(l.a!=null),u(l.a,49)).a)))),l.a!=null&&(b=wu(e.a,(at(l.a!=null),l.a),0),b>0&&b!=u(C(f,Nh),15).a&&(he(f,wye,(Pn(),!0)),he(f,Nh,ve(b))))):r==(vr(),Zc)||r==ru?f.e.a=p:f.e.b=p}t.Ug()}}function rBn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be;if(t.Tg("Coffman-Graham Layering",1),n.a.c.length==0){t.Ug();return}for(be=u(C(n,(Ne(),e4e)),15).a,f=0,o=0,y=new L(n.a);y.a=be||!vEn(R,i))&&(i=$Ie(n,b)),Or(R,i),c=new qn(Vn(rr(R).a.Jc(),new ee));ht(c);)r=u(tt(c),17),!e.a[r.p]&&(O=r.c.i,--e.e[O.p],e.e[O.p]==0&&E4(v8(S,O),B8));for(h=b.c.length-1;h>=0;--h)Ce(n.b,(vn(h,b.c.length),u(b.c[h],25)));n.a.c.length=0,t.Ug()}function WVe(e){var n,t,i,r,c,o,l,f,h;for(e.b=1,li(e),n=null,e.c==0&&e.a==94?(li(e),n=(fi(),fi(),new ul(4)),ho(n,0,l7),l=new ul(4)):l=(fi(),fi(),new ul(4)),r=!0;(h=e.c)!=1;){if(h==0&&e.a==93&&!r){n&&(dS(n,l),l=n);break}if(t=e.a,i=!1,h==10)switch(t){case 100:case 68:case 119:case 87:case 115:case 83:V2(l,C8(t)),i=!0;break;case 105:case 73:case 99:case 67:t=(V2(l,C8(t)),-1),t<0&&(i=!0);break;case 112:case 80:if(f=Q0e(e,t),!f)throw $(new zt(Ht((Lt(),Nne))));V2(l,f),i=!0;break;default:t=Lbe(e)}else if(h==24&&!r){if(n&&(dS(n,l),l=n),c=WVe(e),dS(l,c),e.c!=0||e.a!=93)throw $(new zt(Ht((Lt(),SZe))));break}if(li(e),!i){if(h==0){if(t==91)throw $(new zt(Ht((Lt(),Q2e))));if(t==93)throw $(new zt(Ht((Lt(),W2e))));if(t==45&&!r&&e.a!=93)throw $(new zt(Ht((Lt(),Dne))))}if(e.c!=0||e.a!=45||t==45&&r)ho(l,t,t);else{if(li(e),(h=e.c)==1)throw $(new zt(Ht((Lt(),UF))));if(h==0&&e.a==93)ho(l,t,t),ho(l,45,45);else{if(h==0&&e.a==93||h==24)throw $(new zt(Ht((Lt(),Dne))));if(o=e.a,h==0){if(o==91)throw $(new zt(Ht((Lt(),Q2e))));if(o==93)throw $(new zt(Ht((Lt(),W2e))));if(o==45)throw $(new zt(Ht((Lt(),Dne))))}else h==10&&(o=Lbe(e));if(li(e),t>o)throw $(new zt(Ht((Lt(),MZe))));ho(l,t,o)}}}r=!1}if(e.c==1)throw $(new zt(Ht((Lt(),UF))));return ov(l),aS(l),e.b=0,li(e),l}function ZVe(e,n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te;te=!1;do for(te=!1,c=n?new nt(e.a.b).a.gc()-2:1;n?c>=0:cu(C(I,Ti),15).a)&&(V=!1);if(V){for(f=n?c+1:c-1,l=Pae(e.a,ve(f)),o=!1,q=!0,i=!1,b=St(l,0);b.b!=b.d.c;)h=u(jt(b),9),bi(h,Ti)?h.p!=p.p&&(o=o|(n?u(C(h,Ti),15).au(C(p,Ti),15).a),q=!1):!o&&q&&h.k==(zn(),Uu)&&(i=!0,n?y=u(tt(new qn(Vn(rr(h).a.Jc(),new ee))),17).c.i:y=u(tt(new qn(Vn(Ni(h).a.Jc(),new ee))),17).d.i,y==p&&(n?t=u(tt(new qn(Vn(Ni(h).a.Jc(),new ee))),17).d.i:t=u(tt(new qn(Vn(rr(h).a.Jc(),new ee))),17).c.i,(n?u(f2(e.a,t),15).a-u(f2(e.a,y),15).a:u(f2(e.a,y),15).a-u(f2(e.a,t),15).a)<=2&&(q=!1)));if(i&&q&&(n?t=u(tt(new qn(Vn(Ni(p).a.Jc(),new ee))),17).d.i:t=u(tt(new qn(Vn(rr(p).a.Jc(),new ee))),17).c.i,(n?u(f2(e.a,t),15).a-u(f2(e.a,p),15).a:u(f2(e.a,p),15).a-u(f2(e.a,t),15).a)<=2&&t.k==(zn(),Qi)&&(q=!1)),o||q){for(O=_Ue(e,p,n);O.a.gc()!=0;)A=u(O.a.ec().Jc().Pb(),9),O.a.Ac(A)!=null,fc(O,_Ue(e,A,n));--S,te=!0}}}while(te)}function cBn(e){Ct(e.c,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#decimal"])),Ct(e.d,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#integer"])),Ct(e.e,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#boolean"])),Ct(e.f,Ut,z(B(ze,1),Se,2,6,[oc,"EBoolean",ui,"EBoolean:Object"])),Ct(e.i,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#byte"])),Ct(e.g,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#hexBinary"])),Ct(e.j,Ut,z(B(ze,1),Se,2,6,[oc,"EByte",ui,"EByte:Object"])),Ct(e.n,Ut,z(B(ze,1),Se,2,6,[oc,"EChar",ui,"EChar:Object"])),Ct(e.t,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#double"])),Ct(e.u,Ut,z(B(ze,1),Se,2,6,[oc,"EDouble",ui,"EDouble:Object"])),Ct(e.F,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#float"])),Ct(e.G,Ut,z(B(ze,1),Se,2,6,[oc,"EFloat",ui,"EFloat:Object"])),Ct(e.I,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#int"])),Ct(e.J,Ut,z(B(ze,1),Se,2,6,[oc,"EInt",ui,"EInt:Object"])),Ct(e.N,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#long"])),Ct(e.O,Ut,z(B(ze,1),Se,2,6,[oc,"ELong",ui,"ELong:Object"])),Ct(e.Z,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#short"])),Ct(e.$,Ut,z(B(ze,1),Se,2,6,[oc,"EShort",ui,"EShort:Object"])),Ct(e._,Ut,z(B(ze,1),Se,2,6,[oc,"http://www.w3.org/2001/XMLSchema#string"]))}function Ne(){Ne=Y,Bie=(Xt(),Ifn),w4e=_fn,dD=Lfn,Vf=Pfn,Bv=q9e,Sg=U9e,Em=X9e,O7=K9e,N7=V9e,zie=iG,xg=Vd,Fie=$fn,yx=W9e,yH=Jy,hD=(Ige(),Jcn),jm=Hcn,ub=Gcn,Sm=qcn,Nun=new Vr(PD,ve(0)),T7=Bcn,g4e=zcn,Ly=Fcn,x4e=dun,m4e=Kcn,v4e=Qcn,Hie=run,y4e=eun,k4e=tun,kH=pun,Gie=bun,E4e=lun,j4e=oun,S4e=aun,r4e=kcn,Lie=pcn,gH=wcn,Pie=vcn,gp=Icn,vx=_cn,Iie=qrn,X5e=Xrn,Pun=z7,$un=rG,Lun=Im,_un=B7,p4e=(G4(),Pm),new Vr(Hy,p4e),f4e=new pw(12),l4e=new Vr(o1,f4e),G5e=(z1(),H7),Y1=new Vr(j9e,G5e),vm=new Vr(Ps,0),Dun=new Vr(Ece,ve(1)),uH=new Vr($7,H8),Eg=tG,Wi=Kx,C7=Qv,Sun=DD,Th=yfn,wm=Xv,Iun=new Vr(Sce,(Pn(),!0)),pm=ID,kg=gce,jg=Cg,vH=ab,Rie=Om,H5e=(vr(),eh),pl=new Vr(Mg,H5e),bp=Vv,pH=O9e,ym=Nm,Oun=jce,d4e=H9e,h4e=(nv(),FD),new Vr(R9e,h4e),Mun=mce,Cun=vce,Tun=yce,Aun=pce,Jie=Xcn,bH=gcn,aD=bcn,kx=Ucn,yu=ocn,_y=$rn,wx=Prn,A7=krn,z5e=jrn,Oie=Arn,fD=Ern,Nie=_rn,c4e=jcn,u4e=Ecn,Z5e=ncn,mH=$cn,$ie=Acn,_ie=Yrn,s4e=Ncn,U5e=Hrn,Die=Grn,Tie=ND,o4e=Scn,sH=irn,P5e=trn,oH=nrn,Y5e=Zrn,V5e=Wrn,Q5e=ecn,M7=Yv,Wc=Kv,Gd=Sfn,Oh=bce,Rv=dce,F5e=Crn,qd=kce,hx=Efn,dH=Afn,wp=z9e,a4e=Tfn,mm=Ofn,n4e=lcn,t4e=acn,km=Fy,xie=ern,i4e=dcn,hH=zrn,aH=Brn,wH=R7,e4e=rcn,mx=Ccn,bD=Y9e,J5e=Rrn,b4e=Rcn,q5e=Frn,kun=Orn,jun=Nrn,xun=ucn,Eun=Drn,W5e=wce,px=scn,fH=Irn,u1=yrn,Mie=prn,lD=crn,Aie=urn,lH=mrn,dx=rrn,Cie=vrn,gm=wrn,gx=grn,yun=brn,Iy=orn,bx=drn,B5e=hrn,$5e=srn,R5e=frn,K5e=Qrn}function uBn(e,n,t,i,r,c,o){var l,f,h,b,p,y,S,A;return y=u(i.a,15).a,S=u(i.b,15).a,p=e.b,A=e.c,l=0,b=0,n==(vr(),Zc)||n==ru?(b=cT(_Fe(k2(So(new wn(null,new pn(t.b,16)),new T_),new xM))),p.e.b+p.f.b/2>b?(h=++S,l=ne(re(Js(p2(So(new wn(null,new pn(t.b,16)),new pCe(r,h)),new iw))))):(f=++y,l=ne(re(Js(x4(So(new wn(null,new pn(t.b,16)),new mCe(r,f)),new Dk)))))):(b=cT(_Fe(k2(So(new wn(null,new pn(t.b,16)),new k_),new I6))),p.e.a+p.f.a/2>b?(h=++S,l=ne(re(Js(p2(So(new wn(null,new pn(t.b,16)),new wCe(r,h)),new AM))))):(f=++y,l=ne(re(Js(x4(So(new wn(null,new pn(t.b,16)),new gCe(r,f)),new MM)))))),n==Zc?(gc(e.a,new je(ne(re(C(p,(Ci(),Ea))))-r,l)),gc(e.a,new je(A.e.a+A.f.a+r+c,l)),gc(e.a,new je(A.e.a+A.f.a+r+c,A.e.b+A.f.b/2)),gc(e.a,new je(A.e.a+A.f.a,A.e.b+A.f.b/2))):n==ru?(gc(e.a,new je(ne(re(C(p,(Ci(),Yf))))+r,p.e.b+p.f.b/2)),gc(e.a,new je(p.e.a+p.f.a+r,l)),gc(e.a,new je(A.e.a-r-c,l)),gc(e.a,new je(A.e.a-r-c,A.e.b+A.f.b/2)),gc(e.a,new je(A.e.a,A.e.b+A.f.b/2))):n==Vl?(gc(e.a,new je(l,ne(re(C(p,(Ci(),Ea))))-r)),gc(e.a,new je(l,A.e.b+A.f.b+r+c)),gc(e.a,new je(A.e.a+A.f.a/2,A.e.b+A.f.b+r+c)),gc(e.a,new je(A.e.a+A.f.a/2,A.e.b+A.f.b+r))):(e.a.b==0||(u(If(e.a),8).b=ne(re(C(p,(Ci(),Yf))))+r*u(o.b,15).a),gc(e.a,new je(l,ne(re(C(p,(Ci(),Yf))))+r*u(o.b,15).a)),gc(e.a,new je(l,A.e.b-r*u(o.a,15).a-c))),new jc(ve(y),ve(S))}function oBn(e){var n,t,i,r,c,o,l,f,h,b,p,y,S;if(o=!0,p=null,i=null,r=null,n=!1,S=Pan,h=null,c=null,l=0,f=NQ(e,l,U8e,X8e),f=0&&bn(e.substr(l,2),"//")?(l+=2,f=NQ(e,l,uA,oA),i=(Yr(l,f,e.length),e.substr(l,f-l)),l=f):p!=null&&(l==e.length||(Yn(l,e.length),e.charCodeAt(l)!=47))&&(o=!1,f=rle(e,Xo(35),l),f==-1&&(f=e.length),i=(Yr(l,f,e.length),e.substr(l,f-l)),l=f);if(!t&&l0&&ic(b,b.length-1)==58&&(r=b,l=f)),lo?(Ys(e,n,t),1):(Ys(e,t,n),-1)}for(q=e.f,V=0,te=q.length;V0?Ys(e,n,t):Ys(e,t,n),i;if(!bi(n,(me(),Ti))||!bi(t,Ti))return c=rW(e,n),l=rW(e,t),c>l?(Ys(e,n,t),1):(Ys(e,t,n),-1)}if(!y&&!A&&(i=nYe(e,n,t),i!=0))return i>0?Ys(e,n,t):Ys(e,t,n),i}return bi(n,(me(),Ti))&&bi(t,Ti)?(c=qw(n,t,e.c,u(C(e.c,cb),15).a),l=qw(t,n,e.c,u(C(e.c,cb),15).a),c>l?(Ys(e,n,t),1):(Ys(e,t,n),-1)):(Ys(e,t,n),-1)}function eYe(){eYe=Y,sZ(),Wt=new Cw,gn(Wt,(De(),na),th),gn(Wt,mf,th),gn(Wt,ks,th),gn(Wt,ta,th),gn(Wt,es,th),gn(Wt,js,th),gn(Wt,ta,na),gn(Wt,th,Yl),gn(Wt,na,Yl),gn(Wt,mf,Yl),gn(Wt,ks,Yl),gn(Wt,Zo,Yl),gn(Wt,ta,Yl),gn(Wt,es,Yl),gn(Wt,js,Yl),gn(Wt,zo,Yl),gn(Wt,th,vl),gn(Wt,na,vl),gn(Wt,Yl,vl),gn(Wt,mf,vl),gn(Wt,ks,vl),gn(Wt,Zo,vl),gn(Wt,ta,vl),gn(Wt,zo,vl),gn(Wt,yl,vl),gn(Wt,es,vl),gn(Wt,hs,vl),gn(Wt,js,vl),gn(Wt,na,mf),gn(Wt,ks,mf),gn(Wt,ta,mf),gn(Wt,js,mf),gn(Wt,na,ks),gn(Wt,mf,ks),gn(Wt,ta,ks),gn(Wt,ks,ks),gn(Wt,es,ks),gn(Wt,th,Ql),gn(Wt,na,Ql),gn(Wt,Yl,Ql),gn(Wt,vl,Ql),gn(Wt,mf,Ql),gn(Wt,ks,Ql),gn(Wt,Zo,Ql),gn(Wt,ta,Ql),gn(Wt,yl,Ql),gn(Wt,zo,Ql),gn(Wt,js,Ql),gn(Wt,es,Ql),gn(Wt,mo,Ql),gn(Wt,th,yl),gn(Wt,na,yl),gn(Wt,Yl,yl),gn(Wt,mf,yl),gn(Wt,ks,yl),gn(Wt,Zo,yl),gn(Wt,ta,yl),gn(Wt,zo,yl),gn(Wt,js,yl),gn(Wt,hs,yl),gn(Wt,mo,yl),gn(Wt,na,zo),gn(Wt,mf,zo),gn(Wt,ks,zo),gn(Wt,ta,zo),gn(Wt,yl,zo),gn(Wt,js,zo),gn(Wt,es,zo),gn(Wt,th,Wo),gn(Wt,na,Wo),gn(Wt,Yl,Wo),gn(Wt,mf,Wo),gn(Wt,ks,Wo),gn(Wt,Zo,Wo),gn(Wt,ta,Wo),gn(Wt,zo,Wo),gn(Wt,js,Wo),gn(Wt,na,es),gn(Wt,Yl,es),gn(Wt,vl,es),gn(Wt,ks,es),gn(Wt,th,hs),gn(Wt,na,hs),gn(Wt,vl,hs),gn(Wt,mf,hs),gn(Wt,ks,hs),gn(Wt,Zo,hs),gn(Wt,ta,hs),gn(Wt,ta,mo),gn(Wt,ks,mo),gn(Wt,zo,th),gn(Wt,zo,mf),gn(Wt,zo,Yl),gn(Wt,Zo,th),gn(Wt,Zo,na),gn(Wt,Zo,vl)}function sBn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;switch(t.Tg("Brandes & Koepf node placement",1),e.a=n,e.c=F_n(n),i=u(C(n,(Ne(),$ie)),282),S=Re($e(C(n,mx))),e.d=i==(zO(),YJ)&&!S||i==sie,_Pn(e,n),be=null,fe=null,R=null,q=null,I=(ll(4,Q2),new xo(4)),u(C(n,$ie),282).g){case 3:R=new lv(n,e.c.d,(Da(),Ag),(ah(),Ud)),Hn(I.c,R);break;case 1:q=new lv(n,e.c.d,(Da(),Ya),(ah(),Ud)),Hn(I.c,q);break;case 4:be=new lv(n,e.c.d,(Da(),Ag),(ah(),pp)),Hn(I.c,be);break;case 2:fe=new lv(n,e.c.d,(Da(),Ya),(ah(),pp)),Hn(I.c,fe);break;default:R=new lv(n,e.c.d,(Da(),Ag),(ah(),Ud)),q=new lv(n,e.c.d,Ya,Ud),be=new lv(n,e.c.d,Ag,pp),fe=new lv(n,e.c.d,Ya,pp),Hn(I.c,be),Hn(I.c,fe),Hn(I.c,R),Hn(I.c,q)}for(r=new lCe(n,e.c),l=new L(I);l.axW(c))&&(p=c);for(!p&&(p=(vn(0,I.c.length),u(I.c[0],185))),O=new L(n.b);O.a0?(Ys(e,t,n),1):(Ys(e,n,t),-1);if(b&&V)return Ys(e,t,n),1;if(p&&q)return Ys(e,n,t),-1;if(p&&V)return 0}else for(tn=new L(h.j);tn.ap&&(Dn=0,ot+=b+Ie,b=0),XXe(be,o,Dn,ot),n=k.Math.max(n,Dn+fe.a),b=k.Math.max(b,fe.b),Dn+=fe.a+Ie;for(te=new pt,t=new pt,tn=new L(e);tn.a=-1900?1:0,t>=4?Kt(e,z(B(ze,1),Se,2,6,[mYe,vYe])[l]):Kt(e,z(B(ze,1),Se,2,6,["BC","AD"])[l]);break;case 121:VEn(e,t,i);break;case 77:GIn(e,t,i);break;case 107:f=r.q.getHours(),f==0?Kh(e,24,t):Kh(e,f,t);break;case 83:uNn(e,t,r);break;case 69:b=i.q.getDay(),t==5?Kt(e,z(B(ze,1),Se,2,6,["S","M","T","W","T","F","S"])[b]):t==4?Kt(e,z(B(ze,1),Se,2,6,[TZ,OZ,NZ,DZ,IZ,_Z,LZ])[b]):Kt(e,z(B(ze,1),Se,2,6,["Sun","Mon","Tue","Wed","Thu","Fri","Sat"])[b]);break;case 97:r.q.getHours()>=12&&r.q.getHours()<24?Kt(e,z(B(ze,1),Se,2,6,["AM","PM"])[1]):Kt(e,z(B(ze,1),Se,2,6,["AM","PM"])[0]);break;case 104:p=r.q.getHours()%12,p==0?Kh(e,12,t):Kh(e,p,t);break;case 75:y=r.q.getHours()%12,Kh(e,y,t);break;case 72:S=r.q.getHours(),Kh(e,S,t);break;case 99:A=i.q.getDay(),t==5?Kt(e,z(B(ze,1),Se,2,6,["S","M","T","W","T","F","S"])[A]):t==4?Kt(e,z(B(ze,1),Se,2,6,[TZ,OZ,NZ,DZ,IZ,_Z,LZ])[A]):t==3?Kt(e,z(B(ze,1),Se,2,6,["Sun","Mon","Tue","Wed","Thu","Fri","Sat"])[A]):Kh(e,A,1);break;case 76:O=i.q.getMonth(),t==5?Kt(e,z(B(ze,1),Se,2,6,["J","F","M","A","M","J","J","A","S","O","N","D"])[O]):t==4?Kt(e,z(B(ze,1),Se,2,6,[mZ,vZ,yZ,kZ,uy,jZ,EZ,SZ,xZ,AZ,MZ,CZ])[O]):t==3?Kt(e,z(B(ze,1),Se,2,6,["Jan","Feb","Mar","Apr",uy,"Jun","Jul","Aug","Sep","Oct","Nov","Dec"])[O]):Kh(e,O+1,t);break;case 81:I=i.q.getMonth()/3|0,t<4?Kt(e,z(B(ze,1),Se,2,6,["Q1","Q2","Q3","Q4"])[I]):Kt(e,z(B(ze,1),Se,2,6,["1st quarter","2nd quarter","3rd quarter","4th quarter"])[I]);break;case 100:R=i.q.getDate(),Kh(e,R,t);break;case 109:h=r.q.getMinutes(),Kh(e,h,t);break;case 115:o=r.q.getSeconds(),Kh(e,o,t);break;case 122:t<4?Kt(e,c.c[0]):Kt(e,c.c[1]);break;case 118:Kt(e,c.b);break;case 90:t<3?Kt(e,lTn(c)):t==3?Kt(e,dTn(c)):Kt(e,bTn(c.a));break;default:return!1}return!0}function Dge(e,n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Cn,Dn,ot,Qt;if(LXe(n),f=u(K((!n.b&&(n.b=new Nn(vt,n,4,7)),n.b),0),84),b=u(K((!n.c&&(n.c=new Nn(vt,n,5,8)),n.c),0),84),l=iu(f),h=iu(b),o=(!n.a&&(n.a=new pe(Pi,n,6,6)),n.a).i==0?null:u(K((!n.a&&(n.a=new pe(Pi,n,6,6)),n.a),0),170),Ie=u(Bn(e.a,l),9),Dn=u(Bn(e.a,h),9),cn=null,ot=null,X(f,193)&&(fe=u(Bn(e.a,f),246),X(fe,12)?cn=u(fe,12):X(fe,9)&&(Ie=u(fe,9),cn=u(Le(Ie.j,0),12))),X(b,193)&&(Cn=u(Bn(e.a,b),246),X(Cn,12)?ot=u(Cn,12):X(Cn,9)&&(Dn=u(Cn,9),ot=u(Le(Dn.j,0),12))),!Ie||!Dn)throw $(new u4("The source or the target of edge "+n+" could not be found. This usually happens when an edge connects a node laid out by ELK Layered to a node in another level of hierarchy laid out by either another instance of ELK Layered or another layout algorithm alltogether. The former can be solved by setting the hierarchyHandling option to INCLUDE_CHILDREN."));for(O=new Mw,$u(O,n),he(O,(me(),wi),n),he(O,(Ne(),Wc),null),S=u(C(i,po),22),Ie==Dn&&S.Ec((Dc(),ux)),cn||(be=(Nc(),Do),tn=null,o&&D3(u(C(Ie,Wi),102))&&(tn=new je(o.j,o.k),fPe(tn,j2(n)),$Pe(tn,t),T2(h,l)&&(be=ys,gi(tn,Ie.n))),cn=RKe(Ie,tn,be,i)),ot||(be=(Nc(),ys),Qt=null,o&&D3(u(C(Dn,Wi),102))&&(Qt=new je(o.b,o.c),fPe(Qt,j2(n)),$Pe(Qt,t)),ot=RKe(Dn,Qt,be,_r(Dn))),lc(O,cn),Gr(O,ot),(cn.e.c.length>1||cn.g.c.length>1||ot.e.c.length>1||ot.g.c.length>1)&&S.Ec((Dc(),cx)),y=new ut((!n.n&&(n.n=new pe(ju,n,1,7)),n.n));y.e!=y.i.gc();)if(p=u(ft(y),157),!Re($e(ye(p,Eg)))&&p.a)switch(I=fQ(p),Ce(O.b,I),u(C(I,Oh),279).g){case 1:case 2:S.Ec((Dc(),E7));break;case 0:S.Ec((Dc(),j7)),he(I,Oh,($a(),F7))}if(c=u(C(i,wx),301),R=u(C(i,mH),328),r=c==(BE(),eD)||R==(HE(),Wie),o&&(!o.a&&(o.a=new mr(kl,o,5)),o.a).i!=0&&r){for(q=lCn(o),A=new xs,te=St(q,0);te.b!=te.d.c;)V=u(jt(te),8),Vt(A,new wc(V));he(O,Kve,A)}return O}function hBn(e,n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Cn,Dn,ot,Qt,qi;for(tn=0,Cn=0,Ie=new pt,be=u(Js(p2(So(new wn(null,new pn(e.b,16)),new y_),new Nk)),15).a+1,cn=oe($t,ni,30,be,15,1),I=oe($t,ni,30,be,15,1),O=0;O1)for(l=ot+1;lh.b.e.b*(1-R)+h.c.e.b*R));A++);if(fe.gc()>0&&(Qt=h.a.b==0?pc(h.b.e):u(If(h.a),8),V=gi(pc(u(fe.Xb(fe.gc()-1),40).e),u(fe.Xb(fe.gc()-1),40).f),y=gi(pc(u(fe.Xb(0),40).e),u(fe.Xb(0),40).f),A>=fe.gc()-1&&Qt.b>V.b&&h.c.e.b>V.b||A<=0&&Qt.bh.b.e.a*(1-R)+h.c.e.a*R));A++);if(fe.gc()>0&&(Qt=h.a.b==0?pc(h.b.e):u(If(h.a),8),V=gi(pc(u(fe.Xb(fe.gc()-1),40).e),u(fe.Xb(fe.gc()-1),40).f),y=gi(pc(u(fe.Xb(0),40).e),u(fe.Xb(0),40).f),A>=fe.gc()-1&&Qt.a>V.a&&h.c.e.a>V.a||A<=0&&Qt.a=ne(re(C(e,(Ci(),vye))))&&++Cn):(S.f&&S.d.e.a<=ne(re(C(e,(Ci(),wre))))&&++tn,S.g&&S.c.e.a+S.c.f.a>=ne(re(C(e,(Ci(),mye))))&&++Cn)}else te==0?K0e(h):te<0&&(++cn[ot],++I[qi],Dn=uBn(h,n,e,new jc(ve(tn),ve(Cn)),t,i,new jc(ve(I[qi]),ve(cn[ot]))),tn=u(Dn.a,15).a,Cn=u(Dn.b,15).a)}function dBn(e){e.gb||(e.gb=!0,e.b=Au(e,0),Vi(e.b,18),Ii(e.b,19),e.a=Au(e,1),Vi(e.a,1),Ii(e.a,2),Ii(e.a,3),Ii(e.a,4),Ii(e.a,5),e.o=Au(e,2),Vi(e.o,8),Vi(e.o,9),Ii(e.o,10),Ii(e.o,11),Ii(e.o,12),Ii(e.o,13),Ii(e.o,14),Ii(e.o,15),Ii(e.o,16),Ii(e.o,17),Ii(e.o,18),Ii(e.o,19),Ii(e.o,20),Ii(e.o,21),Ii(e.o,22),Ii(e.o,23),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),Yc(e.o),e.p=Au(e,3),Vi(e.p,2),Vi(e.p,3),Vi(e.p,4),Vi(e.p,5),Ii(e.p,6),Ii(e.p,7),Yc(e.p),Yc(e.p),e.q=Au(e,4),Vi(e.q,8),e.v=Au(e,5),Ii(e.v,9),Yc(e.v),Yc(e.v),Yc(e.v),e.w=Au(e,6),Vi(e.w,2),Vi(e.w,3),Vi(e.w,4),Ii(e.w,5),e.B=Au(e,7),Ii(e.B,1),Yc(e.B),Yc(e.B),Yc(e.B),e.Q=Au(e,8),Ii(e.Q,0),Yc(e.Q),e.R=Au(e,9),Vi(e.R,1),e.S=Au(e,10),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),Yc(e.S),e.T=Au(e,11),Ii(e.T,10),Ii(e.T,11),Ii(e.T,12),Ii(e.T,13),Ii(e.T,14),Yc(e.T),Yc(e.T),e.U=Au(e,12),Vi(e.U,2),Vi(e.U,3),Ii(e.U,4),Ii(e.U,5),Ii(e.U,6),Ii(e.U,7),Yc(e.U),e.V=Au(e,13),Ii(e.V,10),e.W=Au(e,14),Vi(e.W,18),Vi(e.W,19),Vi(e.W,20),Ii(e.W,21),Ii(e.W,22),Ii(e.W,23),e.bb=Au(e,15),Vi(e.bb,10),Vi(e.bb,11),Vi(e.bb,12),Vi(e.bb,13),Vi(e.bb,14),Vi(e.bb,15),Vi(e.bb,16),Ii(e.bb,17),Yc(e.bb),Yc(e.bb),e.eb=Au(e,16),Vi(e.eb,2),Vi(e.eb,3),Vi(e.eb,4),Vi(e.eb,5),Vi(e.eb,6),Vi(e.eb,7),Ii(e.eb,8),Ii(e.eb,9),e.ab=Au(e,17),Vi(e.ab,0),Vi(e.ab,1),e.H=Au(e,18),Ii(e.H,0),Ii(e.H,1),Ii(e.H,2),Ii(e.H,3),Ii(e.H,4),Ii(e.H,5),Yc(e.H),e.db=Au(e,19),Ii(e.db,2),e.c=ci(e,20),e.d=ci(e,21),e.e=ci(e,22),e.f=ci(e,23),e.i=ci(e,24),e.g=ci(e,25),e.j=ci(e,26),e.k=ci(e,27),e.n=ci(e,28),e.r=ci(e,29),e.s=ci(e,30),e.t=ci(e,31),e.u=ci(e,32),e.fb=ci(e,33),e.A=ci(e,34),e.C=ci(e,35),e.D=ci(e,36),e.F=ci(e,37),e.G=ci(e,38),e.I=ci(e,39),e.J=ci(e,40),e.L=ci(e,41),e.M=ci(e,42),e.N=ci(e,43),e.O=ci(e,44),e.P=ci(e,45),e.X=ci(e,46),e.Y=ci(e,47),e.Z=ci(e,48),e.$=ci(e,49),e._=ci(e,50),e.cb=ci(e,51),e.K=ci(e,52))}function bBn(e,n,t,i){var r,c,o,l,f,h,b,p,y,S,A;for(p=St(e.b,0);p.b!=p.d.c;)if(b=u(jt(p),40),!bn(b.c,DF))for(c=u(gs(new wn(null,new pn(MTn(b,e),16)),Cs(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),16),n==(vr(),Zc)||n==ru?c.gd(new A_):c.gd(new M_),A=c.gc(),r=0;r0&&(l=u(If(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(If(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&k.Math.abs(f-S)/(k.Math.abs(l-y)/40)>50&&(S>f?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a+i/5.3,b.e.b+b.f.b*o-i/2)):gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a+i/5.3,b.e.b+b.f.b*o+i/2)))),gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a,b.e.b+b.f.b*o))):n==ru?(h=ne(re(C(b,(Ci(),Ea)))),b.e.a-i>h?gc(u(c.Xb(r),65).a,new je(h-t,b.e.b+b.f.b*o)):u(c.Xb(r),65).a.b>0&&(l=u(If(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(If(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&k.Math.abs(f-S)/(k.Math.abs(l-y)/40)>50&&(S>f?gc(u(c.Xb(r),65).a,new je(b.e.a-i/5.3,b.e.b+b.f.b*o-i/2)):gc(u(c.Xb(r),65).a,new je(b.e.a-i/5.3,b.e.b+b.f.b*o+i/2)))),gc(u(c.Xb(r),65).a,new je(b.e.a,b.e.b+b.f.b*o))):n==Vl?(h=ne(re(C(b,(Ci(),Yf)))),b.e.b+b.f.b+i0&&(l=u(If(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(If(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&k.Math.abs(l-y)/(k.Math.abs(f-S)/40)>50&&(y>l?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o-i/2,b.e.b+i/5.3+b.f.b)):gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o+i/2,b.e.b+i/5.3+b.f.b)))),gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o,b.e.b+b.f.b))):(h=ne(re(C(b,(Ci(),Ea)))),Bze(u(c.Xb(r),65),e)?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o,u(If(u(c.Xb(r),65).a),8).b)):b.e.b-i>h?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o,h-t)):u(c.Xb(r),65).a.b>0&&(l=u(If(u(c.Xb(r),65).a),8).a,y=b.e.a+b.f.a/2,f=u(If(u(c.Xb(r),65).a),8).b,S=b.e.b+b.f.b/2,i>0&&k.Math.abs(l-y)/(k.Math.abs(f-S)/40)>50&&(y>l?gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o-i/2,b.e.b-i/5.3)):gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o+i/2,b.e.b-i/5.3)))),gc(u(c.Xb(r),65).a,new je(b.e.a+b.f.a*o,b.e.b)))}function iYe(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe;if(o=n,y=t,so(e.a,o)){if(rf(u(Bn(e.a,o),47),y))return 1}else ei(e.a,o,new ar);if(so(e.a,y)){if(rf(u(Bn(e.a,y),47),o))return-1}else ei(e.a,y,new ar);if(so(e.e,o)){if(rf(u(Bn(e.e,o),47),y))return-1}else ei(e.e,o,new ar);if(so(e.e,y)){if(rf(u(Bn(e.a,y),47),o))return 1}else ei(e.e,y,new ar);if(o.j!=y.j)return be=iwn(o.j,y.j),be>0?Hl(e,o,y,1):Hl(e,y,o,1),be;if(fe=1,o.e.c.length!=0&&y.e.c.length!=0){if((o.j==(De(),Kn)&&y.j==Kn||o.j==Xn&&y.j==Xn||o.j==bt&&y.j==bt)&&(fe=-fe),b=u(Le(o.e,0),17).c,I=u(Le(y.e,0),17).c,f=b.i,A=I.i,f==A)for(V=new L(f.j);V.a0?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe);if(i=kFe(u(gs(mV(e.d),Cs(new Fi,new mi,new kc,z(B(Qo,1),ke,130,0,[(zl(),Yo)]))),20),f,A),i!=0)return i>0?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe);if(e.c&&(be=QJe(e,o,y),be!=0))return be>0?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe)}return o.g.c.length!=0&&y.g.c.length!=0?((o.j==(De(),Kn)&&y.j==Kn||o.j==bt&&y.j==bt)&&(fe=-fe),p=u(C(o,(me(),mie)),9),R=u(C(y,mie),9),e.f==(F1(),nre)&&p&&R&&bi(p,Ti)&&bi(R,Ti)?(l=qw(p,R,e.b,u(C(e.b,cb),15).a),S=qw(R,p,e.b,u(C(e.b,cb),15).a),l>S?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe)):e.c&&(be=QJe(e,o,y),be!=0)?be>0?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe):(h=0,O=0,bi(u(Le(o.g,0),17),Ti)&&(h=qw(u(Le(o.g,0),246),u(Le(y.g,0),246),e.b,o.g.c.length+o.e.c.length)),bi(u(Le(y.g,0),17),Ti)&&(O=qw(u(Le(y.g,0),246),u(Le(o.g,0),246),e.b,y.g.c.length+y.e.c.length)),p&&p==R||e.g&&(e.g._b(p)&&(h=u(e.g.xc(p),15).a),e.g._b(R)&&(O=u(e.g.xc(R),15).a)),h>O?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe))):o.e.c.length!=0&&y.g.c.length!=0?(Hl(e,o,y,fe),1):o.g.c.length!=0&&y.e.c.length!=0?(Hl(e,y,o,fe),-1):bi(o,(me(),Ti))&&bi(y,Ti)?(c=o.i.j.c.length,l=qw(o,y,e.b,c),S=qw(y,o,e.b,c),(o.j==(De(),Kn)&&y.j==Kn||o.j==bt&&y.j==bt)&&(fe=-fe),l>S?(Hl(e,o,y,fe),fe):(Hl(e,y,o,fe),-fe)):(Hl(e,y,o,fe),-fe)}function me(){me=Y;var e,n;wi=new yi(owe),Gve=new yi("coordinateOrigin"),yie=new yi("processors"),Hve=new Li("compoundNode",(Pn(),!1)),uD=new Li("insideConnections",!1),Kve=new yi("originalBendpoints"),Vve=new yi("originalDummyNodePosition"),Yve=new yi("originalLabelEdge"),sx=new yi("representedLabels"),ox=new yi("endLabels"),My=new yi("endLabel.origin"),Ty=new Li("labelSide",(al(),zD)),Iv=new Li("maxEdgeThickness",0),Hd=new Li("reversed",!1),Oy=new yi(tQe),ja=new Li("longEdgeSource",null),gf=new Li("longEdgeTarget",null),bm=new Li("longEdgeHasLabelDummies",!1),oD=new Li("longEdgeBeforeLabelDummy",!1),tH=new Li("edgeConstraint",(Wb(),tie)),ap=new yi("inLayerLayoutUnit"),mg=new Li("inLayerConstraint",(_1(),rD)),Cy=new Li("inLayerSuccessorConstraint",new Te),Xve=new Li("inLayerSuccessorConstraintBetweenNonDummies",!1),vs=new yi("portDummy"),nH=new Li("crossingHint",ve(0)),po=new Li("graphProperties",(n=u(sa(lie),10),new _l(n,u(_f(n,n.length),10),0))),Du=new Li("externalPortSide",(De(),ku)),Uve=new Li("externalPortSize",new Kr),gie=new yi("externalPortReplacedDummies"),iH=new yi("externalPortReplacedDummy"),K1=new Li("externalPortConnections",(e=u(sa(xc),10),new _l(e,u(_f(e,e.length),10),0))),hp=new Li(QYe,0),Jve=new yi("barycenterAssociates"),Dy=new yi("TopSideComments"),xy=new yi("BottomSideComments"),eH=new yi("CommentConnectionPort"),pie=new Li("inputCollect",!1),vie=new Li("outputCollect",!1),Ay=new Li("cyclic",!1),qve=new yi("crossHierarchyMap"),jie=new yi("targetOffset"),new Li("splineLabelSize",new Kr),Lv=new yi("spacings"),rH=new Li("partitionConstraint",!1),fp=new yi("breakingPoint.info"),Zve=new yi("splines.survivingEdge"),vg=new yi("splines.route.start"),Pv=new yi("splines.edgeChain"),Wve=new yi("originalPortConstraints"),dp=new yi("selfLoopHolder"),x7=new yi("splines.nsPortY"),Ti=new yi("modelOrder"),cb=new yi("modelOrder.maximum"),cD=new yi("modelOrderGroups.cb.number"),mie=new yi("longEdgeTargetNode"),rb=new Li(CQe,!1),_v=new Li(CQe,!1),wie=new yi("layerConstraints.hiddenNodes"),Qve=new yi("layerConstraints.opposidePort"),kie=new yi("targetNode.modelOrder"),Ny=new Li("tarjan.lowlink",ve(oi)),lx=new Li("tarjan.id",ve(-1)),cH=new Li("tarjan.onstack",!1),Qin=new Li("partOfCycle",!1),$v=new yi("medianHeuristic.weight")}function Xt(){Xt=Y;var e,n;zy=new yi(pWe),Dm=new yi(mWe),p9e=(Vh(),sce),yfn=new fn(ppe,p9e),$7=new fn(G8,null),kfn=new yi(N2e),v9e=(rg(),Mi(ace,z(B(hce,1),ke,299,0,[fce]))),ND=new fn(CF,v9e),DD=new fn(_N,(Pn(),!1)),y9e=(vr(),eh),Mg=new fn(Fee,y9e),E9e=(z1(),xce),j9e=new fn(IN,E9e),xfn=new fn(T2e,!1),x9e=(B1(),oG),Xv=new fn(MF,x9e),P9e=new pw(12),o1=new fn(nm,P9e),_D=new fn(jS,!1),wce=new fn(OF,!1),LD=new fn(ES,!1),F9e=(Br(),bb),Kx=new fn(WZ,F9e),Fy=new yi(TF),PD=new yi(EN),Ece=new yi(sF),Sce=new yi(kS),C9e=new xs,Kv=new fn(Cpe,C9e),Efn=new fn(Dpe,!1),Afn=new fn(Ipe,!1),new fn(vWe,0),T9e=new wj,R7=new fn(Lpe,T9e),tG=new fn(gpe,!1),Dfn=new fn(yWe,1),Tm=new yi(kWe),Cm=new yi(jWe),z7=new fn(SN,!1),new fn(EWe,!0),ve(0),new fn(SWe,ve(100)),new fn(xWe,!1),ve(0),new fn(AWe,ve(4e3)),ve(0),new fn(MWe,ve(400)),new fn(CWe,!1),new fn(TWe,!1),new fn(OWe,!0),new fn(NWe,!1),m9e=(KB(),Nce),jfn=new fn(O2e,m9e),M9e=(jE(),HD),Cfn=new fn(DWe,M9e),A9e=(u8(),$D),Mfn=new fn(IWe,A9e),Ifn=new fn(ipe,10),_fn=new fn(rpe,10),Lfn=new fn(cpe,20),Pfn=new fn(upe,10),q9e=new fn(QZ,2),U9e=new fn(zee,10),X9e=new fn(ope,0),iG=new fn(fpe,5),K9e=new fn(spe,1),V9e=new fn(lpe,1),Vd=new fn(em,20),$fn=new fn(ape,10),W9e=new fn(hpe,10),Jy=new yi(dpe),Q9e=new pTe,Y9e=new fn(Ppe,Q9e),Ofn=new yi(Hee),$9e=!1,Tfn=new fn(Jee,$9e),N9e=new pw(5),O9e=new fn(ype,N9e),D9e=(G2(),n=u(sa($c),10),new _l(n,u(_f(n,n.length),10),0)),Vv=new fn(U8,D9e),B9e=(nv(),db),R9e=new fn(Epe,B9e),mce=new yi(Spe),vce=new yi(xpe),yce=new yi(Ape),pce=new yi(Mpe),I9e=(e=u(sa(tA),10),new _l(e,u(_f(e,e.length),10),0)),Cg=new fn(wv,I9e),L9e=nn((_s(),q7)),ab=new fn(hy,L9e),_9e=new je(0,0),Yv=new fn(dy,_9e),Om=new fn(q8,!1),k9e=($a(),F7),bce=new fn(Ope,k9e),dce=new fn(lF,!1),ve(1),new fn(_We,null),z9e=new yi(_pe),kce=new yi(Npe),G9e=(De(),ku),Qv=new fn(wpe,G9e),Ps=new yi(bpe),J9e=(ps(),nn(gb)),Nm=new fn(X8,J9e),jce=new fn(kpe,!1),H9e=new fn(jpe,!0),ve(1),Jfn=new fn(hne,ve(3)),ve(1),Gfn=new fn(D2e,ve(4)),rG=new fn(xN,1),cG=new fn(dne,null),Im=new fn(AN,150),B7=new fn(MN,1.414),Hy=new fn(Ww,null),Rfn=new fn(I2e,1),ID=new fn(mpe,!1),gce=new fn(vpe,!1),Sfn=new fn(Tpe,1),S9e=(jz(),Mce),new fn(LWe,S9e),Nfn=!0,Hfn=(UR(),Oce),zfn=(G4(),Pm),Ffn=Pm,Bfn=Pm}function Ur(){Ur=Y,B3e=new br("DIRECTION_PREPROCESSOR",0),P3e=new br("COMMENT_PREPROCESSOR",1),Av=new br("EDGE_AND_LAYER_CONSTRAINT_EDGE_REVERSER",2),Ite=new br("INTERACTIVE_EXTERNAL_PORT_POSITIONER",3),rve=new br("PARTITION_PREPROCESSOR",4),CJ=new br("LABEL_DUMMY_INSERTER",5),RJ=new br("SELF_LOOP_PREPROCESSOR",6),fm=new br("LAYER_CONSTRAINT_PREPROCESSOR",7),tve=new br("PARTITION_MIDPROCESSOR",8),X3e=new br("HIGH_DEGREE_NODE_LAYER_PROCESSOR",9),eve=new br("NODE_PROMOTION",10),lm=new br("LAYER_CONSTRAINT_POSTPROCESSOR",11),ive=new br("PARTITION_POSTPROCESSOR",12),G3e=new br("HIERARCHICAL_PORT_CONSTRAINT_PROCESSOR",13),cve=new br("SEMI_INTERACTIVE_CROSSMIN_PROCESSOR",14),O3e=new br("BREAKING_POINT_INSERTER",15),DJ=new br("LONG_EDGE_SPLITTER",16),_te=new br("PORT_SIDE_PROCESSOR",17),AJ=new br("INVERTED_PORT_PROCESSOR",18),LJ=new br("PORT_LIST_SORTER",19),ove=new br("SORT_BY_INPUT_ORDER_OF_MODEL",20),_J=new br("NORTH_SOUTH_PORT_PREPROCESSOR",21),N3e=new br("BREAKING_POINT_PROCESSOR",22),nve=new br(yQe,23),sve=new br(kQe,24),PJ=new br("SELF_LOOP_PORT_RESTORER",25),T3e=new br("ALTERNATING_LAYER_UNZIPPER",26),uve=new br("SINGLE_EDGE_GRAPH_WRAPPER",27),MJ=new br("IN_LAYER_CONSTRAINT_PROCESSOR",28),F3e=new br("END_NODE_PORT_LABEL_MANAGEMENT_PROCESSOR",29),W3e=new br("LABEL_AND_NODE_SIZE_PROCESSOR",30),Q3e=new br("INNERMOST_NODE_MARGIN_CALCULATOR",31),BJ=new br("SELF_LOOP_ROUTER",32),_3e=new br("COMMENT_NODE_MARGIN_CALCULATOR",33),xJ=new br("END_LABEL_PREPROCESSOR",34),OJ=new br("LABEL_DUMMY_SWITCHER",35),I3e=new br("CENTER_LABEL_MANAGEMENT_PROCESSOR",36),g7=new br("LABEL_SIDE_SELECTOR",37),V3e=new br("HYPEREDGE_DUMMY_MERGER",38),q3e=new br("HIERARCHICAL_PORT_DUMMY_SIZE_PROCESSOR",39),Z3e=new br("LAYER_SIZE_AND_GRAPH_HEIGHT_CALCULATOR",40),nx=new br("HIERARCHICAL_PORT_POSITION_PROCESSOR",41),$3e=new br("CONSTRAINTS_POSTPROCESSOR",42),L3e=new br("COMMENT_POSTPROCESSOR",43),Y3e=new br("HYPERNODE_PROCESSOR",44),U3e=new br("HIERARCHICAL_PORT_ORTHOGONAL_EDGE_ROUTER",45),NJ=new br("LONG_EDGE_JOINER",46),$J=new br("SELF_LOOP_POSTPROCESSOR",47),D3e=new br("BREAKING_POINT_REMOVER",48),IJ=new br("NORTH_SOUTH_PORT_POSTPROCESSOR",49),K3e=new br("HORIZONTAL_COMPACTOR",50),TJ=new br("LABEL_DUMMY_REMOVER",51),J3e=new br("FINAL_SPLINE_BENDPOINTS_CALCULATOR",52),z3e=new br("END_LABEL_SORTER",53),Ey=new br("REVERSED_EDGE_RESTORER",54),SJ=new br("END_LABEL_POSTPROCESSOR",55),H3e=new br("HIERARCHICAL_NODE_RESIZER",56),R3e=new br("DIRECTION_POSTPROCESSOR",57)}function gBn(e,n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Cn,Dn,ot,Qt,qi,Es,eu,jl,i5,i0,ia,nd,Zl,Yy,gA,td,Ma,r0,Ig,_g,Qy,Lg,Pg,id,Gm,M7e,Sp,wA,Kce,Wy,pA,qm,mA,Vce,Ihn;for(M7e=0,Qt=n,eu=0,i0=Qt.length;eu0&&(e.a[Ma.p]=M7e++)}for(pA=0,qi=t,jl=0,ia=qi.length;jl0;){for(Ma=(at(Qy.b>0),u(Qy.a.Xb(Qy.c=--Qy.b),12)),_g=0,l=new L(Ma.e);l.a0&&(Ma.j==(De(),Xn)?(e.a[Ma.p]=pA,++pA):(e.a[Ma.p]=pA+nd+Yy,++Yy))}pA+=Yy}for(Ig=new pt,A=new zh,ot=n,Es=0,i5=ot.length;Esh.b&&(h.b=Lg)):Ma.i.c==Gm&&(Lgh.c&&(h.c=Lg));for(J9(O,0,O.length,null),Wy=oe($t,ni,30,O.length,15,1),i=oe($t,ni,30,pA+1,15,1),R=0;R0;)Ie%2>0&&(r+=Vce[Ie+1]),Ie=(Ie-1)/2|0,++Vce[Ie];for(tn=oe(kon,On,370,O.length*2,0,1),te=0;te0&&JT(Es.f),ye(R,cG)!=null&&(!R.a&&(R.a=new pe(Jt,R,10,11)),!!R.a)&&(!R.a&&(R.a=new pe(Jt,R,10,11)),R.a).i>0?(l=u(ye(R,cG),521),_g=l.Sg(R),ww(R,k.Math.max(R.g,_g.a+nd.b+nd.c),k.Math.max(R.f,_g.b+nd.d+nd.a))):(!R.a&&(R.a=new pe(Jt,R,10,11)),R.a).i!=0&&(_g=new je(ne(re(ye(R,Im))),ne(re(ye(R,Im)))/ne(re(ye(R,B7)))),ww(R,k.Math.max(R.g,_g.a+nd.b+nd.c),k.Math.max(R.f,_g.b+nd.d+nd.a)));if(ia=u(ye(n,o1),104),S=n.g-(ia.b+ia.c),y=n.f-(ia.d+ia.a),Pg.ah("Available Child Area: ("+S+"|"+y+")"),ji(n,$7,S/y),DJe(n,r,i.dh(i5)),u(ye(n,Hy),281)==dG&&(oZ(n),ww(n,ia.b+ne(re(ye(n,Tm)))+ia.c,ia.d+ne(re(ye(n,Cm)))+ia.a)),Pg.ah("Executed layout algorithm: "+Pt(ye(n,zy))+" on node "+n.k),u(ye(n,Hy),281)==Pm){if(S<0||y<0)throw $(new wd("The size defined by the parent parallel node is too small for the space provided by the paddings of the child hierarchical node. "+n.k));for(da(n,Tm)||da(n,Cm)||oZ(n),O=ne(re(ye(n,Tm))),A=ne(re(ye(n,Cm))),Pg.ah("Desired Child Area: ("+O+"|"+A+")"),Yy=S/O,gA=y/A,Zl=k.Math.min(Yy,k.Math.min(gA,ne(re(ye(n,Rfn))))),ji(n,rG,Zl),Pg.ah(n.k+" -- Local Scale Factor (X|Y): ("+Yy+"|"+gA+")"),te=u(ye(n,ND),22),c=0,o=0,Zl'?":bn(bZe,e)?"'(?<' or '(? toIndex: ",Yge=", toIndex: ",Qge="Index: ",Wge=", Size: ",z8="org.eclipse.elk.alg.common",Yt={51:1},IYe="org.eclipse.elk.alg.common.compaction",_Ye="Scanline/EventHandler",n1="org.eclipse.elk.alg.common.compaction.oned",LYe="CNode belongs to another CGroup.",PYe="ISpacingsHandler/1",qZ="The ",UZ=" instance has been finished already.",$Ye="The direction ",RYe=" is not supported by the CGraph instance.",BYe="OneDimensionalCompactor",zYe="OneDimensionalCompactor/lambda$0$Type",FYe="Quadruplet",JYe="ScanlineConstraintCalculator",HYe="ScanlineConstraintCalculator/ConstraintsScanlineHandler",GYe="ScanlineConstraintCalculator/ConstraintsScanlineHandler/lambda$0$Type",qYe="ScanlineConstraintCalculator/Timestamp",UYe="ScanlineConstraintCalculator/lambda$0$Type",jh={178:1,48:1},mS="org.eclipse.elk.alg.common.networksimplex",pa={171:1,3:1,4:1},XYe="org.eclipse.elk.alg.common.nodespacing",lg="org.eclipse.elk.alg.common.nodespacing.cellsystem",F8="CENTER",KYe={216:1,337:1},Zge={3:1,4:1,5:1,592:1},ly="LEFT",fy="RIGHT",ewe="Vertical alignment cannot be null",nwe="BOTTOM",uF="org.eclipse.elk.alg.common.nodespacing.internal",vS="UNDEFINED",Ga=.01,yN="org.eclipse.elk.alg.common.nodespacing.internal.algorithm",VYe="LabelPlacer/lambda$0$Type",YYe="LabelPlacer/lambda$1$Type",QYe="portRatioOrPosition",J8="org.eclipse.elk.alg.common.overlaps",XZ="DOWN",ay="org.eclipse.elk.alg.common.spore",Z2={3:1,4:1,5:1,198:1},WYe={3:1,6:1,4:1,5:1,90:1,110:1},KZ="org.eclipse.elk.alg.force",twe="ComponentsProcessor",ZYe="ComponentsProcessor/1",iwe="ElkGraphImporter/lambda$0$Type",Qw={214:1},gv="org.eclipse.elk.core",kN="org.eclipse.elk.graph.properties",eQe="IPropertyHolder",jN="org.eclipse.elk.alg.force.graph",nQe="Component Layout",rwe="org.eclipse.elk.alg.force.model",vu="org.eclipse.elk.core.data",oF="org.eclipse.elk.force.model",cwe="org.eclipse.elk.force.iterations",uwe="org.eclipse.elk.force.repulsivePower",VZ="org.eclipse.elk.force.temperature",Eh=.001,YZ="org.eclipse.elk.force.repulsion",qa={148:1},yS="org.eclipse.elk.alg.force.options",H8=1.600000023841858,$o="org.eclipse.elk.force",EN="org.eclipse.elk.priority",em="org.eclipse.elk.spacing.nodeNode",QZ="org.eclipse.elk.spacing.edgeLabel",G8="org.eclipse.elk.aspectRatio",sF="org.eclipse.elk.randomSeed",kS="org.eclipse.elk.separateConnectedComponents",nm="org.eclipse.elk.padding",jS="org.eclipse.elk.interactive",WZ="org.eclipse.elk.portConstraints",lF="org.eclipse.elk.edgeLabels.inline",ES="org.eclipse.elk.omitNodeMicroLayout",q8="org.eclipse.elk.nodeSize.fixedGraphSize",hy="org.eclipse.elk.nodeSize.options",wv="org.eclipse.elk.nodeSize.constraints",U8="org.eclipse.elk.nodeLabels.placement",X8="org.eclipse.elk.portLabels.placement",SN="org.eclipse.elk.topdownLayout",xN="org.eclipse.elk.topdown.scaleFactor",AN="org.eclipse.elk.topdown.hierarchicalNodeWidth",MN="org.eclipse.elk.topdown.hierarchicalNodeAspectRatio",Ww="org.eclipse.elk.topdown.nodeType",owe="origin",tQe="random",iQe="boundingBox.upLeft",rQe="boundingBox.lowRight",swe="org.eclipse.elk.stress.fixed",lwe="org.eclipse.elk.stress.desiredEdgeLength",fwe="org.eclipse.elk.stress.dimension",awe="org.eclipse.elk.stress.epsilon",hwe="org.eclipse.elk.stress.iterationLimit",V0="org.eclipse.elk.stress",cQe="ELK Stress",dy="org.eclipse.elk.nodeSize.minimum",fF="org.eclipse.elk.alg.force.stress",uQe="Layered layout",by="org.eclipse.elk.alg.layered",CN="org.eclipse.elk.alg.layered.compaction.components",SS="org.eclipse.elk.alg.layered.compaction.oned",aF="org.eclipse.elk.alg.layered.compaction.oned.algs",fg="org.eclipse.elk.alg.layered.compaction.recthull",Ua="org.eclipse.elk.alg.layered.components",ma="NONE",ZZ="MODEL_ORDER",qu={3:1,6:1,4:1,10:1,5:1,126:1},oQe={3:1,6:1,4:1,5:1,135:1,90:1,110:1},hF="org.eclipse.elk.alg.layered.compound",Ai={43:1},Zu="org.eclipse.elk.alg.layered.graph",eee=" -> ",sQe="Not supported by LGraph",dwe="Port side is undefined",K8={3:1,6:1,4:1,5:1,323:1,135:1,90:1,110:1},Bd={3:1,6:1,4:1,5:1,135:1,199:1,209:1,90:1,110:1},lQe={3:1,6:1,4:1,5:1,135:1,2004:1,209:1,90:1,110:1},fQe=`([{"' \r `,aQe=`)]}"' \r -`,hQe="The given string contains parts that cannot be parsed as numbers.",CN="org.eclipse.elk.core.math",dQe={3:1,4:1,140:1,213:1,414:1},bQe={3:1,4:1,104:1,213:1,414:1},zd="org.eclipse.elk.alg.layered.graph.transform",gQe="ElkGraphImporter",wQe="ElkGraphImporter/lambda$1$Type",pQe="ElkGraphImporter/lambda$2$Type",mQe="ElkGraphImporter/lambda$4$Type",Wn="org.eclipse.elk.alg.layered.intermediate",vQe="Node margin calculation",yQe="ONE_SIDED_GREEDY_SWITCH",kQe="TWO_SIDED_GREEDY_SWITCH",nee="No implementation is available for the layout processor ",tee="IntermediateProcessorStrategy",iee="Node '",jQe="FIRST_SEPARATE",EQe="LAST_SEPARATE",SQe="Odd port side processing",lr="org.eclipse.elk.alg.layered.intermediate.compaction",xS="org.eclipse.elk.alg.layered.intermediate.greedyswitch",t1="org.eclipse.elk.alg.layered.p3order.counting",AS={220:1},gy="org.eclipse.elk.alg.layered.intermediate.loops",gl="org.eclipse.elk.alg.layered.intermediate.loops.ordering",Y0="org.eclipse.elk.alg.layered.intermediate.loops.routing",dF="org.eclipse.elk.alg.layered.intermediate.preserveorder",Sh="org.eclipse.elk.alg.layered.intermediate.wrapping",Cu="org.eclipse.elk.alg.layered.options",ree="INTERACTIVE",bwe="GREEDY",xQe="DEPTH_FIRST",AQe="EDGE_LENGTH",MQe="SELF_LOOPS",TQe="firstTryWithInitialOrder",gwe="org.eclipse.elk.layered.directionCongruency",wwe="org.eclipse.elk.layered.feedbackEdges",bF="org.eclipse.elk.layered.interactiveReferencePoint",pwe="org.eclipse.elk.layered.mergeEdges",mwe="org.eclipse.elk.layered.mergeHierarchyEdges",vwe="org.eclipse.elk.layered.allowNonFlowPortsToSwitchSides",ywe="org.eclipse.elk.layered.portSortingStrategy",kwe="org.eclipse.elk.layered.thoroughness",jwe="org.eclipse.elk.layered.unnecessaryBendpoints",Ewe="org.eclipse.elk.layered.generatePositionAndLayerIds",ON="org.eclipse.elk.layered.cycleBreaking.strategy",NN="org.eclipse.elk.layered.layering.strategy",Swe="org.eclipse.elk.layered.layering.layerConstraint",xwe="org.eclipse.elk.layered.layering.layerChoiceConstraint",Awe="org.eclipse.elk.layered.layering.layerId",cee="org.eclipse.elk.layered.layering.minWidth.upperBoundOnWidth",uee="org.eclipse.elk.layered.layering.minWidth.upperLayerEstimationScalingFactor",oee="org.eclipse.elk.layered.layering.nodePromotion.strategy",see="org.eclipse.elk.layered.layering.nodePromotion.maxIterations",lee="org.eclipse.elk.layered.layering.coffmanGraham.layerBound",MS="org.eclipse.elk.layered.crossingMinimization.strategy",Mwe="org.eclipse.elk.layered.crossingMinimization.forceNodeModelOrder",fee="org.eclipse.elk.layered.crossingMinimization.hierarchicalSweepiness",aee="org.eclipse.elk.layered.crossingMinimization.semiInteractive",Twe="org.eclipse.elk.layered.crossingMinimization.inLayerPredOf",Cwe="org.eclipse.elk.layered.crossingMinimization.inLayerSuccOf",Owe="org.eclipse.elk.layered.crossingMinimization.positionChoiceConstraint",Nwe="org.eclipse.elk.layered.crossingMinimization.positionId",Dwe="org.eclipse.elk.layered.crossingMinimization.greedySwitch.activationThreshold",hee="org.eclipse.elk.layered.crossingMinimization.greedySwitch.type",gF="org.eclipse.elk.layered.crossingMinimization.greedySwitchHierarchical.type",pv="org.eclipse.elk.layered.nodePlacement.strategy",wF="org.eclipse.elk.layered.nodePlacement.favorStraightEdges",dee="org.eclipse.elk.layered.nodePlacement.bk.edgeStraightening",bee="org.eclipse.elk.layered.nodePlacement.bk.fixedAlignment",gee="org.eclipse.elk.layered.nodePlacement.linearSegments.deflectionDampening",wee="org.eclipse.elk.layered.nodePlacement.networkSimplex.nodeFlexibility",pee="org.eclipse.elk.layered.nodePlacement.networkSimplex.nodeFlexibility.default",Iwe="org.eclipse.elk.layered.edgeRouting.selfLoopDistribution",_we="org.eclipse.elk.layered.edgeRouting.selfLoopOrdering",pF="org.eclipse.elk.layered.edgeRouting.splines.mode",mF="org.eclipse.elk.layered.edgeRouting.splines.sloppy.layerSpacingFactor",mee="org.eclipse.elk.layered.edgeRouting.polyline.slopedEdgeZoneWidth",Lwe="org.eclipse.elk.layered.spacing.baseValue",Pwe="org.eclipse.elk.layered.spacing.edgeNodeBetweenLayers",$we="org.eclipse.elk.layered.spacing.edgeEdgeBetweenLayers",Rwe="org.eclipse.elk.layered.spacing.nodeNodeBetweenLayers",Bwe="org.eclipse.elk.layered.priority.direction",zwe="org.eclipse.elk.layered.priority.shortness",Fwe="org.eclipse.elk.layered.priority.straightness",vee="org.eclipse.elk.layered.compaction.connectedComponents",Jwe="org.eclipse.elk.layered.compaction.postCompaction.strategy",Hwe="org.eclipse.elk.layered.compaction.postCompaction.constraints",vF="org.eclipse.elk.layered.highDegreeNodes.treatment",yee="org.eclipse.elk.layered.highDegreeNodes.threshold",kee="org.eclipse.elk.layered.highDegreeNodes.treeHeight",q1="org.eclipse.elk.layered.wrapping.strategy",yF="org.eclipse.elk.layered.wrapping.additionalEdgeSpacing",kF="org.eclipse.elk.layered.wrapping.correctionFactor",TS="org.eclipse.elk.layered.wrapping.cutting.strategy",jee="org.eclipse.elk.layered.wrapping.cutting.cuts",Eee="org.eclipse.elk.layered.wrapping.cutting.msd.freedom",jF="org.eclipse.elk.layered.wrapping.validify.strategy",EF="org.eclipse.elk.layered.wrapping.validify.forbiddenIndices",SF="org.eclipse.elk.layered.wrapping.multiEdge.improveCuts",xF="org.eclipse.elk.layered.wrapping.multiEdge.distancePenalty",See="org.eclipse.elk.layered.wrapping.multiEdge.improveWrappedEdges",xee="org.eclipse.elk.layered.layerUnzipping.strategy",Aee="org.eclipse.elk.layered.layerUnzipping.minimizeEdgeLength",Mee="org.eclipse.elk.layered.layerUnzipping.layerSplit",Tee="org.eclipse.elk.layered.layerUnzipping.resetOnLongEdges",Gwe="org.eclipse.elk.layered.edgeLabels.sideSelection",qwe="org.eclipse.elk.layered.edgeLabels.centerLabelPlacementStrategy",AF="org.eclipse.elk.layered.considerModelOrder.strategy",Uwe="org.eclipse.elk.layered.considerModelOrder.portModelOrder",DN="org.eclipse.elk.layered.considerModelOrder.noModelOrder",Cee="org.eclipse.elk.layered.considerModelOrder.components",Xwe="org.eclipse.elk.layered.considerModelOrder.longEdgeStrategy",Oee="org.eclipse.elk.layered.considerModelOrder.crossingCounterNodeInfluence",Nee="org.eclipse.elk.layered.considerModelOrder.crossingCounterPortInfluence",Dee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cycleBreakingId",Iee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.crossingMinimizationId",_ee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.componentGroupId",Kwe="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cbGroupOrderStrategy",Lee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cbPreferredSourceId",Pee="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cbPreferredTargetId",Vwe="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cmGroupOrderStrategy",Ywe="org.eclipse.elk.layered.considerModelOrder.groupModelOrder.cmEnforcedGroupOrders",$ee="layering",CQe="layering.minWidth",OQe="layering.nodePromotion",V8="crossingMinimization",MF="org.eclipse.elk.hierarchyHandling",NQe="crossingMinimization.greedySwitch",DQe="nodePlacement",IQe="nodePlacement.bk",_Qe="edgeRouting",IN="org.eclipse.elk.edgeRouting",Xa="spacing",Qwe="priority",Wwe="compaction",LQe="compaction.postCompaction",PQe="Specifies whether and how post-process compaction is applied.",Zwe="highDegreeNodes",epe="wrapping",$Qe="wrapping.cutting",RQe="wrapping.validify",npe="wrapping.multiEdge",Ree="layerUnzipping",Bee="edgeLabels",CS="considerModelOrder",Y8="considerModelOrder.groupModelOrder",tpe="Group ID of the Node Type",ipe="org.eclipse.elk.spacing.commentComment",rpe="org.eclipse.elk.spacing.commentNode",cpe="org.eclipse.elk.spacing.componentComponent",upe="org.eclipse.elk.spacing.edgeEdge",zee="org.eclipse.elk.spacing.edgeNode",ope="org.eclipse.elk.spacing.labelLabel",spe="org.eclipse.elk.spacing.labelPortHorizontal",lpe="org.eclipse.elk.spacing.labelPortVertical",fpe="org.eclipse.elk.spacing.labelNode",ape="org.eclipse.elk.spacing.nodeSelfLoop",hpe="org.eclipse.elk.spacing.portPort",dpe="org.eclipse.elk.spacing.individual",bpe="org.eclipse.elk.port.borderOffset",gpe="org.eclipse.elk.noLayout",wpe="org.eclipse.elk.port.side",_N="org.eclipse.elk.debugMode",ppe="org.eclipse.elk.alignment",mpe="org.eclipse.elk.insideSelfLoops.activate",vpe="org.eclipse.elk.insideSelfLoops.yo",Fee="org.eclipse.elk.direction",ype="org.eclipse.elk.nodeLabels.padding",kpe="org.eclipse.elk.portLabels.nextToPortIfPossible",jpe="org.eclipse.elk.portLabels.treatAsGroup",Epe="org.eclipse.elk.portAlignment.default",Spe="org.eclipse.elk.portAlignment.north",xpe="org.eclipse.elk.portAlignment.south",Ape="org.eclipse.elk.portAlignment.west",Mpe="org.eclipse.elk.portAlignment.east",TF="org.eclipse.elk.contentAlignment",Tpe="org.eclipse.elk.junctionPoints",Cpe="org.eclipse.elk.edge.thickness",Ope="org.eclipse.elk.edgeLabels.placement",Npe="org.eclipse.elk.port.index",Dpe="org.eclipse.elk.commentBox",Ipe="org.eclipse.elk.hypernode",_pe="org.eclipse.elk.port.anchor",Jee="org.eclipse.elk.partitioning.activate",Hee="org.eclipse.elk.partitioning.partition",CF="org.eclipse.elk.position",Lpe="org.eclipse.elk.margins",Ppe="org.eclipse.elk.spacing.portsSurrounding",OF="org.eclipse.elk.interactiveLayout",Ru="org.eclipse.elk.core.util",$pe={3:1,4:1,5:1,590:1},BQe="NETWORK_SIMPLEX",Rpe="SIMPLE",uc={95:1,43:1},Zw="org.eclipse.elk.alg.layered.p1cycles",zQe="Depth-first cycle removal",FQe="Model order cycle breaking",U1="org.eclipse.elk.alg.layered.p2layers",Bpe={406:1,220:1},JQe={830:1,3:1,4:1},Ro="org.eclipse.elk.alg.layered.p3order",mv=17976931348623157e292,Gee=5e-324,Lc="org.eclipse.elk.alg.layered.p4nodes",HQe={3:1,4:1,5:1,838:1},xh=1e-5,Q0="org.eclipse.elk.alg.layered.p4nodes.bk",qee="org.eclipse.elk.alg.layered.p5edges",va="org.eclipse.elk.alg.layered.p5edges.orthogonal",Uee="org.eclipse.elk.alg.layered.p5edges.orthogonal.direction",Xee=1e-6,tm="org.eclipse.elk.alg.layered.p5edges.splines",Kee=.09999999999999998,NF=1e-8,GQe=4.71238898038469,qQe=1.5707963267948966,zpe=3.141592653589793,X1="org.eclipse.elk.alg.mrtree",Vee=.10000000149011612,DF="SUPER_ROOT",OS="org.eclipse.elk.alg.mrtree.graph",Fpe=-17976931348623157e292,go="org.eclipse.elk.alg.mrtree.intermediate",UQe="Processor compute fanout",IF={3:1,6:1,4:1,5:1,522:1,90:1,110:1},XQe="Set neighbors in level",LN="org.eclipse.elk.alg.mrtree.options",KQe="DESCENDANTS",Jpe="org.eclipse.elk.mrtree.compaction",Hpe="org.eclipse.elk.mrtree.edgeEndTextureLength",Gpe="org.eclipse.elk.mrtree.treeLevel",qpe="org.eclipse.elk.mrtree.positionConstraint",Upe="org.eclipse.elk.mrtree.weighting",Xpe="org.eclipse.elk.mrtree.edgeRoutingMode",Kpe="org.eclipse.elk.mrtree.searchOrder",VQe="Position Constraint",Bo="org.eclipse.elk.mrtree",YQe="org.eclipse.elk.tree",QQe="Processor arrange level",Q8="org.eclipse.elk.alg.mrtree.p2order",Qs="org.eclipse.elk.alg.mrtree.p4route",Vpe="org.eclipse.elk.alg.radial",ag=6.283185307179586,Ype="Before",_F="After",Qpe="org.eclipse.elk.alg.radial.intermediate",WQe="COMPACTION",Yee="org.eclipse.elk.alg.radial.intermediate.compaction",ZQe={3:1,4:1,5:1,90:1},Wpe="org.eclipse.elk.alg.radial.intermediate.optimization",Qee="No implementation is available for the layout option ",NS="org.eclipse.elk.alg.radial.options",eWe="CompactionStrategy",Zpe="org.eclipse.elk.radial.centerOnRoot",e2e="org.eclipse.elk.radial.orderId",n2e="org.eclipse.elk.radial.radius",LF="org.eclipse.elk.radial.rotate",Wee="org.eclipse.elk.radial.compactor",Zee="org.eclipse.elk.radial.compactionStepSize",t2e="org.eclipse.elk.radial.sorter",i2e="org.eclipse.elk.radial.wedgeCriteria",r2e="org.eclipse.elk.radial.optimizationCriteria",ene="org.eclipse.elk.radial.rotation.targetAngle",nne="org.eclipse.elk.radial.rotation.computeAdditionalWedgeSpace",c2e="org.eclipse.elk.radial.rotation.outgoingEdgeAngles",nWe="Compaction",u2e="rotation",Gl="org.eclipse.elk.radial",tWe="org.eclipse.elk.alg.radial.p1position.wedge",o2e="org.eclipse.elk.alg.radial.sorting",iWe=5.497787143782138,rWe=3.9269908169872414,cWe=2.356194490192345,uWe="org.eclipse.elk.alg.rectpacking",DS="org.eclipse.elk.alg.rectpacking.intermediate",tne="org.eclipse.elk.alg.rectpacking.options",s2e="org.eclipse.elk.rectpacking.trybox",l2e="org.eclipse.elk.rectpacking.currentPosition",f2e="org.eclipse.elk.rectpacking.desiredPosition",a2e="org.eclipse.elk.rectpacking.inNewRow",h2e="org.eclipse.elk.rectpacking.orderBySize",d2e="org.eclipse.elk.rectpacking.widthApproximation.strategy",b2e="org.eclipse.elk.rectpacking.widthApproximation.targetWidth",g2e="org.eclipse.elk.rectpacking.widthApproximation.optimizationGoal",w2e="org.eclipse.elk.rectpacking.widthApproximation.lastPlaceShift",p2e="org.eclipse.elk.rectpacking.packing.strategy",m2e="org.eclipse.elk.rectpacking.packing.compaction.rowHeightReevaluation",v2e="org.eclipse.elk.rectpacking.packing.compaction.iterations",y2e="org.eclipse.elk.rectpacking.whiteSpaceElimination.strategy",ine="widthApproximation",oWe="Compaction Strategy",sWe="packing.compaction",ms="org.eclipse.elk.rectpacking",W8="org.eclipse.elk.alg.rectpacking.p1widthapproximation",PF="org.eclipse.elk.alg.rectpacking.p2packing",lWe="No Compaction",k2e="org.eclipse.elk.alg.rectpacking.p3whitespaceelimination",PN="org.eclipse.elk.alg.rectpacking.util",$F="No implementation available for ",im="org.eclipse.elk.alg.spore",rm="org.eclipse.elk.alg.spore.options",ep="org.eclipse.elk.sporeCompaction",rne="org.eclipse.elk.underlyingLayoutAlgorithm",j2e="org.eclipse.elk.processingOrder.treeConstruction",E2e="org.eclipse.elk.processingOrder.spanningTreeCostFunction",cne="org.eclipse.elk.processingOrder.preferredRoot",une="org.eclipse.elk.processingOrder.rootSelection",one="org.eclipse.elk.structure.structureExtractionStrategy",S2e="org.eclipse.elk.compaction.compactionStrategy",x2e="org.eclipse.elk.compaction.orthogonal",A2e="org.eclipse.elk.overlapRemoval.maxIterations",M2e="org.eclipse.elk.overlapRemoval.runScanline",sne="processingOrder",fWe="overlapRemoval",Z8="org.eclipse.elk.sporeOverlap",aWe="org.eclipse.elk.alg.spore.p1structure",lne="org.eclipse.elk.alg.spore.p2processingorder",fne="org.eclipse.elk.alg.spore.p3execution",hWe="Topdown Layout",dWe="Invalid index: ",e7="org.eclipse.elk.core.alg",vv={342:1},cm={296:1},bWe="Make sure its type is registered with the ",T2e=" utility class.",n7="true",ane="false",gWe="Couldn't clone property '",np=.05,Oo="org.eclipse.elk.core.options",wWe=1.2999999523162842,tp="org.eclipse.elk.box",C2e="org.eclipse.elk.expandNodes",O2e="org.eclipse.elk.box.packingMode",pWe="org.eclipse.elk.algorithm",mWe="org.eclipse.elk.resolvedAlgorithm",N2e="org.eclipse.elk.bendPoints",yBn="org.eclipse.elk.labelManager",vWe="org.eclipse.elk.softwrappingFuzziness",yWe="org.eclipse.elk.scaleFactor",kWe="org.eclipse.elk.childAreaWidth",jWe="org.eclipse.elk.childAreaHeight",EWe="org.eclipse.elk.animate",SWe="org.eclipse.elk.animTimeFactor",xWe="org.eclipse.elk.layoutAncestors",AWe="org.eclipse.elk.maxAnimTime",MWe="org.eclipse.elk.minAnimTime",TWe="org.eclipse.elk.progressBar",CWe="org.eclipse.elk.validateGraph",OWe="org.eclipse.elk.validateOptions",NWe="org.eclipse.elk.zoomToFit",DWe="org.eclipse.elk.json.shapeCoords",IWe="org.eclipse.elk.json.edgeCoords",kBn="org.eclipse.elk.font.name",_We="org.eclipse.elk.font.size",hne="org.eclipse.elk.topdown.sizeCategories",D2e="org.eclipse.elk.topdown.sizeCategoriesHierarchicalNodeWeight",dne="org.eclipse.elk.topdown.sizeApproximator",I2e="org.eclipse.elk.topdown.scaleCap",LWe="org.eclipse.elk.edge.type",PWe="partitioning",$We="nodeLabels",RF="portAlignment",bne="nodeSize",gne="port",_2e="portLabels",t7="topdown",RWe="insideSelfLoops",L2e="INHERIT",i7="org.eclipse.elk.fixed",BF="org.eclipse.elk.random",zF={3:1,35:1,23:1,521:1,288:1},BWe="port must have a parent node to calculate the port side",zWe="The edge needs to have exactly one edge section. Found: ",IS="org.eclipse.elk.core.util.adapters",ql="org.eclipse.emf.ecore",yv="org.eclipse.elk.graph",FWe="EMapPropertyHolder",JWe="ElkBendPoint",HWe="ElkGraphElement",GWe="ElkConnectableShape",P2e="ElkEdge",qWe="ElkEdgeSection",UWe="EModelElement",XWe="ENamedElement",$2e="ElkLabel",R2e="ElkNode",B2e="ElkPort",KWe={94:1,93:1},wy="org.eclipse.emf.common.notify.impl",W0="The feature '",_S="' is not a valid changeable feature",VWe="Expecting null",wne="' is not a valid feature",YWe="The feature ID",QWe=" is not a valid feature ID",Bu=32768,WWe={109:1,94:1,93:1,57:1,52:1,100:1},Fn="org.eclipse.emf.ecore.impl",hg="org.eclipse.elk.graph.impl",LS="Recursive containment not allowed for ",r7="The datatype '",ip="' is not a valid classifier",pne="The value '",kv={195:1,3:1,4:1},mne="The class '",c7="http://www.eclipse.org/elk/ElkGraph",z2e="property",PS="value",vne="source",ZWe="properties",eZe="identifier",yne="height",kne="width",jne="parent",Ene="text",Sne="children",nZe="hierarchical",F2e="sources",xne="targets",Ane="sections",FF="bendPoints",J2e="outgoingShape",H2e="incomingShape",G2e="outgoingSections",q2e="incomingSections",yc="org.eclipse.emf.common.util",U2e="Severe implementation error in the Json to ElkGraph importer.",Ah="id",Qr="org.eclipse.elk.graph.json",u7="Unhandled parameter types: ",tZe="startPoint",iZe="An edge must have at least one source and one target (edge id: '",o7="').",rZe="Referenced edge section does not exist: ",cZe=" (edge id: '",X2e="target",uZe="sourcePoint",oZe="targetPoint",JF="group",ui="name",sZe="connectableShape cannot be null",lZe="edge cannot be null",fZe="Passed edge is not 'simple'.",HF="org.eclipse.elk.graph.util",$N="The 'no duplicates' constraint is violated",Mne="targetIndex=",dg=", size=",Tne="sourceIndex=",Mh={3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,67:1,61:1},Cne={3:1,4:1,20:1,31:1,56:1,18:1,50:1,16:1,59:1,71:1,67:1,61:1,585:1},GF="logging",aZe="measureExecutionTime",hZe="parser.parse.1",dZe="parser.parse.2",qF="parser.next.1",One="parser.next.2",bZe="parser.next.3",gZe="parser.next.4",bg="parser.factor.1",K2e="parser.factor.2",wZe="parser.factor.3",pZe="parser.factor.4",mZe="parser.factor.5",vZe="parser.factor.6",yZe="parser.atom.1",kZe="parser.atom.2",jZe="parser.atom.3",V2e="parser.atom.4",Nne="parser.atom.5",Y2e="parser.cc.1",UF="parser.cc.2",EZe="parser.cc.3",SZe="parser.cc.5",Q2e="parser.cc.6",W2e="parser.cc.7",Dne="parser.cc.8",xZe="parser.ope.1",AZe="parser.ope.2",MZe="parser.ope.3",Fd="parser.descape.1",TZe="parser.descape.2",CZe="parser.descape.3",OZe="parser.descape.4",NZe="parser.descape.5",Ul="parser.process.1",DZe="parser.quantifier.1",IZe="parser.quantifier.2",_Ze="parser.quantifier.3",LZe="parser.quantifier.4",Z2e="parser.quantifier.5",PZe="org.eclipse.emf.common.notify",eme={415:1,676:1},$Ze={3:1,4:1,20:1,31:1,56:1,18:1,16:1,71:1,61:1},RN={373:1,151:1},$S="index=",Ine={3:1,4:1,5:1,129:1},RZe={3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,61:1},nme={3:1,6:1,4:1,5:1,198:1},BZe={3:1,4:1,5:1,175:1,374:1},qf=1024,zZe=";/?:@&=+$,",FZe="invalid authority: ",JZe="EAnnotation",HZe="ETypedElement",GZe="EStructuralFeature",qZe="EAttribute",UZe="EClassifier",XZe="EEnumLiteral",KZe="EGenericType",VZe="EOperation",YZe="EParameter",QZe="EReference",WZe="ETypeParameter",$i="org.eclipse.emf.ecore.util",_ne={77:1},tme={3:1,20:1,18:1,16:1,61:1,586:1,77:1,72:1,98:1},ZZe="org.eclipse.emf.ecore.util.FeatureMap$Entry",as=8192,RS="byte",XF="char",BS="double",zS="float",FS="int",JS="long",HS="short",een="java.lang.Object",jv={3:1,4:1,5:1,255:1},ime={3:1,4:1,5:1,678:1},nen={3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,67:1,61:1,72:1},lu={3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,67:1,61:1,77:1,72:1,98:1},BN="mixed",Ut="http:///org/eclipse/emf/ecore/util/ExtendedMetaData",af="kind",ten={3:1,4:1,5:1,679:1},rme={3:1,4:1,20:1,31:1,56:1,18:1,16:1,71:1,61:1,77:1,72:1,98:1},KF={20:1,31:1,56:1,18:1,16:1,61:1,72:1},VF={50:1,128:1,287:1},YF={75:1,344:1},QF="The value of type '",WF="' must be of type '",Ev=1306,hf="http://www.eclipse.org/emf/2002/Ecore",ZF=-32768,rp="constraints",oc="baseType",ien="getEStructuralFeature",ren="getFeatureID",GS="feature",cen="getOperationID",cme="operation",uen="defaultValue",oen="eTypeParameters",sen="isInstance",len="getEEnumLiteral",fen="eContainingClass",ii={58:1},aen={3:1,4:1,5:1,122:1},hen="org.eclipse.emf.ecore.resource",den={94:1,93:1,588:1,1996:1},Lne="org.eclipse.emf.ecore.resource.impl",ume="unspecified",zN="simple",eJ="attribute",ben="attributeWildcard",nJ="element",Pne="elementWildcard",ya="collapse",$ne="itemType",tJ="namespace",FN="##targetNamespace",df="whiteSpace",ome="wildcards",gg="http://www.eclipse.org/emf/2003/XMLType",Rne="##any",s7="uninitialized",JN="The multiplicity constraint is violated",iJ="org.eclipse.emf.ecore.xml.type",gen="ProcessingInstruction",wen="SimpleAnyType",pen="XMLTypeDocumentRoot",kr="org.eclipse.emf.ecore.xml.type.impl",HN="INF",men="processing",ven="ENTITIES_._base",sme="minLength",lme="ENTITY",rJ="NCName",yen="IDREFS_._base",fme="integer",Bne="token",zne="pattern",ken="[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*",ame="\\i\\c*",jen="[\\i-[:]][\\c-[:]]*",Een="nonPositiveInteger",GN="maxInclusive",hme="NMTOKEN",Sen="NMTOKENS_._base",dme="nonNegativeInteger",qN="minInclusive",xen="normalizedString",Aen="unsignedByte",Men="unsignedInt",Ten="18446744073709551615",Cen="unsignedShort",Oen="processingInstruction",Jd="org.eclipse.emf.ecore.xml.type.internal",l7=1114111,Nen="Internal Error: shorthands: \\u",qS="xml:isDigit",Fne="xml:isWord",Jne="xml:isSpace",Hne="xml:isNameChar",Gne="xml:isInitialNameChar",Den="09٠٩۰۹०९০৯੦੯૦૯୦୯௧௯౦౯೦೯൦൯๐๙໐໙༠༩",Ien="AZazÀÖØöøıĴľŁňŊžƀǃǍǰǴǵǺȗɐʨʻˁΆΆΈΊΌΌΎΡΣώϐϖϚϚϜϜϞϞϠϠϢϳЁЌЎяёќўҁҐӄӇӈӋӌӐӫӮӵӸӹԱՖՙՙաֆאתװײءغفيٱڷںھۀێېۓەەۥۦअहऽऽक़ॡঅঌএঐওনপরললশহড়ঢ়য়ৡৰৱਅਊਏਐਓਨਪਰਲਲ਼ਵਸ਼ਸਹਖ਼ੜਫ਼ਫ਼ੲੴઅઋઍઍએઑઓનપરલળવહઽઽૠૠଅଌଏଐଓନପରଲଳଶହଽଽଡ଼ଢ଼ୟୡஅஊஎஐஒகஙசஜஜஞடணதநபமவஷஹఅఌఎఐఒనపళవహౠౡಅಌಎಐಒನಪಳವಹೞೞೠೡഅഌഎഐഒനപഹൠൡกฮะะาำเๅກຂຄຄງຈຊຊຍຍດທນຟມຣລລວວສຫອຮະະາຳຽຽເໄཀཇཉཀྵႠჅაჶᄀᄀᄂᄃᄅᄇᄉᄉᄋᄌᄎᄒᄼᄼᄾᄾᅀᅀᅌᅌᅎᅎᅐᅐᅔᅕᅙᅙᅟᅡᅣᅣᅥᅥᅧᅧᅩᅩᅭᅮᅲᅳᅵᅵᆞᆞᆨᆨᆫᆫᆮᆯᆷᆸᆺᆺᆼᇂᇫᇫᇰᇰᇹᇹḀẛẠỹἀἕἘἝἠὅὈὍὐὗὙὙὛὛὝὝὟώᾀᾴᾶᾼιιῂῄῆῌῐΐῖΊῠῬῲῴῶῼΩΩKÅ℮℮ↀↂ〇〇〡〩ぁゔァヺㄅㄬ一龥가힣",_en="Private Use",qne="ASSIGNED",Une="\0€ÿĀſƀɏɐʯʰ˿̀ͯͰϿЀӿ԰֏֐׿؀ۿ܀ݏހ޿ऀॿঀ৿਀੿઀૿଀୿஀௿ఀ౿ಀ೿ഀൿ඀෿฀๿຀໿ༀ࿿က႟Ⴀჿᄀᇿሀ፿Ꭰ᏿᐀ᙿ ᚟ᚠ᛿ក៿᠀᢯Ḁỿἀ῿ ⁰₟₠⃏⃐⃿℀⅏⅐↏←⇿∀⋿⌀⏿␀␿⑀⑟①⓿─╿▀▟■◿☀⛿✀➿⠀⣿⺀⻿⼀⿟⿰⿿ 〿぀ゟ゠ヿ㄀ㄯ㄰㆏㆐㆟ㆠㆿ㈀㋿㌀㏿㐀䶵一鿿ꀀ꒏꒐꓏가힣豈﫿ffﭏﭐ﷿︠︯︰﹏﹐﹯ﹰ﻾\uFEFF\uFEFF＀￯",bme="UNASSIGNED",f7={3:1,121:1},Len="org.eclipse.emf.ecore.xml.type.util",cJ={3:1,4:1,5:1,376:1},gme="org.eclipse.xtext.xbase.lib",Pen="Cannot add elements to a Range",$en="Cannot set elements in a Range",Ren="Cannot remove elements from a Range",Ben="user.agent",s,uJ,Xne;k.goog=k.goog||{},k.goog.global=k.goog.global||k,uJ={},m(1,null,{},U),s.Fb=function(n){return bCe(this,n)},s.Gb=function(){return this.Pm},s.Hb=function(){return vw(this)},s.Ib=function(){var n;return Db(Us(this))+"@"+(n=Oi(this)>>>0,n.toString(16))},s.equals=function(e){return this.Fb(e)},s.hashCode=function(){return this.Hb()},s.toString=function(){return this.Ib()};var zen,Fen,Jen;m(298,1,{298:1,2086:1},g1e),s.te=function(n){var t;return t=new g1e,t.i=4,n>1?t.c=H_e(this,n-1):t.c=this,t},s.ue=function(){return M1(this),this.b},s.ve=function(){return Db(this)},s.we=function(){return M1(this),this.k},s.xe=function(){return(this.i&4)!=0},s.ye=function(){return(this.i&1)!=0},s.Ib=function(){return xhe(this)},s.i=0;var Ar=v(Tu,"Object",1),wme=v(Tu,"Class",298);m(2058,1,lN),v(fN,"Optional",2058),m(1160,2058,lN,G),s.Fb=function(n){return n===this},s.Hb=function(){return 2040732332},s.Ib=function(){return"Optional.absent()"},s.Jb=function(n){return Nt(n),mj(),Kne};var Kne;v(fN,"Absent",1160),m(627,1,{},NX),v(fN,"Joiner",627);var jBn=Hi(fN,"Predicate");m(577,1,{178:1,577:1,3:1,48:1},NT),s.Mb=function(n){return Jze(this,n)},s.Lb=function(n){return Jze(this,n)},s.Fb=function(n){var t;return X(n,577)?(t=u(n,577),abe(this.a,t.a)):!1},s.Hb=function(){return y1e(this.a)+306654252},s.Ib=function(){return jTn(this.a)},v(fN,"Predicates/AndPredicate",577),m(411,2058,{411:1,3:1},W6),s.Fb=function(n){var t;return X(n,411)?(t=u(n,411),di(this.a,t.a)):!1},s.Hb=function(){return 1502476572+Oi(this.a)},s.Ib=function(){return sYe+this.a+")"},s.Jb=function(n){return new W6(CR(n.Kb(this.a),"the Function passed to Optional.transform() must not return null."))},v(fN,"Present",411),m(204,1,I8),s.Nb=function(n){Zr(this,n)},s.Qb=function(){rAe()},v(dn,"UnmodifiableIterator",204),m(2038,204,_8),s.Qb=function(){rAe()},s.Rb=function(n){throw $(new _t)},s.Wb=function(n){throw $(new _t)},v(dn,"UnmodifiableListIterator",2038),m(392,2038,_8),s.Ob=function(){return this.b0},s.Pb=function(){if(this.b>=this.c)throw $(new au);return this.Xb(this.b++)},s.Tb=function(){return this.b},s.Ub=function(){if(this.b<=0)throw $(new au);return this.Xb(--this.b)},s.Vb=function(){return this.b-1},s.b=0,s.c=0,v(dn,"AbstractIndexedListIterator",392),m(702,204,I8),s.Ob=function(){return LY(this)},s.Pb=function(){return vhe(this)},s.e=1,v(dn,"AbstractIterator",702),m(2046,1,{229:1}),s.Zb=function(){var n;return n=this.f,n||(this.f=this.ac())},s.Fb=function(n){return iQ(this,n)},s.Hb=function(){return Oi(this.Zb())},s.dc=function(){return this.gc()==0},s.ec=function(){return S4(this)},s.Ib=function(){return su(this.Zb())},v(dn,"AbstractMultimap",2046),m(730,2046,og),s.$b=function(){mB(this)},s._b=function(n){return kAe(this,n)},s.ac=function(){return new g9(this,this.c)},s.ic=function(n){return this.hc()},s.bc=function(){return new R3(this,this.c)},s.jc=function(){return this.mc(this.hc())},s.kc=function(){return new Hxe(this)},s.lc=function(){return fW(this.c.vc().Lc(),new W,64,this.d)},s.cc=function(n){return pi(this,n)},s.fc=function(n){return SO(this,n)},s.gc=function(){return this.d},s.mc=function(n){return jn(),new Hr(n)},s.nc=function(){return new Jxe(this)},s.oc=function(){return fW(this.c.Bc().Lc(),new ie,64,this.d)},s.pc=function(n,t){return new ZR(this,n,t,null)},s.d=0,v(dn,"AbstractMapBasedMultimap",730),m(1661,730,og),s.hc=function(){return new xo(this.a)},s.jc=function(){return jn(),jn(),Sc},s.cc=function(n){return u(pi(this,n),16)},s.fc=function(n){return u(SO(this,n),16)},s.Zb=function(){return O4(this)},s.Fb=function(n){return iQ(this,n)},s.qc=function(n){return u(pi(this,n),16)},s.rc=function(n){return u(SO(this,n),16)},s.mc=function(n){return OR(u(n,16))},s.pc=function(n,t){return WLe(this,n,u(t,16),null)},v(dn,"AbstractListMultimap",1661),m(736,1,Fr),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.c.Ob()||this.e.Ob()},s.Pb=function(){var n;return this.e.Ob()||(n=u(this.c.Pb(),45),this.b=n.jd(),this.a=u(n.kd(),18),this.e=this.a.Jc()),this.sc(this.b,this.e.Pb())},s.Qb=function(){this.e.Qb(),u(uf(this.a),18).dc()&&this.c.Qb(),--this.d.d},v(dn,"AbstractMapBasedMultimap/Itr",736),m(1098,736,Fr,Jxe),s.sc=function(n,t){return t},v(dn,"AbstractMapBasedMultimap/1",1098),m(1099,1,{},ie),s.Kb=function(n){return u(n,18).Lc()},v(dn,"AbstractMapBasedMultimap/1methodref$spliterator$Type",1099),m(1100,736,Fr,Hxe),s.sc=function(n,t){return new bw(n,t)},v(dn,"AbstractMapBasedMultimap/2",1100);var pme=Hi(mt,"Map");m(2027,1,Vw),s.wc=function(n){bO(this,n)},s.$b=function(){this.vc().$b()},s.tc=function(n){return UQ(this,n)},s._b=function(n){return!!l0e(this,n,!1)},s.uc=function(n){var t,i,r;for(i=this.vc().Jc();i.Ob();)if(t=u(i.Pb(),45),r=t.kd(),ue(n)===ue(r)||n!=null&&di(n,r))return!0;return!1},s.Fb=function(n){var t,i,r;if(n===this)return!0;if(!X(n,92)||(r=u(n,92),this.gc()!=r.gc()))return!1;for(i=r.vc().Jc();i.Ob();)if(t=u(i.Pb(),45),!this.tc(t))return!1;return!0},s.xc=function(n){return du(l0e(this,n,!1))},s.Hb=function(){return a1e(this.vc())},s.dc=function(){return this.gc()==0},s.ec=function(){return new nt(this)},s.yc=function(n,t){throw $(new gd("Put not supported on this map"))},s.zc=function(n){xE(this,n)},s.Ac=function(n){return du(l0e(this,n,!0))},s.gc=function(){return this.vc().gc()},s.Ib=function(){return oGe(this)},s.Bc=function(){return new ct(this)},v(mt,"AbstractMap",2027),m(2047,2027,Vw),s.bc=function(){return new VP(this)},s.vc=function(){return zDe(this)},s.ec=function(){var n;return n=this.g,n||(this.g=this.bc())},s.Bc=function(){var n;return n=this.i,n||(this.i=new hMe(this))},v(dn,"Maps/ViewCachingAbstractMap",2047),m(395,2047,Vw,g9),s.xc=function(n){return m8n(this,n)},s.Ac=function(n){return Ckn(this,n)},s.$b=function(){this.d==this.e.c?this.e.$b():uR(new yfe(this))},s._b=function(n){return yFe(this.d,n)},s.Dc=function(){return new dP(this)},s.Cc=function(){return this.Dc()},s.Fb=function(n){return this===n||di(this.d,n)},s.Hb=function(){return Oi(this.d)},s.ec=function(){return this.e.ec()},s.gc=function(){return this.d.gc()},s.Ib=function(){return su(this.d)},v(dn,"AbstractMapBasedMultimap/AsMap",395);var Xl=Hi(Tu,"Iterable");m(31,1,Y2),s.Ic=function(n){rc(this,n)},s.Lc=function(){return new pn(this,0)},s.Mc=function(){return new wn(null,this.Lc())},s.Ec=function(n){throw $(new gd("Add not supported on this collection"))},s.Fc=function(n){return fc(this,n)},s.$b=function(){rae(this)},s.Gc=function(n){return P2(this,n,!1)},s.Hc=function(n){return yO(this,n)},s.dc=function(){return this.gc()==0},s.Kc=function(n){return P2(this,n,!0)},s.Nc=function(){return Ofe(this)},s.Oc=function(n){return GE(this,n)},s.Ib=function(){return Fa(this)},v(mt,"AbstractCollection",31);var bf=Hi(mt,"Set");m(Ha,31,fs),s.Lc=function(){return new pn(this,1)},s.Fb=function(n){return jJe(this,n)},s.Hb=function(){return a1e(this)},v(mt,"AbstractSet",Ha),m(2030,Ha,fs),v(dn,"Sets/ImprovedAbstractSet",2030),m(2031,2030,fs),s.$b=function(){this.Pc().$b()},s.Gc=function(n){return iJe(this,n)},s.dc=function(){return this.Pc().dc()},s.Kc=function(n){var t;return this.Gc(n)&&X(n,45)?(t=u(n,45),this.Pc().ec().Kc(t.jd())):!1},s.gc=function(){return this.Pc().gc()},v(dn,"Maps/EntrySet",2031),m(1096,2031,fs,dP),s.Gc=function(n){return $1e(this.a.d.vc(),n)},s.Jc=function(){return new yfe(this.a)},s.Pc=function(){return this.a},s.Kc=function(n){var t;return $1e(this.a.d.vc(),n)?(t=u(uf(u(n,45)),45),t9n(this.a.e,t.jd()),!0):!1},s.Lc=function(){return NC(this.a.d.vc().Lc(),new bP(this.a))},v(dn,"AbstractMapBasedMultimap/AsMap/AsMapEntries",1096),m(1097,1,{},bP),s.Kb=function(n){return LPe(this.a,u(n,45))},v(dn,"AbstractMapBasedMultimap/AsMap/AsMapEntries/0methodref$wrapEntry$Type",1097),m(734,1,Fr,yfe),s.Nb=function(n){Zr(this,n)},s.Pb=function(){var n;return n=u(this.b.Pb(),45),this.a=u(n.kd(),18),LPe(this.c,n)},s.Ob=function(){return this.b.Ob()},s.Qb=function(){x9(!!this.a),this.b.Qb(),this.c.e.d-=this.a.gc(),this.a.$b(),this.a=null},v(dn,"AbstractMapBasedMultimap/AsMap/AsMapIterator",734),m(530,2030,fs,VP),s.$b=function(){this.b.$b()},s.Gc=function(n){return this.b._b(n)},s.Ic=function(n){Nt(n),this.b.wc(new cj(n))},s.dc=function(){return this.b.dc()},s.Jc=function(){return new vj(this.b.vc().Jc())},s.Kc=function(n){return this.b._b(n)?(this.b.Ac(n),!0):!1},s.gc=function(){return this.b.gc()},v(dn,"Maps/KeySet",530),m(332,530,fs,R3),s.$b=function(){var n;uR((n=this.b.vc().Jc(),new Uoe(this,n)))},s.Hc=function(n){return this.b.ec().Hc(n)},s.Fb=function(n){return this===n||di(this.b.ec(),n)},s.Hb=function(){return Oi(this.b.ec())},s.Jc=function(){var n;return n=this.b.vc().Jc(),new Uoe(this,n)},s.Kc=function(n){var t,i;return i=0,t=u(this.b.Ac(n),18),t&&(i=t.gc(),t.$b(),this.a.d-=i),i>0},s.Lc=function(){return this.b.ec().Lc()},v(dn,"AbstractMapBasedMultimap/KeySet",332),m(735,1,Fr,Uoe),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.c.Ob()},s.Pb=function(){return this.a=u(this.c.Pb(),45),this.a.jd()},s.Qb=function(){var n;x9(!!this.a),n=u(this.a.kd(),18),this.c.Qb(),this.b.a.d-=n.gc(),n.$b(),this.a=null},v(dn,"AbstractMapBasedMultimap/KeySet/1",735),m(489,395,{92:1,134:1},SC),s.bc=function(){return this.Qc()},s.ec=function(){return this.Sc()},s.Qc=function(){return new WT(this.c,this.Uc())},s.Rc=function(){return this.Uc().Rc()},s.Sc=function(){var n;return n=this.b,n||(this.b=this.Qc())},s.Tc=function(){return this.Uc().Tc()},s.Uc=function(){return u(this.d,134)},v(dn,"AbstractMapBasedMultimap/SortedAsMap",489),m(437,489,$ge,Zj),s.bc=function(){return new w9(this.a,u(u(this.d,134),138))},s.Qc=function(){return new w9(this.a,u(u(this.d,134),138))},s.ec=function(){var n;return n=this.b,u(n||(this.b=new w9(this.a,u(u(this.d,134),138))),277)},s.Sc=function(){var n;return n=this.b,u(n||(this.b=new w9(this.a,u(u(this.d,134),138))),277)},s.Uc=function(){return u(u(this.d,134),138)},s.Vc=function(n){return u(u(this.d,134),138).Vc(n)},s.Wc=function(n){return u(u(this.d,134),138).Wc(n)},s.Xc=function(n,t){return new Zj(this.a,u(u(this.d,134),138).Xc(n,t))},s.Yc=function(n){return u(u(this.d,134),138).Yc(n)},s.Zc=function(n){return u(u(this.d,134),138).Zc(n)},s.$c=function(n,t){return new Zj(this.a,u(u(this.d,134),138).$c(n,t))},v(dn,"AbstractMapBasedMultimap/NavigableAsMap",437),m(488,332,lYe,WT),s.Lc=function(){return this.b.ec().Lc()},v(dn,"AbstractMapBasedMultimap/SortedKeySet",488),m(394,488,Rge,w9),v(dn,"AbstractMapBasedMultimap/NavigableKeySet",394),m(539,31,Y2,ZR),s.Ec=function(n){var t,i;return Ds(this),i=this.d.dc(),t=this.d.Ec(n),t&&(++this.f.d,i&&TC(this)),t},s.Fc=function(n){var t,i,r;return n.dc()?!1:(r=(Ds(this),this.d.gc()),t=this.d.Fc(n),t&&(i=this.d.gc(),this.f.d+=i-r,r==0&&TC(this)),t)},s.$b=function(){var n;n=(Ds(this),this.d.gc()),n!=0&&(this.d.$b(),this.f.d-=n,hR(this))},s.Gc=function(n){return Ds(this),this.d.Gc(n)},s.Hc=function(n){return Ds(this),this.d.Hc(n)},s.Fb=function(n){return n===this?!0:(Ds(this),di(this.d,n))},s.Hb=function(){return Ds(this),Oi(this.d)},s.Jc=function(){return Ds(this),new rfe(this)},s.Kc=function(n){var t;return Ds(this),t=this.d.Kc(n),t&&(--this.f.d,hR(this)),t},s.gc=function(){return tCe(this)},s.Lc=function(){return Ds(this),this.d.Lc()},s.Ib=function(){return Ds(this),su(this.d)},v(dn,"AbstractMapBasedMultimap/WrappedCollection",539);var wl=Hi(mt,"List");m(732,539,{20:1,31:1,18:1,16:1},Nfe),s.gd=function(n){Vb(this,n)},s.Lc=function(){return Ds(this),this.d.Lc()},s._c=function(n,t){var i;Ds(this),i=this.d.dc(),u(this.d,16)._c(n,t),++this.a.d,i&&TC(this)},s.ad=function(n,t){var i,r,c;return t.dc()?!1:(c=(Ds(this),this.d.gc()),i=u(this.d,16).ad(n,t),i&&(r=this.d.gc(),this.a.d+=r-c,c==0&&TC(this)),i)},s.Xb=function(n){return Ds(this),u(this.d,16).Xb(n)},s.bd=function(n){return Ds(this),u(this.d,16).bd(n)},s.cd=function(){return Ds(this),new ICe(this)},s.dd=function(n){return Ds(this),new ZIe(this,n)},s.ed=function(n){var t;return Ds(this),t=u(this.d,16).ed(n),--this.a.d,hR(this),t},s.fd=function(n,t){return Ds(this),u(this.d,16).fd(n,t)},s.hd=function(n,t){return Ds(this),WLe(this.a,this.e,u(this.d,16).hd(n,t),this.b?this.b:this)},v(dn,"AbstractMapBasedMultimap/WrappedList",732),m(1095,732,{20:1,31:1,18:1,16:1,59:1},kOe),v(dn,"AbstractMapBasedMultimap/RandomAccessWrappedList",1095),m(619,1,Fr,rfe),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return P9(this),this.b.Ob()},s.Pb=function(){return P9(this),this.b.Pb()},s.Qb=function(){rOe(this)},v(dn,"AbstractMapBasedMultimap/WrappedCollection/WrappedIterator",619),m(733,619,Qh,ICe,ZIe),s.Qb=function(){rOe(this)},s.Rb=function(n){var t;t=tCe(this.a)==0,(P9(this),u(this.b,128)).Rb(n),++this.a.a.d,t&&TC(this.a)},s.Sb=function(){return(P9(this),u(this.b,128)).Sb()},s.Tb=function(){return(P9(this),u(this.b,128)).Tb()},s.Ub=function(){return(P9(this),u(this.b,128)).Ub()},s.Vb=function(){return(P9(this),u(this.b,128)).Vb()},s.Wb=function(n){(P9(this),u(this.b,128)).Wb(n)},v(dn,"AbstractMapBasedMultimap/WrappedList/WrappedListIterator",733),m(731,539,lYe,Sle),s.Lc=function(){return Ds(this),this.d.Lc()},v(dn,"AbstractMapBasedMultimap/WrappedSortedSet",731),m(1094,731,Rge,TCe),v(dn,"AbstractMapBasedMultimap/WrappedNavigableSet",1094),m(1093,539,fs,XOe),s.Lc=function(){return Ds(this),this.d.Lc()},v(dn,"AbstractMapBasedMultimap/WrappedSet",1093),m(1102,1,{},W),s.Kb=function(n){return h9n(u(n,45))},v(dn,"AbstractMapBasedMultimap/lambda$1$Type",1102),m(1101,1,{},Z6),s.Kb=function(n){return new bw(this.a,n)},v(dn,"AbstractMapBasedMultimap/lambda$2$Type",1101);var wg=Hi(mt,"Map/Entry");m(358,1,hZ),s.Fb=function(n){var t;return X(n,45)?(t=u(n,45),T1(this.jd(),t.jd())&&T1(this.kd(),t.kd())):!1},s.Hb=function(){var n,t;return n=this.jd(),t=this.kd(),(n==null?0:Oi(n))^(t==null?0:Oi(t))},s.ld=function(n){throw $(new _t)},s.Ib=function(){return this.jd()+"="+this.kd()},v(dn,fYe,358),m(U0,31,Y2),s.$b=function(){this.md().$b()},s.Gc=function(n){var t;return X(n,45)?(t=u(n,45),_yn(this.md(),t.jd(),t.kd())):!1},s.Kc=function(n){var t;return X(n,45)?(t=u(n,45),$Le(this.md(),t.jd(),t.kd())):!1},s.gc=function(){return this.md().d},v(dn,"Multimaps/Entries",U0),m(737,U0,Y2,E1),s.Jc=function(){return this.a.kc()},s.md=function(){return this.a},s.Lc=function(){return this.a.lc()},v(dn,"AbstractMultimap/Entries",737),m(738,737,fs,xoe),s.Lc=function(){return this.a.lc()},s.Fb=function(n){return C0e(this,n)},s.Hb=function(){return FBe(this)},v(dn,"AbstractMultimap/EntrySet",738),m(739,31,Y2,DT),s.$b=function(){this.a.$b()},s.Gc=function(n){return xkn(this.a,n)},s.Jc=function(){return this.a.nc()},s.gc=function(){return this.a.d},s.Lc=function(){return this.a.oc()},v(dn,"AbstractMultimap/Values",739),m(2049,31,{833:1,20:1,31:1,18:1}),s.Ic=function(n){Nt(n),z3(this).Ic(new VU(n))},s.Lc=function(){var n;return n=z3(this).Lc(),fW(n,new Me,64|n.wd()&1296,this.a.d)},s.Ec=function(n){return Noe(),!0},s.Fc=function(n){return Nt(this),Nt(n),X(n,540)?Jyn(u(n,833)):!n.dc()&&AY(this,n.Jc())},s.Gc=function(n){var t;return t=u(L2(O4(this.a),n),18),(t?t.gc():0)>0},s.Fb=function(n){return vOn(this,n)},s.Hb=function(){return Oi(z3(this))},s.dc=function(){return z3(this).dc()},s.Kc=function(n){return Eqe(this,n,1)>0},s.Ib=function(){return su(z3(this))},v(dn,"AbstractMultiset",2049),m(2051,2030,fs),s.$b=function(){mB(this.a.a)},s.Gc=function(n){var t,i;return X(n,490)?(i=u(n,416),u(i.a.kd(),18).gc()<=0?!1:(t=cLe(this.a,i.a.jd()),t==u(i.a.kd(),18).gc())):!1},s.Kc=function(n){var t,i,r,c;return X(n,490)&&(i=u(n,416),t=i.a.jd(),r=u(i.a.kd(),18).gc(),r!=0)?(c=this.a,pCn(c,t,r)):!1},v(dn,"Multisets/EntrySet",2051),m(1108,2051,fs,rj),s.Jc=function(){return new Kxe(zDe(O4(this.a.a)).Jc())},s.gc=function(){return O4(this.a.a).gc()},v(dn,"AbstractMultiset/EntrySet",1108),m(618,730,og),s.hc=function(){return this.nd()},s.jc=function(){return this.od()},s.cc=function(n){return this.pd(n)},s.fc=function(n){return this.qd(n)},s.Zb=function(){var n;return n=this.f,n||(this.f=this.ac())},s.od=function(){return jn(),jn(),hJ},s.Fb=function(n){return iQ(this,n)},s.pd=function(n){return u(pi(this,n),22)},s.qd=function(n){return u(SO(this,n),22)},s.mc=function(n){return jn(),new f9(u(n,22))},s.pc=function(n,t){return new XOe(this,n,u(t,22))},v(dn,"AbstractSetMultimap",618),m(1689,618,og),s.hc=function(){return new vd(this.b)},s.nd=function(){return new vd(this.b)},s.jc=function(){return Ufe(new vd(this.b))},s.od=function(){return Ufe(new vd(this.b))},s.cc=function(n){return u(u(pi(this,n),22),83)},s.pd=function(n){return u(u(pi(this,n),22),83)},s.fc=function(n){return u(u(SO(this,n),22),83)},s.qd=function(n){return u(u(SO(this,n),22),83)},s.mc=function(n){return X(n,277)?Ufe(u(n,277)):(jn(),new ole(u(n,83)))},s.Zb=function(){var n;return n=this.f,n||(this.f=X(this.c,138)?new Zj(this,u(this.c,138)):X(this.c,134)?new SC(this,u(this.c,134)):new g9(this,this.c))},s.pc=function(n,t){return X(t,277)?new TCe(this,n,u(t,277)):new Sle(this,n,u(t,83))},v(dn,"AbstractSortedSetMultimap",1689),m(1690,1689,og),s.Zb=function(){var n;return n=this.f,u(u(n||(this.f=X(this.c,138)?new Zj(this,u(this.c,138)):X(this.c,134)?new SC(this,u(this.c,134)):new g9(this,this.c)),134),138)},s.ec=function(){var n;return n=this.i,u(u(n||(this.i=X(this.c,138)?new w9(this,u(this.c,138)):X(this.c,134)?new WT(this,u(this.c,134)):new R3(this,this.c)),83),277)},s.bc=function(){return X(this.c,138)?new w9(this,u(this.c,138)):X(this.c,134)?new WT(this,u(this.c,134)):new R3(this,this.c)},v(dn,"AbstractSortedKeySortedSetMultimap",1690),m(2071,1,{2008:1}),s.Fb=function(n){return sAn(this,n)},s.Hb=function(){var n;return a1e((n=this.g,n||(this.g=new b0(this))))},s.Ib=function(){var n;return oGe((n=this.f,n||(this.f=new ele(this))))},v(dn,"AbstractTable",2071),m(669,Ha,fs,b0),s.$b=function(){cAe()},s.Gc=function(n){var t,i;return X(n,468)?(t=u(n,687),i=u(L2(fIe(this.a),j0(t.c.e,t.b)),92),!!i&&$1e(i.vc(),new bw(j0(t.c.c,t.a),P4(t.c,t.b,t.a)))):!1},s.Jc=function(){return F5n(this.a)},s.Kc=function(n){var t,i;return X(n,468)?(t=u(n,687),i=u(L2(fIe(this.a),j0(t.c.e,t.b)),92),!!i&&Vkn(i.vc(),new bw(j0(t.c.c,t.a),P4(t.c,t.b,t.a)))):!1},s.gc=function(){return wDe(this.a)},s.Lc=function(){return Gyn(this.a)},v(dn,"AbstractTable/CellSet",669),m(1987,31,Y2,FU),s.$b=function(){cAe()},s.Gc=function(n){return WAn(this.a,n)},s.Jc=function(){return J5n(this.a)},s.gc=function(){return wDe(this.a)},s.Lc=function(){return OLe(this.a)},v(dn,"AbstractTable/Values",1987),m(1662,1661,og),v(dn,"ArrayListMultimapGwtSerializationDependencies",1662),m(506,1662,og,OX,Sae),s.hc=function(){return new xo(this.a)},s.a=0,v(dn,"ArrayListMultimap",506),m(668,2071,{668:1,2008:1,3:1},jqe),v(dn,"ArrayTable",668),m(1983,392,_8,nOe),s.Xb=function(n){return new w1e(this.a,n)},v(dn,"ArrayTable/1",1983),m(1984,1,{},JU),s.rd=function(n){return new w1e(this.a,n)},v(dn,"ArrayTable/1methodref$getCell$Type",1984),m(2072,1,{687:1}),s.Fb=function(n){var t;return n===this?!0:X(n,468)?(t=u(n,687),T1(j0(this.c.e,this.b),j0(t.c.e,t.b))&&T1(j0(this.c.c,this.a),j0(t.c.c,t.a))&&T1(P4(this.c,this.b,this.a),P4(t.c,t.b,t.a))):!1},s.Hb=function(){return RB(z(B(Ar,1),On,1,5,[j0(this.c.e,this.b),j0(this.c.c,this.a),P4(this.c,this.b,this.a)]))},s.Ib=function(){return"("+j0(this.c.e,this.b)+","+j0(this.c.c,this.a)+")="+P4(this.c,this.b,this.a)},v(dn,"Tables/AbstractCell",2072),m(468,2072,{468:1,687:1},w1e),s.a=0,s.b=0,s.d=0,v(dn,"ArrayTable/2",468),m(1986,1,{},X5),s.rd=function(n){return z$e(this.a,n)},v(dn,"ArrayTable/2methodref$getValue$Type",1986),m(1985,392,_8,tOe),s.Xb=function(n){return z$e(this.a,n)},v(dn,"ArrayTable/3",1985),m(2039,2027,Vw),s.$b=function(){uR(this.kc())},s.vc=function(){return new uj(this)},s.lc=function(){return new HIe(this.kc(),this.gc())},v(dn,"Maps/IteratorBasedAbstractMap",2039),m(826,2039,Vw),s.$b=function(){throw $(new _t)},s._b=function(n){return jAe(this.c,n)},s.kc=function(){return new iOe(this,this.c.b.c.gc())},s.lc=function(){return iV(this.c.b.c.gc(),16,new gP(this))},s.xc=function(n){var t;return t=u(eE(this.c,n),15),t?this.td(t.a):null},s.dc=function(){return this.c.b.c.dc()},s.ec=function(){return hV(this.c)},s.yc=function(n,t){var i;if(i=u(eE(this.c,n),15),!i)throw $(new Gn(this.sd()+" "+n+" not in "+hV(this.c)));return this.ud(i.a,t)},s.Ac=function(n){throw $(new _t)},s.gc=function(){return this.c.b.c.gc()},v(dn,"ArrayTable/ArrayMap",826),m(1982,1,{},gP),s.rd=function(n){return dIe(this.a,n)},v(dn,"ArrayTable/ArrayMap/0methodref$getEntry$Type",1982),m(1980,358,hZ,YAe),s.jd=function(){return apn(this.a,this.b)},s.kd=function(){return this.a.td(this.b)},s.ld=function(n){return this.a.ud(this.b,n)},s.b=0,v(dn,"ArrayTable/ArrayMap/1",1980),m(1981,392,_8,iOe),s.Xb=function(n){return dIe(this.a,n)},v(dn,"ArrayTable/ArrayMap/2",1981),m(1979,826,Vw,nIe),s.sd=function(){return"Column"},s.td=function(n){return P4(this.b,this.a,n)},s.ud=function(n,t){return jze(this.b,this.a,n,t)},s.a=0,v(dn,"ArrayTable/Row",1979),m(827,826,Vw,ele),s.td=function(n){return new nIe(this.a,n)},s.yc=function(n,t){return u(t,92),_bn()},s.ud=function(n,t){return u(t,92),Lbn()},s.sd=function(){return"Row"},v(dn,"ArrayTable/RowMap",827),m(1126,1,bl,QAe),s.yd=function(n){return(this.a.wd()&-262&n)!=0},s.wd=function(){return this.a.wd()&-262},s.xd=function(){return this.a.xd()},s.Nb=function(n){this.a.Nb(new ZAe(n,this.b))},s.zd=function(n){return this.a.zd(new WAe(n,this.b))},v(dn,"CollectSpliterators/1",1126),m(1127,1,it,WAe),s.Ad=function(n){this.a.Ad(this.b.Kb(n))},v(dn,"CollectSpliterators/1/lambda$0$Type",1127),m(1128,1,it,ZAe),s.Ad=function(n){this.a.Ad(this.b.Kb(n))},v(dn,"CollectSpliterators/1/lambda$1$Type",1128),m(1123,1,bl,jNe),s.yd=function(n){return((16464|this.b)&n)!=0},s.wd=function(){return 16464|this.b},s.xd=function(){return this.a.xd()},s.Nb=function(n){this.a.Oe(new nMe(n,this.c))},s.zd=function(n){return this.a.Pe(new eMe(n,this.c))},s.b=0,v(dn,"CollectSpliterators/1WithCharacteristics",1123),m(1124,1,aN,eMe),s.Bd=function(n){this.a.Ad(this.b.rd(n))},v(dn,"CollectSpliterators/1WithCharacteristics/lambda$0$Type",1124),m(1125,1,aN,nMe),s.Bd=function(n){this.a.Ad(this.b.rd(n))},v(dn,"CollectSpliterators/1WithCharacteristics/lambda$1$Type",1125),m(1119,1,bl),s.yd=function(n){return(this.a&n)!=0},s.wd=function(){return this.a},s.xd=function(){return this.e&&(this.b=Gse(this.b,this.e.xd())),Gse(this.b,0)},s.Nb=function(n){this.e&&(this.e.Nb(n),this.e=null),this.c.Nb(new tMe(this,n)),this.b=0},s.zd=function(n){for(;;){if(this.e&&this.e.zd(n))return Gj(this.b,hN)&&(this.b=lf(this.b,1)),!0;if(this.e=null,!this.c.zd(new K5(this)))return!1}},s.a=0,s.b=0,v(dn,"CollectSpliterators/FlatMapSpliterator",1119),m(1121,1,it,K5),s.Ad=function(n){c2n(this.a,n)},v(dn,"CollectSpliterators/FlatMapSpliterator/lambda$0$Type",1121),m(1122,1,it,tMe),s.Ad=function(n){p5n(this.a,this.b,n)},v(dn,"CollectSpliterators/FlatMapSpliterator/lambda$1$Type",1122),m(1120,1119,bl,sPe),v(dn,"CollectSpliterators/FlatMapSpliteratorOfObject",1120),m(254,1,dZ),s.Dd=function(n){return this.Cd(u(n,254))},s.Cd=function(n){var t;return n==(EX(),Yne)?1:n==(jX(),Vne)?-1:(t=(eR(),dO(this.a,n.a)),t!=0?t:(Pn(),X(this,513)==X(n,513)?0:X(this,513)?1:-1))},s.Gd=function(){return this.a},s.Fb=function(n){return Lde(this,n)},v(dn,"Cut",254),m(1793,254,dZ,Fxe),s.Cd=function(n){return n==this?0:1},s.Ed=function(n){throw $(new loe)},s.Fd=function(n){n.a+="+∞)"},s.Gd=function(){throw $(new Uc(hYe))},s.Hb=function(){return yd(),jde(this)},s.Hd=function(n){return!1},s.Ib=function(){return"+∞"};var Vne;v(dn,"Cut/AboveAll",1793),m(513,254,{254:1,513:1,3:1,35:1},oOe),s.Ed=function(n){uo((n.a+="(",n),this.a)},s.Fd=function(n){Fb(uo(n,this.a),93)},s.Hb=function(){return~Oi(this.a)},s.Hd=function(n){return eR(),dO(this.a,n)<0},s.Ib=function(){return"/"+this.a+"\\"},v(dn,"Cut/AboveValue",513),m(1792,254,dZ,zxe),s.Cd=function(n){return n==this?0:-1},s.Ed=function(n){n.a+="(-∞"},s.Fd=function(n){throw $(new loe)},s.Gd=function(){throw $(new Uc(hYe))},s.Hb=function(){return yd(),jde(this)},s.Hd=function(n){return!0},s.Ib=function(){return"-∞"};var Yne;v(dn,"Cut/BelowAll",1792),m(1794,254,dZ,sOe),s.Ed=function(n){uo((n.a+="[",n),this.a)},s.Fd=function(n){Fb(uo(n,this.a),41)},s.Hb=function(){return Oi(this.a)},s.Hd=function(n){return eR(),dO(this.a,n)<=0},s.Ib=function(){return"\\"+this.a+"/"},v(dn,"Cut/BelowValue",1794),m(535,1,Wh),s.Ic=function(n){rc(this,n)},s.Ib=function(){return xjn(u(CR(this,"use Optional.orNull() instead of Optional.or(null)"),20).Jc())},v(dn,"FluentIterable",535),m(433,535,Wh,Xj),s.Jc=function(){return new qn(Vn(this.a.Jc(),new ee))},v(dn,"FluentIterable/2",433),m(36,1,{},ee),s.Kb=function(n){return u(n,20).Jc()},s.Fb=function(n){return this===n},v(dn,"FluentIterable/2/0methodref$iterator$Type",36),m(1040,535,Wh,jCe),s.Jc=function(){return qh(this)},v(dn,"FluentIterable/3",1040),m(714,392,_8,sle),s.Xb=function(n){return this.a[n].Jc()},v(dn,"FluentIterable/3/1",714),m(2032,1,{}),s.Ib=function(){return su(this.Id().b)},v(dn,"ForwardingObject",2032),m(2033,2032,dYe),s.Id=function(){return this.Jd()},s.Ic=function(n){rc(this,n)},s.Lc=function(){return new pn(this,0)},s.Mc=function(){return new wn(null,this.Lc())},s.Ec=function(n){return this.Jd(),AAe()},s.Fc=function(n){return this.Jd(),MAe()},s.$b=function(){this.Jd(),TAe()},s.Gc=function(n){return this.Jd().Gc(n)},s.Hc=function(n){return this.Jd().Hc(n)},s.dc=function(){return this.Jd().b.dc()},s.Jc=function(){return this.Jd().Jc()},s.Kc=function(n){return this.Jd(),CAe()},s.gc=function(){return this.Jd().b.gc()},s.Nc=function(){return this.Jd().Nc()},s.Oc=function(n){return this.Jd().Oc(n)},v(dn,"ForwardingCollection",2033),m(2040,31,Bge),s.Jc=function(){return this.Md()},s.Ec=function(n){throw $(new _t)},s.Fc=function(n){throw $(new _t)},s.Kd=function(){var n;return n=this.c,n||(this.c=this.Ld())},s.$b=function(){throw $(new _t)},s.Gc=function(n){return n!=null&&P2(this,n,!1)},s.Ld=function(){switch(this.gc()){case 0:return cR(),Zne;case 1:return new HK(Nt(this.Md().Pb()));default:return new cfe(this,this.Nc())}},s.Kc=function(n){throw $(new _t)},v(dn,"ImmutableCollection",2040),m(1259,2040,Bge,pP),s.Jc=function(){return $4(new tc(this.a.b.Jc()))},s.Gc=function(n){return n!=null&&Sj(this.a,n)},s.Hc=function(n){return Koe(this.a,n)},s.dc=function(){return this.a.b.dc()},s.Md=function(){return $4(new tc(this.a.b.Jc()))},s.gc=function(){return this.a.b.gc()},s.Nc=function(){return this.a.b.Nc()},s.Oc=function(n){return Voe(this.a,n)},s.Ib=function(){return su(this.a.b)},v(dn,"ForwardingImmutableCollection",1259),m(311,2040,L8),s.Jc=function(){return this.Md()},s.cd=function(){return this.Nd(0)},s.dd=function(n){return this.Nd(n)},s.gd=function(n){Vb(this,n)},s.Lc=function(){return new pn(this,16)},s.hd=function(n,t){return this.Od(n,t)},s._c=function(n,t){throw $(new _t)},s.ad=function(n,t){throw $(new _t)},s.Kd=function(){return this},s.Fb=function(n){return lOn(this,n)},s.Hb=function(){return P7n(this)},s.bd=function(n){return n==null?-1:HSn(this,n)},s.Md=function(){return this.Nd(0)},s.Nd=function(n){return LK(this,n)},s.ed=function(n){throw $(new _t)},s.fd=function(n,t){throw $(new _t)},s.Od=function(n,t){var i;return qB((i=new fMe(this),new T0(i,n,t)))},v(dn,"ImmutableList",311),m(2067,311,L8),s.Jc=function(){return $4(this.Pd().Jc())},s.hd=function(n,t){return qB(this.Pd().hd(n,t))},s.Gc=function(n){return n!=null&&this.Pd().Gc(n)},s.Hc=function(n){return this.Pd().Hc(n)},s.Fb=function(n){return di(this.Pd(),n)},s.Xb=function(n){return j0(this,n)},s.Hb=function(){return Oi(this.Pd())},s.bd=function(n){return this.Pd().bd(n)},s.dc=function(){return this.Pd().dc()},s.Md=function(){return $4(this.Pd().Jc())},s.gc=function(){return this.Pd().gc()},s.Od=function(n,t){return qB(this.Pd().hd(n,t))},s.Nc=function(){return this.Pd().Oc(oe(Ar,On,1,this.Pd().gc(),5,1))},s.Oc=function(n){return this.Pd().Oc(n)},s.Ib=function(){return su(this.Pd())},v(dn,"ForwardingImmutableList",2067),m(717,1,P8),s.vc=function(){return $b(this)},s.wc=function(n){bO(this,n)},s.ec=function(){return hV(this)},s.Bc=function(){return this.Td()},s.$b=function(){throw $(new _t)},s._b=function(n){return this.xc(n)!=null},s.uc=function(n){return this.Td().Gc(n)},s.Rd=function(){return new qU(this)},s.Sd=function(){return new UU(this)},s.Fb=function(n){return Akn(this,n)},s.Hb=function(){return $b(this).Hb()},s.dc=function(){return this.gc()==0},s.yc=function(n,t){return Pbn()},s.Ac=function(n){throw $(new _t)},s.Ib=function(){return UMn(this)},s.Td=function(){return this.e?this.e:this.e=this.Sd()},s.c=null,s.d=null,s.e=null,v(dn,"ImmutableMap",717),m(718,717,P8),s._b=function(n){return jAe(this,n)},s.uc=function(n){return pMe(this.b,n)},s.Qd=function(){return cFe(new wP(this))},s.Rd=function(){return cFe(LIe(this.b))},s.Sd=function(){return new pP(PIe(this.b))},s.Fb=function(n){return vMe(this.b,n)},s.xc=function(n){return eE(this,n)},s.Hb=function(){return Oi(this.b.c)},s.dc=function(){return this.b.c.dc()},s.gc=function(){return this.b.c.gc()},s.Ib=function(){return su(this.b.c)},v(dn,"ForwardingImmutableMap",718),m(2034,2033,bZ),s.Id=function(){return this.Ud()},s.Jd=function(){return this.Ud()},s.Lc=function(){return new pn(this,1)},s.Fb=function(n){return n===this||this.Ud().Fb(n)},s.Hb=function(){return this.Ud().Hb()},v(dn,"ForwardingSet",2034),m(1055,2034,bZ,wP),s.Id=function(){return _9(this.a.b)},s.Jd=function(){return _9(this.a.b)},s.Gc=function(n){if(X(n,45)&&u(n,45).jd()==null)return!1;try{return mMe(_9(this.a.b),n)}catch(t){if(t=sr(t),X(t,211))return!1;throw $(t)}},s.Ud=function(){return _9(this.a.b)},s.Oc=function(n){var t,i;return t=k_e(_9(this.a.b),n),_9(this.a.b).b.gc()=0?"+":"")+(i/60|0),t=D$(k.Math.abs(i)%60),(vGe(),snn)[this.q.getDay()]+" "+lnn[this.q.getMonth()]+" "+D$(this.q.getDate())+" "+D$(this.q.getHours())+":"+D$(this.q.getMinutes())+":"+D$(this.q.getSeconds())+" GMT"+n+t+" "+this.q.getFullYear()};var lJ=v(mt,"Date",205);m(1977,205,jYe,zHe),s.a=!1,s.b=0,s.c=0,s.d=0,s.e=0,s.f=0,s.g=!1,s.i=0,s.j=0,s.k=0,s.n=0,s.o=0,s.p=0,v("com.google.gwt.i18n.shared.impl","DateRecord",1977),m(2026,1,{}),s.ne=function(){return null},s.oe=function(){return null},s.pe=function(){return null},s.qe=function(){return null},s.re=function(){return null},v(oy,"JSONValue",2026),m(139,2026,{139:1},bd,n9),s.Fb=function(n){return X(n,139)?Mae(this.a,u(n,139).a):!1},s.me=function(){return tbn},s.Hb=function(){return aae(this.a)},s.ne=function(){return this},s.Ib=function(){var n,t,i;for(i=new il("["),t=0,n=this.a.length;t0&&(i.a+=","),uo(i,T2(this,t));return i.a+="]",i.a},v(oy,"JSONArray",139),m(479,2026,{479:1},t9),s.me=function(){return ibn},s.oe=function(){return this},s.Ib=function(){return Pn(),""+this.a},s.a=!1;var Yen,Qen;v(oy,"JSONBoolean",479),m(981,63,H1,Vxe),v(oy,"JSONException",981),m(1017,2026,{},wt),s.me=function(){return obn},s.Ib=function(){return Vo};var Wen;v(oy,"JSONNull",1017),m(265,2026,{265:1},k3),s.Fb=function(n){return X(n,265)?this.a==u(n,265).a:!1},s.me=function(){return rbn},s.Hb=function(){return b4(this.a)},s.pe=function(){return this},s.Ib=function(){return this.a+""},s.a=0,v(oy,"JSONNumber",265),m(149,2026,{149:1},r4,i9),s.Fb=function(n){return X(n,149)?Mae(this.a,u(n,149).a):!1},s.me=function(){return cbn},s.Hb=function(){return aae(this.a)},s.qe=function(){return this},s.Ib=function(){var n,t,i,r,c,o,l;for(l=new il("{"),n=!0,o=BY(this,oe(ze,Se,2,0,6,1)),i=o,r=0,c=i.length;r=0?":"+this.c:"")+")"},s.c=0;var _me=v(Tu,"StackTraceElement",324);Jen={3:1,472:1,35:1,2:1};var ze=v(Tu,zge,2);m(111,418,{472:1},pd,jj,cf),v(Tu,"StringBuffer",111),m(106,418,{472:1},p0,o4,il),v(Tu,"StringBuilder",106),m(691,99,iF,Doe),v(Tu,"StringIndexOutOfBoundsException",691),m(2107,1,{});var tnn;m(46,63,{3:1,101:1,63:1,80:1,46:1},_t,gd),v(Tu,"UnsupportedOperationException",46),m(247,242,{3:1,35:1,242:1,247:1},MO,Joe),s.Dd=function(n){return vKe(this,u(n,247))},s.se=function(){return F2(UKe(this))},s.Fb=function(n){var t;return this===n?!0:X(n,247)?(t=u(n,247),this.e==t.e&&vKe(this,t)==0):!1},s.Hb=function(){var n;return this.b!=0?this.b:this.a<54?(n=Pu(this.f),this.b=Rt(Rr(n,-1)),this.b=33*this.b+Rt(Rr(kw(n,32),-1)),this.b=17*this.b+sc(this.e),this.b):(this.b=17*wFe(this.c)+sc(this.e),this.b)},s.Ib=function(){return UKe(this)},s.a=0,s.b=0,s.d=0,s.e=0,s.f=0;var inn,pg,Lme,Pme,$me,Rme,Bme,zme,cte=v("java.math","BigDecimal",247);m(91,242,{3:1,35:1,242:1,91:1},D1,hLe,zb,xJe,E0),s.Dd=function(n){return yJe(this,u(n,91))},s.se=function(){return F2(lZ(this,0))},s.Fb=function(n){return ude(this,n)},s.Hb=function(){return wFe(this)},s.Ib=function(){return lZ(this,0)},s.b=-2,s.c=0,s.d=0,s.e=0;var rnn,fJ,cnn,ute,aJ,KS,Sv=v("java.math","BigInteger",91),unn,onn,my,VS;m(484,2027,Vw),s.$b=function(){Hu(this)},s._b=function(n){return so(this,n)},s.uc=function(n){return eFe(this,n,this.i)||eFe(this,n,this.f)},s.vc=function(){return new on(this)},s.xc=function(n){return Bn(this,n)},s.yc=function(n,t){return ei(this,n,t)},s.Ac=function(n){return L4(this,n)},s.gc=function(){return xj(this)},s.g=0,v(mt,"AbstractHashMap",484),m(306,Ha,fs,on),s.$b=function(){this.a.$b()},s.Gc=function(n){return JLe(this,n)},s.Jc=function(){return new D2(this.a)},s.Kc=function(n){var t;return JLe(this,n)?(t=u(n,45).jd(),this.a.Ac(t),!0):!1},s.gc=function(){return this.a.gc()},v(mt,"AbstractHashMap/EntrySet",306),m(307,1,Fr,D2),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return Q3(this)},s.Ob=function(){return this.b},s.Qb=function(){gRe(this)},s.b=!1,s.d=0,v(mt,"AbstractHashMap/EntrySetIterator",307),m(417,1,Fr,qc),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return zX(this)},s.Pb=function(){return oae(this)},s.Qb=function(){As(this)},s.b=0,s.c=-1,v(mt,"AbstractList/IteratorImpl",417),m(97,417,Qh,qr),s.Qb=function(){As(this)},s.Rb=function(n){d2(this,n)},s.Sb=function(){return this.b>0},s.Tb=function(){return this.b},s.Ub=function(){return at(this.b>0),this.a.Xb(this.c=--this.b)},s.Vb=function(){return this.b-1},s.Wb=function(n){l2(this.c!=-1),this.a.fd(this.c,n)},v(mt,"AbstractList/ListIteratorImpl",97),m(258,56,$8,T0),s._c=function(n,t){S2(n,this.b),this.c._c(this.a+n,t),++this.b},s.Xb=function(n){return vn(n,this.b),this.c.Xb(this.a+n)},s.ed=function(n){var t;return vn(n,this.b),t=this.c.ed(this.a+n),--this.b,t},s.fd=function(n,t){return vn(n,this.b),this.c.fd(this.a+n,t)},s.gc=function(){return this.b},s.a=0,s.b=0,v(mt,"AbstractList/SubList",258),m(232,Ha,fs,nt),s.$b=function(){this.a.$b()},s.Gc=function(n){return this.a._b(n)},s.Jc=function(){var n;return n=this.a.vc().Jc(),new gt(n)},s.Kc=function(n){return this.a._b(n)?(this.a.Ac(n),!0):!1},s.gc=function(){return this.a.gc()},v(mt,"AbstractMap/1",232),m(529,1,Fr,gt),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.a.Ob()},s.Pb=function(){var n;return n=u(this.a.Pb(),45),n.jd()},s.Qb=function(){this.a.Qb()},v(mt,"AbstractMap/1/1",529),m(230,31,Y2,ct),s.$b=function(){this.a.$b()},s.Gc=function(n){return this.a.uc(n)},s.Jc=function(){var n;return n=this.a.vc().Jc(),new Ji(n)},s.gc=function(){return this.a.gc()},v(mt,"AbstractMap/2",230),m(304,1,Fr,Ji),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.a.Ob()},s.Pb=function(){var n;return n=u(this.a.Pb(),45),n.kd()},s.Qb=function(){this.a.Qb()},v(mt,"AbstractMap/2/1",304),m(480,1,{480:1,45:1}),s.Fb=function(n){var t;return X(n,45)?(t=u(n,45),Ku(this.d,t.jd())&&Ku(this.e,t.kd())):!1},s.jd=function(){return this.d},s.kd=function(){return this.e},s.Hb=function(){return _3(this.d)^_3(this.e)},s.ld=function(n){return Dle(this,n)},s.Ib=function(){return this.d+"="+this.e},v(mt,"AbstractMap/AbstractEntry",480),m(390,480,{480:1,390:1,45:1},r$),v(mt,"AbstractMap/SimpleEntry",390),m(2044,1,RZ),s.Fb=function(n){var t;return X(n,45)?(t=u(n,45),Ku(this.jd(),t.jd())&&Ku(this.kd(),t.kd())):!1},s.Hb=function(){return _3(this.jd())^_3(this.kd())},s.Ib=function(){return this.jd()+"="+this.kd()},v(mt,fYe,2044),m(2052,2027,$ge),s.Vc=function(n){return _X(this.Ce(n))},s.tc=function(n){return PPe(this,n)},s._b=function(n){return Ile(this,n)},s.vc=function(){return new Ui(this)},s.Rc=function(){return tIe(this.Ee())},s.Wc=function(n){return _X(this.Fe(n))},s.xc=function(n){var t;return t=n,du(this.De(t))},s.Yc=function(n){return _X(this.Ge(n))},s.ec=function(){return new Lu(this)},s.Tc=function(){return tIe(this.He())},s.Zc=function(n){return _X(this.Ie(n))},v(mt,"AbstractNavigableMap",2052),m(620,Ha,fs,Ui),s.Gc=function(n){return X(n,45)&&PPe(this.b,u(n,45))},s.Jc=function(){return this.b.Be()},s.Kc=function(n){var t;return X(n,45)?(t=u(n,45),this.b.Je(t)):!1},s.gc=function(){return this.b.gc()},v(mt,"AbstractNavigableMap/EntrySet",620),m(1115,Ha,Rge,Lu),s.Lc=function(){return new o$(this)},s.$b=function(){this.a.$b()},s.Gc=function(n){return Ile(this.a,n)},s.Jc=function(){var n;return n=this.a.vc().b.Be(),new _ke(n)},s.Kc=function(n){return Ile(this.a,n)?(this.a.Ac(n),!0):!1},s.gc=function(){return this.a.gc()},v(mt,"AbstractNavigableMap/NavigableKeySet",1115),m(1116,1,Fr,_ke),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return zX(this.a.a)},s.Pb=function(){var n;return n=TOe(this.a),n.jd()},s.Qb=function(){NNe(this.a)},v(mt,"AbstractNavigableMap/NavigableKeySet/1",1116),m(2065,31,Y2),s.Ec=function(n){return E4(v8(this,n),B8),!0},s.Fc=function(n){return _n(n),IC(n!=this,"Can't add a queue to itself"),fc(this,n)},s.$b=function(){for(;MY(this)!=null;);},v(mt,"AbstractQueue",2065),m(314,31,{4:1,20:1,31:1,18:1},P3,ILe),s.Ec=function(n){return Lae(this,n),!0},s.$b=function(){zae(this)},s.Gc=function(n){return mze(new hE(this),n)},s.dc=function(){return kj(this)},s.Jc=function(){return new hE(this)},s.Kc=function(n){return j4n(new hE(this),n)},s.gc=function(){return this.c-this.b&this.a.length-1},s.Lc=function(){return new pn(this,272)},s.Oc=function(n){var t;return t=this.c-this.b&this.a.length-1,n.lengtht&&tr(n,t,null),n},s.b=0,s.c=0,v(mt,"ArrayDeque",314),m(448,1,Fr,hE),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.a!=this.b},s.Pb=function(){return zB(this)},s.Qb=function(){mBe(this)},s.a=0,s.b=0,s.c=-1,v(mt,"ArrayDeque/IteratorImpl",448),m(13,56,AYe,Ce,xo,bs),s._c=function(n,t){Pb(this,n,t)},s.Ec=function(n){return Te(this,n)},s.ad=function(n,t){return N1e(this,n,t)},s.Fc=function(n){return Er(this,n)},s.$b=function(){Qp(this.c,0)},s.Gc=function(n){return wu(this,n,0)!=-1},s.Ic=function(n){Ao(this,n)},s.Xb=function(n){return Le(this,n)},s.bd=function(n){return wu(this,n,0)},s.dc=function(){return this.c.length==0},s.Jc=function(){return new L(this)},s.ed=function(n){return Ad(this,n)},s.Kc=function(n){return qo(this,n)},s.ae=function(n,t){rLe(this,n,t)},s.fd=function(n,t){return ol(this,n,t)},s.gc=function(){return this.c.length},s.gd=function(n){Cr(this,n)},s.Nc=function(){return nR(this.c)},s.Oc=function(n){return Ra(this,n)};var EBn=v(mt,"ArrayList",13);m(7,1,Fr,L),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return bu(this)},s.Pb=function(){return _(this)},s.Qb=function(){oE(this)},s.a=0,s.b=-1,v(mt,"ArrayList/1",7),m(2074,k.Function,{},mn),s.Ke=function(n,t){return ki(n,t)},m(123,56,MYe,Su),s.Gc=function(n){return pBe(this,n)!=-1},s.Ic=function(n){var t,i,r,c;for(_n(n),i=this.a,r=0,c=i.length;r0)throw $(new Gn(Kge+n+" greater than "+this.e));return this.f.Re()?M_e(this.c,this.b,this.a,n,t):tLe(this.c,n,t)},s.yc=function(n,t){if(!ZQ(this.c,this.f,n,this.b,this.a,this.e,this.d))throw $(new Gn(n+" outside the range "+this.b+" to "+this.e));return Pze(this.c,n,t)},s.Ac=function(n){var t;return t=n,ZQ(this.c,this.f,t,this.b,this.a,this.e,this.d)?T_e(this.c,t):null},s.Je=function(n){return ER(this,n.jd())&&uhe(this.c,n)},s.gc=function(){var n,t,i;if(this.f.Re()?this.a?t=h8(this.c,this.b,!0):t=h8(this.c,this.b,!1):t=mhe(this.c),!(t&&ER(this,t.d)&&t))return 0;for(n=0,i=new zY(this.c,this.f,this.b,this.a,this.e,this.d);zX(i.a);i.b=u(oae(i.a),45))++n;return n},s.$c=function(n,t){if(this.f.Re()&&this.c.a.Le(n,this.b)<0)throw $(new Gn(Kge+n+OYe+this.b));return this.f.Se()?M_e(this.c,n,t,this.e,this.d):iLe(this.c,n,t)},s.a=!1,s.d=!1,v(mt,"TreeMap/SubMap",622),m(309,23,JZ,c$),s.Re=function(){return!1},s.Se=function(){return!1};var lte,fte,ate,hte,dJ=yt(mt,"TreeMap/SubMapType",309,Ct,Wyn,S2n);m(1112,309,JZ,ACe),s.Se=function(){return!0},yt(mt,"TreeMap/SubMapType/1",1112,dJ,null,null),m(1113,309,JZ,RCe),s.Re=function(){return!0},s.Se=function(){return!0},yt(mt,"TreeMap/SubMapType/2",1113,dJ,null,null),m(1114,309,JZ,MCe),s.Re=function(){return!0},yt(mt,"TreeMap/SubMapType/3",1114,dJ,null,null);var gnn;m(141,Ha,{3:1,20:1,31:1,18:1,277:1,22:1,83:1,141:1},wX,lle,vd,c9),s.Lc=function(){return new o$(this)},s.Ec=function(n){return PC(this,n)},s.$b=function(){this.a.$b()},s.Gc=function(n){return this.a._b(n)},s.Jc=function(){return this.a.ec().Jc()},s.Kc=function(n){return DK(this,n)},s.gc=function(){return this.a.gc()};var CBn=v(mt,"TreeSet",141);m(1052,1,{},$ke),s.Te=function(n,t){return Gpn(this.a,n,t)},v(HZ,"BinaryOperator/lambda$0$Type",1052),m(1053,1,{},Rke),s.Te=function(n,t){return qpn(this.a,n,t)},v(HZ,"BinaryOperator/lambda$1$Type",1053),m(935,1,{},Eu),s.Kb=function(n){return n},v(HZ,"Function/lambda$0$Type",935),m(388,1,Ft,u9),s.Mb=function(n){return!this.a.Mb(n)},v(HZ,"Predicate/lambda$2$Type",388),m(567,1,{567:1});var wnn=v(pS,"Handler",567);m(2069,1,lN),s.ve=function(){return"DUMMY"},s.Ib=function(){return this.ve()};var Xme;v(pS,"Level",2069),m(1672,2069,lN,Ws),s.ve=function(){return"INFO"},v(pS,"Level/LevelInfo",1672),m(1824,1,{},txe);var dte;v(pS,"LogManager",1824),m(1866,1,lN,ONe),s.b=null,v(pS,"LogRecord",1866),m(511,1,{511:1},uY),s.e=!1;var pnn=!1,mnn=!1,Ka=!1,vnn=!1,ynn=!1;v(pS,"Logger",511),m(819,567,{567:1},Mr),v(pS,"SimpleConsoleLogHandler",819),m(130,23,{3:1,35:1,23:1,130:1},HX);var Kme,Yo,Vme,Qo=yt(_c,"Collector/Characteristics",130,Ct,P4n,x2n),knn;m(746,1,{},Bfe),v(_c,"CollectorImpl",746),m(1050,1,{},Wr),s.Te=function(n,t){return sjn(u(n,212),u(t,212))},v(_c,"Collectors/10methodref$merge$Type",1050),m(1051,1,{},ur),s.Kb=function(n){return DLe(u(n,212))},v(_c,"Collectors/11methodref$toString$Type",1051),m(152,1,{},mi),s.Wd=function(n,t){u(n,18).Ec(t)},v(_c,"Collectors/20methodref$add$Type",152),m(154,1,{},Fi),s.Ve=function(){return new Ce},v(_c,"Collectors/21methodref$ctor$Type",154),m(1049,1,{},fu),s.Wd=function(n,t){I1(u(n,212),u(t,472))},v(_c,"Collectors/9methodref$add$Type",1049),m(1048,1,{},VNe),s.Ve=function(){return new Qb(this.a,this.b,this.c)},v(_c,"Collectors/lambda$15$Type",1048),m(153,1,{},kc),s.Te=function(n,t){return ygn(u(n,18),u(t,18))},v(_c,"Collectors/lambda$45$Type",153),m(538,1,{}),s.Ye=function(){aE(this)},s.d=!1,v(_c,"TerminatableStream",538),m(768,538,Vge,xle),s.Ye=function(){aE(this)},v(_c,"DoubleStreamImpl",768),m(1297,724,bl,YNe),s.Pe=function(n){return LSn(this,u(n,189))},s.a=null,v(_c,"DoubleStreamImpl/2",1297),m(1298,1,mN,Bke),s.Ne=function(n){dwn(this.a,n)},v(_c,"DoubleStreamImpl/2/lambda$0$Type",1298),m(1295,1,mN,zke),s.Ne=function(n){hwn(this.a,n)},v(_c,"DoubleStreamImpl/lambda$0$Type",1295),m(1296,1,mN,Fke),s.Ne=function(n){sJe(this.a,n)},v(_c,"DoubleStreamImpl/lambda$2$Type",1296),m(1351,723,bl,JPe),s.Pe=function(n){return Fyn(this,u(n,202))},s.a=0,s.b=0,s.c=0,v(_c,"IntStream/5",1351),m(793,538,Vge,Ale),s.Ye=function(){aE(this)},s.Ze=function(){return A0(this),this.a},v(_c,"IntStreamImpl",793),m(794,538,Vge,Yoe),s.Ye=function(){aE(this)},s.Ze=function(){return A0(this),Yse(),bnn},v(_c,"IntStreamImpl/Empty",794),m(1651,1,aN,Jke),s.Bd=function(n){eze(this.a,n)},v(_c,"IntStreamImpl/lambda$4$Type",1651);var OBn=Hi(_c,"Stream");m(28,538,{520:1,677:1,832:1},wn),s.Ye=function(){aE(this)};var vy;v(_c,"StreamImpl",28),m(1072,486,bl,kNe),s.zd=function(n){for(;F9n(this);){if(this.a.zd(n))return!0;aE(this.b),this.b=null,this.a=null}return!1},v(_c,"StreamImpl/1",1072),m(1073,1,it,Hke),s.Ad=function(n){C3n(this.a,u(n,832))},v(_c,"StreamImpl/1/lambda$0$Type",1073),m(1074,1,Ft,Gke),s.Mb=function(n){return hr(this.a,n)},v(_c,"StreamImpl/1methodref$add$Type",1074),m(1075,486,bl,e_e),s.zd=function(n){var t;return this.a||(t=new Ce,this.b.a.Nb(new qke(t)),jn(),Cr(t,this.c),this.a=new pn(t,16)),JRe(this.a,n)},s.a=null,v(_c,"StreamImpl/5",1075),m(1076,1,it,qke),s.Ad=function(n){Te(this.a,n)},v(_c,"StreamImpl/5/2methodref$add$Type",1076),m(725,486,bl,whe),s.zd=function(n){for(this.b=!1;!this.b&&this.c.zd(new BMe(this,n)););return this.b},s.b=!1,v(_c,"StreamImpl/FilterSpliterator",725),m(1066,1,it,BMe),s.Ad=function(n){kvn(this.a,this.b,n)},v(_c,"StreamImpl/FilterSpliterator/lambda$0$Type",1066),m(1061,724,bl,WPe),s.Pe=function(n){return g2n(this,u(n,189))},v(_c,"StreamImpl/MapToDoubleSpliterator",1061),m(1065,1,it,zMe),s.Ad=function(n){Pgn(this.a,this.b,n)},v(_c,"StreamImpl/MapToDoubleSpliterator/lambda$0$Type",1065),m(1060,723,bl,ZPe),s.Pe=function(n){return w2n(this,u(n,202))},v(_c,"StreamImpl/MapToIntSpliterator",1060),m(1064,1,it,FMe),s.Ad=function(n){$gn(this.a,this.b,n)},v(_c,"StreamImpl/MapToIntSpliterator/lambda$0$Type",1064),m(722,486,bl,the),s.zd=function(n){return ENe(this,n)},v(_c,"StreamImpl/MapToObjSpliterator",722),m(1063,1,it,JMe),s.Ad=function(n){Rgn(this.a,this.b,n)},v(_c,"StreamImpl/MapToObjSpliterator/lambda$0$Type",1063),m(1062,486,bl,vBe),s.zd=function(n){for(;FX(this.b,0);){if(!this.a.zd(new ef))return!1;this.b=lf(this.b,1)}return this.a.zd(n)},s.b=0,v(_c,"StreamImpl/SkipSpliterator",1062),m(1067,1,it,ef),s.Ad=function(n){},v(_c,"StreamImpl/SkipSpliterator/lambda$0$Type",1067),m(617,1,it,ch),s.Ad=function(n){jP(this,n)},v(_c,"StreamImpl/ValueConsumer",617),m(1068,1,it,Dh),s.Ad=function(n){Ib()},v(_c,"StreamImpl/lambda$0$Type",1068),m(1069,1,it,cd),s.Ad=function(n){Ib()},v(_c,"StreamImpl/lambda$1$Type",1069),m(1070,1,{},Uke),s.Te=function(n,t){return j2n(this.a,n,t)},v(_c,"StreamImpl/lambda$4$Type",1070),m(1071,1,it,HMe),s.Ad=function(n){Qpn(this.b,this.a,n)},v(_c,"StreamImpl/lambda$5$Type",1071),m(1077,1,it,Xke),s.Ad=function(n){B7n(this.a,u(n,375))},v(_c,"TerminatableStream/lambda$0$Type",1077),m(2104,1,{}),m(1976,1,{},jb),v("javaemul.internal","ConsoleLogger",1976);var NBn=0;m(2096,1,{}),m(1800,1,it,Rs),s.Ad=function(n){u(n,321)},v(z8,"BowyerWatsonTriangulation/lambda$0$Type",1800),m(1801,1,it,Kke),s.Ad=function(n){fc(this.a,u(n,321).e)},v(z8,"BowyerWatsonTriangulation/lambda$1$Type",1801),m(1802,1,it,c0),s.Ad=function(n){u(n,177)},v(z8,"BowyerWatsonTriangulation/lambda$2$Type",1802),m(1797,1,Yt,Vke),s.Le=function(n,t){return A6n(this.a,u(n,177),u(t,177))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(z8,"NaiveMinST/lambda$0$Type",1797),m(440,1,{},hj),v(z8,"NodeMicroLayout",440),m(177,1,{177:1},f4),s.Fb=function(n){var t;return X(n,177)?(t=u(n,177),Ku(this.a,t.a)&&Ku(this.b,t.b)||Ku(this.a,t.b)&&Ku(this.b,t.a)):!1},s.Hb=function(){return _3(this.a)+_3(this.b)};var DBn=v(z8,"TEdge",177);m(321,1,{321:1},lge),s.Fb=function(n){var t;return X(n,321)?(t=u(n,321),cB(this,t.a)&&cB(this,t.b)&&cB(this,t.c)):!1},s.Hb=function(){return _3(this.a)+_3(this.b)+_3(this.c)},v(z8,"TTriangle",321),m(225,1,{225:1},_$),v(z8,"Tree",225),m(1183,1,{},G_e),v(IYe,"Scanline",1183);var jnn=Hi(IYe,_Ye);m(1728,1,{},GRe),v(n1,"CGraph",1728),m(320,1,{320:1},$_e),s.b=0,s.c=0,s.d=0,s.g=0,s.i=0,s.k=Dr,v(n1,"CGroup",320),m(814,1,{},doe),v(n1,"CGroup/CGroupBuilder",814),m(60,1,{60:1},iNe),s.Ib=function(){var n;return this.j?Pt(this.j.Kb(this)):(M1(bJ),bJ.o+"@"+(n=vw(this)>>>0,n.toString(16)))},s.f=0,s.i=Dr;var bJ=v(n1,"CNode",60);m(813,1,{},boe),v(n1,"CNode/CNodeBuilder",813);var Enn;m(1551,1,{},u0),s.df=function(n,t){return 0},s.ef=function(n,t){return 0},v(n1,PYe,1551),m(1830,1,{},o0),s.af=function(n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;for(b=Ki,r=new L(n.a.b);r.ar.d.c||r.d.c==o.d.c&&r.d.b0?n+this.n.d+this.n.a:0},s.gf=function(){var n,t,i,r,c;if(c=0,this.e)this.b?c=this.b.a:this.a[1][1]&&(c=this.a[1][1].gf());else if(this.g)c=ide(this,nW(this,null,!0));else for(t=(ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])),i=0,r=t.length;i0?c+this.n.b+this.n.c:0},s.hf=function(){var n,t,i,r,c;if(this.g)for(n=nW(this,null,!1),i=(ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])),r=0,c=i.length;r0&&(r[0]+=this.d,i-=r[0]),r[2]>0&&(r[2]+=this.d,i-=r[2]),this.c.a=k.Math.max(0,i),this.c.d=t.d+n.d+(this.c.a-i)/2,r[1]=k.Math.max(r[1],i),Wae(this,No,t.d+n.d+r[0]-(r[1]-i)/2,r)},s.b=null,s.d=0,s.e=!1,s.f=!1,s.g=!1;var gte=0,gJ=0;v(lg,"GridContainerCell",1499),m(461,23,{3:1,35:1,23:1,461:1},qX);var nb,Th,Uf,Onn=yt(lg,"HorizontalLabelAlignment",461,Ct,W4n,A2n),Nnn;m(318,216,{216:1,318:1},C_e,HRe,j_e),s.ff=function(){return oDe(this)},s.gf=function(){return wfe(this)},s.a=0,s.c=!1;var IBn=v(lg,"LabelCell",318);m(253,337,{216:1,337:1,253:1},JE),s.ff=function(){return YE(this)},s.gf=function(){return QE(this)},s.hf=function(){HW(this)},s.jf=function(){GW(this)},s.b=0,s.c=0,s.d=!1,v(lg,"StripContainerCell",253),m(1655,1,Ft,r6),s.Mb=function(n){return Nbn(u(n,216))},v(lg,"StripContainerCell/lambda$0$Type",1655),m(1656,1,{},zg),s.We=function(n){return u(n,216).gf()},v(lg,"StripContainerCell/lambda$1$Type",1656),m(1657,1,Ft,Bg),s.Mb=function(n){return Dbn(u(n,216))},v(lg,"StripContainerCell/lambda$2$Type",1657),m(1658,1,{},c6),s.We=function(n){return u(n,216).ff()},v(lg,"StripContainerCell/lambda$3$Type",1658),m(462,23,{3:1,35:1,23:1,462:1},UX);var Xf,tb,ka,Dnn=yt(lg,"VerticalLabelAlignment",462,Ct,Z4n,M2n),Inn;m(787,1,{},Mge),s.c=0,s.d=0,s.k=0,s.s=0,s.t=0,s.v=!1,s.w=0,s.D=!1,s.F=!1,v(uF,"NodeContext",787),m(1497,1,Yt,f5),s.Le=function(n,t){return mCe(u(n,64),u(t,64))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(uF,"NodeContext/0methodref$comparePortSides$Type",1497),m(1498,1,Yt,a5),s.Le=function(n,t){return gMn(u(n,115),u(t,115))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(uF,"NodeContext/1methodref$comparePortContexts$Type",1498),m(168,23,{3:1,35:1,23:1,168:1},Bl);var _nn,Lnn,Pnn,$nn,Rnn,Bnn,znn,Fnn,Jnn,Hnn,Gnn,qnn,Unn,Xnn,Knn,Vnn,Ynn,Qnn,Wnn,Znn,etn,wte,ntn=yt(uF,"NodeLabelLocation",168,Ct,DQ,T2n),ttn;m(115,1,{115:1},Bqe),s.a=!1,v(uF,"PortContext",115),m(1502,1,it,Fg),s.Ad=function(n){IAe(u(n,318))},v(yN,VYe,1502),m(1503,1,Ft,Eb),s.Mb=function(n){return!!u(n,115).c},v(yN,YYe,1503),m(1504,1,it,Jg),s.Ad=function(n){IAe(u(n,115).c)},v(yN,"LabelPlacer/lambda$2$Type",1504);var Qme;m(1501,1,it,_u),s.Ad=function(n){h2(),fbn(u(n,115))},v(yN,"NodeLabelAndSizeUtilities/lambda$0$Type",1501),m(788,1,it,Kle),s.Ad=function(n){Sgn(this.b,this.c,this.a,u(n,187))},s.a=!1,s.c=!1,v(yN,"NodeLabelCellCreator/lambda$0$Type",788),m(1500,1,it,Wke),s.Ad=function(n){bbn(this.a,u(n,187))},v(yN,"PortContextCreator/lambda$0$Type",1500);var wJ;m(1872,1,{},Hg),v(J8,"GreedyRectangleStripOverlapRemover",1872),m(1873,1,Yt,Gg),s.Le=function(n,t){return tpn(u(n,226),u(t,226))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(J8,"GreedyRectangleStripOverlapRemover/0methodref$compareByYCoordinate$Type",1873),m(1826,1,{},oxe),s.a=5,s.e=0,v(J8,"RectangleStripOverlapRemover",1826),m(1827,1,Yt,u6),s.Le=function(n,t){return ipn(u(n,226),u(t,226))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(J8,"RectangleStripOverlapRemover/0methodref$compareLeftRectangleBorders$Type",1827),m(1829,1,Yt,h5),s.Le=function(n,t){return $vn(u(n,226),u(t,226))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(J8,"RectangleStripOverlapRemover/1methodref$compareRightRectangleBorders$Type",1829),m(409,23,{3:1,35:1,23:1,409:1},u$);var UN,pte,mte,XN,itn=yt(J8,"RectangleStripOverlapRemover/OverlapRemovalDirection",409,Ct,Yyn,C2n),rtn;m(226,1,{226:1},fV),v(J8,"RectangleStripOverlapRemover/RectangleNode",226),m(1828,1,it,Zke),s.Ad=function(n){USn(this.a,u(n,226))},v(J8,"RectangleStripOverlapRemover/lambda$1$Type",1828);var ctn=!1,YS,Wme;m(1798,1,it,Wm),s.Ad=function(n){XKe(u(n,225))},v(ay,"DepthFirstCompaction/0methodref$compactTree$Type",1798),m(810,1,it,Wue),s.Ad=function(n){a5n(this.a,u(n,225))},v(ay,"DepthFirstCompaction/lambda$1$Type",810),m(1799,1,it,_Ne),s.Ad=function(n){IEn(this.a,this.b,this.c,u(n,225))},v(ay,"DepthFirstCompaction/lambda$2$Type",1799);var QS,Zme;m(68,1,{68:1},U_e),v(ay,"Node",68),m(1179,1,{},PCe),v(ay,"ScanlineOverlapCheck",1179),m(1180,1,{683:1},p_e),s._e=function(n){Fpn(this,u(n,442))},v(ay,"ScanlineOverlapCheck/OverlapsScanlineHandler",1180),m(1181,1,Yt,qg),s.Le=function(n,t){return yjn(u(n,68),u(t,68))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(ay,"ScanlineOverlapCheck/OverlapsScanlineHandler/lambda$0$Type",1181),m(442,1,{442:1},lse),s.a=!1,v(ay,"ScanlineOverlapCheck/Timestamp",442),m(1182,1,Yt,o6),s.Le=function(n,t){return Uxn(u(n,442),u(t,442))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(ay,"ScanlineOverlapCheck/lambda$0$Type",1182),m(545,1,{},Ug),v("org.eclipse.elk.alg.common.utils","SVGImage",545),m(748,1,{},Zm),v(KZ,twe,748),m(1164,1,Yt,d5),s.Le=function(n,t){return yCn(u(n,235),u(t,235))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(KZ,ZYe,1164),m(1165,1,it,GMe),s.Ad=function(n){hyn(this.b,this.a,u(n,251))},v(KZ,iwe,1165),m(214,1,Qw),v(gv,"AbstractLayoutProvider",214),m(726,214,Qw,goe),s.kf=function(n,t){CUe(this,n,t)},v(KZ,"ForceLayoutProvider",726);var _Bn=Hi(kN,eQe);m(150,1,{3:1,105:1,150:1},Mt),s.of=function(n,t){return jO(this,n,t)},s.lf=function(){return jDe(this)},s.mf=function(n){return T(this,n)},s.nf=function(n){return bi(this,n)},v(kN,"MapPropertyHolder",150),m(313,150,{3:1,313:1,105:1,150:1}),v(jN,"FParticle",313),m(251,313,{3:1,251:1,313:1,105:1,150:1},oIe),s.Ib=function(){var n;return this.a?(n=wu(this.a.a,this,0),n>=0?"b"+n+"["+tY(this.a)+"]":"b["+tY(this.a)+"]"):"b_"+vw(this)},v(jN,"FBendpoint",251),m(291,150,{3:1,291:1,105:1,150:1},nNe),s.Ib=function(){return tY(this)},v(jN,"FEdge",291),m(235,150,{3:1,235:1,105:1,150:1},YR);var LBn=v(jN,"FGraph",235);m(445,313,{3:1,445:1,313:1,105:1,150:1},lPe),s.Ib=function(){return this.b==null||this.b.length==0?"l["+tY(this.a)+"]":"l_"+this.b},v(jN,"FLabel",445),m(155,313,{3:1,155:1,313:1,105:1,150:1},$Ce),s.Ib=function(){return Aae(this)},s.a=0,v(jN,"FNode",155),m(2062,1,{}),s.qf=function(n){ige(this,n)},s.rf=function(){dHe(this)},s.d=0,v(rwe,"AbstractForceModel",2062),m(631,2062,{631:1},tze),s.pf=function(n,t){var i,r,c,o,l;return YKe(this.f,n,t),c=Nr(pc(t.d),n.d),l=k.Math.sqrt(c.a*c.a+c.b*c.b),r=k.Math.max(0,l-fE(n.e)/2-fE(t.e)/2),i=Cqe(this.e,n,t),i>0?o=-Tvn(r,this.c)*i:o=ppn(r,this.b)*u(T(n,(Gf(),ky)),15).a,A1(c,o/l),c},s.qf=function(n){ige(this,n),this.a=u(T(n,(Gf(),mJ)),15).a,this.c=ne(re(T(n,vJ))),this.b=ne(re(T(n,yte)))},s.sf=function(n){return n0&&(o-=Mbn(r,this.a)*i),A1(c,o*this.b/l),c},s.qf=function(n){var t,i,r,c,o,l,f;for(ige(this,n),this.b=ne(re(T(n,(Gf(),kte)))),this.c=this.b/u(T(n,mJ),15).a,r=n.e.c.length,o=0,c=0,f=new L(n.e);f.a0},s.a=0,s.b=0,s.c=0,v(rwe,"FruchtermanReingoldModel",632);var yy=Hi(vu,"ILayoutMetaDataProvider");m(844,1,qa,pT),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,oF),""),"Force Model"),"Determines the model for force calculation."),e3e),(cg(),Ri)),n3e),nn((ph(),Mn))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,cwe),""),"Iterations"),"The number of iterations on the force model."),ve(300)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,uwe),""),"Repulsive Power"),"Determines how many bend points are added to the edge; such bend points are regarded as repelling particles in the force model"),ve(0)),hc),jr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,VZ),""),"FR Temperature"),"The temperature is used as a scaling factor for particle displacements."),Eh),ec),gr),nn(Mn)))),Gi(n,VZ,oF,htn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,YZ),""),"Eades Repulsion"),"Factor for repulsive forces in Eades' model."),5),ec),gr),nn(Mn)))),Gi(n,YZ,oF,ltn),RVe((new eP,n))};var utn,otn,e3e,stn,ltn,ftn,atn,htn;v(yS,"ForceMetaDataProvider",844),m(424,23,{3:1,35:1,23:1,424:1},fse);var vte,pJ,n3e=yt(yS,"ForceModelStrategy",424,Ct,s4n,N2n),dtn;m(984,1,qa,eP),s.tf=function(n){RVe(n)};var btn,gtn,t3e,mJ,i3e,wtn,ptn,mtn,vtn,r3e,ytn,c3e,u3e,ktn,ky,jtn,yte,o3e,Etn,Stn,vJ,kte,xtn,Atn,Mtn,s3e,Ttn;v(yS,"ForceOptions",984),m(985,1,{},Tc),s.uf=function(){var n;return n=new goe,n},s.vf=function(n){},v(yS,"ForceOptions/ForceFactory",985);var KN,WS,jy,yJ;m(845,1,qa,nP),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,swe),""),"Fixed Position"),"Prevent that the node is moved by the layout algorithm."),(Pn(),!1)),(cg(),Sr)),Yi),nn((ph(),fr))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,lwe),""),"Desired Edge Length"),"Either specified for parent nodes or for individual edges, where the latter takes higher precedence."),100),ec),gr),Mi(Mn,z(B(Qa,1),ke,160,0,[Sa]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,fwe),""),"Layout Dimension"),"Dimensions that are permitted to be altered during layout."),l3e),Ri),w3e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,awe),""),"Stress Epsilon"),"Termination criterion for the iterative process."),Eh),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hwe),""),"Iteration Limit"),"Maximum number of performed iterations. Takes higher precedence than 'epsilon'."),ve(oi)),hc),jr),nn(Mn)))),hVe((new xU,n))};var Ctn,Otn,l3e,Ntn,Dtn,Itn;v(yS,"StressMetaDataProvider",845),m(988,1,qa,xU),s.tf=function(n){hVe(n)};var kJ,f3e,a3e,h3e,d3e,b3e,_tn,Ltn,Ptn,$tn,g3e,Rtn;v(yS,"StressOptions",988),m(989,1,{},dc),s.uf=function(){var n;return n=new tNe,n},s.vf=function(n){},v(yS,"StressOptions/StressFactory",989),m(1080,214,Qw,tNe),s.kf=function(n,t){var i,r,c,o,l;for(t.Tg(cQe,1),Re($e(ye(n,(PO(),d3e))))?Re($e(ye(n,g3e)))||HC((i=new hj((_b(),new w0(n))),i)):CUe(new goe,n,t.dh(1)),c=Nze(n),r=EKe(this.a,c),l=r.Jc();l.Ob();)o=u(l.Pb(),235),!(o.e.c.length<=1)&&(HLn(this.b,o),gOn(this.b),Ao(o.d,new s6));c=LVe(r),GVe(c),t.Ug()},v(fF,"StressLayoutProvider",1080),m(1081,1,it,s6),s.Ad=function(n){hge(u(n,445))},v(fF,"StressLayoutProvider/lambda$0$Type",1081),m(986,1,{},nxe),s.c=0,s.e=0,s.g=0,v(fF,"StressMajorization",986),m(384,23,{3:1,35:1,23:1,384:1},XX);var jte,Ete,Ste,w3e=yt(fF,"StressMajorization/Dimension",384,Ct,Y4n,D2n),Btn;m(987,1,Yt,eje),s.Le=function(n,t){return s2n(this.a,u(n,155),u(t,155))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fF,"StressMajorization/lambda$0$Type",987),m(1161,1,{},wLe),v(by,"ElkLayered",1161),m(1162,1,it,nje),s.Ad=function(n){iCn(this.a,u(n,37))},v(by,"ElkLayered/lambda$0$Type",1162),m(1163,1,it,tje),s.Ad=function(n){b2n(this.a,u(n,37))},v(by,"ElkLayered/lambda$1$Type",1163),m(1246,1,{},LCe);var ztn,Ftn,Jtn;v(by,"GraphConfigurator",1246),m(757,1,it,Zue),s.Ad=function(n){OGe(this.a,u(n,9))},v(by,"GraphConfigurator/lambda$0$Type",757),m(758,1,{},Af),s.Kb=function(n){return Vde(),new wn(null,new pn(u(n,25).a,16))},v(by,"GraphConfigurator/lambda$1$Type",758),m(759,1,it,eoe),s.Ad=function(n){OGe(this.a,u(n,9))},v(by,"GraphConfigurator/lambda$2$Type",759),m(1079,214,Qw,ixe),s.kf=function(n,t){var i;i=kLn(new lxe,n),ue(ye(n,(Ne(),wm)))===ue((B1(),Yd))?Ojn(this.a,i,t):aOn(this.a,i,t),t.Zg()||TVe(new vT,i)},v(by,"LayeredLayoutProvider",1079),m(363,23,{3:1,35:1,23:1,363:1},uC);var Kf,r1,eo,no,Pc,p3e=yt(by,"LayeredPhases",363,Ct,U6n,I2n),Htn;m(1683,1,{},jBe),s.i=0;var Gtn;v(TN,"ComponentsToCGraphTransformer",1683);var qtn;m(1684,1,{},Zs),s.wf=function(n,t){return k.Math.min(n.a!=null?ne(n.a):n.c.i,t.a!=null?ne(t.a):t.c.i)},s.xf=function(n,t){return k.Math.min(n.a!=null?ne(n.a):n.c.i,t.a!=null?ne(t.a):t.c.i)},v(TN,"ComponentsToCGraphTransformer/1",1684),m(82,1,{82:1}),s.i=0,s.k=!0,s.o=Dr;var xte=v(SS,"CNode",82);m(460,82,{460:1,82:1},hle,Sde),s.Ib=function(){return""},v(TN,"ComponentsToCGraphTransformer/CRectNode",460),m(1652,1,{},Ca);var Ate,Mte;v(TN,"OneDimensionalComponentsCompaction",1652),m(1653,1,{},Xg),s.Kb=function(n){return T4n(u(n,49))},s.Fb=function(n){return this===n},v(TN,"OneDimensionalComponentsCompaction/lambda$0$Type",1653),m(1654,1,{},b5),s.Kb=function(n){return Ljn(u(n,49))},s.Fb=function(n){return this===n},v(TN,"OneDimensionalComponentsCompaction/lambda$1$Type",1654),m(1686,1,{},vIe),v(SS,"CGraph",1686),m(194,1,{194:1},CQ),s.b=0,s.c=0,s.e=0,s.g=!0,s.i=Dr,v(SS,"CGroup",194),m(1685,1,{},ek),s.wf=function(n,t){return k.Math.max(n.a!=null?ne(n.a):n.c.i,t.a!=null?ne(t.a):t.c.i)},s.xf=function(n,t){return k.Math.max(n.a!=null?ne(n.a):n.c.i,t.a!=null?ne(t.a):t.c.i)},v(SS,PYe,1685),m(1687,1,{},Nqe),s.d=!1;var Utn,Tte=v(SS,BYe,1687);m(1688,1,{},MA),s.Kb=function(n){return Zoe(),Pn(),u(u(n,49).a,82).d.e!=0},s.Fb=function(n){return this===n},v(SS,zYe,1688),m(817,1,{},mfe),s.a=!1,s.b=!1,s.c=!1,s.d=!1,v(SS,FYe,817),m(1868,1,{},IDe),v(aF,JYe,1868);var VN=Hi(fg,_Ye);m(1869,1,{377:1},w_e),s._e=function(n){mDn(this,u(n,465))},v(aF,HYe,1869),m(1870,1,Yt,nk),s.Le=function(n,t){return k5n(u(n,82),u(t,82))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(aF,GYe,1870),m(465,1,{465:1},ase),s.a=!1,v(aF,qYe,465),m(1871,1,Yt,e3),s.Le=function(n,t){return Xxn(u(n,465),u(t,465))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(aF,UYe,1871),m(146,1,{146:1},v9,ofe),s.Fb=function(n){var t;return n==null||PBn!=Us(n)?!1:(t=u(n,146),Ku(this.c,t.c)&&Ku(this.d,t.d))},s.Hb=function(){return RB(z(B(Ar,1),On,1,5,[this.c,this.d]))},s.Ib=function(){return"("+this.c+Co+this.d+(this.a?"cx":"")+this.b+")"},s.a=!0,s.c=0,s.d=0;var PBn=v(fg,"Point",146);m(408,23,{3:1,35:1,23:1,408:1},s$);var op,om,xv,sm,Xtn=yt(fg,"Point/Quadrant",408,Ct,Qyn,O2n),Ktn;m(1674,1,{},rxe),s.b=null,s.c=null,s.d=null,s.e=null,s.f=null;var Vtn,Ytn,Qtn,Wtn,Ztn;v(fg,"RectilinearConvexHull",1674),m(569,1,{377:1},cz),s._e=function(n){$9n(this,u(n,146))},s.b=0;var m3e;v(fg,"RectilinearConvexHull/MaximalElementsEventHandler",569),m(1676,1,Yt,l6),s.Le=function(n,t){return v5n(re(n),re(t))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/MaximalElementsEventHandler/lambda$0$Type",1676),m(1675,1,{377:1},TRe),s._e=function(n){_Nn(this,u(n,146))},s.a=0,s.b=null,s.c=null,s.d=null,s.e=null,v(fg,"RectilinearConvexHull/RectangleEventHandler",1675),m(1677,1,Yt,xp),s.Le=function(n,t){return kyn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$0$Type",1677),m(1678,1,Yt,Ap),s.Le=function(n,t){return jyn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$1$Type",1678),m(1679,1,Yt,Mp),s.Le=function(n,t){return Syn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$2$Type",1679),m(1680,1,Yt,Tp),s.Le=function(n,t){return Eyn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$3$Type",1680),m(1681,1,Yt,xl),s.Le=function(n,t){return OMn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$4$Type",1681),m(1682,1,{},q_e),v(fg,"Scanline",1682),m(2066,1,{}),v(Ua,"AbstractGraphPlacer",2066),m(336,1,{336:1},AOe),s.Df=function(n){return this.Ef(n)?(gn(this.b,u(T(n,(me(),K1)),22),n),!0):!1},s.Ef=function(n){var t,i,r,c;for(t=u(T(n,(me(),K1)),22),c=u(pi(xi,t),22),r=c.Jc();r.Ob();)if(i=u(r.Pb(),22),!u(pi(this.b,i),16).dc())return!1;return!0};var xi;v(Ua,"ComponentGroup",336),m(766,2066,{},woe),s.Ff=function(n){var t,i;for(i=new L(this.a);i.ai&&(p=0,y+=f+r,f=0),h=o.c,M8(o,p+h.a,y+h.b),la(h),c=k.Math.max(c,p+b.a),f=k.Math.max(f,b.b),p+=b.a+r;t.f.a=c,t.f.b=y+f},s.Hf=function(n,t){var i,r,c,o,l;if(ue(T(t,(Ne(),dx)))===ue((X4(),ZS))){for(r=n.Jc();r.Ob();){for(i=u(r.Pb(),37),l=0,o=new L(i.a);o.ai&&!u(T(o,(me(),K1)),22).Gc((De(),Xn))||h&&u(T(h,(me(),K1)),22).Gc((De(),et))||u(T(o,(me(),K1)),22).Gc((De(),Kn)))&&(S=y,A+=f+r,f=0),b=o.c,u(T(o,(me(),K1)),22).Gc((De(),Xn))&&(S=c+r),M8(o,S+b.a,A+b.b),c=k.Math.max(c,S+p.a),u(T(o,K1),22).Gc(bt)&&(y=k.Math.max(y,S+p.a+r)),la(b),f=k.Math.max(f,p.b),S+=p.a+r,h=o;t.f.a=c,t.f.b=A+f},s.Hf=function(n,t){},v(Ua,"ModelOrderRowGraphPlacer",1277),m(1275,1,Yt,TA),s.Le=function(n,t){return $7n(u(n,37),u(t,37))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ua,"SimpleRowGraphPlacer/1",1275);var nin;m(1245,1,jh,f6),s.Lb=function(n){var t;return t=u(T(u(n,250).b,(Ne(),Wc)),78),!!t&&t.b!=0},s.Fb=function(n){return this===n},s.Mb=function(n){var t;return t=u(T(u(n,250).b,(Ne(),Wc)),78),!!t&&t.b!=0},v(hF,"CompoundGraphPostprocessor/1",1245),m(1244,1,Ai,fxe),s.If=function(n,t){VJe(this,u(n,37),t)},v(hF,"CompoundGraphPreprocessor",1244),m(444,1,{444:1},PFe),s.c=!1,v(hF,"CompoundGraphPreprocessor/ExternalPort",444),m(250,1,{250:1},Y$),s.Ib=function(){return $K(this.c)+":"+xqe(this.b)},v(hF,"CrossHierarchyEdge",250),m(764,1,Yt,noe),s.Le=function(n,t){return vxn(this,u(n,250),u(t,250))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(hF,"CrossHierarchyEdgeComparator",764),m(246,150,{3:1,246:1,105:1,150:1}),s.p=0,v(Zu,"LGraphElement",246),m(17,246,{3:1,17:1,246:1,105:1,150:1},Mw),s.Ib=function(){return xqe(this)};var b7=v(Zu,"LEdge",17);m(37,246,{3:1,20:1,37:1,246:1,105:1,150:1},$he),s.Ic=function(n){rc(this,n)},s.Jc=function(){return new L(this.b)},s.Ib=function(){return this.b.c.length==0?"G-unlayered"+Fa(this.a):this.a.c.length==0?"G-layered"+Fa(this.b):"G[layerless"+Fa(this.a)+", layers"+Fa(this.b)+"]"};var tin=v(Zu,"LGraph",37),iin;m(655,1,{}),s.Jf=function(){return this.e.n},s.mf=function(n){return T(this.e,n)},s.Kf=function(){return this.e.o},s.Lf=function(){return this.e.p},s.nf=function(n){return bi(this.e,n)},s.Mf=function(n){this.e.n.a=n.a,this.e.n.b=n.b},s.Nf=function(n){this.e.o.a=n.a,this.e.o.b=n.b},s.Of=function(n){this.e.p=n},v(Zu,"LGraphAdapters/AbstractLShapeAdapter",655),m(464,1,{837:1},dj),s.Pf=function(){var n,t;if(!this.b)for(this.b=Fh(this.a.b.c.length),t=new L(this.a.b);t.a0&&hFe((Yn(t-1,n.length),n.charCodeAt(t-1)),aQe);)--t;if(o> ",n),bz(i)),Kt(uo((n.a+="[",n),i.i),"]")),n.a},s.c=!0,s.d=!1;var E3e,S3e,x3e,A3e,M3e,T3e,cin=v(Zu,"LPort",12);m(399,1,Wh,o9),s.Ic=function(n){rc(this,n)},s.Jc=function(){var n;return n=new L(this.a.e),new ije(n)},v(Zu,"LPort/1",399),m(1273,1,Fr,ije),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(_(this.a),17).c},s.Ob=function(){return bu(this.a)},s.Qb=function(){oE(this.a)},v(Zu,"LPort/1/1",1273),m(365,1,Wh,W5),s.Ic=function(n){rc(this,n)},s.Jc=function(){var n;return n=new L(this.a.g),new toe(n)},v(Zu,"LPort/2",365),m(763,1,Fr,toe),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(_(this.a),17).d},s.Ob=function(){return bu(this.a)},s.Qb=function(){oE(this.a)},v(Zu,"LPort/2/1",763),m(1266,1,Wh,UMe),s.Ic=function(n){rc(this,n)},s.Jc=function(){return new La(this)},v(Zu,"LPort/CombineIter",1266),m(207,1,Fr,La),s.Nb=function(n){Zr(this,n)},s.Qb=function(){xAe()},s.Ob=function(){return Wj(this)},s.Pb=function(){return bu(this.a)?_(this.a):_(this.b)},v(Zu,"LPort/CombineIter/1",207),m(1267,1,jh,w5),s.Lb=function(n){return GDe(n)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).g.c.length!=0},v(Zu,"LPort/lambda$0$Type",1267),m(1268,1,jh,s0),s.Lb=function(n){return qDe(n)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).e.c.length!=0},v(Zu,"LPort/lambda$1$Type",1268),m(1269,1,jh,Ih),s.Lb=function(n){return ss(),u(n,12).j==(De(),Xn)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).j==(De(),Xn)},v(Zu,"LPort/lambda$2$Type",1269),m(1270,1,jh,ck),s.Lb=function(n){return ss(),u(n,12).j==(De(),et)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).j==(De(),et)},v(Zu,"LPort/lambda$3$Type",1270),m(1271,1,jh,CA),s.Lb=function(n){return ss(),u(n,12).j==(De(),bt)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).j==(De(),bt)},v(Zu,"LPort/lambda$4$Type",1271),m(1272,1,jh,p5),s.Lb=function(n){return ss(),u(n,12).j==(De(),Kn)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).j==(De(),Kn)},v(Zu,"LPort/lambda$5$Type",1272),m(25,246,{3:1,20:1,246:1,25:1,105:1,150:1},Xu),s.Ic=function(n){rc(this,n)},s.Jc=function(){return new L(this.a)},s.Ib=function(){return"L_"+wu(this.b.b,this,0)+Fa(this.a)},v(Zu,"Layer",25),m(1659,1,{},_$e),s.b=0,v(Zu,"Tarjan",1659),m(1282,1,{},lxe),v(zd,gQe,1282),m(1286,1,{},uk),s.Kb=function(n){return iu(u(n,84))},v(zd,"ElkGraphImporter/0methodref$connectableShapeToNode$Type",1286),m(1289,1,{},t3),s.Kb=function(n){return iu(u(n,84))},v(zd,"ElkGraphImporter/1methodref$connectableShapeToNode$Type",1289),m(1283,1,it,rje),s.Ad=function(n){Jqe(this.a,u(n,125))},v(zd,iwe,1283),m(1284,1,it,cje),s.Ad=function(n){Jqe(this.a,u(n,125))},v(zd,wQe,1284),m(1285,1,{},_h),s.Kb=function(n){return new wn(null,new pn(tae(u(n,85)),16))},v(zd,pQe,1285),m(1287,1,Ft,uje),s.Mb=function(n){return fwn(this.a,u(n,26))},v(zd,mQe,1287),m(1288,1,{},i3),s.Kb=function(n){return new wn(null,new pn(w5n(u(n,85)),16))},v(zd,"ElkGraphImporter/lambda$5$Type",1288),m(1290,1,Ft,oje),s.Mb=function(n){return awn(this.a,u(n,26))},v(zd,"ElkGraphImporter/lambda$7$Type",1290),m(1291,1,Ft,ok),s.Mb=function(n){return N5n(u(n,85))},v(zd,"ElkGraphImporter/lambda$8$Type",1291),m(1261,1,{},vT);var uin;v(zd,"ElkGraphLayoutTransferrer",1261),m(1262,1,Ft,sje),s.Mb=function(n){return t2n(this.a,u(n,17))},v(zd,"ElkGraphLayoutTransferrer/lambda$0$Type",1262),m(1263,1,it,lje),s.Ad=function(n){iC(),Te(this.a,u(n,17))},v(zd,"ElkGraphLayoutTransferrer/lambda$1$Type",1263),m(1264,1,Ft,fje),s.Mb=function(n){return Jpn(this.a,u(n,17))},v(zd,"ElkGraphLayoutTransferrer/lambda$2$Type",1264),m(1265,1,it,aje),s.Ad=function(n){iC(),Te(this.a,u(n,17))},v(zd,"ElkGraphLayoutTransferrer/lambda$3$Type",1265),m(806,1,{},Lle),v(Wn,"BiLinkedHashMultiMap",806),m(1511,1,Ai,a6),s.If=function(n,t){t7n(u(n,37),t)},v(Wn,"CommentNodeMarginCalculator",1511),m(1512,1,{},Vg),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"CommentNodeMarginCalculator/lambda$0$Type",1512),m(1513,1,it,vI),s.Ad=function(n){mLn(u(n,9))},v(Wn,"CommentNodeMarginCalculator/lambda$1$Type",1513),m(1514,1,Ai,OA),s.If=function(n,t){xDn(u(n,37),t)},v(Wn,"CommentPostprocessor",1514),m(1515,1,Ai,yI),s.If=function(n,t){q$n(u(n,37),t)},v(Wn,"CommentPreprocessor",1515),m(1516,1,Ai,m5),s.If=function(n,t){RNn(u(n,37),t)},v(Wn,"ConstraintsPostprocessor",1516),m(1517,1,Ai,uq),s.If=function(n,t){M7n(u(n,37),t)},v(Wn,"EdgeAndLayerConstraintEdgeReverser",1517),m(1518,1,Ai,kI),s.If=function(n,t){tEn(u(n,37),t)},v(Wn,"EndLabelPostprocessor",1518),m(1519,1,{},jI),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"EndLabelPostprocessor/lambda$0$Type",1519),m(1520,1,Ft,NA),s.Mb=function(n){return z6n(u(n,9))},v(Wn,"EndLabelPostprocessor/lambda$1$Type",1520),m(1521,1,it,oq),s.Ad=function(n){Kxn(u(n,9))},v(Wn,"EndLabelPostprocessor/lambda$2$Type",1521),m(1522,1,Ai,sq),s.If=function(n,t){OTn(u(n,37),t)},v(Wn,"EndLabelPreprocessor",1522),m(1523,1,{},sk),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"EndLabelPreprocessor/lambda$0$Type",1523),m(1524,1,it,LNe),s.Ad=function(n){xgn(this.a,this.b,this.c,u(n,9))},s.a=0,s.b=0,s.c=!1,v(Wn,"EndLabelPreprocessor/lambda$1$Type",1524),m(1525,1,Ft,Yg),s.Mb=function(n){return ue(T(u(n,70),(Ne(),Oh)))===ue(($a(),J7))},v(Wn,"EndLabelPreprocessor/lambda$2$Type",1525),m(1526,1,it,hje),s.Ad=function(n){Vt(this.a,u(n,70))},v(Wn,"EndLabelPreprocessor/lambda$3$Type",1526),m(1527,1,Ft,DA),s.Mb=function(n){return ue(T(u(n,70),(Ne(),Oh)))===ue(($a(),_m))},v(Wn,"EndLabelPreprocessor/lambda$4$Type",1527),m(1528,1,it,dje),s.Ad=function(n){Vt(this.a,u(n,70))},v(Wn,"EndLabelPreprocessor/lambda$5$Type",1528),m(1576,1,Ai,AU),s.If=function(n,t){pjn(u(n,37),t)};var oin;v(Wn,"EndLabelSorter",1576),m(1577,1,Yt,IA),s.Le=function(n,t){return REn(u(n,455),u(t,455))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"EndLabelSorter/1",1577),m(455,1,{455:1},o_e),v(Wn,"EndLabelSorter/LabelGroup",455),m(1578,1,{},v5),s.Kb=function(n){return tC(),new wn(null,new pn(u(n,25).a,16))},v(Wn,"EndLabelSorter/lambda$0$Type",1578),m(1579,1,Ft,y5),s.Mb=function(n){return tC(),u(n,9).k==(zn(),Qi)},v(Wn,"EndLabelSorter/lambda$1$Type",1579),m(1580,1,it,EI),s.Ad=function(n){GMn(u(n,9))},v(Wn,"EndLabelSorter/lambda$2$Type",1580),m(1581,1,Ft,_A),s.Mb=function(n){return tC(),ue(T(u(n,70),(Ne(),Oh)))===ue(($a(),_m))},v(Wn,"EndLabelSorter/lambda$3$Type",1581),m(1582,1,Ft,SI),s.Mb=function(n){return tC(),ue(T(u(n,70),(Ne(),Oh)))===ue(($a(),J7))},v(Wn,"EndLabelSorter/lambda$4$Type",1582),m(1529,1,Ai,h6),s.If=function(n,t){LLn(this,u(n,37))},s.b=0,s.c=0,v(Wn,"FinalSplineBendpointsCalculator",1529),m(1530,1,{},Qg),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"FinalSplineBendpointsCalculator/lambda$0$Type",1530),m(1531,1,{},LA),s.Kb=function(n){return new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(Wn,"FinalSplineBendpointsCalculator/lambda$1$Type",1531),m(1532,1,Ft,d6),s.Mb=function(n){return!cc(u(n,17))},v(Wn,"FinalSplineBendpointsCalculator/lambda$2$Type",1532),m(1533,1,Ft,Op),s.Mb=function(n){return bi(u(n,17),(me(),vg))},v(Wn,"FinalSplineBendpointsCalculator/lambda$3$Type",1533),m(1534,1,it,bje),s.Ad=function(n){HIn(this.a,u(n,132))},v(Wn,"FinalSplineBendpointsCalculator/lambda$4$Type",1534),m(1535,1,it,PA),s.Ad=function(n){HO(u(n,17).a)},v(Wn,"FinalSplineBendpointsCalculator/lambda$5$Type",1535),m(790,1,Ai,ioe),s.If=function(n,t){CPn(this,u(n,37),t)},v(Wn,"GraphTransformer",790),m(502,23,{3:1,35:1,23:1,502:1},hse);var Dte,QN,sin=yt(Wn,"GraphTransformer/Mode",502,Ct,l4n,P2n),lin;m(1536,1,Ai,lk),s.If=function(n,t){QOn(u(n,37),t)},v(Wn,"HierarchicalNodeResizingProcessor",1536),m(1537,1,Ai,xI),s.If=function(n,t){H8n(u(n,37),t)},v(Wn,"HierarchicalPortConstraintProcessor",1537),m(1538,1,Yt,fk),s.Le=function(n,t){return rSn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"HierarchicalPortConstraintProcessor/NodeComparator",1538),m(1539,1,Ai,ak),s.If=function(n,t){P_n(u(n,37),t)},v(Wn,"HierarchicalPortDummySizeProcessor",1539),m(1540,1,Ai,AI),s.If=function(n,t){VDn(this,u(n,37),t)},s.a=0,v(Wn,"HierarchicalPortOrthogonalEdgeRouter",1540),m(1541,1,Yt,Lh),s.Le=function(n,t){return rpn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"HierarchicalPortOrthogonalEdgeRouter/1",1541),m(1542,1,Yt,r3),s.Le=function(n,t){return J9n(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"HierarchicalPortOrthogonalEdgeRouter/2",1542),m(1543,1,Ai,hk),s.If=function(n,t){MMn(u(n,37),t)},v(Wn,"HierarchicalPortPositionProcessor",1543),m(1544,1,Ai,mT),s.If=function(n,t){TRn(this,u(n,37))},s.a=0,s.c=0;var jJ,EJ;v(Wn,"HighDegreeNodeLayeringProcessor",1544),m(566,1,{566:1},b6),s.b=-1,s.d=-1,v(Wn,"HighDegreeNodeLayeringProcessor/HighDegreeNodeInformation",566),m(1545,1,{},lq),s.Kb=function(n){return OC(),rr(u(n,9))},s.Fb=function(n){return this===n},v(Wn,"HighDegreeNodeLayeringProcessor/lambda$0$Type",1545),m(1546,1,{},$A),s.Kb=function(n){return OC(),Ni(u(n,9))},s.Fb=function(n){return this===n},v(Wn,"HighDegreeNodeLayeringProcessor/lambda$1$Type",1546),m(1552,1,Ai,RA),s.If=function(n,t){M_n(this,u(n,37),t)},v(Wn,"HyperedgeDummyMerger",1552),m(791,1,{},Wle),s.a=!1,s.b=!1,s.c=!1,v(Wn,"HyperedgeDummyMerger/MergeState",791),m(1553,1,{},g6),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"HyperedgeDummyMerger/lambda$0$Type",1553),m(1554,1,{},dk),s.Kb=function(n){return new wn(null,new pn(u(n,9).j,16))},v(Wn,"HyperedgeDummyMerger/lambda$1$Type",1554),m(1555,1,it,MI),s.Ad=function(n){u(n,12).p=-1},v(Wn,"HyperedgeDummyMerger/lambda$2$Type",1555),m(1556,1,Ai,aq),s.If=function(n,t){A_n(u(n,37),t)},v(Wn,"HypernodesProcessor",1556),m(1557,1,Ai,hq),s.If=function(n,t){L_n(u(n,37),t)},v(Wn,"InLayerConstraintProcessor",1557),m(1558,1,Ai,BA),s.If=function(n,t){p7n(u(n,37),t)},v(Wn,"InnermostNodeMarginCalculator",1558),m(1559,1,Ai,dq),s.If=function(n,t){F$n(this,u(n,37))},s.a=Dr,s.b=Dr,s.c=Ki,s.d=Ki;var $Bn=v(Wn,"InteractiveExternalPortPositioner",1559);m(1560,1,{},bq),s.Kb=function(n){return u(n,17).d.i},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$0$Type",1560),m(1561,1,{},gje),s.Kb=function(n){return cpn(this.a,re(n))},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$1$Type",1561),m(1562,1,{},gq),s.Kb=function(n){return u(n,17).c.i},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$2$Type",1562),m(1563,1,{},wje),s.Kb=function(n){return upn(this.a,re(n))},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$3$Type",1563),m(1564,1,{},pje),s.Kb=function(n){return e2n(this.a,re(n))},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$4$Type",1564),m(1565,1,{},mje),s.Kb=function(n){return n2n(this.a,re(n))},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$5$Type",1565),m(79,23,{3:1,35:1,23:1,79:1,196:1},br),s.bg=function(){switch(this.g){case 15:return new ew;case 22:return new Pp;case 48:return new uM;case 29:case 36:return new Eq;case 33:return new a6;case 43:return new OA;case 1:return new yI;case 42:return new m5;case 57:return new ioe((K9(),QN));case 0:return new ioe((K9(),Dte));case 2:return new uq;case 55:return new kI;case 34:return new sq;case 52:return new h6;case 56:return new lk;case 13:return new xI;case 39:return new ak;case 45:return new AI;case 41:return new hk;case 9:return new mT;case 50:return new vOe;case 38:return new RA;case 44:return new aq;case 28:return new hq;case 31:return new BA;case 3:return new dq;case 18:return new fq;case 30:return new wq;case 5:return new MU;case 51:return new vq;case 35:return new Y6;case 37:return new Sq;case 53:return new AU;case 11:return new CI;case 7:return new TU;case 40:return new xq;case 46:return new Aq;case 16:return new Mq;case 10:return new kTe;case 49:return new Nq;case 21:return new Dq;case 23:return new zP((eg(),Mx));case 8:return new FA;case 12:return new _q;case 4:return new OI;case 19:return new tP;case 17:return new PI;case 54:return new p6;case 6:return new Fq;case 25:return new hxe;case 26:return new rM;case 47:return new HA;case 32:return new uNe;case 14:return new GI;case 27:return new Vq;case 20:return new y6;case 24:return new zP((eg(),CH));default:throw $(new Gn(nee+(this.f!=null?this.f:""+this.g)))}};var C3e,O3e,N3e,D3e,I3e,_3e,L3e,P3e,$3e,R3e,B3e,Av,SJ,xJ,z3e,F3e,J3e,H3e,G3e,q3e,U3e,nx,X3e,K3e,V3e,Y3e,Q3e,Ite,AJ,MJ,W3e,TJ,CJ,OJ,g7,lm,fm,Z3e,NJ,DJ,eve,IJ,_J,nve,tve,ive,rve,LJ,_te,Ey,PJ,$J,RJ,BJ,cve,uve,ove,sve,RBn=yt(Wn,tee,79,Ct,FUe,$2n),fin;m(1566,1,Ai,fq),s.If=function(n,t){R$n(u(n,37),t)},v(Wn,"InvertedPortProcessor",1566),m(1567,1,Ai,wq),s.If=function(n,t){$In(u(n,37),t)},v(Wn,"LabelAndNodeSizeProcessor",1567),m(1568,1,Ft,pq),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"LabelAndNodeSizeProcessor/lambda$0$Type",1568),m(1569,1,Ft,TI),s.Mb=function(n){return u(n,9).k==(zn(),wr)},v(Wn,"LabelAndNodeSizeProcessor/lambda$1$Type",1569),m(1570,1,it,RNe),s.Ad=function(n){Agn(this.b,this.a,this.c,u(n,9))},s.a=!1,s.c=!1,v(Wn,"LabelAndNodeSizeProcessor/lambda$2$Type",1570),m(1571,1,Ai,MU),s.If=function(n,t){w$n(u(n,37),t)};var ain;v(Wn,"LabelDummyInserter",1571),m(1572,1,jh,mq),s.Lb=function(n){return ue(T(u(n,70),(Ne(),Oh)))===ue(($a(),F7))},s.Fb=function(n){return this===n},s.Mb=function(n){return ue(T(u(n,70),(Ne(),Oh)))===ue(($a(),F7))},v(Wn,"LabelDummyInserter/1",1572),m(1573,1,Ai,vq),s.If=function(n,t){i$n(u(n,37),t)},v(Wn,"LabelDummyRemover",1573),m(1574,1,Ft,yq),s.Mb=function(n){return Re($e(T(u(n,70),(Ne(),Rv))))},v(Wn,"LabelDummyRemover/lambda$0$Type",1574),m(1332,1,Ai,Y6),s.If=function(n,t){QPn(this,u(n,37),t)},s.a=null;var Lte;v(Wn,"LabelDummySwitcher",1332),m(294,1,{294:1},$Xe),s.c=0,s.d=null,s.f=0,v(Wn,"LabelDummySwitcher/LabelDummyInfo",294),m(1333,1,{},kq),s.Kb=function(n){return z4(),new wn(null,new pn(u(n,25).a,16))},v(Wn,"LabelDummySwitcher/lambda$0$Type",1333),m(1334,1,Ft,zA),s.Mb=function(n){return z4(),u(n,9).k==(zn(),Uu)},v(Wn,"LabelDummySwitcher/lambda$1$Type",1334),m(1335,1,{},vje),s.Kb=function(n){return Hpn(this.a,u(n,9))},v(Wn,"LabelDummySwitcher/lambda$2$Type",1335),m(1336,1,it,yje),s.Ad=function(n){qvn(this.a,u(n,294))},v(Wn,"LabelDummySwitcher/lambda$3$Type",1336),m(1337,1,Yt,jq),s.Le=function(n,t){return yvn(u(n,294),u(t,294))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"LabelDummySwitcher/lambda$4$Type",1337),m(789,1,Ai,Eq),s.If=function(n,t){j9n(u(n,37),t)},v(Wn,"LabelManagementProcessor",789),m(1575,1,Ai,Sq),s.If=function(n,t){bDn(u(n,37),t)},v(Wn,"LabelSideSelector",1575),m(1583,1,Ai,CI),s.If=function(n,t){Z_n(u(n,37),t)},v(Wn,"LayerConstraintPostprocessor",1583),m(1584,1,Ai,TU),s.If=function(n,t){QCn(u(n,37),t)};var lve;v(Wn,"LayerConstraintPreprocessor",1584),m(367,23,{3:1,35:1,23:1,367:1},f$);var WN,zJ,FJ,Pte,hin=yt(Wn,"LayerConstraintPreprocessor/HiddenNodeConnections",367,Ct,e6n,vmn),din;m(1585,1,Ai,xq),s.If=function(n,t){pPn(u(n,37),t)},v(Wn,"LayerSizeAndGraphHeightCalculator",1585),m(1586,1,Ai,Aq),s.If=function(n,t){WOn(u(n,37),t)},v(Wn,"LongEdgeJoiner",1586),m(1587,1,Ai,Mq),s.If=function(n,t){XLn(u(n,37),t)},v(Wn,"LongEdgeSplitter",1587),m(1588,1,Ai,kTe),s.If=function(n,t){O$n(this,u(n,37),t)},s.e=0,s.f=0,s.j=0,s.k=0,s.n=0,s.o=0;var bin,gin;v(Wn,"NodePromotion",1588),m(1589,1,Yt,Tq),s.Le=function(n,t){return kkn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"NodePromotion/1",1589),m(1590,1,Yt,Cq),s.Le=function(n,t){return jkn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"NodePromotion/2",1590),m(1591,1,{},Oq),s.Kb=function(n){return u(n,49),K$(),Pn(),!0},s.Fb=function(n){return this===n},v(Wn,"NodePromotion/lambda$0$Type",1591),m(1592,1,{},kje),s.Kb=function(n){return M4n(this.a,u(n,49))},s.Fb=function(n){return this===n},s.a=0,v(Wn,"NodePromotion/lambda$1$Type",1592),m(1593,1,{},jje),s.Kb=function(n){return A4n(this.a,u(n,49))},s.Fb=function(n){return this===n},s.a=0,v(Wn,"NodePromotion/lambda$2$Type",1593),m(1594,1,Ai,Nq),s.If=function(n,t){vRn(u(n,37),t)},v(Wn,"NorthSouthPortPostprocessor",1594),m(1595,1,Ai,Dq),s.If=function(n,t){xRn(u(n,37),t)},v(Wn,"NorthSouthPortPreprocessor",1595),m(1596,1,Yt,Iq),s.Le=function(n,t){return z7n(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"NorthSouthPortPreprocessor/lambda$0$Type",1596),m(1597,1,Ai,FA),s.If=function(n,t){w_n(u(n,37),t)},v(Wn,"PartitionMidprocessor",1597),m(1598,1,Ft,w6),s.Mb=function(n){return bi(u(n,9),(Ne(),mm))},v(Wn,"PartitionMidprocessor/lambda$0$Type",1598),m(1599,1,it,Eje),s.Ad=function(n){O5n(this.a,u(n,9))},v(Wn,"PartitionMidprocessor/lambda$1$Type",1599),m(1600,1,Ai,_q),s.If=function(n,t){vNn(u(n,37),t)},v(Wn,"PartitionPostprocessor",1600),m(1601,1,Ai,OI),s.If=function(n,t){jIn(u(n,37),t)},v(Wn,"PartitionPreprocessor",1601),m(1602,1,Ft,NI),s.Mb=function(n){return bi(u(n,9),(Ne(),mm))},v(Wn,"PartitionPreprocessor/lambda$0$Type",1602),m(1603,1,Ft,DI),s.Mb=function(n){return bi(u(n,9),(Ne(),mm))},v(Wn,"PartitionPreprocessor/lambda$1$Type",1603),m(1604,1,{},II),s.Kb=function(n){return new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(Wn,"PartitionPreprocessor/lambda$2$Type",1604),m(1605,1,Ft,Sje),s.Mb=function(n){return lgn(this.a,u(n,17))},v(Wn,"PartitionPreprocessor/lambda$3$Type",1605),m(1606,1,it,_I),s.Ad=function(n){Z7n(u(n,17))},v(Wn,"PartitionPreprocessor/lambda$4$Type",1606),m(1607,1,Ft,xje),s.Mb=function(n){return Uvn(this.a,u(n,9))},s.a=0,v(Wn,"PartitionPreprocessor/lambda$5$Type",1607),m(1608,1,Ai,tP),s.If=function(n,t){YIn(u(n,37),t)};var fve,win,pin,min,ave,hve;v(Wn,"PortListSorter",1608),m(1609,1,{},k5),s.Kb=function(n){return n8(),u(n,12).e},v(Wn,"PortListSorter/lambda$0$Type",1609),m(1610,1,{},Lq),s.Kb=function(n){return n8(),u(n,12).g},v(Wn,"PortListSorter/lambda$1$Type",1610),m(1611,1,Yt,Pq),s.Le=function(n,t){return aPe(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"PortListSorter/lambda$2$Type",1611),m(1612,1,Yt,$q),s.Le=function(n,t){return hxn(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"PortListSorter/lambda$3$Type",1612),m(1613,1,Yt,LI),s.Le=function(n,t){return lKe(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"PortListSorter/lambda$4$Type",1613),m(1614,1,Ai,PI),s.If=function(n,t){rOn(u(n,37),t)},v(Wn,"PortSideProcessor",1614),m(1615,1,Ai,p6),s.If=function(n,t){sIn(u(n,37),t)},v(Wn,"ReversedEdgeRestorer",1615),m(1620,1,Ai,hxe),s.If=function(n,t){VSn(this,u(n,37),t)},v(Wn,"SelfLoopPortRestorer",1620),m(1621,1,{},m6),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"SelfLoopPortRestorer/lambda$0$Type",1621),m(1622,1,Ft,Rq),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"SelfLoopPortRestorer/lambda$1$Type",1622),m(1623,1,Ft,bk),s.Mb=function(n){return bi(u(n,9),(me(),dp))},v(Wn,"SelfLoopPortRestorer/lambda$2$Type",1623),m(1624,1,{},$I),s.Kb=function(n){return u(T(u(n,9),(me(),dp)),338)},v(Wn,"SelfLoopPortRestorer/lambda$3$Type",1624),m(1625,1,it,Aje),s.Ad=function(n){rTn(this.a,u(n,338))},v(Wn,"SelfLoopPortRestorer/lambda$4$Type",1625),m(792,1,it,JA),s.Ad=function(n){gTn(u(n,107))},v(Wn,"SelfLoopPortRestorer/lambda$5$Type",792),m(1627,1,Ai,HA),s.If=function(n,t){oSn(u(n,37),t)},v(Wn,"SelfLoopPostProcessor",1627),m(1628,1,{},GA),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"SelfLoopPostProcessor/lambda$0$Type",1628),m(1629,1,Ft,RI),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"SelfLoopPostProcessor/lambda$1$Type",1629),m(1630,1,Ft,BI),s.Mb=function(n){return bi(u(n,9),(me(),dp))},v(Wn,"SelfLoopPostProcessor/lambda$2$Type",1630),m(1631,1,it,zI),s.Ad=function(n){aAn(u(n,9))},v(Wn,"SelfLoopPostProcessor/lambda$3$Type",1631),m(1632,1,{},Bq),s.Kb=function(n){return new wn(null,new pn(u(n,107).f,1))},v(Wn,"SelfLoopPostProcessor/lambda$4$Type",1632),m(1633,1,it,Mje),s.Ad=function(n){Kyn(this.a,u(n,341))},v(Wn,"SelfLoopPostProcessor/lambda$5$Type",1633),m(1634,1,Ft,zq),s.Mb=function(n){return!!u(n,107).i},v(Wn,"SelfLoopPostProcessor/lambda$6$Type",1634),m(1635,1,it,Tje),s.Ad=function(n){Abn(this.a,u(n,107))},v(Wn,"SelfLoopPostProcessor/lambda$7$Type",1635),m(1616,1,Ai,Fq),s.If=function(n,t){$On(u(n,37),t)},v(Wn,"SelfLoopPreProcessor",1616),m(1617,1,{},Jq),s.Kb=function(n){return new wn(null,new pn(u(n,107).f,1))},v(Wn,"SelfLoopPreProcessor/lambda$0$Type",1617),m(1618,1,{},Hq),s.Kb=function(n){return u(n,341).a},v(Wn,"SelfLoopPreProcessor/lambda$1$Type",1618),m(1619,1,it,g1),s.Ad=function(n){Twn(u(n,17))},v(Wn,"SelfLoopPreProcessor/lambda$2$Type",1619),m(1636,1,Ai,uNe),s.If=function(n,t){FMn(this,u(n,37),t)},v(Wn,"SelfLoopRouter",1636),m(1637,1,{},v6),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"SelfLoopRouter/lambda$0$Type",1637),m(1638,1,Ft,FI),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"SelfLoopRouter/lambda$1$Type",1638),m(1639,1,Ft,JI),s.Mb=function(n){return bi(u(n,9),(me(),dp))},v(Wn,"SelfLoopRouter/lambda$2$Type",1639),m(1640,1,{},HI),s.Kb=function(n){return u(T(u(n,9),(me(),dp)),338)},v(Wn,"SelfLoopRouter/lambda$3$Type",1640),m(1641,1,it,XMe),s.Ad=function(n){S5n(this.a,this.b,u(n,338))},v(Wn,"SelfLoopRouter/lambda$4$Type",1641),m(1642,1,Ai,GI),s.If=function(n,t){nDn(u(n,37),t)},v(Wn,"SemiInteractiveCrossMinProcessor",1642),m(1643,1,Ft,qA),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"SemiInteractiveCrossMinProcessor/lambda$0$Type",1643),m(1644,1,Ft,Gq),s.Mb=function(n){return jDe(u(n,9))._b((Ne(),km))},v(Wn,"SemiInteractiveCrossMinProcessor/lambda$1$Type",1644),m(1645,1,Yt,j5),s.Le=function(n,t){return Z8n(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"SemiInteractiveCrossMinProcessor/lambda$2$Type",1645),m(1646,1,{},UA),s.Te=function(n,t){return C5n(u(n,9),u(t,9))},v(Wn,"SemiInteractiveCrossMinProcessor/lambda$3$Type",1646),m(1648,1,Ai,y6),s.If=function(n,t){LPn(u(n,37),t)},v(Wn,"SortByInputModelProcessor",1648),m(1649,1,Ft,XA),s.Mb=function(n){return u(n,12).g.c.length!=0},v(Wn,"SortByInputModelProcessor/lambda$0$Type",1649),m(1650,1,it,Cje),s.Ad=function(n){yTn(this.a,u(n,12))},v(Wn,"SortByInputModelProcessor/lambda$1$Type",1650),m(1729,804,{},LBe),s.bf=function(n){var t,i,r,c;switch(this.c=n,this.a.g){case 2:t=new Ce,Zi(si(new wn(null,new pn(this.c.a.b,16)),new QI),new WMe(this,t)),GO(this,new c3),Ao(t,new E5),t.c.length=0,Zi(si(new wn(null,new pn(this.c.a.b,16)),new KA),new Nje(t)),GO(this,new UI),Ao(t,new u3),t.c.length=0,i=_Ce(HY(k2(new wn(null,new pn(this.c.a.b,16)),new Dje(this))),new XI),Zi(new wn(null,new pn(this.c.a.a,16)),new VMe(i,t)),GO(this,new VI),Ao(t,new qq),t.c.length=0;break;case 3:r=new Ce,GO(this,new qI),c=_Ce(HY(k2(new wn(null,new pn(this.c.a.b,16)),new Oje(this))),new KI),Zi(si(new wn(null,new pn(this.c.a.b,16)),new Uq),new QMe(c,r)),GO(this,new Xq),Ao(r,new YI),r.c.length=0;break;default:throw $(new exe)}},s.b=0,v(lr,"EdgeAwareScanlineConstraintCalculation",1729),m(1730,1,jh,qI),s.Lb=function(n){return X(u(n,60).g,156)},s.Fb=function(n){return this===n},s.Mb=function(n){return X(u(n,60).g,156)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$0$Type",1730),m(1731,1,{},Oje),s.We=function(n){return XTn(this.a,u(n,60))},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$1$Type",1731),m(1739,1,nF,KMe),s.be=function(){UE(this.a,this.b,-1)},s.b=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$10$Type",1739),m(1741,1,jh,c3),s.Lb=function(n){return X(u(n,60).g,156)},s.Fb=function(n){return this===n},s.Mb=function(n){return X(u(n,60).g,156)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$11$Type",1741),m(1742,1,it,E5),s.Ad=function(n){u(n,375).be()},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$12$Type",1742),m(1743,1,Ft,KA),s.Mb=function(n){return X(u(n,60).g,9)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$13$Type",1743),m(1745,1,it,Nje),s.Ad=function(n){Pjn(this.a,u(n,60))},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$14$Type",1745),m(1744,1,nF,nTe),s.be=function(){UE(this.b,this.a,-1)},s.a=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$15$Type",1744),m(1746,1,jh,UI),s.Lb=function(n){return X(u(n,60).g,9)},s.Fb=function(n){return this===n},s.Mb=function(n){return X(u(n,60).g,9)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$16$Type",1746),m(1747,1,it,u3),s.Ad=function(n){u(n,375).be()},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$17$Type",1747),m(1748,1,{},Dje),s.We=function(n){return KTn(this.a,u(n,60))},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$18$Type",1748),m(1749,1,{},XI),s.Ue=function(){return 0},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$19$Type",1749),m(1732,1,{},KI),s.Ue=function(){return 0},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$2$Type",1732),m(1751,1,it,VMe),s.Ad=function(n){hvn(this.a,this.b,u(n,320))},s.a=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$20$Type",1751),m(1750,1,nF,YMe),s.be=function(){aUe(this.a,this.b,-1)},s.b=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$21$Type",1750),m(1752,1,jh,VI),s.Lb=function(n){return u(n,60),!0},s.Fb=function(n){return this===n},s.Mb=function(n){return u(n,60),!0},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$22$Type",1752),m(1753,1,it,qq),s.Ad=function(n){u(n,375).be()},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$23$Type",1753),m(1733,1,Ft,Uq),s.Mb=function(n){return X(u(n,60).g,9)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$3$Type",1733),m(1735,1,it,QMe),s.Ad=function(n){dvn(this.a,this.b,u(n,60))},s.a=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$4$Type",1735),m(1734,1,nF,tTe),s.be=function(){UE(this.b,this.a,-1)},s.a=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$5$Type",1734),m(1736,1,jh,Xq),s.Lb=function(n){return u(n,60),!0},s.Fb=function(n){return this===n},s.Mb=function(n){return u(n,60),!0},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$6$Type",1736),m(1737,1,it,YI),s.Ad=function(n){u(n,375).be()},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$7$Type",1737),m(1738,1,Ft,QI),s.Mb=function(n){return X(u(n,60).g,156)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$8$Type",1738),m(1740,1,it,WMe),s.Ad=function(n){j8n(this.a,this.b,u(n,60))},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$9$Type",1740),m(1547,1,Ai,vOe),s.If=function(n,t){YLn(this,u(n,37),t)};var vin;v(lr,"HorizontalGraphCompactor",1547),m(1548,1,{},Ije),s.df=function(n,t){var i,r,c;return yhe(n,t)||(i=J3(n),r=J3(t),i&&i.k==(zn(),wr)||r&&r.k==(zn(),wr))?0:(c=u(T(this.a.a,(me(),Lv)),316),lpn(c,i?i.k:(zn(),dr),r?r.k:(zn(),dr)))},s.ef=function(n,t){var i,r,c;return yhe(n,t)?1:(i=J3(n),r=J3(t),c=u(T(this.a.a,(me(),Lv)),316),ale(c,i?i.k:(zn(),dr),r?r.k:(zn(),dr)))},v(lr,"HorizontalGraphCompactor/1",1548),m(1549,1,{},VA),s.cf=function(n,t){return Mj(),n.a.i==0},v(lr,"HorizontalGraphCompactor/lambda$0$Type",1549),m(1550,1,{},_je),s.cf=function(n,t){return D5n(this.a,n,t)},v(lr,"HorizontalGraphCompactor/lambda$1$Type",1550),m(1696,1,{},dRe);var yin,kin;v(lr,"LGraphToCGraphTransformer",1696),m(1704,1,Ft,l0),s.Mb=function(n){return n!=null},v(lr,"LGraphToCGraphTransformer/0methodref$nonNull$Type",1704),m(1697,1,{},gk),s.Kb=function(n){return rl(),su(T(u(u(n,60).g,9),(me(),wi)))},v(lr,"LGraphToCGraphTransformer/lambda$0$Type",1697),m(1698,1,{},od),s.Kb=function(n){return rl(),MFe(u(u(n,60).g,156))},v(lr,"LGraphToCGraphTransformer/lambda$1$Type",1698),m(1707,1,Ft,S5),s.Mb=function(n){return rl(),X(u(n,60).g,9)},v(lr,"LGraphToCGraphTransformer/lambda$10$Type",1707),m(1708,1,it,YA),s.Ad=function(n){E5n(u(n,60))},v(lr,"LGraphToCGraphTransformer/lambda$11$Type",1708),m(1709,1,Ft,wk),s.Mb=function(n){return rl(),X(u(n,60).g,156)},v(lr,"LGraphToCGraphTransformer/lambda$12$Type",1709),m(1713,1,it,pk),s.Ad=function(n){njn(u(n,60))},v(lr,"LGraphToCGraphTransformer/lambda$13$Type",1713),m(1710,1,it,Lje),s.Ad=function(n){rwn(this.a,u(n,8))},s.a=0,v(lr,"LGraphToCGraphTransformer/lambda$14$Type",1710),m(1711,1,it,Pje),s.Ad=function(n){uwn(this.a,u(n,119))},s.a=0,v(lr,"LGraphToCGraphTransformer/lambda$15$Type",1711),m(1712,1,it,$je),s.Ad=function(n){cwn(this.a,u(n,8))},s.a=0,v(lr,"LGraphToCGraphTransformer/lambda$16$Type",1712),m(1714,1,{},QA),s.Kb=function(n){return rl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(lr,"LGraphToCGraphTransformer/lambda$17$Type",1714),m(1715,1,Ft,o3),s.Mb=function(n){return rl(),cc(u(n,17))},v(lr,"LGraphToCGraphTransformer/lambda$18$Type",1715),m(1716,1,it,Rje),s.Ad=function(n){W9n(this.a,u(n,17))},v(lr,"LGraphToCGraphTransformer/lambda$19$Type",1716),m(1700,1,it,Bje),s.Ad=function(n){Myn(this.a,u(n,156))},v(lr,"LGraphToCGraphTransformer/lambda$2$Type",1700),m(1717,1,{},WI),s.Kb=function(n){return rl(),new wn(null,new pn(u(n,25).a,16))},v(lr,"LGraphToCGraphTransformer/lambda$20$Type",1717),m(1718,1,{},mk),s.Kb=function(n){return rl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(lr,"LGraphToCGraphTransformer/lambda$21$Type",1718),m(1719,1,{},x5),s.Kb=function(n){return rl(),u(T(u(n,17),(me(),vg)),16)},v(lr,"LGraphToCGraphTransformer/lambda$22$Type",1719),m(1720,1,Ft,Kq),s.Mb=function(n){return fpn(u(n,16))},v(lr,"LGraphToCGraphTransformer/lambda$23$Type",1720),m(1721,1,it,zje),s.Ad=function(n){VTn(this.a,u(n,16))},v(lr,"LGraphToCGraphTransformer/lambda$24$Type",1721),m(1722,1,{},w1),s.Kb=function(n){return rl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(lr,"LGraphToCGraphTransformer/lambda$25$Type",1722),m(1723,1,Ft,WA),s.Mb=function(n){return rl(),cc(u(n,17))},v(lr,"LGraphToCGraphTransformer/lambda$26$Type",1723),m(1725,1,it,Fje),s.Ad=function(n){q8n(this.a,u(n,17))},v(lr,"LGraphToCGraphTransformer/lambda$27$Type",1725),m(1724,1,it,Jje),s.Ad=function(n){Qbn(this.a,u(n,70))},s.a=0,v(lr,"LGraphToCGraphTransformer/lambda$28$Type",1724),m(1699,1,it,ZMe),s.Ad=function(n){M6n(this.a,this.b,u(n,156))},v(lr,"LGraphToCGraphTransformer/lambda$3$Type",1699),m(1701,1,{},Wg),s.Kb=function(n){return rl(),new wn(null,new pn(u(n,25).a,16))},v(lr,"LGraphToCGraphTransformer/lambda$4$Type",1701),m(1702,1,{},ZI),s.Kb=function(n){return rl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(lr,"LGraphToCGraphTransformer/lambda$5$Type",1702),m(1703,1,{},vk),s.Kb=function(n){return rl(),u(T(u(n,17),(me(),vg)),16)},v(lr,"LGraphToCGraphTransformer/lambda$6$Type",1703),m(1705,1,it,Hje),s.Ad=function(n){uCn(this.a,u(n,16))},v(lr,"LGraphToCGraphTransformer/lambda$8$Type",1705),m(1706,1,it,eTe),s.Ad=function(n){Cwn(this.a,this.b,u(n,156))},v(lr,"LGraphToCGraphTransformer/lambda$9$Type",1706),m(1695,1,{},s3),s.af=function(n){var t,i,r,c,o;for(this.a=n,this.d=new gX,this.c=oe(Yme,On,124,this.a.a.a.c.length,0,1),this.b=0,i=new L(this.a.a.a);i.a>>0,n.toString(16))},s.equals=function(e){return this.Fb(e)},s.hashCode=function(){return this.Hb()},s.toString=function(){return this.Ib()};var zen,Fen,Jen;m(298,1,{298:1,2086:1},g1e),s.te=function(n){var t;return t=new g1e,t.i=4,n>1?t.c=H_e(this,n-1):t.c=this,t},s.ue=function(){return M1(this),this.b},s.ve=function(){return Db(this)},s.we=function(){return M1(this),this.k},s.xe=function(){return(this.i&4)!=0},s.ye=function(){return(this.i&1)!=0},s.Ib=function(){return xhe(this)},s.i=0;var Ar=v(Cu,"Object",1),wme=v(Cu,"Class",298);m(2058,1,lN),v(fN,"Optional",2058),m(1160,2058,lN,G),s.Fb=function(n){return n===this},s.Hb=function(){return 2040732332},s.Ib=function(){return"Optional.absent()"},s.Jb=function(n){return Nt(n),mj(),Kne};var Kne;v(fN,"Absent",1160),m(627,1,{},NX),v(fN,"Joiner",627);var jBn=Hi(fN,"Predicate");m(577,1,{178:1,577:1,3:1,48:1},NC),s.Mb=function(n){return Jze(this,n)},s.Lb=function(n){return Jze(this,n)},s.Fb=function(n){var t;return X(n,577)?(t=u(n,577),abe(this.a,t.a)):!1},s.Hb=function(){return y1e(this.a)+306654252},s.Ib=function(){return jCn(this.a)},v(fN,"Predicates/AndPredicate",577),m(411,2058,{411:1,3:1},W6),s.Fb=function(n){var t;return X(n,411)?(t=u(n,411),di(this.a,t.a)):!1},s.Hb=function(){return 1502476572+Oi(this.a)},s.Ib=function(){return sYe+this.a+")"},s.Jb=function(n){return new W6(TR(n.Kb(this.a),"the Function passed to Optional.transform() must not return null."))},v(fN,"Present",411),m(204,1,I8),s.Nb=function(n){Zr(this,n)},s.Qb=function(){rAe()},v(dn,"UnmodifiableIterator",204),m(2038,204,_8),s.Qb=function(){rAe()},s.Rb=function(n){throw $(new _t)},s.Wb=function(n){throw $(new _t)},v(dn,"UnmodifiableListIterator",2038),m(392,2038,_8),s.Ob=function(){return this.b0},s.Pb=function(){if(this.b>=this.c)throw $(new au);return this.Xb(this.b++)},s.Tb=function(){return this.b},s.Ub=function(){if(this.b<=0)throw $(new au);return this.Xb(--this.b)},s.Vb=function(){return this.b-1},s.b=0,s.c=0,v(dn,"AbstractIndexedListIterator",392),m(702,204,I8),s.Ob=function(){return LY(this)},s.Pb=function(){return vhe(this)},s.e=1,v(dn,"AbstractIterator",702),m(2046,1,{229:1}),s.Zb=function(){var n;return n=this.f,n||(this.f=this.ac())},s.Fb=function(n){return iQ(this,n)},s.Hb=function(){return Oi(this.Zb())},s.dc=function(){return this.gc()==0},s.ec=function(){return S4(this)},s.Ib=function(){return su(this.Zb())},v(dn,"AbstractMultimap",2046),m(730,2046,og),s.$b=function(){mB(this)},s._b=function(n){return kAe(this,n)},s.ac=function(){return new g9(this,this.c)},s.ic=function(n){return this.hc()},s.bc=function(){return new R3(this,this.c)},s.jc=function(){return this.mc(this.hc())},s.kc=function(){return new Hxe(this)},s.lc=function(){return fW(this.c.vc().Lc(),new W,64,this.d)},s.cc=function(n){return pi(this,n)},s.fc=function(n){return SO(this,n)},s.gc=function(){return this.d},s.mc=function(n){return jn(),new Hr(n)},s.nc=function(){return new Jxe(this)},s.oc=function(){return fW(this.c.Bc().Lc(),new ie,64,this.d)},s.pc=function(n,t){return new ZR(this,n,t,null)},s.d=0,v(dn,"AbstractMapBasedMultimap",730),m(1661,730,og),s.hc=function(){return new xo(this.a)},s.jc=function(){return jn(),jn(),Sc},s.cc=function(n){return u(pi(this,n),16)},s.fc=function(n){return u(SO(this,n),16)},s.Zb=function(){return O4(this)},s.Fb=function(n){return iQ(this,n)},s.qc=function(n){return u(pi(this,n),16)},s.rc=function(n){return u(SO(this,n),16)},s.mc=function(n){return OR(u(n,16))},s.pc=function(n,t){return WLe(this,n,u(t,16),null)},v(dn,"AbstractListMultimap",1661),m(736,1,Fr),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.c.Ob()||this.e.Ob()},s.Pb=function(){var n;return this.e.Ob()||(n=u(this.c.Pb(),45),this.b=n.jd(),this.a=u(n.kd(),18),this.e=this.a.Jc()),this.sc(this.b,this.e.Pb())},s.Qb=function(){this.e.Qb(),u(uf(this.a),18).dc()&&this.c.Qb(),--this.d.d},v(dn,"AbstractMapBasedMultimap/Itr",736),m(1098,736,Fr,Jxe),s.sc=function(n,t){return t},v(dn,"AbstractMapBasedMultimap/1",1098),m(1099,1,{},ie),s.Kb=function(n){return u(n,18).Lc()},v(dn,"AbstractMapBasedMultimap/1methodref$spliterator$Type",1099),m(1100,736,Fr,Hxe),s.sc=function(n,t){return new bw(n,t)},v(dn,"AbstractMapBasedMultimap/2",1100);var pme=Hi(mt,"Map");m(2027,1,Vw),s.wc=function(n){bO(this,n)},s.$b=function(){this.vc().$b()},s.tc=function(n){return UQ(this,n)},s._b=function(n){return!!l0e(this,n,!1)},s.uc=function(n){var t,i,r;for(i=this.vc().Jc();i.Ob();)if(t=u(i.Pb(),45),r=t.kd(),ue(n)===ue(r)||n!=null&&di(n,r))return!0;return!1},s.Fb=function(n){var t,i,r;if(n===this)return!0;if(!X(n,92)||(r=u(n,92),this.gc()!=r.gc()))return!1;for(i=r.vc().Jc();i.Ob();)if(t=u(i.Pb(),45),!this.tc(t))return!1;return!0},s.xc=function(n){return du(l0e(this,n,!1))},s.Hb=function(){return a1e(this.vc())},s.dc=function(){return this.gc()==0},s.ec=function(){return new nt(this)},s.yc=function(n,t){throw $(new gd("Put not supported on this map"))},s.zc=function(n){xE(this,n)},s.Ac=function(n){return du(l0e(this,n,!0))},s.gc=function(){return this.vc().gc()},s.Ib=function(){return oGe(this)},s.Bc=function(){return new ct(this)},v(mt,"AbstractMap",2027),m(2047,2027,Vw),s.bc=function(){return new VP(this)},s.vc=function(){return zDe(this)},s.ec=function(){var n;return n=this.g,n||(this.g=this.bc())},s.Bc=function(){var n;return n=this.i,n||(this.i=new hMe(this))},v(dn,"Maps/ViewCachingAbstractMap",2047),m(395,2047,Vw,g9),s.xc=function(n){return m8n(this,n)},s.Ac=function(n){return Tkn(this,n)},s.$b=function(){this.d==this.e.c?this.e.$b():uR(new yfe(this))},s._b=function(n){return yFe(this.d,n)},s.Dc=function(){return new dP(this)},s.Cc=function(){return this.Dc()},s.Fb=function(n){return this===n||di(this.d,n)},s.Hb=function(){return Oi(this.d)},s.ec=function(){return this.e.ec()},s.gc=function(){return this.d.gc()},s.Ib=function(){return su(this.d)},v(dn,"AbstractMapBasedMultimap/AsMap",395);var Xl=Hi(Cu,"Iterable");m(31,1,Y2),s.Ic=function(n){rc(this,n)},s.Lc=function(){return new pn(this,0)},s.Mc=function(){return new wn(null,this.Lc())},s.Ec=function(n){throw $(new gd("Add not supported on this collection"))},s.Fc=function(n){return fc(this,n)},s.$b=function(){rae(this)},s.Gc=function(n){return P2(this,n,!1)},s.Hc=function(n){return yO(this,n)},s.dc=function(){return this.gc()==0},s.Kc=function(n){return P2(this,n,!0)},s.Nc=function(){return Ofe(this)},s.Oc=function(n){return GE(this,n)},s.Ib=function(){return Fa(this)},v(mt,"AbstractCollection",31);var bf=Hi(mt,"Set");m(Ha,31,fs),s.Lc=function(){return new pn(this,1)},s.Fb=function(n){return jJe(this,n)},s.Hb=function(){return a1e(this)},v(mt,"AbstractSet",Ha),m(2030,Ha,fs),v(dn,"Sets/ImprovedAbstractSet",2030),m(2031,2030,fs),s.$b=function(){this.Pc().$b()},s.Gc=function(n){return iJe(this,n)},s.dc=function(){return this.Pc().dc()},s.Kc=function(n){var t;return this.Gc(n)&&X(n,45)?(t=u(n,45),this.Pc().ec().Kc(t.jd())):!1},s.gc=function(){return this.Pc().gc()},v(dn,"Maps/EntrySet",2031),m(1096,2031,fs,dP),s.Gc=function(n){return $1e(this.a.d.vc(),n)},s.Jc=function(){return new yfe(this.a)},s.Pc=function(){return this.a},s.Kc=function(n){var t;return $1e(this.a.d.vc(),n)?(t=u(uf(u(n,45)),45),t9n(this.a.e,t.jd()),!0):!1},s.Lc=function(){return NT(this.a.d.vc().Lc(),new bP(this.a))},v(dn,"AbstractMapBasedMultimap/AsMap/AsMapEntries",1096),m(1097,1,{},bP),s.Kb=function(n){return LPe(this.a,u(n,45))},v(dn,"AbstractMapBasedMultimap/AsMap/AsMapEntries/0methodref$wrapEntry$Type",1097),m(734,1,Fr,yfe),s.Nb=function(n){Zr(this,n)},s.Pb=function(){var n;return n=u(this.b.Pb(),45),this.a=u(n.kd(),18),LPe(this.c,n)},s.Ob=function(){return this.b.Ob()},s.Qb=function(){x9(!!this.a),this.b.Qb(),this.c.e.d-=this.a.gc(),this.a.$b(),this.a=null},v(dn,"AbstractMapBasedMultimap/AsMap/AsMapIterator",734),m(530,2030,fs,VP),s.$b=function(){this.b.$b()},s.Gc=function(n){return this.b._b(n)},s.Ic=function(n){Nt(n),this.b.wc(new cj(n))},s.dc=function(){return this.b.dc()},s.Jc=function(){return new vj(this.b.vc().Jc())},s.Kc=function(n){return this.b._b(n)?(this.b.Ac(n),!0):!1},s.gc=function(){return this.b.gc()},v(dn,"Maps/KeySet",530),m(332,530,fs,R3),s.$b=function(){var n;uR((n=this.b.vc().Jc(),new Uoe(this,n)))},s.Hc=function(n){return this.b.ec().Hc(n)},s.Fb=function(n){return this===n||di(this.b.ec(),n)},s.Hb=function(){return Oi(this.b.ec())},s.Jc=function(){var n;return n=this.b.vc().Jc(),new Uoe(this,n)},s.Kc=function(n){var t,i;return i=0,t=u(this.b.Ac(n),18),t&&(i=t.gc(),t.$b(),this.a.d-=i),i>0},s.Lc=function(){return this.b.ec().Lc()},v(dn,"AbstractMapBasedMultimap/KeySet",332),m(735,1,Fr,Uoe),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.c.Ob()},s.Pb=function(){return this.a=u(this.c.Pb(),45),this.a.jd()},s.Qb=function(){var n;x9(!!this.a),n=u(this.a.kd(),18),this.c.Qb(),this.b.a.d-=n.gc(),n.$b(),this.a=null},v(dn,"AbstractMapBasedMultimap/KeySet/1",735),m(489,395,{92:1,134:1},ST),s.bc=function(){return this.Qc()},s.ec=function(){return this.Sc()},s.Qc=function(){return new WC(this.c,this.Uc())},s.Rc=function(){return this.Uc().Rc()},s.Sc=function(){var n;return n=this.b,n||(this.b=this.Qc())},s.Tc=function(){return this.Uc().Tc()},s.Uc=function(){return u(this.d,134)},v(dn,"AbstractMapBasedMultimap/SortedAsMap",489),m(437,489,$ge,Zj),s.bc=function(){return new w9(this.a,u(u(this.d,134),138))},s.Qc=function(){return new w9(this.a,u(u(this.d,134),138))},s.ec=function(){var n;return n=this.b,u(n||(this.b=new w9(this.a,u(u(this.d,134),138))),277)},s.Sc=function(){var n;return n=this.b,u(n||(this.b=new w9(this.a,u(u(this.d,134),138))),277)},s.Uc=function(){return u(u(this.d,134),138)},s.Vc=function(n){return u(u(this.d,134),138).Vc(n)},s.Wc=function(n){return u(u(this.d,134),138).Wc(n)},s.Xc=function(n,t){return new Zj(this.a,u(u(this.d,134),138).Xc(n,t))},s.Yc=function(n){return u(u(this.d,134),138).Yc(n)},s.Zc=function(n){return u(u(this.d,134),138).Zc(n)},s.$c=function(n,t){return new Zj(this.a,u(u(this.d,134),138).$c(n,t))},v(dn,"AbstractMapBasedMultimap/NavigableAsMap",437),m(488,332,lYe,WC),s.Lc=function(){return this.b.ec().Lc()},v(dn,"AbstractMapBasedMultimap/SortedKeySet",488),m(394,488,Rge,w9),v(dn,"AbstractMapBasedMultimap/NavigableKeySet",394),m(539,31,Y2,ZR),s.Ec=function(n){var t,i;return Ds(this),i=this.d.dc(),t=this.d.Ec(n),t&&(++this.f.d,i&&CT(this)),t},s.Fc=function(n){var t,i,r;return n.dc()?!1:(r=(Ds(this),this.d.gc()),t=this.d.Fc(n),t&&(i=this.d.gc(),this.f.d+=i-r,r==0&&CT(this)),t)},s.$b=function(){var n;n=(Ds(this),this.d.gc()),n!=0&&(this.d.$b(),this.f.d-=n,hR(this))},s.Gc=function(n){return Ds(this),this.d.Gc(n)},s.Hc=function(n){return Ds(this),this.d.Hc(n)},s.Fb=function(n){return n===this?!0:(Ds(this),di(this.d,n))},s.Hb=function(){return Ds(this),Oi(this.d)},s.Jc=function(){return Ds(this),new rfe(this)},s.Kc=function(n){var t;return Ds(this),t=this.d.Kc(n),t&&(--this.f.d,hR(this)),t},s.gc=function(){return tTe(this)},s.Lc=function(){return Ds(this),this.d.Lc()},s.Ib=function(){return Ds(this),su(this.d)},v(dn,"AbstractMapBasedMultimap/WrappedCollection",539);var wl=Hi(mt,"List");m(732,539,{20:1,31:1,18:1,16:1},Nfe),s.gd=function(n){Vb(this,n)},s.Lc=function(){return Ds(this),this.d.Lc()},s._c=function(n,t){var i;Ds(this),i=this.d.dc(),u(this.d,16)._c(n,t),++this.a.d,i&&CT(this)},s.ad=function(n,t){var i,r,c;return t.dc()?!1:(c=(Ds(this),this.d.gc()),i=u(this.d,16).ad(n,t),i&&(r=this.d.gc(),this.a.d+=r-c,c==0&&CT(this)),i)},s.Xb=function(n){return Ds(this),u(this.d,16).Xb(n)},s.bd=function(n){return Ds(this),u(this.d,16).bd(n)},s.cd=function(){return Ds(this),new ITe(this)},s.dd=function(n){return Ds(this),new ZIe(this,n)},s.ed=function(n){var t;return Ds(this),t=u(this.d,16).ed(n),--this.a.d,hR(this),t},s.fd=function(n,t){return Ds(this),u(this.d,16).fd(n,t)},s.hd=function(n,t){return Ds(this),WLe(this.a,this.e,u(this.d,16).hd(n,t),this.b?this.b:this)},v(dn,"AbstractMapBasedMultimap/WrappedList",732),m(1095,732,{20:1,31:1,18:1,16:1,59:1},kOe),v(dn,"AbstractMapBasedMultimap/RandomAccessWrappedList",1095),m(619,1,Fr,rfe),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return P9(this),this.b.Ob()},s.Pb=function(){return P9(this),this.b.Pb()},s.Qb=function(){rOe(this)},v(dn,"AbstractMapBasedMultimap/WrappedCollection/WrappedIterator",619),m(733,619,Qh,ITe,ZIe),s.Qb=function(){rOe(this)},s.Rb=function(n){var t;t=tTe(this.a)==0,(P9(this),u(this.b,128)).Rb(n),++this.a.a.d,t&&CT(this.a)},s.Sb=function(){return(P9(this),u(this.b,128)).Sb()},s.Tb=function(){return(P9(this),u(this.b,128)).Tb()},s.Ub=function(){return(P9(this),u(this.b,128)).Ub()},s.Vb=function(){return(P9(this),u(this.b,128)).Vb()},s.Wb=function(n){(P9(this),u(this.b,128)).Wb(n)},v(dn,"AbstractMapBasedMultimap/WrappedList/WrappedListIterator",733),m(731,539,lYe,Sle),s.Lc=function(){return Ds(this),this.d.Lc()},v(dn,"AbstractMapBasedMultimap/WrappedSortedSet",731),m(1094,731,Rge,CTe),v(dn,"AbstractMapBasedMultimap/WrappedNavigableSet",1094),m(1093,539,fs,XOe),s.Lc=function(){return Ds(this),this.d.Lc()},v(dn,"AbstractMapBasedMultimap/WrappedSet",1093),m(1102,1,{},W),s.Kb=function(n){return h9n(u(n,45))},v(dn,"AbstractMapBasedMultimap/lambda$1$Type",1102),m(1101,1,{},Z6),s.Kb=function(n){return new bw(this.a,n)},v(dn,"AbstractMapBasedMultimap/lambda$2$Type",1101);var wg=Hi(mt,"Map/Entry");m(358,1,hZ),s.Fb=function(n){var t;return X(n,45)?(t=u(n,45),C1(this.jd(),t.jd())&&C1(this.kd(),t.kd())):!1},s.Hb=function(){var n,t;return n=this.jd(),t=this.kd(),(n==null?0:Oi(n))^(t==null?0:Oi(t))},s.ld=function(n){throw $(new _t)},s.Ib=function(){return this.jd()+"="+this.kd()},v(dn,fYe,358),m(U0,31,Y2),s.$b=function(){this.md().$b()},s.Gc=function(n){var t;return X(n,45)?(t=u(n,45),_yn(this.md(),t.jd(),t.kd())):!1},s.Kc=function(n){var t;return X(n,45)?(t=u(n,45),$Le(this.md(),t.jd(),t.kd())):!1},s.gc=function(){return this.md().d},v(dn,"Multimaps/Entries",U0),m(737,U0,Y2,E1),s.Jc=function(){return this.a.kc()},s.md=function(){return this.a},s.Lc=function(){return this.a.lc()},v(dn,"AbstractMultimap/Entries",737),m(738,737,fs,xoe),s.Lc=function(){return this.a.lc()},s.Fb=function(n){return T0e(this,n)},s.Hb=function(){return FBe(this)},v(dn,"AbstractMultimap/EntrySet",738),m(739,31,Y2,DC),s.$b=function(){this.a.$b()},s.Gc=function(n){return xkn(this.a,n)},s.Jc=function(){return this.a.nc()},s.gc=function(){return this.a.d},s.Lc=function(){return this.a.oc()},v(dn,"AbstractMultimap/Values",739),m(2049,31,{833:1,20:1,31:1,18:1}),s.Ic=function(n){Nt(n),z3(this).Ic(new VU(n))},s.Lc=function(){var n;return n=z3(this).Lc(),fW(n,new Me,64|n.wd()&1296,this.a.d)},s.Ec=function(n){return Noe(),!0},s.Fc=function(n){return Nt(this),Nt(n),X(n,540)?Jyn(u(n,833)):!n.dc()&&AY(this,n.Jc())},s.Gc=function(n){var t;return t=u(L2(O4(this.a),n),18),(t?t.gc():0)>0},s.Fb=function(n){return vOn(this,n)},s.Hb=function(){return Oi(z3(this))},s.dc=function(){return z3(this).dc()},s.Kc=function(n){return Eqe(this,n,1)>0},s.Ib=function(){return su(z3(this))},v(dn,"AbstractMultiset",2049),m(2051,2030,fs),s.$b=function(){mB(this.a.a)},s.Gc=function(n){var t,i;return X(n,490)?(i=u(n,416),u(i.a.kd(),18).gc()<=0?!1:(t=cLe(this.a,i.a.jd()),t==u(i.a.kd(),18).gc())):!1},s.Kc=function(n){var t,i,r,c;return X(n,490)&&(i=u(n,416),t=i.a.jd(),r=u(i.a.kd(),18).gc(),r!=0)?(c=this.a,pTn(c,t,r)):!1},v(dn,"Multisets/EntrySet",2051),m(1108,2051,fs,rj),s.Jc=function(){return new Kxe(zDe(O4(this.a.a)).Jc())},s.gc=function(){return O4(this.a.a).gc()},v(dn,"AbstractMultiset/EntrySet",1108),m(618,730,og),s.hc=function(){return this.nd()},s.jc=function(){return this.od()},s.cc=function(n){return this.pd(n)},s.fc=function(n){return this.qd(n)},s.Zb=function(){var n;return n=this.f,n||(this.f=this.ac())},s.od=function(){return jn(),jn(),hJ},s.Fb=function(n){return iQ(this,n)},s.pd=function(n){return u(pi(this,n),22)},s.qd=function(n){return u(SO(this,n),22)},s.mc=function(n){return jn(),new f9(u(n,22))},s.pc=function(n,t){return new XOe(this,n,u(t,22))},v(dn,"AbstractSetMultimap",618),m(1689,618,og),s.hc=function(){return new vd(this.b)},s.nd=function(){return new vd(this.b)},s.jc=function(){return Ufe(new vd(this.b))},s.od=function(){return Ufe(new vd(this.b))},s.cc=function(n){return u(u(pi(this,n),22),83)},s.pd=function(n){return u(u(pi(this,n),22),83)},s.fc=function(n){return u(u(SO(this,n),22),83)},s.qd=function(n){return u(u(SO(this,n),22),83)},s.mc=function(n){return X(n,277)?Ufe(u(n,277)):(jn(),new ole(u(n,83)))},s.Zb=function(){var n;return n=this.f,n||(this.f=X(this.c,138)?new Zj(this,u(this.c,138)):X(this.c,134)?new ST(this,u(this.c,134)):new g9(this,this.c))},s.pc=function(n,t){return X(t,277)?new CTe(this,n,u(t,277)):new Sle(this,n,u(t,83))},v(dn,"AbstractSortedSetMultimap",1689),m(1690,1689,og),s.Zb=function(){var n;return n=this.f,u(u(n||(this.f=X(this.c,138)?new Zj(this,u(this.c,138)):X(this.c,134)?new ST(this,u(this.c,134)):new g9(this,this.c)),134),138)},s.ec=function(){var n;return n=this.i,u(u(n||(this.i=X(this.c,138)?new w9(this,u(this.c,138)):X(this.c,134)?new WC(this,u(this.c,134)):new R3(this,this.c)),83),277)},s.bc=function(){return X(this.c,138)?new w9(this,u(this.c,138)):X(this.c,134)?new WC(this,u(this.c,134)):new R3(this,this.c)},v(dn,"AbstractSortedKeySortedSetMultimap",1690),m(2071,1,{2008:1}),s.Fb=function(n){return sAn(this,n)},s.Hb=function(){var n;return a1e((n=this.g,n||(this.g=new b0(this))))},s.Ib=function(){var n;return oGe((n=this.f,n||(this.f=new ele(this))))},v(dn,"AbstractTable",2071),m(669,Ha,fs,b0),s.$b=function(){cAe()},s.Gc=function(n){var t,i;return X(n,468)?(t=u(n,687),i=u(L2(fIe(this.a),j0(t.c.e,t.b)),92),!!i&&$1e(i.vc(),new bw(j0(t.c.c,t.a),P4(t.c,t.b,t.a)))):!1},s.Jc=function(){return F5n(this.a)},s.Kc=function(n){var t,i;return X(n,468)?(t=u(n,687),i=u(L2(fIe(this.a),j0(t.c.e,t.b)),92),!!i&&Vkn(i.vc(),new bw(j0(t.c.c,t.a),P4(t.c,t.b,t.a)))):!1},s.gc=function(){return wDe(this.a)},s.Lc=function(){return Gyn(this.a)},v(dn,"AbstractTable/CellSet",669),m(1987,31,Y2,FU),s.$b=function(){cAe()},s.Gc=function(n){return WAn(this.a,n)},s.Jc=function(){return J5n(this.a)},s.gc=function(){return wDe(this.a)},s.Lc=function(){return OLe(this.a)},v(dn,"AbstractTable/Values",1987),m(1662,1661,og),v(dn,"ArrayListMultimapGwtSerializationDependencies",1662),m(506,1662,og,OX,Sae),s.hc=function(){return new xo(this.a)},s.a=0,v(dn,"ArrayListMultimap",506),m(668,2071,{668:1,2008:1,3:1},jqe),v(dn,"ArrayTable",668),m(1983,392,_8,nOe),s.Xb=function(n){return new w1e(this.a,n)},v(dn,"ArrayTable/1",1983),m(1984,1,{},JU),s.rd=function(n){return new w1e(this.a,n)},v(dn,"ArrayTable/1methodref$getCell$Type",1984),m(2072,1,{687:1}),s.Fb=function(n){var t;return n===this?!0:X(n,468)?(t=u(n,687),C1(j0(this.c.e,this.b),j0(t.c.e,t.b))&&C1(j0(this.c.c,this.a),j0(t.c.c,t.a))&&C1(P4(this.c,this.b,this.a),P4(t.c,t.b,t.a))):!1},s.Hb=function(){return RB(z(B(Ar,1),On,1,5,[j0(this.c.e,this.b),j0(this.c.c,this.a),P4(this.c,this.b,this.a)]))},s.Ib=function(){return"("+j0(this.c.e,this.b)+","+j0(this.c.c,this.a)+")="+P4(this.c,this.b,this.a)},v(dn,"Tables/AbstractCell",2072),m(468,2072,{468:1,687:1},w1e),s.a=0,s.b=0,s.d=0,v(dn,"ArrayTable/2",468),m(1986,1,{},X5),s.rd=function(n){return z$e(this.a,n)},v(dn,"ArrayTable/2methodref$getValue$Type",1986),m(1985,392,_8,tOe),s.Xb=function(n){return z$e(this.a,n)},v(dn,"ArrayTable/3",1985),m(2039,2027,Vw),s.$b=function(){uR(this.kc())},s.vc=function(){return new uj(this)},s.lc=function(){return new HIe(this.kc(),this.gc())},v(dn,"Maps/IteratorBasedAbstractMap",2039),m(826,2039,Vw),s.$b=function(){throw $(new _t)},s._b=function(n){return jAe(this.c,n)},s.kc=function(){return new iOe(this,this.c.b.c.gc())},s.lc=function(){return iV(this.c.b.c.gc(),16,new gP(this))},s.xc=function(n){var t;return t=u(eE(this.c,n),15),t?this.td(t.a):null},s.dc=function(){return this.c.b.c.dc()},s.ec=function(){return hV(this.c)},s.yc=function(n,t){var i;if(i=u(eE(this.c,n),15),!i)throw $(new Gn(this.sd()+" "+n+" not in "+hV(this.c)));return this.ud(i.a,t)},s.Ac=function(n){throw $(new _t)},s.gc=function(){return this.c.b.c.gc()},v(dn,"ArrayTable/ArrayMap",826),m(1982,1,{},gP),s.rd=function(n){return dIe(this.a,n)},v(dn,"ArrayTable/ArrayMap/0methodref$getEntry$Type",1982),m(1980,358,hZ,YAe),s.jd=function(){return apn(this.a,this.b)},s.kd=function(){return this.a.td(this.b)},s.ld=function(n){return this.a.ud(this.b,n)},s.b=0,v(dn,"ArrayTable/ArrayMap/1",1980),m(1981,392,_8,iOe),s.Xb=function(n){return dIe(this.a,n)},v(dn,"ArrayTable/ArrayMap/2",1981),m(1979,826,Vw,nIe),s.sd=function(){return"Column"},s.td=function(n){return P4(this.b,this.a,n)},s.ud=function(n,t){return jze(this.b,this.a,n,t)},s.a=0,v(dn,"ArrayTable/Row",1979),m(827,826,Vw,ele),s.td=function(n){return new nIe(this.a,n)},s.yc=function(n,t){return u(t,92),_bn()},s.ud=function(n,t){return u(t,92),Lbn()},s.sd=function(){return"Row"},v(dn,"ArrayTable/RowMap",827),m(1126,1,bl,QAe),s.yd=function(n){return(this.a.wd()&-262&n)!=0},s.wd=function(){return this.a.wd()&-262},s.xd=function(){return this.a.xd()},s.Nb=function(n){this.a.Nb(new ZAe(n,this.b))},s.zd=function(n){return this.a.zd(new WAe(n,this.b))},v(dn,"CollectSpliterators/1",1126),m(1127,1,it,WAe),s.Ad=function(n){this.a.Ad(this.b.Kb(n))},v(dn,"CollectSpliterators/1/lambda$0$Type",1127),m(1128,1,it,ZAe),s.Ad=function(n){this.a.Ad(this.b.Kb(n))},v(dn,"CollectSpliterators/1/lambda$1$Type",1128),m(1123,1,bl,jNe),s.yd=function(n){return((16464|this.b)&n)!=0},s.wd=function(){return 16464|this.b},s.xd=function(){return this.a.xd()},s.Nb=function(n){this.a.Oe(new nMe(n,this.c))},s.zd=function(n){return this.a.Pe(new eMe(n,this.c))},s.b=0,v(dn,"CollectSpliterators/1WithCharacteristics",1123),m(1124,1,aN,eMe),s.Bd=function(n){this.a.Ad(this.b.rd(n))},v(dn,"CollectSpliterators/1WithCharacteristics/lambda$0$Type",1124),m(1125,1,aN,nMe),s.Bd=function(n){this.a.Ad(this.b.rd(n))},v(dn,"CollectSpliterators/1WithCharacteristics/lambda$1$Type",1125),m(1119,1,bl),s.yd=function(n){return(this.a&n)!=0},s.wd=function(){return this.a},s.xd=function(){return this.e&&(this.b=Gse(this.b,this.e.xd())),Gse(this.b,0)},s.Nb=function(n){this.e&&(this.e.Nb(n),this.e=null),this.c.Nb(new tMe(this,n)),this.b=0},s.zd=function(n){for(;;){if(this.e&&this.e.zd(n))return Gj(this.b,hN)&&(this.b=lf(this.b,1)),!0;if(this.e=null,!this.c.zd(new K5(this)))return!1}},s.a=0,s.b=0,v(dn,"CollectSpliterators/FlatMapSpliterator",1119),m(1121,1,it,K5),s.Ad=function(n){c2n(this.a,n)},v(dn,"CollectSpliterators/FlatMapSpliterator/lambda$0$Type",1121),m(1122,1,it,tMe),s.Ad=function(n){p5n(this.a,this.b,n)},v(dn,"CollectSpliterators/FlatMapSpliterator/lambda$1$Type",1122),m(1120,1119,bl,sPe),v(dn,"CollectSpliterators/FlatMapSpliteratorOfObject",1120),m(254,1,dZ),s.Dd=function(n){return this.Cd(u(n,254))},s.Cd=function(n){var t;return n==(EX(),Yne)?1:n==(jX(),Vne)?-1:(t=(eR(),dO(this.a,n.a)),t!=0?t:(Pn(),X(this,513)==X(n,513)?0:X(this,513)?1:-1))},s.Gd=function(){return this.a},s.Fb=function(n){return Lde(this,n)},v(dn,"Cut",254),m(1793,254,dZ,Fxe),s.Cd=function(n){return n==this?0:1},s.Ed=function(n){throw $(new loe)},s.Fd=function(n){n.a+="+∞)"},s.Gd=function(){throw $(new Uc(hYe))},s.Hb=function(){return yd(),jde(this)},s.Hd=function(n){return!1},s.Ib=function(){return"+∞"};var Vne;v(dn,"Cut/AboveAll",1793),m(513,254,{254:1,513:1,3:1,35:1},oOe),s.Ed=function(n){uo((n.a+="(",n),this.a)},s.Fd=function(n){Fb(uo(n,this.a),93)},s.Hb=function(){return~Oi(this.a)},s.Hd=function(n){return eR(),dO(this.a,n)<0},s.Ib=function(){return"/"+this.a+"\\"},v(dn,"Cut/AboveValue",513),m(1792,254,dZ,zxe),s.Cd=function(n){return n==this?0:-1},s.Ed=function(n){n.a+="(-∞"},s.Fd=function(n){throw $(new loe)},s.Gd=function(){throw $(new Uc(hYe))},s.Hb=function(){return yd(),jde(this)},s.Hd=function(n){return!0},s.Ib=function(){return"-∞"};var Yne;v(dn,"Cut/BelowAll",1792),m(1794,254,dZ,sOe),s.Ed=function(n){uo((n.a+="[",n),this.a)},s.Fd=function(n){Fb(uo(n,this.a),41)},s.Hb=function(){return Oi(this.a)},s.Hd=function(n){return eR(),dO(this.a,n)<=0},s.Ib=function(){return"\\"+this.a+"/"},v(dn,"Cut/BelowValue",1794),m(535,1,Wh),s.Ic=function(n){rc(this,n)},s.Ib=function(){return xjn(u(TR(this,"use Optional.orNull() instead of Optional.or(null)"),20).Jc())},v(dn,"FluentIterable",535),m(433,535,Wh,Xj),s.Jc=function(){return new qn(Vn(this.a.Jc(),new ee))},v(dn,"FluentIterable/2",433),m(36,1,{},ee),s.Kb=function(n){return u(n,20).Jc()},s.Fb=function(n){return this===n},v(dn,"FluentIterable/2/0methodref$iterator$Type",36),m(1040,535,Wh,jTe),s.Jc=function(){return qh(this)},v(dn,"FluentIterable/3",1040),m(714,392,_8,sle),s.Xb=function(n){return this.a[n].Jc()},v(dn,"FluentIterable/3/1",714),m(2032,1,{}),s.Ib=function(){return su(this.Id().b)},v(dn,"ForwardingObject",2032),m(2033,2032,dYe),s.Id=function(){return this.Jd()},s.Ic=function(n){rc(this,n)},s.Lc=function(){return new pn(this,0)},s.Mc=function(){return new wn(null,this.Lc())},s.Ec=function(n){return this.Jd(),AAe()},s.Fc=function(n){return this.Jd(),MAe()},s.$b=function(){this.Jd(),CAe()},s.Gc=function(n){return this.Jd().Gc(n)},s.Hc=function(n){return this.Jd().Hc(n)},s.dc=function(){return this.Jd().b.dc()},s.Jc=function(){return this.Jd().Jc()},s.Kc=function(n){return this.Jd(),TAe()},s.gc=function(){return this.Jd().b.gc()},s.Nc=function(){return this.Jd().Nc()},s.Oc=function(n){return this.Jd().Oc(n)},v(dn,"ForwardingCollection",2033),m(2040,31,Bge),s.Jc=function(){return this.Md()},s.Ec=function(n){throw $(new _t)},s.Fc=function(n){throw $(new _t)},s.Kd=function(){var n;return n=this.c,n||(this.c=this.Ld())},s.$b=function(){throw $(new _t)},s.Gc=function(n){return n!=null&&P2(this,n,!1)},s.Ld=function(){switch(this.gc()){case 0:return cR(),Zne;case 1:return new HK(Nt(this.Md().Pb()));default:return new cfe(this,this.Nc())}},s.Kc=function(n){throw $(new _t)},v(dn,"ImmutableCollection",2040),m(1259,2040,Bge,pP),s.Jc=function(){return $4(new tc(this.a.b.Jc()))},s.Gc=function(n){return n!=null&&Sj(this.a,n)},s.Hc=function(n){return Koe(this.a,n)},s.dc=function(){return this.a.b.dc()},s.Md=function(){return $4(new tc(this.a.b.Jc()))},s.gc=function(){return this.a.b.gc()},s.Nc=function(){return this.a.b.Nc()},s.Oc=function(n){return Voe(this.a,n)},s.Ib=function(){return su(this.a.b)},v(dn,"ForwardingImmutableCollection",1259),m(311,2040,L8),s.Jc=function(){return this.Md()},s.cd=function(){return this.Nd(0)},s.dd=function(n){return this.Nd(n)},s.gd=function(n){Vb(this,n)},s.Lc=function(){return new pn(this,16)},s.hd=function(n,t){return this.Od(n,t)},s._c=function(n,t){throw $(new _t)},s.ad=function(n,t){throw $(new _t)},s.Kd=function(){return this},s.Fb=function(n){return lOn(this,n)},s.Hb=function(){return P7n(this)},s.bd=function(n){return n==null?-1:HSn(this,n)},s.Md=function(){return this.Nd(0)},s.Nd=function(n){return LK(this,n)},s.ed=function(n){throw $(new _t)},s.fd=function(n,t){throw $(new _t)},s.Od=function(n,t){var i;return qB((i=new fMe(this),new C0(i,n,t)))},v(dn,"ImmutableList",311),m(2067,311,L8),s.Jc=function(){return $4(this.Pd().Jc())},s.hd=function(n,t){return qB(this.Pd().hd(n,t))},s.Gc=function(n){return n!=null&&this.Pd().Gc(n)},s.Hc=function(n){return this.Pd().Hc(n)},s.Fb=function(n){return di(this.Pd(),n)},s.Xb=function(n){return j0(this,n)},s.Hb=function(){return Oi(this.Pd())},s.bd=function(n){return this.Pd().bd(n)},s.dc=function(){return this.Pd().dc()},s.Md=function(){return $4(this.Pd().Jc())},s.gc=function(){return this.Pd().gc()},s.Od=function(n,t){return qB(this.Pd().hd(n,t))},s.Nc=function(){return this.Pd().Oc(oe(Ar,On,1,this.Pd().gc(),5,1))},s.Oc=function(n){return this.Pd().Oc(n)},s.Ib=function(){return su(this.Pd())},v(dn,"ForwardingImmutableList",2067),m(717,1,P8),s.vc=function(){return $b(this)},s.wc=function(n){bO(this,n)},s.ec=function(){return hV(this)},s.Bc=function(){return this.Td()},s.$b=function(){throw $(new _t)},s._b=function(n){return this.xc(n)!=null},s.uc=function(n){return this.Td().Gc(n)},s.Rd=function(){return new qU(this)},s.Sd=function(){return new UU(this)},s.Fb=function(n){return Akn(this,n)},s.Hb=function(){return $b(this).Hb()},s.dc=function(){return this.gc()==0},s.yc=function(n,t){return Pbn()},s.Ac=function(n){throw $(new _t)},s.Ib=function(){return UMn(this)},s.Td=function(){return this.e?this.e:this.e=this.Sd()},s.c=null,s.d=null,s.e=null,v(dn,"ImmutableMap",717),m(718,717,P8),s._b=function(n){return jAe(this,n)},s.uc=function(n){return pMe(this.b,n)},s.Qd=function(){return cFe(new wP(this))},s.Rd=function(){return cFe(LIe(this.b))},s.Sd=function(){return new pP(PIe(this.b))},s.Fb=function(n){return vMe(this.b,n)},s.xc=function(n){return eE(this,n)},s.Hb=function(){return Oi(this.b.c)},s.dc=function(){return this.b.c.dc()},s.gc=function(){return this.b.c.gc()},s.Ib=function(){return su(this.b.c)},v(dn,"ForwardingImmutableMap",718),m(2034,2033,bZ),s.Id=function(){return this.Ud()},s.Jd=function(){return this.Ud()},s.Lc=function(){return new pn(this,1)},s.Fb=function(n){return n===this||this.Ud().Fb(n)},s.Hb=function(){return this.Ud().Hb()},v(dn,"ForwardingSet",2034),m(1055,2034,bZ,wP),s.Id=function(){return _9(this.a.b)},s.Jd=function(){return _9(this.a.b)},s.Gc=function(n){if(X(n,45)&&u(n,45).jd()==null)return!1;try{return mMe(_9(this.a.b),n)}catch(t){if(t=sr(t),X(t,211))return!1;throw $(t)}},s.Ud=function(){return _9(this.a.b)},s.Oc=function(n){var t,i;return t=k_e(_9(this.a.b),n),_9(this.a.b).b.gc()=0?"+":"")+(i/60|0),t=D$(k.Math.abs(i)%60),(vGe(),snn)[this.q.getDay()]+" "+lnn[this.q.getMonth()]+" "+D$(this.q.getDate())+" "+D$(this.q.getHours())+":"+D$(this.q.getMinutes())+":"+D$(this.q.getSeconds())+" GMT"+n+t+" "+this.q.getFullYear()};var lJ=v(mt,"Date",205);m(1977,205,jYe,zHe),s.a=!1,s.b=0,s.c=0,s.d=0,s.e=0,s.f=0,s.g=!1,s.i=0,s.j=0,s.k=0,s.n=0,s.o=0,s.p=0,v("com.google.gwt.i18n.shared.impl","DateRecord",1977),m(2026,1,{}),s.ne=function(){return null},s.oe=function(){return null},s.pe=function(){return null},s.qe=function(){return null},s.re=function(){return null},v(oy,"JSONValue",2026),m(139,2026,{139:1},bd,n9),s.Fb=function(n){return X(n,139)?Mae(this.a,u(n,139).a):!1},s.me=function(){return tbn},s.Hb=function(){return aae(this.a)},s.ne=function(){return this},s.Ib=function(){var n,t,i;for(i=new il("["),t=0,n=this.a.length;t0&&(i.a+=","),uo(i,C2(this,t));return i.a+="]",i.a},v(oy,"JSONArray",139),m(479,2026,{479:1},t9),s.me=function(){return ibn},s.oe=function(){return this},s.Ib=function(){return Pn(),""+this.a},s.a=!1;var Yen,Qen;v(oy,"JSONBoolean",479),m(981,63,H1,Vxe),v(oy,"JSONException",981),m(1017,2026,{},wt),s.me=function(){return obn},s.Ib=function(){return Vo};var Wen;v(oy,"JSONNull",1017),m(265,2026,{265:1},k3),s.Fb=function(n){return X(n,265)?this.a==u(n,265).a:!1},s.me=function(){return rbn},s.Hb=function(){return b4(this.a)},s.pe=function(){return this},s.Ib=function(){return this.a+""},s.a=0,v(oy,"JSONNumber",265),m(149,2026,{149:1},r4,i9),s.Fb=function(n){return X(n,149)?Mae(this.a,u(n,149).a):!1},s.me=function(){return cbn},s.Hb=function(){return aae(this.a)},s.qe=function(){return this},s.Ib=function(){var n,t,i,r,c,o,l;for(l=new il("{"),n=!0,o=BY(this,oe(ze,Se,2,0,6,1)),i=o,r=0,c=i.length;r=0?":"+this.c:"")+")"},s.c=0;var _me=v(Cu,"StackTraceElement",324);Jen={3:1,472:1,35:1,2:1};var ze=v(Cu,zge,2);m(111,418,{472:1},pd,jj,cf),v(Cu,"StringBuffer",111),m(106,418,{472:1},p0,o4,il),v(Cu,"StringBuilder",106),m(691,99,iF,Doe),v(Cu,"StringIndexOutOfBoundsException",691),m(2107,1,{});var tnn;m(46,63,{3:1,101:1,63:1,80:1,46:1},_t,gd),v(Cu,"UnsupportedOperationException",46),m(247,242,{3:1,35:1,242:1,247:1},MO,Joe),s.Dd=function(n){return vKe(this,u(n,247))},s.se=function(){return F2(UKe(this))},s.Fb=function(n){var t;return this===n?!0:X(n,247)?(t=u(n,247),this.e==t.e&&vKe(this,t)==0):!1},s.Hb=function(){var n;return this.b!=0?this.b:this.a<54?(n=Pu(this.f),this.b=Rt(Rr(n,-1)),this.b=33*this.b+Rt(Rr(kw(n,32),-1)),this.b=17*this.b+sc(this.e),this.b):(this.b=17*wFe(this.c)+sc(this.e),this.b)},s.Ib=function(){return UKe(this)},s.a=0,s.b=0,s.d=0,s.e=0,s.f=0;var inn,pg,Lme,Pme,$me,Rme,Bme,zme,cte=v("java.math","BigDecimal",247);m(91,242,{3:1,35:1,242:1,91:1},D1,hLe,zb,xJe,E0),s.Dd=function(n){return yJe(this,u(n,91))},s.se=function(){return F2(lZ(this,0))},s.Fb=function(n){return ude(this,n)},s.Hb=function(){return wFe(this)},s.Ib=function(){return lZ(this,0)},s.b=-2,s.c=0,s.d=0,s.e=0;var rnn,fJ,cnn,ute,aJ,KS,Sv=v("java.math","BigInteger",91),unn,onn,my,VS;m(484,2027,Vw),s.$b=function(){Hu(this)},s._b=function(n){return so(this,n)},s.uc=function(n){return eFe(this,n,this.i)||eFe(this,n,this.f)},s.vc=function(){return new on(this)},s.xc=function(n){return Bn(this,n)},s.yc=function(n,t){return ei(this,n,t)},s.Ac=function(n){return L4(this,n)},s.gc=function(){return xj(this)},s.g=0,v(mt,"AbstractHashMap",484),m(306,Ha,fs,on),s.$b=function(){this.a.$b()},s.Gc=function(n){return JLe(this,n)},s.Jc=function(){return new D2(this.a)},s.Kc=function(n){var t;return JLe(this,n)?(t=u(n,45).jd(),this.a.Ac(t),!0):!1},s.gc=function(){return this.a.gc()},v(mt,"AbstractHashMap/EntrySet",306),m(307,1,Fr,D2),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return Q3(this)},s.Ob=function(){return this.b},s.Qb=function(){gRe(this)},s.b=!1,s.d=0,v(mt,"AbstractHashMap/EntrySetIterator",307),m(417,1,Fr,qc),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return zX(this)},s.Pb=function(){return oae(this)},s.Qb=function(){As(this)},s.b=0,s.c=-1,v(mt,"AbstractList/IteratorImpl",417),m(97,417,Qh,qr),s.Qb=function(){As(this)},s.Rb=function(n){d2(this,n)},s.Sb=function(){return this.b>0},s.Tb=function(){return this.b},s.Ub=function(){return at(this.b>0),this.a.Xb(this.c=--this.b)},s.Vb=function(){return this.b-1},s.Wb=function(n){l2(this.c!=-1),this.a.fd(this.c,n)},v(mt,"AbstractList/ListIteratorImpl",97),m(258,56,$8,C0),s._c=function(n,t){S2(n,this.b),this.c._c(this.a+n,t),++this.b},s.Xb=function(n){return vn(n,this.b),this.c.Xb(this.a+n)},s.ed=function(n){var t;return vn(n,this.b),t=this.c.ed(this.a+n),--this.b,t},s.fd=function(n,t){return vn(n,this.b),this.c.fd(this.a+n,t)},s.gc=function(){return this.b},s.a=0,s.b=0,v(mt,"AbstractList/SubList",258),m(232,Ha,fs,nt),s.$b=function(){this.a.$b()},s.Gc=function(n){return this.a._b(n)},s.Jc=function(){var n;return n=this.a.vc().Jc(),new gt(n)},s.Kc=function(n){return this.a._b(n)?(this.a.Ac(n),!0):!1},s.gc=function(){return this.a.gc()},v(mt,"AbstractMap/1",232),m(529,1,Fr,gt),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.a.Ob()},s.Pb=function(){var n;return n=u(this.a.Pb(),45),n.jd()},s.Qb=function(){this.a.Qb()},v(mt,"AbstractMap/1/1",529),m(230,31,Y2,ct),s.$b=function(){this.a.$b()},s.Gc=function(n){return this.a.uc(n)},s.Jc=function(){var n;return n=this.a.vc().Jc(),new Ji(n)},s.gc=function(){return this.a.gc()},v(mt,"AbstractMap/2",230),m(304,1,Fr,Ji),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.a.Ob()},s.Pb=function(){var n;return n=u(this.a.Pb(),45),n.kd()},s.Qb=function(){this.a.Qb()},v(mt,"AbstractMap/2/1",304),m(480,1,{480:1,45:1}),s.Fb=function(n){var t;return X(n,45)?(t=u(n,45),Ku(this.d,t.jd())&&Ku(this.e,t.kd())):!1},s.jd=function(){return this.d},s.kd=function(){return this.e},s.Hb=function(){return _3(this.d)^_3(this.e)},s.ld=function(n){return Dle(this,n)},s.Ib=function(){return this.d+"="+this.e},v(mt,"AbstractMap/AbstractEntry",480),m(390,480,{480:1,390:1,45:1},r$),v(mt,"AbstractMap/SimpleEntry",390),m(2044,1,RZ),s.Fb=function(n){var t;return X(n,45)?(t=u(n,45),Ku(this.jd(),t.jd())&&Ku(this.kd(),t.kd())):!1},s.Hb=function(){return _3(this.jd())^_3(this.kd())},s.Ib=function(){return this.jd()+"="+this.kd()},v(mt,fYe,2044),m(2052,2027,$ge),s.Vc=function(n){return _X(this.Ce(n))},s.tc=function(n){return PPe(this,n)},s._b=function(n){return Ile(this,n)},s.vc=function(){return new Ui(this)},s.Rc=function(){return tIe(this.Ee())},s.Wc=function(n){return _X(this.Fe(n))},s.xc=function(n){var t;return t=n,du(this.De(t))},s.Yc=function(n){return _X(this.Ge(n))},s.ec=function(){return new Lu(this)},s.Tc=function(){return tIe(this.He())},s.Zc=function(n){return _X(this.Ie(n))},v(mt,"AbstractNavigableMap",2052),m(620,Ha,fs,Ui),s.Gc=function(n){return X(n,45)&&PPe(this.b,u(n,45))},s.Jc=function(){return this.b.Be()},s.Kc=function(n){var t;return X(n,45)?(t=u(n,45),this.b.Je(t)):!1},s.gc=function(){return this.b.gc()},v(mt,"AbstractNavigableMap/EntrySet",620),m(1115,Ha,Rge,Lu),s.Lc=function(){return new o$(this)},s.$b=function(){this.a.$b()},s.Gc=function(n){return Ile(this.a,n)},s.Jc=function(){var n;return n=this.a.vc().b.Be(),new _ke(n)},s.Kc=function(n){return Ile(this.a,n)?(this.a.Ac(n),!0):!1},s.gc=function(){return this.a.gc()},v(mt,"AbstractNavigableMap/NavigableKeySet",1115),m(1116,1,Fr,_ke),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return zX(this.a.a)},s.Pb=function(){var n;return n=COe(this.a),n.jd()},s.Qb=function(){NNe(this.a)},v(mt,"AbstractNavigableMap/NavigableKeySet/1",1116),m(2065,31,Y2),s.Ec=function(n){return E4(v8(this,n),B8),!0},s.Fc=function(n){return _n(n),IT(n!=this,"Can't add a queue to itself"),fc(this,n)},s.$b=function(){for(;MY(this)!=null;);},v(mt,"AbstractQueue",2065),m(314,31,{4:1,20:1,31:1,18:1},P3,ILe),s.Ec=function(n){return Lae(this,n),!0},s.$b=function(){zae(this)},s.Gc=function(n){return mze(new hE(this),n)},s.dc=function(){return kj(this)},s.Jc=function(){return new hE(this)},s.Kc=function(n){return j4n(new hE(this),n)},s.gc=function(){return this.c-this.b&this.a.length-1},s.Lc=function(){return new pn(this,272)},s.Oc=function(n){var t;return t=this.c-this.b&this.a.length-1,n.lengtht&&tr(n,t,null),n},s.b=0,s.c=0,v(mt,"ArrayDeque",314),m(448,1,Fr,hE),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return this.a!=this.b},s.Pb=function(){return zB(this)},s.Qb=function(){mBe(this)},s.a=0,s.b=0,s.c=-1,v(mt,"ArrayDeque/IteratorImpl",448),m(13,56,AYe,Te,xo,bs),s._c=function(n,t){Pb(this,n,t)},s.Ec=function(n){return Ce(this,n)},s.ad=function(n,t){return N1e(this,n,t)},s.Fc=function(n){return Er(this,n)},s.$b=function(){Qp(this.c,0)},s.Gc=function(n){return wu(this,n,0)!=-1},s.Ic=function(n){Ao(this,n)},s.Xb=function(n){return Le(this,n)},s.bd=function(n){return wu(this,n,0)},s.dc=function(){return this.c.length==0},s.Jc=function(){return new L(this)},s.ed=function(n){return Ad(this,n)},s.Kc=function(n){return qo(this,n)},s.ae=function(n,t){rLe(this,n,t)},s.fd=function(n,t){return ol(this,n,t)},s.gc=function(){return this.c.length},s.gd=function(n){Tr(this,n)},s.Nc=function(){return nR(this.c)},s.Oc=function(n){return Ra(this,n)};var EBn=v(mt,"ArrayList",13);m(7,1,Fr,L),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return bu(this)},s.Pb=function(){return _(this)},s.Qb=function(){oE(this)},s.a=0,s.b=-1,v(mt,"ArrayList/1",7),m(2074,k.Function,{},mn),s.Ke=function(n,t){return ki(n,t)},m(123,56,MYe,Su),s.Gc=function(n){return pBe(this,n)!=-1},s.Ic=function(n){var t,i,r,c;for(_n(n),i=this.a,r=0,c=i.length;r0)throw $(new Gn(Kge+n+" greater than "+this.e));return this.f.Re()?M_e(this.c,this.b,this.a,n,t):tLe(this.c,n,t)},s.yc=function(n,t){if(!ZQ(this.c,this.f,n,this.b,this.a,this.e,this.d))throw $(new Gn(n+" outside the range "+this.b+" to "+this.e));return Pze(this.c,n,t)},s.Ac=function(n){var t;return t=n,ZQ(this.c,this.f,t,this.b,this.a,this.e,this.d)?C_e(this.c,t):null},s.Je=function(n){return ER(this,n.jd())&&uhe(this.c,n)},s.gc=function(){var n,t,i;if(this.f.Re()?this.a?t=h8(this.c,this.b,!0):t=h8(this.c,this.b,!1):t=mhe(this.c),!(t&&ER(this,t.d)&&t))return 0;for(n=0,i=new zY(this.c,this.f,this.b,this.a,this.e,this.d);zX(i.a);i.b=u(oae(i.a),45))++n;return n},s.$c=function(n,t){if(this.f.Re()&&this.c.a.Le(n,this.b)<0)throw $(new Gn(Kge+n+OYe+this.b));return this.f.Se()?M_e(this.c,n,t,this.e,this.d):iLe(this.c,n,t)},s.a=!1,s.d=!1,v(mt,"TreeMap/SubMap",622),m(309,23,JZ,c$),s.Re=function(){return!1},s.Se=function(){return!1};var lte,fte,ate,hte,dJ=yt(mt,"TreeMap/SubMapType",309,Tt,Wyn,S2n);m(1112,309,JZ,ATe),s.Se=function(){return!0},yt(mt,"TreeMap/SubMapType/1",1112,dJ,null,null),m(1113,309,JZ,RTe),s.Re=function(){return!0},s.Se=function(){return!0},yt(mt,"TreeMap/SubMapType/2",1113,dJ,null,null),m(1114,309,JZ,MTe),s.Re=function(){return!0},yt(mt,"TreeMap/SubMapType/3",1114,dJ,null,null);var gnn;m(141,Ha,{3:1,20:1,31:1,18:1,277:1,22:1,83:1,141:1},wX,lle,vd,c9),s.Lc=function(){return new o$(this)},s.Ec=function(n){return PT(this,n)},s.$b=function(){this.a.$b()},s.Gc=function(n){return this.a._b(n)},s.Jc=function(){return this.a.ec().Jc()},s.Kc=function(n){return DK(this,n)},s.gc=function(){return this.a.gc()};var TBn=v(mt,"TreeSet",141);m(1052,1,{},$ke),s.Te=function(n,t){return Gpn(this.a,n,t)},v(HZ,"BinaryOperator/lambda$0$Type",1052),m(1053,1,{},Rke),s.Te=function(n,t){return qpn(this.a,n,t)},v(HZ,"BinaryOperator/lambda$1$Type",1053),m(935,1,{},Eu),s.Kb=function(n){return n},v(HZ,"Function/lambda$0$Type",935),m(388,1,Ft,u9),s.Mb=function(n){return!this.a.Mb(n)},v(HZ,"Predicate/lambda$2$Type",388),m(567,1,{567:1});var wnn=v(pS,"Handler",567);m(2069,1,lN),s.ve=function(){return"DUMMY"},s.Ib=function(){return this.ve()};var Xme;v(pS,"Level",2069),m(1672,2069,lN,Ws),s.ve=function(){return"INFO"},v(pS,"Level/LevelInfo",1672),m(1824,1,{},txe);var dte;v(pS,"LogManager",1824),m(1866,1,lN,ONe),s.b=null,v(pS,"LogRecord",1866),m(511,1,{511:1},uY),s.e=!1;var pnn=!1,mnn=!1,Ka=!1,vnn=!1,ynn=!1;v(pS,"Logger",511),m(819,567,{567:1},Mr),v(pS,"SimpleConsoleLogHandler",819),m(130,23,{3:1,35:1,23:1,130:1},HX);var Kme,Yo,Vme,Qo=yt(_c,"Collector/Characteristics",130,Tt,P4n,x2n),knn;m(746,1,{},Bfe),v(_c,"CollectorImpl",746),m(1050,1,{},Wr),s.Te=function(n,t){return sjn(u(n,212),u(t,212))},v(_c,"Collectors/10methodref$merge$Type",1050),m(1051,1,{},ur),s.Kb=function(n){return DLe(u(n,212))},v(_c,"Collectors/11methodref$toString$Type",1051),m(152,1,{},mi),s.Wd=function(n,t){u(n,18).Ec(t)},v(_c,"Collectors/20methodref$add$Type",152),m(154,1,{},Fi),s.Ve=function(){return new Te},v(_c,"Collectors/21methodref$ctor$Type",154),m(1049,1,{},fu),s.Wd=function(n,t){I1(u(n,212),u(t,472))},v(_c,"Collectors/9methodref$add$Type",1049),m(1048,1,{},VNe),s.Ve=function(){return new Qb(this.a,this.b,this.c)},v(_c,"Collectors/lambda$15$Type",1048),m(153,1,{},kc),s.Te=function(n,t){return ygn(u(n,18),u(t,18))},v(_c,"Collectors/lambda$45$Type",153),m(538,1,{}),s.Ye=function(){aE(this)},s.d=!1,v(_c,"TerminatableStream",538),m(768,538,Vge,xle),s.Ye=function(){aE(this)},v(_c,"DoubleStreamImpl",768),m(1297,724,bl,YNe),s.Pe=function(n){return LSn(this,u(n,189))},s.a=null,v(_c,"DoubleStreamImpl/2",1297),m(1298,1,mN,Bke),s.Ne=function(n){dwn(this.a,n)},v(_c,"DoubleStreamImpl/2/lambda$0$Type",1298),m(1295,1,mN,zke),s.Ne=function(n){hwn(this.a,n)},v(_c,"DoubleStreamImpl/lambda$0$Type",1295),m(1296,1,mN,Fke),s.Ne=function(n){sJe(this.a,n)},v(_c,"DoubleStreamImpl/lambda$2$Type",1296),m(1351,723,bl,JPe),s.Pe=function(n){return Fyn(this,u(n,202))},s.a=0,s.b=0,s.c=0,v(_c,"IntStream/5",1351),m(793,538,Vge,Ale),s.Ye=function(){aE(this)},s.Ze=function(){return A0(this),this.a},v(_c,"IntStreamImpl",793),m(794,538,Vge,Yoe),s.Ye=function(){aE(this)},s.Ze=function(){return A0(this),Yse(),bnn},v(_c,"IntStreamImpl/Empty",794),m(1651,1,aN,Jke),s.Bd=function(n){eze(this.a,n)},v(_c,"IntStreamImpl/lambda$4$Type",1651);var OBn=Hi(_c,"Stream");m(28,538,{520:1,677:1,832:1},wn),s.Ye=function(){aE(this)};var vy;v(_c,"StreamImpl",28),m(1072,486,bl,kNe),s.zd=function(n){for(;F9n(this);){if(this.a.zd(n))return!0;aE(this.b),this.b=null,this.a=null}return!1},v(_c,"StreamImpl/1",1072),m(1073,1,it,Hke),s.Ad=function(n){T3n(this.a,u(n,832))},v(_c,"StreamImpl/1/lambda$0$Type",1073),m(1074,1,Ft,Gke),s.Mb=function(n){return hr(this.a,n)},v(_c,"StreamImpl/1methodref$add$Type",1074),m(1075,486,bl,e_e),s.zd=function(n){var t;return this.a||(t=new Te,this.b.a.Nb(new qke(t)),jn(),Tr(t,this.c),this.a=new pn(t,16)),JRe(this.a,n)},s.a=null,v(_c,"StreamImpl/5",1075),m(1076,1,it,qke),s.Ad=function(n){Ce(this.a,n)},v(_c,"StreamImpl/5/2methodref$add$Type",1076),m(725,486,bl,whe),s.zd=function(n){for(this.b=!1;!this.b&&this.c.zd(new BMe(this,n)););return this.b},s.b=!1,v(_c,"StreamImpl/FilterSpliterator",725),m(1066,1,it,BMe),s.Ad=function(n){kvn(this.a,this.b,n)},v(_c,"StreamImpl/FilterSpliterator/lambda$0$Type",1066),m(1061,724,bl,WPe),s.Pe=function(n){return g2n(this,u(n,189))},v(_c,"StreamImpl/MapToDoubleSpliterator",1061),m(1065,1,it,zMe),s.Ad=function(n){Pgn(this.a,this.b,n)},v(_c,"StreamImpl/MapToDoubleSpliterator/lambda$0$Type",1065),m(1060,723,bl,ZPe),s.Pe=function(n){return w2n(this,u(n,202))},v(_c,"StreamImpl/MapToIntSpliterator",1060),m(1064,1,it,FMe),s.Ad=function(n){$gn(this.a,this.b,n)},v(_c,"StreamImpl/MapToIntSpliterator/lambda$0$Type",1064),m(722,486,bl,the),s.zd=function(n){return ENe(this,n)},v(_c,"StreamImpl/MapToObjSpliterator",722),m(1063,1,it,JMe),s.Ad=function(n){Rgn(this.a,this.b,n)},v(_c,"StreamImpl/MapToObjSpliterator/lambda$0$Type",1063),m(1062,486,bl,vBe),s.zd=function(n){for(;FX(this.b,0);){if(!this.a.zd(new ef))return!1;this.b=lf(this.b,1)}return this.a.zd(n)},s.b=0,v(_c,"StreamImpl/SkipSpliterator",1062),m(1067,1,it,ef),s.Ad=function(n){},v(_c,"StreamImpl/SkipSpliterator/lambda$0$Type",1067),m(617,1,it,ch),s.Ad=function(n){jP(this,n)},v(_c,"StreamImpl/ValueConsumer",617),m(1068,1,it,Dh),s.Ad=function(n){Ib()},v(_c,"StreamImpl/lambda$0$Type",1068),m(1069,1,it,cd),s.Ad=function(n){Ib()},v(_c,"StreamImpl/lambda$1$Type",1069),m(1070,1,{},Uke),s.Te=function(n,t){return j2n(this.a,n,t)},v(_c,"StreamImpl/lambda$4$Type",1070),m(1071,1,it,HMe),s.Ad=function(n){Qpn(this.b,this.a,n)},v(_c,"StreamImpl/lambda$5$Type",1071),m(1077,1,it,Xke),s.Ad=function(n){B7n(this.a,u(n,375))},v(_c,"TerminatableStream/lambda$0$Type",1077),m(2104,1,{}),m(1976,1,{},jb),v("javaemul.internal","ConsoleLogger",1976);var NBn=0;m(2096,1,{}),m(1800,1,it,Rs),s.Ad=function(n){u(n,321)},v(z8,"BowyerWatsonTriangulation/lambda$0$Type",1800),m(1801,1,it,Kke),s.Ad=function(n){fc(this.a,u(n,321).e)},v(z8,"BowyerWatsonTriangulation/lambda$1$Type",1801),m(1802,1,it,c0),s.Ad=function(n){u(n,177)},v(z8,"BowyerWatsonTriangulation/lambda$2$Type",1802),m(1797,1,Yt,Vke),s.Le=function(n,t){return A6n(this.a,u(n,177),u(t,177))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(z8,"NaiveMinST/lambda$0$Type",1797),m(440,1,{},hj),v(z8,"NodeMicroLayout",440),m(177,1,{177:1},f4),s.Fb=function(n){var t;return X(n,177)?(t=u(n,177),Ku(this.a,t.a)&&Ku(this.b,t.b)||Ku(this.a,t.b)&&Ku(this.b,t.a)):!1},s.Hb=function(){return _3(this.a)+_3(this.b)};var DBn=v(z8,"TEdge",177);m(321,1,{321:1},lge),s.Fb=function(n){var t;return X(n,321)?(t=u(n,321),cB(this,t.a)&&cB(this,t.b)&&cB(this,t.c)):!1},s.Hb=function(){return _3(this.a)+_3(this.b)+_3(this.c)},v(z8,"TTriangle",321),m(225,1,{225:1},_$),v(z8,"Tree",225),m(1183,1,{},G_e),v(IYe,"Scanline",1183);var jnn=Hi(IYe,_Ye);m(1728,1,{},GRe),v(n1,"CGraph",1728),m(320,1,{320:1},$_e),s.b=0,s.c=0,s.d=0,s.g=0,s.i=0,s.k=Dr,v(n1,"CGroup",320),m(814,1,{},doe),v(n1,"CGroup/CGroupBuilder",814),m(60,1,{60:1},iNe),s.Ib=function(){var n;return this.j?Pt(this.j.Kb(this)):(M1(bJ),bJ.o+"@"+(n=vw(this)>>>0,n.toString(16)))},s.f=0,s.i=Dr;var bJ=v(n1,"CNode",60);m(813,1,{},boe),v(n1,"CNode/CNodeBuilder",813);var Enn;m(1551,1,{},u0),s.df=function(n,t){return 0},s.ef=function(n,t){return 0},v(n1,PYe,1551),m(1830,1,{},o0),s.af=function(n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I;for(b=Ki,r=new L(n.a.b);r.ar.d.c||r.d.c==o.d.c&&r.d.b0?n+this.n.d+this.n.a:0},s.gf=function(){var n,t,i,r,c;if(c=0,this.e)this.b?c=this.b.a:this.a[1][1]&&(c=this.a[1][1].gf());else if(this.g)c=ide(this,nW(this,null,!0));else for(t=(ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])),i=0,r=t.length;i0?c+this.n.b+this.n.c:0},s.hf=function(){var n,t,i,r,c;if(this.g)for(n=nW(this,null,!1),i=(ga(),z(B(um,1),ke,237,0,[Ou,No,Nu])),r=0,c=i.length;r0&&(r[0]+=this.d,i-=r[0]),r[2]>0&&(r[2]+=this.d,i-=r[2]),this.c.a=k.Math.max(0,i),this.c.d=t.d+n.d+(this.c.a-i)/2,r[1]=k.Math.max(r[1],i),Wae(this,No,t.d+n.d+r[0]-(r[1]-i)/2,r)},s.b=null,s.d=0,s.e=!1,s.f=!1,s.g=!1;var gte=0,gJ=0;v(lg,"GridContainerCell",1499),m(461,23,{3:1,35:1,23:1,461:1},qX);var nb,Ch,Uf,Onn=yt(lg,"HorizontalLabelAlignment",461,Tt,W4n,A2n),Nnn;m(318,216,{216:1,318:1},T_e,HRe,j_e),s.ff=function(){return oDe(this)},s.gf=function(){return wfe(this)},s.a=0,s.c=!1;var IBn=v(lg,"LabelCell",318);m(253,337,{216:1,337:1,253:1},JE),s.ff=function(){return YE(this)},s.gf=function(){return QE(this)},s.hf=function(){HW(this)},s.jf=function(){GW(this)},s.b=0,s.c=0,s.d=!1,v(lg,"StripContainerCell",253),m(1655,1,Ft,r6),s.Mb=function(n){return Nbn(u(n,216))},v(lg,"StripContainerCell/lambda$0$Type",1655),m(1656,1,{},zg),s.We=function(n){return u(n,216).gf()},v(lg,"StripContainerCell/lambda$1$Type",1656),m(1657,1,Ft,Bg),s.Mb=function(n){return Dbn(u(n,216))},v(lg,"StripContainerCell/lambda$2$Type",1657),m(1658,1,{},c6),s.We=function(n){return u(n,216).ff()},v(lg,"StripContainerCell/lambda$3$Type",1658),m(462,23,{3:1,35:1,23:1,462:1},UX);var Xf,tb,ka,Dnn=yt(lg,"VerticalLabelAlignment",462,Tt,Z4n,M2n),Inn;m(787,1,{},Mge),s.c=0,s.d=0,s.k=0,s.s=0,s.t=0,s.v=!1,s.w=0,s.D=!1,s.F=!1,v(uF,"NodeContext",787),m(1497,1,Yt,f5),s.Le=function(n,t){return mTe(u(n,64),u(t,64))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(uF,"NodeContext/0methodref$comparePortSides$Type",1497),m(1498,1,Yt,a5),s.Le=function(n,t){return gMn(u(n,115),u(t,115))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(uF,"NodeContext/1methodref$comparePortContexts$Type",1498),m(168,23,{3:1,35:1,23:1,168:1},Bl);var _nn,Lnn,Pnn,$nn,Rnn,Bnn,znn,Fnn,Jnn,Hnn,Gnn,qnn,Unn,Xnn,Knn,Vnn,Ynn,Qnn,Wnn,Znn,etn,wte,ntn=yt(uF,"NodeLabelLocation",168,Tt,DQ,C2n),ttn;m(115,1,{115:1},Bqe),s.a=!1,v(uF,"PortContext",115),m(1502,1,it,Fg),s.Ad=function(n){IAe(u(n,318))},v(yN,VYe,1502),m(1503,1,Ft,Eb),s.Mb=function(n){return!!u(n,115).c},v(yN,YYe,1503),m(1504,1,it,Jg),s.Ad=function(n){IAe(u(n,115).c)},v(yN,"LabelPlacer/lambda$2$Type",1504);var Qme;m(1501,1,it,_u),s.Ad=function(n){h2(),fbn(u(n,115))},v(yN,"NodeLabelAndSizeUtilities/lambda$0$Type",1501),m(788,1,it,Kle),s.Ad=function(n){Sgn(this.b,this.c,this.a,u(n,187))},s.a=!1,s.c=!1,v(yN,"NodeLabelCellCreator/lambda$0$Type",788),m(1500,1,it,Wke),s.Ad=function(n){bbn(this.a,u(n,187))},v(yN,"PortContextCreator/lambda$0$Type",1500);var wJ;m(1872,1,{},Hg),v(J8,"GreedyRectangleStripOverlapRemover",1872),m(1873,1,Yt,Gg),s.Le=function(n,t){return tpn(u(n,226),u(t,226))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(J8,"GreedyRectangleStripOverlapRemover/0methodref$compareByYCoordinate$Type",1873),m(1826,1,{},oxe),s.a=5,s.e=0,v(J8,"RectangleStripOverlapRemover",1826),m(1827,1,Yt,u6),s.Le=function(n,t){return ipn(u(n,226),u(t,226))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(J8,"RectangleStripOverlapRemover/0methodref$compareLeftRectangleBorders$Type",1827),m(1829,1,Yt,h5),s.Le=function(n,t){return $vn(u(n,226),u(t,226))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(J8,"RectangleStripOverlapRemover/1methodref$compareRightRectangleBorders$Type",1829),m(409,23,{3:1,35:1,23:1,409:1},u$);var UN,pte,mte,XN,itn=yt(J8,"RectangleStripOverlapRemover/OverlapRemovalDirection",409,Tt,Yyn,T2n),rtn;m(226,1,{226:1},fV),v(J8,"RectangleStripOverlapRemover/RectangleNode",226),m(1828,1,it,Zke),s.Ad=function(n){USn(this.a,u(n,226))},v(J8,"RectangleStripOverlapRemover/lambda$1$Type",1828);var ctn=!1,YS,Wme;m(1798,1,it,Wm),s.Ad=function(n){XKe(u(n,225))},v(ay,"DepthFirstCompaction/0methodref$compactTree$Type",1798),m(810,1,it,Wue),s.Ad=function(n){a5n(this.a,u(n,225))},v(ay,"DepthFirstCompaction/lambda$1$Type",810),m(1799,1,it,_Ne),s.Ad=function(n){IEn(this.a,this.b,this.c,u(n,225))},v(ay,"DepthFirstCompaction/lambda$2$Type",1799);var QS,Zme;m(68,1,{68:1},U_e),v(ay,"Node",68),m(1179,1,{},PTe),v(ay,"ScanlineOverlapCheck",1179),m(1180,1,{683:1},p_e),s._e=function(n){Fpn(this,u(n,442))},v(ay,"ScanlineOverlapCheck/OverlapsScanlineHandler",1180),m(1181,1,Yt,qg),s.Le=function(n,t){return yjn(u(n,68),u(t,68))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(ay,"ScanlineOverlapCheck/OverlapsScanlineHandler/lambda$0$Type",1181),m(442,1,{442:1},lse),s.a=!1,v(ay,"ScanlineOverlapCheck/Timestamp",442),m(1182,1,Yt,o6),s.Le=function(n,t){return Uxn(u(n,442),u(t,442))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(ay,"ScanlineOverlapCheck/lambda$0$Type",1182),m(545,1,{},Ug),v("org.eclipse.elk.alg.common.utils","SVGImage",545),m(748,1,{},Zm),v(KZ,twe,748),m(1164,1,Yt,d5),s.Le=function(n,t){return yTn(u(n,235),u(t,235))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(KZ,ZYe,1164),m(1165,1,it,GMe),s.Ad=function(n){hyn(this.b,this.a,u(n,251))},v(KZ,iwe,1165),m(214,1,Qw),v(gv,"AbstractLayoutProvider",214),m(726,214,Qw,goe),s.kf=function(n,t){TUe(this,n,t)},v(KZ,"ForceLayoutProvider",726);var _Bn=Hi(kN,eQe);m(150,1,{3:1,105:1,150:1},Mt),s.of=function(n,t){return jO(this,n,t)},s.lf=function(){return jDe(this)},s.mf=function(n){return C(this,n)},s.nf=function(n){return bi(this,n)},v(kN,"MapPropertyHolder",150),m(313,150,{3:1,313:1,105:1,150:1}),v(jN,"FParticle",313),m(251,313,{3:1,251:1,313:1,105:1,150:1},oIe),s.Ib=function(){var n;return this.a?(n=wu(this.a.a,this,0),n>=0?"b"+n+"["+tY(this.a)+"]":"b["+tY(this.a)+"]"):"b_"+vw(this)},v(jN,"FBendpoint",251),m(291,150,{3:1,291:1,105:1,150:1},nNe),s.Ib=function(){return tY(this)},v(jN,"FEdge",291),m(235,150,{3:1,235:1,105:1,150:1},YR);var LBn=v(jN,"FGraph",235);m(445,313,{3:1,445:1,313:1,105:1,150:1},lPe),s.Ib=function(){return this.b==null||this.b.length==0?"l["+tY(this.a)+"]":"l_"+this.b},v(jN,"FLabel",445),m(155,313,{3:1,155:1,313:1,105:1,150:1},$Te),s.Ib=function(){return Aae(this)},s.a=0,v(jN,"FNode",155),m(2062,1,{}),s.qf=function(n){ige(this,n)},s.rf=function(){dHe(this)},s.d=0,v(rwe,"AbstractForceModel",2062),m(631,2062,{631:1},tze),s.pf=function(n,t){var i,r,c,o,l;return YKe(this.f,n,t),c=Nr(pc(t.d),n.d),l=k.Math.sqrt(c.a*c.a+c.b*c.b),r=k.Math.max(0,l-fE(n.e)/2-fE(t.e)/2),i=Tqe(this.e,n,t),i>0?o=-Cvn(r,this.c)*i:o=ppn(r,this.b)*u(C(n,(Gf(),ky)),15).a,A1(c,o/l),c},s.qf=function(n){ige(this,n),this.a=u(C(n,(Gf(),mJ)),15).a,this.c=ne(re(C(n,vJ))),this.b=ne(re(C(n,yte)))},s.sf=function(n){return n0&&(o-=Mbn(r,this.a)*i),A1(c,o*this.b/l),c},s.qf=function(n){var t,i,r,c,o,l,f;for(ige(this,n),this.b=ne(re(C(n,(Gf(),kte)))),this.c=this.b/u(C(n,mJ),15).a,r=n.e.c.length,o=0,c=0,f=new L(n.e);f.a0},s.a=0,s.b=0,s.c=0,v(rwe,"FruchtermanReingoldModel",632);var yy=Hi(vu,"ILayoutMetaDataProvider");m(844,1,qa,pC),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,oF),""),"Force Model"),"Determines the model for force calculation."),e3e),(cg(),Ri)),n3e),nn((ph(),Mn))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,cwe),""),"Iterations"),"The number of iterations on the force model."),ve(300)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,uwe),""),"Repulsive Power"),"Determines how many bend points are added to the edge; such bend points are regarded as repelling particles in the force model"),ve(0)),hc),jr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,VZ),""),"FR Temperature"),"The temperature is used as a scaling factor for particle displacements."),Eh),ec),gr),nn(Mn)))),Gi(n,VZ,oF,htn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,YZ),""),"Eades Repulsion"),"Factor for repulsive forces in Eades' model."),5),ec),gr),nn(Mn)))),Gi(n,YZ,oF,ltn),RVe((new eP,n))};var utn,otn,e3e,stn,ltn,ftn,atn,htn;v(yS,"ForceMetaDataProvider",844),m(424,23,{3:1,35:1,23:1,424:1},fse);var vte,pJ,n3e=yt(yS,"ForceModelStrategy",424,Tt,s4n,N2n),dtn;m(984,1,qa,eP),s.tf=function(n){RVe(n)};var btn,gtn,t3e,mJ,i3e,wtn,ptn,mtn,vtn,r3e,ytn,c3e,u3e,ktn,ky,jtn,yte,o3e,Etn,Stn,vJ,kte,xtn,Atn,Mtn,s3e,Ctn;v(yS,"ForceOptions",984),m(985,1,{},Cc),s.uf=function(){var n;return n=new goe,n},s.vf=function(n){},v(yS,"ForceOptions/ForceFactory",985);var KN,WS,jy,yJ;m(845,1,qa,nP),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,swe),""),"Fixed Position"),"Prevent that the node is moved by the layout algorithm."),(Pn(),!1)),(cg(),Sr)),Yi),nn((ph(),fr))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,lwe),""),"Desired Edge Length"),"Either specified for parent nodes or for individual edges, where the latter takes higher precedence."),100),ec),gr),Mi(Mn,z(B(Qa,1),ke,160,0,[Sa]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,fwe),""),"Layout Dimension"),"Dimensions that are permitted to be altered during layout."),l3e),Ri),w3e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,awe),""),"Stress Epsilon"),"Termination criterion for the iterative process."),Eh),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hwe),""),"Iteration Limit"),"Maximum number of performed iterations. Takes higher precedence than 'epsilon'."),ve(oi)),hc),jr),nn(Mn)))),hVe((new xU,n))};var Ttn,Otn,l3e,Ntn,Dtn,Itn;v(yS,"StressMetaDataProvider",845),m(988,1,qa,xU),s.tf=function(n){hVe(n)};var kJ,f3e,a3e,h3e,d3e,b3e,_tn,Ltn,Ptn,$tn,g3e,Rtn;v(yS,"StressOptions",988),m(989,1,{},dc),s.uf=function(){var n;return n=new tNe,n},s.vf=function(n){},v(yS,"StressOptions/StressFactory",989),m(1080,214,Qw,tNe),s.kf=function(n,t){var i,r,c,o,l;for(t.Tg(cQe,1),Re($e(ye(n,(PO(),d3e))))?Re($e(ye(n,g3e)))||HT((i=new hj((_b(),new w0(n))),i)):TUe(new goe,n,t.dh(1)),c=Nze(n),r=EKe(this.a,c),l=r.Jc();l.Ob();)o=u(l.Pb(),235),!(o.e.c.length<=1)&&(HLn(this.b,o),gOn(this.b),Ao(o.d,new s6));c=LVe(r),GVe(c),t.Ug()},v(fF,"StressLayoutProvider",1080),m(1081,1,it,s6),s.Ad=function(n){hge(u(n,445))},v(fF,"StressLayoutProvider/lambda$0$Type",1081),m(986,1,{},nxe),s.c=0,s.e=0,s.g=0,v(fF,"StressMajorization",986),m(384,23,{3:1,35:1,23:1,384:1},XX);var jte,Ete,Ste,w3e=yt(fF,"StressMajorization/Dimension",384,Tt,Y4n,D2n),Btn;m(987,1,Yt,eje),s.Le=function(n,t){return s2n(this.a,u(n,155),u(t,155))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fF,"StressMajorization/lambda$0$Type",987),m(1161,1,{},wLe),v(by,"ElkLayered",1161),m(1162,1,it,nje),s.Ad=function(n){iTn(this.a,u(n,37))},v(by,"ElkLayered/lambda$0$Type",1162),m(1163,1,it,tje),s.Ad=function(n){b2n(this.a,u(n,37))},v(by,"ElkLayered/lambda$1$Type",1163),m(1246,1,{},LTe);var ztn,Ftn,Jtn;v(by,"GraphConfigurator",1246),m(757,1,it,Zue),s.Ad=function(n){OGe(this.a,u(n,9))},v(by,"GraphConfigurator/lambda$0$Type",757),m(758,1,{},Af),s.Kb=function(n){return Vde(),new wn(null,new pn(u(n,25).a,16))},v(by,"GraphConfigurator/lambda$1$Type",758),m(759,1,it,eoe),s.Ad=function(n){OGe(this.a,u(n,9))},v(by,"GraphConfigurator/lambda$2$Type",759),m(1079,214,Qw,ixe),s.kf=function(n,t){var i;i=kLn(new lxe,n),ue(ye(n,(Ne(),wm)))===ue((B1(),Yd))?Ojn(this.a,i,t):aOn(this.a,i,t),t.Zg()||CVe(new vC,i)},v(by,"LayeredLayoutProvider",1079),m(363,23,{3:1,35:1,23:1,363:1},uT);var Kf,r1,eo,no,Pc,p3e=yt(by,"LayeredPhases",363,Tt,U6n,I2n),Htn;m(1683,1,{},jBe),s.i=0;var Gtn;v(CN,"ComponentsToCGraphTransformer",1683);var qtn;m(1684,1,{},Zs),s.wf=function(n,t){return k.Math.min(n.a!=null?ne(n.a):n.c.i,t.a!=null?ne(t.a):t.c.i)},s.xf=function(n,t){return k.Math.min(n.a!=null?ne(n.a):n.c.i,t.a!=null?ne(t.a):t.c.i)},v(CN,"ComponentsToCGraphTransformer/1",1684),m(82,1,{82:1}),s.i=0,s.k=!0,s.o=Dr;var xte=v(SS,"CNode",82);m(460,82,{460:1,82:1},hle,Sde),s.Ib=function(){return""},v(CN,"ComponentsToCGraphTransformer/CRectNode",460),m(1652,1,{},Ta);var Ate,Mte;v(CN,"OneDimensionalComponentsCompaction",1652),m(1653,1,{},Xg),s.Kb=function(n){return C4n(u(n,49))},s.Fb=function(n){return this===n},v(CN,"OneDimensionalComponentsCompaction/lambda$0$Type",1653),m(1654,1,{},b5),s.Kb=function(n){return Ljn(u(n,49))},s.Fb=function(n){return this===n},v(CN,"OneDimensionalComponentsCompaction/lambda$1$Type",1654),m(1686,1,{},vIe),v(SS,"CGraph",1686),m(194,1,{194:1},TQ),s.b=0,s.c=0,s.e=0,s.g=!0,s.i=Dr,v(SS,"CGroup",194),m(1685,1,{},ek),s.wf=function(n,t){return k.Math.max(n.a!=null?ne(n.a):n.c.i,t.a!=null?ne(t.a):t.c.i)},s.xf=function(n,t){return k.Math.max(n.a!=null?ne(n.a):n.c.i,t.a!=null?ne(t.a):t.c.i)},v(SS,PYe,1685),m(1687,1,{},Nqe),s.d=!1;var Utn,Cte=v(SS,BYe,1687);m(1688,1,{},MA),s.Kb=function(n){return Zoe(),Pn(),u(u(n,49).a,82).d.e!=0},s.Fb=function(n){return this===n},v(SS,zYe,1688),m(817,1,{},mfe),s.a=!1,s.b=!1,s.c=!1,s.d=!1,v(SS,FYe,817),m(1868,1,{},IDe),v(aF,JYe,1868);var VN=Hi(fg,_Ye);m(1869,1,{377:1},w_e),s._e=function(n){mDn(this,u(n,465))},v(aF,HYe,1869),m(1870,1,Yt,nk),s.Le=function(n,t){return k5n(u(n,82),u(t,82))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(aF,GYe,1870),m(465,1,{465:1},ase),s.a=!1,v(aF,qYe,465),m(1871,1,Yt,e3),s.Le=function(n,t){return Xxn(u(n,465),u(t,465))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(aF,UYe,1871),m(146,1,{146:1},v9,ofe),s.Fb=function(n){var t;return n==null||PBn!=Us(n)?!1:(t=u(n,146),Ku(this.c,t.c)&&Ku(this.d,t.d))},s.Hb=function(){return RB(z(B(Ar,1),On,1,5,[this.c,this.d]))},s.Ib=function(){return"("+this.c+To+this.d+(this.a?"cx":"")+this.b+")"},s.a=!0,s.c=0,s.d=0;var PBn=v(fg,"Point",146);m(408,23,{3:1,35:1,23:1,408:1},s$);var op,om,xv,sm,Xtn=yt(fg,"Point/Quadrant",408,Tt,Qyn,O2n),Ktn;m(1674,1,{},rxe),s.b=null,s.c=null,s.d=null,s.e=null,s.f=null;var Vtn,Ytn,Qtn,Wtn,Ztn;v(fg,"RectilinearConvexHull",1674),m(569,1,{377:1},cz),s._e=function(n){$9n(this,u(n,146))},s.b=0;var m3e;v(fg,"RectilinearConvexHull/MaximalElementsEventHandler",569),m(1676,1,Yt,l6),s.Le=function(n,t){return v5n(re(n),re(t))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/MaximalElementsEventHandler/lambda$0$Type",1676),m(1675,1,{377:1},CRe),s._e=function(n){_Nn(this,u(n,146))},s.a=0,s.b=null,s.c=null,s.d=null,s.e=null,v(fg,"RectilinearConvexHull/RectangleEventHandler",1675),m(1677,1,Yt,xp),s.Le=function(n,t){return kyn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$0$Type",1677),m(1678,1,Yt,Ap),s.Le=function(n,t){return jyn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$1$Type",1678),m(1679,1,Yt,Mp),s.Le=function(n,t){return Syn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$2$Type",1679),m(1680,1,Yt,Cp),s.Le=function(n,t){return Eyn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$3$Type",1680),m(1681,1,Yt,xl),s.Le=function(n,t){return OMn(u(n,146),u(t,146))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(fg,"RectilinearConvexHull/lambda$4$Type",1681),m(1682,1,{},q_e),v(fg,"Scanline",1682),m(2066,1,{}),v(Ua,"AbstractGraphPlacer",2066),m(336,1,{336:1},AOe),s.Df=function(n){return this.Ef(n)?(gn(this.b,u(C(n,(me(),K1)),22),n),!0):!1},s.Ef=function(n){var t,i,r,c;for(t=u(C(n,(me(),K1)),22),c=u(pi(xi,t),22),r=c.Jc();r.Ob();)if(i=u(r.Pb(),22),!u(pi(this.b,i),16).dc())return!1;return!0};var xi;v(Ua,"ComponentGroup",336),m(766,2066,{},woe),s.Ff=function(n){var t,i;for(i=new L(this.a);i.ai&&(p=0,y+=f+r,f=0),h=o.c,M8(o,p+h.a,y+h.b),la(h),c=k.Math.max(c,p+b.a),f=k.Math.max(f,b.b),p+=b.a+r;t.f.a=c,t.f.b=y+f},s.Hf=function(n,t){var i,r,c,o,l;if(ue(C(t,(Ne(),dx)))===ue((X4(),ZS))){for(r=n.Jc();r.Ob();){for(i=u(r.Pb(),37),l=0,o=new L(i.a);o.ai&&!u(C(o,(me(),K1)),22).Gc((De(),Xn))||h&&u(C(h,(me(),K1)),22).Gc((De(),et))||u(C(o,(me(),K1)),22).Gc((De(),Kn)))&&(S=y,A+=f+r,f=0),b=o.c,u(C(o,(me(),K1)),22).Gc((De(),Xn))&&(S=c+r),M8(o,S+b.a,A+b.b),c=k.Math.max(c,S+p.a),u(C(o,K1),22).Gc(bt)&&(y=k.Math.max(y,S+p.a+r)),la(b),f=k.Math.max(f,p.b),S+=p.a+r,h=o;t.f.a=c,t.f.b=A+f},s.Hf=function(n,t){},v(Ua,"ModelOrderRowGraphPlacer",1277),m(1275,1,Yt,CA),s.Le=function(n,t){return $7n(u(n,37),u(t,37))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ua,"SimpleRowGraphPlacer/1",1275);var nin;m(1245,1,jh,f6),s.Lb=function(n){var t;return t=u(C(u(n,250).b,(Ne(),Wc)),78),!!t&&t.b!=0},s.Fb=function(n){return this===n},s.Mb=function(n){var t;return t=u(C(u(n,250).b,(Ne(),Wc)),78),!!t&&t.b!=0},v(hF,"CompoundGraphPostprocessor/1",1245),m(1244,1,Ai,fxe),s.If=function(n,t){VJe(this,u(n,37),t)},v(hF,"CompoundGraphPreprocessor",1244),m(444,1,{444:1},PFe),s.c=!1,v(hF,"CompoundGraphPreprocessor/ExternalPort",444),m(250,1,{250:1},Y$),s.Ib=function(){return $K(this.c)+":"+xqe(this.b)},v(hF,"CrossHierarchyEdge",250),m(764,1,Yt,noe),s.Le=function(n,t){return vxn(this,u(n,250),u(t,250))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(hF,"CrossHierarchyEdgeComparator",764),m(246,150,{3:1,246:1,105:1,150:1}),s.p=0,v(Zu,"LGraphElement",246),m(17,246,{3:1,17:1,246:1,105:1,150:1},Mw),s.Ib=function(){return xqe(this)};var b7=v(Zu,"LEdge",17);m(37,246,{3:1,20:1,37:1,246:1,105:1,150:1},$he),s.Ic=function(n){rc(this,n)},s.Jc=function(){return new L(this.b)},s.Ib=function(){return this.b.c.length==0?"G-unlayered"+Fa(this.a):this.a.c.length==0?"G-layered"+Fa(this.b):"G[layerless"+Fa(this.a)+", layers"+Fa(this.b)+"]"};var tin=v(Zu,"LGraph",37),iin;m(655,1,{}),s.Jf=function(){return this.e.n},s.mf=function(n){return C(this.e,n)},s.Kf=function(){return this.e.o},s.Lf=function(){return this.e.p},s.nf=function(n){return bi(this.e,n)},s.Mf=function(n){this.e.n.a=n.a,this.e.n.b=n.b},s.Nf=function(n){this.e.o.a=n.a,this.e.o.b=n.b},s.Of=function(n){this.e.p=n},v(Zu,"LGraphAdapters/AbstractLShapeAdapter",655),m(464,1,{837:1},dj),s.Pf=function(){var n,t;if(!this.b)for(this.b=Fh(this.a.b.c.length),t=new L(this.a.b);t.a0&&hFe((Yn(t-1,n.length),n.charCodeAt(t-1)),aQe);)--t;if(o> ",n),bz(i)),Kt(uo((n.a+="[",n),i.i),"]")),n.a},s.c=!0,s.d=!1;var E3e,S3e,x3e,A3e,M3e,C3e,cin=v(Zu,"LPort",12);m(399,1,Wh,o9),s.Ic=function(n){rc(this,n)},s.Jc=function(){var n;return n=new L(this.a.e),new ije(n)},v(Zu,"LPort/1",399),m(1273,1,Fr,ije),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(_(this.a),17).c},s.Ob=function(){return bu(this.a)},s.Qb=function(){oE(this.a)},v(Zu,"LPort/1/1",1273),m(365,1,Wh,W5),s.Ic=function(n){rc(this,n)},s.Jc=function(){var n;return n=new L(this.a.g),new toe(n)},v(Zu,"LPort/2",365),m(763,1,Fr,toe),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(_(this.a),17).d},s.Ob=function(){return bu(this.a)},s.Qb=function(){oE(this.a)},v(Zu,"LPort/2/1",763),m(1266,1,Wh,UMe),s.Ic=function(n){rc(this,n)},s.Jc=function(){return new La(this)},v(Zu,"LPort/CombineIter",1266),m(207,1,Fr,La),s.Nb=function(n){Zr(this,n)},s.Qb=function(){xAe()},s.Ob=function(){return Wj(this)},s.Pb=function(){return bu(this.a)?_(this.a):_(this.b)},v(Zu,"LPort/CombineIter/1",207),m(1267,1,jh,w5),s.Lb=function(n){return GDe(n)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).g.c.length!=0},v(Zu,"LPort/lambda$0$Type",1267),m(1268,1,jh,s0),s.Lb=function(n){return qDe(n)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).e.c.length!=0},v(Zu,"LPort/lambda$1$Type",1268),m(1269,1,jh,Ih),s.Lb=function(n){return ss(),u(n,12).j==(De(),Xn)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).j==(De(),Xn)},v(Zu,"LPort/lambda$2$Type",1269),m(1270,1,jh,ck),s.Lb=function(n){return ss(),u(n,12).j==(De(),et)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).j==(De(),et)},v(Zu,"LPort/lambda$3$Type",1270),m(1271,1,jh,TA),s.Lb=function(n){return ss(),u(n,12).j==(De(),bt)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).j==(De(),bt)},v(Zu,"LPort/lambda$4$Type",1271),m(1272,1,jh,p5),s.Lb=function(n){return ss(),u(n,12).j==(De(),Kn)},s.Fb=function(n){return this===n},s.Mb=function(n){return ss(),u(n,12).j==(De(),Kn)},v(Zu,"LPort/lambda$5$Type",1272),m(25,246,{3:1,20:1,246:1,25:1,105:1,150:1},Xu),s.Ic=function(n){rc(this,n)},s.Jc=function(){return new L(this.a)},s.Ib=function(){return"L_"+wu(this.b.b,this,0)+Fa(this.a)},v(Zu,"Layer",25),m(1659,1,{},_$e),s.b=0,v(Zu,"Tarjan",1659),m(1282,1,{},lxe),v(zd,gQe,1282),m(1286,1,{},uk),s.Kb=function(n){return iu(u(n,84))},v(zd,"ElkGraphImporter/0methodref$connectableShapeToNode$Type",1286),m(1289,1,{},t3),s.Kb=function(n){return iu(u(n,84))},v(zd,"ElkGraphImporter/1methodref$connectableShapeToNode$Type",1289),m(1283,1,it,rje),s.Ad=function(n){Jqe(this.a,u(n,125))},v(zd,iwe,1283),m(1284,1,it,cje),s.Ad=function(n){Jqe(this.a,u(n,125))},v(zd,wQe,1284),m(1285,1,{},_h),s.Kb=function(n){return new wn(null,new pn(tae(u(n,85)),16))},v(zd,pQe,1285),m(1287,1,Ft,uje),s.Mb=function(n){return fwn(this.a,u(n,26))},v(zd,mQe,1287),m(1288,1,{},i3),s.Kb=function(n){return new wn(null,new pn(w5n(u(n,85)),16))},v(zd,"ElkGraphImporter/lambda$5$Type",1288),m(1290,1,Ft,oje),s.Mb=function(n){return awn(this.a,u(n,26))},v(zd,"ElkGraphImporter/lambda$7$Type",1290),m(1291,1,Ft,ok),s.Mb=function(n){return N5n(u(n,85))},v(zd,"ElkGraphImporter/lambda$8$Type",1291),m(1261,1,{},vC);var uin;v(zd,"ElkGraphLayoutTransferrer",1261),m(1262,1,Ft,sje),s.Mb=function(n){return t2n(this.a,u(n,17))},v(zd,"ElkGraphLayoutTransferrer/lambda$0$Type",1262),m(1263,1,it,lje),s.Ad=function(n){iT(),Ce(this.a,u(n,17))},v(zd,"ElkGraphLayoutTransferrer/lambda$1$Type",1263),m(1264,1,Ft,fje),s.Mb=function(n){return Jpn(this.a,u(n,17))},v(zd,"ElkGraphLayoutTransferrer/lambda$2$Type",1264),m(1265,1,it,aje),s.Ad=function(n){iT(),Ce(this.a,u(n,17))},v(zd,"ElkGraphLayoutTransferrer/lambda$3$Type",1265),m(806,1,{},Lle),v(Wn,"BiLinkedHashMultiMap",806),m(1511,1,Ai,a6),s.If=function(n,t){t7n(u(n,37),t)},v(Wn,"CommentNodeMarginCalculator",1511),m(1512,1,{},Vg),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"CommentNodeMarginCalculator/lambda$0$Type",1512),m(1513,1,it,vI),s.Ad=function(n){mLn(u(n,9))},v(Wn,"CommentNodeMarginCalculator/lambda$1$Type",1513),m(1514,1,Ai,OA),s.If=function(n,t){xDn(u(n,37),t)},v(Wn,"CommentPostprocessor",1514),m(1515,1,Ai,yI),s.If=function(n,t){q$n(u(n,37),t)},v(Wn,"CommentPreprocessor",1515),m(1516,1,Ai,m5),s.If=function(n,t){RNn(u(n,37),t)},v(Wn,"ConstraintsPostprocessor",1516),m(1517,1,Ai,uq),s.If=function(n,t){M7n(u(n,37),t)},v(Wn,"EdgeAndLayerConstraintEdgeReverser",1517),m(1518,1,Ai,kI),s.If=function(n,t){tEn(u(n,37),t)},v(Wn,"EndLabelPostprocessor",1518),m(1519,1,{},jI),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"EndLabelPostprocessor/lambda$0$Type",1519),m(1520,1,Ft,NA),s.Mb=function(n){return z6n(u(n,9))},v(Wn,"EndLabelPostprocessor/lambda$1$Type",1520),m(1521,1,it,oq),s.Ad=function(n){Kxn(u(n,9))},v(Wn,"EndLabelPostprocessor/lambda$2$Type",1521),m(1522,1,Ai,sq),s.If=function(n,t){OCn(u(n,37),t)},v(Wn,"EndLabelPreprocessor",1522),m(1523,1,{},sk),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"EndLabelPreprocessor/lambda$0$Type",1523),m(1524,1,it,LNe),s.Ad=function(n){xgn(this.a,this.b,this.c,u(n,9))},s.a=0,s.b=0,s.c=!1,v(Wn,"EndLabelPreprocessor/lambda$1$Type",1524),m(1525,1,Ft,Yg),s.Mb=function(n){return ue(C(u(n,70),(Ne(),Oh)))===ue(($a(),J7))},v(Wn,"EndLabelPreprocessor/lambda$2$Type",1525),m(1526,1,it,hje),s.Ad=function(n){Vt(this.a,u(n,70))},v(Wn,"EndLabelPreprocessor/lambda$3$Type",1526),m(1527,1,Ft,DA),s.Mb=function(n){return ue(C(u(n,70),(Ne(),Oh)))===ue(($a(),_m))},v(Wn,"EndLabelPreprocessor/lambda$4$Type",1527),m(1528,1,it,dje),s.Ad=function(n){Vt(this.a,u(n,70))},v(Wn,"EndLabelPreprocessor/lambda$5$Type",1528),m(1576,1,Ai,AU),s.If=function(n,t){pjn(u(n,37),t)};var oin;v(Wn,"EndLabelSorter",1576),m(1577,1,Yt,IA),s.Le=function(n,t){return REn(u(n,455),u(t,455))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"EndLabelSorter/1",1577),m(455,1,{455:1},o_e),v(Wn,"EndLabelSorter/LabelGroup",455),m(1578,1,{},v5),s.Kb=function(n){return tT(),new wn(null,new pn(u(n,25).a,16))},v(Wn,"EndLabelSorter/lambda$0$Type",1578),m(1579,1,Ft,y5),s.Mb=function(n){return tT(),u(n,9).k==(zn(),Qi)},v(Wn,"EndLabelSorter/lambda$1$Type",1579),m(1580,1,it,EI),s.Ad=function(n){GMn(u(n,9))},v(Wn,"EndLabelSorter/lambda$2$Type",1580),m(1581,1,Ft,_A),s.Mb=function(n){return tT(),ue(C(u(n,70),(Ne(),Oh)))===ue(($a(),_m))},v(Wn,"EndLabelSorter/lambda$3$Type",1581),m(1582,1,Ft,SI),s.Mb=function(n){return tT(),ue(C(u(n,70),(Ne(),Oh)))===ue(($a(),J7))},v(Wn,"EndLabelSorter/lambda$4$Type",1582),m(1529,1,Ai,h6),s.If=function(n,t){LLn(this,u(n,37))},s.b=0,s.c=0,v(Wn,"FinalSplineBendpointsCalculator",1529),m(1530,1,{},Qg),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"FinalSplineBendpointsCalculator/lambda$0$Type",1530),m(1531,1,{},LA),s.Kb=function(n){return new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(Wn,"FinalSplineBendpointsCalculator/lambda$1$Type",1531),m(1532,1,Ft,d6),s.Mb=function(n){return!cc(u(n,17))},v(Wn,"FinalSplineBendpointsCalculator/lambda$2$Type",1532),m(1533,1,Ft,Op),s.Mb=function(n){return bi(u(n,17),(me(),vg))},v(Wn,"FinalSplineBendpointsCalculator/lambda$3$Type",1533),m(1534,1,it,bje),s.Ad=function(n){HIn(this.a,u(n,132))},v(Wn,"FinalSplineBendpointsCalculator/lambda$4$Type",1534),m(1535,1,it,PA),s.Ad=function(n){HO(u(n,17).a)},v(Wn,"FinalSplineBendpointsCalculator/lambda$5$Type",1535),m(790,1,Ai,ioe),s.If=function(n,t){TPn(this,u(n,37),t)},v(Wn,"GraphTransformer",790),m(502,23,{3:1,35:1,23:1,502:1},hse);var Dte,QN,sin=yt(Wn,"GraphTransformer/Mode",502,Tt,l4n,P2n),lin;m(1536,1,Ai,lk),s.If=function(n,t){QOn(u(n,37),t)},v(Wn,"HierarchicalNodeResizingProcessor",1536),m(1537,1,Ai,xI),s.If=function(n,t){H8n(u(n,37),t)},v(Wn,"HierarchicalPortConstraintProcessor",1537),m(1538,1,Yt,fk),s.Le=function(n,t){return rSn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"HierarchicalPortConstraintProcessor/NodeComparator",1538),m(1539,1,Ai,ak),s.If=function(n,t){P_n(u(n,37),t)},v(Wn,"HierarchicalPortDummySizeProcessor",1539),m(1540,1,Ai,AI),s.If=function(n,t){VDn(this,u(n,37),t)},s.a=0,v(Wn,"HierarchicalPortOrthogonalEdgeRouter",1540),m(1541,1,Yt,Lh),s.Le=function(n,t){return rpn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"HierarchicalPortOrthogonalEdgeRouter/1",1541),m(1542,1,Yt,r3),s.Le=function(n,t){return J9n(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"HierarchicalPortOrthogonalEdgeRouter/2",1542),m(1543,1,Ai,hk),s.If=function(n,t){MMn(u(n,37),t)},v(Wn,"HierarchicalPortPositionProcessor",1543),m(1544,1,Ai,mC),s.If=function(n,t){CRn(this,u(n,37))},s.a=0,s.c=0;var jJ,EJ;v(Wn,"HighDegreeNodeLayeringProcessor",1544),m(566,1,{566:1},b6),s.b=-1,s.d=-1,v(Wn,"HighDegreeNodeLayeringProcessor/HighDegreeNodeInformation",566),m(1545,1,{},lq),s.Kb=function(n){return OT(),rr(u(n,9))},s.Fb=function(n){return this===n},v(Wn,"HighDegreeNodeLayeringProcessor/lambda$0$Type",1545),m(1546,1,{},$A),s.Kb=function(n){return OT(),Ni(u(n,9))},s.Fb=function(n){return this===n},v(Wn,"HighDegreeNodeLayeringProcessor/lambda$1$Type",1546),m(1552,1,Ai,RA),s.If=function(n,t){M_n(this,u(n,37),t)},v(Wn,"HyperedgeDummyMerger",1552),m(791,1,{},Wle),s.a=!1,s.b=!1,s.c=!1,v(Wn,"HyperedgeDummyMerger/MergeState",791),m(1553,1,{},g6),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"HyperedgeDummyMerger/lambda$0$Type",1553),m(1554,1,{},dk),s.Kb=function(n){return new wn(null,new pn(u(n,9).j,16))},v(Wn,"HyperedgeDummyMerger/lambda$1$Type",1554),m(1555,1,it,MI),s.Ad=function(n){u(n,12).p=-1},v(Wn,"HyperedgeDummyMerger/lambda$2$Type",1555),m(1556,1,Ai,aq),s.If=function(n,t){A_n(u(n,37),t)},v(Wn,"HypernodesProcessor",1556),m(1557,1,Ai,hq),s.If=function(n,t){L_n(u(n,37),t)},v(Wn,"InLayerConstraintProcessor",1557),m(1558,1,Ai,BA),s.If=function(n,t){p7n(u(n,37),t)},v(Wn,"InnermostNodeMarginCalculator",1558),m(1559,1,Ai,dq),s.If=function(n,t){F$n(this,u(n,37))},s.a=Dr,s.b=Dr,s.c=Ki,s.d=Ki;var $Bn=v(Wn,"InteractiveExternalPortPositioner",1559);m(1560,1,{},bq),s.Kb=function(n){return u(n,17).d.i},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$0$Type",1560),m(1561,1,{},gje),s.Kb=function(n){return cpn(this.a,re(n))},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$1$Type",1561),m(1562,1,{},gq),s.Kb=function(n){return u(n,17).c.i},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$2$Type",1562),m(1563,1,{},wje),s.Kb=function(n){return upn(this.a,re(n))},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$3$Type",1563),m(1564,1,{},pje),s.Kb=function(n){return e2n(this.a,re(n))},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$4$Type",1564),m(1565,1,{},mje),s.Kb=function(n){return n2n(this.a,re(n))},s.Fb=function(n){return this===n},v(Wn,"InteractiveExternalPortPositioner/lambda$5$Type",1565),m(79,23,{3:1,35:1,23:1,79:1,196:1},br),s.bg=function(){switch(this.g){case 15:return new ew;case 22:return new Pp;case 48:return new uM;case 29:case 36:return new Eq;case 33:return new a6;case 43:return new OA;case 1:return new yI;case 42:return new m5;case 57:return new ioe((K9(),QN));case 0:return new ioe((K9(),Dte));case 2:return new uq;case 55:return new kI;case 34:return new sq;case 52:return new h6;case 56:return new lk;case 13:return new xI;case 39:return new ak;case 45:return new AI;case 41:return new hk;case 9:return new mC;case 50:return new vOe;case 38:return new RA;case 44:return new aq;case 28:return new hq;case 31:return new BA;case 3:return new dq;case 18:return new fq;case 30:return new wq;case 5:return new MU;case 51:return new vq;case 35:return new Y6;case 37:return new Sq;case 53:return new AU;case 11:return new TI;case 7:return new CU;case 40:return new xq;case 46:return new Aq;case 16:return new Mq;case 10:return new kCe;case 49:return new Nq;case 21:return new Dq;case 23:return new zP((eg(),Mx));case 8:return new FA;case 12:return new _q;case 4:return new OI;case 19:return new tP;case 17:return new PI;case 54:return new p6;case 6:return new Fq;case 25:return new hxe;case 26:return new rM;case 47:return new HA;case 32:return new uNe;case 14:return new GI;case 27:return new Vq;case 20:return new y6;case 24:return new zP((eg(),TH));default:throw $(new Gn(nee+(this.f!=null?this.f:""+this.g)))}};var T3e,O3e,N3e,D3e,I3e,_3e,L3e,P3e,$3e,R3e,B3e,Av,SJ,xJ,z3e,F3e,J3e,H3e,G3e,q3e,U3e,nx,X3e,K3e,V3e,Y3e,Q3e,Ite,AJ,MJ,W3e,CJ,TJ,OJ,g7,lm,fm,Z3e,NJ,DJ,eve,IJ,_J,nve,tve,ive,rve,LJ,_te,Ey,PJ,$J,RJ,BJ,cve,uve,ove,sve,RBn=yt(Wn,tee,79,Tt,FUe,$2n),fin;m(1566,1,Ai,fq),s.If=function(n,t){R$n(u(n,37),t)},v(Wn,"InvertedPortProcessor",1566),m(1567,1,Ai,wq),s.If=function(n,t){$In(u(n,37),t)},v(Wn,"LabelAndNodeSizeProcessor",1567),m(1568,1,Ft,pq),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"LabelAndNodeSizeProcessor/lambda$0$Type",1568),m(1569,1,Ft,CI),s.Mb=function(n){return u(n,9).k==(zn(),wr)},v(Wn,"LabelAndNodeSizeProcessor/lambda$1$Type",1569),m(1570,1,it,RNe),s.Ad=function(n){Agn(this.b,this.a,this.c,u(n,9))},s.a=!1,s.c=!1,v(Wn,"LabelAndNodeSizeProcessor/lambda$2$Type",1570),m(1571,1,Ai,MU),s.If=function(n,t){w$n(u(n,37),t)};var ain;v(Wn,"LabelDummyInserter",1571),m(1572,1,jh,mq),s.Lb=function(n){return ue(C(u(n,70),(Ne(),Oh)))===ue(($a(),F7))},s.Fb=function(n){return this===n},s.Mb=function(n){return ue(C(u(n,70),(Ne(),Oh)))===ue(($a(),F7))},v(Wn,"LabelDummyInserter/1",1572),m(1573,1,Ai,vq),s.If=function(n,t){i$n(u(n,37),t)},v(Wn,"LabelDummyRemover",1573),m(1574,1,Ft,yq),s.Mb=function(n){return Re($e(C(u(n,70),(Ne(),Rv))))},v(Wn,"LabelDummyRemover/lambda$0$Type",1574),m(1332,1,Ai,Y6),s.If=function(n,t){QPn(this,u(n,37),t)},s.a=null;var Lte;v(Wn,"LabelDummySwitcher",1332),m(294,1,{294:1},$Xe),s.c=0,s.d=null,s.f=0,v(Wn,"LabelDummySwitcher/LabelDummyInfo",294),m(1333,1,{},kq),s.Kb=function(n){return z4(),new wn(null,new pn(u(n,25).a,16))},v(Wn,"LabelDummySwitcher/lambda$0$Type",1333),m(1334,1,Ft,zA),s.Mb=function(n){return z4(),u(n,9).k==(zn(),Uu)},v(Wn,"LabelDummySwitcher/lambda$1$Type",1334),m(1335,1,{},vje),s.Kb=function(n){return Hpn(this.a,u(n,9))},v(Wn,"LabelDummySwitcher/lambda$2$Type",1335),m(1336,1,it,yje),s.Ad=function(n){qvn(this.a,u(n,294))},v(Wn,"LabelDummySwitcher/lambda$3$Type",1336),m(1337,1,Yt,jq),s.Le=function(n,t){return yvn(u(n,294),u(t,294))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"LabelDummySwitcher/lambda$4$Type",1337),m(789,1,Ai,Eq),s.If=function(n,t){j9n(u(n,37),t)},v(Wn,"LabelManagementProcessor",789),m(1575,1,Ai,Sq),s.If=function(n,t){bDn(u(n,37),t)},v(Wn,"LabelSideSelector",1575),m(1583,1,Ai,TI),s.If=function(n,t){Z_n(u(n,37),t)},v(Wn,"LayerConstraintPostprocessor",1583),m(1584,1,Ai,CU),s.If=function(n,t){QTn(u(n,37),t)};var lve;v(Wn,"LayerConstraintPreprocessor",1584),m(367,23,{3:1,35:1,23:1,367:1},f$);var WN,zJ,FJ,Pte,hin=yt(Wn,"LayerConstraintPreprocessor/HiddenNodeConnections",367,Tt,e6n,vmn),din;m(1585,1,Ai,xq),s.If=function(n,t){pPn(u(n,37),t)},v(Wn,"LayerSizeAndGraphHeightCalculator",1585),m(1586,1,Ai,Aq),s.If=function(n,t){WOn(u(n,37),t)},v(Wn,"LongEdgeJoiner",1586),m(1587,1,Ai,Mq),s.If=function(n,t){XLn(u(n,37),t)},v(Wn,"LongEdgeSplitter",1587),m(1588,1,Ai,kCe),s.If=function(n,t){O$n(this,u(n,37),t)},s.e=0,s.f=0,s.j=0,s.k=0,s.n=0,s.o=0;var bin,gin;v(Wn,"NodePromotion",1588),m(1589,1,Yt,Cq),s.Le=function(n,t){return kkn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"NodePromotion/1",1589),m(1590,1,Yt,Tq),s.Le=function(n,t){return jkn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"NodePromotion/2",1590),m(1591,1,{},Oq),s.Kb=function(n){return u(n,49),K$(),Pn(),!0},s.Fb=function(n){return this===n},v(Wn,"NodePromotion/lambda$0$Type",1591),m(1592,1,{},kje),s.Kb=function(n){return M4n(this.a,u(n,49))},s.Fb=function(n){return this===n},s.a=0,v(Wn,"NodePromotion/lambda$1$Type",1592),m(1593,1,{},jje),s.Kb=function(n){return A4n(this.a,u(n,49))},s.Fb=function(n){return this===n},s.a=0,v(Wn,"NodePromotion/lambda$2$Type",1593),m(1594,1,Ai,Nq),s.If=function(n,t){vRn(u(n,37),t)},v(Wn,"NorthSouthPortPostprocessor",1594),m(1595,1,Ai,Dq),s.If=function(n,t){xRn(u(n,37),t)},v(Wn,"NorthSouthPortPreprocessor",1595),m(1596,1,Yt,Iq),s.Le=function(n,t){return z7n(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"NorthSouthPortPreprocessor/lambda$0$Type",1596),m(1597,1,Ai,FA),s.If=function(n,t){w_n(u(n,37),t)},v(Wn,"PartitionMidprocessor",1597),m(1598,1,Ft,w6),s.Mb=function(n){return bi(u(n,9),(Ne(),mm))},v(Wn,"PartitionMidprocessor/lambda$0$Type",1598),m(1599,1,it,Eje),s.Ad=function(n){O5n(this.a,u(n,9))},v(Wn,"PartitionMidprocessor/lambda$1$Type",1599),m(1600,1,Ai,_q),s.If=function(n,t){vNn(u(n,37),t)},v(Wn,"PartitionPostprocessor",1600),m(1601,1,Ai,OI),s.If=function(n,t){jIn(u(n,37),t)},v(Wn,"PartitionPreprocessor",1601),m(1602,1,Ft,NI),s.Mb=function(n){return bi(u(n,9),(Ne(),mm))},v(Wn,"PartitionPreprocessor/lambda$0$Type",1602),m(1603,1,Ft,DI),s.Mb=function(n){return bi(u(n,9),(Ne(),mm))},v(Wn,"PartitionPreprocessor/lambda$1$Type",1603),m(1604,1,{},II),s.Kb=function(n){return new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(Wn,"PartitionPreprocessor/lambda$2$Type",1604),m(1605,1,Ft,Sje),s.Mb=function(n){return lgn(this.a,u(n,17))},v(Wn,"PartitionPreprocessor/lambda$3$Type",1605),m(1606,1,it,_I),s.Ad=function(n){Z7n(u(n,17))},v(Wn,"PartitionPreprocessor/lambda$4$Type",1606),m(1607,1,Ft,xje),s.Mb=function(n){return Uvn(this.a,u(n,9))},s.a=0,v(Wn,"PartitionPreprocessor/lambda$5$Type",1607),m(1608,1,Ai,tP),s.If=function(n,t){YIn(u(n,37),t)};var fve,win,pin,min,ave,hve;v(Wn,"PortListSorter",1608),m(1609,1,{},k5),s.Kb=function(n){return n8(),u(n,12).e},v(Wn,"PortListSorter/lambda$0$Type",1609),m(1610,1,{},Lq),s.Kb=function(n){return n8(),u(n,12).g},v(Wn,"PortListSorter/lambda$1$Type",1610),m(1611,1,Yt,Pq),s.Le=function(n,t){return aPe(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"PortListSorter/lambda$2$Type",1611),m(1612,1,Yt,$q),s.Le=function(n,t){return hxn(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"PortListSorter/lambda$3$Type",1612),m(1613,1,Yt,LI),s.Le=function(n,t){return lKe(u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"PortListSorter/lambda$4$Type",1613),m(1614,1,Ai,PI),s.If=function(n,t){rOn(u(n,37),t)},v(Wn,"PortSideProcessor",1614),m(1615,1,Ai,p6),s.If=function(n,t){sIn(u(n,37),t)},v(Wn,"ReversedEdgeRestorer",1615),m(1620,1,Ai,hxe),s.If=function(n,t){VSn(this,u(n,37),t)},v(Wn,"SelfLoopPortRestorer",1620),m(1621,1,{},m6),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"SelfLoopPortRestorer/lambda$0$Type",1621),m(1622,1,Ft,Rq),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"SelfLoopPortRestorer/lambda$1$Type",1622),m(1623,1,Ft,bk),s.Mb=function(n){return bi(u(n,9),(me(),dp))},v(Wn,"SelfLoopPortRestorer/lambda$2$Type",1623),m(1624,1,{},$I),s.Kb=function(n){return u(C(u(n,9),(me(),dp)),338)},v(Wn,"SelfLoopPortRestorer/lambda$3$Type",1624),m(1625,1,it,Aje),s.Ad=function(n){rCn(this.a,u(n,338))},v(Wn,"SelfLoopPortRestorer/lambda$4$Type",1625),m(792,1,it,JA),s.Ad=function(n){gCn(u(n,107))},v(Wn,"SelfLoopPortRestorer/lambda$5$Type",792),m(1627,1,Ai,HA),s.If=function(n,t){oSn(u(n,37),t)},v(Wn,"SelfLoopPostProcessor",1627),m(1628,1,{},GA),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"SelfLoopPostProcessor/lambda$0$Type",1628),m(1629,1,Ft,RI),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"SelfLoopPostProcessor/lambda$1$Type",1629),m(1630,1,Ft,BI),s.Mb=function(n){return bi(u(n,9),(me(),dp))},v(Wn,"SelfLoopPostProcessor/lambda$2$Type",1630),m(1631,1,it,zI),s.Ad=function(n){aAn(u(n,9))},v(Wn,"SelfLoopPostProcessor/lambda$3$Type",1631),m(1632,1,{},Bq),s.Kb=function(n){return new wn(null,new pn(u(n,107).f,1))},v(Wn,"SelfLoopPostProcessor/lambda$4$Type",1632),m(1633,1,it,Mje),s.Ad=function(n){Kyn(this.a,u(n,341))},v(Wn,"SelfLoopPostProcessor/lambda$5$Type",1633),m(1634,1,Ft,zq),s.Mb=function(n){return!!u(n,107).i},v(Wn,"SelfLoopPostProcessor/lambda$6$Type",1634),m(1635,1,it,Cje),s.Ad=function(n){Abn(this.a,u(n,107))},v(Wn,"SelfLoopPostProcessor/lambda$7$Type",1635),m(1616,1,Ai,Fq),s.If=function(n,t){$On(u(n,37),t)},v(Wn,"SelfLoopPreProcessor",1616),m(1617,1,{},Jq),s.Kb=function(n){return new wn(null,new pn(u(n,107).f,1))},v(Wn,"SelfLoopPreProcessor/lambda$0$Type",1617),m(1618,1,{},Hq),s.Kb=function(n){return u(n,341).a},v(Wn,"SelfLoopPreProcessor/lambda$1$Type",1618),m(1619,1,it,g1),s.Ad=function(n){Cwn(u(n,17))},v(Wn,"SelfLoopPreProcessor/lambda$2$Type",1619),m(1636,1,Ai,uNe),s.If=function(n,t){FMn(this,u(n,37),t)},v(Wn,"SelfLoopRouter",1636),m(1637,1,{},v6),s.Kb=function(n){return new wn(null,new pn(u(n,25).a,16))},v(Wn,"SelfLoopRouter/lambda$0$Type",1637),m(1638,1,Ft,FI),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"SelfLoopRouter/lambda$1$Type",1638),m(1639,1,Ft,JI),s.Mb=function(n){return bi(u(n,9),(me(),dp))},v(Wn,"SelfLoopRouter/lambda$2$Type",1639),m(1640,1,{},HI),s.Kb=function(n){return u(C(u(n,9),(me(),dp)),338)},v(Wn,"SelfLoopRouter/lambda$3$Type",1640),m(1641,1,it,XMe),s.Ad=function(n){S5n(this.a,this.b,u(n,338))},v(Wn,"SelfLoopRouter/lambda$4$Type",1641),m(1642,1,Ai,GI),s.If=function(n,t){nDn(u(n,37),t)},v(Wn,"SemiInteractiveCrossMinProcessor",1642),m(1643,1,Ft,qA),s.Mb=function(n){return u(n,9).k==(zn(),Qi)},v(Wn,"SemiInteractiveCrossMinProcessor/lambda$0$Type",1643),m(1644,1,Ft,Gq),s.Mb=function(n){return jDe(u(n,9))._b((Ne(),km))},v(Wn,"SemiInteractiveCrossMinProcessor/lambda$1$Type",1644),m(1645,1,Yt,j5),s.Le=function(n,t){return Z8n(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Wn,"SemiInteractiveCrossMinProcessor/lambda$2$Type",1645),m(1646,1,{},UA),s.Te=function(n,t){return T5n(u(n,9),u(t,9))},v(Wn,"SemiInteractiveCrossMinProcessor/lambda$3$Type",1646),m(1648,1,Ai,y6),s.If=function(n,t){LPn(u(n,37),t)},v(Wn,"SortByInputModelProcessor",1648),m(1649,1,Ft,XA),s.Mb=function(n){return u(n,12).g.c.length!=0},v(Wn,"SortByInputModelProcessor/lambda$0$Type",1649),m(1650,1,it,Tje),s.Ad=function(n){yCn(this.a,u(n,12))},v(Wn,"SortByInputModelProcessor/lambda$1$Type",1650),m(1729,804,{},LBe),s.bf=function(n){var t,i,r,c;switch(this.c=n,this.a.g){case 2:t=new Te,Zi(si(new wn(null,new pn(this.c.a.b,16)),new QI),new WMe(this,t)),GO(this,new c3),Ao(t,new E5),t.c.length=0,Zi(si(new wn(null,new pn(this.c.a.b,16)),new KA),new Nje(t)),GO(this,new UI),Ao(t,new u3),t.c.length=0,i=_Te(HY(k2(new wn(null,new pn(this.c.a.b,16)),new Dje(this))),new XI),Zi(new wn(null,new pn(this.c.a.a,16)),new VMe(i,t)),GO(this,new VI),Ao(t,new qq),t.c.length=0;break;case 3:r=new Te,GO(this,new qI),c=_Te(HY(k2(new wn(null,new pn(this.c.a.b,16)),new Oje(this))),new KI),Zi(si(new wn(null,new pn(this.c.a.b,16)),new Uq),new QMe(c,r)),GO(this,new Xq),Ao(r,new YI),r.c.length=0;break;default:throw $(new exe)}},s.b=0,v(lr,"EdgeAwareScanlineConstraintCalculation",1729),m(1730,1,jh,qI),s.Lb=function(n){return X(u(n,60).g,156)},s.Fb=function(n){return this===n},s.Mb=function(n){return X(u(n,60).g,156)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$0$Type",1730),m(1731,1,{},Oje),s.We=function(n){return XCn(this.a,u(n,60))},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$1$Type",1731),m(1739,1,nF,KMe),s.be=function(){UE(this.a,this.b,-1)},s.b=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$10$Type",1739),m(1741,1,jh,c3),s.Lb=function(n){return X(u(n,60).g,156)},s.Fb=function(n){return this===n},s.Mb=function(n){return X(u(n,60).g,156)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$11$Type",1741),m(1742,1,it,E5),s.Ad=function(n){u(n,375).be()},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$12$Type",1742),m(1743,1,Ft,KA),s.Mb=function(n){return X(u(n,60).g,9)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$13$Type",1743),m(1745,1,it,Nje),s.Ad=function(n){Pjn(this.a,u(n,60))},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$14$Type",1745),m(1744,1,nF,nCe),s.be=function(){UE(this.b,this.a,-1)},s.a=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$15$Type",1744),m(1746,1,jh,UI),s.Lb=function(n){return X(u(n,60).g,9)},s.Fb=function(n){return this===n},s.Mb=function(n){return X(u(n,60).g,9)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$16$Type",1746),m(1747,1,it,u3),s.Ad=function(n){u(n,375).be()},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$17$Type",1747),m(1748,1,{},Dje),s.We=function(n){return KCn(this.a,u(n,60))},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$18$Type",1748),m(1749,1,{},XI),s.Ue=function(){return 0},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$19$Type",1749),m(1732,1,{},KI),s.Ue=function(){return 0},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$2$Type",1732),m(1751,1,it,VMe),s.Ad=function(n){hvn(this.a,this.b,u(n,320))},s.a=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$20$Type",1751),m(1750,1,nF,YMe),s.be=function(){aUe(this.a,this.b,-1)},s.b=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$21$Type",1750),m(1752,1,jh,VI),s.Lb=function(n){return u(n,60),!0},s.Fb=function(n){return this===n},s.Mb=function(n){return u(n,60),!0},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$22$Type",1752),m(1753,1,it,qq),s.Ad=function(n){u(n,375).be()},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$23$Type",1753),m(1733,1,Ft,Uq),s.Mb=function(n){return X(u(n,60).g,9)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$3$Type",1733),m(1735,1,it,QMe),s.Ad=function(n){dvn(this.a,this.b,u(n,60))},s.a=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$4$Type",1735),m(1734,1,nF,tCe),s.be=function(){UE(this.b,this.a,-1)},s.a=0,v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$5$Type",1734),m(1736,1,jh,Xq),s.Lb=function(n){return u(n,60),!0},s.Fb=function(n){return this===n},s.Mb=function(n){return u(n,60),!0},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$6$Type",1736),m(1737,1,it,YI),s.Ad=function(n){u(n,375).be()},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$7$Type",1737),m(1738,1,Ft,QI),s.Mb=function(n){return X(u(n,60).g,156)},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$8$Type",1738),m(1740,1,it,WMe),s.Ad=function(n){j8n(this.a,this.b,u(n,60))},v(lr,"EdgeAwareScanlineConstraintCalculation/lambda$9$Type",1740),m(1547,1,Ai,vOe),s.If=function(n,t){YLn(this,u(n,37),t)};var vin;v(lr,"HorizontalGraphCompactor",1547),m(1548,1,{},Ije),s.df=function(n,t){var i,r,c;return yhe(n,t)||(i=J3(n),r=J3(t),i&&i.k==(zn(),wr)||r&&r.k==(zn(),wr))?0:(c=u(C(this.a.a,(me(),Lv)),316),lpn(c,i?i.k:(zn(),dr),r?r.k:(zn(),dr)))},s.ef=function(n,t){var i,r,c;return yhe(n,t)?1:(i=J3(n),r=J3(t),c=u(C(this.a.a,(me(),Lv)),316),ale(c,i?i.k:(zn(),dr),r?r.k:(zn(),dr)))},v(lr,"HorizontalGraphCompactor/1",1548),m(1549,1,{},VA),s.cf=function(n,t){return Mj(),n.a.i==0},v(lr,"HorizontalGraphCompactor/lambda$0$Type",1549),m(1550,1,{},_je),s.cf=function(n,t){return D5n(this.a,n,t)},v(lr,"HorizontalGraphCompactor/lambda$1$Type",1550),m(1696,1,{},dRe);var yin,kin;v(lr,"LGraphToCGraphTransformer",1696),m(1704,1,Ft,l0),s.Mb=function(n){return n!=null},v(lr,"LGraphToCGraphTransformer/0methodref$nonNull$Type",1704),m(1697,1,{},gk),s.Kb=function(n){return rl(),su(C(u(u(n,60).g,9),(me(),wi)))},v(lr,"LGraphToCGraphTransformer/lambda$0$Type",1697),m(1698,1,{},od),s.Kb=function(n){return rl(),MFe(u(u(n,60).g,156))},v(lr,"LGraphToCGraphTransformer/lambda$1$Type",1698),m(1707,1,Ft,S5),s.Mb=function(n){return rl(),X(u(n,60).g,9)},v(lr,"LGraphToCGraphTransformer/lambda$10$Type",1707),m(1708,1,it,YA),s.Ad=function(n){E5n(u(n,60))},v(lr,"LGraphToCGraphTransformer/lambda$11$Type",1708),m(1709,1,Ft,wk),s.Mb=function(n){return rl(),X(u(n,60).g,156)},v(lr,"LGraphToCGraphTransformer/lambda$12$Type",1709),m(1713,1,it,pk),s.Ad=function(n){njn(u(n,60))},v(lr,"LGraphToCGraphTransformer/lambda$13$Type",1713),m(1710,1,it,Lje),s.Ad=function(n){rwn(this.a,u(n,8))},s.a=0,v(lr,"LGraphToCGraphTransformer/lambda$14$Type",1710),m(1711,1,it,Pje),s.Ad=function(n){uwn(this.a,u(n,119))},s.a=0,v(lr,"LGraphToCGraphTransformer/lambda$15$Type",1711),m(1712,1,it,$je),s.Ad=function(n){cwn(this.a,u(n,8))},s.a=0,v(lr,"LGraphToCGraphTransformer/lambda$16$Type",1712),m(1714,1,{},QA),s.Kb=function(n){return rl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(lr,"LGraphToCGraphTransformer/lambda$17$Type",1714),m(1715,1,Ft,o3),s.Mb=function(n){return rl(),cc(u(n,17))},v(lr,"LGraphToCGraphTransformer/lambda$18$Type",1715),m(1716,1,it,Rje),s.Ad=function(n){W9n(this.a,u(n,17))},v(lr,"LGraphToCGraphTransformer/lambda$19$Type",1716),m(1700,1,it,Bje),s.Ad=function(n){Myn(this.a,u(n,156))},v(lr,"LGraphToCGraphTransformer/lambda$2$Type",1700),m(1717,1,{},WI),s.Kb=function(n){return rl(),new wn(null,new pn(u(n,25).a,16))},v(lr,"LGraphToCGraphTransformer/lambda$20$Type",1717),m(1718,1,{},mk),s.Kb=function(n){return rl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(lr,"LGraphToCGraphTransformer/lambda$21$Type",1718),m(1719,1,{},x5),s.Kb=function(n){return rl(),u(C(u(n,17),(me(),vg)),16)},v(lr,"LGraphToCGraphTransformer/lambda$22$Type",1719),m(1720,1,Ft,Kq),s.Mb=function(n){return fpn(u(n,16))},v(lr,"LGraphToCGraphTransformer/lambda$23$Type",1720),m(1721,1,it,zje),s.Ad=function(n){VCn(this.a,u(n,16))},v(lr,"LGraphToCGraphTransformer/lambda$24$Type",1721),m(1722,1,{},w1),s.Kb=function(n){return rl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(lr,"LGraphToCGraphTransformer/lambda$25$Type",1722),m(1723,1,Ft,WA),s.Mb=function(n){return rl(),cc(u(n,17))},v(lr,"LGraphToCGraphTransformer/lambda$26$Type",1723),m(1725,1,it,Fje),s.Ad=function(n){q8n(this.a,u(n,17))},v(lr,"LGraphToCGraphTransformer/lambda$27$Type",1725),m(1724,1,it,Jje),s.Ad=function(n){Qbn(this.a,u(n,70))},s.a=0,v(lr,"LGraphToCGraphTransformer/lambda$28$Type",1724),m(1699,1,it,ZMe),s.Ad=function(n){M6n(this.a,this.b,u(n,156))},v(lr,"LGraphToCGraphTransformer/lambda$3$Type",1699),m(1701,1,{},Wg),s.Kb=function(n){return rl(),new wn(null,new pn(u(n,25).a,16))},v(lr,"LGraphToCGraphTransformer/lambda$4$Type",1701),m(1702,1,{},ZI),s.Kb=function(n){return rl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(lr,"LGraphToCGraphTransformer/lambda$5$Type",1702),m(1703,1,{},vk),s.Kb=function(n){return rl(),u(C(u(n,17),(me(),vg)),16)},v(lr,"LGraphToCGraphTransformer/lambda$6$Type",1703),m(1705,1,it,Hje),s.Ad=function(n){uTn(this.a,u(n,16))},v(lr,"LGraphToCGraphTransformer/lambda$8$Type",1705),m(1706,1,it,eCe),s.Ad=function(n){Twn(this.a,this.b,u(n,156))},v(lr,"LGraphToCGraphTransformer/lambda$9$Type",1706),m(1695,1,{},s3),s.af=function(n){var t,i,r,c,o;for(this.a=n,this.d=new gX,this.c=oe(Yme,On,124,this.a.a.a.c.length,0,1),this.b=0,i=new L(this.a.a.a);i.a=I&&(Te(o,ve(p)),V=k.Math.max(V,te[p-1]-y),f+=O,R+=te[p-1]-R,y=te[p-1],O=h[p]),O=k.Math.max(O,h[p]),++p;f+=O}A=k.Math.min(1/V,1/t.b/f),A>r&&(r=A,i=o)}return i},s.ng=function(){return!1},v(Sh,"MSDCutIndexHeuristic",803),m(1647,1,Ai,Vq),s.If=function(n,t){eLn(u(n,37),t)},v(Sh,"SingleEdgeGraphWrapper",1647),m(231,23,{3:1,35:1,23:1,231:1},_j);var Tv,m7,v7,hm,tx,Cv,y7=yt(Cu,"CenterEdgeLabelPlacementStrategy",231,Ct,S9n,J2n),Iin;m(422,23,{3:1,35:1,23:1,422:1},dse);var bve,Xte,gve=yt(Cu,"ConstraintCalculationStrategy",422,Ct,K5n,H2n),_in;m(301,23,{3:1,35:1,23:1,301:1,188:1,196:1},h$),s.bg=function(){return yUe(this)},s.og=function(){return yUe(this)};var eD,ix,wve,pve,mve=yt(Cu,"CrossingMinimizationStrategy",301,Ct,n6n,G2n),Lin;m(350,23,{3:1,35:1,23:1,350:1},KX);var vve,Kte,UJ,yve=yt(Cu,"CuttingStrategy",350,Ct,R4n,q2n),Pin;m(267,23,{3:1,35:1,23:1,267:1,188:1,196:1},A3),s.bg=function(){return SXe(this)},s.og=function(){return SXe(this)};var Vte,kve,Yte,Qte,Wte,Zte,eie,nie,nD,jve=yt(Cu,"CycleBreakingStrategy",267,Ct,R8n,U2n),$in;m(419,23,{3:1,35:1,23:1,419:1},bse);var XJ,Eve,Sve=yt(Cu,"DirectionCongruency",419,Ct,V5n,X2n),Rin;m(449,23,{3:1,35:1,23:1,449:1},YX);var k7,tie,Ov,Bin=yt(Cu,"EdgeConstraint",449,Ct,B4n,K2n),zin;m(284,23,{3:1,35:1,23:1,284:1},$j);var iie,rie,cie,uie,KJ,oie,xve=yt(Cu,"EdgeLabelSideSelection",284,Ct,x9n,V2n),Fin;m(476,23,{3:1,35:1,23:1,476:1},gse);var VJ,Ave,Mve=yt(Cu,"EdgeStraighteningStrategy",476,Ct,Y5n,Y2n),Jin;m(282,23,{3:1,35:1,23:1,282:1},Lj);var sie,Tve,Cve,YJ,Ove,Nve,Dve=yt(Cu,"FixedAlignment",282,Ct,A9n,Q2n),Hin;m(283,23,{3:1,35:1,23:1,283:1},Pj);var Ive,_ve,Lve,Pve,rx,$ve,Rve=yt(Cu,"GraphCompactionStrategy",283,Ct,M9n,W2n),Gin;m(261,23,{3:1,35:1,23:1,261:1},c2);var j7,QJ,E7,Kl,cx,WJ,S7,Nv,ZJ,ux,lie=yt(Cu,"GraphProperties",261,Ct,a7n,Z2n),qin;m(302,23,{3:1,35:1,23:1,302:1},QX);var tD,fie,aie,hie=yt(Cu,"GreedySwitchType",302,Ct,z4n,emn),Uin;m(329,23,{3:1,35:1,23:1,329:1},WX);var dm,Bve,iD,die=yt(Cu,"GroupOrderStrategy",329,Ct,F4n,nmn),Xin;m(315,23,{3:1,35:1,23:1,315:1},ZX);var Sy,rD,Dv,Kin=yt(Cu,"InLayerConstraint",315,Ct,J4n,tmn),Vin;m(420,23,{3:1,35:1,23:1,420:1},wse);var bie,zve,Fve=yt(Cu,"InteractiveReferencePoint",420,Ct,Q5n,imn),Yin,Jve,xy,fp,cD,eH,Hve,Gve,nH,qve,Ay,tH,ox,My,K1,gie,iH,Du,Uve,rb,po,wie,pie,uD,mg,ap,Ty,Xve,Qin,Cy,oD,bm,ja,gf,mie,Iv,cb,Ci,wi,Kve,Vve,Yve,Qve,Wve,vie,rH,vs,hp,yie,Oy,sx,Hd,_v,dp,Lv,Pv,x7,vg,Zve,kie,jie,lx,Ny,cH,Dy,$v;m(165,23,{3:1,35:1,23:1,165:1},sC);var fx,V1,ax,yg,sD,e5e=yt(Cu,"LayerConstraint",165,Ct,Y6n,rmn),Win;m(423,23,{3:1,35:1,23:1,423:1},pse);var Eie,Sie,n5e=yt(Cu,"LayerUnzippingStrategy",423,Ct,W5n,cmn),Zin;m(843,1,qa,ET),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,gwe),""),"Direction Congruency"),"Specifies how drawings of the same graph with different layout directions compare to each other: either a natural reading direction is preserved or the drawings are rotated versions of each other."),b5e),(cg(),Ri)),Sve),nn((ph(),Mn))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,wwe),""),"Feedback Edges"),"Whether feedback edges should be highlighted by routing around the nodes."),(Pn(),!1)),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,bF),""),"Interactive Reference Point"),"Determines which point of a node is considered by interactive layout phases."),y5e),Ri),Fve),nn(Mn)))),Gi(n,bF,ON,icn),Gi(n,bF,MS,tcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,pwe),""),"Merge Edges"),"Edges that have no ports are merged so they touch the connected nodes at the same points. When this option is disabled, one port is created for each edge directly connected to a node. When it is enabled, all such incoming edges share an input port, and all outgoing edges share an output port."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,mwe),""),"Merge Hierarchy-Crossing Edges"),"If hierarchical layout is active, hierarchy-crossing edges use as few hierarchical ports as possible. They are broken by the algorithm, with hierarchical ports inserted as required. Usually, one such port is created for each edge at each hierarchy crossing point. With this option set to true, we try to create as few hierarchical ports as possible in the process. In particular, all edges that form a hyperedge can share a port."),!0),Sr),Yi),nn(Mn)))),We(n,new Ge(Zbn(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vwe),""),"Allow Non-Flow Ports To Switch Sides"),"Specifies whether non-flow ports may switch sides if their node's port constraints are either FIXED_SIDE or FIXED_ORDER. A non-flow port is a port on a side that is not part of the currently configured layout flow. For instance, given a left-to-right layout direction, north and south ports would be considered non-flow ports. Further note that the underlying criterium whether to switch sides or not solely relies on the minimization of edge crossings. Hence, edge length and other aesthetics criteria are not addressed."),!1),Sr),Yi),nn(Kd)),z(B(ze,1),Se,2,6,["org.eclipse.elk.layered.northOrSouthPort"])))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ywe),""),"Port Sorting Strategy"),"Only relevant for nodes with FIXED_SIDE port constraints. Determines the way a node's ports are distributed on the sides of a node if their order is not prescribed. The option is set on parent nodes."),N5e),Ri),J4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,kwe),""),"Thoroughness"),"How much effort should be spent to produce a nice layout."),ve(7)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,jwe),""),"Add Unnecessary Bendpoints"),"Adds bend points even if an edge does not change direction. If true, each long edge dummy will contribute a bend point to its edges and hierarchy-crossing edges will always get a bend point where they cross hierarchy boundaries. By default, bend points are only added where an edge changes direction."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ewe),""),"Generate Position and Layer IDs"),"If enabled position id and layer id are generated, which are usually only used internally when setting the interactiveLayout option. This option should be specified on the root node."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ON),"cycleBreaking"),"Cycle Breaking Strategy"),"Strategy for cycle breaking. Cycle breaking looks for cycles in the graph and determines which edges to reverse to break the cycles. Reversed edges will end up pointing to the opposite direction of regular edges (that is, reversed edges will point left if edges usually point right)."),d5e),Ri),jve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,NN),$ee),"Node Layering Strategy"),"Strategy for node layering."),E5e),Ri),O4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Swe),$ee),"Layer Constraint"),"Determines a constraint on the placement of the node regarding the layering."),k5e),Ri),e5e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xwe),$ee),"Layer Choice Constraint"),"Allows to set a constraint regarding the layer placement of a node. Let i be the value of teh constraint. Assumed the drawing has n layers and i < n. If set to i, it expresses that the node should be placed in i-th layer. Should i>=n be true then the node is placed in the last layer of the drawing. Note that this option is not part of any of ELK Layered's default configurations but is only evaluated as part of the `InteractiveLayeredGraphVisitor`, which must be applied manually or used via the `DiagramLayoutEngine."),null),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Awe),$ee),"Layer ID"),"Layer identifier that was calculated by ELK Layered for a node. This is only generated if interactiveLayot or generatePositionAndLayerIds is set."),ve(-1)),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,cee),CQe),"Upper Bound On Width [MinWidth Layerer]"),"Defines a loose upper bound on the width of the MinWidth layerer. If set to '-1' multiple values are tested and the best result is selected."),ve(4)),hc),jr),nn(Mn)))),Gi(n,cee,NN,fcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,uee),CQe),"Upper Layer Estimation Scaling Factor [MinWidth Layerer]"),"Multiplied with Upper Bound On Width for defining an upper bound on the width of layers which haven't been determined yet, but whose maximum width had been (roughly) estimated by the MinWidth algorithm. Compensates for too high estimations. If set to '-1' multiple values are tested and the best result is selected."),ve(2)),hc),jr),nn(Mn)))),Gi(n,uee,NN,hcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,oee),OQe),"Node Promotion Strategy"),"Reduces number of dummy nodes after layering phase (if possible)."),j5e),Ri),B4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,see),OQe),"Max Node Promotion Iterations"),"Limits the number of iterations for node promotion."),ve(0)),hc),jr),nn(Mn)))),Gi(n,see,oee,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,lee),"layering.coffmanGraham"),"Layer Bound"),"The maximum number of nodes allowed per layer."),ve(oi)),hc),jr),nn(Mn)))),Gi(n,lee,NN,ccn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,MS),V8),"Crossing Minimization Strategy"),"Strategy for crossing minimization."),h5e),Ri),mve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Mwe),V8),"Force Node Model Order"),"The node order given by the model does not change to produce a better layout. E.g. if node A is before node B in the model this is not changed during crossing minimization. This assumes that the node model order is already respected before crossing minimization. This can be achieved by setting considerModelOrder.strategy to NODES_AND_EDGES."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,fee),V8),"Hierarchical Sweepiness"),"How likely it is to use cross-hierarchy (1) vs bottom-up (-1)."),.1),ec),gr),nn(Mn)))),Gi(n,fee,MF,Crn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,aee),V8),"Semi-Interactive Crossing Minimization"),"Preserves the order of nodes within a layer but still minimizes crossings between edges connecting long edge dummies. Derives the desired order from positions specified by the 'org.eclipse.elk.position' layout option. Requires a crossing minimization strategy that is able to process 'in-layer' constraints."),!1),Sr),Yi),nn(Mn)))),Gi(n,aee,MS,Lrn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Twe),V8),"In Layer Predecessor of"),"Allows to set a constraint which specifies of which node the current node is the predecessor. If set to 's' then the node is the predecessor of 's' and is in the same layer"),null),By),ze),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Cwe),V8),"In Layer Successor of"),"Allows to set a constraint which specifies of which node the current node is the successor. If set to 's' then the node is the successor of 's' and is in the same layer"),null),By),ze),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Owe),V8),"Position Choice Constraint"),"Allows to set a constraint regarding the position placement of a node in a layer. Assumed the layer in which the node placed includes n other nodes and i < n. If set to i, it expresses that the node should be placed at the i-th position. Should i>=n be true then the node is placed at the last position in the layer. Note that this option is not part of any of ELK Layered's default configurations but is only evaluated as part of the `InteractiveLayeredGraphVisitor`, which must be applied manually or used via the `DiagramLayoutEngine."),null),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Nwe),V8),"Position ID"),"Position within a layer that was determined by ELK Layered for a node. This is only generated if interactiveLayot or generatePositionAndLayerIds is set."),ve(-1)),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Dwe),NQe),"Greedy Switch Activation Threshold"),"By default it is decided automatically if the greedy switch is activated or not. The decision is based on whether the size of the input graph (without dummy nodes) is smaller than the value of this option. A '0' enforces the activation."),ve(40)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hee),NQe),"Greedy Switch Crossing Minimization"),"Greedy Switch strategy for crossing minimization. The greedy switch heuristic is executed after the regular crossing minimization as a post-processor. Note that if 'hierarchyHandling' is set to 'INCLUDE_CHILDREN', the 'greedySwitchHierarchical.type' option must be used."),a5e),Ri),hie),nn(Mn)))),Gi(n,hee,MS,Mrn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,gF),"crossingMinimization.greedySwitchHierarchical"),"Greedy Switch Crossing Minimization (hierarchical)"),"Activates the greedy switch heuristic in case hierarchical layout is used. The differences to the non-hierarchical case (see 'greedySwitch.type') are: 1) greedy switch is inactive by default, 3) only the option value set on the node at which hierarchical layout starts is relevant, and 2) if it's activated by the user, it properly addresses hierarchy-crossing edges."),f5e),Ri),hie),nn(Mn)))),Gi(n,gF,MS,Srn),Gi(n,gF,MF,xrn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,pv),DQe),"Node Placement Strategy"),"Strategy for node placement."),O5e),Ri),_4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,wF),DQe),"Favor Straight Edges Over Balancing"),"Favor straight edges over a balanced node placement. The default behavior is determined automatically based on the used 'edgeRouting'. For an orthogonal style it is set to true, for all other styles to false."),Sr),Yi),nn(Mn)))),Gi(n,wF,pv,Ccn),Gi(n,wF,pv,Ocn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,dee),IQe),"BK Edge Straightening"),"Specifies whether the Brandes Koepf node placer tries to increase the number of straight edges at the expense of diagram size. There is a subtle difference to the 'favorStraightEdges' option, which decides whether a balanced placement of the nodes is desired, or not. In bk terms this means combining the four alignments into a single balanced one, or not. This option on the other hand tries to straighten additional edges during the creation of each of the four alignments."),M5e),Ri),Mve),nn(Mn)))),Gi(n,dee,pv,xcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,bee),IQe),"BK Fixed Alignment"),"Tells the BK node placer to use a certain alignment (out of its four) instead of the one producing the smallest height, or the combination of all four."),T5e),Ri),Dve),nn(Mn)))),Gi(n,bee,pv,Mcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,gee),"nodePlacement.linearSegments"),"Linear Segments Deflection Dampening"),"Dampens the movement of nodes to keep the diagram from getting too large."),.3),ec),gr),nn(Mn)))),Gi(n,gee,pv,Dcn),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,wee),"nodePlacement.networkSimplex"),"Node Flexibility"),"Aims at shorter and straighter edges. Two configurations are possible: (a) allow ports to move freely on the side they are assigned to (the order is always defined beforehand), (b) additionally allow to enlarge a node wherever it helps. If this option is not configured for a node, the 'nodeFlexibility.default' value is used, which is specified for the node's parent."),Ri),Qie),nn(fr)))),Gi(n,wee,pv,Pcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,pee),"nodePlacement.networkSimplex.nodeFlexibility"),"Node Flexibility Default"),"Default value of the 'nodeFlexibility' option for the children of a hierarchical node."),C5e),Ri),Qie),nn(Mn)))),Gi(n,pee,pv,Lcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Iwe),_Qe),"Self-Loop Distribution"),"Alter the distribution of the loops around the node. It only takes effect for PortConstraints.FREE."),p5e),Ri),q4e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,_we),_Qe),"Self-Loop Ordering"),"Alter the ordering of the loops they can either be stacked or sequenced. It only takes effect for PortConstraints.FREE."),m5e),Ri),U4e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,pF),"edgeRouting.splines"),"Spline Routing Mode"),"Specifies the way control points are assembled for each individual edge. CONSERVATIVE ensures that edges are properly routed around the nodes but feels rather orthogonal at times. SLOPPY uses fewer control points to obtain curvier edge routes but may result in edges overlapping nodes."),v5e),Ri),K4e),nn(Mn)))),Gi(n,pF,IN,Urn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,mF),"edgeRouting.splines.sloppy"),"Sloppy Spline Layer Spacing Factor"),"Spacing factor for routing area between layers when using sloppy spline routing."),.2),ec),gr),nn(Mn)))),Gi(n,mF,IN,Krn),Gi(n,mF,pF,Vrn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,mee),"edgeRouting.polyline"),"Sloped Edge Zone Width"),"Width of the strip to the left and to the right of each layer where the polyline edge router is allowed to refrain from ensuring that edges are routed horizontally. This prevents awkward bend points for nodes that extent almost to the edge of their layer."),2),ec),gr),nn(Mn)))),Gi(n,mee,IN,Jrn),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Lwe),Xa),"Spacing Base Value"),"An optional base value for all other layout options of the 'spacing' group. It can be used to conveniently alter the overall 'spaciousness' of the drawing. Whenever an explicit value is set for the other layout options, this base value will have no effect. The base value is not inherited, i.e. it must be set for each hierarchical node."),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Pwe),Xa),"Edge Node Between Layers Spacing"),"The spacing to be preserved between nodes and edges that are routed next to the node's layer. For the spacing between nodes and edges that cross the node's layer 'spacing.edgeNode' is used."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,$we),Xa),"Edge Edge Between Layer Spacing"),"Spacing to be preserved between pairs of edges that are routed between the same pair of layers. Note that 'spacing.edgeEdge' is used for the spacing between pairs of edges crossing the same layer."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Rwe),Xa),"Node Node Between Layers Spacing"),"The spacing to be preserved between any pair of nodes of two adjacent layers. Note that 'spacing.nodeNode' is used for the spacing between nodes within the layer itself."),20),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Bwe),Qwe),"Direction Priority"),"Defines how important it is to have a certain edge point into the direction of the overall layout. This option is evaluated during the cycle breaking phase."),ve(0)),hc),jr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,zwe),Qwe),"Shortness Priority"),"Defines how important it is to keep an edge as short as possible. This option is evaluated during the layering phase."),ve(0)),hc),jr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Fwe),Qwe),"Straightness Priority"),"Defines how important it is to keep an edge straight, i.e. aligned with one of the two axes. This option is evaluated during node placement."),ve(0)),hc),jr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vee),Wwe),"Connected Components Compaction"),"Tries to further compact components (disconnected sub-graphs)."),!1),Sr),Yi),nn(Mn)))),Gi(n,vee,kS,!0),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Jwe),LQe),"Post Compaction Strategy"),PQe),i5e),Ri),Rve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Hwe),LQe),"Post Compaction Constraint Calculation"),PQe),t5e),Ri),gve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vF),Zwe),"High Degree Node Treatment"),"Makes room around high degree nodes to place leafs and trees."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,yee),Zwe),"High Degree Node Threshold"),"Whether a node is considered to have a high degree."),ve(16)),hc),jr),nn(Mn)))),Gi(n,yee,vF,!0),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,kee),Zwe),"High Degree Node Maximum Tree Height"),"Maximum height of a subtree connected to a high degree node to be moved to separate layers."),ve(5)),hc),jr),nn(Mn)))),Gi(n,kee,vF,!0),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,q1),epe),"Graph Wrapping Strategy"),"For certain graphs and certain prescribed drawing areas it may be desirable to split the laid out graph into chunks that are placed side by side. The edges that connect different chunks are 'wrapped' around from the end of one chunk to the start of the other chunk. The points between the chunks are referred to as 'cuts'."),_5e),Ri),W4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,yF),epe),"Additional Wrapped Edges Spacing"),"To visually separate edges that are wrapped from regularly routed edges an additional spacing value can be specified in form of this layout option. The spacing is added to the regular edgeNode spacing."),10),ec),gr),nn(Mn)))),Gi(n,yF,q1,Vcn),Gi(n,yF,q1,Ycn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,kF),epe),"Correction Factor for Wrapping"),"At times and for certain types of graphs the executed wrapping may produce results that are consistently biased in the same fashion: either wrapping to often or to rarely. This factor can be used to correct the bias. Internally, it is simply multiplied with the 'aspect ratio' layout option."),1),ec),gr),nn(Mn)))),Gi(n,kF,q1,Wcn),Gi(n,kF,q1,Zcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,TS),$Qe),"Cutting Strategy"),"The strategy by which the layer indexes are determined at which the layering crumbles into chunks."),I5e),Ri),yve),nn(Mn)))),Gi(n,TS,q1,cun),Gi(n,TS,q1,uun),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,jee),$Qe),"Manually Specified Cuts"),"Allows the user to specify her own cuts for a certain graph."),Wa),wl),nn(Mn)))),Gi(n,jee,TS,nun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Eee),"wrapping.cutting.msd"),"MSD Freedom"),"The MSD cutting strategy starts with an initial guess on the number of chunks the graph should be split into. The freedom specifies how much the strategy may deviate from this guess. E.g. if an initial number of 3 is computed, a freedom of 1 allows 2, 3, and 4 cuts."),D5e),hc),jr),nn(Mn)))),Gi(n,Eee,TS,iun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,jF),RQe),"Validification Strategy"),"When wrapping graphs, one can specify indices that are not allowed as split points. The validification strategy makes sure every computed split point is allowed."),L5e),Ri),Q4e),nn(Mn)))),Gi(n,jF,q1,mun),Gi(n,jF,q1,vun),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,EF),RQe),"Valid Indices for Wrapping"),null),Wa),wl),nn(Mn)))),Gi(n,EF,q1,gun),Gi(n,EF,q1,wun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,SF),npe),"Improve Cuts"),"For general graphs it is important that not too many edges wrap backwards. Thus a compromise between evenly-distributed cuts and the total number of cut edges is sought."),!0),Sr),Yi),nn(Mn)))),Gi(n,SF,q1,fun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xF),npe),"Distance Penalty When Improving Cuts"),null),2),ec),gr),nn(Mn)))),Gi(n,xF,q1,sun),Gi(n,xF,SF,!0),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,See),npe),"Improve Wrapped Edges"),"The initial wrapping is performed in a very simple way. As a consequence, edges that wrap from one chunk to another may be unnecessarily long. Activating this option tries to shorten such edges."),!0),Sr),Yi),nn(Mn)))),Gi(n,See,q1,hun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xee),Ree),"Layer Unzipping Strategy"),"The strategy to use for unzipping a layer into multiple sublayers while maintaining the existing ordering of nodes and edges after crossing minimization. The default value is 'NONE'."),A5e),Ri),n5e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Aee),Ree),"Minimize Edge Length Heuristic"),"Use a heuristic to decide whether or not to actually perform the layer split with the goal of minimizing the total edge length. This option only works when layerSplit is set to 2. The property can be set to the nodes in a layer, which then applies the property for the layer. If any node sets the value to true, then the value is set to true for the entire layer."),!1),Sr),Yi),nn(fr)))),Gi(n,Aee,Mee,mcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Mee),Ree),"Unzipping Layer Split"),"Defines the number of sublayers to split a layer into. The property can be set to the nodes in a layer, which then applies the property for the layer. If multiple nodes set the value to different values, then the lowest value is chosen."),S5e),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Tee),Ree),"Reset Alternation on Long Edges"),"If set to true, nodes will always be placed in the first sublayer after a long edge when using the ALTERNATING strategy. Otherwise long edge dummies are treated the same as regular nodes. The default value is true. The property can be set to the nodes in a layer, which then applies the property for the layer. If any node sets the value to false, then the value is set to false for the entire layer."),x5e),Sr),Yi),nn(fr)))),Gi(n,Tee,xee,ycn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Gwe),Bee),"Edge Label Side Selection"),"Method to decide on edge label sides."),w5e),Ri),xve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,qwe),Bee),"Edge Center Label Placement Strategy"),"Determines in which layer center labels of long edges should be placed."),g5e),Ri),y7),Mi(Mn,z(B(Qa,1),ke,160,0,[Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,AF),CS),"Consider Model Order"),"Preserves the order of nodes and edges in the model file if this does not lead to additional edge crossings. Depending on the strategy this is not always possible since the node and edge order might be conflicting."),l5e),Ri),F4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Uwe),CS),"Consider Port Order"),"If disabled the port order of output ports is derived from the edge order and input ports are ordered by their incoming connections. If enabled all ports are ordered by the port model order."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,DN),CS),"No Model Order"),"Set on a node to not set a model order for this node even though it is a real node."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Cee),CS),"Consider Model Order for Components"),"If set to NONE the usual ordering strategy (by cumulative node priority and size of nodes) is used. INSIDE_PORT_SIDES orders the components with external ports only inside the groups with the same port side. FORCE_MODEL_ORDER enforces the mode order on components. This option might produce bad alignments and sub optimal drawings in terms of used area since the ordering should be respected."),r5e),Ri),y3e),nn(Mn)))),Gi(n,Cee,kS,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Xwe),CS),"Long Edge Ordering Strategy"),"Indicates whether long edges are sorted under, over, or equal to nodes that have no connection to a previous layer in a left-to-right or right-to-left layout. Under and over changes to right and left in a vertical layout."),s5e),Ri),D4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Oee),CS),"Crossing Counter Node Order Influence"),"Indicates with what percentage (1 for 100%) violations of the node model order are weighted against the crossings e.g. a value of 0.5 means two model order violations are as important as on edge crossing. This allows some edge crossings in favor of preserving the model order. It is advised to set this value to a very small positive value (e.g. 0.001) to have minimal crossing and a optimal node order. Defaults to no influence (0)."),0),ec),gr),nn(Mn)))),Gi(n,Oee,AF,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Nee),CS),"Crossing Counter Port Order Influence"),"Indicates with what percentage (1 for 100%) violations of the port model order are weighted against the crossings e.g. a value of 0.5 means two model order violations are as important as on edge crossing. This allows some edge crossings in favor of preserving the model order. It is advised to set this value to a very small positive value (e.g. 0.001) to have minimal crossing and a optimal port order. Defaults to no influence (0)."),0),ec),gr),nn(Mn)))),Gi(n,Nee,AF,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Dee),Y8),tpe),"Used to define partial ordering groups during cycle breaking. A lower group id means that the group is sorted before other groups. A group model order of 0 is the default group."),ve(0)),hc),jr),nn(fr)))),Gi(n,Dee,DN,!1),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Iee),Y8),tpe),"Used to define partial ordering groups during crossing minimization. A lower group id means that the group is sorted before other groups. A group model order of 0 is the default group."),ve(0)),hc),jr),Mi(fr,z(B(Qa,1),ke,160,0,[Sa,Kd]))))),Gi(n,Iee,DN,!1),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,_ee),Y8),tpe),"Used to define partial ordering groups during component packing. A lower group id means that the group is sorted before other groups. A group model order of 0 is the default group."),ve(0)),hc),jr),Mi(fr,z(B(Qa,1),ke,160,0,[Sa,Kd]))))),Gi(n,_ee,DN,!1),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Kwe),Y8),"Cycle Breaking Group Ordering Strategy"),"Determines how to count ordering violations during cycle breaking. NONE: They do not count. ENFORCED: A group with a higher model order is before a node with a smaller. MODEL_ORDER: The model order counts instead of the model order group id ordering."),c5e),Ri),die),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Lee),Y8),"Cycle Breaking Preferred Source Id"),"The model order group id for which should be preferred as a source if possible."),hc),jr),nn(Mn)))),Gi(n,Lee,ON,lrn),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Pee),Y8),"Cycle Breaking Preferred Target Id"),"The model order group id for which should be preferred as a target if possible."),hc),jr),nn(Mn)))),Gi(n,Pee,ON,arn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Vwe),Y8),"Crossing Minimization Group Ordering Strategy"),"Determines how to count ordering violations during crossing minimization. NONE: They do not count. ENFORCED: A group with a lower id is before a group with a higher id. MODEL_ORDER: The model order counts instead of the model order group id ordering."),o5e),Ri),die),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ywe),Y8),"Crossing Minimization Enforced Group Orders"),"Holds all group ids which are enforcing their order during crossing minimization strategies. E.g. if only groups 2 and -1 (default) enforce their ordering. Other groups e.g. the group of timer nodes can be ordered arbitrarily if it helps and the mentioned groups may not change their order."),u5e),Wa),wl),nn(Mn)))),rYe((new OU,n))};var ern,nrn,trn,t5e,irn,i5e,rrn,r5e,crn,urn,orn,c5e,srn,lrn,frn,arn,hrn,u5e,drn,o5e,brn,grn,wrn,prn,s5e,mrn,vrn,yrn,l5e,krn,jrn,Ern,f5e,Srn,xrn,Arn,a5e,Mrn,Trn,Crn,Orn,Nrn,Drn,Irn,_rn,Lrn,Prn,h5e,$rn,d5e,Rrn,b5e,Brn,g5e,zrn,w5e,Frn,Jrn,Hrn,p5e,Grn,m5e,qrn,v5e,Urn,Xrn,Krn,Vrn,Yrn,Qrn,Wrn,Zrn,ecn,ncn,y5e,tcn,icn,rcn,ccn,ucn,ocn,k5e,scn,lcn,fcn,acn,hcn,dcn,bcn,j5e,gcn,E5e,wcn,S5e,pcn,mcn,vcn,x5e,ycn,kcn,A5e,jcn,Ecn,Scn,M5e,xcn,Acn,T5e,Mcn,Tcn,Ccn,Ocn,Ncn,Dcn,Icn,_cn,C5e,Lcn,Pcn,$cn,O5e,Rcn,N5e,Bcn,zcn,Fcn,Jcn,Hcn,Gcn,qcn,Ucn,Xcn,Kcn,Vcn,Ycn,Qcn,Wcn,Zcn,eun,nun,tun,D5e,iun,run,I5e,cun,uun,oun,sun,lun,fun,aun,hun,dun,_5e,bun,gun,wun,pun,L5e,mun,vun;v(Cu,"LayeredMetaDataProvider",843),m(982,1,qa,OU),s.tf=function(n){rYe(n)};var Ch,xie,uH,hx,oH,P5e,sH,dx,lD,Aie,Iy,$5e,R5e,B5e,bx,yun,gx,gm,Mie,lH,Tie,u1,Cie,A7,z5e,fD,Oie,F5e,kun,jun,Eun,fH,Nie,wx,_y,Sun,pl,J5e,H5e,aH,Rv,Oh,hH,Y1,G5e,q5e,U5e,Die,Iie,X5e,Gd,_ie,K5e,wm,V5e,Y5e,Q5e,dH,pm,kg,W5e,Z5e,Wc,e4e,xun,yu,px,n4e,t4e,i4e,aD,bH,gH,Lie,Pie,r4e,wH,c4e,u4e,pH,bp,o4e,$ie,mx,s4e,gp,vx,mH,jg,Rie,M7,vH,Eg,l4e,f4e,a4e,mm,h4e,Aun,Mun,Tun,Cun,wp,vm,Wi,qd,Oun,ym,d4e,T7,b4e,km,Nun,C7,g4e,Ly,Dun,Iun,hD,Bie,w4e,dD,Vf,jm,Bv,Sg,ub,yH,Em,zie,O7,N7,xg,Sm,Fie,bD,yx,kx,_un,Lun,Pun,p4e,$un,Jie,m4e,v4e,y4e,k4e,Hie,j4e,E4e,S4e,x4e,Gie,kH;v(Cu,"LayeredOptions",982),m(983,1,{},Yq),s.uf=function(){var n;return n=new ixe,n},s.vf=function(n){},v(Cu,"LayeredOptions/LayeredFactory",983),m(1345,1,{}),s.a=0;var Run;v(Ru,"ElkSpacings/AbstractSpacingsBuilder",1345),m(778,1345,{},tde);var jH,Bun;v(Cu,"LayeredSpacings/LayeredSpacingsBuilder",778),m(268,23,{3:1,35:1,23:1,268:1,188:1,196:1},M3),s.bg=function(){return yXe(this)},s.og=function(){return yXe(this)};var qie,Uie,Xie,A4e,M4e,T4e,EH,Kie,C4e,O4e=yt(Cu,"LayeringStrategy",268,Ct,B8n,umn),zun;m(352,23,{3:1,35:1,23:1,352:1},eK);var Vie,N4e,SH,D4e=yt(Cu,"LongEdgeOrderingStrategy",352,Ct,H4n,omn),Fun;m(203,23,{3:1,35:1,23:1,203:1},d$);var zv,Fv,xH,Yie,Qie=yt(Cu,"NodeFlexibility",203,Ct,t6n,smn),Jun;m(328,23,{3:1,35:1,23:1,328:1,188:1,196:1},lC),s.bg=function(){return sUe(this)},s.og=function(){return sUe(this)};var jx,Wie,Zie,Ex,I4e,_4e=yt(Cu,"NodePlacementStrategy",328,Ct,V6n,lmn),Hun;m(243,23,{3:1,35:1,23:1,243:1},u2);var L4e,D7,Sx,gD,P4e,$4e,wD,R4e,AH,MH,B4e=yt(Cu,"NodePromotionStrategy",243,Ct,f7n,fmn),Gun;m(269,23,{3:1,35:1,23:1,269:1},b$);var z4e,ob,ere,nre,F4e=yt(Cu,"OrderingStrategy",269,Ct,i6n,amn),qun;m(421,23,{3:1,35:1,23:1,421:1},mse);var tre,ire,J4e=yt(Cu,"PortSortingStrategy",421,Ct,Z5n,hmn),Uun;m(452,23,{3:1,35:1,23:1,452:1},nK);var ys,Do,xx,Xun=yt(Cu,"PortType",452,Ct,G4n,dmn),Kun;m(381,23,{3:1,35:1,23:1,381:1},tK);var H4e,rre,G4e,q4e=yt(Cu,"SelfLoopDistributionStrategy",381,Ct,q4n,bmn),Vun;m(348,23,{3:1,35:1,23:1,348:1},iK);var cre,pD,ure,U4e=yt(Cu,"SelfLoopOrderingStrategy",348,Ct,U4n,gmn),Yun;m(316,1,{316:1},tVe),v(Cu,"Spacings",316),m(349,23,{3:1,35:1,23:1,349:1},rK);var ore,X4e,Ax,K4e=yt(Cu,"SplineRoutingMode",349,Ct,X4n,wmn),Qun;m(351,23,{3:1,35:1,23:1,351:1},cK);var sre,V4e,Y4e,Q4e=yt(Cu,"ValidifyStrategy",351,Ct,K4n,pmn),Wun;m(382,23,{3:1,35:1,23:1,382:1},uK);var xm,lre,I7,W4e=yt(Cu,"WrappingStrategy",382,Ct,V4n,mmn),Zun;m(1361,1,uc,jT),s.pg=function(n){return u(n,37),eon},s.If=function(n,t){RPn(this,u(n,37),t)};var eon;v(Zw,"BFSNodeOrderCycleBreaker",1361),m(1359,1,uc,iP),s.pg=function(n){return u(n,37),non},s.If=function(n,t){ILn(this,u(n,37),t)};var non;v(Zw,"DFSNodeOrderCycleBreaker",1359),m(1360,1,it,$Ne),s.Ad=function(n){LIn(this.a,this.c,this.b,u(n,17))},s.b=!1,v(Zw,"DFSNodeOrderCycleBreaker/lambda$0$Type",1360),m(1353,1,uc,Q6),s.pg=function(n){return u(n,37),ton},s.If=function(n,t){DLn(this,u(n,37),t)};var ton;v(Zw,"DepthFirstCycleBreaker",1353),m(779,1,uc,Afe),s.pg=function(n){return u(n,37),ion},s.If=function(n,t){ZRn(this,u(n,37),t)},s.qg=function(n){return u(Le(n,sz(this.e,n.c.length)),9)};var ion;v(Zw,"GreedyCycleBreaker",779),m(1356,779,uc,STe),s.qg=function(n){var t,i,r,c,o,l,f,h,b;for(b=null,r=oi,h=k.Math.max(this.b.a.c.length,u(T(this.b,(me(),cb)),15).a),t=h*u(T(this.b,cD),15).a,c=new S6,i=ue(T(this.b,(Ne(),Iy)))===ue((_0(),dm)),f=new L(n);f.ao&&(r=o,b=l));return b||u(Le(n,sz(this.e,n.c.length)),9)},v(Zw,"GreedyModelOrderCycleBreaker",1356),m(505,1,{},S6),s.a=0,s.b=0,v(Zw,"GroupModelOrderCalculator",505),m(1354,1,uc,nj),s.pg=function(n){return u(n,37),ron},s.If=function(n,t){cPn(this,u(n,37),t)};var ron;v(Zw,"InteractiveCycleBreaker",1354),m(1355,1,uc,ej),s.pg=function(n){return u(n,37),con},s.If=function(n,t){oPn(u(n,37),t)};var con;v(Zw,"ModelOrderCycleBreaker",1355),m(780,1,uc),s.pg=function(n){return u(n,37),uon},s.If=function(n,t){V_n(this,u(n,37),t)},s.rg=function(n,t){var i,r,c,o,l,f,h,b,p,y;for(l=0;lb&&(h=S,y=b),paa(new qn(Vn(Ni(f).a.Jc(),new ee))))for(c=new qn(Vn(rr(h).a.Jc(),new ee));ht(c);)r=u(tt(c),17),u(Yu(this.d,l),22).Gc(r.c.i)&&Te(this.c,r);else for(c=new qn(Vn(Ni(f).a.Jc(),new ee));ht(c);)r=u(tt(c),17),u(Yu(this.d,l),22).Gc(r.d.i)&&Te(this.c,r)}},v(Zw,"SCCNodeTypeCycleBreaker",1358),m(1357,780,uc,ATe),s.rg=function(n,t){var i,r,c,o,l,f,h,b,p,y,S,A;for(l=0;lb&&(h=S,y=b),paa(new qn(Vn(Ni(f).a.Jc(),new ee))))for(c=new qn(Vn(rr(h).a.Jc(),new ee));ht(c);)r=u(tt(c),17),u(Yu(this.d,l),22).Gc(r.c.i)&&Te(this.c,r);else for(c=new qn(Vn(Ni(f).a.Jc(),new ee));ht(c);)r=u(tt(c),17),u(Yu(this.d,l),22).Gc(r.d.i)&&Te(this.c,r)}},v(Zw,"SCConnectivity",1357),m(1373,1,uc,kT),s.pg=function(n){return u(n,37),oon},s.If=function(n,t){iRn(this,u(n,37),t)};var oon;v(U1,"BreadthFirstModelOrderLayerer",1373),m(1374,1,Yt,sM),s.Le=function(n,t){return JTn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"BreadthFirstModelOrderLayerer/lambda$0$Type",1374),m(1364,1,uc,CMe),s.pg=function(n){return u(n,37),son},s.If=function(n,t){rBn(this,u(n,37),t)};var son;v(U1,"CoffmanGrahamLayerer",1364),m(1365,1,Yt,Wje),s.Le=function(n,t){return VNn(this.a,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"CoffmanGrahamLayerer/0methodref$compareNodesInTopo$Type",1365),m(1366,1,Yt,Zje),s.Le=function(n,t){return fvn(this.a,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"CoffmanGrahamLayerer/lambda$1$Type",1366),m(1375,1,uc,yT),s.pg=function(n){return u(n,37),lon},s.If=function(n,t){GRn(this,u(n,37),t)},s.c=0,s.e=0;var lon;v(U1,"DepthFirstModelOrderLayerer",1375),m(1376,1,Yt,x6),s.Le=function(n,t){return HTn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"DepthFirstModelOrderLayerer/lambda$0$Type",1376),m(1367,1,uc,A6),s.pg=function(n){return u(n,37),qt(qt(qt(new or,(zr(),Kf),(Ur(),Ite)),r1,fm),eo,lm)},s.If=function(n,t){bRn(u(n,37),t)},v(U1,"InteractiveLayerer",1367),m(564,1,{564:1},axe),s.a=0,s.c=0,v(U1,"InteractiveLayerer/LayerSpan",564),m(1363,1,uc,cP),s.pg=function(n){return u(n,37),fon},s.If=function(n,t){HNn(this,u(n,37),t)};var fon;v(U1,"LongestPathLayerer",1363),m(1372,1,uc,uP),s.pg=function(n){return u(n,37),aon},s.If=function(n,t){fDn(this,u(n,37),t)};var aon;v(U1,"LongestPathSourceLayerer",1372),m(1370,1,uc,ko),s.pg=function(n){return u(n,37),qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)},s.If=function(n,t){SRn(this,u(n,37),t)},s.a=0,s.b=0,s.d=0;var Z4e,eye;v(U1,"MinWidthLayerer",1370),m(1371,1,Yt,eEe),s.Le=function(n,t){return N7n(this,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"MinWidthLayerer/MinOutgoingEdgesComparator",1371),m(1362,1,uc,rP),s.pg=function(n){return u(n,37),hon},s.If=function(n,t){FPn(this,u(n,37),t)};var hon;v(U1,"NetworkSimplexLayerer",1362),m(1368,1,uc,rNe),s.pg=function(n){return u(n,37),qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)},s.If=function(n,t){A$n(this,u(n,37),t)},s.d=0,s.f=0,s.g=0,s.i=0,s.s=0,s.t=0,s.u=0,v(U1,"StretchWidthLayerer",1368),m(1369,1,Yt,Wq),s.Le=function(n,t){return a9n(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"StretchWidthLayerer/1",1369),m(406,1,Bpe),s.eg=function(n,t,i,r,c,o){},s.tg=function(n,t,i){return YXe(this,n,t,i)},s.dg=function(){this.g=oe(Hm,JQe,30,this.d,15,1),this.f=oe(Hm,JQe,30,this.d,15,1)},s.fg=function(n,t){this.e[n]=oe($t,ni,30,t[n].length,15,1)},s.gg=function(n,t,i){var r;r=i[n][t],r.p=t,this.e[n][t]=t},s.hg=function(n,t,i,r){u(Le(r[n][t].j,i),12).p=this.d++},s.b=0,s.c=0,s.d=0,v(Ro,"AbstractBarycenterPortDistributor",406),m(1663,1,Yt,nEe),s.Le=function(n,t){return BEn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ro,"AbstractBarycenterPortDistributor/lambda$0$Type",1663),m(816,1,AS,Iae),s.eg=function(n,t,i,r,c,o){},s.gg=function(n,t,i){},s.hg=function(n,t,i,r){},s.cg=function(){return!1},s.dg=function(){this.c=this.e.a,this.g=this.f.g},s.fg=function(n,t){t[n][0].c.p=n},s.ig=function(){return!1},s.ug=function(n,t,i,r){i?FHe(this,n):(XHe(this,n,r),dVe(this,n,t)),n.c.length>1&&(Re($e(T(_r((vn(0,n.c.length),u(n.c[0],9))),(Ne(),A7))))?vUe(n,this.d,u(this,660)):(jn(),Cr(n,this.d)),lze(this.e,n))},s.jg=function(n,t,i,r){var c,o,l,f,h,b,p;for(t!=SDe(i,n.length)&&(o=n[t-(i?1:-1)],rhe(this.f,o,i?(Nc(),Do):(Nc(),ys))),c=n[t][0],p=!r||c.k==(zn(),wr),b=$f(n[t]),this.ug(b,p,!1,i),l=0,h=new L(b);h.a"),n0?HV(this.a,n[t-1],n[t]):!i&&t1&&(Re($e(T(_r((vn(0,n.c.length),u(n.c[0],9))),(Ne(),A7))))?vUe(n,this.d,this):(jn(),Cr(n,this.d)),Re($e(T(_r((vn(0,n.c.length),u(n.c[0],9))),A7)))||lze(this.e,n))},v(Ro,"ModelOrderBarycenterHeuristic",660),m(1843,1,Yt,lEe),s.Le=function(n,t){return jLn(this.a,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ro,"ModelOrderBarycenterHeuristic/lambda$0$Type",1843),m(1383,1,uc,sP),s.pg=function(n){var t;return u(n,37),t=I$(yon),qt(t,(zr(),eo),(Ur(),LJ)),t},s.If=function(n,t){R5n((u(n,37),t))};var yon;v(Ro,"NoCrossingMinimizer",1383),m(796,406,Bpe,Roe),s.sg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,A;switch(y=this.g,i.g){case 1:{for(c=0,o=0,p=new L(n.j);p.a1&&(c.j==(De(),et)?this.b[n]=!0:c.j==Kn&&n>0&&(this.b[n-1]=!0))},s.f=0,v(t1,"AllCrossingsCounter",1838),m(583,1,{},AB),s.b=0,s.d=0,v(t1,"BinaryIndexedTree",583),m(519,1,{},CC);var nye,OH;v(t1,"CrossingsCounter",519),m(1912,1,Yt,fEe),s.Le=function(n,t){return evn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(t1,"CrossingsCounter/lambda$0$Type",1912),m(1913,1,Yt,aEe),s.Le=function(n,t){return nvn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(t1,"CrossingsCounter/lambda$1$Type",1913),m(1914,1,Yt,hEe),s.Le=function(n,t){return tvn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(t1,"CrossingsCounter/lambda$2$Type",1914),m(1915,1,Yt,dEe),s.Le=function(n,t){return ivn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(t1,"CrossingsCounter/lambda$3$Type",1915),m(1916,1,it,bEe),s.Ad=function(n){K9n(this.a,u(n,12))},v(t1,"CrossingsCounter/lambda$4$Type",1916),m(1917,1,Ft,gEe),s.Mb=function(n){return Fgn(this.a,u(n,12))},v(t1,"CrossingsCounter/lambda$5$Type",1917),m(1918,1,it,wEe),s.Ad=function(n){eCe(this,n)},v(t1,"CrossingsCounter/lambda$6$Type",1918),m(1919,1,it,cTe),s.Ad=function(n){var t;A9(),C0(this.b,(t=this.a,u(n,12),t))},v(t1,"CrossingsCounter/lambda$7$Type",1919),m(823,1,jh,hM),s.Lb=function(n){return A9(),bi(u(n,12),(me(),vs))},s.Fb=function(n){return this===n},s.Mb=function(n){return A9(),bi(u(n,12),(me(),vs))},v(t1,"CrossingsCounter/lambda$8$Type",823),m(1911,1,{},pEe),v(t1,"HyperedgeCrossingsCounter",1911),m(467,1,{35:1,467:1},cNe),s.Dd=function(n){return TEn(this,u(n,467))},s.b=0,s.c=0,s.e=0,s.f=0;var BBn=v(t1,"HyperedgeCrossingsCounter/Hyperedge",467);m(370,1,{35:1,370:1},AR),s.Dd=function(n){return kOn(this,u(n,370))},s.b=0,s.c=0;var kon=v(t1,"HyperedgeCrossingsCounter/HyperedgeCorner",370);m(518,23,{3:1,35:1,23:1,518:1},vse);var Tx,Cx,jon=yt(t1,"HyperedgeCrossingsCounter/HyperedgeCorner/Type",518,Ct,e4n,jmn),Eon;m(1385,1,uc,CU),s.pg=function(n){return u(T(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?Son:null},s.If=function(n,t){Qxn(this,u(n,37),t)};var Son;v(Lc,"InteractiveNodePlacer",1385),m(1386,1,uc,AT),s.pg=function(n){return u(T(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?xon:null},s.If=function(n,t){$Sn(this,u(n,37),t)};var xon,NH,DH;v(Lc,"LinearSegmentsNodePlacer",1386),m(263,1,{35:1,263:1},poe),s.Dd=function(n){return ngn(this,u(n,263))},s.Fb=function(n){var t;return X(n,263)?(t=u(n,263),this.b==t.b):!1},s.Hb=function(){return this.b},s.Ib=function(){return"ls"+Fa(this.e)},s.a=0,s.b=0,s.c=-1,s.d=-1,s.g=0;var Aon=v(Lc,"LinearSegmentsNodePlacer/LinearSegment",263);m(1388,1,uc,_De),s.pg=function(n){return u(T(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?Mon:null},s.If=function(n,t){qRn(this,u(n,37),t)},s.b=0,s.g=0;var Mon;v(Lc,"NetworkSimplexPlacer",1388),m(1407,1,Yt,f3),s.Le=function(n,t){return oo(u(n,15).a,u(t,15).a)},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Lc,"NetworkSimplexPlacer/0methodref$compare$Type",1407),m(1409,1,Yt,fM),s.Le=function(n,t){return oo(u(n,15).a,u(t,15).a)},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Lc,"NetworkSimplexPlacer/1methodref$compare$Type",1409),m(644,1,{644:1},uTe);var zBn=v(Lc,"NetworkSimplexPlacer/EdgeRep",644);m(405,1,{405:1},cae),s.b=!1;var FBn=v(Lc,"NetworkSimplexPlacer/NodeRep",405);m(500,13,{3:1,4:1,20:1,31:1,56:1,13:1,18:1,16:1,59:1,500:1},pxe),v(Lc,"NetworkSimplexPlacer/Path",500),m(1389,1,{},kk),s.Kb=function(n){return u(n,17).d.i.k},v(Lc,"NetworkSimplexPlacer/Path/lambda$0$Type",1389),m(1390,1,Ft,o_),s.Mb=function(n){return u(n,249)==(zn(),dr)},v(Lc,"NetworkSimplexPlacer/Path/lambda$1$Type",1390),m(1391,1,{},s_),s.Kb=function(n){return u(n,17).d.i},v(Lc,"NetworkSimplexPlacer/Path/lambda$2$Type",1391),m(1392,1,Ft,mEe),s.Mb=function(n){return qOe(rJe(u(n,9)))},v(Lc,"NetworkSimplexPlacer/Path/lambda$3$Type",1392),m(1393,1,Ft,I5),s.Mb=function(n){return F3n(u(n,12))},v(Lc,"NetworkSimplexPlacer/lambda$0$Type",1393),m(1394,1,it,oTe),s.Ad=function(n){Iwn(this.a,this.b,u(n,12))},v(Lc,"NetworkSimplexPlacer/lambda$1$Type",1394),m(1403,1,it,vEe),s.Ad=function(n){sCn(this.a,u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$10$Type",1403),m(1404,1,{},aM),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$11$Type",1404),m(1405,1,it,yEe),s.Ad=function(n){JDn(this.a,u(n,9))},v(Lc,"NetworkSimplexPlacer/lambda$12$Type",1405),m(1406,1,{},jk),s.Kb=function(n){return cl(),ve(u(n,124).e)},v(Lc,"NetworkSimplexPlacer/lambda$13$Type",1406),m(1408,1,{},Ek),s.Kb=function(n){return cl(),ve(u(n,124).e)},v(Lc,"NetworkSimplexPlacer/lambda$15$Type",1408),m(1410,1,Ft,l_),s.Mb=function(n){return cl(),u(n,405).c.k==(zn(),Qi)},v(Lc,"NetworkSimplexPlacer/lambda$17$Type",1410),m(1411,1,Ft,f_),s.Mb=function(n){return cl(),u(n,405).c.j.c.length>1},v(Lc,"NetworkSimplexPlacer/lambda$18$Type",1411),m(1412,1,it,FIe),s.Ad=function(n){Wjn(this.c,this.b,this.d,this.a,u(n,405))},s.c=0,s.d=0,v(Lc,"NetworkSimplexPlacer/lambda$19$Type",1412),m(1395,1,{},a3),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$2$Type",1395),m(1413,1,it,kEe),s.Ad=function(n){$wn(this.a,u(n,12))},s.a=0,v(Lc,"NetworkSimplexPlacer/lambda$20$Type",1413),m(1414,1,{},h3),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$21$Type",1414),m(1415,1,it,jEe),s.Ad=function(n){Hwn(this.a,u(n,9))},v(Lc,"NetworkSimplexPlacer/lambda$22$Type",1415),m(1416,1,Ft,a_),s.Mb=function(n){return qOe(n)},v(Lc,"NetworkSimplexPlacer/lambda$23$Type",1416),m(1417,1,{},_5),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$24$Type",1417),m(1418,1,Ft,EEe),s.Mb=function(n){return Ygn(this.a,u(n,9))},v(Lc,"NetworkSimplexPlacer/lambda$25$Type",1418),m(1419,1,it,sTe),s.Ad=function(n){aTn(this.a,this.b,u(n,9))},v(Lc,"NetworkSimplexPlacer/lambda$26$Type",1419),m(1420,1,Ft,M6),s.Mb=function(n){return cl(),!cc(u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$27$Type",1420),m(1421,1,Ft,Sk),s.Mb=function(n){return cl(),!cc(u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$28$Type",1421),m(1422,1,{},SEe),s.Te=function(n,t){return Pwn(this.a,u(n,25),u(t,25))},v(Lc,"NetworkSimplexPlacer/lambda$29$Type",1422),m(1396,1,{},T6),s.Kb=function(n){return cl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(Lc,"NetworkSimplexPlacer/lambda$3$Type",1396),m(1397,1,Ft,xk),s.Mb=function(n){return cl(),Ryn(u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$4$Type",1397),m(1398,1,it,xEe),s.Ad=function(n){Q_n(this.a,u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$5$Type",1398),m(1399,1,{},h_),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$6$Type",1399),m(1400,1,Ft,d3),s.Mb=function(n){return cl(),u(n,9).k==(zn(),Qi)},v(Lc,"NetworkSimplexPlacer/lambda$7$Type",1400),m(1401,1,{},d_),s.Kb=function(n){return cl(),new wn(null,new v2(new qn(Vn(bh(u(n,9)).a.Jc(),new ee))))},v(Lc,"NetworkSimplexPlacer/lambda$8$Type",1401),m(1402,1,Ft,Rp),s.Mb=function(n){return cl(),B3n(u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$9$Type",1402),m(1384,1,uc,MT),s.pg=function(n){return u(T(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?Ton:null},s.If=function(n,t){CLn(u(n,37),t)};var Ton;v(Lc,"SimpleNodePlacer",1384),m(185,1,{185:1},lv),s.Ib=function(){var n;return n="",this.c==(ah(),pp)?n+=fy:this.c==Ud&&(n+=ly),this.o==(Da(),Ag)?n+=XZ:this.o==Ya?n+="UP":n+="BALANCED",n},v(Q0,"BKAlignedLayout",185),m(509,23,{3:1,35:1,23:1,509:1},yse);var Ud,pp,Con=yt(Q0,"BKAlignedLayout/HDirection",509,Ct,t4n,Emn),Oon;m(508,23,{3:1,35:1,23:1,508:1},kse);var Ag,Ya,Non=yt(Q0,"BKAlignedLayout/VDirection",508,Ct,n4n,Smn),Don;m(1664,1,{},lTe),v(Q0,"BKAligner",1664),m(1667,1,{},OHe),v(Q0,"BKCompactor",1667),m(652,1,{652:1},dM),s.a=0,v(Q0,"BKCompactor/ClassEdge",652),m(456,1,{456:1},dxe),s.a=null,s.b=0,v(Q0,"BKCompactor/ClassNode",456),m(1387,1,uc,ETe),s.pg=function(n){return u(T(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?Ion:null},s.If=function(n,t){sBn(this,u(n,37),t)},s.d=!1;var Ion;v(Q0,"BKNodePlacer",1387),m(1665,1,{},bM),s.d=0,v(Q0,"NeighborhoodInformation",1665),m(1666,1,Yt,AEe),s.Le=function(n,t){return l8n(this,u(n,49),u(t,49))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Q0,"NeighborhoodInformation/NeighborComparator",1666),m(809,1,{}),v(Q0,"ThresholdStrategy",809),m(1795,809,{},mxe),s.vg=function(n,t,i){return this.a.o==(Da(),Ya)?Ki:Dr},s.wg=function(){},v(Q0,"ThresholdStrategy/NullThresholdStrategy",1795),m(576,1,{576:1},hTe),s.c=!1,s.d=!1,v(Q0,"ThresholdStrategy/Postprocessable",576),m(1796,809,{},vxe),s.vg=function(n,t,i){var r,c,o;return c=t==i,r=this.a.a[i.p]==t,c||r?(o=n,this.a.c==(ah(),pp)?(c&&(o=VW(this,t,!0)),!isNaN(o)&&!isFinite(o)&&r&&(o=VW(this,i,!1))):(c&&(o=VW(this,t,!0)),!isNaN(o)&&!isFinite(o)&&r&&(o=VW(this,i,!1))),o):n},s.wg=function(){for(var n,t,i,r,c;this.d.b!=0;)c=u(A_e(this.d),576),r=hKe(this,c),r.a&&(n=r.a,i=Re(this.a.f[this.a.g[c.b.p].p]),!(!i&&!cc(n)&&n.c.i.c==n.d.i.c)&&(t=bUe(this,c),t||vCe(this.e,c)));for(;this.e.a.c.length!=0;)bUe(this,u(C1e(this.e),576))},v(Q0,"ThresholdStrategy/SimpleThresholdStrategy",1796),m(635,1,{635:1,188:1,196:1},Bp),s.bg=function(){return sze(this)},s.og=function(){return sze(this)};var fre;v(qee,"EdgeRouterFactory",635),m(1445,1,uc,_U),s.pg=function(n){return vDn(u(n,37))},s.If=function(n,t){RLn(u(n,37),t)};var _on,Lon,Pon,$on,Ron,tye,Bon,zon;v(qee,"OrthogonalEdgeRouter",1445),m(1438,1,uc,jTe),s.pg=function(n){return uAn(u(n,37))},s.If=function(n,t){oRn(this,u(n,37),t)};var Fon,Jon,Hon,Gon,vD,qon;v(qee,"PolylineEdgeRouter",1438),m(1439,1,jh,zp),s.Lb=function(n){return u1e(u(n,9))},s.Fb=function(n){return this===n},s.Mb=function(n){return u1e(u(n,9))},v(qee,"PolylineEdgeRouter/1",1439),m(1851,1,Ft,C6),s.Mb=function(n){return u(n,133).c==(ha(),sb)},v(va,"HyperEdgeCycleDetector/lambda$0$Type",1851),m(1852,1,{},gM),s.Xe=function(n){return u(n,133).d},v(va,"HyperEdgeCycleDetector/lambda$1$Type",1852),m(1853,1,Ft,wM),s.Mb=function(n){return u(n,133).c==(ha(),sb)},v(va,"HyperEdgeCycleDetector/lambda$2$Type",1853),m(1854,1,{},O6),s.Xe=function(n){return u(n,133).d},v(va,"HyperEdgeCycleDetector/lambda$3$Type",1854),m(1855,1,{},N6),s.Xe=function(n){return u(n,133).d},v(va,"HyperEdgeCycleDetector/lambda$4$Type",1855),m(1856,1,{},b_),s.Xe=function(n){return u(n,133).d},v(va,"HyperEdgeCycleDetector/lambda$5$Type",1856),m(116,1,{35:1,116:1},mO),s.Dd=function(n){return tgn(this,u(n,116))},s.Fb=function(n){var t;return X(n,116)?(t=u(n,116),this.g==t.g):!1},s.Hb=function(){return this.g},s.Ib=function(){var n,t,i,r;for(n=new il("{"),r=new L(this.n);r.a"+this.b+" ("+vpn(this.c)+")"},s.d=0,v(va,"HyperEdgeSegmentDependency",133),m(515,23,{3:1,35:1,23:1,515:1},jse);var sb,Am,Uon=yt(va,"HyperEdgeSegmentDependency/DependencyType",515,Ct,i4n,xmn),Xon;m(1857,1,{},MEe),v(va,"HyperEdgeSegmentSplitter",1857),m(1858,1,{},dAe),s.a=0,s.b=0,v(va,"HyperEdgeSegmentSplitter/AreaRating",1858),m(340,1,{340:1},QK),s.a=0,s.b=0,s.c=0,v(va,"HyperEdgeSegmentSplitter/FreeArea",340),m(1859,1,Yt,g_),s.Le=function(n,t){return a2n(u(n,116),u(t,116))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(va,"HyperEdgeSegmentSplitter/lambda$0$Type",1859),m(1860,1,it,JIe),s.Ad=function(n){T6n(this.a,this.d,this.c,this.b,u(n,116))},s.b=0,v(va,"HyperEdgeSegmentSplitter/lambda$1$Type",1860),m(1861,1,{},L5),s.Kb=function(n){return new wn(null,new pn(u(n,116).e,16))},v(va,"HyperEdgeSegmentSplitter/lambda$2$Type",1861),m(1862,1,{},Ak),s.Kb=function(n){return new wn(null,new pn(u(n,116).j,16))},v(va,"HyperEdgeSegmentSplitter/lambda$3$Type",1862),m(1863,1,{},w_),s.We=function(n){return ne(re(n))},v(va,"HyperEdgeSegmentSplitter/lambda$4$Type",1863),m(653,1,{},jV),s.a=0,s.b=0,s.c=0,v(va,"OrthogonalRoutingGenerator",653),m(1668,1,{},pM),s.Kb=function(n){return new wn(null,new pn(u(n,116).e,16))},v(va,"OrthogonalRoutingGenerator/lambda$0$Type",1668),m(1669,1,{},mM),s.Kb=function(n){return new wn(null,new pn(u(n,116).j,16))},v(va,"OrthogonalRoutingGenerator/lambda$1$Type",1669),m(661,1,{}),v(Uee,"BaseRoutingDirectionStrategy",661),m(1849,661,{},yxe),s.xg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I;if(!(n.r&&!n.q))for(p=t+n.o*i,b=new L(n.n);b.aEh&&(o=p,c=n,r=new je(y,o),Vt(l.a,r),Uw(this,l,c,r,!1),S=n.r,S&&(A=ne(re(Yu(S.e,0))),r=new je(A,o),Vt(l.a,r),Uw(this,l,c,r,!1),o=t+S.o*i,c=S,r=new je(A,o),Vt(l.a,r),Uw(this,l,c,r,!1)),r=new je(I,o),Vt(l.a,r),Uw(this,l,c,r,!1)))},s.yg=function(n){return n.i.n.a+n.n.a+n.a.a},s.zg=function(){return De(),bt},s.Ag=function(){return De(),Xn},v(Uee,"NorthToSouthRoutingStrategy",1849),m(1850,661,{},kxe),s.xg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I;if(!(n.r&&!n.q))for(p=t-n.o*i,b=new L(n.n);b.aEh&&(o=p,c=n,r=new je(y,o),Vt(l.a,r),Uw(this,l,c,r,!1),S=n.r,S&&(A=ne(re(Yu(S.e,0))),r=new je(A,o),Vt(l.a,r),Uw(this,l,c,r,!1),o=t-S.o*i,c=S,r=new je(A,o),Vt(l.a,r),Uw(this,l,c,r,!1)),r=new je(I,o),Vt(l.a,r),Uw(this,l,c,r,!1)))},s.yg=function(n){return n.i.n.a+n.n.a+n.a.a},s.zg=function(){return De(),Xn},s.Ag=function(){return De(),bt},v(Uee,"SouthToNorthRoutingStrategy",1850),m(1848,661,{},jxe),s.xg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I;if(!(n.r&&!n.q))for(p=t+n.o*i,b=new L(n.n);b.aEh&&(o=p,c=n,r=new je(o,y),Vt(l.a,r),Uw(this,l,c,r,!0),S=n.r,S&&(A=ne(re(Yu(S.e,0))),r=new je(o,A),Vt(l.a,r),Uw(this,l,c,r,!0),o=t+S.o*i,c=S,r=new je(o,A),Vt(l.a,r),Uw(this,l,c,r,!0)),r=new je(o,I),Vt(l.a,r),Uw(this,l,c,r,!0)))},s.yg=function(n){return n.i.n.b+n.n.b+n.a.b},s.zg=function(){return De(),et},s.Ag=function(){return De(),Kn},v(Uee,"WestToEastRoutingStrategy",1848),m(812,1,{},fge),s.Ib=function(){return Fa(this.a)},s.b=0,s.c=!1,s.d=!1,s.f=0,v(tm,"NubSpline",812),m(410,1,{410:1},VUe,S_e),v(tm,"NubSpline/PolarCP",410),m(1440,1,uc,yHe),s.pg=function(n){return XAn(u(n,37))},s.If=function(n,t){MRn(this,u(n,37),t)};var Kon,Von,Yon,Qon,Won;v(tm,"SplineEdgeRouter",1440),m(273,1,{273:1},QR),s.Ib=function(){return this.a+" ->("+this.c+") "+this.b},s.c=0,v(tm,"SplineEdgeRouter/Dependency",273),m(454,23,{3:1,35:1,23:1,454:1},Ese);var lb,Jv,Zon=yt(tm,"SplineEdgeRouter/SideToProcess",454,Ct,r4n,Amn),esn;m(1441,1,Ft,p1),s.Mb=function(n){return iS(),!u(n,132).o},v(tm,"SplineEdgeRouter/lambda$0$Type",1441),m(1442,1,{},hd),s.Xe=function(n){return iS(),u(n,132).v+1},v(tm,"SplineEdgeRouter/lambda$1$Type",1442),m(1443,1,it,fTe),s.Ad=function(n){G3n(this.a,this.b,u(n,49))},v(tm,"SplineEdgeRouter/lambda$2$Type",1443),m(1444,1,it,aTe),s.Ad=function(n){q3n(this.a,this.b,u(n,49))},v(tm,"SplineEdgeRouter/lambda$3$Type",1444),m(132,1,{35:1,132:1},iqe,wge),s.Dd=function(n){return ign(this,u(n,132))},s.b=0,s.e=!1,s.f=0,s.g=0,s.j=!1,s.k=!1,s.n=0,s.o=!1,s.p=!1,s.q=!1,s.s=0,s.u=0,s.v=0,s.F=0,v(tm,"SplineSegment",132),m(457,1,{457:1},Fp),s.a=0,s.b=!1,s.c=!1,s.d=!1,s.e=!1,s.f=0,v(tm,"SplineSegment/EdgeInformation",457),m(1167,1,{},vM),v(X1,twe,1167),m(1168,1,Yt,p_),s.Le=function(n,t){return kCn(u(n,120),u(t,120))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(X1,ZYe,1168),m(1166,1,{},_Ae),v(X1,"MrTree",1166),m(398,23,{3:1,35:1,23:1,398:1,188:1,196:1},w$),s.bg=function(){return Aqe(this)},s.og=function(){return Aqe(this)};var IH,Ox,Nx,Dx,iye=yt(X1,"TreeLayoutPhases",398,Ct,u6n,Mmn),nsn;m(1082,214,Qw,oNe),s.kf=function(n,t){var i,r,c,o,l,f,h,b;for(Re($e(ye(n,(Mu(),Tye))))||HC((i=new hj((_b(),new w0(n))),i)),l=t.dh(Vee),l.Tg("build tGraph",1),f=(h=new QC,$u(h,n),he(h,(Ti(),_x),n),b=new pt,i_n(n,h,b),y_n(n,h,b),h),l.Ug(),l=t.dh(Vee),l.Tg("Split graph",1),o=l_n(this.a,f),l.Ug(),c=new L(o);c.a"+Ub(this.c):"e_"+Oi(this)},v(OS,"TEdge",65),m(120,150,{3:1,120:1,105:1,150:1},QC),s.Ib=function(){var n,t,i,r,c;for(c=null,r=St(this.b,0);r.b!=r.d.c;)i=u(jt(r),40),c+=(i.c==null||i.c.length==0?"n_"+i.g:"n_"+i.c)+` + endInLayerEdge=`,uo(n,this.c),n.a},v(Sh,"BreakingPointInserter/BPInfo",317),m(650,1,{650:1},Qje),s.a=!1,s.b=0,s.c=0,v(Sh,"BreakingPointInserter/Cut",650),m(1506,1,Ai,Pp),s.If=function(n,t){UOn(u(n,37),t)},v(Sh,"BreakingPointProcessor",1506),m(1507,1,Ft,nw),s.Mb=function(n){return zRe(u(n,9))},v(Sh,"BreakingPointProcessor/0methodref$isEnd$Type",1507),m(1508,1,Ft,cM),s.Mb=function(n){return FRe(u(n,9))},v(Sh,"BreakingPointProcessor/1methodref$isStart$Type",1508),m(1509,1,Ai,uM),s.If=function(n,t){gNn(this,u(n,37),t)},v(Sh,"BreakingPointRemover",1509),m(1510,1,it,O5),s.Ad=function(n){u(n,132).k=!0},v(Sh,"BreakingPointRemover/lambda$0$Type",1510),m(798,1,{},sbe),s.b=0,s.e=0,s.f=0,s.j=0,v(Sh,"GraphStats",798),m(799,1,{},j6),s.Te=function(n,t){return k.Math.max(ne(re(n)),ne(re(t)))},v(Sh,"GraphStats/0methodref$max$Type",799),m(800,1,{},$p),s.Te=function(n,t){return k.Math.max(ne(re(n)),ne(re(t)))},v(Sh,"GraphStats/2methodref$max$Type",800),m(1692,1,{},ca),s.Te=function(n,t){return kmn(re(n),re(t))},v(Sh,"GraphStats/lambda$1$Type",1692),m(1693,1,{},Vje),s.Kb=function(n){return LJe(this.a,u(n,25))},v(Sh,"GraphStats/lambda$2$Type",1693),m(1694,1,{},Yje),s.Kb=function(n){return LUe(this.a,u(n,25))},v(Sh,"GraphStats/lambda$6$Type",1694),m(801,1,{},E6),s.mg=function(n,t){var i;return i=u(C(n,(Ne(),y4e)),16),i||(jn(),jn(),Sc)},s.ng=function(){return!1},v(Sh,"ICutIndexCalculator/ManualCutIndexCalculator",801),m(803,1,{},oM),s.mg=function(n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be;for(be=(t.n==null&&sHe(t),t.n),h=(t.d==null&&sHe(t),t.d),te=oe(Jr,Jc,30,be.length,15,1),te[0]=be[0],q=be[0],b=1;b=I&&(Ce(o,ve(p)),V=k.Math.max(V,te[p-1]-y),f+=O,R+=te[p-1]-R,y=te[p-1],O=h[p]),O=k.Math.max(O,h[p]),++p;f+=O}A=k.Math.min(1/V,1/t.b/f),A>r&&(r=A,i=o)}return i},s.ng=function(){return!1},v(Sh,"MSDCutIndexHeuristic",803),m(1647,1,Ai,Vq),s.If=function(n,t){eLn(u(n,37),t)},v(Sh,"SingleEdgeGraphWrapper",1647),m(231,23,{3:1,35:1,23:1,231:1},_j);var Cv,m7,v7,hm,tx,Tv,y7=yt(Tu,"CenterEdgeLabelPlacementStrategy",231,Tt,S9n,J2n),Iin;m(422,23,{3:1,35:1,23:1,422:1},dse);var bve,Xte,gve=yt(Tu,"ConstraintCalculationStrategy",422,Tt,K5n,H2n),_in;m(301,23,{3:1,35:1,23:1,301:1,188:1,196:1},h$),s.bg=function(){return yUe(this)},s.og=function(){return yUe(this)};var eD,ix,wve,pve,mve=yt(Tu,"CrossingMinimizationStrategy",301,Tt,n6n,G2n),Lin;m(350,23,{3:1,35:1,23:1,350:1},KX);var vve,Kte,UJ,yve=yt(Tu,"CuttingStrategy",350,Tt,R4n,q2n),Pin;m(267,23,{3:1,35:1,23:1,267:1,188:1,196:1},A3),s.bg=function(){return SXe(this)},s.og=function(){return SXe(this)};var Vte,kve,Yte,Qte,Wte,Zte,eie,nie,nD,jve=yt(Tu,"CycleBreakingStrategy",267,Tt,R8n,U2n),$in;m(419,23,{3:1,35:1,23:1,419:1},bse);var XJ,Eve,Sve=yt(Tu,"DirectionCongruency",419,Tt,V5n,X2n),Rin;m(449,23,{3:1,35:1,23:1,449:1},YX);var k7,tie,Ov,Bin=yt(Tu,"EdgeConstraint",449,Tt,B4n,K2n),zin;m(284,23,{3:1,35:1,23:1,284:1},$j);var iie,rie,cie,uie,KJ,oie,xve=yt(Tu,"EdgeLabelSideSelection",284,Tt,x9n,V2n),Fin;m(476,23,{3:1,35:1,23:1,476:1},gse);var VJ,Ave,Mve=yt(Tu,"EdgeStraighteningStrategy",476,Tt,Y5n,Y2n),Jin;m(282,23,{3:1,35:1,23:1,282:1},Lj);var sie,Cve,Tve,YJ,Ove,Nve,Dve=yt(Tu,"FixedAlignment",282,Tt,A9n,Q2n),Hin;m(283,23,{3:1,35:1,23:1,283:1},Pj);var Ive,_ve,Lve,Pve,rx,$ve,Rve=yt(Tu,"GraphCompactionStrategy",283,Tt,M9n,W2n),Gin;m(261,23,{3:1,35:1,23:1,261:1},c2);var j7,QJ,E7,Kl,cx,WJ,S7,Nv,ZJ,ux,lie=yt(Tu,"GraphProperties",261,Tt,a7n,Z2n),qin;m(302,23,{3:1,35:1,23:1,302:1},QX);var tD,fie,aie,hie=yt(Tu,"GreedySwitchType",302,Tt,z4n,emn),Uin;m(329,23,{3:1,35:1,23:1,329:1},WX);var dm,Bve,iD,die=yt(Tu,"GroupOrderStrategy",329,Tt,F4n,nmn),Xin;m(315,23,{3:1,35:1,23:1,315:1},ZX);var Sy,rD,Dv,Kin=yt(Tu,"InLayerConstraint",315,Tt,J4n,tmn),Vin;m(420,23,{3:1,35:1,23:1,420:1},wse);var bie,zve,Fve=yt(Tu,"InteractiveReferencePoint",420,Tt,Q5n,imn),Yin,Jve,xy,fp,cD,eH,Hve,Gve,nH,qve,Ay,tH,ox,My,K1,gie,iH,Du,Uve,rb,po,wie,pie,uD,mg,ap,Cy,Xve,Qin,Ty,oD,bm,ja,gf,mie,Iv,cb,Ti,wi,Kve,Vve,Yve,Qve,Wve,vie,rH,vs,hp,yie,Oy,sx,Hd,_v,dp,Lv,Pv,x7,vg,Zve,kie,jie,lx,Ny,cH,Dy,$v;m(165,23,{3:1,35:1,23:1,165:1},sT);var fx,V1,ax,yg,sD,e5e=yt(Tu,"LayerConstraint",165,Tt,Y6n,rmn),Win;m(423,23,{3:1,35:1,23:1,423:1},pse);var Eie,Sie,n5e=yt(Tu,"LayerUnzippingStrategy",423,Tt,W5n,cmn),Zin;m(843,1,qa,EC),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,gwe),""),"Direction Congruency"),"Specifies how drawings of the same graph with different layout directions compare to each other: either a natural reading direction is preserved or the drawings are rotated versions of each other."),b5e),(cg(),Ri)),Sve),nn((ph(),Mn))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,wwe),""),"Feedback Edges"),"Whether feedback edges should be highlighted by routing around the nodes."),(Pn(),!1)),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,bF),""),"Interactive Reference Point"),"Determines which point of a node is considered by interactive layout phases."),y5e),Ri),Fve),nn(Mn)))),Gi(n,bF,ON,icn),Gi(n,bF,MS,tcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,pwe),""),"Merge Edges"),"Edges that have no ports are merged so they touch the connected nodes at the same points. When this option is disabled, one port is created for each edge directly connected to a node. When it is enabled, all such incoming edges share an input port, and all outgoing edges share an output port."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,mwe),""),"Merge Hierarchy-Crossing Edges"),"If hierarchical layout is active, hierarchy-crossing edges use as few hierarchical ports as possible. They are broken by the algorithm, with hierarchical ports inserted as required. Usually, one such port is created for each edge at each hierarchy crossing point. With this option set to true, we try to create as few hierarchical ports as possible in the process. In particular, all edges that form a hyperedge can share a port."),!0),Sr),Yi),nn(Mn)))),We(n,new Ge(Zbn(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vwe),""),"Allow Non-Flow Ports To Switch Sides"),"Specifies whether non-flow ports may switch sides if their node's port constraints are either FIXED_SIDE or FIXED_ORDER. A non-flow port is a port on a side that is not part of the currently configured layout flow. For instance, given a left-to-right layout direction, north and south ports would be considered non-flow ports. Further note that the underlying criterium whether to switch sides or not solely relies on the minimization of edge crossings. Hence, edge length and other aesthetics criteria are not addressed."),!1),Sr),Yi),nn(Kd)),z(B(ze,1),Se,2,6,["org.eclipse.elk.layered.northOrSouthPort"])))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ywe),""),"Port Sorting Strategy"),"Only relevant for nodes with FIXED_SIDE port constraints. Determines the way a node's ports are distributed on the sides of a node if their order is not prescribed. The option is set on parent nodes."),N5e),Ri),J4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,kwe),""),"Thoroughness"),"How much effort should be spent to produce a nice layout."),ve(7)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,jwe),""),"Add Unnecessary Bendpoints"),"Adds bend points even if an edge does not change direction. If true, each long edge dummy will contribute a bend point to its edges and hierarchy-crossing edges will always get a bend point where they cross hierarchy boundaries. By default, bend points are only added where an edge changes direction."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ewe),""),"Generate Position and Layer IDs"),"If enabled position id and layer id are generated, which are usually only used internally when setting the interactiveLayout option. This option should be specified on the root node."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ON),"cycleBreaking"),"Cycle Breaking Strategy"),"Strategy for cycle breaking. Cycle breaking looks for cycles in the graph and determines which edges to reverse to break the cycles. Reversed edges will end up pointing to the opposite direction of regular edges (that is, reversed edges will point left if edges usually point right)."),d5e),Ri),jve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,NN),$ee),"Node Layering Strategy"),"Strategy for node layering."),E5e),Ri),O4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Swe),$ee),"Layer Constraint"),"Determines a constraint on the placement of the node regarding the layering."),k5e),Ri),e5e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xwe),$ee),"Layer Choice Constraint"),"Allows to set a constraint regarding the layer placement of a node. Let i be the value of teh constraint. Assumed the drawing has n layers and i < n. If set to i, it expresses that the node should be placed in i-th layer. Should i>=n be true then the node is placed in the last layer of the drawing. Note that this option is not part of any of ELK Layered's default configurations but is only evaluated as part of the `InteractiveLayeredGraphVisitor`, which must be applied manually or used via the `DiagramLayoutEngine."),null),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Awe),$ee),"Layer ID"),"Layer identifier that was calculated by ELK Layered for a node. This is only generated if interactiveLayot or generatePositionAndLayerIds is set."),ve(-1)),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,cee),TQe),"Upper Bound On Width [MinWidth Layerer]"),"Defines a loose upper bound on the width of the MinWidth layerer. If set to '-1' multiple values are tested and the best result is selected."),ve(4)),hc),jr),nn(Mn)))),Gi(n,cee,NN,fcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,uee),TQe),"Upper Layer Estimation Scaling Factor [MinWidth Layerer]"),"Multiplied with Upper Bound On Width for defining an upper bound on the width of layers which haven't been determined yet, but whose maximum width had been (roughly) estimated by the MinWidth algorithm. Compensates for too high estimations. If set to '-1' multiple values are tested and the best result is selected."),ve(2)),hc),jr),nn(Mn)))),Gi(n,uee,NN,hcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,oee),OQe),"Node Promotion Strategy"),"Reduces number of dummy nodes after layering phase (if possible)."),j5e),Ri),B4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,see),OQe),"Max Node Promotion Iterations"),"Limits the number of iterations for node promotion."),ve(0)),hc),jr),nn(Mn)))),Gi(n,see,oee,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,lee),"layering.coffmanGraham"),"Layer Bound"),"The maximum number of nodes allowed per layer."),ve(oi)),hc),jr),nn(Mn)))),Gi(n,lee,NN,ccn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,MS),V8),"Crossing Minimization Strategy"),"Strategy for crossing minimization."),h5e),Ri),mve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Mwe),V8),"Force Node Model Order"),"The node order given by the model does not change to produce a better layout. E.g. if node A is before node B in the model this is not changed during crossing minimization. This assumes that the node model order is already respected before crossing minimization. This can be achieved by setting considerModelOrder.strategy to NODES_AND_EDGES."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,fee),V8),"Hierarchical Sweepiness"),"How likely it is to use cross-hierarchy (1) vs bottom-up (-1)."),.1),ec),gr),nn(Mn)))),Gi(n,fee,MF,Trn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,aee),V8),"Semi-Interactive Crossing Minimization"),"Preserves the order of nodes within a layer but still minimizes crossings between edges connecting long edge dummies. Derives the desired order from positions specified by the 'org.eclipse.elk.position' layout option. Requires a crossing minimization strategy that is able to process 'in-layer' constraints."),!1),Sr),Yi),nn(Mn)))),Gi(n,aee,MS,Lrn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Cwe),V8),"In Layer Predecessor of"),"Allows to set a constraint which specifies of which node the current node is the predecessor. If set to 's' then the node is the predecessor of 's' and is in the same layer"),null),By),ze),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Twe),V8),"In Layer Successor of"),"Allows to set a constraint which specifies of which node the current node is the successor. If set to 's' then the node is the successor of 's' and is in the same layer"),null),By),ze),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Owe),V8),"Position Choice Constraint"),"Allows to set a constraint regarding the position placement of a node in a layer. Assumed the layer in which the node placed includes n other nodes and i < n. If set to i, it expresses that the node should be placed at the i-th position. Should i>=n be true then the node is placed at the last position in the layer. Note that this option is not part of any of ELK Layered's default configurations but is only evaluated as part of the `InteractiveLayeredGraphVisitor`, which must be applied manually or used via the `DiagramLayoutEngine."),null),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Nwe),V8),"Position ID"),"Position within a layer that was determined by ELK Layered for a node. This is only generated if interactiveLayot or generatePositionAndLayerIds is set."),ve(-1)),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Dwe),NQe),"Greedy Switch Activation Threshold"),"By default it is decided automatically if the greedy switch is activated or not. The decision is based on whether the size of the input graph (without dummy nodes) is smaller than the value of this option. A '0' enforces the activation."),ve(40)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hee),NQe),"Greedy Switch Crossing Minimization"),"Greedy Switch strategy for crossing minimization. The greedy switch heuristic is executed after the regular crossing minimization as a post-processor. Note that if 'hierarchyHandling' is set to 'INCLUDE_CHILDREN', the 'greedySwitchHierarchical.type' option must be used."),a5e),Ri),hie),nn(Mn)))),Gi(n,hee,MS,Mrn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,gF),"crossingMinimization.greedySwitchHierarchical"),"Greedy Switch Crossing Minimization (hierarchical)"),"Activates the greedy switch heuristic in case hierarchical layout is used. The differences to the non-hierarchical case (see 'greedySwitch.type') are: 1) greedy switch is inactive by default, 3) only the option value set on the node at which hierarchical layout starts is relevant, and 2) if it's activated by the user, it properly addresses hierarchy-crossing edges."),f5e),Ri),hie),nn(Mn)))),Gi(n,gF,MS,Srn),Gi(n,gF,MF,xrn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,pv),DQe),"Node Placement Strategy"),"Strategy for node placement."),O5e),Ri),_4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,wF),DQe),"Favor Straight Edges Over Balancing"),"Favor straight edges over a balanced node placement. The default behavior is determined automatically based on the used 'edgeRouting'. For an orthogonal style it is set to true, for all other styles to false."),Sr),Yi),nn(Mn)))),Gi(n,wF,pv,Tcn),Gi(n,wF,pv,Ocn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,dee),IQe),"BK Edge Straightening"),"Specifies whether the Brandes Koepf node placer tries to increase the number of straight edges at the expense of diagram size. There is a subtle difference to the 'favorStraightEdges' option, which decides whether a balanced placement of the nodes is desired, or not. In bk terms this means combining the four alignments into a single balanced one, or not. This option on the other hand tries to straighten additional edges during the creation of each of the four alignments."),M5e),Ri),Mve),nn(Mn)))),Gi(n,dee,pv,xcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,bee),IQe),"BK Fixed Alignment"),"Tells the BK node placer to use a certain alignment (out of its four) instead of the one producing the smallest height, or the combination of all four."),C5e),Ri),Dve),nn(Mn)))),Gi(n,bee,pv,Mcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,gee),"nodePlacement.linearSegments"),"Linear Segments Deflection Dampening"),"Dampens the movement of nodes to keep the diagram from getting too large."),.3),ec),gr),nn(Mn)))),Gi(n,gee,pv,Dcn),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,wee),"nodePlacement.networkSimplex"),"Node Flexibility"),"Aims at shorter and straighter edges. Two configurations are possible: (a) allow ports to move freely on the side they are assigned to (the order is always defined beforehand), (b) additionally allow to enlarge a node wherever it helps. If this option is not configured for a node, the 'nodeFlexibility.default' value is used, which is specified for the node's parent."),Ri),Qie),nn(fr)))),Gi(n,wee,pv,Pcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,pee),"nodePlacement.networkSimplex.nodeFlexibility"),"Node Flexibility Default"),"Default value of the 'nodeFlexibility' option for the children of a hierarchical node."),T5e),Ri),Qie),nn(Mn)))),Gi(n,pee,pv,Lcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Iwe),_Qe),"Self-Loop Distribution"),"Alter the distribution of the loops around the node. It only takes effect for PortConstraints.FREE."),p5e),Ri),q4e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,_we),_Qe),"Self-Loop Ordering"),"Alter the ordering of the loops they can either be stacked or sequenced. It only takes effect for PortConstraints.FREE."),m5e),Ri),U4e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,pF),"edgeRouting.splines"),"Spline Routing Mode"),"Specifies the way control points are assembled for each individual edge. CONSERVATIVE ensures that edges are properly routed around the nodes but feels rather orthogonal at times. SLOPPY uses fewer control points to obtain curvier edge routes but may result in edges overlapping nodes."),v5e),Ri),K4e),nn(Mn)))),Gi(n,pF,IN,Urn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,mF),"edgeRouting.splines.sloppy"),"Sloppy Spline Layer Spacing Factor"),"Spacing factor for routing area between layers when using sloppy spline routing."),.2),ec),gr),nn(Mn)))),Gi(n,mF,IN,Krn),Gi(n,mF,pF,Vrn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,mee),"edgeRouting.polyline"),"Sloped Edge Zone Width"),"Width of the strip to the left and to the right of each layer where the polyline edge router is allowed to refrain from ensuring that edges are routed horizontally. This prevents awkward bend points for nodes that extent almost to the edge of their layer."),2),ec),gr),nn(Mn)))),Gi(n,mee,IN,Jrn),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Lwe),Xa),"Spacing Base Value"),"An optional base value for all other layout options of the 'spacing' group. It can be used to conveniently alter the overall 'spaciousness' of the drawing. Whenever an explicit value is set for the other layout options, this base value will have no effect. The base value is not inherited, i.e. it must be set for each hierarchical node."),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Pwe),Xa),"Edge Node Between Layers Spacing"),"The spacing to be preserved between nodes and edges that are routed next to the node's layer. For the spacing between nodes and edges that cross the node's layer 'spacing.edgeNode' is used."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,$we),Xa),"Edge Edge Between Layer Spacing"),"Spacing to be preserved between pairs of edges that are routed between the same pair of layers. Note that 'spacing.edgeEdge' is used for the spacing between pairs of edges crossing the same layer."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Rwe),Xa),"Node Node Between Layers Spacing"),"The spacing to be preserved between any pair of nodes of two adjacent layers. Note that 'spacing.nodeNode' is used for the spacing between nodes within the layer itself."),20),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Bwe),Qwe),"Direction Priority"),"Defines how important it is to have a certain edge point into the direction of the overall layout. This option is evaluated during the cycle breaking phase."),ve(0)),hc),jr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,zwe),Qwe),"Shortness Priority"),"Defines how important it is to keep an edge as short as possible. This option is evaluated during the layering phase."),ve(0)),hc),jr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Fwe),Qwe),"Straightness Priority"),"Defines how important it is to keep an edge straight, i.e. aligned with one of the two axes. This option is evaluated during node placement."),ve(0)),hc),jr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vee),Wwe),"Connected Components Compaction"),"Tries to further compact components (disconnected sub-graphs)."),!1),Sr),Yi),nn(Mn)))),Gi(n,vee,kS,!0),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Jwe),LQe),"Post Compaction Strategy"),PQe),i5e),Ri),Rve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Hwe),LQe),"Post Compaction Constraint Calculation"),PQe),t5e),Ri),gve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vF),Zwe),"High Degree Node Treatment"),"Makes room around high degree nodes to place leafs and trees."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,yee),Zwe),"High Degree Node Threshold"),"Whether a node is considered to have a high degree."),ve(16)),hc),jr),nn(Mn)))),Gi(n,yee,vF,!0),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,kee),Zwe),"High Degree Node Maximum Tree Height"),"Maximum height of a subtree connected to a high degree node to be moved to separate layers."),ve(5)),hc),jr),nn(Mn)))),Gi(n,kee,vF,!0),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,q1),epe),"Graph Wrapping Strategy"),"For certain graphs and certain prescribed drawing areas it may be desirable to split the laid out graph into chunks that are placed side by side. The edges that connect different chunks are 'wrapped' around from the end of one chunk to the start of the other chunk. The points between the chunks are referred to as 'cuts'."),_5e),Ri),W4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,yF),epe),"Additional Wrapped Edges Spacing"),"To visually separate edges that are wrapped from regularly routed edges an additional spacing value can be specified in form of this layout option. The spacing is added to the regular edgeNode spacing."),10),ec),gr),nn(Mn)))),Gi(n,yF,q1,Vcn),Gi(n,yF,q1,Ycn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,kF),epe),"Correction Factor for Wrapping"),"At times and for certain types of graphs the executed wrapping may produce results that are consistently biased in the same fashion: either wrapping to often or to rarely. This factor can be used to correct the bias. Internally, it is simply multiplied with the 'aspect ratio' layout option."),1),ec),gr),nn(Mn)))),Gi(n,kF,q1,Wcn),Gi(n,kF,q1,Zcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,CS),$Qe),"Cutting Strategy"),"The strategy by which the layer indexes are determined at which the layering crumbles into chunks."),I5e),Ri),yve),nn(Mn)))),Gi(n,CS,q1,cun),Gi(n,CS,q1,uun),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,jee),$Qe),"Manually Specified Cuts"),"Allows the user to specify her own cuts for a certain graph."),Wa),wl),nn(Mn)))),Gi(n,jee,CS,nun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Eee),"wrapping.cutting.msd"),"MSD Freedom"),"The MSD cutting strategy starts with an initial guess on the number of chunks the graph should be split into. The freedom specifies how much the strategy may deviate from this guess. E.g. if an initial number of 3 is computed, a freedom of 1 allows 2, 3, and 4 cuts."),D5e),hc),jr),nn(Mn)))),Gi(n,Eee,CS,iun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,jF),RQe),"Validification Strategy"),"When wrapping graphs, one can specify indices that are not allowed as split points. The validification strategy makes sure every computed split point is allowed."),L5e),Ri),Q4e),nn(Mn)))),Gi(n,jF,q1,mun),Gi(n,jF,q1,vun),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,EF),RQe),"Valid Indices for Wrapping"),null),Wa),wl),nn(Mn)))),Gi(n,EF,q1,gun),Gi(n,EF,q1,wun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,SF),npe),"Improve Cuts"),"For general graphs it is important that not too many edges wrap backwards. Thus a compromise between evenly-distributed cuts and the total number of cut edges is sought."),!0),Sr),Yi),nn(Mn)))),Gi(n,SF,q1,fun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xF),npe),"Distance Penalty When Improving Cuts"),null),2),ec),gr),nn(Mn)))),Gi(n,xF,q1,sun),Gi(n,xF,SF,!0),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,See),npe),"Improve Wrapped Edges"),"The initial wrapping is performed in a very simple way. As a consequence, edges that wrap from one chunk to another may be unnecessarily long. Activating this option tries to shorten such edges."),!0),Sr),Yi),nn(Mn)))),Gi(n,See,q1,hun),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xee),Ree),"Layer Unzipping Strategy"),"The strategy to use for unzipping a layer into multiple sublayers while maintaining the existing ordering of nodes and edges after crossing minimization. The default value is 'NONE'."),A5e),Ri),n5e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Aee),Ree),"Minimize Edge Length Heuristic"),"Use a heuristic to decide whether or not to actually perform the layer split with the goal of minimizing the total edge length. This option only works when layerSplit is set to 2. The property can be set to the nodes in a layer, which then applies the property for the layer. If any node sets the value to true, then the value is set to true for the entire layer."),!1),Sr),Yi),nn(fr)))),Gi(n,Aee,Mee,mcn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Mee),Ree),"Unzipping Layer Split"),"Defines the number of sublayers to split a layer into. The property can be set to the nodes in a layer, which then applies the property for the layer. If multiple nodes set the value to different values, then the lowest value is chosen."),S5e),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Cee),Ree),"Reset Alternation on Long Edges"),"If set to true, nodes will always be placed in the first sublayer after a long edge when using the ALTERNATING strategy. Otherwise long edge dummies are treated the same as regular nodes. The default value is true. The property can be set to the nodes in a layer, which then applies the property for the layer. If any node sets the value to false, then the value is set to false for the entire layer."),x5e),Sr),Yi),nn(fr)))),Gi(n,Cee,xee,ycn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Gwe),Bee),"Edge Label Side Selection"),"Method to decide on edge label sides."),w5e),Ri),xve),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,qwe),Bee),"Edge Center Label Placement Strategy"),"Determines in which layer center labels of long edges should be placed."),g5e),Ri),y7),Mi(Mn,z(B(Qa,1),ke,160,0,[Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,AF),TS),"Consider Model Order"),"Preserves the order of nodes and edges in the model file if this does not lead to additional edge crossings. Depending on the strategy this is not always possible since the node and edge order might be conflicting."),l5e),Ri),F4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Uwe),TS),"Consider Port Order"),"If disabled the port order of output ports is derived from the edge order and input ports are ordered by their incoming connections. If enabled all ports are ordered by the port model order."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,DN),TS),"No Model Order"),"Set on a node to not set a model order for this node even though it is a real node."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Tee),TS),"Consider Model Order for Components"),"If set to NONE the usual ordering strategy (by cumulative node priority and size of nodes) is used. INSIDE_PORT_SIDES orders the components with external ports only inside the groups with the same port side. FORCE_MODEL_ORDER enforces the mode order on components. This option might produce bad alignments and sub optimal drawings in terms of used area since the ordering should be respected."),r5e),Ri),y3e),nn(Mn)))),Gi(n,Tee,kS,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Xwe),TS),"Long Edge Ordering Strategy"),"Indicates whether long edges are sorted under, over, or equal to nodes that have no connection to a previous layer in a left-to-right or right-to-left layout. Under and over changes to right and left in a vertical layout."),s5e),Ri),D4e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Oee),TS),"Crossing Counter Node Order Influence"),"Indicates with what percentage (1 for 100%) violations of the node model order are weighted against the crossings e.g. a value of 0.5 means two model order violations are as important as on edge crossing. This allows some edge crossings in favor of preserving the model order. It is advised to set this value to a very small positive value (e.g. 0.001) to have minimal crossing and a optimal node order. Defaults to no influence (0)."),0),ec),gr),nn(Mn)))),Gi(n,Oee,AF,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Nee),TS),"Crossing Counter Port Order Influence"),"Indicates with what percentage (1 for 100%) violations of the port model order are weighted against the crossings e.g. a value of 0.5 means two model order violations are as important as on edge crossing. This allows some edge crossings in favor of preserving the model order. It is advised to set this value to a very small positive value (e.g. 0.001) to have minimal crossing and a optimal port order. Defaults to no influence (0)."),0),ec),gr),nn(Mn)))),Gi(n,Nee,AF,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Dee),Y8),tpe),"Used to define partial ordering groups during cycle breaking. A lower group id means that the group is sorted before other groups. A group model order of 0 is the default group."),ve(0)),hc),jr),nn(fr)))),Gi(n,Dee,DN,!1),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Iee),Y8),tpe),"Used to define partial ordering groups during crossing minimization. A lower group id means that the group is sorted before other groups. A group model order of 0 is the default group."),ve(0)),hc),jr),Mi(fr,z(B(Qa,1),ke,160,0,[Sa,Kd]))))),Gi(n,Iee,DN,!1),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,_ee),Y8),tpe),"Used to define partial ordering groups during component packing. A lower group id means that the group is sorted before other groups. A group model order of 0 is the default group."),ve(0)),hc),jr),Mi(fr,z(B(Qa,1),ke,160,0,[Sa,Kd]))))),Gi(n,_ee,DN,!1),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Kwe),Y8),"Cycle Breaking Group Ordering Strategy"),"Determines how to count ordering violations during cycle breaking. NONE: They do not count. ENFORCED: A group with a higher model order is before a node with a smaller. MODEL_ORDER: The model order counts instead of the model order group id ordering."),c5e),Ri),die),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Lee),Y8),"Cycle Breaking Preferred Source Id"),"The model order group id for which should be preferred as a source if possible."),hc),jr),nn(Mn)))),Gi(n,Lee,ON,lrn),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Pee),Y8),"Cycle Breaking Preferred Target Id"),"The model order group id for which should be preferred as a target if possible."),hc),jr),nn(Mn)))),Gi(n,Pee,ON,arn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Vwe),Y8),"Crossing Minimization Group Ordering Strategy"),"Determines how to count ordering violations during crossing minimization. NONE: They do not count. ENFORCED: A group with a lower id is before a group with a higher id. MODEL_ORDER: The model order counts instead of the model order group id ordering."),o5e),Ri),die),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ywe),Y8),"Crossing Minimization Enforced Group Orders"),"Holds all group ids which are enforcing their order during crossing minimization strategies. E.g. if only groups 2 and -1 (default) enforce their ordering. Other groups e.g. the group of timer nodes can be ordered arbitrarily if it helps and the mentioned groups may not change their order."),u5e),Wa),wl),nn(Mn)))),rYe((new OU,n))};var ern,nrn,trn,t5e,irn,i5e,rrn,r5e,crn,urn,orn,c5e,srn,lrn,frn,arn,hrn,u5e,drn,o5e,brn,grn,wrn,prn,s5e,mrn,vrn,yrn,l5e,krn,jrn,Ern,f5e,Srn,xrn,Arn,a5e,Mrn,Crn,Trn,Orn,Nrn,Drn,Irn,_rn,Lrn,Prn,h5e,$rn,d5e,Rrn,b5e,Brn,g5e,zrn,w5e,Frn,Jrn,Hrn,p5e,Grn,m5e,qrn,v5e,Urn,Xrn,Krn,Vrn,Yrn,Qrn,Wrn,Zrn,ecn,ncn,y5e,tcn,icn,rcn,ccn,ucn,ocn,k5e,scn,lcn,fcn,acn,hcn,dcn,bcn,j5e,gcn,E5e,wcn,S5e,pcn,mcn,vcn,x5e,ycn,kcn,A5e,jcn,Ecn,Scn,M5e,xcn,Acn,C5e,Mcn,Ccn,Tcn,Ocn,Ncn,Dcn,Icn,_cn,T5e,Lcn,Pcn,$cn,O5e,Rcn,N5e,Bcn,zcn,Fcn,Jcn,Hcn,Gcn,qcn,Ucn,Xcn,Kcn,Vcn,Ycn,Qcn,Wcn,Zcn,eun,nun,tun,D5e,iun,run,I5e,cun,uun,oun,sun,lun,fun,aun,hun,dun,_5e,bun,gun,wun,pun,L5e,mun,vun;v(Tu,"LayeredMetaDataProvider",843),m(982,1,qa,OU),s.tf=function(n){rYe(n)};var Th,xie,uH,hx,oH,P5e,sH,dx,lD,Aie,Iy,$5e,R5e,B5e,bx,yun,gx,gm,Mie,lH,Cie,u1,Tie,A7,z5e,fD,Oie,F5e,kun,jun,Eun,fH,Nie,wx,_y,Sun,pl,J5e,H5e,aH,Rv,Oh,hH,Y1,G5e,q5e,U5e,Die,Iie,X5e,Gd,_ie,K5e,wm,V5e,Y5e,Q5e,dH,pm,kg,W5e,Z5e,Wc,e4e,xun,yu,px,n4e,t4e,i4e,aD,bH,gH,Lie,Pie,r4e,wH,c4e,u4e,pH,bp,o4e,$ie,mx,s4e,gp,vx,mH,jg,Rie,M7,vH,Eg,l4e,f4e,a4e,mm,h4e,Aun,Mun,Cun,Tun,wp,vm,Wi,qd,Oun,ym,d4e,C7,b4e,km,Nun,T7,g4e,Ly,Dun,Iun,hD,Bie,w4e,dD,Vf,jm,Bv,Sg,ub,yH,Em,zie,O7,N7,xg,Sm,Fie,bD,yx,kx,_un,Lun,Pun,p4e,$un,Jie,m4e,v4e,y4e,k4e,Hie,j4e,E4e,S4e,x4e,Gie,kH;v(Tu,"LayeredOptions",982),m(983,1,{},Yq),s.uf=function(){var n;return n=new ixe,n},s.vf=function(n){},v(Tu,"LayeredOptions/LayeredFactory",983),m(1345,1,{}),s.a=0;var Run;v(Ru,"ElkSpacings/AbstractSpacingsBuilder",1345),m(778,1345,{},tde);var jH,Bun;v(Tu,"LayeredSpacings/LayeredSpacingsBuilder",778),m(268,23,{3:1,35:1,23:1,268:1,188:1,196:1},M3),s.bg=function(){return yXe(this)},s.og=function(){return yXe(this)};var qie,Uie,Xie,A4e,M4e,C4e,EH,Kie,T4e,O4e=yt(Tu,"LayeringStrategy",268,Tt,B8n,umn),zun;m(352,23,{3:1,35:1,23:1,352:1},eK);var Vie,N4e,SH,D4e=yt(Tu,"LongEdgeOrderingStrategy",352,Tt,H4n,omn),Fun;m(203,23,{3:1,35:1,23:1,203:1},d$);var zv,Fv,xH,Yie,Qie=yt(Tu,"NodeFlexibility",203,Tt,t6n,smn),Jun;m(328,23,{3:1,35:1,23:1,328:1,188:1,196:1},lT),s.bg=function(){return sUe(this)},s.og=function(){return sUe(this)};var jx,Wie,Zie,Ex,I4e,_4e=yt(Tu,"NodePlacementStrategy",328,Tt,V6n,lmn),Hun;m(243,23,{3:1,35:1,23:1,243:1},u2);var L4e,D7,Sx,gD,P4e,$4e,wD,R4e,AH,MH,B4e=yt(Tu,"NodePromotionStrategy",243,Tt,f7n,fmn),Gun;m(269,23,{3:1,35:1,23:1,269:1},b$);var z4e,ob,ere,nre,F4e=yt(Tu,"OrderingStrategy",269,Tt,i6n,amn),qun;m(421,23,{3:1,35:1,23:1,421:1},mse);var tre,ire,J4e=yt(Tu,"PortSortingStrategy",421,Tt,Z5n,hmn),Uun;m(452,23,{3:1,35:1,23:1,452:1},nK);var ys,Do,xx,Xun=yt(Tu,"PortType",452,Tt,G4n,dmn),Kun;m(381,23,{3:1,35:1,23:1,381:1},tK);var H4e,rre,G4e,q4e=yt(Tu,"SelfLoopDistributionStrategy",381,Tt,q4n,bmn),Vun;m(348,23,{3:1,35:1,23:1,348:1},iK);var cre,pD,ure,U4e=yt(Tu,"SelfLoopOrderingStrategy",348,Tt,U4n,gmn),Yun;m(316,1,{316:1},tVe),v(Tu,"Spacings",316),m(349,23,{3:1,35:1,23:1,349:1},rK);var ore,X4e,Ax,K4e=yt(Tu,"SplineRoutingMode",349,Tt,X4n,wmn),Qun;m(351,23,{3:1,35:1,23:1,351:1},cK);var sre,V4e,Y4e,Q4e=yt(Tu,"ValidifyStrategy",351,Tt,K4n,pmn),Wun;m(382,23,{3:1,35:1,23:1,382:1},uK);var xm,lre,I7,W4e=yt(Tu,"WrappingStrategy",382,Tt,V4n,mmn),Zun;m(1361,1,uc,jC),s.pg=function(n){return u(n,37),eon},s.If=function(n,t){RPn(this,u(n,37),t)};var eon;v(Zw,"BFSNodeOrderCycleBreaker",1361),m(1359,1,uc,iP),s.pg=function(n){return u(n,37),non},s.If=function(n,t){ILn(this,u(n,37),t)};var non;v(Zw,"DFSNodeOrderCycleBreaker",1359),m(1360,1,it,$Ne),s.Ad=function(n){LIn(this.a,this.c,this.b,u(n,17))},s.b=!1,v(Zw,"DFSNodeOrderCycleBreaker/lambda$0$Type",1360),m(1353,1,uc,Q6),s.pg=function(n){return u(n,37),ton},s.If=function(n,t){DLn(this,u(n,37),t)};var ton;v(Zw,"DepthFirstCycleBreaker",1353),m(779,1,uc,Afe),s.pg=function(n){return u(n,37),ion},s.If=function(n,t){ZRn(this,u(n,37),t)},s.qg=function(n){return u(Le(n,sz(this.e,n.c.length)),9)};var ion;v(Zw,"GreedyCycleBreaker",779),m(1356,779,uc,SCe),s.qg=function(n){var t,i,r,c,o,l,f,h,b;for(b=null,r=oi,h=k.Math.max(this.b.a.c.length,u(C(this.b,(me(),cb)),15).a),t=h*u(C(this.b,cD),15).a,c=new S6,i=ue(C(this.b,(Ne(),Iy)))===ue((_0(),dm)),f=new L(n);f.ao&&(r=o,b=l));return b||u(Le(n,sz(this.e,n.c.length)),9)},v(Zw,"GreedyModelOrderCycleBreaker",1356),m(505,1,{},S6),s.a=0,s.b=0,v(Zw,"GroupModelOrderCalculator",505),m(1354,1,uc,nj),s.pg=function(n){return u(n,37),ron},s.If=function(n,t){cPn(this,u(n,37),t)};var ron;v(Zw,"InteractiveCycleBreaker",1354),m(1355,1,uc,ej),s.pg=function(n){return u(n,37),con},s.If=function(n,t){oPn(u(n,37),t)};var con;v(Zw,"ModelOrderCycleBreaker",1355),m(780,1,uc),s.pg=function(n){return u(n,37),uon},s.If=function(n,t){V_n(this,u(n,37),t)},s.rg=function(n,t){var i,r,c,o,l,f,h,b,p,y;for(l=0;lb&&(h=S,y=b),paa(new qn(Vn(Ni(f).a.Jc(),new ee))))for(c=new qn(Vn(rr(h).a.Jc(),new ee));ht(c);)r=u(tt(c),17),u(Yu(this.d,l),22).Gc(r.c.i)&&Ce(this.c,r);else for(c=new qn(Vn(Ni(f).a.Jc(),new ee));ht(c);)r=u(tt(c),17),u(Yu(this.d,l),22).Gc(r.d.i)&&Ce(this.c,r)}},v(Zw,"SCCNodeTypeCycleBreaker",1358),m(1357,780,uc,ACe),s.rg=function(n,t){var i,r,c,o,l,f,h,b,p,y,S,A;for(l=0;lb&&(h=S,y=b),paa(new qn(Vn(Ni(f).a.Jc(),new ee))))for(c=new qn(Vn(rr(h).a.Jc(),new ee));ht(c);)r=u(tt(c),17),u(Yu(this.d,l),22).Gc(r.c.i)&&Ce(this.c,r);else for(c=new qn(Vn(Ni(f).a.Jc(),new ee));ht(c);)r=u(tt(c),17),u(Yu(this.d,l),22).Gc(r.d.i)&&Ce(this.c,r)}},v(Zw,"SCConnectivity",1357),m(1373,1,uc,kC),s.pg=function(n){return u(n,37),oon},s.If=function(n,t){iRn(this,u(n,37),t)};var oon;v(U1,"BreadthFirstModelOrderLayerer",1373),m(1374,1,Yt,sM),s.Le=function(n,t){return JCn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"BreadthFirstModelOrderLayerer/lambda$0$Type",1374),m(1364,1,uc,TMe),s.pg=function(n){return u(n,37),son},s.If=function(n,t){rBn(this,u(n,37),t)};var son;v(U1,"CoffmanGrahamLayerer",1364),m(1365,1,Yt,Wje),s.Le=function(n,t){return VNn(this.a,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"CoffmanGrahamLayerer/0methodref$compareNodesInTopo$Type",1365),m(1366,1,Yt,Zje),s.Le=function(n,t){return fvn(this.a,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"CoffmanGrahamLayerer/lambda$1$Type",1366),m(1375,1,uc,yC),s.pg=function(n){return u(n,37),lon},s.If=function(n,t){GRn(this,u(n,37),t)},s.c=0,s.e=0;var lon;v(U1,"DepthFirstModelOrderLayerer",1375),m(1376,1,Yt,x6),s.Le=function(n,t){return HCn(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"DepthFirstModelOrderLayerer/lambda$0$Type",1376),m(1367,1,uc,A6),s.pg=function(n){return u(n,37),qt(qt(qt(new or,(zr(),Kf),(Ur(),Ite)),r1,fm),eo,lm)},s.If=function(n,t){bRn(u(n,37),t)},v(U1,"InteractiveLayerer",1367),m(564,1,{564:1},axe),s.a=0,s.c=0,v(U1,"InteractiveLayerer/LayerSpan",564),m(1363,1,uc,cP),s.pg=function(n){return u(n,37),fon},s.If=function(n,t){HNn(this,u(n,37),t)};var fon;v(U1,"LongestPathLayerer",1363),m(1372,1,uc,uP),s.pg=function(n){return u(n,37),aon},s.If=function(n,t){fDn(this,u(n,37),t)};var aon;v(U1,"LongestPathSourceLayerer",1372),m(1370,1,uc,ko),s.pg=function(n){return u(n,37),qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)},s.If=function(n,t){SRn(this,u(n,37),t)},s.a=0,s.b=0,s.d=0;var Z4e,eye;v(U1,"MinWidthLayerer",1370),m(1371,1,Yt,eEe),s.Le=function(n,t){return N7n(this,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"MinWidthLayerer/MinOutgoingEdgesComparator",1371),m(1362,1,uc,rP),s.pg=function(n){return u(n,37),hon},s.If=function(n,t){FPn(this,u(n,37),t)};var hon;v(U1,"NetworkSimplexLayerer",1362),m(1368,1,uc,rNe),s.pg=function(n){return u(n,37),qt(qt(qt(new or,(zr(),Kf),(Ur(),Av)),r1,fm),eo,lm)},s.If=function(n,t){A$n(this,u(n,37),t)},s.d=0,s.f=0,s.g=0,s.i=0,s.s=0,s.t=0,s.u=0,v(U1,"StretchWidthLayerer",1368),m(1369,1,Yt,Wq),s.Le=function(n,t){return a9n(u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(U1,"StretchWidthLayerer/1",1369),m(406,1,Bpe),s.eg=function(n,t,i,r,c,o){},s.tg=function(n,t,i){return YXe(this,n,t,i)},s.dg=function(){this.g=oe(Hm,JQe,30,this.d,15,1),this.f=oe(Hm,JQe,30,this.d,15,1)},s.fg=function(n,t){this.e[n]=oe($t,ni,30,t[n].length,15,1)},s.gg=function(n,t,i){var r;r=i[n][t],r.p=t,this.e[n][t]=t},s.hg=function(n,t,i,r){u(Le(r[n][t].j,i),12).p=this.d++},s.b=0,s.c=0,s.d=0,v(Ro,"AbstractBarycenterPortDistributor",406),m(1663,1,Yt,nEe),s.Le=function(n,t){return BEn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ro,"AbstractBarycenterPortDistributor/lambda$0$Type",1663),m(816,1,AS,Iae),s.eg=function(n,t,i,r,c,o){},s.gg=function(n,t,i){},s.hg=function(n,t,i,r){},s.cg=function(){return!1},s.dg=function(){this.c=this.e.a,this.g=this.f.g},s.fg=function(n,t){t[n][0].c.p=n},s.ig=function(){return!1},s.ug=function(n,t,i,r){i?FHe(this,n):(XHe(this,n,r),dVe(this,n,t)),n.c.length>1&&(Re($e(C(_r((vn(0,n.c.length),u(n.c[0],9))),(Ne(),A7))))?vUe(n,this.d,u(this,660)):(jn(),Tr(n,this.d)),lze(this.e,n))},s.jg=function(n,t,i,r){var c,o,l,f,h,b,p;for(t!=SDe(i,n.length)&&(o=n[t-(i?1:-1)],rhe(this.f,o,i?(Nc(),Do):(Nc(),ys))),c=n[t][0],p=!r||c.k==(zn(),wr),b=$f(n[t]),this.ug(b,p,!1,i),l=0,h=new L(b);h.a"),n0?HV(this.a,n[t-1],n[t]):!i&&t1&&(Re($e(C(_r((vn(0,n.c.length),u(n.c[0],9))),(Ne(),A7))))?vUe(n,this.d,this):(jn(),Tr(n,this.d)),Re($e(C(_r((vn(0,n.c.length),u(n.c[0],9))),A7)))||lze(this.e,n))},v(Ro,"ModelOrderBarycenterHeuristic",660),m(1843,1,Yt,lEe),s.Le=function(n,t){return jLn(this.a,u(n,9),u(t,9))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ro,"ModelOrderBarycenterHeuristic/lambda$0$Type",1843),m(1383,1,uc,sP),s.pg=function(n){var t;return u(n,37),t=I$(yon),qt(t,(zr(),eo),(Ur(),LJ)),t},s.If=function(n,t){R5n((u(n,37),t))};var yon;v(Ro,"NoCrossingMinimizer",1383),m(796,406,Bpe,Roe),s.sg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,A;switch(y=this.g,i.g){case 1:{for(c=0,o=0,p=new L(n.j);p.a1&&(c.j==(De(),et)?this.b[n]=!0:c.j==Kn&&n>0&&(this.b[n-1]=!0))},s.f=0,v(t1,"AllCrossingsCounter",1838),m(583,1,{},AB),s.b=0,s.d=0,v(t1,"BinaryIndexedTree",583),m(519,1,{},TT);var nye,OH;v(t1,"CrossingsCounter",519),m(1912,1,Yt,fEe),s.Le=function(n,t){return evn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(t1,"CrossingsCounter/lambda$0$Type",1912),m(1913,1,Yt,aEe),s.Le=function(n,t){return nvn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(t1,"CrossingsCounter/lambda$1$Type",1913),m(1914,1,Yt,hEe),s.Le=function(n,t){return tvn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(t1,"CrossingsCounter/lambda$2$Type",1914),m(1915,1,Yt,dEe),s.Le=function(n,t){return ivn(this.a,u(n,12),u(t,12))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(t1,"CrossingsCounter/lambda$3$Type",1915),m(1916,1,it,bEe),s.Ad=function(n){K9n(this.a,u(n,12))},v(t1,"CrossingsCounter/lambda$4$Type",1916),m(1917,1,Ft,gEe),s.Mb=function(n){return Fgn(this.a,u(n,12))},v(t1,"CrossingsCounter/lambda$5$Type",1917),m(1918,1,it,wEe),s.Ad=function(n){eTe(this,n)},v(t1,"CrossingsCounter/lambda$6$Type",1918),m(1919,1,it,cCe),s.Ad=function(n){var t;A9(),T0(this.b,(t=this.a,u(n,12),t))},v(t1,"CrossingsCounter/lambda$7$Type",1919),m(823,1,jh,hM),s.Lb=function(n){return A9(),bi(u(n,12),(me(),vs))},s.Fb=function(n){return this===n},s.Mb=function(n){return A9(),bi(u(n,12),(me(),vs))},v(t1,"CrossingsCounter/lambda$8$Type",823),m(1911,1,{},pEe),v(t1,"HyperedgeCrossingsCounter",1911),m(467,1,{35:1,467:1},cNe),s.Dd=function(n){return CEn(this,u(n,467))},s.b=0,s.c=0,s.e=0,s.f=0;var BBn=v(t1,"HyperedgeCrossingsCounter/Hyperedge",467);m(370,1,{35:1,370:1},AR),s.Dd=function(n){return kOn(this,u(n,370))},s.b=0,s.c=0;var kon=v(t1,"HyperedgeCrossingsCounter/HyperedgeCorner",370);m(518,23,{3:1,35:1,23:1,518:1},vse);var Cx,Tx,jon=yt(t1,"HyperedgeCrossingsCounter/HyperedgeCorner/Type",518,Tt,e4n,jmn),Eon;m(1385,1,uc,TU),s.pg=function(n){return u(C(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?Son:null},s.If=function(n,t){Qxn(this,u(n,37),t)};var Son;v(Lc,"InteractiveNodePlacer",1385),m(1386,1,uc,AC),s.pg=function(n){return u(C(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?xon:null},s.If=function(n,t){$Sn(this,u(n,37),t)};var xon,NH,DH;v(Lc,"LinearSegmentsNodePlacer",1386),m(263,1,{35:1,263:1},poe),s.Dd=function(n){return ngn(this,u(n,263))},s.Fb=function(n){var t;return X(n,263)?(t=u(n,263),this.b==t.b):!1},s.Hb=function(){return this.b},s.Ib=function(){return"ls"+Fa(this.e)},s.a=0,s.b=0,s.c=-1,s.d=-1,s.g=0;var Aon=v(Lc,"LinearSegmentsNodePlacer/LinearSegment",263);m(1388,1,uc,_De),s.pg=function(n){return u(C(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?Mon:null},s.If=function(n,t){qRn(this,u(n,37),t)},s.b=0,s.g=0;var Mon;v(Lc,"NetworkSimplexPlacer",1388),m(1407,1,Yt,f3),s.Le=function(n,t){return oo(u(n,15).a,u(t,15).a)},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Lc,"NetworkSimplexPlacer/0methodref$compare$Type",1407),m(1409,1,Yt,fM),s.Le=function(n,t){return oo(u(n,15).a,u(t,15).a)},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Lc,"NetworkSimplexPlacer/1methodref$compare$Type",1409),m(644,1,{644:1},uCe);var zBn=v(Lc,"NetworkSimplexPlacer/EdgeRep",644);m(405,1,{405:1},cae),s.b=!1;var FBn=v(Lc,"NetworkSimplexPlacer/NodeRep",405);m(500,13,{3:1,4:1,20:1,31:1,56:1,13:1,18:1,16:1,59:1,500:1},pxe),v(Lc,"NetworkSimplexPlacer/Path",500),m(1389,1,{},kk),s.Kb=function(n){return u(n,17).d.i.k},v(Lc,"NetworkSimplexPlacer/Path/lambda$0$Type",1389),m(1390,1,Ft,o_),s.Mb=function(n){return u(n,249)==(zn(),dr)},v(Lc,"NetworkSimplexPlacer/Path/lambda$1$Type",1390),m(1391,1,{},s_),s.Kb=function(n){return u(n,17).d.i},v(Lc,"NetworkSimplexPlacer/Path/lambda$2$Type",1391),m(1392,1,Ft,mEe),s.Mb=function(n){return qOe(rJe(u(n,9)))},v(Lc,"NetworkSimplexPlacer/Path/lambda$3$Type",1392),m(1393,1,Ft,I5),s.Mb=function(n){return F3n(u(n,12))},v(Lc,"NetworkSimplexPlacer/lambda$0$Type",1393),m(1394,1,it,oCe),s.Ad=function(n){Iwn(this.a,this.b,u(n,12))},v(Lc,"NetworkSimplexPlacer/lambda$1$Type",1394),m(1403,1,it,vEe),s.Ad=function(n){sTn(this.a,u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$10$Type",1403),m(1404,1,{},aM),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$11$Type",1404),m(1405,1,it,yEe),s.Ad=function(n){JDn(this.a,u(n,9))},v(Lc,"NetworkSimplexPlacer/lambda$12$Type",1405),m(1406,1,{},jk),s.Kb=function(n){return cl(),ve(u(n,124).e)},v(Lc,"NetworkSimplexPlacer/lambda$13$Type",1406),m(1408,1,{},Ek),s.Kb=function(n){return cl(),ve(u(n,124).e)},v(Lc,"NetworkSimplexPlacer/lambda$15$Type",1408),m(1410,1,Ft,l_),s.Mb=function(n){return cl(),u(n,405).c.k==(zn(),Qi)},v(Lc,"NetworkSimplexPlacer/lambda$17$Type",1410),m(1411,1,Ft,f_),s.Mb=function(n){return cl(),u(n,405).c.j.c.length>1},v(Lc,"NetworkSimplexPlacer/lambda$18$Type",1411),m(1412,1,it,FIe),s.Ad=function(n){Wjn(this.c,this.b,this.d,this.a,u(n,405))},s.c=0,s.d=0,v(Lc,"NetworkSimplexPlacer/lambda$19$Type",1412),m(1395,1,{},a3),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$2$Type",1395),m(1413,1,it,kEe),s.Ad=function(n){$wn(this.a,u(n,12))},s.a=0,v(Lc,"NetworkSimplexPlacer/lambda$20$Type",1413),m(1414,1,{},h3),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$21$Type",1414),m(1415,1,it,jEe),s.Ad=function(n){Hwn(this.a,u(n,9))},v(Lc,"NetworkSimplexPlacer/lambda$22$Type",1415),m(1416,1,Ft,a_),s.Mb=function(n){return qOe(n)},v(Lc,"NetworkSimplexPlacer/lambda$23$Type",1416),m(1417,1,{},_5),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$24$Type",1417),m(1418,1,Ft,EEe),s.Mb=function(n){return Ygn(this.a,u(n,9))},v(Lc,"NetworkSimplexPlacer/lambda$25$Type",1418),m(1419,1,it,sCe),s.Ad=function(n){aCn(this.a,this.b,u(n,9))},v(Lc,"NetworkSimplexPlacer/lambda$26$Type",1419),m(1420,1,Ft,M6),s.Mb=function(n){return cl(),!cc(u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$27$Type",1420),m(1421,1,Ft,Sk),s.Mb=function(n){return cl(),!cc(u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$28$Type",1421),m(1422,1,{},SEe),s.Te=function(n,t){return Pwn(this.a,u(n,25),u(t,25))},v(Lc,"NetworkSimplexPlacer/lambda$29$Type",1422),m(1396,1,{},C6),s.Kb=function(n){return cl(),new wn(null,new v2(new qn(Vn(Ni(u(n,9)).a.Jc(),new ee))))},v(Lc,"NetworkSimplexPlacer/lambda$3$Type",1396),m(1397,1,Ft,xk),s.Mb=function(n){return cl(),Ryn(u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$4$Type",1397),m(1398,1,it,xEe),s.Ad=function(n){Q_n(this.a,u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$5$Type",1398),m(1399,1,{},h_),s.Kb=function(n){return cl(),new wn(null,new pn(u(n,25).a,16))},v(Lc,"NetworkSimplexPlacer/lambda$6$Type",1399),m(1400,1,Ft,d3),s.Mb=function(n){return cl(),u(n,9).k==(zn(),Qi)},v(Lc,"NetworkSimplexPlacer/lambda$7$Type",1400),m(1401,1,{},d_),s.Kb=function(n){return cl(),new wn(null,new v2(new qn(Vn(bh(u(n,9)).a.Jc(),new ee))))},v(Lc,"NetworkSimplexPlacer/lambda$8$Type",1401),m(1402,1,Ft,Rp),s.Mb=function(n){return cl(),B3n(u(n,17))},v(Lc,"NetworkSimplexPlacer/lambda$9$Type",1402),m(1384,1,uc,MC),s.pg=function(n){return u(C(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?Con:null},s.If=function(n,t){TLn(u(n,37),t)};var Con;v(Lc,"SimpleNodePlacer",1384),m(185,1,{185:1},lv),s.Ib=function(){var n;return n="",this.c==(ah(),pp)?n+=fy:this.c==Ud&&(n+=ly),this.o==(Da(),Ag)?n+=XZ:this.o==Ya?n+="UP":n+="BALANCED",n},v(Q0,"BKAlignedLayout",185),m(509,23,{3:1,35:1,23:1,509:1},yse);var Ud,pp,Ton=yt(Q0,"BKAlignedLayout/HDirection",509,Tt,t4n,Emn),Oon;m(508,23,{3:1,35:1,23:1,508:1},kse);var Ag,Ya,Non=yt(Q0,"BKAlignedLayout/VDirection",508,Tt,n4n,Smn),Don;m(1664,1,{},lCe),v(Q0,"BKAligner",1664),m(1667,1,{},OHe),v(Q0,"BKCompactor",1667),m(652,1,{652:1},dM),s.a=0,v(Q0,"BKCompactor/ClassEdge",652),m(456,1,{456:1},dxe),s.a=null,s.b=0,v(Q0,"BKCompactor/ClassNode",456),m(1387,1,uc,ECe),s.pg=function(n){return u(C(u(n,37),(me(),po)),22).Gc((Dc(),Kl))?Ion:null},s.If=function(n,t){sBn(this,u(n,37),t)},s.d=!1;var Ion;v(Q0,"BKNodePlacer",1387),m(1665,1,{},bM),s.d=0,v(Q0,"NeighborhoodInformation",1665),m(1666,1,Yt,AEe),s.Le=function(n,t){return l8n(this,u(n,49),u(t,49))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Q0,"NeighborhoodInformation/NeighborComparator",1666),m(809,1,{}),v(Q0,"ThresholdStrategy",809),m(1795,809,{},mxe),s.vg=function(n,t,i){return this.a.o==(Da(),Ya)?Ki:Dr},s.wg=function(){},v(Q0,"ThresholdStrategy/NullThresholdStrategy",1795),m(576,1,{576:1},hCe),s.c=!1,s.d=!1,v(Q0,"ThresholdStrategy/Postprocessable",576),m(1796,809,{},vxe),s.vg=function(n,t,i){var r,c,o;return c=t==i,r=this.a.a[i.p]==t,c||r?(o=n,this.a.c==(ah(),pp)?(c&&(o=VW(this,t,!0)),!isNaN(o)&&!isFinite(o)&&r&&(o=VW(this,i,!1))):(c&&(o=VW(this,t,!0)),!isNaN(o)&&!isFinite(o)&&r&&(o=VW(this,i,!1))),o):n},s.wg=function(){for(var n,t,i,r,c;this.d.b!=0;)c=u(A_e(this.d),576),r=hKe(this,c),r.a&&(n=r.a,i=Re(this.a.f[this.a.g[c.b.p].p]),!(!i&&!cc(n)&&n.c.i.c==n.d.i.c)&&(t=bUe(this,c),t||vTe(this.e,c)));for(;this.e.a.c.length!=0;)bUe(this,u(T1e(this.e),576))},v(Q0,"ThresholdStrategy/SimpleThresholdStrategy",1796),m(635,1,{635:1,188:1,196:1},Bp),s.bg=function(){return sze(this)},s.og=function(){return sze(this)};var fre;v(qee,"EdgeRouterFactory",635),m(1445,1,uc,_U),s.pg=function(n){return vDn(u(n,37))},s.If=function(n,t){RLn(u(n,37),t)};var _on,Lon,Pon,$on,Ron,tye,Bon,zon;v(qee,"OrthogonalEdgeRouter",1445),m(1438,1,uc,jCe),s.pg=function(n){return uAn(u(n,37))},s.If=function(n,t){oRn(this,u(n,37),t)};var Fon,Jon,Hon,Gon,vD,qon;v(qee,"PolylineEdgeRouter",1438),m(1439,1,jh,zp),s.Lb=function(n){return u1e(u(n,9))},s.Fb=function(n){return this===n},s.Mb=function(n){return u1e(u(n,9))},v(qee,"PolylineEdgeRouter/1",1439),m(1851,1,Ft,T6),s.Mb=function(n){return u(n,133).c==(ha(),sb)},v(va,"HyperEdgeCycleDetector/lambda$0$Type",1851),m(1852,1,{},gM),s.Xe=function(n){return u(n,133).d},v(va,"HyperEdgeCycleDetector/lambda$1$Type",1852),m(1853,1,Ft,wM),s.Mb=function(n){return u(n,133).c==(ha(),sb)},v(va,"HyperEdgeCycleDetector/lambda$2$Type",1853),m(1854,1,{},O6),s.Xe=function(n){return u(n,133).d},v(va,"HyperEdgeCycleDetector/lambda$3$Type",1854),m(1855,1,{},N6),s.Xe=function(n){return u(n,133).d},v(va,"HyperEdgeCycleDetector/lambda$4$Type",1855),m(1856,1,{},b_),s.Xe=function(n){return u(n,133).d},v(va,"HyperEdgeCycleDetector/lambda$5$Type",1856),m(116,1,{35:1,116:1},mO),s.Dd=function(n){return tgn(this,u(n,116))},s.Fb=function(n){var t;return X(n,116)?(t=u(n,116),this.g==t.g):!1},s.Hb=function(){return this.g},s.Ib=function(){var n,t,i,r;for(n=new il("{"),r=new L(this.n);r.a"+this.b+" ("+vpn(this.c)+")"},s.d=0,v(va,"HyperEdgeSegmentDependency",133),m(515,23,{3:1,35:1,23:1,515:1},jse);var sb,Am,Uon=yt(va,"HyperEdgeSegmentDependency/DependencyType",515,Tt,i4n,xmn),Xon;m(1857,1,{},MEe),v(va,"HyperEdgeSegmentSplitter",1857),m(1858,1,{},dAe),s.a=0,s.b=0,v(va,"HyperEdgeSegmentSplitter/AreaRating",1858),m(340,1,{340:1},QK),s.a=0,s.b=0,s.c=0,v(va,"HyperEdgeSegmentSplitter/FreeArea",340),m(1859,1,Yt,g_),s.Le=function(n,t){return a2n(u(n,116),u(t,116))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(va,"HyperEdgeSegmentSplitter/lambda$0$Type",1859),m(1860,1,it,JIe),s.Ad=function(n){C6n(this.a,this.d,this.c,this.b,u(n,116))},s.b=0,v(va,"HyperEdgeSegmentSplitter/lambda$1$Type",1860),m(1861,1,{},L5),s.Kb=function(n){return new wn(null,new pn(u(n,116).e,16))},v(va,"HyperEdgeSegmentSplitter/lambda$2$Type",1861),m(1862,1,{},Ak),s.Kb=function(n){return new wn(null,new pn(u(n,116).j,16))},v(va,"HyperEdgeSegmentSplitter/lambda$3$Type",1862),m(1863,1,{},w_),s.We=function(n){return ne(re(n))},v(va,"HyperEdgeSegmentSplitter/lambda$4$Type",1863),m(653,1,{},jV),s.a=0,s.b=0,s.c=0,v(va,"OrthogonalRoutingGenerator",653),m(1668,1,{},pM),s.Kb=function(n){return new wn(null,new pn(u(n,116).e,16))},v(va,"OrthogonalRoutingGenerator/lambda$0$Type",1668),m(1669,1,{},mM),s.Kb=function(n){return new wn(null,new pn(u(n,116).j,16))},v(va,"OrthogonalRoutingGenerator/lambda$1$Type",1669),m(661,1,{}),v(Uee,"BaseRoutingDirectionStrategy",661),m(1849,661,{},yxe),s.xg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I;if(!(n.r&&!n.q))for(p=t+n.o*i,b=new L(n.n);b.aEh&&(o=p,c=n,r=new je(y,o),Vt(l.a,r),Uw(this,l,c,r,!1),S=n.r,S&&(A=ne(re(Yu(S.e,0))),r=new je(A,o),Vt(l.a,r),Uw(this,l,c,r,!1),o=t+S.o*i,c=S,r=new je(A,o),Vt(l.a,r),Uw(this,l,c,r,!1)),r=new je(I,o),Vt(l.a,r),Uw(this,l,c,r,!1)))},s.yg=function(n){return n.i.n.a+n.n.a+n.a.a},s.zg=function(){return De(),bt},s.Ag=function(){return De(),Xn},v(Uee,"NorthToSouthRoutingStrategy",1849),m(1850,661,{},kxe),s.xg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I;if(!(n.r&&!n.q))for(p=t-n.o*i,b=new L(n.n);b.aEh&&(o=p,c=n,r=new je(y,o),Vt(l.a,r),Uw(this,l,c,r,!1),S=n.r,S&&(A=ne(re(Yu(S.e,0))),r=new je(A,o),Vt(l.a,r),Uw(this,l,c,r,!1),o=t-S.o*i,c=S,r=new je(A,o),Vt(l.a,r),Uw(this,l,c,r,!1)),r=new je(I,o),Vt(l.a,r),Uw(this,l,c,r,!1)))},s.yg=function(n){return n.i.n.a+n.n.a+n.a.a},s.zg=function(){return De(),Xn},s.Ag=function(){return De(),bt},v(Uee,"SouthToNorthRoutingStrategy",1850),m(1848,661,{},jxe),s.xg=function(n,t,i){var r,c,o,l,f,h,b,p,y,S,A,O,I;if(!(n.r&&!n.q))for(p=t+n.o*i,b=new L(n.n);b.aEh&&(o=p,c=n,r=new je(o,y),Vt(l.a,r),Uw(this,l,c,r,!0),S=n.r,S&&(A=ne(re(Yu(S.e,0))),r=new je(o,A),Vt(l.a,r),Uw(this,l,c,r,!0),o=t+S.o*i,c=S,r=new je(o,A),Vt(l.a,r),Uw(this,l,c,r,!0)),r=new je(o,I),Vt(l.a,r),Uw(this,l,c,r,!0)))},s.yg=function(n){return n.i.n.b+n.n.b+n.a.b},s.zg=function(){return De(),et},s.Ag=function(){return De(),Kn},v(Uee,"WestToEastRoutingStrategy",1848),m(812,1,{},fge),s.Ib=function(){return Fa(this.a)},s.b=0,s.c=!1,s.d=!1,s.f=0,v(tm,"NubSpline",812),m(410,1,{410:1},VUe,S_e),v(tm,"NubSpline/PolarCP",410),m(1440,1,uc,yHe),s.pg=function(n){return XAn(u(n,37))},s.If=function(n,t){MRn(this,u(n,37),t)};var Kon,Von,Yon,Qon,Won;v(tm,"SplineEdgeRouter",1440),m(273,1,{273:1},QR),s.Ib=function(){return this.a+" ->("+this.c+") "+this.b},s.c=0,v(tm,"SplineEdgeRouter/Dependency",273),m(454,23,{3:1,35:1,23:1,454:1},Ese);var lb,Jv,Zon=yt(tm,"SplineEdgeRouter/SideToProcess",454,Tt,r4n,Amn),esn;m(1441,1,Ft,p1),s.Mb=function(n){return iS(),!u(n,132).o},v(tm,"SplineEdgeRouter/lambda$0$Type",1441),m(1442,1,{},hd),s.Xe=function(n){return iS(),u(n,132).v+1},v(tm,"SplineEdgeRouter/lambda$1$Type",1442),m(1443,1,it,fCe),s.Ad=function(n){G3n(this.a,this.b,u(n,49))},v(tm,"SplineEdgeRouter/lambda$2$Type",1443),m(1444,1,it,aCe),s.Ad=function(n){q3n(this.a,this.b,u(n,49))},v(tm,"SplineEdgeRouter/lambda$3$Type",1444),m(132,1,{35:1,132:1},iqe,wge),s.Dd=function(n){return ign(this,u(n,132))},s.b=0,s.e=!1,s.f=0,s.g=0,s.j=!1,s.k=!1,s.n=0,s.o=!1,s.p=!1,s.q=!1,s.s=0,s.u=0,s.v=0,s.F=0,v(tm,"SplineSegment",132),m(457,1,{457:1},Fp),s.a=0,s.b=!1,s.c=!1,s.d=!1,s.e=!1,s.f=0,v(tm,"SplineSegment/EdgeInformation",457),m(1167,1,{},vM),v(X1,twe,1167),m(1168,1,Yt,p_),s.Le=function(n,t){return kTn(u(n,120),u(t,120))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(X1,ZYe,1168),m(1166,1,{},_Ae),v(X1,"MrTree",1166),m(398,23,{3:1,35:1,23:1,398:1,188:1,196:1},w$),s.bg=function(){return Aqe(this)},s.og=function(){return Aqe(this)};var IH,Ox,Nx,Dx,iye=yt(X1,"TreeLayoutPhases",398,Tt,u6n,Mmn),nsn;m(1082,214,Qw,oNe),s.kf=function(n,t){var i,r,c,o,l,f,h,b;for(Re($e(ye(n,(Mu(),Cye))))||HT((i=new hj((_b(),new w0(n))),i)),l=t.dh(Vee),l.Tg("build tGraph",1),f=(h=new QT,$u(h,n),he(h,(Ci(),_x),n),b=new pt,i_n(n,h,b),y_n(n,h,b),h),l.Ug(),l=t.dh(Vee),l.Tg("Split graph",1),o=l_n(this.a,f),l.Ug(),c=new L(o);c.a"+Ub(this.c):"e_"+Oi(this)},v(OS,"TEdge",65),m(120,150,{3:1,120:1,105:1,150:1},QT),s.Ib=function(){var n,t,i,r,c;for(c=null,r=St(this.b,0);r.b!=r.d.c;)i=u(jt(r),40),c+=(i.c==null||i.c.length==0?"n_"+i.g:"n_"+i.c)+` `;for(t=St(this.a,0);t.b!=t.d.c;)n=u(jt(t),65),c+=(n.b&&n.c?Ub(n.b)+"->"+Ub(n.c):"e_"+Oi(n))+` -`;return c};var JBn=v(OS,"TGraph",120);m(633,494,{3:1,494:1,633:1,105:1,150:1}),v(OS,"TShape",633),m(40,633,{3:1,494:1,40:1,633:1,105:1,150:1},nQ),s.Ib=function(){return Ub(this)};var _H=v(OS,"TNode",40);m(236,1,Wh,S1),s.Ic=function(n){rc(this,n)},s.Jc=function(){var n;return n=St(this.a.d,0),new E3(n)},v(OS,"TNode/2",236),m(334,1,Fr,E3),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(jt(this.a),65).c},s.Ob=function(){return YT(this.a)},s.Qb=function(){CY(this.a)},v(OS,"TNode/2/1",334),m(1893,1,Ai,vo),s.If=function(n,t){iBn(this,u(n,120),t)},v(go,"CompactionProcessor",1893),m(1894,1,Yt,DEe),s.Le=function(n,t){return C7n(this.a,u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$0$Type",1894),m(1895,1,Ft,bTe),s.Mb=function(n){return q5n(this.b,this.a,u(n,49))},s.a=0,s.b=0,v(go,"CompactionProcessor/lambda$1$Type",1895),m(1904,1,Yt,Ml),s.Le=function(n,t){return Rvn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$10$Type",1904),m(1905,1,Yt,Tk),s.Le=function(n,t){return opn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$11$Type",1905),m(1906,1,Yt,P5),s.Le=function(n,t){return Bvn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$12$Type",1906),m(1896,1,Ft,IEe),s.Mb=function(n){return Xwn(this.a,u(n,49))},s.a=0,v(go,"CompactionProcessor/lambda$2$Type",1896),m(1897,1,Ft,_Ee),s.Mb=function(n){return Kwn(this.a,u(n,49))},s.a=0,v(go,"CompactionProcessor/lambda$3$Type",1897),m(1898,1,Ft,b3),s.Mb=function(n){return u(n,40).c.indexOf(DF)==-1},v(go,"CompactionProcessor/lambda$4$Type",1898),m(1899,1,{},LEe),s.Kb=function(n){return Pyn(this.a,u(n,40))},s.a=0,v(go,"CompactionProcessor/lambda$5$Type",1899),m(K0,1,{},PEe),s.Kb=function(n){return Y9n(this.a,u(n,40))},s.a=0,v(go,"CompactionProcessor/lambda$6$Type",K0),m(1901,1,Yt,$Ee),s.Le=function(n,t){return r9n(this.a,u(n,240),u(t,240))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$7$Type",1901),m(1902,1,Yt,REe),s.Le=function(n,t){return c9n(this.a,u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$8$Type",1902),m(1903,1,Yt,Ck),s.Le=function(n,t){return spn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$9$Type",1903),m(1891,1,Ai,D6),s.If=function(n,t){ZDn(u(n,120),t)},v(go,"DirectionProcessor",1891),m(1883,1,Ai,sNe),s.If=function(n,t){v_n(this,u(n,120),t)},v(go,"FanProcessor",1883),m(1251,1,Ai,$5),s.If=function(n,t){gXe(u(n,120),t)},v(go,"GraphBoundsProcessor",1251),m(1252,1,{},Zq),s.We=function(n){return u(n,40).e.a},v(go,"GraphBoundsProcessor/lambda$0$Type",1252),m(1253,1,{},Bs),s.We=function(n){return u(n,40).e.b},v(go,"GraphBoundsProcessor/lambda$1$Type",1253),m(1254,1,{},yM),s.We=function(n){return Cgn(u(n,40))},v(go,"GraphBoundsProcessor/lambda$2$Type",1254),m(1255,1,{},kM),s.We=function(n){return Ogn(u(n,40))},v(go,"GraphBoundsProcessor/lambda$3$Type",1255),m(264,23,{3:1,35:1,23:1,264:1,196:1},gw),s.bg=function(){switch(this.g){case 0:return new Pxe;case 1:return new sNe;case 2:return new Lxe;case 3:return new EM;case 4:return new v_;case 8:return new m_;case 5:return new D6;case 6:return new uh;case 7:return new vo;case 9:return new $5;case 10:return new nl;default:throw $(new Gn(nee+(this.f!=null?this.f:""+this.g)))}};var rye,cye,uye,oye,sye,lye,fye,aye,hye,dye,are,HBn=yt(go,tee,264,Ct,oze,Tmn),tsn;m(1890,1,Ai,m_),s.If=function(n,t){nRn(u(n,120),t)},v(go,"LevelCoordinatesProcessor",1890),m(1888,1,Ai,v_),s.If=function(n,t){ENn(this,u(n,120),t)},s.a=0,v(go,"LevelHeightProcessor",1888),m(1889,1,Wh,eU),s.Ic=function(n){rc(this,n)},s.Jc=function(){return jn(),p9(),d7},v(go,"LevelHeightProcessor/1",1889),m(1884,1,Ai,Lxe),s.If=function(n,t){PDn(this,u(n,120),t)},v(go,"LevelProcessor",1884),m(1885,1,Ft,jM),s.Mb=function(n){return Re($e(T(u(n,40),(Ti(),fb))))},v(go,"LevelProcessor/lambda$0$Type",1885),m(1886,1,Ai,EM),s.If=function(n,t){NTn(this,u(n,120),t)},s.a=0,v(go,"NeighborsProcessor",1886),m(1887,1,Wh,SM),s.Ic=function(n){rc(this,n)},s.Jc=function(){return jn(),p9(),d7},v(go,"NeighborsProcessor/1",1887),m(1892,1,Ai,uh),s.If=function(n,t){p_n(this,u(n,120),t)},s.a=0,v(go,"NodePositionProcessor",1892),m(1882,1,Ai,Pxe),s.If=function(n,t){tPn(this,u(n,120),t)},v(go,"RootProcessor",1882),m(1907,1,Ai,nl),s.If=function(n,t){vSn(u(n,120),t)},v(go,"Untreeifyer",1907),m(385,23,{3:1,35:1,23:1,385:1},sK);var yD,hre,bye,gye=yt(LN,"EdgeRoutingMode",385,Ct,eyn,Cmn),isn,kD,_7,dre,wye,pye,bre,gre,mye,wre,vye,pre,Ix,mre,LH,PH,Yf,Ea,L7,_x,Lx,Xd,yye,rsn,vre,fb,jD,ED;m(846,1,qa,xT),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Jpe),""),VQe),"Turns on Tree compaction which decreases the size of the whole tree by placing nodes of multiple levels in one large level"),(Pn(),!1)),(cg(),Sr)),Yi),nn((ph(),Mn))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Hpe),""),"Edge End Texture Length"),"Should be set to the length of the texture at the end of an edge. This value can be used to improve the Edge Routing."),7),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Gpe),""),"Tree Level"),"The index for the tree level the node is in"),ve(0)),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,qpe),""),VQe),"When set to a positive number this option will force the algorithm to place the node to the specified position within the trees layer if weighting is set to constraint"),ve(-1)),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Upe),""),"Weighting of Nodes"),"Which weighting to use when computing a node order."),Eye),Ri),Lye),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Xpe),""),"Edge Routing Mode"),"Chooses an Edge Routing algorithm."),kye),Ri),gye),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Kpe),""),"Search Order"),"Which search order to use when computing a spanning tree."),jye),Ri),$ye),nn(Mn)))),BVe((new fP,n))};var csn,usn,osn,kye,ssn,lsn,jye,fsn,asn,Eye;v(LN,"MrTreeMetaDataProvider",846),m(990,1,qa,fP),s.tf=function(n){BVe(n)};var hsn,Sye,xye,mp,Aye,Mye,yre,dsn,bsn,gsn,wsn,psn,msn,vsn,Tye,Cye,Oye,ysn,Hv,$H,Nye,ksn,Dye,kre,jsn,Esn,Ssn,Iye,xsn,Nh,_ye;v(LN,"MrTreeOptions",990),m(991,1,{},Ok),s.uf=function(){var n;return n=new oNe,n},s.vf=function(n){},v(LN,"MrTreeOptions/MrtreeFactory",991),m(353,23,{3:1,35:1,23:1,353:1},p$);var jre,RH,Ere,Sre,Lye=yt(LN,"OrderWeighting",353,Ct,f6n,Omn),Asn;m(425,23,{3:1,35:1,23:1,425:1},Sse);var Pye,xre,$ye=yt(LN,"TreeifyingOrder",425,Ct,c4n,Nmn),Msn;m(1446,1,uc,DU),s.pg=function(n){return u(n,120),Tsn},s.If=function(n,t){c7n(this,u(n,120),t)};var Tsn;v("org.eclipse.elk.alg.mrtree.p1treeify","DFSTreeifyer",1446),m(1447,1,uc,oP),s.pg=function(n){return u(n,120),Csn},s.If=function(n,t){zDn(this,u(n,120),t)};var Csn;v(Q8,"NodeOrderer",1447),m(1454,1,{},nU),s.rd=function(n){return fDe(n)},v(Q8,"NodeOrderer/0methodref$lambda$6$Type",1454),m(1448,1,Ft,E_),s.Mb=function(n){return R4(),Re($e(T(u(n,40),(Ti(),fb))))},v(Q8,"NodeOrderer/lambda$0$Type",1448),m(1449,1,Ft,S_),s.Mb=function(n){return R4(),u(T(u(n,40),(Mu(),Hv)),15).a<0},v(Q8,"NodeOrderer/lambda$1$Type",1449),m(1450,1,Ft,zEe),s.Mb=function(n){return U8n(this.a,u(n,40))},v(Q8,"NodeOrderer/lambda$2$Type",1450),m(1451,1,Ft,BEe),s.Mb=function(n){return $yn(this.a,u(n,40))},v(Q8,"NodeOrderer/lambda$3$Type",1451),m(1452,1,Yt,TM),s.Le=function(n,t){return a8n(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Q8,"NodeOrderer/lambda$4$Type",1452),m(1453,1,Ft,x_),s.Mb=function(n){return R4(),u(T(u(n,40),(Ti(),gre)),15).a!=0},v(Q8,"NodeOrderer/lambda$5$Type",1453),m(1455,1,uc,IU),s.pg=function(n){return u(n,120),Osn},s.If=function(n,t){UIn(this,u(n,120),t)},s.b=0;var Osn;v("org.eclipse.elk.alg.mrtree.p3place","NodePlacer",1455),m(1456,1,uc,ST),s.pg=function(n){return u(n,120),Nsn},s.If=function(n,t){MIn(u(n,120),t)};var Nsn,GBn=v(Qs,"EdgeRouter",1456);m(1458,1,Yt,Nk),s.Le=function(n,t){return oo(u(n,15).a,u(t,15).a)},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/0methodref$compare$Type",1458),m(1463,1,{},xM),s.We=function(n){return ne(re(n))},v(Qs,"EdgeRouter/1methodref$doubleValue$Type",1463),m(1465,1,Yt,iw),s.Le=function(n,t){return ki(ne(re(n)),ne(re(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/2methodref$compare$Type",1465),m(1467,1,Yt,Dk),s.Le=function(n,t){return ki(ne(re(n)),ne(re(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/3methodref$compare$Type",1467),m(1469,1,{},I6),s.We=function(n){return ne(re(n))},v(Qs,"EdgeRouter/4methodref$doubleValue$Type",1469),m(1471,1,Yt,AM),s.Le=function(n,t){return ki(ne(re(n)),ne(re(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/5methodref$compare$Type",1471),m(1473,1,Yt,MM),s.Le=function(n,t){return ki(ne(re(n)),ne(re(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/6methodref$compare$Type",1473),m(1457,1,{},y_),s.Kb=function(n){return P1(),u(T(u(n,40),(Mu(),Nh)),15)},v(Qs,"EdgeRouter/lambda$0$Type",1457),m(1468,1,{},k_),s.Kb=function(n){return ypn(u(n,40))},v(Qs,"EdgeRouter/lambda$11$Type",1468),m(1470,1,{},wTe),s.Kb=function(n){return J3n(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Qs,"EdgeRouter/lambda$13$Type",1470),m(1472,1,{},gTe),s.Kb=function(n){return Epn(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Qs,"EdgeRouter/lambda$15$Type",1472),m(1474,1,Yt,j_),s.Le=function(n,t){return QEn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$17$Type",1474),m(1475,1,Yt,tU),s.Le=function(n,t){return WEn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$18$Type",1475),m(1476,1,Yt,A_),s.Le=function(n,t){return eSn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$19$Type",1476),m(1459,1,Ft,FEe),s.Mb=function(n){return y4n(this.a,u(n,40))},s.a=0,v(Qs,"EdgeRouter/lambda$2$Type",1459),m(1477,1,Yt,M_),s.Le=function(n,t){return ZEn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$20$Type",1477),m(1460,1,Yt,T_),s.Le=function(n,t){return N3n(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$3$Type",1460),m(1461,1,Yt,CM),s.Le=function(n,t){return D3n(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$4$Type",1461),m(1462,1,{},C_),s.Kb=function(n){return kpn(u(n,40))},v(Qs,"EdgeRouter/lambda$5$Type",1462),m(1464,1,{},pTe),s.Kb=function(n){return H3n(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Qs,"EdgeRouter/lambda$7$Type",1464),m(1466,1,{},mTe),s.Kb=function(n){return jpn(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Qs,"EdgeRouter/lambda$9$Type",1466),m(662,1,{662:1},lHe),s.e=0,s.f=!1,s.g=!1,v(Qs,"MultiLevelEdgeNodeNodeGap",662),m(1864,1,Yt,O_),s.Le=function(n,t){return I4n(u(n,240),u(t,240))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"MultiLevelEdgeNodeNodeGap/lambda$0$Type",1864),m(1865,1,Yt,N_),s.Le=function(n,t){return _4n(u(n,240),u(t,240))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"MultiLevelEdgeNodeNodeGap/lambda$1$Type",1865);var Gv;m(487,23,{3:1,35:1,23:1,487:1,188:1,196:1},xse),s.bg=function(){return XFe(this)},s.og=function(){return XFe(this)};var BH,qv,Rye=yt(Vpe,"RadialLayoutPhases",487,Ct,u4n,Dmn),Dsn;m(1083,214,Qw,$Ae),s.kf=function(n,t){var i,r,c,o,l,f;if(i=qUe(this,n),t.Tg("Radial layout",i.c.length),Re($e(ye(n,(J0(),Vye))))||HC((r=new hj((_b(),new w0(n))),r)),f=YAn(n),ji(n,(B3(),Gv),f),!f)throw $(new Gn("The given graph is not a tree!"));for(c=ne(re(ye(n,JH))),c==0&&(c=vqe(n)),ji(n,JH,c),l=new L(qUe(this,n));l.a=3)for(fe=u(K(te,0),26),Ie=u(K(te,1),26),o=0;o+2=fe.f+Ie.f+p||Ie.f>=be.f+fe.f+p){cn=!0;break}else++o;else cn=!0;if(!cn){for(S=te.i,f=new ut(te);f.e!=f.i.gc();)l=u(ft(f),26),ji(l,(Xt(),PD),ve(S)),--S;kKe(n,new i4),t.Ug();return}for(i=(RC(this.a),fa(this.a,(WB(),Px),u(ye(n,A6e),188)),fa(this.a,HH,u(ye(n,y6e),188)),fa(this.a,$re,u(ye(n,E6e),188)),Fse(this.a,(Tn=new or,qt(Tn,Px,(kz(),zre)),qt(Tn,HH,Bre),Re($e(ye(n,m6e)))&&qt(Tn,Px,Fre),Re($e(ye(n,p6e)))&&qt(Tn,Px,Rre),Tn)),rN(this.a,n)),b=1/i.c.length,O=new L(i);O.a0&&gFe((Yn(t-1,n.length),n.charCodeAt(t-1)),aQe);)--t;if(r>=t)throw $(new Gn("The given string does not contain any numbers."));if(c=K2((Yr(r,t,n.length),n.substr(r,t-r)),`,|;|\r| -`),c.length!=2)throw $(new Gn("Exactly two numbers are expected, "+c.length+" were found."));try{this.a=F2(J2(c[0])),this.b=F2(J2(c[1]))}catch(o){throw o=sr(o),X(o,131)?(i=o,$(new Gn(hQe+i))):$(o)}},s.Ib=function(){return"("+this.a+","+this.b+")"},s.a=0,s.b=0;var Lr=v(CN,"KVector",8);m(78,66,{3:1,4:1,20:1,31:1,56:1,18:1,66:1,16:1,78:1,414:1},xs,qP,NOe),s.Nc=function(){return Mkn(this)},s.ag=function(n){var t,i,r,c,o,l;r=K2(n,`,|;|\\(|\\)|\\[|\\]|\\{|\\}| | | -`),qs(this);try{for(i=0,o=0,c=0,l=0;i0&&(o%2==0?c=F2(r[i]):l=F2(r[i]),o>0&&o%2!=0&&Vt(this,new je(c,l)),++o),++i}catch(f){throw f=sr(f),X(f,131)?(t=f,$(new Gn("The given string does not match the expected format for vectors."+t))):$(f)}},s.Ib=function(){var n,t,i;for(n=new il("("),t=St(this,0);t.b!=t.d.c;)i=u(jt(t),8),Kt(n,i.a+","+i.b),t.b!=t.d.c&&(n.a+="; ");return(n.a+=")",n).a};var l9e=v(CN,"KVectorChain",78);m(256,23,{3:1,35:1,23:1,256:1},Rj);var sce,ZH,eG,CD,OD,nG,f9e=yt(Oo,"Alignment",256,Ct,T9n,c3n),dfn;m(975,1,qa,CT),s.tf=function(n){rKe(n)};var a9e,lce,bfn,h9e,d9e,gfn,b9e,wfn,pfn,g9e,w9e,mfn;v(Oo,"BoxLayouterOptions",975),m(976,1,{},GM),s.uf=function(){var n;return n=new hL,n},s.vf=function(n){},v(Oo,"BoxLayouterOptions/BoxFactory",976),m(299,23,{3:1,35:1,23:1,299:1},Bj);var Gx,fce,qx,Ux,Xx,ace,hce=yt(Oo,"ContentAlignment",299,Ct,C9n,u3n),vfn;m(689,1,qa,TT),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,pWe),""),"Layout Algorithm"),"Select a specific layout algorithm."),(cg(),By)),ze),nn((ph(),Mn))))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,mWe),""),"Resolved Layout Algorithm"),"Meta data associated with the selected algorithm."),Wa),XBn),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ppe),""),"Alignment"),"Alignment of the selected node relative to other nodes; the exact meaning depends on the used algorithm."),p9e),Ri),f9e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,G8),""),"Aspect Ratio"),"The desired aspect ratio of the drawing, that is the quotient of width by height."),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,N2e),""),"Bend Points"),"A fixed list of bend points for the edge. This is used by the 'Fixed Layout' algorithm to specify a pre-defined routing for an edge. The vector chain must include the source point, any bend points, and the target point, so it must have at least two points."),Wa),l9e),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,TF),""),"Content Alignment"),"Specifies how the content of a node are aligned. Each node can individually control the alignment of its contents. I.e. if a node should be aligned top left in its parent node, the parent node should specify that option."),v9e),Ry),hce),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,_N),""),"Debug Mode"),"Whether additional debug information shall be generated."),(Pn(),!1)),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Fee),""),"Direction"),"Overall direction of edges: horizontal (right / left) or vertical (down / up)."),y9e),Ri),Vx),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,IN),""),"Edge Routing"),"What kind of edge routing style should be applied for the content of a parent node. Algorithms may also set this option to single edges in order to mark them as splines. The bend point list of edges with this option set to SPLINES must be interpreted as control points for a piecewise cubic spline."),E9e),Ri),Ace),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,C2e),""),"Expand Nodes"),"If active, nodes are expanded to fill the area of their parent."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,MF),""),"Hierarchy Handling"),"Determines whether separate layout runs are triggered for different compound nodes in a hierarchical graph. Setting a node's hierarchy handling to `INCLUDE_CHILDREN` will lay out that node and all of its descendants in a single layout run, until a descendant is encountered which has its hierarchy handling set to `SEPARATE_CHILDREN`. In general, `SEPARATE_CHILDREN` will ensure that a new layout run is triggered for a node with that setting. Including multiple levels of hierarchy in a single layout run may allow cross-hierarchical edges to be laid out properly. If the root node is set to `INHERIT` (or not set at all), the default behavior is `SEPARATE_CHILDREN`."),x9e),Ri),b8e),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,nm),""),"Padding"),"The padding to be left to a parent element's border when placing child elements. This can also serve as an output option of a layout algorithm if node size calculation is setup appropriately."),P9e),Wa),j3e),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,jS),""),"Interactive"),"Whether the algorithm should be run in interactive mode for the content of a parent node. What this means exactly depends on how the specific algorithm interprets this option. Usually in the interactive mode algorithms try to modify the current layout as little as possible."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,OF),""),"interactive Layout"),"Whether the graph should be changeable interactively and by setting constraints"),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ES),""),"Omit Node Micro Layout"),"Node micro layout comprises the computation of node dimensions (if requested), the placement of ports and their labels, and the placement of node labels. The functionality is implemented independent of any specific layout algorithm and shouldn't have any negative impact on the layout algorithm's performance itself. Yet, if any unforeseen behavior occurs, this option allows to deactivate the micro layout."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,WZ),""),"Port Constraints"),"Defines constraints of the position of the ports of a node."),F9e),Ri),p8e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,CF),""),"Position"),"The position of a node, port, or label. This is used by the 'Fixed Layout' algorithm to specify a pre-defined position."),Wa),Lr),Mi(fr,z(B(Qa,1),ke,160,0,[Kd,Q1]))))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,EN),""),"Priority"),"Defines the priority of an object; its meaning depends on the specific layout algorithm and the context where it is used."),hc),jr),Mi(fr,z(B(Qa,1),ke,160,0,[Sa]))))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,sF),""),"Randomization Seed"),"Seed used for pseudo-random number generators to control the layout algorithm. If the value is 0, the seed shall be determined pseudo-randomly (e.g. from the system time)."),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,kS),""),"Separate Connected Components"),"Whether each connected component should be processed separately."),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Tpe),""),"Junction Points"),"This option is not used as option, but as output of the layout algorithms. It is attached to edges and determines the points where junction symbols should be drawn in order to represent hyperedges with orthogonal routing. Whether such points are computed depends on the chosen layout algorithm and edge routing style. The points are put into the vector chain with no specific order."),T9e),Wa),l9e),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Dpe),""),"Comment Box"),"Whether the node should be regarded as a comment box instead of a regular node. In that case its placement should be similar to how labels are handled. Any edges incident to a comment box specify to which graph elements the comment is related."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ipe),""),"Hypernode"),"Whether the node should be handled as a hypernode."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,yBn),""),"Label Manager"),"Label managers can shorten labels upon a layout algorithm's request."),Wa),ZBn),Mi(Mn,z(B(Qa,1),ke,160,0,[Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vWe),""),"Softwrapping Fuzziness"),"Determines the amount of fuzziness to be used when performing softwrapping on labels. The value expresses the percent of overhang that is permitted for each line. If the next line would take up less space than this threshold, it is appended to the current line instead of being placed in a new line."),0),ec),gr),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Lpe),""),"Margins"),"Margins define additional space around the actual bounds of a graph element. For instance, ports or labels being placed on the outside of a node's border might introduce such a margin. The margin is used to guarantee non-overlap of other graph elements with those ports or labels."),C9e),Wa),k3e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,gpe),""),"No Layout"),"No layout is done for the associated element. This is used to mark parts of a diagram to avoid their inclusion in the layout graph, or to mark parts of the layout graph to prevent layout engines from processing them. If you wish to exclude the contents of a compound node from automatic layout, while the node itself is still considered on its own layer, use the 'Fixed Layout' algorithm for that node."),!1),Sr),Yi),Mi(fr,z(B(Qa,1),ke,160,0,[Sa,Kd,Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,yWe),""),"Scale Factor"),"The scaling factor to be applied to the corresponding node in recursive layout. It causes the corresponding node's size to be adjusted, and its ports and labels to be sized and placed accordingly after the layout of that node has been determined (and before the node itself and its siblings are arranged). The scaling is not reverted afterwards, so the resulting layout graph contains the adjusted size and position data. This option is currently not supported if 'Layout Hierarchy' is set."),1),ec),gr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,kWe),""),"Child Area Width"),"The width of the area occupied by the laid out children of a node."),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,jWe),""),"Child Area Height"),"The height of the area occupied by the laid out children of a node."),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,SN),""),hWe),"Turns topdown layout on and off. If this option is enabled, hierarchical layout will be computed first for the root node and then for its children recursively. Layouts are then scaled down to fit the area provided by their parents. Graphs must follow a certain structure for topdown layout to work properly. {@link TopdownNodeTypes.PARALLEL_NODE} nodes must have children of type {@link TopdownNodeTypes.HIERARCHICAL_NODE} and must define {@link topdown.hierarchicalNodeWidth} and {@link topdown.hierarchicalNodeAspectRatio} for their children. Furthermore they need to be laid out using an algorithm that is a {@link TopdownLayoutProvider}. Hierarchical nodes can also be parents of other hierarchical nodes and can optionally use a {@link TopdownSizeApproximator} to dynamically set sizes during topdown layout. In this case {@link topdown.hierarchicalNodeWidth} and {@link topdown.hierarchicalNodeAspectRatio} should be set on the node itself rather than the parent. The values are then used by the size approximator as base values. Hierarchical nodes require the layout option {@link nodeSize.fixedGraphSize} to be true to prevent the algorithm used there from resizing the hierarchical node. This option is not supported if 'Hierarchy Handling' is set to 'INCLUDE_CHILDREN'"),!1),Sr),Yi),nn(Mn)))),Gi(n,SN,Ww,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,EWe),""),"Animate"),"Whether the shift from the old layout to the new computed layout shall be animated."),!0),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,SWe),""),"Animation Time Factor"),"Factor for computation of animation time. The higher the value, the longer the animation time. If the value is 0, the resulting time is always equal to the minimum defined by 'Minimal Animation Time'."),ve(100)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xWe),""),"Layout Ancestors"),"Whether the hierarchy levels on the path from the selected element to the root of the diagram shall be included in the layout process."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,AWe),""),"Maximal Animation Time"),"The maximal time for animations, in milliseconds."),ve(4e3)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,MWe),""),"Minimal Animation Time"),"The minimal time for animations, in milliseconds."),ve(400)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,TWe),""),"Progress Bar"),"Whether a progress bar shall be displayed during layout computations."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,CWe),""),"Validate Graph"),"Whether the graph shall be validated before any layout algorithm is applied. If this option is enabled and at least one error is found, the layout process is aborted and a message is shown to the user."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,OWe),""),"Validate Options"),"Whether layout options shall be validated before any layout algorithm is applied. If this option is enabled and at least one error is found, the layout process is aborted and a message is shown to the user."),!0),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,NWe),""),"Zoom to Fit"),"Whether the zoom level shall be set to view the whole diagram after layout."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,O2e),"box"),"Box Layout Mode"),"Configures the packing mode used by the {@link BoxLayoutProvider}. If SIMPLE is not required (neither priorities are used nor the interactive mode), GROUP_DEC can improve the packing and decrease the area. GROUP_MIXED and GROUP_INC may, in very specific scenarios, work better."),m9e),Ri),O8e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,DWe),"json"),"Shape Coords"),"For layouts transferred into JSON graphs, specify the coordinate system to be used for nodes, ports, and labels of nodes and ports."),M9e),Ri),y8e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,IWe),"json"),"Edge Coords"),"For layouts transferred into JSON graphs, specify the coordinate system to be used for edge route points and edge labels."),A9e),Ri),n8e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ipe),Xa),"Comment Comment Spacing"),"Spacing to be preserved between a comment box and other comment boxes connected to the same node. The space left between comment boxes of different nodes is controlled by the node-node spacing."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,rpe),Xa),"Comment Node Spacing"),"Spacing to be preserved between a node and its connected comment boxes. The space left between a node and the comments of another node is controlled by the node-node spacing."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,cpe),Xa),"Components Spacing"),"Spacing to be preserved between pairs of connected components. This option is only relevant if 'separateConnectedComponents' is activated."),20),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,upe),Xa),"Edge Spacing"),"Spacing to be preserved between any two edges. Note that while this can somewhat easily be satisfied for the segments of orthogonally drawn edges, it is harder for general polylines or splines."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,QZ),Xa),"Edge Label Spacing"),"The minimal distance to be preserved between a label and the edge it is associated with. Note that the placement of a label is influenced by the 'edgelabels.placement' option."),2),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,zee),Xa),"Edge Node Spacing"),"Spacing to be preserved between nodes and edges."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ope),Xa),"Label Spacing"),"Determines the amount of space to be left between two labels of the same graph element."),0),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,fpe),Xa),"Label Node Spacing"),"Spacing to be preserved between labels and the border of node they are associated with. Note that the placement of a label is influenced by the 'nodelabels.placement' option."),5),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,spe),Xa),"Horizontal spacing between Label and Port"),"Horizontal spacing to be preserved between labels and the ports they are associated with. Note that the placement of a label is influenced by the 'portlabels.placement' option."),1),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,lpe),Xa),"Vertical spacing between Label and Port"),"Vertical spacing to be preserved between labels and the ports they are associated with. Note that the placement of a label is influenced by the 'portlabels.placement' option."),1),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,em),Xa),"Node Spacing"),"The minimal distance to be preserved between each two nodes."),20),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ape),Xa),"Node Self Loop Spacing"),"Spacing to be preserved between a node and its self loops."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hpe),Xa),"Port Spacing"),"Spacing between pairs of ports of the same node."),10),ec),gr),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,dpe),Xa),"Individual Spacing"),"Allows to specify individual spacing values for graph elements that shall be different from the value specified for the element's parent."),Wa),gan),Mi(fr,z(B(Qa,1),ke,160,0,[Sa,Kd,Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ppe),Xa),"Additional Port Space"),"Additional space around the sets of ports on each node side. For each side of a node, this option can reserve additional space before and after the ports on each side. For example, a top spacing of 20 makes sure that the first port on the western and eastern side is 20 units away from the northern border."),Q9e),Wa),k3e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Hee),PWe),"Layout Partition"),"Partition to which the node belongs. This requires Layout Partitioning to be active. Nodes with lower partition IDs will appear to the left of nodes with higher partition IDs (assuming a left-to-right layout direction)."),hc),jr),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),Gi(n,Hee,Jee,Nfn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Jee),PWe),"Layout Partitioning"),"Whether to activate partitioned layout. This will allow to group nodes through the Layout Partition option. a pair of nodes with different partition indices is then placed such that the node with lower index is placed to the left of the other node (with left-to-right layout direction). Depending on the layout algorithm, this may only be guaranteed to work if all nodes have a layout partition configured, or at least if edges that cross partitions are not part of a partition-crossing cycle."),$9e),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ype),$We),"Node Label Padding"),"Define padding for node labels that are placed inside of a node."),N9e),Wa),j3e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,U8),$We),"Node Label Placement"),"Hints for where node labels are to be placed; if empty, the node label's position is not modified."),D9e),Ry),$c),Mi(fr,z(B(Qa,1),ke,160,0,[Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Epe),RF),"Port Alignment"),"Defines the default port distribution for a node. May be overridden for each side individually."),B9e),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Spe),RF),"Port Alignment (North)"),"Defines how ports on the northern side are placed, overriding the node's general port alignment."),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,xpe),RF),"Port Alignment (South)"),"Defines how ports on the southern side are placed, overriding the node's general port alignment."),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Ape),RF),"Port Alignment (West)"),"Defines how ports on the western side are placed, overriding the node's general port alignment."),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Mpe),RF),"Port Alignment (East)"),"Defines how ports on the eastern side are placed, overriding the node's general port alignment."),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,wv),bne),"Node Size Constraints"),"What should be taken into account when calculating a node's size. Empty size constraints specify that a node's size is already fixed and should not be changed."),I9e),Ry),tA),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hy),bne),"Node Size Options"),"Options modifying the behavior of the size constraints set on a node. Each member of the set specifies something that should be taken into account when calculating node sizes. The empty set corresponds to no further modifications."),L9e),Ry),k8e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,dy),bne),"Node Size Minimum"),"The minimal size to which a node can be reduced."),_9e),Wa),Lr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,q8),bne),"Fixed Graph Size"),"By default, the fixed layout provider will enlarge a graph until it is large enough to contain its children. If this option is set, it won't do so."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ope),Bee),"Edge Label Placement"),"Gives a hint on where to put edge labels."),k9e),Ri),t8e),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,lF),Bee),"Inline Edge Labels"),"If true, an edge label is placed directly on its edge. May only apply to center edge labels. This kind of label placement is only advisable if the label's rendering is such that it is not crossed by its edge and thus stays legible."),!1),Sr),Yi),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,kBn),"font"),"Font Name"),"Font name used for a label."),By),ze),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,_We),"font"),"Font Size"),"Font size used for a label."),hc),jr),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,_pe),gne),"Port Anchor Offset"),"The offset to the port position where connections shall be attached."),Wa),Lr),nn(Kd)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Npe),gne),"Port Index"),"The index of a port in the fixed order around a node. The order is assumed as clockwise, starting with the leftmost port on the top side. This option must be set if 'Port Constraints' is set to FIXED_ORDER and no specific positions are given for the ports. Additionally, the option 'Port Side' must be defined in this case."),hc),jr),nn(Kd)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,wpe),gne),"Port Side"),"The side of a node on which a port is situated. This option must be set if 'Port Constraints' is set to FIXED_SIDE or FIXED_ORDER and no specific positions are given for the ports."),G9e),Ri),xc),nn(Kd)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,bpe),gne),"Port Border Offset"),"The offset of ports on the node border. With a positive offset the port is moved outside of the node, while with a negative offset the port is moved towards the inside. An offset of 0 means that the port is placed directly on the node border, i.e. if the port side is north, the port's south border touches the nodes's north border; if the port side is east, the port's west border touches the nodes's east border; if the port side is south, the port's north border touches the node's south border; if the port side is west, the port's east border touches the node's west border."),ec),gr),nn(Kd)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,X8),_2e),"Port Label Placement"),"Decides on a placement method for port labels; if empty, the node label's position is not modified."),J9e),Ry),sG),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,kpe),_2e),"Port Labels Next to Port"),"Use 'portLabels.placement': NEXT_TO_PORT_OF_POSSIBLE."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,jpe),_2e),"Treat Port Labels as Group"),"If this option is true (default), the labels of a port will be treated as a group when it comes to centering them next to their port. If this option is false, only the first label will be centered next to the port, with the others being placed below. This only applies to labels of eastern and western ports and will have no effect if labels are not placed next to their port."),!0),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hne),t7),"Number of size categories"),"Defines the number of categories to use for the FIXED_INTEGER_RATIO_BOXES size approximator."),ve(3)),hc),jr),nn(Mn)))),Gi(n,hne,dne,Hfn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,D2e),t7),"Weight of a node containing children for determining the graph size"),"When determining the graph size for the size categorisation, this value determines how many times a node containing children is weighted more than a simple node. For example setting this value to four would result in a graph containing a simple node and a hierarchical node to be counted as having a size of five."),ve(4)),hc),jr),nn(Mn)))),Gi(n,D2e,hne,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xN),t7),"Topdown Scale Factor"),"The scaling factor to be applied to the nodes laid out within the node in recursive topdown layout. The difference to 'Scale Factor' is that the node itself is not scaled. This value has to be set on hierarchical nodes."),1),ec),gr),nn(Mn)))),Gi(n,xN,Ww,zfn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,dne),t7),"Topdown Size Approximator"),"The size approximator to be used to set sizes of hierarchical nodes during topdown layout. The default value is null, which results in nodes keeping whatever size is defined for them e.g. through parent parallel node or by manually setting the size."),null),Wa),KBn),nn(fr)))),Gi(n,dne,Ww,Ffn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,AN),t7),"Topdown Hierarchical Node Width"),"The fixed size of a hierarchical node when using topdown layout. If this value is set on a parallel node it applies to its children, when set on a hierarchical node it applies to the node itself."),150),ec),gr),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),Gi(n,AN,Ww,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,MN),t7),"Topdown Hierarchical Node Aspect Ratio"),"The fixed aspect ratio of a hierarchical node when using topdown layout. Default is 1/sqrt(2). If this value is set on a parallel node it applies to its children, when set on a hierarchical node it applies to the node itself."),1.414),ec),gr),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),Gi(n,MN,Ww,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ww),t7),"Topdown Node Type"),"The different node types used for topdown layout. If the node type is set to {@link TopdownNodeTypes.PARALLEL_NODE} the algorithm must be set to a {@link TopdownLayoutProvider} such as {@link TopdownPacking}. The {@link nodeSize.fixedGraphSize} option is technically only required for hierarchical nodes."),null),Ri),E8e),nn(fr)))),Gi(n,Ww,q8,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,I2e),t7),"Topdown Scale Cap"),"Determines the upper limit for the topdown scale factor. The default value is 1.0 which ensures that nested children never end up appearing larger than their parents in terms of unit sizes such as the font size. If the limit is larger, nodes will fully utilize the available space, but it is counteriniuitive for inner nodes to have a larger scale than outer nodes."),1),ec),gr),nn(Mn)))),Gi(n,I2e,Ww,Bfn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,mpe),RWe),"Activate Inside Self Loops"),"Whether this node allows to route self loops inside of it instead of around it. If set to true, this will make the node a compound node if it isn't already, and will require the layout algorithm to support compound nodes with hierarchical ports."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vpe),RWe),"Inside Self Loop"),"Whether a self loop should be routed inside a node instead of around that node."),!1),Sr),Yi),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Cpe),"edge"),"Edge Thickness"),"The thickness of an edge. This is a hint on the line width used to draw an edge, possibly requiring more space to be reserved for it."),1),ec),gr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,LWe),"edge"),"Edge Type"),"The type of an edge. This is usually used for UML class diagrams, where associations must be handled differently from generalizations."),S9e),Ri),s8e),nn(Sa)))),Cj(n,new N4(Ej(h9(a9(new f0,$n),"Layered"),'The layer-based method was introduced by Sugiyama, Tagawa and Toda in 1981. It emphasizes the direction of edges by pointing as many edges as possible into the same direction. The nodes are arranged in layers, which are sometimes called "hierarchies", and then reordered such that the number of edge crossings is minimized. Afterwards, concrete coordinates are computed for the nodes and edge bend points.'))),Cj(n,new N4(Ej(h9(a9(new f0,"org.eclipse.elk.orthogonal"),"Orthogonal"),`Orthogonal methods that follow the "topology-shape-metrics" approach by Batini, Nardelli and Tamassia '86. The first phase determines the topology of the drawing by applying a planarization technique, which results in a planar representation of the graph. The orthogonal shape is computed in the second phase, which aims at minimizing the number of edge bends, and is called orthogonalization. The third phase leads to concrete coordinates for nodes and edge bend points by applying a compaction method, thus defining the metrics.`))),Cj(n,new N4(Ej(h9(a9(new f0,$o),"Force"),"Layout algorithms that follow physical analogies by simulating a system of attractive and repulsive forces. The first successful method of this kind was proposed by Eades in 1984."))),Cj(n,new N4(Ej(h9(a9(new f0,"org.eclipse.elk.circle"),"Circle"),"Circular layout algorithms emphasize cycles or biconnected components of a graph by arranging them in circles. This is useful if a drawing is desired where such components are clearly grouped, or where cycles are shown as prominent OPTIONS of the graph."))),Cj(n,new N4(Ej(h9(a9(new f0,YQe),"Tree"),"Specialized layout methods for trees, i.e. acyclic graphs. The regular structure of graphs that have no undirected cycles can be emphasized using an algorithm of this type."))),Cj(n,new N4(Ej(h9(a9(new f0,"org.eclipse.elk.planar"),"Planar"),"Algorithms that require a planar or upward planar graph. Most of these algorithms are theoretically interesting, but not practically usable."))),Cj(n,new N4(Ej(h9(a9(new f0,Gl),"Radial"),"Radial layout algorithms usually position the nodes of the graph on concentric circles."))),JXe((new RU,n)),rKe((new CT,n)),bXe((new BU,n))};var zy,yfn,p9e,$7,kfn,jfn,m9e,Tm,Cm,Efn,ND,v9e,DD,Mg,y9e,dce,bce,k9e,j9e,E9e,Sfn,S9e,xfn,Xv,x9e,Afn,ID,gce,_D,wce,Mfn,A9e,Tfn,M9e,Kv,T9e,R7,C9e,O9e,N9e,Vv,D9e,Tg,I9e,Om,Yv,_9e,ab,L9e,tG,LD,o1,P9e,Cfn,$9e,Ofn,Nfn,R9e,B9e,pce,mce,vce,yce,z9e,Ps,Kx,F9e,kce,jce,Nm,J9e,H9e,Qv,G9e,Fy,PD,Ece,Dm,Dfn,Sce,Ifn,_fn,Lfn,Pfn,q9e,U9e,Jy,X9e,iG,K9e,V9e,Vd,$fn,Y9e,Q9e,W9e,B7,Im,z7,Hy,Rfn,Bfn,rG,zfn,cG,Ffn,Jfn,Hfn,Gfn;v(Oo,"CoreOptions",689),m(86,23,{3:1,35:1,23:1,86:1},bC);var Za,Zc,ru,eh,Vl,Vx=yt(Oo,"Direction",86,Ct,J6n,t3n),qfn;m(278,23,{3:1,35:1,23:1,278:1},y$);var uG,$D,Z9e,e8e,n8e=yt(Oo,"EdgeCoords",278,Ct,a6n,i3n),Ufn;m(279,23,{3:1,35:1,23:1,279:1},wK);var F7,_m,J7,t8e=yt(Oo,"EdgeLabelPlacement",279,Ct,syn,r3n),Xfn;m(222,23,{3:1,35:1,23:1,222:1},k$);var H7,RD,Gy,xce,Ace=yt(Oo,"EdgeRouting",222,Ct,h6n,n3n),Kfn;m(327,23,{3:1,35:1,23:1,327:1},zj);var i8e,r8e,c8e,u8e,Mce,o8e,s8e=yt(Oo,"EdgeType",327,Ct,D9n,h3n),Vfn;m(973,1,qa,RU),s.tf=function(n){JXe(n)};var l8e,f8e,a8e,h8e,Yfn,d8e,Yx;v(Oo,"FixedLayouterOptions",973),m(974,1,{},qM),s.uf=function(){var n;return n=new rw,n},s.vf=function(n){},v(Oo,"FixedLayouterOptions/FixedFactory",974),m(347,23,{3:1,35:1,23:1,347:1},pK);var Yd,oG,Qx,b8e=yt(Oo,"HierarchyHandling",347,Ct,lyn,d3n),Qfn,KBn=Hi(Oo,"ITopdownSizeApproximator");m(292,23,{3:1,35:1,23:1,292:1},j$);var s1,hb,BD,zD,Wfn=yt(Oo,"LabelSide",292,Ct,d6n,a3n),Zfn;m(96,23,{3:1,35:1,23:1,96:1},T3);var W1,Qf,wf,Wf,ml,Zf,pf,l1,ea,$c=yt(Oo,"NodeLabelPlacement",96,Ct,I8n,o3n),ean;m(257,23,{3:1,35:1,23:1,257:1},gC);var g8e,Wx,db,w8e,FD,Zx=yt(Oo,"PortAlignment",257,Ct,e9n,s3n),nan;m(102,23,{3:1,35:1,23:1,102:1},Fj);var Cg,to,f1,G7,nh,bb,p8e=yt(Oo,"PortConstraints",102,Ct,N9n,l3n),tan;m(280,23,{3:1,35:1,23:1,280:1},Jj);var eA,nA,Z1,JD,gb,qy,sG=yt(Oo,"PortLabelPlacement",280,Ct,O9n,f3n),ian;m(64,23,{3:1,35:1,23:1,64:1},wC);var et,Xn,Yl,Ql,Wo,zo,th,na,ks,hs,mo,js,Zo,es,ta,vl,yl,mf,bt,ku,Kn,xc=yt(Oo,"PortSide",64,Ct,H6n,p3n),ran;m(977,1,qa,BU),s.tf=function(n){bXe(n)};var can,uan,m8e,oan,san;v(Oo,"RandomLayouterOptions",977),m(978,1,{},UM),s.uf=function(){var n;return n=new YM,n},s.vf=function(n){},v(Oo,"RandomLayouterOptions/RandomFactory",978),m(300,23,{3:1,35:1,23:1,300:1},mK);var HD,Tce,v8e,y8e=yt(Oo,"ShapeCoords",300,Ct,fyn,m3n),lan;m(380,23,{3:1,35:1,23:1,380:1},E$);var Lm,GD,qD,Og,tA=yt(Oo,"SizeConstraint",380,Ct,g6n,v3n),fan;m(266,23,{3:1,35:1,23:1,266:1},C3);var UD,lG,q7,Cce,XD,iA,fG,aG,hG,k8e=yt(Oo,"SizeOptions",266,Ct,z8n,g3n),aan;m(281,23,{3:1,35:1,23:1,281:1},vK);var Pm,j8e,dG,E8e=yt(Oo,"TopdownNodeTypes",281,Ct,ayn,w3n),han;m(288,23,zF);var S8e,Oce,x8e,A8e,KD=yt(Oo,"TopdownSizeApproximator",288,Ct,b6n,b3n);m(969,288,zF,gDe),s.Sg=function(n){return WJe(n)},yt(Oo,"TopdownSizeApproximator/1",969,KD,null,null),m(970,288,zF,QDe),s.Sg=function(n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Tn;for(t=u(ye(n,(Xt(),Dm)),144),Ie=(v0(),A=new pj,A),VO(Ie,n),cn=new pt,o=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));o.e!=o.i.gc();)r=u(ft(o),26),V=(S=new pj,S),Iz(V,Ie),VO(V,r),Tn=WJe(r),ww(V,k.Math.max(r.g,Tn.a),k.Math.max(r.f,Tn.b)),Ko(cn.f,r,V);for(c=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));c.e!=c.i.gc();)for(r=u(ft(c),26),p=new ut((!r.e&&(r.e=new Nn(pr,r,7,4)),r.e));p.e!=p.i.gc();)b=u(ft(p),85),be=u(du(Xc(cn.f,r)),26),fe=u(Bn(cn,K((!b.c&&(b.c=new Nn(vt,b,5,8)),b.c),0)),26),te=(y=new w3,y),Et((!te.b&&(te.b=new Nn(vt,te,4,7)),te.b),be),Et((!te.c&&(te.c=new Nn(vt,te,5,8)),te.c),fe),Dz(te,Bi(be)),VO(te,b);I=u(JC(t.f),214);try{I.kf(Ie,new a0),Wfe(t.f,I)}catch(Dn){throw Dn=sr(Dn),X(Dn,101)?(O=Dn,$(O)):$(Dn)}return da(Ie,Cm)||da(Ie,Tm)||oZ(Ie),h=ne(re(ye(Ie,Cm))),f=ne(re(ye(Ie,Tm))),l=h/f,i=ne(re(ye(Ie,Im)))*k.Math.sqrt((!Ie.a&&(Ie.a=new pe(Jt,Ie,10,11)),Ie.a).i),tn=u(ye(Ie,o1),104),q=tn.b+tn.c+1,R=tn.d+tn.a+1,new je(k.Math.max(q,i),k.Math.max(R,i/l))},yt(Oo,"TopdownSizeApproximator/2",970,KD,null,null),m(971,288,zF,E_e),s.Sg=function(n){var t,i,r,c,o,l;return i=ne(re(ye(n,(Xt(),Im)))),t=i/ne(re(ye(n,B7))),r=z_n(n),o=u(ye(n,o1),104),c=ne(re(_e(Vd))),Bi(n)&&(c=ne(re(ye(Bi(n),Vd)))),l=A1(new je(i,t),r),gi(l,new je(-(o.b+o.c)-c,-(o.d+o.a)-c))},yt(Oo,"TopdownSizeApproximator/3",971,KD,null,null),m(972,288,zF,WDe),s.Sg=function(n){var t,i,r,c,o,l,f,h,b,p;for(l=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));l.e!=l.i.gc();)o=u(ft(l),26),ye(o,(Xt(),cG))!=null&&(!o.a&&(o.a=new pe(Jt,o,10,11)),!!o.a)&&(!o.a&&(o.a=new pe(Jt,o,10,11)),o.a).i>0?(i=u(ye(o,cG),521),p=i.Sg(o),b=u(ye(o,o1),104),ww(o,k.Math.max(o.g,p.a+b.b+b.c),k.Math.max(o.f,p.b+b.d+b.a))):(!o.a&&(o.a=new pe(Jt,o,10,11)),o.a).i!=0&&ww(o,ne(re(ye(o,Im))),ne(re(ye(o,Im)))/ne(re(ye(o,B7))));t=u(ye(n,(Xt(),Dm)),144),h=u(JC(t.f),214);try{h.kf(n,new a0),Wfe(t.f,h)}catch(y){throw y=sr(y),X(y,101)?(f=y,$(f)):$(y)}return ji(n,zy,i7),dPe(n),oZ(n),c=ne(re(ye(n,Cm))),r=ne(re(ye(n,Tm))),new je(c,r)},yt(Oo,"TopdownSizeApproximator/4",972,KD,null,null);var dan;m(345,1,{852:1},i4),s.Tg=function(n,t){return aGe(this,n,t)},s.Ug=function(){$Ge(this)},s.Vg=function(){return this.q},s.Wg=function(){return this.f?OR(this.f):null},s.Xg=function(){return OR(this.a)},s.Yg=function(){return this.p},s.Zg=function(){return!1},s.$g=function(){return this.n},s._g=function(){return this.p!=null&&!this.b},s.ah=function(n){var t;this.n&&(t=n,Te(this.f,t))},s.bh=function(n,t){var i,r;this.n&&n&&Cyn(this,(i=new hIe,r=zW(i,n),x$n(i),r),(PB(),Dce))},s.dh=function(n){var t;return this.b?null:(t=w8n(this,this.g),Vt(this.a,t),t.i=this,this.d=n,t)},s.eh=function(n){n>0&&!this.b&&Vhe(this,n)},s.b=!1,s.c=0,s.d=-1,s.e=null,s.f=null,s.g=-1,s.j=!1,s.k=!1,s.n=!1,s.o=0,s.q=0,s.r=0,v(Ru,"BasicProgressMonitor",345),m(706,214,Qw,hL),s.kf=function(n,t){kKe(n,t)},v(Ru,"BoxLayoutProvider",706),m(965,1,Yt,ZEe),s.Le=function(n,t){return xNn(this,u(n,26),u(t,26))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},s.a=!1,v(Ru,"BoxLayoutProvider/1",965),m(167,1,{167:1},dB,OOe),s.Ib=function(){return this.c?Jbe(this.c):Fa(this.b)},v(Ru,"BoxLayoutProvider/Group",167),m(326,23,{3:1,35:1,23:1,326:1},S$);var M8e,T8e,C8e,Nce,O8e=yt(Ru,"BoxLayoutProvider/PackingMode",326,Ct,w6n,y3n),ban;m(966,1,Yt,XM),s.Le=function(n,t){return L5n(u(n,167),u(t,167))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ru,"BoxLayoutProvider/lambda$0$Type",966),m(967,1,Yt,Bk),s.Le=function(n,t){return x5n(u(n,167),u(t,167))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ru,"BoxLayoutProvider/lambda$1$Type",967),m(968,1,Yt,KM),s.Le=function(n,t){return A5n(u(n,167),u(t,167))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ru,"BoxLayoutProvider/lambda$2$Type",968),m(1338,1,{829:1},dL),s.Lg=function(n,t){return WP(),!X(t,174)||DAe((J4(),u(n,174)),t)},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$0$Type",1338),m(1339,1,it,eSe),s.Ad=function(n){Tkn(this.a,u(n,147))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$1$Type",1339),m(1340,1,it,VM),s.Ad=function(n){u(n,105),WP()},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$2$Type",1340),m(1344,1,it,nSe),s.Ad=function(n){e7n(this.a,u(n,105))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$3$Type",1344),m(1342,1,Ft,MTe),s.Mb=function(n){return fkn(this.a,this.b,u(n,147))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$4$Type",1342),m(1341,1,Ft,TTe),s.Mb=function(n){return Spn(this.a,this.b,u(n,829))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$5$Type",1341),m(1343,1,it,CTe),s.Ad=function(n){Evn(this.a,this.b,u(n,147))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$6$Type",1343),m(930,1,{},bL),s.Kb=function(n){return SCe(n)},s.Fb=function(n){return this===n},v(Ru,"ElkUtil/lambda$0$Type",930),m(931,1,it,OTe),s.Ad=function(n){CCn(this.a,this.b,u(n,85))},s.a=0,s.b=0,v(Ru,"ElkUtil/lambda$1$Type",931),m(932,1,it,NTe),s.Ad=function(n){xbn(this.a,this.b,u(n,170))},s.a=0,s.b=0,v(Ru,"ElkUtil/lambda$2$Type",932),m(933,1,it,DTe),s.Ad=function(n){vwn(this.a,this.b,u(n,157))},s.a=0,s.b=0,v(Ru,"ElkUtil/lambda$3$Type",933),m(934,1,it,tSe),s.Ad=function(n){U3n(this.a,u(n,372))},v(Ru,"ElkUtil/lambda$4$Type",934),m(331,1,{35:1,331:1},ebn),s.Dd=function(n){return Gwn(this,u(n,242))},s.Fb=function(n){var t;return X(n,331)?(t=u(n,331),this.a==t.a):!1},s.Hb=function(){return sc(this.a)},s.Ib=function(){return this.a+" (exclusive)"},s.a=0,v(Ru,"ExclusiveBounds/ExclusiveLowerBound",331),m(1088,214,Qw,rw),s.kf=function(n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn;for(t.Tg("Fixed Layout",1),o=u(ye(n,(Xt(),j9e)),222),y=0,S=0,V=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));V.e!=V.i.gc();){for(R=u(ft(V),26),tn=u(ye(R,($B(),Yx)),8),tn&&(Dl(R,tn.a,tn.b),u(ye(R,f8e),182).Gc((Vs(),Lm))&&(A=u(ye(R,h8e),8),A.a>0&&A.b>0&&Xw(R,A.a,A.b,!0,!0))),y=k.Math.max(y,R.i+R.g),S=k.Math.max(S,R.j+R.f),b=new ut((!R.n&&(R.n=new pe(ju,R,1,7)),R.n));b.e!=b.i.gc();)f=u(ft(b),157),tn=u(ye(f,Yx),8),tn&&Dl(f,tn.a,tn.b),y=k.Math.max(y,R.i+f.i+f.g),S=k.Math.max(S,R.j+f.j+f.f);for(fe=new ut((!R.c&&(R.c=new pe($s,R,9,9)),R.c));fe.e!=fe.i.gc();)for(be=u(ft(fe),125),tn=u(ye(be,Yx),8),tn&&Dl(be,tn.a,tn.b),Ie=R.i+be.i,cn=R.j+be.j,y=k.Math.max(y,Ie+be.g),S=k.Math.max(S,cn+be.f),h=new ut((!be.n&&(be.n=new pe(ju,be,1,7)),be.n));h.e!=h.i.gc();)f=u(ft(h),157),tn=u(ye(f,Yx),8),tn&&Dl(f,tn.a,tn.b),y=k.Math.max(y,Ie+f.i+f.g),S=k.Math.max(S,cn+f.j+f.f);for(c=new qn(Vn(H0(R).a.Jc(),new ee));ht(c);)i=u(tt(c),85),p=IVe(i),y=k.Math.max(y,p.a),S=k.Math.max(S,p.b);for(r=new qn(Vn(AW(R).a.Jc(),new ee));ht(r);)i=u(tt(r),85),Bi(hW(i))!=n&&(p=IVe(i),y=k.Math.max(y,p.a),S=k.Math.max(S,p.b))}if(o==(z1(),H7))for(q=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));q.e!=q.i.gc();)for(R=u(ft(q),26),r=new qn(Vn(H0(R).a.Jc(),new ee));ht(r);)i=u(tt(r),85),l=x_n(i),l.b==0?ji(i,Kv,null):ji(i,Kv,l);Re($e(ye(n,($B(),a8e))))||(te=u(ye(n,Yfn),104),I=y+te.b+te.c,O=S+te.d+te.a,Xw(n,I,O,!0,!0)),t.Ug()},v(Ru,"FixedLayoutProvider",1088),m(379,150,{3:1,414:1,379:1,105:1,150:1},R6,kRe),s.ag=function(n){var t,i,r,c,o,l,f,h,b;if(n)try{for(h=K2(n,";,;"),o=h,l=0,f=o.length;l>16&yr|t^r<<16},s.Jc=function(){return new iSe(this)},s.Ib=function(){return this.a==null&&this.b==null?"pair(null,null)":this.a==null?"pair(null,"+su(this.b)+")":this.b==null?"pair("+su(this.a)+",null)":"pair("+su(this.a)+","+su(this.b)+")"},v(Ru,"Pair",49),m(979,1,Fr,iSe),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return!this.c&&(!this.b&&this.a.a!=null||this.a.b!=null)},s.Pb=function(){if(!this.c&&!this.b&&this.a.a!=null)return this.b=!0,this.a.a;if(!this.c&&this.a.b!=null)return this.c=!0,this.a.b;throw $(new au)},s.Qb=function(){throw this.c&&this.a.b!=null?this.a.b=null:this.b&&this.a.a!=null&&(this.a.a=null),$(new is)},s.b=!1,s.c=!1,v(Ru,"Pair/1",979),m(1078,214,Qw,YM),s.kf=function(n,t){var i,r,c,o,l;if(t.Tg("Random Layout",1),(!n.a&&(n.a=new pe(Jt,n,10,11)),n.a).i==0){t.Ug();return}o=u(ye(n,(bde(),oan)),15),o&&o.a!=0?c=new XR(o.a):c=new vQ,i=VT(re(ye(n,can))),l=VT(re(ye(n,san))),r=u(ye(n,uan),104),U$n(n,c,i,l,r),t.Ug()},v(Ru,"RandomLayoutProvider",1078),m(240,1,{240:1},ZK),s.Fb=function(n){return Ku(this.a,u(n,240).a)&&Ku(this.b,u(n,240).b)&&Ku(this.c,u(n,240).c)},s.Hb=function(){return RB(z(B(Ar,1),On,1,5,[this.a,this.b,this.c]))},s.Ib=function(){return"("+this.a+Co+this.b+Co+this.c+")"},v(Ru,"Triple",240);var man;m(550,1,{}),s.Jf=function(){return new je(this.f.i,this.f.j)},s.mf=function(n){return y_e(n,(Xt(),Ps))?ye(this.f,van):ye(this.f,n)},s.Kf=function(){return new je(this.f.g,this.f.f)},s.Lf=function(){return this.g},s.nf=function(n){return da(this.f,n)},s.Mf=function(n){Os(this.f,n.a),Ns(this.f,n.b)},s.Nf=function(n){Iw(this.f,n.a),Dw(this.f,n.b)},s.Of=function(n){this.g=n},s.g=0;var van;v(IS,"ElkGraphAdapters/AbstractElkGraphElementAdapter",550),m(552,1,{837:1},CP),s.Pf=function(){var n,t;if(!this.b)for(this.b=zR(OV(this.a).i),t=new ut(OV(this.a));t.e!=t.i.gc();)n=u(ft(t),157),Te(this.b,new AX(n));return this.b},s.b=null,v(IS,"ElkGraphAdapters/ElkEdgeAdapter",552),m(260,550,{},w0),s.Qf=function(){return mHe(this)},s.a=null,v(IS,"ElkGraphAdapters/ElkGraphAdapter",260),m(630,550,{187:1},AX),v(IS,"ElkGraphAdapters/ElkLabelAdapter",630),m(551,550,{685:1},H$),s.Pf=function(){return ZSn(this)},s.Tf=function(){var n;return n=u(ye(this.f,(Xt(),R7)),140),!n&&(n=new wj),n},s.Vf=function(){return exn(this)},s.Xf=function(n){var t;t=new YK(n),ji(this.f,(Xt(),R7),t)},s.Yf=function(n){ji(this.f,(Xt(),o1),new Yle(n))},s.Rf=function(){return this.d},s.Sf=function(){var n,t;if(!this.a)for(this.a=new Ce,t=new qn(Vn(AW(u(this.f,26)).a.Jc(),new ee));ht(t);)n=u(tt(t),85),Te(this.a,new CP(n));return this.a},s.Uf=function(){var n,t;if(!this.c)for(this.c=new Ce,t=new qn(Vn(H0(u(this.f,26)).a.Jc(),new ee));ht(t);)n=u(tt(t),85),Te(this.c,new CP(n));return this.c},s.Wf=function(){return TR(u(this.f,26)).i!=0||Re($e(u(this.f,26).mf((Xt(),ID))))},s.Zf=function(){Q9n(this,(_b(),man))},s.a=null,s.b=null,s.c=null,s.d=null,s.e=null,v(IS,"ElkGraphAdapters/ElkNodeAdapter",551),m(1249,550,{836:1},rSe),s.Pf=function(){return oxn(this)},s.Sf=function(){var n,t;if(!this.a)for(this.a=Fh(u(this.f,125).gh().i),t=new ut(u(this.f,125).gh());t.e!=t.i.gc();)n=u(ft(t),85),Te(this.a,new CP(n));return this.a},s.Uf=function(){var n,t;if(!this.c)for(this.c=Fh(u(this.f,125).hh().i),t=new ut(u(this.f,125).hh());t.e!=t.i.gc();)n=u(ft(t),85),Te(this.c,new CP(n));return this.c},s.$f=function(){return u(u(this.f,125).mf((Xt(),Qv)),64)},s._f=function(){var n,t,i,r,c,o,l,f;for(r=Ia(u(this.f,125)),i=new ut(u(this.f,125).hh());i.e!=i.i.gc();)for(n=u(ft(i),85),f=new ut((!n.c&&(n.c=new Nn(vt,n,5,8)),n.c));f.e!=f.i.gc();){if(l=u(ft(f),84),C2(iu(l),r))return!0;if(iu(l)==r&&Re($e(ye(n,(Xt(),gce)))))return!0}for(t=new ut(u(this.f,125).gh());t.e!=t.i.gc();)for(n=u(ft(t),85),o=new ut((!n.b&&(n.b=new Nn(vt,n,4,7)),n.b));o.e!=o.i.gc();)if(c=u(ft(o),84),C2(iu(c),r))return!0;return!1},s.a=null,s.b=null,s.c=null,v(IS,"ElkGraphAdapters/ElkPortAdapter",1249),m(1250,1,Yt,wL),s.Le=function(n,t){return pIn(u(n,125),u(t,125))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(IS,"ElkGraphAdapters/PortComparator",1250);var wb=Hi(ql,"EObject"),U7=Hi(yv,FWe),kl=Hi(yv,JWe),VD=Hi(yv,HWe),YD=Hi(yv,"ElkShape"),vt=Hi(yv,GWe),pr=Hi(yv,P2e),Pi=Hi(yv,qWe),QD=Hi(ql,UWe),rA=Hi(ql,"EFactory"),yan,Ice=Hi(ql,XWe),xa=Hi(ql,"EPackage"),Pr,kan,jan,_8e,bG,Ean,L8e,P8e,$8e,a1,San,xan,ju=Hi(yv,$2e),Jt=Hi(yv,R2e),$s=Hi(yv,B2e);m(93,1,KWe),s.qh=function(){return this.rh(),null},s.rh=function(){return null},s.sh=function(){return this.rh(),!1},s.th=function(){return!1},s.uh=function(n){ai(this,n)},v(wy,"BasicNotifierImpl",93),m(100,93,WWe),s.Vh=function(){return Fs(this)},s.vh=function(n,t){return n},s.wh=function(){throw $(new _t)},s.xh=function(n){var t;return t=Oc(u(An(this.Ah(),this.Ch()),19)),this.Mh().Qh(this,t.n,t.f,n)},s.yh=function(n,t){throw $(new _t)},s.zh=function(n,t,i){return dl(this,n,t,i)},s.Ah=function(){var n;return this.wh()&&(n=this.wh().Lk(),n)?n:this.fi()},s.Bh=function(){return SW(this)},s.Ch=function(){throw $(new _t)},s.Dh=function(){var n,t;return t=this.Xh().Mk(),!t&&this.wh().Rk(t=(Nj(),n=dae(vh(this.Ah())),n==null?Fce:new jC(this,n))),t},s.Eh=function(n,t){return n},s.Fh=function(n){var t;return t=n.nk(),t?n.Jj():zi(this.Ah(),n)},s.Gh=function(){var n;return n=this.wh(),n?n.Ok():null},s.Hh=function(){return this.wh()?this.wh().Lk():null},s.Ih=function(n,t,i){return uz(this,n,t,i)},s.Jh=function(n){return F9(this,n)},s.Kh=function(n,t){return fY(this,n,t)},s.Lh=function(){var n;return n=this.wh(),!!n&&n.Pk()},s.Mh=function(){throw $(new _t)},s.Nh=function(){return nz(this)},s.Oh=function(n,t,i,r){return K4(this,n,t,r)},s.Ph=function(n,t,i){var r;return r=u(An(this.Ah(),t),69),r.uk().xk(this,this.ei(),t-this.gi(),n,i)},s.Qh=function(n,t,i,r){return IR(this,n,t,r)},s.Rh=function(n,t,i){var r;return r=u(An(this.Ah(),t),69),r.uk().yk(this,this.ei(),t-this.gi(),n,i)},s.Sh=function(){return!!this.wh()&&!!this.wh().Nk()},s.Th=function(n){return _Q(this,n)},s.Uh=function(n){return L_e(this,n)},s.Wh=function(n){return wVe(this,n)},s.Xh=function(){throw $(new _t)},s.Yh=function(){return this.wh()?this.wh().Nk():null},s.Zh=function(){return nz(this)},s.$h=function(n,t){vW(this,n,t)},s._h=function(n){this.Xh().Qk(n)},s.ai=function(n){this.Xh().Tk(n)},s.bi=function(n){this.Xh().Sk(n)},s.ci=function(n,t){var i,r,c,o;return o=this.Gh(),o&&n&&(t=vc(o.Cl(),this,t),o.Gl(this)),r=this.Mh(),r&&(($W(this,this.Mh(),this.Ch()).Bb&Ec)!=0?(c=r.Nh(),c&&(n?!o&&c.Gl(this):c.Fl(this))):(t=(i=this.Ch(),i>=0?this.xh(t):this.Mh().Qh(this,-1-i,null,t)),t=this.zh(null,-1,t))),this.ai(n),t},s.di=function(n){var t,i,r,c,o,l,f,h;if(i=this.Ah(),o=zi(i,n),t=this.gi(),o>=t)return u(n,69).uk().Bk(this,this.ei(),o-t);if(o<=-1)if(l=av((ls(),nc),i,n),l){if(Cc(),u(l,69).vk()||(l=D4(Vc(nc,l))),c=(r=this.Fh(l),u(r>=0?this.Ih(r,!0,!0):Gw(this,l,!0),163)),h=l.Gk(),h>1||h==-1)return u(u(c,219).Ql(n,!1),77)}else throw $(new Gn(W0+n.ve()+wne));else if(n.Hk())return r=this.Fh(n),u(r>=0?this.Ih(r,!1,!0):Gw(this,n,!1),77);return f=new KTe(this,n),f},s.ei=function(){return khe(this)},s.fi=function(){return(x0(),Rn).S},s.gi=function(){return dt(this.fi())},s.hi=function(n){wW(this,n)},s.Ib=function(){return Jf(this)},v(Fn,"BasicEObjectImpl",100);var Aan;m(117,100,{109:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1}),s.ii=function(n){var t;return t=jhe(this),t[n]},s.ji=function(n,t){var i;i=jhe(this),tr(i,n,t)},s.ki=function(n){var t;t=jhe(this),tr(t,n,null)},s.qh=function(){return u(Un(this,4),129)},s.rh=function(){throw $(new _t)},s.sh=function(){return(this.Db&4)!=0},s.wh=function(){throw $(new _t)},s.li=function(n){U4(this,2,n)},s.yh=function(n,t){this.Db=t<<16|this.Db&255,this.li(n)},s.Ah=function(){return Go(this)},s.Ch=function(){return this.Db>>16},s.Dh=function(){var n,t;return Nj(),t=dae(vh((n=u(Un(this,16),29),n||this.fi()))),t==null?Fce:new jC(this,t)},s.th=function(){return(this.Db&1)==0},s.Gh=function(){return u(Un(this,128),1996)},s.Hh=function(){return u(Un(this,16),29)},s.Lh=function(){return(this.Db&32)!=0},s.Mh=function(){return u(Un(this,2),52)},s.Sh=function(){return(this.Db&64)!=0},s.Xh=function(){throw $(new _t)},s.Yh=function(){return u(Un(this,64),290)},s._h=function(n){U4(this,16,n)},s.ai=function(n){U4(this,128,n)},s.bi=function(n){U4(this,64,n)},s.ei=function(){return Lo(this)},s.Db=0,v(Fn,"MinimalEObjectImpl",117),m(118,117,{109:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.li=function(n){this.Cb=n},s.Mh=function(){return this.Cb},v(Fn,"MinimalEObjectImpl/Container",118),m(2045,118,{109:1,343:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return $de(this,n,t,i)},s.Rh=function(n,t,i){return A0e(this,n,t,i)},s.Th=function(n){return Oae(this,n)},s.$h=function(n,t){E1e(this,n,t)},s.fi=function(){return Gu(),xan},s.hi=function(n){f1e(this,n)},s.lf=function(){return RJe(this)},s.fh=function(){return!this.o&&(this.o=new os((Gu(),a1),Qd,this,0)),this.o},s.mf=function(n){return ye(this,n)},s.nf=function(n){return da(this,n)},s.of=function(n,t){return ji(this,n,t)},v(hg,"EMapPropertyHolderImpl",2045),m(559,118,{109:1,372:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},Fk),s.Ih=function(n,t,i){switch(n){case 0:return this.a;case 1:return this.b}return uz(this,n,t,i)},s.Th=function(n){switch(n){case 0:return this.a!=0;case 1:return this.b!=0}return _Q(this,n)},s.$h=function(n,t){switch(n){case 0:bB(this,ne(re(t)));return;case 1:gB(this,ne(re(t)));return}vW(this,n,t)},s.fi=function(){return Gu(),kan},s.hi=function(n){switch(n){case 0:bB(this,0);return;case 1:gB(this,0);return}wW(this,n)},s.Ib=function(){var n;return(this.Db&64)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (x: ",S3(n,this.a),n.a+=", y: ",S3(n,this.b),n.a+=")",n.a)},s.a=0,s.b=0,v(hg,"ElkBendPointImpl",559),m(727,2045,{109:1,343:1,174:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return J1e(this,n,t,i)},s.Ph=function(n,t,i){return sW(this,n,t,i)},s.Rh=function(n,t,i){return XY(this,n,t,i)},s.Th=function(n){return r1e(this,n)},s.$h=function(n,t){t0e(this,n,t)},s.fi=function(){return Gu(),Ean},s.hi=function(n){R1e(this,n)},s.ih=function(){return this.k},s.jh=function(){return OV(this)},s.Ib=function(){return mQ(this)},s.k=null,v(hg,"ElkGraphElementImpl",727),m(728,727,{109:1,343:1,174:1,276:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return nde(this,n,t,i)},s.Th=function(n){return sde(this,n)},s.$h=function(n,t){i0e(this,n,t)},s.fi=function(){return Gu(),San},s.hi=function(n){dde(this,n)},s.kh=function(){return this.f},s.lh=function(){return this.g},s.mh=function(){return this.i},s.nh=function(){return this.j},s.oh=function(n,t){ww(this,n,t)},s.ph=function(n,t){Dl(this,n,t)},s.Ib=function(){return bW(this)},s.f=0,s.g=0,s.i=0,s.j=0,v(hg,"ElkShapeImpl",728),m(729,728,{109:1,343:1,84:1,174:1,276:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return Nde(this,n,t,i)},s.Ph=function(n,t,i){return Yde(this,n,t,i)},s.Rh=function(n,t,i){return Qde(this,n,t,i)},s.Th=function(n){return v1e(this,n)},s.$h=function(n,t){fbe(this,n,t)},s.fi=function(){return Gu(),jan},s.hi=function(n){Ade(this,n)},s.gh=function(){return!this.d&&(this.d=new Nn(pr,this,8,5)),this.d},s.hh=function(){return!this.e&&(this.e=new Nn(pr,this,7,4)),this.e},v(hg,"ElkConnectableShapeImpl",729),m(271,727,{109:1,343:1,85:1,174:1,271:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},w3),s.xh=function(n){return Ude(this,n)},s.Ih=function(n,t,i){switch(n){case 3:return j2(this);case 4:return!this.b&&(this.b=new Nn(vt,this,4,7)),this.b;case 5:return!this.c&&(this.c=new Nn(vt,this,5,8)),this.c;case 6:return!this.a&&(this.a=new pe(Pi,this,6,6)),this.a;case 7:return Pn(),!this.b&&(this.b=new Nn(vt,this,4,7)),!(this.b.i<=1&&(!this.c&&(this.c=new Nn(vt,this,5,8)),this.c.i<=1));case 8:return Pn(),!!ZE(this);case 9:return Pn(),!!Hw(this);case 10:return Pn(),!this.b&&(this.b=new Nn(vt,this,4,7)),this.b.i!=0&&(!this.c&&(this.c=new Nn(vt,this,5,8)),this.c.i!=0)}return J1e(this,n,t,i)},s.Ph=function(n,t,i){var r;switch(t){case 3:return this.Cb&&(i=(r=this.Db>>16,r>=0?Ude(this,i):this.Cb.Qh(this,-1-r,null,i))),Tle(this,u(n,26),i);case 4:return!this.b&&(this.b=new Nn(vt,this,4,7)),To(this.b,n,i);case 5:return!this.c&&(this.c=new Nn(vt,this,5,8)),To(this.c,n,i);case 6:return!this.a&&(this.a=new pe(Pi,this,6,6)),To(this.a,n,i)}return sW(this,n,t,i)},s.Rh=function(n,t,i){switch(t){case 3:return Tle(this,null,i);case 4:return!this.b&&(this.b=new Nn(vt,this,4,7)),vc(this.b,n,i);case 5:return!this.c&&(this.c=new Nn(vt,this,5,8)),vc(this.c,n,i);case 6:return!this.a&&(this.a=new pe(Pi,this,6,6)),vc(this.a,n,i)}return XY(this,n,t,i)},s.Th=function(n){switch(n){case 3:return!!j2(this);case 4:return!!this.b&&this.b.i!=0;case 5:return!!this.c&&this.c.i!=0;case 6:return!!this.a&&this.a.i!=0;case 7:return!this.b&&(this.b=new Nn(vt,this,4,7)),!(this.b.i<=1&&(!this.c&&(this.c=new Nn(vt,this,5,8)),this.c.i<=1));case 8:return ZE(this);case 9:return Hw(this);case 10:return!this.b&&(this.b=new Nn(vt,this,4,7)),this.b.i!=0&&(!this.c&&(this.c=new Nn(vt,this,5,8)),this.c.i!=0)}return r1e(this,n)},s.$h=function(n,t){switch(n){case 3:Dz(this,u(t,26));return;case 4:!this.b&&(this.b=new Nn(vt,this,4,7)),kt(this.b),!this.b&&(this.b=new Nn(vt,this,4,7)),er(this.b,u(t,18));return;case 5:!this.c&&(this.c=new Nn(vt,this,5,8)),kt(this.c),!this.c&&(this.c=new Nn(vt,this,5,8)),er(this.c,u(t,18));return;case 6:!this.a&&(this.a=new pe(Pi,this,6,6)),kt(this.a),!this.a&&(this.a=new pe(Pi,this,6,6)),er(this.a,u(t,18));return}t0e(this,n,t)},s.fi=function(){return Gu(),_8e},s.hi=function(n){switch(n){case 3:Dz(this,null);return;case 4:!this.b&&(this.b=new Nn(vt,this,4,7)),kt(this.b);return;case 5:!this.c&&(this.c=new Nn(vt,this,5,8)),kt(this.c);return;case 6:!this.a&&(this.a=new pe(Pi,this,6,6)),kt(this.a);return}R1e(this,n)},s.Ib=function(){return $Ke(this)},v(hg,"ElkEdgeImpl",271),m(443,2045,{109:1,343:1,170:1,443:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},yo),s.xh=function(n){return Jde(this,n)},s.Ih=function(n,t,i){switch(n){case 1:return this.j;case 2:return this.k;case 3:return this.b;case 4:return this.c;case 5:return!this.a&&(this.a=new mr(kl,this,5)),this.a;case 6:return __e(this);case 7:return t?BQ(this):this.i;case 8:return t?RQ(this):this.f;case 9:return!this.g&&(this.g=new Nn(Pi,this,9,10)),this.g;case 10:return!this.e&&(this.e=new Nn(Pi,this,10,9)),this.e;case 11:return this.d}return $de(this,n,t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 6:return this.Cb&&(i=(c=this.Db>>16,c>=0?Jde(this,i):this.Cb.Qh(this,-1-c,null,i))),Cle(this,u(n,85),i);case 9:return!this.g&&(this.g=new Nn(Pi,this,9,10)),To(this.g,n,i);case 10:return!this.e&&(this.e=new Nn(Pi,this,10,9)),To(this.e,n,i)}return o=u(An((r=u(Un(this,16),29),r||(Gu(),bG)),t),69),o.uk().xk(this,Lo(this),t-dt((Gu(),bG)),n,i)},s.Rh=function(n,t,i){switch(t){case 5:return!this.a&&(this.a=new mr(kl,this,5)),vc(this.a,n,i);case 6:return Cle(this,null,i);case 9:return!this.g&&(this.g=new Nn(Pi,this,9,10)),vc(this.g,n,i);case 10:return!this.e&&(this.e=new Nn(Pi,this,10,9)),vc(this.e,n,i)}return A0e(this,n,t,i)},s.Th=function(n){switch(n){case 1:return this.j!=0;case 2:return this.k!=0;case 3:return this.b!=0;case 4:return this.c!=0;case 5:return!!this.a&&this.a.i!=0;case 6:return!!__e(this);case 7:return!!this.i;case 8:return!!this.f;case 9:return!!this.g&&this.g.i!=0;case 10:return!!this.e&&this.e.i!=0;case 11:return this.d!=null}return Oae(this,n)},s.$h=function(n,t){switch(n){case 1:V3(this,ne(re(t)));return;case 2:Y3(this,ne(re(t)));return;case 3:X3(this,ne(re(t)));return;case 4:K3(this,ne(re(t)));return;case 5:!this.a&&(this.a=new mr(kl,this,5)),kt(this.a),!this.a&&(this.a=new mr(kl,this,5)),er(this.a,u(t,18));return;case 6:PUe(this,u(t,85));return;case 7:jB(this,u(t,84));return;case 8:kB(this,u(t,84));return;case 9:!this.g&&(this.g=new Nn(Pi,this,9,10)),kt(this.g),!this.g&&(this.g=new Nn(Pi,this,9,10)),er(this.g,u(t,18));return;case 10:!this.e&&(this.e=new Nn(Pi,this,10,9)),kt(this.e),!this.e&&(this.e=new Nn(Pi,this,10,9)),er(this.e,u(t,18));return;case 11:Xhe(this,Pt(t));return}E1e(this,n,t)},s.fi=function(){return Gu(),bG},s.hi=function(n){switch(n){case 1:V3(this,0);return;case 2:Y3(this,0);return;case 3:X3(this,0);return;case 4:K3(this,0);return;case 5:!this.a&&(this.a=new mr(kl,this,5)),kt(this.a);return;case 6:PUe(this,null);return;case 7:jB(this,null);return;case 8:kB(this,null);return;case 9:!this.g&&(this.g=new Nn(Pi,this,9,10)),kt(this.g);return;case 10:!this.e&&(this.e=new Nn(Pi,this,10,9)),kt(this.e);return;case 11:Xhe(this,null);return}f1e(this,n)},s.Ib=function(){return Xqe(this)},s.b=0,s.c=0,s.d=null,s.j=0,s.k=0,v(hg,"ElkEdgeSectionImpl",443),m(161,118,{109:1,94:1,93:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1}),s.Ih=function(n,t,i){var r;return n==0?(!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab):Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Ph=function(n,t,i){var r,c;return t==0?(!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i)):(c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().xk(this,Lo(this),t-dt(this.fi()),n,i))},s.Rh=function(n,t,i){var r,c;return t==0?(!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i)):(c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,Lo(this),t-dt(this.fi()),n,i))},s.Th=function(n){var t;return n==0?!!this.Ab&&this.Ab.i!=0:Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.Wh=function(n){return Tge(this,n)},s.$h=function(n,t){var i;if(n===0){!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.ai=function(n){U4(this,128,n)},s.fi=function(){return yn(),Gan},s.hi=function(n){var t;if(n===0){!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.mi=function(){this.Bb|=1},s.ni=function(n){return uS(this,n)},s.Bb=0,v(Fn,"EModelElementImpl",161),m(710,161,{109:1,94:1,93:1,469:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1},OT),s.oi=function(n,t){return lVe(this,n,t)},s.pi=function(n){var t,i,r,c,o;if(this.a!=sl(n)||(n.Bb&256)!=0)throw $(new Gn(mne+n.zb+ip));for(r=tu(n);Vu(r.a).i!=0;){if(i=u(cN(r,0,(t=u(K(Vu(r.a),0),87),o=t.c,X(o,88)?u(o,29):(yn(),jf))),29),Fw(i))return c=sl(i).ti().pi(i),u(c,52)._h(n),c;r=tu(i)}return(n.D!=null?n.D:n.B)=="java.util.Map$Entry"?new bDe(n):new bfe(n)},s.qi=function(n,t){return Kw(this,n,t)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.a}return Pl(this,n-dt((yn(),vb)),An((r=u(Un(this,16),29),r||vb),n),t,i)},s.Ph=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 1:return this.a&&(i=u(this.a,52).Qh(this,4,xa,i)),P1e(this,u(n,241),i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),vb)),t),69),c.uk().xk(this,Lo(this),t-dt((yn(),vb)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 1:return P1e(this,null,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),vb)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),vb)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return!!this.a}return Ll(this,n-dt((yn(),vb)),An((t=u(Un(this,16),29),t||vb),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:EGe(this,u(t,241));return}Jl(this,n-dt((yn(),vb)),An((i=u(Un(this,16),29),i||vb),n),t)},s.fi=function(){return yn(),vb},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:EGe(this,null);return}Fl(this,n-dt((yn(),vb)),An((t=u(Un(this,16),29),t||vb),n))};var cA,R8e,Man;v(Fn,"EFactoryImpl",710),m(1018,710,{109:1,2075:1,94:1,93:1,469:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1},aU),s.oi=function(n,t){switch(n.fk()){case 12:return u(t,147).Og();case 13:return su(t);default:throw $(new Gn(r7+n.ve()+ip))}},s.pi=function(n){var t,i,r,c,o,l,f,h;switch(n.G==-1&&(n.G=(t=sl(n),t?Ld(t.si(),n):-1)),n.G){case 4:return o=new QM,o;case 6:return l=new pj,l;case 7:return f=new voe,f;case 8:return r=new w3,r;case 9:return i=new Fk,i;case 10:return c=new yo,c;case 11:return h=new B6,h;default:throw $(new Gn(mne+n.zb+ip))}},s.qi=function(n,t){switch(n.fk()){case 13:case 12:return null;default:throw $(new Gn(r7+n.ve()+ip))}},v(hg,"ElkGraphFactoryImpl",1018),m(439,161,{109:1,94:1,93:1,158:1,197:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1}),s.Dh=function(){var n,t;return t=(n=u(Un(this,16),29),dae(vh(n||this.fi()))),t==null?(Nj(),Nj(),Fce):new LOe(this,t)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.ve()}return Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null}return Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:this.ri(Pt(t));return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return yn(),qan},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:this.ri(null);return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.ve=function(){return this.zb},s.ri=function(n){Mo(this,n)},s.Ib=function(){return _E(this)},s.zb=null,v(Fn,"ENamedElementImpl",439),m(184,439,{109:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,52:1,100:1,161:1,184:1,117:1,118:1,680:1},c_e),s.xh=function(n){return _He(this,n)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return this.yb;case 3:return this.xb;case 4:return this.sb;case 5:return!this.rb&&(this.rb=new m2(this,Aa,this)),this.rb;case 6:return!this.vb&&(this.vb=new y4(xa,this,6,7)),this.vb;case 7:return t?this.Db>>16==7?u(this.Cb,241):null:F_e(this)}return Pl(this,n-dt((yn(),n0)),An((r=u(Un(this,16),29),r||n0),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 4:return this.sb&&(i=u(this.sb,52).Qh(this,1,rA,i)),B1e(this,u(n,469),i);case 5:return!this.rb&&(this.rb=new m2(this,Aa,this)),To(this.rb,n,i);case 6:return!this.vb&&(this.vb=new y4(xa,this,6,7)),To(this.vb,n,i);case 7:return this.Cb&&(i=(c=this.Db>>16,c>=0?_He(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,7,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),n0)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),n0)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 4:return B1e(this,null,i);case 5:return!this.rb&&(this.rb=new m2(this,Aa,this)),vc(this.rb,n,i);case 6:return!this.vb&&(this.vb=new y4(xa,this,6,7)),vc(this.vb,n,i);case 7:return dl(this,null,7,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),n0)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),n0)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.yb!=null;case 3:return this.xb!=null;case 4:return!!this.sb;case 5:return!!this.rb&&this.rb.i!=0;case 6:return!!this.vb&&this.vb.i!=0;case 7:return!!F_e(this)}return Ll(this,n-dt((yn(),n0)),An((t=u(Un(this,16),29),t||n0),n))},s.Wh=function(n){var t;return t=LNn(this,n),t||Tge(this,n)},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Pt(t));return;case 2:TB(this,Pt(t));return;case 3:MB(this,Pt(t));return;case 4:dW(this,u(t,469));return;case 5:!this.rb&&(this.rb=new m2(this,Aa,this)),kt(this.rb),!this.rb&&(this.rb=new m2(this,Aa,this)),er(this.rb,u(t,18));return;case 6:!this.vb&&(this.vb=new y4(xa,this,6,7)),kt(this.vb),!this.vb&&(this.vb=new y4(xa,this,6,7)),er(this.vb,u(t,18));return}Jl(this,n-dt((yn(),n0)),An((i=u(Un(this,16),29),i||n0),n),t)},s.bi=function(n){var t,i;if(n&&this.rb)for(i=new ut(this.rb);i.e!=i.i.gc();)t=ft(i),X(t,360)&&(u(t,360).w=null);U4(this,64,n)},s.fi=function(){return yn(),n0},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Mo(this,null);return;case 2:TB(this,null);return;case 3:MB(this,null);return;case 4:dW(this,null);return;case 5:!this.rb&&(this.rb=new m2(this,Aa,this)),kt(this.rb);return;case 6:!this.vb&&(this.vb=new y4(xa,this,6,7)),kt(this.vb);return}Fl(this,n-dt((yn(),n0)),An((t=u(Un(this,16),29),t||n0),n))},s.mi=function(){WQ(this)},s.si=function(){return!this.rb&&(this.rb=new m2(this,Aa,this)),this.rb},s.ti=function(){return this.sb},s.ui=function(){return this.ub},s.vi=function(){return this.xb},s.wi=function(){return this.yb},s.xi=function(n){this.ub=n},s.Ib=function(){var n;return(this.Db&64)!=0?_E(this):(n=new cf(_E(this)),n.a+=" (nsURI: ",Bc(n,this.yb),n.a+=", nsPrefix: ",Bc(n,this.xb),n.a+=")",n.a)},s.xb=null,s.yb=null,v(Fn,"EPackageImpl",184),m(556,184,{109:1,2077:1,556:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,52:1,100:1,161:1,184:1,117:1,118:1,680:1},nUe),s.q=!1,s.r=!1;var Tan=!1;v(hg,"ElkGraphPackageImpl",556),m(362,728,{109:1,343:1,174:1,157:1,276:1,362:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},QM),s.xh=function(n){return Hde(this,n)},s.Ih=function(n,t,i){switch(n){case 7:return vae(this);case 8:return this.a}return nde(this,n,t,i)},s.Ph=function(n,t,i){var r;return t===7?(this.Cb&&(i=(r=this.Db>>16,r>=0?Hde(this,i):this.Cb.Qh(this,-1-r,null,i))),Mfe(this,u(n,174),i)):sW(this,n,t,i)},s.Rh=function(n,t,i){return t==7?Mfe(this,null,i):XY(this,n,t,i)},s.Th=function(n){switch(n){case 7:return!!vae(this);case 8:return!bn("",this.a)}return sde(this,n)},s.$h=function(n,t){switch(n){case 7:Abe(this,u(t,174));return;case 8:Ghe(this,Pt(t));return}i0e(this,n,t)},s.fi=function(){return Gu(),L8e},s.hi=function(n){switch(n){case 7:Abe(this,null);return;case 8:Ghe(this,"");return}dde(this,n)},s.Ib=function(){return JGe(this)},s.a="",v(hg,"ElkLabelImpl",362),m(206,729,{109:1,343:1,84:1,174:1,26:1,276:1,206:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},pj),s.xh=function(n){return Xde(this,n)},s.Ih=function(n,t,i){switch(n){case 9:return!this.c&&(this.c=new pe($s,this,9,9)),this.c;case 10:return!this.a&&(this.a=new pe(Jt,this,10,11)),this.a;case 11:return Bi(this);case 12:return!this.b&&(this.b=new pe(pr,this,12,3)),this.b;case 13:return Pn(),!this.a&&(this.a=new pe(Jt,this,10,11)),this.a.i>0}return Nde(this,n,t,i)},s.Ph=function(n,t,i){var r;switch(t){case 9:return!this.c&&(this.c=new pe($s,this,9,9)),To(this.c,n,i);case 10:return!this.a&&(this.a=new pe(Jt,this,10,11)),To(this.a,n,i);case 11:return this.Cb&&(i=(r=this.Db>>16,r>=0?Xde(this,i):this.Cb.Qh(this,-1-r,null,i))),Gle(this,u(n,26),i);case 12:return!this.b&&(this.b=new pe(pr,this,12,3)),To(this.b,n,i)}return Yde(this,n,t,i)},s.Rh=function(n,t,i){switch(t){case 9:return!this.c&&(this.c=new pe($s,this,9,9)),vc(this.c,n,i);case 10:return!this.a&&(this.a=new pe(Jt,this,10,11)),vc(this.a,n,i);case 11:return Gle(this,null,i);case 12:return!this.b&&(this.b=new pe(pr,this,12,3)),vc(this.b,n,i)}return Qde(this,n,t,i)},s.Th=function(n){switch(n){case 9:return!!this.c&&this.c.i!=0;case 10:return!!this.a&&this.a.i!=0;case 11:return!!Bi(this);case 12:return!!this.b&&this.b.i!=0;case 13:return!this.a&&(this.a=new pe(Jt,this,10,11)),this.a.i>0}return v1e(this,n)},s.$h=function(n,t){switch(n){case 9:!this.c&&(this.c=new pe($s,this,9,9)),kt(this.c),!this.c&&(this.c=new pe($s,this,9,9)),er(this.c,u(t,18));return;case 10:!this.a&&(this.a=new pe(Jt,this,10,11)),kt(this.a),!this.a&&(this.a=new pe(Jt,this,10,11)),er(this.a,u(t,18));return;case 11:Iz(this,u(t,26));return;case 12:!this.b&&(this.b=new pe(pr,this,12,3)),kt(this.b),!this.b&&(this.b=new pe(pr,this,12,3)),er(this.b,u(t,18));return}fbe(this,n,t)},s.fi=function(){return Gu(),P8e},s.hi=function(n){switch(n){case 9:!this.c&&(this.c=new pe($s,this,9,9)),kt(this.c);return;case 10:!this.a&&(this.a=new pe(Jt,this,10,11)),kt(this.a);return;case 11:Iz(this,null);return;case 12:!this.b&&(this.b=new pe(pr,this,12,3)),kt(this.b);return}Ade(this,n)},s.Ib=function(){return Jbe(this)},v(hg,"ElkNodeImpl",206),m(193,729,{109:1,343:1,84:1,174:1,125:1,276:1,193:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},voe),s.xh=function(n){return Gde(this,n)},s.Ih=function(n,t,i){return n==9?Ia(this):Nde(this,n,t,i)},s.Ph=function(n,t,i){var r;return t===9?(this.Cb&&(i=(r=this.Db>>16,r>=0?Gde(this,i):this.Cb.Qh(this,-1-r,null,i))),Ole(this,u(n,26),i)):Yde(this,n,t,i)},s.Rh=function(n,t,i){return t==9?Ole(this,null,i):Qde(this,n,t,i)},s.Th=function(n){return n==9?!!Ia(this):v1e(this,n)},s.$h=function(n,t){if(n===9){kbe(this,u(t,26));return}fbe(this,n,t)},s.fi=function(){return Gu(),$8e},s.hi=function(n){if(n===9){kbe(this,null);return}Ade(this,n)},s.Ib=function(){return _Xe(this)},v(hg,"ElkPortImpl",193);var Can=Hi(yc,"BasicEMap/Entry");m(1091,118,{109:1,45:1,94:1,93:1,136:1,57:1,114:1,52:1,100:1,117:1,118:1},B6),s.Fb=function(n){return this===n},s.jd=function(){return this.b},s.Hb=function(){return vw(this)},s.Ai=function(n){zhe(this,u(n,147))},s.Ih=function(n,t,i){switch(n){case 0:return this.b;case 1:return this.c}return uz(this,n,t,i)},s.Th=function(n){switch(n){case 0:return!!this.b;case 1:return this.c!=null}return _Q(this,n)},s.$h=function(n,t){switch(n){case 0:zhe(this,u(t,147));return;case 1:Fhe(this,t);return}vW(this,n,t)},s.fi=function(){return Gu(),a1},s.hi=function(n){switch(n){case 0:zhe(this,null);return;case 1:Fhe(this,null);return}wW(this,n)},s.yi=function(){var n;return this.a==-1&&(n=this.b,this.a=n?Oi(n):0),this.a},s.kd=function(){return this.c},s.zi=function(n){this.a=n},s.ld=function(n){var t;return t=this.c,Fhe(this,n),t},s.Ib=function(){var n;return(this.Db&64)!=0?Jf(this):(n=new p0,Kt(Kt(Kt(n,this.b?this.b.Og():Vo),eee),Qj(this.c)),n.a)},s.a=-1,s.c=null;var Qd=v(hg,"ElkPropertyToValueMapEntryImpl",1091);m(980,1,{},Hp),v(Qr,"JsonAdapter",980),m(215,63,H1,oh),v(Qr,"JsonImportException",215),m(850,1,{},Yqe),v(Qr,"JsonImporter",850),m(884,1,{},ITe),s.Bi=function(n){GHe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$0$Type",884),m(885,1,{},_Te),s.Bi=function(n){Mqe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$1$Type",885),m(893,1,{},cSe),s.Bi=function(n){BIe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$10$Type",893),m(895,1,{},LTe),s.Bi=function(n){bqe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$11$Type",895),m(896,1,{},PTe),s.Bi=function(n){gqe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$12$Type",896),m(902,1,{},VIe),s.Bi=function(n){RGe(this.a,this.b,this.c,this.d,u(n,139))},v(Qr,"JsonImporter/lambda$13$Type",902),m(901,1,{},YIe),s.Bi=function(n){nKe(this.a,this.b,this.c,this.d,u(n,149))},v(Qr,"JsonImporter/lambda$14$Type",901),m(897,1,{},$Te),s.Bi=function(n){aNe(this.a,this.b,Pt(n))},v(Qr,"JsonImporter/lambda$15$Type",897),m(898,1,{},RTe),s.Bi=function(n){hNe(this.a,this.b,Pt(n))},v(Qr,"JsonImporter/lambda$16$Type",898),m(899,1,{},BTe),s.Bi=function(n){xHe(this.b,this.a,u(n,139))},v(Qr,"JsonImporter/lambda$17$Type",899),m(900,1,{},zTe),s.Bi=function(n){AHe(this.b,this.a,u(n,139))},v(Qr,"JsonImporter/lambda$18$Type",900),m(905,1,{},uSe),s.Bi=function(n){CGe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$19$Type",905),m(886,1,{},oSe),s.Bi=function(n){$He(this.a,u(n,139))},v(Qr,"JsonImporter/lambda$2$Type",886),m(903,1,{},sSe),s.Bi=function(n){V3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$20$Type",903),m(904,1,{},lSe),s.Bi=function(n){Y3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$21$Type",904),m(908,1,{},fSe),s.Bi=function(n){TGe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$22$Type",908),m(906,1,{},aSe),s.Bi=function(n){X3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$23$Type",906),m(907,1,{},hSe),s.Bi=function(n){K3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$24$Type",907),m(910,1,{},dSe),s.Bi=function(n){nGe(this.a,u(n,139))},v(Qr,"JsonImporter/lambda$25$Type",910),m(909,1,{},bSe),s.Bi=function(n){zIe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$26$Type",909),m(911,1,it,FTe),s.Ad=function(n){L9n(this.b,this.a,Pt(n))},v(Qr,"JsonImporter/lambda$27$Type",911),m(912,1,it,JTe),s.Ad=function(n){P9n(this.b,this.a,Pt(n))},v(Qr,"JsonImporter/lambda$28$Type",912),m(913,1,{},HTe),s.Bi=function(n){fUe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$29$Type",913),m(889,1,{},gSe),s.Bi=function(n){VFe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$3$Type",889),m(914,1,{},GTe),s.Bi=function(n){DUe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$30$Type",914),m(915,1,{},wSe),s.Bi=function(n){pRe(this.a,re(n))},v(Qr,"JsonImporter/lambda$31$Type",915),m(916,1,{},pSe),s.Bi=function(n){mRe(this.a,re(n))},v(Qr,"JsonImporter/lambda$32$Type",916),m(917,1,{},mSe),s.Bi=function(n){vRe(this.a,re(n))},v(Qr,"JsonImporter/lambda$33$Type",917),m(918,1,{},vSe),s.Bi=function(n){yRe(this.a,re(n))},v(Qr,"JsonImporter/lambda$34$Type",918),m(919,1,{},ySe),s.Bi=function(n){DMn(this.a,u(n,57))},v(Qr,"JsonImporter/lambda$35$Type",919),m(920,1,{},kSe),s.Bi=function(n){IMn(this.a,u(n,57))},v(Qr,"JsonImporter/lambda$36$Type",920),m(924,1,{},KIe),v(Qr,"JsonImporter/lambda$37$Type",924),m(921,1,it,GNe),s.Ad=function(n){s7n(this.a,this.c,this.b,u(n,372))},v(Qr,"JsonImporter/lambda$38$Type",921),m(922,1,it,qTe),s.Ad=function(n){Xgn(this.a,this.b,u(n,170))},v(Qr,"JsonImporter/lambda$39$Type",922),m(887,1,{},jSe),s.Bi=function(n){V3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$4$Type",887),m(923,1,it,UTe),s.Ad=function(n){Kgn(this.a,this.b,u(n,170))},v(Qr,"JsonImporter/lambda$40$Type",923),m(925,1,it,qNe),s.Ad=function(n){l7n(this.a,this.b,this.c,u(n,8))},v(Qr,"JsonImporter/lambda$41$Type",925),m(888,1,{},ESe),s.Bi=function(n){Y3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$5$Type",888),m(892,1,{},SSe),s.Bi=function(n){YFe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$6$Type",892),m(890,1,{},xSe),s.Bi=function(n){X3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$7$Type",890),m(891,1,{},ASe),s.Bi=function(n){K3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$8$Type",891),m(894,1,{},MSe),s.Bi=function(n){tGe(this.a,u(n,139))},v(Qr,"JsonImporter/lambda$9$Type",894),m(944,1,it,TSe),s.Ad=function(n){T4(this.a,new y2(Pt(n)))},v(Qr,"JsonMetaDataConverter/lambda$0$Type",944),m(945,1,it,CSe),s.Ad=function(n){zvn(this.a,u(n,244))},v(Qr,"JsonMetaDataConverter/lambda$1$Type",945),m(946,1,it,OSe),s.Ad=function(n){N4n(this.a,u(n,144))},v(Qr,"JsonMetaDataConverter/lambda$2$Type",946),m(947,1,it,NSe),s.Ad=function(n){Fvn(this.a,u(n,160))},v(Qr,"JsonMetaDataConverter/lambda$3$Type",947),m(244,23,{3:1,35:1,23:1,244:1},d4);var gG,wG,_ce,pG,mG,vG,Lce,Pce,yG=yt(kN,"GraphFeature",244,Ct,b8n,j3n),Oan;m(11,1,{35:1,147:1},yi,Li,fn,Vr),s.Dd=function(n){return qwn(this,u(n,147))},s.Fb=function(n){return y_e(this,n)},s.Rg=function(){return _e(this)},s.Og=function(){return this.b},s.Hb=function(){return Od(this.b)},s.Ib=function(){return this.b},v(kN,"Property",11),m(657,1,Yt,aX),s.Le=function(n,t){return Ajn(this,u(n,105),u(t,105))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(kN,"PropertyHolderComparator",657),m(698,1,Fr,roe),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return z9n(this)},s.Qb=function(){xAe()},s.Ob=function(){return!!this.a},v(HF,"ElkGraphUtil/AncestorIterator",698);var B8e=Hi(yc,"EList");m(71,56,{20:1,31:1,56:1,18:1,16:1,71:1,61:1}),s._c=function(n,t){$E(this,n,t)},s.Ec=function(n){return Et(this,n)},s.ad=function(n,t){return h1e(this,n,t)},s.Fc=function(n){return er(this,n)},s.Gi=function(){return new m4(this)},s.Hi=function(){return new kC(this)},s.Ii=function(n){return hO(this,n)},s.Ji=function(){return!0},s.Ki=function(n,t){},s.Li=function(){},s.Mi=function(n,t){bY(this,n,t)},s.Ni=function(n,t,i){},s.Oi=function(n,t){},s.Pi=function(n,t,i){},s.Fb=function(n){return pXe(this,n)},s.Hb=function(){return s1e(this)},s.Qi=function(){return!1},s.Jc=function(){return new ut(this)},s.cd=function(){return new p4(this)},s.dd=function(n){var t;if(t=this.gc(),n<0||n>t)throw $(new b2(n,t));return new vV(this,n)},s.Si=function(n,t){this.Ri(n,this.bd(t))},s.Kc=function(n){return sB(this,n)},s.Ui=function(n,t){return t},s.fd=function(n,t){return tv(this,n,t)},s.Ib=function(){return rde(this)},s.Wi=function(){return!0},s.Xi=function(n,t){return t8(this,t)},v(yc,"AbstractEList",71),m(67,71,Mh,z6,Nw,t1e),s.Ci=function(n,t){return lW(this,n,t)},s.Di=function(n){return uHe(this,n)},s.Ei=function(n,t){xO(this,n,t)},s.Fi=function(n){YC(this,n)},s.Yi=function(n){return phe(this,n)},s.$b=function(){vE(this)},s.Gc=function(n){return m8(this,n)},s.Xb=function(n){return K(this,n)},s.Zi=function(n){var t,i,r;++this.j,i=this.g==null?0:this.g.length,n>i&&(r=this.g,t=i+(i/2|0)+4,t=0?(this.ed(t),!0):!1},s.Vi=function(n,t){return this.Bj(n,this.Xi(n,t))},s.gc=function(){return this.Cj()},s.Nc=function(){return this.Dj()},s.Oc=function(n){return this.Ej(n)},s.Ib=function(){return this.Fj()},v(yc,"DelegatingEList",2055),m(2056,2055,$Ze),s.Ci=function(n,t){return tge(this,n,t)},s.Di=function(n){return this.Ci(this.Cj(),n)},s.Ei=function(n,t){tUe(this,n,t)},s.Fi=function(n){qqe(this,n)},s.Ji=function(){return!this.Kj()},s.$b=function(){hS(this)},s.Gj=function(n,t,i,r,c){return new m_e(this,n,t,i,r,c)},s.Hj=function(n){ai(this.hj(),n)},s.Ij=function(){return null},s.Jj=function(){return-1},s.hj=function(){return null},s.Kj=function(){return!1},s.Lj=function(n,t){return t},s.Mj=function(n,t){return t},s.Nj=function(){return!1},s.Oj=function(){return!this.yj()},s.Ri=function(n,t){var i,r;return this.Nj()?(r=this.Oj(),i=E0e(this,n,t),this.Hj(this.Gj(7,ve(t),i,n,r)),i):E0e(this,n,t)},s.ed=function(n){var t,i,r,c;return this.Nj()?(i=null,r=this.Oj(),t=this.Gj(4,c=iR(this,n),null,n,r),this.Kj()&&c?(i=this.Mj(c,i),i?(i.lj(t),i.mj()):this.Hj(t)):i?(i.lj(t),i.mj()):this.Hj(t),c):(c=iR(this,n),this.Kj()&&c&&(i=this.Mj(c,null),i&&i.mj()),c)},s.Vi=function(n,t){return dKe(this,n,t)},v(wy,"DelegatingNotifyingListImpl",2056),m(151,1,RN),s.lj=function(n){return s0e(this,n)},s.mj=function(){jY(this)},s.ej=function(){return this.d},s.Ij=function(){return null},s.Pj=function(){return null},s.fj=function(n){return-1},s.gj=function(){return WUe(this)},s.hj=function(){return null},s.ij=function(){return Nbe(this)},s.jj=function(){return this.o<0?this.o<-2?-2-this.o-1:-1:this.o},s.Qj=function(){return!1},s.kj=function(n){var t,i,r,c,o,l,f,h,b,p,y;switch(this.d){case 1:case 2:switch(c=n.ej(),c){case 1:case 2:if(o=n.hj(),ue(o)===ue(this.hj())&&this.fj(null)==n.fj(null))return this.g=n.gj(),n.ej()==1&&(this.d=1),!0}case 4:{switch(c=n.ej(),c){case 4:{if(o=n.hj(),ue(o)===ue(this.hj())&&this.fj(null)==n.fj(null))return b=yge(this),h=this.o<0?this.o<-2?-2-this.o-1:-1:this.o,l=n.jj(),this.d=6,y=new Nw(2),h<=l?(Et(y,this.n),Et(y,n.ij()),this.g=z(B($t,1),ni,30,15,[this.o=h,l+1])):(Et(y,n.ij()),Et(y,this.n),this.g=z(B($t,1),ni,30,15,[this.o=l,h])),this.n=y,b||(this.o=-2-this.o-1),!0;break}}break}case 6:{switch(c=n.ej(),c){case 4:{if(o=n.hj(),ue(o)===ue(this.hj())&&this.fj(null)==n.fj(null)){for(b=yge(this),l=n.jj(),p=u(this.g,54),r=oe($t,ni,30,p.length+1,15,1),t=0;t>>0,t.toString(16))),r.a+=" (eventType: ",this.d){case 1:{r.a+="SET";break}case 2:{r.a+="UNSET";break}case 3:{r.a+="ADD";break}case 5:{r.a+="ADD_MANY";break}case 4:{r.a+="REMOVE";break}case 6:{r.a+="REMOVE_MANY";break}case 7:{r.a+="MOVE";break}case 8:{r.a+="REMOVING_ADAPTER";break}case 9:{r.a+="RESOLVE";break}default:{IX(r,this.d);break}}if(zXe(this)&&(r.a+=", touch: true"),r.a+=", position: ",IX(r,this.o<0?this.o<-2?-2-this.o-1:-1:this.o),r.a+=", notifier: ",qj(r,this.hj()),r.a+=", feature: ",qj(r,this.Ij()),r.a+=", oldValue: ",qj(r,Nbe(this)),r.a+=", newValue: ",this.d==6&&X(this.g,54)){for(i=u(this.g,54),r.a+="[",n=0;n10?((!this.b||this.c.j!=this.a)&&(this.b=new w2(this),this.a=this.j),rf(this.b,n)):m8(this,n)},s.Wi=function(){return!0},s.a=0,v(yc,"AbstractEList/1",949),m(305,99,iF,b2),v(yc,"AbstractEList/BasicIndexOutOfBoundsException",305),m(42,1,Fr,ut),s.Nb=function(n){Zr(this,n)},s.Vj=function(){if(this.i.j!=this.f)throw $(new Nl)},s.Wj=function(){return ft(this)},s.Ob=function(){return this.e!=this.i.gc()},s.Pb=function(){return this.Wj()},s.Qb=function(){KE(this)},s.e=0,s.f=0,s.g=-1,v(yc,"AbstractEList/EIterator",42),m(286,42,Qh,p4,vV),s.Qb=function(){KE(this)},s.Rb=function(n){oJe(this,n)},s.Xj=function(){var n;try{return n=this.d.Xb(--this.e),this.Vj(),this.g=this.e,n}catch(t){throw t=sr(t),X(t,99)?(this.Vj(),$(new au)):$(t)}},s.Yj=function(n){oHe(this,n)},s.Sb=function(){return this.e!=0},s.Tb=function(){return this.e},s.Ub=function(){return this.Xj()},s.Vb=function(){return this.e-1},s.Wb=function(n){this.Yj(n)},v(yc,"AbstractEList/EListIterator",286),m(355,42,Fr,m4),s.Wj=function(){return LQ(this)},s.Qb=function(){throw $(new _t)},v(yc,"AbstractEList/NonResolvingEIterator",355),m(391,286,Qh,kC,Xle),s.Rb=function(n){throw $(new _t)},s.Wj=function(){var n;try{return n=this.c.Ti(this.e),this.Vj(),this.g=this.e++,n}catch(t){throw t=sr(t),X(t,99)?(this.Vj(),$(new au)):$(t)}},s.Xj=function(){var n;try{return n=this.c.Ti(--this.e),this.Vj(),this.g=this.e,n}catch(t){throw t=sr(t),X(t,99)?(this.Vj(),$(new au)):$(t)}},s.Qb=function(){throw $(new _t)},s.Wb=function(n){throw $(new _t)},v(yc,"AbstractEList/NonResolvingEListIterator",391),m(2042,71,RZe),s.Ci=function(n,t){var i,r,c,o,l,f,h,b,p,y,S;if(c=t.gc(),c!=0){for(b=u(Un(this.a,4),129),p=b==null?0:b.length,S=p+c,r=uQ(this,S),y=p-n,y>0&&Wu(b,n,r,n+c,y),h=t.Jc(),l=0;li)throw $(new b2(n,i));return new IIe(this,n)},s.$b=function(){var n,t;++this.j,n=u(Un(this.a,4),129),t=n==null?0:n.length,g8(this,null),bY(this,t,n)},s.Gc=function(n){var t,i,r,c,o;if(t=u(Un(this.a,4),129),t!=null){if(n!=null){for(r=t,c=0,o=r.length;c=i)throw $(new b2(n,i));return t[n]},s.bd=function(n){var t,i,r;if(t=u(Un(this.a,4),129),t!=null){if(n!=null){for(i=0,r=t.length;ii)throw $(new b2(n,i));return new DIe(this,n)},s.Ri=function(n,t){var i,r,c;if(i=wJe(this),c=i==null?0:i.length,n>=c)throw $(new jo(Mne+n+dg+c));if(t>=c)throw $(new jo(Tne+t+dg+c));return r=i[t],n!=t&&(n0&&Wu(n,0,t,0,i),t},s.Oc=function(n){var t,i,r;return t=u(Un(this.a,4),129),r=t==null?0:t.length,r>0&&(n.lengthr&&tr(n,r,null),n};var Nan;v(yc,"ArrayDelegatingEList",2042),m(1032,42,Fr,zPe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw $(new Nl)},s.Qb=function(){KE(this),this.a=u(Un(this.b.a,4),129)},v(yc,"ArrayDelegatingEList/EIterator",1032),m(712,286,Qh,ZDe,DIe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw $(new Nl)},s.Yj=function(n){oHe(this,n),this.a=u(Un(this.b.a,4),129)},s.Qb=function(){KE(this),this.a=u(Un(this.b.a,4),129)},v(yc,"ArrayDelegatingEList/EListIterator",712),m(1033,355,Fr,FPe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw $(new Nl)},v(yc,"ArrayDelegatingEList/NonResolvingEIterator",1033),m(713,391,Qh,eIe,IIe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw $(new Nl)},v(yc,"ArrayDelegatingEList/NonResolvingEListIterator",713),m(605,305,iF,jK),v(yc,"BasicEList/BasicIndexOutOfBoundsException",605),m(699,67,Mh,Nse),s._c=function(n,t){throw $(new _t)},s.Ec=function(n){throw $(new _t)},s.ad=function(n,t){throw $(new _t)},s.Fc=function(n){throw $(new _t)},s.$b=function(){throw $(new _t)},s.Zi=function(n){throw $(new _t)},s.Jc=function(){return this.Gi()},s.cd=function(){return this.Hi()},s.dd=function(n){return this.Ii(n)},s.Ri=function(n,t){throw $(new _t)},s.Si=function(n,t){throw $(new _t)},s.ed=function(n){throw $(new _t)},s.Kc=function(n){throw $(new _t)},s.fd=function(n,t){throw $(new _t)},v(yc,"BasicEList/UnmodifiableEList",699),m(711,1,{3:1,20:1,18:1,16:1,61:1,586:1}),s._c=function(n,t){_wn(this,n,u(t,45))},s.Ec=function(n){return Tpn(this,u(n,45))},s.Ic=function(n){rc(this,n)},s.Xb=function(n){return u(K(this.c,n),136)},s.Ri=function(n,t){return u(this.c.Ri(n,t),45)},s.Si=function(n,t){Lwn(this,n,u(t,45))},s.ed=function(n){return u(this.c.ed(n),45)},s.fd=function(n,t){return Hvn(this,n,u(t,45))},s.gd=function(n){Vb(this,n)},s.Lc=function(){return new pn(this,16)},s.Mc=function(){return new wn(null,new pn(this,16))},s.ad=function(n,t){return this.c.ad(n,t)},s.Fc=function(n){return this.c.Fc(n)},s.$b=function(){this.c.$b()},s.Gc=function(n){return this.c.Gc(n)},s.Hc=function(n){return yO(this.c,n)},s.Zj=function(){var n,t,i;if(this.d==null){for(this.d=oe(z8e,nme,67,2*this.f+1,0,1),i=this.e,this.f=0,t=this.c.Jc();t.e!=t.i.gc();)n=u(t.Wj(),136),lz(this,n);this.e=i}},s.Fb=function(n){return SNe(this,n)},s.Hb=function(){return s1e(this.c)},s.bd=function(n){return this.c.bd(n)},s.$j=function(){this.c=new DSe(this)},s.dc=function(){return this.f==0},s.Jc=function(){return this.c.Jc()},s.cd=function(){return this.c.cd()},s.dd=function(n){return this.c.dd(n)},s._j=function(){return ZC(this)},s.ak=function(n,t,i){return new UNe(n,t,i)},s.bk=function(){return new mL},s.Kc=function(n){return wBe(this,n)},s.gc=function(){return this.f},s.hd=function(n,t){return new T0(this.c,n,t)},s.Nc=function(){return this.c.Nc()},s.Oc=function(n){return this.c.Oc(n)},s.Ib=function(){return rde(this.c)},s.e=0,s.f=0,v(yc,"BasicEMap",711),m(1027,67,Mh,DSe),s.Ki=function(n,t){wbn(this,u(t,136))},s.Ni=function(n,t,i){var r;++(r=this,u(t,136),r).a.e},s.Oi=function(n,t){pbn(this,u(t,136))},s.Pi=function(n,t,i){bpn(this,u(t,136),u(i,136))},s.Mi=function(n,t){fze(this.a)},v(yc,"BasicEMap/1",1027),m(1028,67,Mh,mL),s.$i=function(n){return oe(YBn,BZe,611,n,0,1)},v(yc,"BasicEMap/2",1028),m(1029,Ha,fs,ISe),s.$b=function(){this.a.c.$b()},s.Gc=function(n){return SQ(this.a,n)},s.Jc=function(){return this.a.f==0?(S9(),eI.a):new pAe(this.a)},s.Kc=function(n){var t;return t=this.a.f,ZB(this.a,n),this.a.f!=t},s.gc=function(){return this.a.f},v(yc,"BasicEMap/3",1029),m(1030,31,Y2,_Se),s.$b=function(){this.a.c.$b()},s.Gc=function(n){return mXe(this.a,n)},s.Jc=function(){return this.a.f==0?(S9(),eI.a):new mAe(this.a)},s.gc=function(){return this.a.f},v(yc,"BasicEMap/4",1030),m(1031,Ha,fs,LSe),s.$b=function(){this.a.c.$b()},s.Gc=function(n){var t,i,r,c,o,l,f,h,b;if(this.a.f>0&&X(n,45)&&(this.a.Zj(),h=u(n,45),f=h.jd(),c=f==null?0:Oi(f),o=Nle(this.a,c),t=this.a.d[o],t)){for(i=u(t.g,374),b=t.i,l=0;l"+this.c},s.a=0;var YBn=v(yc,"BasicEMap/EntryImpl",611);m(534,1,{},J6),v(yc,"BasicEMap/View",534);var eI;m(769,1,{}),s.Fb=function(n){return abe((jn(),Sc),n)},s.Hb=function(){return y1e((jn(),Sc))},s.Ib=function(){return Fa((jn(),Sc))},v(yc,"ECollections/BasicEmptyUnmodifiableEList",769),m(1302,1,Qh,WM),s.Nb=function(n){Zr(this,n)},s.Rb=function(n){throw $(new _t)},s.Ob=function(){return!1},s.Sb=function(){return!1},s.Pb=function(){throw $(new au)},s.Tb=function(){return 0},s.Ub=function(){throw $(new au)},s.Vb=function(){return-1},s.Qb=function(){throw $(new _t)},s.Wb=function(n){throw $(new _t)},v(yc,"ECollections/BasicEmptyUnmodifiableEList/1",1302),m(1300,769,{20:1,18:1,16:1,61:1},Sxe),s._c=function(n,t){zAe()},s.Ec=function(n){return BAe()},s.ad=function(n,t){return FAe()},s.Fc=function(n){return JAe()},s.$b=function(){HAe()},s.Gc=function(n){return!1},s.Hc=function(n){return!1},s.Ic=function(n){rc(this,n)},s.Xb=function(n){return Pse((jn(),n)),null},s.bd=function(n){return-1},s.dc=function(){return!0},s.Jc=function(){return this.a},s.cd=function(){return this.a},s.dd=function(n){return this.a},s.Ri=function(n,t){return GAe()},s.Si=function(n,t){qAe()},s.ed=function(n){return UAe()},s.Kc=function(n){return XAe()},s.fd=function(n,t){return KAe()},s.gc=function(){return 0},s.gd=function(n){Vb(this,n)},s.Lc=function(){return new pn(this,16)},s.Mc=function(){return new wn(null,new pn(this,16))},s.hd=function(n,t){return jn(),new T0(Sc,n,t)},s.Nc=function(){return Ofe((jn(),Sc))},s.Oc=function(n){return jn(),GE(Sc,n)},v(yc,"ECollections/EmptyUnmodifiableEList",1300),m(1301,769,{20:1,18:1,16:1,61:1,586:1},xxe),s._c=function(n,t){zAe()},s.Ec=function(n){return BAe()},s.ad=function(n,t){return FAe()},s.Fc=function(n){return JAe()},s.$b=function(){HAe()},s.Gc=function(n){return!1},s.Hc=function(n){return!1},s.Ic=function(n){rc(this,n)},s.Xb=function(n){return Pse((jn(),n)),null},s.bd=function(n){return-1},s.dc=function(){return!0},s.Jc=function(){return this.a},s.cd=function(){return this.a},s.dd=function(n){return this.a},s.Ri=function(n,t){return GAe()},s.Si=function(n,t){qAe()},s.ed=function(n){return UAe()},s.Kc=function(n){return XAe()},s.fd=function(n,t){return KAe()},s.gc=function(){return 0},s.gd=function(n){Vb(this,n)},s.Lc=function(){return new pn(this,16)},s.Mc=function(){return new wn(null,new pn(this,16))},s.hd=function(n,t){return jn(),new T0(Sc,n,t)},s.Nc=function(){return Ofe((jn(),Sc))},s.Oc=function(n){return jn(),GE(Sc,n)},s._j=function(){return jn(),jn(),i1},v(yc,"ECollections/EmptyUnmodifiableEMap",1301);var J8e=Hi(yc,"Enumerator"),kG;m(290,1,{290:1},DW),s.Fb=function(n){var t;return this===n?!0:X(n,290)?(t=u(n,290),this.f==t.f&&uvn(this.i,t.i)&&cV(this.a,(this.f&256)!=0?(t.f&256)!=0?t.a:null:(t.f&256)!=0?null:t.a)&&cV(this.d,t.d)&&cV(this.g,t.g)&&cV(this.e,t.e)&&lSn(this,t)):!1},s.Hb=function(){return this.f},s.Ib=function(){return WXe(this)},s.f=0;var Dan=0,Ian=0,_an=0,Lan=0,H8e=0,G8e=0,q8e=0,U8e=0,X8e=0,Pan,uA=0,oA=0,$an=0,Ran=0,jG,K8e;v(yc,"URI",290),m(1090,44,bv,Axe),s.yc=function(n,t){return u(Kc(this,Pt(n),u(t,290)),290)},v(yc,"URI/URICache",1090),m(492,67,Mh,ZM,lR),s.Qi=function(){return!0},v(yc,"UniqueEList",492),m(578,63,H1,uB),v(yc,"WrappedException",578);var Zt=Hi(ql,JZe),$m=Hi(ql,HZe),ns=Hi(ql,GZe),Rm=Hi(ql,qZe),Aa=Hi(ql,UZe),vf=Hi(ql,"EClass"),Bce=Hi(ql,"EDataType"),Ban;m(1198,44,bv,Mxe),s.xc=function(n){return $r(n)?lo(this,n):du(Xc(this.f,n))},v(ql,"EDataType/Internal/ConversionDelegate/Factory/Registry/Impl",1198);var EG=Hi(ql,"EEnum"),ed=Hi(ql,XZe),Rc=Hi(ql,KZe),yf=Hi(ql,VZe),kf,vp=Hi(ql,YZe),Bm=Hi(ql,QZe);m(1023,1,{},eT),s.Ib=function(){return"NIL"},v(ql,"EStructuralFeature/Internal/DynamicValueHolder/1",1023);var zan;m(1022,44,bv,Txe),s.xc=function(n){return $r(n)?lo(this,n):du(Xc(this.f,n))},v(ql,"EStructuralFeature/Internal/SettingDelegate/Factory/Registry/Impl",1022);var Fo=Hi(ql,WZe),Uy=Hi(ql,"EValidator/PatternMatcher"),V8e,Y8e,Rn,Wd,zm,pb,Fan,Jan,Han,mb,Zd,vb,yp,ih,Gan,qan,jf,e0,Uan,n0,Fm,Wv,Ac,Xan,Kan,kp,SG=Hi($i,"FeatureMap/Entry");m(533,1,{75:1},A$),s.Jk=function(){return this.a},s.kd=function(){return this.b},v(Fn,"BasicEObjectImpl/1",533),m(1021,1,_ne,KTe),s.Dk=function(n){return fY(this.a,this.b,n)},s.Oj=function(){return L_e(this.a,this.b)},s.Wb=function(n){pae(this.a,this.b,n)},s.Ek=function(){l5n(this.a,this.b)},v(Fn,"BasicEObjectImpl/4",1021),m(2043,1,{114:1}),s.Kk=function(n){this.e=n==0?Van:oe(Ar,On,1,n,5,1)},s.ii=function(n){return this.e[n]},s.ji=function(n,t){this.e[n]=t},s.ki=function(n){this.e[n]=null},s.Lk=function(){return this.c},s.Mk=function(){throw $(new _t)},s.Nk=function(){throw $(new _t)},s.Ok=function(){return this.d},s.Pk=function(){return this.e!=null},s.Qk=function(n){this.c=n},s.Rk=function(n){throw $(new _t)},s.Sk=function(n){throw $(new _t)},s.Tk=function(n){this.d=n};var Van;v(Fn,"BasicEObjectImpl/EPropertiesHolderBaseImpl",2043),m(192,2043,{114:1},tl),s.Mk=function(){return this.a},s.Nk=function(){return this.b},s.Rk=function(n){this.a=n},s.Sk=function(n){this.b=n},v(Fn,"BasicEObjectImpl/EPropertiesHolderImpl",192),m(501,100,WWe,p3),s.rh=function(){return this.f},s.wh=function(){return this.k},s.yh=function(n,t){this.g=n,this.i=t},s.Ah=function(){return(this.j&2)==0?this.fi():this.Xh().Lk()},s.Ch=function(){return this.i},s.th=function(){return(this.j&1)!=0},s.Mh=function(){return this.g},s.Sh=function(){return(this.j&4)!=0},s.Xh=function(){return!this.k&&(this.k=new tl),this.k},s._h=function(n){this.Xh().Qk(n),n?this.j|=2:this.j&=-3},s.bi=function(n){this.Xh().Sk(n),n?this.j|=4:this.j&=-5},s.fi=function(){return(x0(),Rn).S},s.i=0,s.j=1,v(Fn,"EObjectImpl",501),m(785,501,{109:1,94:1,93:1,57:1,114:1,52:1,100:1},bfe),s.ii=function(n){return this.e[n]},s.ji=function(n,t){this.e[n]=t},s.ki=function(n){this.e[n]=null},s.Ah=function(){return this.d},s.Fh=function(n){return zi(this.d,n)},s.Hh=function(){return this.d},s.Lh=function(){return this.e!=null},s.Xh=function(){return!this.k&&(this.k=new vL),this.k},s._h=function(n){this.d=n},s.ei=function(){var n;return this.e==null&&(n=dt(this.d),this.e=n==0?Yan:oe(Ar,On,1,n,5,1)),this},s.gi=function(){return 0};var Yan;v(Fn,"DynamicEObjectImpl",785),m(1483,785,{109:1,45:1,94:1,93:1,136:1,57:1,114:1,52:1,100:1},bDe),s.Fb=function(n){return this===n},s.Hb=function(){return vw(this)},s._h=function(n){this.d=n,this.b=QO(n,"key"),this.c=QO(n,PS)},s.yi=function(){var n;return this.a==-1&&(n=EY(this,this.b),this.a=n==null?0:Oi(n)),this.a},s.jd=function(){return EY(this,this.b)},s.kd=function(){return EY(this,this.c)},s.zi=function(n){this.a=n},s.Ai=function(n){pae(this,this.b,n)},s.ld=function(n){var t;return t=EY(this,this.c),pae(this,this.c,n),t},s.a=0,v(Fn,"DynamicEObjectImpl/BasicEMapEntry",1483),m(1484,1,{114:1},vL),s.Kk=function(n){throw $(new _t)},s.ii=function(n){throw $(new _t)},s.ji=function(n,t){throw $(new _t)},s.ki=function(n){throw $(new _t)},s.Lk=function(){throw $(new _t)},s.Mk=function(){return this.a},s.Nk=function(){return this.b},s.Ok=function(){return this.c},s.Pk=function(){throw $(new _t)},s.Qk=function(n){throw $(new _t)},s.Rk=function(n){this.a=n},s.Sk=function(n){this.b=n},s.Tk=function(n){this.c=n},v(Fn,"DynamicEObjectImpl/DynamicEPropertiesHolderImpl",1484),m(504,161,{109:1,94:1,93:1,587:1,158:1,57:1,114:1,52:1,100:1,504:1,161:1,117:1,118:1},Mb),s.xh=function(n){return qde(this,n)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.d;case 2:return i?(!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),this.b):(!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),ZC(this.b));case 3:return J_e(this);case 4:return!this.a&&(this.a=new mr(wb,this,4)),this.a;case 5:return!this.c&&(this.c=new $3(wb,this,5)),this.c}return Pl(this,n-dt((yn(),Wd)),An((r=u(Un(this,16),29),r||Wd),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 3:return this.Cb&&(i=(c=this.Db>>16,c>=0?qde(this,i):this.Cb.Qh(this,-1-c,null,i))),Tfe(this,u(n,158),i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),Wd)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),Wd)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 2:return!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),U$(this.b,n,i);case 3:return Tfe(this,null,i);case 4:return!this.a&&(this.a=new mr(wb,this,4)),vc(this.a,n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),Wd)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),Wd)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.d!=null;case 2:return!!this.b&&this.b.f!=0;case 3:return!!J_e(this);case 4:return!!this.a&&this.a.i!=0;case 5:return!!this.c&&this.c.i!=0}return Ll(this,n-dt((yn(),Wd)),An((t=u(Un(this,16),29),t||Wd),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:X3n(this,Pt(t));return;case 2:!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),CB(this.b,t);return;case 3:zUe(this,u(t,158));return;case 4:!this.a&&(this.a=new mr(wb,this,4)),kt(this.a),!this.a&&(this.a=new mr(wb,this,4)),er(this.a,u(t,18));return;case 5:!this.c&&(this.c=new $3(wb,this,5)),kt(this.c),!this.c&&(this.c=new $3(wb,this,5)),er(this.c,u(t,18));return}Jl(this,n-dt((yn(),Wd)),An((i=u(Un(this,16),29),i||Wd),n),t)},s.fi=function(){return yn(),Wd},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Hhe(this,null);return;case 2:!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),this.b.c.$b();return;case 3:zUe(this,null);return;case 4:!this.a&&(this.a=new mr(wb,this,4)),kt(this.a);return;case 5:!this.c&&(this.c=new $3(wb,this,5)),kt(this.c);return}Fl(this,n-dt((yn(),Wd)),An((t=u(Un(this,16),29),t||Wd),n))},s.Ib=function(){return NFe(this)},s.d=null,v(Fn,"EAnnotationImpl",504),m(142,711,tme,os),s.Ei=function(n,t){mwn(this,n,u(t,45))},s.Uk=function(n,t){return m2n(this,u(n,45),t)},s.Yi=function(n){return u(u(this.c,72).Yi(n),136)},s.Gi=function(){return u(this.c,72).Gi()},s.Hi=function(){return u(this.c,72).Hi()},s.Ii=function(n){return u(this.c,72).Ii(n)},s.Vk=function(n,t){return U$(this,n,t)},s.Dk=function(n){return u(this.c,77).Dk(n)},s.$j=function(){},s.Oj=function(){return u(this.c,77).Oj()},s.ak=function(n,t,i){var r;return r=u(sl(this.b).ti().pi(this.b),136),r.zi(n),r.Ai(t),r.ld(i),r},s.bk=function(){return new uoe(this)},s.Wb=function(n){CB(this,n)},s.Ek=function(){u(this.c,77).Ek()},v($i,"EcoreEMap",142),m(169,142,tme,Hs),s.Zj=function(){var n,t,i,r,c,o;if(this.d==null){for(o=oe(z8e,nme,67,2*this.f+1,0,1),i=this.c.Jc();i.e!=i.i.gc();)t=u(i.Wj(),136),r=t.yi(),c=(r&oi)%o.length,n=o[c],!n&&(n=o[c]=new uoe(this)),n.Ec(t);this.d=o}},v(Fn,"EAnnotationImpl/1",169),m(293,439,{109:1,94:1,93:1,158:1,197:1,57:1,114:1,470:1,52:1,100:1,161:1,293:1,117:1,118:1}),s.Ih=function(n,t,i){var r,c;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),!!this.Hk();case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q}return Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 9:return EV(this,i)}return c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,Lo(this),t-dt(this.fi()),n,i)},s.Th=function(n){var t,i;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return this.Hk();case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0)}return Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:this.ri(Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Cd(this,u(t,15).a);return;case 5:this.Xk(u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return yn(),Kan},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:this.ri(null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Cd(this,0);return;case 5:this.Xk(1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.mi=function(){ff(this),this.Bb|=1},s.Fk=function(){return ff(this)},s.Gk=function(){return this.t},s.Hk=function(){var n;return n=this.t,n>1||n==-1},s.Qi=function(){return(this.Bb&512)!=0},s.Wk=function(n,t){return z1e(this,n,t)},s.Xk=function(n){O2(this,n)},s.Ib=function(){return tbe(this)},s.s=0,s.t=1,v(Fn,"ETypedElementImpl",293),m(451,293,{109:1,94:1,93:1,158:1,197:1,57:1,179:1,69:1,114:1,470:1,52:1,100:1,161:1,451:1,293:1,117:1,118:1,682:1}),s.xh=function(n){return jHe(this,n)},s.Ih=function(n,t,i){var r,c;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),!!this.Hk();case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return Pn(),(this.Bb&qf)!=0;case 11:return Pn(),(this.Bb&U0)!=0;case 12:return Pn(),(this.Bb&W2)!=0;case 13:return this.j;case 14:return S8(this);case 15:return Pn(),(this.Bb&as)!=0;case 16:return Pn(),(this.Bb&yh)!=0;case 17:return E2(this)}return Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 17:return this.Cb&&(i=(c=this.Db>>16,c>=0?jHe(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,17,i)}return o=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),o.uk().xk(this,Lo(this),t-dt(this.fi()),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 9:return EV(this,i);case 17:return dl(this,null,17,i)}return c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,Lo(this),t-dt(this.fi()),n,i)},s.Th=function(n){var t,i;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return this.Hk();case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return(this.Bb&qf)==0;case 11:return(this.Bb&U0)!=0;case 12:return(this.Bb&W2)!=0;case 13:return this.j!=null;case 14:return S8(this)!=null;case 15:return(this.Bb&as)!=0;case 16:return(this.Bb&yh)!=0;case 17:return!!E2(this)}return Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:UV(this,Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Cd(this,u(t,15).a);return;case 5:this.Xk(u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return;case 10:o8(this,Re($e(t)));return;case 11:f8(this,Re($e(t)));return;case 12:l8(this,Re($e(t)));return;case 13:Ise(this,Pt(t));return;case 15:s8(this,Re($e(t)));return;case 16:a8(this,Re($e(t)));return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return yn(),Xan},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,88)&&H2(Ms(u(this.Cb,88)),4),Mo(this,null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Cd(this,0);return;case 5:this.Xk(1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return;case 10:o8(this,!0);return;case 11:f8(this,!1);return;case 12:l8(this,!1);return;case 13:this.i=null,EB(this,null);return;case 15:s8(this,!1);return;case 16:a8(this,!1);return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.mi=function(){L9(Vc((ls(),nc),this)),ff(this),this.Bb|=1},s.nk=function(){return this.f},s.gk=function(){return S8(this)},s.ok=function(){return E2(this)},s.sk=function(){return null},s.Yk=function(){return this.k},s.Jj=function(){return this.n},s.tk=function(){return wz(this)},s.uk=function(){var n,t,i,r,c,o,l,f,h;return this.p||(i=E2(this),(i.i==null&&vh(i),i.i).length,r=this.sk(),r&&dt(E2(r)),c=ff(this),l=c.ik(),n=l?(l.i&1)!=0?l==ts?Yi:l==$t?jr:l==Hm?h7:l==Jr?gr:l==Ep?cp:l==t5?up:l==ds?py:XS:l:null,t=S8(this),f=c.gk(),Djn(this),(this.Bb&yh)!=0&&((o=Wde((ls(),nc),i))&&o!=this||(o=D4(Vc(nc,this))))?this.p=new YTe(this,o):this.Hk()?this.$k()?r?(this.Bb&as)!=0?n?this._k()?this.p=new Jb(47,n,this,r):this.p=new Jb(5,n,this,r):this._k()?this.p=new Kb(46,this,r):this.p=new Kb(4,this,r):n?this._k()?this.p=new Jb(49,n,this,r):this.p=new Jb(7,n,this,r):this._k()?this.p=new Kb(48,this,r):this.p=new Kb(6,this,r):(this.Bb&as)!=0?n?n==wg?this.p=new Ed(50,Can,this):this._k()?this.p=new Ed(43,n,this):this.p=new Ed(1,n,this):this._k()?this.p=new xd(42,this):this.p=new xd(0,this):n?n==wg?this.p=new Ed(41,Can,this):this._k()?this.p=new Ed(45,n,this):this.p=new Ed(3,n,this):this._k()?this.p=new xd(44,this):this.p=new xd(2,this):X(c,159)?n==SG?this.p=new xd(40,this):(this.Bb&512)!=0?(this.Bb&as)!=0?n?this.p=new Ed(9,n,this):this.p=new xd(8,this):n?this.p=new Ed(11,n,this):this.p=new xd(10,this):(this.Bb&as)!=0?n?this.p=new Ed(13,n,this):this.p=new xd(12,this):n?this.p=new Ed(15,n,this):this.p=new xd(14,this):r?(h=r.t,h>1||h==-1?this._k()?(this.Bb&as)!=0?n?this.p=new Jb(25,n,this,r):this.p=new Kb(24,this,r):n?this.p=new Jb(27,n,this,r):this.p=new Kb(26,this,r):(this.Bb&as)!=0?n?this.p=new Jb(29,n,this,r):this.p=new Kb(28,this,r):n?this.p=new Jb(31,n,this,r):this.p=new Kb(30,this,r):this._k()?(this.Bb&as)!=0?n?this.p=new Jb(33,n,this,r):this.p=new Kb(32,this,r):n?this.p=new Jb(35,n,this,r):this.p=new Kb(34,this,r):(this.Bb&as)!=0?n?this.p=new Jb(37,n,this,r):this.p=new Kb(36,this,r):n?this.p=new Jb(39,n,this,r):this.p=new Kb(38,this,r)):this._k()?(this.Bb&as)!=0?n?this.p=new Ed(17,n,this):this.p=new xd(16,this):n?this.p=new Ed(19,n,this):this.p=new xd(18,this):(this.Bb&as)!=0?n?this.p=new Ed(21,n,this):this.p=new xd(20,this):n?this.p=new Ed(23,n,this):this.p=new xd(22,this):this.Zk()?this._k()?this.p=new BNe(u(c,29),this,r):this.p=new bae(u(c,29),this,r):X(c,159)?n==SG?this.p=new xd(40,this):(this.Bb&as)!=0?n?this.p=new PDe(t,f,this,(AQ(),l==$t?i7e:l==ts?W8e:l==Ep?r7e:l==Hm?t7e:l==Jr?n7e:l==t5?c7e:l==ds?Z8e:l==Wl?e7e:Jce)):this.p=new WIe(u(c,159),t,f,this):n?this.p=new LDe(t,f,this,(AQ(),l==$t?i7e:l==ts?W8e:l==Ep?r7e:l==Hm?t7e:l==Jr?n7e:l==t5?c7e:l==ds?Z8e:l==Wl?e7e:Jce)):this.p=new QIe(u(c,159),t,f,this):this.$k()?r?(this.Bb&as)!=0?this._k()?this.p=new FNe(u(c,29),this,r):this.p=new Zle(u(c,29),this,r):this._k()?this.p=new zNe(u(c,29),this,r):this.p=new WK(u(c,29),this,r):(this.Bb&as)!=0?this._k()?this.p=new $Oe(u(c,29),this):this.p=new mle(u(c,29),this):this._k()?this.p=new POe(u(c,29),this):this.p=new RK(u(c,29),this):this._k()?r?(this.Bb&as)!=0?this.p=new JNe(u(c,29),this,r):this.p=new efe(u(c,29),this,r):(this.Bb&as)!=0?this.p=new ROe(u(c,29),this):this.p=new vle(u(c,29),this):r?(this.Bb&as)!=0?this.p=new HNe(u(c,29),this,r):this.p=new nfe(u(c,29),this,r):(this.Bb&as)!=0?this.p=new BOe(u(c,29),this):this.p=new sR(u(c,29),this)),this.p},s.pk=function(){return(this.Bb&qf)!=0},s.Zk=function(){return!1},s.$k=function(){return!1},s.qk=function(){return(this.Bb&yh)!=0},s.vk=function(){return xY(this)},s._k=function(){return!1},s.rk=function(){return(this.Bb&as)!=0},s.al=function(n){this.k=n},s.ri=function(n){UV(this,n)},s.Ib=function(){return zz(this)},s.e=!1,s.n=0,v(Fn,"EStructuralFeatureImpl",451),m(335,451,{109:1,94:1,93:1,38:1,158:1,197:1,57:1,179:1,69:1,114:1,470:1,52:1,100:1,335:1,161:1,451:1,293:1,117:1,118:1,682:1},mX),s.Ih=function(n,t,i){var r,c;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),!!Y0e(this);case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return Pn(),(this.Bb&qf)!=0;case 11:return Pn(),(this.Bb&U0)!=0;case 12:return Pn(),(this.Bb&W2)!=0;case 13:return this.j;case 14:return S8(this);case 15:return Pn(),(this.Bb&as)!=0;case 16:return Pn(),(this.Bb&yh)!=0;case 17:return E2(this);case 18:return Pn(),(this.Bb&Bu)!=0;case 19:return t?UY(this):e$e(this)}return Pl(this,n-dt((yn(),zm)),An((r=u(Un(this,16),29),r||zm),n),t,i)},s.Th=function(n){var t,i;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return Y0e(this);case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return(this.Bb&qf)==0;case 11:return(this.Bb&U0)!=0;case 12:return(this.Bb&W2)!=0;case 13:return this.j!=null;case 14:return S8(this)!=null;case 15:return(this.Bb&as)!=0;case 16:return(this.Bb&yh)!=0;case 17:return!!E2(this);case 18:return(this.Bb&Bu)!=0;case 19:return!!e$e(this)}return Ll(this,n-dt((yn(),zm)),An((t=u(Un(this,16),29),t||zm),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:UV(this,Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Cd(this,u(t,15).a);return;case 5:SAe(this,u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return;case 10:o8(this,Re($e(t)));return;case 11:f8(this,Re($e(t)));return;case 12:l8(this,Re($e(t)));return;case 13:Ise(this,Pt(t));return;case 15:s8(this,Re($e(t)));return;case 16:a8(this,Re($e(t)));return;case 18:pQ(this,Re($e(t)));return}Jl(this,n-dt((yn(),zm)),An((i=u(Un(this,16),29),i||zm),n),t)},s.fi=function(){return yn(),zm},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,88)&&H2(Ms(u(this.Cb,88)),4),Mo(this,null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Cd(this,0);return;case 5:this.b=0,O2(this,1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return;case 10:o8(this,!0);return;case 11:f8(this,!1);return;case 12:l8(this,!1);return;case 13:this.i=null,EB(this,null);return;case 15:s8(this,!1);return;case 16:a8(this,!1);return;case 18:pQ(this,!1);return}Fl(this,n-dt((yn(),zm)),An((t=u(Un(this,16),29),t||zm),n))},s.mi=function(){UY(this),L9(Vc((ls(),nc),this)),ff(this),this.Bb|=1},s.Hk=function(){return Y0e(this)},s.Wk=function(n,t){return this.b=0,this.a=null,z1e(this,n,t)},s.Xk=function(n){SAe(this,n)},s.Ib=function(){var n;return(this.Db&64)!=0?zz(this):(n=new cf(zz(this)),n.a+=" (iD: ",md(n,(this.Bb&Bu)!=0),n.a+=")",n.a)},s.b=0,v(Fn,"EAttributeImpl",335),m(360,439,{109:1,94:1,93:1,143:1,158:1,197:1,57:1,114:1,52:1,100:1,360:1,161:1,117:1,118:1,681:1}),s.bl=function(n){return n.Ah()==this},s.xh=function(n){return QQ(this,n)},s.yh=function(n,t){this.w=null,this.Db=t<<16|this.Db&255,this.Cb=n},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return this.D!=null?this.D:this.B;case 3:return Fw(this);case 4:return this.gk();case 5:return this.F;case 6:return t?sl(this):R9(this);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),this.A}return Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 6:return this.Cb&&(i=(c=this.Db>>16,c>=0?QQ(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,6,i)}return o=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),o.uk().xk(this,Lo(this),t-dt(this.fi()),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 6:return dl(this,null,6,i);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),vc(this.A,n,i)}return c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,Lo(this),t-dt(this.fi()),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.D!=null&&this.D==this.F;case 3:return!!Fw(this);case 4:return this.gk()!=null;case 5:return this.F!=null&&this.F!=this.D&&this.F!=this.B;case 6:return!!R9(this);case 7:return!!this.A&&this.A.i!=0}return Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:RR(this,Pt(t));return;case 2:xK(this,Pt(t));return;case 5:N8(this,Pt(t));return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A),!this.A&&(this.A=new rs(Fo,this,7)),er(this.A,u(t,18));return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return yn(),Fan},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,184)&&(u(this.Cb,184).tb=null),Mo(this,null);return;case 2:i8(this,null),U9(this,this.D);return;case 5:N8(this,null);return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A);return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.fk=function(){var n;return this.G==-1&&(this.G=(n=sl(this),n?Ld(n.si(),this):-1)),this.G},s.gk=function(){return null},s.hk=function(){return sl(this)},s.cl=function(){return this.v},s.ik=function(){return Fw(this)},s.jk=function(){return this.D!=null?this.D:this.B},s.kk=function(){return this.F},s.dk=function(n){return FW(this,n)},s.dl=function(n){this.v=n},s.el=function(n){HBe(this,n)},s.fl=function(n){this.C=n},s.ri=function(n){RR(this,n)},s.Ib=function(){return VB(this)},s.C=null,s.D=null,s.G=-1,v(Fn,"EClassifierImpl",360),m(88,360,{109:1,94:1,93:1,29:1,143:1,158:1,197:1,57:1,114:1,52:1,100:1,88:1,360:1,161:1,471:1,117:1,118:1,681:1},ij),s.bl=function(n){return r2n(this,n.Ah())},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return this.D!=null?this.D:this.B;case 3:return Fw(this);case 4:return null;case 5:return this.F;case 6:return t?sl(this):R9(this);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),this.A;case 8:return Pn(),(this.Bb&256)!=0;case 9:return Pn(),(this.Bb&512)!=0;case 10:return tu(this);case 11:return!this.q&&(this.q=new pe(yf,this,11,10)),this.q;case 12:return fv(this);case 13:return lS(this);case 14:return lS(this),this.r;case 15:return fv(this),this.k;case 16:return B0e(this);case 17:return qW(this);case 18:return vh(this);case 19:return Nz(this);case 20:return fv(this),this.o;case 21:return!this.s&&(this.s=new pe(ns,this,21,17)),this.s;case 22:return Vu(this);case 23:return NW(this)}return Pl(this,n-dt((yn(),pb)),An((r=u(Un(this,16),29),r||pb),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 6:return this.Cb&&(i=(c=this.Db>>16,c>=0?QQ(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,6,i);case 11:return!this.q&&(this.q=new pe(yf,this,11,10)),To(this.q,n,i);case 21:return!this.s&&(this.s=new pe(ns,this,21,17)),To(this.s,n,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),pb)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),pb)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 6:return dl(this,null,6,i);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),vc(this.A,n,i);case 11:return!this.q&&(this.q=new pe(yf,this,11,10)),vc(this.q,n,i);case 21:return!this.s&&(this.s=new pe(ns,this,21,17)),vc(this.s,n,i);case 22:return vc(Vu(this),n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),pb)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),pb)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.D!=null&&this.D==this.F;case 3:return!!Fw(this);case 4:return!1;case 5:return this.F!=null&&this.F!=this.D&&this.F!=this.B;case 6:return!!R9(this);case 7:return!!this.A&&this.A.i!=0;case 8:return(this.Bb&256)!=0;case 9:return(this.Bb&512)!=0;case 10:return!!this.u&&Vu(this.u.a).i!=0&&!(this.n&&zQ(this.n));case 11:return!!this.q&&this.q.i!=0;case 12:return fv(this).i!=0;case 13:return lS(this).i!=0;case 14:return lS(this),this.r.i!=0;case 15:return fv(this),this.k.i!=0;case 16:return B0e(this).i!=0;case 17:return qW(this).i!=0;case 18:return vh(this).i!=0;case 19:return Nz(this).i!=0;case 20:return fv(this),!!this.o;case 21:return!!this.s&&this.s.i!=0;case 22:return!!this.n&&zQ(this.n);case 23:return NW(this).i!=0}return Ll(this,n-dt((yn(),pb)),An((t=u(Un(this,16),29),t||pb),n))},s.Wh=function(n){var t;return t=this.i==null||this.q&&this.q.i!=0?null:QO(this,n),t||Tge(this,n)},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:RR(this,Pt(t));return;case 2:xK(this,Pt(t));return;case 5:N8(this,Pt(t));return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A),!this.A&&(this.A=new rs(Fo,this,7)),er(this.A,u(t,18));return;case 8:H1e(this,Re($e(t)));return;case 9:G1e(this,Re($e(t)));return;case 10:hS(tu(this)),er(tu(this),u(t,18));return;case 11:!this.q&&(this.q=new pe(yf,this,11,10)),kt(this.q),!this.q&&(this.q=new pe(yf,this,11,10)),er(this.q,u(t,18));return;case 21:!this.s&&(this.s=new pe(ns,this,21,17)),kt(this.s),!this.s&&(this.s=new pe(ns,this,21,17)),er(this.s,u(t,18));return;case 22:kt(Vu(this)),er(Vu(this),u(t,18));return}Jl(this,n-dt((yn(),pb)),An((i=u(Un(this,16),29),i||pb),n),t)},s.fi=function(){return yn(),pb},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,184)&&(u(this.Cb,184).tb=null),Mo(this,null);return;case 2:i8(this,null),U9(this,this.D);return;case 5:N8(this,null);return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A);return;case 8:H1e(this,!1);return;case 9:G1e(this,!1);return;case 10:this.u&&hS(this.u);return;case 11:!this.q&&(this.q=new pe(yf,this,11,10)),kt(this.q);return;case 21:!this.s&&(this.s=new pe(ns,this,21,17)),kt(this.s);return;case 22:this.n&&kt(this.n);return}Fl(this,n-dt((yn(),pb)),An((t=u(Un(this,16),29),t||pb),n))},s.mi=function(){var n,t;if(fv(this),lS(this),B0e(this),qW(this),vh(this),Nz(this),NW(this),vE(A3n(Ms(this))),this.s)for(n=0,t=this.s.i;n=0;--t)K(this,t);return hde(this,n)},s.Ek=function(){kt(this)},s.Xi=function(n,t){return gBe(this,n,t)},v($i,"EcoreEList",623),m(491,623,lu,_C),s.Ji=function(){return!1},s.Jj=function(){return this.c},s.Kj=function(){return!1},s.ml=function(){return!0},s.Qi=function(){return!0},s.Ui=function(n,t){return t},s.Wi=function(){return!1},s.c=0,v($i,"EObjectEList",491),m(81,491,lu,mr),s.Kj=function(){return!0},s.kl=function(){return!1},s.$k=function(){return!0},v($i,"EObjectContainmentEList",81),m(543,81,lu,$$),s.Li=function(){this.b=!0},s.Oj=function(){return this.b},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.b,this.b=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.b=!1},s.b=!1,v($i,"EObjectContainmentEList/Unsettable",543),m(1130,543,lu,$De),s.Ri=function(n,t){var i,r;return i=u(RE(this,n,t),87),Fs(this.e)&&s9(this,new tO(this.a,7,(yn(),Jan),ve(t),(r=i.c,X(r,88)?u(r,29):jf),n)),i},s.Sj=function(n,t){return fEn(this,u(n,87),t)},s.Tj=function(n,t){return aEn(this,u(n,87),t)},s.Uj=function(n,t,i){return hAn(this,u(n,87),u(t,87),i)},s.Gj=function(n,t,i,r,c){switch(n){case 3:return dE(this,n,t,i,r,this.i>1);case 5:return dE(this,n,t,i,r,this.i-u(i,16).gc()>0);default:return new L1(this.e,n,this.c,t,i,r,!0)}},s.Rj=function(){return!0},s.Oj=function(){return zQ(this)},s.Ek=function(){kt(this)},v(Fn,"EClassImpl/1",1130),m(1144,1143,eme),s.bj=function(n){var t,i,r,c,o,l,f;if(i=n.ej(),i!=8){if(r=KEn(n),r==0)switch(i){case 1:case 9:{f=n.ij(),f!=null&&(t=Ms(u(f,471)),!t.c&&(t.c=new Ol),sB(t.c,n.hj())),l=n.gj(),l!=null&&(c=u(l,471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),Et(t.c,u(n.hj(),29))));break}case 3:{l=n.gj(),l!=null&&(c=u(l,471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),Et(t.c,u(n.hj(),29))));break}case 5:{if(l=n.gj(),l!=null)for(o=u(l,18).Jc();o.Ob();)c=u(o.Pb(),471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),Et(t.c,u(n.hj(),29)));break}case 4:{f=n.ij(),f!=null&&(c=u(f,471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),sB(t.c,n.hj())));break}case 6:{if(f=n.ij(),f!=null)for(o=u(f,18).Jc();o.Ob();)c=u(o.Pb(),471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),sB(t.c,n.hj()));break}}this.ol(r)}},s.ol=function(n){AXe(this,n)},s.b=63,v(Fn,"ESuperAdapter",1144),m(1145,1144,eme,$Se),s.ol=function(n){H2(this,n)},v(Fn,"EClassImpl/10",1145),m(1134,699,lu),s.Ci=function(n,t){return lW(this,n,t)},s.Di=function(n){return uHe(this,n)},s.Ei=function(n,t){xO(this,n,t)},s.Fi=function(n){YC(this,n)},s.Yi=function(n){return phe(this,n)},s.Vi=function(n,t){return SY(this,n,t)},s.Uk=function(n,t){throw $(new _t)},s.Gi=function(){return new m4(this)},s.Hi=function(){return new kC(this)},s.Ii=function(n){return hO(this,n)},s.Vk=function(n,t){throw $(new _t)},s.Dk=function(n){return this},s.Oj=function(){return this.i!=0},s.Wb=function(n){throw $(new _t)},s.Ek=function(){throw $(new _t)},v($i,"EcoreEList/UnmodifiableEList",1134),m(333,1134,lu,N3),s.Wi=function(){return!1},v($i,"EcoreEList/UnmodifiableEList/FastCompare",333),m(1137,333,lu,Lze),s.bd=function(n){var t,i,r;if(X(n,179)&&(t=u(n,179),i=t.Jj(),i!=-1)){for(r=this.i;i4)if(this.dk(n)){if(this.$k()){if(r=u(n,52),i=r.Bh(),f=i==this.b&&(this.kl()?r.vh(r.Ch(),u(An(Go(this.b),this.Jj()).Fk(),29).ik())==Oc(u(An(Go(this.b),this.Jj()),19)).n:-1-r.Ch()==this.Jj()),this.ll()&&!f&&!i&&r.Gh()){for(c=0;c1||r==-1)):!1},s.kl=function(){var n,t,i;return t=An(Go(this.b),this.Jj()),X(t,103)?(n=u(t,19),i=Oc(n),!!i):!1},s.ll=function(){var n,t;return t=An(Go(this.b),this.Jj()),X(t,103)?(n=u(t,19),(n.Bb&Ec)!=0):!1},s.bd=function(n){var t,i,r,c;if(r=this.xj(n),r>=0)return r;if(this.ml()){for(i=0,c=this.Cj();i=0;--n)cN(this,n,this.vj(n));return this.Dj()},s.Oc=function(n){var t;if(this.ll())for(t=this.Cj()-1;t>=0;--t)cN(this,t,this.vj(t));return this.Ej(n)},s.Ek=function(){hS(this)},s.Xi=function(n,t){return B$e(this,n,t)},v($i,"DelegatingEcoreEList",744),m(1140,744,rme,VOe),s.oj=function(n,t){Ipn(this,n,u(t,29))},s.pj=function(n){ywn(this,u(n,29))},s.vj=function(n){var t,i;return t=u(K(Vu(this.a),n),87),i=t.c,X(i,88)?u(i,29):(yn(),jf)},s.Aj=function(n){var t,i;return t=u(U2(Vu(this.a),n),87),i=t.c,X(i,88)?u(i,29):(yn(),jf)},s.Bj=function(n,t){return zSn(this,n,u(t,29))},s.Ji=function(){return!1},s.Gj=function(n,t,i,r,c){return null},s.qj=function(){return new zSe(this)},s.rj=function(){kt(Vu(this.a))},s.sj=function(n){return DFe(this,n)},s.tj=function(n){var t,i;for(i=n.Jc();i.Ob();)if(t=i.Pb(),!DFe(this,t))return!1;return!0},s.uj=function(n){var t,i,r;if(X(n,16)&&(r=u(n,16),r.gc()==Vu(this.a).i)){for(t=r.Jc(),i=new ut(this);t.Ob();)if(ue(t.Pb())!==ue(ft(i)))return!1;return!0}return!1},s.wj=function(){var n,t,i,r,c;for(i=1,t=new ut(Vu(this.a));t.e!=t.i.gc();)n=u(ft(t),87),r=(c=n.c,X(c,88)?u(c,29):(yn(),jf)),i=31*i+(r?vw(r):0);return i},s.xj=function(n){var t,i,r,c;for(r=0,i=new ut(Vu(this.a));i.e!=i.i.gc();){if(t=u(ft(i),87),ue(n)===ue((c=t.c,X(c,88)?u(c,29):(yn(),jf))))return r;++r}return-1},s.yj=function(){return Vu(this.a).i==0},s.zj=function(){return null},s.Cj=function(){return Vu(this.a).i},s.Dj=function(){var n,t,i,r,c,o;for(o=Vu(this.a).i,c=oe(Ar,On,1,o,5,1),i=0,t=new ut(Vu(this.a));t.e!=t.i.gc();)n=u(ft(t),87),c[i++]=(r=n.c,X(r,88)?u(r,29):(yn(),jf));return c},s.Ej=function(n){var t,i,r,c,o,l,f;for(f=Vu(this.a).i,n.lengthf&&tr(n,f,null),r=0,i=new ut(Vu(this.a));i.e!=i.i.gc();)t=u(ft(i),87),o=(l=t.c,X(l,88)?u(l,29):(yn(),jf)),tr(n,r++,o);return n},s.Fj=function(){var n,t,i,r,c;for(c=new pd,c.a+="[",n=Vu(this.a),t=0,r=Vu(this.a).i;t>16,c>=0?QQ(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,6,i);case 9:return!this.a&&(this.a=new pe(ed,this,9,5)),To(this.a,n,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),mb)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),mb)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 6:return dl(this,null,6,i);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),vc(this.A,n,i);case 9:return!this.a&&(this.a=new pe(ed,this,9,5)),vc(this.a,n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),mb)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),mb)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.D!=null&&this.D==this.F;case 3:return!!Fw(this);case 4:return!!O1e(this);case 5:return this.F!=null&&this.F!=this.D&&this.F!=this.B;case 6:return!!R9(this);case 7:return!!this.A&&this.A.i!=0;case 8:return(this.Bb&256)==0;case 9:return!!this.a&&this.a.i!=0}return Ll(this,n-dt((yn(),mb)),An((t=u(Un(this,16),29),t||mb),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:RR(this,Pt(t));return;case 2:xK(this,Pt(t));return;case 5:N8(this,Pt(t));return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A),!this.A&&(this.A=new rs(Fo,this,7)),er(this.A,u(t,18));return;case 8:FB(this,Re($e(t)));return;case 9:!this.a&&(this.a=new pe(ed,this,9,5)),kt(this.a),!this.a&&(this.a=new pe(ed,this,9,5)),er(this.a,u(t,18));return}Jl(this,n-dt((yn(),mb)),An((i=u(Un(this,16),29),i||mb),n),t)},s.fi=function(){return yn(),mb},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,184)&&(u(this.Cb,184).tb=null),Mo(this,null);return;case 2:i8(this,null),U9(this,this.D);return;case 5:N8(this,null);return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A);return;case 8:FB(this,!0);return;case 9:!this.a&&(this.a=new pe(ed,this,9,5)),kt(this.a);return}Fl(this,n-dt((yn(),mb)),An((t=u(Un(this,16),29),t||mb),n))},s.mi=function(){var n,t;if(this.a)for(n=0,t=this.a.i;n>16==5?u(this.Cb,675):null}return Pl(this,n-dt((yn(),Zd)),An((r=u(Un(this,16),29),r||Zd),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 5:return this.Cb&&(i=(c=this.Db>>16,c>=0?IHe(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,5,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),Zd)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),Zd)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 5:return dl(this,null,5,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),Zd)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),Zd)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.d!=0;case 3:return!!this.b;case 4:return this.c!=null;case 5:return!!(this.Db>>16==5&&u(this.Cb,675))}return Ll(this,n-dt((yn(),Zd)),An((t=u(Un(this,16),29),t||Zd),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Pt(t));return;case 2:NY(this,u(t,15).a);return;case 3:Pqe(this,u(t,2001));return;case 4:IY(this,Pt(t));return}Jl(this,n-dt((yn(),Zd)),An((i=u(Un(this,16),29),i||Zd),n),t)},s.fi=function(){return yn(),Zd},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Mo(this,null);return;case 2:NY(this,0);return;case 3:Pqe(this,null);return;case 4:IY(this,null);return}Fl(this,n-dt((yn(),Zd)),An((t=u(Un(this,16),29),t||Zd),n))},s.Ib=function(){var n;return n=this.c,n??this.zb},s.b=null,s.c=null,s.d=0,v(Fn,"EEnumLiteralImpl",568);var QBn=Hi(Fn,"EFactoryImpl/InternalEDateTimeFormat");m(485,1,{2076:1},UT),v(Fn,"EFactoryImpl/1ClientInternalEDateTimeFormat",485),m(248,118,{109:1,94:1,93:1,87:1,57:1,114:1,52:1,100:1,248:1,117:1,118:1},hw),s.zh=function(n,t,i){var r;return i=dl(this,n,t,i),this.e&&X(n,179)&&(r=Oz(this,this.e),r!=this.c&&(i=D8(this,r,i))),i},s.Ih=function(n,t,i){var r;switch(n){case 0:return this.f;case 1:return!this.d&&(this.d=new mr(Rc,this,1)),this.d;case 2:return t?Jz(this):this.c;case 3:return this.b;case 4:return this.e;case 5:return t?HQ(this):this.a}return Pl(this,n-dt((yn(),yp)),An((r=u(Un(this,16),29),r||yp),n),t,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return mFe(this,null,i);case 1:return!this.d&&(this.d=new mr(Rc,this,1)),vc(this.d,n,i);case 3:return pFe(this,null,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),yp)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),yp)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.f;case 1:return!!this.d&&this.d.i!=0;case 2:return!!this.c;case 3:return!!this.b;case 4:return!!this.e;case 5:return!!this.a}return Ll(this,n-dt((yn(),yp)),An((t=u(Un(this,16),29),t||yp),n))},s.$h=function(n,t){var i;switch(n){case 0:WHe(this,u(t,87));return;case 1:!this.d&&(this.d=new mr(Rc,this,1)),kt(this.d),!this.d&&(this.d=new mr(Rc,this,1)),er(this.d,u(t,18));return;case 3:u0e(this,u(t,87));return;case 4:x0e(this,u(t,834));return;case 5:q9(this,u(t,143));return}Jl(this,n-dt((yn(),yp)),An((i=u(Un(this,16),29),i||yp),n),t)},s.fi=function(){return yn(),yp},s.hi=function(n){var t;switch(n){case 0:WHe(this,null);return;case 1:!this.d&&(this.d=new mr(Rc,this,1)),kt(this.d);return;case 3:u0e(this,null);return;case 4:x0e(this,null);return;case 5:q9(this,null);return}Fl(this,n-dt((yn(),yp)),An((t=u(Un(this,16),29),t||yp),n))},s.Ib=function(){var n;return n=new il(Jf(this)),n.a+=" (expression: ",YW(this,n),n.a+=")",n.a};var Q8e;v(Fn,"EGenericTypeImpl",248),m(2029,2024,KF),s.Ei=function(n,t){QOe(this,n,t)},s.Uk=function(n,t){return QOe(this,this.gc(),n),t},s.Yi=function(n){return Yu(this.nj(),n)},s.Gi=function(){return this.Hi()},s.nj=function(){return new GSe(this)},s.Hi=function(){return this.Ii(0)},s.Ii=function(n){return this.nj().dd(n)},s.Vk=function(n,t){return P2(this,n,!0),t},s.Ri=function(n,t){var i,r;return r=eW(this,t),i=this.dd(n),i.Rb(r),r},s.Si=function(n,t){var i;P2(this,t,!0),i=this.dd(n),i.Rb(t)},v($i,"AbstractSequentialInternalEList",2029),m(482,2029,KF,jC),s.Yi=function(n){return Yu(this.nj(),n)},s.Gi=function(){return this.b==null?(kd(),kd(),nI):this.ql()},s.nj=function(){return new wCe(this.a,this.b)},s.Hi=function(){return this.b==null?(kd(),kd(),nI):this.ql()},s.Ii=function(n){var t,i;if(this.b==null){if(n<0||n>1)throw $(new jo($S+n+", size=0"));return kd(),kd(),nI}for(i=this.ql(),t=0;t0;)if(t=this.c[--this.d],(!this.e||t.nk()!=U7||t.Jj()!=0)&&(!this.tl()||this.b.Uh(t))){if(o=this.b.Kh(t,this.sl()),this.f=(Cc(),u(t,69).vk()),this.f||t.Hk()){if(this.sl()?(r=u(o,16),this.k=r):(r=u(o,72),this.k=this.j=r),X(this.k,59)?(this.o=this.k.gc(),this.n=this.o):this.p=this.j?this.j.Ii(this.k.gc()):this.k.dd(this.k.gc()),this.p?YGe(this,this.p):uqe(this))return c=this.p?this.p.Ub():this.j?this.j.Yi(--this.n):this.k.Xb(--this.n),this.f?(n=u(c,75),n.Jk(),i=n.kd(),this.i=i):(i=c,this.i=i),this.g=-3,!0}else if(o!=null)return this.k=null,this.p=null,i=o,this.i=i,this.g=-2,!0}return this.k=null,this.p=null,this.g=-1,!1}else return c=this.p?this.p.Ub():this.j?this.j.Yi(--this.n):this.k.Xb(--this.n),this.f?(n=u(c,75),n.Jk(),i=n.kd(),this.i=i):(i=c,this.i=i),this.g=-3,!0}},s.Pb=function(){return OB(this)},s.Tb=function(){return this.a},s.Ub=function(){var n;if(this.g<-1||this.Sb())return--this.a,this.g=0,n=this.i,this.Sb(),n;throw $(new au)},s.Vb=function(){return this.a-1},s.Qb=function(){throw $(new _t)},s.sl=function(){return!1},s.Wb=function(n){throw $(new _t)},s.tl=function(){return!0},s.a=0,s.d=0,s.f=!1,s.g=0,s.n=0,s.o=0;var nI;v($i,"EContentsEList/FeatureIteratorImpl",287),m(700,287,VF,ple),s.sl=function(){return!0},v($i,"EContentsEList/ResolvingFeatureIteratorImpl",700),m(1147,700,VF,IOe),s.tl=function(){return!1},v(Fn,"ENamedElementImpl/1/1",1147),m(1148,287,VF,_Oe),s.tl=function(){return!1},v(Fn,"ENamedElementImpl/1/2",1148),m(39,151,RN,M2,iY,Ir,pY,L1,Pf,Che,vLe,Ohe,yLe,Gae,kLe,Ihe,jLe,qae,ELe,Nhe,SLe,uE,tO,$V,Dhe,xLe,Uae,ALe),s.Ij=function(){return ahe(this)},s.Pj=function(){var n;return n=ahe(this),n?n.gk():null},s.fj=function(n){return this.b==-1&&this.a&&(this.b=this.c.Eh(this.a.Jj(),this.a.nk())),this.c.vh(this.b,n)},s.hj=function(){return this.c},s.Qj=function(){var n;return n=ahe(this),n?n.rk():!1},s.b=-1,v(Fn,"ENotificationImpl",39),m(403,293,{109:1,94:1,93:1,158:1,197:1,57:1,62:1,114:1,470:1,52:1,100:1,161:1,403:1,293:1,117:1,118:1},vX),s.xh=function(n){return LHe(this,n)},s.Ih=function(n,t,i){var r,c,o;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),o=this.t,o>1||o==-1;case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return this.Db>>16==10?u(this.Cb,29):null;case 11:return!this.d&&(this.d=new rs(Fo,this,11)),this.d;case 12:return!this.c&&(this.c=new pe(vp,this,12,10)),this.c;case 13:return!this.a&&(this.a=new AC(this,this)),this.a;case 14:return Cs(this)}return Pl(this,n-dt((yn(),e0)),An((r=u(Un(this,16),29),r||e0),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 10:return this.Cb&&(i=(c=this.Db>>16,c>=0?LHe(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,10,i);case 12:return!this.c&&(this.c=new pe(vp,this,12,10)),To(this.c,n,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),e0)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),e0)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 9:return EV(this,i);case 10:return dl(this,null,10,i);case 11:return!this.d&&(this.d=new rs(Fo,this,11)),vc(this.d,n,i);case 12:return!this.c&&(this.c=new pe(vp,this,12,10)),vc(this.c,n,i);case 14:return vc(Cs(this),n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),e0)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),e0)),n,i)},s.Th=function(n){var t,i,r;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return r=this.t,r>1||r==-1;case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return!!(this.Db>>16==10&&u(this.Cb,29));case 11:return!!this.d&&this.d.i!=0;case 12:return!!this.c&&this.c.i!=0;case 13:return!!this.a&&Cs(this.a.a).i!=0&&!(this.b&&FQ(this.b));case 14:return!!this.b&&FQ(this.b)}return Ll(this,n-dt((yn(),e0)),An((t=u(Un(this,16),29),t||e0),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Cd(this,u(t,15).a);return;case 5:O2(this,u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return;case 11:!this.d&&(this.d=new rs(Fo,this,11)),kt(this.d),!this.d&&(this.d=new rs(Fo,this,11)),er(this.d,u(t,18));return;case 12:!this.c&&(this.c=new pe(vp,this,12,10)),kt(this.c),!this.c&&(this.c=new pe(vp,this,12,10)),er(this.c,u(t,18));return;case 13:!this.a&&(this.a=new AC(this,this)),hS(this.a),!this.a&&(this.a=new AC(this,this)),er(this.a,u(t,18));return;case 14:kt(Cs(this)),er(Cs(this),u(t,18));return}Jl(this,n-dt((yn(),e0)),An((i=u(Un(this,16),29),i||e0),n),t)},s.fi=function(){return yn(),e0},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Mo(this,null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Cd(this,0);return;case 5:O2(this,1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return;case 11:!this.d&&(this.d=new rs(Fo,this,11)),kt(this.d);return;case 12:!this.c&&(this.c=new pe(vp,this,12,10)),kt(this.c);return;case 13:this.a&&hS(this.a);return;case 14:this.b&&kt(this.b);return}Fl(this,n-dt((yn(),e0)),An((t=u(Un(this,16),29),t||e0),n))},s.mi=function(){var n,t;if(this.c)for(n=0,t=this.c.i;nf&&tr(n,f,null),r=0,i=new ut(Cs(this.a));i.e!=i.i.gc();)t=u(ft(i),87),o=(l=t.c,l||(yn(),ih)),tr(n,r++,o);return n},s.Fj=function(){var n,t,i,r,c;for(c=new pd,c.a+="[",n=Cs(this.a),t=0,r=Cs(this.a).i;t1);case 5:return dE(this,n,t,i,r,this.i-u(i,16).gc()>0);default:return new L1(this.e,n,this.c,t,i,r,!0)}},s.Rj=function(){return!0},s.Oj=function(){return FQ(this)},s.Ek=function(){kt(this)},v(Fn,"EOperationImpl/2",1331),m(493,1,{1999:1,493:1},VTe),v(Fn,"EPackageImpl/1",493),m(14,81,lu,pe),s.gl=function(){return this.d},s.hl=function(){return this.b},s.kl=function(){return!0},s.b=0,v($i,"EObjectContainmentWithInverseEList",14),m(361,14,lu,y4),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectContainmentWithInverseEList/Resolving",361),m(312,361,lu,m2),s.Li=function(){this.a.tb=null},v(Fn,"EPackageImpl/2",312),m(1243,1,{},Ss),v(Fn,"EPackageImpl/3",1243),m(721,44,bv,yoe),s._b=function(n){return $r(n)?RV(this,n):!!Xc(this.f,n)},v(Fn,"EPackageRegistryImpl",721),m(503,293,{109:1,94:1,93:1,158:1,197:1,57:1,2078:1,114:1,470:1,52:1,100:1,161:1,503:1,293:1,117:1,118:1},yX),s.xh=function(n){return PHe(this,n)},s.Ih=function(n,t,i){var r,c,o;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),o=this.t,o>1||o==-1;case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return this.Db>>16==10?u(this.Cb,62):null}return Pl(this,n-dt((yn(),Fm)),An((r=u(Un(this,16),29),r||Fm),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),To(this.Ab,n,i);case 10:return this.Cb&&(i=(c=this.Db>>16,c>=0?PHe(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,10,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),Fm)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),Fm)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 9:return EV(this,i);case 10:return dl(this,null,10,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),Fm)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),Fm)),n,i)},s.Th=function(n){var t,i,r;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return r=this.t,r>1||r==-1;case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return!!(this.Db>>16==10&&u(this.Cb,62))}return Ll(this,n-dt((yn(),Fm)),An((t=u(Un(this,16),29),t||Fm),n))},s.fi=function(){return yn(),Fm},v(Fn,"EParameterImpl",503),m(103,451,{109:1,94:1,93:1,158:1,197:1,57:1,19:1,179:1,69:1,114:1,470:1,52:1,100:1,161:1,103:1,451:1,293:1,117:1,118:1,682:1},kle),s.Ih=function(n,t,i){var r,c,o,l;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),l=this.t,l>1||l==-1;case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return Pn(),(this.Bb&qf)!=0;case 11:return Pn(),(this.Bb&U0)!=0;case 12:return Pn(),(this.Bb&W2)!=0;case 13:return this.j;case 14:return S8(this);case 15:return Pn(),(this.Bb&as)!=0;case 16:return Pn(),(this.Bb&yh)!=0;case 17:return E2(this);case 18:return Pn(),(this.Bb&Bu)!=0;case 19:return Pn(),o=Oc(this),!!(o&&(o.Bb&Bu)!=0);case 20:return Pn(),(this.Bb&Ec)!=0;case 21:return t?Oc(this):this.b;case 22:return t?d1e(this):HPe(this);case 23:return!this.a&&(this.a=new $3(Rm,this,23)),this.a}return Pl(this,n-dt((yn(),Wv)),An((r=u(Un(this,16),29),r||Wv),n),t,i)},s.Th=function(n){var t,i,r,c;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return c=this.t,c>1||c==-1;case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return(this.Bb&qf)==0;case 11:return(this.Bb&U0)!=0;case 12:return(this.Bb&W2)!=0;case 13:return this.j!=null;case 14:return S8(this)!=null;case 15:return(this.Bb&as)!=0;case 16:return(this.Bb&yh)!=0;case 17:return!!E2(this);case 18:return(this.Bb&Bu)!=0;case 19:return r=Oc(this),!!r&&(r.Bb&Bu)!=0;case 20:return(this.Bb&Ec)==0;case 21:return!!this.b;case 22:return!!HPe(this);case 23:return!!this.a&&this.a.i!=0}return Ll(this,n-dt((yn(),Wv)),An((t=u(Un(this,16),29),t||Wv),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:UV(this,Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Cd(this,u(t,15).a);return;case 5:O2(this,u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return;case 10:o8(this,Re($e(t)));return;case 11:f8(this,Re($e(t)));return;case 12:l8(this,Re($e(t)));return;case 13:Ise(this,Pt(t));return;case 15:s8(this,Re($e(t)));return;case 16:a8(this,Re($e(t)));return;case 18:D4n(this,Re($e(t)));return;case 20:Y1e(this,Re($e(t)));return;case 21:Khe(this,u(t,19));return;case 23:!this.a&&(this.a=new $3(Rm,this,23)),kt(this.a),!this.a&&(this.a=new $3(Rm,this,23)),er(this.a,u(t,18));return}Jl(this,n-dt((yn(),Wv)),An((i=u(Un(this,16),29),i||Wv),n),t)},s.fi=function(){return yn(),Wv},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,88)&&H2(Ms(u(this.Cb,88)),4),Mo(this,null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Cd(this,0);return;case 5:O2(this,1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return;case 10:o8(this,!0);return;case 11:f8(this,!1);return;case 12:l8(this,!1);return;case 13:this.i=null,EB(this,null);return;case 15:s8(this,!1);return;case 16:a8(this,!1);return;case 18:Q1e(this,!1),X(this.Cb,88)&&H2(Ms(u(this.Cb,88)),2);return;case 20:Y1e(this,!0);return;case 21:Khe(this,null);return;case 23:!this.a&&(this.a=new $3(Rm,this,23)),kt(this.a);return}Fl(this,n-dt((yn(),Wv)),An((t=u(Un(this,16),29),t||Wv),n))},s.mi=function(){d1e(this),L9(Vc((ls(),nc),this)),ff(this),this.Bb|=1},s.sk=function(){return Oc(this)},s.Zk=function(){var n;return n=Oc(this),!!n&&(n.Bb&Bu)!=0},s.$k=function(){return(this.Bb&Bu)!=0},s._k=function(){return(this.Bb&Ec)!=0},s.Wk=function(n,t){return this.c=null,z1e(this,n,t)},s.Ib=function(){var n;return(this.Db&64)!=0?zz(this):(n=new cf(zz(this)),n.a+=" (containment: ",md(n,(this.Bb&Bu)!=0),n.a+=", resolveProxies: ",md(n,(this.Bb&Ec)!=0),n.a+=")",n.a)},v(Fn,"EReferenceImpl",103),m(549,118,{109:1,45:1,94:1,93:1,136:1,57:1,114:1,52:1,100:1,549:1,117:1,118:1},Ph),s.Fb=function(n){return this===n},s.jd=function(){return this.b},s.kd=function(){return this.c},s.Hb=function(){return vw(this)},s.Ai=function(n){K3n(this,Pt(n))},s.ld=function(n){return $3n(this,Pt(n))},s.Ih=function(n,t,i){var r;switch(n){case 0:return this.b;case 1:return this.c}return Pl(this,n-dt((yn(),Ac)),An((r=u(Un(this,16),29),r||Ac),n),t,i)},s.Th=function(n){var t;switch(n){case 0:return this.b!=null;case 1:return this.c!=null}return Ll(this,n-dt((yn(),Ac)),An((t=u(Un(this,16),29),t||Ac),n))},s.$h=function(n,t){var i;switch(n){case 0:V3n(this,Pt(t));return;case 1:Jhe(this,Pt(t));return}Jl(this,n-dt((yn(),Ac)),An((i=u(Un(this,16),29),i||Ac),n),t)},s.fi=function(){return yn(),Ac},s.hi=function(n){var t;switch(n){case 0:qhe(this,null);return;case 1:Jhe(this,null);return}Fl(this,n-dt((yn(),Ac)),An((t=u(Un(this,16),29),t||Ac),n))},s.yi=function(){var n;return this.a==-1&&(n=this.b,this.a=n==null?0:Od(n)),this.a},s.zi=function(n){this.a=n},s.Ib=function(){var n;return(this.Db&64)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (key: ",Bc(n,this.b),n.a+=", value: ",Bc(n,this.c),n.a+=")",n.a)},s.a=-1,s.b=null,s.c=null;var Iu=v(Fn,"EStringToStringMapEntryImpl",549),Wan=Hi($i,"FeatureMap/Entry/Internal");m(562,1,YF),s.vl=function(n){return this.wl(u(n,52))},s.wl=function(n){return this.vl(n)},s.Fb=function(n){var t,i;return this===n?!0:X(n,75)?(t=u(n,75),t.Jk()==this.c?(i=this.kd(),i==null?t.kd()==null:di(i,t.kd())):!1):!1},s.Jk=function(){return this.c},s.Hb=function(){var n;return n=this.kd(),Oi(this.c)^(n==null?0:Oi(n))},s.Ib=function(){var n,t;return n=this.c,t=sl(n.ok()).vi(),n.ve(),(t!=null&&t.length!=0?t+":"+n.ve():n.ve())+"="+this.kd()},v(Fn,"EStructuralFeatureImpl/BasicFeatureMapEntry",562),m(777,562,YF,Mle),s.wl=function(n){return new Mle(this.c,n)},s.kd=function(){return this.a},s.xl=function(n,t,i){return y7n(this,n,this.a,t,i)},s.yl=function(n,t,i){return k7n(this,n,this.a,t,i)},v(Fn,"EStructuralFeatureImpl/ContainmentUpdatingFeatureMapEntry",777),m(1304,1,{},YTe),s.wk=function(n,t,i,r,c){var o;return o=u(F9(n,this.b),219),o.Wl(this.a).Dk(r)},s.xk=function(n,t,i,r,c){var o;return o=u(F9(n,this.b),219),o.Nl(this.a,r,c)},s.yk=function(n,t,i,r,c){var o;return o=u(F9(n,this.b),219),o.Ol(this.a,r,c)},s.zk=function(n,t,i){var r;return r=u(F9(n,this.b),219),r.Wl(this.a).Oj()},s.Ak=function(n,t,i,r){var c;c=u(F9(n,this.b),219),c.Wl(this.a).Wb(r)},s.Bk=function(n,t,i){return u(F9(n,this.b),219).Wl(this.a)},s.Ck=function(n,t,i){var r;r=u(F9(n,this.b),219),r.Wl(this.a).Ek()},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateFeatureMapDelegator",1304),m(89,1,{},Ed,Jb,xd,Kb),s.wk=function(n,t,i,r,c){var o;if(o=t.ii(i),o==null&&t.ji(i,o=Wz(this,n)),!c)switch(this.e){case 50:case 41:return u(o,586)._j();case 40:return u(o,219).Tl()}return o},s.xk=function(n,t,i,r,c){var o,l;return l=t.ii(i),l==null&&t.ji(i,l=Wz(this,n)),o=u(l,72).Uk(r,c),o},s.yk=function(n,t,i,r,c){var o;return o=t.ii(i),o!=null&&(c=u(o,72).Vk(r,c)),c},s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null&&u(r,77).Oj()},s.Ak=function(n,t,i,r){var c;c=u(t.ii(i),77),!c&&t.ji(i,c=Wz(this,n)),c.Wb(r)},s.Bk=function(n,t,i){var r,c;return c=t.ii(i),c==null&&t.ji(i,c=Wz(this,n)),X(c,77)?u(c,77):(r=u(t.ii(i),16),new JSe(r))},s.Ck=function(n,t,i){var r;r=u(t.ii(i),77),!r&&t.ji(i,r=Wz(this,n)),r.Ek()},s.b=0,s.e=0,v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateMany",89),m(498,1,{}),s.xk=function(n,t,i,r,c){throw $(new _t)},s.yk=function(n,t,i,r,c){throw $(new _t)},s.Bk=function(n,t,i){return new XIe(this,n,t,i)};var h1;v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingle",498),m(1321,1,_ne,XIe),s.Dk=function(n){return this.a.wk(this.c,this.d,this.b,n,!0)},s.Oj=function(){return this.a.zk(this.c,this.d,this.b)},s.Wb=function(n){this.a.Ak(this.c,this.d,this.b,n)},s.Ek=function(){this.a.Ck(this.c,this.d,this.b)},s.b=0,v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingle/1",1321),m(770,498,{},bae),s.wk=function(n,t,i,r,c){return $W(n,n.Mh(),n.Ch())==this.b?this._k()&&r?SW(n):n.Mh():null},s.xk=function(n,t,i,r,c){var o,l;return n.Mh()&&(c=(o=n.Ch(),o>=0?n.xh(c):n.Mh().Qh(n,-1-o,null,c))),l=zi(n.Ah(),this.e),n.zh(r,l,c)},s.yk=function(n,t,i,r,c){var o;return o=zi(n.Ah(),this.e),n.zh(null,o,c)},s.zk=function(n,t,i){var r;return r=zi(n.Ah(),this.e),!!n.Mh()&&n.Ch()==r},s.Ak=function(n,t,i,r){var c,o,l,f,h;if(r!=null&&!FW(this.a,r))throw $(new l9(QF+(X(r,57)?c0e(u(r,57).Ah()):xhe(Us(r)))+WF+this.a+"'"));if(c=n.Mh(),l=zi(n.Ah(),this.e),ue(r)!==ue(c)||n.Ch()!=l&&r!=null){if(w8(n,u(r,57)))throw $(new Gn(LS+n.Ib()));h=null,c&&(h=(o=n.Ch(),o>=0?n.xh(h):n.Mh().Qh(n,-1-o,null,h))),f=u(r,52),f&&(h=f.Oh(n,zi(f.Ah(),this.b),null,h)),h=n.zh(f,l,h),h&&h.mj()}else n.sh()&&n.th()&&ai(n,new Ir(n,1,l,r,r))},s.Ck=function(n,t,i){var r,c,o,l;r=n.Mh(),r?(l=(c=n.Ch(),c>=0?n.xh(null):n.Mh().Qh(n,-1-c,null,null)),o=zi(n.Ah(),this.e),l=n.zh(null,o,l),l&&l.mj()):n.sh()&&n.th()&&ai(n,new uE(n,1,this.e,null,null))},s._k=function(){return!1},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleContainer",770),m(1305,770,{},BNe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleContainerResolving",1305),m(560,498,{}),s.wk=function(n,t,i,r,c){var o;return o=t.ii(i),o==null?this.b:ue(o)===ue(h1)?null:o},s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null&&(ue(r)===ue(h1)||!di(r,this.b))},s.Ak=function(n,t,i,r){var c,o;n.sh()&&n.th()?(c=(o=t.ii(i),o==null?this.b:ue(o)===ue(h1)?null:o),r==null?this.c!=null?(t.ji(i,null),r=this.b):this.b!=null?t.ji(i,h1):t.ji(i,null):(this.zl(r),t.ji(i,r)),ai(n,this.d.Al(n,1,this.e,c,r))):r==null?this.c!=null?t.ji(i,null):this.b!=null?t.ji(i,h1):t.ji(i,null):(this.zl(r),t.ji(i,r))},s.Ck=function(n,t,i){var r,c;n.sh()&&n.th()?(r=(c=t.ii(i),c==null?this.b:ue(c)===ue(h1)?null:c),t.ki(i),ai(n,this.d.Al(n,1,this.e,r,this.b))):t.ki(i)},s.zl=function(n){throw $(new WSe)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData",560),m(Ev,1,{},cw),s.Al=function(n,t,i,r,c){return new uE(n,t,i,r,c)},s.Bl=function(n,t,i,r,c,o){return new $V(n,t,i,r,c,o)};var W8e,Z8e,e7e,n7e,t7e,i7e,r7e,Jce,c7e;v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator",Ev),m(1322,Ev,{},jL),s.Al=function(n,t,i,r,c){return new Uae(n,t,i,Re($e(r)),Re($e(c)))},s.Bl=function(n,t,i,r,c,o){return new ALe(n,t,i,Re($e(r)),Re($e(c)),o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/1",1322),m(1323,Ev,{},EL),s.Al=function(n,t,i,r,c){return new Che(n,t,i,u(r,221).a,u(c,221).a)},s.Bl=function(n,t,i,r,c,o){return new vLe(n,t,i,u(r,221).a,u(c,221).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/2",1323),m(1324,Ev,{},SL),s.Al=function(n,t,i,r,c){return new Ohe(n,t,i,u(r,180).a,u(c,180).a)},s.Bl=function(n,t,i,r,c,o){return new yLe(n,t,i,u(r,180).a,u(c,180).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/3",1324),m(1325,Ev,{},dU),s.Al=function(n,t,i,r,c){return new Gae(n,t,i,ne(re(r)),ne(re(c)))},s.Bl=function(n,t,i,r,c,o){return new kLe(n,t,i,ne(re(r)),ne(re(c)),o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/4",1325),m(1326,Ev,{},Jk),s.Al=function(n,t,i,r,c){return new Ihe(n,t,i,u(r,164).a,u(c,164).a)},s.Bl=function(n,t,i,r,c,o){return new jLe(n,t,i,u(r,164).a,u(c,164).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/5",1326),m(1327,Ev,{},$h),s.Al=function(n,t,i,r,c){return new qae(n,t,i,u(r,15).a,u(c,15).a)},s.Bl=function(n,t,i,r,c,o){return new ELe(n,t,i,u(r,15).a,u(c,15).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/6",1327),m(1328,Ev,{},h0),s.Al=function(n,t,i,r,c){return new Nhe(n,t,i,u(r,190).a,u(c,190).a)},s.Bl=function(n,t,i,r,c,o){return new SLe(n,t,i,u(r,190).a,u(c,190).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/7",1328),m(1329,Ev,{},xL),s.Al=function(n,t,i,r,c){return new Dhe(n,t,i,u(r,191).a,u(c,191).a)},s.Bl=function(n,t,i,r,c,o){return new xLe(n,t,i,u(r,191).a,u(c,191).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/8",1329),m(1307,560,{},QIe),s.zl=function(n){if(!this.a.dk(n))throw $(new l9(QF+Us(n)+WF+this.a+"'"))},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataDynamic",1307),m(1308,560,{},LDe),s.zl=function(n){},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataStatic",1308),m(771,560,{}),s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null},s.Ak=function(n,t,i,r){var c,o;n.sh()&&n.th()?(c=!0,o=t.ii(i),o==null?(c=!1,o=this.b):ue(o)===ue(h1)&&(o=null),r==null?this.c!=null?(t.ji(i,null),r=this.b):t.ji(i,h1):(this.zl(r),t.ji(i,r)),ai(n,this.d.Bl(n,1,this.e,o,r,!c))):r==null?this.c!=null?t.ji(i,null):t.ji(i,h1):(this.zl(r),t.ji(i,r))},s.Ck=function(n,t,i){var r,c;n.sh()&&n.th()?(r=!0,c=t.ii(i),c==null?(r=!1,c=this.b):ue(c)===ue(h1)&&(c=null),t.ki(i),ai(n,this.d.Bl(n,2,this.e,c,this.b,r))):t.ki(i)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettable",771),m(1309,771,{},WIe),s.zl=function(n){if(!this.a.dk(n))throw $(new l9(QF+Us(n)+WF+this.a+"'"))},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettableDynamic",1309),m(1310,771,{},PDe),s.zl=function(n){},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettableStatic",1310),m(402,498,{},sR),s.wk=function(n,t,i,r,c){var o,l,f,h,b;if(b=t.ii(i),this.rk()&&ue(b)===ue(h1))return null;if(this._k()&&r&&b!=null){if(f=u(b,52),f.Sh()&&(h=$0(n,f),f!=h)){if(!FW(this.a,h))throw $(new l9(QF+Us(h)+WF+this.a+"'"));t.ji(i,b=h),this.$k()&&(o=u(h,52),l=f.Qh(n,this.b?zi(f.Ah(),this.b):-1-zi(n.Ah(),this.e),null,null),!o.Mh()&&(l=o.Oh(n,this.b?zi(o.Ah(),this.b):-1-zi(n.Ah(),this.e),null,l)),l&&l.mj()),n.sh()&&n.th()&&ai(n,new uE(n,9,this.e,f,h))}return b}else return b},s.xk=function(n,t,i,r,c){var o,l;return l=t.ii(i),ue(l)===ue(h1)&&(l=null),t.ji(i,r),this.Kj()?ue(l)!==ue(r)&&l!=null&&(o=u(l,52),c=o.Qh(n,zi(o.Ah(),this.b),null,c)):this.$k()&&l!=null&&(c=u(l,52).Qh(n,-1-zi(n.Ah(),this.e),null,c)),n.sh()&&n.th()&&(!c&&(c=new m0(4)),c.lj(new uE(n,1,this.e,l,r))),c},s.yk=function(n,t,i,r,c){var o;return o=t.ii(i),ue(o)===ue(h1)&&(o=null),t.ki(i),n.sh()&&n.th()&&(!c&&(c=new m0(4)),this.rk()?c.lj(new uE(n,2,this.e,o,null)):c.lj(new uE(n,1,this.e,o,null))),c},s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null},s.Ak=function(n,t,i,r){var c,o,l,f,h;if(r!=null&&!FW(this.a,r))throw $(new l9(QF+(X(r,57)?c0e(u(r,57).Ah()):xhe(Us(r)))+WF+this.a+"'"));h=t.ii(i),f=h!=null,this.rk()&&ue(h)===ue(h1)&&(h=null),l=null,this.Kj()?ue(h)!==ue(r)&&(h!=null&&(c=u(h,52),l=c.Qh(n,zi(c.Ah(),this.b),null,l)),r!=null&&(c=u(r,52),l=c.Oh(n,zi(c.Ah(),this.b),null,l))):this.$k()&&ue(h)!==ue(r)&&(h!=null&&(l=u(h,52).Qh(n,-1-zi(n.Ah(),this.e),null,l)),r!=null&&(l=u(r,52).Oh(n,-1-zi(n.Ah(),this.e),null,l))),r==null&&this.rk()?t.ji(i,h1):t.ji(i,r),n.sh()&&n.th()?(o=new $V(n,1,this.e,h,r,this.rk()&&!f),l?(l.lj(o),l.mj()):ai(n,o)):l&&l.mj()},s.Ck=function(n,t,i){var r,c,o,l,f;f=t.ii(i),l=f!=null,this.rk()&&ue(f)===ue(h1)&&(f=null),o=null,f!=null&&(this.Kj()?(r=u(f,52),o=r.Qh(n,zi(r.Ah(),this.b),null,o)):this.$k()&&(o=u(f,52).Qh(n,-1-zi(n.Ah(),this.e),null,o))),t.ki(i),n.sh()&&n.th()?(c=new $V(n,this.rk()?2:1,this.e,f,null,l),o?(o.lj(c),o.mj()):ai(n,c)):o&&o.mj()},s.Kj=function(){return!1},s.$k=function(){return!1},s._k=function(){return!1},s.rk=function(){return!1},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObject",402),m(561,402,{},RK),s.$k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainment",561),m(1313,561,{},POe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentResolving",1313),m(773,561,{},mle),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentUnsettable",773),m(1315,773,{},$Oe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentUnsettableResolving",1315),m(638,561,{},WK),s.Kj=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverse",638),m(1314,638,{},zNe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseResolving",1314),m(774,638,{},Zle),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseUnsettable",774),m(1316,774,{},FNe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseUnsettableResolving",1316),m(639,402,{},vle),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolving",639),m(1317,639,{},ROe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingUnsettable",1317),m(775,639,{},efe),s.Kj=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingWithInverse",775),m(1318,775,{},JNe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingWithInverseUnsettable",1318),m(1311,402,{},BOe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectUnsettable",1311),m(772,402,{},nfe),s.Kj=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectWithInverse",772),m(1312,772,{},HNe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectWithInverseUnsettable",1312),m(776,562,YF,Qfe),s.wl=function(n){return new Qfe(this.a,this.c,n)},s.kd=function(){return this.b},s.xl=function(n,t,i){return p9n(this,n,this.b,i)},s.yl=function(n,t,i){return m9n(this,n,this.b,i)},v(Fn,"EStructuralFeatureImpl/InverseUpdatingFeatureMapEntry",776),m(1319,1,_ne,JSe),s.Dk=function(n){return this.a},s.Oj=function(){return X(this.a,98)?u(this.a,98).Oj():!this.a.dc()},s.Wb=function(n){this.a.$b(),this.a.Fc(u(n,16))},s.Ek=function(){X(this.a,98)?u(this.a,98).Ek():this.a.$b()},v(Fn,"EStructuralFeatureImpl/SettingMany",1319),m(1320,562,YF,gPe),s.vl=function(n){return new FK((Ei(),aA),this.b.oi(this.a,n))},s.kd=function(){return null},s.xl=function(n,t,i){return i},s.yl=function(n,t,i){return i},v(Fn,"EStructuralFeatureImpl/SimpleContentFeatureMapEntry",1320),m(640,562,YF,FK),s.vl=function(n){return new FK(this.c,n)},s.kd=function(){return this.a},s.xl=function(n,t,i){return i},s.yl=function(n,t,i){return i},v(Fn,"EStructuralFeatureImpl/SimpleFeatureMapEntry",640),m(396,492,Mh,Ol),s.$i=function(n){return oe(vf,On,29,n,0,1)},s.Wi=function(){return!1},v(Fn,"ESuperAdapter/1",396),m(446,439,{109:1,94:1,93:1,158:1,197:1,57:1,114:1,834:1,52:1,100:1,161:1,446:1,117:1,118:1},Hk),s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return!this.a&&(this.a=new tE(this,Rc,this)),this.a}return Pl(this,n-dt((yn(),kp)),An((r=u(Un(this,16),29),r||kp),n),t,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 2:return!this.a&&(this.a=new tE(this,Rc,this)),vc(this.a,n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),kp)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),kp)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return!!this.a&&this.a.i!=0}return Ll(this,n-dt((yn(),kp)),An((t=u(Un(this,16),29),t||kp),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Pt(t));return;case 2:!this.a&&(this.a=new tE(this,Rc,this)),kt(this.a),!this.a&&(this.a=new tE(this,Rc,this)),er(this.a,u(t,18));return}Jl(this,n-dt((yn(),kp)),An((i=u(Un(this,16),29),i||kp),n),t)},s.fi=function(){return yn(),kp},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Mo(this,null);return;case 2:!this.a&&(this.a=new tE(this,Rc,this)),kt(this.a);return}Fl(this,n-dt((yn(),kp)),An((t=u(Un(this,16),29),t||kp),n))},v(Fn,"ETypeParameterImpl",446),m(447,81,lu,tE),s.Lj=function(n,t){return lMn(this,u(n,87),t)},s.Mj=function(n,t){return fMn(this,u(n,87),t)},v(Fn,"ETypeParameterImpl/1",447),m(637,44,bv,kX),s.ec=function(){return new OP(this)},v(Fn,"ETypeParameterImpl/2",637),m(557,Ha,fs,OP),s.Ec=function(n){return mNe(this,u(n,87))},s.Fc=function(n){var t,i,r;for(r=!1,i=n.Jc();i.Ob();)t=u(i.Pb(),87),ei(this.a,t,"")==null&&(r=!0);return r},s.$b=function(){Hu(this.a)},s.Gc=function(n){return so(this.a,n)},s.Jc=function(){var n;return n=new D2(new on(this.a).a),new NP(n)},s.Kc=function(n){return n$e(this,n)},s.gc=function(){return xj(this.a)},v(Fn,"ETypeParameterImpl/2/1",557),m(558,1,Fr,NP),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(Q3(this.a).jd(),87)},s.Ob=function(){return this.a.b},s.Qb=function(){gRe(this.a)},v(Fn,"ETypeParameterImpl/2/1/1",558),m(1281,44,bv,Nxe),s._b=function(n){return $r(n)?RV(this,n):!!Xc(this.f,n)},s.xc=function(n){var t,i;return t=$r(n)?lo(this,n):du(Xc(this.f,n)),X(t,835)?(i=u(t,835),t=i.Ik(),ei(this,u(n,241),t),t):t??(n==null?(BX(),ehn):null)},v(Fn,"EValidatorRegistryImpl",1281),m(1303,710,{109:1,94:1,93:1,469:1,158:1,57:1,114:1,2002:1,52:1,100:1,161:1,117:1,118:1},uw),s.oi=function(n,t){switch(n.fk()){case 21:case 22:case 23:case 24:case 26:case 31:case 32:case 37:case 38:case 39:case 40:case 43:case 44:case 48:case 49:case 20:return t==null?null:su(t);case 25:return C8n(t);case 27:return G9n(t);case 28:return q9n(t);case 29:return t==null?null:FCe(cA[0],u(t,205));case 41:return t==null?"":Db(u(t,298));case 42:return su(t);case 50:return Pt(t);default:throw $(new Gn(r7+n.ve()+ip))}},s.pi=function(n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;switch(n.G==-1&&(n.G=(S=sl(n),S?Ld(S.si(),n):-1)),n.G){case 0:return i=new mX,i;case 1:return t=new Mb,t;case 2:return r=new ij,r;case 4:return c=new _P,c;case 5:return o=new Oxe,o;case 6:return l=new XSe,l;case 7:return f=new OT,f;case 10:return b=new p3,b;case 11:return p=new vX,p;case 12:return y=new c_e,y;case 13:return A=new yX,A;case 14:return O=new kle,O;case 17:return I=new Ph,I;case 18:return h=new hw,h;case 19:return R=new Hk,R;default:throw $(new Gn(mne+n.zb+ip))}},s.qi=function(n,t){switch(n.fk()){case 20:return t==null?null:new Joe(t);case 21:return t==null?null:new E0(t);case 23:case 22:return t==null?null:MEn(t);case 26:case 24:return t==null?null:sO(hl(t,-128,127)<<24>>24);case 25:return EOn(t);case 27:return lxn(t);case 28:return fxn(t);case 29:return CMn(t);case 32:case 31:return t==null?null:F2(t);case 38:case 37:return t==null?null:new aoe(t);case 40:case 39:return t==null?null:ve(hl(t,Xr,oi));case 41:return null;case 42:return t==null,null;case 44:case 43:return t==null?null:R2(Qz(t));case 49:case 48:return t==null?null:c8(hl(t,ZF,32767)<<16>>16);case 50:return t;default:throw $(new Gn(r7+n.ve()+ip))}},v(Fn,"EcoreFactoryImpl",1303),m(548,184,{109:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,2e3:1,52:1,100:1,161:1,184:1,548:1,117:1,118:1,680:1},OIe),s.gb=!1,s.hb=!1;var u7e,Zan=!1;v(Fn,"EcorePackageImpl",548),m(1199,1,{835:1},m3),s.Ik=function(){return fOe(),nhn},v(Fn,"EcorePackageImpl/1",1199),m(1208,1,ii,ow),s.dk=function(n){return X(n,158)},s.ek=function(n){return oe(QD,On,158,n,0,1)},v(Fn,"EcorePackageImpl/10",1208),m(1209,1,ii,tT),s.dk=function(n){return X(n,197)},s.ek=function(n){return oe(Ice,On,197,n,0,1)},v(Fn,"EcorePackageImpl/11",1209),m(1210,1,ii,iT),s.dk=function(n){return X(n,57)},s.ek=function(n){return oe(wb,On,57,n,0,1)},v(Fn,"EcorePackageImpl/12",1210),m(1211,1,ii,d0),s.dk=function(n){return X(n,403)},s.ek=function(n){return oe(yf,ime,62,n,0,1)},v(Fn,"EcorePackageImpl/13",1211),m(1212,1,ii,AL),s.dk=function(n){return X(n,241)},s.ek=function(n){return oe(xa,On,241,n,0,1)},v(Fn,"EcorePackageImpl/14",1212),m(1213,1,ii,B5),s.dk=function(n){return X(n,503)},s.ek=function(n){return oe(vp,On,2078,n,0,1)},v(Fn,"EcorePackageImpl/15",1213),m(1214,1,ii,G6),s.dk=function(n){return X(n,103)},s.ek=function(n){return oe(Bm,jv,19,n,0,1)},v(Fn,"EcorePackageImpl/16",1214),m(1215,1,ii,q6),s.dk=function(n){return X(n,179)},s.ek=function(n){return oe(ns,jv,179,n,0,1)},v(Fn,"EcorePackageImpl/17",1215),m(1216,1,ii,z5),s.dk=function(n){return X(n,470)},s.ek=function(n){return oe($m,On,470,n,0,1)},v(Fn,"EcorePackageImpl/18",1216),m(1217,1,ii,ML),s.dk=function(n){return X(n,549)},s.ek=function(n){return oe(Iu,BZe,549,n,0,1)},v(Fn,"EcorePackageImpl/19",1217),m(1200,1,ii,TL),s.dk=function(n){return X(n,335)},s.ek=function(n){return oe(Rm,jv,38,n,0,1)},v(Fn,"EcorePackageImpl/2",1200),m(1218,1,ii,U6),s.dk=function(n){return X(n,248)},s.ek=function(n){return oe(Rc,ten,87,n,0,1)},v(Fn,"EcorePackageImpl/20",1218),m(1219,1,ii,CL),s.dk=function(n){return X(n,446)},s.ek=function(n){return oe(Fo,On,834,n,0,1)},v(Fn,"EcorePackageImpl/21",1219),m(1220,1,ii,Gk),s.dk=function(n){return o2(n)},s.ek=function(n){return oe(Yi,Se,473,n,8,1)},v(Fn,"EcorePackageImpl/22",1220),m(1221,1,ii,OL),s.dk=function(n){return X(n,195)},s.ek=function(n){return oe(ds,Se,195,n,0,2)},v(Fn,"EcorePackageImpl/23",1221),m(1222,1,ii,bU),s.dk=function(n){return X(n,221)},s.ek=function(n){return oe(py,Se,221,n,0,1)},v(Fn,"EcorePackageImpl/24",1222),m(1223,1,ii,gU),s.dk=function(n){return X(n,180)},s.ek=function(n){return oe(XS,Se,180,n,0,1)},v(Fn,"EcorePackageImpl/25",1223),m(1224,1,ii,Ju),s.dk=function(n){return X(n,205)},s.ek=function(n){return oe(lJ,Se,205,n,0,1)},v(Fn,"EcorePackageImpl/26",1224),m(1225,1,ii,Io),s.dk=function(n){return!1},s.ek=function(n){return oe(S7e,On,2174,n,0,1)},v(Fn,"EcorePackageImpl/27",1225),m(1226,1,ii,Hc),s.dk=function(n){return s2(n)},s.ek=function(n){return oe(gr,Se,346,n,7,1)},v(Fn,"EcorePackageImpl/28",1226),m(1227,1,ii,nu),s.dk=function(n){return X(n,61)},s.ek=function(n){return oe(B8e,Z2,61,n,0,1)},v(Fn,"EcorePackageImpl/29",1227),m(1201,1,ii,io),s.dk=function(n){return X(n,504)},s.ek=function(n){return oe(Zt,{3:1,4:1,5:1,1995:1},587,n,0,1)},v(Fn,"EcorePackageImpl/3",1201),m(1228,1,ii,v1),s.dk=function(n){return X(n,568)},s.ek=function(n){return oe(J8e,On,2001,n,0,1)},v(Fn,"EcorePackageImpl/30",1228),m(1229,1,ii,Gp),s.dk=function(n){return X(n,163)},s.ek=function(n){return oe(a7e,Z2,163,n,0,1)},v(Fn,"EcorePackageImpl/31",1229),m(1230,1,ii,F5),s.dk=function(n){return X(n,75)},s.ek=function(n){return oe(SG,aen,75,n,0,1)},v(Fn,"EcorePackageImpl/32",1230),m(1231,1,ii,rT),s.dk=function(n){return X(n,164)},s.ek=function(n){return oe(h7,Se,164,n,0,1)},v(Fn,"EcorePackageImpl/33",1231),m(1232,1,ii,sw),s.dk=function(n){return X(n,15)},s.ek=function(n){return oe(jr,Se,15,n,0,1)},v(Fn,"EcorePackageImpl/34",1232),m(1233,1,ii,zs),s.dk=function(n){return X(n,298)},s.ek=function(n){return oe(wme,On,298,n,0,1)},v(Fn,"EcorePackageImpl/35",1233),m(1234,1,ii,qp),s.dk=function(n){return X(n,190)},s.ek=function(n){return oe(cp,Se,190,n,0,1)},v(Fn,"EcorePackageImpl/36",1234),m(1235,1,ii,v3),s.dk=function(n){return X(n,92)},s.ek=function(n){return oe(pme,On,92,n,0,1)},v(Fn,"EcorePackageImpl/37",1235),m(1236,1,ii,cT),s.dk=function(n){return X(n,588)},s.ek=function(n){return oe(o7e,On,588,n,0,1)},v(Fn,"EcorePackageImpl/38",1236),m(1237,1,ii,y1),s.dk=function(n){return!1},s.ek=function(n){return oe(x7e,On,2175,n,0,1)},v(Fn,"EcorePackageImpl/39",1237),m(1202,1,ii,J5),s.dk=function(n){return X(n,88)},s.ek=function(n){return oe(vf,On,29,n,0,1)},v(Fn,"EcorePackageImpl/4",1202),m(1238,1,ii,X6),s.dk=function(n){return X(n,191)},s.ek=function(n){return oe(up,Se,191,n,0,1)},v(Fn,"EcorePackageImpl/40",1238),m(1239,1,ii,Rh),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(Fn,"EcorePackageImpl/41",1239),m(1240,1,ii,uT),s.dk=function(n){return X(n,585)},s.ek=function(n){return oe(F8e,On,585,n,0,1)},v(Fn,"EcorePackageImpl/42",1240),m(1241,1,ii,qk),s.dk=function(n){return!1},s.ek=function(n){return oe(A7e,Se,2176,n,0,1)},v(Fn,"EcorePackageImpl/43",1241),m(1242,1,ii,NL),s.dk=function(n){return X(n,45)},s.ek=function(n){return oe(wg,eF,45,n,0,1)},v(Fn,"EcorePackageImpl/44",1242),m(1203,1,ii,Uk),s.dk=function(n){return X(n,143)},s.ek=function(n){return oe(Aa,On,143,n,0,1)},v(Fn,"EcorePackageImpl/5",1203),m(1204,1,ii,Xk),s.dk=function(n){return X(n,159)},s.ek=function(n){return oe(Bce,On,159,n,0,1)},v(Fn,"EcorePackageImpl/6",1204),m(1205,1,ii,Up),s.dk=function(n){return X(n,459)},s.ek=function(n){return oe(EG,On,675,n,0,1)},v(Fn,"EcorePackageImpl/7",1205),m(1206,1,ii,nf),s.dk=function(n){return X(n,568)},s.ek=function(n){return oe(ed,On,684,n,0,1)},v(Fn,"EcorePackageImpl/8",1206),m(1207,1,ii,Xp),s.dk=function(n){return X(n,469)},s.ek=function(n){return oe(rA,On,469,n,0,1)},v(Fn,"EcorePackageImpl/9",1207),m(1019,2042,RZe,nAe),s.Ki=function(n,t){ujn(this,u(t,415))},s.Oi=function(n,t){rqe(this,n,u(t,415))},v(Fn,"MinimalEObjectImpl/1ArrayDelegatingAdapterList",1019),m(1020,151,RN,yIe),s.hj=function(){return this.a.a},v(Fn,"MinimalEObjectImpl/1ArrayDelegatingAdapterList/1",1020),m(1047,1046,{},NCe),v("org.eclipse.emf.ecore.plugin","EcorePlugin",1047);var o7e=Hi(hen,"Resource");m(786,1485,den),s.Fl=function(n){},s.Gl=function(n){},s.Cl=function(){return!this.a&&(this.a=new hX(this)),this.a},s.Dl=function(n){var t,i,r,c,o;if(r=n.length,r>0)if(Yn(0,n.length),n.charCodeAt(0)==47){for(o=new xo(4),c=1,t=1;t0&&(n=(Yr(0,i,n.length),n.substr(0,i))));return wCn(this,n)},s.El=function(){return this.c},s.Ib=function(){var n;return Db(this.Pm)+"@"+(n=Oi(this)>>>0,n.toString(16))+" uri='"+this.d+"'"},s.b=!1,v(Lne,"ResourceImpl",786),m(1486,786,den,HSe),v(Lne,"BinaryResourceImpl",1486),m(1159,697,Cne),s._i=function(n){return X(n,57)?X5n(this,u(n,57)):X(n,588)?new ut(u(n,588).Cl()):ue(n)===ue(this.f)?u(n,18).Jc():(S9(),eI.a)},s.Ob=function(){return Z0e(this)},s.a=!1,v($i,"EcoreUtil/ContentTreeIterator",1159),m(1487,1159,Cne,YDe),s._i=function(n){return ue(n)===ue(this.f)?u(n,16).Jc():new QLe(u(n,57))},v(Lne,"ResourceImpl/5",1487),m(647,2054,nen,hX),s.Gc=function(n){return this.i<=4?m8(this,n):X(n,52)&&u(n,52).Gh()==this.a},s.Ki=function(n,t){n==this.i-1&&(this.a.b||(this.a.b=!0))},s.Mi=function(n,t){n==0?this.a.b||(this.a.b=!0):bY(this,n,t)},s.Oi=function(n,t){},s.Pi=function(n,t,i){},s.Jj=function(){return 2},s.hj=function(){return this.a},s.Kj=function(){return!0},s.Lj=function(n,t){var i;return i=u(n,52),t=i.ci(this.a,t),t},s.Mj=function(n,t){var i;return i=u(n,52),i.ci(null,t)},s.Nj=function(){return!1},s.Qi=function(){return!0},s.$i=function(n){return oe(wb,On,57,n,0,1)},s.Wi=function(){return!1},v(Lne,"ResourceImpl/ContentsEList",647),m(953,2024,$8,GSe),s.dd=function(n){return this.a.Ii(n)},s.gc=function(){return this.a.gc()},v($i,"AbstractSequentialInternalEList/1",953);var s7e,l7e,nc,f7e;m(625,1,{},ZNe);var xG,AG;v($i,"BasicExtendedMetaData",625),m(1150,1,{},QTe),s.Hl=function(){return null},s.Il=function(){return this.a==-2&&HT(this,jMn(this.d,this.b)),this.a},s.Jl=function(){return null},s.Kl=function(){return jn(),jn(),Sc},s.ve=function(){return this.c==s7&&uX(this,AJe(this.d,this.b)),this.c},s.Ll=function(){return 0},s.a=-2,s.c=s7,v($i,"BasicExtendedMetaData/EClassExtendedMetaDataImpl",1150),m(1151,1,{},TLe),s.Hl=function(){return this.a==(z9(),xG)&&MP(this,uIn(this.f,this.b)),this.a},s.Il=function(){return 0},s.Jl=function(){return this.c==(z9(),xG)&&r9(this,oIn(this.f,this.b)),this.c},s.Kl=function(){return!this.d&&sX(this,G_n(this.f,this.b)),this.d},s.ve=function(){return this.e==s7&&qT(this,AJe(this.f,this.b)),this.e},s.Ll=function(){return this.g==-2&&d(this,HAn(this.f,this.b)),this.g},s.e=s7,s.g=-2,v($i,"BasicExtendedMetaData/EDataTypeExtendedMetaDataImpl",1151),m(1149,1,{},WTe),s.b=!1,s.c=!1,v($i,"BasicExtendedMetaData/EPackageExtendedMetaDataImpl",1149),m(1152,1,{},CLe),s.c=-2,s.e=s7,s.f=s7,v($i,"BasicExtendedMetaData/EStructuralFeatureExtendedMetaDataImpl",1152),m(581,623,lu,Z$),s.Jj=function(){return this.c},s.ml=function(){return!1},s.Ui=function(n,t){return t},s.c=0,v($i,"EDataTypeEList",581);var a7e=Hi($i,"FeatureMap");m(76,581,{3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,67:1,61:1,77:1,163:1,219:1,1998:1,72:1,98:1},nr),s._c=function(n,t){MNn(this,n,u(t,75))},s.Ec=function(n){return GOn(this,u(n,75))},s.Fi=function(n){Jvn(this,u(n,75))},s.Lj=function(n,t){return v2n(this,u(n,75),t)},s.Mj=function(n,t){return qle(this,u(n,75),t)},s.Ri=function(n,t){return QIn(this,n,t)},s.Ui=function(n,t){return BPn(this,n,u(t,75))},s.fd=function(n,t){return hDn(this,n,u(t,75))},s.Sj=function(n,t){return y2n(this,u(n,75),t)},s.Tj=function(n,t){return ANe(this,u(n,75),t)},s.Uj=function(n,t,i){return DAn(this,u(n,75),u(t,75),i)},s.Xi=function(n,t){return uW(this,n,u(t,75))},s.Ml=function(n,t){return Xbe(this,n,t)},s.ad=function(n,t){var i,r,c,o,l,f,h,b,p;for(b=new Nw(t.gc()),c=t.Jc();c.Ob();)if(r=u(c.Pb(),75),o=r.Jk(),J1(this.e,o))(!o.Qi()||!HR(this,o,r.kd())&&!m8(b,r))&&Et(b,r);else{for(p=Po(this.e.Ah(),o),i=u(this.g,122),l=!0,f=0;f=0;)if(t=n[this.c],this.k.$l(t.Jk()))return this.j=this.f?t:t.kd(),this.i=-2,!0;return this.i=-1,this.g=-1,!1},v($i,"BasicFeatureMap/FeatureEIterator",412),m(666,412,Qh,EK),s.sl=function(){return!0},v($i,"BasicFeatureMap/ResolvingFeatureEIterator",666),m(951,482,KF,qCe),s.nj=function(){return this},v($i,"EContentsEList/1",951),m(952,482,KF,wCe),s.sl=function(){return!1},v($i,"EContentsEList/2",952),m(950,287,VF,UCe),s.ul=function(n){},s.Ob=function(){return!1},s.Sb=function(){return!1},v($i,"EContentsEList/FeatureIteratorImpl/1",950),m(824,581,lu,Zse),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EDataTypeEList/Unsettable",824),m(1920,581,lu,WCe),s.Qi=function(){return!0},v($i,"EDataTypeUniqueEList",1920),m(1921,824,lu,ZCe),s.Qi=function(){return!0},v($i,"EDataTypeUniqueEList/Unsettable",1921),m(145,81,lu,rs),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectContainmentEList/Resolving",145),m(1153,543,lu,QCe),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectContainmentEList/Unsettable/Resolving",1153),m(753,14,lu,Rle),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EObjectContainmentWithInverseEList/Unsettable",753),m(1187,753,lu,dNe),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectContainmentWithInverseEList/Unsettable/Resolving",1187),m(745,491,lu,Wse),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EObjectEList/Unsettable",745),m(339,491,lu,$3),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectResolvingEList",339),m(1825,745,lu,eOe),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectResolvingEList/Unsettable",1825),m(1488,1,{},H5);var ehn;v($i,"EObjectValidator",1488),m(547,491,lu,mR),s.gl=function(){return this.d},s.hl=function(){return this.b},s.Kj=function(){return!0},s.kl=function(){return!0},s.b=0,v($i,"EObjectWithInverseEList",547),m(1190,547,lu,bNe),s.jl=function(){return!0},v($i,"EObjectWithInverseEList/ManyInverse",1190),m(626,547,lu,GK),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EObjectWithInverseEList/Unsettable",626),m(1189,626,lu,gNe),s.jl=function(){return!0},v($i,"EObjectWithInverseEList/Unsettable/ManyInverse",1189),m(754,547,lu,Ble),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectWithInverseResolvingEList",754),m(33,754,lu,Nn),s.jl=function(){return!0},v($i,"EObjectWithInverseResolvingEList/ManyInverse",33),m(755,626,lu,zle),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectWithInverseResolvingEList/Unsettable",755),m(1188,755,lu,wNe),s.jl=function(){return!0},v($i,"EObjectWithInverseResolvingEList/Unsettable/ManyInverse",1188),m(1154,623,lu),s.Ji=function(){return(this.b&1792)==0},s.Li=function(){this.b|=1},s.il=function(){return(this.b&4)!=0},s.Kj=function(){return(this.b&40)!=0},s.jl=function(){return(this.b&16)!=0},s.kl=function(){return(this.b&8)!=0},s.ll=function(){return(this.b&U0)!=0},s.$k=function(){return(this.b&32)!=0},s.ml=function(){return(this.b&qf)!=0},s.dk=function(n){return this.d?rPe(this.d,n):this.Jk().Fk().dk(n)},s.Oj=function(){return(this.b&2)!=0?(this.b&1)!=0:this.i!=0},s.Qi=function(){return(this.b&128)!=0},s.Ek=function(){var n;kt(this),(this.b&2)!=0&&(Fs(this.e)?(n=(this.b&1)!=0,this.b&=-2,s9(this,new Pf(this.e,2,zi(this.e.Ah(),this.Jk()),n,!1))):this.b&=-2)},s.Wi=function(){return(this.b&1536)==0},s.b=0,v($i,"EcoreEList/Generic",1154),m(1155,1154,lu,l_e),s.Jk=function(){return this.a},v($i,"EcoreEList/Dynamic",1155),m(752,67,Mh,uoe),s.$i=function(n){return aO(this.a.a,n)},v($i,"EcoreEMap/1",752),m(751,81,lu,Lfe),s.Ki=function(n,t){lz(this.b,u(t,136))},s.Mi=function(n,t){fze(this.b)},s.Ni=function(n,t,i){var r;++(r=this.b,u(t,136),r).e},s.Oi=function(n,t){gQ(this.b,u(t,136))},s.Pi=function(n,t,i){gQ(this.b,u(i,136)),ue(i)===ue(t)&&u(i,136).zi(pwn(u(t,136).jd())),lz(this.b,u(t,136))},v($i,"EcoreEMap/DelegateEObjectContainmentEList",751),m(1185,142,tme,kBe),v($i,"EcoreEMap/Unsettable",1185),m(1186,751,lu,pNe),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EcoreEMap/Unsettable/UnsettableDelegateEObjectContainmentEList",1186),m(1158,223,bv,hIe),s.a=!1,s.b=!1,v($i,"EcoreUtil/Copier",1158),m(747,1,Fr,QLe),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return aJe(this)},s.Pb=function(){var n;return aJe(this),n=this.b,this.b=null,n},s.Qb=function(){this.a.Qb()},v($i,"EcoreUtil/ProperContentIterator",747),m(1489,1488,{},zU);var nhn;v($i,"EcoreValidator",1489);var thn;Hi($i,"FeatureMapUtil/Validator"),m(1258,1,{2003:1},lw),s.$l=function(n){return!0},v($i,"FeatureMapUtil/1",1258),m(760,1,{2003:1},Age),s.$l=function(n){var t;return this.c==n?!0:(t=$e(Bn(this.a,n)),t==null?hIn(this,n)?(UPe(this.a,n,(Pn(),a7)),!0):(UPe(this.a,n,(Pn(),eb)),!1):t==(Pn(),a7))},s.e=!1;var Hce;v($i,"FeatureMapUtil/BasicValidator",760),m(761,44,bv,Vse),v($i,"FeatureMapUtil/BasicValidator/Cache",761),m(495,56,{20:1,31:1,56:1,18:1,16:1,61:1,77:1,72:1,98:1},pC),s._c=function(n,t){ZUe(this.c,this.b,n,t)},s.Ec=function(n){return Xbe(this.c,this.b,n)},s.ad=function(n,t){return NLn(this.c,this.b,n,t)},s.Fc=function(n){return Vj(this,n)},s.Ei=function(n,t){p8n(this.c,this.b,n,t)},s.Uk=function(n,t){return Bbe(this.c,this.b,n,t)},s.Yi=function(n){return Uz(this.c,this.b,n,!1)},s.Gi=function(){return xCe(this.c,this.b)},s.Hi=function(){return lwn(this.c,this.b)},s.Ii=function(n){return v9n(this.c,this.b,n)},s.Vk=function(n,t){return YOe(this,n,t)},s.$b=function(){n4(this)},s.Gc=function(n){return HR(this.c,this.b,n)},s.Hc=function(n){return m7n(this.c,this.b,n)},s.Xb=function(n){return Uz(this.c,this.b,n,!0)},s.Dk=function(n){return this},s.bd=function(n){return C6n(this.c,this.b,n)},s.dc=function(){return M$(this)},s.Oj=function(){return!OO(this.c,this.b)},s.Jc=function(){return n8n(this.c,this.b)},s.cd=function(){return t8n(this.c,this.b)},s.dd=function(n){return Ejn(this.c,this.b,n)},s.Ri=function(n,t){return wKe(this.c,this.b,n,t)},s.Si=function(n,t){E9n(this.c,this.b,n,t)},s.ed=function(n){return HGe(this.c,this.b,n)},s.Kc=function(n){return PIn(this.c,this.b,n)},s.fd=function(n,t){return xKe(this.c,this.b,n,t)},s.Wb=function(n){Mz(this.c,this.b),Vj(this,u(n,16))},s.gc=function(){return Sjn(this.c,this.b)},s.Nc=function(){return Oyn(this.c,this.b)},s.Oc=function(n){return O6n(this.c,this.b,n)},s.Ib=function(){var n,t;for(t=new pd,t.a+="[",n=xCe(this.c,this.b);cQ(n);)Bc(t,Qj(oz(n))),cQ(n)&&(t.a+=Co);return t.a+="]",t.a},s.Ek=function(){Mz(this.c,this.b)},v($i,"FeatureMapUtil/FeatureEList",495),m(634,39,RN,rY),s.fj=function(n){return PE(this,n)},s.kj=function(n){var t,i,r,c,o,l,f;switch(this.d){case 1:case 2:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return this.g=n.gj(),n.ej()==1&&(this.d=1),!0;break}case 3:{switch(c=n.ej(),c){case 3:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return this.d=5,t=new Nw(2),Et(t,this.g),Et(t,n.gj()),this.g=t,!0;break}}break}case 5:{switch(c=n.ej(),c){case 3:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return i=u(this.g,18),i.Ec(n.gj()),!0;break}}break}case 4:{switch(c=n.ej(),c){case 3:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return this.d=1,this.g=n.gj(),!0;break}case 4:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return this.d=6,f=new Nw(2),Et(f,this.n),Et(f,n.ij()),this.n=f,l=z(B($t,1),ni,30,15,[this.o,n.jj()]),this.g=l,!0;break}}break}case 6:{switch(c=n.ej(),c){case 4:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return i=u(this.n,18),i.Ec(n.ij()),l=u(this.g,54),r=oe($t,ni,30,l.length+1,15,1),Wu(l,0,r,0,l.length),r[l.length]=n.jj(),this.g=r,!0;break}}break}}return!1},v($i,"FeatureMapUtil/FeatureENotificationImpl",634),m(553,495,{20:1,31:1,56:1,18:1,16:1,61:1,77:1,163:1,219:1,1998:1,72:1,98:1},rR),s.Ml=function(n,t){return Xbe(this.c,n,t)},s.Nl=function(n,t,i){return Bbe(this.c,n,t,i)},s.Ol=function(n,t,i){return bge(this.c,n,t,i)},s.Pl=function(){return this},s.Ql=function(n,t){return iN(this.c,n,t)},s.Rl=function(n){return u(Uz(this.c,this.b,n,!1),75).Jk()},s.Sl=function(n){return u(Uz(this.c,this.b,n,!1),75).kd()},s.Tl=function(){return this.a},s.Ul=function(n){return!OO(this.c,n)},s.Vl=function(n,t){Xz(this.c,n,t)},s.Wl=function(n){return CBe(this.c,n)},s.Xl=function(n){fHe(this.c,n)},v($i,"FeatureMapUtil/FeatureFeatureMap",553),m(1257,1,_ne,nCe),s.Dk=function(n){return Uz(this.b,this.a,-1,n)},s.Oj=function(){return!OO(this.b,this.a)},s.Wb=function(n){Xz(this.b,this.a,n)},s.Ek=function(){Mz(this.b,this.a)},v($i,"FeatureMapUtil/FeatureValue",1257);var Xy,Gce,qce,Ky,ihn,tI=Hi(iJ,"AnyType");m(670,63,H1,TX),v(iJ,"InvalidDatatypeValueException",670);var MG=Hi(iJ,gen),iI=Hi(iJ,wen),h7e=Hi(iJ,pen),rhn,zu,d7e,Dg,chn,uhn,ohn,shn,lhn,fhn,ahn,hhn,dhn,bhn,ghn,Zv,whn,e5,lA,phn,jp,rI,cI,mhn,fA,aA;m(828,501,{109:1,94:1,93:1,57:1,52:1,100:1,841:1},koe),s.Ih=function(n,t,i){switch(n){case 0:return i?(!this.c&&(this.c=new nr(this,0)),this.c):(!this.c&&(this.c=new nr(this,0)),this.c.b);case 1:return i?(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)):(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),219)).Tl();case 2:return i?(!this.b&&(this.b=new nr(this,2)),this.b):(!this.b&&(this.b=new nr(this,2)),this.b.b)}return Pl(this,n-dt(this.fi()),An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),n),t,i)},s.Rh=function(n,t,i){var r;switch(t){case 0:return!this.c&&(this.c=new nr(this,0)),eN(this.c,n,i);case 1:return(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),72)).Vk(n,i);case 2:return!this.b&&(this.b=new nr(this,2)),eN(this.b,n,i)}return r=u(An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),t),69),r.uk().yk(this,khe(this),t-dt(this.fi()),n,i)},s.Th=function(n){switch(n){case 0:return!!this.c&&this.c.i!=0;case 1:return!(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)).dc();case 2:return!!this.b&&this.b.i!=0}return Ll(this,n-dt(this.fi()),An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),$C(this.c,t);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),219)).Wb(t);return;case 2:!this.b&&(this.b=new nr(this,2)),$C(this.b,t);return}Jl(this,n-dt(this.fi()),An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),n),t)},s.fi=function(){return Ei(),d7e},s.hi=function(n){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),kt(this.c);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)).$b();return;case 2:!this.b&&(this.b=new nr(this,2)),kt(this.b);return}Fl(this,n-dt(this.fi()),An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.Ib=function(){var n;return(this.j&4)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (mixed: ",qj(n,this.c),n.a+=", anyAttribute: ",qj(n,this.b),n.a+=")",n.a)},v(kr,"AnyTypeImpl",828),m(671,501,{109:1,94:1,93:1,57:1,52:1,100:1,2081:1,671:1},wU),s.Ih=function(n,t,i){switch(n){case 0:return this.a;case 1:return this.b}return Pl(this,n-dt((Ei(),Zv)),An((this.j&2)==0?Zv:(!this.k&&(this.k=new tl),this.k).Lk(),n),t,i)},s.Th=function(n){switch(n){case 0:return this.a!=null;case 1:return this.b!=null}return Ll(this,n-dt((Ei(),Zv)),An((this.j&2)==0?Zv:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:C(this,Pt(t));return;case 1:Q(this,Pt(t));return}Jl(this,n-dt((Ei(),Zv)),An((this.j&2)==0?Zv:(!this.k&&(this.k=new tl),this.k).Lk(),n),t)},s.fi=function(){return Ei(),Zv},s.hi=function(n){switch(n){case 0:this.a=null;return;case 1:this.b=null;return}Fl(this,n-dt((Ei(),Zv)),An((this.j&2)==0?Zv:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.Ib=function(){var n;return(this.j&4)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (data: ",Bc(n,this.a),n.a+=", target: ",Bc(n,this.b),n.a+=")",n.a)},s.a=null,s.b=null,v(kr,"ProcessingInstructionImpl",671),m(672,828,{109:1,94:1,93:1,57:1,52:1,100:1,841:1,2082:1,672:1},Dxe),s.Ih=function(n,t,i){switch(n){case 0:return i?(!this.c&&(this.c=new nr(this,0)),this.c):(!this.c&&(this.c=new nr(this,0)),this.c.b);case 1:return i?(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)):(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),219)).Tl();case 2:return i?(!this.b&&(this.b=new nr(this,2)),this.b):(!this.b&&(this.b=new nr(this,2)),this.b.b);case 3:return!this.c&&(this.c=new nr(this,0)),Pt(iN(this.c,(Ei(),lA),!0));case 4:return Jle(this.a,(!this.c&&(this.c=new nr(this,0)),Pt(iN(this.c,(Ei(),lA),!0))));case 5:return this.a}return Pl(this,n-dt((Ei(),e5)),An((this.j&2)==0?e5:(!this.k&&(this.k=new tl),this.k).Lk(),n),t,i)},s.Th=function(n){switch(n){case 0:return!!this.c&&this.c.i!=0;case 1:return!(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)).dc();case 2:return!!this.b&&this.b.i!=0;case 3:return!this.c&&(this.c=new nr(this,0)),Pt(iN(this.c,(Ei(),lA),!0))!=null;case 4:return Jle(this.a,(!this.c&&(this.c=new nr(this,0)),Pt(iN(this.c,(Ei(),lA),!0))))!=null;case 5:return!!this.a}return Ll(this,n-dt((Ei(),e5)),An((this.j&2)==0?e5:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),$C(this.c,t);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),219)).Wb(t);return;case 2:!this.b&&(this.b=new nr(this,2)),$C(this.b,t);return;case 3:Cae(this,Pt(t));return;case 4:Cae(this,Fle(this.a,t));return;case 5:D(this,u(t,159));return}Jl(this,n-dt((Ei(),e5)),An((this.j&2)==0?e5:(!this.k&&(this.k=new tl),this.k).Lk(),n),t)},s.fi=function(){return Ei(),e5},s.hi=function(n){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),kt(this.c);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)).$b();return;case 2:!this.b&&(this.b=new nr(this,2)),kt(this.b);return;case 3:!this.c&&(this.c=new nr(this,0)),Xz(this.c,(Ei(),lA),null);return;case 4:Cae(this,Fle(this.a,null));return;case 5:this.a=null;return}Fl(this,n-dt((Ei(),e5)),An((this.j&2)==0?e5:(!this.k&&(this.k=new tl),this.k).Lk(),n))},v(kr,"SimpleAnyTypeImpl",672),m(673,501,{109:1,94:1,93:1,57:1,52:1,100:1,2083:1,673:1},Ixe),s.Ih=function(n,t,i){switch(n){case 0:return i?(!this.a&&(this.a=new nr(this,0)),this.a):(!this.a&&(this.a=new nr(this,0)),this.a.b);case 1:return i?(!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),this.b):(!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),ZC(this.b));case 2:return i?(!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),this.c):(!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),ZC(this.c));case 3:return!this.a&&(this.a=new nr(this,0)),fo(this.a,(Ei(),rI));case 4:return!this.a&&(this.a=new nr(this,0)),fo(this.a,(Ei(),cI));case 5:return!this.a&&(this.a=new nr(this,0)),fo(this.a,(Ei(),fA));case 6:return!this.a&&(this.a=new nr(this,0)),fo(this.a,(Ei(),aA))}return Pl(this,n-dt((Ei(),jp)),An((this.j&2)==0?jp:(!this.k&&(this.k=new tl),this.k).Lk(),n),t,i)},s.Rh=function(n,t,i){var r;switch(t){case 0:return!this.a&&(this.a=new nr(this,0)),eN(this.a,n,i);case 1:return!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),U$(this.b,n,i);case 2:return!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),U$(this.c,n,i);case 5:return!this.a&&(this.a=new nr(this,0)),YOe(fo(this.a,(Ei(),fA)),n,i)}return r=u(An((this.j&2)==0?(Ei(),jp):(!this.k&&(this.k=new tl),this.k).Lk(),t),69),r.uk().yk(this,khe(this),t-dt((Ei(),jp)),n,i)},s.Th=function(n){switch(n){case 0:return!!this.a&&this.a.i!=0;case 1:return!!this.b&&this.b.f!=0;case 2:return!!this.c&&this.c.f!=0;case 3:return!this.a&&(this.a=new nr(this,0)),!M$(fo(this.a,(Ei(),rI)));case 4:return!this.a&&(this.a=new nr(this,0)),!M$(fo(this.a,(Ei(),cI)));case 5:return!this.a&&(this.a=new nr(this,0)),!M$(fo(this.a,(Ei(),fA)));case 6:return!this.a&&(this.a=new nr(this,0)),!M$(fo(this.a,(Ei(),aA)))}return Ll(this,n-dt((Ei(),jp)),An((this.j&2)==0?jp:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:!this.a&&(this.a=new nr(this,0)),$C(this.a,t);return;case 1:!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),CB(this.b,t);return;case 2:!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),CB(this.c,t);return;case 3:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),rI))),!this.a&&(this.a=new nr(this,0)),Vj(fo(this.a,rI),u(t,18));return;case 4:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),cI))),!this.a&&(this.a=new nr(this,0)),Vj(fo(this.a,cI),u(t,18));return;case 5:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),fA))),!this.a&&(this.a=new nr(this,0)),Vj(fo(this.a,fA),u(t,18));return;case 6:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),aA))),!this.a&&(this.a=new nr(this,0)),Vj(fo(this.a,aA),u(t,18));return}Jl(this,n-dt((Ei(),jp)),An((this.j&2)==0?jp:(!this.k&&(this.k=new tl),this.k).Lk(),n),t)},s.fi=function(){return Ei(),jp},s.hi=function(n){switch(n){case 0:!this.a&&(this.a=new nr(this,0)),kt(this.a);return;case 1:!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),this.b.c.$b();return;case 2:!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),this.c.c.$b();return;case 3:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),rI)));return;case 4:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),cI)));return;case 5:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),fA)));return;case 6:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),aA)));return}Fl(this,n-dt((Ei(),jp)),An((this.j&2)==0?jp:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.Ib=function(){var n;return(this.j&4)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (mixed: ",qj(n,this.a),n.a+=")",n.a)},v(kr,"XMLTypeDocumentRootImpl",673),m(1990,710,{109:1,94:1,93:1,469:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1,2084:1},oT),s.oi=function(n,t){switch(n.fk()){case 7:case 8:case 9:case 10:case 16:case 22:case 23:case 24:case 25:case 26:case 32:case 33:case 34:case 36:case 37:case 44:case 45:case 50:case 51:case 53:case 55:case 56:case 57:case 58:case 60:case 61:case 4:return t==null?null:su(t);case 19:case 28:case 29:case 35:case 38:case 39:case 41:case 46:case 52:case 54:case 5:return Pt(t);case 6:return Rpn(u(t,195));case 12:case 47:case 49:case 11:return lVe(this,n,t);case 13:return t==null?null:$Ln(u(t,247));case 15:case 14:return t==null?null:Dvn(ne(re(t)));case 17:return ZHe((Ei(),t));case 18:return ZHe(t);case 21:case 20:return t==null?null:Ivn(u(t,164).a);case 27:return $pn(u(t,195));case 30:return aHe((Ei(),u(t,16)));case 31:return aHe(u(t,16));case 40:return Ppn((Ei(),t));case 42:return eGe((Ei(),t));case 43:return eGe(t);case 59:case 48:return Lpn((Ei(),t));default:throw $(new Gn(r7+n.ve()+ip))}},s.pi=function(n){var t,i,r,c,o;switch(n.G==-1&&(n.G=(i=sl(n),i?Ld(i.si(),n):-1)),n.G){case 0:return t=new koe,t;case 1:return r=new wU,r;case 2:return c=new Dxe,c;case 3:return o=new Ixe,o;default:throw $(new Gn(mne+n.zb+ip))}},s.qi=function(n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q;switch(n.fk()){case 5:case 52:case 4:return t;case 6:return nSn(t);case 8:case 7:return t==null?null:BAn(t);case 9:return t==null?null:sO(hl((r=bo(t,!0),r.length>0&&(Yn(0,r.length),r.charCodeAt(0)==43)?(Yn(1,r.length+1),r.substr(1)):r),-128,127)<<24>>24);case 10:return t==null?null:sO(hl((c=bo(t,!0),c.length>0&&(Yn(0,c.length),c.charCodeAt(0)==43)?(Yn(1,c.length+1),c.substr(1)):c),-128,127)<<24>>24);case 11:return Pt(Kw(this,(Ei(),ohn),t));case 12:return Pt(Kw(this,(Ei(),shn),t));case 13:return t==null?null:new Joe(bo(t,!0));case 15:case 14:return XOn(t);case 16:return Pt(Kw(this,(Ei(),lhn),t));case 17:return dJe((Ei(),t));case 18:return dJe(t);case 28:case 29:case 35:case 38:case 39:case 41:case 54:case 19:return bo(t,!0);case 21:case 20:return iNn(t);case 22:return Pt(Kw(this,(Ei(),fhn),t));case 23:return Pt(Kw(this,(Ei(),ahn),t));case 24:return Pt(Kw(this,(Ei(),hhn),t));case 25:return Pt(Kw(this,(Ei(),dhn),t));case 26:return Pt(Kw(this,(Ei(),bhn),t));case 27:return UEn(t);case 30:return bJe((Ei(),t));case 31:return bJe(t);case 32:return t==null?null:ve(hl((p=bo(t,!0),p.length>0&&(Yn(0,p.length),p.charCodeAt(0)==43)?(Yn(1,p.length+1),p.substr(1)):p),Xr,oi));case 33:return t==null?null:new E0((y=bo(t,!0),y.length>0&&(Yn(0,y.length),y.charCodeAt(0)==43)?(Yn(1,y.length+1),y.substr(1)):y));case 34:return t==null?null:ve(hl((S=bo(t,!0),S.length>0&&(Yn(0,S.length),S.charCodeAt(0)==43)?(Yn(1,S.length+1),S.substr(1)):S),Xr,oi));case 36:return t==null?null:R2(Qz((A=bo(t,!0),A.length>0&&(Yn(0,A.length),A.charCodeAt(0)==43)?(Yn(1,A.length+1),A.substr(1)):A)));case 37:return t==null?null:R2(Qz((O=bo(t,!0),O.length>0&&(Yn(0,O.length),O.charCodeAt(0)==43)?(Yn(1,O.length+1),O.substr(1)):O)));case 40:return JSn((Ei(),t));case 42:return gJe((Ei(),t));case 43:return gJe(t);case 44:return t==null?null:new E0((I=bo(t,!0),I.length>0&&(Yn(0,I.length),I.charCodeAt(0)==43)?(Yn(1,I.length+1),I.substr(1)):I));case 45:return t==null?null:new E0((R=bo(t,!0),R.length>0&&(Yn(0,R.length),R.charCodeAt(0)==43)?(Yn(1,R.length+1),R.substr(1)):R));case 46:return bo(t,!1);case 47:return Pt(Kw(this,(Ei(),ghn),t));case 59:case 48:return FSn((Ei(),t));case 49:return Pt(Kw(this,(Ei(),whn),t));case 50:return t==null?null:c8(hl((q=bo(t,!0),q.length>0&&(Yn(0,q.length),q.charCodeAt(0)==43)?(Yn(1,q.length+1),q.substr(1)):q),ZF,32767)<<16>>16);case 51:return t==null?null:c8(hl((o=bo(t,!0),o.length>0&&(Yn(0,o.length),o.charCodeAt(0)==43)?(Yn(1,o.length+1),o.substr(1)):o),ZF,32767)<<16>>16);case 53:return Pt(Kw(this,(Ei(),phn),t));case 55:return t==null?null:c8(hl((l=bo(t,!0),l.length>0&&(Yn(0,l.length),l.charCodeAt(0)==43)?(Yn(1,l.length+1),l.substr(1)):l),ZF,32767)<<16>>16);case 56:return t==null?null:c8(hl((f=bo(t,!0),f.length>0&&(Yn(0,f.length),f.charCodeAt(0)==43)?(Yn(1,f.length+1),f.substr(1)):f),ZF,32767)<<16>>16);case 57:return t==null?null:R2(Qz((h=bo(t,!0),h.length>0&&(Yn(0,h.length),h.charCodeAt(0)==43)?(Yn(1,h.length+1),h.substr(1)):h)));case 58:return t==null?null:R2(Qz((b=bo(t,!0),b.length>0&&(Yn(0,b.length),b.charCodeAt(0)==43)?(Yn(1,b.length+1),b.substr(1)):b)));case 60:return t==null?null:ve(hl((i=bo(t,!0),i.length>0&&(Yn(0,i.length),i.charCodeAt(0)==43)?(Yn(1,i.length+1),i.substr(1)):i),Xr,oi));case 61:return t==null?null:ve(hl(bo(t,!0),Xr,oi));default:throw $(new Gn(r7+n.ve()+ip))}};var vhn,b7e,yhn,g7e;v(kr,"XMLTypeFactoryImpl",1990),m(582,184,{109:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,52:1,100:1,161:1,184:1,117:1,118:1,680:1,2006:1,582:1},CIe),s.N=!1,s.O=!1;var khn=!1;v(kr,"XMLTypePackageImpl",582),m(1923,1,{835:1},sT),s.Ik=function(){return rge(),Ohn},v(kr,"XMLTypePackageImpl/1",1923),m(1932,1,ii,DL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/10",1932),m(1933,1,ii,K6),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/11",1933),m(1934,1,ii,lT),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/12",1934),m(1935,1,ii,k1),s.dk=function(n){return s2(n)},s.ek=function(n){return oe(gr,Se,346,n,7,1)},v(kr,"XMLTypePackageImpl/13",1935),m(1936,1,ii,IL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/14",1936),m(1937,1,ii,Bh),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/15",1937),m(1938,1,ii,_L),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/16",1938),m(1939,1,ii,LL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/17",1939),m(1940,1,ii,Kp),s.dk=function(n){return X(n,164)},s.ek=function(n){return oe(h7,Se,164,n,0,1)},v(kr,"XMLTypePackageImpl/18",1940),m(1941,1,ii,Kk),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/19",1941),m(1924,1,ii,fT),s.dk=function(n){return X(n,841)},s.ek=function(n){return oe(tI,On,841,n,0,1)},v(kr,"XMLTypePackageImpl/2",1924),m(1942,1,ii,G5),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/20",1942),m(1943,1,ii,PL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/21",1943),m(1944,1,ii,$L),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/22",1944),m(1945,1,ii,RL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/23",1945),m(1946,1,ii,BL),s.dk=function(n){return X(n,195)},s.ek=function(n){return oe(ds,Se,195,n,0,2)},v(kr,"XMLTypePackageImpl/24",1946),m(1947,1,ii,Vk),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/25",1947),m(1948,1,ii,aT),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/26",1948),m(1949,1,ii,pU),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/27",1949),m(1950,1,ii,mU),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/28",1950),m(1951,1,ii,vU),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/29",1951),m(1925,1,ii,zL),s.dk=function(n){return X(n,671)},s.ek=function(n){return oe(MG,On,2081,n,0,1)},v(kr,"XMLTypePackageImpl/3",1925),m(1952,1,ii,FL),s.dk=function(n){return X(n,15)},s.ek=function(n){return oe(jr,Se,15,n,0,1)},v(kr,"XMLTypePackageImpl/30",1952),m(1953,1,ii,q5),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/31",1953),m(1954,1,ii,Yk),s.dk=function(n){return X(n,190)},s.ek=function(n){return oe(cp,Se,190,n,0,1)},v(kr,"XMLTypePackageImpl/32",1954),m(1955,1,ii,JL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/33",1955),m(1956,1,ii,HL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/34",1956),m(1957,1,ii,GL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/35",1957),m(1958,1,ii,qL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/36",1958),m(1959,1,ii,UL),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/37",1959),m(1960,1,ii,XL),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/38",1960),m(1961,1,ii,Qk),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/39",1961),m(1926,1,ii,KL),s.dk=function(n){return X(n,672)},s.ek=function(n){return oe(iI,On,2082,n,0,1)},v(kr,"XMLTypePackageImpl/4",1926),m(1962,1,ii,VL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/40",1962),m(1963,1,ii,ro),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/41",1963),m(1964,1,ii,hT),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/42",1964),m(1965,1,ii,yU),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/43",1965),m(1966,1,ii,YL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/44",1966),m(1967,1,ii,kU),s.dk=function(n){return X(n,191)},s.ek=function(n){return oe(up,Se,191,n,0,1)},v(kr,"XMLTypePackageImpl/45",1967),m(1968,1,ii,jU),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/46",1968),m(1969,1,ii,EU),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/47",1969),m(1970,1,ii,Wk),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/48",1970),m(1971,1,ii,U5),s.dk=function(n){return X(n,191)},s.ek=function(n){return oe(up,Se,191,n,0,1)},v(kr,"XMLTypePackageImpl/49",1971),m(1927,1,ii,dT),s.dk=function(n){return X(n,673)},s.ek=function(n){return oe(h7e,On,2083,n,0,1)},v(kr,"XMLTypePackageImpl/5",1927),m(1972,1,ii,Zk),s.dk=function(n){return X(n,190)},s.ek=function(n){return oe(cp,Se,190,n,0,1)},v(kr,"XMLTypePackageImpl/50",1972),m(1973,1,ii,bT),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/51",1973),m(1974,1,ii,Vp),s.dk=function(n){return X(n,15)},s.ek=function(n){return oe(jr,Se,15,n,0,1)},v(kr,"XMLTypePackageImpl/52",1974),m(1928,1,ii,Tb),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/6",1928),m(1929,1,ii,V6),s.dk=function(n){return X(n,195)},s.ek=function(n){return oe(ds,Se,195,n,0,2)},v(kr,"XMLTypePackageImpl/7",1929),m(1930,1,ii,SU),s.dk=function(n){return o2(n)},s.ek=function(n){return oe(Yi,Se,473,n,8,1)},v(kr,"XMLTypePackageImpl/8",1930),m(1931,1,ii,QL),s.dk=function(n){return X(n,221)},s.ek=function(n){return oe(py,Se,221,n,0,1)},v(kr,"XMLTypePackageImpl/9",1931);var rh,t0,hA,TG,J;m(53,63,H1,zt),v(Jd,"RegEx/ParseException",53),m(820,1,{},gT),s._l=function(n){return ni*16)throw $(new zt(Ht((Lt(),TZe))));i=i*16+c}while(!0);if(this.a!=125)throw $(new zt(Ht((Lt(),CZe))));if(i>l7)throw $(new zt(Ht((Lt(),OZe))));n=i}else{if(c=0,this.c!=0||(c=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(i=c,li(this),this.c!=0||(c=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));i=i*16+c,n=i}break;case 117:if(r=0,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));t=t*16+r,n=t;break;case 118:if(li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,t>l7)throw $(new zt(Ht((Lt(),"parser.descappe.4"))));n=t;break;case 65:case 90:case 122:throw $(new zt(Ht((Lt(),NZe))))}return n},s.bm=function(n){var t,i;switch(n){case 100:i=(this.e&32)==32?q0("Nd",!0):(fi(),CG);break;case 68:i=(this.e&32)==32?q0("Nd",!1):(fi(),k7e);break;case 119:i=(this.e&32)==32?q0("IsWord",!0):(fi(),V7);break;case 87:i=(this.e&32)==32?q0("IsWord",!1):(fi(),E7e);break;case 115:i=(this.e&32)==32?q0("IsSpace",!0):(fi(),Vy);break;case 83:i=(this.e&32)==32?q0("IsSpace",!1):(fi(),j7e);break;default:throw $(new hu((t=n,Nen+t.toString(16))))}return i},s.cm=function(n){var t,i,r,c,o,l,f,h,b,p,y,S;for(this.b=1,li(this),t=null,this.c==0&&this.a==94?(li(this),n?p=(fi(),fi(),new ul(5)):(t=(fi(),fi(),new ul(4)),ho(t,0,l7),p=new ul(4))):p=(fi(),fi(),new ul(4)),c=!0;(S=this.c)!=1&&!(S==0&&this.a==93&&!c);){if(c=!1,i=this.a,r=!1,S==10)switch(i){case 100:case 68:case 119:case 87:case 115:case 83:V2(p,this.bm(i)),r=!0;break;case 105:case 73:case 99:case 67:i=this.sm(p,i),i<0&&(r=!0);break;case 112:case 80:if(y=Q0e(this,i),!y)throw $(new zt(Ht((Lt(),Nne))));V2(p,y),r=!0;break;default:i=this.am()}else if(S==20){if(l=k9(this.i,58,this.d),l<0)throw $(new zt(Ht((Lt(),Y2e))));if(f=!0,ic(this.i,this.d)==94&&(++this.d,f=!1),o=of(this.i,this.d,l),h=P$e(o,f,(this.e&512)==512),!h)throw $(new zt(Ht((Lt(),EZe))));if(V2(p,h),r=!0,l+1>=this.j||ic(this.i,l+1)!=93)throw $(new zt(Ht((Lt(),Y2e))));this.d=l+2}if(li(this),!r)if(this.c!=0||this.a!=45)ho(p,i,i);else{if(li(this),(S=this.c)==1)throw $(new zt(Ht((Lt(),UF))));S==0&&this.a==93?(ho(p,i,i),ho(p,45,45)):(b=this.a,S==10&&(b=this.am()),li(this),ho(p,i,b))}(this.e&qf)==qf&&this.c==0&&this.a==44&&li(this)}if(this.c==1)throw $(new zt(Ht((Lt(),UF))));return t&&(dS(t,p),p=t),ov(p),aS(p),this.b=0,li(this),p},s.dm=function(){var n,t,i,r;for(i=this.cm(!1);(r=this.c)!=7;)if(n=this.a,r==0&&(n==45||n==38)||r==4){if(li(this),this.c!=9)throw $(new zt(Ht((Lt(),xZe))));if(t=this.cm(!1),r==4)V2(i,t);else if(n==45)dS(i,t);else if(n==38)cVe(i,t);else throw $(new hu("ASSERT"))}else throw $(new zt(Ht((Lt(),AZe))));return li(this),i},s.em=function(){var n,t;return n=this.a-48,t=(fi(),fi(),new FV(12,null,n)),!this.g&&(this.g=new PP),LP(this.g,new ooe(n)),li(this),t},s.fm=function(){return li(this),fi(),Shn},s.gm=function(){return li(this),fi(),Ehn},s.hm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.im=function(){throw $(new zt(Ht((Lt(),Ul))))},s.jm=function(){return li(this),wkn()},s.km=function(){return li(this),fi(),Ahn},s.lm=function(){return li(this),fi(),Thn},s.mm=function(){var n;if(this.d>=this.j||((n=ic(this.i,this.d++))&65504)!=64)throw $(new zt(Ht((Lt(),yZe))));return li(this),fi(),fi(),new Hh(0,n-64)},s.nm=function(){return li(this),B_n()},s.om=function(){return li(this),fi(),Chn},s.pm=function(){var n;return n=(fi(),fi(),new Hh(0,105)),li(this),n},s.qm=function(){return li(this),fi(),Mhn},s.rm=function(){return li(this),fi(),xhn},s.sm=function(n,t){return this.am()},s.tm=function(){return li(this),fi(),v7e},s.um=function(){var n,t,i,r,c;if(this.d+1>=this.j)throw $(new zt(Ht((Lt(),pZe))));if(r=-1,t=null,n=ic(this.i,this.d),49<=n&&n<=57){if(r=n-48,!this.g&&(this.g=new PP),LP(this.g,new ooe(r)),++this.d,ic(this.i,this.d)!=41)throw $(new zt(Ht((Lt(),bg))));++this.d}else switch(n==63&&--this.d,li(this),t=Oge(this),t.e){case 20:case 21:case 22:case 23:break;case 8:if(this.c!=7)throw $(new zt(Ht((Lt(),bg))));break;default:throw $(new zt(Ht((Lt(),mZe))))}if(li(this),c=Bw(this),i=null,c.e==2){if(c.Nm()!=2)throw $(new zt(Ht((Lt(),vZe))));i=c.Jm(1),c=c.Jm(0)}if(this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),fi(),fi(),new MRe(r,t,c,i)},s.vm=function(){return li(this),fi(),y7e},s.wm=function(){var n;if(li(this),n=vR(24,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.xm=function(){var n;if(li(this),n=vR(20,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.ym=function(){var n;if(li(this),n=vR(22,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.zm=function(){var n,t,i,r,c;for(n=0,i=0,t=-1;this.d=this.j)throw $(new zt(Ht((Lt(),K2e))));if(t==45){for(++this.d;this.d=this.j)throw $(new zt(Ht((Lt(),K2e))))}if(t==58){if(++this.d,li(this),r=bIe(Bw(this),n,i),this.c!=7)throw $(new zt(Ht((Lt(),bg))));li(this)}else if(t==41)++this.d,li(this),r=bIe(Bw(this),n,i);else throw $(new zt(Ht((Lt(),wZe))));return r},s.Am=function(){var n;if(li(this),n=vR(21,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.Bm=function(){var n;if(li(this),n=vR(23,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.Cm=function(){var n,t;if(li(this),n=this.f++,t=wV(Bw(this),n),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),t},s.Dm=function(){var n;if(li(this),n=wV(Bw(this),0),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.Em=function(n){return li(this),this.c==5?(li(this),aR(n,(fi(),fi(),new A2(9,n)))):aR(n,(fi(),fi(),new A2(3,n)))},s.Fm=function(n){var t;return li(this),t=(fi(),fi(),new Kj(2)),this.c==5?(li(this),ug(t,bA),ug(t,n)):(ug(t,n),ug(t,bA)),t},s.Gm=function(n){return li(this),this.c==5?(li(this),fi(),fi(),new A2(9,n)):(fi(),fi(),new A2(3,n))},s.a=0,s.b=0,s.c=0,s.d=0,s.e=0,s.f=1,s.g=null,s.j=0,v(Jd,"RegEx/RegexParser",820),m(1910,820,{},_xe),s._l=function(n){return!1},s.am=function(){return Lbe(this)},s.bm=function(n){return T8(n)},s.cm=function(n){return WVe(this)},s.dm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.em=function(){throw $(new zt(Ht((Lt(),Ul))))},s.fm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.gm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.hm=function(){return li(this),T8(67)},s.im=function(){return li(this),T8(73)},s.jm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.km=function(){throw $(new zt(Ht((Lt(),Ul))))},s.lm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.mm=function(){return li(this),T8(99)},s.nm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.om=function(){throw $(new zt(Ht((Lt(),Ul))))},s.pm=function(){return li(this),T8(105)},s.qm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.rm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.sm=function(n,t){return V2(n,T8(t)),-1},s.tm=function(){return li(this),fi(),fi(),new Hh(0,94)},s.um=function(){throw $(new zt(Ht((Lt(),Ul))))},s.vm=function(){return li(this),fi(),fi(),new Hh(0,36)},s.wm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.xm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.ym=function(){throw $(new zt(Ht((Lt(),Ul))))},s.zm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.Am=function(){throw $(new zt(Ht((Lt(),Ul))))},s.Bm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.Cm=function(){var n;if(li(this),n=wV(Bw(this),0),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.Dm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.Em=function(n){return li(this),aR(n,(fi(),fi(),new A2(3,n)))},s.Fm=function(n){var t;return li(this),t=(fi(),fi(),new Kj(2)),ug(t,n),ug(t,bA),t},s.Gm=function(n){return li(this),fi(),fi(),new A2(3,n)};var n5=null,X7=null;v(Jd,"RegEx/ParserForXMLSchema",1910),m(121,1,f7,aw),s.Hm=function(n){throw $(new hu("Not supported."))},s.Im=function(){return-1},s.Jm=function(n){return null},s.Km=function(){return null},s.Lm=function(n){},s.Mm=function(n){},s.Nm=function(){return 0},s.Ib=function(){return this.Om(0)},s.Om=function(n){return this.e==11?".":""},s.e=0;var w7e,K7,dA,jhn,p7e,Jm=null,CG,Uce=null,m7e,bA,Xce=null,v7e,y7e,k7e,j7e,E7e,Ehn,Vy,Shn,xhn,Ahn,Mhn,V7,Thn,Chn,WBn=v(Jd,"RegEx/Token",121);m(137,121,{3:1,137:1,121:1},ul),s.Om=function(n){var t,i,r;if(this.e==4)if(this==m7e)i=".";else if(this==CG)i="\\d";else if(this==V7)i="\\w";else if(this==Vy)i="\\s";else{for(r=new pd,r.a+="[",t=0;t0&&(r.a+=","),this.b[t]===this.b[t+1]?Bc(r,tN(this.b[t])):(Bc(r,tN(this.b[t])),r.a+="-",Bc(r,tN(this.b[t+1])));r.a+="]",i=r.a}else if(this==k7e)i="\\D";else if(this==E7e)i="\\W";else if(this==j7e)i="\\S";else{for(r=new pd,r.a+="[^",t=0;t0&&(r.a+=","),this.b[t]===this.b[t+1]?Bc(r,tN(this.b[t])):(Bc(r,tN(this.b[t])),r.a+="-",Bc(r,tN(this.b[t+1])));r.a+="]",i=r.a}return i},s.a=!1,s.c=!1,v(Jd,"RegEx/RangeToken",137),m(580,1,{580:1},ooe),s.a=0,v(Jd,"RegEx/RegexParser/ReferencePosition",580),m(579,1,{3:1,579:1},wMe),s.Fb=function(n){var t;return n==null||!X(n,579)?!1:(t=u(n,579),bn(this.b,t.b)&&this.a==t.a)},s.Hb=function(){return Od(this.b+"/"+Tbe(this.a))},s.Ib=function(){return this.c.Om(this.a)},s.a=0,v(Jd,"RegEx/RegularExpression",579),m(228,121,f7,Hh),s.Im=function(){return this.a},s.Om=function(n){var t,i,r;switch(this.e){case 0:switch(this.a){case 124:case 42:case 43:case 63:case 40:case 41:case 46:case 91:case 123:case 92:r="\\"+JK(this.a&yr);break;case 12:r="\\f";break;case 10:r="\\n";break;case 13:r="\\r";break;case 9:r="\\t";break;case 27:r="\\e";break;default:this.a>=Ec?(i=(t=this.a>>>0,"0"+t.toString(16)),r="\\v"+of(i,i.length-6,i.length)):r=""+JK(this.a&yr)}break;case 8:this==v7e||this==y7e?r=""+JK(this.a&yr):r="\\"+JK(this.a&yr);break;default:r=null}return r},s.a=0,v(Jd,"RegEx/Token/CharToken",228),m(322,121,f7,A2),s.Jm=function(n){return this.a},s.Lm=function(n){this.b=n},s.Mm=function(n){this.c=n},s.Nm=function(){return 1},s.Om=function(n){var t;if(this.e==3)if(this.c<0&&this.b<0)t=this.a.Om(n)+"*";else if(this.c==this.b)t=this.a.Om(n)+"{"+this.c+"}";else if(this.c>=0&&this.b>=0)t=this.a.Om(n)+"{"+this.c+","+this.b+"}";else if(this.c>=0&&this.b<0)t=this.a.Om(n)+"{"+this.c+",}";else throw $(new hu("Token#toString(): CLOSURE "+this.c+Co+this.b));else if(this.c<0&&this.b<0)t=this.a.Om(n)+"*?";else if(this.c==this.b)t=this.a.Om(n)+"{"+this.c+"}?";else if(this.c>=0&&this.b>=0)t=this.a.Om(n)+"{"+this.c+","+this.b+"}?";else if(this.c>=0&&this.b<0)t=this.a.Om(n)+"{"+this.c+",}?";else throw $(new hu("Token#toString(): NONGREEDYCLOSURE "+this.c+Co+this.b));return t},s.b=0,s.c=0,v(Jd,"RegEx/Token/ClosureToken",322),m(821,121,f7,zfe),s.Jm=function(n){return n==0?this.a:this.b},s.Nm=function(){return 2},s.Om=function(n){var t;return this.b.e==3&&this.b.Jm(0)==this.a?t=this.a.Om(n)+"+":this.b.e==9&&this.b.Jm(0)==this.a?t=this.a.Om(n)+"+?":t=this.a.Om(n)+(""+this.b.Om(n)),t},v(Jd,"RegEx/Token/ConcatToken",821),m(1908,121,f7,MRe),s.Jm=function(n){if(n==0)return this.d;if(n==1)return this.b;throw $(new hu("Internal Error: "+n))},s.Nm=function(){return this.b?2:1},s.Om=function(n){var t;return this.c>0?t="(?("+this.c+")":this.a.e==8?t="(?("+this.a+")":t="(?"+this.a,this.b?t+=this.d+"|"+this.b+")":t+=this.d+")",t},s.c=0,v(Jd,"RegEx/Token/ConditionToken",1908),m(1909,121,f7,aLe),s.Jm=function(n){return this.b},s.Nm=function(){return 1},s.Om=function(n){return"(?"+(this.a==0?"":Tbe(this.a))+(this.c==0?"":Tbe(this.c))+":"+this.b.Om(n)+")"},s.a=0,s.c=0,v(Jd,"RegEx/Token/ModifierToken",1909),m(822,121,f7,Yfe),s.Jm=function(n){return this.a},s.Nm=function(){return 1},s.Om=function(n){var t;switch(t=null,this.e){case 6:this.b==0?t="(?:"+this.a.Om(n)+")":t="("+this.a.Om(n)+")";break;case 20:t="(?="+this.a.Om(n)+")";break;case 21:t="(?!"+this.a.Om(n)+")";break;case 22:t="(?<="+this.a.Om(n)+")";break;case 23:t="(?"+this.a.Om(n)+")"}return t},s.b=0,v(Jd,"RegEx/Token/ParenToken",822),m(517,121,{3:1,121:1,517:1},FV),s.Km=function(){return this.b},s.Om=function(n){return this.e==12?"\\"+this.a:IOn(this.b)},s.a=0,v(Jd,"RegEx/Token/StringToken",517),m(466,121,f7,Kj),s.Hm=function(n){ug(this,n)},s.Jm=function(n){return u(Ew(this.a,n),121)},s.Nm=function(){return this.a?this.a.a.c.length:0},s.Om=function(n){var t,i,r,c,o;if(this.e==1){if(this.a.a.c.length==2)t=u(Ew(this.a,0),121),i=u(Ew(this.a,1),121),i.e==3&&i.Jm(0)==t?c=t.Om(n)+"+":i.e==9&&i.Jm(0)==t?c=t.Om(n)+"+?":c=t.Om(n)+(""+i.Om(n));else{for(o=new pd,r=0;r=this.c.b:this.a<=this.c.b},s.Sb=function(){return this.b>0},s.Tb=function(){return this.b},s.Vb=function(){return this.b-1},s.Qb=function(){throw $(new gd(Ren))},s.a=0,s.b=0,v(gme,"ExclusiveRange/RangeIterator",259);var Wl=I9(XF,"C"),$t=I9(FS,"I"),ts=I9(ry,"Z"),Ep=I9(JS,"J"),ds=I9(RS,"B"),Jr=I9(BS,"D"),Hm=I9(zS,"F"),t5=I9(HS,"S"),ZBn=Hi("org.eclipse.elk.core.labels","ILabelManager"),S7e=Hi(yc,"DiagnosticChain"),x7e=Hi(hen,"ResourceSet"),A7e=v(yc,"InvocationTargetException",null),Nhn=(JP(),G6n),Dhn=Dhn=EAn;F8n(dbn),i7n("permProps",[[["locale","default"],[Ben,"gecko1_8"]],[["locale","default"],[Ben,"safari"]]]),Dhn(null,"elk",null)}).call(this)}).call(this,typeof _hn<"u"?_hn:typeof self<"u"?self:typeof window<"u"?window:{})},{}],3:[function(x,M,N){function P(ge){"@babel/helpers - typeof";return P=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(Pe){return typeof Pe}:function(Pe){return Pe&&typeof Symbol=="function"&&Pe.constructor===Symbol&&Pe!==Symbol.prototype?"symbol":typeof Pe},P(ge)}function k(ge,Pe,ae){return Object.defineProperty(ge,"prototype",{writable:!1}),ge}function H(ge,Pe){if(!(ge instanceof Pe))throw new TypeError("Cannot call a class as a function")}function U(ge,Pe,ae){return Pe=W(Pe),G(ge,Z()?Reflect.construct(Pe,ae||[],W(ge).constructor):Pe.apply(ge,ae))}function G(ge,Pe){if(Pe&&(P(Pe)=="object"||typeof Pe=="function"))return Pe;if(Pe!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return ie(ge)}function ie(ge){if(ge===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return ge}function Z(){try{var ge=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch{}return(Z=function(){return!!ge})()}function W(ge){return W=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(Pe){return Pe.__proto__||Object.getPrototypeOf(Pe)},W(ge)}function se(ge,Pe){if(typeof Pe!="function"&&Pe!==null)throw new TypeError("Super expression must either be null or a function");ge.prototype=Object.create(Pe&&Pe.prototype,{constructor:{value:ge,writable:!0,configurable:!0}}),Object.defineProperty(ge,"prototype",{writable:!1}),Pe&&le(ge,Pe)}function le(ge,Pe){return le=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(ae,Me){return ae.__proto__=Me,ae},le(ge,Pe)}var ee=x("./elk-api.js").default,Oe=(function(ge){function Pe(){var ae=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};H(this,Pe);var Me=Object.assign({},ae),Be=!1;try{x.resolve("web-worker"),Be=!0}catch{}if(ae.workerUrl)if(Be){var rn=x("web-worker");Me.workerFactory=function(hn){return new rn(hn)}}else console.warn(`Web worker requested but 'web-worker' package not installed. +`;return c};var JBn=v(OS,"TGraph",120);m(633,494,{3:1,494:1,633:1,105:1,150:1}),v(OS,"TShape",633),m(40,633,{3:1,494:1,40:1,633:1,105:1,150:1},nQ),s.Ib=function(){return Ub(this)};var _H=v(OS,"TNode",40);m(236,1,Wh,S1),s.Ic=function(n){rc(this,n)},s.Jc=function(){var n;return n=St(this.a.d,0),new E3(n)},v(OS,"TNode/2",236),m(334,1,Fr,E3),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(jt(this.a),65).c},s.Ob=function(){return YC(this.a)},s.Qb=function(){TY(this.a)},v(OS,"TNode/2/1",334),m(1893,1,Ai,vo),s.If=function(n,t){iBn(this,u(n,120),t)},v(go,"CompactionProcessor",1893),m(1894,1,Yt,DEe),s.Le=function(n,t){return T7n(this.a,u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$0$Type",1894),m(1895,1,Ft,bCe),s.Mb=function(n){return q5n(this.b,this.a,u(n,49))},s.a=0,s.b=0,v(go,"CompactionProcessor/lambda$1$Type",1895),m(1904,1,Yt,Ml),s.Le=function(n,t){return Rvn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$10$Type",1904),m(1905,1,Yt,Ck),s.Le=function(n,t){return opn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$11$Type",1905),m(1906,1,Yt,P5),s.Le=function(n,t){return Bvn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$12$Type",1906),m(1896,1,Ft,IEe),s.Mb=function(n){return Xwn(this.a,u(n,49))},s.a=0,v(go,"CompactionProcessor/lambda$2$Type",1896),m(1897,1,Ft,_Ee),s.Mb=function(n){return Kwn(this.a,u(n,49))},s.a=0,v(go,"CompactionProcessor/lambda$3$Type",1897),m(1898,1,Ft,b3),s.Mb=function(n){return u(n,40).c.indexOf(DF)==-1},v(go,"CompactionProcessor/lambda$4$Type",1898),m(1899,1,{},LEe),s.Kb=function(n){return Pyn(this.a,u(n,40))},s.a=0,v(go,"CompactionProcessor/lambda$5$Type",1899),m(K0,1,{},PEe),s.Kb=function(n){return Y9n(this.a,u(n,40))},s.a=0,v(go,"CompactionProcessor/lambda$6$Type",K0),m(1901,1,Yt,$Ee),s.Le=function(n,t){return r9n(this.a,u(n,240),u(t,240))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$7$Type",1901),m(1902,1,Yt,REe),s.Le=function(n,t){return c9n(this.a,u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$8$Type",1902),m(1903,1,Yt,Tk),s.Le=function(n,t){return spn(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(go,"CompactionProcessor/lambda$9$Type",1903),m(1891,1,Ai,D6),s.If=function(n,t){ZDn(u(n,120),t)},v(go,"DirectionProcessor",1891),m(1883,1,Ai,sNe),s.If=function(n,t){v_n(this,u(n,120),t)},v(go,"FanProcessor",1883),m(1251,1,Ai,$5),s.If=function(n,t){gXe(u(n,120),t)},v(go,"GraphBoundsProcessor",1251),m(1252,1,{},Zq),s.We=function(n){return u(n,40).e.a},v(go,"GraphBoundsProcessor/lambda$0$Type",1252),m(1253,1,{},Bs),s.We=function(n){return u(n,40).e.b},v(go,"GraphBoundsProcessor/lambda$1$Type",1253),m(1254,1,{},yM),s.We=function(n){return Tgn(u(n,40))},v(go,"GraphBoundsProcessor/lambda$2$Type",1254),m(1255,1,{},kM),s.We=function(n){return Ogn(u(n,40))},v(go,"GraphBoundsProcessor/lambda$3$Type",1255),m(264,23,{3:1,35:1,23:1,264:1,196:1},gw),s.bg=function(){switch(this.g){case 0:return new Pxe;case 1:return new sNe;case 2:return new Lxe;case 3:return new EM;case 4:return new v_;case 8:return new m_;case 5:return new D6;case 6:return new uh;case 7:return new vo;case 9:return new $5;case 10:return new nl;default:throw $(new Gn(nee+(this.f!=null?this.f:""+this.g)))}};var rye,cye,uye,oye,sye,lye,fye,aye,hye,dye,are,HBn=yt(go,tee,264,Tt,oze,Cmn),tsn;m(1890,1,Ai,m_),s.If=function(n,t){nRn(u(n,120),t)},v(go,"LevelCoordinatesProcessor",1890),m(1888,1,Ai,v_),s.If=function(n,t){ENn(this,u(n,120),t)},s.a=0,v(go,"LevelHeightProcessor",1888),m(1889,1,Wh,eU),s.Ic=function(n){rc(this,n)},s.Jc=function(){return jn(),p9(),d7},v(go,"LevelHeightProcessor/1",1889),m(1884,1,Ai,Lxe),s.If=function(n,t){PDn(this,u(n,120),t)},v(go,"LevelProcessor",1884),m(1885,1,Ft,jM),s.Mb=function(n){return Re($e(C(u(n,40),(Ci(),fb))))},v(go,"LevelProcessor/lambda$0$Type",1885),m(1886,1,Ai,EM),s.If=function(n,t){NCn(this,u(n,120),t)},s.a=0,v(go,"NeighborsProcessor",1886),m(1887,1,Wh,SM),s.Ic=function(n){rc(this,n)},s.Jc=function(){return jn(),p9(),d7},v(go,"NeighborsProcessor/1",1887),m(1892,1,Ai,uh),s.If=function(n,t){p_n(this,u(n,120),t)},s.a=0,v(go,"NodePositionProcessor",1892),m(1882,1,Ai,Pxe),s.If=function(n,t){tPn(this,u(n,120),t)},v(go,"RootProcessor",1882),m(1907,1,Ai,nl),s.If=function(n,t){vSn(u(n,120),t)},v(go,"Untreeifyer",1907),m(385,23,{3:1,35:1,23:1,385:1},sK);var yD,hre,bye,gye=yt(LN,"EdgeRoutingMode",385,Tt,eyn,Tmn),isn,kD,_7,dre,wye,pye,bre,gre,mye,wre,vye,pre,Ix,mre,LH,PH,Yf,Ea,L7,_x,Lx,Xd,yye,rsn,vre,fb,jD,ED;m(846,1,qa,xC),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Jpe),""),VQe),"Turns on Tree compaction which decreases the size of the whole tree by placing nodes of multiple levels in one large level"),(Pn(),!1)),(cg(),Sr)),Yi),nn((ph(),Mn))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Hpe),""),"Edge End Texture Length"),"Should be set to the length of the texture at the end of an edge. This value can be used to improve the Edge Routing."),7),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Gpe),""),"Tree Level"),"The index for the tree level the node is in"),ve(0)),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,qpe),""),VQe),"When set to a positive number this option will force the algorithm to place the node to the specified position within the trees layer if weighting is set to constraint"),ve(-1)),hc),jr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Upe),""),"Weighting of Nodes"),"Which weighting to use when computing a node order."),Eye),Ri),Lye),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Xpe),""),"Edge Routing Mode"),"Chooses an Edge Routing algorithm."),kye),Ri),gye),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Kpe),""),"Search Order"),"Which search order to use when computing a spanning tree."),jye),Ri),$ye),nn(Mn)))),BVe((new fP,n))};var csn,usn,osn,kye,ssn,lsn,jye,fsn,asn,Eye;v(LN,"MrTreeMetaDataProvider",846),m(990,1,qa,fP),s.tf=function(n){BVe(n)};var hsn,Sye,xye,mp,Aye,Mye,yre,dsn,bsn,gsn,wsn,psn,msn,vsn,Cye,Tye,Oye,ysn,Hv,$H,Nye,ksn,Dye,kre,jsn,Esn,Ssn,Iye,xsn,Nh,_ye;v(LN,"MrTreeOptions",990),m(991,1,{},Ok),s.uf=function(){var n;return n=new oNe,n},s.vf=function(n){},v(LN,"MrTreeOptions/MrtreeFactory",991),m(353,23,{3:1,35:1,23:1,353:1},p$);var jre,RH,Ere,Sre,Lye=yt(LN,"OrderWeighting",353,Tt,f6n,Omn),Asn;m(425,23,{3:1,35:1,23:1,425:1},Sse);var Pye,xre,$ye=yt(LN,"TreeifyingOrder",425,Tt,c4n,Nmn),Msn;m(1446,1,uc,DU),s.pg=function(n){return u(n,120),Csn},s.If=function(n,t){c7n(this,u(n,120),t)};var Csn;v("org.eclipse.elk.alg.mrtree.p1treeify","DFSTreeifyer",1446),m(1447,1,uc,oP),s.pg=function(n){return u(n,120),Tsn},s.If=function(n,t){zDn(this,u(n,120),t)};var Tsn;v(Q8,"NodeOrderer",1447),m(1454,1,{},nU),s.rd=function(n){return fDe(n)},v(Q8,"NodeOrderer/0methodref$lambda$6$Type",1454),m(1448,1,Ft,E_),s.Mb=function(n){return R4(),Re($e(C(u(n,40),(Ci(),fb))))},v(Q8,"NodeOrderer/lambda$0$Type",1448),m(1449,1,Ft,S_),s.Mb=function(n){return R4(),u(C(u(n,40),(Mu(),Hv)),15).a<0},v(Q8,"NodeOrderer/lambda$1$Type",1449),m(1450,1,Ft,zEe),s.Mb=function(n){return U8n(this.a,u(n,40))},v(Q8,"NodeOrderer/lambda$2$Type",1450),m(1451,1,Ft,BEe),s.Mb=function(n){return $yn(this.a,u(n,40))},v(Q8,"NodeOrderer/lambda$3$Type",1451),m(1452,1,Yt,CM),s.Le=function(n,t){return a8n(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Q8,"NodeOrderer/lambda$4$Type",1452),m(1453,1,Ft,x_),s.Mb=function(n){return R4(),u(C(u(n,40),(Ci(),gre)),15).a!=0},v(Q8,"NodeOrderer/lambda$5$Type",1453),m(1455,1,uc,IU),s.pg=function(n){return u(n,120),Osn},s.If=function(n,t){UIn(this,u(n,120),t)},s.b=0;var Osn;v("org.eclipse.elk.alg.mrtree.p3place","NodePlacer",1455),m(1456,1,uc,SC),s.pg=function(n){return u(n,120),Nsn},s.If=function(n,t){MIn(u(n,120),t)};var Nsn,GBn=v(Qs,"EdgeRouter",1456);m(1458,1,Yt,Nk),s.Le=function(n,t){return oo(u(n,15).a,u(t,15).a)},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/0methodref$compare$Type",1458),m(1463,1,{},xM),s.We=function(n){return ne(re(n))},v(Qs,"EdgeRouter/1methodref$doubleValue$Type",1463),m(1465,1,Yt,iw),s.Le=function(n,t){return ki(ne(re(n)),ne(re(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/2methodref$compare$Type",1465),m(1467,1,Yt,Dk),s.Le=function(n,t){return ki(ne(re(n)),ne(re(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/3methodref$compare$Type",1467),m(1469,1,{},I6),s.We=function(n){return ne(re(n))},v(Qs,"EdgeRouter/4methodref$doubleValue$Type",1469),m(1471,1,Yt,AM),s.Le=function(n,t){return ki(ne(re(n)),ne(re(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/5methodref$compare$Type",1471),m(1473,1,Yt,MM),s.Le=function(n,t){return ki(ne(re(n)),ne(re(t)))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/6methodref$compare$Type",1473),m(1457,1,{},y_),s.Kb=function(n){return P1(),u(C(u(n,40),(Mu(),Nh)),15)},v(Qs,"EdgeRouter/lambda$0$Type",1457),m(1468,1,{},k_),s.Kb=function(n){return ypn(u(n,40))},v(Qs,"EdgeRouter/lambda$11$Type",1468),m(1470,1,{},wCe),s.Kb=function(n){return J3n(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Qs,"EdgeRouter/lambda$13$Type",1470),m(1472,1,{},gCe),s.Kb=function(n){return Epn(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Qs,"EdgeRouter/lambda$15$Type",1472),m(1474,1,Yt,j_),s.Le=function(n,t){return QEn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$17$Type",1474),m(1475,1,Yt,tU),s.Le=function(n,t){return WEn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$18$Type",1475),m(1476,1,Yt,A_),s.Le=function(n,t){return eSn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$19$Type",1476),m(1459,1,Ft,FEe),s.Mb=function(n){return y4n(this.a,u(n,40))},s.a=0,v(Qs,"EdgeRouter/lambda$2$Type",1459),m(1477,1,Yt,M_),s.Le=function(n,t){return ZEn(u(n,65),u(t,65))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$20$Type",1477),m(1460,1,Yt,C_),s.Le=function(n,t){return N3n(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$3$Type",1460),m(1461,1,Yt,TM),s.Le=function(n,t){return D3n(u(n,40),u(t,40))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"EdgeRouter/lambda$4$Type",1461),m(1462,1,{},T_),s.Kb=function(n){return kpn(u(n,40))},v(Qs,"EdgeRouter/lambda$5$Type",1462),m(1464,1,{},pCe),s.Kb=function(n){return H3n(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Qs,"EdgeRouter/lambda$7$Type",1464),m(1466,1,{},mCe),s.Kb=function(n){return jpn(this.b,this.a,u(n,40))},s.a=0,s.b=0,v(Qs,"EdgeRouter/lambda$9$Type",1466),m(662,1,{662:1},lHe),s.e=0,s.f=!1,s.g=!1,v(Qs,"MultiLevelEdgeNodeNodeGap",662),m(1864,1,Yt,O_),s.Le=function(n,t){return I4n(u(n,240),u(t,240))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"MultiLevelEdgeNodeNodeGap/lambda$0$Type",1864),m(1865,1,Yt,N_),s.Le=function(n,t){return _4n(u(n,240),u(t,240))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Qs,"MultiLevelEdgeNodeNodeGap/lambda$1$Type",1865);var Gv;m(487,23,{3:1,35:1,23:1,487:1,188:1,196:1},xse),s.bg=function(){return XFe(this)},s.og=function(){return XFe(this)};var BH,qv,Rye=yt(Vpe,"RadialLayoutPhases",487,Tt,u4n,Dmn),Dsn;m(1083,214,Qw,$Ae),s.kf=function(n,t){var i,r,c,o,l,f;if(i=qUe(this,n),t.Tg("Radial layout",i.c.length),Re($e(ye(n,(J0(),Vye))))||HT((r=new hj((_b(),new w0(n))),r)),f=YAn(n),ji(n,(B3(),Gv),f),!f)throw $(new Gn("The given graph is not a tree!"));for(c=ne(re(ye(n,JH))),c==0&&(c=vqe(n)),ji(n,JH,c),l=new L(qUe(this,n));l.a=3)for(fe=u(K(te,0),26),Ie=u(K(te,1),26),o=0;o+2=fe.f+Ie.f+p||Ie.f>=be.f+fe.f+p){cn=!0;break}else++o;else cn=!0;if(!cn){for(S=te.i,f=new ut(te);f.e!=f.i.gc();)l=u(ft(f),26),ji(l,(Xt(),PD),ve(S)),--S;kKe(n,new i4),t.Ug();return}for(i=(RT(this.a),fa(this.a,(WB(),Px),u(ye(n,A6e),188)),fa(this.a,HH,u(ye(n,y6e),188)),fa(this.a,$re,u(ye(n,E6e),188)),Fse(this.a,(Cn=new or,qt(Cn,Px,(kz(),zre)),qt(Cn,HH,Bre),Re($e(ye(n,m6e)))&&qt(Cn,Px,Fre),Re($e(ye(n,p6e)))&&qt(Cn,Px,Rre),Cn)),rN(this.a,n)),b=1/i.c.length,O=new L(i);O.a0&&gFe((Yn(t-1,n.length),n.charCodeAt(t-1)),aQe);)--t;if(r>=t)throw $(new Gn("The given string does not contain any numbers."));if(c=K2((Yr(r,t,n.length),n.substr(r,t-r)),`,|;|\r| +`),c.length!=2)throw $(new Gn("Exactly two numbers are expected, "+c.length+" were found."));try{this.a=F2(J2(c[0])),this.b=F2(J2(c[1]))}catch(o){throw o=sr(o),X(o,131)?(i=o,$(new Gn(hQe+i))):$(o)}},s.Ib=function(){return"("+this.a+","+this.b+")"},s.a=0,s.b=0;var Lr=v(TN,"KVector",8);m(78,66,{3:1,4:1,20:1,31:1,56:1,18:1,66:1,16:1,78:1,414:1},xs,qP,NOe),s.Nc=function(){return Mkn(this)},s.ag=function(n){var t,i,r,c,o,l;r=K2(n,`,|;|\\(|\\)|\\[|\\]|\\{|\\}| | | +`),qs(this);try{for(i=0,o=0,c=0,l=0;i0&&(o%2==0?c=F2(r[i]):l=F2(r[i]),o>0&&o%2!=0&&Vt(this,new je(c,l)),++o),++i}catch(f){throw f=sr(f),X(f,131)?(t=f,$(new Gn("The given string does not match the expected format for vectors."+t))):$(f)}},s.Ib=function(){var n,t,i;for(n=new il("("),t=St(this,0);t.b!=t.d.c;)i=u(jt(t),8),Kt(n,i.a+","+i.b),t.b!=t.d.c&&(n.a+="; ");return(n.a+=")",n).a};var l9e=v(TN,"KVectorChain",78);m(256,23,{3:1,35:1,23:1,256:1},Rj);var sce,ZH,eG,TD,OD,nG,f9e=yt(Oo,"Alignment",256,Tt,C9n,c3n),dfn;m(975,1,qa,TC),s.tf=function(n){rKe(n)};var a9e,lce,bfn,h9e,d9e,gfn,b9e,wfn,pfn,g9e,w9e,mfn;v(Oo,"BoxLayouterOptions",975),m(976,1,{},GM),s.uf=function(){var n;return n=new hL,n},s.vf=function(n){},v(Oo,"BoxLayouterOptions/BoxFactory",976),m(299,23,{3:1,35:1,23:1,299:1},Bj);var Gx,fce,qx,Ux,Xx,ace,hce=yt(Oo,"ContentAlignment",299,Tt,T9n,u3n),vfn;m(689,1,qa,CC),s.tf=function(n){We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,pWe),""),"Layout Algorithm"),"Select a specific layout algorithm."),(cg(),By)),ze),nn((ph(),Mn))))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,mWe),""),"Resolved Layout Algorithm"),"Meta data associated with the selected algorithm."),Wa),XBn),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ppe),""),"Alignment"),"Alignment of the selected node relative to other nodes; the exact meaning depends on the used algorithm."),p9e),Ri),f9e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,G8),""),"Aspect Ratio"),"The desired aspect ratio of the drawing, that is the quotient of width by height."),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,N2e),""),"Bend Points"),"A fixed list of bend points for the edge. This is used by the 'Fixed Layout' algorithm to specify a pre-defined routing for an edge. The vector chain must include the source point, any bend points, and the target point, so it must have at least two points."),Wa),l9e),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,CF),""),"Content Alignment"),"Specifies how the content of a node are aligned. Each node can individually control the alignment of its contents. I.e. if a node should be aligned top left in its parent node, the parent node should specify that option."),v9e),Ry),hce),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,_N),""),"Debug Mode"),"Whether additional debug information shall be generated."),(Pn(),!1)),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Fee),""),"Direction"),"Overall direction of edges: horizontal (right / left) or vertical (down / up)."),y9e),Ri),Vx),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,IN),""),"Edge Routing"),"What kind of edge routing style should be applied for the content of a parent node. Algorithms may also set this option to single edges in order to mark them as splines. The bend point list of edges with this option set to SPLINES must be interpreted as control points for a piecewise cubic spline."),E9e),Ri),Ace),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,T2e),""),"Expand Nodes"),"If active, nodes are expanded to fill the area of their parent."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,MF),""),"Hierarchy Handling"),"Determines whether separate layout runs are triggered for different compound nodes in a hierarchical graph. Setting a node's hierarchy handling to `INCLUDE_CHILDREN` will lay out that node and all of its descendants in a single layout run, until a descendant is encountered which has its hierarchy handling set to `SEPARATE_CHILDREN`. In general, `SEPARATE_CHILDREN` will ensure that a new layout run is triggered for a node with that setting. Including multiple levels of hierarchy in a single layout run may allow cross-hierarchical edges to be laid out properly. If the root node is set to `INHERIT` (or not set at all), the default behavior is `SEPARATE_CHILDREN`."),x9e),Ri),b8e),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,nm),""),"Padding"),"The padding to be left to a parent element's border when placing child elements. This can also serve as an output option of a layout algorithm if node size calculation is setup appropriately."),P9e),Wa),j3e),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,jS),""),"Interactive"),"Whether the algorithm should be run in interactive mode for the content of a parent node. What this means exactly depends on how the specific algorithm interprets this option. Usually in the interactive mode algorithms try to modify the current layout as little as possible."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,OF),""),"interactive Layout"),"Whether the graph should be changeable interactively and by setting constraints"),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ES),""),"Omit Node Micro Layout"),"Node micro layout comprises the computation of node dimensions (if requested), the placement of ports and their labels, and the placement of node labels. The functionality is implemented independent of any specific layout algorithm and shouldn't have any negative impact on the layout algorithm's performance itself. Yet, if any unforeseen behavior occurs, this option allows to deactivate the micro layout."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,WZ),""),"Port Constraints"),"Defines constraints of the position of the ports of a node."),F9e),Ri),p8e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,TF),""),"Position"),"The position of a node, port, or label. This is used by the 'Fixed Layout' algorithm to specify a pre-defined position."),Wa),Lr),Mi(fr,z(B(Qa,1),ke,160,0,[Kd,Q1]))))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,EN),""),"Priority"),"Defines the priority of an object; its meaning depends on the specific layout algorithm and the context where it is used."),hc),jr),Mi(fr,z(B(Qa,1),ke,160,0,[Sa]))))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,sF),""),"Randomization Seed"),"Seed used for pseudo-random number generators to control the layout algorithm. If the value is 0, the seed shall be determined pseudo-randomly (e.g. from the system time)."),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,kS),""),"Separate Connected Components"),"Whether each connected component should be processed separately."),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Cpe),""),"Junction Points"),"This option is not used as option, but as output of the layout algorithms. It is attached to edges and determines the points where junction symbols should be drawn in order to represent hyperedges with orthogonal routing. Whether such points are computed depends on the chosen layout algorithm and edge routing style. The points are put into the vector chain with no specific order."),C9e),Wa),l9e),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Dpe),""),"Comment Box"),"Whether the node should be regarded as a comment box instead of a regular node. In that case its placement should be similar to how labels are handled. Any edges incident to a comment box specify to which graph elements the comment is related."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ipe),""),"Hypernode"),"Whether the node should be handled as a hypernode."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,yBn),""),"Label Manager"),"Label managers can shorten labels upon a layout algorithm's request."),Wa),ZBn),Mi(Mn,z(B(Qa,1),ke,160,0,[Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vWe),""),"Softwrapping Fuzziness"),"Determines the amount of fuzziness to be used when performing softwrapping on labels. The value expresses the percent of overhang that is permitted for each line. If the next line would take up less space than this threshold, it is appended to the current line instead of being placed in a new line."),0),ec),gr),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Lpe),""),"Margins"),"Margins define additional space around the actual bounds of a graph element. For instance, ports or labels being placed on the outside of a node's border might introduce such a margin. The margin is used to guarantee non-overlap of other graph elements with those ports or labels."),T9e),Wa),k3e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,gpe),""),"No Layout"),"No layout is done for the associated element. This is used to mark parts of a diagram to avoid their inclusion in the layout graph, or to mark parts of the layout graph to prevent layout engines from processing them. If you wish to exclude the contents of a compound node from automatic layout, while the node itself is still considered on its own layer, use the 'Fixed Layout' algorithm for that node."),!1),Sr),Yi),Mi(fr,z(B(Qa,1),ke,160,0,[Sa,Kd,Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,yWe),""),"Scale Factor"),"The scaling factor to be applied to the corresponding node in recursive layout. It causes the corresponding node's size to be adjusted, and its ports and labels to be sized and placed accordingly after the layout of that node has been determined (and before the node itself and its siblings are arranged). The scaling is not reverted afterwards, so the resulting layout graph contains the adjusted size and position data. This option is currently not supported if 'Layout Hierarchy' is set."),1),ec),gr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,kWe),""),"Child Area Width"),"The width of the area occupied by the laid out children of a node."),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,jWe),""),"Child Area Height"),"The height of the area occupied by the laid out children of a node."),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,SN),""),hWe),"Turns topdown layout on and off. If this option is enabled, hierarchical layout will be computed first for the root node and then for its children recursively. Layouts are then scaled down to fit the area provided by their parents. Graphs must follow a certain structure for topdown layout to work properly. {@link TopdownNodeTypes.PARALLEL_NODE} nodes must have children of type {@link TopdownNodeTypes.HIERARCHICAL_NODE} and must define {@link topdown.hierarchicalNodeWidth} and {@link topdown.hierarchicalNodeAspectRatio} for their children. Furthermore they need to be laid out using an algorithm that is a {@link TopdownLayoutProvider}. Hierarchical nodes can also be parents of other hierarchical nodes and can optionally use a {@link TopdownSizeApproximator} to dynamically set sizes during topdown layout. In this case {@link topdown.hierarchicalNodeWidth} and {@link topdown.hierarchicalNodeAspectRatio} should be set on the node itself rather than the parent. The values are then used by the size approximator as base values. Hierarchical nodes require the layout option {@link nodeSize.fixedGraphSize} to be true to prevent the algorithm used there from resizing the hierarchical node. This option is not supported if 'Hierarchy Handling' is set to 'INCLUDE_CHILDREN'"),!1),Sr),Yi),nn(Mn)))),Gi(n,SN,Ww,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,EWe),""),"Animate"),"Whether the shift from the old layout to the new computed layout shall be animated."),!0),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,SWe),""),"Animation Time Factor"),"Factor for computation of animation time. The higher the value, the longer the animation time. If the value is 0, the resulting time is always equal to the minimum defined by 'Minimal Animation Time'."),ve(100)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xWe),""),"Layout Ancestors"),"Whether the hierarchy levels on the path from the selected element to the root of the diagram shall be included in the layout process."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,AWe),""),"Maximal Animation Time"),"The maximal time for animations, in milliseconds."),ve(4e3)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,MWe),""),"Minimal Animation Time"),"The minimal time for animations, in milliseconds."),ve(400)),hc),jr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,CWe),""),"Progress Bar"),"Whether a progress bar shall be displayed during layout computations."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,TWe),""),"Validate Graph"),"Whether the graph shall be validated before any layout algorithm is applied. If this option is enabled and at least one error is found, the layout process is aborted and a message is shown to the user."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,OWe),""),"Validate Options"),"Whether layout options shall be validated before any layout algorithm is applied. If this option is enabled and at least one error is found, the layout process is aborted and a message is shown to the user."),!0),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,NWe),""),"Zoom to Fit"),"Whether the zoom level shall be set to view the whole diagram after layout."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,O2e),"box"),"Box Layout Mode"),"Configures the packing mode used by the {@link BoxLayoutProvider}. If SIMPLE is not required (neither priorities are used nor the interactive mode), GROUP_DEC can improve the packing and decrease the area. GROUP_MIXED and GROUP_INC may, in very specific scenarios, work better."),m9e),Ri),O8e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,DWe),"json"),"Shape Coords"),"For layouts transferred into JSON graphs, specify the coordinate system to be used for nodes, ports, and labels of nodes and ports."),M9e),Ri),y8e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,IWe),"json"),"Edge Coords"),"For layouts transferred into JSON graphs, specify the coordinate system to be used for edge route points and edge labels."),A9e),Ri),n8e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ipe),Xa),"Comment Comment Spacing"),"Spacing to be preserved between a comment box and other comment boxes connected to the same node. The space left between comment boxes of different nodes is controlled by the node-node spacing."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,rpe),Xa),"Comment Node Spacing"),"Spacing to be preserved between a node and its connected comment boxes. The space left between a node and the comments of another node is controlled by the node-node spacing."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,cpe),Xa),"Components Spacing"),"Spacing to be preserved between pairs of connected components. This option is only relevant if 'separateConnectedComponents' is activated."),20),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,upe),Xa),"Edge Spacing"),"Spacing to be preserved between any two edges. Note that while this can somewhat easily be satisfied for the segments of orthogonally drawn edges, it is harder for general polylines or splines."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,QZ),Xa),"Edge Label Spacing"),"The minimal distance to be preserved between a label and the edge it is associated with. Note that the placement of a label is influenced by the 'edgelabels.placement' option."),2),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,zee),Xa),"Edge Node Spacing"),"Spacing to be preserved between nodes and edges."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ope),Xa),"Label Spacing"),"Determines the amount of space to be left between two labels of the same graph element."),0),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,fpe),Xa),"Label Node Spacing"),"Spacing to be preserved between labels and the border of node they are associated with. Note that the placement of a label is influenced by the 'nodelabels.placement' option."),5),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,spe),Xa),"Horizontal spacing between Label and Port"),"Horizontal spacing to be preserved between labels and the ports they are associated with. Note that the placement of a label is influenced by the 'portlabels.placement' option."),1),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,lpe),Xa),"Vertical spacing between Label and Port"),"Vertical spacing to be preserved between labels and the ports they are associated with. Note that the placement of a label is influenced by the 'portlabels.placement' option."),1),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,em),Xa),"Node Spacing"),"The minimal distance to be preserved between each two nodes."),20),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ape),Xa),"Node Self Loop Spacing"),"Spacing to be preserved between a node and its self loops."),10),ec),gr),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hpe),Xa),"Port Spacing"),"Spacing between pairs of ports of the same node."),10),ec),gr),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,dpe),Xa),"Individual Spacing"),"Allows to specify individual spacing values for graph elements that shall be different from the value specified for the element's parent."),Wa),gan),Mi(fr,z(B(Qa,1),ke,160,0,[Sa,Kd,Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ppe),Xa),"Additional Port Space"),"Additional space around the sets of ports on each node side. For each side of a node, this option can reserve additional space before and after the ports on each side. For example, a top spacing of 20 makes sure that the first port on the western and eastern side is 20 units away from the northern border."),Q9e),Wa),k3e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Hee),PWe),"Layout Partition"),"Partition to which the node belongs. This requires Layout Partitioning to be active. Nodes with lower partition IDs will appear to the left of nodes with higher partition IDs (assuming a left-to-right layout direction)."),hc),jr),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),Gi(n,Hee,Jee,Nfn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Jee),PWe),"Layout Partitioning"),"Whether to activate partitioned layout. This will allow to group nodes through the Layout Partition option. a pair of nodes with different partition indices is then placed such that the node with lower index is placed to the left of the other node (with left-to-right layout direction). Depending on the layout algorithm, this may only be guaranteed to work if all nodes have a layout partition configured, or at least if edges that cross partitions are not part of a partition-crossing cycle."),$9e),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,ype),$We),"Node Label Padding"),"Define padding for node labels that are placed inside of a node."),N9e),Wa),j3e),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,U8),$We),"Node Label Placement"),"Hints for where node labels are to be placed; if empty, the node label's position is not modified."),D9e),Ry),$c),Mi(fr,z(B(Qa,1),ke,160,0,[Q1]))))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Epe),RF),"Port Alignment"),"Defines the default port distribution for a node. May be overridden for each side individually."),B9e),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Spe),RF),"Port Alignment (North)"),"Defines how ports on the northern side are placed, overriding the node's general port alignment."),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,xpe),RF),"Port Alignment (South)"),"Defines how ports on the southern side are placed, overriding the node's general port alignment."),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Ape),RF),"Port Alignment (West)"),"Defines how ports on the western side are placed, overriding the node's general port alignment."),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Mpe),RF),"Port Alignment (East)"),"Defines how ports on the eastern side are placed, overriding the node's general port alignment."),Ri),Zx),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,wv),bne),"Node Size Constraints"),"What should be taken into account when calculating a node's size. Empty size constraints specify that a node's size is already fixed and should not be changed."),I9e),Ry),tA),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hy),bne),"Node Size Options"),"Options modifying the behavior of the size constraints set on a node. Each member of the set specifies something that should be taken into account when calculating node sizes. The empty set corresponds to no further modifications."),L9e),Ry),k8e),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,dy),bne),"Node Size Minimum"),"The minimal size to which a node can be reduced."),_9e),Wa),Lr),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,q8),bne),"Fixed Graph Size"),"By default, the fixed layout provider will enlarge a graph until it is large enough to contain its children. If this option is set, it won't do so."),!1),Sr),Yi),nn(Mn)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ope),Bee),"Edge Label Placement"),"Gives a hint on where to put edge labels."),k9e),Ri),t8e),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,lF),Bee),"Inline Edge Labels"),"If true, an edge label is placed directly on its edge. May only apply to center edge labels. This kind of label placement is only advisable if the label's rendering is such that it is not crossed by its edge and thus stays legible."),!1),Sr),Yi),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,kBn),"font"),"Font Name"),"Font name used for a label."),By),ze),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,_We),"font"),"Font Size"),"Font size used for a label."),hc),jr),nn(Q1)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,_pe),gne),"Port Anchor Offset"),"The offset to the port position where connections shall be attached."),Wa),Lr),nn(Kd)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,Npe),gne),"Port Index"),"The index of a port in the fixed order around a node. The order is assumed as clockwise, starting with the leftmost port on the top side. This option must be set if 'Port Constraints' is set to FIXED_ORDER and no specific positions are given for the ports. Additionally, the option 'Port Side' must be defined in this case."),hc),jr),nn(Kd)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,wpe),gne),"Port Side"),"The side of a node on which a port is situated. This option must be set if 'Port Constraints' is set to FIXED_SIDE or FIXED_ORDER and no specific positions are given for the ports."),G9e),Ri),xc),nn(Kd)))),We(n,new Ge(Ye(Ve(Qe(qe(Ke(Ue(Xe(new Je,bpe),gne),"Port Border Offset"),"The offset of ports on the node border. With a positive offset the port is moved outside of the node, while with a negative offset the port is moved towards the inside. An offset of 0 means that the port is placed directly on the node border, i.e. if the port side is north, the port's south border touches the nodes's north border; if the port side is east, the port's west border touches the nodes's east border; if the port side is south, the port's north border touches the node's south border; if the port side is west, the port's east border touches the node's west border."),ec),gr),nn(Kd)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,X8),_2e),"Port Label Placement"),"Decides on a placement method for port labels; if empty, the node label's position is not modified."),J9e),Ry),sG),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,kpe),_2e),"Port Labels Next to Port"),"Use 'portLabels.placement': NEXT_TO_PORT_OF_POSSIBLE."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,jpe),_2e),"Treat Port Labels as Group"),"If this option is true (default), the labels of a port will be treated as a group when it comes to centering them next to their port. If this option is false, only the first label will be centered next to the port, with the others being placed below. This only applies to labels of eastern and western ports and will have no effect if labels are not placed next to their port."),!0),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,hne),t7),"Number of size categories"),"Defines the number of categories to use for the FIXED_INTEGER_RATIO_BOXES size approximator."),ve(3)),hc),jr),nn(Mn)))),Gi(n,hne,dne,Hfn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,D2e),t7),"Weight of a node containing children for determining the graph size"),"When determining the graph size for the size categorisation, this value determines how many times a node containing children is weighted more than a simple node. For example setting this value to four would result in a graph containing a simple node and a hierarchical node to be counted as having a size of five."),ve(4)),hc),jr),nn(Mn)))),Gi(n,D2e,hne,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,xN),t7),"Topdown Scale Factor"),"The scaling factor to be applied to the nodes laid out within the node in recursive topdown layout. The difference to 'Scale Factor' is that the node itself is not scaled. This value has to be set on hierarchical nodes."),1),ec),gr),nn(Mn)))),Gi(n,xN,Ww,zfn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,dne),t7),"Topdown Size Approximator"),"The size approximator to be used to set sizes of hierarchical nodes during topdown layout. The default value is null, which results in nodes keeping whatever size is defined for them e.g. through parent parallel node or by manually setting the size."),null),Wa),KBn),nn(fr)))),Gi(n,dne,Ww,Ffn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,AN),t7),"Topdown Hierarchical Node Width"),"The fixed size of a hierarchical node when using topdown layout. If this value is set on a parallel node it applies to its children, when set on a hierarchical node it applies to the node itself."),150),ec),gr),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),Gi(n,AN,Ww,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,MN),t7),"Topdown Hierarchical Node Aspect Ratio"),"The fixed aspect ratio of a hierarchical node when using topdown layout. Default is 1/sqrt(2). If this value is set on a parallel node it applies to its children, when set on a hierarchical node it applies to the node itself."),1.414),ec),gr),Mi(Mn,z(B(Qa,1),ke,160,0,[fr]))))),Gi(n,MN,Ww,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Ww),t7),"Topdown Node Type"),"The different node types used for topdown layout. If the node type is set to {@link TopdownNodeTypes.PARALLEL_NODE} the algorithm must be set to a {@link TopdownLayoutProvider} such as {@link TopdownPacking}. The {@link nodeSize.fixedGraphSize} option is technically only required for hierarchical nodes."),null),Ri),E8e),nn(fr)))),Gi(n,Ww,q8,null),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,I2e),t7),"Topdown Scale Cap"),"Determines the upper limit for the topdown scale factor. The default value is 1.0 which ensures that nested children never end up appearing larger than their parents in terms of unit sizes such as the font size. If the limit is larger, nodes will fully utilize the available space, but it is counteriniuitive for inner nodes to have a larger scale than outer nodes."),1),ec),gr),nn(Mn)))),Gi(n,I2e,Ww,Bfn),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,mpe),RWe),"Activate Inside Self Loops"),"Whether this node allows to route self loops inside of it instead of around it. If set to true, this will make the node a compound node if it isn't already, and will require the layout algorithm to support compound nodes with hierarchical ports."),!1),Sr),Yi),nn(fr)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,vpe),RWe),"Inside Self Loop"),"Whether a self loop should be routed inside a node instead of around that node."),!1),Sr),Yi),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,Tpe),"edge"),"Edge Thickness"),"The thickness of an edge. This is a hint on the line width used to draw an edge, possibly requiring more space to be reserved for it."),1),ec),gr),nn(Sa)))),We(n,new Ge(Ye(Ve(Qe(an(qe(Ke(Ue(Xe(new Je,LWe),"edge"),"Edge Type"),"The type of an edge. This is usually used for UML class diagrams, where associations must be handled differently from generalizations."),S9e),Ri),s8e),nn(Sa)))),Tj(n,new N4(Ej(h9(a9(new f0,$n),"Layered"),'The layer-based method was introduced by Sugiyama, Tagawa and Toda in 1981. It emphasizes the direction of edges by pointing as many edges as possible into the same direction. The nodes are arranged in layers, which are sometimes called "hierarchies", and then reordered such that the number of edge crossings is minimized. Afterwards, concrete coordinates are computed for the nodes and edge bend points.'))),Tj(n,new N4(Ej(h9(a9(new f0,"org.eclipse.elk.orthogonal"),"Orthogonal"),`Orthogonal methods that follow the "topology-shape-metrics" approach by Batini, Nardelli and Tamassia '86. The first phase determines the topology of the drawing by applying a planarization technique, which results in a planar representation of the graph. The orthogonal shape is computed in the second phase, which aims at minimizing the number of edge bends, and is called orthogonalization. The third phase leads to concrete coordinates for nodes and edge bend points by applying a compaction method, thus defining the metrics.`))),Tj(n,new N4(Ej(h9(a9(new f0,$o),"Force"),"Layout algorithms that follow physical analogies by simulating a system of attractive and repulsive forces. The first successful method of this kind was proposed by Eades in 1984."))),Tj(n,new N4(Ej(h9(a9(new f0,"org.eclipse.elk.circle"),"Circle"),"Circular layout algorithms emphasize cycles or biconnected components of a graph by arranging them in circles. This is useful if a drawing is desired where such components are clearly grouped, or where cycles are shown as prominent OPTIONS of the graph."))),Tj(n,new N4(Ej(h9(a9(new f0,YQe),"Tree"),"Specialized layout methods for trees, i.e. acyclic graphs. The regular structure of graphs that have no undirected cycles can be emphasized using an algorithm of this type."))),Tj(n,new N4(Ej(h9(a9(new f0,"org.eclipse.elk.planar"),"Planar"),"Algorithms that require a planar or upward planar graph. Most of these algorithms are theoretically interesting, but not practically usable."))),Tj(n,new N4(Ej(h9(a9(new f0,Gl),"Radial"),"Radial layout algorithms usually position the nodes of the graph on concentric circles."))),JXe((new RU,n)),rKe((new TC,n)),bXe((new BU,n))};var zy,yfn,p9e,$7,kfn,jfn,m9e,Cm,Tm,Efn,ND,v9e,DD,Mg,y9e,dce,bce,k9e,j9e,E9e,Sfn,S9e,xfn,Xv,x9e,Afn,ID,gce,_D,wce,Mfn,A9e,Cfn,M9e,Kv,C9e,R7,T9e,O9e,N9e,Vv,D9e,Cg,I9e,Om,Yv,_9e,ab,L9e,tG,LD,o1,P9e,Tfn,$9e,Ofn,Nfn,R9e,B9e,pce,mce,vce,yce,z9e,Ps,Kx,F9e,kce,jce,Nm,J9e,H9e,Qv,G9e,Fy,PD,Ece,Dm,Dfn,Sce,Ifn,_fn,Lfn,Pfn,q9e,U9e,Jy,X9e,iG,K9e,V9e,Vd,$fn,Y9e,Q9e,W9e,B7,Im,z7,Hy,Rfn,Bfn,rG,zfn,cG,Ffn,Jfn,Hfn,Gfn;v(Oo,"CoreOptions",689),m(86,23,{3:1,35:1,23:1,86:1},bT);var Za,Zc,ru,eh,Vl,Vx=yt(Oo,"Direction",86,Tt,J6n,t3n),qfn;m(278,23,{3:1,35:1,23:1,278:1},y$);var uG,$D,Z9e,e8e,n8e=yt(Oo,"EdgeCoords",278,Tt,a6n,i3n),Ufn;m(279,23,{3:1,35:1,23:1,279:1},wK);var F7,_m,J7,t8e=yt(Oo,"EdgeLabelPlacement",279,Tt,syn,r3n),Xfn;m(222,23,{3:1,35:1,23:1,222:1},k$);var H7,RD,Gy,xce,Ace=yt(Oo,"EdgeRouting",222,Tt,h6n,n3n),Kfn;m(327,23,{3:1,35:1,23:1,327:1},zj);var i8e,r8e,c8e,u8e,Mce,o8e,s8e=yt(Oo,"EdgeType",327,Tt,D9n,h3n),Vfn;m(973,1,qa,RU),s.tf=function(n){JXe(n)};var l8e,f8e,a8e,h8e,Yfn,d8e,Yx;v(Oo,"FixedLayouterOptions",973),m(974,1,{},qM),s.uf=function(){var n;return n=new rw,n},s.vf=function(n){},v(Oo,"FixedLayouterOptions/FixedFactory",974),m(347,23,{3:1,35:1,23:1,347:1},pK);var Yd,oG,Qx,b8e=yt(Oo,"HierarchyHandling",347,Tt,lyn,d3n),Qfn,KBn=Hi(Oo,"ITopdownSizeApproximator");m(292,23,{3:1,35:1,23:1,292:1},j$);var s1,hb,BD,zD,Wfn=yt(Oo,"LabelSide",292,Tt,d6n,a3n),Zfn;m(96,23,{3:1,35:1,23:1,96:1},C3);var W1,Qf,wf,Wf,ml,Zf,pf,l1,ea,$c=yt(Oo,"NodeLabelPlacement",96,Tt,I8n,o3n),ean;m(257,23,{3:1,35:1,23:1,257:1},gT);var g8e,Wx,db,w8e,FD,Zx=yt(Oo,"PortAlignment",257,Tt,e9n,s3n),nan;m(102,23,{3:1,35:1,23:1,102:1},Fj);var Tg,to,f1,G7,nh,bb,p8e=yt(Oo,"PortConstraints",102,Tt,N9n,l3n),tan;m(280,23,{3:1,35:1,23:1,280:1},Jj);var eA,nA,Z1,JD,gb,qy,sG=yt(Oo,"PortLabelPlacement",280,Tt,O9n,f3n),ian;m(64,23,{3:1,35:1,23:1,64:1},wT);var et,Xn,Yl,Ql,Wo,zo,th,na,ks,hs,mo,js,Zo,es,ta,vl,yl,mf,bt,ku,Kn,xc=yt(Oo,"PortSide",64,Tt,H6n,p3n),ran;m(977,1,qa,BU),s.tf=function(n){bXe(n)};var can,uan,m8e,oan,san;v(Oo,"RandomLayouterOptions",977),m(978,1,{},UM),s.uf=function(){var n;return n=new YM,n},s.vf=function(n){},v(Oo,"RandomLayouterOptions/RandomFactory",978),m(300,23,{3:1,35:1,23:1,300:1},mK);var HD,Cce,v8e,y8e=yt(Oo,"ShapeCoords",300,Tt,fyn,m3n),lan;m(380,23,{3:1,35:1,23:1,380:1},E$);var Lm,GD,qD,Og,tA=yt(Oo,"SizeConstraint",380,Tt,g6n,v3n),fan;m(266,23,{3:1,35:1,23:1,266:1},T3);var UD,lG,q7,Tce,XD,iA,fG,aG,hG,k8e=yt(Oo,"SizeOptions",266,Tt,z8n,g3n),aan;m(281,23,{3:1,35:1,23:1,281:1},vK);var Pm,j8e,dG,E8e=yt(Oo,"TopdownNodeTypes",281,Tt,ayn,w3n),han;m(288,23,zF);var S8e,Oce,x8e,A8e,KD=yt(Oo,"TopdownSizeApproximator",288,Tt,b6n,b3n);m(969,288,zF,gDe),s.Sg=function(n){return WJe(n)},yt(Oo,"TopdownSizeApproximator/1",969,KD,null,null),m(970,288,zF,QDe),s.Sg=function(n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn,Cn;for(t=u(ye(n,(Xt(),Dm)),144),Ie=(v0(),A=new pj,A),VO(Ie,n),cn=new pt,o=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));o.e!=o.i.gc();)r=u(ft(o),26),V=(S=new pj,S),Iz(V,Ie),VO(V,r),Cn=WJe(r),ww(V,k.Math.max(r.g,Cn.a),k.Math.max(r.f,Cn.b)),Ko(cn.f,r,V);for(c=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));c.e!=c.i.gc();)for(r=u(ft(c),26),p=new ut((!r.e&&(r.e=new Nn(pr,r,7,4)),r.e));p.e!=p.i.gc();)b=u(ft(p),85),be=u(du(Xc(cn.f,r)),26),fe=u(Bn(cn,K((!b.c&&(b.c=new Nn(vt,b,5,8)),b.c),0)),26),te=(y=new w3,y),Et((!te.b&&(te.b=new Nn(vt,te,4,7)),te.b),be),Et((!te.c&&(te.c=new Nn(vt,te,5,8)),te.c),fe),Dz(te,Bi(be)),VO(te,b);I=u(JT(t.f),214);try{I.kf(Ie,new a0),Wfe(t.f,I)}catch(Dn){throw Dn=sr(Dn),X(Dn,101)?(O=Dn,$(O)):$(Dn)}return da(Ie,Tm)||da(Ie,Cm)||oZ(Ie),h=ne(re(ye(Ie,Tm))),f=ne(re(ye(Ie,Cm))),l=h/f,i=ne(re(ye(Ie,Im)))*k.Math.sqrt((!Ie.a&&(Ie.a=new pe(Jt,Ie,10,11)),Ie.a).i),tn=u(ye(Ie,o1),104),q=tn.b+tn.c+1,R=tn.d+tn.a+1,new je(k.Math.max(q,i),k.Math.max(R,i/l))},yt(Oo,"TopdownSizeApproximator/2",970,KD,null,null),m(971,288,zF,E_e),s.Sg=function(n){var t,i,r,c,o,l;return i=ne(re(ye(n,(Xt(),Im)))),t=i/ne(re(ye(n,B7))),r=z_n(n),o=u(ye(n,o1),104),c=ne(re(_e(Vd))),Bi(n)&&(c=ne(re(ye(Bi(n),Vd)))),l=A1(new je(i,t),r),gi(l,new je(-(o.b+o.c)-c,-(o.d+o.a)-c))},yt(Oo,"TopdownSizeApproximator/3",971,KD,null,null),m(972,288,zF,WDe),s.Sg=function(n){var t,i,r,c,o,l,f,h,b,p;for(l=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));l.e!=l.i.gc();)o=u(ft(l),26),ye(o,(Xt(),cG))!=null&&(!o.a&&(o.a=new pe(Jt,o,10,11)),!!o.a)&&(!o.a&&(o.a=new pe(Jt,o,10,11)),o.a).i>0?(i=u(ye(o,cG),521),p=i.Sg(o),b=u(ye(o,o1),104),ww(o,k.Math.max(o.g,p.a+b.b+b.c),k.Math.max(o.f,p.b+b.d+b.a))):(!o.a&&(o.a=new pe(Jt,o,10,11)),o.a).i!=0&&ww(o,ne(re(ye(o,Im))),ne(re(ye(o,Im)))/ne(re(ye(o,B7))));t=u(ye(n,(Xt(),Dm)),144),h=u(JT(t.f),214);try{h.kf(n,new a0),Wfe(t.f,h)}catch(y){throw y=sr(y),X(y,101)?(f=y,$(f)):$(y)}return ji(n,zy,i7),dPe(n),oZ(n),c=ne(re(ye(n,Tm))),r=ne(re(ye(n,Cm))),new je(c,r)},yt(Oo,"TopdownSizeApproximator/4",972,KD,null,null);var dan;m(345,1,{852:1},i4),s.Tg=function(n,t){return aGe(this,n,t)},s.Ug=function(){$Ge(this)},s.Vg=function(){return this.q},s.Wg=function(){return this.f?OR(this.f):null},s.Xg=function(){return OR(this.a)},s.Yg=function(){return this.p},s.Zg=function(){return!1},s.$g=function(){return this.n},s._g=function(){return this.p!=null&&!this.b},s.ah=function(n){var t;this.n&&(t=n,Ce(this.f,t))},s.bh=function(n,t){var i,r;this.n&&n&&Tyn(this,(i=new hIe,r=zW(i,n),x$n(i),r),(PB(),Dce))},s.dh=function(n){var t;return this.b?null:(t=w8n(this,this.g),Vt(this.a,t),t.i=this,this.d=n,t)},s.eh=function(n){n>0&&!this.b&&Vhe(this,n)},s.b=!1,s.c=0,s.d=-1,s.e=null,s.f=null,s.g=-1,s.j=!1,s.k=!1,s.n=!1,s.o=0,s.q=0,s.r=0,v(Ru,"BasicProgressMonitor",345),m(706,214,Qw,hL),s.kf=function(n,t){kKe(n,t)},v(Ru,"BoxLayoutProvider",706),m(965,1,Yt,ZEe),s.Le=function(n,t){return xNn(this,u(n,26),u(t,26))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},s.a=!1,v(Ru,"BoxLayoutProvider/1",965),m(167,1,{167:1},dB,OOe),s.Ib=function(){return this.c?Jbe(this.c):Fa(this.b)},v(Ru,"BoxLayoutProvider/Group",167),m(326,23,{3:1,35:1,23:1,326:1},S$);var M8e,C8e,T8e,Nce,O8e=yt(Ru,"BoxLayoutProvider/PackingMode",326,Tt,w6n,y3n),ban;m(966,1,Yt,XM),s.Le=function(n,t){return L5n(u(n,167),u(t,167))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ru,"BoxLayoutProvider/lambda$0$Type",966),m(967,1,Yt,Bk),s.Le=function(n,t){return x5n(u(n,167),u(t,167))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ru,"BoxLayoutProvider/lambda$1$Type",967),m(968,1,Yt,KM),s.Le=function(n,t){return A5n(u(n,167),u(t,167))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(Ru,"BoxLayoutProvider/lambda$2$Type",968),m(1338,1,{829:1},dL),s.Lg=function(n,t){return WP(),!X(t,174)||DAe((J4(),u(n,174)),t)},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$0$Type",1338),m(1339,1,it,eSe),s.Ad=function(n){Ckn(this.a,u(n,147))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$1$Type",1339),m(1340,1,it,VM),s.Ad=function(n){u(n,105),WP()},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$2$Type",1340),m(1344,1,it,nSe),s.Ad=function(n){e7n(this.a,u(n,105))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$3$Type",1344),m(1342,1,Ft,MCe),s.Mb=function(n){return fkn(this.a,this.b,u(n,147))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$4$Type",1342),m(1341,1,Ft,CCe),s.Mb=function(n){return Spn(this.a,this.b,u(n,829))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$5$Type",1341),m(1343,1,it,TCe),s.Ad=function(n){Evn(this.a,this.b,u(n,147))},v(Ru,"ElkSpacings/AbstractSpacingsBuilder/lambda$6$Type",1343),m(930,1,{},bL),s.Kb=function(n){return STe(n)},s.Fb=function(n){return this===n},v(Ru,"ElkUtil/lambda$0$Type",930),m(931,1,it,OCe),s.Ad=function(n){TTn(this.a,this.b,u(n,85))},s.a=0,s.b=0,v(Ru,"ElkUtil/lambda$1$Type",931),m(932,1,it,NCe),s.Ad=function(n){xbn(this.a,this.b,u(n,170))},s.a=0,s.b=0,v(Ru,"ElkUtil/lambda$2$Type",932),m(933,1,it,DCe),s.Ad=function(n){vwn(this.a,this.b,u(n,157))},s.a=0,s.b=0,v(Ru,"ElkUtil/lambda$3$Type",933),m(934,1,it,tSe),s.Ad=function(n){U3n(this.a,u(n,372))},v(Ru,"ElkUtil/lambda$4$Type",934),m(331,1,{35:1,331:1},ebn),s.Dd=function(n){return Gwn(this,u(n,242))},s.Fb=function(n){var t;return X(n,331)?(t=u(n,331),this.a==t.a):!1},s.Hb=function(){return sc(this.a)},s.Ib=function(){return this.a+" (exclusive)"},s.a=0,v(Ru,"ExclusiveBounds/ExclusiveLowerBound",331),m(1088,214,Qw,rw),s.kf=function(n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q,V,te,be,fe,Ie,cn,tn;for(t.Tg("Fixed Layout",1),o=u(ye(n,(Xt(),j9e)),222),y=0,S=0,V=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));V.e!=V.i.gc();){for(R=u(ft(V),26),tn=u(ye(R,($B(),Yx)),8),tn&&(Dl(R,tn.a,tn.b),u(ye(R,f8e),182).Gc((Vs(),Lm))&&(A=u(ye(R,h8e),8),A.a>0&&A.b>0&&Xw(R,A.a,A.b,!0,!0))),y=k.Math.max(y,R.i+R.g),S=k.Math.max(S,R.j+R.f),b=new ut((!R.n&&(R.n=new pe(ju,R,1,7)),R.n));b.e!=b.i.gc();)f=u(ft(b),157),tn=u(ye(f,Yx),8),tn&&Dl(f,tn.a,tn.b),y=k.Math.max(y,R.i+f.i+f.g),S=k.Math.max(S,R.j+f.j+f.f);for(fe=new ut((!R.c&&(R.c=new pe($s,R,9,9)),R.c));fe.e!=fe.i.gc();)for(be=u(ft(fe),125),tn=u(ye(be,Yx),8),tn&&Dl(be,tn.a,tn.b),Ie=R.i+be.i,cn=R.j+be.j,y=k.Math.max(y,Ie+be.g),S=k.Math.max(S,cn+be.f),h=new ut((!be.n&&(be.n=new pe(ju,be,1,7)),be.n));h.e!=h.i.gc();)f=u(ft(h),157),tn=u(ye(f,Yx),8),tn&&Dl(f,tn.a,tn.b),y=k.Math.max(y,Ie+f.i+f.g),S=k.Math.max(S,cn+f.j+f.f);for(c=new qn(Vn(H0(R).a.Jc(),new ee));ht(c);)i=u(tt(c),85),p=IVe(i),y=k.Math.max(y,p.a),S=k.Math.max(S,p.b);for(r=new qn(Vn(AW(R).a.Jc(),new ee));ht(r);)i=u(tt(r),85),Bi(hW(i))!=n&&(p=IVe(i),y=k.Math.max(y,p.a),S=k.Math.max(S,p.b))}if(o==(z1(),H7))for(q=new ut((!n.a&&(n.a=new pe(Jt,n,10,11)),n.a));q.e!=q.i.gc();)for(R=u(ft(q),26),r=new qn(Vn(H0(R).a.Jc(),new ee));ht(r);)i=u(tt(r),85),l=x_n(i),l.b==0?ji(i,Kv,null):ji(i,Kv,l);Re($e(ye(n,($B(),a8e))))||(te=u(ye(n,Yfn),104),I=y+te.b+te.c,O=S+te.d+te.a,Xw(n,I,O,!0,!0)),t.Ug()},v(Ru,"FixedLayoutProvider",1088),m(379,150,{3:1,414:1,379:1,105:1,150:1},R6,kRe),s.ag=function(n){var t,i,r,c,o,l,f,h,b;if(n)try{for(h=K2(n,";,;"),o=h,l=0,f=o.length;l>16&yr|t^r<<16},s.Jc=function(){return new iSe(this)},s.Ib=function(){return this.a==null&&this.b==null?"pair(null,null)":this.a==null?"pair(null,"+su(this.b)+")":this.b==null?"pair("+su(this.a)+",null)":"pair("+su(this.a)+","+su(this.b)+")"},v(Ru,"Pair",49),m(979,1,Fr,iSe),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return!this.c&&(!this.b&&this.a.a!=null||this.a.b!=null)},s.Pb=function(){if(!this.c&&!this.b&&this.a.a!=null)return this.b=!0,this.a.a;if(!this.c&&this.a.b!=null)return this.c=!0,this.a.b;throw $(new au)},s.Qb=function(){throw this.c&&this.a.b!=null?this.a.b=null:this.b&&this.a.a!=null&&(this.a.a=null),$(new is)},s.b=!1,s.c=!1,v(Ru,"Pair/1",979),m(1078,214,Qw,YM),s.kf=function(n,t){var i,r,c,o,l;if(t.Tg("Random Layout",1),(!n.a&&(n.a=new pe(Jt,n,10,11)),n.a).i==0){t.Ug();return}o=u(ye(n,(bde(),oan)),15),o&&o.a!=0?c=new XR(o.a):c=new vQ,i=VC(re(ye(n,can))),l=VC(re(ye(n,san))),r=u(ye(n,uan),104),U$n(n,c,i,l,r),t.Ug()},v(Ru,"RandomLayoutProvider",1078),m(240,1,{240:1},ZK),s.Fb=function(n){return Ku(this.a,u(n,240).a)&&Ku(this.b,u(n,240).b)&&Ku(this.c,u(n,240).c)},s.Hb=function(){return RB(z(B(Ar,1),On,1,5,[this.a,this.b,this.c]))},s.Ib=function(){return"("+this.a+To+this.b+To+this.c+")"},v(Ru,"Triple",240);var man;m(550,1,{}),s.Jf=function(){return new je(this.f.i,this.f.j)},s.mf=function(n){return y_e(n,(Xt(),Ps))?ye(this.f,van):ye(this.f,n)},s.Kf=function(){return new je(this.f.g,this.f.f)},s.Lf=function(){return this.g},s.nf=function(n){return da(this.f,n)},s.Mf=function(n){Os(this.f,n.a),Ns(this.f,n.b)},s.Nf=function(n){Iw(this.f,n.a),Dw(this.f,n.b)},s.Of=function(n){this.g=n},s.g=0;var van;v(IS,"ElkGraphAdapters/AbstractElkGraphElementAdapter",550),m(552,1,{837:1},TP),s.Pf=function(){var n,t;if(!this.b)for(this.b=zR(OV(this.a).i),t=new ut(OV(this.a));t.e!=t.i.gc();)n=u(ft(t),157),Ce(this.b,new AX(n));return this.b},s.b=null,v(IS,"ElkGraphAdapters/ElkEdgeAdapter",552),m(260,550,{},w0),s.Qf=function(){return mHe(this)},s.a=null,v(IS,"ElkGraphAdapters/ElkGraphAdapter",260),m(630,550,{187:1},AX),v(IS,"ElkGraphAdapters/ElkLabelAdapter",630),m(551,550,{685:1},H$),s.Pf=function(){return ZSn(this)},s.Tf=function(){var n;return n=u(ye(this.f,(Xt(),R7)),140),!n&&(n=new wj),n},s.Vf=function(){return exn(this)},s.Xf=function(n){var t;t=new YK(n),ji(this.f,(Xt(),R7),t)},s.Yf=function(n){ji(this.f,(Xt(),o1),new Yle(n))},s.Rf=function(){return this.d},s.Sf=function(){var n,t;if(!this.a)for(this.a=new Te,t=new qn(Vn(AW(u(this.f,26)).a.Jc(),new ee));ht(t);)n=u(tt(t),85),Ce(this.a,new TP(n));return this.a},s.Uf=function(){var n,t;if(!this.c)for(this.c=new Te,t=new qn(Vn(H0(u(this.f,26)).a.Jc(),new ee));ht(t);)n=u(tt(t),85),Ce(this.c,new TP(n));return this.c},s.Wf=function(){return CR(u(this.f,26)).i!=0||Re($e(u(this.f,26).mf((Xt(),ID))))},s.Zf=function(){Q9n(this,(_b(),man))},s.a=null,s.b=null,s.c=null,s.d=null,s.e=null,v(IS,"ElkGraphAdapters/ElkNodeAdapter",551),m(1249,550,{836:1},rSe),s.Pf=function(){return oxn(this)},s.Sf=function(){var n,t;if(!this.a)for(this.a=Fh(u(this.f,125).gh().i),t=new ut(u(this.f,125).gh());t.e!=t.i.gc();)n=u(ft(t),85),Ce(this.a,new TP(n));return this.a},s.Uf=function(){var n,t;if(!this.c)for(this.c=Fh(u(this.f,125).hh().i),t=new ut(u(this.f,125).hh());t.e!=t.i.gc();)n=u(ft(t),85),Ce(this.c,new TP(n));return this.c},s.$f=function(){return u(u(this.f,125).mf((Xt(),Qv)),64)},s._f=function(){var n,t,i,r,c,o,l,f;for(r=Ia(u(this.f,125)),i=new ut(u(this.f,125).hh());i.e!=i.i.gc();)for(n=u(ft(i),85),f=new ut((!n.c&&(n.c=new Nn(vt,n,5,8)),n.c));f.e!=f.i.gc();){if(l=u(ft(f),84),T2(iu(l),r))return!0;if(iu(l)==r&&Re($e(ye(n,(Xt(),gce)))))return!0}for(t=new ut(u(this.f,125).gh());t.e!=t.i.gc();)for(n=u(ft(t),85),o=new ut((!n.b&&(n.b=new Nn(vt,n,4,7)),n.b));o.e!=o.i.gc();)if(c=u(ft(o),84),T2(iu(c),r))return!0;return!1},s.a=null,s.b=null,s.c=null,v(IS,"ElkGraphAdapters/ElkPortAdapter",1249),m(1250,1,Yt,wL),s.Le=function(n,t){return pIn(u(n,125),u(t,125))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(IS,"ElkGraphAdapters/PortComparator",1250);var wb=Hi(ql,"EObject"),U7=Hi(yv,FWe),kl=Hi(yv,JWe),VD=Hi(yv,HWe),YD=Hi(yv,"ElkShape"),vt=Hi(yv,GWe),pr=Hi(yv,P2e),Pi=Hi(yv,qWe),QD=Hi(ql,UWe),rA=Hi(ql,"EFactory"),yan,Ice=Hi(ql,XWe),xa=Hi(ql,"EPackage"),Pr,kan,jan,_8e,bG,Ean,L8e,P8e,$8e,a1,San,xan,ju=Hi(yv,$2e),Jt=Hi(yv,R2e),$s=Hi(yv,B2e);m(93,1,KWe),s.qh=function(){return this.rh(),null},s.rh=function(){return null},s.sh=function(){return this.rh(),!1},s.th=function(){return!1},s.uh=function(n){ai(this,n)},v(wy,"BasicNotifierImpl",93),m(100,93,WWe),s.Vh=function(){return Fs(this)},s.vh=function(n,t){return n},s.wh=function(){throw $(new _t)},s.xh=function(n){var t;return t=Oc(u(An(this.Ah(),this.Ch()),19)),this.Mh().Qh(this,t.n,t.f,n)},s.yh=function(n,t){throw $(new _t)},s.zh=function(n,t,i){return dl(this,n,t,i)},s.Ah=function(){var n;return this.wh()&&(n=this.wh().Lk(),n)?n:this.fi()},s.Bh=function(){return SW(this)},s.Ch=function(){throw $(new _t)},s.Dh=function(){var n,t;return t=this.Xh().Mk(),!t&&this.wh().Rk(t=(Nj(),n=dae(vh(this.Ah())),n==null?Fce:new jT(this,n))),t},s.Eh=function(n,t){return n},s.Fh=function(n){var t;return t=n.nk(),t?n.Jj():zi(this.Ah(),n)},s.Gh=function(){var n;return n=this.wh(),n?n.Ok():null},s.Hh=function(){return this.wh()?this.wh().Lk():null},s.Ih=function(n,t,i){return uz(this,n,t,i)},s.Jh=function(n){return F9(this,n)},s.Kh=function(n,t){return fY(this,n,t)},s.Lh=function(){var n;return n=this.wh(),!!n&&n.Pk()},s.Mh=function(){throw $(new _t)},s.Nh=function(){return nz(this)},s.Oh=function(n,t,i,r){return K4(this,n,t,r)},s.Ph=function(n,t,i){var r;return r=u(An(this.Ah(),t),69),r.uk().xk(this,this.ei(),t-this.gi(),n,i)},s.Qh=function(n,t,i,r){return IR(this,n,t,r)},s.Rh=function(n,t,i){var r;return r=u(An(this.Ah(),t),69),r.uk().yk(this,this.ei(),t-this.gi(),n,i)},s.Sh=function(){return!!this.wh()&&!!this.wh().Nk()},s.Th=function(n){return _Q(this,n)},s.Uh=function(n){return L_e(this,n)},s.Wh=function(n){return wVe(this,n)},s.Xh=function(){throw $(new _t)},s.Yh=function(){return this.wh()?this.wh().Nk():null},s.Zh=function(){return nz(this)},s.$h=function(n,t){vW(this,n,t)},s._h=function(n){this.Xh().Qk(n)},s.ai=function(n){this.Xh().Tk(n)},s.bi=function(n){this.Xh().Sk(n)},s.ci=function(n,t){var i,r,c,o;return o=this.Gh(),o&&n&&(t=vc(o.Cl(),this,t),o.Gl(this)),r=this.Mh(),r&&(($W(this,this.Mh(),this.Ch()).Bb&Ec)!=0?(c=r.Nh(),c&&(n?!o&&c.Gl(this):c.Fl(this))):(t=(i=this.Ch(),i>=0?this.xh(t):this.Mh().Qh(this,-1-i,null,t)),t=this.zh(null,-1,t))),this.ai(n),t},s.di=function(n){var t,i,r,c,o,l,f,h;if(i=this.Ah(),o=zi(i,n),t=this.gi(),o>=t)return u(n,69).uk().Bk(this,this.ei(),o-t);if(o<=-1)if(l=av((ls(),nc),i,n),l){if(Tc(),u(l,69).vk()||(l=D4(Vc(nc,l))),c=(r=this.Fh(l),u(r>=0?this.Ih(r,!0,!0):Gw(this,l,!0),163)),h=l.Gk(),h>1||h==-1)return u(u(c,219).Ql(n,!1),77)}else throw $(new Gn(W0+n.ve()+wne));else if(n.Hk())return r=this.Fh(n),u(r>=0?this.Ih(r,!1,!0):Gw(this,n,!1),77);return f=new KCe(this,n),f},s.ei=function(){return khe(this)},s.fi=function(){return(x0(),Rn).S},s.gi=function(){return dt(this.fi())},s.hi=function(n){wW(this,n)},s.Ib=function(){return Jf(this)},v(Fn,"BasicEObjectImpl",100);var Aan;m(117,100,{109:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1}),s.ii=function(n){var t;return t=jhe(this),t[n]},s.ji=function(n,t){var i;i=jhe(this),tr(i,n,t)},s.ki=function(n){var t;t=jhe(this),tr(t,n,null)},s.qh=function(){return u(Un(this,4),129)},s.rh=function(){throw $(new _t)},s.sh=function(){return(this.Db&4)!=0},s.wh=function(){throw $(new _t)},s.li=function(n){U4(this,2,n)},s.yh=function(n,t){this.Db=t<<16|this.Db&255,this.li(n)},s.Ah=function(){return Go(this)},s.Ch=function(){return this.Db>>16},s.Dh=function(){var n,t;return Nj(),t=dae(vh((n=u(Un(this,16),29),n||this.fi()))),t==null?Fce:new jT(this,t)},s.th=function(){return(this.Db&1)==0},s.Gh=function(){return u(Un(this,128),1996)},s.Hh=function(){return u(Un(this,16),29)},s.Lh=function(){return(this.Db&32)!=0},s.Mh=function(){return u(Un(this,2),52)},s.Sh=function(){return(this.Db&64)!=0},s.Xh=function(){throw $(new _t)},s.Yh=function(){return u(Un(this,64),290)},s._h=function(n){U4(this,16,n)},s.ai=function(n){U4(this,128,n)},s.bi=function(n){U4(this,64,n)},s.ei=function(){return Lo(this)},s.Db=0,v(Fn,"MinimalEObjectImpl",117),m(118,117,{109:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.li=function(n){this.Cb=n},s.Mh=function(){return this.Cb},v(Fn,"MinimalEObjectImpl/Container",118),m(2045,118,{109:1,343:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return $de(this,n,t,i)},s.Rh=function(n,t,i){return A0e(this,n,t,i)},s.Th=function(n){return Oae(this,n)},s.$h=function(n,t){E1e(this,n,t)},s.fi=function(){return Gu(),xan},s.hi=function(n){f1e(this,n)},s.lf=function(){return RJe(this)},s.fh=function(){return!this.o&&(this.o=new os((Gu(),a1),Qd,this,0)),this.o},s.mf=function(n){return ye(this,n)},s.nf=function(n){return da(this,n)},s.of=function(n,t){return ji(this,n,t)},v(hg,"EMapPropertyHolderImpl",2045),m(559,118,{109:1,372:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},Fk),s.Ih=function(n,t,i){switch(n){case 0:return this.a;case 1:return this.b}return uz(this,n,t,i)},s.Th=function(n){switch(n){case 0:return this.a!=0;case 1:return this.b!=0}return _Q(this,n)},s.$h=function(n,t){switch(n){case 0:bB(this,ne(re(t)));return;case 1:gB(this,ne(re(t)));return}vW(this,n,t)},s.fi=function(){return Gu(),kan},s.hi=function(n){switch(n){case 0:bB(this,0);return;case 1:gB(this,0);return}wW(this,n)},s.Ib=function(){var n;return(this.Db&64)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (x: ",S3(n,this.a),n.a+=", y: ",S3(n,this.b),n.a+=")",n.a)},s.a=0,s.b=0,v(hg,"ElkBendPointImpl",559),m(727,2045,{109:1,343:1,174:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return J1e(this,n,t,i)},s.Ph=function(n,t,i){return sW(this,n,t,i)},s.Rh=function(n,t,i){return XY(this,n,t,i)},s.Th=function(n){return r1e(this,n)},s.$h=function(n,t){t0e(this,n,t)},s.fi=function(){return Gu(),Ean},s.hi=function(n){R1e(this,n)},s.ih=function(){return this.k},s.jh=function(){return OV(this)},s.Ib=function(){return mQ(this)},s.k=null,v(hg,"ElkGraphElementImpl",727),m(728,727,{109:1,343:1,174:1,276:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return nde(this,n,t,i)},s.Th=function(n){return sde(this,n)},s.$h=function(n,t){i0e(this,n,t)},s.fi=function(){return Gu(),San},s.hi=function(n){dde(this,n)},s.kh=function(){return this.f},s.lh=function(){return this.g},s.mh=function(){return this.i},s.nh=function(){return this.j},s.oh=function(n,t){ww(this,n,t)},s.ph=function(n,t){Dl(this,n,t)},s.Ib=function(){return bW(this)},s.f=0,s.g=0,s.i=0,s.j=0,v(hg,"ElkShapeImpl",728),m(729,728,{109:1,343:1,84:1,174:1,276:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1}),s.Ih=function(n,t,i){return Nde(this,n,t,i)},s.Ph=function(n,t,i){return Yde(this,n,t,i)},s.Rh=function(n,t,i){return Qde(this,n,t,i)},s.Th=function(n){return v1e(this,n)},s.$h=function(n,t){fbe(this,n,t)},s.fi=function(){return Gu(),jan},s.hi=function(n){Ade(this,n)},s.gh=function(){return!this.d&&(this.d=new Nn(pr,this,8,5)),this.d},s.hh=function(){return!this.e&&(this.e=new Nn(pr,this,7,4)),this.e},v(hg,"ElkConnectableShapeImpl",729),m(271,727,{109:1,343:1,85:1,174:1,271:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},w3),s.xh=function(n){return Ude(this,n)},s.Ih=function(n,t,i){switch(n){case 3:return j2(this);case 4:return!this.b&&(this.b=new Nn(vt,this,4,7)),this.b;case 5:return!this.c&&(this.c=new Nn(vt,this,5,8)),this.c;case 6:return!this.a&&(this.a=new pe(Pi,this,6,6)),this.a;case 7:return Pn(),!this.b&&(this.b=new Nn(vt,this,4,7)),!(this.b.i<=1&&(!this.c&&(this.c=new Nn(vt,this,5,8)),this.c.i<=1));case 8:return Pn(),!!ZE(this);case 9:return Pn(),!!Hw(this);case 10:return Pn(),!this.b&&(this.b=new Nn(vt,this,4,7)),this.b.i!=0&&(!this.c&&(this.c=new Nn(vt,this,5,8)),this.c.i!=0)}return J1e(this,n,t,i)},s.Ph=function(n,t,i){var r;switch(t){case 3:return this.Cb&&(i=(r=this.Db>>16,r>=0?Ude(this,i):this.Cb.Qh(this,-1-r,null,i))),Cle(this,u(n,26),i);case 4:return!this.b&&(this.b=new Nn(vt,this,4,7)),Co(this.b,n,i);case 5:return!this.c&&(this.c=new Nn(vt,this,5,8)),Co(this.c,n,i);case 6:return!this.a&&(this.a=new pe(Pi,this,6,6)),Co(this.a,n,i)}return sW(this,n,t,i)},s.Rh=function(n,t,i){switch(t){case 3:return Cle(this,null,i);case 4:return!this.b&&(this.b=new Nn(vt,this,4,7)),vc(this.b,n,i);case 5:return!this.c&&(this.c=new Nn(vt,this,5,8)),vc(this.c,n,i);case 6:return!this.a&&(this.a=new pe(Pi,this,6,6)),vc(this.a,n,i)}return XY(this,n,t,i)},s.Th=function(n){switch(n){case 3:return!!j2(this);case 4:return!!this.b&&this.b.i!=0;case 5:return!!this.c&&this.c.i!=0;case 6:return!!this.a&&this.a.i!=0;case 7:return!this.b&&(this.b=new Nn(vt,this,4,7)),!(this.b.i<=1&&(!this.c&&(this.c=new Nn(vt,this,5,8)),this.c.i<=1));case 8:return ZE(this);case 9:return Hw(this);case 10:return!this.b&&(this.b=new Nn(vt,this,4,7)),this.b.i!=0&&(!this.c&&(this.c=new Nn(vt,this,5,8)),this.c.i!=0)}return r1e(this,n)},s.$h=function(n,t){switch(n){case 3:Dz(this,u(t,26));return;case 4:!this.b&&(this.b=new Nn(vt,this,4,7)),kt(this.b),!this.b&&(this.b=new Nn(vt,this,4,7)),er(this.b,u(t,18));return;case 5:!this.c&&(this.c=new Nn(vt,this,5,8)),kt(this.c),!this.c&&(this.c=new Nn(vt,this,5,8)),er(this.c,u(t,18));return;case 6:!this.a&&(this.a=new pe(Pi,this,6,6)),kt(this.a),!this.a&&(this.a=new pe(Pi,this,6,6)),er(this.a,u(t,18));return}t0e(this,n,t)},s.fi=function(){return Gu(),_8e},s.hi=function(n){switch(n){case 3:Dz(this,null);return;case 4:!this.b&&(this.b=new Nn(vt,this,4,7)),kt(this.b);return;case 5:!this.c&&(this.c=new Nn(vt,this,5,8)),kt(this.c);return;case 6:!this.a&&(this.a=new pe(Pi,this,6,6)),kt(this.a);return}R1e(this,n)},s.Ib=function(){return $Ke(this)},v(hg,"ElkEdgeImpl",271),m(443,2045,{109:1,343:1,170:1,443:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},yo),s.xh=function(n){return Jde(this,n)},s.Ih=function(n,t,i){switch(n){case 1:return this.j;case 2:return this.k;case 3:return this.b;case 4:return this.c;case 5:return!this.a&&(this.a=new mr(kl,this,5)),this.a;case 6:return __e(this);case 7:return t?BQ(this):this.i;case 8:return t?RQ(this):this.f;case 9:return!this.g&&(this.g=new Nn(Pi,this,9,10)),this.g;case 10:return!this.e&&(this.e=new Nn(Pi,this,10,9)),this.e;case 11:return this.d}return $de(this,n,t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 6:return this.Cb&&(i=(c=this.Db>>16,c>=0?Jde(this,i):this.Cb.Qh(this,-1-c,null,i))),Tle(this,u(n,85),i);case 9:return!this.g&&(this.g=new Nn(Pi,this,9,10)),Co(this.g,n,i);case 10:return!this.e&&(this.e=new Nn(Pi,this,10,9)),Co(this.e,n,i)}return o=u(An((r=u(Un(this,16),29),r||(Gu(),bG)),t),69),o.uk().xk(this,Lo(this),t-dt((Gu(),bG)),n,i)},s.Rh=function(n,t,i){switch(t){case 5:return!this.a&&(this.a=new mr(kl,this,5)),vc(this.a,n,i);case 6:return Tle(this,null,i);case 9:return!this.g&&(this.g=new Nn(Pi,this,9,10)),vc(this.g,n,i);case 10:return!this.e&&(this.e=new Nn(Pi,this,10,9)),vc(this.e,n,i)}return A0e(this,n,t,i)},s.Th=function(n){switch(n){case 1:return this.j!=0;case 2:return this.k!=0;case 3:return this.b!=0;case 4:return this.c!=0;case 5:return!!this.a&&this.a.i!=0;case 6:return!!__e(this);case 7:return!!this.i;case 8:return!!this.f;case 9:return!!this.g&&this.g.i!=0;case 10:return!!this.e&&this.e.i!=0;case 11:return this.d!=null}return Oae(this,n)},s.$h=function(n,t){switch(n){case 1:V3(this,ne(re(t)));return;case 2:Y3(this,ne(re(t)));return;case 3:X3(this,ne(re(t)));return;case 4:K3(this,ne(re(t)));return;case 5:!this.a&&(this.a=new mr(kl,this,5)),kt(this.a),!this.a&&(this.a=new mr(kl,this,5)),er(this.a,u(t,18));return;case 6:PUe(this,u(t,85));return;case 7:jB(this,u(t,84));return;case 8:kB(this,u(t,84));return;case 9:!this.g&&(this.g=new Nn(Pi,this,9,10)),kt(this.g),!this.g&&(this.g=new Nn(Pi,this,9,10)),er(this.g,u(t,18));return;case 10:!this.e&&(this.e=new Nn(Pi,this,10,9)),kt(this.e),!this.e&&(this.e=new Nn(Pi,this,10,9)),er(this.e,u(t,18));return;case 11:Xhe(this,Pt(t));return}E1e(this,n,t)},s.fi=function(){return Gu(),bG},s.hi=function(n){switch(n){case 1:V3(this,0);return;case 2:Y3(this,0);return;case 3:X3(this,0);return;case 4:K3(this,0);return;case 5:!this.a&&(this.a=new mr(kl,this,5)),kt(this.a);return;case 6:PUe(this,null);return;case 7:jB(this,null);return;case 8:kB(this,null);return;case 9:!this.g&&(this.g=new Nn(Pi,this,9,10)),kt(this.g);return;case 10:!this.e&&(this.e=new Nn(Pi,this,10,9)),kt(this.e);return;case 11:Xhe(this,null);return}f1e(this,n)},s.Ib=function(){return Xqe(this)},s.b=0,s.c=0,s.d=null,s.j=0,s.k=0,v(hg,"ElkEdgeSectionImpl",443),m(161,118,{109:1,94:1,93:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1}),s.Ih=function(n,t,i){var r;return n==0?(!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab):Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Ph=function(n,t,i){var r,c;return t==0?(!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),Co(this.Ab,n,i)):(c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().xk(this,Lo(this),t-dt(this.fi()),n,i))},s.Rh=function(n,t,i){var r,c;return t==0?(!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i)):(c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,Lo(this),t-dt(this.fi()),n,i))},s.Th=function(n){var t;return n==0?!!this.Ab&&this.Ab.i!=0:Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.Wh=function(n){return Cge(this,n)},s.$h=function(n,t){var i;if(n===0){!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.ai=function(n){U4(this,128,n)},s.fi=function(){return yn(),Gan},s.hi=function(n){var t;if(n===0){!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.mi=function(){this.Bb|=1},s.ni=function(n){return uS(this,n)},s.Bb=0,v(Fn,"EModelElementImpl",161),m(710,161,{109:1,94:1,93:1,469:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1},OC),s.oi=function(n,t){return lVe(this,n,t)},s.pi=function(n){var t,i,r,c,o;if(this.a!=sl(n)||(n.Bb&256)!=0)throw $(new Gn(mne+n.zb+ip));for(r=tu(n);Vu(r.a).i!=0;){if(i=u(cN(r,0,(t=u(K(Vu(r.a),0),87),o=t.c,X(o,88)?u(o,29):(yn(),jf))),29),Fw(i))return c=sl(i).ti().pi(i),u(c,52)._h(n),c;r=tu(i)}return(n.D!=null?n.D:n.B)=="java.util.Map$Entry"?new bDe(n):new bfe(n)},s.qi=function(n,t){return Kw(this,n,t)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.a}return Pl(this,n-dt((yn(),vb)),An((r=u(Un(this,16),29),r||vb),n),t,i)},s.Ph=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),Co(this.Ab,n,i);case 1:return this.a&&(i=u(this.a,52).Qh(this,4,xa,i)),P1e(this,u(n,241),i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),vb)),t),69),c.uk().xk(this,Lo(this),t-dt((yn(),vb)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 1:return P1e(this,null,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),vb)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),vb)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return!!this.a}return Ll(this,n-dt((yn(),vb)),An((t=u(Un(this,16),29),t||vb),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:EGe(this,u(t,241));return}Jl(this,n-dt((yn(),vb)),An((i=u(Un(this,16),29),i||vb),n),t)},s.fi=function(){return yn(),vb},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:EGe(this,null);return}Fl(this,n-dt((yn(),vb)),An((t=u(Un(this,16),29),t||vb),n))};var cA,R8e,Man;v(Fn,"EFactoryImpl",710),m(1018,710,{109:1,2075:1,94:1,93:1,469:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1},aU),s.oi=function(n,t){switch(n.fk()){case 12:return u(t,147).Og();case 13:return su(t);default:throw $(new Gn(r7+n.ve()+ip))}},s.pi=function(n){var t,i,r,c,o,l,f,h;switch(n.G==-1&&(n.G=(t=sl(n),t?Ld(t.si(),n):-1)),n.G){case 4:return o=new QM,o;case 6:return l=new pj,l;case 7:return f=new voe,f;case 8:return r=new w3,r;case 9:return i=new Fk,i;case 10:return c=new yo,c;case 11:return h=new B6,h;default:throw $(new Gn(mne+n.zb+ip))}},s.qi=function(n,t){switch(n.fk()){case 13:case 12:return null;default:throw $(new Gn(r7+n.ve()+ip))}},v(hg,"ElkGraphFactoryImpl",1018),m(439,161,{109:1,94:1,93:1,158:1,197:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1}),s.Dh=function(){var n,t;return t=(n=u(Un(this,16),29),dae(vh(n||this.fi()))),t==null?(Nj(),Nj(),Fce):new LOe(this,t)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.ve()}return Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null}return Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:this.ri(Pt(t));return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return yn(),qan},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:this.ri(null);return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.ve=function(){return this.zb},s.ri=function(n){Mo(this,n)},s.Ib=function(){return _E(this)},s.zb=null,v(Fn,"ENamedElementImpl",439),m(184,439,{109:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,52:1,100:1,161:1,184:1,117:1,118:1,680:1},c_e),s.xh=function(n){return _He(this,n)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return this.yb;case 3:return this.xb;case 4:return this.sb;case 5:return!this.rb&&(this.rb=new m2(this,Aa,this)),this.rb;case 6:return!this.vb&&(this.vb=new y4(xa,this,6,7)),this.vb;case 7:return t?this.Db>>16==7?u(this.Cb,241):null:F_e(this)}return Pl(this,n-dt((yn(),n0)),An((r=u(Un(this,16),29),r||n0),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),Co(this.Ab,n,i);case 4:return this.sb&&(i=u(this.sb,52).Qh(this,1,rA,i)),B1e(this,u(n,469),i);case 5:return!this.rb&&(this.rb=new m2(this,Aa,this)),Co(this.rb,n,i);case 6:return!this.vb&&(this.vb=new y4(xa,this,6,7)),Co(this.vb,n,i);case 7:return this.Cb&&(i=(c=this.Db>>16,c>=0?_He(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,7,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),n0)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),n0)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 4:return B1e(this,null,i);case 5:return!this.rb&&(this.rb=new m2(this,Aa,this)),vc(this.rb,n,i);case 6:return!this.vb&&(this.vb=new y4(xa,this,6,7)),vc(this.vb,n,i);case 7:return dl(this,null,7,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),n0)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),n0)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.yb!=null;case 3:return this.xb!=null;case 4:return!!this.sb;case 5:return!!this.rb&&this.rb.i!=0;case 6:return!!this.vb&&this.vb.i!=0;case 7:return!!F_e(this)}return Ll(this,n-dt((yn(),n0)),An((t=u(Un(this,16),29),t||n0),n))},s.Wh=function(n){var t;return t=LNn(this,n),t||Cge(this,n)},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Pt(t));return;case 2:CB(this,Pt(t));return;case 3:MB(this,Pt(t));return;case 4:dW(this,u(t,469));return;case 5:!this.rb&&(this.rb=new m2(this,Aa,this)),kt(this.rb),!this.rb&&(this.rb=new m2(this,Aa,this)),er(this.rb,u(t,18));return;case 6:!this.vb&&(this.vb=new y4(xa,this,6,7)),kt(this.vb),!this.vb&&(this.vb=new y4(xa,this,6,7)),er(this.vb,u(t,18));return}Jl(this,n-dt((yn(),n0)),An((i=u(Un(this,16),29),i||n0),n),t)},s.bi=function(n){var t,i;if(n&&this.rb)for(i=new ut(this.rb);i.e!=i.i.gc();)t=ft(i),X(t,360)&&(u(t,360).w=null);U4(this,64,n)},s.fi=function(){return yn(),n0},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Mo(this,null);return;case 2:CB(this,null);return;case 3:MB(this,null);return;case 4:dW(this,null);return;case 5:!this.rb&&(this.rb=new m2(this,Aa,this)),kt(this.rb);return;case 6:!this.vb&&(this.vb=new y4(xa,this,6,7)),kt(this.vb);return}Fl(this,n-dt((yn(),n0)),An((t=u(Un(this,16),29),t||n0),n))},s.mi=function(){WQ(this)},s.si=function(){return!this.rb&&(this.rb=new m2(this,Aa,this)),this.rb},s.ti=function(){return this.sb},s.ui=function(){return this.ub},s.vi=function(){return this.xb},s.wi=function(){return this.yb},s.xi=function(n){this.ub=n},s.Ib=function(){var n;return(this.Db&64)!=0?_E(this):(n=new cf(_E(this)),n.a+=" (nsURI: ",Bc(n,this.yb),n.a+=", nsPrefix: ",Bc(n,this.xb),n.a+=")",n.a)},s.xb=null,s.yb=null,v(Fn,"EPackageImpl",184),m(556,184,{109:1,2077:1,556:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,52:1,100:1,161:1,184:1,117:1,118:1,680:1},nUe),s.q=!1,s.r=!1;var Can=!1;v(hg,"ElkGraphPackageImpl",556),m(362,728,{109:1,343:1,174:1,157:1,276:1,362:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},QM),s.xh=function(n){return Hde(this,n)},s.Ih=function(n,t,i){switch(n){case 7:return vae(this);case 8:return this.a}return nde(this,n,t,i)},s.Ph=function(n,t,i){var r;return t===7?(this.Cb&&(i=(r=this.Db>>16,r>=0?Hde(this,i):this.Cb.Qh(this,-1-r,null,i))),Mfe(this,u(n,174),i)):sW(this,n,t,i)},s.Rh=function(n,t,i){return t==7?Mfe(this,null,i):XY(this,n,t,i)},s.Th=function(n){switch(n){case 7:return!!vae(this);case 8:return!bn("",this.a)}return sde(this,n)},s.$h=function(n,t){switch(n){case 7:Abe(this,u(t,174));return;case 8:Ghe(this,Pt(t));return}i0e(this,n,t)},s.fi=function(){return Gu(),L8e},s.hi=function(n){switch(n){case 7:Abe(this,null);return;case 8:Ghe(this,"");return}dde(this,n)},s.Ib=function(){return JGe(this)},s.a="",v(hg,"ElkLabelImpl",362),m(206,729,{109:1,343:1,84:1,174:1,26:1,276:1,206:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},pj),s.xh=function(n){return Xde(this,n)},s.Ih=function(n,t,i){switch(n){case 9:return!this.c&&(this.c=new pe($s,this,9,9)),this.c;case 10:return!this.a&&(this.a=new pe(Jt,this,10,11)),this.a;case 11:return Bi(this);case 12:return!this.b&&(this.b=new pe(pr,this,12,3)),this.b;case 13:return Pn(),!this.a&&(this.a=new pe(Jt,this,10,11)),this.a.i>0}return Nde(this,n,t,i)},s.Ph=function(n,t,i){var r;switch(t){case 9:return!this.c&&(this.c=new pe($s,this,9,9)),Co(this.c,n,i);case 10:return!this.a&&(this.a=new pe(Jt,this,10,11)),Co(this.a,n,i);case 11:return this.Cb&&(i=(r=this.Db>>16,r>=0?Xde(this,i):this.Cb.Qh(this,-1-r,null,i))),Gle(this,u(n,26),i);case 12:return!this.b&&(this.b=new pe(pr,this,12,3)),Co(this.b,n,i)}return Yde(this,n,t,i)},s.Rh=function(n,t,i){switch(t){case 9:return!this.c&&(this.c=new pe($s,this,9,9)),vc(this.c,n,i);case 10:return!this.a&&(this.a=new pe(Jt,this,10,11)),vc(this.a,n,i);case 11:return Gle(this,null,i);case 12:return!this.b&&(this.b=new pe(pr,this,12,3)),vc(this.b,n,i)}return Qde(this,n,t,i)},s.Th=function(n){switch(n){case 9:return!!this.c&&this.c.i!=0;case 10:return!!this.a&&this.a.i!=0;case 11:return!!Bi(this);case 12:return!!this.b&&this.b.i!=0;case 13:return!this.a&&(this.a=new pe(Jt,this,10,11)),this.a.i>0}return v1e(this,n)},s.$h=function(n,t){switch(n){case 9:!this.c&&(this.c=new pe($s,this,9,9)),kt(this.c),!this.c&&(this.c=new pe($s,this,9,9)),er(this.c,u(t,18));return;case 10:!this.a&&(this.a=new pe(Jt,this,10,11)),kt(this.a),!this.a&&(this.a=new pe(Jt,this,10,11)),er(this.a,u(t,18));return;case 11:Iz(this,u(t,26));return;case 12:!this.b&&(this.b=new pe(pr,this,12,3)),kt(this.b),!this.b&&(this.b=new pe(pr,this,12,3)),er(this.b,u(t,18));return}fbe(this,n,t)},s.fi=function(){return Gu(),P8e},s.hi=function(n){switch(n){case 9:!this.c&&(this.c=new pe($s,this,9,9)),kt(this.c);return;case 10:!this.a&&(this.a=new pe(Jt,this,10,11)),kt(this.a);return;case 11:Iz(this,null);return;case 12:!this.b&&(this.b=new pe(pr,this,12,3)),kt(this.b);return}Ade(this,n)},s.Ib=function(){return Jbe(this)},v(hg,"ElkNodeImpl",206),m(193,729,{109:1,343:1,84:1,174:1,125:1,276:1,193:1,105:1,94:1,93:1,57:1,114:1,52:1,100:1,117:1,118:1},voe),s.xh=function(n){return Gde(this,n)},s.Ih=function(n,t,i){return n==9?Ia(this):Nde(this,n,t,i)},s.Ph=function(n,t,i){var r;return t===9?(this.Cb&&(i=(r=this.Db>>16,r>=0?Gde(this,i):this.Cb.Qh(this,-1-r,null,i))),Ole(this,u(n,26),i)):Yde(this,n,t,i)},s.Rh=function(n,t,i){return t==9?Ole(this,null,i):Qde(this,n,t,i)},s.Th=function(n){return n==9?!!Ia(this):v1e(this,n)},s.$h=function(n,t){if(n===9){kbe(this,u(t,26));return}fbe(this,n,t)},s.fi=function(){return Gu(),$8e},s.hi=function(n){if(n===9){kbe(this,null);return}Ade(this,n)},s.Ib=function(){return _Xe(this)},v(hg,"ElkPortImpl",193);var Tan=Hi(yc,"BasicEMap/Entry");m(1091,118,{109:1,45:1,94:1,93:1,136:1,57:1,114:1,52:1,100:1,117:1,118:1},B6),s.Fb=function(n){return this===n},s.jd=function(){return this.b},s.Hb=function(){return vw(this)},s.Ai=function(n){zhe(this,u(n,147))},s.Ih=function(n,t,i){switch(n){case 0:return this.b;case 1:return this.c}return uz(this,n,t,i)},s.Th=function(n){switch(n){case 0:return!!this.b;case 1:return this.c!=null}return _Q(this,n)},s.$h=function(n,t){switch(n){case 0:zhe(this,u(t,147));return;case 1:Fhe(this,t);return}vW(this,n,t)},s.fi=function(){return Gu(),a1},s.hi=function(n){switch(n){case 0:zhe(this,null);return;case 1:Fhe(this,null);return}wW(this,n)},s.yi=function(){var n;return this.a==-1&&(n=this.b,this.a=n?Oi(n):0),this.a},s.kd=function(){return this.c},s.zi=function(n){this.a=n},s.ld=function(n){var t;return t=this.c,Fhe(this,n),t},s.Ib=function(){var n;return(this.Db&64)!=0?Jf(this):(n=new p0,Kt(Kt(Kt(n,this.b?this.b.Og():Vo),eee),Qj(this.c)),n.a)},s.a=-1,s.c=null;var Qd=v(hg,"ElkPropertyToValueMapEntryImpl",1091);m(980,1,{},Hp),v(Qr,"JsonAdapter",980),m(215,63,H1,oh),v(Qr,"JsonImportException",215),m(850,1,{},Yqe),v(Qr,"JsonImporter",850),m(884,1,{},ICe),s.Bi=function(n){GHe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$0$Type",884),m(885,1,{},_Ce),s.Bi=function(n){Mqe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$1$Type",885),m(893,1,{},cSe),s.Bi=function(n){BIe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$10$Type",893),m(895,1,{},LCe),s.Bi=function(n){bqe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$11$Type",895),m(896,1,{},PCe),s.Bi=function(n){gqe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$12$Type",896),m(902,1,{},VIe),s.Bi=function(n){RGe(this.a,this.b,this.c,this.d,u(n,139))},v(Qr,"JsonImporter/lambda$13$Type",902),m(901,1,{},YIe),s.Bi=function(n){nKe(this.a,this.b,this.c,this.d,u(n,149))},v(Qr,"JsonImporter/lambda$14$Type",901),m(897,1,{},$Ce),s.Bi=function(n){aNe(this.a,this.b,Pt(n))},v(Qr,"JsonImporter/lambda$15$Type",897),m(898,1,{},RCe),s.Bi=function(n){hNe(this.a,this.b,Pt(n))},v(Qr,"JsonImporter/lambda$16$Type",898),m(899,1,{},BCe),s.Bi=function(n){xHe(this.b,this.a,u(n,139))},v(Qr,"JsonImporter/lambda$17$Type",899),m(900,1,{},zCe),s.Bi=function(n){AHe(this.b,this.a,u(n,139))},v(Qr,"JsonImporter/lambda$18$Type",900),m(905,1,{},uSe),s.Bi=function(n){TGe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$19$Type",905),m(886,1,{},oSe),s.Bi=function(n){$He(this.a,u(n,139))},v(Qr,"JsonImporter/lambda$2$Type",886),m(903,1,{},sSe),s.Bi=function(n){V3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$20$Type",903),m(904,1,{},lSe),s.Bi=function(n){Y3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$21$Type",904),m(908,1,{},fSe),s.Bi=function(n){CGe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$22$Type",908),m(906,1,{},aSe),s.Bi=function(n){X3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$23$Type",906),m(907,1,{},hSe),s.Bi=function(n){K3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$24$Type",907),m(910,1,{},dSe),s.Bi=function(n){nGe(this.a,u(n,139))},v(Qr,"JsonImporter/lambda$25$Type",910),m(909,1,{},bSe),s.Bi=function(n){zIe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$26$Type",909),m(911,1,it,FCe),s.Ad=function(n){L9n(this.b,this.a,Pt(n))},v(Qr,"JsonImporter/lambda$27$Type",911),m(912,1,it,JCe),s.Ad=function(n){P9n(this.b,this.a,Pt(n))},v(Qr,"JsonImporter/lambda$28$Type",912),m(913,1,{},HCe),s.Bi=function(n){fUe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$29$Type",913),m(889,1,{},gSe),s.Bi=function(n){VFe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$3$Type",889),m(914,1,{},GCe),s.Bi=function(n){DUe(this.a,this.b,u(n,139))},v(Qr,"JsonImporter/lambda$30$Type",914),m(915,1,{},wSe),s.Bi=function(n){pRe(this.a,re(n))},v(Qr,"JsonImporter/lambda$31$Type",915),m(916,1,{},pSe),s.Bi=function(n){mRe(this.a,re(n))},v(Qr,"JsonImporter/lambda$32$Type",916),m(917,1,{},mSe),s.Bi=function(n){vRe(this.a,re(n))},v(Qr,"JsonImporter/lambda$33$Type",917),m(918,1,{},vSe),s.Bi=function(n){yRe(this.a,re(n))},v(Qr,"JsonImporter/lambda$34$Type",918),m(919,1,{},ySe),s.Bi=function(n){DMn(this.a,u(n,57))},v(Qr,"JsonImporter/lambda$35$Type",919),m(920,1,{},kSe),s.Bi=function(n){IMn(this.a,u(n,57))},v(Qr,"JsonImporter/lambda$36$Type",920),m(924,1,{},KIe),v(Qr,"JsonImporter/lambda$37$Type",924),m(921,1,it,GNe),s.Ad=function(n){s7n(this.a,this.c,this.b,u(n,372))},v(Qr,"JsonImporter/lambda$38$Type",921),m(922,1,it,qCe),s.Ad=function(n){Xgn(this.a,this.b,u(n,170))},v(Qr,"JsonImporter/lambda$39$Type",922),m(887,1,{},jSe),s.Bi=function(n){V3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$4$Type",887),m(923,1,it,UCe),s.Ad=function(n){Kgn(this.a,this.b,u(n,170))},v(Qr,"JsonImporter/lambda$40$Type",923),m(925,1,it,qNe),s.Ad=function(n){l7n(this.a,this.b,this.c,u(n,8))},v(Qr,"JsonImporter/lambda$41$Type",925),m(888,1,{},ESe),s.Bi=function(n){Y3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$5$Type",888),m(892,1,{},SSe),s.Bi=function(n){YFe(this.a,u(n,149))},v(Qr,"JsonImporter/lambda$6$Type",892),m(890,1,{},xSe),s.Bi=function(n){X3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$7$Type",890),m(891,1,{},ASe),s.Bi=function(n){K3(this.a,ne(re(n)))},v(Qr,"JsonImporter/lambda$8$Type",891),m(894,1,{},MSe),s.Bi=function(n){tGe(this.a,u(n,139))},v(Qr,"JsonImporter/lambda$9$Type",894),m(944,1,it,CSe),s.Ad=function(n){C4(this.a,new y2(Pt(n)))},v(Qr,"JsonMetaDataConverter/lambda$0$Type",944),m(945,1,it,TSe),s.Ad=function(n){zvn(this.a,u(n,244))},v(Qr,"JsonMetaDataConverter/lambda$1$Type",945),m(946,1,it,OSe),s.Ad=function(n){N4n(this.a,u(n,144))},v(Qr,"JsonMetaDataConverter/lambda$2$Type",946),m(947,1,it,NSe),s.Ad=function(n){Fvn(this.a,u(n,160))},v(Qr,"JsonMetaDataConverter/lambda$3$Type",947),m(244,23,{3:1,35:1,23:1,244:1},d4);var gG,wG,_ce,pG,mG,vG,Lce,Pce,yG=yt(kN,"GraphFeature",244,Tt,b8n,j3n),Oan;m(11,1,{35:1,147:1},yi,Li,fn,Vr),s.Dd=function(n){return qwn(this,u(n,147))},s.Fb=function(n){return y_e(this,n)},s.Rg=function(){return _e(this)},s.Og=function(){return this.b},s.Hb=function(){return Od(this.b)},s.Ib=function(){return this.b},v(kN,"Property",11),m(657,1,Yt,aX),s.Le=function(n,t){return Ajn(this,u(n,105),u(t,105))},s.Fb=function(n){return this===n},s.Me=function(){return new xt(this)},v(kN,"PropertyHolderComparator",657),m(698,1,Fr,roe),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return z9n(this)},s.Qb=function(){xAe()},s.Ob=function(){return!!this.a},v(HF,"ElkGraphUtil/AncestorIterator",698);var B8e=Hi(yc,"EList");m(71,56,{20:1,31:1,56:1,18:1,16:1,71:1,61:1}),s._c=function(n,t){$E(this,n,t)},s.Ec=function(n){return Et(this,n)},s.ad=function(n,t){return h1e(this,n,t)},s.Fc=function(n){return er(this,n)},s.Gi=function(){return new m4(this)},s.Hi=function(){return new kT(this)},s.Ii=function(n){return hO(this,n)},s.Ji=function(){return!0},s.Ki=function(n,t){},s.Li=function(){},s.Mi=function(n,t){bY(this,n,t)},s.Ni=function(n,t,i){},s.Oi=function(n,t){},s.Pi=function(n,t,i){},s.Fb=function(n){return pXe(this,n)},s.Hb=function(){return s1e(this)},s.Qi=function(){return!1},s.Jc=function(){return new ut(this)},s.cd=function(){return new p4(this)},s.dd=function(n){var t;if(t=this.gc(),n<0||n>t)throw $(new b2(n,t));return new vV(this,n)},s.Si=function(n,t){this.Ri(n,this.bd(t))},s.Kc=function(n){return sB(this,n)},s.Ui=function(n,t){return t},s.fd=function(n,t){return tv(this,n,t)},s.Ib=function(){return rde(this)},s.Wi=function(){return!0},s.Xi=function(n,t){return t8(this,t)},v(yc,"AbstractEList",71),m(67,71,Mh,z6,Nw,t1e),s.Ci=function(n,t){return lW(this,n,t)},s.Di=function(n){return uHe(this,n)},s.Ei=function(n,t){xO(this,n,t)},s.Fi=function(n){YT(this,n)},s.Yi=function(n){return phe(this,n)},s.$b=function(){vE(this)},s.Gc=function(n){return m8(this,n)},s.Xb=function(n){return K(this,n)},s.Zi=function(n){var t,i,r;++this.j,i=this.g==null?0:this.g.length,n>i&&(r=this.g,t=i+(i/2|0)+4,t=0?(this.ed(t),!0):!1},s.Vi=function(n,t){return this.Bj(n,this.Xi(n,t))},s.gc=function(){return this.Cj()},s.Nc=function(){return this.Dj()},s.Oc=function(n){return this.Ej(n)},s.Ib=function(){return this.Fj()},v(yc,"DelegatingEList",2055),m(2056,2055,$Ze),s.Ci=function(n,t){return tge(this,n,t)},s.Di=function(n){return this.Ci(this.Cj(),n)},s.Ei=function(n,t){tUe(this,n,t)},s.Fi=function(n){qqe(this,n)},s.Ji=function(){return!this.Kj()},s.$b=function(){hS(this)},s.Gj=function(n,t,i,r,c){return new m_e(this,n,t,i,r,c)},s.Hj=function(n){ai(this.hj(),n)},s.Ij=function(){return null},s.Jj=function(){return-1},s.hj=function(){return null},s.Kj=function(){return!1},s.Lj=function(n,t){return t},s.Mj=function(n,t){return t},s.Nj=function(){return!1},s.Oj=function(){return!this.yj()},s.Ri=function(n,t){var i,r;return this.Nj()?(r=this.Oj(),i=E0e(this,n,t),this.Hj(this.Gj(7,ve(t),i,n,r)),i):E0e(this,n,t)},s.ed=function(n){var t,i,r,c;return this.Nj()?(i=null,r=this.Oj(),t=this.Gj(4,c=iR(this,n),null,n,r),this.Kj()&&c?(i=this.Mj(c,i),i?(i.lj(t),i.mj()):this.Hj(t)):i?(i.lj(t),i.mj()):this.Hj(t),c):(c=iR(this,n),this.Kj()&&c&&(i=this.Mj(c,null),i&&i.mj()),c)},s.Vi=function(n,t){return dKe(this,n,t)},v(wy,"DelegatingNotifyingListImpl",2056),m(151,1,RN),s.lj=function(n){return s0e(this,n)},s.mj=function(){jY(this)},s.ej=function(){return this.d},s.Ij=function(){return null},s.Pj=function(){return null},s.fj=function(n){return-1},s.gj=function(){return WUe(this)},s.hj=function(){return null},s.ij=function(){return Nbe(this)},s.jj=function(){return this.o<0?this.o<-2?-2-this.o-1:-1:this.o},s.Qj=function(){return!1},s.kj=function(n){var t,i,r,c,o,l,f,h,b,p,y;switch(this.d){case 1:case 2:switch(c=n.ej(),c){case 1:case 2:if(o=n.hj(),ue(o)===ue(this.hj())&&this.fj(null)==n.fj(null))return this.g=n.gj(),n.ej()==1&&(this.d=1),!0}case 4:{switch(c=n.ej(),c){case 4:{if(o=n.hj(),ue(o)===ue(this.hj())&&this.fj(null)==n.fj(null))return b=yge(this),h=this.o<0?this.o<-2?-2-this.o-1:-1:this.o,l=n.jj(),this.d=6,y=new Nw(2),h<=l?(Et(y,this.n),Et(y,n.ij()),this.g=z(B($t,1),ni,30,15,[this.o=h,l+1])):(Et(y,n.ij()),Et(y,this.n),this.g=z(B($t,1),ni,30,15,[this.o=l,h])),this.n=y,b||(this.o=-2-this.o-1),!0;break}}break}case 6:{switch(c=n.ej(),c){case 4:{if(o=n.hj(),ue(o)===ue(this.hj())&&this.fj(null)==n.fj(null)){for(b=yge(this),l=n.jj(),p=u(this.g,54),r=oe($t,ni,30,p.length+1,15,1),t=0;t>>0,t.toString(16))),r.a+=" (eventType: ",this.d){case 1:{r.a+="SET";break}case 2:{r.a+="UNSET";break}case 3:{r.a+="ADD";break}case 5:{r.a+="ADD_MANY";break}case 4:{r.a+="REMOVE";break}case 6:{r.a+="REMOVE_MANY";break}case 7:{r.a+="MOVE";break}case 8:{r.a+="REMOVING_ADAPTER";break}case 9:{r.a+="RESOLVE";break}default:{IX(r,this.d);break}}if(zXe(this)&&(r.a+=", touch: true"),r.a+=", position: ",IX(r,this.o<0?this.o<-2?-2-this.o-1:-1:this.o),r.a+=", notifier: ",qj(r,this.hj()),r.a+=", feature: ",qj(r,this.Ij()),r.a+=", oldValue: ",qj(r,Nbe(this)),r.a+=", newValue: ",this.d==6&&X(this.g,54)){for(i=u(this.g,54),r.a+="[",n=0;n10?((!this.b||this.c.j!=this.a)&&(this.b=new w2(this),this.a=this.j),rf(this.b,n)):m8(this,n)},s.Wi=function(){return!0},s.a=0,v(yc,"AbstractEList/1",949),m(305,99,iF,b2),v(yc,"AbstractEList/BasicIndexOutOfBoundsException",305),m(42,1,Fr,ut),s.Nb=function(n){Zr(this,n)},s.Vj=function(){if(this.i.j!=this.f)throw $(new Nl)},s.Wj=function(){return ft(this)},s.Ob=function(){return this.e!=this.i.gc()},s.Pb=function(){return this.Wj()},s.Qb=function(){KE(this)},s.e=0,s.f=0,s.g=-1,v(yc,"AbstractEList/EIterator",42),m(286,42,Qh,p4,vV),s.Qb=function(){KE(this)},s.Rb=function(n){oJe(this,n)},s.Xj=function(){var n;try{return n=this.d.Xb(--this.e),this.Vj(),this.g=this.e,n}catch(t){throw t=sr(t),X(t,99)?(this.Vj(),$(new au)):$(t)}},s.Yj=function(n){oHe(this,n)},s.Sb=function(){return this.e!=0},s.Tb=function(){return this.e},s.Ub=function(){return this.Xj()},s.Vb=function(){return this.e-1},s.Wb=function(n){this.Yj(n)},v(yc,"AbstractEList/EListIterator",286),m(355,42,Fr,m4),s.Wj=function(){return LQ(this)},s.Qb=function(){throw $(new _t)},v(yc,"AbstractEList/NonResolvingEIterator",355),m(391,286,Qh,kT,Xle),s.Rb=function(n){throw $(new _t)},s.Wj=function(){var n;try{return n=this.c.Ti(this.e),this.Vj(),this.g=this.e++,n}catch(t){throw t=sr(t),X(t,99)?(this.Vj(),$(new au)):$(t)}},s.Xj=function(){var n;try{return n=this.c.Ti(--this.e),this.Vj(),this.g=this.e,n}catch(t){throw t=sr(t),X(t,99)?(this.Vj(),$(new au)):$(t)}},s.Qb=function(){throw $(new _t)},s.Wb=function(n){throw $(new _t)},v(yc,"AbstractEList/NonResolvingEListIterator",391),m(2042,71,RZe),s.Ci=function(n,t){var i,r,c,o,l,f,h,b,p,y,S;if(c=t.gc(),c!=0){for(b=u(Un(this.a,4),129),p=b==null?0:b.length,S=p+c,r=uQ(this,S),y=p-n,y>0&&Wu(b,n,r,n+c,y),h=t.Jc(),l=0;li)throw $(new b2(n,i));return new IIe(this,n)},s.$b=function(){var n,t;++this.j,n=u(Un(this.a,4),129),t=n==null?0:n.length,g8(this,null),bY(this,t,n)},s.Gc=function(n){var t,i,r,c,o;if(t=u(Un(this.a,4),129),t!=null){if(n!=null){for(r=t,c=0,o=r.length;c=i)throw $(new b2(n,i));return t[n]},s.bd=function(n){var t,i,r;if(t=u(Un(this.a,4),129),t!=null){if(n!=null){for(i=0,r=t.length;ii)throw $(new b2(n,i));return new DIe(this,n)},s.Ri=function(n,t){var i,r,c;if(i=wJe(this),c=i==null?0:i.length,n>=c)throw $(new jo(Mne+n+dg+c));if(t>=c)throw $(new jo(Cne+t+dg+c));return r=i[t],n!=t&&(n0&&Wu(n,0,t,0,i),t},s.Oc=function(n){var t,i,r;return t=u(Un(this.a,4),129),r=t==null?0:t.length,r>0&&(n.lengthr&&tr(n,r,null),n};var Nan;v(yc,"ArrayDelegatingEList",2042),m(1032,42,Fr,zPe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw $(new Nl)},s.Qb=function(){KE(this),this.a=u(Un(this.b.a,4),129)},v(yc,"ArrayDelegatingEList/EIterator",1032),m(712,286,Qh,ZDe,DIe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw $(new Nl)},s.Yj=function(n){oHe(this,n),this.a=u(Un(this.b.a,4),129)},s.Qb=function(){KE(this),this.a=u(Un(this.b.a,4),129)},v(yc,"ArrayDelegatingEList/EListIterator",712),m(1033,355,Fr,FPe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw $(new Nl)},v(yc,"ArrayDelegatingEList/NonResolvingEIterator",1033),m(713,391,Qh,eIe,IIe),s.Vj=function(){if(this.b.j!=this.f||ue(u(Un(this.b.a,4),129))!==ue(this.a))throw $(new Nl)},v(yc,"ArrayDelegatingEList/NonResolvingEListIterator",713),m(605,305,iF,jK),v(yc,"BasicEList/BasicIndexOutOfBoundsException",605),m(699,67,Mh,Nse),s._c=function(n,t){throw $(new _t)},s.Ec=function(n){throw $(new _t)},s.ad=function(n,t){throw $(new _t)},s.Fc=function(n){throw $(new _t)},s.$b=function(){throw $(new _t)},s.Zi=function(n){throw $(new _t)},s.Jc=function(){return this.Gi()},s.cd=function(){return this.Hi()},s.dd=function(n){return this.Ii(n)},s.Ri=function(n,t){throw $(new _t)},s.Si=function(n,t){throw $(new _t)},s.ed=function(n){throw $(new _t)},s.Kc=function(n){throw $(new _t)},s.fd=function(n,t){throw $(new _t)},v(yc,"BasicEList/UnmodifiableEList",699),m(711,1,{3:1,20:1,18:1,16:1,61:1,586:1}),s._c=function(n,t){_wn(this,n,u(t,45))},s.Ec=function(n){return Cpn(this,u(n,45))},s.Ic=function(n){rc(this,n)},s.Xb=function(n){return u(K(this.c,n),136)},s.Ri=function(n,t){return u(this.c.Ri(n,t),45)},s.Si=function(n,t){Lwn(this,n,u(t,45))},s.ed=function(n){return u(this.c.ed(n),45)},s.fd=function(n,t){return Hvn(this,n,u(t,45))},s.gd=function(n){Vb(this,n)},s.Lc=function(){return new pn(this,16)},s.Mc=function(){return new wn(null,new pn(this,16))},s.ad=function(n,t){return this.c.ad(n,t)},s.Fc=function(n){return this.c.Fc(n)},s.$b=function(){this.c.$b()},s.Gc=function(n){return this.c.Gc(n)},s.Hc=function(n){return yO(this.c,n)},s.Zj=function(){var n,t,i;if(this.d==null){for(this.d=oe(z8e,nme,67,2*this.f+1,0,1),i=this.e,this.f=0,t=this.c.Jc();t.e!=t.i.gc();)n=u(t.Wj(),136),lz(this,n);this.e=i}},s.Fb=function(n){return SNe(this,n)},s.Hb=function(){return s1e(this.c)},s.bd=function(n){return this.c.bd(n)},s.$j=function(){this.c=new DSe(this)},s.dc=function(){return this.f==0},s.Jc=function(){return this.c.Jc()},s.cd=function(){return this.c.cd()},s.dd=function(n){return this.c.dd(n)},s._j=function(){return ZT(this)},s.ak=function(n,t,i){return new UNe(n,t,i)},s.bk=function(){return new mL},s.Kc=function(n){return wBe(this,n)},s.gc=function(){return this.f},s.hd=function(n,t){return new C0(this.c,n,t)},s.Nc=function(){return this.c.Nc()},s.Oc=function(n){return this.c.Oc(n)},s.Ib=function(){return rde(this.c)},s.e=0,s.f=0,v(yc,"BasicEMap",711),m(1027,67,Mh,DSe),s.Ki=function(n,t){wbn(this,u(t,136))},s.Ni=function(n,t,i){var r;++(r=this,u(t,136),r).a.e},s.Oi=function(n,t){pbn(this,u(t,136))},s.Pi=function(n,t,i){bpn(this,u(t,136),u(i,136))},s.Mi=function(n,t){fze(this.a)},v(yc,"BasicEMap/1",1027),m(1028,67,Mh,mL),s.$i=function(n){return oe(YBn,BZe,611,n,0,1)},v(yc,"BasicEMap/2",1028),m(1029,Ha,fs,ISe),s.$b=function(){this.a.c.$b()},s.Gc=function(n){return SQ(this.a,n)},s.Jc=function(){return this.a.f==0?(S9(),eI.a):new pAe(this.a)},s.Kc=function(n){var t;return t=this.a.f,ZB(this.a,n),this.a.f!=t},s.gc=function(){return this.a.f},v(yc,"BasicEMap/3",1029),m(1030,31,Y2,_Se),s.$b=function(){this.a.c.$b()},s.Gc=function(n){return mXe(this.a,n)},s.Jc=function(){return this.a.f==0?(S9(),eI.a):new mAe(this.a)},s.gc=function(){return this.a.f},v(yc,"BasicEMap/4",1030),m(1031,Ha,fs,LSe),s.$b=function(){this.a.c.$b()},s.Gc=function(n){var t,i,r,c,o,l,f,h,b;if(this.a.f>0&&X(n,45)&&(this.a.Zj(),h=u(n,45),f=h.jd(),c=f==null?0:Oi(f),o=Nle(this.a,c),t=this.a.d[o],t)){for(i=u(t.g,374),b=t.i,l=0;l"+this.c},s.a=0;var YBn=v(yc,"BasicEMap/EntryImpl",611);m(534,1,{},J6),v(yc,"BasicEMap/View",534);var eI;m(769,1,{}),s.Fb=function(n){return abe((jn(),Sc),n)},s.Hb=function(){return y1e((jn(),Sc))},s.Ib=function(){return Fa((jn(),Sc))},v(yc,"ECollections/BasicEmptyUnmodifiableEList",769),m(1302,1,Qh,WM),s.Nb=function(n){Zr(this,n)},s.Rb=function(n){throw $(new _t)},s.Ob=function(){return!1},s.Sb=function(){return!1},s.Pb=function(){throw $(new au)},s.Tb=function(){return 0},s.Ub=function(){throw $(new au)},s.Vb=function(){return-1},s.Qb=function(){throw $(new _t)},s.Wb=function(n){throw $(new _t)},v(yc,"ECollections/BasicEmptyUnmodifiableEList/1",1302),m(1300,769,{20:1,18:1,16:1,61:1},Sxe),s._c=function(n,t){zAe()},s.Ec=function(n){return BAe()},s.ad=function(n,t){return FAe()},s.Fc=function(n){return JAe()},s.$b=function(){HAe()},s.Gc=function(n){return!1},s.Hc=function(n){return!1},s.Ic=function(n){rc(this,n)},s.Xb=function(n){return Pse((jn(),n)),null},s.bd=function(n){return-1},s.dc=function(){return!0},s.Jc=function(){return this.a},s.cd=function(){return this.a},s.dd=function(n){return this.a},s.Ri=function(n,t){return GAe()},s.Si=function(n,t){qAe()},s.ed=function(n){return UAe()},s.Kc=function(n){return XAe()},s.fd=function(n,t){return KAe()},s.gc=function(){return 0},s.gd=function(n){Vb(this,n)},s.Lc=function(){return new pn(this,16)},s.Mc=function(){return new wn(null,new pn(this,16))},s.hd=function(n,t){return jn(),new C0(Sc,n,t)},s.Nc=function(){return Ofe((jn(),Sc))},s.Oc=function(n){return jn(),GE(Sc,n)},v(yc,"ECollections/EmptyUnmodifiableEList",1300),m(1301,769,{20:1,18:1,16:1,61:1,586:1},xxe),s._c=function(n,t){zAe()},s.Ec=function(n){return BAe()},s.ad=function(n,t){return FAe()},s.Fc=function(n){return JAe()},s.$b=function(){HAe()},s.Gc=function(n){return!1},s.Hc=function(n){return!1},s.Ic=function(n){rc(this,n)},s.Xb=function(n){return Pse((jn(),n)),null},s.bd=function(n){return-1},s.dc=function(){return!0},s.Jc=function(){return this.a},s.cd=function(){return this.a},s.dd=function(n){return this.a},s.Ri=function(n,t){return GAe()},s.Si=function(n,t){qAe()},s.ed=function(n){return UAe()},s.Kc=function(n){return XAe()},s.fd=function(n,t){return KAe()},s.gc=function(){return 0},s.gd=function(n){Vb(this,n)},s.Lc=function(){return new pn(this,16)},s.Mc=function(){return new wn(null,new pn(this,16))},s.hd=function(n,t){return jn(),new C0(Sc,n,t)},s.Nc=function(){return Ofe((jn(),Sc))},s.Oc=function(n){return jn(),GE(Sc,n)},s._j=function(){return jn(),jn(),i1},v(yc,"ECollections/EmptyUnmodifiableEMap",1301);var J8e=Hi(yc,"Enumerator"),kG;m(290,1,{290:1},DW),s.Fb=function(n){var t;return this===n?!0:X(n,290)?(t=u(n,290),this.f==t.f&&uvn(this.i,t.i)&&cV(this.a,(this.f&256)!=0?(t.f&256)!=0?t.a:null:(t.f&256)!=0?null:t.a)&&cV(this.d,t.d)&&cV(this.g,t.g)&&cV(this.e,t.e)&&lSn(this,t)):!1},s.Hb=function(){return this.f},s.Ib=function(){return WXe(this)},s.f=0;var Dan=0,Ian=0,_an=0,Lan=0,H8e=0,G8e=0,q8e=0,U8e=0,X8e=0,Pan,uA=0,oA=0,$an=0,Ran=0,jG,K8e;v(yc,"URI",290),m(1090,44,bv,Axe),s.yc=function(n,t){return u(Kc(this,Pt(n),u(t,290)),290)},v(yc,"URI/URICache",1090),m(492,67,Mh,ZM,lR),s.Qi=function(){return!0},v(yc,"UniqueEList",492),m(578,63,H1,uB),v(yc,"WrappedException",578);var Zt=Hi(ql,JZe),$m=Hi(ql,HZe),ns=Hi(ql,GZe),Rm=Hi(ql,qZe),Aa=Hi(ql,UZe),vf=Hi(ql,"EClass"),Bce=Hi(ql,"EDataType"),Ban;m(1198,44,bv,Mxe),s.xc=function(n){return $r(n)?lo(this,n):du(Xc(this.f,n))},v(ql,"EDataType/Internal/ConversionDelegate/Factory/Registry/Impl",1198);var EG=Hi(ql,"EEnum"),ed=Hi(ql,XZe),Rc=Hi(ql,KZe),yf=Hi(ql,VZe),kf,vp=Hi(ql,YZe),Bm=Hi(ql,QZe);m(1023,1,{},eC),s.Ib=function(){return"NIL"},v(ql,"EStructuralFeature/Internal/DynamicValueHolder/1",1023);var zan;m(1022,44,bv,Cxe),s.xc=function(n){return $r(n)?lo(this,n):du(Xc(this.f,n))},v(ql,"EStructuralFeature/Internal/SettingDelegate/Factory/Registry/Impl",1022);var Fo=Hi(ql,WZe),Uy=Hi(ql,"EValidator/PatternMatcher"),V8e,Y8e,Rn,Wd,zm,pb,Fan,Jan,Han,mb,Zd,vb,yp,ih,Gan,qan,jf,e0,Uan,n0,Fm,Wv,Ac,Xan,Kan,kp,SG=Hi($i,"FeatureMap/Entry");m(533,1,{75:1},A$),s.Jk=function(){return this.a},s.kd=function(){return this.b},v(Fn,"BasicEObjectImpl/1",533),m(1021,1,_ne,KCe),s.Dk=function(n){return fY(this.a,this.b,n)},s.Oj=function(){return L_e(this.a,this.b)},s.Wb=function(n){pae(this.a,this.b,n)},s.Ek=function(){l5n(this.a,this.b)},v(Fn,"BasicEObjectImpl/4",1021),m(2043,1,{114:1}),s.Kk=function(n){this.e=n==0?Van:oe(Ar,On,1,n,5,1)},s.ii=function(n){return this.e[n]},s.ji=function(n,t){this.e[n]=t},s.ki=function(n){this.e[n]=null},s.Lk=function(){return this.c},s.Mk=function(){throw $(new _t)},s.Nk=function(){throw $(new _t)},s.Ok=function(){return this.d},s.Pk=function(){return this.e!=null},s.Qk=function(n){this.c=n},s.Rk=function(n){throw $(new _t)},s.Sk=function(n){throw $(new _t)},s.Tk=function(n){this.d=n};var Van;v(Fn,"BasicEObjectImpl/EPropertiesHolderBaseImpl",2043),m(192,2043,{114:1},tl),s.Mk=function(){return this.a},s.Nk=function(){return this.b},s.Rk=function(n){this.a=n},s.Sk=function(n){this.b=n},v(Fn,"BasicEObjectImpl/EPropertiesHolderImpl",192),m(501,100,WWe,p3),s.rh=function(){return this.f},s.wh=function(){return this.k},s.yh=function(n,t){this.g=n,this.i=t},s.Ah=function(){return(this.j&2)==0?this.fi():this.Xh().Lk()},s.Ch=function(){return this.i},s.th=function(){return(this.j&1)!=0},s.Mh=function(){return this.g},s.Sh=function(){return(this.j&4)!=0},s.Xh=function(){return!this.k&&(this.k=new tl),this.k},s._h=function(n){this.Xh().Qk(n),n?this.j|=2:this.j&=-3},s.bi=function(n){this.Xh().Sk(n),n?this.j|=4:this.j&=-5},s.fi=function(){return(x0(),Rn).S},s.i=0,s.j=1,v(Fn,"EObjectImpl",501),m(785,501,{109:1,94:1,93:1,57:1,114:1,52:1,100:1},bfe),s.ii=function(n){return this.e[n]},s.ji=function(n,t){this.e[n]=t},s.ki=function(n){this.e[n]=null},s.Ah=function(){return this.d},s.Fh=function(n){return zi(this.d,n)},s.Hh=function(){return this.d},s.Lh=function(){return this.e!=null},s.Xh=function(){return!this.k&&(this.k=new vL),this.k},s._h=function(n){this.d=n},s.ei=function(){var n;return this.e==null&&(n=dt(this.d),this.e=n==0?Yan:oe(Ar,On,1,n,5,1)),this},s.gi=function(){return 0};var Yan;v(Fn,"DynamicEObjectImpl",785),m(1483,785,{109:1,45:1,94:1,93:1,136:1,57:1,114:1,52:1,100:1},bDe),s.Fb=function(n){return this===n},s.Hb=function(){return vw(this)},s._h=function(n){this.d=n,this.b=QO(n,"key"),this.c=QO(n,PS)},s.yi=function(){var n;return this.a==-1&&(n=EY(this,this.b),this.a=n==null?0:Oi(n)),this.a},s.jd=function(){return EY(this,this.b)},s.kd=function(){return EY(this,this.c)},s.zi=function(n){this.a=n},s.Ai=function(n){pae(this,this.b,n)},s.ld=function(n){var t;return t=EY(this,this.c),pae(this,this.c,n),t},s.a=0,v(Fn,"DynamicEObjectImpl/BasicEMapEntry",1483),m(1484,1,{114:1},vL),s.Kk=function(n){throw $(new _t)},s.ii=function(n){throw $(new _t)},s.ji=function(n,t){throw $(new _t)},s.ki=function(n){throw $(new _t)},s.Lk=function(){throw $(new _t)},s.Mk=function(){return this.a},s.Nk=function(){return this.b},s.Ok=function(){return this.c},s.Pk=function(){throw $(new _t)},s.Qk=function(n){throw $(new _t)},s.Rk=function(n){this.a=n},s.Sk=function(n){this.b=n},s.Tk=function(n){this.c=n},v(Fn,"DynamicEObjectImpl/DynamicEPropertiesHolderImpl",1484),m(504,161,{109:1,94:1,93:1,587:1,158:1,57:1,114:1,52:1,100:1,504:1,161:1,117:1,118:1},Mb),s.xh=function(n){return qde(this,n)},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.d;case 2:return i?(!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),this.b):(!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),ZT(this.b));case 3:return J_e(this);case 4:return!this.a&&(this.a=new mr(wb,this,4)),this.a;case 5:return!this.c&&(this.c=new $3(wb,this,5)),this.c}return Pl(this,n-dt((yn(),Wd)),An((r=u(Un(this,16),29),r||Wd),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),Co(this.Ab,n,i);case 3:return this.Cb&&(i=(c=this.Db>>16,c>=0?qde(this,i):this.Cb.Qh(this,-1-c,null,i))),Cfe(this,u(n,158),i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),Wd)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),Wd)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 2:return!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),U$(this.b,n,i);case 3:return Cfe(this,null,i);case 4:return!this.a&&(this.a=new mr(wb,this,4)),vc(this.a,n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),Wd)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),Wd)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.d!=null;case 2:return!!this.b&&this.b.f!=0;case 3:return!!J_e(this);case 4:return!!this.a&&this.a.i!=0;case 5:return!!this.c&&this.c.i!=0}return Ll(this,n-dt((yn(),Wd)),An((t=u(Un(this,16),29),t||Wd),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:X3n(this,Pt(t));return;case 2:!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),TB(this.b,t);return;case 3:zUe(this,u(t,158));return;case 4:!this.a&&(this.a=new mr(wb,this,4)),kt(this.a),!this.a&&(this.a=new mr(wb,this,4)),er(this.a,u(t,18));return;case 5:!this.c&&(this.c=new $3(wb,this,5)),kt(this.c),!this.c&&(this.c=new $3(wb,this,5)),er(this.c,u(t,18));return}Jl(this,n-dt((yn(),Wd)),An((i=u(Un(this,16),29),i||Wd),n),t)},s.fi=function(){return yn(),Wd},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Hhe(this,null);return;case 2:!this.b&&(this.b=new Hs((yn(),Ac),Iu,this)),this.b.c.$b();return;case 3:zUe(this,null);return;case 4:!this.a&&(this.a=new mr(wb,this,4)),kt(this.a);return;case 5:!this.c&&(this.c=new $3(wb,this,5)),kt(this.c);return}Fl(this,n-dt((yn(),Wd)),An((t=u(Un(this,16),29),t||Wd),n))},s.Ib=function(){return NFe(this)},s.d=null,v(Fn,"EAnnotationImpl",504),m(142,711,tme,os),s.Ei=function(n,t){mwn(this,n,u(t,45))},s.Uk=function(n,t){return m2n(this,u(n,45),t)},s.Yi=function(n){return u(u(this.c,72).Yi(n),136)},s.Gi=function(){return u(this.c,72).Gi()},s.Hi=function(){return u(this.c,72).Hi()},s.Ii=function(n){return u(this.c,72).Ii(n)},s.Vk=function(n,t){return U$(this,n,t)},s.Dk=function(n){return u(this.c,77).Dk(n)},s.$j=function(){},s.Oj=function(){return u(this.c,77).Oj()},s.ak=function(n,t,i){var r;return r=u(sl(this.b).ti().pi(this.b),136),r.zi(n),r.Ai(t),r.ld(i),r},s.bk=function(){return new uoe(this)},s.Wb=function(n){TB(this,n)},s.Ek=function(){u(this.c,77).Ek()},v($i,"EcoreEMap",142),m(169,142,tme,Hs),s.Zj=function(){var n,t,i,r,c,o;if(this.d==null){for(o=oe(z8e,nme,67,2*this.f+1,0,1),i=this.c.Jc();i.e!=i.i.gc();)t=u(i.Wj(),136),r=t.yi(),c=(r&oi)%o.length,n=o[c],!n&&(n=o[c]=new uoe(this)),n.Ec(t);this.d=o}},v(Fn,"EAnnotationImpl/1",169),m(293,439,{109:1,94:1,93:1,158:1,197:1,57:1,114:1,470:1,52:1,100:1,161:1,293:1,117:1,118:1}),s.Ih=function(n,t,i){var r,c;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),!!this.Hk();case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q}return Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 9:return EV(this,i)}return c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,Lo(this),t-dt(this.fi()),n,i)},s.Th=function(n){var t,i;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return this.Hk();case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0)}return Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:this.ri(Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Td(this,u(t,15).a);return;case 5:this.Xk(u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return yn(),Kan},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:this.ri(null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Td(this,0);return;case 5:this.Xk(1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.mi=function(){ff(this),this.Bb|=1},s.Fk=function(){return ff(this)},s.Gk=function(){return this.t},s.Hk=function(){var n;return n=this.t,n>1||n==-1},s.Qi=function(){return(this.Bb&512)!=0},s.Wk=function(n,t){return z1e(this,n,t)},s.Xk=function(n){O2(this,n)},s.Ib=function(){return tbe(this)},s.s=0,s.t=1,v(Fn,"ETypedElementImpl",293),m(451,293,{109:1,94:1,93:1,158:1,197:1,57:1,179:1,69:1,114:1,470:1,52:1,100:1,161:1,451:1,293:1,117:1,118:1,682:1}),s.xh=function(n){return jHe(this,n)},s.Ih=function(n,t,i){var r,c;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),!!this.Hk();case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return Pn(),(this.Bb&qf)!=0;case 11:return Pn(),(this.Bb&U0)!=0;case 12:return Pn(),(this.Bb&W2)!=0;case 13:return this.j;case 14:return S8(this);case 15:return Pn(),(this.Bb&as)!=0;case 16:return Pn(),(this.Bb&yh)!=0;case 17:return E2(this)}return Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),Co(this.Ab,n,i);case 17:return this.Cb&&(i=(c=this.Db>>16,c>=0?jHe(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,17,i)}return o=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),o.uk().xk(this,Lo(this),t-dt(this.fi()),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 9:return EV(this,i);case 17:return dl(this,null,17,i)}return c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,Lo(this),t-dt(this.fi()),n,i)},s.Th=function(n){var t,i;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return this.Hk();case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return(this.Bb&qf)==0;case 11:return(this.Bb&U0)!=0;case 12:return(this.Bb&W2)!=0;case 13:return this.j!=null;case 14:return S8(this)!=null;case 15:return(this.Bb&as)!=0;case 16:return(this.Bb&yh)!=0;case 17:return!!E2(this)}return Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:UV(this,Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Td(this,u(t,15).a);return;case 5:this.Xk(u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return;case 10:o8(this,Re($e(t)));return;case 11:f8(this,Re($e(t)));return;case 12:l8(this,Re($e(t)));return;case 13:Ise(this,Pt(t));return;case 15:s8(this,Re($e(t)));return;case 16:a8(this,Re($e(t)));return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return yn(),Xan},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,88)&&H2(Ms(u(this.Cb,88)),4),Mo(this,null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Td(this,0);return;case 5:this.Xk(1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return;case 10:o8(this,!0);return;case 11:f8(this,!1);return;case 12:l8(this,!1);return;case 13:this.i=null,EB(this,null);return;case 15:s8(this,!1);return;case 16:a8(this,!1);return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.mi=function(){L9(Vc((ls(),nc),this)),ff(this),this.Bb|=1},s.nk=function(){return this.f},s.gk=function(){return S8(this)},s.ok=function(){return E2(this)},s.sk=function(){return null},s.Yk=function(){return this.k},s.Jj=function(){return this.n},s.tk=function(){return wz(this)},s.uk=function(){var n,t,i,r,c,o,l,f,h;return this.p||(i=E2(this),(i.i==null&&vh(i),i.i).length,r=this.sk(),r&&dt(E2(r)),c=ff(this),l=c.ik(),n=l?(l.i&1)!=0?l==ts?Yi:l==$t?jr:l==Hm?h7:l==Jr?gr:l==Ep?cp:l==t5?up:l==ds?py:XS:l:null,t=S8(this),f=c.gk(),Djn(this),(this.Bb&yh)!=0&&((o=Wde((ls(),nc),i))&&o!=this||(o=D4(Vc(nc,this))))?this.p=new YCe(this,o):this.Hk()?this.$k()?r?(this.Bb&as)!=0?n?this._k()?this.p=new Jb(47,n,this,r):this.p=new Jb(5,n,this,r):this._k()?this.p=new Kb(46,this,r):this.p=new Kb(4,this,r):n?this._k()?this.p=new Jb(49,n,this,r):this.p=new Jb(7,n,this,r):this._k()?this.p=new Kb(48,this,r):this.p=new Kb(6,this,r):(this.Bb&as)!=0?n?n==wg?this.p=new Ed(50,Tan,this):this._k()?this.p=new Ed(43,n,this):this.p=new Ed(1,n,this):this._k()?this.p=new xd(42,this):this.p=new xd(0,this):n?n==wg?this.p=new Ed(41,Tan,this):this._k()?this.p=new Ed(45,n,this):this.p=new Ed(3,n,this):this._k()?this.p=new xd(44,this):this.p=new xd(2,this):X(c,159)?n==SG?this.p=new xd(40,this):(this.Bb&512)!=0?(this.Bb&as)!=0?n?this.p=new Ed(9,n,this):this.p=new xd(8,this):n?this.p=new Ed(11,n,this):this.p=new xd(10,this):(this.Bb&as)!=0?n?this.p=new Ed(13,n,this):this.p=new xd(12,this):n?this.p=new Ed(15,n,this):this.p=new xd(14,this):r?(h=r.t,h>1||h==-1?this._k()?(this.Bb&as)!=0?n?this.p=new Jb(25,n,this,r):this.p=new Kb(24,this,r):n?this.p=new Jb(27,n,this,r):this.p=new Kb(26,this,r):(this.Bb&as)!=0?n?this.p=new Jb(29,n,this,r):this.p=new Kb(28,this,r):n?this.p=new Jb(31,n,this,r):this.p=new Kb(30,this,r):this._k()?(this.Bb&as)!=0?n?this.p=new Jb(33,n,this,r):this.p=new Kb(32,this,r):n?this.p=new Jb(35,n,this,r):this.p=new Kb(34,this,r):(this.Bb&as)!=0?n?this.p=new Jb(37,n,this,r):this.p=new Kb(36,this,r):n?this.p=new Jb(39,n,this,r):this.p=new Kb(38,this,r)):this._k()?(this.Bb&as)!=0?n?this.p=new Ed(17,n,this):this.p=new xd(16,this):n?this.p=new Ed(19,n,this):this.p=new xd(18,this):(this.Bb&as)!=0?n?this.p=new Ed(21,n,this):this.p=new xd(20,this):n?this.p=new Ed(23,n,this):this.p=new xd(22,this):this.Zk()?this._k()?this.p=new BNe(u(c,29),this,r):this.p=new bae(u(c,29),this,r):X(c,159)?n==SG?this.p=new xd(40,this):(this.Bb&as)!=0?n?this.p=new PDe(t,f,this,(AQ(),l==$t?i7e:l==ts?W8e:l==Ep?r7e:l==Hm?t7e:l==Jr?n7e:l==t5?c7e:l==ds?Z8e:l==Wl?e7e:Jce)):this.p=new WIe(u(c,159),t,f,this):n?this.p=new LDe(t,f,this,(AQ(),l==$t?i7e:l==ts?W8e:l==Ep?r7e:l==Hm?t7e:l==Jr?n7e:l==t5?c7e:l==ds?Z8e:l==Wl?e7e:Jce)):this.p=new QIe(u(c,159),t,f,this):this.$k()?r?(this.Bb&as)!=0?this._k()?this.p=new FNe(u(c,29),this,r):this.p=new Zle(u(c,29),this,r):this._k()?this.p=new zNe(u(c,29),this,r):this.p=new WK(u(c,29),this,r):(this.Bb&as)!=0?this._k()?this.p=new $Oe(u(c,29),this):this.p=new mle(u(c,29),this):this._k()?this.p=new POe(u(c,29),this):this.p=new RK(u(c,29),this):this._k()?r?(this.Bb&as)!=0?this.p=new JNe(u(c,29),this,r):this.p=new efe(u(c,29),this,r):(this.Bb&as)!=0?this.p=new ROe(u(c,29),this):this.p=new vle(u(c,29),this):r?(this.Bb&as)!=0?this.p=new HNe(u(c,29),this,r):this.p=new nfe(u(c,29),this,r):(this.Bb&as)!=0?this.p=new BOe(u(c,29),this):this.p=new sR(u(c,29),this)),this.p},s.pk=function(){return(this.Bb&qf)!=0},s.Zk=function(){return!1},s.$k=function(){return!1},s.qk=function(){return(this.Bb&yh)!=0},s.vk=function(){return xY(this)},s._k=function(){return!1},s.rk=function(){return(this.Bb&as)!=0},s.al=function(n){this.k=n},s.ri=function(n){UV(this,n)},s.Ib=function(){return zz(this)},s.e=!1,s.n=0,v(Fn,"EStructuralFeatureImpl",451),m(335,451,{109:1,94:1,93:1,38:1,158:1,197:1,57:1,179:1,69:1,114:1,470:1,52:1,100:1,335:1,161:1,451:1,293:1,117:1,118:1,682:1},mX),s.Ih=function(n,t,i){var r,c;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),!!Y0e(this);case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return Pn(),(this.Bb&qf)!=0;case 11:return Pn(),(this.Bb&U0)!=0;case 12:return Pn(),(this.Bb&W2)!=0;case 13:return this.j;case 14:return S8(this);case 15:return Pn(),(this.Bb&as)!=0;case 16:return Pn(),(this.Bb&yh)!=0;case 17:return E2(this);case 18:return Pn(),(this.Bb&Bu)!=0;case 19:return t?UY(this):e$e(this)}return Pl(this,n-dt((yn(),zm)),An((r=u(Un(this,16),29),r||zm),n),t,i)},s.Th=function(n){var t,i;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return Y0e(this);case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return(this.Bb&qf)==0;case 11:return(this.Bb&U0)!=0;case 12:return(this.Bb&W2)!=0;case 13:return this.j!=null;case 14:return S8(this)!=null;case 15:return(this.Bb&as)!=0;case 16:return(this.Bb&yh)!=0;case 17:return!!E2(this);case 18:return(this.Bb&Bu)!=0;case 19:return!!e$e(this)}return Ll(this,n-dt((yn(),zm)),An((t=u(Un(this,16),29),t||zm),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:UV(this,Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Td(this,u(t,15).a);return;case 5:SAe(this,u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return;case 10:o8(this,Re($e(t)));return;case 11:f8(this,Re($e(t)));return;case 12:l8(this,Re($e(t)));return;case 13:Ise(this,Pt(t));return;case 15:s8(this,Re($e(t)));return;case 16:a8(this,Re($e(t)));return;case 18:pQ(this,Re($e(t)));return}Jl(this,n-dt((yn(),zm)),An((i=u(Un(this,16),29),i||zm),n),t)},s.fi=function(){return yn(),zm},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,88)&&H2(Ms(u(this.Cb,88)),4),Mo(this,null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Td(this,0);return;case 5:this.b=0,O2(this,1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return;case 10:o8(this,!0);return;case 11:f8(this,!1);return;case 12:l8(this,!1);return;case 13:this.i=null,EB(this,null);return;case 15:s8(this,!1);return;case 16:a8(this,!1);return;case 18:pQ(this,!1);return}Fl(this,n-dt((yn(),zm)),An((t=u(Un(this,16),29),t||zm),n))},s.mi=function(){UY(this),L9(Vc((ls(),nc),this)),ff(this),this.Bb|=1},s.Hk=function(){return Y0e(this)},s.Wk=function(n,t){return this.b=0,this.a=null,z1e(this,n,t)},s.Xk=function(n){SAe(this,n)},s.Ib=function(){var n;return(this.Db&64)!=0?zz(this):(n=new cf(zz(this)),n.a+=" (iD: ",md(n,(this.Bb&Bu)!=0),n.a+=")",n.a)},s.b=0,v(Fn,"EAttributeImpl",335),m(360,439,{109:1,94:1,93:1,143:1,158:1,197:1,57:1,114:1,52:1,100:1,360:1,161:1,117:1,118:1,681:1}),s.bl=function(n){return n.Ah()==this},s.xh=function(n){return QQ(this,n)},s.yh=function(n,t){this.w=null,this.Db=t<<16|this.Db&255,this.Cb=n},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return this.D!=null?this.D:this.B;case 3:return Fw(this);case 4:return this.gk();case 5:return this.F;case 6:return t?sl(this):R9(this);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),this.A}return Pl(this,n-dt(this.fi()),An((r=u(Un(this,16),29),r||this.fi()),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),Co(this.Ab,n,i);case 6:return this.Cb&&(i=(c=this.Db>>16,c>=0?QQ(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,6,i)}return o=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),o.uk().xk(this,Lo(this),t-dt(this.fi()),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 6:return dl(this,null,6,i);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),vc(this.A,n,i)}return c=u(An((r=u(Un(this,16),29),r||this.fi()),t),69),c.uk().yk(this,Lo(this),t-dt(this.fi()),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.D!=null&&this.D==this.F;case 3:return!!Fw(this);case 4:return this.gk()!=null;case 5:return this.F!=null&&this.F!=this.D&&this.F!=this.B;case 6:return!!R9(this);case 7:return!!this.A&&this.A.i!=0}return Ll(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:RR(this,Pt(t));return;case 2:xK(this,Pt(t));return;case 5:N8(this,Pt(t));return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A),!this.A&&(this.A=new rs(Fo,this,7)),er(this.A,u(t,18));return}Jl(this,n-dt(this.fi()),An((i=u(Un(this,16),29),i||this.fi()),n),t)},s.fi=function(){return yn(),Fan},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,184)&&(u(this.Cb,184).tb=null),Mo(this,null);return;case 2:i8(this,null),U9(this,this.D);return;case 5:N8(this,null);return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A);return}Fl(this,n-dt(this.fi()),An((t=u(Un(this,16),29),t||this.fi()),n))},s.fk=function(){var n;return this.G==-1&&(this.G=(n=sl(this),n?Ld(n.si(),this):-1)),this.G},s.gk=function(){return null},s.hk=function(){return sl(this)},s.cl=function(){return this.v},s.ik=function(){return Fw(this)},s.jk=function(){return this.D!=null?this.D:this.B},s.kk=function(){return this.F},s.dk=function(n){return FW(this,n)},s.dl=function(n){this.v=n},s.el=function(n){HBe(this,n)},s.fl=function(n){this.C=n},s.ri=function(n){RR(this,n)},s.Ib=function(){return VB(this)},s.C=null,s.D=null,s.G=-1,v(Fn,"EClassifierImpl",360),m(88,360,{109:1,94:1,93:1,29:1,143:1,158:1,197:1,57:1,114:1,52:1,100:1,88:1,360:1,161:1,471:1,117:1,118:1,681:1},ij),s.bl=function(n){return r2n(this,n.Ah())},s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return this.D!=null?this.D:this.B;case 3:return Fw(this);case 4:return null;case 5:return this.F;case 6:return t?sl(this):R9(this);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),this.A;case 8:return Pn(),(this.Bb&256)!=0;case 9:return Pn(),(this.Bb&512)!=0;case 10:return tu(this);case 11:return!this.q&&(this.q=new pe(yf,this,11,10)),this.q;case 12:return fv(this);case 13:return lS(this);case 14:return lS(this),this.r;case 15:return fv(this),this.k;case 16:return B0e(this);case 17:return qW(this);case 18:return vh(this);case 19:return Nz(this);case 20:return fv(this),this.o;case 21:return!this.s&&(this.s=new pe(ns,this,21,17)),this.s;case 22:return Vu(this);case 23:return NW(this)}return Pl(this,n-dt((yn(),pb)),An((r=u(Un(this,16),29),r||pb),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),Co(this.Ab,n,i);case 6:return this.Cb&&(i=(c=this.Db>>16,c>=0?QQ(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,6,i);case 11:return!this.q&&(this.q=new pe(yf,this,11,10)),Co(this.q,n,i);case 21:return!this.s&&(this.s=new pe(ns,this,21,17)),Co(this.s,n,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),pb)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),pb)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 6:return dl(this,null,6,i);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),vc(this.A,n,i);case 11:return!this.q&&(this.q=new pe(yf,this,11,10)),vc(this.q,n,i);case 21:return!this.s&&(this.s=new pe(ns,this,21,17)),vc(this.s,n,i);case 22:return vc(Vu(this),n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),pb)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),pb)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.D!=null&&this.D==this.F;case 3:return!!Fw(this);case 4:return!1;case 5:return this.F!=null&&this.F!=this.D&&this.F!=this.B;case 6:return!!R9(this);case 7:return!!this.A&&this.A.i!=0;case 8:return(this.Bb&256)!=0;case 9:return(this.Bb&512)!=0;case 10:return!!this.u&&Vu(this.u.a).i!=0&&!(this.n&&zQ(this.n));case 11:return!!this.q&&this.q.i!=0;case 12:return fv(this).i!=0;case 13:return lS(this).i!=0;case 14:return lS(this),this.r.i!=0;case 15:return fv(this),this.k.i!=0;case 16:return B0e(this).i!=0;case 17:return qW(this).i!=0;case 18:return vh(this).i!=0;case 19:return Nz(this).i!=0;case 20:return fv(this),!!this.o;case 21:return!!this.s&&this.s.i!=0;case 22:return!!this.n&&zQ(this.n);case 23:return NW(this).i!=0}return Ll(this,n-dt((yn(),pb)),An((t=u(Un(this,16),29),t||pb),n))},s.Wh=function(n){var t;return t=this.i==null||this.q&&this.q.i!=0?null:QO(this,n),t||Cge(this,n)},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:RR(this,Pt(t));return;case 2:xK(this,Pt(t));return;case 5:N8(this,Pt(t));return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A),!this.A&&(this.A=new rs(Fo,this,7)),er(this.A,u(t,18));return;case 8:H1e(this,Re($e(t)));return;case 9:G1e(this,Re($e(t)));return;case 10:hS(tu(this)),er(tu(this),u(t,18));return;case 11:!this.q&&(this.q=new pe(yf,this,11,10)),kt(this.q),!this.q&&(this.q=new pe(yf,this,11,10)),er(this.q,u(t,18));return;case 21:!this.s&&(this.s=new pe(ns,this,21,17)),kt(this.s),!this.s&&(this.s=new pe(ns,this,21,17)),er(this.s,u(t,18));return;case 22:kt(Vu(this)),er(Vu(this),u(t,18));return}Jl(this,n-dt((yn(),pb)),An((i=u(Un(this,16),29),i||pb),n),t)},s.fi=function(){return yn(),pb},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,184)&&(u(this.Cb,184).tb=null),Mo(this,null);return;case 2:i8(this,null),U9(this,this.D);return;case 5:N8(this,null);return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A);return;case 8:H1e(this,!1);return;case 9:G1e(this,!1);return;case 10:this.u&&hS(this.u);return;case 11:!this.q&&(this.q=new pe(yf,this,11,10)),kt(this.q);return;case 21:!this.s&&(this.s=new pe(ns,this,21,17)),kt(this.s);return;case 22:this.n&&kt(this.n);return}Fl(this,n-dt((yn(),pb)),An((t=u(Un(this,16),29),t||pb),n))},s.mi=function(){var n,t;if(fv(this),lS(this),B0e(this),qW(this),vh(this),Nz(this),NW(this),vE(A3n(Ms(this))),this.s)for(n=0,t=this.s.i;n=0;--t)K(this,t);return hde(this,n)},s.Ek=function(){kt(this)},s.Xi=function(n,t){return gBe(this,n,t)},v($i,"EcoreEList",623),m(491,623,lu,_T),s.Ji=function(){return!1},s.Jj=function(){return this.c},s.Kj=function(){return!1},s.ml=function(){return!0},s.Qi=function(){return!0},s.Ui=function(n,t){return t},s.Wi=function(){return!1},s.c=0,v($i,"EObjectEList",491),m(81,491,lu,mr),s.Kj=function(){return!0},s.kl=function(){return!1},s.$k=function(){return!0},v($i,"EObjectContainmentEList",81),m(543,81,lu,$$),s.Li=function(){this.b=!0},s.Oj=function(){return this.b},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.b,this.b=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.b=!1},s.b=!1,v($i,"EObjectContainmentEList/Unsettable",543),m(1130,543,lu,$De),s.Ri=function(n,t){var i,r;return i=u(RE(this,n,t),87),Fs(this.e)&&s9(this,new tO(this.a,7,(yn(),Jan),ve(t),(r=i.c,X(r,88)?u(r,29):jf),n)),i},s.Sj=function(n,t){return fEn(this,u(n,87),t)},s.Tj=function(n,t){return aEn(this,u(n,87),t)},s.Uj=function(n,t,i){return hAn(this,u(n,87),u(t,87),i)},s.Gj=function(n,t,i,r,c){switch(n){case 3:return dE(this,n,t,i,r,this.i>1);case 5:return dE(this,n,t,i,r,this.i-u(i,16).gc()>0);default:return new L1(this.e,n,this.c,t,i,r,!0)}},s.Rj=function(){return!0},s.Oj=function(){return zQ(this)},s.Ek=function(){kt(this)},v(Fn,"EClassImpl/1",1130),m(1144,1143,eme),s.bj=function(n){var t,i,r,c,o,l,f;if(i=n.ej(),i!=8){if(r=KEn(n),r==0)switch(i){case 1:case 9:{f=n.ij(),f!=null&&(t=Ms(u(f,471)),!t.c&&(t.c=new Ol),sB(t.c,n.hj())),l=n.gj(),l!=null&&(c=u(l,471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),Et(t.c,u(n.hj(),29))));break}case 3:{l=n.gj(),l!=null&&(c=u(l,471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),Et(t.c,u(n.hj(),29))));break}case 5:{if(l=n.gj(),l!=null)for(o=u(l,18).Jc();o.Ob();)c=u(o.Pb(),471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),Et(t.c,u(n.hj(),29)));break}case 4:{f=n.ij(),f!=null&&(c=u(f,471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),sB(t.c,n.hj())));break}case 6:{if(f=n.ij(),f!=null)for(o=u(f,18).Jc();o.Ob();)c=u(o.Pb(),471),(c.Bb&1)==0&&(t=Ms(c),!t.c&&(t.c=new Ol),sB(t.c,n.hj()));break}}this.ol(r)}},s.ol=function(n){AXe(this,n)},s.b=63,v(Fn,"ESuperAdapter",1144),m(1145,1144,eme,$Se),s.ol=function(n){H2(this,n)},v(Fn,"EClassImpl/10",1145),m(1134,699,lu),s.Ci=function(n,t){return lW(this,n,t)},s.Di=function(n){return uHe(this,n)},s.Ei=function(n,t){xO(this,n,t)},s.Fi=function(n){YT(this,n)},s.Yi=function(n){return phe(this,n)},s.Vi=function(n,t){return SY(this,n,t)},s.Uk=function(n,t){throw $(new _t)},s.Gi=function(){return new m4(this)},s.Hi=function(){return new kT(this)},s.Ii=function(n){return hO(this,n)},s.Vk=function(n,t){throw $(new _t)},s.Dk=function(n){return this},s.Oj=function(){return this.i!=0},s.Wb=function(n){throw $(new _t)},s.Ek=function(){throw $(new _t)},v($i,"EcoreEList/UnmodifiableEList",1134),m(333,1134,lu,N3),s.Wi=function(){return!1},v($i,"EcoreEList/UnmodifiableEList/FastCompare",333),m(1137,333,lu,Lze),s.bd=function(n){var t,i,r;if(X(n,179)&&(t=u(n,179),i=t.Jj(),i!=-1)){for(r=this.i;i4)if(this.dk(n)){if(this.$k()){if(r=u(n,52),i=r.Bh(),f=i==this.b&&(this.kl()?r.vh(r.Ch(),u(An(Go(this.b),this.Jj()).Fk(),29).ik())==Oc(u(An(Go(this.b),this.Jj()),19)).n:-1-r.Ch()==this.Jj()),this.ll()&&!f&&!i&&r.Gh()){for(c=0;c1||r==-1)):!1},s.kl=function(){var n,t,i;return t=An(Go(this.b),this.Jj()),X(t,103)?(n=u(t,19),i=Oc(n),!!i):!1},s.ll=function(){var n,t;return t=An(Go(this.b),this.Jj()),X(t,103)?(n=u(t,19),(n.Bb&Ec)!=0):!1},s.bd=function(n){var t,i,r,c;if(r=this.xj(n),r>=0)return r;if(this.ml()){for(i=0,c=this.Cj();i=0;--n)cN(this,n,this.vj(n));return this.Dj()},s.Oc=function(n){var t;if(this.ll())for(t=this.Cj()-1;t>=0;--t)cN(this,t,this.vj(t));return this.Ej(n)},s.Ek=function(){hS(this)},s.Xi=function(n,t){return B$e(this,n,t)},v($i,"DelegatingEcoreEList",744),m(1140,744,rme,VOe),s.oj=function(n,t){Ipn(this,n,u(t,29))},s.pj=function(n){ywn(this,u(n,29))},s.vj=function(n){var t,i;return t=u(K(Vu(this.a),n),87),i=t.c,X(i,88)?u(i,29):(yn(),jf)},s.Aj=function(n){var t,i;return t=u(U2(Vu(this.a),n),87),i=t.c,X(i,88)?u(i,29):(yn(),jf)},s.Bj=function(n,t){return zSn(this,n,u(t,29))},s.Ji=function(){return!1},s.Gj=function(n,t,i,r,c){return null},s.qj=function(){return new zSe(this)},s.rj=function(){kt(Vu(this.a))},s.sj=function(n){return DFe(this,n)},s.tj=function(n){var t,i;for(i=n.Jc();i.Ob();)if(t=i.Pb(),!DFe(this,t))return!1;return!0},s.uj=function(n){var t,i,r;if(X(n,16)&&(r=u(n,16),r.gc()==Vu(this.a).i)){for(t=r.Jc(),i=new ut(this);t.Ob();)if(ue(t.Pb())!==ue(ft(i)))return!1;return!0}return!1},s.wj=function(){var n,t,i,r,c;for(i=1,t=new ut(Vu(this.a));t.e!=t.i.gc();)n=u(ft(t),87),r=(c=n.c,X(c,88)?u(c,29):(yn(),jf)),i=31*i+(r?vw(r):0);return i},s.xj=function(n){var t,i,r,c;for(r=0,i=new ut(Vu(this.a));i.e!=i.i.gc();){if(t=u(ft(i),87),ue(n)===ue((c=t.c,X(c,88)?u(c,29):(yn(),jf))))return r;++r}return-1},s.yj=function(){return Vu(this.a).i==0},s.zj=function(){return null},s.Cj=function(){return Vu(this.a).i},s.Dj=function(){var n,t,i,r,c,o;for(o=Vu(this.a).i,c=oe(Ar,On,1,o,5,1),i=0,t=new ut(Vu(this.a));t.e!=t.i.gc();)n=u(ft(t),87),c[i++]=(r=n.c,X(r,88)?u(r,29):(yn(),jf));return c},s.Ej=function(n){var t,i,r,c,o,l,f;for(f=Vu(this.a).i,n.lengthf&&tr(n,f,null),r=0,i=new ut(Vu(this.a));i.e!=i.i.gc();)t=u(ft(i),87),o=(l=t.c,X(l,88)?u(l,29):(yn(),jf)),tr(n,r++,o);return n},s.Fj=function(){var n,t,i,r,c;for(c=new pd,c.a+="[",n=Vu(this.a),t=0,r=Vu(this.a).i;t>16,c>=0?QQ(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,6,i);case 9:return!this.a&&(this.a=new pe(ed,this,9,5)),Co(this.a,n,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),mb)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),mb)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 6:return dl(this,null,6,i);case 7:return!this.A&&(this.A=new rs(Fo,this,7)),vc(this.A,n,i);case 9:return!this.a&&(this.a=new pe(ed,this,9,5)),vc(this.a,n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),mb)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),mb)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.D!=null&&this.D==this.F;case 3:return!!Fw(this);case 4:return!!O1e(this);case 5:return this.F!=null&&this.F!=this.D&&this.F!=this.B;case 6:return!!R9(this);case 7:return!!this.A&&this.A.i!=0;case 8:return(this.Bb&256)==0;case 9:return!!this.a&&this.a.i!=0}return Ll(this,n-dt((yn(),mb)),An((t=u(Un(this,16),29),t||mb),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:RR(this,Pt(t));return;case 2:xK(this,Pt(t));return;case 5:N8(this,Pt(t));return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A),!this.A&&(this.A=new rs(Fo,this,7)),er(this.A,u(t,18));return;case 8:FB(this,Re($e(t)));return;case 9:!this.a&&(this.a=new pe(ed,this,9,5)),kt(this.a),!this.a&&(this.a=new pe(ed,this,9,5)),er(this.a,u(t,18));return}Jl(this,n-dt((yn(),mb)),An((i=u(Un(this,16),29),i||mb),n),t)},s.fi=function(){return yn(),mb},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,184)&&(u(this.Cb,184).tb=null),Mo(this,null);return;case 2:i8(this,null),U9(this,this.D);return;case 5:N8(this,null);return;case 7:!this.A&&(this.A=new rs(Fo,this,7)),kt(this.A);return;case 8:FB(this,!0);return;case 9:!this.a&&(this.a=new pe(ed,this,9,5)),kt(this.a);return}Fl(this,n-dt((yn(),mb)),An((t=u(Un(this,16),29),t||mb),n))},s.mi=function(){var n,t;if(this.a)for(n=0,t=this.a.i;n>16==5?u(this.Cb,675):null}return Pl(this,n-dt((yn(),Zd)),An((r=u(Un(this,16),29),r||Zd),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),Co(this.Ab,n,i);case 5:return this.Cb&&(i=(c=this.Db>>16,c>=0?IHe(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,5,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),Zd)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),Zd)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 5:return dl(this,null,5,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),Zd)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),Zd)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return this.d!=0;case 3:return!!this.b;case 4:return this.c!=null;case 5:return!!(this.Db>>16==5&&u(this.Cb,675))}return Ll(this,n-dt((yn(),Zd)),An((t=u(Un(this,16),29),t||Zd),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Pt(t));return;case 2:NY(this,u(t,15).a);return;case 3:Pqe(this,u(t,2001));return;case 4:IY(this,Pt(t));return}Jl(this,n-dt((yn(),Zd)),An((i=u(Un(this,16),29),i||Zd),n),t)},s.fi=function(){return yn(),Zd},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Mo(this,null);return;case 2:NY(this,0);return;case 3:Pqe(this,null);return;case 4:IY(this,null);return}Fl(this,n-dt((yn(),Zd)),An((t=u(Un(this,16),29),t||Zd),n))},s.Ib=function(){var n;return n=this.c,n??this.zb},s.b=null,s.c=null,s.d=0,v(Fn,"EEnumLiteralImpl",568);var QBn=Hi(Fn,"EFactoryImpl/InternalEDateTimeFormat");m(485,1,{2076:1},UC),v(Fn,"EFactoryImpl/1ClientInternalEDateTimeFormat",485),m(248,118,{109:1,94:1,93:1,87:1,57:1,114:1,52:1,100:1,248:1,117:1,118:1},hw),s.zh=function(n,t,i){var r;return i=dl(this,n,t,i),this.e&&X(n,179)&&(r=Oz(this,this.e),r!=this.c&&(i=D8(this,r,i))),i},s.Ih=function(n,t,i){var r;switch(n){case 0:return this.f;case 1:return!this.d&&(this.d=new mr(Rc,this,1)),this.d;case 2:return t?Jz(this):this.c;case 3:return this.b;case 4:return this.e;case 5:return t?HQ(this):this.a}return Pl(this,n-dt((yn(),yp)),An((r=u(Un(this,16),29),r||yp),n),t,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return mFe(this,null,i);case 1:return!this.d&&(this.d=new mr(Rc,this,1)),vc(this.d,n,i);case 3:return pFe(this,null,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),yp)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),yp)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.f;case 1:return!!this.d&&this.d.i!=0;case 2:return!!this.c;case 3:return!!this.b;case 4:return!!this.e;case 5:return!!this.a}return Ll(this,n-dt((yn(),yp)),An((t=u(Un(this,16),29),t||yp),n))},s.$h=function(n,t){var i;switch(n){case 0:WHe(this,u(t,87));return;case 1:!this.d&&(this.d=new mr(Rc,this,1)),kt(this.d),!this.d&&(this.d=new mr(Rc,this,1)),er(this.d,u(t,18));return;case 3:u0e(this,u(t,87));return;case 4:x0e(this,u(t,834));return;case 5:q9(this,u(t,143));return}Jl(this,n-dt((yn(),yp)),An((i=u(Un(this,16),29),i||yp),n),t)},s.fi=function(){return yn(),yp},s.hi=function(n){var t;switch(n){case 0:WHe(this,null);return;case 1:!this.d&&(this.d=new mr(Rc,this,1)),kt(this.d);return;case 3:u0e(this,null);return;case 4:x0e(this,null);return;case 5:q9(this,null);return}Fl(this,n-dt((yn(),yp)),An((t=u(Un(this,16),29),t||yp),n))},s.Ib=function(){var n;return n=new il(Jf(this)),n.a+=" (expression: ",YW(this,n),n.a+=")",n.a};var Q8e;v(Fn,"EGenericTypeImpl",248),m(2029,2024,KF),s.Ei=function(n,t){QOe(this,n,t)},s.Uk=function(n,t){return QOe(this,this.gc(),n),t},s.Yi=function(n){return Yu(this.nj(),n)},s.Gi=function(){return this.Hi()},s.nj=function(){return new GSe(this)},s.Hi=function(){return this.Ii(0)},s.Ii=function(n){return this.nj().dd(n)},s.Vk=function(n,t){return P2(this,n,!0),t},s.Ri=function(n,t){var i,r;return r=eW(this,t),i=this.dd(n),i.Rb(r),r},s.Si=function(n,t){var i;P2(this,t,!0),i=this.dd(n),i.Rb(t)},v($i,"AbstractSequentialInternalEList",2029),m(482,2029,KF,jT),s.Yi=function(n){return Yu(this.nj(),n)},s.Gi=function(){return this.b==null?(kd(),kd(),nI):this.ql()},s.nj=function(){return new wTe(this.a,this.b)},s.Hi=function(){return this.b==null?(kd(),kd(),nI):this.ql()},s.Ii=function(n){var t,i;if(this.b==null){if(n<0||n>1)throw $(new jo($S+n+", size=0"));return kd(),kd(),nI}for(i=this.ql(),t=0;t0;)if(t=this.c[--this.d],(!this.e||t.nk()!=U7||t.Jj()!=0)&&(!this.tl()||this.b.Uh(t))){if(o=this.b.Kh(t,this.sl()),this.f=(Tc(),u(t,69).vk()),this.f||t.Hk()){if(this.sl()?(r=u(o,16),this.k=r):(r=u(o,72),this.k=this.j=r),X(this.k,59)?(this.o=this.k.gc(),this.n=this.o):this.p=this.j?this.j.Ii(this.k.gc()):this.k.dd(this.k.gc()),this.p?YGe(this,this.p):uqe(this))return c=this.p?this.p.Ub():this.j?this.j.Yi(--this.n):this.k.Xb(--this.n),this.f?(n=u(c,75),n.Jk(),i=n.kd(),this.i=i):(i=c,this.i=i),this.g=-3,!0}else if(o!=null)return this.k=null,this.p=null,i=o,this.i=i,this.g=-2,!0}return this.k=null,this.p=null,this.g=-1,!1}else return c=this.p?this.p.Ub():this.j?this.j.Yi(--this.n):this.k.Xb(--this.n),this.f?(n=u(c,75),n.Jk(),i=n.kd(),this.i=i):(i=c,this.i=i),this.g=-3,!0}},s.Pb=function(){return OB(this)},s.Tb=function(){return this.a},s.Ub=function(){var n;if(this.g<-1||this.Sb())return--this.a,this.g=0,n=this.i,this.Sb(),n;throw $(new au)},s.Vb=function(){return this.a-1},s.Qb=function(){throw $(new _t)},s.sl=function(){return!1},s.Wb=function(n){throw $(new _t)},s.tl=function(){return!0},s.a=0,s.d=0,s.f=!1,s.g=0,s.n=0,s.o=0;var nI;v($i,"EContentsEList/FeatureIteratorImpl",287),m(700,287,VF,ple),s.sl=function(){return!0},v($i,"EContentsEList/ResolvingFeatureIteratorImpl",700),m(1147,700,VF,IOe),s.tl=function(){return!1},v(Fn,"ENamedElementImpl/1/1",1147),m(1148,287,VF,_Oe),s.tl=function(){return!1},v(Fn,"ENamedElementImpl/1/2",1148),m(39,151,RN,M2,iY,Ir,pY,L1,Pf,The,vLe,Ohe,yLe,Gae,kLe,Ihe,jLe,qae,ELe,Nhe,SLe,uE,tO,$V,Dhe,xLe,Uae,ALe),s.Ij=function(){return ahe(this)},s.Pj=function(){var n;return n=ahe(this),n?n.gk():null},s.fj=function(n){return this.b==-1&&this.a&&(this.b=this.c.Eh(this.a.Jj(),this.a.nk())),this.c.vh(this.b,n)},s.hj=function(){return this.c},s.Qj=function(){var n;return n=ahe(this),n?n.rk():!1},s.b=-1,v(Fn,"ENotificationImpl",39),m(403,293,{109:1,94:1,93:1,158:1,197:1,57:1,62:1,114:1,470:1,52:1,100:1,161:1,403:1,293:1,117:1,118:1},vX),s.xh=function(n){return LHe(this,n)},s.Ih=function(n,t,i){var r,c,o;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),o=this.t,o>1||o==-1;case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return this.Db>>16==10?u(this.Cb,29):null;case 11:return!this.d&&(this.d=new rs(Fo,this,11)),this.d;case 12:return!this.c&&(this.c=new pe(vp,this,12,10)),this.c;case 13:return!this.a&&(this.a=new AT(this,this)),this.a;case 14:return Ts(this)}return Pl(this,n-dt((yn(),e0)),An((r=u(Un(this,16),29),r||e0),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),Co(this.Ab,n,i);case 10:return this.Cb&&(i=(c=this.Db>>16,c>=0?LHe(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,10,i);case 12:return!this.c&&(this.c=new pe(vp,this,12,10)),Co(this.c,n,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),e0)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),e0)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 9:return EV(this,i);case 10:return dl(this,null,10,i);case 11:return!this.d&&(this.d=new rs(Fo,this,11)),vc(this.d,n,i);case 12:return!this.c&&(this.c=new pe(vp,this,12,10)),vc(this.c,n,i);case 14:return vc(Ts(this),n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),e0)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),e0)),n,i)},s.Th=function(n){var t,i,r;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return r=this.t,r>1||r==-1;case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return!!(this.Db>>16==10&&u(this.Cb,29));case 11:return!!this.d&&this.d.i!=0;case 12:return!!this.c&&this.c.i!=0;case 13:return!!this.a&&Ts(this.a.a).i!=0&&!(this.b&&FQ(this.b));case 14:return!!this.b&&FQ(this.b)}return Ll(this,n-dt((yn(),e0)),An((t=u(Un(this,16),29),t||e0),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Td(this,u(t,15).a);return;case 5:O2(this,u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return;case 11:!this.d&&(this.d=new rs(Fo,this,11)),kt(this.d),!this.d&&(this.d=new rs(Fo,this,11)),er(this.d,u(t,18));return;case 12:!this.c&&(this.c=new pe(vp,this,12,10)),kt(this.c),!this.c&&(this.c=new pe(vp,this,12,10)),er(this.c,u(t,18));return;case 13:!this.a&&(this.a=new AT(this,this)),hS(this.a),!this.a&&(this.a=new AT(this,this)),er(this.a,u(t,18));return;case 14:kt(Ts(this)),er(Ts(this),u(t,18));return}Jl(this,n-dt((yn(),e0)),An((i=u(Un(this,16),29),i||e0),n),t)},s.fi=function(){return yn(),e0},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Mo(this,null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Td(this,0);return;case 5:O2(this,1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return;case 11:!this.d&&(this.d=new rs(Fo,this,11)),kt(this.d);return;case 12:!this.c&&(this.c=new pe(vp,this,12,10)),kt(this.c);return;case 13:this.a&&hS(this.a);return;case 14:this.b&&kt(this.b);return}Fl(this,n-dt((yn(),e0)),An((t=u(Un(this,16),29),t||e0),n))},s.mi=function(){var n,t;if(this.c)for(n=0,t=this.c.i;nf&&tr(n,f,null),r=0,i=new ut(Ts(this.a));i.e!=i.i.gc();)t=u(ft(i),87),o=(l=t.c,l||(yn(),ih)),tr(n,r++,o);return n},s.Fj=function(){var n,t,i,r,c;for(c=new pd,c.a+="[",n=Ts(this.a),t=0,r=Ts(this.a).i;t1);case 5:return dE(this,n,t,i,r,this.i-u(i,16).gc()>0);default:return new L1(this.e,n,this.c,t,i,r,!0)}},s.Rj=function(){return!0},s.Oj=function(){return FQ(this)},s.Ek=function(){kt(this)},v(Fn,"EOperationImpl/2",1331),m(493,1,{1999:1,493:1},VCe),v(Fn,"EPackageImpl/1",493),m(14,81,lu,pe),s.gl=function(){return this.d},s.hl=function(){return this.b},s.kl=function(){return!0},s.b=0,v($i,"EObjectContainmentWithInverseEList",14),m(361,14,lu,y4),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectContainmentWithInverseEList/Resolving",361),m(312,361,lu,m2),s.Li=function(){this.a.tb=null},v(Fn,"EPackageImpl/2",312),m(1243,1,{},Ss),v(Fn,"EPackageImpl/3",1243),m(721,44,bv,yoe),s._b=function(n){return $r(n)?RV(this,n):!!Xc(this.f,n)},v(Fn,"EPackageRegistryImpl",721),m(503,293,{109:1,94:1,93:1,158:1,197:1,57:1,2078:1,114:1,470:1,52:1,100:1,161:1,503:1,293:1,117:1,118:1},yX),s.xh=function(n){return PHe(this,n)},s.Ih=function(n,t,i){var r,c,o;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),o=this.t,o>1||o==-1;case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return this.Db>>16==10?u(this.Cb,62):null}return Pl(this,n-dt((yn(),Fm)),An((r=u(Un(this,16),29),r||Fm),n),t,i)},s.Ph=function(n,t,i){var r,c,o;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),Co(this.Ab,n,i);case 10:return this.Cb&&(i=(c=this.Db>>16,c>=0?PHe(this,i):this.Cb.Qh(this,-1-c,null,i))),dl(this,n,10,i)}return o=u(An((r=u(Un(this,16),29),r||(yn(),Fm)),t),69),o.uk().xk(this,Lo(this),t-dt((yn(),Fm)),n,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 9:return EV(this,i);case 10:return dl(this,null,10,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),Fm)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),Fm)),n,i)},s.Th=function(n){var t,i,r;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return r=this.t,r>1||r==-1;case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return!!(this.Db>>16==10&&u(this.Cb,62))}return Ll(this,n-dt((yn(),Fm)),An((t=u(Un(this,16),29),t||Fm),n))},s.fi=function(){return yn(),Fm},v(Fn,"EParameterImpl",503),m(103,451,{109:1,94:1,93:1,158:1,197:1,57:1,19:1,179:1,69:1,114:1,470:1,52:1,100:1,161:1,103:1,451:1,293:1,117:1,118:1,682:1},kle),s.Ih=function(n,t,i){var r,c,o,l;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return Pn(),(this.Bb&256)!=0;case 3:return Pn(),(this.Bb&512)!=0;case 4:return ve(this.s);case 5:return ve(this.t);case 6:return Pn(),l=this.t,l>1||l==-1;case 7:return Pn(),c=this.s,c>=1;case 8:return t?ff(this):this.r;case 9:return this.q;case 10:return Pn(),(this.Bb&qf)!=0;case 11:return Pn(),(this.Bb&U0)!=0;case 12:return Pn(),(this.Bb&W2)!=0;case 13:return this.j;case 14:return S8(this);case 15:return Pn(),(this.Bb&as)!=0;case 16:return Pn(),(this.Bb&yh)!=0;case 17:return E2(this);case 18:return Pn(),(this.Bb&Bu)!=0;case 19:return Pn(),o=Oc(this),!!(o&&(o.Bb&Bu)!=0);case 20:return Pn(),(this.Bb&Ec)!=0;case 21:return t?Oc(this):this.b;case 22:return t?d1e(this):HPe(this);case 23:return!this.a&&(this.a=new $3(Rm,this,23)),this.a}return Pl(this,n-dt((yn(),Wv)),An((r=u(Un(this,16),29),r||Wv),n),t,i)},s.Th=function(n){var t,i,r,c;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return(this.Bb&256)==0;case 3:return(this.Bb&512)==0;case 4:return this.s!=0;case 5:return this.t!=1;case 6:return c=this.t,c>1||c==-1;case 7:return i=this.s,i>=1;case 8:return!!this.r&&!this.q.e&&Sw(this.q).i==0;case 9:return!!this.q&&!(this.r&&!this.q.e&&Sw(this.q).i==0);case 10:return(this.Bb&qf)==0;case 11:return(this.Bb&U0)!=0;case 12:return(this.Bb&W2)!=0;case 13:return this.j!=null;case 14:return S8(this)!=null;case 15:return(this.Bb&as)!=0;case 16:return(this.Bb&yh)!=0;case 17:return!!E2(this);case 18:return(this.Bb&Bu)!=0;case 19:return r=Oc(this),!!r&&(r.Bb&Bu)!=0;case 20:return(this.Bb&Ec)==0;case 21:return!!this.b;case 22:return!!HPe(this);case 23:return!!this.a&&this.a.i!=0}return Ll(this,n-dt((yn(),Wv)),An((t=u(Un(this,16),29),t||Wv),n))},s.$h=function(n,t){var i,r;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:UV(this,Pt(t));return;case 2:Id(this,Re($e(t)));return;case 3:_d(this,Re($e(t)));return;case 4:Td(this,u(t,15).a);return;case 5:O2(this,u(t,15).a);return;case 8:ng(this,u(t,143));return;case 9:r=za(this,u(t,87),null),r&&r.mj();return;case 10:o8(this,Re($e(t)));return;case 11:f8(this,Re($e(t)));return;case 12:l8(this,Re($e(t)));return;case 13:Ise(this,Pt(t));return;case 15:s8(this,Re($e(t)));return;case 16:a8(this,Re($e(t)));return;case 18:D4n(this,Re($e(t)));return;case 20:Y1e(this,Re($e(t)));return;case 21:Khe(this,u(t,19));return;case 23:!this.a&&(this.a=new $3(Rm,this,23)),kt(this.a),!this.a&&(this.a=new $3(Rm,this,23)),er(this.a,u(t,18));return}Jl(this,n-dt((yn(),Wv)),An((i=u(Un(this,16),29),i||Wv),n),t)},s.fi=function(){return yn(),Wv},s.hi=function(n){var t,i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:X(this.Cb,88)&&H2(Ms(u(this.Cb,88)),4),Mo(this,null);return;case 2:Id(this,!0);return;case 3:_d(this,!0);return;case 4:Td(this,0);return;case 5:O2(this,1);return;case 8:ng(this,null);return;case 9:i=za(this,null,null),i&&i.mj();return;case 10:o8(this,!0);return;case 11:f8(this,!1);return;case 12:l8(this,!1);return;case 13:this.i=null,EB(this,null);return;case 15:s8(this,!1);return;case 16:a8(this,!1);return;case 18:Q1e(this,!1),X(this.Cb,88)&&H2(Ms(u(this.Cb,88)),2);return;case 20:Y1e(this,!0);return;case 21:Khe(this,null);return;case 23:!this.a&&(this.a=new $3(Rm,this,23)),kt(this.a);return}Fl(this,n-dt((yn(),Wv)),An((t=u(Un(this,16),29),t||Wv),n))},s.mi=function(){d1e(this),L9(Vc((ls(),nc),this)),ff(this),this.Bb|=1},s.sk=function(){return Oc(this)},s.Zk=function(){var n;return n=Oc(this),!!n&&(n.Bb&Bu)!=0},s.$k=function(){return(this.Bb&Bu)!=0},s._k=function(){return(this.Bb&Ec)!=0},s.Wk=function(n,t){return this.c=null,z1e(this,n,t)},s.Ib=function(){var n;return(this.Db&64)!=0?zz(this):(n=new cf(zz(this)),n.a+=" (containment: ",md(n,(this.Bb&Bu)!=0),n.a+=", resolveProxies: ",md(n,(this.Bb&Ec)!=0),n.a+=")",n.a)},v(Fn,"EReferenceImpl",103),m(549,118,{109:1,45:1,94:1,93:1,136:1,57:1,114:1,52:1,100:1,549:1,117:1,118:1},Ph),s.Fb=function(n){return this===n},s.jd=function(){return this.b},s.kd=function(){return this.c},s.Hb=function(){return vw(this)},s.Ai=function(n){K3n(this,Pt(n))},s.ld=function(n){return $3n(this,Pt(n))},s.Ih=function(n,t,i){var r;switch(n){case 0:return this.b;case 1:return this.c}return Pl(this,n-dt((yn(),Ac)),An((r=u(Un(this,16),29),r||Ac),n),t,i)},s.Th=function(n){var t;switch(n){case 0:return this.b!=null;case 1:return this.c!=null}return Ll(this,n-dt((yn(),Ac)),An((t=u(Un(this,16),29),t||Ac),n))},s.$h=function(n,t){var i;switch(n){case 0:V3n(this,Pt(t));return;case 1:Jhe(this,Pt(t));return}Jl(this,n-dt((yn(),Ac)),An((i=u(Un(this,16),29),i||Ac),n),t)},s.fi=function(){return yn(),Ac},s.hi=function(n){var t;switch(n){case 0:qhe(this,null);return;case 1:Jhe(this,null);return}Fl(this,n-dt((yn(),Ac)),An((t=u(Un(this,16),29),t||Ac),n))},s.yi=function(){var n;return this.a==-1&&(n=this.b,this.a=n==null?0:Od(n)),this.a},s.zi=function(n){this.a=n},s.Ib=function(){var n;return(this.Db&64)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (key: ",Bc(n,this.b),n.a+=", value: ",Bc(n,this.c),n.a+=")",n.a)},s.a=-1,s.b=null,s.c=null;var Iu=v(Fn,"EStringToStringMapEntryImpl",549),Wan=Hi($i,"FeatureMap/Entry/Internal");m(562,1,YF),s.vl=function(n){return this.wl(u(n,52))},s.wl=function(n){return this.vl(n)},s.Fb=function(n){var t,i;return this===n?!0:X(n,75)?(t=u(n,75),t.Jk()==this.c?(i=this.kd(),i==null?t.kd()==null:di(i,t.kd())):!1):!1},s.Jk=function(){return this.c},s.Hb=function(){var n;return n=this.kd(),Oi(this.c)^(n==null?0:Oi(n))},s.Ib=function(){var n,t;return n=this.c,t=sl(n.ok()).vi(),n.ve(),(t!=null&&t.length!=0?t+":"+n.ve():n.ve())+"="+this.kd()},v(Fn,"EStructuralFeatureImpl/BasicFeatureMapEntry",562),m(777,562,YF,Mle),s.wl=function(n){return new Mle(this.c,n)},s.kd=function(){return this.a},s.xl=function(n,t,i){return y7n(this,n,this.a,t,i)},s.yl=function(n,t,i){return k7n(this,n,this.a,t,i)},v(Fn,"EStructuralFeatureImpl/ContainmentUpdatingFeatureMapEntry",777),m(1304,1,{},YCe),s.wk=function(n,t,i,r,c){var o;return o=u(F9(n,this.b),219),o.Wl(this.a).Dk(r)},s.xk=function(n,t,i,r,c){var o;return o=u(F9(n,this.b),219),o.Nl(this.a,r,c)},s.yk=function(n,t,i,r,c){var o;return o=u(F9(n,this.b),219),o.Ol(this.a,r,c)},s.zk=function(n,t,i){var r;return r=u(F9(n,this.b),219),r.Wl(this.a).Oj()},s.Ak=function(n,t,i,r){var c;c=u(F9(n,this.b),219),c.Wl(this.a).Wb(r)},s.Bk=function(n,t,i){return u(F9(n,this.b),219).Wl(this.a)},s.Ck=function(n,t,i){var r;r=u(F9(n,this.b),219),r.Wl(this.a).Ek()},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateFeatureMapDelegator",1304),m(89,1,{},Ed,Jb,xd,Kb),s.wk=function(n,t,i,r,c){var o;if(o=t.ii(i),o==null&&t.ji(i,o=Wz(this,n)),!c)switch(this.e){case 50:case 41:return u(o,586)._j();case 40:return u(o,219).Tl()}return o},s.xk=function(n,t,i,r,c){var o,l;return l=t.ii(i),l==null&&t.ji(i,l=Wz(this,n)),o=u(l,72).Uk(r,c),o},s.yk=function(n,t,i,r,c){var o;return o=t.ii(i),o!=null&&(c=u(o,72).Vk(r,c)),c},s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null&&u(r,77).Oj()},s.Ak=function(n,t,i,r){var c;c=u(t.ii(i),77),!c&&t.ji(i,c=Wz(this,n)),c.Wb(r)},s.Bk=function(n,t,i){var r,c;return c=t.ii(i),c==null&&t.ji(i,c=Wz(this,n)),X(c,77)?u(c,77):(r=u(t.ii(i),16),new JSe(r))},s.Ck=function(n,t,i){var r;r=u(t.ii(i),77),!r&&t.ji(i,r=Wz(this,n)),r.Ek()},s.b=0,s.e=0,v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateMany",89),m(498,1,{}),s.xk=function(n,t,i,r,c){throw $(new _t)},s.yk=function(n,t,i,r,c){throw $(new _t)},s.Bk=function(n,t,i){return new XIe(this,n,t,i)};var h1;v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingle",498),m(1321,1,_ne,XIe),s.Dk=function(n){return this.a.wk(this.c,this.d,this.b,n,!0)},s.Oj=function(){return this.a.zk(this.c,this.d,this.b)},s.Wb=function(n){this.a.Ak(this.c,this.d,this.b,n)},s.Ek=function(){this.a.Ck(this.c,this.d,this.b)},s.b=0,v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingle/1",1321),m(770,498,{},bae),s.wk=function(n,t,i,r,c){return $W(n,n.Mh(),n.Ch())==this.b?this._k()&&r?SW(n):n.Mh():null},s.xk=function(n,t,i,r,c){var o,l;return n.Mh()&&(c=(o=n.Ch(),o>=0?n.xh(c):n.Mh().Qh(n,-1-o,null,c))),l=zi(n.Ah(),this.e),n.zh(r,l,c)},s.yk=function(n,t,i,r,c){var o;return o=zi(n.Ah(),this.e),n.zh(null,o,c)},s.zk=function(n,t,i){var r;return r=zi(n.Ah(),this.e),!!n.Mh()&&n.Ch()==r},s.Ak=function(n,t,i,r){var c,o,l,f,h;if(r!=null&&!FW(this.a,r))throw $(new l9(QF+(X(r,57)?c0e(u(r,57).Ah()):xhe(Us(r)))+WF+this.a+"'"));if(c=n.Mh(),l=zi(n.Ah(),this.e),ue(r)!==ue(c)||n.Ch()!=l&&r!=null){if(w8(n,u(r,57)))throw $(new Gn(LS+n.Ib()));h=null,c&&(h=(o=n.Ch(),o>=0?n.xh(h):n.Mh().Qh(n,-1-o,null,h))),f=u(r,52),f&&(h=f.Oh(n,zi(f.Ah(),this.b),null,h)),h=n.zh(f,l,h),h&&h.mj()}else n.sh()&&n.th()&&ai(n,new Ir(n,1,l,r,r))},s.Ck=function(n,t,i){var r,c,o,l;r=n.Mh(),r?(l=(c=n.Ch(),c>=0?n.xh(null):n.Mh().Qh(n,-1-c,null,null)),o=zi(n.Ah(),this.e),l=n.zh(null,o,l),l&&l.mj()):n.sh()&&n.th()&&ai(n,new uE(n,1,this.e,null,null))},s._k=function(){return!1},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleContainer",770),m(1305,770,{},BNe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleContainerResolving",1305),m(560,498,{}),s.wk=function(n,t,i,r,c){var o;return o=t.ii(i),o==null?this.b:ue(o)===ue(h1)?null:o},s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null&&(ue(r)===ue(h1)||!di(r,this.b))},s.Ak=function(n,t,i,r){var c,o;n.sh()&&n.th()?(c=(o=t.ii(i),o==null?this.b:ue(o)===ue(h1)?null:o),r==null?this.c!=null?(t.ji(i,null),r=this.b):this.b!=null?t.ji(i,h1):t.ji(i,null):(this.zl(r),t.ji(i,r)),ai(n,this.d.Al(n,1,this.e,c,r))):r==null?this.c!=null?t.ji(i,null):this.b!=null?t.ji(i,h1):t.ji(i,null):(this.zl(r),t.ji(i,r))},s.Ck=function(n,t,i){var r,c;n.sh()&&n.th()?(r=(c=t.ii(i),c==null?this.b:ue(c)===ue(h1)?null:c),t.ki(i),ai(n,this.d.Al(n,1,this.e,r,this.b))):t.ki(i)},s.zl=function(n){throw $(new WSe)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData",560),m(Ev,1,{},cw),s.Al=function(n,t,i,r,c){return new uE(n,t,i,r,c)},s.Bl=function(n,t,i,r,c,o){return new $V(n,t,i,r,c,o)};var W8e,Z8e,e7e,n7e,t7e,i7e,r7e,Jce,c7e;v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator",Ev),m(1322,Ev,{},jL),s.Al=function(n,t,i,r,c){return new Uae(n,t,i,Re($e(r)),Re($e(c)))},s.Bl=function(n,t,i,r,c,o){return new ALe(n,t,i,Re($e(r)),Re($e(c)),o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/1",1322),m(1323,Ev,{},EL),s.Al=function(n,t,i,r,c){return new The(n,t,i,u(r,221).a,u(c,221).a)},s.Bl=function(n,t,i,r,c,o){return new vLe(n,t,i,u(r,221).a,u(c,221).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/2",1323),m(1324,Ev,{},SL),s.Al=function(n,t,i,r,c){return new Ohe(n,t,i,u(r,180).a,u(c,180).a)},s.Bl=function(n,t,i,r,c,o){return new yLe(n,t,i,u(r,180).a,u(c,180).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/3",1324),m(1325,Ev,{},dU),s.Al=function(n,t,i,r,c){return new Gae(n,t,i,ne(re(r)),ne(re(c)))},s.Bl=function(n,t,i,r,c,o){return new kLe(n,t,i,ne(re(r)),ne(re(c)),o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/4",1325),m(1326,Ev,{},Jk),s.Al=function(n,t,i,r,c){return new Ihe(n,t,i,u(r,164).a,u(c,164).a)},s.Bl=function(n,t,i,r,c,o){return new jLe(n,t,i,u(r,164).a,u(c,164).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/5",1326),m(1327,Ev,{},$h),s.Al=function(n,t,i,r,c){return new qae(n,t,i,u(r,15).a,u(c,15).a)},s.Bl=function(n,t,i,r,c,o){return new ELe(n,t,i,u(r,15).a,u(c,15).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/6",1327),m(1328,Ev,{},h0),s.Al=function(n,t,i,r,c){return new Nhe(n,t,i,u(r,190).a,u(c,190).a)},s.Bl=function(n,t,i,r,c,o){return new SLe(n,t,i,u(r,190).a,u(c,190).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/7",1328),m(1329,Ev,{},xL),s.Al=function(n,t,i,r,c){return new Dhe(n,t,i,u(r,191).a,u(c,191).a)},s.Bl=function(n,t,i,r,c,o){return new xLe(n,t,i,u(r,191).a,u(c,191).a,o)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleData/NotificationCreator/8",1329),m(1307,560,{},QIe),s.zl=function(n){if(!this.a.dk(n))throw $(new l9(QF+Us(n)+WF+this.a+"'"))},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataDynamic",1307),m(1308,560,{},LDe),s.zl=function(n){},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataStatic",1308),m(771,560,{}),s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null},s.Ak=function(n,t,i,r){var c,o;n.sh()&&n.th()?(c=!0,o=t.ii(i),o==null?(c=!1,o=this.b):ue(o)===ue(h1)&&(o=null),r==null?this.c!=null?(t.ji(i,null),r=this.b):t.ji(i,h1):(this.zl(r),t.ji(i,r)),ai(n,this.d.Bl(n,1,this.e,o,r,!c))):r==null?this.c!=null?t.ji(i,null):t.ji(i,h1):(this.zl(r),t.ji(i,r))},s.Ck=function(n,t,i){var r,c;n.sh()&&n.th()?(r=!0,c=t.ii(i),c==null?(r=!1,c=this.b):ue(c)===ue(h1)&&(c=null),t.ki(i),ai(n,this.d.Bl(n,2,this.e,c,this.b,r))):t.ki(i)},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettable",771),m(1309,771,{},WIe),s.zl=function(n){if(!this.a.dk(n))throw $(new l9(QF+Us(n)+WF+this.a+"'"))},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettableDynamic",1309),m(1310,771,{},PDe),s.zl=function(n){},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleDataUnsettableStatic",1310),m(402,498,{},sR),s.wk=function(n,t,i,r,c){var o,l,f,h,b;if(b=t.ii(i),this.rk()&&ue(b)===ue(h1))return null;if(this._k()&&r&&b!=null){if(f=u(b,52),f.Sh()&&(h=$0(n,f),f!=h)){if(!FW(this.a,h))throw $(new l9(QF+Us(h)+WF+this.a+"'"));t.ji(i,b=h),this.$k()&&(o=u(h,52),l=f.Qh(n,this.b?zi(f.Ah(),this.b):-1-zi(n.Ah(),this.e),null,null),!o.Mh()&&(l=o.Oh(n,this.b?zi(o.Ah(),this.b):-1-zi(n.Ah(),this.e),null,l)),l&&l.mj()),n.sh()&&n.th()&&ai(n,new uE(n,9,this.e,f,h))}return b}else return b},s.xk=function(n,t,i,r,c){var o,l;return l=t.ii(i),ue(l)===ue(h1)&&(l=null),t.ji(i,r),this.Kj()?ue(l)!==ue(r)&&l!=null&&(o=u(l,52),c=o.Qh(n,zi(o.Ah(),this.b),null,c)):this.$k()&&l!=null&&(c=u(l,52).Qh(n,-1-zi(n.Ah(),this.e),null,c)),n.sh()&&n.th()&&(!c&&(c=new m0(4)),c.lj(new uE(n,1,this.e,l,r))),c},s.yk=function(n,t,i,r,c){var o;return o=t.ii(i),ue(o)===ue(h1)&&(o=null),t.ki(i),n.sh()&&n.th()&&(!c&&(c=new m0(4)),this.rk()?c.lj(new uE(n,2,this.e,o,null)):c.lj(new uE(n,1,this.e,o,null))),c},s.zk=function(n,t,i){var r;return r=t.ii(i),r!=null},s.Ak=function(n,t,i,r){var c,o,l,f,h;if(r!=null&&!FW(this.a,r))throw $(new l9(QF+(X(r,57)?c0e(u(r,57).Ah()):xhe(Us(r)))+WF+this.a+"'"));h=t.ii(i),f=h!=null,this.rk()&&ue(h)===ue(h1)&&(h=null),l=null,this.Kj()?ue(h)!==ue(r)&&(h!=null&&(c=u(h,52),l=c.Qh(n,zi(c.Ah(),this.b),null,l)),r!=null&&(c=u(r,52),l=c.Oh(n,zi(c.Ah(),this.b),null,l))):this.$k()&&ue(h)!==ue(r)&&(h!=null&&(l=u(h,52).Qh(n,-1-zi(n.Ah(),this.e),null,l)),r!=null&&(l=u(r,52).Oh(n,-1-zi(n.Ah(),this.e),null,l))),r==null&&this.rk()?t.ji(i,h1):t.ji(i,r),n.sh()&&n.th()?(o=new $V(n,1,this.e,h,r,this.rk()&&!f),l?(l.lj(o),l.mj()):ai(n,o)):l&&l.mj()},s.Ck=function(n,t,i){var r,c,o,l,f;f=t.ii(i),l=f!=null,this.rk()&&ue(f)===ue(h1)&&(f=null),o=null,f!=null&&(this.Kj()?(r=u(f,52),o=r.Qh(n,zi(r.Ah(),this.b),null,o)):this.$k()&&(o=u(f,52).Qh(n,-1-zi(n.Ah(),this.e),null,o))),t.ki(i),n.sh()&&n.th()?(c=new $V(n,this.rk()?2:1,this.e,f,null,l),o?(o.lj(c),o.mj()):ai(n,c)):o&&o.mj()},s.Kj=function(){return!1},s.$k=function(){return!1},s._k=function(){return!1},s.rk=function(){return!1},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObject",402),m(561,402,{},RK),s.$k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainment",561),m(1313,561,{},POe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentResolving",1313),m(773,561,{},mle),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentUnsettable",773),m(1315,773,{},$Oe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentUnsettableResolving",1315),m(638,561,{},WK),s.Kj=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverse",638),m(1314,638,{},zNe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseResolving",1314),m(774,638,{},Zle),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseUnsettable",774),m(1316,774,{},FNe),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectContainmentWithInverseUnsettableResolving",1316),m(639,402,{},vle),s._k=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolving",639),m(1317,639,{},ROe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingUnsettable",1317),m(775,639,{},efe),s.Kj=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingWithInverse",775),m(1318,775,{},JNe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectResolvingWithInverseUnsettable",1318),m(1311,402,{},BOe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectUnsettable",1311),m(772,402,{},nfe),s.Kj=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectWithInverse",772),m(1312,772,{},HNe),s.rk=function(){return!0},v(Fn,"EStructuralFeatureImpl/InternalSettingDelegateSingleEObjectWithInverseUnsettable",1312),m(776,562,YF,Qfe),s.wl=function(n){return new Qfe(this.a,this.c,n)},s.kd=function(){return this.b},s.xl=function(n,t,i){return p9n(this,n,this.b,i)},s.yl=function(n,t,i){return m9n(this,n,this.b,i)},v(Fn,"EStructuralFeatureImpl/InverseUpdatingFeatureMapEntry",776),m(1319,1,_ne,JSe),s.Dk=function(n){return this.a},s.Oj=function(){return X(this.a,98)?u(this.a,98).Oj():!this.a.dc()},s.Wb=function(n){this.a.$b(),this.a.Fc(u(n,16))},s.Ek=function(){X(this.a,98)?u(this.a,98).Ek():this.a.$b()},v(Fn,"EStructuralFeatureImpl/SettingMany",1319),m(1320,562,YF,gPe),s.vl=function(n){return new FK((Ei(),aA),this.b.oi(this.a,n))},s.kd=function(){return null},s.xl=function(n,t,i){return i},s.yl=function(n,t,i){return i},v(Fn,"EStructuralFeatureImpl/SimpleContentFeatureMapEntry",1320),m(640,562,YF,FK),s.vl=function(n){return new FK(this.c,n)},s.kd=function(){return this.a},s.xl=function(n,t,i){return i},s.yl=function(n,t,i){return i},v(Fn,"EStructuralFeatureImpl/SimpleFeatureMapEntry",640),m(396,492,Mh,Ol),s.$i=function(n){return oe(vf,On,29,n,0,1)},s.Wi=function(){return!1},v(Fn,"ESuperAdapter/1",396),m(446,439,{109:1,94:1,93:1,158:1,197:1,57:1,114:1,834:1,52:1,100:1,161:1,446:1,117:1,118:1},Hk),s.Ih=function(n,t,i){var r;switch(n){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),this.Ab;case 1:return this.zb;case 2:return!this.a&&(this.a=new tE(this,Rc,this)),this.a}return Pl(this,n-dt((yn(),kp)),An((r=u(Un(this,16),29),r||kp),n),t,i)},s.Rh=function(n,t,i){var r,c;switch(t){case 0:return!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),vc(this.Ab,n,i);case 2:return!this.a&&(this.a=new tE(this,Rc,this)),vc(this.a,n,i)}return c=u(An((r=u(Un(this,16),29),r||(yn(),kp)),t),69),c.uk().yk(this,Lo(this),t-dt((yn(),kp)),n,i)},s.Th=function(n){var t;switch(n){case 0:return!!this.Ab&&this.Ab.i!=0;case 1:return this.zb!=null;case 2:return!!this.a&&this.a.i!=0}return Ll(this,n-dt((yn(),kp)),An((t=u(Un(this,16),29),t||kp),n))},s.$h=function(n,t){var i;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab),!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),er(this.Ab,u(t,18));return;case 1:Mo(this,Pt(t));return;case 2:!this.a&&(this.a=new tE(this,Rc,this)),kt(this.a),!this.a&&(this.a=new tE(this,Rc,this)),er(this.a,u(t,18));return}Jl(this,n-dt((yn(),kp)),An((i=u(Un(this,16),29),i||kp),n),t)},s.fi=function(){return yn(),kp},s.hi=function(n){var t;switch(n){case 0:!this.Ab&&(this.Ab=new pe(Zt,this,0,3)),kt(this.Ab);return;case 1:Mo(this,null);return;case 2:!this.a&&(this.a=new tE(this,Rc,this)),kt(this.a);return}Fl(this,n-dt((yn(),kp)),An((t=u(Un(this,16),29),t||kp),n))},v(Fn,"ETypeParameterImpl",446),m(447,81,lu,tE),s.Lj=function(n,t){return lMn(this,u(n,87),t)},s.Mj=function(n,t){return fMn(this,u(n,87),t)},v(Fn,"ETypeParameterImpl/1",447),m(637,44,bv,kX),s.ec=function(){return new OP(this)},v(Fn,"ETypeParameterImpl/2",637),m(557,Ha,fs,OP),s.Ec=function(n){return mNe(this,u(n,87))},s.Fc=function(n){var t,i,r;for(r=!1,i=n.Jc();i.Ob();)t=u(i.Pb(),87),ei(this.a,t,"")==null&&(r=!0);return r},s.$b=function(){Hu(this.a)},s.Gc=function(n){return so(this.a,n)},s.Jc=function(){var n;return n=new D2(new on(this.a).a),new NP(n)},s.Kc=function(n){return n$e(this,n)},s.gc=function(){return xj(this.a)},v(Fn,"ETypeParameterImpl/2/1",557),m(558,1,Fr,NP),s.Nb=function(n){Zr(this,n)},s.Pb=function(){return u(Q3(this.a).jd(),87)},s.Ob=function(){return this.a.b},s.Qb=function(){gRe(this.a)},v(Fn,"ETypeParameterImpl/2/1/1",558),m(1281,44,bv,Nxe),s._b=function(n){return $r(n)?RV(this,n):!!Xc(this.f,n)},s.xc=function(n){var t,i;return t=$r(n)?lo(this,n):du(Xc(this.f,n)),X(t,835)?(i=u(t,835),t=i.Ik(),ei(this,u(n,241),t),t):t??(n==null?(BX(),ehn):null)},v(Fn,"EValidatorRegistryImpl",1281),m(1303,710,{109:1,94:1,93:1,469:1,158:1,57:1,114:1,2002:1,52:1,100:1,161:1,117:1,118:1},uw),s.oi=function(n,t){switch(n.fk()){case 21:case 22:case 23:case 24:case 26:case 31:case 32:case 37:case 38:case 39:case 40:case 43:case 44:case 48:case 49:case 20:return t==null?null:su(t);case 25:return T8n(t);case 27:return G9n(t);case 28:return q9n(t);case 29:return t==null?null:FTe(cA[0],u(t,205));case 41:return t==null?"":Db(u(t,298));case 42:return su(t);case 50:return Pt(t);default:throw $(new Gn(r7+n.ve()+ip))}},s.pi=function(n){var t,i,r,c,o,l,f,h,b,p,y,S,A,O,I,R;switch(n.G==-1&&(n.G=(S=sl(n),S?Ld(S.si(),n):-1)),n.G){case 0:return i=new mX,i;case 1:return t=new Mb,t;case 2:return r=new ij,r;case 4:return c=new _P,c;case 5:return o=new Oxe,o;case 6:return l=new XSe,l;case 7:return f=new OC,f;case 10:return b=new p3,b;case 11:return p=new vX,p;case 12:return y=new c_e,y;case 13:return A=new yX,A;case 14:return O=new kle,O;case 17:return I=new Ph,I;case 18:return h=new hw,h;case 19:return R=new Hk,R;default:throw $(new Gn(mne+n.zb+ip))}},s.qi=function(n,t){switch(n.fk()){case 20:return t==null?null:new Joe(t);case 21:return t==null?null:new E0(t);case 23:case 22:return t==null?null:MEn(t);case 26:case 24:return t==null?null:sO(hl(t,-128,127)<<24>>24);case 25:return EOn(t);case 27:return lxn(t);case 28:return fxn(t);case 29:return TMn(t);case 32:case 31:return t==null?null:F2(t);case 38:case 37:return t==null?null:new aoe(t);case 40:case 39:return t==null?null:ve(hl(t,Xr,oi));case 41:return null;case 42:return t==null,null;case 44:case 43:return t==null?null:R2(Qz(t));case 49:case 48:return t==null?null:c8(hl(t,ZF,32767)<<16>>16);case 50:return t;default:throw $(new Gn(r7+n.ve()+ip))}},v(Fn,"EcoreFactoryImpl",1303),m(548,184,{109:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,2e3:1,52:1,100:1,161:1,184:1,548:1,117:1,118:1,680:1},OIe),s.gb=!1,s.hb=!1;var u7e,Zan=!1;v(Fn,"EcorePackageImpl",548),m(1199,1,{835:1},m3),s.Ik=function(){return fOe(),nhn},v(Fn,"EcorePackageImpl/1",1199),m(1208,1,ii,ow),s.dk=function(n){return X(n,158)},s.ek=function(n){return oe(QD,On,158,n,0,1)},v(Fn,"EcorePackageImpl/10",1208),m(1209,1,ii,tC),s.dk=function(n){return X(n,197)},s.ek=function(n){return oe(Ice,On,197,n,0,1)},v(Fn,"EcorePackageImpl/11",1209),m(1210,1,ii,iC),s.dk=function(n){return X(n,57)},s.ek=function(n){return oe(wb,On,57,n,0,1)},v(Fn,"EcorePackageImpl/12",1210),m(1211,1,ii,d0),s.dk=function(n){return X(n,403)},s.ek=function(n){return oe(yf,ime,62,n,0,1)},v(Fn,"EcorePackageImpl/13",1211),m(1212,1,ii,AL),s.dk=function(n){return X(n,241)},s.ek=function(n){return oe(xa,On,241,n,0,1)},v(Fn,"EcorePackageImpl/14",1212),m(1213,1,ii,B5),s.dk=function(n){return X(n,503)},s.ek=function(n){return oe(vp,On,2078,n,0,1)},v(Fn,"EcorePackageImpl/15",1213),m(1214,1,ii,G6),s.dk=function(n){return X(n,103)},s.ek=function(n){return oe(Bm,jv,19,n,0,1)},v(Fn,"EcorePackageImpl/16",1214),m(1215,1,ii,q6),s.dk=function(n){return X(n,179)},s.ek=function(n){return oe(ns,jv,179,n,0,1)},v(Fn,"EcorePackageImpl/17",1215),m(1216,1,ii,z5),s.dk=function(n){return X(n,470)},s.ek=function(n){return oe($m,On,470,n,0,1)},v(Fn,"EcorePackageImpl/18",1216),m(1217,1,ii,ML),s.dk=function(n){return X(n,549)},s.ek=function(n){return oe(Iu,BZe,549,n,0,1)},v(Fn,"EcorePackageImpl/19",1217),m(1200,1,ii,CL),s.dk=function(n){return X(n,335)},s.ek=function(n){return oe(Rm,jv,38,n,0,1)},v(Fn,"EcorePackageImpl/2",1200),m(1218,1,ii,U6),s.dk=function(n){return X(n,248)},s.ek=function(n){return oe(Rc,ten,87,n,0,1)},v(Fn,"EcorePackageImpl/20",1218),m(1219,1,ii,TL),s.dk=function(n){return X(n,446)},s.ek=function(n){return oe(Fo,On,834,n,0,1)},v(Fn,"EcorePackageImpl/21",1219),m(1220,1,ii,Gk),s.dk=function(n){return o2(n)},s.ek=function(n){return oe(Yi,Se,473,n,8,1)},v(Fn,"EcorePackageImpl/22",1220),m(1221,1,ii,OL),s.dk=function(n){return X(n,195)},s.ek=function(n){return oe(ds,Se,195,n,0,2)},v(Fn,"EcorePackageImpl/23",1221),m(1222,1,ii,bU),s.dk=function(n){return X(n,221)},s.ek=function(n){return oe(py,Se,221,n,0,1)},v(Fn,"EcorePackageImpl/24",1222),m(1223,1,ii,gU),s.dk=function(n){return X(n,180)},s.ek=function(n){return oe(XS,Se,180,n,0,1)},v(Fn,"EcorePackageImpl/25",1223),m(1224,1,ii,Ju),s.dk=function(n){return X(n,205)},s.ek=function(n){return oe(lJ,Se,205,n,0,1)},v(Fn,"EcorePackageImpl/26",1224),m(1225,1,ii,Io),s.dk=function(n){return!1},s.ek=function(n){return oe(S7e,On,2174,n,0,1)},v(Fn,"EcorePackageImpl/27",1225),m(1226,1,ii,Hc),s.dk=function(n){return s2(n)},s.ek=function(n){return oe(gr,Se,346,n,7,1)},v(Fn,"EcorePackageImpl/28",1226),m(1227,1,ii,nu),s.dk=function(n){return X(n,61)},s.ek=function(n){return oe(B8e,Z2,61,n,0,1)},v(Fn,"EcorePackageImpl/29",1227),m(1201,1,ii,io),s.dk=function(n){return X(n,504)},s.ek=function(n){return oe(Zt,{3:1,4:1,5:1,1995:1},587,n,0,1)},v(Fn,"EcorePackageImpl/3",1201),m(1228,1,ii,v1),s.dk=function(n){return X(n,568)},s.ek=function(n){return oe(J8e,On,2001,n,0,1)},v(Fn,"EcorePackageImpl/30",1228),m(1229,1,ii,Gp),s.dk=function(n){return X(n,163)},s.ek=function(n){return oe(a7e,Z2,163,n,0,1)},v(Fn,"EcorePackageImpl/31",1229),m(1230,1,ii,F5),s.dk=function(n){return X(n,75)},s.ek=function(n){return oe(SG,aen,75,n,0,1)},v(Fn,"EcorePackageImpl/32",1230),m(1231,1,ii,rC),s.dk=function(n){return X(n,164)},s.ek=function(n){return oe(h7,Se,164,n,0,1)},v(Fn,"EcorePackageImpl/33",1231),m(1232,1,ii,sw),s.dk=function(n){return X(n,15)},s.ek=function(n){return oe(jr,Se,15,n,0,1)},v(Fn,"EcorePackageImpl/34",1232),m(1233,1,ii,zs),s.dk=function(n){return X(n,298)},s.ek=function(n){return oe(wme,On,298,n,0,1)},v(Fn,"EcorePackageImpl/35",1233),m(1234,1,ii,qp),s.dk=function(n){return X(n,190)},s.ek=function(n){return oe(cp,Se,190,n,0,1)},v(Fn,"EcorePackageImpl/36",1234),m(1235,1,ii,v3),s.dk=function(n){return X(n,92)},s.ek=function(n){return oe(pme,On,92,n,0,1)},v(Fn,"EcorePackageImpl/37",1235),m(1236,1,ii,cC),s.dk=function(n){return X(n,588)},s.ek=function(n){return oe(o7e,On,588,n,0,1)},v(Fn,"EcorePackageImpl/38",1236),m(1237,1,ii,y1),s.dk=function(n){return!1},s.ek=function(n){return oe(x7e,On,2175,n,0,1)},v(Fn,"EcorePackageImpl/39",1237),m(1202,1,ii,J5),s.dk=function(n){return X(n,88)},s.ek=function(n){return oe(vf,On,29,n,0,1)},v(Fn,"EcorePackageImpl/4",1202),m(1238,1,ii,X6),s.dk=function(n){return X(n,191)},s.ek=function(n){return oe(up,Se,191,n,0,1)},v(Fn,"EcorePackageImpl/40",1238),m(1239,1,ii,Rh),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(Fn,"EcorePackageImpl/41",1239),m(1240,1,ii,uC),s.dk=function(n){return X(n,585)},s.ek=function(n){return oe(F8e,On,585,n,0,1)},v(Fn,"EcorePackageImpl/42",1240),m(1241,1,ii,qk),s.dk=function(n){return!1},s.ek=function(n){return oe(A7e,Se,2176,n,0,1)},v(Fn,"EcorePackageImpl/43",1241),m(1242,1,ii,NL),s.dk=function(n){return X(n,45)},s.ek=function(n){return oe(wg,eF,45,n,0,1)},v(Fn,"EcorePackageImpl/44",1242),m(1203,1,ii,Uk),s.dk=function(n){return X(n,143)},s.ek=function(n){return oe(Aa,On,143,n,0,1)},v(Fn,"EcorePackageImpl/5",1203),m(1204,1,ii,Xk),s.dk=function(n){return X(n,159)},s.ek=function(n){return oe(Bce,On,159,n,0,1)},v(Fn,"EcorePackageImpl/6",1204),m(1205,1,ii,Up),s.dk=function(n){return X(n,459)},s.ek=function(n){return oe(EG,On,675,n,0,1)},v(Fn,"EcorePackageImpl/7",1205),m(1206,1,ii,nf),s.dk=function(n){return X(n,568)},s.ek=function(n){return oe(ed,On,684,n,0,1)},v(Fn,"EcorePackageImpl/8",1206),m(1207,1,ii,Xp),s.dk=function(n){return X(n,469)},s.ek=function(n){return oe(rA,On,469,n,0,1)},v(Fn,"EcorePackageImpl/9",1207),m(1019,2042,RZe,nAe),s.Ki=function(n,t){ujn(this,u(t,415))},s.Oi=function(n,t){rqe(this,n,u(t,415))},v(Fn,"MinimalEObjectImpl/1ArrayDelegatingAdapterList",1019),m(1020,151,RN,yIe),s.hj=function(){return this.a.a},v(Fn,"MinimalEObjectImpl/1ArrayDelegatingAdapterList/1",1020),m(1047,1046,{},NTe),v("org.eclipse.emf.ecore.plugin","EcorePlugin",1047);var o7e=Hi(hen,"Resource");m(786,1485,den),s.Fl=function(n){},s.Gl=function(n){},s.Cl=function(){return!this.a&&(this.a=new hX(this)),this.a},s.Dl=function(n){var t,i,r,c,o;if(r=n.length,r>0)if(Yn(0,n.length),n.charCodeAt(0)==47){for(o=new xo(4),c=1,t=1;t0&&(n=(Yr(0,i,n.length),n.substr(0,i))));return wTn(this,n)},s.El=function(){return this.c},s.Ib=function(){var n;return Db(this.Pm)+"@"+(n=Oi(this)>>>0,n.toString(16))+" uri='"+this.d+"'"},s.b=!1,v(Lne,"ResourceImpl",786),m(1486,786,den,HSe),v(Lne,"BinaryResourceImpl",1486),m(1159,697,Tne),s._i=function(n){return X(n,57)?X5n(this,u(n,57)):X(n,588)?new ut(u(n,588).Cl()):ue(n)===ue(this.f)?u(n,18).Jc():(S9(),eI.a)},s.Ob=function(){return Z0e(this)},s.a=!1,v($i,"EcoreUtil/ContentTreeIterator",1159),m(1487,1159,Tne,YDe),s._i=function(n){return ue(n)===ue(this.f)?u(n,16).Jc():new QLe(u(n,57))},v(Lne,"ResourceImpl/5",1487),m(647,2054,nen,hX),s.Gc=function(n){return this.i<=4?m8(this,n):X(n,52)&&u(n,52).Gh()==this.a},s.Ki=function(n,t){n==this.i-1&&(this.a.b||(this.a.b=!0))},s.Mi=function(n,t){n==0?this.a.b||(this.a.b=!0):bY(this,n,t)},s.Oi=function(n,t){},s.Pi=function(n,t,i){},s.Jj=function(){return 2},s.hj=function(){return this.a},s.Kj=function(){return!0},s.Lj=function(n,t){var i;return i=u(n,52),t=i.ci(this.a,t),t},s.Mj=function(n,t){var i;return i=u(n,52),i.ci(null,t)},s.Nj=function(){return!1},s.Qi=function(){return!0},s.$i=function(n){return oe(wb,On,57,n,0,1)},s.Wi=function(){return!1},v(Lne,"ResourceImpl/ContentsEList",647),m(953,2024,$8,GSe),s.dd=function(n){return this.a.Ii(n)},s.gc=function(){return this.a.gc()},v($i,"AbstractSequentialInternalEList/1",953);var s7e,l7e,nc,f7e;m(625,1,{},ZNe);var xG,AG;v($i,"BasicExtendedMetaData",625),m(1150,1,{},QCe),s.Hl=function(){return null},s.Il=function(){return this.a==-2&&HC(this,jMn(this.d,this.b)),this.a},s.Jl=function(){return null},s.Kl=function(){return jn(),jn(),Sc},s.ve=function(){return this.c==s7&&uX(this,AJe(this.d,this.b)),this.c},s.Ll=function(){return 0},s.a=-2,s.c=s7,v($i,"BasicExtendedMetaData/EClassExtendedMetaDataImpl",1150),m(1151,1,{},CLe),s.Hl=function(){return this.a==(z9(),xG)&&MP(this,uIn(this.f,this.b)),this.a},s.Il=function(){return 0},s.Jl=function(){return this.c==(z9(),xG)&&r9(this,oIn(this.f,this.b)),this.c},s.Kl=function(){return!this.d&&sX(this,G_n(this.f,this.b)),this.d},s.ve=function(){return this.e==s7&&qC(this,AJe(this.f,this.b)),this.e},s.Ll=function(){return this.g==-2&&d(this,HAn(this.f,this.b)),this.g},s.e=s7,s.g=-2,v($i,"BasicExtendedMetaData/EDataTypeExtendedMetaDataImpl",1151),m(1149,1,{},WCe),s.b=!1,s.c=!1,v($i,"BasicExtendedMetaData/EPackageExtendedMetaDataImpl",1149),m(1152,1,{},TLe),s.c=-2,s.e=s7,s.f=s7,v($i,"BasicExtendedMetaData/EStructuralFeatureExtendedMetaDataImpl",1152),m(581,623,lu,Z$),s.Jj=function(){return this.c},s.ml=function(){return!1},s.Ui=function(n,t){return t},s.c=0,v($i,"EDataTypeEList",581);var a7e=Hi($i,"FeatureMap");m(76,581,{3:1,4:1,20:1,31:1,56:1,18:1,16:1,59:1,71:1,67:1,61:1,77:1,163:1,219:1,1998:1,72:1,98:1},nr),s._c=function(n,t){MNn(this,n,u(t,75))},s.Ec=function(n){return GOn(this,u(n,75))},s.Fi=function(n){Jvn(this,u(n,75))},s.Lj=function(n,t){return v2n(this,u(n,75),t)},s.Mj=function(n,t){return qle(this,u(n,75),t)},s.Ri=function(n,t){return QIn(this,n,t)},s.Ui=function(n,t){return BPn(this,n,u(t,75))},s.fd=function(n,t){return hDn(this,n,u(t,75))},s.Sj=function(n,t){return y2n(this,u(n,75),t)},s.Tj=function(n,t){return ANe(this,u(n,75),t)},s.Uj=function(n,t,i){return DAn(this,u(n,75),u(t,75),i)},s.Xi=function(n,t){return uW(this,n,u(t,75))},s.Ml=function(n,t){return Xbe(this,n,t)},s.ad=function(n,t){var i,r,c,o,l,f,h,b,p;for(b=new Nw(t.gc()),c=t.Jc();c.Ob();)if(r=u(c.Pb(),75),o=r.Jk(),J1(this.e,o))(!o.Qi()||!HR(this,o,r.kd())&&!m8(b,r))&&Et(b,r);else{for(p=Po(this.e.Ah(),o),i=u(this.g,122),l=!0,f=0;f=0;)if(t=n[this.c],this.k.$l(t.Jk()))return this.j=this.f?t:t.kd(),this.i=-2,!0;return this.i=-1,this.g=-1,!1},v($i,"BasicFeatureMap/FeatureEIterator",412),m(666,412,Qh,EK),s.sl=function(){return!0},v($i,"BasicFeatureMap/ResolvingFeatureEIterator",666),m(951,482,KF,qTe),s.nj=function(){return this},v($i,"EContentsEList/1",951),m(952,482,KF,wTe),s.sl=function(){return!1},v($i,"EContentsEList/2",952),m(950,287,VF,UTe),s.ul=function(n){},s.Ob=function(){return!1},s.Sb=function(){return!1},v($i,"EContentsEList/FeatureIteratorImpl/1",950),m(824,581,lu,Zse),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EDataTypeEList/Unsettable",824),m(1920,581,lu,WTe),s.Qi=function(){return!0},v($i,"EDataTypeUniqueEList",1920),m(1921,824,lu,ZTe),s.Qi=function(){return!0},v($i,"EDataTypeUniqueEList/Unsettable",1921),m(145,81,lu,rs),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectContainmentEList/Resolving",145),m(1153,543,lu,QTe),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectContainmentEList/Unsettable/Resolving",1153),m(753,14,lu,Rle),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EObjectContainmentWithInverseEList/Unsettable",753),m(1187,753,lu,dNe),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectContainmentWithInverseEList/Unsettable/Resolving",1187),m(745,491,lu,Wse),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EObjectEList/Unsettable",745),m(339,491,lu,$3),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectResolvingEList",339),m(1825,745,lu,eOe),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectResolvingEList/Unsettable",1825),m(1488,1,{},H5);var ehn;v($i,"EObjectValidator",1488),m(547,491,lu,mR),s.gl=function(){return this.d},s.hl=function(){return this.b},s.Kj=function(){return!0},s.kl=function(){return!0},s.b=0,v($i,"EObjectWithInverseEList",547),m(1190,547,lu,bNe),s.jl=function(){return!0},v($i,"EObjectWithInverseEList/ManyInverse",1190),m(626,547,lu,GK),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EObjectWithInverseEList/Unsettable",626),m(1189,626,lu,gNe),s.jl=function(){return!0},v($i,"EObjectWithInverseEList/Unsettable/ManyInverse",1189),m(754,547,lu,Ble),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectWithInverseResolvingEList",754),m(33,754,lu,Nn),s.jl=function(){return!0},v($i,"EObjectWithInverseResolvingEList/ManyInverse",33),m(755,626,lu,zle),s.ll=function(){return!0},s.Ui=function(n,t){return ty(this,n,u(t,57))},v($i,"EObjectWithInverseResolvingEList/Unsettable",755),m(1188,755,lu,wNe),s.jl=function(){return!0},v($i,"EObjectWithInverseResolvingEList/Unsettable/ManyInverse",1188),m(1154,623,lu),s.Ji=function(){return(this.b&1792)==0},s.Li=function(){this.b|=1},s.il=function(){return(this.b&4)!=0},s.Kj=function(){return(this.b&40)!=0},s.jl=function(){return(this.b&16)!=0},s.kl=function(){return(this.b&8)!=0},s.ll=function(){return(this.b&U0)!=0},s.$k=function(){return(this.b&32)!=0},s.ml=function(){return(this.b&qf)!=0},s.dk=function(n){return this.d?rPe(this.d,n):this.Jk().Fk().dk(n)},s.Oj=function(){return(this.b&2)!=0?(this.b&1)!=0:this.i!=0},s.Qi=function(){return(this.b&128)!=0},s.Ek=function(){var n;kt(this),(this.b&2)!=0&&(Fs(this.e)?(n=(this.b&1)!=0,this.b&=-2,s9(this,new Pf(this.e,2,zi(this.e.Ah(),this.Jk()),n,!1))):this.b&=-2)},s.Wi=function(){return(this.b&1536)==0},s.b=0,v($i,"EcoreEList/Generic",1154),m(1155,1154,lu,l_e),s.Jk=function(){return this.a},v($i,"EcoreEList/Dynamic",1155),m(752,67,Mh,uoe),s.$i=function(n){return aO(this.a.a,n)},v($i,"EcoreEMap/1",752),m(751,81,lu,Lfe),s.Ki=function(n,t){lz(this.b,u(t,136))},s.Mi=function(n,t){fze(this.b)},s.Ni=function(n,t,i){var r;++(r=this.b,u(t,136),r).e},s.Oi=function(n,t){gQ(this.b,u(t,136))},s.Pi=function(n,t,i){gQ(this.b,u(i,136)),ue(i)===ue(t)&&u(i,136).zi(pwn(u(t,136).jd())),lz(this.b,u(t,136))},v($i,"EcoreEMap/DelegateEObjectContainmentEList",751),m(1185,142,tme,kBe),v($i,"EcoreEMap/Unsettable",1185),m(1186,751,lu,pNe),s.Li=function(){this.a=!0},s.Oj=function(){return this.a},s.Ek=function(){var n;kt(this),Fs(this.e)?(n=this.a,this.a=!1,ai(this.e,new Pf(this.e,2,this.c,n,!1))):this.a=!1},s.a=!1,v($i,"EcoreEMap/Unsettable/UnsettableDelegateEObjectContainmentEList",1186),m(1158,223,bv,hIe),s.a=!1,s.b=!1,v($i,"EcoreUtil/Copier",1158),m(747,1,Fr,QLe),s.Nb=function(n){Zr(this,n)},s.Ob=function(){return aJe(this)},s.Pb=function(){var n;return aJe(this),n=this.b,this.b=null,n},s.Qb=function(){this.a.Qb()},v($i,"EcoreUtil/ProperContentIterator",747),m(1489,1488,{},zU);var nhn;v($i,"EcoreValidator",1489);var thn;Hi($i,"FeatureMapUtil/Validator"),m(1258,1,{2003:1},lw),s.$l=function(n){return!0},v($i,"FeatureMapUtil/1",1258),m(760,1,{2003:1},Age),s.$l=function(n){var t;return this.c==n?!0:(t=$e(Bn(this.a,n)),t==null?hIn(this,n)?(UPe(this.a,n,(Pn(),a7)),!0):(UPe(this.a,n,(Pn(),eb)),!1):t==(Pn(),a7))},s.e=!1;var Hce;v($i,"FeatureMapUtil/BasicValidator",760),m(761,44,bv,Vse),v($i,"FeatureMapUtil/BasicValidator/Cache",761),m(495,56,{20:1,31:1,56:1,18:1,16:1,61:1,77:1,72:1,98:1},pT),s._c=function(n,t){ZUe(this.c,this.b,n,t)},s.Ec=function(n){return Xbe(this.c,this.b,n)},s.ad=function(n,t){return NLn(this.c,this.b,n,t)},s.Fc=function(n){return Vj(this,n)},s.Ei=function(n,t){p8n(this.c,this.b,n,t)},s.Uk=function(n,t){return Bbe(this.c,this.b,n,t)},s.Yi=function(n){return Uz(this.c,this.b,n,!1)},s.Gi=function(){return xTe(this.c,this.b)},s.Hi=function(){return lwn(this.c,this.b)},s.Ii=function(n){return v9n(this.c,this.b,n)},s.Vk=function(n,t){return YOe(this,n,t)},s.$b=function(){n4(this)},s.Gc=function(n){return HR(this.c,this.b,n)},s.Hc=function(n){return m7n(this.c,this.b,n)},s.Xb=function(n){return Uz(this.c,this.b,n,!0)},s.Dk=function(n){return this},s.bd=function(n){return T6n(this.c,this.b,n)},s.dc=function(){return M$(this)},s.Oj=function(){return!OO(this.c,this.b)},s.Jc=function(){return n8n(this.c,this.b)},s.cd=function(){return t8n(this.c,this.b)},s.dd=function(n){return Ejn(this.c,this.b,n)},s.Ri=function(n,t){return wKe(this.c,this.b,n,t)},s.Si=function(n,t){E9n(this.c,this.b,n,t)},s.ed=function(n){return HGe(this.c,this.b,n)},s.Kc=function(n){return PIn(this.c,this.b,n)},s.fd=function(n,t){return xKe(this.c,this.b,n,t)},s.Wb=function(n){Mz(this.c,this.b),Vj(this,u(n,16))},s.gc=function(){return Sjn(this.c,this.b)},s.Nc=function(){return Oyn(this.c,this.b)},s.Oc=function(n){return O6n(this.c,this.b,n)},s.Ib=function(){var n,t;for(t=new pd,t.a+="[",n=xTe(this.c,this.b);cQ(n);)Bc(t,Qj(oz(n))),cQ(n)&&(t.a+=To);return t.a+="]",t.a},s.Ek=function(){Mz(this.c,this.b)},v($i,"FeatureMapUtil/FeatureEList",495),m(634,39,RN,rY),s.fj=function(n){return PE(this,n)},s.kj=function(n){var t,i,r,c,o,l,f;switch(this.d){case 1:case 2:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return this.g=n.gj(),n.ej()==1&&(this.d=1),!0;break}case 3:{switch(c=n.ej(),c){case 3:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return this.d=5,t=new Nw(2),Et(t,this.g),Et(t,n.gj()),this.g=t,!0;break}}break}case 5:{switch(c=n.ej(),c){case 3:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return i=u(this.g,18),i.Ec(n.gj()),!0;break}}break}case 4:{switch(c=n.ej(),c){case 3:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return this.d=1,this.g=n.gj(),!0;break}case 4:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return this.d=6,f=new Nw(2),Et(f,this.n),Et(f,n.ij()),this.n=f,l=z(B($t,1),ni,30,15,[this.o,n.jj()]),this.g=l,!0;break}}break}case 6:{switch(c=n.ej(),c){case 4:{if(o=n.hj(),ue(o)===ue(this.c)&&PE(this,null)==n.fj(null))return i=u(this.n,18),i.Ec(n.ij()),l=u(this.g,54),r=oe($t,ni,30,l.length+1,15,1),Wu(l,0,r,0,l.length),r[l.length]=n.jj(),this.g=r,!0;break}}break}}return!1},v($i,"FeatureMapUtil/FeatureENotificationImpl",634),m(553,495,{20:1,31:1,56:1,18:1,16:1,61:1,77:1,163:1,219:1,1998:1,72:1,98:1},rR),s.Ml=function(n,t){return Xbe(this.c,n,t)},s.Nl=function(n,t,i){return Bbe(this.c,n,t,i)},s.Ol=function(n,t,i){return bge(this.c,n,t,i)},s.Pl=function(){return this},s.Ql=function(n,t){return iN(this.c,n,t)},s.Rl=function(n){return u(Uz(this.c,this.b,n,!1),75).Jk()},s.Sl=function(n){return u(Uz(this.c,this.b,n,!1),75).kd()},s.Tl=function(){return this.a},s.Ul=function(n){return!OO(this.c,n)},s.Vl=function(n,t){Xz(this.c,n,t)},s.Wl=function(n){return TBe(this.c,n)},s.Xl=function(n){fHe(this.c,n)},v($i,"FeatureMapUtil/FeatureFeatureMap",553),m(1257,1,_ne,nTe),s.Dk=function(n){return Uz(this.b,this.a,-1,n)},s.Oj=function(){return!OO(this.b,this.a)},s.Wb=function(n){Xz(this.b,this.a,n)},s.Ek=function(){Mz(this.b,this.a)},v($i,"FeatureMapUtil/FeatureValue",1257);var Xy,Gce,qce,Ky,ihn,tI=Hi(iJ,"AnyType");m(670,63,H1,CX),v(iJ,"InvalidDatatypeValueException",670);var MG=Hi(iJ,gen),iI=Hi(iJ,wen),h7e=Hi(iJ,pen),rhn,zu,d7e,Dg,chn,uhn,ohn,shn,lhn,fhn,ahn,hhn,dhn,bhn,ghn,Zv,whn,e5,lA,phn,jp,rI,cI,mhn,fA,aA;m(828,501,{109:1,94:1,93:1,57:1,52:1,100:1,841:1},koe),s.Ih=function(n,t,i){switch(n){case 0:return i?(!this.c&&(this.c=new nr(this,0)),this.c):(!this.c&&(this.c=new nr(this,0)),this.c.b);case 1:return i?(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)):(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),219)).Tl();case 2:return i?(!this.b&&(this.b=new nr(this,2)),this.b):(!this.b&&(this.b=new nr(this,2)),this.b.b)}return Pl(this,n-dt(this.fi()),An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),n),t,i)},s.Rh=function(n,t,i){var r;switch(t){case 0:return!this.c&&(this.c=new nr(this,0)),eN(this.c,n,i);case 1:return(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),72)).Vk(n,i);case 2:return!this.b&&(this.b=new nr(this,2)),eN(this.b,n,i)}return r=u(An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),t),69),r.uk().yk(this,khe(this),t-dt(this.fi()),n,i)},s.Th=function(n){switch(n){case 0:return!!this.c&&this.c.i!=0;case 1:return!(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)).dc();case 2:return!!this.b&&this.b.i!=0}return Ll(this,n-dt(this.fi()),An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),$T(this.c,t);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),219)).Wb(t);return;case 2:!this.b&&(this.b=new nr(this,2)),$T(this.b,t);return}Jl(this,n-dt(this.fi()),An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),n),t)},s.fi=function(){return Ei(),d7e},s.hi=function(n){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),kt(this.c);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)).$b();return;case 2:!this.b&&(this.b=new nr(this,2)),kt(this.b);return}Fl(this,n-dt(this.fi()),An((this.j&2)==0?this.fi():(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.Ib=function(){var n;return(this.j&4)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (mixed: ",qj(n,this.c),n.a+=", anyAttribute: ",qj(n,this.b),n.a+=")",n.a)},v(kr,"AnyTypeImpl",828),m(671,501,{109:1,94:1,93:1,57:1,52:1,100:1,2081:1,671:1},wU),s.Ih=function(n,t,i){switch(n){case 0:return this.a;case 1:return this.b}return Pl(this,n-dt((Ei(),Zv)),An((this.j&2)==0?Zv:(!this.k&&(this.k=new tl),this.k).Lk(),n),t,i)},s.Th=function(n){switch(n){case 0:return this.a!=null;case 1:return this.b!=null}return Ll(this,n-dt((Ei(),Zv)),An((this.j&2)==0?Zv:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:T(this,Pt(t));return;case 1:Q(this,Pt(t));return}Jl(this,n-dt((Ei(),Zv)),An((this.j&2)==0?Zv:(!this.k&&(this.k=new tl),this.k).Lk(),n),t)},s.fi=function(){return Ei(),Zv},s.hi=function(n){switch(n){case 0:this.a=null;return;case 1:this.b=null;return}Fl(this,n-dt((Ei(),Zv)),An((this.j&2)==0?Zv:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.Ib=function(){var n;return(this.j&4)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (data: ",Bc(n,this.a),n.a+=", target: ",Bc(n,this.b),n.a+=")",n.a)},s.a=null,s.b=null,v(kr,"ProcessingInstructionImpl",671),m(672,828,{109:1,94:1,93:1,57:1,52:1,100:1,841:1,2082:1,672:1},Dxe),s.Ih=function(n,t,i){switch(n){case 0:return i?(!this.c&&(this.c=new nr(this,0)),this.c):(!this.c&&(this.c=new nr(this,0)),this.c.b);case 1:return i?(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)):(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),219)).Tl();case 2:return i?(!this.b&&(this.b=new nr(this,2)),this.b):(!this.b&&(this.b=new nr(this,2)),this.b.b);case 3:return!this.c&&(this.c=new nr(this,0)),Pt(iN(this.c,(Ei(),lA),!0));case 4:return Jle(this.a,(!this.c&&(this.c=new nr(this,0)),Pt(iN(this.c,(Ei(),lA),!0))));case 5:return this.a}return Pl(this,n-dt((Ei(),e5)),An((this.j&2)==0?e5:(!this.k&&(this.k=new tl),this.k).Lk(),n),t,i)},s.Th=function(n){switch(n){case 0:return!!this.c&&this.c.i!=0;case 1:return!(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)).dc();case 2:return!!this.b&&this.b.i!=0;case 3:return!this.c&&(this.c=new nr(this,0)),Pt(iN(this.c,(Ei(),lA),!0))!=null;case 4:return Jle(this.a,(!this.c&&(this.c=new nr(this,0)),Pt(iN(this.c,(Ei(),lA),!0))))!=null;case 5:return!!this.a}return Ll(this,n-dt((Ei(),e5)),An((this.j&2)==0?e5:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),$T(this.c,t);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(u(fo(this.c,(Ei(),Dg)),163),219)).Wb(t);return;case 2:!this.b&&(this.b=new nr(this,2)),$T(this.b,t);return;case 3:Tae(this,Pt(t));return;case 4:Tae(this,Fle(this.a,t));return;case 5:D(this,u(t,159));return}Jl(this,n-dt((Ei(),e5)),An((this.j&2)==0?e5:(!this.k&&(this.k=new tl),this.k).Lk(),n),t)},s.fi=function(){return Ei(),e5},s.hi=function(n){switch(n){case 0:!this.c&&(this.c=new nr(this,0)),kt(this.c);return;case 1:(!this.c&&(this.c=new nr(this,0)),u(fo(this.c,(Ei(),Dg)),163)).$b();return;case 2:!this.b&&(this.b=new nr(this,2)),kt(this.b);return;case 3:!this.c&&(this.c=new nr(this,0)),Xz(this.c,(Ei(),lA),null);return;case 4:Tae(this,Fle(this.a,null));return;case 5:this.a=null;return}Fl(this,n-dt((Ei(),e5)),An((this.j&2)==0?e5:(!this.k&&(this.k=new tl),this.k).Lk(),n))},v(kr,"SimpleAnyTypeImpl",672),m(673,501,{109:1,94:1,93:1,57:1,52:1,100:1,2083:1,673:1},Ixe),s.Ih=function(n,t,i){switch(n){case 0:return i?(!this.a&&(this.a=new nr(this,0)),this.a):(!this.a&&(this.a=new nr(this,0)),this.a.b);case 1:return i?(!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),this.b):(!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),ZT(this.b));case 2:return i?(!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),this.c):(!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),ZT(this.c));case 3:return!this.a&&(this.a=new nr(this,0)),fo(this.a,(Ei(),rI));case 4:return!this.a&&(this.a=new nr(this,0)),fo(this.a,(Ei(),cI));case 5:return!this.a&&(this.a=new nr(this,0)),fo(this.a,(Ei(),fA));case 6:return!this.a&&(this.a=new nr(this,0)),fo(this.a,(Ei(),aA))}return Pl(this,n-dt((Ei(),jp)),An((this.j&2)==0?jp:(!this.k&&(this.k=new tl),this.k).Lk(),n),t,i)},s.Rh=function(n,t,i){var r;switch(t){case 0:return!this.a&&(this.a=new nr(this,0)),eN(this.a,n,i);case 1:return!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),U$(this.b,n,i);case 2:return!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),U$(this.c,n,i);case 5:return!this.a&&(this.a=new nr(this,0)),YOe(fo(this.a,(Ei(),fA)),n,i)}return r=u(An((this.j&2)==0?(Ei(),jp):(!this.k&&(this.k=new tl),this.k).Lk(),t),69),r.uk().yk(this,khe(this),t-dt((Ei(),jp)),n,i)},s.Th=function(n){switch(n){case 0:return!!this.a&&this.a.i!=0;case 1:return!!this.b&&this.b.f!=0;case 2:return!!this.c&&this.c.f!=0;case 3:return!this.a&&(this.a=new nr(this,0)),!M$(fo(this.a,(Ei(),rI)));case 4:return!this.a&&(this.a=new nr(this,0)),!M$(fo(this.a,(Ei(),cI)));case 5:return!this.a&&(this.a=new nr(this,0)),!M$(fo(this.a,(Ei(),fA)));case 6:return!this.a&&(this.a=new nr(this,0)),!M$(fo(this.a,(Ei(),aA)))}return Ll(this,n-dt((Ei(),jp)),An((this.j&2)==0?jp:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.$h=function(n,t){switch(n){case 0:!this.a&&(this.a=new nr(this,0)),$T(this.a,t);return;case 1:!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),TB(this.b,t);return;case 2:!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),TB(this.c,t);return;case 3:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),rI))),!this.a&&(this.a=new nr(this,0)),Vj(fo(this.a,rI),u(t,18));return;case 4:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),cI))),!this.a&&(this.a=new nr(this,0)),Vj(fo(this.a,cI),u(t,18));return;case 5:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),fA))),!this.a&&(this.a=new nr(this,0)),Vj(fo(this.a,fA),u(t,18));return;case 6:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),aA))),!this.a&&(this.a=new nr(this,0)),Vj(fo(this.a,aA),u(t,18));return}Jl(this,n-dt((Ei(),jp)),An((this.j&2)==0?jp:(!this.k&&(this.k=new tl),this.k).Lk(),n),t)},s.fi=function(){return Ei(),jp},s.hi=function(n){switch(n){case 0:!this.a&&(this.a=new nr(this,0)),kt(this.a);return;case 1:!this.b&&(this.b=new os((yn(),Ac),Iu,this,1)),this.b.c.$b();return;case 2:!this.c&&(this.c=new os((yn(),Ac),Iu,this,2)),this.c.c.$b();return;case 3:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),rI)));return;case 4:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),cI)));return;case 5:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),fA)));return;case 6:!this.a&&(this.a=new nr(this,0)),n4(fo(this.a,(Ei(),aA)));return}Fl(this,n-dt((Ei(),jp)),An((this.j&2)==0?jp:(!this.k&&(this.k=new tl),this.k).Lk(),n))},s.Ib=function(){var n;return(this.j&4)!=0?Jf(this):(n=new cf(Jf(this)),n.a+=" (mixed: ",qj(n,this.a),n.a+=")",n.a)},v(kr,"XMLTypeDocumentRootImpl",673),m(1990,710,{109:1,94:1,93:1,469:1,158:1,57:1,114:1,52:1,100:1,161:1,117:1,118:1,2084:1},oC),s.oi=function(n,t){switch(n.fk()){case 7:case 8:case 9:case 10:case 16:case 22:case 23:case 24:case 25:case 26:case 32:case 33:case 34:case 36:case 37:case 44:case 45:case 50:case 51:case 53:case 55:case 56:case 57:case 58:case 60:case 61:case 4:return t==null?null:su(t);case 19:case 28:case 29:case 35:case 38:case 39:case 41:case 46:case 52:case 54:case 5:return Pt(t);case 6:return Rpn(u(t,195));case 12:case 47:case 49:case 11:return lVe(this,n,t);case 13:return t==null?null:$Ln(u(t,247));case 15:case 14:return t==null?null:Dvn(ne(re(t)));case 17:return ZHe((Ei(),t));case 18:return ZHe(t);case 21:case 20:return t==null?null:Ivn(u(t,164).a);case 27:return $pn(u(t,195));case 30:return aHe((Ei(),u(t,16)));case 31:return aHe(u(t,16));case 40:return Ppn((Ei(),t));case 42:return eGe((Ei(),t));case 43:return eGe(t);case 59:case 48:return Lpn((Ei(),t));default:throw $(new Gn(r7+n.ve()+ip))}},s.pi=function(n){var t,i,r,c,o;switch(n.G==-1&&(n.G=(i=sl(n),i?Ld(i.si(),n):-1)),n.G){case 0:return t=new koe,t;case 1:return r=new wU,r;case 2:return c=new Dxe,c;case 3:return o=new Ixe,o;default:throw $(new Gn(mne+n.zb+ip))}},s.qi=function(n,t){var i,r,c,o,l,f,h,b,p,y,S,A,O,I,R,q;switch(n.fk()){case 5:case 52:case 4:return t;case 6:return nSn(t);case 8:case 7:return t==null?null:BAn(t);case 9:return t==null?null:sO(hl((r=bo(t,!0),r.length>0&&(Yn(0,r.length),r.charCodeAt(0)==43)?(Yn(1,r.length+1),r.substr(1)):r),-128,127)<<24>>24);case 10:return t==null?null:sO(hl((c=bo(t,!0),c.length>0&&(Yn(0,c.length),c.charCodeAt(0)==43)?(Yn(1,c.length+1),c.substr(1)):c),-128,127)<<24>>24);case 11:return Pt(Kw(this,(Ei(),ohn),t));case 12:return Pt(Kw(this,(Ei(),shn),t));case 13:return t==null?null:new Joe(bo(t,!0));case 15:case 14:return XOn(t);case 16:return Pt(Kw(this,(Ei(),lhn),t));case 17:return dJe((Ei(),t));case 18:return dJe(t);case 28:case 29:case 35:case 38:case 39:case 41:case 54:case 19:return bo(t,!0);case 21:case 20:return iNn(t);case 22:return Pt(Kw(this,(Ei(),fhn),t));case 23:return Pt(Kw(this,(Ei(),ahn),t));case 24:return Pt(Kw(this,(Ei(),hhn),t));case 25:return Pt(Kw(this,(Ei(),dhn),t));case 26:return Pt(Kw(this,(Ei(),bhn),t));case 27:return UEn(t);case 30:return bJe((Ei(),t));case 31:return bJe(t);case 32:return t==null?null:ve(hl((p=bo(t,!0),p.length>0&&(Yn(0,p.length),p.charCodeAt(0)==43)?(Yn(1,p.length+1),p.substr(1)):p),Xr,oi));case 33:return t==null?null:new E0((y=bo(t,!0),y.length>0&&(Yn(0,y.length),y.charCodeAt(0)==43)?(Yn(1,y.length+1),y.substr(1)):y));case 34:return t==null?null:ve(hl((S=bo(t,!0),S.length>0&&(Yn(0,S.length),S.charCodeAt(0)==43)?(Yn(1,S.length+1),S.substr(1)):S),Xr,oi));case 36:return t==null?null:R2(Qz((A=bo(t,!0),A.length>0&&(Yn(0,A.length),A.charCodeAt(0)==43)?(Yn(1,A.length+1),A.substr(1)):A)));case 37:return t==null?null:R2(Qz((O=bo(t,!0),O.length>0&&(Yn(0,O.length),O.charCodeAt(0)==43)?(Yn(1,O.length+1),O.substr(1)):O)));case 40:return JSn((Ei(),t));case 42:return gJe((Ei(),t));case 43:return gJe(t);case 44:return t==null?null:new E0((I=bo(t,!0),I.length>0&&(Yn(0,I.length),I.charCodeAt(0)==43)?(Yn(1,I.length+1),I.substr(1)):I));case 45:return t==null?null:new E0((R=bo(t,!0),R.length>0&&(Yn(0,R.length),R.charCodeAt(0)==43)?(Yn(1,R.length+1),R.substr(1)):R));case 46:return bo(t,!1);case 47:return Pt(Kw(this,(Ei(),ghn),t));case 59:case 48:return FSn((Ei(),t));case 49:return Pt(Kw(this,(Ei(),whn),t));case 50:return t==null?null:c8(hl((q=bo(t,!0),q.length>0&&(Yn(0,q.length),q.charCodeAt(0)==43)?(Yn(1,q.length+1),q.substr(1)):q),ZF,32767)<<16>>16);case 51:return t==null?null:c8(hl((o=bo(t,!0),o.length>0&&(Yn(0,o.length),o.charCodeAt(0)==43)?(Yn(1,o.length+1),o.substr(1)):o),ZF,32767)<<16>>16);case 53:return Pt(Kw(this,(Ei(),phn),t));case 55:return t==null?null:c8(hl((l=bo(t,!0),l.length>0&&(Yn(0,l.length),l.charCodeAt(0)==43)?(Yn(1,l.length+1),l.substr(1)):l),ZF,32767)<<16>>16);case 56:return t==null?null:c8(hl((f=bo(t,!0),f.length>0&&(Yn(0,f.length),f.charCodeAt(0)==43)?(Yn(1,f.length+1),f.substr(1)):f),ZF,32767)<<16>>16);case 57:return t==null?null:R2(Qz((h=bo(t,!0),h.length>0&&(Yn(0,h.length),h.charCodeAt(0)==43)?(Yn(1,h.length+1),h.substr(1)):h)));case 58:return t==null?null:R2(Qz((b=bo(t,!0),b.length>0&&(Yn(0,b.length),b.charCodeAt(0)==43)?(Yn(1,b.length+1),b.substr(1)):b)));case 60:return t==null?null:ve(hl((i=bo(t,!0),i.length>0&&(Yn(0,i.length),i.charCodeAt(0)==43)?(Yn(1,i.length+1),i.substr(1)):i),Xr,oi));case 61:return t==null?null:ve(hl(bo(t,!0),Xr,oi));default:throw $(new Gn(r7+n.ve()+ip))}};var vhn,b7e,yhn,g7e;v(kr,"XMLTypeFactoryImpl",1990),m(582,184,{109:1,94:1,93:1,158:1,197:1,57:1,241:1,114:1,52:1,100:1,161:1,184:1,117:1,118:1,680:1,2006:1,582:1},TIe),s.N=!1,s.O=!1;var khn=!1;v(kr,"XMLTypePackageImpl",582),m(1923,1,{835:1},sC),s.Ik=function(){return rge(),Ohn},v(kr,"XMLTypePackageImpl/1",1923),m(1932,1,ii,DL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/10",1932),m(1933,1,ii,K6),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/11",1933),m(1934,1,ii,lC),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/12",1934),m(1935,1,ii,k1),s.dk=function(n){return s2(n)},s.ek=function(n){return oe(gr,Se,346,n,7,1)},v(kr,"XMLTypePackageImpl/13",1935),m(1936,1,ii,IL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/14",1936),m(1937,1,ii,Bh),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/15",1937),m(1938,1,ii,_L),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/16",1938),m(1939,1,ii,LL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/17",1939),m(1940,1,ii,Kp),s.dk=function(n){return X(n,164)},s.ek=function(n){return oe(h7,Se,164,n,0,1)},v(kr,"XMLTypePackageImpl/18",1940),m(1941,1,ii,Kk),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/19",1941),m(1924,1,ii,fC),s.dk=function(n){return X(n,841)},s.ek=function(n){return oe(tI,On,841,n,0,1)},v(kr,"XMLTypePackageImpl/2",1924),m(1942,1,ii,G5),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/20",1942),m(1943,1,ii,PL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/21",1943),m(1944,1,ii,$L),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/22",1944),m(1945,1,ii,RL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/23",1945),m(1946,1,ii,BL),s.dk=function(n){return X(n,195)},s.ek=function(n){return oe(ds,Se,195,n,0,2)},v(kr,"XMLTypePackageImpl/24",1946),m(1947,1,ii,Vk),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/25",1947),m(1948,1,ii,aC),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/26",1948),m(1949,1,ii,pU),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/27",1949),m(1950,1,ii,mU),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/28",1950),m(1951,1,ii,vU),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/29",1951),m(1925,1,ii,zL),s.dk=function(n){return X(n,671)},s.ek=function(n){return oe(MG,On,2081,n,0,1)},v(kr,"XMLTypePackageImpl/3",1925),m(1952,1,ii,FL),s.dk=function(n){return X(n,15)},s.ek=function(n){return oe(jr,Se,15,n,0,1)},v(kr,"XMLTypePackageImpl/30",1952),m(1953,1,ii,q5),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/31",1953),m(1954,1,ii,Yk),s.dk=function(n){return X(n,190)},s.ek=function(n){return oe(cp,Se,190,n,0,1)},v(kr,"XMLTypePackageImpl/32",1954),m(1955,1,ii,JL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/33",1955),m(1956,1,ii,HL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/34",1956),m(1957,1,ii,GL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/35",1957),m(1958,1,ii,qL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/36",1958),m(1959,1,ii,UL),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/37",1959),m(1960,1,ii,XL),s.dk=function(n){return X(n,16)},s.ek=function(n){return oe(wl,Z2,16,n,0,1)},v(kr,"XMLTypePackageImpl/38",1960),m(1961,1,ii,Qk),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/39",1961),m(1926,1,ii,KL),s.dk=function(n){return X(n,672)},s.ek=function(n){return oe(iI,On,2082,n,0,1)},v(kr,"XMLTypePackageImpl/4",1926),m(1962,1,ii,VL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/40",1962),m(1963,1,ii,ro),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/41",1963),m(1964,1,ii,hC),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/42",1964),m(1965,1,ii,yU),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/43",1965),m(1966,1,ii,YL),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/44",1966),m(1967,1,ii,kU),s.dk=function(n){return X(n,191)},s.ek=function(n){return oe(up,Se,191,n,0,1)},v(kr,"XMLTypePackageImpl/45",1967),m(1968,1,ii,jU),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/46",1968),m(1969,1,ii,EU),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/47",1969),m(1970,1,ii,Wk),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/48",1970),m(1971,1,ii,U5),s.dk=function(n){return X(n,191)},s.ek=function(n){return oe(up,Se,191,n,0,1)},v(kr,"XMLTypePackageImpl/49",1971),m(1927,1,ii,dC),s.dk=function(n){return X(n,673)},s.ek=function(n){return oe(h7e,On,2083,n,0,1)},v(kr,"XMLTypePackageImpl/5",1927),m(1972,1,ii,Zk),s.dk=function(n){return X(n,190)},s.ek=function(n){return oe(cp,Se,190,n,0,1)},v(kr,"XMLTypePackageImpl/50",1972),m(1973,1,ii,bC),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/51",1973),m(1974,1,ii,Vp),s.dk=function(n){return X(n,15)},s.ek=function(n){return oe(jr,Se,15,n,0,1)},v(kr,"XMLTypePackageImpl/52",1974),m(1928,1,ii,Cb),s.dk=function(n){return $r(n)},s.ek=function(n){return oe(ze,Se,2,n,6,1)},v(kr,"XMLTypePackageImpl/6",1928),m(1929,1,ii,V6),s.dk=function(n){return X(n,195)},s.ek=function(n){return oe(ds,Se,195,n,0,2)},v(kr,"XMLTypePackageImpl/7",1929),m(1930,1,ii,SU),s.dk=function(n){return o2(n)},s.ek=function(n){return oe(Yi,Se,473,n,8,1)},v(kr,"XMLTypePackageImpl/8",1930),m(1931,1,ii,QL),s.dk=function(n){return X(n,221)},s.ek=function(n){return oe(py,Se,221,n,0,1)},v(kr,"XMLTypePackageImpl/9",1931);var rh,t0,hA,CG,J;m(53,63,H1,zt),v(Jd,"RegEx/ParseException",53),m(820,1,{},gC),s._l=function(n){return ni*16)throw $(new zt(Ht((Lt(),CZe))));i=i*16+c}while(!0);if(this.a!=125)throw $(new zt(Ht((Lt(),TZe))));if(i>l7)throw $(new zt(Ht((Lt(),OZe))));n=i}else{if(c=0,this.c!=0||(c=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(i=c,li(this),this.c!=0||(c=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));i=i*16+c,n=i}break;case 117:if(r=0,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));t=t*16+r,n=t;break;case 118:if(li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,li(this),this.c!=0||(r=ig(this.a))<0)throw $(new zt(Ht((Lt(),Fd))));if(t=t*16+r,t>l7)throw $(new zt(Ht((Lt(),"parser.descappe.4"))));n=t;break;case 65:case 90:case 122:throw $(new zt(Ht((Lt(),NZe))))}return n},s.bm=function(n){var t,i;switch(n){case 100:i=(this.e&32)==32?q0("Nd",!0):(fi(),TG);break;case 68:i=(this.e&32)==32?q0("Nd",!1):(fi(),k7e);break;case 119:i=(this.e&32)==32?q0("IsWord",!0):(fi(),V7);break;case 87:i=(this.e&32)==32?q0("IsWord",!1):(fi(),E7e);break;case 115:i=(this.e&32)==32?q0("IsSpace",!0):(fi(),Vy);break;case 83:i=(this.e&32)==32?q0("IsSpace",!1):(fi(),j7e);break;default:throw $(new hu((t=n,Nen+t.toString(16))))}return i},s.cm=function(n){var t,i,r,c,o,l,f,h,b,p,y,S;for(this.b=1,li(this),t=null,this.c==0&&this.a==94?(li(this),n?p=(fi(),fi(),new ul(5)):(t=(fi(),fi(),new ul(4)),ho(t,0,l7),p=new ul(4))):p=(fi(),fi(),new ul(4)),c=!0;(S=this.c)!=1&&!(S==0&&this.a==93&&!c);){if(c=!1,i=this.a,r=!1,S==10)switch(i){case 100:case 68:case 119:case 87:case 115:case 83:V2(p,this.bm(i)),r=!0;break;case 105:case 73:case 99:case 67:i=this.sm(p,i),i<0&&(r=!0);break;case 112:case 80:if(y=Q0e(this,i),!y)throw $(new zt(Ht((Lt(),Nne))));V2(p,y),r=!0;break;default:i=this.am()}else if(S==20){if(l=k9(this.i,58,this.d),l<0)throw $(new zt(Ht((Lt(),Y2e))));if(f=!0,ic(this.i,this.d)==94&&(++this.d,f=!1),o=of(this.i,this.d,l),h=P$e(o,f,(this.e&512)==512),!h)throw $(new zt(Ht((Lt(),EZe))));if(V2(p,h),r=!0,l+1>=this.j||ic(this.i,l+1)!=93)throw $(new zt(Ht((Lt(),Y2e))));this.d=l+2}if(li(this),!r)if(this.c!=0||this.a!=45)ho(p,i,i);else{if(li(this),(S=this.c)==1)throw $(new zt(Ht((Lt(),UF))));S==0&&this.a==93?(ho(p,i,i),ho(p,45,45)):(b=this.a,S==10&&(b=this.am()),li(this),ho(p,i,b))}(this.e&qf)==qf&&this.c==0&&this.a==44&&li(this)}if(this.c==1)throw $(new zt(Ht((Lt(),UF))));return t&&(dS(t,p),p=t),ov(p),aS(p),this.b=0,li(this),p},s.dm=function(){var n,t,i,r;for(i=this.cm(!1);(r=this.c)!=7;)if(n=this.a,r==0&&(n==45||n==38)||r==4){if(li(this),this.c!=9)throw $(new zt(Ht((Lt(),xZe))));if(t=this.cm(!1),r==4)V2(i,t);else if(n==45)dS(i,t);else if(n==38)cVe(i,t);else throw $(new hu("ASSERT"))}else throw $(new zt(Ht((Lt(),AZe))));return li(this),i},s.em=function(){var n,t;return n=this.a-48,t=(fi(),fi(),new FV(12,null,n)),!this.g&&(this.g=new PP),LP(this.g,new ooe(n)),li(this),t},s.fm=function(){return li(this),fi(),Shn},s.gm=function(){return li(this),fi(),Ehn},s.hm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.im=function(){throw $(new zt(Ht((Lt(),Ul))))},s.jm=function(){return li(this),wkn()},s.km=function(){return li(this),fi(),Ahn},s.lm=function(){return li(this),fi(),Chn},s.mm=function(){var n;if(this.d>=this.j||((n=ic(this.i,this.d++))&65504)!=64)throw $(new zt(Ht((Lt(),yZe))));return li(this),fi(),fi(),new Hh(0,n-64)},s.nm=function(){return li(this),B_n()},s.om=function(){return li(this),fi(),Thn},s.pm=function(){var n;return n=(fi(),fi(),new Hh(0,105)),li(this),n},s.qm=function(){return li(this),fi(),Mhn},s.rm=function(){return li(this),fi(),xhn},s.sm=function(n,t){return this.am()},s.tm=function(){return li(this),fi(),v7e},s.um=function(){var n,t,i,r,c;if(this.d+1>=this.j)throw $(new zt(Ht((Lt(),pZe))));if(r=-1,t=null,n=ic(this.i,this.d),49<=n&&n<=57){if(r=n-48,!this.g&&(this.g=new PP),LP(this.g,new ooe(r)),++this.d,ic(this.i,this.d)!=41)throw $(new zt(Ht((Lt(),bg))));++this.d}else switch(n==63&&--this.d,li(this),t=Oge(this),t.e){case 20:case 21:case 22:case 23:break;case 8:if(this.c!=7)throw $(new zt(Ht((Lt(),bg))));break;default:throw $(new zt(Ht((Lt(),mZe))))}if(li(this),c=Bw(this),i=null,c.e==2){if(c.Nm()!=2)throw $(new zt(Ht((Lt(),vZe))));i=c.Jm(1),c=c.Jm(0)}if(this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),fi(),fi(),new MRe(r,t,c,i)},s.vm=function(){return li(this),fi(),y7e},s.wm=function(){var n;if(li(this),n=vR(24,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.xm=function(){var n;if(li(this),n=vR(20,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.ym=function(){var n;if(li(this),n=vR(22,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.zm=function(){var n,t,i,r,c;for(n=0,i=0,t=-1;this.d=this.j)throw $(new zt(Ht((Lt(),K2e))));if(t==45){for(++this.d;this.d=this.j)throw $(new zt(Ht((Lt(),K2e))))}if(t==58){if(++this.d,li(this),r=bIe(Bw(this),n,i),this.c!=7)throw $(new zt(Ht((Lt(),bg))));li(this)}else if(t==41)++this.d,li(this),r=bIe(Bw(this),n,i);else throw $(new zt(Ht((Lt(),wZe))));return r},s.Am=function(){var n;if(li(this),n=vR(21,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.Bm=function(){var n;if(li(this),n=vR(23,Bw(this)),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.Cm=function(){var n,t;if(li(this),n=this.f++,t=wV(Bw(this),n),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),t},s.Dm=function(){var n;if(li(this),n=wV(Bw(this),0),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.Em=function(n){return li(this),this.c==5?(li(this),aR(n,(fi(),fi(),new A2(9,n)))):aR(n,(fi(),fi(),new A2(3,n)))},s.Fm=function(n){var t;return li(this),t=(fi(),fi(),new Kj(2)),this.c==5?(li(this),ug(t,bA),ug(t,n)):(ug(t,n),ug(t,bA)),t},s.Gm=function(n){return li(this),this.c==5?(li(this),fi(),fi(),new A2(9,n)):(fi(),fi(),new A2(3,n))},s.a=0,s.b=0,s.c=0,s.d=0,s.e=0,s.f=1,s.g=null,s.j=0,v(Jd,"RegEx/RegexParser",820),m(1910,820,{},_xe),s._l=function(n){return!1},s.am=function(){return Lbe(this)},s.bm=function(n){return C8(n)},s.cm=function(n){return WVe(this)},s.dm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.em=function(){throw $(new zt(Ht((Lt(),Ul))))},s.fm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.gm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.hm=function(){return li(this),C8(67)},s.im=function(){return li(this),C8(73)},s.jm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.km=function(){throw $(new zt(Ht((Lt(),Ul))))},s.lm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.mm=function(){return li(this),C8(99)},s.nm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.om=function(){throw $(new zt(Ht((Lt(),Ul))))},s.pm=function(){return li(this),C8(105)},s.qm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.rm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.sm=function(n,t){return V2(n,C8(t)),-1},s.tm=function(){return li(this),fi(),fi(),new Hh(0,94)},s.um=function(){throw $(new zt(Ht((Lt(),Ul))))},s.vm=function(){return li(this),fi(),fi(),new Hh(0,36)},s.wm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.xm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.ym=function(){throw $(new zt(Ht((Lt(),Ul))))},s.zm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.Am=function(){throw $(new zt(Ht((Lt(),Ul))))},s.Bm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.Cm=function(){var n;if(li(this),n=wV(Bw(this),0),this.c!=7)throw $(new zt(Ht((Lt(),bg))));return li(this),n},s.Dm=function(){throw $(new zt(Ht((Lt(),Ul))))},s.Em=function(n){return li(this),aR(n,(fi(),fi(),new A2(3,n)))},s.Fm=function(n){var t;return li(this),t=(fi(),fi(),new Kj(2)),ug(t,n),ug(t,bA),t},s.Gm=function(n){return li(this),fi(),fi(),new A2(3,n)};var n5=null,X7=null;v(Jd,"RegEx/ParserForXMLSchema",1910),m(121,1,f7,aw),s.Hm=function(n){throw $(new hu("Not supported."))},s.Im=function(){return-1},s.Jm=function(n){return null},s.Km=function(){return null},s.Lm=function(n){},s.Mm=function(n){},s.Nm=function(){return 0},s.Ib=function(){return this.Om(0)},s.Om=function(n){return this.e==11?".":""},s.e=0;var w7e,K7,dA,jhn,p7e,Jm=null,TG,Uce=null,m7e,bA,Xce=null,v7e,y7e,k7e,j7e,E7e,Ehn,Vy,Shn,xhn,Ahn,Mhn,V7,Chn,Thn,WBn=v(Jd,"RegEx/Token",121);m(137,121,{3:1,137:1,121:1},ul),s.Om=function(n){var t,i,r;if(this.e==4)if(this==m7e)i=".";else if(this==TG)i="\\d";else if(this==V7)i="\\w";else if(this==Vy)i="\\s";else{for(r=new pd,r.a+="[",t=0;t0&&(r.a+=","),this.b[t]===this.b[t+1]?Bc(r,tN(this.b[t])):(Bc(r,tN(this.b[t])),r.a+="-",Bc(r,tN(this.b[t+1])));r.a+="]",i=r.a}else if(this==k7e)i="\\D";else if(this==E7e)i="\\W";else if(this==j7e)i="\\S";else{for(r=new pd,r.a+="[^",t=0;t0&&(r.a+=","),this.b[t]===this.b[t+1]?Bc(r,tN(this.b[t])):(Bc(r,tN(this.b[t])),r.a+="-",Bc(r,tN(this.b[t+1])));r.a+="]",i=r.a}return i},s.a=!1,s.c=!1,v(Jd,"RegEx/RangeToken",137),m(580,1,{580:1},ooe),s.a=0,v(Jd,"RegEx/RegexParser/ReferencePosition",580),m(579,1,{3:1,579:1},wMe),s.Fb=function(n){var t;return n==null||!X(n,579)?!1:(t=u(n,579),bn(this.b,t.b)&&this.a==t.a)},s.Hb=function(){return Od(this.b+"/"+Cbe(this.a))},s.Ib=function(){return this.c.Om(this.a)},s.a=0,v(Jd,"RegEx/RegularExpression",579),m(228,121,f7,Hh),s.Im=function(){return this.a},s.Om=function(n){var t,i,r;switch(this.e){case 0:switch(this.a){case 124:case 42:case 43:case 63:case 40:case 41:case 46:case 91:case 123:case 92:r="\\"+JK(this.a&yr);break;case 12:r="\\f";break;case 10:r="\\n";break;case 13:r="\\r";break;case 9:r="\\t";break;case 27:r="\\e";break;default:this.a>=Ec?(i=(t=this.a>>>0,"0"+t.toString(16)),r="\\v"+of(i,i.length-6,i.length)):r=""+JK(this.a&yr)}break;case 8:this==v7e||this==y7e?r=""+JK(this.a&yr):r="\\"+JK(this.a&yr);break;default:r=null}return r},s.a=0,v(Jd,"RegEx/Token/CharToken",228),m(322,121,f7,A2),s.Jm=function(n){return this.a},s.Lm=function(n){this.b=n},s.Mm=function(n){this.c=n},s.Nm=function(){return 1},s.Om=function(n){var t;if(this.e==3)if(this.c<0&&this.b<0)t=this.a.Om(n)+"*";else if(this.c==this.b)t=this.a.Om(n)+"{"+this.c+"}";else if(this.c>=0&&this.b>=0)t=this.a.Om(n)+"{"+this.c+","+this.b+"}";else if(this.c>=0&&this.b<0)t=this.a.Om(n)+"{"+this.c+",}";else throw $(new hu("Token#toString(): CLOSURE "+this.c+To+this.b));else if(this.c<0&&this.b<0)t=this.a.Om(n)+"*?";else if(this.c==this.b)t=this.a.Om(n)+"{"+this.c+"}?";else if(this.c>=0&&this.b>=0)t=this.a.Om(n)+"{"+this.c+","+this.b+"}?";else if(this.c>=0&&this.b<0)t=this.a.Om(n)+"{"+this.c+",}?";else throw $(new hu("Token#toString(): NONGREEDYCLOSURE "+this.c+To+this.b));return t},s.b=0,s.c=0,v(Jd,"RegEx/Token/ClosureToken",322),m(821,121,f7,zfe),s.Jm=function(n){return n==0?this.a:this.b},s.Nm=function(){return 2},s.Om=function(n){var t;return this.b.e==3&&this.b.Jm(0)==this.a?t=this.a.Om(n)+"+":this.b.e==9&&this.b.Jm(0)==this.a?t=this.a.Om(n)+"+?":t=this.a.Om(n)+(""+this.b.Om(n)),t},v(Jd,"RegEx/Token/ConcatToken",821),m(1908,121,f7,MRe),s.Jm=function(n){if(n==0)return this.d;if(n==1)return this.b;throw $(new hu("Internal Error: "+n))},s.Nm=function(){return this.b?2:1},s.Om=function(n){var t;return this.c>0?t="(?("+this.c+")":this.a.e==8?t="(?("+this.a+")":t="(?"+this.a,this.b?t+=this.d+"|"+this.b+")":t+=this.d+")",t},s.c=0,v(Jd,"RegEx/Token/ConditionToken",1908),m(1909,121,f7,aLe),s.Jm=function(n){return this.b},s.Nm=function(){return 1},s.Om=function(n){return"(?"+(this.a==0?"":Cbe(this.a))+(this.c==0?"":Cbe(this.c))+":"+this.b.Om(n)+")"},s.a=0,s.c=0,v(Jd,"RegEx/Token/ModifierToken",1909),m(822,121,f7,Yfe),s.Jm=function(n){return this.a},s.Nm=function(){return 1},s.Om=function(n){var t;switch(t=null,this.e){case 6:this.b==0?t="(?:"+this.a.Om(n)+")":t="("+this.a.Om(n)+")";break;case 20:t="(?="+this.a.Om(n)+")";break;case 21:t="(?!"+this.a.Om(n)+")";break;case 22:t="(?<="+this.a.Om(n)+")";break;case 23:t="(?"+this.a.Om(n)+")"}return t},s.b=0,v(Jd,"RegEx/Token/ParenToken",822),m(517,121,{3:1,121:1,517:1},FV),s.Km=function(){return this.b},s.Om=function(n){return this.e==12?"\\"+this.a:IOn(this.b)},s.a=0,v(Jd,"RegEx/Token/StringToken",517),m(466,121,f7,Kj),s.Hm=function(n){ug(this,n)},s.Jm=function(n){return u(Ew(this.a,n),121)},s.Nm=function(){return this.a?this.a.a.c.length:0},s.Om=function(n){var t,i,r,c,o;if(this.e==1){if(this.a.a.c.length==2)t=u(Ew(this.a,0),121),i=u(Ew(this.a,1),121),i.e==3&&i.Jm(0)==t?c=t.Om(n)+"+":i.e==9&&i.Jm(0)==t?c=t.Om(n)+"+?":c=t.Om(n)+(""+i.Om(n));else{for(o=new pd,r=0;r=this.c.b:this.a<=this.c.b},s.Sb=function(){return this.b>0},s.Tb=function(){return this.b},s.Vb=function(){return this.b-1},s.Qb=function(){throw $(new gd(Ren))},s.a=0,s.b=0,v(gme,"ExclusiveRange/RangeIterator",259);var Wl=I9(XF,"C"),$t=I9(FS,"I"),ts=I9(ry,"Z"),Ep=I9(JS,"J"),ds=I9(RS,"B"),Jr=I9(BS,"D"),Hm=I9(zS,"F"),t5=I9(HS,"S"),ZBn=Hi("org.eclipse.elk.core.labels","ILabelManager"),S7e=Hi(yc,"DiagnosticChain"),x7e=Hi(hen,"ResourceSet"),A7e=v(yc,"InvocationTargetException",null),Nhn=(JP(),G6n),Dhn=Dhn=EAn;F8n(dbn),i7n("permProps",[[["locale","default"],[Ben,"gecko1_8"]],[["locale","default"],[Ben,"safari"]]]),Dhn(null,"elk",null)}).call(this)}).call(this,typeof _hn<"u"?_hn:typeof self<"u"?self:typeof window<"u"?window:{})},{}],3:[function(x,M,N){function P(ge){"@babel/helpers - typeof";return P=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(Pe){return typeof Pe}:function(Pe){return Pe&&typeof Symbol=="function"&&Pe.constructor===Symbol&&Pe!==Symbol.prototype?"symbol":typeof Pe},P(ge)}function k(ge,Pe,ae){return Object.defineProperty(ge,"prototype",{writable:!1}),ge}function H(ge,Pe){if(!(ge instanceof Pe))throw new TypeError("Cannot call a class as a function")}function U(ge,Pe,ae){return Pe=W(Pe),G(ge,Z()?Reflect.construct(Pe,ae||[],W(ge).constructor):Pe.apply(ge,ae))}function G(ge,Pe){if(Pe&&(P(Pe)=="object"||typeof Pe=="function"))return Pe;if(Pe!==void 0)throw new TypeError("Derived constructors may only return object or undefined");return ie(ge)}function ie(ge){if(ge===void 0)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return ge}function Z(){try{var ge=!Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],function(){}))}catch{}return(Z=function(){return!!ge})()}function W(ge){return W=Object.setPrototypeOf?Object.getPrototypeOf.bind():function(Pe){return Pe.__proto__||Object.getPrototypeOf(Pe)},W(ge)}function se(ge,Pe){if(typeof Pe!="function"&&Pe!==null)throw new TypeError("Super expression must either be null or a function");ge.prototype=Object.create(Pe&&Pe.prototype,{constructor:{value:ge,writable:!0,configurable:!0}}),Object.defineProperty(ge,"prototype",{writable:!1}),Pe&&le(ge,Pe)}function le(ge,Pe){return le=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(ae,Me){return ae.__proto__=Me,ae},le(ge,Pe)}var ee=x("./elk-api.js").default,Oe=(function(ge){function Pe(){var ae=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};H(this,Pe);var Me=Object.assign({},ae),Be=!1;try{x.resolve("web-worker"),Be=!0}catch{}if(ae.workerUrl)if(Be){var rn=x("web-worker");Me.workerFactory=function(hn){return new rn(hn)}}else console.warn(`Web worker requested but 'web-worker' package not installed. Consider installing the package or pass your own 'workerFactory' to ELK's constructor. -... Falling back to non-web worker version.`);if(!Me.workerFactory){var ln=x("./elk-worker.min.js"),xn=ln.Worker;Me.workerFactory=function(hn){return new xn(hn)}}return U(this,Pe,[Me])}return se(Pe,ge),k(Pe)})(ee);Object.defineProperty(M.exports,"__esModule",{value:!0}),M.exports=Oe,Oe.default=Oe},{"./elk-api.js":1,"./elk-worker.min.js":2,"web-worker":4}],4:[function(x,M,N){var P=typeof Worker<"u"?Worker:void 0;M.exports=P},{}]},{},[3])(3)})})(K7e)),K7e.exports}var rKn=iKn();const cKn=bke(rKn),uKn=new cKn;async function oKn(g,E,x){const M={id:"root",layoutOptions:sKn(x),children:g.map(k=>({id:k.id,width:lKn(k.data),height:fKn(k.data),ports:k.data.nodeKind==="application"?[...k.data.inputs.map((H,U)=>lue(H.id,"WEST",U)),...k.data.outputs.map((H,U)=>lue(H.id,"EAST",U))]:[...(k.data.inputPortIds??[]).map((H,U)=>lue(H,"WEST",U)),...(k.data.outputPortIds??[]).map((H,U)=>lue(H,"EAST",U))],layoutOptions:{"org.eclipse.elk.portConstraints":"FIXED_ORDER"}})),edges:E.map(k=>({id:k.id,sources:[k.sourceHandle??k.source],targets:[k.targetHandle??k.target]}))},N=await uKn.layout(M),P=new Map((N.children??[]).map(k=>[k.id,{x:k.x??0,y:k.y??0}]));return g.map(k=>({...k,position:P.get(k.id)??k.position}))}function sKn(g){return{"elk.algorithm":g==="topology"?"mrtree":"layered","elk.direction":g==="topology"?"DOWN":"RIGHT","elk.spacing.nodeNode":g==="overview"?"24":g==="compact"?"32":"56","elk.layered.spacing.nodeNodeBetweenLayers":g==="overview"?"48":g==="compact"?"60":"110","elk.layered.nodePlacement.strategy":"BRANDES_KOEPF","elk.layered.crossingMinimization.semiInteractive":"true","elk.edgeRouting":"ORTHOGONAL"}}function lue(g,E,x){return{id:g,width:9,height:9,layoutOptions:{"org.eclipse.elk.port.side":E,"org.eclipse.elk.port.index":String(x)}}}function lKn(g){return g.nodeKind==="application"?Z0n(g):240}function fKn(g){return g.nodeKind!=="application"?112:g.detailMode==="overview"?108:Math.max(178,142+Math.max(g.inputs.length,g.outputs.length)*27)}const V7e=Ike("source","degree_days","ToyDegreeDaysCumulModel",[],["TT_cu"]),fue=Ike("lai","lai_dynamic","ToyLAIModel",["TT_cu"],["LAI"]),Y7e=Ike("light","light_interception","Beer",["LAI"],["aPPFD"]),edn={schemaVersion:1,level:"applications",metadata:{title:"PlantSimEngine Scene Graph",sceneRevision:0,objectCount:1,instanceCount:0,applicationCount:3,executionCount:3,bindingCount:2,callCount:0,unresolvedInitializationCount:1,cyclic:!1,strictlyCompiled:!0},objects:[{id:"object:plant",objectId:"plant",scale:"Plant",kind:"plant",species:null,name:"plant",instance:null,parent:null,children:[],hasGeometry:!1,hasStatus:!0}],instances:[],applications:[V7e,fue,Y7e],executions:[V7e,fue,Y7e].map(g=>({id:`execution:${g.applicationId}:plant`,applicationId:g.applicationId,applicationNodeId:g.id,objectId:"plant",objectNodeId:"object:plant",modelType:g.modelType,modelParameters:{},overridden:!1})),edges:[tdn(V7e,"TT_cu",fue,"TT_cu"),tdn(fue,"LAI",Y7e,"LAI")],modelLibrary:[],initialization:[{applicationId:"source",objectId:"plant",variable:"TT",role:"input",disposition:"unresolved",value:"-Inf",valueJulia:"-Inf",expectedType:"Float64",sourceApplicationIds:[],sourceObjectIds:[],sourceVariable:null,origin:"missing",previousTimeStep:!1}],diagnostics:[],cycles:[],availableActions:["inspect"]};function Ike(g,E,x,M,N){return{id:`application:${g}`,applicationId:g,name:g,process:E,modelType:x,modelName:x,module:"PlantSimEngine.Examples",package:"PlantSimEngine",modelParameters:{},selector:{type:"One",multiplicity:"one",criteria:{scale:"Plant"},julia:"One(scale=:Plant)"},targetIds:["plant"],targetCount:1,targetScales:["Plant"],targetKinds:["plant"],targetSpecies:[],targetInstances:[],timestep:null,clock:null,inputs:M.map(P=>ndn(g,"input",P)),outputs:N.map(P=>ndn(g,"output",P)),environmentInputs:[],environmentOutputs:[],inputBindings:{},callBindings:{},environment:null,meteoBindings:{},meteoWindow:null,outputRouting:{},updates:[],modelStorage:"shared_application",objectOverrides:[]}}function ndn(g,E,x){return{id:`application:${g}:${E}:${x}`,name:x,role:E,default:"-Inf",defaultJulia:"-Inf",expectedType:"Float64"}}function tdn(g,E,x,M){return{id:`binding:${g.applicationId}:${E}:${x.applicationId}:${M}`,source:g.id,target:x.id,sourcePort:`application:${g.applicationId}:output:${E}`,targetPort:`application:${x.applicationId}:input:${M}`,sourceVariable:E,targetVariable:M,sourceApplicationId:g.applicationId,targetApplicationId:x.applicationId,kind:"inferred_same_object",cycle:!1,projection:"applications"}}const aKn={application:YXn,entity:QXn},hKn={sceneEdge:nKn};function dKn(){const[g,E]=He.useState(W7e),[x,M]=He.useState(()=>W7e().level),[N,P]=He.useState(()=>W7e().metadata.applicationCount>24?"overview":"detail"),[k,H]=He.useState(""),[U,G]=He.useState(null),[ie,Z]=He.useState(null),[W,se]=He.useState(null),[le,ee]=He.useState(null),[Oe,ge]=He.useState(!1),[Pe,ae]=He.useState(!1),[Me,Be]=He.useState(!1),[rn,ln]=He.useState(!1),[xn,hn]=He.useState(!1),[wt,Cn]=He.useState(""),[Qn,Y]=He.useState(null),[Fe,mn]=He.useState(null),[Ae,Ze]=He.useState([]),[sn,Sn]=He.useState(null),[kn,xe]=He.useState(!1),[un,rt]=He.useState(!1),[lt,Bt]=He.useState(!1),[hi,Gt]=He.useState(null),[At,st]=He.useState(null),[Wr,Mr]=He.useState(null),[ur,mi]=He.useState(null),[Fi,fu]=He.useState(null),[Eu,Ws]=He.useState(null),[Dh,ef]=He.useState(null),[ch,kc]=He.useState(null),[cd,jb]=He.useState(!1),[Rs,c0]=He.useState(null),[u0,o0,Bg]=HUn([]),[r6,zg,c6]=GUn([]),d1=He.useMemo(IKn,[]),ud=He.useMemo(()=>new Map(g.applications.map(Mt=>[Mt.applicationId,Mt])),[g.applications]),Sf=He.useMemo(()=>new Set(g.initialization.filter(Mt=>Mt.role==="input"&&Mt.disposition==="unresolved").map(Mt=>Q7e(Mt.applicationId,"input",Mt.variable))),[g.initialization]),b1=He.useMemo(()=>new Set(g.initialization.filter(Mt=>Mt.role==="input"&&Mt.previousTimeStep).map(Mt=>Q7e(Mt.applicationId,"input",Mt.variable))),[g.initialization]),xf=He.useMemo(()=>new Set(g.cycles.flatMap(Mt=>Mt.applicationIds)),[g.cycles]),l5=He.useMemo(()=>new Set(g.cycles.flatMap(Mt=>Mt.breakCandidates.map(Tc=>Q7e(Tc.applicationId,"input",Tc.input)))),[g.cycles]),f5=He.useMemo(()=>vKn(g),[g]),a5=He.useMemo(()=>le?yKn(g.modelLibrary,le.port):[],[le,g.modelLibrary]),Fg=He.useMemo(()=>le?jKn(g.applications,le):[],[le,g.applications]),Eb=He.useMemo(()=>{const Mt=new Map;for(const Tc of g.applications)for(const dc of[...Tc.inputs,...Tc.outputs])Mt.set(dc.id,{application:Tc,port:dc});return Mt},[g.applications]),Jg=He.useMemo(()=>ie?new Set(ie.objectIds.map(n6)):null,[ie]);He.useEffect(()=>{if(!d1?.websocketUrl)return;const Mt=new WebSocket(d1.websocketUrl);return Sn(Mt),Mt.addEventListener("open",()=>{xe(!0),Gt(null)}),Mt.addEventListener("close",()=>{xe(!1),Gt("Editor connection closed.")}),Mt.addEventListener("message",Tc=>{const dc=JSON.parse(Tc.data);dc.graph&&E(dc.graph),typeof dc.sceneCode=="string"&&Cn(dc.sceneCode),Y(dc.autosavePath??null),mn(dc.savePath??null),Ze(dc.recentPaths??[]),dc.selectorPreview&&kc(dc.selectorPreview),dc.targetPreview&&Mr(dc.targetPreview),dc.ok===!1&&(kc(null),Mr(null)),rt(!!dc.canUndo),Bt(!!dc.canRedo),Gt(dc.ok===!1?dc.diagnostics?.[0]||"The edit failed.":null)}),()=>Mt.close()},[d1?.websocketUrl]),He.useEffect(()=>{g.metadata.cyclic||(jb(!1),c0(null))},[g.metadata.cyclic]);const _u=He.useCallback(Mt=>{if(!sn||sn.readyState!==WebSocket.OPEN){Gt("This action requires an interactive Julia editor session.");return}sn.send(JSON.stringify(Mt))},[sn]),Hg=He.useCallback((Mt,Tc,dc)=>{G(Mt),se(Tc),ee({application:Mt,port:Tc,x:dc.x,y:dc.y})},[]);He.useEffect(()=>{const Mt=bKn({graph:g,view:x,detailMode:N,query:k,scopedObjectIds:Jg,unresolvedPortIds:Sf,previousPortIds:b1,candidatePortIds:f5,cyclicApplications:xf,cycleBreakPortIds:l5,cycleBreakMode:cd,openCandidates:Hg,onPortClick:se,onCycleBreak:(Af,Zs)=>c0({application:Af,port:Zs})}),Tc=new Set(Mt.map(Af=>Af.id)),dc=gKn(g,x).filter(Af=>Tc.has(Af.source)&&Tc.has(Af.target));oKn(Mt,dc,x==="topology"?"topology":N==="overview"?"overview":"data_flow").then(o0),zg(dc)},[f5,cd,l5,xf,N,g,Hg,b1,k,Jg,zg,o0,Sf,x]);const Gg=He.useCallback((Mt,Tc)=>{if(Tc.data.nodeKind==="application")G(ud.get(Tc.data.applicationId)??null);else if(G(Tc.data.detail),Tc.data.nodeKind==="object"){const dc=Tc.data.detail;Z({label:`subtree ${dc.name||String(dc.objectId)}`,objectIds:pKn(g.objects,dc.objectId)})}else if(Tc.data.nodeKind==="instance"){const dc=Tc.data.detail;Z({label:`instance ${dc.name}`,objectIds:dc.objectIds})}else Tc.data.nodeKind==="scene"&&Z(null);se(null)},[ud,g.objects]),u6=He.useCallback(Mt=>{le&&(Mr(null),st({mode:"add",initialModelType:Mt.type,suggestedSelector:DKn(le.application)}),kn||Gt(`${Mt.name} matches ${le.port.name}. Start an interactive Julia editor session to add it to the Scene.`),ee(null))},[le,kn]),h5=He.useCallback(Mt=>{if(!kn){Gt("Adding or updating an application requires an interactive Julia editor session."),st(null);return}_u({action:"edit",kind:Mt.applicationId?At?.scope==="template"?"update_template_application":"update_application":"add_application",instance:At?.instance,...Mt}),st(null)},[At?.instance,At?.scope,kn,_u]),Wm=He.useCallback(Mt=>{if(!kn){Gt("Creating a binding requires an interactive Julia editor session."),ef(null);return}_u({action:"edit",kind:"set_input_binding",...Mt}),ef(null)},[kn,_u]),qg=He.useCallback(Mt=>{if(!kn){Gt("Adding or updating an object requires an interactive Julia editor session."),mi(null);return}_u({action:"edit",kind:ur?.mode==="update"?"update_object":"add_object",objectId:Mt.objectId,configuration:Mt.configuration}),mi(null)},[kn,ur?.mode,_u]),o6=He.useCallback(Mt=>{if(!kn){Gt("Creating an override requires an interactive Julia editor session."),fu(null);return}_u({action:"edit",kind:Mt.scope==="instance"?"set_instance_override":"set_object_override",...Mt}),fu(null)},[kn,_u]),Ug=He.useCallback(Mt=>{if(!kn){Gt("Removing an override requires an interactive Julia editor session.");return}_u({action:"edit",kind:Mt.scope==="instance"?"remove_instance_override":"remove_object_override",...Mt}),fu(null)},[kn,_u]),Zm=He.useCallback(Mt=>{if(!Mt.sourceHandle||!Mt.targetHandle)return;const Tc=Eb.get(Mt.sourceHandle),dc=Eb.get(Mt.targetHandle);if(!Tc||!dc||Tc.port.role!=="output"||dc.port.role!=="input"){Gt("Connect an application output to an application input.");return}ef({sourceApplication:Tc.application,sourcePort:Tc.port,targetApplication:dc.application,targetPort:dc.port}),kc(null)},[Eb]),d5=He.useMemo(()=>{if(!U)return g.initialization;if("applicationId"in U)return g.initialization.filter(Mt=>Mt.applicationId===U.applicationId);if("objectId"in U)return g.initialization.filter(Mt=>String(Mt.objectId)===String(U.objectId));if("objectIds"in U){const Mt=new Set(U.objectIds.map(n6));return g.initialization.filter(Tc=>Mt.has(n6(Tc.objectId)))}return g.initialization},[g.initialization,U]);return F.jsxs("main",{className:"scene-editor-shell","data-testid":"scene-graph-viewer",children:[F.jsxs("header",{className:"scene-toolbar",children:[F.jsxs("div",{className:"scene-brand",children:[F.jsx("span",{className:"brand-mark"}),F.jsxs("div",{children:[F.jsx("small",{children:"PLANTSIMENGINE"}),F.jsx("strong",{children:"Scene Graph"})]})]}),F.jsxs("div",{className:"scene-search",children:[F.jsx(IXn,{size:17}),F.jsx("input",{value:k,onChange:Mt=>H(Mt.target.value),placeholder:"Search application, object, or variable"}),k&&F.jsx("button",{"aria-label":"Clear search",onClick:()=>H(""),children:F.jsx(Ym,{size:15})})]}),F.jsxs("div",{className:"scene-counts",children:[F.jsxs("span",{children:[g.metadata.applicationCount," applications"]}),F.jsxs("span",{children:[g.metadata.objectCount," objects"]}),g.metadata.unresolvedInitializationCount>0&&F.jsxs("button",{className:"count-warning",onClick:()=>ae(!0),children:[F.jsx(SXn,{size:14})," ",g.metadata.unresolvedInitializationCount," init"]}),g.diagnostics.length>0&&F.jsxs("button",{className:"count-error",onClick:()=>ge(!0),children:[F.jsx(dke,{size:14})," ",g.diagnostics.length]})]}),F.jsxs("nav",{className:"view-tabs","aria-label":"Graph projection",children:[F.jsxs("button",{className:x==="applications"?"active":"",onClick:()=>M("applications"),children:[F.jsx(Y0n,{size:15})," Applications"]}),F.jsxs("button",{className:x==="topology"?"active":"",onClick:()=>M("topology"),children:[F.jsx(TXn,{size:15})," Objects"]}),F.jsxs("button",{className:x==="resolved"?"active":"",onClick:()=>M("resolved"),children:[F.jsx(CXn,{size:15})," Executions"]})]}),F.jsxs("div",{className:"scene-actions",children:[d1&&F.jsxs("button",{"data-testid":"open-scene",onClick:()=>ln(!0),children:[F.jsx(MXn,{size:15})," Open"]}),d1&&F.jsxs("button",{"data-testid":"save-scene",onClick:()=>hn(!0),children:[F.jsx(DXn,{size:15})," ",Fe?"Saved":"Save"]}),x!=="topology"&&F.jsx("button",{className:N==="overview"?"overview-cta":"",onClick:()=>P(Mt=>Mt==="overview"?"detail":"overview"),children:N==="overview"?"Overview Mode - Show Detailed View":"Show Overview"}),d1&&F.jsxs("button",{"data-testid":"add-application",onClick:()=>{Mr(null),st({mode:"add"})},children:[F.jsx(QG,{size:15})," Add application"]}),d1&&F.jsxs("button",{"data-testid":"add-object",onClick:()=>mi({mode:"add"}),children:[F.jsx(QG,{size:15})," Add object"]}),d1&&F.jsx("button",{disabled:!un,onClick:()=>_u({action:"undo"}),"aria-label":"Undo",children:F.jsx(_Xn,{size:15})}),d1&&F.jsx("button",{disabled:!lt,onClick:()=>_u({action:"redo"}),"aria-label":"Redo",children:F.jsx(OXn,{size:15})}),F.jsxs("button",{onClick:()=>Be(!0),children:[F.jsx(AXn,{size:15})," Scene code"]})]})]}),g.metadata.cyclic&&F.jsxs("section",{className:"cycle-callout","data-testid":"cycle-callout",children:[F.jsx(dke,{size:19}),F.jsxs("div",{children:[F.jsx("strong",{children:"Current-step dependency cycle"}),F.jsx("span",{children:"Select a cycle input to read its previous accepted timestep value."})]}),F.jsx("button",{className:cd?"active":"",onClick:()=>{M("applications"),P("detail"),jb(Mt=>!Mt)},"data-testid":"choose-cycle-break",children:cd?"Cancel break selection":"Choose a break point in graph"})]}),hi&&F.jsxs("div",{className:"editor-feedback",children:[hi,F.jsx("button",{onClick:()=>Gt(null),children:F.jsx(Ym,{size:14})})]}),ie&&F.jsxs("section",{className:"graph-scope-filter","data-testid":"graph-scope-filter",children:[F.jsxs("span",{children:["Showing ",x==="resolved"?"executions":x==="applications"?"applications":"topology"," for ",F.jsx("strong",{children:ie.label})," (",ie.objectIds.length," objects)"]}),x==="topology"&&F.jsx("button",{onClick:()=>M("applications"),children:"Show related applications"}),F.jsxs("button",{"aria-label":"Clear graph scope",onClick:()=>Z(null),children:[F.jsx(Ym,{size:14})," Clear"]})]}),F.jsxs("section",{className:"scene-workspace",children:[F.jsx("div",{className:"flow-wrap",children:F.jsxs(zUn,{nodes:u0,edges:r6,nodeTypes:aKn,edgeTypes:hKn,onNodesChange:Bg,onEdgesChange:c6,onConnect:Zm,onNodeClick:Gg,onEdgeClick:(Mt,Tc)=>G(Tc.data??null),fitView:!0,minZoom:.05,maxZoom:2,children:[F.jsx(VUn,{color:"#d8cdbc",gap:22,size:1}),F.jsx(tXn,{}),F.jsx(gXn,{pannable:!0,zoomable:!0})]})}),F.jsx(SKn,{selection:U,port:W,initialization:d5,interactive:kn,onEditApplication:Mt=>{Mr(null),st({mode:"update",scope:Mt.targetInstances.length>0?"template":"application",instance:Mt.targetInstances[0],application:Mt})},onRemoveApplication:Mt=>_u({action:"edit",kind:Mt.targetInstances.length>0?"remove_template_application":"remove_application",instance:Mt.targetInstances[0],applicationId:Mt.applicationId}),onConfigureApplication:Mt=>Ws(Mt.applicationId),onOverrideApplication:fu,onEditObject:Mt=>mi({mode:"update",object:Mt}),onRemoveObject:Mt=>_u({action:"edit",kind:"remove_object",objectId:Mt.objectId,recursive:!0})})]}),le&&(a5.length>0||Fg.length>0)&&F.jsx(kKn,{candidate:le,models:a5,applications:Fg,onSelectModel:u6,onSelectApplication:Mt=>{ef(EKn(le,Mt)),kc(null),ee(null)},onClose:()=>ee(null)}),Oe&&F.jsx(xKn,{graph:g,onClose:()=>ge(!1),sendCommand:_u,interactive:kn}),Pe&&F.jsx(AKn,{graph:g,onClose:()=>ae(!1),sendCommand:_u,interactive:kn}),Me&&F.jsx(TKn,{code:wt,onClose:()=>Be(!1)}),rn&&F.jsx(udn,{mode:"open",recentPaths:Ae,currentPath:Fe,autosavePath:Qn,onSubmit:Mt=>{_u({action:"open_scene_code",path:Mt}),ln(!1)},onClose:()=>ln(!1)}),xn&&F.jsx(udn,{mode:"save",recentPaths:Ae,currentPath:Fe,autosavePath:Qn,onSubmit:Mt=>{_u({action:"save_scene_code",path:Mt}),hn(!1)},onClose:()=>hn(!1)}),At&&F.jsx(LXn,{mode:At.mode,models:g.modelLibrary,objects:g.objects,application:At.application,initialModelType:At.initialModelType,suggestedSelector:At.suggestedSelector,nameReadOnly:At.scope==="template",preview:Wr,onPreview:Mt=>{Mr(null),_u({action:"preview_application_targets",selector:Mt})},onSubmit:h5,onClose:()=>{st(null),Mr(null)}}),Dh&&F.jsx(FXn,{endpoints:Dh,objects:g.objects,preview:ch,onPreview:Mt=>{kc(null),_u({action:"preview_input_binding",...Mt})},onSubmit:Wm,onClose:()=>{ef(null),kc(null)}}),ur&&F.jsx(UXn,{mode:ur.mode,objects:g.objects,object:ur.object,onSubmit:qg,onClose:()=>mi(null)}),Fi&&F.jsx(KXn,{application:Fi,models:g.modelLibrary,instances:g.instances,onSubmit:o6,onRemove:Ug,onClose:()=>fu(null)}),Eu&&ud.get(Eu)&&F.jsx(zXn,{application:ud.get(Eu),applications:g.applications,onCommand:_u,onClose:()=>Ws(null)}),Rs&&F.jsx(CKn,{selection:Rs,initialization:g.initialization,onSubmit:(Mt,Tc)=>{_u({action:"edit",kind:"break_cycle",applicationId:Rs.application.applicationId,input:Rs.port.name,initializeMissing:Mt,initialValue:Tc}),c0(null)},onClose:()=>c0(null)})]})}function bKn({graph:g,view:E,detailMode:x,query:M,scopedObjectIds:N,unresolvedPortIds:P,previousPortIds:k,candidatePortIds:H,cyclicApplications:U,cycleBreakPortIds:G,cycleBreakMode:ie,openCandidates:Z,onPortClick:W,onCycleBreak:se}){const le=Oe=>!M||JSON.stringify(Oe).toLowerCase().includes(M.toLowerCase());if(E==="topology"){const Oe={entity:"scene",objectCount:g.metadata.objectCount,instanceCount:g.metadata.instanceCount,applicationCount:g.metadata.applicationCount},ge={id:"scene:root",type:"entity",position:{x:0,y:0},data:{nodeKind:"scene",title:g.metadata.title||"Scene",subtitle:"scene root",badges:[`${g.metadata.instanceCount} instances`,`${g.metadata.objectCount} objects`],detail:Oe}},Pe=g.instances.filter(le).map(Me=>({id:Me.id,type:"entity",position:{x:0,y:0},data:{nodeKind:"instance",title:Me.name,subtitle:[Me.kind,Me.species].filter(Boolean).join(" · ")||"object instance",badges:[`${Me.objectIds.length} objects`,`${Me.applicationIds.length} applications`,`${Me.instanceOverrides.length+Me.objectOverrides.length} overrides`],detail:Me}})),ae=g.objects.filter(le).map(Me=>({id:Me.id,type:"entity",position:{x:0,y:0},data:{nodeKind:"object",title:Me.name||String(Me.objectId),subtitle:[Me.kind,Me.scale,Me.instance].filter(Boolean).join(" · "),badges:[Me.species,Me.hasStatus?"status":null,Me.hasGeometry?"geometry":null].filter(Boolean),detail:Me}}));return[ge,...Pe,...ae]}if(E==="resolved"){const Oe=new Map(g.applications.map(Pe=>[Pe.applicationId,Pe]));return[...g.executions.filter(Pe=>!N||N.has(n6(Pe.objectId))).filter(le).map(Pe=>{const ae=Oe.get(Pe.applicationId);return{id:Pe.id,type:"entity",position:{x:0,y:0},data:{nodeKind:"execution",title:Pe.applicationId,subtitle:`object ${String(Pe.objectId)}`,badges:[NKn(Pe.modelType),Pe.overridden?"override":"shared"],inputPortIds:[...ae?.inputs??[],...ae?.environmentInputs??[]].map(Me=>Me.id),outputPortIds:[...ae?.outputs??[],...ae?.environmentOutputs??[]].map(Me=>Me.id),detail:Pe}}}),...idn(g,"resolved")]}return[...g.applications.filter(Oe=>!N||Oe.targetIds.some(ge=>N.has(n6(ge)))).filter(le).map(Oe=>({id:Oe.id,type:"application",position:{x:0,y:0},data:{...Oe,nodeKind:"application",detailMode:x,cyclic:U.has(Oe.applicationId),requiredInputPortIds:Oe.inputs.filter(ge=>P.has(ge.id)).map(ge=>ge.id),candidatePortIds:[...Oe.inputs,...Oe.outputs].filter(ge=>H.has(ge.id)).map(ge=>ge.id),previousTimeStepPortIds:Oe.inputs.filter(ge=>k.has(ge.id)).map(ge=>ge.id),cycleBreakInputPortIds:Oe.inputs.filter(ge=>G.has(ge.id)).map(ge=>ge.id),cycleBreakMode:ie,onCandidateClick:(ge,Pe)=>Z(Oe,ge,Pe),onPortClick:W,onCycleBreak:se}})),...idn(g,"applications")]}function idn(g,E){const x=g.edges.filter(N=>N.kind==="environment_binding"&&N.projection===E);return[...new Set(x.flatMap(N=>[N.source,N.target]).filter(N=>N.startsWith("environment:")))].map(N=>{const P=N.slice(12),k=rdn(x.filter(U=>U.target===N).map(U=>U.targetPort).filter(Boolean)),H=rdn(x.filter(U=>U.source===N).map(U=>U.sourcePort).filter(Boolean));return{id:N,type:"entity",position:{x:0,y:0},data:{nodeKind:"environment",title:P,subtitle:"environment provider",badges:[`${H.length} inputs`,`${k.length} outputs`],inputPortIds:k,outputPortIds:H,detail:{provider:P}}}})}function rdn(g){return[...new Set(g)]}function gKn(g,E){return(E==="topology"?[...g.edges,...wKn(g)]:g.edges).filter(M=>mKn(M,E)).map(M=>({id:M.id,source:M.source,target:M.target,sourceHandle:M.sourcePort||void 0,targetHandle:M.targetPort||void 0,type:"sceneEdge",data:M,markerEnd:{type:UG.ArrowClosed,color:cdn(M),width:16,height:16},style:{stroke:cdn(M),strokeWidth:M.cycle?4:M.kind==="manual_call"?2.5:1.8,strokeDasharray:M.kind==="previous_timestep"?"7 5":M.kind==="manual_call"?"3 4":void 0}}))}function wKn(g){const E=[],x=new Set(g.instances.flatMap(M=>M.objectIds.map(n6)));for(const M of g.instances)E.push({id:`topology:scene:${M.id}`,source:"scene:root",target:M.id,kind:"object_topology",projection:"topology",cycle:!1}),E.push({id:`topology:${M.id}:object:${String(M.rootId)}`,source:M.id,target:`object:${String(M.rootId)}`,kind:"object_topology",projection:"topology",cycle:!1});for(const M of g.objects)M.parent===null&&!x.has(n6(M.objectId))&&E.push({id:`topology:scene:${M.id}`,source:"scene:root",target:M.id,kind:"object_topology",projection:"topology",cycle:!1});return E}function pKn(g,E){const x=new Map;for(const k of g){if(k.parent===null)continue;const H=n6(k.parent);x.set(H,[...x.get(H)??[],k.objectId])}const M=[],N=[E],P=new Set;for(;N.length>0;){const k=N.pop(),H=n6(k);P.has(H)||(P.add(H),M.push(k),N.push(...x.get(H)??[]))}return M}function n6(g){return String(g)}function mKn(g,E){const x=g.projection;return E==="topology"?g.kind==="object_topology":E==="resolved"?x==="resolved":x==="applications"||!x&&!["object_topology","application_target"].includes(g.kind)}function cdn(g){return g.cycle?"#cf4937":g.kind==="previous_timestep"?"#317b62":g.kind==="manual_call"?"#be6a54":g.kind==="object_topology"?"#7b7167":g.kind==="environment_binding"?"#367b8b":"#a59687"}function vKn(g){const E=new Set;for(const x of g.applications){for(const M of x.inputs)(g.applications.some(P=>P.applicationId!==x.applicationId&&P.outputs.some(k=>k.name===M.name))||g.modelLibrary.some(P=>Object.prototype.hasOwnProperty.call(P.outputs,M.name)))&&E.add(M.id);for(const M of x.outputs)(g.applications.some(P=>P.applicationId!==x.applicationId&&P.inputs.some(k=>k.name===M.name))||g.modelLibrary.some(P=>Object.prototype.hasOwnProperty.call(P.inputs,M.name)))&&E.add(M.id)}return E}function yKn(g,E){const x=E.role==="input"?"outputs":"inputs";return g.filter(M=>Object.prototype.hasOwnProperty.call(M[x],E.name)).sort((M,N)=>`${M.package}.${M.name}`.localeCompare(`${N.package}.${N.name}`))}function kKn({candidate:g,models:E,applications:x,onSelectModel:M,onSelectApplication:N,onClose:P}){const k=g.port.role==="input"?`Models that compute ${g.port.name}`:`Models that consume ${g.port.name}`;return F.jsxs("section",{className:"candidate-popover",style:{left:Math.min(g.x+8,window.innerWidth-390),top:Math.min(g.y-20,window.innerHeight-480)},children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:k}),F.jsx("span",{children:"Exact declared variable-name matches"})]}),F.jsx("button",{onClick:P,children:F.jsx(Ym,{size:15})})]}),F.jsxs("div",{className:"candidate-list",children:[x.length>0&&F.jsx("div",{className:"candidate-section-label",children:"Existing applications"}),x.map(H=>F.jsxs("button",{className:"candidate-card existing",onClick:()=>N(H),children:[F.jsx("strong",{children:H.name||H.applicationId}),F.jsx("span",{children:H.modelName}),F.jsxs("small",{children:[H.targetCount," target",H.targetCount===1?"":"s"]}),F.jsx("div",{children:"Connect without adding another application"})]},H.applicationId)),E.length>0&&F.jsx("div",{className:"candidate-section-label",children:"Available models"}),E.map(H=>F.jsxs("button",{className:"candidate-card",onClick:()=>M(H),children:[F.jsx("strong",{children:H.name}),F.jsx("span",{children:H.process}),F.jsx("small",{children:H.package||H.module}),F.jsxs("div",{children:[Object.keys(H.inputs).length," inputs · ",Object.keys(H.outputs).length," outputs"]})]},H.type))]})]})}function jKn(g,E){return g.filter(x=>x.applicationId!==E.application.applicationId).filter(x=>(E.port.role==="input"?x.outputs:x.inputs).some(N=>N.name===E.port.name)).sort((x,M)=>x.applicationId.localeCompare(M.applicationId))}function EKn(g,E){if(g.port.role==="input"){const M=E.outputs.find(N=>N.name===g.port.name);if(!M)throw new Error(`Application ${E.applicationId} does not output ${g.port.name}.`);return{sourceApplication:E,sourcePort:M,targetApplication:g.application,targetPort:g.port}}const x=E.inputs.find(M=>M.name===g.port.name);if(!x)throw new Error(`Application ${E.applicationId} does not input ${g.port.name}.`);return{sourceApplication:g.application,sourcePort:g.port,targetApplication:E,targetPort:x}}function SKn({selection:g,port:E,initialization:x,interactive:M,onEditApplication:N,onConfigureApplication:P,onRemoveApplication:k,onOverrideApplication:H,onEditObject:U,onRemoveObject:G}){const ie=g&&"applicationId"in g&&"selector"in g?g:null,Z=g&&"objectId"in g&&!("applicationId"in g)?g:null;return F.jsxs("aside",{className:"scene-inspector",children:[F.jsxs("header",{children:[F.jsx("strong",{children:"Inspector"}),g&&F.jsx("span",{children:OKn(g)})]}),!g&&F.jsxs("div",{className:"empty-inspector",children:[F.jsx(EXn,{size:28}),F.jsx("p",{children:"Select an application, object, execution, or relationship."})]}),g&&F.jsx("pre",{children:JSON.stringify(g,null,2)}),ie&&M&&F.jsxs("div",{className:"inspector-actions",children:[F.jsx("button",{onClick:()=>N(ie),children:ie.targetInstances.length>0?"Edit shared template":"Edit application"}),ie.targetInstances.length===0&&F.jsx("button",{"data-testid":"configure-application",onClick:()=>P(ie),children:"Configure coupling"}),ie.targetInstances.length>0&&F.jsx("button",{onClick:()=>H(ie),children:"Create override"}),F.jsx("button",{className:"danger",onClick:()=>k(ie),children:ie.targetInstances.length>0?"Remove from shared template":"Remove application"})]}),Z&&M&&F.jsxs("div",{className:"inspector-actions",children:[F.jsx("button",{onClick:()=>U(Z),children:"Edit object"}),F.jsx("button",{className:"danger",onClick:()=>G(Z),children:"Remove object and descendants"})]}),E&&F.jsxs("section",{children:[F.jsx("h4",{children:"Selected variable"}),F.jsx("code",{children:E.name}),F.jsx("p",{children:E.expectedType})]}),g&&x.length>0&&F.jsxs("section",{className:"inspector-initialization",children:[F.jsx("h4",{children:"Initialization"}),x.slice(0,8).map(W=>F.jsxs("div",{children:[F.jsx("code",{children:W.variable}),F.jsx("span",{className:W.disposition==="unresolved"?"unresolved":"",children:W.disposition})]},`${W.applicationId}:${W.objectId}:${W.variable}`))]})]})}function xKn({graph:g,onClose:E,sendCommand:x,interactive:M}){return F.jsxs(Fue,{title:"Diagnostics and cycles",onClose:E,children:[g.diagnostics.map(N=>F.jsxs("article",{className:"diagnostic-card",children:[F.jsx("strong",{children:N.code}),F.jsx("p",{children:N.message}),N.suggestions.map(P=>F.jsx("small",{children:P},P))]},`${N.code}:${N.message}`)),g.cycles.map(N=>F.jsxs("article",{className:"cycle-card",children:[F.jsx("strong",{children:N.applicationIds.join(" → ")}),F.jsx("p",{children:"Choose an input to read from the previous timestep."}),N.breakCandidates.map(P=>F.jsxs("button",{disabled:!M,onClick:()=>x({action:"edit",kind:"mark_previous_timestep",applicationId:P.applicationId,input:P.input}),children:[P.applicationId,".",P.input]},`${P.applicationId}:${P.objectId}:${P.input}`))]},N.id)),g.diagnostics.length===0&&g.cycles.length===0&&F.jsx("p",{children:"No diagnostics."})]})}function AKn({graph:g,onClose:E,sendCommand:x,interactive:M}){const N=g.initialization.filter(k=>k.disposition==="unresolved"),P=new Map;for(const k of N){const H=`${k.applicationId}:${k.variable}`;P.set(H,[...P.get(H)||[],k])}return F.jsxs(Fue,{title:"Initialization",onClose:E,children:[[...P.entries()].map(([k,H])=>F.jsx(MKn,{rows:H,interactive:M,sendCommand:x},k)),N.length===0&&F.jsx("p",{children:"No unresolved initial values."})]})}function MKn({rows:g,interactive:E,sendCommand:x}){const[M,N]=He.useState("float"),[P,k]=He.useState(""),H=g[0],U={type:M,value:P};return F.jsxs("article",{className:"initialization-group",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:H.variable}),F.jsx("span",{children:H.applicationId})]}),F.jsxs("small",{children:[g.length," object",g.length===1?"":"s"," · expected ",H.expectedType]})]}),F.jsx("p",{children:"Required because the input has no producer, environment source, status value, or usable temporal initialization."}),E&&F.jsxs("div",{className:"initialization-value",children:[F.jsxs("label",{children:["Type",F.jsxs("select",{value:M,onChange:G=>N(G.target.value),children:[F.jsx("option",{value:"float",children:"Float"}),F.jsx("option",{value:"integer",children:"Integer"}),F.jsx("option",{value:"boolean",children:"Boolean"}),F.jsx("option",{value:"symbol",children:"Symbol"}),F.jsx("option",{value:"string",children:"String"}),F.jsx("option",{value:"julia",children:"Julia expression"})]})]}),F.jsxs("label",{children:["Value",F.jsx("input",{value:P,onChange:G=>k(G.target.value)})]}),F.jsx("button",{disabled:!P.trim(),onClick:()=>x({action:"edit",kind:"set_object_statuses",objectIds:g.map(G=>G.objectId),variable:H.variable,value:U}),children:"Set all targets"})]}),F.jsx("div",{className:"initialization-object-list",children:g.map(G=>F.jsxs("div",{children:[F.jsxs("span",{children:["Object ",String(G.objectId)]}),F.jsx("code",{children:G.origin}),E&&F.jsx("button",{disabled:!P.trim(),onClick:()=>x({action:"edit",kind:"set_object_status",objectId:G.objectId,variable:G.variable,value:U}),children:"Set this object"})]},String(G.objectId)))})]})}function TKn({code:g,onClose:E}){return F.jsx(Fue,{title:"Scene code",onClose:E,children:F.jsx("pre",{className:"scene-code",children:g||"Scene code is available from an interactive editor session."})})}function udn({mode:g,recentPaths:E,currentPath:x,autosavePath:M,onSubmit:N,onClose:P}){const[k,H]=He.useState(x||"");return F.jsx(Fue,{title:g==="open"?"Open Scene":"Save Scene",onClose:P,children:F.jsxs("div",{className:"scene-file-dialog",children:[F.jsx("p",{children:g==="open"?"Open a Julia script whose final binding is `scene = Scene(...)`. Future edits will be saved back to that file.":"After the first save, every successful graph edit automatically rewrites this Julia script."}),F.jsxs("label",{children:["Julia file path",F.jsxs("div",{className:"scene-path-input",children:[F.jsx("input",{value:k,onChange:U=>H(U.target.value),placeholder:"/absolute/path/to/scene.jl",autoFocus:!0}),F.jsx("button",{className:"primary",disabled:!k.trim(),onClick:()=>N(k.trim()),children:g==="open"?"Open":"Save"})]})]}),g==="open"&&E.length>0&&F.jsxs("section",{children:[F.jsx("strong",{children:"Recent scenes"}),F.jsx("div",{className:"recent-scene-list",children:E.map(U=>F.jsxs("button",{onClick:()=>N(U),children:[F.jsx("span",{children:U.split("/").at(-1)}),F.jsx("small",{children:U})]},U))})]}),g==="open"&&M&&F.jsxs("section",{children:[F.jsx("strong",{children:"Recovery autosave"}),F.jsx("button",{className:"recovery-path",onClick:()=>N(M),children:M})]}),F.jsx("small",{children:"Use Git to version saved Scene scripts and review scientific configuration changes."})]})})}function CKn({selection:g,initialization:E,onSubmit:x,onClose:M}){const N=E.filter(G=>G.applicationId===g.application.applicationId&&G.variable===g.port.name&&G.disposition!=="supplied"),[P,k]=He.useState("float"),[H,U]=He.useState("");return F.jsx("div",{className:"overlay-backdrop",onMouseDown:M,children:F.jsxs("section",{className:"overlay-panel cycle-break-dialog",onMouseDown:G=>G.stopPropagation(),"data-testid":"cycle-break-dialog",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:"Break the current-step cycle"}),F.jsxs("span",{children:[g.application.applicationId,".",g.port.name]})]}),F.jsx("button",{onClick:M,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content",children:[F.jsx("p",{children:"This changes the application input to read its value from the previous accepted timestep. The model is disconnected from the current value during each run step."}),F.jsxs("div",{className:"cycle-impact",children:[F.jsx("strong",{children:"Application-wide change"}),F.jsxs("span",{children:["It affects all ",g.application.targetCount," targets selected by this application."]})]}),N.length>0&&F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Required initial value"}),F.jsxs("p",{children:[N.length," target",N.length===1?"":"s"," need a value before the first timestep."]}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Value type",F.jsxs("select",{value:P,onChange:G=>k(G.target.value),children:[F.jsx("option",{value:"float",children:"Float"}),F.jsx("option",{value:"integer",children:"Integer"}),F.jsx("option",{value:"boolean",children:"Boolean"}),F.jsx("option",{value:"symbol",children:"Symbol"}),F.jsx("option",{value:"string",children:"String"}),F.jsx("option",{value:"julia",children:"Julia expression"})]})]}),F.jsxs("label",{children:["Initial value",F.jsx("input",{value:H,onChange:G=>U(G.target.value),autoFocus:!0})]})]})]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:M,children:"Cancel"}),F.jsxs("button",{className:"primary",disabled:N.length>0&&!H.trim(),onClick:()=>x(N.length>0,N.length>0?{type:P,value:H}:null),"data-testid":"confirm-cycle-break",children:[F.jsx(Q0n,{size:15})," Use previous timestep"]})]})]})})}function Fue({title:g,onClose:E,children:x}){return F.jsx("div",{className:"overlay-backdrop",onMouseDown:E,children:F.jsxs("section",{className:"overlay-panel",onMouseDown:M=>M.stopPropagation(),children:[F.jsxs("header",{children:[F.jsx("strong",{children:g}),F.jsx("button",{onClick:E,children:F.jsx(Ym,{size:17})})]}),F.jsx("div",{className:"overlay-content",children:x})]})})}function OKn(g){return"applicationId"in g?g.applicationId:"objectId"in g?String(g.objectId):"objectIds"in g?g.name:"entity"in g?"Scene":"provider"in g?g.provider:g.kind.replaceAll("_"," ")}function NKn(g){return g.split(".").at(-1)||g}function DKn(g){const E={selectors:[]};return g.targetInstances.length===0&&g.targetScales.length===1&&(E.scale=g.targetScales[0]),g.targetKinds.length===1&&(E.kind=g.targetKinds[0]),g.targetSpecies.length===1&&(E.species=g.targetSpecies[0]),{type:g.targetCount===1?"One":"Many",multiplicity:g.targetCount===1?"one":"many",criteria:E,julia:""}}function Q7e(g,E,x){return`application:${g}:${E}:${x}`}function W7e(){const g=document.getElementById("pse-scene-graph-data");if(!g?.textContent)return edn;try{return JSON.parse(g.textContent)}catch{return edn}}function IKn(){const g=document.getElementById("pse-editor-config");if(!g?.textContent)return null;try{return JSON.parse(g.textContent)}catch{return null}}class _Kn extends He.Component{state={error:null};static getDerivedStateFromError(E){return{error:E}}componentDidCatch(E,x){console.error("PlantSimEngine Scene graph frontend failed",E,x)}render(){return this.state.error?F.jsxs("main",{className:"frontend-error","data-testid":"frontend-error",children:[F.jsx(dke,{size:28}),F.jsx("h1",{children:"The graph view could not be rendered"}),F.jsx("p",{children:this.state.error.message}),F.jsxs("button",{onClick:()=>window.location.reload(),children:[F.jsx(NXn,{size:15})," Reload graph"]})]}):this.props.children}}lzn.createRoot(document.getElementById("root")).render(F.jsx(He.StrictMode,{children:F.jsx(_Kn,{children:F.jsx(dKn,{})})})); +... Falling back to non-web worker version.`);if(!Me.workerFactory){var ln=x("./elk-worker.min.js"),xn=ln.Worker;Me.workerFactory=function(hn){return new xn(hn)}}return U(this,Pe,[Me])}return se(Pe,ge),k(Pe)})(ee);Object.defineProperty(M.exports,"__esModule",{value:!0}),M.exports=Oe,Oe.default=Oe},{"./elk-api.js":1,"./elk-worker.min.js":2,"web-worker":4}],4:[function(x,M,N){var P=typeof Worker<"u"?Worker:void 0;M.exports=P},{}]},{},[3])(3)})})(K7e)),K7e.exports}var rKn=iKn();const cKn=bke(rKn),uKn=new cKn;async function oKn(g,E,x){const M={id:"root",layoutOptions:sKn(x),children:g.map(k=>({id:k.id,width:lKn(k.data),height:fKn(k.data),ports:k.data.nodeKind==="application"?[...k.data.inputs.map((H,U)=>lue(H.id,"WEST",U)),...k.data.outputs.map((H,U)=>lue(H.id,"EAST",U))]:[...(k.data.inputPortIds??[]).map((H,U)=>lue(H,"WEST",U)),...(k.data.outputPortIds??[]).map((H,U)=>lue(H,"EAST",U))],layoutOptions:{"org.eclipse.elk.portConstraints":"FIXED_ORDER"}})),edges:E.map(k=>({id:k.id,sources:[k.sourceHandle??k.source],targets:[k.targetHandle??k.target]}))},N=await uKn.layout(M),P=new Map((N.children??[]).map(k=>[k.id,{x:k.x??0,y:k.y??0}]));return g.map(k=>({...k,position:P.get(k.id)??k.position}))}function sKn(g){return{"elk.algorithm":g==="topology"?"mrtree":"layered","elk.direction":g==="topology"?"DOWN":"RIGHT","elk.spacing.nodeNode":g==="overview"?"24":g==="compact"?"32":"56","elk.layered.spacing.nodeNodeBetweenLayers":g==="overview"?"48":g==="compact"?"60":"110","elk.layered.nodePlacement.strategy":"BRANDES_KOEPF","elk.layered.crossingMinimization.semiInteractive":"true","elk.edgeRouting":"ORTHOGONAL"}}function lue(g,E,x){return{id:g,width:9,height:9,layoutOptions:{"org.eclipse.elk.port.side":E,"org.eclipse.elk.port.index":String(x)}}}function lKn(g){return g.nodeKind==="application"?Z0n(g):240}function fKn(g){return g.nodeKind!=="application"?112:g.detailMode==="overview"?108:Math.max(178,142+Math.max(g.inputs.length,g.outputs.length)*27)}const V7e=Ike("source","degree_days","ToyDegreeDaysCumulModel",[],["TT_cu"]),fue=Ike("lai","lai_dynamic","ToyLAIModel",["TT_cu"],["LAI"]),Y7e=Ike("light","light_interception","Beer",["LAI"],["aPPFD"]),edn={schemaVersion:1,level:"applications",metadata:{title:"PlantSimEngine Model Graph",modelRevision:0,objectCount:1,instanceCount:0,applicationCount:3,executionCount:3,bindingCount:2,callCount:0,unresolvedInitializationCount:1,cyclic:!1,strictlyCompiled:!0},objects:[{id:"object:plant",objectId:"plant",scale:"Plant",kind:"plant",species:null,name:"plant",instance:null,parent:null,children:[],hasGeometry:!1,hasStatus:!0}],instances:[],applications:[V7e,fue,Y7e],executions:[V7e,fue,Y7e].map(g=>({id:`execution:${g.applicationId}:plant`,applicationId:g.applicationId,applicationNodeId:g.id,objectId:"plant",objectNodeId:"object:plant",modelType:g.modelType,modelParameters:{},overridden:!1})),edges:[tdn(V7e,"TT_cu",fue,"TT_cu"),tdn(fue,"LAI",Y7e,"LAI")],modelLibrary:[],initialization:[{applicationId:"source",objectId:"plant",variable:"TT",role:"input",disposition:"unresolved",value:"-Inf",valueJulia:"-Inf",expectedType:"Float64",sourceApplicationIds:[],sourceObjectIds:[],sourceVariable:null,origin:"missing",previousTimeStep:!1}],diagnostics:[],cycles:[],availableActions:["inspect"]};function Ike(g,E,x,M,N){return{id:`application:${g}`,applicationId:g,name:g,process:E,modelType:x,modelName:x,module:"PlantSimEngine.Examples",package:"PlantSimEngine",modelParameters:{},selector:{type:"One",multiplicity:"one",criteria:{scale:"Plant"},julia:"One(scale=:Plant)"},targetIds:["plant"],targetCount:1,targetScales:["Plant"],targetKinds:["plant"],targetSpecies:[],targetInstances:[],timestep:null,clock:null,inputs:M.map(P=>ndn(g,"input",P)),outputs:N.map(P=>ndn(g,"output",P)),environmentInputs:[],environmentOutputs:[],inputBindings:{},callBindings:{},environment:null,meteoBindings:{},meteoWindow:null,outputRouting:{},updates:[],modelStorage:"shared_application",objectOverrides:[]}}function ndn(g,E,x){return{id:`application:${g}:${E}:${x}`,name:x,role:E,default:"-Inf",defaultJulia:"-Inf",expectedType:"Float64"}}function tdn(g,E,x,M){return{id:`binding:${g.applicationId}:${E}:${x.applicationId}:${M}`,source:g.id,target:x.id,sourcePort:`application:${g.applicationId}:output:${E}`,targetPort:`application:${x.applicationId}:input:${M}`,sourceVariable:E,targetVariable:M,sourceApplicationId:g.applicationId,targetApplicationId:x.applicationId,kind:"inferred_same_object",cycle:!1,projection:"applications"}}const aKn={application:YXn,entity:QXn},hKn={modelEdge:nKn};function dKn(){const[g,E]=He.useState(W7e),[x,M]=He.useState(()=>W7e().level),[N,P]=He.useState(()=>W7e().metadata.applicationCount>24?"overview":"detail"),[k,H]=He.useState(""),[U,G]=He.useState(null),[ie,Z]=He.useState(null),[W,se]=He.useState(null),[le,ee]=He.useState(null),[Oe,ge]=He.useState(!1),[Pe,ae]=He.useState(!1),[Me,Be]=He.useState(!1),[rn,ln]=He.useState(!1),[xn,hn]=He.useState(!1),[wt,Tn]=He.useState(""),[Qn,Y]=He.useState(null),[Fe,mn]=He.useState(null),[Ae,Ze]=He.useState([]),[sn,Sn]=He.useState(null),[kn,xe]=He.useState(!1),[un,rt]=He.useState(!1),[lt,Bt]=He.useState(!1),[hi,Gt]=He.useState(null),[At,st]=He.useState(null),[Wr,Mr]=He.useState(null),[ur,mi]=He.useState(null),[Fi,fu]=He.useState(null),[Eu,Ws]=He.useState(null),[Dh,ef]=He.useState(null),[ch,kc]=He.useState(null),[cd,jb]=He.useState(!1),[Rs,c0]=He.useState(null),[u0,o0,Bg]=HUn([]),[r6,zg,c6]=GUn([]),d1=He.useMemo(IKn,[]),ud=He.useMemo(()=>new Map(g.applications.map(Mt=>[Mt.applicationId,Mt])),[g.applications]),Sf=He.useMemo(()=>new Set(g.initialization.filter(Mt=>Mt.role==="input"&&Mt.disposition==="unresolved").map(Mt=>Q7e(Mt.applicationId,"input",Mt.variable))),[g.initialization]),b1=He.useMemo(()=>new Set(g.initialization.filter(Mt=>Mt.role==="input"&&Mt.previousTimeStep).map(Mt=>Q7e(Mt.applicationId,"input",Mt.variable))),[g.initialization]),xf=He.useMemo(()=>new Set(g.cycles.flatMap(Mt=>Mt.applicationIds)),[g.cycles]),l5=He.useMemo(()=>new Set(g.cycles.flatMap(Mt=>Mt.breakCandidates.map(Cc=>Q7e(Cc.applicationId,"input",Cc.input)))),[g.cycles]),f5=He.useMemo(()=>vKn(g),[g]),a5=He.useMemo(()=>le?yKn(g.modelLibrary,le.port):[],[le,g.modelLibrary]),Fg=He.useMemo(()=>le?jKn(g.applications,le):[],[le,g.applications]),Eb=He.useMemo(()=>{const Mt=new Map;for(const Cc of g.applications)for(const dc of[...Cc.inputs,...Cc.outputs])Mt.set(dc.id,{application:Cc,port:dc});return Mt},[g.applications]),Jg=He.useMemo(()=>ie?new Set(ie.objectIds.map(n6)):null,[ie]);He.useEffect(()=>{if(!d1?.websocketUrl)return;const Mt=new WebSocket(d1.websocketUrl);return Sn(Mt),Mt.addEventListener("open",()=>{xe(!0),Gt(null)}),Mt.addEventListener("close",()=>{xe(!1),Gt("Editor connection closed.")}),Mt.addEventListener("message",Cc=>{const dc=JSON.parse(Cc.data);dc.graph&&E(dc.graph),typeof dc.modelCode=="string"&&Tn(dc.modelCode),Y(dc.autosavePath??null),mn(dc.savePath??null),Ze(dc.recentPaths??[]),dc.selectorPreview&&kc(dc.selectorPreview),dc.targetPreview&&Mr(dc.targetPreview),dc.ok===!1&&(kc(null),Mr(null)),rt(!!dc.canUndo),Bt(!!dc.canRedo),Gt(dc.ok===!1?dc.diagnostics?.[0]||"The edit failed.":null)}),()=>Mt.close()},[d1?.websocketUrl]),He.useEffect(()=>{g.metadata.cyclic||(jb(!1),c0(null))},[g.metadata.cyclic]);const _u=He.useCallback(Mt=>{if(!sn||sn.readyState!==WebSocket.OPEN){Gt("This action requires an interactive Julia editor session.");return}sn.send(JSON.stringify(Mt))},[sn]),Hg=He.useCallback((Mt,Cc,dc)=>{G(Mt),se(Cc),ee({application:Mt,port:Cc,x:dc.x,y:dc.y})},[]);He.useEffect(()=>{const Mt=bKn({graph:g,view:x,detailMode:N,query:k,scopedObjectIds:Jg,unresolvedPortIds:Sf,previousPortIds:b1,candidatePortIds:f5,cyclicApplications:xf,cycleBreakPortIds:l5,cycleBreakMode:cd,openCandidates:Hg,onPortClick:se,onCycleBreak:(Af,Zs)=>c0({application:Af,port:Zs})}),Cc=new Set(Mt.map(Af=>Af.id)),dc=gKn(g,x).filter(Af=>Cc.has(Af.source)&&Cc.has(Af.target));oKn(Mt,dc,x==="topology"?"topology":N==="overview"?"overview":"data_flow").then(o0),zg(dc)},[f5,cd,l5,xf,N,g,Hg,b1,k,Jg,zg,o0,Sf,x]);const Gg=He.useCallback((Mt,Cc)=>{if(Cc.data.nodeKind==="application")G(ud.get(Cc.data.applicationId)??null);else if(G(Cc.data.detail),Cc.data.nodeKind==="object"){const dc=Cc.data.detail;Z({label:`subtree ${dc.name||String(dc.objectId)}`,objectIds:pKn(g.objects,dc.objectId)})}else if(Cc.data.nodeKind==="instance"){const dc=Cc.data.detail;Z({label:`instance ${dc.name}`,objectIds:dc.objectIds})}else Cc.data.nodeKind==="model"&&Z(null);se(null)},[ud,g.objects]),u6=He.useCallback(Mt=>{le&&(Mr(null),st({mode:"add",initialModelType:Mt.type,suggestedSelector:DKn(le.application)}),kn||Gt(`${Mt.name} matches ${le.port.name}. Start an interactive Julia editor session to add it to the composite model.`),ee(null))},[le,kn]),h5=He.useCallback(Mt=>{if(!kn){Gt("Adding or updating an application requires an interactive Julia editor session."),st(null);return}_u({action:"edit",kind:Mt.applicationId?At?.scope==="template"?"update_template_application":"update_application":"add_application",instance:At?.instance,...Mt}),st(null)},[At?.instance,At?.scope,kn,_u]),Wm=He.useCallback(Mt=>{if(!kn){Gt("Creating a binding requires an interactive Julia editor session."),ef(null);return}_u({action:"edit",kind:"set_input_binding",...Mt}),ef(null)},[kn,_u]),qg=He.useCallback(Mt=>{if(!kn){Gt("Adding or updating an object requires an interactive Julia editor session."),mi(null);return}_u({action:"edit",kind:ur?.mode==="update"?"update_object":"add_object",objectId:Mt.objectId,configuration:Mt.configuration}),mi(null)},[kn,ur?.mode,_u]),o6=He.useCallback(Mt=>{if(!kn){Gt("Creating an override requires an interactive Julia editor session."),fu(null);return}_u({action:"edit",kind:Mt.scope==="instance"?"set_instance_override":"set_object_override",...Mt}),fu(null)},[kn,_u]),Ug=He.useCallback(Mt=>{if(!kn){Gt("Removing an override requires an interactive Julia editor session.");return}_u({action:"edit",kind:Mt.scope==="instance"?"remove_instance_override":"remove_object_override",...Mt}),fu(null)},[kn,_u]),Zm=He.useCallback(Mt=>{if(!Mt.sourceHandle||!Mt.targetHandle)return;const Cc=Eb.get(Mt.sourceHandle),dc=Eb.get(Mt.targetHandle);if(!Cc||!dc||Cc.port.role!=="output"||dc.port.role!=="input"){Gt("Connect an application output to an application input.");return}ef({sourceApplication:Cc.application,sourcePort:Cc.port,targetApplication:dc.application,targetPort:dc.port}),kc(null)},[Eb]),d5=He.useMemo(()=>{if(!U)return g.initialization;if("applicationId"in U)return g.initialization.filter(Mt=>Mt.applicationId===U.applicationId);if("objectId"in U)return g.initialization.filter(Mt=>String(Mt.objectId)===String(U.objectId));if("objectIds"in U){const Mt=new Set(U.objectIds.map(n6));return g.initialization.filter(Cc=>Mt.has(n6(Cc.objectId)))}return g.initialization},[g.initialization,U]);return F.jsxs("main",{className:"model-editor-shell","data-testid":"model-graph-viewer",children:[F.jsxs("header",{className:"model-toolbar",children:[F.jsxs("div",{className:"model-brand",children:[F.jsx("span",{className:"brand-mark"}),F.jsxs("div",{children:[F.jsx("small",{children:"PLANTSIMENGINE"}),F.jsx("strong",{children:"Model Graph"})]})]}),F.jsxs("div",{className:"model-search",children:[F.jsx(IXn,{size:17}),F.jsx("input",{value:k,onChange:Mt=>H(Mt.target.value),placeholder:"Search application, object, or variable"}),k&&F.jsx("button",{"aria-label":"Clear search",onClick:()=>H(""),children:F.jsx(Ym,{size:15})})]}),F.jsxs("div",{className:"model-counts",children:[F.jsxs("span",{children:[g.metadata.applicationCount," applications"]}),F.jsxs("span",{children:[g.metadata.objectCount," objects"]}),g.metadata.unresolvedInitializationCount>0&&F.jsxs("button",{className:"count-warning",onClick:()=>ae(!0),children:[F.jsx(SXn,{size:14})," ",g.metadata.unresolvedInitializationCount," init"]}),g.diagnostics.length>0&&F.jsxs("button",{className:"count-error",onClick:()=>ge(!0),children:[F.jsx(dke,{size:14})," ",g.diagnostics.length]})]}),F.jsxs("nav",{className:"view-tabs","aria-label":"Graph projection",children:[F.jsxs("button",{className:x==="applications"?"active":"",onClick:()=>M("applications"),children:[F.jsx(Y0n,{size:15})," Applications"]}),F.jsxs("button",{className:x==="topology"?"active":"",onClick:()=>M("topology"),children:[F.jsx(CXn,{size:15})," Objects"]}),F.jsxs("button",{className:x==="resolved"?"active":"",onClick:()=>M("resolved"),children:[F.jsx(TXn,{size:15})," Executions"]})]}),F.jsxs("div",{className:"model-actions",children:[d1&&F.jsxs("button",{"data-testid":"open-model",onClick:()=>ln(!0),children:[F.jsx(MXn,{size:15})," Open"]}),d1&&F.jsxs("button",{"data-testid":"save-model",onClick:()=>hn(!0),children:[F.jsx(DXn,{size:15})," ",Fe?"Saved":"Save"]}),x!=="topology"&&F.jsx("button",{className:N==="overview"?"overview-cta":"",onClick:()=>P(Mt=>Mt==="overview"?"detail":"overview"),children:N==="overview"?"Overview Mode - Show Detailed View":"Show Overview"}),d1&&F.jsxs("button",{"data-testid":"add-application",onClick:()=>{Mr(null),st({mode:"add"})},children:[F.jsx(QG,{size:15})," Add application"]}),d1&&F.jsxs("button",{"data-testid":"add-object",onClick:()=>mi({mode:"add"}),children:[F.jsx(QG,{size:15})," Add object"]}),d1&&F.jsx("button",{disabled:!un,onClick:()=>_u({action:"undo"}),"aria-label":"Undo",children:F.jsx(_Xn,{size:15})}),d1&&F.jsx("button",{disabled:!lt,onClick:()=>_u({action:"redo"}),"aria-label":"Redo",children:F.jsx(OXn,{size:15})}),F.jsxs("button",{onClick:()=>Be(!0),children:[F.jsx(AXn,{size:15})," Model code"]})]})]}),g.metadata.cyclic&&F.jsxs("section",{className:"cycle-callout","data-testid":"cycle-callout",children:[F.jsx(dke,{size:19}),F.jsxs("div",{children:[F.jsx("strong",{children:"Current-step dependency cycle"}),F.jsx("span",{children:"Select a cycle input to read its previous accepted timestep value."})]}),F.jsx("button",{className:cd?"active":"",onClick:()=>{M("applications"),P("detail"),jb(Mt=>!Mt)},"data-testid":"choose-cycle-break",children:cd?"Cancel break selection":"Choose a break point in graph"})]}),hi&&F.jsxs("div",{className:"editor-feedback",children:[hi,F.jsx("button",{onClick:()=>Gt(null),children:F.jsx(Ym,{size:14})})]}),ie&&F.jsxs("section",{className:"graph-scope-filter","data-testid":"graph-scope-filter",children:[F.jsxs("span",{children:["Showing ",x==="resolved"?"executions":x==="applications"?"applications":"topology"," for ",F.jsx("strong",{children:ie.label})," (",ie.objectIds.length," objects)"]}),x==="topology"&&F.jsx("button",{onClick:()=>M("applications"),children:"Show related applications"}),F.jsxs("button",{"aria-label":"Clear graph scope",onClick:()=>Z(null),children:[F.jsx(Ym,{size:14})," Clear"]})]}),F.jsxs("section",{className:"model-workspace",children:[F.jsx("div",{className:"flow-wrap",children:F.jsxs(zUn,{nodes:u0,edges:r6,nodeTypes:aKn,edgeTypes:hKn,onNodesChange:Bg,onEdgesChange:c6,onConnect:Zm,onNodeClick:Gg,onEdgeClick:(Mt,Cc)=>G(Cc.data??null),fitView:!0,minZoom:.05,maxZoom:2,children:[F.jsx(VUn,{color:"#d8cdbc",gap:22,size:1}),F.jsx(tXn,{}),F.jsx(gXn,{pannable:!0,zoomable:!0})]})}),F.jsx(SKn,{selection:U,port:W,initialization:d5,interactive:kn,onEditApplication:Mt=>{Mr(null),st({mode:"update",scope:Mt.targetInstances.length>0?"template":"application",instance:Mt.targetInstances[0],application:Mt})},onRemoveApplication:Mt=>_u({action:"edit",kind:Mt.targetInstances.length>0?"remove_template_application":"remove_application",instance:Mt.targetInstances[0],applicationId:Mt.applicationId}),onConfigureApplication:Mt=>Ws(Mt.applicationId),onOverrideApplication:fu,onEditObject:Mt=>mi({mode:"update",object:Mt}),onRemoveObject:Mt=>_u({action:"edit",kind:"remove_object",objectId:Mt.objectId,recursive:!0})})]}),le&&(a5.length>0||Fg.length>0)&&F.jsx(kKn,{candidate:le,models:a5,applications:Fg,onSelectModel:u6,onSelectApplication:Mt=>{ef(EKn(le,Mt)),kc(null),ee(null)},onClose:()=>ee(null)}),Oe&&F.jsx(xKn,{graph:g,onClose:()=>ge(!1),sendCommand:_u,interactive:kn}),Pe&&F.jsx(AKn,{graph:g,onClose:()=>ae(!1),sendCommand:_u,interactive:kn}),Me&&F.jsx(CKn,{code:wt,onClose:()=>Be(!1)}),rn&&F.jsx(udn,{mode:"open",recentPaths:Ae,currentPath:Fe,autosavePath:Qn,onSubmit:Mt=>{_u({action:"open_model_code",path:Mt}),ln(!1)},onClose:()=>ln(!1)}),xn&&F.jsx(udn,{mode:"save",recentPaths:Ae,currentPath:Fe,autosavePath:Qn,onSubmit:Mt=>{_u({action:"save_model_code",path:Mt}),hn(!1)},onClose:()=>hn(!1)}),At&&F.jsx(LXn,{mode:At.mode,models:g.modelLibrary,objects:g.objects,application:At.application,initialModelType:At.initialModelType,suggestedSelector:At.suggestedSelector,nameReadOnly:At.scope==="template",preview:Wr,onPreview:Mt=>{Mr(null),_u({action:"preview_application_targets",selector:Mt})},onSubmit:h5,onClose:()=>{st(null),Mr(null)}}),Dh&&F.jsx(FXn,{endpoints:Dh,objects:g.objects,preview:ch,onPreview:Mt=>{kc(null),_u({action:"preview_input_binding",...Mt})},onSubmit:Wm,onClose:()=>{ef(null),kc(null)}}),ur&&F.jsx(UXn,{mode:ur.mode,objects:g.objects,object:ur.object,onSubmit:qg,onClose:()=>mi(null)}),Fi&&F.jsx(KXn,{application:Fi,models:g.modelLibrary,instances:g.instances,onSubmit:o6,onRemove:Ug,onClose:()=>fu(null)}),Eu&&ud.get(Eu)&&F.jsx(zXn,{application:ud.get(Eu),applications:g.applications,onCommand:_u,onClose:()=>Ws(null)}),Rs&&F.jsx(TKn,{selection:Rs,initialization:g.initialization,onSubmit:(Mt,Cc)=>{_u({action:"edit",kind:"break_cycle",applicationId:Rs.application.applicationId,input:Rs.port.name,initializeMissing:Mt,initialValue:Cc}),c0(null)},onClose:()=>c0(null)})]})}function bKn({graph:g,view:E,detailMode:x,query:M,scopedObjectIds:N,unresolvedPortIds:P,previousPortIds:k,candidatePortIds:H,cyclicApplications:U,cycleBreakPortIds:G,cycleBreakMode:ie,openCandidates:Z,onPortClick:W,onCycleBreak:se}){const le=Oe=>!M||JSON.stringify(Oe).toLowerCase().includes(M.toLowerCase());if(E==="topology"){const Oe={entity:"model",objectCount:g.metadata.objectCount,instanceCount:g.metadata.instanceCount,applicationCount:g.metadata.applicationCount},ge={id:"model:root",type:"entity",position:{x:0,y:0},data:{nodeKind:"model",title:g.metadata.title||"Composite model",subtitle:"model root",badges:[`${g.metadata.instanceCount} instances`,`${g.metadata.objectCount} objects`],detail:Oe}},Pe=g.instances.filter(le).map(Me=>({id:Me.id,type:"entity",position:{x:0,y:0},data:{nodeKind:"instance",title:Me.name,subtitle:[Me.kind,Me.species].filter(Boolean).join(" · ")||"object instance",badges:[`${Me.objectIds.length} objects`,`${Me.applicationIds.length} applications`,`${Me.instanceOverrides.length+Me.objectOverrides.length} overrides`],detail:Me}})),ae=g.objects.filter(le).map(Me=>({id:Me.id,type:"entity",position:{x:0,y:0},data:{nodeKind:"object",title:Me.name||String(Me.objectId),subtitle:[Me.kind,Me.scale,Me.instance].filter(Boolean).join(" · "),badges:[Me.species,Me.hasStatus?"status":null,Me.hasGeometry?"geometry":null].filter(Boolean),detail:Me}}));return[ge,...Pe,...ae]}if(E==="resolved"){const Oe=new Map(g.applications.map(Pe=>[Pe.applicationId,Pe]));return[...g.executions.filter(Pe=>!N||N.has(n6(Pe.objectId))).filter(le).map(Pe=>{const ae=Oe.get(Pe.applicationId);return{id:Pe.id,type:"entity",position:{x:0,y:0},data:{nodeKind:"execution",title:Pe.applicationId,subtitle:`object ${String(Pe.objectId)}`,badges:[NKn(Pe.modelType),Pe.overridden?"override":"shared"],inputPortIds:[...ae?.inputs??[],...ae?.environmentInputs??[]].map(Me=>Me.id),outputPortIds:[...ae?.outputs??[],...ae?.environmentOutputs??[]].map(Me=>Me.id),detail:Pe}}}),...idn(g,"resolved")]}return[...g.applications.filter(Oe=>!N||Oe.targetIds.some(ge=>N.has(n6(ge)))).filter(le).map(Oe=>({id:Oe.id,type:"application",position:{x:0,y:0},data:{...Oe,nodeKind:"application",detailMode:x,cyclic:U.has(Oe.applicationId),requiredInputPortIds:Oe.inputs.filter(ge=>P.has(ge.id)).map(ge=>ge.id),candidatePortIds:[...Oe.inputs,...Oe.outputs].filter(ge=>H.has(ge.id)).map(ge=>ge.id),previousTimeStepPortIds:Oe.inputs.filter(ge=>k.has(ge.id)).map(ge=>ge.id),cycleBreakInputPortIds:Oe.inputs.filter(ge=>G.has(ge.id)).map(ge=>ge.id),cycleBreakMode:ie,onCandidateClick:(ge,Pe)=>Z(Oe,ge,Pe),onPortClick:W,onCycleBreak:se}})),...idn(g,"applications")]}function idn(g,E){const x=g.edges.filter(N=>N.kind==="environment_binding"&&N.projection===E);return[...new Set(x.flatMap(N=>[N.source,N.target]).filter(N=>N.startsWith("environment:")))].map(N=>{const P=N.slice(12),k=rdn(x.filter(U=>U.target===N).map(U=>U.targetPort).filter(Boolean)),H=rdn(x.filter(U=>U.source===N).map(U=>U.sourcePort).filter(Boolean));return{id:N,type:"entity",position:{x:0,y:0},data:{nodeKind:"environment",title:P,subtitle:"environment provider",badges:[`${H.length} inputs`,`${k.length} outputs`],inputPortIds:k,outputPortIds:H,detail:{provider:P}}}})}function rdn(g){return[...new Set(g)]}function gKn(g,E){return(E==="topology"?[...g.edges,...wKn(g)]:g.edges).filter(M=>mKn(M,E)).map(M=>({id:M.id,source:M.source,target:M.target,sourceHandle:M.sourcePort||void 0,targetHandle:M.targetPort||void 0,type:"modelEdge",data:M,markerEnd:{type:UG.ArrowClosed,color:cdn(M),width:16,height:16},style:{stroke:cdn(M),strokeWidth:M.cycle?4:M.kind==="manual_call"?2.5:1.8,strokeDasharray:M.kind==="previous_timestep"?"7 5":M.kind==="manual_call"?"3 4":void 0}}))}function wKn(g){const E=[],x=new Set(g.instances.flatMap(M=>M.objectIds.map(n6)));for(const M of g.instances)E.push({id:`topology:model:${M.id}`,source:"model:root",target:M.id,kind:"object_topology",projection:"topology",cycle:!1}),E.push({id:`topology:${M.id}:object:${String(M.rootId)}`,source:M.id,target:`object:${String(M.rootId)}`,kind:"object_topology",projection:"topology",cycle:!1});for(const M of g.objects)M.parent===null&&!x.has(n6(M.objectId))&&E.push({id:`topology:model:${M.id}`,source:"model:root",target:M.id,kind:"object_topology",projection:"topology",cycle:!1});return E}function pKn(g,E){const x=new Map;for(const k of g){if(k.parent===null)continue;const H=n6(k.parent);x.set(H,[...x.get(H)??[],k.objectId])}const M=[],N=[E],P=new Set;for(;N.length>0;){const k=N.pop(),H=n6(k);P.has(H)||(P.add(H),M.push(k),N.push(...x.get(H)??[]))}return M}function n6(g){return String(g)}function mKn(g,E){const x=g.projection;return E==="topology"?g.kind==="object_topology":E==="resolved"?x==="resolved":x==="applications"||!x&&!["object_topology","application_target"].includes(g.kind)}function cdn(g){return g.cycle?"#cf4937":g.kind==="previous_timestep"?"#317b62":g.kind==="manual_call"?"#be6a54":g.kind==="object_topology"?"#7b7167":g.kind==="environment_binding"?"#367b8b":"#a59687"}function vKn(g){const E=new Set;for(const x of g.applications){for(const M of x.inputs)(g.applications.some(P=>P.applicationId!==x.applicationId&&P.outputs.some(k=>k.name===M.name))||g.modelLibrary.some(P=>Object.prototype.hasOwnProperty.call(P.outputs,M.name)))&&E.add(M.id);for(const M of x.outputs)(g.applications.some(P=>P.applicationId!==x.applicationId&&P.inputs.some(k=>k.name===M.name))||g.modelLibrary.some(P=>Object.prototype.hasOwnProperty.call(P.inputs,M.name)))&&E.add(M.id)}return E}function yKn(g,E){const x=E.role==="input"?"outputs":"inputs";return g.filter(M=>Object.prototype.hasOwnProperty.call(M[x],E.name)).sort((M,N)=>`${M.package}.${M.name}`.localeCompare(`${N.package}.${N.name}`))}function kKn({candidate:g,models:E,applications:x,onSelectModel:M,onSelectApplication:N,onClose:P}){const k=g.port.role==="input"?`Models that compute ${g.port.name}`:`Models that consume ${g.port.name}`;return F.jsxs("section",{className:"candidate-popover",style:{left:Math.min(g.x+8,window.innerWidth-390),top:Math.min(g.y-20,window.innerHeight-480)},children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:k}),F.jsx("span",{children:"Exact declared variable-name matches"})]}),F.jsx("button",{onClick:P,children:F.jsx(Ym,{size:15})})]}),F.jsxs("div",{className:"candidate-list",children:[x.length>0&&F.jsx("div",{className:"candidate-section-label",children:"Existing applications"}),x.map(H=>F.jsxs("button",{className:"candidate-card existing",onClick:()=>N(H),children:[F.jsx("strong",{children:H.name||H.applicationId}),F.jsx("span",{children:H.modelName}),F.jsxs("small",{children:[H.targetCount," target",H.targetCount===1?"":"s"]}),F.jsx("div",{children:"Connect without adding another application"})]},H.applicationId)),E.length>0&&F.jsx("div",{className:"candidate-section-label",children:"Available models"}),E.map(H=>F.jsxs("button",{className:"candidate-card",onClick:()=>M(H),children:[F.jsx("strong",{children:H.name}),F.jsx("span",{children:H.process}),F.jsx("small",{children:H.package||H.module}),F.jsxs("div",{children:[Object.keys(H.inputs).length," inputs · ",Object.keys(H.outputs).length," outputs"]})]},H.type))]})]})}function jKn(g,E){return g.filter(x=>x.applicationId!==E.application.applicationId).filter(x=>(E.port.role==="input"?x.outputs:x.inputs).some(N=>N.name===E.port.name)).sort((x,M)=>x.applicationId.localeCompare(M.applicationId))}function EKn(g,E){if(g.port.role==="input"){const M=E.outputs.find(N=>N.name===g.port.name);if(!M)throw new Error(`Application ${E.applicationId} does not output ${g.port.name}.`);return{sourceApplication:E,sourcePort:M,targetApplication:g.application,targetPort:g.port}}const x=E.inputs.find(M=>M.name===g.port.name);if(!x)throw new Error(`Application ${E.applicationId} does not input ${g.port.name}.`);return{sourceApplication:g.application,sourcePort:g.port,targetApplication:E,targetPort:x}}function SKn({selection:g,port:E,initialization:x,interactive:M,onEditApplication:N,onConfigureApplication:P,onRemoveApplication:k,onOverrideApplication:H,onEditObject:U,onRemoveObject:G}){const ie=g&&"applicationId"in g&&"selector"in g?g:null,Z=g&&"objectId"in g&&!("applicationId"in g)?g:null;return F.jsxs("aside",{className:"model-inspector",children:[F.jsxs("header",{children:[F.jsx("strong",{children:"Inspector"}),g&&F.jsx("span",{children:OKn(g)})]}),!g&&F.jsxs("div",{className:"empty-inspector",children:[F.jsx(EXn,{size:28}),F.jsx("p",{children:"Select an application, object, execution, or relationship."})]}),g&&F.jsx("pre",{children:JSON.stringify(g,null,2)}),ie&&M&&F.jsxs("div",{className:"inspector-actions",children:[F.jsx("button",{onClick:()=>N(ie),children:ie.targetInstances.length>0?"Edit shared template":"Edit application"}),ie.targetInstances.length===0&&F.jsx("button",{"data-testid":"configure-application",onClick:()=>P(ie),children:"Configure coupling"}),ie.targetInstances.length>0&&F.jsx("button",{onClick:()=>H(ie),children:"Create override"}),F.jsx("button",{className:"danger",onClick:()=>k(ie),children:ie.targetInstances.length>0?"Remove from shared template":"Remove application"})]}),Z&&M&&F.jsxs("div",{className:"inspector-actions",children:[F.jsx("button",{onClick:()=>U(Z),children:"Edit object"}),F.jsx("button",{className:"danger",onClick:()=>G(Z),children:"Remove object and descendants"})]}),E&&F.jsxs("section",{children:[F.jsx("h4",{children:"Selected variable"}),F.jsx("code",{children:E.name}),F.jsx("p",{children:E.expectedType})]}),g&&x.length>0&&F.jsxs("section",{className:"inspector-initialization",children:[F.jsx("h4",{children:"Initialization"}),x.slice(0,8).map(W=>F.jsxs("div",{children:[F.jsx("code",{children:W.variable}),F.jsx("span",{className:W.disposition==="unresolved"?"unresolved":"",children:W.disposition})]},`${W.applicationId}:${W.objectId}:${W.variable}`))]})]})}function xKn({graph:g,onClose:E,sendCommand:x,interactive:M}){return F.jsxs(Fue,{title:"Diagnostics and cycles",onClose:E,children:[g.diagnostics.map(N=>F.jsxs("article",{className:"diagnostic-card",children:[F.jsx("strong",{children:N.code}),F.jsx("p",{children:N.message}),N.suggestions.map(P=>F.jsx("small",{children:P},P))]},`${N.code}:${N.message}`)),g.cycles.map(N=>F.jsxs("article",{className:"cycle-card",children:[F.jsx("strong",{children:N.applicationIds.join(" → ")}),F.jsx("p",{children:"Choose an input to read from the previous timestep."}),N.breakCandidates.map(P=>F.jsxs("button",{disabled:!M,onClick:()=>x({action:"edit",kind:"mark_previous_timestep",applicationId:P.applicationId,input:P.input}),children:[P.applicationId,".",P.input]},`${P.applicationId}:${P.objectId}:${P.input}`))]},N.id)),g.diagnostics.length===0&&g.cycles.length===0&&F.jsx("p",{children:"No diagnostics."})]})}function AKn({graph:g,onClose:E,sendCommand:x,interactive:M}){const N=g.initialization.filter(k=>k.disposition==="unresolved"),P=new Map;for(const k of N){const H=`${k.applicationId}:${k.variable}`;P.set(H,[...P.get(H)||[],k])}return F.jsxs(Fue,{title:"Initialization",onClose:E,children:[[...P.entries()].map(([k,H])=>F.jsx(MKn,{rows:H,interactive:M,sendCommand:x},k)),N.length===0&&F.jsx("p",{children:"No unresolved initial values."})]})}function MKn({rows:g,interactive:E,sendCommand:x}){const[M,N]=He.useState("float"),[P,k]=He.useState(""),H=g[0],U={type:M,value:P};return F.jsxs("article",{className:"initialization-group",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:H.variable}),F.jsx("span",{children:H.applicationId})]}),F.jsxs("small",{children:[g.length," object",g.length===1?"":"s"," · expected ",H.expectedType]})]}),F.jsx("p",{children:"Required because the input has no producer, environment source, status value, or usable temporal initialization."}),E&&F.jsxs("div",{className:"initialization-value",children:[F.jsxs("label",{children:["Type",F.jsxs("select",{value:M,onChange:G=>N(G.target.value),children:[F.jsx("option",{value:"float",children:"Float"}),F.jsx("option",{value:"integer",children:"Integer"}),F.jsx("option",{value:"boolean",children:"Boolean"}),F.jsx("option",{value:"symbol",children:"Symbol"}),F.jsx("option",{value:"string",children:"String"}),F.jsx("option",{value:"julia",children:"Julia expression"})]})]}),F.jsxs("label",{children:["Value",F.jsx("input",{value:P,onChange:G=>k(G.target.value)})]}),F.jsx("button",{disabled:!P.trim(),onClick:()=>x({action:"edit",kind:"set_object_statuses",objectIds:g.map(G=>G.objectId),variable:H.variable,value:U}),children:"Set all targets"})]}),F.jsx("div",{className:"initialization-object-list",children:g.map(G=>F.jsxs("div",{children:[F.jsxs("span",{children:["Object ",String(G.objectId)]}),F.jsx("code",{children:G.origin}),E&&F.jsx("button",{disabled:!P.trim(),onClick:()=>x({action:"edit",kind:"set_object_status",objectId:G.objectId,variable:G.variable,value:U}),children:"Set this object"})]},String(G.objectId)))})]})}function CKn({code:g,onClose:E}){return F.jsx(Fue,{title:"Model code",onClose:E,children:F.jsx("pre",{className:"model-code",children:g||"Model code is available from an interactive editor session."})})}function udn({mode:g,recentPaths:E,currentPath:x,autosavePath:M,onSubmit:N,onClose:P}){const[k,H]=He.useState(x||"");return F.jsx(Fue,{title:g==="open"?"Open Model":"Save Model",onClose:P,children:F.jsxs("div",{className:"model-file-dialog",children:[F.jsx("p",{children:g==="open"?"Open a Julia script whose final binding is `model = CompositeModel(...)`. Future edits will be saved back to that file.":"After the first save, every successful graph edit automatically rewrites this Julia script."}),F.jsxs("label",{children:["Julia file path",F.jsxs("div",{className:"model-path-input",children:[F.jsx("input",{value:k,onChange:U=>H(U.target.value),placeholder:"/absolute/path/to/model.jl",autoFocus:!0}),F.jsx("button",{className:"primary",disabled:!k.trim(),onClick:()=>N(k.trim()),children:g==="open"?"Open":"Save"})]})]}),g==="open"&&E.length>0&&F.jsxs("section",{children:[F.jsx("strong",{children:"Recent models"}),F.jsx("div",{className:"recent-model-list",children:E.map(U=>F.jsxs("button",{onClick:()=>N(U),children:[F.jsx("span",{children:U.split("/").at(-1)}),F.jsx("small",{children:U})]},U))})]}),g==="open"&&M&&F.jsxs("section",{children:[F.jsx("strong",{children:"Recovery autosave"}),F.jsx("button",{className:"recovery-path",onClick:()=>N(M),children:M})]}),F.jsx("small",{children:"Use Git to version saved composite-model scripts and review scientific configuration changes."})]})})}function TKn({selection:g,initialization:E,onSubmit:x,onClose:M}){const N=E.filter(G=>G.applicationId===g.application.applicationId&&G.variable===g.port.name&&G.disposition!=="supplied"),[P,k]=He.useState("float"),[H,U]=He.useState("");return F.jsx("div",{className:"overlay-backdrop",onMouseDown:M,children:F.jsxs("section",{className:"overlay-panel cycle-break-dialog",onMouseDown:G=>G.stopPropagation(),"data-testid":"cycle-break-dialog",children:[F.jsxs("header",{children:[F.jsxs("div",{children:[F.jsx("strong",{children:"Break the current-step cycle"}),F.jsxs("span",{children:[g.application.applicationId,".",g.port.name]})]}),F.jsx("button",{onClick:M,children:F.jsx(Ym,{size:17})})]}),F.jsxs("div",{className:"overlay-content",children:[F.jsx("p",{children:"This changes the application input to read its value from the previous accepted timestep. The model is disconnected from the current value during each run step."}),F.jsxs("div",{className:"cycle-impact",children:[F.jsx("strong",{children:"Application-wide change"}),F.jsxs("span",{children:["It affects all ",g.application.targetCount," targets selected by this application."]})]}),N.length>0&&F.jsxs("fieldset",{children:[F.jsx("legend",{children:"Required initial value"}),F.jsxs("p",{children:[N.length," target",N.length===1?"":"s"," need a value before the first timestep."]}),F.jsxs("div",{className:"form-grid",children:[F.jsxs("label",{children:["Value type",F.jsxs("select",{value:P,onChange:G=>k(G.target.value),children:[F.jsx("option",{value:"float",children:"Float"}),F.jsx("option",{value:"integer",children:"Integer"}),F.jsx("option",{value:"boolean",children:"Boolean"}),F.jsx("option",{value:"symbol",children:"Symbol"}),F.jsx("option",{value:"string",children:"String"}),F.jsx("option",{value:"julia",children:"Julia expression"})]})]}),F.jsxs("label",{children:["Initial value",F.jsx("input",{value:H,onChange:G=>U(G.target.value),autoFocus:!0})]})]})]})]}),F.jsxs("footer",{children:[F.jsx("button",{onClick:M,children:"Cancel"}),F.jsxs("button",{className:"primary",disabled:N.length>0&&!H.trim(),onClick:()=>x(N.length>0,N.length>0?{type:P,value:H}:null),"data-testid":"confirm-cycle-break",children:[F.jsx(Q0n,{size:15})," Use previous timestep"]})]})]})})}function Fue({title:g,onClose:E,children:x}){return F.jsx("div",{className:"overlay-backdrop",onMouseDown:E,children:F.jsxs("section",{className:"overlay-panel",onMouseDown:M=>M.stopPropagation(),children:[F.jsxs("header",{children:[F.jsx("strong",{children:g}),F.jsx("button",{onClick:E,children:F.jsx(Ym,{size:17})})]}),F.jsx("div",{className:"overlay-content",children:x})]})})}function OKn(g){return"applicationId"in g?g.applicationId:"objectId"in g?String(g.objectId):"objectIds"in g?g.name:"entity"in g?"Composite model":"provider"in g?g.provider:g.kind.replaceAll("_"," ")}function NKn(g){return g.split(".").at(-1)||g}function DKn(g){const E={selectors:[]};return g.targetInstances.length===0&&g.targetScales.length===1&&(E.scale=g.targetScales[0]),g.targetKinds.length===1&&(E.kind=g.targetKinds[0]),g.targetSpecies.length===1&&(E.species=g.targetSpecies[0]),{type:g.targetCount===1?"One":"Many",multiplicity:g.targetCount===1?"one":"many",criteria:E,julia:""}}function Q7e(g,E,x){return`application:${g}:${E}:${x}`}function W7e(){const g=document.getElementById("pse-model-graph-data");if(!g?.textContent)return edn;try{return JSON.parse(g.textContent)}catch{return edn}}function IKn(){const g=document.getElementById("pse-editor-config");if(!g?.textContent)return null;try{return JSON.parse(g.textContent)}catch{return null}}class _Kn extends He.Component{state={error:null};static getDerivedStateFromError(E){return{error:E}}componentDidCatch(E,x){console.error("PlantSimEngine model graph frontend failed",E,x)}render(){return this.state.error?F.jsxs("main",{className:"frontend-error","data-testid":"frontend-error",children:[F.jsx(dke,{size:28}),F.jsx("h1",{children:"The graph view could not be rendered"}),F.jsx("p",{children:this.state.error.message}),F.jsxs("button",{onClick:()=>window.location.reload(),children:[F.jsx(NXn,{size:15})," Reload graph"]})]}):this.props.children}}lzn.createRoot(document.getElementById("root")).render(F.jsx(He.StrictMode,{children:F.jsx(_Kn,{children:F.jsx(dKn,{})})})); diff --git a/frontend/dist/index.html b/frontend/dist/index.html index c460938f3..288d9e058 100644 --- a/frontend/dist/index.html +++ b/frontend/dist/index.html @@ -4,8 +4,8 @@ PlantSimEngine Dependency Graph - - + +
diff --git a/frontend/e2e/graph-editor.spec.ts b/frontend/e2e/graph-editor.spec.ts index b63aae681..6d1f261a0 100644 --- a/frontend/e2e/graph-editor.spec.ts +++ b/frontend/e2e/graph-editor.spec.ts @@ -2,7 +2,7 @@ import { expect, test, type APIRequestContext, type Locator, type Page } from "@ import type { ApplicationGraphNode, EditorState } from "../src/types"; import { startGraphEditorServer, type GraphEditorServer } from "./graphEditorServer"; -test.describe.serial("PlantSimEngine Scene graph editor", () => { +test.describe.serial("PlantSimEngine model graph editor", () => { let server: GraphEditorServer; test.beforeAll(async () => { @@ -15,7 +15,7 @@ test.describe.serial("PlantSimEngine Scene graph editor", () => { test("starts empty and adds an object", async ({ page, request }) => { await page.goto(server.url); - await expect(page.getByText("Scene Graph")).toBeVisible(); + await expect(page.getByText("Model Graph")).toBeVisible(); let state = await getState(request, server.url); expect(state.ok).toBe(true); @@ -64,7 +64,7 @@ test.describe.serial("PlantSimEngine Scene graph editor", () => { await page.goto(url.toString()); await expect(page.getByTestId("application-node-light")).toBeVisible(); await page.getByTestId("application-node-light").click(); - await expect(page.locator(".scene-inspector")).toContainText("light"); + await expect(page.locator(".model-inspector")).toContainText("light"); await expect(page.getByText("Edit application")).toHaveCount(0); }); @@ -91,7 +91,7 @@ test.describe.serial("PlantSimEngine Scene graph editor", () => { state = await waitForState(request, server.url, (value) => value.graph.metadata.cyclic === false); expect(state.graph.edges.some((edge) => edge.kind === "previous_timestep")).toBe(true); - expect(state.sceneCode).toContain("PreviousTimeStep"); + expect(state.modelCode).toContain("PreviousTimeStep"); }); test("connects another consumer and supports undo, redo, remove, and save", async ({ page, request }, testInfo) => { @@ -107,9 +107,9 @@ test.describe.serial("PlantSimEngine Scene graph editor", () => { await page.getByTestId("call-target").selectOption("consumer"); await page.getByTestId("add-call-binding").click(); await waitForState(request, server.url, (value) => value.graph.metadata.callCount === 1); - await page.getByTestId("environment-provider").fill("scene"); + await page.getByTestId("environment-provider").fill("model"); await page.getByTestId("apply-environment-provider").click(); - let state = await waitForState(request, server.url, (value) => findApplication(value, "light").environment?.provider === "scene"); + let state = await waitForState(request, server.url, (value) => findApplication(value, "light").environment?.provider === "model"); expect(state.graph.edges.some((edge) => edge.kind === "manual_call" && edge.call === "consumer_call")).toBe(true); await page.getByRole("button", { name: "Done" }).click(); @@ -136,9 +136,9 @@ test.describe.serial("PlantSimEngine Scene graph editor", () => { await page.getByRole("button", { name: "Redo" }).click(); await waitForState(request, server.url, (value) => !value.graph.applications.some((application) => application.applicationId === "consumer")); - const savePath = testInfo.outputPath("scene.jl"); - await page.getByTestId("save-scene").click(); - await page.getByPlaceholder("/absolute/path/to/scene.jl").fill(savePath); + const savePath = testInfo.outputPath("model.jl"); + await page.getByTestId("save-model").click(); + await page.getByPlaceholder("/absolute/path/to/model.jl").fill(savePath); await page.getByRole("button", { name: "Save", exact: true }).click(); state = await waitForState(request, server.url, (value) => value.savePath === savePath); expect(state.recentPaths).toContain(savePath); diff --git a/frontend/src/App.test.ts b/frontend/src/App.test.ts index 6f27897fa..46ebec9d9 100644 --- a/frontend/src/App.test.ts +++ b/frontend/src/App.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; import { applicationPortId, applicationsForPort, deriveCandidatePortIds, endpointsForCandidate, modelsForPort, objectSubtreeIds, selectorSuggestion } from "./App"; -import type { ApplicationGraphNode, GraphPort, ModelDescriptor, ObjectGraphNode, SceneGraphView } from "./types"; +import type { ApplicationGraphNode, GraphPort, ModelDescriptor, ObjectGraphNode, ModelGraphView } from "./types"; const output: GraphPort = { id: applicationPortId("source", "output", "signal"), name: "signal", role: "output", default: 0, defaultJulia: "0", expectedType: "Int" }; const input: GraphPort = { id: applicationPortId("consumer", "input", "signal"), name: "signal", role: "input", default: 0, defaultJulia: "0", expectedType: "Int" }; @@ -69,9 +69,9 @@ function object(id: string, parent: string | null): ObjectGraphNode { return { id: `object:${id}`, objectId: id, scale: null, kind: null, species: null, name: id, instance: null, parent, children: [], hasGeometry: false, hasStatus: false }; } -function graphView(applications: ApplicationGraphNode[], modelLibrary: ModelDescriptor[]): SceneGraphView { +function graphView(applications: ApplicationGraphNode[], modelLibrary: ModelDescriptor[]): ModelGraphView { return { - schemaVersion: 1, level: "applications", metadata: { title: "", sceneRevision: 0, objectCount: 1, instanceCount: 0, applicationCount: applications.length, executionCount: applications.length, bindingCount: 0, callCount: 0, unresolvedInitializationCount: 0, cyclic: false, strictlyCompiled: true }, + schemaVersion: 1, level: "applications", metadata: { title: "", modelRevision: 0, objectCount: 1, instanceCount: 0, applicationCount: applications.length, executionCount: applications.length, bindingCount: 0, callCount: 0, unresolvedInitializationCount: 0, cyclic: false, strictlyCompiled: true }, objects: [], instances: [], applications, executions: [], edges: [], modelLibrary, initialization: [], diagnostics: [], cycles: [], availableActions: [], }; } diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 7e7e1c2c4..b97fe91e5 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -37,7 +37,7 @@ import { OverrideForm, type OverrideFormValue } from "./OverrideForm"; import { ApplicationNode, EntityNode } from "./ModelNode"; import { DependencyEdge } from "./DependencyEdge"; import { layoutGraph, type LayoutMode } from "./layout"; -import { sampleGraph } from "./sampleGraph"; +import { sampleModelGraph } from "./sampleModelGraph"; import type { ApplicationGraphNode, DetailMode, @@ -51,19 +51,19 @@ import type { ObjectGraphNode, RuntimeApplicationNode, RuntimeEntityNode, - SceneGraphEdge, - SceneGraphView, - SceneRootDescriptor, + ModelGraphEdge, + ModelGraphView, + ModelRootDescriptor, SelectorPreview, TargetPreview, } from "./types"; import "./styles.css"; type FlowNode = Node; -type FlowEdge = Edge; +type FlowEdge = Edge; type CandidatePopover = { port: GraphPort; application: ApplicationGraphNode; x: number; y: number }; type CycleBreakSelection = { application: ApplicationGraphNode; port: GraphPort }; -type InspectorSelection = ApplicationGraphNode | InstanceDescriptor | ObjectGraphNode | ExecutionGraphNode | EnvironmentGraphNode | SceneRootDescriptor | SceneGraphEdge | null; +type InspectorSelection = ApplicationGraphNode | InstanceDescriptor | ObjectGraphNode | ExecutionGraphNode | EnvironmentGraphNode | ModelRootDescriptor | ModelGraphEdge | null; type GraphScopeFilter = { label: string; objectIds: unknown[] }; type ApplicationFormState = { mode: "add" | "update"; @@ -76,10 +76,10 @@ type ApplicationFormState = { type ObjectFormState = { mode: "add" | "update"; object?: ObjectGraphNode }; const nodeTypes = { application: ApplicationNode, entity: EntityNode }; -const edgeTypes = { sceneEdge: DependencyEdge }; +const edgeTypes = { modelEdge: DependencyEdge }; export default function App() { - const [graph, setGraph] = useState(loadInitialGraph); + const [graph, setGraph] = useState(loadInitialGraph); const [view, setView] = useState(() => loadInitialGraph().level); const [detailMode, setDetailMode] = useState(() => loadInitialGraph().metadata.applicationCount > 24 ? "overview" : "detail"); const [query, setQuery] = useState(""); @@ -89,10 +89,10 @@ export default function App() { const [candidate, setCandidate] = useState(null); const [showDiagnostics, setShowDiagnostics] = useState(false); const [showInitialization, setShowInitialization] = useState(false); - const [showSceneCode, setShowSceneCode] = useState(false); + const [showModelCode, setShowModelCode] = useState(false); const [showOpen, setShowOpen] = useState(false); const [showSave, setShowSave] = useState(false); - const [sceneCode, setSceneCode] = useState(""); + const [modelCode, setSceneCode] = useState(""); const [autosavePath, setAutosavePath] = useState(null); const [savePath, setSavePath] = useState(null); const [recentPaths, setRecentPaths] = useState([]); @@ -153,7 +153,7 @@ export default function App() { nextSocket.addEventListener("message", (event) => { const payload = JSON.parse(event.data) as EditorState; if (payload.graph) setGraph(payload.graph); - if (typeof payload.sceneCode === "string") setSceneCode(payload.sceneCode); + if (typeof payload.modelCode === "string") setSceneCode(payload.modelCode); setAutosavePath(payload.autosavePath ?? null); setSavePath(payload.savePath ?? null); setRecentPaths(payload.recentPaths ?? []); @@ -229,7 +229,7 @@ export default function App() { } else if (node.data.nodeKind === "instance") { const instance = node.data.detail as InstanceDescriptor; setScopeFilter({ label: `instance ${instance.name}`, objectIds: instance.objectIds }); - } else if (node.data.nodeKind === "scene") { + } else if (node.data.nodeKind === "model") { setScopeFilter(null); } } @@ -244,7 +244,7 @@ export default function App() { initialModelType: model.type, suggestedSelector: selectorSuggestion(candidate.application), }); - if (!connected) setFeedback(`${model.name} matches ${candidate.port.name}. Start an interactive Julia editor session to add it to the Scene.`); + if (!connected) setFeedback(`${model.name} matches ${candidate.port.name}. Start an interactive Julia editor session to add it to the composite model.`); setCandidate(null); }, [candidate, connected]); @@ -344,18 +344,18 @@ export default function App() { }, [graph.initialization, selected]); return ( -
-
-
+
+
+
-
PLANTSIMENGINEScene Graph
+
PLANTSIMENGINEModel Graph
-
+
setQuery(event.target.value)} placeholder="Search application, object, or variable" /> {query && }
-
+
{graph.metadata.applicationCount} applications {graph.metadata.objectCount} objects {graph.metadata.unresolvedInitializationCount > 0 && ( @@ -370,9 +370,9 @@ export default function App() { -
- {editorConfig && } - {editorConfig && } +
+ {editorConfig && } + {editorConfig && } {view !== "topology" && ( } {editorConfig && } {editorConfig && } - +
@@ -412,7 +412,7 @@ export default function App() { )} -
+
setShowDiagnostics(false)} sendCommand={sendCommand} interactive={connected} />} {showInitialization && setShowInitialization(false)} sendCommand={sendCommand} interactive={connected} />} - {showSceneCode && setShowSceneCode(false)} />} - {showOpen && { sendCommand({ action: "open_scene_code", path }); setShowOpen(false); }} onClose={() => setShowOpen(false)} />} - {showSave && { sendCommand({ action: "save_scene_code", path }); setShowSave(false); }} onClose={() => setShowSave(false)} />} + {showModelCode && setShowModelCode(false)} />} + {showOpen && { sendCommand({ action: "open_model_code", path }); setShowOpen(false); }} onClose={() => setShowOpen(false)} />} + {showSave && { sendCommand({ action: "save_model_code", path }); setShowSave(false); }} onClose={() => setShowSave(false)} />} {applicationForm && ( !query || JSON.stringify(value).toLowerCase().includes(query.toLowerCase()); if (view === "topology") { - const sceneDetail: SceneRootDescriptor = { - entity: "scene", + const modelDetail: ModelRootDescriptor = { + entity: "model", objectCount: graph.metadata.objectCount, instanceCount: graph.metadata.instanceCount, applicationCount: graph.metadata.applicationCount, }; - const sceneNode: FlowNode = { - id: "scene:root", + const modelNode: FlowNode = { + id: "model:root", type: "entity", position: { x: 0, y: 0 }, data: { - nodeKind: "scene", - title: graph.metadata.title || "Scene", - subtitle: "scene root", + nodeKind: "model", + title: graph.metadata.title || "Composite model", + subtitle: "model root", badges: [`${graph.metadata.instanceCount} instances`, `${graph.metadata.objectCount} objects`], - detail: sceneDetail, + detail: modelDetail, }, }; const instanceNodes: FlowNode[] = graph.instances.filter(matches).map((instance) => ({ @@ -610,7 +610,7 @@ function buildNodes({ detail: object, }, })); - return [sceneNode, ...instanceNodes, ...objectNodes]; + return [modelNode, ...instanceNodes, ...objectNodes]; } if (view === "resolved") { const applications = new Map(graph.applications.map((application) => [application.applicationId, application])); @@ -659,7 +659,7 @@ function buildNodes({ return [...applicationNodes, ...environmentNodes(graph, "applications")]; } -function environmentNodes(graph: SceneGraphView, projection: "applications" | "resolved"): FlowNode[] { +function environmentNodes(graph: ModelGraphView, projection: "applications" | "resolved"): FlowNode[] { const relevant = graph.edges.filter((edge) => edge.kind === "environment_binding" && edge.projection === projection); const ids = new Set(relevant.flatMap((edge) => [edge.source, edge.target]).filter((id) => id.startsWith("environment:"))); return [...ids].map((id) => { @@ -685,7 +685,7 @@ function environmentNodes(graph: SceneGraphView, projection: "applications" | "r function uniqueStrings(values: string[]) { return [...new Set(values)]; } -function buildEdges(graph: SceneGraphView, view: GraphViewMode): FlowEdge[] { +function buildEdges(graph: ModelGraphView, view: GraphViewMode): FlowEdge[] { const sourceEdges = view === "topology" ? [...graph.edges, ...topologyContainerEdges(graph)] : graph.edges; return sourceEdges .filter((edge) => edgeProjectionMatches(edge, view)) @@ -695,7 +695,7 @@ function buildEdges(graph: SceneGraphView, view: GraphViewMode): FlowEdge[] { target: edge.target, sourceHandle: edge.sourcePort || undefined, targetHandle: edge.targetPort || undefined, - type: "sceneEdge", + type: "modelEdge", data: edge, markerEnd: { type: MarkerType.ArrowClosed, color: edgeColor(edge), width: 16, height: 16 }, style: { @@ -706,13 +706,13 @@ function buildEdges(graph: SceneGraphView, view: GraphViewMode): FlowEdge[] { })); } -function topologyContainerEdges(graph: SceneGraphView): SceneGraphEdge[] { - const edges: SceneGraphEdge[] = []; +function topologyContainerEdges(graph: ModelGraphView): ModelGraphEdge[] { + const edges: ModelGraphEdge[] = []; const instanceObjectIds = new Set(graph.instances.flatMap((instance) => instance.objectIds.map(objectKey))); for (const instance of graph.instances) { edges.push({ - id: `topology:scene:${instance.id}`, - source: "scene:root", + id: `topology:model:${instance.id}`, + source: "model:root", target: instance.id, kind: "object_topology", projection: "topology", @@ -730,8 +730,8 @@ function topologyContainerEdges(graph: SceneGraphView): SceneGraphEdge[] { for (const object of graph.objects) { if (object.parent === null && !instanceObjectIds.has(objectKey(object.objectId))) { edges.push({ - id: `topology:scene:${object.id}`, - source: "scene:root", + id: `topology:model:${object.id}`, + source: "model:root", target: object.id, kind: "object_topology", projection: "topology", @@ -765,14 +765,14 @@ export function objectSubtreeIds(objects: ObjectGraphNode[], rootId: unknown): u function objectKey(value: unknown) { return String(value); } -function edgeProjectionMatches(edge: SceneGraphEdge, view: GraphViewMode) { - const projection = (edge as SceneGraphEdge & { projection?: string }).projection; +function edgeProjectionMatches(edge: ModelGraphEdge, view: GraphViewMode) { + const projection = (edge as ModelGraphEdge & { projection?: string }).projection; if (view === "topology") return edge.kind === "object_topology"; if (view === "resolved") return projection === "resolved"; return projection === "applications" || (!projection && !["object_topology", "application_target"].includes(edge.kind)); } -function edgeColor(edge: SceneGraphEdge) { +function edgeColor(edge: ModelGraphEdge) { if (edge.cycle) return "#cf4937"; if (edge.kind === "previous_timestep") return "#317b62"; if (edge.kind === "manual_call") return "#be6a54"; @@ -781,7 +781,7 @@ function edgeColor(edge: SceneGraphEdge) { return "#a59687"; } -export function deriveCandidatePortIds(graph: SceneGraphView) { +export function deriveCandidatePortIds(graph: ModelGraphView) { const result = new Set(); for (const application of graph.applications) { for (const input of application.inputs) { @@ -871,11 +871,11 @@ export function endpointsForCandidate(candidate: CandidatePopover, application: return { sourceApplication: candidate.application, sourcePort: candidate.port, targetApplication: application, targetPort }; } -function Inspector({ selection, port, initialization, interactive, onEditApplication, onConfigureApplication, onRemoveApplication, onOverrideApplication, onEditObject, onRemoveObject }: { selection: InspectorSelection; port: GraphPort | null; initialization: SceneGraphView["initialization"]; interactive: boolean; onEditApplication: (application: ApplicationGraphNode) => void; onConfigureApplication: (application: ApplicationGraphNode) => void; onRemoveApplication: (application: ApplicationGraphNode) => void; onOverrideApplication: (application: ApplicationGraphNode) => void; onEditObject: (object: ObjectGraphNode) => void; onRemoveObject: (object: ObjectGraphNode) => void }) { +function Inspector({ selection, port, initialization, interactive, onEditApplication, onConfigureApplication, onRemoveApplication, onOverrideApplication, onEditObject, onRemoveObject }: { selection: InspectorSelection; port: GraphPort | null; initialization: ModelGraphView["initialization"]; interactive: boolean; onEditApplication: (application: ApplicationGraphNode) => void; onConfigureApplication: (application: ApplicationGraphNode) => void; onRemoveApplication: (application: ApplicationGraphNode) => void; onOverrideApplication: (application: ApplicationGraphNode) => void; onEditObject: (object: ObjectGraphNode) => void; onRemoveObject: (object: ObjectGraphNode) => void }) { const application = selection && "applicationId" in selection && "selector" in selection ? selection as ApplicationGraphNode : null; const object = selection && "objectId" in selection && !("applicationId" in selection) ? selection as ObjectGraphNode : null; return ( -